From: Li Guifu <[email protected]>

Large xattrs or xattrs shared by a lot of files
can be stored in shared xattrs rather than
inlined right after inode.

Signed-off-by: Li Guifu <[email protected]>
Signed-off-by: Gao Xiang <[email protected]>
---
 include/erofs/config.h |   2 +
 include/erofs/xattr.h  |   1 +
 lib/config.c           |   1 +
 lib/xattr.c            | 193 ++++++++++++++++++++++++++++++++++++++++-
 mkfs/main.c            |  10 ++-
 5 files changed, 205 insertions(+), 2 deletions(-)

diff --git a/include/erofs/config.h b/include/erofs/config.h
index 8b09430..fde936c 100644
--- a/include/erofs/config.h
+++ b/include/erofs/config.h
@@ -28,6 +28,8 @@ struct erofs_configure {
        char *c_compr_alg_master;
        int c_compr_level_master;
        int c_force_inodeversion;
+       /* < 0, xattr disabled and INT_MAX, always use inline xattrs */
+       int c_inline_xattr_tolerance;
 };
 
 extern struct erofs_configure cfg;
diff --git a/include/erofs/xattr.h b/include/erofs/xattr.h
index 29df025..3dff1ea 100644
--- a/include/erofs/xattr.h
+++ b/include/erofs/xattr.h
@@ -44,5 +44,6 @@
 
 int erofs_prepare_xattr_ibody(const char *path, struct list_head *ixattrs);
 char *erofs_export_xattr_ibody(struct list_head *ixattrs, unsigned int size);
+int erofs_build_shared_xattrs_from_path(const char *path);
 
 #endif
diff --git a/lib/config.c b/lib/config.c
index 110c8b6..dc10754 100644
--- a/lib/config.c
+++ b/lib/config.c
@@ -23,6 +23,7 @@ void erofs_init_configure(void)
        cfg.c_compr_level_master = -1;
        sbi.feature_incompat = EROFS_FEATURE_INCOMPAT_LZ4_0PADDING;
        cfg.c_force_inodeversion = 0;
+       cfg.c_inline_xattr_tolerance = 2;
 }
 
 void erofs_show_config(void)
diff --git a/lib/xattr.c b/lib/xattr.c
index 8156f3e..781d210 100644
--- a/lib/xattr.c
+++ b/lib/xattr.c
@@ -6,20 +6,26 @@
  * heavily changed by Li Guifu <[email protected]>
  *                and Gao Xiang <[email protected]>
  */
+#define _GNU_SOURCE
+#include <limits.h>
 #include <stdlib.h>
 #include <sys/xattr.h>
 #ifdef HAVE_LINUX_XATTR_H
 #include <linux/xattr.h>
 #endif
+#include <sys/stat.h>
+#include <dirent.h>
 #include "erofs/print.h"
 #include "erofs/hashtable.h"
 #include "erofs/xattr.h"
+#include "erofs/cache.h"
 
 #define EA_HASHTABLE_BITS 16
 
 struct xattr_item {
        const char *kvbuf;
        unsigned int hash[2], len[2], count;
+       int shared_xattr_id;
        u8 prefix;
        struct hlist_node node;
 };
