Эх сурвалжийг харах

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 жил өмнө
parent
commit
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
 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.
 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

+ 76 - 68
src/drv_maildir.c

@@ -73,6 +73,7 @@ typedef struct maildir_store {
 	char *trash;
 #ifdef USE_DB
 	DB *db;
+	char *usedb;
 #endif /* USE_DB */
 	wakeup_t lcktmr;
 } maildir_store_t;
@@ -184,6 +185,7 @@ maildir_cleanup( store_t *gctx )
 #ifdef USE_DB
 	if (ctx->db)
 		ctx->db->close( ctx->db, 0 );
+	free( ctx->usedb );
 #endif /* USE_DB */
 	free( gctx->path );
 	free( ctx->excs );
@@ -518,6 +520,10 @@ static int
 maildir_uidval_lock( maildir_store_t *ctx )
 {
 	int n;
+#ifdef USE_DB
+	int ret;
+	struct stat st;
+#endif
 	char buf[128];
 
 	if (ctx->lcktmr.links.next) {
@@ -541,21 +547,52 @@ maildir_uidval_lock( maildir_store_t *ctx )
 		error( "Maildir error: cannot fcntl lock UIDVALIDITY.\n" );
 		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;
 		}
-#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
-		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 );
 	return DRV_OK;
 }
@@ -563,6 +600,12 @@ maildir_uidval_lock( maildir_store_t *ctx )
 static void
 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;
 	fcntl( ctx->uvfd, F_SETLK, &lck );
 #ifdef LEGACY_FLOCK
@@ -594,6 +637,8 @@ maildir_set_uid( maildir_store_t *ctx, const char *name, int *uid )
 {
 	int ret;
 
+	if ((ret = maildir_uidval_lock( ctx )) != DRV_OK)
+		return ret;
 	*uid = ++ctx->nuid;
 
 	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;
 	if (ctx->uvok || ctx->maxuid == INT_MAX) {
 #ifdef USE_DB
-		if (ctx->db) {
+		if (ctx->usedb) {
 			if (db_create( &tdb, 0, 0 )) {
 				fputs( "Maildir error: db_create() failed\n", stderr );
 				return DRV_BOX_BAD;
@@ -734,7 +779,7 @@ maildir_scan( maildir_store_t *ctx, msglist_t *msglist )
 				maildir_free_scan( msglist );
 			  dfail:
 #ifdef USE_DB
-				if (ctx->db)
+				if (ctx->usedb)
 					tdb->close( tdb, 0 );
 #endif /* USE_DB */
 				return DRV_BOX_BAD;
@@ -745,7 +790,9 @@ maildir_scan( maildir_store_t *ctx, msglist_t *msglist )
 				ctx->gen.count++;
 				ctx->gen.recent += i;
 #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 );
 					if ((ret = ctx->db->get( ctx->db, 0, &key, &value, 0 ))) {
 						if (ret != DB_NOTFOUND) {
@@ -802,7 +849,7 @@ maildir_scan( maildir_store_t *ctx, msglist_t *msglist )
 			if (st.st_mtime != stamps[i]) {
 				/* Somebody messed with the mailbox since we started listing it. */
 #ifdef USE_DB
-				if (ctx->db)
+				if (ctx->usedb)
 					tdb->close( tdb, 0 );
 #endif /* USE_DB */
 				maildir_free_scan( msglist );
@@ -810,8 +857,10 @@ maildir_scan( maildir_store_t *ctx, msglist_t *msglist )
 			}
 		}
 #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()" );
 			else {
 				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))
 					nfsnprintf( buf + bl, sizeof(buf) - bl, "%s/%s", subdirs[entry->recent], entry->base );
 #ifdef USE_DB
-			} else if (ctx->db) {
+			} else if (ctx->usedb) {
 				if ((ret = maildir_set_uid( ctx, entry->base, &uid )) != DRV_OK) {
 					maildir_free_scan( msglist );
 					return ret;
@@ -982,6 +1031,7 @@ maildir_select_box( store_t *gctx, const char *name )
 	ctx->uvfd = -1;
 #ifdef USE_DB
 	ctx->db = 0;
+	ctx->usedb = 0;
 #endif /* USE_DB */
 	ctx->fresh[0] = ctx->fresh[1] = 0;
 	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;
 	int ret;
-#ifdef USE_DB
-	struct stat st;
-#endif /* USE_DB */
 	char uvpath[_POSIX_PATH_MAX];
 
 	if ((ret = maildir_validate( gctx->path, 0, ctx )) != DRV_OK)
@@ -1018,6 +1065,7 @@ maildir_open_box( store_t *gctx,
 		return;
 	}
 #else
+	ctx->usedb = 0;
 	if ((ctx->uvfd = open( uvpath, O_RDWR, 0600 )) < 0) {
 		nfsnprintf( uvpath, sizeof(uvpath), "%s/.isyncuidmap.db", gctx->path );
 		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 );
 			cb( DRV_BOX_BAD, aux );
 			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 {
-			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:
 #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 );
 	if (!to_trash) {
 #ifdef USE_DB
-		if (ctx->db) {
+		if (ctx->usedb) {
 			if ((ret = maildir_set_uid( ctx, base, &uid )) != DRV_OK) {
 				free( data->data );
 				cb( ret, 0, aux );
@@ -1480,6 +1486,8 @@ maildir_purge_msg( maildir_store_t *ctx, const char *name )
 {
 	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 );
 	if ((ret = ctx->db->del( ctx->db, 0, &key, 0 ))) {
 		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--;
 
 #ifdef USE_DB
-	if (ctx->db) {
+	if (ctx->usedb) {
 		cb( maildir_purge_msg( ctx, msg->base ), aux );
 		return;
 	}