[dovecot-cvs] dovecot/src/lib-index/maildir maildir-build.c,1.24,1.25 maildir-expunge.c,1.4,1.5 maildir-index.c,1.34,1.35 maildir-index.h,1.24,1.25 maildir-open.c,1.17,1.18 maildir-sync.c,1.59,1.60 maildir-uidlist.c,1.9,1.10 maildir-update-flags.c,1.7,1.8

cras at procontrol.fi cras at procontrol.fi
Mon Aug 11 03:56:25 EEST 2003


Update of /home/cvs/dovecot/src/lib-index/maildir
In directory danu:/tmp/cvs-serv17929/lib-index/maildir

Modified Files:
	maildir-build.c maildir-expunge.c maildir-index.c 
	maildir-index.h maildir-open.c maildir-sync.c 
	maildir-uidlist.c maildir-update-flags.c 
Log Message:
Maildir syncing works now without requiring base filenames to be in index
cache file. Also message flag updates with +FLAGS and -FLAGS works correctly
now if another client had just changed it's flags.



Index: maildir-build.c
===================================================================
RCS file: /home/cvs/dovecot/src/lib-index/maildir/maildir-build.c,v
retrieving revision 1.24
retrieving revision 1.25
diff -u -d -r1.24 -r1.25
--- maildir-build.c	6 Aug 2003 20:15:32 -0000	1.24
+++ maildir-build.c	10 Aug 2003 23:56:23 -0000	1.25
@@ -4,40 +4,34 @@
 #include "maildir-index.h"
 #include "mail-cache.h"
 
