linux-btrfs.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [RFC PATCH 01/19] btrfs: add encryption structs and constants
@ 2019-01-09  1:26 Mark Harmstone
  2019-01-09  1:26 ` [RFC PATCH 02/19] btrfs: add encryption dependencies to Kconfig Mark Harmstone
                   ` (17 more replies)
  0 siblings, 18 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

This is a series of patches to add per-extent AES encryption to Btrfs. Unlike
previous attempts, this isn't implemented by adding a new compression type,
meaning that extents that are both encrypted and compressed should work fine.

It's implemented by adding a 8-byte key number and a 16-byte
randomized initialization vector to the end of the BTRFS_EXTENT_DATA_KEY (which
for inline extents means immediately before the encrypted data). The key number
is an index into the new key root, which stores the password hashes, with the
key number doubling as the salt. This means that (unlike, AIUI, ext4), there's
no chance that a file by can be decrypted using the wrong key.

The ioctl BTRFS_IOC_GET_KEY_SALT provides the key number for a given password,
or if not present adds a new key with a randomized number.

We use AES in counter mode, so that we can do random reads without having to
read the whole extent.

Some notes:

* At the moment COW is forced for encrypted extents, meaning that preallocation
and the nocow option don't have any effect.

* Direct IO is disabled for encrypted extents. Direct reading should be
possible, as you'd do the decryption after the bio returns. I'm not sure direct
writing is feasible though, as you'd either have to copy the buffer or change
the process's memory.

* Once a key is added by keyctl, it persists until the FS is unmounted. Ideally
we'd like it to be removed when unlinked or revoked.

* We don't use fscrypt - I understand that the XTS mode that it operates in
means that sectors are keyed to their logical address, which would break
balancing.

* Sending still dumps unencrypted data, and won't work on encrypted
files without the key. We could change this, but it raises the possibility that
the same password could correspond to multiple keys.

* These patches assume that the number of keys in the key tree will be small -
in particular, the ioctl walks the tree testing the hashes. There's no way to
remove keys from the key tree; I think we'd probably want btrfs-check to warn
about unused keys, something like that. We could use reference counting, but
that would mean writing to the key tree whenever an encrypted extent is created
or removed.

* Filenames aren't encrypted at present; I considered this a different enough
problem to save for another patchset, given that this one's already quite
hefty. I'm not sure if we could use fscrypt for this either, as btrfs allows
for multiple inodes with the same number, and it would be a security flaw to
duplicate IVs.

I've attached a quick-and-dirty userspace program demonstrating the ioctl:

#include <linux/btrfs.h>
#include <stdio.h>
#include <sys/types.h>
#include <stdlib.h>
#include <fcntl.h>
#include <termios.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <string.h>
#include <keyutils.h>
#include <sys/xattr.h>

static int getpw(char **pw)
{
	struct termios t, orig_t;
	size_t sz;
	int nread;

	if (tcgetattr(fileno(stdin), &t) != 0)
		return errno;

	orig_t = t;
	t.c_lflag &= ~ECHO;
	if (tcsetattr(fileno(stdin), TCSAFLUSH, &t) != 0)
		return errno;

	nread = getline(pw, &sz, stdin);
	if (nread > 0 && (*pw)[nread - 1] == '\n')
		(*pw)[nread - 1] = 0; /* remove trailing newline */

	tcsetattr(fileno(stdin), TCSAFLUSH, &orig_t);

	return 0;
}

static int add_btrfs_key(__u64 salt, char *pw)
{
	key_serial_t key;
	char name[20];

	sprintf(name, "btrfs:%016llx", salt);

	/*
	 * In pratice, you would want a type of "logon" here, so that
	 * the key can't be read from userspace.
	 */

	key = add_key("user", name, pw, strlen(pw), KEY_SPEC_USER_KEYRING);
	if (key == -1)
		return errno;

	printf("Added key %u to keyring\n", key);

	return 0;
}

int main(int argc, char *argv[])
{
	int fd, ret;
	char *pw = NULL;
	struct btrfs_ioctl_get_key_salt_args args;
	char keyhex[17];

	if (argc < 2) {
		fprintf(stderr, "Usage: btrfscrypt <directory>\n");
		return 0;
	}

	/* Prompt for password */

	printf("Password: ");

	ret = getpw(&pw);

	printf("\n");

	if (ret) {
		fprintf(stderr, "getpw returned %i\n", ret);
		return ret;
	}

	fd = open(argv[1], 0);
	if (fd < 0) {
		fprintf(stderr, "open returned %i\n", fd);
		goto end;
	}

	strcpy(args.password, pw);

	/* Get salt */

	ret = ioctl(fd, BTRFS_IOC_GET_KEY_SALT, &args);
	if (ret) {
		fprintf(stderr, "ioctl returned %i\n", ret);
		goto end2;
	}

	printf("BTRFS_IOC_GET_KEY_SALT returned salt %llx\n", args.salt);

	ret = add_btrfs_key(args.salt, pw);
	if (ret) {
		fprintf(stderr, "add_btrfs_key returned %i\n", ret);
		goto end2;
	}

	/* Set xattr */

	sprintf(keyhex, "%016llx", args.salt);

	ret = setxattr(argv[1], "btrfs.key", keyhex, strlen(keyhex), 0);
	if (ret) {
		fprintf(stderr, "setxattr returned %i\n", ret);
		goto end2;
	}

	ret = 0;

end2:
	close(fd);

end:
	free(pw);

	return ret;
}


