#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)