mbox-sync-rewrite.c revision dc8552739fa29f011ab71ec383ec6d580a5a9661
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. Whiteint mbox_move(struct mbox_sync_context *sync_ctx,
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White output = o_stream_create_file(sync_ctx->fd, default_pool, 4096, FALSE);
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White input = i_stream_create_limit(default_pool, sync_ctx->file_input,
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White else if (ret >= 0) {
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White mail_storage_set_critical(sync_ctx->ibox->box.storage,
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White "mbox_move(%"PRIuUOFF_T", %"PRIuUOFF_T", %"PRIuUOFF_T
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White ") moved only %"PRIuUOFF_T" bytes in mbox file %s",
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White dest, source, size, (uoff_t)ret, sync_ctx->ibox->path);
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White } else if (ret < 0) {
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White "o_stream_send_istream()");
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. Whitestatic int mbox_fill_space(struct mbox_sync_context *sync_ctx,
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White mbox_set_syscall_error(sync_ctx->ibox, "pwrite_full()");
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White if (pwrite_full(sync_ctx->fd, space, size, offset) < 0) {
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White mbox_set_syscall_error(sync_ctx->ibox, "pwrite_full()");
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. Whitembox_sync_headers_add_space(struct mbox_sync_mail_context *ctx, size_t size)
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White const unsigned char *data;
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White start_pos = ctx->hdr_pos[MBOX_HDR_X_IMAPBASE];
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White /* Append at the end of X-Keywords header,
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White or X-UID if it doesn't exist */
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White start_pos = ctx->hdr_pos[MBOX_HDR_X_KEYWORDS] != (size_t)-1 ?
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White for (pos = start_pos; pos < data_size; pos++) {
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White /* possibly continues in next line */
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White if (pos+1 == data_size || !IS_LWSP(data[pos+1]))
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White /* pos points to end of header now, and start_pos to beginning
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White of whitespace. */
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White p = buffer_get_space_unsafe(ctx->header, pos, size);
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White ctx->mail.offset = ctx->hdr_offset + start_pos;
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. Whitestatic void mbox_sync_header_remove_space(struct mbox_sync_mail_context *ctx,
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White const unsigned char *data;
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White /* find the end of the LWSP */
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White for (pos = last_line_pos = start_pos; pos < data_size; pos++) {
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White /* possibly continues in next line */
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White if (pos+1 == data_size || !IS_LWSP(data[pos+1])) {
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White /* and remove what we can */
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White /* remove it all */
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White mbox_sync_move_buffer(ctx, start_pos, 0, data_size - start_pos);
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White /* we have more space than needed. since we're removing from
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White the beginning of header instead of end, we don't have to
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White worry about multiline-headers. */
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White mbox_sync_move_buffer(ctx, start_pos, 0, *size);
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White if (ctx->mail.space < (off_t)(data_size - last_line_pos)) {
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White ctx->mail.offset = ctx->hdr_offset + last_line_pos;
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. Whitestatic void mbox_sync_headers_remove_space(struct mbox_sync_mail_context *ctx,
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White static enum header_position space_positions[] = {
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White mbox_sync_header_remove_space(ctx, ctx->hdr_pos[pos],
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White /* FIXME: see if we could remove X-Keywords header completely */
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. Whiteint mbox_sync_try_rewrite(struct mbox_sync_mail_context *ctx, off_t move_diff)
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White i_assert(ctx->sync_ctx->ibox->mbox_lock_type == F_WRLCK);
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White old_hdr_size = ctx->body_offset - ctx->hdr_offset;
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White /* add space. note that we must call add_space() even if we're
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White not adding anything so mail.offset gets fixed. */
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White mbox_sync_headers_add_space(ctx, old_hdr_size - new_hdr_size);
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White /* try removing the space where we can */
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White /* good, we removed enough. */
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White } else if (move_diff < 0 &&
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White new_hdr_size - old_hdr_size <= (uoff_t)-move_diff) {
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White /* moving backwards - we can use the extra space from
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White it, just update expunged_space accordingly */
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White /* couldn't get enough space */
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White if (ctx->header_first_change == (size_t)-1 && move_diff == 0) {
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White /* no changes actually. we get here if index sync record told
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White us to do something that was already there */
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White /* forget about partial write optimizations */
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White str_truncate(ctx->header, ctx->header_last_change);
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White str_data(ctx->header) + ctx->header_first_change,
2b9e5de12a7774e2f1db30c1c7a762eb76093e1aLiam P. White str_len(ctx->header) - ctx->header_first_change,
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White mbox_set_syscall_error(ctx->sync_ctx->ibox, "pwrite_full()");
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. Whitestatic int mbox_sync_read_and_move(struct mbox_sync_context *sync_ctx,
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White if (mbox_sync_seek(sync_ctx, mails[idx].from_offset) < 0)
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White hdr_offset = istream_raw_mbox_get_header_offset(sync_ctx->input);
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White mail_ctx.mail.body_size = mails[idx].body_size;
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White /* mbox_sync_parse_next_mail() checks that UIDs are growing,
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White so we have to fool it. */
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White sync_ctx->prev_msg_uid = mails[idx].uid == 0 ? 0 : mails[idx].uid-1;
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White if (mails[idx].from_offset+1 - expunged_space != 0) {
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White sync_ctx->dest_first_mail = mails[idx].from_offset == 0;
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White /* we need to skip over the initial \n (it's already counted in
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White expunged_space) */
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White mbox_sync_parse_next_mail(sync_ctx->input, &mail_ctx);
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White mbox_sync_update_header_from(&mail_ctx, &mails[idx]);
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White /* updating might just try to add headers and mess up our
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White calculations completely. so only add the EOH here. */
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White if (first_nonexpunged && expunged_space > 0) {
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White /* move From-line (after parsing headers so we don't
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White overwrite them) */
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White if (mbox_move(sync_ctx, mails[idx].from_offset - expunged_space,
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White mails[idx].offset - mails[idx].from_offset) < 0)
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White need_space = str_len(mail_ctx.header) - mail_ctx.mail.space -
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White i_assert(need_space == (uoff_t)-mails[idx].space);
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White /* don't touch spacing */
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White } else if (padding < (uoff_t)mail_ctx.mail.space) {
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White mbox_sync_headers_remove_space(&mail_ctx, mail_ctx.mail.space -
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White mbox_sync_headers_add_space(&mail_ctx, padding -
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White /* move the body of this message and headers of next message forward,
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White then write the headers */
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White if (mbox_move(sync_ctx, dest_offset, offset, end_offset - offset) < 0)
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White /* the header may actually be moved backwards if there was expunged
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White space which we wanted to remove */
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White i_assert(dest_offset >= str_len(mail_ctx.header));
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White if (pwrite_full(sync_ctx->fd, str_data(mail_ctx.header),
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White mbox_set_syscall_error(sync_ctx->ibox, "pwrite_full()");
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. Whiteint mbox_sync_rewrite(struct mbox_sync_context *sync_ctx,
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White uoff_t end_offset, off_t move_diff, uoff_t extra_space,
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White uoff_t offset, dest_offset, next_end_offset, next_move_diff;
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White uint32_t idx, first_nonexpunged_idx, padding_per_mail;
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White i_assert(sync_ctx->ibox->mbox_lock_type == F_WRLCK);
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White mails = buffer_get_modifyable_data(sync_ctx->mails, &size);
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White i_assert(size / sizeof(*mails) == last_seq - first_seq + 1);
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White /* if there's expunges in mails[], we would get more correct balancing
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White by counting only them here. however, that might make us overwrite
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White data which hasn't yet been copied backwards. to avoid too much
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White complexity, we just leave all the rest of the extra space to first
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White /* after expunge the next mail must have been missing space, or we
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White would have moved it backwards already */
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White for (first_nonexpunged_idx = 0;; first_nonexpunged_idx++) {
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White if ((mails[first_nonexpunged_idx].flags & MBOX_EXPUNGED) == 0)
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White expunged_space += mails[first_nonexpunged_idx].space;
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White i_assert(mails[first_nonexpunged_idx].space < 0);
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White /* start moving backwards. */
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White /* give the rest of the extra space to first mail.
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White we might also have to move the mail backwards to
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White fill the expunged space */
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White padding_per_mail = move_diff + expunged_space +
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White /* give space to this mail. end_offset is left to
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White contain this message's From-line (ie. below we
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White move only headers + body). */
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White int first_nonexpunged = idx == first_nonexpunged_idx;
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White move_diff -= next_move_diff + mails[idx].space;
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White /* this mail provides more space. just move it forward
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White from the extra space offset and set end_offset to
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White point to beginning of extra space. that way the
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White header will be moved along with previous mail's
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White if this is expunged mail, we're moving following
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White mail's From-line and maybe headers. */
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White offset = mails[idx].offset + mails[idx].space;
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White if ((mails[idx].flags & MBOX_EXPUNGED) == 0) {
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White i_assert(move_diff >= 0 || idx == first_nonexpunged_idx);
a73b1f7fc9a9ba7e0d68f33292a885da6c2981d0Liam P. White i_assert(mails[idx].from_offset == start_offset);