Let's drop the legacy approach and `tarerofs` will be applied too.

Signed-off-by: Gao Xiang <hsiang...@linux.alibaba.com>
---
 include/erofs/internal.h |   7 +-
 lib/diskbuf.c            |  14 +-
 lib/inode.c              | 384 ++++++++++++++++++++++-----------------
 3 files changed, 232 insertions(+), 173 deletions(-)

diff --git a/include/erofs/internal.h b/include/erofs/internal.h
index ecbbdf6..46345e0 100644
--- a/include/erofs/internal.h
+++ b/include/erofs/internal.h
@@ -290,7 +290,12 @@ static inline unsigned int erofs_inode_datalayout(unsigned 
int value)
                              EROFS_I_DATALAYOUT_BITS);
 }
 
-#define IS_ROOT(x)     ((x) == (x)->i_parent)
+static inline struct erofs_inode *erofs_parent_inode(struct erofs_inode *inode)
+{
+       return (void *)((unsigned long)inode->i_parent & ~1UL);
+}
+
+#define IS_ROOT(x)     ((x) == erofs_parent_inode(x))
 
 struct erofs_dentry {
        struct list_head d_child;       /* child of parent list */
diff --git a/lib/diskbuf.c b/lib/diskbuf.c
index 8205ba5..e5889df 100644
--- a/lib/diskbuf.c
+++ b/lib/diskbuf.c
@@ -10,7 +10,7 @@
 
 /* A simple approach to avoid creating too many temporary files */
 static struct erofs_diskbufstrm {
-       u64 count;
+       erofs_atomic_t count;
        u64 tailoffset, devpos;
        int fd;
        unsigned int alignsize;
@@ -25,8 +25,6 @@ int erofs_diskbuf_getfd(struct erofs_diskbuf *db, u64 *fpos)
        if (!strm)
                return -1;
        offset = db->offset + strm->devpos;
-       if (lseek(strm->fd, offset, SEEK_SET) != offset)
-               return -E2BIG;
        if (fpos)
                *fpos = offset;
        return strm->fd;
@@ -46,7 +44,7 @@ int erofs_diskbuf_reserve(struct erofs_diskbuf *db, int sid, 
u64 *off)
        if (off)
                *off = db->offset + strm->devpos;
        db->sp = strm;
-       ++strm->count;
+       (void)erofs_atomic_inc_return(&strm->count);
        strm->locked = true;    /* TODO: need a real lock for MT */
        return strm->fd;
 }
@@ -66,8 +64,8 @@ void erofs_diskbuf_close(struct erofs_diskbuf *db)
        struct erofs_diskbufstrm *strm = db->sp;
 
        DBG_BUGON(!strm);
-       DBG_BUGON(strm->count <= 1);
-       --strm->count;
+       DBG_BUGON(erofs_atomic_read(&strm->count) <= 1);
+       (void)erofs_atomic_dec_return(&strm->count);
        db->sp = NULL;
 }
 
@@ -122,7 +120,7 @@ int erofs_diskbuf_init(unsigned int nstrms)
                        return -ENOSPC;
 setupone:
                strm->tailoffset = 0;
-               strm->count = 1;
+               erofs_atomic_set(&strm->count, 1);
                if (fstat(strm->fd, &st))
                        return -errno;
                strm->alignsize = max_t(u32, st.st_blksize, getpagesize());
@@ -138,7 +136,7 @@ void erofs_diskbuf_exit(void)
                return;
 
        for (strm = dbufstrm; strm->fd >= 0; ++strm) {
-               DBG_BUGON(strm->count != 1);
+               DBG_BUGON(erofs_atomic_read(&strm->count) != 1);
 
                close(strm->fd);
                strm->fd = -1;
diff --git a/lib/inode.c b/lib/inode.c
index 44d684f..8ec87e6 100644
--- a/lib/inode.c
+++ b/lib/inode.c
@@ -264,7 +264,7 @@ int erofs_init_empty_dir(struct erofs_inode *dir)
        d = erofs_d_alloc(dir, "..");
        if (IS_ERR(d))
                return PTR_ERR(d);
-       d->inode = erofs_igrab(dir->i_parent);
+       d->inode = erofs_igrab(erofs_parent_inode(dir));
        d->type = EROFS_FT_DIR;
 
        dir->i_nlink = 2;
@@ -494,29 +494,6 @@ int erofs_write_unencoded_file(struct erofs_inode *inode, 
int fd, u64 fpos)
        return write_uncompressed_file_from_fd(inode, fd);
 }
 
