fs/affs: add rename exchange
authorFabian Frederick <fabf@skynet.be>
Fri, 5 May 2017 18:52:07 +0000 (20:52 +0200)
committerAl Viro <viro@zeniv.linux.org.uk>
Fri, 5 May 2017 19:24:52 +0000 (15:24 -0400)
Process RENAME_EXCHANGE in affs_rename2() adding static
affs_xrename() based on affs_rename().

We remove headers from respective directories then
affect bh to other inode directory entries for swapping.

Signed-off-by: Fabian Frederick <fabf@skynet.be>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
fs/affs/namei.c

index 49a8dc12d60434c2395b15a8553c8d8f2eef0ac0..46d3ace6761d7df89dcbbc598991a41561e16518 100644 (file)
@@ -441,17 +441,73 @@ done:
        return retval;
 }
 
+static int
+affs_xrename(struct inode *old_dir, struct dentry *old_dentry,
+            struct inode *new_dir, struct dentry *new_dentry)
+{
+
+       struct super_block *sb = old_dir->i_sb;
+       struct buffer_head *bh_old = NULL;
+       struct buffer_head *bh_new = NULL;
+       int retval;
+
+       bh_old = affs_bread(sb, d_inode(old_dentry)->i_ino);
+       if (!bh_old)
+               return -EIO;
+
+       bh_new = affs_bread(sb, d_inode(new_dentry)->i_ino);
+       if (!bh_new)
+               return -EIO;
+
+       /* Remove old header from its parent directory. */
+       affs_lock_dir(old_dir);
+       retval = affs_remove_hash(old_dir, bh_old);
+       affs_unlock_dir(old_dir);
+       if (retval)
+               goto done;
+
+       /* Remove new header from its parent directory. */
+       affs_lock_dir(new_dir);
+       retval = affs_remove_hash(new_dir, bh_new);
+       affs_unlock_dir(new_dir);
+       if (retval)
+               goto done;
+
+       /* Insert old into the new directory with the new name. */
+       affs_copy_name(AFFS_TAIL(sb, bh_old)->name, new_dentry);
+       affs_fix_checksum(sb, bh_old);
+       affs_lock_dir(new_dir);
+       retval = affs_insert_hash(new_dir, bh_old);
+       affs_unlock_dir(new_dir);
+
+       /* Insert new into the old directory with the old name. */
+       affs_copy_name(AFFS_TAIL(sb, bh_new)->name, old_dentry);
+       affs_fix_checksum(sb, bh_new);
+       affs_lock_dir(old_dir);
+       retval = affs_insert_hash(old_dir, bh_new);
+       affs_unlock_dir(old_dir);
+done:
+       mark_buffer_dirty_inode(bh_old, new_dir);
+       mark_buffer_dirty_inode(bh_new, old_dir);
+       affs_brelse(bh_old);
+       affs_brelse(bh_new);
+       return retval;
+}
+
 int affs_rename2(struct inode *old_dir, struct dentry *old_dentry,
                        struct inode *new_dir, struct dentry *new_dentry,
                        unsigned int flags)
 {
 
-       if (flags & ~RENAME_NOREPLACE)
+       if (flags & ~(RENAME_NOREPLACE | RENAME_EXCHANGE))
                return -EINVAL;
 
        pr_debug("%s(old=%lu,\"%pd\" to new=%lu,\"%pd\")\n", __func__,
                 old_dir->i_ino, old_dentry, new_dir->i_ino, new_dentry);
 
+       if (flags & RENAME_EXCHANGE)
+               return affs_xrename(old_dir, old_dentry, new_dir, new_dentry);
+
        return affs_rename(old_dir, old_dentry, new_dir, new_dentry);
 }