redo boot around an explicit system volume and fixed kernel/HAL images, redo volumes and filesystem paths around capabilities instead of drive numbers and kernel CWD state, add a capability/security model with derived and revocable handles and security contexts, add port IPC and rssrv to manage the userspace resource namespace and publish the system volume, move process, dll and path loading to userspace and add process builder syscalls, init now supervises rssrv and the session process, redo the syscall ABI around request structs and four arguments, add a volume manager and proper partition arrival/removal handling so FAT and NTFS mount dynamically, clean up the HAL/DDK boundary so drivers don't depend on HAL internals, save fpu state properly now, the serial driver doesn't hang on a missing port, fix AHCI startup races, fix timeout accounting, fix shared mmap writeback, more smoke tests
This commit is contained in:
1 parent
4add6789ba
commit
f882d339fb
232 files changed
+20934
-8898
No files matched your search
@@ -0,0 +1,499 @@
|
||||
// Native userspace path policy. RSSRV resolves global resource prefixes; kernel I/O sees only capabilities and relative paths.
|
||||
|
||||
#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 long gPathState;
|
||||
static volatile long 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 (_InterlockedCompareExchange(&gPathLock, 1, 0) != 0)
|
||||
(void)SysYield();
|
||||
}
|
||||
|
||||
// Releases the userspace CWD lock.
|
||||
static void NtlpUnlockPathState(void) {
|
||||
_InterlockedExchange(&gPathLock, 0);
|
||||
}
|
||||
|
||||
// Initializes the process-local CWD from opaque loader data, or /SystemRoot for bootstrap init.
|
||||
static KSTATUS NtlpEnsurePathState(void) {
|
||||
if (_InterlockedCompareExchange(&gPathState, 2, 2) == 2)
|
||||
return NTLSTATUS_SUCCESS;
|
||||
|
||||
if (_InterlockedCompareExchange(&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')) {
|
||||
_InterlockedExchange(&gPathState, 0);
|
||||
return NTLSTATUS_FAILURE;
|
||||
}
|
||||
memcpy(gCurrentDirectory, inherited.Data, inherited.Length);
|
||||
gCurrentDirectory[inherited.Length] = '\0';
|
||||
gCurrentDirectoryLength = inherited.Length;
|
||||
gCurrentDirectoryRootLength = 0;
|
||||
_InterlockedExchange(&gPathState, 2);
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
while (_InterlockedCompareExchange(&gPathState, 2, 2) == 1)
|
||||
(void)SysYield();
|
||||
return _InterlockedCompareExchange(&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;
|
||||
}
|
||||
Reference in new issue
Block a user