Btrfs: fix listxattrs not listing all xattrs packed in the same item
[cascardo/linux.git] / fs / btrfs / xattr.c
index 6c68d63..145d2b8 100644 (file)
@@ -249,7 +249,7 @@ int __btrfs_setxattr(struct btrfs_trans_handle *trans,
                goto out;
 
        inode_inc_iversion(inode);
-       inode->i_ctime = CURRENT_TIME;
+       inode->i_ctime = current_fs_time(inode->i_sb);
        set_bit(BTRFS_INODE_COPY_EVERYTHING, &BTRFS_I(inode)->runtime_flags);
        ret = btrfs_update_inode(trans, root, inode);
        BUG_ON(ret);
@@ -260,16 +260,12 @@ out:
 
 ssize_t btrfs_listxattr(struct dentry *dentry, char *buffer, size_t size)
 {
-       struct btrfs_key key, found_key;
+       struct btrfs_key key;
        struct inode *inode = d_inode(dentry);
        struct btrfs_root *root = BTRFS_I(inode)->root;
        struct btrfs_path *path;
-       struct extent_buffer *leaf;
-       struct btrfs_dir_item *di;
-       int ret = 0, slot;
+       int ret = 0;
        size_t total_size = 0, size_left = size;
-       unsigned long name_ptr;
-       size_t name_len;
 
        /*
         * ok we want all objects associated with this id.
@@ -291,6 +287,13 @@ ssize_t btrfs_listxattr(struct dentry *dentry, char *buffer, size_t size)
                goto err;
 
        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];
 
@@ -316,31 +319,45 @@ ssize_t btrfs_listxattr(struct dentry *dentry, char *buffer, size_t size)
                if (found_key.type > BTRFS_XATTR_ITEM_KEY)
                        break;
                if (found_key.type < BTRFS_XATTR_ITEM_KEY)
-                       goto next;
+                       goto next_item;
 
                di = btrfs_item_ptr(leaf, slot, struct btrfs_dir_item);
-               if (verify_dir_item(root, leaf, di))
-                       goto next;
-
-               name_len = btrfs_dir_name_len(leaf, di);
-               total_size += name_len + 1;
+               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);
+
+                       if (verify_dir_item(root, leaf, di)) {
+                               ret = -EIO;
+                               goto err;
+                       }
 
-               /* we are just looking for how big our buffer needs to be */
-               if (!size)
-                       goto next;
+                       total_size += name_len + 1;
+                       /*
+                        * We are just looking for how big our buffer needs to
+                        * be.
+                        */
+                       if (!size)
+                               goto next;
 
-               if (!buffer || (name_len + 1) > size_left) {
-                       ret = -ERANGE;
-                       goto err;
-               }
+                       if (!buffer || (name_len + 1) > size_left) {
+                               ret = -ERANGE;
+                               goto err;
+                       }
 
-               name_ptr = (unsigned long)(di + 1);
-               read_extent_buffer(leaf, buffer, name_ptr, name_len);
-               buffer[name_len] = '\0';
+                       read_extent_buffer(leaf, buffer, name_ptr, name_len);
+                       buffer[name_len] = '\0';
 
-               size_left -= name_len + 1;
-               buffer += name_len + 1;
+                       size_left -= name_len + 1;
+                       buffer += name_len + 1;
 next:
+                       cur += this_len;
+                       di = (struct btrfs_dir_item *)((char *)di + this_len);
+               }
+next_item:
                path->slots[0]++;
        }
        ret = total_size;