Merge branch 'jt/perf-updates' into maint
[git] / tmp-objdir.c
1 #include "cache.h"
2 #include "tmp-objdir.h"
3 #include "dir.h"
4 #include "sigchain.h"
5 #include "string-list.h"
6 #include "strbuf.h"
7 #include "argv-array.h"
8 #include "quote.h"
9
10 struct tmp_objdir {
11         struct strbuf path;
12         struct argv_array env;
13 };
14
15 /*
16  * Allow only one tmp_objdir at a time in a running process, which simplifies
17  * our signal/atexit cleanup routines.  It's doubtful callers will ever need
18  * more than one, and we can expand later if so.  You can have many such
19  * tmp_objdirs simultaneously in many processes, of course.
20  */
21 static struct tmp_objdir *the_tmp_objdir;
22
23 static void tmp_objdir_free(struct tmp_objdir *t)
24 {
25         strbuf_release(&t->path);
26         argv_array_clear(&t->env);
27         free(t);
28 }
29
30 static int tmp_objdir_destroy_1(struct tmp_objdir *t, int on_signal)
31 {
32         int err;
33
34         if (!t)
35                 return 0;
36
37         if (t == the_tmp_objdir)
38                 the_tmp_objdir = NULL;
39
40         /*
41          * This may use malloc via strbuf_grow(), but we should
42          * have pre-grown t->path sufficiently so that this
43          * doesn't happen in practice.
44          */
45         err = remove_dir_recursively(&t->path, 0);
46
47         /*
48          * When we are cleaning up due to a signal, we won't bother
49          * freeing memory; it may cause a deadlock if the signal
50          * arrived while libc's allocator lock is held.
51          */
52         if (!on_signal)
53                 tmp_objdir_free(t);
54         return err;
55 }
56
57 int tmp_objdir_destroy(struct tmp_objdir *t)
58 {
59         return tmp_objdir_destroy_1(t, 0);
60 }
61
62 static void remove_tmp_objdir(void)
63 {
64         tmp_objdir_destroy(the_tmp_objdir);
65 }
66
67 static void remove_tmp_objdir_on_signal(int signo)
68 {
69         tmp_objdir_destroy_1(the_tmp_objdir, 1);
70         sigchain_pop(signo);
71         raise(signo);
72 }
73
74 /*
75  * These env_* functions are for setting up the child environment; the
76  * "replace" variant overrides the value of any existing variable with that
77  * "key". The "append" variant puts our new value at the end of a list,
78  * separated by PATH_SEP (which is what separate values in
79  * GIT_ALTERNATE_OBJECT_DIRECTORIES).
80  */
81 static void env_append(struct argv_array *env, const char *key, const char *val)
82 {
83         struct strbuf quoted = STRBUF_INIT;
84         const char *old;
85
86         /*
87          * Avoid quoting if it's not necessary, for maximum compatibility
88          * with older parsers which don't understand the quoting.
89          */
90         if (*val == '"' || strchr(val, PATH_SEP)) {
91                 strbuf_addch(&quoted, '"');
92                 quote_c_style(val, &quoted, NULL, 1);
93                 strbuf_addch(&quoted, '"');
94                 val = quoted.buf;
95         }
96
97         old = getenv(key);
98         if (!old)
99                 argv_array_pushf(env, "%s=%s", key, val);
100         else
101                 argv_array_pushf(env, "%s=%s%c%s", key, old, PATH_SEP, val);
102
103         strbuf_release(&quoted);
104 }
105
106 static void env_replace(struct argv_array *env, const char *key, const char *val)
107 {
108         argv_array_pushf(env, "%s=%s", key, val);
109 }
110
111 static int setup_tmp_objdir(const char *root)
112 {
113         char *path;
114         int ret = 0;
115
116         path = xstrfmt("%s/pack", root);
117         ret = mkdir(path, 0777);
118         free(path);
119
120         return ret;
121 }
122
123 struct tmp_objdir *tmp_objdir_create(void)
124 {
125         static int installed_handlers;
126         struct tmp_objdir *t;
127
128         if (the_tmp_objdir)
129                 die("BUG: only one tmp_objdir can be used at a time");
130
131         t = xmalloc(sizeof(*t));
132         strbuf_init(&t->path, 0);
133         argv_array_init(&t->env);
134
135         strbuf_addf(&t->path, "%s/incoming-XXXXXX", get_object_directory());
136
137         /*
138          * Grow the strbuf beyond any filename we expect to be placed in it.
139          * If tmp_objdir_destroy() is called by a signal handler, then
140          * we should be able to use the strbuf to remove files without
141          * having to call malloc.
142          */
143         strbuf_grow(&t->path, 1024);
144
145         if (!mkdtemp(t->path.buf)) {
146                 /* free, not destroy, as we never touched the filesystem */
147                 tmp_objdir_free(t);
148                 return NULL;
149         }
150
151         the_tmp_objdir = t;
152         if (!installed_handlers) {
153                 atexit(remove_tmp_objdir);
154                 sigchain_push_common(remove_tmp_objdir_on_signal);
155                 installed_handlers++;
156         }
157
158         if (setup_tmp_objdir(t->path.buf)) {
159                 tmp_objdir_destroy(t);
160                 return NULL;
161         }
162
163         env_append(&t->env, ALTERNATE_DB_ENVIRONMENT,
164                    absolute_path(get_object_directory()));
165         env_replace(&t->env, DB_ENVIRONMENT, absolute_path(t->path.buf));
166         env_replace(&t->env, GIT_QUARANTINE_ENVIRONMENT,
167                     absolute_path(t->path.buf));
168
169         return t;
170 }
171
172 /*
173  * Make sure we copy packfiles and their associated metafiles in the correct
174  * order. All of these ends_with checks are slightly expensive to do in
175  * the midst of a sorting routine, but in practice it shouldn't matter.
176  * We will have a relatively small number of packfiles to order, and loose
177  * objects exit early in the first line.
178  */
179 static int pack_copy_priority(const char *name)
180 {
181         if (!starts_with(name, "pack"))
182                 return 0;
183         if (ends_with(name, ".keep"))
184                 return 1;
185         if (ends_with(name, ".pack"))
186                 return 2;
187         if (ends_with(name, ".idx"))
188                 return 3;
189         return 4;
190 }
191
192 static int pack_copy_cmp(const char *a, const char *b)
193 {
194         return pack_copy_priority(a) - pack_copy_priority(b);
195 }
196
197 static int read_dir_paths(struct string_list *out, const char *path)
198 {
199         DIR *dh;
200         struct dirent *de;
201
202         dh = opendir(path);
203         if (!dh)
204                 return -1;
205
206         while ((de = readdir(dh)))
207                 if (de->d_name[0] != '.')
208                         string_list_append(out, de->d_name);
209
210         closedir(dh);
211         return 0;
212 }
213
214 static int migrate_paths(struct strbuf *src, struct strbuf *dst);
215
216 static int migrate_one(struct strbuf *src, struct strbuf *dst)
217 {
218         struct stat st;
219
220         if (stat(src->buf, &st) < 0)
221                 return -1;
222         if (S_ISDIR(st.st_mode)) {
223                 if (!mkdir(dst->buf, 0777)) {
224                         if (adjust_shared_perm(dst->buf))
225                                 return -1;
226                 } else if (errno != EEXIST)
227                         return -1;
228                 return migrate_paths(src, dst);
229         }
230         return finalize_object_file(src->buf, dst->buf);
231 }
232
233 static int migrate_paths(struct strbuf *src, struct strbuf *dst)
234 {
235         size_t src_len = src->len, dst_len = dst->len;
236         struct string_list paths = STRING_LIST_INIT_DUP;
237         int i;
238         int ret = 0;
239
240         if (read_dir_paths(&paths, src->buf) < 0)
241                 return -1;
242         paths.cmp = pack_copy_cmp;
243         string_list_sort(&paths);
244
245         for (i = 0; i < paths.nr; i++) {
246                 const char *name = paths.items[i].string;
247
248                 strbuf_addf(src, "/%s", name);
249                 strbuf_addf(dst, "/%s", name);
250
251                 ret |= migrate_one(src, dst);
252
253                 strbuf_setlen(src, src_len);
254                 strbuf_setlen(dst, dst_len);
255         }
256
257         string_list_clear(&paths, 0);
258         return ret;
259 }
260
261 int tmp_objdir_migrate(struct tmp_objdir *t)
262 {
263         struct strbuf src = STRBUF_INIT, dst = STRBUF_INIT;
264         int ret;
265
266         if (!t)
267                 return 0;
268
269         strbuf_addbuf(&src, &t->path);
270         strbuf_addstr(&dst, get_object_directory());
271
272         ret = migrate_paths(&src, &dst);
273
274         strbuf_release(&src);
275         strbuf_release(&dst);
276
277         tmp_objdir_destroy(t);
278         return ret;
279 }
280
281 const char **tmp_objdir_env(const struct tmp_objdir *t)
282 {
283         if (!t)
284                 return NULL;
285         return t->env.argv;
286 }
287
288 void tmp_objdir_add_as_alternate(const struct tmp_objdir *t)
289 {
290         add_to_alternates_memory(t->path.buf);
291 }