Signed-off-by: Mark Harmstone <[email protected]>
---
 fs/btrfs/ctree.h      |  6 +++
 fs/btrfs/encryption.c | 37 +++++++++++++++++
 fs/btrfs/encryption.h |  3 ++
 fs/btrfs/extent_io.c  | 96 ++++++++++++++++++++++++++++++++++++++++---
 fs/btrfs/extent_io.h  |  2 +
 fs/btrfs/extent_map.c | 16 +++++++-
 fs/btrfs/extent_map.h |  5 +++
 fs/btrfs/file-item.c  | 15 ++++++-
 fs/btrfs/inode.c      | 10 ++++-
 fs/btrfs/volumes.h    |  2 +
 10 files changed, 184 insertions(+), 8 deletions(-)

diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
index 51fcc24047f8..4c5b8e8a8580 100644
--- a/fs/btrfs/ctree.h
+++ b/fs/btrfs/ctree.h
@@ -2420,6 +2420,12 @@ BTRFS_SETGET_FUNCS(file_extent_encryption, struct 
btrfs_file_extent_item,
 BTRFS_SETGET_FUNCS(file_extent_other_encoding, struct btrfs_file_extent_item,
                   other_encoding, 16);
 
+BTRFS_SETGET_FUNCS(file_extent_enc_key_number,
+                  struct btrfs_file_extent_item_enc, key_number, 64);
+
+BTRFS_SETGET_FUNCS(file_extent_inline_enc_key_number,
+                  struct btrfs_file_extent_inline_enc, key_number, 64);
+
 #define BTRFS_ENCRYPTION_KEY_ID_LENGTH 64
 
 struct btrfs_encryption_key_item {
diff --git a/fs/btrfs/encryption.c b/fs/btrfs/encryption.c
index 81313c4378b4..41c001339cc7 100644
--- a/fs/btrfs/encryption.c
+++ b/fs/btrfs/encryption.c
@@ -312,6 +312,43 @@ int btrfs_decrypt(struct btrfs_fs_info *fs_info,
        return ret;
 }
 
+int btrfs_decrypt_page(struct btrfs_fs_info *fs_info, struct page *page,
+                      u64 key_number, char *iv)
+{
+       struct scatterlist sg;
+       struct skcipher_request *req = NULL;
+       char *kaddr;
+       int ret = -EFAULT;
+       struct btrfs_enc_key *key;
+
+       ret = find_key(fs_info, key_number, &key);
+       if (ret)
+               return ret;
+
+       req = skcipher_request_alloc(key->skcipher, GFP_KERNEL);
+       if (!req) {
+               pr_info("could not allocate skcipher request\n");
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       kaddr = kmap_atomic(page);
+
+       sg_init_one(&sg, kaddr, PAGE_SIZE);
+       skcipher_request_set_crypt(req, &sg, &sg, PAGE_SIZE, iv);
+
+       ret = crypto_skcipher_decrypt(req);
+
+       kunmap_atomic(kaddr);
+
+       if (ret < 0)
+               goto out;
+
+out:
+       if (req)
+               skcipher_request_free(req);
+       return ret;
+}
 
 int btrfs_get_key_id(u64 salt, char *password, unsigned int pwdlen,
                     char *key_id)
diff --git a/fs/btrfs/encryption.h b/fs/btrfs/encryption.h
index dbc035a880a5..0d24dc51793c 100644
--- a/fs/btrfs/encryption.h
+++ b/fs/btrfs/encryption.h
@@ -33,6 +33,9 @@ struct btrfs_enc_key {
 int btrfs_decrypt(struct btrfs_fs_info *fs_info,
                  unsigned char *data, size_t len);
 
+int btrfs_decrypt_page(struct btrfs_fs_info *fs_info, struct page *page,
+                      u64 key_number, char *iv);
+
 int btrfs_get_key_id(u64 salt, char *password, unsigned int pwdlen,
                     char *key_id);
 
diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c
index d228f706ff3e..73fb0af50da8 100644
--- a/fs/btrfs/extent_io.c
+++ b/fs/btrfs/extent_io.c
@@ -23,6 +23,7 @@
 #include "rcu-string.h"
 #include "backref.h"
 #include "disk-io.h"
+#include "encryption.h"
 
 static struct kmem_cache *extent_state_cache;
 static struct kmem_cache *extent_buffer_cache;
@@ -2490,6 +2491,26 @@ endio_readpage_release_extent(struct extent_io_tree 
*tree, u64 start, u64 len,
        unlock_extent_cached_atomic(tree, start, end, &cached);
 }
 
+void btrfs_increment_iv(char *iv, u32 inc)
+{
+       u64 i1, ni1, i2;
+
+       if (inc == 0)
+               return;
+
+       i1 = be64_to_cpu(*(u64 *)(iv + sizeof(u64)));
+       ni1 = i1 + inc;
+
+       *(u64 *)(iv + sizeof(u64)) = cpu_to_be64(ni1);
+
+       if (ni1 > i1)
+               return;
+
+       i2 = be64_to_cpu(*(u64 *)iv);
+       i2++;
+       *(u64 *)iv = cpu_to_be64(i2);
+}
+
 /*
  * after a readpage IO is done, we need to:
  * clear the uptodate bits on error
@@ -2606,11 +2627,32 @@ static void end_bio_extent_readpage(struct bio *bio)
                        pgoff_t end_index = i_size >> PAGE_SHIFT;
                        unsigned off;
 
+                       if (io_bio->key_number != 0) {
+                               char iv[BTRFS_ENCRYPTION_BLOCK_LENGTH];
+
+                               memcpy(iv, io_bio->iv,
+                                      BTRFS_ENCRYPTION_BLOCK_LENGTH);
+                               btrfs_increment_iv(iv,
+                               i * PAGE_SIZE / BTRFS_ENCRYPTION_BLOCK_LENGTH);
+
+                               ret = btrfs_decrypt_page(fs_info, page,
+                                                  io_bio->key_number, iv);
+                       } else {
+                               ret = 0;
+                       }
+
                        /* Zero out the end if this page straddles i_size */
                        off = i_size & (PAGE_SIZE-1);
                        if (page->index == end_index && off)
                                zero_user_segment(page, off, PAGE_SIZE);
-                       SetPageUptodate(page);
+
+                       if (ret) {
+                               uptodate = 0;
+                               ClearPageUptodate(page);
+                               SetPageError(page);
+                       } else {
+                               SetPageUptodate(page);
+                       }
                } else {
                        ClearPageUptodate(page);
                        SetPageError(page);
@@ -2752,6 +2794,8 @@ static int __must_check submit_one_bio(struct bio *bio, 
int mirror_num,
  * @mirror_num:             desired mirror to read/write
  * @prev_bio_flags:  flags of previous bio to see if we can merge the current 
one
  * @bio_flags: flags of the current bio to see if we can merge them
+ * @key_number:        number of the encryption key (0 if not encrypted)
+ * @iv:                encryption initialization vector
  */
 static int submit_extent_page(unsigned int opf, struct extent_io_tree *tree,
                              struct writeback_control *wbc,
@@ -2763,7 +2807,8 @@ static int submit_extent_page(unsigned int opf, struct 
extent_io_tree *tree,
                              int mirror_num,
                              unsigned long prev_bio_flags,
                              unsigned long bio_flags,
-                             bool force_bio_submit)
+                             bool force_bio_submit, u64 key_number,
+                             char *iv)
 {
        int ret = 0;
        struct bio *bio;
@@ -2786,6 +2831,25 @@ static int submit_extent_page(unsigned int opf, struct 
extent_io_tree *tree,
                                                      bio, bio_flags))
                        can_merge = false;
 
+               if (prev_bio_flags == bio_flags && contig &&
+                       can_merge && !force_bio_submit) {
+                       struct btrfs_io_bio *io_bio = btrfs_io_bio(bio);
+
+                       if (key_number != io_bio->key_number) {
+                               can_merge = false;
+                       } else if (key_number != 0) {
+                               char iv2[BTRFS_ENCRYPTION_BLOCK_LENGTH];
+
+                               memcpy(iv2, io_bio->iv,
+                                      BTRFS_ENCRYPTION_BLOCK_LENGTH);
+                               btrfs_increment_iv(iv2, bio->bi_iter.bi_size /
+                                       BTRFS_ENCRYPTION_BLOCK_LENGTH);
+
+                               can_merge = !memcmp(iv, iv2,
+                                                BTRFS_ENCRYPTION_BLOCK_LENGTH);
+                       }
+               }
+
                if (prev_bio_flags != bio_flags || !contig || !can_merge ||
                    force_bio_submit ||
                    bio_add_page(bio, page, page_size, pg_offset) < page_size) {
@@ -2813,6 +2877,13 @@ static int submit_extent_page(unsigned int opf, struct 
extent_io_tree *tree,
                wbc_account_io(wbc, page, page_size);
        }
 
+       if (key_number != 0) {
+               struct btrfs_io_bio *io_bio = btrfs_io_bio(bio);
+
+               io_bio->key_number = key_number;
+               memcpy(io_bio->iv, iv, BTRFS_ENCRYPTION_BLOCK_LENGTH);
+       }
+
        *bio_ret = bio;
 
        return ret;
@@ -2924,6 +2995,8 @@ static int __do_readpage(struct extent_io_tree *tree,
        while (cur <= end) {
                bool force_bio_submit = false;
                u64 offset;
+               char iv[BTRFS_ENCRYPTION_BLOCK_LENGTH];
+               u64 key_number = 0;
 
                if (cur >= last_byte) {
                        char *userpage;
@@ -2951,6 +3024,9 @@ static int __do_readpage(struct extent_io_tree *tree,
                BUG_ON(extent_map_end(em) <= cur);
                BUG_ON(end < cur);
 
+               if (test_bit(EXTENT_FLAG_ENCRYPTED, &em->flags))
+                       this_bio_flag |= EXTENT_BIO_ENCRYPTED;
+
                if (test_bit(EXTENT_FLAG_COMPRESSED, &em->flags)) {
                        this_bio_flag |= EXTENT_BIO_COMPRESSED;
                        extent_set_compress_type(&this_bio_flag,
@@ -3014,6 +3090,16 @@ static int __do_readpage(struct extent_io_tree *tree,
                if (prev_em_start)
                        *prev_em_start = em->orig_start;
 
+               if (this_bio_flag & EXTENT_BIO_ENCRYPTED &&
+                       !(this_bio_flag & EXTENT_BIO_COMPRESSED)) {
+                       key_number = em->key_number;
+                       memcpy(iv, em->iv, BTRFS_ENCRYPTION_BLOCK_LENGTH);
+
+                       if (extent_offset != 0)
+                               btrfs_increment_iv(iv,
+                               extent_offset / BTRFS_ENCRYPTION_BLOCK_LENGTH);
+               }
+
                free_extent_map(em);
                em = NULL;
 
@@ -3061,7 +3147,7 @@ static int __do_readpage(struct extent_io_tree *tree,
                                         end_bio_extent_readpage, mirror_num,
                                         *bio_flags,
                                         this_bio_flag,
-                                        force_bio_submit);
+                                        force_bio_submit, key_number, iv);
                if (!ret) {
                        nr++;
                        *bio_flags = this_bio_flag;
@@ -3425,7 +3511,7 @@ static noinline_for_stack int 
__extent_writepage_io(struct inode *inode,
                                         page, offset, iosize, pg_offset,
                                         bdev, &epd->bio,
                                         end_bio_extent_writepage,
-                                        0, 0, 0, false);
+                                        0, 0, 0, false, 0, NULL);
                if (ret) {
                        SetPageError(page);
                        if (PageWriteback(page))
@@ -3738,7 +3824,7 @@ static noinline_for_stack int write_one_eb(struct 
extent_buffer *eb,
                                         p, offset, PAGE_SIZE, 0, bdev,
                                         &epd->bio,
                                         end_bio_extent_buffer_writepage,
-                                        0, 0, 0, false);
+                                        0, 0, 0, false, 0, NULL);
                if (ret) {
                        set_btree_ioerr(p);
                        if (PageWriteback(p))
diff --git a/fs/btrfs/extent_io.h b/fs/btrfs/extent_io.h
index 369daa5d4f73..54a7d6864d05 100644
--- a/fs/btrfs/extent_io.h
+++ b/fs/btrfs/extent_io.h
@@ -35,6 +35,7 @@
  * type for this bio
  */
 #define EXTENT_BIO_COMPRESSED 1
+#define EXTENT_BIO_ENCRYPTED 2
 #define EXTENT_BIO_FLAG_SHIFT 16
 
 /* these are bit numbers for test/set bit */
@@ -553,5 +554,6 @@ u64 btrfs_find_lock_delalloc_range(struct inode *inode,
 #endif
 struct extent_buffer *alloc_test_extent_buffer(struct btrfs_fs_info *fs_info,
                                               u64 start);
+void btrfs_increment_iv(char *iv, u32 inc);
 
 #endif
diff --git a/fs/btrfs/extent_map.c b/fs/btrfs/extent_map.c
index 7eea8b6e2cd3..0cc0ffbe0453 100644
--- a/fs/btrfs/extent_map.c
+++ b/fs/btrfs/extent_map.c
@@ -6,7 +6,7 @@
 #include "ctree.h"
 #include "extent_map.h"
 #include "compression.h"
-
+#include "extent_io.h"
 
 static struct kmem_cache *extent_map_cache;
 
@@ -210,6 +210,20 @@ static int mergable_maps(struct extent_map *prev, struct 
extent_map *next)
        if (!list_empty(&prev->list) || !list_empty(&next->list))
                return 0;
 
+       if (prev->key_number != next->key_number)
+               return 0;
+
+       if (next->key_number != 0) {
+               char iv[BTRFS_ENCRYPTION_BLOCK_LENGTH];
+
+               memcpy(iv, prev->iv, BTRFS_ENCRYPTION_BLOCK_LENGTH);
+               btrfs_increment_iv(iv, prev->len /
+                                  BTRFS_ENCRYPTION_BLOCK_LENGTH);
+
+               if (memcmp(iv, next->iv, BTRFS_ENCRYPTION_BLOCK_LENGTH))
+                       return 0;
+       }
+
        if (extent_map_end(prev) == next->start &&
            prev->flags == next->flags &&
            prev->bdev == next->bdev &&
diff --git a/fs/btrfs/extent_map.h b/fs/btrfs/extent_map.h
index 31977ffd6190..2bc1087564a1 100644
--- a/fs/btrfs/extent_map.h
+++ b/fs/btrfs/extent_map.h
@@ -5,6 +5,7 @@
 
 #include <linux/rbtree.h>
 #include <linux/refcount.h>
+#include <linux/btrfs_tree.h>
 
 #define EXTENT_MAP_LAST_BYTE ((u64)-4)
 #define EXTENT_MAP_HOLE ((u64)-3)
@@ -14,6 +15,7 @@
 /* bits for the flags field */
 #define EXTENT_FLAG_PINNED 0 /* this entry not yet on disk, don't free it */
 #define EXTENT_FLAG_COMPRESSED 1
+#define EXTENT_FLAG_ENCRYPTED 2
 #define EXTENT_FLAG_PREALLOC 3 /* pre-allocated extent */
 #define EXTENT_FLAG_LOGGING 4 /* Logging this extent */
 #define EXTENT_FLAG_FILLING 5 /* Filling in a preallocated extent */
@@ -45,6 +47,9 @@ struct extent_map {
        };
        refcount_t refs;
        unsigned int compress_type;
+       unsigned int encrypt_type;
+       u64 key_number;
+       char iv[BTRFS_ENCRYPTION_BLOCK_LENGTH];
        struct list_head list;
 };
 
diff --git a/fs/btrfs/file-item.c b/fs/btrfs/file-item.c
index ba74827beb32..a903997e67ba 100644
--- a/fs/btrfs/file-item.c
+++ b/fs/btrfs/file-item.c
@@ -13,6 +13,7 @@
 #include "volumes.h"
 #include "print-tree.h"
 #include "compression.h"
+#include "encryption.h"
 
 #define __MAX_CSUM_ITEMS(r, size) ((unsigned long)(((BTRFS_LEAF_DATA_SIZE(r) - 
\
                                   sizeof(struct btrfs_item) * 2) / \
@@ -931,6 +932,7 @@ void btrfs_extent_item_to_extent_map(struct btrfs_inode 
*inode,
        u64 bytenr;
        u8 type = btrfs_file_extent_type(leaf, fi);
        int compress_type = btrfs_file_extent_compression(leaf, fi);
+       int encrypt_type = btrfs_file_extent_encryption(leaf, fi);
 
        em->bdev = fs_info->fs_devices->latest_bdev;
        btrfs_item_key_to_cpu(leaf, &key, slot);
@@ -960,7 +962,18 @@ void btrfs_extent_item_to_extent_map(struct btrfs_inode 
*inode,
                        em->block_start = EXTENT_MAP_HOLE;
                        return;
                }
-               if (compress_type != BTRFS_COMPRESS_NONE) {
+
+               if (encrypt_type != BTRFS_ENCRYPTION_NONE) {
+                       set_bit(EXTENT_FLAG_ENCRYPTED, &em->flags);
+                       em->encrypt_type = encrypt_type;
+                       em->block_start = bytenr;
+                       em->block_len = em->orig_block_len;
+
+                       if (compress_type != BTRFS_COMPRESS_NONE) {
+                               set_bit(EXTENT_FLAG_COMPRESSED, &em->flags);
+                               em->compress_type = compress_type;
+                       }
+               } else if (compress_type != BTRFS_COMPRESS_NONE) {
                        set_bit(EXTENT_FLAG_COMPRESSED, &em->flags);
                        em->compress_type = compress_type;
                        em->block_start = bytenr;
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index 7018a2169e3e..52ea7d7c880b 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -1953,7 +1953,7 @@ int btrfs_merge_bio_hook(struct page *page, unsigned long 
offset,
        u64 map_length;
        int ret;
 
-       if (bio_flags & EXTENT_BIO_COMPRESSED)
+       if (bio_flags & (EXTENT_BIO_COMPRESSED | EXTENT_BIO_ENCRYPTED))
                return 0;
 
        length = bio->bi_iter.bi_size;
@@ -7039,6 +7039,14 @@ struct extent_map *btrfs_get_extent(struct btrfs_inode 
*inode,
        btrfs_extent_item_to_extent_map(inode, path, item,
                        new_inline, em);
 
+       if (btrfs_file_extent_type(leaf, item) != BTRFS_ENCRYPTION_NONE) {
+               em->key_number = btrfs_file_extent_enc_key_number(leaf,
+                               (struct btrfs_file_extent_item_enc *)item);
+
+               read_eb_member(leaf, item, struct btrfs_file_extent_item_enc,
+                              iv, em->iv);
+       }
+
        if (found_type == BTRFS_FILE_EXTENT_REG ||
            found_type == BTRFS_FILE_EXTENT_PREALLOC) {
                goto insert;
diff --git a/fs/btrfs/volumes.h b/fs/btrfs/volumes.h
index aefce895e994..fc12c28bdd93 100644
--- a/fs/btrfs/volumes.h
+++ b/fs/btrfs/volumes.h
@@ -271,6 +271,8 @@ struct btrfs_io_bio {
        u8 *csum_allocated;
        btrfs_io_bio_end_io_t *end_io;
        struct bvec_iter iter;
+       u64 key_number;
+       char iv[BTRFS_ENCRYPTION_BLOCK_LENGTH];
        /*
         * This member must come last, bio_alloc_bioset will allocate enough
         * bytes for entire btrfs_io_bio but relies on bio being last.
-- 
2.19.2

Reply via email to