Beispiel #1
0
int mail_transaction_log_create(struct mail_transaction_log *log, bool reset)
{
	struct mail_transaction_log_file *file;

	if (MAIL_INDEX_IS_IN_MEMORY(log->index)) {
		file = mail_transaction_log_file_alloc_in_memory(log);
		mail_transaction_log_set_head(log, file);
		return 0;
	}

	file = mail_transaction_log_file_alloc(log, log->filepath);
	if (log->open_file != NULL) {
		/* remember what file we tried to open. if someone else created
		   a new file, use it instead of recreating it */
		file->st_ino = log->open_file->st_ino;
		file->st_dev = log->open_file->st_dev;
		file->last_size = log->open_file->last_size;
		file->last_mtime = log->open_file->last_mtime;
		mail_transaction_log_file_free(&log->open_file);
	}

	if (mail_transaction_log_file_create(file, reset) < 0) {
		mail_transaction_log_file_free(&file);
		return -1;
	}

	mail_transaction_log_set_head(log, file);
	return 1;
}
Beispiel #2
0
int mail_transaction_log_rotate(struct mail_transaction_log *log, bool reset)
{
	struct mail_transaction_log_file *file;
	const char *path = log->head->filepath;
	struct stat st;
	int ret;

	i_assert(log->head->locked);

	if (MAIL_INDEX_IS_IN_MEMORY(log->index)) {
		file = mail_transaction_log_file_alloc_in_memory(log);
		if (reset) {
			file->hdr.prev_file_seq = 0;
			file->hdr.prev_file_offset = 0;
		}
	} else {
                /* we're locked, we shouldn't need to worry about ESTALE
                   problems in here. */
		if (fstat(log->head->fd, &st) < 0) {
			mail_index_file_set_syscall_error(log->index,
				log->head->filepath, "fstat()");
			return -1;
		}

		file = mail_transaction_log_file_alloc(log, path);

		file->st_dev = st.st_dev;
		file->st_ino = st.st_ino;
		file->last_mtime = st.st_mtime;
		file->last_size = st.st_size;

		if ((ret = mail_transaction_log_file_create(file, reset)) < 0) {
			mail_transaction_log_file_free(&file);
			return -1;
		}
		if (ret == 0) {
			mail_index_set_error(log->index,
				"Transaction log %s was recreated while we had it locked - "
				"locking is broken (lock_method=%s)", path,
				file_lock_method_to_str(log->index->lock_method));
			mail_transaction_log_file_free(&file);
			return -1;
		}
		i_assert(file->locked);
	}

	if (--log->head->refcount == 0)
		mail_transaction_logs_clean(log);
	else {
		/* the newly created log file is already locked */
		mail_transaction_log_file_unlock(log->head,
			!log->index->log_sync_locked ? "rotating" :
			"rotating while syncing");
	}
	mail_transaction_log_set_head(log, file);
	return 0;
}
Beispiel #3
0
int mail_transaction_log_open(struct mail_transaction_log *log)
{
	struct mail_transaction_log_file *file;
	const char *reason;
	int ret;

	i_free(log->filepath);
	i_free(log->filepath2);
	log->filepath = i_strconcat(log->index->filepath,
				    MAIL_TRANSACTION_LOG_SUFFIX, NULL);
	log->filepath2 = i_strconcat(log->filepath, ".2", NULL);

	/* these settings aren't available at alloc() time, so we need to
	   set them here: */
	log->nfs_flush =
		(log->index->flags & MAIL_INDEX_OPEN_FLAG_NFS_FLUSH) != 0;

	if (log->open_file != NULL)
		mail_transaction_log_file_free(&log->open_file);

	if (MAIL_INDEX_IS_IN_MEMORY(log->index))
		return 0;

	file = mail_transaction_log_file_alloc(log, log->filepath);
	if ((ret = mail_transaction_log_file_open(file, &reason)) <= 0) {
		/* leave the file for _create() */
		log->open_file = file;
		return ret;
	}
	mail_transaction_log_set_head(log, file);
	return 1;
}
static int
mail_transaction_log_refresh(struct mail_transaction_log *log, bool nfs_flush)
{
        struct mail_transaction_log_file *file;
	struct stat st;

	i_assert(log->head != NULL);

	if (MAIL_TRANSACTION_LOG_FILE_IN_MEMORY(log->head))
		return 0;

	if (nfs_flush && log->nfs_flush)
		nfs_flush_file_handle_cache(log->filepath);
	if (nfs_safe_stat(log->filepath, &st) < 0) {
		if (errno != ENOENT) {
			mail_index_file_set_syscall_error(log->index,
							  log->filepath,
							  "stat()");
			return -1;
		}
		/* see if the whole directory got deleted */
		if (nfs_safe_stat(log->index->dir, &st) < 0 &&
		    errno == ENOENT) {
			log->index->index_deleted = TRUE;
			return -1;
		}

		/* the file should always exist at this point. if it doesn't,
		   someone deleted it manually while the index was open. try to
		   handle this nicely by creating a new log file. */
		file = log->head;
		if (mail_transaction_log_create(log, FALSE) < 0)
			return -1;
		i_assert(file->refcount > 0);
		file->refcount--;
		log->index->need_recreate = TRUE;
		return 0;
	} else if (log->head->st_ino == st.st_ino &&
		   CMP_DEV_T(log->head->st_dev, st.st_dev)) {
		/* NFS: log files get rotated to .log.2 files instead
		   of being unlinked, so we don't bother checking if
		   the existing file has already been unlinked here
		   (in which case inodes could match but point to
		   different files) */
		return 0;
	}

	file = mail_transaction_log_file_alloc(log, log->filepath);
	if (mail_transaction_log_file_open(file, FALSE) <= 0) {
		mail_transaction_log_file_free(&file);
		return -1;
	}

	i_assert(!file->locked);

	if (--log->head->refcount == 0)
		mail_transaction_logs_clean(log);
	mail_transaction_log_set_head(log, file);
	return 0;
}
int mail_transaction_log_find_file(struct mail_transaction_log *log,
				   uint32_t file_seq, bool nfs_flush,
				   struct mail_transaction_log_file **file_r)
{
	struct mail_transaction_log_file *file;
	int ret;

