]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
xfs: refactor xfs_file_fallocate
authorChristoph Hellwig <hch@lst.de>
Tue, 27 Aug 2024 06:50:50 +0000 (08:50 +0200)
committerChristian Brauner <brauner@kernel.org>
Wed, 28 Aug 2024 14:53:58 +0000 (16:53 +0200)
Refactor xfs_file_fallocate into separate helpers for each mode,
two factors for i_size handling and a single switch statement over the
supported modes.

Signed-off-by: Christoph Hellwig <hch@lst.de>
Link: https://lore.kernel.org/r/20240827065123.1762168-7-hch@lst.de
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Signed-off-by: Christian Brauner <brauner@kernel.org>
fs/xfs/xfs_file.c

index 489bc1b173c26835650503bd837636b46c483d82..f6e4912769a0d57692ff499a88e9bb228d9062fa 100644 (file)
@@ -852,6 +852,192 @@ static inline bool xfs_file_sync_writes(struct file *filp)
        return false;
 }
 
+static int
+xfs_falloc_newsize(
+       struct file             *file,
+       int                     mode,
+       loff_t                  offset,
+       loff_t                  len,
+       loff_t                  *new_size)
+{
+       struct inode            *inode = file_inode(file);
+
+       if ((mode & FALLOC_FL_KEEP_SIZE) || offset + len <= i_size_read(inode))
+               return 0;
+       *new_size = offset + len;
+       return inode_newsize_ok(inode, *new_size);
+}
+
+static int
+xfs_falloc_setsize(
+       struct file             *file,
+       loff_t                  new_size)
+{
+       struct iattr iattr = {
+               .ia_valid       = ATTR_SIZE,
+               .ia_size        = new_size,
+       };
+
+       if (!new_size)
+               return 0;
+       return xfs_vn_setattr_size(file_mnt_idmap(file), file_dentry(file),
+                       &iattr);
+}
+
+static int
+xfs_falloc_collapse_range(
+       struct file             *file,
+       loff_t                  offset,
+       loff_t                  len)
+{
+       struct inode            *inode = file_inode(file);
+       loff_t                  new_size = i_size_read(inode) - len;
+       int                     error;
+
+       if (!xfs_is_falloc_aligned(XFS_I(inode), offset, len))
+               return -EINVAL;
+
+       /*
+        * There is no need to overlap collapse range with EOF, in which case it
+        * is effectively a truncate operation
+        */
+       if (offset + len >= i_size_read(inode))
+               return -EINVAL;
+
+       error = xfs_collapse_file_space(XFS_I(inode), offset, len);
+       if (error)
+               return error;
+       return xfs_falloc_setsize(file, new_size);
+}
+
+static int
+xfs_falloc_insert_range(
+       struct file             *file,
+       loff_t                  offset,
+       loff_t                  len)
+{
+       struct inode            *inode = file_inode(file);
+       loff_t                  isize = i_size_read(inode);
+       int                     error;
+
+       if (!xfs_is_falloc_aligned(XFS_I(inode), offset, len))
+               return -EINVAL;
+
+       /*
+        * New inode size must not exceed ->s_maxbytes, accounting for
+        * possible signed overflow.
+        */
+       if (inode->i_sb->s_maxbytes - isize < len)
+               return -EFBIG;
+
+       /* Offset should be less than i_size */
+       if (offset >= isize)
+               return -EINVAL;
+
+       error = xfs_falloc_setsize(file, isize + len);
+       if (error)
+               return error;
+
+       /*
+        * Perform hole insertion now that the file size has been updated so
+        * that if we crash during the operation we don't leave shifted extents
+        * past EOF and hence losing access to the data that is contained within
+        * them.
+        */
+       return xfs_insert_file_space(XFS_I(inode), offset, len);
+}
+
+/*
+ * Punch a hole and prealloc the range.  We use a hole punch rather than
+ * unwritten extent conversion for two reasons:
+ *
+ *   1.) Hole punch handles partial block zeroing for us.
+ *   2.) If prealloc returns ENOSPC, the file range is still zero-valued by
+ *      virtue of the hole punch.
+ */
+static int
+xfs_falloc_zero_range(
+       struct file             *file,
+       int                     mode,
+       loff_t                  offset,
+       loff_t                  len)
+{
+       struct inode            *inode = file_inode(file);
+       unsigned int            blksize = i_blocksize(inode);
+       loff_t                  new_size = 0;
+       int                     error;
+
+       trace_xfs_zero_file_space(XFS_I(inode));
+
+       error = xfs_falloc_newsize(file, mode, offset, len, &new_size);
+       if (error)
+               return error;
+
+       error = xfs_free_file_space(XFS_I(inode), offset, len);
+       if (error)
+               return error;
+
+       len = round_up(offset + len, blksize) - round_down(offset, blksize);
+       offset = round_down(offset, blksize);
+       error = xfs_alloc_file_space(XFS_I(inode), offset, len);
+       if (error)
+               return error;
+       return xfs_falloc_setsize(file, new_size);
+}
+
+static int
+xfs_falloc_unshare_range(
+       struct file             *file,
+       int                     mode,
+       loff_t                  offset,
+       loff_t                  len)
+{
+       struct inode            *inode = file_inode(file);
+       loff_t                  new_size = 0;
+       int                     error;
+
+       error = xfs_falloc_newsize(file, mode, offset, len, &new_size);
+       if (error)
+               return error;
+
+       error = xfs_reflink_unshare(XFS_I(inode), offset, len);
+       if (error)
+               return error;
+
+       error = xfs_alloc_file_space(XFS_I(inode), offset, len);
+       if (error)
+               return error;
+       return xfs_falloc_setsize(file, new_size);
+}
+
+static int
+xfs_falloc_allocate_range(
+       struct file             *file,
+       int                     mode,
+       loff_t                  offset,
+       loff_t                  len)
+{
+       struct inode            *inode = file_inode(file);
+       loff_t                  new_size = 0;
+       int                     error;
+
+       /*
+        * If always_cow mode we can't use preallocations and thus should not
+        * create them.
+        */
+       if (xfs_is_always_cow_inode(XFS_I(inode)))
+               return -EOPNOTSUPP;
+
+       error = xfs_falloc_newsize(file, mode, offset, len, &new_size);
+       if (error)
+               return error;
+
+       error = xfs_alloc_file_space(XFS_I(inode), offset, len);
+       if (error)
+               return error;
+       return xfs_falloc_setsize(file, new_size);
+}
+
 #define        XFS_FALLOC_FL_SUPPORTED                                         \
                (FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE |           \
                 FALLOC_FL_COLLAPSE_RANGE | FALLOC_FL_ZERO_RANGE |      \
@@ -868,8 +1054,6 @@ xfs_file_fallocate(
        struct xfs_inode        *ip = XFS_I(inode);
        long                    error;
        uint                    iolock = XFS_IOLOCK_EXCL | XFS_MMAPLOCK_EXCL;
-       loff_t                  new_size = 0;
-       bool                    do_file_insert = false;
 
        if (!S_ISREG(inode->i_mode))
                return -EINVAL;
@@ -894,129 +1078,31 @@ xfs_file_fallocate(
        if (error)
                goto out_unlock;
 
-       if (mode & FALLOC_FL_PUNCH_HOLE) {
+       switch (mode & FALLOC_FL_MODE_MASK) {
+       case FALLOC_FL_PUNCH_HOLE:
                error = xfs_free_file_space(ip, offset, len);
-               if (error)
-                       goto out_unlock;
-       } else if (mode & FALLOC_FL_COLLAPSE_RANGE) {
-               if (!xfs_is_falloc_aligned(ip, offset, len)) {
-                       error = -EINVAL;
-                       goto out_unlock;
-               }
-
-               /*
-                * There is no need to overlap collapse range with EOF,
-                * in which case it is effectively a truncate operation
-                */
-               if (offset + len >= i_size_read(inode)) {
-                       error = -EINVAL;
-                       goto out_unlock;
-               }
-
-               new_size = i_size_read(inode) - len;
-
-               error = xfs_collapse_file_space(ip, offset, len);
-               if (error)
-                       goto out_unlock;
-       } else if (mode & FALLOC_FL_INSERT_RANGE) {
-               loff_t          isize = i_size_read(inode);
-
-               if (!xfs_is_falloc_aligned(ip, offset, len)) {
-                       error = -EINVAL;
-                       goto out_unlock;
-               }
-
-               /*
-                * New inode size must not exceed ->s_maxbytes, accounting for
-                * possible signed overflow.
-                */
-               if (inode->i_sb->s_maxbytes - isize < len) {
-                       error = -EFBIG;
-                       goto out_unlock;
-               }
-               new_size = isize + len;
-
-               /* Offset should be less than i_size */
-               if (offset >= isize) {
-                       error = -EINVAL;
-                       goto out_unlock;
-               }
-               do_file_insert = true;
-       } else {
-               if (!(mode & FALLOC_FL_KEEP_SIZE) &&
-                   offset + len > i_size_read(inode)) {
-                       new_size = offset + len;
-                       error = inode_newsize_ok(inode, new_size);
-                       if (error)
-                               goto out_unlock;
-               }
-
-               if (mode & FALLOC_FL_ZERO_RANGE) {
-                       /*
-                        * Punch a hole and prealloc the range.  We use a hole
-                        * punch rather than unwritten extent conversion for two
-                        * reasons:
-                        *
-                        *   1.) Hole punch handles partial block zeroing for us.
-                        *   2.) If prealloc returns ENOSPC, the file range is
-                        *       still zero-valued by virtue of the hole punch.
-                        */
-                       unsigned int blksize = i_blocksize(inode);
-
-                       trace_xfs_zero_file_space(ip);
-
-                       error = xfs_free_file_space(ip, offset, len);
-                       if (error)
-                               goto out_unlock;
-
-                       len = round_up(offset + len, blksize) -
-                             round_down(offset, blksize);
-                       offset = round_down(offset, blksize);
-               } else if (mode & FALLOC_FL_UNSHARE_RANGE) {
-                       error = xfs_reflink_unshare(ip, offset, len);
-                       if (error)
-                               goto out_unlock;
-               } else {
-                       /*
-                        * If always_cow mode we can't use preallocations and
-                        * thus should not create them.
-                        */
-                       if (xfs_is_always_cow_inode(ip)) {
-                               error = -EOPNOTSUPP;
-                               goto out_unlock;
-                       }
-               }
-
-               error = xfs_alloc_file_space(ip, offset, len);
-               if (error)
-                       goto out_unlock;
-       }
-
-       /* Change file size if needed */
-       if (new_size) {
-               struct iattr iattr;
-
-               iattr.ia_valid = ATTR_SIZE;
-               iattr.ia_size = new_size;
-               error = xfs_vn_setattr_size(file_mnt_idmap(file),
-                                           file_dentry(file), &iattr);
-               if (error)
-                       goto out_unlock;
-       }
-
-       /*
-        * Perform hole insertion now that the file size has been
-        * updated so that if we crash during the operation we don't
-        * leave shifted extents past EOF and hence losing access to
-        * the data that is contained within them.
-        */
-       if (do_file_insert) {
-               error = xfs_insert_file_space(ip, offset, len);
-               if (error)
-                       goto out_unlock;
+               break;
+       case FALLOC_FL_COLLAPSE_RANGE:
+               error = xfs_falloc_collapse_range(file, offset, len);
+               break;
+       case FALLOC_FL_INSERT_RANGE:
+               error = xfs_falloc_insert_range(file, offset, len);
+               break;
+       case FALLOC_FL_ZERO_RANGE:
+               error = xfs_falloc_zero_range(file, mode, offset, len);
+               break;
+       case FALLOC_FL_UNSHARE_RANGE:
+               error = xfs_falloc_unshare_range(file, mode, offset, len);
+               break;
+       case FALLOC_FL_ALLOCATE_RANGE:
+               error = xfs_falloc_allocate_range(file, mode, offset, len);
+               break;
+       default:
+               error = -EOPNOTSUPP;
+               break;
        }
 
-       if (xfs_file_sync_writes(file))
+       if (!error && xfs_file_sync_writes(file))
                error = xfs_log_force_inode(ip);
 
 out_unlock: