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.c110
1 files changed, 110 insertions, 0 deletions
diff --git a/fsync.c b/fsync.c
new file mode 100644
index 0000000..994ca72
--- /dev/null
+++ b/fsync.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;
+}