static int mailbox_repack_setup(struct mailbox *mailbox, int version,
                                struct mailbox_repack **repackptr)
{
    struct mailbox_repack *repack = xzmalloc(sizeof(struct mailbox_repack));
    const char *fname;
    indexbuffer_t ibuf;
    unsigned char *buf = ibuf.buf;
    int n;

    /* if we're changing version at all, recalculate counts up-front */
    if (version != mailbox->i.minor_version) {
        /* NOTE: this maps in annot_state in mailbox, which will get copied
         * into newmailbox below, which means all the annotate stuff later
         * is going to be good!  The counters will be updated in the original
         * mailbox index, but that's OK because we recalc everything at the
         * end */
        int r = mailbox_index_recalc(mailbox);
        if (r) goto fail;
    }

    /* init */
    repack->mailbox = mailbox;
    repack->crcs = mailbox->i.synccrcs;
    repack->newmailbox = *mailbox; // struct copy
    repack->newmailbox.index_fd = -1;

    /* new files */
    fname = mailbox_meta_newfname(mailbox, META_INDEX);
    repack->newmailbox.index_fd = open(fname, O_RDWR|O_TRUNC|O_CREAT, 0666);
    if (repack->newmailbox.index_fd == -1) {
        xsyslog(LOG_ERR, "IOERROR: create failed",
                         "fname=<%s>",
                         fname);
        goto fail;
    }

    /* update the generation number */
    repack->newmailbox.i.generation_no++;

    /* track the version number */
    repack->newmailbox.i.minor_version = version;
    switch (version) {
    case 6:
        repack->newmailbox.i.start_offset = 76;
        repack->newmailbox.i.record_size = 60;
        break;
    case 7:
        repack->newmailbox.i.start_offset = 76;
        repack->newmailbox.i.record_size = 72;
        break;
    case 8:
        repack->newmailbox.i.start_offset = 92;
        repack->newmailbox.i.record_size = 80;
        break;
    case 9:
        repack->newmailbox.i.start_offset = 96;
        repack->newmailbox.i.record_size = 80;
        break;
    case 10:
        repack->newmailbox.i.start_offset = 96;
        repack->newmailbox.i.record_size = 88;
        break;
    /* 11 was FastMail internal */
    case 12:
        repack->newmailbox.i.start_offset = 128;
        repack->newmailbox.i.record_size = 96;
        break;
    case 13:
        repack->newmailbox.i.start_offset = 128;
        repack->newmailbox.i.record_size = 104;
        break;
    case 14:
        /* version 15 just repurposed a field */
    case 15:
        repack->newmailbox.i.start_offset = 160;
        repack->newmailbox.i.record_size = 104;
        break;
    case 16:
    case 17:
        repack->newmailbox.i.start_offset = 160;
        repack->newmailbox.i.record_size = 112;
        break;
    default:
        fatal("index version not supported", EX_SOFTWARE);
    }

    /* upgrades or downgrades across version 12 boundary?  Sort out seen state */
    if (version >= 12 && mailbox->i.minor_version < 12) {
        /* we need to read the current seen state for the owner */
        struct seendata sd = SEENDATA_INITIALIZER;
        int r = IMAP_MAILBOX_NONEXISTENT;
        if (mailbox->i.options & OPT_IMAP_SHAREDSEEN)
            repack->userid = xstrdup("anyone");
        else
            repack->userid = mboxname_to_userid(mailbox_name(mailbox));

        if (repack->userid) {
            struct seen *seendb = NULL;
            r = seen_open(repack->userid, SEEN_SILENT, &seendb);
            if (!r) r = seen_read(seendb, mailbox_uniqueid(mailbox), &sd);
            seen_close(&seendb);
        }

        if (!r) {
            repack->newmailbox.i.recentuid = sd.lastuid;
            repack->newmailbox.i.recenttime = sd.lastchange;
            repack->seqset = seqset_parse(sd.seenuids, NULL, sd.lastuid);
            seen_freedata(&sd);
        }
    }
    else if (version < 12 && mailbox->i.minor_version >= 12) {
        if (mailbox->i.options & OPT_IMAP_SHAREDSEEN)
            repack->userid = xstrdup("anyone");
        else
            repack->userid = mboxname_to_userid(mailbox_name(mailbox));

        /* we need to create the seen state for the owner from the mailbox */
        if (repack->userid)
            repack->seqset = seqset_init(mailbox->i.last_uid, SEQ_MERGE);
    }

    /* we'll count the records as they get added */
    repack->newmailbox.i.num_records = 0;
    /* we're recreating caches, so there'll be nothing leaked */
    repack->newmailbox.i.leaked_cache_records = 0;

    /* prepare initial header buffer */
    mailbox_index_header_to_buf(&repack->newmailbox.i, buf);

    n = retry_write(repack->newmailbox.index_fd, buf, repack->newmailbox.i.start_offset);
    if (n == -1) goto fail;

