diff options
Diffstat (limited to 'target/linux/generic/pending-4.4/052-02-ubifs-Implement-RENAME_WHITEOUT.patch')
-rw-r--r-- | target/linux/generic/pending-4.4/052-02-ubifs-Implement-RENAME_WHITEOUT.patch | 343 |
1 files changed, 343 insertions, 0 deletions
diff --git a/target/linux/generic/pending-4.4/052-02-ubifs-Implement-RENAME_WHITEOUT.patch b/target/linux/generic/pending-4.4/052-02-ubifs-Implement-RENAME_WHITEOUT.patch new file mode 100644 index 0000000000..c2ec904939 --- /dev/null +++ b/target/linux/generic/pending-4.4/052-02-ubifs-Implement-RENAME_WHITEOUT.patch @@ -0,0 +1,343 @@ +From: Richard Weinberger <richard@nod.at> +Date: Tue, 13 Sep 2016 16:18:56 +0200 +Subject: [PATCH] ubifs: Implement RENAME_WHITEOUT + +Adds RENAME_WHITEOUT support to UBIFS, we implement +it in the same way as ext4 and xfs do. +For an overview of other ways to implement it please +refere to commit 7dcf5c3e4527 ("xfs: add RENAME_WHITEOUT support"). + +Signed-off-by: Richard Weinberger <richard@nod.at> +--- + +--- a/fs/ubifs/dir.c ++++ b/fs/ubifs/dir.c +@@ -301,8 +301,8 @@ out_budg: + return err; + } + +-static int ubifs_tmpfile(struct inode *dir, struct dentry *dentry, +- umode_t mode) ++static int do_tmpfile(struct inode *dir, struct dentry *dentry, ++ umode_t mode, struct inode **whiteout) + { + struct inode *inode; + struct ubifs_info *c = dir->i_sb->s_fs_info; +@@ -336,14 +336,27 @@ static int ubifs_tmpfile(struct inode *d + } + ui = ubifs_inode(inode); + ++ if (whiteout) { ++ init_special_inode(inode, inode->i_mode, WHITEOUT_DEV); ++ ubifs_assert(inode->i_op == &ubifs_file_inode_operations); ++ } ++ + err = ubifs_init_security(dir, inode, &dentry->d_name); + if (err) + goto out_inode; + + mutex_lock(&ui->ui_mutex); + insert_inode_hash(inode); +- d_tmpfile(dentry, inode); ++ ++ if (whiteout) { ++ mark_inode_dirty(inode); ++ drop_nlink(inode); ++ *whiteout = inode; ++ } else { ++ d_tmpfile(dentry, inode); ++ } + ubifs_assert(ui->dirty); ++ + instantiated = 1; + mutex_unlock(&ui->ui_mutex); + +@@ -371,6 +384,12 @@ out_budg: + return err; + } + ++static int ubifs_tmpfile(struct inode *dir, struct dentry *dentry, ++ umode_t mode) ++{ ++ return do_tmpfile(dir, dentry, mode, NULL); ++} ++ + /** + * vfs_dent_type - get VFS directory entry type. + * @type: UBIFS directory entry type +@@ -1003,37 +1022,43 @@ out_budg: + } + + /** +- * lock_3_inodes - a wrapper for locking three UBIFS inodes. ++ * lock_4_inodes - a wrapper for locking three UBIFS inodes. + * @inode1: first inode + * @inode2: second inode + * @inode3: third inode ++ * @inode4: fouth inode + * + * This function is used for 'ubifs_rename()' and @inode1 may be the same as +- * @inode2 whereas @inode3 may be %NULL. ++ * @inode2 whereas @inode3 and @inode4 may be %NULL. + * + * We do not implement any tricks to guarantee strict lock ordering, because + * VFS has already done it for us on the @i_mutex. So this is just a simple + * wrapper function. + */ +-static void lock_3_inodes(struct inode *inode1, struct inode *inode2, +- struct inode *inode3) ++static void lock_4_inodes(struct inode *inode1, struct inode *inode2, ++ struct inode *inode3, struct inode *inode4) + { + mutex_lock_nested(&ubifs_inode(inode1)->ui_mutex, WB_MUTEX_1); + if (inode2 != inode1) + mutex_lock_nested(&ubifs_inode(inode2)->ui_mutex, WB_MUTEX_2); + if (inode3) + mutex_lock_nested(&ubifs_inode(inode3)->ui_mutex, WB_MUTEX_3); ++ if (inode4) ++ mutex_lock_nested(&ubifs_inode(inode4)->ui_mutex, WB_MUTEX_4); + } + + /** +- * unlock_3_inodes - a wrapper for unlocking three UBIFS inodes for rename. ++ * unlock_4_inodes - a wrapper for unlocking three UBIFS inodes for rename. + * @inode1: first inode + * @inode2: second inode + * @inode3: third inode ++ * @inode4: fouth inode + */ +-static void unlock_3_inodes(struct inode *inode1, struct inode *inode2, +- struct inode *inode3) ++static void unlock_4_inodes(struct inode *inode1, struct inode *inode2, ++ struct inode *inode3, struct inode *inode4) + { ++ if (inode4) ++ mutex_unlock(&ubifs_inode(inode4)->ui_mutex); + if (inode3) + mutex_unlock(&ubifs_inode(inode3)->ui_mutex); + if (inode1 != inode2) +@@ -1042,12 +1067,15 @@ static void unlock_3_inodes(struct inode + } + + static int ubifs_rename(struct inode *old_dir, struct dentry *old_dentry, +- struct inode *new_dir, struct dentry *new_dentry) ++ struct inode *new_dir, struct dentry *new_dentry, ++ unsigned int flags) + { + struct ubifs_info *c = old_dir->i_sb->s_fs_info; + struct inode *old_inode = d_inode(old_dentry); + struct inode *new_inode = d_inode(new_dentry); ++ struct inode *whiteout = NULL; + struct ubifs_inode *old_inode_ui = ubifs_inode(old_inode); ++ struct ubifs_inode *whiteout_ui = NULL; + int err, release, sync = 0, move = (new_dir != old_dir); + int is_dir = S_ISDIR(old_inode->i_mode); + int unlink = !!new_inode; +@@ -1069,9 +1097,13 @@ static int ubifs_rename(struct inode *ol + * separately. + */ + +- dbg_gen("dent '%pd' ino %lu in dir ino %lu to dent '%pd' in dir ino %lu", ++ dbg_gen("dent '%pd' ino %lu in dir ino %lu to dent '%pd' in dir ino %lu flags 0x%x", + old_dentry, old_inode->i_ino, old_dir->i_ino, +- new_dentry, new_dir->i_ino); ++ new_dentry, new_dir->i_ino, flags); ++ ++ if (flags & ~(RENAME_NOREPLACE | RENAME_WHITEOUT)) ++ return -EINVAL; ++ + ubifs_assert(mutex_is_locked(&old_dir->i_mutex)); + ubifs_assert(mutex_is_locked(&new_dir->i_mutex)); + if (unlink) +@@ -1093,7 +1125,32 @@ static int ubifs_rename(struct inode *ol + return err; + } + +- lock_3_inodes(old_dir, new_dir, new_inode); ++ if (flags & RENAME_WHITEOUT) { ++ union ubifs_dev_desc *dev = NULL; ++ ++ dev = kmalloc(sizeof(union ubifs_dev_desc), GFP_NOFS); ++ if (!dev) { ++ ubifs_release_budget(c, &req); ++ ubifs_release_budget(c, &ino_req); ++ return -ENOMEM; ++ } ++ ++ err = do_tmpfile(old_dir, old_dentry, S_IFCHR | WHITEOUT_MODE, &whiteout); ++ if (err) { ++ ubifs_release_budget(c, &req); ++ ubifs_release_budget(c, &ino_req); ++ kfree(dev); ++ return err; ++ } ++ ++ whiteout->i_state |= I_LINKABLE; ++ whiteout_ui = ubifs_inode(whiteout); ++ whiteout_ui->data = dev; ++ whiteout_ui->data_len = ubifs_encode_dev(dev, MKDEV(0, 0)); ++ ubifs_assert(!whiteout_ui->dirty); ++ } ++ ++ lock_4_inodes(old_dir, new_dir, new_inode, whiteout); + + /* + * Like most other Unix systems, set the @i_ctime for inodes on a +@@ -1163,12 +1220,34 @@ static int ubifs_rename(struct inode *ol + if (unlink && IS_SYNC(new_inode)) + sync = 1; + } +- err = ubifs_jnl_rename(c, old_dir, old_dentry, new_dir, new_dentry, ++ ++ if (whiteout) { ++ struct ubifs_budget_req wht_req = { .dirtied_ino = 1, ++ .dirtied_ino_d = \ ++ ALIGN(ubifs_inode(whiteout)->data_len, 8) }; ++ ++ err = ubifs_budget_space(c, &wht_req); ++ if (err) { ++ ubifs_release_budget(c, &req); ++ ubifs_release_budget(c, &ino_req); ++ kfree(whiteout_ui->data); ++ whiteout_ui->data_len = 0; ++ iput(whiteout); ++ return err; ++ } ++ ++ inc_nlink(whiteout); ++ mark_inode_dirty(whiteout); ++ whiteout->i_state &= ~I_LINKABLE; ++ iput(whiteout); ++ } ++ ++ err = ubifs_jnl_rename(c, old_dir, old_dentry, new_dir, new_dentry, whiteout, + sync); + if (err) + goto out_cancel; + +- unlock_3_inodes(old_dir, new_dir, new_inode); ++ unlock_4_inodes(old_dir, new_dir, new_inode, whiteout); + ubifs_release_budget(c, &req); + + mutex_lock(&old_inode_ui->ui_mutex); +@@ -1201,7 +1280,11 @@ out_cancel: + inc_nlink(old_dir); + } + } +- unlock_3_inodes(old_dir, new_dir, new_inode); ++ if (whiteout) { ++ drop_nlink(whiteout); ++ iput(whiteout); ++ } ++ unlock_4_inodes(old_dir, new_dir, new_inode, whiteout); + ubifs_release_budget(c, &ino_req); + ubifs_release_budget(c, &req); + return err; +@@ -1255,7 +1338,7 @@ const struct inode_operations ubifs_dir_ + .mkdir = ubifs_mkdir, + .rmdir = ubifs_rmdir, + .mknod = ubifs_mknod, +- .rename = ubifs_rename, ++ .rename2 = ubifs_rename, + .setattr = ubifs_setattr, + .getattr = ubifs_getattr, + .setxattr = ubifs_setxattr, +--- a/fs/ubifs/journal.c ++++ b/fs/ubifs/journal.c +@@ -917,14 +917,15 @@ int ubifs_jnl_delete_inode(struct ubifs_ + * @sync: non-zero if the write-buffer has to be synchronized + * + * This function implements the re-name operation which may involve writing up +- * to 3 inodes and 2 directory entries. It marks the written inodes as clean ++ * to 4 inodes and 2 directory entries. It marks the written inodes as clean + * and returns zero on success. In case of failure, a negative error code is + * returned. + */ + int ubifs_jnl_rename(struct ubifs_info *c, const struct inode *old_dir, + const struct dentry *old_dentry, + const struct inode *new_dir, +- const struct dentry *new_dentry, int sync) ++ const struct dentry *new_dentry, ++ const struct inode *whiteout, int sync) + { + void *p; + union ubifs_key key; +@@ -980,13 +981,19 @@ int ubifs_jnl_rename(struct ubifs_info * + zero_dent_node_unused(dent); + ubifs_prep_grp_node(c, dent, dlen1, 0); + +- /* Make deletion dent */ + dent2 = (void *)dent + aligned_dlen1; + dent2->ch.node_type = UBIFS_DENT_NODE; + dent_key_init_flash(c, &dent2->key, old_dir->i_ino, + &old_dentry->d_name); +- dent2->inum = 0; +- dent2->type = DT_UNKNOWN; ++ ++ if (whiteout) { ++ dent2->inum = cpu_to_le64(whiteout->i_ino); ++ dent2->type = get_dent_type(whiteout->i_mode); ++ } else { ++ /* Make deletion dent */ ++ dent2->inum = 0; ++ dent2->type = DT_UNKNOWN; ++ } + dent2->nlen = cpu_to_le16(old_dentry->d_name.len); + memcpy(dent2->name, old_dentry->d_name.name, old_dentry->d_name.len); + dent2->name[old_dentry->d_name.len] = '\0'; +@@ -1035,16 +1042,26 @@ int ubifs_jnl_rename(struct ubifs_info * + if (err) + goto out_ro; + +- err = ubifs_add_dirt(c, lnum, dlen2); +- if (err) +- goto out_ro; +- +- dent_key_init(c, &key, old_dir->i_ino, &old_dentry->d_name); +- err = ubifs_tnc_remove_nm(c, &key, &old_dentry->d_name); +- if (err) +- goto out_ro; ++ offs += aligned_dlen1; ++ if (whiteout) { ++ dent_key_init(c, &key, old_dir->i_ino, &old_dentry->d_name); ++ err = ubifs_tnc_add_nm(c, &key, lnum, offs, dlen2, &old_dentry->d_name); ++ if (err) ++ goto out_ro; ++ ++ ubifs_delete_orphan(c, whiteout->i_ino); ++ } else { ++ err = ubifs_add_dirt(c, lnum, dlen2); ++ if (err) ++ goto out_ro; ++ ++ dent_key_init(c, &key, old_dir->i_ino, &old_dentry->d_name); ++ err = ubifs_tnc_remove_nm(c, &key, &old_dentry->d_name); ++ if (err) ++ goto out_ro; ++ } + +- offs += aligned_dlen1 + aligned_dlen2; ++ offs += aligned_dlen2; + if (new_inode) { + ino_key_init(c, &key, new_inode->i_ino); + err = ubifs_tnc_add(c, &key, lnum, offs, ilen); +--- a/fs/ubifs/ubifs.h ++++ b/fs/ubifs/ubifs.h +@@ -180,6 +180,7 @@ enum { + WB_MUTEX_1 = 0, + WB_MUTEX_2 = 1, + WB_MUTEX_3 = 2, ++ WB_MUTEX_4 = 3, + }; + + /* +@@ -1546,7 +1547,8 @@ int ubifs_jnl_delete_inode(struct ubifs_ + int ubifs_jnl_rename(struct ubifs_info *c, const struct inode *old_dir, + const struct dentry *old_dentry, + const struct inode *new_dir, +- const struct dentry *new_dentry, int sync); ++ const struct dentry *new_dentry, ++ const struct inode *whiteout, int sync); + int ubifs_jnl_truncate(struct ubifs_info *c, const struct inode *inode, + loff_t old_size, loff_t new_size); + int ubifs_jnl_delete_xattr(struct ubifs_info *c, const struct inode *host, |