void DeleteRedundantSlashes(char *str) { int move_from; // Invariant: newpos <= oldpos int oldpos = RootDirLength(str); int newpos = oldpos; while (str[oldpos] != '\0') { // Skip over subsequent separators. while (IsFileSep(str[oldpos])) { oldpos++; } move_from = oldpos; // And then skip over the next path component. while (str[oldpos] != '\0' && !IsFileSep(str[oldpos])) { oldpos++; } // If next character is file separator, move past it, since we want to keep one. if (IsFileSep(str[oldpos])) { oldpos++; } int move_len = oldpos - move_from; memmove(&str[newpos], &str[move_from], move_len); newpos += move_len; } str[newpos] = '\0'; }
int CompressPath(char *dest, const char *src) { char node[CF_BUFSIZE]; int nodelen; int rootlen; memset(dest, 0, CF_BUFSIZE); rootlen = RootDirLength(src); strncpy(dest, src, rootlen); for (const char *sp = src + rootlen; *sp != '\0'; sp++) { if (IsFileSep(*sp)) { continue; } for (nodelen = 0; (sp[nodelen] != '\0') && (!IsFileSep(sp[nodelen])); nodelen++) { if (nodelen > CF_MAXLINKSIZE) { Log(LOG_LEVEL_ERR, "Link in path suspiciously large"); return false; } } strncpy(node, sp, nodelen); node[nodelen] = '\0'; sp += nodelen - 1; if (strcmp(node, ".") == 0) { continue; } if (strcmp(node, "..") == 0) { if (!ChopLastNode(dest)) { Log(LOG_LEVEL_DEBUG, "used .. beyond top of filesystem!"); return false; } continue; } else { AddSlash(dest); } if (!JoinPath(dest, node)) { return false; } } return true; }
// Can remove several slashes if they are redundant. void DeleteSlash(char *str) { int size = strlen(str); if ((size == 0) || (str == NULL)) { return; } int root = RootDirLength(str); while (IsFileSep(str[size - 1]) && size - 1 > root) { size--; } str[size] = '\0'; /* no-op if we didn't change size */ }
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; }
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; }
/** * @TODO fix the dangerous path lengths */ int CompressPath(char *dest, const char *src) { char node[CF_BUFSIZE]; int nodelen; int rootlen; memset(dest, 0, CF_BUFSIZE); rootlen = RootDirLength(src); memcpy(dest, src, rootlen); for (const char *sp = src + rootlen; *sp != '\0'; sp++) { if (IsFileSep(*sp)) { continue; } for (nodelen = 0; (sp[nodelen] != '\0') && (!IsFileSep(sp[nodelen])); nodelen++) { if (nodelen > CF_MAXLINKSIZE) { Log(LOG_LEVEL_ERR, "Link in path suspiciously large"); return false; } } strncpy(node, sp, nodelen); node[nodelen] = '\0'; sp += nodelen - 1; if (strcmp(node, ".") == 0) { continue; } if (strcmp(node, "..") == 0) { if (!ChopLastNode(dest)) { Log(LOG_LEVEL_DEBUG, "used .. beyond top of filesystem!"); return false; } continue; } AddSlash(dest); /* TODO use dest_size parameter instead of CF_BUFSIZE. */ size_t ret = strlcat(dest, node, CF_BUFSIZE); if (ret >= CF_BUFSIZE) { Log(LOG_LEVEL_ERR, "Internal limit reached in CompressPath()," " path too long: '%s' + '%s'", dest, node); return false; } } return true; }
int CompressPath(char *dest,char *src) { char *sp; char node[CF_BUFSIZE]; int nodelen; int rootlen; Debug2("CompressPath(%s,%s)\n",dest,src); memset(dest,0,CF_BUFSIZE); rootlen = RootDirLength(src); strncpy(dest,src,rootlen); for (sp = src+rootlen; *sp != '\0'; sp++) { if (IsFileSep(*sp)) { continue; } for (nodelen = 0; sp[nodelen] != '\0' && !IsFileSep(sp[nodelen]); nodelen++) { if (nodelen > CF_MAXLINKSIZE) { CfLog(cferror,"Link in path suspiciously large",""); return false; } } strncpy(node, sp, nodelen); node[nodelen] = '\0'; sp += nodelen - 1; if (strcmp(node,".") == 0) { continue; } if (strcmp(node,"..") == 0) { if (!ChopLastNode(dest)) { Debug("cfengine: used .. beyond top of filesystem!\n"); return false; } continue; } else { AddSlash(dest); } if (BufferOverflow(dest,node)) { return false; } strcat(dest,node); } return true; }
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); }
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; }