static int mailbox_index_repack(struct mailbox *mailbox, int version)
{
    struct mailbox_repack *repack = NULL;
    const message_t *msg;
    struct mailbox_iter *iter = NULL;
    struct buf buf = BUF_INITIALIZER;
    int r = IMAP_IOERROR;

    syslog(LOG_INFO, "Repacking mailbox %s version %d", mailbox_name(mailbox), version);

    r = mailbox_repack_setup(mailbox, version, &repack);
    if (r) goto done;

    iter = mailbox_iter_init(mailbox, 0, 0);
    while ((msg = mailbox_iter_step(iter))) {
        const struct index_record *record = msg_record(msg);
        struct index_record copyrecord = *record;
        int needs_cache_upgrade = 0;
        annotate_state_t *astate = NULL;

        r = mailbox_get_annotate_state(mailbox, record->uid, &astate);
        if (r) goto done;

        /* version changes? */
        if (mailbox->i.minor_version < 12 && repack->newmailbox.i.minor_version >= 12) {
            if (seqset_ismember(repack->seqset, copyrecord.uid))
                copyrecord.system_flags |= FLAG_SEEN;
            else
                copyrecord.system_flags &= ~FLAG_SEEN;

            needs_cache_upgrade = 1;

        }
        if (mailbox->i.minor_version >= 12 && repack->newmailbox.i.minor_version < 12) {
            if (repack->seqset)
                seqset_add(repack->seqset, copyrecord.uid, copyrecord.system_flags & FLAG_SEEN ? 1 : 0);
            copyrecord.system_flags &= ~FLAG_SEEN;
        }

        /* force cache upgrade across version 15 repack */
        if (repack->newmailbox.i.minor_version >= 15 && record->cache_version < 9)
            needs_cache_upgrade = 1;

        /* better handle the cleanup just in case it's unlinked too */
        /* still gotta check for FLAG_INTERNAL_UNLINKED, because it may have been
         * created by old code.  Woot */
        if (copyrecord.internal_flags & (FLAG_INTERNAL_NEEDS_CLEANUP | FLAG_INTERNAL_UNLINKED)) {
            mailbox_record_cleanup(mailbox, &copyrecord);
            copyrecord.internal_flags &= ~FLAG_INTERNAL_NEEDS_CLEANUP;
            /* no need to rewrite - it's already being written to the new file */
        }

        /* we aren't keeping unlinked files, that's kind of the point */
        if (copyrecord.internal_flags & FLAG_INTERNAL_UNLINKED) {
            /* track the modseq for QRESYNC purposes */
            if (copyrecord.modseq > repack->newmailbox.i.deletedmodseq) {
                repack->newmailbox.i.deletedmodseq = copyrecord.modseq;
                repack->newmailbox.i.changes_epoch = copyrecord.last_updated;
            }
            continue;
        }

        if (needs_cache_upgrade) {
            const char *fname = mailbox_record_fname(mailbox, &copyrecord);

            if (message_parse(fname, &copyrecord)) {
                /* failed to parse, don't try to write out record */
                copyrecord.crec.len = 0;
                /* and the record is expunged too! */
                copyrecord.internal_flags |= FLAG_INTERNAL_EXPUNGED | FLAG_INTERNAL_UNLINKED;
                xsyslog(LOG_ERR, "IOERROR: FATAL - failed to parse file, expunging",
                                 "mailbox=<%s> record=<%u>",
                                 mailbox_name(repack->mailbox), copyrecord.uid);
            }
        }

        /* virtual annotations */
        if (mailbox->i.minor_version < 13 && repack->newmailbox.i.minor_version >= 13) {
            /* extract CID */
            buf_reset(&buf);
            mailbox_annotation_lookup(mailbox, record->uid, IMAP_ANNOT_NS "thrid", "", &buf);
            if (buf.len == 16) {
                const char *p = buf_cstring(&buf);
                parsehex(p, &p, 16, &copyrecord.cid);
            }
            buf_reset(&buf);
            r = annotate_state_writesilent(astate, IMAP_ANNOT_NS "thrid", "", &buf);
            if (r) goto done;
        }
        if (mailbox->i.minor_version >= 13 && repack->newmailbox.i.minor_version < 13) {
            if (record->cid) {
                buf_reset(&buf);
                buf_printf(&buf, "%llx", record->cid);
                r = annotate_state_writesilent(astate, IMAP_ANNOT_NS "thrid", "", &buf);
                if (r) goto done;
            }
        }

        if (mailbox->i.minor_version < 15 && repack->newmailbox.i.minor_version >= 15) {
            /* extract CID */
            buf_reset(&buf);
            mailbox_annotation_lookup(mailbox, record->uid, IMAP_ANNOT_NS "savedate", "", &buf);
            if (buf.len) {
                const char *p = buf_cstring(&buf);
                bit64 newval;
                parsenum(p, &p, 0, &newval);
                copyrecord.savedate = newval;
            }
            buf_reset(&buf);
            r = annotate_state_writesilent(astate, IMAP_ANNOT_NS "savedate", "", &buf);
            if (r) goto done;
        }
        if (mailbox->i.minor_version >= 15 && repack->newmailbox.i.minor_version < 15) {
            if (record->savedate) {
                buf_reset(&buf);
                buf_printf(&buf, TIME_T_FMT, record->savedate);
                r = annotate_state_writesilent(astate, IMAP_ANNOT_NS "savedate", "", &buf);
                if (r) goto done;
            }
        }

        if (mailbox->i.minor_version < 16 && repack->newmailbox.i.minor_version >= 16) {
            /* extract CID */
            buf_reset(&buf);
            mailbox_annotation_lookup(mailbox, record->uid, IMAP_ANNOT_NS "createdmodseq", "", &buf);
            if (buf.len) {
                const char *p = buf_cstring(&buf);
                bit64 newval;
                parsenum(p, &p, 0, &newval);
                copyrecord.createdmodseq = newval;
            }
            buf_reset(&buf);
            r = annotate_state_writesilent(astate, IMAP_ANNOT_NS "createdmodseq", "", &buf);
            if (r) goto done;
        }
        if (mailbox->i.minor_version >= 16 && repack->newmailbox.i.minor_version < 16) {
            if (record->createdmodseq) {
                buf_reset(&buf);
                buf_printf(&buf, "%llu", record->createdmodseq);
                r = annotate_state_writesilent(astate, IMAP_ANNOT_NS "createdmodseq", "", &buf);
                if (r) goto done;
            }
        }

        /* read in the old cache record */
        r = mailbox_cacherecord(mailbox, &copyrecord);
        if (r) goto done;

        r = mailbox_repack_add(repack, &copyrecord);
        if (r) goto done;
    }

    /* we unlinked any "needs unlink" in the process */
    repack->newmailbox.i.options &= ~(OPT_MAILBOX_NEEDS_REPACK|OPT_MAILBOX_NEEDS_UNLINK);

done:
    mailbox_iter_done(&iter);
    buf_free(&buf);
    if (r) mailbox_repack_abort(&repack);
    else {
        modseq_t deletedmodseq = repack->newmailbox.i.deletedmodseq;

        r = mailbox_repack_commit(&repack);
        if (!r) {
            mboxname_setmodseq(mailbox_name(mailbox), deletedmodseq, mailbox_mbtype(mailbox),
                               MBOXMODSEQ_ISDELETE);
        }
    }       

    return r;
}
static int mailbox_index_repack(struct mailbox *mailbox, int version)
{
    struct mailbox_repack *repack = NULL;
    const message_t *msg;
    struct mailbox_iter *iter = NULL;
    struct buf buf = BUF_INITIALIZER;
    int r = IMAP_IOERROR;

    syslog(LOG_INFO, "Repacking mailbox %s version %d", mailbox_name(mailbox), version);

    r = mailbox_repack_setup(mailbox, version, &repack);
    if (r) goto done;

    iter = mailbox_iter_init(mailbox, 0, 0);
    while ((msg = mailbox_iter_step(iter))) {
        const struct index_record *record = msg_record(msg);
        struct index_record copyrecord = *record;
        int needs_cache_upgrade = 0;
        annotate_state_t *astate = NULL;

        r = mailbox_get_annotate_state(mailbox, record->uid, &astate);
        if (r) goto done;

         
        if (mailbox->i.minor_version < 12 && repack->newmailbox.i.minor_version >= 12) {
            if (seqset_ismember(repack->seqset, copyrecord.uid))
                copyrecord.system_flags |= FLAG_SEEN;
            else
                copyrecord.system_flags &= ~FLAG_SEEN;

            needs_cache_upgrade = 1;

        }
        if (mailbox->i.minor_version >= 12 && repack->newmailbox.i.minor_version < 12) {
            if (repack->seqset)
                seqset_add(repack->seqset, copyrecord.uid, copyrecord.system_flags & FLAG_SEEN ? 1 : 0);
            copyrecord.system_flags &= ~FLAG_SEEN;
        }

         
        if (repack->newmailbox.i.minor_version >= 15 && record->cache_version < 9)
            needs_cache_upgrade = 1;

         
         
        if (copyrecord.internal_flags & (FLAG_INTERNAL_NEEDS_CLEANUP | FLAG_INTERNAL_UNLINKED)) {
            mailbox_record_cleanup(mailbox, &copyrecord);
            copyrecord.internal_flags &= ~FLAG_INTERNAL_NEEDS_CLEANUP;
             
        }

         
        if (copyrecord.internal_flags & FLAG_INTERNAL_UNLINKED) {
             
            if (copyrecord.modseq > repack->newmailbox.i.deletedmodseq) {
                repack->newmailbox.i.deletedmodseq = copyrecord.modseq;
                repack->newmailbox.i.changes_epoch = copyrecord.last_updated;
            }
            continue;
        }

        if (needs_cache_upgrade) {
            const char *fname = mailbox_record_fname(mailbox, &copyrecord);

            if (message_parse(fname, &copyrecord)) {
                 
                copyrecord.crec.len = 0;
                 
                copyrecord.internal_flags |= FLAG_INTERNAL_EXPUNGED | FLAG_INTERNAL_UNLINKED;
                xsyslog(LOG_ERR, "IOERROR: FATAL - failed to parse file, expunging",
                                 "mailbox=<%s> record=<%u>",
                                 mailbox_name(repack->mailbox), copyrecord.uid);
            }
        }

         
        if (mailbox->i.minor_version < 13 && repack->newmailbox.i.minor_version >= 13) {
             
            buf_reset(&buf);
            mailbox_annotation_lookup(mailbox, record->uid, IMAP_ANNOT_NS "thrid", "", &buf);
            if (buf.len == 16) {
                const char *p = buf_cstring(&buf);
                parsehex(p, &p, 16, &copyrecord.cid);
            }
            buf_reset(&buf);
            r = annotate_state_writesilent(astate, IMAP_ANNOT_NS "thrid", "", &buf);
            if (r) goto done;
        }
        if (mailbox->i.minor_version >= 13 && repack->newmailbox.i.minor_version < 13) {
            if (record->cid) {
                buf_reset(&buf);
                buf_printf(&buf, "%llx", record->cid);
                r = annotate_state_writesilent(astate, IMAP_ANNOT_NS "thrid", "", &buf);
                if (r) goto done;
            }
        }

        if (mailbox->i.minor_version < 15 && repack->newmailbox.i.minor_version >= 15) {
             
            buf_reset(&buf);
            mailbox_annotation_lookup(mailbox, record->uid, IMAP_ANNOT_NS "savedate", "", &buf);
            if (buf.len) {
                const char *p = buf_cstring(&buf);
                bit64 newval;
                parsenum(p, &p, 0, &newval);
                copyrecord.savedate = newval;
            }
            buf_reset(&buf);
            r = annotate_state_writesilent(astate, IMAP_ANNOT_NS "savedate", "", &buf);
            if (r) goto done;
        }
        if (mailbox->i.minor_version >= 15 && repack->newmailbox.i.minor_version < 15) {
            if (record->savedate) {
                buf_reset(&buf);
                buf_printf(&buf, TIME_T_FMT, record->savedate);
                r = annotate_state_writesilent(astate, IMAP_ANNOT_NS "savedate", "", &buf);
                if (r) goto done;
            }
        }

        if (mailbox->i.minor_version < 16 && repack->newmailbox.i.minor_version >= 16) {
             
            buf_reset(&buf);
            mailbox_annotation_lookup(mailbox, record->uid, IMAP_ANNOT_NS "createdmodseq", "", &buf);
            if (buf.len) {
                const char *p = buf_cstring(&buf);
                bit64 newval;
                parsenum(p, &p, 0, &newval);
                copyrecord.createdmodseq = newval;
            }
            buf_reset(&buf);
            r = annotate_state_writesilent(astate, IMAP_ANNOT_NS "createdmodseq", "", &buf);
            if (r) goto done;
        }
        if (mailbox->i.minor_version >= 16 && repack->newmailbox.i.minor_version < 16) {
            if (record->createdmodseq) {
                buf_reset(&buf);
                buf_printf(&buf, "%llu", record->createdmodseq);
                r = annotate_state_writesilent(astate, IMAP_ANNOT_NS "createdmodseq", "", &buf);
                if (r) goto done;
            }
        }

         
        r = mailbox_cacherecord(mailbox, &copyrecord);
        if (r) goto done;

        r = mailbox_repack_add(repack, &copyrecord);
        if (r) goto done;
    }

     
    repack->newmailbox.i.options &= ~(OPT_MAILBOX_NEEDS_REPACK|OPT_MAILBOX_NEEDS_UNLINK);

done:
    mailbox_iter_done(&iter);
    buf_free(&buf);
    if (r) mailbox_repack_abort(&repack);
    else {
        modseq_t deletedmodseq = repack->newmailbox.i.deletedmodseq;

        r = mailbox_repack_commit(&repack);
        if (!r) {
            mboxname_setmodseq(mailbox_name(mailbox), deletedmodseq, mailbox_mbtype(mailbox),
                               MBOXMODSEQ_ISDELETE);
        }
    }       

    return r;
}