f2fs crypto: add symlink encryption

This patch implements encryption support for symlink.

Signed-off-by: Uday Savagaonkar <savagaon@google.com>
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
This commit is contained in:
Jaegeuk Kim 2015-04-29 15:10:53 -07:00
parent e7d5545285
commit cbaf042a3c
3 changed files with 142 additions and 6 deletions

View file

@ -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 inode_operations f2fs_dir_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 struct kmem_cache *inode_entry_slab;

View file

@ -198,7 +198,10 @@ struct inode *f2fs_iget(struct super_block *sb, unsigned long ino)
inode->i_mapping->a_ops = &f2fs_dblock_aops;
mapping_set_gfp_mask(inode->i_mapping, GFP_F2FS_HIGH_ZERO);
} else if (S_ISLNK(inode->i_mode)) {
inode->i_op = &f2fs_symlink_inode_operations;
if (f2fs_encrypted_inode(inode))
inode->i_op = &f2fs_encrypted_symlink_inode_operations;
else
inode->i_op = &f2fs_symlink_inode_operations;
inode->i_mapping->a_ops = &f2fs_dblock_aops;
} else if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode) ||
S_ISFIFO(inode->i_mode) || S_ISSOCK(inode->i_mode)) {

View file

@ -338,16 +338,26 @@ static int f2fs_symlink(struct inode *dir, struct dentry *dentry,
{
struct f2fs_sb_info *sbi = F2FS_I_SB(dir);
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;
if (len > dir->i_sb->s_blocksize)
return -ENAMETOOLONG;
f2fs_balance_fs(sbi);
inode = f2fs_new_inode(dir, S_IFLNK | S_IRWXUGO);
if (IS_ERR(inode))
return PTR_ERR(inode);
inode->i_op = &f2fs_symlink_inode_operations;
if (f2fs_encrypted_inode(inode))
inode->i_op = &f2fs_encrypted_symlink_inode_operations;
else
inode->i_op = &f2fs_symlink_inode_operations;
inode->i_mapping->a_ops = &f2fs_dblock_aops;
f2fs_lock_op(sbi);
@ -355,10 +365,50 @@ static int f2fs_symlink(struct inode *dir, struct dentry *dentry,
if (err)
goto out;
f2fs_unlock_op(sbi);
err = page_symlink(inode, symname, symlen);
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);
unlock_new_inode(inode);
@ -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
* 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))
f2fs_sync_fs(sbi->sb, 1);
kfree(sd);
f2fs_fname_crypto_free_buffer(&disk_link);
return err;
out:
handle_failed_inode(inode);
@ -818,6 +872,84 @@ static int f2fs_tmpfile(struct inode *dir, struct dentry *dentry, umode_t mode)
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 = {
.create = f2fs_create,
.lookup = f2fs_lookup,