ext4 crypto: enable filename encryption
Michael Halcrow [Wed, 15 Apr 2015 00:55:49 +0000 (20:55 -0400)]
Change-Id: Ic3ee64ce59dc349944acc5cf0140c89796009c7f
Signed-off-by: Uday Savagaonkar <savagaon@google.com>
Signed-off-by: Ildar Muslukhov <ildarm@google.com>
Signed-off-by: Michael Halcrow <mhalcrow@google.com>
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
Signed-off-by: Theodore Ts'o <tytso@google.com>

fs/ext4/dir.c
fs/ext4/ialloc.c

index f3d4001..18a4085 100644 (file)
@@ -111,7 +111,10 @@ static int ext4_readdir2(struct file *file, struct dir_context *ctx)
        int err;
        struct inode *inode = file_inode(file);
        struct super_block *sb = inode->i_sb;
+       struct buffer_head *bh = NULL;
        int dir_has_error = 0;
+       struct ext4_fname_crypto_ctx *enc_ctx = NULL;
+       struct ext4_str fname_crypto_str = {.name = NULL, .len = 0};
 
        if (is_dx_dir(inode)) {
                err = ext4_dx_readdir(file, ctx);
@@ -128,17 +131,28 @@ static int ext4_readdir2(struct file *file, struct dir_context *ctx)
 
        if (ext4_has_inline_data(inode)) {
                int has_inline_data = 1;
-               int ret = ext4_read_inline_dir(file, ctx,
+               err = ext4_read_inline_dir(file, ctx,
                                           &has_inline_data);
                if (has_inline_data)
-                       return ret;
+                       return err;
+       }
+
+       enc_ctx = ext4_get_fname_crypto_ctx(inode, EXT4_NAME_LEN);
+       if (IS_ERR(enc_ctx))
+               return PTR_ERR(enc_ctx);
+       if (enc_ctx) {
+               err = ext4_fname_crypto_alloc_buffer(enc_ctx, EXT4_NAME_LEN,
+                                                    &fname_crypto_str);
+               if (err < 0) {
+                       ext4_put_fname_crypto_ctx(&enc_ctx);
+                       return err;
+               }
        }
 
        offset = ctx->pos & (sb->s_blocksize - 1);
 
        while (ctx->pos < inode->i_size) {
                struct ext4_map_blocks map;
-               struct buffer_head *bh = NULL;
 
                map.m_lblk = ctx->pos >> EXT4_BLOCK_SIZE_BITS(sb);
                map.m_len = 1;
@@ -181,6 +195,7 @@ static int ext4_readdir2(struct file *file, struct dir_context *ctx)
                                        (unsigned long long)ctx->pos);
                        ctx->pos += sb->s_blocksize - offset;
                        brelse(bh);
+                       bh = NULL;
                        continue;
                }
                set_buffer_verified(bh);
@@ -227,25 +242,44 @@ static int ext4_readdir2(struct file *file, struct dir_context *ctx)
                        offset += ext4_rec_len_from_disk(de->rec_len,
                                        sb->s_blocksize);
                        if (le32_to_cpu(de->inode)) {
-                               if (!dir_emit(ctx, de->name,
-                                               de->name_len,
-                                               le32_to_cpu(de->inode),
-                                               get_dtype(sb, de->file_type))) {
-                                       brelse(bh);
-                                       return 0;
+                               if (enc_ctx == NULL) {
+                                       /* Directory is not encrypted */
+                                       if (!dir_emit(ctx, de->name,
+                                           de->name_len,
+                                           le32_to_cpu(de->inode),
+                                           get_dtype(sb, de->file_type)))
+                                               goto done;
+                               } else {
+                                       /* Directory is encrypted */
+                                       err = ext4_fname_disk_to_usr(enc_ctx,
+                                                       de, &fname_crypto_str);
+                                       if (err < 0)
+                                               goto errout;
+                                       if (!dir_emit(ctx,
+                                           fname_crypto_str.name, err,
+                                           le32_to_cpu(de->inode),
+                                           get_dtype(sb, de->file_type)))
+                                               goto done;
                                }
                        }
                        ctx->pos += ext4_rec_len_from_disk(de->rec_len,
                                                sb->s_blocksize);
                }
-               offset = 0;
+               if ((ctx->pos < inode->i_size) && !dir_relax(inode))
+                       goto done;
                brelse(bh);
-               if (ctx->pos < inode->i_size) {
-                       if (!dir_relax(inode))
-                               return 0;
-               }
+               bh = NULL;
+               offset = 0;
        }
-       return 0;
+done:
+       err = 0;
+errout:
+#ifdef CONFIG_EXT4_FS_ENCRYPTION
+       ext4_put_fname_crypto_ctx(&enc_ctx);
+       ext4_fname_crypto_free_buffer(&fname_crypto_str);
+#endif
+       brelse(bh);
+       return err;
 }
 
 static int ext4_readdir(struct file *file,
index e554ca3..8f37c9e 100644 (file)
@@ -1034,11 +1034,28 @@ got:
        ext4_set_inode_state(inode, EXT4_STATE_NEW);
 
        ei->i_extra_isize = EXT4_SB(sb)->s_want_extra_isize;
-
+#ifdef CONFIG_EXT4_FS_ENCRYPTION
+       if ((sbi->s_file_encryption_mode == EXT4_ENCRYPTION_MODE_INVALID) &&
+           (sbi->s_dir_encryption_mode == EXT4_ENCRYPTION_MODE_INVALID)) {
+               ei->i_inline_off = 0;
+               if (EXT4_HAS_INCOMPAT_FEATURE(sb,
+                       EXT4_FEATURE_INCOMPAT_INLINE_DATA))
+                       ext4_set_inode_state(inode,
+                       EXT4_STATE_MAY_INLINE_DATA);
+       } else {
+               /* Inline data and encryption are incompatible
+                * We turn off inline data since encryption is enabled */
+               ei->i_inline_off = 1;
+               if (EXT4_HAS_INCOMPAT_FEATURE(sb,
+                       EXT4_FEATURE_INCOMPAT_INLINE_DATA))
+                       ext4_clear_inode_state(inode,
+                       EXT4_STATE_MAY_INLINE_DATA);
+       }
+#else
        ei->i_inline_off = 0;
        if (EXT4_HAS_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_INLINE_DATA))
                ext4_set_inode_state(inode, EXT4_STATE_MAY_INLINE_DATA);
-
+#endif
        ret = inode;
        err = dquot_alloc_inode(inode);
        if (err)