d->name[EROFS_NAME_LEN - 1] = '\0';
d->inode = NULL;
d->type = EROFS_FT_UNKNOWN;
+ d->validnid = false;
list_add_tail(&d->d_child, &parent->i_subdirs);
return d;
}
{
struct erofs_inode *const inode = d->inode;
+ if (d->validnid)
+ return;
d->nid = erofs_lookupnid(inode);
+ d->validnid = true;
erofs_iput(inode);
}
if (inode->extent_isize)
inodesize = roundup(inodesize, 8) + inode->extent_isize;
+ if (inode->datalayout == EROFS_INODE_FLAT_PLAIN)
+ goto noinline;
+
/* TODO: tailpacking inline of chunk-based format isn't finalized */
if (inode->datalayout == EROFS_INODE_CHUNK_BASED)
goto noinline;
if (!inode->idata_size)
goto out;
+ DBG_BUGON(!inode->idata);
/* have enough room to inline data */
if (inode->bh_inline) {
ibh = inode->bh_inline;
return ret;
}
-static int erofs_rebuild_handle_directory(struct erofs_inode *dir)
+int erofs_rebuild_load_basedir(struct erofs_inode *dir);
+
+bool erofs_dentry_is_wht(struct erofs_sb_info *sbi, struct erofs_dentry *d)
{
+ if (!d->validnid)
+ return erofs_inode_is_whiteout(d->inode);
+ if (d->type == EROFS_FT_CHRDEV) {
+ struct erofs_inode ei = { .sbi = sbi, .nid = d->nid };
+ int ret;
+
+ ret = erofs_read_inode_from_disk(&ei);
+ if (ret) {
+ erofs_err("failed to check DT_WHT: %s",
+ erofs_strerror(ret));
+ DBG_BUGON(1);
+ return false;
+ }
+ return erofs_inode_is_whiteout(&ei);
+ }
+ return false;
+}
+
+static int erofs_rebuild_handle_directory(struct erofs_inode *dir,
+ bool incremental)
+{
+ struct erofs_sb_info *sbi = dir->sbi;
struct erofs_dentry *d, *n;
unsigned int nr_subdirs, i_nlink;
+ bool delwht = cfg.c_ovlfs_strip && dir->whiteouts;
int ret;
nr_subdirs = 0;
i_nlink = 0;
+
list_for_each_entry_safe(d, n, &dir->i_subdirs, d_child) {
- if (cfg.c_ovlfs_strip && erofs_inode_is_whiteout(d->inode)) {
+ if (delwht && erofs_dentry_is_wht(sbi, d)) {
erofs_dbg("remove whiteout %s", d->inode->i_srcpath);
list_del(&d->d_child);
erofs_d_invalidate(d);
free(d);
continue;
}
- i_nlink += S_ISDIR(d->inode->i_mode);
+ i_nlink += (d->type == EROFS_FT_DIR);
++nr_subdirs;
}
if (ret)
return ret;
+ if (IS_ROOT(dir) && incremental)
+ dir->datalayout = EROFS_INODE_FLAT_PLAIN;
+
/*
* if there're too many subdirs as compact form, set nlink=1
* rather than upgrade to use extented form instead.
else
dir->i_nlink = i_nlink;
- return erofs_mkfs_go(dir->sbi, EROFS_MKFS_JOB_DIR, &dir, sizeof(dir));
+ return erofs_mkfs_go(sbi, EROFS_MKFS_JOB_DIR, &dir, sizeof(dir));
}
static int erofs_mkfs_handle_inode(struct erofs_inode *inode)
return ret;
}
-static int erofs_rebuild_handle_inode(struct erofs_inode *inode)
+static int erofs_rebuild_handle_inode(struct erofs_inode *inode,
+ bool incremental)
{
char *trimmed;
int ret;
inode->inode_isize = sizeof(struct erofs_inode_compact);
}
+ if (incremental && S_ISDIR(inode->i_mode) &&
+ inode->dev == inode->sbi->dev && !inode->opaque) {
+ ret = erofs_rebuild_load_basedir(inode);
+ if (ret)
+ return ret;
+ }
+
/* strip all unnecessary overlayfs xattrs when ovlfs_strip is enabled */
if (cfg.c_ovlfs_strip)
erofs_clear_opaque_xattr(inode);
ret = erofs_mkfs_go(inode->sbi, EROFS_MKFS_JOB_NDIR,
&ctx, sizeof(ctx));
} else {
- ret = erofs_rebuild_handle_directory(inode);
+ ret = erofs_rebuild_handle_directory(inode, incremental);
}
erofs_info("file %s dumped (mode %05o)", erofs_fspath(inode->i_srcpath),
inode->i_mode);
erofs_mark_parent_inode(root, root); /* rootdir mark */
root->next_dirwrite = NULL;
+ /* update dev/i_ino[1] to keep track of the base image */
+ if (incremental) {
+ root->dev = root->sbi->dev;
+ root->i_ino[1] = sbi->root_nid;
+ list_del(&root->i_hash);
+ erofs_insert_ihash(root);
+ }
err = !rebuild ? erofs_mkfs_handle_inode(root) :
- erofs_rebuild_handle_inode(root);
+ erofs_rebuild_handle_inode(root, incremental);
if (err)
return err;
list_for_each_entry(d, &dir->i_subdirs, d_child) {
struct erofs_inode *inode = d->inode;
- if (is_dot_dotdot(d->name))
+ if (is_dot_dotdot(d->name) || d->validnid)
continue;
if (!erofs_inode_visited(inode)) {
if (!rebuild)
err = erofs_mkfs_handle_inode(inode);
else
- err = erofs_rebuild_handle_inode(inode);
+ err = erofs_rebuild_handle_inode(inode,
+ incremental);
if (err)
break;
if (S_ISDIR(inode->i_mode)) {
char *ibuf;
int err;
- if (sbi->root_nid == root->nid)
+ if (sbi->root_nid == root->nid) /* for most mkfs cases */
return 0;
if (root->nid <= 0xffff) {
}
/*
- * @parent: parent directory in inode tree
- * @ctx.dir: parent directory when itering erofs_iterate_dir()
+ * @mergedir: parent directory in the merged tree
+ * @ctx.dir: parent directory when itering erofs_iterate_dir()
*/
struct erofs_rebuild_dir_context {
struct erofs_dir_context ctx;
- struct erofs_inode *parent;
+ struct erofs_inode *mergedir;
};
static int erofs_rebuild_dirent_iter(struct erofs_dir_context *ctx)
{
struct erofs_rebuild_dir_context *rctx = (void *)ctx;
- struct erofs_inode *parent = rctx->parent;
+ struct erofs_inode *mergedir = rctx->mergedir;
struct erofs_inode *dir = ctx->dir;
struct erofs_inode *inode, *candidate;
struct erofs_inode src;
if (ctx->dot_dotdot)
return 0;
- ret = asprintf(&path, "%s/%.*s", rctx->parent->i_srcpath,
+ ret = asprintf(&path, "%s/%.*s", rctx->mergedir->i_srcpath,
ctx->de_namelen, ctx->dname);
if (ret < 0)
return ret;
erofs_dbg("parsing %s", path);
- dname = path + strlen(parent->i_srcpath) + 1;
+ dname = path + strlen(mergedir->i_srcpath) + 1;
- d = erofs_rebuild_get_dentry(parent, dname, false,
+ d = erofs_rebuild_get_dentry(mergedir, dname, false,
&dumb, &dumb, false);
if (IS_ERR(d)) {
ret = PTR_ERR(d);
ret = erofs_read_inode_from_disk(&src);
if (ret || !S_ISDIR(src.i_mode))
goto out;
- parent = d->inode;
+ mergedir = d->inode;
inode = dir = &src;
} else {
u64 nid;
- DBG_BUGON(parent != d->inode);
+ DBG_BUGON(mergedir != d->inode);
inode = erofs_new_inode(dir->sbi);
if (IS_ERR(inode)) {
ret = PTR_ERR(inode);
}
erofs_insert_ihash(inode);
- parent = dir = inode;
+ mergedir = dir = inode;
}
d->inode = inode;
if (S_ISDIR(inode->i_mode)) {
struct erofs_rebuild_dir_context nctx = *rctx;
- nctx.parent = parent;
+ nctx.mergedir = mergedir;
nctx.ctx.dir = dir;
ret = erofs_iterate_dir(&nctx.ctx, false);
if (ret)
ctx = (struct erofs_rebuild_dir_context) {
.ctx.dir = &inode,
.ctx.cb = erofs_rebuild_dirent_iter,
- .parent = root,
+ .mergedir = root,
};
ret = erofs_iterate_dir(&ctx.ctx, false);
free(inode.i_srcpath);
return ret;
}
+
+static int erofs_rebuild_basedir_dirent_iter(struct erofs_dir_context *ctx)
+{
+ struct erofs_rebuild_dir_context *rctx = (void *)ctx;
+ struct erofs_inode *dir = ctx->dir;
+ struct erofs_inode *mergedir = rctx->mergedir;
+ struct erofs_dentry *d;
+ char *dname;
+ bool dumb;
+ int ret;
+
+ if (ctx->dot_dotdot)
+ return 0;
+
+ dname = strndup(ctx->dname, ctx->de_namelen);
+ if (!dname)
+ return -ENOMEM;
+ d = erofs_rebuild_get_dentry(mergedir, dname, false,
+ &dumb, &dumb, false);
+ if (IS_ERR(d)) {
+ ret = PTR_ERR(d);
+ goto out;
+ }
+
+ if (d->type == EROFS_FT_UNKNOWN) {
+ d->nid = ctx->de_nid;
+ d->type = ctx->de_ftype;
+ d->validnid = true;
+ if (!mergedir->whiteouts && erofs_dentry_is_wht(dir->sbi, d))
+ mergedir->whiteouts = true;
+ } else {
+ struct erofs_inode *inode = d->inode;
+
+ list_del(&inode->i_hash);
+ inode->dev = dir->sbi->dev;
+ inode->i_ino[1] = ctx->de_nid;
+ erofs_insert_ihash(inode);
+ }
+ ret = 0;
+out:
+ free(dname);
+ return ret;
+}
+
+int erofs_rebuild_load_basedir(struct erofs_inode *dir)
+{
+ struct erofs_inode fakeinode = {
+ .sbi = dir->sbi,
+ .nid = dir->i_ino[1],
+ };
+ struct erofs_rebuild_dir_context ctx;
+ int ret;
+
+ ret = erofs_read_inode_from_disk(&fakeinode);
+ if (ret) {
+ erofs_err("failed to read inode @ %llu", fakeinode.nid);
+ return ret;
+ }
+ ctx = (struct erofs_rebuild_dir_context) {
+ .ctx.dir = &fakeinode,
+ .ctx.cb = erofs_rebuild_basedir_dirent_iter,
+ .mergedir = dir,
+ };
+ return erofs_iterate_dir(&ctx.ctx, false);
+}