Document lockfile API
[git/git.git] / lockfile.c
1 /*
2 * Copyright (c) 2005, Junio C Hamano
3 */
4 #include "cache.h"
5
6 static struct lock_file *lock_file_list;
7 static const char *alternate_index_output;
8
9 static void remove_lock_file(void)
10 {
11 pid_t me = getpid();
12
13 while (lock_file_list) {
14 if (lock_file_list->owner == me &&
15 lock_file_list->filename[0]) {
16 close(lock_file_list->fd);
17 unlink(lock_file_list->filename);
18 }
19 lock_file_list = lock_file_list->next;
20 }
21 }
22
23 static void remove_lock_file_on_signal(int signo)
24 {
25 remove_lock_file();
26 signal(SIGINT, SIG_DFL);
27 raise(signo);
28 }
29
30 /*
31 * p = absolute or relative path name
32 *
33 * Return a pointer into p showing the beginning of the last path name
34 * element. If p is empty or the root directory ("/"), just return p.
35 */
36 static char *last_path_elm(char *p)
37 {
38 /* r starts pointing to null at the end of the string */
39 char *r = strchr(p, '\0');
40
41 if (r == p)
42 return p; /* just return empty string */
43
44 r--; /* back up to last non-null character */
45
46 /* back up past trailing slashes, if any */
47 while (r > p && *r == '/')
48 r--;
49
50 /*
51 * then go backwards until I hit a slash, or the beginning of
52 * the string
53 */
54 while (r > p && *(r-1) != '/')
55 r--;
56 return r;
57 }
58
59
60 /* We allow "recursive" symbolic links. Only within reason, though */
61 #define MAXDEPTH 5
62
63 /*
64 * p = path that may be a symlink
65 * s = full size of p
66 *
67 * If p is a symlink, attempt to overwrite p with a path to the real
68 * file or directory (which may or may not exist), following a chain of
69 * symlinks if necessary. Otherwise, leave p unmodified.
70 *
71 * This is a best-effort routine. If an error occurs, p will either be
72 * left unmodified or will name a different symlink in a symlink chain
73 * that started with p's initial contents.
74 *
75 * Always returns p.
76 */
77
78 static char *resolve_symlink(char *p, size_t s)
79 {
80 int depth = MAXDEPTH;
81
82 while (depth--) {
83 char link[PATH_MAX];
84 int link_len = readlink(p, link, sizeof(link));
85 if (link_len < 0) {
86 /* not a symlink anymore */
87 return p;
88 }
89 else if (link_len < sizeof(link))
90 /* readlink() never null-terminates */
91 link[link_len] = '\0';
92 else {
93 warning("%s: symlink too long", p);
94 return p;
95 }
96
97 if (is_absolute_path(link)) {
98 /* absolute path simply replaces p */
99 if (link_len < s)
100 strcpy(p, link);
101 else {
102 warning("%s: symlink too long", p);
103 return p;
104 }
105 } else {
106 /*
107 * link is a relative path, so I must replace the
108 * last element of p with it.
109 */
110 char *r = (char*)last_path_elm(p);
111 if (r - p + link_len < s)
112 strcpy(r, link);
113 else {
114 warning("%s: symlink too long", p);
115 return p;
116 }
117 }
118 }
119 return p;
120 }
121
122
123 static int lock_file(struct lock_file *lk, const char *path)
124 {
125 if (strlen(path) >= sizeof(lk->filename)) return -1;
126 strcpy(lk->filename, path);
127 /*
128 * subtract 5 from size to make sure there's room for adding
129 * ".lock" for the lock file name
130 */
131 resolve_symlink(lk->filename, sizeof(lk->filename)-5);
132 strcat(lk->filename, ".lock");
133 lk->fd = open(lk->filename, O_RDWR | O_CREAT | O_EXCL, 0666);
134 if (0 <= lk->fd) {
135 if (!lock_file_list) {
136 signal(SIGINT, remove_lock_file_on_signal);
137 atexit(remove_lock_file);
138 }
139 lk->owner = getpid();
140 if (!lk->on_list) {
141 lk->next = lock_file_list;
142 lock_file_list = lk;
143 lk->on_list = 1;
144 }
145 if (adjust_shared_perm(lk->filename))
146 return error("cannot fix permission bits on %s",
147 lk->filename);
148 }
149 else
150 lk->filename[0] = 0;
151 return lk->fd;
152 }
153
154 int hold_lock_file_for_update(struct lock_file *lk, const char *path, int die_on_error)
155 {
156 int fd = lock_file(lk, path);
157 if (fd < 0 && die_on_error)
158 die("unable to create '%s.lock': %s", path, strerror(errno));
159 return fd;
160 }
161
162 int commit_lock_file(struct lock_file *lk)
163 {
164 char result_file[PATH_MAX];
165 int i;
166 close(lk->fd);
167 strcpy(result_file, lk->filename);
168 i = strlen(result_file) - 5; /* .lock */
169 result_file[i] = 0;
170 i = rename(lk->filename, result_file);
171 lk->filename[0] = 0;
172 return i;
173 }
174
175 int hold_locked_index(struct lock_file *lk, int die_on_error)
176 {
177 return hold_lock_file_for_update(lk, get_index_file(), die_on_error);
178 }
179
180 void set_alternate_index_output(const char *name)
181 {
182 alternate_index_output = name;
183 }
184
185 int commit_locked_index(struct lock_file *lk)
186 {
187 if (alternate_index_output) {
188 int result = rename(lk->filename, alternate_index_output);
189 lk->filename[0] = 0;
190 return result;
191 }
192 else
193 return commit_lock_file(lk);
194 }
195
196 void rollback_lock_file(struct lock_file *lk)
197 {
198 if (lk->filename[0]) {
199 close(lk->fd);
200 unlink(lk->filename);
201 }
202 lk->filename[0] = 0;
203 }