-int erofs_write_file(struct erofs_inode *inode, int fd, u64 fpos)
-{
-       DBG_BUGON(!inode->i_size);
-
-       if (cfg.c_compr_opts[0].alg && erofs_file_is_compressible(inode)) {
-               void *ictx;
-               int ret;
-
-               ictx = erofs_begin_compressed_file(inode, fd, fpos);
-               if (IS_ERR(ictx))
-                       return PTR_ERR(ictx);
-
-               ret = erofs_write_compressed_file(ictx);
-               if (ret != -ENOSPC)
-                       return ret;
-
-               if (lseek(fd, fpos, SEEK_SET) < 0)
-                       return -errno;
-       }
-       /* fallback to all data uncompressed */
-       return erofs_write_unencoded_file(inode, fd, fpos);
-}
-
 static int erofs_bh_flush_write_inode(struct erofs_buffer_head *bh)
 {
        struct erofs_inode *const inode = bh->fsprivate;
@@ -1113,6 +1090,7 @@ struct erofs_mkfs_job_ndir_ctx {
        struct erofs_inode *inode;
        void *ictx;
        int fd;
+       u64 fpos;
 };
 
 static int erofs_mkfs_job_write_file(struct erofs_mkfs_job_ndir_ctx *ctx)
@@ -1121,18 +1099,31 @@ static int erofs_mkfs_job_write_file(struct 
erofs_mkfs_job_ndir_ctx *ctx)
        int ret;
 
        if (ctx->ictx) {
+               if (inode->with_diskbuf &&
+                   lseek(ctx->fd, ctx->fpos, SEEK_SET) < 0) {
+                       ret = -errno;
+                       goto out;
+               }
+
                ret = erofs_write_compressed_file(ctx->ictx);
                if (ret != -ENOSPC)
                        goto out;
-               if (lseek(ctx->fd, 0, SEEK_SET) < 0) {
+               if (lseek(ctx->fd, ctx->fpos, SEEK_SET) < 0) {
                        ret = -errno;
                        goto out;
                }
        }
        /* fallback to all data uncompressed */
-       ret = erofs_write_unencoded_file(inode, ctx->fd, 0);
+       ret = erofs_write_unencoded_file(inode, ctx->fd, ctx->fpos);
 out:
-       close(ctx->fd);
+       if (inode->with_diskbuf) {
+               erofs_diskbuf_close(inode->i_diskbuf);
+               free(inode->i_diskbuf);
+               inode->i_diskbuf = NULL;
+               inode->with_diskbuf = false;
+       } else {
+               close(ctx->fd);
+       }
        return ret;
 }
 
@@ -1142,17 +1133,21 @@ static int erofs_mkfs_handle_nondirectory(struct 
erofs_mkfs_job_ndir_ctx *ctx)
        int ret = 0;
 
        if (S_ISLNK(inode->i_mode)) {
-               char *const symlink = malloc(inode->i_size);
-
-               if (!symlink)
-                       return -ENOMEM;
-               ret = readlink(inode->i_srcpath, symlink, inode->i_size);
-               if (ret < 0) {
-                       free(symlink);
-                       return -errno;
+               char *symlink = inode->i_link;
+
+               if (!symlink) {
+                       symlink = malloc(inode->i_size);
+                       if (!symlink)
+                               return -ENOMEM;
+                       ret = readlink(inode->i_srcpath, symlink, 
inode->i_size);
+                       if (ret < 0) {
+                               free(symlink);
+                               return -errno;
+                       }
                }
                ret = erofs_write_file_from_buffer(inode, symlink);
                free(symlink);
+               inode->i_link = NULL;
        } else if (inode->i_size) {
                ret = erofs_mkfs_job_write_file(ctx);
        }
@@ -1371,6 +1366,43 @@ err_closedir:
        return ret;
 }
 
+static int erofs_rebuild_handle_directory(struct erofs_inode *dir)
+{
+       struct erofs_dentry *d, *n;
+       unsigned int nr_subdirs, i_nlink;
+       int ret;
+
+       nr_subdirs = 0;
+       i_nlink = 2;
+       list_for_each_entry_safe(d, n, &dir->i_subdirs, d_child) {
+               if (cfg.c_ovlfs_strip && erofs_inode_is_whiteout(d->inode)) {
+                       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);
+               ++nr_subdirs;
+       }
+
+       ret = erofs_prepare_dir_layout(dir, nr_subdirs);
+       if (ret)
+               return ret;
+
+       /*
+        * if there're too many subdirs as compact form, set nlink=1
+        * rather than upgrade to use extented form instead.
+        */
+       if (i_nlink > USHRT_MAX &&
+           dir->inode_isize == sizeof(struct erofs_inode_compact))
+               dir->i_nlink = 1;
+       else
+               dir->i_nlink = i_nlink;
+
+       return erofs_mkfs_go(dir->sbi, EROFS_MKFS_JOB_DIR, &dir, sizeof(dir));
+}
+
 static int erofs_mkfs_handle_inode(struct erofs_inode *inode)
 {
        char *trimmed;
@@ -1415,27 +1447,89 @@ static int erofs_mkfs_handle_inode(struct erofs_inode 
*inode)
        return ret;
 }
 
-#ifndef EROFS_MT_ENABLED
-#define __erofs_mkfs_build_tree_from_path erofs_mkfs_build_tree_from_path
-#endif
+static int erofs_rebuild_handle_inode(struct erofs_inode *inode)
+{
+       char *trimmed;
+       int ret;
+
+       trimmed = erofs_trim_for_progressinfo(erofs_fspath(inode->i_srcpath),
+                                             sizeof("Processing  ...") - 1);
+       erofs_update_progressinfo("Processing %s ...", trimmed);
+       free(trimmed);
+
+       if (erofs_should_use_inode_extended(inode)) {
+               if (cfg.c_force_inodeversion == FORCE_INODE_COMPACT) {
+                       erofs_err("file %s cannot be in compact form",
+                                 inode->i_srcpath);
+                       return -EINVAL;
+               }
+               inode->inode_isize = sizeof(struct erofs_inode_extended);
+       } else {
+               inode->inode_isize = sizeof(struct erofs_inode_compact);
+       }
+
+       /* strip all unnecessary overlayfs xattrs when ovlfs_strip is enabled */
+       if (cfg.c_ovlfs_strip)
+               erofs_clear_opaque_xattr(inode);
+       else if (inode->whiteouts)
+               erofs_set_origin_xattr(inode);
+
+       ret = erofs_prepare_xattr_ibody(inode);
+       if (ret < 0)
+               return ret;
+
+       if (!S_ISDIR(inode->i_mode)) {
+               struct erofs_mkfs_job_ndir_ctx ctx = { .inode = inode };
 
-struct erofs_inode *__erofs_mkfs_build_tree_from_path(const char *path)
+               if (!S_ISLNK(inode->i_mode) && inode->i_size) {
+                       DBG_BUGON(!inode->with_diskbuf);
+                       ctx.fd = erofs_diskbuf_getfd(inode->i_diskbuf, 
&ctx.fpos);
+                       if (ctx.fd < 0)
+                               return ret;
+
+                       if (cfg.c_compr_opts[0].alg &&
+                           erofs_file_is_compressible(inode)) {
+                               ctx.ictx = erofs_begin_compressed_file(inode,
+                                                       ctx.fd, ctx.fpos);
+                               if (IS_ERR(ctx.ictx))
+                                       return PTR_ERR(ctx.ictx);
+                       }
+               }
+               ret = erofs_mkfs_go(inode->sbi, EROFS_MKFS_JOB_NDIR,
+                                   &ctx, sizeof(ctx));
+       } else {
+               ret = erofs_rebuild_handle_directory(inode);
+       }
+       erofs_info("file %s dumped (mode %05o)", erofs_fspath(inode->i_srcpath),
+                  inode->i_mode);
+       return ret;
+}
+
+static bool erofs_inode_visited(struct erofs_inode *inode)
 {
-       struct erofs_inode *root, *dumpdir;
-       int err;
+       return (unsigned long)inode->i_parent & 1UL;
+}
 
-       root = erofs_iget_from_path(path, true);
-       if (IS_ERR(root))
-               return root;
+static void erofs_mark_parent_inode(struct erofs_inode *inode,
+                                   struct erofs_inode *dir)
+{
+       inode->i_parent = (void *)((unsigned long)dir | 1);
+}
+
+static int erofs_mkfs_dump_tree(struct erofs_inode *root, bool rebuild)
+{
+       struct erofs_inode *dumpdir;
+       int err;
 
-       root->i_parent = root;  /* rootdir mark */
+       erofs_mark_parent_inode(root, root);    /* rootdir mark */
        root->next_dirwrite = NULL;
        (void)erofs_igrab(root);
        dumpdir = root;
 
-       err = erofs_mkfs_handle_inode(root);
+       err = !rebuild ? erofs_mkfs_handle_inode(root) :
+                       erofs_rebuild_handle_inode(root);
        if (err)
-               return ERR_PTR(err);
+               return err;
 
        do {
                int err;
@@ -1450,21 +1544,25 @@ struct erofs_inode 
*__erofs_mkfs_build_tree_from_path(const char *path)
 
                        if (is_dot_dotdot(d->name))
                                continue;
-                       if (inode->i_parent) {
-                               ++inode->i_nlink;
-                       } else {
-                               inode->i_parent = dir;
 
-                               err = erofs_mkfs_handle_inode(inode);
-                               if (err) {
-                                       root = ERR_PTR(err);
+                       if (!erofs_inode_visited(inode)) {
+                               DBG_BUGON(rebuild &&
+                                         erofs_parent_inode(inode) != dir);
+                               erofs_mark_parent_inode(inode, dir);
+
+                               if (!rebuild)
+                                       err = erofs_mkfs_handle_inode(inode);
+                               else
+                                       err = erofs_rebuild_handle_inode(inode);
+                               if (err)
                                        break;
-                               }
                                if (S_ISDIR(inode->i_mode)) {
                                        *last = inode;
                                        last = &inode->next_dirwrite;
                                        (void)erofs_igrab(inode);
                                }
+                       } else if (!rebuild) {
+                               ++inode->i_nlink;
                        }
                }
                *last = dumpdir;        /* fixup the last (or the only) one */
@@ -1472,28 +1570,61 @@ struct erofs_inode 
*__erofs_mkfs_build_tree_from_path(const char *path)
                err = erofs_mkfs_go(dir->sbi, EROFS_MKFS_JOB_DIR_BH,
                                    &dir, sizeof(dir));
                if (err)
-                       return ERR_PTR(err);
+                       return err;
        } while (dumpdir);
 
-       return root;
+       return err;
 }
 
-#ifdef EROFS_MT_ENABLED
-struct erofs_inode *erofs_mkfs_build_tree_from_path(const char *path)
+struct erofs_mkfs_buildtree_ctx {
+       union {
+               const char *path;
+               struct erofs_inode *root;
+       } u;
+       bool from_path;
+};
+#ifndef EROFS_MT_ENABLED
+#define __erofs_mkfs_build_tree erofs_mkfs_build_tree
+#endif
+
+static int __erofs_mkfs_build_tree(struct erofs_mkfs_buildtree_ctx *ctx)
 {
-       struct erofs_mkfs_dfops *q;
        struct erofs_inode *root;
        int err;
 
+       if (ctx->from_path) {
+               root = erofs_iget_from_path(ctx->u.path, true);
+               if (IS_ERR(root))
+                       return PTR_ERR(root);
+       } else {
+               root = ctx->u.root;
+       }
+
+       err = erofs_mkfs_dump_tree(root, !ctx->from_path);
+       if (err) {
+               if (ctx->from_path)
+                       erofs_iput(root);
+               return err;
+       }
+       ctx->u.root = root;
+       return 0;
+}
+
+#ifdef EROFS_MT_ENABLED
+static int erofs_mkfs_build_tree(struct erofs_mkfs_buildtree_ctx *ctx)
+{
+       struct erofs_mkfs_dfops *q;
+       int err, err2;
+
        q = malloc(sizeof(*q));
        if (!q)
-               return ERR_PTR(-ENOMEM);
+               return -ENOMEM;
 
        q->entries = EROFS_MT_QUEUE_SIZE;
        q->queue = malloc(q->entries * sizeof(*q->queue));
        if (!q->queue) {
                free(q);
-               return ERR_PTR(-ENOMEM);
+               return -ENOMEM;
        }
        pthread_mutex_init(&q->lock, NULL);
        pthread_cond_init(&q->empty, NULL);
@@ -1506,10 +1637,12 @@ struct erofs_inode 
*erofs_mkfs_build_tree_from_path(const char *path)
                             z_erofs_mt_dfops_worker, &sbi);
        if (err)
                goto fail;
-       root = __erofs_mkfs_build_tree_from_path(path);
 
+       err = __erofs_mkfs_build_tree(ctx);
        erofs_mkfs_go(&sbi, ~0, NULL, 0);
-       err = pthread_join(sbi.dfops_worker, NULL);
+       err2 = pthread_join(sbi.dfops_worker, NULL);
+       if (!err)
+               err = err2;
 
 fail:
        pthread_cond_destroy(&q->empty);
@@ -1517,10 +1650,32 @@ fail:
        pthread_mutex_destroy(&q->lock);
        free(q->queue);
        free(q);
-       return err ? ERR_PTR(err) : root;
+       return err;
 }
 #endif
 
