Commit 53bc1d85 authored by Eric Biggers's avatar Eric Biggers

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: default avatarChandan Rajendra <chandan@linux.ibm.com>
Signed-off-by: default avatarEric Biggers <ebiggers@google.com>
parent 03569f2f
...@@ -122,8 +122,8 @@ struct page *fscrypt_alloc_bounce_page(gfp_t gfp_flags) ...@@ -122,8 +122,8 @@ struct page *fscrypt_alloc_bounce_page(gfp_t gfp_flags)
/** /**
* fscrypt_free_bounce_page() - free a ciphertext bounce page * fscrypt_free_bounce_page() - free a ciphertext bounce page
* *
* Free a bounce page that was allocated by fscrypt_encrypt_page(), or by * Free a bounce page that was allocated by fscrypt_encrypt_pagecache_blocks(),
* fscrypt_alloc_bounce_page() directly. * or by fscrypt_alloc_bounce_page() directly.
*/ */
void fscrypt_free_bounce_page(struct page *bounce_page) 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, ...@@ -198,52 +198,63 @@ int fscrypt_crypt_block(const struct inode *inode, fscrypt_direction_t rw,
} }
/** /**
* fscypt_encrypt_page() - Encrypts a page * fscrypt_encrypt_pagecache_blocks() - Encrypt filesystem blocks from a pagecache page
* @inode: The inode for which the encryption should take place * @page: The locked pagecache page containing the block(s) to encrypt
* @page: The page to encrypt. Must be locked. * @len: Total size of the block(s) to encrypt. Must be a nonzero
* @len: Length of data to encrypt in @page and encrypted * multiple of the filesystem's block size.
* data in returned page. * @offs: Byte offset within @page of the first block to encrypt. Must be
* @offs: Offset of data within @page and returned * a multiple of the filesystem's block size.
* page holding encrypted data. * @gfp_flags: Memory allocation flags
* @lblk_num: Logical block number. This must be unique for multiple *
* calls with same inode, except when overwriting * A new bounce page is allocated, and the specified block(s) are encrypted into
* previously written data. * it. In the bounce page, the ciphertext block(s) will be located at the same
* @gfp_flags: The gfp flag for memory allocation * 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.
* *
* Encrypts @page. A bounce page is allocated, the data is encrypted into the * This is for use by the filesystem's ->writepages() method.
* 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() * Return: the new encrypted bounce page on success; an ERR_PTR() on failure
*/ */
struct page *fscrypt_encrypt_page(const struct inode *inode, struct page *fscrypt_encrypt_pagecache_blocks(struct page *page,
struct page *page, unsigned int len,
unsigned int len, unsigned int offs,
unsigned int offs, gfp_t gfp_flags)
u64 lblk_num, 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; struct page *ciphertext_page;
u64 lblk_num = ((u64)page->index << (PAGE_SHIFT - blockbits)) +
(offs >> blockbits);
unsigned int i;
int err; int err;
if (WARN_ON_ONCE(!PageLocked(page))) if (WARN_ON_ONCE(!PageLocked(page)))
return ERR_PTR(-EINVAL); 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); ciphertext_page = fscrypt_alloc_bounce_page(gfp_flags);
if (!ciphertext_page) if (!ciphertext_page)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
err = fscrypt_crypt_block(inode, FS_ENCRYPT, lblk_num, page, for (i = offs; i < offs + len; i += blocksize, lblk_num++) {
ciphertext_page, len, offs, gfp_flags); err = fscrypt_crypt_block(inode, FS_ENCRYPT, lblk_num,
if (err) { page, ciphertext_page,
fscrypt_free_bounce_page(ciphertext_page); blocksize, i, gfp_flags);
return ERR_PTR(err); if (err) {
fscrypt_free_bounce_page(ciphertext_page);
return ERR_PTR(err);
}
} }
SetPagePrivate(ciphertext_page); SetPagePrivate(ciphertext_page);
set_page_private(ciphertext_page, (unsigned long)page); set_page_private(ciphertext_page, (unsigned long)page);
return ciphertext_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 * fscrypt_encrypt_block_inplace() - Encrypt a filesystem block in-place
......
...@@ -471,8 +471,8 @@ int ext4_bio_write_page(struct ext4_io_submit *io, ...@@ -471,8 +471,8 @@ int ext4_bio_write_page(struct ext4_io_submit *io,
gfp_t gfp_flags = GFP_NOFS; gfp_t gfp_flags = GFP_NOFS;
retry_encrypt: retry_encrypt:
bounce_page = fscrypt_encrypt_page(inode, page, PAGE_SIZE, 0, bounce_page = fscrypt_encrypt_pagecache_blocks(page, PAGE_SIZE,
page->index, gfp_flags); 0, gfp_flags);
if (IS_ERR(bounce_page)) { if (IS_ERR(bounce_page)) {
ret = PTR_ERR(bounce_page); ret = PTR_ERR(bounce_page);
if (ret == -ENOMEM && wbc->sync_mode == WB_SYNC_ALL) { if (ret == -ENOMEM && wbc->sync_mode == WB_SYNC_ALL) {
......
...@@ -1726,8 +1726,9 @@ static int encrypt_one_page(struct f2fs_io_info *fio) ...@@ -1726,8 +1726,9 @@ static int encrypt_one_page(struct f2fs_io_info *fio)
f2fs_wait_on_block_writeback(inode, fio->old_blkaddr); f2fs_wait_on_block_writeback(inode, fio->old_blkaddr);
retry_encrypt: retry_encrypt:
fio->encrypted_page = fscrypt_encrypt_page(inode, fio->page, fio->encrypted_page = fscrypt_encrypt_pagecache_blocks(fio->page,
PAGE_SIZE, 0, fio->page->index, gfp_flags); PAGE_SIZE, 0,
gfp_flags);
if (IS_ERR(fio->encrypted_page)) { if (IS_ERR(fio->encrypted_page)) {
/* flush pending IOs and wait for a while in the ENOMEM case */ /* flush pending IOs and wait for a while in the ENOMEM case */
if (PTR_ERR(fio->encrypted_page) == -ENOMEM) { if (PTR_ERR(fio->encrypted_page) == -ENOMEM) {
......
...@@ -103,9 +103,11 @@ static inline void fscrypt_handle_d_move(struct dentry *dentry) ...@@ -103,9 +103,11 @@ static inline void fscrypt_handle_d_move(struct dentry *dentry)
extern void fscrypt_enqueue_decrypt_work(struct work_struct *); extern void fscrypt_enqueue_decrypt_work(struct work_struct *);
extern struct fscrypt_ctx *fscrypt_get_ctx(gfp_t); extern struct fscrypt_ctx *fscrypt_get_ctx(gfp_t);
extern void fscrypt_release_ctx(struct fscrypt_ctx *); extern void fscrypt_release_ctx(struct fscrypt_ctx *);
extern struct page *fscrypt_encrypt_page(const struct inode *, struct page *,
unsigned int, unsigned int, extern struct page *fscrypt_encrypt_pagecache_blocks(struct page *page,
u64, gfp_t); unsigned int len,
unsigned int offs,
gfp_t gfp_flags);
extern int fscrypt_encrypt_block_inplace(const struct inode *inode, extern int fscrypt_encrypt_block_inplace(const struct inode *inode,
struct page *page, unsigned int len, struct page *page, unsigned int len,
unsigned int offs, u64 lblk_num, unsigned int offs, u64 lblk_num,
...@@ -288,11 +290,10 @@ static inline void fscrypt_release_ctx(struct fscrypt_ctx *ctx) ...@@ -288,11 +290,10 @@ static inline void fscrypt_release_ctx(struct fscrypt_ctx *ctx)
return; return;
} }
static inline struct page *fscrypt_encrypt_page(const struct inode *inode, static inline struct page *fscrypt_encrypt_pagecache_blocks(struct page *page,
struct page *page, unsigned int len,
unsigned int len, unsigned int offs,
unsigned int offs, gfp_t gfp_flags)
u64 lblk_num, gfp_t gfp_flags)
{ {
return ERR_PTR(-EOPNOTSUPP); return ERR_PTR(-EOPNOTSUPP);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment