int CfSecOpenDirectory(ServerConnectionState *conn, char *sendbuffer, char *dirname) { Dir *dirh; const struct dirent *dirp; int offset, cipherlen; char out[CF_BUFSIZE]; if (!IsAbsoluteFileName(dirname)) { sprintf(sendbuffer, "BAD: request to access a non-absolute filename\n"); cipherlen = EncryptString(conn->encryption_type, sendbuffer, out, conn->session_key, strlen(sendbuffer) + 1); SendTransaction(&conn->conn_info, out, cipherlen, CF_DONE); return -1; } if ((dirh = DirOpen(dirname)) == NULL) { Log(LOG_LEVEL_VERBOSE, "Couldn't open dir %s", dirname); snprintf(sendbuffer, CF_BUFSIZE, "BAD: cfengine, couldn't open dir %s\n", dirname); cipherlen = EncryptString(conn->encryption_type, sendbuffer, out, conn->session_key, strlen(sendbuffer) + 1); SendTransaction(&conn->conn_info, out, cipherlen, CF_DONE); return -1; } /* Pack names for transmission */ memset(sendbuffer, 0, CF_BUFSIZE); offset = 0; for (dirp = DirRead(dirh); dirp != NULL; dirp = DirRead(dirh)) { if (strlen(dirp->d_name) + 1 + offset >= CF_BUFSIZE - CF_MAXLINKSIZE) { cipherlen = EncryptString(conn->encryption_type, sendbuffer, out, conn->session_key, offset + 1); SendTransaction(&conn->conn_info, out, cipherlen, CF_MORE); offset = 0; memset(sendbuffer, 0, CF_BUFSIZE); memset(out, 0, CF_BUFSIZE); } strncpy(sendbuffer + offset, dirp->d_name, CF_MAXLINKSIZE); /* + zero byte separator */ offset += strlen(dirp->d_name) + 1; } strcpy(sendbuffer + offset, CFD_TERMINATOR); cipherlen = EncryptString(conn->encryption_type, sendbuffer, out, conn->session_key, offset + 2 + strlen(CFD_TERMINATOR)); SendTransaction(&conn->conn_info, out, cipherlen, CF_DONE); DirClose(dirh); return 0; }
static char *AbsLinkPath(const char *from, const char *relto) /* Take an abolute source and a relative destination object and find the absolute name of the to object */ { int pop = 1; static char destination[CF_BUFSIZE]; if (IsAbsoluteFileName(relto)) { ProgrammingError("Call to AbsLInkPath with absolute pathname"); } strcpy(destination, from); /* reuse to save stack space */ const char *sp = NULL; for (sp = relto; *sp != '\0'; sp++) { if (strncmp(sp, "../", 3) == 0) { pop++; sp += 2; continue; } if (strncmp(sp, "./", 2) == 0) { sp += 1; continue; } break; /* real link */ } while (pop > 0) { ChopLastNode(destination); pop--; } if (strlen(destination) == 0) { strcpy(destination, "/"); } else { AddSlash(destination); } strcat(destination, sp); Log(LOG_LEVEL_DEBUG, "Reconstructed absolute linkname '%s'", destination); return destination; }
bool KillGhostLink(EvalContext *ctx, const char *name, Attributes attr, const Promise *pp, PromiseResult *result) { char linkbuf[CF_BUFSIZE], tmp[CF_BUFSIZE]; char linkpath[CF_BUFSIZE], *sp; struct stat statbuf; memset(linkbuf, 0, CF_BUFSIZE); memset(linkpath, 0, CF_BUFSIZE); if (readlink(name, linkbuf, CF_BUFSIZE - 1) == -1) { Log(LOG_LEVEL_VERBOSE, "Can't read link '%s' while checking for deadlinks", name); return true; /* ignore */ } if (!IsAbsoluteFileName(linkbuf)) { strcpy(linkpath, name); /* Get path to link */ for (sp = linkpath + strlen(linkpath); (*sp != FILE_SEPARATOR) && (sp >= linkpath); sp--) { *sp = '\0'; } } strcat(linkpath, linkbuf); CompressPath(tmp, sizeof(tmp), linkpath); if (stat(tmp, &statbuf) == -1) /* link points nowhere */ { if ((attr.link.when_no_file == cfa_delete) || (attr.recursion.rmdeadlinks)) { Log(LOG_LEVEL_VERBOSE, "'%s' is a link which points to '%s', but that file doesn't seem to exist", name, linkbuf); if (!DONTDO) { unlink(name); /* May not work on a client-mounted system ! */ cfPS(ctx, LOG_LEVEL_INFO, PROMISE_RESULT_CHANGE, pp, attr, "Removing ghost '%s', reference to something that is not there", name); *result = PromiseResultUpdate(*result, PROMISE_RESULT_CHANGE); return true; } } } return false; }
EditContext *NewEditContext(EvalContext *ctx, char *filename, Attributes a, const Promise *pp) { EditContext *ec; if (!IsAbsoluteFileName(filename)) { CfOut(OUTPUT_LEVEL_ERROR, "", "Relative file name %s was marked for editing but has no invariant meaning\n", filename); return NULL; } ec = xcalloc(1, sizeof(EditContext)); ec->filename = filename; ec->empty_first = a.edits.empty_before_use; if (a.haveeditline) { if (!LoadFileAsItemList(ctx, &(ec->file_start), filename, a, pp)) { free(ec); return NULL; } } if (a.haveeditxml) { #ifdef HAVE_LIBXML2 if (!LoadFileAsXmlDoc(ctx, &(ec->xmldoc), filename, a, pp)) { free(ec); return NULL; } #else cfPS(ctx, OUTPUT_LEVEL_ERROR, CF_FAIL, "", pp, a, " !! Cannot edit XML files without LIBXML2\n"); free(ec); return NULL; #endif } if (a.edits.empty_before_use) { CfOut(OUTPUT_LEVEL_VERBOSE, "", " -> Build file model from a blank slate (emptying)\n"); DeleteItemList(ec->file_start); ec->file_start = NULL; } EDIT_MODEL = true; return ec; }
FilePathType FilePathGetType(const char *file_path) { if (IsAbsoluteFileName(file_path)) { return FILE_PATH_TYPE_ABSOLUTE; } else if (*file_path == '.') { return FILE_PATH_TYPE_RELATIVE; } else { return FILE_PATH_TYPE_NON_ANCHORED; } }
FilePathType FilePathGetType(const char *file_path) { if (IsAbsoluteFileName(file_path)) { return FILE_PATH_TYPE_ABSOLUTE; } else if (IsFileOutsideDefaultRepository(file_path)) { return FILE_PATH_TYPE_RELATIVE; } else { return FILE_PATH_TYPE_NON_ANCHORED; } }
int CfOpenDirectory(ServerConnectionState *conn, char *sendbuffer, char *oldDirname) { Dir *dirh; const struct dirent *dirp; int offset; char dirname[CF_BUFSIZE]; TranslatePath(dirname, oldDirname); if (!IsAbsoluteFileName(dirname)) { sprintf(sendbuffer, "BAD: request to access a non-absolute filename\n"); SendTransaction(&conn->conn_info, sendbuffer, 0, CF_DONE); return -1; } if ((dirh = DirOpen(dirname)) == NULL) { Log(LOG_LEVEL_DEBUG, "Couldn't open dir '%s'", dirname); snprintf(sendbuffer, CF_BUFSIZE, "BAD: cfengine, couldn't open dir %s\n", dirname); SendTransaction(&conn->conn_info, sendbuffer, 0, CF_DONE); return -1; } /* Pack names for transmission */ memset(sendbuffer, 0, CF_BUFSIZE); offset = 0; for (dirp = DirRead(dirh); dirp != NULL; dirp = DirRead(dirh)) { if (strlen(dirp->d_name) + 1 + offset >= CF_BUFSIZE - CF_MAXLINKSIZE) { SendTransaction(&conn->conn_info, sendbuffer, offset + 1, CF_MORE); offset = 0; memset(sendbuffer, 0, CF_BUFSIZE); } strncpy(sendbuffer + offset, dirp->d_name, CF_MAXLINKSIZE); offset += strlen(dirp->d_name) + 1; /* + zero byte separator */ } strcpy(sendbuffer + offset, CFD_TERMINATOR); SendTransaction(&conn->conn_info, sendbuffer, offset + 2 + strlen(CFD_TERMINATOR), CF_DONE); DirClose(dirh); return 0; }
EditContext *NewEditContext(char *filename, Attributes a) { EditContext *ec; if (!IsAbsoluteFileName(filename)) { Log(LOG_LEVEL_ERR, "Relative file name '%s' was marked for editing but has no invariant meaning", filename); return NULL; } ec = xcalloc(1, sizeof(EditContext)); ec->filename = filename; if (a.haveeditline) { if (!LoadFileAsItemList(&(ec->file_start), filename, a.edits)) { free(ec); return NULL; } } if (a.haveeditxml) { #ifdef HAVE_LIBXML2 if (!LoadFileAsXmlDoc(&(ec->xmldoc), filename, a.edits)) { free(ec); return NULL; } #else Log(LOG_LEVEL_ERR, "Cannot edit XML files without LIBXML2"); free(ec); return NULL; #endif } if (a.edits.empty_before_use) { Log(LOG_LEVEL_VERBOSE, "Build file model from a blank slate (emptying)"); DeleteItemList(ec->file_start); ec->file_start = NULL; } return ec; }
bool ChopLastNode(char *str) /* Chop off trailing node name (possible blank) starting from last character and removing up to the first / encountered e.g. /a/b/c -> /a/b /a/b/ -> /a/b Will also collapse redundant/repeating path separators. */ { char *sp; int ret; DeleteRedundantSlashes(str); /* Here cast is necessary and harmless, str is modifiable */ if ((sp = (char *) LastFileSeparator(str)) == NULL) { int pos = RootDirLength(str); if (str[pos] == '\0') { ret = false; } else { str[pos] = '.'; str[pos + 1] = '\0'; ret = true; } } else { // Don't chop the root slash in an absolute path. if (IsAbsoluteFileName(str) && FirstFileSeparator(str) == sp) { *(++sp) = '\0'; } else { *sp = '\0'; } ret = true; } return ret; }
int ExpandLinks(char *dest, const char *from, int level) { char buff[CF_BUFSIZE]; char node[CF_MAXLINKSIZE]; struct stat statbuf; int lastnode = false; memset(dest, 0, CF_BUFSIZE); if (level >= CF_MAXLINKLEVEL) { Log(LOG_LEVEL_ERR, "Too many levels of symbolic links to evaluate absolute path"); return false; } const char *sp = from; while (*sp != '\0') { if (*sp == FILE_SEPARATOR) { sp++; continue; } sscanf(sp, "%[^/]", node); sp += strlen(node); if (*sp == '\0') { lastnode = true; } if (strcmp(node, ".") == 0) { continue; } if (strcmp(node, "..") == 0) { continue; } else { strcat(dest, "/"); } strcat(dest, node); if (lstat(dest, &statbuf) == -1) /* File doesn't exist so we can stop here */ { Log(LOG_LEVEL_ERR, "Can't stat '%s' in ExpandLinks. (lstat: %s)", dest, GetErrorStr()); return false; } if (S_ISLNK(statbuf.st_mode)) { memset(buff, 0, CF_BUFSIZE); if (readlink(dest, buff, CF_BUFSIZE - 1) == -1) { Log(LOG_LEVEL_ERR, "Expand links can't stat '%s'. (readlink: %s)", dest, GetErrorStr()); return false; } else { if (buff[0] == '.') { ChopLastNode(dest); AddSlash(dest); if (!JoinPath(dest, buff)) { return false; } } else if (IsAbsoluteFileName(buff)) { strcpy(dest, buff); DeleteSlash(dest); if (strcmp(dest, from) == 0) { Log(LOG_LEVEL_DEBUG, "No links to be expanded"); return true; } if ((!lastnode) && (!ExpandLinks(buff, dest, level + 1))) { return false; } } else { ChopLastNode(dest); AddSlash(dest); strcat(dest, buff); DeleteSlash(dest); if (strcmp(dest, from) == 0) { Log(LOG_LEVEL_DEBUG, "No links to be expanded"); return true; } memset(buff, 0, CF_BUFSIZE); if ((!lastnode) && (!ExpandLinks(buff, dest, level + 1))) { return false; } } } } } return true; }
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 VerifyRelativeLink(EvalContext *ctx, char *destination, const char *source, Attributes attr, const Promise *pp) { char *sp, *commonto, *commonfrom; char buff[CF_BUFSIZE], linkto[CF_BUFSIZE], add[CF_BUFSIZE]; int levels = 0; if (*source == '.') { return VerifyLink(ctx, destination, source, attr, pp); } if (!CompressPath(linkto, source)) { cfPS(ctx, LOG_LEVEL_ERR, PROMISE_RESULT_INTERRUPTED, pp, attr, "Failed to link %s to %s\n", destination, source); return PROMISE_RESULT_FAIL; } commonto = linkto; commonfrom = destination; if (strcmp(commonto, commonfrom) == 0) { cfPS(ctx, LOG_LEVEL_ERR, PROMISE_RESULT_INTERRUPTED, pp, attr, "Failed to link %s to %s - can't link file %s to itself\n", destination, source, commonto); return PROMISE_RESULT_FAIL; } while (*commonto == *commonfrom) { commonto++; commonfrom++; } while (!((IsAbsoluteFileName(commonto)) && (IsAbsoluteFileName(commonfrom)))) { commonto--; commonfrom--; } commonto++; for (sp = commonfrom; *sp != '\0'; sp++) { if (IsFileSep(*sp)) { levels++; } } memset(buff, 0, CF_BUFSIZE); strcat(buff, "."); strcat(buff, FILE_SEPARATOR_STR); while (--levels > 0) { snprintf(add, CF_BUFSIZE - 1, "..%c", FILE_SEPARATOR); if (!JoinPath(buff, add)) { return PROMISE_RESULT_FAIL; } } if (!JoinPath(buff, commonto)) { return PROMISE_RESULT_FAIL; } return VerifyLink(ctx, destination, buff, attr, pp); }
int LoadMountInfo(Rlist **list) /* This is, in fact, the most portable way to read the mount info! */ /* Depressing, isn't it? */ { FILE *pp; char buf1[CF_BUFSIZE], buf2[CF_BUFSIZE], buf3[CF_BUFSIZE]; char host[CF_MAXVARSIZE], source[CF_BUFSIZE], mounton[CF_BUFSIZE], vbuff[CF_BUFSIZE]; int i, nfs = false; for (i = 0; VMOUNTCOMM[VSYSTEMHARDCLASS][i] != ' '; i++) { buf1[i] = VMOUNTCOMM[VSYSTEMHARDCLASS][i]; } buf1[i] = '\0'; SetTimeOut(RPCTIMEOUT); if ((pp = cf_popen(buf1, "r")) == NULL) { CfOut(cf_error, "cf_popen", "Can't open %s\n", buf1); return false; } do { vbuff[0] = buf1[0] = buf2[0] = buf3[0] = source[0] = '\0'; if (ferror(pp)) /* abortable */ { CfOut(cf_error, "ferror", "Error getting mount info\n"); break; } if (CfReadLine(vbuff, CF_BUFSIZE, pp) == -1) { FatalError("Error in CfReadLine"); } if (ferror(pp)) /* abortable */ { CfOut(cf_error, "ferror", "Error getting mount info\n"); break; } if (strstr(vbuff, "nfs")) { nfs = true; } sscanf(vbuff, "%s%s%s", buf1, buf2, buf3); if ((vbuff[0] == '\0') || (vbuff[0] == '\n')) { break; } if (strstr(vbuff, "not responding")) { CfOut(cf_error, "", "%s\n", vbuff); } if (strstr(vbuff, "be root")) { CfOut(cf_error, "", "Mount access is denied. You must be root.\n"); CfOut(cf_error, "", "Use the -n option to run safely."); } if ((strstr(vbuff, "retrying")) || (strstr(vbuff, "denied")) || (strstr(vbuff, "backgrounding"))) { continue; } if ((strstr(vbuff, "exceeded")) || (strstr(vbuff, "busy"))) { continue; } if (strstr(vbuff, "RPC")) { CfOut(cf_inform, "", "There was an RPC timeout. Aborting mount operations.\n"); CfOut(cf_inform, "", "Session failed while trying to talk to remote host\n"); CfOut(cf_inform, "", "%s\n", vbuff); cf_pclose(pp); return false; } #if defined(__sun) || defined(__hpux) if (IsAbsoluteFileName(buf3)) { strcpy(host, "localhost"); strcpy(mounton, buf1); } else { sscanf(buf1, "%[^:]:%s", host, source); strcpy(mounton, buf1); } #elif defined(_AIX) /* skip header */ if (IsAbsoluteFileName(buf1)) { strcpy(host, "localhost"); strcpy(mounton, buf2); } else { strcpy(host, buf1); strcpy(source, buf1); strcpy(mounton, buf3); } #elif defined(__CYGWIN__) strcpy(mounton, buf2); strcpy(host, buf1); #elif defined(sco) || defined(__SCO_DS) CfOut(cf_error, "", "Don't understand SCO mount format, no data"); #else if (IsAbsoluteFileName(buf1)) { strcpy(host, "localhost"); strcpy(mounton, buf3); } else { sscanf(buf1, "%[^:]:%s", host, source); strcpy(mounton, buf3); } #endif CfDebug("GOT: host=%s, source=%s, mounton=%s\n", host, source, mounton); if (nfs) { AugmentMountInfo(list, host, source, mounton, "nfs"); } else { AugmentMountInfo(list, host, source, mounton, NULL); } } while (!feof(pp)); alarm(0); signal(SIGALRM, SIG_DFL); cf_pclose(pp); return true; }
int LoadMountInfo(Rlist **list) /* This is, in fact, the most portable way to read the mount info! */ /* Depressing, isn't it? */ { FILE *pp; char buf1[CF_BUFSIZE], buf2[CF_BUFSIZE], buf3[CF_BUFSIZE]; char host[CF_MAXVARSIZE], source[CF_BUFSIZE], mounton[CF_BUFSIZE], vbuff[CF_BUFSIZE]; int i, nfs = false; for (i = 0; VMOUNTCOMM[VSYSTEMHARDCLASS][i] != ' '; i++) { buf1[i] = VMOUNTCOMM[VSYSTEMHARDCLASS][i]; } buf1[i] = '\0'; SetTimeOut(RPCTIMEOUT); if ((pp = cf_popen(buf1, "r")) == NULL) { CfOut(cf_error, "cf_popen", "Can't open %s\n", buf1); return false; } do { vbuff[0] = buf1[0] = buf2[0] = buf3[0] = source[0] = '\0'; if (ferror(pp)) /* abortable */ { CfOut(cf_error, "ferror", "Error getting mount info\n"); break; } CfReadLine(vbuff, CF_BUFSIZE, pp); if (ferror(pp)) /* abortable */ { CfOut(cf_error, "ferror", "Error getting mount info\n"); break; } if (strstr(vbuff, "nfs")) { nfs = true; } sscanf(vbuff, "%s%s%s", buf1, buf2, buf3); if ((vbuff[0] == '\0') || (vbuff[0] == '\n')) { break; } if (strstr(vbuff, "not responding")) { CfOut(cf_error, "", "%s\n", vbuff); } if (strstr(vbuff, "be root")) { CfOut(cf_error, "", "Mount access is denied. You must be root.\n"); CfOut(cf_error, "", "Use the -n option to run safely."); } if ((strstr(vbuff, "retrying")) || (strstr(vbuff, "denied")) || (strstr(vbuff, "backgrounding"))) { continue; } if ((strstr(vbuff, "exceeded")) || (strstr(vbuff, "busy"))) { continue; } if (strstr(vbuff, "RPC")) { CfOut(cf_inform, "", "There was an RPC timeout. Aborting mount operations.\n"); CfOut(cf_inform, "", "Session failed while trying to talk to remote host\n"); CfOut(cf_inform, "", "%s\n", vbuff); cf_pclose(pp); return false; } switch (VSYSTEMHARDCLASS) { case darwin: case linuxx: case unix_sv: case freebsd: case netbsd: case openbsd: case qnx: case crayos: case dragonfly: if (IsAbsoluteFileName(buf1)) { strcpy(host, "localhost"); strcpy(mounton, buf3); } else { sscanf(buf1, "%[^:]:%s", host, source); strcpy(mounton, buf3); } break; case solaris: case hp: if (IsAbsoluteFileName(buf3)) { strcpy(host, "localhost"); strcpy(mounton, buf1); } else { sscanf(buf1, "%[^:]:%s", host, source); strcpy(mounton, buf1); } break; case aix: /* skip header */ if (IsAbsoluteFileName(buf1)) { strcpy(host, "localhost"); strcpy(mounton, buf2); } else { strcpy(host, buf1); strcpy(source, buf1); strcpy(mounton, buf3); } break; case cfnt: strcpy(mounton, buf2); strcpy(host, buf1); break; case cfsco: CfOut(cf_error, "", "Don't understand SCO mount format, no data"); default: printf("cfengine software error: case %d = %s\n", VSYSTEMHARDCLASS, CLASSTEXT[VSYSTEMHARDCLASS]); FatalError("System error in GetMountInfo - no such class!"); } CfDebug("GOT: host=%s, source=%s, mounton=%s\n", host, source, mounton); if (nfs) { AugmentMountInfo(list, host, source, mounton, "nfs"); } else { AugmentMountInfo(list, host, source, mounton, NULL); } } while (!feof(pp)); alarm(0); signal(SIGALRM, SIG_DFL); cf_pclose(pp); return true; }
static char *InputLocation(char *filename) { static char wfilename[CF_BUFSIZE], path[CF_BUFSIZE]; if (MINUSF && (filename != VINPUTFILE) && IsFileOutsideDefaultRepository(VINPUTFILE) && !IsAbsoluteFileName(filename)) { /* If -f assume included relative files are in same directory */ strncpy(path,VINPUTFILE,CF_BUFSIZE-1); ChopLastNode(path); snprintf(wfilename,CF_BUFSIZE-1,"%s%c%s",path,FILE_SEPARATOR,filename); } else if (IsFileOutsideDefaultRepository(filename)) { strncpy(wfilename,filename,CF_BUFSIZE-1); } else { snprintf(wfilename,CF_BUFSIZE-1,"%s%cinputs%c%s",CFWORKDIR,FILE_SEPARATOR,FILE_SEPARATOR,filename); } return MapName(wfilename); }
bool IsFileOutsideDefaultRepository(const char *f) { return (*f == '.') || (IsAbsoluteFileName(f)); }
bool LoadMountInfo(Seq *list) /* This is, in fact, the most portable way to read the mount info! */ /* Depressing, isn't it? */ { FILE *pp; char buf1[CF_BUFSIZE], buf2[CF_BUFSIZE], buf3[CF_BUFSIZE]; int i, nfs = false; for (i = 0; VMOUNTCOMM[VSYSTEMHARDCLASS][i] != ' '; i++) { buf1[i] = VMOUNTCOMM[VSYSTEMHARDCLASS][i]; } buf1[i] = '\0'; SetTimeOut(RPCTIMEOUT); if ((pp = cf_popen(buf1, "r", true)) == NULL) { Log(LOG_LEVEL_ERR, "Can't open '%s'. (cf_popen: %s)", buf1, GetErrorStr()); return false; } size_t vbuff_size = CF_BUFSIZE; char *vbuff = xmalloc(vbuff_size); for (;;) { buf1[0] = buf2[0] = buf3[0] = '\0'; nfs = false; ssize_t res = CfReadLine(&vbuff, &vbuff_size, pp); if (res == -1) { if (!feof(pp)) { Log(LOG_LEVEL_ERR, "Unable to read list of mounted filesystems. (fread: %s)", GetErrorStr()); cf_pclose(pp); free(vbuff); return false; } else { break; } } if (strstr(vbuff, "nfs")) { nfs = true; } // security note: buff is CF_BUFSIZE, so that is the max that can be written to buf1, buf2 or buf3 sscanf(vbuff, "%s%s%s", buf1, buf2, buf3); if ((vbuff[0] == '\0') || (vbuff[0] == '\n')) { break; } if (strstr(vbuff, "not responding")) { Log(LOG_LEVEL_ERR, "%s", vbuff); } if (strstr(vbuff, "be root")) { Log(LOG_LEVEL_ERR, "Mount access is denied. You must be root. Use the -n option to run safely"); } if ((strstr(vbuff, "retrying")) || (strstr(vbuff, "denied")) || (strstr(vbuff, "backgrounding"))) { continue; } if ((strstr(vbuff, "exceeded")) || (strstr(vbuff, "busy"))) { continue; } if (strstr(vbuff, "RPC")) { Log(LOG_LEVEL_INFO, "There was an RPC timeout. Aborting mount operations."); Log(LOG_LEVEL_INFO, "Session failed while trying to talk to remote host"); Log(LOG_LEVEL_INFO, "%s", vbuff); cf_pclose(pp); free(vbuff); return false; } // host: max FQDN is 255 chars (max IPv6 with IPv4 tunneling is 45 chars) // source, mounton: hardcoding max path length to 1023; longer is very unlikely char host[256], source[1024], mounton[1024]; host[0] = source[0] = mounton[0] = '\0'; #if defined(__sun) || defined(__hpux) if (IsAbsoluteFileName(buf3)) { strlcpy(host, "localhost", sizeof(host)); strlcpy(mounton, buf1, sizeof(mounton)); } else { sscanf(buf1, "%255[^:]:%1023s", host, source); strlcpy(mounton, buf1, sizeof(mounton)); } #elif defined(_AIX) /* skip header */ if (IsAbsoluteFileName(buf1)) { strlcpy(host, "localhost", sizeof(host)); strlcpy(mounton, buf2, sizeof(mounton)); } else { strlcpy(host, buf1, sizeof(host)); strlcpy(source, buf1, sizeof(source)); strlcpy(mounton, buf3, sizeof(mounton)); } #elif defined(__CYGWIN__) strlcpy(mounton, buf2, sizeof(mounton)); strlcpy(host, buf1, sizeof(host)); #elif defined(sco) || defined(__SCO_DS) Log(LOG_LEVEL_ERR, "Don't understand SCO mount format, no data"); #else if (IsAbsoluteFileName(buf1)) { strlcpy(host, "localhost", sizeof(host)); strlcpy(mounton, buf3, sizeof(mounton)); } else { sscanf(buf1, "%255[^:]:%1023s", host, source); strlcpy(mounton, buf3, sizeof(mounton)); } #endif Log(LOG_LEVEL_DEBUG, "LoadMountInfo: host '%s', source '%s', mounton '%s'", host, source, mounton); if (nfs) { AugmentMountInfo(list, host, source, mounton, "nfs"); } else { AugmentMountInfo(list, host, source, mounton, NULL); } } free(vbuff); alarm(0); signal(SIGALRM, SIG_DFL); cf_pclose(pp); return true; }
PromiseResult VerifyRelativeLink(EvalContext *ctx, char *destination, const char *source, Attributes attr, const Promise *pp) { char *sp, *commonto, *commonfrom; char buff[CF_BUFSIZE], linkto[CF_BUFSIZE]; int levels = 0; if (*source == '.') { return VerifyLink(ctx, destination, source, attr, pp); } if (!CompressPath(linkto, sizeof(linkto), source)) { cfPS(ctx, LOG_LEVEL_ERR, PROMISE_RESULT_INTERRUPTED, pp, attr, "Failed to link '%s' to '%s'", destination, source); return PROMISE_RESULT_INTERRUPTED; } commonto = linkto; commonfrom = destination; if (strcmp(commonto, commonfrom) == 0) { cfPS(ctx, LOG_LEVEL_ERR, PROMISE_RESULT_INTERRUPTED, pp, attr, "Failed to link '%s' to '%s', can't link file '%s' to itself", destination, source, commonto); return PROMISE_RESULT_INTERRUPTED; } while (*commonto == *commonfrom) { commonto++; commonfrom++; } while (!((IsAbsoluteFileName(commonto)) && (IsAbsoluteFileName(commonfrom)))) { commonto--; commonfrom--; } commonto++; for (sp = commonfrom; *sp != '\0'; sp++) { if (IsFileSep(*sp)) { levels++; } } memset(buff, 0, CF_BUFSIZE); strcat(buff, "."); strcat(buff, FILE_SEPARATOR_STR); while (--levels > 0) { const char add[] = ".." FILE_SEPARATOR_STR; if (!PathAppend(buff, sizeof(buff), add, FILE_SEPARATOR)) { Log(LOG_LEVEL_ERR, "Internal limit reached in VerifyRelativeLink()," " path too long: '%s' + '%s'", buff, add); return PROMISE_RESULT_FAIL; } } if (!PathAppend(buff, sizeof(buff), commonto, FILE_SEPARATOR)) { Log(LOG_LEVEL_ERR, "Internal limit reached in VerifyRelativeLink() end," " path too long: '%s' + '%s'", buff, commonto); return PROMISE_RESULT_FAIL; } return VerifyLink(ctx, destination, buff, attr, pp); }
static ActionResult RepairExec(EvalContext *ctx, Attributes a, Promise *pp) { char line[CF_BUFSIZE], eventname[CF_BUFSIZE]; char cmdline[CF_BUFSIZE]; char comm[20]; int outsourced, count = 0; #if !defined(__MINGW32__) mode_t maskval = 0; #endif FILE *pfp; char cmdOutBuf[CF_BUFSIZE]; int cmdOutBufPos = 0; int lineOutLen; if (IsAbsoluteFileName(CommandArg0(pp->promiser)) || a.contain.shelltype == SHELL_TYPE_NONE) { if (!IsExecutable(CommandArg0(pp->promiser))) { cfPS(ctx, LOG_LEVEL_ERR, PROMISE_RESULT_FAIL, pp, a, "'%s' promises to be executable but isn't", pp->promiser); if (strchr(pp->promiser, ' ')) { Log(LOG_LEVEL_VERBOSE, "Paths with spaces must be inside escaped quoutes (e.g. \\\"%s\\\")", pp->promiser); } return ACTION_RESULT_FAILED; } else { Log(LOG_LEVEL_VERBOSE, "Promiser string contains a valid executable '%s' - ok", CommandArg0(pp->promiser)); } } char timeout_str[CF_BUFSIZE]; if (a.contain.timeout == CF_NOINT) { snprintf(timeout_str, CF_BUFSIZE, "no timeout"); } else { snprintf(timeout_str, CF_BUFSIZE, "timeout=%ds", a.contain.timeout); } char owner_str[CF_BUFSIZE] = ""; if (a.contain.owner != -1) { snprintf(owner_str, CF_BUFSIZE, ",uid=%ju", (uintmax_t)a.contain.owner); } char group_str[CF_BUFSIZE] = ""; if (a.contain.group != -1) { snprintf(group_str, CF_BUFSIZE, ",gid=%ju", (uintmax_t)a.contain.group); } snprintf(cmdline, CF_BUFSIZE, "%s%s%s", pp->promiser, a.args ? " " : "", a.args ? a.args : ""); Log(LOG_LEVEL_INFO, "Executing '%s%s%s' ... '%s'", timeout_str, owner_str, group_str, cmdline); BeginMeasure(); if (DONTDO && (!a.contain.preview)) { Log(LOG_LEVEL_ERR, "Would execute script '%s'", cmdline); return ACTION_RESULT_OK; } if (a.transaction.action != cfa_fix) { Log(LOG_LEVEL_ERR, "Command '%s' needs to be executed, but only warning was promised", cmdline); return ACTION_RESULT_OK; } CommandPrefix(cmdline, comm); if (a.transaction.background) { #ifdef __MINGW32__ outsourced = true; #else Log(LOG_LEVEL_VERBOSE, "Backgrounding job '%s'", cmdline); outsourced = fork(); #endif } else { outsourced = false; } if (outsourced || (!a.transaction.background)) // work done here: either by child or non-background parent { if (a.contain.timeout != CF_NOINT) { SetTimeOut(a.contain.timeout); } #ifndef __MINGW32__ Log(LOG_LEVEL_VERBOSE, "(Setting umask to %jo)", (uintmax_t)a.contain.umask); maskval = umask(a.contain.umask); if (a.contain.umask == 0) { Log(LOG_LEVEL_VERBOSE, "Programming '%s' running with umask 0! Use umask= to set", cmdline); } #endif /* !__MINGW32__ */ if (a.contain.shelltype == SHELL_TYPE_POWERSHELL) { #ifdef __MINGW32__ pfp = cf_popen_powershell_setuid(cmdline, "r", a.contain.owner, a.contain.group, a.contain.chdir, a.contain.chroot, a.transaction.background); #else // !__MINGW32__ Log(LOG_LEVEL_ERR, "Powershell is only supported on Windows"); return ACTION_RESULT_FAILED; #endif // !__MINGW32__ } else if (a.contain.shelltype == SHELL_TYPE_USE) { pfp = cf_popen_shsetuid(cmdline, "r", a.contain.owner, a.contain.group, a.contain.chdir, a.contain.chroot, a.transaction.background); } else { pfp = cf_popensetuid(cmdline, "r", a.contain.owner, a.contain.group, a.contain.chdir, a.contain.chroot, a.transaction.background); } if (pfp == NULL) { Log(LOG_LEVEL_ERR, "Couldn't open pipe to command '%s'. (cf_popen: %s)", cmdline, GetErrorStr()); return ACTION_RESULT_FAILED; } for (;;) { ssize_t res = CfReadLine(line, CF_BUFSIZE, pfp); if (res == 0) { break; } if (res == -1) { Log(LOG_LEVEL_ERR, "Unable to read output from command '%s'. (fread: %s)", cmdline, GetErrorStr()); cf_pclose(pfp); return ACTION_RESULT_FAILED; } if (strstr(line, "cfengine-die")) { break; } if (a.contain.preview) { PreviewProtocolLine(line, cmdline); } if (a.module) { ModuleProtocol(ctx, cmdline, line, !a.contain.nooutput, PromiseGetNamespace(pp)); } else if ((!a.contain.nooutput) && (!EmptyString(line))) { lineOutLen = strlen(comm) + strlen(line) + 12; // if buffer is to small for this line, output it directly if (lineOutLen > sizeof(cmdOutBuf)) { Log(LOG_LEVEL_NOTICE, "Q: '%s': %s", comm, line); } else { if (cmdOutBufPos + lineOutLen > sizeof(cmdOutBuf)) { Log(LOG_LEVEL_NOTICE, "%s", cmdOutBuf); cmdOutBufPos = 0; } sprintf(cmdOutBuf + cmdOutBufPos, "Q: \"...%s\": %s\n", comm, line); cmdOutBufPos += (lineOutLen - 1); } count++; } } #ifdef __MINGW32__ if (outsourced) // only get return value if we waited for command execution { cf_pclose(pfp); } else #endif /* __MINGW32__ */ { int ret = cf_pclose(pfp); if (ret == -1) { cfPS(ctx, LOG_LEVEL_INFO, PROMISE_RESULT_FAIL, pp, a, "Finished script '%s' - failed (abnormal termination)", pp->promiser); } else { VerifyCommandRetcode(ctx, ret, true, a, pp); } } } if (count) { if (cmdOutBufPos) { Log(LOG_LEVEL_NOTICE, "%s", cmdOutBuf); } Log(LOG_LEVEL_INFO, "Last %d quoted lines were generated by promiser '%s'", count, cmdline); } if (a.contain.timeout != CF_NOINT) { alarm(0); signal(SIGALRM, SIG_DFL); } Log(LOG_LEVEL_INFO, "Completed execution of '%s'", cmdline); #ifndef __MINGW32__ umask(maskval); #endif snprintf(eventname, CF_BUFSIZE - 1, "Exec(%s)", cmdline); #ifndef __MINGW32__ if ((a.transaction.background) && outsourced) { Log(LOG_LEVEL_VERBOSE, "Backgrounded command '%s' is done - exiting", cmdline); exit(0); } #endif /* !__MINGW32__ */ return ACTION_RESULT_OK; }
int MakeDirectoriesFor(char *file,char force) /* Make all directories which underpin file */ { char *sp,*spc; char currentpath[CF_BUFSIZE]; char pathbuf[CF_BUFSIZE]; struct stat statbuf; mode_t mask; int rootlen; char Path_File_Separator; #ifdef DARWIN /* Keeps track of if dealing w. resource fork */ int rsrcfork; rsrcfork = 0; char * tmpstr; #endif if (!IsAbsoluteFileName(file)) { snprintf(OUTPUT,CF_BUFSIZE*2,"Will not create directories for a relative filename (%s). Has no invariant meaning\n",file); CfLog(cferror,OUTPUT,""); return false; } strncpy(pathbuf,file,CF_BUFSIZE-1); /* local copy */ #ifdef DARWIN /* Dealing w. a rsrc fork? */ if (strstr(pathbuf, _PATH_RSRCFORKSPEC) != NULL) { rsrcfork = 1; } #endif /* skip link name */ sp = LastFileSeparator(pathbuf); if (sp == NULL) { sp = pathbuf; } *sp = '\0'; DeleteSlash(pathbuf); if (lstat(pathbuf,&statbuf) != -1) { if (S_ISLNK(statbuf.st_mode)) { Verbose("%s: INFO: %s is a symbolic link, not a true directory!\n",VPREFIX,pathbuf); } if (force == 'y') /* force in-the-way directories aside */ { if (!S_ISDIR(statbuf.st_mode)) /* if the dir exists - no problem */ { if (ISCFENGINE) { struct Tidy tp; struct TidyPattern tpat; struct stat sbuf; if (DONTDO) { return true; } strcpy(currentpath,pathbuf); DeleteSlash(currentpath); strcat(currentpath,".cf-moved"); snprintf(OUTPUT,CF_BUFSIZE,"Moving obstructing file/link %s to %s to make directory",pathbuf,currentpath); CfLog(cferror,OUTPUT,""); /* If cfagent, remove an obstructing backup object */ if (lstat(currentpath,&sbuf) != -1) { if (S_ISDIR(sbuf.st_mode)) { tp.maxrecurse = 2; tp.tidylist = &tpat; tp.next = NULL; tp.path = currentpath; tpat.recurse = CF_INF_RECURSE; tpat.age = 0; tpat.size = 0; tpat.pattern = strdup("*"); tpat.classes = strdup("any"); tpat.defines = NULL; tpat.elsedef = NULL; tpat.dirlinks = 'y'; tpat.travlinks = 'n'; tpat.rmdirs = 'y'; tpat.searchtype = 'a'; tpat.log = 'd'; tpat.inform = 'd'; tpat.next = NULL; // RecursiveTidySpecialArea(currentpath,&tp,CF_INF_RECURSE,&sbuf); free(tpat.pattern); free(tpat.classes); if (rmdir(currentpath) == -1) { snprintf(OUTPUT,CF_BUFSIZE*2,"Couldn't remove directory %s while trying to remove a backup\n",currentpath); CfLog(cfinform,OUTPUT,"rmdir"); } } else { if (unlink(currentpath) == -1) { snprintf(OUTPUT,CF_BUFSIZE*2,"Couldn't remove file/link %s while trying to remove a backup\n",currentpath); CfLog(cfinform,OUTPUT,"rmdir"); } } } /* And then move the current object out of the way...*/ if (rename(pathbuf,currentpath) == -1) { snprintf(OUTPUT,CF_BUFSIZE*2,"Warning. The object %s is not a directory.\n",pathbuf); CfLog(cfinform,OUTPUT,""); CfLog(cfinform,"Could not make a new directory or move the block","rename"); return(false); } } } else { if (! S_ISLNK(statbuf.st_mode) && ! S_ISDIR(statbuf.st_mode)) { snprintf(OUTPUT,CF_BUFSIZE*2,"Warning. The object %s is not a directory.\n",pathbuf); CfLog(cfinform,OUTPUT,""); CfLog(cfinform,"Cannot make a new directory without deleting it!\n\n",""); return(false); } } } } /* Now we can make a new directory .. */ currentpath[0] = '\0'; rootlen = RootDirLength(sp); strncpy(currentpath, file, rootlen); for (sp = file+rootlen, spc = currentpath+rootlen; *sp != '\0'; sp++) { if (!IsFileSep(*sp) && *sp != '\0') { *spc = *sp; spc++; } else { Path_File_Separator = *sp; *spc = '\0'; if (strlen(currentpath) == 0) { } else if (stat(currentpath,&statbuf) == -1) { Debug2("cfengine: Making directory %s, mode %o\n",currentpath,DEFAULTMODE); if (! DONTDO) { mask = umask(0); if (mkdir(currentpath,DEFAULTMODE) == -1) { snprintf(OUTPUT,CF_BUFSIZE*2,"Unable to make directories to %s\n",file); CfLog(cferror,OUTPUT,"mkdir"); umask(mask); return(false); } umask(mask); } } else { if (! S_ISDIR(statbuf.st_mode)) { #ifdef DARWIN /* Ck if rsrc fork */ if (rsrcfork) { tmpstr = malloc(CF_BUFSIZE); strncpy(tmpstr, currentpath, CF_BUFSIZE); strncat(tmpstr, _PATH_FORKSPECIFIER, CF_BUFSIZE); /* Cfengine removed terminating slashes */ DeleteSlash(tmpstr); if (strncmp(tmpstr, pathbuf, CF_BUFSIZE) == 0) { free(tmpstr); return(true); } free(tmpstr); } #endif snprintf(OUTPUT,CF_BUFSIZE*2,"Cannot make %s - %s is not a directory! (use forcedirs=true)\n",pathbuf,currentpath); CfLog(cferror,OUTPUT,""); return(false); } } /* *spc = FILE_SEPARATOR; */ *spc = Path_File_Separator; spc++; } } Debug("Directory for %s exists. Okay\n",file); return(true); }
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; } } }
bool MakeParentDirectory(const char *parentandchild, bool force) { char *sp; char currentpath[CF_BUFSIZE]; char pathbuf[CF_BUFSIZE]; struct stat statbuf; mode_t mask; int rootlen; #ifdef __APPLE__ /* Keeps track of if dealing w. resource fork */ int rsrcfork; rsrcfork = 0; char *tmpstr; #endif Log(LOG_LEVEL_DEBUG, "Trying to create a parent directory%s for: %s", force ? " (force applied)" : "", parentandchild); if (!IsAbsoluteFileName(parentandchild)) { Log(LOG_LEVEL_ERR, "Will not create directories for a relative filename: %s", parentandchild); return false; } strlcpy(pathbuf, parentandchild, CF_BUFSIZE); /* local copy */ #ifdef __APPLE__ if (strstr(pathbuf, _PATH_RSRCFORKSPEC) != NULL) { rsrcfork = 1; } #endif /* skip link name */ sp = (char *) LastFileSeparator(pathbuf); /* de-constify */ if (sp == NULL) { sp = pathbuf; } *sp = '\0'; DeleteSlash(pathbuf); if (lstat(pathbuf, &statbuf) != -1) { if (S_ISLNK(statbuf.st_mode)) { Log(LOG_LEVEL_VERBOSE, "'%s' is a symbolic link, not a directory", pathbuf); } if (force) /* force in-the-way directories aside */ { struct stat dir; stat(pathbuf, &dir); /* If the target directory exists as a directory, no problem. */ /* If the target directory exists but is not a directory, then * rename it to ".cf-moved": */ if (!S_ISDIR(dir.st_mode)) { struct stat sbuf; if (DONTDO) { return true; } strcpy(currentpath, pathbuf); DeleteSlash(currentpath); /* TODO overflow check! */ strlcat(currentpath, ".cf-moved", sizeof(currentpath)); Log(LOG_LEVEL_INFO, "Moving obstructing file/link %s to %s to make directory", pathbuf, currentpath); /* Remove possibly pre-existing ".cf-moved" backup object. */ if (lstat(currentpath, &sbuf) != -1) { if (S_ISDIR(sbuf.st_mode)) /* directory */ { DeleteDirectoryTree(currentpath); } else /* not a directory */ { if (unlink(currentpath) == -1) { Log(LOG_LEVEL_INFO, "Couldn't remove file/link" " '%s' while trying to remove a backup" " (unlink: %s)", currentpath, GetErrorStr()); } } } /* And then rename the current object to ".cf-moved". */ if (rename(pathbuf, currentpath) == -1) { Log(LOG_LEVEL_INFO, "Couldn't rename '%s' to .cf-moved" " (rename: %s)", pathbuf, GetErrorStr()); return false; } } } else { if (!S_ISLNK(statbuf.st_mode) && !S_ISDIR(statbuf.st_mode)) { Log(LOG_LEVEL_INFO, "The object '%s' is not a directory." " Cannot make a new directory without deleting it.", pathbuf); return false; } } } /* Now we make directories descending from the root folder down to the leaf */ currentpath[0] = '\0'; rootlen = RootDirLength(parentandchild); /* currentpath is not NULL terminated on purpose! */ strncpy(currentpath, parentandchild, rootlen); for (size_t z = rootlen; parentandchild[z] != '\0'; z++) { const char c = parentandchild[z]; /* Copy up to the next separator. */ if (!IsFileSep(c)) { currentpath[z] = c; continue; } const char path_file_separator = c; currentpath[z] = '\0'; /* currentpath is complete path for each of the parent directories. */ if (currentpath[0] == '\0') { /* We are at dir "/" of an absolute path, no need to create. */ } /* WARNING: on Windows stat() fails if path has a trailing slash! */ else if (stat(currentpath, &statbuf) == -1) { if (!DONTDO) { mask = umask(0); if (mkdir(currentpath, DEFAULTMODE) == -1) { Log(LOG_LEVEL_ERR, "Unable to make directory: %s (mkdir: %s)", currentpath, GetErrorStr()); umask(mask); return false; } umask(mask); } } else { if (!S_ISDIR(statbuf.st_mode)) { #ifdef __APPLE__ /* Ck if rsrc fork */ if (rsrcfork) { tmpstr = xmalloc(CF_BUFSIZE); strlcpy(tmpstr, currentpath, CF_BUFSIZE); strncat(tmpstr, _PATH_FORKSPECIFIER, CF_BUFSIZE); /* CFEngine removed terminating slashes */ DeleteSlash(tmpstr); if (strncmp(tmpstr, pathbuf, CF_BUFSIZE) == 0) { free(tmpstr); return true; } free(tmpstr); } #endif Log(LOG_LEVEL_ERR, "Cannot make %s - %s is not a directory!" " (use forcedirs=true)", pathbuf, currentpath); return false; } } currentpath[z] = path_file_separator; } Log(LOG_LEVEL_DEBUG, "Directory for '%s' exists. Okay", parentandchild); return true; }
bool MakeParentDirectory(const char *parentandchild, bool force) { char *spc, *sp; char currentpath[CF_BUFSIZE]; char pathbuf[CF_BUFSIZE]; struct stat statbuf; mode_t mask; int rootlen; char Path_File_Separator; #ifdef __APPLE__ /* Keeps track of if dealing w. resource fork */ int rsrcfork; rsrcfork = 0; char *tmpstr; #endif Log(LOG_LEVEL_DEBUG, "Trying to create a parent directory for '%s%s'", parentandchild, force ? " (force applied)" : ""); if (!IsAbsoluteFileName(parentandchild)) { Log(LOG_LEVEL_ERR, "Will not create directories for a relative filename '%s'. Has no invariant meaning", parentandchild); return false; } strncpy(pathbuf, parentandchild, CF_BUFSIZE - 1); /* local copy */ #ifdef __APPLE__ if (strstr(pathbuf, _PATH_RSRCFORKSPEC) != NULL) { rsrcfork = 1; } #endif /* skip link name */ /* This cast is necessary, as you can't have (char* -> char*) and (const char* -> const char*) functions in C */ sp = (char *) LastFileSeparator(pathbuf); if (sp == NULL) { sp = pathbuf; } *sp = '\0'; DeleteSlash(pathbuf); if (lstat(pathbuf, &statbuf) != -1) { if (S_ISLNK(statbuf.st_mode)) { Log(LOG_LEVEL_VERBOSE, "INFO: %s is a symbolic link, not a true directory!", pathbuf); } if (force) /* force in-the-way directories aside */ { struct stat dir; stat(pathbuf, &dir); if (!S_ISDIR(dir.st_mode)) /* if the dir exists - no problem */ { struct stat sbuf; if (DONTDO) { return true; } strcpy(currentpath, pathbuf); DeleteSlash(currentpath); strcat(currentpath, ".cf-moved"); Log(LOG_LEVEL_INFO, "Moving obstructing file/link %s to %s to make directory", pathbuf, currentpath); /* If cfagent, remove an obstructing backup object */ if (lstat(currentpath, &sbuf) != -1) { if (S_ISDIR(sbuf.st_mode)) { DeleteDirectoryTree(currentpath); } else { if (unlink(currentpath) == -1) { Log(LOG_LEVEL_INFO, "Couldn't remove file/link '%s' while trying to remove a backup. (unlink: %s)", currentpath, GetErrorStr()); } } } /* And then move the current object out of the way... */ if (rename(pathbuf, currentpath) == -1) { Log(LOG_LEVEL_INFO, "Warning: The object '%s' is not a directory. (rename: %s)", pathbuf, GetErrorStr()); return false; } } } else { if (!S_ISLNK(statbuf.st_mode) && !S_ISDIR(statbuf.st_mode)) { Log(LOG_LEVEL_INFO, "The object %s is not a directory. Cannot make a new directory without deleting it.", pathbuf); return false; } } } /* Now we can make a new directory .. */ currentpath[0] = '\0'; rootlen = RootDirLength(parentandchild); strncpy(currentpath, parentandchild, rootlen); for (sp = (char*) parentandchild + rootlen, spc = currentpath + rootlen; *sp != '\0'; sp++) { if (!IsFileSep(*sp) && *sp != '\0') { *spc = *sp; spc++; } else { Path_File_Separator = *sp; *spc = '\0'; if (strlen(currentpath) == 0) { } else if (stat(currentpath, &statbuf) == -1) { if (!DONTDO) { mask = umask(0); if (mkdir(currentpath, DEFAULTMODE) == -1) { Log(LOG_LEVEL_ERR, "Unable to make directories to '%s'. (mkdir: %s)", parentandchild, GetErrorStr()); umask(mask); return false; } umask(mask); } } else { if (!S_ISDIR(statbuf.st_mode)) { #ifdef __APPLE__ /* Ck if rsrc fork */ if (rsrcfork) { tmpstr = xmalloc(CF_BUFSIZE); strncpy(tmpstr, currentpath, CF_BUFSIZE); strncat(tmpstr, _PATH_FORKSPECIFIER, CF_BUFSIZE); /* CFEngine removed terminating slashes */ DeleteSlash(tmpstr); if (strncmp(tmpstr, pathbuf, CF_BUFSIZE) == 0) { free(tmpstr); return true; } free(tmpstr); } #endif Log(LOG_LEVEL_ERR, "Cannot make %s - %s is not a directory! (use forcedirs=true)", pathbuf, currentpath); return false; } } /* *spc = FILE_SEPARATOR; */ *spc = Path_File_Separator; spc++; } } Log(LOG_LEVEL_DEBUG, "Directory for '%s' exists. Okay", parentandchild); return true; }