Loading fs/ext4/ext4.h +1 −0 Original line number Diff line number Diff line Loading @@ -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, Loading fs/ext4/ext4_crypto.h +20 −0 Original line number Diff line number Diff line Loading @@ -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 */ fs/ext4/inode.c +3 −2 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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); Loading fs/ext4/namei.c +65 −20 Original line number Diff line number Diff line Loading @@ -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, Loading @@ -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 (l > EXT4_N_BLOCKS * 4) { 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 ((disk_link.len > EXT4_N_BLOCKS * 4)) { inode->i_op = &ext4_symlink_inode_operations; ext4_set_aops(inode); /* Loading @@ -3246,9 +3287,10 @@ static int ext4_symlink(struct inode *dir, 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; /* Loading @@ -3260,34 +3302,37 @@ static int ext4_symlink(struct inode *dir, 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; Loading fs/ext4/symlink.c +95 −1 Original line number Diff line number Diff line Loading @@ -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); Loading @@ -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, Loading @@ -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, Loading Loading
fs/ext4/ext4.h +1 −0 Original line number Diff line number Diff line Loading @@ -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, Loading
fs/ext4/ext4_crypto.h +20 −0 Original line number Diff line number Diff line Loading @@ -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 */
fs/ext4/inode.c +3 −2 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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); Loading
fs/ext4/namei.c +65 −20 Original line number Diff line number Diff line Loading @@ -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, Loading @@ -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 (l > EXT4_N_BLOCKS * 4) { 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 ((disk_link.len > EXT4_N_BLOCKS * 4)) { inode->i_op = &ext4_symlink_inode_operations; ext4_set_aops(inode); /* Loading @@ -3246,9 +3287,10 @@ static int ext4_symlink(struct inode *dir, 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; /* Loading @@ -3260,34 +3302,37 @@ static int ext4_symlink(struct inode *dir, 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; Loading
fs/ext4/symlink.c +95 −1 Original line number Diff line number Diff line Loading @@ -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); Loading @@ -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, Loading @@ -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, Loading