lockfile.h: extract new header file for the functions in lockfile.c
[git/git.git] / lockfile.c
1 /*
2 * Copyright (c) 2005, Junio C Hamano
3 */
4 #include "cache.h"
5 #include "lockfile.h"
6 #include "sigchain.h"
7
8 static struct lock_file *volatile lock_file_list;
9
10 static void remove_lock_files(void)
11 {
12 pid_t me = getpid();
13
14 while (lock_file_list) {
15 if (lock_file_list->owner == me)
16 rollback_lock_file(lock_file_list);
17 lock_file_list = lock_file_list->next;
18 }
19 }
20
21 static void remove_lock_files_on_signal(int signo)
22 {
23 remove_lock_files();
24 sigchain_pop(signo);
25 raise(signo);
26 }
27
28 /*
29 * path = absolute or relative path name
30 *
31 * Remove the last path name element from path (leaving the preceding
32 * "/", if any). If path is empty or the root directory ("/"), set
33 * path to the empty string.
34 */
35 static void trim_last_path_component(struct strbuf *path)
36 {
37 int i = path->len;
38
39 /* back up past trailing slashes, if any */
40 while (i && path->buf[i - 1] == '/')
41 i--;
42
43 /*
44 * then go backwards until a slash, or the beginning of the
45 * string
46 */
47 while (i && path->buf[i - 1] != '/')
48 i--;
49
50 strbuf_setlen(path, i);
51 }
52
53
54 /* We allow "recursive" symbolic links. Only within reason, though */
55 #define MAXDEPTH 5
56
57 /*
58 * path contains a path that might be a symlink.
59 *
60 * If path is a symlink, attempt to overwrite it with a path to the
61 * real file or directory (which may or may not exist), following a
62 * chain of symlinks if necessary. Otherwise, leave path unmodified.
63 *
64 * This is a best-effort routine. If an error occurs, path will
65 * either be left unmodified or will name a different symlink in a
66 * symlink chain that started with the original path.
67 */
68 static void resolve_symlink(struct strbuf *path)
69 {
70 int depth = MAXDEPTH;
71 static struct strbuf link = STRBUF_INIT;
72
73 while (depth--) {
74 if (strbuf_readlink(&link, path->buf, path->len) < 0)
75 break;
76
77 if (is_absolute_path(link.buf))
78 /* absolute path simply replaces p */
79 strbuf_reset(path);
80 else
81 /*
82 * link is a relative path, so replace the
83 * last element of p with it.
84 */
85 trim_last_path_component(path);
86
87 strbuf_addbuf(path, &link);
88 }
89 strbuf_reset(&link);
90 }
91
92 /* Make sure errno contains a meaningful value on error */
93 static int lock_file(struct lock_file *lk, const char *path, int flags)
94 {
95 size_t pathlen = strlen(path);
96
97 if (!lock_file_list) {
98 /* One-time initialization */
99 sigchain_push_common(remove_lock_files_on_signal);
100 atexit(remove_lock_files);
101 }
102
103 if (lk->active)
104 die("BUG: cannot lock_file(\"%s\") using active struct lock_file",
105 path);
106 if (!lk->on_list) {
107 /* Initialize *lk and add it to lock_file_list: */
108 lk->fd = -1;
109 lk->active = 0;
110 lk->owner = 0;
111 strbuf_init(&lk->filename, pathlen + LOCK_SUFFIX_LEN);
112 lk->next = lock_file_list;
113 lock_file_list = lk;
114 lk->on_list = 1;
115 } else if (lk->filename.len) {
116 /* This shouldn't happen, but better safe than sorry. */
117 die("BUG: lock_file(\"%s\") called with improperly-reset lock_file object",
118 path);
119 }
120
121 strbuf_add(&lk->filename, path, pathlen);
122 if (!(flags & LOCK_NO_DEREF))
123 resolve_symlink(&lk->filename);
124 strbuf_addstr(&lk->filename, LOCK_SUFFIX);
125 lk->fd = open(lk->filename.buf, O_RDWR | O_CREAT | O_EXCL, 0666);
126 if (lk->fd < 0) {
127 strbuf_reset(&lk->filename);
128 return -1;
129 }
130 lk->owner = getpid();
131 lk->active = 1;
132 if (adjust_shared_perm(lk->filename.buf)) {
133 int save_errno = errno;
134 error("cannot fix permission bits on %s", lk->filename.buf);
135 rollback_lock_file(lk);
136 errno = save_errno;
137 return -1;
138 }
139 return lk->fd;
140 }
141
142 void unable_to_lock_message(const char *path, int err, struct strbuf *buf)
143 {
144 if (err == EEXIST) {
145 strbuf_addf(buf, "Unable to create '%s.lock': %s.\n\n"
146 "If no other git process is currently running, this probably means a\n"
147 "git process crashed in this repository earlier. Make sure no other git\n"
148 "process is running and remove the file manually to continue.",
149 absolute_path(path), strerror(err));
150 } else
151 strbuf_addf(buf, "Unable to create '%s.lock': %s",
152 absolute_path(path), strerror(err));
153 }
154
155 int unable_to_lock_error(const char *path, int err)
156 {
157 struct strbuf buf = STRBUF_INIT;
158
159 unable_to_lock_message(path, err, &buf);
160 error("%s", buf.buf);
161 strbuf_release(&buf);
162 return -1;
163 }
164
165 NORETURN void unable_to_lock_die(const char *path, int err)
166 {
167 struct strbuf buf = STRBUF_INIT;
168
169 unable_to_lock_message(path, err, &buf);
170 die("%s", buf.buf);
171 }
172
173 /* This should return a meaningful errno on failure */
174 int hold_lock_file_for_update(struct lock_file *lk, const char *path, int flags)
175 {
176 int fd = lock_file(lk, path, flags);
177 if (fd < 0 && (flags & LOCK_DIE_ON_ERROR))
178 unable_to_lock_die(path, errno);
179 return fd;
180 }
181
182 int hold_lock_file_for_append(struct lock_file *lk, const char *path, int flags)
183 {
184 int fd, orig_fd;
185
186 fd = lock_file(lk, path, flags);
187 if (fd < 0) {
188 if (flags & LOCK_DIE_ON_ERROR)
189 unable_to_lock_die(path, errno);
190 return fd;
191 }
192
193 orig_fd = open(path, O_RDONLY);
194 if (orig_fd < 0) {
195 if (errno != ENOENT) {
196 int save_errno = errno;
197
198 if (flags & LOCK_DIE_ON_ERROR)
199 die("cannot open '%s' for copying", path);
200 rollback_lock_file(lk);
201 error("cannot open '%s' for copying", path);
202 errno = save_errno;
203 return -1;
204 }
205 } else if (copy_fd(orig_fd, fd)) {
206 int save_errno = errno;
207
208 if (flags & LOCK_DIE_ON_ERROR)
209 exit(128);
210 rollback_lock_file(lk);
211 errno = save_errno;
212 return -1;
213 }
214 return fd;
215 }
216
217 char *get_locked_file_path(struct lock_file *lk)
218 {
219 if (!lk->active)
220 die("BUG: get_locked_file_path() called for unlocked object");
221 if (lk->filename.len <= LOCK_SUFFIX_LEN)
222 die("BUG: get_locked_file_path() called for malformed lock object");
223 return xmemdupz(lk->filename.buf, lk->filename.len - LOCK_SUFFIX_LEN);
224 }
225
226 int close_lock_file(struct lock_file *lk)
227 {
228 int fd = lk->fd;
229
230 if (fd < 0)
231 return 0;
232
233 lk->fd = -1;
234 if (close(fd)) {
235 int save_errno = errno;
236 rollback_lock_file(lk);
237 errno = save_errno;
238 return -1;
239 }
240 return 0;
241 }
242
243 int reopen_lock_file(struct lock_file *lk)
244 {
245 if (0 <= lk->fd)
246 die(_("BUG: reopen a lockfile that is still open"));
247 if (!lk->active)
248 die(_("BUG: reopen a lockfile that has been committed"));
249 lk->fd = open(lk->filename.buf, O_WRONLY);
250 return lk->fd;
251 }
252
253 int commit_lock_file_to(struct lock_file *lk, const char *path)
254 {
255 if (!lk->active)
256 die("BUG: attempt to commit unlocked object to \"%s\"", path);
257
258 if (close_lock_file(lk))
259 return -1;
260
261 if (rename(lk->filename.buf, path)) {
262 int save_errno = errno;
263 rollback_lock_file(lk);
264 errno = save_errno;
265 return -1;
266 }
267
268 lk->active = 0;
269 strbuf_reset(&lk->filename);
270 return 0;
271 }
272
273 int commit_lock_file(struct lock_file *lk)
274 {
275 static struct strbuf result_file = STRBUF_INIT;
276 int err;
277
278 if (!lk->active)
279 die("BUG: attempt to commit unlocked object");
280
281 if (lk->filename.len <= LOCK_SUFFIX_LEN ||
282 strcmp(lk->filename.buf + lk->filename.len - LOCK_SUFFIX_LEN, LOCK_SUFFIX))
283 die("BUG: lockfile filename corrupt");
284
285 /* remove ".lock": */
286 strbuf_add(&result_file, lk->filename.buf,
287 lk->filename.len - LOCK_SUFFIX_LEN);
288 err = commit_lock_file_to(lk, result_file.buf);
289 strbuf_reset(&result_file);
290 return err;
291 }
292
293 void rollback_lock_file(struct lock_file *lk)
294 {
295 if (!lk->active)
296 return;
297
298 if (!close_lock_file(lk)) {
299 unlink_or_warn(lk->filename.buf);
300 lk->active = 0;
301 strbuf_reset(&lk->filename);
302 }
303 }