Signed-off-by: Mark Harmstone <mark@harmstone.com>
---
 include/uapi/linux/btrfs.h      |  1 +
 include/uapi/linux/btrfs_tree.h | 21 +++++++++++++++++++++
 2 files changed, 22 insertions(+)

diff --git a/include/uapi/linux/btrfs.h b/include/uapi/linux/btrfs.h
index 5ca1d21fc4a7..6c785d5cfb4b 100644
--- a/include/uapi/linux/btrfs.h
+++ b/include/uapi/linux/btrfs.h
@@ -269,6 +269,7 @@ struct btrfs_ioctl_fs_info_args {
 #define BTRFS_FEATURE_INCOMPAT_RAID56		(1ULL << 7)
 #define BTRFS_FEATURE_INCOMPAT_SKINNY_METADATA	(1ULL << 8)
 #define BTRFS_FEATURE_INCOMPAT_NO_HOLES		(1ULL << 9)
+#define BTRFS_FEATURE_INCOMPAT_ENCRYPTION	(1ULL << 10)
 
 struct btrfs_ioctl_feature_flags {
 	__u64 compat_flags;
diff --git a/include/uapi/linux/btrfs_tree.h b/include/uapi/linux/btrfs_tree.h
index aff1356c2bb8..40acca24c69f 100644
--- a/include/uapi/linux/btrfs_tree.h
+++ b/include/uapi/linux/btrfs_tree.h
@@ -48,6 +48,9 @@
 /* tracks free space in block groups. */
 #define BTRFS_FREE_SPACE_TREE_OBJECTID 10ULL
 
+/* stores encryption keys */
+#define BTRFS_KEY_TREE_OBJECTID 11ULL
+
 /* device stats in the device tree */
 #define BTRFS_DEV_STATS_OBJECTID 0ULL
 
@@ -141,6 +144,11 @@
  */
 #define BTRFS_ROOT_ITEM_KEY	132
 
+/*
+ * encryption key, consisting of the hashed password
+ */
+#define BTRFS_ENCRYPTION_KEY	133
+
 /*
  * root backrefs tie subvols and snapshots to the directory entries that
  * reference them
@@ -789,6 +797,19 @@ struct btrfs_file_extent_item {
 
 } __attribute__ ((__packed__));
 
+#define BTRFS_ENCRYPTION_BLOCK_LENGTH 16
+
+struct btrfs_file_extent_item_enc {
+	struct btrfs_file_extent_item extent_item;
+	__le64 key_number;
+	__u8 iv[BTRFS_ENCRYPTION_BLOCK_LENGTH];
+} __attribute__ ((__packed__));
+
+struct btrfs_file_extent_inline_enc {
+	__le64 key_number;
+	u8 iv[BTRFS_ENCRYPTION_BLOCK_LENGTH];
+} __attribute__ ((__packed__));
+
 struct btrfs_csum_item {
 	__u8 csum;
 } __attribute__ ((__packed__));
-- 
2.19.2


^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [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

end of thread, other threads:[~2019-01-09  1:29 UTC | newest]

Thread overview: 19+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
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 ` [RFC PATCH 04/19] btrfs: allow encrypted volumes to be mounted Mark Harmstone
2019-01-09  1:26 ` [RFC PATCH 05/19] btrfs: add key list Mark Harmstone
2019-01-09  1:26 ` [RFC PATCH 06/19] btrfs: add ioctl BTRFS_IOC_GET_KEY_SALT Mark Harmstone
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 ` [RFC PATCH 08/19] btrfs: change extract in prop_handler to write into string Mark Harmstone
2019-01-09  1:26 ` [RFC PATCH 09/19] btrfs: add btrfs.key property Mark Harmstone
2019-01-09  1:26 ` [RFC PATCH 10/19] btrfs: allow reading encrypted inline extents Mark Harmstone
2019-01-09  1:26 ` [RFC PATCH 11/19] btrfs: allow writing " Mark Harmstone
2019-01-09  1:26 ` [RFC PATCH 12/19] btrfs: allow reading normal encrypted extents Mark Harmstone
2019-01-09  1:26 ` [RFC PATCH 13/19] btrfs: allow writing normal and compressed " Mark Harmstone
2019-01-09  1:26 ` [RFC PATCH 14/19] btrfs: allow reading " Mark Harmstone
2019-01-09  1:26 ` [RFC PATCH 15/19] btrfs: allow writing compressed, encrypted, inline extents Mark Harmstone
2019-01-09  1:26 ` [RFC PATCH 16/19] btrfs: add encryption incompat flag to sysfs Mark Harmstone
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 ` [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

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).