@@ -38,6 +38,7 @@ btrfs-$(CONFIG_BTRFS_FS_CHECK_INTEGRITY) += check-integrity.o
btrfs-$(CONFIG_BTRFS_FS_REF_VERIFY) += ref-verify.o
btrfs-$(CONFIG_BLK_DEV_ZONED) += zoned.o
btrfs-$(CONFIG_FS_VERITY) += verity.o
+btrfs-$(CONFIG_FS_ENCRYPTION) += fscrypt.o
btrfs-$(CONFIG_BTRFS_FS_RUN_SANITY_TESTS) += tests/free-space-tests.o \
tests/extent-buffer-tests.o tests/btrfs-tests.o \
@@ -3067,6 +3067,7 @@ struct btrfs_new_inode_args {
struct posix_acl *default_acl;
struct posix_acl *acl;
struct fscrypt_name fname;
+ bool encrypt;
};
int btrfs_new_inode_prepare(struct btrfs_new_inode_args *args,
unsigned int *trans_num_items);
@@ -4009,6 +4009,9 @@ static int btrfs_file_open(struct inode *inode, struct file *filp)
int ret;
filp->f_mode |= FMODE_NOWAIT | FMODE_BUF_RASYNC | FMODE_BUF_WASYNC;
+ ret = fscrypt_file_open(inode, filp);
+ if (ret)
+ return ret;
ret = fsverity_file_open(inode, filp);
if (ret)
new file mode 100644
@@ -0,0 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include "ctree.h"
+#include "fscrypt.h"
+
+const struct fscrypt_operations btrfs_fscrypt_ops = {
+};
new file mode 100644
@@ -0,0 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef BTRFS_FSCRYPT_H
+#define BTRFS_FSCRYPT_H
+
+#include <linux/fscrypt.h>
+
+extern const struct fscrypt_operations btrfs_fscrypt_ops;
+
+#endif /* BTRFS_FSCRYPT_H */
@@ -5447,6 +5447,7 @@ void btrfs_evict_inode(struct inode *inode)
trace_btrfs_inode_evict(inode);
if (!root) {
+ fscrypt_put_encryption_info(inode);
fsverity_cleanup_inode(inode);
clear_inode(inode);
return;
@@ -5548,6 +5549,7 @@ void btrfs_evict_inode(struct inode *inode)
* to retry these periodically in the future.
*/
btrfs_remove_delayed_node(BTRFS_I(inode));
+ fscrypt_put_encryption_info(inode);
fsverity_cleanup_inode(inode);
clear_inode(inode);
}
@@ -6294,6 +6296,10 @@ int btrfs_new_inode_prepare(struct btrfs_new_inode_args *args,
return ret;
}
+ ret = fscrypt_prepare_new_inode(dir, inode, &args->encrypt);
+ if (ret)
+ return ret;
+
/* 1 to add inode item */
*trans_num_items = 1;
/* 1 to add compression property */
@@ -6770,9 +6776,13 @@ static int btrfs_link(struct dentry *old_dentry, struct inode *dir,
if (inode->i_nlink >= BTRFS_LINK_MAX)
return -EMLINK;
+ err = fscrypt_prepare_link(old_dentry, dir, dentry);
+ if (err)
+ return err;
+
err = fscrypt_setup_filename(dir, &dentry->d_name, 0, &fname);
if (err)
- goto fail;
+ return err;
err = btrfs_set_inode_index(BTRFS_I(dir), &index);
if (err)
@@ -7053,6 +7063,7 @@ struct extent_map *btrfs_get_extent(struct btrfs_inode *inode,
unsigned long ptr;
char *map;
size_t size;
+ size_t inline_size;
size_t extent_offset;
size_t copy_size;
@@ -7060,9 +7071,13 @@ struct extent_map *btrfs_get_extent(struct btrfs_inode *inode,
goto out;
size = btrfs_file_extent_ram_bytes(leaf, item);
+ inline_size = btrfs_file_extent_inline_item_len(leaf, path->slots[0]);
+ ASSERT(btrfs_file_extent_encryption(leaf, item) ||
+ btrfs_file_extent_compression(leaf, item) ||
+ (size == inline_size));
extent_offset = page_offset(page) + pg_offset - extent_start;
copy_size = min_t(u64, PAGE_SIZE - pg_offset,
- size - extent_offset);
+ inline_size - extent_offset);
em->start = extent_start + extent_offset;
em->len = ALIGN(copy_size, fs_info->sectorsize);
em->orig_block_len = em->len;
@@ -8880,6 +8895,7 @@ void btrfs_test_destroy_inode(struct inode *inode)
void btrfs_free_inode(struct inode *inode)
{
+ fscrypt_free_inode(inode);
kmem_cache_free(btrfs_inode_cachep, BTRFS_I(inode));
}
@@ -8950,8 +8966,7 @@ int btrfs_drop_inode(struct inode *inode)
/* the snap/subvol tree is on deleting */
if (btrfs_root_refs(&root->root_item) == 0)
return 1;
- else
- return generic_drop_inode(inode);
+ return generic_drop_inode(inode) || fscrypt_drop_inode(inode);
}
static void init_once(void *foo)
@@ -9542,6 +9557,11 @@ static int btrfs_rename2(struct user_namespace *mnt_userns, struct inode *old_di
if (flags & ~(RENAME_NOREPLACE | RENAME_EXCHANGE | RENAME_WHITEOUT))
return -EINVAL;
+ ret = fscrypt_prepare_rename(old_dir, old_dentry, new_dir, new_dentry,
+ flags);
+ if (ret)
+ return ret;
+
if (flags & RENAME_EXCHANGE)
ret = btrfs_rename_exchange(old_dir, old_dentry, new_dir,
new_dentry);
@@ -9761,15 +9781,22 @@ static int btrfs_symlink(struct user_namespace *mnt_userns, struct inode *dir,
};
unsigned int trans_num_items;
int err;
- int name_len;
int datasize;
unsigned long ptr;
struct btrfs_file_extent_item *ei;
struct extent_buffer *leaf;
+ struct fscrypt_str disk_link;
+ u32 name_len = strlen(symname);
- name_len = strlen(symname);
- if (name_len > BTRFS_MAX_INLINE_DATA_SIZE(fs_info))
- return -ENAMETOOLONG;
+ /*
+ * fscrypt sets disk_link.len to be len + 1, including a NUL terminator, but we
+ * don't store that '\0' character.
+ */
+ err = fscrypt_prepare_symlink(dir, symname, name_len,
+ BTRFS_MAX_INLINE_DATA_SIZE(fs_info) + 1,
+ &disk_link);
+ if (err)
+ return err;
inode = new_inode(dir->i_sb);
if (!inode)
@@ -9778,7 +9805,7 @@ static int btrfs_symlink(struct user_namespace *mnt_userns, struct inode *dir,
inode->i_op = &btrfs_symlink_inode_operations;
inode_nohighmem(inode);
inode->i_mapping->a_ops = &btrfs_aops;
- btrfs_i_size_write(BTRFS_I(inode), name_len);
+ btrfs_i_size_write(BTRFS_I(inode), disk_link.len - 1);
inode_set_bytes(inode, name_len);
new_inode_args.inode = inode;
@@ -9806,10 +9833,23 @@ static int btrfs_symlink(struct user_namespace *mnt_userns, struct inode *dir,
inode = NULL;
goto out;
}
+
+ if (IS_ENCRYPTED(inode)) {
+ err = fscrypt_encrypt_symlink(inode, symname, name_len,
+ &disk_link);
+ if (err) {
+ btrfs_abort_transaction(trans, err);
+ btrfs_free_path(path);
+ discard_new_inode(inode);
+ inode = NULL;
+ goto out;
+ }
+ }
+
key.objectid = btrfs_ino(BTRFS_I(inode));
key.offset = 0;
key.type = BTRFS_EXTENT_DATA_KEY;
- datasize = btrfs_file_extent_calc_inline_size(name_len);
+ datasize = btrfs_file_extent_calc_inline_size(disk_link.len - 1);
err = btrfs_insert_empty_item(trans, root, path, &key,
datasize);
if (err) {
@@ -9828,10 +9868,11 @@ static int btrfs_symlink(struct user_namespace *mnt_userns, struct inode *dir,
btrfs_set_file_extent_encryption(leaf, ei, 0);
btrfs_set_file_extent_compression(leaf, ei, 0);
btrfs_set_file_extent_other_encoding(leaf, ei, 0);
+ /* ram size is the unencoded size */
btrfs_set_file_extent_ram_bytes(leaf, ei, name_len);
ptr = btrfs_file_extent_inline_start(ei);
- write_extent_buffer(leaf, symname, ptr, name_len);
+ write_extent_buffer(leaf, disk_link.name, ptr, disk_link.len - 1);
btrfs_mark_buffer_dirty(leaf);
btrfs_free_path(path);
@@ -9848,6 +9889,29 @@ static int btrfs_symlink(struct user_namespace *mnt_userns, struct inode *dir,
return err;
}
+static const char *btrfs_get_link(struct dentry *dentry, struct inode *inode,
+ struct delayed_call *done)
+{
+ struct page *cpage;
+ const char *paddr;
+ struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb);
+
+ if (!IS_ENCRYPTED(inode))
+ return page_get_link(dentry, inode, done);
+
+ if (!dentry)
+ return ERR_PTR(-ECHILD);
+
+ cpage = read_mapping_page(inode->i_mapping, 0, NULL);
+ if (IS_ERR(cpage))
+ return ERR_CAST(cpage);
+
+ paddr = fscrypt_get_symlink(inode, page_address(cpage),
+ BTRFS_MAX_INLINE_DATA_SIZE(fs_info), done);
+ put_page(cpage);
+ return paddr;
+}
+
static struct btrfs_trans_handle *insert_prealloc_file_extent(
struct btrfs_trans_handle *trans_in,
struct btrfs_inode *inode,
@@ -11419,7 +11483,7 @@ static const struct inode_operations btrfs_special_inode_operations = {
.update_time = btrfs_update_time,
};
static const struct inode_operations btrfs_symlink_inode_operations = {
- .get_link = page_get_link,
+ .get_link = btrfs_get_link,
.getattr = btrfs_getattr,
.setattr = btrfs_setattr,
.permission = btrfs_permission,
@@ -11429,4 +11493,7 @@ static const struct inode_operations btrfs_symlink_inode_operations = {
const struct dentry_operations btrfs_dentry_operations = {
.d_delete = btrfs_dentry_delete,
+#ifdef CONFIG_FS_ENCRYPTION
+ .d_revalidate = fscrypt_d_revalidate,
+#endif
};
@@ -47,6 +47,7 @@
#include "tests/btrfs-tests.h"
#include "block-group.h"
#include "discard.h"
+#include "fscrypt.h"
#include "qgroup.h"
#include "raid56.h"
#define CREATE_TRACE_POINTS
@@ -1446,6 +1447,7 @@ static int btrfs_fill_super(struct super_block *sb,
sb->s_vop = &btrfs_verityops;
#endif
sb->s_xattr = btrfs_xattr_handlers;
+ fscrypt_set_ops(sb, &btrfs_fscrypt_ops);
sb->s_time_gran = 1;
#ifdef CONFIG_BTRFS_FS_POSIX_ACL
sb->s_flags |= SB_POSIXACL;