Re: problematic upgrade 2.3.16 -> 2.4.3

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Wed, Nov 10, 2010 at 09:38:34PM +0100, Paul Dekkers wrote:
> Hi Bron,
> 
> Thanks for your reply:
> 
> On 10-11-2010 21:27, Bron Gondwana wrote:
> 
> >>(Can I downgrade? I just need to reconstruct everything, right, because
> >>the index format changed? :-S)
> >
> >Yes, you will need to reconstruct anything which successfully upgraded.
> 
> Ok, there's no way of telling what folders got succesfully upgraded,
> right? I only see messages "Index upgrade failed: user.paul",
> indicating the ones that failed :-S
> 
> (A selective reconstruct would be faster.)

You can run 'reconstruct -s', which is pretty fast, but still
detects the worst cases.
 
> >This issue got reported last night, and I wrote a patch but haven't
> 
> So, just the reconstruct problem, or more of my symptoms? (I've
> looked at the devel archive and the info-cyrus, but did not see
> anything similar, or from last night anyway ;-) so I have no idea
> what issue exactly.)

This will fix all your broken upgrades.

> >yet tested it.  I can give it to you in about 2-3 hours if you'll still
> >be around then!
> 
> Hmm, 3 hours will be quite late here, but if you can help me
> understand if the patch would solve "all" my symptoms, it might be
> worth it.
> 
> But I have the feeling its not just reconstruct (as even after
> succesfull reconstructs I still get errors, and I didn't expect this
> many errors initially anyway).

No - upgrades were all sorts of broken :(  I'm really sorry.  This
patch will get scrambled for 2.4.4.

(Note: I havne't written the cyrus.expunge fix yet - this is just to
make the rest of the upgrades go cleanly!)

https://github.com/brong/cyrus-imapd/tree/for244

Or just apply the attached patches (generated from that tree since
2.4.3 was released)

Patch number 4 is the one you care about, but the others are fine
to apply too.

Now - I'm going to look into cyrus.expunge handling magic for you.

Bron.
>From 7380370a4825071ce460648c4c49a3aa9603f13b Mon Sep 17 00:00:00 2001
From: Bron Gondwana <brong@xxxxxxxxx>
Date: Tue, 9 Nov 2010 16:28:59 +1100
Subject: [PATCH 1/4] Bug 3331 - sync_reset broken

---
 imap/sync_reset.c |   10 ++++++++--
 1 files changed, 8 insertions(+), 2 deletions(-)

diff --git a/imap/sync_reset.c b/imap/sync_reset.c
index 4c81efb..fbb0637 100644
--- a/imap/sync_reset.c
+++ b/imap/sync_reset.c
@@ -86,7 +86,7 @@
 /* global state */
 const int config_need_data = 0;
 
-/* Static global variables and support routines for sync_server */
+/* Static global variables and support routines for sync_reset */
 
 extern char *optarg;
 extern int optind;
@@ -103,6 +103,9 @@ static void shut_down(int code)
 {
     in_shutdown = 1;
 
+    annotatemore_close();
+    annotatemore_done();
+
     if (sync_userid)    free(sync_userid);
     if (sync_authstate) auth_freestate(sync_authstate);
 
@@ -126,7 +129,7 @@ static int usage(const char *name)
 
 void fatal(const char* s, int code)
 {
-    fprintf(stderr, "sync_server: %s\n", s);
+    fprintf(stderr, "sync_reset: %s\n", s);
     exit(code);
 }
 
@@ -241,6 +244,9 @@ main(int argc, char **argv)
     signals_set_shutdown(&shut_down);
     signals_add_handlers(0);
 
+    annotatemore_init(0, NULL, NULL);
+    annotatemore_open(NULL);
+
     if (!force) {
         fprintf(stderr, "Usage: sync_reset -f user user user ...\n");
         fprintf(stderr, "         -f [force] is obligitory for safety\n");
-- 
1.7.1

>From 86cc6436f18eaef7df4c55d8368c890afdc64cd9 Mon Sep 17 00:00:00 2001
From: Bron Gondwana <brong@xxxxxxxxx>
Date: Wed, 10 Nov 2010 08:36:26 +1100
Subject: [PATCH 2/4] Bug 3332 - fix subscriptions handling during unuser/reset

---
 imap/sync_client.c |    2 +-
 imap/sync_reset.c  |    2 +-
 imap/sync_server.c |    2 +-
 3 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/imap/sync_client.c b/imap/sync_client.c
index 074fd9b..283ce12 100644
--- a/imap/sync_client.c
+++ b/imap/sync_client.c
@@ -1764,7 +1764,7 @@ int do_user_sub(const char *userid, struct sync_name_list *replica_subs)
     for (msubs = master_subs->head; msubs; msubs = msubs->next) {
 	r = (sync_namespace.mboxname_tointernal)(&sync_namespace, msubs->name,
 						 userid, buf);
-	if (r) continue;
+	if (r) continue; /* just ignore invalid subs */
 	rsubs = sync_name_lookup(replica_subs, buf);
 	if (rsubs) {
 	    rsubs->mark = 1;
diff --git a/imap/sync_reset.c b/imap/sync_reset.c
index fbb0637..db30134 100644
--- a/imap/sync_reset.c
+++ b/imap/sync_reset.c
@@ -149,11 +149,11 @@ static int reset_single(const char *userid)
 					    addmbox_sub, (void *)list, 1);
     if (r) goto fail;
 
+    /* ignore failures here - the subs file gets deleted soon anyway */
     for (item = list->head; item; item = item->next) {
 	r = (sync_namespacep->mboxname_tointernal)(sync_namespacep, item->name,
 						   userid, buf);
         if (!r) r = mboxlist_changesub(buf, userid, sync_authstate, 0, 0);
-        if (r) goto fail;
     }
     sync_name_list_free(&list);
 
diff --git a/imap/sync_server.c b/imap/sync_server.c
index e87867e..2218a04 100644
--- a/imap/sync_server.c
+++ b/imap/sync_server.c
@@ -1838,11 +1838,11 @@ static int do_unuser(struct dlist *kin)
 					    addmbox_sub, (void *)list, 1);
     if (r) goto fail;
 
+    /* ignore failures here - the subs file gets deleted soon anyway */
     for (item = list->head; item; item = item->next) {
 	r = (sync_namespacep->mboxname_tointernal)(sync_namespacep, item->name,
 						   userid, buf);
         if (!r) r = mboxlist_changesub(buf, userid, sync_authstate, 0, 0);
-        if (r) goto fail;
     }
     sync_name_list_free(&list);
 
-- 
1.7.1

>From 20499abe02a62bd36eaf4fe4d24e3622230a0fa5 Mon Sep 17 00:00:00 2001
From: Bron Gondwana <brong@xxxxxxxxx>
Date: Wed, 10 Nov 2010 11:31:05 +1100
Subject: [PATCH 3/4] Bug 3333 - fix proc_registration in imapd, and refactor login success

---
 imap/fud.c         |   12 +++----
 imap/imapd.c       |   97 ++++++++++++++++++++++++---------------------------
 imap/nntpd.c       |    9 +----
 imap/pop3d.c       |   14 ++-----
 imap/proc.c        |   13 ++-----
 imap/proc.h        |   56 ++++++++++++++++++++++++++++++
 imap/smmapd.c      |    6 ++--
 imap/sync_server.c |    8 +---
 8 files changed, 122 insertions(+), 93 deletions(-)
 create mode 100644 imap/proc.h

diff --git a/imap/fud.c b/imap/fud.c
index 27c08d9..34d8761 100644
--- a/imap/fud.c
+++ b/imap/fud.c
@@ -63,19 +63,20 @@
 #include <errno.h>
 #include <pwd.h>
 
+#include "acl.h"
 #include "assert.h"
 #include "mboxlist.h"
 #include "global.h"
 #include "exitcodes.h"
 #include "imap_err.h"
 #include "mailbox.h"
+#include "map.h"
+#include "mboxname.h"
+#include "proc.h"
+#include "seen.h"
 #include "xmalloc.h"
 #include "xstrlcpy.h"
 #include "xstrlcat.h"
-#include "acl.h"
-#include "seen.h"
-#include "mboxname.h"
-#include "map.h"
 
 #define REQ_OK		0
 #define REQ_DENY	1
@@ -90,9 +91,6 @@ static struct namespace fud_namespace;
  * only if we're not on a frontend, so we won't flat-out require it here */
 const int config_need_data = 0;
 
-/* forward decls */
-extern void setproctitle_init(int argc, char **argv, char **envp);
-
 int handle_request(const char *who, const char *name, 
 		   struct sockaddr_storage sfrom, socklen_t sfromsiz);
 
diff --git a/imap/imapd.c b/imap/imapd.c
index d03bd14..b8b0731 100644
--- a/imap/imapd.c
+++ b/imap/imapd.c
@@ -96,6 +96,7 @@
 #include "mbdump.h"
 #include "mkgmtime.h"
 #include "mupdate-client.h"
+#include "proc.h"
 #include "quota.h"
 #include "seen.h"
 #include "statuscache.h"
@@ -379,11 +380,6 @@ static int subscribed_cb(char *name, int matchlen, int maycreate,
 static void list_data(struct listargs *listargs);
 static void list_data_remote(char *tag, struct listargs *listargs);
 
-extern void setproctitle_init(int argc, char **argv, char **envp);
-extern int proc_register(const char *progname, const char *clienthost, 
-			 const char *userid, const char *mailbox);
-extern void proc_cleanup(void);
-
 extern int saslserver(sasl_conn_t *conn, const char *mech,
 		      const char *init_resp, const char *resp_prefix,
 		      const char *continuation, const char *empty_resp,
@@ -2078,6 +2074,36 @@ void cmdloop()
     }
 }
 
+static void authentication_success(void)
+{
+    int r;
+
+    /* register the user */
+    proc_register("imapd", imapd_clienthost, imapd_userid, NULL);
+    
+    /* authstate already created by mysasl_proxy_policy() */
+    imapd_userisadmin = global_authisa(imapd_authstate, IMAPOPT_ADMINS);
+
+    /* Create telemetry log */
+    imapd_logfd = telemetry_log(imapd_userid, imapd_in, imapd_out, 0);
+
+    /* Set namespace */
+    r = mboxname_init_namespace(&imapd_namespace,
+				imapd_userisadmin || imapd_userisproxyadmin);
+    if (r) {
+	syslog(LOG_ERR, "%s", error_message(r));
+	fatal(error_message(r), EC_CONFIG);
+    }
+
+    /* Make a copy of the external userid for use in proxying */
+    proxy_userid = xstrdup(imapd_userid);
+
+    /* Translate any separators in userid */
+    mboxname_hiersep_tointernal(&imapd_namespace, imapd_userid,
+				config_virtdomains ?
+				strcspn(imapd_userid, "@") : 0);
+}
+
 /*
  * Perform a LOGIN command
  */
@@ -2230,41 +2256,20 @@ void cmd_login(char *tag, char *user)
 	    plaintextloginalert = config_getstring(IMAPOPT_PLAINTEXTLOGINALERT);
 	}
     }
-    
-    /* authstate already created by mysasl_proxy_policy() */
-    imapd_userisadmin = global_authisa(imapd_authstate, IMAPOPT_ADMINS);
+
+    buf_free(&passwdbuf);
 
     prot_printf(imapd_out, "%s OK [CAPABILITY ", tag);
     capa_response(CAPA_PREAUTH|CAPA_POSTAUTH);
     prot_printf(imapd_out, "] %s\r\n", reply);
 
-    /* Create telemetry log */
-    imapd_logfd = telemetry_log(imapd_userid, imapd_in, imapd_out, 0);
-
-    /* Set namespace */
-    if ((r = mboxname_init_namespace(&imapd_namespace,
-				     imapd_userisadmin || imapd_userisproxyadmin)) != 0) {
-	syslog(LOG_ERR, "%s", error_message(r));
-	fatal(error_message(r), EC_CONFIG);
-    }
-
-    /* Make a copy of the external userid for use in proxying */
-    proxy_userid = xstrdup(imapd_userid);
-
-    /* Translate any separators in userid */
-    mboxname_hiersep_tointernal(&imapd_namespace, imapd_userid,
-				config_virtdomains ?
-				strcspn(imapd_userid, "@") : 0);
-
-    buf_free(&passwdbuf);
-    return;
+    authentication_success();
 }
 
 /*
  * Perform an AUTHENTICATE command
  */
