[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