add /IGNORE:4216 to HAL link step, add writing and directory listing to the FAT32 driver, add the concept of current working directory to the VFS, add writing to the VFS, more operations in the shell, rename KeCurrentThread to KeGetCurrentThread and add KeGetCurrentProcess, add STATUS_NO_MORE_FILES, change KeFault* to KePanic, add syscalls for writing to files, add a proper syscall to writing to the console, move Screen.Clear to after memory status debug print, add ObjectTypeDir to OBJECT_TYPE, extend kernel32 by file operations and upgrade user32 (and add gdi32) to match windows a bit more closely, change CreateProcessA to accept the first argument as NULL.
This commit is contained in:
1 parent
a5f0acfac8
commit
8c7564d5e1
33 files changed
+2343
-429
No files matched your search
+283
-16
@@ -1,6 +1,7 @@
|
||||
// io_file.c - VFS dispatch layer
|
||||
#include <string.h>
|
||||
#include <ke_list.h>
|
||||
#include <ke_process.h>
|
||||
#include <io_file.h>
|
||||
#include <mm_virt.h>
|
||||
#include <ob_object.h>
|
||||
@@ -14,6 +15,142 @@ typedef struct {
|
||||
|
||||
static VOLUME_ENTRY gVolumes[MAX_DRIVES];
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Current Working Directory (drive 0 relative, no trailing backslash)
|
||||
// e.g. "" = root, "apps" = root\apps, "apps\\data" = nested
|
||||
// ---------------------------------------------------------------------------
|
||||
#define CWD_MAX 512
|
||||
|
||||
__declspec(dllexport) KSTATUS IoGetCwd(char* buf, size_t size) {
|
||||
if (!buf || size == 0) return STATUS_INVALID_PARAMETER;
|
||||
|
||||
PROCESS* proc = KeGetCurrentProcess();
|
||||
if (!proc) return STATUS_ACCESS_DENIED;
|
||||
|
||||
uintptr_t flags = KeAcquireSpinlock(&proc->CwdLock);
|
||||
size_t cwdLen = strlen(proc->CurrentDirectory);
|
||||
if (cwdLen + 1 > size) {
|
||||
KeReleaseSpinlock(&proc->CwdLock, flags);
|
||||
return STATUS_BUFFER_TOO_SMALL;
|
||||
}
|
||||
memcpy(buf, proc->CurrentDirectory, cwdLen + 1);
|
||||
KeReleaseSpinlock(&proc->CwdLock, flags);
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// Resolve a possibly-relative path component against the CWD.
|
||||
// Writes the fully-resolved, drive-stripped relative path into 'resolved'
|
||||
// (caller supplies buf of at least CWD_MAX bytes).
|
||||
// Returns the drive index.
|
||||
static uint8_t ResolvePath(const char* path, char* resolved, size_t resolvedSize) {
|
||||
// Strip explicit drive prefix "N:\"
|
||||
uint8_t drive = 0;
|
||||
if (path[0] >= '0' && path[0] <= '7' &&
|
||||
path[1] == ':' && (path[2] == '\\' || path[2] == '/')) {
|
||||
drive = (uint8_t)(path[0] - '0');
|
||||
path = path + 3;
|
||||
}
|
||||
|
||||
char work[CWD_MAX * 2];
|
||||
work[0] = '\0';
|
||||
|
||||
// Initialize our working base
|
||||
if (path[0] == '\\' || path[0] == '/') {
|
||||
// Absolute path: start from the root (empty work buffer)
|
||||
// The segment loop below will automatically skip the leading separators.
|
||||
} else {
|
||||
// Relative path: start from the current process's CWD
|
||||
PROCESS* proc = KeGetCurrentProcess();
|
||||
if (proc) {
|
||||
uintptr_t flags = KeAcquireSpinlock(&proc->CwdLock);
|
||||
size_t wlen = strlen(proc->CurrentDirectory);
|
||||
if (wlen >= sizeof(work)) wlen = sizeof(work) - 1;
|
||||
memcpy(work, proc->CurrentDirectory, wlen);
|
||||
work[wlen] = '\0';
|
||||
KeReleaseSpinlock(&proc->CwdLock, flags);
|
||||
}
|
||||
}
|
||||
|
||||
// Segment-by-segment canonicalization
|
||||
const char* p = path;
|
||||
while (*p) {
|
||||
// Skip separators
|
||||
while (*p == '\\' || *p == '/') p++;
|
||||
if (!*p) break;
|
||||
|
||||
// Read next component
|
||||
char comp[256];
|
||||
int clen = 0;
|
||||
while (*p && *p != '\\' && *p != '/' && clen < 255)
|
||||
comp[clen++] = *p++;
|
||||
comp[clen] = '\0';
|
||||
|
||||
if (clen == 0) continue;
|
||||
|
||||
if (strcmp(comp, ".") == 0) {
|
||||
// Stay in place
|
||||
} else if (strcmp(comp, "..") == 0) {
|
||||
// Remove last component from work
|
||||
int i = (int)strlen(work) - 1;
|
||||
while (i >= 0 && work[i] != '\\' && work[i] != '/') i--;
|
||||
if (i < 0) i = 0;
|
||||
work[i] = '\0';
|
||||
} else {
|
||||
// Append separator + component
|
||||
size_t wl = strlen(work);
|
||||
if (wl > 0 && work[wl-1] != '\\' && work[wl-1] != '/') {
|
||||
if (wl + 1 < sizeof(work)) { work[wl] = '\\'; work[wl+1] = '\0'; }
|
||||
}
|
||||
size_t wl2 = strlen(work);
|
||||
if (wl2 + clen + 1 < sizeof(work)) {
|
||||
memcpy(work + wl2, comp, clen);
|
||||
work[wl2 + clen] = '\0';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Output the final canonicalized, drive-stripped path
|
||||
size_t finalLen = strlen(work);
|
||||
if (finalLen + 1 > resolvedSize) finalLen = resolvedSize - 1;
|
||||
memcpy(resolved, work, finalLen);
|
||||
resolved[finalLen] = '\0';
|
||||
|
||||
return drive;
|
||||
}
|
||||
|
||||
__declspec(dllexport) KSTATUS IoSetCwd(const char* path) {
|
||||
if (!path) return STATUS_INVALID_PARAMETER;
|
||||
|
||||
PROCESS* proc = KeGetCurrentProcess();
|
||||
if (!proc) return STATUS_ACCESS_DENIED;
|
||||
|
||||
// Resolve the new path (this also validates ".." navigation).
|
||||
// Note: ResolvePath already uses proc->CurrentDirectory internally!
|
||||
char resolved[CWD_MAX];
|
||||
uint8_t drive = ResolvePath(path, resolved, sizeof(resolved));
|
||||
|
||||
// Validate: try to open the directory to confirm it actually exists on disk
|
||||
IO_FILESYSTEM* fs = gVolumes[drive].Fs;
|
||||
if (!fs) return STATUS_NOT_FOUND;
|
||||
if (!fs->OpenDir) return STATUS_NOT_SUPPORTED;
|
||||
|
||||
void* dirCtx = NULL;
|
||||
KSTATUS s = fs->OpenDir(fs, resolved, &dirCtx);
|
||||
if (!KSUCCESS(s)) return s;
|
||||
fs->CloseDir(fs, dirCtx);
|
||||
|
||||
// Commit the new CWD to the process block
|
||||
size_t len = strlen(resolved);
|
||||
if (len + 1 > sizeof(proc->CurrentDirectory)) return STATUS_BUFFER_TOO_SMALL;
|
||||
|
||||
uintptr_t flags = KeAcquireSpinlock(&proc->CwdLock);
|
||||
memcpy(proc->CurrentDirectory, resolved, len + 1);
|
||||
KeReleaseSpinlock(&proc->CwdLock, flags);
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Registration
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -29,20 +166,6 @@ __declspec(dllexport) KSTATUS IoRegisterFilesystem(uint8_t volumeIndex,
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Path parsing - "0:\apps\shell.exe" or just "apps\shell.exe" (drive 0)
|
||||
// ---------------------------------------------------------------------------
|
||||
static uint8_t ParseDrive(const char* path, const char **pathOut) {
|
||||
// Accept "N:\..." prefix, otherwise default to drive 0.
|
||||
if (path[0] >= '0' && path[0] <= '7' &&
|
||||
path[1] == ':' && (path[2] == '\\' || path[2] == '/')) {
|
||||
*pathOut = path + 3;
|
||||
return (uint8_t)(path[0] - '0');
|
||||
}
|
||||
*pathOut = path;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Destructor
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -57,8 +180,8 @@ static void FileDestroy(OBJECT* obj) {
|
||||
__declspec(dllexport) KSTATUS IoOpenFile(const char* path, FILE_OBJECT **out) {
|
||||
if (!path || !out) return STATUS_INVALID_PARAMETER;
|
||||
|
||||
const char* relPath;
|
||||
uint8_t drive = ParseDrive(path, &relPath);
|
||||
char relPath[CWD_MAX];
|
||||
uint8_t drive = ResolvePath(path, relPath, sizeof(relPath));
|
||||
|
||||
IO_FILESYSTEM* fs = gVolumes[drive].Fs;
|
||||
if (!fs) return STATUS_NOT_FOUND;
|
||||
@@ -97,6 +220,90 @@ __declspec(dllexport) KSTATUS IoReadFile(FILE_OBJECT* file, void* buf, size_t si
|
||||
return s;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// IoWriteFile - sequential write at current offset
|
||||
// ---------------------------------------------------------------------------
|
||||
__declspec(dllexport) KSTATUS IoWriteFile(FILE_OBJECT* file, const void* buf,
|
||||
size_t size, size_t* bytesWritten) {
|
||||
if (!file || !buf || !bytesWritten) return STATUS_INVALID_PARAMETER;
|
||||
if (!file->Fs->Write) return STATUS_NOT_SUPPORTED;
|
||||
|
||||
size_t written = 0;
|
||||
KSTATUS s = file->Fs->Write(file->Fs, file->FileCtx,
|
||||
file->Offset, buf, size, &written);
|
||||
if (KSUCCESS(s)) {
|
||||
file->Offset += written;
|
||||
// Keep the cached size in sync if the file grew.
|
||||
if (file->Offset > file->Size)
|
||||
file->Size = file->Offset;
|
||||
}
|
||||
*bytesWritten = written;
|
||||
return s;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// IoCreateFile - create a new file and return it open for writing
|
||||
// ---------------------------------------------------------------------------
|
||||
__declspec(dllexport) KSTATUS IoCreateFile(const char* path, FILE_OBJECT** out) {
|
||||
if (!path || !out) return STATUS_INVALID_PARAMETER;
|
||||
|
||||
char relPath[CWD_MAX];
|
||||
uint8_t drive = ResolvePath(path, relPath, sizeof(relPath));
|
||||
|
||||
IO_FILESYSTEM* fs = gVolumes[drive].Fs;
|
||||
if (!fs) return STATUS_NOT_FOUND;
|
||||
if (!fs->Create) return STATUS_NOT_SUPPORTED;
|
||||
|
||||
void* fileCtx;
|
||||
KSTATUS s = fs->Create(fs, relPath, &fileCtx);
|
||||
if (!KSUCCESS(s)) return s;
|
||||
|
||||
FILE_OBJECT* f = (FILE_OBJECT*)ObAllocateObject(ObjectTypeFile,
|
||||
sizeof(FILE_OBJECT));
|
||||
if (!f) { fs->Close(fs, fileCtx); return STATUS_NO_MEMORY; }
|
||||
f->Header.Destroy = FileDestroy;
|
||||
f->Fs = fs;
|
||||
f->FileCtx = fileCtx;
|
||||
f->Size = 0;
|
||||
f->Offset = 0;
|
||||
|
||||
*out = f;
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// IoDeleteFile - delete a file by path (does not require an open handle)
|
||||
// ---------------------------------------------------------------------------
|
||||
__declspec(dllexport) KSTATUS IoDeleteFile(const char* path) {
|
||||
if (!path) return STATUS_INVALID_PARAMETER;
|
||||
|
||||
char relPath[CWD_MAX];
|
||||
uint8_t drive = ResolvePath(path, relPath, sizeof(relPath));
|
||||
|
||||
IO_FILESYSTEM* fs = gVolumes[drive].Fs;
|
||||
if (!fs) return STATUS_NOT_FOUND;
|
||||
if (!fs->Delete) return STATUS_NOT_SUPPORTED;
|
||||
|
||||
return fs->Delete(fs, relPath);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// IoTruncateFile - resize the file; updates the cached Size on success
|
||||
// ---------------------------------------------------------------------------
|
||||
__declspec(dllexport) KSTATUS IoTruncateFile(FILE_OBJECT* file, uint64_t newSize) {
|
||||
if (!file) return STATUS_INVALID_PARAMETER;
|
||||
if (!file->Fs->Truncate) return STATUS_NOT_SUPPORTED;
|
||||
|
||||
KSTATUS s = file->Fs->Truncate(file->Fs, file->FileCtx, newSize);
|
||||
if (KSUCCESS(s)) {
|
||||
file->Size = newSize;
|
||||
// Pull the file pointer back if it now sits past EOF.
|
||||
if (file->Offset > file->Size)
|
||||
file->Offset = file->Size;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
__declspec(dllexport) KSTATUS IoSeekFile(FILE_OBJECT* file, uint64_t offset) {
|
||||
if (!file) return STATUS_INVALID_PARAMETER;
|
||||
@@ -111,3 +318,63 @@ __declspec(dllexport) uint64_t IoGetFileSize(FILE_OBJECT* file) {
|
||||
__declspec(dllexport) void IoCloseFile(FILE_OBJECT* file) {
|
||||
if (file) ObDereferenceObject(&file->Header);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Directory enumeration
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
static void DirDestroy(OBJECT* obj) {
|
||||
DIR_OBJECT* d = (DIR_OBJECT*)obj; // Header is first member
|
||||
if (d->Fs && d->DirCtx) d->Fs->CloseDir(d->Fs, d->DirCtx);
|
||||
}
|
||||
|
||||
__declspec(dllexport) KSTATUS IoOpenDir(const char* dirPath, DIR_OBJECT** out) {
|
||||
if (!out) return STATUS_INVALID_PARAMETER;
|
||||
|
||||
// NULL or empty path means the CWD itself
|
||||
char relPath[CWD_MAX];
|
||||
uint8_t drive;
|
||||
|
||||
if (!dirPath || dirPath[0] == '\0') {
|
||||
PROCESS* proc = KeGetCurrentProcess();
|
||||
if (!proc) return STATUS_ACCESS_DENIED;
|
||||
|
||||
uintptr_t flags = KeAcquireSpinlock(&proc->CwdLock);
|
||||
memcpy(relPath, proc->CurrentDirectory, strlen(proc->CurrentDirectory) + 1);
|
||||
KeReleaseSpinlock(&proc->CwdLock, flags);
|
||||
|
||||
// Note: For now, this assumes drive 0. If you add multi-drive support
|
||||
// later, you should add a CurrentDrive field to the PROCESS struct.
|
||||
drive = 0;
|
||||
} else {
|
||||
drive = ResolvePath(dirPath, relPath, sizeof(relPath));
|
||||
}
|
||||
|
||||
IO_FILESYSTEM* fs = gVolumes[drive].Fs;
|
||||
if (!fs) return STATUS_NOT_FOUND;
|
||||
if (!fs->OpenDir) return STATUS_NOT_SUPPORTED;
|
||||
|
||||
void* dirCtx = NULL;
|
||||
KSTATUS s = fs->OpenDir(fs, relPath, &dirCtx);
|
||||
if (!KSUCCESS(s)) return s;
|
||||
|
||||
DIR_OBJECT* d = (DIR_OBJECT*)ObAllocateObject(ObjectTypeDir, sizeof(DIR_OBJECT));
|
||||
if (!d) { fs->CloseDir(fs, dirCtx); return STATUS_NO_MEMORY; }
|
||||
|
||||
d->Header.Destroy = DirDestroy;
|
||||
d->Fs = fs;
|
||||
d->DirCtx = dirCtx;
|
||||
|
||||
*out = d;
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
__declspec(dllexport) KSTATUS IoReadDir(DIR_OBJECT* dir, KDIR_ENTRY* entryOut) {
|
||||
if (!dir || !entryOut) return STATUS_INVALID_PARAMETER;
|
||||
if (!dir->Fs->ReadDir) return STATUS_NOT_SUPPORTED;
|
||||
return dir->Fs->ReadDir(dir->Fs, dir->DirCtx, entryOut);
|
||||
}
|
||||
|
||||
__declspec(dllexport) void IoCloseDir(DIR_OBJECT* dir) {
|
||||
if (dir) ObDereferenceObject(&dir->Header);
|
||||
}
|
||||
Reference in new issue
Block a user