PromiseResult VerifyHardLink(EvalContext *ctx, char *destination, const char *source, 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 { 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', 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\n", source); return PROMISE_RESULT_WARN; } if (!S_ISREG(ssb.st_mode)) { cfPS(ctx, LOG_LEVEL_INFO, PROMISE_RESULT_FAIL, pp, attr, "Source file %s is not a regular file, not appropriate to hard-link\n", to); return PROMISE_RESULT_WARN; } Log(LOG_LEVEL_DEBUG, "Trying to hard link '%s' -> '%s'", destination, to); if (stat(destination, &dsb) == -1) { return MakeHardLink(ctx, destination, to, attr, pp) ? PROMISE_RESULT_CHANGE : PROMISE_RESULT_FAIL; } /* 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\n", 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\n", destination, to); return PROMISE_RESULT_NOOP; } Log(LOG_LEVEL_INFO, "%s does not appear to be a hard link to %s", destination, to); if (!MoveObstruction(ctx, destination, attr, pp)) { return PROMISE_RESULT_FAIL; } return MakeHardLink(ctx, destination, to, attr, pp) ? PROMISE_RESULT_CHANGE : PROMISE_RESULT_FAIL; }
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; } } }
/* should return true if 'to' found */ int LinkFiles(char *from,char *to_tmp, struct Item *inclusions,struct Item *exclusions,struct Item *copy, short nofile,struct Link *ptr) { struct stat buf,savebuf; char to[CF_BUFSIZE], linkbuf[CF_BUFSIZE]; char saved[CF_BUFSIZE],absto[CF_BUFSIZE],*lastnode; struct UidList fakeuid; struct Image ip; char stamp[CF_BUFSIZE]; time_t STAMPNOW; STAMPNOW = time((time_t *)NULL); memset(to,0,CF_BUFSIZE); memset(&ip,0, sizeof(ip)); /* links without a directory reference */ if ((*to_tmp != '/') && (*to_tmp != '.')) { strcpy(to,"./"); } if (strlen(to_tmp)+3 > CF_BUFSIZE) { printf("%s: CF_BUFSIZE boundaries exceeded in LinkFiles(%s->%s)\n", g_vprefix,from,to_tmp); return false; } strcat(to,to_tmp); Debug2("Linkfiles(%s,%s)\n",from,to); for (lastnode = from+strlen(from); *lastnode != '/'; lastnode--) { } lastnode++; if (IgnoredOrExcluded(links,lastnode,inclusions,exclusions)) { Verbose("%s: Skipping non-included pattern %s\n",g_vprefix,from); return true; } if (IsWildItemIn(g_vcopylinks,lastnode) || IsWildItemIn(copy,lastnode)) { fakeuid.uid = CF_SAME_OWNER; fakeuid.next = NULL; ip.plus = CF_SAMEMODE; ip.minus = CF_SAMEMODE; ip.uid = &fakeuid; ip.gid = (struct GidList *) &fakeuid; ip.action = "do"; ip.recurse = 0; ip.type = 't'; ip.defines = ptr->defines; ip.elsedef = ptr->elsedef; ip.backup = true; ip.exclusions = NULL; ip.inclusions = NULL; ip.symlink = NULL; ip.classes = NULL; ip.plus_flags = 0; ip.size = CF_NOSIZE; ip.linktype = 's'; ip.minus_flags = 0; ip.server = strdup("localhost"); Verbose("%s: Link item %s marked for copying instead\n", g_vprefix, from); MakeDirectoriesFor(to,'n'); CheckImage(to,from,&ip); free(ip.server); return true; } /* relative path, must still check if exists */ if (*to != '/') { Debug("Relative link destination detected: %s\n",to); strcpy(absto,AbsLinkPath(from,to)); Debug("Absolute path to relative link = %s, from %s\n",absto,from); } else { strcpy(absto,to); } if (!nofile) { if (stat(absto,&buf) == -1) { /* no error warning, since the higher level routine uses this */ return(false); } } Debug2("Trying to link %s -> %s (%s)\n",from,to,absto); if (lstat(from,&buf) == 0) { if (! S_ISLNK(buf.st_mode) && ! g_enforcelinks) { snprintf(g_output,CF_BUFSIZE*2,"Error linking %s -> %s\n",from,to); CfLog(cfsilent,g_output,""); snprintf(g_output, CF_BUFSIZE*2, "Cannot make link: %s exists and is not a link! " "(uid %d)\n", from, buf.st_uid); CfLog(cfsilent,g_output,""); return(true); } if (S_ISREG(buf.st_mode) && g_enforcelinks) { snprintf(g_output, CF_BUFSIZE*2, "Moving %s to %s%s\n", from, from, CF_SAVED); CfLog(cfsilent,g_output,""); if (g_dontdo) { return true; } saved[0] = '\0'; strcpy(saved,from); sprintf(stamp, "_%d_%s", g_cfstarttime, CanonifyName(ctime(&STAMPNOW))); strcat(saved,stamp); strcat(saved,CF_SAVED); if (rename(from,saved) == -1) { snprintf(g_output, CF_BUFSIZE*2, "Can't rename %s to %s\n", from,saved); CfLog(cferror,g_output,"rename"); return(true); } if (Repository(saved,g_vrepository)) { unlink(saved); } } if (S_ISDIR(buf.st_mode) && g_enforcelinks) { snprintf(g_output,CF_BUFSIZE*2,"Moving directory %s to %s%s.dir\n", from,from,CF_SAVED); CfLog(cfsilent,g_output,""); if (g_dontdo) { return true; } saved[0] = '\0'; strcpy(saved,from); sprintf(stamp, "_%d_%s", g_cfstarttime, CanonifyName(ctime(&STAMPNOW))); strcat(saved,stamp); strcat(saved,CF_SAVED); strcat(saved,".dir"); if (stat(saved,&savebuf) != -1) { snprintf(g_output,CF_BUFSIZE*2, "Couldn't save directory %s, " "since %s exists already\n", from,saved); CfLog(cferror,g_output,""); snprintf(g_output,CF_BUFSIZE*2, "Unable to force link to " "existing directory %s\n",from); CfLog(cferror,g_output,""); return true; } if (rename(from,saved) == -1) { snprintf(g_output, CF_BUFSIZE*2, "Can't rename %s to %s\n", from,saved); CfLog(cferror,g_output,"rename"); return(true); } } } memset(linkbuf,0,CF_BUFSIZE); if (readlink(from,linkbuf,CF_BUFSIZE-1) == -1) { /* link doesn't exist */ if (! MakeDirectoriesFor(from,'n')) { snprintf(g_output,CF_BUFSIZE*2, "Couldn't build directory tree up to %s!\n",from); CfLog(cfsilent,g_output,""); snprintf(g_output,CF_BUFSIZE*2, "One element was a plain file, not a directory!\n"); CfLog(cfsilent,g_output,""); return(true); } } else { int off1 = 0, off2 = 0; DeleteSlash(linkbuf); /* Ignore ./ at beginning */ if (strncmp(linkbuf,"./",2) == 0) { off1 = 2; } if (strncmp(to,"./",2) == 0) { off2 = 2; } if (strcmp(linkbuf+off1,to+off2) != 0) { if (g_enforcelinks) { snprintf(g_output,CF_BUFSIZE*2,"Removing link %s\n",from); CfLog(cfinform,g_output,""); if (!g_dontdo) { if (unlink(from) == -1) { perror("unlink"); return true; } return DoLink(from,to,ptr->defines); } } else { snprintf(g_output,CF_BUFSIZE*2, "Old link %s points somewhere else. Doing nothing!\n", from); CfLog(cfsilent,g_output,""); snprintf(g_output, CF_BUFSIZE*2, "(Link points to %s not %s)\n\n", linkbuf,to); CfLog(cfsilent,g_output,""); return(true); } } else { snprintf(g_output, CF_BUFSIZE*2, "Link (%s->%s) exists.\n", from, to_tmp); CfLog(cfverbose,g_output,""); if (!nofile) { /* Check whether link points somewhere */ KillOldLink(from,ptr->defines); return true; } AddMultipleClasses(ptr->elsedef); return(true); } } return DoLink(from,to,ptr->defines); }