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