diff mbox series

btrfs-fuse: Add listxattr and getxattr interface

Message ID 20211123073747.7497-1-hmsjwzb@zoho.com (mailing list archive)
State New, archived
Headers show
Series btrfs-fuse: Add listxattr and getxattr interface | expand

Commit Message

hmsjwzb Nov. 23, 2021, 7:37 a.m. UTC
Signed-off-by: hmsjwzb <hmsjwzb@zoho.com>
---
 inode.c         |  32 +++++++++++
 inode.h         |   7 +++
 main.c          | 140 ++++++++++++++++++++++++++++++++++++++++++++++++
 ondisk_format.h |   1 +
 4 files changed, 180 insertions(+)
diff mbox series

Patch

diff --git a/inode.c b/inode.c
index 6ed6279..5ea023e 100644
--- a/inode.c
+++ b/inode.c
@@ -9,6 +9,38 @@ 
 #include "hash.h"
 #include "messages.h"
 
+struct btrfs_dir_item *btrfs_match_dir_item_name(struct btrfs_fs_info *fs_info,
+							struct btrfs_path *path,
+							const char *name, int name_len)
+{
+	struct btrfs_dir_item *dir_item;
+	unsigned long name_ptr;
+	u32 total_len;
+	u32 cur = 0;
+	u32 this_len;
+	struct extent_buffer *leaf;
+
+	leaf = path->nodes[0];
+	dir_item = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_dir_item);
+
+	total_len = btrfs_item_size_nr(leaf, path->slots[0]);
+	while (cur < total_len) {
+		this_len = sizeof(*dir_item) +
+			btrfs_dir_name_len(leaf, dir_item) +
+			btrfs_dir_data_len(leaf, dir_item);
+		name_ptr = (unsigned long)(dir_item + 1);
+
+		if (btrfs_dir_name_len(leaf, dir_item) == name_len &&
+		    memcmp_extent_buffer(leaf, name, name_ptr, name_len) == 0)
+			return dir_item;
+
+		cur += this_len;
+		dir_item = (struct btrfs_dir_item *)((char *)dir_item +
+							this_len);
+	}
+	return NULL;
+}
+
 int btrfs_lookup_one_name(struct btrfs_fs_info *fs_info,
 			  struct btrfs_inode *dir, const char *name,
 			  size_t name_len, struct btrfs_inode *inode_ret)
diff --git a/inode.h b/inode.h
index 4515efd..f4cae8e 100644
--- a/inode.h
+++ b/inode.h
@@ -27,6 +27,12 @@  struct btrfs_inode {
 	u8 file_type;
 };
 
