diff options
author | Fabian Frederick <fabf@skynet.be> | 2017-05-05 21:52:07 +0300 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2017-05-05 22:24:52 +0300 |
commit | 6b4657667ba06cd2e07206c65e630acdf248bfd1 (patch) | |
tree | 6ad3808b9827a537754e01be3ce97d310d1ca63a /fs/affs/namei.c | |
parent | c6184028a75cf63036267089741ec7ef975ebc27 (diff) | |
download | linux-6b4657667ba06cd2e07206c65e630acdf248bfd1.tar.xz |
fs/affs: add rename exchange
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>
Diffstat (limited to 'fs/affs/namei.c')
-rw-r--r-- | fs/affs/namei.c | 58 |
1 files changed, 57 insertions, 1 deletions
diff --git a/fs/affs/namei.c b/fs/affs/namei.c index 49a8dc12d604..46d3ace6761d 100644 --- a/fs/affs/namei.c +++ b/fs/affs/namei.c @@ -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); } |