From: Anand Jain <anand.jain@oracle.com>
To: bo.li.liu@oracle.com
Cc: linux-btrfs@vger.kernel.org, clm@fb.com, dsterba@suse.cz
Subject: Re: [RFC PATCH 1/1] btrfs: Encryption: Add btrfs encryption support
Date: Fri, 6 May 2016 17:21:17 +0800 [thread overview]
Message-ID: <572C620D.3050003@oracle.com> (raw)
In-Reply-To: <20160310021959.GC15791@localhost.localdomain>
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
>
next prev parent reply other threads:[~2016-05-06 9:21 UTC|newest]
Thread overview: 26+ messages / expand[flat|nested] mbox.gz Atom feed top
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 [this message]
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
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=572C620D.3050003@oracle.com \
--to=anand.jain@oracle.com \
--cc=bo.li.liu@oracle.com \
--cc=clm@fb.com \
--cc=dsterba@suse.cz \
--cc=linux-btrfs@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).