Merge branch 'np/progress'
[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                         unlink(lock_file_list->filename);
17                 lock_file_list = lock_file_list->next;
18         }
19 }
20
21 static void remove_lock_file_on_signal(int signo)
22 {
23         remove_lock_file();
24         signal(SIGINT, SIG_DFL);
25         raise(signo);
26 }
27
28 /*
29  * p = absolute or relative path name
30  *
31  * Return a pointer into p showing the beginning of the last path name
32  * element.  If p is empty or the root directory ("/"), just return p.
33  */
34 static char *last_path_elm(char *p)
35 {
36         /* r starts pointing to null at the end of the string */
37         char *r = strchr(p, '\0');
38
39         if (r == p)
40                 return p; /* just return empty string */
41
42         r--; /* back up to last non-null character */
43
44         /* back up past trailing slashes, if any */
45         while (r > p && *r == '/')
46                 r--;
47
48         /*
49          * then go backwards until I hit a slash, or the beginning of
50          * the string
51          */
52         while (r > p && *(r-1) != '/')
53                 r--;
54         return r;
55 }
56
57
58 /* We allow "recursive" symbolic links. Only within reason, though */
59 #define MAXDEPTH 5
60
61 /*
62  * p = path that may be a symlink
63  * s = full size of p
64  *
65  * If p is a symlink, attempt to overwrite p with a path to the real
66  * file or directory (which may or may not exist), following a chain of
67  * symlinks if necessary.  Otherwise, leave p unmodified.
68  *
69  * This is a best-effort routine.  If an error occurs, p will either be
70  * left unmodified or will name a different symlink in a symlink chain
71  * that started with p's initial contents.
72  *
73  * Always returns p.
74  */
75
76 static char *resolve_symlink(char *p, size_t s)
77 {
78         int depth = MAXDEPTH;
79
80         while (depth--) {
81                 char link[PATH_MAX];
82                 int link_len = readlink(p, link, sizeof(link));
83                 if (link_len < 0) {
84                         /* not a symlink anymore */
85                         return p;
86                 }
87                 else if (link_len < sizeof(link))
88                         /* readlink() never null-terminates */
89                         link[link_len] = '\0';
90                 else {
91                         warning("%s: symlink too long", p);
92                         return p;
93                 }
94
95                 if (link[0] == '/') {
96                         /* absolute path simply replaces p */
97                         if (link_len < s)
98                                 strcpy(p, link);
99                         else {
100                                 warning("%s: symlink too long", p);
101                                 return p;
102                         }
103                 } else {
104                         /*
105                          * link is a relative path, so I must replace the
106                          * last element of p with it.
107                          */
108                         char *r = (char*)last_path_elm(p);
109                         if (r - p + link_len < s)
110                                 strcpy(r, link);
111                         else {
112                                 warning("%s: symlink too long", p);
113                                 return p;
114                         }
115                 }
116         }
117         return p;
118 }
119
120
121 static int lock_file(struct lock_file *lk, const char *path)
122 {
123         int fd;
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         fd = open(lk->filename, O_RDWR | O_CREAT | O_EXCL, 0666);
134         if (0 <= 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 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         strcpy(result_file, lk->filename);
167         i = strlen(result_file) - 5; /* .lock */
168         result_file[i] = 0;
169         i = rename(lk->filename, result_file);
170         lk->filename[0] = 0;
171         return i;
172 }
173
174 int hold_locked_index(struct lock_file *lk, int die_on_error)
175 {
176         return hold_lock_file_for_update(lk, get_index_file(), die_on_error);
177 }
178
179 void set_alternate_index_output(const char *name)
180 {
181         alternate_index_output = name;
182 }
183
184 int commit_locked_index(struct lock_file *lk)
185 {
186         if (alternate_index_output) {
187                 int result = rename(lk->filename, alternate_index_output);
188                 lk->filename[0] = 0;
189                 return result;
190         }
191         else
192                 return commit_lock_file(lk);
193 }
194
195 void rollback_lock_file(struct lock_file *lk)
196 {
197         if (lk->filename[0])
198                 unlink(lk->filename);
199         lk->filename[0] = 0;
200 }