-void
-cmd_authenticate(char *tag, char *authtype, char *resp)
+void cmd_authenticate(char *tag, char *authtype, char *resp)
 {
     int sasl_result;
 
@@ -2356,8 +2361,6 @@ cmd_authenticate(char *tag, char *authtype, char *resp)
 	imapd_userid = xstrdup(canon_user);
     }
 
-    proc_register("imapd", imapd_clienthost, imapd_userid, (char *)0);
-
     syslog(LOG_NOTICE, "login: %s %s%s %s%s %s", imapd_clienthost,
 	   imapd_userid, imapd_magicplus ? imapd_magicplus : "",
 	   authtype, imapd_starttls_done ? "+TLS" : "", "User logged in");
@@ -2396,25 +2399,7 @@ cmd_authenticate(char *tag, char *authtype, char *resp)
     prot_setsasl(imapd_in,  imapd_saslconn);
     prot_setsasl(imapd_out, imapd_saslconn);
 
-    /* Create telemetry log */
-    imapd_logfd = telemetry_log(imapd_userid, imapd_in, imapd_out, 0);
-
-    /* Set namespace */
-    if ((r = mboxname_init_namespace(&imapd_namespace,
-				     imapd_userisadmin || imapd_userisproxyadmin)) != 0) {
-	syslog(LOG_ERR, "%s", error_message(r));
-	fatal(error_message(r), EC_CONFIG);
-    }
-
-    /* Make a copy of the external userid for use in proxying */
-    proxy_userid = xstrdup(imapd_userid);
-
-    /* Translate any separators in userid */
-    mboxname_hiersep_tointernal(&imapd_namespace, imapd_userid,
-				config_virtdomains ?
-				strcspn(imapd_userid, "@") : 0);
-
-    return;
+    authentication_success();
 }
 
 /*
@@ -3527,6 +3512,13 @@ void cmd_select(char *tag, char *cmd, char *name)
 	wasopen = 1;
     }
 
+    if (wasopen) {
+	/* un-register currently selected mailbox, it may get
+	 * overwritten later, but easier here than handling
+	 * all possible error paths */
+	proc_register("imapd", imapd_clienthost, imapd_userid, NULL);
+    }
+
     r = (*imapd_namespace.mboxname_tointernal)(&imapd_namespace, name,
 					       imapd_userid, mailboxname);
 
