/************************************************************************/
/* Open() */
/************************************************************************/
VSIVirtualHandle *VSIWin32FilesystemHandler::Open( const char *pszFilename,
const char *pszAccess,
bool bSetError )
{
DWORD dwDesiredAccess;
DWORD dwCreationDisposition;
DWORD dwFlagsAndAttributes;
HANDLE hFile;
// GENERICs are used instead of FILE_GENERIC_READ.
dwDesiredAccess = GENERIC_READ;
if (strchr(pszAccess, '+') != nullptr || strchr(pszAccess, 'w') != nullptr)
dwDesiredAccess |= GENERIC_WRITE;
// Append mode only makes sense on files and pipes, have to use FILE_ access
// these are very different from the GENERICs
// Append is read and write but not overwrite data (only append data)
if (strchr(pszAccess, 'a') != nullptr )
{
dwDesiredAccess =
FILE_GENERIC_READ | (FILE_GENERIC_WRITE ^ FILE_WRITE_DATA);
// Wine < 1.7.4 doesn't work properly without FILE_WRITE_DATA bit
// (it refuses to write at all), so we'd better re-add it even if the
// resulting semantics isn't completely conformant.
// See https://bugs.winehq.org/show_bug.cgi?id=33232
const char* pszWineVersion = CPLGetWineVersion();
if( pszWineVersion != nullptr )
{
int nVersion = atoi(pszWineVersion) * 10000;
const char* pszDot = strchr(pszWineVersion, '.');
if( pszDot )
{
nVersion += atoi(pszDot + 1) * 100;
pszDot = strchr(pszDot + 1, '.');
if( pszDot )
{
nVersion += atoi(pszDot + 1);
}
}
if( nVersion < 1 * 10000 + 7 * 100 + 4 )
{
#if DEBUG_VERBOSE
CPLDebug("VSI",
"Wine %s detected. Append mode needs FILE_WRITE_DATA",
pszWineVersion);
#endif
dwDesiredAccess |= FILE_WRITE_DATA;
}
}
}
if( strstr(pszAccess, "w") != nullptr )
dwCreationDisposition = CREATE_ALWAYS;
else if( strstr(pszAccess, "a") != nullptr )
dwCreationDisposition = OPEN_ALWAYS;
else
dwCreationDisposition = OPEN_EXISTING;
dwFlagsAndAttributes = (dwDesiredAccess == GENERIC_READ) ?
FILE_ATTRIBUTE_READONLY : FILE_ATTRIBUTE_NORMAL;
/* -------------------------------------------------------------------- */
/* On Win32 consider treating the filename as utf-8 and */
/* converting to wide characters to open. */
/* -------------------------------------------------------------------- */
DWORD nLastError = 0;
bool bShared = CPLTestBool(CPLGetConfigOption( "GDAL_SHARED_FILE", "YES" ) );
if( CPLTestBool(CPLGetConfigOption( "GDAL_FILENAME_IS_UTF8", "YES" ) ) )
{
wchar_t *pwszFilename =
CPLRecodeToWChar( pszFilename, CPL_ENC_UTF8, CPL_ENC_UCS2 );
hFile = CreateFileW( pwszFilename, dwDesiredAccess,
bShared ? FILE_SHARE_READ | FILE_SHARE_WRITE : 0,
nullptr, dwCreationDisposition, dwFlagsAndAttributes,
nullptr );
if ( hFile == INVALID_HANDLE_VALUE &&
!VSIWin32IsLongFilename(pwszFilename) )
{
nLastError = GetLastError();
#ifdef notdef
switch( nLastError )
{
case ERROR_FILE_NOT_FOUND: CPLDebug("VSI", "ERROR_FILE_NOT_FOUND"); break;
case ERROR_PATH_NOT_FOUND: CPLDebug("VSI", "ERROR_PATH_NOT_FOUND"); break;
case ERROR_INVALID_DRIVE: CPLDebug("VSI", "ERROR_INVALID_DRIVE"); break;
case ERROR_NO_MORE_FILES: CPLDebug("VSI", "ERROR_NO_MORE_FILES"); break;
case ERROR_BAD_PATHNAME: CPLDebug("VSI", "ERROR_BAD_PATHNAME"); break;
case ERROR_BAD_NETPATH: CPLDebug("VSI", "ERROR_BAD_NETPATH"); break;
case ERROR_FILENAME_EXCED_RANGE: CPLDebug("VSI", "ERROR_FILENAME_EXCED_RANGE"); break;
case ERROR_SHARING_VIOLATION: CPLDebug("VSI", "ERROR_SHARING_VIOLATION"); break;
default: CPLDebug("VSI", "other error %d", nLastError); break;
}
#endif
}
if( nLastError == ERROR_PATH_NOT_FOUND ||
nLastError == ERROR_FILENAME_EXCED_RANGE )
{
VSIWin32TryLongFilename(pwszFilename);
nLastError = 0;
hFile = CreateFileW( pwszFilename, dwDesiredAccess,
bShared ? FILE_SHARE_READ | FILE_SHARE_WRITE : 0,
nullptr, dwCreationDisposition, dwFlagsAndAttributes,
nullptr );
}
CPLFree( pwszFilename );
}
else
{
hFile = CreateFile( pszFilename, dwDesiredAccess,
bShared ? FILE_SHARE_READ | FILE_SHARE_WRITE : 0,
nullptr, dwCreationDisposition, dwFlagsAndAttributes,
nullptr );
}
if( hFile == INVALID_HANDLE_VALUE )
{
nLastError = GetLastError();
const int nError = ErrnoFromGetLastError(nLastError);
if( bSetError && nError != 0 )
{
VSIError(VSIE_FileError, "%s: %s", pszFilename,
(nLastError == ERROR_SHARING_VIOLATION) ?
"file used by other process": strerror(nError));
}
errno = nError;
return nullptr;
}
/* -------------------------------------------------------------------- */
/* Create a VSI file handle. */
/* -------------------------------------------------------------------- */
VSIWin32Handle *poHandle = new VSIWin32Handle;
poHandle->hFile = hFile;
poHandle->bEOF = FALSE;
if (strchr(pszAccess, 'a') != nullptr)
poHandle->Seek(0, SEEK_END);
/* -------------------------------------------------------------------- */
/* If VSI_CACHE is set we want to use a cached reader instead */
/* of more direct io on the underlying file. */
/* -------------------------------------------------------------------- */
if( (EQUAL(pszAccess,"r") || EQUAL(pszAccess,"rb"))
&& CPLTestBool( CPLGetConfigOption( "VSI_CACHE", "FALSE" ) ) )
{
return VSICreateCachedFile( poHandle );
}
else
{
return poHandle;
}
}