	if (file_seq > log->head->hdr.file_seq) {
		/* see if the .log file has been recreated */
		if (log->head->locked) {
			/* transaction log is locked. there's no way a newer
			   file exists. */
			return 0;
		}
		if (log->index->open_count == 0) {
			/* we're opening the index and we just opened the
			   log file. don't waste time checking if there's a
			   newer one. */
			return 0;
		}

		if (mail_transaction_log_refresh(log, FALSE) < 0)
			return -1;
		if (file_seq > log->head->hdr.file_seq) {
			if (!nfs_flush || !log->nfs_flush)
				return 0;
			/* try again, this time flush attribute cache */
			if (mail_transaction_log_refresh(log, TRUE) < 0)
				return -1;
			if (file_seq > log->head->hdr.file_seq)
				return 0;
		}
	}

	for (file = log->files; file != NULL; file = file->next) {
		if (file->hdr.file_seq == file_seq) {
			*file_r = file;
			return 1;
		}
	}

	if (MAIL_INDEX_IS_IN_MEMORY(log->index))
		return 0;

	/* see if we have it in log.2 file */
	file = mail_transaction_log_file_alloc(log, log->filepath2);
	if ((ret = mail_transaction_log_file_open(file, TRUE)) <= 0) {
		mail_transaction_log_file_free(&file);
		return ret;
	}

	/* but is it what we expected? */
	if (file->hdr.file_seq != file_seq)
		return 0;

