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