The maildir driver uses a file containing the uid database or validity
to keep track of uids.  Since dry runs shouldn't modify the actual
maildir store, use of this file should be simulated.

The driver's logic heavily depends on this file, especially when using a
database, so access to it can't simply be prevented.  The file shouldn't
be too large however, so it shouldn't be unreasonable performance-wise
to copy its contents.

Alter maildir_open_box() to create a copy of the uid file, if it exists,
for use in place of it throughout the driver.  Also refactor its logic
to better fit this change.
---
 src/drv_maildir.c | 170 +++++++++++++++++++++++++++++++++++++++-------
 1 file changed, 145 insertions(+), 25 deletions(-)

diff --git a/src/drv_maildir.c b/src/drv_maildir.c
index 7ab3acb3e01f..3fc184d60694 100644
--- a/src/drv_maildir.c
+++ b/src/drv_maildir.c
@@ -68,6 +68,7 @@ typedef union maildir_store {
                DB *db;
                char *usedb;
 #endif /* USE_DB */
+               char *uvpath;  // Only for dry runs
                string_list_t *boxes;  // _list results
                char listed;  // was _list already run with these flags?
                // note that the message counts do _not_ reflect stats from 
msgs,
@@ -267,6 +268,9 @@ maildir_cleanup( store_t *gctx )
        free( ctx->excs.data );
        if (ctx->uvfd >= 0)
                close( ctx->uvfd );
+       if (ctx->uvpath && (DFlags & DRYRUN))
+               unlink( ctx->uvpath );
+       free( ctx->uvpath );
        conf_wakeup( &ctx->lcktmr, -1 );
 }
 
@@ -1246,6 +1250,7 @@ maildir_select_box( store_t *gctx, const char *name )
        ctx->app_msgs = &ctx->msgs;
        ctx->excs.data = NULL;
        ctx->uvfd = -1;
+       ctx->uvpath = NULL;
 #ifdef USE_DB
        ctx->db = NULL;
        ctx->usedb = NULL;
@@ -1273,49 +1278,164 @@ maildir_get_box_path( store_t *gctx )
        return ((maildir_store_t *)gctx)->path;
 }
 
