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:
parent
e7d5545285
commit
cbaf042a3c
|
@ -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,7 +198,10 @@ make_now:
|
||||||
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)) {
|
||||||
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;
|
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) ||
|
||||||
S_ISFIFO(inode->i_mode) || S_ISSOCK(inode->i_mode)) {
|
S_ISFIFO(inode->i_mode) || S_ISSOCK(inode->i_mode)) {
|
||||||
|
|
142
fs/f2fs/namei.c
142
fs/f2fs/namei.c
|
@ -338,16 +338,26 @@ 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);
|
||||||
|
|
||||||
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;
|
inode->i_mapping->a_ops = &f2fs_dblock_aops;
|
||||||
|
|
||||||
f2fs_lock_op(sbi);
|
f2fs_lock_op(sbi);
|
||||||
|
@ -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,
|
||||||
* 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 @@ out:
|
||||||
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,
|
||||||
|
|
Loading…
Reference in New Issue