[Btrfs-devel] [PATCH] xattr support for btrfs
Yan Zheng
yanzheng at 21cn.com
Wed Nov 14 23:57:48 PST 2007
2007/11/15, Josef Bacik <jbacik at redhat.com>:
> Hello,
>
> This patch applies against btrfs-unstable. I've taken all of zab's suggestions
> (at least I think I did) and hch's comments. All comments welcome. This has
> been tested and works well, fixed some bugs as well that was present in the
> original code. Thank you,
>
> Josef
>
> diff -r 217ed1dd0f13 Makefile
> --- a/Makefile Thu Nov 08 10:59:22 2007 -0500
> +++ b/Makefile Fri Nov 09 04:41:03 2007 -0500
> @@ -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 struct-funcs.o
> + extent_map.o sysfs.o struct-funcs.o xattr.o acl.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 217ed1dd0f13 acl.c
> --- /dev/null Thu Jan 01 00:00:00 1970 +0000
> +++ b/acl.c Sat Nov 10 00:22:24 2007 -0500
> @@ -0,0 +1,66 @@
> +/*
> + * 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/fs.h>
> +#include <linux/string.h>
> +#include <linux/xattr.h>
> +#include <linux/posix_acl_xattr.h>
> +#include "ctree.h"
> +#include "xattr.h"
> +
> +static int btrfs_xattr_acl_access_get(struct inode *inode, const char *name,
> + void *value, size_t size)
> +{
> + return btrfs_xattr_get(inode, BTRFS_XATTR_INDEX_POSIX_ACL_ACCESS, name,
> + value, size);
> +}
> +
> +static int btrfs_xattr_acl_access_set(struct inode *inode, const char *name,
> + const void *value, size_t size, int flags)
> +{
> + return btrfs_xattr_set(inode, BTRFS_XATTR_INDEX_POSIX_ACL_ACCESS, name,
> + value, size, flags);
> +}
> +
> +static int btrfs_xattr_acl_default_get(struct inode *inode, const char *name,
> + void *value, size_t size)
> +{
> + return btrfs_xattr_get(inode, BTRFS_XATTR_INDEX_POSIX_ACL_DEFAULT,
> + name, value, size);
> +}
> +
> +static int btrfs_xattr_acl_default_set(struct inode *inode, const char *name,
> + const void *value, size_t size, int flags)
> +{
> + return btrfs_xattr_set(inode, BTRFS_XATTR_INDEX_POSIX_ACL_DEFAULT,
> + name, value, size, flags);
> +}
> +
> +struct xattr_handler btrfs_xattr_acl_default_handler = {
> + .prefix = POSIX_ACL_XATTR_DEFAULT,
> + .list = btrfs_xattr_generic_list,
> + .get = btrfs_xattr_acl_default_get,
> + .set = btrfs_xattr_acl_default_set,
> +};
> +
> +struct xattr_handler btrfs_xattr_acl_access_handler = {
> + .prefix = POSIX_ACL_XATTR_ACCESS,
> + .list = btrfs_xattr_generic_list,
> + .get = btrfs_xattr_acl_access_get,
> + .set = btrfs_xattr_acl_access_set,
> +};
> diff -r 217ed1dd0f13 ctree.h
> --- a/ctree.h Thu Nov 08 10:59:22 2007 -0500
> +++ b/ctree.h Fri Nov 09 19:58:31 2007 -0500
> @@ -64,6 +64,7 @@ extern struct kmem_cache *btrfs_path_cac
> #define BTRFS_FT_SOCK 6
> #define BTRFS_FT_SYMLINK 7
> #define BTRFS_FT_MAX 8
> +#define BTRFS_FT_XATTR 9
BTRFS_FT_XATTR > BTRFS_FT_MAX, it confuses me a little. I'm not sure
whether it's intended.
>
> /*
> * the key defines the order in the tree, and so it also defines (optimal)
> @@ -226,7 +227,7 @@ struct btrfs_inode_item {
>
> struct btrfs_dir_item {
> struct btrfs_disk_key location;
> - __le16 flags;
> + __le16 data_len;
> __le16 name_len;
> u8 type;
> } __attribute__ ((__packed__));
> @@ -367,7 +368,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 */
>
> /*
> @@ -621,7 +622,7 @@ static inline void btrfs_set_item_key(st
> }
>
> /* struct btrfs_dir_item */
> -BTRFS_SETGET_FUNCS(dir_flags, struct btrfs_dir_item, flags, 16);
> +BTRFS_SETGET_FUNCS(dir_data_len, struct btrfs_dir_item, data_len, 16);
> BTRFS_SETGET_FUNCS(dir_type, struct btrfs_dir_item, type, 8);
> BTRFS_SETGET_FUNCS(dir_name_len, struct btrfs_dir_item, name_len, 16);
>
> @@ -962,6 +963,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,
> + u16 name_len, const void *data, u16 data_len,
> + u64 dir);
> +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, u16 name_len,
> + int mod);
> /* inode-map.c */
> int btrfs_find_free_objectid(struct btrfs_trans_handle *trans,
> struct btrfs_root *fs_root,
> @@ -1039,4 +1049,8 @@ 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_listxattr(struct dentry *dentry, char *buffer, size_t size);
> +int btrfs_delete_xattrs(struct btrfs_trans_handle *trans,
> + struct btrfs_root *root, struct inode *inode);
> #endif
> diff -r 217ed1dd0f13 dir-item.c
> --- a/dir-item.c Thu Nov 08 10:59:22 2007 -0500
> +++ b/dir-item.c Wed Nov 14 23:10:15 2007 -0500
> @@ -43,8 +43,6 @@ static struct btrfs_dir_item *insert_wit
> return ERR_PTR(-EEXIST);
> ret = btrfs_extend_item(trans, root, path, data_size);
> WARN_ON(ret > 0);
> - if (ret)
> - return ERR_PTR(ret);
> }
> if (ret < 0)
> return ERR_PTR(ret);
> @@ -55,6 +53,57 @@ static struct btrfs_dir_item *insert_wit
> BUG_ON(data_size > btrfs_item_size(leaf, item));
> ptr += btrfs_item_size(leaf, item) - data_size;
> return (struct btrfs_dir_item *)ptr;
> +}
> +
> +int btrfs_insert_xattr_item(struct btrfs_trans_handle *trans,
> + struct btrfs_root *root, const char *name,
> + u16 name_len, const void *data, u16 data_len,
> + u64 dir)
> +{
> + int ret = 0;
> + struct btrfs_path *path;
> + struct btrfs_dir_item *dir_item;
> + unsigned long name_ptr, data_ptr;
> + struct btrfs_key key, location;
> + struct btrfs_disk_key disk_key;
> + struct extent_buffer *leaf;
> + u32 data_size;
> +
> + key.objectid = dir;
> + 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();
> + if (!path)
> + return -ENOMEM;
> +
> + data_size = sizeof(*dir_item) + name_len + data_len;
> + dir_item = insert_with_overflow(trans, root, path, &key, data_size,
> + name, name_len);
> + /*
> + * FIXME: at some point we should handle xattr's that are larger than
> + * what we can fit in our leaf. We set location to NULL b/c we arent
> + * pointing at anything else, that will change if we store the xattr
> + * data in a separate inode.
> + */
> + BUG_ON(IS_ERR(dir_item));
> + memset(&location, 0, sizeof(location));
> +
> + leaf = path->nodes[0];
> + btrfs_cpu_key_to_disk(&disk_key, &location);
> + btrfs_set_dir_item_key(leaf, dir_item, &disk_key);
> + btrfs_set_dir_type(leaf, dir_item, BTRFS_FT_XATTR);
> + btrfs_set_dir_name_len(leaf, dir_item, name_len);
> + btrfs_set_dir_data_len(leaf, dir_item, data_len);
> + name_ptr = (unsigned long)(dir_item + 1);
> + data_ptr = (unsigned long)((char *)name_ptr + name_len);
> +
> + write_extent_buffer(leaf, name, name_ptr, name_len);
> + write_extent_buffer(leaf, data, data_ptr, data_len);
> + btrfs_mark_buffer_dirty(path->nodes[0]);
> +
> + btrfs_free_path(path);
> + return ret;
> }
>
> int btrfs_insert_dir_item(struct btrfs_trans_handle *trans, struct btrfs_root
> @@ -90,7 +139,7 @@ int btrfs_insert_dir_item(struct btrfs_t
> btrfs_cpu_key_to_disk(&disk_key, location);
> btrfs_set_dir_item_key(leaf, dir_item, &disk_key);
> btrfs_set_dir_type(leaf, dir_item, type);
> - btrfs_set_dir_flags(leaf, dir_item, 0);
> + btrfs_set_dir_data_len(leaf, dir_item, 0);
> btrfs_set_dir_name_len(leaf, dir_item, name_len);
> name_ptr = (unsigned long)(dir_item + 1);
>
> @@ -117,7 +166,7 @@ second_insert:
> btrfs_cpu_key_to_disk(&disk_key, location);
> btrfs_set_dir_item_key(leaf, dir_item, &disk_key);
> btrfs_set_dir_type(leaf, dir_item, type);
> - btrfs_set_dir_flags(leaf, dir_item, 0);
> + btrfs_set_dir_data_len(leaf, dir_item, 0);
> btrfs_set_dir_name_len(leaf, dir_item, name_len);
> name_ptr = (unsigned long)(dir_item + 1);
> write_extent_buffer(leaf, name, name_ptr, name_len);
> @@ -194,6 +243,43 @@ btrfs_lookup_dir_index_item(struct btrfs
> 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, u16 name_len,
> + int mod)
> +{
> + int ret;
> + struct btrfs_key key;
> + int ins_len = mod < 0 ? -1 : 0;
> + int cow = mod != 0;
> + struct btrfs_key found_key;
> + struct extent_buffer *leaf;
> +
> + key.objectid = dir;
> + 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 = path->nodes[0];
> + btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]);
> +
> + if (found_key.objectid != dir ||
> + btrfs_key_type(&found_key) != BTRFS_XATTR_ITEM_KEY ||
> + found_key.offset != key.offset)
> + return NULL;
> +
> + return btrfs_match_dir_item_name(root, path, name, name_len);
> +}
> +
> struct btrfs_dir_item *btrfs_match_dir_item_name(struct btrfs_root *root,
> struct btrfs_path *path,
> const char *name, int name_len)
> @@ -236,7 +322,8 @@ int btrfs_delete_one_dir_name(struct btr
> int ret = 0;
>
> leaf = path->nodes[0];
> - sub_item_len = sizeof(*di) + btrfs_dir_name_len(leaf, di);
> + sub_item_len = sizeof(*di) + btrfs_dir_name_len(leaf, di) +
> + btrfs_dir_data_len(leaf, di);
I think 'btrfs_match_dir_item_name' and 'btrfs_readdir' require
similar modifications . I can do it, if you agree.
> item_len = btrfs_item_size_nr(leaf, path->slots[0]);
> if (sub_item_len == item_len) {
> ret = btrfs_del_item(trans, root, path);
> diff -r 217ed1dd0f13 inode.c
> --- a/inode.c Thu Nov 08 10:59:22 2007 -0500
> +++ b/inode.c Fri Nov 09 04:39:44 2007 -0500
> @@ -32,6 +32,7 @@
> #include <linux/compat.h>
> #include <linux/bit_spinlock.h>
> #include <linux/version.h>
> +#include <linux/xattr.h>
> #include "ctree.h"
> #include "disk-io.h"
> #include "transaction.h"
> @@ -399,6 +400,10 @@ static int btrfs_unlink_trans(struct btr
> goto err;
> }
> ret = btrfs_delete_one_dir_name(trans, root, path, di);
> + if (ret)
> + goto err;
> +
> + ret = btrfs_delete_xattrs(trans, root, dentry->d_inode);
I think it's better to add these codes to 'btrfs_delete_inode'.
>
> dentry->d_inode->i_ctime = dir->i_ctime;
> err:
> @@ -2519,6 +2524,10 @@ static struct inode_operations btrfs_dir
> .symlink = btrfs_symlink,
> .setattr = btrfs_setattr,
> .mknod = btrfs_mknod,
> + .setxattr = generic_setxattr,
> + .getxattr = generic_getxattr,
> + .listxattr = btrfs_listxattr,
> + .removexattr = generic_removexattr,
> };
>
> static struct inode_operations btrfs_dir_ro_inode_operations = {
> @@ -2567,6 +2576,10 @@ static struct inode_operations btrfs_fil
> .truncate = btrfs_truncate,
> .getattr = btrfs_getattr,
> .setattr = btrfs_setattr,
> + .setxattr = generic_setxattr,
> + .getxattr = generic_getxattr,
> + .listxattr = btrfs_listxattr,
> + .removexattr = generic_removexattr,
> };
>
> static struct inode_operations btrfs_special_inode_operations = {
> diff -r 217ed1dd0f13 print-tree.c
> --- a/print-tree.c Thu Nov 08 10:59:22 2007 -0500
> +++ b/print-tree.c Fri Nov 09 04:55:35 2007 -0500
> @@ -58,9 +58,8 @@ void btrfs_print_leaf(struct btrfs_root
> case BTRFS_DIR_ITEM_KEY:
> di = btrfs_item_ptr(l, i, struct btrfs_dir_item);
> btrfs_dir_item_key_to_cpu(l, di, &found_key);
> - printk("\t\tdir oid %llu flags %u type %u\n",
> + printk("\t\tdir oid %llu type %u\n",
> (unsigned long long)found_key.objectid,
> - btrfs_dir_flags(l, di),
> btrfs_dir_type(l, di));
> break;
> case BTRFS_ROOT_ITEM_KEY:
> diff -r 217ed1dd0f13 super.c
> --- a/super.c Thu Nov 08 10:59:22 2007 -0500
> +++ b/super.c Fri Nov 09 04:39:44 2007 -0500
> @@ -40,6 +40,7 @@
> #include "btrfs_inode.h"
> #include "ioctl.h"
> #include "print-tree.h"
> +#include "xattr.h"
>
> #define BTRFS_SUPER_MAGIC 0x9123683E
>
> @@ -106,6 +107,7 @@ static int btrfs_fill_super(struct super
> sb->s_maxbytes = MAX_LFS_FILESIZE;
> sb->s_magic = BTRFS_SUPER_MAGIC;
> sb->s_op = &btrfs_super_ops;
> + sb->s_xattr = btrfs_xattr_handlers;
> sb->s_time_gran = 1;
>
> tree_root = open_ctree(sb);
> diff -r 217ed1dd0f13 xattr.c
> --- /dev/null Thu Jan 01 00:00:00 1970 +0000
> +++ b/xattr.c Thu Nov 15 03:58:18 2007 -0500
> @@ -0,0 +1,499 @@
> +/*
> + * 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"
> +#include "xattr.h"
> +#include "disk-io.h"
> +
> +static struct xattr_handler *btrfs_xattr_handler_map[] = {
> + [BTRFS_XATTR_INDEX_USER] = &btrfs_xattr_user_handler,
> + [BTRFS_XATTR_INDEX_POSIX_ACL_ACCESS] = &btrfs_xattr_acl_access_handler,
> + [BTRFS_XATTR_INDEX_POSIX_ACL_DEFAULT] = &btrfs_xattr_acl_default_handler,
> + [BTRFS_XATTR_INDEX_TRUSTED] = &btrfs_xattr_trusted_handler,
> + [BTRFS_XATTR_INDEX_SECURITY] = &btrfs_xattr_security_handler,
> + [BTRFS_XATTR_INDEX_SYSTEM] = &btrfs_xattr_system_handler,
> +};
> +
> +struct xattr_handler *btrfs_xattr_handlers[] = {
> + &btrfs_xattr_user_handler,
> + &btrfs_xattr_acl_access_handler,
> + &btrfs_xattr_acl_default_handler,
> + &btrfs_xattr_trusted_handler,
> + &btrfs_xattr_security_handler,
> + &btrfs_xattr_system_handler,
> + NULL,
> +};
> +
> +/*
> + * @param name - the xattr name
> + * @return - the xattr_handler for the xattr, NULL if its not found
> + *
> + * use this with listxattr where we don't already know the type of xattr we
> + * have
> + */
> +static struct xattr_handler *find_btrfs_xattr_handler(struct extent_buffer *l,
> + unsigned long name_ptr,
> + u16 name_len)
> +{
> + struct xattr_handler *handler = NULL;
> + int i = 0;
> +
> + for (handler = btrfs_xattr_handlers[i]; handler != NULL; i++,
> + handler = btrfs_xattr_handlers[i]) {
> + u16 prefix_len = strlen(handler->prefix);
> +
> + if (name_len < prefix_len)
> + continue;
> +
> + if (memcmp_extent_buffer(l, handler->prefix, name_ptr,
> + prefix_len) == 0)
> + break;
> + }
> +
> + return handler;
> +}
> +
> +/*
> + * @param name_index - the index for the xattr handler
> + * @return the xattr_handler if we found it, NULL otherwise
> + *
> + * use this if we know the type of the xattr already
> + */
> +static struct xattr_handler *btrfs_xattr_handler(int name_index)
> +{
> + struct xattr_handler *handler = NULL;
> +
> + if (name_index >= 0 &&
> + name_index < ARRAY_SIZE(btrfs_xattr_handler_map))
> + handler = btrfs_xattr_handler_map[name_index];
> +
> + return handler;
> +}
> +
> +static inline char *get_name(const char *name, int name_index)
> +{
> + char *ret = NULL;
> + struct xattr_handler *handler = btrfs_xattr_handler(name_index);
> + int prefix_len;
> +
> + if (!handler)
> + return ret;
> +
> + prefix_len = strlen(handler->prefix);
> +
> + ret = kmalloc(strlen(name) + prefix_len + 1, GFP_KERNEL);
> + if (!ret)
> + return ret;
> +
> + memcpy(ret, handler->prefix, prefix_len);
> + memcpy(ret+prefix_len, name, strlen(name));
> + ret[prefix_len + strlen(name)] = '\0';
> +
> + return ret;
> +}
> +
> +size_t btrfs_xattr_generic_list(struct inode *inode, char *list,
> + size_t list_size, const char *name,
> + size_t name_len)
> +{
> + if (list && (name_len+1) <= list_size) {
> + memcpy(list, name, name_len);
> + list[name_len] = '\0';
> + } else
> + return -ERANGE;
> +
> + return name_len+1;
> +}
> +
> +ssize_t btrfs_xattr_get(struct inode *inode, int name_index,
> + const char *attr_name, void *buffer, size_t size)
> +{
> + struct btrfs_dir_item *di;
> + struct btrfs_root *root = BTRFS_I(inode)->root;
> + struct btrfs_path *path;
> + struct extent_buffer *leaf;
> + struct xattr_handler *handler = btrfs_xattr_handler(name_index);
> + int ret = 0;
> + unsigned long data_ptr;
> + char *name;
> +
> + if (!handler)
> + return -EOPNOTSUPP;
> +
> + /* just in case... */
> + if (*attr_name == '\0')
> + return -EINVAL;
> +
> + name = get_name(attr_name, name_index);
> + if (!name)
> + return -ENOMEM;
> +
> + path = btrfs_alloc_path();
> + if (!path) {
> + kfree(name);
> + return -ENOMEM;
> + }
> +
> + mutex_lock(&root->fs_info->fs_mutex);
> + /* lookup the xattr by name */
> + di = btrfs_lookup_xattr(NULL, root, path, inode->i_ino, name,
> + strlen(name), 0);
> + if (!di || IS_ERR(di)) {
> + ret = -ENODATA;
> + goto out;
> + }
> +
> + leaf = path->nodes[0];
> + /* if size is 0, that means we want the size of the attr */
> + if (!size) {
> + ret = btrfs_dir_data_len(leaf, di);
> + goto out;
> + }
> +
> + /* now get the data out of our dir_item */
> + 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, buffer, data_ptr,
> + btrfs_dir_name_len(leaf, di));
should it be 'btrfs_dir_data_len' ?
> + ret = btrfs_dir_data_len(leaf, di);
> +
> +out:
> + mutex_unlock(&root->fs_info->fs_mutex);
> + kfree(name);
> + btrfs_free_path(path);
> + return ret;
> +}
> +
> +int btrfs_xattr_set(struct inode *inode, int name_index,
> + const char *attr_name, const void *value, size_t size,
> + int flags)
> +{
> + struct btrfs_dir_item *di;
> + struct btrfs_root *root = BTRFS_I(inode)->root;
> + struct btrfs_trans_handle *trans;
> + struct btrfs_path *path;
> + struct xattr_handler *handler = btrfs_xattr_handler(name_index);
> + char *name;
> + int ret = 0, mod = 0;
> +
> + if (!handler)
> + return -EOPNOTSUPP;
> +
> + /* just in case... */
> + if (*attr_name == '\0')
> + return -EINVAL;
> +
> + name = get_name(attr_name, name_index);
> + if (!name)
> + return -ENOMEM;
> +
> + path = btrfs_alloc_path();
> + if (!path) {
> + kfree(name);
> + return -ENOMEM;
> + }
> +
> + 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 */
> + di = btrfs_lookup_xattr(trans, root, path, inode->i_ino, name,
> + strlen(name), -1);
> + if (IS_ERR(di)) {
> + ret = PTR_ERR(di);
> + goto out;
> + }
> +
> + /* ok we already have this xattr, lets remove it */
> + if (di) {
> + /* if we want create only exit */
> + if (flags & XATTR_CREATE) {
> + ret = -EEXIST;
> + goto out;
> + }
> +
> + ret = btrfs_delete_one_dir_name(trans, root, path, di);
> + if (ret)
> + goto out;
> + btrfs_release_path(root, path);
> +
> + /* if we don't have a value then we are removing the xattr */
> + if (!value) {
> + mod = 1;
> + goto out;
> + }
> + } else if (flags & XATTR_REPLACE) {
> + /* we couldn't find the attr to replace, so error out */
> + ret = -ENODATA;
> + goto out;
> + }
> +
> + /* ok we have to create a completely new xattr */
> + ret = btrfs_insert_xattr_item(trans, root, name, strlen(name),
> + value, size, inode->i_ino);
> + if (ret)
> + goto out;
> + mod = 1;
> +
> +out:
> + if (mod) {
> + inode->i_ctime = CURRENT_TIME;
> + ret = btrfs_update_inode(trans, root, inode);
> + }
> +
> + btrfs_end_transaction(trans, root);
> + mutex_unlock(&root->fs_info->fs_mutex);
> + kfree(name);
> + btrfs_free_path(path);
> +
> + return ret;
> +}
> +
> +ssize_t btrfs_listxattr(struct dentry *dentry, char *buffer, size_t size)
> +{
> + struct btrfs_key key, found_key;
> + struct inode *inode = dentry->d_inode;
> + struct btrfs_root *root = BTRFS_I(inode)->root;
> + struct btrfs_path *path;
> + struct btrfs_item *item;
> + struct extent_buffer *leaf;
> + struct btrfs_dir_item *di;
> + struct xattr_handler *handler;
> + int ret = 0, slot, advance;
> + size_t total_size = 0, size_left = size, written;
> + unsigned long name_ptr;
> + char *name;
> + u32 nritems;
> +
> + /*
> + * ok we want all objects associated with this id.
> + * NOTE: we set key.offset = 0; because we want to start with the
> + * first xattr that we find and walk forward
> + */
> + key.objectid = inode->i_ino;
> + btrfs_set_key_type(&key, BTRFS_XATTR_ITEM_KEY);
> + key.offset = 0;
> +
> + path = btrfs_alloc_path();
> + path->reada = 2;
> + if (!path)
> + return -ENOMEM;
> +
> + mutex_lock(&root->fs_info->fs_mutex);
> +
> + /* search for our xattrs */
> + ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
> + if (ret < 0)
> + goto err;
> + ret = 0;
> + advance = 0;
> + while (1) {
> + leaf = path->nodes[0];
> + nritems = btrfs_header_nritems(leaf);
> + slot = path->slots[0];
> +
> + /* this is where we start walking through the path */
> + if (advance || slot >= nritems) {
> + /*
> + * if we've reached the last slot in this leaf we need
> + * to go to the next leaf and reset everything
> + */
> + if (slot >= nritems-1) {
> + ret = btrfs_next_leaf(root, path);
> + if (ret)
> + break;
> + leaf = path->nodes[0];
> + nritems = btrfs_header_nritems(leaf);
> + slot = path->slots[0];
> + } else {
> + /*
> + * just walking through the slots on this leaf
> + */
> + slot++;
> + path->slots[0]++;
> + }
> + }
> + advance = 1;
> +
> + item = btrfs_item_nr(leaf, slot);
> + btrfs_item_key_to_cpu(leaf, &found_key, slot);
> +
> + /* check to make sure this item is what we want */
> + if (found_key.objectid != key.objectid)
> + break;
> + if (btrfs_key_type(&found_key) != BTRFS_XATTR_ITEM_KEY)
> + break;
> +
> + di = btrfs_item_ptr(leaf, slot, struct btrfs_dir_item);
> +
> + total_size += btrfs_dir_name_len(leaf, di)+1;
> +
> + /* we are just looking for how big our buffer needs to be */
> + if (!size)
'size' never changes, so the test always fails. should it be 'size_left' ?
> + continue;
> +
> + /* find our handler for this xattr */
> + name_ptr = (unsigned long)(di + 1);
> + printk(KERN_ERR "getting handler\n");
> + handler = find_btrfs_xattr_handler(leaf, name_ptr,
> + btrfs_dir_name_len(leaf, di));
> + if (!handler) {
> + printk(KERN_ERR "btrfs: unsupported xattr found\n");
> + continue;
> + }
> + printk(KERN_ERR "got handler\n");
> +
> + name = kmalloc(btrfs_dir_name_len(leaf, di), GFP_KERNEL);
> + read_extent_buffer(leaf, name, name_ptr,
> + btrfs_dir_name_len(leaf, di));
> +
> + /* call the list function associated with this xattr */
> + written = handler->list(inode, buffer, size_left, name,
> + btrfs_dir_name_len(leaf, di));
> + kfree(name);
> +
> + if (written < 0) {
> + ret = -ERANGE;
> + break;
> + }
> +
> + size_left -= written;
> + buffer += written;
> + }
> + ret = total_size;
> +
> +err:
> + mutex_unlock(&root->fs_info->fs_mutex);
> + btrfs_free_path(path);
> +
> + return ret;
> +}
> +
> +/*
> + * delete all the xattrs associated with the inode. fs_mutex should be
> + * held when we come into here
> + */
> +int btrfs_delete_xattrs(struct btrfs_trans_handle *trans,
> + struct btrfs_root *root, struct inode *inode)
> +{
> + struct btrfs_path *path;
> + struct btrfs_key key, found_key;
> + struct btrfs_item *item;
> + struct extent_buffer *leaf;
> + int ret;
> +
> + path = btrfs_alloc_path();
> + if (!path)
> + return -ENOMEM;
> +
> + key.objectid = inode->i_ino;
> + btrfs_set_key_type(&key, BTRFS_XATTR_ITEM_KEY);
> + key.offset = (u64)-1;
> +
> + while(1) {
> + /* look for our next xattr */
> + ret = btrfs_search_slot(trans, root, &key, path, -1, 1);
> + if (ret < 0)
> + goto out;
> + BUG_ON(ret == 0);
> +
> + if (path->slots[0] == 0)
> + break;
> +
> + path->slots[0]--;
> + leaf = path->nodes[0];
> + item = btrfs_item_nr(leaf, path->slots[0]);
> + btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]);
> +
> + if (found_key.objectid != key.objectid)
> + break;
> + if (btrfs_key_type(&found_key) != BTRFS_XATTR_ITEM_KEY)
> + break;
> +
> + ret = btrfs_del_item(trans, root, path);
> + BUG_ON(ret);
> + btrfs_release_path(root, path);
> + }
> + ret = 0;
> +out:
> + btrfs_free_path(path);
> +
> + return ret;
> +}
> +
> +/*
> + * Handler functions
> + */
> +#define BTRFS_XATTR_SETGET_FUNCS(name, index) \
> +static int btrfs_xattr_##name##_get(struct inode *inode, \
> + const char *name, void *value, \
> + size_t size) \
> +{ \
> + return btrfs_xattr_get(inode, index, name, value, size); \
> +} \
> +static int btrfs_xattr_##name##_set(struct inode *inode, \
> + const char *name, const void *value,\
> + size_t size, int flags) \
> +{ \
> + return btrfs_xattr_set(inode, index, name, value, size, flags); \
> +} \
> +
> +BTRFS_XATTR_SETGET_FUNCS(security, BTRFS_XATTR_INDEX_SECURITY);
> +BTRFS_XATTR_SETGET_FUNCS(system, BTRFS_XATTR_INDEX_SYSTEM);
> +BTRFS_XATTR_SETGET_FUNCS(user, BTRFS_XATTR_INDEX_USER);
> +BTRFS_XATTR_SETGET_FUNCS(trusted, BTRFS_XATTR_INDEX_TRUSTED);
> +
> +struct xattr_handler btrfs_xattr_security_handler = {
> + .prefix = XATTR_SECURITY_PREFIX,
> + .list = btrfs_xattr_generic_list,
> + .get = btrfs_xattr_security_get,
> + .set = btrfs_xattr_security_set,
> +};
> +
> +struct xattr_handler btrfs_xattr_system_handler = {
> + .prefix = XATTR_SYSTEM_PREFIX,
> + .list = btrfs_xattr_generic_list,
> + .get = btrfs_xattr_system_get,
> + .set = btrfs_xattr_system_set,
> +};
> +
> +struct xattr_handler btrfs_xattr_user_handler = {
> + .prefix = XATTR_USER_PREFIX,
> + .list = btrfs_xattr_generic_list,
> + .get = btrfs_xattr_user_get,
> + .set = btrfs_xattr_user_set,
> +};
> +
> +struct xattr_handler btrfs_xattr_trusted_handler = {
> + .prefix = XATTR_USER_PREFIX,
> + .list = btrfs_xattr_generic_list,
> + .get = btrfs_xattr_trusted_get,
> + .set = btrfs_xattr_trusted_set,
> +};
> diff -r 217ed1dd0f13 xattr.h
> --- /dev/null Thu Jan 01 00:00:00 1970 +0000
> +++ b/xattr.h Fri Nov 09 19:56:56 2007 -0500
> @@ -0,0 +1,58 @@
> +/*
> + * 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.
> + */
> +
> +#ifndef __XATTR__
> +#define __XATTR__
> +
> +#include <linux/xattr.h>
> +#include "ctree.h"
> +
> +/* Name indexes */
> +enum {
> + BTRFS_XATTR_INDEX_USER,
> + BTRFS_XATTR_INDEX_POSIX_ACL_ACCESS,
> + BTRFS_XATTR_INDEX_POSIX_ACL_DEFAULT,
> + BTRFS_XATTR_INDEX_TRUSTED,
> + BTRFS_XATTR_INDEX_SECURITY,
> + BTRFS_XATTR_INDEX_SYSTEM,
> + BTRFS_XATTR_INDEX_END,
> +};
> +
> +extern struct xattr_handler btrfs_xattr_user_handler;
> +extern struct xattr_handler btrfs_xattr_trusted_handler;
> +extern struct xattr_handler btrfs_xattr_acl_access_handler;
> +extern struct xattr_handler btrfs_xattr_acl_default_handler;
> +extern struct xattr_handler btrfs_xattr_security_handler;
> +extern struct xattr_handler btrfs_xattr_system_handler;
> +
> +extern struct xattr_handler *btrfs_xattr_handlers[];
> +
> +ssize_t btrfs_xattr_get(struct inode *inode, int name_index, const char *name,
> + void *buffer, size_t size);
> +int btrfs_xattr_set(struct inode *inode, int name_index, const char *name,
> + const void *value, size_t size, int flags);
> +
> +/*
> + * the only reason this is public is for acl.c. There may be a point where
> + * acl.c doesn't need it, and if thats the case we need to remove it and make
> + * it static in xattr.c
> + */
> +size_t btrfs_xattr_generic_list(struct inode *inode, char *list,
> + size_t list_size, const char *name,
> + size_t name_len);
> +#endif /* __XATTR__ */
More information about the Btrfs-devel
mailing list