File handles

This commit is contained in:
Simon Lindholm 2022-07-04 00:40:12 +02:00
parent 84cb52fe70
commit 228bf4663e
4 changed files with 188 additions and 119 deletions

131
files.cpp Normal file
View File

@ -0,0 +1,131 @@
#include "common.h"
#include "files.h"
namespace files {
static FILE *handleFps[0x10000];
static void *stdinHandle;
static void *stdoutHandle;
static void *stderrHandle;
std::filesystem::path pathFromWindows(const char *inStr) {
// Convert to forward slashes
std::string str = inStr;
std::replace(str.begin(), str.end(), '\\', '/');
// Remove the drive letter
if (str.starts_with("c:/") || str.starts_with("C:/")) {
str.erase(0, 2);
}
// Return as-is if it exists, else traverse the filesystem looking for
// a path that matches case insensitively
std::filesystem::path path = std::filesystem::path(str);
if (std::filesystem::exists(path)) {
return path;
}
path = path.lexically_normal();
std::filesystem::path newPath = ".";
bool followingExisting = true;
for (auto component : path) {
std::filesystem::path newPath2 = newPath / component;
if (followingExisting && !std::filesystem::exists(newPath2) && (component != ".." && component != "." && component != "")) {
followingExisting = false;
try {
for (std::filesystem::path entry : std::filesystem::directory_iterator{newPath}) {
if (strcasecmp(entry.filename().c_str(), component.c_str()) == 0) {
followingExisting = true;
newPath2 = entry;
break;
}
}
} catch (const std::filesystem::filesystem_error&) {
// not a directory
}
}
newPath = newPath2;
}
if (followingExisting) {
DEBUG_LOG("Resolved case-insensitive path: %s\n", newPath.c_str());
} else {
DEBUG_LOG("Failed to resolve path: %s\n", newPath.c_str());
}
return newPath;
}
std::string pathToWindows(const std::filesystem::path &path) {
std::string str = path;
if (path.is_absolute()) {
str.insert(0, "C:");
}
std::replace(str.begin(), str.end(), '/', '\\');
return str;
}
FILE *fpFromHandle(void *handle, bool pop) {
uintptr_t index = (uintptr_t)handle;
if (index > 0 && index < 0x10000) {
FILE *ret = handleFps[index];
if (pop)
handleFps[index] = 0;
return ret;
}
if (pop)
return 0;
printf("Invalid file handle %p\n", handle);
assert(0);
}
void *allocFpHandle(FILE *fp) {
for (int i = 1; i < 0x10000; i++) {
if (!handleFps[i]) {
handleFps[i] = fp;
return (void*)i;
}
}
printf("Out of file handles\n");
assert(0);
}
void *getStdHandle(uint32_t nStdHandle) {
switch (nStdHandle) {
case ((uint32_t) -10): // STD_INPUT_HANDLE
return stdinHandle;
case ((uint32_t) -11): // STD_OUTPUT_HANDLE
return stdoutHandle;
case ((uint32_t) -12): // STD_ERROR_HANDLE
return stderrHandle;
default:
return (void *) 0xFFFFFFFF;
}
}
unsigned int setStdHandle(uint32_t nStdHandle, void *hHandle) {
FILE *fp = fpFromHandle(hHandle);
assert(fp);
switch (nStdHandle) {
case ((uint32_t) -10): // STD_INPUT_HANDLE
stdinHandle = hHandle;
break;
case ((uint32_t) -11): // STD_OUTPUT_HANDLE
stdoutHandle = hHandle;
break;
case ((uint32_t) -12): // STD_ERROR_HANDLE
stderrHandle = hHandle;
break;
default:
return 0; // fail
}
return 1; // success
}
void init() {
stdinHandle = allocFpHandle(stdin);
stdoutHandle = allocFpHandle(stdout);
stderrHandle = allocFpHandle(stderr);
}
}

11
files.h Normal file
View File

@ -0,0 +1,11 @@
#include <filesystem>
namespace files {
std::filesystem::path pathFromWindows(const char *inStr);
std::string pathToWindows(const std::filesystem::path &path);
void *allocFpHandle(FILE *fp);
FILE *fpFromHandle(void *handle, bool pop = false);
void *getStdHandle(uint32_t nStdHandle);
unsigned int setStdHandle(uint32_t nStdHandle, void *hHandle);
void init();
}

