erofs-utils: mkfs: support inline xattr reservation for rootdirs
authorGao Xiang <hsiangkao@linux.alibaba.com>
Mon, 29 Jul 2024 07:50:27 +0000 (15:50 +0800)
committerGao Xiang <hsiangkao@linux.alibaba.com>
Tue, 30 Jul 2024 01:57:22 +0000 (09:57 +0800)
Due to the current on-disk limitation (16-bit on-disk root_nid), on-disk
root inodes must be updated in place for now.

If rootdir xattr sizes are expanded during incremental updates, there
may be insufficient space to keep additional extended attributes.

To work around this, let's add a mkfs option `--root-xattr-isize=#` to
specify the minimum inline xattr size of root directories in advance.

Reviewed-by: Sandeep Dhavale <dhavale@google.com>
Signed-off-by: Gao Xiang <hsiangkao@linux.alibaba.com>
Link: https://lore.kernel.org/r/20240729075027.712339-2-hsiangkao@linux.alibaba.com
include/erofs/config.h
include/erofs/xattr.h
lib/inode.c
lib/rebuild.c
lib/xattr.c
mkfs/main.c

index f8726b5ef4c952af9d1890701ea9fe08391033b0..56650e3b37c5ab65e8e2de148f87a72d75bcb368 100644 (file)
@@ -87,6 +87,7 @@ struct erofs_configure {
        u32 c_uid, c_gid;
        const char *mount_point;
        long long c_uid_offset, c_gid_offset;
+       u32 c_root_xattr_isize;
 #ifdef WITH_ANDROID
        char *target_out_path;
        char *fs_config_file;
index 0f76037e7f8f3c35fab0cf59c933b2721d697ff3..7643611d6c113518727c92a38c6abc0002775284 100644 (file)
@@ -44,7 +44,7 @@ static inline unsigned int xattrblock_offset(struct erofs_inode *vi,
        sizeof(struct erofs_xattr_entry) + 1; })
 
 int erofs_scan_file_xattrs(struct erofs_inode *inode);
-int erofs_prepare_xattr_ibody(struct erofs_inode *inode);
+int erofs_prepare_xattr_ibody(struct erofs_inode *inode, bool noroom);
 char *erofs_export_xattr_ibody(struct erofs_inode *inode);
 int erofs_build_shared_xattrs_from_path(struct erofs_sb_info *sbi, const char *path);
 
index 6aee76cdb91be1dc8cbbf5997bed0fcd66fbc248..b3547bfd905bc3d9e837092bdef31f3c650779ee 100644 (file)
@@ -1580,7 +1580,7 @@ static int erofs_mkfs_handle_inode(struct erofs_inode *inode)
        if (ret < 0)
                return ret;
 
-       ret = erofs_prepare_xattr_ibody(inode);
+       ret = erofs_prepare_xattr_ibody(inode, false);
        if (ret < 0)
                return ret;
 
@@ -1644,7 +1644,7 @@ static int erofs_rebuild_handle_inode(struct erofs_inode *inode,
        else if (inode->whiteouts)
                erofs_set_origin_xattr(inode);
 
-       ret = erofs_prepare_xattr_ibody(inode);
+       ret = erofs_prepare_xattr_ibody(inode, incremental && IS_ROOT(inode));
        if (ret < 0)
                return ret;
 
@@ -1702,6 +1702,8 @@ static int erofs_mkfs_dump_tree(struct erofs_inode *root, bool rebuild,
                root->i_ino[1] = sbi->root_nid;
                list_del(&root->i_hash);
                erofs_insert_ihash(root);
+       } else if (cfg.c_root_xattr_isize) {
+               root->xattr_isize = cfg.c_root_xattr_isize;
        }
 
        err = !rebuild ? erofs_mkfs_handle_inode(root) :
index 9e8bf8fd28f0ad760a592818bf40192fc69e59fc..08c1b86025cb08a64f9905a1d8d8f61534d29e7b 100644 (file)
@@ -492,6 +492,11 @@ int erofs_rebuild_load_basedir(struct erofs_inode *dir)
                erofs_err("failed to read inode @ %llu", fakeinode.nid);
                return ret;
        }
