isolate POSIX builds from the host UCRT, wire lwext4 and ntfs-3g as ext4.sys and ntfs.sys, split the FAT driver into directory/file/async/driver/mount units, add posixsrv and dwm plus input/console/desktop syscall clients and a pile of new smoke targets, rename serial.sys to cereal.sys, name HDA/USB/lwIP/AHCI workers, harden ATA probing/reset/removal around actual hardware deadlines, absent-channel detection, diagnostics and explicit worker lifetime, redo ACPI S5 shutdown around GAS/MMIO and HW-reduced firmware, scan SSDTs for _S5 and deal with Intel SMI sleep trapping, make serial and SMP timeouts use real monotonic deadlines, add thread names and fix terminal thread teardown, scheduler and APC prepare/commit races, make advisory file locks queryable and preserve split ranges and add mutable file append state, add shared-memory pipes with kernel-assisted wait/wake, introduce capability-scoped input and display managers, split surface map rights into read/write, add explicit unmap and replace present with commit/generation/damage tracking while keeping physical presentation under the display manager, decouple native exception classes from Win32 exception numbers and translate them in kernel32, add opaque process startup data, bump the resource protocol to v3 with namespace directories, symlinks and enumeration and service control to v2 with generic READY handle exports, add the new POSIX errno/status surface and rebuild fds around posixsrv-owned shared open descriptions, descriptor-local flags, exact inheritance snapshots, dup/fcntl/record locks and shared-ring pipes, add waitpid, POSIX credentials/groups/sessions/passwd/group lookups and move sockets onto stable open descriptions, port the shells and apps to the new console/input services and surface commit API, teach init to load services.ini and start/supervise RSSRV, DWM and generic services under restricted capabilities while publishing their endpoints through RSSRV, split RSSRV into namespace, volume, display, session, input, console and resource components and make /SystemRoot a real symlink to the independently published system volume, shift the syscall table again, expand lock/resource/surface/POSIX/service/thread smoke coverage, make docs scream ASK QUESTIONS and DISCUSS at LLMs, replace MIT No Attribution with WTFPL+.

This commit is contained in:
maksw committed 2026-09-16 10:33:20 +02:00
1 parent 3dacbd32c8
commit 73abd6f90d
441 files changed
+187073 -6549

No files matched your search