+struct erofs_inode *erofs_mkfs_build_tree_from_path(const char *path)
+{
+       struct erofs_mkfs_buildtree_ctx ctx = {
+               .from_path = true,
+               .u.path = path,
+       };
+       int err;
+
+       err = erofs_mkfs_build_tree(&ctx);
+       if (err)
+               return ERR_PTR(err);
+       return ctx.u.root;
+}
+
+int erofs_rebuild_dump_tree(struct erofs_inode *root)
+{
+       return erofs_mkfs_build_tree(&((struct erofs_mkfs_buildtree_ctx) {
+               .from_path = false,
+               .u.root = root,
+       }));
+}
+
 struct erofs_inode *erofs_mkfs_build_special_from_fd(int fd, const char *name)
 {
        struct stat st;
@@ -1578,102 +1733,3 @@ struct erofs_inode 
*erofs_mkfs_build_special_from_fd(int fd, const char *name)
        erofs_write_tail_end(inode);
        return inode;
 }
-
-int erofs_rebuild_dump_tree(struct erofs_inode *dir)
-{
-       struct erofs_dentry *d, *n;
-       unsigned int nr_subdirs;
-       int ret;
-
-       if (erofs_should_use_inode_extended(dir)) {
-               if (cfg.c_force_inodeversion == FORCE_INODE_COMPACT) {
-                       erofs_err("file %s cannot be in compact form",
-                                 dir->i_srcpath);
-                       return -EINVAL;
-               }
-               dir->inode_isize = sizeof(struct erofs_inode_extended);
-       } else {
-               dir->inode_isize = sizeof(struct erofs_inode_compact);
-       }
-
-       /* strip all unnecessary overlayfs xattrs when ovlfs_strip is enabled */
-       if (cfg.c_ovlfs_strip)
-               erofs_clear_opaque_xattr(dir);
-       else if (dir->whiteouts)
-               erofs_set_origin_xattr(dir);
-
-       ret = erofs_prepare_xattr_ibody(dir);
-       if (ret < 0)
-               return ret;
-
-       if (!S_ISDIR(dir->i_mode)) {
-               if (dir->bh)
-                       return 0;
-               if (S_ISLNK(dir->i_mode)) {
-                       ret = erofs_write_file_from_buffer(dir, dir->i_link);
-                       free(dir->i_link);
-                       dir->i_link = NULL;
-               } else if (dir->with_diskbuf) {
-                       u64 fpos;
-
-                       ret = erofs_diskbuf_getfd(dir->i_diskbuf, &fpos);
-                       if (ret >= 0)
-                               ret = erofs_write_file(dir, ret, fpos);
-                       erofs_diskbuf_close(dir->i_diskbuf);
-                       free(dir->i_diskbuf);
-                       dir->i_diskbuf = NULL;
-                       dir->with_diskbuf = false;
-               } else {
-                       ret = 0;
-               }
-               if (ret)
-                       return ret;
-               ret = erofs_prepare_inode_buffer(dir);
-               if (ret)
-                       return ret;
-               erofs_write_tail_end(dir);
-               return 0;
-       }
-
-       nr_subdirs = 0;
-       list_for_each_entry_safe(d, n, &dir->i_subdirs, d_child) {
-               if (cfg.c_ovlfs_strip && erofs_inode_is_whiteout(d->inode)) {
-                       erofs_dbg("remove whiteout %s", d->inode->i_srcpath);
-                       list_del(&d->d_child);
-                       erofs_d_invalidate(d);
-                       free(d);
-                       continue;
-               }
-               ++nr_subdirs;
-       }
-
-       ret = erofs_prepare_dir_layout(dir, nr_subdirs);
-       if (ret)
-               return ret;
-
-       ret = erofs_prepare_inode_buffer(dir);
-       if (ret)
-               return ret;
-       dir->bh->op = &erofs_skip_write_bhops;
-
-       if (IS_ROOT(dir))
-               erofs_fixup_meta_blkaddr(dir);
-
-       list_for_each_entry(d, &dir->i_subdirs, d_child) {
-               struct erofs_inode *inode;
-
-               if (is_dot_dotdot(d->name))
-                       continue;
-
-               inode = erofs_igrab(d->inode);
-               ret = erofs_rebuild_dump_tree(inode);
-               dir->i_nlink += (erofs_mode_to_ftype(inode->i_mode) == 
EROFS_FT_DIR);
-               erofs_iput(inode);
-               if (ret)
-                       return ret;
-       }
-       erofs_write_dir_file(dir);
-       erofs_write_tail_end(dir);
-       dir->bh->op = &erofs_write_inode_bhops;
-       return 0;
-}
-- 
2.39.3

Reply via email to