[PATCH v2] erofs-utils: avoid flushing the image file on closing

Gao Xiang hsiangkao at linux.alibaba.com
Thu Sep 14 19:39:05 AEST 2023


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.

Signed-off-by: Gao Xiang <hsiangkao at linux.alibaba.com>
---
change since v1:
 - fix macOS build error:

 configure.ac |  2 ++
 lib/io.c     | 41 +++++++++++++++++++++++++++++++++++------
 2 files changed, 37 insertions(+), 6 deletions(-)

diff --git a/configure.ac b/configure.ac
index a8cecd0..51ace67 100644
--- a/configure.ac
+++ b/configure.ac
@@ -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
diff --git a/lib/io.c b/lib/io.c
index 1545436..602ac68 100644
--- 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;
-- 
2.39.3



More information about the Linux-erofs mailing list