1 | /***************************************************************************
|
---|
2 | * _ _ ____ _
|
---|
3 | * Project ___| | | | _ \| |
|
---|
4 | * / __| | | | |_) | |
|
---|
5 | * | (__| |_| | _ <| |___
|
---|
6 | * \___|\___/|_| \_\_____|
|
---|
7 | *
|
---|
8 | * Copyright (C) 1998 - 2016, 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.haxx.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 | ***************************************************************************/
|
---|
22 | #include "tool_setup.h"
|
---|
23 |
|
---|
24 | #if defined(MSDOS) || defined(WIN32)
|
---|
25 |
|
---|
26 | #if defined(HAVE_LIBGEN_H) && defined(HAVE_BASENAME)
|
---|
27 | # include <libgen.h>
|
---|
28 | #endif
|
---|
29 |
|
---|
30 | #ifdef WIN32
|
---|
31 | # include "tool_cfgable.h"
|
---|
32 | # include "tool_libinfo.h"
|
---|
33 | #endif
|
---|
34 |
|
---|
35 | #include "tool_bname.h"
|
---|
36 | #include "tool_doswin.h"
|
---|
37 |
|
---|
38 | #include "memdebug.h" /* keep this as LAST include */
|
---|
39 |
|
---|
40 | /*
|
---|
41 | * Macros ALWAYS_TRUE and ALWAYS_FALSE are used to avoid compiler warnings.
|
---|
42 | */
|
---|
43 |
|
---|
44 | #define ALWAYS_TRUE (1)
|
---|
45 | #define ALWAYS_FALSE (0)
|
---|
46 |
|
---|
47 | #if defined(_MSC_VER) && !defined(__POCC__)
|
---|
48 | # undef ALWAYS_TRUE
|
---|
49 | # undef ALWAYS_FALSE
|
---|
50 | # if (_MSC_VER < 1500)
|
---|
51 | # define ALWAYS_TRUE (0, 1)
|
---|
52 | # define ALWAYS_FALSE (1, 0)
|
---|
53 | # else
|
---|
54 | # define ALWAYS_TRUE \
|
---|
55 | __pragma(warning(push)) \
|
---|
56 | __pragma(warning(disable:4127)) \
|
---|
57 | (1) \
|
---|
58 | __pragma(warning(pop))
|
---|
59 | # define ALWAYS_FALSE \
|
---|
60 | __pragma(warning(push)) \
|
---|
61 | __pragma(warning(disable:4127)) \
|
---|
62 | (0) \
|
---|
63 | __pragma(warning(pop))
|
---|
64 | # endif
|
---|
65 | #endif
|
---|
66 |
|
---|
67 | #ifdef WIN32
|
---|
68 | # undef PATH_MAX
|
---|
69 | # define PATH_MAX MAX_PATH
|
---|
70 | #endif
|
---|
71 |
|
---|
72 | #ifndef S_ISCHR
|
---|
73 | # ifdef S_IFCHR
|
---|
74 | # define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR)
|
---|
75 | # else
|
---|
76 | # define S_ISCHR(m) (0) /* cannot tell if file is a device */
|
---|
77 | # endif
|
---|
78 | #endif
|
---|
79 |
|
---|
80 | #ifdef WIN32
|
---|
81 | # define _use_lfn(f) ALWAYS_TRUE /* long file names always available */
|
---|
82 | #elif !defined(__DJGPP__) || (__DJGPP__ < 2) /* DJGPP 2.0 has _use_lfn() */
|
---|
83 | # define _use_lfn(f) ALWAYS_FALSE /* long file names never available */
|
---|
84 | #elif defined(__DJGPP__)
|
---|
85 | # include <fcntl.h> /* _use_lfn(f) prototype */
|
---|
86 | #endif
|
---|
87 |
|
---|
88 | #ifndef UNITTESTS
|
---|
89 | static SANITIZEcode truncate_dryrun(const char *path,
|
---|
90 | const size_t truncate_pos);
|
---|
91 | #ifdef MSDOS
|
---|
92 | static SANITIZEcode msdosify(char **const sanitized, const char *file_name,
|
---|
93 | int flags);
|
---|
94 | #endif
|
---|
95 | static SANITIZEcode rename_if_reserved_dos_device_name(char **const sanitized,
|
---|
96 | const char *file_name,
|
---|
97 | int flags);
|
---|
98 | #endif /* !UNITTESTS (static declarations used if no unit tests) */
|
---|
99 |
|
---|
100 |
|
---|
101 | /*
|
---|
102 | Sanitize a file or path name.
|
---|
103 |
|
---|
104 | All banned characters are replaced by underscores, for example:
|
---|
105 | f?*foo => f__foo
|
---|
106 | f:foo::$DATA => f_foo__$DATA
|
---|
107 | f:\foo:bar => f__foo_bar
|
---|
108 | f:\foo:bar => f:\foo:bar (flag SANITIZE_ALLOW_PATH)
|
---|
109 |
|
---|
110 | This function was implemented according to the guidelines in 'Naming Files,
|
---|
111 | Paths, and Namespaces' section 'Naming Conventions'.
|
---|
112 | https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247.aspx
|
---|
113 |
|
---|
114 | Flags
|
---|
115 | -----
|
---|
116 | SANITIZE_ALLOW_COLONS: Allow colons.
|
---|
117 | Without this flag colons are sanitized.
|
---|
118 |
|
---|
119 | SANITIZE_ALLOW_PATH: Allow path separators and colons.
|
---|
120 | Without this flag path separators and colons are sanitized.
|
---|
121 |
|
---|
122 | SANITIZE_ALLOW_RESERVED: Allow reserved device names.
|
---|
123 | Without this flag a reserved device name is renamed (COM1 => _COM1) unless it's
|
---|
124 | in a UNC prefixed path.
|
---|
125 |
|
---|
126 | SANITIZE_ALLOW_TRUNCATE: Allow truncating a long filename.
|
---|
127 | Without this flag if the sanitized filename or path will be too long an error
|
---|
128 | occurs. With this flag the filename --and not any other parts of the path-- may
|
---|
129 | be truncated to at least a single character. A filename followed by an
|
---|
130 | alternate data stream (ADS) cannot be truncated in any case.
|
---|
131 |
|
---|
132 | Success: (SANITIZE_ERR_OK) *sanitized points to a sanitized copy of file_name.
|
---|
133 | Failure: (!= SANITIZE_ERR_OK) *sanitized is NULL.
|
---|
134 | */
|
---|
135 | SANITIZEcode sanitize_file_name(char **const sanitized, const char *file_name,
|
---|
136 | int flags)
|
---|
137 | {
|
---|
138 | char *p, *target;
|
---|
139 | size_t len;
|
---|
140 | SANITIZEcode sc;
|
---|
141 | size_t max_sanitized_len;
|
---|
142 |
|
---|
143 | if(!sanitized)
|
---|
144 | return SANITIZE_ERR_BAD_ARGUMENT;
|
---|
145 |
|
---|
146 | *sanitized = NULL;
|
---|
147 |
|
---|
148 | if(!file_name)
|
---|
149 | return SANITIZE_ERR_BAD_ARGUMENT;
|
---|
150 |
|
---|
151 | if((flags & SANITIZE_ALLOW_PATH)) {
|
---|
152 | #ifndef MSDOS
|
---|
153 | if(file_name[0] == '\\' && file_name[1] == '\\')
|
---|
154 | /* UNC prefixed path \\ (eg \\?\C:\foo) */
|
---|
155 | max_sanitized_len = 32767-1;
|
---|
156 | else
|
---|
157 | #endif
|
---|
158 | max_sanitized_len = PATH_MAX-1;
|
---|
159 | }
|
---|
160 | else
|
---|
161 | /* The maximum length of a filename.
|
---|
162 | FILENAME_MAX is often the same as PATH_MAX, in other words it is 260 and
|
---|
163 | does not discount the path information therefore we shouldn't use it. */
|
---|
164 | max_sanitized_len = (PATH_MAX-1 > 255) ? 255 : PATH_MAX-1;
|
---|
165 |
|
---|
166 | len = strlen(file_name);
|
---|
167 | if(len > max_sanitized_len) {
|
---|
168 | if(!(flags & SANITIZE_ALLOW_TRUNCATE) ||
|
---|
169 | truncate_dryrun(file_name, max_sanitized_len))
|
---|
170 | return SANITIZE_ERR_INVALID_PATH;
|
---|
171 |
|
---|
172 | len = max_sanitized_len;
|
---|
173 | }
|
---|
174 |
|
---|
175 | target = malloc(len + 1);
|
---|
176 | if(!target)
|
---|
177 | return SANITIZE_ERR_OUT_OF_MEMORY;
|
---|
178 |
|
---|
179 | strncpy(target, file_name, len);
|
---|
180 | target[len] = '\0';
|
---|
181 |
|
---|
182 | #ifndef MSDOS
|
---|
183 | if((flags & SANITIZE_ALLOW_PATH) && !strncmp(target, "\\\\?\\", 4))
|
---|
184 | /* Skip the literal path prefix \\?\ */
|
---|
185 | p = target + 4;
|
---|
186 | else
|
---|
187 | #endif
|
---|
188 | p = target;
|
---|
189 |
|
---|
190 | /* replace control characters and other banned characters */
|
---|
191 | for(; *p; ++p) {
|
---|
192 | const char *banned;
|
---|
193 |
|
---|
194 | if((1 <= *p && *p <= 31) ||
|
---|
195 | (!(flags & (SANITIZE_ALLOW_COLONS|SANITIZE_ALLOW_PATH)) && *p == ':') ||
|
---|
196 | (!(flags & SANITIZE_ALLOW_PATH) && (*p == '/' || *p == '\\'))) {
|
---|
197 | *p = '_';
|
---|
198 | continue;
|
---|
199 | }
|
---|
200 |
|
---|
201 | for(banned = "|<>\"?*"; *banned; ++banned) {
|
---|
202 | if(*p == *banned) {
|
---|
203 | *p = '_';
|
---|
204 | break;
|
---|
205 | }
|
---|
206 | }
|
---|
207 | }
|
---|
208 |
|
---|
209 | /* remove trailing spaces and periods if not allowing paths */
|
---|
210 | if(!(flags & SANITIZE_ALLOW_PATH) && len) {
|
---|
211 | char *clip = NULL;
|
---|
212 |
|
---|
213 | p = &target[len];
|
---|
214 | do {
|
---|
215 | --p;
|
---|
216 | if(*p != ' ' && *p != '.')
|
---|
217 | break;
|
---|
218 | clip = p;
|
---|
219 | } while(p != target);
|
---|
220 |
|
---|
221 | if(clip) {
|
---|
222 | *clip = '\0';
|
---|
223 | len = clip - target;
|
---|
224 | }
|
---|
225 | }
|
---|
226 |
|
---|
227 | #ifdef MSDOS
|
---|
228 | sc = msdosify(&p, target, flags);
|
---|
229 | free(target);
|
---|
230 | if(sc)
|
---|
231 | return sc;
|
---|
232 | target = p;
|
---|
233 | len = strlen(target);
|
---|
234 |
|
---|
235 | if(len > max_sanitized_len) {
|
---|
236 | free(target);
|
---|
237 | return SANITIZE_ERR_INVALID_PATH;
|
---|
238 | }
|
---|
239 | #endif
|
---|
240 |
|
---|
241 | if(!(flags & SANITIZE_ALLOW_RESERVED)) {
|
---|
242 | sc = rename_if_reserved_dos_device_name(&p, target, flags);
|
---|
243 | free(target);
|
---|
244 | if(sc)
|
---|
245 | return sc;
|
---|
246 | target = p;
|
---|
247 | len = strlen(target);
|
---|
248 |
|
---|
249 | if(len > max_sanitized_len) {
|
---|
250 | free(target);
|
---|
251 | return SANITIZE_ERR_INVALID_PATH;
|
---|
252 | }
|
---|
253 | }
|
---|
254 |
|
---|
255 | *sanitized = target;
|
---|
256 | return SANITIZE_ERR_OK;
|
---|
257 | }
|
---|
258 |
|
---|
259 |
|
---|
260 | /*
|
---|
261 | Test if truncating a path to a file will leave at least a single character in
|
---|
262 | the filename. Filenames suffixed by an alternate data stream can't be
|
---|
263 | truncated. This performs a dry run, nothing is modified.
|
---|
264 |
|
---|
265 | Good truncate_pos 9: C:\foo\bar => C:\foo\ba
|
---|
266 | Good truncate_pos 6: C:\foo => C:\foo
|
---|
267 | Good truncate_pos 5: C:\foo => C:\fo
|
---|
268 | Bad* truncate_pos 5: C:foo => C:foo
|
---|
269 | Bad truncate_pos 5: C:\foo:ads => C:\fo
|
---|
270 | Bad truncate_pos 9: C:\foo:ads => C:\foo:ad
|
---|
271 | Bad truncate_pos 5: C:\foo\bar => C:\fo
|
---|
272 | Bad truncate_pos 5: C:\foo\ => C:\fo
|
---|
273 | Bad truncate_pos 7: C:\foo\ => C:\foo\
|
---|
274 | Error truncate_pos 7: C:\foo => (pos out of range)
|
---|
275 | Bad truncate_pos 1: C:\foo\ => C
|
---|
276 |
|
---|
277 | * C:foo is ambiguous, C could end up being a drive or file therefore something
|
---|
278 | like C:superlongfilename can't be truncated.
|
---|
279 |
|
---|
280 | Returns
|
---|
281 | SANITIZE_ERR_OK: Good -- 'path' can be truncated
|
---|
282 | SANITIZE_ERR_INVALID_PATH: Bad -- 'path' cannot be truncated
|
---|
283 | != SANITIZE_ERR_OK && != SANITIZE_ERR_INVALID_PATH: Error
|
---|
284 | */
|
---|
285 | SANITIZEcode truncate_dryrun(const char *path, const size_t truncate_pos)
|
---|
286 | {
|
---|
287 | size_t len;
|
---|
288 |
|
---|
289 | if(!path)
|
---|
290 | return SANITIZE_ERR_BAD_ARGUMENT;
|
---|
291 |
|
---|
292 | len = strlen(path);
|
---|
293 |
|
---|
294 | if(truncate_pos > len)
|
---|
295 | return SANITIZE_ERR_BAD_ARGUMENT;
|
---|
296 |
|
---|
297 | if(!len || !truncate_pos)
|
---|
298 | return SANITIZE_ERR_INVALID_PATH;
|
---|
299 |
|
---|
300 | if(strpbrk(&path[truncate_pos - 1], "\\/:"))
|
---|
301 | return SANITIZE_ERR_INVALID_PATH;
|
---|
302 |
|
---|
303 | /* C:\foo can be truncated but C:\foo:ads can't */
|
---|
304 | if(truncate_pos > 1) {
|
---|
305 | const char *p = &path[truncate_pos - 1];
|
---|
306 | do {
|
---|
307 | --p;
|
---|
308 | if(*p == ':')
|
---|
309 | return SANITIZE_ERR_INVALID_PATH;
|
---|
310 | } while(p != path && *p != '\\' && *p != '/');
|
---|
311 | }
|
---|
312 |
|
---|
313 | return SANITIZE_ERR_OK;
|
---|
314 | }
|
---|
315 |
|
---|
316 | /* The functions msdosify, rename_if_dos_device_name and __crt0_glob_function
|
---|
317 | * were taken with modification from the DJGPP port of tar 1.12. They use
|
---|
318 | * algorithms originally from DJTAR.
|
---|
319 | */
|
---|
320 |
|
---|
321 | /*
|
---|
322 | Extra sanitization MSDOS for file_name.
|
---|
323 |
|
---|
324 | This is a supporting function for sanitize_file_name.
|
---|
325 |
|
---|
326 | Warning: This is an MSDOS legacy function and was purposely written in a way
|
---|
327 | that some path information may pass through. For example drive letter names
|
---|
328 | (C:, D:, etc) are allowed to pass through. For sanitizing a filename use
|
---|
329 | sanitize_file_name.
|
---|
330 |
|
---|
331 | Success: (SANITIZE_ERR_OK) *sanitized points to a sanitized copy of file_name.
|
---|
332 | Failure: (!= SANITIZE_ERR_OK) *sanitized is NULL.
|
---|
333 | */
|
---|
334 | #if defined(MSDOS) || defined(UNITTESTS)
|
---|
335 | SANITIZEcode msdosify(char **const sanitized, const char *file_name,
|
---|
336 | int flags)
|
---|
337 | {
|
---|
338 | char dos_name[PATH_MAX];
|
---|
339 | static const char illegal_chars_dos[] = ".+, ;=[]" /* illegal in DOS */
|
---|
340 | "|<>/\\\":?*"; /* illegal in DOS & W95 */
|
---|
341 | static const char *illegal_chars_w95 = &illegal_chars_dos[8];
|
---|
342 | int idx, dot_idx;
|
---|
343 | const char *s = file_name;
|
---|
344 | char *d = dos_name;
|
---|
345 | const char *const dlimit = dos_name + sizeof(dos_name) - 1;
|
---|
346 | const char *illegal_aliens = illegal_chars_dos;
|
---|
347 | size_t len = sizeof(illegal_chars_dos) - 1;
|
---|
348 |
|
---|
349 | if(!sanitized)
|
---|
350 | return SANITIZE_ERR_BAD_ARGUMENT;
|
---|
351 |
|
---|
352 | *sanitized = NULL;
|
---|
353 |
|
---|
354 | if(!file_name)
|
---|
355 | return SANITIZE_ERR_BAD_ARGUMENT;
|
---|
356 |
|
---|
357 | if(strlen(file_name) > PATH_MAX-1 &&
|
---|
358 | (!(flags & SANITIZE_ALLOW_TRUNCATE) ||
|
---|
359 | truncate_dryrun(file_name, PATH_MAX-1)))
|
---|
360 | return SANITIZE_ERR_INVALID_PATH;
|
---|
361 |
|
---|
362 | /* Support for Windows 9X VFAT systems, when available. */
|
---|
363 | if(_use_lfn(file_name)) {
|
---|
364 | illegal_aliens = illegal_chars_w95;
|
---|
365 | len -= (illegal_chars_w95 - illegal_chars_dos);
|
---|
366 | }
|
---|
367 |
|
---|
368 | /* Get past the drive letter, if any. */
|
---|
369 | if(s[0] >= 'A' && s[0] <= 'z' && s[1] == ':') {
|
---|
370 | *d++ = *s++;
|
---|
371 | *d = ((flags & (SANITIZE_ALLOW_COLONS|SANITIZE_ALLOW_PATH))) ? ':' : '_';
|
---|
372 | ++d, ++s;
|
---|
373 | }
|
---|
374 |
|
---|
375 | for(idx = 0, dot_idx = -1; *s && d < dlimit; s++, d++) {
|
---|
376 | if(memchr(illegal_aliens, *s, len)) {
|
---|
377 |
|
---|
378 | if((flags & (SANITIZE_ALLOW_COLONS|SANITIZE_ALLOW_PATH)) && *s == ':')
|
---|
379 | *d = ':';
|
---|
380 | else if((flags & SANITIZE_ALLOW_PATH) && (*s == '/' || *s == '\\'))
|
---|
381 | *d = *s;
|
---|
382 | /* Dots are special: DOS doesn't allow them as the leading character,
|
---|
383 | and a file name cannot have more than a single dot. We leave the
|
---|
384 | first non-leading dot alone, unless it comes too close to the
|
---|
385 | beginning of the name: we want sh.lex.c to become sh_lex.c, not
|
---|
386 | sh.lex-c. */
|
---|
387 | else if(*s == '.') {
|
---|
388 | if((flags & SANITIZE_ALLOW_PATH) && idx == 0 &&
|
---|
389 | (s[1] == '/' || s[1] == '\\' ||
|
---|
390 | (s[1] == '.' && (s[2] == '/' || s[2] == '\\')))) {
|
---|
391 | /* Copy "./" and "../" verbatim. */
|
---|
392 | *d++ = *s++;
|
---|
393 | if(d == dlimit)
|
---|
394 | break;
|
---|
395 | if(*s == '.') {
|
---|
396 | *d++ = *s++;
|
---|
397 | if(d == dlimit)
|
---|
398 | break;
|
---|
399 | }
|
---|
400 | *d = *s;
|
---|
401 | }
|
---|
402 | else if(idx == 0)
|
---|
403 | *d = '_';
|
---|
404 | else if(dot_idx >= 0) {
|
---|
405 | if(dot_idx < 5) { /* 5 is a heuristic ad-hoc'ery */
|
---|
406 | d[dot_idx - idx] = '_'; /* replace previous dot */
|
---|
407 | *d = '.';
|
---|
408 | }
|
---|
409 | else
|
---|
410 | *d = '-';
|
---|
411 | }
|
---|
412 | else
|
---|
413 | *d = '.';
|
---|
414 |
|
---|
415 | if(*s == '.')
|
---|
416 | dot_idx = idx;
|
---|
417 | }
|
---|
418 | else if(*s == '+' && s[1] == '+') {
|
---|
419 | if(idx - 2 == dot_idx) { /* .c++, .h++ etc. */
|
---|
420 | *d++ = 'x';
|
---|
421 | if(d == dlimit)
|
---|
422 | break;
|
---|
423 | *d = 'x';
|
---|
424 | }
|
---|
425 | else {
|
---|
426 | /* libg++ etc. */
|
---|
427 | if(dlimit - d < 4) {
|
---|
428 | *d++ = 'x';
|
---|
429 | if(d == dlimit)
|
---|
430 | break;
|
---|
431 | *d = 'x';
|
---|
432 | }
|
---|
433 | else {
|
---|
434 | memcpy(d, "plus", 4);
|
---|
435 | d += 3;
|
---|
436 | }
|
---|
437 | }
|
---|
438 | s++;
|
---|
439 | idx++;
|
---|
440 | }
|
---|
441 | else
|
---|
442 | *d = '_';
|
---|
443 | }
|
---|
444 | else
|
---|
445 | *d = *s;
|
---|
446 | if(*s == '/' || *s == '\\') {
|
---|
447 | idx = 0;
|
---|
448 | dot_idx = -1;
|
---|
449 | }
|
---|
450 | else
|
---|
451 | idx++;
|
---|
452 | }
|
---|
453 | *d = '\0';
|
---|
454 |
|
---|
455 | if(*s) {
|
---|
456 | /* dos_name is truncated, check that truncation requirements are met,
|
---|
457 | specifically truncating a filename suffixed by an alternate data stream
|
---|
458 | or truncating the entire filename is not allowed. */
|
---|
459 | if(!(flags & SANITIZE_ALLOW_TRUNCATE) || strpbrk(s, "\\/:") ||
|
---|
460 | truncate_dryrun(dos_name, d - dos_name))
|
---|
461 | return SANITIZE_ERR_INVALID_PATH;
|
---|
462 | }
|
---|
463 |
|
---|
464 | *sanitized = strdup(dos_name);
|
---|
465 | return (*sanitized ? SANITIZE_ERR_OK : SANITIZE_ERR_OUT_OF_MEMORY);
|
---|
466 | }
|
---|
467 | #endif /* MSDOS || UNITTESTS */
|
---|
468 |
|
---|
469 | /*
|
---|
470 | Rename file_name if it's a reserved dos device name.
|
---|
471 |
|
---|
472 | This is a supporting function for sanitize_file_name.
|
---|
473 |
|
---|
474 | Warning: This is an MSDOS legacy function and was purposely written in a way
|
---|
475 | that some path information may pass through. For example drive letter names
|
---|
476 | (C:, D:, etc) are allowed to pass through. For sanitizing a filename use
|
---|
477 | sanitize_file_name.
|
---|
478 |
|
---|
479 | Success: (SANITIZE_ERR_OK) *sanitized points to a sanitized copy of file_name.
|
---|
480 | Failure: (!= SANITIZE_ERR_OK) *sanitized is NULL.
|
---|
481 | */
|
---|
482 | SANITIZEcode rename_if_reserved_dos_device_name(char **const sanitized,
|
---|
483 | const char *file_name,
|
---|
484 | int flags)
|
---|
485 | {
|
---|
486 | /* We could have a file whose name is a device on MS-DOS. Trying to
|
---|
487 | * retrieve such a file would fail at best and wedge us at worst. We need
|
---|
488 | * to rename such files. */
|
---|
489 | char *p, *base;
|
---|
490 | char fname[PATH_MAX];
|
---|
491 | #ifdef MSDOS
|
---|
492 | struct_stat st_buf;
|
---|
493 | #endif
|
---|
494 |
|
---|
495 | if(!sanitized)
|
---|
496 | return SANITIZE_ERR_BAD_ARGUMENT;
|
---|
497 |
|
---|
498 | *sanitized = NULL;
|
---|
499 |
|
---|
500 | if(!file_name)
|
---|
501 | return SANITIZE_ERR_BAD_ARGUMENT;
|
---|
502 |
|
---|
503 | /* Ignore UNC prefixed paths, they are allowed to contain a reserved name. */
|
---|
504 | #ifndef MSDOS
|
---|
505 | if((flags & SANITIZE_ALLOW_PATH) &&
|
---|
506 | file_name[0] == '\\' && file_name[1] == '\\') {
|
---|
507 | size_t len = strlen(file_name);
|
---|
508 | *sanitized = malloc(len + 1);
|
---|
509 | if(!*sanitized)
|
---|
510 | return SANITIZE_ERR_OUT_OF_MEMORY;
|
---|
511 | strncpy(*sanitized, file_name, len + 1);
|
---|
512 | return SANITIZE_ERR_OK;
|
---|
513 | }
|
---|
514 | #endif
|
---|
515 |
|
---|
516 | if(strlen(file_name) > PATH_MAX-1 &&
|
---|
517 | (!(flags & SANITIZE_ALLOW_TRUNCATE) ||
|
---|
518 | truncate_dryrun(file_name, PATH_MAX-1)))
|
---|
519 | return SANITIZE_ERR_INVALID_PATH;
|
---|
520 |
|
---|
521 | strncpy(fname, file_name, PATH_MAX-1);
|
---|
522 | fname[PATH_MAX-1] = '\0';
|
---|
523 | base = basename(fname);
|
---|
524 |
|
---|
525 | /* Rename reserved device names that are known to be accessible without \\.\
|
---|
526 | Examples: CON => _CON, CON.EXT => CON_EXT, CON:ADS => CON_ADS
|
---|
527 | https://support.microsoft.com/en-us/kb/74496
|
---|
528 | https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247.aspx
|
---|
529 | */
|
---|
530 | for(p = fname; p; p = (p == fname && fname != base ? base : NULL)) {
|
---|
531 | size_t p_len;
|
---|
532 | int x = (curl_strnequal(p, "CON", 3) ||
|
---|
533 | curl_strnequal(p, "PRN", 3) ||
|
---|
534 | curl_strnequal(p, "AUX", 3) ||
|
---|
535 | curl_strnequal(p, "NUL", 3)) ? 3 :
|
---|
536 | (curl_strnequal(p, "CLOCK$", 6)) ? 6 :
|
---|
537 | (curl_strnequal(p, "COM", 3) || curl_strnequal(p, "LPT", 3)) ?
|
---|
538 | (('1' <= p[3] && p[3] <= '9') ? 4 : 3) : 0;
|
---|
539 |
|
---|
540 | if(!x)
|
---|
541 | continue;
|
---|
542 |
|
---|
543 | /* the devices may be accessible with an extension or ADS, for
|
---|
544 | example CON.AIR and 'CON . AIR' and CON:AIR access console */
|
---|
545 |
|
---|
546 | for(; p[x] == ' '; ++x)
|
---|
547 | ;
|
---|
548 |
|
---|
549 | if(p[x] == '.') {
|
---|
550 | p[x] = '_';
|
---|
551 | continue;
|
---|
552 | }
|
---|
553 | else if(p[x] == ':') {
|
---|
554 | if(!(flags & (SANITIZE_ALLOW_COLONS|SANITIZE_ALLOW_PATH))) {
|
---|
555 | p[x] = '_';
|
---|
556 | continue;
|
---|
557 | }
|
---|
558 | ++x;
|
---|
559 | }
|
---|
560 | else if(p[x]) /* no match */
|
---|
561 | continue;
|
---|
562 |
|
---|
563 | /* p points to 'CON' or 'CON ' or 'CON:', etc */
|
---|
564 | p_len = strlen(p);
|
---|
565 |
|
---|
566 | /* Prepend a '_' */
|
---|
567 | if(strlen(fname) == PATH_MAX-1) {
|
---|
568 | --p_len;
|
---|
569 | if(!(flags & SANITIZE_ALLOW_TRUNCATE) || truncate_dryrun(p, p_len))
|
---|
570 | return SANITIZE_ERR_INVALID_PATH;
|
---|
571 | p[p_len] = '\0';
|
---|
572 | }
|
---|
573 | memmove(p + 1, p, p_len + 1);
|
---|
574 | p[0] = '_';
|
---|
575 | ++p_len;
|
---|
576 |
|
---|
577 | /* if fname was just modified then the basename pointer must be updated */
|
---|
578 | if(p == fname)
|
---|
579 | base = basename(fname);
|
---|
580 | }
|
---|
581 |
|
---|
582 | /* This is the legacy portion from rename_if_dos_device_name that checks for
|
---|
583 | reserved device names. It only works on MSDOS. On Windows XP the stat
|
---|
584 | check errors with EINVAL if the device name is reserved. On Windows
|
---|
585 | Vista/7/8 it sets mode S_IFREG (regular file or device). According to MSDN
|
---|
586 | stat doc the latter behavior is correct, but that doesn't help us identify
|
---|
587 | whether it's a reserved device name and not a regular file name. */
|
---|
588 | #ifdef MSDOS
|
---|
589 | if(base && ((stat(base, &st_buf)) == 0) && (S_ISCHR(st_buf.st_mode))) {
|
---|
590 | /* Prepend a '_' */
|
---|
591 | size_t blen = strlen(base);
|
---|
592 | if(blen) {
|
---|
593 | if(strlen(fname) == PATH_MAX-1) {
|
---|
594 | --blen;
|
---|
595 | if(!(flags & SANITIZE_ALLOW_TRUNCATE) || truncate_dryrun(base, blen))
|
---|
596 | return SANITIZE_ERR_INVALID_PATH;
|
---|
597 | base[blen] = '\0';
|
---|
598 | }
|
---|
599 | memmove(base + 1, base, blen + 1);
|
---|
600 | base[0] = '_';
|
---|
601 | ++blen;
|
---|
602 | }
|
---|
603 | }
|
---|
604 | #endif
|
---|
605 |
|
---|
606 | *sanitized = strdup(fname);
|
---|
607 | return (*sanitized ? SANITIZE_ERR_OK : SANITIZE_ERR_OUT_OF_MEMORY);
|
---|
608 | }
|
---|
609 |
|
---|
610 | #if defined(MSDOS) && (defined(__DJGPP__) || defined(__GO32__))
|
---|
611 |
|
---|
612 | /*
|
---|
613 | * Disable program default argument globbing. We do it on our own.
|
---|
614 | */
|
---|
615 | char **__crt0_glob_function(char *arg)
|
---|
616 | {
|
---|
617 | (void)arg;
|
---|
618 | return (char **)0;
|
---|
619 | }
|
---|
620 |
|
---|
621 | #endif /* MSDOS && (__DJGPP__ || __GO32__) */
|
---|
622 |
|
---|
623 | #ifdef WIN32
|
---|
624 |
|
---|
625 | /*
|
---|
626 | * Function to find CACert bundle on a Win32 platform using SearchPath.
|
---|
627 | * (SearchPath is already declared via inclusions done in setup header file)
|
---|
628 | * (Use the ASCII version instead of the unicode one!)
|
---|
629 | * The order of the directories it searches is:
|
---|
630 | * 1. application's directory
|
---|
631 | * 2. current working directory
|
---|
632 | * 3. Windows System directory (e.g. C:\windows\system32)
|
---|
633 | * 4. Windows Directory (e.g. C:\windows)
|
---|
634 | * 5. all directories along %PATH%
|
---|
635 | *
|
---|
636 | * For WinXP and later search order actually depends on registry value:
|
---|
637 | * HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\SafeProcessSearchMode
|
---|
638 | */
|
---|
639 |
|
---|
640 | CURLcode FindWin32CACert(struct OperationConfig *config,
|
---|
641 | const char *bundle_file)
|
---|
642 | {
|
---|
643 | CURLcode result = CURLE_OK;
|
---|
644 |
|
---|
645 | /* search and set cert file only if libcurl supports SSL */
|
---|
646 | if(curlinfo->features & CURL_VERSION_SSL) {
|
---|
647 |
|
---|
648 | DWORD res_len;
|
---|
649 | char buf[PATH_MAX];
|
---|
650 | char *ptr = NULL;
|
---|
651 |
|
---|
652 | buf[0] = '\0';
|
---|
653 |
|
---|
654 | res_len = SearchPathA(NULL, bundle_file, NULL, PATH_MAX, buf, &ptr);
|
---|
655 | if(res_len > 0) {
|
---|
656 | Curl_safefree(config->cacert);
|
---|
657 | config->cacert = strdup(buf);
|
---|
658 | if(!config->cacert)
|
---|
659 | result = CURLE_OUT_OF_MEMORY;
|
---|
660 | }
|
---|
661 | }
|
---|
662 |
|
---|
663 | return result;
|
---|
664 | }
|
---|
665 |
|
---|
666 | #endif /* WIN32 */
|
---|
667 |
|
---|
668 | #endif /* MSDOS || WIN32 */
|
---|