From d2d0727b1654e11563f181f4d3d48b9275514480 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Mon, 20 May 2019 09:29:39 -0700 Subject: [PATCH 01/18] fscrypt: simplify bounce page handling Currently, bounce page handling for writes to encrypted files is unnecessarily complicated. A fscrypt_ctx is allocated along with each bounce page, page_private(bounce_page) points to this fscrypt_ctx, and fscrypt_ctx::w::control_page points to the original pagecache page. However, because writes don't use the fscrypt_ctx for anything else, there's no reason why page_private(bounce_page) can't just point to the original pagecache page directly. Therefore, this patch makes this change. In the process, it also cleans up the API exposed to filesystems that allows testing whether a page is a bounce page, getting the pagecache page from a bounce page, and freeing a bounce page. Reviewed-by: Chandan Rajendra Signed-off-by: Eric Biggers --- fs/crypto/bio.c | 38 ++----------- fs/crypto/crypto.c | 104 ++++++++++++------------------------ fs/crypto/fscrypt_private.h | 4 +- fs/ext4/page-io.c | 36 +++++-------- fs/f2fs/data.c | 12 ++--- include/linux/fscrypt.h | 38 ++++++++----- 6 files changed, 84 insertions(+), 148 deletions(-) diff --git a/fs/crypto/bio.c b/fs/crypto/bio.c index b46021ebde85..c857b70b5328 100644 --- a/fs/crypto/bio.c +++ b/fs/crypto/bio.c @@ -70,46 +70,18 @@ void fscrypt_enqueue_decrypt_bio(struct fscrypt_ctx *ctx, struct bio *bio) } EXPORT_SYMBOL(fscrypt_enqueue_decrypt_bio); -void fscrypt_pullback_bio_page(struct page **page, bool restore) -{ - struct fscrypt_ctx *ctx; - struct page *bounce_page; - - /* The bounce data pages are unmapped. */ - if ((*page)->mapping) - return; - - /* The bounce data page is unmapped. */ - bounce_page = *page; - ctx = (struct fscrypt_ctx *)page_private(bounce_page); - - /* restore control page */ - *page = ctx->w.control_page; - - if (restore) - fscrypt_restore_control_page(bounce_page); -} -EXPORT_SYMBOL(fscrypt_pullback_bio_page); - int fscrypt_zeroout_range(const struct inode *inode, pgoff_t lblk, sector_t pblk, unsigned int len) { - struct fscrypt_ctx *ctx; - struct page *ciphertext_page = NULL; + struct page *ciphertext_page; struct bio *bio; int ret, err = 0; BUG_ON(inode->i_sb->s_blocksize != PAGE_SIZE); - ctx = fscrypt_get_ctx(GFP_NOFS); - if (IS_ERR(ctx)) - return PTR_ERR(ctx); - - ciphertext_page = fscrypt_alloc_bounce_page(ctx, GFP_NOWAIT); - if (IS_ERR(ciphertext_page)) { - err = PTR_ERR(ciphertext_page); - goto errout; - } + ciphertext_page = fscrypt_alloc_bounce_page(GFP_NOWAIT); + if (!ciphertext_page) + return -ENOMEM; while (len--) { err = fscrypt_do_page_crypto(inode, FS_ENCRYPT, lblk, @@ -147,7 +119,7 @@ int fscrypt_zeroout_range(const struct inode *inode, pgoff_t lblk, } err = 0; errout: - fscrypt_release_ctx(ctx); + fscrypt_free_bounce_page(ciphertext_page); return err; } EXPORT_SYMBOL(fscrypt_zeroout_range); diff --git a/fs/crypto/crypto.c b/fs/crypto/crypto.c index 335a362ee446..881e2a69f8a6 100644 --- a/fs/crypto/crypto.c +++ b/fs/crypto/crypto.c @@ -64,18 +64,11 @@ EXPORT_SYMBOL(fscrypt_enqueue_decrypt_work); * * If the encryption context was allocated from the pre-allocated pool, returns * it to that pool. Else, frees it. - * - * If there's a bounce page in the context, this frees that. */ void fscrypt_release_ctx(struct fscrypt_ctx *ctx) { unsigned long flags; - if (ctx->flags & FS_CTX_HAS_BOUNCE_BUFFER_FL && ctx->w.bounce_page) { - mempool_free(ctx->w.bounce_page, fscrypt_bounce_page_pool); - ctx->w.bounce_page = NULL; - } - ctx->w.control_page = NULL; if (ctx->flags & FS_CTX_REQUIRES_FREE_ENCRYPT_FL) { kmem_cache_free(fscrypt_ctx_cachep, ctx); } else { @@ -100,14 +93,8 @@ struct fscrypt_ctx *fscrypt_get_ctx(gfp_t gfp_flags) unsigned long flags; /* - * We first try getting the ctx from a free list because in - * the common case the ctx will have an allocated and - * initialized crypto tfm, so it's probably a worthwhile - * optimization. For the bounce page, we first try getting it - * from the kernel allocator because that's just about as fast - * as getting it from a list and because a cache of free pages - * should generally be a "last resort" option for a filesystem - * to be able to do its job. + * First try getting a ctx from the free list so that we don't have to + * call into the slab allocator. */ spin_lock_irqsave(&fscrypt_ctx_lock, flags); ctx = list_first_entry_or_null(&fscrypt_free_ctxs, @@ -123,11 +110,31 @@ struct fscrypt_ctx *fscrypt_get_ctx(gfp_t gfp_flags) } else { ctx->flags &= ~FS_CTX_REQUIRES_FREE_ENCRYPT_FL; } - ctx->flags &= ~FS_CTX_HAS_BOUNCE_BUFFER_FL; return ctx; } EXPORT_SYMBOL(fscrypt_get_ctx); +struct page *fscrypt_alloc_bounce_page(gfp_t gfp_flags) +{ + return mempool_alloc(fscrypt_bounce_page_pool, gfp_flags); +} + +/** + * fscrypt_free_bounce_page() - free a ciphertext bounce page + * + * Free a bounce page that was allocated by fscrypt_encrypt_page(), or by + * fscrypt_alloc_bounce_page() directly. + */ +void fscrypt_free_bounce_page(struct page *bounce_page) +{ + if (!bounce_page) + return; + set_page_private(bounce_page, (unsigned long)NULL); + ClearPagePrivate(bounce_page); + mempool_free(bounce_page, fscrypt_bounce_page_pool); +} +EXPORT_SYMBOL(fscrypt_free_bounce_page); + void fscrypt_generate_iv(union fscrypt_iv *iv, u64 lblk_num, const struct fscrypt_info *ci) { @@ -186,16 +193,6 @@ int fscrypt_do_page_crypto(const struct inode *inode, fscrypt_direction_t rw, return 0; } -struct page *fscrypt_alloc_bounce_page(struct fscrypt_ctx *ctx, - gfp_t gfp_flags) -{ - ctx->w.bounce_page = mempool_alloc(fscrypt_bounce_page_pool, gfp_flags); - if (ctx->w.bounce_page == NULL) - return ERR_PTR(-ENOMEM); - ctx->flags |= FS_CTX_HAS_BOUNCE_BUFFER_FL; - return ctx->w.bounce_page; -} - /** * fscypt_encrypt_page() - Encrypts a page * @inode: The inode for which the encryption should take place @@ -210,22 +207,12 @@ struct page *fscrypt_alloc_bounce_page(struct fscrypt_ctx *ctx, * previously written data. * @gfp_flags: The gfp flag for memory allocation * - * Encrypts @page using the ctx encryption context. Performs encryption - * either in-place or into a newly allocated bounce page. - * Called on the page write path. + * Encrypts @page. If the filesystem set FS_CFLG_OWN_PAGES, then the data is + * encrypted in-place and @page is returned. Else, a bounce page is allocated, + * the data is encrypted into the bounce page, and the bounce page is returned. + * The caller is responsible for calling fscrypt_free_bounce_page(). * - * Bounce page allocation is the default. - * In this case, the contents of @page are encrypted and stored in an - * allocated bounce page. @page has to be locked and the caller must call - * fscrypt_restore_control_page() on the returned ciphertext page to - * release the bounce buffer and the encryption context. - * - * In-place encryption is used by setting the FS_CFLG_OWN_PAGES flag in - * fscrypt_operations. Here, the input-page is returned with its content - * encrypted. - * - * Return: A page with the encrypted content on success. Else, an - * error value or NULL. + * Return: A page containing the encrypted data on success, else an ERR_PTR() */ struct page *fscrypt_encrypt_page(const struct inode *inode, struct page *page, @@ -234,7 +221,6 @@ struct page *fscrypt_encrypt_page(const struct inode *inode, u64 lblk_num, gfp_t gfp_flags) { - struct fscrypt_ctx *ctx; struct page *ciphertext_page = page; int err; @@ -253,30 +239,20 @@ struct page *fscrypt_encrypt_page(const struct inode *inode, BUG_ON(!PageLocked(page)); - ctx = fscrypt_get_ctx(gfp_flags); - if (IS_ERR(ctx)) - return ERR_CAST(ctx); - /* The encryption operation will require a bounce page. */ - ciphertext_page = fscrypt_alloc_bounce_page(ctx, gfp_flags); - if (IS_ERR(ciphertext_page)) - goto errout; + ciphertext_page = fscrypt_alloc_bounce_page(gfp_flags); + if (!ciphertext_page) + return ERR_PTR(-ENOMEM); - ctx->w.control_page = page; err = fscrypt_do_page_crypto(inode, FS_ENCRYPT, lblk_num, page, ciphertext_page, len, offs, gfp_flags); if (err) { - ciphertext_page = ERR_PTR(err); - goto errout; + fscrypt_free_bounce_page(ciphertext_page); + return ERR_PTR(err); } SetPagePrivate(ciphertext_page); - set_page_private(ciphertext_page, (unsigned long)ctx); - lock_page(ciphertext_page); - return ciphertext_page; - -errout: - fscrypt_release_ctx(ctx); + set_page_private(ciphertext_page, (unsigned long)page); return ciphertext_page; } EXPORT_SYMBOL(fscrypt_encrypt_page); @@ -355,18 +331,6 @@ const struct dentry_operations fscrypt_d_ops = { .d_revalidate = fscrypt_d_revalidate, }; -void fscrypt_restore_control_page(struct page *page) -{ - struct fscrypt_ctx *ctx; - - ctx = (struct fscrypt_ctx *)page_private(page); - set_page_private(page, (unsigned long)NULL); - ClearPagePrivate(page); - unlock_page(page); - fscrypt_release_ctx(ctx); -} -EXPORT_SYMBOL(fscrypt_restore_control_page); - static void fscrypt_destroy(void) { struct fscrypt_ctx *pos, *n; diff --git a/fs/crypto/fscrypt_private.h b/fs/crypto/fscrypt_private.h index 7da276159593..4122ee1a0b7b 100644 --- a/fs/crypto/fscrypt_private.h +++ b/fs/crypto/fscrypt_private.h @@ -94,7 +94,6 @@ typedef enum { } fscrypt_direction_t; #define FS_CTX_REQUIRES_FREE_ENCRYPT_FL 0x00000001 -#define FS_CTX_HAS_BOUNCE_BUFFER_FL 0x00000002 static inline bool fscrypt_valid_enc_modes(u32 contents_mode, u32 filenames_mode) @@ -123,8 +122,7 @@ extern int fscrypt_do_page_crypto(const struct inode *inode, struct page *dest_page, unsigned int len, unsigned int offs, gfp_t gfp_flags); -extern struct page *fscrypt_alloc_bounce_page(struct fscrypt_ctx *ctx, - gfp_t gfp_flags); +extern struct page *fscrypt_alloc_bounce_page(gfp_t gfp_flags); extern const struct dentry_operations fscrypt_d_ops; extern void __printf(3, 4) __cold diff --git a/fs/ext4/page-io.c b/fs/ext4/page-io.c index 4690618a92e9..13d5ecc0af03 100644 --- a/fs/ext4/page-io.c +++ b/fs/ext4/page-io.c @@ -66,9 +66,7 @@ static void ext4_finish_bio(struct bio *bio) bio_for_each_segment_all(bvec, bio, iter_all) { struct page *page = bvec->bv_page; -#ifdef CONFIG_FS_ENCRYPTION - struct page *data_page = NULL; -#endif + struct page *bounce_page = NULL; struct buffer_head *bh, *head; unsigned bio_start = bvec->bv_offset; unsigned bio_end = bio_start + bvec->bv_len; @@ -78,13 +76,10 @@ static void ext4_finish_bio(struct bio *bio) if (!page) continue; -#ifdef CONFIG_FS_ENCRYPTION - if (!page->mapping) { - /* The bounce data pages are unmapped. */ - data_page = page; - fscrypt_pullback_bio_page(&page, false); + if (fscrypt_is_bounce_page(page)) { + bounce_page = page; + page = fscrypt_pagecache_page(bounce_page); } -#endif if (bio->bi_status) { SetPageError(page); @@ -111,10 +106,7 @@ static void ext4_finish_bio(struct bio *bio) bit_spin_unlock(BH_Uptodate_Lock, &head->b_state); local_irq_restore(flags); if (!under_io) { -#ifdef CONFIG_FS_ENCRYPTION - if (data_page) - fscrypt_restore_control_page(data_page); -#endif + fscrypt_free_bounce_page(bounce_page); end_page_writeback(page); } } @@ -415,7 +407,7 @@ int ext4_bio_write_page(struct ext4_io_submit *io, struct writeback_control *wbc, bool keep_towrite) { - struct page *data_page = NULL; + struct page *bounce_page = NULL; struct inode *inode = page->mapping->host; unsigned block_start; struct buffer_head *bh, *head; @@ -479,10 +471,10 @@ int ext4_bio_write_page(struct ext4_io_submit *io, gfp_t gfp_flags = GFP_NOFS; retry_encrypt: - data_page = fscrypt_encrypt_page(inode, page, PAGE_SIZE, 0, - page->index, gfp_flags); - if (IS_ERR(data_page)) { - ret = PTR_ERR(data_page); + bounce_page = fscrypt_encrypt_page(inode, page, PAGE_SIZE, 0, + page->index, gfp_flags); + if (IS_ERR(bounce_page)) { + ret = PTR_ERR(bounce_page); if (ret == -ENOMEM && wbc->sync_mode == WB_SYNC_ALL) { if (io->io_bio) { ext4_io_submit(io); @@ -491,7 +483,7 @@ int ext4_bio_write_page(struct ext4_io_submit *io, gfp_flags |= __GFP_NOFAIL; goto retry_encrypt; } - data_page = NULL; + bounce_page = NULL; goto out; } } @@ -500,8 +492,7 @@ int ext4_bio_write_page(struct ext4_io_submit *io, do { if (!buffer_async_write(bh)) continue; - ret = io_submit_add_bh(io, inode, - data_page ? data_page : page, bh); + ret = io_submit_add_bh(io, inode, bounce_page ?: page, bh); if (ret) { /* * We only get here on ENOMEM. Not much else @@ -517,8 +508,7 @@ int ext4_bio_write_page(struct ext4_io_submit *io, /* Error stopped previous loop? Clean up buffers... */ if (ret) { out: - if (data_page) - fscrypt_restore_control_page(data_page); + fscrypt_free_bounce_page(bounce_page); printk_ratelimited(KERN_ERR "%s: ret = %d\n", __func__, ret); redirty_page_for_writepage(wbc, page); do { diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index eda4181d2092..968ebdbcb583 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -185,7 +185,7 @@ static void f2fs_write_end_io(struct bio *bio) continue; } - fscrypt_pullback_bio_page(&page, true); + fscrypt_finalize_bounce_page(&page); if (unlikely(bio->bi_status)) { mapping_set_error(page->mapping, -EIO); @@ -362,10 +362,9 @@ static bool __has_merged_page(struct f2fs_bio_info *io, struct inode *inode, bio_for_each_segment_all(bvec, io->bio, iter_all) { - if (bvec->bv_page->mapping) - target = bvec->bv_page; - else - target = fscrypt_control_page(bvec->bv_page); + target = bvec->bv_page; + if (fscrypt_is_bounce_page(target)) + target = fscrypt_pagecache_page(target); if (inode && inode == target->mapping->host) return true; @@ -1900,8 +1899,7 @@ got_it: err = f2fs_inplace_write_data(fio); if (err) { if (f2fs_encrypted_file(inode)) - fscrypt_pullback_bio_page(&fio->encrypted_page, - true); + fscrypt_finalize_bounce_page(&fio->encrypted_page); if (PageWriteback(page)) end_page_writeback(page); } else { diff --git a/include/linux/fscrypt.h b/include/linux/fscrypt.h index f7680ef1abd2..d016fa384d60 100644 --- a/include/linux/fscrypt.h +++ b/include/linux/fscrypt.h @@ -112,12 +112,17 @@ extern struct page *fscrypt_encrypt_page(const struct inode *, struct page *, extern int fscrypt_decrypt_page(const struct inode *, struct page *, unsigned int, unsigned int, u64); -static inline struct page *fscrypt_control_page(struct page *page) +static inline bool fscrypt_is_bounce_page(struct page *page) { - return ((struct fscrypt_ctx *)page_private(page))->w.control_page; + return page->mapping == NULL; } -extern void fscrypt_restore_control_page(struct page *); +static inline struct page *fscrypt_pagecache_page(struct page *bounce_page) +{ + return (struct page *)page_private(bounce_page); +} + +extern void fscrypt_free_bounce_page(struct page *bounce_page); /* policy.c */ extern int fscrypt_ioctl_set_policy(struct file *, const void __user *); @@ -223,7 +228,6 @@ static inline bool fscrypt_match_name(const struct fscrypt_name *fname, extern void fscrypt_decrypt_bio(struct bio *); extern void fscrypt_enqueue_decrypt_bio(struct fscrypt_ctx *ctx, struct bio *bio); -extern void fscrypt_pullback_bio_page(struct page **, bool); extern int fscrypt_zeroout_range(const struct inode *, pgoff_t, sector_t, unsigned int); @@ -300,15 +304,19 @@ static inline int fscrypt_decrypt_page(const struct inode *inode, return -EOPNOTSUPP; } -static inline struct page *fscrypt_control_page(struct page *page) +static inline bool fscrypt_is_bounce_page(struct page *page) +{ + return false; +} + +static inline struct page *fscrypt_pagecache_page(struct page *bounce_page) { WARN_ON_ONCE(1); return ERR_PTR(-EINVAL); } -static inline void fscrypt_restore_control_page(struct page *page) +static inline void fscrypt_free_bounce_page(struct page *bounce_page) { - return; } /* policy.c */ @@ -410,11 +418,6 @@ static inline void fscrypt_enqueue_decrypt_bio(struct fscrypt_ctx *ctx, { } -static inline void fscrypt_pullback_bio_page(struct page **page, bool restore) -{ - return; -} - static inline int fscrypt_zeroout_range(const struct inode *inode, pgoff_t lblk, sector_t pblk, unsigned int len) { @@ -692,4 +695,15 @@ static inline int fscrypt_encrypt_symlink(struct inode *inode, return 0; } +/* If *pagep is a bounce page, free it and set *pagep to the pagecache page */ +static inline void fscrypt_finalize_bounce_page(struct page **pagep) +{ + struct page *page = *pagep; + + if (fscrypt_is_bounce_page(page)) { + *pagep = fscrypt_pagecache_page(page); + fscrypt_free_bounce_page(page); + } +} + #endif /* _LINUX_FSCRYPT_H */ From 2a415a0257314cb2e49fb9ac4c6770837112f261 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Mon, 20 May 2019 09:29:40 -0700 Subject: [PATCH 02/18] fscrypt: remove the "write" part of struct fscrypt_ctx Now that fscrypt_ctx is not used for writes, remove the 'w' fields. Reviewed-by: Chandan Rajendra Signed-off-by: Eric Biggers --- fs/crypto/bio.c | 11 +++++------ fs/crypto/crypto.c | 14 +++++++------- include/linux/fscrypt.h | 7 ++----- 3 files changed, 14 insertions(+), 18 deletions(-) diff --git a/fs/crypto/bio.c b/fs/crypto/bio.c index c857b70b5328..c53425348387 100644 --- a/fs/crypto/bio.c +++ b/fs/crypto/bio.c @@ -53,9 +53,8 @@ EXPORT_SYMBOL(fscrypt_decrypt_bio); static void completion_pages(struct work_struct *work) { - struct fscrypt_ctx *ctx = - container_of(work, struct fscrypt_ctx, r.work); - struct bio *bio = ctx->r.bio; + struct fscrypt_ctx *ctx = container_of(work, struct fscrypt_ctx, work); + struct bio *bio = ctx->bio; __fscrypt_decrypt_bio(bio, true); fscrypt_release_ctx(ctx); @@ -64,9 +63,9 @@ static void completion_pages(struct work_struct *work) void fscrypt_enqueue_decrypt_bio(struct fscrypt_ctx *ctx, struct bio *bio) { - INIT_WORK(&ctx->r.work, completion_pages); - ctx->r.bio = bio; - fscrypt_enqueue_decrypt_work(&ctx->r.work); + INIT_WORK(&ctx->work, completion_pages); + ctx->bio = bio; + fscrypt_enqueue_decrypt_work(&ctx->work); } EXPORT_SYMBOL(fscrypt_enqueue_decrypt_bio); diff --git a/fs/crypto/crypto.c b/fs/crypto/crypto.c index 881e2a69f8a6..9dd7a643eae0 100644 --- a/fs/crypto/crypto.c +++ b/fs/crypto/crypto.c @@ -59,11 +59,11 @@ void fscrypt_enqueue_decrypt_work(struct work_struct *work) EXPORT_SYMBOL(fscrypt_enqueue_decrypt_work); /** - * fscrypt_release_ctx() - Releases an encryption context - * @ctx: The encryption context to release. + * fscrypt_release_ctx() - Release a decryption context + * @ctx: The decryption context to release. * - * If the encryption context was allocated from the pre-allocated pool, returns - * it to that pool. Else, frees it. + * If the decryption context was allocated from the pre-allocated pool, return + * it to that pool. Else, free it. */ void fscrypt_release_ctx(struct fscrypt_ctx *ctx) { @@ -80,12 +80,12 @@ void fscrypt_release_ctx(struct fscrypt_ctx *ctx) EXPORT_SYMBOL(fscrypt_release_ctx); /** - * fscrypt_get_ctx() - Gets an encryption context + * fscrypt_get_ctx() - Get a decryption context * @gfp_flags: The gfp flag for memory allocation * - * Allocates and initializes an encryption context. + * Allocate and initialize a decryption context. * - * Return: A new encryption context on success; an ERR_PTR() otherwise. + * Return: A new decryption context on success; an ERR_PTR() otherwise. */ struct fscrypt_ctx *fscrypt_get_ctx(gfp_t gfp_flags) { diff --git a/include/linux/fscrypt.h b/include/linux/fscrypt.h index d016fa384d60..1c7287f146a9 100644 --- a/include/linux/fscrypt.h +++ b/include/linux/fscrypt.h @@ -63,16 +63,13 @@ struct fscrypt_operations { unsigned int max_namelen; }; +/* Decryption work */ struct fscrypt_ctx { union { - struct { - struct page *bounce_page; /* Ciphertext page */ - struct page *control_page; /* Original page */ - } w; struct { struct bio *bio; struct work_struct work; - } r; + }; struct list_head free_list; /* Free list */ }; u8 flags; /* Flags */ From f47fcbb2b578bdb213d9ac5875aab56a2034d466 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Mon, 20 May 2019 09:29:41 -0700 Subject: [PATCH 03/18] fscrypt: rename fscrypt_do_page_crypto() to fscrypt_crypt_block() fscrypt_do_page_crypto() only does a single encryption or decryption operation, with a single logical block number (single IV). So it actually operates on a filesystem block, not a "page" per se. To reflect this, rename it to fscrypt_crypt_block(). Reviewed-by: Chandan Rajendra Signed-off-by: Eric Biggers --- fs/crypto/bio.c | 6 +++--- fs/crypto/crypto.c | 24 ++++++++++++------------ fs/crypto/fscrypt_private.h | 11 +++++------ 3 files changed, 20 insertions(+), 21 deletions(-) diff --git a/fs/crypto/bio.c b/fs/crypto/bio.c index c53425348387..92b2d5da5d8e 100644 --- a/fs/crypto/bio.c +++ b/fs/crypto/bio.c @@ -83,9 +83,9 @@ int fscrypt_zeroout_range(const struct inode *inode, pgoff_t lblk, return -ENOMEM; while (len--) { - err = fscrypt_do_page_crypto(inode, FS_ENCRYPT, lblk, - ZERO_PAGE(0), ciphertext_page, - PAGE_SIZE, 0, GFP_NOFS); + err = fscrypt_crypt_block(inode, FS_ENCRYPT, lblk, + ZERO_PAGE(0), ciphertext_page, + PAGE_SIZE, 0, GFP_NOFS); if (err) goto errout; diff --git a/fs/crypto/crypto.c b/fs/crypto/crypto.c index 9dd7a643eae0..a798c52e3dc9 100644 --- a/fs/crypto/crypto.c +++ b/fs/crypto/crypto.c @@ -148,10 +148,11 @@ void fscrypt_generate_iv(union fscrypt_iv *iv, u64 lblk_num, crypto_cipher_encrypt_one(ci->ci_essiv_tfm, iv->raw, iv->raw); } -int fscrypt_do_page_crypto(const struct inode *inode, fscrypt_direction_t rw, - u64 lblk_num, struct page *src_page, - struct page *dest_page, unsigned int len, - unsigned int offs, gfp_t gfp_flags) +/* Encrypt or decrypt a single filesystem block of file contents */ +int fscrypt_crypt_block(const struct inode *inode, fscrypt_direction_t rw, + u64 lblk_num, struct page *src_page, + struct page *dest_page, unsigned int len, + unsigned int offs, gfp_t gfp_flags) { union fscrypt_iv iv; struct skcipher_request *req = NULL; @@ -228,9 +229,9 @@ struct page *fscrypt_encrypt_page(const struct inode *inode, if (inode->i_sb->s_cop->flags & FS_CFLG_OWN_PAGES) { /* with inplace-encryption we just encrypt the page */ - err = fscrypt_do_page_crypto(inode, FS_ENCRYPT, lblk_num, page, - ciphertext_page, len, offs, - gfp_flags); + err = fscrypt_crypt_block(inode, FS_ENCRYPT, lblk_num, page, + ciphertext_page, len, offs, + gfp_flags); if (err) return ERR_PTR(err); @@ -244,9 +245,8 @@ struct page *fscrypt_encrypt_page(const struct inode *inode, if (!ciphertext_page) return ERR_PTR(-ENOMEM); - err = fscrypt_do_page_crypto(inode, FS_ENCRYPT, lblk_num, - page, ciphertext_page, len, offs, - gfp_flags); + err = fscrypt_crypt_block(inode, FS_ENCRYPT, lblk_num, page, + ciphertext_page, len, offs, gfp_flags); if (err) { fscrypt_free_bounce_page(ciphertext_page); return ERR_PTR(err); @@ -278,8 +278,8 @@ int fscrypt_decrypt_page(const struct inode *inode, struct page *page, if (!(inode->i_sb->s_cop->flags & FS_CFLG_OWN_PAGES)) BUG_ON(!PageLocked(page)); - return fscrypt_do_page_crypto(inode, FS_DECRYPT, lblk_num, page, page, - len, offs, GFP_NOFS); + return fscrypt_crypt_block(inode, FS_DECRYPT, lblk_num, page, page, + len, offs, GFP_NOFS); } EXPORT_SYMBOL(fscrypt_decrypt_page); diff --git a/fs/crypto/fscrypt_private.h b/fs/crypto/fscrypt_private.h index 4122ee1a0b7b..8978eec9d766 100644 --- a/fs/crypto/fscrypt_private.h +++ b/fs/crypto/fscrypt_private.h @@ -116,12 +116,11 @@ static inline bool fscrypt_valid_enc_modes(u32 contents_mode, /* crypto.c */ extern struct kmem_cache *fscrypt_info_cachep; extern int fscrypt_initialize(unsigned int cop_flags); -extern int fscrypt_do_page_crypto(const struct inode *inode, - fscrypt_direction_t rw, u64 lblk_num, - struct page *src_page, - struct page *dest_page, - unsigned int len, unsigned int offs, - gfp_t gfp_flags); +extern int fscrypt_crypt_block(const struct inode *inode, + fscrypt_direction_t rw, u64 lblk_num, + struct page *src_page, struct page *dest_page, + unsigned int len, unsigned int offs, + gfp_t gfp_flags); extern struct page *fscrypt_alloc_bounce_page(gfp_t gfp_flags); extern const struct dentry_operations fscrypt_d_ops; From eeacfdc68a104967162dfcba60f53f6f5b62a334 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Mon, 20 May 2019 09:29:42 -0700 Subject: [PATCH 04/18] fscrypt: clean up some BUG_ON()s in block encryption/decryption Replace some BUG_ON()s with WARN_ON_ONCE() and returning an error code, and move the check for len divisible by FS_CRYPTO_BLOCK_SIZE into fscrypt_crypt_block() so that it's done for both encryption and decryption, not just encryption. Reviewed-by: Chandan Rajendra Signed-off-by: Eric Biggers --- fs/crypto/crypto.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/fs/crypto/crypto.c b/fs/crypto/crypto.c index a798c52e3dc9..59337287e580 100644 --- a/fs/crypto/crypto.c +++ b/fs/crypto/crypto.c @@ -162,7 +162,10 @@ int fscrypt_crypt_block(const struct inode *inode, fscrypt_direction_t rw, struct crypto_skcipher *tfm = ci->ci_ctfm; int res = 0; - BUG_ON(len == 0); + if (WARN_ON_ONCE(len <= 0)) + return -EINVAL; + if (WARN_ON_ONCE(len % FS_CRYPTO_BLOCK_SIZE != 0)) + return -EINVAL; fscrypt_generate_iv(&iv, lblk_num, ci); @@ -225,8 +228,6 @@ struct page *fscrypt_encrypt_page(const struct inode *inode, struct page *ciphertext_page = page; int err; - BUG_ON(len % FS_CRYPTO_BLOCK_SIZE != 0); - if (inode->i_sb->s_cop->flags & FS_CFLG_OWN_PAGES) { /* with inplace-encryption we just encrypt the page */ err = fscrypt_crypt_block(inode, FS_ENCRYPT, lblk_num, page, @@ -238,7 +239,8 @@ struct page *fscrypt_encrypt_page(const struct inode *inode, return ciphertext_page; } - BUG_ON(!PageLocked(page)); + if (WARN_ON_ONCE(!PageLocked(page))) + return ERR_PTR(-EINVAL); /* The encryption operation will require a bounce page. */ ciphertext_page = fscrypt_alloc_bounce_page(gfp_flags); @@ -275,8 +277,9 @@ EXPORT_SYMBOL(fscrypt_encrypt_page); int fscrypt_decrypt_page(const struct inode *inode, struct page *page, unsigned int len, unsigned int offs, u64 lblk_num) { - if (!(inode->i_sb->s_cop->flags & FS_CFLG_OWN_PAGES)) - BUG_ON(!PageLocked(page)); + if (WARN_ON_ONCE(!PageLocked(page) && + !(inode->i_sb->s_cop->flags & FS_CFLG_OWN_PAGES))) + return -EINVAL; return fscrypt_crypt_block(inode, FS_DECRYPT, lblk_num, page, page, len, offs, GFP_NOFS); From 03569f2fb8e734f281379767de674e23c38b0b14 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Mon, 20 May 2019 09:29:43 -0700 Subject: [PATCH 05/18] fscrypt: introduce fscrypt_encrypt_block_inplace() fscrypt_encrypt_page() behaves very differently depending on whether the filesystem set FS_CFLG_OWN_PAGES in its fscrypt_operations. This makes the function difficult to understand and document. It also makes it so that all callers have to provide inode and lblk_num, when fscrypt could determine these itself for pagecache pages. Therefore, move the FS_CFLG_OWN_PAGES behavior into a new function fscrypt_encrypt_block_inplace(). This is in preparation for allowing encryption on ext4 filesystems with blocksize != PAGE_SIZE. Reviewed-by: Chandan Rajendra Signed-off-by: Eric Biggers --- fs/crypto/crypto.c | 50 +++++++++++++++++++++++++---------------- fs/ubifs/crypto.c | 12 +++++----- include/linux/fscrypt.h | 13 +++++++++++ 3 files changed, 50 insertions(+), 25 deletions(-) diff --git a/fs/crypto/crypto.c b/fs/crypto/crypto.c index 59337287e580..2969a1dff10b 100644 --- a/fs/crypto/crypto.c +++ b/fs/crypto/crypto.c @@ -200,8 +200,7 @@ int fscrypt_crypt_block(const struct inode *inode, fscrypt_direction_t rw, /** * fscypt_encrypt_page() - Encrypts a page * @inode: The inode for which the encryption should take place - * @page: The page to encrypt. Must be locked for bounce-page - * encryption. + * @page: The page to encrypt. Must be locked. * @len: Length of data to encrypt in @page and encrypted * data in returned page. * @offs: Offset of data within @page and returned @@ -211,10 +210,9 @@ int fscrypt_crypt_block(const struct inode *inode, fscrypt_direction_t rw, * previously written data. * @gfp_flags: The gfp flag for memory allocation * - * Encrypts @page. If the filesystem set FS_CFLG_OWN_PAGES, then the data is - * encrypted in-place and @page is returned. Else, a bounce page is allocated, - * the data is encrypted into the bounce page, and the bounce page is returned. - * The caller is responsible for calling fscrypt_free_bounce_page(). + * Encrypts @page. A bounce page is allocated, the data is encrypted into the + * bounce page, and the bounce page is returned. The caller is responsible for + * calling fscrypt_free_bounce_page(). * * Return: A page containing the encrypted data on success, else an ERR_PTR() */ @@ -225,24 +223,12 @@ struct page *fscrypt_encrypt_page(const struct inode *inode, u64 lblk_num, gfp_t gfp_flags) { - struct page *ciphertext_page = page; + struct page *ciphertext_page; int err; - if (inode->i_sb->s_cop->flags & FS_CFLG_OWN_PAGES) { - /* with inplace-encryption we just encrypt the page */ - err = fscrypt_crypt_block(inode, FS_ENCRYPT, lblk_num, page, - ciphertext_page, len, offs, - gfp_flags); - if (err) - return ERR_PTR(err); - - return ciphertext_page; - } - if (WARN_ON_ONCE(!PageLocked(page))) return ERR_PTR(-EINVAL); - /* The encryption operation will require a bounce page. */ ciphertext_page = fscrypt_alloc_bounce_page(gfp_flags); if (!ciphertext_page) return ERR_PTR(-ENOMEM); @@ -259,6 +245,32 @@ struct page *fscrypt_encrypt_page(const struct inode *inode, } EXPORT_SYMBOL(fscrypt_encrypt_page); +/** + * fscrypt_encrypt_block_inplace() - Encrypt a filesystem block in-place + * @inode: The inode to which this block belongs + * @page: The page containing the block to encrypt + * @len: Size of block to encrypt. Doesn't need to be a multiple of the + * fs block size, but must be a multiple of FS_CRYPTO_BLOCK_SIZE. + * @offs: Byte offset within @page at which the block to encrypt begins + * @lblk_num: Filesystem logical block number of the block, i.e. the 0-based + * number of the block within the file + * @gfp_flags: Memory allocation flags + * + * Encrypt a possibly-compressed filesystem block that is located in an + * arbitrary page, not necessarily in the original pagecache page. The @inode + * and @lblk_num must be specified, as they can't be determined from @page. + * + * Return: 0 on success; -errno on failure + */ +int fscrypt_encrypt_block_inplace(const struct inode *inode, struct page *page, + unsigned int len, unsigned int offs, + u64 lblk_num, gfp_t gfp_flags) +{ + return fscrypt_crypt_block(inode, FS_ENCRYPT, lblk_num, page, page, + len, offs, gfp_flags); +} +EXPORT_SYMBOL(fscrypt_encrypt_block_inplace); + /** * fscrypt_decrypt_page() - Decrypts a page in-place * @inode: The corresponding inode for the page to decrypt. diff --git a/fs/ubifs/crypto.c b/fs/ubifs/crypto.c index 4aaedf2d7f44..032efdad2e66 100644 --- a/fs/ubifs/crypto.c +++ b/fs/ubifs/crypto.c @@ -29,8 +29,8 @@ int ubifs_encrypt(const struct inode *inode, struct ubifs_data_node *dn, { struct ubifs_info *c = inode->i_sb->s_fs_info; void *p = &dn->data; - struct page *ret; unsigned int pad_len = round_up(in_len, UBIFS_CIPHER_BLOCK_SIZE); + int err; ubifs_assert(c, pad_len <= *out_len); dn->compr_size = cpu_to_le16(in_len); @@ -39,11 +39,11 @@ int ubifs_encrypt(const struct inode *inode, struct ubifs_data_node *dn, if (pad_len != in_len) memset(p + in_len, 0, pad_len - in_len); - ret = fscrypt_encrypt_page(inode, virt_to_page(&dn->data), pad_len, - offset_in_page(&dn->data), block, GFP_NOFS); - if (IS_ERR(ret)) { - ubifs_err(c, "fscrypt_encrypt_page failed: %ld", PTR_ERR(ret)); - return PTR_ERR(ret); + err = fscrypt_encrypt_block_inplace(inode, virt_to_page(p), pad_len, + offset_in_page(p), block, GFP_NOFS); + if (err) { + ubifs_err(c, "fscrypt_encrypt_block_inplace() failed: %d", err); + return err; } *out_len = pad_len; diff --git a/include/linux/fscrypt.h b/include/linux/fscrypt.h index 1c7287f146a9..a9b2d26e615d 100644 --- a/include/linux/fscrypt.h +++ b/include/linux/fscrypt.h @@ -106,6 +106,10 @@ extern void fscrypt_release_ctx(struct fscrypt_ctx *); extern struct page *fscrypt_encrypt_page(const struct inode *, struct page *, unsigned int, unsigned int, u64, gfp_t); +extern int fscrypt_encrypt_block_inplace(const struct inode *inode, + struct page *page, unsigned int len, + unsigned int offs, u64 lblk_num, + gfp_t gfp_flags); extern int fscrypt_decrypt_page(const struct inode *, struct page *, unsigned int, unsigned int, u64); @@ -293,6 +297,15 @@ static inline struct page *fscrypt_encrypt_page(const struct inode *inode, return ERR_PTR(-EOPNOTSUPP); } +static inline int fscrypt_encrypt_block_inplace(const struct inode *inode, + struct page *page, + unsigned int len, + unsigned int offs, u64 lblk_num, + gfp_t gfp_flags) +{ + return -EOPNOTSUPP; +} + static inline int fscrypt_decrypt_page(const struct inode *inode, struct page *page, unsigned int len, unsigned int offs, From 53bc1d854c64c20d967dab15b111baca02a6d99e Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Mon, 20 May 2019 09:29:44 -0700 Subject: [PATCH 06/18] fscrypt: support encrypting multiple filesystem blocks per page Rename fscrypt_encrypt_page() to fscrypt_encrypt_pagecache_blocks() and redefine its behavior to encrypt all filesystem blocks from the given region of the given page, rather than assuming that the region consists of just one filesystem block. Also remove the 'inode' and 'lblk_num' parameters, since they can be retrieved from the page as it's already assumed to be a pagecache page. This is in preparation for allowing encryption on ext4 filesystems with blocksize != PAGE_SIZE. This is based on work by Chandan Rajendra. Reviewed-by: Chandan Rajendra Signed-off-by: Eric Biggers --- fs/crypto/crypto.c | 67 ++++++++++++++++++++++++----------------- fs/ext4/page-io.c | 4 +-- fs/f2fs/data.c | 5 +-- include/linux/fscrypt.h | 17 ++++++----- 4 files changed, 53 insertions(+), 40 deletions(-) diff --git a/fs/crypto/crypto.c b/fs/crypto/crypto.c index 2969a1dff10b..ff43a13c3abf 100644 --- a/fs/crypto/crypto.c +++ b/fs/crypto/crypto.c @@ -122,8 +122,8 @@ struct page *fscrypt_alloc_bounce_page(gfp_t gfp_flags) /** * fscrypt_free_bounce_page() - free a ciphertext bounce page * - * Free a bounce page that was allocated by fscrypt_encrypt_page(), or by - * fscrypt_alloc_bounce_page() directly. + * Free a bounce page that was allocated by fscrypt_encrypt_pagecache_blocks(), + * or by fscrypt_alloc_bounce_page() directly. */ void fscrypt_free_bounce_page(struct page *bounce_page) { @@ -198,52 +198,63 @@ int fscrypt_crypt_block(const struct inode *inode, fscrypt_direction_t rw, } /** - * fscypt_encrypt_page() - Encrypts a page - * @inode: The inode for which the encryption should take place - * @page: The page to encrypt. Must be locked. - * @len: Length of data to encrypt in @page and encrypted - * data in returned page. - * @offs: Offset of data within @page and returned - * page holding encrypted data. - * @lblk_num: Logical block number. This must be unique for multiple - * calls with same inode, except when overwriting - * previously written data. - * @gfp_flags: The gfp flag for memory allocation + * fscrypt_encrypt_pagecache_blocks() - Encrypt filesystem blocks from a pagecache page + * @page: The locked pagecache page containing the block(s) to encrypt + * @len: Total size of the block(s) to encrypt. Must be a nonzero + * multiple of the filesystem's block size. + * @offs: Byte offset within @page of the first block to encrypt. Must be + * a multiple of the filesystem's block size. + * @gfp_flags: Memory allocation flags * - * Encrypts @page. A bounce page is allocated, the data is encrypted into the - * bounce page, and the bounce page is returned. The caller is responsible for - * calling fscrypt_free_bounce_page(). + * A new bounce page is allocated, and the specified block(s) are encrypted into + * it. In the bounce page, the ciphertext block(s) will be located at the same + * offsets at which the plaintext block(s) were located in the source page; any + * other parts of the bounce page will be left uninitialized. However, normally + * blocksize == PAGE_SIZE and the whole page is encrypted at once. * - * Return: A page containing the encrypted data on success, else an ERR_PTR() + * This is for use by the filesystem's ->writepages() method. + * + * Return: the new encrypted bounce page on success; an ERR_PTR() on failure */ -struct page *fscrypt_encrypt_page(const struct inode *inode, - struct page *page, - unsigned int len, - unsigned int offs, - u64 lblk_num, gfp_t gfp_flags) +struct page *fscrypt_encrypt_pagecache_blocks(struct page *page, + unsigned int len, + unsigned int offs, + gfp_t gfp_flags) { + const struct inode *inode = page->mapping->host; + const unsigned int blockbits = inode->i_blkbits; + const unsigned int blocksize = 1 << blockbits; struct page *ciphertext_page; + u64 lblk_num = ((u64)page->index << (PAGE_SHIFT - blockbits)) + + (offs >> blockbits); + unsigned int i; int err; if (WARN_ON_ONCE(!PageLocked(page))) return ERR_PTR(-EINVAL); + if (WARN_ON_ONCE(len <= 0 || !IS_ALIGNED(len | offs, blocksize))) + return ERR_PTR(-EINVAL); + ciphertext_page = fscrypt_alloc_bounce_page(gfp_flags); if (!ciphertext_page) return ERR_PTR(-ENOMEM); - err = fscrypt_crypt_block(inode, FS_ENCRYPT, lblk_num, page, - ciphertext_page, len, offs, gfp_flags); - if (err) { - fscrypt_free_bounce_page(ciphertext_page); - return ERR_PTR(err); + for (i = offs; i < offs + len; i += blocksize, lblk_num++) { + err = fscrypt_crypt_block(inode, FS_ENCRYPT, lblk_num, + page, ciphertext_page, + blocksize, i, gfp_flags); + if (err) { + fscrypt_free_bounce_page(ciphertext_page); + return ERR_PTR(err); + } } SetPagePrivate(ciphertext_page); set_page_private(ciphertext_page, (unsigned long)page); return ciphertext_page; } -EXPORT_SYMBOL(fscrypt_encrypt_page); +EXPORT_SYMBOL(fscrypt_encrypt_pagecache_blocks); /** * fscrypt_encrypt_block_inplace() - Encrypt a filesystem block in-place diff --git a/fs/ext4/page-io.c b/fs/ext4/page-io.c index 13d5ecc0af03..40ee33df5764 100644 --- a/fs/ext4/page-io.c +++ b/fs/ext4/page-io.c @@ -471,8 +471,8 @@ int ext4_bio_write_page(struct ext4_io_submit *io, gfp_t gfp_flags = GFP_NOFS; retry_encrypt: - bounce_page = fscrypt_encrypt_page(inode, page, PAGE_SIZE, 0, - page->index, gfp_flags); + bounce_page = fscrypt_encrypt_pagecache_blocks(page, PAGE_SIZE, + 0, gfp_flags); if (IS_ERR(bounce_page)) { ret = PTR_ERR(bounce_page); if (ret == -ENOMEM && wbc->sync_mode == WB_SYNC_ALL) { diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 968ebdbcb583..a546ac8685ea 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -1726,8 +1726,9 @@ static int encrypt_one_page(struct f2fs_io_info *fio) f2fs_wait_on_block_writeback(inode, fio->old_blkaddr); retry_encrypt: - fio->encrypted_page = fscrypt_encrypt_page(inode, fio->page, - PAGE_SIZE, 0, fio->page->index, gfp_flags); + fio->encrypted_page = fscrypt_encrypt_pagecache_blocks(fio->page, + PAGE_SIZE, 0, + gfp_flags); if (IS_ERR(fio->encrypted_page)) { /* flush pending IOs and wait for a while in the ENOMEM case */ if (PTR_ERR(fio->encrypted_page) == -ENOMEM) { diff --git a/include/linux/fscrypt.h b/include/linux/fscrypt.h index a9b2d26e615d..c7e16bd16a6c 100644 --- a/include/linux/fscrypt.h +++ b/include/linux/fscrypt.h @@ -103,9 +103,11 @@ static inline void fscrypt_handle_d_move(struct dentry *dentry) extern void fscrypt_enqueue_decrypt_work(struct work_struct *); extern struct fscrypt_ctx *fscrypt_get_ctx(gfp_t); extern void fscrypt_release_ctx(struct fscrypt_ctx *); -extern struct page *fscrypt_encrypt_page(const struct inode *, struct page *, - unsigned int, unsigned int, - u64, gfp_t); + +extern struct page *fscrypt_encrypt_pagecache_blocks(struct page *page, + unsigned int len, + unsigned int offs, + gfp_t gfp_flags); extern int fscrypt_encrypt_block_inplace(const struct inode *inode, struct page *page, unsigned int len, unsigned int offs, u64 lblk_num, @@ -288,11 +290,10 @@ static inline void fscrypt_release_ctx(struct fscrypt_ctx *ctx) return; } -static inline struct page *fscrypt_encrypt_page(const struct inode *inode, - struct page *page, - unsigned int len, - unsigned int offs, - u64 lblk_num, gfp_t gfp_flags) +static inline struct page *fscrypt_encrypt_pagecache_blocks(struct page *page, + unsigned int len, + unsigned int offs, + gfp_t gfp_flags) { return ERR_PTR(-EOPNOTSUPP); } From 930d453995bdf4a0000bd162dfd4b70c6d7329f8 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Mon, 20 May 2019 09:29:45 -0700 Subject: [PATCH 07/18] fscrypt: handle blocksize < PAGE_SIZE in fscrypt_zeroout_range() Adjust fscrypt_zeroout_range() to encrypt a block at a time rather than a page at a time, so that it works when blocksize < PAGE_SIZE. This isn't optimized for performance, but then again this function already wasn't optimized for performance. As a future optimization, we could submit much larger bios here. This is in preparation for allowing encryption on ext4 filesystems with blocksize != PAGE_SIZE. This is based on work by Chandan Rajendra. Reviewed-by: Chandan Rajendra Signed-off-by: Eric Biggers --- fs/crypto/bio.c | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/fs/crypto/bio.c b/fs/crypto/bio.c index 92b2d5da5d8e..f9111ffa12ff 100644 --- a/fs/crypto/bio.c +++ b/fs/crypto/bio.c @@ -72,12 +72,12 @@ EXPORT_SYMBOL(fscrypt_enqueue_decrypt_bio); int fscrypt_zeroout_range(const struct inode *inode, pgoff_t lblk, sector_t pblk, unsigned int len) { + const unsigned int blockbits = inode->i_blkbits; + const unsigned int blocksize = 1 << blockbits; struct page *ciphertext_page; struct bio *bio; int ret, err = 0; - BUG_ON(inode->i_sb->s_blocksize != PAGE_SIZE); - ciphertext_page = fscrypt_alloc_bounce_page(GFP_NOWAIT); if (!ciphertext_page) return -ENOMEM; @@ -85,7 +85,7 @@ int fscrypt_zeroout_range(const struct inode *inode, pgoff_t lblk, while (len--) { err = fscrypt_crypt_block(inode, FS_ENCRYPT, lblk, ZERO_PAGE(0), ciphertext_page, - PAGE_SIZE, 0, GFP_NOFS); + blocksize, 0, GFP_NOFS); if (err) goto errout; @@ -95,14 +95,11 @@ int fscrypt_zeroout_range(const struct inode *inode, pgoff_t lblk, goto errout; } bio_set_dev(bio, inode->i_sb->s_bdev); - bio->bi_iter.bi_sector = - pblk << (inode->i_sb->s_blocksize_bits - 9); + bio->bi_iter.bi_sector = pblk << (blockbits - 9); bio_set_op_attrs(bio, REQ_OP_WRITE, 0); - ret = bio_add_page(bio, ciphertext_page, - inode->i_sb->s_blocksize, 0); - if (ret != inode->i_sb->s_blocksize) { + ret = bio_add_page(bio, ciphertext_page, blocksize, 0); + if (WARN_ON(ret != blocksize)) { /* should never happen! */ - WARN_ON(1); bio_put(bio); err = -EIO; goto errout; From 41adbcb7267b0060682576d523956160b5c617bd Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Mon, 20 May 2019 09:29:46 -0700 Subject: [PATCH 08/18] fscrypt: introduce fscrypt_decrypt_block_inplace() Currently fscrypt_decrypt_page() does one of two logically distinct things depending on whether FS_CFLG_OWN_PAGES is set in the filesystem's fscrypt_operations: decrypt a pagecache page in-place, or decrypt a filesystem block in-place in any page. Currently these happen to share the same implementation, but this conflates the notion of blocks and pages. It also makes it so that all callers have to provide inode and lblk_num, when fscrypt could determine these itself for pagecache pages. Therefore, move the FS_CFLG_OWN_PAGES behavior into a new function fscrypt_decrypt_block_inplace(). This mirrors fscrypt_encrypt_block_inplace(). This is in preparation for allowing encryption on ext4 filesystems with blocksize != PAGE_SIZE. Reviewed-by: Chandan Rajendra Signed-off-by: Eric Biggers --- fs/crypto/crypto.c | 31 +++++++++++++++++++++++++++---- fs/ubifs/crypto.c | 7 ++++--- include/linux/fscrypt.h | 11 +++++++++++ 3 files changed, 42 insertions(+), 7 deletions(-) diff --git a/fs/crypto/crypto.c b/fs/crypto/crypto.c index ff43a13c3abf..f82c45ac285a 100644 --- a/fs/crypto/crypto.c +++ b/fs/crypto/crypto.c @@ -285,8 +285,7 @@ EXPORT_SYMBOL(fscrypt_encrypt_block_inplace); /** * fscrypt_decrypt_page() - Decrypts a page in-place * @inode: The corresponding inode for the page to decrypt. - * @page: The page to decrypt. Must be locked in case - * it is a writeback page (FS_CFLG_OWN_PAGES unset). + * @page: The page to decrypt. Must be locked. * @len: Number of bytes in @page to be decrypted. * @offs: Start of data in @page. * @lblk_num: Logical block number. @@ -300,8 +299,7 @@ EXPORT_SYMBOL(fscrypt_encrypt_block_inplace); int fscrypt_decrypt_page(const struct inode *inode, struct page *page, unsigned int len, unsigned int offs, u64 lblk_num) { - if (WARN_ON_ONCE(!PageLocked(page) && - !(inode->i_sb->s_cop->flags & FS_CFLG_OWN_PAGES))) + if (WARN_ON_ONCE(!PageLocked(page))) return -EINVAL; return fscrypt_crypt_block(inode, FS_DECRYPT, lblk_num, page, page, @@ -309,6 +307,31 @@ int fscrypt_decrypt_page(const struct inode *inode, struct page *page, } EXPORT_SYMBOL(fscrypt_decrypt_page); +/** + * fscrypt_decrypt_block_inplace() - Decrypt a filesystem block in-place + * @inode: The inode to which this block belongs + * @page: The page containing the block to decrypt + * @len: Size of block to decrypt. Doesn't need to be a multiple of the + * fs block size, but must be a multiple of FS_CRYPTO_BLOCK_SIZE. + * @offs: Byte offset within @page at which the block to decrypt begins + * @lblk_num: Filesystem logical block number of the block, i.e. the 0-based + * number of the block within the file + * + * Decrypt a possibly-compressed filesystem block that is located in an + * arbitrary page, not necessarily in the original pagecache page. The @inode + * and @lblk_num must be specified, as they can't be determined from @page. + * + * Return: 0 on success; -errno on failure + */ +int fscrypt_decrypt_block_inplace(const struct inode *inode, struct page *page, + unsigned int len, unsigned int offs, + u64 lblk_num) +{ + return fscrypt_crypt_block(inode, FS_DECRYPT, lblk_num, page, page, + len, offs, GFP_NOFS); +} +EXPORT_SYMBOL(fscrypt_decrypt_block_inplace); + /* * Validate dentries in encrypted directories to make sure we aren't potentially * caching stale dentries after a key has been added. diff --git a/fs/ubifs/crypto.c b/fs/ubifs/crypto.c index 032efdad2e66..22be7aeb96c4 100644 --- a/fs/ubifs/crypto.c +++ b/fs/ubifs/crypto.c @@ -64,10 +64,11 @@ int ubifs_decrypt(const struct inode *inode, struct ubifs_data_node *dn, } ubifs_assert(c, dlen <= UBIFS_BLOCK_SIZE); - err = fscrypt_decrypt_page(inode, virt_to_page(&dn->data), dlen, - offset_in_page(&dn->data), block); + err = fscrypt_decrypt_block_inplace(inode, virt_to_page(&dn->data), + dlen, offset_in_page(&dn->data), + block); if (err) { - ubifs_err(c, "fscrypt_decrypt_page failed: %i", err); + ubifs_err(c, "fscrypt_decrypt_block_inplace() failed: %d", err); return err; } *out_len = clen; diff --git a/include/linux/fscrypt.h b/include/linux/fscrypt.h index c7e16bd16a6c..315affc99b05 100644 --- a/include/linux/fscrypt.h +++ b/include/linux/fscrypt.h @@ -114,6 +114,9 @@ extern int fscrypt_encrypt_block_inplace(const struct inode *inode, gfp_t gfp_flags); extern int fscrypt_decrypt_page(const struct inode *, struct page *, unsigned int, unsigned int, u64); +extern int fscrypt_decrypt_block_inplace(const struct inode *inode, + struct page *page, unsigned int len, + unsigned int offs, u64 lblk_num); static inline bool fscrypt_is_bounce_page(struct page *page) { @@ -315,6 +318,14 @@ static inline int fscrypt_decrypt_page(const struct inode *inode, return -EOPNOTSUPP; } +static inline int fscrypt_decrypt_block_inplace(const struct inode *inode, + struct page *page, + unsigned int len, + unsigned int offs, u64 lblk_num) +{ + return -EOPNOTSUPP; +} + static inline bool fscrypt_is_bounce_page(struct page *page) { return false; From aa8bc1ac6ef32a332671ca25e06cfd277a3839a5 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Mon, 20 May 2019 09:29:47 -0700 Subject: [PATCH 09/18] fscrypt: support decrypting multiple filesystem blocks per page Rename fscrypt_decrypt_page() to fscrypt_decrypt_pagecache_blocks() and redefine its behavior to decrypt all filesystem blocks in the given region of the given page, rather than assuming that the region consists of just one filesystem block. Also remove the 'inode' and 'lblk_num' parameters, since they can be retrieved from the page as it's already assumed to be a pagecache page. This is in preparation for allowing encryption on ext4 filesystems with blocksize != PAGE_SIZE. This is based on work by Chandan Rajendra. Reviewed-by: Chandan Rajendra Signed-off-by: Eric Biggers --- fs/crypto/bio.c | 3 +-- fs/crypto/crypto.c | 46 ++++++++++++++++++++++++++++------------- fs/ext4/inode.c | 7 +++---- include/linux/fscrypt.h | 12 +++++------ 4 files changed, 42 insertions(+), 26 deletions(-) diff --git a/fs/crypto/bio.c b/fs/crypto/bio.c index f9111ffa12ff..61da06fda45c 100644 --- a/fs/crypto/bio.c +++ b/fs/crypto/bio.c @@ -33,8 +33,7 @@ static void __fscrypt_decrypt_bio(struct bio *bio, bool done) bio_for_each_segment_all(bv, bio, iter_all) { struct page *page = bv->bv_page; - int ret = fscrypt_decrypt_page(page->mapping->host, page, - PAGE_SIZE, 0, page->index); + int ret = fscrypt_decrypt_pagecache_blocks(page, PAGE_SIZE, 0); if (ret) SetPageError(page); diff --git a/fs/crypto/crypto.c b/fs/crypto/crypto.c index f82c45ac285a..45c3d0427fb2 100644 --- a/fs/crypto/crypto.c +++ b/fs/crypto/crypto.c @@ -283,29 +283,47 @@ int fscrypt_encrypt_block_inplace(const struct inode *inode, struct page *page, EXPORT_SYMBOL(fscrypt_encrypt_block_inplace); /** - * fscrypt_decrypt_page() - Decrypts a page in-place - * @inode: The corresponding inode for the page to decrypt. - * @page: The page to decrypt. Must be locked. - * @len: Number of bytes in @page to be decrypted. - * @offs: Start of data in @page. - * @lblk_num: Logical block number. + * fscrypt_decrypt_pagecache_blocks() - Decrypt filesystem blocks in a pagecache page + * @page: The locked pagecache page containing the block(s) to decrypt + * @len: Total size of the block(s) to decrypt. Must be a nonzero + * multiple of the filesystem's block size. + * @offs: Byte offset within @page of the first block to decrypt. Must be + * a multiple of the filesystem's block size. * - * Decrypts page in-place using the ctx encryption context. + * The specified block(s) are decrypted in-place within the pagecache page, + * which must still be locked and not uptodate. Normally, blocksize == + * PAGE_SIZE and the whole page is decrypted at once. * - * Called from the read completion callback. + * This is for use by the filesystem's ->readpages() method. * - * Return: Zero on success, non-zero otherwise. + * Return: 0 on success; -errno on failure */ -int fscrypt_decrypt_page(const struct inode *inode, struct page *page, - unsigned int len, unsigned int offs, u64 lblk_num) +int fscrypt_decrypt_pagecache_blocks(struct page *page, unsigned int len, + unsigned int offs) { + const struct inode *inode = page->mapping->host; + const unsigned int blockbits = inode->i_blkbits; + const unsigned int blocksize = 1 << blockbits; + u64 lblk_num = ((u64)page->index << (PAGE_SHIFT - blockbits)) + + (offs >> blockbits); + unsigned int i; + int err; + if (WARN_ON_ONCE(!PageLocked(page))) return -EINVAL; - return fscrypt_crypt_block(inode, FS_DECRYPT, lblk_num, page, page, - len, offs, GFP_NOFS); + if (WARN_ON_ONCE(len <= 0 || !IS_ALIGNED(len | offs, blocksize))) + return -EINVAL; + + for (i = offs; i < offs + len; i += blocksize, lblk_num++) { + err = fscrypt_crypt_block(inode, FS_DECRYPT, lblk_num, page, + page, blocksize, i, GFP_NOFS); + if (err) + return err; + } + return 0; } -EXPORT_SYMBOL(fscrypt_decrypt_page); +EXPORT_SYMBOL(fscrypt_decrypt_pagecache_blocks); /** * fscrypt_decrypt_block_inplace() - Decrypt a filesystem block in-place diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index c7f77c643008..8bfd8941f5ff 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -1232,8 +1232,7 @@ static int ext4_block_write_begin(struct page *page, loff_t pos, unsigned len, if (unlikely(err)) page_zero_new_buffers(page, from, to); else if (decrypt) - err = fscrypt_decrypt_page(page->mapping->host, page, - PAGE_SIZE, 0, page->index); + err = fscrypt_decrypt_pagecache_blocks(page, PAGE_SIZE, 0); return err; } #endif @@ -4066,8 +4065,8 @@ static int __ext4_block_zero_page_range(handle_t *handle, /* We expect the key to be set. */ BUG_ON(!fscrypt_has_encryption_key(inode)); BUG_ON(blocksize != PAGE_SIZE); - WARN_ON_ONCE(fscrypt_decrypt_page(page->mapping->host, - page, PAGE_SIZE, 0, page->index)); + WARN_ON_ONCE(fscrypt_decrypt_pagecache_blocks( + page, PAGE_SIZE, 0)); } } if (ext4_should_journal_data(inode)) { diff --git a/include/linux/fscrypt.h b/include/linux/fscrypt.h index 315affc99b05..bd8f207a2fb6 100644 --- a/include/linux/fscrypt.h +++ b/include/linux/fscrypt.h @@ -112,8 +112,9 @@ extern int fscrypt_encrypt_block_inplace(const struct inode *inode, struct page *page, unsigned int len, unsigned int offs, u64 lblk_num, gfp_t gfp_flags); -extern int fscrypt_decrypt_page(const struct inode *, struct page *, unsigned int, - unsigned int, u64); + +extern int fscrypt_decrypt_pagecache_blocks(struct page *page, unsigned int len, + unsigned int offs); extern int fscrypt_decrypt_block_inplace(const struct inode *inode, struct page *page, unsigned int len, unsigned int offs, u64 lblk_num); @@ -310,10 +311,9 @@ static inline int fscrypt_encrypt_block_inplace(const struct inode *inode, return -EOPNOTSUPP; } -static inline int fscrypt_decrypt_page(const struct inode *inode, - struct page *page, - unsigned int len, unsigned int offs, - u64 lblk_num) +static inline int fscrypt_decrypt_pagecache_blocks(struct page *page, + unsigned int len, + unsigned int offs) { return -EOPNOTSUPP; } From ffceeefb337b3ba9da36633072490caa96289345 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Mon, 20 May 2019 09:29:48 -0700 Subject: [PATCH 10/18] fscrypt: decrypt only the needed blocks in __fscrypt_decrypt_bio() In __fscrypt_decrypt_bio(), only decrypt the blocks that actually comprise the bio, rather than assuming blocksize == PAGE_SIZE and decrypting the entirety of every page used in the bio. This is in preparation for allowing encryption on ext4 filesystems with blocksize != PAGE_SIZE. This is based on work by Chandan Rajendra. Reviewed-by: Chandan Rajendra Signed-off-by: Eric Biggers --- fs/crypto/bio.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/crypto/bio.c b/fs/crypto/bio.c index 61da06fda45c..82da2510721f 100644 --- a/fs/crypto/bio.c +++ b/fs/crypto/bio.c @@ -33,8 +33,8 @@ static void __fscrypt_decrypt_bio(struct bio *bio, bool done) bio_for_each_segment_all(bv, bio, iter_all) { struct page *page = bv->bv_page; - int ret = fscrypt_decrypt_pagecache_blocks(page, PAGE_SIZE, 0); - + int ret = fscrypt_decrypt_pagecache_blocks(page, bv->bv_len, + bv->bv_offset); if (ret) SetPageError(page); else if (done) From 7e0785fce14f75976a80b241d732e210e380923e Mon Sep 17 00:00:00 2001 From: Chandan Rajendra Date: Mon, 20 May 2019 09:29:49 -0700 Subject: [PATCH 11/18] ext4: clear BH_Uptodate flag on decryption error If decryption fails, ext4_block_write_begin() can return with the page's buffer_head marked with the BH_Uptodate flag. This commit clears the BH_Uptodate flag in such cases. Signed-off-by: Chandan Rajendra Signed-off-by: Eric Biggers --- fs/ext4/inode.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 8bfd8941f5ff..92776d0ff9b9 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -1229,10 +1229,14 @@ static int ext4_block_write_begin(struct page *page, loff_t pos, unsigned len, if (!buffer_uptodate(*wait_bh)) err = -EIO; } - if (unlikely(err)) + if (unlikely(err)) { page_zero_new_buffers(page, from, to); - else if (decrypt) + } else if (decrypt) { err = fscrypt_decrypt_pagecache_blocks(page, PAGE_SIZE, 0); + if (err) + clear_buffer_uptodate(*wait_bh); + } + return err; } #endif From 0b578f358a6a7afee2ddc48dd39c2972726187de Mon Sep 17 00:00:00 2001 From: Chandan Rajendra Date: Mon, 20 May 2019 09:29:50 -0700 Subject: [PATCH 12/18] ext4: decrypt only the needed blocks in ext4_block_write_begin() In ext4_block_write_begin(), only decrypt the blocks that actually need to be decrypted (up to two blocks which intersect the boundaries of the region that will be written to), rather than assuming blocksize == PAGE_SIZE and decrypting the whole page. This is in preparation for allowing encryption on ext4 filesystems with blocksize != PAGE_SIZE. Signed-off-by: Chandan Rajendra (EB: rebase onto previous changes, improve the commit message, and move the check for encrypted inode) Signed-off-by: Eric Biggers --- fs/ext4/inode.c | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 92776d0ff9b9..8e48feddad83 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -1164,8 +1164,9 @@ static int ext4_block_write_begin(struct page *page, loff_t pos, unsigned len, int err = 0; unsigned blocksize = inode->i_sb->s_blocksize; unsigned bbits; - struct buffer_head *bh, *head, *wait[2], **wait_bh = wait; - bool decrypt = false; + struct buffer_head *bh, *head, *wait[2]; + int nr_wait = 0; + int i; BUG_ON(!PageLocked(page)); BUG_ON(from > PAGE_SIZE); @@ -1217,24 +1218,30 @@ static int ext4_block_write_begin(struct page *page, loff_t pos, unsigned len, !buffer_unwritten(bh) && (block_start < from || block_end > to)) { ll_rw_block(REQ_OP_READ, 0, 1, &bh); - *wait_bh++ = bh; - decrypt = IS_ENCRYPTED(inode) && S_ISREG(inode->i_mode); + wait[nr_wait++] = bh; } } /* * If we issued read requests, let them complete. */ - while (wait_bh > wait) { - wait_on_buffer(*--wait_bh); - if (!buffer_uptodate(*wait_bh)) + for (i = 0; i < nr_wait; i++) { + wait_on_buffer(wait[i]); + if (!buffer_uptodate(wait[i])) err = -EIO; } if (unlikely(err)) { page_zero_new_buffers(page, from, to); - } else if (decrypt) { - err = fscrypt_decrypt_pagecache_blocks(page, PAGE_SIZE, 0); - if (err) - clear_buffer_uptodate(*wait_bh); + } else if (IS_ENCRYPTED(inode) && S_ISREG(inode->i_mode)) { + for (i = 0; i < nr_wait; i++) { + int err2; + + err2 = fscrypt_decrypt_pagecache_blocks(page, blocksize, + bh_offset(wait[i])); + if (err2) { + clear_buffer_uptodate(wait[i]); + err = err2; + } + } } return err; From ec39a36867440995c9675b2800f5ddaeb51b024e Mon Sep 17 00:00:00 2001 From: Chandan Rajendra Date: Mon, 20 May 2019 09:29:51 -0700 Subject: [PATCH 13/18] ext4: decrypt only the needed block in __ext4_block_zero_page_range() In __ext4_block_zero_page_range(), only decrypt the block that actually needs to be decrypted, rather than assuming blocksize == PAGE_SIZE and decrypting the whole page. This is in preparation for allowing encryption on ext4 filesystems with blocksize != PAGE_SIZE. Signed-off-by: Chandan Rajendra (EB: rebase onto previous changes, improve the commit message, and use bh_offset()) Signed-off-by: Eric Biggers --- fs/ext4/inode.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 8e48feddad83..f65357735a1a 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -4075,9 +4075,8 @@ static int __ext4_block_zero_page_range(handle_t *handle, if (S_ISREG(inode->i_mode) && IS_ENCRYPTED(inode)) { /* We expect the key to be set. */ BUG_ON(!fscrypt_has_encryption_key(inode)); - BUG_ON(blocksize != PAGE_SIZE); WARN_ON_ONCE(fscrypt_decrypt_pagecache_blocks( - page, PAGE_SIZE, 0)); + page, blocksize, bh_offset(bh))); } } if (ext4_should_journal_data(inode)) { From 6e4b73bcd1519d50680d92ba74887c80c4e59140 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Mon, 20 May 2019 09:29:52 -0700 Subject: [PATCH 14/18] ext4: encrypt only up to last block in ext4_bio_write_page() As an optimization, don't encrypt blocks fully beyond i_size, since those definitely won't need to be written out. Also add a comment. This is in preparation for allowing encryption on ext4 filesystems with blocksize != PAGE_SIZE. This is based on work by Chandan Rajendra. Reviewed-by: Chandan Rajendra Signed-off-by: Eric Biggers --- fs/ext4/page-io.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/fs/ext4/page-io.c b/fs/ext4/page-io.c index 40ee33df5764..a18a47a2a1d1 100644 --- a/fs/ext4/page-io.c +++ b/fs/ext4/page-io.c @@ -467,11 +467,19 @@ int ext4_bio_write_page(struct ext4_io_submit *io, bh = head = page_buffers(page); + /* + * If any blocks are being written to an encrypted file, encrypt them + * into a bounce page. For simplicity, just encrypt until the last + * block which might be needed. This may cause some unneeded blocks + * (e.g. holes) to be unnecessarily encrypted, but this is rare and + * can't happen in the common case of blocksize == PAGE_SIZE. + */ if (IS_ENCRYPTED(inode) && S_ISREG(inode->i_mode) && nr_to_submit) { gfp_t gfp_flags = GFP_NOFS; + unsigned int enc_bytes = round_up(len, i_blocksize(inode)); retry_encrypt: - bounce_page = fscrypt_encrypt_pagecache_blocks(page, PAGE_SIZE, + bounce_page = fscrypt_encrypt_pagecache_blocks(page, enc_bytes, 0, gfp_flags); if (IS_ERR(bounce_page)) { ret = PTR_ERR(bounce_page); From 5858bdad4d0d0fc18bf29f34c3ac836e0b59441f Mon Sep 17 00:00:00 2001 From: Hongjie Fang Date: Wed, 22 May 2019 10:02:53 +0800 Subject: [PATCH 15/18] fscrypt: don't set policy for a dead directory The directory may have been removed when entering fscrypt_ioctl_set_policy(). If so, the empty_dir() check will return error for ext4 file system. ext4_rmdir() sets i_size = 0, then ext4_empty_dir() reports an error because 'inode->i_size < EXT4_DIR_REC_LEN(1) + EXT4_DIR_REC_LEN(2)'. If the fs is mounted with errors=panic, it will trigger a panic issue. Add the check IS_DEADDIR() to fix this problem. Fixes: 9bd8212f981e ("ext4 crypto: add encryption policy and password salt support") Cc: # v4.1+ Signed-off-by: Hongjie Fang Signed-off-by: Eric Biggers --- fs/crypto/policy.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/crypto/policy.c b/fs/crypto/policy.c index d536889ac31b..4941fe8471ce 100644 --- a/fs/crypto/policy.c +++ b/fs/crypto/policy.c @@ -81,6 +81,8 @@ int fscrypt_ioctl_set_policy(struct file *filp, const void __user *arg) if (ret == -ENODATA) { if (!S_ISDIR(inode->i_mode)) ret = -ENOTDIR; + else if (IS_DEADDIR(inode)) + ret = -ENOENT; else if (!inode->i_sb->s_cop->empty_dir(inode)) ret = -ENOTEMPTY; else From 0bb06cac060dd033153277a0d9dab9fd8aa455a2 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Tue, 28 May 2019 12:59:08 -0700 Subject: [PATCH 16/18] fscrypt: remove unnecessary includes of ratelimit.h These should have been removed during commit 544d08fde258 ("fscrypt: use a common logging function"), but I missed them. Signed-off-by: Eric Biggers --- fs/crypto/fname.c | 1 - fs/crypto/hooks.c | 1 - fs/crypto/keyinfo.c | 1 - 3 files changed, 3 deletions(-) diff --git a/fs/crypto/fname.c b/fs/crypto/fname.c index eccea3d8f923..00d150ff3033 100644 --- a/fs/crypto/fname.c +++ b/fs/crypto/fname.c @@ -12,7 +12,6 @@ */ #include -#include #include #include "fscrypt_private.h" diff --git a/fs/crypto/hooks.c b/fs/crypto/hooks.c index bd525f7573a4..c1d6715d88e9 100644 --- a/fs/crypto/hooks.c +++ b/fs/crypto/hooks.c @@ -5,7 +5,6 @@ * Encryption hooks for higher-level filesystem operations. */ -#include #include "fscrypt_private.h" /** diff --git a/fs/crypto/keyinfo.c b/fs/crypto/keyinfo.c index dcd91a3fbe49..207ebed918c1 100644 --- a/fs/crypto/keyinfo.c +++ b/fs/crypto/keyinfo.c @@ -12,7 +12,6 @@ #include #include #include -#include #include #include #include From adbd9b4dee70c36eaa30ce93ffcd968533044efc Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Thu, 20 Jun 2019 11:15:05 -0700 Subject: [PATCH 17/18] fscrypt: remove selection of CONFIG_CRYPTO_SHA256 fscrypt only uses SHA-256 for AES-128-CBC-ESSIV, which isn't the default and is only recommended on platforms that have hardware accelerated AES-CBC but not AES-XTS. There's no link-time dependency, since SHA-256 is requested via the crypto API on first use. To reduce bloat, we should limit FS_ENCRYPTION to selecting the default algorithms only. SHA-256 by itself isn't that much bloat, but it's being discussed to move ESSIV into a crypto API template, which would incidentally bring in other things like "authenc" support, which would all end up being built-in since FS_ENCRYPTION is now a bool. For Adiantum encryption we already just document that users who want to use it have to enable CONFIG_CRYPTO_ADIANTUM themselves. So, let's do the same for AES-128-CBC-ESSIV and CONFIG_CRYPTO_SHA256. Acked-by: Ard Biesheuvel Reviewed-by: Theodore Ts'o Signed-off-by: Eric Biggers --- Documentation/filesystems/fscrypt.rst | 4 +++- fs/crypto/Kconfig | 1 - 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Documentation/filesystems/fscrypt.rst b/Documentation/filesystems/fscrypt.rst index 08c23b60e016..87d4e266ffc8 100644 --- a/Documentation/filesystems/fscrypt.rst +++ b/Documentation/filesystems/fscrypt.rst @@ -191,7 +191,9 @@ Currently, the following pairs of encryption modes are supported: If unsure, you should use the (AES-256-XTS, AES-256-CTS-CBC) pair. AES-128-CBC was added only for low-powered embedded devices with -crypto accelerators such as CAAM or CESA that do not support XTS. +crypto accelerators such as CAAM or CESA that do not support XTS. To +use AES-128-CBC, CONFIG_CRYPTO_SHA256 (or another SHA-256 +implementation) must be enabled so that ESSIV can be used. Adiantum is a (primarily) stream cipher-based mode that is fast even on CPUs without dedicated crypto instructions. It's also a true diff --git a/fs/crypto/Kconfig b/fs/crypto/Kconfig index 24ed99e2eca0..5fdf24877c17 100644 --- a/fs/crypto/Kconfig +++ b/fs/crypto/Kconfig @@ -7,7 +7,6 @@ config FS_ENCRYPTION select CRYPTO_ECB select CRYPTO_XTS select CRYPTO_CTS - select CRYPTO_SHA256 select KEYS help Enable encryption of files and directories. This From 0564336329f0b03a78221ddf51e52af3665e5720 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Thu, 20 Jun 2019 11:16:58 -0700 Subject: [PATCH 18/18] fscrypt: document testing with xfstests Document how to test ext4, f2fs, and ubifs encryption with xfstests. Reviewed-by: Theodore Ts'o Signed-off-by: Eric Biggers --- Documentation/filesystems/fscrypt.rst | 39 +++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/Documentation/filesystems/fscrypt.rst b/Documentation/filesystems/fscrypt.rst index 87d4e266ffc8..82efa41b0e6c 100644 --- a/Documentation/filesystems/fscrypt.rst +++ b/Documentation/filesystems/fscrypt.rst @@ -649,3 +649,42 @@ Note that the precise way that filenames are presented to userspace without the key is subject to change in the future. It is only meant as a way to temporarily present valid filenames so that commands like ``rm -r`` work as expected on encrypted directories. + +Tests +===== + +To test fscrypt, use xfstests, which is Linux's de facto standard +filesystem test suite. First, run all the tests in the "encrypt" +group on the relevant filesystem(s). For example, to test ext4 and +f2fs encryption using `kvm-xfstests +`_:: + + kvm-xfstests -c ext4,f2fs -g encrypt + +UBIFS encryption can also be tested this way, but it should be done in +a separate command, and it takes some time for kvm-xfstests to set up +emulated UBI volumes:: + + kvm-xfstests -c ubifs -g encrypt + +No tests should fail. However, tests that use non-default encryption +modes (e.g. generic/549 and generic/550) will be skipped if the needed +algorithms were not built into the kernel's crypto API. Also, tests +that access the raw block device (e.g. generic/399, generic/548, +generic/549, generic/550) will be skipped on UBIFS. + +Besides running the "encrypt" group tests, for ext4 and f2fs it's also +possible to run most xfstests with the "test_dummy_encryption" mount +option. This option causes all new files to be automatically +encrypted with a dummy key, without having to make any API calls. +This tests the encrypted I/O paths more thoroughly. To do this with +kvm-xfstests, use the "encrypt" filesystem configuration:: + + kvm-xfstests -c ext4/encrypt,f2fs/encrypt -g auto + +Because this runs many more tests than "-g encrypt" does, it takes +much longer to run; so also consider using `gce-xfstests +`_ +instead of kvm-xfstests:: + + gce-xfstests -c ext4/encrypt,f2fs/encrypt -g auto