| File: | libexec/got-fetch-pack/got-fetch-pack.c |
| Warning: | line 431, column 9 Potential leak of memory pointed to by 'name' |
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
| |||
| 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 | } |