Merge branch 'maint-1.6.0' into maint-1.6.1
[alt-git.git] / lockfile.c
blob8e556ff8c9671864db44dc8b6f4a861bd35142a6
1 /*
2 * Copyright (c) 2005, Junio C Hamano
3 */
4 #include "cache.h"
6 static struct lock_file *lock_file_list;
7 static const char *alternate_index_output;
9 static void remove_lock_file(void)
11 pid_t me = getpid();
13 while (lock_file_list) {
14 if (lock_file_list->owner == me &&
15 lock_file_list->filename[0]) {
16 if (lock_file_list->fd >= 0)
17 close(lock_file_list->fd);
18 unlink(lock_file_list->filename);
20 lock_file_list = lock_file_list->next;
24 static void remove_lock_file_on_signal(int signo)
26 remove_lock_file();
27 signal(signo, SIG_DFL);
28 raise(signo);
32 * p = absolute or relative path name
34 * Return a pointer into p showing the beginning of the last path name
35 * element. If p is empty or the root directory ("/"), just return p.
37 static char *last_path_elm(char *p)
39 /* r starts pointing to null at the end of the string */
40 char *r = strchr(p, '\0');
42 if (r == p)
43 return p; /* just return empty string */
45 r--; /* back up to last non-null character */
47 /* back up past trailing slashes, if any */
48 while (r > p && *r == '/')
49 r--;
52 * then go backwards until I hit a slash, or the beginning of
53 * the string
55 while (r > p && *(r-1) != '/')
56 r--;
57 return r;
61 /* We allow "recursive" symbolic links. Only within reason, though */
62 #define MAXDEPTH 5
65 * p = path that may be a symlink
66 * s = full size of p
68 * If p is a symlink, attempt to overwrite p with a path to the real
69 * file or directory (which may or may not exist), following a chain of
70 * symlinks if necessary. Otherwise, leave p unmodified.
72 * This is a best-effort routine. If an error occurs, p will either be
73 * left unmodified or will name a different symlink in a symlink chain
74 * that started with p's initial contents.
76 * Always returns p.
79 static char *resolve_symlink(char *p, size_t s)
81 int depth = MAXDEPTH;
83 while (depth--) {
84 char link[PATH_MAX];
85 int link_len = readlink(p, link, sizeof(link));
86 if (link_len < 0) {
87 /* not a symlink anymore */
88 return p;
90 else if (link_len < sizeof(link))
91 /* readlink() never null-terminates */
92 link[link_len] = '\0';
93 else {
94 warning("%s: symlink too long", p);
95 return p;
98 if (is_absolute_path(link)) {
99 /* absolute path simply replaces p */
100 if (link_len < s)
101 strcpy(p, link);
102 else {
103 warning("%s: symlink too long", p);
104 return p;
106 } else {
108 * link is a relative path, so I must replace the
109 * last element of p with it.
111 char *r = (char*)last_path_elm(p);
112 if (r - p + link_len < s)
113 strcpy(r, link);
114 else {
115 warning("%s: symlink too long", p);
116 return p;
120 return p;
124 static int lock_file(struct lock_file *lk, const char *path, int flags)
126 if (strlen(path) >= sizeof(lk->filename))
127 return -1;
128 strcpy(lk->filename, path);
130 * subtract 5 from size to make sure there's room for adding
131 * ".lock" for the lock file name
133 if (!(flags & LOCK_NODEREF))
134 resolve_symlink(lk->filename, sizeof(lk->filename)-5);
135 strcat(lk->filename, ".lock");
136 lk->fd = open(lk->filename, O_RDWR | O_CREAT | O_EXCL, 0666);
137 if (0 <= lk->fd) {
138 if (!lock_file_list) {
139 signal(SIGINT, remove_lock_file_on_signal);
140 signal(SIGHUP, remove_lock_file_on_signal);
141 signal(SIGTERM, remove_lock_file_on_signal);
142 signal(SIGQUIT, remove_lock_file_on_signal);
143 signal(SIGPIPE, remove_lock_file_on_signal);
144 atexit(remove_lock_file);
146 lk->owner = getpid();
147 if (!lk->on_list) {
148 lk->next = lock_file_list;
149 lock_file_list = lk;
150 lk->on_list = 1;
152 if (adjust_shared_perm(lk->filename))
153 return error("cannot fix permission bits on %s",
154 lk->filename);
156 else
157 lk->filename[0] = 0;
158 return lk->fd;
162 NORETURN void unable_to_lock_index_die(const char *path, int err)
164 if (errno == EEXIST) {
165 die("Unable to create '%s.lock': %s.\n\n"
166 "If no other git process is currently running, this probably means a\n"
167 "git process crashed in this repository earlier. Make sure no other git\n"
168 "process is running and remove the file manually to continue.",
169 path, strerror(err));
170 } else {
171 die("Unable to create '%s.lock': %s", path, strerror(err));
175 int hold_lock_file_for_update(struct lock_file *lk, const char *path, int flags)
177 int fd = lock_file(lk, path, flags);
178 if (fd < 0 && (flags & LOCK_DIE_ON_ERROR))
179 unable_to_lock_index_die(path, errno);
180 return fd;
183 int hold_lock_file_for_append(struct lock_file *lk, const char *path, int flags)
185 int fd, orig_fd;
187 fd = lock_file(lk, path, flags);
188 if (fd < 0) {
189 if (flags & LOCK_DIE_ON_ERROR)
190 die("unable to create '%s.lock': %s", path, strerror(errno));
191 return fd;
194 orig_fd = open(path, O_RDONLY);
195 if (orig_fd < 0) {
196 if (errno != ENOENT) {
197 if (flags & LOCK_DIE_ON_ERROR)
198 die("cannot open '%s' for copying", path);
199 close(fd);
200 return error("cannot open '%s' for copying", path);
202 } else if (copy_fd(orig_fd, fd)) {
203 if (flags & LOCK_DIE_ON_ERROR)
204 exit(128);
205 close(fd);
206 return -1;
208 return fd;
211 int close_lock_file(struct lock_file *lk)
213 int fd = lk->fd;
214 lk->fd = -1;
215 return close(fd);
218 int commit_lock_file(struct lock_file *lk)
220 char result_file[PATH_MAX];
221 size_t i;
222 if (lk->fd >= 0 && close_lock_file(lk))
223 return -1;
224 strcpy(result_file, lk->filename);
225 i = strlen(result_file) - 5; /* .lock */
226 result_file[i] = 0;
227 if (rename(lk->filename, result_file))
228 return -1;
229 lk->filename[0] = 0;
230 return 0;
233 int hold_locked_index(struct lock_file *lk, int die_on_error)
235 return hold_lock_file_for_update(lk, get_index_file(),
236 die_on_error
237 ? LOCK_DIE_ON_ERROR
238 : 0);
241 void set_alternate_index_output(const char *name)
243 alternate_index_output = name;
246 int commit_locked_index(struct lock_file *lk)
248 if (alternate_index_output) {
249 if (lk->fd >= 0 && close_lock_file(lk))
250 return -1;
251 if (rename(lk->filename, alternate_index_output))
252 return -1;
253 lk->filename[0] = 0;
254 return 0;
256 else
257 return commit_lock_file(lk);
260 void rollback_lock_file(struct lock_file *lk)
262 if (lk->filename[0]) {
263 if (lk->fd >= 0)
264 close(lk->fd);
265 unlink(lk->filename);
267 lk->filename[0] = 0;