    if (version != mailbox->i.minor_version) {
        /* we're might be reading this file as we go, so let's
         * start the job by mapping the new file into the newmailbox!
         */
        repack->newmailbox.index_size = 0;
        repack->newmailbox.index_base = NULL;
        repack->newmailbox.index_len = 0;
        int r = mailbox_refresh_index_map(&repack->newmailbox);
        if (r) goto fail;
    }

    *repackptr = repack;
    return 0;

 fail:
    mailbox_repack_abort(&repack);
    return IMAP_IOERROR;
}
static int mailbox_repack_setup(struct mailbox *mailbox, int version,
                                struct mailbox_repack **repackptr)
{
    struct mailbox_repack *repack = xzmalloc(sizeof(struct mailbox_repack));
    const char *fname;
    indexbuffer_t ibuf;
    unsigned char *buf = ibuf.buf;
    int n;

     
    if (version != mailbox->i.minor_version) {
         
        int r = mailbox_index_recalc(mailbox);
        if (r) goto fail;
    }

     
    repack->mailbox = mailbox;
    repack->crcs = mailbox->i.synccrcs;
    repack->newmailbox = *mailbox;  
    repack->newmailbox.index_fd = -1;

     
    fname = mailbox_meta_newfname(mailbox, META_INDEX);
    repack->newmailbox.index_fd = open(fname, O_RDWR|O_TRUNC|O_CREAT, 0666);
    if (repack->newmailbox.index_fd == -1) {
        xsyslog(LOG_ERR, "IOERROR: create failed",
                         "fname=<%s>",
                         fname);
        goto fail;
    }

     
    repack->newmailbox.i.generation_no++;

     
    repack->newmailbox.i.minor_version = version;
    switch (version) {
    case 6:
        repack->newmailbox.i.start_offset = 76;
        repack->newmailbox.i.record_size = 60;
        break;
    case 7:
        repack->newmailbox.i.start_offset = 76;
        repack->newmailbox.i.record_size = 72;
        break;
    case 8:
        repack->newmailbox.i.start_offset = 92;
        repack->newmailbox.i.record_size = 80;
        break;
    case 9:
        repack->newmailbox.i.start_offset = 96;
        repack->newmailbox.i.record_size = 80;
        break;
    case 10:
        repack->newmailbox.i.start_offset = 96;
        repack->newmailbox.i.record_size = 88;
        break;
     
    case 12:
        repack->newmailbox.i.start_offset = 128;
        repack->newmailbox.i.record_size = 96;
        break;
    case 13:
        repack->newmailbox.i.start_offset = 128;
        repack->newmailbox.i.record_size = 104;
        break;
    case 14:
         
    case 15:
        repack->newmailbox.i.start_offset = 160;
        repack->newmailbox.i.record_size = 104;
        break;
    case 16:
    case 17:
        repack->newmailbox.i.start_offset = 160;
        repack->newmailbox.i.record_size = 112;
        break;
    default:
        fatal("index version not supported", EX_SOFTWARE);
    }

     
    if (version >= 12 && mailbox->i.minor_version < 12) {
         
        struct seendata sd = SEENDATA_INITIALIZER;
        int r = IMAP_MAILBOX_NONEXISTENT;
        if (mailbox->i.options & OPT_IMAP_SHAREDSEEN)
            repack->userid = xstrdup("anyone");
        else
            repack->userid = mboxname_to_userid(mailbox_name(mailbox));

        if (repack->userid) {
            struct seen *seendb = NULL;
            r = seen_open(repack->userid, SEEN_SILENT, &seendb);
            if (!r) r = seen_read(seendb, mailbox_uniqueid(mailbox), &sd);
            seen_close(&seendb);
        }

        if (!r) {
            repack->newmailbox.i.recentuid = sd.lastuid;
            repack->newmailbox.i.recenttime = sd.lastchange;
            repack->seqset = seqset_parse(sd.seenuids, NULL, sd.lastuid);
            seen_freedata(&sd);
        }
    }
    else if (version < 12 && mailbox->i.minor_version >= 12) {
        if (mailbox->i.options & OPT_IMAP_SHAREDSEEN)
            repack->userid = xstrdup("anyone");
        else
            repack->userid = mboxname_to_userid(mailbox_name(mailbox));

         
        if (repack->userid)
            repack->seqset = seqset_init(mailbox->i.last_uid, SEQ_MERGE);
    }

     
    repack->newmailbox.i.num_records = 0;
     
    repack->newmailbox.i.leaked_cache_records = 0;

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

    n = retry_write(repack->newmailbox.index_fd, buf, repack->newmailbox.i.start_offset);
    if (n == -1) goto fail;

    if (version != mailbox->i.minor_version) {
         
        repack->newmailbox.index_size = 0;
        repack->newmailbox.index_base = NULL;
        repack->newmailbox.index_len = 0;
        int r = mailbox_refresh_index_map(&repack->newmailbox);
        if (r) goto fail;
    }

    *repackptr = repack;
    return 0;

 fail:
    mailbox_repack_abort(&repack);
    return IMAP_IOERROR;
}