All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC] Experimental btrfs encryption
@ 2016-03-01 16:08 Anand Jain
  2016-03-01 16:08 ` [RFC PATCH 1/1] btrfs: Encryption: Add btrfs encryption support Anand Jain
                   ` (6 more replies)
  0 siblings, 7 replies; 26+ messages in thread
From: Anand Jain @ 2016-03-01 16:08 UTC (permalink / raw)
  To: linux-btrfs; +Cc: clm, dsterba, Anand Jain

This patchset adds btrfs encryption support.

Warning:
The code is in prototype/experimental stage and is not suitable
for the production data yet.

Example usage:
Create an encrypted subvolume:
  btrfs subvol create -e /btrfs/sv1
  Paraphrase: <-

Review encryption status
  btrfs subvol show /btrfs/sv1
  btrfs/sv1
    Name: sv1
    UID: d8bf1718-56a7-da40-86d9-b8e87315f63f
    Parent UUID: -
    Received UUID: -
    Creation time: 2016-03-01 17:11:58 +0800
    Subvolume ID: 257
    Generation: 13
    Gen at creation:7
    Parent ID: 5
    Top level ID: 5
    Flags: -
    Encryption: aes@btrfs:d8bf1718 (188612608)
                   ^ ^^^^^^^^^^^^^^ ^^^^^^^^^
                   |        |               |
                Algorithm Key-Tag Key-serial-number

  keyctl show
  ::
  188612608 --alswrv 0 0 \_ user: btrfs:d8bf1718

Logout/revoke:
  btrfs subvol encrypt -k out /btrfs/sv1
  btrfs subvol show /btrfs/sv1 | egrep Encrypt
  Encryption: aes@btrfs:d8bf1718 (Required key not available)

sign in:
  btrfs subvol encrypt -k in /btrfs/sv1

Known issues / limitation / for future expansion:
- Need to set FS incompatible feature.

- No password verification yet.

- Move of files across subvolume is not supported when both
  or either one has encryption set.

- No way to change the password.

- Does not drop the cached pages when key is revoked.

- Need to get password twice from the user.

- No user permeable subvol info ioctl.

- Provide a method to pass key using the mount option.

- Provide a method to read the key from the file.

- Current encryption method is symmetric (same key for both
  encryption and decryption), however we could easily expand
  this to other potentially useful methods like asymmetric
  (private/public) encryption.

- As of now uses "user" keytype, I am still considering/
  evaluating other key type such as logon.

- Evaluate other encryption algorithms,  as of now it is
  using "cts(cbc(aes)".

- Uses btrfs compression framework, so compression and then
  encryption is not possible. However yet evaluate if there
  are encryption algorithm which can compress as well.


Anand Jain (1):
  btrfs: encryption

 fs/btrfs/Makefile      |   2 +-
 fs/btrfs/btrfs_inode.h |   2 +
 fs/btrfs/compression.c |  53 ++++-
 fs/btrfs/compression.h |   1 +
 fs/btrfs/ctree.h       |  11 +-
 fs/btrfs/encrypt.c     | 544 +++++++++++++++++++++++++++++++++++++++++++++++++
 fs/btrfs/encrypt.h     |  21 ++
 fs/btrfs/inode.c       |  37 +++-
 fs/btrfs/ioctl.c       |   7 +
 fs/btrfs/props.c       | 140 ++++++++++++-
 fs/btrfs/super.c       |   5 +-
 11 files changed, 812 insertions(+), 11 deletions(-)
 create mode 100644 fs/btrfs/encrypt.c
 create mode 100644 fs/btrfs/encrypt.h

Anand Jain (2):
  btrfs-progs: subvolume functions reorg
  btrfs-progs: add encrypt as subvol sub-command

 Makefile.in      |   5 +-
 btrfs-list.c     |  33 +++++
 cmds-qgroup.c    |   1 +
 cmds-send.c      |  12 +-
 cmds-subvolume.c | 209 +++++++++++++++--------------
 commands.h       |   1 +
 encrypt.c        | 397 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 encrypt.h        |  33 +++++
 props.c          |   3 +
 subvolume.c      | 152 +++++++++++++++++++++
 subvolume.h      |  22 +++
 11 files changed, 757 insertions(+), 111 deletions(-)
 create mode 100644 encrypt.c
 create mode 100644 encrypt.h
 create mode 100644 subvolume.c
 create mode 100644 subvolume.h

-- 
2.7.0


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

* [RFC PATCH 1/1] btrfs: Encryption: Add btrfs encryption support
  2016-03-01 16:08 [RFC] Experimental btrfs encryption Anand Jain
@ 2016-03-01 16:08 ` Anand Jain
  2016-03-10  2:19   ` Liu Bo
  2016-03-01 16:08 ` [RFC PATCH 1/2] btrfs-progs: subvolume functions reorg Anand Jain
                   ` (5 subsequent siblings)
  6 siblings, 1 reply; 26+ messages in thread
From: Anand Jain @ 2016-03-01 16:08 UTC (permalink / raw)
  To: linux-btrfs; +Cc: clm, dsterba, Anand Jain

***
*** Warning: Experimental code.
***

Adds encryption support. The branch is based on v4.5-rc6.

Signed-off-by: Anand Jain <anand.jain@oracle.com>
---
 fs/btrfs/Makefile      |   2 +-
 fs/btrfs/btrfs_inode.h |   2 +
 fs/btrfs/compression.c |  53 ++++-
 fs/btrfs/compression.h |   1 +
 fs/btrfs/ctree.h       |  11 +-
 fs/btrfs/encrypt.c     | 544 +++++++++++++++++++++++++++++++++++++++++++++++++
 fs/btrfs/encrypt.h     |  21 ++
 fs/btrfs/inode.c       |  37 +++-
 fs/btrfs/ioctl.c       |   7 +
 fs/btrfs/props.c       | 140 ++++++++++++-
 fs/btrfs/super.c       |   5 +-
 11 files changed, 812 insertions(+), 11 deletions(-)
 create mode 100644 fs/btrfs/encrypt.c
 create mode 100644 fs/btrfs/encrypt.h

diff --git a/fs/btrfs/Makefile b/fs/btrfs/Makefile
index 128ce17a80b0..2765778c5898 100644
--- a/fs/btrfs/Makefile
+++ b/fs/btrfs/Makefile
@@ -9,7 +9,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 \
 	   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 hash.o free-space-tree.o
+	   uuid-tree.o props.o hash.o free-space-tree.o encrypt.o
 
 btrfs-$(CONFIG_BTRFS_FS_POSIX_ACL) += acl.o
 btrfs-$(CONFIG_BTRFS_FS_CHECK_INTEGRITY) += check-integrity.o
diff --git a/fs/btrfs/btrfs_inode.h b/fs/btrfs/btrfs_inode.h
index 61205e3bbefa..4f09572b4922 100644
--- a/fs/btrfs/btrfs_inode.h
+++ b/fs/btrfs/btrfs_inode.h
@@ -197,6 +197,8 @@ struct btrfs_inode {
 	long delayed_iput_count;
 
 	struct inode vfs_inode;
+
+	unsigned char key_payload[16];
 };
 
 extern unsigned char btrfs_filetype_table[];
diff --git a/fs/btrfs/compression.c b/fs/btrfs/compression.c
index 3346cd8f9910..d59c366f4200 100644
--- a/fs/btrfs/compression.c
+++ b/fs/btrfs/compression.c
@@ -41,6 +41,7 @@
 #include "compression.h"
 #include "extent_io.h"
 #include "extent_map.h"
+#include "encrypt.h"
 
 struct compressed_bio {
 	/* number of bios pending for this compressed extent */
@@ -182,7 +183,7 @@ static void end_compressed_bio_read(struct bio *bio)
 				      cb->orig_bio->bi_vcnt,
 				      cb->compressed_len);
 csum_failed:
-	if (ret)
+	if (ret && ret != -ENOKEY)
 		cb->errors = 1;
 
 	/* release the compressed pages */
