Merge tag 'upstream-4.9-rc1' of git://git.infradead.org/linux-ubifs
[sfrench/cifs-2.6.git] / fs / ubifs / dir.c
index e0282534f1707060a9de39e898f586faef44fccc..c8f60df2733eba516b189716a6b3bb3ab64520d1 100644 (file)
@@ -1082,6 +1082,9 @@ static int ubifs_rename(struct inode *old_dir, struct dentry *old_dentry,
        struct timespec time;
        unsigned int uninitialized_var(saved_nlink);
 
+       if (flags & ~RENAME_NOREPLACE)
+               return -EINVAL;
+
        /*
         * Budget request settings: deletion direntry, new direntry, removing
         * the old inode, and changing old and new parent directory inodes.
@@ -1095,11 +1098,6 @@ static int ubifs_rename(struct inode *old_dir, struct dentry *old_dentry,
                old_dentry, old_inode->i_ino, old_dir->i_ino,
                new_dentry, new_dir->i_ino, flags);
 
-       if (flags & ~(RENAME_NOREPLACE | RENAME_WHITEOUT))
-               return -EINVAL;
-
-       ubifs_assert(inode_is_locked(old_dir));
-       ubifs_assert(inode_is_locked(new_dir));
        if (unlink)
                ubifs_assert(inode_is_locked(new_inode));
 
@@ -1283,6 +1281,64 @@ out_cancel:
        return err;
 }
 
+static int ubifs_xrename(struct inode *old_dir, struct dentry *old_dentry,
+                       struct inode *new_dir, struct dentry *new_dentry)
+{
+       struct ubifs_info *c = old_dir->i_sb->s_fs_info;
+       struct ubifs_budget_req req = { .new_dent = 1, .mod_dent = 1,
+                               .dirtied_ino = 2 };
+       int sync = IS_DIRSYNC(old_dir) || IS_DIRSYNC(new_dir);
+       struct inode *fst_inode = d_inode(old_dentry);
+       struct inode *snd_inode = d_inode(new_dentry);
+       struct timespec time;
+       int err;
+
+       ubifs_assert(fst_inode && snd_inode);
+
+       lock_4_inodes(old_dir, new_dir, NULL, NULL);
+
+       time = ubifs_current_time(old_dir);
+       fst_inode->i_ctime = time;
+       snd_inode->i_ctime = time;
+       old_dir->i_mtime = old_dir->i_ctime = time;
+       new_dir->i_mtime = new_dir->i_ctime = time;
+
+       if (old_dir != new_dir) {
+               if (S_ISDIR(fst_inode->i_mode) && !S_ISDIR(snd_inode->i_mode)) {
+                       inc_nlink(new_dir);
+                       drop_nlink(old_dir);
+               }
+               else if (!S_ISDIR(fst_inode->i_mode) && S_ISDIR(snd_inode->i_mode)) {
+                       drop_nlink(new_dir);
+                       inc_nlink(old_dir);
+               }
+       }
+
+       err = ubifs_jnl_xrename(c, old_dir, old_dentry, new_dir, new_dentry,
+                               sync);
+
+       unlock_4_inodes(old_dir, new_dir, NULL, NULL);
+       ubifs_release_budget(c, &req);
+
+       return err;
+}
+
+static int ubifs_rename2(struct inode *old_dir, struct dentry *old_dentry,
+                       struct inode *new_dir, struct dentry *new_dentry,
+                       unsigned int flags)
+{
+       if (flags & ~(RENAME_NOREPLACE | RENAME_WHITEOUT | RENAME_EXCHANGE))
+               return -EINVAL;
+
+       ubifs_assert(inode_is_locked(old_dir));
+       ubifs_assert(inode_is_locked(new_dir));
+
+       if (flags & RENAME_EXCHANGE)
+               return ubifs_xrename(old_dir, old_dentry, new_dir, new_dentry);
+
+       return ubifs_rename(old_dir, old_dentry, new_dir, new_dentry, flags);
+}
+
 int ubifs_getattr(struct vfsmount *mnt, struct dentry *dentry,
                  struct kstat *stat)
 {
@@ -1331,13 +1387,10 @@ const struct inode_operations ubifs_dir_inode_operations = {
        .mkdir       = ubifs_mkdir,
        .rmdir       = ubifs_rmdir,
        .mknod       = ubifs_mknod,
-       .rename2     = ubifs_rename,
+       .rename      = ubifs_rename2,
        .setattr     = ubifs_setattr,
        .getattr     = ubifs_getattr,
-       .setxattr    = generic_setxattr,
-       .getxattr    = generic_getxattr,
        .listxattr   = ubifs_listxattr,
-       .removexattr = generic_removexattr,
 #ifdef CONFIG_UBIFS_ATIME_SUPPORT
        .update_time = ubifs_update_time,
 #endif