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