fs/ntfs3: Enable FALLOC_FL_INSERT_RANGE
authorKonstantin Komarov <almaz.alexandrovich@paragon-software.com>
Tue, 21 Jun 2022 09:49:52 +0000 (12:49 +0300)
committerKonstantin Komarov <almaz.alexandrovich@paragon-software.com>
Tue, 28 Jun 2022 15:51:12 +0000 (18:51 +0300)
Changed logic in ntfs_fallocate - more clear checks in beginning
instead of the middle of function and added FALLOC_FL_INSERT_RANGE.
Fixes xfstest generic/064
Fixes: 4342306f0f0d ("fs/ntfs3: Add file operations and implementation")
Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
fs/ntfs3/file.c

index 27c32692513c45fac8db079176e17d0a6cf09b4a..bdffe4b8554bd18cd684b05af2ef71b782496bac 100644 (file)
@@ -533,21 +533,35 @@ static int ntfs_truncate(struct inode *inode, loff_t new_size)
 static long ntfs_fallocate(struct file *file, int mode, loff_t vbo, loff_t len)
 {
        struct inode *inode = file->f_mapping->host;
+       struct address_space *mapping = inode->i_mapping;
        struct super_block *sb = inode->i_sb;
        struct ntfs_sb_info *sbi = sb->s_fs_info;
        struct ntfs_inode *ni = ntfs_i(inode);
        loff_t end = vbo + len;
        loff_t vbo_down = round_down(vbo, PAGE_SIZE);
-       loff_t i_size;
+       bool is_supported_holes = is_sparsed(ni) || is_compressed(ni);
+       loff_t i_size, new_size;
+       bool map_locked;
        int err;
 
        /* No support for dir. */
        if (!S_ISREG(inode->i_mode))
                return -EOPNOTSUPP;
 
-       /* Return error if mode is not supported. */
-       if (mode & ~(FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE |
-                    FALLOC_FL_COLLAPSE_RANGE)) {
+       /*
+        * vfs_fallocate checks all possible combinations of mode.
+        * Do additional checks here before ntfs_set_state(dirty).
+        */
+       if (mode & FALLOC_FL_PUNCH_HOLE) {
+               if (!is_supported_holes)
+                       return -EOPNOTSUPP;
+       } else if (mode & FALLOC_FL_COLLAPSE_RANGE) {
+       } else if (mode & FALLOC_FL_INSERT_RANGE) {
+               if (!is_supported_holes)
+                       return -EOPNOTSUPP;
+       } else if (mode &
+                  ~(FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE |
+                    FALLOC_FL_COLLAPSE_RANGE | FALLOC_FL_INSERT_RANGE)) {
                ntfs_inode_warn(inode, "fallocate(0x%x) is not supported",
                                mode);
                return -EOPNOTSUPP;
@@ -557,6 +571,8 @@ static long ntfs_fallocate(struct file *file, int mode, loff_t vbo, loff_t len)
 
        inode_lock(inode);
        i_size = inode->i_size;
+       new_size = max(end, i_size);
+       map_locked = false;
 
        if (WARN_ON(ni->ni_flags & NI_FLAG_COMPRESSED_MASK)) {
                /* Should never be here, see ntfs_file_open. */
@@ -564,38 +580,27 @@ static long ntfs_fallocate(struct file *file, int mode, loff_t vbo, loff_t len)
                goto out;
        }
 
+       if (mode & (FALLOC_FL_PUNCH_HOLE | FALLOC_FL_COLLAPSE_RANGE |
+                   FALLOC_FL_INSERT_RANGE)) {
+               inode_dio_wait(inode);
+               filemap_invalidate_lock(mapping);
+               map_locked = true;
+       }
+
        if (mode & FALLOC_FL_PUNCH_HOLE) {
                u32 frame_size;
                loff_t mask, vbo_a, end_a, tmp;
 
-               if (!(mode & FALLOC_FL_KEEP_SIZE)) {
-                       err = -EINVAL;
-                       goto out;
-               }
-
-               err = filemap_write_and_wait_range(inode->i_mapping, vbo,
-                                                  end - 1);
+               err = filemap_write_and_wait_range(mapping, vbo, end - 1);
                if (err)
                        goto out;
 
-               err = filemap_write_and_wait_range(inode->i_mapping, end,
-                                                  LLONG_MAX);
+               err = filemap_write_and_wait_range(mapping, end, LLONG_MAX);
                if (err)
                        goto out;
 
-               inode_dio_wait(inode);
-
                truncate_pagecache(inode, vbo_down);
 
-               if (!is_sparsed(ni) && !is_compressed(ni)) {
-                       /*
-                        * Normal file, can't make hole.
-                        * TODO: Try to find way to save info about hole.
-                        */
-                       err = -EOPNOTSUPP;
-                       goto out;
-               }
-
                ni_lock(ni);
                err = attr_punch_hole(ni, vbo, len, &frame_size);
                ni_unlock(ni);
@@ -627,17 +632,11 @@ static long ntfs_fallocate(struct file *file, int mode, loff_t vbo, loff_t len)
                        ni_unlock(ni);
                }
        } else if (mode & FALLOC_FL_COLLAPSE_RANGE) {
-               if (mode & ~FALLOC_FL_COLLAPSE_RANGE) {
-                       err = -EINVAL;
-                       goto out;
-               }
-
                /*
                 * Write tail of the last page before removed range since
                 * it will get removed from the page cache below.
                 */
-               err = filemap_write_and_wait_range(inode->i_mapping, vbo_down,
-                                                  vbo);
+               err = filemap_write_and_wait_range(mapping, vbo_down, vbo);
                if (err)
                        goto out;
 
@@ -645,34 +644,45 @@ static long ntfs_fallocate(struct file *file, int mode, loff_t vbo, loff_t len)
                 * Write data that will be shifted to preserve them
                 * when discarding page cache below.
                 */
-               err = filemap_write_and_wait_range(inode->i_mapping, end,
-                                                  LLONG_MAX);
+               err = filemap_write_and_wait_range(mapping, end, LLONG_MAX);
                if (err)
                        goto out;
 
-               /* Wait for existing dio to complete. */
-               inode_dio_wait(inode);
-
                truncate_pagecache(inode, vbo_down);
 
                ni_lock(ni);
                err = attr_collapse_range(ni, vbo, len);
                ni_unlock(ni);
-       } else {
-               /*
-                * Normal file: Allocate clusters, do not change 'valid' size.
-                */
-               loff_t new_size = max(end, i_size);
+       } else if (mode & FALLOC_FL_INSERT_RANGE) {
+               /* Check new size. */
+               err = inode_newsize_ok(inode, new_size);
+               if (err)
+                       goto out;
+
+               /* Write out all dirty pages. */
+               err = filemap_write_and_wait_range(mapping, vbo_down,
+                                                  LLONG_MAX);
+               if (err)
+                       goto out;
+               truncate_pagecache(inode, vbo_down);
 
+               ni_lock(ni);
+               err = attr_insert_range(ni, vbo, len);
+               ni_unlock(ni);
+       } else {
+               /* Check new size. */
                err = inode_newsize_ok(inode, new_size);
                if (err)
                        goto out;
 
+               /*
+                * Allocate clusters, do not change 'valid' size.
+                */
                err = ntfs_set_size(inode, new_size);
                if (err)
                        goto out;
 
-               if (is_sparsed(ni) || is_compressed(ni)) {
+               if (is_supported_holes) {
                        CLST vcn_v = ni->i_valid >> sbi->cluster_bits;
                        CLST vcn = vbo >> sbi->cluster_bits;
                        CLST cend = bytes_to_cluster(sbi, end);
@@ -720,6 +730,9 @@ static long ntfs_fallocate(struct file *file, int mode, loff_t vbo, loff_t len)
        }
 
 out:
+       if (map_locked)
+               filemap_invalidate_unlock(mapping);
+
        if (err == -EFBIG)
                err = -ENOSPC;