Add `pcu-sync' command
authorEric Wong <normalperson@yhbt.net>
Sun, 9 Aug 2009 22:33:48 +0000 (9 15:33 -0700)
committerEric Wong <normalperson@yhbt.net>
Sun, 9 Aug 2009 22:36:25 +0000 (9 15:36 -0700)
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.

.gitignore
Makefile
sync.c [new file with mode: 0644]

index 4b4583d..e4a8a6f 100644 (file)
@@ -1,5 +1,6 @@
 pcu-mincore
 pcu-fadvise
+pcu-sync
 GIT-VERSION-FILE
 *.1
 *.html
index 7871952..5d8a09e 100644 (file)
--- 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 (file)
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;
+}