123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519 |
- /*
- * Copyright (C) 2007 Red Hat. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public
- * License v2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public
- * License along with this program; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 021110-1307, USA.
- */
- #include <linux/init.h>
- #include <linux/fs.h>
- #include <linux/slab.h>
- #include <linux/rwsem.h>
- #include <linux/xattr.h>
- #include <linux/security.h>
- #include <linux/posix_acl_xattr.h>
- #include "ctree.h"
- #include "btrfs_inode.h"
- #include "transaction.h"
- #include "xattr.h"
- #include "disk-io.h"
- #include "props.h"
- #include "locking.h"
- ssize_t __btrfs_getxattr(struct inode *inode, const char *name,
- void *buffer, size_t size)
- {
- struct btrfs_dir_item *di;
- struct btrfs_root *root = BTRFS_I(inode)->root;
- struct btrfs_path *path;
- struct extent_buffer *leaf;
- int ret = 0;
- unsigned long data_ptr;
- path = btrfs_alloc_path();
- if (!path)
- return -ENOMEM;
- /* lookup the xattr by name */
- di = btrfs_lookup_xattr(NULL, root, path, btrfs_ino(inode), name,
- strlen(name), 0);
- if (!di) {
- ret = -ENODATA;
- goto out;
- } else if (IS_ERR(di)) {
- ret = PTR_ERR(di);
- goto out;
- }
- leaf = path->nodes[0];
- /* if size is 0, that means we want the size of the attr */
- if (!size) {
- ret = btrfs_dir_data_len(leaf, di);
- goto out;
- }
- /* now get the data out of our dir_item */
- if (btrfs_dir_data_len(leaf, di) > size) {
- ret = -ERANGE;
- goto out;
- }
- /*
- * The way things are packed into the leaf is like this
- * |struct btrfs_dir_item|name|data|
- * where name is the xattr name, so security.foo, and data is the
- * content of the xattr. data_ptr points to the location in memory
- * where the data starts in the in memory leaf
- */
- data_ptr = (unsigned long)((char *)(di + 1) +
- btrfs_dir_name_len(leaf, di));
- read_extent_buffer(leaf, buffer, data_ptr,
- btrfs_dir_data_len(leaf, di));
- ret = btrfs_dir_data_len(leaf, di);
- out:
- btrfs_free_path(path);
- return ret;
- }
- static int do_setxattr(struct btrfs_trans_handle *trans,
- struct inode *inode, const char *name,
- const void *value, size_t size, int flags)
- {
- struct btrfs_dir_item *di = NULL;
- struct btrfs_root *root = BTRFS_I(inode)->root;
- struct btrfs_path *path;
- size_t name_len = strlen(name);
- int ret = 0;
- if (name_len + size > BTRFS_MAX_XATTR_SIZE(root))
- return -ENOSPC;
- path = btrfs_alloc_path();
- if (!path)
- return -ENOMEM;
- path->skip_release_on_error = 1;
- if (!value) {
- di = btrfs_lookup_xattr(trans, root, path, btrfs_ino(inode),
- name, name_len, -1);
- if (!di && (flags & XATTR_REPLACE))
- ret = -ENODATA;
- else if (IS_ERR(di))
- ret = PTR_ERR(di);
- else if (di)
- ret = btrfs_delete_one_dir_name(trans, root, path, di);
- goto out;
- }
- /*
- * For a replace we can't just do the insert blindly.
- * Do a lookup first (read-only btrfs_search_slot), and return if xattr
- * doesn't exist. If it exists, fall down below to the insert/replace
- * path - we can't race with a concurrent xattr delete, because the VFS
- * locks the inode's i_mutex before calling setxattr or removexattr.
- */
- if (flags & XATTR_REPLACE) {
- ASSERT(mutex_is_locked(&inode->i_mutex));
- di = btrfs_lookup_xattr(NULL, root, path, btrfs_ino(inode),
- name, name_len, 0);
- if (!di)
- ret = -ENODATA;
- else if (IS_ERR(di))
- ret = PTR_ERR(di);
- if (ret)
- goto out;
- btrfs_release_path(path);
- di = NULL;
- }
- ret = btrfs_insert_xattr_item(trans, root, path, btrfs_ino(inode),
- name, name_len, value, size);
- if (ret == -EOVERFLOW) {
- /*
- * We have an existing item in a leaf, split_leaf couldn't
- * expand it. That item might have or not a dir_item that
- * matches our target xattr, so lets check.
- */
- ret = 0;
- btrfs_assert_tree_locked(path->nodes[0]);
- di = btrfs_match_dir_item_name(root, path, name, name_len);
- if (!di && !(flags & XATTR_REPLACE)) {
- ret = -ENOSPC;
- goto out;
- }
- } else if (ret == -EEXIST) {
- ret = 0;
- di = btrfs_match_dir_item_name(root, path, name, name_len);
- ASSERT(di); /* logic error */
- } else if (ret) {
- goto out;
- }
- if (di && (flags & XATTR_CREATE)) {
- ret = -EEXIST;
- goto out;
- }
- if (di) {
- /*
- * We're doing a replace, and it must be atomic, that is, at
- * any point in time we have either the old or the new xattr
- * value in the tree. We don't want readers (getxattr and
- * listxattrs) to miss a value, this is specially important
- * for ACLs.
- */
- const int slot = path->slots[0];
- struct extent_buffer *leaf = path->nodes[0];
- const u16 old_data_len = btrfs_dir_data_len(leaf, di);
- const u32 item_size = btrfs_item_size_nr(leaf, slot);
- const u32 data_size = sizeof(*di) + name_len + size;
- struct btrfs_item *item;
- unsigned long data_ptr;
- char *ptr;
- if (size > old_data_len) {
- if (btrfs_leaf_free_space(root, leaf) <
- (size - old_data_len)) {
- ret = -ENOSPC;
- goto out;
- }
- }
- if (old_data_len + name_len + sizeof(*di) == item_size) {
- /* No other xattrs packed in the same leaf item. */
- if (size > old_data_len)
- btrfs_extend_item(root, path,
- size - old_data_len);
- else if (size < old_data_len)
- btrfs_truncate_item(root, path, data_size, 1);
- } else {
- /* There are other xattrs packed in the same item. */
- ret = btrfs_delete_one_dir_name(trans, root, path, di);
- if (ret)
- goto out;
- btrfs_extend_item(root, path, data_size);
- }
- item = btrfs_item_nr(slot);
- ptr = btrfs_item_ptr(leaf, slot, char);
- ptr += btrfs_item_size(leaf, item) - data_size;
- di = (struct btrfs_dir_item *)ptr;
- btrfs_set_dir_data_len(leaf, di, size);
- data_ptr = ((unsigned long)(di + 1)) + name_len;
- write_extent_buffer(leaf, value, data_ptr, size);
- btrfs_mark_buffer_dirty(leaf);
- } else {
- /*
- * Insert, and we had space for the xattr, so path->slots[0] is
- * where our xattr dir_item is and btrfs_insert_xattr_item()
- * filled it.
- */
- }
- out:
- btrfs_free_path(path);
- return ret;
- }
- /*
- * @value: "" makes the attribute to empty, NULL removes it
- */
- int __btrfs_setxattr(struct btrfs_trans_handle *trans,
- struct inode *inode, const char *name,
- const void *value, size_t size, int flags)
- {
- struct btrfs_root *root = BTRFS_I(inode)->root;
- int ret;
- if (trans)
- return do_setxattr(trans, inode, name, value, size, flags);
- trans = btrfs_start_transaction(root, 2);
- if (IS_ERR(trans))
- return PTR_ERR(trans);
- ret = do_setxattr(trans, inode, name, value, size, flags);
- if (ret)
- goto out;
- inode_inc_iversion(inode);
- inode->i_ctime = CURRENT_TIME;
- set_bit(BTRFS_INODE_COPY_EVERYTHING, &BTRFS_I(inode)->runtime_flags);
- ret = btrfs_update_inode(trans, root, inode);
- BUG_ON(ret);
- out:
- btrfs_end_transaction(trans, root);
- return ret;
- }
- ssize_t btrfs_listxattr(struct dentry *dentry, char *buffer, size_t size)
- {
- struct btrfs_key key, found_key;
- struct inode *inode = d_inode(dentry);
- struct btrfs_root *root = BTRFS_I(inode)->root;
- struct btrfs_path *path;
- struct extent_buffer *leaf;
- struct btrfs_dir_item *di;
- int ret = 0, slot;
- size_t total_size = 0, size_left = size;
- unsigned long name_ptr;
- size_t name_len;
- /*
- * ok we want all objects associated with this id.
- * NOTE: we set key.offset = 0; because we want to start with the
- * first xattr that we find and walk forward
- */
- key.objectid = btrfs_ino(inode);
- key.type = BTRFS_XATTR_ITEM_KEY;
- key.offset = 0;
- path = btrfs_alloc_path();
- if (!path)
- return -ENOMEM;
- path->reada = 2;
- /* search for our xattrs */
- ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
- if (ret < 0)
- goto err;
- while (1) {
- leaf = path->nodes[0];
- slot = path->slots[0];
- /* this is where we start walking through the path */
- if (slot >= btrfs_header_nritems(leaf)) {
- /*
- * if we've reached the last slot in this leaf we need
- * to go to the next leaf and reset everything
- */
- ret = btrfs_next_leaf(root, path);
- if (ret < 0)
- goto err;
- else if (ret > 0)
- break;
- continue;
- }
- btrfs_item_key_to_cpu(leaf, &found_key, slot);
- /* check to make sure this item is what we want */
- if (found_key.objectid != key.objectid)
- break;
- if (found_key.type > BTRFS_XATTR_ITEM_KEY)
- break;
- if (found_key.type < BTRFS_XATTR_ITEM_KEY)
- goto next;
- di = btrfs_item_ptr(leaf, slot, struct btrfs_dir_item);
- if (verify_dir_item(root, leaf, di))
- goto next;
- name_len = btrfs_dir_name_len(leaf, di);
- total_size += name_len + 1;
- /* we are just looking for how big our buffer needs to be */
- if (!size)
- goto next;
- if (!buffer || (name_len + 1) > size_left) {
- ret = -ERANGE;
- goto err;
- }
- name_ptr = (unsigned long)(di + 1);
- read_extent_buffer(leaf, buffer, name_ptr, name_len);
- buffer[name_len] = '\0';
- size_left -= name_len + 1;
- buffer += name_len + 1;
- next:
- path->slots[0]++;
- }
- ret = total_size;
- err:
- btrfs_free_path(path);
- return ret;
- }
- /*
- * List of handlers for synthetic system.* attributes. All real ondisk
- * attributes are handled directly.
- */
- const struct xattr_handler *btrfs_xattr_handlers[] = {
- #ifdef CONFIG_BTRFS_FS_POSIX_ACL
- &posix_acl_access_xattr_handler,
- &posix_acl_default_xattr_handler,
- #endif
- NULL,
- };
- /*
- * Check if the attribute is in a supported namespace.
- *
- * This is applied after the check for the synthetic attributes in the system
- * namespace.
- */
- static int btrfs_is_valid_xattr(const char *name)
- {
- int len = strlen(name);
- int prefixlen = 0;
- if (!strncmp(name, XATTR_SECURITY_PREFIX,
- XATTR_SECURITY_PREFIX_LEN))
- prefixlen = XATTR_SECURITY_PREFIX_LEN;
- else if (!strncmp(name, XATTR_SYSTEM_PREFIX, XATTR_SYSTEM_PREFIX_LEN))
- prefixlen = XATTR_SYSTEM_PREFIX_LEN;
- else if (!strncmp(name, XATTR_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN))
- prefixlen = XATTR_TRUSTED_PREFIX_LEN;
- else if (!strncmp(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN))
- prefixlen = XATTR_USER_PREFIX_LEN;
- else if (!strncmp(name, XATTR_BTRFS_PREFIX, XATTR_BTRFS_PREFIX_LEN))
- prefixlen = XATTR_BTRFS_PREFIX_LEN;
- else
- return -EOPNOTSUPP;
- /*
- * The name cannot consist of just prefix
- */
- if (len <= prefixlen)
- return -EINVAL;
- return 0;
- }
- ssize_t btrfs_getxattr(struct dentry *dentry, const char *name,
- void *buffer, size_t size)
- {
- int ret;
- /*
- * If this is a request for a synthetic attribute in the system.*
- * namespace use the generic infrastructure to resolve a handler
- * for it via sb->s_xattr.
- */
- if (!strncmp(name, XATTR_SYSTEM_PREFIX, XATTR_SYSTEM_PREFIX_LEN))
- return generic_getxattr(dentry, name, buffer, size);
- ret = btrfs_is_valid_xattr(name);
- if (ret)
- return ret;
- return __btrfs_getxattr(d_inode(dentry), name, buffer, size);
- }
- int btrfs_setxattr(struct dentry *dentry, const char *name, const void *value,
- size_t size, int flags)
- {
- struct btrfs_root *root = BTRFS_I(d_inode(dentry))->root;
- int ret;
- /*
- * The permission on security.* and system.* is not checked
- * in permission().
- */
- if (btrfs_root_readonly(root))
- return -EROFS;
- /*
- * If this is a request for a synthetic attribute in the system.*
- * namespace use the generic infrastructure to resolve a handler
- * for it via sb->s_xattr.
- */
- if (!strncmp(name, XATTR_SYSTEM_PREFIX, XATTR_SYSTEM_PREFIX_LEN))
- return generic_setxattr(dentry, name, value, size, flags);
- ret = btrfs_is_valid_xattr(name);
- if (ret)
- return ret;
- if (!strncmp(name, XATTR_BTRFS_PREFIX, XATTR_BTRFS_PREFIX_LEN))
- return btrfs_set_prop(d_inode(dentry), name,
- value, size, flags);
- if (size == 0)
- value = ""; /* empty EA, do not remove */
- return __btrfs_setxattr(NULL, d_inode(dentry), name, value, size,
- flags);
- }
- int btrfs_removexattr(struct dentry *dentry, const char *name)
- {
- struct btrfs_root *root = BTRFS_I(d_inode(dentry))->root;
- int ret;
- /*
- * The permission on security.* and system.* is not checked
- * in permission().
- */
- if (btrfs_root_readonly(root))
- return -EROFS;
- /*
- * If this is a request for a synthetic attribute in the system.*
- * namespace use the generic infrastructure to resolve a handler
- * for it via sb->s_xattr.
- */
- if (!strncmp(name, XATTR_SYSTEM_PREFIX, XATTR_SYSTEM_PREFIX_LEN))
- return generic_removexattr(dentry, name);
- ret = btrfs_is_valid_xattr(name);
- if (ret)
- return ret;
- if (!strncmp(name, XATTR_BTRFS_PREFIX, XATTR_BTRFS_PREFIX_LEN))
- return btrfs_set_prop(d_inode(dentry), name,
- NULL, 0, XATTR_REPLACE);
- return __btrfs_setxattr(NULL, d_inode(dentry), name, NULL, 0,
- XATTR_REPLACE);
- }
- static int btrfs_initxattrs(struct inode *inode,
- const struct xattr *xattr_array, void *fs_info)
- {
- const struct xattr *xattr;
- struct btrfs_trans_handle *trans = fs_info;
- char *name;
- int err = 0;
- for (xattr = xattr_array; xattr->name != NULL; xattr++) {
- name = kmalloc(XATTR_SECURITY_PREFIX_LEN +
- strlen(xattr->name) + 1, GFP_NOFS);
- if (!name) {
- err = -ENOMEM;
- break;
- }
- strcpy(name, XATTR_SECURITY_PREFIX);
- strcpy(name + XATTR_SECURITY_PREFIX_LEN, xattr->name);
- err = __btrfs_setxattr(trans, inode, name,
- xattr->value, xattr->value_len, 0);
- kfree(name);
- if (err < 0)
- break;
- }
- return err;
- }
- int btrfs_xattr_security_init(struct btrfs_trans_handle *trans,
- struct inode *inode, struct inode *dir,
- const struct qstr *qstr)
- {
- return security_inode_init_security(inode, dir, qstr,
- &btrfs_initxattrs, trans);
- }
|