refactor syscall ABI and FAT32 driver to use request structs and string views natively, enforce handle access rights on object lookup, add scheduler statistics tracking, fix KiSchedule to prevent 50% idle throttling on CPU-bound threads, better document x64 initial user stack alignment, fix process teardown to properly signal exit events and manage thread counts, optimize doom file I/O with a custom handle struct to internally track position and size, add SubsystemData to TEB for generic thread-local state, apply PAGE_NX to non-executable PE sections in the loader, update native apps, tests, and shells to match new APIs, update kernel32 directory retrieval.
This commit is contained in:
1 parent
603fbdfab1
commit
e1248f4afe
43 files changed
+2892
-1857
No files matched your search
Binary file not shown.
@@ -7,7 +7,13 @@
|
|||||||
#define EVENT_AUTO_RESET 0u
|
#define EVENT_AUTO_RESET 0u
|
||||||
|
|
||||||
static void print(const char* s) {
|
static void print(const char* s) {
|
||||||
SysWriteConsole(s, strlen(s));
|
SYS_WRITE_CONSOLE_REQUEST req = { SysSvFromCstr(s) };
|
||||||
|
SysWriteConsole(&req);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t open_file(const char* path, uint32_t* handleOut) {
|
||||||
|
SYS_PATH_HANDLE_REQUEST req = { SysSvFromCstr(path), handleOut };
|
||||||
|
return SysOpenFile(&req);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void print_hex8(uint8_t v) {
|
static void print_hex8(uint8_t v) {
|
||||||
@@ -44,7 +50,7 @@ static void file_apc(void* context, SYS_IO_STATUS_BLOCK* iosb, uint64_t informat
|
|||||||
st->Status = iosb ? iosb->Status : 0xFFFFFFFFu;
|
st->Status = iosb ? iosb->Status : 0xFFFFFFFFu;
|
||||||
st->Information = information;
|
st->Information = information;
|
||||||
st->Delivered++;
|
st->Delivered++;
|
||||||
SysDebugPrint("async_apc: user APC callback ran\n");
|
do { SYS_DEBUG_PRINT_REQUEST req = { SYS_SV_LITERAL("async_apc: user APC callback ran\n") }; SysDebugPrint(&req); } while (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(void) {
|
int main(void) {
|
||||||
@@ -59,7 +65,7 @@ int main(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
uint32_t file = 0;
|
uint32_t file = 0;
|
||||||
s = SysOpenFile("system/init.exe", &file);
|
s = open_file("system/init.exe", &file);
|
||||||
if (s != SYS_STATUS_SUCCESS) {
|
if (s != SYS_STATUS_SUCCESS) {
|
||||||
print("async_apc: open failed status=");
|
print("async_apc: open failed status=");
|
||||||
print_u64(s);
|
print_u64(s);
|
||||||
|
|||||||
@@ -6,7 +6,13 @@
|
|||||||
#define EVENT_AUTO_RESET 0u
|
#define EVENT_AUTO_RESET 0u
|
||||||
|
|
||||||
static void print(const char* s) {
|
static void print(const char* s) {
|
||||||
SysWriteConsole(s, strlen(s));
|
SYS_WRITE_CONSOLE_REQUEST req = { SysSvFromCstr(s) };
|
||||||
|
SysWriteConsole(&req);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t open_file(const char* path, uint32_t* handleOut) {
|
||||||
|
SYS_PATH_HANDLE_REQUEST req = { SysSvFromCstr(path), handleOut };
|
||||||
|
return SysOpenFile(&req);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void print_u64(uint64_t v) {
|
static void print_u64(uint64_t v) {
|
||||||
@@ -22,7 +28,7 @@ static void print_u64(uint64_t v) {
|
|||||||
}
|
}
|
||||||
while (i--) {
|
while (i--) {
|
||||||
char c = buf[i];
|
char c = buf[i];
|
||||||
SysWriteConsole(&c, 1);
|
do { SYS_WRITE_CONSOLE_REQUEST req = { SysSvFromParts(&c, 1) }; SysWriteConsole(&req); } while (0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -33,7 +39,7 @@ int main(void) {
|
|||||||
uint32_t event = 0;
|
uint32_t event = 0;
|
||||||
uint32_t request = 0;
|
uint32_t request = 0;
|
||||||
|
|
||||||
uint32_t s = SysOpenFile("system/init.exe", &file);
|
uint32_t s = open_file("system/init.exe", &file);
|
||||||
if (s != SYS_STATUS_SUCCESS) {
|
if (s != SYS_STATUS_SUCCESS) {
|
||||||
print("async_cancel: open failed status="); print_u64(s); print("\n");
|
print("async_cancel: open failed status="); print_u64(s); print("\n");
|
||||||
SysExitProcess(1);
|
SysExitProcess(1);
|
||||||
|
|||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
[kernel]
|
[kernel]
|
||||||
; bitmask: 1 = debug prints, 2 = self check
|
; bitmask: 1 = debug prints, 2 = self check
|
||||||
debug = 1
|
debug = 3
|
||||||
[init]
|
[init]
|
||||||
commandline = apps/shell_native.exe
|
commandline = apps/shell_native.exe
|
||||||
+62
-27
@@ -19,7 +19,8 @@ int _fltused;
|
|||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
static void doom_print_wrapper(const char* msg)
|
static void doom_print_wrapper(const char* msg)
|
||||||
{
|
{
|
||||||
SysWriteConsole(msg, strlen(msg));
|
SYS_WRITE_CONSOLE_REQUEST req = { SysSvFromCstr(msg) };
|
||||||
|
SysWriteConsole(&req);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
@@ -42,74 +43,108 @@ static void doom_free_wrapper(void* ptr)
|
|||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// File I/O wrappers
|
// File I/O wrappers
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
typedef struct DOOM_FILE_HANDLE {
|
||||||
|
uint32_t Handle;
|
||||||
|
uint64_t Pos;
|
||||||
|
uint64_t Size;
|
||||||
|
} DOOM_FILE_HANDLE;
|
||||||
|
|
||||||
static void* doom_open_wrapper(const char* path, const char* mode)
|
static void* doom_open_wrapper(const char* path, const char* mode)
|
||||||
{
|
{
|
||||||
uint32_t handle = 0;
|
uint32_t handle = 0;
|
||||||
uint32_t result;
|
uint32_t result;
|
||||||
|
|
||||||
|
SYS_PATH_HANDLE_REQUEST req = { SysSvFromCstr(path), &handle };
|
||||||
if (mode[0] == 'w') {
|
if (mode[0] == 'w') {
|
||||||
result = SysCreateFile(path, &handle);
|
result = SysCreateFile(&req);
|
||||||
} else {
|
} else {
|
||||||
result = SysOpenFile(path, &handle);
|
result = SysOpenFile(&req);
|
||||||
|
}
|
||||||
|
if (result != 0) return 0;
|
||||||
|
|
||||||
|
DOOM_FILE_HANDLE* f = (DOOM_FILE_HANDLE*)SysAlloc(sizeof(DOOM_FILE_HANDLE));
|
||||||
|
if (!f) {
|
||||||
|
SysCloseHandle(handle);
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (result == 0) ? (void*)(uintptr_t)handle : 0;
|
f->Handle = handle;
|
||||||
|
f->Pos = 0;
|
||||||
|
f->Size = SysGetFileSize(handle);
|
||||||
|
return f;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void doom_close_wrapper(void* handle)
|
static void doom_close_wrapper(void* handle)
|
||||||
{
|
{
|
||||||
SysCloseHandle((uint32_t)(uintptr_t)handle);
|
if (!handle) return;
|
||||||
|
DOOM_FILE_HANDLE* f = (DOOM_FILE_HANDLE*)handle;
|
||||||
|
SysCloseHandle(f->Handle);
|
||||||
|
SysFree(f);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int doom_read_wrapper(void* handle, void* buf, int count)
|
static int doom_read_wrapper(void* handle, void* buf, int count)
|
||||||
{
|
{
|
||||||
|
if (!handle || count <= 0) return 0;
|
||||||
|
DOOM_FILE_HANDLE* f = (DOOM_FILE_HANDLE*)handle;
|
||||||
size_t read = 0;
|
size_t read = 0;
|
||||||
SysReadFile((uint32_t)(uintptr_t)handle, buf, (size_t)count, &read);
|
SysReadFile(f->Handle, buf, (size_t)count, &read);
|
||||||
|
f->Pos += read;
|
||||||
|
if (f->Pos > f->Size) f->Size = f->Pos;
|
||||||
return (int)read;
|
return (int)read;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int doom_write_wrapper(void* handle, const void* buf, int count)
|
static int doom_write_wrapper(void* handle, const void* buf, int count)
|
||||||
{
|
{
|
||||||
|
if (!handle || count <= 0) return 0;
|
||||||
|
DOOM_FILE_HANDLE* f = (DOOM_FILE_HANDLE*)handle;
|
||||||
size_t written = 0;
|
size_t written = 0;
|
||||||
SysWriteFile((uint32_t)(uintptr_t)handle, buf, (size_t)count, &written);
|
SysWriteFile(f->Handle, buf, (size_t)count, &written);
|
||||||
|
f->Pos += written;
|
||||||
|
if (f->Pos > f->Size) f->Size = f->Pos;
|
||||||
return (int)written;
|
return (int)written;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int doom_seek_wrapper(void* handle, int offset, doom_seek_t origin)
|
static int doom_seek_wrapper(void* handle, int offset, doom_seek_t origin)
|
||||||
{
|
{
|
||||||
uint32_t h = (uint32_t)(uintptr_t)handle;
|
if (!handle) return -1;
|
||||||
uint64_t absolute_offset = 0;
|
DOOM_FILE_HANDLE* f = (DOOM_FILE_HANDLE*)handle;
|
||||||
|
int64_t base = 0;
|
||||||
|
|
||||||
switch (origin) {
|
switch (origin) {
|
||||||
case DOOM_SEEK_SET:
|
case DOOM_SEEK_SET:
|
||||||
absolute_offset = (uint64_t)offset;
|
base = 0;
|
||||||
break;
|
break;
|
||||||
case DOOM_SEEK_CUR:
|
case DOOM_SEEK_CUR:
|
||||||
absolute_offset = SysGetFilePos(h) + offset;
|
base = (int64_t)f->Pos;
|
||||||
break;
|
break;
|
||||||
case DOOM_SEEK_END:
|
case DOOM_SEEK_END:
|
||||||
absolute_offset = SysGetFileSize(h) + offset;
|
base = (int64_t)f->Size;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
absolute_offset = (uint64_t)offset;
|
base = 0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
SysSeekFile(h, absolute_offset);
|
int64_t target = base + (int64_t)offset;
|
||||||
|
if (target < 0) target = 0;
|
||||||
|
|
||||||
|
uint32_t s = SysSeekFile(f->Handle, (uint64_t)target);
|
||||||
|
if (s != 0) return -1;
|
||||||
|
f->Pos = (uint64_t)target;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int doom_tell_wrapper(void* handle)
|
static int doom_tell_wrapper(void* handle)
|
||||||
{
|
{
|
||||||
return (int)SysGetFilePos((uint32_t)(uintptr_t)handle);
|
if (!handle) return -1;
|
||||||
|
return (int)((DOOM_FILE_HANDLE*)handle)->Pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int doom_eof_wrapper(void* handle)
|
static int doom_eof_wrapper(void* handle)
|
||||||
{
|
{
|
||||||
uint32_t h = (uint32_t)(uintptr_t)handle;
|
if (!handle) return 1;
|
||||||
uint64_t size = SysGetFileSize(h);
|
DOOM_FILE_HANDLE* f = (DOOM_FILE_HANDLE*)handle;
|
||||||
uint64_t pos = SysGetFilePos(h);
|
return (f->Pos >= f->Size) ? 1 : 0;
|
||||||
return (pos >= size) ? 1 : 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
@@ -305,9 +340,9 @@ static int build_argv_from_command_line(const char* cmdline, char* storage, size
|
|||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
int main(void)
|
int main(void)
|
||||||
{
|
{
|
||||||
const char* cmdline = SysGetPeb()->CommandLine;
|
const char* cmdline = SysGetPeb()->CommandLine.Data;
|
||||||
|
|
||||||
SysWriteConsole("doom: starting\n", 15);
|
do { SYS_WRITE_CONSOLE_REQUEST req = { SYS_SV_LITERAL("doom: starting\n") }; SysWriteConsole(&req); } while (0);
|
||||||
|
|
||||||
// Wire up PureDOOM core mechanics
|
// Wire up PureDOOM core mechanics
|
||||||
doom_set_print(doom_print_wrapper);
|
doom_set_print(doom_print_wrapper);
|
||||||
@@ -339,7 +374,7 @@ int main(void)
|
|||||||
int argc = build_argv_from_command_line(cmdline, argvStorage, sizeof(argvStorage), argv, 32);
|
int argc = build_argv_from_command_line(cmdline, argvStorage, sizeof(argvStorage), argv, 32);
|
||||||
doom_init(argc, argv, 0);
|
doom_init(argc, argv, 0);
|
||||||
|
|
||||||
SysWriteConsole("doom: entering game loop\n", 25);
|
do { SYS_WRITE_CONSOLE_REQUEST req = { SYS_SV_LITERAL("doom: entering game loop\n") }; SysWriteConsole(&req); } while (0);
|
||||||
|
|
||||||
// Core execution loop
|
// Core execution loop
|
||||||
while (1) {
|
while (1) {
|
||||||
|
|||||||
+248
-144
@@ -425,26 +425,6 @@ static KSTATUS DirentUpdateFirstCluster(FAT32_VOL *v, uint64_t lba,
|
|||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// 8.3 name matching / assembly
|
// 8.3 name matching / assembly
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
static int MatchName83(const char *dirName, const char *dirExt,
|
|
||||||
const char *want) {
|
|
||||||
char buf[13]; int n = 0;
|
|
||||||
for (int i = 0; i < 8 && dirName[i] != ' '; i++)
|
|
||||||
buf[n++] = (char)(dirName[i] >= 'A' && dirName[i] <= 'Z' ? dirName[i]+32 : dirName[i]);
|
|
||||||
if (dirExt[0] != ' ') {
|
|
||||||
buf[n++] = '.';
|
|
||||||
for (int i = 0; i < 3 && dirExt[i] != ' '; i++)
|
|
||||||
buf[n++] = (char)(dirExt[i] >= 'A' && dirExt[i] <= 'Z' ? dirExt[i]+32 : dirExt[i]);
|
|
||||||
}
|
|
||||||
buf[n] = '\0';
|
|
||||||
const char *p = buf;
|
|
||||||
while (*want && *p) {
|
|
||||||
char a = *p++, b = *want++;
|
|
||||||
if (a >= 'A' && a <= 'Z') a += 32;
|
|
||||||
if (b >= 'A' && b <= 'Z') b += 32;
|
|
||||||
if (a != b) return 0;
|
|
||||||
}
|
|
||||||
return *want == '\0' && *p == '\0';
|
|
||||||
}
|
|
||||||
|
|
||||||
static int MatchRaw83(const char dirName[8], const char dirExt[3],
|
static int MatchRaw83(const char dirName[8], const char dirExt[3],
|
||||||
const char wantName[8], const char wantExt[3]) {
|
const char wantName[8], const char wantExt[3]) {
|
||||||
@@ -452,34 +432,35 @@ static int MatchRaw83(const char dirName[8], const char dirExt[3],
|
|||||||
memcmp(dirExt, wantExt, 3) == 0;
|
memcmp(dirExt, wantExt, 3) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void Make83Name(const char *src, char name8[8], char ext3[3]) {
|
static char FatUpperChar(char c);
|
||||||
memset(name8, ' ', 8); memset(ext3, ' ', 3);
|
static int FatIsLowerChar(char c);
|
||||||
|
static int FatIsValidShortChar(char c);
|
||||||
|
|
||||||
// A leading dot is not a FAT 8.3 extension separator. The old version
|
static void Make83NameV(SYS_STRING_VIEW src, char name8[8], char ext3[3]) {
|
||||||
// turned ".doomrc" into an invalid empty-base short name with extension
|
memset(name8, ' ', 8);
|
||||||
// "DOO", making .doomrc, .doom and .doo all alias the same entry. Treat
|
memset(ext3, ' ', 3);
|
||||||
// leading-dot names as a basename here; the create path will usually emit
|
if (!SysSvIsValid(src) || src.Length == 0) return;
|
||||||
// a VFAT long-name entry for them anyway.
|
|
||||||
const char *base = src;
|
size_t baseStart = 0;
|
||||||
const char *dot = NULL;
|
size_t dot = (size_t)-1;
|
||||||
if (src && src[0] == '.' && src[1]) {
|
if (src.Data[0] == '.' && src.Length > 1) {
|
||||||
base = src + 1;
|
baseStart = 1;
|
||||||
} else {
|
} else {
|
||||||
for (const char *p = src; p && *p; p++) if (*p == '.') dot = p;
|
for (size_t i = 0; i < src.Length; i++)
|
||||||
|
if (src.Data[i] == '.') dot = i;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t end = (dot != (size_t)-1) ? dot : src.Length;
|
||||||
int ni = 0;
|
int ni = 0;
|
||||||
const char *end = dot ? dot : base + strlen(base);
|
for (size_t i = baseStart; i < end && ni < 8; i++) {
|
||||||
for (const char *p = base; p < end && ni < 8; p++) {
|
char c = FatUpperChar(src.Data[i]);
|
||||||
char c = *p;
|
|
||||||
if (c >= 'a' && c <= 'z') c -= 32;
|
|
||||||
name8[ni++] = c;
|
name8[ni++] = c;
|
||||||
}
|
}
|
||||||
if (dot) {
|
|
||||||
|
if (dot != (size_t)-1) {
|
||||||
int ei = 0;
|
int ei = 0;
|
||||||
for (const char *p = dot + 1; *p && ei < 3; p++) {
|
for (size_t i = dot + 1; i < src.Length && ei < 3; i++) {
|
||||||
char c = *p;
|
char c = FatUpperChar(src.Data[i]);
|
||||||
if (c >= 'a' && c <= 'z') c -= 32;
|
|
||||||
ext3[ei++] = c;
|
ext3[ei++] = c;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -515,30 +496,31 @@ static int FatIsValidShortChar(char c) {
|
|||||||
// True when the user spelling can refer to a normal FAT 8.3 entry. This is
|
// True when the user spelling can refer to a normal FAT 8.3 entry. This is
|
||||||
// intentionally structural and case-insensitive: "doom1.wad" must still open
|
// intentionally structural and case-insensitive: "doom1.wad" must still open
|
||||||
// an existing short entry named DOOM1.WAD. Leading-dot names are not 8.3.
|
// an existing short entry named DOOM1.WAD. Leading-dot names are not 8.3.
|
||||||
static int FatNameIsStructurallyShort83(const char *name) {
|
static int FatNameIsStructurallyShort83V(SYS_STRING_VIEW name) {
|
||||||
if (!name || !name[0]) return 0;
|
if (!SysSvIsValid(name) || name.Length == 0) return 0;
|
||||||
if (name[0] == '.') return 0;
|
if (name.Data[0] == '.') return 0;
|
||||||
|
|
||||||
const char *dot = NULL;
|
size_t dot = (size_t)-1;
|
||||||
for (const char *p = name; *p; p++) {
|
for (size_t i = 0; i < name.Length; i++) {
|
||||||
if (*p == '.') {
|
char c = name.Data[i];
|
||||||
if (dot) return 0; // multiple dots need LFN
|
if (c == '.') {
|
||||||
dot = p;
|
if (dot != (size_t)-1) return 0;
|
||||||
} else if (!FatIsValidShortChar(*p)) {
|
dot = i;
|
||||||
|
} else if (!FatIsValidShortChar(c)) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t baseLen = dot ? (size_t)(dot - name) : strlen(name);
|
size_t baseLen = (dot != (size_t)-1) ? dot : name.Length;
|
||||||
size_t extLen = dot ? strlen(dot + 1) : 0;
|
size_t extLen = (dot != (size_t)-1) ? (name.Length - dot - 1) : 0;
|
||||||
return baseLen >= 1 && baseLen <= 8 && extLen <= 3;
|
return baseLen >= 1 && baseLen <= 8 && extLen <= 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Creation uses LFN when needed for structure, or when lowercase would be lost.
|
// Creation uses LFN when needed for structure, or when lowercase would be lost.
|
||||||
static int FatNameNeedsLfnForCreate(const char *name) {
|
static int FatNameNeedsLfnForCreateV(SYS_STRING_VIEW name) {
|
||||||
if (!FatNameIsStructurallyShort83(name)) return 1;
|
if (!FatNameIsStructurallyShort83V(name)) return 1;
|
||||||
for (const char *p = name; *p; p++)
|
for (size_t i = 0; i < name.Length; i++)
|
||||||
if (FatIsLowerChar(*p)) return 1;
|
if (FatIsLowerChar(name.Data[i])) return 1;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -624,33 +606,35 @@ static void FatAliasAppendChar(char *dst, int *len, int maxLen, char c) {
|
|||||||
if (*len < maxLen) dst[(*len)++] = c;
|
if (*len < maxLen) dst[(*len)++] = c;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void BuildAliasStemExt(const char *src, char stem[9], char ext3[3]) {
|
static void BuildAliasStemExtV(SYS_STRING_VIEW src, char stem[9], char ext3[3]) {
|
||||||
memset(stem, 0, 9);
|
memset(stem, 0, 9);
|
||||||
memset(ext3, ' ', 3);
|
memset(ext3, ' ', 3);
|
||||||
|
if (!SysSvIsValid(src)) return;
|
||||||
|
|
||||||
const char *base = src;
|
size_t baseStart = 0;
|
||||||
const char *dot = NULL;
|
size_t dot = (size_t)-1;
|
||||||
if (src[0] == '.' && src[1]) {
|
if (src.Length > 1 && src.Data[0] == '.') {
|
||||||
// Hidden/Unix-style names like .doomrc become alias stem DOOMRC.
|
// Hidden/Unix-style names like .doomrc become alias stem DOOMRC.
|
||||||
base = src + 1;
|
baseStart = 1;
|
||||||
} else {
|
} else {
|
||||||
for (const char *p = src; *p; p++) if (*p == '.') dot = p;
|
for (size_t i = 0; i < src.Length; i++)
|
||||||
|
if (src.Data[i] == '.') dot = i;
|
||||||
}
|
}
|
||||||
|
|
||||||
int slen = 0;
|
int slen = 0;
|
||||||
const char *end = dot ? dot : src + strlen(src);
|
size_t end = (dot != (size_t)-1) ? dot : src.Length;
|
||||||
for (const char *p = base; p < end && *p; p++)
|
for (size_t i = baseStart; i < end; i++)
|
||||||
FatAliasAppendChar(stem, &slen, 8, *p);
|
FatAliasAppendChar(stem, &slen, 8, src.Data[i]);
|
||||||
if (slen == 0) {
|
if (slen == 0) {
|
||||||
memcpy(stem, "FILE", 4);
|
memcpy(stem, "FILE", 4);
|
||||||
slen = 4;
|
slen = 4;
|
||||||
}
|
}
|
||||||
stem[slen] = '\0';
|
stem[slen] = '\0';
|
||||||
|
|
||||||
if (dot && dot[1]) {
|
if (dot != (size_t)-1 && dot + 1 < src.Length) {
|
||||||
int ei = 0;
|
int ei = 0;
|
||||||
for (const char *p = dot + 1; *p && ei < 3; p++) {
|
for (size_t i = dot + 1; i < src.Length && ei < 3; i++) {
|
||||||
char c = FatUpperChar(*p);
|
char c = FatUpperChar(src.Data[i]);
|
||||||
if (!FatIsValidShortChar(c)) continue;
|
if (!FatIsValidShortChar(c)) continue;
|
||||||
ext3[ei++] = c;
|
ext3[ei++] = c;
|
||||||
}
|
}
|
||||||
@@ -683,11 +667,11 @@ static void MakeAliasAttempt(const char stem[9], const char extIn[3],
|
|||||||
memcpy(name8 + keep, suffix, suffixLen);
|
memcpy(name8 + keep, suffix, suffixLen);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int MakeUniqueShortAlias(FAT32_VOL *v, uint32_t dirCluster,
|
static int MakeUniqueShortAliasV(FAT32_VOL *v, uint32_t dirCluster,
|
||||||
const char *longName,
|
SYS_STRING_VIEW longName,
|
||||||
char name8[8], char ext3[3]) {
|
char name8[8], char ext3[3]) {
|
||||||
char stem[9], extCandidate[3];
|
char stem[9], extCandidate[3];
|
||||||
BuildAliasStemExt(longName, stem, extCandidate);
|
BuildAliasStemExtV(longName, stem, extCandidate);
|
||||||
|
|
||||||
for (uint32_t attempt = 1; attempt < 1000000; attempt++) {
|
for (uint32_t attempt = 1; attempt < 1000000; attempt++) {
|
||||||
MakeAliasAttempt(stem, extCandidate, attempt, name8, ext3);
|
MakeAliasAttempt(stem, extCandidate, attempt, name8, ext3);
|
||||||
@@ -696,12 +680,25 @@ static int MakeUniqueShortAlias(FAT32_VOL *v, uint32_t dirCluster,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static KSTATUS DeleteLegacyBrokenDotAlias(FAT32_VOL *v, uint32_t dirCluster,
|
static void MakeLegacyBrokenDot83V(SYS_STRING_VIEW src, char name8[8], char ext3[3]) {
|
||||||
const char *longName) {
|
// This exactly models the pre-fix bug for ".doomrc": empty name + "DOO".
|
||||||
if (!longName || longName[0] != '.' || !longName[1]) return STATUS_SUCCESS;
|
memset(name8, ' ', 8);
|
||||||
|
memset(ext3, ' ', 3);
|
||||||
|
if (!SysSvIsValid(src)) return;
|
||||||
|
size_t dot = (size_t)-1;
|
||||||
|
for (size_t i = 0; i < src.Length; i++) if (src.Data[i] == '.') dot = i;
|
||||||
|
if (dot == (size_t)-1) return;
|
||||||
|
int ei = 0;
|
||||||
|
for (size_t i = dot + 1; i < src.Length && ei < 3; i++)
|
||||||
|
ext3[ei++] = FatUpperChar(src.Data[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static KSTATUS DeleteLegacyBrokenDotAliasV(FAT32_VOL *v, uint32_t dirCluster,
|
||||||
|
SYS_STRING_VIEW longName) {
|
||||||
|
if (!SysSvIsValid(longName) || longName.Length < 2 || longName.Data[0] != '.') return STATUS_SUCCESS;
|
||||||
|
|
||||||
char badName[8], badExt[3];
|
char badName[8], badExt[3];
|
||||||
MakeLegacyBrokenDot83(longName, badName, badExt);
|
MakeLegacyBrokenDot83V(longName, badName, badExt);
|
||||||
|
|
||||||
uint8_t *clusterBuf = (uint8_t *)MmAllocatePool(v->BytesPerCluster);
|
uint8_t *clusterBuf = (uint8_t *)MmAllocatePool(v->BytesPerCluster);
|
||||||
if (!clusterBuf) return STATUS_NO_MEMORY;
|
if (!clusterBuf) return STATUS_NO_MEMORY;
|
||||||
@@ -754,29 +751,71 @@ static void LfnAppend(char *lfn, const LFN_ENTRY *e) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int LfnMatch(const char *lfn, const char *want) {
|
|
||||||
while (*lfn && *want) {
|
// ---------------------------------------------------------------------------
|
||||||
char a = *lfn++, b = *want++;
|
// Directory search and view-native path traversal
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
static int Fat32IsPathSep(char c) {
|
||||||
|
return c == '\\' || c == '/';
|
||||||
|
}
|
||||||
|
|
||||||
|
static int Fat32NameViewIsValid(SYS_STRING_VIEW name) {
|
||||||
|
if (!SysSvIsValid(name)) return 0;
|
||||||
|
if (name.Length == 0 || name.Length >= MAX_LFN) return 0;
|
||||||
|
for (size_t i = 0; i < name.Length; i++) {
|
||||||
|
char c = name.Data[i];
|
||||||
|
if (c == '\0' || Fat32IsPathSep(c)) return 0;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int LfnMatchV(const char *lfn, SYS_STRING_VIEW want) {
|
||||||
|
if (!lfn || !SysSvIsValid(want)) return 0;
|
||||||
|
size_t i = 0;
|
||||||
|
while (lfn[i] && i < want.Length) {
|
||||||
|
char a = lfn[i], b = want.Data[i];
|
||||||
|
if (a >= 'A' && a <= 'Z') a += 32;
|
||||||
|
if (b >= 'A' && b <= 'Z') b += 32;
|
||||||
|
if (a != b) return 0;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
return lfn[i] == '\0' && i == want.Length;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int MatchName83V(const char *dirName, const char *dirExt,
|
||||||
|
SYS_STRING_VIEW want) {
|
||||||
|
char buf[13];
|
||||||
|
int n = 0;
|
||||||
|
for (int i = 0; i < 8 && dirName[i] != ' '; i++)
|
||||||
|
buf[n++] = (char)(dirName[i] >= 'A' && dirName[i] <= 'Z' ? dirName[i]+32 : dirName[i]);
|
||||||
|
if (dirExt[0] != ' ') {
|
||||||
|
buf[n++] = '.';
|
||||||
|
for (int i = 0; i < 3 && dirExt[i] != ' '; i++)
|
||||||
|
buf[n++] = (char)(dirExt[i] >= 'A' && dirExt[i] <= 'Z' ? dirExt[i]+32 : dirExt[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((size_t)n != want.Length) return 0;
|
||||||
|
for (int i = 0; i < n; i++) {
|
||||||
|
char a = buf[i], b = want.Data[i];
|
||||||
if (a >= 'A' && a <= 'Z') a += 32;
|
if (a >= 'A' && a <= 'Z') a += 32;
|
||||||
if (b >= 'A' && b <= 'Z') b += 32;
|
if (b >= 'A' && b <= 'Z') b += 32;
|
||||||
if (a != b) return 0;
|
if (a != b) return 0;
|
||||||
}
|
}
|
||||||
return *lfn == '\0' && *want == '\0';
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
static int SearchDirV(FAT32_VOL *v, uint32_t dirCluster, SYS_STRING_VIEW name,
|
||||||
// Directory search
|
uint32_t *outCluster, uint32_t *outSize, uint8_t *outAttr,
|
||||||
// ---------------------------------------------------------------------------
|
uint64_t *outDirentLba, uint32_t *outDirentOffset) {
|
||||||
static int SearchDir(FAT32_VOL *v, uint32_t dirCluster, const char *name,
|
if (!Fat32NameViewIsValid(name)) return 0;
|
||||||
uint32_t *outCluster, uint32_t *outSize, uint8_t *outAttr,
|
|
||||||
uint64_t *outDirentLba, uint32_t *outDirentOffset) {
|
|
||||||
uint8_t *clusterBuf = (uint8_t *)MmAllocatePool(v->BytesPerCluster);
|
uint8_t *clusterBuf = (uint8_t *)MmAllocatePool(v->BytesPerCluster);
|
||||||
if (!clusterBuf) return 0;
|
if (!clusterBuf) return 0;
|
||||||
|
|
||||||
char lfn[MAX_LFN]; int hasLfn = 0; memset(lfn, 0, sizeof(lfn));
|
char lfn[MAX_LFN]; int hasLfn = 0; memset(lfn, 0, sizeof(lfn));
|
||||||
char wantName83[8], wantExt83[3];
|
char wantName83[8], wantExt83[3];
|
||||||
int allowShortMatch = FatNameIsStructurallyShort83(name);
|
int allowShortMatch = FatNameIsStructurallyShort83V(name);
|
||||||
Make83Name(name, wantName83, wantExt83);
|
Make83NameV(name, wantName83, wantExt83);
|
||||||
uint32_t cluster = dirCluster;
|
uint32_t cluster = dirCluster;
|
||||||
|
|
||||||
while (cluster >= 2 && cluster < FAT32_EOC) {
|
while (cluster >= 2 && cluster < FAT32_EOC) {
|
||||||
@@ -792,9 +831,9 @@ static int SearchDir(FAT32_VOL *v, uint32_t dirCluster, const char *name,
|
|||||||
if (dir[i].Attr == ATTR_LFN) { LfnAppend(lfn, (LFN_ENTRY *)&dir[i]); hasLfn = 1; continue; }
|
if (dir[i].Attr == ATTR_LFN) { LfnAppend(lfn, (LFN_ENTRY *)&dir[i]); hasLfn = 1; continue; }
|
||||||
|
|
||||||
int match = 0;
|
int match = 0;
|
||||||
if (hasLfn && lfn[0]) match = LfnMatch(lfn, name);
|
if (hasLfn && lfn[0]) match = LfnMatchV(lfn, name);
|
||||||
if (!match && allowShortMatch)
|
if (!match && allowShortMatch)
|
||||||
match = MatchName83(dir[i].Name, dir[i].Ext, name);
|
match = MatchName83V(dir[i].Name, dir[i].Ext, name);
|
||||||
if (!match && allowShortMatch)
|
if (!match && allowShortMatch)
|
||||||
match = MatchRaw83(dir[i].Name, dir[i].Ext, wantName83, wantExt83);
|
match = MatchRaw83(dir[i].Name, dir[i].Ext, wantName83, wantExt83);
|
||||||
hasLfn = 0; memset(lfn, 0, sizeof(lfn));
|
hasLfn = 0; memset(lfn, 0, sizeof(lfn));
|
||||||
@@ -819,58 +858,92 @@ done:
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
static int Fat32PathNextComponent(SYS_STRING_VIEW path, size_t* cursor,
|
||||||
// Path traversal
|
SYS_STRING_VIEW* component) {
|
||||||
// ---------------------------------------------------------------------------
|
if (!cursor || !component || !SysSvIsValid(path)) return 0;
|
||||||
static KSTATUS Fat32LookupPath(FAT32_VOL *v, const char *path,
|
size_t pos = *cursor;
|
||||||
uint32_t *outCluster, uint32_t *outSize,
|
while (pos < path.Length && Fat32IsPathSep(path.Data[pos])) pos++;
|
||||||
uint64_t *outDirentLba, uint32_t *outDirentOffset) {
|
if (pos >= path.Length) {
|
||||||
|
*cursor = pos;
|
||||||
|
*component = SYS_SV_NULL;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t start = pos;
|
||||||
|
while (pos < path.Length && !Fat32IsPathSep(path.Data[pos])) pos++;
|
||||||
|
*cursor = pos;
|
||||||
|
*component = SysSvFromParts(path.Data + start, pos - start);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static KSTATUS Fat32LookupPathV(FAT32_VOL *v, SYS_STRING_VIEW path,
|
||||||
|
uint32_t *outCluster, uint32_t *outSize,
|
||||||
|
uint64_t *outDirentLba, uint32_t *outDirentOffset) {
|
||||||
|
if (!v || !outCluster || !outSize || !SysSvIsValid(path))
|
||||||
|
return STATUS_INVALID_PARAMETER;
|
||||||
|
if (SysSvContainsByte(path, '\0'))
|
||||||
|
return STATUS_INVALID_PARAMETER;
|
||||||
|
|
||||||
uint32_t cluster = v->RootCluster, size = 0;
|
uint32_t cluster = v->RootCluster, size = 0;
|
||||||
uint8_t attr = ATTR_DIRECTORY;
|
uint8_t attr = ATTR_DIRECTORY;
|
||||||
if (outDirentLba) *outDirentLba = 0;
|
if (outDirentLba) *outDirentLba = 0;
|
||||||
if (outDirentOffset) *outDirentOffset = 0;
|
if (outDirentOffset) *outDirentOffset = 0;
|
||||||
|
|
||||||
char component[256]; const char *p = path;
|
size_t cursor = 0;
|
||||||
while (*p) {
|
SYS_STRING_VIEW component;
|
||||||
while (*p == '\\' || *p == '/') p++;
|
while (Fat32PathNextComponent(path, &cursor, &component)) {
|
||||||
if (!*p) break;
|
if (component.Length >= MAX_LFN) return STATUS_BUFFER_TOO_SMALL;
|
||||||
int len = 0;
|
if (!Fat32NameViewIsValid(component)) return STATUS_INVALID_PARAMETER;
|
||||||
while (*p && *p != '\\' && *p != '/' && len < 255) component[len++] = *p++;
|
|
||||||
component[len] = '\0';
|
|
||||||
if (!len) break;
|
|
||||||
if (!(attr & ATTR_DIRECTORY)) return STATUS_NOT_FOUND;
|
if (!(attr & ATTR_DIRECTORY)) return STATUS_NOT_FOUND;
|
||||||
|
|
||||||
uint32_t childCluster, childSize; uint8_t childAttr;
|
uint32_t childCluster, childSize; uint8_t childAttr;
|
||||||
uint64_t childLba = 0; uint32_t childOffset = 0;
|
uint64_t childLba = 0; uint32_t childOffset = 0;
|
||||||
if (!SearchDir(v, cluster, component, &childCluster, &childSize,
|
if (!SearchDirV(v, cluster, component, &childCluster, &childSize,
|
||||||
&childAttr, &childLba, &childOffset))
|
&childAttr, &childLba, &childOffset))
|
||||||
return STATUS_NOT_FOUND;
|
return STATUS_NOT_FOUND;
|
||||||
|
|
||||||
cluster = childCluster; size = childSize; attr = childAttr;
|
cluster = childCluster; size = childSize; attr = childAttr;
|
||||||
if (outDirentLba) *outDirentLba = childLba;
|
if (outDirentLba) *outDirentLba = childLba;
|
||||||
if (outDirentOffset) *outDirentOffset = childOffset;
|
if (outDirentOffset) *outDirentOffset = childOffset;
|
||||||
}
|
}
|
||||||
*outCluster = cluster; *outSize = size;
|
|
||||||
|
*outCluster = cluster;
|
||||||
|
*outSize = size;
|
||||||
return STATUS_SUCCESS;
|
return STATUS_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
static KSTATUS Traverse(FAT32_VOL *v, const char *path,
|
static KSTATUS TraverseV(FAT32_VOL *v, SYS_STRING_VIEW path,
|
||||||
uint32_t *outCluster, uint32_t *outSize) {
|
uint32_t *outCluster, uint32_t *outSize) {
|
||||||
return Fat32LookupPath(v, path, outCluster, outSize, NULL, NULL);
|
return Fat32LookupPathV(v, path, outCluster, outSize, NULL, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
static KSTATUS SplitPathV(SYS_STRING_VIEW path, SYS_STRING_VIEW* parent,
|
||||||
// Path helpers for Fat32Create
|
SYS_STRING_VIEW* name) {
|
||||||
// ---------------------------------------------------------------------------
|
if (!parent || !name || !SysSvIsValid(path)) return STATUS_INVALID_PARAMETER;
|
||||||
static void SplitPath(const char *path, char parent[256], char name[256]) {
|
*parent = SYS_SV_NULL;
|
||||||
const char *last = NULL;
|
*name = SYS_SV_NULL;
|
||||||
for (const char *p = path; *p; p++) if (*p == '\\' || *p == '/') last = p;
|
|
||||||
if (!last) { parent[0] = '\0'; strncpy(name, path, 255); name[255] = '\0'; }
|
while (path.Length && Fat32IsPathSep(path.Data[path.Length - 1]))
|
||||||
else {
|
path.Length--;
|
||||||
size_t len = (size_t)(last - path); if (len > 255) len = 255;
|
if (path.Length == 0) return STATUS_INVALID_PARAMETER;
|
||||||
memcpy(parent, path, len); parent[len] = '\0';
|
|
||||||
strncpy(name, last+1, 255); name[255] = '\0';
|
if (SysSvContainsByte(path, '\0')) return STATUS_INVALID_PARAMETER;
|
||||||
|
|
||||||
|
size_t lastSep = (size_t)-1;
|
||||||
|
for (size_t i = 0; i < path.Length; i++) {
|
||||||
|
if (Fat32IsPathSep(path.Data[i])) lastSep = i;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (lastSep == (size_t)-1) {
|
||||||
|
*parent = SYS_SV_NULL;
|
||||||
|
*name = path;
|
||||||
|
} else {
|
||||||
|
*parent = SysSvFromParts(path.Data, lastSep);
|
||||||
|
*name = SysSvFromParts(path.Data + lastSep + 1, path.Length - lastSep - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Fat32NameViewIsValid(*name)) return STATUS_INVALID_PARAMETER;
|
||||||
|
return STATUS_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
@@ -969,11 +1042,15 @@ static int FindFreeDirentRun(FAT32_VOL *v, uint32_t dirCluster, uint32_t needed,
|
|||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Open / Close
|
// Open / Close
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
static KSTATUS Fat32Open(IO_FILESYSTEM *fs, const char *path, void **fileCtx) {
|
static KSTATUS Fat32Open(IO_FILESYSTEM *fs, SYS_STRING_VIEW path, void **fileCtx) {
|
||||||
|
if (!fs || !fileCtx || !SysSvIsValid(path) || path.Length == 0)
|
||||||
|
return STATUS_INVALID_PARAMETER;
|
||||||
|
|
||||||
FAT32_VOL *v = (FAT32_VOL *)fs->DriverCtx;
|
FAT32_VOL *v = (FAT32_VOL *)fs->DriverCtx;
|
||||||
uint32_t cluster, size; uint64_t direntLba; uint32_t direntOffset;
|
uint32_t cluster, size; uint64_t direntLba; uint32_t direntOffset;
|
||||||
KSTATUS s = Fat32LookupPath(v, path, &cluster, &size, &direntLba, &direntOffset);
|
KSTATUS s = Fat32LookupPathV(v, path, &cluster, &size, &direntLba, &direntOffset);
|
||||||
if (!KSUCCESS(s)) return s;
|
if (!KSUCCESS(s)) return s;
|
||||||
|
|
||||||
FAT32_FILE *f = (FAT32_FILE *)MmAllocatePool(sizeof(FAT32_FILE));
|
FAT32_FILE *f = (FAT32_FILE *)MmAllocatePool(sizeof(FAT32_FILE));
|
||||||
if (!f) return STATUS_NO_MEMORY;
|
if (!f) return STATUS_NO_MEMORY;
|
||||||
f->StartCluster = cluster; f->FileSize = size; f->Vol = v;
|
f->StartCluster = cluster; f->FileSize = size; f->Vol = v;
|
||||||
@@ -1013,11 +1090,19 @@ static KSTATUS Fat32Read(IO_FILESYSTEM *fs, void *fileCtx,
|
|||||||
FAT32_FILE *f = (FAT32_FILE *)fileCtx;
|
FAT32_FILE *f = (FAT32_FILE *)fileCtx;
|
||||||
FAT32_VOL *v = f->Vol;
|
FAT32_VOL *v = f->Vol;
|
||||||
if (offset >= f->FileSize || size == 0) return STATUS_SUCCESS;
|
if (offset >= f->FileSize || size == 0) return STATUS_SUCCESS;
|
||||||
if (offset + size > f->FileSize) size = (size_t)(f->FileSize - offset);
|
if (size > (size_t)(f->FileSize - offset)) size = (size_t)(f->FileSize - offset);
|
||||||
size_t wanted = size;
|
size_t wanted = size;
|
||||||
|
|
||||||
// Single allocation for the largest possible coalesced run.
|
// Allocate only as much bounce space as this read can actually use.
|
||||||
uint8_t *runBuf = (uint8_t *)MmAllocatePool(MAX_RUN_SECTORS * 512u);
|
// The old code allocated 64 KiB and could read a 64 KiB contiguous run even
|
||||||
|
// for tiny random reads; Doom does lots of seek/read pairs for small lumps.
|
||||||
|
uint32_t initialOffsetInCluster = (uint32_t)(offset % v->BytesPerCluster);
|
||||||
|
uint64_t initialBytesCovered = (uint64_t)initialOffsetInCluster + (uint64_t)size;
|
||||||
|
uint32_t maxUsefulSectors = (uint32_t)((initialBytesCovered + 511u) / 512u);
|
||||||
|
if (maxUsefulSectors < v->SectorsPerCluster) maxUsefulSectors = v->SectorsPerCluster;
|
||||||
|
if (maxUsefulSectors > MAX_RUN_SECTORS) maxUsefulSectors = MAX_RUN_SECTORS;
|
||||||
|
|
||||||
|
uint8_t *runBuf = (uint8_t *)MmAllocatePool((size_t)maxUsefulSectors * 512u);
|
||||||
if (!runBuf) return STATUS_NO_MEMORY;
|
if (!runBuf) return STATUS_NO_MEMORY;
|
||||||
|
|
||||||
size_t totalRead = 0;
|
size_t totalRead = 0;
|
||||||
@@ -1031,13 +1116,21 @@ static KSTATUS Fat32Read(IO_FILESYSTEM *fs, void *fileCtx,
|
|||||||
cluster = FatEntry(v, cluster);
|
cluster = FatEntry(v, cluster);
|
||||||
|
|
||||||
while (size > 0 && cluster >= 2 && cluster < FAT32_EOC) {
|
while (size > 0 && cluster >= 2 && cluster < FAT32_EOC) {
|
||||||
// Build the longest physically-contiguous run we can.
|
// Build the longest physically-contiguous run that can contribute to
|
||||||
|
// this caller's remaining read. Do not prefetch an unrelated 64 KiB run
|
||||||
|
// for a tiny random read.
|
||||||
|
uint64_t bytesCovered = (uint64_t)offsetInCluster + (uint64_t)size;
|
||||||
|
uint32_t usefulSectors = (uint32_t)((bytesCovered + 511u) / 512u);
|
||||||
|
if (usefulSectors < v->SectorsPerCluster) usefulSectors = v->SectorsPerCluster;
|
||||||
|
if (usefulSectors > MAX_RUN_SECTORS) usefulSectors = MAX_RUN_SECTORS;
|
||||||
|
if (usefulSectors > maxUsefulSectors) usefulSectors = maxUsefulSectors;
|
||||||
|
|
||||||
uint32_t runStart = cluster;
|
uint32_t runStart = cluster;
|
||||||
uint32_t runSectors = v->SectorsPerCluster;
|
uint32_t runSectors = v->SectorsPerCluster;
|
||||||
uint32_t next = FatEntry(v, cluster);
|
uint32_t next = FatEntry(v, cluster);
|
||||||
|
|
||||||
while (next == cluster + 1 && next < FAT32_EOC
|
while (next == cluster + 1 && next < FAT32_EOC
|
||||||
&& runSectors + v->SectorsPerCluster <= MAX_RUN_SECTORS) {
|
&& runSectors + v->SectorsPerCluster <= usefulSectors) {
|
||||||
cluster = next;
|
cluster = next;
|
||||||
next = FatEntry(v, cluster);
|
next = FatEntry(v, cluster);
|
||||||
runSectors += v->SectorsPerCluster;
|
runSectors += v->SectorsPerCluster;
|
||||||
@@ -1188,21 +1281,25 @@ static KSTATUS Fat32Write(IO_FILESYSTEM *fs, void *fileCtx,
|
|||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
static KSTATUS Fat32Truncate(IO_FILESYSTEM *fs, void *fileCtx, uint64_t newSize);
|
static KSTATUS Fat32Truncate(IO_FILESYSTEM *fs, void *fileCtx, uint64_t newSize);
|
||||||
|
|
||||||
static KSTATUS Fat32Create(IO_FILESYSTEM *fs, const char *path, void **fileCtx) {
|
static KSTATUS Fat32Create(IO_FILESYSTEM *fs, SYS_STRING_VIEW path, void **fileCtx) {
|
||||||
|
if (!fs || !fileCtx || !SysSvIsValid(path) || path.Length == 0)
|
||||||
|
return STATUS_INVALID_PARAMETER;
|
||||||
|
|
||||||
FAT32_VOL *v = (FAT32_VOL *)fs->DriverCtx;
|
FAT32_VOL *v = (FAT32_VOL *)fs->DriverCtx;
|
||||||
char parentPath[256], filename[256];
|
SYS_STRING_VIEW parentPath;
|
||||||
SplitPath(path, parentPath, filename);
|
SYS_STRING_VIEW filenameView;
|
||||||
if (!filename[0]) return STATUS_INVALID_PARAMETER;
|
KSTATUS s = SplitPathV(path, &parentPath, &filenameView);
|
||||||
|
if (!KSUCCESS(s)) return s;
|
||||||
|
|
||||||
uint32_t parentCluster, parentSize;
|
uint32_t parentCluster, parentSize;
|
||||||
KSTATUS s = Traverse(v, parentPath, &parentCluster, &parentSize);
|
s = TraverseV(v, parentPath, &parentCluster, &parentSize);
|
||||||
if (!KSUCCESS(s)) return s;
|
if (!KSUCCESS(s)) return s;
|
||||||
|
|
||||||
{
|
{
|
||||||
uint32_t ec, es; uint8_t ea;
|
uint32_t ec, es; uint8_t ea;
|
||||||
uint64_t existingLba = 0; uint32_t existingOffset = 0;
|
uint64_t existingLba = 0; uint32_t existingOffset = 0;
|
||||||
if (SearchDir(v, parentCluster, filename, &ec, &es, &ea,
|
if (SearchDirV(v, parentCluster, filenameView, &ec, &es, &ea,
|
||||||
&existingLba, &existingOffset)) {
|
&existingLba, &existingOffset)) {
|
||||||
if (ea & ATTR_DIRECTORY)
|
if (ea & ATTR_DIRECTORY)
|
||||||
return STATUS_OBJECT_NAME_COLLISION;
|
return STATUS_OBJECT_NAME_COLLISION;
|
||||||
|
|
||||||
@@ -1224,18 +1321,18 @@ static KSTATUS Fat32Create(IO_FILESYSTEM *fs, const char *path, void **fileCtx)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int useLfn = FatNameNeedsLfnForCreate(filename);
|
int useLfn = FatNameNeedsLfnForCreateV(filenameView);
|
||||||
char shortName[8], shortExt[3];
|
char shortName[8], shortExt[3];
|
||||||
if (useLfn) {
|
if (useLfn) {
|
||||||
s = DeleteLegacyBrokenDotAlias(v, parentCluster, filename);
|
s = DeleteLegacyBrokenDotAliasV(v, parentCluster, filenameView);
|
||||||
if (!KSUCCESS(s)) return s;
|
if (!KSUCCESS(s)) return s;
|
||||||
if (!MakeUniqueShortAlias(v, parentCluster, filename, shortName, shortExt))
|
if (!MakeUniqueShortAliasV(v, parentCluster, filenameView, shortName, shortExt))
|
||||||
return STATUS_OBJECT_NAME_COLLISION;
|
return STATUS_OBJECT_NAME_COLLISION;
|
||||||
} else {
|
} else {
|
||||||
Make83Name(filename, shortName, shortExt);
|
Make83NameV(filenameView, shortName, shortExt);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t nameLen = strlen(filename);
|
size_t nameLen = filenameView.Length;
|
||||||
if (nameLen == 0 || nameLen >= MAX_LFN) return STATUS_INVALID_PARAMETER;
|
if (nameLen == 0 || nameLen >= MAX_LFN) return STATUS_INVALID_PARAMETER;
|
||||||
uint32_t lfnCount = useLfn ? (uint32_t)((nameLen + 12u) / 13u) : 0;
|
uint32_t lfnCount = useLfn ? (uint32_t)((nameLen + 12u) / 13u) : 0;
|
||||||
uint32_t neededEntries = lfnCount + 1u;
|
uint32_t neededEntries = lfnCount + 1u;
|
||||||
@@ -1258,7 +1355,7 @@ static KSTATUS Fat32Create(IO_FILESYSTEM *fs, const char *path, void **fileCtx)
|
|||||||
uint8_t checksum = LfnChecksum(shortName, shortExt);
|
uint8_t checksum = LfnChecksum(shortName, shortExt);
|
||||||
for (uint32_t slot = 0; slot < lfnCount; slot++) {
|
for (uint32_t slot = 0; slot < lfnCount; slot++) {
|
||||||
uint32_t ordinal = lfnCount - slot;
|
uint32_t ordinal = lfnCount - slot;
|
||||||
BuildLfnEntry((LFN_ENTRY *)&dir[firstIndex + slot], filename,
|
BuildLfnEntry((LFN_ENTRY *)&dir[firstIndex + slot], filenameView.Data,
|
||||||
nameLen, ordinal, lfnCount, checksum);
|
nameLen, ordinal, lfnCount, checksum);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1348,10 +1445,13 @@ update_size:
|
|||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Delete
|
// Delete
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
static KSTATUS Fat32Delete(IO_FILESYSTEM *fs, const char *path) {
|
static KSTATUS Fat32Delete(IO_FILESYSTEM *fs, SYS_STRING_VIEW path) {
|
||||||
|
if (!fs || !SysSvIsValid(path) || path.Length == 0)
|
||||||
|
return STATUS_INVALID_PARAMETER;
|
||||||
|
|
||||||
FAT32_VOL *v = (FAT32_VOL *)fs->DriverCtx;
|
FAT32_VOL *v = (FAT32_VOL *)fs->DriverCtx;
|
||||||
uint32_t cluster, size; uint64_t direntLba; uint32_t direntOffset;
|
uint32_t cluster, size; uint64_t direntLba; uint32_t direntOffset;
|
||||||
KSTATUS s = Fat32LookupPath(v, path, &cluster, &size, &direntLba, &direntOffset);
|
KSTATUS s = Fat32LookupPathV(v, path, &cluster, &size, &direntLba, &direntOffset);
|
||||||
if (!KSUCCESS(s)) return s;
|
if (!KSUCCESS(s)) return s;
|
||||||
if (!direntLba) return STATUS_ACCESS_DENIED;
|
if (!direntLba) return STATUS_ACCESS_DENIED;
|
||||||
|
|
||||||
@@ -1389,13 +1489,17 @@ typedef struct {
|
|||||||
uint8_t *ClusterBuf; // allocated once in OpenDir, freed in CloseDir
|
uint8_t *ClusterBuf; // allocated once in OpenDir, freed in CloseDir
|
||||||
} FAT32_DIR_CTX;
|
} FAT32_DIR_CTX;
|
||||||
|
|
||||||
static KSTATUS Fat32OpenDir(IO_FILESYSTEM *fs, const char *dirPath, void **dirCtx) {
|
static KSTATUS Fat32OpenDir(IO_FILESYSTEM *fs, SYS_STRING_VIEW dirPath, void **dirCtx) {
|
||||||
|
if (!fs || !dirCtx || !SysSvIsValid(dirPath))
|
||||||
|
return STATUS_INVALID_PARAMETER;
|
||||||
|
|
||||||
FAT32_VOL *v = (FAT32_VOL *)fs->DriverCtx;
|
FAT32_VOL *v = (FAT32_VOL *)fs->DriverCtx;
|
||||||
uint32_t cluster;
|
uint32_t cluster;
|
||||||
if (!dirPath || dirPath[0] == '\0') {
|
if (dirPath.Length == 0) {
|
||||||
cluster = v->RootCluster;
|
cluster = v->RootCluster;
|
||||||
} else {
|
} else {
|
||||||
uint32_t size; KSTATUS s = Traverse(v, dirPath, &cluster, &size);
|
uint32_t size;
|
||||||
|
KSTATUS s = TraverseV(v, dirPath, &cluster, &size);
|
||||||
if (!KSUCCESS(s)) return s;
|
if (!KSUCCESS(s)) return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+8
-5
@@ -302,8 +302,9 @@ static void move_down(void) {
|
|||||||
|
|
||||||
static void save_file(void) {
|
static void save_file(void) {
|
||||||
uint32_t h = 0;
|
uint32_t h = 0;
|
||||||
uint32_t st = SysCreateFile(gFilename, &h);
|
SYS_PATH_HANDLE_REQUEST pathReq = { SysSvFromCstr(gFilename), &h };
|
||||||
if (st != 0) st = SysOpenFile(gFilename, &h);
|
uint32_t st = SysCreateFile(&pathReq);
|
||||||
|
if (st != 0) st = SysOpenFile(&pathReq);
|
||||||
if (st != 0) { msg_set("save failed: cannot open file"); return; }
|
if (st != 0) { msg_set("save failed: cannot open file"); return; }
|
||||||
|
|
||||||
SysTruncateFile(h, 0);
|
SysTruncateFile(h, 0);
|
||||||
@@ -339,7 +340,8 @@ static void load_file(void) {
|
|||||||
reset_buffer();
|
reset_buffer();
|
||||||
|
|
||||||
uint32_t h = 0;
|
uint32_t h = 0;
|
||||||
if (SysOpenFile(gFilename, &h) != 0) {
|
SYS_PATH_HANDLE_REQUEST pathReq = { SysSvFromCstr(gFilename), &h };
|
||||||
|
if (SysOpenFile(&pathReq) != 0) {
|
||||||
msg_set("new file");
|
msg_set("new file");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -411,7 +413,7 @@ static bool read_token(const char* s, char* out, size_t cap) {
|
|||||||
|
|
||||||
static void parse_filename_from_cmdline(void) {
|
static void parse_filename_from_cmdline(void) {
|
||||||
SYS_PEB* peb = SysGetPeb();
|
SYS_PEB* peb = SysGetPeb();
|
||||||
const char* cmd = peb ? peb->CommandLine : NULL;
|
const char* cmd = (peb && peb->CommandLine.Data) ? peb->CommandLine.Data : NULL;
|
||||||
const char* arg = skip_token(cmd);
|
const char* arg = skip_token(cmd);
|
||||||
if (read_token(arg, gFilename, sizeof(gFilename))) return;
|
if (read_token(arg, gFilename, sizeof(gFilename))) return;
|
||||||
strcpy(gFilename, "untitled.txt");
|
strcpy(gFilename, "untitled.txt");
|
||||||
@@ -476,7 +478,8 @@ static void editor_loop(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void print(const char* s) {
|
static void print(const char* s) {
|
||||||
SysWriteConsole(s, strlen(s));
|
SYS_WRITE_CONSOLE_REQUEST req = { SysSvFromCstr(s) };
|
||||||
|
SysWriteConsole(&req);
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(void) {
|
int main(void) {
|
||||||
|
|||||||
+29
-10
@@ -23,6 +23,7 @@ static volatile uint32_t* gLapic = NULL;
|
|||||||
static uint32_t gApicTicksPerUs = 0;
|
static uint32_t gApicTicksPerUs = 0;
|
||||||
static uint32_t gApicTicksPerQuantum = 0;
|
static uint32_t gApicTicksPerQuantum = 0;
|
||||||
static uint64_t gHpetTicksPerUs = 0;
|
static uint64_t gHpetTicksPerUs = 0;
|
||||||
|
static uint64_t gHpetHz = 0;
|
||||||
static volatile uint64_t* gHpet = NULL;
|
static volatile uint64_t* gHpet = NULL;
|
||||||
#define LAPIC_REG(off) (*(volatile uint32_t *)((uint8_t *)gLapic + (off)))
|
#define LAPIC_REG(off) (*(volatile uint32_t *)((uint8_t *)gLapic + (off)))
|
||||||
|
|
||||||
@@ -39,11 +40,28 @@ static void TimerIsr(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static uint64_t QueryTicks(void) {
|
static uint64_t QueryTicks(void) {
|
||||||
uint64_t baseTicks = gTicks; // The 10ms "coarse" ticks
|
// Prefer HPET when present: it is already a monotonically increasing main
|
||||||
|
// counter and avoids races between the LAPIC periodic tick and the current
|
||||||
// The LAPIC timer counts DOWN.
|
// countdown register.
|
||||||
// Ticks elapsed = (Initial Value) - (Current Value)
|
if (gHpet && gHpetHz) {
|
||||||
uint32_t current = LAPIC_REG(LAPIC_TIMER_CUR);
|
uint64_t counter = gHpet[30]; // offset 0xF0: main counter
|
||||||
|
uint64_t seconds = counter / gHpetHz;
|
||||||
|
uint64_t rem = counter % gHpetHz;
|
||||||
|
return seconds * 1000000ull + (rem * 1000000ull) / gHpetHz;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback: combine the coarse periodic tick with the current LAPIC
|
||||||
|
// countdown. Read the coarse tick twice so an interrupt between gTicks and
|
||||||
|
// LAPIC_TIMER_CUR cannot make time jump backwards by almost one quantum.
|
||||||
|
uint64_t baseTicks1;
|
||||||
|
uint64_t baseTicks2;
|
||||||
|
uint32_t current;
|
||||||
|
do {
|
||||||
|
baseTicks1 = gTicks;
|
||||||
|
current = LAPIC_REG(LAPIC_TIMER_CUR);
|
||||||
|
baseTicks2 = gTicks;
|
||||||
|
} while (baseTicks1 != baseTicks2);
|
||||||
|
|
||||||
uint32_t initial = gApicTicksPerQuantum;
|
uint32_t initial = gApicTicksPerQuantum;
|
||||||
|
|
||||||
uint32_t elapsedApicTicks = 0;
|
uint32_t elapsedApicTicks = 0;
|
||||||
@@ -51,11 +69,11 @@ static uint64_t QueryTicks(void) {
|
|||||||
elapsedApicTicks = initial - current;
|
elapsedApicTicks = initial - current;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert base 10ms ticks to microseconds + the remainder
|
uint64_t subQuantumUs = 0;
|
||||||
// gTicks * 10,000 (ms to us) + (elapsed / ticks_per_us)
|
if (gApicTicksPerUs)
|
||||||
uint64_t totalUs = (baseTicks * QUANTUM_MS * 1000) + (elapsedApicTicks / gApicTicksPerUs);
|
subQuantumUs = elapsedApicTicks / gApicTicksPerUs;
|
||||||
|
|
||||||
return totalUs;
|
return (baseTicks1 * QUANTUM_MS * 1000ull) + subQuantumUs;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void Stall(uint32_t us) {
|
static void Stall(uint32_t us) {
|
||||||
@@ -181,6 +199,7 @@ KSTATUS HalTimerInitialize(uintptr_t lapicPhys, uintptr_t hpetPhys, HAL_TIMER *o
|
|||||||
if (hpetPhys) {
|
if (hpetPhys) {
|
||||||
gHpet = (volatile uint64_t*)gApi.PhysToVirt(hpetPhys);
|
gHpet = (volatile uint64_t*)gApi.PhysToVirt(hpetPhys);
|
||||||
uint64_t hpetHz = HpetInit(hpetPhys);
|
uint64_t hpetHz = HpetInit(hpetPhys);
|
||||||
|
gHpetHz = hpetHz;
|
||||||
gHpetTicksPerUs = hpetHz / 1000000;
|
gHpetTicksPerUs = hpetHz / 1000000;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,11 +5,12 @@
|
|||||||
#include <syscall.h>
|
#include <syscall.h>
|
||||||
|
|
||||||
static void print(const char* s) {
|
static void print(const char* s) {
|
||||||
if (s) SysWriteConsole(s, strlen(s));
|
if (s) { SYS_WRITE_CONSOLE_REQUEST req = { SysSvFromCstr(s) }; SysWriteConsole(&req); }
|
||||||
}
|
}
|
||||||
|
|
||||||
static void print_char(char c) {
|
static void print_char(char c) {
|
||||||
SysWriteConsole(&c, 1);
|
SYS_WRITE_CONSOLE_REQUEST req = { SysSvFromParts(&c, 1) };
|
||||||
|
SysWriteConsole(&req);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void print_u64(uint64_t v) {
|
static void print_u64(uint64_t v) {
|
||||||
@@ -115,8 +116,7 @@ static uint32_t resolve_host(const char* host, uint8_t ip[4]) {
|
|||||||
SYS_RESOLVE_HOST_REQUEST req;
|
SYS_RESOLVE_HOST_REQUEST req;
|
||||||
memset(&resolved, 0, sizeof(resolved));
|
memset(&resolved, 0, sizeof(resolved));
|
||||||
memset(&req, 0, sizeof(req));
|
memset(&req, 0, sizeof(req));
|
||||||
req.Name = host;
|
req.Name = SysSvFromCstr(host);
|
||||||
req.NameLength = strlen(host);
|
|
||||||
req.Family = SYS_AF_INET;
|
req.Family = SYS_AF_INET;
|
||||||
req.AddressOut = &resolved;
|
req.AddressOut = &resolved;
|
||||||
req.AddressSizeInOut = &resolvedSize;
|
req.AddressSizeInOut = &resolvedSize;
|
||||||
@@ -142,7 +142,7 @@ int main(void) {
|
|||||||
path[0] = '/';
|
path[0] = '/';
|
||||||
path[1] = 0;
|
path[1] = 0;
|
||||||
|
|
||||||
const char* cmd = SysGetPeb()->CommandLine;
|
const char* cmd = SysGetPeb()->CommandLine.Data;
|
||||||
char tok[256];
|
char tok[256];
|
||||||
cmd = next_token(cmd, tok, sizeof(tok)); // program path
|
cmd = next_token(cmd, tok, sizeof(tok)); // program path
|
||||||
if (cmd) {
|
if (cmd) {
|
||||||
|
|||||||
+17
-11
@@ -48,32 +48,38 @@ void itoa(int num, char* str) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void debug_print(const char* s) {
|
||||||
|
SYS_DEBUG_PRINT_REQUEST req = { SysSvFromCstr(s) };
|
||||||
|
(void)SysDebugPrint(&req);
|
||||||
|
}
|
||||||
|
|
||||||
void _start(void)
|
void _start(void)
|
||||||
{
|
{
|
||||||
const char* args = SysGetPeb()->CommandLine;
|
SYS_STRING_VIEW args = SysGetPeb()->CommandLine;
|
||||||
uint32_t h = 0;
|
uint32_t h = 0;
|
||||||
uint32_t s = 0;
|
uint32_t s = 0;
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
SysDebugPrint("[init] starting\n");
|
debug_print("[init] starting\n");
|
||||||
s = SysCreateProcess(args, &h, NULL);
|
SYS_CREATE_PROCESS_REQUEST cp = { args, SysSvFromParts(0, 0), &h };
|
||||||
|
s = SysCreateProcess(&cp);
|
||||||
if (s != STATUS_SUCCESS) {
|
if (s != STATUS_SUCCESS) {
|
||||||
SysDebugPrint("[init] FATAL: failed to start!\n");
|
debug_print("[init] FATAL: failed to start!\n");
|
||||||
char buffer[12];
|
char buffer[12];
|
||||||
itoa(s, buffer);
|
itoa(s, buffer);
|
||||||
SysDebugPrint(buffer);
|
debug_print(buffer);
|
||||||
SysDebugPrint("\n");
|
debug_print("\n");
|
||||||
SysExitProcess(1);
|
SysExitProcess(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
SysDebugPrint("[init] waiting\n");
|
debug_print("[init] waiting\n");
|
||||||
s = SysWaitForProcess(h);
|
s = SysWaitForProcess(h);
|
||||||
if (s != STATUS_SUCCESS)
|
if (s != STATUS_SUCCESS)
|
||||||
SysDebugPrint("[init] WARNING: SysWaitForProcess returned error\n");
|
debug_print("[init] WARNING: SysWaitForProcess returned error\n");
|
||||||
|
|
||||||
int exitCode = 0;
|
int exitCode = 0;
|
||||||
if (SysGetProcessExitCode(h, &exitCode) != STATUS_SUCCESS) {
|
if (SysGetProcessExitCode(h, &exitCode) != STATUS_SUCCESS) {
|
||||||
SysDebugPrint("[init] WARNING: failed to read exit code\n");
|
debug_print("[init] WARNING: failed to read exit code\n");
|
||||||
exitCode = 1;
|
exitCode = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -82,9 +88,9 @@ void _start(void)
|
|||||||
if (exitCode == 0)
|
if (exitCode == 0)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
SysDebugPrint("[init] child crashed, restarting\n");
|
debug_print("[init] child crashed, restarting\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
SysDebugPrint("[init] done\n");
|
debug_print("[init] done\n");
|
||||||
SysExitProcess(0);
|
SysExitProcess(0);
|
||||||
}
|
}
|
||||||
+176
-145
@@ -387,22 +387,26 @@ __declspec(dllexport) KSTATUS IoQueryAsyncFileRequest(IO_ASYNC_FILE_REQUEST_OBJE
|
|||||||
// Stored as (CurrentDrive, drive-relative path with no trailing backslash).
|
// Stored as (CurrentDrive, drive-relative path with no trailing backslash).
|
||||||
// Exported as native "N:/path" so personalities can round-trip volumes.
|
// Exported as native "N:/path" so personalities can round-trip volumes.
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
#define CWD_MAX 512
|
#define CWD_MAX PROCESS_CWD_MAX
|
||||||
|
|
||||||
static size_t KiFormatCwd(uint8_t drive, const char* rel, char* buf, size_t size) {
|
typedef struct KRESOLVED_PATH {
|
||||||
if (!buf || size == 0) return 0;
|
uint8_t Drive;
|
||||||
|
char Storage[CWD_MAX];
|
||||||
|
SYS_STRING_VIEW Path;
|
||||||
|
} KRESOLVED_PATH;
|
||||||
|
|
||||||
|
static size_t KiFormatCwd(uint8_t drive, SYS_STRING_VIEW rel, char* buf, size_t size) {
|
||||||
|
if (!buf || size == 0 || !SysSvIsValid(rel)) return 0;
|
||||||
if (drive > 7) drive = 0;
|
if (drive > 7) drive = 0;
|
||||||
if (!rel) rel = "";
|
|
||||||
|
|
||||||
size_t relLen = strlen(rel);
|
size_t needed = 3 + rel.Length + 1;
|
||||||
size_t needed = 3 + relLen + 1;
|
|
||||||
if (needed > size) return needed;
|
if (needed > size) return needed;
|
||||||
|
|
||||||
buf[0] = (char)('0' + drive);
|
buf[0] = (char)('0' + drive);
|
||||||
buf[1] = ':';
|
buf[1] = ':';
|
||||||
buf[2] = '/';
|
buf[2] = '/';
|
||||||
memcpy(buf + 3, rel, relLen);
|
if (rel.Length) memcpy(buf + 3, rel.Data, rel.Length);
|
||||||
buf[3 + relLen] = '\0';
|
buf[3 + rel.Length] = '\0';
|
||||||
return needed;
|
return needed;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -413,17 +417,57 @@ __declspec(dllexport) KSTATUS IoGetCwd(char* buf, size_t size) {
|
|||||||
if (!proc) return STATUS_ACCESS_DENIED;
|
if (!proc) return STATUS_ACCESS_DENIED;
|
||||||
|
|
||||||
uintptr_t flags = KeAcquireSpinlock(&proc->CwdLock);
|
uintptr_t flags = KeAcquireSpinlock(&proc->CwdLock);
|
||||||
size_t needed = KiFormatCwd(proc->CurrentDrive, proc->CurrentDirectory, buf, size);
|
size_t needed = KiFormatCwd(proc->CurrentDrive, SysSvFromParts(proc->CurrentDirectory, proc->CurrentDirectoryLength), buf, size);
|
||||||
KeReleaseSpinlock(&proc->CwdLock, flags);
|
KeReleaseSpinlock(&proc->CwdLock, flags);
|
||||||
|
|
||||||
return needed && needed <= size ? STATUS_SUCCESS : STATUS_BUFFER_TOO_SMALL;
|
return needed && needed <= size ? STATUS_SUCCESS : STATUS_BUFFER_TOO_SMALL;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resolve a possibly-relative path component against the CWD.
|
static int KiSvHasDriveRoot(SYS_STRING_VIEW path) {
|
||||||
// Writes the fully-resolved, drive-stripped relative path into 'resolved'
|
return path.Length >= 3 &&
|
||||||
// (caller supplies buf of at least CWD_MAX bytes).
|
path.Data[0] >= '0' && path.Data[0] <= '7' &&
|
||||||
// Returns the drive index.
|
path.Data[1] == ':' &&
|
||||||
static uint8_t KiResolvePath(const char* path, char* resolved, size_t resolvedSize) {
|
(path.Data[2] == '\\' || path.Data[2] == '/');
|
||||||
|
}
|
||||||
|
|
||||||
|
static int KiAppendBytes(char* work, size_t* workLen, size_t cap,
|
||||||
|
const char* data, size_t len) {
|
||||||
|
if (!work || !workLen || (!data && len)) return 0;
|
||||||
|
if (*workLen + len >= cap) return 0;
|
||||||
|
if (len) memcpy(work + *workLen, data, len);
|
||||||
|
*workLen += len;
|
||||||
|
work[*workLen] = '\0';
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int KiAppendComponent(char* work, size_t* workLen, size_t cap,
|
||||||
|
const char* data, size_t len) {
|
||||||
|
if (!work || !workLen || (!data && len)) return 0;
|
||||||
|
if (*workLen && work[*workLen - 1] != '\\' && work[*workLen - 1] != '/') {
|
||||||
|
if (!KiAppendBytes(work, workLen, cap, "\\", 1)) return 0;
|
||||||
|
}
|
||||||
|
return KiAppendBytes(work, workLen, cap, data, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void KiPopComponent(char* work, size_t* workLen) {
|
||||||
|
if (!work || !workLen || *workLen == 0) return;
|
||||||
|
size_t i = *workLen;
|
||||||
|
while (i > 0 && work[i - 1] != '\\' && work[i - 1] != '/') i--;
|
||||||
|
if (i > 0) i--;
|
||||||
|
*workLen = i;
|
||||||
|
work[i] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resolve a possibly-relative string view against the CWD.
|
||||||
|
// The returned view is backed by out->Storage and remains valid while 'out' lives.
|
||||||
|
static KSTATUS KiResolvePathV(SYS_STRING_VIEW input, KRESOLVED_PATH* out) {
|
||||||
|
if (!out) return STATUS_INVALID_PARAMETER;
|
||||||
|
memset(out, 0, sizeof(*out));
|
||||||
|
|
||||||
|
if (!SysSvIsValid(input)) return STATUS_INVALID_PARAMETER;
|
||||||
|
if (SysSvContainsByte(input, '\0')) return STATUS_INVALID_PARAMETER;
|
||||||
|
if (input.Length >= CWD_MAX) return STATUS_BUFFER_TOO_SMALL;
|
||||||
|
|
||||||
uint8_t drive = 0;
|
uint8_t drive = 0;
|
||||||
PROCESS* proc = KeGetCurrentProcess();
|
PROCESS* proc = KeGetCurrentProcess();
|
||||||
if (proc) {
|
if (proc) {
|
||||||
@@ -432,128 +476,98 @@ static uint8_t KiResolvePath(const char* path, char* resolved, size_t resolvedSi
|
|||||||
KeReleaseSpinlock(&proc->CwdLock, flags);
|
KeReleaseSpinlock(&proc->CwdLock, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Strip explicit drive prefix "N:\" or "N:/".
|
size_t pos = 0;
|
||||||
bool fromDriveRoot = false;
|
bool fromDriveRoot = false;
|
||||||
if (path[0] >= '0' && path[0] <= '7' &&
|
if (KiSvHasDriveRoot(input)) {
|
||||||
path[1] == ':' && (path[2] == '\\' || path[2] == '/')) {
|
drive = (uint8_t)(input.Data[0] - '0');
|
||||||
drive = (uint8_t)(path[0] - '0');
|
pos = 3;
|
||||||
path = path + 3;
|
|
||||||
fromDriveRoot = true;
|
fromDriveRoot = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
char work[CWD_MAX * 2];
|
char work[CWD_MAX * 2];
|
||||||
|
size_t workLen = 0;
|
||||||
work[0] = '\0';
|
work[0] = '\0';
|
||||||
|
|
||||||
// Initialize our working base
|
bool absolute = fromDriveRoot ||
|
||||||
if (path[0] == '\\' || path[0] == '/' || fromDriveRoot) {
|
(pos < input.Length && (input.Data[pos] == '\\' || input.Data[pos] == '/'));
|
||||||
// Absolute path: start from the root (empty work buffer)
|
if (!absolute && proc) {
|
||||||
// The segment loop below will automatically skip the leading separators.
|
uintptr_t flags = KeAcquireSpinlock(&proc->CwdLock);
|
||||||
} else {
|
size_t cwdLen = proc->CurrentDirectoryLength;
|
||||||
// Relative path: start from the current process's CWD
|
if (cwdLen >= sizeof(work)) cwdLen = sizeof(work) - 1;
|
||||||
PROCESS* proc = KeGetCurrentProcess();
|
memcpy(work, proc->CurrentDirectory, cwdLen);
|
||||||
if (proc) {
|
work[cwdLen] = '\0';
|
||||||
uintptr_t flags = KeAcquireSpinlock(&proc->CwdLock);
|
workLen = cwdLen;
|
||||||
size_t wlen = strlen(proc->CurrentDirectory);
|
KeReleaseSpinlock(&proc->CwdLock, flags);
|
||||||
if (wlen >= sizeof(work)) wlen = sizeof(work) - 1;
|
|
||||||
memcpy(work, proc->CurrentDirectory, wlen);
|
|
||||||
work[wlen] = '\0';
|
|
||||||
KeReleaseSpinlock(&proc->CwdLock, flags);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Segment-by-segment canonicalization
|
while (pos < input.Length) {
|
||||||
const char* p = path;
|
while (pos < input.Length && (input.Data[pos] == '\\' || input.Data[pos] == '/')) pos++;
|
||||||
while (*p) {
|
if (pos >= input.Length) break;
|
||||||
// Skip separators
|
|
||||||
while (*p == '\\' || *p == '/') p++;
|
|
||||||
if (!*p) break;
|
|
||||||
|
|
||||||
// Read next component
|
size_t compStart = pos;
|
||||||
char comp[256];
|
while (pos < input.Length && input.Data[pos] != '\\' && input.Data[pos] != '/') pos++;
|
||||||
int clen = 0;
|
size_t compLen = pos - compStart;
|
||||||
while (*p && *p != '\\' && *p != '/' && clen < 255)
|
if (compLen == 0) continue;
|
||||||
comp[clen++] = *p++;
|
|
||||||
comp[clen] = '\0';
|
|
||||||
|
|
||||||
if (clen == 0) continue;
|
if (compLen == 1 && input.Data[compStart] == '.') {
|
||||||
|
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';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
if (compLen == 2 && input.Data[compStart] == '.' && input.Data[compStart + 1] == '.') {
|
||||||
|
KiPopComponent(work, &workLen);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (compLen >= 256) return STATUS_BUFFER_TOO_SMALL;
|
||||||
|
if (!KiAppendComponent(work, &workLen, sizeof(work), input.Data + compStart, compLen))
|
||||||
|
return STATUS_BUFFER_TOO_SMALL;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Output the final canonicalized, drive-stripped path
|
if (workLen + 1 > sizeof(out->Storage)) return STATUS_BUFFER_TOO_SMALL;
|
||||||
size_t finalLen = strlen(work);
|
memcpy(out->Storage, work, workLen);
|
||||||
if (finalLen + 1 > resolvedSize) finalLen = resolvedSize - 1;
|
out->Storage[workLen] = '\0';
|
||||||
memcpy(resolved, work, finalLen);
|
out->Drive = drive;
|
||||||
resolved[finalLen] = '\0';
|
out->Path = SysSvFromParts(out->Storage, workLen);
|
||||||
|
return STATUS_SUCCESS;
|
||||||
return drive;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
__declspec(dllexport) KSTATUS IoSetCwd(const char* path) {
|
__declspec(dllexport) KSTATUS IoSetCwd(SYS_STRING_VIEW path) {
|
||||||
if (!path) return STATUS_INVALID_PARAMETER;
|
if (path.Length && !path.Data) return STATUS_INVALID_PARAMETER;
|
||||||
if (!KeCanWait()) return STATUS_INVALID_IRQL;
|
if (!KeCanWait()) return STATUS_INVALID_IRQL;
|
||||||
|
|
||||||
PROCESS* proc = KeGetCurrentProcess();
|
PROCESS* proc = KeGetCurrentProcess();
|
||||||
if (!proc) return STATUS_ACCESS_DENIED;
|
if (!proc) return STATUS_ACCESS_DENIED;
|
||||||
|
|
||||||
// Resolve the new path (this also validates ".." navigation).
|
KRESOLVED_PATH resolved;
|
||||||
// Note: KiResolvePath already uses proc->CurrentDirectory internally!
|
KSTATUS s = KiResolvePathV(path, &resolved);
|
||||||
char resolved[CWD_MAX];
|
if (!KSUCCESS(s)) return s;
|
||||||
uint8_t drive = KiResolvePath(path, resolved, sizeof(resolved));
|
|
||||||
|
|
||||||
// Validate: try to open the directory to confirm it actually exists on disk
|
IO_FILESYSTEM* fs = gVolumes[resolved.Drive].Fs;
|
||||||
IO_FILESYSTEM* fs = gVolumes[drive].Fs;
|
|
||||||
if (!fs) return STATUS_NOT_FOUND;
|
if (!fs) return STATUS_NOT_FOUND;
|
||||||
if (!fs->OpenDir) return STATUS_NOT_SUPPORTED;
|
if (!fs->OpenDir) return STATUS_NOT_SUPPORTED;
|
||||||
|
|
||||||
void* dirCtx = NULL;
|
void* dirCtx = NULL;
|
||||||
KSTATUS s = fs->OpenDir(fs, resolved, &dirCtx);
|
s = fs->OpenDir(fs, resolved.Path, &dirCtx);
|
||||||
if (!KSUCCESS(s)) return s;
|
if (!KSUCCESS(s)) return s;
|
||||||
fs->CloseDir(fs, dirCtx);
|
fs->CloseDir(fs, dirCtx);
|
||||||
|
|
||||||
// Commit the new CWD to the process block
|
if (resolved.Path.Length + 1 > sizeof(proc->CurrentDirectory)) return STATUS_BUFFER_TOO_SMALL;
|
||||||
size_t len = strlen(resolved);
|
|
||||||
if (len + 1 > sizeof(proc->CurrentDirectory)) return STATUS_BUFFER_TOO_SMALL;
|
|
||||||
|
|
||||||
uintptr_t flags = KeAcquireSpinlock(&proc->CwdLock);
|
uintptr_t flags = KeAcquireSpinlock(&proc->CwdLock);
|
||||||
proc->CurrentDrive = drive;
|
proc->CurrentDrive = resolved.Drive;
|
||||||
memcpy(proc->CurrentDirectory, resolved, len + 1);
|
proc->CurrentDirectoryLength = resolved.Path.Length;
|
||||||
|
memcpy(proc->CurrentDirectory, resolved.Path.Data, resolved.Path.Length);
|
||||||
|
proc->CurrentDirectory[resolved.Path.Length] = '\0';
|
||||||
|
|
||||||
// Keep the user-visible PEB mirror in sync. The PEB's CWD buffer is a
|
|
||||||
// fixed SYS_CWD_MAX allocation made at process creation, so the pointer
|
|
||||||
// remains stable for personalities that cache SYS_PEB*.
|
|
||||||
if (proc->UserCwdBase && proc->UserCwdSize) {
|
if (proc->UserCwdBase && proc->UserCwdSize) {
|
||||||
size_t formatted = KiFormatCwd(drive, resolved, (char*)proc->UserCwdBase, proc->UserCwdSize);
|
size_t formatted = KiFormatCwd(resolved.Drive, resolved.Path, (char*)proc->UserCwdBase, proc->UserCwdSize);
|
||||||
size_t copyLen = (formatted && formatted <= proc->UserCwdSize) ? formatted - 1 : proc->UserCwdSize - 1;
|
size_t copyLen = (formatted && formatted <= proc->UserCwdSize) ? formatted - 1 : proc->UserCwdSize - 1;
|
||||||
|
|
||||||
SYS_PEB* peb = (SYS_PEB*)proc->UserPebBase;
|
SYS_PEB* peb = (SYS_PEB*)proc->UserPebBase;
|
||||||
if (peb && peb->Magic == SYS_PEB_MAGIC) {
|
if (peb && peb->Magic == SYS_PEB_MAGIC) {
|
||||||
peb->CurrentDirectory = (const char*)proc->UserCwdBase;
|
peb->CurrentDirectory = SysSvFromParts((const char*)proc->UserCwdBase, copyLen);
|
||||||
peb->CurrentDirectoryLength = copyLen;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
KeReleaseSpinlock(&proc->CwdLock, flags);
|
KeReleaseSpinlock(&proc->CwdLock, flags);
|
||||||
|
|
||||||
return STATUS_SUCCESS;
|
return STATUS_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -583,18 +597,20 @@ static void KiFileDestroy(OBJECT* obj) {
|
|||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// IoOpenFile
|
// IoOpenFile
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
__declspec(dllexport) KSTATUS IoOpenFile(const char* path, FILE_OBJECT **out) {
|
__declspec(dllexport) KSTATUS IoOpenFile(SYS_STRING_VIEW path, FILE_OBJECT **out) {
|
||||||
if (!path || !out) return STATUS_INVALID_PARAMETER;
|
if ((!path.Data && path.Length) || !out) return STATUS_INVALID_PARAMETER;
|
||||||
if (!KeCanWait()) return STATUS_INVALID_IRQL;
|
if (!KeCanWait()) return STATUS_INVALID_IRQL;
|
||||||
|
if (path.Length == 0) return STATUS_INVALID_PARAMETER;
|
||||||
|
|
||||||
char relPath[CWD_MAX];
|
KRESOLVED_PATH resolved;
|
||||||
uint8_t drive = KiResolvePath(path, relPath, sizeof(relPath));
|
KSTATUS s = KiResolvePathV(path, &resolved);
|
||||||
|
if (!KSUCCESS(s)) return s;
|
||||||
|
|
||||||
IO_FILESYSTEM* fs = gVolumes[drive].Fs;
|
IO_FILESYSTEM* fs = gVolumes[resolved.Drive].Fs;
|
||||||
if (!fs) return STATUS_NOT_FOUND;
|
if (!fs) return STATUS_NOT_FOUND;
|
||||||
|
|
||||||
void* fileCtx;
|
void* fileCtx;
|
||||||
KSTATUS s = fs->Open(fs, relPath, &fileCtx);
|
s = fs->Open(fs, resolved.Path, &fileCtx);
|
||||||
if (!KSUCCESS(s)) return s;
|
if (!KSUCCESS(s)) return s;
|
||||||
|
|
||||||
FILE_OBJECT* f = (FILE_OBJECT *)ObAllocateObject(ObjectTypeFile /* reuse slot */,
|
FILE_OBJECT* f = (FILE_OBJECT *)ObAllocateObject(ObjectTypeFile /* reuse slot */,
|
||||||
@@ -603,6 +619,7 @@ __declspec(dllexport) KSTATUS IoOpenFile(const char* path, FILE_OBJECT **out) {
|
|||||||
f->Header.Destroy = KiFileDestroy;
|
f->Header.Destroy = KiFileDestroy;
|
||||||
f->Fs = fs;
|
f->Fs = fs;
|
||||||
f->FileCtx = fileCtx;
|
f->FileCtx = fileCtx;
|
||||||
|
f->Lock = (SPINLOCK)SPINLOCK_INIT;
|
||||||
f->Size = fs->GetSize(fs, fileCtx);
|
f->Size = fs->GetSize(fs, fileCtx);
|
||||||
f->Offset = 0;
|
f->Offset = 0;
|
||||||
|
|
||||||
@@ -701,19 +718,20 @@ __declspec(dllexport) KSTATUS IoWriteFile(FILE_OBJECT* file, const void* buf,
|
|||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// IoCreateFile - create a new file and return it open for writing
|
// IoCreateFile - create a new file and return it open for writing
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
__declspec(dllexport) KSTATUS IoCreateFile(const char* path, FILE_OBJECT** out) {
|
__declspec(dllexport) KSTATUS IoCreateFile(SYS_STRING_VIEW path, FILE_OBJECT** out) {
|
||||||
if (!path || !out) return STATUS_INVALID_PARAMETER;
|
if ((!path.Data && path.Length) || !out || path.Length == 0) return STATUS_INVALID_PARAMETER;
|
||||||
if (!KeCanWait()) return STATUS_INVALID_IRQL;
|
if (!KeCanWait()) return STATUS_INVALID_IRQL;
|
||||||
|
|
||||||
char relPath[CWD_MAX];
|
KRESOLVED_PATH resolved;
|
||||||
uint8_t drive = KiResolvePath(path, relPath, sizeof(relPath));
|
KSTATUS s = KiResolvePathV(path, &resolved);
|
||||||
|
if (!KSUCCESS(s)) return s;
|
||||||
|
|
||||||
IO_FILESYSTEM* fs = gVolumes[drive].Fs;
|
IO_FILESYSTEM* fs = gVolumes[resolved.Drive].Fs;
|
||||||
if (!fs) return STATUS_NOT_FOUND;
|
if (!fs) return STATUS_NOT_FOUND;
|
||||||
if (!fs->Create) return STATUS_NOT_SUPPORTED;
|
if (!fs->Create) return STATUS_NOT_SUPPORTED;
|
||||||
|
|
||||||
void* fileCtx;
|
void* fileCtx;
|
||||||
KSTATUS s = fs->Create(fs, relPath, &fileCtx);
|
s = fs->Create(fs, resolved.Path, &fileCtx);
|
||||||
if (!KSUCCESS(s)) return s;
|
if (!KSUCCESS(s)) return s;
|
||||||
|
|
||||||
FILE_OBJECT* f = (FILE_OBJECT*)ObAllocateObject(ObjectTypeFile,
|
FILE_OBJECT* f = (FILE_OBJECT*)ObAllocateObject(ObjectTypeFile,
|
||||||
@@ -733,63 +751,69 @@ __declspec(dllexport) KSTATUS IoCreateFile(const char* path, FILE_OBJECT** out)
|
|||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// IoDeleteFile - delete a file by path (does not require an open handle)
|
// IoDeleteFile - delete a file by path (does not require an open handle)
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
__declspec(dllexport) KSTATUS IoDeleteFile(const char* path) {
|
__declspec(dllexport) KSTATUS IoDeleteFile(SYS_STRING_VIEW path) {
|
||||||
if (!path) return STATUS_INVALID_PARAMETER;
|
if ((!path.Data && path.Length) || path.Length == 0) return STATUS_INVALID_PARAMETER;
|
||||||
if (!KeCanWait()) return STATUS_INVALID_IRQL;
|
if (!KeCanWait()) return STATUS_INVALID_IRQL;
|
||||||
|
|
||||||
char relPath[CWD_MAX];
|
KRESOLVED_PATH resolved;
|
||||||
uint8_t drive = KiResolvePath(path, relPath, sizeof(relPath));
|
KSTATUS s = KiResolvePathV(path, &resolved);
|
||||||
|
if (!KSUCCESS(s)) return s;
|
||||||
|
|
||||||
IO_FILESYSTEM* fs = gVolumes[drive].Fs;
|
IO_FILESYSTEM* fs = gVolumes[resolved.Drive].Fs;
|
||||||
if (!fs) return STATUS_NOT_FOUND;
|
if (!fs) return STATUS_NOT_FOUND;
|
||||||
if (!fs->Delete) return STATUS_NOT_SUPPORTED;
|
if (!fs->Delete) return STATUS_NOT_SUPPORTED;
|
||||||
|
|
||||||
return fs->Delete(fs, relPath);
|
return fs->Delete(fs, resolved.Path);
|
||||||
}
|
}
|
||||||
|
|
||||||
__declspec(dllexport) KSTATUS IoRenameFile(const char* oldPath, const char* newPath) {
|
__declspec(dllexport) KSTATUS IoRenameFile(SYS_STRING_VIEW oldPath, SYS_STRING_VIEW newPath) {
|
||||||
if (!oldPath || !newPath) return STATUS_INVALID_PARAMETER;
|
if ((!oldPath.Data && oldPath.Length) || (!newPath.Data && newPath.Length) ||
|
||||||
|
oldPath.Length == 0 || newPath.Length == 0) return STATUS_INVALID_PARAMETER;
|
||||||
if (!KeCanWait()) return STATUS_INVALID_IRQL;
|
if (!KeCanWait()) return STATUS_INVALID_IRQL;
|
||||||
|
|
||||||
char oldRel[CWD_MAX];
|
KRESOLVED_PATH oldResolved;
|
||||||
char newRel[CWD_MAX];
|
KRESOLVED_PATH newResolved;
|
||||||
uint8_t oldDrive = KiResolvePath(oldPath, oldRel, sizeof(oldRel));
|
KSTATUS s = KiResolvePathV(oldPath, &oldResolved);
|
||||||
uint8_t newDrive = KiResolvePath(newPath, newRel, sizeof(newRel));
|
if (!KSUCCESS(s)) return s;
|
||||||
if (oldDrive != newDrive) return STATUS_NOT_SUPPORTED;
|
s = KiResolvePathV(newPath, &newResolved);
|
||||||
|
if (!KSUCCESS(s)) return s;
|
||||||
|
if (oldResolved.Drive != newResolved.Drive) return STATUS_NOT_SUPPORTED;
|
||||||
|
|
||||||
IO_FILESYSTEM* fs = gVolumes[oldDrive].Fs;
|
IO_FILESYSTEM* fs = gVolumes[oldResolved.Drive].Fs;
|
||||||
if (!fs) return STATUS_NOT_FOUND;
|
if (!fs) return STATUS_NOT_FOUND;
|
||||||
if (!fs->Rename) return STATUS_NOT_SUPPORTED;
|
if (!fs->Rename) return STATUS_NOT_SUPPORTED;
|
||||||
|
|
||||||
return fs->Rename(fs, oldRel, newRel);
|
return fs->Rename(fs, oldResolved.Path, newResolved.Path);
|
||||||
}
|
}
|
||||||
|
|
||||||
__declspec(dllexport) KSTATUS IoCreateDir(const char* path) {
|
__declspec(dllexport) KSTATUS IoCreateDir(SYS_STRING_VIEW path) {
|
||||||
if (!path) return STATUS_INVALID_PARAMETER;
|
if ((!path.Data && path.Length) || path.Length == 0) return STATUS_INVALID_PARAMETER;
|
||||||
if (!KeCanWait()) return STATUS_INVALID_IRQL;
|
if (!KeCanWait()) return STATUS_INVALID_IRQL;
|
||||||
|
|
||||||
char relPath[CWD_MAX];
|
KRESOLVED_PATH resolved;
|
||||||
uint8_t drive = KiResolvePath(path, relPath, sizeof(relPath));
|
KSTATUS s = KiResolvePathV(path, &resolved);
|
||||||
|
if (!KSUCCESS(s)) return s;
|
||||||
|
|
||||||
IO_FILESYSTEM* fs = gVolumes[drive].Fs;
|
IO_FILESYSTEM* fs = gVolumes[resolved.Drive].Fs;
|
||||||
if (!fs) return STATUS_NOT_FOUND;
|
if (!fs) return STATUS_NOT_FOUND;
|
||||||
if (!fs->CreateDir) return STATUS_NOT_SUPPORTED;
|
if (!fs->CreateDir) return STATUS_NOT_SUPPORTED;
|
||||||
|
|
||||||
return fs->CreateDir(fs, relPath);
|
return fs->CreateDir(fs, resolved.Path);
|
||||||
}
|
}
|
||||||
|
|
||||||
__declspec(dllexport) KSTATUS IoRemoveDir(const char* path) {
|
__declspec(dllexport) KSTATUS IoRemoveDir(SYS_STRING_VIEW path) {
|
||||||
if (!path) return STATUS_INVALID_PARAMETER;
|
if ((!path.Data && path.Length) || path.Length == 0) return STATUS_INVALID_PARAMETER;
|
||||||
if (!KeCanWait()) return STATUS_INVALID_IRQL;
|
if (!KeCanWait()) return STATUS_INVALID_IRQL;
|
||||||
|
|
||||||
char relPath[CWD_MAX];
|
KRESOLVED_PATH resolved;
|
||||||
uint8_t drive = KiResolvePath(path, relPath, sizeof(relPath));
|
KSTATUS s = KiResolvePathV(path, &resolved);
|
||||||
|
if (!KSUCCESS(s)) return s;
|
||||||
|
|
||||||
IO_FILESYSTEM* fs = gVolumes[drive].Fs;
|
IO_FILESYSTEM* fs = gVolumes[resolved.Drive].Fs;
|
||||||
if (!fs) return STATUS_NOT_FOUND;
|
if (!fs) return STATUS_NOT_FOUND;
|
||||||
if (!fs->RemoveDir) return STATUS_NOT_SUPPORTED;
|
if (!fs->RemoveDir) return STATUS_NOT_SUPPORTED;
|
||||||
|
|
||||||
return fs->RemoveDir(fs, relPath);
|
return fs->RemoveDir(fs, resolved.Path);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
@@ -840,37 +864,44 @@ static void KiDirDestroy(OBJECT* obj) {
|
|||||||
if (d->Fs && d->DirCtx) d->Fs->CloseDir(d->Fs, d->DirCtx);
|
if (d->Fs && d->DirCtx) d->Fs->CloseDir(d->Fs, d->DirCtx);
|
||||||
}
|
}
|
||||||
|
|
||||||
__declspec(dllexport) KSTATUS IoOpenDir(const char* dirPath, DIR_OBJECT** out) {
|
__declspec(dllexport) KSTATUS IoOpenDir(SYS_STRING_VIEW dirPath, DIR_OBJECT** out) {
|
||||||
if (!out) return STATUS_INVALID_PARAMETER;
|
if (!out) return STATUS_INVALID_PARAMETER;
|
||||||
|
if (dirPath.Length && !dirPath.Data) return STATUS_INVALID_PARAMETER;
|
||||||
if (!KeCanWait()) return STATUS_INVALID_IRQL;
|
if (!KeCanWait()) return STATUS_INVALID_IRQL;
|
||||||
|
|
||||||
// NULL or empty path means the CWD itself
|
KRESOLVED_PATH resolved;
|
||||||
char relPath[CWD_MAX];
|
KSTATUS s;
|
||||||
uint8_t drive;
|
|
||||||
|
|
||||||
if (!dirPath || dirPath[0] == '\0') {
|
if (dirPath.Length == 0) {
|
||||||
PROCESS* proc = KeGetCurrentProcess();
|
PROCESS* proc = KeGetCurrentProcess();
|
||||||
if (!proc) return STATUS_ACCESS_DENIED;
|
if (!proc) return STATUS_ACCESS_DENIED;
|
||||||
|
|
||||||
uintptr_t flags = KeAcquireSpinlock(&proc->CwdLock);
|
uintptr_t flags = KeAcquireSpinlock(&proc->CwdLock);
|
||||||
memcpy(relPath, proc->CurrentDirectory, strlen(proc->CurrentDirectory) + 1);
|
size_t cwdLen = proc->CurrentDirectoryLength;
|
||||||
drive = proc->CurrentDrive;
|
if (cwdLen + 1 > sizeof(resolved.Storage)) {
|
||||||
|
KeReleaseSpinlock(&proc->CwdLock, flags);
|
||||||
|
return STATUS_BUFFER_TOO_SMALL;
|
||||||
|
}
|
||||||
|
memcpy(resolved.Storage, proc->CurrentDirectory, cwdLen + 1);
|
||||||
|
resolved.Drive = proc->CurrentDrive;
|
||||||
|
resolved.Path = SysSvFromParts(resolved.Storage, cwdLen);
|
||||||
KeReleaseSpinlock(&proc->CwdLock, flags);
|
KeReleaseSpinlock(&proc->CwdLock, flags);
|
||||||
} else {
|
} else {
|
||||||
drive = KiResolvePath(dirPath, relPath, sizeof(relPath));
|
s = KiResolvePathV(dirPath, &resolved);
|
||||||
|
if (!KSUCCESS(s)) return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
IO_FILESYSTEM* fs = gVolumes[drive].Fs;
|
IO_FILESYSTEM* fs = gVolumes[resolved.Drive].Fs;
|
||||||
if (!fs) return STATUS_NOT_FOUND;
|
if (!fs) return STATUS_NOT_FOUND;
|
||||||
if (!fs->OpenDir) return STATUS_NOT_SUPPORTED;
|
if (!fs->OpenDir) return STATUS_NOT_SUPPORTED;
|
||||||
|
|
||||||
void* dirCtx = NULL;
|
void* dirCtx = NULL;
|
||||||
KSTATUS s = fs->OpenDir(fs, relPath, &dirCtx);
|
s = fs->OpenDir(fs, resolved.Path, &dirCtx);
|
||||||
if (!KSUCCESS(s)) return s;
|
if (!KSUCCESS(s)) return s;
|
||||||
|
|
||||||
DIR_OBJECT* d = (DIR_OBJECT*)ObAllocateObject(ObjectTypeDir, sizeof(DIR_OBJECT));
|
DIR_OBJECT* d = (DIR_OBJECT*)ObAllocateObject(ObjectTypeDir, sizeof(DIR_OBJECT));
|
||||||
if (!d) { fs->CloseDir(fs, dirCtx); return STATUS_NO_MEMORY; }
|
if (!d) { fs->CloseDir(fs, dirCtx); return STATUS_NO_MEMORY; }
|
||||||
|
|
||||||
d->Header.Destroy = KiDirDestroy;
|
d->Header.Destroy = KiDirDestroy;
|
||||||
d->Fs = fs;
|
d->Fs = fs;
|
||||||
d->DirCtx = dirCtx;
|
d->DirCtx = dirCtx;
|
||||||
|
|||||||
+16
-15
@@ -7,6 +7,7 @@
|
|||||||
#include <ke_status.h>
|
#include <ke_status.h>
|
||||||
#include <ob_object.h>
|
#include <ob_object.h>
|
||||||
#include <ke_spinlock.h>
|
#include <ke_spinlock.h>
|
||||||
|
#include <sys_string.h>
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// KDIR_ENTRY - one directory entry returned by ReadDir / SYS_READ_DIR
|
// KDIR_ENTRY - one directory entry returned by ReadDir / SYS_READ_DIR
|
||||||
@@ -35,7 +36,7 @@ typedef struct IO_FILESYSTEM {
|
|||||||
// Open a file by absolute path (e.g. "apps\\shell.exe").
|
// Open a file by absolute path (e.g. "apps\\shell.exe").
|
||||||
// Returns an opaque per-file driver cookie in* fileCtx.
|
// Returns an opaque per-file driver cookie in* fileCtx.
|
||||||
KSTATUS (*Open)(struct IO_FILESYSTEM* fs,
|
KSTATUS (*Open)(struct IO_FILESYSTEM* fs,
|
||||||
const char* path, void **fileCtx);
|
SYS_STRING_VIEW path, void **fileCtx);
|
||||||
|
|
||||||
// Read up to 'size' bytes at 'offset' into 'buf'.
|
// Read up to 'size' bytes at 'offset' into 'buf'.
|
||||||
// Sets* bytesRead to actual bytes copied.
|
// Sets* bytesRead to actual bytes copied.
|
||||||
@@ -58,19 +59,19 @@ typedef struct IO_FILESYSTEM {
|
|||||||
void* completeContext);
|
void* completeContext);
|
||||||
|
|
||||||
// Create a new file at 'path'; returns an open fileCtx on success.
|
// Create a new file at 'path'; returns an open fileCtx on success.
|
||||||
KSTATUS (*Create)(struct IO_FILESYSTEM* fs, const char* path,
|
KSTATUS (*Create)(struct IO_FILESYSTEM* fs, SYS_STRING_VIEW path,
|
||||||
void** fileCtx);
|
void** fileCtx);
|
||||||
|
|
||||||
// Delete the file at 'path'.
|
// Delete the file at 'path'.
|
||||||
KSTATUS (*Delete)(struct IO_FILESYSTEM* fs, const char* path);
|
KSTATUS (*Delete)(struct IO_FILESYSTEM* fs, SYS_STRING_VIEW path);
|
||||||
|
|
||||||
// Rename or move a file/directory within the same filesystem.
|
// Rename or move a file/directory within the same filesystem.
|
||||||
KSTATUS (*Rename)(struct IO_FILESYSTEM* fs,
|
KSTATUS (*Rename)(struct IO_FILESYSTEM* fs,
|
||||||
const char* oldPath, const char* newPath);
|
SYS_STRING_VIEW oldPath, SYS_STRING_VIEW newPath);
|
||||||
|
|
||||||
// Create or remove directories.
|
// Create or remove directories.
|
||||||
KSTATUS (*CreateDir)(struct IO_FILESYSTEM* fs, const char* path);
|
KSTATUS (*CreateDir)(struct IO_FILESYSTEM* fs, SYS_STRING_VIEW path);
|
||||||
KSTATUS (*RemoveDir)(struct IO_FILESYSTEM* fs, const char* path);
|
KSTATUS (*RemoveDir)(struct IO_FILESYSTEM* fs, SYS_STRING_VIEW path);
|
||||||
|
|
||||||
// Truncate or extend the file to exactly 'newSize' bytes.
|
// Truncate or extend the file to exactly 'newSize' bytes.
|
||||||
KSTATUS (*Truncate)(struct IO_FILESYSTEM* fs, void* fileCtx,
|
KSTATUS (*Truncate)(struct IO_FILESYSTEM* fs, void* fileCtx,
|
||||||
@@ -92,7 +93,7 @@ typedef struct IO_FILESYSTEM {
|
|||||||
// Open a directory iterator for 'dirPath' (relative or drive-prefixed).
|
// Open a directory iterator for 'dirPath' (relative or drive-prefixed).
|
||||||
// Returns an opaque per-iteration driver cookie in *dirCtx.
|
// Returns an opaque per-iteration driver cookie in *dirCtx.
|
||||||
KSTATUS (*OpenDir)(struct IO_FILESYSTEM* fs,
|
KSTATUS (*OpenDir)(struct IO_FILESYSTEM* fs,
|
||||||
const char* dirPath, void** dirCtx);
|
SYS_STRING_VIEW dirPath, void** dirCtx);
|
||||||
|
|
||||||
// Advance to the next entry.
|
// Advance to the next entry.
|
||||||
// Fills 'out' with name, attributes, and size.
|
// Fills 'out' with name, attributes, and size.
|
||||||
@@ -153,7 +154,7 @@ __declspec(dllexport) KSTATUS IoRegisterFilesystem(uint8_t driveIndex, IO_FILESY
|
|||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Kernel-side API
|
// Kernel-side API
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
__declspec(dllexport) KSTATUS IoOpenFile(const char* path, FILE_OBJECT **out);
|
__declspec(dllexport) KSTATUS IoOpenFile(SYS_STRING_VIEW path, FILE_OBJECT **out);
|
||||||
__declspec(dllexport) KSTATUS IoReadFile(FILE_OBJECT* file, void* buf,
|
__declspec(dllexport) KSTATUS IoReadFile(FILE_OBJECT* file, void* buf,
|
||||||
size_t size, size_t* bytesRead);
|
size_t size, size_t* bytesRead);
|
||||||
__declspec(dllexport) KSTATUS IoReadFileAt(FILE_OBJECT* file, uint64_t offset,
|
__declspec(dllexport) KSTATUS IoReadFileAt(FILE_OBJECT* file, uint64_t offset,
|
||||||
@@ -175,11 +176,11 @@ __declspec(dllexport) OBJECT* IoAsyncFileRequestObject(IO_ASYNC_FILE_REQUEST_OB
|
|||||||
__declspec(dllexport) KSTATUS IoCancelAsyncFileRequest(IO_ASYNC_FILE_REQUEST_OBJECT* request);
|
__declspec(dllexport) KSTATUS IoCancelAsyncFileRequest(IO_ASYNC_FILE_REQUEST_OBJECT* request);
|
||||||
__declspec(dllexport) KSTATUS IoQueryAsyncFileRequest(IO_ASYNC_FILE_REQUEST_OBJECT* request,
|
__declspec(dllexport) KSTATUS IoQueryAsyncFileRequest(IO_ASYNC_FILE_REQUEST_OBJECT* request,
|
||||||
IO_STATUS_BLOCK* iosbOut);
|
IO_STATUS_BLOCK* iosbOut);
|
||||||
__declspec(dllexport) KSTATUS IoCreateFile (const char* path, FILE_OBJECT** out);
|
__declspec(dllexport) KSTATUS IoCreateFile (SYS_STRING_VIEW path, FILE_OBJECT** out);
|
||||||
__declspec(dllexport) KSTATUS IoDeleteFile (const char* path);
|
__declspec(dllexport) KSTATUS IoDeleteFile (SYS_STRING_VIEW path);
|
||||||
__declspec(dllexport) KSTATUS IoRenameFile (const char* oldPath, const char* newPath);
|
__declspec(dllexport) KSTATUS IoRenameFile (SYS_STRING_VIEW oldPath, SYS_STRING_VIEW newPath);
|
||||||
__declspec(dllexport) KSTATUS IoCreateDir (const char* path);
|
__declspec(dllexport) KSTATUS IoCreateDir (SYS_STRING_VIEW path);
|
||||||
__declspec(dllexport) KSTATUS IoRemoveDir (const char* path);
|
__declspec(dllexport) KSTATUS IoRemoveDir (SYS_STRING_VIEW path);
|
||||||
__declspec(dllexport) KSTATUS IoTruncateFile(FILE_OBJECT* file, uint64_t newSize);
|
__declspec(dllexport) KSTATUS IoTruncateFile(FILE_OBJECT* file, uint64_t newSize);
|
||||||
__declspec(dllexport) KSTATUS IoSeekFile(FILE_OBJECT* file, uint64_t offset);
|
__declspec(dllexport) KSTATUS IoSeekFile(FILE_OBJECT* file, uint64_t offset);
|
||||||
__declspec(dllexport) uint64_t IoGetFileSize(FILE_OBJECT* file);
|
__declspec(dllexport) uint64_t IoGetFileSize(FILE_OBJECT* file);
|
||||||
@@ -188,12 +189,12 @@ __declspec(dllexport) void IoCloseFile(FILE_OBJECT* file);
|
|||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Directory enumeration API
|
// Directory enumeration API
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
__declspec(dllexport) KSTATUS IoOpenDir (const char* dirPath, DIR_OBJECT** out);
|
__declspec(dllexport) KSTATUS IoOpenDir (SYS_STRING_VIEW dirPath, DIR_OBJECT** out);
|
||||||
__declspec(dllexport) KSTATUS IoReadDir (DIR_OBJECT* dir, KDIR_ENTRY* entryOut);
|
__declspec(dllexport) KSTATUS IoReadDir (DIR_OBJECT* dir, KDIR_ENTRY* entryOut);
|
||||||
__declspec(dllexport) void IoCloseDir(DIR_OBJECT* dir);
|
__declspec(dllexport) void IoCloseDir(DIR_OBJECT* dir);
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Current Working Directory
|
// Current Working Directory
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
__declspec(dllexport) KSTATUS IoSetCwd(const char* path);
|
__declspec(dllexport) KSTATUS IoSetCwd(SYS_STRING_VIEW path);
|
||||||
__declspec(dllexport) KSTATUS IoGetCwd(char* buf, size_t size);
|
__declspec(dllexport) KSTATUS IoGetCwd(char* buf, size_t size);
|
||||||
+161
-56
@@ -88,15 +88,17 @@ static void KiProcessDestroy(OBJECT* obj) {
|
|||||||
|
|
||||||
// Release all remaining handles.
|
// Release all remaining handles.
|
||||||
for (int i = 1; i < HANDLE_TABLE_SIZE; i++) {
|
for (int i = 1; i < HANDLE_TABLE_SIZE; i++) {
|
||||||
if (proc->Handles[i]) {
|
if (proc->Handles[i].Object) {
|
||||||
ObDereferenceObject(proc->Handles[i]);
|
ObDereferenceObject(proc->Handles[i].Object);
|
||||||
proc->Handles[i] = (OBJECT *)0;
|
proc->Handles[i].Object = (OBJECT*)0;
|
||||||
|
proc->Handles[i].Rights = 0;
|
||||||
|
proc->Handles[i].Flags = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Release the main executable image.
|
// Release the main executable image.
|
||||||
if (proc->ImageBase && proc->ImageSize) {
|
if (proc->ImageBase && proc->ImageSize) {
|
||||||
MmFreeUserMemory((void *)proc->ImageBase);
|
MmFreeUserImage((void *)proc->ImageBase, proc->ImageSize);
|
||||||
proc->ImageBase = 0;
|
proc->ImageBase = 0;
|
||||||
proc->ImageSize = 0;
|
proc->ImageSize = 0;
|
||||||
}
|
}
|
||||||
@@ -104,7 +106,7 @@ static void KiProcessDestroy(OBJECT* obj) {
|
|||||||
// Release per-process modules.
|
// Release per-process modules.
|
||||||
for (uint32_t i = 0; i < proc->ModuleCount; i++) {
|
for (uint32_t i = 0; i < proc->ModuleCount; i++) {
|
||||||
if (proc->Modules[i].Base && proc->Modules[i].Size) {
|
if (proc->Modules[i].Base && proc->Modules[i].Size) {
|
||||||
MmFreeUserMemory((void *)proc->Modules[i].Base);
|
MmFreeUserImage((void *)proc->Modules[i].Base, proc->Modules[i].Size);
|
||||||
}
|
}
|
||||||
proc->Modules[i].Base = 0;
|
proc->Modules[i].Base = 0;
|
||||||
proc->Modules[i].Size = 0;
|
proc->Modules[i].Size = 0;
|
||||||
@@ -170,6 +172,8 @@ KSTATUS KeCreateProcess(const char* name, PROCESS **out) {
|
|||||||
proc->ThreadCount = 0;
|
proc->ThreadCount = 0;
|
||||||
proc->HandleLock = (SPINLOCK)SPINLOCK_INIT;
|
proc->HandleLock = (SPINLOCK)SPINLOCK_INIT;
|
||||||
proc->ExitCode = 0;
|
proc->ExitCode = 0;
|
||||||
|
proc->Terminating = 0;
|
||||||
|
proc->Exited = 0;
|
||||||
proc->ImageBase = 0;
|
proc->ImageBase = 0;
|
||||||
proc->ImageSize = 0;
|
proc->ImageSize = 0;
|
||||||
proc->UserPebBase = 0;
|
proc->UserPebBase = 0;
|
||||||
@@ -182,6 +186,7 @@ KSTATUS KeCreateProcess(const char* name, PROCESS **out) {
|
|||||||
proc->UserCwdSize = 0;
|
proc->UserCwdSize = 0;
|
||||||
proc->ModuleCount = 0;
|
proc->ModuleCount = 0;
|
||||||
proc->CurrentDirectory[0] = '\0';
|
proc->CurrentDirectory[0] = '\0';
|
||||||
|
proc->CurrentDirectoryLength = 0;
|
||||||
proc->CurrentDrive = 0;
|
proc->CurrentDrive = 0;
|
||||||
proc->CwdLock = (SPINLOCK)SPINLOCK_INIT;
|
proc->CwdLock = (SPINLOCK)SPINLOCK_INIT;
|
||||||
|
|
||||||
@@ -208,33 +213,47 @@ KSTATUS KeCreateProcess(const char* name, PROCESS **out) {
|
|||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// KeSetupUserProcessEnvironment
|
// KeSetupUserProcessEnvironment
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
static KSTATUS KiCopyEnvironmentString(const char* src, uintptr_t* baseOut, size_t* sizeOut) {
|
static KSTATUS KiCopyEnvironmentStringView(SYS_STRING_VIEW src,
|
||||||
|
uintptr_t* baseOut,
|
||||||
|
size_t* sizeOut) {
|
||||||
if (!baseOut || !sizeOut) return STATUS_INVALID_PARAMETER;
|
if (!baseOut || !sizeOut) return STATUS_INVALID_PARAMETER;
|
||||||
if (!src) src = "";
|
*baseOut = 0;
|
||||||
|
*sizeOut = 0;
|
||||||
|
|
||||||
size_t len = strlen(src) + 1;
|
if (!SysSvIsValid(src)) return STATUS_INVALID_PARAMETER;
|
||||||
|
if (SysSvContainsByte(src, '\0')) return STATUS_INVALID_PARAMETER;
|
||||||
|
if (src.Length == (size_t)-1) return STATUS_BUFFER_TOO_SMALL;
|
||||||
|
|
||||||
|
size_t len = src.Length + 1;
|
||||||
char* dst = (char*)MmAllocateUserMemory(len);
|
char* dst = (char*)MmAllocateUserMemory(len);
|
||||||
if (!dst) return STATUS_NO_MEMORY;
|
if (!dst) return STATUS_NO_MEMORY;
|
||||||
|
|
||||||
memcpy(dst, src, len);
|
if (src.Length) memcpy(dst, src.Data, src.Length);
|
||||||
|
dst[src.Length] = '\0';
|
||||||
|
|
||||||
*baseOut = (uintptr_t)dst;
|
*baseOut = (uintptr_t)dst;
|
||||||
*sizeOut = len;
|
*sizeOut = len;
|
||||||
return STATUS_SUCCESS;
|
return STATUS_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
static KSTATUS KiCopyEnvironmentStringFixed(const char* src, size_t capacity,
|
static KSTATUS KiCopyEnvironmentStringViewFixed(SYS_STRING_VIEW src,
|
||||||
uintptr_t* baseOut, size_t* sizeOut) {
|
size_t capacity,
|
||||||
|
uintptr_t* baseOut,
|
||||||
|
size_t* sizeOut) {
|
||||||
if (!baseOut || !sizeOut || capacity == 0) return STATUS_INVALID_PARAMETER;
|
if (!baseOut || !sizeOut || capacity == 0) return STATUS_INVALID_PARAMETER;
|
||||||
if (!src) src = "";
|
*baseOut = 0;
|
||||||
|
*sizeOut = 0;
|
||||||
|
|
||||||
|
if (!SysSvIsValid(src)) return STATUS_INVALID_PARAMETER;
|
||||||
|
if (SysSvContainsByte(src, '\0')) return STATUS_INVALID_PARAMETER;
|
||||||
|
if (src.Length + 1 > capacity) return STATUS_BUFFER_TOO_SMALL;
|
||||||
|
|
||||||
char* dst = (char*)MmAllocateUserMemory(capacity);
|
char* dst = (char*)MmAllocateUserMemory(capacity);
|
||||||
if (!dst) return STATUS_NO_MEMORY;
|
if (!dst) return STATUS_NO_MEMORY;
|
||||||
memset(dst, 0, capacity);
|
memset(dst, 0, capacity);
|
||||||
|
|
||||||
size_t len = strlen(src);
|
if (src.Length) memcpy(dst, src.Data, src.Length);
|
||||||
if (len >= capacity) len = capacity - 1;
|
dst[src.Length] = '\0';
|
||||||
memcpy(dst, src, len);
|
|
||||||
dst[len] = '\0';
|
|
||||||
|
|
||||||
*baseOut = (uintptr_t)dst;
|
*baseOut = (uintptr_t)dst;
|
||||||
*sizeOut = capacity;
|
*sizeOut = capacity;
|
||||||
@@ -266,43 +285,68 @@ static void KiFreePartialProcessEnvironment(PROCESS* proc) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void KiFormatNativeCwd(const PROCESS* proc, const char* currentDirectory,
|
static KSTATUS KiFormatNativeCwdV(const PROCESS* proc,
|
||||||
char* out, size_t outSize) {
|
SYS_STRING_VIEW currentDirectory,
|
||||||
if (!out || outSize == 0) return;
|
char* out,
|
||||||
const char* rel = currentDirectory ? currentDirectory : "";
|
size_t outSize,
|
||||||
|
size_t* lengthOut) {
|
||||||
|
if (lengthOut) *lengthOut = 0;
|
||||||
|
if (!out || outSize == 0) return STATUS_INVALID_PARAMETER;
|
||||||
|
out[0] = '\0';
|
||||||
|
|
||||||
|
if (!SysSvIsValid(currentDirectory)) return STATUS_INVALID_PARAMETER;
|
||||||
|
if (SysSvContainsByte(currentDirectory, '\0')) return STATUS_INVALID_PARAMETER;
|
||||||
|
|
||||||
uint8_t drive = proc ? proc->CurrentDrive : 0;
|
uint8_t drive = proc ? proc->CurrentDrive : 0;
|
||||||
if (drive > 7) drive = 0;
|
if (drive > 7) drive = 0;
|
||||||
|
|
||||||
if (outSize < 4) { out[0] = '\0'; return; }
|
if (currentDirectory.Length + 4 > outSize) return STATUS_BUFFER_TOO_SMALL;
|
||||||
|
|
||||||
out[0] = (char)('0' + drive);
|
out[0] = (char)('0' + drive);
|
||||||
out[1] = ':';
|
out[1] = ':';
|
||||||
out[2] = '/';
|
out[2] = '/';
|
||||||
size_t len = strlen(rel);
|
if (currentDirectory.Length)
|
||||||
if (len + 4 > outSize) len = outSize - 4;
|
memcpy(out + 3, currentDirectory.Data, currentDirectory.Length);
|
||||||
memcpy(out + 3, rel, len);
|
out[3 + currentDirectory.Length] = '\0';
|
||||||
out[3 + len] = '\0';
|
|
||||||
|
if (lengthOut) *lengthOut = 3 + currentDirectory.Length;
|
||||||
|
return STATUS_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
KSTATUS KeSetupUserProcessEnvironment(PROCESS* proc,
|
KSTATUS KeSetupUserProcessEnvironment(PROCESS* proc,
|
||||||
const char* imagePath,
|
SYS_STRING_VIEW imagePath,
|
||||||
const char* commandLine,
|
SYS_STRING_VIEW commandLine,
|
||||||
const char* currentDirectory) {
|
SYS_STRING_VIEW currentDirectory) {
|
||||||
if (!proc || !imagePath) return STATUS_INVALID_PARAMETER;
|
if (!proc || imagePath.Length == 0) return STATUS_INVALID_PARAMETER;
|
||||||
|
if (!SysSvIsValid(imagePath) || !SysSvIsValid(commandLine) || !SysSvIsValid(currentDirectory))
|
||||||
|
return STATUS_INVALID_PARAMETER;
|
||||||
|
if (SysSvContainsByte(imagePath, '\0') ||
|
||||||
|
SysSvContainsByte(commandLine, '\0') ||
|
||||||
|
SysSvContainsByte(currentDirectory, '\0'))
|
||||||
|
return STATUS_INVALID_PARAMETER;
|
||||||
|
|
||||||
|
SYS_STRING_VIEW finalCommandLine = commandLine.Length ? commandLine : imagePath;
|
||||||
|
SYS_STRING_VIEW finalCwdRel = currentDirectory;
|
||||||
|
if (finalCwdRel.Length == 0 && proc->CurrentDirectoryLength)
|
||||||
|
finalCwdRel = SysSvFromParts(proc->CurrentDirectory, proc->CurrentDirectoryLength);
|
||||||
|
|
||||||
const char* finalCommandLine = (commandLine && commandLine[0]) ? commandLine : imagePath;
|
|
||||||
const char* finalCwdRel = currentDirectory ? currentDirectory : proc->CurrentDirectory;
|
|
||||||
char finalCwd[SYS_CWD_MAX];
|
char finalCwd[SYS_CWD_MAX];
|
||||||
KiFormatNativeCwd(proc, finalCwdRel, finalCwd, sizeof(finalCwd));
|
size_t finalCwdLength = 0;
|
||||||
|
KSTATUS s = KiFormatNativeCwdV(proc, finalCwdRel, finalCwd, sizeof(finalCwd), &finalCwdLength);
|
||||||
|
if (!KSUCCESS(s)) return s;
|
||||||
|
|
||||||
KiFreePartialProcessEnvironment(proc);
|
KiFreePartialProcessEnvironment(proc);
|
||||||
|
|
||||||
KSTATUS s = KiCopyEnvironmentString(imagePath, &proc->UserImagePathBase, &proc->UserImagePathSize);
|
s = KiCopyEnvironmentStringView(imagePath, &proc->UserImagePathBase, &proc->UserImagePathSize);
|
||||||
if (!KSUCCESS(s)) goto fail;
|
if (!KSUCCESS(s)) goto fail;
|
||||||
|
|
||||||
s = KiCopyEnvironmentString(finalCommandLine, &proc->UserCmdlineBase, &proc->UserCmdlineSize);
|
s = KiCopyEnvironmentStringView(finalCommandLine, &proc->UserCmdlineBase, &proc->UserCmdlineSize);
|
||||||
if (!KSUCCESS(s)) goto fail;
|
if (!KSUCCESS(s)) goto fail;
|
||||||
|
|
||||||
s = KiCopyEnvironmentStringFixed(finalCwd, SYS_CWD_MAX, &proc->UserCwdBase, &proc->UserCwdSize);
|
s = KiCopyEnvironmentStringViewFixed(SysSvFromParts(finalCwd, finalCwdLength),
|
||||||
|
SYS_CWD_MAX,
|
||||||
|
&proc->UserCwdBase,
|
||||||
|
&proc->UserCwdSize);
|
||||||
if (!KSUCCESS(s)) goto fail;
|
if (!KSUCCESS(s)) goto fail;
|
||||||
|
|
||||||
SYS_PEB* peb = (SYS_PEB*)MmAllocateUserMemory(sizeof(SYS_PEB));
|
SYS_PEB* peb = (SYS_PEB*)MmAllocateUserMemory(sizeof(SYS_PEB));
|
||||||
@@ -312,12 +356,9 @@ KSTATUS KeSetupUserProcessEnvironment(PROCESS* proc,
|
|||||||
peb->Magic = SYS_PEB_MAGIC;
|
peb->Magic = SYS_PEB_MAGIC;
|
||||||
peb->Version = SYS_ENV_VERSION;
|
peb->Version = SYS_ENV_VERSION;
|
||||||
peb->ProcessId = proc->ProcessId;
|
peb->ProcessId = proc->ProcessId;
|
||||||
peb->ImagePath = (const char*)proc->UserImagePathBase;
|
peb->ImagePath = SysSvFromParts((const char*)proc->UserImagePathBase, imagePath.Length);
|
||||||
peb->ImagePathLength = proc->UserImagePathSize ? proc->UserImagePathSize - 1 : 0;
|
peb->CommandLine = SysSvFromParts((const char*)proc->UserCmdlineBase, finalCommandLine.Length);
|
||||||
peb->CommandLine = (const char*)proc->UserCmdlineBase;
|
peb->CurrentDirectory = SysSvFromParts((const char*)proc->UserCwdBase, finalCwdLength);
|
||||||
peb->CommandLineLength = proc->UserCmdlineSize ? proc->UserCmdlineSize - 1 : 0;
|
|
||||||
peb->CurrentDirectory = (const char*)proc->UserCwdBase;
|
|
||||||
peb->CurrentDirectoryLength = strlen(finalCwd);
|
|
||||||
|
|
||||||
proc->UserPebBase = (uintptr_t)peb;
|
proc->UserPebBase = (uintptr_t)peb;
|
||||||
proc->UserPebSize = sizeof(SYS_PEB);
|
proc->UserPebSize = sizeof(SYS_PEB);
|
||||||
@@ -329,16 +370,56 @@ fail:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// KeExitProcess
|
// Process exit / termination
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
static void KiCompleteProcessExitIfLastThread(PROCESS* process) {
|
||||||
|
if (!process) return;
|
||||||
|
|
||||||
|
uintptr_t flags = KeAcquireSpinlock(&process->HandleLock);
|
||||||
|
uint32_t threadCount = process->ThreadCount;
|
||||||
|
KeReleaseSpinlock(&process->HandleLock, flags);
|
||||||
|
|
||||||
|
if (threadCount == 0 && _InterlockedExchange(&process->Exited, 1) == 0) {
|
||||||
|
if (process->ExitEvent) KeSignalEvent(process->ExitEvent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void KeExitProcess(PROCESS* process, int exitCode) {
|
void KeExitProcess(PROCESS* process, int exitCode) {
|
||||||
if (!process) return;
|
if (!process) return;
|
||||||
process->ExitCode = exitCode;
|
process->ExitCode = exitCode;
|
||||||
// Wake any threads waiting for this process to finish.
|
_InterlockedExchange(&process->Terminating, 1);
|
||||||
if (process->ExitEvent) KeSignalEvent(process->ExitEvent);
|
|
||||||
// The last thread exiting via KeExitThread will drop the process refcount.
|
// The calling thread exits immediately after this in SysExitProcess. Do
|
||||||
// This function is a policy hook; actual cleanup is in KiProcessDestroy.
|
// not drop the process object here: each thread owns one process reference,
|
||||||
ObDereferenceObject(&process->Header);
|
// and KiThreadDestroy releases that reference after unlinking the thread.
|
||||||
|
KiCompleteProcessExitIfLastThread(process);
|
||||||
|
}
|
||||||
|
|
||||||
|
KSTATUS KeTerminateProcess(PROCESS* process, int exitCode) {
|
||||||
|
if (!process) return STATUS_INVALID_PARAMETER;
|
||||||
|
|
||||||
|
process->ExitCode = exitCode;
|
||||||
|
_InterlockedExchange(&process->Terminating, 1);
|
||||||
|
|
||||||
|
// Wake alertable waiters so they can notice Terminating at the syscall/
|
||||||
|
// wait boundary. This is intentionally conservative: fully asynchronous
|
||||||
|
// forced thread teardown needs object-specific wait-list locking and is a
|
||||||
|
// larger process-lifecycle project.
|
||||||
|
uintptr_t flags = KeAcquireSpinlock(&process->HandleLock);
|
||||||
|
LIST_ENTRY* head = &process->ThreadList;
|
||||||
|
for (LIST_ENTRY* node = head->Fwd; node != head; node = node->Fwd) {
|
||||||
|
THREAD* t = CONTAINING_RECORD(node, THREAD, ProcessLink);
|
||||||
|
if (t != KeGetCurrentThread())
|
||||||
|
(void)KeAlertThread(t);
|
||||||
|
}
|
||||||
|
KeReleaseSpinlock(&process->HandleLock, flags);
|
||||||
|
|
||||||
|
KiCompleteProcessExitIfLastThread(process);
|
||||||
|
return STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
int KeIsProcessTerminating(PROCESS* process) {
|
||||||
|
return process && process->Terminating != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
@@ -353,15 +434,17 @@ KSTATUS KeGetProcessExitCode(PROCESS* process, int* exitCodeOut) {
|
|||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Handle table
|
// Handle table
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
KSTATUS KeAllocateHandle(PROCESS* process, OBJECT* object, uint32_t* handleOut) {
|
KSTATUS KeAllocateHandleWithRights(PROCESS* process, OBJECT* object, uint32_t rights, uint32_t* handleOut) {
|
||||||
if (!process || !object || !handleOut) return STATUS_INVALID_PARAMETER;
|
if (!process || !object || !handleOut || rights == 0) return STATUS_INVALID_PARAMETER;
|
||||||
|
|
||||||
uintptr_t flags = KeAcquireSpinlock(&process->HandleLock);
|
uintptr_t flags = KeAcquireSpinlock(&process->HandleLock);
|
||||||
|
|
||||||
for (int i = 1; i < HANDLE_TABLE_SIZE; i++) {
|
for (int i = 1; i < HANDLE_TABLE_SIZE; i++) {
|
||||||
if (!process->Handles[i]) {
|
if (!process->Handles[i].Object) {
|
||||||
ObReferenceObject(object);
|
ObReferenceObject(object);
|
||||||
process->Handles[i] = object;
|
process->Handles[i].Object = object;
|
||||||
|
process->Handles[i].Rights = rights;
|
||||||
|
process->Handles[i].Flags = 0;
|
||||||
*handleOut = (uint32_t)i;
|
*handleOut = (uint32_t)i;
|
||||||
KeReleaseSpinlock(&process->HandleLock, flags);
|
KeReleaseSpinlock(&process->HandleLock, flags);
|
||||||
return STATUS_SUCCESS;
|
return STATUS_SUCCESS;
|
||||||
@@ -372,13 +455,19 @@ KSTATUS KeAllocateHandle(PROCESS* process, OBJECT* object, uint32_t* handleOut)
|
|||||||
return STATUS_NO_MEMORY;
|
return STATUS_NO_MEMORY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
KSTATUS KeAllocateHandle(PROCESS* process, OBJECT* object, uint32_t* handleOut) {
|
||||||
|
return KeAllocateHandleWithRights(process, object, HANDLE_RIGHT_ALL, handleOut);
|
||||||
|
}
|
||||||
|
|
||||||
void KeCloseHandle(PROCESS* process, uint32_t handle) {
|
void KeCloseHandle(PROCESS* process, uint32_t handle) {
|
||||||
if (!process || handle == 0 || handle >= HANDLE_TABLE_SIZE) return;
|
if (!process || handle == 0 || handle >= HANDLE_TABLE_SIZE) return;
|
||||||
|
|
||||||
uintptr_t flags = KeAcquireSpinlock(&process->HandleLock);
|
uintptr_t flags = KeAcquireSpinlock(&process->HandleLock);
|
||||||
OBJECT* obj = process->Handles[handle];
|
OBJECT* obj = process->Handles[handle].Object;
|
||||||
if (obj) {
|
if (obj) {
|
||||||
process->Handles[handle] = (OBJECT *)0;
|
process->Handles[handle].Object = (OBJECT*)0;
|
||||||
|
process->Handles[handle].Rights = 0;
|
||||||
|
process->Handles[handle].Flags = 0;
|
||||||
KeReleaseSpinlock(&process->HandleLock, flags);
|
KeReleaseSpinlock(&process->HandleLock, flags);
|
||||||
ObDereferenceObject(obj);
|
ObDereferenceObject(obj);
|
||||||
return;
|
return;
|
||||||
@@ -386,17 +475,33 @@ void KeCloseHandle(PROCESS* process, uint32_t handle) {
|
|||||||
KeReleaseSpinlock(&process->HandleLock, flags);
|
KeReleaseSpinlock(&process->HandleLock, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
OBJECT* KeLookupHandle(PROCESS* process, uint32_t handle) {
|
OBJECT* KeLookupHandleWithRights(PROCESS* process, uint32_t handle, uint32_t requiredRights) {
|
||||||
if (!process || handle == 0 || handle >= HANDLE_TABLE_SIZE)
|
if (!process || handle == 0 || handle >= HANDLE_TABLE_SIZE)
|
||||||
return (OBJECT *)0;
|
return (OBJECT*)0;
|
||||||
|
|
||||||
uintptr_t flags = KeAcquireSpinlock(&process->HandleLock);
|
uintptr_t flags = KeAcquireSpinlock(&process->HandleLock);
|
||||||
OBJECT* obj = process->Handles[handle];
|
HANDLE_ENTRY* entry = &process->Handles[handle];
|
||||||
|
OBJECT* obj = entry->Object;
|
||||||
|
if (obj && requiredRights && ((entry->Rights & requiredRights) != requiredRights))
|
||||||
|
obj = (OBJECT*)0;
|
||||||
if (obj) ObReferenceObject(obj);
|
if (obj) ObReferenceObject(obj);
|
||||||
KeReleaseSpinlock(&process->HandleLock, flags);
|
KeReleaseSpinlock(&process->HandleLock, flags);
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
OBJECT* KeLookupHandle(PROCESS* process, uint32_t handle) {
|
||||||
|
return KeLookupHandleWithRights(process, handle, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t KeGetHandleRights(PROCESS* process, uint32_t handle) {
|
||||||
|
if (!process || handle == 0 || handle >= HANDLE_TABLE_SIZE) return 0;
|
||||||
|
|
||||||
|
uintptr_t flags = KeAcquireSpinlock(&process->HandleLock);
|
||||||
|
uint32_t rights = process->Handles[handle].Object ? process->Handles[handle].Rights : 0;
|
||||||
|
KeReleaseSpinlock(&process->HandleLock, flags);
|
||||||
|
return rights;
|
||||||
|
}
|
||||||
|
|
||||||
PROCESS* KeGetProcessById(uint32_t pid) {
|
PROCESS* KeGetProcessById(uint32_t pid) {
|
||||||
KiProcessInit();
|
KiProcessInit();
|
||||||
uintptr_t flags = KeAcquireSpinlock(&gProcessListLock);
|
uintptr_t flags = KeAcquireSpinlock(&gProcessListLock);
|
||||||
|
|||||||
@@ -8,12 +8,27 @@
|
|||||||
#include <ke_event.h>
|
#include <ke_event.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include <sys_string.h>
|
||||||
|
|
||||||
#define PROCESS_NAME_MAX 64
|
#define PROCESS_NAME_MAX 64
|
||||||
#define HANDLE_TABLE_SIZE 256
|
#define HANDLE_TABLE_SIZE 256
|
||||||
|
|
||||||
|
|
||||||
#define PROCESS_MODULE_MAX 16
|
#define PROCESS_MODULE_MAX 16
|
||||||
|
#define PROCESS_CWD_MAX 512
|
||||||
|
|
||||||
|
#define HANDLE_RIGHT_READ 0x00000001u
|
||||||
|
#define HANDLE_RIGHT_WRITE 0x00000002u
|
||||||
|
#define HANDLE_RIGHT_WAIT 0x00000004u
|
||||||
|
#define HANDLE_RIGHT_SIGNAL 0x00000008u
|
||||||
|
#define HANDLE_RIGHT_DUPLICATE 0x00000010u
|
||||||
|
#define HANDLE_RIGHT_QUERY 0x00000020u
|
||||||
|
#define HANDLE_RIGHT_TERMINATE 0x00000040u
|
||||||
|
#define HANDLE_RIGHT_ALL 0xFFFFFFFFu
|
||||||
|
|
||||||
|
typedef struct HANDLE_ENTRY {
|
||||||
|
OBJECT* Object;
|
||||||
|
uint32_t Rights;
|
||||||
|
uint32_t Flags;
|
||||||
|
} HANDLE_ENTRY;
|
||||||
|
|
||||||
typedef struct PROCESS_MODULE {
|
typedef struct PROCESS_MODULE {
|
||||||
char Name[32];
|
char Name[32];
|
||||||
@@ -27,7 +42,8 @@ typedef struct PROCESS {
|
|||||||
uint32_t ProcessId;
|
uint32_t ProcessId;
|
||||||
char Name[PROCESS_NAME_MAX]; /* UTF-8 */
|
char Name[PROCESS_NAME_MAX]; /* UTF-8 */
|
||||||
|
|
||||||
char CurrentDirectory[512];
|
char CurrentDirectory[PROCESS_CWD_MAX];
|
||||||
|
size_t CurrentDirectoryLength;
|
||||||
uint8_t CurrentDrive;
|
uint8_t CurrentDrive;
|
||||||
SPINLOCK CwdLock;
|
SPINLOCK CwdLock;
|
||||||
|
|
||||||
@@ -36,12 +52,14 @@ typedef struct PROCESS {
|
|||||||
uint32_t ThreadCount;
|
uint32_t ThreadCount;
|
||||||
|
|
||||||
/* Handle table.
|
/* Handle table.
|
||||||
Index 0 is always NULL (the invalid handle).
|
Index 0 is always empty (the invalid handle).
|
||||||
Entries are OBJECT pointers, each holding a reference. */
|
Each live entry holds one object reference plus rights metadata. */
|
||||||
OBJECT* Handles[HANDLE_TABLE_SIZE];
|
HANDLE_ENTRY Handles[HANDLE_TABLE_SIZE];
|
||||||
SPINLOCK HandleLock;
|
SPINLOCK HandleLock;
|
||||||
|
|
||||||
int ExitCode;
|
int ExitCode;
|
||||||
|
volatile long Terminating;
|
||||||
|
volatile long Exited;
|
||||||
|
|
||||||
/* Main executable image. Used for cleanup. */
|
/* Main executable image. Used for cleanup. */
|
||||||
uintptr_t ImageBase;
|
uintptr_t ImageBase;
|
||||||
@@ -70,14 +88,22 @@ typedef struct PROCESS {
|
|||||||
|
|
||||||
KSTATUS KeCreateProcess(const char* name, PROCESS **out);
|
KSTATUS KeCreateProcess(const char* name, PROCESS **out);
|
||||||
KSTATUS KeCreateSystemProcess(PROCESS** out);
|
KSTATUS KeCreateSystemProcess(PROCESS** out);
|
||||||
KSTATUS KeSetupUserProcessEnvironment(PROCESS* process, const char* imagePath, const char* commandLine, const char* currentDirectory);
|
KSTATUS KeSetupUserProcessEnvironment(PROCESS* process,
|
||||||
|
SYS_STRING_VIEW imagePath,
|
||||||
|
SYS_STRING_VIEW commandLine,
|
||||||
|
SYS_STRING_VIEW currentDirectory);
|
||||||
void KeExitProcess(PROCESS* process, int exitCode);
|
void KeExitProcess(PROCESS* process, int exitCode);
|
||||||
|
KSTATUS KeTerminateProcess(PROCESS* process, int exitCode);
|
||||||
|
int KeIsProcessTerminating(PROCESS* process);
|
||||||
KSTATUS KeGetProcessExitCode(PROCESS* process, int* exitCodeOut);
|
KSTATUS KeGetProcessExitCode(PROCESS* process, int* exitCodeOut);
|
||||||
|
|
||||||
/* Handle table operations. */
|
/* Handle table operations. */
|
||||||
KSTATUS KeAllocateHandle(PROCESS* process, OBJECT* object, uint32_t* handleOut);
|
KSTATUS KeAllocateHandle(PROCESS* process, OBJECT* object, uint32_t* handleOut);
|
||||||
|
KSTATUS KeAllocateHandleWithRights(PROCESS* process, OBJECT* object, uint32_t rights, uint32_t* handleOut);
|
||||||
void KeCloseHandle(PROCESS* process, uint32_t handle);
|
void KeCloseHandle(PROCESS* process, uint32_t handle);
|
||||||
OBJECT* KeLookupHandle(PROCESS* process, uint32_t handle);
|
OBJECT* KeLookupHandle(PROCESS* process, uint32_t handle);
|
||||||
|
OBJECT* KeLookupHandleWithRights(PROCESS* process, uint32_t handle, uint32_t requiredRights);
|
||||||
|
uint32_t KeGetHandleRights(PROCESS* process, uint32_t handle);
|
||||||
|
|
||||||
/* Return the global process list head (kernel-internal). */
|
/* Return the global process list head (kernel-internal). */
|
||||||
LIST_ENTRY* KeGetProcessList(void);
|
LIST_ENTRY* KeGetProcessList(void);
|
||||||
|
|||||||
@@ -20,10 +20,18 @@ static THREAD* gIdleThread;
|
|||||||
static THREAD* gOutgoingThread;
|
static THREAD* gOutgoingThread;
|
||||||
static uint32_t gAgingCounter = 0;
|
static uint32_t gAgingCounter = 0;
|
||||||
static volatile long gReschedulePending = 0;
|
static volatile long gReschedulePending = 0;
|
||||||
|
static KE_SCHED_STATS gSchedStats;
|
||||||
|
|
||||||
THREAD* KeGetCurrentThread(void) { return gCurrentThread; }
|
THREAD* KeGetCurrentThread(void) { return gCurrentThread; }
|
||||||
void KeSetCurrentThread(THREAD* t) { gCurrentThread = t; }
|
void KeSetCurrentThread(THREAD* t) { gCurrentThread = t; }
|
||||||
|
|
||||||
|
void KeGetSchedulerStats(KE_SCHED_STATS* out) {
|
||||||
|
if (!out) return;
|
||||||
|
uintptr_t flags = KeAcquireSpinlock(&gSchedLock);
|
||||||
|
*out = gSchedStats;
|
||||||
|
KeReleaseSpinlock(&gSchedLock, flags);
|
||||||
|
}
|
||||||
|
|
||||||
static void KiIdleLoop(void) {
|
static void KiIdleLoop(void) {
|
||||||
while (true) { __halt(); }
|
while (true) { __halt(); }
|
||||||
}
|
}
|
||||||
@@ -121,6 +129,14 @@ void KeUnreadyThread(THREAD* thread) {
|
|||||||
KeReleaseSpinlock(&gSchedLock, flags);
|
KeReleaseSpinlock(&gSchedLock, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int KiAnyRunnableLocked(void) {
|
||||||
|
for (int p = 0; p < PriorityCount; p++) {
|
||||||
|
if (!LIST_IS_EMPTY(&gRunQueue[p]))
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static THREAD* KiPickNext(void) {
|
static THREAD* KiPickNext(void) {
|
||||||
for (int p = PriorityCount - 1; p >= 0; p--) {
|
for (int p = PriorityCount - 1; p >= 0; p--) {
|
||||||
if (!LIST_IS_EMPTY(&gRunQueue[p])) {
|
if (!LIST_IS_EMPTY(&gRunQueue[p])) {
|
||||||
@@ -149,7 +165,18 @@ static void KiSchedule(void) {
|
|||||||
uintptr_t flags = KeAcquireSpinlock(&gSchedLock);
|
uintptr_t flags = KeAcquireSpinlock(&gSchedLock);
|
||||||
cur->SchedLockFlags = flags;
|
cur->SchedLockFlags = flags;
|
||||||
|
|
||||||
|
// The currently running thread is not present in a run queue. If it is
|
||||||
|
// still runnable and nobody else is ready, keep executing it. Otherwise a
|
||||||
|
// lone CPU-bound thread gets alternated with the idle thread on every timer
|
||||||
|
// tick, effectively turning a periodic preemption check into a 50% throttle.
|
||||||
|
if (cur != gIdleThread && cur->State == ThreadRunning && !KiAnyRunnableLocked()) {
|
||||||
|
gSchedStats.KeepCurrentDecisions++;
|
||||||
|
KeReleaseSpinlock(&gSchedLock, flags);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
THREAD* next = KiPickNext();
|
THREAD* next = KiPickNext();
|
||||||
|
if (next == gIdleThread) gSchedStats.IdleSelections++;
|
||||||
if (next == cur) {
|
if (next == cur) {
|
||||||
// Only re-queue if still running normally; a ThreadWaiting/ThreadDead
|
// Only re-queue if still running normally; a ThreadWaiting/ThreadDead
|
||||||
// caller has already placed SchedulerLink elsewhere (or is discarding
|
// caller has already placed SchedulerLink elsewhere (or is discarding
|
||||||
@@ -174,6 +201,7 @@ static void KiSchedule(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
gOutgoingThread = cur;
|
gOutgoingThread = cur;
|
||||||
|
gSchedStats.ContextSwitches++;
|
||||||
KeSwitchContext(cur, next);
|
KeSwitchContext(cur, next);
|
||||||
KeSchedulerPostSwitch();
|
KeSchedulerPostSwitch();
|
||||||
}
|
}
|
||||||
@@ -190,12 +218,14 @@ void KeSchedulerTick(void) {
|
|||||||
uintptr_t flags = KeAcquireSpinlock(&gSchedLock);
|
uintptr_t flags = KeAcquireSpinlock(&gSchedLock);
|
||||||
cur->SchedLockFlags = flags; // Issue 5 fix
|
cur->SchedLockFlags = flags; // Issue 5 fix
|
||||||
|
|
||||||
|
gSchedStats.Ticks++;
|
||||||
cur->QuantaConsumed++;
|
cur->QuantaConsumed++;
|
||||||
|
|
||||||
if (cur != gIdleThread && cur->QuantaConsumed >= DEMOTION_QUANTA) {
|
if (cur != gIdleThread && cur->QuantaConsumed >= DEMOTION_QUANTA) {
|
||||||
cur->QuantaConsumed = 0;
|
cur->QuantaConsumed = 0;
|
||||||
if (cur->Priority > PriorityLow) {
|
if (cur->Priority > PriorityLow) {
|
||||||
cur->Priority = (THREAD_PRIORITY)(cur->Priority - 1);
|
cur->Priority = (THREAD_PRIORITY)(cur->Priority - 1);
|
||||||
|
gSchedStats.Demotions++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -208,6 +238,7 @@ void KeSchedulerTick(void) {
|
|||||||
LIST_ENTRY* node = ListPopHead(&gRunQueue[p]);
|
LIST_ENTRY* node = ListPopHead(&gRunQueue[p]);
|
||||||
THREAD* t = CONTAINING_RECORD(node, THREAD, SchedulerLink);
|
THREAD* t = CONTAINING_RECORD(node, THREAD, SchedulerLink);
|
||||||
t->Priority = PriorityHigh;
|
t->Priority = PriorityHigh;
|
||||||
|
gSchedStats.AgingPromotions++;
|
||||||
ListInsertTail(&gRunQueue[PriorityHigh], &t->SchedulerLink);
|
ListInsertTail(&gRunQueue[PriorityHigh], &t->SchedulerLink);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+45
-34
@@ -1,34 +1,45 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
// ke_sched.h - Multi-Level Feedback Queue scheduler
|
// ke_sched.h - Multi-Level Feedback Queue scheduler
|
||||||
// Three run queues (Low / Normal / High). IRQ 0 drives KeSchedulerTick.
|
// Three run queues (Low / Normal / High). IRQ 0 drives KeSchedulerTick.
|
||||||
#include <ke_thread.h>
|
#include <ke_thread.h>
|
||||||
|
|
||||||
#define QUANTUM_MS 10 // base time slice in milliseconds
|
#define QUANTUM_MS 10 // base time slice in milliseconds
|
||||||
|
|
||||||
// Called once by KernelMain before any threads are created.
|
typedef struct KE_SCHED_STATS {
|
||||||
void KeInitializeScheduler(void);
|
uint64_t Ticks;
|
||||||
|
uint64_t ContextSwitches;
|
||||||
// Add / remove a thread from the run queues.
|
uint64_t KeepCurrentDecisions;
|
||||||
void KeReadyThread(THREAD* thread);
|
uint64_t IdleSelections;
|
||||||
void KeUnreadyThread(THREAD* thread);
|
uint64_t Demotions;
|
||||||
|
uint64_t AgingPromotions;
|
||||||
// Called from the timer ISR every QUANTUM_MS. This only performs timer
|
} KE_SCHED_STATS;
|
||||||
// accounting and requests a reschedule; the actual context switch happens from
|
|
||||||
// KiExitInterrupt after the IRQ handler has returned and EOI has been sent.
|
// Called once by KernelMain before any threads are created.
|
||||||
void KeSchedulerTick(void);
|
void KeInitializeScheduler(void);
|
||||||
|
|
||||||
// Internal scheduler hooks used by the interrupt-exit path. KiRequestReschedule
|
// Add / remove a thread from the run queues.
|
||||||
// is cheap and can be called from interrupt context. KiScheduleFromInterrupt
|
void KeReadyThread(THREAD* thread);
|
||||||
// consumes the pending request and performs the context switch, if needed.
|
void KeUnreadyThread(THREAD* thread);
|
||||||
void KiRequestReschedule(void);
|
|
||||||
int KiIsReschedulePending(void);
|
// Called from the timer ISR every QUANTUM_MS. This only performs timer
|
||||||
void KiScheduleFromInterrupt(void);
|
// accounting and requests a reschedule; the actual context switch happens from
|
||||||
|
// KiExitInterrupt after the IRQ handler has returned and EOI has been sent.
|
||||||
// Voluntarily yield the CPU. Picks the next runnable thread immediately.
|
void KeSchedulerTick(void);
|
||||||
__declspec(dllexport) void KeYield(void);
|
|
||||||
|
// Internal scheduler hooks used by the interrupt-exit path. KiRequestReschedule
|
||||||
// Returns the thread currently running on this CPU (reads GS:[0]).
|
// is cheap and can be called from interrupt context. KiScheduleFromInterrupt
|
||||||
THREAD* KeGetCurrentThread(void);
|
// consumes the pending request and performs the context switch, if needed.
|
||||||
|
void KiRequestReschedule(void);
|
||||||
// Set the current-thread GS slot. Called once per CPU at boot.
|
int KiIsReschedulePending(void);
|
||||||
void KeSetCurrentThread(THREAD* thread);
|
void KiScheduleFromInterrupt(void);
|
||||||
|
|
||||||
|
// Voluntarily yield the CPU. Picks the next runnable thread immediately.
|
||||||
|
__declspec(dllexport) void KeYield(void);
|
||||||
|
|
||||||
|
void KeGetSchedulerStats(KE_SCHED_STATS* out);
|
||||||
|
|
||||||
|
// Returns the thread currently running on this CPU (reads GS:[0]).
|
||||||
|
THREAD* KeGetCurrentThread(void);
|
||||||
|
|
||||||
|
// Set the current-thread GS slot. Called once per CPU at boot.
|
||||||
|
void KeSetCurrentThread(THREAD* thread);
|
||||||
@@ -476,6 +476,43 @@ static void KiRunIrpKernelApcSelfTest(const HAL_API* hal) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// String-view ABI self-test
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
static void KiRunStringViewSelfTest(void) {
|
||||||
|
DbgPrint("STRINGVIEW selftest: begin\n");
|
||||||
|
|
||||||
|
SYS_STRING_VIEW hello = SYS_SV_LITERAL("Hello");
|
||||||
|
if (hello.Length != 5 || !SysSvEqualLit(hello, "Hello"))
|
||||||
|
KePanic("STRINGVIEW selftest: literal/equality failed\n");
|
||||||
|
|
||||||
|
static const char embedded[] = { 'a', 'b', '\0', 'c', 'd' };
|
||||||
|
SYS_STRING_VIEW emb = SysSvFromParts(embedded, sizeof(embedded));
|
||||||
|
if (emb.Length != 5 || !SysSvContainsByte(emb, '\0'))
|
||||||
|
KePanic("STRINGVIEW selftest: embedded-NUL detection failed\n");
|
||||||
|
|
||||||
|
char copy[6];
|
||||||
|
if (!SysSvCopyZ(copy, sizeof(copy), emb))
|
||||||
|
KePanic("STRINGVIEW selftest: bounded copy failed\n");
|
||||||
|
if (memcmp(copy, embedded, sizeof(embedded)) != 0 || copy[5] != '\0')
|
||||||
|
KePanic("STRINGVIEW selftest: bounded copy contents wrong\n");
|
||||||
|
|
||||||
|
if (SysSvCopyZ(copy, 4, emb))
|
||||||
|
KePanic("STRINGVIEW selftest: undersized copy unexpectedly succeeded\n");
|
||||||
|
|
||||||
|
// Paths are string views at the ABI boundary, but FAT/VFS path names do not
|
||||||
|
// accept embedded NUL bytes. This must fail before reaching any filesystem.
|
||||||
|
FILE_OBJECT* f = (FILE_OBJECT*)0;
|
||||||
|
KSTATUS s = IoOpenFile(SysSvFromParts("system/init.exe\0junk", 20), &f);
|
||||||
|
if (s != STATUS_INVALID_PARAMETER)
|
||||||
|
KePanic("STRINGVIEW selftest: embedded-NUL path status=%u\n", s);
|
||||||
|
|
||||||
|
DbgPrint("STRINGVIEW selftest: sample view='%v' len=%llu\n",
|
||||||
|
hello, (uint64_t)hello.Length);
|
||||||
|
DbgPrint("STRINGVIEW selftest: PASS\n");
|
||||||
|
}
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
// Async file I/O self-test
|
// Async file I/O self-test
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
@@ -534,7 +571,7 @@ static void KiRunAsyncFileIoSelfTest(void) {
|
|||||||
memset(&st, 0, sizeof(st));
|
memset(&st, 0, sizeof(st));
|
||||||
st.Iosb.Status = STATUS_FAILURE;
|
st.Iosb.Status = STATUS_FAILURE;
|
||||||
|
|
||||||
KSTATUS s = IoOpenFile("system/init.exe", &st.File);
|
KSTATUS s = IoOpenFile(SYS_SV_LITERAL("system/init.exe"), &st.File);
|
||||||
if (!KSUCCESS(s)) {
|
if (!KSUCCESS(s)) {
|
||||||
DbgPrint("FILE/ASYNC selftest: SKIP, system/init.exe open failed %u\n", s);
|
DbgPrint("FILE/ASYNC selftest: SKIP, system/init.exe open failed %u\n", s);
|
||||||
return;
|
return;
|
||||||
@@ -614,7 +651,7 @@ static void KiRunAsyncFileIoSelfTest(void) {
|
|||||||
wiosb.Status = STATUS_FAILURE;
|
wiosb.Status = STATUS_FAILURE;
|
||||||
wiosb.Information = 0;
|
wiosb.Information = 0;
|
||||||
|
|
||||||
s = IoCreateFile("asyncio.tmp", &wf);
|
s = IoCreateFile(SYS_SV_LITERAL("asyncio.tmp"), &wf);
|
||||||
if (!KSUCCESS(s))
|
if (!KSUCCESS(s))
|
||||||
KePanic("FILE/ASYNC selftest: create write temp failed %u\n", s);
|
KePanic("FILE/ASYNC selftest: create write temp failed %u\n", s);
|
||||||
|
|
||||||
@@ -651,7 +688,7 @@ static void KiRunAsyncFileIoSelfTest(void) {
|
|||||||
IoCloseFile(wf);
|
IoCloseFile(wf);
|
||||||
wf = (FILE_OBJECT*)0;
|
wf = (FILE_OBJECT*)0;
|
||||||
|
|
||||||
s = IoOpenFile("asyncio.tmp", &rf);
|
s = IoOpenFile(SYS_SV_LITERAL("asyncio.tmp"), &rf);
|
||||||
if (!KSUCCESS(s))
|
if (!KSUCCESS(s))
|
||||||
KePanic("FILE/ASYNC selftest: reopen write temp failed %u\n", s);
|
KePanic("FILE/ASYNC selftest: reopen write temp failed %u\n", s);
|
||||||
|
|
||||||
@@ -664,7 +701,7 @@ static void KiRunAsyncFileIoSelfTest(void) {
|
|||||||
|
|
||||||
IoCloseFile(rf);
|
IoCloseFile(rf);
|
||||||
ObDereferenceObject(&wev->Header);
|
ObDereferenceObject(&wev->Header);
|
||||||
s = IoDeleteFile("asyncio.tmp");
|
s = IoDeleteFile(SYS_SV_LITERAL("asyncio.tmp"));
|
||||||
if (!KSUCCESS(s))
|
if (!KSUCCESS(s))
|
||||||
KePanic("FILE/ASYNC selftest: delete temp failed %u\n", s);
|
KePanic("FILE/ASYNC selftest: delete temp failed %u\n", s);
|
||||||
|
|
||||||
@@ -695,6 +732,7 @@ static void KiRunTrapFrameSelfTest(void) {
|
|||||||
void KiRunKernelSelfTests(const HAL_API* hal) {
|
void KiRunKernelSelfTests(const HAL_API* hal) {
|
||||||
DbgPrint("Kernel selftests: begin\n");
|
DbgPrint("Kernel selftests: begin\n");
|
||||||
KiRunKernelApcSelfTest();
|
KiRunKernelApcSelfTest();
|
||||||
|
KiRunStringViewSelfTest();
|
||||||
KiRunIrpKernelApcSelfTest(hal);
|
KiRunIrpKernelApcSelfTest(hal);
|
||||||
KiRunAsyncFileIoSelfTest();
|
KiRunAsyncFileIoSelfTest();
|
||||||
KiRunTrapFrameSelfTest();
|
KiRunTrapFrameSelfTest();
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ __declspec(dllexport) KSTATUS KeGetSettingString(const char* section,
|
|||||||
buffer[0] = '\0';
|
buffer[0] = '\0';
|
||||||
|
|
||||||
FILE_OBJECT* file = NULL;
|
FILE_OBJECT* file = NULL;
|
||||||
KSTATUS st = IoOpenFile(KE_SETTINGS_PATH, &file);
|
KSTATUS st = IoOpenFile(SYS_SV_LITERAL(KE_SETTINGS_PATH), &file);
|
||||||
if (!KSUCCESS(st))
|
if (!KSUCCESS(st))
|
||||||
return st;
|
return st;
|
||||||
|
|
||||||
|
|||||||
+356
-195
File diff suppressed because it is too large.
Load diff
+18
-7
@@ -40,11 +40,20 @@ static void KiThreadDestroy(OBJECT* obj) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (thread->Owner) {
|
if (thread->Owner) {
|
||||||
uintptr_t flags = KeAcquireSpinlock(&thread->Owner->HandleLock);
|
PROCESS* owner = thread->Owner;
|
||||||
|
uint32_t remaining = 0;
|
||||||
|
uintptr_t flags = KeAcquireSpinlock(&owner->HandleLock);
|
||||||
ListRemove(&thread->ProcessLink);
|
ListRemove(&thread->ProcessLink);
|
||||||
thread->Owner->ThreadCount--;
|
if (owner->ThreadCount) owner->ThreadCount--;
|
||||||
KeReleaseSpinlock(&thread->Owner->HandleLock, flags);
|
remaining = owner->ThreadCount;
|
||||||
ObDereferenceObject(&thread->Owner->Header);
|
KeReleaseSpinlock(&owner->HandleLock, flags);
|
||||||
|
|
||||||
|
if (remaining == 0 && owner->Terminating &&
|
||||||
|
_InterlockedExchange(&owner->Exited, 1) == 0) {
|
||||||
|
if (owner->ExitEvent) KeSignalEvent(owner->ExitEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
ObDereferenceObject(&owner->Header);
|
||||||
thread->Owner = (PROCESS *)0;
|
thread->Owner = (PROCESS *)0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -104,9 +113,11 @@ __declspec(dllexport) KSTATUS KeCreateThread(PROCESS* owner, uintptr_t entry, ui
|
|||||||
return STATUS_NO_MEMORY;
|
return STATUS_NO_MEMORY;
|
||||||
}
|
}
|
||||||
thread->UserStackBase = ustack;
|
thread->UserStackBase = ustack;
|
||||||
uintptr_t userStackTop = (uintptr_t)ustack + USTACK_SIZE - 8; // i don't know why, i don't want to know why
|
// Ring-3 starts at the image entry via IRET, not CALL, so there is no
|
||||||
// i shouldn't have to wonder why but the stack is off by 8 bytes unless we do this terribleness
|
// pushed return address. The x64 ABI expects RSP % 16 == 8 at function
|
||||||
// this magic number will hunt me in my dreams
|
// entry; bias the initial stack as if a call had happened so compiler
|
||||||
|
// generated aligned SSE spills (movaps) are safe.
|
||||||
|
uintptr_t userStackTop = (uintptr_t)ustack + USTACK_SIZE - 8;
|
||||||
|
|
||||||
SYS_TEB* teb = (SYS_TEB*)MmAllocateUserMemory(sizeof(SYS_TEB));
|
SYS_TEB* teb = (SYS_TEB*)MmAllocateUserMemory(sizeof(SYS_TEB));
|
||||||
if (!teb) {
|
if (!teb) {
|
||||||
|
|||||||
@@ -4,25 +4,24 @@
|
|||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
#include <sys_string.h>
|
||||||
|
|
||||||
#define SYS_PEB_MAGIC 0x30424550u /* "PEB0" */
|
#define SYS_PEB_MAGIC 0x30424550u /* "PEB0" */
|
||||||
#define SYS_TEB_MAGIC 0x30424554u /* "TEB0" */
|
#define SYS_TEB_MAGIC 0x30424554u /* "TEB0" */
|
||||||
#define SYS_ENV_VERSION 1u
|
#define SYS_ENV_VERSION 1u
|
||||||
#define SYS_CWD_MAX 520u
|
#define SYS_CWD_MAX 520u
|
||||||
|
|
||||||
|
#ifndef SYS_USER_ENV_TYPES_DEFINED
|
||||||
|
#define SYS_USER_ENV_TYPES_DEFINED 1
|
||||||
|
|
||||||
typedef struct SYS_PEB {
|
typedef struct SYS_PEB {
|
||||||
uint32_t Magic;
|
uint32_t Magic;
|
||||||
uint32_t Version;
|
uint32_t Version;
|
||||||
uint32_t ProcessId;
|
uint32_t ProcessId;
|
||||||
|
|
||||||
const char* ImagePath;
|
SYS_STRING_VIEW ImagePath;
|
||||||
size_t ImagePathLength;
|
SYS_STRING_VIEW CommandLine;
|
||||||
|
SYS_STRING_VIEW CurrentDirectory;
|
||||||
const char* CommandLine;
|
|
||||||
size_t CommandLineLength;
|
|
||||||
|
|
||||||
const char* CurrentDirectory;
|
|
||||||
size_t CurrentDirectoryLength;
|
|
||||||
} SYS_PEB;
|
} SYS_PEB;
|
||||||
|
|
||||||
typedef struct SYS_TEB {
|
typedef struct SYS_TEB {
|
||||||
@@ -32,4 +31,11 @@ typedef struct SYS_TEB {
|
|||||||
struct SYS_TEB* Self;
|
struct SYS_TEB* Self;
|
||||||
uint32_t ThreadId;
|
uint32_t ThreadId;
|
||||||
uint32_t Magic;
|
uint32_t Magic;
|
||||||
|
|
||||||
|
// Native per-thread extension pointer. The kernel leaves this NULL;
|
||||||
|
// user-mode personalities/libraries may hang their own thread-local
|
||||||
|
// state here without baking subsystem semantics into SYS_TEB.
|
||||||
|
void* SubsystemData;
|
||||||
} SYS_TEB;
|
} SYS_TEB;
|
||||||
|
|
||||||
|
#endif // SYS_USER_ENV_TYPES_DEFINED
|
||||||
+9
-6
@@ -188,7 +188,7 @@ static void KiInitializeDrivers(BOOT_INFO* bootInfo, const HAL_API* hal) {
|
|||||||
KSTATUS s;
|
KSTATUS s;
|
||||||
FILE_OBJECT* f;
|
FILE_OBJECT* f;
|
||||||
|
|
||||||
s = IoOpenFile("drivers\\drivers.ini", &f);
|
s = IoOpenFile(SYS_SV_LITERAL("drivers\\drivers.ini"), &f);
|
||||||
if (s != STATUS_SUCCESS) {
|
if (s != STATUS_SUCCESS) {
|
||||||
KePanic("KiInitializeDrivers: Failed to open drivers.ini (status %d)\n", s);
|
KePanic("KiInitializeDrivers: Failed to open drivers.ini (status %d)\n", s);
|
||||||
}
|
}
|
||||||
@@ -222,7 +222,7 @@ static void KiInitializeDrivers(BOOT_INFO* bootInfo, const HAL_API* hal) {
|
|||||||
memcpy(path, "drivers\\", 8);
|
memcpy(path, "drivers\\", 8);
|
||||||
memcpy(path + 8, runtimeKeys[i], len + 1);
|
memcpy(path + 8, runtimeKeys[i], len + 1);
|
||||||
|
|
||||||
s = IoOpenFile(path, &f);
|
s = IoOpenFile(SysSvFromCstr(path), &f);
|
||||||
if (s != STATUS_SUCCESS) {
|
if (s != STATUS_SUCCESS) {
|
||||||
DbgPrint("KiInitializeDrivers: Failed to open runtime driver: %s (%s)\n", runtimeKeys[i], path);
|
DbgPrint("KiInitializeDrivers: Failed to open runtime driver: %s (%s)\n", runtimeKeys[i], path);
|
||||||
continue; // Skip this driver but keep going with others
|
continue; // Skip this driver but keep going with others
|
||||||
@@ -285,7 +285,7 @@ static PROCESS* KiLaunchUserProcess(const char* filePath, const char* args) {
|
|||||||
FILE_OBJECT* f;
|
FILE_OBJECT* f;
|
||||||
|
|
||||||
// Read the executable file into a raw buffer.
|
// Read the executable file into a raw buffer.
|
||||||
s = IoOpenFile(filePath, &f);
|
s = IoOpenFile(SysSvFromCstr(filePath), &f);
|
||||||
DbgPrint("KiLaunchUserProcess: IoOpenFile: %d\n", s);
|
DbgPrint("KiLaunchUserProcess: IoOpenFile: %d\n", s);
|
||||||
if (s != STATUS_SUCCESS) {
|
if (s != STATUS_SUCCESS) {
|
||||||
DbgPrint("Failed to load file: \"%s\"!\n", filePath);
|
DbgPrint("Failed to load file: \"%s\"!\n", filePath);
|
||||||
@@ -323,7 +323,10 @@ static PROCESS* KiLaunchUserProcess(const char* filePath, const char* args) {
|
|||||||
}
|
}
|
||||||
DbgPrint("Created process (PID %u) with command line: %s\n", proc->ProcessId, (args && args[0]) ? args : filePath);
|
DbgPrint("Created process (PID %u) with command line: %s\n", proc->ProcessId, (args && args[0]) ? args : filePath);
|
||||||
|
|
||||||
s = KeSetupUserProcessEnvironment(proc, filePath, args, proc->CurrentDirectory);
|
s = KeSetupUserProcessEnvironment(proc,
|
||||||
|
SysSvFromCstr(filePath),
|
||||||
|
SysSvFromCstr(args),
|
||||||
|
SysSvFromCstr(proc->CurrentDirectory));
|
||||||
if (!KSUCCESS(s)) {
|
if (!KSUCCESS(s)) {
|
||||||
DbgPrint("KiLaunchUserProcess: Failed to build user process environment. Status: %d\n", s);
|
DbgPrint("KiLaunchUserProcess: Failed to build user process environment. Status: %d\n", s);
|
||||||
MmFreePool(mem);
|
MmFreePool(mem);
|
||||||
@@ -344,7 +347,7 @@ static PROCESS* KiLaunchUserProcess(const char* filePath, const char* args) {
|
|||||||
s = LdrResolveImportsForProcess(proc, imageBase);
|
s = LdrResolveImportsForProcess(proc, imageBase);
|
||||||
if (!KSUCCESS(s)) {
|
if (!KSUCCESS(s)) {
|
||||||
DbgPrint("KiLaunchUserProcess: Failed to resolve EXE imports for %s (status %d)\n", filePath, s);
|
DbgPrint("KiLaunchUserProcess: Failed to resolve EXE imports for %s (status %d)\n", filePath, s);
|
||||||
MmFreeUserMemory((void*)imageBase);
|
MmFreeUserImage((void*)imageBase, LdrGetImageSize(imageBase));
|
||||||
ObDereferenceObject(&proc->Header);
|
ObDereferenceObject(&proc->Header);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -361,7 +364,7 @@ static PROCESS* KiLaunchUserProcess(const char* filePath, const char* args) {
|
|||||||
DbgPrint("KiLaunchUserProcess: Failed to create thread. Status: %d\n", s);
|
DbgPrint("KiLaunchUserProcess: Failed to create thread. Status: %d\n", s);
|
||||||
proc->ImageBase = 0;
|
proc->ImageBase = 0;
|
||||||
proc->ImageSize = 0;
|
proc->ImageSize = 0;
|
||||||
MmFreeUserMemory((void*)imageBase);
|
MmFreeUserImage((void*)imageBase, LdrGetImageSize(imageBase));
|
||||||
ObDereferenceObject(&proc->Header);
|
ObDereferenceObject(&proc->Header);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
+52
-6
@@ -107,6 +107,10 @@ typedef struct {
|
|||||||
#pragma pack(pop)
|
#pragma pack(pop)
|
||||||
|
|
||||||
#define IMAGE_REL_BASED_DIR64 10
|
#define IMAGE_REL_BASED_DIR64 10
|
||||||
|
#define IMAGE_SCN_CNT_CODE 0x00000020u
|
||||||
|
#define IMAGE_SCN_MEM_EXECUTE 0x20000000u
|
||||||
|
#define IMAGE_SCN_MEM_READ 0x40000000u
|
||||||
|
#define IMAGE_SCN_MEM_WRITE 0x80000000u
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Module registry
|
// Module registry
|
||||||
@@ -248,6 +252,40 @@ size_t LdrGetImageSize(uintptr_t base) {
|
|||||||
return (size_t)pe->OptionalHeader.SizeOfImage;
|
return (size_t)pe->OptionalHeader.SizeOfImage;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void KiFreeLoadedImage(uintptr_t imageBase, bool user) {
|
||||||
|
if (!imageBase) return;
|
||||||
|
size_t imageSize = LdrGetImageSize(imageBase);
|
||||||
|
if (user) MmFreeUserImage((void*)imageBase, imageSize);
|
||||||
|
else MmFreePool((void*)imageBase);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
KSTATUS LdrProtectImage(uintptr_t imageBase, bool user) {
|
||||||
|
if (!imageBase) return STATUS_INVALID_PARAMETER;
|
||||||
|
|
||||||
|
uint32_t peOffset = *(uint32_t *)(imageBase + 0x3C);
|
||||||
|
PE_HEADER* pe = (PE_HEADER *)(imageBase + peOffset);
|
||||||
|
if (pe->Signature != PE_SIGNATURE) return STATUS_FAILURE;
|
||||||
|
|
||||||
|
SECTION_HEADER* sec = (SECTION_HEADER *)((uint8_t *)&pe->OptionalHeader + pe->SizeOfOptionalHeader);
|
||||||
|
for (int i = 0; i < pe->NumberOfSections; i++) {
|
||||||
|
size_t protectSize = sec[i].VirtualSize ? sec[i].VirtualSize : sec[i].SizeOfRawData;
|
||||||
|
if (!protectSize) continue;
|
||||||
|
|
||||||
|
uint64_t prot = PAGE_PRESENT | (user ? PAGE_USER : 0);
|
||||||
|
if (sec[i].Characteristics & IMAGE_SCN_MEM_WRITE)
|
||||||
|
prot |= PAGE_WRITE;
|
||||||
|
if (!((sec[i].Characteristics & IMAGE_SCN_MEM_EXECUTE) ||
|
||||||
|
(sec[i].Characteristics & IMAGE_SCN_CNT_CODE)))
|
||||||
|
prot |= PAGE_NX;
|
||||||
|
|
||||||
|
KSTATUS s = MmSetPageProtect(imageBase + sec[i].VirtualAddress, protectSize, prot);
|
||||||
|
if (!KSUCCESS(s)) return s;
|
||||||
|
}
|
||||||
|
return STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Entry point lookup
|
// Entry point lookup
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
@@ -470,13 +508,13 @@ KSTATUS LdrLoadModuleForProcess(PROCESS* process, const char* filename, uintptr_
|
|||||||
}
|
}
|
||||||
|
|
||||||
FILE_OBJECT* f;
|
FILE_OBJECT* f;
|
||||||
KSTATUS s = IoOpenFile(filename, &f);
|
KSTATUS s = IoOpenFile(SysSvFromCstr(filename), &f);
|
||||||
if (!KSUCCESS(s)) {
|
if (!KSUCCESS(s)) {
|
||||||
char path[260];
|
char path[260];
|
||||||
memcpy(path, "0:/system/", 10);
|
memcpy(path, "0:/system/", 10);
|
||||||
memcpy(path + 10, filename, strnlen(filename, 248) + 1);
|
memcpy(path + 10, filename, strnlen(filename, 248) + 1);
|
||||||
path[259] = '\0';
|
path[259] = '\0';
|
||||||
s = IoOpenFile(path, &f);
|
s = IoOpenFile(SysSvFromCstr(path), &f);
|
||||||
if (!KSUCCESS(s)) {
|
if (!KSUCCESS(s)) {
|
||||||
DbgPrint("LdrLoadModuleForProcess: Failed to open %s (%d)\n", filename, s);
|
DbgPrint("LdrLoadModuleForProcess: Failed to open %s (%d)\n", filename, s);
|
||||||
return s;
|
return s;
|
||||||
@@ -509,7 +547,13 @@ KSTATUS LdrLoadModuleForProcess(PROCESS* process, const char* filename, uintptr_
|
|||||||
|
|
||||||
s = LdrResolveImportsForProcess(process, imageBase);
|
s = LdrResolveImportsForProcess(process, imageBase);
|
||||||
if (!KSUCCESS(s)) {
|
if (!KSUCCESS(s)) {
|
||||||
MmFreeUserMemory((void*)imageBase);
|
KiFreeLoadedImage(imageBase, true);
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
s = LdrProtectImage(imageBase, true);
|
||||||
|
if (!KSUCCESS(s)) {
|
||||||
|
KiFreeLoadedImage(imageBase, true);
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -517,7 +561,7 @@ KSTATUS LdrLoadModuleForProcess(PROCESS* process, const char* filename, uintptr_
|
|||||||
|
|
||||||
s = LdrRegisterProcessModule(process, filename, imageBase);
|
s = LdrRegisterProcessModule(process, filename, imageBase);
|
||||||
if (!KSUCCESS(s)) {
|
if (!KSUCCESS(s)) {
|
||||||
MmFreeUserMemory((void*)imageBase);
|
KiFreeLoadedImage(imageBase, true);
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -561,7 +605,7 @@ KSTATUS LdrLoadImage(const void* buffer, size_t size, uintptr_t* baseOut, bool u
|
|||||||
OPT_HEADER64* opt = &pe->OptionalHeader;
|
OPT_HEADER64* opt = &pe->OptionalHeader;
|
||||||
size_t imageSize = opt->SizeOfImage;
|
size_t imageSize = opt->SizeOfImage;
|
||||||
|
|
||||||
void* mem = user ? MmAllocateUserMemory(imageSize) : MmAllocatePool(imageSize);
|
void* mem = user ? MmAllocateUserImage(imageSize) : MmAllocatePool(imageSize);
|
||||||
if (!mem) return STATUS_NO_MEMORY;
|
if (!mem) return STATUS_NO_MEMORY;
|
||||||
memset(mem, 0, imageSize);
|
memset(mem, 0, imageSize);
|
||||||
|
|
||||||
@@ -575,7 +619,9 @@ KSTATUS LdrLoadImage(const void* buffer, size_t size, uintptr_t* baseOut, bool u
|
|||||||
}
|
}
|
||||||
|
|
||||||
uint64_t prot = PAGE_PRESENT | PAGE_WRITE | (user ? PAGE_USER : 0);
|
uint64_t prot = PAGE_PRESENT | PAGE_WRITE | (user ? PAGE_USER : 0);
|
||||||
MmSetPageProtect((uintptr_t)mem + sec[i].VirtualAddress, sec[i].VirtualSize, prot);
|
MmSetPageProtect((uintptr_t)mem + sec[i].VirtualAddress,
|
||||||
|
sec[i].VirtualSize ? sec[i].VirtualSize : sec[i].SizeOfRawData,
|
||||||
|
prot);
|
||||||
}
|
}
|
||||||
|
|
||||||
uintptr_t delta = (uintptr_t)mem - opt->ImageBase;
|
uintptr_t delta = (uintptr_t)mem - opt->ImageBase;
|
||||||
|
|||||||
+41
-38
@@ -1,38 +1,41 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
// ldr_image.h - minimal PE32+ loader
|
// ldr_image.h - minimal PE32+ loader
|
||||||
// Loads an already-mapped PE image and resolves its imports.
|
// Loads an already-mapped PE image and resolves its imports.
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <ke_status.h>
|
#include <ke_status.h>
|
||||||
|
|
||||||
// Map a PE32+ image from 'buffer' into the kernel virtual address space.
|
// Map a PE32+ image from 'buffer' into the kernel virtual address space.
|
||||||
// On success, *baseOut receives the virtual base address.
|
// On success, *baseOut receives the virtual base address.
|
||||||
KSTATUS LdrLoadImage(const void* buffer, size_t size, uintptr_t* baseOut, bool user);
|
KSTATUS LdrLoadImage(const void* buffer, size_t size, uintptr_t* baseOut, bool user);
|
||||||
|
|
||||||
// Return the SizeOfImage recorded in the PE header for an already-mapped image.
|
// Return the SizeOfImage recorded in the PE header for an already-mapped image.
|
||||||
size_t LdrGetImageSize(uintptr_t base);
|
size_t LdrGetImageSize(uintptr_t base);
|
||||||
|
|
||||||
// Look up a named export in a loaded module.
|
// Look up a named export in a loaded module.
|
||||||
// 'moduleName' must match a name in BOOT_INFO.Modules (e.g., "hal.dll").
|
// 'moduleName' must match a name in BOOT_INFO.Modules (e.g., "hal.dll").
|
||||||
void* LdrGetExport(const char* moduleName, const char* functionName);
|
void* LdrGetExport(const char* moduleName, const char* functionName);
|
||||||
|
|
||||||
// Get the entry point address of a loaded image.
|
// Get the entry point address of a loaded image.
|
||||||
uintptr_t LdrGetEntryPoint(uintptr_t base);
|
uintptr_t LdrGetEntryPoint(uintptr_t base);
|
||||||
|
|
||||||
// Resolve the import table of an already-loaded image using the global registry.
|
// Resolve the import table of an already-loaded image using the global registry.
|
||||||
KSTATUS LdrResolveImports(uintptr_t imageBase);
|
KSTATUS LdrResolveImports(uintptr_t imageBase);
|
||||||
|
|
||||||
// Resolve imports using a process-local module registry.
|
// Resolve imports using a process-local module registry.
|
||||||
struct PROCESS;
|
struct PROCESS;
|
||||||
KSTATUS LdrResolveImportsForProcess(struct PROCESS* process, uintptr_t imageBase);
|
KSTATUS LdrResolveImportsForProcess(struct PROCESS* process, uintptr_t imageBase);
|
||||||
|
|
||||||
// Load a DLL for a specific process, resolve it against that process's
|
// Apply final page permissions for a loaded image after relocations/imports.
|
||||||
// loaded modules, and register it in the process-local registry.
|
KSTATUS LdrProtectImage(uintptr_t imageBase, bool user);
|
||||||
KSTATUS LdrLoadModuleForProcess(struct PROCESS* process, const char* filename, uintptr_t* baseOut);
|
|
||||||
|
// Load a DLL for a specific process, resolve it against that process's
|
||||||
// Called by KernelMain to register all modules from BOOT_INFO so that
|
// loaded modules, and register it in the process-local registry.
|
||||||
// LdrGetExport can find them.
|
KSTATUS LdrLoadModuleForProcess(struct PROCESS* process, const char* filename, uintptr_t* baseOut);
|
||||||
struct BOOT_INFO; // forward declare for the compiler to shut the fuck up
|
|
||||||
void LdrRegisterModulesFromBootInfo(const struct BOOT_INFO* bootInfo);
|
// Called by KernelMain to register all modules from BOOT_INFO so that
|
||||||
void LdrRegisterModule(const char* name, uintptr_t base);
|
// LdrGetExport can find them.
|
||||||
|
struct BOOT_INFO; // forward declare for the compiler to shut the fuck up
|
||||||
|
void LdrRegisterModulesFromBootInfo(const struct BOOT_INFO* bootInfo);
|
||||||
|
void LdrRegisterModule(const char* name, uintptr_t base);
|
||||||
+65
-24
@@ -290,29 +290,6 @@ __declspec(dllexport) KSTATUS CopyToUser(void* userDst, const void* kernelSrc, s
|
|||||||
return STATUS_SUCCESS;
|
return STATUS_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
__declspec(dllexport) KSTATUS CopyStringFromUser(char* kernelDst,
|
|
||||||
size_t kernelDstSize,
|
|
||||||
const char* userSrc) {
|
|
||||||
if (!kernelDst || kernelDstSize == 0 || !userSrc)
|
|
||||||
return STATUS_INVALID_PARAMETER;
|
|
||||||
|
|
||||||
for (size_t i = 0; i < kernelDstSize; i++) {
|
|
||||||
KSTATUS s = MmProbeUserRead(userSrc + i, 1);
|
|
||||||
if (!KSUCCESS(s)) {
|
|
||||||
kernelDst[0] = '\0';
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
char ch = userSrc[i];
|
|
||||||
kernelDst[i] = ch;
|
|
||||||
if (ch == '\0')
|
|
||||||
return STATUS_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
kernelDst[kernelDstSize - 1] = '\0';
|
|
||||||
return STATUS_BUFFER_TOO_SMALL;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Boundary-tag allocator (used for both pool and user heap)
|
// Boundary-tag allocator (used for both pool and user heap)
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
@@ -595,6 +572,9 @@ static HEAP gPoolHeap;
|
|||||||
// User heap
|
// User heap
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
static HEAP gUserHeap;
|
static HEAP gUserHeap;
|
||||||
|
__declspec(dllexport) void MmFreeUserImage(void* ptr, size_t size);
|
||||||
|
static SPINLOCK gUserImageLock = SPINLOCK_INIT;
|
||||||
|
static uintptr_t gUserImageNext = USER_LIMIT;
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Module init
|
// Module init
|
||||||
@@ -621,8 +601,15 @@ void MmInitializeVirtual(PHYSICAL_ADDRESS bootPml4Phys, size_t poolSize) {
|
|||||||
gUserHeap.Start = USER_BASE;
|
gUserHeap.Start = USER_BASE;
|
||||||
gUserHeap.Brk = USER_BASE;
|
gUserHeap.Brk = USER_BASE;
|
||||||
gUserHeap.Limit = USER_LIMIT;
|
gUserHeap.Limit = USER_LIMIT;
|
||||||
gUserHeap.MapFlags = PAGE_PRESENT | PAGE_WRITE | PAGE_USER;
|
gUserHeap.MapFlags = PAGE_PRESENT | PAGE_WRITE | PAGE_USER | PAGE_NX;
|
||||||
gUserHeap.Lock = (SPINLOCK)SPINLOCK_INIT;
|
gUserHeap.Lock = (SPINLOCK)SPINLOCK_INIT;
|
||||||
|
|
||||||
|
// Executable image mappings grow downward from the top of user space.
|
||||||
|
// The regular user heap grows upward from USER_BASE. Keeping them in
|
||||||
|
// separate page-aligned regions prevents W^X section protection from
|
||||||
|
// changing heap metadata or adjacent allocations.
|
||||||
|
gUserImageLock = (SPINLOCK)SPINLOCK_INIT;
|
||||||
|
gUserImageNext = USER_LIMIT;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MmActivate(void) {
|
void MmActivate(void) {
|
||||||
@@ -654,6 +641,60 @@ __declspec(dllexport) void* MmReallocateUserMemory(void* ptr, size_t newSize) {
|
|||||||
return KiHeapRealloc(&gUserHeap, ptr, newSize);
|
return KiHeapRealloc(&gUserHeap, ptr, newSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
__declspec(dllexport) void* MmAllocateUserImage(size_t size) {
|
||||||
|
KeAssertIrql(PASSIVE_LEVEL);
|
||||||
|
if (!size) return (void*)0;
|
||||||
|
|
||||||
|
size_t pagesSize = (size + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1);
|
||||||
|
if (pagesSize < size) return (void*)0;
|
||||||
|
|
||||||
|
uintptr_t flags = KeAcquireSpinlock(&gUserImageLock);
|
||||||
|
|
||||||
|
if (pagesSize > gUserImageNext || gUserImageNext - pagesSize < gUserHeap.Brk) {
|
||||||
|
KeReleaseSpinlock(&gUserImageLock, flags);
|
||||||
|
return (void*)0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uintptr_t base = gUserImageNext - pagesSize;
|
||||||
|
gUserImageNext = base;
|
||||||
|
KeReleaseSpinlock(&gUserImageLock, flags);
|
||||||
|
|
||||||
|
for (uintptr_t va = base; va < base + pagesSize; va += PAGE_SIZE) {
|
||||||
|
PHYSICAL_ADDRESS frame = MmAllocateFrame();
|
||||||
|
if (!frame) {
|
||||||
|
MmFreeUserImage((void*)base, va - base);
|
||||||
|
return (void*)0;
|
||||||
|
}
|
||||||
|
KSTATUS s = MmMapPage(va, frame, PAGE_PRESENT | PAGE_WRITE | PAGE_USER | PAGE_NX);
|
||||||
|
if (!KSUCCESS(s)) {
|
||||||
|
MmFreeFrame(frame);
|
||||||
|
MmFreeUserImage((void*)base, va - base);
|
||||||
|
return (void*)0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
memset((void*)base, 0, pagesSize);
|
||||||
|
return (void*)base;
|
||||||
|
}
|
||||||
|
|
||||||
|
__declspec(dllexport) void MmFreeUserImage(void* ptr, size_t size) {
|
||||||
|
KeAssertIrql(PASSIVE_LEVEL);
|
||||||
|
if (!ptr || !size) return;
|
||||||
|
|
||||||
|
uintptr_t base = (uintptr_t)ptr & PAGE_MASK;
|
||||||
|
size_t pagesSize = (size + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1);
|
||||||
|
if (pagesSize < size) return;
|
||||||
|
|
||||||
|
for (uintptr_t va = base; va < base + pagesSize; va += PAGE_SIZE) {
|
||||||
|
PHYSICAL_ADDRESS phys = MmGetPhysicalAddress(va);
|
||||||
|
if (!phys) continue;
|
||||||
|
if (KSUCCESS(MmUnmapPage(va))) {
|
||||||
|
MmFreeFrame(phys);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
size_t MmGetKernelPoolUsage(void) {
|
size_t MmGetKernelPoolUsage(void) {
|
||||||
// Returns bytes committed to the kernel pool
|
// Returns bytes committed to the kernel pool
|
||||||
return (size_t)(gPoolHeap.Brk - gPoolHeap.Start);
|
return (size_t)(gPoolHeap.Brk - gPoolHeap.Start);
|
||||||
|
|||||||
@@ -73,11 +73,6 @@ __declspec(dllexport) KSTATUS MmProbeUserWrite(void* ptr, size_t size);
|
|||||||
__declspec(dllexport) KSTATUS CopyFromUser(void* kernelDst, const void* userSrc, size_t size);
|
__declspec(dllexport) KSTATUS CopyFromUser(void* kernelDst, const void* userSrc, size_t size);
|
||||||
__declspec(dllexport) KSTATUS CopyToUser(void* userDst, const void* kernelSrc, size_t size);
|
__declspec(dllexport) KSTATUS CopyToUser(void* userDst, const void* kernelSrc, size_t size);
|
||||||
|
|
||||||
// Bounded NUL-terminated string import from user memory. Always NUL-terminates
|
|
||||||
// kernelDst when kernelDstSize is non-zero. Returns STATUS_BUFFER_TOO_SMALL if
|
|
||||||
// no NUL byte was found within kernelDstSize bytes.
|
|
||||||
__declspec(dllexport) KSTATUS CopyStringFromUser(char* kernelDst, size_t kernelDstSize, const char* userSrc);
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Kernel pool - ring 0 only, never PAGE_USER.
|
// Kernel pool - ring 0 only, never PAGE_USER.
|
||||||
// Best-fit free-list allocator, thread-safe via spinlock.
|
// Best-fit free-list allocator, thread-safe via spinlock.
|
||||||
@@ -88,11 +83,17 @@ __declspec(dllexport) void* MmReallocatePool(void* ptr, size_t newSize);
|
|||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// User heap - shared flat space, ring 3 accessible.
|
// User heap - shared flat space, ring 3 accessible.
|
||||||
// All allocations are PAGE_PRESENT | PAGE_WRITE | PAGE_USER.
|
// Allocations are writable user pages with NX set by default; PE image
|
||||||
|
// sections opt back into execution through the loader.
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
__declspec(dllexport) void* MmAllocateUserMemory(size_t size);
|
__declspec(dllexport) void* MmAllocateUserMemory(size_t size);
|
||||||
__declspec(dllexport) void MmFreeUserMemory(void* ptr);
|
__declspec(dllexport) void MmFreeUserMemory(void* ptr);
|
||||||
__declspec(dllexport) void* MmReallocateUserMemory(void* ptr, size_t newSize);
|
__declspec(dllexport) void* MmReallocateUserMemory(void* ptr, size_t newSize);
|
||||||
|
|
||||||
|
// Page-aligned user image mapping. PE section protection is page based, so
|
||||||
|
// executable images must not be allocated from the normal sub-page heap.
|
||||||
|
__declspec(dllexport) void* MmAllocateUserImage(size_t size);
|
||||||
|
__declspec(dllexport) void MmFreeUserImage(void* ptr, size_t size);
|
||||||
|
|
||||||
size_t MmGetKernelPoolUsage(void);
|
size_t MmGetKernelPoolUsage(void);
|
||||||
size_t MmGetUserHeapUsage(void);
|
size_t MmGetUserHeapUsage(void);
|
||||||
+5
-5
@@ -12,11 +12,12 @@
|
|||||||
#define PING_PACKET_BYTES (8u + PING_PAYLOAD_BYTES)
|
#define PING_PACKET_BYTES (8u + PING_PAYLOAD_BYTES)
|
||||||
|
|
||||||
static void print(const char* s) {
|
static void print(const char* s) {
|
||||||
if (s) SysWriteConsole(s, strlen(s));
|
if (s) { SYS_WRITE_CONSOLE_REQUEST req = { SysSvFromCstr(s) }; SysWriteConsole(&req); }
|
||||||
}
|
}
|
||||||
|
|
||||||
static void print_char(char c) {
|
static void print_char(char c) {
|
||||||
SysWriteConsole(&c, 1);
|
SYS_WRITE_CONSOLE_REQUEST req = { SysSvFromParts(&c, 1) };
|
||||||
|
SysWriteConsole(&req);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void print_u64(uint64_t v) {
|
static void print_u64(uint64_t v) {
|
||||||
@@ -126,8 +127,7 @@ static uint32_t resolve_host(const char* host, uint8_t ip[4]) {
|
|||||||
SYS_RESOLVE_HOST_REQUEST req;
|
SYS_RESOLVE_HOST_REQUEST req;
|
||||||
memset(&resolved, 0, sizeof(resolved));
|
memset(&resolved, 0, sizeof(resolved));
|
||||||
memset(&req, 0, sizeof(req));
|
memset(&req, 0, sizeof(req));
|
||||||
req.Name = host;
|
req.Name = SysSvFromCstr(host);
|
||||||
req.NameLength = strlen(host);
|
|
||||||
req.Family = SYS_AF_INET;
|
req.Family = SYS_AF_INET;
|
||||||
req.AddressOut = &resolved;
|
req.AddressOut = &resolved;
|
||||||
req.AddressSizeInOut = &resolvedSize;
|
req.AddressSizeInOut = &resolvedSize;
|
||||||
@@ -218,7 +218,7 @@ int main(void) {
|
|||||||
uint32_t count = PING_DEFAULT_COUNT;
|
uint32_t count = PING_DEFAULT_COUNT;
|
||||||
strcpy(host, PING_DEFAULT_TARGET);
|
strcpy(host, PING_DEFAULT_TARGET);
|
||||||
|
|
||||||
const char* cmd = SysGetPeb()->CommandLine;
|
const char* cmd = SysGetPeb()->CommandLine.Data;
|
||||||
cmd = next_token(cmd, tok, sizeof(tok)); // program path
|
cmd = next_token(cmd, tok, sizeof(tok)); // program path
|
||||||
if (cmd) {
|
if (cmd) {
|
||||||
const char* rest = next_token(cmd, tok, sizeof(tok));
|
const char* rest = next_token(cmd, tok, sizeof(tok));
|
||||||
|
|||||||
@@ -18,7 +18,8 @@ typedef struct {
|
|||||||
__declspec(dllexport) int chdir(const char* path) {
|
__declspec(dllexport) int chdir(const char* path) {
|
||||||
char native[512];
|
char native[512];
|
||||||
PosixTranslatePath(path, native, sizeof(native));
|
PosixTranslatePath(path, native, sizeof(native));
|
||||||
uint32_t s = SysSetCwd(native);
|
SYS_PATH_REQUEST req = { SysSvFromCstr(native) };
|
||||||
|
uint32_t s = SysSetCwd(&req);
|
||||||
if (s != 0) { errno = ENOENT; return -1; }
|
if (s != 0) { errno = ENOENT; return -1; }
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -26,7 +27,10 @@ __declspec(dllexport) int chdir(const char* path) {
|
|||||||
__declspec(dllexport) char* getcwd(char* buf, size_t size) {
|
__declspec(dllexport) char* getcwd(char* buf, size_t size) {
|
||||||
if (!buf || size == 0) { errno = EINVAL; return (char*)0; }
|
if (!buf || size == 0) { errno = EINVAL; return (char*)0; }
|
||||||
char native[512];
|
char native[512];
|
||||||
uint32_t s = SysGetCwd(native, sizeof(native));
|
size_t nativeLen = 0;
|
||||||
|
SYS_GET_CWD_REQUEST req = { native, sizeof(native) - 1, &nativeLen };
|
||||||
|
uint32_t s = SysGetCwd(&req);
|
||||||
|
if (s == 0) native[nativeLen] = '\0';
|
||||||
if (s != 0) { errno = ERANGE; return (char*)0; }
|
if (s != 0) { errno = ERANGE; return (char*)0; }
|
||||||
NativeToPosix(native, buf, size);
|
NativeToPosix(native, buf, size);
|
||||||
return buf;
|
return buf;
|
||||||
@@ -36,7 +40,8 @@ __declspec(dllexport) DIR* opendir(const char* path) {
|
|||||||
char native[512];
|
char native[512];
|
||||||
PosixTranslatePath(path, native, sizeof(native));
|
PosixTranslatePath(path, native, sizeof(native));
|
||||||
uint32_t kh;
|
uint32_t kh;
|
||||||
uint32_t s = SysOpenDir(native, &kh);
|
SYS_PATH_HANDLE_REQUEST req = { SysSvFromCstr(native), &kh };
|
||||||
|
uint32_t s = SysOpenDir(&req);
|
||||||
if (s != 0) { errno = ENOENT; return (DIR*)0; }
|
if (s != 0) { errno = ENOENT; return (DIR*)0; }
|
||||||
|
|
||||||
// Allocate DIR on the kernel heap (SysAlloc) so it outlives the stack.
|
// Allocate DIR on the kernel heap (SysAlloc) so it outlives the stack.
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
// posix_fd.c - file descriptor table
|
// posix_fd.c - file descriptor table
|
||||||
|
#include <syscall.h>
|
||||||
#include "posix.h"
|
#include "posix.h"
|
||||||
#include "posix_fd.h"
|
#include "posix_fd.h"
|
||||||
#include <syscall.h>
|
|
||||||
|
|
||||||
int errno = 0;
|
int errno = 0;
|
||||||
|
|
||||||
|
|||||||
@@ -33,11 +33,12 @@ __declspec(dllexport) int open(const char* path, int flags, ...) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
uint32_t kh = 0;
|
uint32_t kh = 0;
|
||||||
|
SYS_PATH_HANDLE_REQUEST req = { SysSvFromCstr(p), &kh };
|
||||||
uint32_t s;
|
uint32_t s;
|
||||||
if (flags & O_WRONLY) {
|
if (flags & O_WRONLY) {
|
||||||
s = SysCreateFile(p, &kh);
|
s = SysCreateFile(&req);
|
||||||
} else {
|
} else {
|
||||||
s = SysOpenFile(p, &kh);
|
s = SysOpenFile(&req);
|
||||||
}
|
}
|
||||||
if (s != 0) { errno = PosixStatusToErrno(s); return -1; }
|
if (s != 0) { errno = PosixStatusToErrno(s); return -1; }
|
||||||
|
|
||||||
@@ -98,9 +99,11 @@ __declspec(dllexport) ssize_t write(int fd, const void* buf, size_t count) {
|
|||||||
if (!e) return -1;
|
if (!e) return -1;
|
||||||
|
|
||||||
switch (e->Type) {
|
switch (e->Type) {
|
||||||
case FD_CONSOLE_W:
|
case FD_CONSOLE_W: {
|
||||||
SysWriteConsole((const char*)buf, count);
|
SYS_WRITE_CONSOLE_REQUEST req = { SysSvFromParts((const char*)buf, count) };
|
||||||
|
SysWriteConsole(&req);
|
||||||
return (ssize_t)count;
|
return (ssize_t)count;
|
||||||
|
}
|
||||||
case FD_DEVNULL:
|
case FD_DEVNULL:
|
||||||
case FD_DEVZERO:
|
case FD_DEVZERO:
|
||||||
return (ssize_t)count;
|
return (ssize_t)count;
|
||||||
@@ -143,7 +146,8 @@ __declspec(dllexport) int unlink(const char* path) {
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t s = SysDeleteFile(p);
|
SYS_PATH_REQUEST req = { SysSvFromCstr(p) };
|
||||||
|
uint32_t s = SysDeleteFile(&req);
|
||||||
if (s != 0) { errno = PosixStatusToErrno(s); return -1; }
|
if (s != 0) { errno = PosixStatusToErrno(s); return -1; }
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,8 +15,8 @@ static SYS_PEB* PosixPeb(void) {
|
|||||||
|
|
||||||
__declspec(dllexport) const char* posix_get_command_line(void) {
|
__declspec(dllexport) const char* posix_get_command_line(void) {
|
||||||
SYS_PEB* peb = PosixPeb();
|
SYS_PEB* peb = PosixPeb();
|
||||||
if (!peb || peb->Magic != SYS_PEB_MAGIC || !peb->CommandLine) return "";
|
if (!peb || peb->Magic != SYS_PEB_MAGIC || !peb->CommandLine.Data) return "";
|
||||||
return peb->CommandLine;
|
return peb->CommandLine.Data;
|
||||||
}
|
}
|
||||||
|
|
||||||
// pid -> proc handle table (user-mode side, for waitpid)
|
// pid -> proc handle table (user-mode side, for waitpid)
|
||||||
@@ -104,7 +104,8 @@ __declspec(dllexport) int posix_spawn(pid_t* pidOut,
|
|||||||
}
|
}
|
||||||
|
|
||||||
uint32_t procHandle;
|
uint32_t procHandle;
|
||||||
uint32_t s = SysCreateProcess(native, &procHandle, cmdline);
|
SYS_CREATE_PROCESS_REQUEST req = { SysSvFromCstr(native), SysSvFromCstr(cmdline), &procHandle };
|
||||||
|
uint32_t s = SysCreateProcess(&req);
|
||||||
if (s != 0) { errno = ENOENT; return -1; }
|
if (s != 0) { errno = ENOENT; return -1; }
|
||||||
|
|
||||||
uint32_t kpid = 0;
|
uint32_t kpid = 0;
|
||||||
|
|||||||
@@ -392,8 +392,7 @@ static int ResolveNameIpv4(const char* name, SYS_SOCKET_ADDRESS_IPV4* out) {
|
|||||||
size_t size = sizeof(*out);
|
size_t size = sizeof(*out);
|
||||||
SYS_RESOLVE_HOST_REQUEST req;
|
SYS_RESOLVE_HOST_REQUEST req;
|
||||||
memset(&req, 0, sizeof(req));
|
memset(&req, 0, sizeof(req));
|
||||||
req.Name = name;
|
req.Name = SysSvFromCstr(name);
|
||||||
req.NameLength = strlen(name);
|
|
||||||
req.Family = SYS_AF_INET;
|
req.Family = SYS_AF_INET;
|
||||||
req.AddressOut = out;
|
req.AddressOut = out;
|
||||||
req.AddressSizeInOut = &size;
|
req.AddressSizeInOut = &size;
|
||||||
|
|||||||
+952
-920
File diff suppressed because it is too large.
Load diff
@@ -4,11 +4,12 @@
|
|||||||
#include <syscall.h>
|
#include <syscall.h>
|
||||||
|
|
||||||
static void print(const char* s) {
|
static void print(const char* s) {
|
||||||
if (s) SysWriteConsole(s, strlen(s));
|
if (s) { SYS_WRITE_CONSOLE_REQUEST req = { SysSvFromCstr(s) }; SysWriteConsole(&req); }
|
||||||
}
|
}
|
||||||
|
|
||||||
static void print_char(char c) {
|
static void print_char(char c) {
|
||||||
SysWriteConsole(&c, 1);
|
SYS_WRITE_CONSOLE_REQUEST req = { SysSvFromParts(&c, 1) };
|
||||||
|
SysWriteConsole(&req);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void print_u64(uint64_t v) {
|
static void print_u64(uint64_t v) {
|
||||||
@@ -43,7 +44,7 @@ static void print_ipv4(const uint8_t ip[4]) {
|
|||||||
|
|
||||||
int main(void) {
|
int main(void) {
|
||||||
char name[256];
|
char name[256];
|
||||||
const char* cmd = SysGetPeb()->CommandLine;
|
const char* cmd = SysGetPeb()->CommandLine.Data;
|
||||||
cmd = next_token(cmd, name, sizeof(name)); // program path
|
cmd = next_token(cmd, name, sizeof(name)); // program path
|
||||||
(void)cmd;
|
(void)cmd;
|
||||||
|
|
||||||
@@ -60,8 +61,7 @@ int main(void) {
|
|||||||
SYS_RESOLVE_HOST_REQUEST req;
|
SYS_RESOLVE_HOST_REQUEST req;
|
||||||
memset(&addr, 0, sizeof(addr));
|
memset(&addr, 0, sizeof(addr));
|
||||||
memset(&req, 0, sizeof(req));
|
memset(&req, 0, sizeof(req));
|
||||||
req.Name = name;
|
req.Name = SysSvFromCstr(name);
|
||||||
req.NameLength = strlen(name);
|
|
||||||
req.Family = SYS_AF_INET;
|
req.Family = SYS_AF_INET;
|
||||||
req.AddressOut = &addr;
|
req.AddressOut = &addr;
|
||||||
req.AddressSizeInOut = &addrSize;
|
req.AddressSizeInOut = &addrSize;
|
||||||
|
|||||||
@@ -10,7 +10,37 @@
|
|||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Output helpers
|
// Output helpers
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
static void print(const char* s) { SysWriteConsole(s, strlen(s)); }
|
static void print(const char* s) { SYS_WRITE_CONSOLE_REQUEST req = { SysSvFromCstr(s) }; SysWriteConsole(&req); }
|
||||||
|
|
||||||
|
static uint32_t open_file(const char* path, uint32_t* handleOut) {
|
||||||
|
SYS_PATH_HANDLE_REQUEST req = { SysSvFromCstr(path), handleOut };
|
||||||
|
return SysOpenFile(&req);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t create_file(const char* path, uint32_t* handleOut) {
|
||||||
|
SYS_PATH_HANDLE_REQUEST req = { SysSvFromCstr(path), handleOut };
|
||||||
|
return SysCreateFile(&req);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t open_dir(const char* path, uint32_t* handleOut) {
|
||||||
|
SYS_PATH_HANDLE_REQUEST req = { SysSvFromCstr(path), handleOut };
|
||||||
|
return SysOpenDir(&req);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t set_cwd(const char* path) {
|
||||||
|
SYS_PATH_REQUEST req = { SysSvFromCstr(path) };
|
||||||
|
return SysSetCwd(&req);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t delete_file(const char* path) {
|
||||||
|
SYS_PATH_REQUEST req = { SysSvFromCstr(path) };
|
||||||
|
return SysDeleteFile(&req);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t create_process(const char* path, uint32_t* handleOut, const char* cmdline) {
|
||||||
|
SYS_CREATE_PROCESS_REQUEST req = { SysSvFromCstr(path), SysSvFromCstr(cmdline), handleOut };
|
||||||
|
return SysCreateProcess(&req);
|
||||||
|
}
|
||||||
|
|
||||||
static void printUInt(uint32_t n) {
|
static void printUInt(uint32_t n) {
|
||||||
char buf[12]; int i = 0;
|
char buf[12]; int i = 0;
|
||||||
@@ -101,7 +131,7 @@ static void cmd_write(const char* args) {
|
|||||||
filename[i] = '\0';
|
filename[i] = '\0';
|
||||||
if (args[i] == '\0' || args[i+1] == '\0') { print("write: usage: write <filename> <text>\n"); return; }
|
if (args[i] == '\0' || args[i+1] == '\0') { print("write: usage: write <filename> <text>\n"); return; }
|
||||||
uint32_t hFile;
|
uint32_t hFile;
|
||||||
if (SysCreateFile(filename, &hFile) != 0) { print("write: could not create file\n"); return; }
|
if (create_file(filename, &hFile) != 0) { print("write: could not create file\n"); return; }
|
||||||
size_t written;
|
size_t written;
|
||||||
SysWriteFile(hFile, args + i + 1, strlen(args + i + 1), &written);
|
SysWriteFile(hFile, args + i + 1, strlen(args + i + 1), &written);
|
||||||
SysCloseHandle(hFile);
|
SysCloseHandle(hFile);
|
||||||
@@ -112,7 +142,7 @@ static void cmd_dir(const char* path) {
|
|||||||
uint32_t hDir;
|
uint32_t hDir;
|
||||||
const char* target = (path && *path != '\0') ? path : ".";
|
const char* target = (path && *path != '\0') ? path : ".";
|
||||||
|
|
||||||
if (SysOpenDir(target, &hDir) != 0) {
|
if (open_dir(target, &hDir) != 0) {
|
||||||
print("dir: failed to open directory\n");
|
print("dir: failed to open directory\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -142,7 +172,7 @@ static void cmd_dir(const char* path) {
|
|||||||
|
|
||||||
static void cmd_type(const char* filename) {
|
static void cmd_type(const char* filename) {
|
||||||
uint32_t hFile;
|
uint32_t hFile;
|
||||||
if (SysOpenFile(filename, &hFile) != 0) { print("type: cannot open file\n"); return; }
|
if (open_file(filename, &hFile) != 0) { print("type: cannot open file\n"); return; }
|
||||||
char buffer[512]; size_t bytesRead;
|
char buffer[512]; size_t bytesRead;
|
||||||
while (SysReadFile(hFile, buffer, sizeof(buffer) - 1, &bytesRead) == 0 && bytesRead > 0) {
|
while (SysReadFile(hFile, buffer, sizeof(buffer) - 1, &bytesRead) == 0 && bytesRead > 0) {
|
||||||
buffer[bytesRead] = '\0'; print(buffer);
|
buffer[bytesRead] = '\0'; print(buffer);
|
||||||
@@ -183,8 +213,8 @@ static int runCommand(const char* cmd) {
|
|||||||
if (strcmp(cmd, "mem") == 0) { cmd_mem(); return 0; }
|
if (strcmp(cmd, "mem") == 0) { cmd_mem(); return 0; }
|
||||||
if (strcmp(cmd, "dir") == 0) { cmd_dir("."); return 0; }
|
if (strcmp(cmd, "dir") == 0) { cmd_dir("."); return 0; }
|
||||||
if (strncmp(cmd, "dir ", 4) == 0) { cmd_dir(cmd + 4); return 0; }
|
if (strncmp(cmd, "dir ", 4) == 0) { cmd_dir(cmd + 4); return 0; }
|
||||||
if (strncmp(cmd, "cd ", 3) == 0) { if (SysSetCwd(cmd + 3) != 0) print("cd: failed\n"); return 0; }
|
if (strncmp(cmd, "cd ", 3) == 0) { if (set_cwd(cmd + 3) != 0) print("cd: failed\n"); return 0; }
|
||||||
if (strncmp(cmd, "del ", 4) == 0) { SysDeleteFile(cmd + 4); return 0; }
|
if (strncmp(cmd, "del ", 4) == 0) { delete_file(cmd + 4); return 0; }
|
||||||
if (strncmp(cmd, "type ", 5) == 0) { cmd_type(cmd + 5); return 0; }
|
if (strncmp(cmd, "type ", 5) == 0) { cmd_type(cmd + 5); return 0; }
|
||||||
if (strncmp(cmd, "write ", 6) == 0) { cmd_write(cmd + 6); return 0; }
|
if (strncmp(cmd, "write ", 6) == 0) { cmd_write(cmd + 6); return 0; }
|
||||||
|
|
||||||
@@ -192,7 +222,7 @@ static int runCommand(const char* cmd) {
|
|||||||
if (!extractProgramPath(cmd, program, sizeof(program))) return 0;
|
if (!extractProgramPath(cmd, program, sizeof(program))) return 0;
|
||||||
|
|
||||||
uint32_t hProc;
|
uint32_t hProc;
|
||||||
if (SysCreateProcess(program, &hProc, cmd) == 0) {
|
if (create_process(program, &hProc, cmd) == 0) {
|
||||||
SysWaitForProcess(hProc);
|
SysWaitForProcess(hProc);
|
||||||
SysCloseHandle(hProc);
|
SysCloseHandle(hProc);
|
||||||
while (SysKbPoll()) (void)SysKbReadChar();
|
while (SysKbPoll()) (void)SysKbReadChar();
|
||||||
@@ -201,7 +231,7 @@ static int runCommand(const char* cmd) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int main(void) {
|
int main(void) {
|
||||||
const char* cmdline = SysGetPeb()->CommandLine;
|
const char* cmdline = SysGetPeb()->CommandLine.Data;
|
||||||
char lineBuf[MAX_CMD];
|
char lineBuf[MAX_CMD];
|
||||||
|
|
||||||
print("shell: command line: ");
|
print("shell: command line: ");
|
||||||
|
|||||||
@@ -165,7 +165,7 @@ static int runCommand(const char* cmd) {
|
|||||||
int main(void) {
|
int main(void) {
|
||||||
PosixFdInit();
|
PosixFdInit();
|
||||||
|
|
||||||
const char* cmdline = SysGetPeb()->CommandLine;
|
const char* cmdline = SysGetPeb()->CommandLine.Data;
|
||||||
char lineBuf[MAX_CMD];
|
char lineBuf[MAX_CMD];
|
||||||
|
|
||||||
print("shell: command line: ");
|
print("shell: command line: ");
|
||||||
|
|||||||
@@ -0,0 +1,112 @@
|
|||||||
|
#pragma once
|
||||||
|
// sys_string.h - shared native string-view ABI.
|
||||||
|
// A SYS_STRING_VIEW is a borrowed byte span. It is not NUL-terminated by
|
||||||
|
// contract and it does not own the pointed-to storage.
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#ifndef SYS_STRING_VIEW_DEFINED
|
||||||
|
#define SYS_STRING_VIEW_DEFINED 1
|
||||||
|
typedef struct SYS_STRING_VIEW {
|
||||||
|
const char* Data;
|
||||||
|
size_t Length;
|
||||||
|
} SYS_STRING_VIEW;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define SYS_SV_LITERAL(s) ((SYS_STRING_VIEW){ (s), sizeof(s) - 1 })
|
||||||
|
#define SYS_SV_NULL ((SYS_STRING_VIEW){ (const char*)0, 0 })
|
||||||
|
|
||||||
|
static inline SYS_STRING_VIEW SysSvFromParts(const char* data, size_t length) {
|
||||||
|
SYS_STRING_VIEW v;
|
||||||
|
v.Data = data;
|
||||||
|
v.Length = length;
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline SYS_STRING_VIEW SysSvFromCstr(const char* s) {
|
||||||
|
SYS_STRING_VIEW v;
|
||||||
|
v.Data = s;
|
||||||
|
v.Length = s ? strlen(s) : 0;
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int SysSvIsEmpty(SYS_STRING_VIEW s) {
|
||||||
|
return s.Length == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int SysSvIsValid(SYS_STRING_VIEW s) {
|
||||||
|
return s.Data != (const char*)0 || s.Length == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int SysSvEqual(SYS_STRING_VIEW a, SYS_STRING_VIEW b) {
|
||||||
|
if (a.Length != b.Length) return 0;
|
||||||
|
if (a.Length == 0) return 1;
|
||||||
|
if (!a.Data || !b.Data) return 0;
|
||||||
|
return memcmp(a.Data, b.Data, a.Length) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline char SysAsciiLower(char c) {
|
||||||
|
return (c >= 'A' && c <= 'Z') ? (char)(c + ('a' - 'A')) : c;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int SysSvEqualAsciiI(SYS_STRING_VIEW a, SYS_STRING_VIEW b) {
|
||||||
|
if (a.Length != b.Length) return 0;
|
||||||
|
if (a.Length == 0) return 1;
|
||||||
|
if (!a.Data || !b.Data) return 0;
|
||||||
|
for (size_t i = 0; i < a.Length; i++) {
|
||||||
|
if (SysAsciiLower(a.Data[i]) != SysAsciiLower(b.Data[i])) return 0;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int SysSvEqualLit(SYS_STRING_VIEW a, const char* lit) {
|
||||||
|
return SysSvEqual(a, SysSvFromCstr(lit));
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int SysSvEqualLitAsciiI(SYS_STRING_VIEW a, const char* lit) {
|
||||||
|
return SysSvEqualAsciiI(a, SysSvFromCstr(lit));
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int SysSvContainsByte(SYS_STRING_VIEW s, char ch) {
|
||||||
|
if (!s.Data && s.Length) return 0;
|
||||||
|
for (size_t i = 0; i < s.Length; i++) {
|
||||||
|
if (s.Data[i] == ch) return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline SYS_STRING_VIEW SysSvSlice(SYS_STRING_VIEW s, size_t off, size_t len) {
|
||||||
|
if (off > s.Length) off = s.Length;
|
||||||
|
if (len > s.Length - off) len = s.Length - off;
|
||||||
|
return SysSvFromParts(s.Data ? s.Data + off : (const char*)0, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline SYS_STRING_VIEW SysSvTrimAscii(SYS_STRING_VIEW s) {
|
||||||
|
size_t begin = 0;
|
||||||
|
size_t end = s.Length;
|
||||||
|
while (begin < end) {
|
||||||
|
char c = s.Data[begin];
|
||||||
|
if (c != ' ' && c != '\t' && c != '\r' && c != '\n') break;
|
||||||
|
begin++;
|
||||||
|
}
|
||||||
|
while (end > begin) {
|
||||||
|
char c = s.Data[end - 1];
|
||||||
|
if (c != ' ' && c != '\t' && c != '\r' && c != '\n') break;
|
||||||
|
end--;
|
||||||
|
}
|
||||||
|
return SysSvSlice(s, begin, end - begin);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int SysSvCopyZ(char* dst, size_t dstSize, SYS_STRING_VIEW src) {
|
||||||
|
if (!dst || dstSize == 0) return 0;
|
||||||
|
if (src.Length && !src.Data) return 0;
|
||||||
|
if (src.Length + 1 > dstSize) {
|
||||||
|
dst[0] = '\0';
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (src.Length) memcpy(dst, src.Data, src.Length);
|
||||||
|
dst[src.Length] = '\0';
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
+82
-25
@@ -5,6 +5,7 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <intrin.h>
|
#include <intrin.h>
|
||||||
|
#include "sys_string.h"
|
||||||
|
|
||||||
#define SYS_STATUS_SUCCESS 0u
|
#define SYS_STATUS_SUCCESS 0u
|
||||||
#define SYS_STATUS_TIMEOUT 3u
|
#define SYS_STATUS_TIMEOUT 3u
|
||||||
@@ -25,19 +26,17 @@
|
|||||||
#define SYS_ENV_VERSION 1u
|
#define SYS_ENV_VERSION 1u
|
||||||
#define SYS_CWD_MAX 520u
|
#define SYS_CWD_MAX 520u
|
||||||
|
|
||||||
|
#ifndef SYS_USER_ENV_TYPES_DEFINED
|
||||||
|
#define SYS_USER_ENV_TYPES_DEFINED 1
|
||||||
|
|
||||||
typedef struct SYS_PEB {
|
typedef struct SYS_PEB {
|
||||||
uint32_t Magic;
|
uint32_t Magic;
|
||||||
uint32_t Version;
|
uint32_t Version;
|
||||||
uint32_t ProcessId;
|
uint32_t ProcessId;
|
||||||
|
|
||||||
const char* ImagePath;
|
SYS_STRING_VIEW ImagePath;
|
||||||
size_t ImagePathLength;
|
SYS_STRING_VIEW CommandLine;
|
||||||
|
SYS_STRING_VIEW CurrentDirectory;
|
||||||
const char* CommandLine;
|
|
||||||
size_t CommandLineLength;
|
|
||||||
|
|
||||||
const char* CurrentDirectory;
|
|
||||||
size_t CurrentDirectoryLength;
|
|
||||||
} SYS_PEB;
|
} SYS_PEB;
|
||||||
|
|
||||||
typedef struct SYS_TEB {
|
typedef struct SYS_TEB {
|
||||||
@@ -47,8 +46,15 @@ typedef struct SYS_TEB {
|
|||||||
struct SYS_TEB* Self;
|
struct SYS_TEB* Self;
|
||||||
uint32_t ThreadId;
|
uint32_t ThreadId;
|
||||||
uint32_t Magic;
|
uint32_t Magic;
|
||||||
|
|
||||||
|
// Native per-thread extension pointer. The kernel leaves this NULL;
|
||||||
|
// user-mode personalities/libraries may hang their own thread-local
|
||||||
|
// state here without baking subsystem semantics into SYS_TEB.
|
||||||
|
void* SubsystemData;
|
||||||
} SYS_TEB;
|
} SYS_TEB;
|
||||||
|
|
||||||
|
#endif // SYS_USER_ENV_TYPES_DEFINED
|
||||||
|
|
||||||
static inline SYS_PEB* SysGetPeb(void) {
|
static inline SYS_PEB* SysGetPeb(void) {
|
||||||
return (SYS_PEB*)(uintptr_t)__readgsqword(0);
|
return (SYS_PEB*)(uintptr_t)__readgsqword(0);
|
||||||
}
|
}
|
||||||
@@ -124,12 +130,64 @@ typedef struct SYS_TRAP_FRAME_INFO {
|
|||||||
uint64_t R11;
|
uint64_t R11;
|
||||||
} SYS_TRAP_FRAME_INFO;
|
} SYS_TRAP_FRAME_INFO;
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// String-view based syscall request structures
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
typedef struct SYS_CREATE_PROCESS_REQUEST {
|
||||||
|
SYS_STRING_VIEW Path;
|
||||||
|
SYS_STRING_VIEW CommandLine;
|
||||||
|
uint32_t* HandleOut;
|
||||||
|
} SYS_CREATE_PROCESS_REQUEST;
|
||||||
|
|
||||||
|
typedef struct SYS_PATH_HANDLE_REQUEST {
|
||||||
|
SYS_STRING_VIEW Path;
|
||||||
|
uint32_t* HandleOut;
|
||||||
|
} SYS_PATH_HANDLE_REQUEST;
|
||||||
|
|
||||||
|
typedef struct SYS_PATH_REQUEST {
|
||||||
|
SYS_STRING_VIEW Path;
|
||||||
|
} SYS_PATH_REQUEST;
|
||||||
|
|
||||||
|
typedef struct SYS_RENAME_FILE_REQUEST {
|
||||||
|
SYS_STRING_VIEW OldPath;
|
||||||
|
SYS_STRING_VIEW NewPath;
|
||||||
|
} SYS_RENAME_FILE_REQUEST;
|
||||||
|
|
||||||
|
typedef struct SYS_GET_CWD_REQUEST {
|
||||||
|
char* Buffer;
|
||||||
|
size_t Capacity;
|
||||||
|
size_t* LengthOut;
|
||||||
|
} SYS_GET_CWD_REQUEST;
|
||||||
|
|
||||||
|
typedef struct SYS_WRITE_CONSOLE_REQUEST {
|
||||||
|
SYS_STRING_VIEW Text;
|
||||||
|
} SYS_WRITE_CONSOLE_REQUEST;
|
||||||
|
|
||||||
|
typedef struct SYS_DEBUG_PRINT_REQUEST {
|
||||||
|
SYS_STRING_VIEW Text;
|
||||||
|
} SYS_DEBUG_PRINT_REQUEST;
|
||||||
|
|
||||||
|
typedef struct SYS_PUBLISH_QUEUE_REQUEST {
|
||||||
|
SYS_STRING_VIEW Name;
|
||||||
|
uint32_t QueueHandle;
|
||||||
|
} SYS_PUBLISH_QUEUE_REQUEST;
|
||||||
|
|
||||||
|
typedef struct SYS_OPEN_NAMED_QUEUE_REQUEST {
|
||||||
|
SYS_STRING_VIEW Name;
|
||||||
|
uint32_t* HandleOut;
|
||||||
|
} SYS_OPEN_NAMED_QUEUE_REQUEST;
|
||||||
|
|
||||||
|
typedef struct SYS_NAMED_QUEUE_OWNER_REQUEST {
|
||||||
|
SYS_STRING_VIEW Name;
|
||||||
|
uint32_t* PidOut;
|
||||||
|
} SYS_NAMED_QUEUE_OWNER_REQUEST;
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Process & thread lifecycle (0-14)
|
// Process & thread lifecycle (0-14)
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
void SysExitProcess(int exitCode);
|
void SysExitProcess(int exitCode);
|
||||||
void SysExitThread(int exitCode);
|
void SysExitThread(int exitCode);
|
||||||
uint32_t SysCreateProcess(const char* path, uint32_t* handleOut, const char* cmdline);
|
uint32_t SysCreateProcess(const SYS_CREATE_PROCESS_REQUEST* request);
|
||||||
uint32_t SysWaitForProcess(uint32_t handle);
|
uint32_t SysWaitForProcess(uint32_t handle);
|
||||||
uint32_t SysGetProcessExitCode(uint32_t handle, int* exitCodeOut);
|
uint32_t SysGetProcessExitCode(uint32_t handle, int* exitCodeOut);
|
||||||
uint32_t SysKillProcess(uint32_t pid, int exitCode);
|
uint32_t SysKillProcess(uint32_t pid, int exitCode);
|
||||||
@@ -178,16 +236,16 @@ uint32_t SysEventWait(uint32_t handle, uint32_t alertable);
|
|||||||
uint32_t SysQueueCreate(uint32_t* handleOut);
|
uint32_t SysQueueCreate(uint32_t* handleOut);
|
||||||
uint32_t SysQueueSend(uint32_t handle, const void* msg, bool blocking);
|
uint32_t SysQueueSend(uint32_t handle, const void* msg, bool blocking);
|
||||||
uint32_t SysQueueRecv(uint32_t handle, void* msg, bool blocking);
|
uint32_t SysQueueRecv(uint32_t handle, void* msg, bool blocking);
|
||||||
uint32_t SysPublishQueue(const char* name, uint32_t handle);
|
uint32_t SysPublishQueue(const SYS_PUBLISH_QUEUE_REQUEST* request);
|
||||||
uint32_t SysOpenNamedQueue(const char* name, uint32_t* handleOut);
|
uint32_t SysOpenNamedQueue(const SYS_OPEN_NAMED_QUEUE_REQUEST* request);
|
||||||
uint32_t SysQueueDuplicate(uint32_t handle, uint32_t targetPid, uint32_t* handleOut);
|
uint32_t SysQueueDuplicate(uint32_t handle, uint32_t targetPid, uint32_t* handleOut);
|
||||||
uint32_t SysNamedQueueOwnerPid(const char* name, uint32_t* pidOut);
|
uint32_t SysNamedQueueOwnerPid(const SYS_NAMED_QUEUE_OWNER_REQUEST* request);
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Files, directories, and current directory (39-56)
|
// Files, directories, and current directory (39-56)
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
uint32_t SysOpenFile(const char* path, uint32_t* handleOut);
|
uint32_t SysOpenFile(const SYS_PATH_HANDLE_REQUEST* request);
|
||||||
uint32_t SysCreateFile(const char* path, uint32_t* handleOut);
|
uint32_t SysCreateFile(const SYS_PATH_HANDLE_REQUEST* request);
|
||||||
uint32_t SysReadFile(uint32_t handle, void* buf, size_t size, size_t* bytesRead);
|
uint32_t SysReadFile(uint32_t handle, void* buf, size_t size, size_t* bytesRead);
|
||||||
uint32_t SysWriteFile(uint32_t handle, const void* buf, size_t size, size_t* written);
|
uint32_t SysWriteFile(uint32_t handle, const void* buf, size_t size, size_t* written);
|
||||||
uint32_t SysReadFileAsync(const SYS_ASYNC_FILE_REQUEST* request);
|
uint32_t SysReadFileAsync(const SYS_ASYNC_FILE_REQUEST* request);
|
||||||
@@ -197,20 +255,20 @@ uint32_t SysSeekFile(uint32_t handle, uint64_t offset);
|
|||||||
uint64_t SysGetFilePos(uint32_t handle);
|
uint64_t SysGetFilePos(uint32_t handle);
|
||||||
uint64_t SysGetFileSize(uint32_t handle);
|
uint64_t SysGetFileSize(uint32_t handle);
|
||||||
uint32_t SysTruncateFile(uint32_t handle, uint64_t newSize);
|
uint32_t SysTruncateFile(uint32_t handle, uint64_t newSize);
|
||||||
uint32_t SysDeleteFile(const char* path);
|
uint32_t SysDeleteFile(const SYS_PATH_REQUEST* request);
|
||||||
uint32_t SysRenameFile(const char* oldPath, const char* newPath);
|
uint32_t SysRenameFile(const SYS_RENAME_FILE_REQUEST* request);
|
||||||
|
|
||||||
uint32_t SysOpenDir(const char* path, uint32_t* handleOut);
|
uint32_t SysOpenDir(const SYS_PATH_HANDLE_REQUEST* request);
|
||||||
uint32_t SysReadDir(uint32_t handle, void* dirEntryBuffer);
|
uint32_t SysReadDir(uint32_t handle, void* dirEntryBuffer);
|
||||||
uint32_t SysCreateDir(const char* path);
|
uint32_t SysCreateDir(const SYS_PATH_REQUEST* request);
|
||||||
uint32_t SysRemoveDir(const char* path);
|
uint32_t SysRemoveDir(const SYS_PATH_REQUEST* request);
|
||||||
uint32_t SysSetCwd(const char* path);
|
uint32_t SysSetCwd(const SYS_PATH_REQUEST* request);
|
||||||
uint32_t SysGetCwd(char* buf, size_t size);
|
uint32_t SysGetCwd(const SYS_GET_CWD_REQUEST* request);
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Console and graphics (57-61)
|
// Console and graphics (57-61)
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
uint32_t SysWriteConsole(const char* buf, size_t len);
|
uint32_t SysWriteConsole(const SYS_WRITE_CONSOLE_REQUEST* request);
|
||||||
uint32_t SysScreenBlit(int x, int y, int w, int h, const uint32_t* pixels);
|
uint32_t SysScreenBlit(int x, int y, int w, int h, const uint32_t* pixels);
|
||||||
uint32_t SysScreenClear(uint32_t color);
|
uint32_t SysScreenClear(uint32_t color);
|
||||||
uint32_t SysScreenGetSize(int* widthOut, int* heightOut);
|
uint32_t SysScreenGetSize(int* widthOut, int* heightOut);
|
||||||
@@ -286,8 +344,7 @@ typedef struct SYS_SOCKET_RECV_FROM_REQUEST {
|
|||||||
|
|
||||||
|
|
||||||
typedef struct SYS_RESOLVE_HOST_REQUEST {
|
typedef struct SYS_RESOLVE_HOST_REQUEST {
|
||||||
const char* Name;
|
SYS_STRING_VIEW Name;
|
||||||
size_t NameLength;
|
|
||||||
uint32_t Family;
|
uint32_t Family;
|
||||||
uint32_t Flags;
|
uint32_t Flags;
|
||||||
SYS_SOCKET_ADDRESS_IPV4* AddressOut;
|
SYS_SOCKET_ADDRESS_IPV4* AddressOut;
|
||||||
@@ -333,7 +390,7 @@ typedef struct SYS_MEMORY_STATUS {
|
|||||||
// Time and diagnostics
|
// Time and diagnostics
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
uint64_t SysGetTick(void);
|
uint64_t SysGetTick(void);
|
||||||
uint32_t SysDebugPrint(const char* str);
|
uint32_t SysDebugPrint(const SYS_DEBUG_PRINT_REQUEST* request);
|
||||||
uint32_t SysQueryTrapFrame(SYS_TRAP_FRAME_INFO* info);
|
uint32_t SysQueryTrapFrame(SYS_TRAP_FRAME_INFO* info);
|
||||||
|
|
||||||
uint32_t SysGetMemoryStatus(SYS_MEMORY_STATUS* statusOut);
|
uint32_t SysGetMemoryStatus(SYS_MEMORY_STATUS* statusOut);
|
||||||
@@ -5,11 +5,12 @@
|
|||||||
#include <syscall.h>
|
#include <syscall.h>
|
||||||
|
|
||||||
static void print(const char* s) {
|
static void print(const char* s) {
|
||||||
if (s) SysWriteConsole(s, strlen(s));
|
if (s) { SYS_WRITE_CONSOLE_REQUEST req = { SysSvFromCstr(s) }; SysWriteConsole(&req); }
|
||||||
}
|
}
|
||||||
|
|
||||||
static void print_char(char c) {
|
static void print_char(char c) {
|
||||||
SysWriteConsole(&c, 1);
|
SYS_WRITE_CONSOLE_REQUEST req = { SysSvFromParts(&c, 1) };
|
||||||
|
SysWriteConsole(&req);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void print_u64(uint64_t v) {
|
static void print_u64(uint64_t v) {
|
||||||
@@ -63,7 +64,7 @@ static void usage(void) {
|
|||||||
int main(void) {
|
int main(void) {
|
||||||
uint16_t port = 8080;
|
uint16_t port = 8080;
|
||||||
|
|
||||||
const char* cmd = SysGetPeb()->CommandLine;
|
const char* cmd = SysGetPeb()->CommandLine.Data;
|
||||||
char tok[64];
|
char tok[64];
|
||||||
cmd = next_token(cmd, tok, sizeof(tok)); // program path
|
cmd = next_token(cmd, tok, sizeof(tok)); // program path
|
||||||
if (cmd) {
|
if (cmd) {
|
||||||
|
|||||||
+4
-3
@@ -5,11 +5,12 @@
|
|||||||
#include <syscall.h>
|
#include <syscall.h>
|
||||||
|
|
||||||
static void print(const char* s) {
|
static void print(const char* s) {
|
||||||
if (s) SysWriteConsole(s, strlen(s));
|
if (s) { SYS_WRITE_CONSOLE_REQUEST req = { SysSvFromCstr(s) }; SysWriteConsole(&req); }
|
||||||
}
|
}
|
||||||
|
|
||||||
static void print_char(char c) {
|
static void print_char(char c) {
|
||||||
SysWriteConsole(&c, 1);
|
SYS_WRITE_CONSOLE_REQUEST req = { SysSvFromParts(&c, 1) };
|
||||||
|
SysWriteConsole(&req);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void print_u64(uint64_t v) {
|
static void print_u64(uint64_t v) {
|
||||||
@@ -211,7 +212,7 @@ static int run_listen(const char* cmd) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int main(void) {
|
int main(void) {
|
||||||
const char* cmd = SysGetPeb()->CommandLine;
|
const char* cmd = SysGetPeb()->CommandLine.Data;
|
||||||
char tok[64];
|
char tok[64];
|
||||||
cmd = next_token(cmd, tok, sizeof(tok)); // program path
|
cmd = next_token(cmd, tok, sizeof(tok)); // program path
|
||||||
cmd = next_token(cmd, tok, sizeof(tok)); // verb
|
cmd = next_token(cmd, tok, sizeof(tok)); // verb
|
||||||
|
|||||||
+114
-33
@@ -11,8 +11,8 @@
|
|||||||
// - HeapAlloc / HeapFree / HeapReAlloc ignore the HANDLE heap argument and
|
// - HeapAlloc / HeapFree / HeapReAlloc ignore the HANDLE heap argument and
|
||||||
// always use the single kernel user-heap. This is fine: we don't expose
|
// always use the single kernel user-heap. This is fine: we don't expose
|
||||||
// multiple heaps, and NULL is a legal (no-op) heap argument under Win32.
|
// multiple heaps, and NULL is a legal (no-op) heap argument under Win32.
|
||||||
// - GetLastError / SetLastError are per-thread on Windows. We keep a single
|
// - Win32 per-thread state lives behind SYS_TEB::SubsystemData so the native
|
||||||
// global for now; enough for Bink and similar single-threaded guests.
|
// TEB ABI stays subsystem-neutral.
|
||||||
// - File I/O uses SYS_OPEN_FILE / SYS_READ_FILE / SYS_SEEK_FILE /
|
// - File I/O uses SYS_OPEN_FILE / SYS_READ_FILE / SYS_SEEK_FILE /
|
||||||
// SYS_GET_FILE_SIZE / SYS_CLOSE_HANDLE (unified close path - no separate
|
// SYS_GET_FILE_SIZE / SYS_CLOSE_HANDLE (unified close path - no separate
|
||||||
// SysCloseFile).
|
// SysCloseFile).
|
||||||
@@ -160,14 +160,94 @@ static void TranslatePath(const char* input, char* output) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static DWORD gLastError = 0;
|
|
||||||
|
static uint32_t K32OpenFileNative(const char* path, uint32_t* handleOut) {
|
||||||
|
SYS_PATH_HANDLE_REQUEST req = { SysSvFromCstr(path), handleOut };
|
||||||
|
return SysOpenFile(&req);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t K32CreateFileNative(const char* path, uint32_t* handleOut) {
|
||||||
|
SYS_PATH_HANDLE_REQUEST req = { SysSvFromCstr(path), handleOut };
|
||||||
|
return SysCreateFile(&req);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t K32DeleteFileNative(const char* path) {
|
||||||
|
SYS_PATH_REQUEST req = { SysSvFromCstr(path) };
|
||||||
|
return SysDeleteFile(&req);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t K32OpenDirNative(const char* path, uint32_t* handleOut) {
|
||||||
|
SYS_PATH_HANDLE_REQUEST req = { SysSvFromCstr(path), handleOut };
|
||||||
|
return SysOpenDir(&req);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t K32SetCwdNative(const char* path) {
|
||||||
|
SYS_PATH_REQUEST req = { SysSvFromCstr(path) };
|
||||||
|
return SysSetCwd(&req);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t K32GetCwdNative(char* buffer, size_t capacity, size_t* lengthOut) {
|
||||||
|
size_t len = 0;
|
||||||
|
size_t writable = (buffer && capacity) ? capacity - 1 : 0;
|
||||||
|
SYS_GET_CWD_REQUEST req = { buffer, writable, &len };
|
||||||
|
uint32_t s = SysGetCwd(&req);
|
||||||
|
if (lengthOut) *lengthOut = len;
|
||||||
|
if (s == 0 && buffer && capacity) buffer[len] = '\0';
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t K32CreateProcessNative(const char* path, uint32_t* handleOut, const char* commandLine) {
|
||||||
|
SYS_CREATE_PROCESS_REQUEST req = { SysSvFromCstr(path), SysSvFromCstr(commandLine), handleOut };
|
||||||
|
return SysCreateProcess(&req);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void K32WriteConsoleSpan(const char* data, size_t length) {
|
||||||
|
SYS_WRITE_CONSOLE_REQUEST req = { SysSvFromParts(data, length) };
|
||||||
|
SysWriteConsole(&req);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void K32DebugPrintA(const char* s) {
|
||||||
|
if (!s) return;
|
||||||
|
SYS_DEBUG_PRINT_REQUEST req = { SysSvFromCstr(s) };
|
||||||
|
SysDebugPrint(&req);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define WIN32_TLS_SLOTS 64
|
||||||
|
|
||||||
|
typedef struct WIN32_THREAD_ENV {
|
||||||
|
DWORD LastError;
|
||||||
|
DWORD Reserved0;
|
||||||
|
void* TlsSlots[WIN32_TLS_SLOTS];
|
||||||
|
} WIN32_THREAD_ENV;
|
||||||
|
|
||||||
|
static WIN32_THREAD_ENV gFallbackThreadEnv;
|
||||||
|
|
||||||
|
static WIN32_THREAD_ENV* K32ThreadEnv(void) {
|
||||||
|
SYS_TEB* teb = SysGetTeb();
|
||||||
|
if (!teb || teb->Magic != SYS_TEB_MAGIC) return &gFallbackThreadEnv;
|
||||||
|
|
||||||
|
WIN32_THREAD_ENV* env = (WIN32_THREAD_ENV*)teb->SubsystemData;
|
||||||
|
if (!env) {
|
||||||
|
env = (WIN32_THREAD_ENV*)SysAlloc(sizeof(WIN32_THREAD_ENV));
|
||||||
|
if (!env) return &gFallbackThreadEnv;
|
||||||
|
memset(env, 0, sizeof(*env));
|
||||||
|
teb->SubsystemData = env;
|
||||||
|
}
|
||||||
|
return env;
|
||||||
|
}
|
||||||
|
|
||||||
|
static DWORD* K32LastErrorSlot(void) {
|
||||||
|
return &K32ThreadEnv()->LastError;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define gLastError (*K32LastErrorSlot())
|
||||||
|
|
||||||
static BOOL KiFileExistsA(const char* path) {
|
static BOOL KiFileExistsA(const char* path) {
|
||||||
if (!path || !*path) return FALSE;
|
if (!path || !*path) return FALSE;
|
||||||
char native[MAX_PATH];
|
char native[MAX_PATH];
|
||||||
TranslatePath(path, native);
|
TranslatePath(path, native);
|
||||||
uint32_t h = 0;
|
uint32_t h = 0;
|
||||||
if (SysOpenFile(native, &h) != 0) return FALSE;
|
if (K32OpenFileNative(native, &h) != 0) return FALSE;
|
||||||
SysCloseHandle(h);
|
SysCloseHandle(h);
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
@@ -268,7 +348,7 @@ __declspec(dllexport) _Noreturn void ExitProcess(unsigned int code) {
|
|||||||
// Debug output
|
// Debug output
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
__declspec(dllexport) void OutputDebugStringA(const char* str) {
|
__declspec(dllexport) void OutputDebugStringA(const char* str) {
|
||||||
if (str) SysDebugPrint(str);
|
if (str) K32DebugPrintA(str);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
@@ -510,7 +590,7 @@ __declspec(dllexport) HANDLE CreateFileA(const char* name,
|
|||||||
if (disposition == OPEN_EXISTING || disposition == TRUNCATE_EXISTING) {
|
if (disposition == OPEN_EXISTING || disposition == TRUNCATE_EXISTING) {
|
||||||
// Open the file via the read path (it must already exist).
|
// Open the file via the read path (it must already exist).
|
||||||
uint32_t kh = 0;
|
uint32_t kh = 0;
|
||||||
uint32_t s = SysOpenFile(translatedName, &kh);
|
uint32_t s = K32OpenFileNative(translatedName, &kh);
|
||||||
if (s != 0) { gLastError = 2; return INVALID_HANDLE_VALUE; }
|
if (s != 0) { gLastError = 2; return INVALID_HANDLE_VALUE; }
|
||||||
|
|
||||||
if (disposition == TRUNCATE_EXISTING && wantWrite) {
|
if (disposition == TRUNCATE_EXISTING && wantWrite) {
|
||||||
@@ -529,10 +609,10 @@ __declspec(dllexport) HANDLE CreateFileA(const char* name,
|
|||||||
// Create-or-replace: try to create; if the file already exists,
|
// Create-or-replace: try to create; if the file already exists,
|
||||||
// open it and truncate it to zero instead.
|
// open it and truncate it to zero instead.
|
||||||
uint32_t kh = 0;
|
uint32_t kh = 0;
|
||||||
uint32_t s = SysCreateFile(translatedName, &kh);
|
uint32_t s = K32CreateFileNative(translatedName, &kh);
|
||||||
if (s != 0) {
|
if (s != 0) {
|
||||||
// File may already exist - open and truncate.
|
// File may already exist - open and truncate.
|
||||||
s = SysOpenFile(translatedName, &kh);
|
s = K32OpenFileNative(translatedName, &kh);
|
||||||
if (s != 0) { gLastError = 5; return INVALID_HANDLE_VALUE; }
|
if (s != 0) { gLastError = 5; return INVALID_HANDLE_VALUE; }
|
||||||
SysTruncateFile(kh, 0);
|
SysTruncateFile(kh, 0);
|
||||||
}
|
}
|
||||||
@@ -542,8 +622,8 @@ __declspec(dllexport) HANDLE CreateFileA(const char* name,
|
|||||||
if (disposition == OPEN_ALWAYS && wantWrite) {
|
if (disposition == OPEN_ALWAYS && wantWrite) {
|
||||||
// Open if exists, create if not.
|
// Open if exists, create if not.
|
||||||
uint32_t kh = 0;
|
uint32_t kh = 0;
|
||||||
if (SysOpenFile(translatedName, &kh) != 0) {
|
if (K32OpenFileNative(translatedName, &kh) != 0) {
|
||||||
if (SysCreateFile(translatedName, &kh) != 0) {
|
if (K32CreateFileNative(translatedName, &kh) != 0) {
|
||||||
gLastError = 5;
|
gLastError = 5;
|
||||||
return INVALID_HANDLE_VALUE;
|
return INVALID_HANDLE_VALUE;
|
||||||
}
|
}
|
||||||
@@ -555,7 +635,7 @@ __declspec(dllexport) HANDLE CreateFileA(const char* name,
|
|||||||
// Fail if the file already exists (we can't check atomically,
|
// Fail if the file already exists (we can't check atomically,
|
||||||
// but SysCreateFile returns NAME_COLLISION for existing files).
|
// but SysCreateFile returns NAME_COLLISION for existing files).
|
||||||
uint32_t kh = 0;
|
uint32_t kh = 0;
|
||||||
uint32_t s = SysCreateFile(translatedName, &kh);
|
uint32_t s = K32CreateFileNative(translatedName, &kh);
|
||||||
if (s != 0) { gLastError = 80; return INVALID_HANDLE_VALUE; } // ERROR_FILE_EXISTS
|
if (s != 0) { gLastError = 80; return INVALID_HANDLE_VALUE; } // ERROR_FILE_EXISTS
|
||||||
return KH_TO_HANDLE(kh);
|
return KH_TO_HANDLE(kh);
|
||||||
}
|
}
|
||||||
@@ -677,7 +757,7 @@ __declspec(dllexport) BOOL DeleteFileA(const char* lpFileName) {
|
|||||||
char translatedPath[MAX_PATH];
|
char translatedPath[MAX_PATH];
|
||||||
TranslatePath(lpFileName, translatedPath);
|
TranslatePath(lpFileName, translatedPath);
|
||||||
|
|
||||||
uint32_t s = SysDeleteFile(translatedPath);
|
uint32_t s = K32DeleteFileNative(translatedPath);
|
||||||
if (s != 0) { gLastError = 2; return FALSE; }
|
if (s != 0) { gLastError = 2; return FALSE; }
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
@@ -747,15 +827,13 @@ __declspec(dllexport) HANDLE GetCurrentThread(void) {
|
|||||||
__declspec(dllexport) DWORD GetCurrentThreadId(void) { return (DWORD)SysGetCurrentTid(); }
|
__declspec(dllexport) DWORD GetCurrentThreadId(void) { return (DWORD)SysGetCurrentTid(); }
|
||||||
__declspec(dllexport) DWORD GetCurrentProcessId(void) { return (DWORD)SysGetCurrentPid(); }
|
__declspec(dllexport) DWORD GetCurrentProcessId(void) { return (DWORD)SysGetCurrentPid(); }
|
||||||
|
|
||||||
// Tiny TLS table; enough for MSVCRT/TCC compatibility paths that probe TLS.
|
// Tiny TLS index allocator; slot values are stored per thread in WIN32_THREAD_ENV.
|
||||||
#define WIN32_TLS_SLOTS 64
|
|
||||||
static void* gTlsSlots[WIN32_TLS_SLOTS];
|
|
||||||
static uint64_t gTlsBitmap = 1; // reserve slot 0
|
static uint64_t gTlsBitmap = 1; // reserve slot 0
|
||||||
|
|
||||||
__declspec(dllexport) DWORD TlsAlloc(void) {
|
__declspec(dllexport) DWORD TlsAlloc(void) {
|
||||||
for (DWORD i = 1; i < WIN32_TLS_SLOTS; i++) {
|
for (DWORD i = 1; i < WIN32_TLS_SLOTS; i++) {
|
||||||
uint64_t bit = 1ULL << i;
|
uint64_t bit = 1ULL << i;
|
||||||
if (!(gTlsBitmap & bit)) { gTlsBitmap |= bit; gTlsSlots[i] = NULL; return i; }
|
if (!(gTlsBitmap & bit)) { gTlsBitmap |= bit; return i; }
|
||||||
}
|
}
|
||||||
gLastError = 8;
|
gLastError = 8;
|
||||||
return 0xFFFFFFFFUL;
|
return 0xFFFFFFFFUL;
|
||||||
@@ -763,20 +841,23 @@ __declspec(dllexport) DWORD TlsAlloc(void) {
|
|||||||
|
|
||||||
__declspec(dllexport) BOOL TlsFree(DWORD dwTlsIndex) {
|
__declspec(dllexport) BOOL TlsFree(DWORD dwTlsIndex) {
|
||||||
if (dwTlsIndex >= WIN32_TLS_SLOTS || !(gTlsBitmap & (1ULL << dwTlsIndex))) { gLastError = 87; return FALSE; }
|
if (dwTlsIndex >= WIN32_TLS_SLOTS || !(gTlsBitmap & (1ULL << dwTlsIndex))) { gLastError = 87; return FALSE; }
|
||||||
gTlsSlots[dwTlsIndex] = NULL;
|
WIN32_THREAD_ENV* env = K32ThreadEnv();
|
||||||
|
if (env) env->TlsSlots[dwTlsIndex] = NULL;
|
||||||
gTlsBitmap &= ~(1ULL << dwTlsIndex);
|
gTlsBitmap &= ~(1ULL << dwTlsIndex);
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
__declspec(dllexport) void* TlsGetValue(DWORD dwTlsIndex) {
|
__declspec(dllexport) void* TlsGetValue(DWORD dwTlsIndex) {
|
||||||
if (dwTlsIndex >= WIN32_TLS_SLOTS || !(gTlsBitmap & (1ULL << dwTlsIndex))) { gLastError = 87; return NULL; }
|
if (dwTlsIndex >= WIN32_TLS_SLOTS || !(gTlsBitmap & (1ULL << dwTlsIndex))) { gLastError = 87; return NULL; }
|
||||||
|
WIN32_THREAD_ENV* env = K32ThreadEnv();
|
||||||
gLastError = 0;
|
gLastError = 0;
|
||||||
return gTlsSlots[dwTlsIndex];
|
return env->TlsSlots[dwTlsIndex];
|
||||||
}
|
}
|
||||||
|
|
||||||
__declspec(dllexport) BOOL TlsSetValue(DWORD dwTlsIndex, void* lpTlsValue) {
|
__declspec(dllexport) BOOL TlsSetValue(DWORD dwTlsIndex, void* lpTlsValue) {
|
||||||
if (dwTlsIndex >= WIN32_TLS_SLOTS || !(gTlsBitmap & (1ULL << dwTlsIndex))) { gLastError = 87; return FALSE; }
|
if (dwTlsIndex >= WIN32_TLS_SLOTS || !(gTlsBitmap & (1ULL << dwTlsIndex))) { gLastError = 87; return FALSE; }
|
||||||
gTlsSlots[dwTlsIndex] = lpTlsValue;
|
WIN32_THREAD_ENV* env = K32ThreadEnv();
|
||||||
|
env->TlsSlots[dwTlsIndex] = lpTlsValue;
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -797,7 +878,7 @@ static BOOL KiIsWin32DllNameA(const char* name) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void KiDbgWriteA(const char* s) {
|
static void KiDbgWriteA(const char* s) {
|
||||||
if (s) SysWriteConsole(s, strlen(s));
|
if (s) K32WriteConsoleSpan(s, strlen(s));
|
||||||
}
|
}
|
||||||
|
|
||||||
__declspec(dllexport) HANDLE GetModuleHandleA(const char* lpModuleName) {
|
__declspec(dllexport) HANDLE GetModuleHandleA(const char* lpModuleName) {
|
||||||
@@ -856,8 +937,8 @@ __declspec(dllexport) DWORD GetModuleFileNameA(HANDLE hModule,
|
|||||||
if (!lpFilename || !nSize) return 0;
|
if (!lpFilename || !nSize) return 0;
|
||||||
|
|
||||||
SYS_PEB* peb = Win32Peb();
|
SYS_PEB* peb = Win32Peb();
|
||||||
const char* src = (peb && peb->Magic == SYS_PEB_MAGIC && peb->ImagePath)
|
const char* src = (peb && peb->Magic == SYS_PEB_MAGIC && peb->ImagePath.Data)
|
||||||
? peb->ImagePath
|
? peb->ImagePath.Data
|
||||||
: "";
|
: "";
|
||||||
|
|
||||||
DWORD len = (DWORD)strlen(src);
|
DWORD len = (DWORD)strlen(src);
|
||||||
@@ -898,8 +979,8 @@ __declspec(dllexport) void GetStartupInfoA(STARTUPINFOA* lpStartupInfo) {
|
|||||||
|
|
||||||
__declspec(dllexport) char* GetCommandLineA(void) {
|
__declspec(dllexport) char* GetCommandLineA(void) {
|
||||||
SYS_PEB* peb = Win32Peb();
|
SYS_PEB* peb = Win32Peb();
|
||||||
if (!peb || peb->Magic != SYS_PEB_MAGIC || !peb->CommandLine) return "";
|
if (!peb || peb->Magic != SYS_PEB_MAGIC || !peb->CommandLine.Data) return "";
|
||||||
return (char*)peb->CommandLine;
|
return (char*)peb->CommandLine.Data;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
@@ -1025,11 +1106,11 @@ __declspec(dllexport) BOOL CreateProcessA(
|
|||||||
|
|
||||||
if (lpCurrentDirectory && lpCurrentDirectory[0]) {
|
if (lpCurrentDirectory && lpCurrentDirectory[0]) {
|
||||||
char translatedCwd[MAX_PATH];
|
char translatedCwd[MAX_PATH];
|
||||||
uint32_t gs = SysGetCwd(savedCwd, sizeof(savedCwd));
|
uint32_t gs = K32GetCwdNative(savedCwd, sizeof(savedCwd), NULL);
|
||||||
if (gs != 0) { gLastError = 3; return FALSE; }
|
if (gs != 0) { gLastError = 3; return FALSE; }
|
||||||
|
|
||||||
TranslatePath(lpCurrentDirectory, translatedCwd);
|
TranslatePath(lpCurrentDirectory, translatedCwd);
|
||||||
gs = SysSetCwd(translatedCwd);
|
gs = K32SetCwdNative(translatedCwd);
|
||||||
if (gs != 0) { gLastError = 3; return FALSE; }
|
if (gs != 0) { gLastError = 3; return FALSE; }
|
||||||
changedCwd = true;
|
changedCwd = true;
|
||||||
}
|
}
|
||||||
@@ -1038,7 +1119,7 @@ __declspec(dllexport) BOOL CreateProcessA(
|
|||||||
// Call the syscall.dll stub. The kernel snapshots the current CWD into
|
// Call the syscall.dll stub. The kernel snapshots the current CWD into
|
||||||
// the child process, so the temporary lpCurrentDirectory switch can be
|
// the child process, so the temporary lpCurrentDirectory switch can be
|
||||||
// restored immediately after creation.
|
// restored immediately after creation.
|
||||||
uint32_t s = SysCreateProcess(translatedPath, &kh, cmdline);
|
uint32_t s = K32CreateProcessNative(translatedPath, &kh, cmdline);
|
||||||
|
|
||||||
if (changedCwd) {
|
if (changedCwd) {
|
||||||
if (savedCwd[0]) {
|
if (savedCwd[0]) {
|
||||||
@@ -1048,9 +1129,9 @@ __declspec(dllexport) BOOL CreateProcessA(
|
|||||||
if (rlen + 4 > sizeof(restoreCwd)) rlen = sizeof(restoreCwd) - 4;
|
if (rlen + 4 > sizeof(restoreCwd)) rlen = sizeof(restoreCwd) - 4;
|
||||||
memcpy(restoreCwd + 3, savedCwd, rlen);
|
memcpy(restoreCwd + 3, savedCwd, rlen);
|
||||||
restoreCwd[3 + rlen] = '\0';
|
restoreCwd[3 + rlen] = '\0';
|
||||||
SysSetCwd(restoreCwd);
|
K32SetCwdNative(restoreCwd);
|
||||||
} else {
|
} else {
|
||||||
SysSetCwd("0:/");
|
K32SetCwdNative("0:/");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1166,7 +1247,7 @@ __declspec(dllexport) BOOL WriteConsoleA(HANDLE hConsoleOutput,
|
|||||||
if (lpNumberOfCharsWritten) *lpNumberOfCharsWritten = 0;
|
if (lpNumberOfCharsWritten) *lpNumberOfCharsWritten = 0;
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
SysWriteConsole((const char*)lpBuffer, (size_t)nNumberOfCharsToWrite);
|
K32WriteConsoleSpan((const char*)lpBuffer, (size_t)nNumberOfCharsToWrite);
|
||||||
if (lpNumberOfCharsWritten) *lpNumberOfCharsWritten = nNumberOfCharsToWrite;
|
if (lpNumberOfCharsWritten) *lpNumberOfCharsWritten = nNumberOfCharsToWrite;
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
@@ -1306,7 +1387,7 @@ __declspec(dllexport) HANDLE FindFirstFileA(const char* lpFileName,
|
|||||||
|
|
||||||
// Open the kernel directory iterator
|
// Open the kernel directory iterator
|
||||||
uint32_t kh = 0;
|
uint32_t kh = 0;
|
||||||
uint32_t s = SysOpenDir(dir[0] ? dir : NULL, &kh);
|
uint32_t s = K32OpenDirNative(dir[0] ? dir : NULL, &kh);
|
||||||
if (s != 0 || kh == 0) {
|
if (s != 0 || kh == 0) {
|
||||||
gLastError = 2; // ERROR_FILE_NOT_FOUND
|
gLastError = 2; // ERROR_FILE_NOT_FOUND
|
||||||
return INVALID_HANDLE_VALUE;
|
return INVALID_HANDLE_VALUE;
|
||||||
@@ -1387,7 +1468,7 @@ __declspec(dllexport) BOOL SetCurrentDirectoryA(const char* lpPathName) {
|
|||||||
char translatedPath[MAX_PATH];
|
char translatedPath[MAX_PATH];
|
||||||
TranslatePath(lpPathName, translatedPath);
|
TranslatePath(lpPathName, translatedPath);
|
||||||
|
|
||||||
uint32_t s = SysSetCwd(translatedPath);
|
uint32_t s = K32SetCwdNative(translatedPath);
|
||||||
if (s != 0) { gLastError = 2; return FALSE; }
|
if (s != 0) { gLastError = 2; return FALSE; }
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
@@ -1400,7 +1481,7 @@ __declspec(dllexport) DWORD GetCurrentDirectoryA(DWORD nBufferLength, char* lpBu
|
|||||||
|
|
||||||
// Kernel returns native "N:/path". Present it as a Win32 drive letter.
|
// Kernel returns native "N:/path". Present it as a Win32 drive letter.
|
||||||
char raw[512];
|
char raw[512];
|
||||||
uint32_t s = SysGetCwd(raw, sizeof(raw));
|
uint32_t s = K32GetCwdNative(raw, sizeof(raw), NULL);
|
||||||
if (s != 0) { gLastError = 1; return 0; }
|
if (s != 0) { gLastError = 1; return 0; }
|
||||||
|
|
||||||
char full[515];
|
char full[515];
|
||||||
|
|||||||
+76
-69
@@ -1,69 +1,76 @@
|
|||||||
// user32.c - Win32 User Interface
|
// user32.c - Win32 User Interface
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <syscall.h>
|
#include <syscall.h>
|
||||||
|
|
||||||
typedef void* HWND;
|
typedef void* HWND;
|
||||||
typedef void* HDC;
|
typedef void* HDC;
|
||||||
typedef long LONG;
|
typedef long LONG;
|
||||||
typedef int BOOL;
|
typedef int BOOL;
|
||||||
|
|
||||||
typedef struct _RECT { LONG left, top, right, bottom; } RECT;
|
typedef struct _RECT { LONG left, top, right, bottom; } RECT;
|
||||||
typedef struct tagPAINTSTRUCT {
|
typedef struct tagPAINTSTRUCT {
|
||||||
HDC hdc; BOOL fErase; RECT rcPaint; BOOL fRestore; BOOL fIncUpdate; char res[32];
|
HDC hdc; BOOL fErase; RECT rcPaint; BOOL fRestore; BOOL fIncUpdate; char res[32];
|
||||||
} PAINTSTRUCT;
|
} PAINTSTRUCT;
|
||||||
|
|
||||||
// --- Painting Functions ---
|
|
||||||
__declspec(dllexport) HDC BeginPaint(HWND hwnd, PAINTSTRUCT* lpPaint) {
|
static void User32DebugPrintA(const char* s) {
|
||||||
(void)hwnd;
|
if (!s) return;
|
||||||
if (lpPaint) {
|
SYS_DEBUG_PRINT_REQUEST req = { SysSvFromCstr(s) };
|
||||||
lpPaint->hdc = (HDC)1;
|
SysDebugPrint(&req);
|
||||||
lpPaint->rcPaint.right = 1920; lpPaint->rcPaint.bottom = 1080;
|
}
|
||||||
}
|
|
||||||
return (HDC)1;
|
// --- Painting Functions ---
|
||||||
}
|
__declspec(dllexport) HDC BeginPaint(HWND hwnd, PAINTSTRUCT* lpPaint) {
|
||||||
|
(void)hwnd;
|
||||||
__declspec(dllexport) BOOL EndPaint(HWND hwnd, const PAINTSTRUCT* lp) { (void)hwnd; (void)lp; return 1; }
|
if (lpPaint) {
|
||||||
|
lpPaint->hdc = (HDC)1;
|
||||||
// --- Message Loop Stubs ---
|
lpPaint->rcPaint.right = 1920; lpPaint->rcPaint.bottom = 1080;
|
||||||
__declspec(dllexport) int GetMessage(void* m, HWND h, uint32_t f, uint32_t l) {
|
}
|
||||||
(void)m; (void)h; (void)f; (void)l; SysYield(); return 0;
|
return (HDC)1;
|
||||||
}
|
}
|
||||||
__declspec(dllexport) int PeekMessage(void* m, HWND h, uint32_t f, uint32_t l, uint32_t r) {
|
|
||||||
(void)m; (void)h; (void)f; (void)l; (void)r;
|
__declspec(dllexport) BOOL EndPaint(HWND hwnd, const PAINTSTRUCT* lp) { (void)hwnd; (void)lp; return 1; }
|
||||||
return 0;
|
|
||||||
}
|
// --- Message Loop Stubs ---
|
||||||
__declspec(dllexport) int DispatchMessage(const void* m) { (void)m; return 0; }
|
__declspec(dllexport) int GetMessage(void* m, HWND h, uint32_t f, uint32_t l) {
|
||||||
__declspec(dllexport) int TranslateMessage(const void* m) { (void)m; return 0; }
|
(void)m; (void)h; (void)f; (void)l; SysYield(); return 0;
|
||||||
|
}
|
||||||
// --- Dialogs & UI ---
|
__declspec(dllexport) int PeekMessage(void* m, HWND h, uint32_t f, uint32_t l, uint32_t r) {
|
||||||
__declspec(dllexport) int MessageBoxA(HWND h, const char* txt, const char* cap, uint32_t type) {
|
(void)m; (void)h; (void)f; (void)l; (void)r;
|
||||||
(void)h; (void)cap; (void)type;
|
return 0;
|
||||||
SysDebugPrint("USER32: MessageBoxA: ");
|
}
|
||||||
SysDebugPrint(txt);
|
__declspec(dllexport) int DispatchMessage(const void* m) { (void)m; return 0; }
|
||||||
SysDebugPrint("\n");
|
__declspec(dllexport) int TranslateMessage(const void* m) { (void)m; return 0; }
|
||||||
return 1; // IDOK
|
|
||||||
}
|
// --- Dialogs & UI ---
|
||||||
|
__declspec(dllexport) int MessageBoxA(HWND h, const char* txt, const char* cap, uint32_t type) {
|
||||||
// --- Window Queries ---
|
(void)h; (void)cap; (void)type;
|
||||||
__declspec(dllexport) HWND GetDesktopWindow(void) { return (HWND)0; }
|
User32DebugPrintA("USER32: MessageBoxA: ");
|
||||||
__declspec(dllexport) HWND GetActiveWindow(void) { return (HWND)0; }
|
User32DebugPrintA(txt);
|
||||||
__declspec(dllexport) HWND GetTopWindow(HWND hwnd){ (void)hwnd; return (HWND)0; }
|
User32DebugPrintA("\n");
|
||||||
|
return 1; // IDOK
|
||||||
__declspec(dllexport) HWND GetWindow(HWND hwnd, unsigned int cmd) {
|
}
|
||||||
(void)hwnd; (void)cmd; return (HWND)0;
|
|
||||||
}
|
// --- Window Queries ---
|
||||||
|
__declspec(dllexport) HWND GetDesktopWindow(void) { return (HWND)0; }
|
||||||
__declspec(dllexport) long GetWindowLongA(HWND hwnd, int index) {
|
__declspec(dllexport) HWND GetActiveWindow(void) { return (HWND)0; }
|
||||||
(void)hwnd; (void)index; return 0;
|
__declspec(dllexport) HWND GetTopWindow(HWND hwnd){ (void)hwnd; return (HWND)0; }
|
||||||
}
|
|
||||||
|
__declspec(dllexport) HWND GetWindow(HWND hwnd, unsigned int cmd) {
|
||||||
__declspec(dllexport) intptr_t GetWindowLongPtrA(HWND hwnd, int index) {
|
(void)hwnd; (void)cmd; return (HWND)0;
|
||||||
(void)hwnd; (void)index; return 0;
|
}
|
||||||
}
|
|
||||||
|
__declspec(dllexport) long GetWindowLongA(HWND hwnd, int index) {
|
||||||
__declspec(dllexport) unsigned long GetWindowThreadProcessId(HWND hwnd, unsigned long* pid) {
|
(void)hwnd; (void)index; return 0;
|
||||||
(void)hwnd;
|
}
|
||||||
if (pid) *pid = 1;
|
|
||||||
return 1; // Thread ID
|
__declspec(dllexport) intptr_t GetWindowLongPtrA(HWND hwnd, int index) {
|
||||||
}
|
(void)hwnd; (void)index; return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
__declspec(dllexport) unsigned long GetWindowThreadProcessId(HWND hwnd, unsigned long* pid) {
|
||||||
|
(void)hwnd;
|
||||||
|
if (pid) *pid = 1;
|
||||||
|
return 1; // Thread ID
|
||||||
|
}
|
||||||
@@ -18,11 +18,13 @@ The codebase is still small enough to understand, but large enough to hurt your
|
|||||||
- higher-half x86-64 kernel
|
- higher-half x86-64 kernel
|
||||||
- physical memory manager
|
- physical memory manager
|
||||||
- virtual memory manager with kernel pool and user heap
|
- virtual memory manager with kernel pool and user heap
|
||||||
|
- W^X-style user page protections
|
||||||
- single address-space design with user/supervisor page permissions
|
- single address-space design with user/supervisor page permissions
|
||||||
- HAL for screen, keyboard, mouse, timer, PCI, APIC, HPET, AHCI, and IDE
|
- HAL for screen, keyboard, mouse, timer, PCI, APIC, HPET, AHCI, and IDE
|
||||||
- preemptive priority scheduler
|
- preemptive priority scheduler accounting for ticks, context switches, idle selections, demotions, and aging promotions
|
||||||
- processes and threads
|
- processes and threads
|
||||||
- object handles
|
- process termination state tracking and exit signaling
|
||||||
|
- object handles with per-handle rights metadata
|
||||||
- events, semaphores, mutexes, queues, and alertable waits
|
- events, semaphores, mutexes, queues, and alertable waits
|
||||||
- IRQL model: `PASSIVE_LEVEL`, `APC_LEVEL`, `DISPATCH_LEVEL`, and device IRQLs
|
- IRQL model: `PASSIVE_LEVEL`, `APC_LEVEL`, `DISPATCH_LEVEL`, and device IRQLs
|
||||||
- interrupt-exit scheduling instead of context-switching inside hard IRQ handlers
|
- interrupt-exit scheduling instead of context-switching inside hard IRQ handlers
|
||||||
@@ -42,14 +44,14 @@ The codebase is still small enough to understand, but large enough to hurt your
|
|||||||
- lwIP network stack as a runtime driver (`lwip.sys`)
|
- lwIP network stack as a runtime driver (`lwip.sys`)
|
||||||
- RTL8139 and RTL816x ethernet drivers
|
- RTL8139 and RTL816x ethernet drivers
|
||||||
- native BSD-style socket syscalls: `SysSocket`, `SysBind`, `SysConnect`, `SysSend`, `SysRecv`, `SysPoll`, `SysResolveHost`, and friends
|
- native BSD-style socket syscalls: `SysSocket`, `SysBind`, `SysConnect`, `SysSend`, `SysRecv`, `SysPoll`, `SysResolveHost`, and friends
|
||||||
- `SysGetMemoryStatus` syscall for querying physical/pool/heap usage
|
- length-tracked string-view syscall ABI with request structs for path/name/text arguments
|
||||||
|
- printf/debug formatting support for bounded string views via `%v`
|
||||||
- INI-based configuration
|
- INI-based configuration
|
||||||
- PE import resolution and dynamic `.sys` driver loading
|
- PE import resolution and dynamic `.sys` driver loading
|
||||||
- partial Win32 personality
|
- partial Win32 personality
|
||||||
- partial (stub/microsoft-level) POSIX personality
|
- partial (stub/microsoft-level) POSIX personality
|
||||||
- self-tests for APCs, async disk I/O, async file I/O, trap frames, and cancellation
|
- self-tests for APCs, async disk I/O, async file I/O, trap frames, and cancellation
|
||||||
- fully GDPR-compliant
|
- fully GDPR-compliant
|
||||||
- Doom, because priorities matter
|
|
||||||
|
|
||||||
## architecture notes
|
## architecture notes
|
||||||
|
|
||||||
@@ -59,8 +61,7 @@ Single address space for the whole system. No separate kernel/user page tables.
|
|||||||
|
|
||||||
Kernel lives in the higher half. User processes only see what they are explicitly given. Kernel pages are supervisor-only so user-mode code cannot touch them. User pages are isolated from each other through the allocator and process creation path, not by hardware address-space separation. One rogue pointer can't crash the kernel but it might corrupt other processes. But what are the chances?
|
Kernel lives in the higher half. User processes only see what they are explicitly given. Kernel pages are supervisor-only so user-mode code cannot touch them. User pages are isolated from each other through the allocator and process creation path, not by hardware address-space separation. One rogue pointer can't crash the kernel but it might corrupt other processes. But what are the chances?
|
||||||
|
|
||||||
Is this how a serious modern OS should isolate processes? No.
|
User heap and stack mappings are non-executable, and the PE loader applies final section protections after loading. Writable sections are left writable and non-executable; executable sections are mapped executable without write access where possible.
|
||||||
Is it very convenient while building? Absolutely.
|
|
||||||
|
|
||||||
### execution model
|
### execution model
|
||||||
|
|
||||||
@@ -135,7 +136,11 @@ lwIP TCP/IP network stack
|
|||||||
|
|
||||||
PE images get loaded, imports resolved, then the first thread is queued. `syscall.dll` is the shared syscall entry point, and both personalities go through it.
|
PE images get loaded, imports resolved, then the first thread is queued. `syscall.dll` is the shared syscall entry point, and both personalities go through it.
|
||||||
|
|
||||||
Scheduler is preemptive and priority-based. Process state and thread state are tracked separately, so a process can die or get reaped without taking the whole system down. `init` can restart things if needed.
|
String and buffer-heavy syscalls use request structs. Text arguments are passed as bounded `SYS_STRING_VIEW` values, then copied and validated by the kernel before use.
|
||||||
|
|
||||||
|
Scheduler is preemptive and priority-based. Process state and thread state are tracked separately, so a process can die or get reaped without taking the whole system down. `init` can restart things if needed. Process objects track termination and exit state separately from individual thread lifetime.
|
||||||
|
|
||||||
|
Handles carry rights metadata. Syscalls check both object type and required access for operations such as read, write, wait, signal, query, duplicate, and terminate.
|
||||||
|
|
||||||
Syscalls build real trap frames. That matters because user APC delivery needs to safely redirect the return path back into user mode and then restore the interrupted context with `SysApcReturn`.
|
Syscalls build real trap frames. That matters because user APC delivery needs to safely redirect the return path back into user mode and then restore the interrupted context with `SysApcReturn`.
|
||||||
|
|
||||||
@@ -203,6 +208,8 @@ Port forwarding is enabled by default in `runa.bat` — host port 8080 maps to g
|
|||||||
Volumes `0`-`7` are available. A volume only works if a filesystem is registered on it (duh).
|
Volumes `0`-`7` are available. A volume only works if a filesystem is registered on it (duh).
|
||||||
The Win32 personality translates drive letters to volume numbers, while the POSIX personality maps volumes onto a tree.
|
The Win32 personality translates drive letters to volume numbers, while the POSIX personality maps volumes onto a tree.
|
||||||
|
|
||||||
|
Path parsing is length-aware internally. Embedded NUL bytes are rejected for text paths and names.
|
||||||
|
|
||||||
## how to build
|
## how to build
|
||||||
|
|
||||||
- open the VS x64 build tools as administrator
|
- open the VS x64 build tools as administrator
|
||||||
@@ -260,11 +267,14 @@ todo
|
|||||||
## faq
|
## faq
|
||||||
|
|
||||||
Q: Why?
|
Q: Why?
|
||||||
A: Learning. Fun. Spite.
|
A: Reasons.
|
||||||
|
|
||||||
Q: Does it run Doom?
|
Q: Does it run Doom?
|
||||||
A: `doom.exe`
|
A: `doom.exe`
|
||||||
|
|
||||||
|
Q: If I write a program now, will it run in the future?
|
||||||
|
A: Not a chance. WE DO in fact BREAK USERSPACE.
|
||||||
|
|
||||||
Q: Can you read and write files?
|
Q: Can you read and write files?
|
||||||
A: As far as I'm aware.
|
A: As far as I'm aware.
|
||||||
|
|
||||||
@@ -278,10 +288,11 @@ Q: Is the Win32 layer real?
|
|||||||
A: Depends how you frame it. Some apps will run, most will not.
|
A: Depends how you frame it. Some apps will run, most will not.
|
||||||
|
|
||||||
Q: Are processes isolated?
|
Q: Are processes isolated?
|
||||||
A: No. But IPC is free.
|
A: No. But IPC is free. Is this how a serious modern OS should isolate processes? No.
|
||||||
|
Is it very convenient while building? Absolutely.
|
||||||
|
|
||||||
Q: How often does it crash?
|
Q: How often does it crash?
|
||||||
A: Less often than before.
|
A: Like Windows 11.
|
||||||
|
|
||||||
Q: Can it recover from a crash?
|
Q: Can it recover from a crash?
|
||||||
A: Yes. Unless the kernel faulted.
|
A: Yes. Unless the kernel faulted.
|
||||||
|
|||||||
Reference in new issue
Block a user