Merge branch 'tb/open-midx-bitmap-fallback'
[alt-git.git] / lockfile.c
blob1d5ed0168287464e24d60e30b0cfbce181676535
1 /*
2 * Copyright (c) 2005, Junio C Hamano
3 */
5 #include "git-compat-util.h"
6 #include "abspath.h"
7 #include "gettext.h"
8 #include "lockfile.h"
11 * path = absolute or relative path name
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.
17 static void trim_last_path_component(struct strbuf *path)
19 int i = path->len;
21 /* back up past trailing slashes, if any */
22 while (i && path->buf[i - 1] == '/')
23 i--;
26 * then go backwards until a slash, or the beginning of the
27 * string
29 while (i && path->buf[i - 1] != '/')
30 i--;
32 strbuf_setlen(path, i);
36 /* We allow "recursive" symbolic links. Only within reason, though */
37 #define MAXDEPTH 5
40 * path contains a path that might be a symlink.
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.
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.
50 static void resolve_symlink(struct strbuf *path)
52 int depth = MAXDEPTH;
53 static struct strbuf link = STRBUF_INIT;
55 while (depth--) {
56 if (strbuf_readlink(&link, path->buf, path->len) < 0)
57 break;
59 if (is_absolute_path(link.buf))
60 /* absolute path simply replaces p */
61 strbuf_reset(path);
62 else
64 * link is a relative path, so replace the
65 * last element of p with it.
67 trim_last_path_component(path);
69 strbuf_addbuf(path, &link);
71 strbuf_reset(&link);
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)
78 struct strbuf filename = STRBUF_INIT;
80 strbuf_addstr(&filename, path);
81 if (!(flags & LOCK_NO_DEREF))
82 resolve_symlink(&filename);
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;
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.
96 #define INITIAL_BACKOFF_MS 1L
97 #define BACKOFF_MAX_MULTIPLIER 1000
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.
104 static int lock_file_timeout(struct lock_file *lk, const char *path,
105 int flags, long timeout_ms, int mode)
107 int n = 1;
108 int multiplier = 1;
109 long remaining_ms = 0;
110 static int random_initialized = 0;
112 if (timeout_ms == 0)
113 return lock_file(lk, path, flags, mode);
115 if (!random_initialized) {
116 srand((unsigned int)getpid());
117 random_initialized = 1;
120 if (timeout_ms > 0)
121 remaining_ms = timeout_ms;
123 while (1) {
124 long backoff_ms, wait_ms;
125 int fd;
127 fd = lock_file(lk, path, flags, mode);
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 */
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;
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++;
151 void unable_to_lock_message(const char *path, int err, struct strbuf *buf)
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));
166 NORETURN void unable_to_lock_die(const char *path, int err)
168 struct strbuf buf = STRBUF_INIT;
170 unable_to_lock_message(path, err, &buf);
171 die("%s", buf.buf);
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)
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);
190 return fd;
193 char *get_locked_file_path(struct lock_file *lk)
195 struct strbuf ret = STRBUF_INIT;
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);
206 int commit_lock_file(struct lock_file *lk)
208 char *result_path = get_locked_file_path(lk);
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;
216 free(result_path);
217 return 0;