@@ -31,6 +37,9 @@ struct inode_xattr_node {
 
 static DECLARE_HASHTABLE(ea_hashtable, EA_HASHTABLE_BITS);
 
+static LIST_HEAD(shared_xattrs_list);
+static unsigned int shared_xattrs_count, shared_xattrs_size;
+
 static struct xattr_prefix {
        const char *prefix;
        u16 prefix_len;
@@ -105,6 +114,7 @@ static struct xattr_item *xattr_item_get(u8 prefix, char 
*kvbuf,
        item->len[1] = len[1];
        item->hash[0] = hash[0];
        item->hash[1] = hash[1];
+       item->shared_xattr_id = -1;
        item->prefix = prefix;
        hash_add(ea_hashtable, &item->node, hkey);
        return item;
@@ -152,7 +162,6 @@ static struct xattr_item *parse_one_xattr(const char *path, 
const char *key,
        kvbuf = malloc(len[0] + len[1]);
        if (!kvbuf)
                return ERR_PTR(-ENOMEM);
-
        memcpy(kvbuf, key + prefixlen, len[0]);
        if (len[1]) {
                /* copy value to buffer */
@@ -182,6 +191,23 @@ static int inode_xattr_add(struct list_head *hlist, struct 
xattr_item *item)
        return 0;
 }
 
+static int shared_xattr_add(struct xattr_item *item)
+{
+       struct inode_xattr_node *node = malloc(sizeof(*node));
+
+       if (!node)
+               return -ENOMEM;
+
+       init_list_head(&node->list);
+       node->item = item;
+       list_add(&node->list, &shared_xattrs_list);
+
+       shared_xattrs_size += sizeof(struct erofs_xattr_entry);
+       shared_xattrs_size = EROFS_XATTR_ALIGN(shared_xattrs_size +
+                                              item->len[0] + item->len[1]);
+       return ++shared_xattrs_count;
+}
+
 static int read_xattrs_from_file(const char *path, struct list_head *ixattrs)
 {
        int ret = 0;
@@ -227,6 +253,11 @@ static int read_xattrs_from_file(const char *path, struct 
list_head *ixattrs)
                        ret = inode_xattr_add(ixattrs, item);
                        if (ret < 0)
                                goto err;
+               } else if (item->count == cfg.c_inline_xattr_tolerance + 1) {
+                       ret = shared_xattr_add(item);
+                       if (ret < 0)
+                               goto err;
+                       ret = 0;
                }
                kllen -= keylen + 1;
                key += keylen + 1;
@@ -242,6 +273,10 @@ int erofs_prepare_xattr_ibody(const char *path, struct 
list_head *ixattrs)
        int ret;
        struct inode_xattr_node *node;
 
+       /* check if xattr is disabled */
+       if (cfg.c_inline_xattr_tolerance < 0)
+               return 0;
+
        ret = read_xattrs_from_file(path, ixattrs);
        if (ret < 0)
                return ret;
@@ -254,16 +289,155 @@ int erofs_prepare_xattr_ibody(const char *path, struct 
list_head *ixattrs)
        list_for_each_entry(node, ixattrs, list) {
                const struct xattr_item *item = node->item;
 
+               if (item->shared_xattr_id >= 0) {
+                       ret += sizeof(__le32);
+                       continue;
+               }
                ret += sizeof(struct erofs_xattr_entry);
                ret = EROFS_XATTR_ALIGN(ret + item->len[0] + item->len[1]);
        }
        return ret;
 }
 
+static int erofs_count_all_xattrs_from_path(const char *path)
+{
+       int ret;
+       DIR *_dir;
+       struct stat64 st;
+
+       _dir = opendir(path);
+       if (!_dir) {
+               erofs_err("%s, failed to opendir at %s: %s",
+                         __func__, path, erofs_strerror(errno));
+               return -errno;
+       }
+
+       ret = 0;
+       while (1) {
+               struct dirent *dp;
+               char buf[PATH_MAX];
+
+               /*
+                * set errno to 0 before calling readdir() in order to
+                * distinguish end of stream and from an error.
+                */
+               errno = 0;
+               dp = readdir(_dir);
+               if (!dp)
+                       break;
+
+               if (is_dot_dotdot(dp->d_name) ||
+                   !strncmp(dp->d_name, "lost+found", strlen("lost+found")))
+                       continue;
+
+               ret = snprintf(buf, PATH_MAX, "%s/%s", path, dp->d_name);
+
+               if (ret < 0 || ret >= PATH_MAX) {
+                       /* ignore the too long path */
+                       ret = -ENOMEM;
+                       goto fail;
+               }
+
+               ret = read_xattrs_from_file(buf, NULL);
+               if (ret)
+                       goto fail;
+
+               ret = lstat64(buf, &st);
+               if (ret) {
+                       ret = -errno;
+                       goto fail;
+               }
+
+               if (!S_ISDIR(st.st_mode))
+                       continue;
+
+               ret = erofs_count_all_xattrs_from_path(buf);
+               if (ret)
+                       goto fail;
+       }
+
+       if (errno)
+               ret = -errno;
+
+fail:
+       closedir(_dir);
+       return ret;
+}
+
+int erofs_build_shared_xattrs_from_path(const char *path)
+{
+       int ret;
+       struct erofs_buffer_head *bh;
+       struct inode_xattr_node *node, *n;
+       char *buf;
+       unsigned int p;
+       erofs_off_t off;
+
+       /* check if xattr or shared xattr is disabled */
+       if (cfg.c_inline_xattr_tolerance < 0 ||
+           cfg.c_inline_xattr_tolerance == INT_MAX)
+               return 0;
+
+       if (shared_xattrs_size || shared_xattrs_count) {
+               DBG_BUGON(1);
+               return -EINVAL;
+       }
+
+       ret = erofs_count_all_xattrs_from_path(path);
+       if (ret)
+               return ret;
+
+       if (!shared_xattrs_size)
+               return 0;
+
+       buf = malloc(shared_xattrs_size);
+       if (!buf)
+               return -ENOMEM;
+
+       bh = erofs_balloc(XATTR, shared_xattrs_size, 0, 0);
+       if (IS_ERR(bh)) {
+               free(buf);
+               return PTR_ERR(bh);
+       }
+       bh->op = &erofs_skip_write_bhops;
+
+       erofs_mapbh(bh->block, true);
+       off = erofs_btell(bh, false);
+
+       sbi.xattr_blkaddr = off / EROFS_BLKSIZ;
+       off %= EROFS_BLKSIZ;
+       p = 0;
+
+       list_for_each_entry_safe(node, n, &shared_xattrs_list, list) {
+               struct xattr_item *const item = node->item;
+               const struct erofs_xattr_entry entry = {
+                       .e_name_index = item->prefix,
+                       .e_name_len = item->len[0],
+                       .e_value_size = cpu_to_le16(item->len[1])
+               };
+
+               list_del(&node->list);
+
+               item->shared_xattr_id = (off + p) /
+                       sizeof(struct erofs_xattr_entry);
+
+               memcpy(buf + p, &entry, sizeof(entry));
+               p += sizeof(struct erofs_xattr_entry);
+               memcpy(buf + p, item->kvbuf, item->len[0] + item->len[1]);
+               p = EROFS_XATTR_ALIGN(p + item->len[0] + item->len[1]);
+               free(node);
+       }
+
+       bh->fsprivate = buf;
+       bh->op = &erofs_buf_write_bhops;
+       return 0;
+}
+
 char *erofs_export_xattr_ibody(struct list_head *ixattrs, unsigned int size)
 {
        struct inode_xattr_node *node, *n;
        struct erofs_xattr_ibody_header *header;
+       LIST_HEAD(ilst);
        unsigned int p;
        char *buf = calloc(1, size);
 
@@ -276,6 +450,23 @@ char *erofs_export_xattr_ibody(struct list_head *ixattrs, 
unsigned int size)
        p = sizeof(struct erofs_xattr_ibody_header);
        list_for_each_entry_safe(node, n, ixattrs, list) {
                struct xattr_item *const item = node->item;
+
+               list_del(&node->list);
+
+               /* move inline xattrs to the onstack list */
+               if (item->shared_xattr_id < 0) {
+                       list_add(&node->list, &ilst);
+                       continue;
+               }
+
+               *(__le32 *)(buf + p) = cpu_to_le32(item->shared_xattr_id);
+               p += sizeof(__le32);
+               ++header->h_shared_count;
+               free(node);
+       }
+
+       list_for_each_entry_safe(node, n, &ilst, list) {
+               struct xattr_item *const item = node->item;
                const struct erofs_xattr_entry entry = {
                        .e_name_index = item->prefix,
                        .e_name_len = item->len[0],
diff --git a/mkfs/main.c b/mkfs/main.c
index 4b279c0..978c5b4 100644
--- a/mkfs/main.c
+++ b/mkfs/main.c
@@ -19,6 +19,7 @@
 #include "erofs/inode.h"
 #include "erofs/io.h"
 #include "erofs/compress.h"
+#include "erofs/xattr.h"
 
 #define EROFS_SUPER_END (EROFS_SUPER_OFFSET + sizeof(struct erofs_super_block))
 
@@ -169,7 +170,7 @@ int erofs_mkfs_update_super_block(struct erofs_buffer_head 
*bh,
                .build_time_nsec = cpu_to_le32(sbi.build_time_nsec),
                .blocks = 0,
                .meta_blkaddr  = sbi.meta_blkaddr,
-               .xattr_blkaddr = 0,
+               .xattr_blkaddr = sbi.xattr_blkaddr,
                .feature_incompat = cpu_to_le32(sbi.feature_incompat),
        };
        const unsigned int sb_blksize =
@@ -259,6 +260,13 @@ int main(int argc, char **argv)
 
        erofs_inode_manager_init();
 
+       err = erofs_build_shared_xattrs_from_path(cfg.c_src_path);
+       if (err) {
+               erofs_err("Failed to build shared xattrs: %s",
+                         erofs_strerror(err));
+               goto exit;
+       }
+
        root_inode = erofs_mkfs_build_tree_from_path(NULL, cfg.c_src_path);
        if (IS_ERR(root_inode)) {
                err = PTR_ERR(root_inode);
-- 
2.17.1

Reply via email to