HIDDEN int mailbox_repack_commit(struct mailbox_repack **repackptr)
{
    strarray_t cachefiles = STRARRAY_INITIALIZER;
    indexbuffer_t ibuf;
    unsigned char *buf = ibuf.buf;
    struct mailbox_repack *repack = *repackptr;
    int r = IMAP_IOERROR;
    int i;

    assert(repack);

    /* if we changed versions, we'll re-calculate counts on the new mailbox too */
    if (repack->newmailbox.i.minor_version != repack->mailbox->i.minor_version) {
        r = mailbox_refresh_index_map(&repack->newmailbox);
        if (r) goto fail;
        r = mailbox_index_recalc(&repack->newmailbox);
        if (r) goto fail;
    }

    if (repack->newmailbox.i.minor_version >= 10 &&
            repack->mailbox->i.minor_version >= 10 &&
            !mailbox_crceq(repack->newmailbox.i.synccrcs, repack->crcs)) {
        xsyslog(LOG_ERR, "IOERROR: CRC mismatch on repack commit",
                         "mailbox=<%s> oldbasic=<%u> newbasic=<%u> "
                             "oldannot=<%u> newannot=<%u>",
                         mailbox_name(repack->mailbox),
                         repack->crcs.basic, repack->newmailbox.i.synccrcs.basic,
                         repack->crcs.annot, repack->newmailbox.i.synccrcs.annot);
        r = IMAP_MAILBOX_CHECKSUM;
        goto fail;
    }

    repack->newmailbox.i.last_repack_time = time(0);

    if (repack->mailbox->i.minor_version >= 12 && repack->newmailbox.i.minor_version < 12
        && repack->seqset && repack->userid) {
        struct seendata sd = SEENDATA_INITIALIZER;
        struct seen *seendb = NULL;
        int r = seen_open(repack->userid, SEEN_CREATE, &seendb);
        if (!r) r = seen_lockread(seendb, mailbox_uniqueid(repack->mailbox), &sd);
        if (!r) {
            sd.lastuid = repack->newmailbox.i.last_uid;
            sd.seenuids = seqset_cstring(repack->seqset);
            if (!sd.seenuids) sd.seenuids = xstrdup("");
            sd.lastread = time(NULL);
            sd.lastchange = repack->newmailbox.i.last_appenddate;
            r = seen_write(seendb, mailbox_uniqueid(repack->mailbox), &sd);
            /* XXX - syslog on errors? */
        }
        seen_close(&seendb);
        seen_freedata(&sd);
    }

    /* rewrite the header with updated details */
    mailbox_index_header_to_buf(&repack->newmailbox.i, buf);

    if (lseek(repack->newmailbox.index_fd, 0, SEEK_SET) < 0)
        goto fail;

    if (retry_write(repack->newmailbox.index_fd, buf, repack->newmailbox.i.start_offset) < 0)
        goto fail;

    /* ensure everything is committed to disk */
    if (fsync(repack->newmailbox.index_fd) < 0)
        goto fail;

    xclose(repack->newmailbox.index_fd);

    /* NOTE: cache files need committing before index is renamed */
    for (i = 0; i < repack->caches.count; i++) {
        struct mappedfile *cachefile = ptrarray_nth(&repack->caches, i);
        r = mappedfile_commit(cachefile);
        if (r) goto fail;
    }

    /* 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;

    /* which cache files might currently exist? */
    strarray_add(&cachefiles, mailbox_meta_fname(repack->mailbox, META_CACHE));
    strarray_add(&cachefiles, mailbox_meta_fname(repack->mailbox, META_ARCHIVECACHE));

    /* now the cache files can be renamed */
    for (i = 0; i < repack->caches.count; i++) {
        struct mappedfile *cachefile = ptrarray_nth(&repack->caches, i);
        char *newname = xstrdup(mappedfile_fname(cachefile));
        size_t len = strlen(newname)-4;
        assert(!strcmp(newname+len, ".NEW"));
        newname[len] = '\0'; /* STRIP .NEW */
        mappedfile_rename(cachefile, newname);
        mappedfile_close(&cachefile);
        strarray_remove_all(&cachefiles, newname);
        free(newname);
    }
    ptrarray_fini(&repack->caches);

    for (i = 0; i < cachefiles.count; i++) {
        const char *fname = strarray_nth(&cachefiles, i);
        if (!unlink(fname))
            syslog(LOG_NOTICE, "Removed unused cache file %s", fname);
    }

    strarray_fini(&cachefiles);

    // drop the map if we've mapped in the newmailbox index separately
    if (repack->newmailbox.index_base != repack->mailbox->index_base) {
        map_free(&repack->newmailbox.index_base, &repack->newmailbox.index_len);
    }

    seqset_free(&repack->seqset);
    free(repack->userid);
    free(repack);
    *repackptr = NULL;
    return 0;

 fail:
    strarray_fini(&cachefiles);
    mailbox_repack_abort(repackptr);
    if (!r) r = IMAP_IOERROR;
    return r;
}
HIDDEN int mailbox_repack_commit(struct mailbox_repack **repackptr)
{
    strarray_t cachefiles = STRARRAY_INITIALIZER;
    indexbuffer_t ibuf;
    unsigned char *buf = ibuf.buf;
    struct mailbox_repack *repack = *repackptr;
    int r = IMAP_IOERROR;
    int i;

    assert(repack);

     
    if (repack->newmailbox.i.minor_version != repack->mailbox->i.minor_version) {
        r = mailbox_refresh_index_map(&repack->newmailbox);
        if (r) goto fail;
        r = mailbox_index_recalc(&repack->newmailbox);
        if (r) goto fail;
    }

    if (repack->newmailbox.i.minor_version >= 10 &&
            repack->mailbox->i.minor_version >= 10 &&
            !mailbox_crceq(repack->newmailbox.i.synccrcs, repack->crcs)) {
        xsyslog(LOG_ERR, "IOERROR: CRC mismatch on repack commit",
                         "mailbox=<%s> oldbasic=<%u> newbasic=<%u> "
                             "oldannot=<%u> newannot=<%u>",
                         mailbox_name(repack->mailbox),
                         repack->crcs.basic, repack->newmailbox.i.synccrcs.basic,
                         repack->crcs.annot, repack->newmailbox.i.synccrcs.annot);
        r = IMAP_MAILBOX_CHECKSUM;
        goto fail;
    }

    repack->newmailbox.i.last_repack_time = time(0);

    if (repack->mailbox->i.minor_version >= 12 && repack->newmailbox.i.minor_version < 12
        && repack->seqset && repack->userid) {
        struct seendata sd = SEENDATA_INITIALIZER;
        struct seen *seendb = NULL;
        int r = seen_open(repack->userid, SEEN_CREATE, &seendb);
        if (!r) r = seen_lockread(seendb, mailbox_uniqueid(repack->mailbox), &sd);
        if (!r) {
            sd.lastuid = repack->newmailbox.i.last_uid;
            sd.seenuids = seqset_cstring(repack->seqset);
            if (!sd.seenuids) sd.seenuids = xstrdup("");
            sd.lastread = time(NULL);
            sd.lastchange = repack->newmailbox.i.last_appenddate;
            r = seen_write(seendb, mailbox_uniqueid(repack->mailbox), &sd);
             
        }
        seen_close(&seendb);
        seen_freedata(&sd);
    }

     
    mailbox_index_header_to_buf(&repack->newmailbox.i, buf);

    if (lseek(repack->newmailbox.index_fd, 0, SEEK_SET) < 0)
        goto fail;

    if (retry_write(repack->newmailbox.index_fd, buf, repack->newmailbox.i.start_offset) < 0)
        goto fail;

     
    if (fsync(repack->newmailbox.index_fd) < 0)
        goto fail;

    xclose(repack->newmailbox.index_fd);

     
    for (i = 0; i < repack->caches.count; i++) {
        struct mappedfile *cachefile = ptrarray_nth(&repack->caches, i);
        r = mappedfile_commit(cachefile);
        if (r) goto fail;
    }

     
    r = mailbox_meta_rename(repack->mailbox, META_INDEX);
    if (r) goto fail;

     
    strarray_add(&cachefiles, mailbox_meta_fname(repack->mailbox, META_CACHE));
    strarray_add(&cachefiles, mailbox_meta_fname(repack->mailbox, META_ARCHIVECACHE));

     
    for (i = 0; i < repack->caches.count; i++) {
        struct mappedfile *cachefile = ptrarray_nth(&repack->caches, i);
        char *newname = xstrdup(mappedfile_fname(cachefile));
        size_t len = strlen(newname)-4;
        assert(!strcmp(newname+len, ".NEW"));
        newname[len] = '\0';  
        mappedfile_rename(cachefile, newname);
        mappedfile_close(&cachefile);
        strarray_remove_all(&cachefiles, newname);
        free(newname);
    }
    ptrarray_fini(&repack->caches);

    for (i = 0; i < cachefiles.count; i++) {
        const char *fname = strarray_nth(&cachefiles, i);
        if (!unlink(fname))
            syslog(LOG_NOTICE, "Removed unused cache file %s", fname);
    }

    strarray_fini(&cachefiles);

     
    if (repack->newmailbox.index_base != repack->mailbox->index_base) {
        map_free(&repack->newmailbox.index_base, &repack->newmailbox.index_len);
    }

    seqset_free(&repack->seqset);
    free(repack->userid);
    free(repack);
    *repackptr = NULL;
    return 0;

 fail:
    strarray_fini(&cachefiles);
    mailbox_repack_abort(repackptr);
    if (!r) r = IMAP_IOERROR;
    return r;
}