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.
This commit is contained in:
parent
3386285205
commit
233f563569
|
@ -507,6 +507,7 @@ maildir_scan( maildir_store_t *ctx, msglist_t *msglist )
|
||||||
#endif /* USE_DB */
|
#endif /* USE_DB */
|
||||||
msg_t *entry;
|
msg_t *entry;
|
||||||
int i, j, uid, bl, fnl, ret;
|
int i, j, uid, bl, fnl, ret;
|
||||||
|
time_t now, stamps[2];
|
||||||
struct stat st;
|
struct stat st;
|
||||||
char buf[_POSIX_PATH_MAX], nbuf[_POSIX_PATH_MAX];
|
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 */
|
#endif /* USE_DB */
|
||||||
bl = nfsnprintf( buf, sizeof(buf) - 4, "%s/", ctx->gen.path );
|
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++) {
|
for (i = 0; i < 2; i++) {
|
||||||
memcpy( buf + bl, subdirs[i], 4 );
|
memcpy( buf + bl, subdirs[i], 4 );
|
||||||
if (!(d = opendir( buf ))) {
|
if (!(d = opendir( buf ))) {
|
||||||
sys_error( "Maildir error: cannot list %s", buf );
|
sys_error( "Maildir error: cannot list %s", buf );
|
||||||
|
rfail:
|
||||||
maildir_free_scan( msglist );
|
maildir_free_scan( msglist );
|
||||||
|
dfail:
|
||||||
#ifdef USE_DB
|
#ifdef USE_DB
|
||||||
if (ctx->db)
|
if (ctx->db)
|
||||||
tdb->close( tdb, 0 );
|
tdb->close( tdb, 0 );
|
||||||
|
@ -600,6 +622,22 @@ maildir_scan( maildir_store_t *ctx, msglist_t *msglist )
|
||||||
}
|
}
|
||||||
closedir( d );
|
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
|
#ifdef USE_DB
|
||||||
if (ctx->db) {
|
if (ctx->db) {
|
||||||
if ((ret = ctx->db->cursor( ctx->db, 0, &dbc, 0 )))
|
if ((ret = ctx->db->cursor( ctx->db, 0, &dbc, 0 )))
|
||||||
|
|
|
@ -395,6 +395,7 @@ void cram( const char *challenge, const char *user, const char *pass,
|
||||||
#define QUIET 8
|
#define QUIET 8
|
||||||
#define VERYQUIET 16
|
#define VERYQUIET 16
|
||||||
#define KEEPJOURNAL 32
|
#define KEEPJOURNAL 32
|
||||||
|
#define ZERODELAY 64
|
||||||
|
|
||||||
extern int DFlags;
|
extern int DFlags;
|
||||||
|
|
||||||
|
|
|
@ -437,6 +437,9 @@ main( int argc, char **argv )
|
||||||
case 'J':
|
case 'J':
|
||||||
DFlags |= KEEPJOURNAL;
|
DFlags |= KEEPJOURNAL;
|
||||||
break;
|
break;
|
||||||
|
case 'Z':
|
||||||
|
DFlags |= ZERODELAY;
|
||||||
|
break;
|
||||||
case 'v':
|
case 'v':
|
||||||
version();
|
version();
|
||||||
case 'h':
|
case 'h':
|
||||||
|
|
|
@ -282,7 +282,7 @@ sub killcfg()
|
||||||
sub runsync($)
|
sub runsync($)
|
||||||
{
|
{
|
||||||
# open FILE, "valgrind -q --log-fd=3 ../mbsync ".shift()." -c .mbsyncrc test 3>&2 2>&1 |";
|
# 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>;
|
my @out = <FILE>;
|
||||||
close FILE or push(@out, $! ? "*** error closing mbsync: $!\n" : "*** mbsync exited with signal ".($?&127).", code ".($?>>8)."\n");
|
close FILE or push(@out, $! ? "*** error closing mbsync: $!\n" : "*** mbsync exited with signal ".($?&127).", code ".($?>>8)."\n");
|
||||||
return $?, @out;
|
return $?, @out;
|
||||||
|
|
Loading…
Reference in New Issue
Block a user