tool_dirhie.c (4315B)
1 /*************************************************************************** 2 * _ _ ____ _ 3 * Project ___| | | | _ \| | 4 * / __| | | | |_) | | 5 * | (__| |_| | _ <| |___ 6 * \___|\___/|_| \_\_____| 7 * 8 * Copyright (C) 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 #if defined(_WIN32) && !defined(UNDER_CE) 27 # include <direct.h> 28 #endif 29 30 #include "tool_dirhie.h" 31 #include "tool_msgs.h" 32 33 #include "memdebug.h" /* keep this as LAST include */ 34 35 #if defined(_WIN32) || (defined(MSDOS) && !defined(__DJGPP__)) 36 # define mkdir(x,y) (mkdir)((x)) 37 # ifndef F_OK 38 # define F_OK 0 39 # endif 40 #endif 41 42 static void show_dir_errno(struct GlobalConfig *global, const char *name) 43 { 44 switch(errno) { 45 #ifdef EACCES 46 /* !checksrc! disable ERRNOVAR 1 */ 47 case EACCES: 48 errorf(global, "You do not have permission to create %s", name); 49 break; 50 #endif 51 #ifdef ENAMETOOLONG 52 case ENAMETOOLONG: 53 errorf(global, "The directory name %s is too long", name); 54 break; 55 #endif 56 #ifdef EROFS 57 case EROFS: 58 errorf(global, "%s resides on a read-only file system", name); 59 break; 60 #endif 61 #ifdef ENOSPC 62 case ENOSPC: 63 errorf(global, "No space left on the file system that will " 64 "contain the directory %s", name); 65 break; 66 #endif 67 #ifdef EDQUOT 68 case EDQUOT: 69 errorf(global, "Cannot create directory %s because you " 70 "exceeded your quota", name); 71 break; 72 #endif 73 default: 74 errorf(global, "Error creating directory %s", name); 75 break; 76 } 77 } 78 79 /* 80 * Create the needed directory hierarchy recursively in order to save 81 * multi-GETs in file output, ie: 82 * curl "http://example.org/dir[1-5]/file[1-5].txt" -o "dir#1/file#2.txt" 83 * should create all the dir* automagically 84 */ 85 86 #if defined(_WIN32) || defined(__DJGPP__) 87 /* systems that may use either or when specifying a path */ 88 #define PATH_DELIMITERS "\\/" 89 #else 90 #define PATH_DELIMITERS DIR_CHAR 91 #endif 92 93 CURLcode create_dir_hierarchy(const char *outfile, struct GlobalConfig *global) 94 { 95 CURLcode result = CURLE_OK; 96 size_t outlen = strlen(outfile); 97 struct dynbuf dirbuf; 98 99 curlx_dyn_init(&dirbuf, outlen + 1); 100 101 while(*outfile) { 102 bool skip = FALSE; 103 size_t seplen = strspn(outfile, PATH_DELIMITERS); 104 size_t len = strcspn(&outfile[seplen], PATH_DELIMITERS); 105 106 /* the last path component is the file and it ends with a null byte */ 107 if(!outfile[len + seplen]) 108 break; 109 110 #if defined(_WIN32) || defined(MSDOS) 111 if(!curlx_dyn_len(&dirbuf)) { 112 /* Skip creating a drive's current directory. It may seem as though that 113 would harmlessly fail but it could be a corner case if X: did not 114 exist, since we would be creating it erroneously. eg if outfile is 115 X:\foo\bar\filename then do not mkdir X: This logic takes into 116 account unsupported drives !:, 1:, etc. */ 117 if(len > 1 && (outfile[1]==':')) 118 skip = TRUE; 119 } 120 #endif 121 /* insert the leading separators (possibly plural) plus the following 122 directory name */ 123 result = curlx_dyn_addn(&dirbuf, outfile, seplen + len); 124 if(result) 125 return result; 126 127 /* Create directory. Ignore access denied error to allow traversal. */ 128 /* !checksrc! disable ERRNOVAR 1 */ 129 if(!skip && (-1 == mkdir(curlx_dyn_ptr(&dirbuf), (mode_t)0000750)) && 130 (errno != EACCES) && (errno != EEXIST)) { 131 show_dir_errno(global, curlx_dyn_ptr(&dirbuf)); 132 result = CURLE_WRITE_ERROR; 133 break; /* get out of loop */ 134 } 135 outfile += len + seplen; 136 } 137 138 curlx_dyn_free(&dirbuf); 139 140 return result; 141 }