xref: /openbsd-src/usr.sbin/rpki-client/repo.c (revision 05a19cc6f2a278228e0d433d4826cba1da805e12)
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