git

Форк
0
/
lockfile.c 
218 строк · 5.7 Кб
1
/*
2
 * Copyright (c) 2005, Junio C Hamano
3
 */
4

5
#include "git-compat-util.h"
6
#include "abspath.h"
7
#include "gettext.h"
8
#include "lockfile.h"
9

10
/*
11
 * path = absolute or relative path name
12
 *
13
 * Remove the last path name element from path (leaving the preceding
14
 * "/", if any).  If path is empty or the root directory ("/"), set
15
 * path to the empty string.
16
 */
17
static void trim_last_path_component(struct strbuf *path)
18
{
19
	int i = path->len;
20

21
	/* back up past trailing slashes, if any */
22
	while (i && path->buf[i - 1] == '/')
23
		i--;
24

25
	/*
26
	 * then go backwards until a slash, or the beginning of the
27
	 * string
28
	 */
29
	while (i && path->buf[i - 1] != '/')
30
		i--;
31

32
	strbuf_setlen(path, i);
33
}
34

35

36
/* We allow "recursive" symbolic links. Only within reason, though */
37
#define MAXDEPTH 5
38

39
/*
40
 * path contains a path that might be a symlink.
41
 *
42
 * If path is a symlink, attempt to overwrite it with a path to the
43
 * real file or directory (which may or may not exist), following a
44
 * chain of symlinks if necessary.  Otherwise, leave path unmodified.
45
 *
46
 * This is a best-effort routine.  If an error occurs, path will
47
 * either be left unmodified or will name a different symlink in a
48
 * symlink chain that started with the original path.
49
 */
50
static void resolve_symlink(struct strbuf *path)
51
{
52
	int depth = MAXDEPTH;
53
	static struct strbuf link = STRBUF_INIT;
54

55
	while (depth--) {
56
		if (strbuf_readlink(&link, path->buf, path->len) < 0)
57
			break;
58

59
		if (is_absolute_path(link.buf))
60
			/* absolute path simply replaces p */
61
			strbuf_reset(path);
62
		else
63
			/*
64
			 * link is a relative path, so replace the
65
			 * last element of p with it.
66
			 */
67
			trim_last_path_component(path);
68

69
		strbuf_addbuf(path, &link);
70
	}
71
	strbuf_reset(&link);
72
}
73

74
/* Make sure errno contains a meaningful value on error */
75
static int lock_file(struct lock_file *lk, const char *path, int flags,
76
		     int mode)
77
{
78
	struct strbuf filename = STRBUF_INIT;
79

80
	strbuf_addstr(&filename, path);
81
	if (!(flags & LOCK_NO_DEREF))
82
		resolve_symlink(&filename);
83

84
	strbuf_addstr(&filename, LOCK_SUFFIX);
85
	lk->tempfile = create_tempfile_mode(filename.buf, mode);
86
	strbuf_release(&filename);
87
	return lk->tempfile ? lk->tempfile->fd : -1;
88
}
89

90
/*
91
 * Constants defining the gaps between attempts to lock a file. The
92
 * first backoff period is approximately INITIAL_BACKOFF_MS
93
 * milliseconds. The longest backoff period is approximately
94
 * (BACKOFF_MAX_MULTIPLIER * INITIAL_BACKOFF_MS) milliseconds.
95
 */
96
#define INITIAL_BACKOFF_MS 1L
97
#define BACKOFF_MAX_MULTIPLIER 1000
98

99
/*
100
 * Try locking path, retrying with quadratic backoff for at least
101
 * timeout_ms milliseconds. If timeout_ms is 0, try locking the file
102
 * exactly once. If timeout_ms is -1, try indefinitely.
103
 */
104
static int lock_file_timeout(struct lock_file *lk, const char *path,
105
			     int flags, long timeout_ms, int mode)
