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; 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; /* first lets see if we already have this xattr */ di = btrfs_lookup_xattr(trans, root, path, btrfs_ino(inode), name, strlen(name), -1); if (IS_ERR(di)) { ret = PTR_ERR(di); goto out; } /* ok we already have this xattr, lets remove it */ if (di) { /* if we want create only exit */ if (flags & XATTR_CREATE) { ret = -EEXIST; goto out; } ret = btrfs_delete_one_dir_name(trans, root, path, di); BUG_ON(ret); btrfs_release_path(path); /* if we don't have a value then we are removing the xattr */ if (!value) goto out; } else { btrfs_release_path(path); if (flags & XATTR_REPLACE) { /* we couldn't find the attr to replace */ ret = -ENODATA; goto out; } } /* ok we have to create a completely new xattr */ ret = btrfs_insert_xattr_item(trans, root, path, btrfs_ino(inode), name, name_len, value, size); BUG_ON(ret); 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; 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; if (flags & XATTR_REPLACE) { di = btrfs_lookup_xattr(trans, root, path, btrfs_ino(inode), name, name_len, -1); if (IS_ERR(di)) { ret = PTR_ERR(di); goto out; } else if (!di) { ret = -ENODATA; goto out; } ret = btrfs_delete_one_dir_name(trans, root, path, di); if (ret) goto out; btrfs_release_path(path); /* * remove the attribute */ if (!value) goto out; } again: ret = btrfs_insert_xattr_item(trans, root, path, btrfs_ino(inode), name, name_len, value, size); if (ret == -EEXIST) { if (flags & XATTR_CREATE) goto out; /* * We can't use the path we already have since we won't have the * proper locking for a delete, so release the path and * re-lookup to delete the thing. */ btrfs_release_path(path); di = btrfs_lookup_xattr(trans, root, path, btrfs_ino(inode), name, name_len, -1); if (IS_ERR(di)) { ret = PTR_ERR(di); goto out; } else if (!di) { /* Shouldn't happen but just in case... */ btrfs_release_path(path); goto again; } ret = btrfs_delete_one_dir_name(trans, root, path, di); if (ret) goto out; /* * We have a value to set, so go back and try to insert it now. */ if (value) { btrfs_release_path(path); goto again; } } 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(inode_is_locked(inode)); 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. */ }
int __btrfs_setxattr(struct inode *inode, const char *name, const void *value, size_t size, int flags) { struct btrfs_dir_item *di; struct btrfs_root *root = BTRFS_I(inode)->root; struct btrfs_trans_handle *trans; struct btrfs_path *path; int ret = 0, mod = 0; path = btrfs_alloc_path(); if (!path) return -ENOMEM; trans = btrfs_join_transaction(root, 1); btrfs_set_trans_block_group(trans, inode); /* first lets see if we already have this xattr */ di = btrfs_lookup_xattr(trans, root, path, inode->i_ino, name, strlen(name), -1); if (IS_ERR(di)) { ret = PTR_ERR(di); goto out; } /* ok we already have this xattr, lets remove it */ if (di) { /* if we want create only exit */ if (flags & XATTR_CREATE) { ret = -EEXIST; goto out; } ret = btrfs_delete_one_dir_name(trans, root, path, di); if (ret) goto out; btrfs_release_path(root, path); /* if we don't have a value then we are removing the xattr */ if (!value) { mod = 1; goto out; } } else { btrfs_release_path(root, path); if (flags & XATTR_REPLACE) { /* we couldn't find the attr to replace */ ret = -ENODATA; goto out; } } /* ok we have to create a completely new xattr */ ret = btrfs_insert_xattr_item(trans, root, name, strlen(name), value, size, inode->i_ino); if (ret) goto out; mod = 1; out: if (mod) { inode->i_ctime = CURRENT_TIME; ret = btrfs_update_inode(trans, root, inode); } btrfs_end_transaction(trans, root); btrfs_free_path(path); return ret; }
/* * Unlink an inode, which will remove its backref and corresponding dir_index/ * dir_item if any of them exists. * * If an inode's nlink is reduced to 0 and 'add_orphan' is true, it will be * added to orphan inode and waiting to be deleted by next kernel mount. */ int btrfs_unlink(struct btrfs_trans_handle *trans, struct btrfs_root *root, u64 ino, u64 parent_ino, u64 index, const char *name, int namelen, int add_orphan) { struct btrfs_path *path; struct btrfs_key key; struct btrfs_inode_item *inode_item; struct btrfs_inode_ref *inode_ref; struct btrfs_dir_item *dir_item; u64 inode_size; u32 nlinks; int del_inode_ref = 0; int del_dir_item = 0; int del_dir_index = 0; int ret = 0; path = btrfs_alloc_path(); if (!path) return -ENOMEM; /* check the ref and backref exists */ inode_ref = btrfs_lookup_inode_ref(trans, root, path, name, namelen, ino, parent_ino, index, 0); if (IS_ERR(inode_ref)) { ret = PTR_ERR(inode_ref); goto out; } if (inode_ref) del_inode_ref = 1; btrfs_release_path(path); dir_item = btrfs_lookup_dir_item(NULL, root, path, parent_ino, name, namelen, 0); if (IS_ERR(dir_item)) { ret = PTR_ERR(dir_item); goto out; } if (dir_item) del_dir_item = 1; btrfs_release_path(path); dir_item = btrfs_lookup_dir_index(NULL, root, path, parent_ino, name, namelen, index, 0); /* * Since lookup_dir_index() will return -ENOENT when not found, * we need to do extra check. */ if (IS_ERR(dir_item) && PTR_ERR(dir_item) == -ENOENT) dir_item = NULL; if (IS_ERR(dir_item)) { ret = PTR_ERR(dir_item); goto out; } if (dir_item) del_dir_index = 1; btrfs_release_path(path); if (!del_inode_ref && !del_dir_item && !del_dir_index) { /* All not found, shouldn't happen */ ret = -ENOENT; goto out; } if (del_inode_ref) { /* Only decrease nlink when deleting inode_ref */ key.objectid = ino; key.type = BTRFS_INODE_ITEM_KEY; key.offset = 0; ret = btrfs_search_slot(trans, root, &key, path, -1, 1); if (ret) { if (ret > 0) ret = -ENOENT; goto out; } inode_item = btrfs_item_ptr(path->nodes[0], path->slots[0], struct btrfs_inode_item); nlinks = btrfs_inode_nlink(path->nodes[0], inode_item); if (nlinks > 0) nlinks--; btrfs_set_inode_nlink(path->nodes[0], inode_item, nlinks); btrfs_mark_buffer_dirty(path->nodes[0]); btrfs_release_path(path); /* For nlinks == 0, add it to orphan list if needed */ if (nlinks == 0 && add_orphan) { ret = btrfs_add_orphan_item(trans, root, path, ino); if (ret < 0) goto out; btrfs_mark_buffer_dirty(path->nodes[0]); btrfs_release_path(path); } ret = btrfs_del_inode_ref(trans, root, name, namelen, ino, parent_ino, &index); if (ret < 0) goto out; } if (del_dir_index) { dir_item = btrfs_lookup_dir_index(trans, root, path, parent_ino, name, namelen, index, -1); if (IS_ERR(dir_item)) { ret = PTR_ERR(dir_item); goto out; } if (!dir_item) { ret = -ENOENT; goto out; } ret = btrfs_delete_one_dir_name(trans, root, path, dir_item); if (ret) goto out; btrfs_release_path(path); /* Update inode size of the parent inode */ key.objectid = parent_ino; key.type = BTRFS_INODE_ITEM_KEY; key.offset = 0; ret = btrfs_search_slot(trans, root, &key, path, 1, 1); if (ret) goto out; inode_item = btrfs_item_ptr(path->nodes[0], path->slots[0], struct btrfs_inode_item); inode_size = btrfs_inode_size(path->nodes[0], inode_item); if (inode_size >= namelen) inode_size -= namelen; btrfs_set_inode_size(path->nodes[0], inode_item, inode_size); btrfs_mark_buffer_dirty(path->nodes[0]); btrfs_release_path(path); } if (del_dir_item) { dir_item = btrfs_lookup_dir_item(trans, root, path, parent_ino, name, namelen, -1); if (IS_ERR(dir_item)) { ret = PTR_ERR(dir_item); goto out; } if (!dir_item) { ret = -ENOENT; goto out; } ret = btrfs_delete_one_dir_name(trans, root, path, dir_item); if (ret < 0) goto out; btrfs_release_path(path); /* Update inode size of the parent inode */ key.objectid = parent_ino; key.type = BTRFS_INODE_ITEM_KEY; key.offset = 0; ret = btrfs_search_slot(trans, root, &key, path, 1, 1); if (ret) goto out; inode_item = btrfs_item_ptr(path->nodes[0], path->slots[0], struct btrfs_inode_item); inode_size = btrfs_inode_size(path->nodes[0], inode_item); if (inode_size >= namelen) inode_size -= namelen; btrfs_set_inode_size(path->nodes[0], inode_item, inode_size); btrfs_mark_buffer_dirty(path->nodes[0]); btrfs_release_path(path); } 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; 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; if (flags & XATTR_REPLACE) { di = btrfs_lookup_xattr(trans, root, path, btrfs_ino(inode), name, name_len, -1); if (IS_ERR(di)) { ret = PTR_ERR(di); goto out; } else if (!di) { ret = -ENODATA; goto out; } ret = btrfs_delete_one_dir_name(trans, root, path, di); if (ret) goto out; btrfs_release_path(path); /* */ if (!value) goto out; } again: ret = btrfs_insert_xattr_item(trans, root, path, btrfs_ino(inode), name, name_len, value, size); /* */ if (ret == -EOVERFLOW) ret = -EEXIST; if (ret == -EEXIST) { if (flags & XATTR_CREATE) goto out; /* */ btrfs_release_path(path); di = btrfs_lookup_xattr(trans, root, path, btrfs_ino(inode), name, name_len, -1); if (IS_ERR(di)) { ret = PTR_ERR(di); goto out; } else if (!di) { /* */ btrfs_release_path(path); goto again; } ret = btrfs_delete_one_dir_name(trans, root, path, di); if (ret) goto out; /* */ if (value) { btrfs_release_path(path); goto again; } } 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; 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; if (flags & XATTR_REPLACE) { di = btrfs_lookup_xattr(trans, root, path, btrfs_ino(inode), name, name_len, -1); if (IS_ERR(di)) { ret = PTR_ERR(di); goto out; } else if (!di) { ret = -ENODATA; goto out; } ret = btrfs_delete_one_dir_name(trans, root, path, di); if (ret) goto out; btrfs_release_path(path); /* * remove the attribute */ if (!value) goto out; } else { di = btrfs_lookup_xattr(NULL, root, path, btrfs_ino(inode), name, name_len, 0); if (IS_ERR(di)) { ret = PTR_ERR(di); goto out; } if (!di && !value) goto out; btrfs_release_path(path); } again: ret = btrfs_insert_xattr_item(trans, root, path, btrfs_ino(inode), name, name_len, value, size); /* * If we're setting an xattr to a new value but the new value is say * exactly BTRFS_MAX_XATTR_SIZE, we could end up with EOVERFLOW getting * back from split_leaf. This is because it thinks we'll be extending * the existing item size, but we're asking for enough space to add the * item itself. So if we get EOVERFLOW just set ret to EEXIST and let * the rest of the function figure it out. */ if (ret == -EOVERFLOW) ret = -EEXIST; if (ret == -EEXIST) { if (flags & XATTR_CREATE) goto out; /* * We can't use the path we already have since we won't have the * proper locking for a delete, so release the path and * re-lookup to delete the thing. */ btrfs_release_path(path); di = btrfs_lookup_xattr(trans, root, path, btrfs_ino(inode), name, name_len, -1); if (IS_ERR(di)) { ret = PTR_ERR(di); goto out; } else if (!di) { /* Shouldn't happen but just in case... */ btrfs_release_path(path); goto again; } ret = btrfs_delete_one_dir_name(trans, root, path, di); if (ret) goto out; /* * We have a value to set, so go back and try to insert it now. */ if (value) { btrfs_release_path(path); goto again; } } out: btrfs_free_path(path); return ret; }