Blame lockfile.c

Packit 4511e4
/*
Packit 4511e4
 * Copyright (c) 2005, Junio C Hamano
Packit 4511e4
 */
Packit 4511e4
Packit 4511e4
#include "cache.h"
Packit 4511e4
#include "lockfile.h"
Packit 4511e4
Packit 4511e4
/*
Packit 4511e4
 * path = absolute or relative path name
Packit 4511e4
 *
Packit 4511e4
 * Remove the last path name element from path (leaving the preceding
Packit 4511e4
 * "/", if any).  If path is empty or the root directory ("/"), set
Packit 4511e4
 * path to the empty string.
Packit 4511e4
 */
Packit 4511e4
static void trim_last_path_component(struct strbuf *path)
Packit 4511e4
{
Packit 4511e4
	int i = path->len;
Packit 4511e4
Packit 4511e4
	/* back up past trailing slashes, if any */
Packit 4511e4
	while (i && path->buf[i - 1] == '/')
Packit 4511e4
		i--;
Packit 4511e4
Packit 4511e4
	/*
Packit 4511e4
	 * then go backwards until a slash, or the beginning of the
Packit 4511e4
	 * string
Packit 4511e4
	 */
Packit 4511e4
	while (i && path->buf[i - 1] != '/')
Packit 4511e4
		i--;
Packit 4511e4
Packit 4511e4
	strbuf_setlen(path, i);
Packit 4511e4
}
Packit 4511e4
Packit 4511e4
Packit 4511e4
/* We allow "recursive" symbolic links. Only within reason, though */
Packit 4511e4
#define MAXDEPTH 5
Packit 4511e4
Packit 4511e4
/*
Packit 4511e4
 * path contains a path that might be a symlink.
Packit 4511e4
 *
Packit 4511e4
 * If path is a symlink, attempt to overwrite it with a path to the
Packit 4511e4
 * real file or directory (which may or may not exist), following a
Packit 4511e4
 * chain of symlinks if necessary.  Otherwise, leave path unmodified.
Packit 4511e4
 *
Packit 4511e4
 * This is a best-effort routine.  If an error occurs, path will
Packit 4511e4
 * either be left unmodified or will name a different symlink in a
Packit 4511e4
 * symlink chain that started with the original path.
Packit 4511e4
 */
Packit 4511e4
static void resolve_symlink(struct strbuf *path)
Packit 4511e4
{
Packit 4511e4
	int depth = MAXDEPTH;
Packit 4511e4
	static struct strbuf link = STRBUF_INIT;
Packit 4511e4
Packit 4511e4
	while (depth--) {
Packit 4511e4
		if (strbuf_readlink(&link, path->buf, path->len) < 0)
Packit 4511e4
			break;
Packit 4511e4
Packit 4511e4
		if (is_absolute_path(link.buf))
Packit 4511e4
			/* absolute path simply replaces p */
Packit 4511e4
			strbuf_reset(path);
Packit 4511e4
		else
Packit 4511e4
			/*
Packit 4511e4
			 * link is a relative path, so replace the
Packit 4511e4
			 * last element of p with it.
Packit 4511e4
			 */
Packit 4511e4
			trim_last_path_component(path);
Packit 4511e4
Packit 4511e4
		strbuf_addbuf(path, &link);
Packit 4511e4
	}
Packit 4511e4
	strbuf_reset(&link);
Packit 4511e4
}
Packit 4511e4
Packit 4511e4
/* Make sure errno contains a meaningful value on error */
Packit Service b5fd21
static int lock_file(struct lock_file *lk, const char *path, int flags,
Packit Service b5fd21
		     int mode)
