mirror of
https://github.com/decompals/wibo.git
synced 2025-10-18 16:15:10 +00:00
WIP file handles refactor
This commit is contained in:
parent
072b3e1da9
commit
704dfd90ec
50
access_mask.cpp
Normal file
50
access_mask.cpp
Normal file
@ -0,0 +1,50 @@
|
||||
#include "access_mask.h"
|
||||
|
||||
namespace wibo::access {
|
||||
|
||||
const GenericMapping kFileGenericMapping{FILE_GENERIC_READ, FILE_GENERIC_WRITE, FILE_GENERIC_EXECUTE, FILE_ALL_ACCESS};
|
||||
|
||||
const GenericMapping kDirectoryGenericMapping{
|
||||
STANDARD_RIGHTS_READ | FILE_LIST_DIRECTORY | FILE_READ_ATTRIBUTES | FILE_READ_EA | FILE_TRAVERSE | SYNCHRONIZE,
|
||||
STANDARD_RIGHTS_WRITE | FILE_ADD_FILE | FILE_ADD_SUBDIRECTORY | FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA | SYNCHRONIZE,
|
||||
STANDARD_RIGHTS_EXECUTE | FILE_TRAVERSE | FILE_READ_ATTRIBUTES | SYNCHRONIZE,
|
||||
FILE_ALL_ACCESS | FILE_ADD_FILE | FILE_ADD_SUBDIRECTORY | FILE_TRAVERSE | FILE_DELETE_CHILD};
|
||||
|
||||
uint32_t mapGenericMask(uint32_t desiredMask, const GenericMapping &mapping) {
|
||||
uint32_t mask = desiredMask;
|
||||
if ((mask & GENERIC_ALL) != 0) {
|
||||
mask = (mask & ~GENERIC_ALL) | mapping.genericAll;
|
||||
}
|
||||
if ((mask & GENERIC_READ) != 0) {
|
||||
mask = (mask & ~GENERIC_READ) | mapping.genericRead;
|
||||
}
|
||||
if ((mask & GENERIC_WRITE) != 0) {
|
||||
mask = (mask & ~GENERIC_WRITE) | mapping.genericWrite;
|
||||
}
|
||||
if ((mask & GENERIC_EXECUTE) != 0) {
|
||||
mask = (mask & ~GENERIC_EXECUTE) | mapping.genericExecute;
|
||||
}
|
||||
return mask;
|
||||
}
|
||||
|
||||
NormalizedAccess normalizeDesiredAccess(uint32_t desiredMask, const GenericMapping &mapping, uint32_t supportedMask,
|
||||
uint32_t alwaysGrantMask, uint32_t defaultMask) {
|
||||
NormalizedAccess out{};
|
||||
uint32_t requested = mapGenericMask(desiredMask, mapping);
|
||||
if (requested == 0 && desiredMask == 0 && defaultMask != 0) {
|
||||
requested = defaultMask;
|
||||
}
|
||||
requested |= alwaysGrantMask;
|
||||
|
||||
out.requestedMask = requested;
|
||||
out.grantedMask = requested & supportedMask;
|
||||
out.deniedMask = requested & ~supportedMask;
|
||||
return out;
|
||||
}
|
||||
|
||||
// handles.cpp: normalizeDesiredAccess(...) can feed HandleTable::create so duplicates never exceed original rights.
|
||||
// files.cpp: store the NormalizedAccess::grantedMask in FileObject::maxAccessMask (or similar) for later enforcement.
|
||||
// dll/kernel32/fileapi.cpp: replace meta.grantedAccess & GENERIC_* checks with FileAccessView helpers once rights are
|
||||
// canonical.
|
||||
|
||||
} // namespace wibo::access
|
40
access_mask.h
Normal file
40
access_mask.h
Normal file
@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace wibo::access {
|
||||
|
||||
// Maps the Win32 generic access bits (GENERIC_* constants) to the
|
||||
// object-specific rights supplied in the mapping table.
|
||||
struct GenericMapping {
|
||||
uint32_t genericRead;
|
||||
uint32_t genericWrite;
|
||||
uint32_t genericExecute;
|
||||
uint32_t genericAll;
|
||||
};
|
||||
|
||||
struct NormalizedAccess {
|
||||
uint32_t requestedMask; // mask after generic expansion + implicit bits
|
||||
uint32_t grantedMask; // requested & supported
|
||||
uint32_t deniedMask; // requested & ~supported
|
||||
};
|
||||
|
||||
uint32_t mapGenericMask(uint32_t desiredMask, const GenericMapping &mapping);
|
||||
NormalizedAccess normalizeDesiredAccess(uint32_t desiredMask, const GenericMapping &mapping, uint32_t supportedMask,
|
||||
uint32_t alwaysGrantMask = 0, uint32_t defaultMask = 0);
|
||||
|
||||
inline bool containsAny(uint32_t mask, uint32_t rights) { return (mask & rights) != 0; }
|
||||
inline bool containsAll(uint32_t mask, uint32_t rights) { return (mask & rights) == rights; }
|
||||
|
||||
extern const GenericMapping kFileGenericMapping;
|
||||
extern const GenericMapping kDirectoryGenericMapping;
|
||||
|
||||
constexpr DWORD kFileSpecificRightsMask = FILE_READ_DATA | FILE_WRITE_DATA | FILE_APPEND_DATA | FILE_EXECUTE |
|
||||
FILE_READ_EA | FILE_WRITE_EA | FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES;
|
||||
constexpr DWORD kDirectorySpecificRightsMask = FILE_LIST_DIRECTORY | FILE_ADD_FILE | FILE_ADD_SUBDIRECTORY |
|
||||
FILE_TRAVERSE | FILE_DELETE_CHILD | FILE_READ_EA | FILE_WRITE_EA |
|
||||
FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES;
|
||||
|
||||
} // namespace wibo::access
|
33
common.h
33
common.h
@ -148,6 +148,39 @@ constexpr DWORD STD_INPUT_HANDLE = ((DWORD)-10);
|
||||
constexpr DWORD STD_OUTPUT_HANDLE = ((DWORD)-11);
|
||||
constexpr DWORD STD_ERROR_HANDLE = ((DWORD)-12);
|
||||
|
||||
constexpr DWORD FILE_READ_DATA = 0x00000001;
|
||||
constexpr DWORD FILE_LIST_DIRECTORY = 0x00000001;
|
||||
constexpr DWORD FILE_WRITE_DATA = 0x00000002;
|
||||
constexpr DWORD FILE_ADD_FILE = 0x00000002;
|
||||
constexpr DWORD FILE_APPEND_DATA = 0x00000004;
|
||||
constexpr DWORD FILE_ADD_SUBDIRECTORY = 0x00000004;
|
||||
constexpr DWORD FILE_CREATE_PIPE_INSTANCE = 0x00000004;
|
||||
constexpr DWORD FILE_READ_EA = 0x00000008;
|
||||
constexpr DWORD FILE_WRITE_EA = 0x00000010;
|
||||
constexpr DWORD FILE_EXECUTE = 0x00000020;
|
||||
constexpr DWORD FILE_TRAVERSE = 0x00000020;
|
||||
constexpr DWORD FILE_DELETE_CHILD = 0x00000040;
|
||||
constexpr DWORD FILE_READ_ATTRIBUTES = 0x00000080;
|
||||
constexpr DWORD FILE_WRITE_ATTRIBUTES = 0x00000100;
|
||||
|
||||
constexpr DWORD STANDARD_RIGHTS_READ = 0x00020000;
|
||||
constexpr DWORD STANDARD_RIGHTS_WRITE = 0x00020000;
|
||||
constexpr DWORD STANDARD_RIGHTS_EXECUTE = 0x00020000;
|
||||
constexpr DWORD STANDARD_RIGHTS_REQUIRED = 0x000f0000;
|
||||
constexpr DWORD SYNCHRONIZE = 0x00100000;
|
||||
|
||||
constexpr DWORD FILE_GENERIC_READ =
|
||||
STANDARD_RIGHTS_READ | FILE_READ_DATA | FILE_READ_ATTRIBUTES | FILE_READ_EA | SYNCHRONIZE;
|
||||
constexpr DWORD FILE_GENERIC_WRITE =
|
||||
STANDARD_RIGHTS_WRITE | FILE_WRITE_DATA | FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA | FILE_APPEND_DATA | SYNCHRONIZE;
|
||||
constexpr DWORD FILE_GENERIC_EXECUTE = STANDARD_RIGHTS_EXECUTE | FILE_READ_ATTRIBUTES | FILE_EXECUTE | SYNCHRONIZE;
|
||||
constexpr DWORD FILE_ALL_ACCESS = STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0x1FF;
|
||||
|
||||
constexpr DWORD GENERIC_READ = 0x80000000;
|
||||
constexpr DWORD GENERIC_WRITE = 0x40000000;
|
||||
constexpr DWORD GENERIC_EXECUTE = 0x20000000;
|
||||
constexpr DWORD GENERIC_ALL = 0x10000000;
|
||||
|
||||
struct UNICODE_STRING {
|
||||
unsigned short Length;
|
||||
unsigned short MaximumLength;
|
||||
|
@ -1,5 +1,7 @@
|
||||
#include "fileapi.h"
|
||||
|
||||
#include "access_mask.h"
|
||||
#include "common.h"
|
||||
#include "errors.h"
|
||||
#include "files.h"
|
||||
#include "handles.h"
|
||||
@ -250,20 +252,12 @@ bool tryOpenConsoleDevice(DWORD dwDesiredAccess, DWORD dwShareMode, DWORD dwCrea
|
||||
return false;
|
||||
}
|
||||
HANDLE baseHandle = files::getStdHandle(*stdHandleKind);
|
||||
auto *baseFile = files::fileHandleFromHandle(baseHandle);
|
||||
if (!baseFile) {
|
||||
wibo::lastError = ERROR_INVALID_HANDLE;
|
||||
outHandle = INVALID_HANDLE_VALUE;
|
||||
return true;
|
||||
}
|
||||
HANDLE duplicated = files::duplicateFileHandle(baseFile, false);
|
||||
if (!duplicated) {
|
||||
if (!wibo::handles().duplicateTo(baseHandle, wibo::handles(), &outHandle, dwDesiredAccess, false, 0)) {
|
||||
wibo::lastError = ERROR_INVALID_HANDLE;
|
||||
outHandle = INVALID_HANDLE_VALUE;
|
||||
return true;
|
||||
}
|
||||
wibo::lastError = ERROR_SUCCESS;
|
||||
outHandle = duplicated;
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -433,85 +427,72 @@ BOOL WIN_FUNC WriteFile(HANDLE hFile, LPCVOID lpBuffer, DWORD nNumberOfBytesToWr
|
||||
DEBUG_LOG("WriteFile(%p, %u)\n", hFile, nNumberOfBytesToWrite);
|
||||
wibo::lastError = ERROR_SUCCESS;
|
||||
|
||||
auto file = files::fileHandleFromHandle(hFile);
|
||||
if (!file || !file->fp) {
|
||||
HandleMeta meta{};
|
||||
auto file = wibo::handles().getAs<files::FileObject>(hFile, &meta);
|
||||
if (!file || !file->valid()) {
|
||||
wibo::lastError = ERROR_INVALID_HANDLE;
|
||||
return FALSE;
|
||||
}
|
||||
if ((meta.grantedAccess & FILE_WRITE_DATA) == 0) {
|
||||
wibo::lastError = ERROR_ACCESS_DENIED;
|
||||
DEBUG_LOG("!!! DENIED: 0x%x\n", meta.grantedAccess);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
bool handleOverlapped = (file->flags & FILE_FLAG_OVERLAPPED) != 0;
|
||||
auto *overlapped = reinterpret_cast<OVERLAPPED *>(lpOverlapped);
|
||||
bool usingOverlapped = overlapped != nullptr;
|
||||
if (!usingOverlapped && lpNumberOfBytesWritten == nullptr) {
|
||||
if (lpOverlapped == nullptr && lpNumberOfBytesWritten == nullptr) {
|
||||
wibo::lastError = ERROR_INVALID_PARAMETER;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
std::optional<uint64_t> offset;
|
||||
std::optional<off64_t> offset;
|
||||
bool updateFilePointer = true;
|
||||
if (usingOverlapped) {
|
||||
offset = (static_cast<uint64_t>(overlapped->Offset) | (static_cast<uint64_t>(overlapped->OffsetHigh) << 32));
|
||||
overlapped->Internal = STATUS_PENDING;
|
||||
overlapped->InternalHigh = 0;
|
||||
updateFilePointer = !handleOverlapped;
|
||||
resetOverlappedEvent(overlapped);
|
||||
if (lpOverlapped != nullptr) {
|
||||
offset = static_cast<off64_t>((static_cast<uint64_t>(lpOverlapped->Offset)) |
|
||||
(static_cast<uint64_t>(lpOverlapped->OffsetHigh) << 32));
|
||||
lpOverlapped->Internal = STATUS_PENDING;
|
||||
lpOverlapped->InternalHigh = 0;
|
||||
updateFilePointer = !file->overlapped;
|
||||
resetOverlappedEvent(lpOverlapped);
|
||||
}
|
||||
|
||||
auto io = files::write(file, lpBuffer, nNumberOfBytesToWrite, offset, updateFilePointer);
|
||||
auto io = files::write(file.get(), lpBuffer, nNumberOfBytesToWrite, offset, updateFilePointer);
|
||||
DWORD completionStatus = STATUS_SUCCESS;
|
||||
if (io.unixError != 0) {
|
||||
completionStatus = wibo::winErrorFromErrno(io.unixError);
|
||||
wibo::lastError = completionStatus;
|
||||
completionStatus = wibo::statusFromErrno(io.unixError);
|
||||
wibo::lastError = wibo::winErrorFromErrno(io.unixError);
|
||||
if (lpNumberOfBytesWritten) {
|
||||
*lpNumberOfBytesWritten = static_cast<DWORD>(io.bytesTransferred);
|
||||
*lpNumberOfBytesWritten = io.bytesTransferred;
|
||||
}
|
||||
if (usingOverlapped) {
|
||||
overlapped->Internal = completionStatus;
|
||||
overlapped->InternalHigh = io.bytesTransferred;
|
||||
signalOverlappedEvent(overlapped);
|
||||
if (lpOverlapped != nullptr) {
|
||||
lpOverlapped->Internal = completionStatus;
|
||||
lpOverlapped->InternalHigh = io.bytesTransferred;
|
||||
signalOverlappedEvent(lpOverlapped);
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (lpNumberOfBytesWritten && (!handleOverlapped || !usingOverlapped)) {
|
||||
if (lpNumberOfBytesWritten && (!file->overlapped || lpOverlapped == nullptr)) {
|
||||
*lpNumberOfBytesWritten = static_cast<DWORD>(io.bytesTransferred);
|
||||
}
|
||||
|
||||
if (usingOverlapped) {
|
||||
overlapped->Internal = completionStatus;
|
||||
overlapped->InternalHigh = io.bytesTransferred;
|
||||
if (!handleOverlapped) {
|
||||
uint64_t baseOffset = offset.value_or(0);
|
||||
uint64_t newOffset = baseOffset + io.bytesTransferred;
|
||||
overlapped->Offset = static_cast<DWORD>(newOffset & 0xFFFFFFFFu);
|
||||
overlapped->OffsetHigh = static_cast<DWORD>(newOffset >> 32);
|
||||
}
|
||||
signalOverlappedEvent(overlapped);
|
||||
if (lpOverlapped != nullptr) {
|
||||
lpOverlapped->Internal = completionStatus;
|
||||
lpOverlapped->InternalHigh = io.bytesTransferred;
|
||||
signalOverlappedEvent(lpOverlapped);
|
||||
}
|
||||
|
||||
return (io.bytesTransferred == nNumberOfBytesToWrite) ? TRUE : FALSE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL WIN_FUNC FlushFileBuffers(HANDLE hFile) {
|
||||
DEBUG_LOG("FlushFileBuffers(%p)\n", hFile);
|
||||
auto data = handles::dataFromHandle(hFile, false);
|
||||
if (data.type != handles::TYPE_FILE || data.ptr == nullptr) {
|
||||
auto file = wibo::handles().getAs<files::FileObject>(hFile);
|
||||
if (!file || !file->valid()) {
|
||||
wibo::lastError = ERROR_INVALID_HANDLE;
|
||||
return FALSE;
|
||||
}
|
||||
auto file = reinterpret_cast<files::FileHandle *>(data.ptr);
|
||||
if (!file || !file->fp) {
|
||||
wibo::lastError = ERROR_INVALID_HANDLE;
|
||||
return FALSE;
|
||||
}
|
||||
FILE *fp = file->fp;
|
||||
if (fflush(fp) != 0) {
|
||||
wibo::lastError = ERROR_ACCESS_DENIED;
|
||||
return FALSE;
|
||||
}
|
||||
int fd = file->fd;
|
||||
if (fd >= 0 && fsync(fd) != 0) {
|
||||
wibo::lastError = ERROR_ACCESS_DENIED;
|
||||
if (fsync(file->host_fd) != 0) {
|
||||
setLastErrorFromErrno();
|
||||
return FALSE;
|
||||
}
|
||||
wibo::lastError = ERROR_SUCCESS;
|
||||
@ -523,64 +504,58 @@ BOOL WIN_FUNC ReadFile(HANDLE hFile, LPVOID lpBuffer, DWORD nNumberOfBytesToRead
|
||||
DEBUG_LOG("ReadFile(%p, %u)\n", hFile, nNumberOfBytesToRead);
|
||||
wibo::lastError = ERROR_SUCCESS;
|
||||
|
||||
auto file = files::fileHandleFromHandle(hFile);
|
||||
if (!file || !file->fp) {
|
||||
HandleMeta meta{};
|
||||
auto file = wibo::handles().getAs<files::FileObject>(hFile, &meta);
|
||||
if (!file || !file->valid()) {
|
||||
wibo::lastError = ERROR_INVALID_HANDLE;
|
||||
return FALSE;
|
||||
}
|
||||
if ((meta.grantedAccess & FILE_READ_DATA) == 0) {
|
||||
wibo::lastError = ERROR_ACCESS_DENIED;
|
||||
DEBUG_LOG("!!! DENIED: 0x%x\n", meta.grantedAccess);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
bool handleOverlapped = (file->flags & FILE_FLAG_OVERLAPPED) != 0;
|
||||
auto *overlapped = reinterpret_cast<OVERLAPPED *>(lpOverlapped);
|
||||
bool usingOverlapped = overlapped != nullptr;
|
||||
if (!usingOverlapped && lpNumberOfBytesRead == nullptr) {
|
||||
if (lpOverlapped == nullptr && lpNumberOfBytesRead == nullptr) {
|
||||
wibo::lastError = ERROR_INVALID_PARAMETER;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
std::optional<uint64_t> offset;
|
||||
std::optional<off64_t> offset;
|
||||
bool updateFilePointer = true;
|
||||
if (usingOverlapped) {
|
||||
offset = (static_cast<uint64_t>(overlapped->Offset) | (static_cast<uint64_t>(overlapped->OffsetHigh) << 32));
|
||||
overlapped->Internal = STATUS_PENDING;
|
||||
overlapped->InternalHigh = 0;
|
||||
updateFilePointer = !handleOverlapped;
|
||||
resetOverlappedEvent(overlapped);
|
||||
if (lpOverlapped != nullptr) {
|
||||
offset = static_cast<off64_t>((static_cast<uint64_t>(lpOverlapped->Offset)) |
|
||||
(static_cast<uint64_t>(lpOverlapped->OffsetHigh) << 32));
|
||||
lpOverlapped->Internal = STATUS_PENDING;
|
||||
lpOverlapped->InternalHigh = 0;
|
||||
updateFilePointer = !file->overlapped;
|
||||
resetOverlappedEvent(lpOverlapped);
|
||||
}
|
||||
|
||||
auto io = files::read(file, lpBuffer, nNumberOfBytesToRead, offset, updateFilePointer);
|
||||
auto io = files::read(file.get(), lpBuffer, nNumberOfBytesToRead, offset, updateFilePointer);
|
||||
DWORD completionStatus = STATUS_SUCCESS;
|
||||
if (io.unixError != 0) {
|
||||
completionStatus = wibo::winErrorFromErrno(io.unixError);
|
||||
wibo::lastError = completionStatus;
|
||||
completionStatus = wibo::statusFromErrno(io.unixError);
|
||||
wibo::lastError = wibo::winErrorFromErrno(io.unixError);
|
||||
if (lpNumberOfBytesRead) {
|
||||
*lpNumberOfBytesRead = static_cast<DWORD>(io.bytesTransferred);
|
||||
}
|
||||
if (usingOverlapped) {
|
||||
overlapped->Internal = completionStatus;
|
||||
overlapped->InternalHigh = io.bytesTransferred;
|
||||
signalOverlappedEvent(overlapped);
|
||||
if (lpOverlapped != nullptr) {
|
||||
lpOverlapped->Internal = completionStatus;
|
||||
lpOverlapped->InternalHigh = io.bytesTransferred;
|
||||
signalOverlappedEvent(lpOverlapped);
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (io.reachedEnd && io.bytesTransferred == 0 && handleOverlapped) {
|
||||
completionStatus = ERROR_HANDLE_EOF;
|
||||
}
|
||||
|
||||
if (lpNumberOfBytesRead && (!handleOverlapped || !usingOverlapped)) {
|
||||
if (lpNumberOfBytesRead && (!file->overlapped || lpOverlapped == nullptr)) {
|
||||
*lpNumberOfBytesRead = static_cast<DWORD>(io.bytesTransferred);
|
||||
}
|
||||
|
||||
if (usingOverlapped) {
|
||||
overlapped->Internal = completionStatus;
|
||||
overlapped->InternalHigh = io.bytesTransferred;
|
||||
if (!handleOverlapped) {
|
||||
uint64_t baseOffset = offset.value_or(0);
|
||||
uint64_t newOffset = baseOffset + io.bytesTransferred;
|
||||
overlapped->Offset = static_cast<DWORD>(newOffset & 0xFFFFFFFFu);
|
||||
overlapped->OffsetHigh = static_cast<DWORD>(newOffset >> 32);
|
||||
}
|
||||
signalOverlappedEvent(overlapped);
|
||||
if (lpOverlapped != nullptr) {
|
||||
lpOverlapped->Internal = completionStatus;
|
||||
lpOverlapped->InternalHigh = io.bytesTransferred;
|
||||
signalOverlappedEvent(lpOverlapped);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
@ -589,6 +564,7 @@ BOOL WIN_FUNC ReadFile(HANDLE hFile, LPVOID lpBuffer, DWORD nNumberOfBytesToRead
|
||||
HANDLE WIN_FUNC CreateFileA(LPCSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode,
|
||||
LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition,
|
||||
DWORD dwFlagsAndAttributes, HANDLE hTemplateFile) {
|
||||
(void)dwShareMode;
|
||||
(void)lpSecurityAttributes;
|
||||
(void)hTemplateFile;
|
||||
if (!lpFileName) {
|
||||
@ -646,27 +622,28 @@ HANDLE WIN_FUNC CreateFileA(LPCSTR lpFileName, DWORD dwDesiredAccess, DWORD dwSh
|
||||
assert(false);
|
||||
}
|
||||
|
||||
FILE *fp = nullptr;
|
||||
int fd = -1;
|
||||
if (dwDesiredAccess == GENERIC_READ) {
|
||||
fp = fopen(path.c_str(), "rb");
|
||||
fd = open(path.c_str(), O_RDONLY);
|
||||
} else if (dwDesiredAccess == GENERIC_WRITE) {
|
||||
if (shouldTruncate || !fileExists) {
|
||||
fp = fopen(path.c_str(), "wb");
|
||||
fd = open(path.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0666);
|
||||
} else {
|
||||
fp = fopen(path.c_str(), "rb+");
|
||||
fd = open(path.c_str(), O_RDWR);
|
||||
}
|
||||
} else if (dwDesiredAccess == (GENERIC_READ | GENERIC_WRITE)) {
|
||||
if (shouldTruncate || !fileExists) {
|
||||
fp = fopen(path.c_str(), "wb+");
|
||||
fd = open(path.c_str(), O_RDWR | O_CREAT | O_TRUNC, 0666);
|
||||
} else {
|
||||
fp = fopen(path.c_str(), "rb+");
|
||||
fd = open(path.c_str(), O_RDWR);
|
||||
}
|
||||
} else {
|
||||
assert(false);
|
||||
}
|
||||
|
||||
if (fp) {
|
||||
void *handle = files::allocFpHandle(fp, dwDesiredAccess, dwShareMode, dwFlagsAndAttributes, true);
|
||||
if (fd >= 0) {
|
||||
bool overlapped = (dwFlagsAndAttributes & FILE_FLAG_OVERLAPPED) != 0;
|
||||
auto handle = wibo::handles().create(new files::FileObject(fd, overlapped), dwDesiredAccess, 0);
|
||||
DEBUG_LOG("-> %p\n", handle);
|
||||
return handle;
|
||||
}
|
||||
@ -683,13 +660,6 @@ HANDLE WIN_FUNC CreateFileW(LPCWSTR lpFileName, DWORD dwDesiredAccess, DWORD dwS
|
||||
return INVALID_HANDLE_VALUE;
|
||||
}
|
||||
std::string lpFileNameA = wideStringToString(lpFileName);
|
||||
HANDLE consoleHandle = INVALID_HANDLE_VALUE;
|
||||
if (tryOpenConsoleDevice(dwDesiredAccess, dwShareMode, dwCreationDisposition, dwFlagsAndAttributes, consoleHandle,
|
||||
lpFileNameA)) {
|
||||
DEBUG_LOG("CreateFileW(console=%s, desiredAccess=0x%x, shareMode=%u, flags=0x%x) -> %p\n", lpFileNameA.c_str(),
|
||||
dwDesiredAccess, dwShareMode, dwFlagsAndAttributes, consoleHandle);
|
||||
return consoleHandle;
|
||||
}
|
||||
return CreateFileA(lpFileNameA.c_str(), dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition,
|
||||
dwFlagsAndAttributes, hTemplateFile);
|
||||
}
|
||||
@ -912,13 +882,19 @@ DWORD WIN_FUNC GetFileSize(HANDLE hFile, LPDWORD lpFileSizeHigh) {
|
||||
BOOL WIN_FUNC GetFileTime(HANDLE hFile, LPFILETIME lpCreationTime, LPFILETIME lpLastAccessTime,
|
||||
LPFILETIME lpLastWriteTime) {
|
||||
DEBUG_LOG("GetFileTime(%p, %p, %p, %p)\n", hFile, lpCreationTime, lpLastAccessTime, lpLastWriteTime);
|
||||
auto file = files::fileHandleFromHandle(hFile);
|
||||
if (!file || !file->fp) {
|
||||
HandleMeta meta{};
|
||||
auto file = wibo::handles().getAs<files::FileObject>(hFile, &meta);
|
||||
if (!file || !file->valid()) {
|
||||
wibo::lastError = ERROR_INVALID_HANDLE;
|
||||
return FALSE;
|
||||
}
|
||||
if ((meta.grantedAccess & FILE_READ_ATTRIBUTES) == 0) {
|
||||
wibo::lastError = ERROR_ACCESS_DENIED;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
struct stat st{};
|
||||
if (fstat(fileno(file->fp), &st) != 0) {
|
||||
if (fstat(file->host_fd, &st) != 0) {
|
||||
setLastErrorFromErrno();
|
||||
return FALSE;
|
||||
}
|
||||
@ -946,16 +922,17 @@ BOOL WIN_FUNC GetFileTime(HANDLE hFile, LPFILETIME lpCreationTime, LPFILETIME lp
|
||||
BOOL WIN_FUNC SetFileTime(HANDLE hFile, const FILETIME *lpCreationTime, const FILETIME *lpLastAccessTime,
|
||||
const FILETIME *lpLastWriteTime) {
|
||||
DEBUG_LOG("SetFileTime(%p, %p, %p, %p)\n", hFile, lpCreationTime, lpLastAccessTime, lpLastWriteTime);
|
||||
FILE *fp = files::fpFromHandle(hFile);
|
||||
if (!fp) {
|
||||
HandleMeta meta{};
|
||||
auto file = wibo::handles().getAs<files::FileObject>(hFile, &meta);
|
||||
if (!file || !file->valid()) {
|
||||
wibo::lastError = ERROR_INVALID_HANDLE;
|
||||
return FALSE;
|
||||
}
|
||||
int fd = fileno(fp);
|
||||
if (fd < 0) {
|
||||
setLastErrorFromErrno();
|
||||
if ((meta.grantedAccess & FILE_WRITE_ATTRIBUTES) == 0) {
|
||||
wibo::lastError = ERROR_ACCESS_DENIED;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
bool changeAccess = !shouldIgnoreFileTimeParam(lpLastAccessTime);
|
||||
bool changeWrite = !shouldIgnoreFileTimeParam(lpLastWriteTime);
|
||||
if (!changeAccess && !changeWrite) {
|
||||
@ -963,7 +940,7 @@ BOOL WIN_FUNC SetFileTime(HANDLE hFile, const FILETIME *lpCreationTime, const FI
|
||||
return TRUE;
|
||||
}
|
||||
struct stat st{};
|
||||
if (fstat(fd, &st) != 0) {
|
||||
if (fstat(file->host_fd, &st) != 0) {
|
||||
setLastErrorFromErrno();
|
||||
return FALSE;
|
||||
}
|
||||
@ -999,7 +976,7 @@ BOOL WIN_FUNC SetFileTime(HANDLE hFile, const FILETIME *lpCreationTime, const FI
|
||||
}
|
||||
#else
|
||||
struct timespec times[2] = {accessSpec, writeSpec};
|
||||
if (futimens(fd, times) != 0) {
|
||||
if (futimens(file->host_fd, times) != 0) {
|
||||
setLastErrorFromErrno();
|
||||
return FALSE;
|
||||
}
|
||||
@ -1047,14 +1024,14 @@ BOOL WIN_FUNC GetFileInformationByHandle(HANDLE hFile, LPBY_HANDLE_FILE_INFORMAT
|
||||
|
||||
DWORD WIN_FUNC GetFileType(HANDLE hFile) {
|
||||
DEBUG_LOG("GetFileType(%p) ", hFile);
|
||||
auto *file = files::fileHandleFromHandle(hFile);
|
||||
if (!file || file->fd < 0) {
|
||||
auto file = wibo::handles().getAs<files::FileObject>(hFile);
|
||||
if (!file || !file->valid()) {
|
||||
wibo::lastError = ERROR_INVALID_HANDLE;
|
||||
DEBUG_LOG("-> ERROR_INVALID_HANDLE\n");
|
||||
return FILE_TYPE_UNKNOWN;
|
||||
}
|
||||
struct stat st{};
|
||||
if (fstat(file->fd, &st) != 0) {
|
||||
if (fstat(file->host_fd, &st) != 0) {
|
||||
setLastErrorFromErrno();
|
||||
DEBUG_LOG("-> fstat error\n");
|
||||
return FILE_TYPE_UNKNOWN;
|
||||
|
@ -19,11 +19,6 @@ struct BY_HANDLE_FILE_INFORMATION {
|
||||
using PBY_HANDLE_FILE_INFORMATION = BY_HANDLE_FILE_INFORMATION *;
|
||||
using LPBY_HANDLE_FILE_INFORMATION = BY_HANDLE_FILE_INFORMATION *;
|
||||
|
||||
constexpr DWORD GENERIC_READ = 0x80000000;
|
||||
constexpr DWORD GENERIC_WRITE = 0x40000000;
|
||||
constexpr DWORD GENERIC_EXECUTE = 0x20000000;
|
||||
constexpr DWORD GENERIC_ALL = 0x10000000;
|
||||
|
||||
constexpr DWORD FILE_SHARE_READ = 0x00000001;
|
||||
constexpr DWORD FILE_SHARE_WRITE = 0x00000002;
|
||||
constexpr DWORD FILE_SHARE_DELETE = 0x00000004;
|
||||
|
@ -50,4 +50,8 @@ NTSTATUS statusFromWinError(DWORD error) {
|
||||
}
|
||||
}
|
||||
|
||||
NTSTATUS statusFromErrno(int err) {
|
||||
return statusFromWinError(winErrorFromErrno(err));
|
||||
}
|
||||
|
||||
} // namespace wibo
|
||||
|
1
errors.h
1
errors.h
@ -59,4 +59,5 @@ typedef int HRESULT;
|
||||
namespace wibo {
|
||||
DWORD winErrorFromErrno(int err);
|
||||
NTSTATUS statusFromWinError(DWORD error);
|
||||
NTSTATUS statusFromErrno(int err);
|
||||
} // namespace wibo
|
||||
|
220
files.cpp
220
files.cpp
@ -1,13 +1,17 @@
|
||||
#include "common.h"
|
||||
#include "files.h"
|
||||
#include "common.h"
|
||||
#include "handles.h"
|
||||
#include "strutil.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cerrno>
|
||||
#include <climits>
|
||||
#include <cstdio>
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include <strings.h>
|
||||
#include <string>
|
||||
#include <strings.h>
|
||||
#include <unistd.h>
|
||||
|
||||
namespace files {
|
||||
|
||||
@ -30,10 +34,10 @@ namespace files {
|
||||
|
||||
static std::string toWindowsPathEntry(const std::string &entry) {
|
||||
if (entry.empty()) {
|
||||
return std::string();
|
||||
return {};
|
||||
}
|
||||
bool looksWindows = entry.find('\\') != std::string::npos ||
|
||||
(entry.size() >= 2 && entry[1] == ':' && entry[0] != '/');
|
||||
bool looksWindows =
|
||||
entry.find('\\') != std::string::npos || (entry.size() >= 2 && entry[1] == ':' && entry[0] != '/');
|
||||
if (looksWindows) {
|
||||
std::string normalized = entry;
|
||||
std::replace(normalized.begin(), normalized.end(), '/', '\\');
|
||||
@ -44,7 +48,7 @@ namespace files {
|
||||
|
||||
static std::string toHostPathEntry(const std::string &entry) {
|
||||
if (entry.empty()) {
|
||||
return std::string();
|
||||
return {};
|
||||
}
|
||||
auto converted = pathFromWindows(entry.c_str());
|
||||
if (!converted.empty()) {
|
||||
@ -85,7 +89,8 @@ namespace files {
|
||||
bool followingExisting = true;
|
||||
for (const auto &component : path) {
|
||||
std::filesystem::path newPath2 = newPath / component;
|
||||
if (followingExisting && !std::filesystem::exists(newPath2) && (component != ".." && component != "." && component != "")) {
|
||||
if (followingExisting && !std::filesystem::exists(newPath2) &&
|
||||
(component != ".." && component != "." && component != "")) {
|
||||
followingExisting = false;
|
||||
try {
|
||||
for (std::filesystem::path entry : std::filesystem::directory_iterator{newPath}) {
|
||||
@ -121,50 +126,40 @@ namespace files {
|
||||
return str;
|
||||
}
|
||||
|
||||
FileHandle *fileHandleFromHandle(void *handle) {
|
||||
handles::Data data = handles::dataFromHandle(handle, false);
|
||||
if (data.type == handles::TYPE_FILE) {
|
||||
return reinterpret_cast<FileHandle *>(data.ptr);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
// FileHandle *fileHandleFromHandle(void *handle) {
|
||||
// handles::Data data = handles::dataFromHandle(handle, false);
|
||||
// if (data.type == handles::TYPE_FILE) {
|
||||
// return reinterpret_cast<FileHandle *>(data.ptr);
|
||||
// }
|
||||
// return nullptr;
|
||||
// }
|
||||
|
||||
FILE *fpFromHandle(void *handle, bool pop) {
|
||||
handles::Data data = handles::dataFromHandle(handle, pop);
|
||||
if (data.type == handles::TYPE_FILE) {
|
||||
return reinterpret_cast<FileHandle *>(data.ptr)->fp;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
// FILE *fpFromHandle(void *handle, bool pop) {
|
||||
// handles::Data data = handles::dataFromHandle(handle, pop);
|
||||
// if (data.type == handles::TYPE_FILE) {
|
||||
// return reinterpret_cast<FileHandle *>(data.ptr)->fp;
|
||||
// }
|
||||
// return nullptr;
|
||||
// }
|
||||
|
||||
void *allocFpHandle(FILE *fp, unsigned int desiredAccess, unsigned int shareMode, unsigned int flags, bool closeOnDestroy) {
|
||||
auto *handle = new FileHandle();
|
||||
handle->fp = fp;
|
||||
handle->fd = (fp ? fileno(fp) : -1);
|
||||
handle->desiredAccess = desiredAccess;
|
||||
handle->shareMode = shareMode;
|
||||
handle->flags = flags;
|
||||
handle->closeOnDestroy = closeOnDestroy;
|
||||
return handles::allocDataHandle({handles::TYPE_FILE, handle, 0});
|
||||
}
|
||||
// void *duplicateFileHandle(FileHandle *source, bool closeOnDestroy) {
|
||||
// if (!source) {
|
||||
// return nullptr;
|
||||
// }
|
||||
// auto *clone = new FileHandle();
|
||||
// clone->fp = source->fp;
|
||||
// clone->fd = source->fd;
|
||||
// clone->desiredAccess = source->desiredAccess;
|
||||
// clone->shareMode = source->shareMode;
|
||||
// clone->flags = source->flags;
|
||||
// clone->closeOnDestroy = closeOnDestroy;
|
||||
// return handles::allocDataHandle({handles::TYPE_FILE, clone, 0});
|
||||
// }
|
||||
|
||||
void *duplicateFileHandle(FileHandle *source, bool closeOnDestroy) {
|
||||
if (!source) {
|
||||
return nullptr;
|
||||
}
|
||||
auto *clone = new FileHandle();
|
||||
clone->fp = source->fp;
|
||||
clone->fd = source->fd;
|
||||
clone->desiredAccess = source->desiredAccess;
|
||||
clone->shareMode = source->shareMode;
|
||||
clone->flags = source->flags;
|
||||
clone->closeOnDestroy = closeOnDestroy;
|
||||
return handles::allocDataHandle({handles::TYPE_FILE, clone, 0});
|
||||
}
|
||||
|
||||
IOResult read(FileHandle *handle, void *buffer, size_t bytesToRead, const std::optional<uint64_t> &offset, bool updateFilePointer) {
|
||||
IOResult read(FileObject *file, void *buffer, size_t bytesToRead, const std::optional<off64_t> &offset,
|
||||
bool updateFilePointer) {
|
||||
IOResult result{};
|
||||
if (!handle || !handle->fp) {
|
||||
if (!file || !file->valid()) {
|
||||
result.unixError = EBADF;
|
||||
return result;
|
||||
}
|
||||
@ -172,71 +167,54 @@ namespace files {
|
||||
return result;
|
||||
}
|
||||
|
||||
bool useOffset = offset.has_value();
|
||||
if (useOffset && !updateFilePointer && handle->fd >= 0) {
|
||||
// Sanity check: if no offset is given, we must update the file pointer
|
||||
assert(offset.has_value() || updateFilePointer);
|
||||
|
||||
const auto doRead = [&](off64_t pos) {
|
||||
size_t total = 0;
|
||||
size_t remaining = bytesToRead;
|
||||
off_t pos = static_cast<off_t>(*offset);
|
||||
uint8_t *in = static_cast<uint8_t *>(buffer);
|
||||
while (remaining > 0) {
|
||||
ssize_t rc = pread(handle->fd, static_cast<uint8_t *>(buffer) + total, remaining, pos);
|
||||
size_t chunk = remaining > SSIZE_MAX ? SSIZE_MAX : remaining;
|
||||
ssize_t rc = pread64(file->host_fd, in + total, chunk, pos);
|
||||
if (rc == -1) {
|
||||
if (errno == EINTR) {
|
||||
continue;
|
||||
}
|
||||
result.bytesTransferred = total;
|
||||
result.unixError = errno ? errno : EIO;
|
||||
return result;
|
||||
return;
|
||||
}
|
||||
if (rc == 0) {
|
||||
result.bytesTransferred = total;
|
||||
result.reachedEnd = true;
|
||||
return result;
|
||||
return;
|
||||
}
|
||||
total += static_cast<size_t>(rc);
|
||||
remaining -= static_cast<size_t>(rc);
|
||||
pos += rc;
|
||||
}
|
||||
result.bytesTransferred = total;
|
||||
};
|
||||
|
||||
if (updateFilePointer || !offset.has_value()) {
|
||||
std::unique_lock<std::mutex> lock(file->pos_mu);
|
||||
off64_t pos = offset.value_or(file->file_pos);
|
||||
doRead(pos);
|
||||
if (updateFilePointer) {
|
||||
file->file_pos = pos + static_cast<off64_t>(result.bytesTransferred);
|
||||
}
|
||||
} else {
|
||||
doRead(*offset);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
off_t originalPos = -1;
|
||||
std::unique_lock<std::mutex> lock(handle->mutex);
|
||||
if (useOffset) {
|
||||
originalPos = ftello(handle->fp);
|
||||
if (!updateFilePointer && originalPos == -1) {
|
||||
result.unixError = errno ? errno : ESPIPE;
|
||||
return result;
|
||||
}
|
||||
if (fseeko(handle->fp, static_cast<off_t>(*offset), SEEK_SET) != 0) {
|
||||
result.unixError = errno ? errno : EINVAL;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
size_t readCount = fread(buffer, 1, bytesToRead, handle->fp);
|
||||
result.bytesTransferred = readCount;
|
||||
if (readCount < bytesToRead) {
|
||||
if (feof(handle->fp)) {
|
||||
result.reachedEnd = true;
|
||||
clearerr(handle->fp);
|
||||
} else if (ferror(handle->fp)) {
|
||||
result.unixError = errno ? errno : EIO;
|
||||
clearerr(handle->fp);
|
||||
}
|
||||
}
|
||||
|
||||
if (useOffset && !updateFilePointer) {
|
||||
if (originalPos != -1) {
|
||||
fseeko(handle->fp, originalPos, SEEK_SET);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
IOResult write(FileHandle *handle, const void *buffer, size_t bytesToWrite, const std::optional<uint64_t> &offset, bool updateFilePointer) {
|
||||
IOResult write(FileObject *handle, const void *buffer, size_t bytesToWrite, const std::optional<off64_t> &offset,
|
||||
bool updateFilePointer) {
|
||||
IOResult result{};
|
||||
if (!handle || !handle->fp) {
|
||||
if (!handle || handle->host_fd < 0) {
|
||||
result.unixError = EBADF;
|
||||
return result;
|
||||
}
|
||||
@ -244,55 +222,46 @@ namespace files {
|
||||
return result;
|
||||
}
|
||||
|
||||
bool useOffset = offset.has_value();
|
||||
if (useOffset && !updateFilePointer && handle->fd >= 0) {
|
||||
// Sanity check: if no offset is given, we must update the file pointer
|
||||
assert(offset.has_value() || updateFilePointer);
|
||||
|
||||
auto doWrite = [&](off64_t pos) {
|
||||
size_t total = 0;
|
||||
size_t remaining = bytesToWrite;
|
||||
off_t pos = static_cast<off_t>(*offset);
|
||||
const uint8_t *in = static_cast<const uint8_t *>(buffer);
|
||||
while (remaining > 0) {
|
||||
ssize_t rc = pwrite(handle->fd, static_cast<const uint8_t *>(buffer) + total, remaining, pos);
|
||||
size_t chunk = remaining > SSIZE_MAX ? SSIZE_MAX : remaining;
|
||||
ssize_t rc = pwrite64(handle->host_fd, in + total, chunk, pos);
|
||||
if (rc == -1) {
|
||||
if (errno == EINTR) {
|
||||
continue;
|
||||
}
|
||||
result.bytesTransferred = total;
|
||||
result.unixError = errno ? errno : EIO;
|
||||
return result;
|
||||
return;
|
||||
}
|
||||
if (rc == 0) {
|
||||
result.bytesTransferred = total;
|
||||
return;
|
||||
}
|
||||
total += static_cast<size_t>(rc);
|
||||
remaining -= static_cast<size_t>(rc);
|
||||
pos += rc;
|
||||
}
|
||||
result.bytesTransferred = total;
|
||||
return result;
|
||||
};
|
||||
|
||||
if (updateFilePointer || !offset.has_value()) {
|
||||
std::unique_lock<std::mutex> lock(handle->pos_mu);
|
||||
const off64_t pos = handle->file_pos;
|
||||
doWrite(pos);
|
||||
if (updateFilePointer) {
|
||||
handle->file_pos = pos + static_cast<off64_t>(result.bytesTransferred);
|
||||
}
|
||||
} else {
|
||||
doWrite(*offset);
|
||||
}
|
||||
|
||||
off_t originalPos = -1;
|
||||
std::unique_lock<std::mutex> lock(handle->mutex);
|
||||
if (useOffset) {
|
||||
originalPos = ftello(handle->fp);
|
||||
if (!updateFilePointer && originalPos == -1) {
|
||||
result.unixError = errno ? errno : ESPIPE;
|
||||
return result;
|
||||
}
|
||||
if (fseeko(handle->fp, static_cast<off_t>(*offset), SEEK_SET) != 0) {
|
||||
result.unixError = errno ? errno : EINVAL;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
size_t writeCount = fwrite(buffer, 1, bytesToWrite, handle->fp);
|
||||
result.bytesTransferred = writeCount;
|
||||
if (writeCount < bytesToWrite) {
|
||||
result.unixError = errno ? errno : EIO;
|
||||
clearerr(handle->fp);
|
||||
}
|
||||
|
||||
if (useOffset && !updateFilePointer) {
|
||||
if (originalPos != -1) {
|
||||
fseeko(handle->fp, originalPos, SEEK_SET);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -327,9 +296,10 @@ namespace files {
|
||||
}
|
||||
|
||||
void init() {
|
||||
stdinHandle = allocFpHandle(stdin, 0, 0, 0, false);
|
||||
stdoutHandle = allocFpHandle(stdout, 0, 0, 0, false);
|
||||
stderrHandle = allocFpHandle(stderr, 0, 0, 0, false);
|
||||
auto &handles = wibo::handles();
|
||||
stdinHandle = handles.create(new FileObject(STDIN_FILENO, false), FILE_GENERIC_READ, 0);
|
||||
stdoutHandle = handles.create(new FileObject(STDOUT_FILENO, false), FILE_GENERIC_WRITE, 0);
|
||||
stderrHandle = handles.create(new FileObject(STDERR_FILENO, false), FILE_GENERIC_WRITE, 0);
|
||||
}
|
||||
|
||||
std::optional<std::filesystem::path> findCaseInsensitiveFile(const std::filesystem::path &directory,
|
||||
@ -403,4 +373,4 @@ namespace files {
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
} // namespace files
|
||||
|
35
files.h
35
files.h
@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "common.h"
|
||||
#include "handles.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include <filesystem>
|
||||
@ -9,14 +10,22 @@
|
||||
#include <string>
|
||||
|
||||
namespace files {
|
||||
struct FileHandle {
|
||||
FILE *fp = nullptr;
|
||||
int fd = -1;
|
||||
unsigned int desiredAccess = 0;
|
||||
unsigned int shareMode = 0;
|
||||
unsigned int flags = 0;
|
||||
bool closeOnDestroy = true;
|
||||
std::mutex mutex;
|
||||
struct FileObject : ObjectHeader {
|
||||
int host_fd = -1; // use pread/pwrite
|
||||
std::mutex pos_mu; // sync for file_pos w/o overlapped
|
||||
off64_t file_pos = 0; // shared across duplicated handles
|
||||
bool overlapped = false; // set from FILE_FLAG_OVERLAPPED
|
||||
|
||||
explicit FileObject(int fd, bool ov)
|
||||
: ObjectHeader(ObjectType::File), host_fd(fd), overlapped(ov) {}
|
||||
virtual ~FileObject() {
|
||||
if (host_fd != -1) {
|
||||
close(host_fd);
|
||||
host_fd = -1;
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] bool valid() const { return host_fd >= 0; }
|
||||
};
|
||||
|
||||
struct IOResult {
|
||||
@ -28,11 +37,11 @@ namespace files {
|
||||
std::filesystem::path pathFromWindows(const char *inStr);
|
||||
std::string pathToWindows(const std::filesystem::path &path);
|
||||
void *allocFpHandle(FILE *fp, unsigned int desiredAccess = 0, unsigned int shareMode = 0, unsigned int flags = 0, bool closeOnDestroy = true);
|
||||
void *duplicateFileHandle(FileHandle *handle, bool closeOnDestroy);
|
||||
// void *duplicateFileHandle(FileHandle *handle, bool closeOnDestroy);
|
||||
FILE *fpFromHandle(void *handle, bool pop = false);
|
||||
FileHandle *fileHandleFromHandle(void *handle);
|
||||
IOResult read(FileHandle *handle, void *buffer, size_t bytesToRead, const std::optional<uint64_t> &offset, bool updateFilePointer);
|
||||
IOResult write(FileHandle *handle, const void *buffer, size_t bytesToWrite, const std::optional<uint64_t> &offset, bool updateFilePointer);
|
||||
// FileHandle *fileHandleFromHandle(void *handle);
|
||||
IOResult read(FileObject *handle, void *buffer, size_t bytesToRead, const std::optional<uint64_t> &offset, bool updateFilePointer);
|
||||
IOResult write(FileObject *handle, const void *buffer, size_t bytesToWrite, const std::optional<uint64_t> &offset, bool updateFilePointer);
|
||||
HANDLE getStdHandle(DWORD nStdHandle);
|
||||
BOOL setStdHandle(DWORD nStdHandle, HANDLE hHandle);
|
||||
void init();
|
||||
@ -40,7 +49,7 @@ namespace files {
|
||||
std::filesystem::path canonicalPath(const std::filesystem::path &path);
|
||||
std::string hostPathListToWindows(const std::string &value);
|
||||
std::string windowsPathListToHost(const std::string &value);
|
||||
}
|
||||
} // namespace files
|
||||
|
||||
inline bool endsWith(const std::string &str, const std::string &suffix) {
|
||||
return str.size() >= suffix.size() && str.compare(str.size() - suffix.size(), suffix.size(), suffix) == 0;
|
||||
|
93
handles.cpp
93
handles.cpp
@ -1,6 +1,4 @@
|
||||
#include "handles.h"
|
||||
#include "common.h"
|
||||
#include <utility>
|
||||
|
||||
namespace {
|
||||
|
||||
@ -9,14 +7,16 @@ constexpr uint32_t kIndexMask = (1u << kIndexBits) - 1; // 0x1FFFF
|
||||
constexpr uint32_t kGenerationMask = (1u << 15) - 1; // 0x7FFF
|
||||
constexpr unsigned kGenerationShift = kIndexBits; // 17
|
||||
|
||||
inline uint32_t indexOf(Handle h) { return h & kIndexMask; }
|
||||
inline uint32_t generationOf(Handle h) { return (h >> kGenerationShift) & kGenerationMask; }
|
||||
inline Handle makeHandle(uint32_t index, uint32_t gen) { return (gen << kGenerationShift) | index; }
|
||||
inline bool isPseudo(Handle h) { return static_cast<int32_t>(h) < 0; }
|
||||
inline uint32_t indexOf(HANDLE h) { return reinterpret_cast<uint32_t>(h) & kIndexMask; }
|
||||
inline uint32_t generationOf(HANDLE h) { return (reinterpret_cast<uint32_t>(h) >> kGenerationShift) & kGenerationMask; }
|
||||
inline HANDLE makeHandle(uint32_t index, uint32_t gen) {
|
||||
return reinterpret_cast<HANDLE>((gen << kGenerationShift) | index);
|
||||
}
|
||||
inline bool isPseudo(HANDLE h) { return reinterpret_cast<int32_t>(h) < 0; }
|
||||
|
||||
} // namespace
|
||||
|
||||
Handle HandleTable::create(ObjectHeader *obj, uint32_t grantedAccess, uint32_t flags) {
|
||||
HANDLE HandleTable::create(ObjectHeader *obj, uint32_t grantedAccess, uint32_t flags) {
|
||||
std::unique_lock lk(mu_);
|
||||
|
||||
uint32_t idx;
|
||||
@ -31,26 +31,26 @@ Handle HandleTable::create(ObjectHeader *obj, uint32_t grantedAccess, uint32_t f
|
||||
auto &e = slots_[idx];
|
||||
|
||||
// Initialize generation if needed
|
||||
if (e.generation == 0) {
|
||||
e.generation = 1;
|
||||
if (e.meta.generation == 0) {
|
||||
e.meta.generation = 1;
|
||||
}
|
||||
const uint16_t gen = e.generation;
|
||||
const uint16_t gen = e.meta.generation;
|
||||
|
||||
// Table owns one pointer ref for this entry
|
||||
detail::ref(obj);
|
||||
|
||||
// Initialize entry
|
||||
e.obj = obj;
|
||||
e.grantedAccess = grantedAccess;
|
||||
e.flags = flags;
|
||||
e.typeCache = obj->type;
|
||||
e.meta.grantedAccess = grantedAccess;
|
||||
e.meta.flags = flags;
|
||||
e.meta.typeCache = obj->type;
|
||||
|
||||
const Handle h = makeHandle(idx, gen);
|
||||
HANDLE h = makeHandle(idx, gen);
|
||||
obj->handleCount.fetch_add(1, std::memory_order_acq_rel);
|
||||
return h;
|
||||
}
|
||||
|
||||
bool HandleTable::get(Handle h, HandleEntry &out, Pin<ObjectHeader> &pinOut) {
|
||||
bool HandleTable::get(HANDLE h, Pin<ObjectHeader> &pinOut, HandleMeta *metaOut) {
|
||||
if (isPseudo(h)) {
|
||||
return false; // pseudo-handles have no entries
|
||||
}
|
||||
@ -61,17 +61,19 @@ bool HandleTable::get(Handle h, HandleEntry &out, Pin<ObjectHeader> &pinOut) {
|
||||
return false;
|
||||
}
|
||||
const auto &e = slots_[idx];
|
||||
if (e.generation != generationOf(h) || !e.obj) {
|
||||
if (e.meta.generation != generationOf(h) || !e.obj) {
|
||||
return false;
|
||||
}
|
||||
|
||||
detail::ref(e.obj); // pin under the lock
|
||||
pinOut = Pin<ObjectHeader>::adopt(e.obj); // dtor will deref
|
||||
out = e;
|
||||
if (metaOut) {
|
||||
*metaOut = e.meta;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool HandleTable::close(Handle h) {
|
||||
bool HandleTable::close(HANDLE h) {
|
||||
if (isPseudo(h)) {
|
||||
return true; // no-op, success
|
||||
}
|
||||
@ -82,16 +84,16 @@ bool HandleTable::close(Handle h) {
|
||||
return false;
|
||||
}
|
||||
auto &e = slots_[idx];
|
||||
if (e.generation != generationOf(h) || !e.obj || e.flags & HANDLE_FLAG_PROTECT_FROM_CLOSE) {
|
||||
if (e.meta.generation != generationOf(h) || !e.obj || e.meta.flags & HANDLE_FLAG_PROTECT_FROM_CLOSE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ObjectHeader *obj = e.obj;
|
||||
e.obj = nullptr; // tombstone
|
||||
auto newHandleCnt = obj->handleCount.fetch_sub(1, std::memory_order_acq_rel) - 1;
|
||||
/*auto newHandleCnt =*/ obj->handleCount.fetch_sub(1, std::memory_order_acq_rel) /* - 1*/;
|
||||
|
||||
// bump generation & recycle while still holding the lock
|
||||
e.generation = static_cast<uint16_t>((e.generation + 1) & kGenerationMask);
|
||||
e.meta.generation = static_cast<uint16_t>((e.meta.generation + 1) & kGenerationMask);
|
||||
freeList_.push_back(idx);
|
||||
lk.unlock();
|
||||
|
||||
@ -102,7 +104,7 @@ bool HandleTable::close(Handle h) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool HandleTable::setInformation(Handle h, uint32_t mask, uint32_t value) {
|
||||
bool HandleTable::setInformation(HANDLE h, uint32_t mask, uint32_t value) {
|
||||
if (isPseudo(h)) {
|
||||
return true; // no-op, success
|
||||
}
|
||||
@ -113,18 +115,18 @@ bool HandleTable::setInformation(Handle h, uint32_t mask, uint32_t value) {
|
||||
return false;
|
||||
}
|
||||
auto &e = slots_[idx];
|
||||
if (e.generation != generationOf(h) || !e.obj) {
|
||||
if (e.meta.generation != generationOf(h) || !e.obj) {
|
||||
return false;
|
||||
}
|
||||
|
||||
constexpr uint32_t kAllowedFlags = HANDLE_FLAG_INHERIT | HANDLE_FLAG_PROTECT_FROM_CLOSE;
|
||||
mask &= kAllowedFlags;
|
||||
|
||||
e.flags = (e.flags & ~mask) | (value & mask);
|
||||
e.meta.flags = (e.meta.flags & ~mask) | (value & mask);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool HandleTable::getInformation(Handle h, uint32_t *outFlags) const {
|
||||
bool HandleTable::getInformation(HANDLE h, uint32_t *outFlags) const {
|
||||
if (!outFlags) {
|
||||
return false;
|
||||
}
|
||||
@ -138,14 +140,14 @@ bool HandleTable::getInformation(Handle h, uint32_t *outFlags) const {
|
||||
return false;
|
||||
}
|
||||
const auto &e = slots_[idx];
|
||||
if (e.generation != generationOf(h) || !e.obj) {
|
||||
if (e.meta.generation != generationOf(h) || !e.obj) {
|
||||
return false;
|
||||
}
|
||||
*outFlags = e.flags;
|
||||
*outFlags = e.meta.flags;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool HandleTable::duplicateTo(Handle src, HandleTable &dst, Handle *out, uint32_t desiredAccess, bool inherit,
|
||||
bool HandleTable::duplicateTo(HANDLE src, HandleTable &dst, HANDLE *out, uint32_t desiredAccess, bool inherit,
|
||||
uint32_t options) {
|
||||
if (!out)
|
||||
return false;
|
||||
@ -160,18 +162,19 @@ bool HandleTable::duplicateTo(Handle src, HandleTable &dst, Handle *out, uint32_
|
||||
// return true;
|
||||
// }
|
||||
|
||||
HandleEntry e{};
|
||||
HandleMeta meta{};
|
||||
Pin<ObjectHeader> pin;
|
||||
if (!get(src, e, pin))
|
||||
if (!get(src, pin, &meta)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool closeSource = (options & DUPLICATE_CLOSE_SOURCE) != 0;
|
||||
if (closeSource && (e.flags & HANDLE_FLAG_PROTECT_FROM_CLOSE) != 0) {
|
||||
if (closeSource && (meta.flags & HANDLE_FLAG_PROTECT_FROM_CLOSE) != 0) {
|
||||
// Cannot close source if it is protected
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t effAccess = (options & DUPLICATE_SAME_ACCESS) ? e.grantedAccess : (desiredAccess & e.grantedAccess);
|
||||
uint32_t effAccess = (options & DUPLICATE_SAME_ACCESS) ? meta.grantedAccess : (desiredAccess & meta.grantedAccess);
|
||||
const uint32_t flags = (inherit ? HANDLE_FLAG_INHERIT : 0);
|
||||
*out = dst.create(pin.obj, effAccess, flags);
|
||||
|
||||
@ -180,29 +183,3 @@ bool HandleTable::duplicateTo(Handle src, HandleTable &dst, Handle *out, uint32_
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
namespace handles {
|
||||
static Data datas[MAX_HANDLES];
|
||||
|
||||
Data dataFromHandle(void *handle, bool pop) {
|
||||
uintptr_t index = (uintptr_t)handle;
|
||||
if (index > 0 && index < MAX_HANDLES) {
|
||||
Data ret = datas[index];
|
||||
if (pop)
|
||||
datas[index] = Data{};
|
||||
return ret;
|
||||
}
|
||||
return Data{};
|
||||
}
|
||||
|
||||
void *allocDataHandle(Data data) {
|
||||
for (size_t i = 1; i < MAX_HANDLES; i++) {
|
||||
if (datas[i].type == TYPE_UNUSED) {
|
||||
datas[i] = data;
|
||||
return (void *)i;
|
||||
}
|
||||
}
|
||||
printf("Out of handles\n");
|
||||
assert(0);
|
||||
}
|
||||
} // namespace handles
|
||||
|
83
handles.h
83
handles.h
@ -1,10 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <condition_variable>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <shared_mutex>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
@ -67,8 +67,8 @@ template <class T> struct Pin {
|
||||
}
|
||||
}
|
||||
|
||||
static Pin acquire(ObjectHeader *o) { return Pin{o, Tag::Acquire}; }
|
||||
static Pin adopt(ObjectHeader *o) { return Pin{o, Tag::Adopt}; }
|
||||
static Pin acquire(ObjectHeader *o) { return {o, Tag::Acquire}; }
|
||||
static Pin adopt(ObjectHeader *o) { return {o, Tag::Adopt}; }
|
||||
|
||||
Pin(const Pin &) = delete;
|
||||
Pin &operator=(const Pin &) = delete;
|
||||
@ -94,9 +94,10 @@ template <class T> struct Pin {
|
||||
|
||||
T *operator->() const { return obj; }
|
||||
T &operator*() const { return *obj; }
|
||||
[[nodiscard]] T *get() const { return obj; }
|
||||
explicit operator bool() const { return obj != nullptr; }
|
||||
|
||||
template <class U> Pin<U> downcast() && {
|
||||
template <typename U> Pin<U> downcast() && {
|
||||
if (obj && obj->type == U::kType) {
|
||||
auto *u = static_cast<U *>(obj);
|
||||
obj = nullptr;
|
||||
@ -106,77 +107,69 @@ template <class T> struct Pin {
|
||||
}
|
||||
};
|
||||
|
||||
using Handle = uint32_t;
|
||||
|
||||
constexpr DWORD HANDLE_FLAG_INHERIT = 0x1;
|
||||
constexpr DWORD HANDLE_FLAG_PROTECT_FROM_CLOSE = 0x2;
|
||||
|
||||
constexpr DWORD DUPLICATE_CLOSE_SOURCE = 0x1;
|
||||
constexpr DWORD DUPLICATE_SAME_ACCESS = 0x2;
|
||||
|
||||
struct HandleEntry {
|
||||
struct ObjectHeader *obj; // intrusive ref (pointerCount++)
|
||||
struct HandleMeta {
|
||||
uint32_t grantedAccess; // effective access mask for this handle
|
||||
uint32_t flags; // inherit/protect/etc
|
||||
ObjectType typeCache; // cached ObjectType for fast getAs
|
||||
uint16_t generation; // must match handle’s generation
|
||||
};
|
||||
|
||||
// template <typename T>
|
||||
// struct HandleRef {
|
||||
// Pin<T> obj;
|
||||
// HandleMeta meta;
|
||||
|
||||
// HandleRef() = default;
|
||||
// HandleRef(Pin<T> o, HandleMeta m) : obj(std::move(o)), meta(m) {}
|
||||
|
||||
// explicit operator bool() const { return obj.operator bool(); }
|
||||
// };
|
||||
|
||||
class HandleTable {
|
||||
public:
|
||||
Handle create(ObjectHeader *obj, uint32_t grantedAccess, uint32_t flags);
|
||||
bool close(Handle h);
|
||||
bool get(Handle h, HandleEntry &out, Pin<ObjectHeader> &pinOut);
|
||||
template <typename T> Pin<T> getAs(Handle h) {
|
||||
HANDLE create(ObjectHeader *obj, uint32_t grantedAccess, uint32_t flags);
|
||||
bool close(HANDLE h);
|
||||
bool get(HANDLE h, Pin<ObjectHeader> &pinOut, HandleMeta *metaOut = nullptr);
|
||||
template <typename T> Pin<T> getAs(HANDLE h, HandleMeta *metaOut = nullptr) {
|
||||
static_assert(std::is_base_of_v<ObjectHeader, T>, "T must derive from ObjectHeader");
|
||||
HandleEntry meta{};
|
||||
Pin<ObjectHeader> pin;
|
||||
if (!get(h, meta, pin)) {
|
||||
HandleMeta metaOutLocal{};
|
||||
if (!metaOut) {
|
||||
metaOut = &metaOutLocal;
|
||||
}
|
||||
if (!get(h, pin, metaOut)) {
|
||||
return {};
|
||||
}
|
||||
if constexpr (std::is_same_v<T, ObjectHeader>) {
|
||||
return std::move(pin);
|
||||
} else if (meta.typeCache != T::kType || pin->type != T::kType) {
|
||||
} else if (metaOut->typeCache != T::kType || pin->type != T::kType) {
|
||||
return {};
|
||||
} else {
|
||||
// Cast directly to T* and transfer ownership to Pin<T>
|
||||
return Pin<T>::adopt(static_cast<T *>(pin.release()));
|
||||
}
|
||||
}
|
||||
bool setInformation(Handle h, uint32_t mask, uint32_t value);
|
||||
bool getInformation(Handle h, uint32_t *outFlags) const;
|
||||
bool duplicateTo(Handle src, HandleTable &dst, Handle *out, uint32_t desiredAccess, bool inherit, uint32_t options);
|
||||
bool setInformation(HANDLE h, uint32_t mask, uint32_t value);
|
||||
bool getInformation(HANDLE h, uint32_t *outFlags) const;
|
||||
bool duplicateTo(HANDLE src, HandleTable &dst, HANDLE *out, uint32_t desiredAccess, bool inherit, uint32_t options);
|
||||
|
||||
private:
|
||||
struct HandleEntry {
|
||||
struct ObjectHeader *obj;
|
||||
HandleMeta meta;
|
||||
};
|
||||
|
||||
std::vector<HandleEntry> slots_;
|
||||
std::vector<uint32_t> freeList_;
|
||||
mutable std::shared_mutex mu_;
|
||||
};
|
||||
|
||||
namespace handles {
|
||||
|
||||
constexpr size_t MAX_HANDLES = 0x10000;
|
||||
|
||||
enum Type {
|
||||
TYPE_UNUSED,
|
||||
TYPE_FILE,
|
||||
TYPE_MAPPED,
|
||||
TYPE_PROCESS,
|
||||
TYPE_TOKEN,
|
||||
TYPE_MUTEX,
|
||||
TYPE_EVENT,
|
||||
TYPE_SEMAPHORE,
|
||||
TYPE_THREAD,
|
||||
TYPE_HEAP,
|
||||
TYPE_REGISTRY_KEY
|
||||
};
|
||||
|
||||
struct Data {
|
||||
Type type = TYPE_UNUSED;
|
||||
void *ptr;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
Data dataFromHandle(void *handle, bool pop);
|
||||
void *allocDataHandle(Data data);
|
||||
} // namespace handles
|
||||
namespace wibo {
|
||||
extern HandleTable &handles();
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user