/* create a downgraded index file in cyrus.index. We don't copy back * expunged messages, sorry */ static int dump_index(struct mailbox *mailbox, int oldversion, struct seqset *expunged_seq, int first, int sync, struct protstream *pin, struct protstream *pout) { char oldname[MAX_MAILBOX_PATH]; const char *fname; int oldindex_fd = -1; indexbuffer_t headerbuf; indexbuffer_t recordbuf; char *hbuf = (char *)headerbuf.buf; char *rbuf = (char *)recordbuf.buf; int header_size; int record_size; int n, r; struct index_record record; unsigned recno; if (oldversion == 6) { header_size = 76; record_size = 60; } else if (oldversion == 7) { header_size = 76; record_size = 72; } else if (oldversion == 8) { header_size = 92; record_size = 80; } else if (oldversion == 9) { header_size = 96; record_size = 80; } else if (oldversion == 10) { header_size = 96; record_size = 88; } else { return IMAP_MAILBOX_BADFORMAT; } fname = mailbox_meta_fname(mailbox, META_INDEX); snprintf(oldname, MAX_MAILBOX_PATH, "%s.OLD", fname); oldindex_fd = open(oldname, O_RDWR|O_TRUNC|O_CREAT, 0666); if (oldindex_fd == -1) goto fail; downgrade_header(&mailbox->i, hbuf, oldversion, header_size, record_size); /* Write header - everything we'll say */ n = retry_write(oldindex_fd, hbuf, header_size); if (n == -1) goto fail; for (recno = 1; recno <= mailbox->i.num_records; recno++) { if (mailbox_read_index_record(mailbox, recno, &record)) goto fail; /* we don't care about unlinked records at all */ if (record.system_flags & FLAG_UNLINKED) continue; /* we have to make sure expunged records don't get the * file copied, or a reconstruct could bring them back * to life! It we're not creating an expunged file... */ if (record.system_flags & FLAG_EXPUNGED) { if (oldversion < 9) seqset_add(expunged_seq, record.uid, 1); continue; } /* not making sure exists matches, we do trust a bit */ downgrade_record(&record, rbuf, oldversion); n = retry_write(oldindex_fd, rbuf, record_size); if (n == -1) goto fail; } close(oldindex_fd); r = dump_file(first, sync, pin, pout, oldname, "cyrus.index"); unlink(oldname); if (r) return r; /* create cyrus.expunge */ if (oldversion > 8 && mailbox->i.num_records > mailbox->i.exists) { int nexpunge = mailbox->i.num_records - mailbox->i.exists; fname = mailbox_meta_fname(mailbox, META_EXPUNGE); snprintf(oldname, MAX_MAILBOX_PATH, "%s.OLD", fname); oldindex_fd = open(oldname, O_RDWR|O_TRUNC|O_CREAT, 0666); if (oldindex_fd == -1) goto fail; *((bit32 *)(hbuf+OFFSET_NUM_RECORDS)) = htonl(nexpunge); /* Write header - everything we'll say */ n = retry_write(oldindex_fd, hbuf, header_size); if (n == -1) goto fail; for (recno = 1; recno <= mailbox->i.num_records; recno++) { if (mailbox_read_index_record(mailbox, recno, &record)) goto fail; /* ignore non-expunged records */ if (!(record.system_flags & FLAG_EXPUNGED)) continue; downgrade_record(&record, rbuf, oldversion); n = retry_write(oldindex_fd, rbuf, record_size); if (n == -1) goto fail; } close(oldindex_fd); r = dump_file(first, sync, pin, pout, oldname, "cyrus.expunge"); unlink(oldname); if (r) return r; } return 0; fail: if (oldindex_fd != -1) close(oldindex_fd); unlink(oldname); return IMAP_IOERROR; }
/* create a downgraded index file in cyrus.index. We don't copy back * expunged messages, sorry */ static int dump_index(struct mailbox *mailbox, int oldversion, struct seqset *expunged_seq, int first, int sync, struct protstream *pin, struct protstream *pout) { char oldname[MAX_MAILBOX_PATH]; const char *fname; int oldindex_fd = -1; indexbuffer_t headerbuf; indexbuffer_t recordbuf; char *hbuf = (char *)headerbuf.buf; char *rbuf = (char *)recordbuf.buf; int header_size; int record_size; int n, r; if (oldversion == 6) { header_size = 76; record_size = 60; } else if (oldversion == 7) { header_size = 76; record_size = 72; } else if (oldversion == 8) { header_size = 92; record_size = 80; } else if (oldversion == 9) { header_size = 96; record_size = 80; } else if (oldversion == 10) { header_size = 96; record_size = 88; } else { return IMAP_MAILBOX_BADFORMAT; } fname = mailbox_meta_fname(mailbox, META_INDEX); snprintf(oldname, MAX_MAILBOX_PATH, "%s.OLD", fname); oldindex_fd = open(oldname, O_RDWR|O_TRUNC|O_CREAT, 0666); if (oldindex_fd == -1) goto fail; downgrade_header(&mailbox->i, hbuf, oldversion, header_size, record_size); /* Write header - everything we'll say */ n = retry_write(oldindex_fd, hbuf, header_size); if (n == -1) goto fail; struct mailbox_iter *iter = mailbox_iter_init(mailbox, 0, ITER_SKIP_UNLINKED); const message_t *msg; while ((msg = mailbox_iter_step(iter))) { const struct index_record *record = msg_record(msg); /* we have to make sure expunged records don't get the * file copied, or a reconstruct could bring them back * to life! It we're not creating an expunged file... */ if (record->system_flags & FLAG_EXPUNGED) { if (oldversion < 9) seqset_add(expunged_seq, record->uid, 1); continue; } /* not making sure exists matches, we do trust a bit */ downgrade_record(record, rbuf, oldversion); n = retry_write(oldindex_fd, rbuf, record_size); if (n == -1) goto fail; } mailbox_iter_done(&iter); close(oldindex_fd); r = dump_file(first, sync, pin, pout, oldname, "cyrus.index", NULL, 0); unlink(oldname); if (r) return r; /* create cyrus.expunge */ if (oldversion > 8 && mailbox->i.num_records > mailbox->i.exists) { int nexpunge = mailbox->i.num_records - mailbox->i.exists; fname = mailbox_meta_fname(mailbox, META_EXPUNGE); snprintf(oldname, MAX_MAILBOX_PATH, "%s.OLD", fname); oldindex_fd = open(oldname, O_RDWR|O_TRUNC|O_CREAT, 0666); if (oldindex_fd == -1) goto fail; header_set_num_records(hbuf, nexpunge); /* Write header - everything we'll say */ n = retry_write(oldindex_fd, hbuf, header_size); if (n == -1) goto fail; iter = mailbox_iter_init(mailbox, 0, ITER_SKIP_UNLINKED); while ((msg = mailbox_iter_step(iter))) { const struct index_record *record = msg_record(msg); /* ignore non-expunged records */ if (!(record->system_flags & FLAG_EXPUNGED)) continue; downgrade_record(record, rbuf, oldversion); n = retry_write(oldindex_fd, rbuf, record_size); if (n == -1) goto fail; } close(oldindex_fd); r = dump_file(first, sync, pin, pout, oldname, "cyrus.expunge", NULL, 0); unlink(oldname); if (r) return r; } return 0; fail: if (oldindex_fd != -1) close(oldindex_fd); unlink(oldname); return IMAP_IOERROR; }
int main(int argc, char *argv[]) { const char *alt_config = NULL; unsigned maxval = 0; int flags = SEQ_MERGE; struct seqset *seq = NULL; int opt; unsigned num; char *res; const char *origlist = NULL; while ((opt = getopt(argc, argv, "C:m:o:s")) != EOF) { switch (opt) { case 'C': /* alt config file */ alt_config = optarg; break; case 'm': /* maxval */ parseuint32(optarg, NULL, &maxval); break; case 'o': origlist = optarg; break; case 's': flags = SEQ_SPARSE; } } if ((argc - optind) < 1) usage(argv[0]); cyrus_init(alt_config, "cyr_sequence", 0); /* special case */ if (!strcmp(argv[optind], "create")) { int i; seq = seqset_init(maxval, flags); for (i = optind + 1; i < argc; i++) { char *ptr = argv[i]; int isadd = 1; if (*ptr == '~') { isadd = 0; ptr++; } if (parseuint32(ptr, NULL, &num)) printf("%s NAN\n", argv[i]); else seqset_add(seq, num, isadd); } if (origlist) { unsigned oldmax = seq_lastnum(origlist, NULL); if (oldmax > maxval) { struct seqset *origseq = seqset_parse(origlist, NULL, oldmax); unsigned val; for (val = maxval + 1; val <= oldmax; val++) seqset_add(seq, val, seqset_ismember(origseq, val)); seqset_free(origseq); } } res = seqset_cstring(seq); printf("%s\n", res); free(res); } else if (!strcmp(argv[optind], "parsed")) { unsigned i; seq = seqset_parse(argv[optind+1], NULL, maxval); printf("Sections: " SIZE_T_FMT "\n", seq->len); for (i = 0; i < seq->len; i++) { if (seq->set[i].high == UINT_MAX) printf(" [%u, *]\n", seq->set[i].low); else printf(" [%u, %u]\n", seq->set[i].low, seq->set[i].high); } } else if (!strcmp(argv[optind], "compress")) { seq = seqset_parse(argv[optind+1], NULL, maxval); res = seqset_cstring(seq); printf("%s\n", res); free(res); } else if (!strcmp(argv[optind], "members")) { seq = seqset_parse(argv[optind+1], NULL, maxval); while ((num = seqset_getnext(seq))) { printf("%u\n", num); } } else if (!strcmp(argv[optind], "join")) { struct seqset *seq2; seq = seqset_parse(argv[optind+1], NULL, maxval); seq2 = seqset_parse(argv[optind+2], NULL, maxval); seqset_join(seq, seq2); res = seqset_cstring(seq); printf("%s\n", res); free(res); } else if (!strcmp(argv[optind], "ismember")) { int i; seq = seqset_parse(argv[optind+1], NULL, maxval); for (i = optind + 2; i < argc; i++) { if (parseuint32(argv[i], NULL, &num)) printf("%s NAN\n", argv[i]); else printf("%d %s\n", num, seqset_ismember(seq, num) ? "Yes" : "No"); } } else { printf("Unknown command %s", argv[optind]); } seqset_free(seq); cyrus_done(); return 0; }