Browse Source

unify .isyncuidmap.db handling with that of .uidvalidity

that is, open the database on demand when locking and close it again
when unlocking.
wip/server-refactor
Oswald Buddenhagen 10 years ago
parent
commit
5f265ad7da
  1. 5
      TODO
  2. 144
      src/drv_maildir.c

5
TODO

@ -18,11 +18,6 @@ add alternative treatments of expired messages. ExpiredMessageMode: Prune
(delete messages like now), Keep (just don't sync) and Archive (move to (delete messages like now), Keep (just don't sync) and Archive (move to
separate folder - ArchiveSuffix, default .archive). separate folder - ArchiveSuffix, default .archive).
unify maildir locking between the two UID storage schemes.
re-opening the db may be expensive, so keep it open.
but keeping lock for too long (e.g., big message downloads) may block other
clients. auto-release lock after 500 ms?
kill the concept of an INBOX, it is a relic from single-channel operation. kill the concept of an INBOX, it is a relic from single-channel operation.
if somebody needs it, he can have two stores with different Paths. the path if somebody needs it, he can have two stores with different Paths. the path
can name a single (in-)box (curr. broken with maildir). an empty box name can name a single (in-)box (curr. broken with maildir). an empty box name

144
src/drv_maildir.c

@ -73,6 +73,7 @@ typedef struct maildir_store {
char *trash; char *trash;
#ifdef USE_DB #ifdef USE_DB
DB *db; DB *db;
char *usedb;
#endif /* USE_DB */ #endif /* USE_DB */
wakeup_t lcktmr; wakeup_t lcktmr;
} maildir_store_t; } maildir_store_t;
@ -184,6 +185,7 @@ maildir_cleanup( store_t *gctx )
#ifdef USE_DB #ifdef USE_DB
if (ctx->db) if (ctx->db)
ctx->db->close( ctx->db, 0 ); ctx->db->close( ctx->db, 0 );
free( ctx->usedb );
#endif /* USE_DB */ #endif /* USE_DB */
free( gctx->path ); free( gctx->path );
free( ctx->excs ); free( ctx->excs );
@ -518,6 +520,10 @@ static int
maildir_uidval_lock( maildir_store_t *ctx ) maildir_uidval_lock( maildir_store_t *ctx )
{ {
int n; int n;
#ifdef USE_DB
int ret;
struct stat st;
#endif
char buf[128]; char buf[128];
if (ctx->lcktmr.links.next) { if (ctx->lcktmr.links.next) {
@ -541,21 +547,52 @@ maildir_uidval_lock( maildir_store_t *ctx )
error( "Maildir error: cannot fcntl lock UIDVALIDITY.\n" ); error( "Maildir error: cannot fcntl lock UIDVALIDITY.\n" );
return DRV_BOX_BAD; return DRV_BOX_BAD;
} }
lseek( ctx->uvfd, 0, SEEK_SET );
if ((n = read( ctx->uvfd, buf, sizeof(buf) - 1 )) <= 0 || #ifdef USE_DB
(buf[n] = 0, sscanf( buf, "%d\n%d", &ctx->gen.uidvalidity, &ctx->nuid ) != 2)) { if (ctx->usedb) {
#if 1 if (fstat( ctx->uvfd, &st )) {
/* In a generic driver, resetting the UID validity would be the right thing. sys_error( "Maildir error: cannot fstat UID database" );
* But this would mess up the sync state completely. So better bail out and
* give the user a chance to fix the mailbox. */
if (n) {
error( "Maildir error: cannot read UIDVALIDITY.\n" );
return DRV_BOX_BAD; return DRV_BOX_BAD;
} }
#endif if (db_create( &ctx->db, 0, 0 )) {
return maildir_init_uidval_new( ctx ); fputs( "Maildir error: db_create() failed\n", stderr );
return DRV_BOX_BAD;
}
if ((ret = (ctx->db->open)( ctx->db, 0, ctx->usedb, 0, DB_HASH,
st.st_size ? 0 : DB_CREATE | DB_TRUNCATE, 0 ))) {
ctx->db->err( ctx->db, ret, "Maildir error: db->open(%s)", ctx->usedb );
return DRV_BOX_BAD;
}
key.data = (void *)"UIDVALIDITY";
key.size = 11;
if ((ret = ctx->db->get( ctx->db, 0, &key, &value, 0 ))) {
if (ret != DB_NOTFOUND) {
ctx->db->err( ctx->db, ret, "Maildir error: db->get()" );
return DRV_BOX_BAD;
}
return maildir_init_uidval_new( ctx );
}
ctx->gen.uidvalidity = ((int *)value.data)[0];
ctx->nuid = ((int *)value.data)[1];
} else } else
ctx->uvok = 1; #endif
{
lseek( ctx->uvfd, 0, SEEK_SET );
if ((n = read( ctx->uvfd, buf, sizeof(buf) - 1 )) <= 0 ||
(buf[n] = 0, sscanf( buf, "%d\n%d", &ctx->gen.uidvalidity, &ctx->nuid ) != 2)) {
#if 1
/* In a generic driver, resetting the UID validity would be the right thing.
* But this would mess up the sync state completely. So better bail out and
* give the user a chance to fix the mailbox. */
if (n) {
error( "Maildir error: cannot read UIDVALIDITY.\n" );
return DRV_BOX_BAD;
}
#endif
return maildir_init_uidval_new( ctx );
}
}
ctx->uvok = 1;
conf_wakeup( &ctx->lcktmr, 2 ); conf_wakeup( &ctx->lcktmr, 2 );
return DRV_OK; return DRV_OK;
} }
@ -563,6 +600,12 @@ maildir_uidval_lock( maildir_store_t *ctx )
static void static void
maildir_uidval_unlock( maildir_store_t *ctx ) maildir_uidval_unlock( maildir_store_t *ctx )
{ {
#ifdef USE_DB
if (ctx->db) {
ctx->db->close( ctx->db, 0 );
ctx->db = 0;
}
#endif /* USE_DB */
lck.l_type = F_UNLCK; lck.l_type = F_UNLCK;
fcntl( ctx->uvfd, F_SETLK, &lck ); fcntl( ctx->uvfd, F_SETLK, &lck );
#ifdef LEGACY_FLOCK #ifdef LEGACY_FLOCK
@ -594,6 +637,8 @@ maildir_set_uid( maildir_store_t *ctx, const char *name, int *uid )
{ {
int ret; int ret;
if ((ret = maildir_uidval_lock( ctx )) != DRV_OK)
return ret;
*uid = ++ctx->nuid; *uid = ++ctx->nuid;
make_key( ((maildir_store_conf_t *)ctx->gen.conf)->info_stop, &key, (char *)name ); make_key( ((maildir_store_conf_t *)ctx->gen.conf)->info_stop, &key, (char *)name );
@ -693,7 +738,7 @@ maildir_scan( maildir_store_t *ctx, msglist_t *msglist )
ctx->gen.count = ctx->gen.recent = 0; ctx->gen.count = ctx->gen.recent = 0;
if (ctx->uvok || ctx->maxuid == INT_MAX) { if (ctx->uvok || ctx->maxuid == INT_MAX) {
#ifdef USE_DB #ifdef USE_DB
if (ctx->db) { if (ctx->usedb) {
if (db_create( &tdb, 0, 0 )) { if (db_create( &tdb, 0, 0 )) {
fputs( "Maildir error: db_create() failed\n", stderr ); fputs( "Maildir error: db_create() failed\n", stderr );
return DRV_BOX_BAD; return DRV_BOX_BAD;
@ -734,7 +779,7 @@ maildir_scan( maildir_store_t *ctx, msglist_t *msglist )
maildir_free_scan( msglist ); maildir_free_scan( msglist );
dfail: dfail:
#ifdef USE_DB #ifdef USE_DB
if (ctx->db) if (ctx->usedb)
tdb->close( tdb, 0 ); tdb->close( tdb, 0 );
#endif /* USE_DB */ #endif /* USE_DB */
return DRV_BOX_BAD; return DRV_BOX_BAD;
@ -745,7 +790,9 @@ maildir_scan( maildir_store_t *ctx, msglist_t *msglist )
ctx->gen.count++; ctx->gen.count++;
ctx->gen.recent += i; ctx->gen.recent += i;
#ifdef USE_DB #ifdef USE_DB
if (ctx->db) { if (ctx->usedb) {
if (maildir_uidval_lock( ctx ) != DRV_OK)
goto mbork;
make_key( conf->info_stop, &key, e->d_name ); make_key( conf->info_stop, &key, e->d_name );
if ((ret = ctx->db->get( ctx->db, 0, &key, &value, 0 ))) { if ((ret = ctx->db->get( ctx->db, 0, &key, &value, 0 ))) {
if (ret != DB_NOTFOUND) { if (ret != DB_NOTFOUND) {
@ -802,7 +849,7 @@ maildir_scan( maildir_store_t *ctx, msglist_t *msglist )
if (st.st_mtime != stamps[i]) { if (st.st_mtime != stamps[i]) {
/* Somebody messed with the mailbox since we started listing it. */ /* Somebody messed with the mailbox since we started listing it. */
#ifdef USE_DB #ifdef USE_DB
if (ctx->db) if (ctx->usedb)
tdb->close( tdb, 0 ); tdb->close( tdb, 0 );
#endif /* USE_DB */ #endif /* USE_DB */
maildir_free_scan( msglist ); maildir_free_scan( msglist );
@ -810,8 +857,10 @@ maildir_scan( maildir_store_t *ctx, msglist_t *msglist )
} }
} }
#ifdef USE_DB #ifdef USE_DB
if (ctx->db) { if (ctx->usedb) {
if ((ret = ctx->db->cursor( ctx->db, 0, &dbc, 0 ))) if (maildir_uidval_lock( ctx ) != DRV_OK)
;
else if ((ret = ctx->db->cursor( ctx->db, 0, &dbc, 0 )))
ctx->db->err( ctx->db, ret, "Maildir error: db->cursor()" ); ctx->db->err( ctx->db, ret, "Maildir error: db->cursor()" );
else { else {
for (;;) { for (;;) {
@ -868,7 +917,7 @@ maildir_scan( maildir_store_t *ctx, msglist_t *msglist )
if ((ctx->gen.opts & OPEN_SIZE) || ((ctx->gen.opts & OPEN_FIND) && uid >= ctx->newuid)) if ((ctx->gen.opts & OPEN_SIZE) || ((ctx->gen.opts & OPEN_FIND) && uid >= ctx->newuid))
nfsnprintf( buf + bl, sizeof(buf) - bl, "%s/%s", subdirs[entry->recent], entry->base ); nfsnprintf( buf + bl, sizeof(buf) - bl, "%s/%s", subdirs[entry->recent], entry->base );
#ifdef USE_DB #ifdef USE_DB
} else if (ctx->db) { } else if (ctx->usedb) {
if ((ret = maildir_set_uid( ctx, entry->base, &uid )) != DRV_OK) { if ((ret = maildir_set_uid( ctx, entry->base, &uid )) != DRV_OK) {
maildir_free_scan( msglist ); maildir_free_scan( msglist );
return ret; return ret;
@ -982,6 +1031,7 @@ maildir_select_box( store_t *gctx, const char *name )
ctx->uvfd = -1; ctx->uvfd = -1;
#ifdef USE_DB #ifdef USE_DB
ctx->db = 0; ctx->db = 0;
ctx->usedb = 0;
#endif /* USE_DB */ #endif /* USE_DB */
ctx->fresh[0] = ctx->fresh[1] = 0; ctx->fresh[0] = ctx->fresh[1] = 0;
if (starts_with( name, -1, "INBOX", 5 ) && (!name[5] || name[5] == '/')) { if (starts_with( name, -1, "INBOX", 5 ) && (!name[5] || name[5] == '/')) {
@ -1002,9 +1052,6 @@ maildir_open_box( store_t *gctx,
{ {
maildir_store_t *ctx = (maildir_store_t *)gctx; maildir_store_t *ctx = (maildir_store_t *)gctx;
int ret; int ret;
#ifdef USE_DB
struct stat st;
#endif /* USE_DB */
char uvpath[_POSIX_PATH_MAX]; char uvpath[_POSIX_PATH_MAX];
if ((ret = maildir_validate( gctx->path, 0, ctx )) != DRV_OK) if ((ret = maildir_validate( gctx->path, 0, ctx )) != DRV_OK)
@ -1018,6 +1065,7 @@ maildir_open_box( store_t *gctx,
return; return;
} }
#else #else
ctx->usedb = 0;
if ((ctx->uvfd = open( uvpath, O_RDWR, 0600 )) < 0) { if ((ctx->uvfd = open( uvpath, O_RDWR, 0600 )) < 0) {
nfsnprintf( uvpath, sizeof(uvpath), "%s/.isyncuidmap.db", gctx->path ); nfsnprintf( uvpath, sizeof(uvpath), "%s/.isyncuidmap.db", gctx->path );
if ((ctx->uvfd = open( uvpath, O_RDWR, 0600 )) < 0) { if ((ctx->uvfd = open( uvpath, O_RDWR, 0600 )) < 0) {
@ -1032,52 +1080,10 @@ maildir_open_box( store_t *gctx,
sys_error( "Maildir error: cannot write %s", uvpath ); sys_error( "Maildir error: cannot write %s", uvpath );
cb( DRV_BOX_BAD, aux ); cb( DRV_BOX_BAD, aux );
return; return;
}
dbok:
#if SEEK_SET != 0
lck.l_whence = SEEK_SET;
#endif
lck.l_type = F_WRLCK;
if (fcntl( ctx->uvfd, F_SETLKW, &lck )) {
sys_error( "Maildir error: cannot lock %s", uvpath );
cb( DRV_BOX_BAD, aux );
return;
}
if (fstat( ctx->uvfd, &st )) {
sys_error( "Maildir error: cannot stat %s", uvpath );
cb( DRV_BOX_BAD, aux );
return;
}
if (db_create( &ctx->db, 0, 0 )) {
fputs( "Maildir error: db_create() failed\n", stderr );
cb( DRV_BOX_BAD, aux );
return;
}
if ((ret = (ctx->db->open)( ctx->db, 0, uvpath, 0, DB_HASH,
st.st_size ? 0 : DB_CREATE | DB_TRUNCATE, 0 ))) {
ctx->db->err( ctx->db, ret, "Maildir error: db->open(%s)", uvpath );
cb( DRV_BOX_BAD, aux );
return;
}
key.data = (void *)"UIDVALIDITY";
key.size = 11;
if ((ret = ctx->db->get( ctx->db, 0, &key, &value, 0 ))) {
if (ret != DB_NOTFOUND) {
ctx->db->err( ctx->db, ret, "Maildir error: db->get()" );
cb( DRV_BOX_BAD, aux );
return;
}
if (maildir_init_uidval_new( ctx ) != DRV_OK) {
cb( DRV_BOX_BAD, aux );
return;
}
} else { } else {
ctx->gen.uidvalidity = ((int *)value.data)[0]; dbok:
ctx->nuid = ((int *)value.data)[1]; ctx->usedb = nfstrdup( uvpath );
ctx->uvok = 1;
} }
cb( DRV_OK, aux );
return;
} }
fnok: fnok:
#endif /* USE_DB */ #endif /* USE_DB */
@ -1327,7 +1333,7 @@ maildir_store_msg( store_t *gctx, msg_data_t *data, int to_trash,
bl = nfsnprintf( base, sizeof(base), "%ld.%d_%d.%s", (long)time( 0 ), Pid, ++MaildirCount, Hostname ); bl = nfsnprintf( base, sizeof(base), "%ld.%d_%d.%s", (long)time( 0 ), Pid, ++MaildirCount, Hostname );
if (!to_trash) { if (!to_trash) {
#ifdef USE_DB #ifdef USE_DB
if (ctx->db) { if (ctx->usedb) {
if ((ret = maildir_set_uid( ctx, base, &uid )) != DRV_OK) { if ((ret = maildir_set_uid( ctx, base, &uid )) != DRV_OK) {
free( data->data ); free( data->data );
cb( ret, 0, aux ); cb( ret, 0, aux );
@ -1480,6 +1486,8 @@ maildir_purge_msg( maildir_store_t *ctx, const char *name )
{ {
int ret; int ret;
if ((ret = maildir_uidval_lock( ctx )) != DRV_OK)
return ret;
make_key( ((maildir_store_conf_t *)ctx->gen.conf)->info_stop, &key, (char *)name ); make_key( ((maildir_store_conf_t *)ctx->gen.conf)->info_stop, &key, (char *)name );
if ((ret = ctx->db->del( ctx->db, 0, &key, 0 ))) { if ((ret = ctx->db->del( ctx->db, 0, &key, 0 ))) {
ctx->db->err( ctx->db, ret, "Maildir error: db->del()" ); ctx->db->err( ctx->db, ret, "Maildir error: db->del()" );
@ -1529,7 +1537,7 @@ maildir_trash_msg( store_t *gctx, message_t *gmsg,
gctx->count--; gctx->count--;
#ifdef USE_DB #ifdef USE_DB
if (ctx->db) { if (ctx->usedb) {
cb( maildir_purge_msg( ctx, msg->base ), aux ); cb( maildir_purge_msg( ctx, msg->base ), aux );
return; return;
} }

Loading…
Cancel
Save