Fix repeated dependency cache rebuild if clock skewed

rc_deptree_update_needed would return early as soon as it found
any file newer than the existing dependency cache.  Unfortunately,
the first file found may not be the newest one there; so the
clock skew workaround in rc-misc:_rc_deptree_load would be given
a timestamp that was still too old.

This fix forces a full scan of all relevant files, so as to
ensure that we return a timestamp that will allow the clock skew
fix to operate.   The runtime cost is no worse than the case where
the cache is up to date (ie. we must check every possible file).

This fixes #161.
This commit is contained in:
Will Miles 2017-08-23 21:53:16 -04:00 committed by William Hubbs
parent f012930775
commit a7c99506d9

View File

@ -542,52 +542,41 @@ rc_deptree_order(const RC_DEPTREE *deptree, const char *runlevel, int options)
} }
librc_hidden_def(rc_deptree_order) librc_hidden_def(rc_deptree_order)
/* Given a time, recurse the target path to find out if there are
any older (or newer) files. If false, sets the time to the
oldest (or newest) found.
*/
static bool static bool
mtime_check(const char *source, const char *target, bool newer, deep_mtime_check(const char *target, bool newer,
time_t *rel, char *file) time_t *rel, char *file)
{ {
struct stat buf; struct stat buf;
time_t mtime;
bool retval = true; bool retval = true;
DIR *dp; DIR *dp;
struct dirent *d; struct dirent *d;
char path[PATH_MAX]; char path[PATH_MAX];
int serrno = errno; int serrno = errno;
/* We have to exist */
if (stat(source, &buf) != 0)
return false;
mtime = buf.st_mtime;
/* If target does not exist, return true to mimic shell test */ /* If target does not exist, return true to mimic shell test */
if (stat(target, &buf) != 0) if (stat(target, &buf) != 0)
return true; return true;
if (newer) { if (newer) {
if (mtime < buf.st_mtime) { if (*rel < buf.st_mtime) {
if (rel == NULL)
return false;
retval = false; retval = false;
}
if (rel != NULL) { if (file)
if (*rel < buf.st_mtime) { strlcpy(file, target, PATH_MAX);
if (file) *rel = buf.st_mtime;
strlcpy(file, target, PATH_MAX);
*rel = buf.st_mtime;
}
} }
} else { } else {
if (mtime > buf.st_mtime) { if (*rel > buf.st_mtime) {
if (rel == NULL)
return false;
retval = false; retval = false;
}
if (rel != NULL) { if (file)
if (*rel > buf.st_mtime) { strlcpy(file, target, PATH_MAX);
if (file) *rel = buf.st_mtime;
strlcpy(file, target, PATH_MAX);
*rel = buf.st_mtime;
}
} }
} }
@ -602,16 +591,38 @@ mtime_check(const char *source, const char *target, bool newer,
if (d->d_name[0] == '.') if (d->d_name[0] == '.')
continue; continue;
snprintf(path, sizeof(path), "%s/%s", target, d->d_name); snprintf(path, sizeof(path), "%s/%s", target, d->d_name);
if (!mtime_check(source, path, newer, rel, file)) { if (!deep_mtime_check(path, newer, rel, file)) {
retval = false; retval = false;
if (rel == NULL)
break;
} }
} }
closedir(dp); closedir(dp);
return retval; return retval;
} }
/* Recursively check if target is older/newer than source.
* If false, return the filename and most different time (if
* the return value arguments are non-null).
*/
static bool
mtime_check(const char *source, const char *target, bool newer,
time_t *rel, char *file)
{
struct stat buf;
time_t mtime;
bool retval = true;
/* We have to exist */
if (stat(source, &buf) != 0)
return false;
mtime = buf.st_mtime;
retval = deep_mtime_check(target,newer,&mtime,file);
if (rel) {
*rel = mtime;
}
return retval;
}
bool bool
rc_newer_than(const char *source, const char *target, rc_newer_than(const char *source, const char *target,
time_t *newest, char *file) time_t *newest, char *file)
@ -670,6 +681,8 @@ rc_deptree_update_needed(time_t *newest, char *file)
RC_STRINGLIST *config; RC_STRINGLIST *config;
RC_STRING *s; RC_STRING *s;
int i; int i;
struct stat buf;
time_t mtime;
/* Create base directories if needed */ /* Create base directories if needed */
for (i = 0; depdirs[i]; i++) for (i = 0; depdirs[i]; i++)
@ -677,42 +690,48 @@ rc_deptree_update_needed(time_t *newest, char *file)
fprintf(stderr, "mkdir `%s': %s\n", depdirs[i], strerror(errno)); fprintf(stderr, "mkdir `%s': %s\n", depdirs[i], strerror(errno));
/* Quick test to see if anything we use has changed and we have /* Quick test to see if anything we use has changed and we have
* data in our deptree */ * data in our deptree. */
if (!existss(RC_DEPTREE_CACHE))
return true; if (stat(RC_DEPTREE_CACHE, &buf) == 0) {
if (!rc_newer_than(RC_DEPTREE_CACHE, RC_INITDIR, newest, file)) mtime = buf.st_mtime;
return true; } else {
if (!rc_newer_than(RC_DEPTREE_CACHE, RC_CONFDIR, newest, file)) /* No previous cache found.
return true; * We still run the scan, in case of clock skew; we still need to return
* the newest time.
*/
newer = true;
mtime = time(NULL);
}
newer |= !deep_mtime_check(RC_INITDIR,true,&mtime,file);
newer |= !deep_mtime_check(RC_CONFDIR,true,&mtime,file);
#ifdef RC_PKG_INITDIR #ifdef RC_PKG_INITDIR
if (!rc_newer_than(RC_DEPTREE_CACHE, RC_PKG_INITDIR, newest, file)) newer |= !deep_mtime_check(RC_PKG_INITDIR,true,&mtime,file);
return true;
#endif #endif
#ifdef RC_PKG_CONFDIR #ifdef RC_PKG_CONFDIR
if (!rc_newer_than(RC_DEPTREE_CACHE, RC_PKG_CONFDIR, newest, file)) newer |= !deep_mtime_check(RC_PKG_CONFDIR,true,&mtime,file);
return true;
#endif #endif
#ifdef RC_LOCAL_INITDIR #ifdef RC_LOCAL_INITDIRs
if (!rc_newer_than(RC_DEPTREE_CACHE, RC_LOCAL_INITDIR, newest, file)) newer |= !deep_mtime_check(RC_LOCAL_INITDIR,true,&mtime,file);
return true;
#endif #endif
#ifdef RC_LOCAL_CONFDIR #ifdef RC_LOCAL_CONFDIR
if (!rc_newer_than(RC_DEPTREE_CACHE, RC_LOCAL_CONFDIR, newest, file)) newer |= !deep_mtime_check(RC_LOCAL_CONFDIR,true,&mtime,file);
return true;
#endif #endif
if (!rc_newer_than(RC_DEPTREE_CACHE, RC_CONF, newest, file)) newer |= !deep_mtime_check(RC_CONF,true,&mtime,file);
return true;
/* Some init scripts dependencies change depending on config files /* Some init scripts dependencies change depending on config files
* outside of baselayout, like syslog-ng, so we check those too. */ * outside of baselayout, like syslog-ng, so we check those too. */
config = rc_config_list(RC_DEPCONFIG); config = rc_config_list(RC_DEPCONFIG);
TAILQ_FOREACH(s, config, entries) { TAILQ_FOREACH(s, config, entries) {
if (!rc_newer_than(RC_DEPTREE_CACHE, s->value, newest, file)) { newer |= !deep_mtime_check(s->value, true, &mtime, file);
newer = true;
break;
}
} }
rc_stringlist_free(config); rc_stringlist_free(config);
/* Return newest file time, if requested */
if ((newer) && (newest != NULL)) {
*newest = mtime;
}
return newer; return newer;
} }
librc_hidden_def(rc_deptree_update_needed) librc_hidden_def(rc_deptree_update_needed)