erofs-utils: avoid flushing the image file on closing
authorGao Xiang <hsiangkao@linux.alibaba.com>
Thu, 14 Sep 2023 09:39:05 +0000 (17:39 +0800)
committerGao Xiang <hsiangkao@linux.alibaba.com>
Thu, 14 Sep 2023 09:45:01 +0000 (17:45 +0800)
Traditionally, truncating to small sizes will trigger some
flush-on-close semantics to avoid the notorious NULL files.

I'm not sure if it's our use case since:
  1) we're creating new image files instead of reusing old ones;
  2) it kills end-to-end performance in practice;
  3) other programs like GNU TAR doesn't work as this too for
     such meaningless comparsion;

Let's work around it now.

Link: https://lore.kernel.org/r/20230914093905.1600316-1-hsiangkao@linux.alibaba.com
Signed-off-by: Gao Xiang <hsiangkao@linux.alibaba.com>
configure.ac
lib/io.c

index a8cecd08832e141f287f29c748fcaf2d520fbd8c..51ace67069df4e87c75a77b3d78533b18f511693 100644 (file)
@@ -190,6 +190,7 @@ AC_CHECK_HEADERS(m4_flatten([
        sys/mman.h
        sys/random.h
        sys/stat.h
+       sys/statfs.h
        sys/sysmacros.h
        sys/time.h
        unistd.h
@@ -249,6 +250,7 @@ AC_CHECK_FUNCS(m4_flatten([
        ftello64
        pread64
        pwrite64
+       fstatfs
        strdup
        strerror
        strrchr
index 1545436aee998742263ce0d5e4eb327bc75e2851..602ac68fe03d5451f4eec4119b47bd8d8209fdd1 100644 (file)
--- a/lib/io.c
+++ b/lib/io.c
@@ -20,7 +20,9 @@
 #ifdef HAVE_LINUX_FALLOC_H
 #include <linux/falloc.h>
 #endif
-
+#ifdef HAVE_SYS_STATFS_H
+#include <sys/statfs.h>
+#endif
 #define EROFS_MODNAME  "erofs_io"
 #include "erofs/print.h"
 
@@ -58,6 +60,11 @@ int dev_open(struct erofs_sb_info *sbi, const char *dev)
        struct stat st;
        int fd, ret;
 
+#if defined(HAVE_SYS_STATFS_H) && defined(HAVE_FSTATFS)
+       bool again = false;
+
+repeat:
+#endif
        fd = open(dev, O_RDWR | O_CREAT | O_BINARY, 0644);
        if (fd < 0) {
                erofs_err("failed to open(%s).", dev);
@@ -82,11 +89,33 @@ int dev_open(struct erofs_sb_info *sbi, const char *dev)
                sbi->devsz = round_down(sbi->devsz, erofs_blksiz(sbi));
                break;
        case S_IFREG:
-               ret = ftruncate(fd, 0);
-               if (ret) {
-                       erofs_err("failed to ftruncate(%s).", dev);
-                       close(fd);
-                       return -errno;
+               if (st.st_size) {
+#if defined(HAVE_SYS_STATFS_H) && defined(HAVE_FSTATFS)
+                       struct statfs stfs;
+
+                       if (again)
+                               return -ENOTEMPTY;
+
+                       /*
+                        * fses like EXT4 and BTRFS will flush dirty blocks
+                        * after truncate(0) even after the writeback happens
+                        * (see kernel commit 7d8f9f7d150d and ccd2506bd431),
+                        * which is NOT our intention.  Let's work around this.
+                        */
+                       if (!fstatfs(fd, &stfs) && (stfs.f_type == 0xEF53 ||
+                                       stfs.f_type == 0x9123683E)) {
+                               close(fd);
+                               unlink(dev);
+                               again = true;
+                               goto repeat;
+                       }
+#endif
+                       ret = ftruncate(fd, 0);
+                       if (ret) {
+                               erofs_err("failed to ftruncate(%s).", dev);
+                               close(fd);
+                               return -errno;
+                       }
                }
                /* INT64_MAX is the limit of kernel vfs */
                sbi->devsz = INT64_MAX;