On Sun, Jul 16, 2023 at 11:52:36PM -0400, Sweet Tea Dorminy wrote: > From: Omar Sandoval <osandov@xxxxxxxxxxx> > > In order to store encryption information for directories, symlinks, > etc., fscrypt stores a context item with each encrypted non-regular > inode. fscrypt provides an arbitrary blob for the filesystem to store, > and it does not clearly fit into an existing structure, so this goes in > a new item type. > > Signed-off-by: Omar Sandoval <osandov@xxxxxxxxxxx> > Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@xxxxxxxxxx> > --- > fs/btrfs/fscrypt.c | 118 ++++++++++++++++++++++++++++++++ > fs/btrfs/fscrypt.h | 2 + > fs/btrfs/inode.c | 19 +++++ > fs/btrfs/ioctl.c | 8 ++- > include/uapi/linux/btrfs_tree.h | 10 +++ > 5 files changed, 155 insertions(+), 2 deletions(-) > > diff --git a/fs/btrfs/fscrypt.c b/fs/btrfs/fscrypt.c > index 3a53dc59c1e4..1dfddf827cf8 100644 > --- a/fs/btrfs/fscrypt.c > +++ b/fs/btrfs/fscrypt.c > @@ -1,8 +1,126 @@ > // SPDX-License-Identifier: GPL-2.0 > > +#include <linux/iversion.h> > #include "ctree.h" > +#include "accessors.h" > +#include "btrfs_inode.h" > +#include "disk-io.h" > +#include "fs.h" > #include "fscrypt.h" > +#include "ioctl.h" > +#include "messages.h" > +#include "transaction.h" > +#include "xattr.h" > + > +static int btrfs_fscrypt_get_context(struct inode *inode, void *ctx, size_t len) > +{ > + struct btrfs_key key = { > + .objectid = btrfs_ino(BTRFS_I(inode)), > + .type = BTRFS_FSCRYPT_CTX_ITEM_KEY, > + .offset = 0, > + }; > + struct btrfs_path *path; > + struct extent_buffer *leaf; > + unsigned long ptr; > + int ret; > + > + > + path = btrfs_alloc_path(); > + if (!path) > + return -ENOMEM; > + > + ret = btrfs_search_slot(NULL, BTRFS_I(inode)->root, &key, path, 0, 0); > + if (ret) { > + len = -ENOENT; > + goto out; > + } > + > + leaf = path->nodes[0]; > + ptr = btrfs_item_ptr_offset(leaf, path->slots[0]); > + /* fscrypt provides max context length, but it could be less */ > + len = min_t(size_t, len, btrfs_item_size(leaf, path->slots[0])); > + read_extent_buffer(leaf, ctx, ptr, len); > + > +out: > + btrfs_free_path(path); > + return len; > +} > + > +static void btrfs_fscrypt_update_context(struct btrfs_path *path, > + const void *ctx, size_t len) > +{ > + struct extent_buffer *leaf = path->nodes[0]; > + unsigned long ptr = btrfs_item_ptr_offset(leaf, path->slots[0]); > + > + len = min_t(size_t, len, btrfs_item_size(leaf, path->slots[0])); > + write_extent_buffer(leaf, ctx, ptr, len); > + btrfs_mark_buffer_dirty(leaf); > +} > + > +static int btrfs_fscrypt_set_context(struct inode *inode, const void *ctx, > + size_t len, void *fs_data) > +{ > + struct btrfs_path *path; > + int ret; > + struct btrfs_trans_handle *trans = fs_data; > + struct btrfs_key key = { > + .objectid = btrfs_ino(BTRFS_I(inode)), > + .type = BTRFS_FSCRYPT_CTX_ITEM_KEY, > + .offset = 0, > + }; > + path = btrfs_alloc_path(); > + if (!path) > + return -ENOMEM; > + Reorder the declarations above to make it cleaner, add a newline between the variables and allocating path. > + if (!trans) > + trans = btrfs_start_transaction(BTRFS_I(inode)->root, 1); > + if (IS_ERR(trans)) > + return PTR_ERR(trans); > + > + ret = btrfs_search_slot(trans, BTRFS_I(inode)->root, &key, path, 0, 1); > + if (ret == 0) { > + btrfs_fscrypt_update_context(path, ctx, len); > + btrfs_free_path(path); What do we do with the transaction if we started it above? Are we responsible for stopping the transaction if it was provided via fsdata? > + return ret; > + } > + > + btrfs_free_path(path); > + if (ret < 0) { > + btrfs_abort_transaction(trans, ret); > + return ret; > + } > + > + ret = btrfs_insert_item(trans, BTRFS_I(inode)->root, &key, (void *) ctx, len); We already have the path here, we can do btrfs_release_path(path); ret = btrfs_insert_empty_item(trans, BTRFS_I(inode)->root, &key, len); if (ret) { btrfs_abort_transaction(trans, ret); btrfs_end_transaction(trans); return ret; } ptr = btrfs_item_ptr_offset(path->nodes[0], path->slots[0]); write_extent_buffer(path->nodes[0], ctx, ptr, len); btrfs_mark_buffer_dirty(path->nodes[0]); btrfs_free_path(path); > + if (ret) { > + btrfs_abort_transaction(trans, ret); > + return ret; > + } > + > + if (fs_data) > + return ret; > + > + BTRFS_I(inode)->flags |= BTRFS_INODE_ENCRYPT; > + btrfs_sync_inode_flags_to_i_flags(inode); > + inode_inc_iversion(inode); > + inode->i_ctime = current_time(inode); > + ret = btrfs_update_inode(trans, BTRFS_I(inode)->root, BTRFS_I(inode)); > + if (!ret) { > + btrfs_end_transaction(trans); > + return ret; > + } > + > + btrfs_abort_transaction(trans, ret); Still need to call end_transaction if we abort if we started it. Thanks, Josef