From: Josef Bacik <[email protected]>

We use this helper for inode-resolve and path resolution in send, so
update this helper to properly decrypt any encrypted names it finds.

Signed-off-by: Josef Bacik <[email protected]>
Signed-off-by: Daniel Vacek <[email protected]>
---

v7 changes:
 * Fix eb leak in failure case as suggested by Chris' AI review.
v6 changes:
 * Adapted to btrfs_iget() now returning binode instead of vfs inode
   as before.
 * Adapted to crypt info being moved from vfs inode to FS specific inode.
v5: 
https://lore.kernel.org/linux-btrfs/365d4f820f70b7cf69b1b9cae9b949a15c3350b0.1706116485.git.jo...@toxicpanda.com/
---
 fs/btrfs/backref.c | 43 ++++++++++++++++++++++++++++++++++++++----
 fs/btrfs/fscrypt.c | 47 ++++++++++++++++++++++++++++++++++++++++++++++
 fs/btrfs/fscrypt.h | 10 ++++++++++
 3 files changed, 96 insertions(+), 4 deletions(-)

diff --git a/fs/btrfs/backref.c b/fs/btrfs/backref.c
index 273924ca912c..33d5df99be8e 100644
--- a/fs/btrfs/backref.c
+++ b/fs/btrfs/backref.c
@@ -20,6 +20,7 @@
 #include "extent-tree.h"
 #include "relocation.h"
 #include "tree-checker.h"
+#include "fscrypt.h"
 
 /* Just arbitrary numbers so we can be sure one of these happened. */
 #define BACKREF_FOUND_SHARED     6
@@ -2104,6 +2105,39 @@ int btrfs_find_one_extref(struct btrfs_root *root, u64 
inode_objectid,
        return ret;
 }
 
