1 /* $OpenBSD: repo.c,v 1.44 2023/04/26 16:32:41 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 <poll.h> 31 #include <stdio.h> 32 #include <stdlib.h> 33 #include <string.h> 34 #include <unistd.h> 35 36 #include <imsg.h> 37 38 #include "extern.h" 39 40 extern struct stats stats; 41 extern int noop; 42 extern int rrdpon; 43 extern int repo_timeout; 44 extern time_t deadline; 45 int nofetch; 46 47 enum repo_state { 48 REPO_LOADING = 0, 49 REPO_DONE = 1, 50 REPO_FAILED = -1, 51 }; 52 53 /* 54 * A ta, rsync or rrdp repository. 55 * Depending on what is needed the generic repository is backed by 56 * a ta, rsync or rrdp repository. Multiple repositories can use the 57 * same backend. 58 */ 59 struct rrdprepo { 60 SLIST_ENTRY(rrdprepo) entry; 61 char *notifyuri; 62 char *basedir; 63 struct filepath_tree deleted; 64 unsigned int id; 65 enum repo_state state; 66 }; 67 static SLIST_HEAD(, rrdprepo) rrdprepos = SLIST_HEAD_INITIALIZER(rrdprepos); 68 69 struct rsyncrepo { 70 SLIST_ENTRY(rsyncrepo) entry; 71 char *repouri; 72 char *basedir; 73 unsigned int id; 74 enum repo_state state; 75 }; 76 static SLIST_HEAD(, rsyncrepo) rsyncrepos = SLIST_HEAD_INITIALIZER(rsyncrepos); 77 78 struct tarepo { 79 SLIST_ENTRY(tarepo) entry; 80 char *descr; 81 char *basedir; 82 char *temp; 83 char **uri; 84 size_t urisz; 85 size_t uriidx; 86 unsigned int id; 87 enum repo_state state; 88 }; 89 static SLIST_HEAD(, tarepo) tarepos = SLIST_HEAD_INITIALIZER(tarepos); 90 91 struct repo { 92 SLIST_ENTRY(repo) entry; 93 char *repouri; 94 char *notifyuri; 95 char *basedir; 96 const struct rrdprepo *rrdp; 97 const struct rsyncrepo *rsync; 98 const struct tarepo *ta; 99 struct entityq queue; /* files waiting for repo */ 100 struct repotalstats stats[TALSZ_MAX]; 101 struct repostats repostats; 102 struct timespec start_time; 103 time_t alarm; /* sync timeout */ 104 int talid; 105 int stats_used[TALSZ_MAX]; 106 unsigned int id; /* identifier */ 107 }; 108 static SLIST_HEAD(, repo) repos = SLIST_HEAD_INITIALIZER(repos); 109 110 /* counter for unique repo id */ 111 unsigned int repoid; 112 113 static struct rsyncrepo *rsync_get(const char *, const char *); 114 static void remove_contents(char *); 115 116 /* 117 * Database of all file path accessed during a run. 118 */ 119 struct filepath { 120 RB_ENTRY(filepath) entry; 121 char *file; 122 }; 123 124 static inline int 125 filepathcmp(struct filepath *a, struct filepath *b) 126 { 127 return strcmp(a->file, b->file); 128 } 129 130 RB_PROTOTYPE(filepath_tree, filepath, entry, filepathcmp); 131 132 /* 133 * Functions to lookup which files have been accessed during computation. 134 */ 135 int 136 filepath_add(struct filepath_tree *tree, char *file) 137 { 138 struct filepath *fp; 139 140 if ((fp = malloc(sizeof(*fp))) == NULL) 141 err(1, NULL); 142 if ((fp->file = strdup(file)) == NULL) 143 err(1, NULL); 144 145 if (RB_INSERT(filepath_tree, tree, fp) != NULL) { 146 /* already in the tree */ 147 free(fp->file); 148 free(fp); 149 return 0; 150 } 151 152 return 1; 153 } 154 155 /* 156 * Lookup a file path in the tree and return the object if found or NULL. 157 */ 158 static struct filepath * 159 filepath_find(struct filepath_tree *tree, char *file) 160 { 161 struct filepath needle = { .file = file }; 162 163 return RB_FIND(filepath_tree, tree, &needle); 164 } 165 166 /* 167 * Returns true if file exists in the tree. 168 */ 169 static int 170 filepath_exists(struct filepath_tree *tree, char *file) 171 { 172 return filepath_find(tree, file) != NULL; 173 } 174 175 /* 176 * Remove entry from tree and free it. 177 */ 178 static void 179 filepath_put(struct filepath_tree *tree, struct filepath *fp) 180 { 181 RB_REMOVE(filepath_tree, tree, fp); 182 free((void *)fp->file); 183 free(fp); 184 } 185 186 /* 187 * Free all elements of a filepath tree. 188 */ 189 static void 190 filepath_free(struct filepath_tree *tree) 191 { 192 struct filepath *fp, *nfp; 193 194 RB_FOREACH_SAFE(fp, filepath_tree, tree, nfp) 195 filepath_put(tree, fp); 196 } 197 198 RB_GENERATE(filepath_tree, filepath, entry, filepathcmp); 199 200 /* 201 * Function to hash a string into a unique directory name. 202 * Returned hash needs to be freed. 203 */ 204 static char * 205 hash_dir(const char *uri) 206 { 207 unsigned char m[SHA256_DIGEST_LENGTH]; 208 209 SHA256(uri, strlen(uri), m); 210 return hex_encode(m, sizeof(m)); 211 } 212 213 /* 214 * Function to build the directory name based on URI and a directory 215 * as prefix. Skip the proto:// in URI but keep everything else. 216 */ 217 static char * 218 repo_dir(const char *uri, const char *dir, int hash) 219 { 220 const char *local; 221 char *out, *hdir = NULL; 222 223 if (hash) { 224 local = hdir = hash_dir(uri); 225 } else { 226 local = strchr(uri, ':'); 227 if (local != NULL) 228 local += strlen("://"); 229 else 230 local = uri; 231 } 232 233 if (dir == NULL) { 234 if ((out = strdup(local)) == NULL) 235 err(1, NULL); 236 } else { 237 if (asprintf(&out, "%s/%s", dir, local) == -1) 238 err(1, NULL); 239 } 240 241 free(hdir); 242 return out; 243 } 244 245 /* 246 * Function to create all missing directories to a path. 247 * This functions alters the path temporarily. 248 */ 249 static int 250 repo_mkpath(int fd, char *file) 251 { 252 char *slash; 253 254 /* build directory hierarchy */ 255 slash = strrchr(file, '/'); 256 assert(slash != NULL); 257 *slash = '\0'; 258 if (mkpathat(fd, file) == -1) { 259 warn("mkpath %s", file); 260 return -1; 261 } 262 *slash = '/'; 263 return 0; 264 } 265 266 /* 267 * Return the state of a repository. 268 */ 269 static enum repo_state 270 repo_state(const struct repo *rp) 271 { 272 if (rp->ta) 273 return rp->ta->state; 274 if (rp->rsync) 275 return rp->rsync->state; 276 if (rp->rrdp) 277 return rp->rrdp->state; 278 /* No backend so sync is by definition done. */ 279 return REPO_DONE; 280 } 281 282 /* 283 * Function called once a repository is done with the sync. Either 284 * successfully or after failure. 285 */ 286 static void 287 repo_done(const void *vp, int ok) 288 { 289 struct repo *rp; 290 struct timespec flush_time; 291 292 SLIST_FOREACH(rp, &repos, entry) { 293 if (vp != rp->ta && vp != rp->rsync && vp != rp->rrdp) 294 continue; 295 296 /* for rrdp try to fall back to rsync */ 297 if (vp == rp->rrdp && !ok && !nofetch) { 298 rp->rrdp = NULL; 299 rp->rsync = rsync_get(rp->repouri, rp->basedir); 300 /* need to check if it was already loaded */ 301 if (repo_state(rp) == REPO_LOADING) 302 continue; 303 } 304 305 entityq_flush(&rp->queue, rp); 306 clock_gettime(CLOCK_MONOTONIC, &flush_time); 307 timespecsub(&flush_time, &rp->start_time, 308 &rp->repostats.sync_time); 309 } 310 } 311 312 /* 313 * Build TA file name based on the repo info. 314 * If temp is set add Xs for mkostemp. 315 */ 316 static char * 317 ta_filename(const struct tarepo *tr, int temp) 318 { 319 const char *file; 320 char *nfile; 321 322 /* does not matter which URI, all end with same filename */ 323 file = strrchr(tr->uri[0], '/'); 324 assert(file); 325 326 if (asprintf(&nfile, "%s%s%s", tr->basedir, file, 327 temp ? ".XXXXXXXX" : "") == -1) 328 err(1, NULL); 329 330 return nfile; 331 } 332 333 static void 334 ta_fetch(struct tarepo *tr) 335 { 336 if (!rrdpon) { 337 for (; tr->uriidx < tr->urisz; tr->uriidx++) { 338 if (strncasecmp(tr->uri[tr->uriidx], 339 "rsync://", 8) == 0) 340 break; 341 } 342 } 343 344 if (tr->uriidx >= tr->urisz) { 345 tr->state = REPO_FAILED; 346 logx("ta/%s: fallback to cache", tr->descr); 347 348 repo_done(tr, 0); 349 return; 350 } 351 352 logx("ta/%s: pulling from %s", tr->descr, tr->uri[tr->uriidx]); 353 354 if (strncasecmp(tr->uri[tr->uriidx], "rsync://", 8) == 0) { 355 /* 356 * Create destination location. 357 * Build up the tree to this point. 358 */ 359 rsync_fetch(tr->id, tr->uri[tr->uriidx], tr->basedir, NULL); 360 } else { 361 int fd; 362 363 tr->temp = ta_filename(tr, 1); 364 fd = mkostemp(tr->temp, O_CLOEXEC); 365 if (fd == -1) { 366 warn("mkostemp: %s", tr->temp); 367 http_finish(tr->id, HTTP_FAILED, NULL); 368 return; 369 } 370 if (fchmod(fd, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) == -1) 371 warn("fchmod: %s", tr->temp); 372 373 http_fetch(tr->id, tr->uri[tr->uriidx], NULL, fd); 374 } 375 } 376 377 static struct tarepo * 378 ta_get(struct tal *tal) 379 { 380 struct tarepo *tr; 381 382 /* no need to look for possible other repo */ 383 384 if ((tr = calloc(1, sizeof(*tr))) == NULL) 385 err(1, NULL); 386 tr->id = ++repoid; 387 SLIST_INSERT_HEAD(&tarepos, tr, entry); 388 389 if ((tr->descr = strdup(tal->descr)) == NULL) 390 err(1, NULL); 391 tr->basedir = repo_dir(tal->descr, "ta", 0); 392 393 /* steal URI information from TAL */ 394 tr->urisz = tal->urisz; 395 tr->uri = tal->uri; 396 tal->urisz = 0; 397 tal->uri = NULL; 398 399 ta_fetch(tr); 400 401 return tr; 402 } 403 404 static struct tarepo * 405 ta_find(unsigned int id) 406 { 407 struct tarepo *tr; 408 409 SLIST_FOREACH(tr, &tarepos, entry) 410 if (id == tr->id) 411 break; 412 return tr; 413 } 414 415 static void 416 ta_free(void) 417 { 418 struct tarepo *tr; 419 420 while ((tr = SLIST_FIRST(&tarepos)) != NULL) { 421 SLIST_REMOVE_HEAD(&tarepos, entry); 422 free(tr->descr); 423 free(tr->basedir); 424 free(tr->temp); 425 free(tr->uri); 426 free(tr); 427 } 428 } 429 430 static struct rsyncrepo * 431 rsync_get(const char *uri, const char *validdir) 432 { 433 struct rsyncrepo *rr; 434 char *repo; 435 436 if ((repo = rsync_base_uri(uri)) == NULL) 437 errx(1, "bad caRepository URI: %s", uri); 438 439 SLIST_FOREACH(rr, &rsyncrepos, entry) 440 if (strcmp(rr->repouri, repo) == 0) { 441 free(repo); 442 return rr; 443 } 444 445 if ((rr = calloc(1, sizeof(*rr))) == NULL) 446 err(1, NULL); 447 448 rr->id = ++repoid; 449 SLIST_INSERT_HEAD(&rsyncrepos, rr, entry); 450 451 rr->repouri = repo; 452 rr->basedir = repo_dir(repo, ".rsync", 0); 453 454 /* create base directory */ 455 if (mkpath(rr->basedir) == -1) { 456 warn("mkpath %s", rr->basedir); 457 rsync_finish(rr->id, 0); 458 return rr; 459 } 460 461 logx("%s: pulling from %s", rr->basedir, rr->repouri); 462 rsync_fetch(rr->id, rr->repouri, rr->basedir, validdir); 463 464 return rr; 465 } 466 467 static struct rsyncrepo * 468 rsync_find(unsigned int 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 /* 492 * Build local file name base on the URI and the rrdprepo info. 493 */ 494 static char * 495 rrdp_filename(const struct rrdprepo *rr, const char *uri, int valid) 496 { 497 char *nfile; 498 const char *dir = rr->basedir; 499 500 if (!valid_uri(uri, strlen(uri), "rsync://")) 501 errx(1, "%s: bad URI %s", rr->basedir, uri); 502 uri += strlen("rsync://"); /* skip proto */ 503 if (valid) { 504 if ((nfile = strdup(uri)) == NULL) 505 err(1, NULL); 506 } else { 507 if (asprintf(&nfile, "%s/%s", dir, uri) == -1) 508 err(1, NULL); 509 } 510 return nfile; 511 } 512 513 /* 514 * Build RRDP state file name based on the repo info. 515 * If temp is set add Xs for mkostemp. 516 */ 517 static char * 518 rrdp_state_filename(const struct rrdprepo *rr, int temp) 519 { 520 char *nfile; 521 522 if (asprintf(&nfile, "%s/.state%s", rr->basedir, 523 temp ? ".XXXXXXXX" : "") == -1) 524 err(1, NULL); 525 526 return nfile; 527 } 528 529 static struct rrdprepo * 530 rrdp_find(unsigned int id) 531 { 532 struct rrdprepo *rr; 533 534 SLIST_FOREACH(rr, &rrdprepos, entry) 535 if (id == rr->id) 536 break; 537 return rr; 538 } 539 540 static void 541 rrdp_free(void) 542 { 543 struct rrdprepo *rr; 544 545 while ((rr = SLIST_FIRST(&rrdprepos)) != NULL) { 546 SLIST_REMOVE_HEAD(&rrdprepos, entry); 547 548 free(rr->notifyuri); 549 free(rr->basedir); 550 551 filepath_free(&rr->deleted); 552 553 free(rr); 554 } 555 } 556 557 /* 558 * Check if a directory is an active rrdp repository. 559 * Returns 1 if found else 0. 560 */ 561 static struct repo * 562 repo_rrdp_bypath(const char *dir) 563 { 564 struct repo *rp; 565 566 SLIST_FOREACH(rp, &repos, entry) { 567 if (rp->rrdp == NULL) 568 continue; 569 if (strcmp(dir, rp->rrdp->basedir) == 0) 570 return rp; 571 } 572 return NULL; 573 } 574 575 /* 576 * Check if the URI is actually covered by one of the repositories 577 * that depend on this RRDP repository. 578 * Returns 1 if the URI is valid, 0 if no repouri matches the URI. 579 */ 580 static int 581 rrdp_uri_valid(struct rrdprepo *rr, const char *uri) 582 { 583 struct repo *rp; 584 585 SLIST_FOREACH(rp, &repos, entry) { 586 if (rp->rrdp != rr) 587 continue; 588 if (strncmp(uri, rp->repouri, strlen(rp->repouri)) == 0) 589 return 1; 590 } 591 return 0; 592 } 593 594 /* 595 * Allocate and insert a new repository. 596 */ 597 static struct repo * 598 repo_alloc(int talid) 599 { 600 struct repo *rp; 601 602 if ((rp = calloc(1, sizeof(*rp))) == NULL) 603 err(1, NULL); 604 605 rp->id = ++repoid; 606 rp->talid = talid; 607 rp->alarm = getmonotime() + repo_timeout; 608 TAILQ_INIT(&rp->queue); 609 SLIST_INSERT_HEAD(&repos, rp, entry); 610 clock_gettime(CLOCK_MONOTONIC, &rp->start_time); 611 612 stats.repos++; 613 return rp; 614 } 615 616 /* 617 * Parse the RRDP state file if it exists and set the session struct 618 * based on that information. 619 */ 620 static void 621 rrdp_parse_state(const struct rrdprepo *rr, struct rrdp_session *state) 622 { 623 FILE *f; 624 int fd, ln = 0; 625 const char *errstr; 626 char *line = NULL, *file; 627 size_t len = 0; 628 ssize_t n; 629 630 file = rrdp_state_filename(rr, 0); 631 if ((fd = open(file, O_RDONLY)) == -1) { 632 if (errno != ENOENT) 633 warn("%s: open state file", rr->basedir); 634 free(file); 635 return; 636 } 637 free(file); 638 f = fdopen(fd, "r"); 639 if (f == NULL) 640 err(1, "fdopen"); 641 642 while ((n = getline(&line, &len, f)) != -1) { 643 if (line[n - 1] == '\n') 644 line[n - 1] = '\0'; 645 switch (ln) { 646 case 0: 647 if ((state->session_id = strdup(line)) == NULL) 648 err(1, NULL); 649 break; 650 case 1: 651 state->serial = strtonum(line, 1, LLONG_MAX, &errstr); 652 if (errstr) 653 goto fail; 654 break; 655 case 2: 656 if ((state->last_mod = strdup(line)) == NULL) 657 err(1, NULL); 658 break; 659 default: 660 goto fail; 661 } 662 ln++; 663 } 664 665 free(line); 666 if (ferror(f)) 667 goto fail; 668 fclose(f); 669 return; 670 671 fail: 672 warnx("%s: troubles reading state file", rr->basedir); 673 fclose(f); 674 free(state->session_id); 675 free(state->last_mod); 676 memset(state, 0, sizeof(*state)); 677 } 678 679 /* 680 * Carefully write the RRDP session state file back. 681 */ 682 void 683 rrdp_save_state(unsigned int id, struct rrdp_session *state) 684 { 685 struct rrdprepo *rr; 686 char *temp, *file; 687 FILE *f; 688 int fd; 689 690 rr = rrdp_find(id); 691 if (rr == NULL) 692 errx(1, "non-existent rrdp repo %u", id); 693 694 file = rrdp_state_filename(rr, 0); 695 temp = rrdp_state_filename(rr, 1); 696 697 if ((fd = mkostemp(temp, O_CLOEXEC)) == -1) { 698 warn("mkostemp %s", temp); 699 goto fail; 700 } 701 (void)fchmod(fd, 0644); 702 f = fdopen(fd, "w"); 703 if (f == NULL) 704 err(1, "fdopen"); 705 706 /* write session state file out */ 707 if (fprintf(f, "%s\n%lld\n", state->session_id, 708 state->serial) < 0) { 709 fclose(f); 710 goto fail; 711 } 712 if (state->last_mod != NULL) { 713 if (fprintf(f, "%s\n", state->last_mod) < 0) { 714 fclose(f); 715 goto fail; 716 } 717 } 718 if (fclose(f) != 0) 719 goto fail; 720 721 if (rename(temp, file) == -1) 722 warn("%s: rename state file", rr->basedir); 723 724 free(temp); 725 free(file); 726 return; 727 728 fail: 729 warnx("%s: failed to save state", rr->basedir); 730 unlink(temp); 731 free(temp); 732 free(file); 733 } 734 735 static struct rrdprepo * 736 rrdp_get(const char *uri) 737 { 738 struct rrdp_session state = { 0 }; 739 struct rrdprepo *rr; 740 741 SLIST_FOREACH(rr, &rrdprepos, entry) 742 if (strcmp(rr->notifyuri, uri) == 0) { 743 if (rr->state == REPO_FAILED) 744 return NULL; 745 return rr; 746 } 747 748 if ((rr = calloc(1, sizeof(*rr))) == NULL) 749 err(1, NULL); 750 751 rr->id = ++repoid; 752 SLIST_INSERT_HEAD(&rrdprepos, rr, entry); 753 754 if ((rr->notifyuri = strdup(uri)) == NULL) 755 err(1, NULL); 756 rr->basedir = repo_dir(uri, ".rrdp", 1); 757 758 RB_INIT(&rr->deleted); 759 760 /* create base directory */ 761 if (mkpath(rr->basedir) == -1) { 762 warn("mkpath %s", rr->basedir); 763 rrdp_finish(rr->id, 0); 764 return rr; 765 } 766 767 /* parse state and start the sync */ 768 rrdp_parse_state(rr, &state); 769 rrdp_fetch(rr->id, rr->notifyuri, rr->notifyuri, &state); 770 free(state.session_id); 771 free(state.last_mod); 772 773 logx("%s: pulling from %s", rr->notifyuri, "network"); 774 775 return rr; 776 } 777 778 /* 779 * Remove RRDP repo and start over. 780 */ 781 void 782 rrdp_clear(unsigned int id) 783 { 784 struct rrdprepo *rr; 785 786 rr = rrdp_find(id); 787 if (rr == NULL) 788 errx(1, "non-existent rrdp repo %u", id); 789 790 /* remove rrdp repository contents */ 791 remove_contents(rr->basedir); 792 } 793 794 /* 795 * Write a file into the temporary RRDP dir but only after checking 796 * its hash (if required). The function also makes sure that the file 797 * tracking is properly adjusted. 798 * Returns 1 on success, 0 if the repo is corrupt, -1 on IO error 799 */ 800 int 801 rrdp_handle_file(unsigned int id, enum publish_type pt, char *uri, 802 char *hash, size_t hlen, char *data, size_t dlen) 803 { 804 struct rrdprepo *rr; 805 struct filepath *fp; 806 ssize_t s; 807 char *fn = NULL; 808 int fd = -1, try = 0; 809 810 rr = rrdp_find(id); 811 if (rr == NULL) 812 errx(1, "non-existent rrdp repo %u", id); 813 if (rr->state == REPO_FAILED) 814 return -1; 815 816 /* check hash of original file for updates and deletes */ 817 if (pt == PUB_UPD || pt == PUB_DEL) { 818 if (filepath_exists(&rr->deleted, uri)) { 819 warnx("%s: already deleted", uri); 820 return 0; 821 } 822 /* try to open file first in rrdp then in valid repo */ 823 do { 824 free(fn); 825 if ((fn = rrdp_filename(rr, uri, try++)) == NULL) 826 return 0; 827 fd = open(fn, O_RDONLY); 828 } while (fd == -1 && try < 2); 829 830 if (!valid_filehash(fd, hash, hlen)) { 831 warnx("%s: bad file digest for %s", rr->notifyuri, fn); 832 free(fn); 833 return 0; 834 } 835 free(fn); 836 } 837 838 /* write new content or mark uri as deleted. */ 839 if (pt == PUB_DEL) { 840 filepath_add(&rr->deleted, uri); 841 } else { 842 fp = filepath_find(&rr->deleted, uri); 843 if (fp != NULL) 844 filepath_put(&rr->deleted, fp); 845 846 /* add new file to rrdp dir */ 847 if ((fn = rrdp_filename(rr, uri, 0)) == NULL) 848 return 0; 849 850 if (repo_mkpath(AT_FDCWD, fn) == -1) 851 goto fail; 852 853 fd = open(fn, O_WRONLY|O_CREAT|O_TRUNC, 0644); 854 if (fd == -1) { 855 warn("open %s", fn); 856 goto fail; 857 } 858 859 if ((s = write(fd, data, dlen)) == -1) { 860 warn("write %s", fn); 861 goto fail; 862 } 863 close(fd); 864 if ((size_t)s != dlen) /* impossible */ 865 errx(1, "short write %s", fn); 866 free(fn); 867 } 868 869 return 1; 870 871 fail: 872 rr->state = REPO_FAILED; 873 if (fd != -1) 874 close(fd); 875 free(fn); 876 return -1; 877 } 878 879 /* 880 * RSYNC sync finished, either with or without success. 881 */ 882 void 883 rsync_finish(unsigned int id, int ok) 884 { 885 struct rsyncrepo *rr; 886 struct tarepo *tr; 887 888 tr = ta_find(id); 889 if (tr != NULL) { 890 /* repository changed state already, ignore request */ 891 if (tr->state != REPO_LOADING) 892 return; 893 if (ok) { 894 logx("ta/%s: loaded from network", tr->descr); 895 stats.rsync_repos++; 896 tr->state = REPO_DONE; 897 repo_done(tr, 1); 898 } else { 899 warnx("ta/%s: load from network failed", tr->descr); 900 stats.rsync_fails++; 901 tr->uriidx++; 902 ta_fetch(tr); 903 } 904 return; 905 } 906 907 rr = rsync_find(id); 908 if (rr == NULL) 909 errx(1, "unknown rsync repo %u", id); 910 /* repository changed state already, ignore request */ 911 if (rr->state != REPO_LOADING) 912 return; 913 914 if (ok) { 915 logx("%s: loaded from network", rr->basedir); 916 stats.rsync_repos++; 917 rr->state = REPO_DONE; 918 } else { 919 warnx("%s: load from network failed, fallback to cache", 920 rr->basedir); 921 stats.rsync_fails++; 922 rr->state = REPO_FAILED; 923 /* clear rsync repo since it failed */ 924 remove_contents(rr->basedir); 925 } 926 927 repo_done(rr, ok); 928 } 929 930 /* 931 * RRDP sync finished, either with or without success. 932 */ 933 void 934 rrdp_finish(unsigned int id, int ok) 935 { 936 struct rrdprepo *rr; 937 938 rr = rrdp_find(id); 939 if (rr == NULL) 940 errx(1, "unknown RRDP repo %u", id); 941 /* repository changed state already, ignore request */ 942 if (rr->state != REPO_LOADING) 943 return; 944 945 if (ok) { 946 logx("%s: loaded from network", rr->notifyuri); 947 stats.rrdp_repos++; 948 rr->state = REPO_DONE; 949 } else { 950 warnx("%s: load from network failed, fallback to %s", 951 rr->notifyuri, nofetch ? "cache" : "rsync"); 952 stats.rrdp_fails++; 953 rr->state = REPO_FAILED; 954 /* clear the RRDP repo since it failed */ 955 remove_contents(rr->basedir); 956 /* also clear the list of deleted files */ 957 filepath_free(&rr->deleted); 958 } 959 960 repo_done(rr, ok); 961 } 962 963 /* 964 * Handle responses from the http process. For TA file, either rename 965 * or delete the temporary file. For RRDP requests relay the request 966 * over to the rrdp process. 967 */ 968 void 969 http_finish(unsigned int id, enum http_result res, const char *last_mod) 970 { 971 struct tarepo *tr; 972 973 tr = ta_find(id); 974 if (tr == NULL) { 975 /* not a TA fetch therefore RRDP */ 976 rrdp_http_done(id, res, last_mod); 977 return; 978 } 979 980 /* repository changed state already, ignore request */ 981 if (tr->state != REPO_LOADING) 982 return; 983 984 /* Move downloaded TA file into place, or unlink on failure. */ 985 if (res == HTTP_OK) { 986 char *file; 987 988 file = ta_filename(tr, 0); 989 if (rename(tr->temp, file) == -1) 990 warn("rename to %s", file); 991 free(file); 992 993 logx("ta/%s: loaded from network", tr->descr); 994 tr->state = REPO_DONE; 995 stats.http_repos++; 996 repo_done(tr, 1); 997 } else { 998 if (unlink(tr->temp) == -1 && errno != ENOENT) 999 warn("unlink %s", tr->temp); 1000 1001 tr->uriidx++; 1002 warnx("ta/%s: load from network failed", tr->descr); 1003 ta_fetch(tr); 1004 } 1005 } 1006 1007 /* 1008 * Look up a trust anchor, queueing it for download if not found. 1009 */ 1010 struct repo * 1011 ta_lookup(int id, struct tal *tal) 1012 { 1013 struct repo *rp; 1014 1015 if (tal->urisz == 0) 1016 errx(1, "TAL %s has no URI", tal->descr); 1017 1018 /* Look up in repository table. (Lookup should actually fail here) */ 1019 SLIST_FOREACH(rp, &repos, entry) { 1020 if (strcmp(rp->repouri, tal->uri[0]) == 0) 1021 return rp; 1022 } 1023 1024 rp = repo_alloc(id); 1025 rp->basedir = repo_dir(tal->descr, "ta", 0); 1026 if ((rp->repouri = strdup(tal->uri[0])) == NULL) 1027 err(1, NULL); 1028 1029 /* check if sync disabled ... */ 1030 if (noop) { 1031 logx("%s: using cache", rp->basedir); 1032 entityq_flush(&rp->queue, rp); 1033 return rp; 1034 } 1035 1036 /* try to create base directory */ 1037 if (mkpath(rp->basedir) == -1) 1038 warn("mkpath %s", rp->basedir); 1039 1040 rp->ta = ta_get(tal); 1041 1042 /* need to check if it was already loaded */ 1043 if (repo_state(rp) != REPO_LOADING) 1044 entityq_flush(&rp->queue, rp); 1045 1046 return rp; 1047 } 1048 1049 /* 1050 * Look up a repository, queueing it for discovery if not found. 1051 */ 1052 struct repo * 1053 repo_lookup(int talid, const char *uri, const char *notify) 1054 { 1055 struct repo *rp; 1056 char *repouri; 1057 1058 if ((repouri = rsync_base_uri(uri)) == NULL) 1059 errx(1, "bad caRepository URI: %s", uri); 1060 1061 /* Look up in repository table. */ 1062 SLIST_FOREACH(rp, &repos, entry) { 1063 if (strcmp(rp->repouri, repouri) != 0) 1064 continue; 1065 if (rp->notifyuri != NULL) { 1066 if (notify == NULL) 1067 continue; 1068 if (strcmp(rp->notifyuri, notify) != 0) 1069 continue; 1070 } else if (notify != NULL) 1071 continue; 1072 /* found matching repo */ 1073 free(repouri); 1074 return rp; 1075 } 1076 1077 rp = repo_alloc(talid); 1078 rp->basedir = repo_dir(repouri, NULL, 0); 1079 rp->repouri = repouri; 1080 if (notify != NULL) 1081 if ((rp->notifyuri = strdup(notify)) == NULL) 1082 err(1, NULL); 1083 1084 if (++talrepocnt[talid] >= MAX_REPO_PER_TAL) { 1085 if (talrepocnt[talid] == MAX_REPO_PER_TAL) 1086 warnx("too many repositories under %s", tals[talid]); 1087 nofetch = 1; 1088 } 1089 1090 /* check if sync disabled ... */ 1091 if (noop || nofetch) { 1092 logx("%s: using cache", rp->basedir); 1093 entityq_flush(&rp->queue, rp); 1094 return rp; 1095 } 1096 1097 /* try to create base directory */ 1098 if (mkpath(rp->basedir) == -1) 1099 warn("mkpath %s", rp->basedir); 1100 1101 /* ... else try RRDP first if available then rsync */ 1102 if (notify != NULL) 1103 rp->rrdp = rrdp_get(notify); 1104 if (rp->rrdp == NULL) 1105 rp->rsync = rsync_get(uri, rp->basedir); 1106 1107 /* need to check if it was already loaded */ 1108 if (repo_state(rp) != REPO_LOADING) 1109 entityq_flush(&rp->queue, rp); 1110 1111 return rp; 1112 } 1113 1114 /* 1115 * Find repository by identifier. 1116 */ 1117 struct repo * 1118 repo_byid(unsigned int id) 1119 { 1120 struct repo *rp; 1121 1122 SLIST_FOREACH(rp, &repos, entry) { 1123 if (rp->id == id) 1124 return rp; 1125 } 1126 return NULL; 1127 } 1128 1129 /* 1130 * Find repository by base path. 1131 */ 1132 static struct repo * 1133 repo_bypath(const char *path) 1134 { 1135 struct repo *rp; 1136 1137 SLIST_FOREACH(rp, &repos, entry) { 1138 if (strcmp(rp->basedir, path) == 0) 1139 return rp; 1140 } 1141 return NULL; 1142 } 1143 1144 /* 1145 * Return the repository base or alternate directory. 1146 * Returned string must be freed by caller. 1147 */ 1148 char * 1149 repo_basedir(const struct repo *rp, int wantvalid) 1150 { 1151 char *path = NULL; 1152 1153 if (!wantvalid) { 1154 if (rp->ta) { 1155 if ((path = strdup(rp->ta->basedir)) == NULL) 1156 err(1, NULL); 1157 } else if (rp->rsync) { 1158 if ((path = strdup(rp->rsync->basedir)) == NULL) 1159 err(1, NULL); 1160 } else if (rp->rrdp) { 1161 path = rrdp_filename(rp->rrdp, rp->repouri, 0); 1162 } else 1163 path = NULL; /* only valid repo available */ 1164 } else if (rp->basedir != NULL) { 1165 if ((path = strdup(rp->basedir)) == NULL) 1166 err(1, NULL); 1167 } 1168 1169 return path; 1170 } 1171 1172 /* 1173 * Return the repository identifier. 1174 */ 1175 unsigned int 1176 repo_id(const struct repo *rp) 1177 { 1178 return rp->id; 1179 } 1180 1181 /* 1182 * Return the repository URI. 1183 */ 1184 const char * 1185 repo_uri(const struct repo *rp) 1186 { 1187 return rp->repouri; 1188 } 1189 1190 /* 1191 * Return the repository URI. 1192 */ 1193 void 1194 repo_fetch_uris(const struct repo *rp, const char **carepo, 1195 const char **notifyuri) 1196 { 1197 *carepo = rp->repouri; 1198 *notifyuri = rp->notifyuri; 1199 } 1200 1201 /* 1202 * Return 1 if repository is synced else 0. 1203 */ 1204 int 1205 repo_synced(const struct repo *rp) 1206 { 1207 if (repo_state(rp) == REPO_DONE && 1208 !(rp->rrdp == NULL && rp->rsync == NULL && rp->ta == NULL)) 1209 return 1; 1210 return 0; 1211 } 1212 1213 /* 1214 * Return the protocol string "rrdp", "rsync", "https" which was used to sync. 1215 * Result is only correct if repository was properly synced. 1216 */ 1217 const char * 1218 repo_proto(const struct repo *rp) 1219 { 1220 1221 if (rp->ta != NULL) { 1222 const struct tarepo *tr = rp->ta; 1223 if (tr->uriidx < tr->urisz && 1224 strncasecmp(tr->uri[tr->uriidx], "rsync://", 8) == 0) 1225 return "rsync"; 1226 else 1227 return "https"; 1228 } 1229 if (rp->rrdp != NULL) 1230 return "rrdp"; 1231 return "rsync"; 1232 } 1233 1234 /* 1235 * Return the repository tal ID. 1236 */ 1237 int 1238 repo_talid(const struct repo *rp) 1239 { 1240 return rp->talid; 1241 } 1242 1243 int 1244 repo_queued(struct repo *rp, struct entity *p) 1245 { 1246 if (repo_state(rp) == REPO_LOADING) { 1247 TAILQ_INSERT_TAIL(&rp->queue, p, entries); 1248 return 1; 1249 } 1250 return 0; 1251 } 1252 1253 static void 1254 repo_fail(struct repo *rp) 1255 { 1256 /* reset the alarm since code may fallback to rsync */ 1257 rp->alarm = getmonotime() + repo_timeout; 1258 1259 if (rp->ta) 1260 http_finish(rp->ta->id, HTTP_FAILED, NULL); 1261 else if (rp->rsync) 1262 rsync_finish(rp->rsync->id, 0); 1263 else if (rp->rrdp) 1264 rrdp_finish(rp->rrdp->id, 0); 1265 else 1266 errx(1, "%s: bad repo", rp->repouri); 1267 } 1268 1269 static void 1270 repo_abort(struct repo *rp) 1271 { 1272 /* reset the alarm */ 1273 rp->alarm = getmonotime() + repo_timeout; 1274 1275 if (rp->rsync) 1276 rsync_abort(rp->rsync->id); 1277 else if (rp->rrdp) 1278 rrdp_abort(rp->rrdp->id); 1279 else 1280 repo_fail(rp); 1281 } 1282 1283 int 1284 repo_check_timeout(int timeout) 1285 { 1286 struct repo *rp; 1287 time_t now; 1288 int diff; 1289 1290 now = getmonotime(); 1291 1292 /* check against our runtime deadline first */ 1293 if (deadline != 0) { 1294 if (deadline <= now) { 1295 warnx("deadline reached, giving up on repository sync"); 1296 nofetch = 1; 1297 /* clear deadline since nofetch is set */ 1298 deadline = 0; 1299 /* increase now enough so that all pending repos fail */ 1300 now += repo_timeout; 1301 } else { 1302 diff = deadline - now; 1303 diff *= 1000; 1304 if (timeout == INFTIM || diff < timeout) 1305 timeout = diff; 1306 } 1307 } 1308 /* Look up in repository table. (Lookup should actually fail here) */ 1309 SLIST_FOREACH(rp, &repos, entry) { 1310 if (repo_state(rp) == REPO_LOADING) { 1311 if (rp->alarm <= now) { 1312 warnx("%s: synchronisation timeout", 1313 rp->repouri); 1314 repo_abort(rp); 1315 } else { 1316 diff = rp->alarm - now; 1317 diff *= 1000; 1318 if (timeout == INFTIM || diff < timeout) 1319 timeout = diff; 1320 } 1321 } 1322 } 1323 return timeout; 1324 } 1325 1326 /* 1327 * Update stats object of repository depending on rtype and subtype. 1328 */ 1329 void 1330 repo_stat_inc(struct repo *rp, int talid, enum rtype type, enum stype subtype) 1331 { 1332 if (rp == NULL) 1333 return; 1334 rp->stats_used[talid] = 1; 1335 switch (type) { 1336 case RTYPE_CER: 1337 if (subtype == STYPE_OK) 1338 rp->stats[talid].certs++; 1339 if (subtype == STYPE_FAIL) 1340 rp->stats[talid].certs_fail++; 1341 if (subtype == STYPE_BGPSEC) { 1342 rp->stats[talid].certs--; 1343 rp->stats[talid].brks++; 1344 } 1345 break; 1346 case RTYPE_MFT: 1347 if (subtype == STYPE_OK) 1348 rp->stats[talid].mfts++; 1349 if (subtype == STYPE_FAIL) 1350 rp->stats[talid].mfts_fail++; 1351 if (subtype == STYPE_STALE) 1352 rp->stats[talid].mfts_stale++; 1353 break; 1354 case RTYPE_ROA: 1355 switch (subtype) { 1356 case STYPE_OK: 1357 rp->stats[talid].roas++; 1358 break; 1359 case STYPE_FAIL: 1360 rp->stats[talid].roas_fail++; 1361 break; 1362 case STYPE_INVALID: 1363 rp->stats[talid].roas_invalid++; 1364 break; 1365 case STYPE_TOTAL: 1366 rp->stats[talid].vrps++; 1367 break; 1368 case STYPE_UNIQUE: 1369 rp->stats[talid].vrps_uniqs++; 1370 break; 1371 case STYPE_DEC_UNIQUE: 1372 rp->stats[talid].vrps_uniqs--; 1373 break; 1374 default: 1375 break; 1376 } 1377 break; 1378 case RTYPE_ASPA: 1379 switch (subtype) { 1380 case STYPE_OK: 1381 rp->stats[talid].aspas++; 1382 break; 1383 case STYPE_FAIL: 1384 rp->stats[talid].aspas_fail++; 1385 break; 1386 case STYPE_INVALID: 1387 rp->stats[talid].aspas_invalid++; 1388 break; 1389 case STYPE_TOTAL: 1390 rp->stats[talid].vaps++; 1391 break; 1392 case STYPE_UNIQUE: 1393 rp->stats[talid].vaps_uniqs++; 1394 break; 1395 case STYPE_DEC_UNIQUE: 1396 rp->stats[talid].vaps_uniqs--; 1397 break; 1398 case STYPE_BOTH: 1399 rp->stats[talid].vaps_pas++; 1400 break; 1401 case STYPE_ONLY_IPV4: 1402 rp->stats[talid].vaps_pas4++; 1403 break; 1404 case STYPE_ONLY_IPV6: 1405 rp->stats[talid].vaps_pas6++; 1406 break; 1407 default: 1408 break; 1409 } 1410 break; 1411 case RTYPE_CRL: 1412 rp->stats[talid].crls++; 1413 break; 1414 case RTYPE_GBR: 1415 rp->stats[talid].gbrs++; 1416 break; 1417 case RTYPE_TAK: 1418 rp->stats[talid].taks++; 1419 break; 1420 default: 1421 break; 1422 } 1423 } 1424 1425 void 1426 repo_tal_stats_collect(void (*cb)(const struct repo *, 1427 const struct repotalstats *, void *), int talid, void *arg) 1428 { 1429 struct repo *rp; 1430 1431 SLIST_FOREACH(rp, &repos, entry) { 1432 if (rp->stats_used[talid]) 1433 cb(rp, &rp->stats[talid], arg); 1434 } 1435 } 1436 1437 void 1438 repo_stats_collect(void (*cb)(const struct repo *, const struct repostats *, 1439 void *), void *arg) 1440 { 1441 struct repo *rp; 1442 1443 SLIST_FOREACH(rp, &repos, entry) 1444 cb(rp, &rp->repostats, arg); 1445 } 1446 1447 /* 1448 * Delayed delete of files from RRDP. Since RRDP has no security built-in 1449 * this code needs to check if this RRDP repository is actually allowed to 1450 * remove the file referenced by the URI. 1451 */ 1452 static void 1453 repo_cleanup_rrdp(struct filepath_tree *tree) 1454 { 1455 struct repo *rp; 1456 struct rrdprepo *rr; 1457 struct filepath *fp, *nfp; 1458 char *fn; 1459 1460 SLIST_FOREACH(rp, &repos, entry) { 1461 if (rp->rrdp == NULL) 1462 continue; 1463 rr = (struct rrdprepo *)rp->rrdp; 1464 RB_FOREACH_SAFE(fp, filepath_tree, &rr->deleted, nfp) { 1465 if (!rrdp_uri_valid(rr, fp->file)) { 1466 warnx("%s: external URI %s", rr->notifyuri, 1467 fp->file); 1468 filepath_put(&rr->deleted, fp); 1469 continue; 1470 } 1471 /* try to remove file from rrdp repo ... */ 1472 fn = rrdp_filename(rr, fp->file, 0); 1473 1474 if (unlink(fn) == -1) { 1475 if (errno != ENOENT) 1476 warn("unlink %s", fn); 1477 } else { 1478 if (verbose > 1) 1479 logx("deleted %s", fn); 1480 rp->repostats.del_files++; 1481 } 1482 free(fn); 1483 1484 /* ... and from the valid repository if unused. */ 1485 fn = rrdp_filename(rr, fp->file, 1); 1486 if (!filepath_exists(tree, fn)) { 1487 if (unlink(fn) == -1) { 1488 if (errno != ENOENT) 1489 warn("unlink %s", fn); 1490 } else { 1491 if (verbose > 1) 1492 logx("deleted %s", fn); 1493 rp->repostats.del_files++; 1494 } 1495 } else 1496 warnx("%s: referenced file supposed to be " 1497 "deleted", fn); 1498 1499 free(fn); 1500 filepath_put(&rr->deleted, fp); 1501 } 1502 } 1503 } 1504 1505 /* 1506 * All files in tree are valid and should be moved to the valid repository 1507 * if not already there. Rename the files to the new path and readd the 1508 * filepath entry with the new path if successful. 1509 */ 1510 static void 1511 repo_move_valid(struct filepath_tree *tree) 1512 { 1513 struct filepath *fp, *nfp; 1514 size_t rsyncsz = strlen(".rsync/"); 1515 size_t rrdpsz = strlen(".rrdp/"); 1516 char *fn, *base; 1517 1518 RB_FOREACH_SAFE(fp, filepath_tree, tree, nfp) { 1519 if (strncmp(fp->file, ".rsync/", rsyncsz) != 0 && 1520 strncmp(fp->file, ".rrdp/", rrdpsz) != 0) 1521 continue; /* not a temporary file path */ 1522 1523 if (strncmp(fp->file, ".rsync/", rsyncsz) == 0) { 1524 fn = fp->file + rsyncsz; 1525 } else { 1526 base = strchr(fp->file + rrdpsz, '/'); 1527 assert(base != NULL); 1528 fn = base + 1; 1529 } 1530 1531 if (repo_mkpath(AT_FDCWD, fn) == -1) 1532 continue; 1533 1534 if (rename(fp->file, fn) == -1) { 1535 warn("rename %s", fp->file); 1536 continue; 1537 } 1538 1539 /* switch filepath node to new path */ 1540 RB_REMOVE(filepath_tree, tree, fp); 1541 base = fp->file; 1542 if ((fp->file = strdup(fn)) == NULL) 1543 err(1, NULL); 1544 free(base); 1545 if (RB_INSERT(filepath_tree, tree, fp) != NULL) 1546 errx(1, "%s: both possibilities of file present", 1547 fp->file); 1548 } 1549 } 1550 1551 struct fts_state { 1552 enum { BASE_DIR, RSYNC_DIR, RRDP_DIR } type; 1553 struct repo *rp; 1554 } fts_state; 1555 1556 static const struct rrdprepo * 1557 repo_is_rrdp(struct repo *rp) 1558 { 1559 /* check for special pointers first these are not a repository */ 1560 if (rp != NULL && rp->rrdp != NULL) 1561 return rp->rrdp->state == REPO_DONE ? rp->rrdp : NULL; 1562 return NULL; 1563 } 1564 1565 static inline char * 1566 skip_dotslash(char *in) 1567 { 1568 if (memcmp(in, "./", 2) == 0) 1569 return in + 2; 1570 return in; 1571 } 1572 1573 static void 1574 repo_cleanup_entry(FTSENT *e, struct filepath_tree *tree, int cachefd) 1575 { 1576 const struct rrdprepo *rr; 1577 char *path; 1578 1579 path = skip_dotslash(e->fts_path); 1580 switch (e->fts_info) { 1581 case FTS_NSOK: 1582 if (filepath_exists(tree, path)) { 1583 e->fts_parent->fts_number++; 1584 break; 1585 } 1586 if (fts_state.type == RRDP_DIR && fts_state.rp != NULL) { 1587 e->fts_parent->fts_number++; 1588 /* handle rrdp .state files explicitly */ 1589 if (e->fts_level == 3 && 1590 strcmp(e->fts_name, ".state") == 0) 1591 break; 1592 /* can't delete these extra files */ 1593 fts_state.rp->repostats.extra_files++; 1594 if (verbose > 1) 1595 logx("superfluous %s", path); 1596 break; 1597 } 1598 rr = repo_is_rrdp(fts_state.rp); 1599 if (rr != NULL) { 1600 struct stat st; 1601 char *fn; 1602 1603 if (asprintf(&fn, "%s/%s", rr->basedir, path) == -1) 1604 err(1, NULL); 1605 1606 /* 1607 * If the file exists in the rrdp dir 1608 * that file is newer and needs to be kept 1609 * so unlink this file instead of moving 1610 * it over the file in the rrdp dir. 1611 */ 1612 if (fstatat(cachefd, fn, &st, 0) == 0 && 1613 S_ISREG(st.st_mode)) { 1614 free(fn); 1615 goto unlink; 1616 } 1617 if (repo_mkpath(cachefd, fn) == 0) { 1618 if (renameat(AT_FDCWD, e->fts_accpath, 1619 cachefd, fn) == -1) 1620 warn("rename %s to %s", path, fn); 1621 else if (verbose > 1) 1622 logx("moved %s", path); 1623 fts_state.rp->repostats.extra_files++; 1624 } 1625 free(fn); 1626 } else { 1627 unlink: 1628 if (unlink(e->fts_accpath) == -1) { 1629 warn("unlink %s", path); 1630 } else if (fts_state.type == RSYNC_DIR) { 1631 /* no need to keep rsync files */ 1632 if (verbose > 1) 1633 logx("superfluous %s", path); 1634 if (fts_state.rp != NULL) 1635 fts_state.rp->repostats.del_extra_files++; 1636 else 1637 stats.repo_stats.del_extra_files++; 1638 } else { 1639 if (verbose > 1) 1640 logx("deleted %s", path); 1641 if (fts_state.rp != NULL) 1642 fts_state.rp->repostats.del_files++; 1643 else 1644 stats.repo_stats.del_files++; 1645 } 1646 } 1647 break; 1648 case FTS_D: 1649 if (e->fts_level == FTS_ROOTLEVEL) 1650 fts_state.type = BASE_DIR; 1651 if (e->fts_level == 1) { 1652 if (strcmp(".rsync", e->fts_name) == 0) { 1653 fts_state.type = RSYNC_DIR; 1654 fts_state.rp = NULL; 1655 } else if (strcmp(".rrdp", e->fts_name) == 0) { 1656 fts_state.type = RRDP_DIR; 1657 fts_state.rp = NULL; 1658 } else { 1659 fts_state.type = BASE_DIR; 1660 fts_state.rp = repo_bypath(path); 1661 } 1662 } 1663 if (e->fts_level == 2) { 1664 if (fts_state.type == RSYNC_DIR) 1665 fts_state.rp = repo_bypath(path); 1666 /* 1667 * special handling for rrdp directories, 1668 * clear them if they are not used anymore but 1669 * only if rrdp is active. 1670 */ 1671 if (fts_state.type == RRDP_DIR) 1672 fts_state.rp = repo_rrdp_bypath(path); 1673 } 1674 break; 1675 case FTS_DP: 1676 if (e->fts_level == FTS_ROOTLEVEL) 1677 break; 1678 if (e->fts_level == 1) { 1679 /* do not remove .rsync and .rrdp */ 1680 fts_state.rp = NULL; 1681 if (fts_state.type == RRDP_DIR || 1682 fts_state.type == RSYNC_DIR) 1683 break; 1684 } 1685 1686 e->fts_parent->fts_number += e->fts_number; 1687 1688 if (e->fts_number == 0) { 1689 if (rmdir(e->fts_accpath) == -1) 1690 warn("rmdir %s", path); 1691 if (fts_state.rp != NULL) 1692 fts_state.rp->repostats.del_dirs++; 1693 else 1694 stats.repo_stats.del_dirs++; 1695 } 1696 break; 1697 case FTS_SL: 1698 case FTS_SLNONE: 1699 warnx("symlink %s", path); 1700 if (unlink(e->fts_accpath) == -1) 1701 warn("unlink %s", path); 1702 stats.repo_stats.del_extra_files++; 1703 break; 1704 case FTS_NS: 1705 case FTS_ERR: 1706 if (e->fts_errno == ENOENT && e->fts_level == FTS_ROOTLEVEL) 1707 break; 1708 warnx("fts_read %s: %s", path, strerror(e->fts_errno)); 1709 break; 1710 default: 1711 warnx("fts_read %s: unhandled[%x]", path, e->fts_info); 1712 break; 1713 } 1714 } 1715 1716 void 1717 repo_cleanup(struct filepath_tree *tree, int cachefd) 1718 { 1719 char *argv[2] = { ".", NULL }; 1720 FTS *fts; 1721 FTSENT *e; 1722 1723 /* first move temp files which have been used to valid dir */ 1724 repo_move_valid(tree); 1725 /* then delete files requested by rrdp */ 1726 repo_cleanup_rrdp(tree); 1727 1728 if ((fts = fts_open(argv, FTS_PHYSICAL | FTS_NOSTAT, NULL)) == NULL) 1729 err(1, "fts_open"); 1730 errno = 0; 1731 while ((e = fts_read(fts)) != NULL) { 1732 repo_cleanup_entry(e, tree, cachefd); 1733 errno = 0; 1734 } 1735 if (errno) 1736 err(1, "fts_read"); 1737 if (fts_close(fts) == -1) 1738 err(1, "fts_close"); 1739 } 1740 1741 void 1742 repo_free(void) 1743 { 1744 struct repo *rp; 1745 1746 while ((rp = SLIST_FIRST(&repos)) != NULL) { 1747 SLIST_REMOVE_HEAD(&repos, entry); 1748 free(rp->repouri); 1749 free(rp->notifyuri); 1750 free(rp->basedir); 1751 free(rp); 1752 } 1753 1754 ta_free(); 1755 rrdp_free(); 1756 rsync_free(); 1757 } 1758 1759 /* 1760 * Remove all files and directories under base but do not remove base itself. 1761 */ 1762 static void 1763 remove_contents(char *base) 1764 { 1765 char *argv[2] = { base, NULL }; 1766 FTS *fts; 1767 FTSENT *e; 1768 1769 if ((fts = fts_open(argv, FTS_PHYSICAL | FTS_NOSTAT, NULL)) == NULL) 1770 err(1, "fts_open"); 1771 errno = 0; 1772 while ((e = fts_read(fts)) != NULL) { 1773 switch (e->fts_info) { 1774 case FTS_NSOK: 1775 case FTS_SL: 1776 case FTS_SLNONE: 1777 if (unlink(e->fts_accpath) == -1) 1778 warn("unlink %s", e->fts_path); 1779 break; 1780 case FTS_D: 1781 break; 1782 case FTS_DP: 1783 /* keep root directory */ 1784 if (e->fts_level == FTS_ROOTLEVEL) 1785 break; 1786 if (rmdir(e->fts_accpath) == -1) 1787 warn("rmdir %s", e->fts_path); 1788 break; 1789 case FTS_NS: 1790 case FTS_ERR: 1791 warnx("fts_read %s: %s", e->fts_path, 1792 strerror(e->fts_errno)); 1793 break; 1794 default: 1795 warnx("unhandled[%x] %s", e->fts_info, 1796 e->fts_path); 1797 break; 1798 } 1799 errno = 0; 1800 } 1801 if (errno) 1802 err(1, "fts_read"); 1803 if (fts_close(fts) == -1) 1804 err(1, "fts_close"); 1805 } 1806