浏览代码

unify .isyncuidmap.db handling with that of .uidvalidity

that is, open the database on demand when locking and close it again
when unlocking.
Oswald Buddenhagen 10 年之前
父节点
当前提交
5f265ad7da
共有 2 个文件被更改,包括 76 次插入73 次删除
  1. 0 5
      TODO
  2. 76 68
      src/drv_maildir.c

+ 0 - 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

+ 76 - 68
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 ||
-	    (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" );
+
+#ifdef USE_DB
+	if (ctx->usedb) {
+		if (fstat( ctx->uvfd, &st )) {
+			sys_error( "Maildir error: cannot fstat UID database" );
 			return DRV_BOX_BAD;
 			return DRV_BOX_BAD;
 		}
 		}
-#endif
-		return maildir_init_uidval_new( ctx );
+		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
 	} 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 ((ret = ctx->db->cursor( ctx->db, 0, &dbc, 0 )))
+		if (ctx->usedb) {
+			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];
-			ctx->nuid = ((int *)value.data)[1];
-			ctx->uvok = 1;
+		  dbok:
+			ctx->usedb = nfstrdup( uvpath );
 		}
 		}
-		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;
 	}
 	}