+static int copy_resolved_iref_to_buf(struct btrfs_root *fs_root,
+                                    struct extent_buffer *eb, char *dest,
+                                    u64 parent, unsigned long name_off,
+                                    u32 name_len, s64 *bytes_left)
+{
+       struct btrfs_fs_info *fs_info = fs_root->fs_info;
+       struct fscrypt_str fname = FSTR_INIT(NULL, 0);
+       int ret;
+
+       /* No encryption, just copy the name in. */
+       if (!btrfs_fs_incompat(fs_info, ENCRYPT)) {
+               *bytes_left -= name_len;
+               if (*bytes_left >= 0)
+                       read_extent_buffer(eb, dest + *bytes_left, name_off, 
name_len);
+               return 0;
+       }
+
+       ret = fscrypt_fname_alloc_buffer(BTRFS_NAME_LEN, &fname);
+       if (ret)
+               return ret;
+
+       ret = btrfs_decrypt_name(fs_root, eb, name_off, name_len, parent, 
&fname);
+       if (ret)
+               goto out;
+
+       *bytes_left -= fname.len;
+       if (*bytes_left >= 0)
+               memcpy(dest + *bytes_left, fname.name, fname.len);
+out:
+       fscrypt_fname_free_buffer(&fname);
+       return ret;
+}
+
 /*
  * this iterates to turn a name (from iref/extref) into a full filesystem path.
  * Elements of the path are separated by '/' and the path is guaranteed to be
@@ -2135,15 +2169,16 @@ char *btrfs_ref_to_path(struct btrfs_root *fs_root, 
struct btrfs_path *path,
                dest[bytes_left] = '\0';
 
        while (1) {
-               bytes_left -= name_len;
-               if (bytes_left >= 0)
-                       read_extent_buffer(eb, dest + bytes_left,
-                                          name_off, name_len);
+               ret = copy_resolved_iref_to_buf(fs_root, eb, dest, parent,
+                                               name_off, name_len, 
&bytes_left);
                if (eb != eb_in) {
                        if (!path->skip_locking)
                                btrfs_tree_read_unlock(eb);
                        free_extent_buffer(eb);
                }
+               if (ret)
+                       break;
+
                ret = btrfs_find_item(fs_root, path, parent, 0,
                                BTRFS_INODE_REF_KEY, &found_key);
                if (ret > 0)
diff --git a/fs/btrfs/fscrypt.c b/fs/btrfs/fscrypt.c
index 111ca92a3450..a972c8eadfef 100644
--- a/fs/btrfs/fscrypt.c
+++ b/fs/btrfs/fscrypt.c
@@ -352,6 +352,53 @@ int btrfs_fscrypt_bio_length(struct bio *bio, u64 
map_length)
        return map_length;
 }
 
+int btrfs_decrypt_name(struct btrfs_root *root, struct extent_buffer *eb,
+                      unsigned long name_off, u32 name_len,
+                      u64 parent_ino, struct fscrypt_str *name)
+{
+       struct btrfs_inode *inode;
+       struct inode *dir;
+       struct fscrypt_str iname = FSTR_INIT(NULL, 0);
+       int ret;
+
+       ASSERT(name_len <= BTRFS_NAME_LEN);
+
+       ret = fscrypt_fname_alloc_buffer(name_len, &iname);
+       if (ret)
+               return ret;
+
+       inode = btrfs_iget(parent_ino, root);
+       if (IS_ERR(inode)) {
+               ret = PTR_ERR(inode);
+               goto out;
+       }
+       dir = &inode->vfs_inode;
+
+       /*
+        * Directory isn't encrypted, the name isn't encrypted, we can just copy
+        * it into the buffer.
+        */
+       if (!IS_ENCRYPTED(dir)) {
+               read_extent_buffer(eb, name->name, name_off, name_len);
+               name->len = name_len;
+               goto out_inode;
+       }
+
+       read_extent_buffer(eb, iname.name, name_off, name_len);
+
+       ret = fscrypt_prepare_readdir(dir);
+       if (ret)
+               goto out_inode;
+
+       ASSERT(inode->i_crypt_info);
+       ret = fscrypt_fname_disk_to_usr(dir, 0, 0, &iname, name);
+out_inode:
+       iput(dir);
+out:
+       fscrypt_fname_free_buffer(&iname);
+       return ret;
+}
+
 const struct fscrypt_operations btrfs_fscrypt_ops = {
        .inode_info_offs = (int)offsetof(struct btrfs_inode, i_crypt_info) -
                           (int)offsetof(struct btrfs_inode, vfs_inode),
diff --git a/fs/btrfs/fscrypt.h b/fs/btrfs/fscrypt.h
index f7ce2b2e6639..4a1daed90d06 100644
--- a/fs/btrfs/fscrypt.h
+++ b/fs/btrfs/fscrypt.h
@@ -25,6 +25,9 @@ ssize_t btrfs_fscrypt_context_for_new_extent(struct 
btrfs_inode *inode,
                                             struct fscrypt_extent_info *info,
                                             u8 *ctx);
 int btrfs_fscrypt_bio_length(struct bio *bio, u64 map_length);
+int btrfs_decrypt_name(struct btrfs_root *root, struct extent_buffer *eb,
+                      unsigned long name_off, u32 name_len,
+                      u64 parent_ino, struct fscrypt_str *name);
 
 #else
 static inline void btrfs_fscrypt_save_extent_info(struct btrfs_path *path,
@@ -69,6 +72,13 @@ static inline u64 btrfs_fscrypt_bio_length(struct bio *bio, 
u64 map_length)
        return map_length;
 }
 
+static inline int btrfs_decrypt_name(struct btrfs_root *root, struct 
extent_buffer *eb,
+                                    unsigned long name_off, u32 name_len,
+                                    u64 parent_ino, struct fscrypt_str *name)
+{
+       return -EINVAL;
+}
+
 #endif /* CONFIG_FS_ENCRYPTION */
 
 extern const struct fscrypt_operations btrfs_fscrypt_ops;
-- 
2.53.0


Reply via email to