+
+       /* Inherit the maximum xattr size for the root directory */
+       if (__erofs_unlikely(IS_ROOT(dir)))
+               dir->xattr_isize = fakeinode.xattr_isize;
+
        ctx = (struct erofs_rebuild_dir_context) {
                .ctx.dir = &fakeinode,
                .ctx.cb = erofs_rebuild_basedir_dirent_iter,
index 563c6887cefdd71ec76037bc861c977670f0bdfe..f860f2e6642937f9c8fb768afb33a42f0f1ced7c 100644 (file)
@@ -659,16 +659,17 @@ static inline unsigned int erofs_next_xattr_align(unsigned int pos,
                        item->len[0] + item->len[1] - item->prefix_len);
 }
 
-int erofs_prepare_xattr_ibody(struct erofs_inode *inode)
+int erofs_prepare_xattr_ibody(struct erofs_inode *inode, bool noroom)
 {
-       int ret;
-       struct inode_xattr_node *node;
+       unsigned int target_xattr_isize = inode->xattr_isize;
        struct list_head *ixattrs = &inode->i_xattrs;
+       struct inode_xattr_node *node;
        unsigned int h_shared_count;
+       int ret;
 
        if (list_empty(ixattrs)) {
-               inode->xattr_isize = 0;
-               return 0;
+               ret = 0;
+               goto out;
        }
 
        /* get xattr ibody size */
@@ -684,6 +685,18 @@ int erofs_prepare_xattr_ibody(struct erofs_inode *inode)
                }
                ret = erofs_next_xattr_align(ret, item);
        }
+out:
+       while (ret < target_xattr_isize) {
+               ret += sizeof(struct erofs_xattr_entry);
+               if (ret < target_xattr_isize)
+                       ret = EROFS_XATTR_ALIGN(ret +
+                               min_t(int, target_xattr_isize - ret, UINT16_MAX));
+       }
+       if (noroom && target_xattr_isize && ret > target_xattr_isize) {
+               erofs_err("no enough space to keep xattrs @ nid %llu",
+                         inode->nid | 0ULL);
+               return -ENOSPC;
+       }
        inode->xattr_isize = ret;
        return ret;
 }
@@ -1003,7 +1016,12 @@ char *erofs_export_xattr_ibody(struct erofs_inode *inode)
                free(node);
                put_xattritem(item);
        }
-       DBG_BUGON(p > size);
+       if (p < size) {
+               memset(buf + p, 0, size - p);
+       } else if (__erofs_unlikely(p > size)) {
+               DBG_BUGON(1);
+               return ERR_PTR(-EFAULT);
+       }
        return buf;
 }
 
index 20f12fc7e82648833b09b9c4b8a12845f66194b9..f9ac4bd33b318562ea5ca4e1c6a2515d492d6a34 100644 (file)
@@ -81,6 +81,7 @@ static struct option long_options[] = {
        {"zfeature-bits", required_argument, NULL, 521},
        {"clean", optional_argument, NULL, 522},
        {"incremental", optional_argument, NULL, 523},
+       {"root-xattr-isize", required_argument, NULL, 524},
        {0, 0, 0, 0},
 };
 
@@ -173,6 +174,7 @@ static void usage(int argc, char **argv)
                " --mount-point=X       X=prefix of target fs path (default: /)\n"
                " --preserve-mtime      keep per-file modification time strictly\n"
                " --offset=#            skip # bytes at the beginning of IMAGE.\n"
+               " --root-xattr-isize=#  ensure the inline xattr size of the root directory is # bytes at least\n"
                " --aufs                replace aufs special files with overlayfs metadata\n"
                " --tar=X               generate a full or index-only image from a tarball(-ish) source\n"
                "                       (X = f|i|headerball; f=full mode, i=index mode,\n"
@@ -820,6 +822,13 @@ static int mkfs_parse_options_cfg(int argc, char *argv[])
                        }
                        incremental_mode = (opt == 523);
                        break;
+               case 524:
+                       cfg.c_root_xattr_isize = strtoull(optarg, &endptr, 0);
+                       if (*endptr != '\0') {
+                               erofs_err("invalid the minimum inline xattr size %s", optarg);
+                               return -EINVAL;
+                       }
+                       break;
                case 'V':
                        version();
                        exit(0);