/***************************************************************************1* _ _ ____ _2* Project ___| | | | _ \| |3* / __| | | | |_) | |4* | (__| |_| | _ <| |___5* \___|\___/|_| \_\_____|6*7* Copyright (C) Daniel Stenberg, <[email protected]>, et al.8*9* This software is licensed as described in the file COPYING, which10* you should have received as part of this distribution. The terms11* are also available at https://curl.se/docs/copyright.html.12*13* You may opt to use, copy, modify, merge, publish, distribute and/or sell14* copies of the Software, and permit persons to whom the Software is15* furnished to do so, under the terms of the COPYING file.16*17* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY18* KIND, either express or implied.19*20* SPDX-License-Identifier: curl21*22***************************************************************************/23#include "tool_setup.h"2425#if defined(_WIN32) && !defined(UNDER_CE)26# include <direct.h>27#endif2829#include "tool_dirhie.h"30#include "tool_msgs.h"3132#include "memdebug.h" /* keep this as LAST include */3334#if defined(_WIN32) || (defined(MSDOS) && !defined(__DJGPP__))35# define mkdir(x,y) (mkdir)((x))36# ifndef F_OK37# define F_OK 038# endif39#endif4041static void show_dir_errno(const char *name)42{43switch(errno) {44#ifdef EACCES45/* !checksrc! disable ERRNOVAR 1 */46case EACCES:47errorf("You do not have permission to create %s", name);48break;49#endif50#ifdef ENAMETOOLONG51case ENAMETOOLONG:52errorf("The directory name %s is too long", name);53break;54#endif55#ifdef EROFS56case EROFS:57errorf("%s resides on a read-only file system", name);58break;59#endif60#ifdef ENOSPC61case ENOSPC:62errorf("No space left on the file system that will "63"contain the directory %s", name);64break;65#endif66#ifdef EDQUOT67case EDQUOT:68errorf("Cannot create directory %s because you "69"exceeded your quota", name);70break;71#endif72default:73errorf("Error creating directory %s", name);74break;75}76}7778/*79* Create the needed directory hierarchy recursively in order to save80* multi-GETs in file output, ie:81* curl "http://example.org/dir[1-5]/file[1-5].txt" -o "dir#1/file#2.txt"82* should create all the dir* automagically83*/8485#if defined(_WIN32) || defined(__DJGPP__)86/* systems that may use either or when specifying a path */87#define PATH_DELIMITERS "\\/"88#else89#define PATH_DELIMITERS DIR_CHAR90#endif9192CURLcode create_dir_hierarchy(const char *outfile)93{94CURLcode result = CURLE_OK;95size_t outlen = strlen(outfile);96struct dynbuf dirbuf;9798curlx_dyn_init(&dirbuf, outlen + 1);99100while(*outfile) {101bool skip = FALSE;102size_t seplen = strspn(outfile, PATH_DELIMITERS);103size_t len = strcspn(&outfile[seplen], PATH_DELIMITERS);104105/* the last path component is the file and it ends with a null byte */106if(!outfile[len + seplen])107break;108109#if defined(_WIN32) || defined(MSDOS)110if(!curlx_dyn_len(&dirbuf)) {111/* Skip creating a drive's current directory. It may seem as though that112would harmlessly fail but it could be a corner case if X: did not113exist, since we would be creating it erroneously. eg if outfile is114X:\foo\bar\filename then do not mkdir X: This logic takes into115account unsupported drives !:, 1:, etc. */116if(len > 1 && (outfile[1]==':'))117skip = TRUE;118}119#endif120/* insert the leading separators (possibly plural) plus the following121directory name */122result = curlx_dyn_addn(&dirbuf, outfile, seplen + len);123if(result)124return result;125126/* Create directory. Ignore access denied error to allow traversal. */127/* !checksrc! disable ERRNOVAR 1 */128if(!skip && (mkdir(curlx_dyn_ptr(&dirbuf), (mode_t)0000750) == -1) &&129(errno != EACCES) && (errno != EEXIST)) {130show_dir_errno(curlx_dyn_ptr(&dirbuf));131result = CURLE_WRITE_ERROR;132break; /* get out of loop */133}134outfile += len + seplen;135}136137curlx_dyn_free(&dirbuf);138139return result;140}141142143