Dear all, Claudio made some suggestions to pass the desired modification times around in a different way, below is an updated patch proposal. I also added some instrumentation to also adjust GBRs and TAKs.
RIPE & APNIC informally indicated some interest in this hack. Kind regards, Job Index: extern.h =================================================================== RCS file: /cvs/src/usr.sbin/rpki-client/extern.h,v retrieving revision 1.181 diff -u -p -r1.181 extern.h --- extern.h 9 May 2023 10:34:32 -0000 1.181 +++ extern.h 24 May 2023 16:11:15 -0000 @@ -348,6 +348,7 @@ struct gbr { time_t notbefore; /* EE cert's Not Before */ time_t notafter; /* Not After of the GBR EE */ time_t expires; /* when the signature path expires */ + int talid; /* TAL the GBR is chained up to */ }; struct aspa_provider { @@ -755,7 +756,7 @@ void proc_http(char *, int) __attribut void proc_rrdp(int) __attribute__((noreturn)); /* Repository handling */ -int filepath_add(struct filepath_tree *, char *); +int filepath_add(struct filepath_tree *, char *, time_t); void rrdp_clear(unsigned int); void rrdp_save_state(unsigned int, struct rrdp_session *); int rrdp_handle_file(unsigned int, enum publish_type, char *, Index: filemode.c =================================================================== RCS file: /cvs/src/usr.sbin/rpki-client/filemode.c,v retrieving revision 1.31 diff -u -p -r1.31 filemode.c --- filemode.c 3 May 2023 10:22:30 -0000 1.31 +++ filemode.c 24 May 2023 16:11:15 -0000 @@ -586,6 +586,7 @@ parse_file(struct entityq *q, struct msg struct entity *entp; struct ibuf *b; struct tal *tal; + time_t dummy = 0; while ((entp = TAILQ_FIRST(q)) != NULL) { TAILQ_REMOVE(q, entp, entries); @@ -612,6 +613,7 @@ parse_file(struct entityq *q, struct msg io_simple_buffer(b, &entp->repoid, sizeof(entp->repoid)); io_simple_buffer(b, &entp->talid, sizeof(entp->talid)); io_str_buffer(b, entp->file); + io_simple_buffer(b, &dummy, sizeof(dummy)); io_close_buffer(msgq, b); entity_free(entp); } Index: main.c =================================================================== RCS file: /cvs/src/usr.sbin/rpki-client/main.c,v retrieving revision 1.236 diff -u -p -r1.236 main.c --- main.c 27 Apr 2023 08:37:53 -0000 1.236 +++ main.c 24 May 2023 16:11:15 -0000 @@ -551,6 +551,7 @@ entity_process(struct ibuf *b, struct st struct aspa *aspa; struct repo *rp; char *file; + time_t mtime; unsigned int id; int talid; int c; @@ -565,12 +566,13 @@ entity_process(struct ibuf *b, struct st io_read_buf(b, &id, sizeof(id)); io_read_buf(b, &talid, sizeof(talid)); io_read_str(b, &file); + io_read_buf(b, &mtime, sizeof(mtime)); /* in filemode messages can be ignored, only the accounting matters */ if (filemode) goto done; - if (filepath_add(&fpt, file) == 0) { + if (filepath_add(&fpt, file, mtime) == 0) { warnx("%s: File already visited", file); goto done; } Index: parser.c =================================================================== RCS file: /cvs/src/usr.sbin/rpki-client/parser.c,v retrieving revision 1.94 diff -u -p -r1.94 parser.c --- parser.c 11 May 2023 20:13:30 -0000 1.94 +++ parser.c 24 May 2023 16:11:15 -0000 @@ -485,7 +485,7 @@ proc_parser_root_cert(char *file, const /* * Parse a ghostbuster record */ -static void +static struct gbr * proc_parser_gbr(char *file, const unsigned char *der, size_t len, const char *mftaki) { @@ -496,17 +496,23 @@ proc_parser_gbr(char *file, const unsign const char *errstr; if ((gbr = gbr_parse(&x509, file, der, len)) == NULL) - return; + return NULL; a = valid_ski_aki(file, &auths, gbr->ski, gbr->aki, mftaki); crl = crl_get(&crlt, a); /* return value can be ignored since nothing happens here */ - if (!valid_x509(file, ctx, x509, a, crl, &errstr)) + if (!valid_x509(file, ctx, x509, a, crl, &errstr)) { warnx("%s: %s", file, errstr); - + X509_free(x509); + gbr_free(gbr); + return NULL; + } X509_free(x509); - gbr_free(gbr); + + gbr->talid = a->cert->talid; + + return gbr; } /* @@ -615,8 +621,11 @@ parse_entity(struct entityq *q, struct m struct mft *mft; struct roa *roa; struct aspa *aspa; + struct gbr *gbr; + struct tak *tak; struct ibuf *b; unsigned char *f; + time_t mtime = 0; size_t flen; char *file, *crlfile; int c; @@ -642,6 +651,7 @@ parse_entity(struct entityq *q, struct m switch (entp->type) { case RTYPE_TAL: io_str_buffer(b, entp->file); + io_simple_buffer(b, &mtime, sizeof(mtime)); if ((tal = tal_parse(entp->file, entp->data, entp->datasz)) == NULL) errx(1, "%s: could not parse tal file", @@ -660,6 +670,9 @@ parse_entity(struct entityq *q, struct m else cert = proc_parser_cert(file, f, flen, entp->mftaki); + if (cert != NULL) + mtime = cert->notbefore; + io_simple_buffer(b, &mtime, sizeof(mtime)); c = (cert != NULL); io_simple_buffer(b, &c, sizeof(int)); if (cert != NULL) { @@ -675,6 +688,9 @@ parse_entity(struct entityq *q, struct m case RTYPE_MFT: file = proc_parser_mft(entp, &mft, &crlfile); io_str_buffer(b, file); + if (mft != NULL) + mtime = mft->signtime; + io_simple_buffer(b, &mtime, sizeof(mtime)); c = (mft != NULL); io_simple_buffer(b, &c, sizeof(int)); if (mft != NULL) @@ -693,6 +709,11 @@ parse_entity(struct entityq *q, struct m io_simple_buffer(b2, &entp->talid, sizeof(entp->talid)); io_str_buffer(b2, crlfile); + /* + * FIXME: MFT CMS signing-time is used as mtime + * instead of CRL lastUpdate. + */ + io_simple_buffer(b2, &mtime, sizeof(mtime)); free(crlfile); io_close_buffer(msgq, b2); @@ -703,6 +724,9 @@ parse_entity(struct entityq *q, struct m file = parse_load_file(entp, &f, &flen); io_str_buffer(b, file); roa = proc_parser_roa(file, f, flen, entp->mftaki); + if (roa != NULL) + mtime = roa->signtime; + io_simple_buffer(b, &mtime, sizeof(mtime)); c = (roa != NULL); io_simple_buffer(b, &c, sizeof(int)); if (roa != NULL) @@ -712,12 +736,19 @@ parse_entity(struct entityq *q, struct m case RTYPE_GBR: file = parse_load_file(entp, &f, &flen); io_str_buffer(b, file); - proc_parser_gbr(file, f, flen, entp->mftaki); + gbr = proc_parser_gbr(file, f, flen, entp->mftaki); + if (gbr != NULL) + mtime = gbr->signtime; + io_simple_buffer(b, &mtime, sizeof(mtime)); + gbr_free(gbr); break; case RTYPE_ASPA: file = parse_load_file(entp, &f, &flen); io_str_buffer(b, file); aspa = proc_parser_aspa(file, f, flen, entp->mftaki); + if (aspa != NULL) + mtime = aspa->signtime; + io_simple_buffer(b, &mtime, sizeof(mtime)); c = (aspa != NULL); io_simple_buffer(b, &c, sizeof(int)); if (aspa != NULL) @@ -727,13 +758,18 @@ parse_entity(struct entityq *q, struct m case RTYPE_TAK: file = parse_load_file(entp, &f, &flen); io_str_buffer(b, file); - proc_parser_tak(file, f, flen, entp->mftaki); + tak = proc_parser_tak(file, f, flen, entp->mftaki); + if (tak != NULL) + mtime = tak->signtime; + io_simple_buffer(b, &mtime, sizeof(mtime)); + tak_free(tak); break; case RTYPE_CRL: default: file = parse_filepath(entp->repoid, entp->path, entp->file, entp->location); io_str_buffer(b, file); + io_simple_buffer(b, &mtime, sizeof(mtime)); warnx("%s: unhandled type %d", file, entp->type); break; } Index: repo.c =================================================================== RCS file: /cvs/src/usr.sbin/rpki-client/repo.c,v retrieving revision 1.45 diff -u -p -r1.45 repo.c --- repo.c 16 May 2023 17:01:31 -0000 1.45 +++ repo.c 24 May 2023 16:11:15 -0000 @@ -119,6 +119,7 @@ static void remove_contents(char *); struct filepath { RB_ENTRY(filepath) entry; char *file; + time_t mtime; }; static inline int @@ -133,12 +134,13 @@ RB_PROTOTYPE(filepath_tree, filepath, en * Functions to lookup which files have been accessed during computation. */ int -filepath_add(struct filepath_tree *tree, char *file) +filepath_add(struct filepath_tree *tree, char *file, time_t mtime) { struct filepath *fp; if ((fp = malloc(sizeof(*fp))) == NULL) err(1, NULL); + fp->mtime = mtime; if ((fp->file = strdup(file)) == NULL) err(1, NULL); @@ -838,7 +840,7 @@ rrdp_handle_file(unsigned int id, enum p /* write new content or mark uri as deleted. */ if (pt == PUB_DEL) { - filepath_add(&rr->deleted, uri); + filepath_add(&rr->deleted, uri, 0); } else { fp = filepath_find(&rr->deleted, uri); if (fp != NULL) @@ -1540,6 +1542,26 @@ repo_move_valid(struct filepath_tree *tr if (repo_mkpath(AT_FDCWD, fn) == -1) continue; + + /* + * Adjust file last modification time in order to minimize + * RSYNC synchronization load after transport failover. + * While serializing RRDP datastructures to disk, set the last + * modified timestamp to the CMS signing-time or the X.509 + * notBefore timestamp. + */ + if (fp->mtime != 0 && + strncmp(fp->file, ".rrdp/", rrdpsz) == 0) { + struct timespec ts[2]; + + ts[0].tv_nsec = UTIME_OMIT; + ts[1].tv_sec = fp->mtime; + ts[1].tv_nsec = 0; + if (utimensat(AT_FDCWD, fp->file, ts, 0) == -1) { + warn("utimensat %s", fp->file); + continue; + } + } if (rename(fp->file, fn) == -1) { warn("rename %s", fp->file);