librelist archives

« back to archive

[PATCH] pcu-fsync: add support for syncfs() on Linux (-f flag)

[PATCH] pcu-fsync: add support for syncfs() on Linux (-f flag)

From:
Eric Wong
Date:
2012-11-15 @ 04:07
Sometimes it is useful to just commit a single filesystem.
This transparently falls back to using sync(2) if syncfs(2)
is not available.
---
 Pushed to master of git://bogomips.org/pcu.git
 (commit e11a55dee1f5ad36bfb6f5a9b2484ef63522df41)

 Makefile        |  2 ++
 compat-util.h   |  1 +
 fsync.c         | 52 +++++++++++++++++++++++++++++++++++++++++++++++++---
 pcu-fsync.1.txt |  7 ++++++-
 4 files changed, 58 insertions(+), 4 deletions(-)

diff --git a/Makefile b/Makefile
index 0027fdd..b7d4ede 100644
--- a/Makefile
+++ b/Makefile
@@ -26,6 +26,8 @@ pcu-fsync: fsync.c compat-util.h
 
 PCU_BIN := pcu-fadvise pcu-mincore pcu-fsync
 
+pcu-fsync: LDFLAGS += -ldl
+
 $(PCU_BIN):
 	$(CC) $(CFLAGS) $(LDFLAGS) -o $@+ $<
 	mv $@+ $@
diff --git a/compat-util.h b/compat-util.h
index 413dab7..d3520e1 100644
--- a/compat-util.h
+++ b/compat-util.h
@@ -1,6 +1,7 @@
 #ifndef OS_COMPAT_H
 #define OS_COMPAT_H
 
+#define _GNU_SOURCE
 #define _LARGE_FILES
 #define _FILE_OFFSET_BITS 64
 #define _BSD_SOURCE /* for mincore */
diff --git a/fsync.c b/fsync.c
index 994ca72..ebf039f 100644
--- a/fsync.c
+++ b/fsync.c
@@ -1,6 +1,7 @@
 #include "compat-util.h"
 #include <dirent.h>
 #include <libgen.h>
+#include <dlfcn.h>
 
 #if defined(_POSIX_SYNCHRONIZED_IO) && _POSIX_SYNCHRONIZED_IO > 0
 static int have_fdatasync = 1;
@@ -16,6 +17,42 @@ static int usage(const char * argv0)
 	return 1;
 }
 
+#define FN_NOT_FOUND ((void *)(1))
+
+static int fs_sync(const char *path)
+{
+	int rc = 0;
+#if defined(__linux__) && defined(RTLD_NEXT)
+	static int (*syncfs_fn)(int);
+
+	if (syncfs_fn == NULL) {
+		syncfs_fn = dlsym(RTLD_DEFAULT, "syncfs");
+		if (syncfs_fn == NULL || dlerror())
+			syncfs_fn = FN_NOT_FOUND;
+	}
+	if (syncfs_fn != NULL && syncfs_fn != FN_NOT_FOUND) {
+		int fd = open(path, O_RDONLY|O_NOATIME);
+
+		if (fd >= 0) {
+			int err;
+			rc = syncfs_fn(fd);
+			err = errno;
+			close(fd);
+
+			/*
+			 * if glibc has syncfs(2) but we're running an
+			 * old kernel, fall back to sync(2) below
+			 */
+			if (err != ENOSYS)
+				return rc;
+			rc = 0;
+		}
+	}
+#endif /* ! __linux__ */
+	sync();
+	return rc;
+}
+
 static int do_sync(const char *path, int data_only, int directory)
 {
 	int fd;
@@ -80,11 +117,12 @@ err:
 int main(int argc, char * const argv[])
 {
 	int data_only = 0;
+	int fs = 0;
 	int directory = 0;
 	int opt;
 	int argi = 1;
 
-	while ((opt = getopt(argc, argv, "dD")) != -1) {
+	while ((opt = getopt(argc, argv, "dDf")) != -1) {
 		++argi;
 		switch(opt) {
 		case 'd':
@@ -93,6 +131,9 @@ int main(int argc, char * const argv[])
 		case 'D':
 			directory = 1;
 			break;
+		case 'f':
+			fs = 1;
+			break;
 		default:
 			return usage(argv[0]);
 		}
@@ -102,8 +143,13 @@ int main(int argc, char * const argv[])
 		return usage(argv[0]);
 
 	for (; argi < argc; ++argi) {
-		if (do_sync(argv[argi], data_only, directory) < 0)
-			return 1;
+		if (fs) {
+			if (fs_sync(argv[argi]) < 0)
+				return 1;
+		} else {
+			if (do_sync(argv[argi], data_only, directory) < 0)
+				return 1;
+		}
 	}
 
 	return 0;
diff --git a/pcu-fsync.1.txt b/pcu-fsync.1.txt
index 958f0d3..3cafe08 100644
--- a/pcu-fsync.1.txt
+++ b/pcu-fsync.1.txt
@@ -6,7 +6,7 @@
 pcu-fsync - synchronizes a files in-core state with storage device
 
 # SYNOPSIS
-pcu-fsync [-D] [-d] FILE...
+pcu-fsync [-D] [-d] [-f] FILE...
 
 # DESCRIPTION
 A command-line interface to the fsync(2) and fdatasync(2) system calls
@@ -25,6 +25,11 @@ using applications that fail to explicitly do so.
     special cases where applications do not require the performance
     overhead of flushing metadata to the storage device.
 
+-f
+:   Flush data on the filesystem containing the specified file
+    (or directory).  This uses the syncfs(2) syscall under Linux,
+    and falls back to sync(2) if unavailable.
+
 # OUTPUT
 Errors only.
 
-- 
Eric Wong