]> git.ipfire.org Git - thirdparty/git.git/blob - lockfile.c
chain kill signals for cleanup functions
[thirdparty/git.git] / lockfile.c
1 /*
2 * Copyright (c) 2005, Junio C Hamano
3 */
4 #include "cache.h"
5 #include "sigchain.h"
6
7 static struct lock_file *lock_file_list;
8 static const char *alternate_index_output;
9
10 static void remove_lock_file(void)
11 {
12 pid_t me = getpid();
13
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(lock_file_list->filename);
20 }
21 lock_file_list = lock_file_list->next;
22 }
23 }
24
25 static void remove_lock_file_on_signal(int signo)
26 {
27 remove_lock_file();
28 sigchain_pop(signo);
29 raise(signo);
30 }
31
32 /*
33 * p = absolute or relative path name
34 *
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.
37 */
38 static char *last_path_elm(char *p)
39 {
40 /* r starts pointing to null at the end of the string */
41 char *r = strchr(p, '\0');
42
43 if (r == p)
44 return p; /* just return empty string */
45
46 r--; /* back up to last non-null character */
47
48 /* back up past trailing slashes, if any */
49 while (r > p && *r == '/')
50 r--;
51
52 /*
53 * then go backwards until I hit a slash, or the beginning of
54 * the string
55 */
56 while (r > p && *(r-1) != '/')
57 r--;
58 return r;
59 }
60
61
62 /* We allow "recursive" symbolic links. Only within reason, though */
63 #define MAXDEPTH 5
64
65 /*
66 * p = path that may be a symlink
67 * s = full size of p
68 *
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.
72 *
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.
76 *
77 * Always returns p.
78 */
79
80 static char *resolve_symlink(char *p, size_t s)
81 {
82 int depth = MAXDEPTH;
83
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;
90 }
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;
97 }
98
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;
106 }
107 } else {
108 /*
109 * link is a relative path, so I must replace the
110 * last element of p with it.
111 */
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;
118 }
119 }
120 }
121 return p;
122 }
123
124
125 static int lock_file(struct lock_file *lk, const char *path, int flags)
126 {
127 if (strlen(path) >= sizeof(lk->filename))
128 return -1;
129 strcpy(lk->filename, path);
130 /*
131 * subtract 5 from size to make sure there's room for adding
132 * ".lock" for the lock file name
133 */
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(SIGINT, remove_lock_file_on_signal);
141 sigchain_push(SIGHUP, remove_lock_file_on_signal);
142 sigchain_push(SIGTERM, remove_lock_file_on_signal);
143 sigchain_push(SIGQUIT, remove_lock_file_on_signal);
144 sigchain_push(SIGPIPE, remove_lock_file_on_signal);
145 atexit(remove_lock_file);
146 }
147 lk->owner = getpid();
148 if (!lk->on_list) {
149 lk->next = lock_file_list;
150 lock_file_list = lk;
151 lk->on_list = 1;
152 }
153 if (adjust_shared_perm(lk->filename))
154 return error("cannot fix permission bits on %s",
155 lk->filename);
156 }
157 else
158 lk->filename[0] = 0;
159 return lk->fd;
160 }
161
162 int hold_lock_file_for_update(struct lock_file *lk, const char *path, int flags)
163 {
164 int fd = lock_file(lk, path, flags);
165 if (fd < 0 && (flags & LOCK_DIE_ON_ERROR))
166 die("unable to create '%s.lock': %s", path, strerror(errno));
167 return fd;
168 }
169
170 int hold_lock_file_for_append(struct lock_file *lk, const char *path, int flags)
171 {
172 int fd, orig_fd;
173
174 fd = lock_file(lk, path, flags);
175 if (fd < 0) {
176 if (flags & LOCK_DIE_ON_ERROR)
177 die("unable to create '%s.lock': %s", path, strerror(errno));
178 return fd;
179 }
180
181 orig_fd = open(path, O_RDONLY);
182 if (orig_fd < 0) {
183 if (errno != ENOENT) {
184 if (flags & LOCK_DIE_ON_ERROR)
185 die("cannot open '%s' for copying", path);
186 close(fd);
187 return error("cannot open '%s' for copying", path);
188 }
189 } else if (copy_fd(orig_fd, fd)) {
190 if (flags & LOCK_DIE_ON_ERROR)
191 exit(128);
192 close(fd);
193 return -1;
194 }
195 return fd;
196 }
197
198 int close_lock_file(struct lock_file *lk)
199 {
200 int fd = lk->fd;
201 lk->fd = -1;
202 return close(fd);
203 }
204
205 int commit_lock_file(struct lock_file *lk)
206 {
207 char result_file[PATH_MAX];
208 size_t i;
209 if (lk->fd >= 0 && close_lock_file(lk))
210 return -1;
211 strcpy(result_file, lk->filename);
212 i = strlen(result_file) - 5; /* .lock */
213 result_file[i] = 0;
214 if (rename(lk->filename, result_file))
215 return -1;
216 lk->filename[0] = 0;
217 return 0;
218 }
219
220 int hold_locked_index(struct lock_file *lk, int die_on_error)
221 {
222 return hold_lock_file_for_update(lk, get_index_file(),
223 die_on_error
224 ? LOCK_DIE_ON_ERROR
225 : 0);
226 }
227
228 void set_alternate_index_output(const char *name)
229 {
230 alternate_index_output = name;
231 }
232
233 int commit_locked_index(struct lock_file *lk)
234 {
235 if (alternate_index_output) {
236 if (lk->fd >= 0 && close_lock_file(lk))
237 return -1;
238 if (rename(lk->filename, alternate_index_output))
239 return -1;
240 lk->filename[0] = 0;
241 return 0;
242 }
243 else
244 return commit_lock_file(lk);
245 }
246
247 void rollback_lock_file(struct lock_file *lk)
248 {
249 if (lk->filename[0]) {
250 if (lk->fd >= 0)
251 close(lk->fd);
252 unlink(lk->filename);
253 }
254 lk->filename[0] = 0;
255 }