| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324 |
- /***************************************************************************
- * _ _ ____ _
- * Project ___| | | | _ \| |
- * / __| | | | |_) | |
- * | (__| |_| | _ <| |___
- * \___|\___/|_| \_\_____|
- *
- * Copyright (C) Daniel Stenberg, <[email protected]>, 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
- *
- ***************************************************************************/
- /*
- * This file is 'mem-include-scan' clean, which means its memory allocations
- * are not tracked by the curl memory tracker memdebug, so they must not use
- * `CURLDEBUG` macro replacements in memdebug.h for free, malloc, etc. To avoid
- * these macro replacements, wrap the names in parentheses to call the original
- * versions: `ptr = (malloc)(123)`, `(free)(ptr)`, etc.
- */
- #include "../curl_setup.h"
- #include "fopen.h"
- int curlx_fseek(void *stream, curl_off_t offset, int whence)
- {
- #if defined(_WIN32) && defined(USE_WIN32_LARGE_FILES)
- return _fseeki64(stream, (__int64)offset, whence);
- #elif defined(HAVE_FSEEKO) && defined(HAVE_DECL_FSEEKO)
- return fseeko(stream, (off_t)offset, whence);
- #else
- if(offset > LONG_MAX)
- return -1;
- return fseek(stream, (long)offset, whence);
- #endif
- }
- #if defined(_WIN32) && !defined(UNDER_CE)
- #include "multibyte.h"
- /* declare GetFullPathNameW for mingw-w64 UWP builds targeting old windows */
- #if defined(CURL_WINDOWS_UWP) && defined(__MINGW32__) && \
- (_WIN32_WINNT < _WIN32_WINNT_WIN10)
- WINBASEAPI DWORD WINAPI GetFullPathNameW(LPCWSTR, DWORD, LPWSTR, LPWSTR *);
- #endif
- /* Fix excessive paths (paths that exceed MAX_PATH length of 260).
- *
- * This is a helper function to fix paths that would exceed the MAX_PATH
- * limitation check done by Windows APIs. It does so by normalizing the passed
- * in filename or path 'in' to its full canonical path, and if that path is
- * longer than MAX_PATH then setting 'out' to "\\?\" prefix + that full path.
- *
- * For example 'in' filename255chars in current directory C:\foo\bar is
- * fixed as \\?\C:\foo\bar\filename255chars for 'out' which will tell Windows
- * it is ok to access that filename even though the actual full path is longer
- * than 260 chars.
- *
- * For non-Unicode builds this function may fail sometimes because only the
- * Unicode versions of some Windows API functions can access paths longer than
- * MAX_PATH, for example GetFullPathNameW which is used in this function. When
- * the full path is then converted from Unicode to multibyte that fails if any
- * directories in the path contain characters not in the current codepage.
- */
- static bool fix_excessive_path(const TCHAR *in, TCHAR **out)
- {
- size_t needed, count;
- const wchar_t *in_w;
- wchar_t *fbuf = NULL;
- /* MS documented "approximate" limit for the maximum path length */
- const size_t max_path_len = 32767;
- #ifndef _UNICODE
- wchar_t *ibuf = NULL;
- char *obuf = NULL;
- #endif
- *out = NULL;
- /* skip paths already normalized */
- if(!_tcsncmp(in, _T("\\\\?\\"), 4))
- goto cleanup;
- #ifndef _UNICODE
- /* convert multibyte input to unicode */
- needed = mbstowcs(NULL, in, 0);
- if(needed == (size_t)-1 || needed >= max_path_len)
- goto cleanup;
- ++needed; /* for NUL */
- ibuf = (malloc)(needed * sizeof(wchar_t));
- if(!ibuf)
- goto cleanup;
- count = mbstowcs(ibuf, in, needed);
- if(count == (size_t)-1 || count >= needed)
- goto cleanup;
- in_w = ibuf;
- #else
- in_w = in;
- #endif
- /* GetFullPathNameW returns the normalized full path in unicode. It converts
- forward slashes to backslashes, processes .. to remove directory segments,
- etc. Unlike GetFullPathNameA it can process paths that exceed MAX_PATH. */
- needed = (size_t)GetFullPathNameW(in_w, 0, NULL, NULL);
- if(!needed || needed > max_path_len)
- goto cleanup;
- /* skip paths that are not excessive and do not need modification */
- if(needed <= MAX_PATH)
- goto cleanup;
- fbuf = (malloc)(needed * sizeof(wchar_t));
- if(!fbuf)
- goto cleanup;
- count = (size_t)GetFullPathNameW(in_w, (DWORD)needed, fbuf, NULL);
- if(!count || count >= needed)
- goto cleanup;
- /* prepend \\?\ or \\?\UNC\ to the excessively long path.
- *
- * c:\longpath ---> \\?\c:\longpath
- * \\.\c:\longpath ---> \\?\c:\longpath
- * \\?\c:\longpath ---> \\?\c:\longpath (unchanged)
- * \\server\c$\longpath ---> \\?\UNC\server\c$\longpath
- *
- * https://learn.microsoft.com/dotnet/standard/io/file-path-formats
- */
- if(!wcsncmp(fbuf, L"\\\\?\\", 4))
- ; /* do nothing */
- else if(!wcsncmp(fbuf, L"\\\\.\\", 4))
- fbuf[2] = '?';
- else if(!wcsncmp(fbuf, L"\\\\.", 3) || !wcsncmp(fbuf, L"\\\\?", 3)) {
- /* Unexpected, not UNC. The formatting doc doesn't allow this AFAICT. */
- goto cleanup;
- }
- else {
- wchar_t *temp;
- if(!wcsncmp(fbuf, L"\\\\", 2)) {
- /* "\\?\UNC\" + full path without "\\" + null */
- needed = 8 + (count - 2) + 1;
- if(needed > max_path_len)
- goto cleanup;
- temp = (malloc)(needed * sizeof(wchar_t));
- if(!temp)
- goto cleanup;
- wcsncpy(temp, L"\\\\?\\UNC\\", 8);
- wcscpy(temp + 8, fbuf + 2);
- }
- else {
- /* "\\?\" + full path + null */
- needed = 4 + count + 1;
- if(needed > max_path_len)
- goto cleanup;
- temp = (malloc)(needed * sizeof(wchar_t));
- if(!temp)
- goto cleanup;
- wcsncpy(temp, L"\\\\?\\", 4);
- wcscpy(temp + 4, fbuf);
- }
- (free)(fbuf);
- fbuf = temp;
- }
- #ifndef _UNICODE
- /* convert unicode full path to multibyte output */
- needed = wcstombs(NULL, fbuf, 0);
- if(needed == (size_t)-1 || needed >= max_path_len)
- goto cleanup;
- ++needed; /* for NUL */
- obuf = (malloc)(needed);
- if(!obuf)
- goto cleanup;
- count = wcstombs(obuf, fbuf, needed);
- if(count == (size_t)-1 || count >= needed)
- goto cleanup;
- *out = obuf;
- obuf = NULL;
- #else
- *out = fbuf;
- fbuf = NULL;
- #endif
- cleanup:
- (free)(fbuf);
- #ifndef _UNICODE
- (free)(ibuf);
- (free)(obuf);
- #endif
- return *out ? true : false;
- }
- int curlx_win32_open(const char *filename, int oflag, ...)
- {
- int pmode = 0;
- int result = -1;
- TCHAR *fixed = NULL;
- const TCHAR *target = NULL;
- #ifdef _UNICODE
- wchar_t *filename_w = curlx_convert_UTF8_to_wchar(filename);
- #endif
- va_list param;
- va_start(param, oflag);
- if(oflag & O_CREAT)
- pmode = va_arg(param, int);
- va_end(param);
- #ifdef _UNICODE
- if(filename_w) {
- if(fix_excessive_path(filename_w, &fixed))
- target = fixed;
- else
- target = filename_w;
- result = _wopen(target, oflag, pmode);
- curlx_unicodefree(filename_w);
- }
- else
- /* !checksrc! disable ERRNOVAR 1 */
- CURL_SETERRNO(EINVAL);
- #else
- if(fix_excessive_path(filename, &fixed))
- target = fixed;
- else
- target = filename;
- result = _open(target, oflag, pmode);
- #endif
- (free)(fixed);
- return result;
- }
- FILE *curlx_win32_fopen(const char *filename, const char *mode)
- {
- FILE *result = NULL;
- TCHAR *fixed = NULL;
- const TCHAR *target = NULL;
- #ifdef _UNICODE
- wchar_t *filename_w = curlx_convert_UTF8_to_wchar(filename);
- wchar_t *mode_w = curlx_convert_UTF8_to_wchar(mode);
- if(filename_w && mode_w) {
- if(fix_excessive_path(filename_w, &fixed))
- target = fixed;
- else
- target = filename_w;
- result = _wfopen(target, mode_w);
- }
- else
- /* !checksrc! disable ERRNOVAR 1 */
- CURL_SETERRNO(EINVAL);
- curlx_unicodefree(filename_w);
- curlx_unicodefree(mode_w);
- #else
- if(fix_excessive_path(filename, &fixed))
- target = fixed;
- else
- target = filename;
- /* !checksrc! disable BANNEDFUNC 1 */
- result = fopen(target, mode);
- #endif
- (free)(fixed);
- return result;
- }
- int curlx_win32_stat(const char *path, struct_stat *buffer)
- {
- int result = -1;
- TCHAR *fixed = NULL;
- const TCHAR *target = NULL;
- #ifdef _UNICODE
- wchar_t *path_w = curlx_convert_UTF8_to_wchar(path);
- if(path_w) {
- if(fix_excessive_path(path_w, &fixed))
- target = fixed;
- else
- target = path_w;
- #ifndef USE_WIN32_LARGE_FILES
- result = _wstat(target, buffer);
- #else
- result = _wstati64(target, buffer);
- #endif
- curlx_unicodefree(path_w);
- }
- else
- /* !checksrc! disable ERRNOVAR 1 */
- CURL_SETERRNO(EINVAL);
- #else
- if(fix_excessive_path(path, &fixed))
- target = fixed;
- else
- target = path;
- #ifndef USE_WIN32_LARGE_FILES
- result = _stat(target, buffer);
- #else
- result = _stati64(target, buffer);
- #endif
- #endif
- (free)(fixed);
- return result;
- }
- #endif /* _WIN32 && !UNDER_CE */
|