diff -Nur cron-3.0pl1.orig/cron.c cron-3.0pl1/cron.c --- cron-3.0pl1.orig/cron.c 2022-06-25 20:59:38.000000000 +0100 +++ cron-3.0pl1/cron.c 2022-06-25 21:09:11.707716332 +0100 @@ -147,7 +147,9 @@ database.sys_mtime = (time_t) 0; database.user_mtime = (time_t) 0; database.sysd_mtime = (time_t) 0; - load_database(&database); + /* 1 here = always load database, regardless of + * mtime */ + load_database(&database, 1); set_time(TRUE); run_reboot_jobs(&database); timeRunning = virtualTime = clockTime; @@ -174,7 +176,7 @@ timeRunning = clockTime; check_orphans(&database); - load_database(&database); + load_database(&database, 0); /* * ... calculate how the current time differs from diff -Nur cron-3.0pl1.orig/cron.h cron-3.0pl1/cron.h --- cron-3.0pl1.orig/cron.h 2022-06-25 20:59:38.000000000 +0100 +++ cron-3.0pl1/cron.h 2022-06-25 21:02:39.659707221 +0100 @@ -225,7 +225,7 @@ void set_cron_uid __P((void)), set_cron_cwd __P((void)), - load_database __P((cron_db *)), + load_database __P((cron_db *, int)), open_logfile __P((void)), sigpipe_func __P((void)), job_add __P((entry *, user *)), diff -Nur cron-3.0pl1.orig/database.c cron-3.0pl1/database.c --- cron-3.0pl1.orig/database.c 2022-06-25 20:59:38.000000000 +0100 +++ cron-3.0pl1/database.c 2022-06-25 21:07:11.183713531 +0100 @@ -57,8 +57,9 @@ static void free_orphan(orphan *o); void -load_database(old_db) +load_database(old_db, always_load) cron_db *old_db; + int always_load; { DIR *dir; struct stat statbuf; @@ -105,7 +106,7 @@ * we avoid reading of SYSCRONDIR and don't change its access time. * This is especially important on laptops with APM. */ - if (old_db->sysd_mtime != syscrond_stat.st_mtime) { + if (old_db->sysd_mtime != syscrond_stat.st_mtime || always_load) { syscrond_change = 1; } else { /* Look through the individual files */ @@ -138,13 +139,15 @@ /* if spooldir's mtime has not changed, we don't need to fiddle with * the database. * - * Note that old_db->mtime is initialized to 0 in main(), and - * so is guaranteed to be different than the stat() mtime the first - * time this function is called. + * we used to rely on old_db->mtime being 0 when first run to force + * a database load here, but this breaks if the actual mtime of our + * crontab files / directories is 0, e.g. on an ostree checkout. See + * https://bugs.launchpad.net/ubuntu/+source/cron/+bug/1773229 */ if ((old_db->user_mtime == statbuf.st_mtime) && (old_db->sys_mtime == syscron_stat.st_mtime) && - (!syscrond_change)) { + (!syscrond_change) && + (!always_load)) { Debug(DLOAD, ("[%d] spool dir mtime unch, no load needed.\n", getpid())) return;