560 lines
22 KiB
C
560 lines
22 KiB
C
// RSSRV native namespace tree, symbolic-link traversal, bindings, and enumeration.
|
|
|
|
#include "rssrv_internal.h"
|
|
#include <stddef.h>
|
|
#include <ntlite/native_heap.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] != '/' ||
|
|
!SysSvIsUtf8Text(SysSvFromParts(path, length)))
|
|
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;
|
|
}
|