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; } } }
PromiseResult VerifyHardLink(EvalContext *ctx, char *destination, const char *source, const Attributes *attr, const Promise *pp) { char to[CF_BUFSIZE], absto[CF_BUFSIZE]; struct stat ssb, dsb; memset(to, 0, CF_BUFSIZE); if ((!IsAbsoluteFileName(source)) && (*source != '.')) /* links without a directory reference */ { snprintf(to, CF_BUFSIZE - 1, ".%c%s", FILE_SEPARATOR, source); } else { strlcpy(to, source, CF_BUFSIZE); } 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', destination '%s'", absto, destination); } else { strcpy(absto, to); } if (stat(absto, &ssb) == -1) { cfPS(ctx, LOG_LEVEL_INFO, PROMISE_RESULT_INTERRUPTED, pp, attr, "Source file '%s' doesn't exist", source); return PROMISE_RESULT_INTERRUPTED; } if (!S_ISREG(ssb.st_mode)) { cfPS(ctx, LOG_LEVEL_ERR, PROMISE_RESULT_FAIL, pp, attr, "Source file '%s' is not a regular file, not appropriate to hard-link", to); return PROMISE_RESULT_FAIL; } Log(LOG_LEVEL_DEBUG, "Trying to hard link '%s' -> '%s'", destination, to); if (stat(destination, &dsb) == -1) { PromiseResult result = PROMISE_RESULT_NOOP; MakeHardLink(ctx, destination, to, attr, pp, &result); return result; } /* both files exist, but are they the same file? POSIX says */ /* the files could be on different devices, but unix doesn't */ /* allow this behaviour so the tests below are theoretical... */ if ((dsb.st_ino != ssb.st_ino) && (dsb.st_dev != ssb.st_dev)) { Log(LOG_LEVEL_VERBOSE, "If this is POSIX, unable to determine if %s is hard link is correct", destination); Log(LOG_LEVEL_VERBOSE, "since it points to a different filesystem!"); if ((dsb.st_mode == ssb.st_mode) && (dsb.st_size == ssb.st_size)) { cfPS(ctx, LOG_LEVEL_VERBOSE, PROMISE_RESULT_NOOP, pp, attr, "Hard link '%s' -> '%s' on different device appears okay", destination, to); return PROMISE_RESULT_NOOP; } } if ((dsb.st_ino == ssb.st_ino) && (dsb.st_dev == ssb.st_dev)) { cfPS(ctx, LOG_LEVEL_VERBOSE, PROMISE_RESULT_NOOP, pp, attr, "Hard link '%s' -> '%s' exists and is okay", destination, to); return PROMISE_RESULT_NOOP; } Log(LOG_LEVEL_INFO, "'%s' does not appear to be a hard link to '%s'", destination, to); if (!EnforcePromise(attr->transaction.action)) { Log(LOG_LEVEL_WARNING, "Hard link '%s' -> '%s' should be created", destination, to); return PROMISE_RESULT_WARN; } PromiseResult result = PROMISE_RESULT_NOOP; if (!MoveObstruction(ctx, destination, attr, pp, &result)) { cfPS(ctx, LOG_LEVEL_ERR, PROMISE_RESULT_FAIL, pp, attr, "Unable to create hard link '%s' -> '%s', unable to move obstruction", destination, to); result = PromiseResultUpdate(result, PROMISE_RESULT_FAIL); return result; } if (MakeHardLink(ctx, destination, to, attr, pp, &result)) { result = PromiseResultUpdate(result, PROMISE_RESULT_CHANGE); } else { cfPS(ctx, LOG_LEVEL_ERR, PROMISE_RESULT_FAIL, pp, attr, "Unable to create hard link '%s' -> '%s'", destination, to); result = PromiseResultUpdate(result, PROMISE_RESULT_FAIL); } return result; }