PromiseResult VerifyLink(EvalContext *ctx, char *destination, const char *source, Attributes attr, const Promise *pp) { char to[CF_BUFSIZE], linkbuf[CF_BUFSIZE], absto[CF_BUFSIZE]; struct stat sb; memset(to, 0, CF_BUFSIZE); if ((!IsAbsoluteFileName(source)) && (*source != '.')) /* links without a directory reference */ { snprintf(to, CF_BUFSIZE - 1, "./%s", source); } else { strncpy(to, source, CF_BUFSIZE - 1); } if (!IsAbsoluteFileName(to)) /* relative path, must still check if exists */ { Log(LOG_LEVEL_DEBUG, "Relative link destination detected '%s'", to); strcpy(absto, AbsLinkPath(destination, to)); Log(LOG_LEVEL_DEBUG, "Absolute path to relative link '%s', '%s'", absto, destination); } else { strcpy(absto, to); } bool source_file_exists = true; if (stat(absto, &sb) == -1) { Log(LOG_LEVEL_DEBUG, "No source file"); source_file_exists = false; } if ((!source_file_exists) && (attr.link.when_no_file != cfa_force) && (attr.link.when_no_file != cfa_delete)) { Log(LOG_LEVEL_INFO, "Source %s for linking is absent", absto); cfPS(ctx, LOG_LEVEL_VERBOSE, PROMISE_RESULT_FAIL, pp, attr, "Unable to create link %s -> %s, no source", destination, to); return PROMISE_RESULT_WARN; } if ((!source_file_exists) && (attr.link.when_no_file == cfa_delete)) { KillGhostLink(ctx, destination, attr, pp); return PROMISE_RESULT_CHANGE; } memset(linkbuf, 0, CF_BUFSIZE); if (readlink(destination, linkbuf, CF_BUFSIZE - 1) == -1) { if (!MakeParentDirectory2(destination, attr.move_obstructions, EnforcePromise(attr.transaction.action))) { cfPS(ctx, LOG_LEVEL_ERR, PROMISE_RESULT_FAIL, pp, attr, "Unable to create parent directory of link %s -> %s (enforce=%d)", destination, to, EnforcePromise(attr.transaction.action)); return PROMISE_RESULT_FAIL; } else { if (!MoveObstruction(ctx, destination, attr, pp)) { cfPS(ctx, LOG_LEVEL_VERBOSE, PROMISE_RESULT_FAIL, pp, attr, "Unable to create link %s -> %s", destination, to); return PROMISE_RESULT_FAIL; } return MakeLink(ctx, destination, source, attr, pp) ? PROMISE_RESULT_CHANGE : PROMISE_RESULT_FAIL; } } else { int ok = false; if ((attr.link.link_type == FILE_LINK_TYPE_SYMLINK) && (strcmp(linkbuf, to) != 0) && (strcmp(linkbuf, source) != 0)) { ok = true; } else if (strcmp(linkbuf, source) != 0) { ok = true; } if (ok) { if (attr.move_obstructions) { if (!DONTDO) { cfPS(ctx, LOG_LEVEL_INFO, PROMISE_RESULT_CHANGE, pp, attr, "Overriding incorrect link %s\n", destination); if (unlink(destination) == -1) { cfPS(ctx, LOG_LEVEL_VERBOSE, PROMISE_RESULT_FAIL, pp, attr, "Link %s points to %s not %s - error removing link", destination, linkbuf, to); return PROMISE_RESULT_FAIL; } return MakeLink(ctx, destination, source, attr, pp); } else { Log(LOG_LEVEL_ERR, "Must remove incorrect link %s", destination); return PROMISE_RESULT_NOOP; } } else { cfPS(ctx, LOG_LEVEL_VERBOSE, PROMISE_RESULT_FAIL, pp, attr, "Link %s points to %s not %s - not authorized to override", destination, linkbuf, to); return true; } } else { cfPS(ctx, LOG_LEVEL_VERBOSE, PROMISE_RESULT_NOOP, pp, attr, "Link %s points to %s - promise kept", destination, source); return PROMISE_RESULT_NOOP; } } }
static PromiseResult VerifyFileSystem(EvalContext *ctx, char *name, Attributes a, Promise *pp) { struct stat statbuf, localstat; Dir *dirh; const struct dirent *dirp; off_t sizeinbytes = 0; long filecount = 0; char buff[CF_BUFSIZE]; Log(LOG_LEVEL_VERBOSE, "Checking required filesystem %s", name); if (stat(name, &statbuf) == -1) { return PROMISE_RESULT_NOOP; } if (S_ISLNK(statbuf.st_mode)) { KillGhostLink(ctx, name, a, pp); return PROMISE_RESULT_NOOP; } if (S_ISDIR(statbuf.st_mode)) { if ((dirh = DirOpen(name)) == NULL) { Log(LOG_LEVEL_ERR, "Can't open directory '%s' which checking required/disk. (opendir: %s)", name, GetErrorStr()); return PROMISE_RESULT_NOOP; } for (dirp = DirRead(dirh); dirp != NULL; dirp = DirRead(dirh)) { if (!ConsiderLocalFile(dirp->d_name, name)) { continue; } filecount++; strcpy(buff, name); if (buff[strlen(buff)] != FILE_SEPARATOR) { strcat(buff, FILE_SEPARATOR_STR); } strcat(buff, dirp->d_name); if (lstat(buff, &localstat) == -1) { if (S_ISLNK(localstat.st_mode)) { KillGhostLink(ctx, buff, a, pp); continue; } Log(LOG_LEVEL_ERR, "Can't stat volume '%s'. (lstat: %s)", buff, GetErrorStr()); continue; } sizeinbytes += localstat.st_size; } DirClose(dirh); if (sizeinbytes < 0) { Log(LOG_LEVEL_VERBOSE, "Internal error: count of byte size was less than zero!"); return PROMISE_RESULT_NOOP; } if (sizeinbytes < a.volume.sensible_size) { cfPS(ctx, LOG_LEVEL_ERR, PROMISE_RESULT_INTERRUPTED, pp, a, "File system '%s' is suspiciously small! (%jd bytes)", name, (intmax_t) sizeinbytes); return PROMISE_RESULT_INTERRUPTED; } if (filecount < a.volume.sensible_count) { cfPS(ctx, LOG_LEVEL_ERR, PROMISE_RESULT_INTERRUPTED, pp, a, "Filesystem '%s' has only %ld files/directories.", name, filecount); return PROMISE_RESULT_INTERRUPTED; } } cfPS(ctx, LOG_LEVEL_INFO, PROMISE_RESULT_NOOP, pp, a, "Filesystem '%s' content seems to be sensible as promised", name); return PROMISE_RESULT_NOOP; }
int DepthSearch(char *name, struct stat *sb, int rlevel, Attributes attr, Promise *pp) { Dir *dirh; int goback; const struct dirent *dirp; char path[CF_BUFSIZE]; struct stat lsb; if (!attr.havedepthsearch) /* if the search is trivial, make sure that we are in the parent dir of the leaf */ { char basedir[CF_BUFSIZE]; CfDebug(" -> Direct file reference %s, no search implied\n", name); snprintf(basedir, sizeof(basedir), "%s", name); ChopLastNode(basedir); chdir(basedir); return VerifyFileLeaf(name, sb, attr, pp); } if (rlevel > CF_RECURSION_LIMIT) { CfOut(cf_error, "", "WARNING: Very deep nesting of directories (>%d deep): %s (Aborting files)", rlevel, name); return false; } if (rlevel > CF_RECURSION_LIMIT) { CfOut(cf_error, "", "WARNING: Very deep nesting of directories (>%d deep): %s (Aborting files)", rlevel, name); return false; } memset(path, 0, CF_BUFSIZE); CfDebug("To iterate is Human, to recurse is Divine...(%s)\n", name); if (!PushDirState(name, sb)) { return false; } if ((dirh = OpenDirLocal(".")) == NULL) { CfOut(cf_inform, "opendir", "Could not open existing directory %s\n", name); return false; } for (dirp = ReadDir(dirh); dirp != NULL; dirp = ReadDir(dirh)) { if (!ConsiderFile(dirp->d_name, name, attr, pp)) { continue; } strcpy(path, name); AddSlash(path); if (!JoinPath(path, dirp->d_name)) { CloseDir(dirh); return true; } if (lstat(dirp->d_name, &lsb) == -1) { CfOut(cf_verbose, "lstat", "Recurse was looking at %s when an error occurred:\n", path); continue; } if (S_ISLNK(lsb.st_mode)) /* should we ignore links? */ { if (!KillGhostLink(path, attr, pp)) { VerifyFileLeaf(path, &lsb, attr, pp); } else { continue; } } /* See if we are supposed to treat links to dirs as dirs and descend */ if (attr.recursion.travlinks && S_ISLNK(lsb.st_mode)) { if (lsb.st_uid != 0 && lsb.st_uid != getuid()) { CfOut(cf_inform, "", "File %s is an untrusted link: cfengine will not follow it with a destructive operation", path); continue; } /* if so, hide the difference by replacing with actual object */ if (cfstat(dirp->d_name, &lsb) == -1) { CfOut(cf_error, "stat", "Recurse was working on %s when this failed:\n", path); continue; } } if (attr.recursion.xdev && DeviceBoundary(&lsb, pp)) { CfOut(cf_verbose, "", "Skipping %s on different device - use xdev option to change this\n", path); continue; } if (S_ISDIR(lsb.st_mode)) { if (SkipDirLinks(path, dirp->d_name, attr.recursion)) { continue; } if (attr.recursion.depth > 1 && rlevel <= attr.recursion.depth) { CfOut(cf_verbose, "", " ->> Entering %s (%d)\n", path, rlevel); goback = DepthSearch(path, &lsb, rlevel + 1, attr, pp); PopDirState(goback, name, sb, attr.recursion); VerifyFileLeaf(path, &lsb, attr, pp); } else { VerifyFileLeaf(path, &lsb, attr, pp); } } else { VerifyFileLeaf(path, &lsb, attr, pp); } } CloseDir(dirh); return true; }
static int VerifyFileSystem(EvalContext *ctx, char *name, Attributes a, Promise *pp) { struct stat statbuf, localstat; Dir *dirh; const struct dirent *dirp; off_t sizeinbytes = 0; long filecount = 0; char buff[CF_BUFSIZE]; CfOut(OUTPUT_LEVEL_VERBOSE, "", " -> Checking required filesystem %s\n", name); if (cfstat(name, &statbuf) == -1) { return (false); } if (S_ISLNK(statbuf.st_mode)) { KillGhostLink(ctx, name, a, pp); return (true); } if (S_ISDIR(statbuf.st_mode)) { if ((dirh = DirOpen(name)) == NULL) { CfOut(OUTPUT_LEVEL_ERROR, "opendir", "Can't open directory %s which checking required/disk\n", name); return false; } for (dirp = DirRead(dirh); dirp != NULL; dirp = DirRead(dirh)) { if (!ConsiderFile(ctx, dirp->d_name, name, a, pp)) { continue; } filecount++; strcpy(buff, name); if (buff[strlen(buff)] != FILE_SEPARATOR) { strcat(buff, FILE_SEPARATOR_STR); } strcat(buff, dirp->d_name); if (lstat(buff, &localstat) == -1) { if (S_ISLNK(localstat.st_mode)) { KillGhostLink(ctx, buff, a, pp); continue; } CfOut(OUTPUT_LEVEL_ERROR, "lstat", "Can't stat volume %s\n", buff); continue; } sizeinbytes += localstat.st_size; } DirClose(dirh); if (sizeinbytes < 0) { CfOut(OUTPUT_LEVEL_VERBOSE, "", "Internal error: count of byte size was less than zero!\n"); return true; } if (sizeinbytes < a.volume.sensible_size) { cfPS(ctx, OUTPUT_LEVEL_ERROR, PROMISE_RESULT_INTERRUPTED, "", pp, a, " !! File system %s is suspiciously small! (%jd bytes)\n", name, (intmax_t) sizeinbytes); return (false); } if (filecount < a.volume.sensible_count) { cfPS(ctx, OUTPUT_LEVEL_ERROR, PROMISE_RESULT_INTERRUPTED, "", pp, a, " !! Filesystem %s has only %ld files/directories.\n", name, filecount); return (false); } } cfPS(ctx, OUTPUT_LEVEL_INFORM, PROMISE_RESULT_NOOP, "", pp, a, " -> Filesystem %s's content seems to be sensible as promised\n", name); return (true); }