+/*
+ * Match one dir item name
+ */
+struct btrfs_dir_item *btrfs_match_dir_item_name(struct btrfs_fs_info *fs_info,
+							struct btrfs_path *path,
+							const char *name, int name_len);
 /*
  * Lookup one name for @dir.
  *
@@ -132,4 +138,5 @@  static inline u32 btrfs_type_to_imode(u8 btrfs_ft)
 
 int btrfs_stat(struct btrfs_fs_info *fs_info, struct btrfs_inode *inode,
 	       struct stat *stbuf);
+
 #endif
diff --git a/main.c b/main.c
index 3735d36..5e8828c 100644
--- a/main.c
+++ b/main.c
@@ -10,6 +10,7 @@ 
 #include "super.h"
 #include "inode.h"
 #include "data.h"
+#include "hash.h"
 
 static struct btrfs_fs_info *global_info = NULL;
 
@@ -190,6 +191,143 @@  static void btrfs_fuse_destroy(void *private_data)
 	btrfs_exit();
 }
 
+static int btrfs_fuse_listxattr(const char *pathname, char *list, size_t size)
+{
+	struct btrfs_key key;
+	struct btrfs_fs_info *fs_info = global_info;
+	struct btrfs_inode inode = {};
+	struct btrfs_path path;
+	size_t size_left = size;
+	int ret =  0;
+	size_t total_size = 0;
+
+	ret = btrfs_resolve_path(fs_info, pathname, strlen(pathname), &inode);
+	if (ret < 0)
+		return ret;
+
+	btrfs_init_path(&path);
+	key.objectid = inode.ino;
+	key.type = BTRFS_XATTR_ITEM_KEY;
+	key.offset = 0;
+
+	ret = __btrfs_search_slot(fs_info->default_root, &path, &key);
+	if (ret < 0)
+		return ret;
+
+	while (1) {
+		struct extent_buffer *leaf;
+		int slot;
+		struct btrfs_dir_item *di;
+		struct btrfs_key found_key;
+		u32 item_size;
+		u32 cur;
+
+		leaf = path.nodes[0];
+		slot = path.slots[0];
+		if (slot >= btrfs_header_nritems(leaf)) {
+			ret = btrfs_next_leaf(&path);
+			if (ret < 0)
+				goto err;
+			else if (ret > 0)
+				break;
+			continue;
+		}
+
+		btrfs_item_key_to_cpu(leaf, &found_key, slot);
+
+		if (found_key.objectid != key.objectid)
+			break;
+		if (found_key.type > BTRFS_XATTR_ITEM_KEY)
+			break;
+		if (found_key.type < BTRFS_XATTR_ITEM_KEY)
+			goto next_item;
+
+		di = btrfs_item_ptr(leaf, slot, struct btrfs_dir_item);
+		item_size = btrfs_item_size_nr(leaf, slot);
+		cur = 0;
+		while (cur < item_size) {
+			u16 name_len = btrfs_dir_name_len(leaf, di);
+			u16 data_len = btrfs_dir_data_len(leaf, di);
+			u32 this_len = sizeof(*di) + name_len + data_len;
+			unsigned long name_ptr = (unsigned long)(di + 1);
+
+			total_size += name_len + 1;
+
+			if (!size)
+				goto next;
+
+			if (!list || (name_len + 1) > size_left) {
+				ret = -ERANGE;
+				goto err;
+			}
+
+			read_extent_buffer(leaf, list, name_ptr, name_len);
+			list[name_len] = '\0';
+
+			size_left -= name_len + 1;
+			list += name_len + 1;
+next:
+			cur += this_len;
+			di = (struct btrfs_dir_item *)((char *)di + this_len);
+		}
+next_item:
+		path.slots[0]++;
+	}
+	ret = total_size;
+err:
+	return ret;
+}
+
+int btrfs_fuse_getxattr(const char *pathname, const char *name, char *value, size_t size)
+{
+	struct btrfs_key key;
+	struct btrfs_dir_item *di;
+	struct btrfs_fs_info *fs_info = global_info;
+	struct btrfs_root *root = fs_info->default_root;
+	struct extent_buffer *leaf;
+	struct btrfs_inode inode = {};
+	struct btrfs_path path;
+	int ret = 0;
+	unsigned long data_ptr;
+
+	memset(&path, 0, sizeof(path));
+	ret = btrfs_resolve_path(fs_info, pathname, strlen(pathname), &inode);
+	if (ret < 0)
+		return ret;
+
+	key.objectid = inode.ino;
+	key.type = BTRFS_XATTR_ITEM_KEY;
+	key.offset = btrfs_name_hash(name, strlen(name));
+	ret = __btrfs_search_slot(root, &path, &key);
+	if (ret != 0)
+		return ret;
+	di = btrfs_match_dir_item_name(fs_info, &path, name, strlen(name));
+	if (!di) {
+		ret = -ENODATA;
+		goto out;
+	}
+
+	leaf = path.nodes[0];
+	if (!size) {
+		ret = btrfs_dir_data_len(leaf, di);
+		goto out;
+	}
+
+	if (btrfs_dir_data_len(leaf, di) > size) {
+		ret = -ERANGE;
+		goto out;
+	}
+
+	data_ptr = (unsigned long)((char *)(di + 1) +
+					btrfs_dir_name_len(leaf, di));
+	read_extent_buffer(leaf, value, data_ptr,
+			btrfs_dir_data_len(leaf, di));
+	ret = btrfs_dir_data_len(leaf, di);
+
+out:
+	return ret;
+}
+
 static const struct fuse_operations btrfs_fuse_ops = {
 	.statfs		= btrfs_fuse_statfs,
 	.getattr	= btrfs_fuse_getattr,
@@ -201,6 +339,8 @@  static const struct fuse_operations btrfs_fuse_ops = {
 	.readdir	= btrfs_fuse_readdir,
 	.init		= btrfs_fuse_init,
 	.destroy	= btrfs_fuse_destroy,
+	.listxattr	= btrfs_fuse_listxattr,
+	.getxattr	= btrfs_fuse_getxattr,
 };
 
 void usage(void)
diff --git a/ondisk_format.h b/ondisk_format.h
index 510c69d..7da533e 100644
--- a/ondisk_format.h
+++ b/ondisk_format.h
@@ -59,6 +59,7 @@  enum btrfs_csum_type {
 
 /* A subset of needed key types for read-only operations */
 #define BTRFS_INODE_ITEM_KEY		1
+#define BTRFS_XATTR_ITEM_KEY		24
 #define BTRFS_DIR_ITEM_KEY		84
 #define BTRFS_DIR_INDEX_KEY		96
 #define BTRFS_EXTENT_DATA_KEY		108