Commit 0b578f35 authored by Chandan Rajendra's avatar Chandan Rajendra Committed by Eric Biggers
Browse files

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: default avatarChandan Rajendra <chandan@linux.ibm.com>
(EB: rebase onto previous changes, improve the commit message,
 and move the check for encrypted inode)
Signed-off-by: default avatarEric Biggers <ebiggers@google.com>
parent 7e0785fc
Loading
Loading
Loading
Loading
+18 −11
Original line number Diff line number Diff line
@@ -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;