view src/lib-index/mail-index-compress.c @ 903:fd8888f6f037 HEAD

Naming style changes, finally got tired of most of the typedefs. Also the previous enum -> macro change reverted so that we don't use the highest bit anymore, that's incompatible with old indexes so they will be rebuilt.
author Timo Sirainen <tss@iki.fi>
date Sun, 05 Jan 2003 15:09:51 +0200
parents e3162b4267ec
children 2512ad6fd9e9
line wrap: on
line source

/* Copyright (C) 2002 Timo Sirainen */

#include "lib.h"
#include "write-full.h"
#include "mail-index.h"
#include "mail-index-data.h"
#include "mail-index-util.h"
#include "mail-tree.h"

#include <stdio.h>
#include <unistd.h>

int mail_index_truncate(struct mail_index *index)
{
	uoff_t empty_space, truncate_threshold;

	i_assert(index->lock_type == MAIL_LOCK_EXCLUSIVE);

	if (index->mmap_full_length <= INDEX_FILE_MIN_SIZE || index->anon_mmap)
		return TRUE;

	/* really truncate the file only when it's almost empty */
	empty_space = index->mmap_full_length - index->mmap_used_length;
	truncate_threshold =
		index->mmap_full_length / 100 * INDEX_TRUNCATE_PERCENTAGE;

	if (empty_space > truncate_threshold) {
		index->mmap_full_length = index->mmap_used_length +
			(empty_space * INDEX_TRUNCATE_KEEP_PERCENTAGE / 100);

		/* keep the size record-aligned */
		index->mmap_full_length -= (index->mmap_full_length -
					    sizeof(struct mail_index_header)) %
			sizeof(struct mail_index_record);

		if (index->mmap_full_length < INDEX_FILE_MIN_SIZE)
                        index->mmap_full_length = INDEX_FILE_MIN_SIZE;

		if (ftruncate(index->fd, (off_t)index->mmap_full_length) < 0)
			return index_set_syscall_error(index, "ftruncate()");

		index->header->sync_id++;
	}

	return TRUE;
}

int mail_index_compress(struct mail_index *index)
{
	struct mail_index_record *rec, *hole_rec, *end_rec;
	unsigned int idx;
	int tree_fd;

	if (!index->set_lock(index, MAIL_LOCK_EXCLUSIVE))
		return FALSE;

	if (index->header->first_hole_records == 0) {
		/* we don't need to compress after all. shouldn't happen.. */
		index->header->flags &= ~MAIL_INDEX_FLAG_COMPRESS;
		return TRUE;
	}

	if (!mail_index_verify_hole_range(index))
		return FALSE;

	/* if we get interrupted, the whole index is probably corrupted.
	   so keep rebuild-flag on while doing this */
	index->header->flags |= MAIL_INDEX_FLAG_REBUILD;
	if (!mail_index_fmdatasync(index, sizeof(struct mail_index_header)))
		return FALSE;

	/* first actually compress the data */
	hole_rec = INDEX_RECORD_AT(index, index->header->first_hole_index);
	end_rec = INDEX_END_RECORD(index);
	rec = hole_rec + index->header->first_hole_records;
	while (rec < end_rec) {
		if (rec->uid != 0) {
			memcpy(hole_rec, rec, sizeof(struct mail_index_record));
			idx = INDEX_RECORD_INDEX(index, hole_rec);
			if (!mail_tree_update(index->tree, rec->uid, idx))
				return FALSE;
			hole_rec++;
		}
		rec++;
	}

	/* truncate the file to get rid of the extra records */
	index->mmap_used_length = (size_t) ((char *) hole_rec -
					    (char *) index->mmap_base);
	index->header->used_file_size = index->mmap_used_length;

	if (!mail_index_truncate(index))
		return FALSE;

	/* update headers */
	index->header->first_hole_index = 0;
	index->header->first_hole_records = 0;

	/* make sure the whole file is synced before removing rebuild-flag */
	if (!mail_tree_sync_file(index->tree, &tree_fd))
		return FALSE;

	if (fdatasync(tree_fd) < 0) {
		index_file_set_syscall_error(index, index->tree->filepath,
					     "fdatasync()");
		return FALSE;
	}

	if (!mail_index_fmdatasync(index, index->mmap_used_length))
		return FALSE;

	index->header->flags &= ~(MAIL_INDEX_FLAG_COMPRESS |
				  MAIL_INDEX_FLAG_REBUILD);
	return TRUE;
}

