[Btrfs-devel] [RFC] implement xattrs for btrfs

Josef Bacik jbacik at redhat.com
Tue Aug 28 13:04:11 PDT 2007


Hello,

This is my quick preview of what I'm doing so I can fix any of the major design
flaws now.  For the most part, every filesystem implements different extended
attribute handlers for each prefix, so ext3 has a different handler for "user.",
"security.", "trusted." etc.  The problem with this is that none of the handlers
seem to do anything different between each of the prefixes, you just get alot
more code and abstraction than I think is necesary.  So the only thing I do is
check to make sure the prefix is something that we support, and then handle
everything the same way.  Is this a bad idea?  Let me know now so I can still
change it :).  Right now setfatter and getfattr are the only things that work,
so this is how I test it

setfattr -n "user.myname" -v "myvalue" /mnt/btrfs-test/default/blah
getfattr -n "user.myname" /mnt/btrfs-test/default/blah 

which works fine.  I embed the value into the dir_item, I don't do any checking
to make sure the insertion of the item went ok, I'll do that when I get the
xattr handling all nailed down.  If the insertion fails b/c the dir_item is too
big then ill just put the data in an inode there and set a flag on the dir_item
so we know we have to look elsewhere for the xattr data.  Let me know what you
think,

Josef

diff -r efddf3207b68 Makefile
--- a/Makefile	Tue Aug 28 14:54:22 2007 -0400
+++ b/Makefile	Tue Aug 28 15:02:00 2007 -0400
@@ -5,7 +5,7 @@ btrfs-y := super.o ctree.o extent-tree.o
 btrfs-y := super.o ctree.o extent-tree.o print-tree.o root-tree.o dir-item.o \
 	   hash.o file-item.o inode-item.o inode-map.o disk-io.o \
 	   transaction.o bit-radix.o inode.o file.o tree-defrag.o \
-	   extent_map.o sysfs.o
+	   extent_map.o sysfs.o xattr.o
 
 #btrfs-y := ctree.o disk-io.o radix-tree.o extent-tree.o print-tree.o \
 #	  root-tree.o dir-item.o hash.o file-item.o inode-item.o \
diff -r efddf3207b68 ctree.h
--- a/ctree.h	Tue Aug 28 14:54:22 2007 -0400
+++ b/ctree.h	Tue Aug 28 15:36:42 2007 -0400
@@ -222,6 +222,7 @@ struct btrfs_dir_item {
 	struct btrfs_disk_key location;
 	__le16 flags;
 	__le16 name_len;
+	__le16 data_len;
 	u8 type;
 } __attribute__ ((__packed__));
 