@@ -751,6 +752,7 @@ static struct {
 static const struct btrfs_compress_op * const btrfs_compress_op[] = {
 	&btrfs_zlib_compress,
 	&btrfs_lzo_compress,
+	&btrfs_encrypt_ops,
 };
 
 void __init btrfs_init_compress(void)
@@ -780,6 +782,10 @@ static struct list_head *find_workspace(int type)
 	atomic_t *alloc_ws		= &btrfs_comp_ws[idx].alloc_ws;
 	wait_queue_head_t *ws_wait	= &btrfs_comp_ws[idx].ws_wait;
 	int *num_ws			= &btrfs_comp_ws[idx].num_ws;
+
+	if (type == BTRFS_ENCRYPT_AES)
+		return NULL;
+
 again:
 	spin_lock(ws_lock);
 	if (!list_empty(idle_ws)) {
@@ -824,6 +830,9 @@ static void free_workspace(int type, struct list_head *workspace)
 	wait_queue_head_t *ws_wait	= &btrfs_comp_ws[idx].ws_wait;
 	int *num_ws			= &btrfs_comp_ws[idx].num_ws;
 
+	if (!workspace)
+		return;
+
 	spin_lock(ws_lock);
 	if (*num_ws < num_online_cpus()) {
 		list_add(workspace, idle_ws);
@@ -862,6 +871,38 @@ static void free_workspaces(void)
 	}
 }
 
+void print_data_encode_status(struct inode *inode, int direction,
+					char *prefix, int type, int ret)
+{
+#ifdef CONFIG_BTRFS_DEBUG
+	char what[10];
+
+	if (type == BTRFS_ENCRYPT_AES) {
+		if (!direction)
+			strcpy(what, "Encrypt");
+		else
+			strcpy(what, "Decrypt");
+	} else {
+		if (!direction)
+			strcpy(what, "Compress");
+		else
+			strcpy(what, "Uncpress");
+	}
+
+	switch (ret) {
+	case 0:
+		pr_debug("%s %s: success     : inode %lu\n",what, prefix, inode->i_ino);
+		return;
+	case -ENOKEY:
+		pr_debug("%s %s: Failed NOKEY: inode %lu\n",what, prefix, inode->i_ino);
+		return;
+	default:
+		pr_debug("%s %s: Failed %d   : inode %lu\n",what, prefix, ret, inode->i_ino);
+	}
+#else
+#endif
+}
+
 /*
  * given an address space and start/len, compress the bytes.
  *
@@ -894,7 +935,7 @@ int btrfs_compress_pages(int type, struct address_space *mapping,
 	int ret;
 
 	workspace = find_workspace(type);
-	if (IS_ERR(workspace))
+	if (workspace && IS_ERR(workspace))
 		return PTR_ERR(workspace);
 
 	ret = btrfs_compress_op[type-1]->compress_pages(workspace, mapping,
@@ -903,6 +944,8 @@ int btrfs_compress_pages(int type, struct address_space *mapping,
 						      total_in, total_out,
 						      max_out);
 	free_workspace(type, workspace);
+
+	print_data_encode_status(mapping->host, 0, "    ", type, ret);
 	return ret;
 }
 
@@ -930,13 +973,14 @@ static int btrfs_decompress_biovec(int type, struct page **pages_in,
 	int ret;
 
 	workspace = find_workspace(type);
-	if (IS_ERR(workspace))
+	if (workspace && IS_ERR(workspace))
 		return PTR_ERR(workspace);
 
 	ret = btrfs_compress_op[type-1]->decompress_biovec(workspace, pages_in,
 							 disk_start,
 							 bvec, vcnt, srclen);
 	free_workspace(type, workspace);
+	print_data_encode_status(bvec->bv_page->mapping->host, 1, "bio ", type, ret);
 	return ret;
 }
 
@@ -952,7 +996,7 @@ int btrfs_decompress(int type, unsigned char *data_in, struct page *dest_page,
 	int ret;
 
 	workspace = find_workspace(type);
-	if (IS_ERR(workspace))
+	if (workspace && IS_ERR(workspace))
 		return PTR_ERR(workspace);
 
 	ret = btrfs_compress_op[type-1]->decompress(workspace, data_in,
@@ -960,6 +1004,7 @@ int btrfs_decompress(int type, unsigned char *data_in, struct page *dest_page,
 						  srclen, destlen);
 
 	free_workspace(type, workspace);
+	print_data_encode_status(dest_page->mapping->host, 1, "page", type, ret);
 	return ret;
 }
 
diff --git a/fs/btrfs/compression.h b/fs/btrfs/compression.h
index 13a4dc0436c9..78e8f38dbf60 100644
--- a/fs/btrfs/compression.h
+++ b/fs/btrfs/compression.h
@@ -79,5 +79,6 @@ struct btrfs_compress_op {
 
 extern const struct btrfs_compress_op btrfs_zlib_compress;
 extern const struct btrfs_compress_op btrfs_lzo_compress;
+extern const struct btrfs_compress_op btrfs_encrypt_ops;
 
 #endif
diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
index bfe4a337fb4d..f30a92bf9c54 100644
--- a/fs/btrfs/ctree.h
+++ b/fs/btrfs/ctree.h
@@ -719,8 +719,9 @@ enum btrfs_compression_type {
 	BTRFS_COMPRESS_NONE  = 0,
 	BTRFS_COMPRESS_ZLIB  = 1,
 	BTRFS_COMPRESS_LZO   = 2,
-	BTRFS_COMPRESS_TYPES = 2,
-	BTRFS_COMPRESS_LAST  = 3,
+	BTRFS_ENCRYPT_AES    = 3,
+	BTRFS_COMPRESS_TYPES = 3,
+	BTRFS_COMPRESS_LAST  = 4,
 };
 
 struct btrfs_inode_item {
@@ -771,6 +772,7 @@ struct btrfs_dir_item {
  * still visible as a directory
  */
 #define BTRFS_ROOT_SUBVOL_DEAD		(1ULL << 48)
+#define BTRFS_ROOT_SUBVOL_ENCRYPT	(1ULL << 49)
 
 struct btrfs_root_item {
 	struct btrfs_inode_item inode;
@@ -814,7 +816,9 @@ struct btrfs_root_item {
 	struct btrfs_timespec otime;
 	struct btrfs_timespec stime;
 	struct btrfs_timespec rtime;
-	__le64 reserved[8]; /* for future */
+	char encrypt_algo[16];
+	char encrypt_keytag[16];
+	__le64 reserved[4]; /* for future */
 } __attribute__ ((__packed__));
 
 /*
@@ -2344,6 +2348,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/encrypt.c b/fs/btrfs/encrypt.c
new file mode 100644
index 000000000000..a6838cccc507
--- /dev/null
+++ b/fs/btrfs/encrypt.c
@@ -0,0 +1,544 @@
+/*
+ * Copyright (C) 2016 Oracle.  All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License v2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 021110-1307, USA.
+ */
+#include <linux/string.h>
+#include <linux/crypto.h>
+#include <linux/scatterlist.h>
+#include <linux/random.h>
+#include <linux/pagemap.h>
+#include <keys/user-type.h>
+#include "compression.h"
+#include <linux/slab.h>
+#include <linux/keyctl.h>
+#include <linux/key-type.h>
+#include <linux/cred.h>
+#include <keys/user-type.h>
+#include "ctree.h"
+#include "btrfs_inode.h"
+#include "props.h"
+
+static const struct btrfs_encrypt_algorithm {
+	const char *name;
+	size_t	keylen;
+} btrfs_encrypt_algorithm_supported[] = {
+	{"aes", 16}
+};
+
+/*
+ * Returns cipher alg key size if the encryption type is found
+ * otherwise 0
+ */
+size_t btrfs_check_encrypt_type(char *type)
+{
+	int i;
+	for (i = 0; i < ARRAY_SIZE(btrfs_encrypt_algorithm_supported); i++)
+		if (!strcmp(btrfs_encrypt_algorithm_supported[i].name, type))
+			return btrfs_encrypt_algorithm_supported[i].keylen;
+
+	return 0;
+}
+
+/* key management*/
+static int btrfs_request_key(char *key_tag, void *key_data)
+{
+	int ret;
+	const struct user_key_payload *payload;
+	struct key *btrfs_key = NULL;
+
+	ret = 0;
+	btrfs_key = request_key(&key_type_user, key_tag, NULL);
+	if (IS_ERR(btrfs_key)) {
+		ret = PTR_ERR(btrfs_key);
+		btrfs_key = NULL;
+		return ret;
+	}
+
+	/*
+	 * caller just need key not payload so return
+	 */
+	if (!key_data)
+		return 0;
+
+	ret = key_validate(btrfs_key);
+	if (ret < 0)
+		goto out;
+
+	rcu_read_lock(); // TODO: check down_write key->sem ?
+	payload = user_key_payload(btrfs_key);
+	if (IS_ERR_OR_NULL(payload)) {
+		ret = PTR_ERR(payload);
+		goto out;
+	}
+
+	/*
+	 * As of now we just hard code as we just use ASE now
+	 */
+	if (payload->datalen != 16)
+		ret = -EINVAL;
+	else
+		memcpy(key_data, payload->data, 16);
+
+out:
+	rcu_read_unlock();
+	key_put(btrfs_key);
+
+	return ret;
+}
+
+static int btrfs_get_key_data_from_inode(struct inode *inode, unsigned char *keydata)
+{
+	int ret;
+	char keytag[15];
+	struct btrfs_inode *binode;
+	struct btrfs_root_item *ri;
+
+	binode = BTRFS_I(inode);
+	ri = &(binode->root->root_item);
+	strncpy(keytag, ri->encrypt_keytag, 14);
+	keytag[14] = '\0';
+
+	ret = btrfs_request_key(keytag, keydata);
+	return ret;
+}
+
+int btrfs_update_key_data_to_binode(struct inode *inode)
+{
+	int ret;
+	unsigned char keydata[16];
+	struct btrfs_inode *binode;
+
+	ret = btrfs_get_key_data_from_inode(inode, keydata);
+	if (ret)
+		return ret;
+
+	binode = BTRFS_I(inode);
+	memcpy(binode->key_payload, keydata, 16);
+
+	return ret;
+}
+
+int btrfs_get_keytag(struct address_space *mapping, char *keytag, struct inode **inode)
+{
+	struct btrfs_inode *binode;
+	struct btrfs_root_item *ri;
+
+	if (!mapping)
+		return -EINVAL;
+
+	if (!(mapping->host))
+		return -EINVAL;
+
+	binode = BTRFS_I(mapping->host);
+	ri = &(binode->root->root_item);
+
+	strncpy(keytag, ri->encrypt_keytag, 14);
+	keytag[14] = '\0';
+	if (inode)
+		*inode = &binode->vfs_inode;
+
+	return 0;
+}
+
+/* Encrypt and decrypt */
+struct workspace {
+	struct list_head list;
+};
+
+struct btrfs_crypt_result {
+	struct completion completion;
+	int err;
+};
+
+struct btrfs_ablkcipher_def {
+	struct scatterlist sg;
+	struct crypto_ablkcipher *tfm;
+	struct ablkcipher_request *req;
+	struct btrfs_crypt_result result;
+};
+
+int btrfs_do_blkcipher(int enc, char *data, size_t len)
+{
+	int ret = -EFAULT;
+	struct scatterlist sg;
+	unsigned int ivsize = 0;
+	char *cipher = "cbc(aes)";
+	struct blkcipher_desc desc;
+	struct crypto_blkcipher *blkcipher = NULL;
+	char *charkey =
+		"\x12\x34\x56\x78\x90\xab\xcd\xef\x12\x34\x56\x78\x90\xab\xcd\xef";
+	char *chariv =
+		"\x12\x34\x56\x78\x90\xab\xcd\xef\x12\x34\x56\x78\x90\xab\xcd\xef";
+
+	blkcipher = crypto_alloc_blkcipher(cipher, 0, 0);
+	if (IS_ERR(blkcipher)) {
+		printk("could not allocate blkcipher handle for %s\n", cipher);
+		return -PTR_ERR(blkcipher);
+	}
+
+	if (crypto_blkcipher_setkey(blkcipher, charkey, 16)) {
+		printk("key could not be set\n");
+		ret = -EAGAIN;
+		goto out;
+	}
+
+	ivsize = crypto_blkcipher_ivsize(blkcipher);
+	if (ivsize) {
+		if (ivsize != strlen(chariv)) {
+			printk("length differs from expected length\n");
+			ret = -EINVAL;
+			goto out;
+		}
+		crypto_blkcipher_set_iv(blkcipher, chariv, ivsize);
+	}
+
+	desc.flags = 0;
+	desc.tfm = blkcipher;
+	sg_init_one(&sg, data, len);
+
+	if (enc) {
+		/* encrypt data in place */
+		ret = crypto_blkcipher_encrypt(&desc, &sg, &sg, len);
+	} else {
+		/* decrypt data in place */
+		ret = crypto_blkcipher_decrypt(&desc, &sg, &sg, len);
+	}
+
+	return ret;
+
+out:
+	crypto_free_blkcipher(blkcipher);
+	return ret;
+}
+
+static void btrfs_ablkcipher_cb(struct crypto_async_request *req, int error)
+{
+	struct btrfs_crypt_result *result;
+
+	if (error == -EINPROGRESS) {
+		pr_info("Encryption callback reports error\n");
+		return;
+	}
+
+	result = req->data;
+	result->err = error;
+	complete(&result->completion);
+	pr_info("Encryption finished successfully\n");
+}
+
+static unsigned int btrfs_ablkcipher_encdec(struct btrfs_ablkcipher_def *ablk, int enc)
+{
+	int rc = 0;
+
+	if (enc)
+		rc = crypto_ablkcipher_encrypt(ablk->req);
+	else
+		rc = crypto_ablkcipher_decrypt(ablk->req);
+
+	switch (rc) {
+	case 0:
+		break;
+	case -EINPROGRESS:
+	case -EBUSY:
+		rc = wait_for_completion_interruptible(
+			&ablk->result.completion);
+		if (!rc && !ablk->result.err) {
+			reinit_completion(&ablk->result.completion);
+			break;
+		}
+	default:
+		pr_info("ablkcipher encrypt returned with %d result %d\n",
+		       rc, ablk->result.err);
+		break;
+	}
+	init_completion(&ablk->result.completion);
+
+	return rc;
+}
+
+void btrfs_cipher_get_ivdata(char **ivdata, unsigned int ivsize, unsigned int *ivdata_size)
+{
+	/* fixme */
+	if (0) {
+		*ivdata = kmalloc(ivsize, GFP_KERNEL);
+		get_random_bytes(ivdata, ivsize);
+		*ivdata_size = ivsize;
+	} else {
+		*ivdata = kstrdup(
+			"\x12\x34\x56\x78\x90\xab\xcd\xef\x12\x34\x56\x78\x90\xab\xcd\xef",
+			GFP_NOFS);
+		*ivdata_size = strlen(*ivdata);
+	}
+}
+
+static int btrfs_do_ablkcipher(int endec, struct page *page, unsigned long len,
+							struct inode *inode)
+{
+	int ret = -EFAULT;
+	unsigned char key_data[16];
+	char *ivdata = NULL;
+	unsigned int key_size;
+	unsigned int ivsize = 0;
+	unsigned int ivdata_size;
+	unsigned int ablksize = 0;
+	struct btrfs_ablkcipher_def ablk_akin;
+	struct ablkcipher_request *req = NULL;
+	struct crypto_ablkcipher *ablkcipher = NULL;
+
+	ret = 0;
+
+	if (!inode) {
+		BUG_ON("Need inode\n");
+		return -EINVAL;
+	}
+	/* get key from the inode */
+	ret = btrfs_get_key_data_from_inode(inode, key_data);
+
+	key_size = 16; //todo: defines, but review for suitable cipher
+
+	ablkcipher = crypto_alloc_ablkcipher("cts(cbc(aes))", 0, 0);
+	if (IS_ERR(ablkcipher)) {
+		pr_info("could not allocate ablkcipher handle\n");
+		return PTR_ERR(ablkcipher);
+	}
+
+	ablksize = crypto_ablkcipher_blocksize(ablkcipher);
+	/* we can't cipher a block less the ciper block size */
+	if (len < ablksize || len > PAGE_CACHE_SIZE) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	ivsize = crypto_ablkcipher_ivsize(ablkcipher);
+	if (ivsize) {
+		btrfs_cipher_get_ivdata(&ivdata, ivsize, &ivdata_size);
+		if (ivsize != ivdata_size) {
+			BUG_ON("IV length differs from expected length\n");
+			ret = -EINVAL;
+			goto out;
+		}
+	} else {
+		BUG_ON("This cipher doesn't need ivdata, but are we ready ?\n");
+		ret = -EINVAL;
+		goto out;
+	}
+
+	req = ablkcipher_request_alloc(ablkcipher, GFP_KERNEL);
+	if (IS_ERR(req)) {
+		pr_info("could not allocate request queue\n");
+		ret = PTR_ERR(req);
+		goto out;
+	}
+
+	ablkcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG,
+					btrfs_ablkcipher_cb, &ablk_akin.result);
+
+	if (crypto_ablkcipher_setkey(ablkcipher, key_data, key_size)) {
+		printk("key could not be set\n");
+		ret = -EAGAIN;
+		goto out;
+	}
+
+	ablk_akin.tfm = ablkcipher;
+	ablk_akin.req = req;
+
+	sg_init_table(&ablk_akin.sg, 1);
+	sg_set_page(&ablk_akin.sg, page, len, 0);
+	ablkcipher_request_set_crypt(req, &ablk_akin.sg, &ablk_akin.sg, len, ivdata);
+
+	init_completion(&ablk_akin.result.completion);
+
+	ret = btrfs_ablkcipher_encdec(&ablk_akin, endec);
+
+out:
+	if (ablkcipher)
+		crypto_free_ablkcipher(ablkcipher);
+	if (req)
+		ablkcipher_request_free(req);
+
+	kfree(ivdata);
+
+	return ret;
+}
+
+static int btrfs_encrypt_pages(struct list_head *na_ws, struct address_space *mapping,
+			u64 start, unsigned long len, struct page **pages,
+			unsigned long nr_pages, unsigned long *na_out_pages,
+			unsigned long *na_total_in, unsigned long *na_total_out,
+			unsigned long na_max_out)
+{
+	int ret;
+	struct page *in_page;
+	struct page *out_page;
+	char *in;
+	char *out;
+	unsigned long bytes_left = len;
+	unsigned long cur_page_len;
+	unsigned long cur_page;
+	struct inode *inode;
+
+	*na_total_in = 0;
+	*na_out_pages = 0;
+
+	if (!mapping && !mapping->host) {
+		WARN_ON("Need mapped pages\n");
+		return -EINVAL;
+	}
+
+	inode = mapping->host;
+
+	for (cur_page = 0; cur_page < nr_pages; cur_page++) {
+
+		WARN_ON(!bytes_left);
+
+		in_page = find_get_page(mapping, start >> PAGE_CACHE_SHIFT);
+		out_page = alloc_page(GFP_NOFS| __GFP_HIGHMEM);
+		cur_page_len = min(bytes_left, PAGE_CACHE_SIZE);
+
+		in = kmap(in_page);
+		out = kmap(out_page);
+		memcpy(out, in, cur_page_len);
+		kunmap(out_page);
+		kunmap(in_page);
+
+		ret = btrfs_do_ablkcipher(1, out_page, cur_page_len, inode);
+		if (ret) {
+			__free_page(out_page);
+			return ret;
+		}
+
+		pages[cur_page] = out_page;
+		*na_out_pages = *na_out_pages + 1;
+		*na_total_in = *na_total_in + cur_page_len;
+
+		start += cur_page_len;
+		bytes_left = bytes_left - cur_page_len;
+	}
+
+	return ret;
+}
+
+static int btrfs_decrypt_pages(struct list_head *na_ws, unsigned char *in, struct page *out_page,
+				unsigned long na_start_byte, size_t in_size, size_t out_size)
+{
+	int ret;
+	char *out_addr;
+	char keytag[24];
+	struct address_space *mapping;
+	struct inode *inode;
+
+	if (!out_page)
+		return -EINVAL;
+
+	if (in_size > PAGE_CACHE_SIZE)
+		return -EINVAL;
+
+	memset(keytag, '\0', 24);
+
+	mapping = out_page->mapping;
+	if (!mapping && !mapping->host) {
+		WARN_ON("Need mapped pages\n");
+		return -EINVAL;
+	}
+
+	inode = mapping->host;
+
+	out_addr = kmap(out_page);
+	memcpy(out_addr, in, in_size);
+	kunmap(out_page);
+
+	ret = btrfs_do_ablkcipher(0, out_page, in_size, inode);
+
+	return ret;
+}
+
+static int btrfs_decrypt_pages_bio(struct list_head *na_ws, struct page **in_pages,
+					u64 in_start_offset, struct bio_vec *out_pages_bio,
+					int bi_vcnt, size_t in_len)
+{
+	char *in;
+	char *out;
+	int ret = 0;
+	struct page *in_page;
+	struct page *out_page;
+	unsigned long cur_page_n;
+	unsigned long bytes_left;
+	unsigned long in_nr_pages;
+	unsigned long cur_page_len;
+	unsigned long processed_len = 0;
+	struct address_space *mapping;
+	struct inode *inode;
+
+	if (na_ws)
+		return -EINVAL;
+
+	out_page = out_pages_bio[0].bv_page;
+	mapping = out_page->mapping;
+	if (!mapping && !mapping->host) {
+		WARN_ON("Need mapped page\n");
+		return -EINVAL;
+	}
+
+	inode = mapping->host;
+
+	in_nr_pages = DIV_ROUND_UP(in_len, PAGE_CACHE_SIZE);
+	bytes_left = in_len;
+	WARN_ON(in_nr_pages != bi_vcnt);
+
+	for (cur_page_n = 0; cur_page_n < in_nr_pages; cur_page_n++) {
+		WARN_ON(!bytes_left);
+
+		in_page = in_pages[cur_page_n];
+		out_page = out_pages_bio[cur_page_n].bv_page;
+
+		cur_page_len = min(bytes_left, PAGE_CACHE_SIZE);
+
+		in = kmap(in_page);
+		out = kmap(out_page);
+		memcpy(out, in, cur_page_len);
+		kunmap(out_page);
+		kunmap(in_page);
+
+		ret = btrfs_do_ablkcipher(0, out_page, cur_page_len, inode);
+
+		if (ret && ret != -ENOKEY)
+			goto error_out;
+
+		if (cur_page_len < PAGE_CACHE_SIZE) {
+			out = kmap(out_page);
+			memset(out + cur_page_len, 0, PAGE_CACHE_SIZE - cur_page_len);
+			kunmap(out_page);
+		}
+
+		bytes_left = bytes_left - cur_page_len;
+		processed_len = processed_len + cur_page_len;
+
+		//flush_dcache_page(out_page);
+	}
+	WARN_ON(processed_len != in_len);
+	WARN_ON(bytes_left);
+
+error_out:
+	return ret;
+}
+
+const struct btrfs_compress_op btrfs_encrypt_ops = {
+	.alloc_workspace	= NULL,
+	.free_workspace		= NULL,
+	.compress_pages		= btrfs_encrypt_pages,
+	.decompress_biovec	= btrfs_decrypt_pages_bio,
+	.decompress		= btrfs_decrypt_pages,
+};
diff --git a/fs/btrfs/encrypt.h b/fs/btrfs/encrypt.h
new file mode 100644
index 000000000000..36a7067e98b1
--- /dev/null
+++ b/fs/btrfs/encrypt.h
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2016 Oracle.  All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License v2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 021110-1307, USA.
+ */
+
+
+size_t btrfs_check_encrypt_type(char *encryption_type);
+int btrfs_update_key_data_to_binode(struct inode *inode);
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index 151b7c71b868..b27a89d89753 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -60,6 +60,7 @@
 #include "hash.h"
 #include "props.h"
 #include "qgroup.h"
+#include "encrypt.h"
 
 struct btrfs_iget_args {
 	struct btrfs_key *location;
@@ -206,6 +207,8 @@ static int insert_inline_extent(struct btrfs_trans_handle *trans,
 		}
 		btrfs_set_file_extent_compression(leaf, ei,
 						  compress_type);
+		if (compress_type == BTRFS_ENCRYPT_AES)
+			btrfs_set_file_extent_encryption(leaf, ei, 1);
 	} else {
 		page = find_get_page(inode->i_mapping,
 				     start >> PAGE_CACHE_SHIFT);
@@ -581,7 +584,7 @@ cont:
 		 * win, compare the page count read with the blocks on disk
 		 */
 		total_in = ALIGN(total_in, PAGE_CACHE_SIZE);
-		if (total_compressed >= total_in) {
+		if (total_compressed >= total_in && compress_type != BTRFS_ENCRYPT_AES) {
 			will_compress = 0;
 		} else {
 			num_bytes = total_in;
@@ -6704,6 +6707,8 @@ static noinline int uncompress_inline(struct btrfs_path *path,
 	max_size = min_t(unsigned long, PAGE_CACHE_SIZE, max_size);
 	ret = btrfs_decompress(compress_type, tmp, page,
 			       extent_offset, inline_size, max_size);
+	if (ret && ret == -ENOKEY)
+		ret = 0;
 	kfree(tmp);
 	return ret;
 }
@@ -9271,6 +9276,20 @@ static int btrfs_rename(struct inode *old_dir, struct dentry *old_dentry,
 	u64 root_objectid;
 	int ret;
 	u64 old_ino = btrfs_ino(old_inode);
+	u64 root_flags;
+	u64 dest_flags;
+
+	/*
+	 * As of now block an encrypted file/dir to move across
+	 * subvol which potentially has different key.
+	 */
+	root_flags = btrfs_root_flags(&root->root_item);
+	dest_flags = btrfs_root_flags(&dest->root_item);
+	if (root != dest &&
+		((root_flags & BTRFS_ROOT_SUBVOL_ENCRYPT) ||
+		(dest_flags & BTRFS_ROOT_SUBVOL_ENCRYPT))) {
+		return -EOPNOTSUPP;
+	}
 
 	if (btrfs_ino(new_dir) == BTRFS_EMPTY_SUBVOL_DIR_OBJECTID)
 		return -EPERM;
@@ -9931,6 +9950,22 @@ static int btrfs_permission(struct inode *inode, int mask)
 		if (BTRFS_I(inode)->flags & BTRFS_INODE_READONLY)
 			return -EACCES;
 	}
+
+	/*
+	 * Get the required key as we encrypt only file data, this
+	 * this applies only files as of now.
+	 */
+	if (S_ISREG(mode)) {
+		int ret = 0;
+		u64 root_flags;
+		root_flags = btrfs_root_flags(&root->root_item);
+		if (root_flags & BTRFS_ROOT_SUBVOL_ENCRYPT) {
+			ret = btrfs_update_key_data_to_binode(inode);
+			if (ret)
+				return -ENOKEY;
+		}
+	}
+
 	return generic_permission(inode, mask);
 }
 
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
index 48aee9846329..3a0f40e4a713 100644
--- a/fs/btrfs/ioctl.c
+++ b/fs/btrfs/ioctl.c
@@ -2139,8 +2139,15 @@ static noinline int btrfs_ioctl_tree_search(struct file *file,
 	int ret;
 	size_t buf_size;
 
+#if 0
+	/*
+	 * Todo: Workaround as of now instead of introduing a new ioctl,
+	 * so that non root user can find info about the subvol
+	 * they own/create. This must be fixed in final.
+	 */
 	if (!capable(CAP_SYS_ADMIN))
 		return -EPERM;
+#endif
 
 	uargs = (struct btrfs_ioctl_search_args __user *)argp;
 
diff --git a/fs/btrfs/props.c b/fs/btrfs/props.c
index f9e60231f685..d40ace5f5492 100644
--- a/fs/btrfs/props.c
+++ b/fs/btrfs/props.c
@@ -22,10 +22,16 @@
 #include "hash.h"
 #include "transaction.h"
 #include "xattr.h"
+#include "encrypt.h"
 
 #define BTRFS_PROP_HANDLERS_HT_BITS 8
 static DEFINE_HASHTABLE(prop_handlers_ht, BTRFS_PROP_HANDLERS_HT_BITS);
 
+#define BTRFS_PROP_INHERIT_NONE		(1U << 0)
+#define BTRFS_PROP_INHERIT_FOR_DIR	(1U << 1)
+#define BTRFS_PROP_INHERIT_FOR_CLONE	(1U << 2)
+#define BTRFS_PROP_INHERIT_FOR_SUBVOL	(1U << 3)
+
 struct prop_handler {
 	struct hlist_node node;
 	const char *xattr_name;
@@ -41,13 +47,28 @@ static int prop_compression_apply(struct inode *inode,
 				  size_t len);
 static const char *prop_compression_extract(struct inode *inode);
 
+static int prop_encrypt_validate(const char *value, size_t len);
+static int prop_encrypt_apply(struct inode *inode,
+				  const char *value, size_t len);
+static const char *prop_encrypt_extract(struct inode *inode);
+
 static struct prop_handler prop_handlers[] = {
 	{
 		.xattr_name = XATTR_BTRFS_PREFIX "compression",
 		.validate = prop_compression_validate,
 		.apply = prop_compression_apply,
 		.extract = prop_compression_extract,
-		.inheritable = 1
+		.inheritable = BTRFS_PROP_INHERIT_FOR_DIR| \
+				BTRFS_PROP_INHERIT_FOR_CLONE| \
+				BTRFS_PROP_INHERIT_FOR_SUBVOL,
+	},
+	{
+		.xattr_name = XATTR_BTRFS_PREFIX "encrypt",
+		.validate = prop_encrypt_validate,
+		.apply = prop_encrypt_apply,
+		.extract = prop_encrypt_extract,
+		.inheritable = BTRFS_PROP_INHERIT_FOR_DIR| \
+				BTRFS_PROP_INHERIT_FOR_CLONE,
 	},
 };
 
@@ -315,6 +336,13 @@ static int inherit_props(struct btrfs_trans_handle *trans,
 		if (!h->inheritable)
 			continue;
 
+		//is_subvolume_inode(); ?
+		if (btrfs_ino(inode) == BTRFS_FIRST_FREE_OBJECTID) {
+			if (!strcmp(h->xattr_name, "btrfs.encrypt")) {
+				continue;
+			}
+		}
+
 		value = h->extract(parent);
 		if (!value)
 			continue;
@@ -425,4 +453,114 @@ static const char *prop_compression_extract(struct inode *inode)
 	return NULL;
 }
 
+static int btrfs_create_encrypt_key_tuplet(char *algo, char *tag, char *val_out)
+{
+	return snprintf(val_out, 32, "%s@%s", algo, tag);
+}
+
+static int btrfs_split_key_tuplet(const char *val, size_t len,
+					char *keyalgo, char *keytag)
+{
+	char *tmp;
+	char *tmp1;
+	char *tmp2;
+
+	tmp1 = tmp = kstrdup(val, GFP_NOFS);
+	tmp[len] = '\0';
+	tmp2 = strsep(&tmp, "@");
+	if (!tmp2) {
+		kfree(tmp1);
+		return -EINVAL;
+	}
+
+	if (strlen(tmp2) > 16 || strlen(tmp) > 16) {
+		kfree(tmp1);
+		return -EINVAL;
+	}
+	strcpy(keyalgo, tmp2);
+	strcpy(keytag, tmp);
+
+	return 0;
+}
+
+/*
+ * The required foramt in the value is <encrypt_algo>@<key_tag>
+ * eg: btrfs.encrypt="aes@btrfs:61e0d004"
+ */
+static int prop_encrypt_validate(const char *value, size_t len)
+{
+	int ret;
+	char keytag[16];
+	char keyalgo[16];
+	size_t keylen;
+
+	if (!len)
+		return 0;
+
+	ret = btrfs_split_key_tuplet(value, len, keyalgo, keytag);
+	if (ret)
+		return ret;
 
+	keylen = btrfs_check_encrypt_type(keyalgo);
+	if (!keylen)
+		return -ENOTSUPP;
+
+	return ret;
+}
+
+static int prop_encrypt_apply(struct inode *inode,
+				const char *value, size_t len)
+{
+	int ret;
+	u64 root_flags;
+	char keytag[16];
+	char keyalgo[16];
+	struct btrfs_root_item *root_item;
+
+	root_item = &(BTRFS_I(inode)->root->root_item);
+
+	if (len == 0) {
+		BTRFS_I(inode)->flags &= ~BTRFS_INODE_ENCRYPT;
+		BTRFS_I(inode)->force_compress = 0;
+
+		if (btrfs_ino(inode) == BTRFS_FIRST_FREE_OBJECTID) {
+			root_flags = btrfs_root_flags(root_item);
+			btrfs_set_root_flags(root_item, root_flags | ~BTRFS_ROOT_SUBVOL_ENCRYPT);
+			memset(root_item->encrypt_algo, '\0', 16);
+			memset(root_item->encrypt_keytag, '\0', 16);
+		}
+		return 0;
+	}
+
+	BTRFS_I(inode)->flags |= BTRFS_INODE_ENCRYPT;
+	BTRFS_I(inode)->force_compress = BTRFS_ENCRYPT_AES;
+
+	ret = btrfs_split_key_tuplet(value, len, keyalgo, keytag);
+	if (ret)
+		return ret;
+
+	/* do it only for the subvol or snapshot */
+	if (btrfs_ino(inode) == BTRFS_FIRST_FREE_OBJECTID) {
+		root_flags = btrfs_root_flags(root_item);
+		btrfs_set_root_flags(root_item, root_flags | BTRFS_ROOT_SUBVOL_ENCRYPT);
+		/* TODO: this is not right, fix it */
+		strncpy(root_item->encrypt_algo, keyalgo, 16);
+		strncpy(root_item->encrypt_keytag, keytag, 16);
+	}
+
+	return 0;
+}
+
+static const char *prop_encrypt_extract(struct inode *inode)
+{
+	int ret;
+	char val[32];
+	struct btrfs_root_item *ri;
+
+	ri = &(BTRFS_I(inode)->root->root_item);
+
+	ret = btrfs_create_encrypt_key_tuplet(ri->encrypt_algo,
+					ri->encrypt_keytag, val);
+
+	return kstrdup(val, GFP_NOFS);
+}
diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c
index d41e09fe8e38..400225890f5f 100644
--- a/fs/btrfs/super.c
+++ b/fs/btrfs/super.c
@@ -59,10 +59,10 @@
 #include "free-space-cache.h"
 #include "backref.h"
 #include "tests/btrfs-tests.h"
-
 #include "qgroup.h"
 #define CREATE_TRACE_POINTS
 #include <trace/events/btrfs.h>
+#include "encrypt.h"
 
 static const struct super_operations btrfs_super_ops;
 static struct file_system_type btrfs_fs_type;
@@ -92,6 +92,9 @@ const char *btrfs_decode_error(int errno)
 	case -ENOENT:
 		errstr = "No such entry";
 		break;
+	case -ENOKEY:
+		errstr = "Required key not available";
+		break;
 	}
 
 	return errstr;
-- 
2.7.0


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

* [RFC PATCH 1/2] btrfs-progs: subvolume functions reorg
  2016-03-01 16:08 [RFC] Experimental btrfs encryption Anand Jain
  2016-03-01 16:08 ` [RFC PATCH 1/1] btrfs: Encryption: Add btrfs encryption support Anand Jain
@ 2016-03-01 16:08 ` Anand Jain
  2016-03-01 16:08 ` [RFC PATCH 2/2] btrfs-progs: Encryption: add encrypt sub cli Anand Jain
                   ` (4 subsequent siblings)
  6 siblings, 0 replies; 26+ messages in thread
From: Anand Jain @ 2016-03-01 16:08 UTC (permalink / raw)
  To: linux-btrfs; +Cc: clm, dsterba, Anand Jain

Make few subvol related functions usable outside of
subvol command set.

Signed-off-by: Anand Jain <anand.jain@oracle.com>
---
 Makefile.in      |   2 +-
 cmds-qgroup.c    |   1 +
 cmds-send.c      |  12 +----
 cmds-subvolume.c | 102 +++++++------------------------------
 subvolume.c      | 152 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 subvolume.h      |  20 ++++++++
 6 files changed, 193 insertions(+), 96 deletions(-)
 create mode 100644 subvolume.c
 create mode 100644 subvolume.h

diff --git a/Makefile.in b/Makefile.in
index 918478968be8..6faf94dedf90 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -70,7 +70,7 @@ objects = ctree.o disk-io.o radix-tree.o extent-tree.o print-tree.o \
 	  extent-cache.o extent_io.o volumes.o utils.o repair.o \
 	  qgroup.o raid6.o free-space-cache.o list_sort.o props.o \
 	  ulist.o qgroup-verify.o backref.o string-table.o task-utils.o \
-	  inode.o file.o find-root.o free-space-tree.o help.o
+	  inode.o file.o find-root.o free-space-tree.o help.o subvolume.o
 cmds_objects = cmds-subvolume.o cmds-filesystem.o cmds-device.o cmds-scrub.o \
 	       cmds-inspect.o cmds-balance.o cmds-send.o cmds-receive.o \
 	       cmds-quota.o cmds-qgroup.o cmds-replace.o cmds-check.o \
diff --git a/cmds-qgroup.c b/cmds-qgroup.c
index db5ee21a83bf..6921226acb26 100644
--- a/cmds-qgroup.c
+++ b/cmds-qgroup.c
@@ -26,6 +26,7 @@
 #include "commands.h"
 #include "qgroup.h"
 #include "utils.h"
+#include "subvolume.h"
 
 static const char * const qgroup_cmd_group_usage[] = {
 	"btrfs qgroup <command> [options] <path>",
diff --git a/cmds-send.c b/cmds-send.c
index 3e34d75bb834..000fd5638039 100644
--- a/cmds-send.c
+++ b/cmds-send.c
@@ -43,6 +43,7 @@
 
 #include "send.h"
 #include "send-utils.h"
+#include "subvolume.h"
 
 static int g_verbose = 0;
 
@@ -335,17 +336,6 @@ out:
 	return ret;
 }
 
-char *get_subvol_name(char *mnt, char *full_path)
-{
-	int len = strlen(mnt);
-	if (!len)
-		return full_path;
-	if (mnt[len - 1] != '/')
-		len += 1;
-
-	return full_path + len;
-}
-
 static int init_root_path(struct btrfs_send *s, const char *subvol)
 {
 	int ret = 0;
diff --git a/cmds-subvolume.c b/cmds-subvolume.c
index 9d9b0af71d38..f9953ab7adb7 100644
--- a/cmds-subvolume.c
+++ b/cmds-subvolume.c
@@ -37,6 +37,7 @@
 #include "utils.h"
 #include "btrfs-list.h"
 #include "utils.h"
+#include "subvolume.h"
 
 static int is_subvolume_cleaned(int fd, u64 subvolid)
 {
@@ -224,33 +225,6 @@ out:
 	return retval;
 }
 
-/*
- * Test if path is a subvolume
- * Returns:
- *   0 - path exists but it is not a subvolume
- *   1 - path exists and it is  a subvolume
- * < 0 - error
- */
-int test_issubvolume(const char *path)
-{
-	struct stat	st;
-	struct statfs stfs;
-	int		res;
-
-	res = stat(path, &st);
-	if (res < 0)
-		return -errno;
-
-	if (st.st_ino != BTRFS_FIRST_FREE_OBJECTID || !S_ISDIR(st.st_mode))
-		return 0;
-
-	res = statfs(path, &stfs);
-	if (res < 0)
-		return -errno;
-
-	return (int)stfs.f_type == BTRFS_SUPER_MAGIC;
-}
-
 static int wait_for_commit(int fd)
 {
 	int ret;
@@ -932,12 +906,11 @@ static int cmd_subvol_show(int argc, char **argv)
 	struct btrfs_list_filter_set *filter_set;
 	char tstr[256];
 	char uuidparse[BTRFS_UUID_UNPARSED_SIZE];
-	char *fullpath = NULL, *svpath = NULL, *mnt = NULL;
+	char *fullpath = NULL;
 	char raw_prefix[] = "\t\t\t\t";
-	u64 sv_id;
-	int fd = -1, mntfd = -1;
+	int fd = -1;
 	int ret = 1;
-	DIR *dirstream1 = NULL, *dirstream2 = NULL;
+	DIR *dirstream1 = NULL;
 
 	clean_args_no_options(argc, argv, cmd_subvol_show_usage);
 
@@ -951,57 +924,14 @@ static int cmd_subvol_show(int argc, char **argv)
 		goto out;
 	}
 
-	ret = test_issubvolume(fullpath);
-	if (ret < 0) {
-		error("cannot access subvolume %s: %s", fullpath,
-			strerror(-ret));
-		goto out;
-	}
-	if (!ret) {
-		error("not a subvolume: %s", fullpath);
-		ret = 1;
-		goto out;
-	}
-
-	ret = find_mount_root(fullpath, &mnt);
-	if (ret < 0) {
-		error("find_mount_root failed on '%s': %s",
-			fullpath, strerror(-ret));
-		goto out;
-	}
-	if (ret > 0) {
-		error("%s doesn't belong to btrfs mount point", fullpath);
-		goto out;
-	}
-	ret = 1;
-	svpath = get_subvol_name(mnt, fullpath);
-
-	fd = btrfs_open_dir(fullpath, &dirstream1, 1);
-	if (fd < 0)
-		goto out;
-
-	ret = btrfs_list_get_path_rootid(fd, &sv_id);
+	ret = btrfs_get_subvol_info(fullpath, &get_ri);
 	if (ret) {
-		error("can't get rootid for '%s'", fullpath);
-		goto out;
-	}
-
-	mntfd = btrfs_open_dir(mnt, &dirstream2, 1);
-	if (mntfd < 0)
-		goto out;
-
-	if (sv_id == BTRFS_FS_TREE_OBJECTID) {
-		printf("%s is toplevel subvolume\n", fullpath);
-		goto out;
-	}
-
-	memset(&get_ri, 0, sizeof(get_ri));
-	get_ri.root_id = sv_id;
-
-	ret = btrfs_get_subvol(mntfd, &get_ri);
-	if (ret) {
-		error("can't find '%s'", svpath);
-		goto out;
+		ret < 0 ?
+			fprintf(stderr, "Failed to get subvol info %s: %s\n",
+							fullpath, strerror(ret)):
+			fprintf(stderr, "Failed to get subvol info %s: %d\n",
+							fullpath, ret);
+		return ret;
 	}
 
 	/* print the info */
@@ -1052,19 +982,23 @@ static int cmd_subvol_show(int argc, char **argv)
 	btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_BY_PARENT,
 				(u64)(unsigned long)get_ri.uuid);
 	btrfs_list_setup_print_column(BTRFS_LIST_PATH);
+
+	fd = open_file_or_dir(fullpath, &dirstream1);
+	if (fd < 0) {
+		fprintf(stderr, "ERROR: can't access '%s'\n", fullpath);
+		goto out;
+	}
 	btrfs_list_subvols_print(fd, filter_set, NULL, BTRFS_LIST_LAYOUT_RAW,
 			1, raw_prefix);
 
+out:
 	/* clean up */
 	free(get_ri.path);
 	free(get_ri.name);
 	free(get_ri.full_path);
 	btrfs_list_free_filter_set(filter_set);
 
-out:
 	close_file_or_dir(fd, dirstream1);
-	close_file_or_dir(mntfd, dirstream2);
-	free(mnt);
 	free(fullpath);
 	return !!ret;
 }
diff --git a/subvolume.c b/subvolume.c
new file mode 100644
index 000000000000..866285e02611
--- /dev/null
+++ b/subvolume.c
@@ -0,0 +1,152 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License v2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 021110-1307, USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <libgen.h>
+#include <limits.h>
+#include <getopt.h>
+#include <uuid/uuid.h>
+#include <linux/magic.h>
+#include <sys/vfs.h>
+
+#include "kerncompat.h"
+#include "ioctl.h"
+#include "qgroup.h"
+
+#include "ctree.h"
+#include "commands.h"
+#include "utils.h"
+#include "btrfs-list.h"
+#include "utils.h"
+#include "subvolume.h"
+
+/*
+ * Test if path is a subvolume
+ * Returns:
+ *   0 - path exists but it is not a subvolume
+ *   1 - path exists and it is  a subvolume
+ * < 0 - error
+ */
+int test_issubvolume(const char *path)
+{
+	struct stat	st;
+	struct statfs stfs;
+	int		res;
+
+	res = stat(path, &st);
+	if (res < 0)
+		return -errno;
+
+	if (st.st_ino != BTRFS_FIRST_FREE_OBJECTID || !S_ISDIR(st.st_mode))
+		return 0;
+
+	res = statfs(path, &stfs);
+	if (res < 0)
+		return -errno;
+
+	return (int)stfs.f_type == BTRFS_SUPER_MAGIC;
+}
+
+char *get_subvol_name(char *mnt, char *full_path)
+{
+	int len = strlen(mnt);
+	if (!len)
+		return full_path;
+
+	if (mnt[len - 1] != '/')
+		len += 1;
+
+	return full_path + len;
+}
+
+/*
+ * fixme: remove the error being printed here, move it to the
+ * leaf function
+ */
+int btrfs_get_subvol_info(char *fullpath, struct root_info *get_ri)
+{
+	u64 sv_id;
+	int ret = 1;
+	int fd = -1;
+	int mntfd = -1;
+	char *mnt = NULL;
+	char *svpath = NULL;
+	DIR *dirstream1 = NULL;
+	DIR *dirstream2 = NULL;
+
+	ret = test_issubvolume(fullpath);
+	if (ret < 0) {
+		error("cannot access subvolume %s: %s", fullpath,
+			strerror(-ret));
+		goto out;
+	}
+	if (!ret) {
+		error("not a subvolume: %s", fullpath);
+		ret = 1;
+		goto out;
+	}
+
+	ret = find_mount_root(fullpath, &mnt);
+	if (ret < 0) {
+		error("find_mount_root failed on '%s': %s",
+			fullpath, strerror(-ret));
+		goto out;
+	}
+	if (ret > 0) {
+		error("%s doesn't belong to btrfs mount point", fullpath);
+		goto out;
+	}
+	ret = 1;
+	svpath = get_subvol_name(mnt, fullpath);
+
+	fd = btrfs_open_dir(fullpath, &dirstream1, 1);
+	if (fd < 0)
+		goto out;
+
+	ret = btrfs_list_get_path_rootid(fd, &sv_id);
+	if (ret) {
+		error("can't get rootid for '%s'", fullpath);
+		goto out;
+	}
+
+	mntfd = btrfs_open_dir(mnt, &dirstream2, 1);
+	if (mntfd < 0)
+		goto out;
+
+	if (sv_id == BTRFS_FS_TREE_OBJECTID) {
+		printf("%s is toplevel subvolume\n", fullpath);
+		goto out;
+	}
+
+	memset(get_ri, 0, sizeof(*get_ri));
+	get_ri->root_id = sv_id;
+
+	ret = btrfs_get_subvol(mntfd, get_ri);
+	if (ret) {
+		fprintf(stderr, "ERROR: can't find '%s'\n",
+			svpath);
+		goto out;
+	}
+
+out:
+	return ret;
+}
diff --git a/subvolume.h b/subvolume.h
new file mode 100644
index 000000000000..ceacf603e01c
--- /dev/null
+++ b/subvolume.h
@@ -0,0 +1,20 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License v2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 021110-1307, USA.
+ */
+#include "btrfs-list.h"
+
+int btrfs_get_subvol_info(char *fullpath, struct root_info *get_ri);
+int test_issubvolume(const char *path);
+char *get_subvol_name(char *mnt, char *full_path);
-- 
2.7.0


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

* [RFC PATCH 2/2] btrfs-progs: Encryption: add encrypt sub cli
  2016-03-01 16:08 [RFC] Experimental btrfs encryption Anand Jain
  2016-03-01 16:08 ` [RFC PATCH 1/1] btrfs: Encryption: Add btrfs encryption support Anand Jain
  2016-03-01 16:08 ` [RFC PATCH 1/2] btrfs-progs: subvolume functions reorg Anand Jain
@ 2016-03-01 16:08 ` Anand Jain
  2016-03-01 16:29 ` [RFC] Experimental btrfs encryption Tomasz Torcz
                   ` (3 subsequent siblings)
  6 siblings, 0 replies; 26+ messages in thread
From: Anand Jain @ 2016-03-01 16:08 UTC (permalink / raw)
  To: linux-btrfs; +Cc: clm, dsterba, Anand Jain

*** Warning: Experimental cli and codes ***

This is the btrfs-progs part of btrfs encryption. The branch
is based on btrfs-progs v4.4.1.

Depends on keyctl-utils and libscrypt packages.

Signed-off-by: Anand Jain <anand.jain@oracle.com>
---
 Makefile.in      |   5 +-
 btrfs-list.c     |  33 +++++
 cmds-subvolume.c | 107 ++++++++++++++--
 commands.h       |   1 +
 encrypt.c        | 383 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 encrypt.h        |  33 +++++
 props.c          |   3 +
 subvolume.h      |   2 +
 8 files changed, 551 insertions(+), 16 deletions(-)
 create mode 100644 encrypt.c
 create mode 100644 encrypt.h

diff --git a/Makefile.in b/Makefile.in
index 6faf94dedf90..9c77461b76c6 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -34,7 +34,7 @@ DISABLE_DOCUMENTATION = @DISABLE_DOCUMENTATION@
 DISABLE_BTRFSCONVERT = @DISABLE_BTRFSCONVERT@
 
 EXTRA_CFLAGS :=
-EXTRA_LDFLAGS :=
+EXTRA_LDFLAGS := /usr/lib/libscrypt.so.0 /usr/lib/libkeyutils.so
 
 # Common build flags
 CFLAGS = @CFLAGS@ \
@@ -70,7 +70,8 @@ objects = ctree.o disk-io.o radix-tree.o extent-tree.o print-tree.o \
 	  extent-cache.o extent_io.o volumes.o utils.o repair.o \
 	  qgroup.o raid6.o free-space-cache.o list_sort.o props.o \
 	  ulist.o qgroup-verify.o backref.o string-table.o task-utils.o \
-	  inode.o file.o find-root.o free-space-tree.o help.o subvolume.o
+	  inode.o file.o find-root.o free-space-tree.o help.o subvolume.o \
+	  encrypt.o
 cmds_objects = cmds-subvolume.o cmds-filesystem.o cmds-device.o cmds-scrub.o \
 	       cmds-inspect.o cmds-balance.o cmds-send.o cmds-receive.o \
 	       cmds-quota.o cmds-qgroup.o cmds-replace.o cmds-check.o \
diff --git a/btrfs-list.c b/btrfs-list.c
index 2da54bf706f3..feffddd9c2cf 100644
--- a/btrfs-list.c
+++ b/btrfs-list.c
@@ -1912,3 +1912,36 @@ int btrfs_list_get_path_rootid(int fd, u64 *treeid)
 	*treeid = args.treeid;
 	return 0;
 }
+
+int wait_for_commit(int fd)
+{
+	int ret;
+
+	ret = ioctl(fd, BTRFS_IOC_START_SYNC, NULL);
+	if (ret < 0)
+		return ret;
+	return ioctl(fd, BTRFS_IOC_WAIT_SYNC, NULL);
+}
+
+/*
+ * A kind of workaround as of now, fixme to a per subvol only sync
+ * instead of entire FS.
+ */
+int wait_for_commit_subvol(char *subvol)
+{
+	int fd;
+	int ret;
+	DIR *ds;
+
+	fd = open_file_or_dir3(subvol, &ds, O_RDWR);
+	if (fd == -1) {
+		ret = -errno;
+		fprintf(stderr, "ERROR: open '%s' failed: %s\n",
+					subvol, strerror(-ret));
+		return ret;
+	}
+
+	ret = wait_for_commit(fd);
+	close_file_or_dir(fd, ds);
+	return ret;
+}
diff --git a/cmds-subvolume.c b/cmds-subvolume.c
index f9953ab7adb7..02a26778ccd2 100644
--- a/cmds-subvolume.c
+++ b/cmds-subvolume.c
@@ -27,6 +27,8 @@
 #include <getopt.h>
 #include <uuid/uuid.h>
 #include <linux/magic.h>
+#include <keyutils.h>
+#include <fcntl.h>
 
 #include "kerncompat.h"
 #include "ioctl.h"
@@ -38,6 +40,7 @@
 #include "btrfs-list.h"
 #include "utils.h"
 #include "subvolume.h"
+#include "encrypt.h"
 
 static int is_subvolume_cleaned(int fd, u64 subvolid)
 {
@@ -113,6 +116,8 @@ static const char * const cmd_subvol_create_usage[] = {
 	"",
 	"-i <qgroupid>  add the newly created subvolume to a qgroup. This",
 	"               option can be given multiple times.",
+	"-e <type>      encrypt file data in this subvol with <type>",
+	"               as of now only 'aes' type is supported",
 	NULL
 };
 
@@ -127,10 +132,11 @@ static int cmd_subvol_create(int argc, char **argv)
 	char	*dst;
 	struct btrfs_qgroup_inherit *inherit = NULL;
 	DIR	*dirstream = NULL;
+	int	encrypt = 0;
 
 	optind = 1;
 	while (1) {
-		int c = getopt(argc, argv, "c:i:v");
+		int c = getopt(argc, argv, "ec:i:v");
 		if (c < 0)
 			break;
 
@@ -149,6 +155,10 @@ static int cmd_subvol_create(int argc, char **argv)
 				goto out;
 			}
 			break;
+		case 'e':
+			encrypt = 1;
+			/* as of now no optarg just use default encrypt type AES*/
+			break;
 		default:
 			usage(cmd_subvol_create_usage);
 		}
@@ -215,6 +225,10 @@ static int cmd_subvol_create(int argc, char **argv)
 		goto out;
 	}
 
+	if (encrypt && btrfs_set_subvol_encrypt(dst))
+		fprintf(stderr,
+			"Failed to set encrypt flag, try using property command\n");
+
 	retval = 0;	/* success */
 out:
 	close_file_or_dir(fddst, dirstream);
@@ -225,16 +239,6 @@ out:
 	return retval;
 }
 
-static int wait_for_commit(int fd)
-{
-	int ret;
-
-	ret = ioctl(fd, BTRFS_IOC_START_SYNC, NULL);
-	if (ret < 0)
-		return ret;
-	return ioctl(fd, BTRFS_IOC_WAIT_SYNC, NULL);
-}
-
 static const char * const cmd_subvol_delete_usage[] = {
 	"btrfs subvolume delete [options] <subvolume> [<subvolume>...]",
 	"Delete subvolume(s)",
@@ -911,6 +915,9 @@ static int cmd_subvol_show(int argc, char **argv)
 	int fd = -1;
 	int ret = 1;
 	DIR *dirstream1 = NULL;
+	key_serial_t key_serial;
+	char key_algo[BTRFS_KEY_ALGO_MAX_LEN + 1];
+	char key_tag[BTRFS_KEY_TAG_MAX_LEN + 1];
 
 	clean_args_no_options(argc, argv, cmd_subvol_show_usage);
 
@@ -921,7 +928,7 @@ static int cmd_subvol_show(int argc, char **argv)
 	if (!fullpath) {
 		error("cannot find real path for '%s': %s",
 			argv[optind], strerror(errno));
-		goto out;
+		return ret;
 	}
 
 	ret = btrfs_get_subvol_info(fullpath, &get_ri);
@@ -931,6 +938,7 @@ static int cmd_subvol_show(int argc, char **argv)
 							fullpath, strerror(ret)):
 			fprintf(stderr, "Failed to get subvol info %s: %d\n",
 							fullpath, ret);
+		free(fullpath);
 		return ret;
 	}
 
@@ -976,6 +984,23 @@ static int cmd_subvol_show(int argc, char **argv)
 	else
 		printf("\tFlags: \t\t\t-\n");
 
+	key_serial = 0;
+	memset(key_algo, '\0', BTRFS_KEY_ALGO_MAX_LEN + 1);
+	memset(key_tag, '\0', BTRFS_KEY_TAG_MAX_LEN + 1);
+
+	ret = btrfs_subvol_key_info(fullpath, key_algo, key_tag, &key_serial);
+	if (strlen(key_tag)) {
+		char key_state[256] = {0};
+		if (key_serial == -1)
+			snprintf(key_state, 256, "(%s)", strerror(-ret));
+		else
+			snprintf(key_state, 256, "(%d)", key_serial);
+
+		printf("\tEncryption: \t\t%s@%s %s\n", key_algo, key_tag, key_state);
+	} else {
+		printf("\tEncryption: \t\t%s\n", "none");
+	}
+
 	/* print the snapshots of the given subvol if any*/
 	printf("\tSnapshot(s):\n");
 	filter_set = btrfs_list_alloc_filter_set();
@@ -990,19 +1015,72 @@ static int cmd_subvol_show(int argc, char **argv)
 	}
 	btrfs_list_subvols_print(fd, filter_set, NULL, BTRFS_LIST_LAYOUT_RAW,
 			1, raw_prefix);
+	btrfs_list_free_filter_set(filter_set);
+	close_file_or_dir(fd, dirstream1);
 
 out:
 	/* clean up */
 	free(get_ri.path);
 	free(get_ri.name);
 	free(get_ri.full_path);
-	btrfs_list_free_filter_set(filter_set);
 
-	close_file_or_dir(fd, dirstream1);
 	free(fullpath);
 	return !!ret;
 }
 
+static const char * const cmd_subvol_encrypt_usage[] = {
+	"btrfs subvolume encrypt <option> <subvol-path>",
+	"Encryption key login / logout",
+	"-k|--key <in|out>     Key login or logout",
+	NULL
+};
+
+static int cmd_subvol_encrypt(int argc, char **argv)
+{
+	int ret;
+	int login;
+	optind = 1;
+
+	login = 1;
+	while (1) {
+		int c;
+		static const struct option long_options[] = {
+			{ "key", required_argument, NULL, 'k'},
+			{ NULL, 0, NULL, 0}
+		};
+
+		c = getopt_long(argc, argv, "k:", long_options, NULL);
+		if (c < 0)
+			break;
+
+		switch (c) {
+		case 'k':
+			if (!strcmp("in", optarg))
+				login = 1;
+			else if (!strcmp("out", optarg))
+				login = 0;
+			else
+				usage(cmd_subvol_encrypt_usage);
+			break;
+		default:
+			usage(cmd_subvol_encrypt_usage);
+		}
+	}
+
+	if (check_argc_exact(argc - optind, 1))
+		usage(cmd_subvol_encrypt_usage);
+
+	if (login)
+		ret = cmd_encrypt_login(argc - 1, &argv[1]);
+	else
+		ret = cmd_encrypt_logout(argc - 1, &argv[1]);
+
+	if (ret == -EAGAIN)
+		usage(cmd_subvol_encrypt_usage);
+
+	return ret;
+}
+
 static const char * const cmd_subvol_sync_usage[] = {
 	"btrfs subvolume sync <path> [<subvol-id>...]",
 	"Wait until given subvolume(s) are completely removed from the filesystem.",
@@ -1270,6 +1348,7 @@ const struct cmd_group subvolume_cmd_group = {
 		{ "find-new", cmd_subvol_find_new, cmd_subvol_find_new_usage,
 			NULL, 0 },
 		{ "show", cmd_subvol_show, cmd_subvol_show_usage, NULL, 0 },
+		{ "encrypt", cmd_subvol_encrypt, cmd_subvol_encrypt_usage, NULL, 0 },
 		{ "sync", cmd_subvol_sync, cmd_subvol_sync_usage, NULL, 0 },
 		NULL_CMD_STRUCT
 	}
diff --git a/commands.h b/commands.h
index 2da093bf81a3..3199424bbe77 100644
--- a/commands.h
+++ b/commands.h
@@ -88,6 +88,7 @@ extern const struct cmd_group subvolume_cmd_group;
 extern const struct cmd_group filesystem_cmd_group;
 extern const struct cmd_group balance_cmd_group;
 extern const struct cmd_group device_cmd_group;
+extern const struct cmd_group encrypt_cmd_group;
 extern const struct cmd_group scrub_cmd_group;
 extern const struct cmd_group inspect_cmd_group;
 extern const struct cmd_group property_cmd_group;
diff --git a/encrypt.c b/encrypt.c
new file mode 100644
index 000000000000..a88fa00ba39c
--- /dev/null
+++ b/encrypt.c
@@ -0,0 +1,383 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License v2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 021110-1307, USA.
+ */
+
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/xattr.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <uuid/uuid.h>
+#include <keyutils.h>
+#include <libscrypt.h>
+#include <termios.h>
+#include <keyutils.h>
+
+#include "ctree.h"
+#include "commands.h"
+#include "utils.h"
+#include "props.h"
+#include "encrypt.h"
+#include "subvolume.h"
+
+#ifndef XATTR_BTRFS_PREFIX
+#define XATTR_BTRFS_PREFIX     "btrfs."
+#define XATTR_BTRFS_PREFIX_LEN (sizeof(XATTR_BTRFS_PREFIX) - 1)
+#endif
+
+/*
+ * Defined as synonyms in attr/xattr.h
+ */
+#ifndef ENOATTR
+#define ENOATTR ENODATA
+#endif
+
+ssize_t __get_pass(char *prompt, char **lineptr, size_t *n)
+{
+	struct termios old, new;
+	int nread;
+
+	fprintf(stderr, "%s", prompt);
+	fflush(stderr);
+
+	/* Turn echoing off and fail if we can’t. */
+	if (tcgetattr(fileno(stdin), &old) != 0)
+		return -1;
+
+	new = old;
+	new.c_lflag &= ~ECHO;
+	if (tcsetattr(fileno(stdin), TCSAFLUSH, &new) != 0)
+		return -1;
+
+	/* Read the password. */
+	nread = getline(lineptr, n, stdin);
+
+	/* Restore terminal. */
+	tcsetattr(fileno(stdin), TCSAFLUSH, &old);
+
+	return nread;
+}
+
+/*
+ * If key is set, returns its key_serial, otherwise -1
+ */
+int ask_key_for_keytag(char *keytag, key_serial_t *keyserial)
+{
+	size_t sz;
+	int retry;
+	char pass_try1[100];
+	unsigned char pass_key[16];
+	size_t tmp_sz;
+	char *pass = pass_try1;
+	const unsigned char salt[100] = {"btrfs"};
+	int ret = 0;
+
+	tmp_sz = sizeof(pass_try1);
+	retry = 4;
+	while (--retry > 0) {
+		sz = __get_pass("Paraphrase: ", &pass, &tmp_sz);
+		if (!sz || sz == 1) {
+			fprintf(stderr, " Password can not be empty, pls try again\n");
+			continue;
+		}
+		break;
+	}
+	if (retry == 0)
+		return -ECANCELED;
+
+	printf("\n");
+	if (libscrypt_scrypt((uint8_t *)pass_try1, sz, salt, sizeof(salt),
+					SCRYPT_N, SCRYPT_r, SCRYPT_p, pass_key, 16)) {
+		fprintf(stderr, "scrypt failed: cannot derive passphrase\n");
+		return -EFAULT;
+	}
+
+	*keyserial = add_key("user", keytag, pass_key, 16, KEY_SPEC_USER_KEYRING);
+	if (*keyserial == -1) {
+		ret = -errno;
+		return ret;
+	}
+
+	return 0;
+}
+
+void btrfs_create_keytag(char *keytag, char *subvol)
+{
+	key_serial_t keyserial;
+	struct root_info get_ri;
+	char uuidparse[BTRFS_UUID_UNPARSED_SIZE];
+
+	btrfs_get_subvol_info(subvol, &get_ri);
+	uuid_unparse(get_ri.uuid, uuidparse);
+	uuidparse[8] = '\0';
+	sprintf(keytag, "btrfs:%s", uuidparse);
+
+	ask_key_for_keytag(keytag, &keyserial);
+}
+
+void btrfs_create_encrypt_keytag_tuplet(char *keystr,
+			const char *encrypt_type, char *keytag)
+{
+	int len;
+
+	len = snprintf(keystr, 256, "%s@%s", encrypt_type, keytag);
+	keystr[len] = '\0';
+}
+
+/*
+ * This probably should be as a property, however the property interface
+ * needs redesign, so as of now its part of subvolume create
+ */
+static int __prop_encrypt(enum prop_object_type type, const char *object,
+			const char *name, const char *value, char *value_out)
+{
+	int ret;
+	ssize_t sret;
+	int fd = -1;
+	DIR *dirstream = NULL;
+	char buf[BTRFS_KEY_ALGO_TAG_MAX_LEN];
+	char *xattr_name = NULL;
+	int open_flags = value ? O_RDWR : O_RDONLY;
+	char keytag[16] = {0};
+	char *tmp_object = strdup(object);
+
+	ret = 0;
+	fd = open_file_or_dir3(object, &dirstream, open_flags);
+	if (fd == -1) {
+		ret = -errno;
+		fprintf(stderr, "ERROR: open %s failed. %s\n",
+						object, strerror(-ret));
+		goto out;
+	}
+
+	xattr_name = malloc(XATTR_BTRFS_PREFIX_LEN + strlen(name) + 1);
+	if (!xattr_name) {
+		ret = -ENOMEM;
+		goto out;
+	}
+	memcpy(xattr_name, XATTR_BTRFS_PREFIX, XATTR_BTRFS_PREFIX_LEN);
+	memcpy(xattr_name + XATTR_BTRFS_PREFIX_LEN, name, strlen(name));
+	xattr_name[XATTR_BTRFS_PREFIX_LEN + strlen(name)] = '\0';
+
+	if (value) {
+		char keystr[BTRFS_KEY_ALGO_TAG_MAX_LEN];
+		memset(keystr, '\0', BTRFS_KEY_ALGO_TAG_MAX_LEN);
+
+		if (strlen(value)) {
+			btrfs_create_keytag(keytag, tmp_object);
+			btrfs_create_encrypt_keytag_tuplet(keystr, value, keytag);
+		}
+		sret = fsetxattr(fd, xattr_name, keystr, strlen(keystr), 0);
+		if (sret) {
+			ret = -errno;
+			goto out;
+		}
+	} else {
+		sret = fgetxattr(fd, xattr_name, buf, BTRFS_KEY_ALGO_TAG_MAX_LEN);
+		ret = -errno;
+		if (sret < 0 && errno == ENOATTR)
+			goto out;
+
+		if (sret < 0)
+			goto out;
+
+		ret = 0;
+		buf[sret] = '\0';
+		if (value_out)
+			strncpy(value_out, buf, BTRFS_KEY_ALGO_TAG_MAX_LEN);
+	}
+
+out:
+	kfree(tmp_object);
+	kfree(xattr_name);
+	if (fd >= 0)
+		close_file_or_dir(fd, dirstream);
+
+	return ret;
+}
+
+int prop_encrypt(enum prop_object_type type, const char *object,
+			const char *name, const char *value)
+{
+	if (value) {
+		fprintf(stderr, "Property set is not allowed at this time\n");
+		return 0;
+	}
+	return 	__prop_encrypt(type, object, name, value, NULL);
+}
+
+int btrfs_set_subvol_encrypt(char *subvol)
+{
+	int ret;
+
+	ret = __prop_encrypt(prop_object_subvol, subvol, "encrypt", "aes", NULL);
+
+	return ret;
+}
+
+int btrfs_get_subvol_encrypt(char *subvol, char *value_out)
+{
+	int ret;
+
+	ret = __prop_encrypt(prop_object_subvol, subvol, "encrypt", NULL, value_out);
+
+	return ret;
+}
+
+static int split_key_alog_tag(const char *val, size_t len,
+                                        char *keyalgo, char *keytag)
+{
+	char *tmp;
+	char *tmp1;
+	char *tmp2;
+
+	tmp1 = tmp = strdup(val);
+	tmp[len] = '\0';
+
+	tmp2 = strsep(&tmp, "@");
+	if (!tmp2) {
+		kfree(tmp1);
+		return -EINVAL;
+	}
+
+	if (strlen(tmp2) > BTRFS_KEY_ALGO_MAX_LEN ||
+		strlen(tmp) > BTRFS_KEY_TAG_MAX_LEN) {
+		kfree(tmp1);
+		return -EINVAL;
+	}
+
+	if (keyalgo)
+		strcpy(keyalgo, tmp2);
+	if (keytag)
+		strcpy(keytag, tmp);
+
+	kfree(tmp1);
+	return 0;
+}
+
+int btrfs_subvol_key_info(char *subvol, char *key_algo, char *key_tag,
+						key_serial_t *key_serial)
+{
+	int ret;
+	char key_algo_tag[BTRFS_KEY_ALGO_TAG_MAX_LEN];
+
+	ret = btrfs_get_subvol_encrypt(subvol, key_algo_tag);
+	if (ret) {
+		#if 0
+		fprintf(stderr, "ERROR: non encrypted subvolume %s: %s\n",
+						subvol, strerror(-ret));
+		fprintf(stderr,
+			"       use 'btrfs subvolume create -e <subvol>' to create an encrypted subvolume\n");
+		#endif
+		return ret;
+	}
+
+	ret = split_key_alog_tag(key_algo_tag, strlen(key_algo_tag), key_algo, key_tag);
+	if (ret) {
+		fprintf(stderr, "ERROR: failed to parse key_tag in %s: %d\n",
+			key_algo_tag, ret);
+		return ret;
+	}
+
+	*key_serial = request_key("user", key_tag, NULL, 0);
+	if (*key_serial == -1) {
+		ret = -errno;
+		if (ret == -ENOKEY || ret == -EKEYEXPIRED || ret == -EKEYREVOKED)
+			ret = -ENOKEY;
+		return ret;
+	}
+
+	return 0;
+}
+
+int cmd_encrypt_login(int argc, char **argv)
+{
+	int ret;
+	char pr[10];
+	char *subvol;
+	key_serial_t keyserial;
+	char key_algo[BTRFS_KEY_ALGO_MAX_LEN + 1];
+	char key_tag[BTRFS_KEY_TAG_MAX_LEN + 1];
+
+	ret = 0;
+	keyserial = 0;
+	strcpy(pr, "already");
+
+#if 0
+	if (check_argc_exact(argc - optind, 1))
+		usage(cmd_encrypt_login_usage);
+#endif
+
+	subvol = argv[argc - 1];
+
+	ret = btrfs_subvol_key_info(subvol, key_algo, key_tag, &keyserial);
+	if (ret && ret != -ENOKEY) {
+		fprintf(stderr, "ERROR: %s\n", strerror(-ret));
+		return ret;
+	}
+
+	if (keyserial == -1) {
+		strcpy(pr, "");
+
+		wait_for_commit_subvol(subvol);
+		ret = ask_key_for_keytag(key_tag, &keyserial);
+		if (ret) {
+			fprintf(stderr, "ERROR: key add failed: %s\n", strerror(-ret));
+			return ret;
+		}
+	}
+
+	fprintf(stderr,
+		"key for '%s' has %s logged in with keytag '%s' keyserial '%d'\n",
+		subvol, pr, key_tag, keyserial);
+
+	return 0;
+}
+
+int cmd_encrypt_logout(int argc, char **argv)
+{
+	int ret;
+	char *subvol;
+	key_serial_t keyserial;
+	char key_tag[BTRFS_KEY_TAG_MAX_LEN + 1];
+	char key_algo[BTRFS_KEY_ALGO_MAX_LEN + 1];
+
+#if 0
+	if (check_argc_exact(argc - optind, 1))
+		usage(cmd_encrypt_login_usage);
+#endif
+
+	subvol = argv[argc - 1];
+
+	ret = btrfs_subvol_key_info(subvol, key_algo, key_tag, &keyserial);
+	if (ret) {
+		fprintf(stderr, "ERROR: %s\n", strerror(-ret));
+		return ret;
+	}
+
+	/*
+	 * Bit loosely coupled as of now, fixme
+	 * ask kernel to revoke, but user could use keyctl in the userspace
+	 * not too sure if using this
+	 *    down_write_nested(&btrfs_subvol_key->sem, 1)
+	 * in the kernel so that user spce can't revoke is a good idea.
+	 */
+	wait_for_commit_subvol(subvol);
+	keyctl(KEYCTL_REVOKE, keyserial);
+	return 0;
+}
+
diff --git a/encrypt.h b/encrypt.h
new file mode 100644
index 000000000000..070e02c77bf7
--- /dev/null
+++ b/encrypt.h
@@ -0,0 +1,33 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License v2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 021110-1307, USA.
+ */
+#include "props.h"
+
+#define	BTRFS_KEY_TAG_MAX_LEN		16
+#define	BTRFS_KEY_ALGO_MAX_LEN		16
+#define BTRFS_KEY_ALGO_TAG_MAX_LEN	(BTRFS_KEY_TAG_MAX_LEN + BTRFS_KEY_ALGO_MAX_LEN)
+
+void btrfs_create_keytag(char *keytag, char *subvol);
+void btrfs_create_encrypt_keytag_tuplet(char *keystr,
+			const char *encrypt_type, char *keytag);
+int btrfs_set_subvol_encrypt(char *subvol);
+int btrfs_get_subvol_encrypt(char *subvol, char *val_out);
+int prop_encrypt(enum prop_object_type type, const char *object,
+			const char *name, const char *value);
+int ask_key_for_keytag(char *keytag, key_serial_t *keyserial);
+int btrfs_subvol_key_info(char *subvol, char *key_algo, char *key_tag,
+						key_serial_t *key_serial);
+int cmd_encrypt_login(int argc, char **argv);
+int cmd_encrypt_logout(int argc, char **argv);
diff --git a/props.c b/props.c
index 5b7493240b09..2b6e81fbd541 100644
--- a/props.c
+++ b/props.c
@@ -20,11 +20,13 @@
 #include <sys/xattr.h>
 #include <fcntl.h>
 #include <unistd.h>
+#include <keyutils.h>
 
 #include "ctree.h"
 #include "commands.h"
 #include "utils.h"
 #include "props.h"
+#include "encrypt.h"
 
 #define XATTR_BTRFS_PREFIX     "btrfs."
 #define XATTR_BTRFS_PREFIX_LEN (sizeof(XATTR_BTRFS_PREFIX) - 1)
@@ -194,5 +196,6 @@ const struct prop_handler prop_handlers[] = {
 	 prop_object_dev | prop_object_root, prop_label},
 	{"compression", "Set/get compression for a file or directory", 0,
 	 prop_object_inode, prop_compression},
+	{"encrypt", "read encrypt property value", 0, prop_object_inode, prop_encrypt},
 	{NULL, NULL, 0, 0, NULL}
 };
diff --git a/subvolume.h b/subvolume.h
index ceacf603e01c..92ca7cd28912 100644
--- a/subvolume.h
+++ b/subvolume.h
@@ -18,3 +18,5 @@
 int btrfs_get_subvol_info(char *fullpath, struct root_info *get_ri);
 int test_issubvolume(const char *path);
 char *get_subvol_name(char *mnt, char *full_path);
+int wait_for_commit(int fd);
+int wait_for_commit_subvol(char *subvol);
-- 
2.7.0


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

* Re: [RFC] Experimental btrfs encryption
  2016-03-01 16:08 [RFC] Experimental btrfs encryption Anand Jain
                   ` (2 preceding siblings ...)
  2016-03-01 16:08 ` [RFC PATCH 2/2] btrfs-progs: Encryption: add encrypt sub cli Anand Jain
@ 2016-03-01 16:29 ` Tomasz Torcz
  2016-03-01 16:46   ` Chris Mason
  2016-03-01 16:41 ` Austin S. Hemmelgarn
                   ` (2 subsequent siblings)
  6 siblings, 1 reply; 26+ messages in thread
From: Tomasz Torcz @ 2016-03-01 16:29 UTC (permalink / raw)
  To: linux-btrfs

On Wed, Mar 02, 2016 at 12:08:09AM +0800, Anand Jain wrote:
> This patchset adds btrfs encryption support.
> 
> Warning:
> The code is in prototype/experimental stage and is not suitable
> for the production data yet.

  Can you share some design documents?  Will it be compatible
with existing encrypting filesystems:
 – ext4 ?
 – ZFS ?

  It would be nice to have common API for encryption, not a dozen
of filesystem-specific interfaces.

-- 
Tomasz Torcz                                                       72->|   80->|
xmpp: zdzichubg@chrome.pl                                          72->|   80->|


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

* Re: [RFC] Experimental btrfs encryption
  2016-03-01 16:08 [RFC] Experimental btrfs encryption Anand Jain
                   ` (3 preceding siblings ...)
  2016-03-01 16:29 ` [RFC] Experimental btrfs encryption Tomasz Torcz
@ 2016-03-01 16:41 ` Austin S. Hemmelgarn
  2016-03-02  1:44   ` Qu Wenruo
  2016-03-02  7:07   ` Anand Jain
  2016-03-02  1:06 ` Qu Wenruo
  2016-03-03  1:58 ` Anand Jain
  6 siblings, 2 replies; 26+ messages in thread
From: Austin S. Hemmelgarn @ 2016-03-01 16:41 UTC (permalink / raw)
  To: Anand Jain, linux-btrfs; +Cc: clm, dsterba

On 2016-03-01 11:08, Anand Jain wrote:
> This patchset adds btrfs encryption support.
While I think this is a great feature to have, I personally think we're 
better off waiting for the ext4/F2FS encryption API's to get pushed up 
to the VFS layer in mainline, and then use those for the user facing API 
(or at least support them).  That said, general comments below.
>
> Warning:
> The code is in prototype/experimental stage and is not suitable
> for the production data yet.
>
> Example usage:
> Create an encrypted subvolume:
>    btrfs subvol create -e /btrfs/sv1
>    Paraphrase: <-
Is there any way to do encryption per file instead of per-subvolume? 
This is a useful feature for a lot of people, and would provide better 
consistency with how most other filesystem-level encryption designs 
work.  That said, I do like the possibility of per-subvolume encryption, 
as it allows for things like per-user encrypted home directories without 
needing some extra layer like ecryptfs.
>
> Review encryption status
>    btrfs subvol show /btrfs/sv1
>    btrfs/sv1
>      Name: sv1
>      UID: d8bf1718-56a7-da40-86d9-b8e87315f63f
>      Parent UUID: -
>      Received UUID: -
>      Creation time: 2016-03-01 17:11:58 +0800
>      Subvolume ID: 257
>      Generation: 13
>      Gen at creation:7
>      Parent ID: 5
>      Top level ID: 5
>      Flags: -
>      Encryption: aes@btrfs:d8bf1718 (188612608)
>                     ^ ^^^^^^^^^^^^^^ ^^^^^^^^^
>                     |        |               |
>                  Algorithm Key-Tag Key-serial-number
>
>    keyctl show
>    ::
>    188612608 --alswrv 0 0 \_ user: btrfs:d8bf1718
>
> Logout/revoke:
>    btrfs subvol encrypt -k out /btrfs/sv1
>    btrfs subvol show /btrfs/sv1 | egrep Encrypt
>    Encryption: aes@btrfs:d8bf1718 (Required key not available)
>
> sign in:
>    btrfs subvol encrypt -k in /btrfs/sv1
>
> Known issues / limitation / for future expansion:
> - Need to set FS incompatible feature.
>
> - No password verification yet.
These two are absolutely essential from a data safety POV.
>
> - Move of files across subvolume is not supported when both
>    or either one has encryption set.
>
> - No way to change the password.
>
> - Does not drop the cached pages when key is revoked.
This is essential from a security POV.
>
> - Need to get password twice from the user.
>
> - No user permeable subvol info ioctl.
>
> - Provide a method to pass key using the mount option.
>
> - Provide a method to read the key from the file.
>
> - Current encryption method is symmetric (same key for both
>    encryption and decryption), however we could easily expand
>    this to other potentially useful methods like asymmetric
>    (private/public) encryption.
>
> - As of now uses "user" keytype, I am still considering/
>    evaluating other key type such as logon.
>
> - Evaluate other encryption algorithms,  as of now it is
>    using "cts(cbc(aes)".
While this would be nice (I would love to see full CryptoAPI support), 
it probably shouldn't be considered a deal breaker for merging this or 
declaring it production ready.
>
> - Uses btrfs compression framework, so compression and then
>    encryption is not possible. However yet evaluate if there
>    are encryption algorithm which can compress as well.
The utility of this is debatable.  Most other filesystems that support 
both compression and encryption support only one or the other for a 
given file, encrypting compressed data is overall less secure than 
encrypting the data itself, and trying to compress encrypted data 
usually just wastes CPU time.

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

* Re: [RFC] Experimental btrfs encryption
  2016-03-01 16:29 ` [RFC] Experimental btrfs encryption Tomasz Torcz
@ 2016-03-01 16:46   ` Chris Mason
  2016-03-01 17:56     ` Austin S. Hemmelgarn
  2016-03-01 17:59     ` Christoph Hellwig
  0 siblings, 2 replies; 26+ messages in thread
From: Chris Mason @ 2016-03-01 16:46 UTC (permalink / raw)
  To: Tomasz Torcz, linux-btrfs

On Tue, Mar 01, 2016 at 05:29:52PM +0100, Tomasz Torcz wrote:
> On Wed, Mar 02, 2016 at 12:08:09AM +0800, Anand Jain wrote:
> > This patchset adds btrfs encryption support.
> > 
> > Warning:
> > The code is in prototype/experimental stage and is not suitable
> > for the production data yet.
> 
>   Can you share some design documents?  Will it be compatible
> with existing encrypting filesystems:
>  – ext4 ?
>  – ZFS ?
> 
>   It would be nice to have common API for encryption, not a dozen
> of filesystem-specific interfaces.

We'll definitely move in line with the common API over time.  Thanks
Anand for starting this!

I'd prefer that we keep it per-subvolume for now, just because
subvolumes are so cheap and because it seems like a better collection
point for general use.  But as the other filesystems add features we'll
make sure and keep parity with what users expect.

-chris


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

* Re: [RFC] Experimental btrfs encryption
  2016-03-01 16:46   ` Chris Mason
@ 2016-03-01 17:56     ` Austin S. Hemmelgarn
  2016-03-01 17:59     ` Christoph Hellwig
  1 sibling, 0 replies; 26+ messages in thread
From: Austin S. Hemmelgarn @ 2016-03-01 17:56 UTC (permalink / raw)
  To: Chris Mason, Tomasz Torcz, linux-btrfs

On 2016-03-01 11:46, Chris Mason wrote:
> On Tue, Mar 01, 2016 at 05:29:52PM +0100, Tomasz Torcz wrote:
>> On Wed, Mar 02, 2016 at 12:08:09AM +0800, Anand Jain wrote:
>>> This patchset adds btrfs encryption support.
>>>
>>> Warning:
>>> The code is in prototype/experimental stage and is not suitable
>>> for the production data yet.
>>
>>    Can you share some design documents?  Will it be compatible
>> with existing encrypting filesystems:
>>   – ext4 ?
>>   – ZFS ?
>>
>>    It would be nice to have common API for encryption, not a dozen
>> of filesystem-specific interfaces.
>
> We'll definitely move in line with the common API over time.  Thanks
> Anand for starting this!
>
> I'd prefer that we keep it per-subvolume for now, just because
> subvolumes are so cheap and because it seems like a better collection
> point for general use.  But as the other filesystems add features we'll
> make sure and keep parity with what users expect.
I hate to tell you, but if you want feature parity, it needs to have \ 
per-file functionality from the start.  Both ext4 and F2FS do per-file, 
as does NTFS (not certain about ZFS, but people are not as likely to be 
coming to BTRFS from ZFS as they are from ext4 or F2FS from what I've seen).


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

* Re: [RFC] Experimental btrfs encryption
  2016-03-01 16:46   ` Chris Mason
  2016-03-01 17:56     ` Austin S. Hemmelgarn
@ 2016-03-01 17:59     ` Christoph Hellwig
  2016-03-01 18:23       ` Chris Mason
  1 sibling, 1 reply; 26+ messages in thread
From: Christoph Hellwig @ 2016-03-01 17:59 UTC (permalink / raw)
  To: Chris Mason, Tomasz Torcz, linux-btrfs

On Tue, Mar 01, 2016 at 11:46:16AM -0500, Chris Mason wrote:
> We'll definitely move in line with the common API over time.  Thanks
> Anand for starting this!
> 
> I'd prefer that we keep it per-subvolume for now, just because
> subvolumes are so cheap and because it seems like a better collection
> point for general use.  But as the other filesystems add features we'll
> make sure and keep parity with what users expect.

We already have per-file encryption in f2fs and ext4, and both have
a compatible userspace API and ABI.  It would be a pitty to deviate
from that intead of reusing it, and if needed extending it.

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

* Re: [RFC] Experimental btrfs encryption
  2016-03-01 17:59     ` Christoph Hellwig
@ 2016-03-01 18:23       ` Chris Mason
  2016-03-02  4:48         ` Anand Jain
  0 siblings, 1 reply; 26+ messages in thread
From: Chris Mason @ 2016-03-01 18:23 UTC (permalink / raw)
  To: Christoph Hellwig; +Cc: Tomasz Torcz, linux-btrfs

On Tue, Mar 01, 2016 at 09:59:27AM -0800, Christoph Hellwig wrote:
> On Tue, Mar 01, 2016 at 11:46:16AM -0500, Chris Mason wrote:
> > We'll definitely move in line with the common API over time.  Thanks
> > Anand for starting this!
> > 
> > I'd prefer that we keep it per-subvolume for now, just because
> > subvolumes are so cheap and because it seems like a better collection
> > point for general use.  But as the other filesystems add features we'll
> > make sure and keep parity with what users expect.
> 
> We already have per-file encryption in f2fs and ext4, and both have
> a compatible userspace API and ABI.  It would be a pitty to deviate
> from that intead of reusing it, and if needed extending it.

I wasn't very clear here sorry.  per-subvolume is my favorite way, but
we'll go with the existing ABIs as well.  There's no reason to be
different.

-chris


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

* Re: [RFC] Experimental btrfs encryption
  2016-03-01 16:08 [RFC] Experimental btrfs encryption Anand Jain
                   ` (4 preceding siblings ...)
  2016-03-01 16:41 ` Austin S. Hemmelgarn
@ 2016-03-02  1:06 ` Qu Wenruo
  2016-03-02  9:09   ` Anand Jain
                     ` (2 more replies)
  2016-03-03  1:58 ` Anand Jain
  6 siblings, 3 replies; 26+ messages in thread
From: Qu Wenruo @ 2016-03-02  1:06 UTC (permalink / raw)
  To: Anand Jain, linux-btrfs; +Cc: clm, dsterba



Anand Jain wrote on 2016/03/02 00:08 +0800:
> This patchset adds btrfs encryption support.
>
> Warning:
> The code is in prototype/experimental stage and is not suitable
> for the production data yet.
>
> Example usage:
> Create an encrypted subvolume:
>    btrfs subvol create -e /btrfs/sv1
>    Paraphrase: <-
>
> Review encryption status
>    btrfs subvol show /btrfs/sv1
>    btrfs/sv1
>      Name: sv1
>      UID: d8bf1718-56a7-da40-86d9-b8e87315f63f
>      Parent UUID: -
>      Received UUID: -
>      Creation time: 2016-03-01 17:11:58 +0800
>      Subvolume ID: 257
>      Generation: 13
>      Gen at creation:7
>      Parent ID: 5
>      Top level ID: 5
>      Flags: -
>      Encryption: aes@btrfs:d8bf1718 (188612608)
>                     ^ ^^^^^^^^^^^^^^ ^^^^^^^^^
>                     |        |               |
>                  Algorithm Key-Tag Key-serial-number
>
>    keyctl show
>    ::
>    188612608 --alswrv 0 0 \_ user: btrfs:d8bf1718
>
> Logout/revoke:
>    btrfs subvol encrypt -k out /btrfs/sv1
>    btrfs subvol show /btrfs/sv1 | egrep Encrypt
>    Encryption: aes@btrfs:d8bf1718 (Required key not available)
>
> sign in:
>    btrfs subvol encrypt -k in /btrfs/sv1
>
> Known issues / limitation / for future expansion:
> - Need to set FS incompatible feature.

Not a limitation at all.

>
> - No password verification yet.
>
> - Move of files across subvolume is not supported when both
>    or either one has encryption set.

Not only move, but also reflink/inband dedup.

>
> - No way to change the password.
>
> - Does not drop the cached pages when key is revoked.
>
> - Need to get password twice from the user.
>
> - No user permeable subvol info ioctl.
>
> - Provide a method to pass key using the mount option.
>
> - Provide a method to read the key from the file.
>
> - Current encryption method is symmetric (same key for both
>    encryption and decryption), however we could easily expand
>    this to other potentially useful methods like asymmetric
>    (private/public) encryption.
>
> - As of now uses "user" keytype, I am still considering/
>    evaluating other key type such as logon.

UI things can always be reconsidered later.
Never a big problem.

>
> - Evaluate other encryption algorithms,  as of now it is
>    using "cts(cbc(aes)".
>
> - Uses btrfs compression framework, so compression and then
>    encryption is not possible. However yet evaluate if there
>    are encryption algorithm which can compress as well.

Yes, but in fact, you can use another method, just like in-band de-dup, 
by adding new hook into async_cow_start() and async_cow_end(), allowing 
compression and encryption can be done at the same time.
(We are already testing the patch to allow dedup to cooperate with 
compression)

So no need to find a encryption with can compress.
(Never mix 2 different work together)


And maybe I just missed something, but the filename seems not touched, 
meaning it will leak a lot of information.
Just like default eCryptfs behavior.

I understand that's an easy design and it's not a high priority thing, 
but I hope we can encrypt the subvolume tree blocks too, if using 
per-subvolume policy.
To provide a feature near block-level encryption.

Thanks,
Qu
>
>
> Anand Jain (1):
>    btrfs: encryption
>
>   fs/btrfs/Makefile      |   2 +-
>   fs/btrfs/btrfs_inode.h |   2 +
>   fs/btrfs/compression.c |  53 ++++-
>   fs/btrfs/compression.h |   1 +
>   fs/btrfs/ctree.h       |  11 +-
>   fs/btrfs/encrypt.c     | 544 +++++++++++++++++++++++++++++++++++++++++++++++++
>   fs/btrfs/encrypt.h     |  21 ++
>   fs/btrfs/inode.c       |  37 +++-
>   fs/btrfs/ioctl.c       |   7 +
>   fs/btrfs/props.c       | 140 ++++++++++++-
>   fs/btrfs/super.c       |   5 +-
>   11 files changed, 812 insertions(+), 11 deletions(-)
>   create mode 100644 fs/btrfs/encrypt.c
>   create mode 100644 fs/btrfs/encrypt.h
>
> Anand Jain (2):
>    btrfs-progs: subvolume functions reorg
>    btrfs-progs: add encrypt as subvol sub-command
>
>   Makefile.in      |   5 +-
>   btrfs-list.c     |  33 +++++
>   cmds-qgroup.c    |   1 +
>   cmds-send.c      |  12 +-
>   cmds-subvolume.c | 209 +++++++++++++++--------------
>   commands.h       |   1 +
>   encrypt.c        | 397 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
>   encrypt.h        |  33 +++++
>   props.c          |   3 +
>   subvolume.c      | 152 +++++++++++++++++++++
>   subvolume.h      |  22 +++
>   11 files changed, 757 insertions(+), 111 deletions(-)
>   create mode 100644 encrypt.c
>   create mode 100644 encrypt.h
>   create mode 100644 subvolume.c
>   create mode 100644 subvolume.h
>



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

* Re: [RFC] Experimental btrfs encryption
  2016-03-01 16:41 ` Austin S. Hemmelgarn
@ 2016-03-02  1:44   ` Qu Wenruo
  2016-03-02  8:50     ` Anand Jain
  2016-03-02  7:07   ` Anand Jain
  1 sibling, 1 reply; 26+ messages in thread
From: Qu Wenruo @ 2016-03-02  1:44 UTC (permalink / raw)
  To: Austin S. Hemmelgarn, Anand Jain, linux-btrfs; +Cc: clm, dsterba



Austin S. Hemmelgarn wrote on 2016/03/01 11:41 -0500:
> On 2016-03-01 11:08, Anand Jain wrote:
>> This patchset adds btrfs encryption support.
> While I think this is a great feature to have, I personally think we're
> better off waiting for the ext4/F2FS encryption API's to get pushed up
> to the VFS layer in mainline, and then use those for the user facing API
> (or at least support them).  That said, general comments below.
>>
>> Warning:
>> The code is in prototype/experimental stage and is not suitable
>> for the production data yet.
>>
>> Example usage:
>> Create an encrypted subvolume:
>>    btrfs subvol create -e /btrfs/sv1
>>    Paraphrase: <-
> Is there any way to do encryption per file instead of per-subvolume?
> This is a useful feature for a lot of people, and would provide better
> consistency with how most other filesystem-level encryption designs
> work.  That said, I do like the possibility of per-subvolume encryption,
> as it allows for things like per-user encrypted home directories without
> needing some extra layer like ecryptfs.
>>
>> Review encryption status
>>    btrfs subvol show /btrfs/sv1
>>    btrfs/sv1
>>      Name: sv1
>>      UID: d8bf1718-56a7-da40-86d9-b8e87315f63f
>>      Parent UUID: -
>>      Received UUID: -
>>      Creation time: 2016-03-01 17:11:58 +0800
>>      Subvolume ID: 257
>>      Generation: 13
>>      Gen at creation:7
>>      Parent ID: 5
>>      Top level ID: 5
>>      Flags: -
>>      Encryption: aes@btrfs:d8bf1718 (188612608)
>>                     ^ ^^^^^^^^^^^^^^ ^^^^^^^^^
>>                     |        |               |
>>                  Algorithm Key-Tag Key-serial-number
>>
>>    keyctl show
>>    ::
>>    188612608 --alswrv 0 0 \_ user: btrfs:d8bf1718
>>
>> Logout/revoke:
>>    btrfs subvol encrypt -k out /btrfs/sv1
>>    btrfs subvol show /btrfs/sv1 | egrep Encrypt
>>    Encryption: aes@btrfs:d8bf1718 (Required key not available)
>>
>> sign in:
>>    btrfs subvol encrypt -k in /btrfs/sv1
>>
>> Known issues / limitation / for future expansion:
>> - Need to set FS incompatible feature.
>>
>> - No password verification yet.
> These two are absolutely essential from a data safety POV.
>>
>> - Move of files across subvolume is not supported when both
>>    or either one has encryption set.
>>
>> - No way to change the password.
>>
>> - Does not drop the cached pages when key is revoked.
> This is essential from a security POV.
>>
>> - Need to get password twice from the user.
>>
>> - No user permeable subvol info ioctl.
>>
>> - Provide a method to pass key using the mount option.
>>
>> - Provide a method to read the key from the file.
>>
>> - Current encryption method is symmetric (same key for both
>>    encryption and decryption), however we could easily expand
>>    this to other potentially useful methods like asymmetric
>>    (private/public) encryption.
>>
>> - As of now uses "user" keytype, I am still considering/
>>    evaluating other key type such as logon.
>>
>> - Evaluate other encryption algorithms,  as of now it is
>>    using "cts(cbc(aes)".
> While this would be nice (I would love to see full CryptoAPI support),
> it probably shouldn't be considered a deal breaker for merging this or
> declaring it production ready.
>>
>> - Uses btrfs compression framework, so compression and then
>>    encryption is not possible. However yet evaluate if there
>>    are encryption algorithm which can compress as well.
> The utility of this is debatable.  Most other filesystems that support
> both compression and encryption support only one or the other for a
> given file, encrypting compressed data is overall less secure than
> encrypting the data itself, and trying to compress encrypted data
> usually just wastes CPU time.

+1 here, but in fact, it's easy to deal with.
As long as not implement encryption as a compression method.

Just like inband dedup, we use the following method to support dedup and 
compression while still using most of CPU cores like compression:

Old compression only implement:
inode needs compress will go into async_cow_start()
async_cow_start()
   |- compress_file_range()

Compression with dedup implement:
inode needs compress *OR* dedup will go into async_cow_start()
async_cow_start()
   |
   |- if (!inode_need_dedup())
   |  |- compress_file_range()  <<Just as normal one
   |     |- btrfs_compress_pages()
   |     |- add_async_extent()
   |
   |- else
      |- hash_file_range()      <<Calculate file hashes
         |- normal dedup hash
         |- if (inode_need_compress())
         |  |- btrfs_compress_pages()
         |- add_async_extent()

Although not the most elegant method, but it shows that we can 
co-operate compress and encrypt.


However the most elegant method, is to rework current cow_file_range() 
and its variant, to an unified btrfs internal API.

Thanks,
Qu

> --
> To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>
>



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

* Re: [RFC] Experimental btrfs encryption
  2016-03-01 18:23       ` Chris Mason
@ 2016-03-02  4:48         ` Anand Jain
  2016-03-04 12:30           ` Austin S. Hemmelgarn
  0 siblings, 1 reply; 26+ messages in thread
From: Anand Jain @ 2016-03-02  4:48 UTC (permalink / raw)
  To: Chris Mason, Christoph Hellwig, Tomasz Torcz, linux-btrfs




On 03/02/2016 02:23 AM, Chris Mason wrote:
> On Tue, Mar 01, 2016 at 09:59:27AM -0800, Christoph Hellwig wrote:
>> On Tue, Mar 01, 2016 at 11:46:16AM -0500, Chris Mason wrote:
>>> We'll definitely move in line with the common API over time.  Thanks
>>> Anand for starting this!
>>>
>>> I'd prefer that we keep it per-subvolume for now, just because
>>> subvolumes are so cheap and because it seems like a better collection
>>> point for general use.  But as the other filesystems add features we'll
>>> make sure and keep parity with what users expect.
>>
>> We already have per-file encryption in f2fs and ext4, and both have
>> a compatible userspace API and ABI.  It would be a pitty to deviate
>> from that intead of reusing it, and if needed extending it.
>
> I wasn't very clear here sorry.  per-subvolume is my favorite way, but
> we'll go with the existing ABIs as well.  There's no reason to be
> different.


Thanks for commenting.

btrfs encryption creates attributes per file its named

   btrfs.encrypt

But when we have a standard attribute for file encryption
this should be updated.

I wrote a design approaches/principles on which btrfs encryption
is based on.. and it here

https://docs.google.com/document/d/1fq9snDM_4ikn44UDNErjHqKXgZHukiJWS4Il3qVhm3M/edit?usp=sharing

for your kind review.


Thanks! Anand


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

* Re: [RFC] Experimental btrfs encryption
  2016-03-01 16:41 ` Austin S. Hemmelgarn
  2016-03-02  1:44   ` Qu Wenruo
@ 2016-03-02  7:07   ` Anand Jain
  1 sibling, 0 replies; 26+ messages in thread
From: Anand Jain @ 2016-03-02  7:07 UTC (permalink / raw)
  To: Austin S. Hemmelgarn, linux-btrfs; +Cc: clm, dsterba



Thanks Austin for commenting.

  Yes to most of it. And probably I should have titled known-issues
  to known-bugs, which I meant to fix before final integration.


In general:
Its good to explore options of both compression+encryption OR an
algorithm engine which can automatically do both because whether
data centers will look for it or not is a balance trade off between

      CPU load VS Storage space saved VS Network bandwidth needed

  Yes, network as well because of the remote replication and remote
  backup data center use cases.

  So its good to keep that option open.

Thanks, Anand


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

* Re: [RFC] Experimental btrfs encryption
  2016-03-02  1:44   ` Qu Wenruo
@ 2016-03-02  8:50     ` Anand Jain
  2016-03-03  1:12       ` Qu Wenruo
  0 siblings, 1 reply; 26+ messages in thread
From: Anand Jain @ 2016-03-02  8:50 UTC (permalink / raw)
  To: Qu Wenruo, Austin S. Hemmelgarn, linux-btrfs; +Cc: clm, dsterba


Thanks! for commenting Qu.

  As you are working near these codes appreciate any
  code review comments.

> +1 here, but in fact, it's easy to deal with.
> As long as not implement encryption as a compression method.
>
> Just like inband dedup, we use the following method to support dedup and
> compression while still using most of CPU cores like compression:
>
> Old compression only implement:
> inode needs compress will go into async_cow_start()
> async_cow_start()
>    |- compress_file_range()
>
> Compression with dedup implement:
> inode needs compress *OR* dedup will go into async_cow_start()
> async_cow_start()
>    |
>    |- if (!inode_need_dedup())
>    |  |- compress_file_range()  <<Just as normal one
>    |     |- btrfs_compress_pages()
>    |     |- add_async_extent()
>    |
>    |- else
>       |- hash_file_range()      <<Calculate file hashes
>          |- normal dedup hash
>          |- if (inode_need_compress())
>          |  |- btrfs_compress_pages()
>          |- add_async_extent()
>
> Although not the most elegant method, but it shows that we can
> co-operate compress and encrypt.
>
> However the most elegant method, is to rework current cow_file_range()
> and its variant, to an unified btrfs internal API.

  Thanks for this. Right. Currently there is no elegant way of doing it.
  Tried a bit of juggles. Unless rework.

  But I am confused. Are you suggesting we should cascade compress engine
  and then an encryption engine ? Austin said against such an approach.
  And I have included it under limitation section just to mention, and
  what's the idea if at all someone seeks such a configurations, which
  is to use engine which provides both instead.

Thanks, Anand

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

* Re: [RFC] Experimental btrfs encryption
  2016-03-02  1:06 ` Qu Wenruo
@ 2016-03-02  9:09   ` Anand Jain
  2016-03-03  1:26     ` Qu Wenruo
  2016-03-03 10:17   ` Alex Elsayed
  2016-03-20 11:56   ` Martin Steigerwald
  2 siblings, 1 reply; 26+ messages in thread
From: Anand Jain @ 2016-03-02  9:09 UTC (permalink / raw)
  To: Qu Wenruo, linux-btrfs; +Cc: clm, dsterba


Hi Qu,

> Not only move, but also reflink/inband dedup.

  oh yes thanks. I shall add those.

> Yes, but in fact, you can use another method, just like in-band de-dup,
> by adding new hook into async_cow_start() and async_cow_end(), allowing
> compression and encryption can be done at the same time.
> (We are already testing the patch to allow dedup to cooperate with
> compression)
>
> So no need to find a encryption with can compress.
> (Never mix 2 different work together)

  I am not too sure about this. But logically if one encoding engine
  can do both that seems to be better than using two separate encoding
  engines.

> And maybe I just missed something, but the filename seems not touched,
> meaning it will leak a lot of information.
> Just like default eCryptfs behavior.
 >
> I understand that's an easy design and it's not a high priority thing,
> but I hope we can encrypt the subvolume tree blocks too, if using
> per-subvolume policy.
> To provide a feature near block-level encryption.

  No you didn't miss about filename, its not there yet. Will add more
  depth, as I obtain feedback/confirmed on the approach concerns if any.

Thanks, Anand

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

* Re: [RFC] Experimental btrfs encryption
  2016-03-02  8:50     ` Anand Jain
@ 2016-03-03  1:12       ` Qu Wenruo
  0 siblings, 0 replies; 26+ messages in thread
From: Qu Wenruo @ 2016-03-03  1:12 UTC (permalink / raw)
  To: Anand Jain, Austin S. Hemmelgarn, linux-btrfs; +Cc: clm, dsterba



Anand Jain wrote on 2016/03/02 16:50 +0800:
>
> Thanks! for commenting Qu.
>
>   As you are working near these codes appreciate any
>   code review comments.
>
>> +1 here, but in fact, it's easy to deal with.
>> As long as not implement encryption as a compression method.
>>
>> Just like inband dedup, we use the following method to support dedup and
>> compression while still using most of CPU cores like compression:
>>
>> Old compression only implement:
>> inode needs compress will go into async_cow_start()
>> async_cow_start()
>>    |- compress_file_range()
>>
>> Compression with dedup implement:
>> inode needs compress *OR* dedup will go into async_cow_start()
>> async_cow_start()
>>    |
>>    |- if (!inode_need_dedup())
>>    |  |- compress_file_range()  <<Just as normal one
>>    |     |- btrfs_compress_pages()
>>    |     |- add_async_extent()
>>    |
>>    |- else
>>       |- hash_file_range()      <<Calculate file hashes
>>          |- normal dedup hash
>>          |- if (inode_need_compress())
>>          |  |- btrfs_compress_pages()
>>          |- add_async_extent()
>>
>> Although not the most elegant method, but it shows that we can
>> co-operate compress and encrypt.
>>
>> However the most elegant method, is to rework current cow_file_range()
>> and its variant, to an unified btrfs internal API.
>
>   Thanks for this. Right. Currently there is no elegant way of doing it.
>   Tried a bit of juggles. Unless rework.
>
>   But I am confused. Are you suggesting we should cascade compress engine
>   and then an encryption engine ?

Just a suggestion.
As personally, I'd like to separate compression and encryption work.
Although most of encryption is just like compression, it's still a 
little differnet, especially for the need of encryption/decryption key 
unlike compression.

And in fact, compression then encrypt is quite common in this days, a 
lot of gzipped data is transmitted by TLS, so the need may exist though.

Although it's also acceptable not to support compression with 
encryption, just for simple implementation in kernel.

>   Austin said against such an approach.
>   And I have included it under limitation section just to mention, and
>   what's the idea if at all someone seeks such a configurations, which
>   is to use engine which provides both instead.

Another reason I mention this idea, is we're considering rework the 
current run_delalloc_range(), to provide a unified, re-entry friendly, 
and independent framework for the growing need of different data 
processing method.
Including normal cow,no data cow, inline, compress, dedup, and hopefully 
encryption.

But current one is good enough for the RFC usage.

Thanks,
Qu
>
> Thanks, Anand
>
>



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

* Re: [RFC] Experimental btrfs encryption
  2016-03-02  9:09   ` Anand Jain
@ 2016-03-03  1:26     ` Qu Wenruo
  0 siblings, 0 replies; 26+ messages in thread
From: Qu Wenruo @ 2016-03-03  1:26 UTC (permalink / raw)
  To: Anand Jain, linux-btrfs; +Cc: clm, dsterba



Anand Jain wrote on 2016/03/02 17:09 +0800:
>
> Hi Qu,
>
>> Not only move, but also reflink/inband dedup.
>
>   oh yes thanks. I shall add those.
>
>> Yes, but in fact, you can use another method, just like in-band de-dup,
>> by adding new hook into async_cow_start() and async_cow_end(), allowing
>> compression and encryption can be done at the same time.
>> (We are already testing the patch to allow dedup to cooperate with
>> compression)
>>
>> So no need to find a encryption with can compress.
>> (Never mix 2 different work together)
>
>   I am not too sure about this. But logically if one encoding engine
>   can do both that seems to be better than using two separate encoding
>   engines.

That's right, if can be done in one iteration, that's best.

Maybe I'm ignorant about encryption, but it seems the design goal of 
current block encryption is safety (confusion and diffusion), and 
normally same plaintext and ciphertext size.
So it may be a little difficult to find such encryption/compression 
algorithm.
And even found, we may need to implement it in kernel, if we're the only 
user, Linus may not be happy with that though.

Another concern is, if using that method, encryption and compression 
must be bond together, make things a little unflex, especially for 
subvolume level encryption.

But that's all my assumption.
Maybe current encryption and compression conflicts method is the best, 
as it's so easy for user to compression a file in user space.

>
>> And maybe I just missed something, but the filename seems not touched,
>> meaning it will leak a lot of information.
>> Just like default eCryptfs behavior.
>  >
>> I understand that's an easy design and it's not a high priority thing,
>> but I hope we can encrypt the subvolume tree blocks too, if using
>> per-subvolume policy.
>> To provide a feature near block-level encryption.
>
>   No you didn't miss about filename, its not there yet. Will add more
>   depth, as I obtain feedback/confirmed on the approach concerns if any.

OK, I'll just forgot this corner and focus on current implement.

Good job on bringing encryption to the view of most developers.

Thanks,
Qu

>
> Thanks, Anand
>
>



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

* Re: [RFC] Experimental btrfs encryption
  2016-03-01 16:08 [RFC] Experimental btrfs encryption Anand Jain
                   ` (5 preceding siblings ...)
  2016-03-02  1:06 ` Qu Wenruo
@ 2016-03-03  1:58 ` Anand Jain
  2016-03-22 14:25   ` David Sterba
  6 siblings, 1 reply; 26+ messages in thread
From: Anand Jain @ 2016-03-03  1:58 UTC (permalink / raw)
  To: linux-btrfs; +Cc: clm, dsterba




. (I received couple of private emails on this, so looks like
  I confused you and I'm writing again to clear the air on this).

> - Uses btrfs compression framework, so compression and then
>    encryption is not possible. However yet evaluate if there
>    are encryption algorithm which can compress as well.

  It should be compression and then encryption. I didn't mean to say
  the other way around. However the btrfs encoding framework is
  designed to handle any one of it in an elegant manner. So as of
  user can configure either encryption OR compression by design.

  Further for users who are looking for both compression and encryption.
  There are two ways that we could implement in future in the btrfs.
  One enhance the btrfs encoding framework so that it can accommodate
  two cascaded engines like compression and followed by encryption.
  Or (mostly) a better approach would be to evaluate a single encoding
  engine (algorithm) which can do both (compression and then encryption).
  Which I think will be less invasive within btrfs, and probably be more
  efficient.

  Hope I sound clearer now. Sorry if I wasn't before.



. I have put up a doc here:

https://docs.google.com/document/d/1fq9snDM_4ikn44UDNErjHqKXgZHukiJWS4Il3qVhm3M/edit?usp=sharing

For your kind review and suggestions.



Thanks, Anand

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

* Re: [RFC] Experimental btrfs encryption
  2016-03-02  1:06 ` Qu Wenruo
  2016-03-02  9:09   ` Anand Jain
@ 2016-03-03 10:17   ` Alex Elsayed
  2016-03-04  2:52     ` Anand Jain
  2016-03-20 11:56   ` Martin Steigerwald
  2 siblings, 1 reply; 26+ messages in thread
From: Alex Elsayed @ 2016-03-03 10:17 UTC (permalink / raw)
  To: linux-btrfs

Qu Wenruo <quwenruo <at> cn.fujitsu.com> writes:
<snip>
> > - As of now uses "user" keytype, I am still considering/
> >    evaluating other key type such as logon.
> 
> UI things can always be reconsidered later.
> Never a big problem.

This is not only a UI concern, but an API/ABI concern.

To use eCryptFS as an example, on mount(2) you pass the information for it
to look up the key in the kernel keyring, and it looks for a key of type
"user" - I have personally written code that uses trusted and encrypted
keys, and the raw mount(2) call (sans any of the eCryptFS userspace
libraries) to mount eCryptFS filesystems sealed to the TPM.

If eCryptFS switched to using another key type, my code would cease to work
unless the filesystem jumped through hoops to do so in a
backwards-compatible way. Similarly, while eCryptFS uses a "user" key, it
requires that key have a specific structure - as a result, the encrypted
keys support added a key type of "ecryptfs" to create random keys with the
appropriate structure, meaning the key type for unencrypted keys and the
encrypted key key type field differ. This is surprising and non-obvious, and
took some time to figure out in my implementation.

The way btrfs encryption interacts with the keyring APIs is important, and
"reconsidering later" will potentially represent an API/ABI break. Please
keep it in mind.


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

* Re: [RFC] Experimental btrfs encryption
  2016-03-03 10:17   ` Alex Elsayed
@ 2016-03-04  2:52     ` Anand Jain
  0 siblings, 0 replies; 26+ messages in thread
From: Anand Jain @ 2016-03-04  2:52 UTC (permalink / raw)
  To: Alex Elsayed, linux-btrfs



> The way btrfs encryption interacts with the keyring APIs is important, and
> "reconsidering later" will potentially represent an API/ABI break. Please
> keep it in mind.

  Yep. We would take considerable time to get the API frozen and
  integrated, as once its in, its there forever. So there are
  warnings as experimental / RFC.

Thanks, Anand

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

* Re: [RFC] Experimental btrfs encryption
  2016-03-02  4:48         ` Anand Jain
@ 2016-03-04 12:30           ` Austin S. Hemmelgarn
  0 siblings, 0 replies; 26+ messages in thread
From: Austin S. Hemmelgarn @ 2016-03-04 12:30 UTC (permalink / raw)
  To: Anand Jain, Chris Mason, Christoph Hellwig, Tomasz Torcz, linux-btrfs

On 2016-03-01 23:48, Anand Jain wrote:
>
>
>
> On 03/02/2016 02:23 AM, Chris Mason wrote:
>> On Tue, Mar 01, 2016 at 09:59:27AM -0800, Christoph Hellwig wrote:
>>> On Tue, Mar 01, 2016 at 11:46:16AM -0500, Chris Mason wrote:
>>>> We'll definitely move in line with the common API over time.  Thanks
>>>> Anand for starting this!
>>>>
>>>> I'd prefer that we keep it per-subvolume for now, just because
>>>> subvolumes are so cheap and because it seems like a better collection
>>>> point for general use.  But as the other filesystems add features we'll
>>>> make sure and keep parity with what users expect.
>>>
>>> We already have per-file encryption in f2fs and ext4, and both have
>>> a compatible userspace API and ABI.  It would be a pitty to deviate
>>> from that intead of reusing it, and if needed extending it.
>>
>> I wasn't very clear here sorry.  per-subvolume is my favorite way, but
>> we'll go with the existing ABIs as well.  There's no reason to be
>> different.
>
>
> Thanks for commenting.
>
> btrfs encryption creates attributes per file its named
>
>    btrfs.encrypt
>
> But when we have a standard attribute for file encryption
> this should be updated.
>
> I wrote a design approaches/principles on which btrfs encryption
> is based on.. and it here
>
> https://docs.google.com/document/d/1fq9snDM_4ikn44UDNErjHqKXgZHukiJWS4Il3qVhm3M/edit?usp=sharing

FWIW, a new version of the patch series pushing the ext4/F2FS API up to 
the VFS layer got posted on LKML just the other day.  Based on the level 
of review, it looks like it may end up in 4.6.


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

* Re: [RFC PATCH 1/1] btrfs: Encryption: Add btrfs encryption support
  2016-03-01 16:08 ` [RFC PATCH 1/1] btrfs: Encryption: Add btrfs encryption support Anand Jain
@ 2016-03-10  2:19   ` Liu Bo
  2016-05-06  9:21     ` Anand Jain
  0 siblings, 1 reply; 26+ messages in thread
From: Liu Bo @ 2016-03-10  2:19 UTC (permalink / raw)
  To: Anand Jain; +Cc: linux-btrfs, clm, dsterba

On Wed, Mar 02, 2016 at 12:08:10AM +0800, Anand Jain wrote:
> ***
> *** Warning: Experimental code.
> ***
> 
> Adds encryption support. The branch is based on v4.5-rc6.
> 
> Signed-off-by: Anand Jain <anand.jain@oracle.com>
> ---
>  fs/btrfs/Makefile      |   2 +-
>  fs/btrfs/btrfs_inode.h |   2 +
>  fs/btrfs/compression.c |  53 ++++-
>  fs/btrfs/compression.h |   1 +
>  fs/btrfs/ctree.h       |  11 +-
>  fs/btrfs/encrypt.c     | 544 +++++++++++++++++++++++++++++++++++++++++++++++++
>  fs/btrfs/encrypt.h     |  21 ++
>  fs/btrfs/inode.c       |  37 +++-
>  fs/btrfs/ioctl.c       |   7 +
>  fs/btrfs/props.c       | 140 ++++++++++++-
>  fs/btrfs/super.c       |   5 +-
>  11 files changed, 812 insertions(+), 11 deletions(-)
>  create mode 100644 fs/btrfs/encrypt.c
>  create mode 100644 fs/btrfs/encrypt.h
> 
> diff --git a/fs/btrfs/Makefile b/fs/btrfs/Makefile
> index 128ce17a80b0..2765778c5898 100644
> --- a/fs/btrfs/Makefile
> +++ b/fs/btrfs/Makefile
> @@ -9,7 +9,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 \
>  	   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 hash.o free-space-tree.o
> +	   uuid-tree.o props.o hash.o free-space-tree.o encrypt.o
>  
>  btrfs-$(CONFIG_BTRFS_FS_POSIX_ACL) += acl.o
>  btrfs-$(CONFIG_BTRFS_FS_CHECK_INTEGRITY) += check-integrity.o
> diff --git a/fs/btrfs/btrfs_inode.h b/fs/btrfs/btrfs_inode.h
> index 61205e3bbefa..4f09572b4922 100644
> --- a/fs/btrfs/btrfs_inode.h
> +++ b/fs/btrfs/btrfs_inode.h
> @@ -197,6 +197,8 @@ struct btrfs_inode {
>  	long delayed_iput_count;
>  
>  	struct inode vfs_inode;
> +
> +	unsigned char key_payload[16];
>  };
>  
>  extern unsigned char btrfs_filetype_table[];
> diff --git a/fs/btrfs/compression.c b/fs/btrfs/compression.c
> index 3346cd8f9910..d59c366f4200 100644
> --- a/fs/btrfs/compression.c
> +++ b/fs/btrfs/compression.c
> @@ -41,6 +41,7 @@
>  #include "compression.h"
>  #include "extent_io.h"
>  #include "extent_map.h"
> +#include "encrypt.h"
>  
>  struct compressed_bio {
>  	/* number of bios pending for this compressed extent */
> @@ -182,7 +183,7 @@ static void end_compressed_bio_read(struct bio *bio)
>  				      cb->orig_bio->bi_vcnt,
>  				      cb->compressed_len);
>  csum_failed:
> -	if (ret)
> +	if (ret && ret != -ENOKEY)
>  		cb->errors = 1;
>  
>  	/* release the compressed pages */
> @@ -751,6 +752,7 @@ static struct {
>  static const struct btrfs_compress_op * const btrfs_compress_op[] = {
>  	&btrfs_zlib_compress,
>  	&btrfs_lzo_compress,
> +	&btrfs_encrypt_ops,
>  };
>  
>  void __init btrfs_init_compress(void)
> @@ -780,6 +782,10 @@ static struct list_head *find_workspace(int type)
>  	atomic_t *alloc_ws		= &btrfs_comp_ws[idx].alloc_ws;
>  	wait_queue_head_t *ws_wait	= &btrfs_comp_ws[idx].ws_wait;
>  	int *num_ws			= &btrfs_comp_ws[idx].num_ws;
> +
> +	if (type == BTRFS_ENCRYPT_AES)
> +		return NULL;
> +
>  again:
>  	spin_lock(ws_lock);
>  	if (!list_empty(idle_ws)) {
> @@ -824,6 +830,9 @@ static void free_workspace(int type, struct list_head *workspace)
>  	wait_queue_head_t *ws_wait	= &btrfs_comp_ws[idx].ws_wait;
>  	int *num_ws			= &btrfs_comp_ws[idx].num_ws;
>  
> +	if (!workspace)
> +		return;
> +
>  	spin_lock(ws_lock);
>  	if (*num_ws < num_online_cpus()) {
>  		list_add(workspace, idle_ws);
> @@ -862,6 +871,38 @@ static void free_workspaces(void)
>  	}
>  }
>  
> +void print_data_encode_status(struct inode *inode, int direction,
> +					char *prefix, int type, int ret)
> +{
> +#ifdef CONFIG_BTRFS_DEBUG
> +	char what[10];
> +
> +	if (type == BTRFS_ENCRYPT_AES) {
> +		if (!direction)
> +			strcpy(what, "Encrypt");
> +		else
> +			strcpy(what, "Decrypt");
> +	} else {
> +		if (!direction)
> +			strcpy(what, "Compress");
> +		else
> +			strcpy(what, "Uncpress");
> +	}
> +
> +	switch (ret) {
> +	case 0:
> +		pr_debug("%s %s: success     : inode %lu\n",what, prefix, inode->i_ino);
> +		return;
> +	case -ENOKEY:
> +		pr_debug("%s %s: Failed NOKEY: inode %lu\n",what, prefix, inode->i_ino);
> +		return;
> +	default:
> +		pr_debug("%s %s: Failed %d   : inode %lu\n",what, prefix, ret, inode->i_ino);
> +	}
> +#else
> +#endif
> +}
> +
>  /*
>   * given an address space and start/len, compress the bytes.
>   *
> @@ -894,7 +935,7 @@ int btrfs_compress_pages(int type, struct address_space *mapping,
>  	int ret;
>  
>  	workspace = find_workspace(type);
> -	if (IS_ERR(workspace))
> +	if (workspace && IS_ERR(workspace))
>  		return PTR_ERR(workspace);
>  
>  	ret = btrfs_compress_op[type-1]->compress_pages(workspace, mapping,
> @@ -903,6 +944,8 @@ int btrfs_compress_pages(int type, struct address_space *mapping,
>  						      total_in, total_out,
>  						      max_out);
>  	free_workspace(type, workspace);
> +
> +	print_data_encode_status(mapping->host, 0, "    ", type, ret);
>  	return ret;
>  }
>  
> @@ -930,13 +973,14 @@ static int btrfs_decompress_biovec(int type, struct page **pages_in,
>  	int ret;
>  
>  	workspace = find_workspace(type);
> -	if (IS_ERR(workspace))
> +	if (workspace && IS_ERR(workspace))
>  		return PTR_ERR(workspace);
>  
>  	ret = btrfs_compress_op[type-1]->decompress_biovec(workspace, pages_in,
>  							 disk_start,
>  							 bvec, vcnt, srclen);
>  	free_workspace(type, workspace);
> +	print_data_encode_status(bvec->bv_page->mapping->host, 1, "bio ", type, ret);
>  	return ret;
>  }
>  
> @@ -952,7 +996,7 @@ int btrfs_decompress(int type, unsigned char *data_in, struct page *dest_page,
>  	int ret;
>  
>  	workspace = find_workspace(type);
> -	if (IS_ERR(workspace))
> +	if (workspace && IS_ERR(workspace))
>  		return PTR_ERR(workspace);
>  
>  	ret = btrfs_compress_op[type-1]->decompress(workspace, data_in,
> @@ -960,6 +1004,7 @@ int btrfs_decompress(int type, unsigned char *data_in, struct page *dest_page,
>  						  srclen, destlen);
>  
>  	free_workspace(type, workspace);
> +	print_data_encode_status(dest_page->mapping->host, 1, "page", type, ret);
>  	return ret;
>  }
>  
> diff --git a/fs/btrfs/compression.h b/fs/btrfs/compression.h
> index 13a4dc0436c9..78e8f38dbf60 100644
> --- a/fs/btrfs/compression.h
> +++ b/fs/btrfs/compression.h
> @@ -79,5 +79,6 @@ struct btrfs_compress_op {
>  
>  extern const struct btrfs_compress_op btrfs_zlib_compress;
>  extern const struct btrfs_compress_op btrfs_lzo_compress;
> +extern const struct btrfs_compress_op btrfs_encrypt_ops;
>  
>  #endif
> diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
> index bfe4a337fb4d..f30a92bf9c54 100644
> --- a/fs/btrfs/ctree.h
> +++ b/fs/btrfs/ctree.h
> @@ -719,8 +719,9 @@ enum btrfs_compression_type {
>  	BTRFS_COMPRESS_NONE  = 0,
>  	BTRFS_COMPRESS_ZLIB  = 1,
>  	BTRFS_COMPRESS_LZO   = 2,
> -	BTRFS_COMPRESS_TYPES = 2,
> -	BTRFS_COMPRESS_LAST  = 3,
> +	BTRFS_ENCRYPT_AES    = 3,
> +	BTRFS_COMPRESS_TYPES = 3,
> +	BTRFS_COMPRESS_LAST  = 4,
>  };
>  
>  struct btrfs_inode_item {
> @@ -771,6 +772,7 @@ struct btrfs_dir_item {
>   * still visible as a directory
>   */
>  #define BTRFS_ROOT_SUBVOL_DEAD		(1ULL << 48)
> +#define BTRFS_ROOT_SUBVOL_ENCRYPT	(1ULL << 49)
>  
>  struct btrfs_root_item {
>  	struct btrfs_inode_item inode;
> @@ -814,7 +816,9 @@ struct btrfs_root_item {
>  	struct btrfs_timespec otime;
>  	struct btrfs_timespec stime;
>  	struct btrfs_timespec rtime;
> -	__le64 reserved[8]; /* for future */
> +	char encrypt_algo[16];
> +	char encrypt_keytag[16];
> +	__le64 reserved[4]; /* for future */
>  } __attribute__ ((__packed__));
>  
>  /*
> @@ -2344,6 +2348,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/encrypt.c b/fs/btrfs/encrypt.c
> new file mode 100644
> index 000000000000..a6838cccc507
> --- /dev/null
> +++ b/fs/btrfs/encrypt.c
> @@ -0,0 +1,544 @@
> +/*
> + * Copyright (C) 2016 Oracle.  All rights reserved.
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public
> + * License v2 as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public
> + * License along with this program; if not, write to the
> + * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
> + * Boston, MA 021110-1307, USA.
> + */
> +#include <linux/string.h>
> +#include <linux/crypto.h>
> +#include <linux/scatterlist.h>
> +#include <linux/random.h>
> +#include <linux/pagemap.h>
> +#include <keys/user-type.h>
> +#include "compression.h"
> +#include <linux/slab.h>
> +#include <linux/keyctl.h>
> +#include <linux/key-type.h>
> +#include <linux/cred.h>
> +#include <keys/user-type.h>
> +#include "ctree.h"
> +#include "btrfs_inode.h"
> +#include "props.h"
> +
> +static const struct btrfs_encrypt_algorithm {
> +	const char *name;
> +	size_t	keylen;
> +} btrfs_encrypt_algorithm_supported[] = {
> +	{"aes", 16}
> +};
> +
> +/*
> + * Returns cipher alg key size if the encryption type is found
> + * otherwise 0
> + */
> +size_t btrfs_check_encrypt_type(char *type)
> +{
> +	int i;
> +	for (i = 0; i < ARRAY_SIZE(btrfs_encrypt_algorithm_supported); i++)
> +		if (!strcmp(btrfs_encrypt_algorithm_supported[i].name, type))
> +			return btrfs_encrypt_algorithm_supported[i].keylen;
> +
> +	return 0;
> +}
> +
> +/* key management*/
> +static int btrfs_request_key(char *key_tag, void *key_data)
> +{
> +	int ret;
> +	const struct user_key_payload *payload;
> +	struct key *btrfs_key = NULL;
> +
> +	ret = 0;
> +	btrfs_key = request_key(&key_type_user, key_tag, NULL);
> +	if (IS_ERR(btrfs_key)) {
> +		ret = PTR_ERR(btrfs_key);
> +		btrfs_key = NULL;
> +		return ret;
> +	}
> +
> +	/*
> +	 * caller just need key not payload so return
> +	 */
> +	if (!key_data)
> +		return 0;
> +
> +	ret = key_validate(btrfs_key);
> +	if (ret < 0)
> +		goto out;
> +
> +	rcu_read_lock(); // TODO: check down_write key->sem ?
> +	payload = user_key_payload(btrfs_key);
> +	if (IS_ERR_OR_NULL(payload)) {
> +		ret = PTR_ERR(payload);
> +		goto out;
> +	}
> +
> +	/*
> +	 * As of now we just hard code as we just use ASE now
> +	 */
> +	if (payload->datalen != 16)
> +		ret = -EINVAL;
> +	else
> +		memcpy(key_data, payload->data, 16);
> +
> +out:
> +	rcu_read_unlock();
> +	key_put(btrfs_key);
> +
> +	return ret;
> +}
> +
> +static int btrfs_get_key_data_from_inode(struct inode *inode, unsigned char *keydata)
> +{
> +	int ret;
> +	char keytag[15];
> +	struct btrfs_inode *binode;
> +	struct btrfs_root_item *ri;
> +
> +	binode = BTRFS_I(inode);
> +	ri = &(binode->root->root_item);
> +	strncpy(keytag, ri->encrypt_keytag, 14);
> +	keytag[14] = '\0';
> +
> +	ret = btrfs_request_key(keytag, keydata);
> +	return ret;
> +}
> +
> +int btrfs_update_key_data_to_binode(struct inode *inode)
> +{
> +	int ret;
> +	unsigned char keydata[16];
> +	struct btrfs_inode *binode;
> +
> +	ret = btrfs_get_key_data_from_inode(inode, keydata);
> +	if (ret)
> +		return ret;
> +
> +	binode = BTRFS_I(inode);
> +	memcpy(binode->key_payload, keydata, 16);
> +
> +	return ret;
> +}
> +
> +int btrfs_get_keytag(struct address_space *mapping, char *keytag, struct inode **inode)
> +{
> +	struct btrfs_inode *binode;
> +	struct btrfs_root_item *ri;
> +
> +	if (!mapping)
> +		return -EINVAL;
> +
> +	if (!(mapping->host))
> +		return -EINVAL;
> +
> +	binode = BTRFS_I(mapping->host);
> +	ri = &(binode->root->root_item);
> +
> +	strncpy(keytag, ri->encrypt_keytag, 14);
> +	keytag[14] = '\0';
> +	if (inode)
> +		*inode = &binode->vfs_inode;
> +
> +	return 0;
> +}
> +
> +/* Encrypt and decrypt */
> +struct workspace {
> +	struct list_head list;
> +};
> +
> +struct btrfs_crypt_result {
> +	struct completion completion;
> +	int err;
> +};
> +
> +struct btrfs_ablkcipher_def {
> +	struct scatterlist sg;
> +	struct crypto_ablkcipher *tfm;
> +	struct ablkcipher_request *req;
> +	struct btrfs_crypt_result result;
> +};
> +
> +int btrfs_do_blkcipher(int enc, char *data, size_t len)
> +{

This function is not used anywhere.

> +	int ret = -EFAULT;
> +	struct scatterlist sg;
> +	unsigned int ivsize = 0;
> +	char *cipher = "cbc(aes)";
> +	struct blkcipher_desc desc;
> +	struct crypto_blkcipher *blkcipher = NULL;
> +	char *charkey =
> +		"\x12\x34\x56\x78\x90\xab\xcd\xef\x12\x34\x56\x78\x90\xab\xcd\xef";
> +	char *chariv =
> +		"\x12\x34\x56\x78\x90\xab\xcd\xef\x12\x34\x56\x78\x90\xab\xcd\xef";
> +
> +	blkcipher = crypto_alloc_blkcipher(cipher, 0, 0);
> +	if (IS_ERR(blkcipher)) {
> +		printk("could not allocate blkcipher handle for %s\n", cipher);
> +		return -PTR_ERR(blkcipher);
> +	}
> +
> +	if (crypto_blkcipher_setkey(blkcipher, charkey, 16)) {
> +		printk("key could not be set\n");
> +		ret = -EAGAIN;
> +		goto out;
> +	}
> +
> +	ivsize = crypto_blkcipher_ivsize(blkcipher);
> +	if (ivsize) {
> +		if (ivsize != strlen(chariv)) {
> +			printk("length differs from expected length\n");
> +			ret = -EINVAL;
> +			goto out;
> +		}
> +		crypto_blkcipher_set_iv(blkcipher, chariv, ivsize);
> +	}
> +
> +	desc.flags = 0;
> +	desc.tfm = blkcipher;
> +	sg_init_one(&sg, data, len);
> +
> +	if (enc) {
> +		/* encrypt data in place */
> +		ret = crypto_blkcipher_encrypt(&desc, &sg, &sg, len);
> +	} else {
> +		/* decrypt data in place */
> +		ret = crypto_blkcipher_decrypt(&desc, &sg, &sg, len);
> +	}
> +
> +	return ret;
> +
> +out:
> +	crypto_free_blkcipher(blkcipher);
> +	return ret;
> +}
> +
> +static void btrfs_ablkcipher_cb(struct crypto_async_request *req, int error)
> +{
> +	struct btrfs_crypt_result *result;
> +
> +	if (error == -EINPROGRESS) {
> +		pr_info("Encryption callback reports error\n");
> +		return;
> +	}
> +
> +	result = req->data;
> +	result->err = error;
> +	complete(&result->completion);
> +	pr_info("Encryption finished successfully\n");
> +}
> +
> +static unsigned int btrfs_ablkcipher_encdec(struct btrfs_ablkcipher_def *ablk, int enc)
> +{
> +	int rc = 0;
> +
> +	if (enc)
> +		rc = crypto_ablkcipher_encrypt(ablk->req);
> +	else
> +		rc = crypto_ablkcipher_decrypt(ablk->req);
> +
> +	switch (rc) {
> +	case 0:
> +		break;
> +	case -EINPROGRESS:
> +	case -EBUSY:
> +		rc = wait_for_completion_interruptible(
> +			&ablk->result.completion);
> +		if (!rc && !ablk->result.err) {
> +			reinit_completion(&ablk->result.completion);
> +			break;
> +		}
> +	default:
> +		pr_info("ablkcipher encrypt returned with %d result %d\n",
> +		       rc, ablk->result.err);
> +		break;
> +	}
> +	init_completion(&ablk->result.completion);
> +
> +	return rc;
> +}
> +
> +void btrfs_cipher_get_ivdata(char **ivdata, unsigned int ivsize, unsigned int *ivdata_size)
> +{
> +	/* fixme */
> +	if (0) {
> +		*ivdata = kmalloc(ivsize, GFP_KERNEL);
> +		get_random_bytes(ivdata, ivsize);
> +		*ivdata_size = ivsize;
> +	} else {
> +		*ivdata = kstrdup(
> +			"\x12\x34\x56\x78\x90\xab\xcd\xef\x12\x34\x56\x78\x90\xab\xcd\xef",
> +			GFP_NOFS);
> +		*ivdata_size = strlen(*ivdata);
> +	}
> +}
> +
> +static int btrfs_do_ablkcipher(int endec, struct page *page, unsigned long len,
> +							struct inode *inode)
> +{
> +	int ret = -EFAULT;
> +	unsigned char key_data[16];
> +	char *ivdata = NULL;
> +	unsigned int key_size;
> +	unsigned int ivsize = 0;
> +	unsigned int ivdata_size;
> +	unsigned int ablksize = 0;
> +	struct btrfs_ablkcipher_def ablk_akin;
> +	struct ablkcipher_request *req = NULL;
> +	struct crypto_ablkcipher *ablkcipher = NULL;
> +
> +	ret = 0;
> +
> +	if (!inode) {
> +		BUG_ON("Need inode\n");
> +		return -EINVAL;
> +	}
> +	/* get key from the inode */
> +	ret = btrfs_get_key_data_from_inode(inode, key_data);
> +
> +	key_size = 16; //todo: defines, but review for suitable cipher
> +
> +	ablkcipher = crypto_alloc_ablkcipher("cts(cbc(aes))", 0, 0);
> +	if (IS_ERR(ablkcipher)) {
> +		pr_info("could not allocate ablkcipher handle\n");
> +		return PTR_ERR(ablkcipher);
> +	}
> +
> +	ablksize = crypto_ablkcipher_blocksize(ablkcipher);
> +	/* we can't cipher a block less the ciper block size */
> +	if (len < ablksize || len > PAGE_CACHE_SIZE) {
> +		ret = -EINVAL;
> +		goto out;
> +	}
> +
> +	ivsize = crypto_ablkcipher_ivsize(ablkcipher);
> +	if (ivsize) {
> +		btrfs_cipher_get_ivdata(&ivdata, ivsize, &ivdata_size);
> +		if (ivsize != ivdata_size) {
> +			BUG_ON("IV length differs from expected length\n");
> +			ret = -EINVAL;
> +			goto out;
> +		}
> +	} else {
> +		BUG_ON("This cipher doesn't need ivdata, but are we ready ?\n");
> +		ret = -EINVAL;
> +		goto out;
> +	}
> +
> +	req = ablkcipher_request_alloc(ablkcipher, GFP_KERNEL);
> +	if (IS_ERR(req)) {
> +		pr_info("could not allocate request queue\n");
> +		ret = PTR_ERR(req);
> +		goto out;
> +	}
> +
> +	ablkcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG,
> +					btrfs_ablkcipher_cb, &ablk_akin.result);
> +
> +	if (crypto_ablkcipher_setkey(ablkcipher, key_data, key_size)) {
> +		printk("key could not be set\n");
> +		ret = -EAGAIN;
> +		goto out;
> +	}
> +
> +	ablk_akin.tfm = ablkcipher;
> +	ablk_akin.req = req;
> +
> +	sg_init_table(&ablk_akin.sg, 1);
> +	sg_set_page(&ablk_akin.sg, page, len, 0);
> +	ablkcipher_request_set_crypt(req, &ablk_akin.sg, &ablk_akin.sg, len, ivdata);

Are you sure it is OK to use the same page for src and dst scatterlist?

I don't think it's supposed to be used this way.

> +
> +	init_completion(&ablk_akin.result.completion);
> +
> +	ret = btrfs_ablkcipher_encdec(&ablk_akin, endec);
> +
> +out:
> +	if (ablkcipher)
> +		crypto_free_ablkcipher(ablkcipher);

req->base.tfm still points to a field in ablkcipher.

> +	if (req)
> +		ablkcipher_request_free(req);
> +
> +	kfree(ivdata);
> +
> +	return ret;
> +}
> +
> +static int btrfs_encrypt_pages(struct list_head *na_ws, struct address_space *mapping,
> +			u64 start, unsigned long len, struct page **pages,
> +			unsigned long nr_pages, unsigned long *na_out_pages,
> +			unsigned long *na_total_in, unsigned long *na_total_out,
> +			unsigned long na_max_out)
> +{
> +	int ret;
> +	struct page *in_page;
> +	struct page *out_page;
> +	char *in;
> +	char *out;
> +	unsigned long bytes_left = len;
> +	unsigned long cur_page_len;
> +	unsigned long cur_page;
> +	struct inode *inode;
> +
> +	*na_total_in = 0;
> +	*na_out_pages = 0;
> +
> +	if (!mapping && !mapping->host) {
> +		WARN_ON("Need mapped pages\n");
> +		return -EINVAL;
> +	}
> +
> +	inode = mapping->host;
> +
> +	for (cur_page = 0; cur_page < nr_pages; cur_page++) {
> +
> +		WARN_ON(!bytes_left);
> +
> +		in_page = find_get_page(mapping, start >> PAGE_CACHE_SHIFT);
> +		out_page = alloc_page(GFP_NOFS| __GFP_HIGHMEM);
> +		cur_page_len = min(bytes_left, PAGE_CACHE_SIZE);
> +
> +		in = kmap(in_page);
> +		out = kmap(out_page);
> +		memcpy(out, in, cur_page_len);
> +		kunmap(out_page);
> +		kunmap(in_page);
> +
> +		ret = btrfs_do_ablkcipher(1, out_page, cur_page_len, inode);
> +		if (ret) {
> +			__free_page(out_page);
> +			return ret;
> +		}
> +
> +		pages[cur_page] = out_page;
> +		*na_out_pages = *na_out_pages + 1;
> +		*na_total_in = *na_total_in + cur_page_len;
> +
> +		start += cur_page_len;
> +		bytes_left = bytes_left - cur_page_len;
> +	}
> +
> +	return ret;
> +}
> +
> +static int btrfs_decrypt_pages(struct list_head *na_ws, unsigned char *in, struct page *out_page,
> +				unsigned long na_start_byte, size_t in_size, size_t out_size)
> +{
> +	int ret;
> +	char *out_addr;
> +	char keytag[24];
> +	struct address_space *mapping;
> +	struct inode *inode;
> +
> +	if (!out_page)
> +		return -EINVAL;
> +
> +	if (in_size > PAGE_CACHE_SIZE)
> +		return -EINVAL;
> +
> +	memset(keytag, '\0', 24);
> +
> +	mapping = out_page->mapping;
> +	if (!mapping && !mapping->host) {
> +		WARN_ON("Need mapped pages\n");
> +		return -EINVAL;
> +	}
> +
> +	inode = mapping->host;
> +
> +	out_addr = kmap(out_page);
> +	memcpy(out_addr, in, in_size);
> +	kunmap(out_page);
> +
> +	ret = btrfs_do_ablkcipher(0, out_page, in_size, inode);
> +
> +	return ret;
> +}
> +
> +static int btrfs_decrypt_pages_bio(struct list_head *na_ws, struct page **in_pages,
> +					u64 in_start_offset, struct bio_vec *out_pages_bio,
> +					int bi_vcnt, size_t in_len)
> +{
> +	char *in;
> +	char *out;
> +	int ret = 0;
> +	struct page *in_page;
> +	struct page *out_page;
> +	unsigned long cur_page_n;
> +	unsigned long bytes_left;
> +	unsigned long in_nr_pages;
> +	unsigned long cur_page_len;
> +	unsigned long processed_len = 0;
> +	struct address_space *mapping;
> +	struct inode *inode;
> +
> +	if (na_ws)
> +		return -EINVAL;
> +
> +	out_page = out_pages_bio[0].bv_page;
> +	mapping = out_page->mapping;
> +	if (!mapping && !mapping->host) {
> +		WARN_ON("Need mapped page\n");
> +		return -EINVAL;
> +	}
> +
> +	inode = mapping->host;
> +
> +	in_nr_pages = DIV_ROUND_UP(in_len, PAGE_CACHE_SIZE);
> +	bytes_left = in_len;
> +	WARN_ON(in_nr_pages != bi_vcnt);
> +
> +	for (cur_page_n = 0; cur_page_n < in_nr_pages; cur_page_n++) {
> +		WARN_ON(!bytes_left);
> +
> +		in_page = in_pages[cur_page_n];
> +		out_page = out_pages_bio[cur_page_n].bv_page;
> +
> +		cur_page_len = min(bytes_left, PAGE_CACHE_SIZE);
> +
> +		in = kmap(in_page);
> +		out = kmap(out_page);
> +		memcpy(out, in, cur_page_len);
> +		kunmap(out_page);
> +		kunmap(in_page);
> +
> +		ret = btrfs_do_ablkcipher(0, out_page, cur_page_len, inode);
> +
> +		if (ret && ret != -ENOKEY)
> +			goto error_out;
> +
> +		if (cur_page_len < PAGE_CACHE_SIZE) {
> +			out = kmap(out_page);
> +			memset(out + cur_page_len, 0, PAGE_CACHE_SIZE - cur_page_len);
> +			kunmap(out_page);
> +		}
> +
> +		bytes_left = bytes_left - cur_page_len;
> +		processed_len = processed_len + cur_page_len;
> +
> +		//flush_dcache_page(out_page);
> +	}
> +	WARN_ON(processed_len != in_len);
> +	WARN_ON(bytes_left);
> +
> +error_out:
> +	return ret;
> +}
> +
> +const struct btrfs_compress_op btrfs_encrypt_ops = {
> +	.alloc_workspace	= NULL,
> +	.free_workspace		= NULL,
> +	.compress_pages		= btrfs_encrypt_pages,
> +	.decompress_biovec	= btrfs_decrypt_pages_bio,
> +	.decompress		= btrfs_decrypt_pages,
> +};
> diff --git a/fs/btrfs/encrypt.h b/fs/btrfs/encrypt.h
> new file mode 100644
> index 000000000000..36a7067e98b1
> --- /dev/null
> +++ b/fs/btrfs/encrypt.h
> @@ -0,0 +1,21 @@
> +/*
> + * Copyright (C) 2016 Oracle.  All rights reserved.
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public
> + * License v2 as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public
> + * License along with this program; if not, write to the
> + * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
> + * Boston, MA 021110-1307, USA.
> + */
> +
> +
> +size_t btrfs_check_encrypt_type(char *encryption_type);
> +int btrfs_update_key_data_to_binode(struct inode *inode);
> diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
> index 151b7c71b868..b27a89d89753 100644
> --- a/fs/btrfs/inode.c
> +++ b/fs/btrfs/inode.c
> @@ -60,6 +60,7 @@
>  #include "hash.h"
>  #include "props.h"
>  #include "qgroup.h"
> +#include "encrypt.h"
>  
>  struct btrfs_iget_args {
>  	struct btrfs_key *location;
> @@ -206,6 +207,8 @@ static int insert_inline_extent(struct btrfs_trans_handle *trans,
>  		}
>  		btrfs_set_file_extent_compression(leaf, ei,
>  						  compress_type);
> +		if (compress_type == BTRFS_ENCRYPT_AES)
> +			btrfs_set_file_extent_encryption(leaf, ei, 1);

Looks like decrypt is not yet used in read_page path, I only see btrfs_set_file_extent_encryption.

>  	} else {
>  		page = find_get_page(inode->i_mapping,
>  				     start >> PAGE_CACHE_SHIFT);
> @@ -581,7 +584,7 @@ cont:
>  		 * win, compare the page count read with the blocks on disk
>  		 */
>  		total_in = ALIGN(total_in, PAGE_CACHE_SIZE);
> -		if (total_compressed >= total_in) {
> +		if (total_compressed >= total_in && compress_type != BTRFS_ENCRYPT_AES) {
>  			will_compress = 0;
>  		} else {
>  			num_bytes = total_in;
> @@ -6704,6 +6707,8 @@ static noinline int uncompress_inline(struct btrfs_path *path,
>  	max_size = min_t(unsigned long, PAGE_CACHE_SIZE, max_size);
>  	ret = btrfs_decompress(compress_type, tmp, page,
>  			       extent_offset, inline_size, max_size);
> +	if (ret && ret == -ENOKEY)
> +		ret = 0;
>  	kfree(tmp);
>  	return ret;
>  }
> @@ -9271,6 +9276,20 @@ static int btrfs_rename(struct inode *old_dir, struct dentry *old_dentry,
>  	u64 root_objectid;
>  	int ret;
>  	u64 old_ino = btrfs_ino(old_inode);
> +	u64 root_flags;
> +	u64 dest_flags;
> +
> +	/*
> +	 * As of now block an encrypted file/dir to move across
> +	 * subvol which potentially has different key.
> +	 */
> +	root_flags = btrfs_root_flags(&root->root_item);
> +	dest_flags = btrfs_root_flags(&dest->root_item);
> +	if (root != dest &&
> +		((root_flags & BTRFS_ROOT_SUBVOL_ENCRYPT) ||
> +		(dest_flags & BTRFS_ROOT_SUBVOL_ENCRYPT))) {
> +		return -EOPNOTSUPP;
> +	}
>  
>  	if (btrfs_ino(new_dir) == BTRFS_EMPTY_SUBVOL_DIR_OBJECTID)
>  		return -EPERM;
> @@ -9931,6 +9950,22 @@ static int btrfs_permission(struct inode *inode, int mask)
>  		if (BTRFS_I(inode)->flags & BTRFS_INODE_READONLY)
>  			return -EACCES;
>  	}
> +
> +	/*
> +	 * Get the required key as we encrypt only file data, this
> +	 * this applies only files as of now.

Double 'this'.

> +	 */
> +	if (S_ISREG(mode)) {
> +		int ret = 0;
> +		u64 root_flags;
> +		root_flags = btrfs_root_flags(&root->root_item);
> +		if (root_flags & BTRFS_ROOT_SUBVOL_ENCRYPT) {
> +			ret = btrfs_update_key_data_to_binode(inode);
> +			if (ret)
> +				return -ENOKEY;
> +		}
> +	}
> +
>  	return generic_permission(inode, mask);
>  }
>  
> diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
> index 48aee9846329..3a0f40e4a713 100644
> --- a/fs/btrfs/ioctl.c
> +++ b/fs/btrfs/ioctl.c
> @@ -2139,8 +2139,15 @@ static noinline int btrfs_ioctl_tree_search(struct file *file,
>  	int ret;
>  	size_t buf_size;
>  
> +#if 0
> +	/*
> +	 * Todo: Workaround as of now instead of introduing a new ioctl,
> +	 * so that non root user can find info about the subvol
> +	 * they own/create. This must be fixed in final.
> +	 */
>  	if (!capable(CAP_SYS_ADMIN))
>  		return -EPERM;
> +#endif
>  
>  	uargs = (struct btrfs_ioctl_search_args __user *)argp;
>  
> diff --git a/fs/btrfs/props.c b/fs/btrfs/props.c
> index f9e60231f685..d40ace5f5492 100644
> --- a/fs/btrfs/props.c
> +++ b/fs/btrfs/props.c
> @@ -22,10 +22,16 @@
>  #include "hash.h"
>  #include "transaction.h"
>  #include "xattr.h"
> +#include "encrypt.h"
>  
>  #define BTRFS_PROP_HANDLERS_HT_BITS 8
>  static DEFINE_HASHTABLE(prop_handlers_ht, BTRFS_PROP_HANDLERS_HT_BITS);
>  
> +#define BTRFS_PROP_INHERIT_NONE		(1U << 0)
> +#define BTRFS_PROP_INHERIT_FOR_DIR	(1U << 1)
> +#define BTRFS_PROP_INHERIT_FOR_CLONE	(1U << 2)
> +#define BTRFS_PROP_INHERIT_FOR_SUBVOL	(1U << 3)
> +
>  struct prop_handler {
>  	struct hlist_node node;
>  	const char *xattr_name;
> @@ -41,13 +47,28 @@ static int prop_compression_apply(struct inode *inode,
>  				  size_t len);
>  static const char *prop_compression_extract(struct inode *inode);
>  
> +static int prop_encrypt_validate(const char *value, size_t len);
> +static int prop_encrypt_apply(struct inode *inode,
> +				  const char *value, size_t len);
> +static const char *prop_encrypt_extract(struct inode *inode);
> +
>  static struct prop_handler prop_handlers[] = {
>  	{
>  		.xattr_name = XATTR_BTRFS_PREFIX "compression",
>  		.validate = prop_compression_validate,
>  		.apply = prop_compression_apply,
>  		.extract = prop_compression_extract,
> -		.inheritable = 1
> +		.inheritable = BTRFS_PROP_INHERIT_FOR_DIR| \
> +				BTRFS_PROP_INHERIT_FOR_CLONE| \
> +				BTRFS_PROP_INHERIT_FOR_SUBVOL,
> +	},
> +	{
> +		.xattr_name = XATTR_BTRFS_PREFIX "encrypt",
> +		.validate = prop_encrypt_validate,
> +		.apply = prop_encrypt_apply,
> +		.extract = prop_encrypt_extract,
> +		.inheritable = BTRFS_PROP_INHERIT_FOR_DIR| \
> +				BTRFS_PROP_INHERIT_FOR_CLONE,
>  	},
>  };
>  
> @@ -315,6 +336,13 @@ static int inherit_props(struct btrfs_trans_handle *trans,
>  		if (!h->inheritable)
>  			continue;
>  
> +		//is_subvolume_inode(); ?
> +		if (btrfs_ino(inode) == BTRFS_FIRST_FREE_OBJECTID) {
> +			if (!strcmp(h->xattr_name, "btrfs.encrypt")) {
> +				continue;
> +			}
> +		}
> +
>  		value = h->extract(parent);
>  		if (!value)
>  			continue;
> @@ -425,4 +453,114 @@ static const char *prop_compression_extract(struct inode *inode)
>  	return NULL;
>  }
>  
> +static int btrfs_create_encrypt_key_tuplet(char *algo, char *tag, char *val_out)
> +{
> +	return snprintf(val_out, 32, "%s@%s", algo, tag);
> +}
> +
> +static int btrfs_split_key_tuplet(const char *val, size_t len,
> +					char *keyalgo, char *keytag)
> +{
> +	char *tmp;
> +	char *tmp1;
> +	char *tmp2;
> +
> +	tmp1 = tmp = kstrdup(val, GFP_NOFS);
> +	tmp[len] = '\0';
> +	tmp2 = strsep(&tmp, "@");
> +	if (!tmp2) {
> +		kfree(tmp1);
> +		return -EINVAL;
> +	}
> +
> +	if (strlen(tmp2) > 16 || strlen(tmp) > 16) {
> +		kfree(tmp1);

tmp2 needs to be freed too.

> +		return -EINVAL;
> +	}
> +	strcpy(keyalgo, tmp2);
> +	strcpy(keytag, tmp);

tmp1 and tmp2 need to be freed.

> +
> +	return 0;
> +}
> +
> +/*
> + * The required foramt in the value is <encrypt_algo>@<key_tag>
> + * eg: btrfs.encrypt="aes@btrfs:61e0d004"
> + */
> +static int prop_encrypt_validate(const char *value, size_t len)
> +{
> +	int ret;
> +	char keytag[16];
> +	char keyalgo[16];
> +	size_t keylen;
> +
> +	if (!len)
> +		return 0;
> +
> +	ret = btrfs_split_key_tuplet(value, len, keyalgo, keytag);
> +	if (ret)
> +		return ret;
>  
> +	keylen = btrfs_check_encrypt_type(keyalgo);
> +	if (!keylen)
> +		return -ENOTSUPP;
> +
> +	return ret;
> +}
> +
> +static int prop_encrypt_apply(struct inode *inode,
> +				const char *value, size_t len)
> +{
> +	int ret;
> +	u64 root_flags;
> +	char keytag[16];
> +	char keyalgo[16];
> +	struct btrfs_root_item *root_item;
> +
> +	root_item = &(BTRFS_I(inode)->root->root_item);
> +
> +	if (len == 0) {
> +		BTRFS_I(inode)->flags &= ~BTRFS_INODE_ENCRYPT;
> +		BTRFS_I(inode)->force_compress = 0;
> +
> +		if (btrfs_ino(inode) == BTRFS_FIRST_FREE_OBJECTID) {
> +			root_flags = btrfs_root_flags(root_item);
> +			btrfs_set_root_flags(root_item, root_flags | ~BTRFS_ROOT_SUBVOL_ENCRYPT);
> +			memset(root_item->encrypt_algo, '\0', 16);
> +			memset(root_item->encrypt_keytag, '\0', 16);
> +		}
> +		return 0;
> +	}
> +
> +	BTRFS_I(inode)->flags |= BTRFS_INODE_ENCRYPT;
> +	BTRFS_I(inode)->force_compress = BTRFS_ENCRYPT_AES;
> +
> +	ret = btrfs_split_key_tuplet(value, len, keyalgo, keytag);
> +	if (ret)
> +		return ret;
> +
> +	/* do it only for the subvol or snapshot */
> +	if (btrfs_ino(inode) == BTRFS_FIRST_FREE_OBJECTID) {
> +		root_flags = btrfs_root_flags(root_item);
> +		btrfs_set_root_flags(root_item, root_flags | BTRFS_ROOT_SUBVOL_ENCRYPT);
> +		/* TODO: this is not right, fix it */
> +		strncpy(root_item->encrypt_algo, keyalgo, 16);
> +		strncpy(root_item->encrypt_keytag, keytag, 16);
> +	}
> +
> +	return 0;
> +}
> +
> +static const char *prop_encrypt_extract(struct inode *inode)
> +{
> +	int ret;
> +	char val[32];
> +	struct btrfs_root_item *ri;
> +
> +	ri = &(BTRFS_I(inode)->root->root_item);
> +
> +	ret = btrfs_create_encrypt_key_tuplet(ri->encrypt_algo,
> +					ri->encrypt_keytag, val);
> +
> +	return kstrdup(val, GFP_NOFS);

A pointer is returned here, but in 

	value = h->extract();

value is not be freed by the caller.

Thanks,

-liubo

> +}
> diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c
> index d41e09fe8e38..400225890f5f 100644
> --- a/fs/btrfs/super.c
> +++ b/fs/btrfs/super.c
> @@ -59,10 +59,10 @@
>  #include "free-space-cache.h"
>  #include "backref.h"
>  #include "tests/btrfs-tests.h"
> -
>  #include "qgroup.h"
>  #define CREATE_TRACE_POINTS
>  #include <trace/events/btrfs.h>
> +#include "encrypt.h"
>  
>  static const struct super_operations btrfs_super_ops;
>  static struct file_system_type btrfs_fs_type;
> @@ -92,6 +92,9 @@ const char *btrfs_decode_error(int errno)
>  	case -ENOENT:
>  		errstr = "No such entry";
>  		break;
> +	case -ENOKEY:
> +		errstr = "Required key not available";
> +		break;
>  	}
>  
>  	return errstr;
> -- 
> 2.7.0
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* Re: [RFC] Experimental btrfs encryption
  2016-03-02  1:06 ` Qu Wenruo
  2016-03-02  9:09   ` Anand Jain
  2016-03-03 10:17   ` Alex Elsayed
@ 2016-03-20 11:56   ` Martin Steigerwald
  2 siblings, 0 replies; 26+ messages in thread
From: Martin Steigerwald @ 2016-03-20 11:56 UTC (permalink / raw)
  To: Qu Wenruo; +Cc: Anand Jain, linux-btrfs, clm, dsterba

On Mittwoch, 2. März 2016 09:06:57 CET Qu Wenruo wrote:
> And maybe I just missed something, but the filename seems not touched, 
> meaning it will leak a lot of information.
> Just like default eCryptfs behavior.
> 
> I understand that's an easy design and it's not a high priority thing, 
> but I hope we can encrypt the subvolume tree blocks too, if using 
> per-subvolume policy.
> To provide a feature near block-level encryption.

I´d really love an approach to at least optionally be able to hide the 
metadata structure completely except for which blocks on the block device are 
allocated. I.e. not just encrypting filenames, but encrypting the directory 
structure, amount of files, their dates, their sizes. I am not sure whether 
BTRFS can allow this and still be at least btrfs check´able without unlocking 
the encryption key. Ideally this could even be backuped by an btrfs send/
receive as a kind opaque stream.

This would excel BTRFS encryption support over anything thats available with 
Ext4, F2FS, ecryptfs and encfs. It would ideal for having encryption on SSD, 
no need to encrypted unallocated blocks, but still most of the advantages of 
block level encryption, even of some would argue that you can find something 
out when you check which blocks are allocated or not, and of course the total 
size of the subvolume and which chunks it allocates are known.

I would the this as requirement for any initial approach and be happy about 
anything that does file name encryption like ecryptfs or the Ext4/F2FS 
approach, but if the subvolume specifics of BTRFS can be used to encrypted 
more of the metadata then even better!

Thanks,
-- 
Martin

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

* Re: [RFC] Experimental btrfs encryption
  2016-03-03  1:58 ` Anand Jain
@ 2016-03-22 14:25   ` David Sterba
  0 siblings, 0 replies; 26+ messages in thread
From: David Sterba @ 2016-03-22 14:25 UTC (permalink / raw)
  To: Anand Jain; +Cc: linux-btrfs, clm, dsterba

On Thu, Mar 03, 2016 at 09:58:53AM +0800, Anand Jain wrote:
> . (I received couple of private emails on this, so looks like
>   I confused you and I'm writing again to clear the air on this).
> 
> > - Uses btrfs compression framework, so compression and then
> >    encryption is not possible. However yet evaluate if there
> >    are encryption algorithm which can compress as well.
> 
>   It should be compression and then encryption. I didn't mean to say
>   the other way around. However the btrfs encoding framework is
>   designed to handle any one of it in an elegant manner. So as of
>   user can configure either encryption OR compression by design.
> 
>   Further for users who are looking for both compression and encryption.
>   There are two ways that we could implement in future in the btrfs.
>   One enhance the btrfs encoding framework so that it can accommodate
>   two cascaded engines like compression and followed by encryption.
>   Or (mostly) a better approach would be to evaluate a single encoding
>   engine (algorithm) which can do both (compression and then encryption).
>   Which I think will be less invasive within btrfs, and probably be more
>   efficient.
> 
>   Hope I sound clearer now. Sorry if I wasn't before.
> 
> . I have put up a doc here:
> 
> https://docs.google.com/document/d/1fq9snDM_4ikn44UDNErjHqKXgZHukiJWS4Il3qVhm3M/edit?usp=sharing

It's just text, you could also send it as part of the patchset.

I'd very much like to see the crypto part covered in more detail, the
threat model, what's the right cipher to use and why. You've chosen
something that would demonstrate how it works, but eg. implementing the
AEAD mode would not be straightforward to add, while it would be a
significant difference against the current per-file encryption (we can
store per-block associated data cheaply). And there are certainly other
questions that I've missed.

The per-file encryption is on the way to the VFS layer, so we can
implement it in btrfs afterwards. Then the benefit of the proposed
patchset would be encrypted data on subvolume with snapshotting enabled.
That's probably something that people want and convers common usecases.
But we can go further, like full subvolume metadata encryption.

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

* Re: [RFC PATCH 1/1] btrfs: Encryption: Add btrfs encryption support
  2016-03-10  2:19   ` Liu Bo
@ 2016-05-06  9:21     ` Anand Jain
  0 siblings, 0 replies; 26+ messages in thread
From: Anand Jain @ 2016-05-06  9:21 UTC (permalink / raw)
  To: bo.li.liu; +Cc: linux-btrfs, clm, dsterba



  Thanks for the review comments Liu bo. I am looking into the comments.

Anand


On 03/10/2016 10:19 AM, Liu Bo wrote:
> On Wed, Mar 02, 2016 at 12:08:10AM +0800, Anand Jain wrote:
>> ***
>> *** Warning: Experimental code.
>> ***
>>
>> Adds encryption support. The branch is based on v4.5-rc6.
>>
>> Signed-off-by: Anand Jain <anand.jain@oracle.com>
>> ---
>>   fs/btrfs/Makefile      |   2 +-
>>   fs/btrfs/btrfs_inode.h |   2 +
>>   fs/btrfs/compression.c |  53 ++++-
>>   fs/btrfs/compression.h |   1 +
>>   fs/btrfs/ctree.h       |  11 +-
>>   fs/btrfs/encrypt.c     | 544 +++++++++++++++++++++++++++++++++++++++++++++++++
>>   fs/btrfs/encrypt.h     |  21 ++
>>   fs/btrfs/inode.c       |  37 +++-
>>   fs/btrfs/ioctl.c       |   7 +
>>   fs/btrfs/props.c       | 140 ++++++++++++-
>>   fs/btrfs/super.c       |   5 +-
>>   11 files changed, 812 insertions(+), 11 deletions(-)
>>   create mode 100644 fs/btrfs/encrypt.c
>>   create mode 100644 fs/btrfs/encrypt.h
>>
>> diff --git a/fs/btrfs/Makefile b/fs/btrfs/Makefile
>> index 128ce17a80b0..2765778c5898 100644
>> --- a/fs/btrfs/Makefile
>> +++ b/fs/btrfs/Makefile
>> @@ -9,7 +9,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 \
>>   	   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 hash.o free-space-tree.o
>> +	   uuid-tree.o props.o hash.o free-space-tree.o encrypt.o
>>
>>   btrfs-$(CONFIG_BTRFS_FS_POSIX_ACL) += acl.o
>>   btrfs-$(CONFIG_BTRFS_FS_CHECK_INTEGRITY) += check-integrity.o
>> diff --git a/fs/btrfs/btrfs_inode.h b/fs/btrfs/btrfs_inode.h
>> index 61205e3bbefa..4f09572b4922 100644
>> --- a/fs/btrfs/btrfs_inode.h
>> +++ b/fs/btrfs/btrfs_inode.h
>> @@ -197,6 +197,8 @@ struct btrfs_inode {
>>   	long delayed_iput_count;
>>
>>   	struct inode vfs_inode;
>> +
>> +	unsigned char key_payload[16];
>>   };
>>
>>   extern unsigned char btrfs_filetype_table[];
>> diff --git a/fs/btrfs/compression.c b/fs/btrfs/compression.c
>> index 3346cd8f9910..d59c366f4200 100644
>> --- a/fs/btrfs/compression.c
>> +++ b/fs/btrfs/compression.c
>> @@ -41,6 +41,7 @@
>>   #include "compression.h"
>>   #include "extent_io.h"
>>   #include "extent_map.h"
>> +#include "encrypt.h"
>>
>>   struct compressed_bio {
>>   	/* number of bios pending for this compressed extent */
>> @@ -182,7 +183,7 @@ static void end_compressed_bio_read(struct bio *bio)
>>   				      cb->orig_bio->bi_vcnt,
>>   				      cb->compressed_len);
>>   csum_failed:
>> -	if (ret)
>> +	if (ret && ret != -ENOKEY)
>>   		cb->errors = 1;
>>
>>   	/* release the compressed pages */
>> @@ -751,6 +752,7 @@ static struct {
>>   static const struct btrfs_compress_op * const btrfs_compress_op[] = {
>>   	&btrfs_zlib_compress,
>>   	&btrfs_lzo_compress,
>> +	&btrfs_encrypt_ops,
>>   };
>>
>>   void __init btrfs_init_compress(void)
>> @@ -780,6 +782,10 @@ static struct list_head *find_workspace(int type)
>>   	atomic_t *alloc_ws		= &btrfs_comp_ws[idx].alloc_ws;
>>   	wait_queue_head_t *ws_wait	= &btrfs_comp_ws[idx].ws_wait;
>>   	int *num_ws			= &btrfs_comp_ws[idx].num_ws;
>> +
>> +	if (type == BTRFS_ENCRYPT_AES)
>> +		return NULL;
>> +
>>   again:
>>   	spin_lock(ws_lock);
>>   	if (!list_empty(idle_ws)) {
>> @@ -824,6 +830,9 @@ static void free_workspace(int type, struct list_head *workspace)
>>   	wait_queue_head_t *ws_wait	= &btrfs_comp_ws[idx].ws_wait;
>>   	int *num_ws			= &btrfs_comp_ws[idx].num_ws;
>>
>> +	if (!workspace)
>> +		return;
>> +
>>   	spin_lock(ws_lock);
>>   	if (*num_ws < num_online_cpus()) {
>>   		list_add(workspace, idle_ws);
>> @@ -862,6 +871,38 @@ static void free_workspaces(void)
>>   	}
>>   }
>>
>> +void print_data_encode_status(struct inode *inode, int direction,
>> +					char *prefix, int type, int ret)
>> +{
>> +#ifdef CONFIG_BTRFS_DEBUG
>> +	char what[10];
>> +
>> +	if (type == BTRFS_ENCRYPT_AES) {
>> +		if (!direction)
>> +			strcpy(what, "Encrypt");
>> +		else
>> +			strcpy(what, "Decrypt");
>> +	} else {
>> +		if (!direction)
>> +			strcpy(what, "Compress");
>> +		else
>> +			strcpy(what, "Uncpress");
>> +	}
>> +
>> +	switch (ret) {
>> +	case 0:
>> +		pr_debug("%s %s: success     : inode %lu\n",what, prefix, inode->i_ino);
>> +		return;
>> +	case -ENOKEY:
>> +		pr_debug("%s %s: Failed NOKEY: inode %lu\n",what, prefix, inode->i_ino);
>> +		return;
>> +	default:
>> +		pr_debug("%s %s: Failed %d   : inode %lu\n",what, prefix, ret, inode->i_ino);
>> +	}
>> +#else
>> +#endif
>> +}
>> +
>>   /*
>>    * given an address space and start/len, compress the bytes.
>>    *
>> @@ -894,7 +935,7 @@ int btrfs_compress_pages(int type, struct address_space *mapping,
>>   	int ret;
>>
>>   	workspace = find_workspace(type);
>> -	if (IS_ERR(workspace))
>> +	if (workspace && IS_ERR(workspace))
>>   		return PTR_ERR(workspace);
>>
>>   	ret = btrfs_compress_op[type-1]->compress_pages(workspace, mapping,
>> @@ -903,6 +944,8 @@ int btrfs_compress_pages(int type, struct address_space *mapping,
>>   						      total_in, total_out,
>>   						      max_out);
>>   	free_workspace(type, workspace);
>> +
>> +	print_data_encode_status(mapping->host, 0, "    ", type, ret);
>>   	return ret;
>>   }
>>
>> @@ -930,13 +973,14 @@ static int btrfs_decompress_biovec(int type, struct page **pages_in,
>>   	int ret;
>>
>>   	workspace = find_workspace(type);
>> -	if (IS_ERR(workspace))
>> +	if (workspace && IS_ERR(workspace))
>>   		return PTR_ERR(workspace);
>>
>>   	ret = btrfs_compress_op[type-1]->decompress_biovec(workspace, pages_in,
>>   							 disk_start,
>>   							 bvec, vcnt, srclen);
>>   	free_workspace(type, workspace);
>> +	print_data_encode_status(bvec->bv_page->mapping->host, 1, "bio ", type, ret);
>>   	return ret;
>>   }
>>
>> @@ -952,7 +996,7 @@ int btrfs_decompress(int type, unsigned char *data_in, struct page *dest_page,
>>   	int ret;
>>
>>   	workspace = find_workspace(type);
>> -	if (IS_ERR(workspace))
>> +	if (workspace && IS_ERR(workspace))
>>   		return PTR_ERR(workspace);
>>
>>   	ret = btrfs_compress_op[type-1]->decompress(workspace, data_in,
>> @@ -960,6 +1004,7 @@ int btrfs_decompress(int type, unsigned char *data_in, struct page *dest_page,
>>   						  srclen, destlen);
>>
>>   	free_workspace(type, workspace);
>> +	print_data_encode_status(dest_page->mapping->host, 1, "page", type, ret);
>>   	return ret;
>>   }
>>
>> diff --git a/fs/btrfs/compression.h b/fs/btrfs/compression.h
>> index 13a4dc0436c9..78e8f38dbf60 100644
>> --- a/fs/btrfs/compression.h
>> +++ b/fs/btrfs/compression.h
>> @@ -79,5 +79,6 @@ struct btrfs_compress_op {
>>
>>   extern const struct btrfs_compress_op btrfs_zlib_compress;
>>   extern const struct btrfs_compress_op btrfs_lzo_compress;
>> +extern const struct btrfs_compress_op btrfs_encrypt_ops;
>>
>>   #endif
>> diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
>> index bfe4a337fb4d..f30a92bf9c54 100644
>> --- a/fs/btrfs/ctree.h
>> +++ b/fs/btrfs/ctree.h
>> @@ -719,8 +719,9 @@ enum btrfs_compression_type {
>>   	BTRFS_COMPRESS_NONE  = 0,
>>   	BTRFS_COMPRESS_ZLIB  = 1,
>>   	BTRFS_COMPRESS_LZO   = 2,
>> -	BTRFS_COMPRESS_TYPES = 2,
>> -	BTRFS_COMPRESS_LAST  = 3,
>> +	BTRFS_ENCRYPT_AES    = 3,
>> +	BTRFS_COMPRESS_TYPES = 3,
>> +	BTRFS_COMPRESS_LAST  = 4,
>>   };
>>
>>   struct btrfs_inode_item {
>> @@ -771,6 +772,7 @@ struct btrfs_dir_item {
>>    * still visible as a directory
>>    */
>>   #define BTRFS_ROOT_SUBVOL_DEAD		(1ULL << 48)
>> +#define BTRFS_ROOT_SUBVOL_ENCRYPT	(1ULL << 49)
>>
>>   struct btrfs_root_item {
>>   	struct btrfs_inode_item inode;
>> @@ -814,7 +816,9 @@ struct btrfs_root_item {
>>   	struct btrfs_timespec otime;
>>   	struct btrfs_timespec stime;
>>   	struct btrfs_timespec rtime;
>> -	__le64 reserved[8]; /* for future */
>> +	char encrypt_algo[16];
>> +	char encrypt_keytag[16];
>> +	__le64 reserved[4]; /* for future */
>>   } __attribute__ ((__packed__));
>>
>>   /*
>> @@ -2344,6 +2348,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/encrypt.c b/fs/btrfs/encrypt.c
>> new file mode 100644
>> index 000000000000..a6838cccc507
>> --- /dev/null
>> +++ b/fs/btrfs/encrypt.c
>> @@ -0,0 +1,544 @@
>> +/*
>> + * Copyright (C) 2016 Oracle.  All rights reserved.
>> + *
>> + * This program is free software; you can redistribute it and/or
>> + * modify it under the terms of the GNU General Public
>> + * License v2 as published by the Free Software Foundation.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
>> + * General Public License for more details.
>> + *
>> + * You should have received a copy of the GNU General Public
>> + * License along with this program; if not, write to the
>> + * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
>> + * Boston, MA 021110-1307, USA.
>> + */
>> +#include <linux/string.h>
>> +#include <linux/crypto.h>
>> +#include <linux/scatterlist.h>
>> +#include <linux/random.h>
>> +#include <linux/pagemap.h>
>> +#include <keys/user-type.h>
>> +#include "compression.h"
>> +#include <linux/slab.h>
>> +#include <linux/keyctl.h>
>> +#include <linux/key-type.h>
>> +#include <linux/cred.h>
>> +#include <keys/user-type.h>
>> +#include "ctree.h"
>> +#include "btrfs_inode.h"
>> +#include "props.h"
>> +
>> +static const struct btrfs_encrypt_algorithm {
>> +	const char *name;
>> +	size_t	keylen;
>> +} btrfs_encrypt_algorithm_supported[] = {
>> +	{"aes", 16}
>> +};
>> +
>> +/*
>> + * Returns cipher alg key size if the encryption type is found
>> + * otherwise 0
>> + */
>> +size_t btrfs_check_encrypt_type(char *type)
>> +{
>> +	int i;
>> +	for (i = 0; i < ARRAY_SIZE(btrfs_encrypt_algorithm_supported); i++)
>> +		if (!strcmp(btrfs_encrypt_algorithm_supported[i].name, type))
>> +			return btrfs_encrypt_algorithm_supported[i].keylen;
>> +
>> +	return 0;
>> +}
>> +
>> +/* key management*/
>> +static int btrfs_request_key(char *key_tag, void *key_data)
>> +{
>> +	int ret;
>> +	const struct user_key_payload *payload;
>> +	struct key *btrfs_key = NULL;
>> +
>> +	ret = 0;
>> +	btrfs_key = request_key(&key_type_user, key_tag, NULL);
>> +	if (IS_ERR(btrfs_key)) {
>> +		ret = PTR_ERR(btrfs_key);
>> +		btrfs_key = NULL;
>> +		return ret;
>> +	}
>> +
>> +	/*
>> +	 * caller just need key not payload so return
>> +	 */
>> +	if (!key_data)
>> +		return 0;
>> +
>> +	ret = key_validate(btrfs_key);
>> +	if (ret < 0)
>> +		goto out;
>> +
>> +	rcu_read_lock(); // TODO: check down_write key->sem ?
>> +	payload = user_key_payload(btrfs_key);
>> +	if (IS_ERR_OR_NULL(payload)) {
>> +		ret = PTR_ERR(payload);
>> +		goto out;
>> +	}
>> +
>> +	/*
>> +	 * As of now we just hard code as we just use ASE now
>> +	 */
>> +	if (payload->datalen != 16)
>> +		ret = -EINVAL;
>> +	else
>> +		memcpy(key_data, payload->data, 16);
>> +
>> +out:
>> +	rcu_read_unlock();
>> +	key_put(btrfs_key);
>> +
>> +	return ret;
>> +}
>> +
>> +static int btrfs_get_key_data_from_inode(struct inode *inode, unsigned char *keydata)
>> +{
>> +	int ret;
>> +	char keytag[15];
>> +	struct btrfs_inode *binode;
>> +	struct btrfs_root_item *ri;
>> +
>> +	binode = BTRFS_I(inode);
>> +	ri = &(binode->root->root_item);
>> +	strncpy(keytag, ri->encrypt_keytag, 14);
>> +	keytag[14] = '\0';
>> +
>> +	ret = btrfs_request_key(keytag, keydata);
>> +	return ret;
>> +}
>> +
>> +int btrfs_update_key_data_to_binode(struct inode *inode)
>> +{
>> +	int ret;
>> +	unsigned char keydata[16];
>> +	struct btrfs_inode *binode;
>> +
>> +	ret = btrfs_get_key_data_from_inode(inode, keydata);
>> +	if (ret)
>> +		return ret;
>> +
>> +	binode = BTRFS_I(inode);
>> +	memcpy(binode->key_payload, keydata, 16);
>> +
>> +	return ret;
>> +}
>> +
>> +int btrfs_get_keytag(struct address_space *mapping, char *keytag, struct inode **inode)
>> +{
>> +	struct btrfs_inode *binode;
>> +	struct btrfs_root_item *ri;
>> +
>> +	if (!mapping)
>> +		return -EINVAL;
>> +
>> +	if (!(mapping->host))
>> +		return -EINVAL;
>> +
>> +	binode = BTRFS_I(mapping->host);
>> +	ri = &(binode->root->root_item);
>> +
>> +	strncpy(keytag, ri->encrypt_keytag, 14);
>> +	keytag[14] = '\0';
>> +	if (inode)
>> +		*inode = &binode->vfs_inode;
>> +
>> +	return 0;
>> +}
>> +
>> +/* Encrypt and decrypt */
>> +struct workspace {
>> +	struct list_head list;
>> +};
>> +
>> +struct btrfs_crypt_result {
>> +	struct completion completion;
>> +	int err;
>> +};
>> +
>> +struct btrfs_ablkcipher_def {
>> +	struct scatterlist sg;
>> +	struct crypto_ablkcipher *tfm;
>> +	struct ablkcipher_request *req;
>> +	struct btrfs_crypt_result result;
>> +};
>> +
>> +int btrfs_do_blkcipher(int enc, char *data, size_t len)
>> +{
>
> This function is not used anywhere.
>
>> +	int ret = -EFAULT;
>> +	struct scatterlist sg;
>> +	unsigned int ivsize = 0;
>> +	char *cipher = "cbc(aes)";
>> +	struct blkcipher_desc desc;
>> +	struct crypto_blkcipher *blkcipher = NULL;
>> +	char *charkey =
>> +		"\x12\x34\x56\x78\x90\xab\xcd\xef\x12\x34\x56\x78\x90\xab\xcd\xef";
>> +	char *chariv =
>> +		"\x12\x34\x56\x78\x90\xab\xcd\xef\x12\x34\x56\x78\x90\xab\xcd\xef";
>> +
>> +	blkcipher = crypto_alloc_blkcipher(cipher, 0, 0);
>> +	if (IS_ERR(blkcipher)) {
>> +		printk("could not allocate blkcipher handle for %s\n", cipher);
>> +		return -PTR_ERR(blkcipher);
>> +	}
>> +
>> +	if (crypto_blkcipher_setkey(blkcipher, charkey, 16)) {
>> +		printk("key could not be set\n");
>> +		ret = -EAGAIN;
>> +		goto out;
>> +	}
>> +
>> +	ivsize = crypto_blkcipher_ivsize(blkcipher);
>> +	if (ivsize) {
>> +		if (ivsize != strlen(chariv)) {
>> +			printk("length differs from expected length\n");
>> +			ret = -EINVAL;
>> +			goto out;
>> +		}
>> +		crypto_blkcipher_set_iv(blkcipher, chariv, ivsize);
>> +	}
>> +
>> +	desc.flags = 0;
>> +	desc.tfm = blkcipher;
>> +	sg_init_one(&sg, data, len);
>> +
>> +	if (enc) {
>> +		/* encrypt data in place */
>> +		ret = crypto_blkcipher_encrypt(&desc, &sg, &sg, len);
>> +	} else {
>> +		/* decrypt data in place */
>> +		ret = crypto_blkcipher_decrypt(&desc, &sg, &sg, len);
>> +	}
>> +
>> +	return ret;
>> +
>> +out:
>> +	crypto_free_blkcipher(blkcipher);
>> +	return ret;
>> +}
>> +
>> +static void btrfs_ablkcipher_cb(struct crypto_async_request *req, int error)
>> +{
>> +	struct btrfs_crypt_result *result;
>> +
>> +	if (error == -EINPROGRESS) {
>> +		pr_info("Encryption callback reports error\n");
>> +		return;
>> +	}
>> +
>> +	result = req->data;
>> +	result->err = error;
>> +	complete(&result->completion);
>> +	pr_info("Encryption finished successfully\n");
>> +}
>> +
>> +static unsigned int btrfs_ablkcipher_encdec(struct btrfs_ablkcipher_def *ablk, int enc)
>> +{
>> +	int rc = 0;
>> +
>> +	if (enc)
>> +		rc = crypto_ablkcipher_encrypt(ablk->req);
>> +	else
>> +		rc = crypto_ablkcipher_decrypt(ablk->req);
>> +
>> +	switch (rc) {
>> +	case 0:
>> +		break;
>> +	case -EINPROGRESS:
>> +	case -EBUSY:
>> +		rc = wait_for_completion_interruptible(
>> +			&ablk->result.completion);
>> +		if (!rc && !ablk->result.err) {
>> +			reinit_completion(&ablk->result.completion);
>> +			break;
>> +		}
>> +	default:
>> +		pr_info("ablkcipher encrypt returned with %d result %d\n",
>> +		       rc, ablk->result.err);
>> +		break;
>> +	}
>> +	init_completion(&ablk->result.completion);
>> +
>> +	return rc;
>> +}
>> +
>> +void btrfs_cipher_get_ivdata(char **ivdata, unsigned int ivsize, unsigned int *ivdata_size)
>> +{
>> +	/* fixme */
>> +	if (0) {
>> +		*ivdata = kmalloc(ivsize, GFP_KERNEL);
>> +		get_random_bytes(ivdata, ivsize);
>> +		*ivdata_size = ivsize;
>> +	} else {
>> +		*ivdata = kstrdup(
>> +			"\x12\x34\x56\x78\x90\xab\xcd\xef\x12\x34\x56\x78\x90\xab\xcd\xef",
>> +			GFP_NOFS);
>> +		*ivdata_size = strlen(*ivdata);
>> +	}
>> +}
>> +
>> +static int btrfs_do_ablkcipher(int endec, struct page *page, unsigned long len,
>> +							struct inode *inode)
>> +{
>> +	int ret = -EFAULT;
>> +	unsigned char key_data[16];
>> +	char *ivdata = NULL;
>> +	unsigned int key_size;
>> +	unsigned int ivsize = 0;
>> +	unsigned int ivdata_size;
>> +	unsigned int ablksize = 0;
>> +	struct btrfs_ablkcipher_def ablk_akin;
>> +	struct ablkcipher_request *req = NULL;
>> +	struct crypto_ablkcipher *ablkcipher = NULL;
>> +
>> +	ret = 0;
>> +
>> +	if (!inode) {
>> +		BUG_ON("Need inode\n");
>> +		return -EINVAL;
>> +	}
>> +	/* get key from the inode */
>> +	ret = btrfs_get_key_data_from_inode(inode, key_data);
>> +
>> +	key_size = 16; //todo: defines, but review for suitable cipher
>> +
>> +	ablkcipher = crypto_alloc_ablkcipher("cts(cbc(aes))", 0, 0);
>> +	if (IS_ERR(ablkcipher)) {
>> +		pr_info("could not allocate ablkcipher handle\n");
>> +		return PTR_ERR(ablkcipher);
>> +	}
>> +
>> +	ablksize = crypto_ablkcipher_blocksize(ablkcipher);
>> +	/* we can't cipher a block less the ciper block size */
>> +	if (len < ablksize || len > PAGE_CACHE_SIZE) {
>> +		ret = -EINVAL;
>> +		goto out;
>> +	}
>> +
>> +	ivsize = crypto_ablkcipher_ivsize(ablkcipher);
>> +	if (ivsize) {
>> +		btrfs_cipher_get_ivdata(&ivdata, ivsize, &ivdata_size);
>> +		if (ivsize != ivdata_size) {
>> +			BUG_ON("IV length differs from expected length\n");
>> +			ret = -EINVAL;
>> +			goto out;
>> +		}
>> +	} else {
>> +		BUG_ON("This cipher doesn't need ivdata, but are we ready ?\n");
>> +		ret = -EINVAL;
>> +		goto out;
>> +	}
>> +
>> +	req = ablkcipher_request_alloc(ablkcipher, GFP_KERNEL);
>> +	if (IS_ERR(req)) {
>> +		pr_info("could not allocate request queue\n");
>> +		ret = PTR_ERR(req);
>> +		goto out;
>> +	}
>> +
>> +	ablkcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG,
>> +					btrfs_ablkcipher_cb, &ablk_akin.result);
>> +
>> +	if (crypto_ablkcipher_setkey(ablkcipher, key_data, key_size)) {
>> +		printk("key could not be set\n");
>> +		ret = -EAGAIN;
>> +		goto out;
>> +	}
>> +
>> +	ablk_akin.tfm = ablkcipher;
>> +	ablk_akin.req = req;
>> +
>> +	sg_init_table(&ablk_akin.sg, 1);
>> +	sg_set_page(&ablk_akin.sg, page, len, 0);
>> +	ablkcipher_request_set_crypt(req, &ablk_akin.sg, &ablk_akin.sg, len, ivdata);
>
> Are you sure it is OK to use the same page for src and dst scatterlist?
>
> I don't think it's supposed to be used this way.
>
>> +
>> +	init_completion(&ablk_akin.result.completion);
>> +
>> +	ret = btrfs_ablkcipher_encdec(&ablk_akin, endec);
>> +
>> +out:
>> +	if (ablkcipher)
>> +		crypto_free_ablkcipher(ablkcipher);
>
> req->base.tfm still points to a field in ablkcipher.
>
>> +	if (req)
>> +		ablkcipher_request_free(req);
>> +
>> +	kfree(ivdata);
>> +
>> +	return ret;
>> +}
>> +
>> +static int btrfs_encrypt_pages(struct list_head *na_ws, struct address_space *mapping,
>> +			u64 start, unsigned long len, struct page **pages,
>> +			unsigned long nr_pages, unsigned long *na_out_pages,
>> +			unsigned long *na_total_in, unsigned long *na_total_out,
>> +			unsigned long na_max_out)
>> +{
>> +	int ret;
>> +	struct page *in_page;
>> +	struct page *out_page;
>> +	char *in;
>> +	char *out;
>> +	unsigned long bytes_left = len;
>> +	unsigned long cur_page_len;
>> +	unsigned long cur_page;
>> +	struct inode *inode;
>> +
>> +	*na_total_in = 0;
>> +	*na_out_pages = 0;
>> +
>> +	if (!mapping && !mapping->host) {
>> +		WARN_ON("Need mapped pages\n");
>> +		return -EINVAL;
>> +	}
>> +
>> +	inode = mapping->host;
>> +
>> +	for (cur_page = 0; cur_page < nr_pages; cur_page++) {
>> +
>> +		WARN_ON(!bytes_left);
>> +
>> +		in_page = find_get_page(mapping, start >> PAGE_CACHE_SHIFT);
>> +		out_page = alloc_page(GFP_NOFS| __GFP_HIGHMEM);
>> +		cur_page_len = min(bytes_left, PAGE_CACHE_SIZE);
>> +
>> +		in = kmap(in_page);
>> +		out = kmap(out_page);
>> +		memcpy(out, in, cur_page_len);
>> +		kunmap(out_page);
>> +		kunmap(in_page);
>> +
>> +		ret = btrfs_do_ablkcipher(1, out_page, cur_page_len, inode);
>> +		if (ret) {
>> +			__free_page(out_page);
>> +			return ret;
>> +		}
>> +
>> +		pages[cur_page] = out_page;
>> +		*na_out_pages = *na_out_pages + 1;
>> +		*na_total_in = *na_total_in + cur_page_len;
>> +
>> +		start += cur_page_len;
>> +		bytes_left = bytes_left - cur_page_len;
>> +	}
>> +
>> +	return ret;
>> +}
>> +
>> +static int btrfs_decrypt_pages(struct list_head *na_ws, unsigned char *in, struct page *out_page,
>> +				unsigned long na_start_byte, size_t in_size, size_t out_size)
>> +{
>> +	int ret;
>> +	char *out_addr;
>> +	char keytag[24];
>> +	struct address_space *mapping;
>> +	struct inode *inode;
>> +
>> +	if (!out_page)
>> +		return -EINVAL;
>> +
>> +	if (in_size > PAGE_CACHE_SIZE)
>> +		return -EINVAL;
>> +
>> +	memset(keytag, '\0', 24);
>> +
>> +	mapping = out_page->mapping;
>> +	if (!mapping && !mapping->host) {
>> +		WARN_ON("Need mapped pages\n");
>> +		return -EINVAL;
>> +	}
>> +
>> +	inode = mapping->host;
>> +
>> +	out_addr = kmap(out_page);
>> +	memcpy(out_addr, in, in_size);
>> +	kunmap(out_page);
>> +
>> +	ret = btrfs_do_ablkcipher(0, out_page, in_size, inode);
>> +
>> +	return ret;
>> +}
>> +
>> +static int btrfs_decrypt_pages_bio(struct list_head *na_ws, struct page **in_pages,
>> +					u64 in_start_offset, struct bio_vec *out_pages_bio,
>> +					int bi_vcnt, size_t in_len)
>> +{
>> +	char *in;
>> +	char *out;
>> +	int ret = 0;
>> +	struct page *in_page;
>> +	struct page *out_page;
>> +	unsigned long cur_page_n;
>> +	unsigned long bytes_left;
>> +	unsigned long in_nr_pages;
>> +	unsigned long cur_page_len;
>> +	unsigned long processed_len = 0;
>> +	struct address_space *mapping;
>> +	struct inode *inode;
>> +
>> +	if (na_ws)
>> +		return -EINVAL;
>> +
>> +	out_page = out_pages_bio[0].bv_page;
>> +	mapping = out_page->mapping;
>> +	if (!mapping && !mapping->host) {
>> +		WARN_ON("Need mapped page\n");
>> +		return -EINVAL;
>> +	}
>> +
>> +	inode = mapping->host;
>> +
>> +	in_nr_pages = DIV_ROUND_UP(in_len, PAGE_CACHE_SIZE);
>> +	bytes_left = in_len;
>> +	WARN_ON(in_nr_pages != bi_vcnt);
>> +
>> +	for (cur_page_n = 0; cur_page_n < in_nr_pages; cur_page_n++) {
>> +		WARN_ON(!bytes_left);
>> +
>> +		in_page = in_pages[cur_page_n];
>> +		out_page = out_pages_bio[cur_page_n].bv_page;
>> +
>> +		cur_page_len = min(bytes_left, PAGE_CACHE_SIZE);
>> +
>> +		in = kmap(in_page);
>> +		out = kmap(out_page);
>> +		memcpy(out, in, cur_page_len);
>> +		kunmap(out_page);
>> +		kunmap(in_page);
>> +
>> +		ret = btrfs_do_ablkcipher(0, out_page, cur_page_len, inode);
>> +
>> +		if (ret && ret != -ENOKEY)
>> +			goto error_out;
>> +
>> +		if (cur_page_len < PAGE_CACHE_SIZE) {
>> +			out = kmap(out_page);
>> +			memset(out + cur_page_len, 0, PAGE_CACHE_SIZE - cur_page_len);
>> +			kunmap(out_page);
>> +		}
>> +
>> +		bytes_left = bytes_left - cur_page_len;
>> +		processed_len = processed_len + cur_page_len;
>> +
>> +		//flush_dcache_page(out_page);
>> +	}
>> +	WARN_ON(processed_len != in_len);
>> +	WARN_ON(bytes_left);
>> +
>> +error_out:
>> +	return ret;
>> +}
>> +
>> +const struct btrfs_compress_op btrfs_encrypt_ops = {
>> +	.alloc_workspace	= NULL,
>> +	.free_workspace		= NULL,
>> +	.compress_pages		= btrfs_encrypt_pages,
>> +	.decompress_biovec	= btrfs_decrypt_pages_bio,
>> +	.decompress		= btrfs_decrypt_pages,
>> +};
>> diff --git a/fs/btrfs/encrypt.h b/fs/btrfs/encrypt.h
>> new file mode 100644
>> index 000000000000..36a7067e98b1
>> --- /dev/null
>> +++ b/fs/btrfs/encrypt.h
>> @@ -0,0 +1,21 @@
>> +/*
>> + * Copyright (C) 2016 Oracle.  All rights reserved.
>> + *
>> + * This program is free software; you can redistribute it and/or
>> + * modify it under the terms of the GNU General Public
>> + * License v2 as published by the Free Software Foundation.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
>> + * General Public License for more details.
>> + *
>> + * You should have received a copy of the GNU General Public
>> + * License along with this program; if not, write to the
>> + * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
>> + * Boston, MA 021110-1307, USA.
>> + */
>> +
>> +
>> +size_t btrfs_check_encrypt_type(char *encryption_type);
>> +int btrfs_update_key_data_to_binode(struct inode *inode);
>> diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
>> index 151b7c71b868..b27a89d89753 100644
>> --- a/fs/btrfs/inode.c
>> +++ b/fs/btrfs/inode.c
>> @@ -60,6 +60,7 @@
>>   #include "hash.h"
>>   #include "props.h"
>>   #include "qgroup.h"
>> +#include "encrypt.h"
>>
>>   struct btrfs_iget_args {
>>   	struct btrfs_key *location;
>> @@ -206,6 +207,8 @@ static int insert_inline_extent(struct btrfs_trans_handle *trans,
>>   		}
>>   		btrfs_set_file_extent_compression(leaf, ei,
>>   						  compress_type);
>> +		if (compress_type == BTRFS_ENCRYPT_AES)
>> +			btrfs_set_file_extent_encryption(leaf, ei, 1);
>
> Looks like decrypt is not yet used in read_page path, I only see btrfs_set_file_extent_encryption.
>
>>   	} else {
>>   		page = find_get_page(inode->i_mapping,
>>   				     start >> PAGE_CACHE_SHIFT);
>> @@ -581,7 +584,7 @@ cont:
>>   		 * win, compare the page count read with the blocks on disk
>>   		 */
>>   		total_in = ALIGN(total_in, PAGE_CACHE_SIZE);
>> -		if (total_compressed >= total_in) {
>> +		if (total_compressed >= total_in && compress_type != BTRFS_ENCRYPT_AES) {
>>   			will_compress = 0;
>>   		} else {
>>   			num_bytes = total_in;
>> @@ -6704,6 +6707,8 @@ static noinline int uncompress_inline(struct btrfs_path *path,
>>   	max_size = min_t(unsigned long, PAGE_CACHE_SIZE, max_size);
>>   	ret = btrfs_decompress(compress_type, tmp, page,
>>   			       extent_offset, inline_size, max_size);
>> +	if (ret && ret == -ENOKEY)
>> +		ret = 0;
>>   	kfree(tmp);
>>   	return ret;
>>   }
>> @@ -9271,6 +9276,20 @@ static int btrfs_rename(struct inode *old_dir, struct dentry *old_dentry,
>>   	u64 root_objectid;
>>   	int ret;
>>   	u64 old_ino = btrfs_ino(old_inode);
>> +	u64 root_flags;
>> +	u64 dest_flags;
>> +
>> +	/*
>> +	 * As of now block an encrypted file/dir to move across
>> +	 * subvol which potentially has different key.
>> +	 */
>> +	root_flags = btrfs_root_flags(&root->root_item);
>> +	dest_flags = btrfs_root_flags(&dest->root_item);
>> +	if (root != dest &&
>> +		((root_flags & BTRFS_ROOT_SUBVOL_ENCRYPT) ||
>> +		(dest_flags & BTRFS_ROOT_SUBVOL_ENCRYPT))) {
>> +		return -EOPNOTSUPP;
>> +	}
>>
>>   	if (btrfs_ino(new_dir) == BTRFS_EMPTY_SUBVOL_DIR_OBJECTID)
>>   		return -EPERM;
>> @@ -9931,6 +9950,22 @@ static int btrfs_permission(struct inode *inode, int mask)
>>   		if (BTRFS_I(inode)->flags & BTRFS_INODE_READONLY)
>>   			return -EACCES;
>>   	}
>> +
>> +	/*
>> +	 * Get the required key as we encrypt only file data, this
>> +	 * this applies only files as of now.
>
> Double 'this'.
>
>> +	 */
>> +	if (S_ISREG(mode)) {
>> +		int ret = 0;
>> +		u64 root_flags;
>> +		root_flags = btrfs_root_flags(&root->root_item);
>> +		if (root_flags & BTRFS_ROOT_SUBVOL_ENCRYPT) {
>> +			ret = btrfs_update_key_data_to_binode(inode);
>> +			if (ret)
>> +				return -ENOKEY;
>> +		}
>> +	}
>> +
>>   	return generic_permission(inode, mask);
>>   }
>>
>> diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
>> index 48aee9846329..3a0f40e4a713 100644
>> --- a/fs/btrfs/ioctl.c
>> +++ b/fs/btrfs/ioctl.c
>> @@ -2139,8 +2139,15 @@ static noinline int btrfs_ioctl_tree_search(struct file *file,
>>   	int ret;
>>   	size_t buf_size;
>>
>> +#if 0
>> +	/*
>> +	 * Todo: Workaround as of now instead of introduing a new ioctl,
>> +	 * so that non root user can find info about the subvol
>> +	 * they own/create. This must be fixed in final.
>> +	 */
>>   	if (!capable(CAP_SYS_ADMIN))
>>   		return -EPERM;
>> +#endif
>>
>>   	uargs = (struct btrfs_ioctl_search_args __user *)argp;
>>
>> diff --git a/fs/btrfs/props.c b/fs/btrfs/props.c
>> index f9e60231f685..d40ace5f5492 100644
>> --- a/fs/btrfs/props.c
>> +++ b/fs/btrfs/props.c
>> @@ -22,10 +22,16 @@
>>   #include "hash.h"
>>   #include "transaction.h"
>>   #include "xattr.h"
>> +#include "encrypt.h"
>>
>>   #define BTRFS_PROP_HANDLERS_HT_BITS 8
>>   static DEFINE_HASHTABLE(prop_handlers_ht, BTRFS_PROP_HANDLERS_HT_BITS);
>>
>> +#define BTRFS_PROP_INHERIT_NONE		(1U << 0)
>> +#define BTRFS_PROP_INHERIT_FOR_DIR	(1U << 1)
>> +#define BTRFS_PROP_INHERIT_FOR_CLONE	(1U << 2)
>> +#define BTRFS_PROP_INHERIT_FOR_SUBVOL	(1U << 3)
>> +
>>   struct prop_handler {
>>   	struct hlist_node node;
>>   	const char *xattr_name;
>> @@ -41,13 +47,28 @@ static int prop_compression_apply(struct inode *inode,
>>   				  size_t len);
>>   static const char *prop_compression_extract(struct inode *inode);
>>
>> +static int prop_encrypt_validate(const char *value, size_t len);
>> +static int prop_encrypt_apply(struct inode *inode,
>> +				  const char *value, size_t len);
>> +static const char *prop_encrypt_extract(struct inode *inode);
>> +
>>   static struct prop_handler prop_handlers[] = {
>>   	{
>>   		.xattr_name = XATTR_BTRFS_PREFIX "compression",
>>   		.validate = prop_compression_validate,
>>   		.apply = prop_compression_apply,
>>   		.extract = prop_compression_extract,
>> -		.inheritable = 1
>> +		.inheritable = BTRFS_PROP_INHERIT_FOR_DIR| \
>> +				BTRFS_PROP_INHERIT_FOR_CLONE| \
>> +				BTRFS_PROP_INHERIT_FOR_SUBVOL,
>> +	},
>> +	{
>> +		.xattr_name = XATTR_BTRFS_PREFIX "encrypt",
>> +		.validate = prop_encrypt_validate,
>> +		.apply = prop_encrypt_apply,
>> +		.extract = prop_encrypt_extract,
>> +		.inheritable = BTRFS_PROP_INHERIT_FOR_DIR| \
>> +				BTRFS_PROP_INHERIT_FOR_CLONE,
>>   	},
>>   };
>>
>> @@ -315,6 +336,13 @@ static int inherit_props(struct btrfs_trans_handle *trans,
>>   		if (!h->inheritable)
>>   			continue;
>>
>> +		//is_subvolume_inode(); ?
>> +		if (btrfs_ino(inode) == BTRFS_FIRST_FREE_OBJECTID) {
>> +			if (!strcmp(h->xattr_name, "btrfs.encrypt")) {
>> +				continue;
>> +			}
>> +		}
>> +
>>   		value = h->extract(parent);
>>   		if (!value)
>>   			continue;
>> @@ -425,4 +453,114 @@ static const char *prop_compression_extract(struct inode *inode)
>>   	return NULL;
>>   }
>>
>> +static int btrfs_create_encrypt_key_tuplet(char *algo, char *tag, char *val_out)
>> +{
>> +	return snprintf(val_out, 32, "%s@%s", algo, tag);
>> +}
>> +
>> +static int btrfs_split_key_tuplet(const char *val, size_t len,
>> +					char *keyalgo, char *keytag)
>> +{
>> +	char *tmp;
>> +	char *tmp1;
>> +	char *tmp2;
>> +
>> +	tmp1 = tmp = kstrdup(val, GFP_NOFS);
>> +	tmp[len] = '\0';
>> +	tmp2 = strsep(&tmp, "@");
>> +	if (!tmp2) {
>> +		kfree(tmp1);
>> +		return -EINVAL;
>> +	}
>> +
>> +	if (strlen(tmp2) > 16 || strlen(tmp) > 16) {
>> +		kfree(tmp1);
>
> tmp2 needs to be freed too.
>
>> +		return -EINVAL;
>> +	}
>> +	strcpy(keyalgo, tmp2);
>> +	strcpy(keytag, tmp);
>
> tmp1 and tmp2 need to be freed.
>
>> +
>> +	return 0;
>> +}
>> +
>> +/*
>> + * The required foramt in the value is <encrypt_algo>@<key_tag>
>> + * eg: btrfs.encrypt="aes@btrfs:61e0d004"
>> + */
>> +static int prop_encrypt_validate(const char *value, size_t len)
>> +{
>> +	int ret;
>> +	char keytag[16];
>> +	char keyalgo[16];
>> +	size_t keylen;
>> +
>> +	if (!len)
>> +		return 0;
>> +
>> +	ret = btrfs_split_key_tuplet(value, len, keyalgo, keytag);
>> +	if (ret)
>> +		return ret;
>>
>> +	keylen = btrfs_check_encrypt_type(keyalgo);
>> +	if (!keylen)
>> +		return -ENOTSUPP;
>> +
>> +	return ret;
>> +}
>> +
>> +static int prop_encrypt_apply(struct inode *inode,
>> +				const char *value, size_t len)
>> +{
>> +	int ret;
>> +	u64 root_flags;
>> +	char keytag[16];
>> +	char keyalgo[16];
>> +	struct btrfs_root_item *root_item;
>> +
>> +	root_item = &(BTRFS_I(inode)->root->root_item);
>> +
>> +	if (len == 0) {
>> +		BTRFS_I(inode)->flags &= ~BTRFS_INODE_ENCRYPT;
>> +		BTRFS_I(inode)->force_compress = 0;
>> +
>> +		if (btrfs_ino(inode) == BTRFS_FIRST_FREE_OBJECTID) {
>> +			root_flags = btrfs_root_flags(root_item);
>> +			btrfs_set_root_flags(root_item, root_flags | ~BTRFS_ROOT_SUBVOL_ENCRYPT);
>> +			memset(root_item->encrypt_algo, '\0', 16);
>> +			memset(root_item->encrypt_keytag, '\0', 16);
>> +		}
>> +		return 0;
>> +	}
>> +
>> +	BTRFS_I(inode)->flags |= BTRFS_INODE_ENCRYPT;
>> +	BTRFS_I(inode)->force_compress = BTRFS_ENCRYPT_AES;
>> +
>> +	ret = btrfs_split_key_tuplet(value, len, keyalgo, keytag);
>> +	if (ret)
>> +		return ret;
>> +
>> +	/* do it only for the subvol or snapshot */
>> +	if (btrfs_ino(inode) == BTRFS_FIRST_FREE_OBJECTID) {
>> +		root_flags = btrfs_root_flags(root_item);
>> +		btrfs_set_root_flags(root_item, root_flags | BTRFS_ROOT_SUBVOL_ENCRYPT);
>> +		/* TODO: this is not right, fix it */
>> +		strncpy(root_item->encrypt_algo, keyalgo, 16);
>> +		strncpy(root_item->encrypt_keytag, keytag, 16);
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +static const char *prop_encrypt_extract(struct inode *inode)
>> +{
>> +	int ret;
>> +	char val[32];
>> +	struct btrfs_root_item *ri;
>> +
>> +	ri = &(BTRFS_I(inode)->root->root_item);
>> +
>> +	ret = btrfs_create_encrypt_key_tuplet(ri->encrypt_algo,
>> +					ri->encrypt_keytag, val);
>> +
>> +	return kstrdup(val, GFP_NOFS);
>
> A pointer is returned here, but in
>
> 	value = h->extract();
>
> value is not be freed by the caller.
>
> Thanks,
>
> -liubo
>
>> +}
>> diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c
>> index d41e09fe8e38..400225890f5f 100644
>> --- a/fs/btrfs/super.c
>> +++ b/fs/btrfs/super.c
>> @@ -59,10 +59,10 @@
>>   #include "free-space-cache.h"
>>   #include "backref.h"
>>   #include "tests/btrfs-tests.h"
>> -
>>   #include "qgroup.h"
>>   #define CREATE_TRACE_POINTS
>>   #include <trace/events/btrfs.h>
>> +#include "encrypt.h"
>>
>>   static const struct super_operations btrfs_super_ops;
>>   static struct file_system_type btrfs_fs_type;
>> @@ -92,6 +92,9 @@ const char *btrfs_decode_error(int errno)
>>   	case -ENOENT:
>>   		errstr = "No such entry";
>>   		break;
>> +	case -ENOKEY:
>> +		errstr = "Required key not available";
>> +		break;
>>   	}
>>
>>   	return errstr;
>> --
>> 2.7.0
>>
>> --
>> To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in
>> the body of a message to majordomo@vger.kernel.org
>> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> --
> To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>

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

end of thread, other threads:[~2016-05-06  9:21 UTC | newest]

Thread overview: 26+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-03-01 16:08 [RFC] Experimental btrfs encryption Anand Jain
2016-03-01 16:08 ` [RFC PATCH 1/1] btrfs: Encryption: Add btrfs encryption support Anand Jain
2016-03-10  2:19   ` Liu Bo
2016-05-06  9:21     ` Anand Jain
2016-03-01 16:08 ` [RFC PATCH 1/2] btrfs-progs: subvolume functions reorg Anand Jain
2016-03-01 16:08 ` [RFC PATCH 2/2] btrfs-progs: Encryption: add encrypt sub cli Anand Jain
2016-03-01 16:29 ` [RFC] Experimental btrfs encryption Tomasz Torcz
2016-03-01 16:46   ` Chris Mason
2016-03-01 17:56     ` Austin S. Hemmelgarn
2016-03-01 17:59     ` Christoph Hellwig
2016-03-01 18:23       ` Chris Mason
2016-03-02  4:48         ` Anand Jain
2016-03-04 12:30           ` Austin S. Hemmelgarn
2016-03-01 16:41 ` Austin S. Hemmelgarn
2016-03-02  1:44   ` Qu Wenruo
2016-03-02  8:50     ` Anand Jain
2016-03-03  1:12       ` Qu Wenruo
2016-03-02  7:07   ` Anand Jain
2016-03-02  1:06 ` Qu Wenruo
2016-03-02  9:09   ` Anand Jain
2016-03-03  1:26     ` Qu Wenruo
2016-03-03 10:17   ` Alex Elsayed
2016-03-04  2:52     ` Anand Jain
2016-03-20 11:56   ` Martin Steigerwald
2016-03-03  1:58 ` Anand Jain
2016-03-22 14:25   ` David Sterba

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.