pcu-fsync: add support for syncfs() on Linux (-f flag)
authorEric Wong <normalperson@yhbt.net>
Thu, 15 Nov 2012 03:42:50 +0000 (15 03:42 +0000)
committerEric Wong <normalperson@yhbt.net>
Thu, 15 Nov 2012 04:05:53 +0000 (15 04:05 +0000)
Sometimes it is useful to just commit a single filesystem.
This transparently falls back to using sync(2) if syncfs(2)
is not available.

Makefile
compat-util.h
fsync.c
pcu-fsync.1.txt

index 0027fdd..b7d4ede 100644 (file)
--- 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 $@+ $@
index 413dab7..d3520e1 100644 (file)
@@ -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 (file)
--- 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;
index 958f0d3..3cafe08 100644 (file)
@@ -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.