* [RFC PATCH 02/19] btrfs: add encryption dependencies to Kconfig
2019-01-09 1:26 [RFC PATCH 01/19] btrfs: add encryption structs and constants Mark Harmstone
@ 2019-01-09 1:26 ` Mark Harmstone
2019-01-09 1:26 ` [RFC PATCH 03/19] btrfs: load key tree Mark Harmstone
` (16 subsequent siblings)
17 siblings, 0 replies; 19+ messages in thread
From: Mark Harmstone @ 2019-01-09 1:26 UTC (permalink / raw)
Cc: mark, Chris Mason, Josef Bacik, David Sterba, linux-btrfs, linux-kernel
Signed-off-by: Mark Harmstone <mark@harmstone.com>
---
fs/btrfs/Kconfig | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/fs/btrfs/Kconfig b/fs/btrfs/Kconfig
index 23537bc8c827..e6b3b51da91f 100644
--- a/fs/btrfs/Kconfig
+++ b/fs/btrfs/Kconfig
@@ -12,6 +12,10 @@ config BTRFS_FS
select RAID6_PQ
select XOR_BLOCKS
select SRCU
+ select KEYS
+ select CRYPTO_AES
+ select CRYPTO_CTR
+ select CRYPTO_RNG
help
Btrfs is a general purpose copy-on-write filesystem with extents,
--
2.19.2
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [RFC PATCH 03/19] btrfs: load key tree
2019-01-09 1:26 [RFC PATCH 01/19] btrfs: add encryption structs and constants Mark Harmstone
2019-01-09 1:26 ` [RFC PATCH 02/19] btrfs: add encryption dependencies to Kconfig Mark Harmstone
@ 2019-01-09 1:26 ` Mark Harmstone
2019-01-09 1:26 ` [RFC PATCH 04/19] btrfs: allow encrypted volumes to be mounted Mark Harmstone
` (15 subsequent siblings)
17 siblings, 0 replies; 19+ messages in thread
From: Mark Harmstone @ 2019-01-09 1:26 UTC (permalink / raw)
Cc: mark, Chris Mason, Josef Bacik, David Sterba, linux-btrfs, linux-kernel
Signed-off-by: Mark Harmstone <mark@harmstone.com>
---
fs/btrfs/ctree.h | 2 ++
fs/btrfs/disk-io.c | 11 +++++++++++
2 files changed, 13 insertions(+)
diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
index 68f322f600a0..bd2e59dd0eba 100644
--- a/fs/btrfs/ctree.h
+++ b/fs/btrfs/ctree.h
@@ -758,6 +758,7 @@ struct btrfs_fs_info {
struct btrfs_root *quota_root;
struct btrfs_root *uuid_root;
struct btrfs_root *free_space_root;
+ struct btrfs_root *key_root;
/* the log root tree is a directory of all the other log roots */
struct btrfs_root *log_root_tree;
@@ -2957,6 +2958,7 @@ static inline void free_fs_info(struct btrfs_fs_info *fs_info)
kfree(fs_info->quota_root);
kfree(fs_info->uuid_root);
kfree(fs_info->free_space_root);
+ kfree(fs_info->key_root);
kfree(fs_info->super_copy);
kfree(fs_info->super_for_commit);
security_free_mnt_opts(&fs_info->security_opts);
diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c
index 6d776717d8b3..a533c00855be 100644
--- a/fs/btrfs/disk-io.c
+++ b/fs/btrfs/disk-io.c
@@ -1559,6 +1559,9 @@ struct btrfs_root *btrfs_get_fs_root(struct btrfs_fs_info *fs_info,
if (location->objectid == BTRFS_FREE_SPACE_TREE_OBJECTID)
return fs_info->free_space_root ? fs_info->free_space_root :
ERR_PTR(-ENOENT);
+ if (location->objectid == BTRFS_KEY_TREE_OBJECTID)
+ return fs_info->key_root ? fs_info->key_root :
+ ERR_PTR(-ENOENT);
again:
root = btrfs_lookup_fs_root(fs_info, location->objectid);
if (root) {
@@ -2029,6 +2032,7 @@ static void free_root_pointers(struct btrfs_fs_info *info, int chunk_root)
if (chunk_root)
free_root_extent_buffers(info->chunk_root);
free_root_extent_buffers(info->free_space_root);
+ free_root_extent_buffers(info->key_root);
}
void btrfs_free_fs_roots(struct btrfs_fs_info *fs_info)
@@ -2349,6 +2353,13 @@ static int btrfs_read_roots(struct btrfs_fs_info *fs_info)
fs_info->free_space_root = root;
}
+ location.objectid = BTRFS_KEY_TREE_OBJECTID;
+ root = btrfs_read_tree_root(tree_root, &location);
+ if (!IS_ERR(root)) {
+ set_bit(BTRFS_ROOT_TRACK_DIRTY, &root->state);
+ fs_info->key_root = root;
+ }
+
return 0;
out:
btrfs_warn(fs_info, "failed to read root (objectid=%llu): %d",
--
2.19.2
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [RFC PATCH 04/19] btrfs: allow encrypted volumes to be mounted
2019-01-09 1:26 [RFC PATCH 01/19] btrfs: add encryption structs and constants Mark Harmstone
2019-01-09 1:26 ` [RFC PATCH 02/19] btrfs: add encryption dependencies to Kconfig Mark Harmstone
2019-01-09 1:26 ` [RFC PATCH 03/19] btrfs: load key tree Mark Harmstone
@ 2019-01-09 1:26 ` Mark Harmstone
2019-01-09 1:26 ` [RFC PATCH 05/19] btrfs: add key list Mark Harmstone
` (14 subsequent siblings)
17 siblings, 0 replies; 19+ messages in thread
From: Mark Harmstone @ 2019-01-09 1:26 UTC (permalink / raw)
Cc: mark, Chris Mason, Josef Bacik, David Sterba, linux-kernel, linux-btrfs
Signed-off-by: Mark Harmstone <mark@harmstone.com>
---
fs/btrfs/ctree.h | 3 ++-
fs/btrfs/encryption.h | 20 ++++++++++++++++++++
fs/btrfs/tree-checker.c | 39 +++++++++++++++++++++++++++++----------
3 files changed, 51 insertions(+), 11 deletions(-)
create mode 100644 fs/btrfs/encryption.h
diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
index bd2e59dd0eba..723e9b38e0aa 100644
--- a/fs/btrfs/ctree.h
+++ b/fs/btrfs/ctree.h
@@ -265,7 +265,8 @@ struct btrfs_super_block {
BTRFS_FEATURE_INCOMPAT_RAID56 | \
BTRFS_FEATURE_INCOMPAT_EXTENDED_IREF | \
BTRFS_FEATURE_INCOMPAT_SKINNY_METADATA | \
- BTRFS_FEATURE_INCOMPAT_NO_HOLES)
+ BTRFS_FEATURE_INCOMPAT_NO_HOLES | \
+ BTRFS_FEATURE_INCOMPAT_ENCRYPTION)
#define BTRFS_FEATURE_INCOMPAT_SAFE_SET \
(BTRFS_FEATURE_INCOMPAT_EXTENDED_IREF)
diff --git a/fs/btrfs/encryption.h b/fs/btrfs/encryption.h
new file mode 100644
index 000000000000..b9a37e76e4a6
--- /dev/null
+++ b/fs/btrfs/encryption.h
@@ -0,0 +1,20 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2019 Mark Harmstone. All rights reserved.
+ */
+
+#ifndef BTRFS_ENCRYPTION_H
+#define BTRFS_ENCRYPTION_H
+
+enum btrfs_encryption_type {
+ BTRFS_ENCRYPTION_NONE = 0,
+ BTRFS_ENCRYPTION_AES256CTR = 1,
+ BTRFS_ENCRYPTION_TYPES = 1,
+};
+
+struct btrfs_encryption_header {
+ u64 key_number;
+ u8 iv[BTRFS_ENCRYPTION_BLOCK_LENGTH];
+};
+
+#endif
diff --git a/fs/btrfs/tree-checker.c b/fs/btrfs/tree-checker.c
index 1a4e2b101ef2..c4fe1cb65d73 100644
--- a/fs/btrfs/tree-checker.c
+++ b/fs/btrfs/tree-checker.c
@@ -20,6 +20,7 @@
#include "disk-io.h"
#include "compression.h"
#include "volumes.h"
+#include "encryption.h"
/*
* Error message should follow the following format:
@@ -140,10 +141,11 @@ static int check_extent_data_item(struct btrfs_fs_info *fs_info,
BTRFS_COMPRESS_TYPES);
return -EUCLEAN;
}
- if (btrfs_file_extent_encryption(leaf, fi)) {
+ if (btrfs_file_extent_encryption(leaf, fi) > BTRFS_ENCRYPTION_TYPES) {
file_extent_err(fs_info, leaf, slot,
- "invalid encryption for file extent, have %u expect 0",
- btrfs_file_extent_encryption(leaf, fi));
+ "invalid encryption for file extent, have %u expect range [0, %u]",
+ btrfs_file_extent_encryption(leaf, fi),
+ BTRFS_ENCRYPTION_TYPES);
return -EUCLEAN;
}
if (btrfs_file_extent_type(leaf, fi) == BTRFS_FILE_EXTENT_INLINE) {
@@ -155,9 +157,14 @@ static int check_extent_data_item(struct btrfs_fs_info *fs_info,
return -EUCLEAN;
}
- /* Compressed inline extent has no on-disk size, skip it */
+ /*
+ * Compressed or encrypted inline extent has no on-disk size,
+ * skip it
+ */
if (btrfs_file_extent_compression(leaf, fi) !=
- BTRFS_COMPRESS_NONE)
+ BTRFS_COMPRESS_NONE ||
+ btrfs_file_extent_encryption(leaf, fi) !=
+ BTRFS_ENCRYPTION_NONE)
return 0;
/* Uncompressed inline extent size must match item size */
@@ -172,13 +179,25 @@ static int check_extent_data_item(struct btrfs_fs_info *fs_info,
return 0;
}
- /* Regular or preallocated extent has fixed item size */
- if (item_size != sizeof(*fi)) {
- file_extent_err(fs_info, leaf, slot,
+ if (btrfs_file_extent_encryption(leaf, fi) == BTRFS_ENCRYPTION_NONE) {
+ /* Regular or preallocated extent has fixed item size */
+ if (item_size != sizeof(*fi)) {
+ file_extent_err(fs_info, leaf, slot,
"invalid item size for reg/prealloc file extent, have %u expect %zu",
- item_size, sizeof(*fi));
- return -EUCLEAN;
+ item_size, sizeof(*fi));
+ return -EUCLEAN;
+ }
+ } else {
+ if (item_size !=
+ sizeof(*fi) + sizeof(struct btrfs_encryption_header)) {
+ file_extent_err(fs_info, leaf, slot,
+ "invalid item size for encrypted file extent, have %u expect %zu",
+ item_size,
+ sizeof(*fi) + sizeof(struct btrfs_encryption_header));
+ return -EUCLEAN;
+ }
}
+
if (CHECK_FE_ALIGNED(fs_info, leaf, slot, fi, ram_bytes, sectorsize) ||
CHECK_FE_ALIGNED(fs_info, leaf, slot, fi, disk_bytenr, sectorsize) ||
CHECK_FE_ALIGNED(fs_info, leaf, slot, fi, disk_num_bytes, sectorsize) ||
--
2.19.2
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [RFC PATCH 05/19] btrfs: add key list
2019-01-09 1:26 [RFC PATCH 01/19] btrfs: add encryption structs and constants Mark Harmstone
` (2 preceding siblings ...)
2019-01-09 1:26 ` [RFC PATCH 04/19] btrfs: allow encrypted volumes to be mounted Mark Harmstone
@ 2019-01-09 1:26 ` Mark Harmstone
2019-01-09 1:26 ` [RFC PATCH 06/19] btrfs: add ioctl BTRFS_IOC_GET_KEY_SALT Mark Harmstone
` (13 subsequent siblings)
17 siblings, 0 replies; 19+ messages in thread
From: Mark Harmstone @ 2019-01-09 1:26 UTC (permalink / raw)
Cc: mark, Chris Mason, Josef Bacik, David Sterba, linux-btrfs, linux-kernel
Signed-off-by: Mark Harmstone <mark@harmstone.com>
---
fs/btrfs/ctree.h | 5 +++++
fs/btrfs/disk-io.c | 18 ++++++++++++++++++
fs/btrfs/encryption.h | 13 +++++++++++++
3 files changed, 36 insertions(+)
diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
index 723e9b38e0aa..3f3356a2d145 100644
--- a/fs/btrfs/ctree.h
+++ b/fs/btrfs/ctree.h
@@ -1116,6 +1116,9 @@ struct btrfs_fs_info {
u32 sectorsize;
u32 stripesize;
+ struct list_head key_list;
+ struct rw_semaphore key_sem;
+
#ifdef CONFIG_BTRFS_FS_REF_VERIFY
spinlock_t ref_verify_lock;
struct rb_root block_tree;
@@ -2413,6 +2416,8 @@ BTRFS_SETGET_FUNCS(file_extent_encryption, struct btrfs_file_extent_item,
BTRFS_SETGET_FUNCS(file_extent_other_encoding, struct btrfs_file_extent_item,
other_encoding, 16);
+#define BTRFS_ENCRYPTION_KEY_ID_LENGTH 64
+
/*
* this returns the number of bytes used by the item on disk, minus the
* size of any extent headers. If a file is compressed on disk, this is
diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c
index a533c00855be..a1964b768750 100644
--- a/fs/btrfs/disk-io.c
+++ b/fs/btrfs/disk-io.c
@@ -18,6 +18,7 @@
#include <linux/error-injection.h>
#include <linux/crc32c.h>
#include <asm/unaligned.h>
+#include <crypto/rng.h>
#include "ctree.h"
#include "disk-io.h"
#include "transaction.h"
@@ -38,6 +39,7 @@
#include "compression.h"
#include "tree-checker.h"
#include "ref-verify.h"
+#include "encryption.h"
#ifdef CONFIG_X86
#include <asm/cpufeature.h>
@@ -2751,6 +2753,9 @@ int open_ctree(struct super_block *sb,
INIT_LIST_HEAD(&fs_info->pinned_chunks);
+ INIT_LIST_HEAD(&fs_info->key_list);
+ init_rwsem(&fs_info->key_sem);
+
/* Usable values until the real ones are cached from the superblock */
fs_info->nodesize = 4096;
fs_info->sectorsize = 4096;
@@ -4013,6 +4018,19 @@ void close_ctree(struct btrfs_fs_info *fs_info)
list_del_init(&em->list);
free_extent_map(em);
}
+
+ while (!list_empty(&fs_info->key_list)) {
+ struct btrfs_enc_key *key;
+
+ key = list_first_entry(&fs_info->key_list,
+ struct btrfs_enc_key, key_list);
+
+ if (key->loaded)
+ crypto_free_skcipher(key->skcipher);
+
+ list_del_init(&key->key_list);
+ kfree(key);
+ }
}
int btrfs_buffer_uptodate(struct extent_buffer *buf, u64 parent_transid,
diff --git a/fs/btrfs/encryption.h b/fs/btrfs/encryption.h
index b9a37e76e4a6..77914d7797c6 100644
--- a/fs/btrfs/encryption.h
+++ b/fs/btrfs/encryption.h
@@ -6,6 +6,8 @@
#ifndef BTRFS_ENCRYPTION_H
#define BTRFS_ENCRYPTION_H
+#include <crypto/skcipher.h>
+
enum btrfs_encryption_type {
BTRFS_ENCRYPTION_NONE = 0,
BTRFS_ENCRYPTION_AES256CTR = 1,
@@ -17,4 +19,15 @@ struct btrfs_encryption_header {
u8 iv[BTRFS_ENCRYPTION_BLOCK_LENGTH];
};
+struct btrfs_enc_key {
+ struct list_head key_list;
+ u8 key_id[BTRFS_ENCRYPTION_KEY_ID_LENGTH];
+ u64 key_number;
+ bool loaded;
+ bool added;
+ bool used;
+ struct crypto_skcipher *skcipher;
+ struct mutex lock;
+};
+
#endif
--
2.19.2
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [RFC PATCH 06/19] btrfs: add ioctl BTRFS_IOC_GET_KEY_SALT
2019-01-09 1:26 [RFC PATCH 01/19] btrfs: add encryption structs and constants Mark Harmstone
` (3 preceding siblings ...)
2019-01-09 1:26 ` [RFC PATCH 05/19] btrfs: add key list Mark Harmstone
@ 2019-01-09 1:26 ` Mark Harmstone
2019-01-09 1:26 ` [RFC PATCH 07/19] btrfs: add new keys to key root when flushed Mark Harmstone
` (12 subsequent siblings)
17 siblings, 0 replies; 19+ messages in thread
From: Mark Harmstone @ 2019-01-09 1:26 UTC (permalink / raw)
Cc: mark, Chris Mason, Josef Bacik, David Sterba, linux-kernel, linux-btrfs
Signed-off-by: Mark Harmstone <mark@harmstone.com>
---
fs/btrfs/Makefile | 2 +-
fs/btrfs/ctree.h | 4 +
fs/btrfs/disk-io.c | 6 ++
fs/btrfs/encryption.c | 58 +++++++++++++
fs/btrfs/encryption.h | 3 +
fs/btrfs/ioctl.c | 170 +++++++++++++++++++++++++++++++++++++
include/uapi/linux/btrfs.h | 10 +++
7 files changed, 252 insertions(+), 1 deletion(-)
create mode 100644 fs/btrfs/encryption.c
diff --git a/fs/btrfs/Makefile b/fs/btrfs/Makefile
index ca693dd554e9..805654060cea 100644
--- a/fs/btrfs/Makefile
+++ b/fs/btrfs/Makefile
@@ -10,7 +10,7 @@ btrfs-y += super.o ctree.o extent-tree.o print-tree.o root-tree.o dir-item.o \
export.o tree-log.o free-space-cache.o zlib.o lzo.o zstd.o \
compression.o delayed-ref.o relocation.o delayed-inode.o scrub.o \
reada.o backref.o ulist.o qgroup.o send.o dev-replace.o raid56.o \
- uuid-tree.o props.o free-space-tree.o tree-checker.o
+ uuid-tree.o props.o free-space-tree.o tree-checker.o encryption.o
btrfs-$(CONFIG_BTRFS_FS_POSIX_ACL) += acl.o
btrfs-$(CONFIG_BTRFS_FS_CHECK_INTEGRITY) += check-integrity.o
diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
index 3f3356a2d145..4bab57e01e21 100644
--- a/fs/btrfs/ctree.h
+++ b/fs/btrfs/ctree.h
@@ -2418,6 +2418,10 @@ BTRFS_SETGET_FUNCS(file_extent_other_encoding, struct btrfs_file_extent_item,
#define BTRFS_ENCRYPTION_KEY_ID_LENGTH 64
+struct btrfs_encryption_key_item {
+ u8 key_id[BTRFS_ENCRYPTION_KEY_ID_LENGTH];
+} __attribute__ ((__packed__));
+
/*
* this returns the number of bytes used by the item on disk, minus the
* size of any extent headers. If a file is compressed on disk, this is
diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c
index a1964b768750..2ecce2ce51be 100644
--- a/fs/btrfs/disk-io.c
+++ b/fs/btrfs/disk-io.c
@@ -2596,6 +2596,10 @@ int open_ctree(struct super_block *sb,
int clear_free_space_tree = 0;
int level;
+ err = crypto_get_default_rng();
+ if (err)
+ goto fail;
+
tree_root = fs_info->tree_root = btrfs_alloc_root(fs_info, GFP_KERNEL);
chunk_root = fs_info->chunk_root = btrfs_alloc_root(fs_info, GFP_KERNEL);
if (!tree_root || !chunk_root) {
@@ -4031,6 +4035,8 @@ void close_ctree(struct btrfs_fs_info *fs_info)
list_del_init(&key->key_list);
kfree(key);
}
+
+ crypto_put_default_rng();
}
int btrfs_buffer_uptodate(struct extent_buffer *buf, u64 parent_transid,
diff --git a/fs/btrfs/encryption.c b/fs/btrfs/encryption.c
new file mode 100644
index 000000000000..0803642c1ec9
--- /dev/null
+++ b/fs/btrfs/encryption.c
@@ -0,0 +1,58 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2019 Mark Harmstone. All rights reserved.
+ */
+
+#include <crypto/hash.h>
+#include "ctree.h"
+#include "encryption.h"
+
+int btrfs_get_key_id(u64 salt, char *password, unsigned int pwdlen,
+ char *key_id)
+{
+ int ret;
+ struct crypto_shash *shash;
+ struct shash_desc *desc;
+
+ shash = crypto_alloc_shash("sha512", 0, 0);
+ if (IS_ERR(shash))
+ return PTR_ERR(shash);
+
+ desc = kmalloc(sizeof(struct shash_desc) + crypto_shash_descsize(shash),
+ GFP_KERNEL);
+ if (!desc) {
+ ret = -ENOMEM;
+ goto free_shash;
+ }
+
+ desc->tfm = shash;
+ desc->flags = 0;
+
+ ret = crypto_shash_init(desc);
+ if (ret)
+ goto free_desc;
+
+ salt = cpu_to_le64(salt);
+
+ ret = crypto_shash_update(desc, (u8 *)&salt, sizeof(salt));
+ if (ret)
+ goto free_desc;
+
+ ret = crypto_shash_update(desc, password, pwdlen);
+ if (ret)
+ goto free_desc;
+
+ ret = crypto_shash_final(desc, key_id);
+ if (ret)
+ goto free_desc;
+
+ ret = 0;
+
+free_desc:
+ kzfree(desc);
+
+free_shash:
+ crypto_free_shash(shash);
+
+ return ret;
+}
diff --git a/fs/btrfs/encryption.h b/fs/btrfs/encryption.h
index 77914d7797c6..adf35696373f 100644
--- a/fs/btrfs/encryption.h
+++ b/fs/btrfs/encryption.h
@@ -30,4 +30,7 @@ struct btrfs_enc_key {
struct mutex lock;
};
+int btrfs_get_key_id(u64 salt, char *password, unsigned int pwdlen,
+ char *key_id);
+
#endif
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
index 802a628e9f7d..92fbed90dc4e 100644
--- a/fs/btrfs/ioctl.c
+++ b/fs/btrfs/ioctl.c
@@ -26,6 +26,7 @@
#include <linux/btrfs.h>
#include <linux/uaccess.h>
#include <linux/iversion.h>
+#include <crypto/rng.h>
#include "ctree.h"
#include "disk-io.h"
#include "transaction.h"
@@ -43,6 +44,7 @@
#include "qgroup.h"
#include "tree-log.h"
#include "compression.h"
+#include "encryption.h"
#ifdef CONFIG_64BIT
/* If we have a 32-bit userspace and 64-bit kernel, then the UAPI
@@ -5852,6 +5854,172 @@ static int _btrfs_ioctl_send(struct file *file, void __user *argp, bool compat)
return ret;
}
+static int btrfs_ioctl_get_key_salt(struct btrfs_fs_info *fs_info,
+ void __user *argp)
+{
+ struct btrfs_ioctl_get_key_salt_args args;
+ int ret;
+ u64 salt;
+ struct btrfs_enc_key *k = NULL, *k2;
+ char key_id[BTRFS_ENCRYPTION_KEY_ID_LENGTH];
+
+ if (copy_from_user(&args, argp, sizeof(args)))
+ return -EFAULT;
+
+ down_write(&fs_info->key_sem);
+
+ /* Search loaded keys */
+ list_for_each_entry(k2, &fs_info->key_list, key_list) {
+ ret = btrfs_get_key_id(k2->key_number, args.password,
+ strlen(args.password), key_id);
+ if (ret) {
+ up_write(&fs_info->key_sem);
+ return ret;
+ }
+
+ if (!memcmp(key_id, k2->key_id, sizeof(key_id))) {
+ k = k2;
+ break;
+ }
+ }
+
+ /* Not loaded - search tree */
+ if (!k && fs_info->key_root) {
+ struct btrfs_key key;
+ struct btrfs_path *path;
+
+ path = btrfs_alloc_path();
+ if (!path) {
+ up_write(&fs_info->key_sem);
+ return -ENOMEM;
+ }
+
+ key.objectid = 0;
+ key.type = BTRFS_ENCRYPTION_KEY;
+ key.offset = 0;
+
+ ret = btrfs_search_slot(NULL, fs_info->key_root, &key,
+ path, 0, 0);
+ if (ret < 0) {
+ up_write(&fs_info->key_sem);
+ btrfs_free_path(path);
+ return ret;
+ }
+
+ do {
+ struct extent_buffer *leaf;
+ int slot;
+ u32 item_size;
+ struct btrfs_encryption_key_item *item;
+ char key_id2[BTRFS_ENCRYPTION_KEY_ID_LENGTH];
+
+ leaf = path->nodes[0];
+ slot = path->slots[0];
+ btrfs_item_key_to_cpu(leaf, &key, slot);
+
+ if (key.type != BTRFS_ENCRYPTION_KEY)
+ goto skip;
+
+ item_size = btrfs_item_size_nr(leaf, slot);
+
+ if (item_size !=
+ sizeof(struct btrfs_encryption_key_item))
+ goto skip;
+
+ item = btrfs_item_ptr(leaf, path->slots[0],
+ struct btrfs_encryption_key_item);
+
+ salt = key.objectid;
+
+ ret = btrfs_get_key_id(salt, args.password,
+ strlen(args.password), key_id);
+ if (ret) {
+ up_write(&fs_info->key_sem);
+ btrfs_free_path(path);
+ return ret;
+ }
+
+ read_eb_member(leaf, item,
+ struct btrfs_encryption_key_item,
+ key_id, key_id2);
+
+ if (!memcmp(key_id, key_id2,
+ BTRFS_ENCRYPTION_KEY_ID_LENGTH)) {
+ k = kmalloc(sizeof(*k), GFP_KERNEL);
+ if (!k) {
+ up_write(&fs_info->key_sem);
+ btrfs_free_path(path);
+ return -ENOMEM;
+ }
+
+ memcpy(k->key_id, key_id,
+ BTRFS_ENCRYPTION_KEY_ID_LENGTH);
+ k->key_number = salt;
+ k->loaded = false;
+ k->added = false;
+ k->used = true;
+ mutex_init(&k->lock);
+
+ list_add(&k->key_list, &fs_info->key_list);
+ break;
+ }
+
+skip:
+ ret = btrfs_next_item(fs_info->key_root, path);
+ if (ret == 0)
+ continue;
+ else if (ret > 0)
+ ret = 0;
+ break;
+ } while (1);
+
+ btrfs_free_path(path);
+ }
+
+ /* Not found - allocate new key */
+ if (!k) {
+ ret = crypto_rng_get_bytes(crypto_default_rng, (u8 *)&salt,
+ sizeof(salt));
+
+ if (ret) {
+ up_write(&fs_info->key_sem);
+ return ret;
+ }
+
+ k = kmalloc(sizeof(*k), GFP_KERNEL);
+ if (!k) {
+ up_write(&fs_info->key_sem);
+ return -ENOMEM;
+ }
+
+ k->key_number = salt;
+
+ ret = btrfs_get_key_id(k->key_number, args.password,
+ strlen(args.password), k->key_id);
+ if (ret) {
+ up_write(&fs_info->key_sem);
+ kzfree(k);
+ return ret;
+ }
+
+ k->loaded = false;
+ k->added = true;
+ k->used = false;
+ mutex_init(&k->lock);
+
+ list_add(&k->key_list, &fs_info->key_list);
+ }
+
+ args.salt = k->key_number;
+
+ up_write(&fs_info->key_sem);
+
+ if (copy_to_user(argp, &args, sizeof(args)))
+ return -EFAULT;
+
+ return 0;
+}
+
long btrfs_ioctl(struct file *file, unsigned int
cmd, unsigned long arg)
{
@@ -5998,6 +6166,8 @@ long btrfs_ioctl(struct file *file, unsigned int
return btrfs_ioctl_get_subvol_rootref(file, argp);
case BTRFS_IOC_INO_LOOKUP_USER:
return btrfs_ioctl_ino_lookup_user(file, argp);
+ case BTRFS_IOC_GET_KEY_SALT:
+ return btrfs_ioctl_get_key_salt(fs_info, argp);
}
return -ENOTTY;
diff --git a/include/uapi/linux/btrfs.h b/include/uapi/linux/btrfs.h
index 6c785d5cfb4b..aef9b695a05c 100644
--- a/include/uapi/linux/btrfs.h
+++ b/include/uapi/linux/btrfs.h
@@ -817,6 +817,14 @@ struct btrfs_ioctl_get_subvol_rootref_args {
__u8 align[7];
};
+#define BTRFS_ENC_PASSWORD_MAX 255
+
+struct btrfs_ioctl_get_key_salt_args {
+ char password[BTRFS_ENC_PASSWORD_MAX]; /* in */
+
+ __u64 salt; /* out */
+};
+
/* Error codes as returned by the kernel */
enum btrfs_err_code {
BTRFS_ERROR_DEV_RAID1_MIN_NOT_MET = 1,
@@ -941,5 +949,7 @@ enum btrfs_err_code {
struct btrfs_ioctl_get_subvol_rootref_args)
#define BTRFS_IOC_INO_LOOKUP_USER _IOWR(BTRFS_IOCTL_MAGIC, 62, \
struct btrfs_ioctl_ino_lookup_user_args)
+#define BTRFS_IOC_GET_KEY_SALT _IOWR(BTRFS_IOCTL_MAGIC, 63, \
+ struct btrfs_ioctl_get_key_salt_args)
#endif /* _UAPI_LINUX_BTRFS_H */
--
2.19.2
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [RFC PATCH 07/19] btrfs: add new keys to key root when flushed
2019-01-09 1:26 [RFC PATCH 01/19] btrfs: add encryption structs and constants Mark Harmstone
` (4 preceding siblings ...)
2019-01-09 1:26 ` [RFC PATCH 06/19] btrfs: add ioctl BTRFS_IOC_GET_KEY_SALT Mark Harmstone
@ 2019-01-09 1:26 ` Mark Harmstone
2019-01-09 1:26 ` [RFC PATCH 08/19] btrfs: change extract in prop_handler to write into string Mark Harmstone
` (11 subsequent siblings)
17 siblings, 0 replies; 19+ messages in thread
From: Mark Harmstone @ 2019-01-09 1:26 UTC (permalink / raw)
Cc: mark, Chris Mason, Josef Bacik, David Sterba, linux-btrfs, linux-kernel
Signed-off-by: Mark Harmstone <mark@harmstone.com>
---
fs/btrfs/transaction.c | 79 +++++++++++++++++++++++++++++++++++++++++-
1 file changed, 78 insertions(+), 1 deletion(-)
diff --git a/fs/btrfs/transaction.c b/fs/btrfs/transaction.c
index d1eeef9ec5da..a6c8d49b6962 100644
--- a/fs/btrfs/transaction.c
+++ b/fs/btrfs/transaction.c
@@ -19,6 +19,7 @@
#include "volumes.h"
#include "dev-replace.h"
#include "qgroup.h"
+#include "encryption.h"
#define BTRFS_ROOT_TRANS_TAG 0
@@ -520,7 +521,7 @@ start_transaction(struct btrfs_root *root, unsigned int num_items,
* and then we deadlock with somebody doing a freeze.
*
* If we are ATTACH, it means we just want to catch the current
- * transaction and commit it, so we needn't do sb_start_intwrite().
+ * transaction and commit it, so we needn't do sb_start_intwrite().
*/
if (type & __TRANS_FREEZABLE)
sb_start_intwrite(fs_info->sb);
@@ -1918,12 +1919,74 @@ btrfs_wait_pending_ordered(struct btrfs_transaction *cur_trans)
atomic_read(&cur_trans->pending_ordered) == 0);
}
+static int flush_key(struct btrfs_trans_handle *trans,
+ struct btrfs_enc_key *k)
+{
+ struct btrfs_fs_info *fs_info = trans->fs_info;
+ struct btrfs_path *path;
+ struct btrfs_key key;
+ struct extent_buffer *leaf;
+ struct btrfs_encryption_key_item *item;
+ int ret;
+
+ path = btrfs_alloc_path();
+ if (!path)
+ return -ENOMEM;
+
+ path->leave_spinning = 1;
+
+ if (!fs_info->key_root) {
+ struct btrfs_root *root;
+
+ root = btrfs_create_tree(trans, fs_info,
+ BTRFS_KEY_TREE_OBJECTID);
+
+ if (IS_ERR(root)) {
+ btrfs_free_path(path);
+ return PTR_ERR(root);
+ }
+
+ fs_info->key_root = root;
+
+ btrfs_set_fs_incompat(fs_info, ENCRYPTION);
+ }
+
+ key.objectid = k->key_number;
+ key.type = BTRFS_ENCRYPTION_KEY;
+ key.offset = 0;
+
+ ret = btrfs_insert_empty_item(trans, fs_info->key_root, path, &key,
+ sizeof(struct btrfs_encryption_key_item));
+
+ if (ret) {
+ btrfs_free_path(path);
+ return ret;
+ }
+
+ leaf = path->nodes[0];
+
+ btrfs_mark_buffer_dirty(leaf);
+
+ item = btrfs_item_ptr(leaf, path->slots[0],
+ struct btrfs_encryption_key_item);
+
+ write_eb_member(leaf, item, struct btrfs_encryption_key_item,
+ key_id, k->key_id);
+
+ btrfs_free_path(path);
+
+ k->added = false;
+
+ return ret;
+}
+
int btrfs_commit_transaction(struct btrfs_trans_handle *trans)
{
struct btrfs_fs_info *fs_info = trans->fs_info;
struct btrfs_transaction *cur_trans = trans->transaction;
struct btrfs_transaction *prev_trans = NULL;
int ret;
+ struct btrfs_enc_key *key;
/* Stop the commit early if ->aborted is set */
if (unlikely(READ_ONCE(cur_trans->aborted))) {
@@ -2071,6 +2134,20 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans)
ret = cur_trans->aborted;
goto scrub_continue;
}
+
+ down_read(&fs_info->key_sem);
+
+ list_for_each_entry(key, &fs_info->key_list, key_list) {
+ if (key->added && key->used) {
+ ret = flush_key(trans, key);
+
+ if (ret)
+ goto scrub_continue;
+ }
+ }
+
+ up_read(&fs_info->key_sem);
+
/*
* the reloc mutex makes sure that we stop
* the balancing code from coming in and moving
--
2.19.2
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [RFC PATCH 08/19] btrfs: change extract in prop_handler to write into string
2019-01-09 1:26 [RFC PATCH 01/19] btrfs: add encryption structs and constants Mark Harmstone
` (5 preceding siblings ...)
2019-01-09 1:26 ` [RFC PATCH 07/19] btrfs: add new keys to key root when flushed Mark Harmstone
@ 2019-01-09 1:26 ` Mark Harmstone
2019-01-09 1:26 ` [RFC PATCH 09/19] btrfs: add btrfs.key property Mark Harmstone
` (10 subsequent siblings)
17 siblings, 0 replies; 19+ messages in thread
From: Mark Harmstone @ 2019-01-09 1:26 UTC (permalink / raw)
Cc: mark, Chris Mason, Josef Bacik, David Sterba, linux-btrfs, linux-kernel
Signed-off-by: Mark Harmstone <mark@harmstone.com>
---
fs/btrfs/props.c | 27 +++++++++++++++++++--------
1 file changed, 19 insertions(+), 8 deletions(-)
diff --git a/fs/btrfs/props.c b/fs/btrfs/props.c
index dc6140013ae8..e57a4edc88c4 100644
--- a/fs/btrfs/props.c
+++ b/fs/btrfs/props.c
@@ -19,7 +19,7 @@ struct prop_handler {
const char *xattr_name;
int (*validate)(const char *value, size_t len);
int (*apply)(struct inode *inode, const char *value, size_t len);
- const char *(*extract)(struct inode *inode);
+ int (*extract)(struct inode *inode, char *value);
int inheritable;
};
@@ -27,7 +27,7 @@ static int prop_compression_validate(const char *value, size_t len);
static int prop_compression_apply(struct inode *inode,
const char *value,
size_t len);
-static const char *prop_compression_extract(struct inode *inode);
+static int prop_compression_extract(struct inode *inode, char *value);
static struct prop_handler prop_handlers[] = {
{
@@ -298,14 +298,14 @@ static int inherit_props(struct btrfs_trans_handle *trans,
for (i = 0; i < ARRAY_SIZE(prop_handlers); i++) {
const struct prop_handler *h = &prop_handlers[i];
- const char *value;
+ char value[64];
u64 num_bytes;
if (!h->inheritable)
continue;
- value = h->extract(parent);
- if (!value)
+ ret = h->extract(parent, value);
+ if (ret)
continue;
num_bytes = btrfs_calc_trans_metadata_size(fs_info, 1);
@@ -410,18 +410,29 @@ static int prop_compression_apply(struct inode *inode,
return 0;
}
-static const char *prop_compression_extract(struct inode *inode)
+static int prop_compression_extract(struct inode *inode, char *value)
{
switch (BTRFS_I(inode)->prop_compress) {
case BTRFS_COMPRESS_ZLIB:
case BTRFS_COMPRESS_LZO:
case BTRFS_COMPRESS_ZSTD:
- return btrfs_compress_type2str(BTRFS_I(inode)->prop_compress);
+ {
+ const char *v;
+
+ v = btrfs_compress_type2str(BTRFS_I(inode)->prop_compress);
+
+ if (!v)
+ return -EINVAL;
+
+ strcpy(value, v);
+
+ return 0;
+ }
default:
break;
}
- return NULL;
+ return -EINVAL;
}
--
2.19.2
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [RFC PATCH 09/19] btrfs: add btrfs.key property
2019-01-09 1:26 [RFC PATCH 01/19] btrfs: add encryption structs and constants Mark Harmstone
` (6 preceding siblings ...)
2019-01-09 1:26 ` [RFC PATCH 08/19] btrfs: change extract in prop_handler to write into string Mark Harmstone
@ 2019-01-09 1:26 ` Mark Harmstone
2019-01-09 1:26 ` [RFC PATCH 10/19] btrfs: allow reading encrypted inline extents Mark Harmstone
` (9 subsequent siblings)
17 siblings, 0 replies; 19+ messages in thread
From: Mark Harmstone @ 2019-01-09 1:26 UTC (permalink / raw)
Cc: mark, Chris Mason, Josef Bacik, David Sterba, linux-btrfs, linux-kernel
Signed-off-by: Mark Harmstone <mark@harmstone.com>
---
fs/btrfs/btrfs_inode.h | 5 ++++
fs/btrfs/ctree.h | 1 +
fs/btrfs/inode.c | 1 +
fs/btrfs/props.c | 68 ++++++++++++++++++++++++++++++++++++++++++
4 files changed, 75 insertions(+)
diff --git a/fs/btrfs/btrfs_inode.h b/fs/btrfs/btrfs_inode.h
index 97d91e55b70a..cf9f50bbe4ef 100644
--- a/fs/btrfs/btrfs_inode.h
+++ b/fs/btrfs/btrfs_inode.h
@@ -175,6 +175,11 @@ struct btrfs_inode {
*/
unsigned defrag_compress;
+ /*
+ * Salt of preferred encryption key
+ */
+ u64 prop_key;
+
struct btrfs_delayed_node *delayed_node;
/* File creation time. */
diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
index 4bab57e01e21..a1368c5c1236 100644
--- a/fs/btrfs/ctree.h
+++ b/fs/btrfs/ctree.h
@@ -1453,6 +1453,7 @@ do { \
#define BTRFS_INODE_NOATIME (1 << 9)
#define BTRFS_INODE_DIRSYNC (1 << 10)
#define BTRFS_INODE_COMPRESS (1 << 11)
+#define BTRFS_INODE_ENCRYPT (1 << 12)
#define BTRFS_INODE_ROOT_ITEM_INIT (1 << 31)
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index 9ea4c6f0352f..1d1084cf9289 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -9167,6 +9167,7 @@ struct inode *btrfs_alloc_inode(struct super_block *sb)
ei->runtime_flags = 0;
ei->prop_compress = BTRFS_COMPRESS_NONE;
ei->defrag_compress = BTRFS_COMPRESS_NONE;
+ ei->prop_key = 0;
ei->delayed_node = NULL;
diff --git a/fs/btrfs/props.c b/fs/btrfs/props.c
index e57a4edc88c4..ebe04194acbd 100644
--- a/fs/btrfs/props.c
+++ b/fs/btrfs/props.c
@@ -29,6 +29,12 @@ static int prop_compression_apply(struct inode *inode,
size_t len);
static int prop_compression_extract(struct inode *inode, char *value);
+static int prop_key_validate(const char *value, size_t len);
+static int prop_key_apply(struct inode *inode,
+ const char *value,
+ size_t len);
+static int prop_key_extract(struct inode *inode, char *value);
+
static struct prop_handler prop_handlers[] = {
{
.xattr_name = XATTR_BTRFS_PREFIX "compression",
@@ -37,6 +43,13 @@ static struct prop_handler prop_handlers[] = {
.extract = prop_compression_extract,
.inheritable = 1
},
+ {
+ .xattr_name = XATTR_BTRFS_PREFIX "key",
+ .validate = prop_key_validate,
+ .apply = prop_key_apply,
+ .extract = prop_key_extract,
+ .inheritable = 1
+ },
};
void __init btrfs_props_init(void)
@@ -435,4 +448,59 @@ static int prop_compression_extract(struct inode *inode, char *value)
return -EINVAL;
}
+static int prop_key_validate(const char *value, size_t len)
+{
+ unsigned int i;
+
+ if (len != 16)
+ return -EINVAL;
+
+ for (i = 0; i < 16; i++) {
+ if (hex_to_bin(value[i]) == -1)
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int prop_key_apply(struct inode *inode,
+ const char *value,
+ size_t len)
+{
+ u64 salt;
+
+ if (len == 0) {
+ BTRFS_I(inode)->flags &= ~BTRFS_INODE_ENCRYPT;
+ BTRFS_I(inode)->prop_key = 0;
+
+ return 0;
+ }
+
+ if (len != 16)
+ return -EINVAL;
+
+ if (hex2bin((u8 *)&salt, value, len / 2))
+ return -EINVAL;
+
+ salt = be64_to_cpu(salt);
+
+ BTRFS_I(inode)->flags |= BTRFS_INODE_ENCRYPT;
+ BTRFS_I(inode)->prop_key = salt;
+
+ return 0;
+}
+
+static int prop_key_extract(struct inode *inode, char *value)
+{
+ u64 salt;
+
+ if (BTRFS_I(inode)->prop_key == 0)
+ return -EINVAL;
+
+ salt = cpu_to_be64(BTRFS_I(inode)->prop_key);
+ bin2hex(value, &salt, sizeof(salt));
+
+ value[16] = 0;
+
+ return 0;
+}
--
2.19.2
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [RFC PATCH 10/19] btrfs: allow reading encrypted inline extents
2019-01-09 1:26 [RFC PATCH 01/19] btrfs: add encryption structs and constants Mark Harmstone
` (7 preceding siblings ...)
2019-01-09 1:26 ` [RFC PATCH 09/19] btrfs: add btrfs.key property Mark Harmstone
@ 2019-01-09 1:26 ` Mark Harmstone
2019-01-09 1:26 ` [RFC PATCH 11/19] btrfs: allow writing " Mark Harmstone
` (8 subsequent siblings)
17 siblings, 0 replies; 19+ messages in thread
From: Mark Harmstone @ 2019-01-09 1:26 UTC (permalink / raw)
Cc: mark, Chris Mason, Josef Bacik, David Sterba, linux-btrfs, linux-kernel
Signed-off-by: Mark Harmstone <mark@harmstone.com>
---
fs/btrfs/encryption.c | 305 ++++++++++++++++++++++++++++++++++++++++++
fs/btrfs/encryption.h | 3 +
fs/btrfs/inode.c | 72 +++++++++-
3 files changed, 379 insertions(+), 1 deletion(-)
diff --git a/fs/btrfs/encryption.c b/fs/btrfs/encryption.c
index 0803642c1ec9..a1c9a982b009 100644
--- a/fs/btrfs/encryption.c
+++ b/fs/btrfs/encryption.c
@@ -4,9 +4,314 @@
*/
#include <crypto/hash.h>
+#include <keys/user-type.h>
#include "ctree.h"
#include "encryption.h"
+#define KEY_SIG_PREFIX "btrfs:"
+#define KEY_SIG_LENGTH (sizeof(KEY_SIG_PREFIX) - 1 + 16)
+
+#define AES256_KEY_LENGTH 32
+
+static int derive_aes_key(u64 salt, char *data, unsigned int datalen,
+ char *aeskey)
+{
+ int ret;
+ struct crypto_shash *shash;
+ struct shash_desc *desc;
+ char tmp[AES256_KEY_LENGTH];
+
+ shash = crypto_alloc_shash("sha256", 0, 0);
+ if (IS_ERR(shash))
+ return PTR_ERR(shash);
+
+ desc = kmalloc(sizeof(struct shash_desc) + crypto_shash_descsize(shash),
+ GFP_KERNEL);
+ if (!desc) {
+ ret = -ENOMEM;
+ goto free_shash;
+ }
+
+ desc->tfm = shash;
+ desc->flags = 0;
+
+ /* Get SHA256 of salted password */
+
+ ret = crypto_shash_init(desc);
+ if (ret)
+ goto free_desc;
+
+ salt = cpu_to_le64(salt);
+
+ ret = crypto_shash_update(desc, (u8 *)&salt, sizeof(salt));
+ if (ret)
+ goto free_desc;
+
+ ret = crypto_shash_update(desc, data, datalen);
+ if (ret)
+ goto free_desc;
+
+ ret = crypto_shash_final(desc, tmp);
+ if (ret)
+ goto free_desc;
+
+ /* SHA256 again */
+
+ ret = crypto_shash_init(desc);
+ if (ret)
+ goto free_desc;
+
+ ret = crypto_shash_update(desc, tmp, AES256_KEY_LENGTH);
+ if (ret)
+ goto free_desc;
+
+ ret = crypto_shash_final(desc, aeskey);
+ if (ret)
+ goto free_desc;
+
+ ret = 0;
+
+free_desc:
+ kzfree(desc);
+
+free_shash:
+ crypto_free_shash(shash);
+
+ return ret;
+}
+
+static int btrfs_load_key(struct btrfs_enc_key *k)
+{
+ struct key *key;
+ u64 salt;
+ char sig[KEY_SIG_LENGTH + 1];
+ char key_id[BTRFS_ENCRYPTION_KEY_ID_LENGTH];
+ char aeskey[AES256_KEY_LENGTH];
+ struct user_key_payload *upayload;
+ int ret;
+
+ if (k->loaded)
+ return 0;
+
+ salt = cpu_to_be64(k->key_number);
+
+ memcpy(sig, KEY_SIG_PREFIX, sizeof(KEY_SIG_PREFIX) - 1);
+ bin2hex(sig + sizeof(KEY_SIG_PREFIX) - 1, &salt, sizeof(u64));
+ sig[KEY_SIG_LENGTH] = 0;
+
+ key = request_key(&key_type_user, sig, NULL);
+
+ if (IS_ERR(key)) {
+ key = request_key(&key_type_logon, sig, NULL);
+
+ if (IS_ERR(key))
+ return -ENOKEY;
+ }
+
+ down_read(&key->sem);
+
+ upayload = user_key_payload_locked(key);
+ if (IS_ERR_OR_NULL(upayload)) {
+ ret = upayload ? PTR_ERR(upayload) : -EINVAL;
+ goto out_key_put;
+ }
+
+ ret = btrfs_get_key_id(k->key_number, upayload->data,
+ upayload->datalen, key_id);
+ if (ret)
+ goto out_key_put;
+
+ if (memcmp(key_id, k->key_id,
+ BTRFS_ENCRYPTION_KEY_ID_LENGTH)) {
+ ret = -EINVAL;
+ goto out_key_put;
+ }
+
+ k->skcipher = crypto_alloc_skcipher("ctr(aes)", 0, 0);
+ if (IS_ERR(k->skcipher)) {
+ ret = PTR_ERR(k->skcipher);
+ goto out_key_put;
+ }
+
+ ret = derive_aes_key(k->key_number, upayload->data,
+ upayload->datalen, aeskey);
+ if (ret) {
+ crypto_free_skcipher(k->skcipher);
+ goto out_key_put;
+ }
+
+ ret = crypto_skcipher_setkey(k->skcipher, aeskey, 32);
+ if (ret) {
+ crypto_free_skcipher(k->skcipher);
+ goto out_key_put;
+ }
+
+ k->loaded = true;
+
+ ret = 0;
+
+out_key_put:
+ up_read(&key->sem);
+
+ key_put(key);
+
+ return ret;
+}
+
+static int find_key(struct btrfs_fs_info *fs_info, u64 key_number,
+ struct btrfs_enc_key **key)
+{
+ int ret;
+ struct btrfs_enc_key *k = NULL, *k2;
+
+ down_write(&fs_info->key_sem);
+
+ list_for_each_entry(k2, &fs_info->key_list, key_list) {
+ if (k2->key_number == key_number) {
+ k = k2;
+ break;
+ }
+ }
+
+ if (!k && fs_info->key_root) {
+ struct btrfs_key key;
+ struct btrfs_path *path;
+ struct extent_buffer *leaf;
+ int slot;
+ u32 item_size;
+ struct btrfs_encryption_key_item *item;
+ char key_id[BTRFS_ENCRYPTION_KEY_ID_LENGTH];
+
+ path = btrfs_alloc_path();
+ if (!path) {
+ up_write(&fs_info->key_sem);
+ return -ENOMEM;
+ }
+
+ key.objectid = key_number;
+ key.type = BTRFS_ENCRYPTION_KEY;
+ key.offset = 0;
+
+ ret = btrfs_search_slot(NULL, fs_info->key_root, &key,
+ path, 0, 0);
+ if (ret < 0) {
+ btrfs_free_path(path);
+ up_write(&fs_info->key_sem);
+ return ret;
+ }
+
+ leaf = path->nodes[0];
+ slot = path->slots[0];
+ btrfs_item_key_to_cpu(leaf, &key, slot);
+
+ if (key.objectid != key_number ||
+ key.type != BTRFS_ENCRYPTION_KEY) {
+ btrfs_free_path(path);
+ up_write(&fs_info->key_sem);
+ return -ENOKEY;
+ }
+
+ item_size = btrfs_item_size_nr(leaf, slot);
+
+ if (item_size != sizeof(struct btrfs_encryption_key_item)) {
+ btrfs_free_path(path);
+ up_write(&fs_info->key_sem);
+ return -ENOKEY;
+ }
+
+ item = btrfs_item_ptr(leaf, path->slots[0],
+ struct btrfs_encryption_key_item);
+
+ read_eb_member(leaf, item,
+ struct btrfs_encryption_key_item,
+ key_id, key_id);
+
+ k = kmalloc(sizeof(*k), GFP_KERNEL);
+ if (!k) {
+ btrfs_free_path(path);
+ up_write(&fs_info->key_sem);
+ return -ENOMEM;
+ }
+
+ memcpy(k->key_id, key_id, BTRFS_ENCRYPTION_KEY_ID_LENGTH);
+ k->key_number = key.objectid;
+ k->loaded = false;
+ k->added = false;
+ k->used = true;
+ mutex_init(&k->lock);
+
+ list_add(&k->key_list, &fs_info->key_list);
+
+ btrfs_free_path(path);
+ }
+
+ up_write(&fs_info->key_sem);
+
+ if (!k)
+ return -ENOKEY;
+
+ if (!k->loaded) {
+ mutex_lock(&k->lock);
+ ret = btrfs_load_key(k);
+ mutex_unlock(&k->lock);
+
+ if (ret)
+ return ret;
+ }
+
+ *key = k;
+
+ return 0;
+}
+
+int btrfs_decrypt(struct btrfs_fs_info *fs_info,
+ unsigned char *data, size_t len)
+{
+ struct scatterlist sg;
+ struct skcipher_request *req = NULL;
+ int ret = -EFAULT;
+ u64 key_number;
+ struct btrfs_enc_key *key;
+ struct btrfs_file_extent_inline_enc *eienc;
+ char iv[BTRFS_ENCRYPTION_BLOCK_LENGTH];
+
+ if (len <= sizeof(struct btrfs_file_extent_inline_enc))
+ return -EINVAL;
+
+ eienc = (struct btrfs_file_extent_inline_enc *)data;
+
+ key_number = le64_to_cpu(eienc->key_number);
+
+ ret = find_key(fs_info, key_number, &key);
+ if (ret)
+ return ret;
+
+ memcpy(iv, eienc->iv, BTRFS_ENCRYPTION_BLOCK_LENGTH);
+
+ len -= sizeof(struct btrfs_file_extent_inline_enc);
+
+ sg_init_one(&sg, data + sizeof(struct btrfs_file_extent_inline_enc),
+ len);
+
+ req = skcipher_request_alloc(key->skcipher, GFP_KERNEL);
+ if (!req) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ skcipher_request_set_crypt(req, &sg, &sg, len, iv);
+
+ ret = crypto_skcipher_decrypt(req);
+ if (ret < 0)
+ goto out;
+
+out:
+ if (req)
+ skcipher_request_free(req);
+ return ret;
+}
+
+
int btrfs_get_key_id(u64 salt, char *password, unsigned int pwdlen,
char *key_id)
{
diff --git a/fs/btrfs/encryption.h b/fs/btrfs/encryption.h
index adf35696373f..add7ee6d879d 100644
--- a/fs/btrfs/encryption.h
+++ b/fs/btrfs/encryption.h
@@ -30,6 +30,9 @@ struct btrfs_enc_key {
struct mutex lock;
};
+int btrfs_decrypt(struct btrfs_fs_info *fs_info,
+ unsigned char *data, size_t len);
+
int btrfs_get_key_id(u64 salt, char *password, unsigned int pwdlen,
char *key_id);
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index 1d1084cf9289..202a7458584f 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -45,6 +45,7 @@
#include "props.h"
#include "qgroup.h"
#include "dedupe.h"
+#include "encryption.h"
struct btrfs_iget_args {
struct btrfs_key *location;
@@ -6757,6 +6758,66 @@ static noinline int uncompress_inline(struct btrfs_path *path,
return ret;
}
+static noinline int decrypt_inline(struct btrfs_fs_info *fs_info,
+ struct btrfs_path *path,
+ struct page *page,
+ size_t pg_offset, u64 extent_offset,
+ struct btrfs_file_extent_item *item,
+ int compress_type)
+{
+ int ret;
+ struct extent_buffer *leaf = path->nodes[0];
+ char *tmp;
+ size_t max_size;
+ unsigned long inline_size;
+ unsigned long ptr;
+ int encryption_type;
+
+ WARN_ON(pg_offset != 0);
+ encryption_type = btrfs_file_extent_encryption(leaf, item);
+ max_size = btrfs_file_extent_ram_bytes(leaf, item);
+ inline_size = btrfs_file_extent_inline_item_len(leaf,
+ btrfs_item_nr(path->slots[0]));
+ tmp = kmalloc(inline_size, GFP_NOFS);
+ if (!tmp)
+ return -ENOMEM;
+ ptr = btrfs_file_extent_inline_start(item);
+
+ read_extent_buffer(leaf, tmp, ptr, inline_size);
+
+ max_size = min_t(unsigned long, PAGE_SIZE, max_size);
+ ret = btrfs_decrypt(fs_info, tmp, inline_size);
+
+ if (ret)
+ goto end;
+
+ inline_size -= sizeof(struct btrfs_file_extent_inline_enc);
+
+ if (compress_type != BTRFS_COMPRESS_NONE) {
+
+ ret = btrfs_decompress(compress_type,
+ tmp + sizeof(struct btrfs_file_extent_inline_enc),
+ page, extent_offset, inline_size, max_size);
+ } else {
+ char *kaddr = kmap_atomic(page);
+
+ memcpy(kaddr + pg_offset,
+ tmp + sizeof(struct btrfs_file_extent_inline_enc) +
+ extent_offset,
+ min(max_size, inline_size));
+
+ if (max_size + pg_offset < PAGE_SIZE)
+ memset(kaddr + pg_offset + max_size, 0,
+ PAGE_SIZE - max_size - pg_offset);
+
+ kunmap_atomic(kaddr);
+ }
+
+end:
+ kfree(tmp);
+ return ret;
+}
+
/*
* a bit scary, this does extent mapping from logical file offset to the disk.
* the ugly parts come from merging extents from the disk with the in-ram
@@ -6932,7 +6993,16 @@ struct extent_map *btrfs_get_extent(struct btrfs_inode *inode,
btrfs_set_path_blocking(path);
if (!PageUptodate(page)) {
- if (btrfs_file_extent_compression(leaf, item) !=
+ if (btrfs_file_extent_encryption(leaf, item) !=
+ BTRFS_ENCRYPTION_NONE) {
+ ret = decrypt_inline(fs_info, path, page,
+ pg_offset, extent_offset, item,
+ btrfs_file_extent_compression(leaf, item));
+ if (ret) {
+ err = ret;
+ goto out;
+ }
+ } else if (btrfs_file_extent_compression(leaf, item) !=
BTRFS_COMPRESS_NONE) {
ret = uncompress_inline(path, page, pg_offset,
extent_offset, item);
--
2.19.2
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [RFC PATCH 11/19] btrfs: allow writing encrypted inline extents
2019-01-09 1:26 [RFC PATCH 01/19] btrfs: add encryption structs and constants Mark Harmstone
` (8 preceding siblings ...)
2019-01-09 1:26 ` [RFC PATCH 10/19] btrfs: allow reading encrypted inline extents Mark Harmstone
@ 2019-01-09 1:26 ` Mark Harmstone
2019-01-09 1:26 ` [RFC PATCH 12/19] btrfs: allow reading normal encrypted extents Mark Harmstone
` (7 subsequent siblings)
17 siblings, 0 replies; 19+ messages in thread
From: Mark Harmstone @ 2019-01-09 1:26 UTC (permalink / raw)
Cc: mark, Chris Mason, Josef Bacik, David Sterba, linux-btrfs, linux-kernel
Signed-off-by: Mark Harmstone <mark@harmstone.com>
---
fs/btrfs/ctree.h | 5 +-
fs/btrfs/encryption.c | 156 +++++++++++++++++++++++++++++++++++++++++-
fs/btrfs/encryption.h | 9 +++
fs/btrfs/inode.c | 99 +++++++++++++++++++++++----
fs/btrfs/ioctl.c | 2 +-
5 files changed, 254 insertions(+), 17 deletions(-)
diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
index a1368c5c1236..51fcc24047f8 100644
--- a/fs/btrfs/ctree.h
+++ b/fs/btrfs/ctree.h
@@ -2393,8 +2393,11 @@ btrfs_file_extent_inline_start(const struct btrfs_file_extent_item *e)
return (unsigned long)e + BTRFS_FILE_EXTENT_INLINE_DATA_START;
}
-static inline u32 btrfs_file_extent_calc_inline_size(u32 datasize)
+static inline u32 btrfs_file_extent_calc_inline_size(u32 datasize, bool enc)
{
+ if (enc)
+ datasize += sizeof(struct btrfs_file_extent_inline_enc);
+
return BTRFS_FILE_EXTENT_INLINE_DATA_START + datasize;
}
diff --git a/fs/btrfs/encryption.c b/fs/btrfs/encryption.c
index a1c9a982b009..81313c4378b4 100644
--- a/fs/btrfs/encryption.c
+++ b/fs/btrfs/encryption.c
@@ -6,6 +6,7 @@
#include <crypto/hash.h>
#include <keys/user-type.h>
#include "ctree.h"
+#include "btrfs_inode.h"
#include "encryption.h"
#define KEY_SIG_PREFIX "btrfs:"
@@ -80,7 +81,7 @@ static int derive_aes_key(u64 salt, char *data, unsigned int datalen,
return ret;
}
-static int btrfs_load_key(struct btrfs_enc_key *k)
+int btrfs_load_key(struct btrfs_enc_key *k)
{
struct key *key;
u64 salt;
@@ -361,3 +362,156 @@ int btrfs_get_key_id(u64 salt, char *password, unsigned int pwdlen,
return ret;
}
+
+void btrfs_find_inode_encryption_key(struct inode *inode,
+ struct btrfs_enc_key **key_ret)
+{
+ struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb);
+ struct btrfs_enc_key *k;
+
+ *key_ret = NULL;
+
+ if (BTRFS_I(inode)->prop_key == 0)
+ return;
+
+ down_write(&fs_info->key_sem);
+
+ list_for_each_entry(k, &fs_info->key_list, key_list) {
+ if (k->key_number == BTRFS_I(inode)->prop_key) {
+ *key_ret = k;
+ break;
+ }
+ }
+
+ if (!*key_ret && fs_info->key_root) {
+ struct btrfs_key key;
+ struct btrfs_path *path;
+ int ret;
+ struct extent_buffer *leaf;
+ int slot;
+ u32 item_size;
+ struct btrfs_encryption_key_item *item;
+ char key_id[BTRFS_ENCRYPTION_KEY_ID_LENGTH];
+
+ path = btrfs_alloc_path();
+ if (!path) {
+ up_write(&fs_info->key_sem);
+ return;
+ }
+
+ key.objectid = BTRFS_I(inode)->prop_key;
+ key.type = BTRFS_ENCRYPTION_KEY;
+ key.offset = 0;
+
+ ret = btrfs_search_slot(NULL, fs_info->key_root, &key,
+ path, 0, 0);
+ if (ret < 0) {
+ btrfs_free_path(path);
+ up_write(&fs_info->key_sem);
+ return;
+ }
+
+ leaf = path->nodes[0];
+ slot = path->slots[0];
+ btrfs_item_key_to_cpu(leaf, &key, slot);
+
+ if (key.objectid != BTRFS_I(inode)->prop_key ||
+ key.type != BTRFS_ENCRYPTION_KEY) {
+ btrfs_free_path(path);
+ up_write(&fs_info->key_sem);
+ return;
+ }
+
+ item_size = btrfs_item_size_nr(leaf, slot);
+
+ if (item_size != sizeof(struct btrfs_encryption_key_item)) {
+ btrfs_free_path(path);
+ up_write(&fs_info->key_sem);
+ }
+
+ item = btrfs_item_ptr(leaf, path->slots[0],
+ struct btrfs_encryption_key_item);
+
+ read_eb_member(leaf, item,
+ struct btrfs_encryption_key_item,
+ key_id, key_id);
+
+ k = kmalloc(sizeof(*k), GFP_KERNEL);
+ if (!k) {
+ btrfs_free_path(path);
+ up_write(&fs_info->key_sem);
+ return;
+ }
+
+ memcpy(k->key_id, key_id, BTRFS_ENCRYPTION_KEY_ID_LENGTH);
+ k->key_number = key.objectid;
+ k->loaded = false;
+ k->added = false;
+ k->used = true;
+ mutex_init(&k->lock);
+
+ list_add(&k->key_list, &fs_info->key_list);
+
+ btrfs_free_path(path);
+
+ *key_ret = k;
+ }
+
+ up_write(&fs_info->key_sem);
+}
+
+int btrfs_encrypt_inline(struct extent_buffer *eb, char *plaintext,
+ unsigned long start, unsigned long len,
+ struct btrfs_enc_key *key, char *iv)
+{
+ struct scatterlist sg;
+ struct scatterlist sg2;
+ struct skcipher_request *req = NULL;
+ int tmp_len;
+ char *tmp = NULL;
+ char ctr[BTRFS_ENCRYPTION_BLOCK_LENGTH];
+ int ret = -EFAULT;
+ struct btrfs_file_extent_inline_enc *eienc;
+
+ key->used = true;
+
+ tmp_len = len + sizeof(struct btrfs_file_extent_inline_enc);
+ tmp = kmalloc(tmp_len, GFP_KERNEL);
+ if (!tmp) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ memcpy(ctr, iv, BTRFS_ENCRYPTION_BLOCK_LENGTH);
+
+ sg_init_one(&sg, plaintext, len);
+ sg_init_one(&sg2, tmp + sizeof(struct btrfs_file_extent_inline_enc),
+ len);
+
+ req = skcipher_request_alloc(key->skcipher, GFP_KERNEL);
+ if (!req) {
+ pr_info("could not allocate skcipher request\n");
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ skcipher_request_set_crypt(req, &sg, &sg2, len, ctr);
+
+ ret = crypto_skcipher_decrypt(req);
+ if (ret < 0)
+ goto out;
+
+ eienc = (struct btrfs_file_extent_inline_enc *)tmp;
+
+ eienc->key_number = cpu_to_le64(key->key_number);
+ memcpy(eienc->iv, iv, BTRFS_ENCRYPTION_BLOCK_LENGTH);
+
+ write_extent_buffer(eb, tmp, start, tmp_len);
+
+out:
+ if (req)
+ skcipher_request_free(req);
+
+ kfree(tmp);
+ return ret;
+}
diff --git a/fs/btrfs/encryption.h b/fs/btrfs/encryption.h
index add7ee6d879d..dbc035a880a5 100644
--- a/fs/btrfs/encryption.h
+++ b/fs/btrfs/encryption.h
@@ -36,4 +36,13 @@ int btrfs_decrypt(struct btrfs_fs_info *fs_info,
int btrfs_get_key_id(u64 salt, char *password, unsigned int pwdlen,
char *key_id);
+void btrfs_find_inode_encryption_key(struct inode *inode,
+ struct btrfs_enc_key **key_ret);
+
+int btrfs_encrypt_inline(struct extent_buffer *eb, char *plaintext,
+ unsigned long start, unsigned long len,
+ struct btrfs_enc_key *key, char *iv);
+
+int btrfs_load_key(struct btrfs_enc_key *k);
+
#endif
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index 202a7458584f..7018a2169e3e 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -28,6 +28,7 @@
#include <linux/magic.h>
#include <linux/iversion.h>
#include <asm/unaligned.h>
+#include <crypto/rng.h>
#include "ctree.h"
#include "disk-io.h"
#include "transaction.h"
@@ -166,7 +167,8 @@ static int insert_inline_extent(struct btrfs_trans_handle *trans,
struct btrfs_root *root, struct inode *inode,
u64 start, size_t size, size_t compressed_size,
int compress_type,
- struct page **compressed_pages)
+ struct page **compressed_pages,
+ struct btrfs_enc_key *enc_key, char *iv)
{
struct extent_buffer *leaf;
struct page *page = NULL;
@@ -190,7 +192,8 @@ static int insert_inline_extent(struct btrfs_trans_handle *trans,
key.offset = start;
key.type = BTRFS_EXTENT_DATA_KEY;
- datasize = btrfs_file_extent_calc_inline_size(cur_size);
+ datasize = btrfs_file_extent_calc_inline_size(cur_size,
+ enc_key);
path->leave_spinning = 1;
ret = btrfs_insert_empty_item(trans, root, path, &key,
datasize);
@@ -202,11 +205,16 @@ static int insert_inline_extent(struct btrfs_trans_handle *trans,
struct btrfs_file_extent_item);
btrfs_set_file_extent_generation(leaf, ei, trans->transid);
btrfs_set_file_extent_type(leaf, ei, BTRFS_FILE_EXTENT_INLINE);
- btrfs_set_file_extent_encryption(leaf, ei, 0);
btrfs_set_file_extent_other_encoding(leaf, ei, 0);
btrfs_set_file_extent_ram_bytes(leaf, ei, size);
ptr = btrfs_file_extent_inline_start(ei);
+ if (enc_key)
+ btrfs_set_file_extent_encryption(leaf, ei,
+ BTRFS_ENCRYPTION_AES256CTR);
+ else
+ btrfs_set_file_extent_encryption(leaf, ei, 0);
+
if (compress_type != BTRFS_COMPRESS_NONE) {
struct page *cpage;
int i = 0;
@@ -231,7 +239,20 @@ static int insert_inline_extent(struct btrfs_trans_handle *trans,
btrfs_set_file_extent_compression(leaf, ei, 0);
kaddr = kmap_atomic(page);
offset = start & (PAGE_SIZE - 1);
- write_extent_buffer(leaf, kaddr + offset, ptr, size);
+
+ if (enc_key) {
+ ret = btrfs_encrypt_inline(leaf, kaddr + offset,
+ ptr, size, enc_key, iv);
+
+ if (ret) {
+ kunmap_atomic(kaddr);
+ put_page(page);
+ goto fail;
+ }
+ } else {
+ write_extent_buffer(leaf, kaddr + offset, ptr, size);
+ }
+
kunmap_atomic(kaddr);
put_page(page);
}
@@ -263,7 +284,8 @@ static int insert_inline_extent(struct btrfs_trans_handle *trans,
static noinline int cow_file_range_inline(struct inode *inode, u64 start,
u64 end, size_t compressed_size,
int compress_type,
- struct page **compressed_pages)
+ struct page **compressed_pages,
+ struct btrfs_enc_key *key, char *iv)
{
struct btrfs_root *root = BTRFS_I(inode)->root;
struct btrfs_fs_info *fs_info = root->fs_info;
@@ -304,10 +326,10 @@ static noinline int cow_file_range_inline(struct inode *inode, u64 start,
if (compressed_size && compressed_pages)
extent_item_size = btrfs_file_extent_calc_inline_size(
- compressed_size);
+ compressed_size, key);
else
extent_item_size = btrfs_file_extent_calc_inline_size(
- inline_len);
+ inline_len, key);
ret = __btrfs_drop_extents(trans, root, inode, path,
start, aligned_end, NULL,
@@ -322,7 +344,7 @@ static noinline int cow_file_range_inline(struct inode *inode, u64 start,
ret = insert_inline_extent(trans, path, extent_inserted,
root, inode, start,
inline_len, compressed_size,
- compress_type, compressed_pages);
+ compress_type, compressed_pages, key, iv);
if (ret && ret != -ENOSPC) {
btrfs_abort_transaction(trans, ret);
goto out;
@@ -408,6 +430,18 @@ static inline int inode_need_compress(struct inode *inode, u64 start, u64 end)
return 0;
}
+static inline int inode_need_encrypt(struct inode *inode)
+{
+ struct btrfs_enc_key *key;
+
+ if (!(BTRFS_I(inode)->flags & BTRFS_INODE_ENCRYPT))
+ return 0;
+
+ btrfs_find_inode_encryption_key(inode, &key);
+
+ return key ? 1 : 0;
+}
+
static inline void inode_should_defrag(struct btrfs_inode *inode,
u64 start, u64 end, u64 num_bytes, u64 small_write)
{
@@ -451,8 +485,10 @@ static noinline void compress_file_range(struct inode *inode,
unsigned long total_in = 0;
int i;
int will_compress;
+ struct btrfs_enc_key *key;
int compress_type = fs_info->compress_type;
int redirty = 0;
+ char iv[BTRFS_ENCRYPTION_BLOCK_LENGTH];
inode_should_defrag(BTRFS_I(inode), start, end, end - start + 1,
SZ_16K);
@@ -480,11 +516,13 @@ static noinline void compress_file_range(struct inode *inode,
total_compressed = actual_end - start;
+ btrfs_find_inode_encryption_key(inode, &key);
+
/*
* skip compression for a small file range(<=blocksize) that
* isn't an inline extent, since it doesn't save disk space at all.
*/
- if (total_compressed <= blocksize &&
+ if (!key && total_compressed <= blocksize &&
(start > 0 || end + 1 < BTRFS_I(inode)->disk_i_size))
goto cleanup_and_bail_uncompressed;
@@ -564,12 +602,14 @@ static noinline void compress_file_range(struct inode *inode,
* to make an uncompressed inline extent.
*/
ret = cow_file_range_inline(inode, start, end, 0,
- BTRFS_COMPRESS_NONE, NULL);
+ BTRFS_COMPRESS_NONE, NULL,
+ key, iv);
} else {
/* try making a compressed inline extent */
ret = cow_file_range_inline(inode, start, end,
total_compressed,
- compress_type, pages);
+ compress_type, pages,
+ key, iv);
}
if (ret <= 0) {
unsigned long clear_flags = EXTENT_DELALLOC |
@@ -952,6 +992,7 @@ static noinline int cow_file_range(struct inode *inode,
unsigned long page_ops;
bool extent_reserved = false;
int ret = 0;
+ struct btrfs_enc_key *key = NULL;
if (btrfs_is_free_space_inode(BTRFS_I(inode))) {
WARN_ON_ONCE(1);
@@ -965,10 +1006,23 @@ static noinline int cow_file_range(struct inode *inode,
inode_should_defrag(BTRFS_I(inode), start, end, num_bytes, SZ_64K);
+ if (inode_need_encrypt(inode))
+ btrfs_find_inode_encryption_key(inode, &key);
+
if (start == 0) {
+ char iv[BTRFS_ENCRYPTION_BLOCK_LENGTH];
+
+ if (key) {
+ ret = crypto_rng_get_bytes(crypto_default_rng, iv,
+ BTRFS_ENCRYPTION_BLOCK_LENGTH);
+
+ if (ret)
+ goto out_unlock;
+ }
+
/* lets try to make an inline extent */
ret = cow_file_range_inline(inode, start, end, 0,
- BTRFS_COMPRESS_NONE, NULL);
+ BTRFS_COMPRESS_NONE, NULL, key, iv);
if (ret == 0) {
/*
* We use DO_ACCOUNTING here because we need the
@@ -3755,6 +3809,19 @@ static int btrfs_read_locked_inode(struct inode *inode,
}
btrfs_sync_inode_flags_to_i_flags(inode);
+
+ if (BTRFS_I(inode)->prop_key != 0) {
+ struct btrfs_enc_key *key;
+
+ btrfs_find_inode_encryption_key(inode, &key);
+
+ if (key && !key->loaded) {
+ mutex_lock(&key->lock);
+ btrfs_load_key(key);
+ mutex_unlock(&key->lock);
+ }
+ }
+
return 0;
}
@@ -4677,7 +4744,8 @@ int btrfs_truncate_inode_items(struct btrfs_trans_handle *trans,
u32 size = (u32)(new_size - found_key.offset);
btrfs_set_file_extent_ram_bytes(leaf, fi, size);
- size = btrfs_file_extent_calc_inline_size(size);
+ size = btrfs_file_extent_calc_inline_size(size,
+ false);
btrfs_truncate_item(root->fs_info, path, size, 1);
} else if (!del_item) {
/*
@@ -6167,6 +6235,9 @@ static void btrfs_inherit_iflags(struct inode *inode, struct inode *dir)
BTRFS_I(inode)->flags |= BTRFS_INODE_NODATASUM;
}
+ if (flags & BTRFS_INODE_ENCRYPT)
+ BTRFS_I(inode)->flags |= BTRFS_INODE_ENCRYPT;
+
btrfs_sync_inode_flags_to_i_flags(inode);
}
@@ -10230,7 +10301,7 @@ static int btrfs_symlink(struct inode *dir, struct dentry *dentry,
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(name_len, false);
err = btrfs_insert_empty_item(trans, root, path, &key,
datasize);
if (err) {
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
index 92fbed90dc4e..6589656e3988 100644
--- a/fs/btrfs/ioctl.c
+++ b/fs/btrfs/ioctl.c
@@ -3851,7 +3851,7 @@ static int clone_copy_inline_extent(struct inode *dst,
return ret;
if (skip) {
- const u32 start = btrfs_file_extent_calc_inline_size(0);
+ const u32 start = btrfs_file_extent_calc_inline_size(0, false);
memmove(inline_data + start, inline_data + start + skip, datal);
}
--
2.19.2
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [RFC PATCH 12/19] btrfs: allow reading normal encrypted extents
2019-01-09 1:26 [RFC PATCH 01/19] btrfs: add encryption structs and constants Mark Harmstone
` (9 preceding siblings ...)
2019-01-09 1:26 ` [RFC PATCH 11/19] btrfs: allow writing " Mark Harmstone
@ 2019-01-09 1:26 ` Mark Harmstone
2019-01-09 1:26 ` [RFC PATCH 13/19] btrfs: allow writing normal and compressed " Mark Harmstone
` (6 subsequent siblings)
17 siblings, 0 replies; 19+ messages in thread
From: Mark Harmstone @ 2019-01-09 1:26 UTC (permalink / raw)
Cc: mark, Chris Mason, Josef Bacik, David Sterba, linux-btrfs, linux-kernel
Signed-off-by: Mark Harmstone <mark@harmstone.com>
---
fs/btrfs/ctree.h | 6 +++
fs/btrfs/encryption.c | 37 +++++++++++++++++
fs/btrfs/encryption.h | 3 ++
fs/btrfs/extent_io.c | 96 ++++++++++++++++++++++++++++++++++++++++---
fs/btrfs/extent_io.h | 2 +
fs/btrfs/extent_map.c | 16 +++++++-
fs/btrfs/extent_map.h | 5 +++
fs/btrfs/file-item.c | 15 ++++++-
fs/btrfs/inode.c | 10 ++++-
fs/btrfs/volumes.h | 2 +
10 files changed, 184 insertions(+), 8 deletions(-)
diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
index 51fcc24047f8..4c5b8e8a8580 100644
--- a/fs/btrfs/ctree.h
+++ b/fs/btrfs/ctree.h
@@ -2420,6 +2420,12 @@ BTRFS_SETGET_FUNCS(file_extent_encryption, struct btrfs_file_extent_item,
BTRFS_SETGET_FUNCS(file_extent_other_encoding, struct btrfs_file_extent_item,
other_encoding, 16);
+BTRFS_SETGET_FUNCS(file_extent_enc_key_number,
+ struct btrfs_file_extent_item_enc, key_number, 64);
+
+BTRFS_SETGET_FUNCS(file_extent_inline_enc_key_number,
+ struct btrfs_file_extent_inline_enc, key_number, 64);
+
#define BTRFS_ENCRYPTION_KEY_ID_LENGTH 64
struct btrfs_encryption_key_item {
diff --git a/fs/btrfs/encryption.c b/fs/btrfs/encryption.c
index 81313c4378b4..41c001339cc7 100644
--- a/fs/btrfs/encryption.c
+++ b/fs/btrfs/encryption.c
@@ -312,6 +312,43 @@ int btrfs_decrypt(struct btrfs_fs_info *fs_info,
return ret;
}
+int btrfs_decrypt_page(struct btrfs_fs_info *fs_info, struct page *page,
+ u64 key_number, char *iv)
+{
+ struct scatterlist sg;
+ struct skcipher_request *req = NULL;
+ char *kaddr;
+ int ret = -EFAULT;
+ struct btrfs_enc_key *key;
+
+ ret = find_key(fs_info, key_number, &key);
+ if (ret)
+ return ret;
+
+ req = skcipher_request_alloc(key->skcipher, GFP_KERNEL);
+ if (!req) {
+ pr_info("could not allocate skcipher request\n");
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ kaddr = kmap_atomic(page);
+
+ sg_init_one(&sg, kaddr, PAGE_SIZE);
+ skcipher_request_set_crypt(req, &sg, &sg, PAGE_SIZE, iv);
+
+ ret = crypto_skcipher_decrypt(req);
+
+ kunmap_atomic(kaddr);
+
+ if (ret < 0)
+ goto out;
+
+out:
+ if (req)
+ skcipher_request_free(req);
+ return ret;
+}
int btrfs_get_key_id(u64 salt, char *password, unsigned int pwdlen,
char *key_id)
diff --git a/fs/btrfs/encryption.h b/fs/btrfs/encryption.h
index dbc035a880a5..0d24dc51793c 100644
--- a/fs/btrfs/encryption.h
+++ b/fs/btrfs/encryption.h
@@ -33,6 +33,9 @@ struct btrfs_enc_key {
int btrfs_decrypt(struct btrfs_fs_info *fs_info,
unsigned char *data, size_t len);
+int btrfs_decrypt_page(struct btrfs_fs_info *fs_info, struct page *page,
+ u64 key_number, char *iv);
+
int btrfs_get_key_id(u64 salt, char *password, unsigned int pwdlen,
char *key_id);
diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c
index d228f706ff3e..73fb0af50da8 100644
--- a/fs/btrfs/extent_io.c
+++ b/fs/btrfs/extent_io.c
@@ -23,6 +23,7 @@
#include "rcu-string.h"
#include "backref.h"
#include "disk-io.h"
+#include "encryption.h"
static struct kmem_cache *extent_state_cache;
static struct kmem_cache *extent_buffer_cache;
@@ -2490,6 +2491,26 @@ endio_readpage_release_extent(struct extent_io_tree *tree, u64 start, u64 len,
unlock_extent_cached_atomic(tree, start, end, &cached);
}
+void btrfs_increment_iv(char *iv, u32 inc)
+{
+ u64 i1, ni1, i2;
+
+ if (inc == 0)
+ return;
+
+ i1 = be64_to_cpu(*(u64 *)(iv + sizeof(u64)));
+ ni1 = i1 + inc;
+
+ *(u64 *)(iv + sizeof(u64)) = cpu_to_be64(ni1);
+
+ if (ni1 > i1)
+ return;
+
+ i2 = be64_to_cpu(*(u64 *)iv);
+ i2++;
+ *(u64 *)iv = cpu_to_be64(i2);
+}
+
/*
* after a readpage IO is done, we need to:
* clear the uptodate bits on error
@@ -2606,11 +2627,32 @@ static void end_bio_extent_readpage(struct bio *bio)
pgoff_t end_index = i_size >> PAGE_SHIFT;
unsigned off;
+ if (io_bio->key_number != 0) {
+ char iv[BTRFS_ENCRYPTION_BLOCK_LENGTH];
+
+ memcpy(iv, io_bio->iv,
+ BTRFS_ENCRYPTION_BLOCK_LENGTH);
+ btrfs_increment_iv(iv,
+ i * PAGE_SIZE / BTRFS_ENCRYPTION_BLOCK_LENGTH);
+
+ ret = btrfs_decrypt_page(fs_info, page,
+ io_bio->key_number, iv);
+ } else {
+ ret = 0;
+ }
+
/* Zero out the end if this page straddles i_size */
off = i_size & (PAGE_SIZE-1);
if (page->index == end_index && off)
zero_user_segment(page, off, PAGE_SIZE);
- SetPageUptodate(page);
+
+ if (ret) {
+ uptodate = 0;
+ ClearPageUptodate(page);
+ SetPageError(page);
+ } else {
+ SetPageUptodate(page);
+ }
} else {
ClearPageUptodate(page);
SetPageError(page);
@@ -2752,6 +2794,8 @@ static int __must_check submit_one_bio(struct bio *bio, int mirror_num,
* @mirror_num: desired mirror to read/write
* @prev_bio_flags: flags of previous bio to see if we can merge the current one
* @bio_flags: flags of the current bio to see if we can merge them
+ * @key_number: number of the encryption key (0 if not encrypted)
+ * @iv: encryption initialization vector
*/
static int submit_extent_page(unsigned int opf, struct extent_io_tree *tree,
struct writeback_control *wbc,
@@ -2763,7 +2807,8 @@ static int submit_extent_page(unsigned int opf, struct extent_io_tree *tree,
int mirror_num,
unsigned long prev_bio_flags,
unsigned long bio_flags,
- bool force_bio_submit)
+ bool force_bio_submit, u64 key_number,
+ char *iv)
{
int ret = 0;
struct bio *bio;
@@ -2786,6 +2831,25 @@ static int submit_extent_page(unsigned int opf, struct extent_io_tree *tree,
bio, bio_flags))
can_merge = false;
+ if (prev_bio_flags == bio_flags && contig &&
+ can_merge && !force_bio_submit) {
+ struct btrfs_io_bio *io_bio = btrfs_io_bio(bio);
+
+ if (key_number != io_bio->key_number) {
+ can_merge = false;
+ } else if (key_number != 0) {
+ char iv2[BTRFS_ENCRYPTION_BLOCK_LENGTH];
+
+ memcpy(iv2, io_bio->iv,
+ BTRFS_ENCRYPTION_BLOCK_LENGTH);
+ btrfs_increment_iv(iv2, bio->bi_iter.bi_size /
+ BTRFS_ENCRYPTION_BLOCK_LENGTH);
+
+ can_merge = !memcmp(iv, iv2,
+ BTRFS_ENCRYPTION_BLOCK_LENGTH);
+ }
+ }
+
if (prev_bio_flags != bio_flags || !contig || !can_merge ||
force_bio_submit ||
bio_add_page(bio, page, page_size, pg_offset) < page_size) {
@@ -2813,6 +2877,13 @@ static int submit_extent_page(unsigned int opf, struct extent_io_tree *tree,
wbc_account_io(wbc, page, page_size);
}
+ if (key_number != 0) {
+ struct btrfs_io_bio *io_bio = btrfs_io_bio(bio);
+
+ io_bio->key_number = key_number;
+ memcpy(io_bio->iv, iv, BTRFS_ENCRYPTION_BLOCK_LENGTH);
+ }
+
*bio_ret = bio;
return ret;
@@ -2924,6 +2995,8 @@ static int __do_readpage(struct extent_io_tree *tree,
while (cur <= end) {
bool force_bio_submit = false;
u64 offset;
+ char iv[BTRFS_ENCRYPTION_BLOCK_LENGTH];
+ u64 key_number = 0;
if (cur >= last_byte) {
char *userpage;
@@ -2951,6 +3024,9 @@ static int __do_readpage(struct extent_io_tree *tree,
BUG_ON(extent_map_end(em) <= cur);
BUG_ON(end < cur);
+ if (test_bit(EXTENT_FLAG_ENCRYPTED, &em->flags))
+ this_bio_flag |= EXTENT_BIO_ENCRYPTED;
+
if (test_bit(EXTENT_FLAG_COMPRESSED, &em->flags)) {
this_bio_flag |= EXTENT_BIO_COMPRESSED;
extent_set_compress_type(&this_bio_flag,
@@ -3014,6 +3090,16 @@ static int __do_readpage(struct extent_io_tree *tree,
if (prev_em_start)
*prev_em_start = em->orig_start;
+ if (this_bio_flag & EXTENT_BIO_ENCRYPTED &&
+ !(this_bio_flag & EXTENT_BIO_COMPRESSED)) {
+ key_number = em->key_number;
+ memcpy(iv, em->iv, BTRFS_ENCRYPTION_BLOCK_LENGTH);
+
+ if (extent_offset != 0)
+ btrfs_increment_iv(iv,
+ extent_offset / BTRFS_ENCRYPTION_BLOCK_LENGTH);
+ }
+
free_extent_map(em);
em = NULL;
@@ -3061,7 +3147,7 @@ static int __do_readpage(struct extent_io_tree *tree,
end_bio_extent_readpage, mirror_num,
*bio_flags,
this_bio_flag,
- force_bio_submit);
+ force_bio_submit, key_number, iv);
if (!ret) {
nr++;
*bio_flags = this_bio_flag;
@@ -3425,7 +3511,7 @@ static noinline_for_stack int __extent_writepage_io(struct inode *inode,
page, offset, iosize, pg_offset,
bdev, &epd->bio,
end_bio_extent_writepage,
- 0, 0, 0, false);
+ 0, 0, 0, false, 0, NULL);
if (ret) {
SetPageError(page);
if (PageWriteback(page))
@@ -3738,7 +3824,7 @@ static noinline_for_stack int write_one_eb(struct extent_buffer *eb,
p, offset, PAGE_SIZE, 0, bdev,
&epd->bio,
end_bio_extent_buffer_writepage,
- 0, 0, 0, false);
+ 0, 0, 0, false, 0, NULL);
if (ret) {
set_btree_ioerr(p);
if (PageWriteback(p))
diff --git a/fs/btrfs/extent_io.h b/fs/btrfs/extent_io.h
index 369daa5d4f73..54a7d6864d05 100644
--- a/fs/btrfs/extent_io.h
+++ b/fs/btrfs/extent_io.h
@@ -35,6 +35,7 @@
* type for this bio
*/
#define EXTENT_BIO_COMPRESSED 1
+#define EXTENT_BIO_ENCRYPTED 2
#define EXTENT_BIO_FLAG_SHIFT 16
/* these are bit numbers for test/set bit */
@@ -553,5 +554,6 @@ u64 btrfs_find_lock_delalloc_range(struct inode *inode,
#endif
struct extent_buffer *alloc_test_extent_buffer(struct btrfs_fs_info *fs_info,
u64 start);
+void btrfs_increment_iv(char *iv, u32 inc);
#endif
diff --git a/fs/btrfs/extent_map.c b/fs/btrfs/extent_map.c
index 7eea8b6e2cd3..0cc0ffbe0453 100644
--- a/fs/btrfs/extent_map.c
+++ b/fs/btrfs/extent_map.c
@@ -6,7 +6,7 @@
#include "ctree.h"
#include "extent_map.h"
#include "compression.h"
-
+#include "extent_io.h"
static struct kmem_cache *extent_map_cache;
@@ -210,6 +210,20 @@ static int mergable_maps(struct extent_map *prev, struct extent_map *next)
if (!list_empty(&prev->list) || !list_empty(&next->list))
return 0;
+ if (prev->key_number != next->key_number)
+ return 0;
+
+ if (next->key_number != 0) {
+ char iv[BTRFS_ENCRYPTION_BLOCK_LENGTH];
+
+ memcpy(iv, prev->iv, BTRFS_ENCRYPTION_BLOCK_LENGTH);
+ btrfs_increment_iv(iv, prev->len /
+ BTRFS_ENCRYPTION_BLOCK_LENGTH);
+
+ if (memcmp(iv, next->iv, BTRFS_ENCRYPTION_BLOCK_LENGTH))
+ return 0;
+ }
+
if (extent_map_end(prev) == next->start &&
prev->flags == next->flags &&
prev->bdev == next->bdev &&
diff --git a/fs/btrfs/extent_map.h b/fs/btrfs/extent_map.h
index 31977ffd6190..2bc1087564a1 100644
--- a/fs/btrfs/extent_map.h
+++ b/fs/btrfs/extent_map.h
@@ -5,6 +5,7 @@
#include <linux/rbtree.h>
#include <linux/refcount.h>
+#include <linux/btrfs_tree.h>
#define EXTENT_MAP_LAST_BYTE ((u64)-4)
#define EXTENT_MAP_HOLE ((u64)-3)
@@ -14,6 +15,7 @@
/* bits for the flags field */
#define EXTENT_FLAG_PINNED 0 /* this entry not yet on disk, don't free it */
#define EXTENT_FLAG_COMPRESSED 1
+#define EXTENT_FLAG_ENCRYPTED 2
#define EXTENT_FLAG_PREALLOC 3 /* pre-allocated extent */
#define EXTENT_FLAG_LOGGING 4 /* Logging this extent */
#define EXTENT_FLAG_FILLING 5 /* Filling in a preallocated extent */
@@ -45,6 +47,9 @@ struct extent_map {
};
refcount_t refs;
unsigned int compress_type;
+ unsigned int encrypt_type;
+ u64 key_number;
+ char iv[BTRFS_ENCRYPTION_BLOCK_LENGTH];
struct list_head list;
};
diff --git a/fs/btrfs/file-item.c b/fs/btrfs/file-item.c
index ba74827beb32..a903997e67ba 100644
--- a/fs/btrfs/file-item.c
+++ b/fs/btrfs/file-item.c
@@ -13,6 +13,7 @@
#include "volumes.h"
#include "print-tree.h"
#include "compression.h"
+#include "encryption.h"
#define __MAX_CSUM_ITEMS(r, size) ((unsigned long)(((BTRFS_LEAF_DATA_SIZE(r) - \
sizeof(struct btrfs_item) * 2) / \
@@ -931,6 +932,7 @@ void btrfs_extent_item_to_extent_map(struct btrfs_inode *inode,
u64 bytenr;
u8 type = btrfs_file_extent_type(leaf, fi);
int compress_type = btrfs_file_extent_compression(leaf, fi);
+ int encrypt_type = btrfs_file_extent_encryption(leaf, fi);
em->bdev = fs_info->fs_devices->latest_bdev;
btrfs_item_key_to_cpu(leaf, &key, slot);
@@ -960,7 +962,18 @@ void btrfs_extent_item_to_extent_map(struct btrfs_inode *inode,
em->block_start = EXTENT_MAP_HOLE;
return;
}
- if (compress_type != BTRFS_COMPRESS_NONE) {
+
+ if (encrypt_type != BTRFS_ENCRYPTION_NONE) {
+ set_bit(EXTENT_FLAG_ENCRYPTED, &em->flags);
+ em->encrypt_type = encrypt_type;
+ em->block_start = bytenr;
+ em->block_len = em->orig_block_len;
+
+ if (compress_type != BTRFS_COMPRESS_NONE) {
+ set_bit(EXTENT_FLAG_COMPRESSED, &em->flags);
+ em->compress_type = compress_type;
+ }
+ } else if (compress_type != BTRFS_COMPRESS_NONE) {
set_bit(EXTENT_FLAG_COMPRESSED, &em->flags);
em->compress_type = compress_type;
em->block_start = bytenr;
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index 7018a2169e3e..52ea7d7c880b 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -1953,7 +1953,7 @@ int btrfs_merge_bio_hook(struct page *page, unsigned long offset,
u64 map_length;
int ret;
- if (bio_flags & EXTENT_BIO_COMPRESSED)
+ if (bio_flags & (EXTENT_BIO_COMPRESSED | EXTENT_BIO_ENCRYPTED))
return 0;
length = bio->bi_iter.bi_size;
@@ -7039,6 +7039,14 @@ struct extent_map *btrfs_get_extent(struct btrfs_inode *inode,
btrfs_extent_item_to_extent_map(inode, path, item,
new_inline, em);
+ if (btrfs_file_extent_type(leaf, item) != BTRFS_ENCRYPTION_NONE) {
+ em->key_number = btrfs_file_extent_enc_key_number(leaf,
+ (struct btrfs_file_extent_item_enc *)item);
+
+ read_eb_member(leaf, item, struct btrfs_file_extent_item_enc,
+ iv, em->iv);
+ }
+
if (found_type == BTRFS_FILE_EXTENT_REG ||
found_type == BTRFS_FILE_EXTENT_PREALLOC) {
goto insert;
diff --git a/fs/btrfs/volumes.h b/fs/btrfs/volumes.h
index aefce895e994..fc12c28bdd93 100644
--- a/fs/btrfs/volumes.h
+++ b/fs/btrfs/volumes.h
@@ -271,6 +271,8 @@ struct btrfs_io_bio {
u8 *csum_allocated;
btrfs_io_bio_end_io_t *end_io;
struct bvec_iter iter;
+ u64 key_number;
+ char iv[BTRFS_ENCRYPTION_BLOCK_LENGTH];
/*
* This member must come last, bio_alloc_bioset will allocate enough
* bytes for entire btrfs_io_bio but relies on bio being last.
--
2.19.2
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [RFC PATCH 13/19] btrfs: allow writing normal and compressed encrypted extents
2019-01-09 1:26 [RFC PATCH 01/19] btrfs: add encryption structs and constants Mark Harmstone
` (10 preceding siblings ...)
2019-01-09 1:26 ` [RFC PATCH 12/19] btrfs: allow reading normal encrypted extents Mark Harmstone
@ 2019-01-09 1:26 ` Mark Harmstone
2019-01-09 1:26 ` [RFC PATCH 14/19] btrfs: allow reading " Mark Harmstone
` (5 subsequent siblings)
17 siblings, 0 replies; 19+ messages in thread
From: Mark Harmstone @ 2019-01-09 1:26 UTC (permalink / raw)
Cc: mark, Chris Mason, Josef Bacik, David Sterba, linux-btrfs, linux-kernel
Signed-off-by: Mark Harmstone <mark@harmstone.com>
---
fs/btrfs/encryption.c | 125 ++++++++++++++++++++++++++++
fs/btrfs/encryption.h | 8 ++
fs/btrfs/extent_io.c | 20 +++--
fs/btrfs/inode.c | 180 ++++++++++++++++++++++++++++++++++------
fs/btrfs/ordered-data.c | 19 +++--
fs/btrfs/ordered-data.h | 12 ++-
6 files changed, 320 insertions(+), 44 deletions(-)
diff --git a/fs/btrfs/encryption.c b/fs/btrfs/encryption.c
index 41c001339cc7..2bf45c9f96fa 100644
--- a/fs/btrfs/encryption.c
+++ b/fs/btrfs/encryption.c
@@ -552,3 +552,128 @@ int btrfs_encrypt_inline(struct extent_buffer *eb, char *plaintext,
kfree(tmp);
return ret;
}
+
+static int btrfs_encrypt_page(char *src, char *dest,
+ struct btrfs_enc_key *key, char *iv)
+{
+ struct scatterlist sg;
+ struct scatterlist sg2;
+ struct skcipher_request *req = NULL;
+ int ret = -EFAULT;
+
+ req = skcipher_request_alloc(key->skcipher, GFP_KERNEL);
+ if (!req) {
+ pr_info("could not allocate skcipher request\n");
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ sg_init_one(&sg, src, PAGE_SIZE);
+ sg_init_one(&sg2, dest, PAGE_SIZE);
+ skcipher_request_set_crypt(req, &sg, &sg2, PAGE_SIZE, iv);
+
+ ret = crypto_skcipher_encrypt(req);
+
+ if (ret < 0)
+ goto out;
+
+out:
+ if (req)
+ skcipher_request_free(req);
+ return ret;
+}
+
+int btrfs_encrypt_pages(struct address_space *mapping, struct page **pages,
+ unsigned long *nr_pages, u64 start,
+ struct btrfs_enc_key *key, char *iv)
+{
+ int ret;
+ unsigned int i;
+ char ctr[BTRFS_ENCRYPTION_BLOCK_LENGTH];
+
+ if (!key->loaded) {
+ mutex_lock(&key->lock);
+ ret = btrfs_load_key(key);
+ mutex_unlock(&key->lock);
+
+ if (ret) {
+ *nr_pages = 0;
+ return ret;
+ }
+ }
+
+ key->used = true;
+
+ memcpy(ctr, iv, BTRFS_ENCRYPTION_BLOCK_LENGTH);
+
+ for (i = 0; i < *nr_pages; i++) {
+ struct page *in_page;
+ char *src, *dest;
+
+ in_page = find_get_page(mapping, start >> PAGE_SHIFT);
+
+ pages[i] = alloc_page(GFP_NOFS | __GFP_HIGHMEM);
+ if (!pages[i]) {
+ *nr_pages = i;
+ return -ENOMEM;
+ }
+
+ src = kmap(in_page);
+ dest = kmap(pages[i]);
+
+ ret = btrfs_encrypt_page(src, dest, key, ctr);
+
+ kunmap(pages[i]);
+ kunmap(in_page);
+
+ if (ret) {
+ *nr_pages = i;
+ return ret;
+ }
+
+ start += PAGE_SIZE;
+ }
+
+ return 0;
+}
+
+int btrfs_encrypt_compressed_pages(struct page **pages,
+ unsigned long *nr_pages,
+ struct btrfs_enc_key *key, char *iv)
+{
+ int ret;
+ unsigned int i;
+ char ctr[BTRFS_ENCRYPTION_BLOCK_LENGTH];
+
+ if (!key->loaded) {
+ mutex_lock(&key->lock);
+ ret = btrfs_load_key(key);
+ mutex_unlock(&key->lock);
+
+ if (ret) {
+ *nr_pages = 0;
+ return ret;
+ }
+ }
+
+ key->used = true;
+
+ memcpy(ctr, iv, BTRFS_ENCRYPTION_BLOCK_LENGTH);
+
+ for (i = 0; i < *nr_pages; i++) {
+ char *buf;
+
+ buf = kmap(pages[i]);
+
+ ret = btrfs_encrypt_page(buf, buf, key, ctr);
+
+ kunmap(pages[i]);
+
+ if (ret) {
+ *nr_pages = i;
+ return ret;
+ }
+ }
+
+ return 0;
+}
diff --git a/fs/btrfs/encryption.h b/fs/btrfs/encryption.h
index 0d24dc51793c..cf10859fafe1 100644
--- a/fs/btrfs/encryption.h
+++ b/fs/btrfs/encryption.h
@@ -46,6 +46,14 @@ int btrfs_encrypt_inline(struct extent_buffer *eb, char *plaintext,
unsigned long start, unsigned long len,
struct btrfs_enc_key *key, char *iv);
+int btrfs_encrypt_pages(struct address_space *mapping, struct page **pages,
+ unsigned long *nr_pages, u64 start,
+ struct btrfs_enc_key *key, char *iv);
+
+int btrfs_encrypt_compressed_pages(struct page **pages,
+ unsigned long *nr_pages,
+ struct btrfs_enc_key *key, char *iv);
+
int btrfs_load_key(struct btrfs_enc_key *k);
#endif
diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c
index 73fb0af50da8..92bc9924c001 100644
--- a/fs/btrfs/extent_io.c
+++ b/fs/btrfs/extent_io.c
@@ -3407,7 +3407,7 @@ static noinline_for_stack int __extent_writepage_io(struct inode *inode,
size_t blocksize;
int ret = 0;
int nr = 0;
- bool compressed;
+ bool compressed, encrypted;
if (tree->ops && tree->ops->writepage_start_hook) {
ret = tree->ops->writepage_start_hook(page, start,
@@ -3469,28 +3469,30 @@ static noinline_for_stack int __extent_writepage_io(struct inode *inode,
bdev = em->bdev;
block_start = em->block_start;
compressed = test_bit(EXTENT_FLAG_COMPRESSED, &em->flags);
+ encrypted = test_bit(EXTENT_FLAG_ENCRYPTED, &em->flags);
free_extent_map(em);
em = NULL;
/*
- * compressed and inline extents are written through other
- * paths in the FS
+ * compressed, encrypted, or inline extents are written through
+ * other paths in the FS
*/
- if (compressed || block_start == EXTENT_MAP_HOLE ||
- block_start == EXTENT_MAP_INLINE) {
+ if (compressed || encrypted ||
+ block_start == EXTENT_MAP_HOLE ||
+ block_start == EXTENT_MAP_INLINE) {
/*
* end_io notification does not happen here for
* compressed extents
*/
- if (!compressed && tree->ops &&
+ if (!compressed && !encrypted && tree->ops &&
tree->ops->writepage_end_io_hook)
tree->ops->writepage_end_io_hook(page, cur,
cur + iosize - 1,
NULL, 1);
- else if (compressed) {
+ else if (compressed || encrypted) {
/* we don't want to end_page_writeback on
- * a compressed extent. this happens
- * elsewhere
+ * a compressed or encrypted extent.
+ * This happens elsewhere
*/
nr++;
}
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index 52ea7d7c880b..61481833f5e4 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -97,7 +97,8 @@ static struct extent_map *create_io_em(struct inode *inode, u64 start, u64 len,
u64 orig_start, u64 block_start,
u64 block_len, u64 orig_block_len,
u64 ram_bytes, int compress_type,
- int type);
+ int type, struct btrfs_enc_key *enc_key,
+ char *iv);
static void __endio_write_update_ordered(struct inode *inode,
const u64 offset, const u64 bytes,
@@ -375,6 +376,8 @@ struct async_extent {
struct page **pages;
unsigned long nr_pages;
int compress_type;
+ struct btrfs_enc_key *key;
+ char iv[BTRFS_ENCRYPTION_BLOCK_LENGTH];
struct list_head list;
};
@@ -394,7 +397,8 @@ static noinline int add_async_extent(struct async_cow *cow,
u64 compressed_size,
struct page **pages,
unsigned long nr_pages,
- int compress_type)
+ int compress_type,
+ struct btrfs_enc_key *key, char *iv)
{
struct async_extent *async_extent;
@@ -406,6 +410,11 @@ static noinline int add_async_extent(struct async_cow *cow,
async_extent->pages = pages;
async_extent->nr_pages = nr_pages;
async_extent->compress_type = compress_type;
+ async_extent->key = key;
+
+ if (key)
+ memcpy(async_extent->iv, iv, BTRFS_ENCRYPTION_BLOCK_LENGTH);
+
list_add_tail(&async_extent->list, &cow->extents);
return 0;
}
@@ -498,8 +507,6 @@ static noinline void compress_file_range(struct inode *inode,
will_compress = 0;
nr_pages = (end >> PAGE_SHIFT) - (start >> PAGE_SHIFT) + 1;
BUILD_BUG_ON((BTRFS_MAX_COMPRESSED % PAGE_SIZE) != 0);
- nr_pages = min_t(unsigned long, nr_pages,
- BTRFS_MAX_COMPRESSED / PAGE_SIZE);
/*
* we don't want to send crud past the end of i_size through
@@ -526,8 +533,6 @@ static noinline void compress_file_range(struct inode *inode,
(start > 0 || end + 1 < BTRFS_I(inode)->disk_i_size))
goto cleanup_and_bail_uncompressed;
- total_compressed = min_t(unsigned long, total_compressed,
- BTRFS_MAX_UNCOMPRESSED);
total_in = 0;
ret = 0;
@@ -537,6 +542,8 @@ static noinline void compress_file_range(struct inode *inode,
* change at any time if we discover bad compression ratios.
*/
if (inode_need_compress(inode, start, end)) {
+ nr_pages = min_t(unsigned long, nr_pages,
+ BTRFS_MAX_COMPRESSED / PAGE_SIZE);
WARN_ON(pages);
pages = kcalloc(nr_pages, sizeof(struct page *), GFP_NOFS);
if (!pages) {
@@ -567,6 +574,9 @@ static noinline void compress_file_range(struct inode *inode,
redirty = 1;
}
+ total_compressed = min_t(unsigned long, total_compressed,
+ BTRFS_MAX_UNCOMPRESSED);
+
/* Compression level is applied here and only here */
ret = btrfs_compress_pages(
compress_type | (fs_info->compress_level << 4),
@@ -594,6 +604,37 @@ static noinline void compress_file_range(struct inode *inode,
will_compress = 1;
}
}
+
+ if (key) {
+ if (!pages) {
+ pages = kcalloc(nr_pages,
+ sizeof(struct page *), GFP_NOFS);
+ if (!pages)
+ goto cont;
+ }
+
+ if (!redirty) {
+ extent_range_clear_dirty_for_io(inode, start, end);
+ redirty = 1;
+ }
+
+ ret = crypto_rng_get_bytes(crypto_default_rng, iv,
+ BTRFS_ENCRYPTION_BLOCK_LENGTH);
+
+ if (ret)
+ goto cont;
+
+ if (will_compress) {
+ ret = btrfs_encrypt_compressed_pages(pages, &nr_pages,
+ key, iv);
+ } else {
+ ret = btrfs_encrypt_pages(inode->i_mapping, pages,
+ &nr_pages, start, key, iv);
+ }
+
+ if (ret)
+ key = NULL;
+ }
cont:
if (start == 0) {
/* lets try to make an inline extent */
@@ -664,7 +705,7 @@ static noinline void compress_file_range(struct inode *inode,
*/
add_async_extent(async_cow, start, total_in,
total_compressed, pages, nr_pages,
- compress_type);
+ compress_type, key, iv);
if (start + total_in < end) {
start += total_in;
@@ -675,6 +716,29 @@ static noinline void compress_file_range(struct inode *inode,
return;
}
}
+
+ if (key) {
+ total_in = ALIGN(end - start, PAGE_SIZE);
+
+ if (total_in == 0)
+ return;
+
+ total_compressed = ALIGN(total_compressed, blocksize);
+
+ *num_added += 1;
+
+ if (!will_compress) {
+ compress_type = BTRFS_COMPRESS_NONE;
+ total_compressed = total_in;
+ }
+
+ add_async_extent(async_cow, start, total_in,
+ total_compressed, pages, nr_pages,
+ compress_type, key, iv);
+
+ return;
+ }
+
if (pages) {
/*
* the compression code ran but failed to make things smaller,
@@ -710,7 +774,7 @@ static noinline void compress_file_range(struct inode *inode,
if (redirty)
extent_range_redirty_for_io(inode, start, end);
add_async_extent(async_cow, start, end - start + 1, 0, NULL, 0,
- BTRFS_COMPRESS_NONE);
+ BTRFS_COMPRESS_NONE, NULL, NULL);
*num_added += 1;
return;
@@ -848,7 +912,8 @@ static noinline void submit_compressed_extents(struct inode *inode,
ins.offset, /* orig_block_len */
async_extent->ram_size, /* ram_bytes */
async_extent->compress_type,
- BTRFS_ORDERED_COMPRESSED);
+ BTRFS_ORDERED_COMPRESSED,
+ async_extent->key, async_extent->iv);
if (IS_ERR(em))
/* ret value is not necessary due to void function */
goto out_free_reserve;
@@ -860,7 +925,9 @@ static noinline void submit_compressed_extents(struct inode *inode,
async_extent->ram_size,
ins.offset,
BTRFS_ORDERED_COMPRESSED,
- async_extent->compress_type);
+ async_extent->compress_type,
+ async_extent->key,
+ async_extent->iv);
if (ret) {
btrfs_drop_extent_cache(BTRFS_I(inode),
async_extent->start,
@@ -1051,6 +1118,8 @@ static noinline int cow_file_range(struct inode *inode,
start + num_bytes - 1, 0);
while (num_bytes > 0) {
+ char iv[BTRFS_ENCRYPTION_BLOCK_LENGTH];
+
cur_alloc_size = num_bytes;
ret = btrfs_reserve_extent(root, cur_alloc_size, cur_alloc_size,
fs_info->sectorsize, 0, alloc_hint,
@@ -1060,6 +1129,14 @@ static noinline int cow_file_range(struct inode *inode,
cur_alloc_size = ins.offset;
extent_reserved = true;
+ if (key) {
+ ret = crypto_rng_get_bytes(crypto_default_rng, iv,
+ BTRFS_ENCRYPTION_BLOCK_LENGTH);
+
+ if (ret)
+ goto out_reserve;
+ }
+
ram_size = ins.offset;
em = create_io_em(inode, start, ins.offset, /* len */
start, /* orig_start */
@@ -1068,7 +1145,8 @@ static noinline int cow_file_range(struct inode *inode,
ins.offset, /* orig_block_len */
ram_size, /* ram_bytes */
BTRFS_COMPRESS_NONE, /* compress_type */
- BTRFS_ORDERED_REGULAR /* type */);
+ BTRFS_ORDERED_REGULAR /* type */,
+ key, iv);
if (IS_ERR(em)) {
ret = PTR_ERR(em);
goto out_reserve;
@@ -1076,7 +1154,8 @@ static noinline int cow_file_range(struct inode *inode,
free_extent_map(em);
ret = btrfs_add_ordered_extent(inode, start, ins.objectid,
- ram_size, cur_alloc_size, 0);
+ ram_size, cur_alloc_size, 0,
+ key, iv);
if (ret)
goto out_drop_extent_cache;
@@ -1249,8 +1328,10 @@ static int cow_file_range_async(struct inode *inode, struct page *locked_page,
async_cow->start = start;
async_cow->write_flags = write_flags;
- if (BTRFS_I(inode)->flags & BTRFS_INODE_NOCOMPRESS &&
- !btrfs_test_opt(fs_info, FORCE_COMPRESS))
+ if ((inode_need_encrypt(inode) &&
+ !inode_need_compress(inode, start, end)) ||
+ (BTRFS_I(inode)->flags & BTRFS_INODE_NOCOMPRESS &&
+ !btrfs_test_opt(fs_info, FORCE_COMPRESS)))
cur_end = end;
else
cur_end = min(end, start + SZ_512K - 1);
@@ -1532,7 +1613,7 @@ static noinline int run_delalloc_nocow(struct inode *inode,
num_bytes, /* block_len */
disk_num_bytes, /* orig_block_len */
ram_bytes, BTRFS_COMPRESS_NONE,
- BTRFS_ORDERED_PREALLOC);
+ BTRFS_ORDERED_PREALLOC, NULL, NULL);
if (IS_ERR(em)) {
if (nocow)
btrfs_dec_nocow_writers(fs_info,
@@ -1550,7 +1631,8 @@ static noinline int run_delalloc_nocow(struct inode *inode,
}
ret = btrfs_add_ordered_extent(inode, cur_offset, disk_bytenr,
- num_bytes, num_bytes, type);
+ num_bytes, num_bytes, type,
+ NULL, NULL);
if (nocow)
btrfs_dec_nocow_writers(fs_info, disk_bytenr);
BUG_ON(ret); /* -ENOMEM */
@@ -1642,14 +1724,17 @@ static int run_delalloc_range(void *private_data, struct page *locked_page,
int ret;
int force_cow = need_force_cow(inode, start, end);
unsigned int write_flags = wbc_to_write_flags(wbc);
+ bool encrypt = inode_need_encrypt(inode);
- if (BTRFS_I(inode)->flags & BTRFS_INODE_NODATACOW && !force_cow) {
+ if (BTRFS_I(inode)->flags & BTRFS_INODE_NODATACOW && !force_cow &&
+ !encrypt) {
ret = run_delalloc_nocow(inode, locked_page, start, end,
page_started, 1, nr_written);
- } else if (BTRFS_I(inode)->flags & BTRFS_INODE_PREALLOC && !force_cow) {
+ } else if (BTRFS_I(inode)->flags & BTRFS_INODE_PREALLOC &&
+ !force_cow && !encrypt) {
ret = run_delalloc_nocow(inode, locked_page, start, end,
page_started, 0, nr_written);
- } else if (!inode_need_compress(inode, start, end)) {
+ } else if (!inode_need_compress(inode, start, end) && !encrypt) {
ret = cow_file_range(inode, locked_page, start, end, end,
page_started, nr_written, 1, NULL);
} else {
@@ -2238,7 +2323,8 @@ static int insert_reserved_file_extent(struct btrfs_trans_handle *trans,
u64 disk_bytenr, u64 disk_num_bytes,
u64 num_bytes, u64 ram_bytes,
u8 compression, u8 encryption,
- u16 other_encoding, int extent_type)
+ u16 other_encoding, int extent_type,
+ u64 key_number, char *iv)
{
struct btrfs_root *root = BTRFS_I(inode)->root;
struct btrfs_file_extent_item *fi;
@@ -2248,11 +2334,17 @@ static int insert_reserved_file_extent(struct btrfs_trans_handle *trans,
u64 qg_released;
int extent_inserted = 0;
int ret;
+ size_t item_size;
path = btrfs_alloc_path();
if (!path)
return -ENOMEM;
+ if (encryption != BTRFS_ENCRYPTION_NONE)
+ item_size = sizeof(struct btrfs_file_extent_item_enc);
+ else
+ item_size = sizeof(*fi);
+
/*
* we may be replacing one extent in the tree with another.
* The new extent is pinned in the extent map, and we don't want
@@ -2264,7 +2356,7 @@ static int insert_reserved_file_extent(struct btrfs_trans_handle *trans,
*/
ret = __btrfs_drop_extents(trans, root, inode, path, file_pos,
file_pos + num_bytes, NULL, 0,
- 1, sizeof(*fi), &extent_inserted);
+ 1, item_size, &extent_inserted);
if (ret)
goto out;
@@ -2275,7 +2367,7 @@ static int insert_reserved_file_extent(struct btrfs_trans_handle *trans,
path->leave_spinning = 1;
ret = btrfs_insert_empty_item(trans, root, path, &ins,
- sizeof(*fi));
+ item_size);
if (ret)
goto out;
}
@@ -2293,6 +2385,17 @@ static int insert_reserved_file_extent(struct btrfs_trans_handle *trans,
btrfs_set_file_extent_encryption(leaf, fi, encryption);
btrfs_set_file_extent_other_encoding(leaf, fi, other_encoding);
+ if (encryption != BTRFS_ENCRYPTION_NONE) {
+ struct btrfs_file_extent_item_enc *fi_enc;
+
+ fi_enc = (struct btrfs_file_extent_item_enc *)fi;
+
+ btrfs_set_file_extent_enc_key_number(leaf, fi_enc, key_number);
+
+ write_eb_member(leaf, fi_enc, struct btrfs_file_extent_item_enc,
+ iv, iv);
+ }
+
btrfs_mark_buffer_dirty(leaf);
btrfs_release_path(path);
@@ -3091,14 +3194,27 @@ static int btrfs_finish_ordered_io(struct btrfs_ordered_extent *ordered_extent)
ordered_extent->file_offset +
logical_len);
} else {
+ u32 encrypt_type;
+ u64 key_number;
+
+ if (ordered_extent->key) {
+ encrypt_type = BTRFS_ENCRYPTION_AES256CTR;
+ key_number = ordered_extent->key->key_number;
+ } else {
+ encrypt_type = BTRFS_ENCRYPTION_NONE;
+ key_number = 0;
+ }
+
BUG_ON(root == fs_info->tree_root);
ret = insert_reserved_file_extent(trans, inode,
ordered_extent->file_offset,
ordered_extent->start,
ordered_extent->disk_len,
logical_len, logical_len,
- compress_type, 0, 0,
- BTRFS_FILE_EXTENT_REG);
+ compress_type, encrypt_type, 0,
+ BTRFS_FILE_EXTENT_REG,
+ key_number,
+ ordered_extent->iv);
if (!ret) {
clear_reserved_extent = false;
btrfs_release_delalloc_bytes(fs_info,
@@ -7283,7 +7399,7 @@ static struct extent_map *btrfs_create_dio_extent(struct inode *inode,
block_start, block_len, orig_block_len,
ram_bytes,
BTRFS_COMPRESS_NONE, /* compress_type */
- type);
+ type, NULL, NULL);
if (IS_ERR(em))
goto out;
}
@@ -7562,7 +7678,8 @@ static struct extent_map *create_io_em(struct inode *inode, u64 start, u64 len,
u64 orig_start, u64 block_start,
u64 block_len, u64 orig_block_len,
u64 ram_bytes, int compress_type,
- int type)
+ int type, struct btrfs_enc_key *enc_key,
+ char *iv)
{
struct extent_map_tree *em_tree;
struct extent_map *em;
@@ -7596,6 +7713,14 @@ static struct extent_map *create_io_em(struct inode *inode, u64 start, u64 len,
em->compress_type = compress_type;
}
+ if (enc_key) {
+ em->encrypt_type = BTRFS_ENCRYPTION_AES256CTR;
+ em->key_number = enc_key->key_number;
+ memcpy(em->iv, iv, BTRFS_ENCRYPTION_BLOCK_LENGTH);
+ } else {
+ em->encrypt_type = BTRFS_ENCRYPTION_NONE;
+ }
+
do {
btrfs_drop_extent_cache(BTRFS_I(inode), em->start,
em->start + em->len - 1, 0);
@@ -10413,7 +10538,8 @@ static int __btrfs_prealloc_file_range(struct inode *inode, int mode,
cur_offset, ins.objectid,
ins.offset, ins.offset,
ins.offset, 0, 0, 0,
- BTRFS_FILE_EXTENT_PREALLOC);
+ BTRFS_FILE_EXTENT_PREALLOC,
+ 0, NULL);
if (ret) {
btrfs_free_reserved_extent(fs_info, ins.objectid,
ins.offset, 0);
diff --git a/fs/btrfs/ordered-data.c b/fs/btrfs/ordered-data.c
index 0c4ef208b8b9..5fbb60b5ddbe 100644
--- a/fs/btrfs/ordered-data.c
+++ b/fs/btrfs/ordered-data.c
@@ -170,7 +170,8 @@ static inline struct rb_node *tree_search(struct btrfs_ordered_inode_tree *tree,
*/
static int __btrfs_add_ordered_extent(struct inode *inode, u64 file_offset,
u64 start, u64 len, u64 disk_len,
- int type, int dio, int compress_type)
+ int type, int dio, int compress_type,
+ struct btrfs_enc_key *key, char *iv)
{
struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb);
struct btrfs_root *root = BTRFS_I(inode)->root;
@@ -190,10 +191,14 @@ static int __btrfs_add_ordered_extent(struct inode *inode, u64 file_offset,
entry->bytes_left = len;
entry->inode = igrab(inode);
entry->compress_type = compress_type;
+ entry->key = key;
entry->truncated_len = (u64)-1;
if (type != BTRFS_ORDERED_IO_DONE && type != BTRFS_ORDERED_COMPLETE)
set_bit(type, &entry->flags);
+ if (key)
+ memcpy(entry->iv, iv, BTRFS_ENCRYPTION_BLOCK_LENGTH);
+
if (dio)
set_bit(BTRFS_ORDERED_DIRECT, &entry->flags);
@@ -241,11 +246,12 @@ static int __btrfs_add_ordered_extent(struct inode *inode, u64 file_offset,
}
int btrfs_add_ordered_extent(struct inode *inode, u64 file_offset,
- u64 start, u64 len, u64 disk_len, int type)
+ u64 start, u64 len, u64 disk_len, int type,
+ struct btrfs_enc_key *key, char *iv)
{
return __btrfs_add_ordered_extent(inode, file_offset, start, len,
disk_len, type, 0,
- BTRFS_COMPRESS_NONE);
+ BTRFS_COMPRESS_NONE, key, iv);
}
int btrfs_add_ordered_extent_dio(struct inode *inode, u64 file_offset,
@@ -253,16 +259,17 @@ int btrfs_add_ordered_extent_dio(struct inode *inode, u64 file_offset,
{
return __btrfs_add_ordered_extent(inode, file_offset, start, len,
disk_len, type, 1,
- BTRFS_COMPRESS_NONE);
+ BTRFS_COMPRESS_NONE, NULL, NULL);
}
int btrfs_add_ordered_extent_compress(struct inode *inode, u64 file_offset,
u64 start, u64 len, u64 disk_len,
- int type, int compress_type)
+ int type, int compress_type,
+ struct btrfs_enc_key *key, char *iv)
{
return __btrfs_add_ordered_extent(inode, file_offset, start, len,
disk_len, type, 0,
- compress_type);
+ compress_type, key, iv);
}
/*
diff --git a/fs/btrfs/ordered-data.h b/fs/btrfs/ordered-data.h
index 02d813aaa261..563d882fdd16 100644
--- a/fs/btrfs/ordered-data.h
+++ b/fs/btrfs/ordered-data.h
@@ -95,6 +95,12 @@ struct btrfs_ordered_extent {
/* compression algorithm */
int compress_type;
+ /* encryption key */
+ struct btrfs_enc_key *key;
+
+ /* encryption initialization vector */
+ char iv[BTRFS_ENCRYPTION_BLOCK_LENGTH];
+
/* reference count */
refcount_t refs;
@@ -158,12 +164,14 @@ int btrfs_dec_test_first_ordered_pending(struct inode *inode,
u64 *file_offset, u64 io_size,
int uptodate);
int btrfs_add_ordered_extent(struct inode *inode, u64 file_offset,
- u64 start, u64 len, u64 disk_len, int type);
+ u64 start, u64 len, u64 disk_len, int type,
+ struct btrfs_enc_key *key, char *iv);
int btrfs_add_ordered_extent_dio(struct inode *inode, u64 file_offset,
u64 start, u64 len, u64 disk_len, int type);
int btrfs_add_ordered_extent_compress(struct inode *inode, u64 file_offset,
u64 start, u64 len, u64 disk_len,
- int type, int compress_type);
+ int type, int compress_type,
+ struct btrfs_enc_key *key, char *iv);
void btrfs_add_ordered_sum(struct inode *inode,
struct btrfs_ordered_extent *entry,
struct btrfs_ordered_sum *sum);
--
2.19.2
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [RFC PATCH 14/19] btrfs: allow reading compressed encrypted extents
2019-01-09 1:26 [RFC PATCH 01/19] btrfs: add encryption structs and constants Mark Harmstone
` (11 preceding siblings ...)
2019-01-09 1:26 ` [RFC PATCH 13/19] btrfs: allow writing normal and compressed " Mark Harmstone
@ 2019-01-09 1:26 ` Mark Harmstone
2019-01-09 1:26 ` [RFC PATCH 15/19] btrfs: allow writing compressed, encrypted, inline extents Mark Harmstone
` (4 subsequent siblings)
17 siblings, 0 replies; 19+ messages in thread
From: Mark Harmstone @ 2019-01-09 1:26 UTC (permalink / raw)
Cc: mark, Chris Mason, Josef Bacik, David Sterba, linux-btrfs, linux-kernel
Signed-off-by: Mark Harmstone <mark@harmstone.com>
---
fs/btrfs/compression.c | 22 ++++++++++++++++++++++
fs/btrfs/compression.h | 7 +++++++
2 files changed, 29 insertions(+)
diff --git a/fs/btrfs/compression.c b/fs/btrfs/compression.c
index 2955a4ea2fa8..55d7b97a7dc9 100644
--- a/fs/btrfs/compression.c
+++ b/fs/btrfs/compression.c
@@ -26,6 +26,7 @@
#include "compression.h"
#include "extent_io.h"
#include "extent_map.h"
+#include "encryption.h"
static const char* const btrfs_compress_types[] = { "", "zlib", "lzo", "zstd" };
@@ -139,6 +140,20 @@ static void end_compressed_bio_read(struct bio *bio)
if (ret)
goto csum_failed;
+ if (cb->key_number != 0) {
+ struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb);
+ unsigned int i;
+
+ for (i = 0; i < cb->nr_pages; i++) {
+ ret = btrfs_decrypt_page(fs_info,
+ cb->compressed_pages[i], cb->key_number,
+ cb->iv);
+
+ if (ret)
+ goto csum_failed;
+ }
+ }
+
/* ok, we're the last bio for this extent, lets start
* the decompression.
*/
@@ -568,6 +583,13 @@ blk_status_t btrfs_submit_compressed_read(struct inode *inode, struct bio *bio,
em_len = em->len;
em_start = em->start;
+ if (em->encrypt_type == BTRFS_ENCRYPTION_NONE) {
+ cb->key_number = 0;
+ } else {
+ cb->key_number = em->key_number;
+ memcpy(cb->iv, em->iv, BTRFS_ENCRYPTION_BLOCK_LENGTH);
+ }
+
free_extent_map(em);
em = NULL;
diff --git a/fs/btrfs/compression.h b/fs/btrfs/compression.h
index ddda9b80bf20..86a536d8cd5f 100644
--- a/fs/btrfs/compression.h
+++ b/fs/btrfs/compression.h
@@ -7,6 +7,7 @@
#define BTRFS_COMPRESSION_H
#include <linux/sizes.h>
+#include <linux/btrfs_tree.h>
/*
* We want to make sure that amount of RAM required to uncompress an extent is
@@ -54,6 +55,12 @@ struct compressed_bio {
int errors;
int mirror_num;
+ /* encryption key number */
+ u64 key_number;
+
+ /* encryption initialization vector */
+ char iv[BTRFS_ENCRYPTION_BLOCK_LENGTH];
+
/* for reads, this is the bio we are copying the data into */
struct bio *orig_bio;
--
2.19.2
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [RFC PATCH 15/19] btrfs: allow writing compressed, encrypted, inline extents
2019-01-09 1:26 [RFC PATCH 01/19] btrfs: add encryption structs and constants Mark Harmstone
` (12 preceding siblings ...)
2019-01-09 1:26 ` [RFC PATCH 14/19] btrfs: allow reading " Mark Harmstone
@ 2019-01-09 1:26 ` Mark Harmstone
2019-01-09 1:26 ` [RFC PATCH 16/19] btrfs: add encryption incompat flag to sysfs Mark Harmstone
` (3 subsequent siblings)
17 siblings, 0 replies; 19+ messages in thread
From: Mark Harmstone @ 2019-01-09 1:26 UTC (permalink / raw)
Cc: mark, Chris Mason, Josef Bacik, David Sterba, linux-btrfs, linux-kernel
Signed-off-by: Mark Harmstone <mark@harmstone.com>
---
fs/btrfs/inode.c | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index 61481833f5e4..f8bc7174f7e3 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -219,6 +219,22 @@ static int insert_inline_extent(struct btrfs_trans_handle *trans,
if (compress_type != BTRFS_COMPRESS_NONE) {
struct page *cpage;
int i = 0;
+
+ if (enc_key) {
+ struct btrfs_file_extent_inline_enc *eienc;
+
+ eienc = (struct btrfs_file_extent_inline_enc *)ptr;
+
+ btrfs_set_file_extent_inline_enc_key_number(leaf,
+ eienc, enc_key->key_number);
+
+ write_eb_member(leaf, eienc,
+ struct btrfs_file_extent_inline_enc,
+ iv, iv);
+
+ ptr += sizeof(struct btrfs_file_extent_inline_enc);
+ }
+
while (compressed_size > 0) {
cpage = compressed_pages[i];
cur_size = min_t(unsigned long, compressed_size,
--
2.19.2
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [RFC PATCH 16/19] btrfs: add encryption incompat flag to sysfs
2019-01-09 1:26 [RFC PATCH 01/19] btrfs: add encryption structs and constants Mark Harmstone
` (13 preceding siblings ...)
2019-01-09 1:26 ` [RFC PATCH 15/19] btrfs: allow writing compressed, encrypted, inline extents Mark Harmstone
@ 2019-01-09 1:26 ` Mark Harmstone
2019-01-09 1:26 ` [RFC PATCH 17/19] btrfs: don't allow direct IO of encrypted extents Mark Harmstone
` (2 subsequent siblings)
17 siblings, 0 replies; 19+ messages in thread
From: Mark Harmstone @ 2019-01-09 1:26 UTC (permalink / raw)
Cc: mark, Chris Mason, Josef Bacik, David Sterba, linux-btrfs, linux-kernel
Signed-off-by: Mark Harmstone <mark@harmstone.com>
---
fs/btrfs/sysfs.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/fs/btrfs/sysfs.c b/fs/btrfs/sysfs.c
index 3717c864ba23..6bd70188ea62 100644
--- a/fs/btrfs/sysfs.c
+++ b/fs/btrfs/sysfs.c
@@ -191,6 +191,7 @@ BTRFS_FEAT_ATTR_INCOMPAT(extended_iref, EXTENDED_IREF);
BTRFS_FEAT_ATTR_INCOMPAT(raid56, RAID56);
BTRFS_FEAT_ATTR_INCOMPAT(skinny_metadata, SKINNY_METADATA);
BTRFS_FEAT_ATTR_INCOMPAT(no_holes, NO_HOLES);
+BTRFS_FEAT_ATTR_INCOMPAT(encryption, ENCRYPTION);
BTRFS_FEAT_ATTR_COMPAT_RO(free_space_tree, FREE_SPACE_TREE);
static struct attribute *btrfs_supported_feature_attrs[] = {
@@ -205,6 +206,7 @@ static struct attribute *btrfs_supported_feature_attrs[] = {
BTRFS_FEAT_ATTR_PTR(skinny_metadata),
BTRFS_FEAT_ATTR_PTR(no_holes),
BTRFS_FEAT_ATTR_PTR(free_space_tree),
+ BTRFS_FEAT_ATTR_PTR(encryption),
NULL
};
--
2.19.2
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [RFC PATCH 17/19] btrfs: don't allow direct IO of encrypted extents
2019-01-09 1:26 [RFC PATCH 01/19] btrfs: add encryption structs and constants Mark Harmstone
` (14 preceding siblings ...)
2019-01-09 1:26 ` [RFC PATCH 16/19] btrfs: add encryption incompat flag to sysfs Mark Harmstone
@ 2019-01-09 1:26 ` Mark Harmstone
2019-01-09 1:27 ` [RFC PATCH 18/19] btrfs: return encrypted flag to statx Mark Harmstone
2019-01-09 1:27 ` [RFC PATCH 19/19] btrfs: translate encryption flag to FS_ENCRYPT_FL Mark Harmstone
17 siblings, 0 replies; 19+ messages in thread
From: Mark Harmstone @ 2019-01-09 1:26 UTC (permalink / raw)
Cc: mark, Chris Mason, Josef Bacik, David Sterba, linux-btrfs, linux-kernel
Signed-off-by: Mark Harmstone <mark@harmstone.com>
---
fs/btrfs/inode.c | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index f8bc7174f7e3..8f58f492abc8 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -7945,6 +7945,17 @@ static int btrfs_get_blocks_direct(struct inode *inode, sector_t iblock,
goto unlock_err;
}
+ /*
+ * Fallback to buffered IO if we have an encrypted inode,
+ * as we would need to change what's in memory.
+ */
+ if ((create && inode_need_encrypt(inode)) ||
+ test_bit(EXTENT_FLAG_ENCRYPTED, &em->flags)) {
+ free_extent_map(em);
+ ret = -ENOTBLK;
+ goto unlock_err;
+ }
+
if (create) {
ret = btrfs_get_blocks_direct_write(&em, bh_result, inode,
dio_data, start, len);
--
2.19.2
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [RFC PATCH 18/19] btrfs: return encrypted flag to statx
2019-01-09 1:26 [RFC PATCH 01/19] btrfs: add encryption structs and constants Mark Harmstone
` (15 preceding siblings ...)
2019-01-09 1:26 ` [RFC PATCH 17/19] btrfs: don't allow direct IO of encrypted extents Mark Harmstone
@ 2019-01-09 1:27 ` Mark Harmstone
2019-01-09 1:27 ` [RFC PATCH 19/19] btrfs: translate encryption flag to FS_ENCRYPT_FL Mark Harmstone
17 siblings, 0 replies; 19+ messages in thread
From: Mark Harmstone @ 2019-01-09 1:27 UTC (permalink / raw)
Cc: mark, Chris Mason, Josef Bacik, David Sterba, linux-btrfs, linux-kernel
Signed-off-by: Mark Harmstone <mark@harmstone.com>
---
fs/btrfs/inode.c | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index 8f58f492abc8..beaeeafe467a 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -9637,11 +9637,14 @@ static int btrfs_getattr(const struct path *path, struct kstat *stat,
stat->attributes |= STATX_ATTR_IMMUTABLE;
if (bi_flags & BTRFS_INODE_NODUMP)
stat->attributes |= STATX_ATTR_NODUMP;
+ if (bi_flags & BTRFS_INODE_ENCRYPT)
+ stat->attributes |= STATX_ATTR_ENCRYPTED;
stat->attributes_mask |= (STATX_ATTR_APPEND |
STATX_ATTR_COMPRESSED |
STATX_ATTR_IMMUTABLE |
- STATX_ATTR_NODUMP);
+ STATX_ATTR_NODUMP |
+ STATX_ATTR_ENCRYPTED);
generic_fillattr(inode, stat);
stat->dev = BTRFS_I(inode)->root->anon_dev;
--
2.19.2
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [RFC PATCH 19/19] btrfs: translate encryption flag to FS_ENCRYPT_FL
2019-01-09 1:26 [RFC PATCH 01/19] btrfs: add encryption structs and constants Mark Harmstone
` (16 preceding siblings ...)
2019-01-09 1:27 ` [RFC PATCH 18/19] btrfs: return encrypted flag to statx Mark Harmstone
@ 2019-01-09 1:27 ` Mark Harmstone
17 siblings, 0 replies; 19+ messages in thread
From: Mark Harmstone @ 2019-01-09 1:27 UTC (permalink / raw)
Cc: mark, Chris Mason, Josef Bacik, David Sterba, linux-btrfs, linux-kernel
Signed-off-by: Mark Harmstone <mark@harmstone.com>
---
fs/btrfs/ioctl.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
index 6589656e3988..af054390d78d 100644
--- a/fs/btrfs/ioctl.c
+++ b/fs/btrfs/ioctl.c
@@ -123,6 +123,8 @@ static unsigned int btrfs_inode_flags_to_fsflags(unsigned int flags)
iflags |= FS_DIRSYNC_FL;
if (flags & BTRFS_INODE_NODATACOW)
iflags |= FS_NOCOW_FL;
+ if (flags & BTRFS_INODE_ENCRYPT)
+ iflags |= FS_ENCRYPT_FL;
if (flags & BTRFS_INODE_NOCOMPRESS)
iflags |= FS_NOCOMP_FL;
@@ -173,7 +175,7 @@ static int check_fsflags(unsigned int flags)
FS_NOATIME_FL | FS_NODUMP_FL | \
FS_SYNC_FL | FS_DIRSYNC_FL | \
FS_NOCOMP_FL | FS_COMPR_FL |
- FS_NOCOW_FL))
+ FS_NOCOW_FL | FS_ENCRYPT_FL))
return -EOPNOTSUPP;
if ((flags & FS_NOCOMP_FL) && (flags & FS_COMPR_FL))
--
2.19.2
^ permalink raw reply related [flat|nested] 19+ messages in thread