From: Gou Hao gouhao@uniontech.com
Patch series "Add extended attributes support for EulerFS".
I refer to the extended attribute implementation code of ext2 file system. At first, I implemented it completely according to the extended attribute of ext file system. But later, I changed the disk layout of entries.
About layout of entry: The layout of ext2's extended attribute is that name and value are stored separately,with name growing from top to bottom and value growing from bottom to top. I think the layout of ext2 wastes space, because, Name and value are aligned with 4 bytes, In the worst case, the name and value of entry need 3 bytes of padding respectively, and a total of 6 bytes are wasted.
I store the name and value together. In the worst case, only need 3 bytes of padding.Use xattr-test.c selftests, with the same block size, my method can store 112 entries, while using ext2 layout can only store 102 entries.
In ext4 file system, value can be stored in different blocks, which increases the scalability. In my method, it can add a pointer to the next block in the header to achieve the same function, and it is simpler.
The performance of my method is the same as that of ext2, or even better.
About block share: In ext2, if the extended attributes of two files are exactly the same, they can share the same disk block. Its implementation process is: 1. When accessing the extended attribute of a file, add this extended attribute block to a specific cache.
2. When setting the extended attribute of a file, if there is a block with exactly the same content as this extended attribute in the cache, share that block directly.
There is a problem here. If the block of a file is not added to the cache, the extended attribute block will not be shared.So in this patch set, I didn't realize the shared disk block. I'll implement it later when there is a good method or when there is a need.
At present, only user. prefix xattr is implemented. Trusted, ACL and security will be implemented later.
Gou Hao (7): eulerfs: add EULER_FS_XATTR config eulerfs: add related fields of extended attributes eulerfs: add alloc xattr block interface eulerfs: implement extended attribute core functions eulerfs: add callback interface of extended attribute eulerfs: add implementation of user. prefix extended attribute eulerfs: add selftests for eulerfs extended attribute
fs/eulerfs/Kconfig | 9 + fs/eulerfs/Makefile | 1 + fs/eulerfs/alloc_interface.h | 4 + fs/eulerfs/const.h | 1 + fs/eulerfs/euler.h | 1 + fs/eulerfs/euler_def.h | 3 + fs/eulerfs/file.c | 1 + fs/eulerfs/inode.c | 3 + fs/eulerfs/namei.c | 2 + fs/eulerfs/nvalloc.c | 1 + fs/eulerfs/nvalloc.h | 1 + fs/eulerfs/nvm_struct.h | 12 + fs/eulerfs/super.c | 36 +- fs/eulerfs/symlink.c | 1 + fs/eulerfs/xattr.c | 422 ++++++++++++++ fs/eulerfs/xattr.h | 99 ++++ fs/eulerfs/xattr_user.c | 43 ++ tools/testing/selftests/eulerfs/Makefile | 6 + tools/testing/selftests/eulerfs/xattr-test.c | 565 +++++++++++++++++++ 19 files changed, 1209 insertions(+), 2 deletions(-) create mode 100644 fs/eulerfs/xattr.c create mode 100644 fs/eulerfs/xattr.h create mode 100644 fs/eulerfs/xattr_user.c create mode 100644 tools/testing/selftests/eulerfs/Makefile create mode 100644 tools/testing/selftests/eulerfs/xattr-test.c
From: Gou Hao gouhao@uniontech.com
uniontech inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I40JRR CVE: NA
--------------
Add EULER_FS_XATTR config.
Signed-off-by: Gou Hao gouhao@uniontech.com --- fs/eulerfs/Kconfig | 9 +++++++++ 1 file changed, 9 insertions(+)
diff --git a/fs/eulerfs/Kconfig b/fs/eulerfs/Kconfig index 6328684f0b6a..f85042d70489 100644 --- a/fs/eulerfs/Kconfig +++ b/fs/eulerfs/Kconfig @@ -8,3 +8,12 @@ config EULER_FS EulerFS is NVDIMM filesystem. It uses soft updates and pointer-based dual views to delay synchronous cache flushes and reduce latency significantly in critical path. + +config EULER_FS_XATTR + bool "EulerFS extended attributes" + depends on EULER_FS + help + Extended attributes are name:value pairs associated with inodes by + the kernel or by users (see the attr(5) manual page for details). + + If unsure, say N.
From: Gou Hao gouhao@uniontech.com
uniontech inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I40JRR CVE: NA
--------------
The i_xattr points to a data block that stores extended attributes. extended attribute's opration protected by xattr_rwsem.
Signed-off-by: Gou Hao gouhao@uniontech.com --- fs/eulerfs/euler_def.h | 3 +++ fs/eulerfs/inode.c | 2 ++ fs/eulerfs/nvm_struct.h | 12 ++++++++++++ fs/eulerfs/super.c | 5 ++++- 4 files changed, 21 insertions(+), 1 deletion(-)
diff --git a/fs/eulerfs/euler_def.h b/fs/eulerfs/euler_def.h index 727f1c4cf181..8b7dc403b4e1 100644 --- a/fs/eulerfs/euler_def.h +++ b/fs/eulerfs/euler_def.h @@ -85,6 +85,9 @@ struct eufs_inode_info { /* Protect pointers to leaf nodes (data pages) */ struct mutex i_leaf_lock;
+ struct rw_semaphore xattr_rwsem; + u64 i_xattr; + spinlock_t i_dentry_persist_lock; struct inode vfs_inode; }; diff --git a/fs/eulerfs/inode.c b/fs/eulerfs/inode.c index c3db0750b66f..414ccf6d80a0 100644 --- a/fs/eulerfs/inode.c +++ b/fs/eulerfs/inode.c @@ -50,6 +50,7 @@ static int eufs_read_pinode(struct inode *inode, struct eufs_inode *pi) i_gid_write(inode, eufs_iread_gid(pi)); vi->i_dotdot = eufs_iread_dotdot(pi); vi->i_ext = eufs_iread_ext(pi); + vi->i_xattr = eufs_iread_xattr(pi);
inode->i_generation = eufs_iread_generation(pi); set_nlink(inode, eufs_iread_nlink(pi)); @@ -215,6 +216,7 @@ void eufs_sync_pinode(struct inode *inode, struct eufs_inode *pi, bool evict) eufs_iwrite_atime_nsec(twin_pi, inode->i_atime.tv_nsec); eufs_iwrite_root(twin_pi, pi_root_o); eufs_iwrite_size(twin_pi, inode->i_size); + eufs_iwrite_xattr(twin_pi, vi->i_xattr); eufs_iwrite_tree_blocks(twin_pi, pi_tree_blocks);
eufs_flush_cacheline(twin_pi); diff --git a/fs/eulerfs/nvm_struct.h b/fs/eulerfs/nvm_struct.h index 4818ae6d49bf..337ed50677f2 100644 --- a/fs/eulerfs/nvm_struct.h +++ b/fs/eulerfs/nvm_struct.h @@ -150,7 +150,11 @@ struct eufs_inode { /* 40 ~ 48 */ __le32 i_ctime_nsec; /* nano sec */ /* 48 ~ 56 */ +#ifdef CONFIG_EULER_FS_XATTR + __le64 i_xattr; +#else __le64 padding1; +#endif /* 56 ~ 64 */ __le64 padding2;
@@ -215,6 +219,14 @@ struct eufs_inode { #define eufs_iwrite_ctime_nsec(i, v) ((i)->i_ctime_nsec = cpu_to_le32(v)) #define eufs_iwrite_ext(i, v) ((i)->i_ext = cpu_to_le64(v))
+#ifdef CONFIG_EULER_FS_XATTR +#define eufs_iread_xattr(i) (le64_to_cpu((i)->i_xattr)) +#define eufs_iwrite_xattr(i, v) ((i)->i_xattr = cpu_to_le64(v)) +#else +#define eufs_iread_xattr(i) (0) +#define eufs_iwrite_xattr(i, v) +#endif + #define eufs_writemostly_inode(i) ((i))
#define eufs_iread_generation(i) \ diff --git a/fs/eulerfs/super.c b/fs/eulerfs/super.c index 43fc717002d7..eeec16d10baf 100644 --- a/fs/eulerfs/super.c +++ b/fs/eulerfs/super.c @@ -370,7 +370,7 @@ static struct eufs_inode *eufs_init(struct super_block *sb, unsigned long size)
eufs_iwrite_dict(root_i, p2o(sb, dict)); eufs_iwrite_size(root_i, 0); - + eufs_iwrite_xattr(root_i, 0); root_i->i_fresh = 2; eufs_flush_cacheline(root_i); eufs_flush_cacheline(&root_i->i_fresh); @@ -664,6 +664,9 @@ static struct inode *eufs_alloc_inode(struct super_block *sb) spin_lock_init(&vi->i_dentry_persist_lock); mutex_init(&vi->i_leaf_lock);
+ vi->i_xattr = 0; + init_rwsem(&vi->xattr_rwsem); + return &vi->vfs_inode; }
From: Gou Hao gouhao@uniontech.com
uniontech inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I40JRR CVE: NA
--------------
Add alloc xattr block interface.
Signed-off-by: Gou Hao gouhao@uniontech.com --- fs/eulerfs/alloc_interface.h | 4 ++++ fs/eulerfs/nvalloc.c | 1 + fs/eulerfs/nvalloc.h | 1 + 3 files changed, 6 insertions(+)
diff --git a/fs/eulerfs/alloc_interface.h b/fs/eulerfs/alloc_interface.h index 22d30c7672e0..23280551918f 100644 --- a/fs/eulerfs/alloc_interface.h +++ b/fs/eulerfs/alloc_interface.h @@ -79,6 +79,10 @@ static __always_inline void *eufs_malloc_inode_ext(struct super_block *sb) { return nvmalloc(sb, PAGE_SIZE, EUFS_PAGE_INODE_EXT, false); } +static __always_inline void *eufs_zalloc_inode_xattr(struct super_block *sb) +{ + return nvzalloc(sb, PAGE_SIZE, EUFS_PAGE_INODE_XATTR, false); +}
static __always_inline void nv_zfree(struct super_block *sb, void *p) { diff --git a/fs/eulerfs/nvalloc.c b/fs/eulerfs/nvalloc.c index 8b60a2494636..c774f7ac8bc5 100644 --- a/fs/eulerfs/nvalloc.c +++ b/fs/eulerfs/nvalloc.c @@ -313,6 +313,7 @@ static void partition(struct super_block *sb, bool init) case EUFS_PAGE_HTABLE: case EUFS_PAGE_SYMLINK: case EUFS_PAGE_INODE_EXT: + case EUFS_PAGE_INODE_XATTR: BUG_ON(init); node->busy = true; node->solid = true; diff --git a/fs/eulerfs/nvalloc.h b/fs/eulerfs/nvalloc.h index a39b81862bfb..abc45443fb35 100644 --- a/fs/eulerfs/nvalloc.h +++ b/fs/eulerfs/nvalloc.h @@ -123,6 +123,7 @@ struct mem_pool { #define EUFS_PAGE_HTABLE (10) #define EUFS_PAGE_SYMLINK (11) #define EUFS_PAGE_INODE_EXT (12) +#define EUFS_PAGE_INODE_XATTR (13)
#define EUFS_LINE4_INODE (4) #define EUFS_LINE_DENTRY (5)
From: Gou Hao gouhao@uniontech.com
uniontech inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I40JRR CVE: NA
--------------
The block header is followed by multiple entry descriptors. These entry descriptors are variable in size, and aligned to EUFS_XATTR_PAD byte boundaries. The entry descriptors are sorted by attribute name, so that two extended attribute blocks can be compared efficiently, attribute name and value are stored in e_data, No additional gaps are left between them.
Signed-off-by: Gou Hao gouhao@uniontech.com --- fs/eulerfs/Makefile | 1 + fs/eulerfs/xattr.c | 416 ++++++++++++++++++++++++++++++++++++++++++++ fs/eulerfs/xattr.h | 98 +++++++++++ 3 files changed, 515 insertions(+) create mode 100644 fs/eulerfs/xattr.c create mode 100644 fs/eulerfs/xattr.h
diff --git a/fs/eulerfs/Makefile b/fs/eulerfs/Makefile index 706e6ebff77e..60803f44e586 100644 --- a/fs/eulerfs/Makefile +++ b/fs/eulerfs/Makefile @@ -7,3 +7,4 @@ obj-$(CONFIG_EULER_FS) += eulerfs.o eulerfs-y := dir.o file.o inode.o namei.o super.o symlink.o eulerfs-y += dax.o dht.o dep.o nvalloc.o wear.o eulerfs-y += kmem_cache.o +eulerfs-$(CONFIG_EULER_FS_XATTR) += xattr.o diff --git a/fs/eulerfs/xattr.c b/fs/eulerfs/xattr.c new file mode 100644 index 000000000000..3dfa582156e8 --- /dev/null +++ b/fs/eulerfs/xattr.c @@ -0,0 +1,416 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2001-2003 Andreas Gruenbacher agruen@suse.de + * + * Fix by Harrison Xing harrison@mountainviewdata.com. + * Extended attributes for symlinks and special files added per + * suggestion of Luka Renko luka.renko@hermes.si. + * xattr consolidation Copyright (c) 2004 James Morris jmorris@redhat.com, + * Red Hat Inc. + * + * This file was modified by UnionTech Software Technology Co., Ltd. in + * 2022/07/05 by Gou Hao gouhao@uniontech.com, add the new xattr block + * layout. + */ + +/* + * Extended attributes are stored on disk blocks allocated outside of + * any inode. The i_xattr field is then made to point to this allocated + * block. If all extended attributes of an inode are identical, these + * inodes may share the same extended attribute block. Such situations + * are automatically detected by keeping a cache of recent attribute block + * numbers and hashes over the block's contents in memory. + * + * + * Extended attribute block layout: + * + * +------------------+ + * | header | + * | entry-value1entry| | + * | -value2... | | growing downwards + * | four null bytes | + * | | + * | | + * | | + * +------------------+ + * + * The block header is followed by multiple entry descriptors. These entry + * descriptors are variable in size, and aligned to EUFS_XATTR_PAD + * byte boundaries. The entry descriptors are sorted by attribute name, + * so that two extended attribute blocks can be compared efficiently, + * attribute name and value are stored in e_data, No additional gaps + * are left between them. + * + * Locking strategy + * ---------------- + * EUFS_I(inode)->i_xattr is protected by EUFS_I(inode)->xattr_rwsem. + * EA blocks are only changed if they are exclusive to an inode, so + * holding xattr_rwsem also means that nothing but the EA block's reference + * count will change. Multiple writers to an EA block are synchronized + * by the bh lock. No more than a single bh lock is held at any time + * to avoid deadlocks. + */ + +#include <linux/string.h> + +#include "euler.h" +#include "dep.h" +#include "xattr.h" + +#define eufs_xattr_dbg(format, args...) eufs_dbg("%s: " format, __func__, ##args) +#define eufs_xattr_err(sb, format, args...) eufs_err(sb, "%s: " format, __func__, ##args) + +#define HDR(ptr) ((struct eufs_xattr_header *)(ptr)) +#define ENTRY(ptr) ((struct eufs_xattr_entry *)(ptr)) +#define FIRST_ENTRY(ptr) ENTRY(HDR(ptr)+1) +#define IS_LAST_ENTRY(entry) (*(__u32 *)(entry) == 0) + +static inline bool +eufs_xattr_header_valid(struct eufs_xattr_header *header) +{ + return header->h_magic == cpu_to_le32(EUFS_XATTR_MAGIC) + && header->h_blocks == cpu_to_le32(1); +} + +static int +eufs_xattr_cmp_entry(int name_index, size_t name_len, const char *name, + struct eufs_xattr_entry *entry) +{ + int cmp; + + cmp = name_index - entry->e_name_index; + if (!cmp) + cmp = name_len - entry->e_name_len; + if (!cmp) + cmp = memcmp(name, entry->e_data, name_len); + + return cmp; +} + +const struct xattr_handler *eufs_xattr_handlers[] = { NULL }; +static const struct xattr_handler *eufs_xattr_handler_map[] = {}; + +static inline +const struct xattr_handler *eufs_xattr_handler(int name_index) +{ + if (name_index > 0 && name_index < ARRAY_SIZE(eufs_xattr_handler_map)) + return eufs_xattr_handler_map[name_index]; + return NULL; +} + +int eufs_xattr_get(struct inode *inode, int name_index, + const char *name, void *buffer, size_t buffer_size) +{ + struct eufs_xattr_entry *entry; + size_t name_len, size; + int error, not_found; + + struct eufs_inode_info *vi = EUFS_I(inode); + void *page = NULL; + struct super_block *sb = inode->i_sb; + + eufs_xattr_dbg("name=%s, buf_size=%ld, i_xattr=%ld\n", + name, (long)buffer_size, vi->i_xattr); + if (!name) + return -EINVAL; + + name_len = strlen(name); + if (name_len > EUFS_XATTR_NAME_MAX) + return -ERANGE; + + down_read(&vi->xattr_rwsem); + + error = -ENODATA; + if (!vi->i_xattr) + goto out; + + page = (void *)o2p(sb, vi->i_xattr); + error = -EIO; + if (!page || page == NULL_ADDR_PTR) + goto bad_block; + + if (!eufs_xattr_header_valid(HDR(page))) + goto bad_block; + + entry = FIRST_ENTRY(page); + while (!IS_LAST_ENTRY(entry)) { + not_found = eufs_xattr_cmp_entry(name_index, + name_len, name, entry); + if (!not_found) { + size = le32_to_cpu(entry->e_value_len); + + if (buffer) { + error = -ERANGE; + if (size > buffer_size) + goto out; + memcpy(buffer, entry->e_data + name_len, size); + } + error = size; + goto out; + } + + if (not_found < 0) + break; + + entry = EUFS_XATTR_NEXT(entry); + } + error = -ENODATA; + goto out; + +bad_block: + eufs_xattr_err(sb, "bad block: inode=%ld, block=%llu", + inode->i_ino, vi->i_xattr); +out: + up_read(&vi->xattr_rwsem); + return error; +} + +int eufs_xattr_set(struct inode *inode, int name_index, + const char *name, const void *value, + size_t value_len, int flags) +{ + struct super_block *sb = inode->i_sb; + struct eufs_inode_info *vi = EUFS_I(inode); + struct eufs_xattr_header *header = NULL; + struct eufs_xattr_entry *here = NULL, *last = NULL; + size_t block_size = sb->s_blocksize; + size_t name_len, free; + int not_found = 1, error = 0; + void *page = NULL; + size_t size, rest, new_entry_len, old_entry_len; + + eufs_xattr_dbg("name=%s, value_len=%ld, i_xattr=%llu, flags=%d\n", + name, (long)value_len, vi->i_xattr, flags); + if (!name) + return -EINVAL; + + name_len = strlen(name); + if (!value) + value_len = 0; + + if (name_len > EUFS_XATTR_NAME_MAX || value_len > block_size) + return -ERANGE; + + down_write(&vi->xattr_rwsem); + + free = block_size - sizeof(*header) - sizeof(__u32); + + if (vi->i_xattr) { + page = (void *)o2p(sb, vi->i_xattr); + error = -EIO; + if (!page || page == NULL_ADDR_PTR) + goto bad_block; + + header = HDR(page); + if (!eufs_xattr_header_valid(header)) + goto bad_block; + + last = FIRST_ENTRY(page); + while (!IS_LAST_ENTRY(last)) { + if (not_found > 0) { + not_found = eufs_xattr_cmp_entry(name_index, + name_len, name, last); + if (not_found <= 0) + here = last; + } + last = EUFS_XATTR_NEXT(last); + } + if (not_found > 0) + here = last; + free = block_size - ((char *)last - (char *)header) - sizeof(__u32); + } + + eufs_xattr_dbg("free=%lu\n", free); + + if (not_found) { + error = -ENODATA; + if (flags & XATTR_REPLACE) + goto out; + error = 0; + if (!value) + goto out; + } else { + error = -EEXIST; + if (flags & XATTR_CREATE) + goto out; + + free += EUFS_XATTR_ENTRY_SIZE(here); + } + + error = -ENOSPC; + if (free < EUFS_XATTR_LEN(name_len + value_len)) + goto out; + + if (!page) { + page = eufs_zalloc_inode_xattr(sb); + error = -ENOMEM; + if (!page) + goto out; + eufs_xattr_dbg("newpage=%llu\n", p2o(sb, page)); + + eufs_alloc_persist(inode->i_sb, page, false); + + header = HDR(page); + header->h_magic = cpu_to_le32(EUFS_XATTR_MAGIC); + header->h_blocks = header->h_refcount = cpu_to_le32(1); + last = here = ENTRY(header + 1); + } + + if (not_found) { + size = EUFS_XATTR_LEN(name_len + value_len); + rest = (char *)last - (char *)here; + + if (rest) { + memmove((char *)here + size, here, rest); + memset(here, 0, size); + } + + here->e_name_index = name_index; + here->e_name_len = name_len; + here->e_value_len = cpu_to_le32(value_len); + memcpy(here->e_data, name, name_len); + memcpy(here->e_data + name_len, value, value_len); + goto sync; + } else { + if (!value) { + size = EUFS_XATTR_ENTRY_SIZE(here); + if ((char *)last == (char *) here + size) { + memset(here, 0, size); + } else { + memmove(here, (char *)here + size, + (char *)last - (char *)here - size); + memset((char *)last - size, 0, size); + } + + } else { + new_entry_len = EUFS_XATTR_LEN(name_len + value_len); + old_entry_len = EUFS_XATTR_ENTRY_SIZE(here); + here->e_value_len = cpu_to_le32(value_len); + + if (new_entry_len != old_entry_len) { + memmove((char *)here + new_entry_len, + (char *)here + old_entry_len, + (char *)last - (char *)here - old_entry_len); + } + if (new_entry_len < old_entry_len) + memset((char *)last - (old_entry_len - new_entry_len), 0, + old_entry_len - new_entry_len); + memset(here + new_entry_len - EUFS_XATTR_PAD, 0, EUFS_XATTR_PAD); + memcpy(here->e_data + name_len, value, value_len); + } + } +sync: + + if (IS_LAST_ENTRY((ENTRY(header + 1)))) { + vi->i_xattr = 0; + memset(page, 0, block_size); + nv_free(sb, page); + } else { + eufs_flush_page(page); + vi->i_xattr = p2o(sb, page); + } + inode->i_ctime = current_time(inode); + eufs_sync_pinode(inode, EUFS_PI(inode), false); + persist_pinode(EUFS_PI(inode)); + + error = 0; + goto out; + +bad_block: + eufs_xattr_err(sb, "bad block: inode=%ld, block=%llu", + inode->i_ino, vi->i_xattr); +out: + up_write(&vi->xattr_rwsem); + return error; +} + +ssize_t eufs_listxattr(struct dentry *dentry, char *buffer, size_t buffer_size) +{ + struct inode *inode = d_inode(dentry); + struct eufs_xattr_entry *entry; + size_t rest = buffer_size; + int error = 0; + + struct eufs_inode_info *vi = EUFS_I(inode); + void *page = NULL; + struct super_block *sb = inode->i_sb; + + eufs_xattr_dbg("buf_size=%ld, i_xattr=%ld\n", + (long)buffer_size, vi->i_xattr); + + down_read(&vi->xattr_rwsem); + if (!vi->i_xattr) + goto out; + + page = (void *)o2p(sb, vi->i_xattr); + error = -EIO; + if (!page || page == NULL_ADDR_PTR) + goto out; + + if (!eufs_xattr_header_valid(HDR(page))) + goto bad_block; + + for (entry = FIRST_ENTRY(page); !IS_LAST_ENTRY(entry); + entry = EUFS_XATTR_NEXT(entry)) { + const struct xattr_handler *handler = + eufs_xattr_handler(entry->e_name_index); + + if (handler && (!handler->list || handler->list(dentry))) { + const char *prefix = handler->prefix ?: handler->name; + size_t prefix_len = strlen(prefix); + size_t name_len = entry->e_name_len; + size_t size = prefix_len + name_len + 1; + + if (buffer) { + if (size > rest) { + error = -ERANGE; + goto out; + } + + memcpy(buffer, prefix, prefix_len); + buffer += prefix_len; + memcpy(buffer, entry->e_data, name_len); + buffer += name_len; + *buffer++ = 0; + } + rest -= size; + } + } + + error = buffer_size - rest; + + goto out; +bad_block: + error = -EIO; + eufs_xattr_err(sb, "bad block: inode=%ld, block=%llu", + inode->i_ino, vi->i_xattr); +out: + up_read(&vi->xattr_rwsem); + return error; +} + +void eufs_xattr_delete_inode(struct inode *inode) +{ + struct eufs_inode_info *vi = EUFS_I(inode); + struct super_block *sb = inode->i_sb; + void *page = NULL; + + down_write(&vi->xattr_rwsem); + + if (!vi->i_xattr) + goto out; + + page = (void *)o2p(sb, vi->i_xattr); + if (page == NULL_ADDR_PTR) + goto out; + + if (!eufs_xattr_header_valid(HDR(page))) { + eufs_xattr_err(sb, "inode=%ld, bad_block=%lld", + inode->i_ino, vi->i_xattr); + goto out; + } + vi->i_xattr = 0; + nv_free(sb, page); +out: + up_write(&vi->xattr_rwsem); +} diff --git a/fs/eulerfs/xattr.h b/fs/eulerfs/xattr.h new file mode 100644 index 000000000000..935fce3acb68 --- /dev/null +++ b/fs/eulerfs/xattr.h @@ -0,0 +1,98 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * On-disk format of extended attributes for the euler filesystem. + * + * (C) 2001 Andreas Gruenbacher, a.gruenbacher@computer.org + * + * + * This file was modified by UnionTech Software Technology Co., Ltd. in + * 2022/07/05 by Gou Hao gouhao@uniontech.com, add the new xattr block + * layout. + */ +#ifndef EUFS_XATTR_H +#define EUFS_XATTR_H + +#include <linux/xattr.h> + +/* Magic value in attribute blocks */ +#define EUFS_XATTR_MAGIC 0xEA002022 + +/* Name indexes */ +#define EUFS_XATTR_INDEX_USER 1 + +#define EUFS_XATTR_NAME_MAX 255 + + +struct eufs_xattr_header { + __le32 h_magic; /* magic number for identification */ + __le32 h_refcount; /* reference count */ + __le32 h_blocks; /* number of disk blocks used */ + __le32 h_hash; /* hash value of all attributes */ + __u32 h_reserved[4]; /* zero right now */ +}; + +struct eufs_xattr_entry { + __u8 e_name_index; /* attribute name index */ + __u8 e_name_len; + __le16 padding; + __le32 e_value_len; /* name and value len. name:0-7bit. value:8-31bit*/ + __le32 e_hash; /* hash value of name and value */ + char e_data[]; /* attribute name and value */ +}; + +#define EUFS_XATTR_PAD_BITS 2 +#define EUFS_XATTR_PAD (1<<EUFS_XATTR_PAD_BITS) +#define EUFS_XATTR_ROUND (EUFS_XATTR_PAD-1) + +#define EUFS_XATTR_LEN(len) \ + (((len) + EUFS_XATTR_ROUND + \ + sizeof(struct eufs_xattr_entry)) & ~EUFS_XATTR_ROUND) + +#define EUFS_XATTR_ENTRY_SIZE(entry) \ + EUFS_XATTR_LEN((entry)->e_name_len + le32_to_cpu((entry)->e_value_len)) + +#define EUFS_XATTR_NEXT(entry) \ + ( (struct eufs_xattr_entry *)( \ + (char *)(entry) + EUFS_XATTR_ENTRY_SIZE((entry)))) + + + +#ifdef CONFIG_EULER_FS_XATTR + +extern const struct xattr_handler *eufs_xattr_handlers[]; + +extern int eufs_xattr_get(struct inode *inode, int name_index, + const char *name, void *value, size_t value_len); +extern int eufs_xattr_set(struct inode *inode, int name_index, + const char *name, const void *value, size_t value_len, int flags); +extern ssize_t eufs_listxattr(struct dentry *dentry, char *buf, size_t buf_len); + +extern void eufs_xattr_delete_inode(struct inode *inode); + +#else /* CONFIG_EULER_FS_XATTR */ + +#define eufs_xattr_handlers NULL +#define eufs_listxattr NULL + +static inline int +eufs_xattr_get(struct inode *inode, int name_index, + const char *name, void *buffer, size_t size) +{ + return -EOPNOTSUPP; +} + +static inline int +eufs_xattr_set(struct inode *inode, int name_index, const char *name, + const void *value, size_t size, int flags) +{ + return -EOPNOTSUPP; +} + +static inline void +eufs_xattr_delete_inode(struct inode *inode) +{ +} + +#endif /* CONFIG_EULER_FS_XATTR */ + +#endif /* EUFS_XATTR_H */
From: Gou Hao gouhao@uniontech.com
uniontech inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I40JRR CVE: NA
--------------
Add inode and super block callback interface of extended attribute.
Signed-off-by: Gou Hao gouhao@uniontech.com --- fs/eulerfs/euler.h | 1 + fs/eulerfs/file.c | 1 + fs/eulerfs/inode.c | 1 + fs/eulerfs/namei.c | 2 ++ fs/eulerfs/super.c | 4 +++- fs/eulerfs/symlink.c | 1 + 6 files changed, 9 insertions(+), 1 deletion(-)
diff --git a/fs/eulerfs/euler.h b/fs/eulerfs/euler.h index 0abb7602bb63..10ba7238a56b 100644 --- a/fs/eulerfs/euler.h +++ b/fs/eulerfs/euler.h @@ -56,6 +56,7 @@ #include "euler_common.h" #include "inode.h" #include "nvalloc.h" +#include "xattr.h"
extern int num_sockets;
diff --git a/fs/eulerfs/file.c b/fs/eulerfs/file.c index d5a743c102e3..d3181abeac33 100644 --- a/fs/eulerfs/file.c +++ b/fs/eulerfs/file.c @@ -291,4 +291,5 @@ const struct file_operations eufs_file_operations = { const struct inode_operations eufs_file_inode_operations = { .setattr = eufs_notify_change, .getattr = eufs_file_getattr, + .listxattr = eufs_listxattr, }; diff --git a/fs/eulerfs/inode.c b/fs/eulerfs/inode.c index 414ccf6d80a0..3504e6b84de4 100644 --- a/fs/eulerfs/inode.c +++ b/fs/eulerfs/inode.c @@ -303,6 +303,7 @@ void eufs_evict_inode(struct inode *inode) eufs_iwrite_nlink(fresh_pi, 0); eufs_iwrite_mode(fresh_pi, 0); eufs_flush_cacheline(fresh_pi); + eufs_xattr_delete_inode(inode); WARN_ON(!EUFS_IS_HEAD_PI(pi)); nv_free(sb, pi); } else if (!is_bad_inode(inode)) { diff --git a/fs/eulerfs/namei.c b/fs/eulerfs/namei.c index e4c6c36575f2..be0ef352215c 100644 --- a/fs/eulerfs/namei.c +++ b/fs/eulerfs/namei.c @@ -865,8 +865,10 @@ const struct inode_operations eufs_dir_inode_operations = { .mknod = eufs_mknod, .rename = eufs_rename, .setattr = eufs_notify_change, + .listxattr = eufs_listxattr, };
const struct inode_operations eufs_special_inode_operations = { .setattr = eufs_notify_change, + .listxattr = eufs_listxattr, }; diff --git a/fs/eulerfs/super.c b/fs/eulerfs/super.c index eeec16d10baf..d1886b5d3545 100644 --- a/fs/eulerfs/super.c +++ b/fs/eulerfs/super.c @@ -493,7 +493,9 @@ static int eufs_fill_super(struct super_block *sb, void *data, int silent) sb->s_op = &eufs_sops; sb->s_maxbytes = EUFS_MAX_FILE_SIZE; sb->s_time_gran = NSEC_PER_SEC; - +#ifdef CONFIG_EULER_FS_XATTR + sb->s_xattr = eufs_xattr_handlers; +#endif err = dep_init(sb); if (err) goto out; diff --git a/fs/eulerfs/symlink.c b/fs/eulerfs/symlink.c index 831c5c8758ea..2527b93e50b4 100644 --- a/fs/eulerfs/symlink.c +++ b/fs/eulerfs/symlink.c @@ -26,4 +26,5 @@ static const char *eufs_get_link(struct dentry *dentry, struct inode *inode, const struct inode_operations eufs_symlink_inode_operations = { .get_link = eufs_get_link, .setattr = eufs_notify_change, + .listxattr = eufs_listxattr, };
From: Gou Hao gouhao@uniontech.com
uniontech inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I40JRR CVE: NA
--------------
Support non privileged process setting extended attributes.
Signed-off-by: Gou Hao gouhao@uniontech.com --- fs/eulerfs/Makefile | 2 +- fs/eulerfs/const.h | 1 + fs/eulerfs/super.c | 27 ++++++++++++++++++++++++++ fs/eulerfs/xattr.c | 10 ++++++++-- fs/eulerfs/xattr.h | 1 + fs/eulerfs/xattr_user.c | 43 +++++++++++++++++++++++++++++++++++++++++ 6 files changed, 81 insertions(+), 3 deletions(-) create mode 100644 fs/eulerfs/xattr_user.c
diff --git a/fs/eulerfs/Makefile b/fs/eulerfs/Makefile index 60803f44e586..fe12ea71603c 100644 --- a/fs/eulerfs/Makefile +++ b/fs/eulerfs/Makefile @@ -7,4 +7,4 @@ obj-$(CONFIG_EULER_FS) += eulerfs.o eulerfs-y := dir.o file.o inode.o namei.o super.o symlink.o eulerfs-y += dax.o dht.o dep.o nvalloc.o wear.o eulerfs-y += kmem_cache.o -eulerfs-$(CONFIG_EULER_FS_XATTR) += xattr.o +eulerfs-$(CONFIG_EULER_FS_XATTR) += xattr.o xattr_user.o diff --git a/fs/eulerfs/const.h b/fs/eulerfs/const.h index 1e3485ecc8a0..94a8a9ec0014 100644 --- a/fs/eulerfs/const.h +++ b/fs/eulerfs/const.h @@ -20,6 +20,7 @@ #define EUFS_MOUNT_ERRORS_RO 0x000001 /* Remount fs ro on errors */ #define EUFS_MOUNT_ERRORS_PANIC 0x000002 /* Panic on errors */ #define EUFS_MOUNT_FORMAT 0x000004 /* was FS formatted on mount? */ +#define EUFS_MOUNT_XATTR_USER 0x000008 /* Extended user attributes */
#define NULL_ADDR ((u64)-1ll) #define NULL_VAL (0) diff --git a/fs/eulerfs/super.c b/fs/eulerfs/super.c index d1886b5d3545..689b24e51cbd 100644 --- a/fs/eulerfs/super.c +++ b/fs/eulerfs/super.c @@ -40,6 +40,7 @@ #include "dep.h" #include "nvalloc.h" #include "wear.h" +#include "xattr.h"
int support_clwb; int support_clflushopt; @@ -164,12 +165,16 @@ static int eufs_get_block_info(struct super_block *sb, struct eufs_sb_info *sbi) enum { Opt_init, Opt_dax, + Opt_user_xattr, + Opt_nouser_xattr, Opt_err };
static const match_table_t tokens = { { Opt_init, "init" }, { Opt_dax, "dax" }, /* DAX is always on. This is for compatibility. */ + { Opt_user_xattr, "user_xattr"}, + { Opt_nouser_xattr, "nouser_xattr"}, { Opt_err, NULL }, };
@@ -197,6 +202,19 @@ static int eufs_parse_options(char *options, struct eufs_sb_info *sbi, break; case Opt_dax: break; +#ifdef CONFIG_EUFS_XATTR + case Opt_user_xattr: + set_opt(sbi->s_mount_opt, XATTR_USER); + break; + case Opt_nouser_xattr: + clear_opt(sbi->s_mount_opt, XATTR_USER); + break; +#else + case Opt_user_xattr: + case Opt_nouser_xattr: + eufs_info("(no)user_xattr options not supported"); + break; +#endif default: goto bad_opt; } @@ -401,6 +419,13 @@ static void eufs_destroy_super(struct super_block *sb) kfree(sbi); }
+static void set_default_mount_opts(struct eufs_sb_info *sbi) +{ +#ifdef CONFIG_EULER_FS_XATTR + set_opt(sbi->s_mount_opt, XATTR_USER); +#endif +} + static int eufs_fill_super(struct super_block *sb, void *data, int silent) { struct eufs_super_block __pmem *super; @@ -438,6 +463,8 @@ static int eufs_fill_super(struct super_block *sb, void *data, int silent) mutex_init(&sbi->gather_mutex); mutex_init(&sbi->sync_mutex);
+ set_default_mount_opts(sbi); + err = eufs_parse_options(data, sbi, 0); if (err) goto out; diff --git a/fs/eulerfs/xattr.c b/fs/eulerfs/xattr.c index 3dfa582156e8..6053477bcd23 100644 --- a/fs/eulerfs/xattr.c +++ b/fs/eulerfs/xattr.c @@ -87,8 +87,14 @@ eufs_xattr_cmp_entry(int name_index, size_t name_len, const char *name, return cmp; }
-const struct xattr_handler *eufs_xattr_handlers[] = { NULL }; -static const struct xattr_handler *eufs_xattr_handler_map[] = {}; +const struct xattr_handler *eufs_xattr_handlers[] = { + &eufs_xattr_user_handler, + NULL +}; + +static const struct xattr_handler *eufs_xattr_handler_map[] = { + [EUFS_XATTR_INDEX_USER] = &eufs_xattr_user_handler, +};
static inline const struct xattr_handler *eufs_xattr_handler(int name_index) diff --git a/fs/eulerfs/xattr.h b/fs/eulerfs/xattr.h index 935fce3acb68..c4ec5b57a208 100644 --- a/fs/eulerfs/xattr.h +++ b/fs/eulerfs/xattr.h @@ -59,6 +59,7 @@ struct eufs_xattr_entry {
#ifdef CONFIG_EULER_FS_XATTR
+extern const struct xattr_handler eufs_xattr_user_handler; extern const struct xattr_handler *eufs_xattr_handlers[];
extern int eufs_xattr_get(struct inode *inode, int name_index, diff --git a/fs/eulerfs/xattr_user.c b/fs/eulerfs/xattr_user.c new file mode 100644 index 000000000000..82b3d7ff83b0 --- /dev/null +++ b/fs/eulerfs/xattr_user.c @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Handler for extended user attributes. + * + * Copyright (C) 2001 by Andreas Gruenbacher, a.gruenbacher@computer.org + * + * This file was modified by UnionTech Software Technology Co., Ltd. in + * 2022/07/05 by Gou Hao gouhao@uniontech.com + */ +#include "xattr.h" +#include "euler.h" + +static bool eufs_xattr_user_list(struct dentry *dentry) +{ + return test_opt(dentry->d_sb, XATTR_USER); +} + +static int eufs_xattr_user_get(const struct xattr_handler *handler, + struct dentry *dentry, struct inode *inode, + const char *name, void *buffer, size_t size) +{ + if (!test_opt(inode->i_sb, XATTR_USER)) + return -EOPNOTSUPP; + return eufs_xattr_get(inode, EUFS_XATTR_INDEX_USER, + name, buffer, size); +} + +static int eufs_xattr_user_set(const struct xattr_handler *handler, + struct dentry *dentry, struct inode *inode, + const char *name, const void *value, + size_t size, int flags) +{ + if (!test_opt(inode->i_sb, XATTR_USER)) + return -EOPNOTSUPP; + return eufs_xattr_set(inode, EUFS_XATTR_INDEX_USER, + name, value, size, flags); +} +const struct xattr_handler eufs_xattr_user_handler = { + .prefix = XATTR_USER_PREFIX, + .list = eufs_xattr_user_list, + .get = eufs_xattr_user_get, + .set = eufs_xattr_user_set +};
From: Gou Hao gouhao@uniontech.com
uniontech inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I40JRR CVE: NA
--------------
xattr-test.c can guarantee the basic functions of extended attributes.
Signed-off-by: Gou Hao gouhao@uniontech.com --- tools/testing/selftests/eulerfs/Makefile | 6 + tools/testing/selftests/eulerfs/xattr-test.c | 562 +++++++++++++++++++ 2 files changed, 568 insertions(+) create mode 100644 tools/testing/selftests/eulerfs/Makefile create mode 100644 tools/testing/selftests/eulerfs/xattr-test.c
diff --git a/tools/testing/selftests/eulerfs/Makefile b/tools/testing/selftests/eulerfs/Makefile new file mode 100644 index 000000000000..e5cb6133fdee --- /dev/null +++ b/tools/testing/selftests/eulerfs/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-3.0-or-later +# Makefile for eulerfs selftests + +TEST_GEN_FILES += xattr-test + +include ../lib.mk diff --git a/tools/testing/selftests/eulerfs/xattr-test.c b/tools/testing/selftests/eulerfs/xattr-test.c new file mode 100644 index 000000000000..e85d7de757c7 --- /dev/null +++ b/tools/testing/selftests/eulerfs/xattr-test.c @@ -0,0 +1,562 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +/* + * SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd. + * + * Gou Hao gouhao@unionntech.com + */ + +/* + * Selftests for eulerfs xattr + */ +#include <stdio.h> +#include <string.h> +#include <fcntl.h> +#include <unistd.h> +#include <errno.h> +#include <stdlib.h> + +#include <sys/xattr.h> +#include <sys/stat.h> +#include <sys/user.h> + +#include <time.h> + +#define BUF_LEN 256 + +#define test_dbg(format, args...) printf("%s: " format, __func__, ##args) + +#define XATTR_PREFIX "user." +#define XATTR_NAME(s) (XATTR_PREFIX#s) +char filename[BUF_LEN], value_buf[BUF_LEN]; +char str255[256], str256[257], str_page[PAGE_SIZE + 1]; + +struct test_data { + char *name; + char *value; + int val_size; + int expect; +}; + +int setup(void) +{ + int fd; + + unlink(filename); + + fd = open(filename, O_RDWR|O_CREAT|O_TRUNC, 0644); + if (fd < 0) { + test_dbg("Create file error!\n"); + return 1; + } + close(fd); + + return 0; +} + +#define print_result_cond(index, cond) \ + test_dbg("case %d %s.\n", (index), (cond) ? "pass" : "failed") + +static inline void print_result(int index, int expect, int real) +{ + if (real == expect) + test_dbg("case %d pass.\n", index); + else + test_dbg("case %d failed: expect=%d, real=%d.\n", index, expect, real); +} + +static inline void fill_value(char *value, int max_size) +{ + int i; + + for (i = 0; i < max_size; i++) + value[i] = 'a'; +} + +static void create_big_buf(void) +{ + int prefix_len = strlen(XATTR_PREFIX); + + sprintf(str255, "%s", XATTR_PREFIX); + fill_value(str255 + prefix_len, 255 - prefix_len); + str255[255] = 0; + + sprintf(str256, "%s", XATTR_PREFIX); + fill_value(str256 + prefix_len, 256 - prefix_len); + str256[256] = 0; + + sprintf(str_page, "%s", XATTR_PREFIX); + fill_value(str_page + prefix_len, PAGE_SIZE - prefix_len); + str_page[PAGE_SIZE] = 0; +} + +void test_setxattr(void) +{ + int ret, i, len; + struct test_data cases[] = { + {NULL, NULL, 0, EFAULT}, + {XATTR_NAME(t1), NULL, 0, 0}, + {NULL, "aaa", 3, EFAULT}, // 3 + {XATTR_NAME(t1), "a", 1, EEXIST}, + {XATTR_NAME(t2), "ab", 2, 0}, + {str255, "a", 1, 0}, // 6 + {str256, "a", 1, ERANGE}, + {XATTR_NAME(t3), str255, 255, 0}, + {XATTR_NAME(t4), str256, 256, 0}, // 9 + {XATTR_NAME(t5), str_page, PAGE_SIZE, ENOSPC}, + {XATTR_NAME(a5), "ab", 2, 0}, + {XATTR_NAME(abbbb5), "ab", 2, 0}, //12 + }; + + if (setup()) { + test_dbg("setup failed!\n"); + return; + } + + len = sizeof(cases) / sizeof(struct test_data); + for (i = 0; i < len; i++) { + errno = 0; + ret = setxattr(filename, cases[i].name, cases[i].value, + cases[i].val_size, XATTR_CREATE); + print_result(i + 1, cases[i].expect, errno); + } +} + +void test_getxattr(void) +{ + int ret, i, len; + char *test_name = XATTR_NAME(getxattr1); + char *val = "test-value"; + int val_len = strlen(val); + + if (setup()) { + test_dbg("setup failed!\n"); + return; + } + + setxattr(filename, test_name, val, + val_len, XATTR_CREATE); + + struct test_data cases[] = { + {NULL, NULL, 0, EFAULT}, + {NULL, "aaa", 3, EFAULT}, + {test_name, NULL, 0, val_len}, // 3 + {test_name, value_buf, BUF_LEN, val_len}, + }; + + len = sizeof(cases) / sizeof(struct test_data); + for (i = 0; i < len; i++) { + errno = 0; + ret = getxattr(filename, cases[i].name, + cases[i].value, cases[i].val_size); + if (i == 2) { + print_result(i + 1, cases[i].expect, ret); + } else if (i == 3) { + print_result(i + 1, cases[i].expect, ret); + print_result(i + 1, 0, strcmp(cases[i].value, val)); + } else { + print_result(i + 1, cases[i].expect, errno); + } + + } +} + +int find_name_index(struct test_data *cases, int len, char *target) +{ + int i; + + if (!cases || !target || len <= 0) + return -1; + + for (i = 0; i < len; i++) { + if (!strcmp(cases[i].name, target)) + return i; + } + return -1; +} + +void test_listxattr(void) +{ + int len, list_len; + char *list_value; + int name_index, ret, i; + int count = 0; + + struct test_data cases[] = { + {XATTR_NAME(listxattr-name1), "listxattr-value1", strlen("listxattr-value1"), 0}, + {XATTR_NAME(listxattr-name2), "listxattr-value2", strlen("listxattr-value2"), 0}, + {XATTR_NAME(listxattr-name3), "listxattr-value3", strlen("listxattr-value3"), 0}, + {XATTR_NAME(listxattr-name4), "listxattr-value4", strlen("listxattr-value4"), 0}, + }; + + if (setup()) { + test_dbg("setup failed!\n"); + return; + } + + len = sizeof(cases) / sizeof(struct test_data); + for (i = 0; i < len; i++) { + ret = setxattr(filename, cases[i].name, cases[i].value, + cases[i].val_size, XATTR_CREATE); + if (ret) { + test_dbg("create xattr FAILED!!\n"); + return; + } + } + + list_len = listxattr(filename, NULL, 0); + if (list_len <= 0) { + if (list_len == 0) + test_dbg("EMPTY!!\n"); + else + test_dbg("call listxattr1 FAILED, error: %s\n", + strerror(errno)); + return; + } + + list_value = (char *) malloc(list_len * sizeof(char)); + if (!list_value) { + test_dbg("malloc FAILED, error: %s\n", + strerror(errno)); + return; + } + list_len = listxattr(filename, list_value, list_len); + if (list_len <= 0) { + test_dbg("call listxattr2 FAILED, error: %s\n", + strerror(errno)); + goto out; + } + for (i = 0; i < list_len; ) { + ret = getxattr(filename, list_value + i, value_buf, BUF_LEN); + if (ret < 0) { + test_dbg("getxattr error: %s\n", strerror(errno)); + goto out; + } + value_buf[ret] = 0; + + name_index = find_name_index(cases, len, list_value + i); + + if (name_index < 0) { + test_dbg("listxattr error: find name index failed\n"); + goto out; + } + print_result(++count, 0, strcmp(value_buf, cases[name_index].value)); + + i += strlen(list_value + i) + 1; + } + +out: + free(list_value); +} + +void test_removexattr(void) +{ + int len, list_len; + char *list_value; + int name_index, ret, i; + int count = 1; + struct test_data cases[] = { + {XATTR_NAME(removexattr-name1), "removexattr-value1", + strlen("removexattr-value1"), 0}, + {XATTR_NAME(removexattr-name2), "removexattr-valuee2", + strlen("removexattr-value2"), 0}, + }; + + if (setup()) { + test_dbg("setup failed!\n"); + return; + } + + ret = setxattr(filename, cases[0].name, cases[0].value, + cases[0].val_size, XATTR_CREATE); + if (ret) { + test_dbg("create xattr FAILED!!\n"); + return; + } + + list_len = listxattr(filename, NULL, 0); + print_result(count++, strlen(cases[0].name), list_len - 1); + + + ret = removexattr(filename, cases[0].name); + print_result(count++, 0, ret); + + list_len = listxattr(filename, NULL, 0); + print_result(count++, 0, list_len); // case3 + + len = sizeof(cases) / sizeof(struct test_data); + for (i = 0; i < len; i++) { + ret = setxattr(filename, cases[i].name, cases[i].value, + cases[i].val_size, XATTR_CREATE); + if (ret) { + test_dbg("create xattr FAILED!!\n"); + return; + } + } + + + ret = removexattr(filename, cases[0].name); + print_result(count++, 0, ret); + + + list_len = listxattr(filename, NULL, 0); + print_result(count++, strlen(cases[1].name), list_len - 1); + +} + +void test_replacexattr(void) +{ + int ret, i, len; + + char *test_name = XATTR_NAME(replacexattr); + char *val = "test-value"; + int val_len = strlen(val); + + struct test_data cases[] = { + {NULL, NULL, 0, EFAULT}, + {test_name, NULL, 0, 0}, + {test_name, "aaa", 3, 0}, // 3 + {test_name, "aaab", 4, 0}, + {test_name, "aa", 2, 0}, + {test_name, str_page, PAGE_SIZE, ENOSPC}, //6 + {"user.nodata", "haha", 4, ENODATA}, + }; + + if (setup()) { + test_dbg("setup failed!\n"); + return; + } + + ret = setxattr(filename, test_name, val, + val_len, XATTR_CREATE); + if (ret) { + test_dbg("create attr failed!\n"); + return; + } + + ret = setxattr(filename, XATTR_NAME(replacexattr-tt), "test-stub", + strlen("test-stub"), XATTR_CREATE); + if (ret) { + test_dbg("create attr failed!\n"); + return; + } + + len = sizeof(cases) / sizeof(struct test_data); + for (i = 0; i < len; i++) { + errno = 0; + ret = setxattr(filename, cases[i].name, cases[i].value, + cases[i].val_size, XATTR_REPLACE); + print_result(i + 1, cases[i].expect, errno); + } +} + +void test_setxattr_full(void) +{ + char name_buf[BUF_LEN]; + char *value = "testvalue"; + int value_len = strlen(value); + int i = 1, ret; + + if (setup()) { + test_dbg("setup failed!\n"); + return; + } + + while (1) { + sprintf(name_buf, "%ssetxattr_full%d", XATTR_PREFIX, i); + errno = 0; + ret = setxattr(filename, name_buf, value, + value_len, XATTR_CREATE); + if (ret) { + test_dbg("create error: i=%d, errno=%d, errstr=%s\n", + i, errno, strerror(errno)); + break; + } + i++; + } +} + +void test_full2empty(void) +{ + char name_buf[BUF_LEN]; + char *value = "testvalue"; + int value_len = strlen(value); + int i = 1, count, ret; + + if (setup()) { + test_dbg("setup failed!\n"); + return; + } + + while (1) { + sprintf(name_buf, "%stest_full2empty%d", XATTR_PREFIX, i); + errno = 0; + ret = setxattr(filename, name_buf, value, + value_len, XATTR_CREATE); + if (ret) + break; + i++; + } + count = i; + + for (i = 1; i < count; i++) { + sprintf(name_buf, "%stest_full2empty%d", XATTR_PREFIX, i); + errno = 0; + ret = removexattr(filename, name_buf); + if (ret) { + test_dbg("create error: i=%d, errno=%d, errstr=%s\n", + i, errno, strerror(errno)); + break; + } + } + if (i == count) + print_result(1, count, i); +} + +char name_chars[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIGHLMNOPQRSTUVWXYZ0123456789"; + + +char *create_random_name(int *out_len) +{ +#define MAX_NAME_LEN 255 + int len = 0, i; + char *ret = NULL; + int name_chars_len = strlen(name_chars); + + len = rand() % (MAX_NAME_LEN) + 1; + ret = malloc(sizeof(char) * len + 1); + if (!ret) + return NULL; + sprintf(ret, XATTR_PREFIX); + for (i = strlen(XATTR_PREFIX); i < len; i++) + ret[i] = name_chars[rand() % name_chars_len]; + ret[i] = 0; + if (out_len) + *out_len = len; + return ret; +} + +char *create_random_value(int *out_len) +{ +#define MAX_VALUE_LEN PAGE_SIZE + int len, i; + char *ret; + + len = rand() % (MAX_NAME_LEN) + 1; + ret = malloc(sizeof(char) * len); + if (!ret) + return NULL; + for (i = 0; i < len; i++) + ret[i] = rand() % 256; + if (out_len) + *out_len = len; + return ret; +} +void test_random_case(void) +{ +#define NUM 100 + int i, op, name_len, value_len; + char *name = NULL, *value = NULL; + char tmpvalue[PAGE_SIZE]; + int ret, count = 1; + + test_dbg("......\n"); + srand(time(NULL)); + for (i = 0; i < NUM; i++, count++) { + memset(tmpvalue, 0, PAGE_SIZE); + op = rand() % 3; + + name = create_random_name(&name_len); + value = create_random_value(&value_len); + + if (!name || !value) + goto free; + switch (op) { + case 0: // create + ret = setxattr(filename, name, value, + value_len, XATTR_CREATE); + if (!ret) { + ret = getxattr(filename, name, tmpvalue, PAGE_SIZE); + print_result_cond(count, ret == value_len + && !memcmp(tmpvalue, value, value_len)); + } + break; + + case 1: // remove + ret = removexattr(filename, name); + if (!ret) { + ret = getxattr(filename, name, tmpvalue, PAGE_SIZE); + print_result_cond(count, !ret && errno == ENODATA); + } + + break; + + case 2: // replace + ret = setxattr(filename, name, value, + value_len, XATTR_REPLACE); + if (!ret) { + ret = getxattr(filename, name, tmpvalue, PAGE_SIZE); + print_result_cond(count, ret == value_len + && !memcmp(tmpvalue, value, value_len)); + } + break; + + default: + break; + } +free: + free(name); + free(value); + } + +} + +int main(int argc, char *argv[]) +{ + + char value[BUF_LEN], *list_value, *p; + int list_len; + int i, ret; + struct stat file_info; + struct timespec start, end; + + void (*test_fun[])(void) = { + test_setxattr, + test_getxattr, + test_listxattr, + test_removexattr, + test_replacexattr, + test_setxattr_full, + test_full2empty, + test_random_case, + }; + + if (argc < 2) { + test_dbg("Need file system folder!\n"); + return 1; + } + + ret = stat(argv[1], &file_info); + if (ret || !S_ISDIR(file_info.st_mode)) { + test_dbg("Bad folder!\n"); + return 1; + } + + printf("PAGE_SIZE=%ld\n", PAGE_SIZE); + + snprintf(filename, BUF_LEN, "%s/test", argv[1]); + + create_big_buf(); + + list_len = sizeof(test_fun) / sizeof(void *); + for (i = 0; i < list_len; i++) { + start.tv_sec = start.tv_nsec = 0; + end.tv_sec = end.tv_nsec = 0; + clock_gettime(CLOCK_REALTIME, &start); + test_fun[i](); + clock_gettime(CLOCK_REALTIME, &end); + printf("exec time: %lds.%ldns\n", + end.tv_sec-start.tv_sec, + end.tv_nsec-start.tv_nsec); + } + return 0; +}