From 7071b715873a66b69a9c0c5839963bb51aeae41b Mon Sep 17 00:00:00 2001 From: Konstantin Khlebnikov Date: Thu, 2 Apr 2015 16:32:15 -0400 Subject: ext4: fix bh leak on error paths in ext4_rename() and ext4_cross_rename() Release references to buffer-heads if ext4_journal_start() fails. Fixes: 5b61de757535 ("ext4: start handle at least possible moment when renaming files") Signed-off-by: Konstantin Khlebnikov Signed-off-by: Theodore Ts'o Reviewed-by: Jan Kara --- fs/ext4/namei.c | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) (limited to 'fs/ext4/namei.c') diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c index 28fe71a2904c..8110dd20ad3f 100644 --- a/fs/ext4/namei.c +++ b/fs/ext4/namei.c @@ -3264,12 +3264,18 @@ static int ext4_rename(struct inode *old_dir, struct dentry *old_dentry, EXT4_INDEX_EXTRA_TRANS_BLOCKS + 2); if (!(flags & RENAME_WHITEOUT)) { handle = ext4_journal_start(old.dir, EXT4_HT_DIR, credits); - if (IS_ERR(handle)) - return PTR_ERR(handle); + if (IS_ERR(handle)) { + retval = PTR_ERR(handle); + handle = NULL; + goto end_rename; + } } else { whiteout = ext4_whiteout_for_rename(&old, credits, &handle); - if (IS_ERR(whiteout)) - return PTR_ERR(whiteout); + if (IS_ERR(whiteout)) { + retval = PTR_ERR(whiteout); + whiteout = NULL; + goto end_rename; + } } if (IS_DIRSYNC(old.dir) || IS_DIRSYNC(new.dir)) @@ -3433,8 +3439,11 @@ static int ext4_cross_rename(struct inode *old_dir, struct dentry *old_dentry, handle = ext4_journal_start(old.dir, EXT4_HT_DIR, (2 * EXT4_DATA_TRANS_BLOCKS(old.dir->i_sb) + 2 * EXT4_INDEX_EXTRA_TRANS_BLOCKS + 2)); - if (IS_ERR(handle)) - return PTR_ERR(handle); + if (IS_ERR(handle)) { + retval = PTR_ERR(handle); + handle = NULL; + goto end_rename; + } if (IS_DIRSYNC(old.dir) || IS_DIRSYNC(new.dir)) ext4_handle_sync(handle); -- cgit v1.2.3 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/acl.c | 5 ----- fs/ext4/balloc.c | 1 - fs/ext4/bitmap.c | 1 - fs/ext4/block_validity.c | 1 - fs/ext4/dir.c | 2 -- fs/ext4/extents_status.c | 2 -- fs/ext4/file.c | 1 - fs/ext4/fsync.c | 1 - fs/ext4/hash.c | 1 - fs/ext4/ialloc.c | 1 - fs/ext4/inode.c | 2 -- fs/ext4/ioctl.c | 1 - fs/ext4/namei.c | 1 - fs/ext4/page-io.c | 2 -- fs/ext4/super.c | 1 - fs/ext4/symlink.c | 1 - fs/ext4/xattr.c | 1 - 17 files changed, 25 deletions(-) (limited to 'fs/ext4/namei.c') diff --git a/fs/ext4/acl.c b/fs/ext4/acl.c index d40c8dbbb0d6..69b1e73026a5 100644 --- a/fs/ext4/acl.c +++ b/fs/ext4/acl.c @@ -4,11 +4,6 @@ * Copyright (C) 2001-2003 Andreas Gruenbacher, */ -#include -#include -#include -#include -#include #include "ext4_jbd2.h" #include "ext4.h" #include "xattr.h" diff --git a/fs/ext4/balloc.c b/fs/ext4/balloc.c index 83a6f497c4e0..9fadcf43342d 100644 --- a/fs/ext4/balloc.c +++ b/fs/ext4/balloc.c @@ -14,7 +14,6 @@ #include #include #include -#include #include #include #include "ext4.h" diff --git a/fs/ext4/bitmap.c b/fs/ext4/bitmap.c index b610779a958c..4a606afb171f 100644 --- a/fs/ext4/bitmap.c +++ b/fs/ext4/bitmap.c @@ -8,7 +8,6 @@ */ #include -#include #include "ext4.h" unsigned int ext4_count_free(char *bitmap, unsigned int numchars) diff --git a/fs/ext4/block_validity.c b/fs/ext4/block_validity.c index 41eb9dcfac7e..3522340c7a99 100644 --- a/fs/ext4/block_validity.c +++ b/fs/ext4/block_validity.c @@ -16,7 +16,6 @@ #include #include #include -#include #include #include "ext4.h" diff --git a/fs/ext4/dir.c b/fs/ext4/dir.c index c24143ea9c08..9e1e9e7869cb 100644 --- a/fs/ext4/dir.c +++ b/fs/ext4/dir.c @@ -22,10 +22,8 @@ */ #include -#include #include #include -#include #include "ext4.h" #include "xattr.h" diff --git a/fs/ext4/extents_status.c b/fs/ext4/extents_status.c index e04d45733976..d33d5a6852b9 100644 --- a/fs/ext4/extents_status.c +++ b/fs/ext4/extents_status.c @@ -9,12 +9,10 @@ * * Ext4 extents status tree core functions. */ -#include #include #include #include #include "ext4.h" -#include "extents_status.h" #include diff --git a/fs/ext4/file.c b/fs/ext4/file.c index 33a09da16c9c..fcc6c1349186 100644 --- a/fs/ext4/file.c +++ b/fs/ext4/file.c @@ -20,7 +20,6 @@ #include #include -#include #include #include #include diff --git a/fs/ext4/fsync.c b/fs/ext4/fsync.c index a8bc47f75fa0..e9d632e9aa4b 100644 --- a/fs/ext4/fsync.c +++ b/fs/ext4/fsync.c @@ -26,7 +26,6 @@ #include #include #include -#include #include #include "ext4.h" diff --git a/fs/ext4/hash.c b/fs/ext4/hash.c index 3d586f02883e..e026aa941fd5 100644 --- a/fs/ext4/hash.c +++ b/fs/ext4/hash.c @@ -10,7 +10,6 @@ */ #include -#include #include #include "ext4.h" 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 diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 5cb9a212b86f..ff0c111e52eb 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -20,7 +20,6 @@ #include #include -#include #include #include #include @@ -36,7 +35,6 @@ #include #include #include -#include #include #include diff --git a/fs/ext4/ioctl.c b/fs/ext4/ioctl.c index f58a0d106726..f1aa32c2277c 100644 --- a/fs/ext4/ioctl.c +++ b/fs/ext4/ioctl.c @@ -8,7 +8,6 @@ */ #include -#include #include #include #include diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c index 8110dd20ad3f..efb64aee5b9e 100644 --- a/fs/ext4/namei.c +++ b/fs/ext4/namei.c @@ -26,7 +26,6 @@ #include #include -#include #include #include #include diff --git a/fs/ext4/page-io.c b/fs/ext4/page-io.c index b24a2541a9ba..5687e4750c1e 100644 --- a/fs/ext4/page-io.c +++ b/fs/ext4/page-io.c @@ -8,7 +8,6 @@ #include #include -#include #include #include #include @@ -25,7 +24,6 @@ #include #include #include -#include #include "ext4_jbd2.h" #include "xattr.h" diff --git a/fs/ext4/super.c b/fs/ext4/super.c index e061e66c8280..c681352f7c69 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -21,7 +21,6 @@ #include #include #include -#include #include #include #include diff --git a/fs/ext4/symlink.c b/fs/ext4/symlink.c index ff3711932018..489b34333ea4 100644 --- a/fs/ext4/symlink.c +++ b/fs/ext4/symlink.c @@ -18,7 +18,6 @@ */ #include -#include #include #include "ext4.h" #include "xattr.h" diff --git a/fs/ext4/xattr.c b/fs/ext4/xattr.c index 1e09fc77395c..99a2cf858c8f 100644 --- a/fs/ext4/xattr.c +++ b/fs/ext4/xattr.c @@ -55,7 +55,6 @@ #include #include #include -#include #include "ext4_jbd2.h" #include "ext4.h" #include "xattr.h" -- cgit v1.2.3 From e12fb97222fc41e8442896934f76d39ef99b590a Mon Sep 17 00:00:00 2001 From: Lukas Czerner Date: Fri, 3 Apr 2015 10:46:58 -0400 Subject: ext4: make fsync to sync parent dir in no-journal for real this time Previously commit 14ece1028b3ed53ffec1b1213ffc6acaf79ad77c added a support for for syncing parent directory of newly created inodes to make sure that the inode is not lost after a power failure in no-journal mode. However this does not work in majority of cases, namely: - if the directory has inline data - if the directory is already indexed - if the directory already has at least one block and: - the new entry fits into it - or we've successfully converted it to indexed So in those cases we might lose the inode entirely even after fsync in the no-journal mode. This also includes ext2 default mode obviously. I've noticed this while running xfstest generic/321 and even though the test should fail (we need to run fsck after a crash in no-journal mode) I could not find a newly created entries even when if it was fsynced before. Fix this by adjusting the ext4_add_entry() successful exit paths to set the inode EXT4_STATE_NEWENTRY so that fsync has the chance to fsync the parent directory as well. Signed-off-by: Lukas Czerner Signed-off-by: Theodore Ts'o Reviewed-by: Jan Kara Cc: Frank Mayhar Cc: stable@vger.kernel.org --- fs/ext4/namei.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) (limited to 'fs/ext4/namei.c') diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c index efb64aee5b9e..23a0b9bf822d 100644 --- a/fs/ext4/namei.c +++ b/fs/ext4/namei.c @@ -1864,7 +1864,7 @@ static int ext4_add_entry(handle_t *handle, struct dentry *dentry, struct inode *inode) { struct inode *dir = dentry->d_parent->d_inode; - struct buffer_head *bh; + struct buffer_head *bh = NULL; struct ext4_dir_entry_2 *de; struct ext4_dir_entry_tail *t; struct super_block *sb; @@ -1888,14 +1888,14 @@ static int ext4_add_entry(handle_t *handle, struct dentry *dentry, return retval; if (retval == 1) { retval = 0; - return retval; + goto out; } } if (is_dx(dir)) { retval = ext4_dx_add_entry(handle, dentry, inode); if (!retval || (retval != ERR_BAD_DX_DIR)) - return retval; + goto out; ext4_clear_inode_flag(dir, EXT4_INODE_INDEX); dx_fallback++; ext4_mark_inode_dirty(handle, dir); @@ -1907,14 +1907,15 @@ static int ext4_add_entry(handle_t *handle, struct dentry *dentry, return PTR_ERR(bh); retval = add_dirent_to_buf(handle, dentry, inode, NULL, bh); - if (retval != -ENOSPC) { - brelse(bh); - return retval; - } + if (retval != -ENOSPC) + goto out; if (blocks == 1 && !dx_fallback && - EXT4_HAS_COMPAT_FEATURE(sb, EXT4_FEATURE_COMPAT_DIR_INDEX)) - return make_indexed_dir(handle, dentry, inode, bh); + EXT4_HAS_COMPAT_FEATURE(sb, EXT4_FEATURE_COMPAT_DIR_INDEX)) { + retval = make_indexed_dir(handle, dentry, inode, bh); + bh = NULL; /* make_indexed_dir releases bh */ + goto out; + } brelse(bh); } bh = ext4_append(handle, dir, &block); @@ -1930,6 +1931,7 @@ static int ext4_add_entry(handle_t *handle, struct dentry *dentry, } retval = add_dirent_to_buf(handle, dentry, inode, de, bh); +out: brelse(bh); if (retval == 0) ext4_set_inode_state(inode, EXT4_STATE_NEWENTRY); -- cgit v1.2.3 From e875a2ddba06ff8e84d4ce1c2bf69b67e4bf3678 Mon Sep 17 00:00:00 2001 From: Michael Halcrow Date: Sat, 11 Apr 2015 07:46:49 -0400 Subject: ext4 crypto: export ext4_empty_dir() Required for future encryption xattr changes. Signed-off-by: Michael Halcrow Signed-off-by: Theodore Ts'o --- fs/ext4/ext4.h | 1 + fs/ext4/namei.c | 11 ++++++----- 2 files changed, 7 insertions(+), 5 deletions(-) (limited to 'fs/ext4/namei.c') diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index e1683829b2b5..180111de2302 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -2199,6 +2199,7 @@ extern int ext4_generic_delete_entry(handle_t *handle, void *entry_buf, int buf_size, int csum_size); +extern int ext4_empty_dir(struct inode *inode); /* resize.c */ extern int ext4_group_add(struct super_block *sb, diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c index 23a0b9bf822d..0dbd2d2937f7 100644 --- a/fs/ext4/namei.c +++ b/fs/ext4/namei.c @@ -2457,7 +2457,7 @@ out_stop: /* * routine to check that the specified directory is empty (for rmdir) */ -static int empty_dir(struct inode *inode) +int ext4_empty_dir(struct inode *inode) { unsigned int offset; struct buffer_head *bh; @@ -2725,7 +2725,7 @@ static int ext4_rmdir(struct inode *dir, struct dentry *dentry) goto end_rmdir; retval = -ENOTEMPTY; - if (!empty_dir(inode)) + if (!ext4_empty_dir(inode)) goto end_rmdir; handle = ext4_journal_start(dir, EXT4_HT_DIR, @@ -3285,7 +3285,7 @@ static int ext4_rename(struct inode *old_dir, struct dentry *old_dentry, if (S_ISDIR(old.inode->i_mode)) { if (new.inode) { retval = -ENOTEMPTY; - if (!empty_dir(new.inode)) + if (!ext4_empty_dir(new.inode)) goto end_rename; } else { retval = -EMLINK; @@ -3359,8 +3359,9 @@ static int ext4_rename(struct inode *old_dir, struct dentry *old_dentry, ext4_dec_count(handle, old.dir); if (new.inode) { - /* checked empty_dir above, can't have another parent, - * ext4_dec_count() won't work for many-linked dirs */ + /* checked ext4_empty_dir above, can't have another + * parent, ext4_dec_count() won't work for many-linked + * dirs */ clear_nlink(new.inode); } else { ext4_inc_count(handle, new.dir); -- cgit v1.2.3 From d9cdc903318171571f1cd1e5737fd0cab94186be Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Sun, 12 Apr 2015 00:55:08 -0400 Subject: ext4 crypto: enforce context consistency Enforce the following inheritance policy: 1) An unencrypted directory may contain encrypted or unencrypted files or directories. 2) All files or directories in a directory must be protected using the same key as their containing directory. As a result, assuming the following setup: mke2fs -t ext4 -Fq -O encrypt /dev/vdc mount -t ext4 /dev/vdc /vdc mkdir /vdc/a /vdc/b /vdc/c echo foo | e4crypt add_key /vdc/a echo bar | e4crypt add_key /vdc/b for i in a b c ; do cp /etc/motd /vdc/$i/motd-$i ; done Then we will see the following results: cd /vdc mv a b # will fail; /vdc/a and /vdc/b have different keys mv b/motd-b a # will fail, see above ln a/motd-a b # will fail, see above mv c a # will fail; all inodes in an encrypted directory # must be encrypted ln c/motd-c b # will fail, see above mv a/motd-a c # will succeed mv c/motd-a a # will succeed Signed-off-by: Michael Halcrow Signed-off-by: Theodore Ts'o --- fs/ext4/namei.c | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) (limited to 'fs/ext4/namei.c') diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c index 0dbd2d2937f7..acd79198b800 100644 --- a/fs/ext4/namei.c +++ b/fs/ext4/namei.c @@ -1416,6 +1416,18 @@ static struct dentry *ext4_lookup(struct inode *dir, struct dentry *dentry, unsi ino); return ERR_PTR(-EIO); } + if (!IS_ERR(inode) && ext4_encrypted_inode(dir) && + (S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) || + S_ISLNK(inode->i_mode)) && + !ext4_is_child_context_consistent_with_parent(dir, + inode)) { + iput(inode); + ext4_warning(inode->i_sb, + "Inconsistent encryption contexts: %lu/%lu\n", + (unsigned long) dir->i_ino, + (unsigned long) inode->i_ino); + return ERR_PTR(-EPERM); + } } return d_splice_alias(inode, dentry); } @@ -2944,7 +2956,9 @@ static int ext4_link(struct dentry *old_dentry, if (inode->i_nlink >= EXT4_LINK_MAX) return -EMLINK; - + if (ext4_encrypted_inode(dir) && + !ext4_is_child_context_consistent_with_parent(dir, inode)) + return -EPERM; dquot_initialize(dir); retry: @@ -3245,6 +3259,14 @@ static int ext4_rename(struct inode *old_dir, struct dentry *old_dentry, if (!old.bh || le32_to_cpu(old.de->inode) != old.inode->i_ino) goto end_rename; + if ((old.dir != new.dir) && + ext4_encrypted_inode(new.dir) && + !ext4_is_child_context_consistent_with_parent(new.dir, + old.inode)) { + retval = -EPERM; + goto end_rename; + } + new.bh = ext4_find_entry(new.dir, &new.dentry->d_name, &new.de, &new.inlined); if (IS_ERR(new.bh)) { -- cgit v1.2.3 From dde680cefc842e58524772099a3b8219e5abf551 Mon Sep 17 00:00:00 2001 From: Michael Halcrow Date: Sun, 12 Apr 2015 00:55:09 -0400 Subject: ext4 crypto: inherit encryption policies on inode and directory create Signed-off-by: Michael Halcrow Signed-off-by: Theodore Ts'o --- fs/ext4/namei.c | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) (limited to 'fs/ext4/namei.c') diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c index acd79198b800..77a63ff4aeb9 100644 --- a/fs/ext4/namei.c +++ b/fs/ext4/namei.c @@ -2253,7 +2253,19 @@ retry: else inode->i_fop = &ext4_file_operations; ext4_set_aops(inode); - err = ext4_add_nondir(handle, dentry, inode); + err = 0; +#ifdef CONFIG_EXT4_FS_ENCRYPTION + if (!err && ext4_encrypted_inode(dir)) { + err = ext4_inherit_context(dir, inode); + if (err) { + clear_nlink(inode); + unlock_new_inode(inode); + iput(inode); + } + } +#endif + if (!err) + err = ext4_add_nondir(handle, dentry, inode); if (!err && IS_DIRSYNC(dir)) ext4_handle_sync(handle); } @@ -2437,6 +2449,13 @@ retry: err = ext4_init_new_dir(handle, dir, inode); if (err) goto out_clear_inode; +#ifdef CONFIG_EXT4_FS_ENCRYPTION + if (ext4_encrypted_inode(dir)) { + err = ext4_inherit_context(dir, inode); + if (err) + goto out_clear_inode; + } +#endif err = ext4_mark_inode_dirty(handle, inode); if (!err) err = ext4_add_entry(handle, dentry, inode); -- cgit v1.2.3 From 2f61830ae33e2944ad66bb8bb40916f534b2e494 Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Sun, 12 Apr 2015 00:56:26 -0400 Subject: ext4 crypto: teach ext4_htree_store_dirent() to store decrypted filenames For encrypted directories, we need to pass in a separate parameter for the decrypted filename, since the directory entry contains the encrypted filename. Signed-off-by: Theodore Ts'o --- fs/ext4/dir.c | 15 ++++++++++----- fs/ext4/ext4.h | 5 +++-- fs/ext4/inline.c | 7 +++++-- fs/ext4/namei.c | 21 +++++++++++++++++---- 4 files changed, 35 insertions(+), 13 deletions(-) (limited to 'fs/ext4/namei.c') diff --git a/fs/ext4/dir.c b/fs/ext4/dir.c index 9e1e9e7869cb..315f13ad382e 100644 --- a/fs/ext4/dir.c +++ b/fs/ext4/dir.c @@ -382,10 +382,15 @@ void ext4_htree_free_dir_info(struct dir_private_info *p) /* * Given a directory entry, enter it into the fname rb tree. + * + * When filename encryption is enabled, the dirent will hold the + * encrypted filename, while the htree will hold decrypted filename. + * The decrypted filename is passed in via ent_name. parameter. */ int ext4_htree_store_dirent(struct file *dir_file, __u32 hash, __u32 minor_hash, - struct ext4_dir_entry_2 *dirent) + struct ext4_dir_entry_2 *dirent, + struct ext4_str *ent_name) { struct rb_node **p, *parent = NULL; struct fname *fname, *new_fn; @@ -396,17 +401,17 @@ int ext4_htree_store_dirent(struct file *dir_file, __u32 hash, p = &info->root.rb_node; /* Create and allocate the fname structure */ - len = sizeof(struct fname) + dirent->name_len + 1; + len = sizeof(struct fname) + ent_name->len + 1; new_fn = kzalloc(len, GFP_KERNEL); if (!new_fn) return -ENOMEM; new_fn->hash = hash; new_fn->minor_hash = minor_hash; new_fn->inode = le32_to_cpu(dirent->inode); - new_fn->name_len = dirent->name_len; + new_fn->name_len = ent_name->len; new_fn->file_type = dirent->file_type; - memcpy(new_fn->name, dirent->name, dirent->name_len); - new_fn->name[dirent->name_len] = 0; + memcpy(new_fn->name, ent_name->name, ent_name->len); + new_fn->name[ent_name->len] = 0; while (*p) { parent = *p; diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index 3462532b227f..ba75838f3588 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -2142,8 +2142,9 @@ extern int __ext4_check_dir_entry(const char *, unsigned int, struct inode *, unlikely(__ext4_check_dir_entry(__func__, __LINE__, (dir), (filp), \ (de), (bh), (buf), (size), (offset))) extern int ext4_htree_store_dirent(struct file *dir_file, __u32 hash, - __u32 minor_hash, - struct ext4_dir_entry_2 *dirent); + __u32 minor_hash, + struct ext4_dir_entry_2 *dirent, + struct ext4_str *ent_name); extern void ext4_htree_free_dir_info(struct dir_private_info *p); extern int ext4_find_dest_de(struct inode *dir, struct inode *inode, struct buffer_head *bh, diff --git a/fs/ext4/inline.c b/fs/ext4/inline.c index 8b64d715e476..661f0b8dcfe0 100644 --- a/fs/ext4/inline.c +++ b/fs/ext4/inline.c @@ -1327,6 +1327,7 @@ int htree_inlinedir_to_tree(struct file *dir_file, struct ext4_iloc iloc; void *dir_buf = NULL; struct ext4_dir_entry_2 fake; + struct ext4_str tmp_str; ret = ext4_get_inode_loc(inode, &iloc); if (ret) @@ -1398,8 +1399,10 @@ int htree_inlinedir_to_tree(struct file *dir_file, continue; if (de->inode == 0) continue; - err = ext4_htree_store_dirent(dir_file, - hinfo->hash, hinfo->minor_hash, de); + tmp_str.name = de->name; + tmp_str.len = de->name_len; + err = ext4_htree_store_dirent(dir_file, hinfo->hash, + hinfo->minor_hash, de, &tmp_str); if (err) { count = err; goto out; diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c index 77a63ff4aeb9..8cef115ee64a 100644 --- a/fs/ext4/namei.c +++ b/fs/ext4/namei.c @@ -877,6 +877,7 @@ static int htree_dirblock_to_tree(struct file *dir_file, struct buffer_head *bh; struct ext4_dir_entry_2 *de, *top; int err = 0, count = 0; + struct ext4_str tmp_str; dxtrace(printk(KERN_INFO "In htree dirblock_to_tree: block %lu\n", (unsigned long)block)); @@ -903,8 +904,11 @@ static int htree_dirblock_to_tree(struct file *dir_file, continue; if (de->inode == 0) continue; - if ((err = ext4_htree_store_dirent(dir_file, - hinfo->hash, hinfo->minor_hash, de)) != 0) { + tmp_str.name = de->name; + tmp_str.len = de->name_len; + err = ext4_htree_store_dirent(dir_file, + hinfo->hash, hinfo->minor_hash, de, &tmp_str); + if (err != 0) { brelse(bh); return err; } @@ -934,6 +938,7 @@ int ext4_htree_fill_tree(struct file *dir_file, __u32 start_hash, int count = 0; int ret, err; __u32 hashval; + struct ext4_str tmp_str; dxtrace(printk(KERN_DEBUG "In htree_fill_tree, start hash: %x:%x\n", start_hash, start_minor_hash)); @@ -969,14 +974,22 @@ int ext4_htree_fill_tree(struct file *dir_file, __u32 start_hash, /* Add '.' and '..' from the htree header */ if (!start_hash && !start_minor_hash) { de = (struct ext4_dir_entry_2 *) frames[0].bh->b_data; - if ((err = ext4_htree_store_dirent(dir_file, 0, 0, de)) != 0) + tmp_str.name = de->name; + tmp_str.len = de->name_len; + err = ext4_htree_store_dirent(dir_file, 0, 0, + de, &tmp_str); + if (err != 0) goto errout; count++; } if (start_hash < 2 || (start_hash ==2 && start_minor_hash==0)) { de = (struct ext4_dir_entry_2 *) frames[0].bh->b_data; de = ext4_next_entry(de, dir->i_sb->s_blocksize); - if ((err = ext4_htree_store_dirent(dir_file, 2, 0, de)) != 0) + tmp_str.name = de->name; + tmp_str.len = de->name_len; + err = ext4_htree_store_dirent(dir_file, 2, 0, + de, &tmp_str); + if (err != 0) goto errout; count++; } -- cgit v1.2.3 From 4bdfc873ba34e425d6532581b4127b960274272a Mon Sep 17 00:00:00 2001 From: Michael Halcrow Date: Sun, 12 Apr 2015 00:56:28 -0400 Subject: ext4 crypto: insert encrypted filenames into a leaf directory block Signed-off-by: Uday Savagaonkar Signed-off-by: Ildar Muslukhov Signed-off-by: Michael Halcrow Signed-off-by: Theodore Ts'o --- fs/ext4/ext4.h | 4 ++- fs/ext4/inline.c | 7 +++-- fs/ext4/namei.c | 81 +++++++++++++++++++++++++++++++++++++++++++++++++------- 3 files changed, 79 insertions(+), 13 deletions(-) (limited to 'fs/ext4/namei.c') diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index ba75838f3588..5146e67e8d51 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -2151,9 +2151,11 @@ extern int ext4_find_dest_de(struct inode *dir, struct inode *inode, void *buf, int buf_size, const char *name, int namelen, struct ext4_dir_entry_2 **dest_de); -void ext4_insert_dentry(struct inode *inode, +int ext4_insert_dentry(struct inode *dir, + struct inode *inode, struct ext4_dir_entry_2 *de, int buf_size, + const struct qstr *iname, const char *name, int namelen); static inline void ext4_update_dx_flag(struct inode *inode) { diff --git a/fs/ext4/inline.c b/fs/ext4/inline.c index 661f0b8dcfe0..feb2cafbeace 100644 --- a/fs/ext4/inline.c +++ b/fs/ext4/inline.c @@ -11,11 +11,13 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ + +#include + #include "ext4_jbd2.h" #include "ext4.h" #include "xattr.h" #include "truncate.h" -#include #define EXT4_XATTR_SYSTEM_DATA "data" #define EXT4_MIN_INLINE_DATA_SIZE ((sizeof(__le32) * EXT4_N_BLOCKS)) @@ -1014,7 +1016,8 @@ static int ext4_add_dirent_to_inline(handle_t *handle, err = ext4_journal_get_write_access(handle, iloc->bh); if (err) return err; - ext4_insert_dentry(inode, de, inline_size, name, namelen); + ext4_insert_dentry(dir, inode, de, inline_size, &dentry->d_name, + name, namelen); ext4_show_inline_dir(dir, iloc->bh, inline_start, inline_size); diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c index 8cef115ee64a..eb11a1b8a3d5 100644 --- a/fs/ext4/namei.c +++ b/fs/ext4/namei.c @@ -1665,19 +1665,49 @@ int ext4_find_dest_de(struct inode *dir, struct inode *inode, return 0; } -void ext4_insert_dentry(struct inode *inode, - struct ext4_dir_entry_2 *de, - int buf_size, - const char *name, int namelen) +int ext4_insert_dentry(struct inode *dir, + struct inode *inode, + struct ext4_dir_entry_2 *de, + int buf_size, + const struct qstr *iname, + const char *name, int namelen) { int nlen, rlen; + struct ext4_fname_crypto_ctx *ctx = NULL; + struct ext4_str fname_crypto_str = {.name = NULL, .len = 0}; + struct ext4_str tmp_str; + int res; + + ctx = ext4_get_fname_crypto_ctx(dir, EXT4_NAME_LEN); + if (IS_ERR(ctx)) + return -EIO; + /* By default, the input name would be written to the disk */ + tmp_str.name = (unsigned char *)name; + tmp_str.len = namelen; + if (ctx != NULL) { + /* Directory is encrypted */ + res = ext4_fname_crypto_alloc_buffer(ctx, EXT4_NAME_LEN, + &fname_crypto_str); + if (res < 0) { + ext4_put_fname_crypto_ctx(&ctx); + return -ENOMEM; + } + res = ext4_fname_usr_to_disk(ctx, iname, &fname_crypto_str); + if (res < 0) { + ext4_put_fname_crypto_ctx(&ctx); + ext4_fname_crypto_free_buffer(&fname_crypto_str); + return res; + } + tmp_str.name = fname_crypto_str.name; + tmp_str.len = fname_crypto_str.len; + } nlen = EXT4_DIR_REC_LEN(de->name_len); rlen = ext4_rec_len_from_disk(de->rec_len, buf_size); if (de->inode) { struct ext4_dir_entry_2 *de1 = - (struct ext4_dir_entry_2 *)((char *)de + nlen); + (struct ext4_dir_entry_2 *)((char *)de + nlen); de1->rec_len = ext4_rec_len_to_disk(rlen - nlen, buf_size); de->rec_len = ext4_rec_len_to_disk(nlen, buf_size); de = de1; @@ -1685,9 +1715,14 @@ void ext4_insert_dentry(struct inode *inode, de->file_type = EXT4_FT_UNKNOWN; de->inode = cpu_to_le32(inode->i_ino); ext4_set_de_type(inode->i_sb, de, inode->i_mode); - de->name_len = namelen; - memcpy(de->name, name, namelen); + de->name_len = tmp_str.len; + + memcpy(de->name, tmp_str.name, tmp_str.len); + ext4_put_fname_crypto_ctx(&ctx); + ext4_fname_crypto_free_buffer(&fname_crypto_str); + return 0; } + /* * Add a new entry into a directory (leaf) block. If de is non-NULL, * it points to a directory entry which is guaranteed to be large @@ -1724,8 +1759,12 @@ static int add_dirent_to_buf(handle_t *handle, struct dentry *dentry, return err; } - /* By now the buffer is marked for journaling */ - ext4_insert_dentry(inode, de, blocksize, name, namelen); + /* By now the buffer is marked for journaling. Due to crypto operations, + * the following function call may fail */ + err = ext4_insert_dentry(dir, inode, de, blocksize, &dentry->d_name, + name, namelen); + if (err < 0) + return err; /* * XXX shouldn't update any times until successful @@ -1757,8 +1796,13 @@ static int make_indexed_dir(handle_t *handle, struct dentry *dentry, struct inode *inode, struct buffer_head *bh) { struct inode *dir = dentry->d_parent->d_inode; +#ifdef CONFIG_EXT4_FS_ENCRYPTION + struct ext4_fname_crypto_ctx *ctx = NULL; + int res; +#else const char *name = dentry->d_name.name; int namelen = dentry->d_name.len; +#endif struct buffer_head *bh2; struct dx_root *root; struct dx_frame frames[2], *frame; @@ -1772,7 +1816,13 @@ static int make_indexed_dir(handle_t *handle, struct dentry *dentry, struct dx_hash_info hinfo; ext4_lblk_t block; struct fake_dirent *fde; - int csum_size = 0; + int csum_size = 0; + +#ifdef CONFIG_EXT4_FS_ENCRYPTION + ctx = ext4_get_fname_crypto_ctx(dir, EXT4_NAME_LEN); + if (IS_ERR(ctx)) + return PTR_ERR(ctx); +#endif if (ext4_has_metadata_csum(inode->i_sb)) csum_size = sizeof(struct ext4_dir_entry_tail); @@ -1839,7 +1889,18 @@ static int make_indexed_dir(handle_t *handle, struct dentry *dentry, if (hinfo.hash_version <= DX_HASH_TEA) hinfo.hash_version += EXT4_SB(dir->i_sb)->s_hash_unsigned; hinfo.seed = EXT4_SB(dir->i_sb)->s_hash_seed; +#ifdef CONFIG_EXT4_FS_ENCRYPTION + res = ext4_fname_usr_to_hash(ctx, &dentry->d_name, &hinfo); + if (res < 0) { + ext4_put_fname_crypto_ctx(&ctx); + ext4_mark_inode_dirty(handle, dir); + brelse(bh); + return res; + } + ext4_put_fname_crypto_ctx(&ctx); +#else ext4fs_dirhash(name, namelen, &hinfo); +#endif memset(frames, 0, sizeof(frames)); frame = frames; frame->entries = entries; -- cgit v1.2.3 From b30984864406ad01b72eee486add103e9cb3526f Mon Sep 17 00:00:00 2001 From: Michael Halcrow Date: Sun, 12 Apr 2015 01:07:01 -0400 Subject: ext4 crypto: partial update to namei.c for fname crypto Modifies dx_show_leaf and dx_probe to support fname encryption. Filename encryption not yet enabled. Signed-off-by: Uday Savagaonkar Signed-off-by: Ildar Muslukhov Signed-off-by: Michael Halcrow Signed-off-by: Theodore Ts'o --- fs/ext4/namei.c | 109 +++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 101 insertions(+), 8 deletions(-) (limited to 'fs/ext4/namei.c') diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c index eb11a1b8a3d5..85add9a9e1cd 100644 --- a/fs/ext4/namei.c +++ b/fs/ext4/namei.c @@ -585,8 +585,10 @@ struct stats unsigned bcount; }; -static struct stats dx_show_leaf(struct dx_hash_info *hinfo, struct ext4_dir_entry_2 *de, - int size, int show_names) +static struct stats dx_show_leaf(struct inode *dir, + struct dx_hash_info *hinfo, + struct ext4_dir_entry_2 *de, + int size, int show_names) { unsigned names = 0, space = 0; char *base = (char *) de; @@ -599,12 +601,80 @@ static struct stats dx_show_leaf(struct dx_hash_info *hinfo, struct ext4_dir_ent { if (show_names) { +#ifdef CONFIG_EXT4_FS_ENCRYPTION + int len; + char *name; + struct ext4_str fname_crypto_str + = {.name = NULL, .len = 0}; + struct ext4_fname_crypto_ctx *ctx = NULL; + int res; + + name = de->name; + len = de->name_len; + ctx = ext4_get_fname_crypto_ctx(dir, + EXT4_NAME_LEN); + if (IS_ERR(ctx)) { + printk(KERN_WARNING "Error acquiring" + " crypto ctxt--skipping crypto\n"); + ctx = NULL; + } + if (ctx == NULL) { + /* Directory is not encrypted */ + ext4fs_dirhash(de->name, + de->name_len, &h); + printk("%*.s:(U)%x.%u ", len, + name, h.hash, + (unsigned) ((char *) de + - base)); + } else { + /* Directory is encrypted */ + res = ext4_fname_crypto_alloc_buffer( + ctx, de->name_len, + &fname_crypto_str); + if (res < 0) { + printk(KERN_WARNING "Error " + "allocating crypto " + "buffer--skipping " + "crypto\n"); + ext4_put_fname_crypto_ctx(&ctx); + ctx = NULL; + } + res = ext4_fname_disk_to_usr(ctx, de, + &fname_crypto_str); + if (res < 0) { + printk(KERN_WARNING "Error " + "converting filename " + "from disk to usr" + "\n"); + name = "??"; + len = 2; + } else { + name = fname_crypto_str.name; + len = fname_crypto_str.len; + } + res = ext4_fname_disk_to_hash(ctx, de, + &h); + if (res < 0) { + printk(KERN_WARNING "Error " + "converting filename " + "from disk to htree" + "\n"); + h.hash = 0xDEADBEEF; + } + printk("%*.s:(E)%x.%u ", len, name, + h.hash, (unsigned) ((char *) de + - base)); + ext4_put_fname_crypto_ctx(&ctx); + ext4_fname_crypto_free_buffer( + &fname_crypto_str); + } +#else int len = de->name_len; char *name = de->name; - while (len--) printk("%c", *name++); ext4fs_dirhash(de->name, de->name_len, &h); - printk(":%x.%u ", h.hash, + printk("%*.s:%x.%u ", len, name, h.hash, (unsigned) ((char *) de - base)); +#endif } space += EXT4_DIR_REC_LEN(de->name_len); names++; @@ -622,7 +692,6 @@ struct stats dx_show_entries(struct dx_hash_info *hinfo, struct inode *dir, unsigned count = dx_get_count(entries), names = 0, space = 0, i; unsigned bcount = 0; struct buffer_head *bh; - int err; printk("%i indexed blocks...\n", count); for (i = 0; i < count; i++, entries++) { @@ -636,7 +705,8 @@ struct stats dx_show_entries(struct dx_hash_info *hinfo, struct inode *dir, continue; stats = levels? dx_show_entries(hinfo, dir, ((struct dx_node *) bh->b_data)->entries, levels - 1): - dx_show_leaf(hinfo, (struct ext4_dir_entry_2 *) bh->b_data, blocksize, 0); + dx_show_leaf(dir, hinfo, (struct ext4_dir_entry_2 *) + bh->b_data, blocksize, 0); names += stats.names; space += stats.space; bcount += stats.bcount; @@ -686,8 +756,28 @@ dx_probe(const struct qstr *d_name, struct inode *dir, if (hinfo->hash_version <= DX_HASH_TEA) hinfo->hash_version += EXT4_SB(dir->i_sb)->s_hash_unsigned; hinfo->seed = EXT4_SB(dir->i_sb)->s_hash_seed; +#ifdef CONFIG_EXT4_FS_ENCRYPTION + if (d_name) { + struct ext4_fname_crypto_ctx *ctx = NULL; + int res; + + /* Check if the directory is encrypted */ + ctx = ext4_get_fname_crypto_ctx(dir, EXT4_NAME_LEN); + if (IS_ERR(ctx)) { + ret_err = ERR_PTR(PTR_ERR(ctx)); + goto fail; + } + res = ext4_fname_usr_to_hash(ctx, d_name, hinfo); + if (res < 0) { + ret_err = ERR_PTR(res); + goto fail; + } + ext4_put_fname_crypto_ctx(&ctx); + } +#else if (d_name) ext4fs_dirhash(d_name->name, d_name->len, hinfo); +#endif hash = hinfo->hash; if (root->info.unused_flags & 1) { @@ -772,6 +862,7 @@ fail: brelse(frame->bh); frame--; } + if (ret_err == ERR_PTR(ERR_BAD_DX_DIR)) ext4_warning(dir->i_sb, "Corrupt dir inode %lu, running e2fsck is " @@ -1604,8 +1695,10 @@ static struct ext4_dir_entry_2 *do_split(handle_t *handle, struct inode *dir, initialize_dirent_tail(t, blocksize); } - dxtrace(dx_show_leaf (hinfo, (struct ext4_dir_entry_2 *) data1, blocksize, 1)); - dxtrace(dx_show_leaf (hinfo, (struct ext4_dir_entry_2 *) data2, blocksize, 1)); + dxtrace(dx_show_leaf(dir, hinfo, (struct ext4_dir_entry_2 *) data1, + blocksize, 1)); + dxtrace(dx_show_leaf(dir, hinfo, (struct ext4_dir_entry_2 *) data2, + blocksize, 1)); /* Which block gets the new entry? */ if (hinfo->hash >= hash2) { -- cgit v1.2.3 From 1f3862b5575b138e83080232706e37ee24b8093e Mon Sep 17 00:00:00 2001 From: Michael Halcrow Date: Sun, 12 Apr 2015 01:09:03 -0400 Subject: ext4 crypto: filename encryption modifications Modifies htree_dirblock_to_tree, dx_make_map, ext4_match search_dir, and ext4_find_dest_de to support fname crypto. Filename encryption feature is not yet enabled at this patch. Signed-off-by: Uday Savagaonkar Signed-off-by: Ildar Muslukhov Signed-off-by: Michael Halcrow Signed-off-by: Theodore Ts'o --- fs/ext4/namei.c | 248 ++++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 204 insertions(+), 44 deletions(-) (limited to 'fs/ext4/namei.c') diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c index 85add9a9e1cd..4c84db862891 100644 --- a/fs/ext4/namei.c +++ b/fs/ext4/namei.c @@ -253,8 +253,9 @@ static struct dx_frame *dx_probe(const struct qstr *d_name, struct dx_hash_info *hinfo, struct dx_frame *frame); static void dx_release(struct dx_frame *frames); -static int dx_make_map(struct ext4_dir_entry_2 *de, unsigned blocksize, - struct dx_hash_info *hinfo, struct dx_map_entry map[]); +static int dx_make_map(struct inode *dir, struct ext4_dir_entry_2 *de, + unsigned blocksize, struct dx_hash_info *hinfo, + struct dx_map_entry map[]); static void dx_sort_map(struct dx_map_entry *map, unsigned count); static struct ext4_dir_entry_2 *dx_move_dirents(char *from, char *to, struct dx_map_entry *offsets, int count, unsigned blocksize); @@ -968,7 +969,8 @@ static int htree_dirblock_to_tree(struct file *dir_file, struct buffer_head *bh; struct ext4_dir_entry_2 *de, *top; int err = 0, count = 0; - struct ext4_str tmp_str; + struct ext4_fname_crypto_ctx *ctx = NULL; + struct ext4_str fname_crypto_str = {.name = NULL, .len = 0}, tmp_str; dxtrace(printk(KERN_INFO "In htree dirblock_to_tree: block %lu\n", (unsigned long)block)); @@ -980,6 +982,24 @@ static int htree_dirblock_to_tree(struct file *dir_file, top = (struct ext4_dir_entry_2 *) ((char *) de + dir->i_sb->s_blocksize - EXT4_DIR_REC_LEN(0)); +#ifdef CONFIG_EXT4_FS_ENCRYPTION + /* Check if the directory is encrypted */ + ctx = ext4_get_fname_crypto_ctx(dir, EXT4_NAME_LEN); + if (IS_ERR(ctx)) { + err = PTR_ERR(ctx); + brelse(bh); + return err; + } + if (ctx != NULL) { + err = ext4_fname_crypto_alloc_buffer(ctx, EXT4_NAME_LEN, + &fname_crypto_str); + if (err < 0) { + ext4_put_fname_crypto_ctx(&ctx); + brelse(bh); + return err; + } + } +#endif for (; de < top; de = ext4_next_entry(de, dir->i_sb->s_blocksize)) { if (ext4_check_dir_entry(dir, NULL, de, bh, bh->b_data, bh->b_size, @@ -988,24 +1008,52 @@ static int htree_dirblock_to_tree(struct file *dir_file, /* silently ignore the rest of the block */ break; } +#ifdef CONFIG_EXT4_FS_ENCRYPTION + err = ext4_fname_disk_to_hash(ctx, de, hinfo); + if (err < 0) { + count = err; + goto errout; + } +#else ext4fs_dirhash(de->name, de->name_len, hinfo); +#endif if ((hinfo->hash < start_hash) || ((hinfo->hash == start_hash) && (hinfo->minor_hash < start_minor_hash))) continue; if (de->inode == 0) continue; - tmp_str.name = de->name; - tmp_str.len = de->name_len; - err = ext4_htree_store_dirent(dir_file, - hinfo->hash, hinfo->minor_hash, de, &tmp_str); + if (ctx == NULL) { + /* Directory is not encrypted */ + tmp_str.name = de->name; + tmp_str.len = de->name_len; + err = ext4_htree_store_dirent(dir_file, + hinfo->hash, hinfo->minor_hash, de, + &tmp_str); + } else { + /* Directory is encrypted */ + err = ext4_fname_disk_to_usr(ctx, de, + &fname_crypto_str); + if (err < 0) { + count = err; + goto errout; + } + err = ext4_htree_store_dirent(dir_file, + hinfo->hash, hinfo->minor_hash, de, + &fname_crypto_str); + } if (err != 0) { - brelse(bh); - return err; + count = err; + goto errout; } count++; } +errout: brelse(bh); +#ifdef CONFIG_EXT4_FS_ENCRYPTION + ext4_put_fname_crypto_ctx(&ctx); + ext4_fname_crypto_free_buffer(&fname_crypto_str); +#endif return count; } @@ -1138,17 +1186,33 @@ static inline int search_dirblock(struct buffer_head *bh, * Create map of hash values, offsets, and sizes, stored at end of block. * Returns number of entries mapped. */ -static int dx_make_map(struct ext4_dir_entry_2 *de, unsigned blocksize, - struct dx_hash_info *hinfo, +static int dx_make_map(struct inode *dir, struct ext4_dir_entry_2 *de, + unsigned blocksize, struct dx_hash_info *hinfo, struct dx_map_entry *map_tail) { int count = 0; char *base = (char *) de; struct dx_hash_info h = *hinfo; +#ifdef CONFIG_EXT4_FS_ENCRYPTION + struct ext4_fname_crypto_ctx *ctx = NULL; + int err; + + ctx = ext4_get_fname_crypto_ctx(dir, EXT4_NAME_LEN); + if (IS_ERR(ctx)) + return PTR_ERR(ctx); +#endif while ((char *) de < base + blocksize) { if (de->name_len && de->inode) { +#ifdef CONFIG_EXT4_FS_ENCRYPTION + err = ext4_fname_disk_to_hash(ctx, de, &h); + if (err < 0) { + ext4_put_fname_crypto_ctx(&ctx); + return err; + } +#else ext4fs_dirhash(de->name, de->name_len, &h); +#endif map_tail--; map_tail->hash = h.hash; map_tail->offs = ((char *) de - base)>>2; @@ -1159,6 +1223,9 @@ static int dx_make_map(struct ext4_dir_entry_2 *de, unsigned blocksize, /* XXX: do we need to check rec_len == 0 case? -Chris */ de = ext4_next_entry(de, blocksize); } +#ifdef CONFIG_EXT4_FS_ENCRYPTION + ext4_put_fname_crypto_ctx(&ctx); +#endif return count; } @@ -1209,57 +1276,107 @@ static void dx_insert_block(struct dx_frame *frame, u32 hash, ext4_lblk_t block) * `len <= EXT4_NAME_LEN' is guaranteed by caller. * `de != NULL' is guaranteed by caller. */ -static inline int ext4_match (int len, const char * const name, - struct ext4_dir_entry_2 * de) +static inline int ext4_match(struct ext4_fname_crypto_ctx *ctx, + struct ext4_str *fname_crypto_str, + int len, const char * const name, + struct ext4_dir_entry_2 *de) { - if (len != de->name_len) - return 0; + int res; + if (!de->inode) return 0; - return !memcmp(name, de->name, len); + +#ifdef CONFIG_EXT4_FS_ENCRYPTION + if (ctx) { + /* Directory is encrypted */ + res = ext4_fname_disk_to_usr(ctx, de, fname_crypto_str); + if (res < 0) + return res; + if (len != res) + return 0; + res = memcmp(name, fname_crypto_str->name, len); + return (res == 0) ? 1 : 0; + } +#endif + if (len != de->name_len) + return 0; + res = memcmp(name, de->name, len); + return (res == 0) ? 1 : 0; } /* * Returns 0 if not found, -1 on failure, and 1 on success */ -int search_dir(struct buffer_head *bh, - char *search_buf, - int buf_size, - struct inode *dir, - const struct qstr *d_name, - unsigned int offset, - struct ext4_dir_entry_2 **res_dir) +int search_dir(struct buffer_head *bh, char *search_buf, int buf_size, + struct inode *dir, const struct qstr *d_name, + unsigned int offset, struct ext4_dir_entry_2 **res_dir) { struct ext4_dir_entry_2 * de; char * dlimit; int de_len; const char *name = d_name->name; int namelen = d_name->len; + struct ext4_fname_crypto_ctx *ctx = NULL; + struct ext4_str fname_crypto_str = {.name = NULL, .len = 0}; + int res; + + ctx = ext4_get_fname_crypto_ctx(dir, EXT4_NAME_LEN); + if (IS_ERR(ctx)) + return -1; + + if (ctx != NULL) { + /* Allocate buffer to hold maximum name length */ + res = ext4_fname_crypto_alloc_buffer(ctx, EXT4_NAME_LEN, + &fname_crypto_str); + if (res < 0) { + ext4_put_fname_crypto_ctx(&ctx); + return -1; + } + } de = (struct ext4_dir_entry_2 *)search_buf; dlimit = search_buf + buf_size; while ((char *) de < dlimit) { /* this code is executed quadratically often */ /* do minimal checking `by hand' */ + if ((char *) de + de->name_len <= dlimit) { + res = ext4_match(ctx, &fname_crypto_str, namelen, + name, de); + if (res < 0) { + res = -1; + goto return_result; + } + if (res > 0) { + /* found a match - just to be sure, do + * a full check */ + if (ext4_check_dir_entry(dir, NULL, de, bh, + bh->b_data, + bh->b_size, offset)) { + res = -1; + goto return_result; + } + *res_dir = de; + res = 1; + goto return_result; + } - if ((char *) de + namelen <= dlimit && - ext4_match (namelen, name, de)) { - /* found a match - just to be sure, do a full check */ - if (ext4_check_dir_entry(dir, NULL, de, bh, bh->b_data, - bh->b_size, offset)) - return -1; - *res_dir = de; - return 1; } /* prevent looping on a bad block */ de_len = ext4_rec_len_from_disk(de->rec_len, dir->i_sb->s_blocksize); - if (de_len <= 0) - return -1; + if (de_len <= 0) { + res = -1; + goto return_result; + } offset += de_len; de = (struct ext4_dir_entry_2 *) ((char *) de + de_len); } - return 0; + + res = 0; +return_result: + ext4_put_fname_crypto_ctx(&ctx); + ext4_fname_crypto_free_buffer(&fname_crypto_str); + return res; } static int is_dx_internal_node(struct inode *dir, ext4_lblk_t block, @@ -1448,6 +1565,9 @@ static struct buffer_head * ext4_dx_find_entry(struct inode *dir, const struct q ext4_lblk_t block; int retval; +#ifdef CONFIG_EXT4_FS_ENCRYPTION + *res_dir = NULL; +#endif frame = dx_probe(d_name, dir, &hinfo, frames); if (IS_ERR(frame)) return (struct buffer_head *) frame; @@ -1656,7 +1776,7 @@ static struct ext4_dir_entry_2 *do_split(handle_t *handle, struct inode *dir, /* create map in the end of data2 block */ map = (struct dx_map_entry *) (data2 + blocksize); - count = dx_make_map((struct ext4_dir_entry_2 *) data1, + count = dx_make_map(dir, (struct ext4_dir_entry_2 *) data1, blocksize, hinfo, map); map -= count; dx_sort_map(map, count); @@ -1679,7 +1799,8 @@ static struct ext4_dir_entry_2 *do_split(handle_t *handle, struct inode *dir, hash2, split, count-split)); /* Fancy dance to stay within two buffers */ - de2 = dx_move_dirents(data1, data2, map + split, count - split, blocksize); + de2 = dx_move_dirents(data1, data2, map + split, count - split, + blocksize); de = dx_pack_dirents(data1, blocksize); de->rec_len = ext4_rec_len_to_disk(data1 + (blocksize - csum_size) - (char *) de, @@ -1735,15 +1856,48 @@ int ext4_find_dest_de(struct inode *dir, struct inode *inode, int nlen, rlen; unsigned int offset = 0; char *top; + struct ext4_fname_crypto_ctx *ctx = NULL; + struct ext4_str fname_crypto_str = {.name = NULL, .len = 0}; + int res; + + ctx = ext4_get_fname_crypto_ctx(dir, EXT4_NAME_LEN); + if (IS_ERR(ctx)) + return -1; + + if (ctx != NULL) { + /* Calculate record length needed to store the entry */ + res = ext4_fname_crypto_namelen_on_disk(ctx, namelen); + if (res < 0) { + ext4_put_fname_crypto_ctx(&ctx); + return res; + } + reclen = EXT4_DIR_REC_LEN(res); + + /* Allocate buffer to hold maximum name length */ + res = ext4_fname_crypto_alloc_buffer(ctx, EXT4_NAME_LEN, + &fname_crypto_str); + if (res < 0) { + ext4_put_fname_crypto_ctx(&ctx); + return -1; + } + } de = (struct ext4_dir_entry_2 *)buf; top = buf + buf_size - reclen; while ((char *) de <= top) { if (ext4_check_dir_entry(dir, NULL, de, bh, - buf, buf_size, offset)) - return -EIO; - if (ext4_match(namelen, name, de)) - return -EEXIST; + buf, buf_size, offset)) { + res = -EIO; + goto return_result; + } + /* Provide crypto context and crypto buffer to ext4 match */ + res = ext4_match(ctx, &fname_crypto_str, namelen, name, de); + if (res < 0) + goto return_result; + if (res > 0) { + res = -EEXIST; + goto return_result; + } nlen = EXT4_DIR_REC_LEN(de->name_len); rlen = ext4_rec_len_from_disk(de->rec_len, buf_size); if ((de->inode ? rlen - nlen : rlen) >= reclen) @@ -1751,11 +1905,17 @@ int ext4_find_dest_de(struct inode *dir, struct inode *inode, de = (struct ext4_dir_entry_2 *)((char *)de + rlen); offset += rlen; } - if ((char *) de > top) - return -ENOSPC; - *dest_de = de; - return 0; + if ((char *) de > top) + res = -ENOSPC; + else { + *dest_de = de; + res = 0; + } +return_result: + ext4_put_fname_crypto_ctx(&ctx); + ext4_fname_crypto_free_buffer(&fname_crypto_str); + return res; } int ext4_insert_dentry(struct inode *dir, -- cgit v1.2.3 From f348c252320b98e11176074fe04223f22bddaf0d Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Thu, 16 Apr 2015 01:55:00 -0400 Subject: ext4 crypto: add symlink encryption Signed-off-by: Uday Savagaonkar Signed-off-by: Theodore Ts'o --- fs/ext4/ext4.h | 1 + fs/ext4/ext4_crypto.h | 20 +++++++++++ fs/ext4/inode.c | 5 +-- fs/ext4/namei.c | 85 ++++++++++++++++++++++++++++++++++----------- fs/ext4/symlink.c | 96 ++++++++++++++++++++++++++++++++++++++++++++++++++- 5 files changed, 184 insertions(+), 23 deletions(-) (limited to 'fs/ext4/namei.c') diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index 5146e67e8d51..86d15706d27a 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -2233,6 +2233,7 @@ extern int ext4_group_add_blocks(handle_t *handle, struct super_block *sb, extern int ext4_trim_fs(struct super_block *, struct fstrim_range *); /* inode.c */ +int ext4_inode_is_fast_symlink(struct inode *inode); struct buffer_head *ext4_getblk(handle_t *, struct inode *, ext4_lblk_t, int); struct buffer_head *ext4_bread(handle_t *, struct inode *, ext4_lblk_t, int); int ext4_get_block_write(struct inode *inode, sector_t iblock, diff --git a/fs/ext4/ext4_crypto.h b/fs/ext4/ext4_crypto.h index f7d46e8dc9d3..c2ba35a914b6 100644 --- a/fs/ext4/ext4_crypto.h +++ b/fs/ext4/ext4_crypto.h @@ -124,4 +124,24 @@ struct ext4_fname_crypto_ctx { unsigned ctfm_key_is_ready : 1; }; +/** + * For encrypted symlinks, the ciphertext length is stored at the beginning + * of the string in little-endian format. + */ +struct ext4_encrypted_symlink_data { + __le16 len; + char encrypted_path[1]; +} __attribute__((__packed__)); + +/** + * This function is used to calculate the disk space required to + * store a filename of length l in encrypted symlink format. + */ +static inline u32 encrypted_symlink_data_len(u32 l) +{ + if (l < EXT4_CRYPTO_BLOCK_SIZE) + l = EXT4_CRYPTO_BLOCK_SIZE; + return (l + sizeof(struct ext4_encrypted_symlink_data) - 1); +} + #endif /* _EXT4_CRYPTO_H */ diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 8b4fe626919a..f6b35d8a4a5b 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -139,7 +139,7 @@ static int ext4_meta_trans_blocks(struct inode *inode, int lblocks, /* * Test whether an inode is a fast symlink. */ -static int ext4_inode_is_fast_symlink(struct inode *inode) +int ext4_inode_is_fast_symlink(struct inode *inode) { int ea_blocks = EXT4_I(inode)->i_file_acl ? EXT4_CLUSTER_SIZE(inode->i_sb) >> 9 : 0; @@ -4215,7 +4215,8 @@ struct inode *ext4_iget(struct super_block *sb, unsigned long ino) inode->i_op = &ext4_dir_inode_operations; inode->i_fop = &ext4_dir_operations; } else if (S_ISLNK(inode->i_mode)) { - if (ext4_inode_is_fast_symlink(inode)) { + if (ext4_inode_is_fast_symlink(inode) && + !ext4_encrypted_inode(inode)) { inode->i_op = &ext4_fast_symlink_inode_operations; nd_terminate_link(ei->i_data, inode->i_size, sizeof(ei->i_data) - 1); diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c index 4c84db862891..d201426b8d39 100644 --- a/fs/ext4/namei.c +++ b/fs/ext4/namei.c @@ -3193,16 +3193,24 @@ static int ext4_symlink(struct inode *dir, { handle_t *handle; struct inode *inode; - int l, err, retries = 0; + int err, len = strlen(symname); int credits; + bool encryption_required; + struct ext4_str disk_link; + struct ext4_encrypted_symlink_data *sd = NULL; - l = strlen(symname)+1; - if (l > dir->i_sb->s_blocksize) + disk_link.len = len + 1; + disk_link.name = (char *) symname; + + encryption_required = ext4_encrypted_inode(dir); + if (encryption_required) + disk_link.len = encrypted_symlink_data_len(len) + 1; + if (disk_link.len > dir->i_sb->s_blocksize) return -ENAMETOOLONG; dquot_initialize(dir); - if (l > EXT4_N_BLOCKS * 4) { + if ((disk_link.len > EXT4_N_BLOCKS * 4)) { /* * For non-fast symlinks, we just allocate inode and put it on * orphan list in the first transaction => we need bitmap, @@ -3221,16 +3229,49 @@ static int ext4_symlink(struct inode *dir, credits = EXT4_DATA_TRANS_BLOCKS(dir->i_sb) + EXT4_INDEX_EXTRA_TRANS_BLOCKS + 3; } -retry: + inode = ext4_new_inode_start_handle(dir, S_IFLNK|S_IRWXUGO, &dentry->d_name, 0, NULL, EXT4_HT_DIR, credits); handle = ext4_journal_current_handle(); - err = PTR_ERR(inode); - if (IS_ERR(inode)) - goto out_stop; + if (IS_ERR(inode)) { + if (handle) + ext4_journal_stop(handle); + return PTR_ERR(inode); + } + + if (encryption_required) { + struct ext4_fname_crypto_ctx *ctx = NULL; + struct qstr istr; + struct ext4_str ostr; + + sd = kzalloc(disk_link.len, GFP_NOFS); + if (!sd) { + err = -ENOMEM; + goto err_drop_inode; + } + err = ext4_inherit_context(dir, inode); + if (err) + goto err_drop_inode; + ctx = ext4_get_fname_crypto_ctx(inode, + inode->i_sb->s_blocksize); + if (IS_ERR_OR_NULL(ctx)) { + /* We just set the policy, so ctx should not be NULL */ + err = (ctx == NULL) ? -EIO : PTR_ERR(ctx); + goto err_drop_inode; + } + istr.name = (const unsigned char *) symname; + istr.len = len; + ostr.name = sd->encrypted_path; + err = ext4_fname_usr_to_disk(ctx, &istr, &ostr); + ext4_put_fname_crypto_ctx(&ctx); + if (err < 0) + goto err_drop_inode; + sd->len = cpu_to_le16(ostr.len); + disk_link.name = (char *) sd; + } - if (l > EXT4_N_BLOCKS * 4) { + if ((disk_link.len > EXT4_N_BLOCKS * 4)) { inode->i_op = &ext4_symlink_inode_operations; ext4_set_aops(inode); /* @@ -3246,9 +3287,10 @@ retry: drop_nlink(inode); err = ext4_orphan_add(handle, inode); ext4_journal_stop(handle); + handle = NULL; if (err) goto err_drop_inode; - err = __page_symlink(inode, symname, l, 1); + err = __page_symlink(inode, disk_link.name, disk_link.len, 1); if (err) goto err_drop_inode; /* @@ -3260,34 +3302,37 @@ retry: EXT4_INDEX_EXTRA_TRANS_BLOCKS + 1); if (IS_ERR(handle)) { err = PTR_ERR(handle); + handle = NULL; goto err_drop_inode; } set_nlink(inode, 1); err = ext4_orphan_del(handle, inode); - if (err) { - ext4_journal_stop(handle); - clear_nlink(inode); + if (err) goto err_drop_inode; - } } else { /* clear the extent format for fast symlink */ ext4_clear_inode_flag(inode, EXT4_INODE_EXTENTS); - inode->i_op = &ext4_fast_symlink_inode_operations; - memcpy((char *)&EXT4_I(inode)->i_data, symname, l); - inode->i_size = l-1; + inode->i_op = encryption_required ? + &ext4_symlink_inode_operations : + &ext4_fast_symlink_inode_operations; + memcpy((char *)&EXT4_I(inode)->i_data, disk_link.name, + disk_link.len); + inode->i_size = disk_link.len - 1; } EXT4_I(inode)->i_disksize = inode->i_size; err = ext4_add_nondir(handle, dentry, inode); if (!err && IS_DIRSYNC(dir)) ext4_handle_sync(handle); -out_stop: if (handle) ext4_journal_stop(handle); - if (err == -ENOSPC && ext4_should_retry_alloc(dir->i_sb, &retries)) - goto retry; + kfree(sd); return err; err_drop_inode: + if (handle) + ext4_journal_stop(handle); + kfree(sd); + clear_nlink(inode); unlock_new_inode(inode); iput(inode); return err; diff --git a/fs/ext4/symlink.c b/fs/ext4/symlink.c index 489b34333ea4..136ca0e911fd 100644 --- a/fs/ext4/symlink.c +++ b/fs/ext4/symlink.c @@ -22,7 +22,96 @@ #include "ext4.h" #include "xattr.h" +#ifdef CONFIG_EXT4_FS_ENCRYPTION static void *ext4_follow_link(struct dentry *dentry, struct nameidata *nd) +{ + struct page *cpage = NULL; + char *caddr, *paddr = NULL; + struct ext4_str cstr, pstr; + struct inode *inode = dentry->d_inode; + struct ext4_fname_crypto_ctx *ctx = NULL; + struct ext4_encrypted_symlink_data *sd; + loff_t size = min_t(loff_t, i_size_read(inode), PAGE_SIZE - 1); + int res; + u32 plen, max_size = inode->i_sb->s_blocksize; + + if (!ext4_encrypted_inode(inode)) + return page_follow_link_light(dentry, nd); + + ctx = ext4_get_fname_crypto_ctx(inode, inode->i_sb->s_blocksize); + if (IS_ERR(ctx)) + return ctx; + + if (ext4_inode_is_fast_symlink(inode)) { + caddr = (char *) EXT4_I(dentry->d_inode)->i_data; + max_size = sizeof(EXT4_I(dentry->d_inode)->i_data); + } else { + cpage = read_mapping_page(inode->i_mapping, 0, NULL); + if (IS_ERR(cpage)) { + ext4_put_fname_crypto_ctx(&ctx); + return cpage; + } + caddr = kmap(cpage); + caddr[size] = 0; + } + + /* Symlink is encrypted */ + sd = (struct ext4_encrypted_symlink_data *)caddr; + cstr.name = sd->encrypted_path; + cstr.len = le32_to_cpu(sd->len); + if ((cstr.len + + sizeof(struct ext4_encrypted_symlink_data) - 1) > + max_size) { + /* Symlink data on the disk is corrupted */ + res = -EIO; + goto errout; + } + plen = (cstr.len < EXT4_FNAME_CRYPTO_DIGEST_SIZE*2) ? + EXT4_FNAME_CRYPTO_DIGEST_SIZE*2 : cstr.len; + paddr = kmalloc(plen + 1, GFP_NOFS); + if (!paddr) { + res = -ENOMEM; + goto errout; + } + pstr.name = paddr; + res = _ext4_fname_disk_to_usr(ctx, &cstr, &pstr); + if (res < 0) + goto errout; + /* Null-terminate the name */ + if (res <= plen) + paddr[res] = '\0'; + nd_set_link(nd, paddr); + ext4_put_fname_crypto_ctx(&ctx); + if (cpage) { + kunmap(cpage); + page_cache_release(cpage); + } + return NULL; +errout: + ext4_put_fname_crypto_ctx(&ctx); + if (cpage) { + kunmap(cpage); + page_cache_release(cpage); + } + kfree(paddr); + return ERR_PTR(res); +} + +static void ext4_put_link(struct dentry *dentry, struct nameidata *nd, + void *cookie) +{ + struct page *page = cookie; + + if (!page) { + kfree(nd_get_link(nd)); + } else { + kunmap(page); + page_cache_release(page); + } +} +#endif + +static void *ext4_follow_fast_link(struct dentry *dentry, struct nameidata *nd) { struct ext4_inode_info *ei = EXT4_I(dentry->d_inode); nd_set_link(nd, (char *) ei->i_data); @@ -31,8 +120,13 @@ static void *ext4_follow_link(struct dentry *dentry, struct nameidata *nd) const struct inode_operations ext4_symlink_inode_operations = { .readlink = generic_readlink, +#ifdef CONFIG_EXT4_FS_ENCRYPTION + .follow_link = ext4_follow_link, + .put_link = ext4_put_link, +#else .follow_link = page_follow_link_light, .put_link = page_put_link, +#endif .setattr = ext4_setattr, .setxattr = generic_setxattr, .getxattr = generic_getxattr, @@ -42,7 +136,7 @@ const struct inode_operations ext4_symlink_inode_operations = { const struct inode_operations ext4_fast_symlink_inode_operations = { .readlink = generic_readlink, - .follow_link = ext4_follow_link, + .follow_link = ext4_follow_fast_link, .setattr = ext4_setattr, .setxattr = generic_setxattr, .getxattr = generic_getxattr, -- 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/namei.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