From 787ee935acd5867bdac836b2043b6095eed2c29e Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 5 Mar 2025 09:10:21 +0100 Subject: [PATCH] tool_dirhie: create dir hierarchy without strtok And use dynbuf Closes #16566 --- src/tool_dirhie.c | 93 ++++++++++++++++++----------------------------- 1 file changed, 36 insertions(+), 57 deletions(-) diff --git a/src/tool_dirhie.c b/src/tool_dirhie.c index 0536dff7d25c..1360ddd6bb2a 100644 --- a/src/tool_dirhie.c +++ b/src/tool_dirhie.c @@ -33,6 +33,7 @@ #include "tool_dirhie.h" #include "tool_msgs.h" +#include "dynbuf.h" #include "memdebug.h" /* keep this as LAST include */ @@ -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; }