@@ -358,7 +359,7 @@ struct btrfs_root {
  * the FS
  */
 #define BTRFS_INODE_ITEM_KEY		1
-
+#define BTRFS_XATTR_ITEM_KEY		2
 /* reserve 2-15 close to the inode for later flexibility */
 
 /*
@@ -400,7 +401,6 @@ struct btrfs_root {
  * data in the FS
  */
 #define BTRFS_STRING_ITEM_KEY	253
-
 
 static inline u64 btrfs_block_group_used(struct btrfs_block_group_item *bi)
 {
@@ -634,6 +634,16 @@ static inline void btrfs_set_dir_name_le
 static inline void btrfs_set_dir_name_len(struct btrfs_dir_item *d, u16 val)
 {
 	d->name_len = cpu_to_le16(val);
+}
+
+static inline u16 btrfs_dir_data_len(struct btrfs_dir_item *d)
+{
+	return le16_to_cpu(d->data_len);
+}
+
+static inline void btrfs_set_dir_data_len(struct btrfs_dir_item *d, u16 val)
+{
+	d->data_len = cpu_to_le16(val);
 }
 
 static inline void btrfs_disk_key_to_cpu(struct btrfs_key *cpu,
@@ -1164,6 +1174,15 @@ int btrfs_delete_one_dir_name(struct btr
 			      struct btrfs_root *root,
 			      struct btrfs_path *path,
 			      struct btrfs_dir_item *di);
+int btrfs_insert_xattr_item(struct btrfs_trans_handle *trans,
+			    struct btrfs_root *root, const char *name,
+			    int name_len, const void *data, int data_len,
+			    u64 dir, struct btrfs_key *location, u8 type);
+struct btrfs_dir_item *btrfs_lookup_xattr(struct btrfs_trans_handle *trans,
+					  struct btrfs_root *root,
+					  struct btrfs_path *path, u64 dir,
+					  const char *name, int name_len,
+					  int mod);
 /* inode-map.c */
 int btrfs_find_free_objectid(struct btrfs_trans_handle *trans,
 			     struct btrfs_root *fs_root,
@@ -1245,4 +1264,11 @@ void btrfs_sysfs_del_root(struct btrfs_r
 void btrfs_sysfs_del_root(struct btrfs_root *root);
 void btrfs_sysfs_del_super(struct btrfs_fs_info *root);
 
+/* xattr.c */
+ssize_t btrfs_getxattr(struct dentry *dentry, const char *name, void *buffer,
+		       size_t size);
+int btrfs_setxattr(struct dentry *dentry, const char *name, const void *value,
+		   size_t size, int flags);
+int btrfs_removexattr(struct dentry *dentry, const char *name);
+ssize_t btrfs_listxattr(struct dentry *dentry, char *buffer, size_t size);
 #endif
diff -r efddf3207b68 dir-item.c
--- a/dir-item.c	Tue Aug 28 14:54:22 2007 -0400
+++ b/dir-item.c	Tue Aug 28 15:44:53 2007 -0400
@@ -57,6 +57,51 @@ static struct btrfs_dir_item *insert_wit
 	return (struct btrfs_dir_item *)ptr;
 }
 
+int btrfs_insert_xattr_item(struct btrfs_trans_handle *trans,
+			    struct btrfs_root *root, const char *name,
+			    int name_len, const void *data, int data_len, 
+			    u64 dir, struct btrfs_key *location, u8 type)
+{
+	int ret = 0;
+	struct btrfs_path *path;
+	struct btrfs_dir_item *dir_item;
+	char *name_ptr, *data_ptr;
+	struct btrfs_key key;
+	u32 data_size;
+
+	key.objectid = dir;
+	key.flags = 0;
+	btrfs_set_key_type(&key, BTRFS_XATTR_ITEM_KEY);
+	ret = btrfs_name_hash(name, name_len, &key.offset);
+	BUG_ON(ret);
+	path = btrfs_alloc_path();
+	data_size = sizeof(*dir_item) + name_len + data_len;
+	dir_item = insert_with_overflow(trans, root, path, &key, data_size,
+					name, name_len);
+	if (IS_ERR(dir_item)) {
+		ret = PTR_ERR(dir_item);
+		if (ret == -EEXIST)
+			printk("WOOO duplicates\n");
+		goto out;
+	}
+
+	btrfs_cpu_key_to_disk(&dir_item->location, location);
+	btrfs_set_dir_type(dir_item, type);
+	btrfs_set_dir_flags(dir_item, 0);
+	btrfs_set_dir_name_len(dir_item, name_len);
+	btrfs_set_dir_data_len(dir_item, data_len);
+	name_ptr = (char *)(dir_item + 1);
+	data_ptr = (char *)(name_ptr + name_len);
+
+	btrfs_memcpy(root, path->nodes[0]->b_data, name_ptr, name, name_len);
+	btrfs_memcpy(root, path->nodes[0]->b_data, data_ptr, data, data_len);
+	btrfs_mark_buffer_dirty(path->nodes[0]);
+
+out:
+	btrfs_free_path(path);
+	return ret;
+}
+
 int btrfs_insert_dir_item(struct btrfs_trans_handle *trans, struct btrfs_root
 			  *root, const char *name, int name_len, u64 dir,
 			  struct btrfs_key *location, u8 type)
@@ -89,6 +134,7 @@ int btrfs_insert_dir_item(struct btrfs_t
 	btrfs_set_dir_type(dir_item, type);
 	btrfs_set_dir_flags(dir_item, 0);
 	btrfs_set_dir_name_len(dir_item, name_len);
+	btrfs_set_dir_data_len(dir_item, 0);
 	name_ptr = (char *)(dir_item + 1);
 
 	btrfs_memcpy(root, path->nodes[0]->b_data, name_ptr, name, name_len);
@@ -114,6 +160,7 @@ second_insert:
 	btrfs_set_dir_type(dir_item, type);
 	btrfs_set_dir_flags(dir_item, 0);
 	btrfs_set_dir_name_len(dir_item, name_len);
+	btrfs_set_dir_data_len(dir_item, 0);
 	name_ptr = (char *)(dir_item + 1);
 	btrfs_memcpy(root, path->nodes[0]->b_data, name_ptr, name, name_len);
 	btrfs_mark_buffer_dirty(path->nodes[0]);
@@ -185,6 +232,43 @@ btrfs_lookup_dir_index_item(struct btrfs
 		return ERR_PTR(ret);
 	if (ret > 0)
 		return ERR_PTR(-ENOENT);
+	return btrfs_match_dir_item_name(root, path, name, name_len);
+}
+
+struct btrfs_dir_item *btrfs_lookup_xattr(struct btrfs_trans_handle *trans,
+					  struct btrfs_root *root,
+					  struct btrfs_path *path, u64 dir,
+					  const char *name, int name_len,
+					  int mod)
+{
+	int ret;
+	struct btrfs_key key;
+	int ins_len = mod < 0 ? -1 : 0;
+	int cow = mod != 0;
+	struct btrfs_disk_key *found_key;
+	struct btrfs_leaf *leaf;
+
+	key.objectid = dir;
+	key.flags = 0;
+	btrfs_set_key_type(&key, BTRFS_XATTR_ITEM_KEY);
+	ret = btrfs_name_hash(name, name_len, &key.offset);
+	BUG_ON(ret);
+	ret = btrfs_search_slot(trans, root, &key, path, ins_len, cow);
+	if (ret < 0)
+		return ERR_PTR(ret);
+	if (ret > 0) {
+		if (path->slots[0] == 0)
+			return NULL;
+		path->slots[0]--;
+	}
+	leaf = btrfs_buffer_leaf(path->nodes[0]);
+	found_key = &leaf->items[path->slots[0]].key;
+
+	if (btrfs_disk_key_objectid(found_key) != dir ||
+	    btrfs_disk_key_type(found_key) != BTRFS_XATTR_ITEM_KEY ||
+	    btrfs_disk_key_offset(found_key) != key.offset)
+		return NULL;
+
 	return btrfs_match_dir_item_name(root, path, name, name_len);
 }
 
diff -r efddf3207b68 inode.c
--- a/inode.c	Tue Aug 28 14:54:22 2007 -0400
+++ b/inode.c	Tue Aug 28 15:02:00 2007 -0400
@@ -2345,6 +2345,10 @@ static struct inode_operations btrfs_dir
 	.symlink	= btrfs_symlink,
 	.setattr	= btrfs_setattr,
 	.mknod		= btrfs_mknod,
+	.setxattr	= btrfs_setxattr,
+	.getxattr	= btrfs_getxattr,
+	.listxattr	= btrfs_listxattr,
+	.removexattr	= btrfs_removexattr,
 };
 
 static struct inode_operations btrfs_dir_ro_inode_operations = {
@@ -2382,6 +2386,10 @@ static struct inode_operations btrfs_fil
 	.truncate	= btrfs_truncate,
 	.getattr	= btrfs_getattr,
 	.setattr	= btrfs_setattr,
+	.setxattr       = btrfs_setxattr,
+	.getxattr       = btrfs_getxattr,
+	.listxattr      = btrfs_listxattr,
+	.removexattr    = btrfs_removexattr,
 };
 
 static struct inode_operations btrfs_special_inode_operations = {
diff -r efddf3207b68 xattr.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/xattr.c	Tue Aug 28 15:57:39 2007 -0400
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2007 Red Hat.  All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License v2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 021110-1307, USA.
+ */
+
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/slab.h>
+#include <linux/rwsem.h>
+#include <linux/xattr.h>
+#include "ctree.h"
+#include "btrfs_inode.h"
+#include "transaction.h"
+
+static const char *btrfs_prefixes[] = {
+	XATTR_SECURITY_PREFIX,
+	XATTR_SYSTEM_PREFIX,
+	XATTR_TRUSTED_PREFIX,
+	XATTR_USER_PREFIX,
+};
+
+static int find_prefix(const char *name)
+{
+	int i;
+
+	for(i = 0; i < ARRAY_SIZE(btrfs_prefixes); i++) {
+		if (!strncmp(name, btrfs_prefixes[i], 
+			    strlen(btrfs_prefixes[i])-1)) 
+			return i;
+	}
+
+	return -EOPNOTSUPP;
+}
+
+ssize_t btrfs_getxattr(struct dentry *dentry, const char *name, void *buffer,
+			size_t size)
+{
+	struct btrfs_dir_item *di;
+	struct inode *inode = dentry->d_inode;
+	struct btrfs_root *root = BTRFS_I(inode)->root;
+	struct btrfs_path *path;
+	int ret = 0;
+	char *data_ptr;
+
+	ret = find_prefix(name);
+	if (ret < 0)
+		return ret;
+
+	mutex_lock(&root->fs_info->fs_mutex);
+	/* lookup the xattr by name */
+	path = btrfs_alloc_path();
+	BUG_ON(!path);
+	di = btrfs_lookup_xattr(NULL, root, path, inode->i_ino, name,
+				strlen(name), 0);
+	if (!di || IS_ERR(di)) {
+		ret = -ENODATA;
+		goto out;
+	}
+
+	/* if size is 0, that means we want the size of the attr */
+	if (!size) {
+		ret = btrfs_dir_data_len(di);
+		goto out;
+	}
+
+	/* now get the data out of our dir_item */
+	if (btrfs_dir_data_len(di) > size) {
+		ret = -ERANGE;
+		goto out;
+	}
+	data_ptr = (char *)(di + 1) + btrfs_dir_name_len(di);
+	memcpy(buffer, data_ptr, btrfs_dir_data_len(di));
+	ret = btrfs_dir_data_len(di);
+
+out:
+	btrfs_free_path(path);
+	mutex_unlock(&root->fs_info->fs_mutex);
+	return ret;
+}
+
+int btrfs_setxattr(struct dentry *dentry, const char *name, const void *value,
+		   size_t size, int flags)
+{
+	struct btrfs_dir_item *di;
+	struct inode *inode = dentry->d_inode;
+	struct btrfs_root *root = BTRFS_I(inode)->root;
+	struct btrfs_trans_handle *trans;
+	struct btrfs_path *path;
+	struct btrfs_key *location = &BTRFS_I(inode)->location;
+	int ret = 0;
+
+	ret = find_prefix(name);
+	if (ret < 0)
+		return ret;
+
+	mutex_lock(&root->fs_info->fs_mutex);
+	trans = btrfs_start_transaction(root, 1);
+	btrfs_set_trans_block_group(trans, inode);
+
+	/* first lets see if we already have this xattr */
+	path = btrfs_alloc_path();
+	BUG_ON(!path);
+	di = btrfs_lookup_xattr(trans, root, path, inode->i_ino, name,
+				strlen(name), 0);
+	if (IS_ERR(di)) {
+		ret = PTR_ERR(di);
+		goto out;
+	}
+
+	/* ok we already have this xattr, lets remove it */
+	if (di) {
+		ret = btrfs_del_item(trans, root, path);
+		if (ret)
+			goto out;
+		btrfs_release_path(root, path);
+	}
+
+	/* ok we have to create a completely new xattr */
+	ret = btrfs_insert_xattr_item(trans, root, name, strlen(name),
+				      value, size, inode->i_ino,
+				      location, 0);
+	if (ret)
+		goto out;
+	
+out:
+	btrfs_free_path(path);
+	btrfs_end_transaction(trans, root);
+	mutex_unlock(&root->fs_info->fs_mutex);
+	return ret;
+}
+
+int btrfs_removexattr(struct dentry *dentry, const char *name)
+{
+	return 0;
+}
+
+ssize_t btrfs_listxattr(struct dentry *dentry, char *buffer, size_t size)
+{
+	return 0;
+}




More information about the Btrfs-devel mailing list