about summary refs log tree commit homepage
diff options
context:
space:
mode:
authorEric Wong <normalperson@yhbt.net>2012-11-15 03:42:50 +0000
committerEric Wong <normalperson@yhbt.net>2012-11-15 04:05:53 +0000
commite11a55dee1f5ad36bfb6f5a9b2484ef63522df41 (patch)
tree880893a4d9471490910de994d7eebb733a51fcac
parent1567a0746ca0083fe43c95a725a69228929ff9a3 (diff)
downloadpcu-e11a55dee1f5ad36bfb6f5a9b2484ef63522df41.tar.gz
pcu-fsync: add support for syncfs() on Linux (-f flag)
Sometimes it is useful to just commit a single filesystem.
This transparently falls back to using sync(2) if syncfs(2)
is not available.
-rw-r--r--Makefile2
-rw-r--r--compat-util.h1
-rw-r--r--fsync.c52
-rw-r--r--pcu-fsync.1.txt7
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.