@@ -3712,6 +3704,9 @@ void cmd_select(char *tag, char *cmd, char *name)
  */
 void cmd_close(char *tag, char *cmd)
 {
+    /* unregister the selected mailbox */
+    proc_register("imapd", imapd_clienthost, imapd_userid, NULL);
+
     if (backend_current) {
 	/* remote mailbox */
 	prot_printf(backend_current->out, "%s %s\r\n", tag, cmd);
diff --git a/imap/nntpd.c b/imap/nntpd.c
index 79e3b80..270127b 100644
--- a/imap/nntpd.c
+++ b/imap/nntpd.c
@@ -97,6 +97,7 @@
 #include "mkgmtime.h"
 #include "mupdate-client.h"
 #include "nntp_err.h"
+#include "proc.h"
 #include "prot.h"
 #include "proxy.h"
 #include "retry.h"
@@ -234,12 +235,6 @@ static void cmd_starttls(int nntps);
 void usage(void);
 void shut_down(int code) __attribute__ ((noreturn));
 
-
-extern void setproctitle_init(int argc, char **argv, char **envp);
-extern int proc_register(const char *progname, const char *clienthost, 
-			 const char *userid, const char *mailbox);
-extern void proc_cleanup(void);
-
 extern int saslserver(sasl_conn_t *conn, const char *mech,
 		      const char *init_resp, const char *resp_prefix,
 		      const char *continuation, const char *empty_resp,
@@ -2197,7 +2192,7 @@ static void cmd_authinfo_sasl(char *cmd, char *mech, char *resp)
     }
     nntp_userid = xstrdup((const char *) val);
 
-    proc_register("nntpd", nntp_clienthost, nntp_userid, (char *)0);
+    proc_register("nntpd", nntp_clienthost, nntp_userid, NULL);
 
     syslog(LOG_NOTICE, "login: %s %s %s%s %s", nntp_clienthost, nntp_userid,
 	   mech, nntp_starttls_done ? "+TLS" : "", "User logged in");
diff --git a/imap/pop3d.c b/imap/pop3d.c
index 4b0e115..f96723f 100644
--- a/imap/pop3d.c
+++ b/imap/pop3d.c
@@ -86,6 +86,7 @@
 #include "idle.h"
 #include "telemetry.h"
 #include "backend.h"
+#include "proc.h"
 #include "proxy.h"
 #include "seen.h"
 #include "userdeny.h"
@@ -198,12 +199,6 @@ static int update_seen(void);
 void usage(void);
 void shut_down(int code) __attribute__ ((noreturn));
 
-
-extern void setproctitle_init(int argc, char **argv, char **envp);
-extern int proc_register(const char *progname, const char *clienthost, 
-			 const char *userid, const char *mailbox);
-extern void proc_cleanup(void);
-
 extern int saslserver(sasl_conn_t *conn, const char *mech,
 		      const char *init_resp, const char *resp_prefix,
 		      const char *continuation, const char *empty_chal,
@@ -1751,8 +1746,6 @@ int openinbox(void)
 	     !sdata.messages) {
 	/* local mailbox (empty) -- don't bother opening the mailbox */
 	syslog(LOG_INFO, "optimized mode for empty maildrop: %s", popd_userid);
-
-	proc_register("pop3d", popd_clienthost, popd_userid, inboxname);
     }
     else {
 	/* local mailbox */
@@ -1827,8 +1820,6 @@ int openinbox(void)
 	}
 	popd_exists = msgno;
 
-	proc_register("pop3d", popd_clienthost, popd_userid,
-		      popd_mailbox->name);
 	/* finished our initial read */
 	mailbox_unlock_index(popd_mailbox, NULL);
 
@@ -1846,6 +1837,9 @@ int openinbox(void)
 	}
     }
 
+    /* register process */
+    proc_register("pop3d", popd_clienthost, popd_userid, inboxname);
+
     /* Create telemetry log */
     popd_logfd = telemetry_log(popd_userid, popd_in, popd_out, 0);
 
diff --git a/imap/proc.c b/imap/proc.c
index 11b3e6d..b1732e3 100644
--- a/imap/proc.c
+++ b/imap/proc.c
@@ -52,8 +52,9 @@
 #include <syslog.h>
 #include <string.h>
 
-#include "global.h"
 #include "exitcodes.h"
+#include "global.h"
+#include "proc.h"
 #include "xmalloc.h"
 
 #define FNAME_PROCDIR "/proc/"
@@ -61,14 +62,8 @@
 static char *procfname = 0;
 static FILE *procfile = 0;
 
