Mercurial > dovecot > original-hg > dovecot-1.2
view src/lib-index/mail-index-data.c @ 103:08186ac2784e HEAD
Fixed some index rebuilding behaviour.
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Sat, 31 Aug 2002 19:02:07 +0300 |
parents | 37a388fe4dab |
children | a6d7ed739926 |
line wrap: on
line source
/* Copyright (C) 2002 Timo Sirainen */ #include "lib.h" #include "mmap-util.h" #include "write-full.h" #include "mail-index.h" #include "mail-index-data.h" #include "mail-index-util.h" #include <stdio.h> #include <fcntl.h> #define DATA_FILE_POSITION(data, rec) \ ((uoff_t) ((char *) (rec) - (char *) ((data)->mmap_base))) /* Never compress the file if it's smaller than this (50kB) */ #define COMPRESS_MIN_SIZE (1024*50) /* Compress the file when deleted space reaches 20% of total size */ #define COMPRESS_PERCENTAGE 20 struct _MailIndexData { MailIndex *index; int fd; char *filepath; void *mmap_base; size_t mmap_length; unsigned int dirty_mmap:1; }; static int mmap_update(MailIndexData *data, uoff_t pos, unsigned int size) { if (!data->dirty_mmap || (size != 0 && pos <= data->mmap_length && pos+size <= data->mmap_length)) return TRUE; if (data->mmap_base != NULL) (void)munmap(data->mmap_base, data->mmap_length); data->mmap_base = mmap_rw_file(data->fd, &data->mmap_length); if (data->mmap_base == MAP_FAILED) { data->mmap_base = NULL; index_set_error(data->index, "index data: mmap() failed with " "file %s: %m", data->filepath); return FALSE; } else if (data->mmap_length < sizeof(MailIndexDataHeader)) { INDEX_MARK_CORRUPTED(data->index); index_set_error(data->index, "index data: truncated data " "file %s", data->filepath); return FALSE; } else { data->dirty_mmap = FALSE; return TRUE; } } int mail_index_data_open(MailIndex *index) { MailIndexData *data; MailIndexDataHeader *hdr; const char *path; int fd; path = t_strconcat(index->filepath, DATA_FILE_PREFIX, NULL); fd = open(path, O_RDWR); if (fd == -1) { if (errno == ENOENT) { /* doesn't exist, rebuild the index */ INDEX_MARK_CORRUPTED(index); } index_set_error(index, "Can't open index data %s: %m", path); return FALSE; } data = i_new(MailIndexData, 1); data->index = index; data->fd = fd; data->filepath = i_strdup(path); data->dirty_mmap = TRUE; index->data = data; if (!mmap_update(data, 0, sizeof(MailIndexDataHeader))) { mail_index_data_free(data); return FALSE; } /* verify that this really is the data file for wanted index */ hdr = data->mmap_base; if (hdr->indexid != index->indexid) { INDEX_MARK_CORRUPTED(index); index_set_error(index, "IndexID mismatch for data file %s", path); mail_index_data_free(data); return FALSE; } return TRUE; } static const char *init_data_file(MailIndex *index, int fd, const char *temppath) { MailIndexDataHeader hdr; const char *realpath; /* write header */ memset(&hdr, 0, sizeof(hdr)); hdr.indexid = index->indexid; if (write_full(fd, &hdr, sizeof(hdr)) < 0) { index_set_error(index, "Error writing to temp index data " "%s: %m", temppath); return NULL; } /* move temp file into .data file, deleting old one if it already exists */ realpath = t_strconcat(index->filepath, DATA_FILE_PREFIX, NULL); if (rename(temppath, realpath) == -1) { index_set_error(index, "rename(%s, %s) failed: %m", temppath, realpath); (void)unlink(temppath); return NULL; } return realpath; } int mail_index_data_create(MailIndex *index) { MailIndexData *data; const char *temppath, *realpath; int fd; fd = mail_index_create_temp_file(index, &temppath); if (fd == -1) return FALSE; realpath = init_data_file(index, fd, temppath); if (realpath == NULL) { (void)close(fd); (void)unlink(temppath); return FALSE; } data = i_new(MailIndexData, 1); data->index = index; data->fd = fd; data->filepath = i_strdup(realpath); data->dirty_mmap = TRUE; index->data = data; return TRUE; } void mail_index_data_free(MailIndexData *data) { data->index->data = NULL; if (data->mmap_base != NULL) { (void)munmap(data->mmap_base, data->mmap_length); data->mmap_base = NULL; } (void)close(data->fd); i_free(data->filepath); i_free(data); } int mail_index_data_reset(MailIndexData *data) { MailIndexDataHeader hdr; if (ftruncate(data->fd, sizeof(MailIndexDataHeader)) == -1) { index_set_error(data->index, "ftruncate() failed for data file " "%s: %m", data->filepath); return FALSE; } memset(&hdr, 0, sizeof(hdr)); hdr.indexid = data->index->indexid; hdr.deleted_space = 0; if (lseek(data->fd, 0, SEEK_SET) == -1) { index_set_error(data->index, "lseek() failed for data file " "%s: %m", data->filepath); return FALSE; } if (write_full(data->fd, &hdr, sizeof(hdr)) < 0) { index_set_error(data->index, "write() failed for data file " "%s: %m", data->filepath); return FALSE; } return TRUE; } void mail_index_data_new_data_notify(MailIndexData *data) { data->dirty_mmap = TRUE; } uoff_t mail_index_data_append(MailIndexData *data, const void *buffer, size_t size) { off_t pos; i_assert((size & (MEM_ALIGN_SIZE-1)) == 0); pos = lseek(data->fd, 0, SEEK_END); if (pos == -1) { index_set_error(data->index, "lseek() failed with file %s: %m", data->filepath); return 0; } if (pos < (int)sizeof(MailIndexDataHeader)) { index_set_error(data->index, "Header missing from data file %s", data->filepath); return 0; } if (write_full(data->fd, buffer, size) < 0) { index_set_error(data->index, "Error appending to file %s: %m", data->filepath); return 0; } mail_index_data_new_data_notify(data); return (uoff_t)pos; } int mail_index_data_add_deleted_space(MailIndexData *data, unsigned int data_size) { MailIndexDataHeader *hdr; uoff_t max_del_space; i_assert(data->index->lock_type == MAIL_LOCK_EXCLUSIVE); /* make sure the whole file is mmaped */ if (!mmap_update(data, 0, 0)) return FALSE; hdr = data->mmap_base; hdr->deleted_space += data_size; /* see if we've reached the max. deleted space in file */ if (data->mmap_length >= COMPRESS_MIN_SIZE) { max_del_space = data->mmap_length / 100 * COMPRESS_PERCENTAGE; if (hdr->deleted_space >= max_del_space) data->index->set_flags |= MAIL_INDEX_FLAG_COMPRESS_DATA; } return TRUE; } int mail_index_data_sync_file(MailIndexData *data) { if (data->mmap_base != NULL) { if (msync(data->mmap_base, data->mmap_length, MS_SYNC) == -1) { index_set_error(data->index, "msync() failed for " "%s: %m", data->filepath); return FALSE; } } if (fsync(data->fd) == -1) { index_set_error(data->index, "fsync() failed for %s: %m", data->filepath); return FALSE; } return TRUE; } MailIndexDataRecord * mail_index_data_lookup(MailIndexData *data, MailIndexRecord *index_rec, MailField field) { MailIndexDataRecord *rec; uoff_t pos, max_pos; if (index_rec->data_position == 0) { /* data not yet written to record */ index_reset_error(data->index); return NULL; } if (!mmap_update(data, index_rec->data_position, index_rec->data_size)) return NULL; if (index_rec->data_position > data->mmap_length || (data->mmap_length - index_rec->data_position < index_rec->data_size)) { INDEX_MARK_CORRUPTED(data->index); index_set_error(data->index, "Error in data file %s: " "Given data size larger than file size " "(%lu + %u > %lu)", data->filepath, (unsigned long) index_rec->data_position, index_rec->data_size, (unsigned long) data->mmap_length); return NULL; } pos = index_rec->data_position; max_pos = pos + index_rec->data_size; do { rec = (MailIndexDataRecord *) ((char *) data->mmap_base + pos); /* pos + DATA_RECORD_SIZE() may actually overflow, but it points to beginning of file then. Don't bother checking this as it won't crash and is quite likely noticed later. */ if (pos + sizeof(MailIndexDataRecord) > max_pos || pos + DATA_RECORD_SIZE(rec) > max_pos) { INDEX_MARK_CORRUPTED(data->index); index_set_error(data->index, "Error in data file %s: " "Field size points outside file " "(%lu / %lu)", data->filepath, (unsigned long) pos, (unsigned long) max_pos); break; } if (rec->field == field) { /* match */ return rec; } else if (rec->field < field) { /* jump to next record */ pos += DATA_RECORD_SIZE(rec); } else { /* the fields are sorted by field type, so it's not possible the wanted field could come after this. */ break; } } while (pos < max_pos); return NULL; } MailIndexDataRecord * mail_index_data_next(MailIndexData *data, MailIndexRecord *index_rec, MailIndexDataRecord *rec) { uoff_t pos, end_pos, max_pos; if (rec == NULL) return NULL; /* get position to next record */ pos = DATA_FILE_POSITION(data, rec) + DATA_RECORD_SIZE(rec); max_pos = index_rec->data_position + index_rec->data_size; /* make sure it's within range */ if (pos >= max_pos) return NULL; rec = (MailIndexDataRecord *) ((char *) data->mmap_base + pos); end_pos = pos + DATA_RECORD_SIZE(rec); if (end_pos < pos || end_pos > max_pos) { INDEX_MARK_CORRUPTED(data->index); index_set_error(data->index, "Error in data file %s: " "Field size points outside file " "(%lu + %u > %lu)", data->filepath, (unsigned long) pos, rec->full_field_size, (unsigned long) max_pos); return NULL; } return rec; } int mail_index_data_record_verify(MailIndexData *data, MailIndexDataRecord *rec) { int i; if (rec->full_field_size > INT_MAX) { INDEX_MARK_CORRUPTED(data->index); index_set_error(data->index, "Error in data file %s: " "full_field_size > INT_MAX", data->filepath); return FALSE; } /* make sure the data actually contains \0 */ for (i = (int)rec->full_field_size-1; i >= 0; i--) { if (rec->data[i] == '\0') { /* yes, everything ok */ return TRUE; } } INDEX_MARK_CORRUPTED(data->index); index_set_error(data->index, "Error in data file %s: " "Missing \\0 with field %u (%lu)", data->filepath, rec->field, (unsigned long) DATA_FILE_POSITION(data, rec)); return FALSE; } void *mail_index_data_get_mmaped(MailIndexData *data, size_t *size) { if (!mmap_update(data, 0, UINT_MAX)) return NULL; *size = data->mmap_length; return data->mmap_base; }