File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+476
View File
@@ -0,0 +1,476 @@
// Init service-configuration parser. Malformed or ambiguous service policy is rejected at boot.
#include "service_config.h"
#include <stddef.h>
#include <string.h>
#include <ntlite/status.h>
#define INIT_SERVICE_KEY_TYPE 0x00000001u
#define INIT_SERVICE_KEY_IMAGE 0x00000002u
#define INIT_SERVICE_KEY_START 0x00000004u
#define INIT_SERVICE_KEY_CRITICAL 0x00000008u
#define INIT_SERVICE_KEY_RESTART 0x00000010u
#define INIT_SERVICE_KEY_READY_TIMEOUT 0x00000020u
#define INIT_SERVICE_KEY_STOP_TIMEOUT 0x00000040u
#define INIT_SERVICE_KEY_REQUIRES 0x00000080u
#define INIT_SERVICE_REQUIRED_KEYS 0x000000FFu
typedef struct INIT_PARSED_SERVICE {
INIT_SERVICE_DESCRIPTOR Descriptor;
char DependencyIds[INIT_SERVICE_DEPENDENCY_MAX][INIT_SERVICE_ID_MAX + 1u];
uint32_t DependencyCount;
uint32_t PresentKeys;
uint32_t SectionLine;
} INIT_PARSED_SERVICE;
static char gServicesIniBuffer[INIT_SERVICES_MAX_FILE_BYTES + 1u];
static INIT_PARSED_SERVICE gParsedServices[INIT_SERVICE_MAX_COUNT];
// Returns whether one byte is horizontal configuration whitespace.
static bool is_space(char c) {
return c == ' ' || c == '\t' || c == '\r';
}
// Trims leading horizontal whitespace in place by returning the first content byte.
static char* trim_left(char* text) {
while (*text && is_space(*text)) ++text;
return text;
}
// Trims trailing horizontal whitespace from one mutable string.
static void trim_right(char* text) {
char* end = text + strlen(text);
while (end > text && is_space(end[-1])) *--end = '\0';
}
// Returns whether one service identifier byte belongs to the deliberately small grammar.
static bool is_service_id_char(char c) {
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
(c >= '0' && c <= '9') || c == '_' || c == '-' || c == '.';
}
// Validates a case-sensitive service identifier used by sections and dependencies.
static bool validate_service_id(const char* id) {
size_t length = id ? strlen(id) : 0;
if (length == 0 || length > INIT_SERVICE_ID_MAX) return false;
for (size_t i = 0; i < length; ++i)
if (!is_service_id_char(id[i])) return false;
return true;
}
// Validates the capability-relative native image path accepted by the process loader.
static bool validate_image_path(const char* path) {
size_t length = path ? strlen(path) : 0;
if (length == 0 || length >= SYS_PATH_MAX || path[0] == '/' || path[length - 1u] == '/')
return false;
const char* component = path;
for (size_t i = 0; i <= length; ++i) {
char c = path[i];
if (c == '\\' || c == ':') return false;
if (c != '/' && c != '\0') continue;
size_t componentLength = (size_t)(&path[i] - component);
if (componentLength == 0 ||
(componentLength == 1 && component[0] == '.') ||
(componentLength == 2 && component[0] == '.' && component[1] == '.'))
return false;
component = &path[i + 1u];
}
return true;
}
// Copies one bounded configuration string into fixed init-owned storage.
static bool copy_bounded(char* destination, size_t capacity, const char* source) {
size_t length = source ? strlen(source) : 0;
if (!destination || capacity == 0 || length >= capacity) return false;
memcpy(destination, source, length + 1u);
return true;
}
// Parses one strict lower-case boolean.
static bool parse_bool(const char* text, bool* valueOut) {
if (!text || !valueOut) return false;
if (strcmp(text, "true") == 0) {
*valueOut = true;
return true;
}
if (strcmp(text, "false") == 0) {
*valueOut = false;
return true;
}
return false;
}
// Parses one bounded unsigned decimal integer without signs or suffixes.
static bool parse_u32(const char* text, uint32_t minimum, uint32_t maximum, uint32_t* valueOut) {
if (!text || !*text || !valueOut) return false;
uint64_t value = 0;
for (const char* p = text; *p; ++p) {
if (*p < '0' || *p > '9') return false;
value = value * 10u + (uint32_t)(*p - '0');
if (value > maximum) return false;
}
if (value < minimum) return false;
*valueOut = (uint32_t)value;
return true;
}
// Looks up a parsed service by its case-sensitive identifier.
static int32_t find_parsed_service(const INIT_PARSED_SERVICE* services,
uint32_t serviceCount,
const char* id) {
for (uint32_t i = 0; i < serviceCount; ++i)
if (strcmp(services[i].Descriptor.Id, id) == 0) return (int32_t)i;
return -1;
}
// Parses the comma-separated hard dependency list for one service.
static bool parse_requires(char* value, INIT_PARSED_SERVICE* service) {
service->DependencyCount = 0;
if (!*value) return true;
char* cursor = value;
for (;;) {
char* comma = strchr(cursor, ',');
if (comma) *comma = '\0';
char* id = trim_left(cursor);
trim_right(id);
if (!validate_service_id(id) || service->DependencyCount >= INIT_SERVICE_DEPENDENCY_MAX)
return false;
for (uint32_t i = 0; i < service->DependencyCount; ++i)
if (strcmp(service->DependencyIds[i], id) == 0) return false;
if (!copy_bounded(service->DependencyIds[service->DependencyCount],
sizeof(service->DependencyIds[0]), id))
return false;
++service->DependencyCount;
if (!comma) break;
cursor = comma + 1;
if (!*cursor) return false;
}
return true;
}
// Applies one known key to the current service and rejects duplicates or invalid values.
static bool parse_service_key(INIT_PARSED_SERVICE* service, char* key, char* value) {
uint32_t bit = 0;
if (strcmp(key, "type") == 0) {
bit = INIT_SERVICE_KEY_TYPE;
if (strcmp(value, "resource") == 0) service->Descriptor.Type = InitServiceTypeResource;
else if (strcmp(value, "dwm") == 0) service->Descriptor.Type = InitServiceTypeDwm;
else if (strcmp(value, "generic") == 0) service->Descriptor.Type = InitServiceTypeGeneric;
else return false;
} else if (strcmp(key, "image") == 0) {
bit = INIT_SERVICE_KEY_IMAGE;
if (!validate_image_path(value) ||
!copy_bounded(service->Descriptor.ImagePath, sizeof(service->Descriptor.ImagePath), value))
return false;
} else if (strcmp(key, "start") == 0) {
bit = INIT_SERVICE_KEY_START;
if (strcmp(value, "auto") == 0) service->Descriptor.StartPolicy = InitServiceStartAuto;
else if (strcmp(value, "disabled") == 0) service->Descriptor.StartPolicy = InitServiceStartDisabled;
else return false;
} else if (strcmp(key, "critical") == 0) {
bit = INIT_SERVICE_KEY_CRITICAL;
if (!parse_bool(value, &service->Descriptor.Critical)) return false;
} else if (strcmp(key, "restart") == 0) {
bit = INIT_SERVICE_KEY_RESTART;
if (strcmp(value, "never") == 0) service->Descriptor.RestartPolicy = InitServiceRestartNever;
else if (strcmp(value, "on-failure") == 0) service->Descriptor.RestartPolicy = InitServiceRestartOnFailure;
else return false;
} else if (strcmp(key, "ready_timeout_ms") == 0) {
bit = INIT_SERVICE_KEY_READY_TIMEOUT;
if (!parse_u32(value, 1u, 600000u, &service->Descriptor.ReadyTimeoutMilliseconds)) return false;
} else if (strcmp(key, "stop_timeout_ms") == 0) {
bit = INIT_SERVICE_KEY_STOP_TIMEOUT;
if (!parse_u32(value, 1u, 600000u, &service->Descriptor.StopTimeoutMilliseconds)) return false;
} else if (strcmp(key, "requires") == 0) {
bit = INIT_SERVICE_KEY_REQUIRES;
if (!parse_requires(value, service)) return false;
} else {
return false;
}
if (service->PresentKeys & bit) return false;
service->PresentKeys |= bit;
return true;
}
// Parses the strict services.ini grammar into temporary fixed storage.
static KSTATUS parse_services_ini(char* buffer, uint32_t* serviceCountOut, uint32_t* errorLineOut) {
uint32_t serviceCount = 0;
INIT_PARSED_SERVICE* current = NULL;
uint32_t lineNumber = 0;
char* line = buffer;
while (*line) {
++lineNumber;
char* end = strchr(line, '\n');
if (!end) end = line + strlen(line);
char saved = *end;
*end = '\0';
char* text = trim_left(line);
trim_right(text);
bool valid = true;
if (!*text || *text == ';' || *text == '#') {
// Blank and whole-line comments are the only ignored line forms.
} else if (*text == '[') {
size_t length = strlen(text);
if (length < 11u || text[length - 1u] != ']' || strchr(text + 1, ']') != &text[length - 1u]) {
valid = false;
} else {
text[length - 1u] = '\0';
const char prefix[] = "service.";
if (strncmp(text + 1, prefix, sizeof(prefix) - 1u) != 0) {
valid = false;
} else {
char* id = text + 1u + sizeof(prefix) - 1u;
if (!validate_service_id(id) || serviceCount >= INIT_SERVICE_MAX_COUNT ||
find_parsed_service(gParsedServices, serviceCount, id) >= 0) {
valid = false;
} else {
current = &gParsedServices[serviceCount++];
memset(current, 0, sizeof(*current));
current->SectionLine = lineNumber;
if (!copy_bounded(current->Descriptor.Id, sizeof(current->Descriptor.Id), id))
valid = false;
}
}
}
} else {
if (!current) {
valid = false;
} else {
char* equals = strchr(text, '=');
if (!equals || strchr(equals + 1, '=')) {
valid = false;
} else {
*equals = '\0';
char* key = text;
char* value = trim_left(equals + 1);
trim_right(key);
trim_right(value);
if (!*key || !parse_service_key(current, key, value)) valid = false;
}
}
}
*end = saved;
if (!valid) {
if (errorLineOut) *errorLineOut = lineNumber;
return NTLSTATUS_INVALID_PARAMETER;
}
line = saved ? end + 1u : end;
}
if (serviceCount == 0) return NTLSTATUS_INVALID_PARAMETER;
*serviceCountOut = serviceCount;
return NTLSTATUS_SUCCESS;
}
// Resolves dependency identifiers, validates invariants, and computes dependency-first order.
static KSTATUS validate_services(uint32_t serviceCount,
INIT_SERVICE_CONFIG* configOut,
uint32_t* errorLineOut) {
uint32_t resourceCount = 0;
uint16_t resourceIndex = 0;
uint32_t dwmCount = 0;
uint16_t dwmIndex = UINT16_MAX;
memset(configOut, 0, sizeof(*configOut));
configOut->ResourceServiceIndex = UINT16_MAX;
configOut->DwmServiceIndex = UINT16_MAX;
for (uint32_t i = 0; i < serviceCount; ++i) {
INIT_PARSED_SERVICE* parsed = &gParsedServices[i];
if (parsed->PresentKeys != INIT_SERVICE_REQUIRED_KEYS) {
if (errorLineOut) *errorLineOut = parsed->SectionLine;
return NTLSTATUS_INVALID_PARAMETER;
}
INIT_SERVICE_DESCRIPTOR* descriptor = &configOut->Services[i];
*descriptor = parsed->Descriptor;
descriptor->DependencyCount = parsed->DependencyCount;
for (uint32_t d = 0; d < parsed->DependencyCount; ++d) {
int32_t dependency = find_parsed_service(gParsedServices, serviceCount, parsed->DependencyIds[d]);
if (dependency < 0 || (uint32_t)dependency == i) {
if (errorLineOut) *errorLineOut = parsed->SectionLine;
return NTLSTATUS_INVALID_PARAMETER;
}
descriptor->Dependencies[d] = (uint16_t)dependency;
}
if (descriptor->Type == InitServiceTypeResource) {
++resourceCount;
resourceIndex = (uint16_t)i;
} else if (descriptor->Type == InitServiceTypeDwm) {
++dwmCount;
dwmIndex = (uint16_t)i;
}
}
if (resourceCount != 1u || dwmCount > 1u) return NTLSTATUS_INVALID_PARAMETER;
INIT_SERVICE_DESCRIPTOR* resource = &configOut->Services[resourceIndex];
if (resource->StartPolicy != InitServiceStartAuto || !resource->Critical ||
resource->RestartPolicy != InitServiceRestartNever || resource->DependencyCount != 0u) {
if (errorLineOut) *errorLineOut = gParsedServices[resourceIndex].SectionLine;
return NTLSTATUS_INVALID_PARAMETER;
}
if (dwmCount == 1u) {
INIT_SERVICE_DESCRIPTOR* dwm = &configOut->Services[dwmIndex];
bool requiresResource = false;
for (uint32_t d = 0; d < dwm->DependencyCount; ++d)
if (dwm->Dependencies[d] == resourceIndex) requiresResource = true;
if (dwm->StartPolicy == InitServiceStartAuto &&
(dwm->Critical || dwm->RestartPolicy != InitServiceRestartNever || !requiresResource)) {
if (errorLineOut) *errorLineOut = gParsedServices[dwmIndex].SectionLine;
return NTLSTATUS_INVALID_PARAMETER;
}
}
// The generic adapter publishes READY listeners through RSSRV, so automatic generic services require it.
for (uint32_t i = 0; i < serviceCount; ++i) {
INIT_SERVICE_DESCRIPTOR* generic = &configOut->Services[i];
if (generic->Type != InitServiceTypeGeneric || generic->StartPolicy != InitServiceStartAuto) continue;
bool requiresResource = false;
for (uint32_t d = 0; d < generic->DependencyCount; ++d)
if (generic->Dependencies[d] == resourceIndex) requiresResource = true;
if (generic->Critical || generic->RestartPolicy != InitServiceRestartNever || !requiresResource) {
if (errorLineOut) *errorLineOut = gParsedServices[i].SectionLine;
return NTLSTATUS_INVALID_PARAMETER;
}
}
for (uint32_t i = 0; i < serviceCount; ++i) {
INIT_SERVICE_DESCRIPTOR* descriptor = &configOut->Services[i];
if (descriptor->StartPolicy != InitServiceStartAuto) continue;
for (uint32_t d = 0; d < descriptor->DependencyCount; ++d) {
INIT_SERVICE_DESCRIPTOR* dependency = &configOut->Services[descriptor->Dependencies[d]];
if (dependency->StartPolicy == InitServiceStartDisabled) {
if (errorLineOut) *errorLineOut = gParsedServices[i].SectionLine;
return NTLSTATUS_INVALID_PARAMETER;
}
}
}
uint32_t indegree[INIT_SERVICE_MAX_COUNT] = {0};
bool emitted[INIT_SERVICE_MAX_COUNT] = {0};
for (uint32_t i = 0; i < serviceCount; ++i)
indegree[i] = configOut->Services[i].DependencyCount;
uint32_t emittedCount = 0;
while (emittedCount < serviceCount) {
int32_t next = -1;
for (uint32_t i = 0; i < serviceCount; ++i) {
if (!emitted[i] && indegree[i] == 0u) {
next = (int32_t)i;
break;
}
}
if (next < 0) {
if (errorLineOut) {
for (uint32_t i = 0; i < serviceCount; ++i) {
if (!emitted[i]) {
*errorLineOut = gParsedServices[i].SectionLine;
break;
}
}
}
return NTLSTATUS_DEADLOCK;
}
emitted[next] = true;
if (configOut->Services[next].StartPolicy == InitServiceStartAuto)
configOut->StartupOrder[configOut->StartupCount++] = (uint16_t)next;
++emittedCount;
for (uint32_t i = 0; i < serviceCount; ++i) {
if (emitted[i]) continue;
INIT_SERVICE_DESCRIPTOR* descriptor = &configOut->Services[i];
for (uint32_t d = 0; d < descriptor->DependencyCount; ++d) {
if (descriptor->Dependencies[d] == (uint16_t)next) {
--indegree[i];
break;
}
}
}
}
configOut->ServiceCount = serviceCount;
configOut->ResourceServiceIndex = resourceIndex;
configOut->DwmServiceIndex = dwmIndex;
return NTLSTATUS_SUCCESS;
}
// Reads the mandatory services.ini file from the bootstrap system volume.
static KSTATUS read_services_ini(SYS_HANDLE systemVolume, size_t* sizeOut) {
SYS_HANDLE file = SYS_HANDLE_INVALID;
SYS_OPEN_FILE_AT_REQUEST open = SysMakeOpenFileAtRequest(systemVolume,
SYS_SV_LITERAL(INIT_SERVICES_PATH),
SYS_FILE_ACCESS_READ | SYS_FILE_ACCESS_QUERY,
SYS_FILE_OPEN_EXISTING,
SYS_FILE_FLAG_NONE,
&file);
KSTATUS status = SysOpenFileAt(&open);
if (!KSUCCESS(status)) return status;
uint64_t fileSize = 0;
status = SysGetFileSize(file, &fileSize);
if (!KSUCCESS(status)) {
(void)SysCloseHandle(file);
return status;
}
if (fileSize == 0u || fileSize > INIT_SERVICES_MAX_FILE_BYTES) {
(void)SysCloseHandle(file);
return fileSize > INIT_SERVICES_MAX_FILE_BYTES ? NTLSTATUS_LIMIT_REACHED : NTLSTATUS_INVALID_PARAMETER;
}
size_t total = 0;
while (total < (size_t)fileSize) {
size_t transferred = 0;
SYS_FILE_IO_REQUEST read = SysMakeFileIoRequest(file,
gServicesIniBuffer + total,
(size_t)fileSize - total,
&transferred);
status = SysReadFile(&read);
if (!KSUCCESS(status) || transferred == 0u) {
(void)SysCloseHandle(file);
return KSUCCESS(status) ? NTLSTATUS_FAILURE : status;
}
total += transferred;
}
(void)SysCloseHandle(file);
for (size_t i = 0; i < total; ++i)
if (gServicesIniBuffer[i] == '\0') return NTLSTATUS_INVALID_PARAMETER;
gServicesIniBuffer[total] = '\0';
*sizeOut = total;
return NTLSTATUS_SUCCESS;
}
// Loads, strictly parses, and validates services.ini beneath the bootstrap system volume.
KSTATUS InitLoadServiceConfig(SYS_HANDLE systemVolume,
INIT_SERVICE_CONFIG* configOut,
uint32_t* errorLineOut) {
if (systemVolume == SYS_HANDLE_INVALID || !configOut) return NTLSTATUS_INVALID_PARAMETER;
if (errorLineOut) *errorLineOut = 0;
memset(gParsedServices, 0, sizeof(gParsedServices));
size_t size = 0;
KSTATUS status = read_services_ini(systemVolume, &size);
if (!KSUCCESS(status)) return status;
(void)size;
uint32_t serviceCount = 0;
status = parse_services_ini(gServicesIniBuffer, &serviceCount, errorLineOut);
if (!KSUCCESS(status)) return status;
return validate_services(serviceCount, configOut, errorLineOut);
}
+56
View File
@@ -0,0 +1,56 @@
// Init service-configuration declarations. services.ini is strict policy owned by init.
#pragma once
#include <stdbool.h>
#include <stdint.h>
#include <ntlite/syscall.h>
#define INIT_SERVICES_PATH "system/services.ini"
#define INIT_SERVICES_MAX_FILE_BYTES (64u * 1024u)
#define INIT_SERVICE_MAX_COUNT 32u
#define INIT_SERVICE_ID_MAX 31u
#define INIT_SERVICE_DEPENDENCY_MAX 8u
typedef enum INIT_SERVICE_TYPE {
InitServiceTypeResource = 0,
InitServiceTypeDwm,
InitServiceTypeGeneric,
} INIT_SERVICE_TYPE;
typedef enum INIT_SERVICE_START_POLICY {
InitServiceStartAuto = 0,
InitServiceStartDisabled,
} INIT_SERVICE_START_POLICY;
typedef enum INIT_SERVICE_RESTART_POLICY {
InitServiceRestartNever = 0,
InitServiceRestartOnFailure,
} INIT_SERVICE_RESTART_POLICY;
typedef struct INIT_SERVICE_DESCRIPTOR {
char Id[INIT_SERVICE_ID_MAX + 1u];
char ImagePath[SYS_PATH_MAX];
INIT_SERVICE_TYPE Type;
INIT_SERVICE_START_POLICY StartPolicy;
bool Critical;
INIT_SERVICE_RESTART_POLICY RestartPolicy;
uint32_t ReadyTimeoutMilliseconds;
uint32_t StopTimeoutMilliseconds;
uint32_t DependencyCount;
uint16_t Dependencies[INIT_SERVICE_DEPENDENCY_MAX];
} INIT_SERVICE_DESCRIPTOR;
typedef struct INIT_SERVICE_CONFIG {
INIT_SERVICE_DESCRIPTOR Services[INIT_SERVICE_MAX_COUNT];
uint16_t StartupOrder[INIT_SERVICE_MAX_COUNT];
uint32_t ServiceCount;
uint32_t StartupCount;
uint16_t ResourceServiceIndex;
uint16_t DwmServiceIndex;
} INIT_SERVICE_CONFIG;
// Loads, strictly parses, and validates services.ini beneath the bootstrap system volume.
KSTATUS InitLoadServiceConfig(SYS_HANDLE systemVolume,
INIT_SERVICE_CONFIG* configOut,
uint32_t* errorLineOut);
File diff suppressed because it is too large. Load diff
+433
View File
@@ -0,0 +1,433 @@
// RSSRV native console. One logical console owns text output policy and consumes RSSRV logical input.
#include "rssrv_internal.h"
#include <stdlib.h>
#include <string.h>
#define RSSRV_EVENT_MANUAL_RESET 1u
#define RSSRV_CONSOLE_KEY_QUEUE_MASK (RSSRV_CONSOLE_KEY_QUEUE_SIZE - 1u)
typedef struct RSSRV_CONSOLE_SESSION {
RSSRV_CONSOLE_STATE* Console;
SYS_HANDLE Endpoint;
} RSSRV_CONSOLE_SESSION;
// Acquires the console mutation lock.
static KSTATUS RssrvConsoleLock(RSSRV_CONSOLE_STATE* console) {
if (!console || console->Mutex == SYS_HANDLE_INVALID) return NTLSTATUS_INVALID_PARAMETER;
return SysWait(console->Mutex, SYS_WAIT_FLAG_NONE);
}
// Releases the console mutation lock.
static void RssrvConsoleUnlock(RSSRV_CONSOLE_STATE* console) {
if (console && console->Mutex != SYS_HANDLE_INVALID) (void)SysReleaseMutex(console->Mutex);
}
// Refreshes the console-input availability event while the console mutex is held.
static void RssrvConsoleRefreshInputEventLocked(RSSRV_CONSOLE_STATE* console) {
if (console->KeyHead != console->KeyTail) (void)SysSignalEvent(console->KeyEvent);
else (void)SysClearEvent(console->KeyEvent);
}
// Receives one focused logical key record from Session 0.
static void RssrvConsoleRouteKey(void* context, const INPUT_KEY_RECORD* record) {
RSSRV_CONSOLE_STATE* console = (RSSRV_CONSOLE_STATE*)context;
if (!console || !record || !KSUCCESS(RssrvConsoleLock(console))) return;
uint32_t next = (console->KeyTail + 1u) & RSSRV_CONSOLE_KEY_QUEUE_MASK;
if (next != console->KeyHead) {
console->KeyQueue[console->KeyTail] = *record;
console->KeyTail = next;
RssrvConsoleRefreshInputEventLocked(console);
}
RssrvConsoleUnlock(console);
}
// Receives pointer routing for the console target. The text console does not consume pointer events yet.
static void RssrvConsoleRouteMouse(void* context, const INPUT_MOUSE_EVENT* event, int32_t x, int32_t y) {
(void)context;
(void)event;
(void)x;
(void)y;
}
// Reads or peeks one keyboard record routed specifically to this console.
static KSTATUS RssrvConsoleReadInput(RSSRV_CONSOLE_STATE* console,
int peek,
uint64_t timeoutMilliseconds,
INPUT_KEY_RECORD* recordOut) {
if (!console || !recordOut) return NTLSTATUS_INVALID_PARAMETER;
for (;;) {
KSTATUS status = RssrvConsoleLock(console);
if (!KSUCCESS(status)) return status;
if (console->KeyHead != console->KeyTail) {
*recordOut = console->KeyQueue[console->KeyHead];
if (!peek) {
console->KeyHead = (console->KeyHead + 1u) & RSSRV_CONSOLE_KEY_QUEUE_MASK;
RssrvConsoleRefreshInputEventLocked(console);
}
RssrvConsoleUnlock(console);
return NTLSTATUS_SUCCESS;
}
RssrvConsoleUnlock(console);
if (timeoutMilliseconds == 0) return NTLSTATUS_WOULD_BLOCK;
status = SysWaitSingle(console->KeyEvent, SYS_WAIT_FLAG_NONE, timeoutMilliseconds);
if (!KSUCCESS(status)) return status;
}
}
// Discards all keyboard records already routed to this console.
static KSTATUS RssrvConsoleFlushInput(RSSRV_CONSOLE_STATE* console, uint32_t* discardedOut) {
if (!console) return NTLSTATUS_INVALID_PARAMETER;
KSTATUS status = RssrvConsoleLock(console);
if (!KSUCCESS(status)) return status;
uint32_t discarded = (console->KeyTail - console->KeyHead) & RSSRV_CONSOLE_KEY_QUEUE_MASK;
console->KeyHead = console->KeyTail;
RssrvConsoleRefreshInputEventLocked(console);
RssrvConsoleUnlock(console);
if (discardedOut) *discardedOut = discarded;
return NTLSTATUS_SUCCESS;
}
// Records one successful visible console mutation while holding the console lock.
static void RssrvConsoleChangedLocked(RSSRV_CONSOLE_STATE* console) {
console->Generation++;
if (console->Generation == 0) console->Generation = 1;
}
// Writes one console chunk in serialized order.
static KSTATUS RssrvConsoleWrite(RSSRV_CONSOLE_STATE* console,
const char* data,
uint32_t length,
uint32_t* writtenOut) {
if (writtenOut) *writtenOut = 0;
if (!console || length > NTL_CONSOLE_WRITE_MAX || (length && !data))
return NTLSTATUS_INVALID_PARAMETER;
KSTATUS status = RssrvConsoleLock(console);
if (!KSUCCESS(status)) return status;
status = RssrvDisplayWriteConsole(console->Display, data, length);
if (KSUCCESS(status) && length) RssrvConsoleChangedLocked(console);
RssrvConsoleUnlock(console);
if (KSUCCESS(status) && writtenOut) *writtenOut = length;
return status;
}
// Queries dimensions from the backing display and snapshots RSSRV's console generation.
static KSTATUS RssrvConsoleQueryInfo(RSSRV_CONSOLE_STATE* console, NTL_CONSOLE_INFO* infoOut) {
if (!console || !infoOut) return NTLSTATUS_INVALID_PARAMETER;
KSTATUS status = RssrvConsoleLock(console);
if (!KSUCCESS(status)) return status;
SYS_CONSOLE_INFO consoleInfo;
SYS_DISPLAY_INFO displayInfo;
memset(&consoleInfo, 0, sizeof(consoleInfo));
memset(&displayInfo, 0, sizeof(displayInfo));
status = RssrvDisplayQueryConsoleInfo(console->Display, &consoleInfo);
if (KSUCCESS(status)) status = RssrvDisplayQueryInfo(console->Display, &displayInfo);
if (KSUCCESS(status)) {
memset(infoOut, 0, sizeof(*infoOut));
infoOut->Size = sizeof(*infoOut);
infoOut->Flags = NTL_CONSOLE_FLAG_NONE;
infoOut->Columns = consoleInfo.Columns;
infoOut->Rows = consoleInfo.Rows;
infoOut->CursorX = consoleInfo.CursorX;
infoOut->CursorY = consoleInfo.CursorY;
infoOut->Attribute = consoleInfo.Attribute;
infoOut->PixelWidth = displayInfo.Mode.Width;
infoOut->PixelHeight = displayInfo.Mode.Height;
infoOut->Generation = console->Generation;
}
RssrvConsoleUnlock(console);
return status;
}
// Sends one fixed console reply.
static KSTATUS RssrvConsoleReply(SYS_HANDLE endpoint,
uint32_t messageId,
uint32_t operation,
KSTATUS status,
uint32_t written,
const INPUT_KEY_RECORD* input,
const NTL_CONSOLE_INFO* consoleInfo) {
NTL_CONSOLE_REPLY reply;
memset(&reply, 0, sizeof(reply));
reply.Size = sizeof(reply);
reply.Version = NTL_CONSOLE_PROTOCOL_VERSION;
reply.Operation = operation;
reply.Flags = NTL_CONSOLE_FLAG_NONE;
reply.Status = status;
reply.Written = written;
if (input) reply.Input = *input;
if (consoleInfo) reply.Info = *consoleInfo;
SYS_REPLY_PORT_REQUEST request;
memset(&request, 0, sizeof(request));
request.Size = sizeof(request);
request.EndpointHandle = endpoint;
request.MessageId = messageId;
request.Flags = SYS_PORT_FLAG_NONE;
request.Payload = &reply;
request.PayloadLength = sizeof(reply);
return SysReplyPort(&request);
}
// Handles one mutation request that must be serialized through the console lock.
static KSTATUS RssrvConsoleMutate(RSSRV_CONSOLE_STATE* console,
const NTL_CONSOLE_REQUEST* request,
uint32_t* writtenOut) {
if (writtenOut) *writtenOut = 0;
KSTATUS status = RssrvConsoleLock(console);
if (!KSUCCESS(status)) return status;
size_t written = 0;
switch (request->Operation) {
case NTL_CONSOLE_OP_SET_CURSOR:
status = RssrvDisplaySetConsoleCursor(console->Display,
request->Data.Cursor.X,
request->Data.Cursor.Y);
break;
case NTL_CONSOLE_OP_FILL_CHARACTER:
if (request->Data.FillCharacter.Reserved[0] || request->Data.FillCharacter.Reserved[1] ||
request->Data.FillCharacter.Reserved[2]) {
status = NTLSTATUS_INVALID_PARAMETER;
break;
}
status = RssrvDisplayFillConsoleCharacter(console->Display,
request->Data.FillCharacter.Character,
request->Data.FillCharacter.X,
request->Data.FillCharacter.Y,
request->Data.FillCharacter.Length,
&written);
break;
case NTL_CONSOLE_OP_FILL_ATTRIBUTE:
if (request->Data.FillAttribute.Reserved != 0) {
status = NTLSTATUS_INVALID_PARAMETER;
break;
}
status = RssrvDisplayFillConsoleAttribute(console->Display,
request->Data.FillAttribute.Attribute,
request->Data.FillAttribute.X,
request->Data.FillAttribute.Y,
request->Data.FillAttribute.Length,
&written);
break;
case NTL_CONSOLE_OP_PUT_CHARACTER:
if (request->Data.PutCharacter.Reserved[0] || request->Data.PutCharacter.Reserved[1] ||
request->Data.PutCharacter.Reserved[2]) {
status = NTLSTATUS_INVALID_PARAMETER;
break;
}
status = RssrvDisplayPutConsoleCharacter(console->Display,
request->Data.PutCharacter.Character,
request->Data.PutCharacter.X,
request->Data.PutCharacter.Y,
request->Data.PutCharacter.Foreground,
request->Data.PutCharacter.Background);
break;
case NTL_CONSOLE_OP_CLEAR:
status = RssrvDisplayClearConsole(console->Display, request->Data.Clear.Color);
break;
default:
status = NTLSTATUS_INVALID_PARAMETER;
break;
}
if (KSUCCESS(status)) RssrvConsoleChangedLocked(console);
RssrvConsoleUnlock(console);
if (writtenOut) *writtenOut = written > UINT32_MAX ? UINT32_MAX : (uint32_t)written;
return status;
}
// Serves one connected logical-console client.
static void RssrvConsoleSessionThread(uintptr_t argument) {
RSSRV_CONSOLE_SESSION* session = (RSSRV_CONSOLE_SESSION*)argument;
if (!session || !session->Console) {
if (session) free(session);
SysExitThread(1);
}
RSSRV_CONSOLE_STATE* console = session->Console;
SYS_HANDLE endpoint = session->Endpoint;
free(session);
RssrvSetCurrentThreadName("rssrv.console.session");
for (;;) {
NTL_CONSOLE_REQUEST request;
memset(&request, 0, sizeof(request));
SYS_PORT_MESSAGE_INFO info;
memset(&info, 0, sizeof(info));
info.Size = sizeof(info);
SYS_RECEIVE_PORT_REQUEST receive;
memset(&receive, 0, sizeof(receive));
receive.Size = sizeof(receive);
receive.EndpointHandle = endpoint;
receive.Flags = SYS_PORT_FLAG_NONE;
receive.Payload = &request;
receive.PayloadCapacity = sizeof(request);
receive.InfoOut = &info;
receive.TimeoutMilliseconds = SYS_PORT_TIMEOUT_INFINITE;
KSTATUS status = SysReceivePort(&receive);
if (status == NTLSTATUS_PORT_DISCONNECTED) break;
if (!KSUCCESS(status)) break;
if (info.Type != SYS_PORT_MESSAGE_REQUEST || info.PayloadLength != sizeof(request) ||
info.HandleCount != 0 || request.Size != sizeof(request) ||
request.Version != NTL_CONSOLE_PROTOCOL_VERSION || request.Flags != NTL_CONSOLE_FLAG_NONE) {
(void)RssrvConsoleReply(endpoint, info.MessageId, request.Operation,
NTLSTATUS_INVALID_PARAMETER, 0, 0, 0);
continue;
}
uint32_t written = 0;
INPUT_KEY_RECORD input;
NTL_CONSOLE_INFO consoleInfo;
memset(&input, 0, sizeof(input));
memset(&consoleInfo, 0, sizeof(consoleInfo));
switch (request.Operation) {
case NTL_CONSOLE_OP_WRITE:
if (request.Data.Write.Length > NTL_CONSOLE_WRITE_MAX) status = NTLSTATUS_INVALID_PARAMETER;
else status = RssrvConsoleWrite(console, request.Data.Write.Data,
request.Data.Write.Length, &written);
(void)RssrvConsoleReply(endpoint, info.MessageId, request.Operation,
status, written, 0, 0);
break;
case NTL_CONSOLE_OP_QUERY_INFO:
status = RssrvConsoleQueryInfo(console, &consoleInfo);
(void)RssrvConsoleReply(endpoint, info.MessageId, request.Operation,
status, 0, 0, KSUCCESS(status) ? &consoleInfo : 0);
break;
case NTL_CONSOLE_OP_SET_CURSOR:
case NTL_CONSOLE_OP_FILL_CHARACTER:
case NTL_CONSOLE_OP_FILL_ATTRIBUTE:
case NTL_CONSOLE_OP_PUT_CHARACTER:
case NTL_CONSOLE_OP_CLEAR:
status = RssrvConsoleMutate(console, &request, &written);
(void)RssrvConsoleReply(endpoint, info.MessageId, request.Operation,
status, written, 0, 0);
break;
case NTL_CONSOLE_OP_READ_INPUT:
status = RssrvConsoleReadInput(console, 0,
request.TimeoutMilliseconds, &input);
(void)RssrvConsoleReply(endpoint, info.MessageId, request.Operation,
status, 0, KSUCCESS(status) ? &input : 0, 0);
break;
case NTL_CONSOLE_OP_PEEK_INPUT:
status = RssrvConsoleReadInput(console, 1,
request.TimeoutMilliseconds, &input);
(void)RssrvConsoleReply(endpoint, info.MessageId, request.Operation,
status, 0, KSUCCESS(status) ? &input : 0, 0);
break;
case NTL_CONSOLE_OP_FLUSH_INPUT:
status = RssrvConsoleFlushInput(console, &written);
(void)RssrvConsoleReply(endpoint, info.MessageId, request.Operation,
status, written, 0, 0);
break;
default:
(void)RssrvConsoleReply(endpoint, info.MessageId, request.Operation,
NTLSTATUS_INVALID_PARAMETER, 0, 0, 0);
break;
}
}
(void)SysCloseHandle(endpoint);
SysExitThread(0);
}
// Accepts console sessions and gives each connection one worker thread.
static void RssrvConsoleListenerThread(uintptr_t argument) {
RSSRV_CONSOLE_STATE* console = (RSSRV_CONSOLE_STATE*)argument;
RssrvSetCurrentThreadName("rssrv.console.listen");
if (!console) SysExitThread(1);
for (;;) {
SYS_HANDLE endpoint = SYS_HANDLE_INVALID;
SYS_ACCEPT_PORT_REQUEST accept;
memset(&accept, 0, sizeof(accept));
accept.Size = sizeof(accept);
accept.ListenerHandle = console->Listener;
accept.Flags = SYS_PORT_ACCEPT_FLAG_NONE;
accept.TimeoutMilliseconds = SYS_PORT_TIMEOUT_INFINITE;
accept.EndpointHandleOut = &endpoint;
KSTATUS status = SysAcceptPort(&accept);
if (!KSUCCESS(status)) RssrvExit(16);
RSSRV_CONSOLE_SESSION* session = (RSSRV_CONSOLE_SESSION*)malloc(sizeof(*session));
if (!session) {
(void)SysCloseHandle(endpoint);
continue;
}
session->Console = console;
session->Endpoint = endpoint;
SYS_CREATE_THREAD_REQUEST create;
memset(&create, 0, sizeof(create));
create.Size = sizeof(create);
create.Entry = (uintptr_t)RssrvConsoleSessionThread;
create.Argument = (uintptr_t)session;
create.Priority = 1;
create.Flags = SYS_CREATE_THREAD_FLAG_NONE;
KSTATUS createStatus = SysCreateThread(&create);
if (!KSUCCESS(createStatus)) {
free(session);
(void)SysCloseHandle(endpoint);
}
}
}
// Initializes Session 0's native console object and registers its focused-input target.
KSTATUS RssrvConsoleInitialize(RSSRV_CONSOLE_STATE* console,
RSSRV_NAMESPACE* namespaceState,
RSSRV_DISPLAY_STATE* display,
RSSRV_SESSION_STATE* session) {
if (!console || !namespaceState || !display || !session)
return NTLSTATUS_INVALID_PARAMETER;
memset(console, 0, sizeof(*console));
console->Mutex = SYS_HANDLE_INVALID;
console->KeyEvent = SYS_HANDLE_INVALID;
console->Listener = SYS_HANDLE_INVALID;
console->Namespace = namespaceState;
console->Display = display;
console->Session = session;
console->Generation = 1;
KSTATUS status = SysCreateMutex(&console->Mutex);
if (!KSUCCESS(status)) return status;
status = SysCreateEvent(RSSRV_EVENT_MANUAL_RESET, 0, &console->KeyEvent);
if (!KSUCCESS(status)) return status;
status = RssrvSessionRegisterInputTarget(session, &console->InputTarget,
RSSRV_INPUT_TARGET_KIND_CONSOLE, console,
RssrvConsoleRouteKey, RssrvConsoleRouteMouse);
if (!KSUCCESS(status)) return status;
return RssrvSessionActivateInputTarget(session, &console->InputTarget);
}
// Publishes /Services/Console and starts its client listener.
KSTATUS RssrvConsoleStart(RSSRV_CONSOLE_STATE* console) {
if (!console || !console->Namespace || !console->Display || !console->Session ||
console->Listener != SYS_HANDLE_INVALID)
return NTLSTATUS_INVALID_PARAMETER;
SYS_CREATE_PORT_REQUEST createPort;
memset(&createPort, 0, sizeof(createPort));
createPort.Size = sizeof(createPort);
createPort.Flags = SYS_PORT_FLAG_NONE;
createPort.Backlog = RSSRV_LISTENER_BACKLOG;
createPort.HandleOut = &console->Listener;
KSTATUS status = SysCreatePort(&createPort);
if (!KSUCCESS(status)) return status;
status = RssrvNamespaceBindOwned(console->Namespace,
NTL_CONSOLE_SERVICE_PATH,
sizeof(NTL_CONSOLE_SERVICE_PATH) - 1u,
NTL_RESOURCE_KIND_CAPABILITY,
console->Listener,
SYS_RIGHT_QUERY | SYS_PORT_RIGHT_CONNECT);
if (!KSUCCESS(status)) return status;
SYS_CREATE_THREAD_REQUEST create;
memset(&create, 0, sizeof(create));
create.Size = sizeof(create);
create.Entry = (uintptr_t)RssrvConsoleListenerThread;
create.Argument = (uintptr_t)console;
create.Priority = 1;
create.Flags = SYS_CREATE_THREAD_FLAG_NONE;
return SysCreateThread(&create);
}
+126
View File
@@ -0,0 +1,126 @@
// RSSRV display authority. This component owns session display metadata, modeset, and transitional console authority.
#include "rssrv_internal.h"
#include <string.h>
// Initializes RSSRV display authority from the attenuated bootstrap manager capability.
KSTATUS RssrvDisplayInitialize(RSSRV_DISPLAY_STATE* display, RSSRV_NAMESPACE* namespaceState, SYS_HANDLE manager) {
if (!display || !namespaceState || manager == SYS_HANDLE_INVALID)
return NTLSTATUS_INVALID_PARAMETER;
memset(display, 0, sizeof(*display));
display->Manager = manager;
display->Namespace = namespaceState;
return NTLSTATUS_SUCCESS;
}
// Publishes read-only display metadata while keeping mode and transitional console authority private to RSSRV.
KSTATUS RssrvDisplayPublishDevice(RSSRV_DISPLAY_STATE* display) {
if (!display || !display->Namespace || display->Manager == SYS_HANDLE_INVALID)
return NTLSTATUS_INVALID_PARAMETER;
SYS_HANDLE deviceHandle = SYS_HANDLE_INVALID;
SYS_DERIVE_HANDLE_REQUEST derive;
memset(&derive, 0, sizeof(derive));
derive.Size = sizeof(derive);
derive.SourceHandle = display->Manager;
derive.DesiredRights = SYS_RIGHT_TRANSFER | SYS_RIGHT_QUERY | SYS_DISPLAY_MANAGER_RIGHT_ENUMERATE;
derive.DerivedHandleOut = &deviceHandle;
KSTATUS status = SysDeriveHandle(&derive);
if (!KSUCCESS(status)) return status;
status = RssrvNamespaceBindOwned(display->Namespace,
"/Device/Display0",
sizeof("/Device/Display0") - 1u,
NTL_RESOURCE_KIND_DEVICE,
deviceHandle,
SYS_RIGHT_QUERY | SYS_DISPLAY_MANAGER_RIGHT_ENUMERATE);
if (!KSUCCESS(status)) (void)SysCloseHandle(deviceHandle);
return status;
}
// Queries the primary display through RSSRV's delegated manager capability.
KSTATUS RssrvDisplayQueryInfo(RSSRV_DISPLAY_STATE* display, SYS_DISPLAY_INFO* infoOut) {
if (!display || display->Manager == SYS_HANDLE_INVALID || !infoOut)
return NTLSTATUS_INVALID_PARAMETER;
return SysQueryDisplayManagerInfo(display->Manager, infoOut);
}
// Writes text through RSSRV's delegated legacy-console bridge.
KSTATUS RssrvDisplayWriteConsole(RSSRV_DISPLAY_STATE* display, const char* data, size_t length) {
if (!display || display->Manager == SYS_HANDLE_INVALID || (length && !data))
return NTLSTATUS_INVALID_PARAMETER;
SYS_WRITE_CONSOLE_REQUEST request;
memset(&request, 0, sizeof(request));
request.Size = sizeof(request);
request.Text = SysSvFromParts(data, length);
return SysWriteDisplayManagerConsole(display->Manager, &request);
}
// Queries the kernel-backed console state during the RSSRV migration period.
KSTATUS RssrvDisplayQueryConsoleInfo(RSSRV_DISPLAY_STATE* display, SYS_CONSOLE_INFO* infoOut) {
if (!display || display->Manager == SYS_HANDLE_INVALID || !infoOut)
return NTLSTATUS_INVALID_PARAMETER;
return SysQueryDisplayManagerConsoleInfo(display->Manager, infoOut);
}
// Updates the kernel-backed console cursor through delegated authority.
KSTATUS RssrvDisplaySetConsoleCursor(RSSRV_DISPLAY_STATE* display, int x, int y) {
if (!display || display->Manager == SYS_HANDLE_INVALID)
return NTLSTATUS_INVALID_PARAMETER;
return SysSetDisplayManagerConsoleCursor(display->Manager, x, y);
}
// Fills kernel-backed console characters through delegated authority.
KSTATUS RssrvDisplayFillConsoleCharacter(RSSRV_DISPLAY_STATE* display, char character,
int x, int y, size_t length, size_t* writtenOut) {
if (!display || display->Manager == SYS_HANDLE_INVALID)
return NTLSTATUS_INVALID_PARAMETER;
SYS_FILL_CONSOLE_CHARACTER_REQUEST request;
memset(&request, 0, sizeof(request));
request.Size = sizeof(request);
request.Character = character;
request.X = x;
request.Y = y;
request.Length = length;
request.WrittenOut = writtenOut;
return SysFillDisplayManagerConsoleCharacter(display->Manager, &request);
}
// Fills kernel-backed console attributes through delegated authority.
KSTATUS RssrvDisplayFillConsoleAttribute(RSSRV_DISPLAY_STATE* display, uint16_t attribute,
int x, int y, size_t length, size_t* writtenOut) {
if (!display || display->Manager == SYS_HANDLE_INVALID)
return NTLSTATUS_INVALID_PARAMETER;
SYS_FILL_CONSOLE_ATTRIBUTE_REQUEST request;
memset(&request, 0, sizeof(request));
request.Size = sizeof(request);
request.Attribute = attribute;
request.X = x;
request.Y = y;
request.Length = length;
request.WrittenOut = writtenOut;
return SysFillDisplayManagerConsoleAttribute(display->Manager, &request);
}
// Draws one explicit glyph through delegated console authority.
KSTATUS RssrvDisplayPutConsoleCharacter(RSSRV_DISPLAY_STATE* display, char character,
int x, int y, uint32_t foreground, uint32_t background) {
if (!display || display->Manager == SYS_HANDLE_INVALID)
return NTLSTATUS_INVALID_PARAMETER;
SYS_PUT_SCREEN_CHAR_REQUEST request;
memset(&request, 0, sizeof(request));
request.Size = sizeof(request);
request.Character = character;
request.X = x;
request.Y = y;
request.Foreground = foreground;
request.Background = background;
return SysPutDisplayManagerConsoleCharacter(display->Manager, &request);
}
// Clears the current console target through delegated console authority.
KSTATUS RssrvDisplayClearConsole(RSSRV_DISPLAY_STATE* display, uint32_t color) {
if (!display || display->Manager == SYS_HANDLE_INVALID)
return NTLSTATUS_INVALID_PARAMETER;
return SysClearDisplayManagerConsole(display->Manager, color);
}
+564
View File
@@ -0,0 +1,564 @@
// RSSRV logical input. The kernel reports normalized physical events; RSSRV owns interpretation, pointer state, and routing.
#include "rssrv_internal.h"
#include <ntlite/port.h>
#include <stdlib.h>
#include <string.h>
#define RSSRV_EVENT_MANUAL_RESET 1u
#define RSSRV_INPUT_KEY_QUEUE_MASK (RSSRV_INPUT_KEY_QUEUE_SIZE - 1u)
#define RSSRV_INPUT_MOUSE_QUEUE_MASK (RSSRV_INPUT_MOUSE_QUEUE_SIZE - 1u)
typedef struct RSSRV_INPUT_SESSION {
RSSRV_INPUT_STATE* Input;
SYS_HANDLE Endpoint;
} RSSRV_INPUT_SESSION;
typedef struct RSSRV_KEY_CHAR_PAIR {
INPUT_KEY Key;
char Normal;
char Shifted;
} RSSRV_KEY_CHAR_PAIR;
static const RSSRV_KEY_CHAR_PAIR gInputCharMap[] = {
{ INPUT_KEY_1, '1', '!' }, { INPUT_KEY_2, '2', '@' },
{ INPUT_KEY_3, '3', '#' }, { INPUT_KEY_4, '4', '$' },
{ INPUT_KEY_5, '5', '%' }, { INPUT_KEY_6, '6', '^' },
{ INPUT_KEY_7, '7', '&' }, { INPUT_KEY_8, '8', '*' },
{ INPUT_KEY_9, '9', '(' }, { INPUT_KEY_0, '0', ')' },
{ INPUT_KEY_MINUS, '-', '_' }, { INPUT_KEY_EQUAL, '=', '+' },
{ INPUT_KEY_LEFT_BRACKET, '[', '{' }, { INPUT_KEY_RIGHT_BRACKET, ']', '}' },
{ INPUT_KEY_BACKSLASH, '\\', '|' }, { INPUT_KEY_SEMICOLON, ';', ':' },
{ INPUT_KEY_APOSTROPHE, '\'', '"' }, { INPUT_KEY_GRAVE, '`', '~' },
{ INPUT_KEY_COMMA, ',', '<' }, { INPUT_KEY_PERIOD, '.', '>' },
{ INPUT_KEY_SLASH, '/', '?' },
{ INPUT_KEY_SPACE, ' ', ' ' }, { INPUT_KEY_TAB, '\t', '\t' },
{ INPUT_KEY_ENTER, '\n', '\n' }, { INPUT_KEY_BACKSPACE, '\b', '\b' },
{ INPUT_KEY_KP_DIVIDE, '/', '/' }, { INPUT_KEY_KP_MULTIPLY, '*', '*' },
{ INPUT_KEY_KP_MINUS, '-', '-' }, { INPUT_KEY_KP_PLUS, '+', '+' },
{ INPUT_KEY_KP_ENTER, '\n', '\n' }, { INPUT_KEY_KP_1, '1', '1' },
{ INPUT_KEY_KP_2, '2', '2' }, { INPUT_KEY_KP_3, '3', '3' },
{ INPUT_KEY_KP_4, '4', '4' }, { INPUT_KEY_KP_5, '5', '5' },
{ INPUT_KEY_KP_6, '6', '6' }, { INPUT_KEY_KP_7, '7', '7' },
{ INPUT_KEY_KP_8, '8', '8' }, { INPUT_KEY_KP_9, '9', '9' },
{ INPUT_KEY_KP_0, '0', '0' }, { INPUT_KEY_KP_PERIOD, '.', '.' },
};
// Acquires the input-state mutex.
static KSTATUS RssrvInputLock(RSSRV_INPUT_STATE* input) {
return input ? SysWait(input->Mutex, SYS_WAIT_FLAG_NONE) : NTLSTATUS_INVALID_PARAMETER;
}
// Releases the input-state mutex.
static void RssrvInputUnlock(RSSRV_INPUT_STATE* input) {
if (input) (void)SysReleaseMutex(input->Mutex);
}
// Tests one logical key bit while the input mutex is held.
static int RssrvInputKeyDownLocked(const RSSRV_INPUT_STATE* input, INPUT_KEY key) {
return key < NTL_INPUT_KEY_BYTES * 8u &&
(input->KeyDown[key >> 3] & (uint8_t)(1u << (key & 7u))) != 0;
}
// Updates one logical key bit while the input mutex is held.
static void RssrvInputSetKeyDownLocked(RSSRV_INPUT_STATE* input, INPUT_KEY key, int down) {
uint8_t mask = (uint8_t)(1u << (key & 7u));
if (down) input->KeyDown[key >> 3] |= mask;
else input->KeyDown[key >> 3] &= (uint8_t)~mask;
}
// Returns whether duplicate physical make events should generate repeated logical key records.
static int RssrvInputKeyIsRepeatable(INPUT_KEY key) {
switch (key) {
case INPUT_KEY_NONE:
case INPUT_KEY_LEFT_SHIFT:
case INPUT_KEY_RIGHT_SHIFT:
case INPUT_KEY_LEFT_CTRL:
case INPUT_KEY_RIGHT_CTRL:
case INPUT_KEY_LEFT_ALT:
case INPUT_KEY_RIGHT_ALT:
case INPUT_KEY_LEFT_GUI:
case INPUT_KEY_RIGHT_GUI:
case INPUT_KEY_CAPS_LOCK:
case INPUT_KEY_NUM_LOCK:
return 0;
default:
return 1;
}
}
// Applies modifier and lock-key state for one first make or final break.
static void RssrvInputUpdateModifierLocked(RSSRV_INPUT_STATE* input, INPUT_KEY key, int pressed) {
uint32_t bit = 0;
switch (key) {
case INPUT_KEY_LEFT_SHIFT: bit = INPUT_MOD_LSHIFT; break;
case INPUT_KEY_RIGHT_SHIFT: bit = INPUT_MOD_RSHIFT; break;
case INPUT_KEY_LEFT_CTRL: bit = INPUT_MOD_LCTRL; break;
case INPUT_KEY_RIGHT_CTRL: bit = INPUT_MOD_RCTRL; break;
case INPUT_KEY_LEFT_ALT: bit = INPUT_MOD_LALT; break;
case INPUT_KEY_RIGHT_ALT: bit = INPUT_MOD_RALT; break;
case INPUT_KEY_LEFT_GUI: bit = INPUT_MOD_LGUI; break;
case INPUT_KEY_RIGHT_GUI: bit = INPUT_MOD_RGUI; break;
default: break;
}
if (bit) {
if (pressed) input->Modifiers |= bit;
else input->Modifiers &= ~bit;
}
if (pressed && key == INPUT_KEY_CAPS_LOCK) input->Modifiers ^= INPUT_MOD_CAPS_LOCK;
if (pressed && key == INPUT_KEY_NUM_LOCK) input->Modifiers ^= INPUT_MOD_NUM_LOCK;
}
// Maps one key through RSSRV's initial US keyboard layout.
static char RssrvInputKeyToCharLocked(const RSSRV_INPUT_STATE* input, INPUT_KEY key) {
if (key >= INPUT_KEY_A && key <= INPUT_KEY_Z) {
int shifted = (input->Modifiers & INPUT_MOD_SHIFT) != 0;
int caps = (input->Modifiers & INPUT_MOD_CAPS_LOCK) != 0;
char c = (char)('a' + (key - INPUT_KEY_A));
if (shifted ^ caps) c = (char)(c - 'a' + 'A');
return c;
}
int shifted = (input->Modifiers & INPUT_MOD_SHIFT) != 0;
for (size_t i = 0; i < sizeof(gInputCharMap) / sizeof(gInputCharMap[0]); ++i) {
if (gInputCharMap[i].Key == key)
return shifted ? gInputCharMap[i].Shifted : gInputCharMap[i].Normal;
}
return 0;
}
// Refreshes the keyboard availability event while the input mutex is held.
static void RssrvInputRefreshKeyEventLocked(RSSRV_INPUT_STATE* input) {
if (input->KeyHead != input->KeyTail) (void)SysSignalEvent(input->KeyEvent);
else (void)SysClearEvent(input->KeyEvent);
}
// Refreshes the mouse availability event while the input mutex is held.
static void RssrvInputRefreshMouseEventLocked(RSSRV_INPUT_STATE* input) {
if (input->MouseHead != input->MouseTail) (void)SysSignalEvent(input->MouseEvent);
else (void)SysClearEvent(input->MouseEvent);
}
// Builds one logical key record and mirrors it into the raw logical-input queue.
static void RssrvInputQueueKeyLocked(RSSRV_INPUT_STATE* input, INPUT_KEY key, INPUT_KEY_RECORD* recordOut) {
INPUT_KEY_RECORD record;
memset(&record, 0, sizeof(record));
record.Key = key;
record.Char = RssrvInputKeyToCharLocked(input, key);
record.Modifiers = input->Modifiers;
if (recordOut) *recordOut = record;
uint32_t next = (input->KeyTail + 1u) & RSSRV_INPUT_KEY_QUEUE_MASK;
if (next == input->KeyHead) return;
input->KeyQueue[input->KeyTail] = record;
input->KeyTail = next;
RssrvInputRefreshKeyEventLocked(input);
}
// Applies one normalized physical keyboard make/break event and routes logical presses after releasing the input lock.
static void RssrvInputApplyKey(RSSRV_INPUT_STATE* input, const INPUT_KEY_EVENT* event) {
if (!input || !event || event->Key == INPUT_KEY_NONE || event->Key >= NTL_INPUT_KEY_BYTES * 8u)
return;
if (!KSUCCESS(RssrvInputLock(input))) return;
INPUT_KEY_RECORD routedRecord;
memset(&routedRecord, 0, sizeof(routedRecord));
int routeRecord = 0;
INPUT_KEY key = event->Key;
int wasDown = RssrvInputKeyDownLocked(input, key);
if (event->Pressed) {
if (!wasDown) {
RssrvInputSetKeyDownLocked(input, key, 1);
RssrvInputUpdateModifierLocked(input, key, 1);
input->Generation++;
RssrvInputQueueKeyLocked(input, key, &routedRecord);
routeRecord = 1;
} else if (RssrvInputKeyIsRepeatable(key)) {
// Hardware typematic make events remain useful until RSSRV grows its own repeat timer.
RssrvInputQueueKeyLocked(input, key, &routedRecord);
routeRecord = 1;
}
} else if (wasDown) {
RssrvInputSetKeyDownLocked(input, key, 0);
RssrvInputUpdateModifierLocked(input, key, 0);
input->Generation++;
}
RssrvInputUnlock(input);
if (routeRecord) {
RssrvSessionObserveKey(input->Session, &routedRecord);
RssrvSessionRouteKey(input->Session, &routedRecord);
}
}
// Applies one normalized relative mouse event and lets Session 0 own cursor and target routing policy.
static void RssrvInputApplyMouse(RSSRV_INPUT_STATE* input, const INPUT_MOUSE_EVENT* event) {
if (!input || !event) return;
int x = 0;
int y = 0;
int changed = 0;
if (!KSUCCESS(RssrvInputLock(input))) return;
int nx = input->MouseX + event->dx;
int ny = input->MouseY + event->dy;
int width = input->MouseWidth > 0 ? input->MouseWidth : 1;
int height = input->MouseHeight > 0 ? input->MouseHeight : 1;
if (nx < 0) nx = 0;
if (ny < 0) ny = 0;
if (nx >= width) nx = width - 1;
if (ny >= height) ny = height - 1;
changed = nx != input->MouseX || ny != input->MouseY ||
event->wheel != 0 || event->buttons != input->MouseButtons;
input->MouseX = nx;
input->MouseY = ny;
input->MouseWheel += event->wheel;
input->MouseButtons = event->buttons;
if (changed) input->Generation++;
uint32_t next = (input->MouseTail + 1u) & RSSRV_INPUT_MOUSE_QUEUE_MASK;
if (next != input->MouseHead) {
input->MouseQueue[input->MouseTail] = *event;
input->MouseTail = next;
RssrvInputRefreshMouseEventLocked(input);
}
x = input->MouseX;
y = input->MouseY;
RssrvInputUnlock(input);
if (changed) {
RssrvSessionObserveMouse(input->Session, event, x, y);
RssrvSessionRouteMouse(input->Session, event, x, y);
}
}
// Consumes the kernel's normalized physical input stream for the lifetime of RSSRV.
static void RssrvInputRawThread(uintptr_t argument) {
RSSRV_INPUT_STATE* input = (RSSRV_INPUT_STATE*)argument;
RssrvSetCurrentThreadName("rssrv.input.raw");
if (!input) SysExitThread(1);
for (;;) {
SYS_INPUT_MANAGER_EVENT event;
memset(&event, 0, sizeof(event));
SYS_INPUT_MANAGER_READ_REQUEST request;
memset(&request, 0, sizeof(request));
request.Size = sizeof(request);
request.ManagerHandle = input->Manager;
request.Flags = SYS_INPUT_MANAGER_EVENT_FLAG_NONE;
request.TimeoutMilliseconds = SYS_WAIT_TIMEOUT_INFINITE;
request.EventOut = &event;
KSTATUS status = SysReadInputManagerEvent(&request);
if (!KSUCCESS(status)) RssrvExit(14);
if (event.Type == SYS_INPUT_MANAGER_EVENT_KEY)
RssrvInputApplyKey(input, &event.Data.Key);
else if (event.Type == SYS_INPUT_MANAGER_EVENT_MOUSE)
RssrvInputApplyMouse(input, &event.Data.Mouse);
}
}
// Reads or peeks one logical keyboard record, blocking on the queue event when requested.
KSTATUS RssrvInputReadKeyRecord(RSSRV_INPUT_STATE* input,
int peek,
uint64_t timeoutMilliseconds,
INPUT_KEY_RECORD* recordOut) {
if (!input || !recordOut) return NTLSTATUS_INVALID_PARAMETER;
for (;;) {
KSTATUS status = RssrvInputLock(input);
if (!KSUCCESS(status)) return status;
if (input->KeyHead != input->KeyTail) {
*recordOut = input->KeyQueue[input->KeyHead];
if (!peek) {
input->KeyHead = (input->KeyHead + 1u) & RSSRV_INPUT_KEY_QUEUE_MASK;
RssrvInputRefreshKeyEventLocked(input);
}
RssrvInputUnlock(input);
return NTLSTATUS_SUCCESS;
}
RssrvInputUnlock(input);
if (timeoutMilliseconds == 0) return NTLSTATUS_WOULD_BLOCK;
status = SysWaitSingle(input->KeyEvent, SYS_WAIT_FLAG_NONE, timeoutMilliseconds);
if (!KSUCCESS(status)) return status;
}
}
// Discards all queued logical keyboard records while preserving current key state.
KSTATUS RssrvInputFlushKeys(RSSRV_INPUT_STATE* input, uint32_t* discardedOut) {
if (!input) return NTLSTATUS_INVALID_PARAMETER;
KSTATUS status = RssrvInputLock(input);
if (!KSUCCESS(status)) return status;
uint32_t discarded = (input->KeyTail - input->KeyHead) & RSSRV_INPUT_KEY_QUEUE_MASK;
input->KeyHead = input->KeyTail;
RssrvInputRefreshKeyEventLocked(input);
RssrvInputUnlock(input);
if (discardedOut) *discardedOut = discarded;
return NTLSTATUS_SUCCESS;
}
// Reads or peeks one logical mouse record, blocking on the queue event when requested.
static KSTATUS RssrvInputGetMouse(RSSRV_INPUT_STATE* input,
int peek,
uint64_t timeoutMilliseconds,
INPUT_MOUSE_EVENT* eventOut) {
if (!input || !eventOut) return NTLSTATUS_INVALID_PARAMETER;
for (;;) {
KSTATUS status = RssrvInputLock(input);
if (!KSUCCESS(status)) return status;
if (input->MouseHead != input->MouseTail) {
*eventOut = input->MouseQueue[input->MouseHead];
if (!peek) {
input->MouseHead = (input->MouseHead + 1u) & RSSRV_INPUT_MOUSE_QUEUE_MASK;
RssrvInputRefreshMouseEventLocked(input);
}
RssrvInputUnlock(input);
return NTLSTATUS_SUCCESS;
}
RssrvInputUnlock(input);
if (timeoutMilliseconds == 0) return NTLSTATUS_WOULD_BLOCK;
status = SysWaitSingle(input->MouseEvent, SYS_WAIT_FLAG_NONE, timeoutMilliseconds);
if (!KSUCCESS(status)) return status;
}
}
// Snapshots RSSRV's logical state while holding its state mutex.
static KSTATUS RssrvInputQueryState(RSSRV_INPUT_STATE* input, NTL_INPUT_STATE* stateOut) {
if (!input || !stateOut) return NTLSTATUS_INVALID_PARAMETER;
KSTATUS status = RssrvInputLock(input);
if (!KSUCCESS(status)) return status;
memset(stateOut, 0, sizeof(*stateOut));
stateOut->Size = sizeof(*stateOut);
stateOut->Flags = NTL_INPUT_STATE_FLAG_NONE;
stateOut->Generation = input->Generation;
stateOut->Modifiers = input->Modifiers;
memcpy(stateOut->KeyDown, input->KeyDown, sizeof(stateOut->KeyDown));
stateOut->MouseX = input->MouseX;
stateOut->MouseY = input->MouseY;
stateOut->MouseWheel = input->MouseWheel;
stateOut->MouseButtons = input->MouseButtons;
RssrvInputUnlock(input);
return NTLSTATUS_SUCCESS;
}
// Sends one fixed logical-input reply.
static KSTATUS RssrvInputReply(SYS_HANDLE endpoint,
uint32_t messageId,
uint32_t operation,
KSTATUS status,
const INPUT_KEY_RECORD* key,
const INPUT_MOUSE_EVENT* mouse,
const NTL_INPUT_STATE* state) {
NTL_INPUT_REPLY reply;
memset(&reply, 0, sizeof(reply));
reply.Size = sizeof(reply);
reply.Version = NTL_INPUT_PROTOCOL_VERSION;
reply.Operation = operation;
reply.Flags = NTL_INPUT_FLAG_NONE;
reply.Status = status;
if (key) reply.Key = *key;
if (mouse) reply.Mouse = *mouse;
if (state) reply.State = *state;
SYS_REPLY_PORT_REQUEST request;
memset(&request, 0, sizeof(request));
request.Size = sizeof(request);
request.EndpointHandle = endpoint;
request.MessageId = messageId;
request.Flags = SYS_PORT_FLAG_NONE;
request.Payload = &reply;
request.PayloadLength = sizeof(reply);
return SysReplyPort(&request);
}
// Serves one connected logical-input client.
static void RssrvInputSessionThread(uintptr_t argument) {
RSSRV_INPUT_SESSION* session = (RSSRV_INPUT_SESSION*)argument;
if (!session || !session->Input) {
if (session) free(session);
SysExitThread(1);
}
RSSRV_INPUT_STATE* input = session->Input;
SYS_HANDLE endpoint = session->Endpoint;
free(session);
RssrvSetCurrentThreadName("rssrv.input.session");
for (;;) {
NTL_INPUT_REQUEST request;
memset(&request, 0, sizeof(request));
SYS_PORT_MESSAGE_INFO info;
memset(&info, 0, sizeof(info));
info.Size = sizeof(info);
SYS_RECEIVE_PORT_REQUEST receive;
memset(&receive, 0, sizeof(receive));
receive.Size = sizeof(receive);
receive.EndpointHandle = endpoint;
receive.Flags = SYS_PORT_FLAG_NONE;
receive.Payload = &request;
receive.PayloadCapacity = sizeof(request);
receive.InfoOut = &info;
receive.TimeoutMilliseconds = SYS_PORT_TIMEOUT_INFINITE;
KSTATUS status = SysReceivePort(&receive);
if (status == NTLSTATUS_PORT_DISCONNECTED) break;
if (!KSUCCESS(status)) break;
if (info.Type != SYS_PORT_MESSAGE_REQUEST || info.PayloadLength != sizeof(request) || info.HandleCount != 0 ||
request.Size != sizeof(request) || request.Version != NTL_INPUT_PROTOCOL_VERSION ||
request.Flags != NTL_INPUT_FLAG_NONE) {
(void)RssrvInputReply(endpoint, info.MessageId, request.Operation,
NTLSTATUS_INVALID_PARAMETER, 0, 0, 0);
continue;
}
INPUT_KEY_RECORD key;
INPUT_MOUSE_EVENT mouse;
NTL_INPUT_STATE state;
memset(&key, 0, sizeof(key));
memset(&mouse, 0, sizeof(mouse));
memset(&state, 0, sizeof(state));
switch (request.Operation) {
case NTL_INPUT_OP_READ_KEY:
status = RssrvInputReadKeyRecord(input, 0, request.TimeoutMilliseconds, &key);
(void)RssrvInputReply(endpoint, info.MessageId, request.Operation, status,
KSUCCESS(status) ? &key : 0, 0, 0);
break;
case NTL_INPUT_OP_PEEK_KEY:
status = RssrvInputReadKeyRecord(input, 1, request.TimeoutMilliseconds, &key);
(void)RssrvInputReply(endpoint, info.MessageId, request.Operation, status,
KSUCCESS(status) ? &key : 0, 0, 0);
break;
case NTL_INPUT_OP_READ_MOUSE:
status = RssrvInputGetMouse(input, 0, request.TimeoutMilliseconds, &mouse);
(void)RssrvInputReply(endpoint, info.MessageId, request.Operation, status,
0, KSUCCESS(status) ? &mouse : 0, 0);
break;
case NTL_INPUT_OP_PEEK_MOUSE:
status = RssrvInputGetMouse(input, 1, request.TimeoutMilliseconds, &mouse);
(void)RssrvInputReply(endpoint, info.MessageId, request.Operation, status,
0, KSUCCESS(status) ? &mouse : 0, 0);
break;
case NTL_INPUT_OP_QUERY_STATE:
status = RssrvInputQueryState(input, &state);
(void)RssrvInputReply(endpoint, info.MessageId, request.Operation, status,
0, 0, KSUCCESS(status) ? &state : 0);
break;
default:
(void)RssrvInputReply(endpoint, info.MessageId, request.Operation,
NTLSTATUS_INVALID_PARAMETER, 0, 0, 0);
break;
}
}
(void)SysCloseHandle(endpoint);
SysExitThread(0);
}
// Accepts logical-input sessions and gives each connection one worker thread.
static void RssrvInputListenerThread(uintptr_t argument) {
RSSRV_INPUT_STATE* input = (RSSRV_INPUT_STATE*)argument;
RssrvSetCurrentThreadName("rssrv.input.listen");
if (!input) SysExitThread(1);
for (;;) {
SYS_HANDLE endpoint = SYS_HANDLE_INVALID;
SYS_ACCEPT_PORT_REQUEST accept;
memset(&accept, 0, sizeof(accept));
accept.Size = sizeof(accept);
accept.ListenerHandle = input->Listener;
accept.Flags = SYS_PORT_ACCEPT_FLAG_NONE;
accept.TimeoutMilliseconds = SYS_PORT_TIMEOUT_INFINITE;
accept.EndpointHandleOut = &endpoint;
KSTATUS status = SysAcceptPort(&accept);
if (!KSUCCESS(status)) RssrvExit(15);
RSSRV_INPUT_SESSION* session = (RSSRV_INPUT_SESSION*)malloc(sizeof(*session));
if (!session) {
(void)SysCloseHandle(endpoint);
continue;
}
session->Input = input;
session->Endpoint = endpoint;
SYS_CREATE_THREAD_REQUEST create;
memset(&create, 0, sizeof(create));
create.Size = sizeof(create);
create.Entry = (uintptr_t)RssrvInputSessionThread;
create.Argument = (uintptr_t)session;
create.Priority = 1;
create.Flags = SYS_CREATE_THREAD_FLAG_NONE;
KSTATUS createStatus = SysCreateThread(&create);
if (!KSUCCESS(createStatus)) {
free(session);
(void)SysCloseHandle(endpoint);
}
}
}
// Initializes RSSRV logical input and its initial display-sized pointer state.
KSTATUS RssrvInputInitialize(RSSRV_INPUT_STATE* input,
RSSRV_NAMESPACE* namespaceState,
RSSRV_SESSION_STATE* session,
SYS_HANDLE manager) {
if (!input || !namespaceState || !session || manager == SYS_HANDLE_INVALID)
return NTLSTATUS_INVALID_PARAMETER;
memset(input, 0, sizeof(*input));
input->Manager = manager;
input->Namespace = namespaceState;
input->Session = session;
input->Mutex = SYS_HANDLE_INVALID;
input->KeyEvent = SYS_HANDLE_INVALID;
input->MouseEvent = SYS_HANDLE_INVALID;
input->Listener = SYS_HANDLE_INVALID;
KSTATUS status = SysCreateMutex(&input->Mutex);
if (!KSUCCESS(status)) return status;
status = SysCreateEvent(RSSRV_EVENT_MANUAL_RESET, 0, &input->KeyEvent);
if (!KSUCCESS(status)) return status;
status = SysCreateEvent(RSSRV_EVENT_MANUAL_RESET, 0, &input->MouseEvent);
if (!KSUCCESS(status)) return status;
status = RssrvSessionQueryDisplayBounds(session, &input->MouseWidth, &input->MouseHeight);
if (!KSUCCESS(status)) return status;
input->MouseX = input->MouseWidth / 2;
input->MouseY = input->MouseHeight / 2;
return NTLSTATUS_SUCCESS;
}
// Publishes /Services/Input and starts the raw-input and client-listener workers.
KSTATUS RssrvInputStart(RSSRV_INPUT_STATE* input) {
if (!input || !input->Namespace || input->Listener != SYS_HANDLE_INVALID)
return NTLSTATUS_INVALID_PARAMETER;
SYS_CREATE_PORT_REQUEST createPort;
memset(&createPort, 0, sizeof(createPort));
createPort.Size = sizeof(createPort);
createPort.Flags = SYS_PORT_FLAG_NONE;
createPort.Backlog = RSSRV_LISTENER_BACKLOG;
createPort.HandleOut = &input->Listener;
KSTATUS status = SysCreatePort(&createPort);
if (!KSUCCESS(status)) return status;
status = RssrvNamespaceBindOwned(input->Namespace,
NTL_INPUT_SERVICE_PATH,
sizeof(NTL_INPUT_SERVICE_PATH) - 1u,
NTL_RESOURCE_KIND_CAPABILITY,
input->Listener,
SYS_RIGHT_QUERY | SYS_PORT_RIGHT_CONNECT);
if (!KSUCCESS(status)) return status;
SYS_CREATE_THREAD_REQUEST create;
memset(&create, 0, sizeof(create));
create.Size = sizeof(create);
create.Entry = (uintptr_t)RssrvInputRawThread;
create.Argument = (uintptr_t)input;
create.Priority = 1;
create.Flags = SYS_CREATE_THREAD_FLAG_NONE;
status = SysCreateThread(&create);
if (!KSUCCESS(status)) return status;
memset(&create, 0, sizeof(create));
create.Size = sizeof(create);
create.Entry = (uintptr_t)RssrvInputListenerThread;
create.Argument = (uintptr_t)input;
create.Priority = 1;
create.Flags = SYS_CREATE_THREAD_FLAG_NONE;
return SysCreateThread(&create);
}
+558
View File
@@ -0,0 +1,558 @@
// RSSRV native namespace tree, symbolic-link traversal, bindings, and enumeration.
#include "rssrv_internal.h"
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
typedef enum RSSRV_NODE_TYPE {
RssrvNodeDirectory = 1,
RssrvNodeBinding = 2,
RssrvNodeSymbolicLink = 3,
} RSSRV_NODE_TYPE;
// One hierarchical namespace node. Name storage follows the fixed header in the same allocation.
struct RSSRV_NODE {
RSSRV_NODE* Parent;
RSSRV_NODE* FirstChild;
RSSRV_NODE* NextSibling;
RSSRV_NODE_TYPE Type;
uint32_t NameLength;
union {
struct {
SYS_HANDLE Handle;
uint32_t Kind;
SYS_RIGHTS ExportRights;
} Binding;
struct {
char* Target;
uint32_t TargetLength;
} Link;
} u;
char Name[1];
};
// Acquires the namespace mutex.
static KSTATUS RssrvNamespaceLock(RSSRV_NAMESPACE* namespaceState) {
if (!namespaceState || namespaceState->Mutex == SYS_HANDLE_INVALID)
return NTLSTATUS_INVALID_PARAMETER;
return SysWait(namespaceState->Mutex, SYS_WAIT_FLAG_NONE);
}
// Releases the namespace mutex.
static void RssrvNamespaceUnlock(RSSRV_NAMESPACE* namespaceState) {
if (namespaceState && namespaceState->Mutex != SYS_HANDLE_INVALID)
(void)SysReleaseMutex(namespaceState->Mutex);
}
// Allocates one namespace node with inline component-name storage.
static RSSRV_NODE* RssrvAllocateNode(RSSRV_NODE_TYPE type, const char* name, uint32_t nameLength) {
if (nameLength > NTL_RESOURCE_NAME_MAX || (nameLength && !name))
return (RSSRV_NODE*)0;
size_t bytes = offsetof(RSSRV_NODE, Name) + (size_t)nameLength + 1u;
RSSRV_NODE* node = (RSSRV_NODE*)malloc(bytes);
if (!node) return (RSSRV_NODE*)0;
memset(node, 0, bytes);
node->Type = type;
node->NameLength = nameLength;
if (nameLength) memcpy(node->Name, name, nameLength);
node->Name[nameLength] = '\0';
return node;
}
// Returns the public resource kind exposed for one exact namespace node.
static uint32_t RssrvNodeKind(const RSSRV_NODE* node) {
if (!node) return NTL_RESOURCE_KIND_INVALID;
if (node->Type == RssrvNodeDirectory) return NTL_RESOURCE_KIND_NAMESPACE_DIRECTORY;
if (node->Type == RssrvNodeSymbolicLink) return NTL_RESOURCE_KIND_SYMBOLIC_LINK;
if (node->Type == RssrvNodeBinding) return node->u.Binding.Kind;
return NTL_RESOURCE_KIND_INVALID;
}
// Finds one immediate child while the namespace mutex is held.
static RSSRV_NODE* RssrvFindChildLocked(RSSRV_NODE* parent, const char* name, uint32_t nameLength) {
if (!parent || parent->Type != RssrvNodeDirectory || !name || !nameLength)
return (RSSRV_NODE*)0;
for (RSSRV_NODE* child = parent->FirstChild; child; child = child->NextSibling) {
if (child->NameLength == nameLength && memcmp(child->Name, name, nameLength) == 0)
return child;
}
return (RSSRV_NODE*)0;
}
// Links one unowned node beneath a namespace directory while the namespace mutex is held.
static KSTATUS RssrvInsertChildLocked(RSSRV_NODE* parent, RSSRV_NODE* node) {
if (!parent || parent->Type != RssrvNodeDirectory || !node || !node->NameLength)
return NTLSTATUS_INVALID_PARAMETER;
if (RssrvFindChildLocked(parent, node->Name, node->NameLength))
return NTLSTATUS_OBJECT_NAME_COLLISION;
node->Parent = parent;
node->NextSibling = parent->FirstChild;
parent->FirstChild = node;
return NTLSTATUS_SUCCESS;
}
// Walks an exact namespace path without following symbolic links.
static RSSRV_NODE* RssrvFindExactLocked(RSSRV_NAMESPACE* namespaceState,
const char* path,
uint32_t length) {
if (!namespaceState || !namespaceState->Root || !path || length == 0 || path[0] != '/')
return (RSSRV_NODE*)0;
if (length == 1)
return namespaceState->Root;
RSSRV_NODE* current = namespaceState->Root;
uint32_t componentStart = 1;
for (uint32_t i = 1; i <= length; ++i) {
if (i != length && path[i] != '/')
continue;
uint32_t componentLength = i - componentStart;
current = RssrvFindChildLocked(current, path + componentStart, componentLength);
if (!current)
return (RSSRV_NODE*)0;
if (i != length && current->Type != RssrvNodeDirectory)
return (RSSRV_NODE*)0;
componentStart = i + 1;
}
return current;
}
// Splits one non-root path into an existing namespace parent and final component.
static KSTATUS RssrvFindParentLocked(RSSRV_NAMESPACE* namespaceState,
const char* path,
uint32_t length,
RSSRV_NODE** parentOut,
const char** nameOut,
uint32_t* nameLengthOut) {
if (!namespaceState || !path || length < 2 || !parentOut || !nameOut || !nameLengthOut)
return NTLSTATUS_INVALID_PARAMETER;
uint32_t slash = length;
while (slash > 1 && path[slash - 1u] != '/') slash--;
uint32_t nameStart = slash;
uint32_t nameLength = length - nameStart;
uint32_t parentLength = nameStart == 1u ? 1u : nameStart - 1u;
RSSRV_NODE* parent = RssrvFindExactLocked(namespaceState, path, parentLength);
if (!parent) return NTLSTATUS_NOT_FOUND;
if (parent->Type != RssrvNodeDirectory) return NTLSTATUS_NOT_SUPPORTED;
*parentOut = parent;
*nameOut = path + nameStart;
*nameLengthOut = nameLength;
return NTLSTATUS_SUCCESS;
}
// Frees one detached namespace node and the payload it owns.
static void RssrvFreeDetachedNode(RSSRV_NODE* node) {
if (!node) return;
if (node->Type == RssrvNodeBinding && node->u.Binding.Handle != SYS_HANDLE_INVALID)
(void)SysCloseHandle(node->u.Binding.Handle);
if (node->Type == RssrvNodeSymbolicLink && node->u.Link.Target)
free(node->u.Link.Target);
free(node);
}
// Resolves a symbolic-link target to one exact namespace node while bounding recursive links.
static KSTATUS RssrvResolveExactNodeLocked(RSSRV_NAMESPACE* namespaceState,
RSSRV_NODE* node,
uint32_t depth,
RSSRV_NODE** nodeOut) {
if (!namespaceState || !nodeOut) return NTLSTATUS_INVALID_PARAMETER;
*nodeOut = (RSSRV_NODE*)0;
while (node && node->Type == RssrvNodeSymbolicLink) {
if (depth++ >= RSSRV_SYMBOLIC_LINK_MAX_DEPTH)
return NTLSTATUS_LIMIT_REACHED;
node = RssrvFindExactLocked(namespaceState, node->u.Link.Target, node->u.Link.TargetLength);
if (!node)
return NTLSTATUS_NOT_FOUND;
}
if (!node) return NTLSTATUS_NOT_FOUND;
*nodeOut = node;
return NTLSTATUS_SUCCESS;
}
// Resolves the first bound capability reached by the caller's path, following namespace links.
static KSTATUS RssrvResolveBindingLocked(RSSRV_NAMESPACE* namespaceState,
const char* path,
uint32_t length,
RSSRV_NODE** bindingOut,
uint32_t* consumedOut) {
if (!namespaceState || !path || length < 2 || !bindingOut || !consumedOut)
return NTLSTATUS_INVALID_PARAMETER;
*bindingOut = (RSSRV_NODE*)0;
*consumedOut = 0;
RSSRV_NODE* current = namespaceState->Root;
uint32_t componentStart = 1;
for (uint32_t i = 1; i <= length; ++i) {
if (i != length && path[i] != '/')
continue;
uint32_t componentLength = i - componentStart;
RSSRV_NODE* next = RssrvFindChildLocked(current, path + componentStart, componentLength);
if (!next) return NTLSTATUS_NOT_FOUND;
uint32_t consumed = i;
RSSRV_NODE* resolved = (RSSRV_NODE*)0;
KSTATUS status = RssrvResolveExactNodeLocked(namespaceState, next, 0, &resolved);
if (!KSUCCESS(status)) return status;
next = resolved;
if (next->Type == RssrvNodeBinding) {
*bindingOut = next;
*consumedOut = consumed;
return NTLSTATUS_SUCCESS;
}
if (next->Type != RssrvNodeDirectory)
return NTLSTATUS_NOT_SUPPORTED;
current = next;
componentStart = i + 1u;
}
return NTLSTATUS_NOT_SUPPORTED;
}
// Returns the lexicographically next immediate child after one optional cursor name.
static RSSRV_NODE* RssrvEnumerateNextLocked(RSSRV_NODE* directory,
const char* after,
uint32_t afterLength) {
if (!directory || directory->Type != RssrvNodeDirectory)
return (RSSRV_NODE*)0;
RSSRV_NODE* best = (RSSRV_NODE*)0;
for (RSSRV_NODE* child = directory->FirstChild; child; child = child->NextSibling) {
int afterCompare = 1;
if (afterLength) {
uint32_t common = child->NameLength < afterLength ? child->NameLength : afterLength;
int cmp = memcmp(child->Name, after, common);
if (cmp == 0) {
if (child->NameLength < afterLength) cmp = -1;
else if (child->NameLength > afterLength) cmp = 1;
}
afterCompare = cmp;
}
if (afterLength && afterCompare <= 0)
continue;
if (!best) {
best = child;
continue;
}
uint32_t common = child->NameLength < best->NameLength ? child->NameLength : best->NameLength;
int cmp = memcmp(child->Name, best->Name, common);
if (cmp < 0 || (cmp == 0 && child->NameLength < best->NameLength))
best = child;
}
return best;
}
// Initializes the native namespace root and its synchronization object.
KSTATUS RssrvNamespaceInitialize(RSSRV_NAMESPACE* namespaceState) {
if (!namespaceState) return NTLSTATUS_INVALID_PARAMETER;
memset(namespaceState, 0, sizeof(*namespaceState));
namespaceState->Mutex = SYS_HANDLE_INVALID;
KSTATUS status = SysCreateMutex(&namespaceState->Mutex);
if (!KSUCCESS(status)) return status;
namespaceState->Root = RssrvAllocateNode(RssrvNodeDirectory, (const char*)0, 0);
if (!namespaceState->Root) {
(void)SysCloseHandle(namespaceState->Mutex);
namespaceState->Mutex = SYS_HANDLE_INVALID;
return NTLSTATUS_NO_MEMORY;
}
return NTLSTATUS_SUCCESS;
}
// Validates one canonical absolute native resource path, including the root path "/".
KSTATUS RssrvNamespaceValidatePath(const char* path, uint32_t length) {
if (!path || length == 0 || length > NTL_RESOURCE_PATH_MAX || path[0] != '/')
return NTLSTATUS_INVALID_PARAMETER;
if (length == 1)
return NTLSTATUS_SUCCESS;
if (path[length - 1] == '/')
return NTLSTATUS_INVALID_PARAMETER;
uint32_t componentStart = 1;
for (uint32_t i = 1; i <= length; ++i) {
if (i < length) {
char c = path[i];
if (c == '\\' || c == '\0')
return NTLSTATUS_INVALID_PARAMETER;
if (c != '/')
continue;
}
uint32_t componentLength = i - componentStart;
if (componentLength == 0 || componentLength > NTL_RESOURCE_NAME_MAX ||
(componentLength == 1 && path[componentStart] == '.') ||
(componentLength == 2 && path[componentStart] == '.' && path[componentStart + 1] == '.'))
return NTLSTATUS_INVALID_PARAMETER;
componentStart = i + 1;
}
return NTLSTATUS_SUCCESS;
}
// Creates one exact namespace directory. Its parent must already exist.
KSTATUS RssrvNamespaceCreateDirectory(RSSRV_NAMESPACE* namespaceState,
const char* path,
uint32_t length) {
KSTATUS status = RssrvNamespaceValidatePath(path, length);
if (!KSUCCESS(status)) return status;
if (!namespaceState || length == 1) return NTLSTATUS_OBJECT_NAME_COLLISION;
RSSRV_NODE* node = (RSSRV_NODE*)0;
status = RssrvNamespaceLock(namespaceState);
if (!KSUCCESS(status)) return status;
RSSRV_NODE* parent = (RSSRV_NODE*)0;
const char* name = (const char*)0;
uint32_t nameLength = 0;
status = RssrvFindParentLocked(namespaceState, path, length, &parent, &name, &nameLength);
if (KSUCCESS(status)) {
node = RssrvAllocateNode(RssrvNodeDirectory, name, nameLength);
if (!node) status = NTLSTATUS_NO_MEMORY;
}
if (KSUCCESS(status))
status = RssrvInsertChildLocked(parent, node);
RssrvNamespaceUnlock(namespaceState);
if (!KSUCCESS(status) && node) free(node);
return status;
}
// Creates one absolute namespace symbolic link. Dangling targets are permitted.
KSTATUS RssrvNamespaceCreateSymbolicLink(RSSRV_NAMESPACE* namespaceState,
const char* path,
uint32_t length,
const char* target,
uint32_t targetLength) {
KSTATUS status = RssrvNamespaceValidatePath(path, length);
if (!KSUCCESS(status)) return status;
status = RssrvNamespaceValidatePath(target, targetLength);
if (!KSUCCESS(status)) return status;
if (!namespaceState || length == 1) return NTLSTATUS_ACCESS_DENIED;
RSSRV_NODE* node = (RSSRV_NODE*)0;
char* targetCopy = (char*)malloc((size_t)targetLength + 1u);
if (!targetCopy) return NTLSTATUS_NO_MEMORY;
memcpy(targetCopy, target, targetLength);
targetCopy[targetLength] = '\0';
status = RssrvNamespaceLock(namespaceState);
if (!KSUCCESS(status)) {
free(targetCopy);
return status;
}
RSSRV_NODE* parent = (RSSRV_NODE*)0;
const char* name = (const char*)0;
uint32_t nameLength = 0;
status = RssrvFindParentLocked(namespaceState, path, length, &parent, &name, &nameLength);
if (KSUCCESS(status)) {
node = RssrvAllocateNode(RssrvNodeSymbolicLink, name, nameLength);
if (!node) status = NTLSTATUS_NO_MEMORY;
}
if (KSUCCESS(status)) {
node->u.Link.Target = targetCopy;
node->u.Link.TargetLength = targetLength;
status = RssrvInsertChildLocked(parent, node);
}
RssrvNamespaceUnlock(namespaceState);
if (!KSUCCESS(status)) {
if (node) free(node);
free(targetCopy);
}
return status;
}
// Takes ownership of one handle and binds it to a new exact resource name.
KSTATUS RssrvNamespaceBindOwned(RSSRV_NAMESPACE* namespaceState,
const char* path,
uint32_t length,
uint32_t kind,
SYS_HANDLE handle,
SYS_RIGHTS exportRights) {
if (!namespaceState || kind == NTL_RESOURCE_KIND_INVALID ||
kind == NTL_RESOURCE_KIND_NAMESPACE_DIRECTORY || kind == NTL_RESOURCE_KIND_SYMBOLIC_LINK ||
handle == SYS_HANDLE_INVALID || !exportRights)
return NTLSTATUS_INVALID_PARAMETER;
KSTATUS status = RssrvNamespaceValidatePath(path, length);
if (!KSUCCESS(status)) return status;
if (length == 1) return NTLSTATUS_ACCESS_DENIED;
RSSRV_NODE* node = (RSSRV_NODE*)0;
status = RssrvNamespaceLock(namespaceState);
if (!KSUCCESS(status)) return status;
RSSRV_NODE* parent = (RSSRV_NODE*)0;
const char* name = (const char*)0;
uint32_t nameLength = 0;
status = RssrvFindParentLocked(namespaceState, path, length, &parent, &name, &nameLength);
if (KSUCCESS(status)) {
node = RssrvAllocateNode(RssrvNodeBinding, name, nameLength);
if (!node) status = NTLSTATUS_NO_MEMORY;
}
if (KSUCCESS(status)) {
node->u.Binding.Handle = handle;
node->u.Binding.Kind = kind;
node->u.Binding.ExportRights = exportRights;
status = RssrvInsertChildLocked(parent, node);
}
RssrvNamespaceUnlock(namespaceState);
if (!KSUCCESS(status) && node) free(node);
return status;
}
// Removes one exact namespace node. Namespace directories must be empty.
KSTATUS RssrvNamespaceUnbind(RSSRV_NAMESPACE* namespaceState,
const char* path,
uint32_t length) {
KSTATUS status = RssrvNamespaceValidatePath(path, length);
if (!KSUCCESS(status)) return status;
if (!namespaceState || length == 1) return NTLSTATUS_ACCESS_DENIED;
status = RssrvNamespaceLock(namespaceState);
if (!KSUCCESS(status)) return status;
RSSRV_NODE* node = RssrvFindExactLocked(namespaceState, path, length);
if (!node) {
RssrvNamespaceUnlock(namespaceState);
return NTLSTATUS_NOT_FOUND;
}
if (node->Type == RssrvNodeDirectory && node->FirstChild) {
RssrvNamespaceUnlock(namespaceState);
return NTLSTATUS_ACCESS_DENIED;
}
RSSRV_NODE* parent = node->Parent;
RSSRV_NODE** link = &parent->FirstChild;
while (*link && *link != node) link = &(*link)->NextSibling;
if (*link != node) {
RssrvNamespaceUnlock(namespaceState);
return NTLSTATUS_FAILURE;
}
*link = node->NextSibling;
node->Parent = (RSSRV_NODE*)0;
node->NextSibling = (RSSRV_NODE*)0;
RssrvNamespaceUnlock(namespaceState);
RssrvFreeDetachedNode(node);
return NTLSTATUS_SUCCESS;
}
// Queries one exact namespace node without following the final symbolic link.
KSTATUS RssrvNamespaceQuery(RSSRV_NAMESPACE* namespaceState,
const char* path,
uint32_t length,
uint32_t* kindOut) {
if (!namespaceState || !kindOut) return NTLSTATUS_INVALID_PARAMETER;
*kindOut = NTL_RESOURCE_KIND_INVALID;
KSTATUS status = RssrvNamespaceLock(namespaceState);
if (!KSUCCESS(status)) return status;
RSSRV_NODE* node = RssrvFindExactLocked(namespaceState, path, length);
if (node) *kindOut = RssrvNodeKind(node);
RssrvNamespaceUnlock(namespaceState);
return node ? NTLSTATUS_SUCCESS : NTLSTATUS_NOT_FOUND;
}
// Returns the lexicographically next immediate child after one optional cursor name.
KSTATUS RssrvNamespaceEnumerate(RSSRV_NAMESPACE* namespaceState,
const char* path,
uint32_t length,
const char* after,
uint32_t afterLength,
RSSRV_NAMESPACE_ENTRY* entryOut) {
if (!namespaceState || !entryOut || (afterLength && !after) || afterLength > NTL_RESOURCE_NAME_MAX)
return NTLSTATUS_INVALID_PARAMETER;
memset(entryOut, 0, sizeof(*entryOut));
entryOut->Kind = NTL_RESOURCE_KIND_INVALID;
KSTATUS status = RssrvNamespaceLock(namespaceState);
if (!KSUCCESS(status)) return status;
RSSRV_NODE* directory = RssrvFindExactLocked(namespaceState, path, length);
if (!directory) {
RssrvNamespaceUnlock(namespaceState);
return NTLSTATUS_NOT_FOUND;
}
if (directory->Type != RssrvNodeDirectory) {
entryOut->Kind = RssrvNodeKind(directory);
RssrvNamespaceUnlock(namespaceState);
return NTLSTATUS_NOT_SUPPORTED;
}
RSSRV_NODE* child = RssrvEnumerateNextLocked(directory, after, afterLength);
if (!child) {
RssrvNamespaceUnlock(namespaceState);
return NTLSTATUS_NO_MORE_FILES;
}
entryOut->Kind = RssrvNodeKind(child);
entryOut->NameLength = child->NameLength;
memcpy(entryOut->Name, child->Name, child->NameLength);
entryOut->Name[child->NameLength] = '\0';
RssrvNamespaceUnlock(namespaceState);
return NTLSTATUS_SUCCESS;
}
// Opens a locked view of one exact bound object, following a final namespace symbolic link.
KSTATUS RssrvNamespaceBeginLookup(RSSRV_NAMESPACE* namespaceState,
const char* path,
uint32_t length,
RSSRV_NAMESPACE_BINDING_VIEW* viewOut,
uint32_t* kindOut) {
if (!namespaceState || !viewOut || !kindOut) return NTLSTATUS_INVALID_PARAMETER;
memset(viewOut, 0, sizeof(*viewOut));
viewOut->Handle = SYS_HANDLE_INVALID;
*kindOut = NTL_RESOURCE_KIND_INVALID;
KSTATUS status = RssrvNamespaceLock(namespaceState);
if (!KSUCCESS(status)) return status;
RSSRV_NODE* node = RssrvFindExactLocked(namespaceState, path, length);
if (!node) {
RssrvNamespaceUnlock(namespaceState);
return NTLSTATUS_NOT_FOUND;
}
RSSRV_NODE* resolved = (RSSRV_NODE*)0;
status = RssrvResolveExactNodeLocked(namespaceState, node, 0, &resolved);
if (!KSUCCESS(status)) {
RssrvNamespaceUnlock(namespaceState);
return status;
}
node = resolved;
*kindOut = RssrvNodeKind(node);
if (node->Type != RssrvNodeBinding) {
RssrvNamespaceUnlock(namespaceState);
return NTLSTATUS_NOT_SUPPORTED;
}
viewOut->Namespace = namespaceState;
viewOut->Handle = node->u.Binding.Handle;
viewOut->Kind = node->u.Binding.Kind;
viewOut->ExportRights = node->u.Binding.ExportRights;
viewOut->Locked = 1u;
return NTLSTATUS_SUCCESS;
}
// Opens a locked view of the first bound object reached while traversing one resource path.
KSTATUS RssrvNamespaceBeginResolve(RSSRV_NAMESPACE* namespaceState,
const char* path,
uint32_t length,
RSSRV_NAMESPACE_BINDING_VIEW* viewOut) {
if (!namespaceState || !viewOut) return NTLSTATUS_INVALID_PARAMETER;
memset(viewOut, 0, sizeof(*viewOut));
viewOut->Handle = SYS_HANDLE_INVALID;
KSTATUS status = RssrvNamespaceLock(namespaceState);
if (!KSUCCESS(status)) return status;
RSSRV_NODE* node = (RSSRV_NODE*)0;
uint32_t resolvedLength = 0;
status = RssrvResolveBindingLocked(namespaceState, path, length, &node, &resolvedLength);
if (!KSUCCESS(status)) {
RssrvNamespaceUnlock(namespaceState);
return status;
}
viewOut->Namespace = namespaceState;
viewOut->Handle = node->u.Binding.Handle;
viewOut->Kind = node->u.Binding.Kind;
viewOut->ExportRights = node->u.Binding.ExportRights;
viewOut->ResolvedLength = resolvedLength;
viewOut->Locked = 1u;
return NTLSTATUS_SUCCESS;
}
// Releases the namespace lock held by a successful binding view.
void RssrvNamespaceEndBinding(RSSRV_NAMESPACE_BINDING_VIEW* view) {
if (!view || !view->Locked || !view->Namespace) return;
RssrvNamespaceUnlock(view->Namespace);
view->Locked = 0;
view->Namespace = (RSSRV_NAMESPACE*)0;
view->Handle = SYS_HANDLE_INVALID;
}
+560
View File
@@ -0,0 +1,560 @@
// RSSRV resource protocol listeners, sessions, request validation, and capability transfer.
#include "rssrv_internal.h"
#include <stdlib.h>
#include <string.h>
typedef struct RSSRV_SESSION {
RSSRV_RESOURCE_SERVICE* Service;
SYS_HANDLE Endpoint;
uint32_t Administrative;
} RSSRV_SESSION;
// Replies with one fixed resource-protocol reply and no handle transfer.
static KSTATUS RssrvReplyStatus(SYS_HANDLE endpoint,
uint32_t messageId,
uint32_t operation,
KSTATUS status,
uint32_t kind) {
NTL_RESOURCE_REPLY reply;
memset(&reply, 0, sizeof(reply));
reply.Size = sizeof(reply);
reply.Version = NTL_RESOURCE_PROTOCOL_VERSION;
reply.Operation = operation;
reply.Flags = NTL_RESOURCE_FLAG_NONE;
reply.Status = status;
reply.Kind = kind;
SYS_REPLY_PORT_REQUEST request;
memset(&request, 0, sizeof(request));
request.Size = sizeof(request);
request.EndpointHandle = endpoint;
request.MessageId = messageId;
request.Flags = SYS_PORT_FLAG_NONE;
request.Payload = &reply;
request.PayloadLength = sizeof(reply);
return SysReplyPort(&request);
}
// Handles one exact capability lookup while retaining the namespace lock through transfer.
static KSTATUS RssrvHandleLookup(RSSRV_RESOURCE_SERVICE* service,
SYS_HANDLE endpoint,
uint32_t messageId,
const NTL_RESOURCE_REQUEST* request,
const char* path) {
if (!request->Rights)
return RssrvReplyStatus(endpoint, messageId, request->Operation,
NTLSTATUS_INVALID_PARAMETER, NTL_RESOURCE_KIND_INVALID);
RSSRV_NAMESPACE_BINDING_VIEW view;
uint32_t kind = NTL_RESOURCE_KIND_INVALID;
KSTATUS status = RssrvNamespaceBeginLookup(service->Namespace,
path,
request->PathLength,
&view,
&kind);
if (!KSUCCESS(status))
return RssrvReplyStatus(endpoint, messageId, request->Operation, status, kind);
kind = view.Kind;
if ((request->Rights & ~view.ExportRights) != 0) {
RssrvNamespaceEndBinding(&view);
return RssrvReplyStatus(endpoint, messageId, request->Operation,
NTLSTATUS_ACCESS_DENIED, kind);
}
NTL_RESOURCE_REPLY reply;
memset(&reply, 0, sizeof(reply));
reply.Size = sizeof(reply);
reply.Version = NTL_RESOURCE_PROTOCOL_VERSION;
reply.Operation = NTL_RESOURCE_OP_LOOKUP;
reply.Status = NTLSTATUS_SUCCESS;
reply.Kind = kind;
SYS_PORT_HANDLE_TRANSFER transfer;
transfer.Handle = view.Handle;
transfer.DesiredRights = request->Rights;
transfer.Flags = SYS_PORT_TRANSFER_FLAG_NONE;
SYS_REPLY_PORT_REQUEST out;
memset(&out, 0, sizeof(out));
out.Size = sizeof(out);
out.EndpointHandle = endpoint;
out.MessageId = messageId;
out.Flags = SYS_PORT_FLAG_NONE;
out.Payload = &reply;
out.PayloadLength = sizeof(reply);
out.Handles = &transfer;
out.HandleCount = 1;
status = SysReplyPort(&out);
RssrvNamespaceEndBinding(&view);
// A transfer failure leaves the synchronous call pending, so complete it with the precise status.
if (!KSUCCESS(status))
return RssrvReplyStatus(endpoint, messageId, request->Operation, status, kind);
return NTLSTATUS_SUCCESS;
}
// Resolves the first bound namespace object and transfers its traversal authority.
static KSTATUS RssrvHandleResolve(RSSRV_RESOURCE_SERVICE* service,
SYS_HANDLE endpoint,
uint32_t messageId,
const NTL_RESOURCE_REQUEST* request,
const char* path) {
if ((request->Flags & ~NTL_RESOURCE_RESOLVE_FLAG_MODIFY) != 0)
return RssrvReplyStatus(endpoint, messageId, request->Operation,
NTLSTATUS_INVALID_PARAMETER, NTL_RESOURCE_KIND_INVALID);
RSSRV_NAMESPACE_BINDING_VIEW view;
KSTATUS status = RssrvNamespaceBeginResolve(service->Namespace,
path,
request->PathLength,
&view);
if (!KSUCCESS(status))
return RssrvReplyStatus(endpoint, messageId, request->Operation,
status, NTL_RESOURCE_KIND_INVALID);
SYS_RIGHTS desiredRights = 0;
if (view.Kind == NTL_RESOURCE_KIND_VOLUME) {
desiredRights = SYS_VOLUME_RIGHT_OPEN;
if (request->Flags & NTL_RESOURCE_RESOLVE_FLAG_MODIFY)
desiredRights |= SYS_VOLUME_RIGHT_MODIFY;
} else if (view.Kind == NTL_RESOURCE_KIND_DIRECTORY) {
desiredRights = SYS_DIR_RIGHT_OPEN;
if (request->Flags & NTL_RESOURCE_RESOLVE_FLAG_MODIFY)
desiredRights |= SYS_DIR_RIGHT_MODIFY;
} else {
uint32_t kind = view.Kind;
RssrvNamespaceEndBinding(&view);
return RssrvReplyStatus(endpoint, messageId, request->Operation,
NTLSTATUS_NOT_SUPPORTED, kind);
}
uint32_t kind = view.Kind;
if ((desiredRights & ~view.ExportRights) != 0) {
RssrvNamespaceEndBinding(&view);
return RssrvReplyStatus(endpoint, messageId, request->Operation,
NTLSTATUS_ACCESS_DENIED, kind);
}
NTL_RESOURCE_REPLY reply;
memset(&reply, 0, sizeof(reply));
reply.Size = sizeof(reply);
reply.Version = NTL_RESOURCE_PROTOCOL_VERSION;
reply.Operation = NTL_RESOURCE_OP_RESOLVE;
reply.Flags = request->Flags;
reply.Status = NTLSTATUS_SUCCESS;
reply.Kind = kind;
reply.ResolvedLength = view.ResolvedLength;
SYS_PORT_HANDLE_TRANSFER transfer;
transfer.Handle = view.Handle;
transfer.DesiredRights = desiredRights;
transfer.Flags = SYS_PORT_TRANSFER_FLAG_NONE;
SYS_REPLY_PORT_REQUEST out;
memset(&out, 0, sizeof(out));
out.Size = sizeof(out);
out.EndpointHandle = endpoint;
out.MessageId = messageId;
out.Flags = SYS_PORT_FLAG_NONE;
out.Payload = &reply;
out.PayloadLength = sizeof(reply);
out.Handles = &transfer;
out.HandleCount = 1;
status = SysReplyPort(&out);
RssrvNamespaceEndBinding(&view);
if (!KSUCCESS(status))
return RssrvReplyStatus(endpoint, messageId, request->Operation, status, kind);
return NTLSTATUS_SUCCESS;
}
// Handles one exact metadata query without following the final symbolic link.
static KSTATUS RssrvHandleQuery(RSSRV_RESOURCE_SERVICE* service,
SYS_HANDLE endpoint,
uint32_t messageId,
const NTL_RESOURCE_REQUEST* request,
const char* path) {
uint32_t kind = NTL_RESOURCE_KIND_INVALID;
KSTATUS status = RssrvNamespaceQuery(service->Namespace, path, request->PathLength, &kind);
return RssrvReplyStatus(endpoint, messageId, request->Operation, status, kind);
}
// Returns one immediate namespace child after the supplied bytewise cursor name.
static KSTATUS RssrvHandleEnumerate(RSSRV_RESOURCE_SERVICE* service,
SYS_HANDLE endpoint,
uint32_t messageId,
const NTL_RESOURCE_REQUEST* request,
const char* path,
const char* after) {
RSSRV_NAMESPACE_ENTRY entry;
KSTATUS status = RssrvNamespaceEnumerate(service->Namespace,
path,
request->PathLength,
after,
request->AuxLength,
&entry);
if (!KSUCCESS(status))
return RssrvReplyStatus(endpoint, messageId, request->Operation, status, entry.Kind);
uint8_t payload[sizeof(NTL_RESOURCE_REPLY) + NTL_RESOURCE_NAME_MAX];
NTL_RESOURCE_REPLY reply;
memset(&reply, 0, sizeof(reply));
reply.Size = sizeof(reply);
reply.Version = NTL_RESOURCE_PROTOCOL_VERSION;
reply.Operation = NTL_RESOURCE_OP_ENUMERATE;
reply.Status = NTLSTATUS_SUCCESS;
reply.Kind = entry.Kind;
reply.NameLength = entry.NameLength;
memcpy(payload, &reply, sizeof(reply));
memcpy(payload + sizeof(reply), entry.Name, entry.NameLength);
uint32_t payloadLength = (uint32_t)sizeof(reply) + entry.NameLength;
SYS_REPLY_PORT_REQUEST out;
memset(&out, 0, sizeof(out));
out.Size = sizeof(out);
out.EndpointHandle = endpoint;
out.MessageId = messageId;
out.Flags = SYS_PORT_FLAG_NONE;
out.Payload = payload;
out.PayloadLength = payloadLength;
return SysReplyPort(&out);
}
// Handles one administrative bind. On success the namespace takes ownership of receivedHandle.
static KSTATUS RssrvHandleBind(RSSRV_RESOURCE_SERVICE* service,
SYS_HANDLE endpoint,
uint32_t messageId,
const NTL_RESOURCE_REQUEST* request,
const char* path,
uint32_t administrative,
SYS_HANDLE receivedHandle,
uint32_t* consumedOut) {
if (!consumedOut) return NTLSTATUS_INVALID_PARAMETER;
*consumedOut = 0;
if (!administrative || RssrvVolumesOwnPath(path, request->PathLength))
return RssrvReplyStatus(endpoint, messageId, request->Operation,
NTLSTATUS_ACCESS_DENIED, NTL_RESOURCE_KIND_INVALID);
KSTATUS bindStatus = RssrvNamespaceBindOwned(service->Namespace,
path,
request->PathLength,
request->Kind,
receivedHandle,
request->ExportRights);
if (KSUCCESS(bindStatus)) *consumedOut = 1;
KSTATUS replyStatus = RssrvReplyStatus(endpoint, messageId, request->Operation, bindStatus,
KSUCCESS(bindStatus) ? request->Kind : NTL_RESOURCE_KIND_INVALID);
return KSUCCESS(replyStatus) ? bindStatus : replyStatus;
}
// Dispatches one received resource request and returns whether a received handle was consumed.
static uint32_t RssrvDispatchRequest(RSSRV_RESOURCE_SERVICE* service,
SYS_HANDLE endpoint,
uint32_t administrative,
const uint8_t* payload,
uint32_t payloadLength,
const SYS_PORT_MESSAGE_INFO* info,
SYS_HANDLE receivedHandle) {
uint32_t operation = 0;
if (!service || payloadLength < sizeof(NTL_RESOURCE_REQUEST)) {
(void)RssrvReplyStatus(endpoint, info->MessageId, operation,
NTLSTATUS_INVALID_PARAMETER, NTL_RESOURCE_KIND_INVALID);
return 0;
}
NTL_RESOURCE_REQUEST request;
memcpy(&request, payload, sizeof(request));
operation = request.Operation;
size_t expectedLength = sizeof(request) + (size_t)request.PathLength + (size_t)request.AuxLength;
if (request.Size != sizeof(request) || request.Version != NTL_RESOURCE_PROTOCOL_VERSION ||
request.PathLength == 0 || expectedLength != payloadLength || expectedLength > SYS_PORT_MAX_INLINE_BYTES) {
(void)RssrvReplyStatus(endpoint, info->MessageId, operation,
NTLSTATUS_INVALID_PARAMETER, NTL_RESOURCE_KIND_INVALID);
return 0;
}
const char* path = (const char*)(payload + sizeof(request));
const char* aux = path + request.PathLength;
KSTATUS status = RssrvNamespaceValidatePath(path, request.PathLength);
if (!KSUCCESS(status)) {
(void)RssrvReplyStatus(endpoint, info->MessageId, operation, status, NTL_RESOURCE_KIND_INVALID);
return 0;
}
switch (operation) {
case NTL_RESOURCE_OP_LOOKUP:
if (request.Flags != NTL_RESOURCE_FLAG_NONE || request.AuxLength != 0 || info->HandleCount != 0 ||
request.ExportRights != 0 || request.Kind != NTL_RESOURCE_KIND_INVALID) {
(void)RssrvReplyStatus(endpoint, info->MessageId, operation,
NTLSTATUS_INVALID_PARAMETER, NTL_RESOURCE_KIND_INVALID);
return 0;
}
(void)RssrvHandleLookup(service, endpoint, info->MessageId, &request, path);
return 0;
case NTL_RESOURCE_OP_QUERY:
if (request.Flags != NTL_RESOURCE_FLAG_NONE || request.AuxLength != 0 || info->HandleCount != 0 ||
request.Rights != 0 || request.ExportRights != 0 || request.Kind != NTL_RESOURCE_KIND_INVALID) {
(void)RssrvReplyStatus(endpoint, info->MessageId, operation,
NTLSTATUS_INVALID_PARAMETER, NTL_RESOURCE_KIND_INVALID);
return 0;
}
(void)RssrvHandleQuery(service, endpoint, info->MessageId, &request, path);
return 0;
case NTL_RESOURCE_OP_RESOLVE:
if (request.AuxLength != 0 || info->HandleCount != 0 || request.Rights != 0 ||
request.ExportRights != 0 || request.Kind != NTL_RESOURCE_KIND_INVALID ||
(request.Flags & ~NTL_RESOURCE_RESOLVE_FLAG_MODIFY) != 0) {
(void)RssrvReplyStatus(endpoint, info->MessageId, operation,
NTLSTATUS_INVALID_PARAMETER, NTL_RESOURCE_KIND_INVALID);
return 0;
}
(void)RssrvHandleResolve(service, endpoint, info->MessageId, &request, path);
return 0;
case NTL_RESOURCE_OP_ENUMERATE:
if (request.Flags != NTL_RESOURCE_FLAG_NONE || info->HandleCount != 0 || request.Rights != 0 ||
request.ExportRights != 0 || request.Kind != NTL_RESOURCE_KIND_INVALID ||
request.AuxLength > NTL_RESOURCE_NAME_MAX) {
(void)RssrvReplyStatus(endpoint, info->MessageId, operation,
NTLSTATUS_INVALID_PARAMETER, NTL_RESOURCE_KIND_INVALID);
return 0;
}
for (uint32_t i = 0; i < request.AuxLength; ++i) {
if (aux[i] == '/' || aux[i] == '\\' || aux[i] == '\0') {
(void)RssrvReplyStatus(endpoint, info->MessageId, operation,
NTLSTATUS_INVALID_PARAMETER, NTL_RESOURCE_KIND_INVALID);
return 0;
}
}
(void)RssrvHandleEnumerate(service, endpoint, info->MessageId, &request, path, aux);
return 0;
case NTL_RESOURCE_OP_CREATE_DIRECTORY:
if (!administrative || request.Flags != NTL_RESOURCE_FLAG_NONE || request.AuxLength != 0 ||
info->HandleCount != 0 || request.Rights != 0 || request.ExportRights != 0 ||
request.Kind != NTL_RESOURCE_KIND_INVALID || RssrvVolumesOwnPath(path, request.PathLength)) {
status = administrative ? NTLSTATUS_INVALID_PARAMETER : NTLSTATUS_ACCESS_DENIED;
(void)RssrvReplyStatus(endpoint, info->MessageId, operation, status, NTL_RESOURCE_KIND_INVALID);
return 0;
}
status = RssrvNamespaceCreateDirectory(service->Namespace, path, request.PathLength);
(void)RssrvReplyStatus(endpoint, info->MessageId, operation, status,
KSUCCESS(status) ? NTL_RESOURCE_KIND_NAMESPACE_DIRECTORY : NTL_RESOURCE_KIND_INVALID);
return 0;
case NTL_RESOURCE_OP_CREATE_SYMBOLIC_LINK:
if (!administrative || request.Flags != NTL_RESOURCE_FLAG_NONE || request.AuxLength == 0 ||
info->HandleCount != 0 || request.Rights != 0 || request.ExportRights != 0 ||
request.Kind != NTL_RESOURCE_KIND_INVALID || RssrvVolumesOwnPath(path, request.PathLength)) {
status = administrative ? NTLSTATUS_INVALID_PARAMETER : NTLSTATUS_ACCESS_DENIED;
(void)RssrvReplyStatus(endpoint, info->MessageId, operation, status, NTL_RESOURCE_KIND_INVALID);
return 0;
}
status = RssrvNamespaceValidatePath(aux, request.AuxLength);
if (KSUCCESS(status))
status = RssrvNamespaceCreateSymbolicLink(service->Namespace,
path,
request.PathLength,
aux,
request.AuxLength);
(void)RssrvReplyStatus(endpoint, info->MessageId, operation, status,
KSUCCESS(status) ? NTL_RESOURCE_KIND_SYMBOLIC_LINK : NTL_RESOURCE_KIND_INVALID);
return 0;
case NTL_RESOURCE_OP_BIND:
if (request.Flags != NTL_RESOURCE_FLAG_NONE || request.AuxLength != 0 || info->HandleCount != 1 ||
!(request.Rights & SYS_RIGHT_TRANSFER) || request.ExportRights == 0 ||
(request.ExportRights & ~request.Rights) != 0 || request.Kind == NTL_RESOURCE_KIND_INVALID ||
request.Kind == NTL_RESOURCE_KIND_NAMESPACE_DIRECTORY || request.Kind == NTL_RESOURCE_KIND_SYMBOLIC_LINK ||
receivedHandle == SYS_HANDLE_INVALID) {
(void)RssrvReplyStatus(endpoint, info->MessageId, operation,
NTLSTATUS_INVALID_PARAMETER, NTL_RESOURCE_KIND_INVALID);
return 0;
}
{
uint32_t consumed = 0;
(void)RssrvHandleBind(service, endpoint, info->MessageId, &request, path, administrative,
receivedHandle, &consumed);
return consumed;
}
case NTL_RESOURCE_OP_UNBIND:
if (request.Flags != NTL_RESOURCE_FLAG_NONE || request.AuxLength != 0 || info->HandleCount != 0 ||
request.Rights != 0 || request.ExportRights != 0 || request.Kind != NTL_RESOURCE_KIND_INVALID) {
(void)RssrvReplyStatus(endpoint, info->MessageId, operation,
NTLSTATUS_INVALID_PARAMETER, NTL_RESOURCE_KIND_INVALID);
return 0;
}
status = administrative && !RssrvVolumesOwnPath(path, request.PathLength)
? RssrvNamespaceUnbind(service->Namespace, path, request.PathLength)
: NTLSTATUS_ACCESS_DENIED;
(void)RssrvReplyStatus(endpoint, info->MessageId, operation, status, NTL_RESOURCE_KIND_INVALID);
return 0;
default:
(void)RssrvReplyStatus(endpoint, info->MessageId, operation,
NTLSTATUS_NOT_SUPPORTED, NTL_RESOURCE_KIND_INVALID);
return 0;
}
}
// Serves one connected resource session until the peer disconnects.
static void RssrvSessionThread(uintptr_t argument) {
RSSRV_SESSION* session = (RSSRV_SESSION*)argument;
if (!session || !session->Service) {
if (session) free(session);
SysExitThread(1);
return;
}
RSSRV_RESOURCE_SERVICE* service = session->Service;
SYS_HANDLE endpoint = session->Endpoint;
uint32_t administrative = session->Administrative;
RssrvSetCurrentThreadName(administrative ? "rssrv.session.admin" : "rssrv.session.public");
free(session);
uint8_t payload[SYS_PORT_MAX_INLINE_BYTES];
SYS_HANDLE handles[SYS_PORT_MAX_TRANSFER_HANDLES];
for (;;) {
memset(handles, 0, sizeof(handles));
SYS_PORT_MESSAGE_INFO info;
memset(&info, 0, sizeof(info));
info.Size = sizeof(info);
SYS_RECEIVE_PORT_REQUEST receive;
memset(&receive, 0, sizeof(receive));
receive.Size = sizeof(receive);
receive.EndpointHandle = endpoint;
receive.Flags = SYS_PORT_FLAG_NONE;
receive.Payload = payload;
receive.PayloadCapacity = sizeof(payload);
receive.HandlesOut = handles;
receive.HandleCapacity = SYS_PORT_MAX_TRANSFER_HANDLES;
receive.InfoOut = &info;
receive.TimeoutMilliseconds = SYS_PORT_TIMEOUT_INFINITE;
KSTATUS status = SysReceivePort(&receive);
if (status == NTLSTATUS_PORT_DISCONNECTED)
break;
if (!KSUCCESS(status))
break;
uint32_t consumed = 0;
if (info.Type == SYS_PORT_MESSAGE_REQUEST)
consumed = RssrvDispatchRequest(service,
endpoint,
administrative,
payload,
info.PayloadLength,
&info,
info.HandleCount == 1 ? handles[0] : SYS_HANDLE_INVALID);
for (uint32_t i = consumed ? 1u : 0u; i < info.HandleCount; ++i) {
if (handles[i] != SYS_HANDLE_INVALID) (void)SysCloseHandle(handles[i]);
}
}
(void)SysCloseHandle(endpoint);
SysExitThread(0);
}
// Accepts sessions from one listener and gives every connection its own worker thread.
static void RssrvListenerThread(uintptr_t argument) {
RSSRV_LISTENER_WORKER* worker = (RSSRV_LISTENER_WORKER*)argument;
if (!worker || !worker->Service) {
SysExitThread(1);
return;
}
RssrvSetCurrentThreadName(worker->Administrative ? "rssrv.listen.admin" : "rssrv.listen.public");
for (;;) {
SYS_HANDLE endpoint = SYS_HANDLE_INVALID;
SYS_ACCEPT_PORT_REQUEST accept;
memset(&accept, 0, sizeof(accept));
accept.Size = sizeof(accept);
accept.ListenerHandle = worker->Listener;
accept.Flags = SYS_PORT_ACCEPT_FLAG_NONE;
accept.TimeoutMilliseconds = SYS_PORT_TIMEOUT_INFINITE;
accept.EndpointHandleOut = &endpoint;
KSTATUS status = SysAcceptPort(&accept);
if (!KSUCCESS(status))
break;
RSSRV_SESSION* session = (RSSRV_SESSION*)malloc(sizeof(*session));
if (!session) {
(void)SysDisconnectPort(endpoint);
(void)SysCloseHandle(endpoint);
continue;
}
session->Service = worker->Service;
session->Endpoint = endpoint;
session->Administrative = worker->Administrative;
SYS_CREATE_THREAD_REQUEST create;
memset(&create, 0, sizeof(create));
create.Size = sizeof(create);
create.Entry = (uintptr_t)RssrvSessionThread;
create.Argument = (uintptr_t)session;
create.Priority = 1;
create.Flags = SYS_CREATE_THREAD_FLAG_NONE;
status = SysCreateThread(&create);
if (!KSUCCESS(status)) {
free(session);
(void)SysDisconnectPort(endpoint);
(void)SysCloseHandle(endpoint);
}
}
SysExitThread(0);
}
// Creates one listener using the service's bounded default backlog.
static KSTATUS RssrvCreateListener(SYS_HANDLE* listenerOut) {
SYS_CREATE_PORT_REQUEST create;
memset(&create, 0, sizeof(create));
create.Size = sizeof(create);
create.Flags = SYS_PORT_FLAG_NONE;
create.Backlog = RSSRV_LISTENER_BACKLOG;
create.HandleOut = listenerOut;
return SysCreatePort(&create);
}
// Starts one resource listener worker thread.
static KSTATUS RssrvStartListenerWorker(RSSRV_RESOURCE_SERVICE* service,
RSSRV_LISTENER_WORKER* worker,
SYS_HANDLE listener,
uint32_t administrative) {
if (!service || !worker || listener == SYS_HANDLE_INVALID)
return NTLSTATUS_INVALID_PARAMETER;
worker->Service = service;
worker->Listener = listener;
worker->Administrative = administrative;
SYS_CREATE_THREAD_REQUEST create;
memset(&create, 0, sizeof(create));
create.Size = sizeof(create);
create.Entry = (uintptr_t)RssrvListenerThread;
create.Argument = (uintptr_t)worker;
create.Priority = 1;
create.Flags = SYS_CREATE_THREAD_FLAG_NONE;
return SysCreateThread(&create);
}
// Initializes the public/admin resource protocol component around existing namespace state.
KSTATUS RssrvResourceInitialize(RSSRV_RESOURCE_SERVICE* service,
RSSRV_NAMESPACE* namespaceState) {
if (!service || !namespaceState)
return NTLSTATUS_INVALID_PARAMETER;
memset(service, 0, sizeof(*service));
service->Namespace = namespaceState;
service->PublicListener = SYS_HANDLE_INVALID;
service->AdminListener = SYS_HANDLE_INVALID;
service->PublicWorker.Listener = SYS_HANDLE_INVALID;
service->AdminWorker.Listener = SYS_HANDLE_INVALID;
return NTLSTATUS_SUCCESS;
}
// Creates the resource listeners and starts their accept workers.
KSTATUS RssrvResourceStart(RSSRV_RESOURCE_SERVICE* service) {
if (!service || !service->Namespace)
return NTLSTATUS_INVALID_PARAMETER;
KSTATUS status = RssrvCreateListener(&service->PublicListener);
if (!KSUCCESS(status)) return status;
status = RssrvCreateListener(&service->AdminListener);
if (!KSUCCESS(status)) {
(void)SysCloseHandle(service->PublicListener);
service->PublicListener = SYS_HANDLE_INVALID;
return status;
}
status = RssrvStartListenerWorker(service, &service->PublicWorker, service->PublicListener, 0);
if (!KSUCCESS(status)) return status;
status = RssrvStartListenerWorker(service, &service->AdminWorker, service->AdminListener, 1);
return status;
}
File diff suppressed because it is too large. Load diff
+304
View File
@@ -0,0 +1,304 @@
// Internal RSSRV component model. One process owns the native resource plane, while each component owns its state.
#pragma once
#include <ntlite/resource.h>
#include <ntlite/input_service.h>
#include <ntlite/console_service.h>
#include <ntlite/syscall.h>
#include <stdint.h>
#define RSSRV_LISTENER_BACKLOG 16u
#define RSSRV_SYMBOLIC_LINK_MAX_DEPTH 16u
#define RSSRV_VOLUME_PATH_CAPACITY 48u
#define RSSRV_INPUT_KEY_QUEUE_SIZE 256u
#define RSSRV_INPUT_MOUSE_QUEUE_SIZE 128u
#define RSSRV_CONSOLE_KEY_QUEUE_SIZE 256u
typedef struct RSSRV_NODE RSSRV_NODE;
typedef struct RSSRV_VOLUME_PUBLICATION RSSRV_VOLUME_PUBLICATION;
typedef struct RSSRV_RESOURCE_SERVICE RSSRV_RESOURCE_SERVICE;
typedef struct RSSRV_SESSION_STATE RSSRV_SESSION_STATE;
typedef struct RSSRV_INPUT_TARGET RSSRV_INPUT_TARGET;
typedef void (*RSSRV_INPUT_TARGET_KEY_ROUTINE)(void* context, const INPUT_KEY_RECORD* record);
typedef void (*RSSRV_INPUT_TARGET_MOUSE_ROUTINE)(void* context, const INPUT_MOUSE_EVENT* event, int32_t x, int32_t y);
#define RSSRV_INPUT_TARGET_KIND_NONE 0u
#define RSSRV_INPUT_TARGET_KIND_CONSOLE 1u
#define RSSRV_INPUT_TARGET_KIND_DESKTOP 2u
struct RSSRV_INPUT_TARGET {
uint64_t Id;
uint32_t Kind;
uint32_t Flags;
RSSRV_SESSION_STATE* Session;
void* Context;
RSSRV_INPUT_TARGET_KEY_ROUTINE KeyRoutine;
RSSRV_INPUT_TARGET_MOUSE_ROUTINE MouseRoutine;
};
typedef struct RSSRV_NAMESPACE {
SYS_HANDLE Mutex;
RSSRV_NODE* Root;
} RSSRV_NAMESPACE;
typedef struct RSSRV_NAMESPACE_BINDING_VIEW {
RSSRV_NAMESPACE* Namespace;
SYS_HANDLE Handle;
uint32_t Kind;
SYS_RIGHTS ExportRights;
uint32_t ResolvedLength;
uint32_t Locked;
} RSSRV_NAMESPACE_BINDING_VIEW;
typedef struct RSSRV_NAMESPACE_ENTRY {
uint32_t Kind;
uint32_t NameLength;
char Name[NTL_RESOURCE_NAME_MAX + 1u];
} RSSRV_NAMESPACE_ENTRY;
typedef struct RSSRV_DISPLAY_STATE {
SYS_HANDLE Manager;
RSSRV_NAMESPACE* Namespace;
} RSSRV_DISPLAY_STATE;
struct RSSRV_SESSION_STATE {
SYS_HANDLE Mutex;
RSSRV_NAMESPACE* Namespace;
RSSRV_DISPLAY_STATE* Display;
uint32_t Id;
uint32_t Reserved;
uint64_t Generation;
uint64_t NextTargetId;
int32_t DisplayWidth;
int32_t DisplayHeight;
RSSRV_INPUT_TARGET* ActiveTarget;
RSSRV_INPUT_TARGET* KeyboardFocus;
RSSRV_INPUT_TARGET* PointerFocus;
RSSRV_INPUT_TARGET* PointerCapture;
SYS_HANDLE DwmListener;
SYS_HANDLE DwmEndpoint;
RSSRV_INPUT_TARGET DesktopTarget;
};
typedef struct RSSRV_INPUT_STATE {
SYS_HANDLE Manager;
SYS_HANDLE Mutex;
SYS_HANDLE KeyEvent;
SYS_HANDLE MouseEvent;
SYS_HANDLE Listener;
RSSRV_NAMESPACE* Namespace;
RSSRV_SESSION_STATE* Session;
uint8_t KeyDown[NTL_INPUT_KEY_BYTES];
uint32_t Modifiers;
uint64_t Generation;
INPUT_KEY_RECORD KeyQueue[RSSRV_INPUT_KEY_QUEUE_SIZE];
uint32_t KeyHead;
uint32_t KeyTail;
INPUT_MOUSE_EVENT MouseQueue[RSSRV_INPUT_MOUSE_QUEUE_SIZE];
uint32_t MouseHead;
uint32_t MouseTail;
int32_t MouseX;
int32_t MouseY;
int32_t MouseWheel;
uint32_t MouseButtons;
int32_t MouseWidth;
int32_t MouseHeight;
} RSSRV_INPUT_STATE;
typedef struct RSSRV_CONSOLE_STATE {
SYS_HANDLE Mutex;
SYS_HANDLE KeyEvent;
SYS_HANDLE Listener;
RSSRV_NAMESPACE* Namespace;
RSSRV_DISPLAY_STATE* Display;
RSSRV_SESSION_STATE* Session;
RSSRV_INPUT_TARGET InputTarget;
INPUT_KEY_RECORD KeyQueue[RSSRV_CONSOLE_KEY_QUEUE_SIZE];
uint32_t KeyHead;
uint32_t KeyTail;
uint64_t Generation;
} RSSRV_CONSOLE_STATE;
typedef struct RSSRV_VOLUME_STATE {
RSSRV_NAMESPACE* Namespace;
SYS_HANDLE Manager;
RSSRV_VOLUME_PUBLICATION* Published;
uint32_t NextHarddiskVolumeId;
} RSSRV_VOLUME_STATE;
typedef struct RSSRV_LISTENER_WORKER {
RSSRV_RESOURCE_SERVICE* Service;
SYS_HANDLE Listener;
uint32_t Administrative;
} RSSRV_LISTENER_WORKER;
struct RSSRV_RESOURCE_SERVICE {
RSSRV_NAMESPACE* Namespace;
SYS_HANDLE PublicListener;
SYS_HANDLE AdminListener;
RSSRV_LISTENER_WORKER PublicWorker;
RSSRV_LISTENER_WORKER AdminWorker;
};
typedef struct RSSRV_STATE {
RSSRV_NAMESPACE Namespace;
RSSRV_VOLUME_STATE Volumes;
RSSRV_DISPLAY_STATE Display;
RSSRV_SESSION_STATE Session;
RSSRV_INPUT_STATE Input;
RSSRV_CONSOLE_STATE Console;
RSSRV_RESOURCE_SERVICE Resource;
} RSSRV_STATE;
// Sets a best-effort diagnostic name for the current RSSRV thread.
void RssrvSetCurrentThreadName(const char* name);
// Terminates RSSRV without depending on a compiler runtime return path.
void RssrvExit(int code);
// Initializes the native namespace root and its synchronization object.
KSTATUS RssrvNamespaceInitialize(RSSRV_NAMESPACE* namespaceState);
// Validates one canonical absolute native resource path, including the root path "/".
KSTATUS RssrvNamespaceValidatePath(const char* path, uint32_t length);
// Creates one exact namespace directory. Its parent must already exist.
KSTATUS RssrvNamespaceCreateDirectory(RSSRV_NAMESPACE* namespaceState,
const char* path,
uint32_t length);
// Creates one absolute namespace symbolic link. Dangling targets are permitted.
KSTATUS RssrvNamespaceCreateSymbolicLink(RSSRV_NAMESPACE* namespaceState,
const char* path,
uint32_t length,
const char* target,
uint32_t targetLength);
// Takes ownership of one handle and binds it to a new exact resource name.
KSTATUS RssrvNamespaceBindOwned(RSSRV_NAMESPACE* namespaceState,
const char* path,
uint32_t length,
uint32_t kind,
SYS_HANDLE handle,
SYS_RIGHTS exportRights);
// Removes one exact namespace node. Namespace directories must be empty.
KSTATUS RssrvNamespaceUnbind(RSSRV_NAMESPACE* namespaceState,
const char* path,
uint32_t length);
// Queries one exact namespace node without following the final symbolic link.
KSTATUS RssrvNamespaceQuery(RSSRV_NAMESPACE* namespaceState,
const char* path,
uint32_t length,
uint32_t* kindOut);
// Returns the lexicographically next immediate child after one optional cursor name.
KSTATUS RssrvNamespaceEnumerate(RSSRV_NAMESPACE* namespaceState,
const char* path,
uint32_t length,
const char* after,
uint32_t afterLength,
RSSRV_NAMESPACE_ENTRY* entryOut);
// Opens a locked view of one exact bound object, following a final namespace symbolic link.
KSTATUS RssrvNamespaceBeginLookup(RSSRV_NAMESPACE* namespaceState,
const char* path,
uint32_t length,
RSSRV_NAMESPACE_BINDING_VIEW* viewOut,
uint32_t* kindOut);
// Opens a locked view of the first bound object reached while traversing one resource path.
KSTATUS RssrvNamespaceBeginResolve(RSSRV_NAMESPACE* namespaceState,
const char* path,
uint32_t length,
RSSRV_NAMESPACE_BINDING_VIEW* viewOut);
// Releases the namespace lock held by a successful binding view.
void RssrvNamespaceEndBinding(RSSRV_NAMESPACE_BINDING_VIEW* view);
// Initializes display authority around the kernel display-manager capability.
KSTATUS RssrvDisplayInitialize(RSSRV_DISPLAY_STATE* display, RSSRV_NAMESPACE* namespaceState, SYS_HANDLE manager);
// Publishes a query-only display device capability while retaining physical display authority inside RSSRV.
KSTATUS RssrvDisplayPublishDevice(RSSRV_DISPLAY_STATE* display);
KSTATUS RssrvDisplayQueryInfo(RSSRV_DISPLAY_STATE* display, SYS_DISPLAY_INFO* infoOut);
KSTATUS RssrvDisplayWriteConsole(RSSRV_DISPLAY_STATE* display, const char* data, size_t length);
KSTATUS RssrvDisplayQueryConsoleInfo(RSSRV_DISPLAY_STATE* display, SYS_CONSOLE_INFO* infoOut);
KSTATUS RssrvDisplaySetConsoleCursor(RSSRV_DISPLAY_STATE* display, int x, int y);
KSTATUS RssrvDisplayFillConsoleCharacter(RSSRV_DISPLAY_STATE* display, char character,
int x, int y, size_t length, size_t* writtenOut);
KSTATUS RssrvDisplayFillConsoleAttribute(RSSRV_DISPLAY_STATE* display, uint16_t attribute,
int x, int y, size_t length, size_t* writtenOut);
KSTATUS RssrvDisplayPutConsoleCharacter(RSSRV_DISPLAY_STATE* display, char character,
int x, int y, uint32_t foreground, uint32_t background);
KSTATUS RssrvDisplayClearConsole(RSSRV_DISPLAY_STATE* display, uint32_t color);
// Initializes Session 0, input routing, and display ownership.
KSTATUS RssrvSessionInitialize(RSSRV_SESSION_STATE* session,
RSSRV_NAMESPACE* namespaceState,
RSSRV_DISPLAY_STATE* display);
KSTATUS RssrvSessionRegisterInputTarget(RSSRV_SESSION_STATE* session,
RSSRV_INPUT_TARGET* target,
uint32_t kind,
void* context,
RSSRV_INPUT_TARGET_KEY_ROUTINE keyRoutine,
RSSRV_INPUT_TARGET_MOUSE_ROUTINE mouseRoutine);
KSTATUS RssrvSessionActivateInputTarget(RSSRV_SESSION_STATE* session, RSSRV_INPUT_TARGET* target);
KSTATUS RssrvSessionSetKeyboardFocus(RSSRV_SESSION_STATE* session, RSSRV_INPUT_TARGET* target);
KSTATUS RssrvSessionSetPointerFocus(RSSRV_SESSION_STATE* session, RSSRV_INPUT_TARGET* target);
KSTATUS RssrvSessionSetPointerCapture(RSSRV_SESSION_STATE* session, RSSRV_INPUT_TARGET* target);
KSTATUS RssrvSessionQueryDisplayBounds(RSSRV_SESSION_STATE* session, int32_t* widthOut, int32_t* heightOut);
void RssrvSessionObserveKey(RSSRV_SESSION_STATE* session, const INPUT_KEY_RECORD* record);
void RssrvSessionObserveMouse(RSSRV_SESSION_STATE* session, const INPUT_MOUSE_EVENT* event, int32_t x, int32_t y);
void RssrvSessionRouteKey(RSSRV_SESSION_STATE* session, const INPUT_KEY_RECORD* record);
void RssrvSessionRouteMouse(RSSRV_SESSION_STATE* session, const INPUT_MOUSE_EVENT* event, int32_t x, int32_t y);
KSTATUS RssrvSessionStart(RSSRV_SESSION_STATE* session, RSSRV_CONSOLE_STATE* console);
// Initializes and starts RSSRV-owned logical input over the kernel manager stream.
KSTATUS RssrvInputInitialize(RSSRV_INPUT_STATE* input,
RSSRV_NAMESPACE* namespaceState,
RSSRV_SESSION_STATE* session,
SYS_HANDLE manager);
KSTATUS RssrvInputStart(RSSRV_INPUT_STATE* input);
KSTATUS RssrvInputReadKeyRecord(RSSRV_INPUT_STATE* input, int peek,
uint64_t timeoutMilliseconds, INPUT_KEY_RECORD* recordOut);
KSTATUS RssrvInputFlushKeys(RSSRV_INPUT_STATE* input, uint32_t* discardedOut);
// Initializes and starts the one native logical console owned by RSSRV.
KSTATUS RssrvConsoleInitialize(RSSRV_CONSOLE_STATE* console,
RSSRV_NAMESPACE* namespaceState,
RSSRV_DISPLAY_STATE* display,
RSSRV_SESSION_STATE* session);
KSTATUS RssrvConsoleStart(RSSRV_CONSOLE_STATE* console);
// Initializes mounted-volume publication state against one volume-manager capability.
KSTATUS RssrvVolumesInitialize(RSSRV_VOLUME_STATE* volumes,
RSSRV_NAMESPACE* namespaceState,
SYS_HANDLE manager);
// Reconciles RSSRV volume bindings with the kernel's mounted-volume snapshot.
KSTATUS RssrvVolumesSynchronize(RSSRV_VOLUME_STATE* volumes, uint64_t* generationOut);
// Copies the namespace path assigned to one mounted kernel volume instance.
KSTATUS RssrvVolumesQueryPublishedPath(RSSRV_VOLUME_STATE* volumes,
uint32_t instanceId,
char* pathOut,
uint32_t capacity,
uint32_t* lengthOut);
// Starts the mounted-volume namespace publication worker.
KSTATUS RssrvVolumesStartWorker(RSSRV_VOLUME_STATE* volumes);
// Returns whether a path belongs to the RSSRV-owned automatic mounted-volume naming family.
int RssrvVolumesOwnPath(const char* path, uint32_t length);
// Initializes the public/admin resource protocol component around existing namespace state.
KSTATUS RssrvResourceInitialize(RSSRV_RESOURCE_SERVICE* service,
RSSRV_NAMESPACE* namespaceState);
// Creates the resource listeners and starts their accept workers.
KSTATUS RssrvResourceStart(RSSRV_RESOURCE_SERVICE* service);
+464
View File
@@ -0,0 +1,464 @@
// RSSRV interactive-session policy. Session 0 owns display routing, activation, focus, pointer capture, and DWM authority.
#include "rssrv_internal.h"
#include <ntlite/internal/session_control.h>
#include <ntlite/port.h>
#include <string.h>
#define RSSRV_SESSION_ZERO_PATH "/Sessions/0"
#define RSSRV_SESSION_ZERO_CONSOLE_PATH "/Sessions/0/Console"
#define RSSRV_CURRENT_CONSOLE_PATH "/Console"
#define RSSRV_DWM_LISTENER_BACKLOG 1u
// Acquires the session-state mutex.
static KSTATUS RssrvSessionLock(RSSRV_SESSION_STATE* session) {
if (!session || session->Mutex == SYS_HANDLE_INVALID) return NTLSTATUS_INVALID_PARAMETER;
return SysWait(session->Mutex, SYS_WAIT_FLAG_NONE);
}
// Releases the session-state mutex.
static void RssrvSessionUnlock(RSSRV_SESSION_STATE* session) {
if (session && session->Mutex != SYS_HANDLE_INVALID) (void)SysReleaseMutex(session->Mutex);
}
// Advances the session generation while preserving zero as an uninitialized value.
static void RssrvSessionChangedLocked(RSSRV_SESSION_STATE* session) {
session->Generation++;
if (session->Generation == 0) session->Generation = 1;
}
// Sends one best-effort DWM input notification while holding the session mutex.
static void RssrvSessionSendDwmEventLocked(RSSRV_SESSION_STATE* session,
uint32_t type,
const INPUT_KEY_RECORD* key,
const INPUT_MOUSE_EVENT* mouse,
int32_t x,
int32_t y) {
if (!session || session->DwmEndpoint == SYS_HANDLE_INVALID) return;
NTL_SESSION_DWM_EVENT event;
memset(&event, 0, sizeof(event));
event.Size = sizeof(event);
event.Version = NTL_SESSION_DWM_PROTOCOL_VERSION;
event.Type = type;
event.Flags = NTL_SESSION_DWM_FLAG_NONE;
event.PointerX = x;
event.PointerY = y;
if (key) event.Key = *key;
if (mouse) event.Mouse = *mouse;
SYS_SEND_PORT_REQUEST send;
memset(&send, 0, sizeof(send));
send.Size = sizeof(send);
send.EndpointHandle = session->DwmEndpoint;
send.Flags = SYS_PORT_FLAG_NONE;
send.Payload = &event;
send.PayloadLength = sizeof(event);
send.TimeoutMilliseconds = 0;
(void)SysSendPort(&send);
}
// Routes one focused keyboard record into the connected DWM desktop target.
static void RssrvSessionDwmRouteKey(void* context, const INPUT_KEY_RECORD* record) {
RSSRV_SESSION_STATE* session = (RSSRV_SESSION_STATE*)context;
if (!session || !record) return;
if (!KSUCCESS(RssrvSessionLock(session))) return;
RssrvSessionSendDwmEventLocked(session, NTL_SESSION_DWM_EVENT_DELIVER_KEY, record, 0, 0, 0);
RssrvSessionUnlock(session);
}
// Routes one focused/captured mouse record into the connected DWM desktop target.
static void RssrvSessionDwmRouteMouse(void* context,
const INPUT_MOUSE_EVENT* event,
int32_t x,
int32_t y) {
RSSRV_SESSION_STATE* session = (RSSRV_SESSION_STATE*)context;
if (!session || !event) return;
if (!KSUCCESS(RssrvSessionLock(session))) return;
RssrvSessionSendDwmEventLocked(session, NTL_SESSION_DWM_EVENT_DELIVER_MOUSE, 0, event, x, y);
RssrvSessionUnlock(session);
}
// Replies to one private DWM session-control request.
static void RssrvSessionReplyDwm(SYS_HANDLE endpoint,
uint32_t messageId,
uint32_t operation,
KSTATUS status) {
NTL_SESSION_DWM_REPLY reply;
memset(&reply, 0, sizeof(reply));
reply.Size = sizeof(reply);
reply.Version = NTL_SESSION_DWM_PROTOCOL_VERSION;
reply.Operation = operation;
reply.Flags = NTL_SESSION_DWM_FLAG_NONE;
reply.Status = status;
SYS_REPLY_PORT_REQUEST send;
memset(&send, 0, sizeof(send));
send.Size = sizeof(send);
send.EndpointHandle = endpoint;
send.MessageId = messageId;
send.Flags = SYS_PORT_FLAG_NONE;
send.Payload = &reply;
send.PayloadLength = sizeof(reply);
(void)SysReplyPort(&send);
}
// Applies one DWM focus/capture request while preserving RSSRV as the session authority owner.
static KSTATUS RssrvSessionApplyDwmRequest(RSSRV_SESSION_STATE* session,
const NTL_SESSION_DWM_REQUEST* request) {
if (!session || !request || request->Enabled > 1u) return NTLSTATUS_INVALID_PARAMETER;
KSTATUS status = RssrvSessionLock(session);
if (!KSUCCESS(status)) return status;
RSSRV_INPUT_TARGET* desktop = &session->DesktopTarget;
RSSRV_INPUT_TARGET* fallback = session->ActiveTarget == desktop ? (RSSRV_INPUT_TARGET*)0 : session->ActiveTarget;
switch (request->Operation) {
case NTL_SESSION_DWM_OP_SET_KEYBOARD_FOCUS:
if (request->Enabled) {
if (session->KeyboardFocus != desktop) {
session->KeyboardFocus = desktop;
RssrvSessionChangedLocked(session);
}
} else if (session->KeyboardFocus == desktop) {
session->KeyboardFocus = fallback;
RssrvSessionChangedLocked(session);
}
break;
case NTL_SESSION_DWM_OP_SET_POINTER_FOCUS:
if (request->Enabled) {
if (session->PointerFocus != desktop) {
session->PointerFocus = desktop;
RssrvSessionChangedLocked(session);
}
} else if (session->PointerFocus == desktop) {
session->PointerFocus = fallback;
RssrvSessionChangedLocked(session);
}
break;
case NTL_SESSION_DWM_OP_SET_POINTER_CAPTURE:
if (request->Enabled) {
if (session->PointerCapture != desktop) {
session->PointerCapture = desktop;
RssrvSessionChangedLocked(session);
}
} else if (session->PointerCapture == desktop) {
session->PointerCapture = (RSSRV_INPUT_TARGET*)0;
RssrvSessionChangedLocked(session);
}
break;
default:
status = NTLSTATUS_INVALID_PARAMETER;
break;
}
RssrvSessionUnlock(session);
return status;
}
// Restores non-DWM routing after one DWM control connection disappears.
static void RssrvSessionDisconnectDwm(RSSRV_SESSION_STATE* session, SYS_HANDLE endpoint) {
if (!session) return;
if (!KSUCCESS(RssrvSessionLock(session))) return;
if (session->DwmEndpoint == endpoint) {
RSSRV_INPUT_TARGET* desktop = &session->DesktopTarget;
RSSRV_INPUT_TARGET* fallback = session->ActiveTarget == desktop ? (RSSRV_INPUT_TARGET*)0 : session->ActiveTarget;
if (session->KeyboardFocus == desktop) session->KeyboardFocus = fallback;
if (session->PointerFocus == desktop) session->PointerFocus = fallback;
if (session->PointerCapture == desktop) session->PointerCapture = (RSSRV_INPUT_TARGET*)0;
session->DwmEndpoint = SYS_HANDLE_INVALID;
RssrvSessionChangedLocked(session);
}
RssrvSessionUnlock(session);
}
// Accepts replaceable DWM connections and serves the private Session 0 authority protocol.
static void RssrvSessionDwmListenerThread(uintptr_t argument) {
RSSRV_SESSION_STATE* session = (RSSRV_SESSION_STATE*)argument;
RssrvSetCurrentThreadName("rssrv.session.dwm");
if (!session) SysExitThread(1);
for (;;) {
SYS_HANDLE endpoint = SYS_HANDLE_INVALID;
SYS_ACCEPT_PORT_REQUEST accept;
memset(&accept, 0, sizeof(accept));
accept.Size = sizeof(accept);
accept.ListenerHandle = session->DwmListener;
accept.Flags = SYS_PORT_ACCEPT_FLAG_NONE;
accept.TimeoutMilliseconds = SYS_PORT_TIMEOUT_INFINITE;
accept.EndpointHandleOut = &endpoint;
KSTATUS status = SysAcceptPort(&accept);
if (!KSUCCESS(status)) RssrvExit(16);
status = RssrvSessionLock(session);
if (!KSUCCESS(status)) {
(void)SysCloseHandle(endpoint);
continue;
}
if (session->DwmEndpoint != SYS_HANDLE_INVALID) {
RssrvSessionUnlock(session);
(void)SysDisconnectPort(endpoint);
(void)SysCloseHandle(endpoint);
continue;
}
session->DwmEndpoint = endpoint;
RssrvSessionChangedLocked(session);
RssrvSessionUnlock(session);
for (;;) {
NTL_SESSION_DWM_REQUEST request;
SYS_PORT_MESSAGE_INFO info;
memset(&request, 0, sizeof(request));
memset(&info, 0, sizeof(info));
info.Size = sizeof(info);
SYS_RECEIVE_PORT_REQUEST receive;
memset(&receive, 0, sizeof(receive));
receive.Size = sizeof(receive);
receive.EndpointHandle = endpoint;
receive.Flags = SYS_PORT_FLAG_NONE;
receive.Payload = &request;
receive.PayloadCapacity = sizeof(request);
receive.InfoOut = &info;
receive.TimeoutMilliseconds = SYS_PORT_TIMEOUT_INFINITE;
status = SysReceivePort(&receive);
if (status == NTLSTATUS_PORT_DISCONNECTED || !KSUCCESS(status)) break;
if (info.Type != SYS_PORT_MESSAGE_REQUEST || info.HandleCount != 0 ||
info.PayloadLength != sizeof(request) || request.Size != sizeof(request) ||
request.Version != NTL_SESSION_DWM_PROTOCOL_VERSION ||
request.Flags != NTL_SESSION_DWM_FLAG_NONE || request.Reserved[0] != 0 ||
request.Reserved[1] != 0 || request.Reserved[2] != 0) {
RssrvSessionReplyDwm(endpoint, info.MessageId, request.Operation, NTLSTATUS_INVALID_PARAMETER);
continue;
}
status = RssrvSessionApplyDwmRequest(session, &request);
RssrvSessionReplyDwm(endpoint, info.MessageId, request.Operation, status);
}
RssrvSessionDisconnectDwm(session, endpoint);
(void)SysDisconnectPort(endpoint);
(void)SysCloseHandle(endpoint);
}
}
// Initializes Session 0 and creates the private listener that init may delegate only to DWM.
KSTATUS RssrvSessionInitialize(RSSRV_SESSION_STATE* session,
RSSRV_NAMESPACE* namespaceState,
RSSRV_DISPLAY_STATE* display) {
if (!session || !namespaceState || !display) return NTLSTATUS_INVALID_PARAMETER;
memset(session, 0, sizeof(*session));
session->Mutex = SYS_HANDLE_INVALID;
session->DwmListener = SYS_HANDLE_INVALID;
session->DwmEndpoint = SYS_HANDLE_INVALID;
session->Namespace = namespaceState;
session->Display = display;
session->Id = 0;
session->Generation = 1;
session->NextTargetId = 1;
SYS_DISPLAY_INFO info;
memset(&info, 0, sizeof(info));
KSTATUS status = RssrvDisplayQueryInfo(display, &info);
if (!KSUCCESS(status)) return status;
session->DisplayWidth = info.Mode.Width ? (int32_t)info.Mode.Width : 1;
session->DisplayHeight = info.Mode.Height ? (int32_t)info.Mode.Height : 1;
status = SysCreateMutex(&session->Mutex);
if (!KSUCCESS(status)) return status;
status = RssrvNamespaceCreateDirectory(namespaceState,
RSSRV_SESSION_ZERO_PATH,
sizeof(RSSRV_SESSION_ZERO_PATH) - 1u);
if (!KSUCCESS(status)) return status;
SYS_CREATE_PORT_REQUEST create;
memset(&create, 0, sizeof(create));
create.Size = sizeof(create);
create.Flags = SYS_PORT_FLAG_NONE;
create.Backlog = RSSRV_DWM_LISTENER_BACKLOG;
create.HandleOut = &session->DwmListener;
return SysCreatePort(&create);
}
// Registers one stable in-process input target with Session 0.
KSTATUS RssrvSessionRegisterInputTarget(RSSRV_SESSION_STATE* session,
RSSRV_INPUT_TARGET* target,
uint32_t kind,
void* context,
RSSRV_INPUT_TARGET_KEY_ROUTINE keyRoutine,
RSSRV_INPUT_TARGET_MOUSE_ROUTINE mouseRoutine) {
if (!session || !target || kind == RSSRV_INPUT_TARGET_KIND_NONE || !context)
return NTLSTATUS_INVALID_PARAMETER;
KSTATUS status = RssrvSessionLock(session);
if (!KSUCCESS(status)) return status;
if (target->Id != 0) {
RssrvSessionUnlock(session);
return NTLSTATUS_OBJECT_NAME_COLLISION;
}
memset(target, 0, sizeof(*target));
target->Id = session->NextTargetId++;
if (target->Id == 0) target->Id = session->NextTargetId++;
target->Kind = kind;
target->Session = session;
target->Context = context;
target->KeyRoutine = keyRoutine;
target->MouseRoutine = mouseRoutine;
RssrvSessionChangedLocked(session);
RssrvSessionUnlock(session);
return NTLSTATUS_SUCCESS;
}
// Activates one registered target and gives it ordinary keyboard and pointer focus.
KSTATUS RssrvSessionActivateInputTarget(RSSRV_SESSION_STATE* session, RSSRV_INPUT_TARGET* target) {
if (!session || !target || target->Id == 0 || target->Session != session) return NTLSTATUS_INVALID_PARAMETER;
KSTATUS status = RssrvSessionLock(session);
if (!KSUCCESS(status)) return status;
session->ActiveTarget = target;
session->KeyboardFocus = target;
session->PointerFocus = target;
RssrvSessionChangedLocked(session);
RssrvSessionUnlock(session);
return NTLSTATUS_SUCCESS;
}
// Sets keyboard focus without changing activation or pointer routing.
KSTATUS RssrvSessionSetKeyboardFocus(RSSRV_SESSION_STATE* session, RSSRV_INPUT_TARGET* target) {
if (!session || (target && (target->Id == 0 || target->Session != session))) return NTLSTATUS_INVALID_PARAMETER;
KSTATUS status = RssrvSessionLock(session);
if (!KSUCCESS(status)) return status;
if (session->KeyboardFocus != target) {
session->KeyboardFocus = target;
RssrvSessionChangedLocked(session);
}
RssrvSessionUnlock(session);
return NTLSTATUS_SUCCESS;
}
// Sets ordinary pointer focus without disturbing an active pointer capture.
KSTATUS RssrvSessionSetPointerFocus(RSSRV_SESSION_STATE* session, RSSRV_INPUT_TARGET* target) {
if (!session || (target && (target->Id == 0 || target->Session != session))) return NTLSTATUS_INVALID_PARAMETER;
KSTATUS status = RssrvSessionLock(session);
if (!KSUCCESS(status)) return status;
if (session->PointerFocus != target) {
session->PointerFocus = target;
RssrvSessionChangedLocked(session);
}
RssrvSessionUnlock(session);
return NTLSTATUS_SUCCESS;
}
// Sets or releases pointer capture independently of ordinary pointer focus.
KSTATUS RssrvSessionSetPointerCapture(RSSRV_SESSION_STATE* session, RSSRV_INPUT_TARGET* target) {
if (!session || (target && (target->Id == 0 || target->Session != session))) return NTLSTATUS_INVALID_PARAMETER;
KSTATUS status = RssrvSessionLock(session);
if (!KSUCCESS(status)) return status;
if (session->PointerCapture != target) {
session->PointerCapture = target;
RssrvSessionChangedLocked(session);
}
RssrvSessionUnlock(session);
return NTLSTATUS_SUCCESS;
}
// Returns the active session display bounds used to clamp the logical pointer.
KSTATUS RssrvSessionQueryDisplayBounds(RSSRV_SESSION_STATE* session, int32_t* widthOut, int32_t* heightOut) {
if (!session || !widthOut || !heightOut) return NTLSTATUS_INVALID_PARAMETER;
KSTATUS status = RssrvSessionLock(session);
if (!KSUCCESS(status)) return status;
*widthOut = session->DisplayWidth;
*heightOut = session->DisplayHeight;
RssrvSessionUnlock(session);
return NTLSTATUS_SUCCESS;
}
// Gives DWM a non-consuming observation of one logical key before authoritative session routing occurs.
void RssrvSessionObserveKey(RSSRV_SESSION_STATE* session, const INPUT_KEY_RECORD* record) {
if (!session || !record) return;
if (!KSUCCESS(RssrvSessionLock(session))) return;
RssrvSessionSendDwmEventLocked(session, NTL_SESSION_DWM_EVENT_OBSERVE_KEY, record, 0, 0, 0);
RssrvSessionUnlock(session);
}
// Gives DWM a non-consuming observation of one logical mouse event for hit testing and cursor policy.
void RssrvSessionObserveMouse(RSSRV_SESSION_STATE* session,
const INPUT_MOUSE_EVENT* event,
int32_t x,
int32_t y) {
if (!session || !event) return;
if (!KSUCCESS(RssrvSessionLock(session))) return;
RssrvSessionSendDwmEventLocked(session, NTL_SESSION_DWM_EVENT_OBSERVE_MOUSE, 0, event, x, y);
RssrvSessionUnlock(session);
}
// Routes one logical keyboard record to the current keyboard-focus target.
void RssrvSessionRouteKey(RSSRV_SESSION_STATE* session, const INPUT_KEY_RECORD* record) {
if (!session || !record) return;
RSSRV_INPUT_TARGET* target = (RSSRV_INPUT_TARGET*)0;
if (KSUCCESS(RssrvSessionLock(session))) {
target = session->KeyboardFocus;
RssrvSessionUnlock(session);
}
if (target && target->KeyRoutine) target->KeyRoutine(target->Context, record);
}
// Routes one logical mouse event through capture or pointer focus; DWM owns visible cursor policy.
void RssrvSessionRouteMouse(RSSRV_SESSION_STATE* session,
const INPUT_MOUSE_EVENT* event,
int32_t x,
int32_t y) {
if (!session || !event) return;
RSSRV_INPUT_TARGET* target = (RSSRV_INPUT_TARGET*)0;
if (KSUCCESS(RssrvSessionLock(session))) {
target = session->PointerCapture ? session->PointerCapture : session->PointerFocus;
RssrvSessionUnlock(session);
}
if (target && target->MouseRoutine) target->MouseRoutine(target->Context, event, x, y);
}
// Publishes Session 0's console alias and starts the private DWM authority listener.
KSTATUS RssrvSessionStart(RSSRV_SESSION_STATE* session, RSSRV_CONSOLE_STATE* console) {
if (!session || !console || console->Listener == SYS_HANDLE_INVALID || !session->ActiveTarget ||
session->DwmListener == SYS_HANDLE_INVALID)
return NTLSTATUS_INVALID_PARAMETER;
KSTATUS status = RssrvSessionRegisterInputTarget(session, &session->DesktopTarget,
RSSRV_INPUT_TARGET_KIND_DESKTOP, session,
RssrvSessionDwmRouteKey, RssrvSessionDwmRouteMouse);
if (!KSUCCESS(status)) return status;
SYS_HANDLE sessionConsole = SYS_HANDLE_INVALID;
SYS_DERIVE_HANDLE_REQUEST derive;
memset(&derive, 0, sizeof(derive));
derive.Size = sizeof(derive);
derive.SourceHandle = console->Listener;
derive.DesiredRights = SYS_RIGHT_TRANSFER | SYS_RIGHT_QUERY | SYS_PORT_RIGHT_CONNECT;
derive.DerivedHandleOut = &sessionConsole;
status = SysDeriveHandle(&derive);
if (!KSUCCESS(status)) return status;
status = RssrvNamespaceBindOwned(session->Namespace,
RSSRV_SESSION_ZERO_CONSOLE_PATH,
sizeof(RSSRV_SESSION_ZERO_CONSOLE_PATH) - 1u,
NTL_RESOURCE_KIND_CAPABILITY,
sessionConsole,
SYS_RIGHT_QUERY | SYS_PORT_RIGHT_CONNECT);
if (!KSUCCESS(status)) {
(void)SysCloseHandle(sessionConsole);
return status;
}
status = RssrvNamespaceCreateSymbolicLink(session->Namespace,
RSSRV_CURRENT_CONSOLE_PATH,
sizeof(RSSRV_CURRENT_CONSOLE_PATH) - 1u,
RSSRV_SESSION_ZERO_CONSOLE_PATH,
sizeof(RSSRV_SESSION_ZERO_CONSOLE_PATH) - 1u);
if (!KSUCCESS(status)) return status;
SYS_CREATE_THREAD_REQUEST create;
memset(&create, 0, sizeof(create));
create.Size = sizeof(create);
create.Entry = (uintptr_t)RssrvSessionDwmListenerThread;
create.Argument = (uintptr_t)session;
create.Priority = 1u;
create.Flags = SYS_CREATE_THREAD_FLAG_NONE;
return SysCreateThread(&create);
}
+279
View File
@@ -0,0 +1,279 @@
// RSSRV mounted-volume publication and /Device/HarddiskVolumeN synchronization.
#include "rssrv_internal.h"
#include <stdlib.h>
#include <string.h>
struct RSSRV_VOLUME_PUBLICATION {
RSSRV_VOLUME_PUBLICATION* Next;
uint32_t InstanceId;
uint32_t NamespaceId;
uint32_t PathLength;
char Path[RSSRV_VOLUME_PATH_CAPACITY];
};
// Finds one published mounted volume by kernel instance identity.
static RSSRV_VOLUME_PUBLICATION* RssrvFindPublishedVolume(RSSRV_VOLUME_STATE* volumes, uint32_t instanceId) {
if (!volumes) return (RSSRV_VOLUME_PUBLICATION*)0;
for (RSSRV_VOLUME_PUBLICATION* publication = volumes->Published; publication; publication = publication->Next) {
if (publication->InstanceId == instanceId)
return publication;
}
return (RSSRV_VOLUME_PUBLICATION*)0;
}
// Returns whether one kernel volume instance is present in the current manager snapshot.
static int RssrvSnapshotContainsVolume(const SYS_VOLUME_INFO* snapshot, uint32_t count, uint32_t instanceId) {
for (uint32_t i = 0; i < count; ++i) {
if (snapshot[i].InstanceId == instanceId)
return 1;
}
return 0;
}
// Builds one RSSRV-local /Device/HarddiskVolumeN name without introducing a formatting-runtime dependency.
static KSTATUS RssrvBuildHarddiskVolumePath(uint32_t namespaceId,
char* path,
uint32_t capacity,
uint32_t* lengthOut) {
static const char prefix[] = "/Device/HarddiskVolume";
if (!namespaceId || !path || !lengthOut)
return NTLSTATUS_INVALID_PARAMETER;
uint32_t prefixLength = (uint32_t)(sizeof(prefix) - 1u);
char digits[10];
uint32_t digitCount = 0;
uint32_t value = namespaceId;
do {
digits[digitCount++] = (char)('0' + (value % 10u));
value /= 10u;
} while (value && digitCount < (uint32_t)sizeof(digits));
if (prefixLength + digitCount + 1u > capacity)
return NTLSTATUS_BUFFER_TOO_SMALL;
memcpy(path, prefix, prefixLength);
for (uint32_t i = 0; i < digitCount; ++i)
path[prefixLength + i] = digits[digitCount - 1u - i];
path[prefixLength + digitCount] = '\0';
*lengthOut = prefixLength + digitCount;
return NTLSTATUS_SUCCESS;
}
// Takes a stable manager snapshot, growing the userspace buffer if the mounted-volume count changes.
static KSTATUS RssrvQueryVolumes(SYS_HANDLE manager,
SYS_VOLUME_INFO** volumesOut,
uint32_t* countOut,
uint64_t* generationOut) {
if (!volumesOut || !countOut || !generationOut)
return NTLSTATUS_INVALID_PARAMETER;
*volumesOut = (SYS_VOLUME_INFO*)0;
*countOut = 0;
*generationOut = 0;
SYS_VOLUME_INFO* snapshot = (SYS_VOLUME_INFO*)0;
uint32_t capacity = 0;
for (;;) {
uint32_t count = 0;
uint64_t generation = 0;
SYS_QUERY_VOLUMES_REQUEST query;
memset(&query, 0, sizeof(query));
query.Size = sizeof(query);
query.ManagerHandle = manager;
query.Flags = SYS_VOLUME_QUERY_FLAG_NONE;
query.Capacity = capacity;
query.Volumes = snapshot;
query.CountOut = &count;
query.GenerationOut = &generation;
KSTATUS status = SysQueryVolumes(&query);
if (status == NTLSTATUS_BUFFER_TOO_SMALL) {
if (snapshot) free(snapshot);
snapshot = (SYS_VOLUME_INFO*)0;
capacity = count;
if (capacity) {
snapshot = (SYS_VOLUME_INFO*)malloc((size_t)capacity * sizeof(*snapshot));
if (!snapshot)
return NTLSTATUS_NO_MEMORY;
memset(snapshot, 0, (size_t)capacity * sizeof(*snapshot));
}
continue;
}
if (!KSUCCESS(status)) {
if (snapshot) free(snapshot);
return status;
}
for (uint32_t i = 0; i < count; ++i) {
if (snapshot[i].Size != sizeof(SYS_VOLUME_INFO) ||
snapshot[i].Version != SYS_VOLUME_ENUM_VERSION || !snapshot[i].InstanceId ||
snapshot[i].Flags != 0) {
if (snapshot) free(snapshot);
return NTLSTATUS_INVALID_PARAMETER;
}
}
*volumesOut = snapshot;
*countOut = count;
*generationOut = generation;
return NTLSTATUS_SUCCESS;
}
}
// Keeps automatic /Device/HarddiskVolumeN bindings synchronized with mounted IO_VOLUME instances.
static void RssrvVolumeWorker(uintptr_t argument) {
RSSRV_VOLUME_STATE* volumes = (RSSRV_VOLUME_STATE*)argument;
if (!volumes) {
SysExitThread(1);
return;
}
RssrvSetCurrentThreadName("rssrv.volume");
for (;;) {
uint64_t generation = 0;
KSTATUS status = RssrvVolumesSynchronize(volumes, &generation);
if (!KSUCCESS(status))
RssrvExit(12);
status = SysWaitVolumeChange(volumes->Manager, generation, SYS_WAIT_TIMEOUT_INFINITE);
if (!KSUCCESS(status))
RssrvExit(12);
}
}
// Initializes mounted-volume publication state against one volume-manager capability.
KSTATUS RssrvVolumesInitialize(RSSRV_VOLUME_STATE* volumes,
RSSRV_NAMESPACE* namespaceState,
SYS_HANDLE manager) {
if (!volumes || !namespaceState || manager == SYS_HANDLE_INVALID)
return NTLSTATUS_INVALID_PARAMETER;
memset(volumes, 0, sizeof(*volumes));
volumes->Namespace = namespaceState;
volumes->Manager = manager;
volumes->NextHarddiskVolumeId = 1u;
return NTLSTATUS_SUCCESS;
}
// Reconciles RSSRV volume bindings with the kernel's mounted-volume snapshot.
KSTATUS RssrvVolumesSynchronize(RSSRV_VOLUME_STATE* volumes, uint64_t* generationOut) {
if (!volumes || !volumes->Namespace || volumes->Manager == SYS_HANDLE_INVALID || !generationOut)
return NTLSTATUS_INVALID_PARAMETER;
SYS_VOLUME_INFO* snapshot = (SYS_VOLUME_INFO*)0;
uint32_t count = 0;
uint64_t generation = 0;
KSTATUS status = RssrvQueryVolumes(volumes->Manager, &snapshot, &count, &generation);
if (!KSUCCESS(status))
return status;
RSSRV_VOLUME_PUBLICATION** link = &volumes->Published;
while (*link) {
RSSRV_VOLUME_PUBLICATION* publication = *link;
if (RssrvSnapshotContainsVolume(snapshot, count, publication->InstanceId)) {
link = &publication->Next;
continue;
}
status = RssrvNamespaceUnbind(volumes->Namespace, publication->Path, publication->PathLength);
if (!KSUCCESS(status) && status != NTLSTATUS_NOT_FOUND) {
free(snapshot);
return status;
}
*link = publication->Next;
free(publication);
}
for (uint32_t i = 0; i < count; ++i) {
if (RssrvFindPublishedVolume(volumes, snapshot[i].InstanceId))
continue;
SYS_HANDLE volume = SYS_HANDLE_INVALID;
status = SysOpenVolume(volumes->Manager, snapshot[i].InstanceId,
SYS_RIGHT_QUERY | SYS_RIGHT_TRANSFER |
SYS_VOLUME_RIGHT_OPEN | SYS_VOLUME_RIGHT_MODIFY,
&volume);
if (status == NTLSTATUS_NOT_FOUND || status == NTLSTATUS_OBJECT_DEAD)
continue;
if (!KSUCCESS(status)) {
free(snapshot);
return status;
}
RSSRV_VOLUME_PUBLICATION* publication =
(RSSRV_VOLUME_PUBLICATION*)malloc(sizeof(*publication));
if (!publication) {
(void)SysCloseHandle(volume);
free(snapshot);
return NTLSTATUS_NO_MEMORY;
}
memset(publication, 0, sizeof(*publication));
publication->InstanceId = snapshot[i].InstanceId;
publication->NamespaceId = volumes->NextHarddiskVolumeId;
status = RssrvBuildHarddiskVolumePath(publication->NamespaceId,
publication->Path,
(uint32_t)sizeof(publication->Path),
&publication->PathLength);
if (!KSUCCESS(status)) {
free(publication);
(void)SysCloseHandle(volume);
free(snapshot);
return status;
}
status = RssrvNamespaceBindOwned(volumes->Namespace,
publication->Path,
publication->PathLength,
NTL_RESOURCE_KIND_VOLUME,
volume,
SYS_RIGHT_QUERY | SYS_VOLUME_RIGHT_OPEN | SYS_VOLUME_RIGHT_MODIFY);
if (!KSUCCESS(status)) {
free(publication);
(void)SysCloseHandle(volume);
free(snapshot);
return status;
}
publication->Next = volumes->Published;
volumes->Published = publication;
volumes->NextHarddiskVolumeId++;
if (!volumes->NextHarddiskVolumeId) {
free(snapshot);
return NTLSTATUS_LIMIT_REACHED;
}
}
if (snapshot) free(snapshot);
*generationOut = generation;
return NTLSTATUS_SUCCESS;
}
// Copies the namespace path assigned to one mounted kernel volume instance.
KSTATUS RssrvVolumesQueryPublishedPath(RSSRV_VOLUME_STATE* volumes,
uint32_t instanceId,
char* pathOut,
uint32_t capacity,
uint32_t* lengthOut) {
if (!volumes || !instanceId || !pathOut || !capacity || !lengthOut)
return NTLSTATUS_INVALID_PARAMETER;
*lengthOut = 0;
RSSRV_VOLUME_PUBLICATION* publication = RssrvFindPublishedVolume(volumes, instanceId);
if (!publication) return NTLSTATUS_NOT_FOUND;
if (publication->PathLength + 1u > capacity)
return NTLSTATUS_BUFFER_TOO_SMALL;
memcpy(pathOut, publication->Path, publication->PathLength + 1u);
*lengthOut = publication->PathLength;
return NTLSTATUS_SUCCESS;
}
// Starts the mounted-volume namespace publication worker.
KSTATUS RssrvVolumesStartWorker(RSSRV_VOLUME_STATE* volumes) {
if (!volumes || volumes->Manager == SYS_HANDLE_INVALID)
return NTLSTATUS_INVALID_PARAMETER;
SYS_CREATE_THREAD_REQUEST create;
memset(&create, 0, sizeof(create));
create.Size = sizeof(create);
create.Entry = (uintptr_t)RssrvVolumeWorker;
create.Argument = (uintptr_t)volumes;
create.Priority = 1;
create.Flags = SYS_CREATE_THREAD_FLAG_NONE;
return SysCreateThread(&create);
}
// Returns whether a path belongs to the RSSRV-owned automatic mounted-volume naming family.
int RssrvVolumesOwnPath(const char* path, uint32_t length) {
static const char prefix[] = "/Device/HarddiskVolume";
uint32_t prefixLength = (uint32_t)(sizeof(prefix) - 1u);
return path && length >= prefixLength && memcmp(path, prefix, prefixLength) == 0;
}