Skip to content

Commit

Permalink
tool_dirhie: create dir hierarchy without strtok
Browse files Browse the repository at this point in the history
And use dynbuf

Closes curl#16566
  • Loading branch information
bagder committed Mar 5, 2025
1 parent 86ac471 commit 787ee93
Showing 1 changed file with 36 additions and 57 deletions.
93 changes: 36 additions & 57 deletions src/tool_dirhie.c
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@

#include "tool_dirhie.h"
#include "tool_msgs.h"
#include "dynbuf.h"

#include "memdebug.h" /* keep this as LAST include */

Expand Down Expand Up @@ -93,73 +94,51 @@ static void show_dir_errno(struct GlobalConfig *global, const char *name)
#define PATH_DELIMITERS DIR_CHAR
#endif


CURLcode create_dir_hierarchy(const char *outfile, struct GlobalConfig *global)
{
char *tempdir;
char *tempdir2;
char *outdup;
char *dirbuildup;
CURLcode result = CURLE_OK;
size_t outlen;
size_t outlen = strlen(outfile);
struct curlx_dynbuf dirbuf;

outlen = strlen(outfile);
outdup = strdup(outfile);
if(!outdup)
return CURLE_OUT_OF_MEMORY;
curlx_dyn_init(&dirbuf, outlen + 1);

while(*outfile) {
bool skip = FALSE;
size_t seplen = strspn(outfile, PATH_DELIMITERS);
size_t len = strcspn(&outfile[seplen], PATH_DELIMITERS);

/* the last path component is the file and it ends with a null byte */
if(!outfile[len + seplen])
break;

dirbuildup = malloc(outlen + 1);
if(!dirbuildup) {
Curl_safefree(outdup);
return CURLE_OUT_OF_MEMORY;
}
dirbuildup[0] = '\0';

/* Allow strtok() here since this is not used threaded */
/* !checksrc! disable BANNEDFUNC 2 */
tempdir = strtok(outdup, PATH_DELIMITERS);

while(tempdir) {
bool skip = false;
tempdir2 = strtok(NULL, PATH_DELIMITERS);
/* since strtok returns a token for the last word even
if not ending with DIR_CHAR, we need to prune it */
if(tempdir2) {
size_t dlen = strlen(dirbuildup);
if(dlen)
msnprintf(&dirbuildup[dlen], outlen - dlen, "%s%s", DIR_CHAR, tempdir);
else {
if(outdup == tempdir) {
#if defined(_WIN32) || defined(MSDOS)
/* Skip creating a drive's current directory.
It may seem as though that would harmlessly fail but it could be
a corner case if X: did not exist, since we would be creating it
erroneously.
eg if outfile is X:\foo\bar\filename then do not mkdir X:
This logic takes into account unsupported drives !:, 1:, etc. */
char *p = strchr(tempdir, ':');
if(p && !p[1])
skip = true;
if(!curlx_dyn_len(&dirbuf)) {
/* Skip creating a drive's current directory. It may seem as though that
would harmlessly fail but it could be a corner case if X: did not
exist, since we would be creating it erroneously. eg if outfile is
X:\foo\bar\filename then do not mkdir X: This logic takes into
account unsupported drives !:, 1:, etc. */
if(len > 1 && (outfile[1]==':'))
skip = TRUE;
}
#endif
/* the output string does not start with a separator */
strcpy(dirbuildup, tempdir);
}
else
msnprintf(dirbuildup, outlen, "%s%s", DIR_CHAR, tempdir);
}
/* Create directory. Ignore access denied error to allow traversal. */
if(!skip && (-1 == mkdir(dirbuildup, (mode_t)0000750)) &&
(errno != EACCES) && (errno != EEXIST)) {
show_dir_errno(global, dirbuildup);
result = CURLE_WRITE_ERROR;
break; /* get out of loop */
}
/* insert the leading separators (possibly plural) plus the following
directory name */
result = curlx_dyn_addn(&dirbuf, outfile, seplen + len);
if(result)
return result;

/* Create directory. Ignore access denied error to allow traversal. */
if(!skip && (-1 == mkdir(curlx_dyn_ptr(&dirbuf), (mode_t)0000750)) &&
(errno != EACCES) && (errno != EEXIST)) {
show_dir_errno(global, curlx_dyn_ptr(&dirbuf));
result = CURLE_WRITE_ERROR;
break; /* get out of loop */
}
tempdir = tempdir2;
outfile += len + seplen;
}

Curl_safefree(dirbuildup);
Curl_safefree(outdup);
curlx_dyn_free(&dirbuf);

return result;
}

0 comments on commit 787ee93

Please sign in to comment.