Merge branch 'ar/unlink-err'
[git/gitweb.git] / lockfile.c
blobeb931eded5a6ed20f1d80dadf08cbb8009d85767
1 /*
2 * Copyright (c) 2005, Junio C Hamano
3 */
4 #include "cache.h"
5 #include "sigchain.h"
7 static struct lock_file *lock_file_list;
8 static const char *alternate_index_output;
10 static void remove_lock_file(void)
12 pid_t me = getpid();
14 while (lock_file_list) {
15 if (lock_file_list->owner == me &&
16 lock_file_list->filename[0]) {
17 if (lock_file_list->fd >= 0)
18 close(lock_file_list->fd);
19 unlink_or_warn(lock_file_list->filename);
21 lock_file_list = lock_file_list->next;
25 static void remove_lock_file_on_signal(int signo)
27 remove_lock_file();
28 sigchain_pop(signo);
29 raise(signo);
33 * p = absolute or relative path name
35 * Return a pointer into p showing the beginning of the last path name
36 * element. If p is empty or the root directory ("/"), just return p.
38 static char *last_path_elm(char *p)
40 /* r starts pointing to null at the end of the string */
41 char *r = strchr(p, '\0');
43 if (r == p)
44 return p; /* just return empty string */
46 r--; /* back up to last non-null character */
48 /* back up past trailing slashes, if any */
49 while (r > p && *r == '/')
50 r--;
53 * then go backwards until I hit a slash, or the beginning of
54 * the string
56 while (r > p && *(r-1) != '/')
57 r--;
58 return r;
62 /* We allow "recursive" symbolic links. Only within reason, though */
63 #define MAXDEPTH 5
66 * p = path that may be a symlink
67 * s = full size of p
69 * If p is a symlink, attempt to overwrite p with a path to the real
70 * file or directory (which may or may not exist), following a chain of
71 * symlinks if necessary. Otherwise, leave p unmodified.
73 * This is a best-effort routine. If an error occurs, p will either be
74 * left unmodified or will name a different symlink in a symlink chain
75 * that started with p's initial contents.
77 * Always returns p.
80 static char *resolve_symlink(char *p, size_t s)
82 int depth = MAXDEPTH;
84 while (depth--) {
85 char link[PATH_MAX];
86 int link_len = readlink(p, link, sizeof(link));
87 if (link_len < 0) {
88 /* not a symlink anymore */
89 return p;
91 else if (link_len < sizeof(link))
92 /* readlink() never null-terminates */
93 link[link_len] = '\0';
94 else {
95 warning("%s: symlink too long", p);
96 return p;
99 if (is_absolute_path(link)) {
100 /* absolute path simply replaces p */
101 if (link_len < s)
102 strcpy(p, link);
103 else {
104 warning("%s: symlink too long", p);
105 return p;
107 } else {
109 * link is a relative path, so I must replace the
110 * last element of p with it.
112 char *r = (char *)last_path_elm(p);
113 if (r - p + link_len < s)
114 strcpy(r, link);
115 else {
116 warning("%s: symlink too long", p);
117 return p;
121 return p;
125 static int lock_file(struct lock_file *lk, const char *path, int flags)
127 if (strlen(path) >= sizeof(lk->filename))
128 return -1;
129 strcpy(lk->filename, path);
131 * subtract 5 from size to make sure there's room for adding
132 * ".lock" for the lock file name
134 if (!(flags & LOCK_NODEREF))
135 resolve_symlink(lk->filename, sizeof(lk->filename)-5);
136 strcat(lk->filename, ".lock");
137 lk->fd = open(lk->filename, O_RDWR | O_CREAT | O_EXCL, 0666);
138 if (0 <= lk->fd) {
139 if (!lock_file_list) {
140 sigchain_push_common(remove_lock_file_on_signal);
141 atexit(remove_lock_file);
143 lk->owner = getpid();
144 if (!lk->on_list) {
145 lk->next = lock_file_list;
146 lock_file_list = lk;
147 lk->on_list = 1;
149 if (adjust_shared_perm(lk->filename))
150 return error("cannot fix permission bits on %s",
151 lk->filename);
153 else
154 lk->filename[0] = 0;
155 return lk->fd;
159 NORETURN void unable_to_lock_index_die(const char *path, int err)
161 if (err == EEXIST) {
162 die("Unable to create '%s.lock': %s.\n\n"
163 "If no other git process is currently running, this probably means a\n"
164 "git process crashed in this repository earlier. Make sure no other git\n"
165 "process is running and remove the file manually to continue.",
166 path, strerror(err));
167 } else {
168 die("Unable to create '%s.lock': %s", path, strerror(err));
172 int hold_lock_file_for_update(struct lock_file *lk, const char *path, int flags)
174 int fd = lock_file(lk, path, flags);
175 if (fd < 0 && (flags & LOCK_DIE_ON_ERROR))
176 unable_to_lock_index_die(path, errno);
177 return fd;
180 int hold_lock_file_for_append(struct lock_file *lk, const char *path, int flags)
182 int fd, orig_fd;
184 fd = lock_file(lk, path, flags);
185 if (fd < 0) {
186 if (flags & LOCK_DIE_ON_ERROR)
187 unable_to_lock_index_die(path, errno);
188 return fd;
191 orig_fd = open(path, O_RDONLY);
192 if (orig_fd < 0) {
193 if (errno != ENOENT) {
194 if (flags & LOCK_DIE_ON_ERROR)
195 die("cannot open '%s' for copying", path);
196 close(fd);
197 return error("cannot open '%s' for copying", path);
199 } else if (copy_fd(orig_fd, fd)) {
200 if (flags & LOCK_DIE_ON_ERROR)
201 exit(128);
202 close(fd);
203 return -1;
205 return fd;
208 int close_lock_file(struct lock_file *lk)
210 int fd = lk->fd;
211 lk->fd = -1;
212 return close(fd);
215 int commit_lock_file(struct lock_file *lk)
217 char result_file[PATH_MAX];
218 size_t i;
219 if (lk->fd >= 0 && close_lock_file(lk))
220 return -1;
221 strcpy(result_file, lk->filename);
222 i = strlen(result_file) - 5; /* .lock */
223 result_file[i] = 0;
224 if (rename(lk->filename, result_file))
225 return -1;
226 lk->filename[0] = 0;
227 return 0;
230 int hold_locked_index(struct lock_file *lk, int die_on_error)
232 return hold_lock_file_for_update(lk, get_index_file(),
233 die_on_error
234 ? LOCK_DIE_ON_ERROR
235 : 0);
238 void set_alternate_index_output(const char *name)
240 alternate_index_output = name;
243 int commit_locked_index(struct lock_file *lk)
245 if (alternate_index_output) {
246 if (lk->fd >= 0 && close_lock_file(lk))
247 return -1;
248 if (rename(lk->filename, alternate_index_output))
249 return -1;
250 lk->filename[0] = 0;
251 return 0;
253 else
254 return commit_lock_file(lk);
257 void rollback_lock_file(struct lock_file *lk)
259 if (lk->filename[0]) {
260 if (lk->fd >= 0)
261 close(lk->fd);
262 unlink_or_warn(lk->filename);
264 lk->filename[0] = 0;