-int maildir_index_append_file(struct mail_cache_transaction_ctx **trans_ctx,
-			      struct mail_index *index, const char *fname,
+int maildir_cache_update_file(struct mail_cache_transaction_ctx **trans_ctx,
+			      struct mail_index *index,
+			      struct mail_index_record *rec, const char *fname,
 			      int new_dir)
 {
-	struct mail_index_record *rec;
+	enum mail_cache_field cached_fields;
         enum mail_index_record_flag index_flags;
 	uoff_t virtual_size;
 	const char *p;
 
-	i_assert(index->lock_type == MAIL_LOCK_EXCLUSIVE);
-
 	if (*trans_ctx == NULL) {
 		if (mail_cache_transaction_begin(index->cache,
 						 TRUE, trans_ctx) <= 0)
 			return FALSE;
 	}
 
-	rec = index->append(index);
-	if (rec == NULL)
-		return FALSE;
-
-	/* set message flags from file name */
-	rec->msg_flags = maildir_filename_get_flags(fname, 0);
-	mail_index_mark_flag_changes(index, rec, 0, rec->msg_flags);
-
-	/* always set index flags */
-	index_flags = new_dir ? MAIL_INDEX_FLAG_MAILDIR_NEW : 0;
-	if (!mail_cache_add(*trans_ctx, rec, MAIL_CACHE_INDEX_FLAGS,
-			    &index_flags, sizeof(index_flags)))
-		return FALSE;
+	cached_fields = mail_cache_get_fields(index->cache, rec);
+	if ((cached_fields & MAIL_CACHE_INDEX_FLAGS) == 0) {
+		/* always set index flags */
+		index_flags = new_dir ? MAIL_INDEX_FLAG_MAILDIR_NEW : 0;
+		if (!mail_cache_add(*trans_ctx, rec, MAIL_CACHE_INDEX_FLAGS,
+				    &index_flags, sizeof(index_flags)))
+			return FALSE;
+	}
 
 	/* set virtual size if found from file name */
 	p = strstr(fname, ",W=");
-	if (p != NULL) {
+	if (p != NULL && (cached_fields & MAIL_CACHE_VIRTUAL_FULL_SIZE) == 0) {
 		p += 3;
 		virtual_size = 0;
 		while (*p >= '0' && *p <= '9') {
@@ -54,10 +48,29 @@
 		}
 	}
 
-	/* always set location */
-	if (!mail_cache_add(*trans_ctx, rec, MAIL_CACHE_LOCATION,
-			    fname, strlen(fname)+1))
-		return FALSE;
+	if ((cached_fields & MAIL_CACHE_LOCATION) == 0) {
+		/* always set location */
+		if (!mail_cache_add(*trans_ctx, rec, MAIL_CACHE_LOCATION,
+				    fname, strlen(fname)+1))
+			return FALSE;
+	}
 
 	return TRUE;
+}
+
+int maildir_index_append_file(struct mail_cache_transaction_ctx **trans_ctx,
+			      struct mail_index *index, const char *fname,
+			      int new_dir)
+{
+	struct mail_index_record *rec;
+
+	rec = index->append(index);
+	if (rec == NULL)
+		return FALSE;
+
+	/* set message flags from file name */
+	rec->msg_flags = maildir_filename_get_flags(fname, 0);
+	mail_index_mark_flag_changes(index, rec, 0, rec->msg_flags);
+
+        return maildir_cache_update_file(trans_ctx, index, rec, fname, new_dir);
 }

Index: maildir-expunge.c
===================================================================
RCS file: /home/cvs/dovecot/src/lib-index/maildir/maildir-expunge.c,v
retrieving revision 1.4
retrieving revision 1.5
diff -u -d -r1.4 -r1.5
--- maildir-expunge.c	6 Aug 2003 20:15:32 -0000	1.4
+++ maildir-expunge.c	10 Aug 2003 23:56:23 -0000	1.5
@@ -7,82 +7,44 @@
 
 #include <unistd.h>
 
-static int maildir_expunge_mail_file(struct mail_index *index,
-				     struct mail_index_record *rec,
-				     const char **fname)
+static int do_expunge(struct mail_index *index, const char *path, void *context)
 {
-	const char *path;
-	int new_dir;
-
-	*fname = maildir_get_location(index, rec, &new_dir);
-	if (*fname == NULL)
-		return -1;
-
-	/* if we're in out-of-space condition, reset it since we'll probably
-	   have enough space now. */
-	index->maildir_keep_new = FALSE;
-	if (index->next_dirty_flush != 0)
-		index->next_dirty_flush = ioloop_time;
+	int *found = context;
 
-	if (new_dir) {
-		/* probably in new/ dir */
-		path = t_strconcat(index->mailbox_path, "/new/", *fname, NULL);
-		if (unlink(path) == 0)
+	if (unlink(path) < 0) {
+		if (errno == ENOENT)
+			return 0;
+		if (errno == EACCES) {
+			index->mailbox_readonly = TRUE;
 			return 1;
-
-		if (errno == EACCES)
-			return -1;
-		if (errno != ENOENT) {
-			index_set_error(index, "unlink(%s) failed: %m", path);
-			return -1;
 		}
-	}
-
-	path = t_strconcat(index->mailbox_path, "/cur/", *fname, NULL);
-	if (unlink(path) == 0)
-		return 1;
-
-	if (errno == EACCES)
-		return -1;
 
-	if (errno != ENOENT) {
 		index_set_error(index, "unlink(%s) failed: %m", path);
 		return -1;
 	}
 
-	return 0;
+	*found = TRUE;
+	return 1;
 }
 
 int maildir_expunge_mail(struct mail_index *index,
 			 struct mail_index_record *rec)
 {
-	const char *fname;
-	int i, ret, found;
-
-	for (i = 0;; i++) {
-		ret = maildir_expunge_mail_file(index, rec, &fname);
-		if (ret > 0)
-			break;
-		if (ret < 0)
-			return FALSE;
+	int found = FALSE;
 
-		if (i == 10) {
-			index_set_error(index, "Filename keeps changing, "
-					"expunge failed: %s", fname);
-			return FALSE;
-		}
+	if (!maildir_file_do(index, rec, do_expunge, &found))
+		return FALSE;
 
-		if (!maildir_index_sync_readonly(index, fname, &found))
-			return FALSE;
+	if (found) {
+		/* if we're in out-of-space condition, reset it since we'll
+		   probably have enough space now. */
+		index->maildir_keep_new = FALSE;
+		if (index->next_dirty_flush != 0)
+			index->next_dirty_flush = ioloop_time;
 
-		if (!found) {
-			/* syncing didn't find it, it's already deleted */
-			return TRUE;
-		}
+		/* cur/ was updated, set it dirty-synced */
+		index->maildir_cur_dirty = ioloop_time;
+		index->file_sync_stamp = ioloop_time;
 	}
-
-	/* cur/ was updated, set it dirty-synced */
-	index->maildir_cur_dirty = ioloop_time;
-	index->file_sync_stamp = ioloop_time;
 	return TRUE;
 }

Index: maildir-index.c
===================================================================
RCS file: /home/cvs/dovecot/src/lib-index/maildir/maildir-index.c,v
retrieving revision 1.34
retrieving revision 1.35
diff -u -d -r1.34 -r1.35
--- maildir-index.c	6 Aug 2003 20:15:32 -0000	1.34
+++ maildir-index.c	10 Aug 2003 23:56:23 -0000	1.35
@@ -45,12 +45,11 @@
 		}
 	}
 
-	/* index file should give us at least the base name. */
+	/* cache file file should give us at least the base name. */
 	fname = mail_cache_lookup_string_field(index->cache, rec,
 					       MAIL_CACHE_LOCATION);
 	if (fname == NULL) {
-		mail_cache_set_corrupted(index->cache,
-			"Missing location field for record %u", rec->uid);
+		/* Not cached, we'll have to resync the directory. */
 		return NULL;
 	}
 
@@ -62,6 +61,54 @@
 	return fname;
 }
 
+static int
+maildir_file_do_try(struct mail_index *index, struct mail_index_record *rec,
+		    const char **fname,
+		    maildir_file_do_func *func, void *context)
+{
+	const char *path;
+	int ret, new_dir;
+
+	*fname = maildir_get_location(index, rec, &new_dir);
+	if (*fname == NULL)
+		return 0;
+
+	if (new_dir) {
+		/* probably in new/ dir */
+		path = t_strconcat(index->mailbox_path, "/new/", *fname, NULL);
+		ret = func(index, path, context);
+		if (ret != 0)
+			return ret;
+	}
+
+	path = t_strconcat(index->mailbox_path, "/cur/", *fname, NULL);
+	return func(index, path, context);
+}
+
+int maildir_file_do(struct mail_index *index, struct mail_index_record *rec,
+		    maildir_file_do_func *func, void *context)
+{
+	const char *fname;
+	int i, ret, found;
+
+	ret = maildir_file_do_try(index, rec, &fname, func, context);
+	for (i = 0; i < 10 && ret == 0; i++) {
+		/* file is either renamed or deleted. sync the maildir and
+		   see which one. if file appears to be renamed constantly,
+		   don't try to open it more than 10 times. */
+		fname = t_strdup(fname);
+		if (!maildir_index_sync_readonly(index, fname, &found))
+			return FALSE;
+
+		if (!found && fname != NULL)
+			return TRUE;
+
+		ret = maildir_file_do_try(index, rec, &fname, func, context);
+	}
+
+	return ret >= 0;
+}
+
 const char *maildir_generate_tmp_filename(const struct timeval *tv)
 {
 	static unsigned int create_count = 0;
@@ -291,46 +338,27 @@
 	i_free(index);
 }
 
-static int maildir_get_received_date_file(struct mail_index *index,
-					  struct mail_index_record *rec,
-					  const char **fname, struct stat *st)
+static int do_get_received_date(struct mail_index *index,
+				const char *path, void *context)
 {
-	const char *path;
-	int new_dir;
-
-	/* stat() gives it */
-	*fname = maildir_get_location(index, rec, &new_dir);
-	if (*fname == NULL)
-		return -1;
-
-	if (new_dir) {
-		/* probably in new/ dir */
-		path = t_strconcat(index->mailbox_path, "/new/", *fname, NULL);
-		if (stat(path, st) < 0 && errno != ENOENT) {
-			index_file_set_syscall_error(index, path, "stat()");
-			return -1;
-		}
-	}
+	time_t *date = context;
+	struct stat st;
 
-	path = t_strconcat(index->mailbox_path, "/cur/", *fname, NULL);
-	if (stat(path, st) < 0) {
+	if (stat(path, &st) < 0) {
 		if (errno == ENOENT)
 			return 0;
-
 		index_file_set_syscall_error(index, path, "stat()");
 		return -1;
 	}
 
-	return TRUE;
+	*date = st.st_mtime;
+	return 1;
 }
 
 static time_t maildir_get_received_date(struct mail_index *index,
 					struct mail_index_record *rec)
 {
-	struct stat st;
-	const char *fname;
 	time_t date;
-	int ret, i, found;
 
 	/* try getting it from cache */
 	if (mail_cache_copy_fixed_field(index->cache, rec,
@@ -338,23 +366,11 @@
 					&date, sizeof(date)))
 		return date;
 
-	ret = maildir_get_received_date_file(index, rec, &fname, &st);
-	for (i = 0; ret == 0 && i < 10; i++) {
-		/* file is either renamed or deleted. sync the maildir and
-		   see which one. if file appears to be renamed constantly,
-		   don't try to open it more than 10 times. */
-		if (!maildir_index_sync_readonly(index, fname, &found))
-			return FALSE;
-
-		if (!found) {
-			/* syncing didn't find it, it's deleted */
-			return (time_t)-1;
-		}
-
-		ret = maildir_get_received_date_file(index, rec, &fname, &st);
-	}
+	date = (time_t)-1;
+	if (!maildir_file_do(index, rec, do_get_received_date, &date))
+		return (time_t)-1;
 
-	return st.st_mtime;
+	return date;
 }
 
 struct mail_index maildir_index = {

Index: maildir-index.h
===================================================================
RCS file: /home/cvs/dovecot/src/lib-index/maildir/maildir-index.h,v
retrieving revision 1.24
retrieving revision 1.25
diff -u -d -r1.24 -r1.25
--- maildir-index.h	6 Aug 2003 20:15:32 -0000	1.24
+++ maildir-index.h	10 Aug 2003 23:56:23 -0000	1.25
@@ -9,6 +9,10 @@
 /* How often to try to flush dirty flags. */
 #define MAILDIR_DIRTY_FLUSH_TIMEOUT (60*5)
 
+/* Return -1 = error, 0 = file not found, 1 = ok */
+typedef int maildir_file_do_func(struct mail_index *index,
+				 const char *path, void *context);
+
 struct mail_index *
 maildir_index_alloc(const char *maildir, const char *index_dir,
 		    const char *control_dir);
@@ -20,6 +24,8 @@
 
 const char *maildir_get_location(struct mail_index *index,
 				 struct mail_index_record *rec, int *new_dir);
+int maildir_file_do(struct mail_index *index, struct mail_index_record *rec,
+		    maildir_file_do_func *func, void *context);
 enum mail_flags maildir_filename_get_flags(const char *fname,
 					   enum mail_flags default_flags);
 const char *maildir_filename_set_flags(const char *fname,
@@ -32,11 +38,16 @@
 int maildir_index_sync(struct mail_index *index, int minimal_sync,
 		       enum mail_lock_type lock_type, int *changes);
 
+int maildir_cache_update_file(struct mail_cache_transaction_ctx **trans_ctx,
+			      struct mail_index *index,
+			      struct mail_index_record *rec, const char *fname,
+			      int new_dir);
 int maildir_index_append_file(struct mail_cache_transaction_ctx **trans_ctx,
 			      struct mail_index *index, const char *fname,
 			      int new_dir);
 int maildir_index_update_flags(struct mail_index *index,
 			       struct mail_index_record *rec, unsigned int seq,
+			       enum modify_type modify_type,
 			       enum mail_flags flags, int external_change);
 int maildir_try_flush_dirty_flags(struct mail_index *index, int force);
 

Index: maildir-open.c
===================================================================
RCS file: /home/cvs/dovecot/src/lib-index/maildir/maildir-open.c,v
retrieving revision 1.17
retrieving revision 1.18
diff -u -d -r1.17 -r1.18
--- maildir-open.c	6 Aug 2003 20:15:32 -0000	1.17
+++ maildir-open.c	10 Aug 2003 23:56:23 -0000	1.18
@@ -10,42 +10,18 @@
 #include <fcntl.h>
 #include <sys/stat.h>
 
-static int maildir_open_mail_file(struct mail_index *index,
-				  struct mail_index_record *rec,
-				  const char **fname, int *deleted)
+static int do_open(struct mail_index *index, const char *path, void *context)
 {
-	const char *path;
-	int new_dir, fd = -1;
-
-	*fname = maildir_get_location(index, rec, &new_dir);
-	if (*fname == NULL)
-		return -1;
-
-	if (new_dir) {
-		/* probably in new/ dir */
-		path = t_strconcat(index->mailbox_path, "/new/", *fname, NULL);
-		fd = open(path, O_RDONLY);
-		if (fd == -1 && errno != ENOENT) {
-			index_set_error(index, "open(%s) failed: %m", path);
-			return -1;
-		}
-	}
-
-	if (fd == -1) {
-		path = t_strconcat(index->mailbox_path, "/cur/", *fname, NULL);
-		fd = open(path, O_RDONLY);
-		if (fd == -1) {
-			if (errno == ENOENT) {
-				*deleted = TRUE;
-				return -1;
-			}
+	int *fd = context;
 
-			index_set_error(index, "open(%s) failed: %m", path);
-			return -1;
-		}
-	}
+	*fd = open(path, O_RDONLY);
+	if (*fd != -1)
+		return 1;
+	if (errno == ENOENT)
+		return 0;
 
-	return fd;
+	index_file_set_syscall_error(index, path, "open()");
+	return -1;
 }
 
 struct istream *maildir_open_mail(struct mail_index *index,
@@ -53,8 +29,7 @@
 				  time_t *received_date, int *deleted)
 {
 	struct stat st;
-	const char *fname;
-	int i, found, fd;
+	int fd;
 
 	i_assert(index->lock_type != MAIL_LOCK_UNLOCK);
 
@@ -64,24 +39,13 @@
 	if (index->inconsistent)
 		return NULL;
 
-	fd = maildir_open_mail_file(index, rec, &fname, deleted);
-	for (i = 0; fd == -1 && *deleted && i < 10; i++) {
-		/* file is either renamed or deleted. sync the maildir and
-		   see which one. if file appears to be renamed constantly,
-		   don't try to open it more than 10 times. */
-		if (!maildir_index_sync_readonly(index, fname, &found)) {
-			*deleted = FALSE;
-			return NULL;
-		}
-
-		if (!found) {
-			/* syncing didn't find it, it's deleted */
-			return NULL;
-		}
+	fd = -1;
+	if (!maildir_file_do(index, rec, do_open, &fd))
+		return NULL;
 
-		fd = maildir_open_mail_file(index, rec, &fname, deleted);
-		if (fd == -1)
-			return NULL;
+	if (fd == -1) {
+		*deleted = TRUE;
+		return NULL;
 	}
 
 	if (received_date != NULL) {

Index: maildir-sync.c
===================================================================
RCS file: /home/cvs/dovecot/src/lib-index/maildir/maildir-sync.c,v
retrieving revision 1.59
retrieving revision 1.60
diff -u -d -r1.59 -r1.60
--- maildir-sync.c	6 Aug 2003 20:15:32 -0000	1.59
+++ maildir-sync.c	10 Aug 2003 23:56:23 -0000	1.60
@@ -241,6 +241,7 @@
 	unsigned int uidlist_rewrite:1;
 	unsigned int new_mails_new:1;
 	unsigned int new_mails_cur:1;
+	unsigned int have_uncached_filenames:1;
 };
 
 static int maildir_sync_cur_dir(struct maildir_sync_context *ctx);
@@ -288,7 +289,7 @@
 	flags = maildir_filename_get_flags(new_fname, rec->msg_flags);
 	if (flags != rec->msg_flags) {
 		if (!ctx->index->update_flags(ctx->index, rec,
-					      seq, flags, TRUE))
+					      seq, MODIFY_REPLACE, flags, TRUE))
 			return FALSE;
 	}
 
@@ -304,7 +305,8 @@
 	if (ctx->uidlist != NULL)
 		return TRUE;
 
-	/* open it only if it's changed since we last synced it. */
+	/* open it only if it's changed since we last synced it,
+	   or if we have uncached filenames. */
 	path = t_strconcat(index->control_dir, "/" MAILDIR_UIDLIST_NAME, NULL);
 	if (stat(path, &st) < 0) {
 		if (errno == ENOENT) {
@@ -323,7 +325,8 @@
 	}
 
 	/* FIXME: last_uidlist_mtime should be in index headers */
-	if (st.st_mtime == index->last_uidlist_mtime)
+	if (st.st_mtime == index->last_uidlist_mtime &&
+	    !ctx->have_uncached_filenames)
 		return TRUE;
 
 	ctx->uidlist = maildir_uidlist_open(index);
@@ -478,15 +481,24 @@
 		}
 
 		fname = maildir_get_location(index, rec, NULL);
-		if (fname == NULL)
-			return FALSE;
+		if (fname == NULL) {
+			/* filename not cached, it must be in uidlist or
+			   it's expunged */
+			fname = uid_rec.uid == rec->uid ?
+				uid_rec.filename : NULL;
+		}
 
-		if (!hash_lookup_full(ctx->files, fname,
-				      &orig_key, &orig_value)) {
-			/* none action */
+		if (fname == NULL) {
 			hash_rec = NULL;
-		} else {
+			action = MAILDIR_FILE_ACTION_EXPUNGE;
+		} else if (hash_lookup_full(ctx->files, fname,
+					    &orig_key, &orig_value)) {
 			hash_rec = orig_value;
+			action = ACTION(hash_rec);
+		} else {
+			/* none action */
+			hash_rec = NULL;
+			action = MAILDIR_FILE_ACTION_NONE;
 		}
 
 		if (uid_rec.uid == uid &&
@@ -498,8 +510,8 @@
 		}
 
 		if (uid_rec.uid > uid && hash_rec != NULL &&
-		    (ACTION(hash_rec) == MAILDIR_FILE_ACTION_UPDATE_FLAGS ||
-		     ACTION(hash_rec) == MAILDIR_FILE_ACTION_NONE)) {
+		    (action == MAILDIR_FILE_ACTION_UPDATE_FLAGS ||
+		     action == MAILDIR_FILE_ACTION_NONE)) {
 			/* it's UID has changed. shouldn't happen. */
 			index_set_corrupted(index,
 					    "UID changed for %s/%s: %u -> %u",
@@ -508,8 +520,6 @@
 			return FALSE;
 		}
 
-		action = hash_rec != NULL ?
-			ACTION(hash_rec) : MAILDIR_FILE_ACTION_NONE;
 		switch (action) {
 		case MAILDIR_FILE_ACTION_EXPUNGE:
 			if (first_rec == NULL) {
@@ -519,12 +529,22 @@
 			last_rec = rec;
 			last_seq = seq;
 			break;
+		case MAILDIR_FILE_ACTION_NEW:
+			/* filename wasn't cached */
+			new_flag = hash_rec->action & MAILDIR_FILE_FLAG_NEWDIR;
+			hash_rec->action = MAILDIR_FILE_ACTION_NONE | new_flag;
+			ctx->new_count--;
+
+			if (!maildir_cache_update_file(&ctx->trans_ctx, index,
+						       rec, fname, new_flag))
+				return FALSE;
+			/* fall through */
 		case MAILDIR_FILE_ACTION_UPDATE_FLAGS:
 			new_dir = (hash_rec->action &
 				   MAILDIR_FILE_FLAG_NEWDIR) != 0;
 			maildir_index_update_filename(index, rec->uid,
 						      orig_key, new_dir);
-			if (!maildir_update_flags(ctx, rec, seq, fname))
+			if (!maildir_update_flags(ctx, rec, seq, orig_key))
 				return FALSE;
 			/* fall through */
 		case MAILDIR_FILE_ACTION_NONE:
@@ -540,8 +560,7 @@
 			}
 			break;
 		default:
-			i_panic("BUG: %s/%s suddenly appeared as UID %u",
-				index->mailbox_path, (char *) orig_key, uid);
+			i_unreached();
 		}
 
 		if (uid_rec.uid == uid) {
@@ -565,9 +584,9 @@
 	}
 
 	if (seq-1 != index->header->messages_count) {
-		index_set_corrupted(index, "Wrong messages_count in header "
-				    "(%u != %u)", seq,
-				    index->header->messages_count);
+		index_set_corrupted(index,
+				    "Wrong messages_count in header (%u != %u)",
+				    seq, index->header->messages_count);
 		return FALSE;
 	}
 
@@ -660,12 +679,6 @@
 	size_t size;
 	int new_dir, have_new;
 
-	/* kludge. we want to have pointers to data file, so we must make sure
-	   that it's base address doesn't change. this call makes sure it's
-	   fully mmaped in memory even when we begin */
-	if (mail_cache_get_mmaped(index->cache, &size) == NULL)
-		return FALSE;
-
 	if (index->header->messages_count >= INT_MAX/32) {
 		index_set_corrupted(index, "Header says %u messages",
 				    index->header->messages_count);
@@ -696,16 +709,26 @@
 
 	have_new = FALSE;
 
+	/* Now we'll fill the hash with cached filenames. This is done mostly
+	   just to save some memory since we can use pointers to mmaped cache
+	   file. Note that all records may not have the filename cached.
+
+	   WARNING: Cache file must not be modified as long as these pointers
+	   exist, as modifying might change the mmap base address. The call
+	   below makes sure that cache file is initially fully mmaped. */
+	if (mail_cache_get_mmaped(index->cache, &size) == NULL)
+		return FALSE;
+
 	rec = index->lookup(index, 1);
 	while (rec != NULL) {
 		fname = maildir_get_location(index, rec, &new_dir);
 		if (fname == NULL)
-			return FALSE;
+                        ctx->have_uncached_filenames = TRUE;
 
 		if (new_dir)
 			have_new = TRUE;
 
-		if (!only_new || new_dir) {
+		if ((!only_new || new_dir) && fname != NULL) {
 			hash_rec = p_new(ctx->pool, struct maildir_hash_rec, 1);
 			hash_rec->rec = rec;
 			hash_rec->action = MAILDIR_FILE_ACTION_EXPUNGE;
@@ -716,9 +739,6 @@
 				return FALSE;
 			}
 
-			/* WARNING: index must not be modified as long as
-			   these hash keys exist. Modifying might change the
-			   mmap base address. */
 			hash_insert(ctx->files, (void *) fname, hash_rec);
 		}
 
@@ -756,7 +776,7 @@
 	return ret;
 }
 
-static void uidlist_hash_fix_allocs(void *key, void *value, void *context)
+static void maildir_sync_hash_fix_allocs(void *key, void *value, void *context)
 {
         struct maildir_sync_context *ctx = context;
 	struct maildir_hash_rec *hash_rec = value;
@@ -808,7 +828,8 @@
 
 		if (hash_rec->rec == NULL) {
 			/* new message */
-			if (ctx->readonly_check)
+			if (ctx->readonly_check &&
+			    !ctx->have_uncached_filenames)
 				continue;
 
 			if (new_dir)
@@ -836,9 +857,9 @@
 	} while ((d = readdir(dirp)) != NULL);
 
 	/* records that are left to hash must not have any (filename) pointers
-	   to index file. So remove none actions, and p_strdup() expunge
+	   to cache file. So remove none actions, and p_strdup() expunge
 	   actions. */
-	hash_foreach(ctx->files, uidlist_hash_fix_allocs, ctx);
+	hash_foreach(ctx->files, maildir_sync_hash_fix_allocs, ctx);
 
 	return TRUE;
 }
@@ -1108,8 +1129,8 @@
 			return FALSE;
 
 		/* this will set maildir_cur_dirty. it may actually be
-		   different from cur/'s mtime if we're unlucky, but that
-		   doesn't really matter and it's not worth the extra stat() */
+		   different from cur/'s mtime if we're unlucky, but that only
+		   causes extra sync and it's not worth the extra stat() */
 		if (ctx->new_dent == NULL &&
 		    (ctx->new_count == 0 || !ctx->new_mails_new))
 			cur_mtime = time(NULL);
@@ -1155,41 +1176,74 @@
 	struct mail_index *index = ctx->index;
 	struct mail_index_record *rec;
 	struct maildir_hash_rec *hash_rec;
+	struct maildir_uidlist *uidlist;
+	struct maildir_uidlist_rec uid_rec;
 	void *orig_key, *orig_value;
 	const char *fname;
 	unsigned int seq;
-	int new_dir;
+	int new_dir, tried_uidlist;
 
-	if (!ctx->flag_updates) {
+	if (!ctx->flag_updates && !ctx->have_uncached_filenames) {
 		ctx->index->maildir_synced_once = TRUE;
 		return TRUE;
 	}
 
+	memset(&uid_rec, 0, sizeof(uid_rec));
+	uidlist = ctx->uidlist;
+	tried_uidlist = FALSE;
+
 	rec = index->lookup(index, 1); seq = 1;
-	while (rec != NULL) {
+	for (; rec != NULL; rec = index->next(index, rec), seq++) {
 		fname = maildir_get_location(index, rec, NULL);
-		if (fname == NULL)
-			return FALSE;
+		if (fname == NULL) {
+			/* not cached, get it from uidlist */
+			if (uidlist == NULL && !tried_uidlist) {
+				ctx->have_uncached_filenames = TRUE;
+				if (!maildir_sync_open_uidlist(ctx))
+					return FALSE;
 
-		if (hash_lookup_full(ctx->files, fname, &orig_key, &orig_value))
-			hash_rec = orig_value;
-		else
-			hash_rec = NULL;
+				uidlist = ctx->uidlist;
+				tried_uidlist = TRUE;
 
-		if (hash_rec != NULL &&
-		    ACTION(hash_rec) == MAILDIR_FILE_ACTION_UPDATE_FLAGS) {
-			new_dir = (hash_rec->action &
-				   MAILDIR_FILE_FLAG_NEWDIR) != 0;
-			maildir_index_update_filename(index, rec->uid,
-						      orig_key, new_dir);
+				/* get the initial record */
+				if (uidlist != NULL &&
+				    maildir_uidlist_next(uidlist, &uid_rec) < 0)
+					return FALSE;
+			}
 
-			if (index->lock_type == MAIL_LOCK_EXCLUSIVE) {
-				if (!maildir_update_flags(ctx, rec, seq, fname))
+			if (uidlist == NULL) {
+				/* uidlist doesn't exist? shouldn't happen */
+				continue;
+			}
+
+			while (uid_rec.uid != 0 && uid_rec.uid < rec->uid) {
+				if (maildir_uidlist_next(uidlist, &uid_rec) < 0)
 					return FALSE;
 			}
+
+			if (uid_rec.uid != rec->uid) {
+				/* not in uidlist, it's expunged */
+				continue;
+			}
+
+			fname = uid_rec.filename;
 		}
 
-		rec = index->next(index, rec); seq++;
+		if (!hash_lookup_full(ctx->files, fname,
+				      &orig_key, &orig_value))
+			continue;
+
+		hash_rec = orig_value;
+		if (ACTION(hash_rec) != MAILDIR_FILE_ACTION_UPDATE_FLAGS &&
+		    ACTION(hash_rec) != MAILDIR_FILE_ACTION_NEW)
+			continue;
+
+		new_dir = (hash_rec->action & MAILDIR_FILE_FLAG_NEWDIR) != 0;
+		maildir_index_update_filename(index, rec->uid,
+					      orig_key, new_dir);
+
+		if (!maildir_update_flags(ctx, rec, seq, orig_key))
+			return FALSE;
 	}
 
 	ctx->index->maildir_synced_once = TRUE;
@@ -1298,7 +1352,7 @@
 
 	ret = maildir_index_sync_context_readonly(ctx);
 
-	if (!ret || ctx->files == NULL)
+	if (!ret || ctx->files == NULL || fname == NULL)
 		*found = FALSE;
 	else {
 		hash_rec = hash_lookup(ctx->files, fname);

Index: maildir-uidlist.c
===================================================================
RCS file: /home/cvs/dovecot/src/lib-index/maildir/maildir-uidlist.c,v
retrieving revision 1.9
retrieving revision 1.10
diff -u -d -r1.9 -r1.10
--- maildir-uidlist.c	6 Aug 2003 20:15:32 -0000	1.9
+++ maildir-uidlist.c	10 Aug 2003 23:56:23 -0000	1.10
@@ -156,8 +156,8 @@
 	rec = index->lookup(index, 1);
 	while (rec != NULL) {
 		fname = maildir_get_location(index, rec, NULL);
-		if (fname == NULL)
-			return FALSE;
+		/* maildir should be synced, so above call should never fail */
+		i_assert(fname != NULL);
 
 		p = strchr(fname, ':');
 		len = p == NULL ? strlen(fname) : (size_t)(p-fname);

Index: maildir-update-flags.c
===================================================================
RCS file: /home/cvs/dovecot/src/lib-index/maildir/maildir-update-flags.c,v
retrieving revision 1.7
retrieving revision 1.8
diff -u -d -r1.7 -r1.8
--- maildir-update-flags.c	6 Aug 2003 20:15:32 -0000	1.7
+++ maildir-update-flags.c	10 Aug 2003 23:56:23 -0000	1.8
@@ -8,6 +8,15 @@
 #include "mail-cache.h"
 
 #include <stdio.h>
+#include <sys/stat.h>
+
+struct update_flags_ctx {
+	const char *new_fname;
+	int found;
+
+        enum modify_type modify_type;
+	enum mail_flags flags;
+};
 
 static int update_filename(struct mail_index *index,
 			   struct mail_index_record *rec)
@@ -91,153 +100,132 @@
 	return TRUE;
 }
 
-static int handle_error(struct mail_index *index,
-			const char *path, const char *new_path)
+static int do_rename(struct mail_index *index, const char *path, void *context)
 {
-	if (errno == ENOENT)
-		return 0;
+	struct update_flags_ctx *ctx = context;
+	const char *fname, *new_path;
+	enum mail_flags old_flags, new_flags;
+	int new_dir;
 
-	if (ENOSPACE(errno)) {
-		index->nodiskspace = TRUE;
-		return -2;
+        old_flags = maildir_filename_get_flags(path, 0);
+	switch (ctx->modify_type) {
+	case MODIFY_ADD:
+		new_flags = old_flags | ctx->flags;
+		break;
+	case MODIFY_REMOVE:
+		new_flags = old_flags & ~ctx->flags;
+		break;
+	case MODIFY_REPLACE:
+		new_flags = ctx->flags;
+		break;
+	default:
+		new_flags = 0;
+		i_unreached();
 	}
 
-	if (errno == EACCES)
-		index->mailbox_readonly = TRUE;
-	else {
-		index_set_error(index, "rename(%s, %s) failed: %m",
-				path, new_path);
-	}
+	fname = strrchr(path, '/');
+	ctx->new_fname = maildir_filename_set_flags(fname != NULL ?
+						    fname+1 : path, new_flags);
 
-	return -1;
-}
+	if (old_flags == new_flags) {
+		/* it's what we wanted. verify that the file exists. */
+		struct stat st;
 
-static int maildir_rename_mail_file(struct mail_index *index, int new_dir,
-				    const char *old_fname, const char *new_path)
-{
-	const char *path;
+		if (stat(path, &st) < 0) {
+			if (errno == ENOENT)
+				return 0;
+			index_file_set_syscall_error(index, path, "stat()");
+			return -1;
+		}
+		ctx->found = TRUE;
+		return 1;
+	}
 
+	new_dir = fname != NULL && path + 4 <= fname &&
+		strncmp(fname-4, "/new", 4) == 0;
 	if (new_dir) {
-		/* probably in new/ dir */
-		path = t_strconcat(index->mailbox_path, "/new/",
-				   old_fname, NULL);
-		if (rename(path, new_path) == 0)
-			return 1;
-
-		if (errno != ENOENT)
-			return handle_error(index, path, new_path);
+		/* move from new/ to cur/ */
+		new_path = t_strconcat(t_strdup_until(path, fname-4),
+				       "/cur/", ctx->new_fname, NULL);
+	} else {
+		new_path = maildir_filename_set_flags(path, new_flags);
 	}
 
-	path = t_strconcat(index->mailbox_path, "/cur/", old_fname, NULL);
-	if (rename(path, new_path) == 0)
-		return 1;
-
-	return handle_error(index, path, new_path);
-}
-
-static int maildir_rename_mail(struct mail_index *index,
-			       struct mail_index_record *rec,
-			       enum mail_flags flags, const char **new_fname_r)
-{
-	const char *old_fname, *new_fname, *new_path;
-        enum mail_index_record_flag index_flags;
-	int i, ret, found, new_dir;
-
-	new_fname = new_path = NULL;
-
-	i = 0;
-	do {
-		/* we need to update the flags in the file name */
-		old_fname = maildir_get_location(index, rec, &new_dir);
-		if (old_fname == NULL)
-			return FALSE;
+	if (rename(path, new_path) < 0) {
+		if (errno == ENOENT)
+			return 0;
 
-		if (new_path == NULL) {
-			new_fname = maildir_filename_set_flags(old_fname,
-							       flags);
-                        *new_fname_r = new_fname;
-			new_path = t_strconcat(index->mailbox_path,
-					       "/cur/", new_fname, NULL);
+		if (ENOSPACE(errno)) {
+			index->nodiskspace = TRUE;
+			return 1;
 		}
 
-		if (strcmp(old_fname, new_fname) == 0)
-			ret = 1;
-		else {
-			ret = maildir_rename_mail_file(index, new_dir,
-						       old_fname, new_path);
-			if (ret == -1)
-				return FALSE;
-
-			if (ret == 1) {
-				if (index->maildir_keep_new && new_dir) {
-					/* looks like we have some more space
-					   again, see if we could move mails
-					   from new/ to cur/ again */
-					index->maildir_keep_new = FALSE;
-				}
-
-				/* cur/ was updated, set it dirty-synced */
-				index->file_sync_stamp = ioloop_time;
-				index->maildir_cur_dirty = ioloop_time;
-			}
-
-		}
-		if (ret == 0) {
-			if (!maildir_index_sync_readonly(index, old_fname,
-							 &found))
-				return FALSE;
-			if (!found)
-				break;
+		if (errno == EACCES) {
+			index->mailbox_readonly = TRUE;
+			return 1;
 		}
 
-		i++;
-	} while (i < 10 && ret == 0);
-
-	if (ret == 1)
-		return TRUE;
-
-	/* we couldn't actually rename() the file now.
-	   leave it's flags dirty so they get changed later. */
-	index_flags = mail_cache_get_index_flags(index->cache, rec);
-	if ((index_flags & MAIL_INDEX_FLAG_DIRTY) == 0) {
-		if (mail_cache_lock(index->cache, FALSE) <= 0)
-			return FALSE;
-		mail_cache_unlock_later(index->cache);
-
-		index_flags |= MAIL_INDEX_FLAG_DIRTY;
-		mail_cache_update_index_flags(index->cache, rec, index_flags);
+		index_set_error(index, "rename(%s, %s) failed: %m",
+				path, new_path);
+		return -1;
+	}
 
-		index->header->flags |= MAIL_INDEX_HDR_FLAG_DIRTY_MESSAGES;
+	if (index->maildir_keep_new && new_dir) {
+		/* looks like we have some more space again, see if we could
+		   move mails from new/ to cur/ again */
+		index->maildir_keep_new = FALSE;
 	}
 
-	index->next_dirty_flush =
-		ioloop_time + MAILDIR_DIRTY_FLUSH_TIMEOUT;
-	*new_fname_r = NULL;
-	return TRUE;
+	/* cur/ was updated, set it dirty-synced */
+	index->file_sync_stamp = ioloop_time;
+	index->maildir_cur_dirty = ioloop_time;
+	ctx->found = TRUE;
+	return 1;
 }
 
 int maildir_index_update_flags(struct mail_index *index,
 			       struct mail_index_record *rec, unsigned int seq,
+			       enum modify_type modify_type,
 			       enum mail_flags flags, int external_change)
 {
-	const char *new_fname;
-	int failed = FALSE;
+	struct update_flags_ctx ctx;
+        enum mail_index_record_flag index_flags;
+
+	memset(&ctx, 0, sizeof(ctx));
+	ctx.modify_type = modify_type;
+	ctx.flags = flags;
 
 	t_push();
-	if (!maildir_rename_mail(index, rec, flags, &new_fname)) {
+	if (!maildir_file_do(index, rec, do_rename, &ctx)) {
 		t_pop();
 		return FALSE;
 	}
 
-	if (new_fname != NULL) {
+	if (!ctx.found) {
+		/* we couldn't actually rename() the file now.
+		   leave it's flags dirty so they get changed later. */
+		index_flags = mail_cache_get_index_flags(index->cache, rec);
+		if ((index_flags & MAIL_INDEX_FLAG_DIRTY) == 0) {
+			if (mail_cache_lock(index->cache, FALSE) <= 0)
+				return FALSE;
+			mail_cache_unlock_later(index->cache);
+
+			index_flags |= MAIL_INDEX_FLAG_DIRTY;
+			mail_cache_update_index_flags(index->cache, rec,
+						      index_flags);
+
+			index->header->flags |=
+				MAIL_INDEX_HDR_FLAG_DIRTY_MESSAGES;
+		}
+
+		index->next_dirty_flush =
+			ioloop_time + MAILDIR_DIRTY_FLUSH_TIMEOUT;
+	} else if (ctx.new_fname != NULL) {
 		maildir_index_update_filename(index, rec->uid,
-					      new_fname, FALSE);
+					      ctx.new_fname, FALSE);
 	}
-
-	if (!failed && !mail_index_update_flags(index, rec, seq, flags,
-						external_change))
-		failed = TRUE;
 	t_pop();
 
-	return !failed;
+	return mail_index_update_flags(index, rec, seq,
+				       modify_type, flags, external_change);
 }



More information about the dovecot-cvs mailing list