Whamcloud - gitweb
fuse2fs: fix post-EOF preallocation clearing on truncation
authorDarrick J. Wong <djwong@kernel.org>
Wed, 21 May 2025 22:41:43 +0000 (15:41 -0700)
committerTheodore Ts'o <tytso@mit.edu>
Fri, 23 May 2025 13:41:20 +0000 (09:41 -0400)
generic/092 shows that truncating a file to its current size does not
clean out post-eof preallocations like the kernel does.  Adopt the
kernel's behavior for consistency.

Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Link: https://lore.kernel.org/r/174786678014.1383760.1321101978729586160.stgit@frogsfrogsfrogs
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
misc/fuse2fs.c

index 2d4a108..e4b06b3 100644 (file)
@@ -2114,9 +2114,30 @@ out:
        return ret;
 }
 
-static int truncate_helper(ext2_filsys fs, ext2_ino_t ino, off_t new_size)
+static int punch_posteof(struct fuse2fs *ff, ext2_ino_t ino, off_t new_size)
 {
+       ext2_filsys fs = ff->fs;
+       struct ext2_inode_large inode;
+       blk64_t truncate_block = (new_size + fs->blocksize - 1) / fs->blocksize;
+       errcode_t err;
+
+       err = fuse2fs_read_inode(fs, ino, &inode);
+       if (err)
+               return translate_error(fs, ino, err);
+
+       err = ext2fs_punch(fs, ino, EXT2_INODE(&inode), 0, truncate_block,
+                          ~0ULL);
+       if (err)
+               return translate_error(fs, ino, err);
+
+       return 0;
+}
+
+static int truncate_helper(struct fuse2fs *ff, ext2_ino_t ino, off_t new_size)
+{
+       ext2_filsys fs = ff->fs;
        ext2_file_t file;
+       __u64 old_isize;
        errcode_t err;
        int ret = 0;
 
@@ -2124,17 +2145,40 @@ static int truncate_helper(ext2_filsys fs, ext2_ino_t ino, off_t new_size)
        if (err)
                return translate_error(fs, ino, err);
 
+       err = ext2fs_file_get_lsize(file, &old_isize);
+       if (err) {
+               ret = translate_error(fs, ino, err);
+               goto out_close;
+       }
+
+       dbg_printf(ff, "%s: ino=%u isize=0x%llx new_size=0x%llx\n", __func__,
+                  ino,
+                  (unsigned long long)old_isize,
+                  (unsigned long long)new_size);
+
        err = ext2fs_file_set_size2(file, new_size);
        if (err)
                ret = translate_error(fs, ino, err);
 
+out_close:
        err = ext2fs_file_close(file);
        if (ret)
                return ret;
        if (err)
                return translate_error(fs, ino, err);
 
-       return update_mtime(fs, ino, NULL);
+       ret = update_mtime(fs, ino, NULL);
+       if (ret)
+               return ret;
+
+       /*
+        * Truncating to the current size is usually understood to mean that
+        * we should clear out post-EOF preallocations.
+        */
+       if (new_size == old_isize)
+               return punch_posteof(ff, ino, new_size);
+
+       return 0;
 }
 
 static int op_truncate(const char *path, off_t len
@@ -2168,7 +2212,7 @@ static int op_truncate(const char *path, off_t len
        if (ret)
                goto out;
 
-       ret = truncate_helper(fs, ino, len);
+       ret = truncate_helper(ff, ino, len);
        if (ret)
                goto out;
 
@@ -2268,7 +2312,7 @@ static int __op_open(struct fuse2fs *ff, const char *path,
        }
 
        if (fp->flags & O_TRUNC) {
-               ret = truncate_helper(fs, file->ino, 0);
+               ret = truncate_helper(ff, file->ino, 0);
                if (ret)
                        goto out;
        }