When dry running, mbsync should not be altering the filesystem by
creating directories. However, mkdir() and mkdir_p() were being called
on both dry and non-dry runs, creating directories in both situations.
Ensure mkdir_p() instead just calls mkdir_p_check() on dry runs.
mkdir_p_check() attempts to determine whether mkdir_p() would succeed or
fail and returns appropriately, thereby simulating a call to mkdir_p()
without modifying the filesystem.
Where appropriate, reroute any calls to mkdir() to mkdir_p() so that the
simulated directory creation is also used at those calls. With
'appropriate' meaning the parent directory of the wanted new directory
is expected to exist prior to the call.
---
src/drv_maildir.c | 2 +-
src/util.c | 102 ++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 103 insertions(+), 1 deletion(-)
diff --git a/src/drv_maildir.c b/src/drv_maildir.c
index fe0e95140703..7ab3acb3e01f 100644
--- a/src/drv_maildir.c
+++ b/src/drv_maildir.c
@@ -614,7 +614,7 @@ maildir_validate( const char *box, int create,
maildir_store_t *ctx )
/* We always create new/ and tmp/ if they are missing.
cur/ is the presence indicator. */
if (!i && !create)
return DRV_BOX_BAD;
- if (mkdir( buf, 0700 )) {
+ if (mkdir_p( buf, bl + 3 )) {
sys_error( "Maildir error: cannot create
directory %s", buf );
return DRV_BOX_BAD;
}
diff --git a/src/util.c b/src/util.c
index 4d17cae5f5d8..94cd9f88e323 100644
--- a/src/util.c
+++ b/src/util.c
@@ -796,9 +796,111 @@ map_name( const char *arg, int l, char **result, uint
reserve, const char *in, c
return 0;
}
+/* See open_last_valid_dir(). */
+static int
+do_open_last_valid_dir( char *path, size_t len )
+{
+ assert( path );
+
+#ifdef __linux__
+ int oflags = O_RDONLY | O_DIRECTORY | O_PATH;
+#else
+ int oflags = O_SEARCH | O_DIRECTORY;
+#endif
+
+ int fd = open( path, oflags );
+ if (fd >= 0)
+ return fd;
+ if (errno != ENOENT)
+ return -1;
+
+ char *last_slash = memrchr( path, '/', len );
+ if (!last_slash)
+ return -1; // open() set errno
+
+ *last_slash = '\0';
+ return do_open_last_valid_dir( path, last_slash - path );
+}
+
+/*
+ * Finds the last component in 'path' that locates an existent file, if any,
and
+ * attempts to open it as a directory with search permissions.
+ *
+ * For example, suppose 'path' is "/A/B/C", /A and /A/B are searchable
+ * directories, and /A/B/C does not exist.
+ * Then on success, the returned value will be an open file descriptor for
/A/B.
+ *
+ * If 'path' is NUL-terminated, 'len' may be zero.
+ * Otherwise, 'len' must be positive.
+ *
+ * On success, returns the resultant file descriptor.
+ * Otherwise, returns -1 and preserves the relevant errno value.
+ */
+static int
+open_last_valid_dir( const char *path, size_t len )
+{
+ assert( path );
+
+ // Make sure to also check cwd if path is relative
+ const char *prefix = (path[0] != '/' && (path[0] != '.' || path[1] !=
'/'))
+ ? "./" : "";
+ size_t prefix_len = strlen( prefix );
+ size_t path_len = len ? len : strlen( path );
+ size_t full_len = prefix_len + path_len;
+
+ // nf*alloc() is overkill here
+ char *full_path = malloc( full_len + 1 );
+ if (!full_path)
+ return -1;
+
+ memcpy( full_path, prefix, prefix_len );
+ memcpy( full_path + prefix_len, path, path_len );
+ full_path[full_len] = '\0';
+
+ int fd = do_open_last_valid_dir( full_path, full_len );
+ free( full_path );
+ return fd;
+}
+
+/*
+ * Checks whether an attempt to recursively create the directory components of
+ * 'path' would be successful, considering the existent components'
permissions.
+ *
+ * If 'path' is NUL-terminated, 'len' may be zero.
+ * Otherwise, 'len' must be positive.
+ *
+ * On success, returns 0.
+ * Otherwise, returns -1 and preserves the relevant errno value.
+ *
+ * Note that a successful call to this function does NOT guarantee a later call
+ * to mkdir_p() will be successful.
+ * Rather, success indicates that if mkdir_p() was called instead of
+ * mkdir_p_check(), then mkdir_p() would have succeeded.
+ */
+static int
+mkdir_p_check( const char *path, size_t len )
+{
+ assert( path );
+
+ int dir = open_last_valid_dir( path, len );
+ if (dir < 0)
+ return -1;
+
+ // open()ing dir guarantees search permissions on all path components.
+ // So we only need to check for write permission on dir itself.
+ int ret = faccessat( dir, ".", W_OK, AT_EACCESS );
+ int errno_bak = errno;
+ close( dir );
+ errno = errno_bak;
+ return ret;
+}
+
int
mkdir_p( char *path, int len )
{
+ if (DFlags & DRYRUN)
+ return mkdir_p_check( path, len );
+
if (!mkdir( path, 0700 ) || errno == EEXIST)
return 0;
char *p = memrchr( path, '/', (size_t)len );
--
2.47.3
_______________________________________________
isync-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/isync-devel