static int mail_index_copy_data(struct mail_index *index,
				int fd, const char *path)
{
	struct mail_index_data_header data_hdr;
	struct mail_index_data_record_header *rec_hdr;
	struct mail_index_record *rec;
	unsigned char *mmap_data;
	size_t mmap_data_size;
	uoff_t offset;

	mmap_data = mail_index_data_get_mmaped(index->data, &mmap_data_size);
	if (mmap_data == NULL)
		return FALSE;

	/* write data header */
	memset(&data_hdr, 0, sizeof(data_hdr));
	data_hdr.indexid = index->indexid;
	if (write_full(fd, &data_hdr, sizeof(data_hdr)) < 0) {
		if (errno == ENOSPC)
			index->nodiskspace = TRUE;

		index_file_set_syscall_error(index, path, "write_full()");
		return FALSE;
	}

	/* now we'll begin the actual moving. keep rebuild-flag on
	   while doing it. */
	index->header->flags |= MAIL_INDEX_FLAG_REBUILD;
	if (!mail_index_fmdatasync(index, sizeof(struct mail_index_header)))
		return FALSE;

	offset = sizeof(data_hdr);
	rec = index->lookup(index, 1);
	while (rec != NULL) {
		if (rec->data_position + sizeof(*rec_hdr) > mmap_data_size) {
			index_set_corrupted(index,
				"data_position points outside file");
			return FALSE;
		}

		rec_hdr = (struct mail_index_data_record_header *)
			(mmap_data + rec->data_position);
		if (rec->data_position + rec_hdr->data_size > mmap_data_size) {
			index_set_corrupted(index,
				"data_size points outside file");
			return FALSE;
		}

		if (write_full(fd, mmap_data + rec->data_position,
			       rec_hdr->data_size) < 0) {
			if (errno == ENOSPC)
				index->nodiskspace = TRUE;

			index_file_set_syscall_error(index, path,
						     "write_full()");
			return FALSE;
		}

		rec->data_position = offset;
		offset += rec_hdr->data_size;

		rec = index->next(index, rec);
	}

	/* update header */
	data_hdr.used_file_size = offset;

	if (lseek(fd, 0, SEEK_SET) < 0)
		return index_file_set_syscall_error(index, path, "lseek()");

	if (write_full(fd, &data_hdr, sizeof(data_hdr)) < 0) {
		index_file_set_syscall_error(index, path, "write_full()");
		return FALSE;
	}

	return TRUE;
}

int mail_index_compress_data(struct mail_index *index)
{
	const char *temppath, *datapath;
	int fd, failed;

	if (index->anon_mmap)
		return TRUE;

	/* write the data into temporary file updating the offsets in index
	   while doing it. if we fail (especially if out of disk space/quota)
	   we'll simply fail and index is rebuilt later */
	if (!index->set_lock(index, MAIL_LOCK_EXCLUSIVE))
		return FALSE;

	fd = mail_index_create_temp_file(index, &temppath);
	if (fd == -1) {
		if (errno == ENOSPC)
			index->nodiskspace = TRUE;
		return FALSE;
	}

	failed = !mail_index_copy_data(index, fd, temppath);

	if (fdatasync(fd) < 0) {
		index_file_set_syscall_error(index, temppath, "fdatasync()");
		failed = TRUE;
	}

	if (close(fd) < 0) {
		index_file_set_syscall_error(index, temppath, "close()");
		failed = TRUE;
	}

	if (!failed) {
		/* now, rename the temp file to new data file. but before that
		   reset indexid to make sure that other processes know the
		   data file is closed. */
		(void)mail_index_data_mark_file_deleted(index->data);

		mail_index_data_free(index->data);

		datapath = t_strconcat(index->filepath, DATA_FILE_PREFIX, NULL);
		if (rename(temppath, datapath) < 0) {
			if (errno == ENOSPC)
				index->nodiskspace = TRUE;

			index_set_error(index, "rename(%s, %s) failed: %m",
					temppath, datapath);
			failed = TRUE;
		}
	}

	if (failed) {
		if (unlink(temppath) < 0) {
			index_file_set_syscall_error(index, temppath,
						     "unlink()");
		}
		return FALSE;
	}

	/* make sure the whole file is synced before removing rebuild-flag */
	if (!mail_index_fmdatasync(index, index->mmap_used_length))
		return FALSE;

	index->header->flags &= ~(MAIL_INDEX_FLAG_COMPRESS_DATA |
				  MAIL_INDEX_FLAG_REBUILD);

	return mail_index_data_open(index);
}