This patch is an *untested* attempt to make metadata encryption work with sparse mode. It's included only in case there are already objections to the approach.
The easiest way to make metadata encryption work with sparse mode might be to encrypt buffers before the calls to sparse_write_blk() and decrypt buffers after the calls to sparse_read_blk() (and just call sparse_write_blk() from sparse_write_zeroed_blk() if metadata encryption is enabled). I didn't do that here because multiple reads on unchanging data would cause unnecessary decryptions, and multiple writes of the same block would cause unnecessary encryptions. Instead, in this patch, I opted to allow each block to be stored as either ciphertext or plaintext. This patch keeps track of whether each block needs to be encrypted before being written out into the sparse file. Any writes to a block will cause the block to store the plaintext (and we mark the block as requiring encryption if the write was supposed to be encrypted). Any reads that want decrypted plaintext from an encrypted block will cause the block to first be decrypted and marked as requiring encryption. Any reads that want the *cipher*-text (not decrypted) data from a decrypted block will cause the block to first be encrypted and marked as no longer requiring encryption (this case shouldn't happen afaict - so each block will be encrypted and decrypted at most once). Signed-off-by: Satya Tangirala <sat...@google.com> --- lib/libf2fs_io.c | 111 ++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 95 insertions(+), 16 deletions(-) diff --git a/lib/libf2fs_io.c b/lib/libf2fs_io.c index df3723d..eec02ae 100644 --- a/lib/libf2fs_io.c +++ b/lib/libf2fs_io.c @@ -41,6 +41,11 @@ struct f2fs_configuration c; #include <sparse/sparse.h> struct sparse_file *f2fs_sparse_file; static char **blocks; +/* + * Whether the block in @blocks needs encryption before being written out to + * disk. + */ +static bool *block_needs_encryption; u_int64_t blocks_count; static char *zeroed_block; #endif @@ -397,24 +402,65 @@ int dev_read_version(void *buf, __u64 offset, size_t len) } #ifdef WITH_ANDROID -static int sparse_read_blk(__u64 block, int count, void *buf) +static int sparse_read_blk(__u64 block, int count, void *buf, + bool metadata_decrypt) { int i; char *out = buf; __u64 cur_block; + char *crypt_block; for (i = 0; i < count; ++i) { cur_block = block + i; - if (blocks[cur_block]) + if (blocks[cur_block]) { + /* + * If metadata_decrypt, caller wants the ciphertext + * *decrypted* with the metadata_key. If + * !metadata_decrypt, caller wants the ciphertext as is. + * + * If block_needs_encryption[cur_block], then cur_block + * is decrypted. Otherwise it is encrypted, and the + * ciphertext is currently stored as the block data. + * + * Hence, if metadata_decrypt && !block_needs_encryption, + * we need to decrypt the (currently encrypted) block + * data, and if !metadata_decrypt && + * block_needs_encryption, we need to encrypt the + * (currently plaintext) block data + * + * We shouldn't actually need the !metadata_decrypt && + * block_needs_encryption case, since the only way that + * can happen is if we write an encrypted block and then + * try to read that block's ciphertext (without + * decrypting it), which afaict the f2fs userspace tools + * don't do. + */ + if (metadata_decrypt != block_needs_encryption[cur_block]) { + crypt_block = f2fs_metadata_crypt_blocks( + blocks[cur_block], F2FS_BLKSIZE, + cur_block, + block_needs_encryption[cur_block]); + if (!crypt_block) + return -EINVAL; + block_needs_encryption[cur_block] = + !block_needs_encryption[cur_block]; + if (crypt_block != blocks[cur_block] && + blocks[cur_block] != zeroed_block) { + free(blocks[cur_block]); + } + blocks[cur_block] = crypt_block; + } memcpy(out + (i * F2FS_BLKSIZE), blocks[cur_block], F2FS_BLKSIZE); + } else if (blocks) memset(out + (i * F2FS_BLKSIZE), 0, F2FS_BLKSIZE); } return 0; } -static int sparse_write_blk(__u64 block, int count, const void *buf) +static int sparse_write_blk(__u64 block, int count, const void *buf, + bool metadata_encrypt) { int i; __u64 cur_block; @@ -429,13 +475,14 @@ static int sparse_write_blk(__u64 block, int count, const void *buf) if (!blocks[cur_block]) return -ENOMEM; } + block_needs_encryption[cur_block] = metadata_encrypt; memcpy(blocks[cur_block], in + (i * F2FS_BLKSIZE), F2FS_BLKSIZE); } return 0; } -static int sparse_write_zeroed_blk(__u64 block, int count) +static int sparse_write_zeroed_blk(__u64 block, int count, bool metadata_encrypt) { int i; __u64 cur_block; @@ -444,6 +491,7 @@ static int sparse_write_zeroed_blk(__u64 block, int count) cur_block = block + i; if (blocks[cur_block]) continue; + block_needs_encryption[cur_block] = metadata_encrypt; blocks[cur_block] = zeroed_block; } return 0; @@ -461,15 +509,23 @@ static int sparse_import_segment(void *UNUSED(priv), const void *data, int len, if (!nr_blocks || len % F2FS_BLKSIZE) return 0; - return sparse_write_blk(block, nr_blocks, data); + return sparse_write_blk(block, nr_blocks, data, false); +} + +static inline void sparse_replace_block(uint64_t blk_num, char *new_block) +{ + if (blocks[cur_block] != zeroed_block) + free(blocks[blk_num]); + blocks[blk_num] = new_block; } static int sparse_merge_blocks(uint64_t start, uint64_t num, int zero) { - char *buf; + char *buf, *enc_buf; uint64_t i; + uint64_t cur_block; - if (zero) { + if (zero && !c.metadata_crypt_key) { blocks[start] = NULL; return sparse_file_add_fill(f2fs_sparse_file, 0x0, F2FS_BLKSIZE * num, start); @@ -483,9 +539,20 @@ static int sparse_merge_blocks(uint64_t start, uint64_t num, int zero) } for (i = 0; i < num; i++) { - memcpy(buf + i * F2FS_BLKSIZE, blocks[start + i], F2FS_BLKSIZE); - free(blocks[start + i]); - blocks[start + i] = NULL; + cur_block = start + i; + if (block_needs_encryption[cur_block]) { + enc_buf = f2fs_metadata_crypt_blocks(blocks[cur_block], + F2FS_BLKSIZE, + cur_block, true); + if (!enc_buf) + return -ENOMEM; + + if (enc_buf != block[cur_block]) + sparse_replace_block(cur_block, enc_buf); + } + + memcpy(buf + i * F2FS_BLKSIZE, blocks[cur_block], F2FS_BLKSIZE); + sparse_replace_block(cur_block, NULL); } /* free_sparse_blocks will release this buf. */ @@ -495,9 +562,12 @@ static int sparse_merge_blocks(uint64_t start, uint64_t num, int zero) F2FS_BLKSIZE * num, start); } #else -static int sparse_read_blk(__u64 block, int count, void *buf) { return 0; } -static int sparse_write_blk(__u64 block, int count, const void *buf) { return 0; } -static int sparse_write_zeroed_blk(__u64 block, int count) { return 0; } +static int sparse_read_blk(__u64 block, int count, void *buf, + bool metadata_decrypt) { return 0; } +static int sparse_write_blk(__u64 block, int count, const void *buf, + bool metadata_encrypt) { return 0; } +static int sparse_write_zeroed_blk(__u64 block, int count, + bool metadata_encrypt) { return 0; } #endif static int __dev_read(void *buf, __u64 offset, size_t len, bool metadata_decrypt) @@ -509,7 +579,8 @@ static int __dev_read(void *buf, __u64 offset, size_t len, bool metadata_decrypt if (c.sparse_mode) return sparse_read_blk(offset / F2FS_BLKSIZE, - len / F2FS_BLKSIZE, buf); + len / F2FS_BLKSIZE, buf, + metadata_decrypt); fd = __get_device_fd(&offset); if (fd < 0) @@ -572,7 +643,8 @@ static int __dev_write(void *buf, __u64 offset, size_t len, if (c.sparse_mode) return sparse_write_blk(offset / F2FS_BLKSIZE, - len / F2FS_BLKSIZE, buf); + len / F2FS_BLKSIZE, buf, + metadata_encrypt); fd = __get_device_fd(&offset); if (fd < 0) @@ -635,7 +707,8 @@ int dev_fill(void *buf, __u64 offset, size_t len) if (c.sparse_mode) return sparse_write_zeroed_blk(offset / F2FS_BLKSIZE, - len / F2FS_BLKSIZE); + len / F2FS_BLKSIZE, + true); fd = __get_device_fd(&offset); if (fd < 0) @@ -725,6 +798,12 @@ int f2fs_init_sparse_file(void) return -1; } + block_needs_encryption = calloc(blocks_count, sizeof(bool)); + if (!block_needs_encryption) { + MSG(0, "\tError: Calloc Failed for block encryption bookkeeping!!!\n"); + return -1; + } + zeroed_block = calloc(1, F2FS_BLKSIZE); if (!zeroed_block) { MSG(0, "\tError: Calloc Failed for zeroed block!!!\n"); -- 2.29.2.729.g45daf8777d-goog _______________________________________________ Linux-f2fs-devel mailing list Linux-f2fs-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/linux-f2fs-devel