	*file_r = file;
	return 1;
}
Beispiel #6
0
void mail_transaction_log_close(struct mail_transaction_log *log)
{
	i_assert(log->views == NULL);

	if (log->open_file != NULL)
		mail_transaction_log_file_free(&log->open_file);
	if (log->head != NULL)
		log->head->refcount--;
	mail_transaction_logs_clean(log);
	i_assert(log->files == NULL);
}
int mail_transaction_log_rotate(struct mail_transaction_log *log, bool reset)
{
	struct mail_transaction_log_file *file;
	const char *path = log->head->filepath;
	struct stat st;

	i_assert(log->head->locked);

	if (MAIL_INDEX_IS_IN_MEMORY(log->index)) {
		file = mail_transaction_log_file_alloc_in_memory(log);
		if (reset) {
			file->hdr.prev_file_seq = 0;
			file->hdr.prev_file_offset = 0;
		}
	} else {
                /* we're locked, we shouldn't need to worry about ESTALE
                   problems in here. */
		if (fstat(log->head->fd, &st) < 0) {
			mail_index_file_set_syscall_error(log->index,
				log->head->filepath, "fstat()");
			return -1;
		}

		file = mail_transaction_log_file_alloc(log, path);

		file->st_dev = st.st_dev;
		file->st_ino = st.st_ino;
		file->last_mtime = st.st_mtime;
		file->last_size = st.st_size;

		if (mail_transaction_log_file_create(file, reset) < 0) {
			mail_transaction_log_file_free(&file);
			return -1;
		}
	}

	if (--log->head->refcount == 0)
		mail_transaction_logs_clean(log);
	else
		mail_transaction_log_file_unlock(log->head);
	mail_transaction_log_set_head(log, file);
	return 0;
}
Beispiel #8
0
void mail_transaction_log_indexid_changed(struct mail_transaction_log *log)
{
	struct mail_transaction_log_file *file;

	mail_transaction_logs_clean(log);

	for (file = log->files; file != NULL; file = file->next) {
		if (file->hdr.indexid != log->index->indexid) {
			mail_transaction_log_file_set_corrupted(file,
				"indexid changed: %u -> %u",
				file->hdr.indexid, log->index->indexid);
		}
	}

	if (log->head != NULL &&
	    log->head->hdr.indexid != log->index->indexid) {
		if (--log->head->refcount == 0)
			mail_transaction_log_file_free(&log->head);
		(void)mail_transaction_log_create(log, FALSE);
	}
}
Beispiel #9
0
void mail_transaction_logs_clean(struct mail_transaction_log *log)
{
	struct mail_transaction_log_file *file, *next;

	/* remove only files from the beginning. this way if a view has
	   referenced an old file, it can still find the new files even if
	   there aren't any references to it currently. */
	for (file = log->files; file != NULL; file = next) {
		next = file->next;

		i_assert(file->refcount >= 0);
		if (file->refcount > 0)
			break;

		mail_transaction_log_file_free(&file);
	}
	/* sanity check: we shouldn't have locked refcount=0 files */
	for (; file != NULL; file = file->next) {
		i_assert(!file->locked || file->refcount > 0);
	}
	i_assert(log->head == NULL || log->files != NULL);
}
Beispiel #10
0
int mail_transaction_log_find_file(struct mail_transaction_log *log,
				   uint32_t file_seq, bool nfs_flush,
				   struct mail_transaction_log_file **file_r,
				   const char **reason_r)
{
	struct mail_transaction_log_file *file;
	const char *reason;
	int ret;

	if (file_seq > log->head->hdr.file_seq) {
		/* see if the .log file has been recreated */
		if (log->head->locked) {
			/* transaction log is locked. there's no way a newer
			   file exists. */
			*reason_r = "Log is locked - newer log can't exist";
			return 0;
		}

		if (mail_transaction_log_refresh(log, FALSE, &reason) < 0) {
			*reason_r = reason;
			return -1;
		}
		if (file_seq > log->head->hdr.file_seq) {
			if (!nfs_flush || !log->nfs_flush) {
				*reason_r = t_strdup_printf(
					"Requested newer log than exists: %s", reason);
				return 0;
			}
			/* try again, this time flush attribute cache */
			if (mail_transaction_log_refresh(log, TRUE, &reason) < 0) {
				*reason_r = t_strdup_printf(
					"Log refresh with NFS flush failed: %s", reason);
				return -1;
			}
			if (file_seq > log->head->hdr.file_seq) {
				*reason_r = t_strdup_printf(
					"Requested newer log than exists - "
					"still after NFS flush: %s", reason);
				return 0;
			}
		}
	}

	for (file = log->files; file != NULL; file = file->next) {
		if (file->hdr.file_seq == file_seq) {
			*file_r = file;
			return 1;
		}
	}

	if (MAIL_INDEX_IS_IN_MEMORY(log->index)) {
		*reason_r = "Logs are only in memory";
		return 0;
	}

	/* see if we have it in log.2 file */
	file = mail_transaction_log_file_alloc(log, log->filepath2);
	if ((ret = mail_transaction_log_file_open(file, reason_r)) <= 0) {
		mail_transaction_log_file_free(&file);
		return ret;
	}

	/* but is it what we expected? */
	if (file->hdr.file_seq != file_seq) {
		*reason_r = t_strdup_printf(".log.2 contains file_seq=%u",
					    file->hdr.file_seq);
		return 0;
	}

	*file_r = file;
	return 1;
}