view src/lib-index/test-mail-transaction-log-append.c @ 21876:ccbdafa83dbc

lib-index: Don't increase modseq for backend/dirty flag changes These flags are used only for internal changes and they shouldn't be triggering any modseq changes. To avoid modseqs from unexpectedly shrinking, the new modseq counting behavior is enabled only for newly rotated transaction log files that have a new minor_version.
author Timo Sirainen <timo.sirainen@dovecot.fi>
date Mon, 27 Mar 2017 18:05:29 +0300
parents 2e2563132d5f
children cb108f786fb4
line wrap: on
line source

/* Copyright (c) 2009-2017 Dovecot authors, see the included COPYING file */

#include "lib.h"
#include "buffer.h"
#include "test-common.h"
#include "mail-index-private.h"
#include "mail-transaction-log-private.h"

#include <sys/stat.h>

static bool log_lock_failure = FALSE;

void mail_index_file_set_syscall_error(struct mail_index *index ATTR_UNUSED,
				       const char *filepath ATTR_UNUSED,
				       const char *function ATTR_UNUSED)
{
}

int mail_transaction_log_lock_head(struct mail_transaction_log *log ATTR_UNUSED,
				   const char *lock_reason ATTR_UNUSED)
{
	return log_lock_failure ? -1 : 0;
}

void mail_transaction_log_file_unlock(struct mail_transaction_log_file *file ATTR_UNUSED,
				      const char *lock_reason ATTR_UNUSED) {}

void mail_transaction_update_modseq(const struct mail_transaction_header *hdr,
				    const void *data ATTR_UNUSED,
				    uint64_t *cur_modseq,
				    unsigned int version ATTR_UNUSED)
{
	if ((hdr->type & MAIL_TRANSACTION_EXPUNGE) != 0)
		*cur_modseq += 1;
}

int mail_index_move_to_memory(struct mail_index *index ATTR_UNUSED)
{
	return -1;
}

static void test_append_expunge(struct mail_transaction_log *log)
{
	static unsigned int buf[] = { 0x12345678, 0xabcdef09 };
	struct mail_transaction_log_file *file = log->head;
	struct mail_transaction_log_append_ctx *ctx;
	const struct mail_transaction_header *hdr;
	const unsigned int *bufp;
	const struct mail_transaction_boundary *bound;

	test_assert(mail_transaction_log_append_begin(log->index, MAIL_TRANSACTION_EXTERNAL, &ctx) == 0);
	mail_transaction_log_append_add(ctx, MAIL_TRANSACTION_APPEND,
					&buf[0], sizeof(buf[0]));
	test_assert(ctx->new_highest_modseq == 0);
	mail_transaction_log_append_add(ctx, MAIL_TRANSACTION_EXPUNGE,
					&buf[1], sizeof(buf[1]));
	test_assert(ctx->new_highest_modseq == 1);

	test_assert(mail_transaction_log_append_commit(&ctx) == 0);
	test_assert(file->sync_highest_modseq == 1);
	test_assert(file->sync_offset == file->buffer_offset + file->buffer->used);

	hdr = file->buffer->data;
	test_assert(hdr->type == (MAIL_TRANSACTION_BOUNDARY |
				  MAIL_TRANSACTION_EXTERNAL));
	test_assert(mail_index_offset_to_uint32(hdr->size) == sizeof(*hdr) + sizeof(*bound));
	bound = (const void *)(hdr + 1);
	test_assert(bound->size == file->buffer->used);
	hdr = (const void *)(bound + 1);

	test_assert(hdr->type == (MAIL_TRANSACTION_APPEND |
				  MAIL_TRANSACTION_EXTERNAL));
	test_assert(mail_index_offset_to_uint32(hdr->size) == sizeof(*hdr) + sizeof(buf[0]));
	bufp = (const void *)(hdr + 1);
	test_assert(*bufp == buf[0]);

	hdr = (const void *)(bufp + 1);
	test_assert(hdr->type == (MAIL_TRANSACTION_EXPUNGE |
				  MAIL_TRANSACTION_EXPUNGE_PROT |
				  MAIL_TRANSACTION_EXTERNAL));
	test_assert(mail_index_offset_to_uint32(hdr->size) == sizeof(*hdr) + sizeof(buf[0]));
	bufp = (const void *)(hdr + 1);
	test_assert(*bufp == buf[1]);

	test_assert(file->buffer->used == (size_t)((const char *)(bufp+1) - (const char *)file->buffer->data));

	buffer_set_used_size(file->buffer, 0);
	file->buffer_offset = 0;
	test_end();
}

static void test_append_sync_offset(struct mail_transaction_log *log)
{
	struct mail_transaction_log_file *file = log->head;
	struct mail_transaction_log_append_ctx *ctx;
	const struct mail_transaction_header *hdr;
	const struct mail_transaction_header_update *u;
	const uint32_t *offsetp;

	test_begin("transaction log append: append_sync_offset only");
	test_assert(mail_transaction_log_append_begin(log->index, 0, &ctx) == 0);
	ctx->index_sync_transaction = TRUE;
	file->max_tail_offset = 123;
	test_assert(mail_transaction_log_append_commit(&ctx) == 0);

	test_assert(file->buffer->used == sizeof(*hdr) + sizeof(*u) + sizeof(*offsetp));
	hdr = file->buffer->data;
	test_assert(hdr->type == MAIL_TRANSACTION_HEADER_UPDATE);
	test_assert(mail_index_offset_to_uint32(hdr->size) == file->buffer->used);
	u = (const void *)(hdr + 1);
	test_assert(u->offset == offsetof(struct mail_index_header, log_file_tail_offset));
	test_assert(u->size == sizeof(*offsetp));
	offsetp = (const void *)(u+1);
	test_assert(*offsetp == 123);

	test_end();
}

static void test_mail_transaction_log_append(void)
{
	struct mail_transaction_log *log;
	struct mail_transaction_log_file *file;
	struct mail_transaction_log_append_ctx *ctx;
	char tmp_path[] = "/tmp/dovecot.test.XXXXXX";
	struct stat st;
	int fd;

	fd = mkstemp(tmp_path);
	if (fd == -1)
		i_fatal("mkstemp(%s) failed: %m", tmp_path);

	test_begin("transaction log append");
	log = i_new(struct mail_transaction_log, 1);
	log->index = i_new(struct mail_index, 1);
	log->index->log = log;
	log->head = file = i_new(struct mail_transaction_log_file, 1);
	file->fd = -1;

	test_append_expunge(log);

	test_begin("transaction log append: lock failure");
	log_lock_failure = TRUE;
	test_assert(mail_transaction_log_append_begin(log->index, 0, &ctx) < 0);
	log_lock_failure = FALSE;
	test_end();

	test_append_sync_offset(log);

	/* do this after head->buffer has already been initialized */
	test_begin("transaction log append: garbage truncation");
	file->sync_offset = 1;
	file->buffer_offset = 1;
	file->last_size = 3;
	file->fd = fd;
	test_assert(mail_transaction_log_append_begin(log->index, 0, &ctx) == 0);
	test_assert(mail_transaction_log_append_commit(&ctx) == 0);
	if (fstat(fd, &st) < 0) i_fatal("fstat() failed: %m");
	test_assert(st.st_size == 1);
	file->fd = -1;
	test_end();

	buffer_free(&log->head->buffer);
	i_free(log->head);
	i_free(log->index);
	i_free(log);
	i_unlink(tmp_path);
}

int main(void)
{
	static void (*test_functions[])(void) = {
		test_mail_transaction_log_append,
		NULL
	};
	return test_run(test_functions);
}