1 /**********************************************************************
2  *
3  * Project:  CPL - Common Portability Library
4  * Purpose:  Implement VSI large file api for Win32.
5  * Author:   Frank Warmerdam, warmerdam@pobox.com
6  *
7  **********************************************************************
8  * Copyright (c) 2000, Frank Warmerdam <warmerdam@pobox.com>
9  * Copyright (c) 2008-2014, Even Rouault <even dot rouault at spatialys.com>
10  *
11  * Permission is hereby granted, free of charge, to any person obtaining a
12  * copy of this software and associated documentation files (the "Software"),
13  * to deal in the Software without restriction, including without limitation
14  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15  * and/or sell copies of the Software, and to permit persons to whom the
16  * Software is furnished to do so, subject to the following conditions:
17  *
18  * The above copyright notice and this permission notice shall be included
19  * in all copies or substantial portions of the Software.
20  *
21  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
24  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27  * DEALINGS IN THE SOFTWARE.
28  ****************************************************************************/
29 
30 #include "cpl_vsi_virtual.h"
31 
32 CPL_CVSID("$Id: cpl_vsil_win32.cpp a044c83f8091becdd11e27be6e9c08d0d3478126 2021-02-24 11:38:17 +0100 Even Rouault $")
33 
34 #if defined(WIN32)
35 
36 #include <windows.h>
37 #include <winioctl.h> // for FSCTL_SET_SPARSE
38 
39 #include "cpl_string.h"
40 
41 #include <sys/types.h>
42 #include <sys/stat.h>
43 #include <io.h>
44 #include <fcntl.h>
45 #include <direct.h>
46 
47 /************************************************************************/
48 /* ==================================================================== */
49 /*                       VSIWin32FilesystemHandler                      */
50 /* ==================================================================== */
51 /************************************************************************/
52 
53 // To avoid aliasing to GetDiskFreeSpace to GetDiskFreeSpaceA on Windows
54 #ifdef GetDiskFreeSpace
55 #undef GetDiskFreeSpace
56 #endif
57 
58 class VSIWin32FilesystemHandler final : public VSIFilesystemHandler
59 {
60     CPL_DISALLOW_COPY_ASSIGN(VSIWin32FilesystemHandler)
61 
62 public:
63     // TODO(schwehr): Fix Open call to remove the need for this using call.
64     using VSIFilesystemHandler::Open;
65 
66     VSIWin32FilesystemHandler() = default;
67 
68     virtual VSIVirtualHandle *Open( const char *pszFilename,
69                                     const char *pszAccess,
70                                     bool bSetError,
71                                     CSLConstList /* papszOptions */ ) override;
72     virtual int      Stat( const char *pszFilename, VSIStatBufL *pStatBuf, int nFlags ) override;
73     virtual int      Unlink( const char *pszFilename ) override;
74     virtual int      Rename( const char *oldpath, const char *newpath ) override;
75     virtual int      Mkdir( const char *pszDirname, long nMode ) override;
76     virtual int      Rmdir( const char *pszDirname ) override;
77     virtual char   **ReadDirEx( const char *pszDirname, int nMaxFiles ) override;
IsCaseSensitive(const char * pszFilename)78     virtual int      IsCaseSensitive( const char* pszFilename ) override
79                       { (void) pszFilename; return FALSE; }
80     virtual GIntBig  GetDiskFreeSpace( const char* pszDirname ) override;
81     virtual int SupportsSparseFiles( const char* pszPath ) override;
82 };
83 
84 /************************************************************************/
85 /* ==================================================================== */
86 /*                            VSIWin32Handle                            */
87 /* ==================================================================== */
88 /************************************************************************/
89 
90 class VSIWin32Handle final : public VSIVirtualHandle
91 {
92     CPL_DISALLOW_COPY_ASSIGN(VSIWin32Handle)
93 
94   public:
95     HANDLE       hFile = nullptr;
96     bool         bEOF = false;
97 
98     VSIWin32Handle() = default;
99 
100     virtual int       Seek( vsi_l_offset nOffset, int nWhence ) override;
101     virtual vsi_l_offset Tell() override;
102     virtual size_t    Read( void *pBuffer, size_t nSize, size_t nMemb ) override;
103     virtual size_t    Write( const void *pBuffer, size_t nSize, size_t nMemb ) override;
104     virtual int       Eof() override;
105     virtual int       Flush() override;
106     virtual int       Close() override;
107     virtual int       Truncate( vsi_l_offset nNewSize ) override;
GetNativeFileDescriptor()108     virtual void     *GetNativeFileDescriptor() override { return static_cast<void*>(hFile); }
109     virtual VSIRangeStatus GetRangeStatus( vsi_l_offset nOffset,
110                                            vsi_l_offset nLength ) override;
111 };
112 
113 /************************************************************************/
114 /*                      ErrnoFromGetLastError()                         */
115 /*                                                                      */
116 /* Private function translating Windows API error codes to POSIX errno. */
117 /*                                                                      */
118 /* TODO: If the function is going to be public CPL function, then       */
119 /* replace the switch with array of (Win32 code, errno code) tuples and */
120 /* complement it with missing codes.                                    */
121 /************************************************************************/
122 
ErrnoFromGetLastError(DWORD dwError=0)123 static int ErrnoFromGetLastError(DWORD dwError = 0)
124 {
125     int err = 0;
126     if( dwError == 0 )
127         dwError = GetLastError();
128 
129     switch( dwError )
130     {
131     case NO_ERROR:
132         err = 0;
133         break;
134     case ERROR_FILE_NOT_FOUND:      /* Cannot find the file specified. */
135     case ERROR_PATH_NOT_FOUND:      /* Cannot find the path specified. */
136     case ERROR_INVALID_DRIVE:       /* Cannot find the drive specified. */
137     case ERROR_NO_MORE_FILES:       /* There are no more files. */
138     case ERROR_BAD_PATHNAME:        /* The specified path is invalid. */
139     case ERROR_BAD_NETPATH:         /* The network path was not found. */
140     case ERROR_FILENAME_EXCED_RANGE:/* The filename or extension is too long. */
141         err = ENOENT;
142         break;
143     case ERROR_TOO_MANY_OPEN_FILES: /* The system cannot open the file. */
144         err = EMFILE;
145         break;
146     case ERROR_ACCESS_DENIED:       /* Access denied. */
147     case ERROR_CURRENT_DIRECTORY:   /* The directory cannot be removed. */
148     case ERROR_WRITE_PROTECT:       /* The media is write protected. */
149     case ERROR_LOCK_VIOLATION:      /* Another process has locked a portion of the file. */
150     case ERROR_WRONG_DISK:          /* The wrong diskette is in the drive. */
151     case ERROR_SHARING_BUFFER_EXCEEDED: /* Too many files opened for sharing. */
152     case ERROR_DRIVE_LOCKED:        /* The disk is in use or locked by another process. */
153     case ERROR_LOCK_FAILED:         /* Unable to lock a region of a file. */
154     case ERROR_SEEK_ON_DEVICE:      /* The file pointer cannot be set on the specified device or file. */
155     case ERROR_SHARING_VIOLATION:   /* The process cannot access the file because it is being used by another process. */
156         err = EACCES;
157         break;
158     case ERROR_INVALID_HANDLE:      /* The handle is invalid. */
159     case ERROR_INVALID_TARGET_HANDLE: /* The target internal file identifier is incorrect. */
160     case ERROR_DIRECT_ACCESS_HANDLE:  /* Operation other than raw disk I/O not permitted. */
161         err = EBADF;
162         break;
163     case ERROR_ARENA_TRASHED:       /* The storage control blocks were destroyed. */
164     case ERROR_NOT_ENOUGH_MEMORY:   /* Not enough storage is available. */
165     case ERROR_INVALID_BLOCK:       /* The storage control block address is invalid. */
166     case ERROR_NOT_ENOUGH_QUOTA:    /* Not enough quota is available to process this command. */
167         err = ENOMEM;
168         break;
169     case ERROR_BAD_ENVIRONMENT:     /* The environment is incorrect. */
170         err = E2BIG;
171         break;
172     case ERROR_INVALID_ACCESS:      /* The access code is invalid. */
173     case ERROR_INVALID_DATA:        /* The data is invalid. */
174         err = EINVAL;
175         break;
176     case ERROR_NOT_SAME_DEVICE:     /* The system cannot move the file to a different disk drive. */
177         err = EXDEV;
178         break;
179     case ERROR_DIR_NOT_EMPTY:       /* The directory is not empty. */
180         err = ENOTEMPTY;
181         break;
182     case ERROR_FILE_EXISTS:         /* The file exists. */
183     case ERROR_ALREADY_EXISTS:      /* Cannot create a file when that file already exists. */
184         err = EEXIST;
185         break;
186     case ERROR_DISK_FULL:           /* There is not enough space on the disk. */
187         err = ENOSPC;
188         break;
189     case ERROR_HANDLE_EOF:          /* Reached the end of the file. */
190         err = 0; /* There is no errno equivalent in the errno.h */
191         break;
192     default:
193         err = 0;
194     }
195     CPLAssert( 0 <= err );
196 
197     return err;
198 }
199 
200 /************************************************************************/
201 /*                               Close()                                */
202 /************************************************************************/
203 
Close()204 int VSIWin32Handle::Close()
205 
206 {
207     return CloseHandle( hFile ) ? 0 : -1;
208 }
209 
210 /************************************************************************/
211 /*                                Seek()                                */
212 /************************************************************************/
213 
Seek(vsi_l_offset nOffset,int nWhence)214 int VSIWin32Handle::Seek( vsi_l_offset nOffset, int nWhence )
215 
216 {
217     LONG       dwMoveMethod, dwMoveHigh;
218     GUInt32       nMoveLow;
219     LARGE_INTEGER li;
220 
221     bEOF = false;
222 
223     switch(nWhence)
224     {
225         case SEEK_CUR:
226             dwMoveMethod = FILE_CURRENT;
227             break;
228         case SEEK_END:
229             dwMoveMethod = FILE_END;
230             break;
231         case SEEK_SET:
232         default:
233             dwMoveMethod = FILE_BEGIN;
234             break;
235     }
236 
237     li.QuadPart = nOffset;
238     nMoveLow = li.LowPart;
239     dwMoveHigh = li.HighPart;
240 
241     SetLastError( 0 );
242     SetFilePointer(hFile, nMoveLow, &dwMoveHigh,
243                        dwMoveMethod);
244 
245     if( GetLastError() != NO_ERROR )
246     {
247 #ifdef notdef
248         LPVOID      lpMsgBuf = nullptr;
249 
250         FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER
251                        | FORMAT_MESSAGE_FROM_SYSTEM
252                        | FORMAT_MESSAGE_IGNORE_INSERTS,
253                        nullptr, GetLastError(),
254                        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
255                        (LPTSTR) &lpMsgBuf, 0, nullptr );
256 
257         printf( "[ERROR %d]\n %s\n", GetLastError(), (char *) lpMsgBuf );/*ok*/
258         printf( "nOffset=%u, nMoveLow=%u, dwMoveHigh=%u\n",/*ok*/
259                 static_cast<GUInt32>(nOffset), nMoveLow, dwMoveHigh );
260 #endif
261         errno = ErrnoFromGetLastError();
262         return -1;
263     }
264     else
265         return 0;
266 }
267 
268 /************************************************************************/
269 /*                                Tell()                                */
270 /************************************************************************/
271 
Tell()272 vsi_l_offset VSIWin32Handle::Tell()
273 
274 {
275     LARGE_INTEGER   li;
276 
277     li.HighPart = 0;
278     li.LowPart = SetFilePointer( hFile, 0, &(li.HighPart),
279                                  FILE_CURRENT );
280 
281     return (static_cast<vsi_l_offset>(li.QuadPart));
282 }
283 
284 /************************************************************************/
285 /*                               Flush()                                */
286 /************************************************************************/
287 
Flush()288 int VSIWin32Handle::Flush()
289 
290 {
291     /* Nothing needed to offer the same guarantee as POSIX fflush() */
292     /* FlushFileBuffers() would be closer to fsync() */
293     /* See http://trac.osgeo.org/gdal/ticket/5556 */
294 
295     // Add this as a hack to make ogr_mitab_30 and _31 tests pass
296     if( CPLTestBool(CPLGetConfigOption("VSI_FLUSH", "FALSE")) )
297         FlushFileBuffers(hFile);
298     return 0;
299 }
300 
301 /************************************************************************/
302 /*                                Read()                                */
303 /************************************************************************/
304 
Read(void * pBuffer,size_t nSize,size_t nCount)305 size_t VSIWin32Handle::Read( void * pBuffer, size_t nSize, size_t nCount )
306 
307 {
308     DWORD dwSizeRead = 0;
309     size_t nResult = 0;
310 
311     if( !ReadFile( hFile, pBuffer, static_cast<DWORD>(nSize*nCount), &dwSizeRead, nullptr ) )
312     {
313         nResult = 0;
314         errno = ErrnoFromGetLastError();
315     }
316     else if( nSize == 0 )
317         nResult = 0;
318     else
319         nResult = dwSizeRead / nSize;
320 
321     if( nResult != nCount )
322         bEOF = true;
323 
324     return nResult;
325 }
326 
327 /************************************************************************/
328 /*                               Write()                                */
329 /************************************************************************/
330 
Write(const void * pBuffer,size_t nSize,size_t nCount)331 size_t VSIWin32Handle::Write( const void *pBuffer, size_t nSize, size_t nCount)
332 
333 {
334     DWORD dwSizeWritten = 0;
335     size_t nResult = 0;
336 
337     if( !WriteFile(hFile, pBuffer,
338                    static_cast<DWORD>(nSize*nCount),&dwSizeWritten,nullptr) )
339     {
340         nResult = 0;
341         errno = ErrnoFromGetLastError();
342     }
343     else if( nSize == 0)
344         nResult = 0;
345     else
346         nResult = dwSizeWritten / nSize;
347 
348     return nResult;
349 }
350 
351 /************************************************************************/
352 /*                                Eof()                                 */
353 /************************************************************************/
354 
Eof()355 int VSIWin32Handle::Eof()
356 
357 {
358     return bEOF;
359 }
360 
361 /************************************************************************/
362 /*                             Truncate()                               */
363 /************************************************************************/
364 
Truncate(vsi_l_offset nNewSize)365 int VSIWin32Handle::Truncate( vsi_l_offset nNewSize )
366 {
367     vsi_l_offset nCur = Tell();
368     Seek( 0, SEEK_END );
369     if( nNewSize > Tell() )
370     {
371         // Enable sparse files if growing size
372         DWORD dwTemp;
373         DeviceIoControl(hFile, FSCTL_SET_SPARSE, nullptr, 0, nullptr, 0, &dwTemp, nullptr);
374     }
375     Seek( nNewSize, SEEK_SET );
376     BOOL bRes = SetEndOfFile( hFile );
377     Seek( nCur, SEEK_SET );
378 
379     if (bRes)
380         return 0;
381     else
382         return -1;
383 }
384 
385 /************************************************************************/
386 /*                           GetRangeStatus()                           */
387 /************************************************************************/
388 
GetRangeStatus(vsi_l_offset nOffset,vsi_l_offset nLength)389 VSIRangeStatus VSIWin32Handle::GetRangeStatus( vsi_l_offset
390 #ifdef FSCTL_QUERY_ALLOCATED_RANGES
391                                                         nOffset
392 #endif
393                                                ,vsi_l_offset
394 #ifdef FSCTL_QUERY_ALLOCATED_RANGES
395                                                         nLength
396 #endif
397                                               )
398 {
399     // Not available on mingw includes
400 #ifdef FSCTL_QUERY_ALLOCATED_RANGES
401     FILE_ALLOCATED_RANGE_BUFFER sQueryRange;
402     FILE_ALLOCATED_RANGE_BUFFER asOutputRange[1];
403     DWORD nOutputBytes = 0;
404 
405     sQueryRange.FileOffset.QuadPart = nOffset;
406     sQueryRange.Length.QuadPart = nOffset + nLength;
407 
408     if( !DeviceIoControl(hFile, FSCTL_QUERY_ALLOCATED_RANGES,
409                         &sQueryRange, sizeof(sQueryRange),
410                         asOutputRange, sizeof(asOutputRange),
411                         &nOutputBytes, nullptr) )
412     {
413         if( GetLastError() == ERROR_MORE_DATA )
414         {
415             return VSI_RANGE_STATUS_DATA;
416         }
417         else
418         {
419             return VSI_RANGE_STATUS_UNKNOWN;
420         }
421     }
422 
423     if( nOutputBytes )
424         return VSI_RANGE_STATUS_DATA;
425     else
426         return VSI_RANGE_STATUS_HOLE;
427 #else
428     return VSI_RANGE_STATUS_UNKNOWN;
429 #endif
430 }
431 
432 /************************************************************************/
433 /* ==================================================================== */
434 /*                       VSIWin32FilesystemHandler                      */
435 /* ==================================================================== */
436 /************************************************************************/
437 
438 /************************************************************************/
439 /*                          CPLGetWineVersion()                         */
440 /************************************************************************/
441 
442 const char* CPLGetWineVersion(); // also used by cpl_aws.cpp
443 
CPLGetWineVersion()444 const char* CPLGetWineVersion()
445 {
446     HMODULE hntdll = GetModuleHandle("ntdll.dll");
447     if( hntdll == nullptr )
448     {
449         CPLDebug("CPLGetWineVersion", "Can't get handle to ntdll.dll.");
450         return nullptr;
451     }
452 
453     const char * (CDECL *pwine_get_version)(void);
454     pwine_get_version = reinterpret_cast<const char* (*)(void)>(GetProcAddress(hntdll, "wine_get_version"));
455     if( pwine_get_version == nullptr )
456     {
457         return nullptr;
458     }
459 
460     return pwine_get_version();
461 }
462 
463 /************************************************************************/
464 /*                           VSIWin32StrlenW()                          */
465 /************************************************************************/
466 
VSIWin32StrlenW(const wchar_t * pwszString)467 static size_t VSIWin32StrlenW(const wchar_t* pwszString)
468 {
469     size_t nLen = 0;
470     while( pwszString[nLen] != 0 )
471         nLen ++;
472     return nLen;
473 }
474 
475 /************************************************************************/
476 /*                        VSIWin32TryLongFilename()                     */
477 /************************************************************************/
478 
VSIWin32TryLongFilename(wchar_t * & pwszFilename)479 static void VSIWin32TryLongFilename(wchar_t*& pwszFilename)
480 {
481     size_t nLen = VSIWin32StrlenW(pwszFilename);
482     if( pwszFilename[0] != 0 &&
483         pwszFilename[1] == ':' &&
484         (pwszFilename[2] == '\\' || pwszFilename[2] == '/' ) )
485     {
486         pwszFilename = static_cast<wchar_t *>(
487             CPLRealloc( pwszFilename, (4 + nLen + 1) * sizeof(wchar_t)));
488         memmove( pwszFilename + 4, pwszFilename, (nLen+1) * sizeof(wchar_t));
489     }
490     else
491     {
492         // TODO(schwehr): 32768 should be a symbolic constant.
493         wchar_t* pwszCurDir =
494             static_cast<wchar_t *>(CPLMalloc(32768 * sizeof(wchar_t)));
495         DWORD nCurDirLen = GetCurrentDirectoryW( 32768, pwszCurDir );
496         CPLAssert(nCurDirLen < 32768);
497         pwszFilename = static_cast<wchar_t *>(
498             CPLRealloc(pwszFilename,
499                        (4 + nCurDirLen + 1 + nLen + 1) * sizeof(wchar_t)));
500         int nOffset = 0;
501         if( pwszFilename[0] == '.' &&
502             (pwszFilename[1] == '/' || pwszFilename[1] == '\\') )
503             nOffset = 2;
504         /* \\$\c:\a\b ..\c --> \\$\c:\a\c */
505         while( pwszFilename[nOffset + 0] == '.' && pwszFilename[nOffset + 1] == '.' &&
506               (pwszFilename[nOffset + 2] == '/' || pwszFilename[nOffset + 2] == '\\') )
507         {
508             DWORD nCurDirLenBefore = nCurDirLen;
509             while( nCurDirLen > 0 && pwszCurDir[nCurDirLen-1] != '\\' )
510                 nCurDirLen --;
511             if( nCurDirLen <= 2 )
512             {
513                 nCurDirLen = nCurDirLenBefore;
514                 break;
515             }
516             nCurDirLen --;
517             nOffset += 3;
518         }
519         memmove( pwszFilename + 4 + nCurDirLen + 1,
520                  pwszFilename + nOffset, (nLen-nOffset+1) * sizeof(wchar_t) );
521         memmove( pwszFilename + 4, pwszCurDir, nCurDirLen * sizeof(wchar_t) );
522         pwszFilename[ 4 + nCurDirLen ] = '\\';
523         CPLFree(pwszCurDir);
524     }
525     pwszFilename[0] = '\\';
526     pwszFilename[1] = '\\';
527     pwszFilename[2] = '?';
528     pwszFilename[3] = '\\';
529 
530     for(size_t i = 4; pwszFilename[i] != 0; i++)
531     {
532         if( pwszFilename[i] == '/' )
533             pwszFilename[i] = '\\';
534     }
535 #if notdef
536     CPLString osFilename;
537     for(size_t i = 0; pwszFilename[i] != 0; i++)
538         osFilename += (char)pwszFilename[i];
539     CPLDebug("VSI", "Trying %s", osFilename.c_str());
540 #endif
541 }
542 
543 /************************************************************************/
544 /*                         VSIWin32IsLongFilename()                     */
545 /************************************************************************/
546 
VSIWin32IsLongFilename(const wchar_t * pwszFilename)547 static bool VSIWin32IsLongFilename( const wchar_t* pwszFilename )
548 {
549     return (pwszFilename[0] == '\\' && pwszFilename[1] == '\\' &&
550             pwszFilename[2] == '?' && pwszFilename[3] == '\\');
551 }
552 
553 /************************************************************************/
554 /*                                Open()                                */
555 /************************************************************************/
556 
Open(const char * pszFilename,const char * pszAccess,bool bSetError,CSLConstList)557 VSIVirtualHandle *VSIWin32FilesystemHandler::Open( const char *pszFilename,
558                                                    const char *pszAccess,
559                                                    bool bSetError,
560                                                    CSLConstList /* papszOptions */ )
561 
562 {
563     DWORD dwDesiredAccess;
564     DWORD dwCreationDisposition;
565     DWORD dwFlagsAndAttributes;
566     HANDLE hFile;
567 
568     // GENERICs are used instead of FILE_GENERIC_READ.
569     dwDesiredAccess = GENERIC_READ;
570     if (strchr(pszAccess, '+') != nullptr || strchr(pszAccess, 'w') != nullptr)
571         dwDesiredAccess |= GENERIC_WRITE;
572 
573     // Append mode only makes sense on files and pipes, have to use FILE_ access
574     // these are very different from the GENERICs
575     // Append is read and write but not overwrite data (only append data)
576     if (strchr(pszAccess, 'a') != nullptr )
577     {
578         dwDesiredAccess =
579             FILE_GENERIC_READ | (FILE_GENERIC_WRITE ^ FILE_WRITE_DATA);
580 
581         // Wine < 1.7.4 doesn't work properly without FILE_WRITE_DATA bit
582         // (it refuses to write at all), so we'd better re-add it even if the
583         // resulting semantics isn't completely conformant.
584         // See https://bugs.winehq.org/show_bug.cgi?id=33232
585         const char* pszWineVersion = CPLGetWineVersion();
586         if( pszWineVersion != nullptr )
587         {
588             int nVersion = atoi(pszWineVersion) * 10000;
589             const char* pszDot = strchr(pszWineVersion, '.');
590             if( pszDot )
591             {
592                 nVersion += atoi(pszDot + 1) * 100;
593                 pszDot = strchr(pszDot + 1, '.');
594                 if( pszDot )
595                 {
596                     nVersion += atoi(pszDot + 1);
597                 }
598             }
599             if( nVersion < 1 * 10000 + 7 * 100 + 4 )
600             {
601 #if DEBUG_VERBOSE
602                 CPLDebug("VSI",
603                          "Wine %s detected. Append mode needs FILE_WRITE_DATA",
604                          pszWineVersion);
605 #endif
606                 dwDesiredAccess |= FILE_WRITE_DATA;
607             }
608         }
609     }
610 
611     if( strstr(pszAccess, "w") != nullptr )
612         dwCreationDisposition = CREATE_ALWAYS;
613     else if( strstr(pszAccess, "a") != nullptr )
614         dwCreationDisposition = OPEN_ALWAYS;
615     else
616         dwCreationDisposition = OPEN_EXISTING;
617 
618     dwFlagsAndAttributes = (dwDesiredAccess == GENERIC_READ) ?
619         FILE_ATTRIBUTE_READONLY : FILE_ATTRIBUTE_NORMAL;
620 
621 /* -------------------------------------------------------------------- */
622 /*      On Win32 consider treating the filename as utf-8 and            */
623 /*      converting to wide characters to open.                          */
624 /* -------------------------------------------------------------------- */
625     DWORD nLastError = 0;
626     bool bShared = CPLTestBool(CPLGetConfigOption( "GDAL_SHARED_FILE", "YES" ) );
627     if( CPLTestBool(CPLGetConfigOption( "GDAL_FILENAME_IS_UTF8", "YES" ) ) )
628     {
629         wchar_t *pwszFilename =
630             CPLRecodeToWChar( pszFilename, CPL_ENC_UTF8, CPL_ENC_UCS2 );
631 
632         hFile = CreateFileW( pwszFilename, dwDesiredAccess,
633                             bShared ? FILE_SHARE_READ | FILE_SHARE_WRITE : 0,
634                             nullptr, dwCreationDisposition,  dwFlagsAndAttributes,
635                             nullptr );
636         if ( hFile == INVALID_HANDLE_VALUE &&
637             !VSIWin32IsLongFilename(pwszFilename) )
638         {
639             nLastError = GetLastError();
640 #ifdef notdef
641             switch( nLastError )
642             {
643                     case ERROR_FILE_NOT_FOUND:      CPLDebug("VSI", "ERROR_FILE_NOT_FOUND"); break;
644                     case ERROR_PATH_NOT_FOUND:      CPLDebug("VSI", "ERROR_PATH_NOT_FOUND"); break;
645                     case ERROR_INVALID_DRIVE:       CPLDebug("VSI", "ERROR_INVALID_DRIVE"); break;
646                     case ERROR_NO_MORE_FILES:       CPLDebug("VSI", "ERROR_NO_MORE_FILES"); break;
647                     case ERROR_BAD_PATHNAME:        CPLDebug("VSI", "ERROR_BAD_PATHNAME"); break;
648                     case ERROR_BAD_NETPATH:         CPLDebug("VSI", "ERROR_BAD_NETPATH"); break;
649                     case ERROR_FILENAME_EXCED_RANGE: CPLDebug("VSI", "ERROR_FILENAME_EXCED_RANGE"); break;
650                     case ERROR_SHARING_VIOLATION:   CPLDebug("VSI", "ERROR_SHARING_VIOLATION"); break;
651                     default:  CPLDebug("VSI", "other error %d", nLastError); break;
652             }
653 #endif
654         }
655         if( nLastError == ERROR_PATH_NOT_FOUND ||
656             nLastError == ERROR_FILENAME_EXCED_RANGE )
657         {
658             VSIWin32TryLongFilename(pwszFilename);
659             nLastError = 0;
660             hFile = CreateFileW( pwszFilename, dwDesiredAccess,
661                             bShared ? FILE_SHARE_READ | FILE_SHARE_WRITE : 0,
662                             nullptr, dwCreationDisposition,  dwFlagsAndAttributes,
663                             nullptr );
664         }
665         CPLFree( pwszFilename );
666     }
667     else
668     {
669         hFile = CreateFile( pszFilename, dwDesiredAccess,
670                             bShared ? FILE_SHARE_READ | FILE_SHARE_WRITE : 0,
671                             nullptr, dwCreationDisposition,  dwFlagsAndAttributes,
672                             nullptr );
673     }
674 
675     if( hFile == INVALID_HANDLE_VALUE )
676     {
677         nLastError = GetLastError();
678         const int nError = ErrnoFromGetLastError(nLastError);
679         if( bSetError && nError != 0 )
680         {
681             VSIError(VSIE_FileError, "%s: %s", pszFilename,
682                      (nLastError == ERROR_SHARING_VIOLATION) ?
683                         "file used by other process": strerror(nError));
684         }
685         errno = nError;
686         return nullptr;
687     }
688 
689 /* -------------------------------------------------------------------- */
690 /*      Create a VSI file handle.                                       */
691 /* -------------------------------------------------------------------- */
692     VSIWin32Handle *poHandle = new VSIWin32Handle;
693 
694     poHandle->hFile = hFile;
695 
696     if (strchr(pszAccess, 'a') != nullptr)
697         poHandle->Seek(0, SEEK_END);
698 
699 /* -------------------------------------------------------------------- */
700 /*      If VSI_CACHE is set we want to use a cached reader instead      */
701 /*      of more direct io on the underlying file.                       */
702 /* -------------------------------------------------------------------- */
703     if( (EQUAL(pszAccess,"r") || EQUAL(pszAccess,"rb"))
704         && CPLTestBool( CPLGetConfigOption( "VSI_CACHE", "FALSE" ) ) )
705     {
706         return VSICreateCachedFile( poHandle );
707     }
708     else
709     {
710         return poHandle;
711     }
712 }
713 
714 /************************************************************************/
715 /*                                Stat()                                */
716 /************************************************************************/
717 
Stat(const char * pszFilename,VSIStatBufL * pStatBuf,int nFlags)718 int VSIWin32FilesystemHandler::Stat( const char * pszFilename,
719                                      VSIStatBufL * pStatBuf,
720                                      int nFlags )
721 
722 {
723     (void) nFlags;
724 
725 #if defined(_MSC_VER) || __MSVCRT_VERSION__ >= 0x0601
726     if( CPLTestBool( CPLGetConfigOption( "GDAL_FILENAME_IS_UTF8", "YES" ) ) )
727     {
728         wchar_t *pwszFilename =
729             CPLRecodeToWChar( pszFilename, CPL_ENC_UTF8, CPL_ENC_UCS2 );
730 
731         bool bTryOtherStatImpl = false;
732         int nResult = 0;
733         if( VSIWin32IsLongFilename(pwszFilename) )
734         {
735             bTryOtherStatImpl = true;
736             nResult = -1;
737         }
738         else
739         {
740             nResult = _wstat64( pwszFilename, pStatBuf );
741             if( nResult < 0 )
742             {
743                 DWORD nLastError = GetLastError();
744                 if( nLastError == ERROR_PATH_NOT_FOUND ||
745                     nLastError == ERROR_FILENAME_EXCED_RANGE )
746                 {
747                     VSIWin32TryLongFilename(pwszFilename);
748                     bTryOtherStatImpl = true;
749                 }
750             }
751         }
752 
753         if( bTryOtherStatImpl )
754         {
755             // _wstat64 doesn't like \\?\ paths, so do our poor-man
756             // stat like.
757             // nResult = _wstat64( pwszFilename, pStatBuf );
758 
759             VSIVirtualHandle* poHandle = Open( pszFilename, "rb");
760             if( poHandle != nullptr )
761             {
762                 nResult = 0;
763                 memset( pStatBuf, 0, sizeof(VSIStatBufL) );
764                 CPL_IGNORE_RET_VAL(poHandle->Seek(0, SEEK_END));
765                 pStatBuf->st_mode = S_IFREG;
766                 pStatBuf->st_size = poHandle->Tell();
767                 poHandle->Close();
768                 delete poHandle;
769             }
770             else
771                 nResult = -1;
772         }
773 
774         CPLFree( pwszFilename );
775 
776         return nResult;
777     }
778     else
779 #endif
780     {
781         return( VSI_STAT64( pszFilename, pStatBuf ) );
782     }
783 }
784 
785 /************************************************************************/
786 
787 /*                               Unlink()                               */
788 /************************************************************************/
789 
Unlink(const char * pszFilename)790 int VSIWin32FilesystemHandler::Unlink( const char * pszFilename )
791 
792 {
793 #if defined(_MSC_VER) || __MSVCRT_VERSION__ >= 0x0601
794     if( CPLTestBool( CPLGetConfigOption( "GDAL_FILENAME_IS_UTF8", "YES" ) ) )
795     {
796         wchar_t *pwszFilename =
797             CPLRecodeToWChar( pszFilename, CPL_ENC_UTF8, CPL_ENC_UCS2 );
798 
799         const int nResult = _wunlink( pwszFilename );
800         CPLFree( pwszFilename );
801         return nResult;
802     }
803     else
804 #endif
805     {
806         return unlink( pszFilename );
807     }
808 }
809 
810 /************************************************************************/
811 /*                               Rename()                               */
812 /************************************************************************/
813 
Rename(const char * oldpath,const char * newpath)814 int VSIWin32FilesystemHandler::Rename( const char *oldpath,
815                                            const char *newpath )
816 
817 {
818 #if defined(_MSC_VER) || __MSVCRT_VERSION__ >= 0x0601
819     if( CPLTestBool( CPLGetConfigOption( "GDAL_FILENAME_IS_UTF8", "YES" ) ) )
820     {
821         wchar_t *pwszOldPath =
822             CPLRecodeToWChar( oldpath, CPL_ENC_UTF8, CPL_ENC_UCS2 );
823         wchar_t *pwszNewPath =
824             CPLRecodeToWChar( newpath, CPL_ENC_UTF8, CPL_ENC_UCS2 );
825 
826         const int nResult = _wrename( pwszOldPath, pwszNewPath );
827         CPLFree( pwszOldPath );
828         CPLFree( pwszNewPath );
829         return nResult;
830     }
831     else
832 #endif
833     {
834         return rename( oldpath, newpath );
835     }
836 }
837 
838 /************************************************************************/
839 /*                               Mkdir()                                */
840 /************************************************************************/
841 
Mkdir(const char * pszPathname,long nMode)842 int VSIWin32FilesystemHandler::Mkdir( const char * pszPathname,
843                                       long nMode )
844 
845 {
846     (void) nMode;
847 #if defined(_MSC_VER) || __MSVCRT_VERSION__ >= 0x0601
848     if( CPLTestBool( CPLGetConfigOption( "GDAL_FILENAME_IS_UTF8", "YES" ) ) )
849     {
850         wchar_t *pwszFilename =
851             CPLRecodeToWChar( pszPathname, CPL_ENC_UTF8, CPL_ENC_UCS2 );
852 
853         const int nResult = _wmkdir( pwszFilename );
854         CPLFree( pwszFilename );
855         return nResult;
856     }
857     else
858 #endif
859     {
860         return mkdir( pszPathname );
861     }
862 }
863 
864 /************************************************************************/
865 /*                               Rmdir()                                */
866 /************************************************************************/
867 
Rmdir(const char * pszPathname)868 int VSIWin32FilesystemHandler::Rmdir( const char * pszPathname )
869 
870 {
871 #if defined(_MSC_VER) || __MSVCRT_VERSION__ >= 0x0601
872     if( CPLTestBool( CPLGetConfigOption( "GDAL_FILENAME_IS_UTF8", "YES" ) ) )
873     {
874         wchar_t *pwszFilename =
875             CPLRecodeToWChar( pszPathname, CPL_ENC_UTF8, CPL_ENC_UCS2 );
876 
877         const int nResult = _wrmdir( pwszFilename );
878         CPLFree( pwszFilename );
879         return nResult;
880     }
881     else
882 #endif
883     {
884         return rmdir( pszPathname );
885     }
886 }
887 
888 /************************************************************************/
889 /*                              ReadDir()                               */
890 /************************************************************************/
891 
ReadDirEx(const char * pszPath,int nMaxFiles)892 char **VSIWin32FilesystemHandler::ReadDirEx( const char *pszPath,
893                                              int nMaxFiles )
894 
895 {
896 #if defined(_MSC_VER) || __MSVCRT_VERSION__ >= 0x0601
897     if( CPLTestBool( CPLGetConfigOption( "GDAL_FILENAME_IS_UTF8", "YES" ) ) )
898     {
899         struct _wfinddata_t c_file;
900         intptr_t hFile;
901         char    *pszFileSpec;
902         CPLStringList oDir;
903 
904         if (strlen(pszPath) == 0)
905             pszPath = ".";
906 
907         pszFileSpec = CPLStrdup(CPLSPrintf("%s\\*.*", pszPath));
908         wchar_t *pwszFileSpec =
909             CPLRecodeToWChar( pszFileSpec, CPL_ENC_UTF8, CPL_ENC_UCS2 );
910 
911         if ( (hFile = _wfindfirst( pwszFileSpec, &c_file )) != -1L )
912         {
913             do
914             {
915                 oDir.AddStringDirectly(
916                     CPLRecodeFromWChar(c_file.name,CPL_ENC_UCS2,CPL_ENC_UTF8));
917                 if( nMaxFiles > 0 && oDir.Count() > nMaxFiles )
918                     break;
919             } while( _wfindnext( hFile, &c_file ) == 0 );
920 
921             _findclose( hFile );
922         }
923         else
924         {
925             /* Should we generate an error???
926              * For now we'll just return NULL (at the end of the function)
927              */
928         }
929 
930         CPLFree(pszFileSpec);
931         CPLFree(pwszFileSpec);
932 
933         return oDir.StealList();
934     }
935     else
936 #endif
937     {
938         struct _finddata_t c_file;
939         intptr_t hFile;
940         char    *pszFileSpec;
941         CPLStringList oDir;
942 
943         if (strlen(pszPath) == 0)
944             pszPath = ".";
945 
946         pszFileSpec = CPLStrdup(CPLSPrintf("%s\\*.*", pszPath));
947 
948         if ( (hFile = _findfirst( pszFileSpec, &c_file )) != -1L )
949         {
950             do
951             {
952                 oDir.AddString(c_file.name);
953                 if( nMaxFiles > 0 && oDir.Count() > nMaxFiles )
954                     break;
955             } while( _findnext( hFile, &c_file ) == 0 );
956 
957             _findclose( hFile );
958         }
959         else
960         {
961             /* Should we generate an error???
962              * For now we'll just return NULL (at the end of the function)
963              */
964         }
965 
966         CPLFree(pszFileSpec);
967 
968         return oDir.StealList();
969     }
970 }
971 
972 /************************************************************************/
973 /*                        GetDiskFreeSpace()                            */
974 /************************************************************************/
975 
GetDiskFreeSpace(const char * pszDirname)976 GIntBig VSIWin32FilesystemHandler::GetDiskFreeSpace( const char* pszDirname )
977 {
978     GIntBig nRet = -1;
979     ULARGE_INTEGER nFreeBytesAvailable;
980     if( GetDiskFreeSpaceEx(pszDirname, &nFreeBytesAvailable, nullptr, nullptr) )
981     {
982         nRet = static_cast<GIntBig>(nFreeBytesAvailable.QuadPart);
983     }
984     return nRet;
985 }
986 
987 /************************************************************************/
988 /*                      SupportsSparseFiles()                           */
989 /************************************************************************/
990 
SupportsSparseFiles(const char * pszPath)991 int VSIWin32FilesystemHandler::SupportsSparseFiles( const char* pszPath )
992 {
993     CPLString osPath(pszPath);
994     DWORD dwVolFlags = 0;
995     if( CPLIsFilenameRelative(pszPath) )
996     {
997         char* pszTmp = CPLGetCurrentDir();
998         osPath = pszTmp;
999         CPLFree(pszTmp);
1000     }
1001     if( osPath.size() >= 3 && osPath[1] == ':' &&
1002         (osPath[2] == '/' || osPath[2] == '\\') )
1003     {
1004         osPath.resize(3);
1005     }
1006 
1007     GetVolumeInformation(osPath.c_str(), nullptr, 0, nullptr,
1008                          nullptr, &dwVolFlags, nullptr, 0);
1009     return (dwVolFlags & FILE_SUPPORTS_SPARSE_FILES);
1010 }
1011 
1012 /************************************************************************/
1013 /*                     VSIInstallLargeFileHandler()                     */
1014 /************************************************************************/
1015 
VSIInstallLargeFileHandler()1016 void VSIInstallLargeFileHandler()
1017 
1018 {
1019     VSIFileManager::InstallHandler( "", new VSIWin32FilesystemHandler );
1020 }
1021 
1022 #endif /* def WIN32 */
1023