about summary refs log tree commit homepage
diff options
context:
space:
mode:
authorEric Wong <normalperson@yhbt.net>2009-08-09 15:33:48 -0700
committerEric Wong <normalperson@yhbt.net>2009-08-09 15:36:25 -0700
commitd4581cb9a1d178cb31416eb30d501a0e46878e45 (patch)
tree77e141f88b76eec83b368aba5222455077356c9b
parent8947b985335cbf9b30cffb9e79f9ed7a1899e298 (diff)
downloadpcu-d4581cb9a1d178cb31416eb30d501a0e46878e45.tar.gz
Add `pcu-sync' command
This can be useful for calling fsync(2)/fdatasync(2) on
individual files (and/or their containing directories).  This
can be used when a system-wide sync(2) is too expensive on
a busy system.
-rw-r--r--.gitignore1
-rw-r--r--Makefile3
-rw-r--r--sync.c110
3 files changed, 113 insertions, 1 deletions
diff --git a/.gitignore b/.gitignore
index 4b4583d..e4a8a6f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,6 @@
 pcu-mincore
 pcu-fadvise
+pcu-sync
 GIT-VERSION-FILE
 *.1
 *.html
diff --git a/Makefile b/Makefile
index 7871952..5d8a09e 100644
--- a/Makefile
+++ b/Makefile
@@ -22,8 +22,9 @@ LDFLAGS = -Wl,-O1
 
 pcu-mincore: mincore.c compat-util.h
 pcu-fadvise: fadvise.c compat-util.h
+pcu-sync: sync.c compat-util.h
 
-PCU_BIN := pcu-fadvise pcu-mincore
+PCU_BIN := pcu-fadvise pcu-mincore pcu-sync
 
 $(PCU_BIN):
         $(CC) $(CFLAGS) $(LDFLAGS) -o $@+ $<
diff --git a/sync.c b/sync.c
new file mode 100644
index 0000000..994ca72
--- /dev/null
+++ b/sync.c
@@ -0,0 +1,110 @@
+#include "compat-util.h"
+#include <dirent.h>
+#include <libgen.h>
+
+#if defined(_POSIX_SYNCHRONIZED_IO) && _POSIX_SYNCHRONIZED_IO > 0
+static int have_fdatasync = 1;
+#else
+static int have_fdatasync;
+#endif
+
+/* TODO: sync_file_range() if on Linux */
+
+static int usage(const char * argv0)
+{
+        fprintf(stderr, "Usage: %s [-d] [-D] FILE...\n", argv0);
+        return 1;
+}
+
+static int do_sync(const char *path, int data_only, int directory)
+{
+        int fd;
+        const char *errfunc = "";
+
+        if ((fd = open(path, O_RDWR|O_NOATIME)) < 0) {
+                if (errno == EISDIR) {
+                        directory = 1;
+                        goto sync_dir;
+                }
+                errfunc = "open";
+                goto err;
+        }
+
+        if (data_only && have_fdatasync) {
+                if (fdatasync(fd) < 0) {
+                        errfunc = "fdatasync";
+                        goto err;
+                }
+        } else {
+                if (fsync(fd) < 0) {
+                        errfunc = "fsync";
+                        goto err;
+                }
+        }
+
+        if (close(fd) < 0) {
+                errfunc = "close";
+                goto err;
+        }
+
+sync_dir:
+        if (directory) {
+                char *dpath;
+                DIR *dir;
+
+                if (!(dpath = strdup(path))){
+                        errfunc = "strdup";
+                        goto err;
+                }
+                if (!(dir = opendir(dirname(dpath)))) {
+                        errfunc = "opendir";
+                        goto err;
+                }
+                if (fsync(dirfd(dir)) < 0) {
+                        errfunc = "(directory) fsync";
+                        goto err;
+                }
+                if (closedir(dir) < 0) {
+                        errfunc = "closedir";
+                        goto err;
+                }
+                free(dpath);
+        }
+
+        return 0;
+err:
+        fprintf(stderr, "%s: %s(): %s\n", path, errfunc, strerror(errno));
+        return -1;
+}
+
+int main(int argc, char * const argv[])
+{
+        int data_only = 0;
+        int directory = 0;
+        int opt;
+        int argi = 1;
+
+        while ((opt = getopt(argc, argv, "dD")) != -1) {
+                ++argi;
+                switch(opt) {
+                case 'd':
+                        data_only = 1;
+                        break;
+                case 'D':
+                        directory = 1;
+                        break;
+                default:
+                        return usage(argv[0]);
+                }
+        }
+
+        if (argi >= argc)
+                return usage(argv[0]);
+
+        for (; argi < argc; ++argi) {
+                if (do_sync(argv[argi], data_only, directory) < 0)
+                        return 1;
+        }
+
+        return 0;
+}