Packit 4511e4
{
Packit 4511e4
	struct strbuf filename = STRBUF_INIT;
Packit 4511e4
Packit 4511e4
	strbuf_addstr(&filename, path);
Packit 4511e4
	if (!(flags & LOCK_NO_DEREF))
Packit 4511e4
		resolve_symlink(&filename);
Packit 4511e4
Packit 4511e4
	strbuf_addstr(&filename, LOCK_SUFFIX);
Packit Service b5fd21
	lk->tempfile = create_tempfile_mode(filename.buf, mode);
Packit 4511e4
	strbuf_release(&filename);
Packit 4511e4
	return lk->tempfile ? lk->tempfile->fd : -1;
Packit 4511e4
}
Packit 4511e4
Packit 4511e4
/*
Packit 4511e4
 * Constants defining the gaps between attempts to lock a file. The
Packit 4511e4
 * first backoff period is approximately INITIAL_BACKOFF_MS
Packit 4511e4
 * milliseconds. The longest backoff period is approximately
Packit 4511e4
 * (BACKOFF_MAX_MULTIPLIER * INITIAL_BACKOFF_MS) milliseconds.
Packit 4511e4
 */
Packit 4511e4
#define INITIAL_BACKOFF_MS 1L
Packit 4511e4
#define BACKOFF_MAX_MULTIPLIER 1000
Packit 4511e4
Packit 4511e4
/*
Packit 4511e4
 * Try locking path, retrying with quadratic backoff for at least
Packit 4511e4
 * timeout_ms milliseconds. If timeout_ms is 0, try locking the file
Packit 4511e4
 * exactly once. If timeout_ms is -1, try indefinitely.
Packit 4511e4
 */
Packit 4511e4
static int lock_file_timeout(struct lock_file *lk, const char *path,
Packit Service b5fd21
			     int flags, long timeout_ms, int mode)
