diff options
-rw-r--r-- | fs/bcachefs/acl.c | 75 | ||||
-rw-r--r-- | fs/bcachefs/acl.h | 22 | ||||
-rw-r--r-- | fs/bcachefs/dirent.c | 289 | ||||
-rw-r--r-- | fs/bcachefs/dirent.h | 12 | ||||
-rw-r--r-- | fs/bcachefs/fsck.c | 35 | ||||
-rw-r--r-- | fs/bcachefs/inode.c | 107 | ||||
-rw-r--r-- | fs/bcachefs/inode.h | 5 | ||||
-rw-r--r-- | fs/bcachefs/str_hash.h | 317 | ||||
-rw-r--r-- | fs/bcachefs/xattr.c | 81 | ||||
-rw-r--r-- | fs/bcachefs/xattr.h | 9 |
10 files changed, 474 insertions, 478 deletions
diff --git a/fs/bcachefs/acl.c b/fs/bcachefs/acl.c index a8735bc04b4d..e98adb07649d 100644 --- a/fs/bcachefs/acl.c +++ b/fs/bcachefs/acl.c @@ -132,7 +132,8 @@ invalid: * Convert from in-memory to filesystem representation. */ static struct bkey_i_xattr * -bch2_acl_to_xattr(const struct posix_acl *acl, +bch2_acl_to_xattr(struct btree_trans *trans, + const struct posix_acl *acl, int type) { struct bkey_i_xattr *xattr; @@ -164,7 +165,7 @@ bch2_acl_to_xattr(const struct posix_acl *acl, if (u64s > U8_MAX) return ERR_PTR(-E2BIG); - xattr = kmalloc(u64s * sizeof(u64), GFP_KERNEL); + xattr = bch2_trans_kmalloc(trans, u64s * sizeof(u64)); if (IS_ERR(xattr)) return xattr; @@ -214,20 +215,29 @@ struct posix_acl *bch2_get_acl(struct inode *vinode, int type) { struct bch_inode_info *inode = to_bch_ei(vinode); struct bch_fs *c = inode->v.i_sb->s_fs_info; - struct btree_iter iter; + struct btree_trans trans; + struct btree_iter *iter; struct bkey_s_c_xattr xattr; - struct bkey_s_c k; struct posix_acl *acl = NULL; - int name_index = acl_to_xattr_type(type); - k = bch2_xattr_get_iter(c, &iter, inode, "", name_index); - if (IS_ERR(k.k)) { - if (PTR_ERR(k.k) != -ENOENT) - acl = ERR_CAST(k.k); + bch2_trans_init(&trans, c); +retry: + bch2_trans_begin(&trans); + + iter = bch2_hash_lookup(&trans, bch2_xattr_hash_desc, + &inode->ei_str_hash, inode->v.i_ino, + &X_SEARCH(acl_to_xattr_type(type), "", 0), + 0); + if (IS_ERR(iter)) { + if (PTR_ERR(iter) == -EINTR) + goto retry; + + if (PTR_ERR(iter) != -ENOENT) + acl = ERR_CAST(iter); goto out; } - xattr = bkey_s_c_to_xattr(k); + xattr = bkey_s_c_to_xattr(bch2_btree_iter_peek_slot(iter)); acl = bch2_acl_from_disk(xattr_val(xattr.v), le16_to_cpu(xattr.v->x_val_len)); @@ -235,43 +245,56 @@ struct posix_acl *bch2_get_acl(struct inode *vinode, int type) if (!IS_ERR(acl)) set_cached_acl(&inode->v, type, acl); out: - bch2_btree_iter_unlock(&iter); + bch2_trans_exit(&trans); return acl; } -int __bch2_set_acl(struct inode *vinode, struct posix_acl *acl, int type) +int bch2_set_acl_trans(struct btree_trans *trans, + struct bch_inode_unpacked *inode_u, + const struct bch_hash_info *hash_info, + struct posix_acl *acl, int type) { - struct bch_inode_info *inode = to_bch_ei(vinode); - struct bch_fs *c = inode->v.i_sb->s_fs_info; int ret; if (type == ACL_TYPE_DEFAULT && - !S_ISDIR(inode->v.i_mode)) + !S_ISDIR(inode_u->bi_mode)) return acl ? -EACCES : 0; if (acl) { struct bkey_i_xattr *xattr = - bch2_acl_to_xattr(acl, type); + bch2_acl_to_xattr(trans, acl, type); if (IS_ERR(xattr)) return PTR_ERR(xattr); - ret = bch2_hash_set(bch2_xattr_hash_desc, &inode->ei_str_hash, - c, inode->v.i_ino, &inode->ei_journal_seq, - &xattr->k_i, 0); - kfree(xattr); + ret = __bch2_hash_set(trans, bch2_xattr_hash_desc, hash_info, + inode_u->bi_inum, &xattr->k_i, 0); } else { struct xattr_search_key search = X_SEARCH(acl_to_xattr_type(type), "", 0); - ret = bch2_hash_delete(bch2_xattr_hash_desc, &inode->ei_str_hash, - c, inode->v.i_ino, &inode->ei_journal_seq, - &search); + ret = bch2_hash_delete(trans, bch2_xattr_hash_desc, hash_info, + inode_u->bi_inum, &search); } - if (!ret) - set_cached_acl(&inode->v, type, acl); + return ret == -ENOENT ? 0 : ret; +} - return ret; +int __bch2_set_acl(struct inode *vinode, struct posix_acl *acl, int type) +{ + struct bch_inode_info *inode = to_bch_ei(vinode); + struct bch_fs *c = inode->v.i_sb->s_fs_info; + int ret; + + ret = bch2_trans_do(c, &inode->ei_journal_seq, BTREE_INSERT_ATOMIC, + bch2_set_acl_trans(&trans, + &inode->ei_inode, + &inode->ei_str_hash, + acl, type)); + if (ret) + return ret; + + set_cached_acl(&inode->v, type, acl); + return 0; } int bch2_set_acl(struct inode *vinode, struct posix_acl *acl, int type) diff --git a/fs/bcachefs/acl.h b/fs/bcachefs/acl.h index 0be31ee9e59d..2e5ab78c779a 100644 --- a/fs/bcachefs/acl.h +++ b/fs/bcachefs/acl.h @@ -1,6 +1,10 @@ #ifndef _BCACHEFS_ACL_H #define _BCACHEFS_ACL_H +struct bch_inode_unpacked; +struct bch_hash_info; +struct posix_acl; + #ifdef CONFIG_BCACHEFS_POSIX_ACL #define BCH_ACL_VERSION 0x0001 @@ -20,20 +24,26 @@ typedef struct { __le32 a_version; } bch_acl_header; -struct posix_acl; +struct posix_acl *bch2_get_acl(struct inode *, int); -extern struct posix_acl *bch2_get_acl(struct inode *, int); -extern int __bch2_set_acl(struct inode *, struct posix_acl *, int); -extern int bch2_set_acl(struct inode *, struct posix_acl *, int); +int bch2_set_acl_trans(struct btree_trans *, + struct bch_inode_unpacked *, + const struct bch_hash_info *, + struct posix_acl *, int); +int __bch2_set_acl(struct inode *, struct posix_acl *, int); +int bch2_set_acl(struct inode *, struct posix_acl *, int); #else -static inline int __bch2_set_acl(struct inode *inode, struct posix_acl *acl, int type) +static inline int bch2_set_acl_trans(struct btree_trans *trans, + struct bch_inode_unpacked *inode_u, + const struct bch_hash_info *hash_info, + struct posix_acl *acl, int type) { return 0; } -static inline int bch2_set_acl(struct inode *inode, struct posix_acl *acl, int type) +static inline int __bch2_set_acl(struct inode *inode, struct posix_acl *acl, int type) { return 0; } diff --git a/fs/bcachefs/dirent.c b/fs/bcachefs/dirent.c index d3dd3eb71837..17fbce473995 100644 --- a/fs/bcachefs/dirent.c +++ b/fs/bcachefs/dirent.c @@ -141,8 +141,8 @@ void bch2_dirent_to_text(struct bch_fs *c, char *buf, } } -static struct bkey_i_dirent *dirent_create_key(u8 type, - const struct qstr *name, u64 dst) +static struct bkey_i_dirent *dirent_create_key(struct btree_trans *trans, + u8 type, const struct qstr *name, u64 dst) { struct bkey_i_dirent *dirent; unsigned u64s = BKEY_U64s + dirent_val_u64s(name->len); @@ -152,9 +152,9 @@ static struct bkey_i_dirent *dirent_create_key(u8 type, BUG_ON(u64s > U8_MAX); - dirent = kmalloc(u64s * sizeof(u64), GFP_NOFS); - if (!dirent) - return ERR_PTR(-ENOMEM); + dirent = bch2_trans_kmalloc(trans, u64s * sizeof(u64)); + if (IS_ERR(dirent)) + return dirent; bkey_dirent_init(&dirent->k_i); dirent->k.u64s = u64s; @@ -172,23 +172,31 @@ static struct bkey_i_dirent *dirent_create_key(u8 type, return dirent; } -int bch2_dirent_create(struct bch_fs *c, u64 dir_inum, - const struct bch_hash_info *hash_info, - u8 type, const struct qstr *name, u64 dst_inum, - u64 *journal_seq, int flags) +int __bch2_dirent_create(struct btree_trans *trans, + u64 dir_inum, const struct bch_hash_info *hash_info, + u8 type, const struct qstr *name, u64 dst_inum, + int flags) { struct bkey_i_dirent *dirent; int ret; - dirent = dirent_create_key(type, name, dst_inum); - if (IS_ERR(dirent)) - return PTR_ERR(dirent); + dirent = dirent_create_key(trans, type, name, dst_inum); + ret = PTR_ERR_OR_ZERO(dirent); + if (ret) + return ret; - ret = bch2_hash_set(bch2_dirent_hash_desc, hash_info, c, dir_inum, - journal_seq, &dirent->k_i, flags); - kfree(dirent); + return __bch2_hash_set(trans, bch2_dirent_hash_desc, hash_info, + dir_inum, &dirent->k_i, flags); +} - return ret; +int bch2_dirent_create(struct bch_fs *c, u64 dir_inum, + const struct bch_hash_info *hash_info, + u8 type, const struct qstr *name, u64 dst_inum, + u64 *journal_seq, int flags) +{ + return bch2_trans_do(c, journal_seq, flags, + __bch2_dirent_create(&trans, dir_inum, hash_info, + type, name, dst_inum, flags)); } static void dirent_copy_target(struct bkey_i_dirent *dst, @@ -204,151 +212,129 @@ static struct bpos bch2_dirent_pos(struct bch_inode_info *inode, return POS(inode->v.i_ino, bch2_dirent_hash(&inode->ei_str_hash, name)); } -int bch2_dirent_rename(struct bch_fs *c, +int __bch2_dirent_rename(struct btree_trans *trans, struct bch_inode_info *src_dir, const struct qstr *src_name, struct bch_inode_info *dst_dir, const struct qstr *dst_name, - u64 *journal_seq, enum bch_rename_mode mode) + enum bch_rename_mode mode) { - struct btree_iter src_iter, dst_iter, whiteout_iter; + struct btree_iter *src_iter, *dst_iter; struct bkey_s_c old_src, old_dst; - struct bkey delete; struct bkey_i_dirent *new_src = NULL, *new_dst = NULL; - struct bpos src_pos = bch2_dirent_pos(src_dir, src_name); struct bpos dst_pos = bch2_dirent_pos(dst_dir, dst_name); - bool need_whiteout; int ret; - bch2_btree_iter_init(&src_iter, c, BTREE_ID_DIRENTS, src_pos, - BTREE_ITER_SLOTS|BTREE_ITER_INTENT); - bch2_btree_iter_init(&dst_iter, c, BTREE_ID_DIRENTS, dst_pos, - BTREE_ITER_SLOTS|BTREE_ITER_INTENT); - bch2_btree_iter_link(&src_iter, &dst_iter); - - bch2_btree_iter_init(&whiteout_iter, c, BTREE_ID_DIRENTS, src_pos, - BTREE_ITER_SLOTS); - bch2_btree_iter_link(&src_iter, &whiteout_iter); - - if (mode == BCH_RENAME_EXCHANGE) { - new_src = dirent_create_key(0, src_name, 0); - if (IS_ERR(new_src)) { - ret = PTR_ERR(new_src); - goto err; - } - } else { - new_src = (void *) &delete; - } - - new_dst = dirent_create_key(0, dst_name, 0); - if (IS_ERR(new_dst)) { - ret = PTR_ERR(new_dst); - goto err; - } -retry: - /* - * Note that on -EINTR/dropped locks we're not restarting the lookup - * from the original hashed position (like we do when creating dirents, - * in bch_hash_set) - we never move existing dirents to different slot: - */ - old_src = bch2_hash_lookup_at(bch2_dirent_hash_desc, - &src_dir->ei_str_hash, - &src_iter, src_name); - if ((ret = btree_iter_err(old_src))) - goto err; - - ret = bch2_hash_needs_whiteout(bch2_dirent_hash_desc, - &src_dir->ei_str_hash, - &whiteout_iter, &src_iter); - if (ret < 0) - goto err; - need_whiteout = ret; - /* + * Lookup dst: + * * Note that in BCH_RENAME mode, we're _not_ checking if * the target already exists - we're relying on the VFS * to do that check for us for correctness: */ - old_dst = mode == BCH_RENAME - ? bch2_hash_hole_at(bch2_dirent_hash_desc, &dst_iter) - : bch2_hash_lookup_at(bch2_dirent_hash_desc, - &dst_dir->ei_str_hash, - &dst_iter, dst_name); - if ((ret = btree_iter_err(old_dst))) - goto err; - - switch (mode) { - case BCH_RENAME: + dst_iter = mode == BCH_RENAME + ? bch2_hash_hole(trans, bch2_dirent_hash_desc, + &dst_dir->ei_str_hash, + dst_dir->v.i_ino, dst_name) + : bch2_hash_lookup(trans, bch2_dirent_hash_desc, + &dst_dir->ei_str_hash, + dst_dir->v.i_ino, dst_name, + BTREE_ITER_INTENT); + if (IS_ERR(dst_iter)) + return PTR_ERR(dst_iter); + old_dst = bch2_btree_iter_peek_slot(dst_iter); + + /* Lookup src: */ + src_iter = bch2_hash_lookup(trans, bch2_dirent_hash_desc, + &src_dir->ei_str_hash, + src_dir->v.i_ino, src_name, + BTREE_ITER_INTENT); + if (IS_ERR(src_iter)) + return PTR_ERR(src_iter); + old_src = bch2_btree_iter_peek_slot(src_iter); + + /* Create new dst key: */ + new_dst = dirent_create_key(trans, 0, dst_name, 0); + if (IS_ERR(new_dst)) + return PTR_ERR(new_dst); + + dirent_copy_target(new_dst, bkey_s_c_to_dirent(old_src)); + new_dst->k.p = dst_iter->pos; + + /* Create new src key: */ + if (mode == BCH_RENAME_EXCHANGE) { + new_src = dirent_create_key(trans, 0, src_name, 0); + if (IS_ERR(new_src)) + return PTR_ERR(new_src); + + dirent_copy_target(new_src, bkey_s_c_to_dirent(old_dst)); + new_src->k.p = src_iter->pos; + } else { + new_src = bch2_trans_kmalloc(trans, sizeof(struct bkey_i)); + if (IS_ERR(new_src)) + return PTR_ERR(new_src); bkey_init(&new_src->k); - dirent_copy_target(new_dst, bkey_s_c_to_dirent(old_src)); + new_src->k.p = src_iter->pos; - if (bkey_cmp(dst_pos, src_iter.pos) <= 0 && - bkey_cmp(src_iter.pos, dst_iter.pos) < 0) { + if (bkey_cmp(dst_pos, src_iter->pos) <= 0 && + bkey_cmp(src_iter->pos, dst_iter->pos) < 0) { /* - * If we couldn't insert new_dst at its hashed - * position (dst_pos) due to a hash collision, - * and we're going to be deleting in - * between the hashed position and first empty - * slot we found - just overwrite the pos we - * were going to delete: - * - * Note: this is a correctness issue, in this - * situation bch2_hash_needs_whiteout() could - * return false when the whiteout would have - * been needed if we inserted at the pos - * __dirent_find_hole() found + * We have a hash collision for the new dst key, + * and new_src - the key we're deleting - is between + * new_dst's hashed slot and the slot we're going to be + * inserting it into - oops. This will break the hash + * table if we don't deal with it: */ - new_dst->k.p = src_iter.pos; - ret = bch2_btree_insert_at(c, NULL, NULL, - journal_seq, - BTREE_INSERT_ATOMIC, - BTREE_INSERT_ENTRY(&src_iter, - &new_dst->k_i)); - goto err; + if (mode == BCH_RENAME) { + /* + * If we're not overwriting, we can just insert + * new_dst at the src position: + */ + new_dst->k.p = src_iter->pos; + bch2_trans_update(trans, src_iter, &new_dst->k_i, 0); + return 0; + } else { + /* If we're overwriting, we can't insert new_dst + * at a different slot because it has to + * overwrite old_dst - just make sure to use a + * whiteout when deleting src: + */ + new_src->k.type = BCH_DIRENT_WHITEOUT; + } + } else { + /* Check if we need a whiteout to delete src: */ + ret = bch2_hash_needs_whiteout(trans, bch2_dirent_hash_desc, + &src_dir->ei_str_hash, + src_iter); + if (ret < 0) + return ret; + + if (ret) + new_src->k.type = BCH_DIRENT_WHITEOUT; } + } - if (need_whiteout) - new_src->k.type = BCH_DIRENT_WHITEOUT; - break; - case BCH_RENAME_OVERWRITE: - bkey_init(&new_src->k); - dirent_copy_target(new_dst, bkey_s_c_to_dirent(old_src)); + bch2_trans_update(trans, src_iter, &new_src->k_i, 0); + bch2_trans_update(trans, dst_iter, &new_dst->k_i, 0); + return 0; +} - if (bkey_cmp(dst_pos, src_iter.pos) <= 0 && - bkey_cmp(src_iter.pos, dst_iter.pos) < 0) { - /* - * Same case described above - - * bch_hash_needs_whiteout could spuriously - * return false, but we have to insert at - * dst_iter.pos because we're overwriting - * another dirent: - */ - new_src->k.type = BCH_DIRENT_WHITEOUT; - } else if (need_whiteout) - new_src->k.type = BCH_DIRENT_WHITEOUT; - break; - case BCH_RENAME_EXCHANGE: - dirent_copy_target(new_src, bkey_s_c_to_dirent(old_dst)); - dirent_copy_target(new_dst, bkey_s_c_to_dirent(old_src)); - break; - } +int bch2_dirent_rename(struct bch_fs *c, + struct bch_inode_info *src_dir, const struct qstr *src_name, + struct bch_inode_info *dst_dir, const struct qstr *dst_name, + u64 *journal_seq, enum bch_rename_mode mode) +{ + return bch2_trans_do(c, journal_seq, BTREE_INSERT_ATOMIC, + __bch2_dirent_rename(&trans, + src_dir, src_name, + dst_dir, dst_name, + mode)); +} - new_src->k.p = src_iter.pos; - new_dst->k.p = dst_iter.pos; - ret = bch2_btree_insert_at(c, NULL, NULL, journal_seq, - BTREE_INSERT_ATOMIC, - BTREE_INSERT_ENTRY(&src_iter, &new_src->k_i), - BTREE_INSERT_ENTRY(&dst_iter, &new_dst->k_i)); -err: - if (ret == -EINTR) - goto retry; - - bch2_btree_iter_unlock(&whiteout_iter); - bch2_btree_iter_unlock(&dst_iter); - bch2_btree_iter_unlock(&src_iter); - - if (new_src != (void *) &delete) - kfree(new_src); - kfree(new_dst); - return ret; +int __bch2_dirent_delete(struct btree_trans *trans, u64 dir_inum, + const struct bch_hash_info *hash_info, + const struct qstr *name) +{ + return bch2_hash_delete(trans, bch2_dirent_hash_desc, hash_info, + dir_inum, name); } int bch2_dirent_delete(struct bch_fs *c, u64 dir_inum, @@ -356,27 +342,34 @@ int bch2_dirent_delete(struct bch_fs *c, u64 dir_inum, const struct qstr *name, u64 *journal_seq) { - return bch2_hash_delete(bch2_dirent_hash_desc, hash_info, - c, dir_inum, journal_seq, name); + return bch2_trans_do(c, journal_seq, + BTREE_INSERT_ATOMIC| + BTREE_INSERT_NOFAIL, + __bch2_dirent_delete(&trans, dir_inum, hash_info, name)); } u64 bch2_dirent_lookup(struct bch_fs *c, u64 dir_inum, const struct bch_hash_info *hash_info, const struct qstr *name) { - struct btree_iter iter; + struct btree_trans trans; + struct btree_iter *iter; struct bkey_s_c k; u64 inum; - k = bch2_hash_lookup(bch2_dirent_hash_desc, hash_info, c, - dir_inum, &iter, name); - if (IS_ERR(k.k)) { - bch2_btree_iter_unlock(&iter); + bch2_trans_init(&trans, c); + + iter = bch2_hash_lookup(&trans, bch2_dirent_hash_desc, + hash_info, dir_inum, name, 0); + if (IS_ERR(iter)) { + BUG_ON(PTR_ERR(iter) == -EINTR); + bch2_trans_exit(&trans); return 0; } + k = bch2_btree_iter_peek_slot(iter); inum = le64_to_cpu(bkey_s_c_to_dirent(k).v->d_inum); - bch2_btree_iter_unlock(&iter); + bch2_trans_exit(&trans); return inum; } diff --git a/fs/bcachefs/dirent.h b/fs/bcachefs/dirent.h index 5d066af18f95..2578bd887c73 100644 --- a/fs/bcachefs/dirent.h +++ b/fs/bcachefs/dirent.h @@ -21,8 +21,16 @@ struct bch_hash_info; struct bch_inode_info; unsigned bch2_dirent_name_bytes(struct bkey_s_c_dirent); + +int __bch2_dirent_create(struct btree_trans *, u64, + const struct bch_hash_info *, u8, + const struct qstr *, u64, int); int bch2_dirent_create(struct bch_fs *c, u64, const struct bch_hash_info *, u8, const struct qstr *, u64, u64 *, int); + +int __bch2_dirent_delete(struct btree_trans *, u64, + const struct bch_hash_info *, + const struct qstr *); int bch2_dirent_delete(struct bch_fs *, u64, const struct bch_hash_info *, const struct qstr *, u64 *); @@ -32,6 +40,10 @@ enum bch_rename_mode { BCH_RENAME_EXCHANGE, }; +int __bch2_dirent_rename(struct btree_trans *, + struct bch_inode_info *, const struct qstr *, + struct bch_inode_info *, const struct qstr *, + enum bch_rename_mode); int bch2_dirent_rename(struct bch_fs *, struct bch_inode_info *, const struct qstr *, struct bch_inode_info *, const struct qstr *, diff --git a/fs/bcachefs/fsck.c b/fs/bcachefs/fsck.c index 3bbb0a2e8ac5..687247c789b1 100644 --- a/fs/bcachefs/fsck.c +++ b/fs/bcachefs/fsck.c @@ -173,6 +173,39 @@ err: return ret; } +/* fsck hasn't been converted to new transactions yet: */ +static int fsck_hash_delete_at(const struct bch_hash_desc desc, + struct bch_hash_info *info, + struct btree_iter *orig_iter) +{ + struct btree_trans trans; + struct btree_iter *iter; + int ret; + + bch2_btree_iter_unlock(orig_iter); + + bch2_trans_init(&trans, orig_iter->c); +retry: + bch2_trans_begin(&trans); + + iter = bch2_trans_copy_iter(&trans, orig_iter); + if (IS_ERR(iter)) { + ret = PTR_ERR(iter); + goto err; + } + + ret = bch2_hash_delete_at(&trans, desc, info, iter) ?: + bch2_trans_commit(&trans, NULL, NULL, NULL, + BTREE_INSERT_ATOMIC| + BTREE_INSERT_NOFAIL); +err: + if (ret == -EINTR) + goto retry; + + bch2_trans_exit(&trans); + return ret; +} + static int hash_check_key(const struct bch_hash_desc desc, struct hash_check *h, struct bch_fs *c, struct btree_iter *k_iter, struct bkey_s_c k) @@ -226,7 +259,7 @@ static int hash_check_key(const struct bch_hash_desc desc, "duplicate hash table keys:\n%s", (bch2_bkey_val_to_text(c, bkey_type(0, desc.btree_id), buf, sizeof(buf), k), buf))) { - ret = bch2_hash_delete_at(desc, &h->info, &h->iter, NULL); + ret = fsck_hash_delete_at(desc, &h->info, &h->iter); if (ret) return ret; return 1; diff --git a/fs/bcachefs/inode.c b/fs/bcachefs/inode.c index 6882fb0aa906..987ff1691c17 100644 --- a/fs/bcachefs/inode.c +++ b/fs/bcachefs/inode.c @@ -278,12 +278,27 @@ void bch2_inode_init(struct bch_fs *c, struct bch_inode_unpacked *inode_u, } } -int bch2_inode_create(struct bch_fs *c, struct bch_inode_unpacked *inode_u, - u64 min, u64 max, u64 *hint) +static inline u32 bkey_generation(struct bkey_s_c k) { - struct bkey_inode_buf inode_p; - struct btree_iter iter; - bool searched_from_start = false; + switch (k.k->type) { + case BCH_INODE_BLOCKDEV: + case BCH_INODE_FS: + BUG(); + case BCH_INODE_GENERATION: + return le32_to_cpu(bkey_s_c_to_inode_generation(k).v->bi_generation); + default: + return 0; + } +} + +int __bch2_inode_create(struct btree_trans *trans, + struct bch_inode_unpacked *inode_u, + u64 min, u64 max, u64 *hint) +{ + struct bch_fs *c = trans->c; + struct bkey_inode_buf *inode_p; + struct btree_iter *iter; + u64 start; int ret; if (!max) @@ -292,82 +307,66 @@ int bch2_inode_create(struct bch_fs *c, struct bch_inode_unpacked *inode_u, if (c->opts.inodes_32bit) max = min_t(u64, max, U32_MAX); - if (*hint >= max || *hint < min) - *hint = min; + start = READ_ONCE(*hint); - if (*hint == min) - searched_from_start = true; -again: - bch2_btree_iter_init(&iter, c, BTREE_ID_INODES, POS(*hint, 0), - BTREE_ITER_SLOTS|BTREE_ITER_INTENT); + if (start >= max || start < min) + start = min; + + inode_p = bch2_trans_kmalloc(trans, sizeof(*inode_p)); + if (IS_ERR(inode_p)) + return PTR_ERR(inode_p); + iter = bch2_trans_get_iter(trans, + BTREE_ID_INODES, POS(start, 0), + BTREE_ITER_SLOTS|BTREE_ITER_INTENT); + if (IS_ERR(iter)) + return PTR_ERR(iter); +again: while (1) { - struct bkey_s_c k = bch2_btree_iter_peek_slot(&iter); - u32 bi_generation = 0; + struct bkey_s_c k = bch2_btree_iter_peek_slot(iter); ret = btree_iter_err(k); - if (ret) { - bch2_btree_iter_unlock(&iter); + if (ret) return ret; - } switch (k.k->type) { case BCH_INODE_BLOCKDEV: case BCH_INODE_FS: /* slot used */ - if (iter.pos.inode == max) + if (iter->pos.inode >= max) goto out; - bch2_btree_iter_next_slot(&iter); + bch2_btree_iter_next_slot(iter); break; - case BCH_INODE_GENERATION: { - struct bkey_s_c_inode_generation g = - bkey_s_c_to_inode_generation(k); - bi_generation = le32_to_cpu(g.v->bi_generation); - /* fallthrough: */ - } default: - inode_u->bi_generation = bi_generation; - - bch2_inode_pack(&inode_p, inode_u); - inode_p.inode.k.p = k.k->p; - - ret = bch2_btree_insert_at(c, NULL, NULL, NULL, - BTREE_INSERT_ATOMIC, - BTREE_INSERT_ENTRY(&iter, - &inode_p.inode.k_i)); - - if (ret != -EINTR) { - bch2_btree_iter_unlock(&iter); - - if (!ret) { - inode_u->bi_inum = - inode_p.inode.k.p.inode; - *hint = inode_p.inode.k.p.inode + 1; - } - - return ret; - } - - if (ret == -EINTR) - continue; + *hint = k.k->p.inode; + inode_u->bi_inum = k.k->p.inode; + inode_u->bi_generation = bkey_generation(k); + bch2_inode_pack(inode_p, inode_u); + bch2_trans_update(trans, iter, &inode_p->inode.k_i, 0); + return 0; } } out: - bch2_btree_iter_unlock(&iter); - - if (!searched_from_start) { + if (start != min) { /* Retry from start */ - *hint = min; - searched_from_start = true; + start = min; + bch2_btree_iter_set_pos(iter, POS(start, 0)); goto again; } return -ENOSPC; } +int bch2_inode_create(struct bch_fs *c, struct bch_inode_unpacked *inode_u, + u64 min, u64 max, u64 *hint) +{ + return bch2_trans_do(c, NULL, BTREE_INSERT_ATOMIC, + __bch2_inode_create(&trans, inode_u, min, max, hint)); +} + int bch2_inode_truncate(struct bch_fs *c, u64 inode_nr, u64 new_size, struct extent_insert_hook *hook, u64 *journal_seq) { diff --git a/fs/bcachefs/inode.h b/fs/bcachefs/inode.h index 055d0053c781..ca995194b2b3 100644 --- a/fs/bcachefs/inode.h +++ b/fs/bcachefs/inode.h @@ -38,8 +38,13 @@ int bch2_inode_unpack(struct bkey_s_c_inode, struct bch_inode_unpacked *); void bch2_inode_init(struct bch_fs *, struct bch_inode_unpacked *, uid_t, gid_t, umode_t, dev_t, struct bch_inode_unpacked *); + +int __bch2_inode_create(struct btree_trans *, + struct bch_inode_unpacked *, + u64, u64, u64 *); int bch2_inode_create(struct bch_fs *, struct bch_inode_unpacked *, u64, u64, u64 *); + int bch2_inode_truncate(struct bch_fs *, u64, u64, struct extent_insert_hook *, u64 *); int bch2_inode_rm(struct bch_fs *, u64); diff --git a/fs/bcachefs/str_hash.h b/fs/bcachefs/str_hash.h index 275ac8625b4a..058b280fe7d9 100644 --- a/fs/bcachefs/str_hash.h +++ b/fs/bcachefs/str_hash.h @@ -126,46 +126,29 @@ struct bch_hash_desc { bool (*cmp_bkey)(struct bkey_s_c, struct bkey_s_c); }; -static inline struct bkey_s_c -bch2_hash_lookup_at(const struct bch_hash_desc desc, - const struct bch_hash_info *info, - struct btree_iter *iter, const void *search) +static inline struct btree_iter * +bch2_hash_lookup(struct btree_trans *trans, + const struct bch_hash_desc desc, + const struct bch_hash_info *info, + u64 inode, const void *key, + unsigned flags) { - u64 inode = iter->pos.inode; + struct btree_iter *iter; struct bkey_s_c k; - for_each_btree_key_continue(iter, BTREE_ITER_SLOTS, k) { - if (iter->pos.inode != inode) - break; - - if (k.k->type == desc.key_type) { - if (!desc.cmp_key(k, search)) - return k; - } else if (k.k->type == desc.whiteout_type) { - ; - } else { - /* hole, not found */ - break; - } - } - return btree_iter_err(k) ? k : bkey_s_c_err(-ENOENT); -} - -static inline struct bkey_s_c -bch2_hash_lookup_bkey_at(const struct bch_hash_desc desc, - const struct bch_hash_info *info, - struct btree_iter *iter, struct bkey_s_c search) -{ - u64 inode = iter->pos.inode; - struct bkey_s_c k; + iter = bch2_trans_get_iter(trans, desc.btree_id, + POS(inode, desc.hash_key(info, key)), + BTREE_ITER_SLOTS|flags); + if (IS_ERR(iter)) + return iter; for_each_btree_key_continue(iter, BTREE_ITER_SLOTS, k) { if (iter->pos.inode != inode) break; if (k.k->type == desc.key_type) { - if (!desc.cmp_bkey(k, search)) - return k; + if (!desc.cmp_key(k, key)) + return iter; } else if (k.k->type == desc.whiteout_type) { ; } else { @@ -173,72 +156,48 @@ bch2_hash_lookup_bkey_at(const struct bch_hash_desc desc, break; } } - return btree_iter_err(k) ? k : bkey_s_c_err(-ENOENT); -} - -static inline struct bkey_s_c -bch2_hash_lookup(const struct bch_hash_desc desc, - const struct bch_hash_info *info, - struct bch_fs *c, u64 inode, - struct btree_iter *iter, const void *key) -{ - bch2_btree_iter_init(iter, c, desc.btree_id, - POS(inode, desc.hash_key(info, key)), - BTREE_ITER_SLOTS); - - return bch2_hash_lookup_at(desc, info, iter, key); -} - -static inline struct bkey_s_c -bch2_hash_lookup_intent(const struct bch_hash_desc desc, - const struct bch_hash_info *info, - struct bch_fs *c, u64 inode, - struct btree_iter *iter, const void *key) -{ - bch2_btree_iter_init(iter, c, desc.btree_id, - POS(inode, desc.hash_key(info, key)), - BTREE_ITER_SLOTS|BTREE_ITER_INTENT); - return bch2_hash_lookup_at(desc, info, iter, key); + return IS_ERR(k.k) ? ERR_CAST(k.k) : ERR_PTR(-ENOENT); } -static inline struct bkey_s_c -bch2_hash_hole_at(const struct bch_hash_desc desc, struct btree_iter *iter) +static inline struct btree_iter * +bch2_hash_hole(struct btree_trans *trans, + const struct bch_hash_desc desc, + const struct bch_hash_info *info, + u64 inode, const void *key) { - u64 inode = iter->pos.inode; + struct btree_iter *iter; struct bkey_s_c k; + iter = bch2_trans_get_iter(trans, desc.btree_id, + POS(inode, desc.hash_key(info, key)), + BTREE_ITER_SLOTS|BTREE_ITER_INTENT); + if (IS_ERR(iter)) + return iter; + for_each_btree_key_continue(iter, BTREE_ITER_SLOTS, k) { if (iter->pos.inode != inode) break; if (k.k->type != desc.key_type) - return k; + return iter; } - return btree_iter_err(k) ? k : bkey_s_c_err(-ENOENT); -} - -static inline struct bkey_s_c bch2_hash_hole(const struct bch_hash_desc desc, - const struct bch_hash_info *info, - struct bch_fs *c, u64 inode, - struct btree_iter *iter, - const void *key) -{ - bch2_btree_iter_init(iter, c, desc.btree_id, - POS(inode, desc.hash_key(info, key)), - BTREE_ITER_SLOTS|BTREE_ITER_INTENT); - return bch2_hash_hole_at(desc, iter); + return IS_ERR(k.k) ? ERR_CAST(k.k) : ERR_PTR(-ENOSPC); } -static inline int bch2_hash_needs_whiteout(const struct bch_hash_desc desc, +static inline int bch2_hash_needs_whiteout(struct btree_trans *trans, + const struct bch_hash_desc desc, const struct bch_hash_info *info, - struct btree_iter *iter, struct btree_iter *start) { + struct btree_iter *iter; struct bkey_s_c k; - bch2_btree_iter_copy(iter, start); + iter = bch2_trans_copy_iter(trans, start); + if (IS_ERR(iter)) + return PTR_ERR(iter); + bch2_btree_iter_next_slot(iter); for_each_btree_key_continue(iter, BTREE_ITER_SLOTS, k) { @@ -253,142 +212,108 @@ static inline int bch2_hash_needs_whiteout(const struct bch_hash_desc desc, return btree_iter_err(k); } -static inline int bch2_hash_set(const struct bch_hash_desc desc, - const struct bch_hash_info *info, - struct bch_fs *c, u64 inode, - u64 *journal_seq, - struct bkey_i *insert, int flags) +static inline int __bch2_hash_set(struct btree_trans *trans, + const struct bch_hash_desc desc, + const struct bch_hash_info *info, + u64 inode, struct bkey_i *insert, int flags) { - struct btree_iter iter, hashed_slot; + struct btree_iter *iter, *slot = NULL; struct bkey_s_c k; - int ret; - bch2_btree_iter_init(&hashed_slot, c, desc.btree_id, - POS(inode, desc.hash_bkey(info, bkey_i_to_s_c(insert))), - BTREE_ITER_SLOTS|BTREE_ITER_INTENT); - bch2_btree_iter_init(&iter, c, desc.btree_id, hashed_slot.pos, - BTREE_ITER_SLOTS|BTREE_ITER_INTENT); - bch2_btree_iter_link(&hashed_slot, &iter); -retry: - /* - * On hash collision, we have to keep the slot we hashed to locked while - * we do the insert - to avoid racing with another thread deleting - * whatever's in the slot we hashed to: - */ - ret = bch2_btree_iter_traverse(&hashed_slot); - if (ret) - goto err; - - /* - * On -EINTR/retry, we dropped locks - always restart from the slot we - * hashed to: - */ - bch2_btree_iter_copy(&iter, &hashed_slot); - - k = bch2_hash_lookup_bkey_at(desc, info, &iter, bkey_i_to_s_c(insert)); - - ret = btree_iter_err(k); - if (ret == -ENOENT) { - if (flags & BCH_HASH_SET_MUST_REPLACE) { - ret = -ENOENT; - goto err; + iter = bch2_trans_get_iter(trans, desc.btree_id, + POS(inode, desc.hash_bkey(info, bkey_i_to_s_c(insert))), + BTREE_ITER_SLOTS|BTREE_ITER_INTENT); + if (IS_ERR(iter)) + return PTR_ERR(iter); + + for_each_btree_key_continue(iter, BTREE_ITER_SLOTS, k) { + if (iter->pos.inode != inode) + break; + + if (k.k->type == desc.key_type) { + if (!desc.cmp_bkey(k, bkey_i_to_s_c(insert))) + goto found; + + /* hash collision: */ + continue; } - /* - * Not found, so we're now looking for any open - * slot - we might have skipped over a whiteout - * that we could have used, so restart from the - * slot we hashed to: - */ - bch2_btree_iter_copy(&iter, &hashed_slot); - k = bch2_hash_hole_at(desc, &iter); - if ((ret = btree_iter_err(k))) - goto err; - } else if (!ret) { - if (flags & BCH_HASH_SET_MUST_CREATE) { - ret = -EEXIST; - goto err; + if (!slot && + !(flags & BCH_HASH_SET_MUST_REPLACE)) { + slot = bch2_trans_copy_iter(trans, iter); + if (IS_ERR(slot)) + return PTR_ERR(slot); } - } else { - goto err; + + if (k.k->type != desc.whiteout_type) + goto not_found; } - insert->k.p = iter.pos; - ret = bch2_btree_insert_at(c, NULL, NULL, journal_seq, - BTREE_INSERT_ATOMIC|flags, - BTREE_INSERT_ENTRY(&iter, insert)); -err: - if (ret == -EINTR) - goto retry; - - /* - * On successful insert, we don't want to clobber ret with error from - * iter: - */ - bch2_btree_iter_unlock(&iter); - bch2_btree_iter_unlock(&hashed_slot); - return ret; + return btree_iter_err(k) ?: -ENOSPC; +not_found: + if (flags & BCH_HASH_SET_MUST_REPLACE) + return -ENOENT; + + insert->k.p = slot->pos; + bch2_trans_update(trans, slot, insert, 0); + return 0; +found: + if (flags & BCH_HASH_SET_MUST_CREATE) + return -EEXIST; + + insert->k.p = iter->pos; + bch2_trans_update(trans, iter, insert, 0); + return 0; } -static inline int bch2_hash_delete_at(const struct bch_hash_desc desc, - const struct bch_hash_info *info, - struct btree_iter *iter, - u64 *journal_seq) +static inline int bch2_hash_set(const struct bch_hash_desc desc, + const struct bch_hash_info *info, + struct bch_fs *c, u64 inode, + u64 *journal_seq, + struct bkey_i *insert, int flags) { - struct btree_iter whiteout_iter; - struct bkey_i delete; - int ret = -ENOENT; + return bch2_trans_do(c, journal_seq, flags|BTREE_INSERT_ATOMIC, + __bch2_hash_set(&trans, desc, info, + inode, insert, flags)); +} - bch2_btree_iter_init(&whiteout_iter, iter->c, desc.btree_id, - iter->pos, BTREE_ITER_SLOTS); - bch2_btree_iter_link(iter, &whiteout_iter); +static inline int bch2_hash_delete_at(struct btree_trans *trans, + const struct bch_hash_desc desc, + const struct bch_hash_info *info, + struct btree_iter *iter) +{ + struct bkey_i *delete; + int ret; - ret = bch2_hash_needs_whiteout(desc, info, &whiteout_iter, iter); + ret = bch2_hash_needs_whiteout(trans, desc, info, iter); if (ret < 0) - goto err; - - bkey_init(&delete.k); - delete.k.p = iter->pos; - delete.k.type = ret ? desc.whiteout_type : KEY_TYPE_DELETED; - - ret = bch2_btree_insert_at(iter->c, NULL, NULL, journal_seq, - BTREE_INSERT_NOFAIL| - BTREE_INSERT_ATOMIC, - BTREE_INSERT_ENTRY(iter, &delete)); -err: - bch2_btree_iter_unlink(&whiteout_iter); - return ret; + return ret; + + delete = bch2_trans_kmalloc(trans, sizeof(*delete)); + if (IS_ERR(delete)) + return PTR_ERR(delete); + + bkey_init(&delete->k); + delete->k.p = iter->pos; + delete->k.type = ret ? desc.whiteout_type : KEY_TYPE_DELETED; + + bch2_trans_update(trans, iter, delete, 0); + return 0; } -static inline int bch2_hash_delete(const struct bch_hash_desc desc, - const struct bch_hash_info *info, - struct bch_fs *c, u64 inode, - u64 *journal_seq, const void *key) +static inline int bch2_hash_delete(struct btree_trans *trans, + const struct bch_hash_desc desc, + const struct bch_hash_info *info, + u64 inode, const void *key) { - struct btree_iter iter, whiteout_iter; - struct bkey_s_c k; - int ret = -ENOENT; - - bch2_btree_iter_init(&iter, c, desc.btree_id, - POS(inode, desc.hash_key(info, key)), - BTREE_ITER_SLOTS|BTREE_ITER_INTENT); - bch2_btree_iter_init(&whiteout_iter, c, desc.btree_id, - POS(inode, desc.hash_key(info, key)), - BTREE_ITER_SLOTS); - bch2_btree_iter_link(&iter, &whiteout_iter); -retry: - k = bch2_hash_lookup_at(desc, info, &iter, key); - if ((ret = btree_iter_err(k))) - goto err; - - ret = bch2_hash_delete_at(desc, info, &iter, journal_seq); -err: - if (ret == -EINTR) - goto retry; - - bch2_btree_iter_unlock(&whiteout_iter); - bch2_btree_iter_unlock(&iter); - return ret; + struct btree_iter *iter; + + iter = bch2_hash_lookup(trans, desc, info, inode, key, + BTREE_ITER_INTENT); + if (IS_ERR(iter)) + return PTR_ERR(iter); + + return bch2_hash_delete_at(trans, desc, info, iter); } #endif /* _BCACHEFS_STR_HASH_H */ diff --git a/fs/bcachefs/xattr.c b/fs/bcachefs/xattr.c index 8eb1b1703e7e..27eb889b76e4 100644 --- a/fs/bcachefs/xattr.c +++ b/fs/bcachefs/xattr.c @@ -143,32 +143,28 @@ void bch2_xattr_to_text(struct bch_fs *c, char *buf, } } -struct bkey_s_c bch2_xattr_get_iter(struct bch_fs *c, - struct btree_iter *iter, - struct bch_inode_info *inode, - const char *name, int type) -{ - return bch2_hash_lookup(bch2_xattr_hash_desc, - &inode->ei_str_hash, - c, inode->v.i_ino, iter, - &X_SEARCH(type, name, strlen(name))); -} - int bch2_xattr_get(struct bch_fs *c, struct bch_inode_info *inode, - const char *name, void *buffer, size_t size, int type) + const char *name, void *buffer, size_t size, int type) { - struct btree_iter iter; - struct bkey_s_c k; + struct btree_trans trans; + struct btree_iter *iter; struct bkey_s_c_xattr xattr; int ret; - k = bch2_hash_lookup(bch2_xattr_hash_desc, &inode->ei_str_hash, c, - inode->v.i_ino, &iter, - &X_SEARCH(type, name, strlen(name))); - if (IS_ERR(k.k)) - return bch2_btree_iter_unlock(&iter) ?: -ENODATA; + bch2_trans_init(&trans, c); + + iter = bch2_hash_lookup(&trans, bch2_xattr_hash_desc, + &inode->ei_str_hash, inode->v.i_ino, + &X_SEARCH(type, name, strlen(name)), + 0); + if (IS_ERR(iter)) { + bch2_trans_exit(&trans); + BUG_ON(PTR_ERR(iter) == -EINTR); - xattr = bkey_s_c_to_xattr(k); + return PTR_ERR(iter) == -ENOENT ? -ENODATA : PTR_ERR(iter); + } + + xattr = bkey_s_c_to_xattr(bch2_btree_iter_peek_slot(iter)); ret = le16_to_cpu(xattr.v->x_val_len); if (buffer) { if (ret > size) @@ -177,47 +173,48 @@ int bch2_xattr_get(struct bch_fs *c, struct bch_inode_info *inode, memcpy(buffer, xattr_val(xattr.v), ret); } - bch2_btree_iter_unlock(&iter); + bch2_trans_exit(&trans); return ret; } -int bch2_xattr_set(struct bch_fs *c, u64 inum, +int bch2_xattr_set(struct btree_trans *trans, u64 inum, const struct bch_hash_info *hash_info, const char *name, const void *value, size_t size, - int flags, int type, u64 *journal_seq) + int type, int flags) { - struct xattr_search_key search = X_SEARCH(type, name, strlen(name)); int ret; if (value) { struct bkey_i_xattr *xattr; + unsigned namelen = strlen(name); unsigned u64s = BKEY_U64s + - xattr_val_u64s(search.name.len, size); + xattr_val_u64s(namelen, size); if (u64s > U8_MAX) return -ERANGE; - xattr = kmalloc(u64s * sizeof(u64), GFP_NOFS); - if (!xattr) - return -ENOMEM; + xattr = bch2_trans_kmalloc(trans, u64s * sizeof(u64)); + if (IS_ERR(xattr)) + return PTR_ERR(xattr); bkey_xattr_init(&xattr->k_i); xattr->k.u64s = u64s; xattr->v.x_type = type; - xattr->v.x_name_len = search.name.len; + xattr->v.x_name_len = namelen; xattr->v.x_val_len = cpu_to_le16(size); - memcpy(xattr->v.x_name, search.name.name, search.name.len); + memcpy(xattr->v.x_name, name, namelen); memcpy(xattr_val(&xattr->v), value, size); - ret = bch2_hash_set(bch2_xattr_hash_desc, hash_info, c, - inum, journal_seq, - &xattr->k_i, - (flags & XATTR_CREATE ? BCH_HASH_SET_MUST_CREATE : 0)| - (flags & XATTR_REPLACE ? BCH_HASH_SET_MUST_REPLACE : 0)); - kfree(xattr); + ret = __bch2_hash_set(trans, bch2_xattr_hash_desc, hash_info, + inum, &xattr->k_i, + (flags & XATTR_CREATE ? BCH_HASH_SET_MUST_CREATE : 0)| + (flags & XATTR_REPLACE ? BCH_HASH_SET_MUST_REPLACE : 0)); } else { - ret = bch2_hash_delete(bch2_xattr_hash_desc, hash_info, - c, inum, journal_seq, &search); + struct xattr_search_key search = + X_SEARCH(type, name, strlen(name)); + + ret = bch2_hash_delete(trans, bch2_xattr_hash_desc, + hash_info, inum, &search); } if (ret == -ENOENT) @@ -309,9 +306,11 @@ static int bch2_xattr_set_handler(const struct xattr_handler *handler, struct bch_inode_info *inode = to_bch_ei(vinode); struct bch_fs *c = inode->v.i_sb->s_fs_info; - return bch2_xattr_set(c, inode->v.i_ino, &inode->ei_str_hash, - name, value, size, flags, handler->flags, - &inode->ei_journal_seq); + return bch2_trans_do(c, &inode->ei_journal_seq, BTREE_INSERT_ATOMIC, + bch2_xattr_set(&trans, inode->v.i_ino, + &inode->ei_str_hash, + name, value, size, + handler->flags, flags)); } static const struct xattr_handler bch_xattr_user_handler = { diff --git a/fs/bcachefs/xattr.h b/fs/bcachefs/xattr.h index 1365032d56c3..0689d327cdc4 100644 --- a/fs/bcachefs/xattr.h +++ b/fs/bcachefs/xattr.h @@ -35,15 +35,12 @@ struct xattr_handler; struct bch_hash_info; struct bch_inode_info; -struct bkey_s_c bch2_xattr_get_iter(struct bch_fs *, - struct btree_iter *, - struct bch_inode_info *, - const char *, int); int bch2_xattr_get(struct bch_fs *, struct bch_inode_info *, const char *, void *, size_t, int); -int bch2_xattr_set(struct bch_fs *, u64, const struct bch_hash_info *, - const char *, const void *, size_t, int, int, u64 *); +int bch2_xattr_set(struct btree_trans *, u64, const struct bch_hash_info *, + const char *, const void *, size_t, int, int); + ssize_t bch2_xattr_list(struct dentry *, char *, size_t); extern const struct xattr_handler *bch2_xattr_handlers[]; |