Esempio n. 1
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;
}
Esempio n. 2
0
int mail_transaction_log_lock_head(struct mail_transaction_log *log)
{
	struct mail_transaction_log_file *file;
	int ret = 0;

	if (!log->log_2_unlink_checked) {
		/* we need to check once in a while if .log.2 should be deleted
		   to avoid wasting space on such old files. but we also don't
		   want to waste time on checking it when the same mailbox
		   gets opened over and over again rapidly (e.g. pop3). so
		   do this only when there have actually been some changes
		   to mailbox (i.e. when it's being locked here) */
		log->log_2_unlink_checked = TRUE;
		mail_transaction_log_2_unlink_old(log);
	}

	/* we want to get the head file locked. this is a bit racy,
	   since by the time we have it locked a new log file may have been
	   created.

	   creating new log file requires locking the head file, so if we
	   can lock it and don't see another file, we can be sure no-one is
	   creating a new log at the moment */

	for (;;) {
		file = log->head;
		if (mail_transaction_log_file_lock(file) < 0)
			return -1;

		file->refcount++;
		ret = mail_transaction_log_refresh(log, TRUE);
		if (--file->refcount == 0) {
			mail_transaction_logs_clean(log);
			file = NULL;
		}

		if (ret == 0 && log->head == file) {
			/* success */
			break;
		}

		if (file != NULL)
			mail_transaction_log_file_unlock(file);

		if (ret < 0)
			break;

		/* try again */
	}

	return ret;
}
Esempio n. 3
0
int mail_transaction_log_lock_head(struct mail_transaction_log *log,
				   const char *lock_reason)
{
	struct mail_transaction_log_file *file;
	time_t lock_wait_started, lock_secs = 0;
	const char *reason;
	int ret = 0;

	if (!log->log_2_unlink_checked) {
		/* we need to check once in a while if .log.2 should be deleted
		   to avoid wasting space on such old files. but we also don't
		   want to waste time on checking it when the same mailbox
		   gets opened over and over again rapidly (e.g. pop3). so
		   do this only when there have actually been some changes
		   to mailbox (i.e. when it's being locked here) */
		log->log_2_unlink_checked = TRUE;
		mail_transaction_log_2_unlink_old(log);
	}

	/* we want to get the head file locked. this is a bit racy,
	   since by the time we have it locked a new log file may have been
	   created.

	   creating new log file requires locking the head file, so if we
	   can lock it and don't see another file, we can be sure no-one is
	   creating a new log at the moment */

	lock_wait_started = time(NULL);
	for (;;) {
		file = log->head;
		if (mail_transaction_log_file_lock(file) < 0)
			return -1;

		file->refcount++;
		ret = mail_transaction_log_refresh(log, TRUE, &reason);
		if (--file->refcount == 0) {
			mail_transaction_log_file_unlock(file, t_strdup_printf(
				"trying to lock head for %s", lock_reason));
			mail_transaction_logs_clean(log);
			file = NULL;
		}

		if (ret == 0 && log->head == file) {
			/* success */
			i_assert(file != NULL);
			lock_secs = file->lock_created - lock_wait_started;
			break;
		}

		if (file != NULL) {
			mail_transaction_log_file_unlock(file, t_strdup_printf(
				"trying to lock head for %s", lock_reason));
		}
		if (ret < 0)
			break;

		/* try again */
	}
	if (lock_secs > MAIL_TRANSACTION_LOG_LOCK_WARN_SECS) {
		i_warning("Locking transaction log file %s took %ld seconds (%s)",
			  log->head->filepath, (long)lock_secs, lock_reason);
	}

	i_assert(ret < 0 || log->head != NULL);
	return ret;
}
Esempio n. 4
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;
}