Without this flag path separators and colons are sanitized.
SANITIZE_ALLOW_RESERVED: Allow reserved device names.
-Without this flag a reserved device name is renamed (COM1 => _COM1) unless it
-is in a UNC prefixed path.
+Without this flag a reserved device name is renamed (COM1 => _COM1).
+
+To fully block reserved device names requires not passing either flag. Some
+less common path styles are allowed to use reserved device names. For example,
+a "\\" prefixed path may use reserved device names if paths are allowed.
Success: (SANITIZE_ERR_OK) *sanitized points to a sanitized copy of file_name.
Failure: (!= SANITIZE_ERR_OK) *sanitized is NULL.
if(!file_name)
return SANITIZE_ERR_BAD_ARGUMENT;
- if(flags & SANITIZE_ALLOW_PATH) {
-#ifndef MSDOS
- if(file_name[0] == '\\' && file_name[1] == '\\')
- /* UNC prefixed path \\ (eg \\?\C:\foo) */
- max_sanitized_len = 32767 - 1;
- else
+ if(flags & SANITIZE_ALLOW_PATH)
+#ifdef MSDOS
+ max_sanitized_len = PATH_MAX - 1;
+#else
+ /* Windows extended-length path max */
+ max_sanitized_len = 32767 - 1;
#endif
- max_sanitized_len = PATH_MAX - 1;
- }
else
/* The maximum length of a filename. FILENAME_MAX is often the same as
PATH_MAX, in other words it is 260 and does not discount the path
#ifndef MSDOS
if((flags & SANITIZE_ALLOW_PATH) && !strncmp(target, "\\\\?\\", 4))
- /* Skip the literal path prefix \\?\ */
+ /* Skip the literal-path prefix \\?\ */
p = target + 4;
else
#endif
/* We could have a file whose name is a device on MS-DOS. Trying to
* retrieve such a file would fail at best and wedge us at worst. We need
* to rename such files. */
- char *p, *base;
- char fname[PATH_MAX];
+ char *p, *base, *buffer;
#ifdef MSDOS
struct_stat st_buf;
#endif
- size_t len;
+ size_t len, bufsize;
if(!sanitized || !file_name)
return SANITIZE_ERR_BAD_ARGUMENT;
*sanitized = NULL;
- len = strlen(file_name);
- /* Ignore UNC prefixed paths, they are allowed to contain a reserved name. */
+ /* Ignore "\\" prefixed paths, they are allowed to use reserved names. */
#ifndef MSDOS
if((flags & SANITIZE_ALLOW_PATH) &&
file_name[0] == '\\' && file_name[1] == '\\') {
}
#endif
- if(len > PATH_MAX - 1)
- return SANITIZE_ERR_INVALID_PATH;
+ /* The buffer contains two extra bytes to allow for path expansion that
+ occurs if reserved name(s) need an underscore prepended. */
+ len = strlen(file_name);
+ bufsize = len + 2 + 1;
+
+ buffer = curlx_malloc(bufsize);
+ if(!buffer)
+ return SANITIZE_ERR_OUT_OF_MEMORY;
+
+ memcpy(buffer, file_name, len + 1);
- memcpy(fname, file_name, len);
- fname[len] = '\0';
- base = basename(fname);
+ base = basename(buffer);
/* Rename reserved device names that are known to be accessible without \\.\
Examples: CON => _CON, CON.EXT => CON_EXT, CON:ADS => CON_ADS
https://web.archive.org/web/20160314141551/support.microsoft.com/en-us/kb/74496
https://learn.microsoft.com/windows/win32/fileio/naming-a-file
*/
- for(p = fname; p; p = (p == fname && fname != base ? base : NULL)) {
+ for(p = buffer; p; p = (p == buffer && buffer != base ? base : NULL)) {
size_t p_len;
int x = (curl_strnequal(p, "CON", 3) ||
curl_strnequal(p, "PRN", 3) ||
p_len = strlen(p);
/* Prepend a '_' */
- if(strlen(fname) == PATH_MAX - 1)
+ if(len == bufsize - 1) {
+ curlx_free(buffer);
return SANITIZE_ERR_INVALID_PATH;
+ }
memmove(p + 1, p, p_len + 1);
p[0] = '_';
++p_len;
+ ++len;
- /* if fname was just modified then the basename pointer must be updated */
- if(p == fname)
- base = basename(fname);
+ /* the basename pointer must be updated since the path has expanded */
+ if(p == buffer)
+ base = basename(buffer);
}
/* This is the legacy portion from rename_if_dos_device_name that checks for
/* Prepend a '_' */
size_t blen = strlen(base);
if(blen) {
- if(strlen(fname) >= PATH_MAX - 1)
+ if(len == bufsize - 1) {
+ curlx_free(buffer);
return SANITIZE_ERR_INVALID_PATH;
+ }
memmove(base + 1, base, blen + 1);
base[0] = '_';
+ ++len;
}
}
#endif
- *sanitized = curlx_strdup(fname);
- return *sanitized ? SANITIZE_ERR_OK : SANITIZE_ERR_OUT_OF_MEMORY;
+ *sanitized = buffer;
+ return SANITIZE_ERR_OK;
}
#ifdef __DJGPP__