+/*
+ * Creates a copy of the uid file located at 'path' with parent directory 
'base'
+ * and filename 'name', if it has sufficient permissions.
+ * The value of 'path' must be consistent with those of 'base' and 'name'.
+ * If 'new' is non-NULL, the copy's path is stored in it.
+ * If the uid file does not exist and 'force' is nonzero, the copy is created 
as
+ * an empty file.
+ *
+ * On success, returns a file descriptor for the copy.
+ * Otherwise, returns -1 and preserves the relevant errno value.
+ */
+static int
+copy_uid_file(
+               const char *base,
+               const char *name,
+               const char *path,
+               char **new,
+               int force )
+{
+       assert( base );
+       assert( name );
+       assert( path );
+
+#ifdef __linux__
+       int oflags = O_RDONLY | O_DIRECTORY | O_PATH;
+#else
+       int oflags = O_SEARCH | O_DIRECTORY;
+#endif
+
+       int can_write = 0;
+       int dir = open( base, oflags );
+       if (dir >= 0) {
+               can_write = !faccessat( dir, name, W_OK, AT_EACCESS );
+               close( dir );
+       } else if (errno != ENOENT) {
+               return -1;
+       }
+
+       int fd = open_temp_copy( path, new, force );
+       if (fd >= 0 && !can_write && !force) {
+               close( fd );
+               errno = EACCES;
+               return -1;
+       }
+       return fd;
+}
+
+/*
+ * Opens the uid file with filename 'name' in directory 'base'.
+ * If successful, the full filepath ('base' + 'name') is stored in 'full'.
+ * If 'create' is nonzero, the file will be created if it does not exist.
+ *
+ * On success, returns a file descriptor for the uid file.
+ * Otherwise, returns -1 and preserves the relevant errno value.
+ */
+static int
+open_uid_file( const char *base, const char *name, char **full, int create )
+{
+       assert( base );
+       assert( name );
+       assert( full );
+
+       char *path;
+       nfasprintf( &path, "%s/%s", base, name );
+
+       int oflags = O_RDWR;
+       if (create)
+               oflags |= O_CREAT;
+
+       int fd = (DFlags & DRYRUN)
+                       ? copy_uid_file( base, name, path, full, create )
+                       : open( path, oflags, 0600 );
+
+       if (fd < 0 && create) {
+               int errno_bak = errno;
+               sys_error( "Maildir error: cannot write %s", path );
+               errno = errno_bak;
+       }
+       if (DFlags & DRYRUN)
+               free( path );
+       else
+               *full = path;
+       return fd;
+}
+
+static int
+open_uidval_file( const char *base, char **path, int create )
+{
+       assert( base );
+
+       char *full = NULL;
+       int fd = open_uid_file( base, ".uidvalidity", &full, create );
+       if (fd >= 0 && path)
+               *path = full;
+       else
+               free( full );
+       return fd;
+}
+
+#ifdef USE_DB
+static int
+open_uiddb_file( const char *base, char **path, int create )
+{
+       assert( base );
+       assert( path );
+
+       char *full = NULL;
+       int fd = open_uid_file( base, ".isyncuidmap.db", &full, create );
+       if (fd >= 0)
+               *path = full;
+       else
+               free( full );
+       return fd;
+}
+#endif /* USE_DB */
+
 static void
 maildir_open_box( store_t *gctx,
                   void (*cb)( int sts, uint uidvalidity, void *aux ), void 
*aux )
 {
        maildir_store_t *ctx = (maildir_store_t *)gctx;
        int ret;
-       char uvpath[_POSIX_PATH_MAX];
 
        if ((ret = maildir_validate( ctx->path, ctx->is_inbox, ctx )) != DRV_OK)
                goto bail;
 
-       nfsnprintf( uvpath, sizeof(uvpath), "%s/.uidvalidity", ctx->path );
+       int create;
+       char **uvpath = (DFlags & DRYRUN) ? &ctx->uvpath : NULL;
 #ifndef USE_DB
-       if ((ctx->uvfd = open( uvpath, O_RDWR | O_CREAT, 0600 )) < 0) {
-               sys_error( "Maildir error: cannot write %s", uvpath );
-               cb( DRV_BOX_BAD, UIDVAL_BAD, aux );
-               return;
-       }
+       create = 1;
+       ctx->uvfd = open_uidval_file( ctx->path, uvpath, create );
 #else
        ctx->usedb = NULL;
-       if ((ctx->uvfd = open( uvpath, O_RDWR, 0600 )) < 0) {
-               nfsnprintf( uvpath, sizeof(uvpath), "%s/.isyncuidmap.db", 
ctx->path );
-               if ((ctx->uvfd = open( uvpath, O_RDWR, 0600 )) < 0) {
-                       if (ctx->conf->alt_map) {
-                               if ((ctx->uvfd = open( uvpath, O_RDWR | 
O_CREAT, 0600 )) >= 0)
-                                       goto dbok;
-                       } else {
-                               nfsnprintf( uvpath, sizeof(uvpath), 
"%s/.uidvalidity", ctx->path );
-                               if ((ctx->uvfd = open( uvpath, O_RDWR | 
O_CREAT, 0600 )) >= 0)
-                                       goto fnok;
-                       }
-                       sys_error( "Maildir error: cannot write %s", uvpath );
-                       cb( DRV_BOX_BAD, UIDVAL_BAD, aux );
-                       return;
-               } else {
-                 dbok:
-                       ctx->usedb = nfstrdup( uvpath );
+       create = 0;
+       ctx->uvfd = open_uidval_file( ctx->path, uvpath, create );
+       if (ctx->uvfd < 0) {
+               ctx->uvfd = open_uiddb_file( ctx->path, &ctx->usedb, create );
+               if (ctx->uvfd < 0) {
+                       create = 1;
+                       ctx->uvfd = ctx->conf->alt_map
+                                       ? open_uiddb_file( ctx->path, 
&ctx->usedb, create )
+                                       : open_uidval_file( ctx->path, uvpath, 
create );
                }
        }
-  fnok:
+       if (ctx->usedb && (DFlags & DRYRUN))
+               ctx->uvpath = nfstrdup( ctx->usedb );
 #endif /* USE_DB */
+
+       if (ctx->uvfd < 0) {
+               cb( DRV_BOX_BAD, UIDVAL_BAD, aux );
+               return;
+       }
        ret = maildir_uidval_lock( ctx );
 
+       // Fake error to reopen as a fake store
+       if (ret == DRV_OK && (DFlags & DRYRUN))
+               ret = DRV_BOX_BAD;
+
   bail:
        cb( ret, ctx->uidvalidity, aux );
 }
-- 
2.47.3



_______________________________________________
isync-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/isync-devel

Reply via email to