-extern void setproctitle_init(int argc, char **argv, char **envp);
-extern void setproctitle(const char *fmt, ...);
-
-int proc_register(progname, clienthost, userid, mailbox)
-const char *progname;
-const char *clienthost;
-const char *userid;
-const char *mailbox;
+int proc_register(const char *progname, const char *clienthost,
+		  const char *userid, const char *mailbox)
 {
     unsigned pid;
     int pos;
diff --git a/imap/proc.h b/imap/proc.h
new file mode 100644
index 0000000..c692c3d
--- /dev/null
+++ b/imap/proc.h
@@ -0,0 +1,56 @@
+/* proc.c -- Server process registry
+ *
+ * Copyright (c) 1994-2008 Carnegie Mellon University.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The name "Carnegie Mellon University" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For permission or any legal
+ *    details, please contact
+ *      Carnegie Mellon University
+ *      Center for Technology Transfer and Enterprise Creation
+ *      4615 Forbes Avenue
+ *      Suite 302
+ *      Pittsburgh, PA  15213
+ *      (412) 268-7393, fax: (412) 268-7395
+ *      innovation@xxxxxxxxxxxxxx
+ *
+ * 4. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by Computing Services
+ *     at Carnegie Mellon University (http://www.cmu.edu/computing/)."
+ *
+ * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
+ * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * $Id: proc.c,v 1.27 2010/01/06 17:01:38 murch Exp $
+ */
+
+#ifndef _PROC_H
+#define _PROC_H
+
+extern void setproctitle_init(int argc, char **argv, char **envp);
+extern void setproctitle(const char *fmt, ...);
+
+extern int proc_register(const char *progname, const char *clienthost,
+		         const char *userid, const char *mailbox);
+
+extern void proc_cleanup(void);
+
+#endif /* _PROC_H */
diff --git a/imap/smmapd.c b/imap/smmapd.c
index ddb9699..90d56de 100644
--- a/imap/smmapd.c
+++ b/imap/smmapd.c
@@ -89,11 +89,12 @@
 
 #include "acl.h"
 #include "append.h"
-#include "mboxlist.h"
-#include "global.h"
 #include "exitcodes.h"
+#include "global.h"
 #include "imap_err.h"
+#include "mboxlist.h"
 #include "mupdate-client.h"
+#include "proc.h"
 #include "util.h"
 #include "xmalloc.h"
 #include "xstrlcpy.h"
@@ -113,7 +114,6 @@ static struct namespace map_namespace;
 const int config_need_data = 0;
 
 /* forward decls */
-extern void setproctitle_init(int argc, char **argv, char **envp);
 int begin_handling(void);
 
 void smmapd_reset(void)
diff --git a/imap/sync_server.c b/imap/sync_server.c
index 2218a04..47c70f2 100644
--- a/imap/sync_server.c
+++ b/imap/sync_server.c
@@ -86,6 +86,7 @@
 #include "mailbox.h"
 #include "map.h"
 #include "mboxlist.h"
+#include "proc.h"
 #include "prot.h"
 #include "quota.h"
 #include "retry.h"
@@ -151,11 +152,6 @@ static void cmd_apply(struct dlist *kl,
 void usage(void);
 void shut_down(int code) __attribute__ ((noreturn));
 
-extern void setproctitle_init(int argc, char **argv, char **envp);
-extern int proc_register(const char *progname, const char *clienthost, 
-			 const char *userid, const char *mailbox);
-extern void proc_cleanup(void);
-
 extern int saslserver(sasl_conn_t *conn, const char *mech,
 		      const char *init_resp, const char *resp_prefix,
 		      const char *continuation, const char *empty_resp,
@@ -820,7 +816,7 @@ static void cmd_authenticate(char *mech, char *resp)
     }
 
     sync_userid = xstrdup((const char *) val);
-    proc_register("sync_server", sync_clienthost, sync_userid, (char *)0);
+    proc_register("sync_server", sync_clienthost, sync_userid, NULL);
 
     syslog(LOG_NOTICE, "login: %s %s %s%s %s", sync_clienthost, sync_userid,
 	   mech, sync_starttls_done ? "+TLS" : "", "User logged in");
-- 
1.7.1

>From 21bf6a722d3bd88cd79d6ed54f3a6fba2842a438 Mon Sep 17 00:00:00 2001
From: Bron Gondwana <brong@xxxxxxxxxxxxxxxxxxxx>
Date: Wed, 10 Nov 2010 21:35:07 +1100
Subject: [PATCH 4/4] Bug 3334 - simplify upgrade path to be much more robust

---
 imap/mailbox.c       |  270 +++++++++++++++++++++++++++++++++----------------
 imap/mailbox.h       |   26 +++--
 imap/upgrade_index.c |  266 ++++++++++++++-----------------------------------
 3 files changed, 274 insertions(+), 288 deletions(-)

diff --git a/imap/mailbox.c b/imap/mailbox.c
index a636085..045261a 100644
--- a/imap/mailbox.c
+++ b/imap/mailbox.c
@@ -1242,7 +1242,7 @@ int mailbox_user_flag(struct mailbox *mailbox, const char *flag,
     return 0;
 }
 
-int mailbox_buf_to_index_header(struct index_header *i, const char *buf)
+int mailbox_buf_to_index_header(const char *buf, struct index_header *i)
 {
     uint32_t crc;
 
@@ -1345,7 +1345,7 @@ static int mailbox_read_index_header(struct mailbox *mailbox)
     if (mailbox->index_size < INDEX_HEADER_SIZE)
 	return IMAP_MAILBOX_BADFORMAT;
 
-    r = mailbox_buf_to_index_header(&mailbox->i, mailbox->index_base);
+    r = mailbox_buf_to_index_header(mailbox->index_base, &mailbox->i);
     if (r) return r;
 
     r = mailbox_refresh_index_map(mailbox);
@@ -1891,7 +1891,7 @@ static void mailbox_quota_dirty(struct mailbox *mailbox)
     }
 }
 
-void mailbox_index_update_counts(struct mailbox *mailbox,
+static void header_update_counts(struct index_header *i,
 				 struct index_record *record,
 				 int is_add)
 {
@@ -1901,35 +1901,43 @@ void mailbox_index_update_counts(struct mailbox *mailbox,
     if (record->system_flags & FLAG_EXPUNGED)
 	return;
 
-    mailbox_quota_dirty(mailbox);
-
     /* update mailbox header fields */
     if (record->system_flags & FLAG_ANSWERED)
-	mailbox->i.answered += num;
+	i->answered += num;
 
     if (record->system_flags & FLAG_FLAGGED)
-	mailbox->i.flagged += num;
+	i->flagged += num;
 
     if (record->system_flags & FLAG_DELETED)
-	mailbox->i.deleted += num;
+	i->deleted += num;
 
     if (is_add) {
-	mailbox->i.exists++;
-	mailbox->i.quota_mailbox_used += record->size;
+	i->exists++;
+	i->quota_mailbox_used += record->size;
     }
     else {
-	if (mailbox->i.exists) 
-	    mailbox->i.exists--;
+	if (i->exists) i->exists--;
 
 	/* corruption prevention - check we don't go negative */
-	if (mailbox->i.quota_mailbox_used > record->size)
-	    mailbox->i.quota_mailbox_used -= record->size;
+	if (i->quota_mailbox_used > record->size)
+	    i->quota_mailbox_used -= record->size;
 	else
-	    mailbox->i.quota_mailbox_used = 0;
+	    i->quota_mailbox_used = 0;
     }
-    mailbox->i.sync_crc ^= make_sync_crc(mailbox, record);
+}
 
+static void mailbox_index_update_counts(struct mailbox *mailbox,
+					struct index_record *record,
+					int is_add)
+{
+    /* avoid dirtying for expunged */
+    if (record->system_flags & FLAG_EXPUNGED)
+	return;
+    mailbox_quota_dirty(mailbox);
     mailbox_index_dirty(mailbox);
+    header_update_counts(&mailbox->i, record, is_add);
+    /* xor doesn't care if it's an add! */
+    mailbox->i.sync_crc ^= make_sync_crc(mailbox, record);
 }
 
 int mailbox_index_recalc(struct mailbox *mailbox)
@@ -1954,7 +1962,7 @@ int mailbox_index_recalc(struct mailbox *mailbox)
     for (recno = 1; recno <= mailbox->i.num_records; recno++) {
 	r = mailbox_read_index_record(mailbox, recno, &record);
 	if (r) return r;
-	mailbox_index_update_counts(mailbox, &record, 1);
+	header_update_counts(&mailbox->i, &record, 1);
     }
 
     return 0;
@@ -2023,6 +2031,7 @@ int mailbox_rewrite_index_record(struct mailbox *mailbox,
 
     /* remove the counts for the old copy, and add them for
      * the new copy */
+
     mailbox_index_update_counts(mailbox, &oldrecord, 0);
     mailbox_index_update_counts(mailbox, record, 1);
 
@@ -2207,36 +2216,166 @@ static int mailbox_index_unlink(struct mailbox *mailbox)
     return 0;
 }
 
-/* need a mailbox exclusive lock, we're rewriting files */
-static int mailbox_index_repack(struct mailbox *mailbox)
+int mailbox_repack_setup(struct mailbox *mailbox,
+			 struct mailbox_repack **repackptr)
 {
-    char *fname;
-    uint32_t newrecno = 1;
+    struct mailbox_repack *repack = xzmalloc(sizeof(struct mailbox_repack));
+    const char *fname;
     indexbuffer_t ibuf;
     unsigned char *buf = ibuf.buf;
-    uint32_t recno;
-    int newindex_fd = -1, newcache_fd = -1;
-    struct index_record record;
-    size_t offset;
-    int newgeneration;
-    int r = IMAP_IOERROR;
     int n;
 
-    syslog(LOG_INFO, "Repacking mailbox %s", mailbox->name);
+    /* init */
+    repack->mailbox = mailbox;
+    repack->i = mailbox->i; /* struct copy */
+    repack->newindex_fd = -1;
+    repack->newcache_fd = -1;
 
+    /* new files */
     fname = mailbox_meta_newfname(mailbox, META_INDEX);
-    newindex_fd = open(fname, O_RDWR|O_TRUNC|O_CREAT, 0666);
-    if (newindex_fd == -1) goto fail;
+    repack->newindex_fd = open(fname, O_RDWR|O_TRUNC|O_CREAT, 0666);
+    if (repack->newindex_fd == -1) goto fail;
 
     fname = mailbox_meta_newfname(mailbox, META_CACHE);
-    newcache_fd = open(fname, O_RDWR|O_TRUNC|O_CREAT, 0666);
-    if (newcache_fd == -1) goto fail;
+    repack->newcache_fd = open(fname, O_RDWR|O_TRUNC|O_CREAT, 0666);
+    if (repack->newcache_fd == -1) goto fail;
 
     /* update the generation number */
-    newgeneration = mailbox->i.generation_no + 1;
-    *((bit32 *)(buf)) = htonl(newgeneration);
-    n = retry_write(newcache_fd, buf, 4);
-    if (n != 4) goto fail;
+    repack->i.generation_no++;
+
+    /* zero out some values */
+    repack->i.num_records = 0;
+    repack->i.quota_mailbox_used = 0;
+    repack->i.num_records = 0;
+    repack->i.sync_crc = 0; /* no records is blank */
+    repack->i.answered = 0;
+    repack->i.deleted = 0;
+    repack->i.flagged = 0;
+    repack->i.exists = 0;   
+    repack->i.first_expunged = 0;
+    repack->i.leaked_cache_records = 0;
+
+    /* prepare initial header buffer */
+    mailbox_index_header_to_buf(&repack->i, buf);
+
+    /* write initial headers */
+    n = retry_write(repack->newcache_fd, buf, 4);
+    if (n == -1) goto fail;
+
+    n = retry_write(repack->newindex_fd, buf, INDEX_HEADER_SIZE);
+    if (n == -1) goto fail;    
+    
+    *repackptr = repack;
+    return 0;
+
+ fail:
+    mailbox_repack_abort(&repack);
+    return IMAP_IOERROR;
+}
+
+int mailbox_repack_add(struct mailbox_repack *repack,
+		       struct index_record *record)
+{
+    int r;
+    int n;
+    indexbuffer_t ibuf;
+    unsigned char *buf = ibuf.buf;
+
+    /* write out the new cache record - need to clear the cache_offset
+     * so it gets reset in the new record */
+    record->cache_offset = 0;
+    r = cache_append_record(repack->newcache_fd, record);
+    if (r) return r;
+
+    /* update counters */
+    header_update_counts(&repack->i, record, 1);
+
+    /* write the index record out */
+    mailbox_index_record_to_buf(record, buf);
+    n = retry_write(repack->newindex_fd, buf, INDEX_RECORD_SIZE);
+    if (n == -1)
+	return IMAP_IOERROR;
+
+    repack->i.num_records++;
+
+    return 0;
+}
+
+/* clean up memory structures and abort repack */
+void mailbox_repack_abort(struct mailbox_repack **repackptr)
+{
+    struct mailbox_repack *repack = *repackptr;
+    if (!repack) return; /* safe against double-free */
+    if (repack->newcache_fd != -1) close(repack->newcache_fd);
+    unlink(mailbox_meta_newfname(repack->mailbox, META_CACHE));
+    if (repack->newindex_fd != -1) close(repack->newindex_fd);
+    unlink(mailbox_meta_newfname(repack->mailbox, META_INDEX));
+    free(repack);
+    *repackptr = NULL;
+}
+
+int mailbox_repack_commit(struct mailbox_repack **repackptr)
+{
+    indexbuffer_t ibuf;
+    unsigned char *buf = ibuf.buf;
+    struct mailbox_repack *repack = *repackptr;
+    int n;
+    int r;
+
+    assert(repack);
+
+    repack->i.last_repack_time = time(0);
+
+    /* rewrite the header with updated details */
+    mailbox_index_header_to_buf(&repack->i, buf);
+    n = lseek(repack->newindex_fd, 0, SEEK_SET);
+    if (n == -1) {
+	r = IMAP_IOERROR;
+	goto fail;
+    }
+    n = retry_write(repack->newindex_fd, buf, INDEX_HEADER_SIZE);
+    if (n == -1) {
+	r = IMAP_IOERROR;
+	goto fail;
+    }
+
+    /* ensure everything is committed to disk */
+    if (fsync(repack->newindex_fd) || fsync(repack->newcache_fd))
+	goto fail;
+
+    close(repack->newcache_fd);
+    repack->newcache_fd = -1;
+    close(repack->newindex_fd);
+    repack->newindex_fd = -1;
+
+    /* rename index first - loader will handle un-renamed cache if
+     * the generation is lower */
+    r = mailbox_meta_rename(repack->mailbox, META_INDEX);
+    if (r) goto fail;
+
+    mailbox_meta_rename(repack->mailbox, META_CACHE);
+
+    free(repack);
+    *repackptr = NULL;
+    return 0;
+
+ fail:
+    mailbox_repack_abort(repackptr);
+    return r;
+}
+
+/* need a mailbox exclusive lock, we're rewriting files */
+static int mailbox_index_repack(struct mailbox *mailbox)
+{
+    struct mailbox_repack *repack = NULL;
+    uint32_t recno;
+    struct index_record record;
+    int r = IMAP_IOERROR;
+
+    syslog(LOG_INFO, "Repacking mailbox %s", mailbox->name);
+
+    r = mailbox_repack_setup(mailbox, &repack);
+    if (r) goto fail;
 
     for (recno = 1; recno <= mailbox->i.num_records; recno++) {
 	r = mailbox_read_index_record(mailbox, recno, &record);
@@ -2247,8 +2386,8 @@ static int mailbox_index_repack(struct mailbox *mailbox)
 	    /* just in case it was left lying around */
 	    /* XXX - log error if unlink fails */
 	    mailbox_message_unlink(mailbox, record.uid);
-	    if (record.modseq > mailbox->i.deletedmodseq)
-		mailbox->i.deletedmodseq = record.modseq;
+	    if (record.modseq > repack->i.deletedmodseq)
+		repack->i.deletedmodseq = record.modseq;
 	    continue;
 	}
 
@@ -2256,63 +2395,18 @@ static int mailbox_index_repack(struct mailbox *mailbox)
 	r = mailbox_cacherecord(mailbox, &record);
 	if (r) goto fail;
 
-	/* write out the new cache record - need to clear the cache_offset
-	 * so it gets reset in the new record */
-	record.cache_offset = 0;
-	r = cache_append_record(newcache_fd, &record);
+	r = mailbox_repack_add(repack, &record);
 	if (r) goto fail;
-
-	offset = mailbox->i.start_offset +
-		 (newrecno-1) * mailbox->i.record_size;
-
-	/* write the index record out */
-	mailbox_index_record_to_buf(&record, buf);
-	n = lseek(newindex_fd, offset, SEEK_SET);
-	if (n == -1) {
-	    syslog(LOG_ERR, "IOERROR: seeking index record %u for %s: %m",
-		   recno, mailbox->name);
-	    goto fail;
-	}
-	n = retry_write(newindex_fd, buf, INDEX_RECORD_SIZE);
-	if (n != INDEX_RECORD_SIZE) {
-	    syslog(LOG_ERR, "IOERROR: writing index record %u for %s: %m",
-		   recno, mailbox->name);
-	    goto fail;
-	}
-
-	newrecno++;
     }
 
-    /* update final header fields */
-    mailbox->i.generation_no = newgeneration;
-    mailbox->i.first_expunged = 0;
-    mailbox->i.last_repack_time = time(0);
-    mailbox->i.num_records = newrecno - 1;
-    mailbox->i.leaked_cache_records = 0;
     /* we unlinked any "needs unlink" in the process */
-    mailbox->i.options &= ~(OPT_MAILBOX_NEEDS_REPACK|OPT_MAILBOX_NEEDS_UNLINK);
-
-    mailbox_index_header_to_buf(&mailbox->i, buf);
-    n = lseek(newindex_fd, 0, SEEK_SET);
-    if (n == -1) goto fail;
-    n = retry_write(newindex_fd, buf, INDEX_HEADER_SIZE);
-    if (n != INDEX_HEADER_SIZE) goto fail;
-
-    if (fsync(newindex_fd) || fsync(newcache_fd))
-	goto fail;
-
-    close(newcache_fd);
-    close(newindex_fd);
-
-    r = mailbox_meta_rename(mailbox, META_INDEX);
-    if (!r) r = mailbox_meta_rename(mailbox, META_CACHE);
+    repack->i.options &= ~(OPT_MAILBOX_NEEDS_REPACK|OPT_MAILBOX_NEEDS_UNLINK);
 
-    return r;
+    return mailbox_repack_commit(&repack);
 
 fail:
-    if (newcache_fd != -1) close(newcache_fd);
-    if (newindex_fd != -1) close(newindex_fd);
-    return IMAP_IOERROR;
+    mailbox_repack_abort(&repack);
+    return r;
 }
 
 /*
diff --git a/imap/mailbox.h b/imap/mailbox.h
index 80001e1..8e9e9fb 100644
--- a/imap/mailbox.h
+++ b/imap/mailbox.h
@@ -498,17 +498,25 @@ extern int mailbox_reconstruct(const char *name, int flags);
 extern int mailbox_index_recalc(struct mailbox *mailbox);
 
 /* for upgrade index */
+extern int mailbox_open_index(struct mailbox *mailbox);
 extern int mailbox_buf_to_index_record(const char *buf,
 				       struct index_record *record);
-extern int mailbox_open_index(struct mailbox *mailbox);
-extern int mailbox_buf_to_index_header(struct index_header *i, const char *buf);
-extern bit32 mailbox_index_header_to_buf(struct index_header *i,
-					 unsigned char *buf);
-extern void mailbox_index_update_counts(struct mailbox *mailbox,
-					struct index_record *record,
-					int is_add);
-extern bit32 mailbox_index_record_to_buf(struct index_record *record,
-					 unsigned char *buf);
+extern int mailbox_buf_to_index_header(const char *buf,
+				       struct index_header *i);
+
+/* for repack */
+struct mailbox_repack {
+    struct mailbox *mailbox;
+    struct index_header i;
+    int newindex_fd;
+    int newcache_fd;
+};
 
+extern int mailbox_repack_setup(struct mailbox *mailbox,
+			        struct mailbox_repack **repackptr);
+extern int mailbox_repack_add(struct mailbox_repack *repack,
+			      struct index_record *record);
+extern void mailbox_repack_abort(struct mailbox_repack **repackptr);
+extern int mailbox_repack_commit(struct mailbox_repack **repackptr);
 
 #endif /* INCLUDED_MAILBOX_H */
diff --git a/imap/upgrade_index.c b/imap/upgrade_index.c
index 59e3044..d871b73 100644
--- a/imap/upgrade_index.c
+++ b/imap/upgrade_index.c
@@ -85,112 +85,46 @@
 #include "sequence.h"
 #include "xmalloc.h"
 
-static int sort_record(const void *a, const void *b)
-{
-    struct index_record *ra = (struct index_record *)a;
-    struct index_record *rb = (struct index_record *)b;
-    return ra->uid - rb->uid;
-}
+struct expunge_data {
+    uint32_t uid;
+    const char *base;
+};
 
-static int update_record_from_cache(struct mailbox *mailbox,
-				    struct index_record *record)
+static int sort_expunge(const void *a, const void *b)
 {
-    int r;
-    bit32 crc;
-
-    r = mailbox_open_cache(mailbox);
-    if (r) return r;
-
-    if (!record->cache_offset)
-	return IMAP_IOERROR;
-
-    r = cache_parserecord(&mailbox->cache_buf,
-			  record->cache_offset, &record->crec);
-    if (r) return r;
-
-    crc = crc32_buf(cache_buf(record));
-    if (record->cache_crc) {
-	if (crc != record->cache_crc)
-	    return IMAP_MAILBOX_CRC;
-    }
-    else {
-	record->cache_crc = crc;
-    }
-
-    /* extract the date for GMTIME field */
-    if (cacheitem_size(record, CACHE_ENVELOPE) > 2) {
-	char *envtokens[NUMENVTOKENS];
-	char *tmpenv = xstrndup(cacheitem_base(record, CACHE_ENVELOPE) + 1,
-				cacheitem_size(record, CACHE_ENVELOPE) - 2);
-	parse_cached_envelope(tmpenv, envtokens, VECTOR_SIZE(envtokens));
-	record->gmtime = message_parse_date(envtokens[ENV_DATE],
-			 PARSE_TIME|PARSE_ZONE|PARSE_NOCREATE|PARSE_GMT);
-	free(tmpenv);
-    }
-    else {
-	/* better than nothing! */
-	record->gmtime = record->internaldate;
-    }
-
-    return 0;
+    struct expunge_data *ea = (struct expunge_data *)a;
+    struct expunge_data *eb = (struct expunge_data *)b;
+    return ea->uid - eb->uid;
 }
 
 static void upgrade_index_record(struct mailbox *mailbox,
 				 const char *buf,
-				 int old_version,
 				 struct index_record *record,
 				 int record_size)
 {
-    indexbuffer_t rbuf;
-    char *recordbuf = (char *)rbuf.buf;
-    int recalc = 0;
+    char recordbuf[INDEX_RECORD_SIZE];
     struct utimbuf settime;
     const char *fname;
 
+    assert(record_size <= INDEX_RECORD_SIZE);
+
     memset(recordbuf, 0, INDEX_RECORD_SIZE);
-    if (INDEX_RECORD_SIZE < record_size)
-	memcpy(recordbuf, buf, INDEX_RECORD_SIZE);
-    else
-	memcpy(recordbuf, buf, record_size);
-
-    /* CONTENT_LINES added with minor version 5 */
-    /* CACHE_VERSION added with minor version 6 */
-
-    /* 12-byte GUIDs added with minor version 7 */
-    /* GUIDs extended from 12 to 20 bytes with minor version 10 */
-    if (old_version < 10)
-	recalc = 1;
-    else {
-	/* if it's all zeros for the final 8 bits, it was probably upgraded
-	 * and also needs recalculation */
-	if (ntohl(*((bit32 *)(recordbuf+OFFSET_MESSAGE_GUID+12))) == 0 &&
-	    ntohl(*((bit32 *)(recordbuf+OFFSET_MESSAGE_GUID+16))) == 0)
-	    recalc = 1;
-    }
+    memcpy(recordbuf, buf, record_size);
 
     /* do the initial parse.  Ignore the result, crc32 will mismatch
      * for sure */
     mailbox_buf_to_index_record(recordbuf, record);
 
-    if (!recalc && old_version < 12) {
-	/* let's try a cheaper upgrade option - just reading the 
-	   cache record for details */
-	if (update_record_from_cache(mailbox, record))
-	    recalc = 1;
-    }
-
     fname = mailbox_message_fname(mailbox, record->uid);
 
-    if (recalc) {
-	if (message_parse(fname, record)) {
-	    /* failed to create, don't try to write */
-	    record->crec.len = 0;
-	    /* and the record is expunged too! */
-	    record->system_flags |= FLAG_EXPUNGED | FLAG_UNLINKED;
-	    syslog(LOG_ERR, "IOERROR: FATAL - failed to parse "
-			    "file %s for upgrade, expunging", fname);
-	    return;
-	}
+    if (message_parse(fname, record)) {
+	/* failed to create, don't try to write */
+	record->crec.len = 0;
+	/* and the record is expunged too! */
+	record->system_flags |= FLAG_EXPUNGED | FLAG_UNLINKED;
+	syslog(LOG_ERR, "IOERROR: FATAL - failed to parse "
+			"file %s for upgrade, expunging", fname);
+	return;
     }
 
     /* update the mtime to match the internaldate */
@@ -204,25 +138,27 @@ static void upgrade_index_record(struct mailbox *mailbox,
 int upgrade_index(struct mailbox *mailbox)
 {
     uint32_t recno, erecno;
+    uint32_t uid, euid;
     unsigned long oldmapnum;
     unsigned long oldnum_records;
     unsigned long expunge_num = 0;
-    unsigned uid = 0;
-    bit32 oldminor_version, oldstart_offset, oldrecord_size;
+    uint32_t oldminor_version, oldstart_offset, oldrecord_size;
     indexbuffer_t headerbuf;
-    indexbuffer_t recordbuf;
     const char *bufp = NULL;
     char *hbuf = (char *)headerbuf.buf;
-    char *rbuf = (char *)recordbuf.buf;
-    int newindex_fd = -1;
     const char *fname;
     const char *datadirname;
     struct stat sbuf;
     struct seqset *seq = NULL;
     struct index_record record;
-    struct index_record *expunge_records = NULL;
-    struct index_record *recordptr;
-    int r, n;
+    int expunge_fd = -1;
+    const char *expunge_base = NULL;
+    unsigned long expunge_len = 0;   /* mapped size */
+    unsigned long emapnum;
+    bit32 eversion, eoffset, expungerecord_size;
+    struct expunge_data *expunge_data = NULL;
+    struct mailbox_repack *repack = NULL;
+    int r;
 
     if (mailbox->index_size < OFFSET_NUM_RECORDS)
 	return IMAP_MAILBOX_BADFORMAT;
@@ -276,7 +212,7 @@ int upgrade_index(struct mailbox *mailbox)
     }
 
     /* ignore the result - we EXPECT a CRC32 mismatch */
-    mailbox_buf_to_index_header(&mailbox->i, hbuf);
+    mailbox_buf_to_index_header(hbuf, &mailbox->i);
 
     /* HIGHESTMODSEQ[_64] added with minor version 8 */
     if (oldminor_version < 8)
@@ -301,13 +237,7 @@ int upgrade_index(struct mailbox *mailbox)
     if (oldminor_version < 12) {
 	struct seen *seendb;
 	struct seendata sd;
-	unsigned long erecno;
-	unsigned long emapnum;
-	bit32 eversion, eoffset, esize;
 	char *owner_userid;
-	int expunge_fd = -1;
-	const char *expunge_base = NULL;
-	unsigned long expunge_len = 0;   /* mapped size */
 
 	/* remove the CONDSTORE option - it's implicit now */
 	mailbox->i.options &= ~OPT_IMAP_CONDSTORE;
@@ -326,8 +256,6 @@ int upgrade_index(struct mailbox *mailbox)
 	mailbox->i.first_expunged = 0;
 	/* we can't know about deletions before the current modseq */
 	mailbox->i.deletedmodseq = mailbox->i.highestmodseq;
-	/* we're repacking now! */
-	mailbox->i.last_repack_time = time(NULL);
 	/* bootstrap CRC matching */
 	mailbox->i.header_file_crc = mailbox->header_file_crc;
 
@@ -369,61 +297,38 @@ int upgrade_index(struct mailbox *mailbox)
 	 * versions are skewed for some reason */
 	eversion = ntohl(*((bit32 *)(expunge_base+OFFSET_MINOR_VERSION)));
 	eoffset = ntohl(*((bit32 *)(expunge_base+OFFSET_START_OFFSET)));
-	esize = ntohl(*((bit32 *)(expunge_base+OFFSET_RECORD_SIZE)));
-	expunge_num = ntohl(*((bit32 *)(expunge_base+OFFSET_NUM_RECORDS)));
+	expungerecord_size = ntohl(*((bit32 *)(expunge_base+OFFSET_RECORD_SIZE)));
 
 	/* bogus data at the start of the expunge file? */
-	if (!eoffset || !esize)
+	if (!eoffset || !expungerecord_size)
 	    goto no_expunge;
 
-	expunge_records = xmalloc(expunge_num * sizeof(struct index_record));
-	emapnum = (sbuf.st_size - eoffset) / esize;
+	expunge_num = ntohl(*((bit32 *)(expunge_base+OFFSET_NUM_RECORDS)));
+	emapnum = (sbuf.st_size - eoffset) / expungerecord_size;
 	if (emapnum < expunge_num) {
 	    syslog(LOG_ERR, "IOERROR: %s map doesn't fit, shrinking expunge %lu to %lu",
 		   mailbox->name, expunge_num, emapnum);
 	    expunge_num = emapnum;
 	}
 
+	/* make sure there's space for them */
+	expunge_data = xmalloc(expunge_num * sizeof(struct expunge_data));
+
+	/* find the start offset for each record, and the UID */
 	for (erecno = 1; erecno <= expunge_num; erecno++) {
-	    struct index_record *record = &expunge_records[erecno-1];
-	    bufp = expunge_base + eoffset + (erecno-1)*esize;
-	    upgrade_index_record(mailbox, bufp, eversion, record, esize);
-	    record->system_flags |= FLAG_EXPUNGED;
-	    if (!mailbox->i.first_expunged ||
-		mailbox->i.first_expunged > record->last_updated)
-		mailbox->i.first_expunged = record->last_updated;
+	    bufp = expunge_base + eoffset + (erecno-1)*expungerecord_size;
+	    expunge_data[erecno-1].uid = ntohl(*((bit32 *)(bufp+OFFSET_UID)));
+	    expunge_data[erecno-1].base = bufp;
 	}
 
 	/* expunge files were not sorted.  So sort them now for easier
 	 * interleaving */
-	qsort(expunge_records, expunge_num, 
-	      sizeof(struct index_record), &sort_record);
-
-no_expunge:
-	if (expunge_fd != -1) close(expunge_fd);
-	if (expunge_base) map_free(&expunge_base, &expunge_len);
+	qsort(expunge_data, expunge_num, 
+	      sizeof(struct expunge_data), &sort_expunge);
     }
+no_expunge:
 
-    /* update buffer with upgraded values */
-    mailbox_index_header_to_buf(&mailbox->i, (unsigned char *)hbuf);
-
-    /* open the new index file */
-    fname = mailbox_meta_newfname(mailbox, META_INDEX);
-    newindex_fd = open(fname, O_RDWR|O_TRUNC|O_CREAT, 0666);
-    if (newindex_fd == -1) goto fail;
-
-    /* Write new header - first pass only */
-    n = retry_write(newindex_fd, hbuf, INDEX_HEADER_SIZE);
-    if (n == -1) goto fail;
-
-    /* initialise counters */
-    mailbox->i.quota_mailbox_used = 0;
-    mailbox->i.num_records = 0;
-    mailbox->i.sync_crc = 0; /* no records is blank */
-    mailbox->i.answered = 0;
-    mailbox->i.deleted = 0;
-    mailbox->i.flagged = 0;
-    mailbox->i.exists = 0;
+    mailbox_repack_setup(mailbox, &repack);
 
     /* Write the rest of new index */
     recno = 1;
@@ -434,57 +339,39 @@ no_expunge:
 	    bufp = mailbox->index_base + oldstart_offset + (recno-1)*oldrecord_size;
 	    uid = ntohl(*((bit32 *)(bufp+OFFSET_UID)));
 	}
-
-	/* case: only expunge records left */
-	if (recno > oldnum_records) {
-	    recordptr = &expunge_records[erecno-1];
-	    erecno++;
+	else {
+	    uid = UINT32_MAX;
+	}
+	if (erecno <= expunge_num) {
+	    euid = expunge_data[erecno-1].uid;
+	}
+	else {
+	    euid = UINT32_MAX;
 	}
 
-	/* case: index record is lower uid */
-	else if (erecno > expunge_num || uid <= expunge_records[erecno-1].uid) {
-	    upgrade_index_record(mailbox, bufp, oldminor_version, &record,
-				 oldrecord_size);
+	/* case: index UID is first, or the same */
+	if (uid <= euid) {
+	    upgrade_index_record(mailbox, bufp, &record, oldrecord_size);
 	    recno++;
-	    if (erecno <= expunge_num && uid == expunge_records[erecno-1].uid)
-		erecno++; /* duplicate UID - skip expunge record */
-	    recordptr = &record;
+	    if (uid == euid) /* duplicate in both, skip expunged */
+		erecno++;
 	}
-
-	/* case: expunge record is lower uid */
 	else {
-	    recordptr = &expunge_records[erecno-1];
+	    upgrade_index_record(mailbox, expunge_data[erecno-1].base,
+				 &record, expungerecord_size);
+	    record.system_flags |= FLAG_EXPUNGED;
 	    erecno++;
 	}
 
-	if (oldminor_version < 12 && seqset_ismember(seq, recordptr->uid))
-	    recordptr->system_flags |= FLAG_SEEN;
+	if (oldminor_version < 12 && seqset_ismember(seq, record.uid))
+	    record.system_flags |= FLAG_SEEN;
 
-	/* write the cache record if necessary */
-	r = mailbox_append_cache(mailbox, recordptr);
+	r = mailbox_repack_add(repack, &record);
 	if (r) goto fail;
-
-	mailbox_index_update_counts(mailbox, recordptr, 1);
-	mailbox_index_record_to_buf(recordptr, (unsigned char *)rbuf);
-
-	n = retry_write(newindex_fd, rbuf, INDEX_RECORD_SIZE);
-	if (n == -1) goto fail;
-
-	mailbox->i.num_records++;
     }
 
-    mailbox_index_header_to_buf(&mailbox->i, (unsigned char *)hbuf);
-
-    lseek(newindex_fd, 0L, SEEK_SET);
-    n = retry_write(newindex_fd, hbuf, INDEX_HEADER_SIZE);
-    if (n == -1) goto fail;
-
-    r = fsync(newindex_fd);
-    if (r == -1) goto fail;
-    close(newindex_fd);
-
-    r = mailbox_meta_rename(mailbox, META_INDEX);
-    if (r == -1) goto fail;
+    r = mailbox_repack_commit(&repack);
+    if (r) goto fail;
 
     /* don't need this file any more! */
     unlink(mailbox_meta_fname(mailbox, META_EXPUNGE));
@@ -495,16 +382,10 @@ no_expunge:
 	   oldminor_version, MAILBOX_MINOR_VERSION);
 
 done:
+    if (expunge_fd != -1) close(expunge_fd);
+    if (expunge_base) map_free(&expunge_base, &expunge_len);
     seqset_free(seq);
-    free(expunge_records);
-
-    /* commit the cache first so it doesn't stay "dirty" */
-    mailbox_commit_cache(mailbox);
-
-    /* special case, completely forgiven from being clean... */
-    mailbox->i.dirty = 0;
-    mailbox->quota_dirty = 0;
-    mailbox->modseq_dirty = 0;
+    free(expunge_data);
 
     /* it's definitely changed! */
     r = mailbox_open_index(mailbox);
@@ -513,9 +394,12 @@ done:
     return 0;
 
 fail:
-    if (newindex_fd != -1) close(newindex_fd);
+    if (expunge_fd != -1) close(expunge_fd);
+    if (expunge_base) map_free(&expunge_base, &expunge_len);
     seqset_free(seq);
-    free(expunge_records);
+    free(expunge_data);
+
+    mailbox_repack_abort(&repack);
 
     syslog(LOG_ERR, "Index upgrade failed: %s", mailbox->name);
 
-- 
1.7.1

----
Cyrus Home Page: http://www.cyrusimap.org/
List Archives/Info: http://lists.andrew.cmu.edu/pipermail/info-cyrus/

[Index of Archives]     [Cyrus SASL]     [Squirrel Mail]     [Asterisk PBX]     [Video For Linux]     [Photo]     [Yosemite News]     [gtk]     [KDE]     [Gimp on Windows]     [Steve's Art]

  Powered by Linux