1 /* $OpenBSD: repo.c,v 1.8 2021/06/14 10:01:23 claudio Exp $ */ 2 /* 3 * Copyright (c) 2021 Claudio Jeker <claudio@openbsd.org> 4 * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <sys/queue.h> 20 #include <sys/tree.h> 21 #include <sys/types.h> 22 #include <sys/stat.h> 23 24 #include <assert.h> 25 #include <err.h> 26 #include <errno.h> 27 #include <fcntl.h> 28 #include <fts.h> 29 #include <limits.h> 30 #include <stdio.h> 31 #include <stdlib.h> 32 #include <string.h> 33 #include <unistd.h> 34 35 #include <imsg.h> 36 37 #include "extern.h" 38 39 extern struct stats stats; 40 extern int noop; 41 42 enum repo_state { 43 REPO_LOADING = 0, 44 REPO_DONE = 1, 45 REPO_FAILED = -1, 46 }; 47 48 /* 49 * A ta, rsync or rrdp repository. 50 * Depending on what is needed the generic repository is backed by 51 * a ta, rsync or rrdp repository. Multiple repositories can use the 52 * same backend. 53 */ 54 struct rrdprepo { 55 SLIST_ENTRY(rrdprepo) entry; 56 char *notifyuri; 57 char *basedir; 58 char *temp; 59 struct filepath_tree added; 60 struct filepath_tree deleted; 61 size_t id; 62 enum repo_state state; 63 }; 64 SLIST_HEAD(, rrdprepo) rrdprepos = SLIST_HEAD_INITIALIZER(rrdprepos); 65 66 struct rsyncrepo { 67 SLIST_ENTRY(rsyncrepo) entry; 68 char *repouri; 69 char *basedir; 70 size_t id; 71 enum repo_state state; 72 }; 73 SLIST_HEAD(, rsyncrepo) rsyncrepos = SLIST_HEAD_INITIALIZER(rsyncrepos); 74 75 struct tarepo { 76 SLIST_ENTRY(tarepo) entry; 77 char *descr; 78 char *basedir; 79 char *temp; 80 char **uri; 81 size_t urisz; 82 size_t uriidx; 83 size_t id; 84 enum repo_state state; 85 }; 86 SLIST_HEAD(, tarepo) tarepos = SLIST_HEAD_INITIALIZER(tarepos); 87 88 struct repo { 89 SLIST_ENTRY(repo) entry; 90 char *repouri; /* CA repository base URI */ 91 const struct rrdprepo *rrdp; 92 const struct rsyncrepo *rsync; 93 const struct tarepo *ta; 94 struct entityq queue; /* files waiting for repo */ 95 size_t id; /* identifier */ 96 }; 97 SLIST_HEAD(, repo) repos = SLIST_HEAD_INITIALIZER(repos); 98 99 /* counter for unique repo id */ 100 size_t repoid; 101 102 /* 103 * Database of all file path accessed during a run. 104 */ 105 struct filepath { 106 RB_ENTRY(filepath) entry; 107 char *file; 108 }; 109 110 static inline int 111 filepathcmp(struct filepath *a, struct filepath *b) 112 { 113 return strcmp(a->file, b->file); 114 } 115 116 RB_PROTOTYPE(filepath_tree, filepath, entry, filepathcmp); 117 118 /* 119 * Functions to lookup which files have been accessed during computation. 120 */ 121 int 122 filepath_add(struct filepath_tree *tree, char *file) 123 { 124 struct filepath *fp; 125 126 if ((fp = malloc(sizeof(*fp))) == NULL) 127 err(1, NULL); 128 if ((fp->file = strdup(file)) == NULL) 129 err(1, NULL); 130 131 if (RB_INSERT(filepath_tree, tree, fp) != NULL) { 132 /* already in the tree */ 133 free(fp->file); 134 free(fp); 135 return 0; 136 } 137 138 return 1; 139 } 140 141 /* 142 * Lookup a file path in the tree and return the object if found or NULL. 143 */ 144 static struct filepath * 145 filepath_find(struct filepath_tree *tree, char *file) 146 { 147 struct filepath needle; 148 149 needle.file = file; 150 return RB_FIND(filepath_tree, tree, &needle); 151 } 152 153 /* 154 * Returns true if file exists in the tree. 155 */ 156 static int 157 filepath_exists(struct filepath_tree *tree, char *file) 158 { 159 return filepath_find(tree, file) != NULL; 160 } 161 162 /* 163 * Return true if a filepath entry exists that starts with path. 164 */ 165 static int 166 filepath_dir_exists(struct filepath_tree *tree, char *path) 167 { 168 struct filepath needle; 169 struct filepath *res; 170 171 needle.file = path; 172 res = RB_NFIND(filepath_tree, tree, &needle); 173 while (res != NULL && strstr(res->file, path) == res->file) { 174 /* make sure that filepath actually is in that path */ 175 if (res->file[strlen(path)] == '/') 176 return 1; 177 res = RB_NEXT(filepath_tree, tree, res); 178 } 179 return 0; 180 } 181 182 /* 183 * Remove entry from tree and free it. 184 */ 185 static void 186 filepath_put(struct filepath_tree *tree, struct filepath *fp) 187 { 188 RB_REMOVE(filepath_tree, tree, fp); 189 free((void *)fp->file); 190 free(fp); 191 } 192 193 /* 194 * Free all elements of a filepath tree. 195 */ 196 static void 197 filepath_free(struct filepath_tree *tree) 198 { 199 struct filepath *fp, *nfp; 200 201 RB_FOREACH_SAFE(fp, filepath_tree, tree, nfp) 202 filepath_put(tree, fp); 203 } 204 205 RB_GENERATE(filepath_tree, filepath, entry, filepathcmp); 206 207 /* 208 * Function to hash a string into a unique directory name. 209 * prefixed with dir. 210 */ 211 static char * 212 hash_dir(const char *uri, const char *dir) 213 { 214 const char hex[] = "0123456789abcdef"; 215 unsigned char m[SHA256_DIGEST_LENGTH]; 216 char hash[SHA256_DIGEST_LENGTH * 2 + 1]; 217 char *out; 218 size_t i; 219 220 SHA256(uri, strlen(uri), m); 221 for (i = 0; i < SHA256_DIGEST_LENGTH; i++) { 222 hash[i * 2] = hex[m[i] >> 4]; 223 hash[i * 2 + 1] = hex[m[i] & 0xf]; 224 } 225 hash[SHA256_DIGEST_LENGTH * 2] = '\0'; 226 227 asprintf(&out, "%s/%s", dir, hash); 228 return out; 229 } 230 231 /* 232 * Function to build the directory name based on URI and a directory 233 * as prefix. Skip the proto:// in URI but keep everything else. 234 */ 235 static char * 236 rsync_dir(const char *uri, const char *dir) 237 { 238 char *local, *out; 239 240 local = strchr(uri, ':') + strlen("://"); 241 242 asprintf(&out, "%s/%s", dir, local); 243 return out; 244 } 245 246 /* 247 * Function to create all missing directories to a path. 248 * This functions alters the path temporarily. 249 */ 250 static int 251 repo_mkpath(char *file) 252 { 253 char *slash; 254 255 /* build directory hierarchy */ 256 slash = strrchr(file, '/'); 257 assert(slash != NULL); 258 *slash = '\0'; 259 if (mkpath(file) == -1) { 260 warn("mkpath %s", file); 261 return -1; 262 } 263 *slash = '/'; 264 return 0; 265 } 266 267 /* 268 * Build TA file name based on the repo info. 269 * If temp is set add Xs for mkostemp. 270 */ 271 static char * 272 ta_filename(const struct tarepo *tr, int temp) 273 { 274 const char *file; 275 char *nfile; 276 277 /* does not matter which URI, all end with same filename */ 278 file = strrchr(tr->uri[0], '/'); 279 assert(file); 280 281 if (asprintf(&nfile, "%s%s%s", tr->basedir, file, 282 temp ? ".XXXXXXXX": "") == -1) 283 err(1, NULL); 284 285 return nfile; 286 } 287 288 /* 289 * Build local file name base on the URI and the rrdprepo info. 290 */ 291 static char * 292 rrdp_filename(const struct rrdprepo *rr, const char *uri, int temp) 293 { 294 char *nfile; 295 char *dir = rr->basedir; 296 297 if (temp) 298 dir = rr->temp; 299 300 if (!valid_uri(uri, strlen(uri), "rsync://")) { 301 warnx("%s: bad URI %s", rr->basedir, uri); 302 return NULL; 303 } 304 305 uri += strlen("rsync://"); /* skip proto */ 306 if (asprintf(&nfile, "%s/%s", dir, uri) == -1) 307 err(1, NULL); 308 return nfile; 309 } 310 311 /* 312 * Build RRDP state file name based on the repo info. 313 * If temp is set add Xs for mkostemp. 314 */ 315 static char * 316 rrdp_state_filename(const struct rrdprepo *rr, int temp) 317 { 318 char *nfile; 319 320 if (asprintf(&nfile, "%s/.state%s", rr->basedir, 321 temp ? ".XXXXXXXX": "") == -1) 322 err(1, NULL); 323 324 return nfile; 325 } 326 327 328 329 static void 330 ta_fetch(struct tarepo *tr) 331 { 332 logx("ta/%s: pulling from %s", tr->descr, tr->uri[tr->uriidx]); 333 334 if (strncasecmp(tr->uri[tr->uriidx], "rsync://", 8) == 0) { 335 /* 336 * Create destination location. 337 * Build up the tree to this point. 338 */ 339 rsync_fetch(tr->id, tr->uri[tr->uriidx], tr->basedir); 340 } else { 341 int fd; 342 343 tr->temp = ta_filename(tr, 1); 344 fd = mkostemp(tr->temp, O_CLOEXEC); 345 if (fd == -1) { 346 warn("mkostemp: %s", tr->temp); 347 http_finish(tr->id, HTTP_FAILED, NULL); 348 return; 349 } 350 if (fchmod(fd, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) == -1) 351 warn("fchmod: %s", tr->temp); 352 353 http_fetch(tr->id, tr->uri[tr->uriidx], NULL, fd); 354 } 355 } 356 357 static struct tarepo * 358 ta_get(struct tal *tal) 359 { 360 struct tarepo *tr; 361 362 /* no need to look for possible other repo */ 363 364 if (tal->urisz == 0) 365 errx(1, "TAL %s has no URI", tal->descr); 366 367 if ((tr = calloc(1, sizeof(*tr))) == NULL) 368 err(1, NULL); 369 tr->id = ++repoid; 370 SLIST_INSERT_HEAD(&tarepos, tr, entry); 371 372 if ((tr->descr = strdup(tal->descr)) == NULL) 373 err(1, NULL); 374 if (asprintf(&tr->basedir, "ta/%s", tal->descr) == -1) 375 err(1, NULL); 376 377 /* steal URI infromation from TAL */ 378 tr->urisz = tal->urisz; 379 tr->uri = tal->uri; 380 tal->urisz = 0; 381 tal->uri = NULL; 382 383 if (noop) { 384 tr->state = REPO_DONE; 385 logx("ta/%s: using cache", tr->descr); 386 /* there is nothing in the queue so no need to flush */ 387 } else { 388 /* try to create base directory */ 389 if (mkpath(tr->basedir) == -1) 390 warn("mkpath %s", tr->basedir); 391 392 ta_fetch(tr); 393 } 394 395 return tr; 396 } 397 398 static struct tarepo * 399 ta_find(size_t id) 400 { 401 struct tarepo *tr; 402 403 SLIST_FOREACH(tr, &tarepos, entry) 404 if (id == tr->id) 405 break; 406 return tr; 407 } 408 409 static void 410 ta_free(void) 411 { 412 struct tarepo *tr; 413 414 while ((tr = SLIST_FIRST(&tarepos)) != NULL) { 415 SLIST_REMOVE_HEAD(&tarepos, entry); 416 free(tr->descr); 417 free(tr->basedir); 418 free(tr->temp); 419 free(tr->uri); 420 free(tr); 421 } 422 } 423 424 static struct rsyncrepo * 425 rsync_get(const char *uri) 426 { 427 struct rsyncrepo *rr; 428 char *repo; 429 430 if ((repo = rsync_base_uri(uri)) == NULL) 431 errx(1, "bad caRepository URI: %s", uri); 432 433 SLIST_FOREACH(rr, &rsyncrepos, entry) 434 if (strcmp(rr->repouri, repo) == 0) { 435 free(repo); 436 return rr; 437 } 438 439 if ((rr = calloc(1, sizeof(*rr))) == NULL) 440 err(1, NULL); 441 442 rr->id = ++repoid; 443 SLIST_INSERT_HEAD(&rsyncrepos, rr, entry); 444 445 rr->repouri = repo; 446 rr->basedir = rsync_dir(repo, "rsync"); 447 448 if (noop) { 449 rr->state = REPO_DONE; 450 logx("%s: using cache", rr->basedir); 451 /* there is nothing in the queue so no need to flush */ 452 } else { 453 /* create base directory */ 454 if (mkpath(rr->basedir) == -1) { 455 warn("mkpath %s", rr->basedir); 456 rsync_finish(rr->id, 0); 457 return rr; 458 } 459 460 logx("%s: pulling from %s", rr->basedir, rr->repouri); 461 rsync_fetch(rr->id, rr->repouri, rr->basedir); 462 } 463 464 return rr; 465 } 466 467 static struct rsyncrepo * 468 rsync_find(size_t id) 469 { 470 struct rsyncrepo *rr; 471 472 SLIST_FOREACH(rr, &rsyncrepos, entry) 473 if (id == rr->id) 474 break; 475 return rr; 476 } 477 478 static void 479 rsync_free(void) 480 { 481 struct rsyncrepo *rr; 482 483 while ((rr = SLIST_FIRST(&rsyncrepos)) != NULL) { 484 SLIST_REMOVE_HEAD(&rsyncrepos, entry); 485 free(rr->repouri); 486 free(rr->basedir); 487 free(rr); 488 } 489 } 490 491 static int rrdprepo_fetch(struct rrdprepo *); 492 493 static struct rrdprepo * 494 rrdp_get(const char *uri) 495 { 496 struct rrdprepo *rr; 497 498 SLIST_FOREACH(rr, &rrdprepos, entry) 499 if (strcmp(rr->notifyuri, uri) == 0) { 500 if (rr->state == REPO_FAILED) 501 return NULL; 502 return rr; 503 } 504 505 if ((rr = calloc(1, sizeof(*rr))) == NULL) 506 err(1, NULL); 507 508 rr->id = ++repoid; 509 SLIST_INSERT_HEAD(&rrdprepos, rr, entry); 510 511 if ((rr->notifyuri = strdup(uri)) == NULL) 512 err(1, NULL); 513 rr->basedir = hash_dir(uri, "rrdp"); 514 515 RB_INIT(&rr->added); 516 RB_INIT(&rr->deleted); 517 518 if (noop) { 519 rr->state = REPO_DONE; 520 logx("%s: using cache", rr->notifyuri); 521 /* there is nothing in the queue so no need to flush */ 522 } else { 523 /* create base directory */ 524 if (mkpath(rr->basedir) == -1) { 525 warn("mkpath %s", rr->basedir); 526 rrdp_finish(rr->id, 0); 527 return rr; 528 } 529 if (rrdprepo_fetch(rr) == -1) { 530 rrdp_finish(rr->id, 0); 531 return rr; 532 } 533 534 logx("%s: pulling from %s", rr->notifyuri, "network"); 535 } 536 537 return rr; 538 } 539 540 static struct rrdprepo * 541 rrdp_find(size_t id) 542 { 543 struct rrdprepo *rr; 544 545 SLIST_FOREACH(rr, &rrdprepos, entry) 546 if (id == rr->id) 547 break; 548 return rr; 549 } 550 551 static void 552 rrdp_free(void) 553 { 554 struct rrdprepo *rr; 555 556 while ((rr = SLIST_FIRST(&rrdprepos)) != NULL) { 557 SLIST_REMOVE_HEAD(&rrdprepos, entry); 558 559 free(rr->notifyuri); 560 free(rr->basedir); 561 free(rr->temp); 562 563 filepath_free(&rr->added); 564 filepath_free(&rr->deleted); 565 566 free(rr); 567 } 568 } 569 570 static struct rrdprepo * 571 rrdp_basedir(const char *dir) 572 { 573 struct rrdprepo *rr; 574 575 SLIST_FOREACH(rr, &rrdprepos, entry) 576 if (strcmp(dir, rr->basedir) == 0) { 577 if (rr->state == REPO_FAILED) 578 return NULL; 579 return rr; 580 } 581 582 return NULL; 583 } 584 585 /* 586 * Allocate and insert a new repository. 587 */ 588 static struct repo * 589 repo_alloc(void) 590 { 591 struct repo *rp; 592 593 if ((rp = calloc(1, sizeof(*rp))) == NULL) 594 err(1, NULL); 595 596 rp->id = ++repoid; 597 TAILQ_INIT(&rp->queue); 598 SLIST_INSERT_HEAD(&repos, rp, entry); 599 600 stats.repos++; 601 return rp; 602 } 603 604 /* 605 * Return the state of a repository. 606 */ 607 static enum repo_state 608 repo_state(struct repo *rp) 609 { 610 if (rp->ta) 611 return rp->ta->state; 612 if (rp->rrdp) 613 return rp->rrdp->state; 614 if (rp->rsync) 615 return rp->rsync->state; 616 errx(1, "%s: bad repo", rp->repouri); 617 } 618 619 /* 620 * Parse the RRDP state file if it exists and set the session struct 621 * based on that information. 622 */ 623 static void 624 rrdp_parse_state(const struct rrdprepo *rr, struct rrdp_session *state) 625 { 626 FILE *f; 627 int fd, ln = 0; 628 const char *errstr; 629 char *line = NULL, *file; 630 size_t len = 0; 631 ssize_t n; 632 633 file = rrdp_state_filename(rr, 0); 634 if ((fd = open(file, O_RDONLY)) == -1) { 635 if (errno != ENOENT) 636 warn("%s: open state file", rr->basedir); 637 free(file); 638 return; 639 } 640 free(file); 641 f = fdopen(fd, "r"); 642 if (f == NULL) 643 err(1, "fdopen"); 644 645 while ((n = getline(&line, &len, f)) != -1) { 646 if (line[n - 1] == '\n') 647 line[n - 1] = '\0'; 648 switch (ln) { 649 case 0: 650 if ((state->session_id = strdup(line)) == NULL) 651 err(1, NULL); 652 break; 653 case 1: 654 state->serial = strtonum(line, 1, LLONG_MAX, &errstr); 655 if (errstr) 656 goto fail; 657 break; 658 case 2: 659 if ((state->last_mod = strdup(line)) == NULL) 660 err(1, NULL); 661 break; 662 default: 663 goto fail; 664 } 665 ln++; 666 } 667 668 free(line); 669 if (ferror(f)) 670 goto fail; 671 fclose(f); 672 return; 673 674 fail: 675 warnx("%s: troubles reading state file", rr->basedir); 676 fclose(f); 677 free(state->session_id); 678 free(state->last_mod); 679 memset(state, 0, sizeof(*state)); 680 } 681 682 /* 683 * Carefully write the RRDP session state file back. 684 */ 685 void 686 rrdp_save_state(size_t id, struct rrdp_session *state) 687 { 688 struct rrdprepo *rr; 689 char *temp, *file; 690 FILE *f; 691 int fd; 692 693 rr = rrdp_find(id); 694 if (rr == NULL) 695 errx(1, "non-existant rrdp repo %zu", id); 696 697 file = rrdp_state_filename(rr, 0); 698 temp = rrdp_state_filename(rr, 1); 699 700 if ((fd = mkostemp(temp, O_CLOEXEC)) == -1) { 701 warn("mkostemp %s", temp); 702 goto fail; 703 } 704 (void) fchmod(fd, 0644); 705 f = fdopen(fd, "w"); 706 if (f == NULL) 707 err(1, "fdopen"); 708 709 /* write session state file out */ 710 if (fprintf(f, "%s\n%lld\n", state->session_id, 711 state->serial) < 0) { 712 fclose(f); 713 goto fail; 714 } 715 if (state->last_mod != NULL) { 716 if (fprintf(f, "%s\n", state->last_mod) < 0) { 717 fclose(f); 718 goto fail; 719 } 720 } 721 if (fclose(f) != 0) 722 goto fail; 723 724 if (rename(temp, file) == -1) 725 warn("%s: rename state file", rr->basedir); 726 727 free(temp); 728 free(file); 729 return; 730 731 fail: 732 warnx("%s: failed to save state", rr->basedir); 733 unlink(temp); 734 free(temp); 735 free(file); 736 } 737 738 /* 739 * Write a file into the temporary RRDP dir but only after checking 740 * its hash (if required). The function also makes sure that the file 741 * tracking is properly adjusted. 742 * Returns 1 on success, 0 if the repo is corrupt, -1 on IO error 743 */ 744 int 745 rrdp_handle_file(size_t id, enum publish_type pt, char *uri, 746 char *hash, size_t hlen, char *data, size_t dlen) 747 { 748 struct rrdprepo *rr; 749 struct filepath *fp; 750 ssize_t s; 751 char *fn; 752 int fd = -1; 753 754 rr = rrdp_find(id); 755 if (rr == NULL) 756 errx(1, "non-existant rrdp repo %zu", id); 757 if (rr->state == REPO_FAILED) 758 return -1; 759 760 if (pt == PUB_UPD || pt == PUB_DEL) { 761 if (filepath_exists(&rr->deleted, uri)) { 762 warnx("%s: already deleted", uri); 763 return 0; 764 } 765 fp = filepath_find(&rr->added, uri); 766 if (fp == NULL) { 767 if ((fn = rrdp_filename(rr, uri, 0)) == NULL) 768 return 0; 769 } else { 770 filepath_put(&rr->added, fp); 771 if ((fn = rrdp_filename(rr, uri, 1)) == NULL) 772 return 0; 773 } 774 if (!valid_filehash(fn, hash, hlen)) { 775 warnx("%s: bad message digest", fn); 776 free(fn); 777 return 0; 778 } 779 free(fn); 780 } 781 782 if (pt == PUB_DEL) { 783 filepath_add(&rr->deleted, uri); 784 } else { 785 fp = filepath_find(&rr->deleted, uri); 786 if (fp != NULL) 787 filepath_put(&rr->deleted, fp); 788 789 /* add new file to temp dir */ 790 if ((fn = rrdp_filename(rr, uri, 1)) == NULL) 791 return 0; 792 793 if (repo_mkpath(fn) == -1) 794 goto fail; 795 796 fd = open(fn, O_WRONLY|O_CREAT|O_TRUNC, 0644); 797 if (fd == -1) { 798 warn("open %s", fn); 799 goto fail; 800 } 801 802 if ((s = write(fd, data, dlen)) == -1) { 803 warn("write %s", fn); 804 goto fail; 805 } 806 close(fd); 807 if ((size_t)s != dlen) /* impossible */ 808 errx(1, "short write %s", fn); 809 free(fn); 810 filepath_add(&rr->added, uri); 811 } 812 813 return 1; 814 815 fail: 816 rr->state = REPO_FAILED; 817 if (fd != -1) 818 close(fd); 819 free(fn); 820 return -1; 821 } 822 823 /* 824 * Initiate a RRDP sync, create the required temporary directory and 825 * parse a possible state file before sending the request to the RRDP process. 826 */ 827 static int 828 rrdprepo_fetch(struct rrdprepo *rr) 829 { 830 struct rrdp_session state = { 0 }; 831 832 if (asprintf(&rr->temp, "%s.XXXXXXXX", rr->basedir) == -1) 833 err(1, NULL); 834 if (mkdtemp(rr->temp) == NULL) { 835 warn("mkdtemp %s", rr->temp); 836 return -1; 837 } 838 839 rrdp_parse_state(rr, &state); 840 rrdp_fetch(rr->id, rr->notifyuri, rr->notifyuri, &state); 841 842 free(state.session_id); 843 free(state.last_mod); 844 845 return 0; 846 } 847 848 static int 849 rrdp_merge_repo(struct rrdprepo *rr) 850 { 851 struct filepath *fp, *nfp; 852 char *fn, *rfn; 853 854 RB_FOREACH_SAFE(fp, filepath_tree, &rr->added, nfp) { 855 fn = rrdp_filename(rr, fp->file, 1); 856 rfn = rrdp_filename(rr, fp->file, 0); 857 858 if (fn == NULL || rfn == NULL) 859 errx(1, "bad filepath"); /* should not happen */ 860 861 if (repo_mkpath(rfn) == -1) { 862 goto fail; 863 } 864 865 if (rename(fn, rfn) == -1) { 866 warn("rename %s", rfn); 867 goto fail; 868 } 869 870 free(rfn); 871 free(fn); 872 filepath_put(&rr->added, fp); 873 } 874 875 return 1; 876 877 fail: 878 free(rfn); 879 free(fn); 880 return 0; 881 } 882 883 static void 884 rrdp_clean_temp(struct rrdprepo *rr) 885 { 886 struct filepath *fp, *nfp; 887 char *fn; 888 889 filepath_free(&rr->deleted); 890 891 RB_FOREACH_SAFE(fp, filepath_tree, &rr->added, nfp) { 892 if ((fn = rrdp_filename(rr, fp->file, 1)) != NULL) { 893 if (unlink(fn) == -1) 894 warn("unlink %s", fn); 895 free(fn); 896 } 897 filepath_put(&rr->added, fp); 898 } 899 } 900 901 /* 902 * RSYNC sync finished, either with or without success. 903 */ 904 void 905 rsync_finish(size_t id, int ok) 906 { 907 struct rsyncrepo *rr; 908 struct tarepo *tr; 909 struct repo *rp; 910 911 tr = ta_find(id); 912 if (tr != NULL) { 913 if (ok) { 914 logx("ta/%s: loaded from network", tr->descr); 915 stats.rsync_repos++; 916 tr->state = REPO_DONE; 917 } else if (++tr->uriidx < tr->urisz) { 918 logx("ta/%s: load from network failed, retry", 919 tr->descr); 920 ta_fetch(tr); 921 return; 922 } else { 923 logx("ta/%s: load from network failed, " 924 "fallback to cache", tr->descr); 925 stats.rsync_fails++; 926 tr->state = REPO_FAILED; 927 } 928 SLIST_FOREACH(rp, &repos, entry) 929 if (rp->ta == tr) 930 entityq_flush(&rp->queue, rp); 931 932 return; 933 } 934 935 rr = rsync_find(id); 936 if (rr == NULL) 937 errx(1, "unknown rsync repo %zu", id); 938 939 if (ok) { 940 logx("%s: loaded from network", rr->basedir); 941 stats.rsync_repos++; 942 rr->state = REPO_DONE; 943 } else { 944 logx("%s: load from network failed, fallback to cache", 945 rr->basedir); 946 stats.rsync_fails++; 947 rr->state = REPO_FAILED; 948 } 949 950 SLIST_FOREACH(rp, &repos, entry) 951 if (rp->rsync == rr) 952 entityq_flush(&rp->queue, rp); 953 } 954 955 /* 956 * RRDP sync finshed, either with or without success. 957 */ 958 void 959 rrdp_finish(size_t id, int ok) 960 { 961 struct rrdprepo *rr; 962 struct repo *rp; 963 964 rr = rrdp_find(id); 965 if (rr == NULL) 966 errx(1, "unknown RRDP repo %zu", id); 967 968 if (ok && rrdp_merge_repo(rr)) { 969 logx("%s: loaded from network", rr->notifyuri); 970 rr->state = REPO_DONE; 971 stats.rrdp_repos++; 972 SLIST_FOREACH(rp, &repos, entry) 973 if (rp->rrdp == rr) 974 entityq_flush(&rp->queue, rp); 975 } else if (!ok) { 976 rrdp_clean_temp(rr); 977 stats.rrdp_fails++; 978 rr->state = REPO_FAILED; 979 logx("%s: load from network failed, fallback to rsync", 980 rr->notifyuri); 981 SLIST_FOREACH(rp, &repos, entry) 982 if (rp->rrdp == rr) { 983 rp->rrdp = NULL; 984 rp->rsync = rsync_get(rp->repouri); 985 /* need to check if it was already loaded */ 986 if (repo_state(rp) != REPO_LOADING) 987 entityq_flush(&rp->queue, rp); 988 } 989 } else { 990 rrdp_clean_temp(rr); 991 stats.rrdp_fails++; 992 rr->state = REPO_FAILED; 993 logx("%s: load from network failed", rr->notifyuri); 994 SLIST_FOREACH(rp, &repos, entry) 995 if (rp->rrdp == rr) 996 entityq_flush(&rp->queue, rp); 997 } 998 } 999 1000 /* 1001 * Handle responses from the http process. For TA file, either rename 1002 * or delete the temporary file. For RRDP requests relay the request 1003 * over to the rrdp process. 1004 */ 1005 void 1006 http_finish(size_t id, enum http_result res, const char *last_mod) 1007 { 1008 struct tarepo *tr; 1009 struct repo *rp; 1010 1011 tr = ta_find(id); 1012 if (tr == NULL) { 1013 /* not a TA fetch therefor RRDP */ 1014 rrdp_http_done(id, res, last_mod); 1015 return; 1016 } 1017 1018 /* Move downloaded TA file into place, or unlink on failure. */ 1019 if (res == HTTP_OK) { 1020 char *file; 1021 1022 file = ta_filename(tr, 0); 1023 if (rename(tr->temp, file) == -1) 1024 warn("rename to %s", file); 1025 free(file); 1026 1027 logx("ta/%s: loaded from network", tr->descr); 1028 tr->state = REPO_DONE; 1029 stats.http_repos++; 1030 } else { 1031 if (unlink(tr->temp) == -1 && errno != ENOENT) 1032 warn("unlink %s", tr->temp); 1033 1034 if (++tr->uriidx < tr->urisz) { 1035 logx("ta/%s: load from network failed, retry", 1036 tr->descr); 1037 ta_fetch(tr); 1038 return; 1039 } 1040 1041 tr->state = REPO_FAILED; 1042 logx("ta/%s: load from network failed, " 1043 "fallback to cache", tr->descr); 1044 } 1045 1046 SLIST_FOREACH(rp, &repos, entry) 1047 if (rp->ta == tr) 1048 entityq_flush(&rp->queue, rp); 1049 } 1050 1051 1052 1053 /* 1054 * Look up a trust anchor, queueing it for download if not found. 1055 */ 1056 struct repo * 1057 ta_lookup(struct tal *tal) 1058 { 1059 struct repo *rp; 1060 1061 /* Look up in repository table. (Lookup should actually fail here) */ 1062 SLIST_FOREACH(rp, &repos, entry) { 1063 if (strcmp(rp->repouri, tal->descr) == 0) 1064 return rp; 1065 } 1066 1067 rp = repo_alloc(); 1068 if ((rp->repouri = strdup(tal->descr)) == NULL) 1069 err(1, NULL); 1070 rp->ta = ta_get(tal); 1071 1072 return rp; 1073 } 1074 1075 /* 1076 * Look up a repository, queueing it for discovery if not found. 1077 */ 1078 struct repo * 1079 repo_lookup(const char *uri, const char *notify) 1080 { 1081 struct repo *rp; 1082 1083 /* Look up in repository table. */ 1084 SLIST_FOREACH(rp, &repos, entry) { 1085 if (strcmp(rp->repouri, uri) != 0) 1086 continue; 1087 return rp; 1088 } 1089 1090 rp = repo_alloc(); 1091 if ((rp->repouri = strdup(uri)) == NULL) 1092 err(1, NULL); 1093 1094 /* try RRDP first if available */ 1095 if (notify != NULL) 1096 rp->rrdp = rrdp_get(notify); 1097 if (rp->rrdp == NULL) 1098 rp->rsync = rsync_get(uri); 1099 1100 return rp; 1101 } 1102 1103 /* 1104 * Build local file name base on the URI and the repo info. 1105 */ 1106 char * 1107 repo_filename(const struct repo *rp, const char *uri) 1108 { 1109 char *nfile; 1110 char *dir, *repouri; 1111 1112 if (uri == NULL && rp->ta) 1113 return ta_filename(rp->ta, 0); 1114 1115 assert(uri != NULL); 1116 if (rp->rrdp) 1117 return rrdp_filename(rp->rrdp, uri, 0); 1118 1119 /* must be rsync */ 1120 dir = rp->rsync->basedir; 1121 repouri = rp->rsync->repouri; 1122 1123 if (strstr(uri, repouri) != uri) { 1124 warnx("%s: URI %s outside of repository", repouri, uri); 1125 return NULL; 1126 } 1127 1128 uri += strlen(repouri) + 1; /* skip base and '/' */ 1129 1130 if (asprintf(&nfile, "%s/%s", dir, uri) == -1) 1131 err(1, NULL); 1132 return nfile; 1133 } 1134 1135 int 1136 repo_queued(struct repo *rp, struct entity *p) 1137 { 1138 if (repo_state(rp) == REPO_LOADING) { 1139 TAILQ_INSERT_TAIL(&rp->queue, p, entries); 1140 return 1; 1141 } 1142 return 0; 1143 } 1144 1145 static char ** 1146 add_to_del(char **del, size_t *dsz, char *file) 1147 { 1148 size_t i = *dsz; 1149 1150 del = reallocarray(del, i + 1, sizeof(*del)); 1151 if (del == NULL) 1152 err(1, NULL); 1153 if ((del[i] = strdup(file)) == NULL) 1154 err(1, NULL); 1155 *dsz = i + 1; 1156 return del; 1157 } 1158 1159 static char ** 1160 repo_rrdp_cleanup(struct filepath_tree *tree, struct rrdprepo *rr, 1161 char **del, size_t *delsz) 1162 { 1163 struct filepath *fp, *nfp; 1164 char *fn; 1165 1166 RB_FOREACH_SAFE(fp, filepath_tree, &rr->deleted, nfp) { 1167 fn = rrdp_filename(rr, fp->file, 0); 1168 /* temp dir will be cleaned up by repo_cleanup() */ 1169 1170 if (fn == NULL) 1171 errx(1, "bad filepath"); /* should not happen */ 1172 1173 if (!filepath_exists(tree, fn)) 1174 del = add_to_del(del, delsz, fn); 1175 else 1176 warnx("%s: referenced file supposed to be deleted", fn); 1177 1178 free(fn); 1179 filepath_put(&rr->deleted, fp); 1180 } 1181 1182 return del; 1183 } 1184 1185 void 1186 repo_cleanup(struct filepath_tree *tree) 1187 { 1188 size_t i, cnt, delsz = 0, dirsz = 0; 1189 char **del = NULL, **dir = NULL; 1190 char *argv[4] = { "ta", "rsync", "rrdp", NULL }; 1191 struct rrdprepo *rr; 1192 FTS *fts; 1193 FTSENT *e; 1194 1195 if ((fts = fts_open(argv, FTS_PHYSICAL | FTS_NOSTAT, NULL)) == NULL) 1196 err(1, "fts_open"); 1197 errno = 0; 1198 while ((e = fts_read(fts)) != NULL) { 1199 switch (e->fts_info) { 1200 case FTS_NSOK: 1201 if (!filepath_exists(tree, e->fts_path)) 1202 del = add_to_del(del, &delsz, 1203 e->fts_path); 1204 break; 1205 case FTS_D: 1206 /* special cleanup for rrdp directories */ 1207 if ((rr = rrdp_basedir(e->fts_path)) != NULL) { 1208 del = repo_rrdp_cleanup(tree, rr, del, &delsz); 1209 if (fts_set(fts, e, FTS_SKIP) == -1) 1210 err(1, "fts_set"); 1211 } 1212 break; 1213 case FTS_DP: 1214 if (!filepath_dir_exists(tree, e->fts_path)) 1215 dir = add_to_del(dir, &dirsz, 1216 e->fts_path); 1217 break; 1218 case FTS_SL: 1219 case FTS_SLNONE: 1220 warnx("symlink %s", e->fts_path); 1221 del = add_to_del(del, &delsz, e->fts_path); 1222 break; 1223 case FTS_NS: 1224 case FTS_ERR: 1225 if (e->fts_errno == ENOENT && 1226 (strcmp(e->fts_path, "rsync") == 0 || 1227 strcmp(e->fts_path, "rrdp") == 0)) 1228 continue; 1229 warnx("fts_read %s: %s", e->fts_path, 1230 strerror(e->fts_errno)); 1231 break; 1232 default: 1233 warnx("unhandled[%x] %s", e->fts_info, 1234 e->fts_path); 1235 break; 1236 } 1237 1238 errno = 0; 1239 } 1240 if (errno) 1241 err(1, "fts_read"); 1242 if (fts_close(fts) == -1) 1243 err(1, "fts_close"); 1244 1245 cnt = 0; 1246 for (i = 0; i < delsz; i++) { 1247 if (unlink(del[i]) == -1) { 1248 if (errno != ENOENT) 1249 warn("unlink %s", del[i]); 1250 } else { 1251 if (verbose > 1) 1252 logx("deleted %s", del[i]); 1253 cnt++; 1254 } 1255 free(del[i]); 1256 } 1257 free(del); 1258 stats.del_files = cnt; 1259 1260 cnt = 0; 1261 for (i = 0; i < dirsz; i++) { 1262 if (rmdir(dir[i]) == -1) 1263 warn("rmdir %s", dir[i]); 1264 else 1265 cnt++; 1266 free(dir[i]); 1267 } 1268 free(dir); 1269 stats.del_dirs = cnt; 1270 } 1271 1272 void 1273 repo_free(void) 1274 { 1275 struct repo *rp; 1276 1277 while ((rp = SLIST_FIRST(&repos)) != NULL) { 1278 SLIST_REMOVE_HEAD(&repos, entry); 1279 free(rp->repouri); 1280 free(rp); 1281 } 1282 1283 ta_free(); 1284 rrdp_free(); 1285 rsync_free(); 1286 } 1287