106
{
107
	int n = 1;
108
	int multiplier = 1;
109
	long remaining_ms = 0;
110
	static int random_initialized = 0;
111

112
	if (timeout_ms == 0)
113
		return lock_file(lk, path, flags, mode);
114

115
	if (!random_initialized) {
116
		srand((unsigned int)getpid());
117
		random_initialized = 1;
118
	}
119

120
	if (timeout_ms > 0)
121
		remaining_ms = timeout_ms;
122

123
	while (1) {
124
		long backoff_ms, wait_ms;
125
		int fd;
126

127
		fd = lock_file(lk, path, flags, mode);
128

129
		if (fd >= 0)
130
			return fd; /* success */
131
		else if (errno != EEXIST)
132
			return -1; /* failure other than lock held */
133
		else if (timeout_ms > 0 && remaining_ms <= 0)
134
			return -1; /* failure due to timeout */
135

136
		backoff_ms = multiplier * INITIAL_BACKOFF_MS;
137
		/* back off for between 0.75*backoff_ms and 1.25*backoff_ms */
138
		wait_ms = (750 + rand() % 500) * backoff_ms / 1000;
139
		sleep_millisec(wait_ms);
140
		remaining_ms -= wait_ms;
141

142
		/* Recursion: (n+1)^2 = n^2 + 2n + 1 */
143
		multiplier += 2*n + 1;
144
		if (multiplier > BACKOFF_MAX_MULTIPLIER)
145
			multiplier = BACKOFF_MAX_MULTIPLIER;
146
		else
147
			n++;
148
	}
149
}
150

151
void unable_to_lock_message(const char *path, int err, struct strbuf *buf)
152
{
153
	if (err == EEXIST) {
154
		strbuf_addf(buf, _("Unable to create '%s.lock': %s.\n\n"
155
		    "Another git process seems to be running in this repository, e.g.\n"
156
		    "an editor opened by 'git commit'. Please make sure all processes\n"
157
		    "are terminated then try again. If it still fails, a git process\n"
158
		    "may have crashed in this repository earlier:\n"
159
		    "remove the file manually to continue."),
160
			    absolute_path(path), strerror(err));
161
	} else
162
		strbuf_addf(buf, _("Unable to create '%s.lock': %s"),
163
			    absolute_path(path), strerror(err));
164
}
165

166
NORETURN void unable_to_lock_die(const char *path, int err)
167
{
168
	struct strbuf buf = STRBUF_INIT;
169

170
	unable_to_lock_message(path, err, &buf);
171
	die("%s", buf.buf);
172
}
173

174
/* This should return a meaningful errno on failure */
175
int hold_lock_file_for_update_timeout_mode(struct lock_file *lk,
176
					   const char *path, int flags,
177
					   long timeout_ms, int mode)
178
{
179
	int fd = lock_file_timeout(lk, path, flags, timeout_ms, mode);
180
	if (fd < 0) {
181
		if (flags & LOCK_DIE_ON_ERROR)
182
			unable_to_lock_die(path, errno);
183
		if (flags & LOCK_REPORT_ON_ERROR) {
184
			struct strbuf buf = STRBUF_INIT;
185
			unable_to_lock_message(path, errno, &buf);
186
			error("%s", buf.buf);
187
			strbuf_release(&buf);
188
		}
189
	}
190
	return fd;
191
}
192

193
char *get_locked_file_path(struct lock_file *lk)
194
{
195
	struct strbuf ret = STRBUF_INIT;
196

197
	strbuf_addstr(&ret, get_tempfile_path(lk->tempfile));
198
	if (ret.len <= LOCK_SUFFIX_LEN ||
199
	    strcmp(ret.buf + ret.len - LOCK_SUFFIX_LEN, LOCK_SUFFIX))
200
		BUG("get_locked_file_path() called for malformed lock object");
201
	/* remove ".lock": */
202
	strbuf_setlen(&ret, ret.len - LOCK_SUFFIX_LEN);
203
	return strbuf_detach(&ret, NULL);
204
}
205

206
int commit_lock_file(struct lock_file *lk)
207
{
208
	char *result_path = get_locked_file_path(lk);
209

210
	if (commit_lock_file_to(lk, result_path)) {
211
		int save_errno = errno;
212
		free(result_path);
213
		errno = save_errno;
214
		return -1;
215
	}
216
	free(result_path);
217
	return 0;
218
}
219

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.