Merge branch 'ab/deprecate-R-for-dynpath'
[git] / server-info.c
1 #include "cache.h"
2 #include "repository.h"
3 #include "refs.h"
4 #include "object.h"
5 #include "commit.h"
6 #include "tag.h"
7 #include "packfile.h"
8 #include "object-store.h"
9 #include "strbuf.h"
10
11 struct update_info_ctx {
12         FILE *cur_fp;
13         FILE *old_fp; /* becomes NULL if it differs from cur_fp */
14         struct strbuf cur_sb;
15         struct strbuf old_sb;
16 };
17
18 static void uic_mark_stale(struct update_info_ctx *uic)
19 {
20         fclose(uic->old_fp);
21         uic->old_fp = NULL;
22 }
23
24 static int uic_is_stale(const struct update_info_ctx *uic)
25 {
26         return uic->old_fp == NULL;
27 }
28
29 static int uic_printf(struct update_info_ctx *uic, const char *fmt, ...)
30 {
31         va_list ap;
32         int ret = -1;
33
34         va_start(ap, fmt);
35
36         if (uic_is_stale(uic)) {
37                 ret = vfprintf(uic->cur_fp, fmt, ap);
38         } else {
39                 ssize_t r;
40                 struct strbuf *cur = &uic->cur_sb;
41                 struct strbuf *old = &uic->old_sb;
42
43                 strbuf_reset(cur);
44                 strbuf_vinsertf(cur, 0, fmt, ap);
45
46                 strbuf_reset(old);
47                 strbuf_grow(old, cur->len);
48                 r = fread(old->buf, 1, cur->len, uic->old_fp);
49                 if (r != cur->len || memcmp(old->buf, cur->buf, r))
50                         uic_mark_stale(uic);
51
52                 if (fwrite(cur->buf, 1, cur->len, uic->cur_fp) == cur->len)
53                         ret = 0;
54         }
55
56         va_end(ap);
57
58         return ret;
59 }
60
61 /*
62  * Create the file "path" by writing to a temporary file and renaming
63  * it into place. The contents of the file come from "generate", which
64  * should return non-zero if it encounters an error.
65  */
66 static int update_info_file(char *path,
67                         int (*generate)(struct update_info_ctx *),
68                         int force)
69 {
70         char *tmp = mkpathdup("%s_XXXXXX", path);
71         int ret = -1;
72         int fd = -1;
73         FILE *to_close;
74         struct update_info_ctx uic = {
75                 .cur_fp = NULL,
76                 .old_fp = NULL,
77                 .cur_sb = STRBUF_INIT,
78                 .old_sb = STRBUF_INIT
79         };
80
81         safe_create_leading_directories(path);
82         fd = git_mkstemp_mode(tmp, 0666);
83         if (fd < 0)
84                 goto out;
85         to_close = uic.cur_fp = fdopen(fd, "w");
86         if (!uic.cur_fp)
87                 goto out;
88         fd = -1;
89
90         /* no problem on ENOENT and old_fp == NULL, it's stale, now */
91         if (!force)
92                 uic.old_fp = fopen_or_warn(path, "r");
93
94         /*
95          * uic_printf will compare incremental comparison aginst old_fp
96          * and mark uic as stale if needed
97          */
98         ret = generate(&uic);
99         if (ret)
100                 goto out;
101
102         /* new file may be shorter than the old one, check here */
103         if (!uic_is_stale(&uic)) {
104                 struct stat st;
105                 long new_len = ftell(uic.cur_fp);
106                 int old_fd = fileno(uic.old_fp);
107
108                 if (new_len < 0) {
109                         ret = -1;
110                         goto out;
111                 }
112                 if (fstat(old_fd, &st) || (st.st_size != (size_t)new_len))
113                         uic_mark_stale(&uic);
114         }
115
116         uic.cur_fp = NULL;
117         if (fclose(to_close))
118                 goto out;
119
120         if (uic_is_stale(&uic)) {
121                 if (adjust_shared_perm(tmp) < 0)
122                         goto out;
123                 if (rename(tmp, path) < 0)
124                         goto out;
125         } else {
126                 unlink(tmp);
127         }
128         ret = 0;
129
130 out:
131         if (ret) {
132                 error_errno("unable to update %s", path);
133                 if (uic.cur_fp)
134                         fclose(uic.cur_fp);
135                 else if (fd >= 0)
136                         close(fd);
137                 unlink(tmp);
138         }
139         free(tmp);
140         if (uic.old_fp)
141                 fclose(uic.old_fp);
142         strbuf_release(&uic.old_sb);
143         strbuf_release(&uic.cur_sb);
144         return ret;
145 }
146
147 static int add_info_ref(const char *path, const struct object_id *oid,
148                         int flag, void *cb_data)
149 {
150         struct update_info_ctx *uic = cb_data;
151         struct object *o = parse_object(the_repository, oid);
152         if (!o)
153                 return -1;
154
155         if (uic_printf(uic, "%s %s\n", oid_to_hex(oid), path) < 0)
156                 return -1;
157
158         if (o->type == OBJ_TAG) {
159                 o = deref_tag(the_repository, o, path, 0);
160                 if (o)
161                         if (uic_printf(uic, "%s %s^{}\n",
162                                 oid_to_hex(&o->oid), path) < 0)
163                                 return -1;
164         }
165         return 0;
166 }
167
168 static int generate_info_refs(struct update_info_ctx *uic)
169 {
170         return for_each_ref(add_info_ref, uic);
171 }
172
173 static int update_info_refs(int force)
174 {
175         char *path = git_pathdup("info/refs");
176         int ret = update_info_file(path, generate_info_refs, force);
177         free(path);
178         return ret;
179 }
180
181 /* packs */
182 static struct pack_info {
183         struct packed_git *p;
184         int old_num;
185         int new_num;
186 } **info;
187 static int num_pack;
188
189 static struct pack_info *find_pack_by_name(const char *name)
190 {
191         int i;
192         for (i = 0; i < num_pack; i++) {
193                 struct packed_git *p = info[i]->p;
194                 if (!strcmp(pack_basename(p), name))
195                         return info[i];
196         }
197         return NULL;
198 }
199
200 /* Returns non-zero when we detect that the info in the
201  * old file is useless.
202  */
203 static int parse_pack_def(const char *packname, int old_cnt)
204 {
205         struct pack_info *i = find_pack_by_name(packname);
206         if (i) {
207                 i->old_num = old_cnt;
208                 return 0;
209         }
210         else {
211                 /* The file describes a pack that is no longer here */
212                 return 1;
213         }
214 }
215
216 /* Returns non-zero when we detect that the info in the
217  * old file is useless.
218  */
219 static int read_pack_info_file(const char *infofile)
220 {
221         FILE *fp;
222         struct strbuf line = STRBUF_INIT;
223         int old_cnt = 0;
224         int stale = 1;
225
226         fp = fopen_or_warn(infofile, "r");
227         if (!fp)
228                 return 1; /* nonexistent is not an error. */
229
230         while (strbuf_getline(&line, fp) != EOF) {
231                 const char *arg;
232
233                 if (!line.len)
234                         continue;
235
236                 if (skip_prefix(line.buf, "P ", &arg)) {
237                         /* P name */
238                         if (parse_pack_def(arg, old_cnt++))
239                                 goto out_stale;
240                 } else if (line.buf[0] == 'D') {
241                         /* we used to emit D but that was misguided. */
242                         goto out_stale;
243                 } else if (line.buf[0] == 'T') {
244                         /* we used to emit T but nobody uses it. */
245                         goto out_stale;
246                 } else {
247                         error("unrecognized: %s", line.buf);
248                 }
249         }
250         stale = 0;
251
252  out_stale:
253         strbuf_release(&line);
254         fclose(fp);
255         return stale;
256 }
257
258 static int compare_info(const void *a_, const void *b_)
259 {
260         struct pack_info *const *a = a_;
261         struct pack_info *const *b = b_;
262
263         if (0 <= (*a)->old_num && 0 <= (*b)->old_num)
264                 /* Keep the order in the original */
265                 return (*a)->old_num - (*b)->old_num;
266         else if (0 <= (*a)->old_num)
267                 /* Only A existed in the original so B is obviously newer */
268                 return -1;
269         else if (0 <= (*b)->old_num)
270                 /* The other way around. */
271                 return 1;
272
273         /* then it does not matter but at least keep the comparison stable */
274         if ((*a)->p == (*b)->p)
275                 return 0;
276         else if ((*a)->p < (*b)->p)
277                 return -1;
278         else
279                 return 1;
280 }
281
282 static void init_pack_info(const char *infofile, int force)
283 {
284         struct packed_git *p;
285         int stale;
286         int i = 0;
287
288         for (p = get_all_packs(the_repository); p; p = p->next) {
289                 /* we ignore things on alternate path since they are
290                  * not available to the pullers in general.
291                  */
292                 if (!p->pack_local)
293                         continue;
294                 i++;
295         }
296         num_pack = i;
297         info = xcalloc(num_pack, sizeof(struct pack_info *));
298         for (i = 0, p = get_all_packs(the_repository); p; p = p->next) {
299                 if (!p->pack_local)
300                         continue;
301                 assert(i < num_pack);
302                 info[i] = xcalloc(1, sizeof(struct pack_info));
303                 info[i]->p = p;
304                 info[i]->old_num = -1;
305                 i++;
306         }
307
308         if (infofile && !force)
309                 stale = read_pack_info_file(infofile);
310         else
311                 stale = 1;
312
313         for (i = 0; i < num_pack; i++)
314                 if (stale)
315                         info[i]->old_num = -1;
316
317         /* renumber them */
318         QSORT(info, num_pack, compare_info);
319         for (i = 0; i < num_pack; i++)
320                 info[i]->new_num = i;
321 }
322
323 static void free_pack_info(void)
324 {
325         int i;
326         for (i = 0; i < num_pack; i++)
327                 free(info[i]);
328         free(info);
329 }
330
331 static int write_pack_info_file(struct update_info_ctx *uic)
332 {
333         int i;
334         for (i = 0; i < num_pack; i++) {
335                 if (uic_printf(uic, "P %s\n", pack_basename(info[i]->p)) < 0)
336                         return -1;
337         }
338         if (uic_printf(uic, "\n") < 0)
339                 return -1;
340         return 0;
341 }
342
343 static int update_info_packs(int force)
344 {
345         char *infofile = mkpathdup("%s/info/packs", get_object_directory());
346         int ret;
347
348         init_pack_info(infofile, force);
349         ret = update_info_file(infofile, write_pack_info_file, force);
350         free_pack_info();
351         free(infofile);
352         return ret;
353 }
354
355 /* public */
356 int update_server_info(int force)
357 {
358         /* We would add more dumb-server support files later,
359          * including index of available pack files and their
360          * intended audiences.
361          */
362         int errs = 0;
363
364         errs = errs | update_info_refs(force);
365         errs = errs | update_info_packs(force);
366
367         /* remove leftover rev-cache file if there is any */
368         unlink_or_warn(git_path("info/rev-cache"));
369
370         return errs;
371 }