about summary refs log tree commit homepage
path: root/fsync.c
diff options
context:
space:
mode:
Diffstat (limited to 'fsync.c')
-rw-r--r--fsync.c52
1 files changed, 49 insertions, 3 deletions
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;