File: | libexec/got-fetch-pack/got-fetch-pack.c |
Warning: | line 704, column 21 Array access (from variable 'server_capabilities') results in a null pointer dereference |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* | |||
2 | * Copyright (c) 2019 Ori Bernstein <ori@openbsd.org> | |||
3 | * | |||
4 | * Permission to use, copy, modify, and distribute this software for any | |||
5 | * purpose with or without fee is hereby granted, provided that the above | |||
6 | * copyright notice and this permission notice appear in all copies. | |||
7 | * | |||
8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||
9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||
10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||
11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||
12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||
13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||
14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
15 | */ | |||
16 | ||||
17 | #include <sys/types.h> | |||
18 | #include <sys/queue.h> | |||
19 | #include <sys/uio.h> | |||
20 | #include <sys/time.h> | |||
21 | #include <sys/stat.h> | |||
22 | ||||
23 | #include <stdint.h> | |||
24 | #include <errno(*__errno()).h> | |||
25 | #include <imsg.h> | |||
26 | #include <limits.h> | |||
27 | #include <signal.h> | |||
28 | #include <stdio.h> | |||
29 | #include <stdlib.h> | |||
30 | #include <string.h> | |||
31 | #include <ctype.h> | |||
32 | #include <sha1.h> | |||
33 | #include <fcntl.h> | |||
34 | #include <unistd.h> | |||
35 | #include <zlib.h> | |||
36 | #include <err.h> | |||
37 | ||||
38 | #include "got_error.h" | |||
39 | #include "got_object.h" | |||
40 | #include "got_path.h" | |||
41 | #include "got_version.h" | |||
42 | #include "got_fetch.h" | |||
43 | #include "got_reference.h" | |||
44 | ||||
45 | #include "got_lib_sha1.h" | |||
46 | #include "got_lib_delta.h" | |||
47 | #include "got_lib_object.h" | |||
48 | #include "got_lib_object_parse.h" | |||
49 | #include "got_lib_privsep.h" | |||
50 | #include "got_lib_pack.h" | |||
51 | ||||
52 | #ifndef nitems | |||
53 | #define nitems(_a)(sizeof((_a)) / sizeof((_a)[0])) (sizeof((_a)) / sizeof((_a)[0])) | |||
54 | #endif | |||
55 | ||||
56 | struct got_object *indexed; | |||
57 | static int chattygot; | |||
58 | static struct got_object_id zhash = {.sha1={0}}; | |||
59 | ||||
60 | static const struct got_error * | |||
61 | readn(ssize_t *off, int fd, void *buf, size_t n) | |||
62 | { | |||
63 | ssize_t r; | |||
64 | ||||
65 | *off = 0; | |||
66 | while (*off != n) { | |||
67 | r = read(fd, buf + *off, n - *off); | |||
68 | if (r == -1) | |||
69 | return got_error_from_errno("read"); | |||
70 | if (r == 0) | |||
71 | return NULL((void *)0); | |||
72 | *off += r; | |||
73 | } | |||
74 | return NULL((void *)0); | |||
75 | } | |||
76 | ||||
77 | static const struct got_error * | |||
78 | flushpkt(int fd) | |||
79 | { | |||
80 | ssize_t w; | |||
81 | ||||
82 | if (chattygot > 1) | |||
83 | fprintf(stderr(&__sF[2]), "%s: writepkt: 0000\n", getprogname()); | |||
84 | ||||
85 | w = write(fd, "0000", 4); | |||
86 | if (w == -1) | |||
87 | return got_error_from_errno("write"); | |||
88 | if (w != 4) | |||
89 | return got_error(GOT_ERR_IO6); | |||
90 | return NULL((void *)0); | |||
91 | } | |||
92 | ||||
93 | /* | |||
94 | * Packet header contains a 4-byte hexstring which specifies the length | |||
95 | * of data which follows. | |||
96 | */ | |||
97 | static const struct got_error * | |||
98 | read_pkthdr(int *datalen, int fd) | |||
99 | { | |||
100 | static const struct got_error *err = NULL((void *)0); | |||
101 | char lenstr[5]; | |||
102 | long len; | |||
103 | char *e; | |||
104 | int n, i; | |||
105 | ssize_t r; | |||
106 | ||||
107 | *datalen = 0; | |||
108 | ||||
109 | err = readn(&r, fd, lenstr, 4); | |||
110 | if (err) | |||
111 | return err; | |||
112 | if (r == 0) { | |||
113 | /* implicit "0000" */ | |||
114 | if (chattygot > 1) | |||
115 | fprintf(stderr(&__sF[2]), "%s: readpkt: 0000\n", getprogname()); | |||
116 | return NULL((void *)0); | |||
117 | } | |||
118 | if (r != 4) | |||
119 | return got_error_msg(GOT_ERR_BAD_PACKET122, | |||
120 | "wrong packet header length"); | |||
121 | ||||
122 | lenstr[4] = '\0'; | |||
123 | for (i = 0; i < 4; i++) { | |||
124 | if (!isprint((unsigned char)lenstr[i])) | |||
125 | return got_error_msg(GOT_ERR_BAD_PACKET122, | |||
126 | "unprintable character in packet length field"); | |||
127 | } | |||
128 | for (i = 0; i < 4; i++) { | |||
129 | if (!isxdigit((unsigned char)lenstr[i])) { | |||
130 | if (chattygot) | |||
131 | fprintf(stderr(&__sF[2]), "%s: bad length: '%s'\n", | |||
132 | getprogname(), lenstr); | |||
133 | return got_error_msg(GOT_ERR_BAD_PACKET122, | |||
134 | "packet length not specified in hex"); | |||
135 | } | |||
136 | } | |||
137 | errno(*__errno()) = 0; | |||
138 | len = strtol(lenstr, &e, 16); | |||
139 | if (lenstr[0] == '\0' || *e != '\0') | |||
140 | return got_error(GOT_ERR_BAD_PACKET122); | |||
141 | if (errno(*__errno()) == ERANGE34 && (len == LONG_MAX9223372036854775807L || len == LONG_MIN(-9223372036854775807L -1L))) | |||
142 | return got_error_msg(GOT_ERR_BAD_PACKET122, "bad packet length"); | |||
143 | if (len > INT_MAX2147483647 || len < INT_MIN(-2147483647 -1)) | |||
144 | return got_error_msg(GOT_ERR_BAD_PACKET122, "bad packet length"); | |||
145 | n = len; | |||
146 | if (n == 0) | |||
147 | return NULL((void *)0); | |||
148 | if (n <= 4) | |||
149 | return got_error_msg(GOT_ERR_BAD_PACKET122, "packet too short"); | |||
150 | n -= 4; | |||
151 | ||||
152 | *datalen = n; | |||
153 | return NULL((void *)0); | |||
154 | } | |||
155 | ||||
156 | static const struct got_error * | |||
157 | readpkt(int *outlen, int fd, char *buf, int buflen) | |||
158 | { | |||
159 | const struct got_error *err = NULL((void *)0); | |||
160 | int datalen, i; | |||
161 | ssize_t n; | |||
162 | ||||
163 | err = read_pkthdr(&datalen, fd); | |||
164 | if (err) | |||
165 | return err; | |||
166 | ||||
167 | if (datalen > buflen) | |||
168 | return got_error(GOT_ERR_NO_SPACE9); | |||
169 | ||||
170 | err = readn(&n, fd, buf, datalen); | |||
171 | if (err) | |||
172 | return err; | |||
173 | if (n != datalen) | |||
174 | return got_error_msg(GOT_ERR_BAD_PACKET122, "short packet"); | |||
175 | ||||
176 | if (chattygot > 1) { | |||
177 | fprintf(stderr(&__sF[2]), "%s: readpkt: %zd:\t", getprogname(), n); | |||
178 | for (i = 0; i < n; i++) { | |||
179 | if (isprint(buf[i])) | |||
180 | fputc(buf[i], stderr(&__sF[2])); | |||
181 | else | |||
182 | fprintf(stderr(&__sF[2]), "[0x%.2x]", buf[i]); | |||
183 | } | |||
184 | fputc('\n', stderr(&__sF[2])); | |||
185 | } | |||
186 | ||||
187 | *outlen = n; | |||
188 | return NULL((void *)0); | |||
189 | } | |||
190 | ||||
191 | static const struct got_error * | |||
192 | writepkt(int fd, char *buf, int nbuf) | |||
193 | { | |||
194 | char len[5]; | |||
195 | int i; | |||
196 | ssize_t w; | |||
197 | ||||
198 | if (snprintf(len, sizeof(len), "%04x", nbuf + 4) >= sizeof(len)) | |||
199 | return got_error(GOT_ERR_NO_SPACE9); | |||
200 | w = write(fd, len, 4); | |||
201 | if (w == -1) | |||
202 | return got_error_from_errno("write"); | |||
203 | if (w != 4) | |||
204 | return got_error(GOT_ERR_IO6); | |||
205 | w = write(fd, buf, nbuf); | |||
206 | if (w == -1) | |||
207 | return got_error_from_errno("write"); | |||
208 | if (w != nbuf) | |||
209 | return got_error(GOT_ERR_IO6); | |||
210 | if (chattygot > 1) { | |||
211 | fprintf(stderr(&__sF[2]), "%s: writepkt: %s:\t", getprogname(), len); | |||
212 | for (i = 0; i < nbuf; i++) { | |||
213 | if (isprint(buf[i])) | |||
214 | fputc(buf[i], stderr(&__sF[2])); | |||
215 | else | |||
216 | fprintf(stderr(&__sF[2]), "[0x%.2x]", buf[i]); | |||
217 | } | |||
218 | fputc('\n', stderr(&__sF[2])); | |||
219 | } | |||
220 | return NULL((void *)0); | |||
221 | } | |||
222 | ||||
223 | static void | |||
224 | match_remote_ref(struct got_pathlist_head *have_refs, | |||
225 | struct got_object_id *my_id, char *refname) | |||
226 | { | |||
227 | struct got_pathlist_entry *pe; | |||
228 | ||||
229 | /* XXX zero-hash signifies we don't have this ref; | |||
230 | * we should use a flag instead */ | |||
231 | memset(my_id, 0, sizeof(*my_id)); | |||
232 | ||||
233 | TAILQ_FOREACH(pe, have_refs, entry)for((pe) = ((have_refs)->tqh_first); (pe) != ((void *)0); ( pe) = ((pe)->entry.tqe_next)) { | |||
234 | struct got_object_id *id = pe->data; | |||
235 | if (strcmp(pe->path, refname) == 0) { | |||
236 | memcpy(my_id, id, sizeof(*my_id)); | |||
237 | break; | |||
238 | } | |||
239 | } | |||
240 | } | |||
241 | ||||
242 | static int | |||
243 | match_branch(const char *branch, const char *wanted_branch) | |||
244 | { | |||
245 | if (strncmp(branch, "refs/heads/", 11) != 0) | |||
246 | return 0; | |||
247 | ||||
248 | if (strncmp(wanted_branch, "refs/heads/", 11) == 0) | |||
249 | wanted_branch += 11; | |||
250 | ||||
251 | return (strcmp(branch + 11, wanted_branch) == 0); | |||
252 | } | |||
253 | ||||
254 | static int | |||
255 | match_wanted_ref(const char *refname, const char *wanted_ref) | |||
256 | { | |||
257 | if (strncmp(refname, "refs/", 5) != 0) | |||
258 | return 0; | |||
259 | refname += 5; | |||
260 | ||||
261 | /* | |||
262 | * Prevent fetching of references that won't make any | |||
263 | * sense outside of the remote repository's context. | |||
264 | */ | |||
265 | if (strncmp(refname, "got/", 4) == 0) | |||
266 | return 0; | |||
267 | if (strncmp(refname, "remotes/", 8) == 0) | |||
268 | return 0; | |||
269 | ||||
270 | if (strncmp(wanted_ref, "refs/", 5) == 0) | |||
271 | wanted_ref += 5; | |||
272 | ||||
273 | /* Allow prefix match. */ | |||
274 | if (got_path_is_child(refname, wanted_ref, strlen(wanted_ref))) | |||
275 | return 1; | |||
276 | ||||
277 | /* Allow exact match. */ | |||
278 | return (strcmp(refname, wanted_ref) == 0); | |||
279 | } | |||
280 | ||||
281 | static const struct got_error * | |||
282 | tokenize_refline(char **tokens, char *line, int len, int maxtokens) | |||
283 | { | |||
284 | const struct got_error *err = NULL((void *)0); | |||
285 | char *p; | |||
286 | size_t i, n = 0; | |||
287 | ||||
288 | for (i = 0; i < maxtokens; i++) | |||
289 | tokens[i] = NULL((void *)0); | |||
290 | ||||
291 | for (i = 0; n < len && i < maxtokens; i++) { | |||
292 | while (isspace(*line)) { | |||
293 | line++; | |||
294 | n++; | |||
295 | } | |||
296 | p = line; | |||
297 | while (*line != '\0' && | |||
298 | (!isspace(*line) || i == maxtokens - 1)) { | |||
299 | line++; | |||
300 | n++; | |||
301 | } | |||
302 | tokens[i] = strndup(p, line - p); | |||
303 | if (tokens[i] == NULL((void *)0)) { | |||
304 | err = got_error_from_errno("strndup"); | |||
305 | goto done; | |||
306 | } | |||
307 | /* Skip \0 field-delimiter at end of token. */ | |||
308 | while (line[0] == '\0' && n < len) { | |||
309 | line++; | |||
310 | n++; | |||
311 | } | |||
312 | } | |||
313 | if (i <= 2) | |||
314 | err = got_error(GOT_ERR_NOT_REF5); | |||
315 | done: | |||
316 | if (err) { | |||
317 | int j; | |||
318 | for (j = 0; j < i; j++) { | |||
319 | free(tokens[j]); | |||
320 | tokens[j] = NULL((void *)0); | |||
321 | } | |||
322 | } | |||
323 | return err; | |||
324 | } | |||
325 | ||||
326 | static const struct got_error * | |||
327 | parse_refline(char **id_str, char **refname, char **server_capabilities, | |||
328 | char *line, int len) | |||
329 | { | |||
330 | const struct got_error *err = NULL((void *)0); | |||
331 | char *tokens[3]; | |||
332 | ||||
333 | err = tokenize_refline(tokens, line, len, nitems(tokens)(sizeof((tokens)) / sizeof((tokens)[0]))); | |||
334 | if (err) | |||
335 | return err; | |||
336 | ||||
337 | if (tokens[0]) | |||
338 | *id_str = tokens[0]; | |||
339 | if (tokens[1]) | |||
340 | *refname = tokens[1]; | |||
341 | if (tokens[2]) { | |||
342 | char *p; | |||
343 | *server_capabilities = tokens[2]; | |||
344 | p = strrchr(*server_capabilities, '\n'); | |||
345 | if (p) | |||
346 | *p = '\0'; | |||
347 | } | |||
348 | ||||
349 | return NULL((void *)0); | |||
350 | } | |||
351 | ||||
352 | #define GOT_CAPA_AGENT"agent" "agent" | |||
353 | #define GOT_CAPA_OFS_DELTA"ofs-delta" "ofs-delta" | |||
354 | #define GOT_CAPA_SIDE_BAND_64K"side-band-64k" "side-band-64k" | |||
355 | ||||
356 | #define GOT_SIDEBAND_PACKFILE_DATA1 1 | |||
357 | #define GOT_SIDEBAND_PROGRESS_INFO2 2 | |||
358 | #define GOT_SIDEBAND_ERROR_INFO3 3 | |||
359 | ||||
360 | ||||
361 | struct got_capability { | |||
362 | const char *key; | |||
363 | const char *value; | |||
364 | }; | |||
365 | static const struct got_capability got_capabilities[] = { | |||
366 | { GOT_CAPA_AGENT"agent", "got/" GOT_VERSION_STR"0.53-current" }, | |||
367 | { GOT_CAPA_OFS_DELTA"ofs-delta", NULL((void *)0) }, | |||
368 | { GOT_CAPA_SIDE_BAND_64K"side-band-64k", NULL((void *)0) }, | |||
369 | }; | |||
370 | ||||
371 | static const struct got_error * | |||
372 | match_capability(char **my_capabilities, const char *capa, | |||
373 | const struct got_capability *mycapa) | |||
374 | { | |||
375 | char *equalsign; | |||
376 | char *s; | |||
377 | ||||
378 | equalsign = strchr(capa, '='); | |||
379 | if (equalsign) { | |||
380 | if (strncmp(capa, mycapa->key, equalsign - capa) != 0) | |||
381 | return NULL((void *)0); | |||
382 | } else { | |||
383 | if (strcmp(capa, mycapa->key) != 0) | |||
384 | return NULL((void *)0); | |||
385 | } | |||
386 | ||||
387 | if (asprintf(&s, "%s %s%s%s", | |||
388 | *my_capabilities != NULL((void *)0) ? *my_capabilities : "", | |||
389 | mycapa->key, | |||
390 | mycapa->value != NULL((void *)0) ? "=" : "", | |||
391 | mycapa->value != NULL((void *)0)? mycapa->value : "") == -1) | |||
392 | return got_error_from_errno("asprintf"); | |||
393 | ||||
394 | free(*my_capabilities); | |||
395 | *my_capabilities = s; | |||
396 | return NULL((void *)0); | |||
397 | } | |||
398 | ||||
399 | static const struct got_error * | |||
400 | add_symref(struct got_pathlist_head *symrefs, char *capa) | |||
401 | { | |||
402 | const struct got_error *err = NULL((void *)0); | |||
403 | char *colon, *name = NULL((void *)0), *target = NULL((void *)0); | |||
404 | ||||
405 | /* Need at least "A:B" */ | |||
406 | if (strlen(capa) < 3) | |||
407 | return NULL((void *)0); | |||
408 | ||||
409 | colon = strchr(capa, ':'); | |||
410 | if (colon == NULL((void *)0)) | |||
411 | return NULL((void *)0); | |||
412 | ||||
413 | *colon = '\0'; | |||
414 | name = strdup(capa); | |||
415 | if (name == NULL((void *)0)) | |||
416 | return got_error_from_errno("strdup"); | |||
417 | ||||
418 | target = strdup(colon + 1); | |||
419 | if (target == NULL((void *)0)) { | |||
420 | err = got_error_from_errno("strdup"); | |||
421 | goto done; | |||
422 | } | |||
423 | ||||
424 | /* We can't validate the ref itself here. The main process will. */ | |||
425 | err = got_pathlist_append(symrefs, name, target); | |||
426 | done: | |||
427 | if (err) { | |||
428 | free(name); | |||
429 | free(target); | |||
430 | } | |||
431 | return err; | |||
432 | } | |||
433 | ||||
434 | static const struct got_error * | |||
435 | match_capabilities(char **my_capabilities, struct got_pathlist_head *symrefs, | |||
436 | char *server_capabilities) | |||
437 | { | |||
438 | const struct got_error *err = NULL((void *)0); | |||
439 | char *capa, *equalsign; | |||
440 | size_t i; | |||
441 | ||||
442 | *my_capabilities = NULL((void *)0); | |||
443 | do { | |||
444 | capa = strsep(&server_capabilities, " "); | |||
445 | if (capa == NULL((void *)0)) | |||
446 | return NULL((void *)0); | |||
447 | ||||
448 | equalsign = strchr(capa, '='); | |||
449 | if (equalsign != NULL((void *)0) && | |||
450 | strncmp(capa, "symref", equalsign - capa) == 0) { | |||
451 | err = add_symref(symrefs, equalsign + 1); | |||
452 | if (err) | |||
453 | break; | |||
454 | continue; | |||
455 | } | |||
456 | ||||
457 | for (i = 0; i < nitems(got_capabilities)(sizeof((got_capabilities)) / sizeof((got_capabilities)[0])); i++) { | |||
458 | err = match_capability(my_capabilities, | |||
459 | capa, &got_capabilities[i]); | |||
460 | if (err) | |||
461 | break; | |||
462 | } | |||
463 | } while (capa); | |||
464 | ||||
465 | if (*my_capabilities == NULL((void *)0)) { | |||
466 | *my_capabilities = strdup(""); | |||
467 | if (*my_capabilities == NULL((void *)0)) | |||
468 | err = got_error_from_errno("strdup"); | |||
469 | } | |||
470 | return err; | |||
471 | } | |||
472 | ||||
473 | static const struct got_error * | |||
474 | send_fetch_server_progress(struct imsgbuf *ibuf, const char *msg, size_t msglen) | |||
475 | { | |||
476 | if (msglen > MAX_IMSGSIZE16384 - IMSG_HEADER_SIZEsizeof(struct imsg_hdr)) | |||
477 | return got_error(GOT_ERR_NO_SPACE9); | |||
478 | ||||
479 | if (msglen == 0) | |||
480 | return NULL((void *)0); | |||
481 | ||||
482 | if (imsg_compose(ibuf, GOT_IMSG_FETCH_SERVER_PROGRESS, 0, 0, -1, | |||
483 | msg, msglen) == -1) | |||
484 | return got_error_from_errno( | |||
485 | "imsg_compose FETCH_SERVER_PROGRESS"); | |||
486 | ||||
487 | return got_privsep_flush_imsg(ibuf); | |||
488 | } | |||
489 | ||||
490 | static const struct got_error * | |||
491 | send_fetch_download_progress(struct imsgbuf *ibuf, off_t bytes) | |||
492 | { | |||
493 | if (imsg_compose(ibuf, GOT_IMSG_FETCH_DOWNLOAD_PROGRESS, 0, 0, -1, | |||
494 | &bytes, sizeof(bytes)) == -1) | |||
495 | return got_error_from_errno( | |||
496 | "imsg_compose FETCH_DOWNLOAD_PROGRESS"); | |||
497 | ||||
498 | return got_privsep_flush_imsg(ibuf); | |||
499 | } | |||
500 | ||||
501 | static const struct got_error * | |||
502 | send_fetch_done(struct imsgbuf *ibuf, uint8_t *pack_sha1) | |||
503 | { | |||
504 | if (imsg_compose(ibuf, GOT_IMSG_FETCH_DONE, 0, 0, -1, | |||
505 | pack_sha1, SHA1_DIGEST_LENGTH20) == -1) | |||
506 | return got_error_from_errno("imsg_compose FETCH"); | |||
507 | return got_privsep_flush_imsg(ibuf); | |||
508 | } | |||
509 | ||||
510 | ||||
511 | ||||
512 | static const struct got_error * | |||
513 | fetch_progress(struct imsgbuf *ibuf, const char *buf, size_t len) | |||
514 | { | |||
515 | size_t i; | |||
516 | ||||
517 | if (len == 0) | |||
518 | return NULL((void *)0); | |||
519 | ||||
520 | /* | |||
521 | * Truncate messages which exceed the maximum imsg payload size. | |||
522 | * Server may send up to 64k. | |||
523 | */ | |||
524 | if (len > MAX_IMSGSIZE16384 - IMSG_HEADER_SIZEsizeof(struct imsg_hdr)) | |||
525 | len = MAX_IMSGSIZE16384 - IMSG_HEADER_SIZEsizeof(struct imsg_hdr); | |||
526 | ||||
527 | /* Only allow printable ASCII. */ | |||
528 | for (i = 0; i < len; i++) { | |||
529 | if (isprint((unsigned char)buf[i]) || | |||
530 | isspace((unsigned char)buf[i])) | |||
531 | continue; | |||
532 | return got_error_msg(GOT_ERR_BAD_PACKET122, | |||
533 | "non-printable progress message received from server"); | |||
534 | } | |||
535 | ||||
536 | return send_fetch_server_progress(ibuf, buf, len); | |||
537 | } | |||
538 | ||||
539 | static const struct got_error * | |||
540 | fetch_error(const char *buf, size_t len) | |||
541 | { | |||
542 | static char msg[1024]; | |||
543 | size_t i; | |||
544 | ||||
545 | for (i = 0; i < len && i < sizeof(msg) - 1; i++) { | |||
546 | if (!isprint(buf[i])) | |||
547 | return got_error_msg(GOT_ERR_BAD_PACKET122, | |||
548 | "non-printable error message received from server"); | |||
549 | msg[i] = buf[i]; | |||
550 | } | |||
551 | msg[i] = '\0'; | |||
552 | return got_error_msg(GOT_ERR_FETCH_FAILED118, msg); | |||
553 | } | |||
554 | ||||
555 | static const struct got_error * | |||
556 | send_fetch_symrefs(struct imsgbuf *ibuf, struct got_pathlist_head *symrefs) | |||
557 | { | |||
558 | const struct got_error *err = NULL((void *)0); | |||
559 | struct ibuf *wbuf; | |||
560 | size_t len, nsymrefs = 0; | |||
561 | struct got_pathlist_entry *pe; | |||
562 | ||||
563 | len = sizeof(struct got_imsg_fetch_symrefs); | |||
564 | TAILQ_FOREACH(pe, symrefs, entry)for((pe) = ((symrefs)->tqh_first); (pe) != ((void *)0); (pe ) = ((pe)->entry.tqe_next)) { | |||
565 | const char *target = pe->data; | |||
566 | len += sizeof(struct got_imsg_fetch_symref) + | |||
567 | pe->path_len + strlen(target); | |||
568 | nsymrefs++; | |||
569 | } | |||
570 | ||||
571 | if (len >= MAX_IMSGSIZE16384 - IMSG_HEADER_SIZEsizeof(struct imsg_hdr)) | |||
572 | return got_error(GOT_ERR_NO_SPACE9); | |||
573 | ||||
574 | wbuf = imsg_create(ibuf, GOT_IMSG_FETCH_SYMREFS, 0, 0, len); | |||
575 | if (wbuf == NULL((void *)0)) | |||
576 | return got_error_from_errno("imsg_create FETCH_SYMREFS"); | |||
577 | ||||
578 | /* Keep in sync with struct got_imsg_fetch_symrefs definition! */ | |||
579 | if (imsg_add(wbuf, &nsymrefs, sizeof(nsymrefs)) == -1) { | |||
580 | err = got_error_from_errno("imsg_add FETCH_SYMREFS"); | |||
581 | ibuf_free(wbuf); | |||
582 | return err; | |||
583 | } | |||
584 | ||||
585 | TAILQ_FOREACH(pe, symrefs, entry)for((pe) = ((symrefs)->tqh_first); (pe) != ((void *)0); (pe ) = ((pe)->entry.tqe_next)) { | |||
586 | const char *name = pe->path; | |||
587 | size_t name_len = pe->path_len; | |||
588 | const char *target = pe->data; | |||
589 | size_t target_len = strlen(target); | |||
590 | ||||
591 | /* Keep in sync with struct got_imsg_fetch_symref definition! */ | |||
592 | if (imsg_add(wbuf, &name_len, sizeof(name_len)) == -1) { | |||
593 | err = got_error_from_errno("imsg_add FETCH_SYMREFS"); | |||
594 | ibuf_free(wbuf); | |||
595 | return err; | |||
596 | } | |||
597 | if (imsg_add(wbuf, &target_len, sizeof(target_len)) == -1) { | |||
598 | err = got_error_from_errno("imsg_add FETCH_SYMREFS"); | |||
599 | ibuf_free(wbuf); | |||
600 | return err; | |||
601 | } | |||
602 | if (imsg_add(wbuf, name, name_len) == -1) { | |||
603 | err = got_error_from_errno("imsg_add FETCH_SYMREFS"); | |||
604 | ibuf_free(wbuf); | |||
605 | return err; | |||
606 | } | |||
607 | if (imsg_add(wbuf, target, target_len) == -1) { | |||
608 | err = got_error_from_errno("imsg_add FETCH_SYMREFS"); | |||
609 | ibuf_free(wbuf); | |||
610 | return err; | |||
611 | } | |||
612 | } | |||
613 | ||||
614 | wbuf->fd = -1; | |||
615 | imsg_close(ibuf, wbuf); | |||
616 | return got_privsep_flush_imsg(ibuf); | |||
617 | } | |||
618 | ||||
619 | static const struct got_error * | |||
620 | send_fetch_ref(struct imsgbuf *ibuf, struct got_object_id *refid, | |||
621 | const char *refname) | |||
622 | { | |||
623 | const struct got_error *err = NULL((void *)0); | |||
624 | struct ibuf *wbuf; | |||
625 | size_t len, reflen = strlen(refname); | |||
626 | ||||
627 | len = sizeof(struct got_imsg_fetch_ref) + reflen; | |||
628 | if (len >= MAX_IMSGSIZE16384 - IMSG_HEADER_SIZEsizeof(struct imsg_hdr)) | |||
629 | return got_error(GOT_ERR_NO_SPACE9); | |||
630 | ||||
631 | wbuf = imsg_create(ibuf, GOT_IMSG_FETCH_REF, 0, 0, len); | |||
632 | if (wbuf == NULL((void *)0)) | |||
633 | return got_error_from_errno("imsg_create FETCH_REF"); | |||
634 | ||||
635 | /* Keep in sync with struct got_imsg_fetch_ref definition! */ | |||
636 | if (imsg_add(wbuf, refid->sha1, SHA1_DIGEST_LENGTH20) == -1) { | |||
637 | err = got_error_from_errno("imsg_add FETCH_REF"); | |||
638 | ibuf_free(wbuf); | |||
639 | return err; | |||
640 | } | |||
641 | if (imsg_add(wbuf, refname, reflen) == -1) { | |||
642 | err = got_error_from_errno("imsg_add FETCH_REF"); | |||
643 | ibuf_free(wbuf); | |||
644 | return err; | |||
645 | } | |||
646 | ||||
647 | wbuf->fd = -1; | |||
648 | imsg_close(ibuf, wbuf); | |||
649 | return got_privsep_flush_imsg(ibuf); | |||
650 | } | |||
651 | ||||
652 | static const struct got_error * | |||
653 | fetch_pack(int fd, int packfd, uint8_t *pack_sha1, | |||
654 | struct got_pathlist_head *have_refs, int fetch_all_branches, | |||
655 | struct got_pathlist_head *wanted_branches, | |||
656 | struct got_pathlist_head *wanted_refs, int list_refs_only, | |||
657 | struct imsgbuf *ibuf) | |||
658 | { | |||
659 | const struct got_error *err = NULL((void *)0); | |||
660 | char buf[GOT_FETCH_PKTMAX65536]; | |||
661 | char hashstr[SHA1_DIGEST_STRING_LENGTH(20 * 2 + 1)]; | |||
662 | struct got_object_id *have, *want; | |||
663 | int is_firstpkt = 1, nref = 0, refsz = 16; | |||
664 | int i, n, nwant = 0, nhave = 0, acked = 0; | |||
665 | off_t packsz = 0, last_reported_packsz = 0; | |||
666 | char *id_str = NULL((void *)0), *refname = NULL((void *)0); | |||
667 | char *server_capabilities = NULL((void *)0), *my_capabilities = NULL((void *)0); | |||
| ||||
668 | const char *default_branch = NULL((void *)0); | |||
669 | struct got_pathlist_head symrefs; | |||
670 | struct got_pathlist_entry *pe; | |||
671 | int sent_my_capabilites = 0, have_sidebands = 0; | |||
672 | int found_branch = 0; | |||
673 | SHA1_CTX sha1_ctx; | |||
674 | uint8_t sha1_buf[SHA1_DIGEST_LENGTH20]; | |||
675 | size_t sha1_buf_len = 0; | |||
676 | ssize_t w; | |||
677 | ||||
678 | TAILQ_INIT(&symrefs)do { (&symrefs)->tqh_first = ((void *)0); (&symrefs )->tqh_last = &(&symrefs)->tqh_first; } while ( 0); | |||
679 | SHA1Init(&sha1_ctx); | |||
680 | ||||
681 | have = malloc(refsz * sizeof(have[0])); | |||
682 | if (have == NULL((void *)0)) | |||
683 | return got_error_from_errno("malloc"); | |||
684 | want = malloc(refsz * sizeof(want[0])); | |||
685 | if (want == NULL((void *)0)) { | |||
686 | err = got_error_from_errno("malloc"); | |||
687 | goto done; | |||
688 | } | |||
689 | while (1) { | |||
690 | err = readpkt(&n, fd, buf, sizeof(buf)); | |||
691 | if (err
| |||
692 | goto done; | |||
693 | if (n == 0) | |||
694 | break; | |||
695 | if (n >= 4 && strncmp(buf, "ERR ", 4) == 0) { | |||
696 | err = fetch_error(&buf[4], n - 4); | |||
697 | goto done; | |||
698 | } | |||
699 | err = parse_refline(&id_str, &refname, &server_capabilities, | |||
700 | buf, n); | |||
701 | if (err
| |||
702 | goto done; | |||
703 | if (is_firstpkt
| |||
704 | if (chattygot && server_capabilities[0] != '\0') | |||
| ||||
705 | fprintf(stderr(&__sF[2]), "%s: server capabilities: %s\n", | |||
706 | getprogname(), server_capabilities); | |||
707 | err = match_capabilities(&my_capabilities, &symrefs, | |||
708 | server_capabilities); | |||
709 | if (err) | |||
710 | goto done; | |||
711 | if (chattygot) | |||
712 | fprintf(stderr(&__sF[2]), "%s: my capabilities:%s\n", | |||
713 | getprogname(), my_capabilities); | |||
714 | err = send_fetch_symrefs(ibuf, &symrefs); | |||
715 | if (err) | |||
716 | goto done; | |||
717 | is_firstpkt = 0; | |||
718 | if (!fetch_all_branches) { | |||
719 | TAILQ_FOREACH(pe, &symrefs, entry)for((pe) = ((&symrefs)->tqh_first); (pe) != ((void *)0 ); (pe) = ((pe)->entry.tqe_next)) { | |||
720 | const char *name = pe->path; | |||
721 | const char *symref_target = pe->data; | |||
722 | if (strcmp(name, GOT_REF_HEAD"HEAD") != 0) | |||
723 | continue; | |||
724 | default_branch = symref_target; | |||
725 | break; | |||
726 | } | |||
727 | } | |||
728 | continue; | |||
729 | } | |||
730 | if (strstr(refname, "^{}")) { | |||
731 | if (chattygot) { | |||
732 | fprintf(stderr(&__sF[2]), "%s: ignoring %s\n", | |||
733 | getprogname(), refname); | |||
734 | } | |||
735 | continue; | |||
736 | } | |||
737 | ||||
738 | if (strncmp(refname, "refs/heads/", 11) == 0) { | |||
739 | if (fetch_all_branches || list_refs_only) { | |||
740 | found_branch = 1; | |||
741 | } else if (!TAILQ_EMPTY(wanted_branches)(((wanted_branches)->tqh_first) == ((void *)0))) { | |||
742 | TAILQ_FOREACH(pe, wanted_branches, entry)for((pe) = ((wanted_branches)->tqh_first); (pe) != ((void * )0); (pe) = ((pe)->entry.tqe_next)) { | |||
743 | if (match_branch(refname, pe->path)) | |||
744 | break; | |||
745 | } | |||
746 | if (pe == NULL((void *)0)) { | |||
747 | if (chattygot) { | |||
748 | fprintf(stderr(&__sF[2]), | |||
749 | "%s: ignoring %s\n", | |||
750 | getprogname(), refname); | |||
751 | } | |||
752 | continue; | |||
753 | } | |||
754 | found_branch = 1; | |||
755 | } else if (default_branch != NULL((void *)0)) { | |||
756 | if (!match_branch(refname, default_branch)) { | |||
757 | if (chattygot) { | |||
758 | fprintf(stderr(&__sF[2]), | |||
759 | "%s: ignoring %s\n", | |||
760 | getprogname(), refname); | |||
761 | } | |||
762 | continue; | |||
763 | } | |||
764 | found_branch = 1; | |||
765 | } | |||
766 | } else if (strncmp(refname, "refs/tags/", 10) != 0) { | |||
767 | if (!TAILQ_EMPTY(wanted_refs)(((wanted_refs)->tqh_first) == ((void *)0))) { | |||
768 | TAILQ_FOREACH(pe, wanted_refs, entry)for((pe) = ((wanted_refs)->tqh_first); (pe) != ((void *)0) ; (pe) = ((pe)->entry.tqe_next)) { | |||
769 | if (match_wanted_ref(refname, pe->path)) | |||
770 | break; | |||
771 | } | |||
772 | if (pe == NULL((void *)0)) { | |||
773 | if (chattygot) { | |||
774 | fprintf(stderr(&__sF[2]), | |||
775 | "%s: ignoring %s\n", | |||
776 | getprogname(), refname); | |||
777 | } | |||
778 | continue; | |||
779 | } | |||
780 | found_branch = 1; | |||
781 | } else if (!list_refs_only) { | |||
782 | if (chattygot) { | |||
783 | fprintf(stderr(&__sF[2]), "%s: ignoring %s\n", | |||
784 | getprogname(), refname); | |||
785 | } | |||
786 | continue; | |||
787 | } | |||
788 | } | |||
789 | ||||
790 | if (refsz == nref + 1) { | |||
791 | refsz *= 2; | |||
792 | have = reallocarray(have, refsz, sizeof(have[0])); | |||
793 | if (have == NULL((void *)0)) { | |||
794 | err = got_error_from_errno("reallocarray"); | |||
795 | goto done; | |||
796 | } | |||
797 | want = reallocarray(want, refsz, sizeof(want[0])); | |||
798 | if (want == NULL((void *)0)) { | |||
799 | err = got_error_from_errno("reallocarray"); | |||
800 | goto done; | |||
801 | } | |||
802 | } | |||
803 | if (!got_parse_sha1_digest(want[nref].sha1, id_str)) { | |||
804 | err = got_error(GOT_ERR_BAD_OBJ_ID_STR23); | |||
805 | goto done; | |||
806 | } | |||
807 | match_remote_ref(have_refs, &have[nref], refname); | |||
808 | err = send_fetch_ref(ibuf, &want[nref], refname); | |||
809 | if (err) | |||
810 | goto done; | |||
811 | ||||
812 | if (chattygot) | |||
813 | fprintf(stderr(&__sF[2]), "%s: %s will be fetched\n", | |||
814 | getprogname(), refname); | |||
815 | if (chattygot > 1) { | |||
816 | char *theirs, *mine; | |||
817 | err = got_object_id_str(&theirs, &want[nref]); | |||
818 | if (err) | |||
819 | goto done; | |||
820 | err = got_object_id_str(&mine, &have[nref]); | |||
821 | if (err) { | |||
822 | free(theirs); | |||
823 | goto done; | |||
824 | } | |||
825 | fprintf(stderr(&__sF[2]), "%s: remote: %s\n%s: local: %s\n", | |||
826 | getprogname(), theirs, getprogname(), mine); | |||
827 | free(theirs); | |||
828 | free(mine); | |||
829 | } | |||
830 | nref++; | |||
831 | } | |||
832 | ||||
833 | if (list_refs_only) | |||
834 | goto done; | |||
835 | ||||
836 | /* Abort if we haven't found any branch to fetch. */ | |||
837 | if (!found_branch) { | |||
838 | err = got_error(GOT_ERR_FETCH_NO_BRANCH124); | |||
839 | goto done; | |||
840 | } | |||
841 | ||||
842 | for (i = 0; i < nref; i++) { | |||
843 | if (got_object_id_cmp(&have[i], &want[i]) == 0) | |||
844 | continue; | |||
845 | got_sha1_digest_to_str(want[i].sha1, hashstr, sizeof(hashstr)); | |||
846 | n = snprintf(buf, sizeof(buf), "want %s%s\n", hashstr, | |||
847 | sent_my_capabilites ? "" : my_capabilities); | |||
848 | if (n >= sizeof(buf)) { | |||
849 | err = got_error(GOT_ERR_NO_SPACE9); | |||
850 | goto done; | |||
851 | } | |||
852 | err = writepkt(fd, buf, n); | |||
853 | if (err) | |||
854 | goto done; | |||
855 | sent_my_capabilites = 1; | |||
856 | nwant++; | |||
857 | } | |||
858 | err = flushpkt(fd); | |||
859 | if (err) | |||
860 | goto done; | |||
861 | ||||
862 | if (nwant == 0) | |||
863 | goto done; | |||
864 | ||||
865 | for (i = 0; i < nref; i++) { | |||
866 | if (got_object_id_cmp(&have[i], &zhash) == 0) | |||
867 | continue; | |||
868 | got_sha1_digest_to_str(have[i].sha1, hashstr, sizeof(hashstr)); | |||
869 | n = snprintf(buf, sizeof(buf), "have %s\n", hashstr); | |||
870 | if (n >= sizeof(buf)) { | |||
871 | err = got_error(GOT_ERR_NO_SPACE9); | |||
872 | goto done; | |||
873 | } | |||
874 | err = writepkt(fd, buf, n); | |||
875 | if (err) | |||
876 | goto done; | |||
877 | nhave++; | |||
878 | } | |||
879 | ||||
880 | while (nhave > 0 && !acked) { | |||
881 | struct got_object_id common_id; | |||
882 | ||||
883 | /* The server should ACK the object IDs we need. */ | |||
884 | err = readpkt(&n, fd, buf, sizeof(buf)); | |||
885 | if (err) | |||
886 | goto done; | |||
887 | if (n >= 4 && strncmp(buf, "ERR ", 4) == 0) { | |||
888 | err = fetch_error(&buf[4], n - 4); | |||
889 | goto done; | |||
890 | } | |||
891 | if (n >= 4 && strncmp(buf, "NAK\n", 4) == 0) { | |||
892 | /* Server has not located our objects yet. */ | |||
893 | continue; | |||
894 | } | |||
895 | if (n < 4 + SHA1_DIGEST_STRING_LENGTH(20 * 2 + 1) || | |||
896 | strncmp(buf, "ACK ", 4) != 0) { | |||
897 | err = got_error_msg(GOT_ERR_BAD_PACKET122, | |||
898 | "unexpected message from server"); | |||
899 | goto done; | |||
900 | } | |||
901 | if (!got_parse_sha1_digest(common_id.sha1, buf + 4)) { | |||
902 | err = got_error_msg(GOT_ERR_BAD_PACKET122, | |||
903 | "bad object ID in ACK packet from server"); | |||
904 | goto done; | |||
905 | } | |||
906 | acked++; | |||
907 | } | |||
908 | ||||
909 | n = snprintf(buf, sizeof(buf), "done\n"); | |||
910 | err = writepkt(fd, buf, n); | |||
911 | if (err) | |||
912 | goto done; | |||
913 | ||||
914 | if (nhave == 0) { | |||
915 | err = readpkt(&n, fd, buf, sizeof(buf)); | |||
916 | if (err) | |||
917 | goto done; | |||
918 | if (n != 4 || strncmp(buf, "NAK\n", n) != 0) { | |||
919 | err = got_error_msg(GOT_ERR_BAD_PACKET122, | |||
920 | "unexpected message from server"); | |||
921 | goto done; | |||
922 | } | |||
923 | } | |||
924 | ||||
925 | if (chattygot) | |||
926 | fprintf(stderr(&__sF[2]), "%s: fetching...\n", getprogname()); | |||
927 | ||||
928 | if (my_capabilities != NULL((void *)0) && | |||
929 | strstr(my_capabilities, GOT_CAPA_SIDE_BAND_64K"side-band-64k") != NULL((void *)0)) | |||
930 | have_sidebands = 1; | |||
931 | ||||
932 | while (1) { | |||
933 | ssize_t r = 0; | |||
934 | int datalen = -1; | |||
935 | ||||
936 | if (have_sidebands) { | |||
937 | err = read_pkthdr(&datalen, fd); | |||
938 | if (err) | |||
939 | goto done; | |||
940 | if (datalen <= 0) | |||
941 | break; | |||
942 | ||||
943 | /* Read sideband channel ID (one byte). */ | |||
944 | r = read(fd, buf, 1); | |||
945 | if (r == -1) { | |||
946 | err = got_error_from_errno("read"); | |||
947 | goto done; | |||
948 | } | |||
949 | if (r != 1) { | |||
950 | err = got_error_msg(GOT_ERR_BAD_PACKET122, | |||
951 | "short packet"); | |||
952 | goto done; | |||
953 | } | |||
954 | if (datalen > sizeof(buf) - 5) { | |||
955 | err = got_error_msg(GOT_ERR_BAD_PACKET122, | |||
956 | "bad packet length"); | |||
957 | goto done; | |||
958 | } | |||
959 | datalen--; /* sideband ID has been read */ | |||
960 | if (buf[0] == GOT_SIDEBAND_PACKFILE_DATA1) { | |||
961 | /* Read packfile data. */ | |||
962 | err = readn(&r, fd, buf, datalen); | |||
963 | if (err) | |||
964 | goto done; | |||
965 | if (r != datalen) { | |||
966 | err = got_error_msg(GOT_ERR_BAD_PACKET122, | |||
967 | "packet too short"); | |||
968 | goto done; | |||
969 | } | |||
970 | } else if (buf[0] == GOT_SIDEBAND_PROGRESS_INFO2) { | |||
971 | err = readn(&r, fd, buf, datalen); | |||
972 | if (err) | |||
973 | goto done; | |||
974 | if (r != datalen) { | |||
975 | err = got_error_msg(GOT_ERR_BAD_PACKET122, | |||
976 | "packet too short"); | |||
977 | goto done; | |||
978 | } | |||
979 | err = fetch_progress(ibuf, buf, r); | |||
980 | if (err) | |||
981 | goto done; | |||
982 | continue; | |||
983 | } else if (buf[0] == GOT_SIDEBAND_ERROR_INFO3) { | |||
984 | err = readn(&r, fd, buf, datalen); | |||
985 | if (err) | |||
986 | goto done; | |||
987 | if (r != datalen) { | |||
988 | err = got_error_msg(GOT_ERR_BAD_PACKET122, | |||
989 | "packet too short"); | |||
990 | goto done; | |||
991 | } | |||
992 | err = fetch_error(buf, r); | |||
993 | goto done; | |||
994 | } else if (buf[0] == 'A') { | |||
995 | err = readn(&r, fd, buf, datalen); | |||
996 | if (err) | |||
997 | goto done; | |||
998 | if (r != datalen) { | |||
999 | err = got_error_msg(GOT_ERR_BAD_PACKET122, | |||
1000 | "packet too short"); | |||
1001 | goto done; | |||
1002 | } | |||
1003 | /* | |||
1004 | * Git server responds with ACK after 'done' | |||
1005 | * even though multi_ack is disabled?!? | |||
1006 | */ | |||
1007 | buf[r] = '\0'; | |||
1008 | if (strncmp(buf, "CK ", 3) == 0) | |||
1009 | continue; /* ignore */ | |||
1010 | err = got_error_msg(GOT_ERR_BAD_PACKET122, | |||
1011 | "unexpected message from server"); | |||
1012 | goto done; | |||
1013 | } else { | |||
1014 | err = got_error_msg(GOT_ERR_BAD_PACKET122, | |||
1015 | "unknown side-band received from server"); | |||
1016 | goto done; | |||
1017 | } | |||
1018 | } else { | |||
1019 | /* No sideband channel. Every byte is packfile data. */ | |||
1020 | err = readn(&r, fd, buf, sizeof buf); | |||
1021 | if (err) | |||
1022 | goto done; | |||
1023 | if (r <= 0) | |||
1024 | break; | |||
1025 | } | |||
1026 | ||||
1027 | /* | |||
1028 | * An expected SHA1 checksum sits at the end of the pack file. | |||
1029 | * Since we don't know the file size ahead of time we have to | |||
1030 | * keep SHA1_DIGEST_LENGTH bytes buffered and avoid mixing | |||
1031 | * those bytes into our SHA1 checksum computation until we | |||
1032 | * know for sure that additional pack file data bytes follow. | |||
1033 | * | |||
1034 | * We can assume r > 0 since otherwise the loop would exit. | |||
1035 | */ | |||
1036 | if (r < SHA1_DIGEST_LENGTH20) { | |||
1037 | if (sha1_buf_len < SHA1_DIGEST_LENGTH20) { | |||
1038 | /* | |||
1039 | * If there's enough buffered + read data to | |||
1040 | * fill up the buffer then shift a sufficient | |||
1041 | * amount of bytes out at the front to make | |||
1042 | * room, mixing those bytes into the checksum. | |||
1043 | */ | |||
1044 | while (sha1_buf_len > 0 && | |||
1045 | sha1_buf_len + r > SHA1_DIGEST_LENGTH20) { | |||
1046 | SHA1Update(&sha1_ctx, sha1_buf, 1); | |||
1047 | memmove(sha1_buf, sha1_buf + 1, 1); | |||
1048 | sha1_buf_len--; | |||
1049 | } | |||
1050 | ||||
1051 | /* Buffer potential checksum bytes. */ | |||
1052 | memcpy(sha1_buf + sha1_buf_len, buf, r); | |||
1053 | sha1_buf_len += r; | |||
1054 | } else { | |||
1055 | /* | |||
1056 | * Mix in previously buffered bytes which | |||
1057 | * are not part of the checksum after all. | |||
1058 | */ | |||
1059 | SHA1Update(&sha1_ctx, sha1_buf, r); | |||
1060 | ||||
1061 | /* Update potential checksum buffer. */ | |||
1062 | memmove(sha1_buf, sha1_buf + r, | |||
1063 | sha1_buf_len - r); | |||
1064 | memcpy(sha1_buf + sha1_buf_len - r, buf, r); | |||
1065 | } | |||
1066 | } else { | |||
1067 | /* Mix in any previously buffered bytes. */ | |||
1068 | SHA1Update(&sha1_ctx, sha1_buf, sha1_buf_len); | |||
1069 | ||||
1070 | /* Mix in bytes read minus potential checksum bytes. */ | |||
1071 | SHA1Update(&sha1_ctx, buf, r - SHA1_DIGEST_LENGTH20); | |||
1072 | ||||
1073 | /* Buffer potential checksum bytes. */ | |||
1074 | memcpy(sha1_buf, buf + r - SHA1_DIGEST_LENGTH20, | |||
1075 | SHA1_DIGEST_LENGTH20); | |||
1076 | sha1_buf_len = SHA1_DIGEST_LENGTH20; | |||
1077 | } | |||
1078 | ||||
1079 | /* Write packfile data to temporary pack file. */ | |||
1080 | w = write(packfd, buf, r); | |||
1081 | if (w == -1) { | |||
1082 | err = got_error_from_errno("write"); | |||
1083 | goto done; | |||
1084 | } | |||
1085 | if (w != r) { | |||
1086 | err = got_error(GOT_ERR_IO6); | |||
1087 | goto done; | |||
1088 | } | |||
1089 | packsz += w; | |||
1090 | ||||
1091 | /* Don't send too many progress privsep messages. */ | |||
1092 | if (packsz > last_reported_packsz + 1024) { | |||
1093 | err = send_fetch_download_progress(ibuf, packsz); | |||
1094 | if (err) | |||
1095 | goto done; | |||
1096 | last_reported_packsz = packsz; | |||
1097 | } | |||
1098 | } | |||
1099 | err = send_fetch_download_progress(ibuf, packsz); | |||
1100 | if (err) | |||
1101 | goto done; | |||
1102 | ||||
1103 | SHA1Final(pack_sha1, &sha1_ctx); | |||
1104 | if (sha1_buf_len != SHA1_DIGEST_LENGTH20 || | |||
1105 | memcmp(pack_sha1, sha1_buf, sha1_buf_len) != 0) { | |||
1106 | err = got_error_msg(GOT_ERR_BAD_PACKFILE16, | |||
1107 | "pack file checksum mismatch"); | |||
1108 | } | |||
1109 | done: | |||
1110 | TAILQ_FOREACH(pe, &symrefs, entry)for((pe) = ((&symrefs)->tqh_first); (pe) != ((void *)0 ); (pe) = ((pe)->entry.tqe_next)) { | |||
1111 | free((void *)pe->path); | |||
1112 | free(pe->data); | |||
1113 | } | |||
1114 | got_pathlist_free(&symrefs); | |||
1115 | free(have); | |||
1116 | free(want); | |||
1117 | free(id_str); | |||
1118 | free(refname); | |||
1119 | free(server_capabilities); | |||
1120 | return err; | |||
1121 | } | |||
1122 | ||||
1123 | ||||
1124 | int | |||
1125 | main(int argc, char **argv) | |||
1126 | { | |||
1127 | const struct got_error *err = NULL((void *)0); | |||
1128 | int fetchfd, packfd = -1, i; | |||
1129 | uint8_t pack_sha1[SHA1_DIGEST_LENGTH20]; | |||
1130 | struct imsgbuf ibuf; | |||
1131 | struct imsg imsg; | |||
1132 | struct got_pathlist_head have_refs; | |||
1133 | struct got_pathlist_head wanted_branches; | |||
1134 | struct got_pathlist_head wanted_refs; | |||
1135 | struct got_pathlist_entry *pe; | |||
1136 | struct got_imsg_fetch_request fetch_req; | |||
1137 | struct got_imsg_fetch_have_ref href; | |||
1138 | struct got_imsg_fetch_wanted_branch wbranch; | |||
1139 | struct got_imsg_fetch_wanted_ref wref; | |||
1140 | size_t datalen; | |||
1141 | #if 0 | |||
1142 | static int attached; | |||
1143 | while (!attached) | |||
1144 | sleep (1); | |||
1145 | #endif | |||
1146 | ||||
1147 | TAILQ_INIT(&have_refs)do { (&have_refs)->tqh_first = ((void *)0); (&have_refs )->tqh_last = &(&have_refs)->tqh_first; } while (0); | |||
1148 | TAILQ_INIT(&wanted_branches)do { (&wanted_branches)->tqh_first = ((void *)0); (& wanted_branches)->tqh_last = &(&wanted_branches)-> tqh_first; } while (0); | |||
1149 | TAILQ_INIT(&wanted_refs)do { (&wanted_refs)->tqh_first = ((void *)0); (&wanted_refs )->tqh_last = &(&wanted_refs)->tqh_first; } while (0); | |||
1150 | ||||
1151 | imsg_init(&ibuf, GOT_IMSG_FD_CHILD(2 + 1)); | |||
1152 | #ifndef PROFILE | |||
1153 | /* revoke access to most system calls */ | |||
1154 | if (pledge("stdio recvfd", NULL((void *)0)) == -1) { | |||
1155 | err = got_error_from_errno("pledge"); | |||
1156 | got_privsep_send_error(&ibuf, err); | |||
1157 | return 1; | |||
1158 | } | |||
1159 | #endif | |||
1160 | if ((err = got_privsep_recv_imsg(&imsg, &ibuf, 0)) != 0) { | |||
1161 | if (err->code == GOT_ERR_PRIVSEP_PIPE37) | |||
1162 | err = NULL((void *)0); | |||
1163 | goto done; | |||
1164 | } | |||
1165 | if (imsg.hdr.type == GOT_IMSG_STOP) | |||
1166 | goto done; | |||
1167 | if (imsg.hdr.type != GOT_IMSG_FETCH_REQUEST) { | |||
1168 | err = got_error(GOT_ERR_PRIVSEP_MSG39); | |||
1169 | goto done; | |||
1170 | } | |||
1171 | datalen = imsg.hdr.len - IMSG_HEADER_SIZEsizeof(struct imsg_hdr); | |||
1172 | if (datalen < sizeof(fetch_req)) { | |||
1173 | err = got_error(GOT_ERR_PRIVSEP_LEN36); | |||
1174 | goto done; | |||
1175 | } | |||
1176 | memcpy(&fetch_req, imsg.data, sizeof(fetch_req)); | |||
1177 | fetchfd = imsg.fd; | |||
1178 | imsg_free(&imsg); | |||
1179 | ||||
1180 | if (fetch_req.verbosity > 0) | |||
1181 | chattygot += fetch_req.verbosity; | |||
1182 | ||||
1183 | for (i = 0; i < fetch_req.n_have_refs; i++) { | |||
1184 | struct got_object_id *id; | |||
1185 | char *refname; | |||
1186 | ||||
1187 | if ((err = got_privsep_recv_imsg(&imsg, &ibuf, 0)) != 0) { | |||
1188 | if (err->code == GOT_ERR_PRIVSEP_PIPE37) | |||
1189 | err = NULL((void *)0); | |||
1190 | goto done; | |||
1191 | } | |||
1192 | if (imsg.hdr.type == GOT_IMSG_STOP) | |||
1193 | goto done; | |||
1194 | if (imsg.hdr.type != GOT_IMSG_FETCH_HAVE_REF) { | |||
1195 | err = got_error(GOT_ERR_PRIVSEP_MSG39); | |||
1196 | goto done; | |||
1197 | } | |||
1198 | datalen = imsg.hdr.len - IMSG_HEADER_SIZEsizeof(struct imsg_hdr); | |||
1199 | if (datalen < sizeof(href)) { | |||
1200 | err = got_error(GOT_ERR_PRIVSEP_LEN36); | |||
1201 | goto done; | |||
1202 | } | |||
1203 | memcpy(&href, imsg.data, sizeof(href)); | |||
1204 | if (datalen - sizeof(href) < href.name_len) { | |||
1205 | err = got_error(GOT_ERR_PRIVSEP_LEN36); | |||
1206 | goto done; | |||
1207 | } | |||
1208 | refname = malloc(href.name_len + 1); | |||
1209 | if (refname == NULL((void *)0)) { | |||
1210 | err = got_error_from_errno("malloc"); | |||
1211 | goto done; | |||
1212 | } | |||
1213 | memcpy(refname, imsg.data + sizeof(href), href.name_len); | |||
1214 | refname[href.name_len] = '\0'; | |||
1215 | ||||
1216 | id = malloc(sizeof(*id)); | |||
1217 | if (id == NULL((void *)0)) { | |||
1218 | free(refname); | |||
1219 | err = got_error_from_errno("malloc"); | |||
1220 | goto done; | |||
1221 | } | |||
1222 | memcpy(id->sha1, href.id, SHA1_DIGEST_LENGTH20); | |||
1223 | err = got_pathlist_append(&have_refs, refname, id); | |||
1224 | if (err) { | |||
1225 | free(refname); | |||
1226 | free(id); | |||
1227 | goto done; | |||
1228 | } | |||
1229 | ||||
1230 | imsg_free(&imsg); | |||
1231 | } | |||
1232 | ||||
1233 | for (i = 0; i < fetch_req.n_wanted_branches; i++) { | |||
1234 | char *refname; | |||
1235 | ||||
1236 | if ((err = got_privsep_recv_imsg(&imsg, &ibuf, 0)) != 0) { | |||
1237 | if (err->code == GOT_ERR_PRIVSEP_PIPE37) | |||
1238 | err = NULL((void *)0); | |||
1239 | goto done; | |||
1240 | } | |||
1241 | if (imsg.hdr.type == GOT_IMSG_STOP) | |||
1242 | goto done; | |||
1243 | if (imsg.hdr.type != GOT_IMSG_FETCH_WANTED_BRANCH) { | |||
1244 | err = got_error(GOT_ERR_PRIVSEP_MSG39); | |||
1245 | goto done; | |||
1246 | } | |||
1247 | datalen = imsg.hdr.len - IMSG_HEADER_SIZEsizeof(struct imsg_hdr); | |||
1248 | if (datalen < sizeof(wbranch)) { | |||
1249 | err = got_error(GOT_ERR_PRIVSEP_LEN36); | |||
1250 | goto done; | |||
1251 | } | |||
1252 | memcpy(&wbranch, imsg.data, sizeof(wbranch)); | |||
1253 | if (datalen - sizeof(wbranch) < wbranch.name_len) { | |||
1254 | err = got_error(GOT_ERR_PRIVSEP_LEN36); | |||
1255 | goto done; | |||
1256 | } | |||
1257 | refname = malloc(wbranch.name_len + 1); | |||
1258 | if (refname == NULL((void *)0)) { | |||
1259 | err = got_error_from_errno("malloc"); | |||
1260 | goto done; | |||
1261 | } | |||
1262 | memcpy(refname, imsg.data + sizeof(wbranch), wbranch.name_len); | |||
1263 | refname[wbranch.name_len] = '\0'; | |||
1264 | ||||
1265 | err = got_pathlist_append(&wanted_branches, refname, NULL((void *)0)); | |||
1266 | if (err) { | |||
1267 | free(refname); | |||
1268 | goto done; | |||
1269 | } | |||
1270 | ||||
1271 | imsg_free(&imsg); | |||
1272 | } | |||
1273 | ||||
1274 | for (i = 0; i < fetch_req.n_wanted_refs; i++) { | |||
1275 | char *refname; | |||
1276 | ||||
1277 | if ((err = got_privsep_recv_imsg(&imsg, &ibuf, 0)) != 0) { | |||
1278 | if (err->code == GOT_ERR_PRIVSEP_PIPE37) | |||
1279 | err = NULL((void *)0); | |||
1280 | goto done; | |||
1281 | } | |||
1282 | if (imsg.hdr.type == GOT_IMSG_STOP) | |||
1283 | goto done; | |||
1284 | if (imsg.hdr.type != GOT_IMSG_FETCH_WANTED_REF) { | |||
1285 | err = got_error(GOT_ERR_PRIVSEP_MSG39); | |||
1286 | goto done; | |||
1287 | } | |||
1288 | datalen = imsg.hdr.len - IMSG_HEADER_SIZEsizeof(struct imsg_hdr); | |||
1289 | if (datalen < sizeof(wref)) { | |||
1290 | err = got_error(GOT_ERR_PRIVSEP_LEN36); | |||
1291 | goto done; | |||
1292 | } | |||
1293 | memcpy(&wref, imsg.data, sizeof(wref)); | |||
1294 | if (datalen - sizeof(wref) < wref.name_len) { | |||
1295 | err = got_error(GOT_ERR_PRIVSEP_LEN36); | |||
1296 | goto done; | |||
1297 | } | |||
1298 | refname = malloc(wref.name_len + 1); | |||
1299 | if (refname == NULL((void *)0)) { | |||
1300 | err = got_error_from_errno("malloc"); | |||
1301 | goto done; | |||
1302 | } | |||
1303 | memcpy(refname, imsg.data + sizeof(wref), wref.name_len); | |||
1304 | refname[wref.name_len] = '\0'; | |||
1305 | ||||
1306 | err = got_pathlist_append(&wanted_refs, refname, NULL((void *)0)); | |||
1307 | if (err) { | |||
1308 | free(refname); | |||
1309 | goto done; | |||
1310 | } | |||
1311 | ||||
1312 | imsg_free(&imsg); | |||
1313 | } | |||
1314 | ||||
1315 | if ((err = got_privsep_recv_imsg(&imsg, &ibuf, 0)) != 0) { | |||
1316 | if (err->code == GOT_ERR_PRIVSEP_PIPE37) | |||
1317 | err = NULL((void *)0); | |||
1318 | goto done; | |||
1319 | } | |||
1320 | if (imsg.hdr.type == GOT_IMSG_STOP) | |||
1321 | goto done; | |||
1322 | if (imsg.hdr.type != GOT_IMSG_FETCH_OUTFD) { | |||
1323 | err = got_error(GOT_ERR_PRIVSEP_MSG39); | |||
1324 | goto done; | |||
1325 | } | |||
1326 | if (imsg.hdr.len - IMSG_HEADER_SIZEsizeof(struct imsg_hdr) != 0) { | |||
1327 | err = got_error(GOT_ERR_PRIVSEP_LEN36); | |||
1328 | goto done; | |||
1329 | } | |||
1330 | packfd = imsg.fd; | |||
1331 | ||||
1332 | err = fetch_pack(fetchfd, packfd, pack_sha1, &have_refs, | |||
1333 | fetch_req.fetch_all_branches, &wanted_branches, | |||
1334 | &wanted_refs, fetch_req.list_refs_only, &ibuf); | |||
1335 | done: | |||
1336 | TAILQ_FOREACH(pe, &have_refs, entry)for((pe) = ((&have_refs)->tqh_first); (pe) != ((void * )0); (pe) = ((pe)->entry.tqe_next)) { | |||
1337 | free((char *)pe->path); | |||
1338 | free(pe->data); | |||
1339 | } | |||
1340 | got_pathlist_free(&have_refs); | |||
1341 | TAILQ_FOREACH(pe, &wanted_branches, entry)for((pe) = ((&wanted_branches)->tqh_first); (pe) != (( void *)0); (pe) = ((pe)->entry.tqe_next)) | |||
1342 | free((char *)pe->path); | |||
1343 | got_pathlist_free(&wanted_branches); | |||
1344 | if (fetchfd != -1 && close(fetchfd) == -1 && err == NULL((void *)0)) | |||
1345 | err = got_error_from_errno("close"); | |||
1346 | if (packfd != -1 && close(packfd) == -1 && err == NULL((void *)0)) | |||
1347 | err = got_error_from_errno("close"); | |||
1348 | if (err != NULL((void *)0)) | |||
1349 | got_privsep_send_error(&ibuf, err); | |||
1350 | else | |||
1351 | err = send_fetch_done(&ibuf, pack_sha1); | |||
1352 | if (err != NULL((void *)0)) { | |||
1353 | fprintf(stderr(&__sF[2]), "%s: %s\n", getprogname(), err->msg); | |||
1354 | got_privsep_send_error(&ibuf, err); | |||
1355 | } | |||
1356 | ||||
1357 | exit(0); | |||
1358 | } |