Files

446 lines
21 KiB
C

// Native resource-service client. Resource naming policy stays in rssrv; this file only speaks its Port protocol.
#include <ntlite/resource.h>
#include <ntlite/service.h>
#include <ntlite/syscall.h>
#include <string.h>
// Validates canonical global resource-path syntax. The namespace root is represented by exactly "/".
static KSTATUS NtlValidateResourcePath(SYS_STRING_VIEW path) {
if (!path.Data || path.Length == 0 || path.Length > NTL_RESOURCE_PATH_MAX || path.Data[0] != '/')
return NTLSTATUS_INVALID_PARAMETER;
if (path.Length == 1)
return NTLSTATUS_SUCCESS;
if (path.Data[path.Length - 1] == '/')
return NTLSTATUS_INVALID_PARAMETER;
size_t componentStart = 1;
for (size_t i = 1; i <= path.Length; ++i) {
if (i < path.Length) {
char c = path.Data[i];
if (c == '\\' || c == '\0')
return NTLSTATUS_INVALID_PARAMETER;
if (c != '/')
continue;
}
size_t componentLength = i - componentStart;
if (componentLength == 0 || componentLength > NTL_RESOURCE_NAME_MAX ||
(componentLength == 1 && path.Data[componentStart] == '.') ||
(componentLength == 2 && path.Data[componentStart] == '.' && path.Data[componentStart + 1] == '.'))
return NTLSTATUS_INVALID_PARAMETER;
componentStart = i + 1;
}
return NTLSTATUS_SUCCESS;
}
// Builds one inline request header with a path and optional operation-specific payload.
static KSTATUS NtlBuildResourceRequest(uint8_t* buffer,
size_t bufferSize,
uint32_t operation,
SYS_STRING_VIEW path,
SYS_STRING_VIEW aux,
SYS_RIGHTS rights,
SYS_RIGHTS exportRights,
uint32_t kind,
uint32_t* lengthOut) {
KSTATUS status = NtlValidateResourcePath(path);
if (!KSUCCESS(status) || !buffer || !lengthOut || !SysSvIsValid(aux))
return KSUCCESS(status) ? NTLSTATUS_INVALID_PARAMETER : status;
size_t total = sizeof(NTL_RESOURCE_REQUEST) + path.Length + aux.Length;
if (total > bufferSize || total > SYS_PORT_MAX_INLINE_BYTES || aux.Length > UINT32_MAX)
return NTLSTATUS_BUFFER_TOO_SMALL;
NTL_RESOURCE_REQUEST header;
memset(&header, 0, sizeof(header));
header.Size = sizeof(header);
header.Version = NTL_RESOURCE_PROTOCOL_VERSION;
header.Operation = operation;
header.Flags = NTL_RESOURCE_FLAG_NONE;
header.Rights = rights;
header.ExportRights = exportRights;
header.Kind = kind;
header.PathLength = (uint32_t)path.Length;
header.AuxLength = (uint32_t)aux.Length;
memcpy(buffer, &header, sizeof(header));
memcpy(buffer + sizeof(header), path.Data, path.Length);
if (aux.Length)
memcpy(buffer + sizeof(header) + path.Length, aux.Data, aux.Length);
*lengthOut = (uint32_t)total;
return NTLSTATUS_SUCCESS;
}
// Executes one resource request and validates its fixed protocol reply.
static KSTATUS NtlCallResource(SYS_HANDLE session,
const void* requestPayload,
uint32_t requestLength,
const SYS_PORT_HANDLE_TRANSFER* requestHandles,
uint32_t requestHandleCount,
NTL_RESOURCE_REPLY* replyOut,
SYS_HANDLE* replyHandleOut) {
if (session == SYS_HANDLE_INVALID || !requestPayload || !requestLength || !replyOut)
return NTLSTATUS_INVALID_PARAMETER;
NTL_RESOURCE_REPLY reply;
memset(&reply, 0, sizeof(reply));
SYS_PORT_MESSAGE_INFO info;
memset(&info, 0, sizeof(info));
info.Size = sizeof(info);
SYS_HANDLE replyHandle = SYS_HANDLE_INVALID;
SYS_CALL_PORT_REQUEST call;
memset(&call, 0, sizeof(call));
call.Size = sizeof(call);
call.EndpointHandle = session;
call.Flags = SYS_PORT_FLAG_NONE;
call.RequestPayload = requestPayload;
call.RequestLength = requestLength;
call.RequestHandles = requestHandles;
call.RequestHandleCount = requestHandleCount;
call.ReplyPayload = &reply;
call.ReplyCapacity = sizeof(reply);
call.ReplyHandlesOut = replyHandleOut ? &replyHandle : (SYS_HANDLE*)0;
call.ReplyHandleCapacity = replyHandleOut ? 1u : 0u;
call.ReplyInfoOut = &info;
call.TimeoutMilliseconds = SYS_PORT_TIMEOUT_INFINITE;
KSTATUS status = SysCallPort(&call);
if (!KSUCCESS(status))
return status;
if (info.Type != SYS_PORT_MESSAGE_REPLY || info.PayloadLength != sizeof(reply) ||
reply.Size != sizeof(reply) || reply.Version != NTL_RESOURCE_PROTOCOL_VERSION || reply.NameLength != 0) {
if (replyHandle != SYS_HANDLE_INVALID) (void)SysCloseHandle(replyHandle);
return NTLSTATUS_FAILURE;
}
if ((!replyHandleOut && info.HandleCount != 0) || (replyHandleOut && info.HandleCount > 1)) {
if (replyHandle != SYS_HANDLE_INVALID) (void)SysCloseHandle(replyHandle);
return NTLSTATUS_FAILURE;
}
*replyOut = reply;
if (replyHandleOut) *replyHandleOut = replyHandle;
return NTLSTATUS_SUCCESS;
}
// Connects to the registered resource listener.
KSTATUS NtlResourceConnect(SYS_HANDLE* sessionOut) {
if (!sessionOut) return NTLSTATUS_INVALID_PARAMETER;
*sessionOut = SYS_HANDLE_INVALID;
SYS_CONNECT_SYSTEM_SERVICE_REQUEST request;
memset(&request, 0, sizeof(request));
request.Size = sizeof(request);
request.ServiceId = SYS_SERVICE_RESOURCE;
request.EndpointHandleOut = sessionOut;
return SysConnectSystemService(&request);
}
// Resolves the canonical /SystemRoot resource and closes the transient resource session.
KSTATUS NtlResourceOpenSystemRoot(SYS_RIGHTS desiredRights, SYS_HANDLE* handleOut) {
if (!desiredRights || !handleOut) return NTLSTATUS_INVALID_PARAMETER;
*handleOut = SYS_HANDLE_INVALID;
SYS_HANDLE session = SYS_HANDLE_INVALID;
KSTATUS status = NtlResourceConnect(&session);
if (!KSUCCESS(status)) return status;
status = NtlResourceLookup(session, SysSvFromCstr("/SystemRoot"), desiredRights, handleOut);
(void)NtlResourceClose(session);
return status;
}
// Connects through one delegated resource-listener capability.
KSTATUS NtlResourceConnectListener(SYS_HANDLE listenerHandle, SYS_HANDLE* sessionOut) {
if (listenerHandle == SYS_HANDLE_INVALID || !sessionOut)
return NTLSTATUS_INVALID_PARAMETER;
*sessionOut = SYS_HANDLE_INVALID;
SYS_CONNECT_PORT_REQUEST request;
memset(&request, 0, sizeof(request));
request.Size = sizeof(request);
request.ListenerHandle = listenerHandle;
request.Flags = SYS_PORT_CONNECT_FLAG_NONE;
request.EndpointHandleOut = sessionOut;
return SysConnectPort(&request);
}
// Delegates the authority to bind or unbind exactly one resource path.
KSTATUS NtlResourceCreatePublisher(SYS_HANDLE adminSession, SYS_STRING_VIEW path,
SYS_HANDLE* listenerOut) {
if (!listenerOut) return NTLSTATUS_INVALID_PARAMETER;
*listenerOut = SYS_HANDLE_INVALID;
uint8_t payload[sizeof(NTL_RESOURCE_REQUEST) + NTL_RESOURCE_PATH_MAX];
uint32_t length = 0;
KSTATUS status = NtlBuildResourceRequest(payload, sizeof(payload),
NTL_RESOURCE_OP_CREATE_PUBLISHER, path,
SysSvFromParts((const char*)0, 0), 0, 0,
NTL_RESOURCE_KIND_INVALID, &length);
if (!KSUCCESS(status)) return status;
NTL_RESOURCE_REPLY reply;
SYS_HANDLE handle = SYS_HANDLE_INVALID;
status = NtlCallResource(adminSession, payload, length, (const SYS_PORT_HANDLE_TRANSFER*)0,
0, &reply, &handle);
if (!KSUCCESS(status)) return status;
if (reply.Operation != NTL_RESOURCE_OP_CREATE_PUBLISHER ||
!KSUCCESS(reply.Status) || handle == SYS_HANDLE_INVALID) {
if (handle != SYS_HANDLE_INVALID) (void)SysCloseHandle(handle);
return reply.Operation == NTL_RESOURCE_OP_CREATE_PUBLISHER && !KSUCCESS(reply.Status)
? reply.Status : NTLSTATUS_FAILURE;
}
*listenerOut = handle;
return NTLSTATUS_SUCCESS;
}
// Looks up one resource and requests an attenuated alias of its bound capability.
KSTATUS NtlResourceLookup(SYS_HANDLE session,
SYS_STRING_VIEW path,
SYS_RIGHTS desiredRights,
SYS_HANDLE* handleOut) {
if (!desiredRights || !handleOut) return NTLSTATUS_INVALID_PARAMETER;
*handleOut = SYS_HANDLE_INVALID;
uint8_t payload[sizeof(NTL_RESOURCE_REQUEST) + NTL_RESOURCE_PATH_MAX];
uint32_t payloadLength = 0;
KSTATUS status = NtlBuildResourceRequest(payload, sizeof(payload), NTL_RESOURCE_OP_LOOKUP,
path, SysSvFromParts((const char*)0, 0),
desiredRights, 0, NTL_RESOURCE_KIND_INVALID,
&payloadLength);
if (!KSUCCESS(status)) return status;
NTL_RESOURCE_REPLY reply;
SYS_HANDLE handle = SYS_HANDLE_INVALID;
status = NtlCallResource(session, payload, payloadLength, (const SYS_PORT_HANDLE_TRANSFER*)0, 0,
&reply, &handle);
if (!KSUCCESS(status)) return status;
if (reply.Operation != NTL_RESOURCE_OP_LOOKUP) {
if (handle != SYS_HANDLE_INVALID) (void)SysCloseHandle(handle);
return NTLSTATUS_FAILURE;
}
if (!KSUCCESS(reply.Status)) {
if (handle != SYS_HANDLE_INVALID) (void)SysCloseHandle(handle);
return reply.Status;
}
if (handle == SYS_HANDLE_INVALID) return NTLSTATUS_FAILURE;
*handleOut = handle;
return NTLSTATUS_SUCCESS;
}
// Queries one exact namespace node kind without transferring authority.
KSTATUS NtlResourceQuery(SYS_HANDLE session, SYS_STRING_VIEW path, uint32_t* kindOut) {
if (!kindOut) return NTLSTATUS_INVALID_PARAMETER;
*kindOut = NTL_RESOURCE_KIND_INVALID;
uint8_t payload[sizeof(NTL_RESOURCE_REQUEST) + NTL_RESOURCE_PATH_MAX];
uint32_t payloadLength = 0;
KSTATUS status = NtlBuildResourceRequest(payload, sizeof(payload), NTL_RESOURCE_OP_QUERY,
path, SysSvFromParts((const char*)0, 0),
0, 0, NTL_RESOURCE_KIND_INVALID, &payloadLength);
if (!KSUCCESS(status)) return status;
NTL_RESOURCE_REPLY reply;
status = NtlCallResource(session, payload, payloadLength, (const SYS_PORT_HANDLE_TRANSFER*)0, 0,
&reply, (SYS_HANDLE*)0);
if (!KSUCCESS(status)) return status;
if (reply.Operation != NTL_RESOURCE_OP_QUERY) return NTLSTATUS_FAILURE;
if (!KSUCCESS(reply.Status)) return reply.Status;
*kindOut = reply.Kind;
return NTLSTATUS_SUCCESS;
}
// Resolves the longest bound resource prefix into one traversal capability.
KSTATUS NtlResourceResolve(SYS_HANDLE session,
SYS_STRING_VIEW path,
uint32_t flags,
SYS_HANDLE* rootHandleOut,
uint32_t* kindOut,
uint32_t* resolvedLengthOut) {
if (!rootHandleOut || !kindOut || !resolvedLengthOut || path.Length < 2 ||
(flags & ~NTL_RESOURCE_RESOLVE_FLAG_MODIFY) != 0)
return NTLSTATUS_INVALID_PARAMETER;
*rootHandleOut = SYS_HANDLE_INVALID;
*kindOut = NTL_RESOURCE_KIND_INVALID;
*resolvedLengthOut = 0;
uint8_t payload[sizeof(NTL_RESOURCE_REQUEST) + NTL_RESOURCE_PATH_MAX];
uint32_t payloadLength = 0;
KSTATUS status = NtlBuildResourceRequest(payload, sizeof(payload), NTL_RESOURCE_OP_RESOLVE,
path, SysSvFromParts((const char*)0, 0),
0, 0, NTL_RESOURCE_KIND_INVALID, &payloadLength);
if (!KSUCCESS(status)) return status;
((NTL_RESOURCE_REQUEST*)payload)->Flags = flags;
NTL_RESOURCE_REPLY reply;
SYS_HANDLE handle = SYS_HANDLE_INVALID;
status = NtlCallResource(session, payload, payloadLength, (const SYS_PORT_HANDLE_TRANSFER*)0, 0,
&reply, &handle);
if (!KSUCCESS(status)) return status;
if (reply.Operation != NTL_RESOURCE_OP_RESOLVE || reply.Flags != flags) {
if (handle != SYS_HANDLE_INVALID) (void)SysCloseHandle(handle);
return NTLSTATUS_FAILURE;
}
if (!KSUCCESS(reply.Status)) {
if (handle != SYS_HANDLE_INVALID) (void)SysCloseHandle(handle);
return reply.Status;
}
if (handle == SYS_HANDLE_INVALID || reply.Kind == NTL_RESOURCE_KIND_INVALID ||
reply.ResolvedLength < 2 || reply.ResolvedLength > path.Length ||
(reply.ResolvedLength != path.Length && path.Data[reply.ResolvedLength] != '/')) {
if (handle != SYS_HANDLE_INVALID) (void)SysCloseHandle(handle);
return NTLSTATUS_FAILURE;
}
*rootHandleOut = handle;
*kindOut = reply.Kind;
*resolvedLengthOut = reply.ResolvedLength;
return NTLSTATUS_SUCCESS;
}
// Enumerates one immediate namespace child after the cursor's last returned bytewise name.
KSTATUS NtlResourceEnumerate(SYS_HANDLE session,
SYS_STRING_VIEW directoryPath,
NTL_RESOURCE_ENUM_CURSOR* cursor,
NTL_RESOURCE_ENUM_ENTRY* entryOut) {
if (session == SYS_HANDLE_INVALID || !cursor || !entryOut ||
cursor->NameLength > NTL_RESOURCE_NAME_MAX || cursor->Name[cursor->NameLength] != '\0')
return NTLSTATUS_INVALID_PARAMETER;
memset(entryOut, 0, sizeof(*entryOut));
SYS_STRING_VIEW after = SysSvFromParts(cursor->Name, cursor->NameLength);
uint8_t requestPayload[sizeof(NTL_RESOURCE_REQUEST) + NTL_RESOURCE_PATH_MAX + NTL_RESOURCE_NAME_MAX];
uint32_t requestLength = 0;
KSTATUS status = NtlBuildResourceRequest(requestPayload, sizeof(requestPayload), NTL_RESOURCE_OP_ENUMERATE,
directoryPath, after, 0, 0, NTL_RESOURCE_KIND_INVALID,
&requestLength);
if (!KSUCCESS(status)) return status;
uint8_t replyPayload[sizeof(NTL_RESOURCE_REPLY) + NTL_RESOURCE_NAME_MAX];
memset(replyPayload, 0, sizeof(replyPayload));
SYS_PORT_MESSAGE_INFO info;
memset(&info, 0, sizeof(info));
info.Size = sizeof(info);
SYS_CALL_PORT_REQUEST call;
memset(&call, 0, sizeof(call));
call.Size = sizeof(call);
call.EndpointHandle = session;
call.Flags = SYS_PORT_FLAG_NONE;
call.RequestPayload = requestPayload;
call.RequestLength = requestLength;
call.ReplyPayload = replyPayload;
call.ReplyCapacity = sizeof(replyPayload);
call.ReplyInfoOut = &info;
call.TimeoutMilliseconds = SYS_PORT_TIMEOUT_INFINITE;
status = SysCallPort(&call);
if (!KSUCCESS(status)) return status;
if (info.Type != SYS_PORT_MESSAGE_REPLY || info.HandleCount != 0 || info.PayloadLength < sizeof(NTL_RESOURCE_REPLY))
return NTLSTATUS_FAILURE;
NTL_RESOURCE_REPLY reply;
memcpy(&reply, replyPayload, sizeof(reply));
if (reply.Size != sizeof(reply) || reply.Version != NTL_RESOURCE_PROTOCOL_VERSION ||
reply.Operation != NTL_RESOURCE_OP_ENUMERATE || reply.Flags != NTL_RESOURCE_FLAG_NONE ||
reply.ResolvedLength != 0 || reply.NameLength > NTL_RESOURCE_NAME_MAX ||
info.PayloadLength != sizeof(reply) + reply.NameLength)
return NTLSTATUS_FAILURE;
if (!KSUCCESS(reply.Status)) {
if (reply.NameLength != 0)
return NTLSTATUS_FAILURE;
return reply.Status;
}
if (reply.NameLength == 0 || reply.Kind == NTL_RESOURCE_KIND_INVALID)
return NTLSTATUS_FAILURE;
memcpy(entryOut->Name, replyPayload + sizeof(reply), reply.NameLength);
entryOut->Name[reply.NameLength] = '\0';
entryOut->Kind = reply.Kind;
memcpy(cursor->Name, entryOut->Name, reply.NameLength + 1u);
cursor->NameLength = reply.NameLength;
return NTLSTATUS_SUCCESS;
}
// Creates one namespace directory through an administrative resource session.
KSTATUS NtlResourceCreateDirectory(SYS_HANDLE session, SYS_STRING_VIEW path) {
uint8_t payload[sizeof(NTL_RESOURCE_REQUEST) + NTL_RESOURCE_PATH_MAX];
uint32_t payloadLength = 0;
KSTATUS status = NtlBuildResourceRequest(payload, sizeof(payload), NTL_RESOURCE_OP_CREATE_DIRECTORY,
path, SysSvFromParts((const char*)0, 0),
0, 0, NTL_RESOURCE_KIND_INVALID, &payloadLength);
if (!KSUCCESS(status)) return status;
NTL_RESOURCE_REPLY reply;
status = NtlCallResource(session, payload, payloadLength, (const SYS_PORT_HANDLE_TRANSFER*)0, 0,
&reply, (SYS_HANDLE*)0);
if (!KSUCCESS(status)) return status;
if (reply.Operation != NTL_RESOURCE_OP_CREATE_DIRECTORY) return NTLSTATUS_FAILURE;
return reply.Status;
}
// Creates one absolute namespace symbolic link through an administrative resource session.
KSTATUS NtlResourceCreateSymbolicLink(SYS_HANDLE session, SYS_STRING_VIEW path, SYS_STRING_VIEW target) {
KSTATUS status = NtlValidateResourcePath(target);
if (!KSUCCESS(status)) return status;
uint8_t payload[sizeof(NTL_RESOURCE_REQUEST) + NTL_RESOURCE_PATH_MAX * 2u];
uint32_t payloadLength = 0;
status = NtlBuildResourceRequest(payload, sizeof(payload), NTL_RESOURCE_OP_CREATE_SYMBOLIC_LINK,
path, target, 0, 0, NTL_RESOURCE_KIND_INVALID, &payloadLength);
if (!KSUCCESS(status)) return status;
NTL_RESOURCE_REPLY reply;
status = NtlCallResource(session, payload, payloadLength, (const SYS_PORT_HANDLE_TRANSFER*)0, 0,
&reply, (SYS_HANDLE*)0);
if (!KSUCCESS(status)) return status;
if (reply.Operation != NTL_RESOURCE_OP_CREATE_SYMBOLIC_LINK) return NTLSTATUS_FAILURE;
return reply.Status;
}
// Binds an explicitly attenuated capability into an administrative resource session.
KSTATUS NtlResourceBind(SYS_HANDLE session,
SYS_STRING_VIEW path,
uint32_t kind,
SYS_HANDLE resourceHandle,
SYS_RIGHTS namespaceRights,
SYS_RIGHTS exportRights) {
if (kind == NTL_RESOURCE_KIND_INVALID || kind == NTL_RESOURCE_KIND_NAMESPACE_DIRECTORY ||
kind == NTL_RESOURCE_KIND_SYMBOLIC_LINK || resourceHandle == SYS_HANDLE_INVALID ||
!(namespaceRights & SYS_RIGHT_TRANSFER) || !exportRights ||
(exportRights & ~namespaceRights) != 0)
return NTLSTATUS_INVALID_PARAMETER;
uint8_t payload[sizeof(NTL_RESOURCE_REQUEST) + NTL_RESOURCE_PATH_MAX];
uint32_t payloadLength = 0;
KSTATUS status = NtlBuildResourceRequest(payload, sizeof(payload), NTL_RESOURCE_OP_BIND,
path, SysSvFromParts((const char*)0, 0),
namespaceRights, exportRights, kind, &payloadLength);
if (!KSUCCESS(status)) return status;
SYS_PORT_HANDLE_TRANSFER transfer;
transfer.Handle = resourceHandle;
transfer.DesiredRights = namespaceRights;
transfer.Flags = SYS_PORT_TRANSFER_FLAG_NONE;
NTL_RESOURCE_REPLY reply;
status = NtlCallResource(session, payload, payloadLength, &transfer, 1, &reply, (SYS_HANDLE*)0);
if (!KSUCCESS(status)) return status;
if (reply.Operation != NTL_RESOURCE_OP_BIND) return NTLSTATUS_FAILURE;
return reply.Status;
}
// Removes one non-root namespace node through an administrative session.
KSTATUS NtlResourceUnbind(SYS_HANDLE session, SYS_STRING_VIEW path) {
uint8_t payload[sizeof(NTL_RESOURCE_REQUEST) + NTL_RESOURCE_PATH_MAX];
uint32_t payloadLength = 0;
KSTATUS status = NtlBuildResourceRequest(payload, sizeof(payload), NTL_RESOURCE_OP_UNBIND,
path, SysSvFromParts((const char*)0, 0),
0, 0, NTL_RESOURCE_KIND_INVALID, &payloadLength);
if (!KSUCCESS(status)) return status;
NTL_RESOURCE_REPLY reply;
status = NtlCallResource(session, payload, payloadLength, (const SYS_PORT_HANDLE_TRANSFER*)0, 0,
&reply, (SYS_HANDLE*)0);
if (!KSUCCESS(status)) return status;
if (reply.Operation != NTL_RESOURCE_OP_UNBIND) return NTLSTATUS_FAILURE;
return reply.Status;
}
// Disconnects and closes one resource session.
KSTATUS NtlResourceClose(SYS_HANDLE session) {
if (session == SYS_HANDLE_INVALID) return NTLSTATUS_INVALID_HANDLE;
KSTATUS disconnect = SysDisconnectPort(session);
KSTATUS close = SysCloseHandle(session);
if (!KSUCCESS(disconnect) && disconnect != NTLSTATUS_PORT_DISCONNECTED)
return disconnect;
return close;
}