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]>
---

v5: 
https://lore.kernel.org/linux-btrfs/365d4f820f70b7cf69b1b9cae9b949a15c3350b0.1706116485.git.jo...@toxicpanda.com/
 * 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.
---
 fs/btrfs/backref.c | 42 +++++++++++++++++++++++++++++++++++++----
 fs/btrfs/fscrypt.c | 47 ++++++++++++++++++++++++++++++++++++++++++++++
 fs/btrfs/fscrypt.h | 10 ++++++++++
 3 files changed, 95 insertions(+), 4 deletions(-)

diff --git a/fs/btrfs/backref.c b/fs/btrfs/backref.c
index 9bb406f7dd30..577c3ef87791 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
@@ -2107,6 +2108,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
@@ -2138,10 +2172,10 @@ 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 (ret)
+                       break;
                if (eb != eb_in) {
                        if (!path->skip_locking)
                                btrfs_tree_read_unlock(eb);
diff --git a/fs/btrfs/fscrypt.c b/fs/btrfs/fscrypt.c
index d1a4cbb990d4..bcb86cbaa171 100644
--- a/fs/btrfs/fscrypt.c
+++ b/fs/btrfs/fscrypt.c
@@ -385,6 +385,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 347b34f45715..4f49ed6176d4 100644
--- a/fs/btrfs/fscrypt.h
+++ b/fs/btrfs/fscrypt.h
@@ -32,6 +32,9 @@ bool btrfs_mergeable_encrypted_bio(struct bio *bio, struct 
inode *inode,
                                   struct fscrypt_extent_info *fi,
                                   u64 logical_offset);
 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,
@@ -91,6 +94,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.51.0


Reply via email to