commit d34a9b7468bc53b4a55d1b553676ba3ba5638c35
Author: Oswald Buddenhagen <o...@kde.org>
Date:   Sat Aug 25 15:29:16 2012 +0200

    deal with concurrent maildir modifications during listing
    
    files may be renamed (due to new -> cur transition or flag changes),
    which may lead to two effects if ignored:
    - we see both the old and the new name, so we report a spurious
      duplicate UID
    - we see neither name, so we report a spurious deletion
    
    as countermeasure, record and compare directory modification times. upon
    mismatch, we just start over - as usual.

 src/drv_maildir.c |   38 ++++++++++++++++++++++++++++++++++++++
 src/isync.h       |    1 +
 src/main.c        |    3 +++
 src/run-tests.pl  |    2 +-
 4 files changed, 43 insertions(+), 1 deletions(-)

diff --git a/src/drv_maildir.c b/src/drv_maildir.c
index 9d4a00e..24aae2d 100644
--- a/src/drv_maildir.c
+++ b/src/drv_maildir.c
@@ -507,6 +507,7 @@ maildir_scan( maildir_store_t *ctx, msglist_t *msglist )
 #endif /* USE_DB */
        msg_t *entry;
        int i, j, uid, bl, fnl, ret;
+       time_t now, stamps[2];
        struct stat st;
        char buf[_POSIX_PATH_MAX], nbuf[_POSIX_PATH_MAX];
 
@@ -536,11 +537,32 @@ maildir_scan( maildir_store_t *ctx, msglist_t *msglist )
                }
 #endif /* USE_DB */
                bl = nfsnprintf( buf, sizeof(buf) - 4, "%s/", ctx->gen.path );
+         restat:
+               now = time( 0 );
+               for (i = 0; i < 2; i++) {
+                       memcpy( buf + bl, subdirs[i], 4 );
+                       if (stat( buf, &st )) {
+                               sys_error( "Maildir error: cannot stat %s", buf 
);
+                               goto dfail;
+                       }
+                       if (st.st_mtime == now && !(DFlags & ZERODELAY)) {
+                               /* If the modification happened during this 
second, we wouldn't be able to
+                                * tell if there were further modifications 
during this second. So wait.
+                                * This has the nice side effect that we wait 
for "batches" of changes to
+                                * complete. On the downside, it can 
potentially block indefinitely. */
+                               info( "Maildir notice: sleeping due to recent 
directory modification.\n" );
+                               sleep( 1 ); /* FIXME: should make this async */
+                               goto restat;
+                       }
+                       stamps[i] = st.st_mtime;
+               }
                for (i = 0; i < 2; i++) {
                        memcpy( buf + bl, subdirs[i], 4 );
                        if (!(d = opendir( buf ))) {
                                sys_error( "Maildir error: cannot list %s", buf 
);
+                         rfail:
                                maildir_free_scan( msglist );
+                         dfail:
 #ifdef USE_DB
                                if (ctx->db)
                                        tdb->close( tdb, 0 );
@@ -600,6 +622,22 @@ maildir_scan( maildir_store_t *ctx, msglist_t *msglist )
                        }
                        closedir( d );
                }
+               for (i = 0; i < 2; i++) {
+                       memcpy( buf + bl, subdirs[i], 4 );
+                       if (stat( buf, &st )) {
+                               sys_error( "Maildir error: cannot re-stat %s", 
buf );
+                               goto rfail;
+                       }
+                       if (st.st_mtime != stamps[i]) {
+                               /* Somebody messed with the mailbox since we 
started listing it. */
+#ifdef USE_DB
+                               if (ctx->db)
+                                       tdb->close( tdb, 0 );
+#endif /* USE_DB */
+                               maildir_free_scan( msglist );
+                               goto again;
+                       }
+               }
 #ifdef USE_DB
                if (ctx->db) {
                        if ((ret = ctx->db->cursor( ctx->db, 0, &dbc, 0 )))
diff --git a/src/isync.h b/src/isync.h
index fa7d082..a39ab21 100644
--- a/src/isync.h
+++ b/src/isync.h
@@ -395,6 +395,7 @@ void cram( const char *challenge, const char *user, const 
char *pass,
 #define QUIET        8
 #define VERYQUIET    16
 #define KEEPJOURNAL  32
+#define ZERODELAY    64
 
 extern int DFlags;
 
diff --git a/src/main.c b/src/main.c
index 85e3699..71470e6 100644
--- a/src/main.c
+++ b/src/main.c
@@ -437,6 +437,9 @@ main( int argc, char **argv )
                case 'J':
                        DFlags |= KEEPJOURNAL;
                        break;
+               case 'Z':
+                       DFlags |= ZERODELAY;
+                       break;
                case 'v':
                        version();
                case 'h':
diff --git a/src/run-tests.pl b/src/run-tests.pl
index f2b4736..842e6e6 100755
--- a/src/run-tests.pl
+++ b/src/run-tests.pl
@@ -282,7 +282,7 @@ sub killcfg()
 sub runsync($)
 {
 #      open FILE, "valgrind -q --log-fd=3 ../mbsync ".shift()." -c .mbsyncrc 
test 3>&2 2>&1 |";
-       open FILE, "../mbsync -D ".shift()." -c .mbsyncrc test 2>&1 |";
+       open FILE, "../mbsync -D -Z ".shift()." -c .mbsyncrc test 2>&1 |";
        my @out = <FILE>;
        close FILE or push(@out, $! ? "*** error closing mbsync: $!\n" : "*** 
mbsync exited with signal ".($?&127).", code ".($?>>8)."\n");
        return $?, @out;

------------------------------------------------------------------------------
Live Security Virtual Conference
Exclusive live event will cover all the ways today's security and 
threat landscape has changed and how IT managers can respond. Discussions 
will include endpoint security, mobile security and the latest in malware 
threats. http://www.accelacomm.com/jaw/sfrnl04242012/114/50122263/
_______________________________________________
isync-devel mailing list
isync-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/isync-devel

Reply via email to