ext4 crypto: insert encrypted filenames into a leaf directory block
Michael Halcrow [Wed, 15 Apr 2015 00:52:49 +0000 (20:52 -0400)]
Change-Id: Idc42ab8360930e42d7a6999215f9016412298b66
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/ext4.h
fs/ext4/inline.c
fs/ext4/namei.c

index c38ba4d..c71d081 100644 (file)
@@ -2143,9 +2143,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)
 {
index 956a32b..1805b51 100644 (file)
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
  */
+
+#include <linux/fiemap.h>
+
 #include "ext4_jbd2.h"
 #include "ext4.h"
 #include "xattr.h"
 #include "truncate.h"
-#include <linux/fiemap.h>
 
 #define EXT4_XATTR_SYSTEM_DATA "data"
 #define EXT4_MIN_INLINE_DATA_SIZE      ((sizeof(__le32) * EXT4_N_BLOCKS))
@@ -1010,7 +1012,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);
 
index acf6cc3..f020ebc 100644 (file)
@@ -1666,19 +1666,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;
@@ -1686,9 +1716,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
@@ -1725,8 +1760,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
@@ -1758,8 +1797,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;
@@ -1773,7 +1817,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);
@@ -1840,7 +1890,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;