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

Reply via email to