1*05a19cc6Sjob /* $OpenBSD: repo.c,v 1.71 2024/12/19 13:23:38 job Exp $ */ 28ecbadc1Sclaudio /* 38ecbadc1Sclaudio * Copyright (c) 2021 Claudio Jeker <claudio@openbsd.org> 48ecbadc1Sclaudio * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv> 58ecbadc1Sclaudio * 68ecbadc1Sclaudio * Permission to use, copy, modify, and distribute this software for any 78ecbadc1Sclaudio * purpose with or without fee is hereby granted, provided that the above 88ecbadc1Sclaudio * copyright notice and this permission notice appear in all copies. 98ecbadc1Sclaudio * 108ecbadc1Sclaudio * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 118ecbadc1Sclaudio * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 128ecbadc1Sclaudio * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 138ecbadc1Sclaudio * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 148ecbadc1Sclaudio * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 158ecbadc1Sclaudio * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 168ecbadc1Sclaudio * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 178ecbadc1Sclaudio */ 188ecbadc1Sclaudio 198ecbadc1Sclaudio #include <sys/queue.h> 208ecbadc1Sclaudio #include <sys/tree.h> 218ecbadc1Sclaudio #include <sys/types.h> 228ecbadc1Sclaudio #include <sys/stat.h> 238ecbadc1Sclaudio 248ecbadc1Sclaudio #include <assert.h> 258ecbadc1Sclaudio #include <err.h> 268ecbadc1Sclaudio #include <errno.h> 278ecbadc1Sclaudio #include <fcntl.h> 288ecbadc1Sclaudio #include <fts.h> 298ecbadc1Sclaudio #include <limits.h> 307af68c5cSclaudio #include <poll.h> 318ecbadc1Sclaudio #include <stdio.h> 328ecbadc1Sclaudio #include <stdlib.h> 338ecbadc1Sclaudio #include <string.h> 348ecbadc1Sclaudio #include <unistd.h> 358ecbadc1Sclaudio 368ecbadc1Sclaudio #include <imsg.h> 378ecbadc1Sclaudio 388ecbadc1Sclaudio #include "extern.h" 398ecbadc1Sclaudio 408ecbadc1Sclaudio extern struct stats stats; 419e8ec711Sclaudio extern int rrdpon; 42cecb0802Sjob extern int repo_timeout; 4347de54a6Sclaudio extern time_t deadline; 4447de54a6Sclaudio int nofetch; 458ecbadc1Sclaudio 468ecbadc1Sclaudio enum repo_state { 478ecbadc1Sclaudio REPO_LOADING = 0, 488ecbadc1Sclaudio REPO_DONE = 1, 498ecbadc1Sclaudio REPO_FAILED = -1, 508ecbadc1Sclaudio }; 518ecbadc1Sclaudio 528ecbadc1Sclaudio /* 538ecbadc1Sclaudio * A ta, rsync or rrdp repository. 548ecbadc1Sclaudio * Depending on what is needed the generic repository is backed by 55e93f5db9Stb * a ta, rsync or rrdp repository. Multiple repositories can use the 568ecbadc1Sclaudio * same backend. 578ecbadc1Sclaudio */ 588ecbadc1Sclaudio struct rrdprepo { 598ecbadc1Sclaudio SLIST_ENTRY(rrdprepo) entry; 608ecbadc1Sclaudio char *notifyuri; 618ecbadc1Sclaudio char *basedir; 628ecbadc1Sclaudio struct filepath_tree deleted; 63b6884e9fSclaudio unsigned int id; 648ecbadc1Sclaudio enum repo_state state; 65d0a27ef8Sjob time_t last_reset; 668ecbadc1Sclaudio }; 67100ded9eSclaudio static SLIST_HEAD(, rrdprepo) rrdprepos = SLIST_HEAD_INITIALIZER(rrdprepos); 688ecbadc1Sclaudio 698ecbadc1Sclaudio struct rsyncrepo { 708ecbadc1Sclaudio SLIST_ENTRY(rsyncrepo) entry; 718ecbadc1Sclaudio char *repouri; 728ecbadc1Sclaudio char *basedir; 73b6884e9fSclaudio unsigned int id; 748ecbadc1Sclaudio enum repo_state state; 758ecbadc1Sclaudio }; 76100ded9eSclaudio static SLIST_HEAD(, rsyncrepo) rsyncrepos = SLIST_HEAD_INITIALIZER(rsyncrepos); 778ecbadc1Sclaudio 788ecbadc1Sclaudio struct tarepo { 798ecbadc1Sclaudio SLIST_ENTRY(tarepo) entry; 808ecbadc1Sclaudio char *descr; 818ecbadc1Sclaudio char *basedir; 828ecbadc1Sclaudio char **uri; 8330a08502Stb size_t num_uris; 848ecbadc1Sclaudio size_t uriidx; 85b6884e9fSclaudio unsigned int id; 868ecbadc1Sclaudio enum repo_state state; 878ecbadc1Sclaudio }; 88100ded9eSclaudio static SLIST_HEAD(, tarepo) tarepos = SLIST_HEAD_INITIALIZER(tarepos); 898ecbadc1Sclaudio 908ecbadc1Sclaudio struct repo { 918ecbadc1Sclaudio SLIST_ENTRY(repo) entry; 92a260f1c5Sclaudio char *repouri; 93a260f1c5Sclaudio char *notifyuri; 940c3a2335Sclaudio char *basedir; 958ecbadc1Sclaudio const struct rrdprepo *rrdp; 968ecbadc1Sclaudio const struct rsyncrepo *rsync; 978ecbadc1Sclaudio const struct tarepo *ta; 988ecbadc1Sclaudio struct entityq queue; /* files waiting for repo */ 991fc2657fSclaudio struct repotalstats stats[TALSZ_MAX]; 1001fc2657fSclaudio struct repostats repostats; 1014f5f25cbSclaudio struct timespec start_time; 1027af68c5cSclaudio time_t alarm; /* sync timeout */ 1037af68c5cSclaudio int talid; 1041fc2657fSclaudio int stats_used[TALSZ_MAX]; 105b6884e9fSclaudio unsigned int id; /* identifier */ 1068ecbadc1Sclaudio }; 107100ded9eSclaudio static SLIST_HEAD(, repo) repos = SLIST_HEAD_INITIALIZER(repos); 1088ecbadc1Sclaudio 1098ecbadc1Sclaudio /* counter for unique repo id */ 110b6884e9fSclaudio unsigned int repoid; 1118ecbadc1Sclaudio 1120c3a2335Sclaudio static struct rsyncrepo *rsync_get(const char *, const char *); 113264f4ef9Sclaudio static void remove_contents(char *); 11499ee4841Sclaudio 1158ecbadc1Sclaudio /* 1168ecbadc1Sclaudio * Database of all file path accessed during a run. 1178ecbadc1Sclaudio */ 1188ecbadc1Sclaudio struct filepath { 1198ecbadc1Sclaudio RB_ENTRY(filepath) entry; 1208ecbadc1Sclaudio char *file; 12108ac1330Sjob time_t mtime; 1220bc420b9Sclaudio unsigned int talmask; 1238ecbadc1Sclaudio }; 1248ecbadc1Sclaudio 1258ecbadc1Sclaudio static inline int 1260bc420b9Sclaudio filepathcmp(const struct filepath *a, const struct filepath *b) 1278ecbadc1Sclaudio { 1288ecbadc1Sclaudio return strcmp(a->file, b->file); 1298ecbadc1Sclaudio } 1308ecbadc1Sclaudio 1318ecbadc1Sclaudio RB_PROTOTYPE(filepath_tree, filepath, entry, filepathcmp); 1328ecbadc1Sclaudio 1338ecbadc1Sclaudio /* 1348ecbadc1Sclaudio * Functions to lookup which files have been accessed during computation. 1358ecbadc1Sclaudio */ 1368ecbadc1Sclaudio int 13792360713Sclaudio filepath_add(struct filepath_tree *tree, char *file, int id, time_t mtime, 13892360713Sclaudio int ok) 1398ecbadc1Sclaudio { 1400bc420b9Sclaudio struct filepath *fp, *rfp; 1418ecbadc1Sclaudio 1420bc420b9Sclaudio CTASSERT(TALSZ_MAX < 8 * sizeof(fp->talmask)); 1430bc420b9Sclaudio assert(id >= 0 && id < 8 * (int)sizeof(fp->talmask)); 1440bc420b9Sclaudio 1450bc420b9Sclaudio if ((fp = calloc(1, sizeof(*fp))) == NULL) 1468ecbadc1Sclaudio err(1, NULL); 1478ecbadc1Sclaudio if ((fp->file = strdup(file)) == NULL) 1488ecbadc1Sclaudio err(1, NULL); 1490bc420b9Sclaudio fp->mtime = mtime; 1508ecbadc1Sclaudio 1510bc420b9Sclaudio if ((rfp = RB_INSERT(filepath_tree, tree, fp)) != NULL) { 1528ecbadc1Sclaudio /* already in the tree */ 1538ecbadc1Sclaudio free(fp->file); 1548ecbadc1Sclaudio free(fp); 1550bc420b9Sclaudio if (rfp->talmask & (1 << id)) 1568ecbadc1Sclaudio return 0; 1570bc420b9Sclaudio fp = rfp; 1588ecbadc1Sclaudio } 15992360713Sclaudio if (ok) 1600bc420b9Sclaudio fp->talmask |= (1 << id); 1618ecbadc1Sclaudio 1628ecbadc1Sclaudio return 1; 1638ecbadc1Sclaudio } 1648ecbadc1Sclaudio 1658ecbadc1Sclaudio /* 1668ecbadc1Sclaudio * Lookup a file path in the tree and return the object if found or NULL. 1678ecbadc1Sclaudio */ 1688ecbadc1Sclaudio static struct filepath * 1698ecbadc1Sclaudio filepath_find(struct filepath_tree *tree, char *file) 1708ecbadc1Sclaudio { 171100ded9eSclaudio struct filepath needle = { .file = file }; 1728ecbadc1Sclaudio 1738ecbadc1Sclaudio return RB_FIND(filepath_tree, tree, &needle); 1748ecbadc1Sclaudio } 1758ecbadc1Sclaudio 1768ecbadc1Sclaudio /* 1778ecbadc1Sclaudio * Returns true if file exists in the tree. 1788ecbadc1Sclaudio */ 1798ecbadc1Sclaudio static int 1808ecbadc1Sclaudio filepath_exists(struct filepath_tree *tree, char *file) 1818ecbadc1Sclaudio { 1828ecbadc1Sclaudio return filepath_find(tree, file) != NULL; 1838ecbadc1Sclaudio } 1848ecbadc1Sclaudio 1858ecbadc1Sclaudio /* 18692360713Sclaudio * Returns true if file exists and the id bit is set and ok flag is true. 18792360713Sclaudio */ 18892360713Sclaudio int 18992360713Sclaudio filepath_valid(struct filepath_tree *tree, char *file, int id) 19092360713Sclaudio { 19192360713Sclaudio struct filepath *fp; 19292360713Sclaudio 19392360713Sclaudio if ((fp = filepath_find(tree, file)) == NULL) 19492360713Sclaudio return 0; 19592360713Sclaudio return (fp->talmask & (1 << id)) != 0; 19692360713Sclaudio } 19792360713Sclaudio 19892360713Sclaudio /* 1998ecbadc1Sclaudio * Remove entry from tree and free it. 2008ecbadc1Sclaudio */ 2018ecbadc1Sclaudio static void 2028ecbadc1Sclaudio filepath_put(struct filepath_tree *tree, struct filepath *fp) 2038ecbadc1Sclaudio { 2048ecbadc1Sclaudio RB_REMOVE(filepath_tree, tree, fp); 2058ecbadc1Sclaudio free((void *)fp->file); 2068ecbadc1Sclaudio free(fp); 2078ecbadc1Sclaudio } 2088ecbadc1Sclaudio 2098ecbadc1Sclaudio /* 2108ecbadc1Sclaudio * Free all elements of a filepath tree. 2118ecbadc1Sclaudio */ 2128ecbadc1Sclaudio static void 2138ecbadc1Sclaudio filepath_free(struct filepath_tree *tree) 2148ecbadc1Sclaudio { 2158ecbadc1Sclaudio struct filepath *fp, *nfp; 2168ecbadc1Sclaudio 2178ecbadc1Sclaudio RB_FOREACH_SAFE(fp, filepath_tree, tree, nfp) 2188ecbadc1Sclaudio filepath_put(tree, fp); 2198ecbadc1Sclaudio } 2208ecbadc1Sclaudio 2218ecbadc1Sclaudio RB_GENERATE(filepath_tree, filepath, entry, filepathcmp); 2228ecbadc1Sclaudio 2238ecbadc1Sclaudio /* 2248ecbadc1Sclaudio * Function to hash a string into a unique directory name. 225341ddebfSclaudio * Returned hash needs to be freed. 2268ecbadc1Sclaudio */ 2278ecbadc1Sclaudio static char * 228341ddebfSclaudio hash_dir(const char *uri) 2298ecbadc1Sclaudio { 2308ecbadc1Sclaudio unsigned char m[SHA256_DIGEST_LENGTH]; 2318ecbadc1Sclaudio 2328ecbadc1Sclaudio SHA256(uri, strlen(uri), m); 233341ddebfSclaudio return hex_encode(m, sizeof(m)); 2348ecbadc1Sclaudio } 2358ecbadc1Sclaudio 2368ecbadc1Sclaudio /* 2378ecbadc1Sclaudio * Function to build the directory name based on URI and a directory 2388ecbadc1Sclaudio * as prefix. Skip the proto:// in URI but keep everything else. 2398ecbadc1Sclaudio */ 2408ecbadc1Sclaudio static char * 241341ddebfSclaudio repo_dir(const char *uri, const char *dir, int hash) 2428ecbadc1Sclaudio { 243341ddebfSclaudio const char *local; 244341ddebfSclaudio char *out, *hdir = NULL; 2458ecbadc1Sclaudio 246341ddebfSclaudio if (hash) { 247341ddebfSclaudio local = hdir = hash_dir(uri); 248341ddebfSclaudio } else { 249341ddebfSclaudio local = strchr(uri, ':'); 250341ddebfSclaudio if (local != NULL) 251341ddebfSclaudio local += strlen("://"); 252341ddebfSclaudio else 253341ddebfSclaudio local = uri; 254341ddebfSclaudio } 2558ecbadc1Sclaudio 2568f17a701Sclaudio if (dir == NULL) { 2578f17a701Sclaudio if ((out = strdup(local)) == NULL) 2588f17a701Sclaudio err(1, NULL); 2598f17a701Sclaudio } else { 260341ddebfSclaudio if (asprintf(&out, "%s/%s", dir, local) == -1) 261341ddebfSclaudio err(1, NULL); 2628f17a701Sclaudio } 2638f17a701Sclaudio 264341ddebfSclaudio free(hdir); 2658ecbadc1Sclaudio return out; 2668ecbadc1Sclaudio } 2678ecbadc1Sclaudio 2688ecbadc1Sclaudio /* 2698ecbadc1Sclaudio * Function to create all missing directories to a path. 2708ecbadc1Sclaudio * This functions alters the path temporarily. 2718ecbadc1Sclaudio */ 272ea10885eSclaudio static int 2736cf9bac2Sclaudio repo_mkpath(int fd, char *file) 2748ecbadc1Sclaudio { 2758ecbadc1Sclaudio char *slash; 2768ecbadc1Sclaudio 2778ecbadc1Sclaudio /* build directory hierarchy */ 2788ecbadc1Sclaudio slash = strrchr(file, '/'); 2798ecbadc1Sclaudio assert(slash != NULL); 2808ecbadc1Sclaudio *slash = '\0'; 2816cf9bac2Sclaudio if (mkpathat(fd, file) == -1) { 282ea10885eSclaudio warn("mkpath %s", file); 283ea10885eSclaudio return -1; 284ea10885eSclaudio } 2858ecbadc1Sclaudio *slash = '/'; 286ea10885eSclaudio return 0; 2878ecbadc1Sclaudio } 2888ecbadc1Sclaudio 2898ecbadc1Sclaudio /* 29099ee4841Sclaudio * Return the state of a repository. 29199ee4841Sclaudio */ 29299ee4841Sclaudio static enum repo_state 2934f5f25cbSclaudio repo_state(const struct repo *rp) 29499ee4841Sclaudio { 29599ee4841Sclaudio if (rp->ta) 29699ee4841Sclaudio return rp->ta->state; 29799ee4841Sclaudio if (rp->rsync) 29899ee4841Sclaudio return rp->rsync->state; 29999ee4841Sclaudio if (rp->rrdp) 30099ee4841Sclaudio return rp->rrdp->state; 3010c3a2335Sclaudio /* No backend so sync is by definition done. */ 3020c3a2335Sclaudio return REPO_DONE; 30399ee4841Sclaudio } 30499ee4841Sclaudio 30599ee4841Sclaudio /* 30699ee4841Sclaudio * Function called once a repository is done with the sync. Either 30799ee4841Sclaudio * successfully or after failure. 30899ee4841Sclaudio */ 30999ee4841Sclaudio static void 31099ee4841Sclaudio repo_done(const void *vp, int ok) 31199ee4841Sclaudio { 31299ee4841Sclaudio struct repo *rp; 3134f5f25cbSclaudio struct timespec flush_time; 31499ee4841Sclaudio 31599ee4841Sclaudio SLIST_FOREACH(rp, &repos, entry) { 3164f5f25cbSclaudio if (vp != rp->ta && vp != rp->rsync && vp != rp->rrdp) 3174f5f25cbSclaudio continue; 3184f5f25cbSclaudio 3194f5f25cbSclaudio /* for rrdp try to fall back to rsync */ 3204f5f25cbSclaudio if (vp == rp->rrdp && !ok && !nofetch) { 32199ee4841Sclaudio rp->rrdp = NULL; 3224f5f25cbSclaudio rp->rsync = rsync_get(rp->repouri, rp->basedir); 32399ee4841Sclaudio /* need to check if it was already loaded */ 3244f5f25cbSclaudio if (repo_state(rp) == REPO_LOADING) 3254f5f25cbSclaudio continue; 32699ee4841Sclaudio } 3274f5f25cbSclaudio 3284f5f25cbSclaudio entityq_flush(&rp->queue, rp); 3294f5f25cbSclaudio clock_gettime(CLOCK_MONOTONIC, &flush_time); 3301fc2657fSclaudio timespecsub(&flush_time, &rp->start_time, 3311fc2657fSclaudio &rp->repostats.sync_time); 33299ee4841Sclaudio } 33399ee4841Sclaudio } 33499ee4841Sclaudio 33599ee4841Sclaudio /* 3368ecbadc1Sclaudio * Build TA file name based on the repo info. 3378ecbadc1Sclaudio * If temp is set add Xs for mkostemp. 3388ecbadc1Sclaudio */ 3398ecbadc1Sclaudio static char * 3402674db3cSclaudio ta_filename(const struct tarepo *tr) 3418ecbadc1Sclaudio { 3428ecbadc1Sclaudio const char *file; 3438ecbadc1Sclaudio char *nfile; 3448ecbadc1Sclaudio 3458ecbadc1Sclaudio /* does not matter which URI, all end with same filename */ 3468ecbadc1Sclaudio file = strrchr(tr->uri[0], '/'); 3478ecbadc1Sclaudio assert(file); 3488ecbadc1Sclaudio 3492674db3cSclaudio if (asprintf(&nfile, "%s%s", tr->basedir, file) == -1) 3508ecbadc1Sclaudio err(1, NULL); 3518ecbadc1Sclaudio 3528ecbadc1Sclaudio return nfile; 3538ecbadc1Sclaudio } 3548ecbadc1Sclaudio 3558ecbadc1Sclaudio static void 3568ecbadc1Sclaudio ta_fetch(struct tarepo *tr) 3578ecbadc1Sclaudio { 3589e8ec711Sclaudio if (!rrdpon) { 35930a08502Stb for (; tr->uriidx < tr->num_uris; tr->uriidx++) { 3609e8ec711Sclaudio if (strncasecmp(tr->uri[tr->uriidx], 3610610060dSjob RSYNC_PROTO, RSYNC_PROTO_LEN) == 0) 3629e8ec711Sclaudio break; 3639e8ec711Sclaudio } 3649e8ec711Sclaudio } 3659e8ec711Sclaudio 36630a08502Stb if (tr->uriidx >= tr->num_uris) { 3679e8ec711Sclaudio tr->state = REPO_FAILED; 3689e8ec711Sclaudio logx("ta/%s: fallback to cache", tr->descr); 3699e8ec711Sclaudio 37099ee4841Sclaudio repo_done(tr, 0); 3719e8ec711Sclaudio return; 3729e8ec711Sclaudio } 3739e8ec711Sclaudio 3748ecbadc1Sclaudio logx("ta/%s: pulling from %s", tr->descr, tr->uri[tr->uriidx]); 3758ecbadc1Sclaudio 3760610060dSjob if (strncasecmp(tr->uri[tr->uriidx], RSYNC_PROTO, 3770610060dSjob RSYNC_PROTO_LEN) == 0) { 3788ecbadc1Sclaudio /* 3798ecbadc1Sclaudio * Create destination location. 3808ecbadc1Sclaudio * Build up the tree to this point. 3818ecbadc1Sclaudio */ 3820c3a2335Sclaudio rsync_fetch(tr->id, tr->uri[tr->uriidx], tr->basedir, NULL); 3838ecbadc1Sclaudio } else { 3842674db3cSclaudio char *temp; 38595b65f7cSderaadt int fd; 38695b65f7cSderaadt 3872674db3cSclaudio temp = ta_filename(tr); 3882674db3cSclaudio fd = open(temp, 3892674db3cSclaudio O_WRONLY | O_CREAT | O_TRUNC | O_NOFOLLOW | O_CLOEXEC, 3902674db3cSclaudio S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); 3918ecbadc1Sclaudio if (fd == -1) { 3922674db3cSclaudio warn("open: %s", temp); 3932674db3cSclaudio free(temp); 394ea10885eSclaudio http_finish(tr->id, HTTP_FAILED, NULL); 395ea10885eSclaudio return; 3968ecbadc1Sclaudio } 3978ecbadc1Sclaudio 3982674db3cSclaudio free(temp); 3998ecbadc1Sclaudio http_fetch(tr->id, tr->uri[tr->uriidx], NULL, fd); 4008ecbadc1Sclaudio } 4018ecbadc1Sclaudio } 4028ecbadc1Sclaudio 4038ecbadc1Sclaudio static struct tarepo * 404578d50dbSclaudio ta_get(struct tal *tal) 4058ecbadc1Sclaudio { 4068ecbadc1Sclaudio struct tarepo *tr; 4078ecbadc1Sclaudio 4088ecbadc1Sclaudio /* no need to look for possible other repo */ 4098ecbadc1Sclaudio 4108ecbadc1Sclaudio if ((tr = calloc(1, sizeof(*tr))) == NULL) 4118ecbadc1Sclaudio err(1, NULL); 4122674db3cSclaudio 4138ecbadc1Sclaudio tr->id = ++repoid; 4148ecbadc1Sclaudio SLIST_INSERT_HEAD(&tarepos, tr, entry); 4158ecbadc1Sclaudio 4168ecbadc1Sclaudio if ((tr->descr = strdup(tal->descr)) == NULL) 4178ecbadc1Sclaudio err(1, NULL); 4182674db3cSclaudio tr->basedir = repo_dir(tal->descr, ".ta", 0); 4192674db3cSclaudio 4202674db3cSclaudio /* create base directory */ 4212674db3cSclaudio if (mkpath(tr->basedir) == -1) { 4222674db3cSclaudio warn("mkpath %s", tr->basedir); 4232674db3cSclaudio tr->state = REPO_FAILED; 4242674db3cSclaudio repo_done(tr, 0); 4252674db3cSclaudio return tr; 4262674db3cSclaudio } 4278ecbadc1Sclaudio 4287e354e24Sjsg /* steal URI information from TAL */ 42930a08502Stb tr->num_uris = tal->num_uris; 4308ecbadc1Sclaudio tr->uri = tal->uri; 43130a08502Stb tal->num_uris = 0; 4328ecbadc1Sclaudio tal->uri = NULL; 4338ecbadc1Sclaudio 4348ecbadc1Sclaudio ta_fetch(tr); 4358ecbadc1Sclaudio 4368ecbadc1Sclaudio return tr; 4378ecbadc1Sclaudio } 4388ecbadc1Sclaudio 4398ecbadc1Sclaudio static struct tarepo * 440b6884e9fSclaudio ta_find(unsigned int id) 4418ecbadc1Sclaudio { 4428ecbadc1Sclaudio struct tarepo *tr; 4438ecbadc1Sclaudio 4448ecbadc1Sclaudio SLIST_FOREACH(tr, &tarepos, entry) 4458ecbadc1Sclaudio if (id == tr->id) 4468ecbadc1Sclaudio break; 4478ecbadc1Sclaudio return tr; 4488ecbadc1Sclaudio } 4498ecbadc1Sclaudio 4508ecbadc1Sclaudio static void 4518ecbadc1Sclaudio ta_free(void) 4528ecbadc1Sclaudio { 4538ecbadc1Sclaudio struct tarepo *tr; 4548ecbadc1Sclaudio 4558ecbadc1Sclaudio while ((tr = SLIST_FIRST(&tarepos)) != NULL) { 4568ecbadc1Sclaudio SLIST_REMOVE_HEAD(&tarepos, entry); 4578ecbadc1Sclaudio free(tr->descr); 4588ecbadc1Sclaudio free(tr->basedir); 4598ecbadc1Sclaudio free(tr->uri); 4608ecbadc1Sclaudio free(tr); 4618ecbadc1Sclaudio } 4628ecbadc1Sclaudio } 4638ecbadc1Sclaudio 4648ecbadc1Sclaudio static struct rsyncrepo * 4650c3a2335Sclaudio rsync_get(const char *uri, const char *validdir) 4668ecbadc1Sclaudio { 4678ecbadc1Sclaudio struct rsyncrepo *rr; 4688ecbadc1Sclaudio char *repo; 4698ecbadc1Sclaudio 4708ecbadc1Sclaudio if ((repo = rsync_base_uri(uri)) == NULL) 4718ecbadc1Sclaudio errx(1, "bad caRepository URI: %s", uri); 4728ecbadc1Sclaudio 4738ecbadc1Sclaudio SLIST_FOREACH(rr, &rsyncrepos, entry) 4748ecbadc1Sclaudio if (strcmp(rr->repouri, repo) == 0) { 4758ecbadc1Sclaudio free(repo); 4768ecbadc1Sclaudio return rr; 4778ecbadc1Sclaudio } 4788ecbadc1Sclaudio 4798ecbadc1Sclaudio if ((rr = calloc(1, sizeof(*rr))) == NULL) 4808ecbadc1Sclaudio err(1, NULL); 4818ecbadc1Sclaudio 4828ecbadc1Sclaudio rr->id = ++repoid; 4838ecbadc1Sclaudio SLIST_INSERT_HEAD(&rsyncrepos, rr, entry); 4848ecbadc1Sclaudio 4858ecbadc1Sclaudio rr->repouri = repo; 4868f17a701Sclaudio rr->basedir = repo_dir(repo, ".rsync", 0); 4878ecbadc1Sclaudio 488ea10885eSclaudio /* create base directory */ 489ea10885eSclaudio if (mkpath(rr->basedir) == -1) { 490ea10885eSclaudio warn("mkpath %s", rr->basedir); 491ea10885eSclaudio rsync_finish(rr->id, 0); 492ea10885eSclaudio return rr; 493ea10885eSclaudio } 494ea10885eSclaudio 4958ecbadc1Sclaudio logx("%s: pulling from %s", rr->basedir, rr->repouri); 4960c3a2335Sclaudio rsync_fetch(rr->id, rr->repouri, rr->basedir, validdir); 4978ecbadc1Sclaudio 4988ecbadc1Sclaudio return rr; 4998ecbadc1Sclaudio } 5008ecbadc1Sclaudio 5018ecbadc1Sclaudio static struct rsyncrepo * 502b6884e9fSclaudio rsync_find(unsigned int id) 5038ecbadc1Sclaudio { 5048ecbadc1Sclaudio struct rsyncrepo *rr; 5058ecbadc1Sclaudio 5068ecbadc1Sclaudio SLIST_FOREACH(rr, &rsyncrepos, entry) 5078ecbadc1Sclaudio if (id == rr->id) 5088ecbadc1Sclaudio break; 5098ecbadc1Sclaudio return rr; 5108ecbadc1Sclaudio } 5118ecbadc1Sclaudio 5128ecbadc1Sclaudio static void 5138ecbadc1Sclaudio rsync_free(void) 5148ecbadc1Sclaudio { 5158ecbadc1Sclaudio struct rsyncrepo *rr; 5168ecbadc1Sclaudio 5178ecbadc1Sclaudio while ((rr = SLIST_FIRST(&rsyncrepos)) != NULL) { 5188ecbadc1Sclaudio SLIST_REMOVE_HEAD(&rsyncrepos, entry); 5198ecbadc1Sclaudio free(rr->repouri); 5208ecbadc1Sclaudio free(rr->basedir); 5218ecbadc1Sclaudio free(rr); 5228ecbadc1Sclaudio } 5238ecbadc1Sclaudio } 5248ecbadc1Sclaudio 5256936df76Sclaudio /* 5266936df76Sclaudio * Build local file name base on the URI and the rrdprepo info. 5276936df76Sclaudio */ 5286936df76Sclaudio static char * 5290c3a2335Sclaudio rrdp_filename(const struct rrdprepo *rr, const char *uri, int valid) 5308ecbadc1Sclaudio { 5316936df76Sclaudio char *nfile; 5320c3a2335Sclaudio const char *dir = rr->basedir; 5338ecbadc1Sclaudio 5340610060dSjob if (!valid_uri(uri, strlen(uri), RSYNC_PROTO)) 5350c3a2335Sclaudio errx(1, "%s: bad URI %s", rr->basedir, uri); 5360610060dSjob uri += RSYNC_PROTO_LEN; /* skip proto */ 5378f17a701Sclaudio if (valid) { 5388f17a701Sclaudio if ((nfile = strdup(uri)) == NULL) 5398f17a701Sclaudio err(1, NULL); 5408f17a701Sclaudio } else { 5416936df76Sclaudio if (asprintf(&nfile, "%s/%s", dir, uri) == -1) 5426936df76Sclaudio err(1, NULL); 5438f17a701Sclaudio } 5446936df76Sclaudio return nfile; 5456936df76Sclaudio } 5466936df76Sclaudio 5476936df76Sclaudio /* 5486936df76Sclaudio * Build RRDP state file name based on the repo info. 5496936df76Sclaudio * If temp is set add Xs for mkostemp. 5506936df76Sclaudio */ 5516936df76Sclaudio static char * 5526936df76Sclaudio rrdp_state_filename(const struct rrdprepo *rr, int temp) 5536936df76Sclaudio { 5546936df76Sclaudio char *nfile; 5556936df76Sclaudio 5566936df76Sclaudio if (asprintf(&nfile, "%s/.state%s", rr->basedir, 5576936df76Sclaudio temp ? ".XXXXXXXX" : "") == -1) 5588ecbadc1Sclaudio err(1, NULL); 5598ecbadc1Sclaudio 5606936df76Sclaudio return nfile; 5618ecbadc1Sclaudio } 5628ecbadc1Sclaudio 5638ecbadc1Sclaudio static struct rrdprepo * 564b6884e9fSclaudio rrdp_find(unsigned int id) 5658ecbadc1Sclaudio { 5668ecbadc1Sclaudio struct rrdprepo *rr; 5678ecbadc1Sclaudio 5688ecbadc1Sclaudio SLIST_FOREACH(rr, &rrdprepos, entry) 5698ecbadc1Sclaudio if (id == rr->id) 5708ecbadc1Sclaudio break; 5718ecbadc1Sclaudio return rr; 5728ecbadc1Sclaudio } 5738ecbadc1Sclaudio 5748ecbadc1Sclaudio static void 5758ecbadc1Sclaudio rrdp_free(void) 5768ecbadc1Sclaudio { 5778ecbadc1Sclaudio struct rrdprepo *rr; 5788ecbadc1Sclaudio 5798ecbadc1Sclaudio while ((rr = SLIST_FIRST(&rrdprepos)) != NULL) { 5808ecbadc1Sclaudio SLIST_REMOVE_HEAD(&rrdprepos, entry); 5818ecbadc1Sclaudio 5828ecbadc1Sclaudio free(rr->notifyuri); 5838ecbadc1Sclaudio free(rr->basedir); 5848ecbadc1Sclaudio 5858ecbadc1Sclaudio filepath_free(&rr->deleted); 5868ecbadc1Sclaudio 5878ecbadc1Sclaudio free(rr); 5888ecbadc1Sclaudio } 5898ecbadc1Sclaudio } 5908ecbadc1Sclaudio 5910c3a2335Sclaudio /* 5920c3a2335Sclaudio * Check if a directory is an active rrdp repository. 5930c3a2335Sclaudio * Returns 1 if found else 0. 5940c3a2335Sclaudio */ 5951fc2657fSclaudio static struct repo * 5961fc2657fSclaudio repo_rrdp_bypath(const char *dir) 5978ecbadc1Sclaudio { 5981fc2657fSclaudio struct repo *rp; 5998ecbadc1Sclaudio 6001fc2657fSclaudio SLIST_FOREACH(rp, &repos, entry) { 6011fc2657fSclaudio if (rp->rrdp == NULL) 6021fc2657fSclaudio continue; 6031fc2657fSclaudio if (strcmp(dir, rp->rrdp->basedir) == 0) 6041fc2657fSclaudio return rp; 6051fc2657fSclaudio } 6061fc2657fSclaudio return NULL; 607512ef3b5Sclaudio } 6088ecbadc1Sclaudio 6090c3a2335Sclaudio /* 6100c3a2335Sclaudio * Check if the URI is actually covered by one of the repositories 6110c3a2335Sclaudio * that depend on this RRDP repository. 6120c3a2335Sclaudio * Returns 1 if the URI is valid, 0 if no repouri matches the URI. 6130c3a2335Sclaudio */ 6140c3a2335Sclaudio static int 6150c3a2335Sclaudio rrdp_uri_valid(struct rrdprepo *rr, const char *uri) 6160c3a2335Sclaudio { 6170c3a2335Sclaudio struct repo *rp; 6180c3a2335Sclaudio 6190c3a2335Sclaudio SLIST_FOREACH(rp, &repos, entry) { 6200c3a2335Sclaudio if (rp->rrdp != rr) 6210c3a2335Sclaudio continue; 6220c3a2335Sclaudio if (strncmp(uri, rp->repouri, strlen(rp->repouri)) == 0) 6230c3a2335Sclaudio return 1; 6240c3a2335Sclaudio } 6250c3a2335Sclaudio return 0; 6268ecbadc1Sclaudio } 6278ecbadc1Sclaudio 6288ecbadc1Sclaudio /* 6298ecbadc1Sclaudio * Allocate and insert a new repository. 6308ecbadc1Sclaudio */ 6318ecbadc1Sclaudio static struct repo * 6327af68c5cSclaudio repo_alloc(int talid) 6338ecbadc1Sclaudio { 6348ecbadc1Sclaudio struct repo *rp; 6358ecbadc1Sclaudio 6368ecbadc1Sclaudio if ((rp = calloc(1, sizeof(*rp))) == NULL) 6378ecbadc1Sclaudio err(1, NULL); 6388ecbadc1Sclaudio 6398ecbadc1Sclaudio rp->id = ++repoid; 6407af68c5cSclaudio rp->talid = talid; 641cecb0802Sjob rp->alarm = getmonotime() + repo_timeout; 6428ecbadc1Sclaudio TAILQ_INIT(&rp->queue); 6438ecbadc1Sclaudio SLIST_INSERT_HEAD(&repos, rp, entry); 6444f5f25cbSclaudio clock_gettime(CLOCK_MONOTONIC, &rp->start_time); 6458ecbadc1Sclaudio 6468ecbadc1Sclaudio stats.repos++; 6478ecbadc1Sclaudio return rp; 6488ecbadc1Sclaudio } 6498ecbadc1Sclaudio 6508ecbadc1Sclaudio /* 6518ecbadc1Sclaudio * Parse the RRDP state file if it exists and set the session struct 6528ecbadc1Sclaudio * based on that information. 6538ecbadc1Sclaudio */ 654b268327aSclaudio static struct rrdp_session * 655d0a27ef8Sjob rrdp_session_parse(struct rrdprepo *rr) 6568ecbadc1Sclaudio { 6578ecbadc1Sclaudio FILE *f; 658b268327aSclaudio struct rrdp_session *state; 659352e6c5dStb int fd, ln = 0, deltacnt = 0; 6608ecbadc1Sclaudio const char *errstr; 6618ecbadc1Sclaudio char *line = NULL, *file; 662352e6c5dStb size_t i, len = 0; 6638ecbadc1Sclaudio ssize_t n; 664d0a27ef8Sjob time_t now, weeks; 665d0a27ef8Sjob 666d0a27ef8Sjob now = time(NULL); 6678ecbadc1Sclaudio 668b268327aSclaudio if ((state = calloc(1, sizeof(*state))) == NULL) 669b268327aSclaudio err(1, NULL); 670b268327aSclaudio 6718ecbadc1Sclaudio file = rrdp_state_filename(rr, 0); 6728ecbadc1Sclaudio if ((fd = open(file, O_RDONLY)) == -1) { 6738ecbadc1Sclaudio if (errno != ENOENT) 6748ecbadc1Sclaudio warn("%s: open state file", rr->basedir); 6758ecbadc1Sclaudio free(file); 676dbcd1c31Sclaudio rr->last_reset = now; 677b268327aSclaudio return state; 6788ecbadc1Sclaudio } 6798ecbadc1Sclaudio free(file); 6808ecbadc1Sclaudio f = fdopen(fd, "r"); 6818ecbadc1Sclaudio if (f == NULL) 6828ecbadc1Sclaudio err(1, "fdopen"); 6838ecbadc1Sclaudio 6848ecbadc1Sclaudio while ((n = getline(&line, &len, f)) != -1) { 6858ecbadc1Sclaudio if (line[n - 1] == '\n') 6868ecbadc1Sclaudio line[n - 1] = '\0'; 6878ecbadc1Sclaudio switch (ln) { 6888ecbadc1Sclaudio case 0: 6898ecbadc1Sclaudio if ((state->session_id = strdup(line)) == NULL) 6908ecbadc1Sclaudio err(1, NULL); 6918ecbadc1Sclaudio break; 6928ecbadc1Sclaudio case 1: 6938ecbadc1Sclaudio state->serial = strtonum(line, 1, LLONG_MAX, &errstr); 694be9e59b4Stb if (errstr) { 695be9e59b4Stb warnx("%s: state file: serial is %s: %s", 696be9e59b4Stb rr->basedir, errstr, line); 697be9e59b4Stb goto reset; 698be9e59b4Stb } 6998ecbadc1Sclaudio break; 7008ecbadc1Sclaudio case 2: 701d0a27ef8Sjob rr->last_reset = strtonum(line, 1, LLONG_MAX, &errstr); 702be9e59b4Stb if (errstr) { 703be9e59b4Stb warnx("%s: state file: last_reset is %s: %s", 704be9e59b4Stb rr->basedir, errstr, line); 705be9e59b4Stb goto reset; 706be9e59b4Stb } 707d0a27ef8Sjob break; 708d0a27ef8Sjob case 3: 709b268327aSclaudio if (strcmp(line, "-") == 0) 710b268327aSclaudio break; 7118ecbadc1Sclaudio if ((state->last_mod = strdup(line)) == NULL) 7128ecbadc1Sclaudio err(1, NULL); 7138ecbadc1Sclaudio break; 7148ecbadc1Sclaudio default: 715be9e59b4Stb if (deltacnt >= MAX_RRDP_DELTAS) { 716be9e59b4Stb warnx("%s: state file: too many deltas: %d", 717be9e59b4Stb rr->basedir, deltacnt); 718be9e59b4Stb goto reset; 719be9e59b4Stb } 720b268327aSclaudio if ((state->deltas[deltacnt++] = strdup(line)) == NULL) 721b268327aSclaudio err(1, NULL); 722b268327aSclaudio break; 7238ecbadc1Sclaudio } 7248ecbadc1Sclaudio ln++; 7258ecbadc1Sclaudio } 7268ecbadc1Sclaudio 727be9e59b4Stb if (ferror(f)) { 728be9e59b4Stb warn("%s: error reading state file", rr->basedir); 729be9e59b4Stb goto reset; 730be9e59b4Stb } 731be9e59b4Stb 732d0a27ef8Sjob /* check if it's time for reinitialization */ 733d0a27ef8Sjob weeks = (now - rr->last_reset) / (86400 * 7); 734d0a27ef8Sjob if (now <= rr->last_reset || weeks > RRDP_RANDOM_REINIT_MAX) { 735d0a27ef8Sjob warnx("%s: reinitializing", rr->notifyuri); 736d0a27ef8Sjob goto reset; 737d0a27ef8Sjob } 738acc46298Stb if (arc4random_uniform(1U << RRDP_RANDOM_REINIT_MAX) < (1U << weeks)) { 739d0a27ef8Sjob warnx("%s: reinitializing", rr->notifyuri); 740d0a27ef8Sjob goto reset; 741d0a27ef8Sjob } 742d0a27ef8Sjob 7438ecbadc1Sclaudio fclose(f); 744b268327aSclaudio free(line); 745b268327aSclaudio return state; 7468ecbadc1Sclaudio 747d0a27ef8Sjob reset: 7488ecbadc1Sclaudio fclose(f); 749b268327aSclaudio free(line); 7508ecbadc1Sclaudio free(state->session_id); 7518ecbadc1Sclaudio free(state->last_mod); 752352e6c5dStb for (i = 0; i < sizeof(state->deltas) / sizeof(state->deltas[0]); i++) 753be9e59b4Stb free(state->deltas[i]); 7548ecbadc1Sclaudio memset(state, 0, sizeof(*state)); 755d0a27ef8Sjob rr->last_reset = now; 756b268327aSclaudio return state; 7578ecbadc1Sclaudio } 7588ecbadc1Sclaudio 7598ecbadc1Sclaudio /* 7608ecbadc1Sclaudio * Carefully write the RRDP session state file back. 7618ecbadc1Sclaudio */ 7628ecbadc1Sclaudio void 763b268327aSclaudio rrdp_session_save(unsigned int id, struct rrdp_session *state) 7648ecbadc1Sclaudio { 7658ecbadc1Sclaudio struct rrdprepo *rr; 7668ecbadc1Sclaudio char *temp, *file; 767b268327aSclaudio FILE *f = NULL; 768b268327aSclaudio int fd, i; 7698ecbadc1Sclaudio 7708ecbadc1Sclaudio rr = rrdp_find(id); 7718ecbadc1Sclaudio if (rr == NULL) 7723a50f0a9Sjmc errx(1, "non-existent rrdp repo %u", id); 7738ecbadc1Sclaudio 7748ecbadc1Sclaudio file = rrdp_state_filename(rr, 0); 7758ecbadc1Sclaudio temp = rrdp_state_filename(rr, 1); 7768ecbadc1Sclaudio 777b268327aSclaudio if ((fd = mkostemp(temp, O_CLOEXEC)) == -1) 778ea10885eSclaudio goto fail; 7798ecbadc1Sclaudio (void)fchmod(fd, 0644); 7808ecbadc1Sclaudio f = fdopen(fd, "w"); 7818ecbadc1Sclaudio if (f == NULL) 7828ecbadc1Sclaudio err(1, "fdopen"); 7838ecbadc1Sclaudio 7848ecbadc1Sclaudio /* write session state file out */ 785d0a27ef8Sjob if (fprintf(f, "%s\n%lld\n%lld\n", state->session_id, 786d0a27ef8Sjob state->serial, (long long)rr->last_reset) < 0) 7878ecbadc1Sclaudio goto fail; 7888ecbadc1Sclaudio 789b268327aSclaudio if (state->last_mod != NULL) { 790b268327aSclaudio if (fprintf(f, "%s\n", state->last_mod) < 0) 791b268327aSclaudio goto fail; 792b268327aSclaudio } else { 793b268327aSclaudio if (fprintf(f, "-\n") < 0) 794b268327aSclaudio goto fail; 795b268327aSclaudio } 796019c44efSclaudio for (i = 0; i < MAX_RRDP_DELTAS && state->deltas[i] != NULL; i++) { 797b268327aSclaudio if (fprintf(f, "%s\n", state->deltas[i]) < 0) 798b268327aSclaudio goto fail; 799b268327aSclaudio } 800b268327aSclaudio if (fclose(f) != 0) { 801b268327aSclaudio f = NULL; 802b268327aSclaudio goto fail; 803b268327aSclaudio } 804b268327aSclaudio 805b268327aSclaudio if (rename(temp, file) == -1) { 806b268327aSclaudio warn("%s: rename %s to %s", rr->basedir, temp, file); 807b268327aSclaudio unlink(temp); 808b268327aSclaudio } 8098ecbadc1Sclaudio 8108ecbadc1Sclaudio free(temp); 8118ecbadc1Sclaudio free(file); 8128ecbadc1Sclaudio return; 8138ecbadc1Sclaudio 8148ecbadc1Sclaudio fail: 815b268327aSclaudio warn("%s: save state to %s", rr->basedir, temp); 816b268327aSclaudio if (f != NULL) 817b268327aSclaudio fclose(f); 8188ecbadc1Sclaudio unlink(temp); 8198ecbadc1Sclaudio free(temp); 8208ecbadc1Sclaudio free(file); 8218ecbadc1Sclaudio } 8228ecbadc1Sclaudio 823b268327aSclaudio /* 824b268327aSclaudio * Free an rrdp_session pointer. Safe to call with NULL. 825b268327aSclaudio */ 826b268327aSclaudio void 827b268327aSclaudio rrdp_session_free(struct rrdp_session *s) 828b268327aSclaudio { 829b268327aSclaudio size_t i; 830b268327aSclaudio 831b268327aSclaudio if (s == NULL) 832b268327aSclaudio return; 833b268327aSclaudio free(s->session_id); 834b268327aSclaudio free(s->last_mod); 835b268327aSclaudio for (i = 0; i < sizeof(s->deltas) / sizeof(s->deltas[0]); i++) 836b268327aSclaudio free(s->deltas[i]); 837b268327aSclaudio free(s); 838b268327aSclaudio } 839b268327aSclaudio 840b268327aSclaudio void 841b268327aSclaudio rrdp_session_buffer(struct ibuf *b, const struct rrdp_session *s) 842b268327aSclaudio { 843b268327aSclaudio size_t i; 844b268327aSclaudio 845b268327aSclaudio io_str_buffer(b, s->session_id); 846b268327aSclaudio io_simple_buffer(b, &s->serial, sizeof(s->serial)); 847b268327aSclaudio io_str_buffer(b, s->last_mod); 848b268327aSclaudio for (i = 0; i < sizeof(s->deltas) / sizeof(s->deltas[0]); i++) 849b268327aSclaudio io_str_buffer(b, s->deltas[i]); 850b268327aSclaudio } 851b268327aSclaudio 852b268327aSclaudio struct rrdp_session * 853b268327aSclaudio rrdp_session_read(struct ibuf *b) 854b268327aSclaudio { 855b268327aSclaudio struct rrdp_session *s; 856b268327aSclaudio size_t i; 857b268327aSclaudio 858b268327aSclaudio if ((s = calloc(1, sizeof(*s))) == NULL) 859b268327aSclaudio err(1, NULL); 860b268327aSclaudio 861b268327aSclaudio io_read_str(b, &s->session_id); 862b268327aSclaudio io_read_buf(b, &s->serial, sizeof(s->serial)); 863b268327aSclaudio io_read_str(b, &s->last_mod); 864b268327aSclaudio for (i = 0; i < sizeof(s->deltas) / sizeof(s->deltas[0]); i++) 865b268327aSclaudio io_read_str(b, &s->deltas[i]); 866b268327aSclaudio 867b268327aSclaudio return s; 868b268327aSclaudio } 869b268327aSclaudio 8706936df76Sclaudio static struct rrdprepo * 8710c3a2335Sclaudio rrdp_get(const char *uri) 8726936df76Sclaudio { 873b268327aSclaudio struct rrdp_session *state; 8746936df76Sclaudio struct rrdprepo *rr; 8756936df76Sclaudio 8766936df76Sclaudio SLIST_FOREACH(rr, &rrdprepos, entry) 8776936df76Sclaudio if (strcmp(rr->notifyuri, uri) == 0) { 8786936df76Sclaudio if (rr->state == REPO_FAILED) 8796936df76Sclaudio return NULL; 8806936df76Sclaudio return rr; 8816936df76Sclaudio } 8826936df76Sclaudio 8836936df76Sclaudio if ((rr = calloc(1, sizeof(*rr))) == NULL) 8846936df76Sclaudio err(1, NULL); 8856936df76Sclaudio 8866936df76Sclaudio rr->id = ++repoid; 8876936df76Sclaudio SLIST_INSERT_HEAD(&rrdprepos, rr, entry); 8886936df76Sclaudio 8896936df76Sclaudio if ((rr->notifyuri = strdup(uri)) == NULL) 8906936df76Sclaudio err(1, NULL); 8918f17a701Sclaudio rr->basedir = repo_dir(uri, ".rrdp", 1); 8926936df76Sclaudio 8936936df76Sclaudio RB_INIT(&rr->deleted); 8946936df76Sclaudio 8956936df76Sclaudio /* create base directory */ 8966936df76Sclaudio if (mkpath(rr->basedir) == -1) { 8976936df76Sclaudio warn("mkpath %s", rr->basedir); 8986936df76Sclaudio rrdp_finish(rr->id, 0); 8996936df76Sclaudio return rr; 9006936df76Sclaudio } 9010c3a2335Sclaudio 9020c3a2335Sclaudio /* parse state and start the sync */ 903b268327aSclaudio state = rrdp_session_parse(rr); 904b268327aSclaudio rrdp_fetch(rr->id, rr->notifyuri, rr->notifyuri, state); 905b268327aSclaudio rrdp_session_free(state); 9066936df76Sclaudio 9076936df76Sclaudio logx("%s: pulling from %s", rr->notifyuri, "network"); 9086936df76Sclaudio 9096936df76Sclaudio return rr; 9106936df76Sclaudio } 9116936df76Sclaudio 912ea10885eSclaudio /* 913264f4ef9Sclaudio * Remove RRDP repo and start over. 914264f4ef9Sclaudio */ 915264f4ef9Sclaudio void 916264f4ef9Sclaudio rrdp_clear(unsigned int id) 917264f4ef9Sclaudio { 918264f4ef9Sclaudio struct rrdprepo *rr; 919264f4ef9Sclaudio 920264f4ef9Sclaudio rr = rrdp_find(id); 921264f4ef9Sclaudio if (rr == NULL) 9223a50f0a9Sjmc errx(1, "non-existent rrdp repo %u", id); 923264f4ef9Sclaudio 924264f4ef9Sclaudio /* remove rrdp repository contents */ 925264f4ef9Sclaudio remove_contents(rr->basedir); 926264f4ef9Sclaudio } 927264f4ef9Sclaudio 928264f4ef9Sclaudio /* 929ea10885eSclaudio * Write a file into the temporary RRDP dir but only after checking 930ea10885eSclaudio * its hash (if required). The function also makes sure that the file 931ea10885eSclaudio * tracking is properly adjusted. 932ea10885eSclaudio * Returns 1 on success, 0 if the repo is corrupt, -1 on IO error 933ea10885eSclaudio */ 9348ecbadc1Sclaudio int 935b6884e9fSclaudio rrdp_handle_file(unsigned int id, enum publish_type pt, char *uri, 9368ecbadc1Sclaudio char *hash, size_t hlen, char *data, size_t dlen) 9378ecbadc1Sclaudio { 9388ecbadc1Sclaudio struct rrdprepo *rr; 9398ecbadc1Sclaudio struct filepath *fp; 9408ecbadc1Sclaudio ssize_t s; 9410c3a2335Sclaudio char *fn = NULL; 94258874dc7Sclaudio int fd = -1, try = 0, deleted = 0; 943f209885aSclaudio int flags; 9448ecbadc1Sclaudio 9458ecbadc1Sclaudio rr = rrdp_find(id); 9468ecbadc1Sclaudio if (rr == NULL) 9473a50f0a9Sjmc errx(1, "non-existent rrdp repo %u", id); 948ea10885eSclaudio if (rr->state == REPO_FAILED) 949ea10885eSclaudio return -1; 9508ecbadc1Sclaudio 9510c3a2335Sclaudio /* check hash of original file for updates and deletes */ 9528ecbadc1Sclaudio if (pt == PUB_UPD || pt == PUB_DEL) { 9538ecbadc1Sclaudio if (filepath_exists(&rr->deleted, uri)) { 9548ecbadc1Sclaudio warnx("%s: already deleted", uri); 9558ecbadc1Sclaudio return 0; 9568ecbadc1Sclaudio } 9570c3a2335Sclaudio /* try to open file first in rrdp then in valid repo */ 9580c3a2335Sclaudio do { 9590c3a2335Sclaudio free(fn); 9600c3a2335Sclaudio if ((fn = rrdp_filename(rr, uri, try++)) == NULL) 9618ecbadc1Sclaudio return 0; 96287c7c78dSclaudio fd = open(fn, O_RDONLY); 9630c3a2335Sclaudio } while (fd == -1 && try < 2); 9640c3a2335Sclaudio 96587c7c78dSclaudio if (!valid_filehash(fd, hash, hlen)) { 96687c7c78dSclaudio warnx("%s: bad file digest for %s", rr->notifyuri, fn); 9678ecbadc1Sclaudio free(fn); 9688ecbadc1Sclaudio return 0; 9698ecbadc1Sclaudio } 9708ecbadc1Sclaudio free(fn); 9718ecbadc1Sclaudio } 9728ecbadc1Sclaudio 9730c3a2335Sclaudio /* write new content or mark uri as deleted. */ 9748ecbadc1Sclaudio if (pt == PUB_DEL) { 97592360713Sclaudio filepath_add(&rr->deleted, uri, 0, 0, 1); 9768ecbadc1Sclaudio } else { 97766477c5cSclaudio fp = filepath_find(&rr->deleted, uri); 97858874dc7Sclaudio if (fp != NULL) { 97966477c5cSclaudio filepath_put(&rr->deleted, fp); 98058874dc7Sclaudio deleted = 1; 98158874dc7Sclaudio } 98266477c5cSclaudio 9830c3a2335Sclaudio /* add new file to rrdp dir */ 9840c3a2335Sclaudio if ((fn = rrdp_filename(rr, uri, 0)) == NULL) 9858ecbadc1Sclaudio return 0; 9868ecbadc1Sclaudio 9876cf9bac2Sclaudio if (repo_mkpath(AT_FDCWD, fn) == -1) 988ea10885eSclaudio goto fail; 989ea10885eSclaudio 990f209885aSclaudio flags = O_WRONLY|O_CREAT|O_TRUNC; 99158874dc7Sclaudio if (pt == PUB_ADD && !deleted) 992f209885aSclaudio flags |= O_EXCL; 993f209885aSclaudio fd = open(fn, flags, 0644); 9948ecbadc1Sclaudio if (fd == -1) { 995f209885aSclaudio if (errno == EEXIST) { 996f209885aSclaudio warnx("%s: duplicate publish element for %s", 997f209885aSclaudio rr->notifyuri, fn); 998f209885aSclaudio free(fn); 999f209885aSclaudio return 0; 1000f209885aSclaudio } 10018ecbadc1Sclaudio warn("open %s", fn); 1002ea10885eSclaudio goto fail; 10038ecbadc1Sclaudio } 10048ecbadc1Sclaudio 10058ecbadc1Sclaudio if ((s = write(fd, data, dlen)) == -1) { 10068ecbadc1Sclaudio warn("write %s", fn); 1007ea10885eSclaudio goto fail; 10088ecbadc1Sclaudio } 10098ecbadc1Sclaudio close(fd); 1010ea10885eSclaudio if ((size_t)s != dlen) /* impossible */ 1011ea10885eSclaudio errx(1, "short write %s", fn); 10128ecbadc1Sclaudio free(fn); 10138ecbadc1Sclaudio } 10148ecbadc1Sclaudio 10158ecbadc1Sclaudio return 1; 1016ea10885eSclaudio 1017ea10885eSclaudio fail: 1018ea10885eSclaudio rr->state = REPO_FAILED; 1019ea10885eSclaudio if (fd != -1) 1020ea10885eSclaudio close(fd); 1021ea10885eSclaudio free(fn); 1022ea10885eSclaudio return -1; 10238ecbadc1Sclaudio } 10248ecbadc1Sclaudio 10258ecbadc1Sclaudio /* 10268ecbadc1Sclaudio * RSYNC sync finished, either with or without success. 10278ecbadc1Sclaudio */ 10288ecbadc1Sclaudio void 1029b6884e9fSclaudio rsync_finish(unsigned int id, int ok) 10308ecbadc1Sclaudio { 10318ecbadc1Sclaudio struct rsyncrepo *rr; 10328ecbadc1Sclaudio struct tarepo *tr; 10338ecbadc1Sclaudio 10348ecbadc1Sclaudio tr = ta_find(id); 10358ecbadc1Sclaudio if (tr != NULL) { 10367af68c5cSclaudio /* repository changed state already, ignore request */ 10377af68c5cSclaudio if (tr->state != REPO_LOADING) 10387af68c5cSclaudio return; 10398ecbadc1Sclaudio if (ok) { 10408ecbadc1Sclaudio logx("ta/%s: loaded from network", tr->descr); 10418ecbadc1Sclaudio stats.rsync_repos++; 10428ecbadc1Sclaudio tr->state = REPO_DONE; 104399ee4841Sclaudio repo_done(tr, 1); 10449e8ec711Sclaudio } else { 10452ed2b457Sjob warnx("ta/%s: load from network failed", tr->descr); 10469e8ec711Sclaudio stats.rsync_fails++; 10479e8ec711Sclaudio tr->uriidx++; 10488ecbadc1Sclaudio ta_fetch(tr); 10498ecbadc1Sclaudio } 10508ecbadc1Sclaudio return; 10518ecbadc1Sclaudio } 10528ecbadc1Sclaudio 10538ecbadc1Sclaudio rr = rsync_find(id); 10548ecbadc1Sclaudio if (rr == NULL) 1055b6884e9fSclaudio errx(1, "unknown rsync repo %u", id); 10567af68c5cSclaudio /* repository changed state already, ignore request */ 10577af68c5cSclaudio if (rr->state != REPO_LOADING) 10587af68c5cSclaudio return; 1059264f4ef9Sclaudio 10608ecbadc1Sclaudio if (ok) { 10618ecbadc1Sclaudio logx("%s: loaded from network", rr->basedir); 10628ecbadc1Sclaudio stats.rsync_repos++; 10638ecbadc1Sclaudio rr->state = REPO_DONE; 10648ecbadc1Sclaudio } else { 10652ed2b457Sjob warnx("%s: load from network failed, fallback to cache", 10668ecbadc1Sclaudio rr->basedir); 10678ecbadc1Sclaudio stats.rsync_fails++; 10688ecbadc1Sclaudio rr->state = REPO_FAILED; 10690c3a2335Sclaudio /* clear rsync repo since it failed */ 10700c3a2335Sclaudio remove_contents(rr->basedir); 10718ecbadc1Sclaudio } 10728ecbadc1Sclaudio 107399ee4841Sclaudio repo_done(rr, ok); 10748ecbadc1Sclaudio } 10758ecbadc1Sclaudio 10768ecbadc1Sclaudio /* 10773a50f0a9Sjmc * RRDP sync finished, either with or without success. 10788ecbadc1Sclaudio */ 10798ecbadc1Sclaudio void 1080b6884e9fSclaudio rrdp_finish(unsigned int id, int ok) 10818ecbadc1Sclaudio { 10828ecbadc1Sclaudio struct rrdprepo *rr; 10838ecbadc1Sclaudio 10848ecbadc1Sclaudio rr = rrdp_find(id); 10858ecbadc1Sclaudio if (rr == NULL) 1086b6884e9fSclaudio errx(1, "unknown RRDP repo %u", id); 10877af68c5cSclaudio /* repository changed state already, ignore request */ 10887af68c5cSclaudio if (rr->state != REPO_LOADING) 10897af68c5cSclaudio return; 10908ecbadc1Sclaudio 10910c3a2335Sclaudio if (ok) { 10928ecbadc1Sclaudio logx("%s: loaded from network", rr->notifyuri); 10938ecbadc1Sclaudio stats.rrdp_repos++; 1094264f4ef9Sclaudio rr->state = REPO_DONE; 109599ee4841Sclaudio } else { 109647de54a6Sclaudio warnx("%s: load from network failed, fallback to %s", 109747de54a6Sclaudio rr->notifyuri, nofetch ? "cache" : "rsync"); 1098264f4ef9Sclaudio stats.rrdp_fails++; 1099264f4ef9Sclaudio rr->state = REPO_FAILED; 11000c3a2335Sclaudio /* clear the RRDP repo since it failed */ 11010c3a2335Sclaudio remove_contents(rr->basedir); 1102ffe0cd68Sclaudio /* also clear the list of deleted files */ 1103ffe0cd68Sclaudio filepath_free(&rr->deleted); 11048ecbadc1Sclaudio } 11050c3a2335Sclaudio 11060c3a2335Sclaudio repo_done(rr, ok); 11078ecbadc1Sclaudio } 11088ecbadc1Sclaudio 11098ecbadc1Sclaudio /* 11108ecbadc1Sclaudio * Handle responses from the http process. For TA file, either rename 11118ecbadc1Sclaudio * or delete the temporary file. For RRDP requests relay the request 11128ecbadc1Sclaudio * over to the rrdp process. 11138ecbadc1Sclaudio */ 11148ecbadc1Sclaudio void 1115b6884e9fSclaudio http_finish(unsigned int id, enum http_result res, const char *last_mod) 11168ecbadc1Sclaudio { 11178ecbadc1Sclaudio struct tarepo *tr; 11188ecbadc1Sclaudio 11198ecbadc1Sclaudio tr = ta_find(id); 11208ecbadc1Sclaudio if (tr == NULL) { 11213a50f0a9Sjmc /* not a TA fetch therefore RRDP */ 11228ecbadc1Sclaudio rrdp_http_done(id, res, last_mod); 11238ecbadc1Sclaudio return; 11248ecbadc1Sclaudio } 11258ecbadc1Sclaudio 11267af68c5cSclaudio /* repository changed state already, ignore request */ 11277af68c5cSclaudio if (tr->state != REPO_LOADING) 11287af68c5cSclaudio return; 11297af68c5cSclaudio 11308ecbadc1Sclaudio /* Move downloaded TA file into place, or unlink on failure. */ 11318ecbadc1Sclaudio if (res == HTTP_OK) { 11328ecbadc1Sclaudio logx("ta/%s: loaded from network", tr->descr); 11338ecbadc1Sclaudio tr->state = REPO_DONE; 11348ecbadc1Sclaudio stats.http_repos++; 113599ee4841Sclaudio repo_done(tr, 1); 11368ecbadc1Sclaudio } else { 11372674db3cSclaudio remove_contents(tr->basedir); 11388ecbadc1Sclaudio 11399e8ec711Sclaudio tr->uriidx++; 11402ed2b457Sjob warnx("ta/%s: load from network failed", tr->descr); 11418ecbadc1Sclaudio ta_fetch(tr); 11428ecbadc1Sclaudio } 11438ecbadc1Sclaudio } 11448ecbadc1Sclaudio 11458ecbadc1Sclaudio /* 11468ecbadc1Sclaudio * Look up a trust anchor, queueing it for download if not found. 11478ecbadc1Sclaudio */ 11488ecbadc1Sclaudio struct repo * 11497af68c5cSclaudio ta_lookup(int id, struct tal *tal) 11508ecbadc1Sclaudio { 11518ecbadc1Sclaudio struct repo *rp; 11528ecbadc1Sclaudio 115330a08502Stb if (tal->num_uris == 0) 11540c3a2335Sclaudio errx(1, "TAL %s has no URI", tal->descr); 11550c3a2335Sclaudio 11568ecbadc1Sclaudio /* Look up in repository table. (Lookup should actually fail here) */ 11578ecbadc1Sclaudio SLIST_FOREACH(rp, &repos, entry) { 1158995b4011Sclaudio if (strcmp(rp->repouri, tal->uri[0]) == 0) 11598ecbadc1Sclaudio return rp; 11608ecbadc1Sclaudio } 11618ecbadc1Sclaudio 11627af68c5cSclaudio rp = repo_alloc(id); 11630c3a2335Sclaudio rp->basedir = repo_dir(tal->descr, "ta", 0); 1164995b4011Sclaudio if ((rp->repouri = strdup(tal->uri[0])) == NULL) 11658ecbadc1Sclaudio err(1, NULL); 1166fee18e37Sclaudio 11670c3a2335Sclaudio /* check if sync disabled ... */ 11680c3a2335Sclaudio if (noop) { 1169995b4011Sclaudio logx("%s: using cache", rp->basedir); 11700c3a2335Sclaudio entityq_flush(&rp->queue, rp); 11710c3a2335Sclaudio return rp; 11720c3a2335Sclaudio } 11730c3a2335Sclaudio 11746e120aeaSjob /* try to create base directory */ 11756e120aeaSjob if (mkpath(rp->basedir) == -1) 11766e120aeaSjob warn("mkpath %s", rp->basedir); 11776e120aeaSjob 1178578d50dbSclaudio rp->ta = ta_get(tal); 11798ecbadc1Sclaudio 11800c3a2335Sclaudio /* need to check if it was already loaded */ 11811c9657abSclaudio if (repo_state(rp) != REPO_LOADING) 11821c9657abSclaudio entityq_flush(&rp->queue, rp); 11831c9657abSclaudio 11848ecbadc1Sclaudio return rp; 11858ecbadc1Sclaudio } 11868ecbadc1Sclaudio 11878ecbadc1Sclaudio /* 11888ecbadc1Sclaudio * Look up a repository, queueing it for discovery if not found. 11898ecbadc1Sclaudio */ 11908ecbadc1Sclaudio struct repo * 119199ee4841Sclaudio repo_lookup(int talid, const char *uri, const char *notify) 11928ecbadc1Sclaudio { 11938ecbadc1Sclaudio struct repo *rp; 1194a260f1c5Sclaudio char *repouri; 1195a260f1c5Sclaudio 1196a260f1c5Sclaudio if ((repouri = rsync_base_uri(uri)) == NULL) 1197a260f1c5Sclaudio errx(1, "bad caRepository URI: %s", uri); 11988ecbadc1Sclaudio 11998ecbadc1Sclaudio /* Look up in repository table. */ 12008ecbadc1Sclaudio SLIST_FOREACH(rp, &repos, entry) { 1201a260f1c5Sclaudio if (strcmp(rp->repouri, repouri) != 0) 12028ecbadc1Sclaudio continue; 1203a260f1c5Sclaudio if (rp->notifyuri != NULL) { 1204a260f1c5Sclaudio if (notify == NULL) 1205a260f1c5Sclaudio continue; 1206a260f1c5Sclaudio if (strcmp(rp->notifyuri, notify) != 0) 1207a260f1c5Sclaudio continue; 1208a260f1c5Sclaudio } else if (notify != NULL) 1209a260f1c5Sclaudio continue; 1210a260f1c5Sclaudio /* found matching repo */ 1211a260f1c5Sclaudio free(repouri); 12128ecbadc1Sclaudio return rp; 12138ecbadc1Sclaudio } 12148ecbadc1Sclaudio 121599ee4841Sclaudio rp = repo_alloc(talid); 12168f17a701Sclaudio rp->basedir = repo_dir(repouri, NULL, 0); 1217a260f1c5Sclaudio rp->repouri = repouri; 1218a260f1c5Sclaudio if (notify != NULL) 1219a260f1c5Sclaudio if ((rp->notifyuri = strdup(notify)) == NULL) 12208ecbadc1Sclaudio err(1, NULL); 12218ecbadc1Sclaudio 122299ee4841Sclaudio if (++talrepocnt[talid] >= MAX_REPO_PER_TAL) { 122399ee4841Sclaudio if (talrepocnt[talid] == MAX_REPO_PER_TAL) 122499ee4841Sclaudio warnx("too many repositories under %s", tals[talid]); 1225fee18e37Sclaudio nofetch = 1; 1226fee18e37Sclaudio } 1227fee18e37Sclaudio 12280c3a2335Sclaudio /* check if sync disabled ... */ 12290c3a2335Sclaudio if (noop || nofetch) { 12300c3a2335Sclaudio logx("%s: using cache", rp->basedir); 12310c3a2335Sclaudio entityq_flush(&rp->queue, rp); 12320c3a2335Sclaudio return rp; 12330c3a2335Sclaudio } 12340c3a2335Sclaudio 12356e120aeaSjob /* try to create base directory */ 12366e120aeaSjob if (mkpath(rp->basedir) == -1) 12376e120aeaSjob warn("mkpath %s", rp->basedir); 12386e120aeaSjob 12390c3a2335Sclaudio /* ... else try RRDP first if available then rsync */ 12400c3a2335Sclaudio if (notify != NULL) 12410c3a2335Sclaudio rp->rrdp = rrdp_get(notify); 12420c3a2335Sclaudio if (rp->rrdp == NULL) 12430c3a2335Sclaudio rp->rsync = rsync_get(uri, rp->basedir); 12440c3a2335Sclaudio 12450c3a2335Sclaudio /* need to check if it was already loaded */ 1246100ded9eSclaudio if (repo_state(rp) != REPO_LOADING) 1247100ded9eSclaudio entityq_flush(&rp->queue, rp); 1248100ded9eSclaudio 12498ecbadc1Sclaudio return rp; 12508ecbadc1Sclaudio } 12518ecbadc1Sclaudio 12528ecbadc1Sclaudio /* 1253100ded9eSclaudio * Find repository by identifier. 12548ecbadc1Sclaudio */ 1255100ded9eSclaudio struct repo * 1256100ded9eSclaudio repo_byid(unsigned int id) 12578ecbadc1Sclaudio { 1258100ded9eSclaudio struct repo *rp; 12598ecbadc1Sclaudio 1260100ded9eSclaudio SLIST_FOREACH(rp, &repos, entry) { 1261100ded9eSclaudio if (rp->id == id) 1262100ded9eSclaudio return rp; 1263100ded9eSclaudio } 12648ecbadc1Sclaudio return NULL; 12658ecbadc1Sclaudio } 12668ecbadc1Sclaudio 1267*05a19cc6Sjob static struct repo * 1268*05a19cc6Sjob repo_rsync_bypath(const char *path) 1269*05a19cc6Sjob { 1270*05a19cc6Sjob struct repo *rp; 1271*05a19cc6Sjob 1272*05a19cc6Sjob SLIST_FOREACH(rp, &repos, entry) { 1273*05a19cc6Sjob if (rp->rsync == NULL) 1274*05a19cc6Sjob continue; 1275*05a19cc6Sjob if (strcmp(rp->basedir, path) == 0) 1276*05a19cc6Sjob return rp; 1277*05a19cc6Sjob } 1278*05a19cc6Sjob return NULL; 1279*05a19cc6Sjob } 1280*05a19cc6Sjob 1281100ded9eSclaudio /* 12826cf9bac2Sclaudio * Find repository by base path. 12836cf9bac2Sclaudio */ 12846cf9bac2Sclaudio static struct repo * 12856cf9bac2Sclaudio repo_bypath(const char *path) 12866cf9bac2Sclaudio { 12876cf9bac2Sclaudio struct repo *rp; 12886cf9bac2Sclaudio 12896cf9bac2Sclaudio SLIST_FOREACH(rp, &repos, entry) { 12906cf9bac2Sclaudio if (strcmp(rp->basedir, path) == 0) 12916cf9bac2Sclaudio return rp; 12926cf9bac2Sclaudio } 12936cf9bac2Sclaudio return NULL; 12946cf9bac2Sclaudio } 12956cf9bac2Sclaudio 12966cf9bac2Sclaudio /* 12970c3a2335Sclaudio * Return the repository base or alternate directory. 1298100ded9eSclaudio * Returned string must be freed by caller. 1299100ded9eSclaudio */ 1300100ded9eSclaudio char * 13010c3a2335Sclaudio repo_basedir(const struct repo *rp, int wantvalid) 1302100ded9eSclaudio { 13030c3a2335Sclaudio char *path = NULL; 13048ecbadc1Sclaudio 13050c3a2335Sclaudio if (!wantvalid) { 1306100ded9eSclaudio if (rp->ta) { 1307100ded9eSclaudio if ((path = strdup(rp->ta->basedir)) == NULL) 13088ecbadc1Sclaudio err(1, NULL); 1309100ded9eSclaudio } else if (rp->rsync) { 1310100ded9eSclaudio if ((path = strdup(rp->rsync->basedir)) == NULL) 1311100ded9eSclaudio err(1, NULL); 1312100ded9eSclaudio } else if (rp->rrdp) { 1313100ded9eSclaudio path = rrdp_filename(rp->rrdp, rp->repouri, 0); 1314100ded9eSclaudio } else 13150c3a2335Sclaudio path = NULL; /* only valid repo available */ 13160c3a2335Sclaudio } else if (rp->basedir != NULL) { 13170c3a2335Sclaudio if ((path = strdup(rp->basedir)) == NULL) 13180c3a2335Sclaudio err(1, NULL); 13190c3a2335Sclaudio } 1320100ded9eSclaudio 1321100ded9eSclaudio return path; 1322100ded9eSclaudio } 1323100ded9eSclaudio 1324100ded9eSclaudio /* 1325100ded9eSclaudio * Return the repository identifier. 1326100ded9eSclaudio */ 1327100ded9eSclaudio unsigned int 1328100ded9eSclaudio repo_id(const struct repo *rp) 1329100ded9eSclaudio { 1330100ded9eSclaudio return rp->id; 1331100ded9eSclaudio } 1332100ded9eSclaudio 1333100ded9eSclaudio /* 1334100ded9eSclaudio * Return the repository URI. 1335100ded9eSclaudio */ 1336100ded9eSclaudio const char * 1337100ded9eSclaudio repo_uri(const struct repo *rp) 1338100ded9eSclaudio { 1339100ded9eSclaudio return rp->repouri; 13408ecbadc1Sclaudio } 13418ecbadc1Sclaudio 13424f5f25cbSclaudio /* 13434f5f25cbSclaudio * Return the repository URI. 13444f5f25cbSclaudio */ 13454f5f25cbSclaudio void 13464f5f25cbSclaudio repo_fetch_uris(const struct repo *rp, const char **carepo, 13474f5f25cbSclaudio const char **notifyuri) 13484f5f25cbSclaudio { 13494f5f25cbSclaudio *carepo = rp->repouri; 13504f5f25cbSclaudio *notifyuri = rp->notifyuri; 13514f5f25cbSclaudio } 13524f5f25cbSclaudio 13534f5f25cbSclaudio /* 13544f5f25cbSclaudio * Return 1 if repository is synced else 0. 13554f5f25cbSclaudio */ 13564f5f25cbSclaudio int 13574f5f25cbSclaudio repo_synced(const struct repo *rp) 13584f5f25cbSclaudio { 13594f5f25cbSclaudio if (repo_state(rp) == REPO_DONE && 13604f5f25cbSclaudio !(rp->rrdp == NULL && rp->rsync == NULL && rp->ta == NULL)) 13614f5f25cbSclaudio return 1; 13624f5f25cbSclaudio return 0; 13634f5f25cbSclaudio } 13644f5f25cbSclaudio 13654f5f25cbSclaudio /* 1366d0837792Sclaudio * Return the protocol string "rrdp", "rsync", "https" which was used to sync. 1367d0837792Sclaudio * Result is only correct if repository was properly synced. 1368d0837792Sclaudio */ 1369d0837792Sclaudio const char * 1370d0837792Sclaudio repo_proto(const struct repo *rp) 1371d0837792Sclaudio { 1372d0837792Sclaudio 1373d0837792Sclaudio if (rp->ta != NULL) { 1374d0837792Sclaudio const struct tarepo *tr = rp->ta; 137530a08502Stb if (tr->uriidx < tr->num_uris && 13760610060dSjob strncasecmp(tr->uri[tr->uriidx], RSYNC_PROTO, 13770610060dSjob RSYNC_PROTO_LEN) == 0) 1378d0837792Sclaudio return "rsync"; 1379d0837792Sclaudio else 1380d0837792Sclaudio return "https"; 1381d0837792Sclaudio } 1382d0837792Sclaudio if (rp->rrdp != NULL) 1383d0837792Sclaudio return "rrdp"; 1384d0837792Sclaudio return "rsync"; 1385d0837792Sclaudio } 1386d0837792Sclaudio 1387d0837792Sclaudio /* 13884f5f25cbSclaudio * Return the repository tal ID. 13894f5f25cbSclaudio */ 13904f5f25cbSclaudio int 13914f5f25cbSclaudio repo_talid(const struct repo *rp) 13924f5f25cbSclaudio { 13934f5f25cbSclaudio return rp->talid; 13944f5f25cbSclaudio } 13954f5f25cbSclaudio 13968ecbadc1Sclaudio int 13978ecbadc1Sclaudio repo_queued(struct repo *rp, struct entity *p) 13988ecbadc1Sclaudio { 13998ecbadc1Sclaudio if (repo_state(rp) == REPO_LOADING) { 14008ecbadc1Sclaudio TAILQ_INSERT_TAIL(&rp->queue, p, entries); 14018ecbadc1Sclaudio return 1; 14028ecbadc1Sclaudio } 14038ecbadc1Sclaudio return 0; 14048ecbadc1Sclaudio } 14058ecbadc1Sclaudio 14067af68c5cSclaudio static void 14077af68c5cSclaudio repo_fail(struct repo *rp) 14087af68c5cSclaudio { 14097af68c5cSclaudio /* reset the alarm since code may fallback to rsync */ 1410cecb0802Sjob rp->alarm = getmonotime() + repo_timeout; 14117af68c5cSclaudio 14127af68c5cSclaudio if (rp->ta) 14137af68c5cSclaudio http_finish(rp->ta->id, HTTP_FAILED, NULL); 14147af68c5cSclaudio else if (rp->rsync) 14157af68c5cSclaudio rsync_finish(rp->rsync->id, 0); 1416100ded9eSclaudio else if (rp->rrdp) 1417100ded9eSclaudio rrdp_finish(rp->rrdp->id, 0); 14187af68c5cSclaudio else 14197af68c5cSclaudio errx(1, "%s: bad repo", rp->repouri); 14207af68c5cSclaudio } 14217af68c5cSclaudio 14220cda9bffSclaudio static void 14230cda9bffSclaudio repo_abort(struct repo *rp) 14240cda9bffSclaudio { 14250cda9bffSclaudio /* reset the alarm */ 14260cda9bffSclaudio rp->alarm = getmonotime() + repo_timeout; 14270cda9bffSclaudio 14287cb76e55Sjob if (rp->rsync) { 14297cb76e55Sjob warnx("%s: synchronisation timeout", rp->repouri); 14300cda9bffSclaudio rsync_abort(rp->rsync->id); 14317cb76e55Sjob } else if (rp->rrdp) { 14327cb76e55Sjob warnx("%s: synchronisation timeout", rp->notifyuri); 14330cda9bffSclaudio rrdp_abort(rp->rrdp->id); 14347cb76e55Sjob } 14357cb76e55Sjob 14360cda9bffSclaudio repo_fail(rp); 14370cda9bffSclaudio } 14380cda9bffSclaudio 14397ba5db23Sclaudio int 14407ba5db23Sclaudio repo_check_timeout(int timeout) 14417af68c5cSclaudio { 14427af68c5cSclaudio struct repo *rp; 14437af68c5cSclaudio time_t now; 144447de54a6Sclaudio int diff; 14457af68c5cSclaudio 14467af68c5cSclaudio now = getmonotime(); 144747de54a6Sclaudio 144847de54a6Sclaudio /* check against our runtime deadline first */ 144947de54a6Sclaudio if (deadline != 0) { 145047de54a6Sclaudio if (deadline <= now) { 145147de54a6Sclaudio warnx("deadline reached, giving up on repository sync"); 145247de54a6Sclaudio nofetch = 1; 145347de54a6Sclaudio /* clear deadline since nofetch is set */ 145447de54a6Sclaudio deadline = 0; 145547de54a6Sclaudio /* increase now enough so that all pending repos fail */ 145647de54a6Sclaudio now += repo_timeout; 145747de54a6Sclaudio } else { 145847de54a6Sclaudio diff = deadline - now; 145947de54a6Sclaudio diff *= 1000; 146047de54a6Sclaudio if (timeout == INFTIM || diff < timeout) 146147de54a6Sclaudio timeout = diff; 146247de54a6Sclaudio } 146347de54a6Sclaudio } 14647af68c5cSclaudio /* Look up in repository table. (Lookup should actually fail here) */ 14657af68c5cSclaudio SLIST_FOREACH(rp, &repos, entry) { 14667af68c5cSclaudio if (repo_state(rp) == REPO_LOADING) { 14677cb76e55Sjob if (rp->alarm <= now) 14680cda9bffSclaudio repo_abort(rp); 14697cb76e55Sjob else { 147047de54a6Sclaudio diff = rp->alarm - now; 14717ba5db23Sclaudio diff *= 1000; 14727ba5db23Sclaudio if (timeout == INFTIM || diff < timeout) 14737ba5db23Sclaudio timeout = diff; 14747af68c5cSclaudio } 14757af68c5cSclaudio } 14767af68c5cSclaudio } 14777ba5db23Sclaudio return timeout; 14787af68c5cSclaudio } 14797af68c5cSclaudio 14800c3a2335Sclaudio /* 1481a5f50487Sjob * Update repo-specific stats when files are going to be moved 1482a5f50487Sjob * from DIR_TEMP to DIR_VALID. 1483a5f50487Sjob */ 1484a5f50487Sjob void 1485a5f50487Sjob repostats_new_files_inc(struct repo *rp, const char *file) 1486a5f50487Sjob { 1487a5f50487Sjob if (strncmp(file, ".rsync/", strlen(".rsync/")) == 0 || 1488a5f50487Sjob strncmp(file, ".rrdp/", strlen(".rrdp/")) == 0) 1489a5f50487Sjob rp->repostats.new_files++; 1490a5f50487Sjob } 1491a5f50487Sjob 1492a5f50487Sjob /* 14934f5f25cbSclaudio * Update stats object of repository depending on rtype and subtype. 14944f5f25cbSclaudio */ 14954f5f25cbSclaudio void 14961fc2657fSclaudio repo_stat_inc(struct repo *rp, int talid, enum rtype type, enum stype subtype) 14974f5f25cbSclaudio { 14984f5f25cbSclaudio if (rp == NULL) 14994f5f25cbSclaudio return; 15001fc2657fSclaudio rp->stats_used[talid] = 1; 15014f5f25cbSclaudio switch (type) { 15024f5f25cbSclaudio case RTYPE_CER: 15034f5f25cbSclaudio if (subtype == STYPE_OK) 15041fc2657fSclaudio rp->stats[talid].certs++; 15054f5f25cbSclaudio if (subtype == STYPE_FAIL) 15061fc2657fSclaudio rp->stats[talid].certs_fail++; 15074f5f25cbSclaudio if (subtype == STYPE_BGPSEC) { 15081fc2657fSclaudio rp->stats[talid].certs--; 15091fc2657fSclaudio rp->stats[talid].brks++; 15104f5f25cbSclaudio } 15114f5f25cbSclaudio break; 15124f5f25cbSclaudio case RTYPE_MFT: 15134f5f25cbSclaudio if (subtype == STYPE_OK) 15141fc2657fSclaudio rp->stats[talid].mfts++; 15154f5f25cbSclaudio if (subtype == STYPE_FAIL) 15161fc2657fSclaudio rp->stats[talid].mfts_fail++; 1517318f0572Sjob if (subtype == STYPE_SEQNUM_GAP) 1518318f0572Sjob rp->stats[talid].mfts_gap++; 15194f5f25cbSclaudio break; 15204f5f25cbSclaudio case RTYPE_ROA: 15214f5f25cbSclaudio switch (subtype) { 15224f5f25cbSclaudio case STYPE_OK: 15231fc2657fSclaudio rp->stats[talid].roas++; 15244f5f25cbSclaudio break; 15254f5f25cbSclaudio case STYPE_FAIL: 15261fc2657fSclaudio rp->stats[talid].roas_fail++; 15274f5f25cbSclaudio break; 15284f5f25cbSclaudio case STYPE_INVALID: 15291fc2657fSclaudio rp->stats[talid].roas_invalid++; 15304f5f25cbSclaudio break; 15314f5f25cbSclaudio case STYPE_TOTAL: 15321fc2657fSclaudio rp->stats[talid].vrps++; 15334f5f25cbSclaudio break; 15344f5f25cbSclaudio case STYPE_UNIQUE: 15351fc2657fSclaudio rp->stats[talid].vrps_uniqs++; 15364f5f25cbSclaudio break; 15374f5f25cbSclaudio case STYPE_DEC_UNIQUE: 15381fc2657fSclaudio rp->stats[talid].vrps_uniqs--; 15394f5f25cbSclaudio break; 15404f5f25cbSclaudio default: 15414f5f25cbSclaudio break; 15424f5f25cbSclaudio } 15434f5f25cbSclaudio break; 15444f5f25cbSclaudio case RTYPE_ASPA: 15454f5f25cbSclaudio switch (subtype) { 15464f5f25cbSclaudio case STYPE_OK: 15471fc2657fSclaudio rp->stats[talid].aspas++; 15484f5f25cbSclaudio break; 15494f5f25cbSclaudio case STYPE_FAIL: 15501fc2657fSclaudio rp->stats[talid].aspas_fail++; 15514f5f25cbSclaudio break; 15524f5f25cbSclaudio case STYPE_INVALID: 15531fc2657fSclaudio rp->stats[talid].aspas_invalid++; 15544f5f25cbSclaudio break; 15554f5f25cbSclaudio case STYPE_TOTAL: 15561fc2657fSclaudio rp->stats[talid].vaps++; 15574f5f25cbSclaudio break; 15584f5f25cbSclaudio case STYPE_UNIQUE: 15591fc2657fSclaudio rp->stats[talid].vaps_uniqs++; 15601fc2657fSclaudio break; 15611fc2657fSclaudio case STYPE_DEC_UNIQUE: 15621fc2657fSclaudio rp->stats[talid].vaps_uniqs--; 15634f5f25cbSclaudio break; 156474a82ef4Stb case STYPE_PROVIDERS: 15651fc2657fSclaudio rp->stats[talid].vaps_pas++; 15664f5f25cbSclaudio break; 15677e284d50Stb case STYPE_OVERFLOW: 15687e284d50Stb rp->stats[talid].vaps_overflowed++; 15697e284d50Stb break; 15704f5f25cbSclaudio default: 15714f5f25cbSclaudio break; 15724f5f25cbSclaudio } 15734f5f25cbSclaudio break; 1574d4be4cdeSjob case RTYPE_SPL: 1575d4be4cdeSjob switch (subtype) { 1576d4be4cdeSjob case STYPE_OK: 1577d4be4cdeSjob rp->stats[talid].spls++; 1578d4be4cdeSjob break; 1579d4be4cdeSjob case STYPE_FAIL: 1580d4be4cdeSjob rp->stats[talid].spls_fail++; 1581d4be4cdeSjob break; 1582d4be4cdeSjob case STYPE_INVALID: 1583d4be4cdeSjob rp->stats[talid].spls_invalid++; 1584d4be4cdeSjob break; 1585d4be4cdeSjob case STYPE_TOTAL: 1586d4be4cdeSjob rp->stats[talid].vsps++; 1587d4be4cdeSjob break; 1588d4be4cdeSjob case STYPE_UNIQUE: 1589d4be4cdeSjob rp->stats[talid].vsps_uniqs++; 1590d4be4cdeSjob break; 1591d4be4cdeSjob case STYPE_DEC_UNIQUE: 1592d4be4cdeSjob rp->stats[talid].vsps_uniqs--; 1593d4be4cdeSjob break; 1594d4be4cdeSjob default: 1595d4be4cdeSjob break; 1596d4be4cdeSjob } 1597d4be4cdeSjob break; 15984f5f25cbSclaudio case RTYPE_CRL: 15991fc2657fSclaudio rp->stats[talid].crls++; 16004f5f25cbSclaudio break; 16014f5f25cbSclaudio case RTYPE_GBR: 16021fc2657fSclaudio rp->stats[talid].gbrs++; 16034f5f25cbSclaudio break; 16044f5f25cbSclaudio case RTYPE_TAK: 16051fc2657fSclaudio rp->stats[talid].taks++; 16064f5f25cbSclaudio break; 16074f5f25cbSclaudio default: 16084f5f25cbSclaudio break; 16094f5f25cbSclaudio } 16104f5f25cbSclaudio } 16114f5f25cbSclaudio 16124f5f25cbSclaudio void 16131fc2657fSclaudio repo_tal_stats_collect(void (*cb)(const struct repo *, 16141fc2657fSclaudio const struct repotalstats *, void *), int talid, void *arg) 16151fc2657fSclaudio { 16161fc2657fSclaudio struct repo *rp; 16171fc2657fSclaudio 16181fc2657fSclaudio SLIST_FOREACH(rp, &repos, entry) { 16191fc2657fSclaudio if (rp->stats_used[talid]) 16201fc2657fSclaudio cb(rp, &rp->stats[talid], arg); 16211fc2657fSclaudio } 16221fc2657fSclaudio } 16231fc2657fSclaudio 16241fc2657fSclaudio void 16254f5f25cbSclaudio repo_stats_collect(void (*cb)(const struct repo *, const struct repostats *, 16264f5f25cbSclaudio void *), void *arg) 16274f5f25cbSclaudio { 16284f5f25cbSclaudio struct repo *rp; 16294f5f25cbSclaudio 16301fc2657fSclaudio SLIST_FOREACH(rp, &repos, entry) 16311fc2657fSclaudio cb(rp, &rp->repostats, arg); 16324f5f25cbSclaudio } 16334f5f25cbSclaudio 16344f5f25cbSclaudio /* 16350c3a2335Sclaudio * Delayed delete of files from RRDP. Since RRDP has no security built-in 16360c3a2335Sclaudio * this code needs to check if this RRDP repository is actually allowed to 16370c3a2335Sclaudio * remove the file referenced by the URI. 16380c3a2335Sclaudio */ 16396cf9bac2Sclaudio static void 16406cf9bac2Sclaudio repo_cleanup_rrdp(struct filepath_tree *tree) 1641512ef3b5Sclaudio { 16421fc2657fSclaudio struct repo *rp; 16430c3a2335Sclaudio struct rrdprepo *rr; 1644512ef3b5Sclaudio struct filepath *fp, *nfp; 1645512ef3b5Sclaudio char *fn; 1646512ef3b5Sclaudio 16471fc2657fSclaudio SLIST_FOREACH(rp, &repos, entry) { 16481fc2657fSclaudio if (rp->rrdp == NULL) 16491fc2657fSclaudio continue; 16501fc2657fSclaudio rr = (struct rrdprepo *)rp->rrdp; 1651512ef3b5Sclaudio RB_FOREACH_SAFE(fp, filepath_tree, &rr->deleted, nfp) { 16520c3a2335Sclaudio if (!rrdp_uri_valid(rr, fp->file)) { 16530c3a2335Sclaudio warnx("%s: external URI %s", rr->notifyuri, 16540c3a2335Sclaudio fp->file); 16550c3a2335Sclaudio filepath_put(&rr->deleted, fp); 16560c3a2335Sclaudio continue; 16570c3a2335Sclaudio } 16580c3a2335Sclaudio /* try to remove file from rrdp repo ... */ 1659512ef3b5Sclaudio fn = rrdp_filename(rr, fp->file, 0); 16606cf9bac2Sclaudio 16616cf9bac2Sclaudio if (unlink(fn) == -1) { 16626cf9bac2Sclaudio if (errno != ENOENT) 16636cf9bac2Sclaudio warn("unlink %s", fn); 16646cf9bac2Sclaudio } else { 16656cf9bac2Sclaudio if (verbose > 1) 16666cf9bac2Sclaudio logx("deleted %s", fn); 16671fc2657fSclaudio rp->repostats.del_files++; 16686cf9bac2Sclaudio } 16690c3a2335Sclaudio free(fn); 1670512ef3b5Sclaudio 16710c3a2335Sclaudio /* ... and from the valid repository if unused. */ 16720c3a2335Sclaudio fn = rrdp_filename(rr, fp->file, 1); 16736cf9bac2Sclaudio if (!filepath_exists(tree, fn)) { 16746cf9bac2Sclaudio if (unlink(fn) == -1) { 16756cf9bac2Sclaudio if (errno != ENOENT) 16766cf9bac2Sclaudio warn("unlink %s", fn); 16776cf9bac2Sclaudio } else { 16786cf9bac2Sclaudio if (verbose > 1) 16796cf9bac2Sclaudio logx("deleted %s", fn); 16801fc2657fSclaudio rp->repostats.del_files++; 16816cf9bac2Sclaudio } 16826cf9bac2Sclaudio } else 16830c3a2335Sclaudio warnx("%s: referenced file supposed to be " 16840c3a2335Sclaudio "deleted", fn); 1685512ef3b5Sclaudio 1686512ef3b5Sclaudio free(fn); 1687512ef3b5Sclaudio filepath_put(&rr->deleted, fp); 1688512ef3b5Sclaudio } 16890c3a2335Sclaudio } 1690512ef3b5Sclaudio } 1691512ef3b5Sclaudio 16920c3a2335Sclaudio /* 16930c3a2335Sclaudio * All files in tree are valid and should be moved to the valid repository 16940c3a2335Sclaudio * if not already there. Rename the files to the new path and readd the 16950c3a2335Sclaudio * filepath entry with the new path if successful. 16960c3a2335Sclaudio */ 16970c3a2335Sclaudio static void 16980c3a2335Sclaudio repo_move_valid(struct filepath_tree *tree) 16990c3a2335Sclaudio { 170092360713Sclaudio struct filepath *fp, *nfp, *ofp; 17018f17a701Sclaudio size_t rsyncsz = strlen(".rsync/"); 17028f17a701Sclaudio size_t rrdpsz = strlen(".rrdp/"); 17032674db3cSclaudio size_t tasz = strlen(".ta/"); 17040c3a2335Sclaudio char *fn, *base; 17050c3a2335Sclaudio 17060c3a2335Sclaudio RB_FOREACH_SAFE(fp, filepath_tree, tree, nfp) { 17078f17a701Sclaudio if (strncmp(fp->file, ".rsync/", rsyncsz) != 0 && 17082674db3cSclaudio strncmp(fp->file, ".rrdp/", rrdpsz) != 0 && 17092674db3cSclaudio strncmp(fp->file, ".ta/", tasz) != 0) 17100c3a2335Sclaudio continue; /* not a temporary file path */ 17110c3a2335Sclaudio 17128f17a701Sclaudio if (strncmp(fp->file, ".rsync/", rsyncsz) == 0) { 17138f17a701Sclaudio fn = fp->file + rsyncsz; 17142674db3cSclaudio } else if (strncmp(fp->file, ".ta/", tasz) == 0) { 17152674db3cSclaudio fn = fp->file + 1; /* just skip the '.' */ 17160c3a2335Sclaudio } else { 17170c3a2335Sclaudio base = strchr(fp->file + rrdpsz, '/'); 17180c3a2335Sclaudio assert(base != NULL); 17198f17a701Sclaudio fn = base + 1; 172008ac1330Sjob 172108ac1330Sjob /* 172208ac1330Sjob * Adjust file last modification time in order to 172308ac1330Sjob * minimize RSYNC synchronization load after transport 172408ac1330Sjob * failover. 172508ac1330Sjob * While serializing RRDP datastructures to disk, set 172608ac1330Sjob * the last modified timestamp to the CMS signing-time, 172708ac1330Sjob * the X.509 notBefore, or CRL lastUpdate timestamp. 172808ac1330Sjob */ 172908ac1330Sjob if (fp->mtime != 0) { 173008ac1330Sjob int ret; 173108ac1330Sjob struct timespec ts[2]; 173208ac1330Sjob 173308ac1330Sjob ts[0].tv_nsec = UTIME_OMIT; 173408ac1330Sjob ts[1].tv_sec = fp->mtime; 173508ac1330Sjob ts[1].tv_nsec = 0; 173608ac1330Sjob ret = utimensat(AT_FDCWD, fp->file, ts, 0); 173708ac1330Sjob if (ret == -1) { 173808ac1330Sjob warn("utimensat %s", fp->file); 173908ac1330Sjob continue; 174008ac1330Sjob } 174108ac1330Sjob } 17420c3a2335Sclaudio } 17430c3a2335Sclaudio 17446cf9bac2Sclaudio if (repo_mkpath(AT_FDCWD, fn) == -1) 17450c3a2335Sclaudio continue; 17460c3a2335Sclaudio 17470c3a2335Sclaudio /* switch filepath node to new path */ 17480c3a2335Sclaudio RB_REMOVE(filepath_tree, tree, fp); 17498f17a701Sclaudio base = fp->file; 17508f17a701Sclaudio if ((fp->file = strdup(fn)) == NULL) 17518f17a701Sclaudio err(1, NULL); 175292360713Sclaudio 175392360713Sclaudio again: 175492360713Sclaudio if ((ofp = RB_INSERT(filepath_tree, tree, fp)) != NULL) { 175592360713Sclaudio if (ofp->talmask == 0) { 175692360713Sclaudio /* conflicting path is not valid, drop it */ 175792360713Sclaudio filepath_put(tree, ofp); 175892360713Sclaudio goto again; 175992360713Sclaudio } 176092360713Sclaudio if (fp->talmask != 0) { 176192360713Sclaudio warnx("%s: file already present in " 176292360713Sclaudio "validated cache", fp->file); 176392360713Sclaudio } 176492360713Sclaudio free(fp->file); 176592360713Sclaudio free(fp); 17668f17a701Sclaudio free(base); 176792360713Sclaudio continue; 176892360713Sclaudio } 176992360713Sclaudio 177092360713Sclaudio if (rename(base, fp->file) == -1) 177192360713Sclaudio warn("rename to %s", fp->file); 177292360713Sclaudio 177392360713Sclaudio free(base); 17740c3a2335Sclaudio } 17750c3a2335Sclaudio } 17760c3a2335Sclaudio 17771fc2657fSclaudio struct fts_state { 17782674db3cSclaudio enum { BASE_DIR, RSYNC_DIR, TA_DIR, RRDP_DIR } type; 17791fc2657fSclaudio struct repo *rp; 17801fc2657fSclaudio } fts_state; 17816cf9bac2Sclaudio 17826cf9bac2Sclaudio static const struct rrdprepo * 17836cf9bac2Sclaudio repo_is_rrdp(struct repo *rp) 17846cf9bac2Sclaudio { 17856cf9bac2Sclaudio /* check for special pointers first these are not a repository */ 17861fc2657fSclaudio if (rp != NULL && rp->rrdp != NULL) 17876cf9bac2Sclaudio return rp->rrdp->state == REPO_DONE ? rp->rrdp : NULL; 17886cf9bac2Sclaudio return NULL; 17896cf9bac2Sclaudio } 17900c3a2335Sclaudio 17918f17a701Sclaudio static inline char * 17928f17a701Sclaudio skip_dotslash(char *in) 17938f17a701Sclaudio { 17948f17a701Sclaudio if (memcmp(in, "./", 2) == 0) 17958f17a701Sclaudio return in + 2; 17968f17a701Sclaudio return in; 17978f17a701Sclaudio } 17988f17a701Sclaudio 17991fc2657fSclaudio static void 18001fc2657fSclaudio repo_cleanup_entry(FTSENT *e, struct filepath_tree *tree, int cachefd) 18018ecbadc1Sclaudio { 18026cf9bac2Sclaudio const struct rrdprepo *rr; 18031fc2657fSclaudio char *path; 18048ecbadc1Sclaudio 18051fc2657fSclaudio path = skip_dotslash(e->fts_path); 18068ecbadc1Sclaudio switch (e->fts_info) { 18078ecbadc1Sclaudio case FTS_NSOK: 18088f17a701Sclaudio if (filepath_exists(tree, path)) { 18098f17a701Sclaudio e->fts_parent->fts_number++; 18108f17a701Sclaudio break; 18118f17a701Sclaudio } 18121fc2657fSclaudio if (fts_state.type == RRDP_DIR && fts_state.rp != NULL) { 18138f17a701Sclaudio e->fts_parent->fts_number++; 18140c3a2335Sclaudio /* handle rrdp .state files explicitly */ 18158f17a701Sclaudio if (e->fts_level == 3 && 18168f17a701Sclaudio strcmp(e->fts_name, ".state") == 0) 18178f17a701Sclaudio break; 18180c3a2335Sclaudio /* can't delete these extra files */ 18191fc2657fSclaudio fts_state.rp->repostats.extra_files++; 18200c3a2335Sclaudio if (verbose > 1) 18218f17a701Sclaudio logx("superfluous %s", path); 18228f17a701Sclaudio break; 18238f17a701Sclaudio } 18241fc2657fSclaudio rr = repo_is_rrdp(fts_state.rp); 18256cf9bac2Sclaudio if (rr != NULL) { 1826664a7260Sclaudio struct stat st; 18276cf9bac2Sclaudio char *fn; 18286cf9bac2Sclaudio 18291fc2657fSclaudio if (asprintf(&fn, "%s/%s", rr->basedir, path) == -1) 18306cf9bac2Sclaudio err(1, NULL); 18316cf9bac2Sclaudio 1832664a7260Sclaudio /* 1833664a7260Sclaudio * If the file exists in the rrdp dir 1834664a7260Sclaudio * that file is newer and needs to be kept 1835664a7260Sclaudio * so unlink this file instead of moving 1836664a7260Sclaudio * it over the file in the rrdp dir. 1837664a7260Sclaudio */ 1838664a7260Sclaudio if (fstatat(cachefd, fn, &st, 0) == 0 && 1839664a7260Sclaudio S_ISREG(st.st_mode)) { 1840664a7260Sclaudio free(fn); 1841664a7260Sclaudio goto unlink; 1842664a7260Sclaudio } 18436cf9bac2Sclaudio if (repo_mkpath(cachefd, fn) == 0) { 18446cf9bac2Sclaudio if (renameat(AT_FDCWD, e->fts_accpath, 18456cf9bac2Sclaudio cachefd, fn) == -1) 18461fc2657fSclaudio warn("rename %s to %s", path, fn); 18476cf9bac2Sclaudio else if (verbose > 1) 18486cf9bac2Sclaudio logx("moved %s", path); 18491fc2657fSclaudio fts_state.rp->repostats.extra_files++; 18506cf9bac2Sclaudio } 18516cf9bac2Sclaudio free(fn); 18526cf9bac2Sclaudio } else { 1853664a7260Sclaudio unlink: 18546cf9bac2Sclaudio if (unlink(e->fts_accpath) == -1) { 18556cf9bac2Sclaudio warn("unlink %s", path); 18562674db3cSclaudio } else if (fts_state.type == RSYNC_DIR || 18572674db3cSclaudio fts_state.type == TA_DIR) { 18582674db3cSclaudio /* no need to keep rsync or ta files */ 18591fc2657fSclaudio if (verbose > 1) 186071f6c49aSclaudio logx("deleted superfluous %s", path); 18611fc2657fSclaudio if (fts_state.rp != NULL) 18621fc2657fSclaudio fts_state.rp->repostats.del_extra_files++; 18631fc2657fSclaudio else 18641fc2657fSclaudio stats.repo_stats.del_extra_files++; 18656cf9bac2Sclaudio } else { 18666cf9bac2Sclaudio if (verbose > 1) 18676cf9bac2Sclaudio logx("deleted %s", path); 18681fc2657fSclaudio if (fts_state.rp != NULL) 18691fc2657fSclaudio fts_state.rp->repostats.del_files++; 18701fc2657fSclaudio else 18711fc2657fSclaudio stats.repo_stats.del_files++; 18726cf9bac2Sclaudio } 18736cf9bac2Sclaudio } 18748ecbadc1Sclaudio break; 18758ecbadc1Sclaudio case FTS_D: 1876477583a6Sclaudio if (e->fts_level == FTS_ROOTLEVEL) { 18771fc2657fSclaudio fts_state.type = BASE_DIR; 18781fc2657fSclaudio fts_state.rp = NULL; 18791fc2657fSclaudio } 1880477583a6Sclaudio if (e->fts_level == 1) { 18812674db3cSclaudio /* rpki.example.org or .rrdp / .rsync / .ta */ 1882477583a6Sclaudio if (strcmp(".rsync", e->fts_name) == 0) 1883477583a6Sclaudio fts_state.type = RSYNC_DIR; 18842674db3cSclaudio else if (strcmp(".ta", e->fts_name) == 0) 18852674db3cSclaudio fts_state.type = TA_DIR; 1886477583a6Sclaudio else if (strcmp(".rrdp", e->fts_name) == 0) 1887477583a6Sclaudio fts_state.type = RRDP_DIR; 1888477583a6Sclaudio else 1889477583a6Sclaudio fts_state.type = BASE_DIR; 1890477583a6Sclaudio fts_state.rp = NULL; 18911fc2657fSclaudio } 18921fc2657fSclaudio if (e->fts_level == 2) { 189371f6c49aSclaudio /* rpki.example.org/repository or .rrdp/hashdir */ 189471f6c49aSclaudio if (fts_state.type == BASE_DIR) 18951fc2657fSclaudio fts_state.rp = repo_bypath(path); 18962674db3cSclaudio if (fts_state.type == TA_DIR) 18972674db3cSclaudio fts_state.rp = repo_bypath(path + 1); 18980c3a2335Sclaudio /* 18990c3a2335Sclaudio * special handling for rrdp directories, 19000c3a2335Sclaudio * clear them if they are not used anymore but 19010c3a2335Sclaudio * only if rrdp is active. 190271f6c49aSclaudio * Look them up just using the hash. 19030c3a2335Sclaudio */ 19041fc2657fSclaudio if (fts_state.type == RRDP_DIR) 19051fc2657fSclaudio fts_state.rp = repo_rrdp_bypath(path); 19066cf9bac2Sclaudio } 190771f6c49aSclaudio if (e->fts_level == 3 && fts_state.type == RSYNC_DIR) { 190871f6c49aSclaudio /* .rsync/rpki.example.org/repository */ 1909*05a19cc6Sjob fts_state.rp = repo_rsync_bypath(path + 1910*05a19cc6Sjob strlen(".rsync/")); 191171f6c49aSclaudio } 19128ecbadc1Sclaudio break; 19138ecbadc1Sclaudio case FTS_DP: 19140c3a2335Sclaudio if (e->fts_level == FTS_ROOTLEVEL) 19150c3a2335Sclaudio break; 19161fc2657fSclaudio if (e->fts_level == 1) { 19178f17a701Sclaudio /* do not remove .rsync and .rrdp */ 19181fc2657fSclaudio fts_state.rp = NULL; 19191fc2657fSclaudio if (fts_state.type == RRDP_DIR || 19202674db3cSclaudio fts_state.type == RSYNC_DIR || 19212674db3cSclaudio fts_state.type == TA_DIR) 19228f17a701Sclaudio break; 19231fc2657fSclaudio } 19240c3a2335Sclaudio 19250c3a2335Sclaudio e->fts_parent->fts_number += e->fts_number; 19266cf9bac2Sclaudio 19276cf9bac2Sclaudio if (e->fts_number == 0) { 19286cf9bac2Sclaudio if (rmdir(e->fts_accpath) == -1) 19296cf9bac2Sclaudio warn("rmdir %s", path); 19301fc2657fSclaudio if (fts_state.rp != NULL) 19311fc2657fSclaudio fts_state.rp->repostats.del_dirs++; 19326cf9bac2Sclaudio else 19331fc2657fSclaudio stats.repo_stats.del_dirs++; 19346cf9bac2Sclaudio } 19358ecbadc1Sclaudio break; 19368ecbadc1Sclaudio case FTS_SL: 19378ecbadc1Sclaudio case FTS_SLNONE: 19388f17a701Sclaudio warnx("symlink %s", path); 19396cf9bac2Sclaudio if (unlink(e->fts_accpath) == -1) 19406cf9bac2Sclaudio warn("unlink %s", path); 19411fc2657fSclaudio stats.repo_stats.del_extra_files++; 19428ecbadc1Sclaudio break; 19438ecbadc1Sclaudio case FTS_NS: 19448ecbadc1Sclaudio case FTS_ERR: 19451fc2657fSclaudio if (e->fts_errno == ENOENT && e->fts_level == FTS_ROOTLEVEL) 19461fc2657fSclaudio break; 19471fc2657fSclaudio warnx("fts_read %s: %s", path, strerror(e->fts_errno)); 19488ecbadc1Sclaudio break; 19498ecbadc1Sclaudio default: 19501fc2657fSclaudio warnx("fts_read %s: unhandled[%x]", path, e->fts_info); 19518ecbadc1Sclaudio break; 19528ecbadc1Sclaudio } 19531fc2657fSclaudio } 19548ecbadc1Sclaudio 19551fc2657fSclaudio void 19561fc2657fSclaudio repo_cleanup(struct filepath_tree *tree, int cachefd) 19571fc2657fSclaudio { 19581fc2657fSclaudio char *argv[2] = { ".", NULL }; 19591fc2657fSclaudio FTS *fts; 19601fc2657fSclaudio FTSENT *e; 19611fc2657fSclaudio 19621fc2657fSclaudio /* first move temp files which have been used to valid dir */ 19631fc2657fSclaudio repo_move_valid(tree); 19641fc2657fSclaudio /* then delete files requested by rrdp */ 19651fc2657fSclaudio repo_cleanup_rrdp(tree); 19661fc2657fSclaudio 19671fc2657fSclaudio if ((fts = fts_open(argv, FTS_PHYSICAL | FTS_NOSTAT, NULL)) == NULL) 19681fc2657fSclaudio err(1, "fts_open"); 19691fc2657fSclaudio errno = 0; 19701fc2657fSclaudio while ((e = fts_read(fts)) != NULL) { 19711fc2657fSclaudio repo_cleanup_entry(e, tree, cachefd); 19728ecbadc1Sclaudio errno = 0; 19738ecbadc1Sclaudio } 19748ecbadc1Sclaudio if (errno) 19758ecbadc1Sclaudio err(1, "fts_read"); 19768ecbadc1Sclaudio if (fts_close(fts) == -1) 19778ecbadc1Sclaudio err(1, "fts_close"); 19788ecbadc1Sclaudio } 19798ecbadc1Sclaudio 19808ecbadc1Sclaudio void 19818ecbadc1Sclaudio repo_free(void) 19828ecbadc1Sclaudio { 19838ecbadc1Sclaudio struct repo *rp; 19848ecbadc1Sclaudio 19858ecbadc1Sclaudio while ((rp = SLIST_FIRST(&repos)) != NULL) { 19868ecbadc1Sclaudio SLIST_REMOVE_HEAD(&repos, entry); 19878ecbadc1Sclaudio free(rp->repouri); 19880c3a2335Sclaudio free(rp->notifyuri); 19890c3a2335Sclaudio free(rp->basedir); 19908ecbadc1Sclaudio free(rp); 19918ecbadc1Sclaudio } 19928ecbadc1Sclaudio 19938ecbadc1Sclaudio ta_free(); 19948ecbadc1Sclaudio rrdp_free(); 19958ecbadc1Sclaudio rsync_free(); 19968ecbadc1Sclaudio } 1997264f4ef9Sclaudio 19980c3a2335Sclaudio /* 1999b268327aSclaudio * Remove all files and directories under base. 2000b268327aSclaudio * Do not remove base directory itself and the .state file. 20010c3a2335Sclaudio */ 2002264f4ef9Sclaudio static void 2003264f4ef9Sclaudio remove_contents(char *base) 2004264f4ef9Sclaudio { 2005264f4ef9Sclaudio char *argv[2] = { base, NULL }; 2006264f4ef9Sclaudio FTS *fts; 2007264f4ef9Sclaudio FTSENT *e; 2008264f4ef9Sclaudio 2009264f4ef9Sclaudio if ((fts = fts_open(argv, FTS_PHYSICAL | FTS_NOSTAT, NULL)) == NULL) 2010264f4ef9Sclaudio err(1, "fts_open"); 2011264f4ef9Sclaudio errno = 0; 2012264f4ef9Sclaudio while ((e = fts_read(fts)) != NULL) { 2013264f4ef9Sclaudio switch (e->fts_info) { 2014264f4ef9Sclaudio case FTS_NSOK: 2015264f4ef9Sclaudio case FTS_SL: 2016264f4ef9Sclaudio case FTS_SLNONE: 2017b268327aSclaudio if (e->fts_level == 1 && 2018b268327aSclaudio strcmp(e->fts_name, ".state") == 0) 2019b268327aSclaudio break; 2020264f4ef9Sclaudio if (unlink(e->fts_accpath) == -1) 2021264f4ef9Sclaudio warn("unlink %s", e->fts_path); 2022264f4ef9Sclaudio break; 2023264f4ef9Sclaudio case FTS_D: 2024264f4ef9Sclaudio break; 2025264f4ef9Sclaudio case FTS_DP: 2026264f4ef9Sclaudio /* keep root directory */ 2027264f4ef9Sclaudio if (e->fts_level == FTS_ROOTLEVEL) 2028264f4ef9Sclaudio break; 2029264f4ef9Sclaudio if (rmdir(e->fts_accpath) == -1) 2030264f4ef9Sclaudio warn("rmdir %s", e->fts_path); 2031264f4ef9Sclaudio break; 2032264f4ef9Sclaudio case FTS_NS: 2033264f4ef9Sclaudio case FTS_ERR: 2034264f4ef9Sclaudio warnx("fts_read %s: %s", e->fts_path, 2035264f4ef9Sclaudio strerror(e->fts_errno)); 2036264f4ef9Sclaudio break; 2037264f4ef9Sclaudio default: 2038264f4ef9Sclaudio warnx("unhandled[%x] %s", e->fts_info, 2039264f4ef9Sclaudio e->fts_path); 2040264f4ef9Sclaudio break; 2041264f4ef9Sclaudio } 2042264f4ef9Sclaudio errno = 0; 2043264f4ef9Sclaudio } 2044264f4ef9Sclaudio if (errno) 2045264f4ef9Sclaudio err(1, "fts_read"); 2046264f4ef9Sclaudio if (fts_close(fts) == -1) 2047264f4ef9Sclaudio err(1, "fts_close"); 2048264f4ef9Sclaudio } 2049