Merge tag 'irq-core-2021-02-15' of git://git.kernel.org/pub/scm/linux/kernel/git...
[linux-2.6-microblaze.git] / fs / xfs / xfs_inode.c
index b7352bc..636ac13 100644 (file)
@@ -775,6 +775,7 @@ xfs_init_new_inode(
        prid_t                  prid,
        struct xfs_inode        **ipp)
 {
+       struct inode            *dir = pip ? VFS_I(pip) : NULL;
        struct xfs_mount        *mp = tp->t_mountp;
        struct xfs_inode        *ip;
        unsigned int            flags;
@@ -804,18 +805,17 @@ xfs_init_new_inode(
 
        ASSERT(ip != NULL);
        inode = VFS_I(ip);
-       inode->i_mode = mode;
        set_nlink(inode, nlink);
-       inode->i_uid = current_fsuid();
        inode->i_rdev = rdev;
        ip->i_d.di_projid = prid;
 
-       if (pip && XFS_INHERIT_GID(pip)) {
-               inode->i_gid = VFS_I(pip)->i_gid;
-               if ((VFS_I(pip)->i_mode & S_ISGID) && S_ISDIR(mode))
-                       inode->i_mode |= S_ISGID;
+       if (dir && !(dir->i_mode & S_ISGID) &&
+           (mp->m_flags & XFS_MOUNT_GRPID)) {
+               inode->i_uid = current_fsuid();
+               inode->i_gid = dir->i_gid;
+               inode->i_mode = mode;
        } else {
-               inode->i_gid = current_fsgid();
+               inode_init_owner(inode, dir, mode);
        }
 
        /*
@@ -1022,23 +1022,22 @@ xfs_create(
         * the case we'll drop the one we have and get a more
         * appropriate transaction later.
         */
-       error = xfs_trans_alloc(mp, tres, resblks, 0, 0, &tp);
+       error = xfs_trans_alloc_icreate(mp, tres, udqp, gdqp, pdqp, resblks,
+                       &tp);
        if (error == -ENOSPC) {
                /* flush outstanding delalloc blocks and retry */
                xfs_flush_inodes(mp);
-               error = xfs_trans_alloc(mp, tres, resblks, 0, 0, &tp);
+               error = xfs_trans_alloc_icreate(mp, tres, udqp, gdqp, pdqp,
+                               resblks, &tp);
        }
        if (error)
-               goto out_release_inode;
+               goto out_release_dquots;
 
        xfs_ilock(dp, XFS_ILOCK_EXCL | XFS_ILOCK_PARENT);
        unlock_dp_on_error = true;
 
-       /*
-        * Reserve disk quota and the inode.
-        */
-       error = xfs_trans_reserve_quota(tp, mp, udqp, gdqp,
-                                               pdqp, resblks, 1, 0);
+       error = xfs_iext_count_may_overflow(dp, XFS_DATA_FORK,
+                       XFS_IEXT_DIR_MANIP_CNT(mp));
        if (error)
                goto out_trans_cancel;
 
@@ -1116,7 +1115,7 @@ xfs_create(
                xfs_finish_inode_setup(ip);
                xfs_irele(ip);
        }
-
+ out_release_dquots:
        xfs_qm_dqrele(udqp);
        xfs_qm_dqrele(gdqp);
        xfs_qm_dqrele(pdqp);
@@ -1160,14 +1159,10 @@ xfs_create_tmpfile(
        resblks = XFS_IALLOC_SPACE_RES(mp);
        tres = &M_RES(mp)->tr_create_tmpfile;
 
-       error = xfs_trans_alloc(mp, tres, resblks, 0, 0, &tp);
-       if (error)
-               goto out_release_inode;
-
-       error = xfs_trans_reserve_quota(tp, mp, udqp, gdqp,
-                                               pdqp, resblks, 1, 0);
+       error = xfs_trans_alloc_icreate(mp, tres, udqp, gdqp, pdqp, resblks,
+                       &tp);
        if (error)
-               goto out_trans_cancel;
+               goto out_release_dquots;
 
        error = xfs_dir_ialloc(&tp, dp, mode, 0, 0, prid, &ip);
        if (error)
@@ -1210,7 +1205,7 @@ xfs_create_tmpfile(
                xfs_finish_inode_setup(ip);
                xfs_irele(ip);
        }
-
+ out_release_dquots:
        xfs_qm_dqrele(udqp);
        xfs_qm_dqrele(gdqp);
        xfs_qm_dqrele(pdqp);
@@ -1258,6 +1253,11 @@ xfs_link(
        xfs_trans_ijoin(tp, sip, XFS_ILOCK_EXCL);
        xfs_trans_ijoin(tp, tdp, XFS_ILOCK_EXCL);
 
+       error = xfs_iext_count_may_overflow(tdp, XFS_DATA_FORK,
+                       XFS_IEXT_DIR_MANIP_CNT(mp));
+       if (error)
+               goto error_return;
+
        /*
         * If we are using project inheritance, we only allow hard link
         * creation in our tree when the project IDs are the same; else
@@ -3017,7 +3017,7 @@ xfs_rename(
        struct xfs_trans        *tp;
        struct xfs_inode        *wip = NULL;            /* whiteout inode */
        struct xfs_inode        *inodes[__XFS_SORT_INODES];
-       struct xfs_buf          *agibp;
+       int                     i;
        int                     num_inodes = __XFS_SORT_INODES;
        bool                    new_parent = (src_dp != target_dp);
        bool                    src_is_directory = S_ISDIR(VFS_I(src_ip)->i_mode);
@@ -3106,6 +3106,35 @@ xfs_rename(
        /*
         * Check for expected errors before we dirty the transaction
         * so we can return an error without a transaction abort.
+        *
+        * Extent count overflow check:
+        *
+        * From the perspective of src_dp, a rename operation is essentially a
+        * directory entry remove operation. Hence the only place where we check
+        * for extent count overflow for src_dp is in
+        * xfs_bmap_del_extent_real(). xfs_bmap_del_extent_real() returns
+        * -ENOSPC when it detects a possible extent count overflow and in
+        * response, the higher layers of directory handling code do the
+        * following:
+        * 1. Data/Free blocks: XFS lets these blocks linger until a
+        *    future remove operation removes them.
+        * 2. Dabtree blocks: XFS swaps the blocks with the last block in the
+        *    Leaf space and unmaps the last block.
+        *
+        * For target_dp, there are two cases depending on whether the
+        * destination directory entry exists or not.
+        *
+        * When destination directory entry does not exist (i.e. target_ip ==
+        * NULL), extent count overflow check is performed only when transaction
+        * has a non-zero sized space reservation associated with it.  With a
+        * zero-sized space reservation, XFS allows a rename operation to
+        * continue only when the directory has sufficient free space in its
+        * data/leaf/free space blocks to hold the new entry.
+        *
+        * When destination directory entry exists (i.e. target_ip != NULL), all
+        * we need to do is change the inode number associated with the already
+        * existing entry. Hence there is no need to perform an extent count
+        * overflow check.
         */
        if (target_ip == NULL) {
                /*
@@ -3116,6 +3145,12 @@ xfs_rename(
                        error = xfs_dir_canenter(tp, target_dp, target_name);
                        if (error)
                                goto out_trans_cancel;
+               } else {
+                       error = xfs_iext_count_may_overflow(target_dp,
+                                       XFS_DATA_FORK,
+                                       XFS_IEXT_DIR_MANIP_CNT(mp));
+                       if (error)
+                               goto out_trans_cancel;
                }
        } else {
                /*
@@ -3130,6 +3165,30 @@ xfs_rename(
                }
        }
 
+       /*
+        * Lock the AGI buffers we need to handle bumping the nlink of the
+        * whiteout inode off the unlinked list and to handle dropping the
+        * nlink of the target inode.  Per locking order rules, do this in
+        * increasing AG order and before directory block allocation tries to
+        * grab AGFs because we grab AGIs before AGFs.
+        *
+        * The (vfs) caller must ensure that if src is a directory then
+        * target_ip is either null or an empty directory.
+        */
+       for (i = 0; i < num_inodes && inodes[i] != NULL; i++) {
+               if (inodes[i] == wip ||
+                   (inodes[i] == target_ip &&
+                    (VFS_I(target_ip)->i_nlink == 1 || src_is_directory))) {
+                       struct xfs_buf  *bp;
+                       xfs_agnumber_t  agno;
+
+                       agno = XFS_INO_TO_AGNO(mp, inodes[i]->i_ino);
+                       error = xfs_read_agi(mp, tp, agno, &bp);
+                       if (error)
+                               goto out_trans_cancel;
+               }
+       }
+
        /*
         * Directory entry creation below may acquire the AGF. Remove
         * the whiteout from the unlinked list first to preserve correct
@@ -3182,22 +3241,6 @@ xfs_rename(
                 * In case there is already an entry with the same
                 * name at the destination directory, remove it first.
                 */
-
-               /*
-                * Check whether the replace operation will need to allocate
-                * blocks.  This happens when the shortform directory lacks
-                * space and we have to convert it to a block format directory.
-                * When more blocks are necessary, we must lock the AGI first
-                * to preserve locking order (AGI -> AGF).
-                */
-               if (xfs_dir2_sf_replace_needblock(target_dp, src_ip->i_ino)) {
-                       error = xfs_read_agi(mp, tp,
-                                       XFS_INO_TO_AGNO(mp, target_ip->i_ino),
-                                       &agibp);
-                       if (error)
-                               goto out_trans_cancel;
-               }
-
                error = xfs_dir_replace(tp, target_dp, target_name,
                                        src_ip->i_ino, spaceres);
                if (error)
@@ -3273,9 +3316,16 @@ xfs_rename(
        if (wip) {
                error = xfs_dir_replace(tp, src_dp, src_name, wip->i_ino,
                                        spaceres);
-       } else
+       } else {
+               /*
+                * NOTE: We don't need to check for extent count overflow here
+                * because the dir remove name code will leave the dir block in
+                * place if the extent count would overflow.
+                */
                error = xfs_dir_removename(tp, src_dp, src_name, src_ip->i_ino,
                                           spaceres);
+       }
+
        if (error)
                goto out_trans_cancel;