aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFilipe Manana <fdmanana@suse.com>2016-02-21 10:03:02 -0500
committerChris Mason <clm@fb.com>2016-03-01 11:23:41 -0500
commitdaac7ba61a0d338c66b70c47d205ba7465718155 (patch)
tree3fddc0bc3210c2c12bec547ab615c8227b6fbc49
parentade770294df29e08f913e5d733a756893128f45e (diff)
Btrfs: fix listxattrs not listing all xattrs packed in the same item
In the listxattrs handler, we were not listing all the xattrs that are packed in the same btree item, which happens when multiple xattrs have a name that when crc32c hashed produce the same checksum value. Fix this by processing them all. The following test case for xfstests reproduces the issue: seq=`basename $0` seqres=$RESULT_DIR/$seq echo "QA output created by $seq" tmp=/tmp/$$ status=1 # failure is the default! trap "_cleanup; exit \$status" 0 1 2 3 15 _cleanup() { cd / rm -f $tmp.* } # get standard environment, filters and checks . ./common/rc . ./common/filter . ./common/attr # real QA test starts here _supported_fs generic _supported_os Linux _require_scratch _require_attrs rm -f $seqres.full _scratch_mkfs >>$seqres.full 2>&1 _scratch_mount # Create our test file with a few xattrs. The first 3 xattrs have a name # that when given as input to a crc32c function result in the same checksum. # This made btrfs list only one of the xattrs through listxattrs system call # (because it packs xattrs with the same name checksum into the same btree # item). touch $SCRATCH_MNT/testfile $SETFATTR_PROG -n user.foobar -v 123 $SCRATCH_MNT/testfile $SETFATTR_PROG -n user.WvG1c1Td -v qwerty $SCRATCH_MNT/testfile $SETFATTR_PROG -n user.J3__T_Km3dVsW_ -v hello $SCRATCH_MNT/testfile $SETFATTR_PROG -n user.something -v pizza $SCRATCH_MNT/testfile $SETFATTR_PROG -n user.ping -v pong $SCRATCH_MNT/testfile # Now call getfattr with --dump, which calls the listxattrs system call. # It should list all the xattrs we have set before. $GETFATTR_PROG --absolute-names --dump $SCRATCH_MNT/testfile | _filter_scratch status=0 exit Signed-off-by: Filipe Manana <fdmanana@suse.com> Signed-off-by: Chris Mason <clm@fb.com>
-rw-r--r--fs/btrfs/xattr.c65
1 files changed, 41 insertions, 24 deletions
diff --git a/fs/btrfs/xattr.c b/fs/btrfs/xattr.c
index f2a20d52b9db..145d2b89e62d 100644
--- a/fs/btrfs/xattr.c
+++ b/fs/btrfs/xattr.c
@@ -260,16 +260,12 @@ out:
260 260
261ssize_t btrfs_listxattr(struct dentry *dentry, char *buffer, size_t size) 261ssize_t btrfs_listxattr(struct dentry *dentry, char *buffer, size_t size)
262{ 262{
263 struct btrfs_key key, found_key; 263 struct btrfs_key key;
264 struct inode *inode = d_inode(dentry); 264 struct inode *inode = d_inode(dentry);
265 struct btrfs_root *root = BTRFS_I(inode)->root; 265 struct btrfs_root *root = BTRFS_I(inode)->root;
266 struct btrfs_path *path; 266 struct btrfs_path *path;
267 struct extent_buffer *leaf; 267 int ret = 0;
268 struct btrfs_dir_item *di;
269 int ret = 0, slot;
270 size_t total_size = 0, size_left = size; 268 size_t total_size = 0, size_left = size;
271 unsigned long name_ptr;
272 size_t name_len;
273 269
274 /* 270 /*
275 * ok we want all objects associated with this id. 271 * 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)
291 goto err; 287 goto err;
292 288
293 while (1) { 289 while (1) {
290 struct extent_buffer *leaf;
291 int slot;
292 struct btrfs_dir_item *di;
293 struct btrfs_key found_key;
294 u32 item_size;
295 u32 cur;
296
294 leaf = path->nodes[0]; 297 leaf = path->nodes[0];
295 slot = path->slots[0]; 298 slot = path->slots[0];
296 299
@@ -316,31 +319,45 @@ ssize_t btrfs_listxattr(struct dentry *dentry, char *buffer, size_t size)
316 if (found_key.type > BTRFS_XATTR_ITEM_KEY) 319 if (found_key.type > BTRFS_XATTR_ITEM_KEY)
317 break; 320 break;
318 if (found_key.type < BTRFS_XATTR_ITEM_KEY) 321 if (found_key.type < BTRFS_XATTR_ITEM_KEY)
319 goto next; 322 goto next_item;
320 323
321 di = btrfs_item_ptr(leaf, slot, struct btrfs_dir_item); 324 di = btrfs_item_ptr(leaf, slot, struct btrfs_dir_item);
322 if (verify_dir_item(root, leaf, di)) 325 item_size = btrfs_item_size_nr(leaf, slot);
323 goto next; 326 cur = 0;
324 327 while (cur < item_size) {
325 name_len = btrfs_dir_name_len(leaf, di); 328 u16 name_len = btrfs_dir_name_len(leaf, di);
326 total_size += name_len + 1; 329 u16 data_len = btrfs_dir_data_len(leaf, di);
330 u32 this_len = sizeof(*di) + name_len + data_len;
331 unsigned long name_ptr = (unsigned long)(di + 1);
332
333 if (verify_dir_item(root, leaf, di)) {
334 ret = -EIO;
335 goto err;
336 }
327 337
328 /* we are just looking for how big our buffer needs to be */ 338 total_size += name_len + 1;
329 if (!size) 339 /*
330 goto next; 340 * We are just looking for how big our buffer needs to
341 * be.
342 */
343 if (!size)
344 goto next;
331 345
332 if (!buffer || (name_len + 1) > size_left) { 346 if (!buffer || (name_len + 1) > size_left) {
333 ret = -ERANGE; 347 ret = -ERANGE;
334 goto err; 348 goto err;
335 } 349 }
336 350
337 name_ptr = (unsigned long)(di + 1); 351 read_extent_buffer(leaf, buffer, name_ptr, name_len);
338 read_extent_buffer(leaf, buffer, name_ptr, name_len); 352 buffer[name_len] = '\0';
339 buffer[name_len] = '\0';
340 353
341 size_left -= name_len + 1; 354 size_left -= name_len + 1;
342 buffer += name_len + 1; 355 buffer += name_len + 1;
343next: 356next:
357 cur += this_len;
358 di = (struct btrfs_dir_item *)((char *)di + this_len);
359 }
360next_item:
344 path->slots[0]++; 361 path->slots[0]++;
345 } 362 }
346 ret = total_size; 363 ret = total_size;