Created
June 3, 2020 19:12
-
-
Save tonyhutter/95994e020bdc0f44dba84207151df337 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* This function works like realpath() but uses string manipulation only. | |
* No filesystem operations are done. It returns a resolved, absolute | |
* path to a file or directory, with no "..", "./" or "//" in it. It | |
* does not resolve symlinks. | |
* | |
* path: The path to a file or directory. It can be a relative or absolute | |
* path. If it's a relative path then basedir must also be specified. | |
* | |
* resolved_path: If non-NULL, then store the resulting string in this buffer | |
* which is assumed to be at least PATH_MAX in length. If NULL, | |
* then allocate and return the resulting string as the return | |
* value. | |
* | |
* basedir: An optional argument for the base directory that gets prepended | |
* if path is relative. For example, if path was "./file1" and | |
* basedir was "/var/tmp" then "/var/tmp/file1" would be returned. | |
* basedir must always be an absolute path. | |
* | |
* If both path and resolved_path both specified, and path is an absolute path, | |
* then resolved_path is ignored. | |
* | |
* If resolved_path is NULL, then allocate and return the resulting string as | |
* the return value. If resolved_path is specified, then return resolved_path. | |
* If there's an error, return NULL and set errno. | |
*/ | |
static char *realpath_str(const char *path, char *resolved_path, | |
const char *basedir) | |
{ | |
char *copy; | |
char *token, *save = NULL; | |
char *newpath; | |
if (!path) { | |
errno = EINVAL; | |
return NULL; | |
} | |
copy = calloc(1, PATH_MAX); | |
/* Path is relative, add in basedir */ | |
if (path[0] != '/') { | |
if (basedir) { | |
if (basedir[0] == '/') { | |
printf("Adding basedir\n"); | |
strcat(copy, basedir); | |
strcat(copy, "/"); | |
} else { | |
printf("bad basedir\n"); | |
/* | |
* We need to use basedir, but it was not | |
* an absolute path. | |
*/ | |
free(copy); | |
errno = EINVAL; | |
return NULL; | |
} | |
} else { | |
/* They specified a relative path with no basedir */ | |
free(copy); | |
errno = EINVAL; | |
return NULL; | |
} | |
} | |
strcat(copy, path); | |
newpath = calloc(1, PATH_MAX); | |
/* | |
* Iterate through our path, one token at a time. | |
*/ | |
do { | |
if (!save) { | |
/* First token */ | |
token = strtok_r(copy, "/", &save); | |
} else { | |
/* Rest of the tokens */ | |
token = strtok_r(NULL, "/", &save); | |
} | |
if (!token) | |
break; | |
/* Skip "./" */ | |
if (strcmp(token, ".") == 0) { | |
continue; | |
} | |
/* We got "..", erase the last directory entry */ | |
if (strcmp(token, "..") == 0) { | |
/* Chomp off the last subdir from newpath */ | |
dirname(newpath); | |
} else { | |
/* It's a normal directory/file, add it in */ | |
strcat(newpath, "/"); | |
strcat(newpath, token); | |
} | |
} while (1); | |
free(copy); | |
if (resolved_path) { | |
memcpy(resolved_path, newpath, strlen(newpath)+1); | |
free(newpath); | |
return resolved_path; | |
} else { | |
return newpath; | |
} | |
} | |
/* | |
* This is a recursive version of mkdir(). Everything is the same except | |
* it will create all the directories in 'path' if they don't exist (it's | |
* like a 'mkdir -p'). All directories will be created with the same mode. | |
* | |
* We assume 'path' is dynamically allocated, and we can manipulate it. | |
*/ | |
static int __mkdir_recursive(char *path, mode_t mode) | |
{ | |
DIR* dir = opendir(path); | |
char *tmp; | |
int rc; | |
if (dir) { | |
/* path already exists. */ | |
closedir(dir); | |
return 0; | |
} else if (errno == ENOENT) { | |
errno = 0; | |
/* | |
* path does not currently exist. See if the subdir one level | |
* own exists, and if so, try to create the last directory of | |
* path. | |
*/ | |
tmp = strdup(path); | |
/* Chomp off the last subdir */ | |
dirname(tmp); | |
rc = __mkdir_recursive(tmp, mode); | |
if (rc == 0) { | |
/* The directory underneath exists */ | |
rc = mkdir(path, mode); | |
} | |
free(tmp); | |
return rc; | |
} else { | |
/* Some other error, errno will already be set */ | |
return -1; | |
} | |
} | |
/* | |
* This is a recursive version of mkdir(). Everything is the same except | |
* it will create all the directories in 'path' if they don't exist (it's | |
* like a 'mkdir -p'). All directories will be created with the same mode. | |
*/ | |
static int mkdir_recursive(char *path, mode_t mode) | |
{ | |
char *newpath; | |
int rc; | |
/* | |
* Convert our path into an absolute path (removing the "../" and "./" | |
* and "//)"). The absolute path is allocated as a new string and | |
* must be freed. | |
*/ | |
newpath = realpath_str(path, NULL, NULL); | |
if (!newpath) { | |
rc = -1; | |
} else { | |
rc = __mkdir_recursive(path, mode); | |
} | |
free(newpath); | |
return rc; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment