From: Michael Halcrow Date: Wed, 8 Apr 2015 04:20:19 +0000 (-0400) Subject: ext4 crypto: implement the ext4 decryption read path X-Git-Tag: KARO-TXA5-2015-06-26~116^2~8 X-Git-Url: https://git.karo-electronics.de/?a=commitdiff_plain;h=b6a139dd9aae2e257818e5fca7a05f0edde472b4;p=karo-tx-linux.git ext4 crypto: implement the ext4 decryption read path Change-Id: Ie9c043a132a01da60d1617662cd30307639f5599 Signed-off-by: Michael Halcrow Signed-off-by: Ildar Muslukhov Signed-off-by: Theodore Ts'o --- diff --git a/fs/ext4/file.c b/fs/ext4/file.c index fcc6c1349186..ce6b92b85b42 100644 --- a/fs/ext4/file.c +++ b/fs/ext4/file.c @@ -27,6 +27,7 @@ #include #include "ext4.h" #include "ext4_jbd2.h" +#include "ext4_crypto.h" #include "xattr.h" #include "acl.h" @@ -218,6 +219,13 @@ static const struct vm_operations_struct ext4_file_vm_ops = { static int ext4_file_mmap(struct file *file, struct vm_area_struct *vma) { + struct inode *inode = file->f_mapping->host; + + if (ext4_encrypted_inode(inode)) { + int res = ext4_generate_encryption_key(inode); + if (res) + return 0; + } file_accessed(file); if (IS_DAX(file_inode(file))) { vma->vm_ops = &ext4_dax_vm_ops; @@ -235,6 +243,7 @@ static int ext4_file_open(struct inode * inode, struct file * filp) struct vfsmount *mnt = filp->f_path.mnt; struct path path; char buf[64], *cp; + int ret; if (unlikely(!(sbi->s_mount_flags & EXT4_MF_MNTDIR_SAMPLED) && !(sb->s_flags & MS_RDONLY))) { @@ -273,11 +282,17 @@ static int ext4_file_open(struct inode * inode, struct file * filp) * writing and the journal is present */ if (filp->f_mode & FMODE_WRITE) { - int ret = ext4_inode_attach_jinode(inode); + ret = ext4_inode_attach_jinode(inode); if (ret < 0) return ret; } - return dquot_file_open(inode, filp); + ret = dquot_file_open(inode, filp); + if (!ret && ext4_encrypted_inode(inode)) { + ret = ext4_generate_encryption_key(inode); + if (ret) + ret = -EACCES; + } + return ret; } /* diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 77b7ff25afa1..7eb70b7a5c19 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -37,6 +37,7 @@ #include #include #include +#include #include "ext4_jbd2.h" #include "ext4_crypto.h" @@ -3371,6 +3372,15 @@ static int __ext4_block_zero_page_range(handle_t *handle, /* Uhhuh. Read error. Complain and punt. */ if (!buffer_uptodate(bh)) goto unlock; +#ifdef CONFIG_EXT4_FS_ENCRYPTION + if (S_ISREG(inode->i_mode) && + ext4_encrypted_inode(inode)) { + /* We expect the key to be set. */ + BUG_ON(!ext4_has_encryption_key(inode)); + BUG_ON(blocksize != PAGE_CACHE_SIZE); + WARN_ON_ONCE(ext4_decrypt_one(inode, page)); + } +#endif } if (ext4_should_journal_data(inode)) { BUFFER_TRACE(bh, "get write access"); diff --git a/fs/ext4/readpage.c b/fs/ext4/readpage.c index fff9fe6aacf8..968558d396fb 100644 --- a/fs/ext4/readpage.c +++ b/fs/ext4/readpage.c @@ -46,6 +46,35 @@ #include "ext4.h" +#ifdef CONFIG_EXT4_FS_ENCRYPTION +/* + * Call ext4_decrypt on every single page, reusing the encryption + * context. + */ +static void completion_pages(struct work_struct *work) +{ + struct ext4_crypto_ctx *ctx = + container_of(work, struct ext4_crypto_ctx, work); + struct bio *bio = ctx->bio; + struct bio_vec *bv; + int i; + + bio_for_each_segment_all(bv, bio, i) { + struct page *page = bv->bv_page; + + int ret = ext4_decrypt(ctx, page); + if (ret) { + WARN_ON_ONCE(1); + SetPageError(page); + } else + SetPageUptodate(page); + unlock_page(page); + } + ext4_release_crypto_ctx(ctx); + bio_put(bio); +} +#endif + /* * I/O completion handler for multipage BIOs. * @@ -63,6 +92,20 @@ static void mpage_end_io(struct bio *bio, int err) struct bio_vec *bv; int i; +#ifdef CONFIG_EXT4_FS_ENCRYPTION + if (bio->bi_private) { + struct ext4_crypto_ctx *ctx = bio->bi_private; + + if (err) + ext4_release_crypto_ctx(ctx); + else { + INIT_WORK(&ctx->work, completion_pages); + ctx->bio = bio; + queue_work(ext4_read_workqueue, &ctx->work); + return; + } + } +#endif bio_for_each_segment_all(bv, bio, i) { struct page *page = bv->bv_page; @@ -97,9 +140,15 @@ int ext4_mpage_readpages(struct address_space *mapping, unsigned page_block; struct block_device *bdev = inode->i_sb->s_bdev; int length; + int do_decryption = 0; unsigned relative_block = 0; struct ext4_map_blocks map; +#ifdef CONFIG_EXT4_FS_ENCRYPTION + if (ext4_encrypted_inode(inode) && S_ISREG(inode->i_mode)) + do_decryption = 1; +#endif + map.m_pblk = 0; map.m_lblk = 0; map.m_len = 0; @@ -223,13 +272,24 @@ int ext4_mpage_readpages(struct address_space *mapping, bio = NULL; } if (bio == NULL) { + struct ext4_crypto_ctx *ctx = NULL; + + if (do_decryption) { + ctx = ext4_get_crypto_ctx(inode); + if (IS_ERR(ctx)) + goto set_error_page; + } bio = bio_alloc(GFP_KERNEL, min_t(int, nr_pages, bio_get_nr_vecs(bdev))); - if (!bio) + if (!bio) { + if (ctx) + ext4_release_crypto_ctx(ctx); goto set_error_page; + } bio->bi_bdev = bdev; bio->bi_iter.bi_sector = blocks[0] << (blkbits - 9); bio->bi_end_io = mpage_end_io; + bio->bi_private = ctx; } length = first_hole << blkbits;