コード例 #1
0
int mail_transaction_log_file_get_highest_modseq_at(
		struct mail_transaction_log_file *file,
		uoff_t offset, uint64_t *highest_modseq_r)
{
	const struct mail_transaction_header *hdr;
	struct modseq_cache *cache;
	uoff_t cur_offset;
	uint64_t cur_modseq;
	int ret;

	i_assert(offset <= file->sync_offset);

	if (offset == file->sync_offset) {
		*highest_modseq_r = file->sync_highest_modseq;
		return 0;
	}

	cache = modseq_cache_get_offset(file, offset);
	if (cache == NULL) {
		/* nothing usable in cache - scan from beginning */
		cur_offset = file->hdr.hdr_size;
		cur_modseq = file->hdr.initial_modseq;
	} else if (cache->offset == offset) {
		/* exact cache hit */
		*highest_modseq_r = cache->highest_modseq;
		return 0;
	} else {
		/* use cache to skip over some records */
		cur_offset = cache->offset;
		cur_modseq = cache->highest_modseq;
	}

	ret = mail_transaction_log_file_map(file, cur_offset, offset);
	if (ret <= 0) {
		if (ret < 0)
			return -1;
		mail_index_set_error(file->log->index,
			"%s: Transaction log corrupted, can't get modseq",
			file->filepath);
		return -1;
	}

	i_assert(cur_offset >= file->buffer_offset);
	i_assert(cur_offset + file->buffer->used >= offset);
	while (cur_offset < offset) {
		if (log_get_synced_record(file, &cur_offset, &hdr) < 0)
			return- 1;
		mail_transaction_update_modseq(hdr, hdr + 1, &cur_modseq);
	}

	/* @UNSAFE: cache the value */
	memmove(file->modseq_cache + 1, file->modseq_cache,
		sizeof(*file->modseq_cache) *
		(N_ELEMENTS(file->modseq_cache) - 1));
	file->modseq_cache[0].offset = cur_offset;
	file->modseq_cache[0].highest_modseq = cur_modseq;

	*highest_modseq_r = cur_modseq;
	return 0;
}
コード例 #2
0
int mail_transaction_log_view_set_all(struct mail_transaction_log_view *view)
{
	struct mail_transaction_log_file *file, *first;

	/* make sure .log.2 file is opened */
	(void)mail_transaction_log_find_file(view->log, 1, FALSE, &file);

	first = view->log->files;
	i_assert(first != NULL);

	for (file = view->log->files; file != NULL; file = file->next) {
		if (mail_transaction_log_file_map(file, file->hdr.hdr_size,
						  (uoff_t)-1) < 0)
			return -1;
		if (file->hdr.prev_file_seq == 0) {
			/* this file resets the index. skip the old ones. */
			first = file;
		}
	}

	mail_transaction_log_view_unref_all(view);
	for (file = first; file != NULL; file = file->next) {
		array_append(&view->file_refs, &file, 1);
		file->refcount++;
	}

	view->tail = first;
	view->cur = view->tail;
	view->cur_offset = view->tail->hdr.hdr_size;

	view->prev_file_seq = view->cur->hdr.file_seq;
	view->prev_file_offset = view->cur_offset;

	view->min_file_seq = view->cur->hdr.file_seq;
	view->min_file_offset = view->cur_offset;
	view->max_file_seq = view->head->hdr.file_seq;
	view->max_file_offset = view->head->sync_offset;
	view->broken = FALSE;

	if (mail_transaction_log_file_get_highest_modseq_at(view->cur,
				view->cur_offset, &view->prev_modseq) < 0)
		return -1;
	return 0;
}
コード例 #3
0
int mail_transaction_log_sync_lock(struct mail_transaction_log *log,
				   uint32_t *file_seq_r, uoff_t *file_offset_r)
{
	i_assert(!log->index->log_sync_locked);

	if (mail_transaction_log_lock_head(log) < 0)
		return -1;

	/* update sync_offset */
	if (mail_transaction_log_file_map(log->head, log->head->sync_offset,
					  (uoff_t)-1) <= 0) {
		mail_transaction_log_file_unlock(log->head);
		return -1;
	}

	log->index->log_sync_locked = TRUE;
	*file_seq_r = log->head->hdr.file_seq;
	*file_offset_r = log->head->sync_offset;
	return 0;
}
コード例 #4
0
int mail_transaction_log_sync_lock(struct mail_transaction_log *log,
				   const char *lock_reason,
				   uint32_t *file_seq_r, uoff_t *file_offset_r)
{
	i_assert(!log->index->log_sync_locked);

	if (mail_transaction_log_lock_head(log, lock_reason) < 0)
		return -1;

	/* update sync_offset */
	if (mail_transaction_log_file_map(log->head, log->head->sync_offset,
					  (uoff_t)-1) <= 0) {
		mail_transaction_log_file_unlock(log->head, t_strdup_printf(
			"%s - map failed", lock_reason));
		return -1;
	}

	log->index->log_sync_locked = TRUE;
	*file_seq_r = log->head->hdr.file_seq;
	*file_offset_r = log->head->sync_offset;
	return 0;
}
コード例 #5
0
int mail_transaction_log_view_set(struct mail_transaction_log_view *view,
				  uint32_t min_file_seq, uoff_t min_file_offset,
				  uint32_t max_file_seq, uoff_t max_file_offset,
				  bool *reset_r, const char **reason_r)
{
	struct mail_transaction_log_file *file, *const *files;
	uoff_t start_offset, end_offset;
	unsigned int i;
	uint32_t seq;
	int ret;

	*reset_r = FALSE;
	*reason_r = NULL;

	if (view->log == NULL) {
		/* transaction log is closed already. this log view shouldn't
		   be used anymore. */
		*reason_r = "Log already closed";
		return -1;
	}

	if (min_file_seq == 0) {
		/* index file doesn't exist yet. this transaction log should
		   start from the beginning */
		if (view->log->files->hdr.prev_file_seq != 0) {
			/* but it doesn't */
			*reason_r = t_strdup_printf(
				"Wanted log beginning, but found prev_file_seq=%u",
				view->log->files->hdr.prev_file_seq);
			return 0;
		}

		min_file_seq = view->log->files->hdr.file_seq;
		min_file_offset = 0;

		if (max_file_seq == 0) {
			max_file_seq = min_file_seq;
			max_file_offset = min_file_offset;
		}
	}

	for (file = view->log->files; file != NULL; file = file->next) {
		if (file->hdr.prev_file_seq == min_file_seq)
			break;
	}

	if (file != NULL && min_file_offset == file->hdr.prev_file_offset) {
		/* we can (and sometimes must) skip to the next file */
		min_file_seq = file->hdr.file_seq;
		min_file_offset = file->hdr.hdr_size;
	}

	for (file = view->log->files; file != NULL; file = file->next) {
		if (file->hdr.prev_file_seq == max_file_seq)
			break;
	}
	if (file != NULL && max_file_offset == file->hdr.prev_file_offset) {
		/* we can skip to the next file. we've delayed checking for
		   min_file_seq <= max_file_seq until now, because it's not
		   really an error to specify the same position twice (even if
		   in "wrong" order) */
		i_assert(min_file_seq <= max_file_seq ||
			 min_file_seq <= file->hdr.file_seq);
		max_file_seq = file->hdr.file_seq;
		max_file_offset = file->hdr.hdr_size;
	} else {
		i_assert(min_file_seq <= max_file_seq);
	}

	if (min_file_seq == max_file_seq && min_file_offset > max_file_offset) {
		/* log file offset is probably corrupted in the index file. */
		*reason_r = t_strdup_printf(
			"Invalid offset: file_seq=%u, min_file_offset (%"PRIuUOFF_T
			") > max_file_offset (%"PRIuUOFF_T")",
			min_file_seq, min_file_offset, max_file_offset);
		mail_transaction_log_view_set_corrupted(view, "%s", *reason_r);
		return -1;
	}

	view->tail = view->head = file = NULL;
	for (seq = min_file_seq; seq <= max_file_seq; seq++) {
		if (file == NULL || file->hdr.file_seq != seq) {
			/* see if we could find the missing file. if we know
			   the max. file sequence, make sure NFS attribute
			   cache gets flushed if necessary. */
			bool nfs_flush = max_file_seq != (uint32_t)-1;

			ret = mail_transaction_log_find_file(view->log, seq,
							     nfs_flush, &file);
			if (ret <= 0) {
				if (ret < 0) {
					*reason_r = t_strdup_printf(
						"Failed to find file seq=%u", seq);
					return -1;
				}

				/* not found / corrupted */
				file = NULL;
			}
		}

		if (file == NULL || file->hdr.file_seq != seq) {
			if (file == NULL && max_file_seq == (uint32_t)-1 &&
			    view->head == view->log->head) {
				/* we just wanted to sync everything */
				i_assert(max_file_offset == (uoff_t)-1);
				max_file_seq = seq-1;
				break;
			}
			/* if any of the found files reset the index,
			   ignore any missing files up to it */
			file = view->tail != NULL ? view->tail :
				view->log->files;
			for (;; file = file->next) {
				if (file == NULL ||
				    file->hdr.file_seq > max_file_seq) {
					/* missing files in the middle */
					*reason_r = t_strdup_printf(
						"Missing middle file seq=%u (between %u..%u)",
						seq, min_file_seq, max_file_seq);
					return 0;
				}

				if (file->hdr.file_seq >= seq &&
				    file->hdr.prev_file_seq == 0) {
					/* we can ignore the missing file */
					break;
				}
			}
			seq = file->hdr.file_seq;
			view->tail = NULL;
		} 

		if (view->tail == NULL)
			view->tail = file;
		view->head = file;
		file = file->next;
	}
	i_assert(view->tail != NULL);

	if (min_file_offset == 0) {
		/* beginning of the file */
		min_file_offset = view->tail->hdr.hdr_size;
		if (min_file_offset > max_file_offset &&
		    min_file_seq == max_file_seq) {
			/* we don't actually want to show anything */
			max_file_offset = min_file_offset;
		}
	}

	if (min_file_offset < view->tail->hdr.hdr_size) {
		/* log file offset is probably corrupted in the index file. */
		*reason_r = t_strdup_printf(
			"Invalid min_file_offset: file_seq=%u, min_file_offset (%"PRIuUOFF_T
			") < hdr_size (%u)",
			min_file_seq, min_file_offset, view->tail->hdr.hdr_size);
		mail_transaction_log_view_set_corrupted(view, "%s", *reason_r);
		return -1;
	}
	if (max_file_offset < view->head->hdr.hdr_size) {
		/* log file offset is probably corrupted in the index file. */
		*reason_r = t_strdup_printf(
			"Invalid max_file_offset: file_seq=%u, min_file_offset (%"PRIuUOFF_T
			") < hdr_size (%u)",
			max_file_seq, max_file_offset, view->head->hdr.hdr_size);
		mail_transaction_log_view_set_corrupted(view, "%s", *reason_r);
		return -1;
	}

	/* we have all of them. update refcounts. */
	mail_transaction_log_view_unref_all(view);

	/* Reference all used files. */
	for (file = view->tail;; file = file->next) {
		array_append(&view->file_refs, &file, 1);
		file->refcount++;

		if (file == view->head)
			break;
	}

	view->cur = view->tail;
	view->cur_offset = view->cur->hdr.file_seq == min_file_seq ?
		min_file_offset : view->cur->hdr.hdr_size;

	/* Map the files only after we've found them all. Otherwise if we map
	   one file and then another file just happens to get rotated, we could
	   include both files in the view but skip the last transactions from
	   the first file.

	   We're mapping the files in reverse order so that _log_file_map()
	   can verify that prev_file_offset matches how far it actually managed
	   to sync the file. */
	files = array_idx(&view->file_refs, 0);
	for (i = array_count(&view->file_refs); i > 0; i--) {
		file = files[i-1];
		start_offset = file->hdr.file_seq == min_file_seq ?
			min_file_offset : file->hdr.hdr_size;
		end_offset = file->hdr.file_seq == max_file_seq ?
			max_file_offset : (uoff_t)-1;
		ret = mail_transaction_log_file_map(file, start_offset,
						    end_offset);
		if (ret <= 0) {
			*reason_r = t_strdup_printf(
				"Failed to map file seq=%u "
				"offset=%"PRIuUOFF_T"..%"PRIuUOFF_T" (ret=%d)",
				file->hdr.file_seq, start_offset, end_offset, ret);
			return ret;
		}

		if (file->hdr.prev_file_seq == 0) {
			/* this file resets the index.
			   don't bother reading the others. */
			if (view->cur != file ||
			    view->cur_offset == file->hdr.hdr_size) {
				view->cur = file;
				view->cur_offset = file->hdr.hdr_size;
				*reset_r = TRUE;
				break;
			}
			i_assert(i == 1);
		}
	}

	if (min_file_seq == view->head->hdr.file_seq &&
	    min_file_offset > view->head->sync_offset) {
		/* log file offset is probably corrupted in the index file. */
		*reason_r = t_strdup_printf(
			"Invalid offset: file_seq=%u, min_file_offset (%"PRIuUOFF_T
			") > sync_offset (%"PRIuUOFF_T")", min_file_seq,
			min_file_offset, view->head->sync_offset);
		mail_transaction_log_view_set_corrupted(view, "%s", *reason_r);
		return -1;
	}

	i_assert(max_file_seq == (uint32_t)-1 ||
		 max_file_seq == view->head->hdr.file_seq);
	i_assert(max_file_offset == (uoff_t)-1 ||
		 max_file_offset <= view->head->sync_offset);
	i_assert(min_file_seq != max_file_seq ||
		 max_file_seq != view->head->hdr.file_seq ||
		 max_file_offset != (uoff_t)-1 ||
		 min_file_offset <= view->head->sync_offset);

	view->prev_file_seq = view->cur->hdr.file_seq;
	view->prev_file_offset = view->cur_offset;

	view->min_file_seq = min_file_seq;
	view->min_file_offset = min_file_offset;
	view->max_file_seq = max_file_seq;
	view->max_file_offset = I_MIN(max_file_offset, view->head->sync_offset);
	view->broken = FALSE;

	if (mail_transaction_log_file_get_highest_modseq_at(view->cur,
				view->cur_offset, &view->prev_modseq) < 0) {
		*reason_r = "Failed to get modseq";
		return -1;
	}

	i_assert(view->cur_offset <= view->cur->sync_offset);
	return 1;
}