aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorFilipe David Borba Manana <fdmanana@gmail.com>2014-01-07 06:47:46 -0500
committerChris Mason <clm@fb.com>2014-01-28 16:20:24 -0500
commit63541927c8d11d2686778b1e8ec71c14b4fd53e4 (patch)
treea868b0d199f0ea10567610d4db78f2201dfa8484 /fs
parent1acae57b161ef1282f565ef907f72aeed0eb71d9 (diff)
Btrfs: add support for inode properties
This change adds infrastructure to allow for generic properties for inodes. Properties are name/value pairs that can be associated with inodes for different purposes. They are stored as xattrs with the prefix "btrfs." Properties can be inherited - this means when a directory inode has inheritable properties set, these are added to new inodes created under that directory. Further, subvolumes can also have properties associated with them, and they can be inherited from their parent subvolume. Naturally, directory properties have priority over subvolume properties (in practice a subvolume property is just a regular property associated with the root inode, objectid 256, of the subvolume's fs tree). This change also adds one specific property implementation, named "compression", whose values can be "lzo" or "zlib" and it's an inheritable property. The corresponding changes to btrfs-progs were also implemented. A patch with xfstests for this feature will follow once there's agreement on this change/feature. Further, the script at the bottom of this commit message was used to do some benchmarks to measure any performance penalties of this feature. Basically the tests correspond to: Test 1 - create a filesystem and mount it with compress-force=lzo, then sequentially create N files of 64Kb each, measure how long it took to create the files, unmount the filesystem, mount the filesystem and perform an 'ls -lha' against the test directory holding the N files, and report the time the command took. Test 2 - create a filesystem and don't use any compression option when mounting it - instead set the compression property of the subvolume's root to 'lzo'. Then create N files of 64Kb, and report the time it took. The unmount the filesystem, mount it again and perform an 'ls -lha' like in the former test. This means every single file ends up with a property (xattr) associated to it. Test 3 - same as test 2, but uses 4 properties - 3 are duplicates of the compression property, have no real effect other than adding more work when inheriting properties and taking more btree leaf space. Test 4 - same as test 3 but with 10 properties per file. Results (in seconds, and averages of 5 runs each), for different N numbers of files follow. * Without properties (test 1) file creation time ls -lha time 10 000 files 3.49 0.76 100 000 files 47.19 8.37 1 000 000 files 518.51 107.06 * With 1 property (compression property set to lzo - test 2) file creation time ls -lha time 10 000 files 3.63 0.93 100 000 files 48.56 9.74 1 000 000 files 537.72 125.11 * With 4 properties (test 3) file creation time ls -lha time 10 000 files 3.94 1.20 100 000 files 52.14 11.48 1 000 000 files 572.70 142.13 * With 10 properties (test 4) file creation time ls -lha time 10 000 files 4.61 1.35 100 000 files 58.86 13.83 1 000 000 files 656.01 177.61 The increased latencies with properties are essencialy because of: *) When creating an inode, we now synchronously write 1 more item (an xattr item) for each property inherited from the parent dir (or subvolume). This could be done in an asynchronous way such as we do for dir intex items (delayed-inode.c), which could help reduce the file creation latency; *) With properties, we now have larger fs trees. For this particular test each xattr item uses 75 bytes of leaf space in the fs tree. This could be less by using a new item for xattr items, instead of the current btrfs_dir_item, since we could cut the 'location' and 'type' fields (saving 18 bytes) and maybe 'transid' too (saving a total of 26 bytes per xattr item) from the btrfs_dir_item type. Also tried batching the xattr insertions (ignoring proper hash collision handling, since it didn't exist) when creating files that inherit properties from their parent inode/subvolume, but the end results were (surprisingly) essentially the same. Test script: $ cat test.pl #!/usr/bin/perl -w use strict; use Time::HiRes qw(time); use constant NUM_FILES => 10_000; use constant FILE_SIZES => (64 * 1024); use constant DEV => '/dev/sdb4'; use constant MNT_POINT => '/home/fdmanana/btrfs-tests/dev'; use constant TEST_DIR => (MNT_POINT . '/testdir'); system("mkfs.btrfs", "-l", "16384", "-f", DEV) == 0 or die "mkfs.btrfs failed!"; # following line for testing without properties #system("mount", "-o", "compress-force=lzo", DEV, MNT_POINT) == 0 or die "mount failed!"; # following 2 lines for testing with properties system("mount", DEV, MNT_POINT) == 0 or die "mount failed!"; system("btrfs", "prop", "set", MNT_POINT, "compression", "lzo") == 0 or die "set prop failed!"; system("mkdir", TEST_DIR) == 0 or die "mkdir failed!"; my ($t1, $t2); $t1 = time(); for (my $i = 1; $i <= NUM_FILES; $i++) { my $p = TEST_DIR . '/file_' . $i; open(my $f, '>', $p) or die "Error opening file!"; $f->autoflush(1); for (my $j = 0; $j < FILE_SIZES; $j += 4096) { print $f ('A' x 4096) or die "Error writing to file!"; } close($f); } $t2 = time(); print "Time to create " . NUM_FILES . ": " . ($t2 - $t1) . " seconds.\n"; system("umount", DEV) == 0 or die "umount failed!"; system("mount", DEV, MNT_POINT) == 0 or die "mount failed!"; $t1 = time(); system("bash -c 'ls -lha " . TEST_DIR . " > /dev/null'") == 0 or die "ls failed!"; $t2 = time(); print "Time to ls -lha all files: " . ($t2 - $t1) . " seconds.\n"; system("umount", DEV) == 0 or die "umount failed!"; Signed-off-by: Filipe David Borba Manana <fdmanana@gmail.com> Signed-off-by: Josef Bacik <jbacik@fb.com> Signed-off-by: Chris Mason <clm@fb.com>
Diffstat (limited to 'fs')
-rw-r--r--fs/btrfs/Makefile2
-rw-r--r--fs/btrfs/btrfs_inode.h1
-rw-r--r--fs/btrfs/ctree.h4
-rw-r--r--fs/btrfs/inode.c42
-rw-r--r--fs/btrfs/ioctl.c19
-rw-r--r--fs/btrfs/props.c427
-rw-r--r--fs/btrfs/props.h42
-rw-r--r--fs/btrfs/super.c3
-rw-r--r--fs/btrfs/xattr.c12
9 files changed, 542 insertions, 10 deletions
diff --git a/fs/btrfs/Makefile b/fs/btrfs/Makefile
index 1a44e42d602a..af7f000e905c 100644
--- a/fs/btrfs/Makefile
+++ b/fs/btrfs/Makefile
@@ -9,7 +9,7 @@ btrfs-y += super.o ctree.o extent-tree.o print-tree.o root-tree.o dir-item.o \
9 export.o tree-log.o free-space-cache.o zlib.o lzo.o \ 9 export.o tree-log.o free-space-cache.o zlib.o lzo.o \
10 compression.o delayed-ref.o relocation.o delayed-inode.o scrub.o \ 10 compression.o delayed-ref.o relocation.o delayed-inode.o scrub.o \
11 reada.o backref.o ulist.o qgroup.o send.o dev-replace.o raid56.o \ 11 reada.o backref.o ulist.o qgroup.o send.o dev-replace.o raid56.o \
12 uuid-tree.o 12 uuid-tree.o props.o
13 13
14btrfs-$(CONFIG_BTRFS_FS_POSIX_ACL) += acl.o 14btrfs-$(CONFIG_BTRFS_FS_POSIX_ACL) += acl.o
15btrfs-$(CONFIG_BTRFS_FS_CHECK_INTEGRITY) += check-integrity.o 15btrfs-$(CONFIG_BTRFS_FS_CHECK_INTEGRITY) += check-integrity.o
diff --git a/fs/btrfs/btrfs_inode.h b/fs/btrfs/btrfs_inode.h
index 661b0ac90e8f..8fed2125689e 100644
--- a/fs/btrfs/btrfs_inode.h
+++ b/fs/btrfs/btrfs_inode.h
@@ -43,6 +43,7 @@
43#define BTRFS_INODE_COPY_EVERYTHING 8 43#define BTRFS_INODE_COPY_EVERYTHING 8
44#define BTRFS_INODE_IN_DELALLOC_LIST 9 44#define BTRFS_INODE_IN_DELALLOC_LIST 9
45#define BTRFS_INODE_READDIO_NEED_LOCK 10 45#define BTRFS_INODE_READDIO_NEED_LOCK 10
46#define BTRFS_INODE_HAS_PROPS 11
46 47
47/* in memory btrfs inode */ 48/* in memory btrfs inode */
48struct btrfs_inode { 49struct btrfs_inode {
diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
index f52a60b9eba5..3cebb4aeddc7 100644
--- a/fs/btrfs/ctree.h
+++ b/fs/btrfs/ctree.h
@@ -3703,7 +3703,9 @@ int btrfs_start_delalloc_roots(struct btrfs_fs_info *fs_info, int delay_iput);
3703int btrfs_set_extent_delalloc(struct inode *inode, u64 start, u64 end, 3703int btrfs_set_extent_delalloc(struct inode *inode, u64 start, u64 end,
3704 struct extent_state **cached_state); 3704 struct extent_state **cached_state);
3705int btrfs_create_subvol_root(struct btrfs_trans_handle *trans, 3705int btrfs_create_subvol_root(struct btrfs_trans_handle *trans,
3706 struct btrfs_root *new_root, u64 new_dirid); 3706 struct btrfs_root *new_root,
3707 struct btrfs_root *parent_root,
3708 u64 new_dirid);
3707int btrfs_merge_bio_hook(int rw, struct page *page, unsigned long offset, 3709int btrfs_merge_bio_hook(int rw, struct page *page, unsigned long offset,
3708 size_t size, struct bio *bio, 3710 size_t size, struct bio *bio,
3709 unsigned long bio_flags); 3711 unsigned long bio_flags);
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index 23f18eb5fb55..1ea19cea96d0 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -58,6 +58,7 @@
58#include "inode-map.h" 58#include "inode-map.h"
59#include "backref.h" 59#include "backref.h"
60#include "hash.h" 60#include "hash.h"
61#include "props.h"
61 62
62struct btrfs_iget_args { 63struct btrfs_iget_args {
63 u64 ino; 64 u64 ino;
@@ -3265,7 +3266,8 @@ out:
3265 * slot is the slot the inode is in, objectid is the objectid of the inode 3266 * slot is the slot the inode is in, objectid is the objectid of the inode
3266 */ 3267 */
3267static noinline int acls_after_inode_item(struct extent_buffer *leaf, 3268static noinline int acls_after_inode_item(struct extent_buffer *leaf,
3268 int slot, u64 objectid) 3269 int slot, u64 objectid,
3270 int *first_xattr_slot)
3269{ 3271{
3270 u32 nritems = btrfs_header_nritems(leaf); 3272 u32 nritems = btrfs_header_nritems(leaf);
3271 struct btrfs_key found_key; 3273 struct btrfs_key found_key;
@@ -3281,6 +3283,7 @@ static noinline int acls_after_inode_item(struct extent_buffer *leaf,
3281 } 3283 }
3282 3284
3283 slot++; 3285 slot++;
3286 *first_xattr_slot = -1;
3284 while (slot < nritems) { 3287 while (slot < nritems) {
3285 btrfs_item_key_to_cpu(leaf, &found_key, slot); 3288 btrfs_item_key_to_cpu(leaf, &found_key, slot);
3286 3289
@@ -3290,6 +3293,8 @@ static noinline int acls_after_inode_item(struct extent_buffer *leaf,
3290 3293
3291 /* we found an xattr, assume we've got an acl */ 3294 /* we found an xattr, assume we've got an acl */
3292 if (found_key.type == BTRFS_XATTR_ITEM_KEY) { 3295 if (found_key.type == BTRFS_XATTR_ITEM_KEY) {
3296 if (*first_xattr_slot == -1)
3297 *first_xattr_slot = slot;
3293 if (found_key.offset == xattr_access || 3298 if (found_key.offset == xattr_access ||
3294 found_key.offset == xattr_default) 3299 found_key.offset == xattr_default)
3295 return 1; 3300 return 1;
@@ -3318,6 +3323,8 @@ static noinline int acls_after_inode_item(struct extent_buffer *leaf,
3318 * something larger than an xattr. We have to assume the inode 3323 * something larger than an xattr. We have to assume the inode
3319 * has acls 3324 * has acls
3320 */ 3325 */
3326 if (*first_xattr_slot == -1)
3327 *first_xattr_slot = slot;
3321 return 1; 3328 return 1;
3322} 3329}
3323 3330
@@ -3337,6 +3344,7 @@ static void btrfs_read_locked_inode(struct inode *inode)
3337 u32 rdev; 3344 u32 rdev;
3338 int ret; 3345 int ret;
3339 bool filled = false; 3346 bool filled = false;
3347 int first_xattr_slot;
3340 3348
3341 ret = btrfs_fill_inode(inode, &rdev); 3349 ret = btrfs_fill_inode(inode, &rdev);
3342 if (!ret) 3350 if (!ret)
@@ -3346,7 +3354,6 @@ static void btrfs_read_locked_inode(struct inode *inode)
3346 if (!path) 3354 if (!path)
3347 goto make_bad; 3355 goto make_bad;
3348 3356
3349 path->leave_spinning = 1;
3350 memcpy(&location, &BTRFS_I(inode)->location, sizeof(location)); 3357 memcpy(&location, &BTRFS_I(inode)->location, sizeof(location));
3351 3358
3352 ret = btrfs_lookup_inode(NULL, root, path, &location, 0); 3359 ret = btrfs_lookup_inode(NULL, root, path, &location, 0);
@@ -3429,12 +3436,21 @@ cache_acl:
3429 * any xattrs or acls 3436 * any xattrs or acls
3430 */ 3437 */
3431 maybe_acls = acls_after_inode_item(leaf, path->slots[0], 3438 maybe_acls = acls_after_inode_item(leaf, path->slots[0],
3432 btrfs_ino(inode)); 3439 btrfs_ino(inode), &first_xattr_slot);
3440 if (first_xattr_slot != -1) {
3441 path->slots[0] = first_xattr_slot;
3442 ret = btrfs_load_inode_props(inode, path);
3443 if (ret)
3444 btrfs_err(root->fs_info,
3445 "error loading props for ino %llu (root %llu): %d\n",
3446 btrfs_ino(inode),
3447 root->root_key.objectid, ret);
3448 }
3449 btrfs_free_path(path);
3450
3433 if (!maybe_acls) 3451 if (!maybe_acls)
3434 cache_no_acl(inode); 3452 cache_no_acl(inode);
3435 3453
3436 btrfs_free_path(path);
3437
3438 switch (inode->i_mode & S_IFMT) { 3454 switch (inode->i_mode & S_IFMT) {
3439 case S_IFREG: 3455 case S_IFREG:
3440 inode->i_mapping->a_ops = &btrfs_aops; 3456 inode->i_mapping->a_ops = &btrfs_aops;
@@ -5607,6 +5623,12 @@ static struct inode *btrfs_new_inode(struct btrfs_trans_handle *trans,
5607 5623
5608 btrfs_update_root_times(trans, root); 5624 btrfs_update_root_times(trans, root);
5609 5625
5626 ret = btrfs_inode_inherit_props(trans, inode, dir);
5627 if (ret)
5628 btrfs_err(root->fs_info,
5629 "error inheriting props for ino %llu (root %llu): %d",
5630 btrfs_ino(inode), root->root_key.objectid, ret);
5631
5610 return inode; 5632 return inode;
5611fail: 5633fail:
5612 if (dir) 5634 if (dir)
@@ -7889,7 +7911,9 @@ out:
7889 * create a new subvolume directory/inode (helper for the ioctl). 7911 * create a new subvolume directory/inode (helper for the ioctl).
7890 */ 7912 */
7891int btrfs_create_subvol_root(struct btrfs_trans_handle *trans, 7913int btrfs_create_subvol_root(struct btrfs_trans_handle *trans,
7892 struct btrfs_root *new_root, u64 new_dirid) 7914 struct btrfs_root *new_root,
7915 struct btrfs_root *parent_root,
7916 u64 new_dirid)
7893{ 7917{
7894 struct inode *inode; 7918 struct inode *inode;
7895 int err; 7919 int err;
@@ -7907,6 +7931,12 @@ int btrfs_create_subvol_root(struct btrfs_trans_handle *trans,
7907 set_nlink(inode, 1); 7931 set_nlink(inode, 1);
7908 btrfs_i_size_write(inode, 0); 7932 btrfs_i_size_write(inode, 0);
7909 7933
7934 err = btrfs_subvol_inherit_props(trans, new_root, parent_root);
7935 if (err)
7936 btrfs_err(new_root->fs_info,
7937 "error inheriting subvolume %llu properties: %d\n",
7938 new_root->root_key.objectid, err);
7939
7910 err = btrfs_update_inode(trans, new_root, inode); 7940 err = btrfs_update_inode(trans, new_root, inode);
7911 7941
7912 iput(inode); 7942 iput(inode);
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
index ed3edc283255..3970f32b2b80 100644
--- a/fs/btrfs/ioctl.c
+++ b/fs/btrfs/ioctl.c
@@ -56,6 +56,7 @@
56#include "rcu-string.h" 56#include "rcu-string.h"
57#include "send.h" 57#include "send.h"
58#include "dev-replace.h" 58#include "dev-replace.h"
59#include "props.h"
59#include "sysfs.h" 60#include "sysfs.h"
60 61
61static int btrfs_clone(struct inode *src, struct inode *inode, 62static int btrfs_clone(struct inode *src, struct inode *inode,
@@ -281,9 +282,25 @@ static int btrfs_ioctl_setflags(struct file *file, void __user *arg)
281 if (flags & FS_NOCOMP_FL) { 282 if (flags & FS_NOCOMP_FL) {
282 ip->flags &= ~BTRFS_INODE_COMPRESS; 283 ip->flags &= ~BTRFS_INODE_COMPRESS;
283 ip->flags |= BTRFS_INODE_NOCOMPRESS; 284 ip->flags |= BTRFS_INODE_NOCOMPRESS;
285
286 ret = btrfs_set_prop(inode, "btrfs.compression", NULL, 0, 0);
287 if (ret && ret != -ENODATA)
288 goto out_drop;
284 } else if (flags & FS_COMPR_FL) { 289 } else if (flags & FS_COMPR_FL) {
290 const char *comp;
291
285 ip->flags |= BTRFS_INODE_COMPRESS; 292 ip->flags |= BTRFS_INODE_COMPRESS;
286 ip->flags &= ~BTRFS_INODE_NOCOMPRESS; 293 ip->flags &= ~BTRFS_INODE_NOCOMPRESS;
294
295 if (root->fs_info->compress_type == BTRFS_COMPRESS_LZO)
296 comp = "lzo";
297 else
298 comp = "zlib";
299 ret = btrfs_set_prop(inode, "btrfs.compression",
300 comp, strlen(comp), 0);
301 if (ret)
302 goto out_drop;
303
287 } else { 304 } else {
288 ip->flags &= ~(BTRFS_INODE_COMPRESS | BTRFS_INODE_NOCOMPRESS); 305 ip->flags &= ~(BTRFS_INODE_COMPRESS | BTRFS_INODE_NOCOMPRESS);
289 } 306 }
@@ -502,7 +519,7 @@ static noinline int create_subvol(struct inode *dir,
502 519
503 btrfs_record_root_in_trans(trans, new_root); 520 btrfs_record_root_in_trans(trans, new_root);
504 521
505 ret = btrfs_create_subvol_root(trans, new_root, new_dirid); 522 ret = btrfs_create_subvol_root(trans, new_root, root, new_dirid);
506 if (ret) { 523 if (ret) {
507 /* We potentially lose an unused inode item here */ 524 /* We potentially lose an unused inode item here */
508 btrfs_abort_transaction(trans, root, ret); 525 btrfs_abort_transaction(trans, root, ret);
diff --git a/fs/btrfs/props.c b/fs/btrfs/props.c
new file mode 100644
index 000000000000..129b1dd28527
--- /dev/null
+++ b/fs/btrfs/props.c
@@ -0,0 +1,427 @@
1/*
2 * Copyright (C) 2014 Filipe David Borba Manana <fdmanana@gmail.com>
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public
6 * License v2 as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public
14 * License along with this program; if not, write to the
15 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
16 * Boston, MA 021110-1307, USA.
17 */
18
19#include <linux/hashtable.h>
20#include "props.h"
21#include "btrfs_inode.h"
22#include "hash.h"
23#include "transaction.h"
24#include "xattr.h"
25
26#define BTRFS_PROP_HANDLERS_HT_BITS 8
27static DEFINE_HASHTABLE(prop_handlers_ht, BTRFS_PROP_HANDLERS_HT_BITS);
28
29struct prop_handler {
30 struct hlist_node node;
31 const char *xattr_name;
32 int (*validate)(const char *value, size_t len);
33 int (*apply)(struct inode *inode, const char *value, size_t len);
34 const char *(*extract)(struct inode *inode);
35 int inheritable;
36};
37
38static int prop_compression_validate(const char *value, size_t len);
39static int prop_compression_apply(struct inode *inode,
40 const char *value,
41 size_t len);
42static const char *prop_compression_extract(struct inode *inode);
43
44static struct prop_handler prop_handlers[] = {
45 {
46 .xattr_name = XATTR_BTRFS_PREFIX "compression",
47 .validate = prop_compression_validate,
48 .apply = prop_compression_apply,
49 .extract = prop_compression_extract,
50 .inheritable = 1
51 },
52 {
53 .xattr_name = NULL
54 }
55};
56
57void __init btrfs_props_init(void)
58{
59 struct prop_handler *p;
60
61 hash_init(prop_handlers_ht);
62
63 for (p = &prop_handlers[0]; p->xattr_name; p++) {
64 u64 h = btrfs_name_hash(p->xattr_name, strlen(p->xattr_name));
65
66 hash_add(prop_handlers_ht, &p->node, h);
67 }
68}
69
70static const struct hlist_head *find_prop_handlers_by_hash(const u64 hash)
71{
72 struct hlist_head *h;
73
74 h = &prop_handlers_ht[hash_min(hash, BTRFS_PROP_HANDLERS_HT_BITS)];
75 if (hlist_empty(h))
76 return NULL;
77
78 return h;
79}
80
81static const struct prop_handler *
82find_prop_handler(const char *name,
83 const struct hlist_head *handlers)
84{
85 struct prop_handler *h;
86
87 if (!handlers) {
88 u64 hash = btrfs_name_hash(name, strlen(name));
89
90 handlers = find_prop_handlers_by_hash(hash);
91 if (!handlers)
92 return NULL;
93 }
94
95 hlist_for_each_entry(h, handlers, node)
96 if (!strcmp(h->xattr_name, name))
97 return h;
98
99 return NULL;
100}
101
102static int __btrfs_set_prop(struct btrfs_trans_handle *trans,
103 struct inode *inode,
104 const char *name,
105 const char *value,
106 size_t value_len,
107 int flags)
108{
109 const struct prop_handler *handler;
110 int ret;
111
112 if (strlen(name) <= XATTR_BTRFS_PREFIX_LEN)
113 return -EINVAL;
114
115 handler = find_prop_handler(name, NULL);
116 if (!handler)
117 return -EINVAL;
118
119 if (value_len == 0) {
120 ret = __btrfs_setxattr(trans, inode, handler->xattr_name,
121 NULL, 0, flags);
122 if (ret)
123 return ret;
124
125 ret = handler->apply(inode, NULL, 0);
126 ASSERT(ret == 0);
127
128 return ret;
129 }
130
131 ret = handler->validate(value, value_len);
132 if (ret)
133 return ret;
134 ret = __btrfs_setxattr(trans, inode, handler->xattr_name,
135 value, value_len, flags);
136 if (ret)
137 return ret;
138 ret = handler->apply(inode, value, value_len);
139 if (ret) {
140 __btrfs_setxattr(trans, inode, handler->xattr_name,
141 NULL, 0, flags);
142 return ret;
143 }
144
145 set_bit(BTRFS_INODE_HAS_PROPS, &BTRFS_I(inode)->runtime_flags);
146
147 return 0;
148}
149
150int btrfs_set_prop(struct inode *inode,
151 const char *name,
152 const char *value,
153 size_t value_len,
154 int flags)
155{
156 return __btrfs_set_prop(NULL, inode, name, value, value_len, flags);
157}
158
159static int iterate_object_props(struct btrfs_root *root,
160 struct btrfs_path *path,
161 u64 objectid,
162 void (*iterator)(void *,
163 const struct prop_handler *,
164 const char *,
165 size_t),
166 void *ctx)
167{
168 int ret;
169 char *name_buf = NULL;
170 char *value_buf = NULL;
171 int name_buf_len = 0;
172 int value_buf_len = 0;
173
174 while (1) {
175 struct btrfs_key key;
176 struct btrfs_dir_item *di;
177 struct extent_buffer *leaf;
178 u32 total_len, cur, this_len;
179 int slot;
180 const struct hlist_head *handlers;
181
182 slot = path->slots[0];
183 leaf = path->nodes[0];
184
185 if (slot >= btrfs_header_nritems(leaf)) {
186 ret = btrfs_next_leaf(root, path);
187 if (ret < 0)
188 goto out;
189 else if (ret > 0)
190 break;
191 continue;
192 }
193
194 btrfs_item_key_to_cpu(leaf, &key, slot);
195 if (key.objectid != objectid)
196 break;
197 if (key.type != BTRFS_XATTR_ITEM_KEY)
198 break;
199
200 handlers = find_prop_handlers_by_hash(key.offset);
201 if (!handlers)
202 goto next_slot;
203
204 di = btrfs_item_ptr(leaf, slot, struct btrfs_dir_item);
205 cur = 0;
206 total_len = btrfs_item_size_nr(leaf, slot);
207
208 while (cur < total_len) {
209 u32 name_len = btrfs_dir_name_len(leaf, di);
210 u32 data_len = btrfs_dir_data_len(leaf, di);
211 unsigned long name_ptr, data_ptr;
212 const struct prop_handler *handler;
213
214 this_len = sizeof(*di) + name_len + data_len;
215 name_ptr = (unsigned long)(di + 1);
216 data_ptr = name_ptr + name_len;
217
218 if (name_len <= XATTR_BTRFS_PREFIX_LEN ||
219 memcmp_extent_buffer(leaf, XATTR_BTRFS_PREFIX,
220 name_ptr,
221 XATTR_BTRFS_PREFIX_LEN))
222 goto next_dir_item;
223
224 if (name_len >= name_buf_len) {
225 kfree(name_buf);
226 name_buf_len = name_len + 1;
227 name_buf = kmalloc(name_buf_len, GFP_NOFS);
228 if (!name_buf) {
229 ret = -ENOMEM;
230 goto out;
231 }
232 }
233 read_extent_buffer(leaf, name_buf, name_ptr, name_len);
234 name_buf[name_len] = '\0';
235
236 handler = find_prop_handler(name_buf, handlers);
237 if (!handler)
238 goto next_dir_item;
239
240 if (data_len > value_buf_len) {
241 kfree(value_buf);
242 value_buf_len = data_len;
243 value_buf = kmalloc(data_len, GFP_NOFS);
244 if (!value_buf) {
245 ret = -ENOMEM;
246 goto out;
247 }
248 }
249 read_extent_buffer(leaf, value_buf, data_ptr, data_len);
250
251 iterator(ctx, handler, value_buf, data_len);
252next_dir_item:
253 cur += this_len;
254 di = (struct btrfs_dir_item *)((char *) di + this_len);
255 }
256
257next_slot:
258 path->slots[0]++;
259 }
260
261 ret = 0;
262out:
263 btrfs_release_path(path);
264 kfree(name_buf);
265 kfree(value_buf);
266
267 return ret;
268}
269
270static void inode_prop_iterator(void *ctx,
271 const struct prop_handler *handler,
272 const char *value,
273 size_t len)
274{
275 struct inode *inode = ctx;
276 struct btrfs_root *root = BTRFS_I(inode)->root;
277 int ret;
278
279 ret = handler->apply(inode, value, len);
280 if (unlikely(ret))
281 btrfs_warn(root->fs_info,
282 "error applying prop %s to ino %llu (root %llu): %d",
283 handler->xattr_name, btrfs_ino(inode),
284 root->root_key.objectid, ret);
285 else
286 set_bit(BTRFS_INODE_HAS_PROPS, &BTRFS_I(inode)->runtime_flags);
287}
288
289int btrfs_load_inode_props(struct inode *inode, struct btrfs_path *path)
290{
291 struct btrfs_root *root = BTRFS_I(inode)->root;
292 u64 ino = btrfs_ino(inode);
293 int ret;
294
295 ret = iterate_object_props(root, path, ino, inode_prop_iterator, inode);
296
297 return ret;
298}
299
300static int inherit_props(struct btrfs_trans_handle *trans,
301 struct inode *inode,
302 struct inode *parent)
303{
304 const struct prop_handler *h;
305 struct btrfs_root *root = BTRFS_I(inode)->root;
306 int ret;
307
308 if (!test_bit(BTRFS_INODE_HAS_PROPS,
309 &BTRFS_I(parent)->runtime_flags))
310 return 0;
311
312 for (h = &prop_handlers[0]; h->xattr_name; h++) {
313 const char *value;
314 u64 num_bytes;
315
316 if (!h->inheritable)
317 continue;
318
319 value = h->extract(parent);
320 if (!value)
321 continue;
322
323 num_bytes = btrfs_calc_trans_metadata_size(root, 1);
324 ret = btrfs_block_rsv_add(root, trans->block_rsv,
325 num_bytes, BTRFS_RESERVE_NO_FLUSH);
326 if (ret)
327 goto out;
328 ret = __btrfs_set_prop(trans, inode, h->xattr_name,
329 value, strlen(value), 0);
330 btrfs_block_rsv_release(root, trans->block_rsv, num_bytes);
331 if (ret)
332 goto out;
333 }
334 ret = 0;
335out:
336 return ret;
337}
338
339int btrfs_inode_inherit_props(struct btrfs_trans_handle *trans,
340 struct inode *inode,
341 struct inode *dir)
342{
343 if (!dir)
344 return 0;
345
346 return inherit_props(trans, inode, dir);
347}
348
349int btrfs_subvol_inherit_props(struct btrfs_trans_handle *trans,
350 struct btrfs_root *root,
351 struct btrfs_root *parent_root)
352{
353 struct btrfs_key key;
354 struct inode *parent_inode, *child_inode;
355 int ret;
356
357 key.objectid = BTRFS_FIRST_FREE_OBJECTID;
358 key.type = BTRFS_INODE_ITEM_KEY;
359 key.offset = 0;
360
361 parent_inode = btrfs_iget(parent_root->fs_info->sb, &key,
362 parent_root, NULL);
363 if (IS_ERR(parent_inode))
364 return PTR_ERR(parent_inode);
365
366 child_inode = btrfs_iget(root->fs_info->sb, &key, root, NULL);
367 if (IS_ERR(child_inode)) {
368 iput(parent_inode);
369 return PTR_ERR(child_inode);
370 }
371
372 ret = inherit_props(trans, child_inode, parent_inode);
373 iput(child_inode);
374 iput(parent_inode);
375
376 return ret;
377}
378
379static int prop_compression_validate(const char *value, size_t len)
380{
381 if (!strncmp("lzo", value, len))
382 return 0;
383 else if (!strncmp("zlib", value, len))
384 return 0;
385
386 return -EINVAL;
387}
388
389static int prop_compression_apply(struct inode *inode,
390 const char *value,
391 size_t len)
392{
393 int type;
394
395 if (len == 0) {
396 BTRFS_I(inode)->flags |= BTRFS_INODE_NOCOMPRESS;
397 BTRFS_I(inode)->flags &= ~BTRFS_INODE_COMPRESS;
398 BTRFS_I(inode)->force_compress = BTRFS_COMPRESS_NONE;
399
400 return 0;
401 }
402
403 if (!strncmp("lzo", value, len))
404 type = BTRFS_COMPRESS_LZO;
405 else if (!strncmp("zlib", value, len))
406 type = BTRFS_COMPRESS_ZLIB;
407 else
408 return -EINVAL;
409
410 BTRFS_I(inode)->flags &= ~BTRFS_INODE_NOCOMPRESS;
411 BTRFS_I(inode)->flags |= BTRFS_INODE_COMPRESS;
412 BTRFS_I(inode)->force_compress = type;
413
414 return 0;
415}
416
417static const char *prop_compression_extract(struct inode *inode)
418{
419 switch (BTRFS_I(inode)->force_compress) {
420 case BTRFS_COMPRESS_ZLIB:
421 return "zlib";
422 case BTRFS_COMPRESS_LZO:
423 return "lzo";
424 }
425
426 return NULL;
427}
diff --git a/fs/btrfs/props.h b/fs/btrfs/props.h
new file mode 100644
index 000000000000..100f18829d50
--- /dev/null
+++ b/fs/btrfs/props.h
@@ -0,0 +1,42 @@
1/*
2 * Copyright (C) 2014 Filipe David Borba Manana <fdmanana@gmail.com>
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public
6 * License v2 as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public
14 * License along with this program; if not, write to the
15 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
16 * Boston, MA 021110-1307, USA.
17 */
18
19#ifndef __BTRFS_PROPS_H
20#define __BTRFS_PROPS_H
21
22#include "ctree.h"
23
24void __init btrfs_props_init(void);
25
26int btrfs_set_prop(struct inode *inode,
27 const char *name,
28 const char *value,
29 size_t value_len,
30 int flags);
31
32int btrfs_load_inode_props(struct inode *inode, struct btrfs_path *path);
33
34int btrfs_inode_inherit_props(struct btrfs_trans_handle *trans,
35 struct inode *inode,
36 struct inode *dir);
37
38int btrfs_subvol_inherit_props(struct btrfs_trans_handle *trans,
39 struct btrfs_root *root,
40 struct btrfs_root *parent_root);
41
42#endif
diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c
index 16d7fc751ba8..461e41cb8ca7 100644
--- a/fs/btrfs/super.c
+++ b/fs/btrfs/super.c
@@ -48,6 +48,7 @@
48#include "transaction.h" 48#include "transaction.h"
49#include "btrfs_inode.h" 49#include "btrfs_inode.h"
50#include "print-tree.h" 50#include "print-tree.h"
51#include "props.h"
51#include "xattr.h" 52#include "xattr.h"
52#include "volumes.h" 53#include "volumes.h"
53#include "export.h" 54#include "export.h"
@@ -1865,6 +1866,8 @@ static int __init init_btrfs_fs(void)
1865{ 1866{
1866 int err; 1867 int err;
1867 1868
1869 btrfs_props_init();
1870
1868 err = btrfs_init_sysfs(); 1871 err = btrfs_init_sysfs();
1869 if (err) 1872 if (err)
1870 return err; 1873 return err;
diff --git a/fs/btrfs/xattr.c b/fs/btrfs/xattr.c
index 05740b9789e4..4b33765add38 100644
--- a/fs/btrfs/xattr.c
+++ b/fs/btrfs/xattr.c
@@ -27,6 +27,7 @@
27#include "transaction.h" 27#include "transaction.h"
28#include "xattr.h" 28#include "xattr.h"
29#include "disk-io.h" 29#include "disk-io.h"
30#include "props.h"
30 31
31 32
32ssize_t __btrfs_getxattr(struct inode *inode, const char *name, 33ssize_t __btrfs_getxattr(struct inode *inode, const char *name,
@@ -331,7 +332,8 @@ static bool btrfs_is_valid_xattr(const char *name)
331 XATTR_SECURITY_PREFIX_LEN) || 332 XATTR_SECURITY_PREFIX_LEN) ||
332 !strncmp(name, XATTR_SYSTEM_PREFIX, XATTR_SYSTEM_PREFIX_LEN) || 333 !strncmp(name, XATTR_SYSTEM_PREFIX, XATTR_SYSTEM_PREFIX_LEN) ||
333 !strncmp(name, XATTR_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN) || 334 !strncmp(name, XATTR_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN) ||
334 !strncmp(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN); 335 !strncmp(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN) ||
336 !strncmp(name, XATTR_BTRFS_PREFIX, XATTR_BTRFS_PREFIX_LEN);
335} 337}
336 338
337ssize_t btrfs_getxattr(struct dentry *dentry, const char *name, 339ssize_t btrfs_getxattr(struct dentry *dentry, const char *name,
@@ -373,6 +375,10 @@ int btrfs_setxattr(struct dentry *dentry, const char *name, const void *value,
373 if (!btrfs_is_valid_xattr(name)) 375 if (!btrfs_is_valid_xattr(name))
374 return -EOPNOTSUPP; 376 return -EOPNOTSUPP;
375 377
378 if (!strncmp(name, XATTR_BTRFS_PREFIX, XATTR_BTRFS_PREFIX_LEN))
379 return btrfs_set_prop(dentry->d_inode, name,
380 value, size, flags);
381
376 if (size == 0) 382 if (size == 0)
377 value = ""; /* empty EA, do not remove */ 383 value = ""; /* empty EA, do not remove */
378 384
@@ -402,6 +408,10 @@ int btrfs_removexattr(struct dentry *dentry, const char *name)
402 if (!btrfs_is_valid_xattr(name)) 408 if (!btrfs_is_valid_xattr(name))
403 return -EOPNOTSUPP; 409 return -EOPNOTSUPP;
404 410
411 if (!strncmp(name, XATTR_BTRFS_PREFIX, XATTR_BTRFS_PREFIX_LEN))
412 return btrfs_set_prop(dentry->d_inode, name,
413 NULL, 0, XATTR_REPLACE);
414
405 return __btrfs_setxattr(NULL, dentry->d_inode, name, NULL, 0, 415 return __btrfs_setxattr(NULL, dentry->d_inode, name, NULL, 0,
406 XATTR_REPLACE); 416 XATTR_REPLACE);
407} 417}