mirror of https://github.com/OpenIPC/firmware.git
1027 lines
26 KiB
Diff
1027 lines
26 KiB
Diff
diff -drupN a/fs/f2fs/namei.c b/fs/f2fs/namei.c
|
|
--- a/fs/f2fs/namei.c 2018-08-06 17:23:04.000000000 +0300
|
|
+++ b/fs/f2fs/namei.c 2022-06-12 05:28:14.000000000 +0300
|
|
@@ -15,6 +15,7 @@
|
|
#include <linux/ctype.h>
|
|
#include <linux/dcache.h>
|
|
#include <linux/namei.h>
|
|
+#include <linux/quotaops.h>
|
|
|
|
#include "f2fs.h"
|
|
#include "node.h"
|
|
@@ -28,6 +29,7 @@ static struct inode *f2fs_new_inode(stru
|
|
nid_t ino;
|
|
struct inode *inode;
|
|
bool nid_free = false;
|
|
+ int xattr_size = 0;
|
|
int err;
|
|
|
|
inode = new_inode(dir->i_sb);
|
|
@@ -42,39 +44,83 @@ static struct inode *f2fs_new_inode(stru
|
|
}
|
|
f2fs_unlock_op(sbi);
|
|
|
|
+ nid_free = true;
|
|
+
|
|
inode_init_owner(inode, dir, mode);
|
|
|
|
inode->i_ino = ino;
|
|
inode->i_blocks = 0;
|
|
- inode->i_mtime = inode->i_atime = inode->i_ctime = current_time(inode);
|
|
+ inode->i_mtime = inode->i_atime = inode->i_ctime =
|
|
+ F2FS_I(inode)->i_crtime = current_time(inode);
|
|
inode->i_generation = sbi->s_next_generation++;
|
|
|
|
err = insert_inode_locked(inode);
|
|
if (err) {
|
|
err = -EINVAL;
|
|
- nid_free = true;
|
|
goto fail;
|
|
}
|
|
|
|
+ if (f2fs_sb_has_project_quota(sbi->sb) &&
|
|
+ (F2FS_I(dir)->i_flags & FS_PROJINHERIT_FL))
|
|
+ F2FS_I(inode)->i_projid = F2FS_I(dir)->i_projid;
|
|
+ else
|
|
+ F2FS_I(inode)->i_projid = make_kprojid(&init_user_ns,
|
|
+ F2FS_DEF_PROJID);
|
|
+
|
|
+ err = dquot_initialize(inode);
|
|
+ if (err)
|
|
+ goto fail_drop;
|
|
+
|
|
+ err = dquot_alloc_inode(inode);
|
|
+ if (err)
|
|
+ goto fail_drop;
|
|
+
|
|
+ set_inode_flag(inode, FI_NEW_INODE);
|
|
+
|
|
/* If the directory encrypted, then we should encrypt the inode. */
|
|
- if (f2fs_encrypted_inode(dir) && f2fs_may_encrypt(inode))
|
|
+ if ((f2fs_encrypted_inode(dir) || DUMMY_ENCRYPTION_ENABLED(sbi)) &&
|
|
+ f2fs_may_encrypt(inode))
|
|
f2fs_set_encrypted_inode(inode);
|
|
|
|
- set_inode_flag(inode, FI_NEW_INODE);
|
|
+ if (f2fs_sb_has_extra_attr(sbi->sb)) {
|
|
+ set_inode_flag(inode, FI_EXTRA_ATTR);
|
|
+ F2FS_I(inode)->i_extra_isize = F2FS_TOTAL_EXTRA_ATTR_SIZE;
|
|
+ }
|
|
|
|
if (test_opt(sbi, INLINE_XATTR))
|
|
set_inode_flag(inode, FI_INLINE_XATTR);
|
|
+
|
|
if (test_opt(sbi, INLINE_DATA) && f2fs_may_inline_data(inode))
|
|
set_inode_flag(inode, FI_INLINE_DATA);
|
|
if (f2fs_may_inline_dentry(inode))
|
|
set_inode_flag(inode, FI_INLINE_DENTRY);
|
|
|
|
+ if (f2fs_sb_has_flexible_inline_xattr(sbi->sb)) {
|
|
+ f2fs_bug_on(sbi, !f2fs_has_extra_attr(inode));
|
|
+ if (f2fs_has_inline_xattr(inode))
|
|
+ xattr_size = F2FS_OPTION(sbi).inline_xattr_size;
|
|
+ /* Otherwise, will be 0 */
|
|
+ } else if (f2fs_has_inline_xattr(inode) ||
|
|
+ f2fs_has_inline_dentry(inode)) {
|
|
+ xattr_size = DEFAULT_INLINE_XATTR_ADDRS;
|
|
+ }
|
|
+ F2FS_I(inode)->i_inline_xattr_size = xattr_size;
|
|
+
|
|
f2fs_init_extent_tree(inode, NULL);
|
|
|
|
stat_inc_inline_xattr(inode);
|
|
stat_inc_inline_inode(inode);
|
|
stat_inc_inline_dir(inode);
|
|
|
|
+ F2FS_I(inode)->i_flags =
|
|
+ f2fs_mask_flags(mode, F2FS_I(dir)->i_flags & F2FS_FL_INHERITED);
|
|
+
|
|
+ if (S_ISDIR(inode->i_mode))
|
|
+ F2FS_I(inode)->i_flags |= FS_INDEX_FL;
|
|
+
|
|
+ if (F2FS_I(inode)->i_flags & FS_PROJINHERIT_FL)
|
|
+ set_inode_flag(inode, FI_PROJ_INHERIT);
|
|
+
|
|
trace_f2fs_new_inode(inode, 0);
|
|
return inode;
|
|
|
|
@@ -85,9 +131,19 @@ fail:
|
|
set_inode_flag(inode, FI_FREE_NID);
|
|
iput(inode);
|
|
return ERR_PTR(err);
|
|
+fail_drop:
|
|
+ trace_f2fs_new_inode(inode, err);
|
|
+ dquot_drop(inode);
|
|
+ inode->i_flags |= S_NOQUOTA;
|
|
+ if (nid_free)
|
|
+ set_inode_flag(inode, FI_FREE_NID);
|
|
+ clear_nlink(inode);
|
|
+ unlock_new_inode(inode);
|
|
+ iput(inode);
|
|
+ return ERR_PTR(err);
|
|
}
|
|
|
|
-static int is_multimedia_file(const unsigned char *s, const char *sub)
|
|
+static int is_extension_exist(const unsigned char *s, const char *sub)
|
|
{
|
|
size_t slen = strlen(s);
|
|
size_t sublen = strlen(sub);
|
|
@@ -113,19 +169,94 @@ static int is_multimedia_file(const unsi
|
|
/*
|
|
* Set multimedia files as cold files for hot/cold data separation
|
|
*/
|
|
-static inline void set_cold_files(struct f2fs_sb_info *sbi, struct inode *inode,
|
|
+static inline void set_file_temperature(struct f2fs_sb_info *sbi, struct inode *inode,
|
|
const unsigned char *name)
|
|
{
|
|
- int i;
|
|
- __u8 (*extlist)[8] = sbi->raw_super->extension_list;
|
|
+ __u8 (*extlist)[F2FS_EXTENSION_LEN] = sbi->raw_super->extension_list;
|
|
+ int i, cold_count, hot_count;
|
|
|
|
- int count = le32_to_cpu(sbi->raw_super->extension_count);
|
|
- for (i = 0; i < count; i++) {
|
|
- if (is_multimedia_file(name, extlist[i])) {
|
|
+ down_read(&sbi->sb_lock);
|
|
+
|
|
+ cold_count = le32_to_cpu(sbi->raw_super->extension_count);
|
|
+ hot_count = sbi->raw_super->hot_ext_count;
|
|
+
|
|
+ for (i = 0; i < cold_count + hot_count; i++) {
|
|
+ if (!is_extension_exist(name, extlist[i]))
|
|
+ continue;
|
|
+ if (i < cold_count)
|
|
file_set_cold(inode);
|
|
- break;
|
|
- }
|
|
+ else
|
|
+ file_set_hot(inode);
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ up_read(&sbi->sb_lock);
|
|
+}
|
|
+
|
|
+int update_extension_list(struct f2fs_sb_info *sbi, const char *name,
|
|
+ bool hot, bool set)
|
|
+{
|
|
+ __u8 (*extlist)[F2FS_EXTENSION_LEN] = sbi->raw_super->extension_list;
|
|
+ int cold_count = le32_to_cpu(sbi->raw_super->extension_count);
|
|
+ int hot_count = sbi->raw_super->hot_ext_count;
|
|
+ int total_count = cold_count + hot_count;
|
|
+ int start, count;
|
|
+ int i;
|
|
+
|
|
+ if (set) {
|
|
+ if (total_count == F2FS_MAX_EXTENSION)
|
|
+ return -EINVAL;
|
|
+ } else {
|
|
+ if (!hot && !cold_count)
|
|
+ return -EINVAL;
|
|
+ if (hot && !hot_count)
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ if (hot) {
|
|
+ start = cold_count;
|
|
+ count = total_count;
|
|
+ } else {
|
|
+ start = 0;
|
|
+ count = cold_count;
|
|
+ }
|
|
+
|
|
+ for (i = start; i < count; i++) {
|
|
+ if (strcmp(name, extlist[i]))
|
|
+ continue;
|
|
+
|
|
+ if (set)
|
|
+ return -EINVAL;
|
|
+
|
|
+ memcpy(extlist[i], extlist[i + 1],
|
|
+ F2FS_EXTENSION_LEN * (total_count - i - 1));
|
|
+ memset(extlist[total_count - 1], 0, F2FS_EXTENSION_LEN);
|
|
+ if (hot)
|
|
+ sbi->raw_super->hot_ext_count = hot_count - 1;
|
|
+ else
|
|
+ sbi->raw_super->extension_count =
|
|
+ cpu_to_le32(cold_count - 1);
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ if (!set)
|
|
+ return -EINVAL;
|
|
+
|
|
+ if (hot) {
|
|
+ strncpy(extlist[count], name, strlen(name));
|
|
+ sbi->raw_super->hot_ext_count = hot_count + 1;
|
|
+ } else {
|
|
+ char buf[F2FS_MAX_EXTENSION][F2FS_EXTENSION_LEN];
|
|
+
|
|
+ memcpy(buf, &extlist[cold_count],
|
|
+ F2FS_EXTENSION_LEN * hot_count);
|
|
+ memset(extlist[cold_count], 0, F2FS_EXTENSION_LEN);
|
|
+ strncpy(extlist[cold_count], name, strlen(name));
|
|
+ memcpy(&extlist[cold_count + 1], buf,
|
|
+ F2FS_EXTENSION_LEN * hot_count);
|
|
+ sbi->raw_super->extension_count = cpu_to_le32(cold_count + 1);
|
|
}
|
|
+ return 0;
|
|
}
|
|
|
|
static int f2fs_create(struct inode *dir, struct dentry *dentry, umode_t mode,
|
|
@@ -136,20 +267,25 @@ static int f2fs_create(struct inode *dir
|
|
nid_t ino = 0;
|
|
int err;
|
|
|
|
+ if (unlikely(f2fs_cp_error(sbi)))
|
|
+ return -EIO;
|
|
+
|
|
+ err = dquot_initialize(dir);
|
|
+ if (err)
|
|
+ return err;
|
|
+
|
|
inode = f2fs_new_inode(dir, mode);
|
|
if (IS_ERR(inode))
|
|
return PTR_ERR(inode);
|
|
|
|
if (!test_opt(sbi, DISABLE_EXT_IDENTIFY))
|
|
- set_cold_files(sbi, inode, dentry->d_name.name);
|
|
+ set_file_temperature(sbi, inode, dentry->d_name.name);
|
|
|
|
inode->i_op = &f2fs_file_inode_operations;
|
|
inode->i_fop = &f2fs_file_operations;
|
|
inode->i_mapping->a_ops = &f2fs_dblock_aops;
|
|
ino = inode->i_ino;
|
|
|
|
- f2fs_balance_fs(sbi, true);
|
|
-
|
|
f2fs_lock_op(sbi);
|
|
err = f2fs_add_link(dentry, inode);
|
|
if (err)
|
|
@@ -162,6 +298,8 @@ static int f2fs_create(struct inode *dir
|
|
|
|
if (IS_DIRSYNC(dir))
|
|
f2fs_sync_fs(sbi->sb, 1);
|
|
+
|
|
+ f2fs_balance_fs(sbi, true);
|
|
return 0;
|
|
out:
|
|
handle_failed_inode(inode);
|
|
@@ -175,9 +313,21 @@ static int f2fs_link(struct dentry *old_
|
|
struct f2fs_sb_info *sbi = F2FS_I_SB(dir);
|
|
int err;
|
|
|
|
- if (f2fs_encrypted_inode(dir) &&
|
|
- !fscrypt_has_permitted_context(dir, inode))
|
|
- return -EPERM;
|
|
+ if (unlikely(f2fs_cp_error(sbi)))
|
|
+ return -EIO;
|
|
+
|
|
+ err = fscrypt_prepare_link(old_dentry, dir, dentry);
|
|
+ if (err)
|
|
+ return err;
|
|
+
|
|
+ if (is_inode_flag_set(dir, FI_PROJ_INHERIT) &&
|
|
+ (!projid_eq(F2FS_I(dir)->i_projid,
|
|
+ F2FS_I(old_dentry->d_inode)->i_projid)))
|
|
+ return -EXDEV;
|
|
+
|
|
+ err = dquot_initialize(dir);
|
|
+ if (err)
|
|
+ return err;
|
|
|
|
f2fs_balance_fs(sbi, true);
|
|
|
|
@@ -232,13 +382,16 @@ static int __recover_dot_dentries(struct
|
|
return 0;
|
|
}
|
|
|
|
+ err = dquot_initialize(dir);
|
|
+ if (err)
|
|
+ return err;
|
|
+
|
|
f2fs_balance_fs(sbi, true);
|
|
|
|
f2fs_lock_op(sbi);
|
|
|
|
de = f2fs_find_entry(dir, &dot, &page);
|
|
if (de) {
|
|
- f2fs_dentry_kunmap(dir, page);
|
|
f2fs_put_page(page, 0);
|
|
} else if (IS_ERR(page)) {
|
|
err = PTR_ERR(page);
|
|
@@ -250,14 +403,12 @@ static int __recover_dot_dentries(struct
|
|
}
|
|
|
|
de = f2fs_find_entry(dir, &dotdot, &page);
|
|
- if (de) {
|
|
- f2fs_dentry_kunmap(dir, page);
|
|
+ if (de)
|
|
f2fs_put_page(page, 0);
|
|
- } else if (IS_ERR(page)) {
|
|
+ else if (IS_ERR(page))
|
|
err = PTR_ERR(page);
|
|
- } else {
|
|
+ else
|
|
err = __f2fs_add_link(dir, &dotdot, NULL, pino, S_IFDIR);
|
|
- }
|
|
out:
|
|
if (!err)
|
|
clear_inode_flag(dir, FI_INLINE_DOTS);
|
|
@@ -272,66 +423,70 @@ static struct dentry *f2fs_lookup(struct
|
|
struct inode *inode = NULL;
|
|
struct f2fs_dir_entry *de;
|
|
struct page *page;
|
|
- nid_t ino;
|
|
+ struct dentry *new;
|
|
+ nid_t ino = -1;
|
|
int err = 0;
|
|
unsigned int root_ino = F2FS_ROOT_INO(F2FS_I_SB(dir));
|
|
|
|
- if (f2fs_encrypted_inode(dir)) {
|
|
- int res = fscrypt_get_encryption_info(dir);
|
|
+ trace_f2fs_lookup_start(dir, dentry, flags);
|
|
|
|
- /*
|
|
- * DCACHE_ENCRYPTED_WITH_KEY is set if the dentry is
|
|
- * created while the directory was encrypted and we
|
|
- * don't have access to the key.
|
|
- */
|
|
- if (fscrypt_has_encryption_key(dir))
|
|
- fscrypt_set_encrypted_dentry(dentry);
|
|
- fscrypt_set_d_op(dentry);
|
|
- if (res && res != -ENOKEY)
|
|
- return ERR_PTR(res);
|
|
- }
|
|
+ err = fscrypt_prepare_lookup(dir, dentry, flags);
|
|
+ if (err)
|
|
+ goto out;
|
|
|
|
- if (dentry->d_name.len > F2FS_NAME_LEN)
|
|
- return ERR_PTR(-ENAMETOOLONG);
|
|
+ if (dentry->d_name.len > F2FS_NAME_LEN) {
|
|
+ err = -ENAMETOOLONG;
|
|
+ goto out;
|
|
+ }
|
|
|
|
de = f2fs_find_entry(dir, &dentry->d_name, &page);
|
|
if (!de) {
|
|
- if (IS_ERR(page))
|
|
- return (struct dentry *)page;
|
|
- return d_splice_alias(inode, dentry);
|
|
+ if (IS_ERR(page)) {
|
|
+ err = PTR_ERR(page);
|
|
+ goto out;
|
|
+ }
|
|
+ goto out_splice;
|
|
}
|
|
|
|
ino = le32_to_cpu(de->ino);
|
|
- f2fs_dentry_kunmap(dir, page);
|
|
f2fs_put_page(page, 0);
|
|
|
|
inode = f2fs_iget(dir->i_sb, ino);
|
|
- if (IS_ERR(inode))
|
|
- return ERR_CAST(inode);
|
|
+ if (IS_ERR(inode)) {
|
|
+ err = PTR_ERR(inode);
|
|
+ goto out;
|
|
+ }
|
|
|
|
if ((dir->i_ino == root_ino) && f2fs_has_inline_dots(dir)) {
|
|
err = __recover_dot_dentries(dir, root_ino);
|
|
if (err)
|
|
- goto err_out;
|
|
+ goto out_iput;
|
|
}
|
|
|
|
if (f2fs_has_inline_dots(inode)) {
|
|
err = __recover_dot_dentries(inode, dir->i_ino);
|
|
if (err)
|
|
- goto err_out;
|
|
+ goto out_iput;
|
|
}
|
|
- if (!IS_ERR(inode) && f2fs_encrypted_inode(dir) &&
|
|
- (S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode)) &&
|
|
- !fscrypt_has_permitted_context(dir, inode)) {
|
|
- bool nokey = f2fs_encrypted_inode(inode) &&
|
|
- !fscrypt_has_encryption_key(inode);
|
|
- err = nokey ? -ENOKEY : -EPERM;
|
|
- goto err_out;
|
|
+ if (f2fs_encrypted_inode(dir) &&
|
|
+ (S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode)) &&
|
|
+ !fscrypt_has_permitted_context(dir, inode)) {
|
|
+ f2fs_msg(inode->i_sb, KERN_WARNING,
|
|
+ "Inconsistent encryption contexts: %lu/%lu",
|
|
+ dir->i_ino, inode->i_ino);
|
|
+ err = -EPERM;
|
|
+ goto out_iput;
|
|
}
|
|
- return d_splice_alias(inode, dentry);
|
|
-
|
|
-err_out:
|
|
+out_splice:
|
|
+ new = d_splice_alias(inode, dentry);
|
|
+ if (IS_ERR(new))
|
|
+ err = PTR_ERR(new);
|
|
+ trace_f2fs_lookup_end(dir, dentry, ino, err);
|
|
+ return new;
|
|
+out_iput:
|
|
iput(inode);
|
|
+out:
|
|
+ trace_f2fs_lookup_end(dir, dentry, ino, err);
|
|
return ERR_PTR(err);
|
|
}
|
|
|
|
@@ -345,6 +500,16 @@ static int f2fs_unlink(struct inode *dir
|
|
|
|
trace_f2fs_unlink_enter(dir, dentry);
|
|
|
|
+ if (unlikely(f2fs_cp_error(sbi)))
|
|
+ return -EIO;
|
|
+
|
|
+ err = dquot_initialize(dir);
|
|
+ if (err)
|
|
+ return err;
|
|
+ err = dquot_initialize(inode);
|
|
+ if (err)
|
|
+ return err;
|
|
+
|
|
de = f2fs_find_entry(dir, &dentry->d_name, &page);
|
|
if (!de) {
|
|
if (IS_ERR(page))
|
|
@@ -358,7 +523,6 @@ static int f2fs_unlink(struct inode *dir
|
|
err = acquire_orphan_inode(sbi);
|
|
if (err) {
|
|
f2fs_unlock_op(sbi);
|
|
- f2fs_dentry_kunmap(dir, page);
|
|
f2fs_put_page(page, 0);
|
|
goto fail;
|
|
}
|
|
@@ -367,6 +531,7 @@ static int f2fs_unlink(struct inode *dir
|
|
|
|
if (IS_DIRSYNC(dir))
|
|
f2fs_sync_fs(sbi->sb, 1);
|
|
+
|
|
fail:
|
|
trace_f2fs_unlink_exit(inode, err);
|
|
return err;
|
|
@@ -392,73 +557,42 @@ static int f2fs_symlink(struct inode *di
|
|
struct f2fs_sb_info *sbi = F2FS_I_SB(dir);
|
|
struct inode *inode;
|
|
size_t len = strlen(symname);
|
|
- struct fscrypt_str disk_link = FSTR_INIT((char *)symname, len + 1);
|
|
- struct fscrypt_symlink_data *sd = NULL;
|
|
+ struct fscrypt_str disk_link;
|
|
int err;
|
|
|
|
- if (f2fs_encrypted_inode(dir)) {
|
|
- err = fscrypt_get_encryption_info(dir);
|
|
- if (err)
|
|
- return err;
|
|
-
|
|
- if (!fscrypt_has_encryption_key(dir))
|
|
- return -ENOKEY;
|
|
+ if (unlikely(f2fs_cp_error(sbi)))
|
|
+ return -EIO;
|
|
|
|
- disk_link.len = (fscrypt_fname_encrypted_size(dir, len) +
|
|
- sizeof(struct fscrypt_symlink_data));
|
|
- }
|
|
+ err = fscrypt_prepare_symlink(dir, symname, len, dir->i_sb->s_blocksize,
|
|
+ &disk_link);
|
|
+ if (err)
|
|
+ return err;
|
|
|
|
- if (disk_link.len > dir->i_sb->s_blocksize)
|
|
- return -ENAMETOOLONG;
|
|
+ err = dquot_initialize(dir);
|
|
+ if (err)
|
|
+ return err;
|
|
|
|
inode = f2fs_new_inode(dir, S_IFLNK | S_IRWXUGO);
|
|
if (IS_ERR(inode))
|
|
return PTR_ERR(inode);
|
|
|
|
- if (f2fs_encrypted_inode(inode))
|
|
+ if (IS_ENCRYPTED(inode))
|
|
inode->i_op = &f2fs_encrypted_symlink_inode_operations;
|
|
else
|
|
inode->i_op = &f2fs_symlink_inode_operations;
|
|
inode_nohighmem(inode);
|
|
inode->i_mapping->a_ops = &f2fs_dblock_aops;
|
|
|
|
- f2fs_balance_fs(sbi, true);
|
|
-
|
|
f2fs_lock_op(sbi);
|
|
err = f2fs_add_link(dentry, inode);
|
|
if (err)
|
|
- goto out;
|
|
+ goto out_handle_failed_inode;
|
|
f2fs_unlock_op(sbi);
|
|
alloc_nid_done(sbi, inode->i_ino);
|
|
|
|
- if (f2fs_encrypted_inode(inode)) {
|
|
- struct qstr istr = QSTR_INIT(symname, len);
|
|
- struct fscrypt_str ostr;
|
|
-
|
|
- sd = kzalloc(disk_link.len, GFP_NOFS);
|
|
- if (!sd) {
|
|
- err = -ENOMEM;
|
|
- goto err_out;
|
|
- }
|
|
-
|
|
- err = fscrypt_get_encryption_info(inode);
|
|
- if (err)
|
|
- goto err_out;
|
|
-
|
|
- if (!fscrypt_has_encryption_key(inode)) {
|
|
- err = -ENOKEY;
|
|
- goto err_out;
|
|
- }
|
|
-
|
|
- ostr.name = sd->encrypted_path;
|
|
- ostr.len = disk_link.len;
|
|
- err = fscrypt_fname_usr_to_disk(inode, &istr, &ostr);
|
|
- if (err)
|
|
- goto err_out;
|
|
-
|
|
- sd->len = cpu_to_le16(ostr.len);
|
|
- disk_link.name = (char *)sd;
|
|
- }
|
|
+ err = fscrypt_encrypt_symlink(inode, symname, len, &disk_link);
|
|
+ if (err)
|
|
+ goto err_out;
|
|
|
|
err = page_symlink(inode, disk_link.name, disk_link.len);
|
|
|
|
@@ -484,10 +618,14 @@ err_out:
|
|
f2fs_unlink(dir, dentry);
|
|
}
|
|
|
|
- kfree(sd);
|
|
- return err;
|
|
-out:
|
|
+ f2fs_balance_fs(sbi, true);
|
|
+ goto out_free_encrypted_link;
|
|
+
|
|
+out_handle_failed_inode:
|
|
handle_failed_inode(inode);
|
|
+out_free_encrypted_link:
|
|
+ if (disk_link.name != (unsigned char *)symname)
|
|
+ kfree(disk_link.name);
|
|
return err;
|
|
}
|
|
|
|
@@ -497,6 +635,13 @@ static int f2fs_mkdir(struct inode *dir,
|
|
struct inode *inode;
|
|
int err;
|
|
|
|
+ if (unlikely(f2fs_cp_error(sbi)))
|
|
+ return -EIO;
|
|
+
|
|
+ err = dquot_initialize(dir);
|
|
+ if (err)
|
|
+ return err;
|
|
+
|
|
inode = f2fs_new_inode(dir, S_IFDIR | mode);
|
|
if (IS_ERR(inode))
|
|
return PTR_ERR(inode);
|
|
@@ -504,9 +649,7 @@ static int f2fs_mkdir(struct inode *dir,
|
|
inode->i_op = &f2fs_dir_inode_operations;
|
|
inode->i_fop = &f2fs_dir_operations;
|
|
inode->i_mapping->a_ops = &f2fs_dblock_aops;
|
|
- mapping_set_gfp_mask(inode->i_mapping, GFP_F2FS_HIGH_ZERO);
|
|
-
|
|
- f2fs_balance_fs(sbi, true);
|
|
+ inode_nohighmem(inode);
|
|
|
|
set_inode_flag(inode, FI_INC_LINK);
|
|
f2fs_lock_op(sbi);
|
|
@@ -521,6 +664,8 @@ static int f2fs_mkdir(struct inode *dir,
|
|
|
|
if (IS_DIRSYNC(dir))
|
|
f2fs_sync_fs(sbi->sb, 1);
|
|
+
|
|
+ f2fs_balance_fs(sbi, true);
|
|
return 0;
|
|
|
|
out_fail:
|
|
@@ -544,6 +689,13 @@ static int f2fs_mknod(struct inode *dir,
|
|
struct inode *inode;
|
|
int err = 0;
|
|
|
|
+ if (unlikely(f2fs_cp_error(sbi)))
|
|
+ return -EIO;
|
|
+
|
|
+ err = dquot_initialize(dir);
|
|
+ if (err)
|
|
+ return err;
|
|
+
|
|
inode = f2fs_new_inode(dir, mode);
|
|
if (IS_ERR(inode))
|
|
return PTR_ERR(inode);
|
|
@@ -551,8 +703,6 @@ static int f2fs_mknod(struct inode *dir,
|
|
init_special_inode(inode, inode->i_mode, rdev);
|
|
inode->i_op = &f2fs_special_inode_operations;
|
|
|
|
- f2fs_balance_fs(sbi, true);
|
|
-
|
|
f2fs_lock_op(sbi);
|
|
err = f2fs_add_link(dentry, inode);
|
|
if (err)
|
|
@@ -565,6 +715,8 @@ static int f2fs_mknod(struct inode *dir,
|
|
|
|
if (IS_DIRSYNC(dir))
|
|
f2fs_sync_fs(sbi->sb, 1);
|
|
+
|
|
+ f2fs_balance_fs(sbi, true);
|
|
return 0;
|
|
out:
|
|
handle_failed_inode(inode);
|
|
@@ -578,6 +730,13 @@ static int __f2fs_tmpfile(struct inode *
|
|
struct inode *inode;
|
|
int err;
|
|
|
|
+ if (unlikely(f2fs_cp_error(sbi)))
|
|
+ return -EIO;
|
|
+
|
|
+ err = dquot_initialize(dir);
|
|
+ if (err)
|
|
+ return err;
|
|
+
|
|
inode = f2fs_new_inode(dir, mode);
|
|
if (IS_ERR(inode))
|
|
return PTR_ERR(inode);
|
|
@@ -591,8 +750,6 @@ static int __f2fs_tmpfile(struct inode *
|
|
inode->i_mapping->a_ops = &f2fs_dblock_aops;
|
|
}
|
|
|
|
- f2fs_balance_fs(sbi, true);
|
|
-
|
|
f2fs_lock_op(sbi);
|
|
err = acquire_orphan_inode(sbi);
|
|
if (err)
|
|
@@ -618,6 +775,8 @@ static int __f2fs_tmpfile(struct inode *
|
|
/* link_count was changed by d_tmpfile as well. */
|
|
f2fs_unlock_op(sbi);
|
|
unlock_new_inode(inode);
|
|
+
|
|
+ f2fs_balance_fs(sbi, true);
|
|
return 0;
|
|
|
|
release_out:
|
|
@@ -629,7 +788,12 @@ out:
|
|
|
|
static int f2fs_tmpfile(struct inode *dir, struct dentry *dentry, umode_t mode)
|
|
{
|
|
- if (f2fs_encrypted_inode(dir)) {
|
|
+ struct f2fs_sb_info *sbi = F2FS_I_SB(dir);
|
|
+
|
|
+ if (unlikely(f2fs_cp_error(sbi)))
|
|
+ return -EIO;
|
|
+
|
|
+ if (f2fs_encrypted_inode(dir) || DUMMY_ENCRYPTION_ENABLED(sbi)) {
|
|
int err = fscrypt_get_encryption_info(dir);
|
|
if (err)
|
|
return err;
|
|
@@ -640,6 +804,9 @@ static int f2fs_tmpfile(struct inode *di
|
|
|
|
static int f2fs_create_whiteout(struct inode *dir, struct inode **whiteout)
|
|
{
|
|
+ if (unlikely(f2fs_cp_error(F2FS_I_SB(dir))))
|
|
+ return -EIO;
|
|
+
|
|
return __f2fs_tmpfile(dir, NULL, S_IFCHR | WHITEOUT_MODE, whiteout);
|
|
}
|
|
|
|
@@ -659,16 +826,26 @@ static int f2fs_rename(struct inode *old
|
|
bool is_old_inline = f2fs_has_inline_dentry(old_dir);
|
|
int err = -ENOENT;
|
|
|
|
- if ((f2fs_encrypted_inode(old_dir) &&
|
|
- !fscrypt_has_encryption_key(old_dir)) ||
|
|
- (f2fs_encrypted_inode(new_dir) &&
|
|
- !fscrypt_has_encryption_key(new_dir)))
|
|
- return -ENOKEY;
|
|
+ if (unlikely(f2fs_cp_error(sbi)))
|
|
+ return -EIO;
|
|
|
|
- if ((old_dir != new_dir) && f2fs_encrypted_inode(new_dir) &&
|
|
- !fscrypt_has_permitted_context(new_dir, old_inode)) {
|
|
- err = -EPERM;
|
|
+ if (is_inode_flag_set(new_dir, FI_PROJ_INHERIT) &&
|
|
+ (!projid_eq(F2FS_I(new_dir)->i_projid,
|
|
+ F2FS_I(old_dentry->d_inode)->i_projid)))
|
|
+ return -EXDEV;
|
|
+
|
|
+ err = dquot_initialize(old_dir);
|
|
+ if (err)
|
|
+ goto out;
|
|
+
|
|
+ err = dquot_initialize(new_dir);
|
|
+ if (err)
|
|
goto out;
|
|
+
|
|
+ if (new_inode) {
|
|
+ err = dquot_initialize(new_inode);
|
|
+ if (err)
|
|
+ goto out;
|
|
}
|
|
|
|
old_entry = f2fs_find_entry(old_dir, &old_dentry->d_name, &old_page);
|
|
@@ -716,13 +893,6 @@ static int f2fs_rename(struct inode *old
|
|
if (err)
|
|
goto put_out_dir;
|
|
|
|
- err = update_dent_inode(old_inode, new_inode,
|
|
- &new_dentry->d_name);
|
|
- if (err) {
|
|
- release_orphan_inode(sbi);
|
|
- goto put_out_dir;
|
|
- }
|
|
-
|
|
f2fs_set_link(new_dir, new_entry, new_page, old_inode);
|
|
|
|
new_inode->i_ctime = current_time(new_inode);
|
|
@@ -774,13 +944,14 @@ static int f2fs_rename(struct inode *old
|
|
}
|
|
|
|
down_write(&F2FS_I(old_inode)->i_sem);
|
|
- file_lost_pino(old_inode);
|
|
- if (new_inode && file_enc_name(new_inode))
|
|
- file_set_enc_name(old_inode);
|
|
+ if (!old_dir_entry || whiteout)
|
|
+ file_lost_pino(old_inode);
|
|
+ else
|
|
+ F2FS_I(old_inode)->i_pino = new_dir->i_ino;
|
|
up_write(&F2FS_I(old_inode)->i_sem);
|
|
|
|
old_inode->i_ctime = current_time(old_inode);
|
|
- f2fs_mark_inode_dirty_sync(old_inode);
|
|
+ f2fs_mark_inode_dirty_sync(old_inode, false);
|
|
|
|
f2fs_delete_entry(old_entry, old_page, old_dir, NULL);
|
|
|
|
@@ -795,15 +966,15 @@ static int f2fs_rename(struct inode *old
|
|
}
|
|
|
|
if (old_dir_entry) {
|
|
- if (old_dir != new_dir && !whiteout) {
|
|
+ if (old_dir != new_dir && !whiteout)
|
|
f2fs_set_link(old_inode, old_dir_entry,
|
|
old_dir_page, new_dir);
|
|
- } else {
|
|
- f2fs_dentry_kunmap(old_inode, old_dir_page);
|
|
+ else
|
|
f2fs_put_page(old_dir_page, 0);
|
|
- }
|
|
f2fs_i_links_write(old_dir, false);
|
|
}
|
|
+ if (F2FS_OPTION(sbi).fsync_mode == FSYNC_MODE_STRICT)
|
|
+ add_ino_entry(sbi, new_dir->i_ino, TRANS_DIR_INO);
|
|
|
|
f2fs_unlock_op(sbi);
|
|
|
|
@@ -813,20 +984,15 @@ static int f2fs_rename(struct inode *old
|
|
|
|
put_out_dir:
|
|
f2fs_unlock_op(sbi);
|
|
- if (new_page) {
|
|
- f2fs_dentry_kunmap(new_dir, new_page);
|
|
+ if (new_page)
|
|
f2fs_put_page(new_page, 0);
|
|
- }
|
|
out_whiteout:
|
|
if (whiteout)
|
|
iput(whiteout);
|
|
out_dir:
|
|
- if (old_dir_entry) {
|
|
- f2fs_dentry_kunmap(old_inode, old_dir_page);
|
|
+ if (old_dir_entry)
|
|
f2fs_put_page(old_dir_page, 0);
|
|
- }
|
|
out_old:
|
|
- f2fs_dentry_kunmap(old_dir, old_page);
|
|
f2fs_put_page(old_page, 0);
|
|
out:
|
|
return err;
|
|
@@ -845,17 +1011,30 @@ static int f2fs_cross_rename(struct inod
|
|
int old_nlink = 0, new_nlink = 0;
|
|
int err = -ENOENT;
|
|
|
|
- if ((f2fs_encrypted_inode(old_dir) &&
|
|
- !fscrypt_has_encryption_key(old_dir)) ||
|
|
- (f2fs_encrypted_inode(new_dir) &&
|
|
- !fscrypt_has_encryption_key(new_dir)))
|
|
- return -ENOKEY;
|
|
+ if (unlikely(f2fs_cp_error(sbi)))
|
|
+ return -EIO;
|
|
|
|
- if ((f2fs_encrypted_inode(old_dir) || f2fs_encrypted_inode(new_dir)) &&
|
|
- (old_dir != new_dir) &&
|
|
- (!fscrypt_has_permitted_context(new_dir, old_inode) ||
|
|
- !fscrypt_has_permitted_context(old_dir, new_inode)))
|
|
- return -EPERM;
|
|
+ if ((is_inode_flag_set(new_dir, FI_PROJ_INHERIT) &&
|
|
+ !projid_eq(F2FS_I(new_dir)->i_projid,
|
|
+ F2FS_I(old_dentry->d_inode)->i_projid)) ||
|
|
+ (is_inode_flag_set(new_dir, FI_PROJ_INHERIT) &&
|
|
+ !projid_eq(F2FS_I(old_dir)->i_projid,
|
|
+ F2FS_I(new_dentry->d_inode)->i_projid)))
|
|
+ return -EXDEV;
|
|
+
|
|
+ err = dquot_initialize(old_dir);
|
|
+ if (err)
|
|
+ goto out;
|
|
+
|
|
+ err = dquot_initialize(new_dir);
|
|
+ if (err)
|
|
+ goto out;
|
|
+
|
|
+ if (new_inode) {
|
|
+ err = dquot_initialize(new_inode);
|
|
+ if (err)
|
|
+ goto out;
|
|
+ }
|
|
|
|
old_entry = f2fs_find_entry(old_dir, &old_dentry->d_name, &old_page);
|
|
if (!old_entry) {
|
|
@@ -904,8 +1083,8 @@ static int f2fs_cross_rename(struct inod
|
|
old_nlink = old_dir_entry ? -1 : 1;
|
|
new_nlink = -old_nlink;
|
|
err = -EMLINK;
|
|
- if ((old_nlink > 0 && old_inode->i_nlink >= F2FS_LINK_MAX) ||
|
|
- (new_nlink > 0 && new_inode->i_nlink >= F2FS_LINK_MAX))
|
|
+ if ((old_nlink > 0 && old_dir->i_nlink >= F2FS_LINK_MAX) ||
|
|
+ (new_nlink > 0 && new_dir->i_nlink >= F2FS_LINK_MAX))
|
|
goto out_new_dir;
|
|
}
|
|
|
|
@@ -913,18 +1092,6 @@ static int f2fs_cross_rename(struct inod
|
|
|
|
f2fs_lock_op(sbi);
|
|
|
|
- err = update_dent_inode(old_inode, new_inode, &new_dentry->d_name);
|
|
- if (err)
|
|
- goto out_unlock;
|
|
- if (file_enc_name(new_inode))
|
|
- file_set_enc_name(old_inode);
|
|
-
|
|
- err = update_dent_inode(new_inode, old_inode, &old_dentry->d_name);
|
|
- if (err)
|
|
- goto out_undo;
|
|
- if (file_enc_name(old_inode))
|
|
- file_set_enc_name(new_inode);
|
|
-
|
|
/* update ".." directory entry info of old dentry */
|
|
if (old_dir_entry)
|
|
f2fs_set_link(old_inode, old_dir_entry, old_dir_page, new_dir);
|
|
@@ -946,7 +1113,7 @@ static int f2fs_cross_rename(struct inod
|
|
f2fs_i_links_write(old_dir, old_nlink > 0);
|
|
up_write(&F2FS_I(old_dir)->i_sem);
|
|
}
|
|
- f2fs_mark_inode_dirty_sync(old_dir);
|
|
+ f2fs_mark_inode_dirty_sync(old_dir, false);
|
|
|
|
/* update directory entry info of new dir inode */
|
|
f2fs_set_link(new_dir, new_entry, new_page, old_inode);
|
|
@@ -961,36 +1128,29 @@ static int f2fs_cross_rename(struct inod
|
|
f2fs_i_links_write(new_dir, new_nlink > 0);
|
|
up_write(&F2FS_I(new_dir)->i_sem);
|
|
}
|
|
- f2fs_mark_inode_dirty_sync(new_dir);
|
|
+ f2fs_mark_inode_dirty_sync(new_dir, false);
|
|
+
|
|
+ if (F2FS_OPTION(sbi).fsync_mode == FSYNC_MODE_STRICT) {
|
|
+ add_ino_entry(sbi, old_dir->i_ino, TRANS_DIR_INO);
|
|
+ add_ino_entry(sbi, new_dir->i_ino, TRANS_DIR_INO);
|
|
+ }
|
|
|
|
f2fs_unlock_op(sbi);
|
|
|
|
if (IS_DIRSYNC(old_dir) || IS_DIRSYNC(new_dir))
|
|
f2fs_sync_fs(sbi->sb, 1);
|
|
return 0;
|
|
-out_undo:
|
|
- /*
|
|
- * Still we may fail to recover name info of f2fs_inode here
|
|
- * Drop it, once its name is set as encrypted
|
|
- */
|
|
- update_dent_inode(old_inode, old_inode, &old_dentry->d_name);
|
|
-out_unlock:
|
|
- f2fs_unlock_op(sbi);
|
|
out_new_dir:
|
|
if (new_dir_entry) {
|
|
- f2fs_dentry_kunmap(new_inode, new_dir_page);
|
|
f2fs_put_page(new_dir_page, 0);
|
|
}
|
|
out_old_dir:
|
|
if (old_dir_entry) {
|
|
- f2fs_dentry_kunmap(old_inode, old_dir_page);
|
|
f2fs_put_page(old_dir_page, 0);
|
|
}
|
|
out_new:
|
|
- f2fs_dentry_kunmap(new_dir, new_page);
|
|
f2fs_put_page(new_page, 0);
|
|
out_old:
|
|
- f2fs_dentry_kunmap(old_dir, old_page);
|
|
f2fs_put_page(old_page, 0);
|
|
out:
|
|
return err;
|
|
@@ -1000,9 +1160,16 @@ static int f2fs_rename2(struct inode *ol
|
|
struct inode *new_dir, struct dentry *new_dentry,
|
|
unsigned int flags)
|
|
{
|
|
+ int err;
|
|
+
|
|
if (flags & ~(RENAME_NOREPLACE | RENAME_EXCHANGE | RENAME_WHITEOUT))
|
|
return -EINVAL;
|
|
|
|
+ err = fscrypt_prepare_rename(old_dir, old_dentry, new_dir, new_dentry,
|
|
+ flags);
|
|
+ if (err)
|
|
+ return err;
|
|
+
|
|
if (flags & RENAME_EXCHANGE) {
|
|
return f2fs_cross_rename(old_dir, old_dentry,
|
|
new_dir, new_dentry);
|
|
@@ -1018,68 +1185,20 @@ static const char *f2fs_encrypted_get_li
|
|
struct inode *inode,
|
|
struct delayed_call *done)
|
|
{
|
|
- struct page *cpage = NULL;
|
|
- char *caddr, *paddr = NULL;
|
|
- struct fscrypt_str cstr = FSTR_INIT(NULL, 0);
|
|
- struct fscrypt_str pstr = FSTR_INIT(NULL, 0);
|
|
- struct fscrypt_symlink_data *sd;
|
|
- u32 max_size = inode->i_sb->s_blocksize;
|
|
- int res;
|
|
+ struct page *page;
|
|
+ const char *target;
|
|
|
|
if (!dentry)
|
|
return ERR_PTR(-ECHILD);
|
|
|
|
- res = fscrypt_get_encryption_info(inode);
|
|
- if (res)
|
|
- return ERR_PTR(res);
|
|
-
|
|
- cpage = read_mapping_page(inode->i_mapping, 0, NULL);
|
|
- if (IS_ERR(cpage))
|
|
- return ERR_CAST(cpage);
|
|
- caddr = page_address(cpage);
|
|
-
|
|
- /* Symlink is encrypted */
|
|
- sd = (struct fscrypt_symlink_data *)caddr;
|
|
- cstr.name = sd->encrypted_path;
|
|
- cstr.len = le16_to_cpu(sd->len);
|
|
-
|
|
- /* this is broken symlink case */
|
|
- if (unlikely(cstr.len == 0)) {
|
|
- res = -ENOENT;
|
|
- goto errout;
|
|
- }
|
|
-
|
|
- if ((cstr.len + sizeof(struct fscrypt_symlink_data) - 1) > max_size) {
|
|
- /* Symlink data on the disk is corrupted */
|
|
- res = -EIO;
|
|
- goto errout;
|
|
- }
|
|
- res = fscrypt_fname_alloc_buffer(inode, cstr.len, &pstr);
|
|
- if (res)
|
|
- goto errout;
|
|
-
|
|
- res = fscrypt_fname_disk_to_usr(inode, 0, 0, &cstr, &pstr);
|
|
- if (res)
|
|
- goto errout;
|
|
-
|
|
- /* this is broken symlink case */
|
|
- if (unlikely(pstr.name[0] == 0)) {
|
|
- res = -ENOENT;
|
|
- goto errout;
|
|
- }
|
|
-
|
|
- paddr = pstr.name;
|
|
-
|
|
- /* Null-terminate the name */
|
|
- paddr[pstr.len] = '\0';
|
|
+ page = read_mapping_page(inode->i_mapping, 0, NULL);
|
|
+ if (IS_ERR(page))
|
|
+ return ERR_CAST(page);
|
|
|
|
- put_page(cpage);
|
|
- set_delayed_call(done, kfree_link, paddr);
|
|
- return paddr;
|
|
-errout:
|
|
- fscrypt_fname_free_buffer(&pstr);
|
|
- put_page(cpage);
|
|
- return ERR_PTR(res);
|
|
+ target = fscrypt_get_symlink(inode, page_address(page),
|
|
+ inode->i_sb->s_blocksize, done);
|
|
+ put_page(page);
|
|
+ return target;
|
|
}
|
|
|
|
const struct inode_operations f2fs_encrypted_symlink_inode_operations = {
|