diff options
| author | David Bremner <david@tethera.net> | 2026-01-25 07:56:35 +0900 |
|---|---|---|
| committer | David Bremner <david@tethera.net> | 2026-02-16 07:24:18 +0900 |
| commit | c8fcc953b5481e96d698c148325563890848c5ff (patch) | |
| tree | 628fb661213369c6d1abc94da9b5d64289177689 /util | |
| parent | b222d18634e95b54010cec02e994d3cc96755746 (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.c | 97 | ||||
| -rw-r--r-- | util/path-util.h | 9 |
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 |
