Commit cbaf042a authored by Jaegeuk Kim's avatar Jaegeuk Kim

f2fs crypto: add symlink encryption

This patch implements encryption support for symlink.
Signed-off-by: default avatarUday Savagaonkar <savagaon@google.com>
Signed-off-by: default avatarTheodore Ts'o <tytso@mit.edu>
Signed-off-by: default avatarJaegeuk Kim <jaegeuk@kernel.org>
parent e7d55452
...@@ -1920,6 +1920,7 @@ extern const struct address_space_operations f2fs_node_aops; ...@@ -1920,6 +1920,7 @@ extern const struct address_space_operations f2fs_node_aops;
extern const struct address_space_operations f2fs_meta_aops; extern const struct address_space_operations f2fs_meta_aops;
extern const struct inode_operations f2fs_dir_inode_operations; extern const struct inode_operations f2fs_dir_inode_operations;
extern const struct inode_operations f2fs_symlink_inode_operations; extern const struct inode_operations f2fs_symlink_inode_operations;
extern const struct inode_operations f2fs_encrypted_symlink_inode_operations;
extern const struct inode_operations f2fs_special_inode_operations; extern const struct inode_operations f2fs_special_inode_operations;
extern struct kmem_cache *inode_entry_slab; extern struct kmem_cache *inode_entry_slab;
......
...@@ -198,6 +198,9 @@ struct inode *f2fs_iget(struct super_block *sb, unsigned long ino) ...@@ -198,6 +198,9 @@ struct inode *f2fs_iget(struct super_block *sb, unsigned long ino)
inode->i_mapping->a_ops = &f2fs_dblock_aops; inode->i_mapping->a_ops = &f2fs_dblock_aops;
mapping_set_gfp_mask(inode->i_mapping, GFP_F2FS_HIGH_ZERO); mapping_set_gfp_mask(inode->i_mapping, GFP_F2FS_HIGH_ZERO);
} else if (S_ISLNK(inode->i_mode)) { } else if (S_ISLNK(inode->i_mode)) {
if (f2fs_encrypted_inode(inode))
inode->i_op = &f2fs_encrypted_symlink_inode_operations;
else
inode->i_op = &f2fs_symlink_inode_operations; inode->i_op = &f2fs_symlink_inode_operations;
inode->i_mapping->a_ops = &f2fs_dblock_aops; inode->i_mapping->a_ops = &f2fs_dblock_aops;
} else if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode) || } else if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode) ||
......
...@@ -338,15 +338,25 @@ static int f2fs_symlink(struct inode *dir, struct dentry *dentry, ...@@ -338,15 +338,25 @@ static int f2fs_symlink(struct inode *dir, struct dentry *dentry,
{ {
struct f2fs_sb_info *sbi = F2FS_I_SB(dir); struct f2fs_sb_info *sbi = F2FS_I_SB(dir);
struct inode *inode; struct inode *inode;
size_t symlen = strlen(symname) + 1; size_t len = strlen(symname);
size_t p_len;
char *p_str;
struct f2fs_str disk_link = FSTR_INIT(NULL, 0);
struct f2fs_encrypted_symlink_data *sd = NULL;
int err; int err;
if (len > dir->i_sb->s_blocksize)
return -ENAMETOOLONG;
f2fs_balance_fs(sbi); f2fs_balance_fs(sbi);
inode = f2fs_new_inode(dir, S_IFLNK | S_IRWXUGO); inode = f2fs_new_inode(dir, S_IFLNK | S_IRWXUGO);
if (IS_ERR(inode)) if (IS_ERR(inode))
return PTR_ERR(inode); return PTR_ERR(inode);
if (f2fs_encrypted_inode(inode))
inode->i_op = &f2fs_encrypted_symlink_inode_operations;
else
inode->i_op = &f2fs_symlink_inode_operations; inode->i_op = &f2fs_symlink_inode_operations;
inode->i_mapping->a_ops = &f2fs_dblock_aops; inode->i_mapping->a_ops = &f2fs_dblock_aops;
...@@ -355,10 +365,50 @@ static int f2fs_symlink(struct inode *dir, struct dentry *dentry, ...@@ -355,10 +365,50 @@ static int f2fs_symlink(struct inode *dir, struct dentry *dentry,
if (err) if (err)
goto out; goto out;
f2fs_unlock_op(sbi); f2fs_unlock_op(sbi);
err = page_symlink(inode, symname, symlen);
alloc_nid_done(sbi, inode->i_ino); alloc_nid_done(sbi, inode->i_ino);
if (f2fs_encrypted_inode(dir)) {
struct qstr istr = QSTR_INIT(symname, len);
err = f2fs_inherit_context(dir, inode, NULL);
if (err)
goto err_out;
err = f2fs_setup_fname_crypto(inode);
if (err)
goto err_out;
err = f2fs_fname_crypto_alloc_buffer(inode, len, &disk_link);
if (err)
goto err_out;
err = f2fs_fname_usr_to_disk(inode, &istr, &disk_link);
if (err < 0)
goto err_out;
p_len = encrypted_symlink_data_len(disk_link.len) + 1;
if (p_len > dir->i_sb->s_blocksize) {
err = -ENAMETOOLONG;
goto err_out;
}
sd = kzalloc(p_len, GFP_NOFS);
if (!sd) {
err = -ENOMEM;
goto err_out;
}
memcpy(sd->encrypted_path, disk_link.name, disk_link.len);
sd->len = cpu_to_le16(disk_link.len);
p_str = (char *)sd;
} else {
p_len = len + 1;
p_str = (char *)symname;
}
err = page_symlink(inode, p_str, p_len);
err_out:
d_instantiate(dentry, inode); d_instantiate(dentry, inode);
unlock_new_inode(inode); unlock_new_inode(inode);
...@@ -371,10 +421,14 @@ static int f2fs_symlink(struct inode *dir, struct dentry *dentry, ...@@ -371,10 +421,14 @@ static int f2fs_symlink(struct inode *dir, struct dentry *dentry,
* If the symlink path is stored into inline_data, there is no * If the symlink path is stored into inline_data, there is no
* performance regression. * performance regression.
*/ */
filemap_write_and_wait_range(inode->i_mapping, 0, symlen - 1); if (!err)
filemap_write_and_wait_range(inode->i_mapping, 0, p_len - 1);
if (IS_DIRSYNC(dir)) if (IS_DIRSYNC(dir))
f2fs_sync_fs(sbi->sb, 1); f2fs_sync_fs(sbi->sb, 1);
kfree(sd);
f2fs_fname_crypto_free_buffer(&disk_link);
return err; return err;
out: out:
handle_failed_inode(inode); handle_failed_inode(inode);
...@@ -818,6 +872,84 @@ static int f2fs_tmpfile(struct inode *dir, struct dentry *dentry, umode_t mode) ...@@ -818,6 +872,84 @@ static int f2fs_tmpfile(struct inode *dir, struct dentry *dentry, umode_t mode)
return err; return err;
} }
#ifdef CONFIG_F2FS_FS_ENCRYPTION
static void *f2fs_encrypted_follow_link(struct dentry *dentry,
struct nameidata *nd)
{
struct page *cpage = NULL;
char *caddr, *paddr = NULL;
struct f2fs_str cstr;
struct f2fs_str pstr = FSTR_INIT(NULL, 0);
struct inode *inode = d_inode(dentry);
struct f2fs_encrypted_symlink_data *sd;
loff_t size = min_t(loff_t, i_size_read(inode), PAGE_SIZE - 1);
u32 max_size = inode->i_sb->s_blocksize;
int res;
res = f2fs_setup_fname_crypto(inode);
if (res)
return ERR_PTR(res);
cpage = read_mapping_page(inode->i_mapping, 0, NULL);
if (IS_ERR(cpage))
return cpage;
caddr = kmap(cpage);
caddr[size] = 0;
/* Symlink is encrypted */
sd = (struct f2fs_encrypted_symlink_data *)caddr;
cstr.name = sd->encrypted_path;
cstr.len = le16_to_cpu(sd->len);
/* this is broken symlink case */
if (cstr.name[0] == 0 && cstr.len == 0) {
res = -ENOENT;
goto errout;
}
if ((cstr.len + sizeof(struct f2fs_encrypted_symlink_data) - 1) >
max_size) {
/* Symlink data on the disk is corrupted */
res = -EIO;
goto errout;
}
res = f2fs_fname_crypto_alloc_buffer(inode, cstr.len, &pstr);
if (res)
goto errout;
res = f2fs_fname_disk_to_usr(inode, NULL, &cstr, &pstr);
if (res < 0)
goto errout;
paddr = pstr.name;
/* Null-terminate the name */
paddr[res] = '\0';
nd_set_link(nd, paddr);
kunmap(cpage);
page_cache_release(cpage);
return NULL;
errout:
f2fs_fname_crypto_free_buffer(&pstr);
kunmap(cpage);
page_cache_release(cpage);
return ERR_PTR(res);
}
const struct inode_operations f2fs_encrypted_symlink_inode_operations = {
.readlink = generic_readlink,
.follow_link = f2fs_encrypted_follow_link,
.put_link = kfree_put_link,
.getattr = f2fs_getattr,
.setattr = f2fs_setattr,
.setxattr = generic_setxattr,
.getxattr = generic_getxattr,
.listxattr = f2fs_listxattr,
.removexattr = generic_removexattr,
};
#endif
const struct inode_operations f2fs_dir_inode_operations = { const struct inode_operations f2fs_dir_inode_operations = {
.create = f2fs_create, .create = f2fs_create,
.lookup = f2fs_lookup, .lookup = f2fs_lookup,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment