Use a strbuf in symlink
[git/mingw/4msysgit.git] / lockfile.c
bloba812d0605237dcbffc82afa6daa27a0269a18731
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);
32 /* mingw requires its own version of resolve_symlink to be use,
33 * including in lock_file below
35 #ifndef resolve_symlink
38 * p = absolute or relative path name
40 * Return a pointer into p showing the beginning of the last path name
41 * element. If p is empty or the root directory ("/"), just return p.
43 static char *last_path_elm(char *p)
45 /* r starts pointing to null at the end of the string */
46 char *r = strchr(p, '\0');
48 if (r == p)
49 return p; /* just return empty string */
51 r--; /* back up to last non-null character */
53 /* back up past trailing slashes, if any */
54 while (r > p && *r == '/')
55 r--;
58 * then go backwards until I hit a slash, or the beginning of
59 * the string
61 while (r > p && *(r-1) != '/')
62 r--;
63 return r;
67 /* We allow "recursive" symbolic links. Only within reason, though */
68 #define MAXDEPTH 5
71 * p = path that may be a symlink
72 * s = full size of p
74 * If p is a symlink, attempt to overwrite p with a path to the real
75 * file or directory (which may or may not exist), following a chain of
76 * symlinks if necessary. Otherwise, leave p unmodified.
78 * This is a best-effort routine. If an error occurs, p will either be
79 * left unmodified or will name a different symlink in a symlink chain
80 * that started with p's initial contents.
82 * Always returns p.
85 static char *resolve_symlink(char *p, size_t s)
87 int depth = MAXDEPTH;
89 while (depth--) {
90 char link[PATH_MAX];
91 int link_len = readlink(p, link, sizeof(link));
92 if (link_len < 0) {
93 /* not a symlink anymore */
94 return p;
96 else if (link_len < sizeof(link))
97 /* readlink() never null-terminates */
98 link[link_len] = '\0';
99 else {
100 warning("%s: symlink too long", p);
101 return p;
104 if (is_absolute_path(link)) {
105 /* absolute path simply replaces p */
106 if (link_len < s)
107 strcpy(p, link);
108 else {
109 warning("%s: symlink too long", p);
110 return p;
112 } else {
114 * link is a relative path, so I must replace the
115 * last element of p with it.
117 char *r = (char *)last_path_elm(p);
118 if (r - p + link_len < s)
119 strcpy(r, link);
120 else {
121 warning("%s: symlink too long", p);
122 return p;
126 return p;
129 #endif
131 static int lock_file(struct lock_file *lk, const char *path, int flags)
134 * subtract 5 from size to make sure there's room for adding
135 * ".lock" for the lock file name
137 static const size_t max_path_len = sizeof(lk->filename) - 5;
139 if (strlen(path) >= max_path_len)
140 return -1;
141 strcpy(lk->filename, path);
142 if (!(flags & LOCK_NODEREF))
143 resolve_symlink(lk->filename, max_path_len);
144 strcat(lk->filename, ".lock");
145 lk->fd = open(lk->filename, O_RDWR | O_CREAT | O_EXCL, 0666);
146 if (0 <= lk->fd) {
147 if (!lock_file_list) {
148 sigchain_push_common(remove_lock_file_on_signal);
149 atexit(remove_lock_file);
151 lk->owner = getpid();
152 if (!lk->on_list) {
153 lk->next = lock_file_list;
154 lock_file_list = lk;
155 lk->on_list = 1;
157 if (adjust_shared_perm(lk->filename))
158 return error("cannot fix permission bits on %s",
159 lk->filename);
161 else
162 lk->filename[0] = 0;
163 return lk->fd;
166 static char *unable_to_lock_message(const char *path, int err)
168 struct strbuf buf = STRBUF_INIT;
170 if (err == EEXIST) {
171 strbuf_addf(&buf, "Unable to create '%s.lock': %s.\n\n"
172 "If no other git process is currently running, this probably means a\n"
173 "git process crashed in this repository earlier. Make sure no other git\n"
174 "process is running and remove the file manually to continue.",
175 absolute_path(path), strerror(err));
176 } else
177 strbuf_addf(&buf, "Unable to create '%s.lock': %s",
178 absolute_path(path), strerror(err));
179 return strbuf_detach(&buf, NULL);
182 int unable_to_lock_error(const char *path, int err)
184 char *msg = unable_to_lock_message(path, err);
185 error("%s", msg);
186 free(msg);
187 return -1;
190 NORETURN void unable_to_lock_index_die(const char *path, int err)
192 die("%s", unable_to_lock_message(path, err));
195 int hold_lock_file_for_update(struct lock_file *lk, const char *path, int flags)
197 int fd = lock_file(lk, path, flags);
198 if (fd < 0 && (flags & LOCK_DIE_ON_ERROR))
199 unable_to_lock_index_die(path, errno);
200 return fd;
203 int hold_lock_file_for_append(struct lock_file *lk, const char *path, int flags)
205 int fd, orig_fd;
207 fd = lock_file(lk, path, flags);
208 if (fd < 0) {
209 if (flags & LOCK_DIE_ON_ERROR)
210 unable_to_lock_index_die(path, errno);
211 return fd;
214 orig_fd = open(path, O_RDONLY);
215 if (orig_fd < 0) {
216 if (errno != ENOENT) {
217 if (flags & LOCK_DIE_ON_ERROR)
218 die("cannot open '%s' for copying", path);
219 close(fd);
220 return error("cannot open '%s' for copying", path);
222 } else if (copy_fd(orig_fd, fd)) {
223 if (flags & LOCK_DIE_ON_ERROR)
224 exit(128);
225 close(fd);
226 return -1;
228 return fd;
231 int close_lock_file(struct lock_file *lk)
233 int fd = lk->fd;
234 lk->fd = -1;
235 return close(fd);
238 int commit_lock_file(struct lock_file *lk)
240 char result_file[PATH_MAX];
241 size_t i;
242 if (lk->fd >= 0 && close_lock_file(lk))
243 return -1;
244 strcpy(result_file, lk->filename);
245 i = strlen(result_file) - 5; /* .lock */
246 result_file[i] = 0;
247 if (rename(lk->filename, result_file))
248 return -1;
249 lk->filename[0] = 0;
250 return 0;
253 int hold_locked_index(struct lock_file *lk, int die_on_error)
255 return hold_lock_file_for_update(lk, get_index_file(),
256 die_on_error
257 ? LOCK_DIE_ON_ERROR
258 : 0);
261 void set_alternate_index_output(const char *name)
263 alternate_index_output = name;
266 int commit_locked_index(struct lock_file *lk)
268 if (alternate_index_output) {
269 if (lk->fd >= 0 && close_lock_file(lk))
270 return -1;
271 if (rename(lk->filename, alternate_index_output))
272 return -1;
273 lk->filename[0] = 0;
274 return 0;
276 else
277 return commit_lock_file(lk);
280 void rollback_lock_file(struct lock_file *lk)
282 if (lk->filename[0]) {
283 if (lk->fd >= 0)
284 close(lk->fd);
285 unlink_or_warn(lk->filename);
287 lk->filename[0] = 0;