#pragma region CPL License /* Nuclex Native Framework Copyright (C) 2002-2021 Nuclex Development Labs This library is free software; you can redistribute it and/or modify it under the terms of the IBM Common Public License as published by the IBM Corporation; either version 1.0 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the IBM Common Public License for more details. You should have received a copy of the IBM Common Public License along with this library */ #pragma endregion // CPL License // If the library is compiled as a DLL, this ensures symbols are exported #define NUCLEX_STORAGE_SOURCE 1 #include "WindowsFileAccessApi.h" #if defined(NUCLEX_STORAGE_WIN32) #include "../../Helpers/WindowsApi.h" #include "../../Helpers/Utf8/checked.h" namespace { // ------------------------------------------------------------------------------------------- // /// Converts a UTF-8 path into a UTF-16 path /// String containing a UTF-8 path /// The UTF-16 path with magic prefix to eliminate the path length limit std::wstring utf16FromUtf8Path(const std::string &utf8Pat) { if(utf8Pat.empty()) { return std::wstring(); } // We guess that we need as many UTF-16 characters as we needed UTF-8 characters // based on the assumption that most text will only use ascii characters. std::wstring utf16Path; utf16Path.reserve(utf8Pat.length() + 4); // According to Microsoft, this is how you lift the 260 char MAX_PATH limit. // Also skips the internal call to GetFullPathName() every API method does internally, // so paths have to be normalized and const wchar_t prefix[] = L"\\\\?\\"; utf16Path.push_back(prefix[0]); utf16Path.push_back(prefix[1]); utf16Path.push_back(prefix[2]); utf16Path.push_back(prefix[3]); // Do the conversions. If the vector was too short, it will be grown in factors // of 2 usually (depending on the standard library implementation) utf8::utf8to16(utf8Pat.begin(), utf8Pat.end(), std::back_inserter(utf16Path)); return utf16Path; } // ------------------------------------------------------------------------------------------- // } // anonymous namespace namespace Nuclex { namespace Storage { namespace FileSystem { namespace Windows { // ------------------------------------------------------------------------------------------- // HANDLE WindowsFileAccessApi::OpenFileForReading(const std::string &path) { std::wstring utf16Path = utf16FromUtf8Path(path); HANDLE fileHandle = ::CreateFileW( utf16Path.c_str(), GENERIC_READ, // desired access FILE_SHARE_READ, // share mode, nullptr, OPEN_EXISTING, // creation disposition FILE_ATTRIBUTE_NORMAL, nullptr ); if(unlikely(fileHandle == INVALID_HANDLE_VALUE)) { DWORD errorCode = ::GetLastError(); std::string errorMessage(u8"Could not open file '"); errorMessage.append(path); errorMessage.append(u8"' for reading"); Helpers::WindowsApi::ThrowExceptionForSystemError(errorMessage, errorCode); } return fileHandle; } // ------------------------------------------------------------------------------------------- // HANDLE WindowsFileAccessApi::OpenFileForWriting(const std::string &path) { std::wstring utf16Path = utf16FromUtf8Path(path); HANDLE fileHandle = ::CreateFileW( utf16Path.c_str(), GENERIC_READ | GENERIC_WRITE, // desired access 0, // share mode nullptr, OPEN_ALWAYS, // creation disposition FILE_ATTRIBUTE_NORMAL, nullptr ); if(unlikely(fileHandle == INVALID_HANDLE_VALUE)) { DWORD errorCode = ::GetLastError(); std::string errorMessage(u8"Could not open file '"); errorMessage.append(path); errorMessage.append(u8"' for writing"); Helpers::WindowsApi::ThrowExceptionForSystemError(errorMessage, errorCode); } return fileHandle; } // ------------------------------------------------------------------------------------------- // std::uint64_t WindowsFileAccessApi::SetFilePointer(HANDLE fileHandle, std::uint64_t location) { LARGE_INTEGER desiredLocation; desiredLocation.QuadPart = location; LARGE_INTEGER actualLocation = {0}; BOOL result = ::SetFilePointerEx(fileHandle, desiredLocation, &actualLocation, FILE_BEGIN); if(unlikely(result == FALSE)) { DWORD errorCode = ::GetLastError(); std::string errorMessage(u8"Could not move the file pointer"); Helpers::WindowsApi::ThrowExceptionForSystemError(errorMessage, errorCode); } return actualLocation.QuadPart; } // ------------------------------------------------------------------------------------------- // std::uint64_t WindowsFileAccessApi::GetFileSize(HANDLE fileHandle) { LARGE_INTEGER fileSize; BOOL result = ::GetFileSizeEx(fileHandle, &fileSize); if(unlikely(result == FALSE)) { DWORD errorCode = ::GetLastError(); std::string errorMessage(u8"Unable to determine file size"); Helpers::WindowsApi::ThrowExceptionForSystemError(errorMessage, errorCode); } return static_cast(fileSize.QuadPart); } // ------------------------------------------------------------------------------------------- // ::FILETIME WindowsFileAccessApi::GetLastModificationTime(HANDLE fileHandle) { BY_HANDLE_FILE_INFORMATION fileInformation; BOOL result = ::GetFileInformationByHandle(fileHandle, &fileInformation); if(unlikely(result == FALSE)) { DWORD errorCode = ::GetLastError(); std::string errorMessage(u8"Unable to determine file modification time"); Helpers::WindowsApi::ThrowExceptionForSystemError(errorMessage, errorCode); } return fileInformation.ftLastWriteTime; } // ------------------------------------------------------------------------------------------- // std::size_t WindowsFileAccessApi::Read(HANDLE fileHandle, void *buffer, std::size_t count) { DWORD desiredCount = static_cast(count); DWORD actualCount = 0; BOOL result = ::ReadFile(fileHandle, buffer, desiredCount, &actualCount, nullptr); if(unlikely(result == FALSE)) { DWORD errorCode = ::GetLastError(); std::string errorMessage(u8"Could not read data from file"); Helpers::WindowsApi::ThrowExceptionForSystemError(errorMessage, errorCode); } return static_cast(actualCount); } // ------------------------------------------------------------------------------------------- // std::size_t WindowsFileAccessApi::Write( HANDLE fileHandle, const void *buffer, std::size_t count ) { DWORD desiredCount = static_cast(count); DWORD actualCount = 0; BOOL result = ::WriteFile(fileHandle, buffer, desiredCount, &actualCount, nullptr); if(unlikely(result == FALSE)) { DWORD errorCode = ::GetLastError(); std::string errorMessage(u8"Could not write data from file"); Helpers::WindowsApi::ThrowExceptionForSystemError(errorMessage, errorCode); } return static_cast(actualCount); } // ------------------------------------------------------------------------------------------- // void WindowsFileAccessApi::FlushFileBuffers(HANDLE fileHandle) { BOOL result = ::FlushFileBuffers(fileHandle); if(unlikely(result == FALSE)) { DWORD errorCode = ::GetLastError(); std::string errorMessage(u8"Could not flush file buffers"); Helpers::WindowsApi::ThrowExceptionForSystemError(errorMessage, errorCode); } } // ------------------------------------------------------------------------------------------- // void WindowsFileAccessApi::CloseFile(HANDLE fileHandle, bool throwOnError /* = true */) { BOOL result = ::CloseHandle(fileHandle); if(throwOnError && (result == FALSE)) { DWORD errorCode = ::GetLastError(); std::string errorMessage(u8"Could not close file handle"); Helpers::WindowsApi::ThrowExceptionForSystemError(errorMessage, errorCode); } } // ------------------------------------------------------------------------------------------- // }}}} // namespace Nuclex::Storage::FileSystem::Windows #endif // defined(NUCLEX_STORAGE_WIN32)