unify .isyncuidmap.db handling with that of .uidvalidity

that is, open the database on demand when locking and close it again
when unlocking.
This commit is contained in:
Oswald Buddenhagen 2015-01-01 19:25:17 +01:00
parent 4da89af7be
commit 5f265ad7da
2 changed files with 76 additions and 73 deletions

5
TODO
View File

@ -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

View File

@ -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,6 +547,36 @@ 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;
} }
#ifdef USE_DB
if (ctx->usedb) {
if (fstat( ctx->uvfd, &st )) {
sys_error( "Maildir error: cannot fstat UID database" );
return DRV_BOX_BAD;
}
if (db_create( &ctx->db, 0, 0 )) {
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
#endif
{
lseek( ctx->uvfd, 0, SEEK_SET ); lseek( ctx->uvfd, 0, SEEK_SET );
if ((n = read( ctx->uvfd, buf, sizeof(buf) - 1 )) <= 0 || if ((n = read( ctx->uvfd, buf, sizeof(buf) - 1 )) <= 0 ||
(buf[n] = 0, sscanf( buf, "%d\n%d", &ctx->gen.uidvalidity, &ctx->nuid ) != 2)) { (buf[n] = 0, sscanf( buf, "%d\n%d", &ctx->gen.uidvalidity, &ctx->nuid ) != 2)) {
@ -554,7 +590,8 @@ maildir_uidval_lock( maildir_store_t *ctx )
} }
#endif #endif
return maildir_init_uidval_new( ctx ); return maildir_init_uidval_new( ctx );
} else }
}
ctx->uvok = 1; 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;
} }