summaryrefslogtreecommitdiff
path: root/src/tool_dirhie.c
blob: 91cc1b60cbd5a84cfcc4099942c5e8a179cdf6a5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
/***************************************************************************
 *                                  _   _ ____  _
 *  Project                     ___| | | |  _ \| |
 *                             / __| | | | |_) | |
 *                            | (__| |_| |  _ <| |___
 *                             \___|\___/|_| \_\_____|
 *
 * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
 *
 * This software is licensed as described in the file COPYING, which
 * you should have received as part of this distribution. The terms
 * are also available at https://curl.se/docs/copyright.html.
 *
 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
 * copies of the Software, and permit persons to whom the Software is
 * furnished to do so, under the terms of the COPYING file.
 *
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
 * KIND, either express or implied.
 *
 * SPDX-License-Identifier: curl
 *
 ***************************************************************************/
#include "tool_setup.h"

#if defined(_WIN32) && !defined(UNDER_CE)
#  include <direct.h>
#endif

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

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

#if defined(_WIN32) || (defined(MSDOS) && !defined(__DJGPP__))
#  define mkdir(x,y) (mkdir)((x))
#  ifndef F_OK
#  define F_OK 0
#  endif
#endif

static void show_dir_errno(const char *name)
{
  switch(errno) {
#ifdef EACCES
  /* !checksrc! disable ERRNOVAR 1 */
  case EACCES:
    errorf("You do not have permission to create %s", name);
    break;
#endif
#ifdef ENAMETOOLONG
  case ENAMETOOLONG:
    errorf("The directory name %s is too long", name);
    break;
#endif
#ifdef EROFS
  case EROFS:
    errorf("%s resides on a read-only file system", name);
    break;
#endif
#ifdef ENOSPC
  case ENOSPC:
    errorf("No space left on the file system that will "
           "contain the directory %s", name);
    break;
#endif
#ifdef EDQUOT
  case EDQUOT:
    errorf("Cannot create directory %s because you "
           "exceeded your quota", name);
    break;
#endif
  default:
    errorf("Error creating directory %s", name);
    break;
  }
}

/*
 * Create the needed directory hierarchy recursively in order to save
 *  multi-GETs in file output, ie:
 *  curl "http://example.org/dir[1-5]/file[1-5].txt" -o "dir#1/file#2.txt"
 *  should create all the dir* automagically
 */

#if defined(_WIN32) || defined(__DJGPP__)
/* systems that may use either or when specifying a path */
#define PATH_DELIMITERS "\\/"
#else
#define PATH_DELIMITERS DIR_CHAR
#endif

CURLcode create_dir_hierarchy(const char *outfile)
{
  CURLcode result = CURLE_OK;
  size_t outlen = strlen(outfile);
  struct dynbuf dirbuf;

  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;

#if defined(_WIN32) || defined(MSDOS)
    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
    /* 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. */
    /* !checksrc! disable ERRNOVAR 1 */
    if(!skip && (mkdir(curlx_dyn_ptr(&dirbuf), (mode_t)0000750) == -1) &&
       (errno != EACCES) && (errno != EEXIST)) {
      show_dir_errno(curlx_dyn_ptr(&dirbuf));
      result = CURLE_WRITE_ERROR;
      break; /* get out of loop */
    }
    outfile += len + seplen;
  }

  curlx_dyn_free(&dirbuf);

  return result;
}