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