Merge remote-tracking branches 'regulator/fix/max1586', 'regulator/fix/max77686'...
[sfrench/cifs-2.6.git] / mm / shmem.c
index cd6fc7590e54f758adacdcfc9cdcb402c7cd3005..185836ba53ef6e23a63b6a0de7c22ebae19eac0b 100644 (file)
@@ -2345,6 +2345,32 @@ static int shmem_exchange(struct inode *old_dir, struct dentry *old_dentry, stru
        return 0;
 }
 
+static int shmem_whiteout(struct inode *old_dir, struct dentry *old_dentry)
+{
+       struct dentry *whiteout;
+       int error;
+
+       whiteout = d_alloc(old_dentry->d_parent, &old_dentry->d_name);
+       if (!whiteout)
+               return -ENOMEM;
+
+       error = shmem_mknod(old_dir, whiteout,
+                           S_IFCHR | WHITEOUT_MODE, WHITEOUT_DEV);
+       dput(whiteout);
+       if (error)
+               return error;
+
+       /*
+        * Cheat and hash the whiteout while the old dentry is still in
+        * place, instead of playing games with FS_RENAME_DOES_D_MOVE.
+        *
+        * d_lookup() will consistently find one of them at this point,
+        * not sure which one, but that isn't even important.
+        */
+       d_rehash(whiteout);
+       return 0;
+}
+
 /*
  * The VFS layer already does all the dentry stuff for rename,
  * we just have to decrement the usage count for the target if
@@ -2356,7 +2382,7 @@ static int shmem_rename2(struct inode *old_dir, struct dentry *old_dentry, struc
        struct inode *inode = old_dentry->d_inode;
        int they_are_dirs = S_ISDIR(inode->i_mode);
 
-       if (flags & ~(RENAME_NOREPLACE | RENAME_EXCHANGE))
+       if (flags & ~(RENAME_NOREPLACE | RENAME_EXCHANGE | RENAME_WHITEOUT))
                return -EINVAL;
 
        if (flags & RENAME_EXCHANGE)
@@ -2365,6 +2391,14 @@ static int shmem_rename2(struct inode *old_dir, struct dentry *old_dentry, struc
        if (!simple_empty(new_dentry))
                return -ENOTEMPTY;
 
+       if (flags & RENAME_WHITEOUT) {
+               int error;
+
+               error = shmem_whiteout(old_dir, old_dentry);
+               if (error)
+                       return error;
+       }
+
        if (new_dentry->d_inode) {
                (void) shmem_unlink(new_dir, new_dentry);
                if (they_are_dirs) {