* [RFC PATCH] Btrfs: add support for inode properties
@ 2013-11-12 13:42 Filipe David Borba Manana
2013-11-12 19:24 ` Goffredo Baroncelli
` (3 more replies)
0 siblings, 4 replies; 11+ messages in thread
From: Filipe David Borba Manana @ 2013-11-12 13:42 UTC (permalink / raw)
To: linux-btrfs; +Cc: Filipe David Borba Manana
This change adds infrastructure to allow for generic properties for
inodes via a new ioctl. Properties are name/value pairs that can be
associated with inodes for different purposes. They're 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.
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.
Signed-off-by: Filipe David Borba Manana <fdmanana@gmail.com>
---
fs/btrfs/Makefile | 2 +-
fs/btrfs/btrfs_inode.h | 1 +
fs/btrfs/inode.c | 27 ++-
fs/btrfs/ioctl.c | 131 +++++++++++++++
fs/btrfs/props.c | 389 ++++++++++++++++++++++++++++++++++++++++++++
fs/btrfs/props.h | 40 +++++
include/uapi/linux/btrfs.h | 12 ++
7 files changed, 597 insertions(+), 5 deletions(-)
create mode 100644 fs/btrfs/props.c
create mode 100644 fs/btrfs/props.h
diff --git a/fs/btrfs/Makefile b/fs/btrfs/Makefile
index 1a44e42..af7f000 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 \
export.o tree-log.o free-space-cache.o zlib.o lzo.o \
compression.o delayed-ref.o relocation.o delayed-inode.o scrub.o \
reada.o backref.o ulist.o qgroup.o send.o dev-replace.o raid56.o \
- uuid-tree.o
+ uuid-tree.o props.o
btrfs-$(CONFIG_BTRFS_FS_POSIX_ACL) += acl.o
btrfs-$(CONFIG_BTRFS_FS_CHECK_INTEGRITY) += check-integrity.o
diff --git a/fs/btrfs/btrfs_inode.h b/fs/btrfs/btrfs_inode.h
index ac0b39d..7dc2f78 100644
--- a/fs/btrfs/btrfs_inode.h
+++ b/fs/btrfs/btrfs_inode.h
@@ -43,6 +43,7 @@
#define BTRFS_INODE_COPY_EVERYTHING 8
#define BTRFS_INODE_IN_DELALLOC_LIST 9
#define BTRFS_INODE_READDIO_NEED_LOCK 10
+#define BTRFS_INODE_HAS_PROPS 11
/* in memory btrfs inode */
struct btrfs_inode {
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index f167ced..7a40aa6 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -58,6 +58,7 @@
#include "inode-map.h"
#include "backref.h"
#include "hash.h"
+#include "props.h"
struct btrfs_iget_args {
u64 ino;
@@ -3247,7 +3248,8 @@ out:
* slot is the slot the inode is in, objectid is the objectid of the inode
*/
static noinline int acls_after_inode_item(struct extent_buffer *leaf,
- int slot, u64 objectid)
+ int slot, u64 objectid,
+ int *has_xattrs)
{
u32 nritems = btrfs_header_nritems(leaf);
struct btrfs_key found_key;
@@ -3272,6 +3274,7 @@ static noinline int acls_after_inode_item(struct extent_buffer *leaf,
/* we found an xattr, assume we've got an acl */
if (found_key.type == BTRFS_XATTR_ITEM_KEY) {
+ *has_xattrs = 1;
if (found_key.offset == xattr_access ||
found_key.offset == xattr_default)
return 1;
@@ -3300,13 +3303,15 @@ static noinline int acls_after_inode_item(struct extent_buffer *leaf,
* something larger than an xattr. We have to assume the inode
* has acls
*/
+ *has_xattrs = 1;
+
return 1;
}
/*
* read an inode from the btree into the in-memory inode
*/
-static void btrfs_read_locked_inode(struct inode *inode)
+static void btrfs_read_locked_inode(struct inode *inode, int *has_xattrs)
{
struct btrfs_path *path;
struct extent_buffer *leaf;
@@ -3386,7 +3391,7 @@ cache_acl:
* any xattrs or acls
*/
maybe_acls = acls_after_inode_item(leaf, path->slots[0],
- btrfs_ino(inode));
+ btrfs_ino(inode), has_xattrs);
if (!maybe_acls)
cache_no_acl(inode);
@@ -4871,6 +4876,8 @@ struct inode *btrfs_iget(struct super_block *s, struct btrfs_key *location,
struct btrfs_root *root, int *new)
{
struct inode *inode;
+ int has_xattrs = 0;
+ int ret;
inode = btrfs_iget_locked(s, location->objectid, root);
if (!inode)
@@ -4879,10 +4886,17 @@ struct inode *btrfs_iget(struct super_block *s, struct btrfs_key *location,
if (inode->i_state & I_NEW) {
BTRFS_I(inode)->root = root;
memcpy(&BTRFS_I(inode)->location, location, sizeof(*location));
- btrfs_read_locked_inode(inode);
+ btrfs_read_locked_inode(inode, &has_xattrs);
if (!is_bad_inode(inode)) {
inode_tree_add(inode);
unlock_new_inode(inode);
+ if (has_xattrs) {
+ ret = btrfs_load_inode_props(inode);
+ if (ret)
+ pr_err("btrfs: error loading props for ino %llu (root %llu): %d\n",
+ btrfs_ino(inode),
+ root->root_key.objectid, ret);
+ }
if (new)
*new = 1;
} else {
@@ -5486,6 +5500,11 @@ static struct inode *btrfs_new_inode(struct btrfs_trans_handle *trans,
btrfs_update_root_times(trans, root);
+ ret = btrfs_inherit_props(trans, inode, dir);
+ if (ret)
+ pr_err("btrfs: error inheriting props for ino %llu (root %llu): %d",
+ btrfs_ino(inode), root->root_key.objectid, ret);
+
return inode;
fail:
if (dir)
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
index 1d04b55..864b8c7 100644
--- a/fs/btrfs/ioctl.c
+++ b/fs/btrfs/ioctl.c
@@ -53,6 +53,7 @@
#include "locking.h"
#include "inode-map.h"
#include "backref.h"
+#include "props.h"
#include "rcu-string.h"
#include "send.h"
#include "dev-replace.h"
@@ -4474,6 +4475,132 @@ out_unlock:
return ret;
}
+static int btrfs_ioctl_get_prop(struct file *file, void __user *arg)
+{
+ struct inode *inode = file_inode(file);
+ struct btrfs_ioctl_getset_prop_args *get_args = NULL;
+ int ret;
+ char *name = NULL;
+ char *value = NULL;
+ size_t value_len = 0;
+ size_t off = offsetof(struct btrfs_ioctl_getset_prop_args, value_len);
+
+ ret = inode_permission(inode, MAY_READ);
+ if (ret)
+ return ret;
+
+ get_args = memdup_user(arg, sizeof(*get_args));
+ if (IS_ERR(get_args)) {
+ ret = PTR_ERR(get_args);
+ get_args = NULL;
+ goto out;
+ }
+
+ name = kmalloc(get_args->name_len + 1, GFP_NOFS);
+ if (!name) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ if (copy_from_user(name, (char __user *) get_args->name_ptr,
+ get_args->name_len)) {
+ ret = -EFAULT;
+ goto out;
+ }
+ name[get_args->name_len] = '\0';
+
+ if (get_args->value_len == 0) {
+ ret = btrfs_get_prop(inode, name, NULL, &value_len);
+ if (!ret) {
+ ASSERT(value_len <= (u16)-1);
+ if (copy_to_user(arg + off, &value_len,
+ sizeof(get_args->value_len)))
+ ret = -EFAULT;
+ }
+ goto out;
+ }
+
+ value_len = get_args->value_len;
+ value = kmalloc(value_len, GFP_NOFS);
+ if (!value) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ ret = btrfs_get_prop(inode, name, value, &value_len);
+ if (ret)
+ goto out;
+
+ if (copy_to_user((void __user *) get_args->value_ptr,
+ value, value_len)) {
+ ret = -EFAULT;
+ goto out;
+ }
+ if (copy_to_user(arg + off, &value_len, sizeof(get_args->value_len)))
+ ret = -EFAULT;
+out:
+ kfree(value);
+ kfree(name);
+ kfree(get_args);
+
+ return ret;
+}
+
+static int btrfs_ioctl_set_prop(struct file *file, void __user *arg)
+{
+ struct inode *inode = file_inode(file);
+ struct btrfs_root *root = BTRFS_I(inode)->root;
+ struct btrfs_ioctl_getset_prop_args *set_args = NULL;
+ char *name = NULL;
+ char *value = NULL;
+ int ret;
+
+ ret = inode_permission(inode, MAY_WRITE);
+ if (ret)
+ return ret;
+
+ set_args = memdup_user(arg, sizeof(*set_args));
+ if (IS_ERR(set_args)) {
+ ret = PTR_ERR(set_args);
+ set_args = NULL;
+ goto out;
+ }
+
+ if (set_args->name_len + set_args->value_len >
+ BTRFS_MAX_XATTR_SIZE(root)) {
+ ret = -ENOSPC;
+ goto out;
+ }
+
+ name = kmalloc(set_args->name_len + 1, GFP_NOFS);
+ if (!name) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ if (copy_from_user(name, (char __user *) set_args->name_ptr,
+ set_args->name_len)) {
+ ret = -EFAULT;
+ goto out;
+ }
+ name[set_args->name_len] = '\0';
+
+ value = memdup_user((char __user *) set_args->value_ptr,
+ set_args->value_len);
+ if (IS_ERR(value)) {
+ ret = PTR_ERR(value);
+ value = NULL;
+ goto out;
+ }
+
+ ret = btrfs_set_prop(inode, name, value, set_args->value_len);
+out:
+ kfree(value);
+ kfree(name);
+ kfree(set_args);
+
+ return ret;
+}
+
long btrfs_ioctl(struct file *file, unsigned int
cmd, unsigned long arg)
{
@@ -4539,6 +4666,10 @@ long btrfs_ioctl(struct file *file, unsigned int
return btrfs_ioctl_logical_to_ino(root, argp);
case BTRFS_IOC_SPACE_INFO:
return btrfs_ioctl_space_info(root, argp);
+ case BTRFS_IOC_GET_PROP:
+ return btrfs_ioctl_get_prop(file, argp);
+ case BTRFS_IOC_SET_PROP:
+ return btrfs_ioctl_set_prop(file, argp);
case BTRFS_IOC_SYNC: {
int ret;
diff --git a/fs/btrfs/props.c b/fs/btrfs/props.c
new file mode 100644
index 0000000..6093fb8
--- /dev/null
+++ b/fs/btrfs/props.c
@@ -0,0 +1,389 @@
+/*
+ * Copyright (C) 2013 Filipe David Borba Manana <fdmanana@gmail.com>
+ *
+ * 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 "props.h"
+#include "btrfs_inode.h"
+#include "transaction.h"
+#include "xattr.h"
+
+struct prop_handler {
+ const char *xattr_name;
+ int (*validate)(const char *value, size_t len);
+ int (*apply)(struct inode *inode, const char *value, size_t len);
+ int inheritable;
+};
+
+static int prop_compression_validate(const char *value, size_t len);
+
+static int prop_compression_apply(struct inode *inode,
+ const char *value,
+ size_t len);
+
+static const struct prop_handler prop_handlers[] = {
+ {
+ .xattr_name = "btrfs.compression",
+ .validate = prop_compression_validate,
+ .apply = prop_compression_apply,
+ .inheritable = 1
+ },
+ {
+ .xattr_name = NULL,
+ .validate = NULL,
+ .apply = NULL
+ }
+};
+
+
+static const struct prop_handler *find_prop_handler(const char *name)
+{
+ const struct prop_handler *p;
+
+ for (p = &prop_handlers[0]; p->xattr_name; p++)
+ if (!strcmp(p->xattr_name + 6, name))
+ return p;
+ return NULL;
+}
+
+static int __btrfs_set_prop(struct btrfs_trans_handle *trans,
+ struct inode *inode,
+ const char *name,
+ const char *value,
+ size_t value_len)
+{
+ const struct prop_handler *handler;
+ int ret;
+
+ handler = find_prop_handler(name);
+ if (!handler)
+ return -EINVAL;
+
+ if (value_len == 0) {
+ ret = __btrfs_setxattr(trans, inode, handler->xattr_name,
+ NULL, 0, XATTR_REPLACE);
+ if (ret)
+ return ret;
+
+ mutex_lock(&inode->i_mutex);
+ ret = handler->apply(inode, NULL, 0);
+ mutex_unlock(&inode->i_mutex);
+ ASSERT(ret == 0);
+
+ return ret;
+ }
+
+ ret = handler->validate(value, value_len);
+ if (ret)
+ return ret;
+
+ ret = __btrfs_setxattr(trans, inode, handler->xattr_name,
+ value, value_len, XATTR_CREATE);
+ if (ret == -EEXIST)
+ ret = __btrfs_setxattr(trans, inode, handler->xattr_name,
+ value, value_len, XATTR_REPLACE);
+
+ if (ret)
+ return ret;
+
+ mutex_lock(&inode->i_mutex);
+ ret = handler->apply(inode, value, value_len);
+ mutex_unlock(&inode->i_mutex);
+
+ if (!ret)
+ set_bit(BTRFS_INODE_HAS_PROPS, &BTRFS_I(inode)->runtime_flags);
+
+ return ret;
+}
+
+int btrfs_set_prop(struct inode *inode,
+ const char *name,
+ const char *value,
+ size_t value_len)
+{
+ return __btrfs_set_prop(NULL, inode, name, value, value_len);
+}
+
+
+/*
+ * Return -ENODATA if property is not set.
+ * Return -ERANGE if input buffer isn't big enough to hold property value.
+ */
+int btrfs_get_prop(struct inode *inode,
+ const char *name,
+ char *buf,
+ size_t *len)
+{
+ const struct prop_handler *handler;
+ ssize_t ret;
+
+ handler = find_prop_handler(name);
+ if (!handler)
+ return -EINVAL;
+
+ if (*len == 0) {
+ ret = __btrfs_getxattr(inode, handler->xattr_name, NULL, 0);
+ if (ret < 0)
+ return ret;
+ *len = (size_t) ret;
+ return 0;
+ }
+
+ ret = __btrfs_getxattr(inode, handler->xattr_name, buf, *len);
+ if (ret < 0)
+ return ret;
+
+ *len = (size_t) ret;
+
+ return 0;
+}
+
+int btrfs_load_inode_props(struct inode *inode)
+{
+ struct btrfs_root *root = BTRFS_I(inode)->root;
+ struct btrfs_key key;
+ struct btrfs_path *path;
+ u64 ino = btrfs_ino(inode);
+ char *name_buf = NULL;
+ char *value_buf = NULL;
+ int name_buf_len = 0;
+ int value_buf_len = 0;
+ int ret;
+
+ path = btrfs_alloc_path();
+ if (!path)
+ return -ENOMEM;
+
+ key.objectid = ino;
+ key.type = BTRFS_XATTR_ITEM_KEY;
+ key.offset = 0;
+
+ ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
+ if (ret < 0)
+ goto out;
+
+ while (1) {
+ struct btrfs_dir_item *di;
+ struct extent_buffer *leaf;
+ u32 total_len, cur, this_len;
+ int slot;
+
+ slot = path->slots[0];
+ leaf = path->nodes[0];
+
+ if (slot >= btrfs_header_nritems(leaf)) {
+ ret = btrfs_next_leaf(root, path);
+ if (ret < 0)
+ goto out;
+ else if (ret > 0)
+ break;
+ continue;
+ }
+
+ btrfs_item_key_to_cpu(leaf, &key, slot);
+ if (key.objectid != ino || key.type != BTRFS_XATTR_ITEM_KEY)
+ break;
+
+ di = btrfs_item_ptr(leaf, slot, struct btrfs_dir_item);
+ cur = 0;
+ total_len = btrfs_item_size_nr(leaf, slot);
+
+ while (cur < total_len) {
+ u32 name_len = btrfs_dir_name_len(leaf, di);
+ u32 data_len = btrfs_dir_data_len(leaf, di);
+ unsigned long name_ptr, data_ptr;
+ const struct prop_handler *handler;
+
+ this_len = sizeof(*di) + name_len + data_len;
+ name_ptr = (unsigned long)(di + 1);
+ data_ptr = name_ptr + name_len;
+
+ if (name_len <= 6 ||
+ memcmp_extent_buffer(leaf, "btrfs.", name_ptr, 6))
+ goto next;
+
+ if (name_len >= name_buf_len) {
+ kfree(name_buf);
+ name_buf_len = name_len + 1;
+ name_buf = kmalloc(name_buf_len, GFP_NOFS);
+ if (!name_buf) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ }
+ read_extent_buffer(leaf, name_buf, name_ptr, name_len);
+ name_buf[name_len] = '\0';
+
+ if (data_len > value_buf_len) {
+ kfree(value_buf);
+ value_buf_len = data_len;
+ value_buf = kmalloc(data_len, GFP_NOFS);
+ if (!value_buf) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ }
+ read_extent_buffer(leaf, value_buf, data_ptr, data_len);
+
+ handler = find_prop_handler(name_buf + 6);
+ if (!handler) {
+ pr_warn("btrfs: handler for prop %s, ino %llu (root %llu), not found",
+ name_buf, ino, root->root_key.objectid);
+ goto next;
+ }
+
+ ret = handler->apply(inode, value_buf, data_len);
+ if (ret)
+ pr_warn("btrfs: error applying prop %s to ino %llu (root %llu): %d",
+ name_buf, ino,
+ root->root_key.objectid, ret);
+ else
+ set_bit(BTRFS_INODE_HAS_PROPS,
+ &BTRFS_I(inode)->runtime_flags);
+next:
+ cur += this_len;
+ di = (struct btrfs_dir_item *)((char *) di + this_len);
+ }
+
+ path->slots[0]++;
+ }
+
+ ret = 0;
+out:
+ btrfs_free_path(path);
+ kfree(name_buf);
+ kfree(value_buf);
+
+ return ret;
+}
+
+int btrfs_inherit_props(struct btrfs_trans_handle *trans,
+ struct inode *inode,
+ struct inode *dir)
+{
+ const struct prop_handler *h;
+ struct btrfs_root *root = BTRFS_I(inode)->root;
+ char *buf = NULL;
+ int buf_len = 0;
+ struct btrfs_block_rsv *rsv = NULL;
+ struct btrfs_block_rsv *trans_rsv = trans->block_rsv;
+ int ret;
+
+ if (!dir)
+ return 0;
+
+ if (!test_bit(BTRFS_INODE_HAS_PROPS,
+ &BTRFS_I(dir)->runtime_flags))
+ return 0;
+
+ for (h = &prop_handlers[0]; h->xattr_name; h++) {
+ size_t len = 0;
+ const char *name = h->xattr_name + 6;
+ u64 num_bytes;
+
+ if (!h->inheritable)
+ continue;
+again:
+ ret = btrfs_get_prop(dir, name, NULL, &len);
+ if (ret == -ENODATA)
+ continue;
+ else if (ret < 0)
+ goto out;
+
+ if (len > buf_len) {
+ kfree(buf);
+ buf = kmalloc(len, GFP_NOFS);
+ if (!buf) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ buf_len = len;
+ }
+
+ ret = btrfs_get_prop(dir, name, buf, &len);
+ if (ret == -ERANGE)
+ goto again;
+ else if (ret == -ENODATA)
+ continue;
+ else if (ret < 0)
+ goto out;
+
+ if (!rsv) {
+ rsv = btrfs_alloc_block_rsv(root, BTRFS_BLOCK_RSV_TEMP);
+ if (!rsv) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ trans->block_rsv = rsv;
+ }
+
+ num_bytes = btrfs_calc_trans_metadata_size(root, 2);
+ ret = btrfs_block_rsv_add(root, trans->block_rsv,
+ num_bytes, BTRFS_RESERVE_NO_FLUSH);
+ if (ret)
+ goto out;
+ ret = __btrfs_set_prop(trans, inode, name, buf, len);
+ if (ret)
+ goto out;
+ }
+ ret = 0;
+out:
+ kfree(buf);
+ if (rsv) {
+ btrfs_free_block_rsv(root, rsv);
+ trans->block_rsv = trans_rsv;
+ }
+
+ return ret;
+}
+
+static int prop_compression_validate(const char *value, size_t len)
+{
+ if (!strncmp("lzo", value, len))
+ return 0;
+ else if (!strncmp("zlib", value, len))
+ return 0;
+
+ return -EINVAL;
+}
+
+static int prop_compression_apply(struct inode *inode,
+ const char *value,
+ size_t len)
+{
+ int type;
+
+ if (len == 0) {
+ BTRFS_I(inode)->flags |= BTRFS_INODE_NOCOMPRESS;
+ BTRFS_I(inode)->flags &= ~BTRFS_INODE_COMPRESS;
+ BTRFS_I(inode)->force_compress = BTRFS_COMPRESS_NONE;
+
+ return 0;
+ }
+
+ if (!strncmp("lzo", value, len))
+ type = BTRFS_COMPRESS_LZO;
+ else if (!strncmp("zlib", value, len))
+ type = BTRFS_COMPRESS_ZLIB;
+ else
+ return -EINVAL;
+
+ BTRFS_I(inode)->flags &= ~BTRFS_INODE_NOCOMPRESS;
+ BTRFS_I(inode)->flags |= BTRFS_INODE_COMPRESS;
+ BTRFS_I(inode)->force_compress = type;
+
+ return 0;
+}
diff --git a/fs/btrfs/props.h b/fs/btrfs/props.h
new file mode 100644
index 0000000..2b07034
--- /dev/null
+++ b/fs/btrfs/props.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2013 Filipe David Borba Manana <fdmanana@gmail.com>
+ *
+ * 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 __BTRFS_PROPS_H
+#define __BTRFS_PROPS_H
+
+#include "ctree.h"
+
+int btrfs_set_prop(struct inode *inode,
+ const char *name,
+ const char *value,
+ size_t value_len);
+
+int btrfs_get_prop(struct inode *inode,
+ const char *name,
+ char *buf,
+ size_t *len);
+
+int btrfs_load_inode_props(struct inode *inode);
+
+int btrfs_inherit_props(struct btrfs_trans_handle *trans,
+ struct inode *inode,
+ struct inode *dir);
+
+#endif
diff --git a/include/uapi/linux/btrfs.h b/include/uapi/linux/btrfs.h
index 45e6189..265b8ae 100644
--- a/include/uapi/linux/btrfs.h
+++ b/include/uapi/linux/btrfs.h
@@ -472,6 +472,14 @@ struct btrfs_ioctl_send_args {
__u64 reserved[4]; /* in */
};
+struct btrfs_ioctl_getset_prop_args {
+ __u16 name_len; /* in */
+ __u16 value_len; /* in/out */
+ __u8 unused[4];
+ __u64 name_ptr; /* in */
+ __u64 value_ptr; /* in/out */
+};
+
/* Error codes as returned by the kernel */
enum btrfs_err_code {
notused,
@@ -552,6 +560,10 @@ static inline char *btrfs_err_str(enum btrfs_err_code err_code)
#define BTRFS_IOC_DEFAULT_SUBVOL _IOW(BTRFS_IOCTL_MAGIC, 19, __u64)
#define BTRFS_IOC_SPACE_INFO _IOWR(BTRFS_IOCTL_MAGIC, 20, \
struct btrfs_ioctl_space_args)
+#define BTRFS_IOC_GET_PROP _IOR(BTRFS_IOCTL_MAGIC, 21, \
+ struct btrfs_ioctl_getset_prop_args)
+#define BTRFS_IOC_SET_PROP _IOW(BTRFS_IOCTL_MAGIC, 21, \
+ struct btrfs_ioctl_getset_prop_args)
#define BTRFS_IOC_START_SYNC _IOR(BTRFS_IOCTL_MAGIC, 24, __u64)
#define BTRFS_IOC_WAIT_SYNC _IOW(BTRFS_IOCTL_MAGIC, 22, __u64)
#define BTRFS_IOC_SNAP_CREATE_V2 _IOW(BTRFS_IOCTL_MAGIC, 23, \
--
1.7.9.5
^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [RFC PATCH] Btrfs: add support for inode properties
2013-11-12 13:42 [RFC PATCH] Btrfs: add support for inode properties Filipe David Borba Manana
@ 2013-11-12 19:24 ` Goffredo Baroncelli
2013-11-12 20:04 ` Filipe David Manana
2013-11-13 1:21 ` [PATCH V2] " Filipe David Borba Manana
` (2 subsequent siblings)
3 siblings, 1 reply; 11+ messages in thread
From: Goffredo Baroncelli @ 2013-11-12 19:24 UTC (permalink / raw)
To: Filipe David Borba Manana; +Cc: linux-btrfs
Hi Filipe,
On 2013-11-12 14:42, Filipe David Borba Manana wrote:
> This change adds infrastructure to allow for generic properties for
> inodes via a new ioctl.
I am sure that there is a valid reason, but I was not able to find it:
why implement a new ioctl instead of using the *{set,get)xattr(2) syscall ?
I noticed that you implemented the Compression properties set/get: is
this from "chattr -c" ?
[... cut other lines ... ]
G.Baroncelli
--
gpg @keyserver.linux.it: Goffredo Baroncelli (kreijackATinwind.it>
Key fingerprint BBF5 1610 0B64 DAC6 5F7D 17B2 0EDA 9B37 8B82 E0B5
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [RFC PATCH] Btrfs: add support for inode properties
2013-11-12 19:24 ` Goffredo Baroncelli
@ 2013-11-12 20:04 ` Filipe David Manana
2013-11-12 20:07 ` Hugo Mills
0 siblings, 1 reply; 11+ messages in thread
From: Filipe David Manana @ 2013-11-12 20:04 UTC (permalink / raw)
To: kreijack; +Cc: linux-btrfs@vger.kernel.org
On Tue, Nov 12, 2013 at 7:24 PM, Goffredo Baroncelli <kreijack@libero.it> wrote:
> Hi Filipe,
Hi
>
> On 2013-11-12 14:42, Filipe David Borba Manana wrote:
>> This change adds infrastructure to allow for generic properties for
>> inodes via a new ioctl.
>
> I am sure that there is a valid reason, but I was not able to find it:
> why implement a new ioctl instead of using the *{set,get)xattr(2) syscall ?
Those require one of the following name prefixes: "user.",
"security.", "trusted." or "system.". Only "user." can be set from
user space and requires no special privileges/capabilities (if you
have the necessary permissions on the target inode).
It could be implemented via the "user." prefix, like
"user.btrfs.something" for example - but it can break user
applications if they use such prefix already, and we want to validate
the values set for such properties too.
>
> I noticed that you implemented the Compression properties set/get: is
> this from "chattr -c" ?
With chattr -c you can't specify the compression algorithm - it will
use the default (zlib) or the one you specified via mount options.
thanks
>
> [... cut other lines ... ]
>
> G.Baroncelli
>
> --
> gpg @keyserver.linux.it: Goffredo Baroncelli (kreijackATinwind.it>
> Key fingerprint BBF5 1610 0B64 DAC6 5F7D 17B2 0EDA 9B37 8B82 E0B5
--
Filipe David Manana,
"Reasonable men adapt themselves to the world.
Unreasonable men adapt the world to themselves.
That's why all progress depends on unreasonable men."
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [RFC PATCH] Btrfs: add support for inode properties
2013-11-12 20:04 ` Filipe David Manana
@ 2013-11-12 20:07 ` Hugo Mills
2013-11-13 0:30 ` Filipe David Manana
0 siblings, 1 reply; 11+ messages in thread
From: Hugo Mills @ 2013-11-12 20:07 UTC (permalink / raw)
To: Filipe David Manana; +Cc: kreijack, linux-btrfs@vger.kernel.org
[-- Attachment #1: Type: text/plain, Size: 1778 bytes --]
On Tue, Nov 12, 2013 at 08:04:47PM +0000, Filipe David Manana wrote:
> On Tue, Nov 12, 2013 at 7:24 PM, Goffredo Baroncelli <kreijack@libero.it> wrote:
> > Hi Filipe,
>
> Hi
>
> >
> > On 2013-11-12 14:42, Filipe David Borba Manana wrote:
> >> This change adds infrastructure to allow for generic properties for
> >> inodes via a new ioctl.
> >
> > I am sure that there is a valid reason, but I was not able to find it:
> > why implement a new ioctl instead of using the *{set,get)xattr(2) syscall ?
>
> Those require one of the following name prefixes: "user.",
> "security.", "trusted." or "system.". Only "user." can be set from
> user space and requires no special privileges/capabilities (if you
> have the necessary permissions on the target inode).
Why only that limited set? Is that limited by POSIX, or executive
fiat? Is there any likelihood of us getting an additional namespace?
(fs.* maybe?)
Hugo.
> It could be implemented via the "user." prefix, like
> "user.btrfs.something" for example - but it can break user
> applications if they use such prefix already, and we want to validate
> the values set for such properties too.
>
> >
> > I noticed that you implemented the Compression properties set/get: is
> > this from "chattr -c" ?
>
> With chattr -c you can't specify the compression algorithm - it will
> use the default (zlib) or the one you specified via mount options.
>
> thanks
>
> >
> > [... cut other lines ... ]
> >
> > G.Baroncelli
> >
--
=== Hugo Mills: hugo@... carfax.org.uk | darksatanic.net | lug.org.uk ===
PGP key: 65E74AC0 from wwwkeys.eu.pgp.net or http://www.carfax.org.uk
--- I write in C because using pointer arithmetic lets people ---
know that you're virile. -- Matthew Garrett
[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 828 bytes --]
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [RFC PATCH] Btrfs: add support for inode properties
2013-11-12 20:07 ` Hugo Mills
@ 2013-11-13 0:30 ` Filipe David Manana
0 siblings, 0 replies; 11+ messages in thread
From: Filipe David Manana @ 2013-11-13 0:30 UTC (permalink / raw)
To: Hugo Mills, Filipe David Manana, kreijack,
linux-btrfs@vger.kernel.org
On Tue, Nov 12, 2013 at 8:07 PM, Hugo Mills <hugo@carfax.org.uk> wrote:
> On Tue, Nov 12, 2013 at 08:04:47PM +0000, Filipe David Manana wrote:
>> On Tue, Nov 12, 2013 at 7:24 PM, Goffredo Baroncelli <kreijack@libero.it> wrote:
>> > Hi Filipe,
>>
>> Hi
>>
>> >
>> > On 2013-11-12 14:42, Filipe David Borba Manana wrote:
>> >> This change adds infrastructure to allow for generic properties for
>> >> inodes via a new ioctl.
>> >
>> > I am sure that there is a valid reason, but I was not able to find it:
>> > why implement a new ioctl instead of using the *{set,get)xattr(2) syscall ?
>>
>> Those require one of the following name prefixes: "user.",
>> "security.", "trusted." or "system.". Only "user." can be set from
>> user space and requires no special privileges/capabilities (if you
>> have the necessary permissions on the target inode).
>
> Why only that limited set? Is that limited by POSIX, or executive
> fiat? Is there any likelihood of us getting an additional namespace?
> (fs.* maybe?)
Good question Hugo.
I have the understanding there's no issue using a new namespace,
perhaps it's not true.
What leads me to think this is that in fact there's one fs which uses
its own custom prefix, hfsplus:
https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/include/uapi/linux/xattr.h?id=refs/tags/v3.12#n20
And it uses it in fs/hfsplus/xattr.c. I'm now realizing we probably
can use directly the regular setxattr and getxattr code paths in
fs/btrfs/xattr.c and avoid the new ioctls, simplifying things. Will
give it a try.
The vfs path for setting and getting xattrs also doesn't block us from
using xattrs with a new prefix (doesn't use the hfsplus "osx." prefix
constant anywhere).
thanks
>
> Hugo.
>
>> It could be implemented via the "user." prefix, like
>> "user.btrfs.something" for example - but it can break user
>> applications if they use such prefix already, and we want to validate
>> the values set for such properties too.
>>
>> >
>> > I noticed that you implemented the Compression properties set/get: is
>> > this from "chattr -c" ?
>>
>> With chattr -c you can't specify the compression algorithm - it will
>> use the default (zlib) or the one you specified via mount options.
>>
>> thanks
>>
>> >
>> > [... cut other lines ... ]
>> >
>> > G.Baroncelli
>> >
>
> --
> === Hugo Mills: hugo@... carfax.org.uk | darksatanic.net | lug.org.uk ===
> PGP key: 65E74AC0 from wwwkeys.eu.pgp.net or http://www.carfax.org.uk
> --- I write in C because using pointer arithmetic lets people ---
> know that you're virile. -- Matthew Garrett
--
Filipe David Manana,
"Reasonable men adapt themselves to the world.
Unreasonable men adapt the world to themselves.
That's why all progress depends on unreasonable men."
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH V2] Btrfs: add support for inode properties
2013-11-12 13:42 [RFC PATCH] Btrfs: add support for inode properties Filipe David Borba Manana
2013-11-12 19:24 ` Goffredo Baroncelli
@ 2013-11-13 1:21 ` Filipe David Borba Manana
2013-11-13 18:59 ` Goffredo Baroncelli
2013-11-19 16:00 ` [PATCH v3] " Filipe David Borba Manana
2014-01-07 11:47 ` [RFC PATCH v4] " Filipe David Borba Manana
3 siblings, 1 reply; 11+ messages in thread
From: Filipe David Borba Manana @ 2013-11-13 1:21 UTC (permalink / raw)
To: linux-btrfs; +Cc: Filipe David Borba Manana
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're 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.
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.
Signed-off-by: Filipe David Borba Manana <fdmanana@gmail.com>
---
V2: Simplified the interface to user space. Got rid of the ioctls and
using now the regular path for setting and getting xattrs. Like
this properties can be set via regular getxattr and setxattr
system calls.
fs/btrfs/Makefile | 2 +-
fs/btrfs/btrfs_inode.h | 1 +
fs/btrfs/inode.c | 27 +++-
fs/btrfs/props.c | 350 ++++++++++++++++++++++++++++++++++++++++++++
fs/btrfs/props.h | 36 +++++
fs/btrfs/xattr.c | 12 +-
include/uapi/linux/xattr.h | 3 +
7 files changed, 425 insertions(+), 6 deletions(-)
create mode 100644 fs/btrfs/props.c
create mode 100644 fs/btrfs/props.h
diff --git a/fs/btrfs/Makefile b/fs/btrfs/Makefile
index 1a44e42..af7f000 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 \
export.o tree-log.o free-space-cache.o zlib.o lzo.o \
compression.o delayed-ref.o relocation.o delayed-inode.o scrub.o \
reada.o backref.o ulist.o qgroup.o send.o dev-replace.o raid56.o \
- uuid-tree.o
+ uuid-tree.o props.o
btrfs-$(CONFIG_BTRFS_FS_POSIX_ACL) += acl.o
btrfs-$(CONFIG_BTRFS_FS_CHECK_INTEGRITY) += check-integrity.o
diff --git a/fs/btrfs/btrfs_inode.h b/fs/btrfs/btrfs_inode.h
index ac0b39d..7dc2f78 100644
--- a/fs/btrfs/btrfs_inode.h
+++ b/fs/btrfs/btrfs_inode.h
@@ -43,6 +43,7 @@
#define BTRFS_INODE_COPY_EVERYTHING 8
#define BTRFS_INODE_IN_DELALLOC_LIST 9
#define BTRFS_INODE_READDIO_NEED_LOCK 10
+#define BTRFS_INODE_HAS_PROPS 11
/* in memory btrfs inode */
struct btrfs_inode {
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index f167ced..7a40aa6 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -58,6 +58,7 @@
#include "inode-map.h"
#include "backref.h"
#include "hash.h"
+#include "props.h"
struct btrfs_iget_args {
u64 ino;
@@ -3247,7 +3248,8 @@ out:
* slot is the slot the inode is in, objectid is the objectid of the inode
*/
static noinline int acls_after_inode_item(struct extent_buffer *leaf,
- int slot, u64 objectid)
+ int slot, u64 objectid,
+ int *has_xattrs)
{
u32 nritems = btrfs_header_nritems(leaf);
struct btrfs_key found_key;
@@ -3272,6 +3274,7 @@ static noinline int acls_after_inode_item(struct extent_buffer *leaf,
/* we found an xattr, assume we've got an acl */
if (found_key.type == BTRFS_XATTR_ITEM_KEY) {
+ *has_xattrs = 1;
if (found_key.offset == xattr_access ||
found_key.offset == xattr_default)
return 1;
@@ -3300,13 +3303,15 @@ static noinline int acls_after_inode_item(struct extent_buffer *leaf,
* something larger than an xattr. We have to assume the inode
* has acls
*/
+ *has_xattrs = 1;
+
return 1;
}
/*
* read an inode from the btree into the in-memory inode
*/
-static void btrfs_read_locked_inode(struct inode *inode)
+static void btrfs_read_locked_inode(struct inode *inode, int *has_xattrs)
{
struct btrfs_path *path;
struct extent_buffer *leaf;
@@ -3386,7 +3391,7 @@ cache_acl:
* any xattrs or acls
*/
maybe_acls = acls_after_inode_item(leaf, path->slots[0],
- btrfs_ino(inode));
+ btrfs_ino(inode), has_xattrs);
if (!maybe_acls)
cache_no_acl(inode);
@@ -4871,6 +4876,8 @@ struct inode *btrfs_iget(struct super_block *s, struct btrfs_key *location,
struct btrfs_root *root, int *new)
{
struct inode *inode;
+ int has_xattrs = 0;
+ int ret;
inode = btrfs_iget_locked(s, location->objectid, root);
if (!inode)
@@ -4879,10 +4886,17 @@ struct inode *btrfs_iget(struct super_block *s, struct btrfs_key *location,
if (inode->i_state & I_NEW) {
BTRFS_I(inode)->root = root;
memcpy(&BTRFS_I(inode)->location, location, sizeof(*location));
- btrfs_read_locked_inode(inode);
+ btrfs_read_locked_inode(inode, &has_xattrs);
if (!is_bad_inode(inode)) {
inode_tree_add(inode);
unlock_new_inode(inode);
+ if (has_xattrs) {
+ ret = btrfs_load_inode_props(inode);
+ if (ret)
+ pr_err("btrfs: error loading props for ino %llu (root %llu): %d\n",
+ btrfs_ino(inode),
+ root->root_key.objectid, ret);
+ }
if (new)
*new = 1;
} else {
@@ -5486,6 +5500,11 @@ static struct inode *btrfs_new_inode(struct btrfs_trans_handle *trans,
btrfs_update_root_times(trans, root);
+ ret = btrfs_inherit_props(trans, inode, dir);
+ if (ret)
+ pr_err("btrfs: error inheriting props for ino %llu (root %llu): %d",
+ btrfs_ino(inode), root->root_key.objectid, ret);
+
return inode;
fail:
if (dir)
diff --git a/fs/btrfs/props.c b/fs/btrfs/props.c
new file mode 100644
index 0000000..26e19bc
--- /dev/null
+++ b/fs/btrfs/props.c
@@ -0,0 +1,350 @@
+/*
+ * Copyright (C) 2013 Filipe David Borba Manana <fdmanana@gmail.com>
+ *
+ * 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 "props.h"
+#include "btrfs_inode.h"
+#include "transaction.h"
+#include "xattr.h"
+
+struct prop_handler {
+ const char *xattr_name;
+ int (*validate)(const char *value, size_t len);
+ int (*apply)(struct inode *inode, const char *value, size_t len);
+ int inheritable;
+};
+
+static int prop_compression_validate(const char *value, size_t len);
+
+static int prop_compression_apply(struct inode *inode,
+ const char *value,
+ size_t len);
+
+static const struct prop_handler prop_handlers[] = {
+ {
+ .xattr_name = XATTR_BTRFS_PREFIX "compression",
+ .validate = prop_compression_validate,
+ .apply = prop_compression_apply,
+ .inheritable = 1
+ },
+ {
+ .xattr_name = NULL,
+ .validate = NULL,
+ .apply = NULL
+ }
+};
+
+
+static const struct prop_handler *find_prop_handler(const char *name)
+{
+ const struct prop_handler *p;
+
+ for (p = &prop_handlers[0]; p->xattr_name; p++)
+ if (!strcmp(p->xattr_name, name))
+ return p;
+ return NULL;
+}
+
+static int __btrfs_set_prop(struct btrfs_trans_handle *trans,
+ struct inode *inode,
+ const char *name,
+ const char *value,
+ size_t value_len,
+ int flags)
+{
+ const struct prop_handler *handler;
+ int ret;
+
+ handler = find_prop_handler(name);
+ if (!handler)
+ return -EINVAL;
+
+ if (value_len == 0) {
+ ret = __btrfs_setxattr(trans, inode, handler->xattr_name,
+ NULL, 0, flags);
+ if (ret)
+ return ret;
+
+ ret = handler->apply(inode, NULL, 0);
+ ASSERT(ret == 0);
+
+ return ret;
+ }
+
+ ret = handler->validate(value, value_len);
+ if (ret)
+ return ret;
+
+ ret = __btrfs_setxattr(trans, inode, handler->xattr_name,
+ value, value_len, flags);
+ if (ret)
+ return ret;
+
+ ret = handler->apply(inode, value, value_len);
+ if (!ret)
+ set_bit(BTRFS_INODE_HAS_PROPS, &BTRFS_I(inode)->runtime_flags);
+
+ return ret;
+}
+
+int btrfs_set_prop(struct inode *inode,
+ const char *name,
+ const char *value,
+ size_t value_len,
+ int flags)
+{
+ return __btrfs_set_prop(NULL, inode, name, value, value_len, flags);
+}
+
+int btrfs_load_inode_props(struct inode *inode)
+{
+ struct btrfs_root *root = BTRFS_I(inode)->root;
+ struct btrfs_key key;
+ struct btrfs_path *path;
+ u64 ino = btrfs_ino(inode);
+ char *name_buf = NULL;
+ char *value_buf = NULL;
+ int name_buf_len = 0;
+ int value_buf_len = 0;
+ int ret;
+
+ path = btrfs_alloc_path();
+ if (!path)
+ return -ENOMEM;
+
+ key.objectid = ino;
+ key.type = BTRFS_XATTR_ITEM_KEY;
+ key.offset = 0;
+
+ ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
+ if (ret < 0)
+ goto out;
+
+ while (1) {
+ struct btrfs_dir_item *di;
+ struct extent_buffer *leaf;
+ u32 total_len, cur, this_len;
+ int slot;
+
+ slot = path->slots[0];
+ leaf = path->nodes[0];
+
+ if (slot >= btrfs_header_nritems(leaf)) {
+ ret = btrfs_next_leaf(root, path);
+ if (ret < 0)
+ goto out;
+ else if (ret > 0)
+ break;
+ continue;
+ }
+
+ btrfs_item_key_to_cpu(leaf, &key, slot);
+ if (key.objectid != ino || key.type != BTRFS_XATTR_ITEM_KEY)
+ break;
+
+ di = btrfs_item_ptr(leaf, slot, struct btrfs_dir_item);
+ cur = 0;
+ total_len = btrfs_item_size_nr(leaf, slot);
+
+ while (cur < total_len) {
+ u32 name_len = btrfs_dir_name_len(leaf, di);
+ u32 data_len = btrfs_dir_data_len(leaf, di);
+ unsigned long name_ptr, data_ptr;
+ const struct prop_handler *handler;
+
+ this_len = sizeof(*di) + name_len + data_len;
+ name_ptr = (unsigned long)(di + 1);
+ data_ptr = name_ptr + name_len;
+
+ if (name_len <= 6 ||
+ memcmp_extent_buffer(leaf, "btrfs.", name_ptr, 6))
+ goto next;
+
+ if (name_len >= name_buf_len) {
+ kfree(name_buf);
+ name_buf_len = name_len + 1;
+ name_buf = kmalloc(name_buf_len, GFP_NOFS);
+ if (!name_buf) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ }
+ read_extent_buffer(leaf, name_buf, name_ptr, name_len);
+ name_buf[name_len] = '\0';
+
+ if (data_len > value_buf_len) {
+ kfree(value_buf);
+ value_buf_len = data_len;
+ value_buf = kmalloc(data_len, GFP_NOFS);
+ if (!value_buf) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ }
+ read_extent_buffer(leaf, value_buf, data_ptr, data_len);
+
+ handler = find_prop_handler(name_buf);
+ if (!handler) {
+ pr_warn("btrfs: handler for prop %s, ino %llu (root %llu), not found",
+ name_buf, ino, root->root_key.objectid);
+ goto next;
+ }
+
+ ret = handler->apply(inode, value_buf, data_len);
+ if (ret)
+ pr_warn("btrfs: error applying prop %s to ino %llu (root %llu): %d",
+ name_buf, ino,
+ root->root_key.objectid, ret);
+ else
+ set_bit(BTRFS_INODE_HAS_PROPS,
+ &BTRFS_I(inode)->runtime_flags);
+next:
+ cur += this_len;
+ di = (struct btrfs_dir_item *)((char *) di + this_len);
+ }
+
+ path->slots[0]++;
+ }
+
+ ret = 0;
+out:
+ btrfs_free_path(path);
+ kfree(name_buf);
+ kfree(value_buf);
+
+ return ret;
+}
+
+int btrfs_inherit_props(struct btrfs_trans_handle *trans,
+ struct inode *inode,
+ struct inode *dir)
+{
+ const struct prop_handler *h;
+ struct btrfs_root *root = BTRFS_I(inode)->root;
+ char *buf = NULL;
+ int buf_len = 0;
+ struct btrfs_block_rsv *rsv = NULL;
+ struct btrfs_block_rsv *trans_rsv = trans->block_rsv;
+ int ret;
+
+ if (!dir)
+ return 0;
+
+ if (!test_bit(BTRFS_INODE_HAS_PROPS,
+ &BTRFS_I(dir)->runtime_flags))
+ return 0;
+
+ for (h = &prop_handlers[0]; h->xattr_name; h++) {
+ size_t len = 0;
+ u64 num_bytes;
+
+ if (!h->inheritable)
+ continue;
+again:
+ ret = __btrfs_getxattr(dir, h->xattr_name, NULL, 0);
+ if (ret == -ENODATA)
+ continue;
+ else if (ret < 0)
+ goto out;
+ else
+ len = ret;
+
+ if (len > buf_len) {
+ kfree(buf);
+ buf = kmalloc(len, GFP_NOFS);
+ if (!buf) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ buf_len = len;
+ }
+
+ ret = __btrfs_getxattr(dir, h->xattr_name, buf, len);
+ if (ret == -ERANGE)
+ goto again;
+ else if (ret == -ENODATA)
+ continue;
+ else if (ret < 0)
+ goto out;
+
+ if (!rsv) {
+ rsv = btrfs_alloc_block_rsv(root, BTRFS_BLOCK_RSV_TEMP);
+ if (!rsv) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ trans->block_rsv = rsv;
+ }
+
+ num_bytes = btrfs_calc_trans_metadata_size(root, 2);
+ ret = btrfs_block_rsv_add(root, trans->block_rsv,
+ num_bytes, BTRFS_RESERVE_NO_FLUSH);
+ if (ret)
+ goto out;
+ ret = __btrfs_set_prop(trans, inode, h->xattr_name,
+ buf, len, 0);
+ if (ret)
+ goto out;
+ }
+ ret = 0;
+out:
+ kfree(buf);
+ if (rsv) {
+ btrfs_free_block_rsv(root, rsv);
+ trans->block_rsv = trans_rsv;
+ }
+
+ return ret;
+}
+
+static int prop_compression_validate(const char *value, size_t len)
+{
+ if (!strncmp("lzo", value, len))
+ return 0;
+ else if (!strncmp("zlib", value, len))
+ return 0;
+
+ return -EINVAL;
+}
+
+static int prop_compression_apply(struct inode *inode,
+ const char *value,
+ size_t len)
+{
+ int type;
+
+ if (len == 0) {
+ BTRFS_I(inode)->flags |= BTRFS_INODE_NOCOMPRESS;
+ BTRFS_I(inode)->flags &= ~BTRFS_INODE_COMPRESS;
+ BTRFS_I(inode)->force_compress = BTRFS_COMPRESS_NONE;
+
+ return 0;
+ }
+
+ if (!strncmp("lzo", value, len))
+ type = BTRFS_COMPRESS_LZO;
+ else if (!strncmp("zlib", value, len))
+ type = BTRFS_COMPRESS_ZLIB;
+ else
+ return -EINVAL;
+
+ BTRFS_I(inode)->flags &= ~BTRFS_INODE_NOCOMPRESS;
+ BTRFS_I(inode)->flags |= BTRFS_INODE_COMPRESS;
+ BTRFS_I(inode)->force_compress = type;
+
+ return 0;
+}
diff --git a/fs/btrfs/props.h b/fs/btrfs/props.h
new file mode 100644
index 0000000..a352189
--- /dev/null
+++ b/fs/btrfs/props.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2013 Filipe David Borba Manana <fdmanana@gmail.com>
+ *
+ * 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 __BTRFS_PROPS_H
+#define __BTRFS_PROPS_H
+
+#include "ctree.h"
+
+int btrfs_set_prop(struct inode *inode,
+ const char *name,
+ const char *value,
+ size_t value_len,
+ int flags);
+
+int btrfs_load_inode_props(struct inode *inode);
+
+int btrfs_inherit_props(struct btrfs_trans_handle *trans,
+ struct inode *inode,
+ struct inode *dir);
+
+#endif
diff --git a/fs/btrfs/xattr.c b/fs/btrfs/xattr.c
index 05740b9..4b33765 100644
--- a/fs/btrfs/xattr.c
+++ b/fs/btrfs/xattr.c
@@ -27,6 +27,7 @@
#include "transaction.h"
#include "xattr.h"
#include "disk-io.h"
+#include "props.h"
ssize_t __btrfs_getxattr(struct inode *inode, const char *name,
@@ -331,7 +332,8 @@ static bool btrfs_is_valid_xattr(const char *name)
XATTR_SECURITY_PREFIX_LEN) ||
!strncmp(name, XATTR_SYSTEM_PREFIX, XATTR_SYSTEM_PREFIX_LEN) ||
!strncmp(name, XATTR_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN) ||
- !strncmp(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN);
+ !strncmp(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN) ||
+ !strncmp(name, XATTR_BTRFS_PREFIX, XATTR_BTRFS_PREFIX_LEN);
}
ssize_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,
if (!btrfs_is_valid_xattr(name))
return -EOPNOTSUPP;
+ if (!strncmp(name, XATTR_BTRFS_PREFIX, XATTR_BTRFS_PREFIX_LEN))
+ return btrfs_set_prop(dentry->d_inode, name,
+ value, size, flags);
+
if (size == 0)
value = ""; /* empty EA, do not remove */
@@ -402,6 +408,10 @@ int btrfs_removexattr(struct dentry *dentry, const char *name)
if (!btrfs_is_valid_xattr(name))
return -EOPNOTSUPP;
+ if (!strncmp(name, XATTR_BTRFS_PREFIX, XATTR_BTRFS_PREFIX_LEN))
+ return btrfs_set_prop(dentry->d_inode, name,
+ NULL, 0, XATTR_REPLACE);
+
return __btrfs_setxattr(NULL, dentry->d_inode, name, NULL, 0,
XATTR_REPLACE);
}
diff --git a/include/uapi/linux/xattr.h b/include/uapi/linux/xattr.h
index e4629b9..40bbc04 100644
--- a/include/uapi/linux/xattr.h
+++ b/include/uapi/linux/xattr.h
@@ -20,6 +20,9 @@
#define XATTR_MAC_OSX_PREFIX "osx."
#define XATTR_MAC_OSX_PREFIX_LEN (sizeof(XATTR_MAC_OSX_PREFIX) - 1)
+#define XATTR_BTRFS_PREFIX "btrfs."
+#define XATTR_BTRFS_PREFIX_LEN (sizeof(XATTR_BTRFS_PREFIX) - 1)
+
#define XATTR_SECURITY_PREFIX "security."
#define XATTR_SECURITY_PREFIX_LEN (sizeof(XATTR_SECURITY_PREFIX) - 1)
--
1.7.9.5
^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [PATCH V2] Btrfs: add support for inode properties
2013-11-13 1:21 ` [PATCH V2] " Filipe David Borba Manana
@ 2013-11-13 18:59 ` Goffredo Baroncelli
2013-11-19 16:06 ` Filipe David Manana
0 siblings, 1 reply; 11+ messages in thread
From: Goffredo Baroncelli @ 2013-11-13 18:59 UTC (permalink / raw)
To: Filipe David Borba Manana; +Cc: linux-btrfs
Hi Filipe,
my comments below
On 2013-11-13 02:21, Filipe David Borba Manana wrote:
> 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're 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.
>
> 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.
>
> Signed-off-by: Filipe David Borba Manana <fdmanana@gmail.com>
> ---
>
> V2: Simplified the interface to user space. Got rid of the ioctls and
> using now the regular path for setting and getting xattrs. Like
> this properties can be set via regular getxattr and setxattr
> system calls.
>
> fs/btrfs/Makefile | 2 +-
> fs/btrfs/btrfs_inode.h | 1 +
> fs/btrfs/inode.c | 27 +++-
> fs/btrfs/props.c | 350 ++++++++++++++++++++++++++++++++++++++++++++
> fs/btrfs/props.h | 36 +++++
> fs/btrfs/xattr.c | 12 +-
> include/uapi/linux/xattr.h | 3 +
> 7 files changed, 425 insertions(+), 6 deletions(-)
> create mode 100644 fs/btrfs/props.c
> create mode 100644 fs/btrfs/props.h
>
[...]
> +static int __btrfs_set_prop(struct btrfs_trans_handle *trans,
> + struct inode *inode,
> + const char *name,
> + const char *value,
> + size_t value_len,
> + int flags)
> +{
> + const struct prop_handler *handler;
> + int ret;
> +
> + handler = find_prop_handler(name);
> + if (!handler)
> + return -EINVAL;
> +
> + if (value_len == 0) {
> + ret = __btrfs_setxattr(trans, inode, handler->xattr_name,
> + NULL, 0, flags);
> + if (ret)
> + return ret;
> +
> + ret = handler->apply(inode, NULL, 0);
> + ASSERT(ret == 0);
> +
> + return ret;
> + }
> +
> + ret = handler->validate(value, value_len);
> + if (ret)
> + return ret;
> +
> + ret = __btrfs_setxattr(trans, inode, handler->xattr_name,
> + value, value_len, flags);
> + if (ret)
> + return ret;
> +
> + ret = handler->apply(inode, value, value_len);
> + if (!ret)
> + set_bit(BTRFS_INODE_HAS_PROPS, &BTRFS_I(inode)->runtime_flags);
Does the line above also apply even if value_len== 0 ?
Moreover I have a doubt about the opportunity to store the information
like the compression both in inode and in the xattr: what happens if the
user does an "chattr -c" ? The xattr will be not updated.
I suggest to update/create a real xattr only if there is no space for
the new information in the inode. In the other case the xattr should be
a fake xattr to get/set the property stored in the inode (or whichever
is involved).
Of course also the function btrfs_listxattr() should be updated to show
the "fake" xattr which doesn't have a real xattr stored on the disk. The
same is true for the getxattr() function...
> +
> + return ret;
> +}
> +
[...]
> +int btrfs_load_inode_props(struct inode *inode)
> +{
> + struct btrfs_root *root = BTRFS_I(inode)->root;
> + struct btrfs_key key;
> + struct btrfs_path *path;
> + u64 ino = btrfs_ino(inode);
> + char *name_buf = NULL;
> + char *value_buf = NULL;
> + int name_buf_len = 0;
> + int value_buf_len = 0;
> + int ret;
> +
> + path = btrfs_alloc_path();
> + if (!path)
> + return -ENOMEM;
> +
> + key.objectid = ino;
> + key.type = BTRFS_XATTR_ITEM_KEY;
> + key.offset = 0;
> +
> + ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
> + if (ret < 0)
> + goto out;
> +
> + while (1) {
> + struct btrfs_dir_item *di;
> + struct extent_buffer *leaf;
> + u32 total_len, cur, this_len;
> + int slot;
> +
> + slot = path->slots[0];
> + leaf = path->nodes[0];
> +
> + if (slot >= btrfs_header_nritems(leaf)) {
> + ret = btrfs_next_leaf(root, path);
> + if (ret < 0)
> + goto out;
> + else if (ret > 0)
> + break;
> + continue;
> + }
> +
> + btrfs_item_key_to_cpu(leaf, &key, slot);
> + if (key.objectid != ino || key.type != BTRFS_XATTR_ITEM_KEY)
> + break;
> +
> + di = btrfs_item_ptr(leaf, slot, struct btrfs_dir_item);
> + cur = 0;
> + total_len = btrfs_item_size_nr(leaf, slot);
> +
> + while (cur < total_len) {
> + u32 name_len = btrfs_dir_name_len(leaf, di);
> + u32 data_len = btrfs_dir_data_len(leaf, di);
> + unsigned long name_ptr, data_ptr;
> + const struct prop_handler *handler;
> +
> + this_len = sizeof(*di) + name_len + data_len;
> + name_ptr = (unsigned long)(di + 1);
> + data_ptr = name_ptr + name_len;
> +
> + if (name_len <= 6 ||
> + memcmp_extent_buffer(leaf, "btrfs.", name_ptr, 6))
You should replace '"btrfs."' with XATTR_BTRFS_PREFIX and '6' with
XATTR_BTRFS_PREFIX_LEN.
> + goto next;
> +
> + if (name_len >= name_buf_len) {
> + kfree(name_buf);
> + name_buf_len = name_len + 1;
> + name_buf = kmalloc(name_buf_len, GFP_NOFS);
> + if (!name_buf) {
> + ret = -ENOMEM;
> + goto out;
> + }
> + }
> + read_extent_buffer(leaf, name_buf, name_ptr, name_len);
> + name_buf[name_len] = '\0';
> +
> + if (data_len > value_buf_len) {
> + kfree(value_buf);
> + value_buf_len = data_len;
> + value_buf = kmalloc(data_len, GFP_NOFS);
> + if (!value_buf) {
> + ret = -ENOMEM;
> + goto out;
> + }
> + }
> + read_extent_buffer(leaf, value_buf, data_ptr, data_len);
> +
> + handler = find_prop_handler(name_buf);
> + if (!handler) {
> + pr_warn("btrfs: handler for prop %s, ino %llu (root %llu), not found",
> + name_buf, ino, root->root_key.objectid);
> + goto next;
> + }
> +
> + ret = handler->apply(inode, value_buf, data_len);
> + if (ret)
> + pr_warn("btrfs: error applying prop %s to ino %llu (root %llu): %d",
> + name_buf, ino,
> + root->root_key.objectid, ret);
> + else
> + set_bit(BTRFS_INODE_HAS_PROPS,
> + &BTRFS_I(inode)->runtime_flags);
> +next:
> + cur += this_len;
> + di = (struct btrfs_dir_item *)((char *) di + this_len);
> + }
> +
> + path->slots[0]++;
> + }
> +
> + ret = 0;
> +out:
> + btrfs_free_path(path);
> + kfree(name_buf);
> + kfree(value_buf);
> +
> + return ret;
> +}
[...]
> +static int prop_compression_apply(struct inode *inode,
> + const char *value,
> + size_t len)
> +{
> + int type;
> +
> + if (len == 0) {
> + BTRFS_I(inode)->flags |= BTRFS_INODE_NOCOMPRESS;
> + BTRFS_I(inode)->flags &= ~BTRFS_INODE_COMPRESS;
> + BTRFS_I(inode)->force_compress = BTRFS_COMPRESS_NONE;
> +
> + return 0;
> + }
> +
> + if (!strncmp("lzo", value, len))
> + type = BTRFS_COMPRESS_LZO;
> + else if (!strncmp("zlib", value, len))
> + type = BTRFS_COMPRESS_ZLIB;
> + else
> + return -EINVAL;
> +
> + BTRFS_I(inode)->flags &= ~BTRFS_INODE_NOCOMPRESS;
> + BTRFS_I(inode)->flags |= BTRFS_INODE_COMPRESS;
> + BTRFS_I(inode)->force_compress = type;
> +
> + return 0;
Other than setting the flag, should the inode be marked "dirty" in order
to flush the new information on the disk ? I noticed that in the
function btrfs_ioctl_setflags(), after updating the flags there are the
following calls:
btrfs_update_iflags(inode);
inode_inc_iversion(inode);
inode->i_ctime = CURRENT_TIME;
ret = btrfs_update_inode(trans, root, inode);
btrfs_end_transaction(trans, root);
> +}
[...]
GB
--
gpg @keyserver.linux.it: Goffredo Baroncelli (kreijackATinwind.it>
Key fingerprint BBF5 1610 0B64 DAC6 5F7D 17B2 0EDA 9B37 8B82 E0B5
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH v3] Btrfs: add support for inode properties
2013-11-12 13:42 [RFC PATCH] Btrfs: add support for inode properties Filipe David Borba Manana
2013-11-12 19:24 ` Goffredo Baroncelli
2013-11-13 1:21 ` [PATCH V2] " Filipe David Borba Manana
@ 2013-11-19 16:00 ` Filipe David Borba Manana
2014-01-07 11:47 ` [RFC PATCH v4] " Filipe David Borba Manana
3 siblings, 0 replies; 11+ messages in thread
From: Filipe David Borba Manana @ 2013-11-19 16:00 UTC (permalink / raw)
To: linux-btrfs; +Cc: Filipe David Borba Manana
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're 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.
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.
Signed-off-by: Filipe David Borba Manana <fdmanana@gmail.com>
---
V2: Simplified the interface to user space. Got rid of the ioctls and
using now the regular path for setting and getting xattrs. Like
this properties can be set via regular getxattr and setxattr
system calls.
V3: Some simplifications, made the compression property play well
with chattr +c/-c and replaced some leftover hardcoded values
with the corresponding constants (xattr prefix string and its
length).
fs/btrfs/Makefile | 2 +-
fs/btrfs/btrfs_inode.h | 1 +
fs/btrfs/inode.c | 27 +++-
fs/btrfs/ioctl.c | 17 +++
fs/btrfs/props.c | 339 ++++++++++++++++++++++++++++++++++++++++++++
fs/btrfs/props.h | 36 +++++
fs/btrfs/xattr.c | 12 +-
include/uapi/linux/xattr.h | 3 +
8 files changed, 431 insertions(+), 6 deletions(-)
create mode 100644 fs/btrfs/props.c
create mode 100644 fs/btrfs/props.h
diff --git a/fs/btrfs/Makefile b/fs/btrfs/Makefile
index 1a44e42..af7f000 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 \
export.o tree-log.o free-space-cache.o zlib.o lzo.o \
compression.o delayed-ref.o relocation.o delayed-inode.o scrub.o \
reada.o backref.o ulist.o qgroup.o send.o dev-replace.o raid56.o \
- uuid-tree.o
+ uuid-tree.o props.o
btrfs-$(CONFIG_BTRFS_FS_POSIX_ACL) += acl.o
btrfs-$(CONFIG_BTRFS_FS_CHECK_INTEGRITY) += check-integrity.o
diff --git a/fs/btrfs/btrfs_inode.h b/fs/btrfs/btrfs_inode.h
index ac0b39d..7dc2f78 100644
--- a/fs/btrfs/btrfs_inode.h
+++ b/fs/btrfs/btrfs_inode.h
@@ -43,6 +43,7 @@
#define BTRFS_INODE_COPY_EVERYTHING 8
#define BTRFS_INODE_IN_DELALLOC_LIST 9
#define BTRFS_INODE_READDIO_NEED_LOCK 10
+#define BTRFS_INODE_HAS_PROPS 11
/* in memory btrfs inode */
struct btrfs_inode {
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index 5a5de36..fcade04 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -58,6 +58,7 @@
#include "inode-map.h"
#include "backref.h"
#include "hash.h"
+#include "props.h"
struct btrfs_iget_args {
u64 ino;
@@ -3248,7 +3249,8 @@ out:
* slot is the slot the inode is in, objectid is the objectid of the inode
*/
static noinline int acls_after_inode_item(struct extent_buffer *leaf,
- int slot, u64 objectid)
+ int slot, u64 objectid,
+ int *has_xattrs)
{
u32 nritems = btrfs_header_nritems(leaf);
struct btrfs_key found_key;
@@ -3273,6 +3275,7 @@ static noinline int acls_after_inode_item(struct extent_buffer *leaf,
/* we found an xattr, assume we've got an acl */
if (found_key.type == BTRFS_XATTR_ITEM_KEY) {
+ *has_xattrs = 1;
if (found_key.offset == xattr_access ||
found_key.offset == xattr_default)
return 1;
@@ -3301,13 +3304,15 @@ static noinline int acls_after_inode_item(struct extent_buffer *leaf,
* something larger than an xattr. We have to assume the inode
* has acls
*/
+ *has_xattrs = 1;
+
return 1;
}
/*
* read an inode from the btree into the in-memory inode
*/
-static void btrfs_read_locked_inode(struct inode *inode)
+static void btrfs_read_locked_inode(struct inode *inode, int *has_xattrs)
{
struct btrfs_path *path;
struct extent_buffer *leaf;
@@ -3387,7 +3392,7 @@ cache_acl:
* any xattrs or acls
*/
maybe_acls = acls_after_inode_item(leaf, path->slots[0],
- btrfs_ino(inode));
+ btrfs_ino(inode), has_xattrs);
if (!maybe_acls)
cache_no_acl(inode);
@@ -4872,6 +4877,8 @@ struct inode *btrfs_iget(struct super_block *s, struct btrfs_key *location,
struct btrfs_root *root, int *new)
{
struct inode *inode;
+ int has_xattrs = 0;
+ int ret;
inode = btrfs_iget_locked(s, location->objectid, root);
if (!inode)
@@ -4880,10 +4887,17 @@ struct inode *btrfs_iget(struct super_block *s, struct btrfs_key *location,
if (inode->i_state & I_NEW) {
BTRFS_I(inode)->root = root;
memcpy(&BTRFS_I(inode)->location, location, sizeof(*location));
- btrfs_read_locked_inode(inode);
+ btrfs_read_locked_inode(inode, &has_xattrs);
if (!is_bad_inode(inode)) {
inode_tree_add(inode);
unlock_new_inode(inode);
+ if (has_xattrs) {
+ ret = btrfs_load_inode_props(inode);
+ if (ret)
+ pr_err("btrfs: error loading props for ino %llu (root %llu): %d\n",
+ btrfs_ino(inode),
+ root->root_key.objectid, ret);
+ }
if (new)
*new = 1;
} else {
@@ -5481,6 +5495,11 @@ static struct inode *btrfs_new_inode(struct btrfs_trans_handle *trans,
btrfs_update_root_times(trans, root);
+ ret = btrfs_inherit_props(trans, inode, dir);
+ if (ret)
+ pr_err("btrfs: error inheriting props for ino %llu (root %llu): %d",
+ btrfs_ino(inode), root->root_key.objectid, ret);
+
return inode;
fail:
if (dir)
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
index d237af8..d164b49 100644
--- a/fs/btrfs/ioctl.c
+++ b/fs/btrfs/ioctl.c
@@ -56,6 +56,7 @@
#include "rcu-string.h"
#include "send.h"
#include "dev-replace.h"
+#include "props.h"
#include "sysfs.h"
static int btrfs_clone(struct inode *src, struct inode *inode,
@@ -281,9 +282,25 @@ static int btrfs_ioctl_setflags(struct file *file, void __user *arg)
if (flags & FS_NOCOMP_FL) {
ip->flags &= ~BTRFS_INODE_COMPRESS;
ip->flags |= BTRFS_INODE_NOCOMPRESS;
+
+ ret = btrfs_set_prop(inode, "btrfs.compression", NULL, 0, 0);
+ if (ret && ret != -ENODATA)
+ goto out_drop;
} else if (flags & FS_COMPR_FL) {
+ const char *comp;
+
ip->flags |= BTRFS_INODE_COMPRESS;
ip->flags &= ~BTRFS_INODE_NOCOMPRESS;
+
+ if (root->fs_info->compress_type == BTRFS_COMPRESS_LZO)
+ comp = "lzo";
+ else
+ comp = "zlib";
+ ret = btrfs_set_prop(inode, "btrfs.compression",
+ comp, strlen(comp), 0);
+ if (ret)
+ goto out_drop;
+
} else {
ip->flags &= ~(BTRFS_INODE_COMPRESS | BTRFS_INODE_NOCOMPRESS);
}
diff --git a/fs/btrfs/props.c b/fs/btrfs/props.c
new file mode 100644
index 0000000..8709058
--- /dev/null
+++ b/fs/btrfs/props.c
@@ -0,0 +1,339 @@
+/*
+ * Copyright (C) 2013 Filipe David Borba Manana <fdmanana@gmail.com>
+ *
+ * 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 "props.h"
+#include "btrfs_inode.h"
+#include "transaction.h"
+#include "xattr.h"
+
+struct prop_handler {
+ const char *xattr_name;
+ int (*apply)(struct inode *inode, const char *value, size_t len);
+ int inheritable;
+};
+
+static int prop_compression_apply(struct inode *inode,
+ const char *value,
+ size_t len);
+
+static const struct prop_handler prop_handlers[] = {
+ {
+ .xattr_name = XATTR_BTRFS_PREFIX "compression",
+ .apply = prop_compression_apply,
+ .inheritable = 1
+ },
+ {
+ .xattr_name = NULL,
+ .apply = NULL
+ }
+};
+
+
+static const struct prop_handler *find_prop_handler(const char *name)
+{
+ const struct prop_handler *p;
+
+ for (p = &prop_handlers[0]; p->xattr_name; p++)
+ if (!strcmp(p->xattr_name, name))
+ return p;
+ return NULL;
+}
+
+static int __btrfs_set_prop(struct btrfs_trans_handle *trans,
+ struct inode *inode,
+ const char *name,
+ const char *value,
+ size_t value_len,
+ int flags)
+{
+ const struct prop_handler *handler;
+ int ret;
+
+ if (strlen(name) <= XATTR_BTRFS_PREFIX_LEN)
+ return -EINVAL;
+
+ handler = find_prop_handler(name);
+ if (!handler)
+ return -EINVAL;
+
+ if (value_len == 0) {
+ ret = __btrfs_setxattr(trans, inode, handler->xattr_name,
+ NULL, 0, flags);
+ if (ret)
+ return ret;
+
+ ret = handler->apply(inode, NULL, 0);
+ ASSERT(ret == 0);
+
+ return ret;
+ }
+
+ ret = handler->apply(inode, value, value_len);
+ if (ret)
+ return ret;
+ ret = __btrfs_setxattr(trans, inode, handler->xattr_name,
+ value, value_len, flags);
+ if (ret) {
+ handler->apply(inode, NULL, 0);
+ return ret;
+ }
+
+ set_bit(BTRFS_INODE_HAS_PROPS, &BTRFS_I(inode)->runtime_flags);
+
+ return 0;
+}
+
+int btrfs_set_prop(struct inode *inode,
+ const char *name,
+ const char *value,
+ size_t value_len,
+ int flags)
+{
+ return __btrfs_set_prop(NULL, inode, name, value, value_len, flags);
+}
+
+int btrfs_load_inode_props(struct inode *inode)
+{
+ struct btrfs_root *root = BTRFS_I(inode)->root;
+ struct btrfs_key key;
+ struct btrfs_path *path;
+ u64 ino = btrfs_ino(inode);
+ char *name_buf = NULL;
+ char *value_buf = NULL;
+ int name_buf_len = 0;
+ int value_buf_len = 0;
+ int ret;
+
+ path = btrfs_alloc_path();
+ if (!path)
+ return -ENOMEM;
+
+ key.objectid = ino;
+ key.type = BTRFS_XATTR_ITEM_KEY;
+ key.offset = 0;
+
+ ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
+ if (ret < 0)
+ goto out;
+
+ while (1) {
+ struct btrfs_dir_item *di;
+ struct extent_buffer *leaf;
+ u32 total_len, cur, this_len;
+ int slot;
+
+ slot = path->slots[0];
+ leaf = path->nodes[0];
+
+ if (slot >= btrfs_header_nritems(leaf)) {
+ ret = btrfs_next_leaf(root, path);
+ if (ret < 0)
+ goto out;
+ else if (ret > 0)
+ break;
+ continue;
+ }
+
+ btrfs_item_key_to_cpu(leaf, &key, slot);
+ if (key.objectid != ino || key.type != BTRFS_XATTR_ITEM_KEY)
+ break;
+
+ di = btrfs_item_ptr(leaf, slot, struct btrfs_dir_item);
+ cur = 0;
+ total_len = btrfs_item_size_nr(leaf, slot);
+
+ while (cur < total_len) {
+ u32 name_len = btrfs_dir_name_len(leaf, di);
+ u32 data_len = btrfs_dir_data_len(leaf, di);
+ unsigned long name_ptr, data_ptr;
+ const struct prop_handler *handler;
+
+ this_len = sizeof(*di) + name_len + data_len;
+ name_ptr = (unsigned long)(di + 1);
+ data_ptr = name_ptr + name_len;
+
+ if (name_len <= XATTR_BTRFS_PREFIX_LEN ||
+ memcmp_extent_buffer(leaf, XATTR_BTRFS_PREFIX,
+ name_ptr,
+ XATTR_BTRFS_PREFIX_LEN))
+ goto next;
+
+ if (name_len >= name_buf_len) {
+ kfree(name_buf);
+ name_buf_len = name_len + 1;
+ name_buf = kmalloc(name_buf_len, GFP_NOFS);
+ if (!name_buf) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ }
+ read_extent_buffer(leaf, name_buf, name_ptr, name_len);
+ name_buf[name_len] = '\0';
+
+ if (data_len > value_buf_len) {
+ kfree(value_buf);
+ value_buf_len = data_len;
+ value_buf = kmalloc(data_len, GFP_NOFS);
+ if (!value_buf) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ }
+ read_extent_buffer(leaf, value_buf, data_ptr, data_len);
+
+ handler = find_prop_handler(name_buf);
+ if (!handler) {
+ pr_warn("btrfs: handler for prop %s, ino %llu (root %llu), not found",
+ name_buf, ino, root->root_key.objectid);
+ goto next;
+ }
+
+ ret = handler->apply(inode, value_buf, data_len);
+ if (ret)
+ pr_warn("btrfs: error applying prop %s to ino %llu (root %llu): %d",
+ name_buf, ino,
+ root->root_key.objectid, ret);
+ else
+ set_bit(BTRFS_INODE_HAS_PROPS,
+ &BTRFS_I(inode)->runtime_flags);
+next:
+ cur += this_len;
+ di = (struct btrfs_dir_item *)((char *) di + this_len);
+ }
+
+ path->slots[0]++;
+ }
+
+ ret = 0;
+out:
+ btrfs_free_path(path);
+ kfree(name_buf);
+ kfree(value_buf);
+
+ return ret;
+}
+
+int btrfs_inherit_props(struct btrfs_trans_handle *trans,
+ struct inode *inode,
+ struct inode *dir)
+{
+ const struct prop_handler *h;
+ struct btrfs_root *root = BTRFS_I(inode)->root;
+ char *buf = NULL;
+ int buf_len = 0;
+ struct btrfs_block_rsv *rsv = NULL;
+ struct btrfs_block_rsv *trans_rsv = trans->block_rsv;
+ int ret;
+
+ if (!dir)
+ return 0;
+
+ if (!test_bit(BTRFS_INODE_HAS_PROPS,
+ &BTRFS_I(dir)->runtime_flags))
+ return 0;
+
+ for (h = &prop_handlers[0]; h->xattr_name; h++) {
+ size_t len = 0;
+ u64 num_bytes;
+
+ if (!h->inheritable)
+ continue;
+again:
+ ret = __btrfs_getxattr(dir, h->xattr_name, NULL, 0);
+ if (ret == -ENODATA)
+ continue;
+ else if (ret < 0)
+ goto out;
+ else
+ len = ret;
+
+ if (len > buf_len) {
+ kfree(buf);
+ buf = kmalloc(len, GFP_NOFS);
+ if (!buf) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ buf_len = len;
+ }
+
+ ret = __btrfs_getxattr(dir, h->xattr_name, buf, len);
+ if (ret == -ERANGE)
+ goto again;
+ else if (ret == -ENODATA)
+ continue;
+ else if (ret < 0)
+ goto out;
+
+ if (!rsv) {
+ rsv = btrfs_alloc_block_rsv(root, BTRFS_BLOCK_RSV_TEMP);
+ if (!rsv) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ trans->block_rsv = rsv;
+ }
+
+ num_bytes = btrfs_calc_trans_metadata_size(root, 2);
+ ret = btrfs_block_rsv_add(root, trans->block_rsv,
+ num_bytes, BTRFS_RESERVE_NO_FLUSH);
+ if (ret)
+ goto out;
+ ret = __btrfs_set_prop(trans, inode, h->xattr_name,
+ buf, len, 0);
+ if (ret)
+ goto out;
+ }
+ ret = 0;
+out:
+ kfree(buf);
+ if (rsv) {
+ btrfs_free_block_rsv(root, rsv);
+ trans->block_rsv = trans_rsv;
+ }
+
+ return ret;
+}
+
+static int prop_compression_apply(struct inode *inode,
+ const char *value,
+ size_t len)
+{
+ int type;
+
+ if (len == 0) {
+ BTRFS_I(inode)->flags |= BTRFS_INODE_NOCOMPRESS;
+ BTRFS_I(inode)->flags &= ~BTRFS_INODE_COMPRESS;
+ BTRFS_I(inode)->force_compress = BTRFS_COMPRESS_NONE;
+
+ return 0;
+ }
+
+ if (!strncmp("lzo", value, len))
+ type = BTRFS_COMPRESS_LZO;
+ else if (!strncmp("zlib", value, len))
+ type = BTRFS_COMPRESS_ZLIB;
+ else
+ return -EINVAL;
+
+ BTRFS_I(inode)->flags &= ~BTRFS_INODE_NOCOMPRESS;
+ BTRFS_I(inode)->flags |= BTRFS_INODE_COMPRESS;
+ BTRFS_I(inode)->force_compress = type;
+
+ return 0;
+}
diff --git a/fs/btrfs/props.h b/fs/btrfs/props.h
new file mode 100644
index 0000000..a352189
--- /dev/null
+++ b/fs/btrfs/props.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2013 Filipe David Borba Manana <fdmanana@gmail.com>
+ *
+ * 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 __BTRFS_PROPS_H
+#define __BTRFS_PROPS_H
+
+#include "ctree.h"
+
+int btrfs_set_prop(struct inode *inode,
+ const char *name,
+ const char *value,
+ size_t value_len,
+ int flags);
+
+int btrfs_load_inode_props(struct inode *inode);
+
+int btrfs_inherit_props(struct btrfs_trans_handle *trans,
+ struct inode *inode,
+ struct inode *dir);
+
+#endif
diff --git a/fs/btrfs/xattr.c b/fs/btrfs/xattr.c
index 05740b9..4b33765 100644
--- a/fs/btrfs/xattr.c
+++ b/fs/btrfs/xattr.c
@@ -27,6 +27,7 @@
#include "transaction.h"
#include "xattr.h"
#include "disk-io.h"
+#include "props.h"
ssize_t __btrfs_getxattr(struct inode *inode, const char *name,
@@ -331,7 +332,8 @@ static bool btrfs_is_valid_xattr(const char *name)
XATTR_SECURITY_PREFIX_LEN) ||
!strncmp(name, XATTR_SYSTEM_PREFIX, XATTR_SYSTEM_PREFIX_LEN) ||
!strncmp(name, XATTR_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN) ||
- !strncmp(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN);
+ !strncmp(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN) ||
+ !strncmp(name, XATTR_BTRFS_PREFIX, XATTR_BTRFS_PREFIX_LEN);
}
ssize_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,
if (!btrfs_is_valid_xattr(name))
return -EOPNOTSUPP;
+ if (!strncmp(name, XATTR_BTRFS_PREFIX, XATTR_BTRFS_PREFIX_LEN))
+ return btrfs_set_prop(dentry->d_inode, name,
+ value, size, flags);
+
if (size == 0)
value = ""; /* empty EA, do not remove */
@@ -402,6 +408,10 @@ int btrfs_removexattr(struct dentry *dentry, const char *name)
if (!btrfs_is_valid_xattr(name))
return -EOPNOTSUPP;
+ if (!strncmp(name, XATTR_BTRFS_PREFIX, XATTR_BTRFS_PREFIX_LEN))
+ return btrfs_set_prop(dentry->d_inode, name,
+ NULL, 0, XATTR_REPLACE);
+
return __btrfs_setxattr(NULL, dentry->d_inode, name, NULL, 0,
XATTR_REPLACE);
}
diff --git a/include/uapi/linux/xattr.h b/include/uapi/linux/xattr.h
index e4629b9..40bbc04 100644
--- a/include/uapi/linux/xattr.h
+++ b/include/uapi/linux/xattr.h
@@ -20,6 +20,9 @@
#define XATTR_MAC_OSX_PREFIX "osx."
#define XATTR_MAC_OSX_PREFIX_LEN (sizeof(XATTR_MAC_OSX_PREFIX) - 1)
+#define XATTR_BTRFS_PREFIX "btrfs."
+#define XATTR_BTRFS_PREFIX_LEN (sizeof(XATTR_BTRFS_PREFIX) - 1)
+
#define XATTR_SECURITY_PREFIX "security."
#define XATTR_SECURITY_PREFIX_LEN (sizeof(XATTR_SECURITY_PREFIX) - 1)
--
1.7.9.5
^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [PATCH V2] Btrfs: add support for inode properties
2013-11-13 18:59 ` Goffredo Baroncelli
@ 2013-11-19 16:06 ` Filipe David Manana
2013-11-19 17:44 ` Goffredo Baroncelli
0 siblings, 1 reply; 11+ messages in thread
From: Filipe David Manana @ 2013-11-19 16:06 UTC (permalink / raw)
To: kreijack; +Cc: linux-btrfs@vger.kernel.org
On Wed, Nov 13, 2013 at 6:59 PM, Goffredo Baroncelli <kreijack@libero.it> wrote:
> Hi Filipe,
>
> my comments below
> On 2013-11-13 02:21, Filipe David Borba Manana wrote:
>> 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're 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.
>>
>> 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.
>>
>> Signed-off-by: Filipe David Borba Manana <fdmanana@gmail.com>
>> ---
>>
>> V2: Simplified the interface to user space. Got rid of the ioctls and
>> using now the regular path for setting and getting xattrs. Like
>> this properties can be set via regular getxattr and setxattr
>> system calls.
>>
>> fs/btrfs/Makefile | 2 +-
>> fs/btrfs/btrfs_inode.h | 1 +
>> fs/btrfs/inode.c | 27 +++-
>> fs/btrfs/props.c | 350 ++++++++++++++++++++++++++++++++++++++++++++
>> fs/btrfs/props.h | 36 +++++
>> fs/btrfs/xattr.c | 12 +-
>> include/uapi/linux/xattr.h | 3 +
>> 7 files changed, 425 insertions(+), 6 deletions(-)
>> create mode 100644 fs/btrfs/props.c
>> create mode 100644 fs/btrfs/props.h
>>
>
> [...]
>
>> +static int __btrfs_set_prop(struct btrfs_trans_handle *trans,
>> + struct inode *inode,
>> + const char *name,
>> + const char *value,
>> + size_t value_len,
>> + int flags)
>> +{
>> + const struct prop_handler *handler;
>> + int ret;
>> +
>> + handler = find_prop_handler(name);
>> + if (!handler)
>> + return -EINVAL;
>> +
>> + if (value_len == 0) {
>> + ret = __btrfs_setxattr(trans, inode, handler->xattr_name,
>> + NULL, 0, flags);
>> + if (ret)
>> + return ret;
>> +
>> + ret = handler->apply(inode, NULL, 0);
>> + ASSERT(ret == 0);
>> +
>> + return ret;
>> + }
>> +
>> + ret = handler->validate(value, value_len);
>> + if (ret)
>> + return ret;
>> +
>> + ret = __btrfs_setxattr(trans, inode, handler->xattr_name,
>> + value, value_len, flags);
>> + if (ret)
>> + return ret;
>> +
>> + ret = handler->apply(inode, value, value_len);
>> + if (!ret)
>> + set_bit(BTRFS_INODE_HAS_PROPS, &BTRFS_I(inode)->runtime_flags);
>
> Does the line above also apply even if value_len== 0 ?
Nop.
>
> Moreover I have a doubt about the opportunity to store the information
> like the compression both in inode and in the xattr: what happens if the
> user does an "chattr -c" ? The xattr will be not updated.
Right, there was no interaction the compression attr/flag.
The next patch version adds such interaction.
> I suggest to update/create a real xattr only if there is no space for
> the new information in the inode. In the other case the xattr should be
> a fake xattr to get/set the property stored in the inode (or whichever
> is involved).
>
> Of course also the function btrfs_listxattr() should be updated to show
> the "fake" xattr which doesn't have a real xattr stored on the disk. The
> same is true for the getxattr() function...
Adding such fake on the fly stuff seems too complex imho. Just
adding/removing the xattr compression properties on chattr +c/-c seems
simpler and more intuitive.
>
>> +
>> + return ret;
>> +}
>> +
>
> [...]
>
>> +int btrfs_load_inode_props(struct inode *inode)
>> +{
>> + struct btrfs_root *root = BTRFS_I(inode)->root;
>> + struct btrfs_key key;
>> + struct btrfs_path *path;
>> + u64 ino = btrfs_ino(inode);
>> + char *name_buf = NULL;
>> + char *value_buf = NULL;
>> + int name_buf_len = 0;
>> + int value_buf_len = 0;
>> + int ret;
>> +
>> + path = btrfs_alloc_path();
>> + if (!path)
>> + return -ENOMEM;
>> +
>> + key.objectid = ino;
>> + key.type = BTRFS_XATTR_ITEM_KEY;
>> + key.offset = 0;
>> +
>> + ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
>> + if (ret < 0)
>> + goto out;
>> +
>> + while (1) {
>> + struct btrfs_dir_item *di;
>> + struct extent_buffer *leaf;
>> + u32 total_len, cur, this_len;
>> + int slot;
>> +
>> + slot = path->slots[0];
>> + leaf = path->nodes[0];
>> +
>> + if (slot >= btrfs_header_nritems(leaf)) {
>> + ret = btrfs_next_leaf(root, path);
>> + if (ret < 0)
>> + goto out;
>> + else if (ret > 0)
>> + break;
>> + continue;
>> + }
>> +
>> + btrfs_item_key_to_cpu(leaf, &key, slot);
>> + if (key.objectid != ino || key.type != BTRFS_XATTR_ITEM_KEY)
>> + break;
>> +
>> + di = btrfs_item_ptr(leaf, slot, struct btrfs_dir_item);
>> + cur = 0;
>> + total_len = btrfs_item_size_nr(leaf, slot);
>> +
>> + while (cur < total_len) {
>> + u32 name_len = btrfs_dir_name_len(leaf, di);
>> + u32 data_len = btrfs_dir_data_len(leaf, di);
>> + unsigned long name_ptr, data_ptr;
>> + const struct prop_handler *handler;
>> +
>> + this_len = sizeof(*di) + name_len + data_len;
>> + name_ptr = (unsigned long)(di + 1);
>> + data_ptr = name_ptr + name_len;
>> +
>> + if (name_len <= 6 ||
>> + memcmp_extent_buffer(leaf, "btrfs.", name_ptr, 6))
>
> You should replace '"btrfs."' with XATTR_BTRFS_PREFIX and '6' with
> XATTR_BTRFS_PREFIX_LEN.
Right, forgot about that on the 2nd patch.
>
>> + goto next;
>> +
>> + if (name_len >= name_buf_len) {
>> + kfree(name_buf);
>> + name_buf_len = name_len + 1;
>> + name_buf = kmalloc(name_buf_len, GFP_NOFS);
>> + if (!name_buf) {
>> + ret = -ENOMEM;
>> + goto out;
>> + }
>> + }
>> + read_extent_buffer(leaf, name_buf, name_ptr, name_len);
>> + name_buf[name_len] = '\0';
>> +
>> + if (data_len > value_buf_len) {
>> + kfree(value_buf);
>> + value_buf_len = data_len;
>> + value_buf = kmalloc(data_len, GFP_NOFS);
>> + if (!value_buf) {
>> + ret = -ENOMEM;
>> + goto out;
>> + }
>> + }
>> + read_extent_buffer(leaf, value_buf, data_ptr, data_len);
>> +
>> + handler = find_prop_handler(name_buf);
>> + if (!handler) {
>> + pr_warn("btrfs: handler for prop %s, ino %llu (root %llu), not found",
>> + name_buf, ino, root->root_key.objectid);
>> + goto next;
>> + }
>> +
>> + ret = handler->apply(inode, value_buf, data_len);
>> + if (ret)
>> + pr_warn("btrfs: error applying prop %s to ino %llu (root %llu): %d",
>> + name_buf, ino,
>> + root->root_key.objectid, ret);
>> + else
>> + set_bit(BTRFS_INODE_HAS_PROPS,
>> + &BTRFS_I(inode)->runtime_flags);
>> +next:
>> + cur += this_len;
>> + di = (struct btrfs_dir_item *)((char *) di + this_len);
>> + }
>> +
>> + path->slots[0]++;
>> + }
>> +
>> + ret = 0;
>> +out:
>> + btrfs_free_path(path);
>> + kfree(name_buf);
>> + kfree(value_buf);
>> +
>> + return ret;
>> +}
>
> [...]
>
>> +static int prop_compression_apply(struct inode *inode,
>> + const char *value,
>> + size_t len)
>> +{
>> + int type;
>> +
>> + if (len == 0) {
>> + BTRFS_I(inode)->flags |= BTRFS_INODE_NOCOMPRESS;
>> + BTRFS_I(inode)->flags &= ~BTRFS_INODE_COMPRESS;
>> + BTRFS_I(inode)->force_compress = BTRFS_COMPRESS_NONE;
>> +
>> + return 0;
>> + }
>> +
>> + if (!strncmp("lzo", value, len))
>> + type = BTRFS_COMPRESS_LZO;
>> + else if (!strncmp("zlib", value, len))
>> + type = BTRFS_COMPRESS_ZLIB;
>> + else
>> + return -EINVAL;
>> +
>> + BTRFS_I(inode)->flags &= ~BTRFS_INODE_NOCOMPRESS;
>> + BTRFS_I(inode)->flags |= BTRFS_INODE_COMPRESS;
>> + BTRFS_I(inode)->force_compress = type;
>> +
>> + return 0;
>
> Other than setting the flag, should the inode be marked "dirty" in order
> to flush the new information on the disk ? I noticed that in the
> function btrfs_ioctl_setflags(), after updating the flags there are the
> following calls:
Right, it was intended to do it via the existing setxattr call, which
updates the on disk inode.
Even if those flags aren't set in the ondisk inode item, it would
still work as intended, since when the inode is loaded all its
properties are applied.
thanks Goffredo
>
> btrfs_update_iflags(inode);
> inode_inc_iversion(inode);
> inode->i_ctime = CURRENT_TIME;
> ret = btrfs_update_inode(trans, root, inode);
>
> btrfs_end_transaction(trans, root);
>
>
>
>
>> +}
>
>
> [...]
>
> GB
> --
> gpg @keyserver.linux.it: Goffredo Baroncelli (kreijackATinwind.it>
> Key fingerprint BBF5 1610 0B64 DAC6 5F7D 17B2 0EDA 9B37 8B82 E0B5
--
Filipe David Manana,
"Reasonable men adapt themselves to the world.
Unreasonable men adapt the world to themselves.
That's why all progress depends on unreasonable men."
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH V2] Btrfs: add support for inode properties
2013-11-19 16:06 ` Filipe David Manana
@ 2013-11-19 17:44 ` Goffredo Baroncelli
0 siblings, 0 replies; 11+ messages in thread
From: Goffredo Baroncelli @ 2013-11-19 17:44 UTC (permalink / raw)
To: fdmanana; +Cc: linux-btrfs@vger.kernel.org
Hi Filipe
On 2013-11-19 17:06, Filipe David Manana wrote:
> On Wed, Nov 13, 2013 at 6:59 PM, Goffredo Baroncelli <kreijack@libero.it> wrote:
>> Hi Filipe,
>>
>> my comments below
>> On 2013-11-13 02:21, Filipe David Borba Manana wrote:
>>> 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're stored as xattrs with the
>>> prefix "btrfs."
[...]
>
>>
>> Moreover I have a doubt about the opportunity to store the information
>> like the compression both in inode and in the xattr: what happens if the
>> user does an "chattr -c" ? The xattr will be not updated.
>
> Right, there was no interaction the compression attr/flag.
> The next patch version adds such interaction.
>
>> I suggest to update/create a real xattr only if there is no space for
>> the new information in the inode. In the other case the xattr should be
>> a fake xattr to get/set the property stored in the inode (or whichever
>> is involved).
>>
>> Of course also the function btrfs_listxattr() should be updated to show
>> the "fake" xattr which doesn't have a real xattr stored on the disk. The
>> same is true for the getxattr() function...
>
> Adding such fake on the fly stuff seems too complex imho. Just
> adding/removing the xattr compression properties on chattr +c/-c seems
> simpler and more intuitive.
Looking at btrfs_listxattr() it seems that the xattr listing is done in
a very simple way: the function *listxattr() returns a buffer filled
with all the xattr as a zero terminated strings sequences. Because the
"fake" attribute are know in advance, listing these became a memcpy() of
a bytes of buffer statically allocated.
GB
--
gpg @keyserver.linux.it: Goffredo Baroncelli (kreijackATinwind.it>
Key fingerprint BBF5 1610 0B64 DAC6 5F7D 17B2 0EDA 9B37 8B82 E0B5
^ permalink raw reply [flat|nested] 11+ messages in thread
* [RFC PATCH v4] Btrfs: add support for inode properties
2013-11-12 13:42 [RFC PATCH] Btrfs: add support for inode properties Filipe David Borba Manana
` (2 preceding siblings ...)
2013-11-19 16:00 ` [PATCH v3] " Filipe David Borba Manana
@ 2014-01-07 11:47 ` Filipe David Borba Manana
3 siblings, 0 replies; 11+ messages in thread
From: Filipe David Borba Manana @ 2014-01-07 11:47 UTC (permalink / raw)
To: linux-btrfs; +Cc: Filipe David Borba Manana
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>
---
V4: Several refactorings and simplfications. Improved performance when loading
inodes with properties and creating inodes that inherit properties from
their parent inode.
NOTE - the xfstest I have been using to test this feature can be found at:
https://friendpaste.com/2MwyvFGA60ESc2FCVkoOBg
fs/btrfs/Makefile | 2 +-
fs/btrfs/btrfs_inode.h | 1 +
fs/btrfs/ctree.h | 4 +-
fs/btrfs/inode.c | 42 ++++-
fs/btrfs/ioctl.c | 19 +-
fs/btrfs/props.c | 427 ++++++++++++++++++++++++++++++++++++++++++++
fs/btrfs/props.h | 42 +++++
fs/btrfs/super.c | 3 +
fs/btrfs/xattr.c | 12 +-
include/uapi/linux/xattr.h | 3 +
10 files changed, 545 insertions(+), 10 deletions(-)
create mode 100644 fs/btrfs/props.c
create mode 100644 fs/btrfs/props.h
diff --git a/fs/btrfs/Makefile b/fs/btrfs/Makefile
index 1a44e42..af7f000 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 \
export.o tree-log.o free-space-cache.o zlib.o lzo.o \
compression.o delayed-ref.o relocation.o delayed-inode.o scrub.o \
reada.o backref.o ulist.o qgroup.o send.o dev-replace.o raid56.o \
- uuid-tree.o
+ uuid-tree.o props.o
btrfs-$(CONFIG_BTRFS_FS_POSIX_ACL) += acl.o
btrfs-$(CONFIG_BTRFS_FS_CHECK_INTEGRITY) += check-integrity.o
diff --git a/fs/btrfs/btrfs_inode.h b/fs/btrfs/btrfs_inode.h
index ac0b39d..7dc2f78 100644
--- a/fs/btrfs/btrfs_inode.h
+++ b/fs/btrfs/btrfs_inode.h
@@ -43,6 +43,7 @@
#define BTRFS_INODE_COPY_EVERYTHING 8
#define BTRFS_INODE_IN_DELALLOC_LIST 9
#define BTRFS_INODE_READDIO_NEED_LOCK 10
+#define BTRFS_INODE_HAS_PROPS 11
/* in memory btrfs inode */
struct btrfs_inode {
diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
index 5be778e..60d2021 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);
int btrfs_set_extent_delalloc(struct inode *inode, u64 start, u64 end,
struct extent_state **cached_state);
int btrfs_create_subvol_root(struct btrfs_trans_handle *trans,
- struct btrfs_root *new_root, u64 new_dirid);
+ struct btrfs_root *new_root,
+ struct btrfs_root *parent_root,
+ u64 new_dirid);
int btrfs_merge_bio_hook(int rw, struct page *page, unsigned long offset,
size_t size, struct bio *bio,
unsigned long bio_flags);
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index 26a1d53..d6a445f 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -58,6 +58,7 @@
#include "inode-map.h"
#include "backref.h"
#include "hash.h"
+#include "props.h"
struct btrfs_iget_args {
u64 ino;
@@ -3242,7 +3243,8 @@ out:
* slot is the slot the inode is in, objectid is the objectid of the inode
*/
static noinline int acls_after_inode_item(struct extent_buffer *leaf,
- int slot, u64 objectid)
+ int slot, u64 objectid,
+ int *first_xattr_slot)
{
u32 nritems = btrfs_header_nritems(leaf);
struct btrfs_key found_key;
@@ -3258,6 +3260,7 @@ static noinline int acls_after_inode_item(struct extent_buffer *leaf,
}
slot++;
+ *first_xattr_slot = -1;
while (slot < nritems) {
btrfs_item_key_to_cpu(leaf, &found_key, slot);
@@ -3267,6 +3270,8 @@ static noinline int acls_after_inode_item(struct extent_buffer *leaf,
/* we found an xattr, assume we've got an acl */
if (found_key.type == BTRFS_XATTR_ITEM_KEY) {
+ if (*first_xattr_slot == -1)
+ *first_xattr_slot = slot;
if (found_key.offset == xattr_access ||
found_key.offset == xattr_default)
return 1;
@@ -3295,6 +3300,8 @@ static noinline int acls_after_inode_item(struct extent_buffer *leaf,
* something larger than an xattr. We have to assume the inode
* has acls
*/
+ if (*first_xattr_slot == -1)
+ *first_xattr_slot = slot;
return 1;
}
@@ -3313,6 +3320,7 @@ static void btrfs_read_locked_inode(struct inode *inode)
u32 rdev;
int ret;
bool filled = false;
+ int first_xattr_slot;
ret = btrfs_fill_inode(inode, &rdev);
if (!ret)
@@ -3322,7 +3330,6 @@ static void btrfs_read_locked_inode(struct inode *inode)
if (!path)
goto make_bad;
- path->leave_spinning = 1;
memcpy(&location, &BTRFS_I(inode)->location, sizeof(location));
ret = btrfs_lookup_inode(NULL, root, path, &location, 0);
@@ -3381,12 +3388,21 @@ cache_acl:
* any xattrs or acls
*/
maybe_acls = acls_after_inode_item(leaf, path->slots[0],
- btrfs_ino(inode));
+ btrfs_ino(inode), &first_xattr_slot);
+ if (first_xattr_slot != -1) {
+ path->slots[0] = first_xattr_slot;
+ ret = btrfs_load_inode_props(inode, path);
+ if (ret)
+ btrfs_err(root->fs_info,
+ "error loading props for ino %llu (root %llu): %d\n",
+ btrfs_ino(inode),
+ root->root_key.objectid, ret);
+ }
+ btrfs_free_path(path);
+
if (!maybe_acls)
cache_no_acl(inode);
- btrfs_free_path(path);
-
switch (inode->i_mode & S_IFMT) {
case S_IFREG:
inode->i_mapping->a_ops = &btrfs_aops;
@@ -5540,6 +5556,12 @@ static struct inode *btrfs_new_inode(struct btrfs_trans_handle *trans,
btrfs_update_root_times(trans, root);
+ ret = btrfs_inode_inherit_props(trans, inode, dir);
+ if (ret)
+ btrfs_err(root->fs_info,
+ "error inheriting props for ino %llu (root %llu): %d",
+ btrfs_ino(inode), root->root_key.objectid, ret);
+
return inode;
fail:
if (dir)
@@ -7817,7 +7839,9 @@ out:
* create a new subvolume directory/inode (helper for the ioctl).
*/
int btrfs_create_subvol_root(struct btrfs_trans_handle *trans,
- struct btrfs_root *new_root, u64 new_dirid)
+ struct btrfs_root *new_root,
+ struct btrfs_root *parent_root,
+ u64 new_dirid)
{
struct inode *inode;
int err;
@@ -7835,6 +7859,12 @@ int btrfs_create_subvol_root(struct btrfs_trans_handle *trans,
set_nlink(inode, 1);
btrfs_i_size_write(inode, 0);
+ err = btrfs_subvol_inherit_props(trans, new_root, parent_root);
+ if (err)
+ btrfs_err(new_root->fs_info,
+ "error inheriting subvolume %llu properties: %d\n",
+ new_root->root_key.objectid, err);
+
err = btrfs_update_inode(trans, new_root, inode);
iput(inode);
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
index edf5f00..dc3d2fa 100644
--- a/fs/btrfs/ioctl.c
+++ b/fs/btrfs/ioctl.c
@@ -56,6 +56,7 @@
#include "rcu-string.h"
#include "send.h"
#include "dev-replace.h"
+#include "props.h"
#include "sysfs.h"
static int btrfs_clone(struct inode *src, struct inode *inode,
@@ -281,9 +282,25 @@ static int btrfs_ioctl_setflags(struct file *file, void __user *arg)
if (flags & FS_NOCOMP_FL) {
ip->flags &= ~BTRFS_INODE_COMPRESS;
ip->flags |= BTRFS_INODE_NOCOMPRESS;
+
+ ret = btrfs_set_prop(inode, "btrfs.compression", NULL, 0, 0);
+ if (ret && ret != -ENODATA)
+ goto out_drop;
} else if (flags & FS_COMPR_FL) {
+ const char *comp;
+
ip->flags |= BTRFS_INODE_COMPRESS;
ip->flags &= ~BTRFS_INODE_NOCOMPRESS;
+
+ if (root->fs_info->compress_type == BTRFS_COMPRESS_LZO)
+ comp = "lzo";
+ else
+ comp = "zlib";
+ ret = btrfs_set_prop(inode, "btrfs.compression",
+ comp, strlen(comp), 0);
+ if (ret)
+ goto out_drop;
+
} else {
ip->flags &= ~(BTRFS_INODE_COMPRESS | BTRFS_INODE_NOCOMPRESS);
}
@@ -502,7 +519,7 @@ static noinline int create_subvol(struct inode *dir,
btrfs_record_root_in_trans(trans, new_root);
- ret = btrfs_create_subvol_root(trans, new_root, new_dirid);
+ ret = btrfs_create_subvol_root(trans, new_root, root, new_dirid);
if (ret) {
/* We potentially lose an unused inode item here */
btrfs_abort_transaction(trans, root, ret);
diff --git a/fs/btrfs/props.c b/fs/btrfs/props.c
new file mode 100644
index 0000000..129b1dd
--- /dev/null
+++ b/fs/btrfs/props.c
@@ -0,0 +1,427 @@
+/*
+ * Copyright (C) 2014 Filipe David Borba Manana <fdmanana@gmail.com>
+ *
+ * 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/hashtable.h>
+#include "props.h"
+#include "btrfs_inode.h"
+#include "hash.h"
+#include "transaction.h"
+#include "xattr.h"
+
+#define BTRFS_PROP_HANDLERS_HT_BITS 8
+static DEFINE_HASHTABLE(prop_handlers_ht, BTRFS_PROP_HANDLERS_HT_BITS);
+
+struct prop_handler {
+ struct hlist_node node;
+ const char *xattr_name;
+ int (*validate)(const char *value, size_t len);
+ int (*apply)(struct inode *inode, const char *value, size_t len);
+ const char *(*extract)(struct inode *inode);
+ int inheritable;
+};
+
+static int prop_compression_validate(const char *value, size_t len);
+static int prop_compression_apply(struct inode *inode,
+ const char *value,
+ size_t len);
+static const char *prop_compression_extract(struct inode *inode);
+
+static struct prop_handler prop_handlers[] = {
+ {
+ .xattr_name = XATTR_BTRFS_PREFIX "compression",
+ .validate = prop_compression_validate,
+ .apply = prop_compression_apply,
+ .extract = prop_compression_extract,
+ .inheritable = 1
+ },
+ {
+ .xattr_name = NULL
+ }
+};
+
+void __init btrfs_props_init(void)
+{
+ struct prop_handler *p;
+
+ hash_init(prop_handlers_ht);
+
+ for (p = &prop_handlers[0]; p->xattr_name; p++) {
+ u64 h = btrfs_name_hash(p->xattr_name, strlen(p->xattr_name));
+
+ hash_add(prop_handlers_ht, &p->node, h);
+ }
+}
+
+static const struct hlist_head *find_prop_handlers_by_hash(const u64 hash)
+{
+ struct hlist_head *h;
+
+ h = &prop_handlers_ht[hash_min(hash, BTRFS_PROP_HANDLERS_HT_BITS)];
+ if (hlist_empty(h))
+ return NULL;
+
+ return h;
+}
+
+static const struct prop_handler *
+find_prop_handler(const char *name,
+ const struct hlist_head *handlers)
+{
+ struct prop_handler *h;
+
+ if (!handlers) {
+ u64 hash = btrfs_name_hash(name, strlen(name));
+
+ handlers = find_prop_handlers_by_hash(hash);
+ if (!handlers)
+ return NULL;
+ }
+
+ hlist_for_each_entry(h, handlers, node)
+ if (!strcmp(h->xattr_name, name))
+ return h;
+
+ return NULL;
+}
+
+static int __btrfs_set_prop(struct btrfs_trans_handle *trans,
+ struct inode *inode,
+ const char *name,
+ const char *value,
+ size_t value_len,
+ int flags)
+{
+ const struct prop_handler *handler;
+ int ret;
+
+ if (strlen(name) <= XATTR_BTRFS_PREFIX_LEN)
+ return -EINVAL;
+
+ handler = find_prop_handler(name, NULL);
+ if (!handler)
+ return -EINVAL;
+
+ if (value_len == 0) {
+ ret = __btrfs_setxattr(trans, inode, handler->xattr_name,
+ NULL, 0, flags);
+ if (ret)
+ return ret;
+
+ ret = handler->apply(inode, NULL, 0);
+ ASSERT(ret == 0);
+
+ return ret;
+ }
+
+ ret = handler->validate(value, value_len);
+ if (ret)
+ return ret;
+ ret = __btrfs_setxattr(trans, inode, handler->xattr_name,
+ value, value_len, flags);
+ if (ret)
+ return ret;
+ ret = handler->apply(inode, value, value_len);
+ if (ret) {
+ __btrfs_setxattr(trans, inode, handler->xattr_name,
+ NULL, 0, flags);
+ return ret;
+ }
+
+ set_bit(BTRFS_INODE_HAS_PROPS, &BTRFS_I(inode)->runtime_flags);
+
+ return 0;
+}
+
+int btrfs_set_prop(struct inode *inode,
+ const char *name,
+ const char *value,
+ size_t value_len,
+ int flags)
+{
+ return __btrfs_set_prop(NULL, inode, name, value, value_len, flags);
+}
+
+static int iterate_object_props(struct btrfs_root *root,
+ struct btrfs_path *path,
+ u64 objectid,
+ void (*iterator)(void *,
+ const struct prop_handler *,
+ const char *,
+ size_t),
+ void *ctx)
+{
+ int ret;
+ char *name_buf = NULL;
+ char *value_buf = NULL;
+ int name_buf_len = 0;
+ int value_buf_len = 0;
+
+ while (1) {
+ struct btrfs_key key;
+ struct btrfs_dir_item *di;
+ struct extent_buffer *leaf;
+ u32 total_len, cur, this_len;
+ int slot;
+ const struct hlist_head *handlers;
+
+ slot = path->slots[0];
+ leaf = path->nodes[0];
+
+ if (slot >= btrfs_header_nritems(leaf)) {
+ ret = btrfs_next_leaf(root, path);
+ if (ret < 0)
+ goto out;
+ else if (ret > 0)
+ break;
+ continue;
+ }
+
+ btrfs_item_key_to_cpu(leaf, &key, slot);
+ if (key.objectid != objectid)
+ break;
+ if (key.type != BTRFS_XATTR_ITEM_KEY)
+ break;
+
+ handlers = find_prop_handlers_by_hash(key.offset);
+ if (!handlers)
+ goto next_slot;
+
+ di = btrfs_item_ptr(leaf, slot, struct btrfs_dir_item);
+ cur = 0;
+ total_len = btrfs_item_size_nr(leaf, slot);
+
+ while (cur < total_len) {
+ u32 name_len = btrfs_dir_name_len(leaf, di);
+ u32 data_len = btrfs_dir_data_len(leaf, di);
+ unsigned long name_ptr, data_ptr;
+ const struct prop_handler *handler;
+
+ this_len = sizeof(*di) + name_len + data_len;
+ name_ptr = (unsigned long)(di + 1);
+ data_ptr = name_ptr + name_len;
+
+ if (name_len <= XATTR_BTRFS_PREFIX_LEN ||
+ memcmp_extent_buffer(leaf, XATTR_BTRFS_PREFIX,
+ name_ptr,
+ XATTR_BTRFS_PREFIX_LEN))
+ goto next_dir_item;
+
+ if (name_len >= name_buf_len) {
+ kfree(name_buf);
+ name_buf_len = name_len + 1;
+ name_buf = kmalloc(name_buf_len, GFP_NOFS);
+ if (!name_buf) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ }
+ read_extent_buffer(leaf, name_buf, name_ptr, name_len);
+ name_buf[name_len] = '\0';
+
+ handler = find_prop_handler(name_buf, handlers);
+ if (!handler)
+ goto next_dir_item;
+
+ if (data_len > value_buf_len) {
+ kfree(value_buf);
+ value_buf_len = data_len;
+ value_buf = kmalloc(data_len, GFP_NOFS);
+ if (!value_buf) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ }
+ read_extent_buffer(leaf, value_buf, data_ptr, data_len);
+
+ iterator(ctx, handler, value_buf, data_len);
+next_dir_item:
+ cur += this_len;
+ di = (struct btrfs_dir_item *)((char *) di + this_len);
+ }
+
+next_slot:
+ path->slots[0]++;
+ }
+
+ ret = 0;
+out:
+ btrfs_release_path(path);
+ kfree(name_buf);
+ kfree(value_buf);
+
+ return ret;
+}
+
+static void inode_prop_iterator(void *ctx,
+ const struct prop_handler *handler,
+ const char *value,
+ size_t len)
+{
+ struct inode *inode = ctx;
+ struct btrfs_root *root = BTRFS_I(inode)->root;
+ int ret;
+
+ ret = handler->apply(inode, value, len);
+ if (unlikely(ret))
+ btrfs_warn(root->fs_info,
+ "error applying prop %s to ino %llu (root %llu): %d",
+ handler->xattr_name, btrfs_ino(inode),
+ root->root_key.objectid, ret);
+ else
+ set_bit(BTRFS_INODE_HAS_PROPS, &BTRFS_I(inode)->runtime_flags);
+}
+
+int btrfs_load_inode_props(struct inode *inode, struct btrfs_path *path)
+{
+ struct btrfs_root *root = BTRFS_I(inode)->root;
+ u64 ino = btrfs_ino(inode);
+ int ret;
+
+ ret = iterate_object_props(root, path, ino, inode_prop_iterator, inode);
+
+ return ret;
+}
+
+static int inherit_props(struct btrfs_trans_handle *trans,
+ struct inode *inode,
+ struct inode *parent)
+{
+ const struct prop_handler *h;
+ struct btrfs_root *root = BTRFS_I(inode)->root;
+ int ret;
+
+ if (!test_bit(BTRFS_INODE_HAS_PROPS,
+ &BTRFS_I(parent)->runtime_flags))
+ return 0;
+
+ for (h = &prop_handlers[0]; h->xattr_name; h++) {
+ const char *value;
+ u64 num_bytes;
+
+ if (!h->inheritable)
+ continue;
+
+ value = h->extract(parent);
+ if (!value)
+ continue;
+
+ num_bytes = btrfs_calc_trans_metadata_size(root, 1);
+ ret = btrfs_block_rsv_add(root, trans->block_rsv,
+ num_bytes, BTRFS_RESERVE_NO_FLUSH);
+ if (ret)
+ goto out;
+ ret = __btrfs_set_prop(trans, inode, h->xattr_name,
+ value, strlen(value), 0);
+ btrfs_block_rsv_release(root, trans->block_rsv, num_bytes);
+ if (ret)
+ goto out;
+ }
+ ret = 0;
+out:
+ return ret;
+}
+
+int btrfs_inode_inherit_props(struct btrfs_trans_handle *trans,
+ struct inode *inode,
+ struct inode *dir)
+{
+ if (!dir)
+ return 0;
+
+ return inherit_props(trans, inode, dir);
+}
+
+int btrfs_subvol_inherit_props(struct btrfs_trans_handle *trans,
+ struct btrfs_root *root,
+ struct btrfs_root *parent_root)
+{
+ struct btrfs_key key;
+ struct inode *parent_inode, *child_inode;
+ int ret;
+
+ key.objectid = BTRFS_FIRST_FREE_OBJECTID;
+ key.type = BTRFS_INODE_ITEM_KEY;
+ key.offset = 0;
+
+ parent_inode = btrfs_iget(parent_root->fs_info->sb, &key,
+ parent_root, NULL);
+ if (IS_ERR(parent_inode))
+ return PTR_ERR(parent_inode);
+
+ child_inode = btrfs_iget(root->fs_info->sb, &key, root, NULL);
+ if (IS_ERR(child_inode)) {
+ iput(parent_inode);
+ return PTR_ERR(child_inode);
+ }
+
+ ret = inherit_props(trans, child_inode, parent_inode);
+ iput(child_inode);
+ iput(parent_inode);
+
+ return ret;
+}
+
+static int prop_compression_validate(const char *value, size_t len)
+{
+ if (!strncmp("lzo", value, len))
+ return 0;
+ else if (!strncmp("zlib", value, len))
+ return 0;
+
+ return -EINVAL;
+}
+
+static int prop_compression_apply(struct inode *inode,
+ const char *value,
+ size_t len)
+{
+ int type;
+
+ if (len == 0) {
+ BTRFS_I(inode)->flags |= BTRFS_INODE_NOCOMPRESS;
+ BTRFS_I(inode)->flags &= ~BTRFS_INODE_COMPRESS;
+ BTRFS_I(inode)->force_compress = BTRFS_COMPRESS_NONE;
+
+ return 0;
+ }
+
+ if (!strncmp("lzo", value, len))
+ type = BTRFS_COMPRESS_LZO;
+ else if (!strncmp("zlib", value, len))
+ type = BTRFS_COMPRESS_ZLIB;
+ else
+ return -EINVAL;
+
+ BTRFS_I(inode)->flags &= ~BTRFS_INODE_NOCOMPRESS;
+ BTRFS_I(inode)->flags |= BTRFS_INODE_COMPRESS;
+ BTRFS_I(inode)->force_compress = type;
+
+ return 0;
+}
+
+static const char *prop_compression_extract(struct inode *inode)
+{
+ switch (BTRFS_I(inode)->force_compress) {
+ case BTRFS_COMPRESS_ZLIB:
+ return "zlib";
+ case BTRFS_COMPRESS_LZO:
+ return "lzo";
+ }
+
+ return NULL;
+}
diff --git a/fs/btrfs/props.h b/fs/btrfs/props.h
new file mode 100644
index 0000000..100f188
--- /dev/null
+++ b/fs/btrfs/props.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2014 Filipe David Borba Manana <fdmanana@gmail.com>
+ *
+ * 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 __BTRFS_PROPS_H
+#define __BTRFS_PROPS_H
+
+#include "ctree.h"
+
+void __init btrfs_props_init(void);
+
+int btrfs_set_prop(struct inode *inode,
+ const char *name,
+ const char *value,
+ size_t value_len,
+ int flags);
+
+int btrfs_load_inode_props(struct inode *inode, struct btrfs_path *path);
+
+int btrfs_inode_inherit_props(struct btrfs_trans_handle *trans,
+ struct inode *inode,
+ struct inode *dir);
+
+int btrfs_subvol_inherit_props(struct btrfs_trans_handle *trans,
+ struct btrfs_root *root,
+ struct btrfs_root *parent_root);
+
+#endif
diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c
index 15b6a1d..f65a409 100644
--- a/fs/btrfs/super.c
+++ b/fs/btrfs/super.c
@@ -48,6 +48,7 @@
#include "transaction.h"
#include "btrfs_inode.h"
#include "print-tree.h"
+#include "props.h"
#include "xattr.h"
#include "volumes.h"
#include "export.h"
@@ -1812,6 +1813,8 @@ static int __init init_btrfs_fs(void)
{
int err;
+ btrfs_props_init();
+
err = btrfs_init_sysfs();
if (err)
return err;
diff --git a/fs/btrfs/xattr.c b/fs/btrfs/xattr.c
index 05740b9..4b33765 100644
--- a/fs/btrfs/xattr.c
+++ b/fs/btrfs/xattr.c
@@ -27,6 +27,7 @@
#include "transaction.h"
#include "xattr.h"
#include "disk-io.h"
+#include "props.h"
ssize_t __btrfs_getxattr(struct inode *inode, const char *name,
@@ -331,7 +332,8 @@ static bool btrfs_is_valid_xattr(const char *name)
XATTR_SECURITY_PREFIX_LEN) ||
!strncmp(name, XATTR_SYSTEM_PREFIX, XATTR_SYSTEM_PREFIX_LEN) ||
!strncmp(name, XATTR_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN) ||
- !strncmp(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN);
+ !strncmp(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN) ||
+ !strncmp(name, XATTR_BTRFS_PREFIX, XATTR_BTRFS_PREFIX_LEN);
}
ssize_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,
if (!btrfs_is_valid_xattr(name))
return -EOPNOTSUPP;
+ if (!strncmp(name, XATTR_BTRFS_PREFIX, XATTR_BTRFS_PREFIX_LEN))
+ return btrfs_set_prop(dentry->d_inode, name,
+ value, size, flags);
+
if (size == 0)
value = ""; /* empty EA, do not remove */
@@ -402,6 +408,10 @@ int btrfs_removexattr(struct dentry *dentry, const char *name)
if (!btrfs_is_valid_xattr(name))
return -EOPNOTSUPP;
+ if (!strncmp(name, XATTR_BTRFS_PREFIX, XATTR_BTRFS_PREFIX_LEN))
+ return btrfs_set_prop(dentry->d_inode, name,
+ NULL, 0, XATTR_REPLACE);
+
return __btrfs_setxattr(NULL, dentry->d_inode, name, NULL, 0,
XATTR_REPLACE);
}
diff --git a/include/uapi/linux/xattr.h b/include/uapi/linux/xattr.h
index e4629b9..40bbc04 100644
--- a/include/uapi/linux/xattr.h
+++ b/include/uapi/linux/xattr.h
@@ -20,6 +20,9 @@
#define XATTR_MAC_OSX_PREFIX "osx."
#define XATTR_MAC_OSX_PREFIX_LEN (sizeof(XATTR_MAC_OSX_PREFIX) - 1)
+#define XATTR_BTRFS_PREFIX "btrfs."
+#define XATTR_BTRFS_PREFIX_LEN (sizeof(XATTR_BTRFS_PREFIX) - 1)
+
#define XATTR_SECURITY_PREFIX "security."
#define XATTR_SECURITY_PREFIX_LEN (sizeof(XATTR_SECURITY_PREFIX) - 1)
--
1.7.9.5
^ permalink raw reply related [flat|nested] 11+ messages in thread
end of thread, other threads:[~2014-01-07 11:48 UTC | newest]
Thread overview: 11+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2013-11-12 13:42 [RFC PATCH] Btrfs: add support for inode properties Filipe David Borba Manana
2013-11-12 19:24 ` Goffredo Baroncelli
2013-11-12 20:04 ` Filipe David Manana
2013-11-12 20:07 ` Hugo Mills
2013-11-13 0:30 ` Filipe David Manana
2013-11-13 1:21 ` [PATCH V2] " Filipe David Borba Manana
2013-11-13 18:59 ` Goffredo Baroncelli
2013-11-19 16:06 ` Filipe David Manana
2013-11-19 17:44 ` Goffredo Baroncelli
2013-11-19 16:00 ` [PATCH v3] " Filipe David Borba Manana
2014-01-07 11:47 ` [RFC PATCH v4] " Filipe David Borba Manana
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).