Files
nt-lite/code/user/system/rssrv/rssrv.c
T

948 lines
37 KiB
C

// Native resource namespace service. RSSRV owns global names and transfers capabilities; it is not a file data path.
#include <ntlite/exception.h>
#include <ntlite/resource.h>
#include <ntlite/service.h>
#include <ntlite/service_control.h>
#include <ntlite/syscall.h>
#include <stdint.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#define RSSRV_LISTENER_BACKLOG 16u
// One namespace binding. Path storage follows the fixed header in the same allocation.
typedef struct RSSRV_ENTRY {
struct RSSRV_ENTRY* Next;
SYS_HANDLE Handle;
uint32_t Kind;
SYS_RIGHTS ExportRights;
uint32_t PathLength;
char Path[1];
} RSSRV_ENTRY;
typedef struct RSSRV_SESSION {
SYS_HANDLE Endpoint;
uint32_t Administrative;
} RSSRV_SESSION;
typedef struct RSSRV_LISTENER_WORKER {
SYS_HANDLE Listener;
uint32_t Administrative;
} RSSRV_LISTENER_WORKER;
typedef struct RSSRV_VOLUME_PUBLICATION {
struct RSSRV_VOLUME_PUBLICATION* Next;
uint32_t InstanceId;
uint32_t NamespaceId;
uint32_t PathLength;
char Path[48];
} RSSRV_VOLUME_PUBLICATION;
static SYS_HANDLE gNamespaceMutex = SYS_HANDLE_INVALID;
static RSSRV_ENTRY* gNamespace = (RSSRV_ENTRY*)0;
static SYS_HANDLE gPublicListener = SYS_HANDLE_INVALID;
static SYS_HANDLE gAdminListener = SYS_HANDLE_INVALID;
static RSSRV_LISTENER_WORKER gPublicWorker;
static RSSRV_LISTENER_WORKER gAdminWorker;
static RSSRV_VOLUME_PUBLICATION* gPublishedVolumes = (RSSRV_VOLUME_PUBLICATION*)0;
static uint32_t gNextHarddiskVolumeId = 1u;
// Exits without depending on a compiler runtime return path.
static void RssrvExit(int code) {
for (;;) SysExitProcess(code);
}
// Acquires the namespace mutex.
static KSTATUS RssrvLockNamespace(void) {
return SysWait(gNamespaceMutex, SYS_WAIT_FLAG_NONE);
}
// Releases the namespace mutex.
static void RssrvUnlockNamespace(void) {
(void)SysReleaseMutex(gNamespaceMutex);
}
// Validates one canonical absolute native resource path.
static KSTATUS RssrvValidatePath(const char* path, uint32_t length) {
if (!path || length < 2 || length > NTL_RESOURCE_PATH_MAX || path[0] != '/' || 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 == 1 && path[componentStart] == '.') ||
(componentLength == 2 && path[componentStart] == '.' && path[componentStart + 1] == '.'))
return NTLSTATUS_INVALID_PARAMETER;
componentStart = i + 1;
}
return NTLSTATUS_SUCCESS;
}
// Returns whether a path belongs to the RSSRV-owned automatic mounted-volume naming family.
static int RssrvIsManagedVolumePath(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;
}
// Finds one exact binding while the namespace mutex is held.
static RSSRV_ENTRY* RssrvFindLocked(const char* path, uint32_t length, RSSRV_ENTRY*** linkOut) {
RSSRV_ENTRY** link = &gNamespace;
while (*link) {
RSSRV_ENTRY* entry = *link;
if (entry->PathLength == length && memcmp(entry->Path, path, length) == 0) {
if (linkOut) *linkOut = link;
return entry;
}
link = &entry->Next;
}
if (linkOut) *linkOut = link;
return (RSSRV_ENTRY*)0;
}
// Finds the longest namespace binding that is an exact component prefix of path.
static RSSRV_ENTRY* RssrvFindPrefixLocked(const char* path, uint32_t length) {
RSSRV_ENTRY* best = (RSSRV_ENTRY*)0;
for (RSSRV_ENTRY* entry = gNamespace; entry; entry = entry->Next) {
if (entry->PathLength > length)
continue;
if (memcmp(entry->Path, path, entry->PathLength) != 0)
continue;
if (entry->PathLength != length && path[entry->PathLength] != '/')
continue;
if (!best || entry->PathLength > best->PathLength)
best = entry;
}
return best;
}
// Takes ownership of one handle and binds it to a new exact resource name.
static KSTATUS RssrvBindOwned(const char* path,
uint32_t length,
uint32_t kind,
SYS_HANDLE handle,
SYS_RIGHTS exportRights) {
if (kind == NTL_RESOURCE_KIND_INVALID || handle == SYS_HANDLE_INVALID || !exportRights)
return NTLSTATUS_INVALID_PARAMETER;
KSTATUS status = RssrvValidatePath(path, length);
if (!KSUCCESS(status)) return status;
size_t bytes = offsetof(RSSRV_ENTRY, Path) + (size_t)length + 1u;
RSSRV_ENTRY* entry = (RSSRV_ENTRY*)malloc(bytes);
if (!entry) return NTLSTATUS_NO_MEMORY;
memset(entry, 0, bytes);
entry->Handle = handle;
entry->Kind = kind;
entry->ExportRights = exportRights;
entry->PathLength = length;
memcpy(entry->Path, path, length);
entry->Path[length] = '\0';
status = RssrvLockNamespace();
if (!KSUCCESS(status)) {
free(entry);
return status;
}
if (RssrvFindLocked(path, length, (RSSRV_ENTRY***)0)) {
RssrvUnlockNamespace();
free(entry);
return NTLSTATUS_OBJECT_NAME_COLLISION;
}
entry->Next = gNamespace;
gNamespace = entry;
RssrvUnlockNamespace();
return NTLSTATUS_SUCCESS;
}
// Removes one binding and closes the namespace's retained handle.
static KSTATUS RssrvUnbind(const char* path, uint32_t length) {
KSTATUS status = RssrvValidatePath(path, length);
if (!KSUCCESS(status)) return status;
status = RssrvLockNamespace();
if (!KSUCCESS(status)) return status;
RSSRV_ENTRY** link = (RSSRV_ENTRY**)0;
RSSRV_ENTRY* entry = RssrvFindLocked(path, length, &link);
if (!entry) {
RssrvUnlockNamespace();
return NTLSTATUS_NOT_FOUND;
}
*link = entry->Next;
RssrvUnlockNamespace();
(void)SysCloseHandle(entry->Handle);
free(entry);
return NTLSTATUS_SUCCESS;
}
// Finds one published mounted volume by kernel instance identity.
static RSSRV_VOLUME_PUBLICATION* RssrvFindPublishedVolume(uint32_t instanceId) {
for (RSSRV_VOLUME_PUBLICATION* publication = gPublishedVolumes; 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* volumes, uint32_t count, uint32_t instanceId) {
for (uint32_t i = 0; i < count; ++i) {
if (volumes[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* volumes = (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 = volumes;
query.CountOut = &count;
query.GenerationOut = &generation;
KSTATUS status = SysQueryVolumes(&query);
if (status == NTLSTATUS_BUFFER_TOO_SMALL) {
if (volumes) free(volumes);
volumes = (SYS_VOLUME_INFO*)0;
capacity = count;
if (capacity) {
volumes = (SYS_VOLUME_INFO*)malloc((size_t)capacity * sizeof(*volumes));
if (!volumes)
return NTLSTATUS_NO_MEMORY;
memset(volumes, 0, (size_t)capacity * sizeof(*volumes));
}
continue;
}
if (!KSUCCESS(status)) {
if (volumes) free(volumes);
return status;
}
for (uint32_t i = 0; i < count; ++i) {
if (volumes[i].Size != sizeof(SYS_VOLUME_INFO) ||
volumes[i].Version != SYS_VOLUME_ENUM_VERSION || !volumes[i].InstanceId ||
volumes[i].Flags != 0) {
if (volumes) free(volumes);
return NTLSTATUS_INVALID_PARAMETER;
}
}
*volumesOut = volumes;
*countOut = count;
*generationOut = generation;
return NTLSTATUS_SUCCESS;
}
}
// Reconciles the RSSRV object namespace with the kernel's mounted-volume snapshot.
static KSTATUS RssrvSyncVolumes(SYS_HANDLE manager, uint64_t* generationOut) {
if (!generationOut)
return NTLSTATUS_INVALID_PARAMETER;
SYS_VOLUME_INFO* volumes = (SYS_VOLUME_INFO*)0;
uint32_t count = 0;
uint64_t generation = 0;
KSTATUS status = RssrvQueryVolumes(manager, &volumes, &count, &generation);
if (!KSUCCESS(status))
return status;
RSSRV_VOLUME_PUBLICATION** link = &gPublishedVolumes;
while (*link) {
RSSRV_VOLUME_PUBLICATION* publication = *link;
if (RssrvSnapshotContainsVolume(volumes, count, publication->InstanceId)) {
link = &publication->Next;
continue;
}
status = RssrvUnbind(publication->Path, publication->PathLength);
if (!KSUCCESS(status) && status != NTLSTATUS_NOT_FOUND) {
free(volumes);
return status;
}
*link = publication->Next;
free(publication);
}
for (uint32_t i = 0; i < count; ++i) {
if (RssrvFindPublishedVolume(volumes[i].InstanceId))
continue;
SYS_HANDLE volume = SYS_HANDLE_INVALID;
status = SysOpenVolume(manager, volumes[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(volumes);
return status;
}
RSSRV_VOLUME_PUBLICATION* publication =
(RSSRV_VOLUME_PUBLICATION*)malloc(sizeof(*publication));
if (!publication) {
(void)SysCloseHandle(volume);
free(volumes);
return NTLSTATUS_NO_MEMORY;
}
memset(publication, 0, sizeof(*publication));
publication->InstanceId = volumes[i].InstanceId;
publication->NamespaceId = gNextHarddiskVolumeId;
status = RssrvBuildHarddiskVolumePath(publication->NamespaceId,
publication->Path,
(uint32_t)sizeof(publication->Path),
&publication->PathLength);
if (!KSUCCESS(status)) {
free(publication);
(void)SysCloseHandle(volume);
free(volumes);
return status;
}
status = RssrvBindOwned(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(volumes);
return status;
}
publication->Next = gPublishedVolumes;
gPublishedVolumes = publication;
gNextHarddiskVolumeId++;
if (!gNextHarddiskVolumeId) {
free(volumes);
return NTLSTATUS_LIMIT_REACHED;
}
}
if (volumes) free(volumes);
*generationOut = generation;
return NTLSTATUS_SUCCESS;
}
// Keeps automatic /Device/HarddiskVolumeN bindings synchronized with mounted IO_VOLUME instances.
static void RssrvVolumeWorker(uintptr_t argument) {
SYS_HANDLE manager = (SYS_HANDLE)argument;
for (;;) {
uint64_t generation = 0;
KSTATUS status = RssrvSyncVolumes(manager, &generation);
if (!KSUCCESS(status))
RssrvExit(12);
status = SysWaitVolumeChange(manager, generation, SYS_WAIT_TIMEOUT_INFINITE);
if (!KSUCCESS(status))
RssrvExit(12);
}
}
// Starts the mounted-volume namespace publication worker.
static KSTATUS RssrvStartVolumeWorker(SYS_HANDLE manager) {
uint32_t threadId = 0;
SYS_CREATE_THREAD_REQUEST create;
memset(&create, 0, sizeof(create));
create.Size = sizeof(create);
create.Entry = (uintptr_t)RssrvVolumeWorker;
create.Argument = (uintptr_t)manager;
create.Priority = 1;
create.Flags = SYS_CREATE_THREAD_FLAG_NONE;
create.ThreadIdOut = &threadId;
return SysCreateThread(&create);
}
// 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 lookup while retaining the namespace mutex through capability transfer.
static KSTATUS RssrvHandleLookup(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);
KSTATUS status = RssrvLockNamespace();
if (!KSUCCESS(status))
return RssrvReplyStatus(endpoint, messageId, request->Operation, status, NTL_RESOURCE_KIND_INVALID);
RSSRV_ENTRY* entry = RssrvFindLocked(path, request->PathLength, (RSSRV_ENTRY***)0);
if (!entry) {
RssrvUnlockNamespace();
return RssrvReplyStatus(endpoint, messageId, request->Operation,
NTLSTATUS_NOT_FOUND, NTL_RESOURCE_KIND_INVALID);
}
uint32_t kind = entry->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 = entry->Handle;
if ((request->Rights & ~entry->ExportRights) != 0) {
RssrvUnlockNamespace();
return RssrvReplyStatus(endpoint, messageId, request->Operation,
NTLSTATUS_ACCESS_DENIED, kind);
}
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);
RssrvUnlockNamespace();
// A rights mismatch leaves the synchronous call pending, so return the precise error without a handle.
if (!KSUCCESS(status))
return RssrvReplyStatus(endpoint, messageId, request->Operation, status, kind);
return NTLSTATUS_SUCCESS;
}
// Resolves the longest bound resource prefix and transfers traversal authority.
static KSTATUS RssrvHandleResolve(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);
KSTATUS status = RssrvLockNamespace();
if (!KSUCCESS(status))
return RssrvReplyStatus(endpoint, messageId, request->Operation, status, NTL_RESOURCE_KIND_INVALID);
RSSRV_ENTRY* entry = RssrvFindPrefixLocked(path, request->PathLength);
if (!entry) {
RssrvUnlockNamespace();
return RssrvReplyStatus(endpoint, messageId, request->Operation,
NTLSTATUS_NOT_FOUND, NTL_RESOURCE_KIND_INVALID);
}
SYS_RIGHTS desiredRights = 0;
if (entry->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 (entry->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 = entry->Kind;
RssrvUnlockNamespace();
return RssrvReplyStatus(endpoint, messageId, request->Operation,
NTLSTATUS_NOT_SUPPORTED, kind);
}
if ((desiredRights & ~entry->ExportRights) != 0) {
uint32_t kind = entry->Kind;
RssrvUnlockNamespace();
return RssrvReplyStatus(endpoint, messageId, request->Operation,
NTLSTATUS_ACCESS_DENIED, kind);
}
uint32_t kind = entry->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 = entry->PathLength;
SYS_PORT_HANDLE_TRANSFER transfer;
transfer.Handle = entry->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);
RssrvUnlockNamespace();
if (!KSUCCESS(status))
return RssrvReplyStatus(endpoint, messageId, request->Operation, status, kind);
return NTLSTATUS_SUCCESS;
}
// Handles one metadata query.
static KSTATUS RssrvHandleQuery(SYS_HANDLE endpoint,
uint32_t messageId,
const NTL_RESOURCE_REQUEST* request,
const char* path) {
KSTATUS status = RssrvLockNamespace();
if (!KSUCCESS(status))
return RssrvReplyStatus(endpoint, messageId, request->Operation, status, NTL_RESOURCE_KIND_INVALID);
RSSRV_ENTRY* entry = RssrvFindLocked(path, request->PathLength, (RSSRV_ENTRY***)0);
uint32_t found = entry != (RSSRV_ENTRY*)0;
uint32_t kind = found ? entry->Kind : NTL_RESOURCE_KIND_INVALID;
RssrvUnlockNamespace();
return RssrvReplyStatus(endpoint, messageId, request->Operation,
found ? NTLSTATUS_SUCCESS : NTLSTATUS_NOT_FOUND, kind);
}
// Handles one administrative bind. On success the namespace takes ownership of receivedHandle.
static KSTATUS RssrvHandleBind(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 || RssrvIsManagedVolumePath(path, request->PathLength))
return RssrvReplyStatus(endpoint, messageId, request->Operation,
NTLSTATUS_ACCESS_DENIED, NTL_RESOURCE_KIND_INVALID);
KSTATUS bindStatus = RssrvBindOwned(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(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 (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;
if (request.Size != sizeof(request) || request.Version != NTL_RESOURCE_PROTOCOL_VERSION ||
request.PathLength == 0 || payloadLength != sizeof(request) + request.PathLength) {
(void)RssrvReplyStatus(endpoint, info->MessageId, operation,
NTLSTATUS_INVALID_PARAMETER, NTL_RESOURCE_KIND_INVALID);
return 0;
}
const char* path = (const char*)(payload + sizeof(request));
KSTATUS status = RssrvValidatePath(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 || 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(endpoint, info->MessageId, &request, path);
return 0;
case NTL_RESOURCE_OP_QUERY:
if (request.Flags != NTL_RESOURCE_FLAG_NONE || 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(endpoint, info->MessageId, &request, path);
return 0;
case NTL_RESOURCE_OP_RESOLVE:
if (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(endpoint, info->MessageId, &request, path);
return 0;
case NTL_RESOURCE_OP_BIND:
if (request.Flags != NTL_RESOURCE_FLAG_NONE || info->HandleCount != 1 ||
!(request.Rights & SYS_RIGHT_TRANSFER) ||
request.ExportRights == 0 || (request.ExportRights & ~request.Rights) != 0 ||
request.Kind == NTL_RESOURCE_KIND_INVALID || 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(endpoint, info->MessageId, &request, path, administrative,
receivedHandle, &consumed);
return consumed;
}
case NTL_RESOURCE_OP_UNBIND:
if (request.Flags != NTL_RESOURCE_FLAG_NONE || 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 && !RssrvIsManagedVolumePath(path, request.PathLength)
? RssrvUnbind(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) {
SysExitThread(1);
return;
}
SYS_HANDLE endpoint = session->Endpoint;
uint32_t administrative = session->Administrative;
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(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) {
SysExitThread(1);
return;
}
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->Endpoint = endpoint;
session->Administrative = worker->Administrative;
uint32_t threadId = 0;
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;
create.ThreadIdOut = &threadId;
status = SysCreateThread(&create);
if (!KSUCCESS(status)) {
free(session);
(void)SysDisconnectPort(endpoint);
(void)SysCloseHandle(endpoint);
}
}
SysExitThread(0);
}
// Starts one resource listener worker thread.
static KSTATUS RssrvStartListenerWorker(RSSRV_LISTENER_WORKER* worker,
SYS_HANDLE listener,
uint32_t administrative) {
worker->Listener = listener;
worker->Administrative = administrative;
uint32_t threadId = 0;
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;
create.ThreadIdOut = &threadId;
return SysCreateThread(&create);
}
// 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);
}
// Accepts the one private service-manager control connection.
static KSTATUS RssrvAcceptControl(SYS_HANDLE controlListener, SYS_HANDLE* endpointOut) {
SYS_ACCEPT_PORT_REQUEST accept;
memset(&accept, 0, sizeof(accept));
accept.Size = sizeof(accept);
accept.ListenerHandle = controlListener;
accept.Flags = SYS_PORT_ACCEPT_FLAG_NONE;
accept.TimeoutMilliseconds = SYS_PORT_TIMEOUT_INFINITE;
accept.EndpointHandleOut = endpointOut;
return SysAcceptPort(&accept);
}
// Sends service readiness and delegates the private administrative resource listener to init.
static KSTATUS RssrvSendReady(SYS_HANDLE controlEndpoint) {
NTL_SERVICE_CONTROL_MESSAGE message;
memset(&message, 0, sizeof(message));
message.Size = sizeof(message);
message.Version = NTL_SERVICE_CONTROL_VERSION;
message.Operation = NTL_SERVICE_CONTROL_READY;
message.Flags = NTL_SERVICE_READY_FLAG_RESOURCE_ADMIN_LISTENER;
message.Status = NTLSTATUS_SUCCESS;
SYS_PORT_HANDLE_TRANSFER transfer;
transfer.Handle = gAdminListener;
transfer.DesiredRights = SYS_RIGHT_QUERY | SYS_PORT_RIGHT_CONNECT;
transfer.Flags = SYS_PORT_TRANSFER_FLAG_NONE;
SYS_SEND_PORT_REQUEST send;
memset(&send, 0, sizeof(send));
send.Size = sizeof(send);
send.EndpointHandle = controlEndpoint;
send.Flags = SYS_PORT_FLAG_NONE;
send.Payload = &message;
send.PayloadLength = sizeof(message);
send.Handles = &transfer;
send.HandleCount = 1;
send.TimeoutMilliseconds = SYS_PORT_TIMEOUT_INFINITE;
return SysSendPort(&send);
}
// Waits for the service manager's stop notification or control-port disconnect.
static void RssrvControlLoop(SYS_HANDLE controlEndpoint) {
for (;;) {
NTL_SERVICE_CONTROL_MESSAGE message;
SYS_PORT_MESSAGE_INFO info;
memset(&message, 0, sizeof(message));
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 = controlEndpoint;
receive.Payload = &message;
receive.PayloadCapacity = sizeof(message);
receive.InfoOut = &info;
receive.TimeoutMilliseconds = SYS_PORT_TIMEOUT_INFINITE;
KSTATUS status = SysReceivePort(&receive);
if (status == NTLSTATUS_PORT_DISCONNECTED)
return;
if (!KSUCCESS(status))
return;
if (info.Type != SYS_PORT_MESSAGE_NOTIFICATION || info.HandleCount != 0 ||
info.PayloadLength != sizeof(message) || message.Size != sizeof(message) ||
message.Version != NTL_SERVICE_CONTROL_VERSION)
continue;
if (message.Operation == NTL_SERVICE_CONTROL_STOP)
return;
}
}
// Starts RSSRV and owns the global native resource namespace until init requests shutdown.
void _start(void) {
KSTATUS status = NtlInitializeExceptionRuntime();
if (!KSUCCESS(status)) RssrvExit(1);
SYS_SECURITY_CONTEXT_INFO security;
memset(&security, 0, sizeof(security));
security.Size = sizeof(security);
status = SysQuerySecurityContext(SYS_SECURITY_CONTEXT_CURRENT, &security);
if (!KSUCCESS(status) || security.EffectivePrivileges != SYS_PRIV_SYSTEM_SERVICE_REGISTER ||
security.DelegablePrivileges != 0 || security.ServiceClaims != SYS_SERVICE_CLAIM_RESOURCE ||
security.DelegableServiceClaims != 0)
RssrvExit(2);
SYS_HANDLE systemVolume = SysGetStartupCapability(SYS_STARTUP_CAP_SYSTEM_VOLUME_NAMESPACE);
SYS_HANDLE controlListener = SysGetStartupCapability(SYS_STARTUP_CAP_SERVICE_CONTROL);
SYS_HANDLE volumeManager = SysGetStartupCapability(SYS_STARTUP_CAP_VOLUME_MANAGER);
if (systemVolume == SYS_HANDLE_INVALID || controlListener == SYS_HANDLE_INVALID ||
volumeManager == SYS_HANDLE_INVALID)
RssrvExit(3);
status = SysCreateMutex(&gNamespaceMutex);
if (!KSUCCESS(status)) RssrvExit(3);
// RSSRV takes ownership of its startup system-volume handle as the first namespace binding.
static const char systemRoot[] = "/SystemRoot";
status = RssrvBindOwned(systemRoot, sizeof(systemRoot) - 1, NTL_RESOURCE_KIND_VOLUME, systemVolume,
SYS_RIGHT_QUERY | SYS_VOLUME_RIGHT_OPEN | SYS_VOLUME_RIGHT_MODIFY);
if (!KSUCCESS(status)) RssrvExit(4);
SYS_HANDLE controlEndpoint = SYS_HANDLE_INVALID;
status = RssrvAcceptControl(controlListener, &controlEndpoint);
(void)SysCloseHandle(controlListener);
if (!KSUCCESS(status)) RssrvExit(5);
status = RssrvCreateListener(&gPublicListener);
if (!KSUCCESS(status)) RssrvExit(6);
status = RssrvCreateListener(&gAdminListener);
if (!KSUCCESS(status)) RssrvExit(7);
status = RssrvStartListenerWorker(&gPublicWorker, gPublicListener, 0);
if (!KSUCCESS(status)) RssrvExit(8);
status = RssrvStartListenerWorker(&gAdminWorker, gAdminListener, 1);
if (!KSUCCESS(status)) RssrvExit(9);
uint64_t volumeGeneration = 0;
status = RssrvSyncVolumes(volumeManager, &volumeGeneration);
if (!KSUCCESS(status)) RssrvExit(10);
status = RssrvStartVolumeWorker(volumeManager);
if (!KSUCCESS(status)) RssrvExit(11);
status = SysRegisterSystemService(SYS_SERVICE_RESOURCE, gPublicListener);
if (!KSUCCESS(status)) RssrvExit(12);
status = RssrvSendReady(controlEndpoint);
if (!KSUCCESS(status)) RssrvExit(13);
RssrvControlLoop(controlEndpoint);
(void)SysUnregisterSystemService(SYS_SERVICE_RESOURCE, gPublicListener);
(void)SysDisconnectPort(controlEndpoint);
(void)SysCloseHandle(controlEndpoint);
RssrvExit(0);
}