add ntfs.sys standalone driver target, change default usb vhd layout formatting to ntfs, implement Fat32CreateDir for directory creation support in the fat32 file system driver, add a dedicated HAL serial output endpoint for COM1 writes in hal_serial.c and subscribe serial to HAL text streams, introduce waitable kernel timers with KeTimerCreate, KeTimerSet, and KeTimerCancel alongside spinlock-protected global timer list initialization and scheduler tick tracking, implement SyscallWriteFileAsync system call for asynchronous non-blocking file writes, refactor NetSocketConnect and NetSocketSendTo to use generic SYS_SOCKET_ADDRESS_K structures instead of being hardcoded to IPv4, update gethostbyname and DNS name resolution to support IPv6 parsing via ResolveNameFamily, update tcpserve.exe with -4 and -6 command-line flags to accept and display IPv6 addresses, append size and version headers to all kernel telemetry and statistics structures including SYS_PMM_STATS, SYS_CPU_STATS, and SYS_SCHEDULER_STATS, update winmm timeGetTime to look up high-resolution hardware frequencies via SysGetTickFrequency, integrate 14 native smoke test applications.
This commit is contained in:
1 parent
2c4a68a75f
commit
ed3d065abd
156 files changed
+10509
-2643
No files matched your search
@@ -0,0 +1,377 @@
|
||||
// Advisory file-lock smoke test.
|
||||
// Verifies process-owned byte-range conflicts, timeout behavior, and exit cleanup.
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <ntlite/syscall.h>
|
||||
|
||||
#define LOCK_OFFSET 0u
|
||||
#define LOCK_LENGTH 128u
|
||||
#define NONOVERLAP_OFFSET 512u
|
||||
#define CHILD_PREFIX_MAX 32u
|
||||
#define CHILD_PATH_MAX 256u
|
||||
|
||||
// Prints one NUL-terminated string to the kernel console.
|
||||
static void print(const char* s) {
|
||||
SYS_WRITE_CONSOLE_REQUEST req = { SysSvFromCstr(s) };
|
||||
SysWriteConsole(&req);
|
||||
}
|
||||
|
||||
// Prints one unsigned integer to the kernel console.
|
||||
static void print_u64(uint64_t v) {
|
||||
char buf[32];
|
||||
int i = 0;
|
||||
if (v == 0) {
|
||||
print("0");
|
||||
return;
|
||||
}
|
||||
while (v && i < (int)sizeof(buf)) {
|
||||
buf[i++] = (char)('0' + (v % 10));
|
||||
v /= 10;
|
||||
}
|
||||
while (i--) {
|
||||
char c = buf[i];
|
||||
SYS_WRITE_CONSOLE_REQUEST req = { SysSvFromParts(&c, 1) };
|
||||
SysWriteConsole(&req);
|
||||
}
|
||||
}
|
||||
|
||||
// Terminates the current test process.
|
||||
static void exit_now(int32_t code) {
|
||||
for (;;) SysExitProcess(code);
|
||||
}
|
||||
|
||||
// Fails the test with one status value.
|
||||
static void fail_status(const char* what, uint32_t status) {
|
||||
print("file_lock_smoke: ");
|
||||
print(what);
|
||||
print(" status=");
|
||||
print_u64(status);
|
||||
print("\n");
|
||||
exit_now(1);
|
||||
}
|
||||
|
||||
// Fails the test with one plain message.
|
||||
static void fail(const char* what) {
|
||||
print("file_lock_smoke: ");
|
||||
print(what);
|
||||
print("\n");
|
||||
exit_now(1);
|
||||
}
|
||||
|
||||
// Deletes one path if it already exists.
|
||||
static void delete_path(const char* path) {
|
||||
SYS_PATH_REQUEST req = { SysSvFromCstr(path) };
|
||||
(void)SysDeleteFile(&req);
|
||||
}
|
||||
|
||||
// Opens one path with lock rights.
|
||||
static uint32_t open_existing_lockable(const char* path, uint32_t* fileOut) {
|
||||
uint32_t h = 0;
|
||||
SYS_OPEN_FILE_REQUEST req = SysMakeOpenFileRequest(SysSvFromCstr(path),
|
||||
SYS_FILE_ACCESS_QUERY | SYS_FILE_ACCESS_LOCK,
|
||||
SYS_FILE_OPEN_EXISTING,
|
||||
SYS_FILE_FLAG_NONE,
|
||||
&h);
|
||||
uint32_t s = SysOpenFile(&req);
|
||||
if (s == STATUS_SUCCESS)
|
||||
*fileOut = h;
|
||||
return s;
|
||||
}
|
||||
|
||||
// Creates a temporary file on the first writable path that works.
|
||||
static uint32_t create_temp_file(uint32_t* fileOut, const char** pathOut) {
|
||||
static const char* paths[] = {
|
||||
"file_lock_smoke.tmp",
|
||||
"/file_lock_smoke.tmp",
|
||||
"1:/file_lock_smoke.tmp",
|
||||
"0:/file_lock_smoke.tmp",
|
||||
};
|
||||
|
||||
for (size_t i = 0; i < sizeof(paths) / sizeof(paths[0]); i++) {
|
||||
uint32_t h = 0;
|
||||
delete_path(paths[i]);
|
||||
SYS_OPEN_FILE_REQUEST req = SysMakeOpenFileRequest(SysSvFromCstr(paths[i]),
|
||||
SYS_FILE_ACCESS_READ | SYS_FILE_ACCESS_WRITE | SYS_FILE_ACCESS_QUERY | SYS_FILE_ACCESS_LOCK,
|
||||
SYS_FILE_CREATE_NEW,
|
||||
SYS_FILE_FLAG_NONE,
|
||||
&h);
|
||||
uint32_t s = SysOpenFile(&req);
|
||||
if (s == STATUS_SUCCESS) {
|
||||
*fileOut = h;
|
||||
*pathOut = paths[i];
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
}
|
||||
return STATUS_NOT_FOUND;
|
||||
}
|
||||
|
||||
// Locks a range through the public request ABI.
|
||||
static uint32_t lock_range(uint32_t file, uint32_t mode, uint64_t offset, uint64_t length, uint64_t timeoutMs) {
|
||||
SYS_LOCK_FILE_REQUEST req = {
|
||||
sizeof(req),
|
||||
file,
|
||||
mode,
|
||||
SYS_FILE_LOCK_FLAG_NONE,
|
||||
offset,
|
||||
length,
|
||||
timeoutMs,
|
||||
};
|
||||
return SysLockFile(&req);
|
||||
}
|
||||
|
||||
// Unlocks a range through the public request ABI.
|
||||
static uint32_t unlock_range(uint32_t file, uint64_t offset, uint64_t length) {
|
||||
SYS_UNLOCK_FILE_REQUEST req = {
|
||||
sizeof(req),
|
||||
file,
|
||||
SYS_FILE_LOCK_FLAG_NONE,
|
||||
offset,
|
||||
length,
|
||||
};
|
||||
return SysUnlockFile(&req);
|
||||
}
|
||||
|
||||
// Splits the child command line into mode and path.
|
||||
static int parse_child_command(SYS_STRING_VIEW view, char* mode, size_t modeCap, char* path, size_t pathCap) {
|
||||
if (!view.Data || view.Length == 0)
|
||||
return 0;
|
||||
size_t sep = 0;
|
||||
while (sep < view.Length && view.Data[sep] != ':')
|
||||
sep++;
|
||||
if (sep == view.Length || sep + 1 >= view.Length)
|
||||
return 0;
|
||||
if (sep + 1 > modeCap || view.Length - sep > pathCap)
|
||||
return 0;
|
||||
memcpy(mode, view.Data, sep);
|
||||
mode[sep] = '\0';
|
||||
memcpy(path, view.Data + sep + 1, view.Length - sep - 1);
|
||||
path[view.Length - sep - 1] = '\0';
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Returns whether two NUL-terminated strings are equal.
|
||||
static int str_eq(const char* a, const char* b) {
|
||||
return strcmp(a, b) == 0;
|
||||
}
|
||||
|
||||
// Returns whether one mode name belongs to the child command protocol.
|
||||
static int is_child_mode(const char* mode) {
|
||||
return str_eq(mode, "excl-blocked") ||
|
||||
str_eq(mode, "excl-timeout") ||
|
||||
str_eq(mode, "excl-wait") ||
|
||||
str_eq(mode, "shared-ok") ||
|
||||
str_eq(mode, "excl-nonoverlap") ||
|
||||
str_eq(mode, "hold-exit");
|
||||
}
|
||||
|
||||
// Child process entry for cross-process lock checks.
|
||||
static void run_child(SYS_STRING_VIEW commandLine) {
|
||||
char mode[CHILD_PREFIX_MAX];
|
||||
char path[CHILD_PATH_MAX];
|
||||
if (!parse_child_command(commandLine, mode, sizeof(mode), path, sizeof(path)))
|
||||
exit_now(2);
|
||||
|
||||
uint32_t file = 0;
|
||||
uint32_t s = open_existing_lockable(path, &file);
|
||||
if (s != STATUS_SUCCESS)
|
||||
exit_now(3);
|
||||
|
||||
if (str_eq(mode, "excl-blocked")) {
|
||||
s = lock_range(file, SYS_FILE_LOCK_EXCLUSIVE, LOCK_OFFSET, LOCK_LENGTH, 0);
|
||||
SysCloseHandle(file);
|
||||
exit_now(s == STATUS_WOULD_BLOCK ? 0 : 4);
|
||||
}
|
||||
|
||||
if (str_eq(mode, "excl-timeout")) {
|
||||
s = lock_range(file, SYS_FILE_LOCK_EXCLUSIVE, LOCK_OFFSET, LOCK_LENGTH, 20);
|
||||
SysCloseHandle(file);
|
||||
exit_now(s == STATUS_TIMEOUT ? 0 : 5);
|
||||
}
|
||||
|
||||
if (str_eq(mode, "excl-wait")) {
|
||||
s = lock_range(file, SYS_FILE_LOCK_EXCLUSIVE, LOCK_OFFSET, LOCK_LENGTH, SYS_WAIT_TIMEOUT_INFINITE);
|
||||
if (s == STATUS_SUCCESS)
|
||||
(void)unlock_range(file, LOCK_OFFSET, LOCK_LENGTH);
|
||||
SysCloseHandle(file);
|
||||
exit_now(s == STATUS_SUCCESS ? 0 : 6);
|
||||
}
|
||||
|
||||
if (str_eq(mode, "shared-ok")) {
|
||||
s = lock_range(file, SYS_FILE_LOCK_SHARED, LOCK_OFFSET, LOCK_LENGTH, 0);
|
||||
if (s == STATUS_SUCCESS)
|
||||
(void)unlock_range(file, LOCK_OFFSET, LOCK_LENGTH);
|
||||
SysCloseHandle(file);
|
||||
exit_now(s == STATUS_SUCCESS ? 0 : 7);
|
||||
}
|
||||
|
||||
if (str_eq(mode, "excl-nonoverlap")) {
|
||||
s = lock_range(file, SYS_FILE_LOCK_EXCLUSIVE, NONOVERLAP_OFFSET, LOCK_LENGTH, 0);
|
||||
if (s == STATUS_SUCCESS)
|
||||
(void)unlock_range(file, NONOVERLAP_OFFSET, LOCK_LENGTH);
|
||||
SysCloseHandle(file);
|
||||
exit_now(s == STATUS_SUCCESS ? 0 : 8);
|
||||
}
|
||||
|
||||
if (str_eq(mode, "hold-exit")) {
|
||||
s = lock_range(file, SYS_FILE_LOCK_EXCLUSIVE, LOCK_OFFSET, LOCK_LENGTH, 0);
|
||||
SysCloseHandle(file);
|
||||
exit_now(s == STATUS_SUCCESS ? 0 : 9);
|
||||
}
|
||||
|
||||
SysCloseHandle(file);
|
||||
exit_now(10);
|
||||
}
|
||||
|
||||
// Builds a child command line of the form mode:path.
|
||||
static void build_child_command(char* out, size_t outCap, const char* mode, const char* path) {
|
||||
size_t modeLen = strlen(mode);
|
||||
size_t pathLen = strlen(path);
|
||||
if (modeLen + 1 + pathLen + 1 > outCap)
|
||||
fail("child command too long");
|
||||
memcpy(out, mode, modeLen);
|
||||
out[modeLen] = ':';
|
||||
memcpy(out + modeLen + 1, path, pathLen + 1);
|
||||
}
|
||||
|
||||
// Runs this image as a child and requires a zero exit code.
|
||||
static void run_child_expect_success(const char* mode, const char* path) {
|
||||
SYS_PEB* peb = SysGetPeb();
|
||||
if (!peb || !peb->ImagePath.Data || peb->ImagePath.Length == 0)
|
||||
fail("missing image path");
|
||||
|
||||
char command[CHILD_PREFIX_MAX + CHILD_PATH_MAX];
|
||||
build_child_command(command, sizeof(command), mode, path);
|
||||
|
||||
uint32_t child = 0;
|
||||
SYS_CREATE_PROCESS_REQUEST req = {
|
||||
peb->ImagePath,
|
||||
SysSvFromCstr(command),
|
||||
&child,
|
||||
};
|
||||
uint32_t s = SysCreateProcess(&req);
|
||||
if (s != STATUS_SUCCESS) fail_status("create child", s);
|
||||
|
||||
s = SysWait(child, SYS_WAIT_FLAG_NONE);
|
||||
if (s != STATUS_SUCCESS) fail_status("wait child", s);
|
||||
|
||||
int32_t exitCode = 0;
|
||||
s = SysGetProcessExitCode(child, &exitCode);
|
||||
if (s != STATUS_SUCCESS) fail_status("query child", s);
|
||||
SysCloseHandle(child);
|
||||
if (exitCode != 0) {
|
||||
print("file_lock_smoke: child ");
|
||||
print(mode);
|
||||
print(" exited ");
|
||||
print_u64((uint32_t)exitCode);
|
||||
print("\n");
|
||||
exit_now(1);
|
||||
}
|
||||
}
|
||||
|
||||
// Verifies validation paths that should not depend on filesystem state.
|
||||
static void test_validation(uint32_t file) {
|
||||
SYS_LOCK_FILE_REQUEST lockReq = {
|
||||
0,
|
||||
file,
|
||||
SYS_FILE_LOCK_EXCLUSIVE,
|
||||
SYS_FILE_LOCK_FLAG_NONE,
|
||||
LOCK_OFFSET,
|
||||
LOCK_LENGTH,
|
||||
0,
|
||||
};
|
||||
uint32_t s = SysLockFile(&lockReq);
|
||||
if (s != STATUS_INVALID_PARAMETER) fail_status("bad lock size", s);
|
||||
|
||||
lockReq.Size = sizeof(lockReq);
|
||||
lockReq.Length = 0;
|
||||
s = SysLockFile(&lockReq);
|
||||
if (s != STATUS_INVALID_PARAMETER) fail_status("zero lock length", s);
|
||||
|
||||
SYS_UNLOCK_FILE_REQUEST unlockReq = {
|
||||
sizeof(unlockReq),
|
||||
file,
|
||||
SYS_FILE_LOCK_FLAG_NONE,
|
||||
LOCK_OFFSET,
|
||||
LOCK_LENGTH,
|
||||
};
|
||||
s = SysUnlockFile(&unlockReq);
|
||||
if (s != STATUS_NOT_FOUND) fail_status("unlock missing range", s);
|
||||
|
||||
lockReq.Length = LOCK_LENGTH;
|
||||
lockReq.FileHandle = 0xeeeeeeeeu;
|
||||
s = SysLockFile(&lockReq);
|
||||
if (s != STATUS_INVALID_HANDLE) fail_status("bad lock handle", s);
|
||||
}
|
||||
|
||||
// Program entry point.
|
||||
int main(void) {
|
||||
SYS_PEB* peb = SysGetPeb();
|
||||
if (peb && peb->CommandLine.Data && peb->CommandLine.Length != 0) {
|
||||
char mode[CHILD_PREFIX_MAX];
|
||||
char path[CHILD_PATH_MAX];
|
||||
if (parse_child_command(peb->CommandLine, mode, sizeof(mode), path, sizeof(path)) &&
|
||||
is_child_mode(mode)) {
|
||||
run_child(peb->CommandLine);
|
||||
}
|
||||
}
|
||||
|
||||
print("file_lock_smoke: start\n");
|
||||
|
||||
uint32_t file = 0;
|
||||
const char* path = 0;
|
||||
uint32_t s = create_temp_file(&file, &path);
|
||||
if (s != STATUS_SUCCESS) fail_status("create temp", s);
|
||||
|
||||
test_validation(file);
|
||||
|
||||
s = lock_range(file, SYS_FILE_LOCK_EXCLUSIVE, LOCK_OFFSET, LOCK_LENGTH, 0);
|
||||
if (s != STATUS_SUCCESS) fail_status("exclusive parent lock", s);
|
||||
run_child_expect_success("excl-blocked", path);
|
||||
run_child_expect_success("excl-timeout", path);
|
||||
run_child_expect_success("excl-nonoverlap", path);
|
||||
s = unlock_range(file, LOCK_OFFSET, LOCK_LENGTH);
|
||||
if (s != STATUS_SUCCESS) fail_status("exclusive parent unlock", s);
|
||||
|
||||
s = lock_range(file, SYS_FILE_LOCK_SHARED, LOCK_OFFSET, LOCK_LENGTH, 0);
|
||||
if (s != STATUS_SUCCESS) fail_status("shared parent lock", s);
|
||||
run_child_expect_success("shared-ok", path);
|
||||
run_child_expect_success("excl-blocked", path);
|
||||
s = unlock_range(file, LOCK_OFFSET, LOCK_LENGTH);
|
||||
if (s != STATUS_SUCCESS) fail_status("shared parent unlock", s);
|
||||
|
||||
s = lock_range(file, SYS_FILE_LOCK_EXCLUSIVE, LOCK_OFFSET, LOCK_LENGTH, 0);
|
||||
if (s != STATUS_SUCCESS) fail_status("blocking setup lock", s);
|
||||
SYS_PEB* selfPeb = SysGetPeb();
|
||||
if (!selfPeb || !selfPeb->ImagePath.Data || selfPeb->ImagePath.Length == 0) fail("missing self image");
|
||||
char command[CHILD_PREFIX_MAX + CHILD_PATH_MAX];
|
||||
build_child_command(command, sizeof(command), "excl-wait", path);
|
||||
uint32_t child = 0;
|
||||
SYS_CREATE_PROCESS_REQUEST createReq = { selfPeb->ImagePath, SysSvFromCstr(command), &child };
|
||||
s = SysCreateProcess(&createReq);
|
||||
if (s != STATUS_SUCCESS) fail_status("create blocking child", s);
|
||||
SysSleep(30);
|
||||
s = unlock_range(file, LOCK_OFFSET, LOCK_LENGTH);
|
||||
if (s != STATUS_SUCCESS) fail_status("wake unlock", s);
|
||||
s = SysWait(child, SYS_WAIT_FLAG_NONE);
|
||||
if (s != STATUS_SUCCESS) fail_status("wait blocking child", s);
|
||||
int32_t exitCode = 0;
|
||||
s = SysGetProcessExitCode(child, &exitCode);
|
||||
if (s != STATUS_SUCCESS) fail_status("query blocking child", s);
|
||||
SysCloseHandle(child);
|
||||
if (exitCode != 0) fail("blocking child failed");
|
||||
|
||||
run_child_expect_success("hold-exit", path);
|
||||
s = lock_range(file, SYS_FILE_LOCK_EXCLUSIVE, LOCK_OFFSET, LOCK_LENGTH, 0);
|
||||
if (s != STATUS_SUCCESS) fail_status("exit released lock", s);
|
||||
s = unlock_range(file, LOCK_OFFSET, LOCK_LENGTH);
|
||||
if (s != STATUS_SUCCESS) fail_status("exit release cleanup unlock", s);
|
||||
|
||||
SysCloseHandle(file);
|
||||
delete_path(path);
|
||||
|
||||
print("file_lock_smoke: PASS\n");
|
||||
exit_now(0);
|
||||
return 0;
|
||||
}
|
||||
Reference in new issue
Block a user