| xf.li | 6c8fc1e | 2023-08-12 00:11:09 -0700 | [diff] [blame] | 1 | /*************************************************************************** | 
|  | 2 | *                                  _   _ ____  _ | 
|  | 3 | *  Project                     ___| | | |  _ \| | | 
|  | 4 | *                             / __| | | | |_) | | | 
|  | 5 | *                            | (__| |_| |  _ <| |___ | 
|  | 6 | *                             \___|\___/|_| \_\_____| | 
|  | 7 | * | 
|  | 8 | * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al. | 
|  | 9 | * | 
|  | 10 | * This software is licensed as described in the file COPYING, which | 
|  | 11 | * you should have received as part of this distribution. The terms | 
|  | 12 | * are also available at https://curl.se/docs/copyright.html. | 
|  | 13 | * | 
|  | 14 | * You may opt to use, copy, modify, merge, publish, distribute and/or sell | 
|  | 15 | * copies of the Software, and permit persons to whom the Software is | 
|  | 16 | * furnished to do so, under the terms of the COPYING file. | 
|  | 17 | * | 
|  | 18 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | 
|  | 19 | * KIND, either express or implied. | 
|  | 20 | * | 
|  | 21 | * SPDX-License-Identifier: curl | 
|  | 22 | * | 
|  | 23 | ***************************************************************************/ | 
|  | 24 | #include "tool_setup.h" | 
|  | 25 |  | 
|  | 26 | #include <sys/stat.h> | 
|  | 27 |  | 
|  | 28 | #ifdef WIN32 | 
|  | 29 | #  include <direct.h> | 
|  | 30 | #endif | 
|  | 31 |  | 
|  | 32 | #define ENABLE_CURLX_PRINTF | 
|  | 33 | /* use our own printf() functions */ | 
|  | 34 | #include "curlx.h" | 
|  | 35 |  | 
|  | 36 | #include "tool_dirhie.h" | 
|  | 37 |  | 
|  | 38 | #include "memdebug.h" /* keep this as LAST include */ | 
|  | 39 |  | 
|  | 40 | #ifdef NETWARE | 
|  | 41 | #  ifndef __NOVELL_LIBC__ | 
|  | 42 | #    define mkdir mkdir_510 | 
|  | 43 | #  endif | 
|  | 44 | #endif | 
|  | 45 |  | 
|  | 46 | #if defined(WIN32) || (defined(MSDOS) && !defined(__DJGPP__)) | 
|  | 47 | #  define mkdir(x,y) (mkdir)((x)) | 
|  | 48 | #  ifndef F_OK | 
|  | 49 | #    define F_OK 0 | 
|  | 50 | #  endif | 
|  | 51 | #endif | 
|  | 52 |  | 
|  | 53 | static void show_dir_errno(FILE *errors, const char *name) | 
|  | 54 | { | 
|  | 55 | switch(errno) { | 
|  | 56 | #ifdef EACCES | 
|  | 57 | case EACCES: | 
|  | 58 | fprintf(errors, "You don't have permission to create %s.\n", name); | 
|  | 59 | break; | 
|  | 60 | #endif | 
|  | 61 | #ifdef ENAMETOOLONG | 
|  | 62 | case ENAMETOOLONG: | 
|  | 63 | fprintf(errors, "The directory name %s is too long.\n", name); | 
|  | 64 | break; | 
|  | 65 | #endif | 
|  | 66 | #ifdef EROFS | 
|  | 67 | case EROFS: | 
|  | 68 | fprintf(errors, "%s resides on a read-only file system.\n", name); | 
|  | 69 | break; | 
|  | 70 | #endif | 
|  | 71 | #ifdef ENOSPC | 
|  | 72 | case ENOSPC: | 
|  | 73 | fprintf(errors, "No space left on the file system that will " | 
|  | 74 | "contain the directory %s.\n", name); | 
|  | 75 | break; | 
|  | 76 | #endif | 
|  | 77 | #ifdef EDQUOT | 
|  | 78 | case EDQUOT: | 
|  | 79 | fprintf(errors, "Cannot create directory %s because you " | 
|  | 80 | "exceeded your quota.\n", name); | 
|  | 81 | break; | 
|  | 82 | #endif | 
|  | 83 | default : | 
|  | 84 | fprintf(errors, "Error creating directory %s.\n", name); | 
|  | 85 | break; | 
|  | 86 | } | 
|  | 87 | } | 
|  | 88 |  | 
|  | 89 | /* | 
|  | 90 | * Create the needed directory hierarchy recursively in order to save | 
|  | 91 | *  multi-GETs in file output, ie: | 
|  | 92 | *  curl "http://my.site/dir[1-5]/file[1-5].txt" -o "dir#1/file#2.txt" | 
|  | 93 | *  should create all the dir* automagically | 
|  | 94 | */ | 
|  | 95 |  | 
|  | 96 | #if defined(WIN32) || defined(__DJGPP__) | 
|  | 97 | /* systems that may use either or when specifying a path */ | 
|  | 98 | #define PATH_DELIMITERS "\\/" | 
|  | 99 | #else | 
|  | 100 | #define PATH_DELIMITERS DIR_CHAR | 
|  | 101 | #endif | 
|  | 102 |  | 
|  | 103 |  | 
|  | 104 | CURLcode create_dir_hierarchy(const char *outfile, FILE *errors) | 
|  | 105 | { | 
|  | 106 | char *tempdir; | 
|  | 107 | char *tempdir2; | 
|  | 108 | char *outdup; | 
|  | 109 | char *dirbuildup; | 
|  | 110 | CURLcode result = CURLE_OK; | 
|  | 111 | size_t outlen; | 
|  | 112 |  | 
|  | 113 | outlen = strlen(outfile); | 
|  | 114 | outdup = strdup(outfile); | 
|  | 115 | if(!outdup) | 
|  | 116 | return CURLE_OUT_OF_MEMORY; | 
|  | 117 |  | 
|  | 118 | dirbuildup = malloc(outlen + 1); | 
|  | 119 | if(!dirbuildup) { | 
|  | 120 | Curl_safefree(outdup); | 
|  | 121 | return CURLE_OUT_OF_MEMORY; | 
|  | 122 | } | 
|  | 123 | dirbuildup[0] = '\0'; | 
|  | 124 |  | 
|  | 125 | /* Allow strtok() here since this isn't used threaded */ | 
|  | 126 | /* !checksrc! disable BANNEDFUNC 2 */ | 
|  | 127 | tempdir = strtok(outdup, PATH_DELIMITERS); | 
|  | 128 |  | 
|  | 129 | while(tempdir) { | 
|  | 130 | bool skip = false; | 
|  | 131 | tempdir2 = strtok(NULL, PATH_DELIMITERS); | 
|  | 132 | /* since strtok returns a token for the last word even | 
|  | 133 | if not ending with DIR_CHAR, we need to prune it */ | 
|  | 134 | if(tempdir2) { | 
|  | 135 | size_t dlen = strlen(dirbuildup); | 
|  | 136 | if(dlen) | 
|  | 137 | msnprintf(&dirbuildup[dlen], outlen - dlen, "%s%s", DIR_CHAR, tempdir); | 
|  | 138 | else { | 
|  | 139 | if(outdup == tempdir) { | 
|  | 140 | #if defined(MSDOS) || defined(WIN32) | 
|  | 141 | /* Skip creating a drive's current directory. | 
|  | 142 | It may seem as though that would harmlessly fail but it could be | 
|  | 143 | a corner case if X: did not exist, since we would be creating it | 
|  | 144 | erroneously. | 
|  | 145 | eg if outfile is X:\foo\bar\filename then don't mkdir X: | 
|  | 146 | This logic takes into account unsupported drives !:, 1:, etc. */ | 
|  | 147 | char *p = strchr(tempdir, ':'); | 
|  | 148 | if(p && !p[1]) | 
|  | 149 | skip = true; | 
|  | 150 | #endif | 
|  | 151 | /* the output string doesn't start with a separator */ | 
|  | 152 | strcpy(dirbuildup, tempdir); | 
|  | 153 | } | 
|  | 154 | else | 
|  | 155 | msnprintf(dirbuildup, outlen, "%s%s", DIR_CHAR, tempdir); | 
|  | 156 | } | 
|  | 157 | /* Create directory. Ignore access denied error to allow traversal. */ | 
|  | 158 | if(!skip && (-1 == mkdir(dirbuildup, (mode_t)0000750)) && | 
|  | 159 | (errno != EACCES) && (errno != EEXIST)) { | 
|  | 160 | show_dir_errno(errors, dirbuildup); | 
|  | 161 | result = CURLE_WRITE_ERROR; | 
|  | 162 | break; /* get out of loop */ | 
|  | 163 | } | 
|  | 164 | } | 
|  | 165 | tempdir = tempdir2; | 
|  | 166 | } | 
|  | 167 |  | 
|  | 168 | Curl_safefree(dirbuildup); | 
|  | 169 | Curl_safefree(outdup); | 
|  | 170 |  | 
|  | 171 | return result; | 
|  | 172 | } |