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