Merge branch 'topic/kerneldoc' into for-next
[cascardo/linux.git] / fs / btrfs / ioctl.c
index 0fe1aa0..4399f0c 100644 (file)
@@ -713,6 +713,39 @@ static int create_snapshot(struct btrfs_root *root, struct inode *dir,
        if (ret)
                goto fail;
 
+       ret = btrfs_orphan_cleanup(pending_snapshot->snap);
+       if (ret)
+               goto fail;
+
+       /*
+        * If orphan cleanup did remove any orphans, it means the tree was
+        * modified and therefore the commit root is not the same as the
+        * current root anymore. This is a problem, because send uses the
+        * commit root and therefore can see inode items that don't exist
+        * in the current root anymore, and for example make calls to
+        * btrfs_iget, which will do tree lookups based on the current root
+        * and not on the commit root. Those lookups will fail, returning a
+        * -ESTALE error, and making send fail with that error. So make sure
+        * a send does not see any orphans we have just removed, and that it
+        * will see the same inodes regardless of whether a transaction
+        * commit happened before it started (meaning that the commit root
+        * will be the same as the current root) or not.
+        */
+       if (readonly && pending_snapshot->snap->node !=
+           pending_snapshot->snap->commit_root) {
+               trans = btrfs_join_transaction(pending_snapshot->snap);
+               if (IS_ERR(trans) && PTR_ERR(trans) != -ENOENT) {
+                       ret = PTR_ERR(trans);
+                       goto fail;
+               }
+               if (!IS_ERR(trans)) {
+                       ret = btrfs_commit_transaction(trans,
+                                                      pending_snapshot->snap);
+                       if (ret)
+                               goto fail;
+               }
+       }
+
        inode = btrfs_lookup_dentry(dentry->d_parent->d_inode, dentry);
        if (IS_ERR(inode)) {
                ret = PTR_ERR(inode);
@@ -732,23 +765,6 @@ out:
        return ret;
 }
 
-/*  copy of check_sticky in fs/namei.c()
-* It's inline, so penalty for filesystems that don't use sticky bit is
-* minimal.
-*/
-static inline int btrfs_check_sticky(struct inode *dir, struct inode *inode)
-{
-       kuid_t fsuid = current_fsuid();
-
-       if (!(dir->i_mode & S_ISVTX))
-               return 0;
-       if (uid_eq(inode->i_uid, fsuid))
-               return 0;
-       if (uid_eq(dir->i_uid, fsuid))
-               return 0;
-       return !capable(CAP_FOWNER);
-}
-
 /*  copy of may_delete in fs/namei.c()
  *     Check whether we can remove a link victim from directory dir, check
  *  whether the type of victim is right.
@@ -784,8 +800,7 @@ static int btrfs_may_delete(struct inode *dir, struct dentry *victim, int isdir)
                return error;
        if (IS_APPEND(dir))
                return -EPERM;
-       if (btrfs_check_sticky(dir, victim->d_inode)||
-               IS_APPEND(victim->d_inode)||
+       if (check_sticky(dir, victim->d_inode) || IS_APPEND(victim->d_inode) ||
            IS_IMMUTABLE(victim->d_inode) || IS_SWAPFILE(victim->d_inode))
                return -EPERM;
        if (isdir) {