aboutsummaryrefslogtreecommitdiff
path: root/util
diff options
context:
space:
mode:
authorDavid Bremner <david@tethera.net>2026-01-25 07:56:35 +0900
committerDavid Bremner <david@tethera.net>2026-02-16 07:24:18 +0900
commitc8fcc953b5481e96d698c148325563890848c5ff (patch)
tree628fb661213369c6d1abc94da9b5d64289177689 /util
parentb222d18634e95b54010cec02e994d3cc96755746 (diff)
util: refactor sync_dir and mkdir_recursive
Moving these functions to libnotmuch_util will allow re-use from either multiple CLI compilation units or from the library. To avoid future surprises, replace printing to stderr with the usual status string mechanism.
Diffstat (limited to 'util')
-rw-r--r--util/path-util.c97
-rw-r--r--util/path-util.h9
2 files changed, 105 insertions, 1 deletions
diff --git a/util/path-util.c b/util/path-util.c
index 3267a967..5511c09b 100644
--- a/util/path-util.c
+++ b/util/path-util.c
@@ -5,10 +5,17 @@
#define _GNU_SOURCE
#include "path-util.h"
-
+#include "compat.h"
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
#include <limits.h>
#include <stdlib.h>
+#include <talloc.h>
char *
notmuch_canonicalize_file_name (const char *path)
@@ -25,3 +32,91 @@ notmuch_canonicalize_file_name (const char *path)
#error undefined PATH_MAX _and_ missing canonicalize_file_name not supported
#endif
}
+
+/* Call fsync() on a directory path. */
+notmuch_status_t
+sync_dir (const char *dir, char **status_string)
+{
+ int fd, r;
+
+ fd = open (dir, O_RDONLY);
+ if (fd == -1) {
+ if (status_string)
+ IGNORE_RESULT (asprintf (status_string,
+ "Error: open %s: %s\n", dir, strerror (errno)));
+ return NOTMUCH_STATUS_FILE_ERROR;
+ }
+
+ r = fsync (fd);
+ if (r && status_string)
+ IGNORE_RESULT (asprintf (status_string,
+ "Error: fsync %s: %s\n", dir, strerror (errno)));
+
+ close (fd);
+
+ return r == 0 ? NOTMUCH_STATUS_SUCCESS : NOTMUCH_STATUS_FILE_ERROR;
+}
+
+/*
+ * Make the given directory and its parents as necessary, using the
+ * given mode. Partial results are not cleaned up on errors.
+ */
+notmuch_status_t
+mkdir_recursive (const void *ctx, const char *path, int mode,
+ char **status_string)
+{
+ notmuch_status_t status;
+ struct stat st;
+ int r;
+ char *parent = NULL, *slash;
+
+ /* First check the common case: directory already exists. */
+ r = stat (path, &st);
+ if (r == 0) {
+ if (! S_ISDIR (st.st_mode)) {
+ if (status_string)
+ IGNORE_RESULT (asprintf (status_string, "Error: '%s' is not a directory: %s\n",
+ path, strerror (EEXIST)));
+ return NOTMUCH_STATUS_FILE_ERROR;
+ }
+
+ return NOTMUCH_STATUS_SUCCESS;
+ } else if (errno != ENOENT) {
+ if (status_string)
+ IGNORE_RESULT (asprintf (status_string,
+ "Error: stat '%s': %s\n", path, strerror (errno)));
+ return NOTMUCH_STATUS_FILE_ERROR;
+ }
+
+ /* mkdir parents, if any */
+ slash = strrchr (path, '/');
+ if (slash && slash != path) {
+ parent = talloc_strndup (ctx, path, slash - path);
+ if (! parent) {
+ if (status_string)
+ IGNORE_RESULT (asprintf (status_string,
+ "Error: %s\n",
+ strerror (ENOMEM)));
+ return NOTMUCH_STATUS_FILE_ERROR;
+ }
+
+ status = mkdir_recursive (ctx, parent, mode, status_string);
+ if (status)
+ return status;
+ }
+
+ if (mkdir (path, mode)) {
+ if (status_string)
+ IGNORE_RESULT (asprintf (status_string,
+ "Error: mkdir '%s': %s\n",
+ path, strerror (errno)));
+ return NOTMUCH_STATUS_FILE_ERROR;
+ }
+
+ if (parent) {
+ status = sync_dir (parent, status_string);
+ if (status)
+ return status;
+ }
+ return NOTMUCH_STATUS_SUCCESS;
+}
diff --git a/util/path-util.h b/util/path-util.h
index ac85f696..4cbbbf1d 100644
--- a/util/path-util.h
+++ b/util/path-util.h
@@ -5,6 +5,9 @@
#ifndef NOTMUCH_UTIL_PATH_UTIL_H_
#define NOTMUCH_UTIL_PATH_UTIL_H_
+/* For notmuch_status_t */
+#include "notmuch.h"
+
#ifdef __cplusplus
extern "C" {
#endif
@@ -12,6 +15,12 @@ extern "C" {
char *
notmuch_canonicalize_file_name (const char *path);
+notmuch_status_t
+mkdir_recursive (const void *ctx, const char *path, int mode, char **status_string);
+
+notmuch_status_t
+sync_dir (const char *path, char **status_string);
+
#ifdef __cplusplus
}
#endif