502 lines
20 KiB
C
502 lines
20 KiB
C
// Native userspace path policy. RSSRV resolves global resource prefixes; kernel I/O sees only capabilities and relative paths.
|
|
|
|
#include <ntlite/atomic.h>
|
|
#include <intrin.h>
|
|
#include <ntlite/path.h>
|
|
#include <ntlite/resource.h>
|
|
#include <string.h>
|
|
#include "module_loader_internal.h"
|
|
|
|
#define NTL_PATH_COMPONENT_MAX 255u
|
|
|
|
typedef struct NTL_RESOLVED_ROOT {
|
|
SYS_HANDLE Handle;
|
|
SYS_STRING_VIEW RelativePath;
|
|
uint32_t Kind;
|
|
size_t ResolvedLength;
|
|
} NTL_RESOLVED_ROOT;
|
|
|
|
static volatile int32_t gPathState;
|
|
static volatile int32_t gPathLock;
|
|
static char gCurrentDirectory[SYS_PATH_MAX + 1u];
|
|
static size_t gCurrentDirectoryLength;
|
|
static size_t gCurrentDirectoryRootLength;
|
|
|
|
// Acquires the tiny userspace CWD lock without introducing a kernel object.
|
|
static void NtlpLockPathState(void) {
|
|
while (NtlAtomicCompareExchange32(&gPathLock, 1, 0) != 0)
|
|
(void)SysYield();
|
|
}
|
|
|
|
// Releases the userspace CWD lock.
|
|
static void NtlpUnlockPathState(void) {
|
|
NtlAtomicExchange32(&gPathLock, 0);
|
|
}
|
|
|
|
// Initializes the process-local CWD from opaque loader data, or /SystemRoot for bootstrap init.
|
|
static KSTATUS NtlpEnsurePathState(void) {
|
|
if (NtlAtomicCompareExchange32(&gPathState, 2, 2) == 2)
|
|
return NTLSTATUS_SUCCESS;
|
|
|
|
if (NtlAtomicCompareExchange32(&gPathState, 1, 0) == 0) {
|
|
SYS_STRING_VIEW inherited;
|
|
KSTATUS status = NtlpGetLoaderCurrentDirectory(&inherited);
|
|
if (!KSUCCESS(status))
|
|
inherited = SYS_SV_LITERAL("/SystemRoot");
|
|
if (!SysSvIsValid(inherited) || inherited.Length == 0 || inherited.Length > SYS_PATH_MAX ||
|
|
inherited.Data[0] != '/' || SysSvContainsByte(inherited, '\\') || SysSvContainsByte(inherited, '\0')) {
|
|
NtlAtomicExchange32(&gPathState, 0);
|
|
return NTLSTATUS_FAILURE;
|
|
}
|
|
memcpy(gCurrentDirectory, inherited.Data, inherited.Length);
|
|
gCurrentDirectory[inherited.Length] = '\0';
|
|
gCurrentDirectoryLength = inherited.Length;
|
|
gCurrentDirectoryRootLength = 0;
|
|
NtlAtomicExchange32(&gPathState, 2);
|
|
return NTLSTATUS_SUCCESS;
|
|
}
|
|
|
|
while (NtlAtomicCompareExchange32(&gPathState, 2, 2) == 1)
|
|
(void)SysYield();
|
|
return NtlAtomicCompareExchange32(&gPathState, 2, 2) == 2
|
|
? NTLSTATUS_SUCCESS : NTLSTATUS_FAILURE;
|
|
}
|
|
|
|
// Appends one normalized path component while enforcing the owning resource boundary.
|
|
static KSTATUS NtlpAppendComponent(char* output,
|
|
size_t outputSize,
|
|
size_t* lengthInOut,
|
|
size_t componentStart,
|
|
const char* component,
|
|
size_t componentLength,
|
|
uint32_t clampAtBoundary) {
|
|
if (!output || !lengthInOut || !component || componentLength == 0 ||
|
|
componentLength > NTL_PATH_COMPONENT_MAX)
|
|
return NTLSTATUS_INVALID_PARAMETER;
|
|
|
|
size_t length = *lengthInOut;
|
|
if (componentLength == 1 && component[0] == '.')
|
|
return NTLSTATUS_SUCCESS;
|
|
if (componentLength == 2 && component[0] == '.' && component[1] == '.') {
|
|
if (length <= componentStart)
|
|
return clampAtBoundary ? NTLSTATUS_SUCCESS : NTLSTATUS_ACCESS_DENIED;
|
|
size_t slash = length;
|
|
while (slash > componentStart && output[slash - 1u] != '/') slash--;
|
|
if (slash <= componentStart)
|
|
return clampAtBoundary ? NTLSTATUS_SUCCESS : NTLSTATUS_ACCESS_DENIED;
|
|
length = slash - 1u;
|
|
*lengthInOut = length;
|
|
output[length] = '\0';
|
|
return NTLSTATUS_SUCCESS;
|
|
}
|
|
|
|
if (length && output[length - 1u] != '/') {
|
|
if (length + 1u >= outputSize) return NTLSTATUS_BUFFER_TOO_SMALL;
|
|
output[length++] = '/';
|
|
}
|
|
if (componentLength > outputSize - length - 1u)
|
|
return NTLSTATUS_BUFFER_TOO_SMALL;
|
|
memcpy(output + length, component, componentLength);
|
|
length += componentLength;
|
|
output[length] = '\0';
|
|
*lengthInOut = length;
|
|
return NTLSTATUS_SUCCESS;
|
|
}
|
|
|
|
// Returns whether one relative path contains a parent-traversal component.
|
|
static uint32_t NtlpContainsParentComponent(SYS_STRING_VIEW path) {
|
|
size_t componentStart = 0;
|
|
for (size_t i = 0; i <= path.Length; ++i) {
|
|
if (i != path.Length && path.Data[i] != '/')
|
|
continue;
|
|
size_t componentLength = i - componentStart;
|
|
if (componentLength == 2 && path.Data[componentStart] == '.' &&
|
|
path.Data[componentStart + 1u] == '.')
|
|
return 1;
|
|
componentStart = i + 1u;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// Builds the canonical absolute prefix preceding the first parent component.
|
|
static KSTATUS NtlpBuildAbsoluteParentPrefix(SYS_STRING_VIEW path,
|
|
char output[SYS_PATH_MAX + 1u],
|
|
size_t* lengthOut) {
|
|
if (!output || !lengthOut || !SysSvIsValid(path) || path.Length == 0 || path.Data[0] != '/')
|
|
return NTLSTATUS_INVALID_PARAMETER;
|
|
|
|
size_t length = 1u;
|
|
output[0] = '/';
|
|
output[1] = '\0';
|
|
|
|
size_t componentStart = 1u;
|
|
for (size_t i = 1u; i <= path.Length; ++i) {
|
|
if (i != path.Length && path.Data[i] != '/')
|
|
continue;
|
|
size_t componentLength = i - componentStart;
|
|
if (componentLength == 1u && path.Data[componentStart] == '.') {
|
|
componentStart = i + 1u;
|
|
continue;
|
|
}
|
|
if (componentLength == 2u && path.Data[componentStart] == '.' &&
|
|
path.Data[componentStart + 1u] == '.') {
|
|
if (length <= 1u) return NTLSTATUS_ACCESS_DENIED;
|
|
*lengthOut = length;
|
|
return NTLSTATUS_SUCCESS;
|
|
}
|
|
if (componentLength != 0) {
|
|
KSTATUS status = NtlpAppendComponent(output, SYS_PATH_MAX + 1u, &length, 1u,
|
|
path.Data + componentStart, componentLength, 0);
|
|
if (!KSUCCESS(status)) return status;
|
|
}
|
|
componentStart = i + 1u;
|
|
}
|
|
|
|
return NTLSTATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
// Resolves one canonical path solely to discover the owning RSSRV resource-prefix boundary.
|
|
static KSTATUS NtlpResolveResourceBoundary(const char* canonical,
|
|
size_t canonicalLength,
|
|
size_t* boundaryOut) {
|
|
if (!canonical || canonicalLength < 2u || !boundaryOut)
|
|
return NTLSTATUS_INVALID_PARAMETER;
|
|
*boundaryOut = 0;
|
|
|
|
SYS_HANDLE session = SYS_HANDLE_INVALID;
|
|
KSTATUS status = NtlResourceConnect(&session);
|
|
if (!KSUCCESS(status)) return status;
|
|
|
|
SYS_HANDLE root = SYS_HANDLE_INVALID;
|
|
uint32_t kind = NTL_RESOURCE_KIND_INVALID;
|
|
uint32_t resolvedLength = 0;
|
|
status = NtlResourceResolve(session, SysSvFromParts(canonical, canonicalLength),
|
|
NTL_RESOURCE_FLAG_NONE, &root, &kind, &resolvedLength);
|
|
(void)NtlResourceClose(session);
|
|
if (root != SYS_HANDLE_INVALID) (void)SysCloseHandle(root);
|
|
if (!KSUCCESS(status)) return status;
|
|
if (kind == NTL_RESOURCE_KIND_INVALID || resolvedLength < 2u || resolvedLength > canonicalLength ||
|
|
(resolvedLength != canonicalLength && canonical[resolvedLength] != '/'))
|
|
return NTLSTATUS_FAILURE;
|
|
|
|
*boundaryOut = resolvedLength;
|
|
return NTLSTATUS_SUCCESS;
|
|
}
|
|
|
|
// Normalizes one absolute or CWD-relative native path into a canonical resource path.
|
|
static KSTATUS NtlpNormalizePath(SYS_STRING_VIEW path, char output[SYS_PATH_MAX + 1u], size_t* lengthOut) {
|
|
if (!output || !lengthOut || !SysSvIsValid(path) || SysSvContainsByte(path, '\0') ||
|
|
SysSvContainsByte(path, '\\'))
|
|
return NTLSTATUS_INVALID_PARAMETER;
|
|
KSTATUS status = NtlpEnsurePathState();
|
|
if (!KSUCCESS(status)) return status;
|
|
|
|
size_t outputLength = 0;
|
|
size_t rootBoundary = 1u;
|
|
size_t sourceOffset = 0;
|
|
uint32_t absolutePath = path.Length && path.Data[0] == '/';
|
|
if (absolutePath) {
|
|
output[0] = '/';
|
|
output[1] = '\0';
|
|
outputLength = 1u;
|
|
sourceOffset = 1u;
|
|
|
|
if (NtlpContainsParentComponent(path)) {
|
|
char prefix[SYS_PATH_MAX + 1u];
|
|
size_t prefixLength = 0;
|
|
status = NtlpBuildAbsoluteParentPrefix(path, prefix, &prefixLength);
|
|
if (!KSUCCESS(status)) return status;
|
|
status = NtlpResolveResourceBoundary(prefix, prefixLength, &rootBoundary);
|
|
if (!KSUCCESS(status)) return status;
|
|
}
|
|
} else {
|
|
NtlpLockPathState();
|
|
if (gCurrentDirectoryLength > SYS_PATH_MAX ||
|
|
gCurrentDirectoryRootLength > gCurrentDirectoryLength) {
|
|
NtlpUnlockPathState();
|
|
return NTLSTATUS_FAILURE;
|
|
}
|
|
memcpy(output, gCurrentDirectory, gCurrentDirectoryLength + 1u);
|
|
outputLength = gCurrentDirectoryLength;
|
|
rootBoundary = gCurrentDirectoryRootLength;
|
|
NtlpUnlockPathState();
|
|
sourceOffset = 0;
|
|
|
|
if (rootBoundary == 0 && NtlpContainsParentComponent(path)) {
|
|
status = NtlpResolveResourceBoundary(output, outputLength, &rootBoundary);
|
|
if (!KSUCCESS(status)) return status;
|
|
|
|
NtlpLockPathState();
|
|
if (gCurrentDirectoryLength == outputLength &&
|
|
memcmp(gCurrentDirectory, output, outputLength + 1u) == 0)
|
|
gCurrentDirectoryRootLength = rootBoundary;
|
|
NtlpUnlockPathState();
|
|
}
|
|
}
|
|
|
|
size_t componentStart = sourceOffset;
|
|
for (size_t i = sourceOffset; i <= path.Length; ++i) {
|
|
if (i != path.Length && path.Data[i] != '/')
|
|
continue;
|
|
size_t componentLength = i - componentStart;
|
|
if (componentLength != 0) {
|
|
status = NtlpAppendComponent(output, SYS_PATH_MAX + 1u, &outputLength,
|
|
rootBoundary, path.Data + componentStart, componentLength,
|
|
absolutePath ? 0u : 1u);
|
|
if (!KSUCCESS(status)) return status;
|
|
}
|
|
componentStart = i + 1u;
|
|
}
|
|
|
|
while (outputLength > 1u && output[outputLength - 1u] == '/') outputLength--;
|
|
output[outputLength] = '\0';
|
|
if (outputLength < 2u || output[0] != '/') return NTLSTATUS_INVALID_PARAMETER;
|
|
*lengthOut = outputLength;
|
|
return NTLSTATUS_SUCCESS;
|
|
}
|
|
|
|
// Resolves a canonical resource path to one volume/directory capability and a relative tail.
|
|
static KSTATUS NtlpResolveRoot(const char* canonical,
|
|
size_t canonicalLength,
|
|
uint32_t modify,
|
|
NTL_RESOLVED_ROOT* rootOut) {
|
|
if (!canonical || canonicalLength < 2u || !rootOut)
|
|
return NTLSTATUS_INVALID_PARAMETER;
|
|
memset(rootOut, 0, sizeof(*rootOut));
|
|
|
|
SYS_HANDLE session = SYS_HANDLE_INVALID;
|
|
KSTATUS status = NtlResourceConnect(&session);
|
|
if (!KSUCCESS(status)) return status;
|
|
|
|
SYS_HANDLE root = SYS_HANDLE_INVALID;
|
|
uint32_t kind = NTL_RESOURCE_KIND_INVALID;
|
|
uint32_t resolvedLength = 0;
|
|
status = NtlResourceResolve(session,
|
|
SysSvFromParts(canonical, canonicalLength),
|
|
modify ? NTL_RESOURCE_RESOLVE_FLAG_MODIFY : NTL_RESOURCE_FLAG_NONE,
|
|
&root, &kind, &resolvedLength);
|
|
(void)NtlResourceClose(session);
|
|
if (!KSUCCESS(status)) return status;
|
|
|
|
size_t relativeOffset = resolvedLength;
|
|
if (relativeOffset < canonicalLength) relativeOffset++;
|
|
if (relativeOffset > canonicalLength) {
|
|
(void)SysCloseHandle(root);
|
|
return NTLSTATUS_FAILURE;
|
|
}
|
|
rootOut->Handle = root;
|
|
rootOut->Kind = kind;
|
|
rootOut->ResolvedLength = resolvedLength;
|
|
rootOut->RelativePath = SysSvFromParts(canonical + relativeOffset, canonicalLength - relativeOffset);
|
|
return NTLSTATUS_SUCCESS;
|
|
}
|
|
|
|
// Returns one canonical absolute resource path.
|
|
KSTATUS NtlRealPath(SYS_STRING_VIEW path,
|
|
char* buffer,
|
|
size_t bufferSize,
|
|
size_t* requiredSizeOut) {
|
|
if (requiredSizeOut) *requiredSizeOut = 0;
|
|
if (!buffer && bufferSize) return NTLSTATUS_INVALID_PARAMETER;
|
|
char canonical[SYS_PATH_MAX + 1u];
|
|
size_t length = 0;
|
|
KSTATUS status = NtlpNormalizePath(path, canonical, &length);
|
|
if (!KSUCCESS(status)) return status;
|
|
|
|
size_t required = length + 1u;
|
|
if (requiredSizeOut) *requiredSizeOut = required;
|
|
if (!buffer) return NTLSTATUS_SUCCESS;
|
|
if (bufferSize < required) return NTLSTATUS_BUFFER_TOO_SMALL;
|
|
memcpy(buffer, canonical, required);
|
|
return NTLSTATUS_SUCCESS;
|
|
}
|
|
|
|
// Returns the process-local userspace current directory.
|
|
KSTATUS NtlGetCurrentDirectory(char* buffer, size_t bufferSize, size_t* requiredSizeOut) {
|
|
if (requiredSizeOut) *requiredSizeOut = 0;
|
|
if (!buffer && bufferSize) return NTLSTATUS_INVALID_PARAMETER;
|
|
KSTATUS status = NtlpEnsurePathState();
|
|
if (!KSUCCESS(status)) return status;
|
|
|
|
NtlpLockPathState();
|
|
size_t required = gCurrentDirectoryLength + 1u;
|
|
if (requiredSizeOut) *requiredSizeOut = required;
|
|
if (buffer && bufferSize >= required)
|
|
memcpy(buffer, gCurrentDirectory, required);
|
|
NtlpUnlockPathState();
|
|
if (!buffer) return NTLSTATUS_SUCCESS;
|
|
return bufferSize >= required ? NTLSTATUS_SUCCESS : NTLSTATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
// Validates and installs one process-local userspace current directory.
|
|
KSTATUS NtlSetCurrentDirectory(SYS_STRING_VIEW path) {
|
|
char canonical[SYS_PATH_MAX + 1u];
|
|
size_t length = 0;
|
|
KSTATUS status = NtlpNormalizePath(path, canonical, &length);
|
|
if (!KSUCCESS(status)) return status;
|
|
|
|
NTL_RESOLVED_ROOT root;
|
|
status = NtlpResolveRoot(canonical, length, 0, &root);
|
|
if (!KSUCCESS(status)) return status;
|
|
|
|
SYS_HANDLE directory = SYS_HANDLE_INVALID;
|
|
SYS_OPEN_DIR_AT_REQUEST open = SysMakeOpenDirAtRequest(root.Handle, root.RelativePath,
|
|
SYS_DIR_ACCESS_ENUMERATE | SYS_DIR_ACCESS_OPEN,
|
|
SYS_DIR_FLAG_NONE, &directory);
|
|
status = SysOpenDirAt(&open);
|
|
(void)SysCloseHandle(root.Handle);
|
|
if (!KSUCCESS(status)) return status;
|
|
(void)SysCloseHandle(directory);
|
|
|
|
NtlpLockPathState();
|
|
memcpy(gCurrentDirectory, canonical, length + 1u);
|
|
gCurrentDirectoryLength = length;
|
|
gCurrentDirectoryRootLength = root.ResolvedLength;
|
|
NtlpUnlockPathState();
|
|
return NTLSTATUS_SUCCESS;
|
|
}
|
|
|
|
// Opens or creates one file through RSSRV resource resolution and capability-relative kernel I/O.
|
|
KSTATUS NtlOpenFile(SYS_STRING_VIEW path,
|
|
uint32_t access,
|
|
uint32_t disposition,
|
|
uint32_t flags,
|
|
SYS_HANDLE* handleOut) {
|
|
if (!handleOut) return NTLSTATUS_INVALID_PARAMETER;
|
|
*handleOut = SYS_HANDLE_INVALID;
|
|
char canonical[SYS_PATH_MAX + 1u];
|
|
size_t length = 0;
|
|
KSTATUS status = NtlpNormalizePath(path, canonical, &length);
|
|
if (!KSUCCESS(status)) return status;
|
|
|
|
uint32_t modify = ((access & (SYS_FILE_ACCESS_WRITE | SYS_FILE_ACCESS_APPEND | SYS_FILE_ACCESS_LOCK)) != 0 ||
|
|
disposition != SYS_FILE_OPEN_EXISTING);
|
|
NTL_RESOLVED_ROOT root;
|
|
status = NtlpResolveRoot(canonical, length, modify, &root);
|
|
if (!KSUCCESS(status)) return status;
|
|
if (root.RelativePath.Length == 0) {
|
|
(void)SysCloseHandle(root.Handle);
|
|
return NTLSTATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
SYS_OPEN_FILE_AT_REQUEST request = SysMakeOpenFileAtRequest(root.Handle, root.RelativePath,
|
|
access, disposition, flags, handleOut);
|
|
status = SysOpenFileAt(&request);
|
|
(void)SysCloseHandle(root.Handle);
|
|
return status;
|
|
}
|
|
|
|
// Opens one directory through RSSRV resource resolution and capability-relative kernel I/O.
|
|
KSTATUS NtlOpenDir(SYS_STRING_VIEW path, uint32_t access, uint32_t flags, SYS_HANDLE* handleOut) {
|
|
if (!handleOut) return NTLSTATUS_INVALID_PARAMETER;
|
|
*handleOut = SYS_HANDLE_INVALID;
|
|
char canonical[SYS_PATH_MAX + 1u];
|
|
size_t length = 0;
|
|
KSTATUS status = NtlpNormalizePath(path, canonical, &length);
|
|
if (!KSUCCESS(status)) return status;
|
|
|
|
NTL_RESOLVED_ROOT root;
|
|
status = NtlpResolveRoot(canonical, length, (access & SYS_DIR_ACCESS_MODIFY) != 0, &root);
|
|
if (!KSUCCESS(status)) return status;
|
|
SYS_OPEN_DIR_AT_REQUEST request = SysMakeOpenDirAtRequest(root.Handle, root.RelativePath,
|
|
access, flags, handleOut);
|
|
status = SysOpenDirAt(&request);
|
|
(void)SysCloseHandle(root.Handle);
|
|
return status;
|
|
}
|
|
|
|
// Queries file metadata through one resolved root capability.
|
|
KSTATUS NtlStatPath(SYS_STRING_VIEW path, SYS_FILE_STAT* statOut) {
|
|
if (!statOut) return NTLSTATUS_INVALID_PARAMETER;
|
|
char canonical[SYS_PATH_MAX + 1u];
|
|
size_t length = 0;
|
|
KSTATUS status = NtlpNormalizePath(path, canonical, &length);
|
|
if (!KSUCCESS(status)) return status;
|
|
NTL_RESOLVED_ROOT root;
|
|
status = NtlpResolveRoot(canonical, length, 0, &root);
|
|
if (!KSUCCESS(status)) return status;
|
|
SYS_STAT_AT_REQUEST request = { sizeof(request), root.Handle, root.RelativePath, statOut };
|
|
status = SysStatAt(&request);
|
|
(void)SysCloseHandle(root.Handle);
|
|
return status;
|
|
}
|
|
|
|
// Deletes one file through one resolved writable root capability.
|
|
KSTATUS NtlDeleteFile(SYS_STRING_VIEW path) {
|
|
char canonical[SYS_PATH_MAX + 1u];
|
|
size_t length = 0;
|
|
KSTATUS status = NtlpNormalizePath(path, canonical, &length);
|
|
if (!KSUCCESS(status)) return status;
|
|
NTL_RESOLVED_ROOT root;
|
|
status = NtlpResolveRoot(canonical, length, 1, &root);
|
|
if (!KSUCCESS(status)) return status;
|
|
SYS_PATH_AT_REQUEST request = { sizeof(request), root.Handle, root.RelativePath };
|
|
status = root.RelativePath.Length ? SysDeleteAt(&request) : NTLSTATUS_INVALID_PARAMETER;
|
|
(void)SysCloseHandle(root.Handle);
|
|
return status;
|
|
}
|
|
|
|
// Renames one file using independently resolved writable roots.
|
|
KSTATUS NtlRenameFile(SYS_STRING_VIEW oldPath, SYS_STRING_VIEW newPath) {
|
|
char oldCanonical[SYS_PATH_MAX + 1u];
|
|
char newCanonical[SYS_PATH_MAX + 1u];
|
|
size_t oldLength = 0, newLength = 0;
|
|
KSTATUS status = NtlpNormalizePath(oldPath, oldCanonical, &oldLength);
|
|
if (!KSUCCESS(status)) return status;
|
|
status = NtlpNormalizePath(newPath, newCanonical, &newLength);
|
|
if (!KSUCCESS(status)) return status;
|
|
|
|
NTL_RESOLVED_ROOT oldRoot, newRoot;
|
|
status = NtlpResolveRoot(oldCanonical, oldLength, 1, &oldRoot);
|
|
if (!KSUCCESS(status)) return status;
|
|
status = NtlpResolveRoot(newCanonical, newLength, 1, &newRoot);
|
|
if (!KSUCCESS(status)) { (void)SysCloseHandle(oldRoot.Handle); return status; }
|
|
if (oldRoot.RelativePath.Length == 0 || newRoot.RelativePath.Length == 0) {
|
|
(void)SysCloseHandle(newRoot.Handle);
|
|
(void)SysCloseHandle(oldRoot.Handle);
|
|
return NTLSTATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
SYS_RENAME_AT_REQUEST request;
|
|
memset(&request, 0, sizeof(request));
|
|
request.Size = sizeof(request);
|
|
request.OldRootHandle = oldRoot.Handle;
|
|
request.OldPath = oldRoot.RelativePath;
|
|
request.NewRootHandle = newRoot.Handle;
|
|
request.NewPath = newRoot.RelativePath;
|
|
status = SysRenameAt(&request);
|
|
(void)SysCloseHandle(newRoot.Handle);
|
|
(void)SysCloseHandle(oldRoot.Handle);
|
|
return status;
|
|
}
|
|
|
|
// Creates one directory through one resolved writable root capability.
|
|
KSTATUS NtlCreateDirectory(SYS_STRING_VIEW path) {
|
|
char canonical[SYS_PATH_MAX + 1u];
|
|
size_t length = 0;
|
|
KSTATUS status = NtlpNormalizePath(path, canonical, &length);
|
|
if (!KSUCCESS(status)) return status;
|
|
NTL_RESOLVED_ROOT root;
|
|
status = NtlpResolveRoot(canonical, length, 1, &root);
|
|
if (!KSUCCESS(status)) return status;
|
|
SYS_PATH_AT_REQUEST request = { sizeof(request), root.Handle, root.RelativePath };
|
|
status = root.RelativePath.Length ? SysCreateDirAt(&request) : NTLSTATUS_INVALID_PARAMETER;
|
|
(void)SysCloseHandle(root.Handle);
|
|
return status;
|
|
}
|
|
|
|
// Removes one directory through one resolved writable root capability.
|
|
KSTATUS NtlRemoveDirectory(SYS_STRING_VIEW path) {
|
|
char canonical[SYS_PATH_MAX + 1u];
|
|
size_t length = 0;
|
|
KSTATUS status = NtlpNormalizePath(path, canonical, &length);
|
|
if (!KSUCCESS(status)) return status;
|
|
NTL_RESOLVED_ROOT root;
|
|
status = NtlpResolveRoot(canonical, length, 1, &root);
|
|
if (!KSUCCESS(status)) return status;
|
|
SYS_PATH_AT_REQUEST request = { sizeof(request), root.Handle, root.RelativePath };
|
|
status = root.RelativePath.Length ? SysRemoveDirAt(&request) : NTLSTATUS_INVALID_PARAMETER;
|
|
(void)SysCloseHandle(root.Handle);
|
|
return status;
|
|
}
|