Packit 4511e4
{
Packit 4511e4
	int n = 1;
Packit 4511e4
	int multiplier = 1;
Packit 4511e4
	long remaining_ms = 0;
Packit 4511e4
	static int random_initialized = 0;
Packit 4511e4
Packit 4511e4
	if (timeout_ms == 0)
Packit Service b5fd21
		return lock_file(lk, path, flags, mode);
Packit 4511e4
Packit 4511e4
	if (!random_initialized) {
Packit 4511e4
		srand((unsigned int)getpid());
Packit 4511e4
		random_initialized = 1;
Packit 4511e4
	}
Packit 4511e4
Packit 4511e4
	if (timeout_ms > 0)
Packit 4511e4
		remaining_ms = timeout_ms;
Packit 4511e4
Packit 4511e4
	while (1) {
Packit 4511e4
		long backoff_ms, wait_ms;
Packit 4511e4
		int fd;
Packit 4511e4
Packit Service b5fd21
		fd = lock_file(lk, path, flags, mode);
Packit 4511e4
Packit 4511e4
		if (fd >= 0)
Packit 4511e4
			return fd; /* success */
Packit 4511e4
		else if (errno != EEXIST)
Packit 4511e4
			return -1; /* failure other than lock held */
Packit 4511e4
		else if (timeout_ms > 0 && remaining_ms <= 0)
Packit 4511e4
			return -1; /* failure due to timeout */
Packit 4511e4
Packit 4511e4
		backoff_ms = multiplier * INITIAL_BACKOFF_MS;
Packit 4511e4
		/* back off for between 0.75*backoff_ms and 1.25*backoff_ms */
Packit 4511e4
		wait_ms = (750 + rand() % 500) * backoff_ms / 1000;
Packit 4511e4
		sleep_millisec(wait_ms);
Packit 4511e4
		remaining_ms -= wait_ms;
Packit 4511e4
Packit 4511e4
		/* Recursion: (n+1)^2 = n^2 + 2n + 1 */
Packit 4511e4
		multiplier += 2*n + 1;
Packit 4511e4
		if (multiplier > BACKOFF_MAX_MULTIPLIER)
Packit 4511e4
			multiplier = BACKOFF_MAX_MULTIPLIER;
Packit 4511e4
		else
Packit 4511e4
			n++;
Packit 4511e4
	}
Packit 4511e4
}
Packit 4511e4
Packit 4511e4
void unable_to_lock_message(const char *path, int err, struct strbuf *buf)
Packit 4511e4
{
Packit 4511e4
	if (err == EEXIST) {
Packit 4511e4
		strbuf_addf(buf, _("Unable to create '%s.lock': %s.\n\n"
Packit 4511e4
		    "Another git process seems to be running in this repository, e.g.\n"
Packit 4511e4
		    "an editor opened by 'git commit'. Please make sure all processes\n"
Packit 4511e4
		    "are terminated then try again. If it still fails, a git process\n"
Packit 4511e4
		    "may have crashed in this repository earlier:\n"
Packit 4511e4
		    "remove the file manually to continue."),
Packit 4511e4
			    absolute_path(path), strerror(err));
Packit 4511e4
	} else
Packit 4511e4
		strbuf_addf(buf, _("Unable to create '%s.lock': %s"),
Packit 4511e4
			    absolute_path(path), strerror(err));
Packit 4511e4
}
Packit 4511e4
Packit 4511e4
NORETURN void unable_to_lock_die(const char *path, int err)
Packit 4511e4
{
Packit 4511e4
	struct strbuf buf = STRBUF_INIT;
Packit 4511e4
Packit 4511e4
	unable_to_lock_message(path, err, &buf;;
Packit 4511e4
	die("%s", buf.buf);
Packit 4511e4
}
Packit 4511e4
Packit 4511e4
/* This should return a meaningful errno on failure */
Packit Service b5fd21
int hold_lock_file_for_update_timeout_mode(struct lock_file *lk,
Packit Service b5fd21
					   const char *path, int flags,
Packit Service b5fd21
					   long timeout_ms, int mode)
Packit 4511e4
{
Packit Service b5fd21
	int fd = lock_file_timeout(lk, path, flags, timeout_ms, mode);
Packit 4511e4
	if (fd < 0) {
Packit 4511e4
		if (flags & LOCK_DIE_ON_ERROR)
Packit 4511e4
			unable_to_lock_die(path, errno);
Packit 4511e4
		if (flags & LOCK_REPORT_ON_ERROR) {
Packit 4511e4
			struct strbuf buf = STRBUF_INIT;
Packit 4511e4
			unable_to_lock_message(path, errno, &buf;;
Packit 4511e4
			error("%s", buf.buf);
Packit 4511e4
			strbuf_release(&buf;;
Packit 4511e4
		}
Packit 4511e4
	}
Packit 4511e4
	return fd;
Packit 4511e4
}
Packit 4511e4
Packit 4511e4
char *get_locked_file_path(struct lock_file *lk)
Packit 4511e4
{
Packit 4511e4
	struct strbuf ret = STRBUF_INIT;
Packit 4511e4
Packit 4511e4
	strbuf_addstr(&ret, get_tempfile_path(lk->tempfile));
Packit 4511e4
	if (ret.len <= LOCK_SUFFIX_LEN ||
Packit 4511e4
	    strcmp(ret.buf + ret.len - LOCK_SUFFIX_LEN, LOCK_SUFFIX))
Packit 4511e4
		BUG("get_locked_file_path() called for malformed lock object");
Packit 4511e4
	/* remove ".lock": */
Packit 4511e4
	strbuf_setlen(&ret, ret.len - LOCK_SUFFIX_LEN);
Packit 4511e4
	return strbuf_detach(&ret, NULL);
Packit 4511e4
}
Packit 4511e4
Packit 4511e4
int commit_lock_file(struct lock_file *lk)
Packit 4511e4
{
Packit 4511e4
	char *result_path = get_locked_file_path(lk);
Packit 4511e4
Packit 4511e4
	if (commit_lock_file_to(lk, result_path)) {
Packit 4511e4
		int save_errno = errno;
Packit 4511e4
		free(result_path);
Packit 4511e4
		errno = save_errno;
Packit 4511e4
		return -1;
Packit 4511e4
	}
Packit 4511e4
	free(result_path);
Packit 4511e4
	return 0;
Packit 4511e4
}