From 72b8e0f9fa8aee7e623808af1a5f33b70ebcb2c7 Mon Sep 17 00:00:00 2001 From: Sheng Yong Date: Thu, 2 Apr 2015 23:47:42 -0400 Subject: ext4: remove unused header files Remove unused header files and header files which are included in ext4.h. Signed-off-by: Sheng Yong Signed-off-by: Theodore Ts'o --- fs/ext4/ialloc.c | 1 - 1 file changed, 1 deletion(-) (limited to 'fs/ext4/ialloc.c') diff --git a/fs/ext4/ialloc.c b/fs/ext4/ialloc.c index ac644c31ca67..6ab6f639c70b 100644 --- a/fs/ext4/ialloc.c +++ b/fs/ext4/ialloc.c @@ -14,7 +14,6 @@ #include #include -#include #include #include #include -- cgit v1.2.3 From 2058f83a728adffbe00bded4f804b37a5ee58cbe Mon Sep 17 00:00:00 2001 From: Michael Halcrow Date: Sun, 12 Apr 2015 00:55:10 -0400 Subject: ext4 crypto: implement the ext4 encryption write path Pulls block_write_begin() into fs/ext4/inode.c because it might need to do a low-level read of the existing data, in which case we need to decrypt it. Signed-off-by: Michael Halcrow Signed-off-by: Ildar Muslukhov Signed-off-by: Theodore Ts'o --- fs/ext4/extents.c | 17 +++++++++ fs/ext4/ialloc.c | 5 +++ fs/ext4/inode.c | 112 +++++++++++++++++++++++++++++++++++++++++++++++++++++- fs/ext4/page-io.c | 45 +++++++++++++++++++--- 4 files changed, 173 insertions(+), 6 deletions(-) (limited to 'fs/ext4/ialloc.c') diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c index 74580ea3ab55..973816bfe4a9 100644 --- a/fs/ext4/extents.c +++ b/fs/ext4/extents.c @@ -3122,6 +3122,9 @@ static int ext4_ext_zeroout(struct inode *inode, struct ext4_extent *ex) ee_len = ext4_ext_get_actual_len(ex); ee_pblock = ext4_ext_pblock(ex); + if (ext4_encrypted_inode(inode)) + return ext4_encrypted_zeroout(inode, ex); + ret = sb_issue_zeroout(inode->i_sb, ee_pblock, ee_len, GFP_NOFS); if (ret > 0) ret = 0; @@ -4898,6 +4901,20 @@ long ext4_fallocate(struct file *file, int mode, loff_t offset, loff_t len) ext4_lblk_t lblk; unsigned int blkbits = inode->i_blkbits; + /* + * Encrypted inodes can't handle collapse range or insert + * range since we would need to re-encrypt blocks with a + * different IV or XTS tweak (which are based on the logical + * block number). + * + * XXX It's not clear why zero range isn't working, but we'll + * leave it disabled for encrypted inodes for now. This is a + * bug we should fix.... + */ + if (ext4_encrypted_inode(inode) && + (mode & (FALLOC_FL_COLLAPSE_RANGE | FALLOC_FL_ZERO_RANGE))) + return -EOPNOTSUPP; + /* Return error if mode is not supported */ if (mode & ~(FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE | FALLOC_FL_COLLAPSE_RANGE | FALLOC_FL_ZERO_RANGE)) diff --git a/fs/ext4/ialloc.c b/fs/ext4/ialloc.c index 6ab6f639c70b..247737efd725 100644 --- a/fs/ext4/ialloc.c +++ b/fs/ext4/ialloc.c @@ -996,6 +996,11 @@ got: ei->i_block_group = group; ei->i_last_alloc_group = ~0; + /* If the directory encrypted, then we should encrypt the inode. */ + if ((S_ISDIR(mode) || S_ISREG(mode) || S_ISLNK(mode)) && + ext4_encrypted_inode(dir)) + ext4_set_inode_flag(inode, EXT4_INODE_ENCRYPT); + ext4_set_inode_flags(inode); if (IS_DIRSYNC(inode)) ext4_handle_sync(handle); diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index cd3009152ae2..7c4527e10ae4 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -886,6 +886,95 @@ int do_journal_get_write_access(handle_t *handle, static int ext4_get_block_write_nolock(struct inode *inode, sector_t iblock, struct buffer_head *bh_result, int create); + +#ifdef CONFIG_EXT4_FS_ENCRYPTION +static int ext4_block_write_begin(struct page *page, loff_t pos, unsigned len, + get_block_t *get_block) +{ + unsigned from = pos & (PAGE_CACHE_SIZE - 1); + unsigned to = from + len; + struct inode *inode = page->mapping->host; + unsigned block_start, block_end; + sector_t block; + 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; + + BUG_ON(!PageLocked(page)); + BUG_ON(from > PAGE_CACHE_SIZE); + BUG_ON(to > PAGE_CACHE_SIZE); + BUG_ON(from > to); + + if (!page_has_buffers(page)) + create_empty_buffers(page, blocksize, 0); + head = page_buffers(page); + bbits = ilog2(blocksize); + block = (sector_t)page->index << (PAGE_CACHE_SHIFT - bbits); + + for (bh = head, block_start = 0; bh != head || !block_start; + block++, block_start = block_end, bh = bh->b_this_page) { + block_end = block_start + blocksize; + if (block_end <= from || block_start >= to) { + if (PageUptodate(page)) { + if (!buffer_uptodate(bh)) + set_buffer_uptodate(bh); + } + continue; + } + if (buffer_new(bh)) + clear_buffer_new(bh); + if (!buffer_mapped(bh)) { + WARN_ON(bh->b_size != blocksize); + err = get_block(inode, block, bh, 1); + if (err) + break; + if (buffer_new(bh)) { + unmap_underlying_metadata(bh->b_bdev, + bh->b_blocknr); + if (PageUptodate(page)) { + clear_buffer_new(bh); + set_buffer_uptodate(bh); + mark_buffer_dirty(bh); + continue; + } + if (block_end > to || block_start < from) + zero_user_segments(page, to, block_end, + block_start, from); + continue; + } + } + if (PageUptodate(page)) { + if (!buffer_uptodate(bh)) + set_buffer_uptodate(bh); + continue; + } + if (!buffer_uptodate(bh) && !buffer_delay(bh) && + !buffer_unwritten(bh) && + (block_start < from || block_end > to)) { + ll_rw_block(READ, 1, &bh); + *wait_bh++ = bh; + decrypt = ext4_encrypted_inode(inode) && + S_ISREG(inode->i_mode); + } + } + /* + * If we issued read requests, let them complete. + */ + while (wait_bh > wait) { + wait_on_buffer(*--wait_bh); + if (!buffer_uptodate(*wait_bh)) + err = -EIO; + } + if (unlikely(err)) + page_zero_new_buffers(page, from, to); + else if (decrypt) + err = ext4_decrypt_one(inode, page); + return err; +} +#endif + static int ext4_write_begin(struct file *file, struct address_space *mapping, loff_t pos, unsigned len, unsigned flags, struct page **pagep, void **fsdata) @@ -948,11 +1037,19 @@ retry_journal: /* In case writeback began while the page was unlocked */ wait_for_stable_page(page); +#ifdef CONFIG_EXT4_FS_ENCRYPTION + if (ext4_should_dioread_nolock(inode)) + ret = ext4_block_write_begin(page, pos, len, + ext4_get_block_write); + else + ret = ext4_block_write_begin(page, pos, len, + ext4_get_block); +#else if (ext4_should_dioread_nolock(inode)) ret = __block_write_begin(page, pos, len, ext4_get_block_write); else ret = __block_write_begin(page, pos, len, ext4_get_block); - +#endif if (!ret && ext4_should_journal_data(inode)) { ret = ext4_walk_page_buffers(handle, page_buffers(page), from, to, NULL, @@ -2574,7 +2671,12 @@ retry_journal: /* In case writeback began while the page was unlocked */ wait_for_stable_page(page); +#ifdef CONFIG_EXT4_FS_ENCRYPTION + ret = ext4_block_write_begin(page, pos, len, + ext4_da_get_block_prep); +#else ret = __block_write_begin(page, pos, len, ext4_da_get_block_prep); +#endif if (ret < 0) { unlock_page(page); ext4_journal_stop(handle); @@ -3032,6 +3134,9 @@ static ssize_t ext4_ext_direct_IO(int rw, struct kiocb *iocb, get_block_func = ext4_get_block_write; dio_flags = DIO_LOCKING; } +#ifdef CONFIG_EXT4_FS_ENCRYPTION + BUG_ON(ext4_encrypted_inode(inode) && S_ISREG(inode->i_mode)); +#endif if (IS_DAX(inode)) ret = dax_do_io(rw, iocb, inode, iter, offset, get_block_func, ext4_end_io_dio, dio_flags); @@ -3096,6 +3201,11 @@ static ssize_t ext4_direct_IO(int rw, struct kiocb *iocb, size_t count = iov_iter_count(iter); ssize_t ret; +#ifdef CONFIG_EXT4_FS_ENCRYPTION + if (ext4_encrypted_inode(inode) && S_ISREG(inode->i_mode)) + return 0; +#endif + /* * If we are doing data journalling we don't support O_DIRECT */ diff --git a/fs/ext4/page-io.c b/fs/ext4/page-io.c index 5687e4750c1e..51a5f1214f48 100644 --- a/fs/ext4/page-io.c +++ b/fs/ext4/page-io.c @@ -67,6 +67,10 @@ static void ext4_finish_bio(struct bio *bio) bio_for_each_segment_all(bvec, bio, i) { struct page *page = bvec->bv_page; +#ifdef CONFIG_EXT4_FS_ENCRYPTION + struct page *data_page = NULL; + struct ext4_crypto_ctx *ctx = NULL; +#endif struct buffer_head *bh, *head; unsigned bio_start = bvec->bv_offset; unsigned bio_end = bio_start + bvec->bv_len; @@ -76,6 +80,15 @@ static void ext4_finish_bio(struct bio *bio) if (!page) continue; +#ifdef CONFIG_EXT4_FS_ENCRYPTION + if (!page->mapping) { + /* The bounce data pages are unmapped. */ + data_page = page; + ctx = (struct ext4_crypto_ctx *)page_private(data_page); + page = ctx->control_page; + } +#endif + if (error) { SetPageError(page); set_bit(AS_EIO, &page->mapping->flags); @@ -100,8 +113,13 @@ static void ext4_finish_bio(struct bio *bio) } while ((bh = bh->b_this_page) != head); bit_spin_unlock(BH_Uptodate_Lock, &head->b_state); local_irq_restore(flags); - if (!under_io) + if (!under_io) { +#ifdef CONFIG_EXT4_FS_ENCRYPTION + if (ctx) + ext4_restore_control_page(data_page); +#endif end_page_writeback(page); + } } } @@ -376,6 +394,7 @@ static int io_submit_init_bio(struct ext4_io_submit *io, static int io_submit_add_bh(struct ext4_io_submit *io, struct inode *inode, + struct page *page, struct buffer_head *bh) { int ret; @@ -389,7 +408,7 @@ submit_and_retry: if (ret) return ret; } - ret = bio_add_page(io->io_bio, bh->b_page, bh->b_size, bh_offset(bh)); + ret = bio_add_page(io->io_bio, page, bh->b_size, bh_offset(bh)); if (ret != bh->b_size) goto submit_and_retry; io->io_next_block++; @@ -402,6 +421,7 @@ int ext4_bio_write_page(struct ext4_io_submit *io, struct writeback_control *wbc, bool keep_towrite) { + struct page *data_page = NULL; struct inode *inode = page->mapping->host; unsigned block_start, blocksize; struct buffer_head *bh, *head; @@ -461,19 +481,29 @@ int ext4_bio_write_page(struct ext4_io_submit *io, set_buffer_async_write(bh); } while ((bh = bh->b_this_page) != head); - /* Now submit buffers to write */ bh = head = page_buffers(page); + + if (ext4_encrypted_inode(inode) && S_ISREG(inode->i_mode)) { + data_page = ext4_encrypt(inode, page); + if (IS_ERR(data_page)) { + ret = PTR_ERR(data_page); + data_page = NULL; + goto out; + } + } + + /* Now submit buffers to write */ do { if (!buffer_async_write(bh)) continue; - ret = io_submit_add_bh(io, inode, bh); + ret = io_submit_add_bh(io, inode, + data_page ? data_page : page, bh); if (ret) { /* * We only get here on ENOMEM. Not much else * we can do but mark the page as dirty, and * better luck next time. */ - redirty_page_for_writepage(wbc, page); break; } nr_submitted++; @@ -482,6 +512,11 @@ int ext4_bio_write_page(struct ext4_io_submit *io, /* Error stopped previous loop? Clean up buffers... */ if (ret) { + out: + if (data_page) + ext4_restore_control_page(data_page); + printk_ratelimited(KERN_ERR "%s: ret = %d\n", __func__, ret); + redirty_page_for_writepage(wbc, page); do { clear_buffer_async_write(bh); bh = bh->b_this_page; -- cgit v1.2.3 From 4461471107b79bee16c497c9f7f69fa26126ae5b Mon Sep 17 00:00:00 2001 From: Michael Halcrow Date: Sun, 12 Apr 2015 01:09:05 -0400 Subject: ext4 crypto: enable filename encryption Signed-off-by: Uday Savagaonkar Signed-off-by: Ildar Muslukhov Signed-off-by: Michael Halcrow Signed-off-by: Theodore Ts'o --- fs/ext4/dir.c | 64 +++++++++++++++++++++++++++++++++++++++++++------------- fs/ext4/ialloc.c | 21 +++++++++++++++++-- 2 files changed, 68 insertions(+), 17 deletions(-) (limited to 'fs/ext4/ialloc.c') diff --git a/fs/ext4/dir.c b/fs/ext4/dir.c index 315f13ad382e..61db51a5ce4c 100644 --- a/fs/ext4/dir.c +++ b/fs/ext4/dir.c @@ -108,7 +108,10 @@ static int ext4_readdir(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); @@ -125,17 +128,28 @@ static int ext4_readdir(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; @@ -178,6 +192,7 @@ static int ext4_readdir(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); @@ -224,25 +239,44 @@ static int ext4_readdir(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 inline int is_32bit_api(void) diff --git a/fs/ext4/ialloc.c b/fs/ext4/ialloc.c index 247737efd725..850267c89407 100644 --- a/fs/ext4/ialloc.c +++ b/fs/ext4/ialloc.c @@ -1033,11 +1033,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) -- cgit v1.2.3 From 6ddb2447846a8ece111e316a2863c2355023682d Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Thu, 16 Apr 2015 01:56:00 -0400 Subject: ext4 crypto: enable encryption feature flag Also add the test dummy encryption mode flag so we can more easily test the encryption patches using xfstests. Signed-off-by: Michael Halcrow Signed-off-by: Theodore Ts'o --- fs/ext4/crypto_key.c | 27 +++++++++++++++------------ fs/ext4/crypto_policy.c | 18 +++++++++++++++--- fs/ext4/ext4.h | 17 +++++++++++++---- fs/ext4/ialloc.c | 3 ++- fs/ext4/namei.c | 9 ++++++--- fs/ext4/super.c | 29 ++++++++++++++++++++++++++++- 6 files changed, 79 insertions(+), 24 deletions(-) (limited to 'fs/ext4/ialloc.c') diff --git a/fs/ext4/crypto_key.c b/fs/ext4/crypto_key.c index 572bd97f58dd..c8392af8abbb 100644 --- a/fs/ext4/crypto_key.c +++ b/fs/ext4/crypto_key.c @@ -98,6 +98,7 @@ int ext4_generate_encryption_key(struct inode *inode) struct ext4_encryption_key *master_key; struct ext4_encryption_context ctx; struct user_key_payload *ukp; + struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb); int res = ext4_xattr_get(inode, EXT4_XATTR_INDEX_ENCRYPTION, EXT4_XATTR_NAME_ENCRYPTION_CONTEXT, &ctx, sizeof(ctx)); @@ -109,6 +110,20 @@ int ext4_generate_encryption_key(struct inode *inode) } res = 0; + if (S_ISREG(inode->i_mode)) + crypt_key->mode = ctx.contents_encryption_mode; + else if (S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode)) + crypt_key->mode = ctx.filenames_encryption_mode; + else { + printk(KERN_ERR "ext4 crypto: Unsupported inode type.\n"); + BUG(); + } + crypt_key->size = ext4_encryption_key_size(crypt_key->mode); + BUG_ON(!crypt_key->size); + if (DUMMY_ENCRYPTION_ENABLED(sbi)) { + memset(crypt_key->raw, 0x42, EXT4_AES_256_XTS_KEY_SIZE); + goto out; + } memcpy(full_key_descriptor, EXT4_KEY_DESC_PREFIX, EXT4_KEY_DESC_PREFIX_SIZE); sprintf(full_key_descriptor + EXT4_KEY_DESC_PREFIX_SIZE, @@ -129,21 +144,9 @@ int ext4_generate_encryption_key(struct inode *inode) goto out; } master_key = (struct ext4_encryption_key *)ukp->data; - - if (S_ISREG(inode->i_mode)) - crypt_key->mode = ctx.contents_encryption_mode; - else if (S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode)) - crypt_key->mode = ctx.filenames_encryption_mode; - else { - printk(KERN_ERR "ext4 crypto: Unsupported inode type.\n"); - BUG(); - } - crypt_key->size = ext4_encryption_key_size(crypt_key->mode); - BUG_ON(!crypt_key->size); BUILD_BUG_ON(EXT4_AES_128_ECB_KEY_SIZE != EXT4_KEY_DERIVATION_NONCE_SIZE); BUG_ON(master_key->size != EXT4_AES_256_XTS_KEY_SIZE); - BUG_ON(crypt_key->size < EXT4_AES_256_CBC_KEY_SIZE); res = ext4_derive_key_aes(ctx.nonce, master_key->raw, crypt_key->raw); out: if (keyring_key) diff --git a/fs/ext4/crypto_policy.c b/fs/ext4/crypto_policy.c index 749ed6e91e50..30eaf9e9864a 100644 --- a/fs/ext4/crypto_policy.c +++ b/fs/ext4/crypto_policy.c @@ -169,13 +169,25 @@ int ext4_inherit_context(struct inode *parent, struct inode *child) EXT4_XATTR_NAME_ENCRYPTION_CONTEXT, &ctx, sizeof(ctx)); - if (res != sizeof(ctx)) - return -ENOENT; - + if (res != sizeof(ctx)) { + if (DUMMY_ENCRYPTION_ENABLED(EXT4_SB(parent->i_sb))) { + ctx.format = EXT4_ENCRYPTION_CONTEXT_FORMAT_V1; + ctx.contents_encryption_mode = + EXT4_ENCRYPTION_MODE_AES_256_XTS; + ctx.filenames_encryption_mode = + EXT4_ENCRYPTION_MODE_AES_256_CTS; + memset(ctx.master_key_descriptor, 0x42, + EXT4_KEY_DESCRIPTOR_SIZE); + res = 0; + } else { + goto out; + } + } get_random_bytes(ctx.nonce, EXT4_KEY_DERIVATION_NONCE_SIZE); res = ext4_xattr_set(child, EXT4_XATTR_INDEX_ENCRYPTION, EXT4_XATTR_NAME_ENCRYPTION_CONTEXT, &ctx, sizeof(ctx), 0); +out: if (!res) ext4_set_inode_flag(child, EXT4_INODE_ENCRYPT); return res; diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index 86d15706d27a..0179654faf79 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -1200,8 +1200,16 @@ struct ext4_super_block { /* * run-time mount flags */ -#define EXT4_MF_MNTDIR_SAMPLED 0x0001 -#define EXT4_MF_FS_ABORTED 0x0002 /* Fatal error detected */ +#define EXT4_MF_MNTDIR_SAMPLED 0x0001 +#define EXT4_MF_FS_ABORTED 0x0002 /* Fatal error detected */ +#define EXT4_MF_TEST_DUMMY_ENCRYPTION 0x0004 + +#ifdef CONFIG_EXT4_FS_ENCRYPTION +#define DUMMY_ENCRYPTION_ENABLED(sbi) (unlikely((sbi)->s_mount_flags & \ + EXT4_MF_TEST_DUMMY_ENCRYPTION)) +#else +#define DUMMY_ENCRYPTION_ENABLED(sbi) (0) +#endif /* Number of quota types we support */ #define EXT4_MAXQUOTAS 2 @@ -1613,8 +1621,9 @@ static inline int ext4_encrypted_inode(struct inode *inode) EXT4_FEATURE_INCOMPAT_EXTENTS| \ EXT4_FEATURE_INCOMPAT_64BIT| \ EXT4_FEATURE_INCOMPAT_FLEX_BG| \ - EXT4_FEATURE_INCOMPAT_MMP | \ - EXT4_FEATURE_INCOMPAT_INLINE_DATA) + EXT4_FEATURE_INCOMPAT_MMP | \ + EXT4_FEATURE_INCOMPAT_INLINE_DATA | \ + EXT4_FEATURE_INCOMPAT_ENCRYPT) #define EXT4_FEATURE_RO_COMPAT_SUPP (EXT4_FEATURE_RO_COMPAT_SPARSE_SUPER| \ EXT4_FEATURE_RO_COMPAT_LARGE_FILE| \ EXT4_FEATURE_RO_COMPAT_GDT_CSUM| \ diff --git a/fs/ext4/ialloc.c b/fs/ext4/ialloc.c index 850267c89407..2cf18a2d5c72 100644 --- a/fs/ext4/ialloc.c +++ b/fs/ext4/ialloc.c @@ -998,7 +998,8 @@ got: /* If the directory encrypted, then we should encrypt the inode. */ if ((S_ISDIR(mode) || S_ISREG(mode) || S_ISLNK(mode)) && - ext4_encrypted_inode(dir)) + (ext4_encrypted_inode(dir) || + DUMMY_ENCRYPTION_ENABLED(sbi))) ext4_set_inode_flag(inode, EXT4_INODE_ENCRYPT); ext4_set_inode_flags(inode); diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c index d201426b8d39..4f87127f781f 100644 --- a/fs/ext4/namei.c +++ b/fs/ext4/namei.c @@ -2582,7 +2582,8 @@ retry: ext4_set_aops(inode); err = 0; #ifdef CONFIG_EXT4_FS_ENCRYPTION - if (!err && ext4_encrypted_inode(dir)) { + if (!err && (ext4_encrypted_inode(dir) || + DUMMY_ENCRYPTION_ENABLED(EXT4_SB(dir->i_sb)))) { err = ext4_inherit_context(dir, inode); if (err) { clear_nlink(inode); @@ -2777,7 +2778,8 @@ retry: if (err) goto out_clear_inode; #ifdef CONFIG_EXT4_FS_ENCRYPTION - if (ext4_encrypted_inode(dir)) { + if (ext4_encrypted_inode(dir) || + DUMMY_ENCRYPTION_ENABLED(EXT4_SB(dir->i_sb))) { err = ext4_inherit_context(dir, inode); if (err) goto out_clear_inode; @@ -3202,7 +3204,8 @@ static int ext4_symlink(struct inode *dir, disk_link.len = len + 1; disk_link.name = (char *) symname; - encryption_required = ext4_encrypted_inode(dir); + encryption_required = (ext4_encrypted_inode(dir) || + DUMMY_ENCRYPTION_ENABLED(EXT4_SB(dir->i_sb))); if (encryption_required) disk_link.len = encrypted_symlink_data_len(len) + 1; if (disk_link.len > dir->i_sb->s_blocksize) diff --git a/fs/ext4/super.c b/fs/ext4/super.c index 1008ca258de4..f9ebd58f40dd 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -1106,7 +1106,7 @@ enum { Opt_commit, Opt_min_batch_time, Opt_max_batch_time, Opt_journal_dev, Opt_journal_path, Opt_journal_checksum, Opt_journal_async_commit, Opt_abort, Opt_data_journal, Opt_data_ordered, Opt_data_writeback, - Opt_data_err_abort, Opt_data_err_ignore, + Opt_data_err_abort, Opt_data_err_ignore, Opt_test_dummy_encryption, Opt_usrjquota, Opt_grpjquota, Opt_offusrjquota, Opt_offgrpjquota, Opt_jqfmt_vfsold, Opt_jqfmt_vfsv0, Opt_jqfmt_vfsv1, Opt_quota, Opt_noquota, Opt_barrier, Opt_nobarrier, Opt_err, @@ -1197,6 +1197,7 @@ static const match_table_t tokens = { {Opt_init_itable, "init_itable"}, {Opt_noinit_itable, "noinit_itable"}, {Opt_max_dir_size_kb, "max_dir_size_kb=%u"}, + {Opt_test_dummy_encryption, "test_dummy_encryption"}, {Opt_removed, "check=none"}, /* mount option from ext2/3 */ {Opt_removed, "nocheck"}, /* mount option from ext2/3 */ {Opt_removed, "reservation"}, /* mount option from ext2/3 */ @@ -1398,6 +1399,7 @@ static const struct mount_opts { {Opt_jqfmt_vfsv0, QFMT_VFS_V0, MOPT_QFMT}, {Opt_jqfmt_vfsv1, QFMT_VFS_V1, MOPT_QFMT}, {Opt_max_dir_size_kb, 0, MOPT_GTE0}, + {Opt_test_dummy_encryption, 0, MOPT_GTE0}, {Opt_err, 0, 0} }; @@ -1574,6 +1576,15 @@ static int handle_mount_opt(struct super_block *sb, char *opt, int token, } *journal_ioprio = IOPRIO_PRIO_VALUE(IOPRIO_CLASS_BE, arg); + } else if (token == Opt_test_dummy_encryption) { +#ifdef CONFIG_EXT4_FS_ENCRYPTION + sbi->s_mount_flags |= EXT4_MF_TEST_DUMMY_ENCRYPTION; + ext4_msg(sb, KERN_WARNING, + "Test dummy encryption mode enabled"); +#else + ext4_msg(sb, KERN_WARNING, + "Test dummy encryption mount option ignored"); +#endif } else if (m->flags & MOPT_DATAJ) { if (is_remount) { if (!sbi->s_journal) @@ -2671,11 +2682,13 @@ static struct attribute *ext4_attrs[] = { EXT4_INFO_ATTR(lazy_itable_init); EXT4_INFO_ATTR(batched_discard); EXT4_INFO_ATTR(meta_bg_resize); +EXT4_INFO_ATTR(encryption); static struct attribute *ext4_feat_attrs[] = { ATTR_LIST(lazy_itable_init), ATTR_LIST(batched_discard), ATTR_LIST(meta_bg_resize), + ATTR_LIST(encryption), NULL, }; @@ -3683,6 +3696,13 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent) } } + if (EXT4_HAS_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_ENCRYPT) && + es->s_encryption_level) { + ext4_msg(sb, KERN_ERR, "Unsupported encryption level %d", + es->s_encryption_level); + goto failed_mount; + } + if (sb->s_blocksize != blocksize) { /* Validate the filesystem blocksize */ if (!sb_set_blocksize(sb, blocksize)) { @@ -4045,6 +4065,13 @@ no_journal: } } + if (unlikely(sbi->s_mount_flags & EXT4_MF_TEST_DUMMY_ENCRYPTION) && + !(sb->s_flags & MS_RDONLY) && + !EXT4_HAS_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_ENCRYPT)) { + EXT4_SET_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_ENCRYPT); + ext4_commit_super(sb, 1); + } + /* * Get the # of file system overhead blocks from the * superblock if present. -- cgit v1.2.3