Files
nt-lite/code/user/system/rssrv/namespace.c
T
maksw 73abd6f90d 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+.
2026-09-16 10:33:20 +02:00

559 lines
22 KiB
C

// 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;
}