Mercurial > dovecot > core-2.2
view src/lib-storage/index/dbox-multi/mdbox-file-purge.c @ 10635:c60910419861 HEAD
mdbox: Purging now also moves mails to alt storage (if it's used).
mdbox_altmove setting specifies how old files should be moved.
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Tue, 02 Feb 2010 23:49:32 +0200 |
parents | 615eef3139c2 |
children | 912ef25c2dcf |
line wrap: on
line source
/* Copyright (c) 2007-2010 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "istream.h" #include "ostream.h" #include "str.h" #include "hex-binary.h" #include "mdbox-storage.h" #include "mdbox-file.h" #include "mdbox-map.h" #include "mdbox-sync.h" #include <stdlib.h> struct dbox_mail_move { struct dbox_file *file; uint32_t offset; }; ARRAY_DEFINE_TYPE(dbox_mail_move, struct dbox_mail_move); static int mdbox_map_file_msg_offset_cmp(const void *p1, const void *p2) { const struct dbox_map_file_msg *m1 = p1, *m2 = p2; if (m1->offset < m2->offset) return -1; else if (m1->offset > m2->offset) return 1; else return 0; } static int mdbox_file_copy_metadata(struct dbox_file *file, struct ostream *output) { struct dbox_metadata_header meta_hdr; const char *line; const unsigned char *data; size_t size; int ret; ret = i_stream_read_data(file->input, &data, &size, sizeof(meta_hdr)); if (ret <= 0) { i_assert(ret == -1); if (file->input->stream_errno == 0) { dbox_file_set_corrupted(file, "missing metadata"); return 0; } mail_storage_set_critical(&file->storage->storage, "read(%s) failed: %m", file->cur_path); return -1; } memcpy(&meta_hdr, data, sizeof(meta_hdr)); if (memcmp(meta_hdr.magic_post, DBOX_MAGIC_POST, sizeof(meta_hdr.magic_post)) != 0) { dbox_file_set_corrupted(file, "invalid metadata magic"); return 0; } i_stream_skip(file->input, sizeof(meta_hdr)); if (output != NULL) o_stream_send(output, &meta_hdr, sizeof(meta_hdr)); while ((line = i_stream_read_next_line(file->input)) != NULL) { if (*line == DBOX_METADATA_OLDV1_SPACE || *line == '\0') { /* end of metadata */ break; } if (output != NULL) { o_stream_send_str(output, line); o_stream_send(output, "\n", 1); } } if (line == NULL) { dbox_file_set_corrupted(file, "missing end-of-metadata line"); return 0; } if (output != NULL) o_stream_send(output, "\n", 1); return 1; } int mdbox_file_purge(struct dbox_file *file) { struct mdbox_storage *dstorage = (struct mdbox_storage *)file->storage; struct dbox_file_append_context *out_file_append; struct stat st; struct istream *input; struct ostream *output = NULL; struct dbox_map_append_context *append_ctx; ARRAY_TYPE(dbox_map_file_msg) msgs_arr; const struct dbox_map_file_msg *msgs; ARRAY_TYPE(seq_range) expunged_map_uids; ARRAY_TYPE(uint32_t) copied_map_uids; unsigned int i, count; uoff_t offset, physical_size, msg_size; enum dbox_map_append_flags append_flags = 0; int ret; if ((ret = dbox_file_try_lock(file)) <= 0) return ret; /* make sure the file still exists. another process may have already deleted it. */ if (stat(file->cur_path, &st) < 0) { dbox_file_unlock(file); if (errno == ENOENT) return 0; mail_storage_set_critical(&file->storage->storage, "stat(%s) failed: %m", file->cur_path); return -1; } if (dstorage->set->mdbox_altmove > 0 && st.st_mtime + dstorage->set->mdbox_altmove < ioloop_time && dstorage->alt_storage_dir != NULL) append_flags |= DBOX_MAP_APPEND_FLAG_ALT; i_array_init(&msgs_arr, 128); if (dbox_map_get_file_msgs(dstorage->map, ((struct mdbox_file *)file)->file_id, &msgs_arr) < 0) { array_free(&msgs_arr); dbox_file_unlock(file); return -1; } /* sort messages by their offset */ array_sort(&msgs_arr, mdbox_map_file_msg_offset_cmp); msgs = array_get(&msgs_arr, &count); append_ctx = dbox_map_append_begin(dstorage->map, append_flags); i_array_init(&copied_map_uids, I_MIN(count, 1)); i_array_init(&expunged_map_uids, I_MIN(count, 1)); offset = file->file_header_size; for (i = 0; i < count; i++) { if ((ret = dbox_file_get_mail_stream(file, offset, &physical_size, NULL)) <= 0) break; msg_size = file->msg_header_size + physical_size; if (msgs[i].offset != offset) { /* map doesn't match file's actual contents */ dbox_file_set_corrupted(file, "purging found mismatched offsets " "(%"PRIuUOFF_T" vs %u, %u/%u)", offset, msgs[i].offset, i, count); ret = 0; break; } if (msgs[i].refcount == 0) { seq_range_array_add(&expunged_map_uids, 0, msgs[i].map_uid); output = NULL; } else { /* non-expunged message. write it to output file. */ if (dbox_map_append_next(append_ctx, physical_size, &out_file_append, &output) < 0) { ret = -1; break; } i_assert(file != out_file_append->file); i_stream_seek(file->input, offset); input = i_stream_create_limit(file->input, msg_size); ret = o_stream_send_istream(output, input); if (input->stream_errno != 0) { errno = input->stream_errno; mail_storage_set_critical(&file->storage->storage, "read(%s) failed: %m", file->cur_path); i_stream_unref(&input); break; } i_stream_unref(&input); if (output->stream_errno != 0) { errno = output->stream_errno; mail_storage_set_critical(&file->storage->storage, "write(%s) failed: %m", out_file_append->file->cur_path); break; } i_assert(ret == (off_t)msg_size); } /* copy/skip metadata */ i_stream_seek(file->input, offset + msg_size); if ((ret = mdbox_file_copy_metadata(file, output)) <= 0) break; if (output != NULL) { dbox_map_append_finish(append_ctx); array_append(&copied_map_uids, &msgs[i].map_uid, 1); } offset = file->input->v_offset; } if (offset != (uoff_t)st.st_size && ret > 0) { /* file has more messages than what map tells us */ dbox_file_set_corrupted(file, "more messages available than in map " "(%"PRIuUOFF_T" < %"PRIuUOFF_T")", offset, st.st_size); ret = 0; } array_free(&msgs_arr); msgs = NULL; if (ret <= 0) { dbox_map_append_free(&append_ctx); dbox_file_unlock(file); ret = -1; } else if (array_count(&copied_map_uids) == 0) { /* everything expunged in this file, unlink it */ ret = dbox_file_unlink(file); dbox_map_append_free(&append_ctx); } else { /* assign new file_id + offset to moved messages */ if (dbox_map_append_move(append_ctx, &copied_map_uids, &expunged_map_uids) < 0 || dbox_map_append_commit(append_ctx) < 0) { dbox_file_unlock(file); ret = -1; } else { ret = 1; (void)dbox_file_unlink(file); } dbox_map_append_free(&append_ctx); } array_free(&copied_map_uids); array_free(&expunged_map_uids); return ret; }