View File

@ -1,4 +1,5 @@
#include "common.h"
#include "files.h"
#include <algorithm>
#include <ctype.h>
#include <filesystem>
@ -239,116 +240,39 @@ namespace kernel32 {
* I/O
*/
void *WIN_FUNC GetStdHandle(uint32_t nStdHandle) {
switch (nStdHandle) {
case ((uint32_t) -10): // STD_INPUT_HANDLE
return stdin;
case ((uint32_t) -11): // STD_OUTPUT_HANDLE
return stdout;
case ((uint32_t) -12): // STD_ERROR_HANDLE
return stderr;
default:
return (void *) 0xFFFFFFFF;
}
return files::getStdHandle(nStdHandle);
}
unsigned int WIN_FUNC SetStdHandle(uint32_t nStdHandle, FILE *handle) {
switch (nStdHandle) {
case ((uint32_t) -10): // STD_INPUT_HANDLE
stdin = handle;
break;
case ((uint32_t) -11): // STD_OUTPUT_HANDLE
stdout = handle;
break;
case ((uint32_t) -12): // STD_ERROR_HANDLE
stderr = handle;
break;
default:
return 0; // fail
}
return 1; // success
unsigned int WIN_FUNC SetStdHandle(uint32_t nStdHandle, void *hHandle) {
return files::setStdHandle(nStdHandle, hHandle);
}
unsigned int WIN_FUNC DuplicateHandle(void *hSourceProcessHandle, void *hSourceHandle, void *hTargetProcessHandle, void **lpTargetHandle, unsigned int dwDesiredAccess, unsigned int bInheritHandle, unsigned int dwOptions) {
// This is kinda silly...
if (hSourceHandle == stdin || hSourceHandle == stdout || hSourceHandle == stderr) {
// Just pretend we duplicated it, why not
*lpTargetHandle = hSourceHandle;
DEBUG_LOG("DuplicateHandle(source=%p)\n", hSourceHandle);
FILE *fp = files::fpFromHandle(hSourceHandle);
if (fp == stdin || fp == stdout || fp == stderr) {
// we never close standard handles so they are fine to duplicate
*lpTargetHandle = files::allocFpHandle(fp);
return 1;
}
// This probably won't come up
DEBUG_LOG("Unhandled DuplicateHandle(source=%p)\n", hSourceHandle);
return 0;
// other handles are more problematic; fail for now
printf("failed to duplicate handle\n");
assert(0);
}
int WIN_FUNC CloseHandle(void *hObject) {
// we *probably* won't run out of file descriptors even if we never close files
DEBUG_LOG("CloseHandle\n");
DEBUG_LOG("CloseHandle %p\n", hObject);
FILE *fp = files::fpFromHandle(hObject, true);
if (fp && fp != stdin && fp != stdout && fp != stderr) {
fclose(fp);
}
return 1;
}
std::filesystem::path pathFromWindows(const char *inStr) {
// Convert to forward slashes
std::string str = inStr;
std::replace(str.begin(), str.end(), '\\', '/');
// Remove the drive letter
if (str.starts_with("c:/") || str.starts_with("C:/")) {
str.erase(0, 2);
}
// Return as-is if it exists, else traverse the filesystem looking for
// a path that matches case insensitively
std::filesystem::path path = std::filesystem::path(str);
if (std::filesystem::exists(path)) {
return path;
}
path = path.lexically_normal();
std::filesystem::path newPath = ".";
bool followingExisting = true;
for (auto component : path) {
std::filesystem::path newPath2 = newPath / component;
if (followingExisting && !std::filesystem::exists(newPath2) && (component != ".." && component != "." && component != "")) {
followingExisting = false;
try {
for (std::filesystem::path entry : std::filesystem::directory_iterator{newPath}) {
if (strcasecmp(entry.filename().c_str(), component.c_str()) == 0) {
followingExisting = true;
newPath2 = entry;
break;
}
}
} catch (const std::filesystem::filesystem_error&) {
// not a directory
}
}
newPath = newPath2;
}
if (followingExisting) {
DEBUG_LOG("Resolved case-insensitive path: %s\n", newPath.c_str());
} else {
DEBUG_LOG("Failed to resolve path: %s\n", newPath.c_str());
}
return newPath;
}
std::string pathToWindows(const std::filesystem::path &path) {
std::string str = path;
if (path.is_absolute()) {
str.insert(0, "C:");
}
std::replace(str.begin(), str.end(), '/', '\\');
return str;
}
unsigned int WIN_FUNC GetFullPathNameA(const char *lpFileName, unsigned int nBufferLength, char *lpBuffer, char **lpFilePart) {
DEBUG_LOG("GetFullPathNameA(%s)...\n", lpFileName);
std::filesystem::path absPath = std::filesystem::absolute(pathFromWindows(lpFileName));
std::string absStr = pathToWindows(absPath);
std::filesystem::path absPath = std::filesystem::absolute(files::pathFromWindows(lpFileName));
std::string absStr = files::pathToWindows(absPath);
DEBUG_LOG("AbsPath: %s - %s\n", absPath.c_str(), absStr.c_str());
// Enough space?
@ -372,14 +296,14 @@ namespace kernel32 {
}
void *WIN_FUNC FindFirstFileA(const char *lpFileName, void *lpFindFileData) {
auto path = pathFromWindows(lpFileName);
auto path = files::pathFromWindows(lpFileName);
DEBUG_LOG("FindFirstFileA %s (%s)\n", lpFileName, path.c_str());
wibo::lastError = 2; // ERROR_FILE_NOT_FOUND
return (void *) 0xFFFFFFFF;
}
unsigned int WIN_FUNC GetFileAttributesA(const char *lpFileName) {
auto path = pathFromWindows(lpFileName);
auto path = files::pathFromWindows(lpFileName);
DEBUG_LOG("GetFileAttributesA(%s)... (%s)\n", lpFileName, path.c_str());
auto status = std::filesystem::status(path);
@ -402,11 +326,10 @@ namespace kernel32 {
unsigned int WIN_FUNC WriteFile(void *hFile, const void *lpBuffer, unsigned int nNumberOfBytesToWrite, unsigned int *lpNumberOfBytesWritten, void *lpOverlapped) {
DEBUG_LOG("WriteFile %d\n", nNumberOfBytesToWrite);
assert(!lpOverlapped);
// for now, we VERY naively assume that the handle is a FILE*
// haha this is gonna come back and bite me, isn't it
wibo::lastError = 0;
size_t written = fwrite(lpBuffer, 1, nNumberOfBytesToWrite, (FILE *) hFile);
FILE *fp = files::fpFromHandle(hFile);
size_t written = fwrite(lpBuffer, 1, nNumberOfBytesToWrite, fp);
if (lpNumberOfBytesWritten)
*lpNumberOfBytesWritten = written;
@ -429,7 +352,8 @@ namespace kernel32 {
assert(!lpOverlapped);
wibo::lastError = 0;
size_t read = fread(lpBuffer, 1, nNumberOfBytesToRead, (FILE *) hFile);
FILE *fp = files::fpFromHandle(hFile);
size_t read = fread(lpBuffer, 1, nNumberOfBytesToRead, fp);
*lpNumberOfBytesRead = read;
return 1;
}
@ -442,25 +366,25 @@ namespace kernel32 {
unsigned int dwCreationDisposition,
unsigned int dwFlagsAndAttributes,
void *hTemplateFile) {
std::string path = pathFromWindows(lpFileName);
std::string path = files::pathFromWindows(lpFileName);
DEBUG_LOG("CreateFileA(filename=%s (%s), desiredAccess=0x%x, shareMode=%u, securityAttributes=%p, creationDisposition=%u, flagsAndAttributes=%u)\n",
lpFileName, path.c_str(),
dwDesiredAccess, dwShareMode, lpSecurityAttributes,
dwCreationDisposition, dwFlagsAndAttributes);
FILE *result = 0;
FILE *fp;
if (dwDesiredAccess == 0x80000000) { // read
result = fopen(path.c_str(), "rb");
fp = fopen(path.c_str(), "rb");
} else if (dwDesiredAccess == 0x40000000) { // write
result = fopen(path.c_str(), "wb");
fp = fopen(path.c_str(), "wb");
} else if (dwDesiredAccess == 0xc0000000) { // read/write
result = fopen(path.c_str(), "wb+");
fp = fopen(path.c_str(), "wb+");
} else {
assert(0);
}
if (result) {
if (fp) {
wibo::lastError = 0;
return result;
return files::allocFpHandle(fp);
} else {
switch (errno) {
case EACCES:
@ -479,12 +403,12 @@ namespace kernel32 {
wibo::lastError = 50; // ERROR_NOT_SUPPORTED
break;
}
return (FILE *) 0xFFFFFFFF; // INVALID_HANDLE_VALUE
return (void *) 0xFFFFFFFF; // INVALID_HANDLE_VALUE
}
}
int WIN_FUNC DeleteFileA(const char* lpFileName) {
std::string path = pathFromWindows(lpFileName);
std::string path = files::pathFromWindows(lpFileName);
DEBUG_LOG("DeleteFileA %s (%s)\n", lpFileName, path.c_str());
unlink(path.c_str());
return 1;
@ -493,7 +417,7 @@ namespace kernel32 {
unsigned int WIN_FUNC SetFilePointer(void *hFile, int lDistanceToMove, int *lpDistanceToMoveHigh, int dwMoveMethod) {
DEBUG_LOG("SetFilePointer %d %d %d\n", lDistanceToMove, (lpDistanceToMoveHigh ? *lpDistanceToMoveHigh : -1), dwMoveMethod);
assert(!lpDistanceToMoveHigh);
FILE *fp = (FILE*) hFile;
FILE *fp = files::fpFromHandle(hFile);
wibo::lastError = 0;
int r = fseek(fp, lDistanceToMove,
dwMoveMethod == 0 ? SEEK_SET :
@ -518,7 +442,7 @@ namespace kernel32 {
*/
unsigned int WIN_FUNC GetFileSize(void *hFile, unsigned int *lpFileSizeHigh) {
DEBUG_LOG("GetFileSize\n");
FILE *fp = (FILE*)hFile;
FILE *fp = files::fpFromHandle(hFile);
long pos = ftell(fp);
assert(pos >= 0);
int r = fseek(fp, 0L, SEEK_END);
@ -667,7 +591,7 @@ namespace kernel32 {
DEBUG_LOG("GetCurrentDirectoryA\n");
std::filesystem::path cwd = std::filesystem::current_path();
std::string path = pathToWindows(cwd);
std::string path = files::pathToWindows(cwd);
assert(path.size() < uSize);
@ -678,7 +602,7 @@ namespace kernel32 {
void* WIN_FUNC GetModuleHandleA(const char* lpModuleName) {
DEBUG_LOG("GetModuleHandleA %s\n", lpModuleName);
// wibo::lastError = 0;
return (void*)1;
return (void*)0x100001;
}
unsigned int WIN_FUNC GetModuleFileNameA(void* hModule, char* lpFilename, unsigned int nSize) {
@ -689,17 +613,17 @@ namespace kernel32 {
void* WIN_FUNC FindResourceA(void* hModule, const char* lpName, const char* lpType) {
DEBUG_LOG("FindResourceA %p %s %s\n", hModule, lpName, lpType);
return (void*)2;
return (void*)0x100002;
}
void* WIN_FUNC LoadResource(void* hModule, void* res) {
DEBUG_LOG("LoadResource %p %p\n", hModule, res);
return (void*)3;
return (void*)0x100003;
}
void* WIN_FUNC LockResource(void* res) {
DEBUG_LOG("LockResource %p\n", res);
return (void*)4;
return (void*)0x100004;
}
unsigned int WIN_FUNC SizeofResource(void* hModule, void* res) {
@ -709,7 +633,7 @@ namespace kernel32 {
void* WIN_FUNC LoadLibraryA(const char* lpLibFileName) {
DEBUG_LOG("LoadLibraryA %s\n", lpLibFileName);
return (void*)5;
return (void*)0x100005;
}
int WIN_FUNC FreeLibrary(void* hLibModule) {
@ -736,7 +660,7 @@ namespace kernel32 {
// return a dummy value
wibo::lastError = 0;
return (void *) 0x12345678;
return (void *) 0x100006;
}
void *WIN_FUNC VirtualAlloc(void *lpAddress, unsigned int dwSize, unsigned int flAllocationType, unsigned int flProtect) {

View File

@ -1,4 +1,5 @@
#include "common.h"
#include "files.h"
#include <asm/ldt.h>
#include <filesystem>
#include <errno.h>
@ -118,6 +119,8 @@ int main(int argc, char **argv) {
wibo::debugEnabled = true;
}
files::init();
// Create TIB
TIB tib;
tib.tib = &tib;