summaryrefslogtreecommitdiffstats
path: root/src/VBox/Runtime/r3/nt
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Runtime/r3/nt')
-rw-r--r--src/VBox/Runtime/r3/nt/Makefile.kup0
-rw-r--r--src/VBox/Runtime/r3/nt/RTFileQueryFsSizes-nt.cpp102
-rw-r--r--src/VBox/Runtime/r3/nt/RTFileSetMode-r3-nt.cpp92
-rw-r--r--src/VBox/Runtime/r3/nt/RTPathQueryInfo-nt.cpp680
-rw-r--r--src/VBox/Runtime/r3/nt/RTPathSetMode-r3-nt.cpp98
-rw-r--r--src/VBox/Runtime/r3/nt/RTProcQueryParent-r3-nt.cpp103
-rw-r--r--src/VBox/Runtime/r3/nt/direnum-r3-nt.cpp1011
-rw-r--r--src/VBox/Runtime/r3/nt/dirrel-r3-nt.cpp651
-rw-r--r--src/VBox/Runtime/r3/nt/fs-nt.cpp279
-rw-r--r--src/VBox/Runtime/r3/nt/internal-r3-nt.h92
-rw-r--r--src/VBox/Runtime/r3/nt/pathint-nt.cpp1161
-rw-r--r--src/VBox/Runtime/r3/nt/time-nt.cpp228
12 files changed, 4497 insertions, 0 deletions
diff --git a/src/VBox/Runtime/r3/nt/Makefile.kup b/src/VBox/Runtime/r3/nt/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Runtime/r3/nt/Makefile.kup
diff --git a/src/VBox/Runtime/r3/nt/RTFileQueryFsSizes-nt.cpp b/src/VBox/Runtime/r3/nt/RTFileQueryFsSizes-nt.cpp
new file mode 100644
index 00000000..63a88211
--- /dev/null
+++ b/src/VBox/Runtime/r3/nt/RTFileQueryFsSizes-nt.cpp
@@ -0,0 +1,102 @@
+/* $Id: RTFileQueryFsSizes-nt.cpp $ */
+/** @file
+ * IPRT - RTFileQueryFsSizes, Native NT.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_FILE
+#include "internal-r3-nt.h"
+
+#include <iprt/file.h>
+#include <iprt/errcore.h>
+
+
+
+RTR3DECL(int) RTFileQueryFsSizes(RTFILE hFile, PRTFOFF pcbTotal, RTFOFF *pcbFree,
+ uint32_t *pcbBlock, uint32_t *pcbSector)
+{
+ int rc;
+
+ /*
+ * Get the volume information.
+ */
+ FILE_FS_SIZE_INFORMATION FsSizeInfo;
+ IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
+ NTSTATUS rcNt = NtQueryVolumeInformationFile((HANDLE)RTFileToNative(hFile), &Ios,
+ &FsSizeInfo, sizeof(FsSizeInfo), FileFsSizeInformation);
+ if (NT_SUCCESS(rcNt))
+ {
+ /*
+ * Calculate the return values.
+ */
+ if (pcbTotal)
+ {
+ *pcbTotal = FsSizeInfo.TotalAllocationUnits.QuadPart
+ * FsSizeInfo.SectorsPerAllocationUnit
+ * FsSizeInfo.BytesPerSector;
+ if ( *pcbTotal / FsSizeInfo.SectorsPerAllocationUnit / FsSizeInfo.BytesPerSector
+ != FsSizeInfo.TotalAllocationUnits.QuadPart)
+ *pcbTotal = UINT64_MAX;
+ }
+
+ if (pcbFree)
+ {
+ *pcbFree = FsSizeInfo.AvailableAllocationUnits.QuadPart
+ * FsSizeInfo.SectorsPerAllocationUnit
+ * FsSizeInfo.BytesPerSector;
+ if ( *pcbFree / FsSizeInfo.SectorsPerAllocationUnit / FsSizeInfo.BytesPerSector
+ != FsSizeInfo.AvailableAllocationUnits.QuadPart)
+ *pcbFree = UINT64_MAX;
+ }
+
+ rc = VINF_SUCCESS;
+ if (pcbBlock)
+ {
+ *pcbBlock = FsSizeInfo.SectorsPerAllocationUnit * FsSizeInfo.BytesPerSector;
+ if (*pcbBlock / FsSizeInfo.BytesPerSector != FsSizeInfo.SectorsPerAllocationUnit)
+ rc = VERR_OUT_OF_RANGE;
+ }
+
+ if (pcbSector)
+ *pcbSector = FsSizeInfo.BytesPerSector;
+ }
+ else
+ rc = RTErrConvertFromNtStatus(rcNt);
+
+ return rc;
+}
+
diff --git a/src/VBox/Runtime/r3/nt/RTFileSetMode-r3-nt.cpp b/src/VBox/Runtime/r3/nt/RTFileSetMode-r3-nt.cpp
new file mode 100644
index 00000000..2ec31d14
--- /dev/null
+++ b/src/VBox/Runtime/r3/nt/RTFileSetMode-r3-nt.cpp
@@ -0,0 +1,92 @@
+/* $Id: RTFileSetMode-r3-nt.cpp $ */
+/** @file
+ * IPRT - RTFileSetMode, Native NT.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_FILE
+#include "internal-r3-nt.h"
+
+#include <iprt/file.h>
+#include <iprt/err.h>
+
+#include "internal/fs.h"
+
+
+/**
+ * Common worker for RTFileSetMode, RTPathSetMode and RTDirRelPathSetMode.
+ *
+ * @returns IPRT status code.
+ * @param hNativeFile The NT handle to the file system object.
+ * @param fMode Valid and normalized file mode mask to set.
+ */
+DECLHIDDEN(int) rtNtFileSetModeWorker(HANDLE hNativeFile, RTFMODE fMode)
+{
+ IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
+ FILE_BASIC_INFORMATION BasicInfo;
+ BasicInfo.CreationTime.QuadPart = 0;
+ BasicInfo.ChangeTime.QuadPart = 0;
+ BasicInfo.LastAccessTime.QuadPart = 0;
+ BasicInfo.LastWriteTime.QuadPart = 0;
+ BasicInfo.FileAttributes = (fMode & ~( RTFS_DOS_NT_ENCRYPTED
+ | RTFS_DOS_NT_COMPRESSED
+ | RTFS_DOS_NT_REPARSE_POINT
+ | RTFS_DOS_NT_SPARSE_FILE
+ | RTFS_DOS_NT_DEVICE
+ | RTFS_DOS_DIRECTORY)
+ & RTFS_DOS_MASK_NT)
+ >> RTFS_DOS_SHIFT;
+ Assert(!(BasicInfo.FileAttributes & ~0x31a7U /* FILE_ATTRIBUTE_VALID_SET_FLAGS */));
+ if (!BasicInfo.FileAttributes)
+ BasicInfo.FileAttributes |= FILE_ATTRIBUTE_NORMAL;
+
+ NTSTATUS rcNt = NtSetInformationFile(hNativeFile, &Ios, &BasicInfo, sizeof(BasicInfo), FileBasicInformation);
+ if (NT_SUCCESS(rcNt))
+ return VINF_SUCCESS;
+ return RTErrConvertFromNtStatus(rcNt);
+}
+
+
+RTDECL(int) RTFileSetMode(RTFILE hFile, RTFMODE fMode)
+{
+ HANDLE hNative = (HANDLE)RTFileToNative(hFile);
+ AssertReturn(hNative != RTNT_INVALID_HANDLE_VALUE, VERR_INVALID_HANDLE);
+ fMode = rtFsModeNormalize(fMode, NULL, 0, RTFS_TYPE_FILE);
+ AssertReturn(rtFsModeIsValidPermissions(fMode), VERR_INVALID_FMODE);
+
+ return rtNtFileSetModeWorker(hNative, fMode);
+}
diff --git a/src/VBox/Runtime/r3/nt/RTPathQueryInfo-nt.cpp b/src/VBox/Runtime/r3/nt/RTPathQueryInfo-nt.cpp
new file mode 100644
index 00000000..a7b6459b
--- /dev/null
+++ b/src/VBox/Runtime/r3/nt/RTPathQueryInfo-nt.cpp
@@ -0,0 +1,680 @@
+/* $Id: RTPathQueryInfo-nt.cpp $ */
+/** @file
+ * IPRT - RTPathQueryInfo[Ex], Native NT.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_FILE
+#include "internal-r3-nt.h"
+
+#include <iprt/path.h>
+#include <iprt/err.h>
+#include <iprt/time.h>
+#include "internal/fs.h"
+#include "internal/path.h"
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** Helper for comparing a UNICODE_STRING with a string litteral. */
+#define ARE_UNICODE_STRINGS_EQUAL(a_UniStr, a_wszType) \
+ ( (a_UniStr)->Length == sizeof(a_wszType) - sizeof(RTUTF16) \
+ && memcmp((a_UniStr)->Buffer, a_wszType, sizeof(a_wszType) - sizeof(RTUTF16)) == 0)
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+typedef NTSTATUS (NTAPI *PFNNTQUERYFULLATTRIBUTESFILE)(struct _OBJECT_ATTRIBUTES *, struct _FILE_NETWORK_OPEN_INFORMATION *);
+extern PFNNTQUERYFULLATTRIBUTESFILE g_pfnNtQueryFullAttributesFile; /* init-win.cpp */
+
+
+/* ASSUMES FileID comes after ShortName and the structs are identical up to that point. */
+AssertCompileMembersSameSizeAndOffset(FILE_BOTH_DIR_INFORMATION, NextEntryOffset, FILE_ID_BOTH_DIR_INFORMATION, NextEntryOffset);
+AssertCompileMembersSameSizeAndOffset(FILE_BOTH_DIR_INFORMATION, FileIndex , FILE_ID_BOTH_DIR_INFORMATION, FileIndex );
+AssertCompileMembersSameSizeAndOffset(FILE_BOTH_DIR_INFORMATION, CreationTime , FILE_ID_BOTH_DIR_INFORMATION, CreationTime );
+AssertCompileMembersSameSizeAndOffset(FILE_BOTH_DIR_INFORMATION, LastAccessTime , FILE_ID_BOTH_DIR_INFORMATION, LastAccessTime );
+AssertCompileMembersSameSizeAndOffset(FILE_BOTH_DIR_INFORMATION, LastWriteTime , FILE_ID_BOTH_DIR_INFORMATION, LastWriteTime );
+AssertCompileMembersSameSizeAndOffset(FILE_BOTH_DIR_INFORMATION, ChangeTime , FILE_ID_BOTH_DIR_INFORMATION, ChangeTime );
+AssertCompileMembersSameSizeAndOffset(FILE_BOTH_DIR_INFORMATION, EndOfFile , FILE_ID_BOTH_DIR_INFORMATION, EndOfFile );
+AssertCompileMembersSameSizeAndOffset(FILE_BOTH_DIR_INFORMATION, AllocationSize , FILE_ID_BOTH_DIR_INFORMATION, AllocationSize );
+AssertCompileMembersSameSizeAndOffset(FILE_BOTH_DIR_INFORMATION, FileAttributes , FILE_ID_BOTH_DIR_INFORMATION, FileAttributes );
+AssertCompileMembersSameSizeAndOffset(FILE_BOTH_DIR_INFORMATION, FileNameLength , FILE_ID_BOTH_DIR_INFORMATION, FileNameLength );
+AssertCompileMembersSameSizeAndOffset(FILE_BOTH_DIR_INFORMATION, EaSize , FILE_ID_BOTH_DIR_INFORMATION, EaSize );
+AssertCompileMembersSameSizeAndOffset(FILE_BOTH_DIR_INFORMATION, ShortNameLength, FILE_ID_BOTH_DIR_INFORMATION, ShortNameLength);
+AssertCompileMembersSameSizeAndOffset(FILE_BOTH_DIR_INFORMATION, ShortName , FILE_ID_BOTH_DIR_INFORMATION, ShortName );
+
+
+
+/**
+ * Splits up an NT path into directory and filename.
+ *
+ * @param pNtName The path to split.
+ * @param pNtParent Where to return the directory path.
+ * @param pNtFilename Where to return the filename part.
+ * @param fNoParentDirSlash Whether to make sure the directory path doesn't
+ * end with a slash (except root).
+ */
+static void ntPathNtSplitName(UNICODE_STRING const *pNtName, UNICODE_STRING *pNtParent, UNICODE_STRING *pNtFilename,
+ bool fNoParentDirSlash)
+{
+ PRTUTF16 pwszBuffer = pNtName->Buffer;
+ size_t off = pNtName->Length / sizeof(RTUTF16);
+
+ /* Skip trailing slash if present. */
+ if ( off > 0
+ && pwszBuffer[off - 1] == '\\')
+ off--;
+
+ /* Find the slash before that. */
+ RTUTF16 wc;
+ while ( off > 0
+ && (wc = pwszBuffer[off - 1]) != '\\'
+ && wc != '/')
+ off--;
+ if (off != 0)
+ {
+ pNtParent->Buffer = pwszBuffer;
+ pNtParent->MaximumLength = pNtParent->Length = (USHORT)(off * sizeof(RTUTF16));
+ }
+ else
+ {
+ AssertFailed(); /* This is impossible and won't work (NT doesn't know '.' or '..'). */
+ /** @todo query the whole path as it is possible relative. Use the buffer for
+ * temporary name storage. */
+ pNtParent->Buffer = L".";
+ pNtParent->Length = 1 * sizeof(RTUTF16);
+ pNtParent->MaximumLength = 2 * sizeof(RTUTF16);
+ }
+
+ pNtFilename->Buffer = &pwszBuffer[off];
+ pNtFilename->Length = pNtName->Length - (USHORT)(off * sizeof(RTUTF16));
+ pNtFilename->MaximumLength = pNtName->MaximumLength - (USHORT)(off * sizeof(RTUTF16));
+
+ while ( fNoParentDirSlash
+ && pNtParent->Length > sizeof(RTUTF16)
+ && pNtParent->Buffer[pNtParent->Length / sizeof(RTUTF16) - 1] == '\\')
+ pNtParent->Length -= sizeof(RTUTF16);
+}
+
+
+/**
+ * Deals with enmAddAttr != RTFSOBJATTRADD_UNIX.
+ *
+ * @returns IPRT status code (usually @a rc).
+ * @param rc The return code.
+ * @param pObjInfo The info to complete.
+ * @param enmAddAttr What to complete it with. Caller should fill in
+ * RTFSOBJATTRADD_UNIX.
+ */
+static int rtPathNtQueryInfoFillInDummyData(int rc, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
+{
+ switch (enmAddAttr)
+ {
+ case RTFSOBJATTRADD_UNIX:
+ pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_UNIX;
+ break;
+
+ case RTFSOBJATTRADD_NOTHING:
+ pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_NOTHING;
+ break;
+
+ case RTFSOBJATTRADD_UNIX_OWNER:
+ pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_UNIX_OWNER;
+ pObjInfo->Attr.u.UnixOwner.uid = NIL_RTUID;
+ pObjInfo->Attr.u.UnixOwner.szName[0] = '\0'; /** @todo return something sensible here. */
+ break;
+
+ case RTFSOBJATTRADD_UNIX_GROUP:
+ pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_UNIX_GROUP;
+ pObjInfo->Attr.u.UnixGroup.gid = NIL_RTGID;
+ pObjInfo->Attr.u.UnixGroup.szName[0] = '\0';
+ break;
+
+ case RTFSOBJATTRADD_EASIZE:
+ pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_EASIZE;
+ pObjInfo->Attr.u.EASize.cb = 0;
+ break;
+
+ default:
+ AssertMsgFailed(("Impossible!\n"));
+ rc = VERR_INTERNAL_ERROR;
+ }
+ return rc;
+}
+
+
+/**
+ * Deal with getting info about something that could be in a directory object.
+ *
+ * @returns IPRT status code
+ * @param pObjAttr The NT object attribute.
+ * @param pObjInfo Where to return the info.
+ * @param enmAddAttr Which extra attributes to get (/fake).
+ * @param fFlags The flags.
+ * @param pvBuf Query buffer space.
+ * @param cbBuf Size of the buffer. ASSUMES lots of space.
+ * @param rcNtCaller The status code that got us here.
+ */
+static int rtPathNtQueryInfoInDirectoryObject(OBJECT_ATTRIBUTES *pObjAttr, PRTFSOBJINFO pObjInfo,
+ RTFSOBJATTRADD enmAddAttr, uint32_t fFlags,
+ void *pvBuf, size_t cbBuf, NTSTATUS rcNtCaller)
+{
+ RT_NOREF(fFlags);
+
+ /*
+ * Special case: Root dir.
+ */
+ if ( pObjAttr->RootDirectory == NULL
+ && pObjAttr->ObjectName->Length == sizeof(RTUTF16)
+ && pObjAttr->ObjectName->Buffer[0] == '\\')
+ {
+ pObjInfo->cbObject = 0;
+ pObjInfo->cbAllocated = 0;
+ RTTimeSpecSetNtTime(&pObjInfo->BirthTime, 0);
+ RTTimeSpecSetNtTime(&pObjInfo->AccessTime, 0);
+ RTTimeSpecSetNtTime(&pObjInfo->ModificationTime, 0);
+ RTTimeSpecSetNtTime(&pObjInfo->ChangeTime, 0);
+ pObjInfo->Attr.fMode = RTFS_DOS_DIRECTORY | RTFS_TYPE_DIRECTORY | 0777;
+ return rtPathNtQueryInfoFillInDummyData(VINF_SUCCESS, pObjInfo, enmAddAttr);
+ }
+
+ /*
+ * We must open and scan the parent directory object.
+ */
+ UNICODE_STRING NtDirName;
+ UNICODE_STRING NtDirEntry;
+ ntPathNtSplitName(pObjAttr->ObjectName, &NtDirName, &NtDirEntry, true /*fNoParentDirSlash*/);
+
+ while ( NtDirEntry.Length > sizeof(RTUTF16)
+ && NtDirEntry.Buffer[NtDirEntry.Length / sizeof(RTUTF16) - 1] == '\\')
+ NtDirEntry.Length -= sizeof(RTUTF16);
+
+ pObjAttr->ObjectName = &NtDirName;
+ HANDLE hDir = RTNT_INVALID_HANDLE_VALUE;
+ NTSTATUS rcNt = NtOpenDirectoryObject(&hDir, DIRECTORY_QUERY | DIRECTORY_TRAVERSE, pObjAttr);
+ if (NT_SUCCESS(rcNt))
+ {
+ ULONG uObjDirCtx = 0;
+ for (;;)
+ {
+ ULONG cbReturned = 0;
+ rcNt = NtQueryDirectoryObject(hDir,
+ pvBuf,
+ (ULONG)cbBuf,
+ FALSE /*ReturnSingleEntry */,
+ FALSE /*RestartScan*/,
+ &uObjDirCtx,
+ &cbReturned);
+ if (!NT_SUCCESS(rcNt))
+ break;
+
+ for (POBJECT_DIRECTORY_INFORMATION pObjDir = (POBJECT_DIRECTORY_INFORMATION)pvBuf;
+ pObjDir->Name.Length != 0;
+ pObjDir++)
+ {
+ if ( pObjDir->Name.Length == NtDirEntry.Length
+ && memcmp(pObjDir->Name.Buffer, NtDirEntry.Buffer, NtDirEntry.Length) == 0)
+ {
+ /*
+ * Find it. Fill in the info we've got and return (see similar code in direnum-r3-nt.cpp).
+ */
+ NtClose(hDir);
+
+ pObjInfo->cbObject = 0;
+ pObjInfo->cbAllocated = 0;
+ RTTimeSpecSetNtTime(&pObjInfo->BirthTime, 0);
+ RTTimeSpecSetNtTime(&pObjInfo->AccessTime, 0);
+ RTTimeSpecSetNtTime(&pObjInfo->ModificationTime, 0);
+ RTTimeSpecSetNtTime(&pObjInfo->ChangeTime, 0);
+
+ if (ARE_UNICODE_STRINGS_EQUAL(&pObjDir->TypeName, L"Directory"))
+ pObjInfo->Attr.fMode = RTFS_DOS_DIRECTORY | RTFS_TYPE_DIRECTORY | 0777;
+ else if (ARE_UNICODE_STRINGS_EQUAL(&pObjDir->TypeName, L"SymbolicLink"))
+ pObjInfo->Attr.fMode = RTFS_DOS_NT_REPARSE_POINT | RTFS_TYPE_SYMLINK | 0777;
+ else if (ARE_UNICODE_STRINGS_EQUAL(&pObjDir->TypeName, L"Device"))
+ pObjInfo->Attr.fMode = RTFS_DOS_NT_DEVICE | RTFS_TYPE_DEV_CHAR | 0666;
+ else
+ pObjInfo->Attr.fMode = RTFS_DOS_NT_NORMAL | RTFS_TYPE_FILE | 0666;
+
+ pObjInfo->Attr.enmAdditional = enmAddAttr;
+ return rtPathNtQueryInfoFillInDummyData(VINF_SUCCESS, pObjInfo, enmAddAttr);
+ }
+ }
+ }
+
+ NtClose(hDir);
+ if (rcNt == STATUS_NO_MORE_FILES || rcNt == STATUS_NO_MORE_ENTRIES || rcNt == STATUS_NO_SUCH_FILE)
+ return VERR_FILE_NOT_FOUND;
+ }
+ else
+ return RTErrConvertFromNtStatus(rcNtCaller);
+ return RTErrConvertFromNtStatus(rcNt);
+}
+
+
+/**
+ * Queries information from a file or directory handle.
+ *
+ * This is shared between the RTPathQueryInfo, RTFileQueryInfo and
+ * RTDirQueryInfo code.
+ *
+ * @returns IPRT status code.
+ * @param hFile The handle to query information from. Must have
+ * the necessary privileges.
+ * @param pvBuf Pointer to a scratch buffer.
+ * @param cbBuf The size of the buffer. This must be large
+ * enough to hold a FILE_ALL_INFORMATION struct.
+ * @param pObjInfo Where to return information about the handle.
+ * @param enmAddAttr What extra info to return.
+ * @param pszPath The path if this is a file (for exe detect).
+ * @param uReparseTag The reparse tag number (0 if not applicable) for
+ * symlink detection/whatnot.
+ */
+DECLHIDDEN(int) rtPathNtQueryInfoFromHandle(HANDLE hFile, void *pvBuf, size_t cbBuf, PRTFSOBJINFO pObjInfo,
+ RTFSOBJATTRADD enmAddAttr, const char *pszPath, ULONG uReparseTag)
+{
+ Assert(cbBuf >= sizeof(FILE_ALL_INFORMATION));
+
+ /** @todo Try optimize this for when RTFSOBJATTRADD_UNIX isn't set? */
+ IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
+ NTSTATUS rcNt = NtQueryInformationFile(hFile, &Ios, pvBuf, sizeof(FILE_ALL_INFORMATION), FileAllInformation);
+ if ( NT_SUCCESS(rcNt)
+ || rcNt == STATUS_BUFFER_OVERFLOW)
+ {
+ FILE_ALL_INFORMATION *pAllInfo = (FILE_ALL_INFORMATION *)pvBuf;
+ pObjInfo->cbObject = pAllInfo->StandardInformation.EndOfFile.QuadPart;
+ pObjInfo->cbAllocated = pAllInfo->StandardInformation.AllocationSize.QuadPart;
+ RTTimeSpecSetNtTime(&pObjInfo->BirthTime, pAllInfo->BasicInformation.CreationTime.QuadPart);
+ RTTimeSpecSetNtTime(&pObjInfo->AccessTime, pAllInfo->BasicInformation.LastAccessTime.QuadPart);
+ RTTimeSpecSetNtTime(&pObjInfo->ModificationTime, pAllInfo->BasicInformation.LastWriteTime.QuadPart);
+ RTTimeSpecSetNtTime(&pObjInfo->ChangeTime, pAllInfo->BasicInformation.ChangeTime.QuadPart);
+ pObjInfo->Attr.fMode = rtFsModeFromDos( (pAllInfo->BasicInformation.FileAttributes << RTFS_DOS_SHIFT)
+ & RTFS_DOS_MASK_NT,
+ pszPath, pszPath ? strlen(pszPath) : 0, uReparseTag, 0);
+ pObjInfo->Attr.enmAdditional = enmAddAttr;
+ if (enmAddAttr == RTFSOBJATTRADD_UNIX)
+ {
+ pObjInfo->Attr.u.Unix.uid = ~0U;
+ pObjInfo->Attr.u.Unix.gid = ~0U;
+ pObjInfo->Attr.u.Unix.cHardlinks = RT_MAX(1, pAllInfo->StandardInformation.NumberOfLinks);
+ pObjInfo->Attr.u.Unix.INodeIdDevice = 0; /* below */
+ pObjInfo->Attr.u.Unix.INodeId = pAllInfo->InternalInformation.IndexNumber.QuadPart;
+ pObjInfo->Attr.u.Unix.fFlags = 0;
+ pObjInfo->Attr.u.Unix.GenerationId = 0;
+ pObjInfo->Attr.u.Unix.Device = 0;
+
+ /* Get the serial number. */
+ rcNt = NtQueryVolumeInformationFile(hFile, &Ios, pvBuf, (ULONG)RT_MIN(cbBuf, _2K), FileFsVolumeInformation);
+ if (NT_SUCCESS(rcNt) || rcNt == STATUS_BUFFER_OVERFLOW)
+ {
+ FILE_FS_VOLUME_INFORMATION *pVolInfo = (FILE_FS_VOLUME_INFORMATION *)pvBuf;
+ pObjInfo->Attr.u.Unix.INodeIdDevice = pVolInfo->VolumeSerialNumber;
+ }
+ }
+
+ return rtPathNtQueryInfoFillInDummyData(VINF_SUCCESS, pObjInfo, enmAddAttr);
+ }
+ return RTErrConvertFromNtStatus(rcNt);
+}
+
+
+/**
+ * Worker for RTPathQueryInfoEx and RTDirRelPathQueryInfo.
+ *
+ * @returns IPRT status code.
+ * @param hRootDir The root directory that pNtName is relative to.
+ * @param pNtName The NT path which we want to query info for.
+ * @param pObjInfo Where to return the info.
+ * @param enmAddAttr What additional info to get/fake.
+ * @param fFlags Query flags (RTPATH_F_XXX).
+ * @param pszPath The path for detecting executables and such.
+ * Pass empty string if not applicable/available.
+ */
+DECLHIDDEN(int) rtPathNtQueryInfoWorker(HANDLE hRootDir, UNICODE_STRING *pNtName, PRTFSOBJINFO pObjInfo,
+ RTFSOBJATTRADD enmAddAttr, uint32_t fFlags, const char *pszPath)
+{
+ /*
+ * There are a three different ways of doing this:
+ * 1. Use NtQueryFullAttributesFile to the get basic file info.
+ * 2. Open whatever the path points to and use NtQueryInformationFile.
+ * 3. Open the parent directory and use NtQueryDirectoryFile like RTDirReadEx.
+ *
+ * The first two options may fail with sharing violations or access denied,
+ * in which case we must use the last one as fallback.
+ */
+ HANDLE hFile = RTNT_INVALID_HANDLE_VALUE;
+ IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
+ NTSTATUS rcNt;
+ OBJECT_ATTRIBUTES ObjAttr;
+ union
+ {
+ FILE_NETWORK_OPEN_INFORMATION NetOpenInfo;
+ FILE_ALL_INFORMATION AllInfo;
+ FILE_FS_VOLUME_INFORMATION VolInfo;
+ FILE_BOTH_DIR_INFORMATION Both;
+ FILE_ID_BOTH_DIR_INFORMATION BothId;
+ uint8_t abPadding[sizeof(FILE_ID_BOTH_DIR_INFORMATION) + RTPATH_MAX * sizeof(wchar_t)];
+ } uBuf;
+
+ /*
+ * We can only use the first option if no additional UNIX attribs are
+ * requested and it isn't a symbolic link. NT directory object
+ */
+ int rc = VINF_TRY_AGAIN;
+ if ( enmAddAttr != RTFSOBJATTRADD_UNIX
+ && g_pfnNtQueryFullAttributesFile)
+ {
+ InitializeObjectAttributes(&ObjAttr, pNtName, OBJ_CASE_INSENSITIVE, hRootDir, NULL);
+ rcNt = g_pfnNtQueryFullAttributesFile(&ObjAttr, &uBuf.NetOpenInfo);
+ if (NT_SUCCESS(rcNt))
+ {
+ if (!(uBuf.NetOpenInfo.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT))
+ {
+ pObjInfo->cbObject = uBuf.NetOpenInfo.EndOfFile.QuadPart;
+ pObjInfo->cbAllocated = uBuf.NetOpenInfo.AllocationSize.QuadPart;
+ RTTimeSpecSetNtTime(&pObjInfo->BirthTime, uBuf.NetOpenInfo.CreationTime.QuadPart);
+ RTTimeSpecSetNtTime(&pObjInfo->AccessTime, uBuf.NetOpenInfo.LastAccessTime.QuadPart);
+ RTTimeSpecSetNtTime(&pObjInfo->ModificationTime, uBuf.NetOpenInfo.LastWriteTime.QuadPart);
+ RTTimeSpecSetNtTime(&pObjInfo->ChangeTime, uBuf.NetOpenInfo.ChangeTime.QuadPart);
+ pObjInfo->Attr.fMode = rtFsModeFromDos((uBuf.NetOpenInfo.FileAttributes << RTFS_DOS_SHIFT) & RTFS_DOS_MASK_NT,
+ pszPath, strlen(pszPath), 0 /*uReparseTag*/, 0);
+ pObjInfo->Attr.enmAdditional = enmAddAttr;
+
+ return rtPathNtQueryInfoFillInDummyData(VINF_SUCCESS, pObjInfo, enmAddAttr);
+ }
+ }
+ else if ( rcNt == STATUS_OBJECT_TYPE_MISMATCH
+ || rcNt == STATUS_OBJECT_NAME_INVALID
+ || rcNt == STATUS_INVALID_PARAMETER)
+ {
+ rc = rtPathNtQueryInfoInDirectoryObject(&ObjAttr, pObjInfo, enmAddAttr, fFlags, &uBuf, sizeof(uBuf), rcNt);
+ if (RT_SUCCESS(rc))
+ return rc;
+ }
+ else if ( rcNt != STATUS_ACCESS_DENIED
+ && rcNt != STATUS_SHARING_VIOLATION)
+ rc = RTErrConvertFromNtStatus(rcNt);
+ else
+ RTNT_IO_STATUS_BLOCK_REINIT(&Ios);
+ }
+
+ /*
+ * Try the 2nd option. We might have to redo this if not following symbolic
+ * links and the reparse point isn't a symbolic link but a mount point or similar.
+ * We want to return information about the mounted root directory if we can, not
+ * the directory in which it was mounted.
+ */
+ if (rc == VINF_TRY_AGAIN)
+ {
+ static int volatile g_fReparsePoints = -1;
+ uint32_t fOptions = FILE_OPEN_FOR_BACKUP_INTENT | FILE_SYNCHRONOUS_IO_NONALERT;
+ int fReparsePoints = g_fReparsePoints;
+ if (fReparsePoints != 0 && !(fFlags & RTPATH_F_FOLLOW_LINK))
+ fOptions |= FILE_OPEN_REPARSE_POINT;
+
+ InitializeObjectAttributes(&ObjAttr, pNtName, OBJ_CASE_INSENSITIVE, hRootDir, NULL);
+ rcNt = NtCreateFile(&hFile,
+ FILE_READ_ATTRIBUTES | SYNCHRONIZE,
+ &ObjAttr,
+ &Ios,
+ NULL /*pcbFile*/,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ FILE_OPEN,
+ fOptions,
+ NULL /*pvEaBuffer*/,
+ 0 /*cbEa*/);
+ if ( ( rcNt == STATUS_INVALID_PARAMETER
+ || rcNt == STATUS_INVALID_PARAMETER_9)
+ && fReparsePoints == -1
+ && (fOptions & FILE_OPEN_REPARSE_POINT))
+ {
+ fOptions &= ~FILE_OPEN_REPARSE_POINT;
+ rcNt = NtCreateFile(&hFile,
+ FILE_READ_ATTRIBUTES | SYNCHRONIZE,
+ &ObjAttr,
+ &Ios,
+ NULL /*pcbFile*/,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ FILE_OPEN,
+ fOptions,
+ NULL /*pvEaBuffer*/,
+ 0 /*cbEa*/);
+ if (rcNt != STATUS_INVALID_PARAMETER)
+ g_fReparsePoints = fReparsePoints = 0;
+ }
+ if (NT_SUCCESS(rcNt))
+ {
+ /* Query tag information first in order to try re-open non-symlink reparse points. */
+ FILE_ATTRIBUTE_TAG_INFORMATION TagInfo;
+ rcNt = NtQueryInformationFile(hFile, &Ios, &TagInfo, sizeof(TagInfo), FileAttributeTagInformation);
+ if (!NT_SUCCESS(rcNt))
+ TagInfo.FileAttributes = TagInfo.ReparseTag = 0;
+ if ( !(TagInfo.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)
+ || TagInfo.ReparseTag == IO_REPARSE_TAG_SYMLINK
+ || (fFlags & RTPATH_F_FOLLOW_LINK))
+ { /* likely */ }
+ else
+ {
+ /* Reparse point that isn't a symbolic link, try follow the reparsing. */
+ HANDLE hFile2;
+ RTNT_IO_STATUS_BLOCK_REINIT(&Ios);
+ rcNt = NtCreateFile(&hFile2,
+ FILE_READ_ATTRIBUTES | SYNCHRONIZE,
+ &ObjAttr,
+ &Ios,
+ NULL /*pcbFile*/,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ FILE_OPEN,
+ FILE_OPEN_FOR_BACKUP_INTENT | FILE_SYNCHRONOUS_IO_NONALERT,
+ NULL /*pvEaBuffer*/,
+ 0 /*cbEa*/);
+ if (NT_SUCCESS(rcNt))
+ {
+ NtClose(hFile);
+ hFile = hFile2;
+ TagInfo.FileAttributes = TagInfo.ReparseTag = 0;
+ }
+ }
+
+ /*
+ * Get the information we need and convert it.
+ */
+ rc = rtPathNtQueryInfoFromHandle(hFile, &uBuf, sizeof(uBuf), pObjInfo, enmAddAttr, pszPath, TagInfo.ReparseTag);
+ NtClose(hFile);
+ if (RT_SUCCESS(rc))
+ return rc;
+
+ if (RT_FAILURE(rc))
+ rc = VINF_TRY_AGAIN;
+ }
+ else if ( rcNt == STATUS_OBJECT_TYPE_MISMATCH
+ || rcNt == STATUS_OBJECT_NAME_INVALID
+ /*|| rcNt == STATUS_INVALID_PARAMETER*/)
+ {
+ rc = rtPathNtQueryInfoInDirectoryObject(&ObjAttr, pObjInfo, enmAddAttr, fFlags, &uBuf, sizeof(uBuf), rcNt);
+ if (RT_SUCCESS(rc))
+ return rc;
+ }
+ else if ( rcNt != STATUS_ACCESS_DENIED
+ && rcNt != STATUS_SHARING_VIOLATION)
+ rc = RTErrConvertFromNtStatus(rcNt);
+ else
+ RTNT_IO_STATUS_BLOCK_REINIT(&Ios);
+ }
+
+ /*
+ * Try the 3rd option if none of the other worked.
+ * If none of the above worked, try open the directory and enumerate
+ * the file we're after. This
+ */
+ if (rc == VINF_TRY_AGAIN)
+ {
+ /* Split up the name into parent directory path and filename. */
+ UNICODE_STRING NtDirName;
+ UNICODE_STRING NtFilter;
+ ntPathNtSplitName(pNtName, &NtDirName, &NtFilter, false /*fNoParentDirSlash*/);
+
+ /* Try open the directory. */
+ InitializeObjectAttributes(&ObjAttr, &NtDirName, OBJ_CASE_INSENSITIVE, hRootDir, NULL);
+ rcNt = NtCreateFile(&hFile,
+ FILE_LIST_DIRECTORY | SYNCHRONIZE,
+ &ObjAttr,
+ &Ios,
+ NULL /*pcbFile*/,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ FILE_OPEN,
+ FILE_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT | FILE_SYNCHRONOUS_IO_NONALERT,
+ NULL /*pvEaBuffer*/,
+ 0 /*cbEa*/);
+ if (NT_SUCCESS(rcNt))
+ {
+ FILE_INFORMATION_CLASS enmInfoClass;
+ if (RT_MAKE_U64(RTNtCurrentPeb()->OSMinorVersion, RTNtCurrentPeb()->OSMajorVersion) > RT_MAKE_U64(0,5) /* > W2K */)
+ enmInfoClass = FileIdBothDirectoryInformation; /* Introduced in XP, from I can tell. */
+ else
+ enmInfoClass = FileBothDirectoryInformation;
+ rcNt = NtQueryDirectoryFile(hFile,
+ NULL /* Event */,
+ NULL /* ApcRoutine */,
+ NULL /* ApcContext */,
+ &Ios,
+ &uBuf,
+ RT_MIN(sizeof(uBuf), 0xfff0),
+ enmInfoClass,
+ TRUE /*ReturnSingleEntry */,
+ &NtFilter,
+ FALSE /*RestartScan */);
+ if (NT_SUCCESS(rcNt))
+ {
+ pObjInfo->cbObject = uBuf.Both.EndOfFile.QuadPart;
+ pObjInfo->cbAllocated = uBuf.Both.AllocationSize.QuadPart;
+
+ RTTimeSpecSetNtTime(&pObjInfo->BirthTime, uBuf.Both.CreationTime.QuadPart);
+ RTTimeSpecSetNtTime(&pObjInfo->AccessTime, uBuf.Both.LastAccessTime.QuadPart);
+ RTTimeSpecSetNtTime(&pObjInfo->ModificationTime, uBuf.Both.LastWriteTime.QuadPart);
+ RTTimeSpecSetNtTime(&pObjInfo->ChangeTime, uBuf.Both.ChangeTime.QuadPart);
+
+ pObjInfo->Attr.fMode = rtFsModeFromDos((uBuf.Both.FileAttributes << RTFS_DOS_SHIFT) & RTFS_DOS_MASK_NT,
+ pszPath, strlen(pszPath), uBuf.Both.EaSize, 0);
+
+ pObjInfo->Attr.enmAdditional = enmAddAttr;
+ if (enmAddAttr == RTFSOBJATTRADD_UNIX)
+ {
+ pObjInfo->Attr.u.Unix.uid = ~0U;
+ pObjInfo->Attr.u.Unix.gid = ~0U;
+ pObjInfo->Attr.u.Unix.cHardlinks = 1;
+ pObjInfo->Attr.u.Unix.INodeIdDevice = 0; /* below */
+ pObjInfo->Attr.u.Unix.INodeId = enmInfoClass == FileIdBothDirectoryInformation
+ ? uBuf.BothId.FileId.QuadPart : 0;
+ pObjInfo->Attr.u.Unix.fFlags = 0;
+ pObjInfo->Attr.u.Unix.GenerationId = 0;
+ pObjInfo->Attr.u.Unix.Device = 0;
+
+ /* Get the serial number. */
+ rcNt = NtQueryVolumeInformationFile(hFile, &Ios, &uBuf, RT_MIN(sizeof(uBuf), _2K),
+ FileFsVolumeInformation);
+ if (NT_SUCCESS(rcNt))
+ pObjInfo->Attr.u.Unix.INodeIdDevice = uBuf.VolInfo.VolumeSerialNumber;
+ }
+
+ rc = rtPathNtQueryInfoFillInDummyData(VINF_SUCCESS, pObjInfo, enmAddAttr);
+ }
+ else
+ rc = RTErrConvertFromNtStatus(rcNt);
+
+ NtClose(hFile);
+ }
+ /*
+ * Quite possibly a object directory.
+ */
+ else if ( rcNt == STATUS_OBJECT_NAME_INVALID /* with trailing slash */
+ || rcNt == STATUS_OBJECT_TYPE_MISMATCH /* without trailing slash */ )
+ {
+ InitializeObjectAttributes(&ObjAttr, pNtName, OBJ_CASE_INSENSITIVE, hRootDir, NULL);
+ rc = rtPathNtQueryInfoInDirectoryObject(&ObjAttr, pObjInfo, enmAddAttr, fFlags, &uBuf, sizeof(uBuf), rcNt);
+ if (RT_FAILURE(rc))
+ rc = RTErrConvertFromNtStatus(rcNt);
+ }
+ else
+ rc = RTErrConvertFromNtStatus(rcNt);
+ }
+
+ return rc;
+}
+
+
+RTR3DECL(int) RTPathQueryInfoEx(const char *pszPath, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAdditionalAttribs, uint32_t fFlags)
+{
+ /*
+ * Validate input.
+ */
+ AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
+ AssertReturn(*pszPath, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pObjInfo, VERR_INVALID_POINTER);
+ AssertMsgReturn( enmAdditionalAttribs >= RTFSOBJATTRADD_NOTHING
+ && enmAdditionalAttribs <= RTFSOBJATTRADD_LAST,
+ ("Invalid enmAdditionalAttribs=%p\n", enmAdditionalAttribs),
+ VERR_INVALID_PARAMETER);
+ AssertMsgReturn(RTPATH_F_IS_VALID(fFlags, 0), ("%#x\n", fFlags), VERR_INVALID_PARAMETER);
+
+
+ /*
+ * Convert the input path and call common worker.
+ */
+ HANDLE hRootDir;
+ UNICODE_STRING NtName;
+ int rc = RTNtPathFromWinUtf8(&NtName, &hRootDir, pszPath);
+ if (RT_SUCCESS(rc))
+ {
+ rc = rtPathNtQueryInfoWorker(hRootDir, &NtName, pObjInfo, enmAdditionalAttribs, fFlags, pszPath);
+ RTNtPathFree(&NtName, &hRootDir);
+ }
+ return rc;
+}
+
+
+RTR3DECL(int) RTPathQueryInfo(const char *pszPath, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAdditionalAttribs)
+{
+ return RTPathQueryInfoEx(pszPath, pObjInfo, enmAdditionalAttribs, RTPATH_F_ON_LINK);
+}
+
diff --git a/src/VBox/Runtime/r3/nt/RTPathSetMode-r3-nt.cpp b/src/VBox/Runtime/r3/nt/RTPathSetMode-r3-nt.cpp
new file mode 100644
index 00000000..d6fb0444
--- /dev/null
+++ b/src/VBox/Runtime/r3/nt/RTPathSetMode-r3-nt.cpp
@@ -0,0 +1,98 @@
+/* $Id: RTPathSetMode-r3-nt.cpp $ */
+/** @file
+ * IPRT - RTPathSetMode, Native NT.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_FILE
+#include "internal-r3-nt.h"
+
+#include <iprt/path.h>
+#include <iprt/err.h>
+
+#include "internal/fs.h"
+
+
+
+RTDECL(int) RTPathSetMode(const char *pszPath, RTFMODE fMode)
+{
+ fMode = rtFsModeNormalize(fMode, pszPath, 0, 0);
+ AssertReturn(rtFsModeIsValidPermissions(fMode), VERR_INVALID_FMODE);
+
+ /*
+ * Convert and normalize the path.
+ */
+ UNICODE_STRING NtName;
+ HANDLE hRootDir;
+ int rc = RTNtPathFromWinUtf8(&NtName, &hRootDir, pszPath);
+ if (RT_SUCCESS(rc))
+ {
+ HANDLE hPath = RTNT_INVALID_HANDLE_VALUE;
+ IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
+ OBJECT_ATTRIBUTES ObjAttr;
+ InitializeObjectAttributes(&ObjAttr, &NtName, 0 /*fAttrib*/, hRootDir, NULL);
+
+ ULONG fOpenOptions = FILE_OPEN_FOR_BACKUP_INTENT | FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_REPARSE_POINT;
+ //if (fFlags & RTPATH_F_ON_LINK)
+ // fOpenOptions |= FILE_OPEN_REPARSE_POINT;
+ NTSTATUS rcNt = NtCreateFile(&hPath,
+ FILE_WRITE_ATTRIBUTES | SYNCHRONIZE,
+ &ObjAttr,
+ &Ios,
+ NULL /*AllocationSize*/,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
+ FILE_OPEN,
+ fOpenOptions,
+ NULL /*EaBuffer*/,
+ 0 /*EaLength*/);
+ if (NT_SUCCESS(rcNt))
+ {
+ rc = rtNtFileSetModeWorker(hPath, fMode);
+
+ rcNt = NtClose(hPath);
+ if (!NT_SUCCESS(rcNt) && RT_SUCCESS(rc))
+ rc = RTErrConvertFromNtStatus(rcNt);
+ }
+ else
+ rc = RTErrConvertFromNtStatus(rcNt);
+
+ RTNtPathFree(&NtName, &hRootDir);
+ }
+ return rc;
+}
+
diff --git a/src/VBox/Runtime/r3/nt/RTProcQueryParent-r3-nt.cpp b/src/VBox/Runtime/r3/nt/RTProcQueryParent-r3-nt.cpp
new file mode 100644
index 00000000..650131d1
--- /dev/null
+++ b/src/VBox/Runtime/r3/nt/RTProcQueryParent-r3-nt.cpp
@@ -0,0 +1,103 @@
+/* $Id: RTProcQueryParent-r3-nt.cpp $ */
+/** @file
+ * IPRT - Process, Windows.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_PROCESS
+#include <iprt/nt/nt.h>
+
+#include <iprt/process.h>
+#include "internal/iprt.h"
+
+#include <iprt/assert.h>
+#include <iprt/errcore.h>
+
+
+
+RTR3DECL(int) RTProcQueryParent(RTPROCESS hProcess, PRTPROCESS phParent)
+{
+ NTSTATUS rcNt;
+ HANDLE hClose = RTNT_INVALID_HANDLE_VALUE;
+ HANDLE hNtProc;
+
+ /*
+ * Open the process. We take a shortcut if it's the current process.
+ */
+ if (hProcess == RTProcSelf())
+ hNtProc = NtCurrentProcess();
+ else
+ {
+ CLIENT_ID ClientId;
+ ClientId.UniqueProcess = (HANDLE)(uintptr_t)hProcess;
+ ClientId.UniqueThread = NULL;
+
+ OBJECT_ATTRIBUTES ObjAttrs;
+ InitializeObjectAttributes(&ObjAttrs, NULL, OBJ_CASE_INSENSITIVE, NULL, NULL);
+
+ rcNt = NtOpenProcess(&hClose, PROCESS_QUERY_LIMITED_INFORMATION, &ObjAttrs, &ClientId);
+ if (!NT_SUCCESS(rcNt))
+ rcNt = NtOpenProcess(&hClose, PROCESS_QUERY_INFORMATION, &ObjAttrs, &ClientId);
+ if (!NT_SUCCESS(rcNt))
+ return RTErrConvertFromNtStatus(rcNt);
+ hNtProc = hClose;
+ }
+
+ /*
+ * Query the information.
+ */
+ int rc;
+ PROCESS_BASIC_INFORMATION BasicInfo;
+ ULONG cbIgn;
+ rcNt = NtQueryInformationProcess(hNtProc, ProcessBasicInformation, &BasicInfo, sizeof(BasicInfo), &cbIgn);
+ if (NT_SUCCESS(rcNt))
+ {
+ *phParent = BasicInfo.InheritedFromUniqueProcessId;
+ rc = VINF_SUCCESS;
+ }
+ else
+ rc = RTErrConvertFromNtStatus(rcNt);
+
+ /*
+ * Clean up.
+ */
+ if (hClose != RTNT_INVALID_HANDLE_VALUE)
+ NtClose(hClose);
+
+ return rc;
+}
+
diff --git a/src/VBox/Runtime/r3/nt/direnum-r3-nt.cpp b/src/VBox/Runtime/r3/nt/direnum-r3-nt.cpp
new file mode 100644
index 00000000..0325e842
--- /dev/null
+++ b/src/VBox/Runtime/r3/nt/direnum-r3-nt.cpp
@@ -0,0 +1,1011 @@
+/* $Id: direnum-r3-nt.cpp $ */
+/** @file
+ * IPRT - Directory Enumeration, Native NT.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_DIR
+#include "internal-r3-nt.h"
+
+#include <iprt/dir.h>
+#include <iprt/path.h>
+#include <iprt/mem.h>
+#include <iprt/string.h>
+#include <iprt/assert.h>
+#include <iprt/err.h>
+#include <iprt/file.h>
+#include <iprt/log.h>
+#include <iprt/utf16.h>
+#include "internal/fs.h"
+#include "internal/dir.h"
+#include "internal/path.h"
+#include "../win/internal-r3-win.h"
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** Whether to return a single record (TRUE) or multiple (FALSE). */
+#define RTDIR_NT_SINGLE_RECORD FALSE
+
+/** Go hard on record chaining (has slight performance impact). */
+#ifdef RT_STRICT
+# define RTDIR_NT_STRICT
+#endif
+
+
+/* ASSUMES FileID comes after ShortName and the structs are identical up to that point. */
+AssertCompileMembersSameSizeAndOffset(FILE_BOTH_DIR_INFORMATION, NextEntryOffset, FILE_ID_BOTH_DIR_INFORMATION, NextEntryOffset);
+AssertCompileMembersSameSizeAndOffset(FILE_BOTH_DIR_INFORMATION, FileIndex , FILE_ID_BOTH_DIR_INFORMATION, FileIndex );
+AssertCompileMembersSameSizeAndOffset(FILE_BOTH_DIR_INFORMATION, CreationTime , FILE_ID_BOTH_DIR_INFORMATION, CreationTime );
+AssertCompileMembersSameSizeAndOffset(FILE_BOTH_DIR_INFORMATION, LastAccessTime , FILE_ID_BOTH_DIR_INFORMATION, LastAccessTime );
+AssertCompileMembersSameSizeAndOffset(FILE_BOTH_DIR_INFORMATION, LastWriteTime , FILE_ID_BOTH_DIR_INFORMATION, LastWriteTime );
+AssertCompileMembersSameSizeAndOffset(FILE_BOTH_DIR_INFORMATION, ChangeTime , FILE_ID_BOTH_DIR_INFORMATION, ChangeTime );
+AssertCompileMembersSameSizeAndOffset(FILE_BOTH_DIR_INFORMATION, EndOfFile , FILE_ID_BOTH_DIR_INFORMATION, EndOfFile );
+AssertCompileMembersSameSizeAndOffset(FILE_BOTH_DIR_INFORMATION, AllocationSize , FILE_ID_BOTH_DIR_INFORMATION, AllocationSize );
+AssertCompileMembersSameSizeAndOffset(FILE_BOTH_DIR_INFORMATION, FileAttributes , FILE_ID_BOTH_DIR_INFORMATION, FileAttributes );
+AssertCompileMembersSameSizeAndOffset(FILE_BOTH_DIR_INFORMATION, FileNameLength , FILE_ID_BOTH_DIR_INFORMATION, FileNameLength );
+AssertCompileMembersSameSizeAndOffset(FILE_BOTH_DIR_INFORMATION, EaSize , FILE_ID_BOTH_DIR_INFORMATION, EaSize );
+AssertCompileMembersSameSizeAndOffset(FILE_BOTH_DIR_INFORMATION, ShortNameLength, FILE_ID_BOTH_DIR_INFORMATION, ShortNameLength);
+AssertCompileMembersSameSizeAndOffset(FILE_BOTH_DIR_INFORMATION, ShortName , FILE_ID_BOTH_DIR_INFORMATION, ShortName );
+
+
+
+size_t rtDirNativeGetStructSize(const char *pszPath)
+{
+ NOREF(pszPath);
+ return sizeof(RTDIRINTERNAL);
+}
+
+
+int rtDirNativeOpen(PRTDIRINTERNAL pDir, uintptr_t hRelativeDir, void *pvNativeRelative)
+{
+ /*
+ * Convert the filter to UTF-16.
+ */
+ int rc;
+ pDir->pNtFilterStr = NULL;
+ if ( pDir->cchFilter > 0
+ && pDir->enmFilter == RTDIRFILTER_WINNT)
+ {
+ PRTUTF16 pwszTmp;
+ rc = RTStrToUtf16(pDir->pszFilter, &pwszTmp);
+ if (RT_FAILURE(rc))
+ return rc;
+ pDir->NtFilterStr.Buffer = pwszTmp;
+ pDir->NtFilterStr.Length = pDir->NtFilterStr.MaximumLength = (uint16_t)(RTUtf16Len(pwszTmp) * sizeof(RTUTF16));
+ pDir->pNtFilterStr = &pDir->NtFilterStr;
+ }
+
+ /*
+ * Try open the directory
+ */
+#ifdef IPRT_WITH_NT_PATH_PASSTHRU
+ bool fObjDir = false;
+#endif
+ if (hRelativeDir != ~(uintptr_t)0 && pvNativeRelative == NULL)
+ {
+ /* Caller already opened it, easy! */
+ pDir->hDir = (HANDLE)hRelativeDir;
+ rc = VINF_SUCCESS;
+ }
+ else
+ {
+ /*
+ * If we have to check for reparse points, this gets complicated!
+ */
+ static int volatile g_fReparsePoints = -1;
+ uint32_t fOptions = FILE_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT | FILE_SYNCHRONOUS_IO_NONALERT;
+ int fReparsePoints = g_fReparsePoints;
+ if ( fReparsePoints != 0
+ && (pDir->fFlags & RTDIR_F_NO_FOLLOW)
+ && !pDir->fDirSlash)
+ fOptions |= FILE_OPEN_REPARSE_POINT;
+
+ ACCESS_MASK fDesiredAccess = FILE_LIST_DIRECTORY | FILE_READ_ATTRIBUTES | FILE_TRAVERSE | SYNCHRONIZE;
+ for (;;)
+ {
+ if (pvNativeRelative == NULL)
+ rc = RTNtPathOpenDir(pDir->pszPath,
+ fDesiredAccess,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ fOptions,
+ OBJ_CASE_INSENSITIVE,
+ &pDir->hDir,
+#ifdef IPRT_WITH_NT_PATH_PASSTHRU
+ &fObjDir
+#else
+ NULL
+#endif
+ );
+ else
+ rc = RTNtPathOpenDirEx((HANDLE)hRelativeDir,
+ (struct _UNICODE_STRING *)pvNativeRelative,
+ fDesiredAccess,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ fOptions,
+ OBJ_CASE_INSENSITIVE,
+ &pDir->hDir,
+#ifdef IPRT_WITH_NT_PATH_PASSTHRU
+ &fObjDir
+#else
+ NULL
+#endif
+ );
+ if ( rc == VERR_ACCESS_DENIED /* Seen with c:\windows\system32\com\dmp on w7 & w10 (admin mode). */
+ && (fDesiredAccess & FILE_TRAVERSE))
+ {
+ fDesiredAccess &= ~FILE_TRAVERSE;
+ continue;
+ }
+ if ( !(fOptions & FILE_OPEN_REPARSE_POINT)
+ || (rc != VINF_SUCCESS && rc != VERR_INVALID_PARAMETER) )
+ break;
+ if (rc == VINF_SUCCESS)
+ {
+ if (fReparsePoints == -1)
+ g_fReparsePoints = 1;
+
+ /*
+ * We now need to check if we opened a symbolic directory link.
+ * (These can be enumerated, but contains only '.' and '..'.)
+ */
+ FILE_ATTRIBUTE_TAG_INFORMATION TagInfo = { 0, 0 };
+ IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
+ NTSTATUS rcNt = NtQueryInformationFile(pDir->hDir, &Ios, &TagInfo, sizeof(TagInfo), FileAttributeTagInformation);
+ AssertMsg(NT_SUCCESS(rcNt), ("%#x\n", rcNt));
+ if (!NT_SUCCESS(rcNt))
+ TagInfo.FileAttributes = TagInfo.ReparseTag = 0;
+ if (!(TagInfo.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT))
+ break;
+
+ NtClose(pDir->hDir);
+ pDir->hDir = RTNT_INVALID_HANDLE_VALUE;
+
+ if (TagInfo.ReparseTag == IO_REPARSE_TAG_SYMLINK)
+ {
+ rc = VERR_IS_A_SYMLINK;
+ break;
+ }
+
+ /* Reparse point that isn't a symbolic link, try follow the reparsing. */
+ }
+ else if (fReparsePoints == -1)
+ g_fReparsePoints = fReparsePoints = 0;
+ fOptions &= ~FILE_OPEN_REPARSE_POINT;
+ }
+
+ }
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Init data.
+ */
+ pDir->fDataUnread = false; /* spelling it out */
+ pDir->uDirDev = 0;
+#ifdef IPRT_WITH_NT_PATH_PASSTHRU
+ if (fObjDir)
+ pDir->enmInfoClass = FileMaximumInformation; /* object directory. */
+#endif
+ }
+ return rc;
+}
+
+
+RTDECL(int) RTDirClose(RTDIR hDir)
+{
+ PRTDIRINTERNAL pDir = hDir;
+
+ /*
+ * Validate input.
+ */
+ if (!pDir)
+ return VERR_INVALID_PARAMETER;
+ if (pDir->u32Magic != RTDIR_MAGIC)
+ {
+ AssertMsgFailed(("Invalid pDir=%p\n", pDir));
+ return VERR_INVALID_PARAMETER;
+ }
+
+ /*
+ * Close the handle.
+ */
+ pDir->u32Magic = ~RTDIR_MAGIC;
+ if (pDir->hDir != RTNT_INVALID_HANDLE_VALUE)
+ {
+ int rc = RTNtPathClose(pDir->hDir);
+ AssertRC(rc);
+ pDir->hDir = RTNT_INVALID_HANDLE_VALUE;
+ }
+ RTStrFree(pDir->pszName);
+ pDir->pszName = NULL;
+ RTUtf16Free(pDir->NtFilterStr.Buffer);
+ pDir->NtFilterStr.Buffer = NULL;
+ RTMemFree(pDir->pabBuffer);
+ pDir->pabBuffer = NULL;
+ RTMemFree(pDir);
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Checks the validity of the current record.
+ *
+ * @returns IPRT status code
+ * @param pThis The directory instance data.
+ */
+static int rtDirNtCheckRecord(PRTDIRINTERNAL pThis)
+{
+#if defined(RTDIR_NT_STRICT) || defined(RT_ARCH_X86)
+# ifdef IPRT_WITH_NT_PATH_PASSTHRU
+ if (pThis->enmInfoClass != FileMaximumInformation)
+# endif
+ {
+ uintptr_t uEndAddr;
+ if (pThis->enmInfoClass == FileIdBothDirectoryInformation)
+ uEndAddr = (uintptr_t)&pThis->uCurData.pBothId->FileName[0];
+ else
+ uEndAddr = (uintptr_t)&pThis->uCurData.pBoth->FileName[0];
+
+# ifdef RT_ARCH_X86
+ /* Workaround for NT 3.1 bug where FAT returns a too short buffer length.
+ Including all NT 3.x versions in case it bug was fixed till NT 4. */
+ uintptr_t const uEndBuffer = (uintptr_t)&pThis->pabBuffer[pThis->cbBuffer];
+ if ( uEndAddr < uEndBuffer
+ && uEndAddr + pThis->uCurData.pBoth->FileNameLength <= uEndBuffer)
+ { /* likely */ }
+ else if ( ( g_enmWinVer == kRTWinOSType_NT310
+ || g_enmWinVer == kRTWinOSType_NT350 // not sure when it was fixed...
+ || g_enmWinVer == kRTWinOSType_NT351)
+ && pThis->enmInfoClass == FileBothDirectoryInformation)
+ {
+ size_t cbLeft = (uintptr_t)&pThis->pabBuffer[pThis->cbBufferAlloc] - (uintptr_t)pThis->uCurData.pBoth;
+ if ( cbLeft >= RT_UOFFSETOF(FILE_BOTH_DIR_INFORMATION, FileName)
+ && pThis->uCurData.pBoth->FileNameLength > 0
+ && cbLeft >= RT_UOFFSETOF(FILE_BOTH_DIR_INFORMATION, FileName) + pThis->uCurData.pBoth->FileNameLength)
+ {
+ pThis->cbBuffer = ((uintptr_t)&pThis->uCurData.pBoth->FileName[0] + pThis->uCurData.pBoth->FileNameLength)
+ - (uintptr_t)&pThis->pabBuffer[0];
+ }
+ }
+# endif
+
+# ifdef RTDIR_NT_STRICT
+ AssertReturn(uEndAddr < (uintptr_t)&pThis->pabBuffer[pThis->cbBuffer], VERR_IO_GEN_FAILURE);
+ AssertReturn(pThis->uCurData.pBoth->FileNameLength < _64K, VERR_FILENAME_TOO_LONG);
+ AssertReturn((pThis->uCurData.pBoth->FileNameLength & 1) == 0, VERR_IO_GEN_FAILURE);
+
+ uEndAddr += pThis->uCurData.pBoth->FileNameLength;
+ AssertReturn(uEndAddr <= (uintptr_t)&pThis->pabBuffer[pThis->cbBuffer], VERR_IO_GEN_FAILURE);
+
+ AssertReturn((unsigned)pThis->uCurData.pBoth->ShortNameLength <= sizeof(pThis->uCurData.pBoth->ShortName),
+ VERR_IO_GEN_FAILURE);
+# endif
+ }
+#else
+ RT_NOREF_PV(pThis);
+#endif
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Advances the buffer pointer.
+ *
+ * @param pThis The directory instance data.
+ */
+static int rtDirNtAdvanceBuffer(PRTDIRINTERNAL pThis)
+{
+ int rc;
+
+#ifdef IPRT_WITH_NT_PATH_PASSTHRU
+ if (pThis->enmInfoClass == FileMaximumInformation)
+ {
+ pThis->uCurData.pObjDir++;
+ pThis->fDataUnread = pThis->uCurData.pObjDir->Name.Length != 0;
+ return VINF_SUCCESS;
+ }
+#endif
+
+ pThis->fDataUnread = false;
+
+ uint32_t const offNext = pThis->uCurData.pBoth->NextEntryOffset;
+ if (offNext == 0)
+ return VINF_SUCCESS;
+
+#ifdef RTDIR_NT_STRICT
+ /* Make sure the next-record offset is beyond the current record. */
+ size_t cbRec;
+ if (pThis->enmInfoClass == FileIdBothDirectoryInformation)
+ cbRec = RT_UOFFSETOF(FILE_ID_BOTH_DIR_INFORMATION, FileName);
+ else
+ cbRec = RT_UOFFSETOF(FILE_BOTH_DIR_INFORMATION, FileName);
+ cbRec += pThis->uCurData.pBoth->FileNameLength;
+ AssertReturn(offNext >= cbRec, VERR_IO_GEN_FAILURE);
+#endif
+ pThis->uCurData.u += offNext;
+
+ rc = rtDirNtCheckRecord(pThis);
+ pThis->fDataUnread = RT_SUCCESS(rc);
+ return rc;
+}
+
+
+/**
+ * Fetches more data from the file system.
+ *
+ * @returns IPRT status code
+ * @param pThis The directory instance data.
+ */
+static int rtDirNtFetchMore(PRTDIRINTERNAL pThis)
+{
+ Assert(!pThis->fDataUnread);
+
+ /*
+ * Allocate the buffer the first time around.
+ * We do this in lazy fashion as some users of RTDirOpen will not actually
+ * list any files, just open it for various reasons.
+ *
+ * We also reduce the buffer size for networked devices as the windows 7-8.1,
+ * server 2012, ++ CIFS servers or/and IFSes screws up buffers larger than 64KB.
+ * There is an alternative hack below, btw. We'll leave both in for now.
+ */
+ bool fFirst = false;
+ if (!pThis->pabBuffer)
+ {
+ pThis->cbBufferAlloc = _256K;
+ if (true) /** @todo skip for known local devices, like the boot device? */
+ {
+ IO_STATUS_BLOCK Ios2 = RTNT_IO_STATUS_BLOCK_INITIALIZER;
+ FILE_FS_DEVICE_INFORMATION Info = { 0, 0 };
+ NTSTATUS rcNt2 = NtQueryVolumeInformationFile(pThis->hDir, &Ios2, &Info, sizeof(Info), FileFsDeviceInformation);
+ if ( !NT_SUCCESS(rcNt2)
+ || (Info.Characteristics & FILE_REMOTE_DEVICE)
+ || Info.DeviceType == FILE_DEVICE_NETWORK
+ || Info.DeviceType == FILE_DEVICE_NETWORK_FILE_SYSTEM
+ || Info.DeviceType == FILE_DEVICE_NETWORK_REDIRECTOR
+ || Info.DeviceType == FILE_DEVICE_SMB)
+ pThis->cbBufferAlloc = _64K;
+ }
+
+ fFirst = false;
+ pThis->pabBuffer = (uint8_t *)RTMemAlloc(pThis->cbBufferAlloc);
+ if (!pThis->pabBuffer)
+ {
+ do
+ {
+ pThis->cbBufferAlloc /= 4;
+ pThis->pabBuffer = (uint8_t *)RTMemAlloc(pThis->cbBufferAlloc);
+ } while (pThis->pabBuffer == NULL && pThis->cbBufferAlloc > _4K);
+ if (!pThis->pabBuffer)
+ return VERR_NO_MEMORY;
+ }
+
+ /*
+ * Also try determining the device number.
+ */
+ PFILE_FS_VOLUME_INFORMATION pVolInfo = (PFILE_FS_VOLUME_INFORMATION)pThis->pabBuffer;
+ pVolInfo->VolumeSerialNumber = 0;
+ IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
+ NTSTATUS rcNt = NtQueryVolumeInformationFile(pThis->hDir, &Ios,
+ pVolInfo, RT_MIN(_2K, pThis->cbBufferAlloc),
+ FileFsVolumeInformation);
+ if (NT_SUCCESS(rcNt) && NT_SUCCESS(Ios.Status))
+ pThis->uDirDev = pVolInfo->VolumeSerialNumber;
+ else
+ pThis->uDirDev = 0;
+ AssertCompile(sizeof(pThis->uDirDev) == sizeof(pVolInfo->VolumeSerialNumber));
+ /** @todo Grow RTDEV to 64-bit and add low dword of VolumeCreationTime to the top of uDirDev. */
+ }
+
+ /*
+ * Read more.
+ */
+ NTSTATUS rcNt;
+ IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
+ if (pThis->enmInfoClass != (FILE_INFORMATION_CLASS)0)
+ {
+#ifdef IPRT_WITH_NT_PATH_PASSTHRU
+ if (pThis->enmInfoClass == FileMaximumInformation)
+ {
+ Ios.Information = 0;
+ Ios.Status = rcNt = NtQueryDirectoryObject(pThis->hDir,
+ pThis->pabBuffer,
+ pThis->cbBufferAlloc,
+ RTDIR_NT_SINGLE_RECORD /*ReturnSingleEntry */,
+ pThis->fRestartScan,
+ &pThis->uObjDirCtx,
+ (PULONG)&Ios.Information);
+ }
+ else
+#endif
+ rcNt = NtQueryDirectoryFile(pThis->hDir,
+ NULL /* Event */,
+ NULL /* ApcRoutine */,
+ NULL /* ApcContext */,
+ &Ios,
+ pThis->pabBuffer,
+ pThis->cbBufferAlloc,
+ pThis->enmInfoClass,
+ RTDIR_NT_SINGLE_RECORD /*ReturnSingleEntry */,
+ pThis->pNtFilterStr,
+ pThis->fRestartScan);
+ }
+ else
+ {
+ /*
+ * The first time around we have to figure which info class we can use
+ * as well as the right buffer size. We prefer an info class which
+ * gives us file IDs (Vista+ IIRC) and we prefer large buffers (for long
+ * ReFS file names and such), but we'll settle for whatever works...
+ *
+ * The windows 7 thru 8.1 CIFS servers have been observed to have
+ * trouble with large buffers, but weirdly only when listing large
+ * directories. Seems 0x10000 is the max. (Samba does not exhibit
+ * these problems, of course.)
+ *
+ * This complicates things. The buffer size issues causes an
+ * STATUS_INVALID_PARAMETER error. Now, you would expect the lack of
+ * FileIdBothDirectoryInformation support to return
+ * STATUS_INVALID_INFO_CLASS, but I'm not entirely sure if we can 100%
+ * depend on third IFSs to get that right. Nor, am I entirely confident
+ * that we can depend on them to check the class before the buffer size.
+ *
+ * Thus the mess.
+ */
+ if (RT_MAKE_U64(RTNtCurrentPeb()->OSMinorVersion, RTNtCurrentPeb()->OSMajorVersion) > RT_MAKE_U64(0,5) /* > W2K */)
+ pThis->enmInfoClass = FileIdBothDirectoryInformation; /* Introduced in XP, from I can tell. */
+ else
+ pThis->enmInfoClass = FileBothDirectoryInformation;
+ rcNt = NtQueryDirectoryFile(pThis->hDir,
+ NULL /* Event */,
+ NULL /* ApcRoutine */,
+ NULL /* ApcContext */,
+ &Ios,
+ pThis->pabBuffer,
+ pThis->cbBufferAlloc,
+ pThis->enmInfoClass,
+ RTDIR_NT_SINGLE_RECORD /*ReturnSingleEntry */,
+ pThis->pNtFilterStr,
+ pThis->fRestartScan);
+ if (NT_SUCCESS(rcNt))
+ { /* likely */ }
+ else
+ {
+ bool fRestartScan = pThis->fRestartScan;
+ for (unsigned iRetry = 0; iRetry < 2; iRetry++)
+ {
+ if ( rcNt == STATUS_INVALID_INFO_CLASS
+ || rcNt == STATUS_INVALID_PARAMETER_8
+ || iRetry != 0)
+ pThis->enmInfoClass = FileBothDirectoryInformation;
+
+ uint32_t cbBuffer = pThis->cbBufferAlloc;
+ if ( rcNt == STATUS_INVALID_PARAMETER
+ || rcNt == STATUS_INVALID_PARAMETER_7
+ || rcNt == STATUS_INVALID_NETWORK_RESPONSE
+ || iRetry != 0)
+ {
+ cbBuffer = RT_MIN(cbBuffer / 2, _64K);
+ fRestartScan = true;
+ }
+
+ for (;;)
+ {
+ rcNt = NtQueryDirectoryFile(pThis->hDir,
+ NULL /* Event */,
+ NULL /* ApcRoutine */,
+ NULL /* ApcContext */,
+ &Ios,
+ pThis->pabBuffer,
+ cbBuffer,
+ pThis->enmInfoClass,
+ RTDIR_NT_SINGLE_RECORD /*ReturnSingleEntry */,
+ pThis->pNtFilterStr,
+ fRestartScan);
+ if ( NT_SUCCESS(rcNt)
+ || cbBuffer == pThis->cbBufferAlloc
+ || cbBuffer <= sizeof(*pThis->uCurData.pBothId) + sizeof(WCHAR) * 260)
+ break;
+
+ /* Reduce the buffer size agressivly and try again. We fall back to
+ FindFirstFile values for the final lap. This means we'll do 4 rounds
+ with the current initial buffer size (64KB, 8KB, 1KB, 0x278/0x268). */
+ cbBuffer /= 8;
+ if (cbBuffer < 1024)
+ cbBuffer = pThis->enmInfoClass == FileIdBothDirectoryInformation
+ ? sizeof(*pThis->uCurData.pBothId) + sizeof(WCHAR) * 260
+ : sizeof(*pThis->uCurData.pBoth) + sizeof(WCHAR) * 260;
+ }
+ if (NT_SUCCESS(rcNt))
+ {
+ pThis->cbBufferAlloc = cbBuffer;
+ break;
+ }
+ }
+ }
+ }
+ if (!NT_SUCCESS(rcNt))
+ {
+ /* Note! VBoxSVR and CIFS file systems both ends up with STATUS_NO_SUCH_FILE here instead of STATUS_NO_MORE_FILES. */
+ if (rcNt == STATUS_NO_MORE_FILES || rcNt == STATUS_NO_MORE_ENTRIES || rcNt == STATUS_NO_SUCH_FILE)
+ return VERR_NO_MORE_FILES;
+ return RTErrConvertFromNtStatus(rcNt);
+ }
+ pThis->fRestartScan = false;
+ AssertMsg( Ios.Information
+ > (pThis->enmInfoClass == FileMaximumInformation ? sizeof(*pThis->uCurData.pObjDir) : sizeof(*pThis->uCurData.pBoth)),
+ ("Ios.Information=%#x\n", Ios.Information));
+
+ /*
+ * Set up the data members.
+ */
+ pThis->uCurData.u = (uintptr_t)pThis->pabBuffer;
+ pThis->cbBuffer = Ios.Information;
+
+ int rc = rtDirNtCheckRecord(pThis);
+ pThis->fDataUnread = RT_SUCCESS(rc);
+
+ return rc;
+}
+
+
+/**
+ * Converts the name from UTF-16 to UTF-8.
+ *
+ * Fortunately, the names are relative to the directory, so we won't have to do
+ * any sweaty path style coversion. :-)
+ *
+ * @returns IPRT status code
+ * @param pThis The directory instance data.
+ * @param cbName The file name length in bytes.
+ * @param pwsName The file name, not terminated.
+ */
+static int rtDirNtConvertName(PRTDIRINTERNAL pThis, uint32_t cbName, PCRTUTF16 pwsName)
+{
+ int rc = RTUtf16ToUtf8Ex(pwsName, cbName / 2, &pThis->pszName, pThis->cbNameAlloc, &pThis->cchName);
+ if (RT_SUCCESS(rc))
+ {
+ if (!pThis->cbNameAlloc)
+ pThis->cbNameAlloc = pThis->cchName + 1;
+ }
+ else if (rc == VERR_BUFFER_OVERFLOW)
+ {
+ RTStrFree(pThis->pszName);
+ pThis->pszName = NULL;
+ pThis->cbNameAlloc = 0;
+
+ rc = RTUtf16ToUtf8Ex(pwsName, cbName / 2, &pThis->pszName, pThis->cbNameAlloc, &pThis->cchName);
+ if (RT_SUCCESS(rc))
+ pThis->cbNameAlloc = pThis->cchName + 1;
+ }
+ Assert(RT_SUCCESS(rc) ? pThis->pszName != NULL : pThis->pszName == NULL);
+ return rc;
+}
+
+
+/**
+ * Converts the name of the current record.
+ *
+ * @returns IPRT status code.
+ * @param pThis The directory instance data.
+ */
+static int rtDirNtConvertCurName(PRTDIRINTERNAL pThis)
+{
+ switch (pThis->enmInfoClass)
+ {
+ case FileIdBothDirectoryInformation:
+ return rtDirNtConvertName(pThis, pThis->uCurData.pBothId->FileNameLength, pThis->uCurData.pBothId->FileName);
+ case FileBothDirectoryInformation:
+ return rtDirNtConvertName(pThis, pThis->uCurData.pBoth->FileNameLength, pThis->uCurData.pBoth->FileName);
+#ifdef IPRT_WITH_NT_PATH_PASSTHRU
+ case FileMaximumInformation:
+ return rtDirNtConvertName(pThis, pThis->uCurData.pObjDir->Name.Length, pThis->uCurData.pObjDir->Name.Buffer);
+#endif
+
+ default:
+ AssertFailedReturn(VERR_INTERNAL_ERROR_3);
+ }
+}
+
+
+RTDECL(int) RTDirRead(RTDIR hDir, PRTDIRENTRY pDirEntry, size_t *pcbDirEntry)
+{
+ PRTDIRINTERNAL pDir = hDir;
+ int rc;
+
+ /*
+ * Validate input.
+ */
+ AssertPtrReturn(pDir, VERR_INVALID_POINTER);
+ AssertReturn(pDir->u32Magic == RTDIR_MAGIC, VERR_INVALID_HANDLE);
+ AssertPtrReturn(pDirEntry, VERR_INVALID_POINTER);
+ size_t cbDirEntry = sizeof(*pDirEntry);
+ if (pcbDirEntry)
+ {
+ cbDirEntry = *pcbDirEntry;
+ AssertMsgReturn(cbDirEntry >= RT_UOFFSETOF(RTDIRENTRY, szName[2]),
+ ("Invalid *pcbDirEntry=%zu (min %zu)\n", *pcbDirEntry, RT_UOFFSETOF(RTDIRENTRY, szName[2])),
+ VERR_INVALID_PARAMETER);
+ }
+
+ /*
+ * Fetch data?
+ */
+ if (!pDir->fDataUnread)
+ {
+ rc = rtDirNtFetchMore(pDir);
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+
+ /*
+ * Convert the filename to UTF-8.
+ */
+ rc = rtDirNtConvertCurName(pDir);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * Check if we've got enough space to return the data.
+ */
+ const char *pszName = pDir->pszName;
+ const size_t cchName = pDir->cchName;
+ const size_t cbRequired = RT_UOFFSETOF(RTDIRENTRY, szName[1]) + cchName;
+ if (pcbDirEntry)
+ *pcbDirEntry = cbRequired;
+ if (cbRequired > cbDirEntry)
+ return VERR_BUFFER_OVERFLOW;
+
+ /*
+ * Setup the returned data.
+ */
+ pDirEntry->cbName = (uint16_t)cchName; Assert(pDirEntry->cbName == cchName);
+ memcpy(pDirEntry->szName, pszName, cchName + 1);
+
+ pDirEntry->INodeId = pDir->enmInfoClass == FileIdBothDirectoryInformation
+ ? pDir->uCurData.pBothId->FileId.QuadPart : 0;
+
+#ifdef IPRT_WITH_NT_PATH_PASSTHRU
+ if (pDir->enmInfoClass != FileMaximumInformation)
+#endif
+ {
+ switch ( pDir->uCurData.pBoth->FileAttributes
+ & (FILE_ATTRIBUTE_REPARSE_POINT | FILE_ATTRIBUTE_REPARSE_POINT | FILE_ATTRIBUTE_DIRECTORY))
+ {
+ default:
+ AssertFailed();
+ case 0:
+ pDirEntry->enmType = RTDIRENTRYTYPE_FILE;
+ break;
+
+ case FILE_ATTRIBUTE_DIRECTORY:
+ pDirEntry->enmType = RTDIRENTRYTYPE_DIRECTORY;
+ break;
+
+ case FILE_ATTRIBUTE_REPARSE_POINT:
+ case FILE_ATTRIBUTE_REPARSE_POINT | FILE_ATTRIBUTE_DIRECTORY:
+ /* EaSize is here reused for returning the repharse tag value. */
+ if (pDir->uCurData.pBoth->EaSize == IO_REPARSE_TAG_SYMLINK)
+ pDirEntry->enmType = RTDIRENTRYTYPE_SYMLINK;
+ break;
+ }
+ }
+#ifdef IPRT_WITH_NT_PATH_PASSTHRU
+ else
+ {
+ pDirEntry->enmType = RTDIRENTRYTYPE_UNKNOWN;
+ if (rtNtCompWideStrAndAscii(pDir->uCurData.pObjDir->TypeName.Buffer, pDir->uCurData.pObjDir->TypeName.Length,
+ RT_STR_TUPLE("Directory")))
+ pDirEntry->enmType = RTDIRENTRYTYPE_DIRECTORY;
+ else if (rtNtCompWideStrAndAscii(pDir->uCurData.pObjDir->TypeName.Buffer, pDir->uCurData.pObjDir->TypeName.Length,
+ RT_STR_TUPLE("SymbolicLink")))
+ pDirEntry->enmType = RTDIRENTRYTYPE_SYMLINK;
+ }
+#endif
+
+ return rtDirNtAdvanceBuffer(pDir);
+}
+
+
+RTDECL(int) RTDirReadEx(RTDIR hDir, PRTDIRENTRYEX pDirEntry, size_t *pcbDirEntry,
+ RTFSOBJATTRADD enmAdditionalAttribs, uint32_t fFlags)
+{
+ PRTDIRINTERNAL pDir = hDir;
+ int rc;
+
+ /*
+ * Validate input.
+ */
+ AssertPtrReturn(pDir, VERR_INVALID_POINTER);
+ AssertReturn(pDir->u32Magic == RTDIR_MAGIC, VERR_INVALID_HANDLE);
+ AssertPtrReturn(pDirEntry, VERR_INVALID_POINTER);
+
+ AssertReturn(enmAdditionalAttribs >= RTFSOBJATTRADD_NOTHING && enmAdditionalAttribs <= RTFSOBJATTRADD_LAST,
+ VERR_INVALID_PARAMETER);
+ AssertMsgReturn(RTPATH_F_IS_VALID(fFlags, 0), ("%#x\n", fFlags), VERR_INVALID_PARAMETER);
+
+ size_t cbDirEntry = sizeof(*pDirEntry);
+ if (pcbDirEntry)
+ {
+ cbDirEntry = *pcbDirEntry;
+ AssertMsgReturn(cbDirEntry >= RT_UOFFSETOF(RTDIRENTRYEX, szName[2]),
+ ("Invalid *pcbDirEntry=%zu (min %zu)\n", *pcbDirEntry, RT_UOFFSETOF(RTDIRENTRYEX, szName[2])),
+ VERR_INVALID_PARAMETER);
+ }
+
+ /*
+ * Fetch data?
+ */
+ if (!pDir->fDataUnread)
+ {
+ rc = rtDirNtFetchMore(pDir);
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+
+ /*
+ * Convert the filename to UTF-8.
+ */
+ rc = rtDirNtConvertCurName(pDir);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * Check if we've got enough space to return the data.
+ */
+ const char *pszName = pDir->pszName;
+ const size_t cchName = pDir->cchName;
+ const size_t cbRequired = RT_UOFFSETOF(RTDIRENTRYEX, szName[1]) + cchName;
+ if (pcbDirEntry)
+ *pcbDirEntry = cbRequired;
+ if (cbRequired > cbDirEntry)
+ return VERR_BUFFER_OVERFLOW;
+
+ /*
+ * Setup the returned data.
+ */
+ PFILE_BOTH_DIR_INFORMATION pBoth = pDir->uCurData.pBoth;
+
+ pDirEntry->cbName = (uint16_t)cchName; Assert(pDirEntry->cbName == cchName);
+ memcpy(pDirEntry->szName, pszName, cchName + 1);
+ memset(pDirEntry->wszShortName, 0, sizeof(pDirEntry->wszShortName));
+#ifdef IPRT_WITH_NT_PATH_PASSTHRU
+ if (pDir->enmInfoClass != FileMaximumInformation)
+#endif
+ {
+ uint8_t cbShort = pBoth->ShortNameLength;
+ if (cbShort > 0)
+ {
+ AssertStmt(cbShort < sizeof(pDirEntry->wszShortName), cbShort = sizeof(pDirEntry->wszShortName) - 2);
+ memcpy(pDirEntry->wszShortName, pBoth->ShortName, cbShort);
+ pDirEntry->cwcShortName = cbShort / 2;
+ }
+ else
+ pDirEntry->cwcShortName = 0;
+
+ pDirEntry->Info.cbObject = pBoth->EndOfFile.QuadPart;
+ pDirEntry->Info.cbAllocated = pBoth->AllocationSize.QuadPart;
+
+ Assert(sizeof(uint64_t) == sizeof(pBoth->CreationTime));
+ RTTimeSpecSetNtTime(&pDirEntry->Info.BirthTime, pBoth->CreationTime.QuadPart);
+ RTTimeSpecSetNtTime(&pDirEntry->Info.AccessTime, pBoth->LastAccessTime.QuadPart);
+ RTTimeSpecSetNtTime(&pDirEntry->Info.ModificationTime, pBoth->LastWriteTime.QuadPart);
+ RTTimeSpecSetNtTime(&pDirEntry->Info.ChangeTime, pBoth->ChangeTime.QuadPart);
+
+ pDirEntry->Info.Attr.fMode = rtFsModeFromDos((pBoth->FileAttributes << RTFS_DOS_SHIFT) & RTFS_DOS_MASK_NT,
+ pszName, cchName, pBoth->EaSize, 0);
+ }
+#ifdef IPRT_WITH_NT_PATH_PASSTHRU
+ else
+ {
+ pDirEntry->cwcShortName = 0;
+ pDirEntry->Info.cbObject = 0;
+ pDirEntry->Info.cbAllocated = 0;
+ RTTimeSpecSetNtTime(&pDirEntry->Info.BirthTime, 0);
+ RTTimeSpecSetNtTime(&pDirEntry->Info.AccessTime, 0);
+ RTTimeSpecSetNtTime(&pDirEntry->Info.ModificationTime, 0);
+ RTTimeSpecSetNtTime(&pDirEntry->Info.ChangeTime, 0);
+
+ if (rtNtCompWideStrAndAscii(pDir->uCurData.pObjDir->TypeName.Buffer, pDir->uCurData.pObjDir->TypeName.Length,
+ RT_STR_TUPLE("Directory")))
+ pDirEntry->Info.Attr.fMode = RTFS_DOS_DIRECTORY | RTFS_TYPE_DIRECTORY | 0777;
+ else if (rtNtCompWideStrAndAscii(pDir->uCurData.pObjDir->TypeName.Buffer, pDir->uCurData.pObjDir->TypeName.Length,
+ RT_STR_TUPLE("SymbolicLink")))
+ pDirEntry->Info.Attr.fMode = RTFS_DOS_NT_REPARSE_POINT | RTFS_TYPE_SYMLINK | 0777;
+ else if (rtNtCompWideStrAndAscii(pDir->uCurData.pObjDir->TypeName.Buffer, pDir->uCurData.pObjDir->TypeName.Length,
+ RT_STR_TUPLE("Device")))
+ pDirEntry->Info.Attr.fMode = RTFS_DOS_NT_DEVICE | RTFS_TYPE_DEV_CHAR | 0666;
+ else
+ pDirEntry->Info.Attr.fMode = RTFS_DOS_NT_NORMAL | RTFS_TYPE_FILE | 0666;
+ }
+#endif
+
+ /*
+ * Requested attributes (we cannot provide anything actually).
+ */
+ switch (enmAdditionalAttribs)
+ {
+ case RTFSOBJATTRADD_EASIZE:
+ pDirEntry->Info.Attr.enmAdditional = RTFSOBJATTRADD_EASIZE;
+#ifdef IPRT_WITH_NT_PATH_PASSTHRU
+ if (pDir->enmInfoClass == FileMaximumInformation)
+ pDirEntry->Info.Attr.u.EASize.cb = 0;
+ else
+#endif
+ pDirEntry->Info.Attr.u.EASize.cb = pBoth->EaSize;
+ break;
+
+ case RTFSOBJATTRADD_UNIX:
+ pDirEntry->Info.Attr.enmAdditional = RTFSOBJATTRADD_UNIX;
+ pDirEntry->Info.Attr.u.Unix.uid = NIL_RTUID;
+ pDirEntry->Info.Attr.u.Unix.gid = NIL_RTGID;
+ pDirEntry->Info.Attr.u.Unix.cHardlinks = 1;
+ pDirEntry->Info.Attr.u.Unix.INodeIdDevice = pDir->uDirDev;
+ pDirEntry->Info.Attr.u.Unix.INodeId = 0;
+ if ( pDir->enmInfoClass == FileIdBothDirectoryInformation
+ && pDir->uCurData.pBothId->FileId.QuadPart != UINT64_MAX)
+ pDirEntry->Info.Attr.u.Unix.INodeId = pDir->uCurData.pBothId->FileId.QuadPart;
+ pDirEntry->Info.Attr.u.Unix.fFlags = 0;
+ pDirEntry->Info.Attr.u.Unix.GenerationId = 0;
+ pDirEntry->Info.Attr.u.Unix.Device = 0;
+ break;
+
+ case RTFSOBJATTRADD_NOTHING:
+ pDirEntry->Info.Attr.enmAdditional = RTFSOBJATTRADD_NOTHING;
+ break;
+
+ case RTFSOBJATTRADD_UNIX_OWNER:
+ pDirEntry->Info.Attr.enmAdditional = RTFSOBJATTRADD_UNIX_OWNER;
+ pDirEntry->Info.Attr.u.UnixOwner.uid = NIL_RTUID;
+ pDirEntry->Info.Attr.u.UnixOwner.szName[0] = '\0'; /** @todo return something sensible here. */
+ break;
+
+ case RTFSOBJATTRADD_UNIX_GROUP:
+ pDirEntry->Info.Attr.enmAdditional = RTFSOBJATTRADD_UNIX_GROUP;
+ pDirEntry->Info.Attr.u.UnixGroup.gid = NIL_RTGID;
+ pDirEntry->Info.Attr.u.UnixGroup.szName[0] = '\0';
+ break;
+
+ default:
+ AssertMsgFailed(("Impossible!\n"));
+ return VERR_INTERNAL_ERROR;
+ }
+
+ /*
+ * Follow links if requested.
+ */
+ if ( (fFlags & RTPATH_F_FOLLOW_LINK)
+ && RTFS_IS_SYMLINK(fFlags))
+ {
+ /** @todo Symlinks: Find[First|Next]FileW will return info about
+ the link, so RTPATH_F_FOLLOW_LINK is not handled correctly. */
+ }
+
+ /*
+ * Finally advance the buffer.
+ */
+ return rtDirNtAdvanceBuffer(pDir);
+}
+
+
+RTDECL(int) RTDirRewind(RTDIR hDir)
+{
+ /*
+ * Validate and digest input.
+ */
+ PRTDIRINTERNAL pThis = hDir;
+ AssertPtrReturn(pThis, VERR_INVALID_POINTER);
+ AssertReturn(pThis->u32Magic == RTDIR_MAGIC, VERR_INVALID_HANDLE);
+
+ /*
+ * The work is done on the next call to rtDirNtFetchMore.
+ */
+ pThis->fRestartScan = true;
+ pThis->fDataUnread = false;
+
+ return VINF_SUCCESS;
+}
+
+
+RTR3DECL(int) RTDirQueryInfo(RTDIR hDir, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAdditionalAttribs)
+{
+ PRTDIRINTERNAL pDir = hDir;
+ AssertPtrReturn(pDir, VERR_INVALID_POINTER);
+ AssertReturn(pDir->u32Magic == RTDIR_MAGIC, VERR_INVALID_HANDLE);
+ AssertReturn(enmAdditionalAttribs >= RTFSOBJATTRADD_NOTHING && enmAdditionalAttribs <= RTFSOBJATTRADD_LAST,
+ VERR_INVALID_PARAMETER);
+
+ if (pDir->enmInfoClass == FileMaximumInformation)
+ {
+ /*
+ * Directory object (see similar code above and rtPathNtQueryInfoInDirectoryObject).
+ */
+ pObjInfo->cbObject = 0;
+ pObjInfo->cbAllocated = 0;
+ RTTimeSpecSetNtTime(&pObjInfo->BirthTime, 0);
+ RTTimeSpecSetNtTime(&pObjInfo->AccessTime, 0);
+ RTTimeSpecSetNtTime(&pObjInfo->ModificationTime, 0);
+ RTTimeSpecSetNtTime(&pObjInfo->ChangeTime, 0);
+ pObjInfo->Attr.fMode = RTFS_DOS_DIRECTORY | RTFS_TYPE_DIRECTORY | 0777;
+ pObjInfo->Attr.enmAdditional = enmAdditionalAttribs;
+ switch (enmAdditionalAttribs)
+ {
+ case RTFSOBJATTRADD_NOTHING:
+ case RTFSOBJATTRADD_UNIX:
+ pObjInfo->Attr.u.Unix.uid = NIL_RTUID;
+ pObjInfo->Attr.u.Unix.gid = NIL_RTGID;
+ pObjInfo->Attr.u.Unix.cHardlinks = 1;
+ pObjInfo->Attr.u.Unix.INodeIdDevice = pDir->uDirDev;
+ pObjInfo->Attr.u.Unix.INodeId = 0;
+ pObjInfo->Attr.u.Unix.fFlags = 0;
+ pObjInfo->Attr.u.Unix.GenerationId = 0;
+ pObjInfo->Attr.u.Unix.Device = 0;
+ break;
+
+ case RTFSOBJATTRADD_EASIZE:
+ pObjInfo->Attr.u.EASize.cb = 0;
+ break;
+
+ case RTFSOBJATTRADD_UNIX_OWNER:
+ pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_UNIX_OWNER;
+ pObjInfo->Attr.u.UnixOwner.uid = NIL_RTUID;
+ pObjInfo->Attr.u.UnixOwner.szName[0] = '\0'; /** @todo return something sensible here. */
+ break;
+
+ case RTFSOBJATTRADD_UNIX_GROUP:
+ pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_UNIX_GROUP;
+ pObjInfo->Attr.u.UnixGroup.gid = NIL_RTGID;
+ pObjInfo->Attr.u.UnixGroup.szName[0] = '\0';
+ break;
+
+ default:
+ AssertMsgFailed(("Impossible!\n"));
+ return VERR_INTERNAL_ERROR_2;
+ }
+ return VINF_SUCCESS;
+ }
+
+ /*
+ * Regular directory file.
+ */
+ uint8_t abBuf[_2K];
+ return rtPathNtQueryInfoFromHandle(pDir->hDir, abBuf, sizeof(abBuf), pObjInfo, enmAdditionalAttribs, "", 0);
+}
+
diff --git a/src/VBox/Runtime/r3/nt/dirrel-r3-nt.cpp b/src/VBox/Runtime/r3/nt/dirrel-r3-nt.cpp
new file mode 100644
index 00000000..a4130725
--- /dev/null
+++ b/src/VBox/Runtime/r3/nt/dirrel-r3-nt.cpp
@@ -0,0 +1,651 @@
+/* $Id: dirrel-r3-nt.cpp $ */
+/** @file
+ * IPRT - Directory relative base APIs, NT implementation
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_DIR
+#include <iprt/dir.h>
+#include "internal-r3-nt.h"
+
+#include <iprt/assert.h>
+#include <iprt/file.h>
+#include <iprt/err.h>
+#include <iprt/path.h>
+#include <iprt/string.h>
+#include <iprt/symlink.h>
+#include <iprt/utf16.h>
+#include "internal/dir.h"
+#include "internal/file.h"
+#include "internal/fs.h"
+#include "internal/path.h"
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** Getst the RTNTPATHRELATIVEASCENT value for RTNtPathRelativeFromUtf8. */
+#define RTDIRREL_NT_GET_ASCENT(a_pThis) \
+ ( !(pThis->fFlags & RTDIR_F_DENY_ASCENT) ? kRTNtPathRelativeAscent_Allow : kRTNtPathRelativeAscent_Fail )
+
+
+
+/**
+ * Helper that builds a full path for a directory relative path.
+ *
+ * @returns IPRT status code.
+ * @param pThis The directory.
+ * @param pszPathDst The destination buffer.
+ * @param cbPathDst The size of the destination buffer.
+ * @param pszRelPath The relative path.
+ */
+static int rtDirRelBuildFullPath(PRTDIRINTERNAL pThis, char *pszPathDst, size_t cbPathDst, const char *pszRelPath)
+{
+ AssertMsgReturn(!RTPathStartsWithRoot(pszRelPath), ("pszRelPath='%s'\n", pszRelPath), VERR_PATH_IS_NOT_RELATIVE);
+
+ /*
+ * Let's hope we can avoid checking for ascension.
+ *
+ * Note! We don't take symbolic links into account here. That can be
+ * done later if desired.
+ */
+ if ( !(pThis->fFlags & RTDIR_F_DENY_ASCENT)
+ || strstr(pszRelPath, "..") == NULL)
+ {
+ size_t const cchRelPath = strlen(pszRelPath);
+ size_t const cchDirPath = pThis->cchPath;
+ if (cchDirPath + cchRelPath < cbPathDst)
+ {
+ memcpy(pszPathDst, pThis->pszPath, cchDirPath);
+ memcpy(&pszPathDst[cchDirPath], pszRelPath, cchRelPath);
+ pszPathDst[cchDirPath + cchRelPath] = '\0';
+ return VINF_SUCCESS;
+ }
+ return VERR_FILENAME_TOO_LONG;
+ }
+
+ /*
+ * Calc the absolute path using the directory as a base, then check if the result
+ * still starts with the full directory path.
+ *
+ * This ASSUMES that pThis->pszPath is an absolute path.
+ */
+ int rc = RTPathAbsEx(pThis->pszPath, pszRelPath, RTPATH_STR_F_STYLE_HOST, pszPathDst, &cbPathDst);
+ if (RT_SUCCESS(rc))
+ {
+ if (RTPathStartsWith(pszPathDst, pThis->pszPath))
+ return VINF_SUCCESS;
+ return VERR_PATH_NOT_FOUND;
+ }
+ return rc;
+}
+
+
+/*
+ *
+ *
+ * RTFile stuff.
+ * RTFile stuff.
+ * RTFile stuff.
+ *
+ *
+ */
+
+
+RTDECL(int) RTDirRelFileOpen(RTDIR hDir, const char *pszRelFilename, uint64_t fOpen, PRTFILE phFile)
+{
+ PRTDIRINTERNAL pThis = hDir;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTDIR_MAGIC, VERR_INVALID_HANDLE);
+
+ /*
+ * Validate and convert flags.
+ */
+ uint32_t fDesiredAccess;
+ uint32_t fObjAttribs;
+ uint32_t fFileAttribs;
+ uint32_t fShareAccess;
+ uint32_t fCreateDisposition;
+ uint32_t fCreateOptions;
+ int rc = rtFileNtValidateAndConvertFlags(fOpen, &fDesiredAccess, &fObjAttribs, &fFileAttribs,
+ &fShareAccess, &fCreateDisposition, &fCreateOptions);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Convert and normalize the path.
+ */
+ UNICODE_STRING NtName;
+ HANDLE hRoot = pThis->hDir;
+ rc = RTNtPathRelativeFromUtf8(&NtName, &hRoot, pszRelFilename, RTDIRREL_NT_GET_ASCENT(pThis),
+ pThis->enmInfoClass == FileMaximumInformation);
+ if (RT_SUCCESS(rc))
+ {
+ HANDLE hFile = RTNT_INVALID_HANDLE_VALUE;
+ IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
+ OBJECT_ATTRIBUTES ObjAttr;
+ InitializeObjectAttributes(&ObjAttr, &NtName, fObjAttribs, hRoot, NULL /*pSecDesc*/);
+
+ NTSTATUS rcNt = NtCreateFile(&hFile,
+ fDesiredAccess,
+ &ObjAttr,
+ &Ios,
+ NULL /* AllocationSize*/,
+ fFileAttribs,
+ fShareAccess,
+ fCreateDisposition,
+ fCreateOptions,
+ NULL /*EaBuffer*/,
+ 0 /*EaLength*/);
+ if (NT_SUCCESS(rcNt))
+ {
+ rc = RTFileFromNative(phFile, (uintptr_t)hFile);
+ if (RT_FAILURE(rc))
+ NtClose(hFile);
+ }
+ else
+ rc = RTErrConvertFromNtStatus(rcNt);
+ RTNtPathFree(&NtName, NULL);
+ }
+ }
+ return rc;
+}
+
+
+
+/*
+ *
+ *
+ * RTDir stuff.
+ * RTDir stuff.
+ * RTDir stuff.
+ *
+ *
+ */
+
+
+/**
+ * Helper for cooking up a path string for rtDirOpenRelativeOrHandle.
+ *
+ * @returns IPRT status code.
+ * @param pszDst The destination buffer.
+ * @param cbDst The size of the destination buffer.
+ * @param pThis The directory this is relative to.
+ * @param pNtPath The NT path with a possibly relative path.
+ * @param fRelative Whether @a pNtPath is relative or not.
+ * @param pszPath The input path.
+ */
+static int rtDirRelJoinPathForDirOpen(char *pszDst, size_t cbDst, PRTDIRINTERNAL pThis,
+ PUNICODE_STRING pNtPath, bool fRelative, const char *pszPath)
+{
+ int rc;
+ if (fRelative)
+ {
+ size_t cchRel = 0;
+ rc = RTUtf16CalcUtf8LenEx(pNtPath->Buffer, pNtPath->Length / sizeof(RTUTF16), &cchRel);
+ AssertRC(rc);
+ if (RT_SUCCESS(rc))
+ {
+ if (pThis->cchPath + cchRel < cbDst)
+ {
+ size_t cchBase = pThis->cchPath;
+ memcpy(pszDst, pThis->pszPath, cchBase);
+ pszDst += cchBase;
+ cbDst -= cchBase;
+ rc = RTUtf16ToUtf8Ex(pNtPath->Buffer, pNtPath->Length / sizeof(RTUTF16), &pszDst, cbDst, NULL);
+ }
+ else
+ rc = VERR_FILENAME_TOO_LONG;
+ }
+ }
+ else
+ {
+ /** @todo would be better to convert pNtName to DOS/WIN path here,
+ * as it is absolute and doesn't need stuff resolved. */
+ rc = RTPathJoin(pszDst, cbDst, pThis->pszPath, pszPath);
+ }
+ return rc;
+}
+
+RTDECL(int) RTDirRelDirOpen(RTDIR hDir, const char *pszDir, RTDIR *phDir)
+{
+ return RTDirRelDirOpenFiltered(hDir, pszDir, RTDIRFILTER_NONE, 0 /*fFlags*/, phDir);
+}
+
+
+RTDECL(int) RTDirRelDirOpenFiltered(RTDIR hDir, const char *pszDirAndFilter, RTDIRFILTER enmFilter,
+ uint32_t fFlags, RTDIR *phDir)
+{
+ PRTDIRINTERNAL pThis = hDir;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTDIR_MAGIC, VERR_INVALID_HANDLE);
+
+ /*
+ * Convert and normalize the path.
+ */
+ UNICODE_STRING NtName;
+ HANDLE hRoot = pThis->hDir;
+ int rc = RTNtPathRelativeFromUtf8(&NtName, &hRoot, pszDirAndFilter, RTDIRREL_NT_GET_ASCENT(pThis),
+ pThis->enmInfoClass == FileMaximumInformation);
+ if (RT_SUCCESS(rc))
+ {
+ char szAbsDirAndFilter[RTPATH_MAX];
+ rc = rtDirRelJoinPathForDirOpen(szAbsDirAndFilter, sizeof(szAbsDirAndFilter), pThis,
+ &NtName, hRoot != NULL, pszDirAndFilter);
+ if (RT_SUCCESS(rc))
+ {
+ /* Drop the filter from the NT name. */
+ switch (enmFilter)
+ {
+ case RTDIRFILTER_NONE:
+ break;
+ case RTDIRFILTER_WINNT:
+ case RTDIRFILTER_UNIX:
+ case RTDIRFILTER_UNIX_UPCASED:
+ {
+ size_t cwc = NtName.Length / sizeof(RTUTF16);
+ while ( cwc > 0
+ && NtName.Buffer[cwc - 1] != '\\')
+ cwc--;
+ NtName.Buffer[cwc] = '\0';
+ NtName.Length = (uint16_t)(cwc * sizeof(RTUTF16));
+ break;
+ }
+ default:
+ AssertFailedBreak();
+ }
+
+ rc = rtDirOpenRelativeOrHandle(phDir, szAbsDirAndFilter, enmFilter, fFlags, (uintptr_t)hRoot, &NtName);
+ }
+ RTNtPathFree(&NtName, NULL);
+ }
+ return rc;
+}
+
+
+RTDECL(int) RTDirRelDirCreate(RTDIR hDir, const char *pszRelPath, RTFMODE fMode, uint32_t fCreate, RTDIR *phSubDir)
+{
+ PRTDIRINTERNAL pThis = hDir;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTDIR_MAGIC, VERR_INVALID_HANDLE);
+ AssertReturn(!(fCreate & ~RTDIRCREATE_FLAGS_VALID_MASK), VERR_INVALID_FLAGS);
+ fMode = rtFsModeNormalize(fMode, pszRelPath, 0, RTFS_TYPE_DIRECTORY);
+ AssertReturn(rtFsModeIsValidPermissions(fMode), VERR_INVALID_FMODE);
+ AssertPtrNullReturn(phSubDir, VERR_INVALID_POINTER);
+
+ /*
+ * Convert and normalize the path.
+ */
+ UNICODE_STRING NtName;
+ HANDLE hRoot = pThis->hDir;
+ int rc = RTNtPathRelativeFromUtf8(&NtName, &hRoot, pszRelPath, RTDIRREL_NT_GET_ASCENT(pThis),
+ pThis->enmInfoClass == FileMaximumInformation);
+ if (RT_SUCCESS(rc))
+ {
+ HANDLE hNewDir = RTNT_INVALID_HANDLE_VALUE;
+ IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
+ OBJECT_ATTRIBUTES ObjAttr;
+ InitializeObjectAttributes(&ObjAttr, &NtName, 0 /*fAttrib*/, hRoot, NULL);
+
+ ULONG fDirAttribs = (fCreate & RTFS_DOS_MASK_NT) >> RTFS_DOS_SHIFT;
+ if (!(fCreate & RTDIRCREATE_FLAGS_NOT_CONTENT_INDEXED_DONT_SET))
+ fDirAttribs |= FILE_ATTRIBUTE_NOT_CONTENT_INDEXED;
+ if (!fDirAttribs)
+ fDirAttribs = FILE_ATTRIBUTE_NORMAL;
+
+ NTSTATUS rcNt = NtCreateFile(&hNewDir,
+ phSubDir
+ ? FILE_WRITE_ATTRIBUTES | FILE_READ_ATTRIBUTES | FILE_LIST_DIRECTORY | FILE_TRAVERSE | SYNCHRONIZE
+ : SYNCHRONIZE,
+ &ObjAttr,
+ &Ios,
+ NULL /*AllocationSize*/,
+ fDirAttribs,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ FILE_CREATE,
+ FILE_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT | FILE_SYNCHRONOUS_IO_NONALERT,
+ NULL /*EaBuffer*/,
+ 0 /*EaLength*/);
+
+ /* Just in case someone takes offence at FILE_ATTRIBUTE_NOT_CONTENT_INDEXED. */
+ if ( ( rcNt == STATUS_INVALID_PARAMETER
+ || rcNt == STATUS_INVALID_PARAMETER_7)
+ && (fDirAttribs & FILE_ATTRIBUTE_NOT_CONTENT_INDEXED)
+ && (fCreate & RTDIRCREATE_FLAGS_NOT_CONTENT_INDEXED_NOT_CRITICAL) )
+ {
+ fDirAttribs &= ~FILE_ATTRIBUTE_NOT_CONTENT_INDEXED;
+ if (!fDirAttribs)
+ fDirAttribs = FILE_ATTRIBUTE_NORMAL;
+ rcNt = NtCreateFile(&hNewDir,
+ phSubDir
+ ? FILE_WRITE_ATTRIBUTES | FILE_READ_ATTRIBUTES | FILE_LIST_DIRECTORY | FILE_TRAVERSE | SYNCHRONIZE
+ : SYNCHRONIZE,
+ &ObjAttr,
+ &Ios,
+ NULL /*AllocationSize*/,
+ fDirAttribs,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ FILE_CREATE,
+ FILE_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT | FILE_SYNCHRONOUS_IO_NONALERT,
+ NULL /*EaBuffer*/,
+ 0 /*EaLength*/);
+ }
+
+ if (NT_SUCCESS(rcNt))
+ {
+ if (!phSubDir)
+ {
+ NtClose(hNewDir);
+ rc = VINF_SUCCESS;
+ }
+ else
+ {
+ char szAbsDirAndFilter[RTPATH_MAX];
+ rc = rtDirRelJoinPathForDirOpen(szAbsDirAndFilter, sizeof(szAbsDirAndFilter), pThis,
+ &NtName, hRoot != NULL, pszRelPath);
+ if (RT_SUCCESS(rc))
+ rc = rtDirOpenRelativeOrHandle(phSubDir, pszRelPath, RTDIRFILTER_NONE, 0 /*fFlags*/,
+ (uintptr_t)hNewDir, NULL /*pvNativeRelative*/);
+ if (RT_FAILURE(rc))
+ NtClose(hNewDir);
+ }
+ }
+ else
+ rc = RTErrConvertFromNtStatus(rcNt);
+ RTNtPathFree(&NtName, NULL);
+ }
+ return rc;
+}
+
+
+RTDECL(int) RTDirRelDirRemove(RTDIR hDir, const char *pszRelPath)
+{
+ PRTDIRINTERNAL pThis = hDir;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTDIR_MAGIC, VERR_INVALID_HANDLE);
+
+ /*
+ * Convert and normalize the path.
+ */
+ UNICODE_STRING NtName;
+ HANDLE hRoot = pThis->hDir;
+ int rc = RTNtPathRelativeFromUtf8(&NtName, &hRoot, pszRelPath, RTDIRREL_NT_GET_ASCENT(pThis),
+ pThis->enmInfoClass == FileMaximumInformation);
+ if (RT_SUCCESS(rc))
+ {
+ HANDLE hSubDir = RTNT_INVALID_HANDLE_VALUE;
+ IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
+ OBJECT_ATTRIBUTES ObjAttr;
+ InitializeObjectAttributes(&ObjAttr, &NtName, 0 /*fAttrib*/, hRoot, NULL);
+
+ NTSTATUS rcNt = NtCreateFile(&hSubDir,
+ DELETE | SYNCHRONIZE,
+ &ObjAttr,
+ &Ios,
+ NULL /*AllocationSize*/,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
+ FILE_OPEN,
+ FILE_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT | FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_REPARSE_POINT,
+ NULL /*EaBuffer*/,
+ 0 /*EaLength*/);
+ if (NT_SUCCESS(rcNt))
+ {
+ FILE_DISPOSITION_INFORMATION DispInfo;
+ DispInfo.DeleteFile = TRUE;
+ RTNT_IO_STATUS_BLOCK_REINIT(&Ios);
+ rcNt = NtSetInformationFile(hSubDir, &Ios, &DispInfo, sizeof(DispInfo), FileDispositionInformation);
+
+ NTSTATUS rcNt2 = NtClose(hSubDir);
+ if (!NT_SUCCESS(rcNt2) && NT_SUCCESS(rcNt))
+ rcNt = rcNt2;
+ }
+
+ if (NT_SUCCESS(rcNt))
+ rc = VINF_SUCCESS;
+ else
+ rc = RTErrConvertFromNtStatus(rcNt);
+
+ RTNtPathFree(&NtName, NULL);
+ }
+ return rc;
+}
+
+
+/*
+ *
+ * RTPath stuff.
+ * RTPath stuff.
+ * RTPath stuff.
+ *
+ *
+ */
+
+
+RTDECL(int) RTDirRelPathQueryInfo(RTDIR hDir, const char *pszRelPath, PRTFSOBJINFO pObjInfo,
+ RTFSOBJATTRADD enmAddAttr, uint32_t fFlags)
+{
+ PRTDIRINTERNAL pThis = hDir;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTDIR_MAGIC, VERR_INVALID_HANDLE);
+
+ /*
+ * Validate and convert flags.
+ */
+ UNICODE_STRING NtName;
+ HANDLE hRoot = pThis->hDir;
+ int rc = RTNtPathRelativeFromUtf8(&NtName, &hRoot, pszRelPath, RTDIRREL_NT_GET_ASCENT(pThis),
+ pThis->enmInfoClass == FileMaximumInformation);
+ if (RT_SUCCESS(rc))
+ {
+ if (NtName.Length != 0 || hRoot == NULL)
+ rc = rtPathNtQueryInfoWorker(hRoot, &NtName, pObjInfo, enmAddAttr, fFlags, pszRelPath);
+ else
+ rc = RTDirQueryInfo(hDir, pObjInfo, enmAddAttr);
+ RTNtPathFree(&NtName, NULL);
+ }
+ return rc;
+}
+
+
+RTDECL(int) RTDirRelPathSetMode(RTDIR hDir, const char *pszRelPath, RTFMODE fMode, uint32_t fFlags)
+{
+ PRTDIRINTERNAL pThis = hDir;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTDIR_MAGIC, VERR_INVALID_HANDLE);
+ fMode = rtFsModeNormalize(fMode, pszRelPath, 0, 0);
+ AssertReturn(rtFsModeIsValidPermissions(fMode), VERR_INVALID_FMODE);
+ AssertMsgReturn(RTPATH_F_IS_VALID(fFlags, 0), ("%#x\n", fFlags), VERR_INVALID_FLAGS);
+
+ /*
+ * Convert and normalize the path.
+ */
+ UNICODE_STRING NtName;
+ HANDLE hRoot = pThis->hDir;
+ int rc = RTNtPathRelativeFromUtf8(&NtName, &hRoot, pszRelPath, RTDIRREL_NT_GET_ASCENT(pThis),
+ pThis->enmInfoClass == FileMaximumInformation);
+ if (RT_SUCCESS(rc))
+ {
+ HANDLE hSubDir = RTNT_INVALID_HANDLE_VALUE;
+ IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
+ OBJECT_ATTRIBUTES ObjAttr;
+ InitializeObjectAttributes(&ObjAttr, &NtName, 0 /*fAttrib*/, hRoot, NULL);
+
+ ULONG fOpenOptions = FILE_OPEN_FOR_BACKUP_INTENT | FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_REPARSE_POINT;
+ if (fFlags & RTPATH_F_ON_LINK)
+ fOpenOptions |= FILE_OPEN_REPARSE_POINT;
+ NTSTATUS rcNt = NtCreateFile(&hSubDir,
+ FILE_WRITE_ATTRIBUTES | SYNCHRONIZE,
+ &ObjAttr,
+ &Ios,
+ NULL /*AllocationSize*/,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
+ FILE_OPEN,
+ fOpenOptions,
+ NULL /*EaBuffer*/,
+ 0 /*EaLength*/);
+ if (NT_SUCCESS(rcNt))
+ {
+ rc = rtNtFileSetModeWorker(hSubDir, fMode);
+
+ rcNt = NtClose(hSubDir);
+ if (!NT_SUCCESS(rcNt) && RT_SUCCESS(rc))
+ rc = RTErrConvertFromNtStatus(rcNt);
+ }
+ else
+ rc = RTErrConvertFromNtStatus(rcNt);
+
+ RTNtPathFree(&NtName, NULL);
+ }
+ return rc;
+}
+
+
+RTDECL(int) RTDirRelPathSetTimes(RTDIR hDir, const char *pszRelPath, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime,
+ PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime, uint32_t fFlags)
+{
+ PRTDIRINTERNAL pThis = hDir;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTDIR_MAGIC, VERR_INVALID_HANDLE);
+
+ char szPath[RTPATH_MAX];
+ int rc = rtDirRelBuildFullPath(pThis, szPath, sizeof(szPath), pszRelPath);
+ if (RT_SUCCESS(rc))
+ rc = RTPathSetTimesEx(szPath, pAccessTime, pModificationTime, pChangeTime, pBirthTime, fFlags);
+ return rc;
+}
+
+
+RTDECL(int) RTDirRelPathSetOwner(RTDIR hDir, const char *pszRelPath, uint32_t uid, uint32_t gid, uint32_t fFlags)
+{
+ PRTDIRINTERNAL pThis = hDir;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTDIR_MAGIC, VERR_INVALID_HANDLE);
+
+ char szPath[RTPATH_MAX];
+ int rc = rtDirRelBuildFullPath(pThis, szPath, sizeof(szPath), pszRelPath);
+ if (RT_SUCCESS(rc))
+ {
+#ifndef RT_OS_WINDOWS
+ rc = RTPathSetOwnerEx(szPath, uid, gid, fFlags);
+#else
+ rc = VERR_NOT_IMPLEMENTED;
+ RT_NOREF(uid, gid, fFlags);
+#endif
+ }
+ return rc;
+}
+
+
+RTDECL(int) RTDirRelPathRename(RTDIR hDirSrc, const char *pszSrc, RTDIR hDirDst, const char *pszDst, unsigned fRename)
+{
+ PRTDIRINTERNAL pThis = hDirSrc;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTDIR_MAGIC, VERR_INVALID_HANDLE);
+
+ PRTDIRINTERNAL pThat = hDirDst;
+ if (pThat != pThis)
+ {
+ AssertPtrReturn(pThat, VERR_INVALID_HANDLE);
+ AssertReturn(pThat->u32Magic != RTDIR_MAGIC, VERR_INVALID_HANDLE);
+ }
+
+ char szSrcPath[RTPATH_MAX];
+ int rc = rtDirRelBuildFullPath(pThis, szSrcPath, sizeof(szSrcPath), pszSrc);
+ if (RT_SUCCESS(rc))
+ {
+ char szDstPath[RTPATH_MAX];
+ rc = rtDirRelBuildFullPath(pThis, szDstPath, sizeof(szDstPath), pszDst);
+ if (RT_SUCCESS(rc))
+ rc = RTPathRename(szSrcPath, szDstPath, fRename);
+ }
+ return rc;
+}
+
+
+RTDECL(int) RTDirRelPathUnlink(RTDIR hDir, const char *pszRelPath, uint32_t fUnlink)
+{
+ PRTDIRINTERNAL pThis = hDir;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTDIR_MAGIC, VERR_INVALID_HANDLE);
+
+ char szPath[RTPATH_MAX];
+ int rc = rtDirRelBuildFullPath(pThis, szPath, sizeof(szPath), pszRelPath);
+ if (RT_SUCCESS(rc))
+ rc = RTPathUnlink(szPath, fUnlink);
+ return rc;
+}
+
+
+/*
+ *
+ * RTSymlink stuff.
+ * RTSymlink stuff.
+ * RTSymlink stuff.
+ *
+ *
+ */
+
+
+RTDECL(int) RTDirRelSymlinkCreate(RTDIR hDir, const char *pszSymlink, const char *pszTarget,
+ RTSYMLINKTYPE enmType, uint32_t fCreate)
+{
+ PRTDIRINTERNAL pThis = hDir;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTDIR_MAGIC, VERR_INVALID_HANDLE);
+
+ char szPath[RTPATH_MAX];
+ int rc = rtDirRelBuildFullPath(pThis, szPath, sizeof(szPath), pszSymlink);
+ if (RT_SUCCESS(rc))
+ rc = RTSymlinkCreate(szPath, pszTarget, enmType, fCreate);
+ return rc;
+}
+
+
+RTDECL(int) RTDirRelSymlinkRead(RTDIR hDir, const char *pszSymlink, char *pszTarget, size_t cbTarget, uint32_t fRead)
+{
+ PRTDIRINTERNAL pThis = hDir;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTDIR_MAGIC, VERR_INVALID_HANDLE);
+
+ char szPath[RTPATH_MAX];
+ int rc = rtDirRelBuildFullPath(pThis, szPath, sizeof(szPath), pszSymlink);
+ if (RT_SUCCESS(rc))
+ rc = RTSymlinkRead(szPath, pszTarget, cbTarget, fRead);
+ return rc;
+}
+
diff --git a/src/VBox/Runtime/r3/nt/fs-nt.cpp b/src/VBox/Runtime/r3/nt/fs-nt.cpp
new file mode 100644
index 00000000..70add1ee
--- /dev/null
+++ b/src/VBox/Runtime/r3/nt/fs-nt.cpp
@@ -0,0 +1,279 @@
+/* $Id: fs-nt.cpp $ */
+/** @file
+ * IPRT - File System, Native NT.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_FS
+#include "internal-r3-nt.h"
+
+#include <iprt/fs.h>
+#include <iprt/file.h>
+#include <iprt/path.h>
+#include <iprt/string.h>
+#include <iprt/param.h>
+#include <iprt/errcore.h>
+#include <iprt/log.h>
+#include <iprt/assert.h>
+#include "internal/fs.h"
+
+
+
+
+RTR3DECL(int) RTFsQuerySizes(const char *pszFsPath, RTFOFF *pcbTotal, RTFOFF *pcbFree,
+ uint32_t *pcbBlock, uint32_t *pcbSector)
+{
+ AssertPtrReturn(pszFsPath, VERR_INVALID_POINTER);
+
+ /*
+ * Open the file/dir/whatever.
+ */
+ HANDLE hFile;
+ int rc = RTNtPathOpen(pszFsPath,
+ GENERIC_READ,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ FILE_OPEN,
+ FILE_OPEN_FOR_BACKUP_INTENT,
+ OBJ_CASE_INSENSITIVE,
+ &hFile,
+ NULL);
+ if (RT_SUCCESS(rc))
+ {
+ RTFILE hIprtFile = NIL_RTFILE;
+ rc = RTFileFromNative(&hIprtFile, (RTHCINTPTR)hFile);
+ AssertRC(rc);
+ if (RT_SUCCESS(rc))
+ rc = RTFileQueryFsSizes(hIprtFile, pcbTotal, pcbFree, pcbBlock, pcbSector);
+
+ RTNtPathClose(hFile);
+ }
+ return rc;
+}
+
+
+RTR3DECL(int) RTFsQuerySerial(const char *pszFsPath, uint32_t *pu32Serial)
+{
+ /*
+ * Validate & get valid root path.
+ */
+ AssertPtrReturn(pszFsPath, VERR_INVALID_POINTER);
+ AssertPtrReturn(pu32Serial, VERR_INVALID_POINTER);
+
+ /*
+ * Open the file/dir/whatever.
+ */
+ HANDLE hFile;
+ int rc = RTNtPathOpen(pszFsPath,
+ GENERIC_READ,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ FILE_OPEN,
+ FILE_OPEN_FOR_BACKUP_INTENT,
+ OBJ_CASE_INSENSITIVE,
+ &hFile,
+ NULL);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Get the volume information.
+ */
+ union
+ {
+ FILE_FS_VOLUME_INFORMATION FsVolInfo;
+ uint8_t abBuf[sizeof(FILE_FS_VOLUME_INFORMATION) + 4096];
+ } u;
+ IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
+ NTSTATUS rcNt = NtQueryVolumeInformationFile(hFile, &Ios, &u, sizeof(u), FileFsVolumeInformation);
+ if (NT_SUCCESS(rcNt))
+ *pu32Serial = u.FsVolInfo.VolumeSerialNumber;
+ else
+ rc = RTErrConvertFromNtStatus(rcNt);
+
+ RTNtPathClose(hFile);
+ }
+ return rc;
+}
+
+
+RTR3DECL(int) RTFsQueryProperties(const char *pszFsPath, PRTFSPROPERTIES pProperties)
+{
+ /*
+ * Validate & get valid root path.
+ */
+ AssertPtrReturn(pszFsPath, VERR_INVALID_POINTER);
+ AssertPtrReturn(pProperties, VERR_INVALID_POINTER);
+
+ /*
+ * Open the file/dir/whatever.
+ */
+ HANDLE hFile;
+ int rc = RTNtPathOpen(pszFsPath,
+ GENERIC_READ,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ FILE_OPEN,
+ FILE_OPEN_FOR_BACKUP_INTENT,
+ OBJ_CASE_INSENSITIVE,
+ &hFile,
+ NULL);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Get the volume information.
+ */
+ union
+ {
+ FILE_FS_ATTRIBUTE_INFORMATION FsAttrInfo;
+ uint8_t abBuf[sizeof(FILE_FS_ATTRIBUTE_INFORMATION) + 4096];
+ } u;
+ IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
+ NTSTATUS rcNt = NtQueryVolumeInformationFile(hFile, &Ios, &u, sizeof(u), FileFsAttributeInformation);
+ if (NT_SUCCESS(rcNt))
+ {
+ FILE_FS_DEVICE_INFORMATION FsDevInfo;
+ rcNt = NtQueryVolumeInformationFile(hFile, &Ios, &FsDevInfo, sizeof(FsDevInfo), FileFsDeviceInformation);
+ if (NT_SUCCESS(rcNt))
+ {
+ /*
+ * Fill in the return structure.
+ */
+ memset(pProperties, 0, sizeof(*pProperties));
+ pProperties->cbMaxComponent = u.FsAttrInfo.MaximumComponentNameLength;
+ pProperties->fFileCompression = !!(u.FsAttrInfo.FileSystemAttributes & FILE_FILE_COMPRESSION);
+ pProperties->fCompressed = !!(u.FsAttrInfo.FileSystemAttributes & FILE_VOLUME_IS_COMPRESSED);
+ pProperties->fReadOnly = !!(u.FsAttrInfo.FileSystemAttributes & FILE_READ_ONLY_VOLUME);
+ pProperties->fSupportsUnicode = !!(u.FsAttrInfo.FileSystemAttributes & FILE_UNICODE_ON_DISK);
+ pProperties->fCaseSensitive = false; /* win32 is case preserving only */
+ /** @todo r=bird: What about FILE_CASE_SENSITIVE_SEARCH ? Is this set for NTFS
+ * as well perchance? If so, better mention it instead of just setting
+ * fCaseSensitive to false. */
+
+ /* figure the remote stuff */
+ pProperties->fRemote = RT_BOOL(FsDevInfo.Characteristics & FILE_REMOTE_DEVICE);
+ }
+ else
+ rc = RTErrConvertFromNtStatus(rcNt);
+ }
+ else
+ rc = RTErrConvertFromNtStatus(rcNt);
+
+ RTNtPathClose(hFile);
+ }
+ return rc;
+}
+
+
+RTR3DECL(bool) RTFsIsCaseSensitive(const char *pszFsPath)
+{
+ RT_NOREF_PV(pszFsPath);
+ return false;
+}
+
+
+int rtNtQueryFsType(HANDLE hHandle, PRTFSTYPE penmType)
+{
+ /*
+ * Get the file system name.
+ */
+ union
+ {
+ FILE_FS_ATTRIBUTE_INFORMATION FsAttrInfo;
+ uint8_t abBuf[sizeof(FILE_FS_ATTRIBUTE_INFORMATION) + 4096];
+ } u;
+ IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
+ NTSTATUS rcNt = NtQueryVolumeInformationFile(hHandle, &Ios, &u, sizeof(u), FileFsAttributeInformation);
+ if (NT_SUCCESS(rcNt))
+ {
+#define IS_FS(a_szName) rtNtCompWideStrAndAscii(u.FsAttrInfo.FileSystemName, u.FsAttrInfo.FileSystemNameLength, RT_STR_TUPLE(a_szName))
+ if (IS_FS("NTFS"))
+ *penmType = RTFSTYPE_NTFS;
+ else if (IS_FS("FAT"))
+ *penmType = RTFSTYPE_FAT;
+ else if (IS_FS("FAT32"))
+ *penmType = RTFSTYPE_FAT;
+ else if (IS_FS("exFAT"))
+ *penmType = RTFSTYPE_EXFAT;
+ else if (IS_FS("UDF"))
+ *penmType = RTFSTYPE_UDF;
+ else if (IS_FS("CDFS"))
+ *penmType = RTFSTYPE_ISO9660;
+ else if (IS_FS("HPFS"))
+ *penmType = RTFSTYPE_HPFS;
+ else if (IS_FS("ReFS")) /** @todo verify ReFS signature. */
+ *penmType = RTFSTYPE_REFS;
+ else if (IS_FS("VBoxSharedFolderFS"))
+ *penmType = RTFSTYPE_VBOXSHF;
+#undef IS_FS
+ return VINF_SUCCESS;
+ }
+
+ *penmType = RTFSTYPE_UNKNOWN;
+ return RTErrConvertFromNtStatus(rcNt);
+}
+
+
+RTR3DECL(int) RTFsQueryType(const char *pszFsPath, PRTFSTYPE penmType)
+{
+ /*
+ * Validate input.
+ */
+ *penmType = RTFSTYPE_UNKNOWN;
+ AssertPtrReturn(pszFsPath, VERR_INVALID_POINTER);
+ AssertReturn(*pszFsPath, VERR_INVALID_PARAMETER);
+
+ /*
+ * Open the file/dir/whatever.
+ */
+ HANDLE hFile;
+ int rc = RTNtPathOpen(pszFsPath,
+ GENERIC_READ,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ FILE_OPEN,
+ FILE_OPEN_FOR_BACKUP_INTENT,
+ OBJ_CASE_INSENSITIVE,
+ &hFile,
+ NULL);
+ if (RT_SUCCESS(rc))
+ {
+ rc = rtNtQueryFsType(hFile, penmType);
+ RTNtPathClose(hFile);
+ }
+ return rc;
+}
+
diff --git a/src/VBox/Runtime/r3/nt/internal-r3-nt.h b/src/VBox/Runtime/r3/nt/internal-r3-nt.h
new file mode 100644
index 00000000..c76a3f6b
--- /dev/null
+++ b/src/VBox/Runtime/r3/nt/internal-r3-nt.h
@@ -0,0 +1,92 @@
+/* $Id: internal-r3-nt.h $ */
+/** @file
+ * IPRT - Internal Header for the Native NT code.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef IPRT_INCLUDED_SRC_r3_nt_internal_r3_nt_h
+#define IPRT_INCLUDED_SRC_r3_nt_internal_r3_nt_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#ifdef IN_SUP_HARDENED_R3
+# include <iprt/nt/nt-and-windows.h>
+#else
+# include <iprt/nt/nt.h>
+#endif
+#include "internal/iprt.h"
+
+
+#if 1
+/** Enables the "\\!\" NT path pass thru as well as hacks for listing NT object
+ * directories. */
+# define IPRT_WITH_NT_PATH_PASSTHRU 1
+#endif
+
+
+
+/**
+ * Internal helper for comparing a WCHAR string with a char string.
+ *
+ * @returns @c true if equal, @c false if not.
+ * @param pwsz1 The first string.
+ * @param cch1 The length of the first string, in bytes.
+ * @param psz2 The second string.
+ * @param cch2 The length of the second string.
+ */
+DECLINLINE(bool) rtNtCompWideStrAndAscii(WCHAR const *pwsz1, size_t cch1, const char *psz2, size_t cch2)
+{
+ if (cch1 != cch2 * 2)
+ return false;
+ while (cch2-- > 0)
+ {
+ unsigned ch1 = *pwsz1++;
+ unsigned ch2 = (unsigned char)*psz2++;
+ if (ch1 != ch2)
+ return false;
+ }
+ return true;
+}
+
+#endif /* !IPRT_INCLUDED_SRC_r3_nt_internal_r3_nt_h */
+
+/**
+ * Common worker for RTFileSetMode, RTPathSetMode and RTDirRelPathSetMode.
+ *
+ * @returns IPRT status code.
+ * @param hNativeFile The NT handle to the file system object.
+ * @param fMode Valid and normalized file mode mask to set.
+ */
+DECLHIDDEN(int) rtNtFileSetModeWorker(HANDLE hNativeFile, RTFMODE fMode);
+
diff --git a/src/VBox/Runtime/r3/nt/pathint-nt.cpp b/src/VBox/Runtime/r3/nt/pathint-nt.cpp
new file mode 100644
index 00000000..95281ca7
--- /dev/null
+++ b/src/VBox/Runtime/r3/nt/pathint-nt.cpp
@@ -0,0 +1,1161 @@
+/* $Id: pathint-nt.cpp $ */
+/** @file
+ * IPRT - Native NT, Internal Path stuff.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_FS
+#include "internal-r3-nt.h"
+
+#include <iprt/assert.h>
+#include <iprt/err.h>
+#include <iprt/mem.h>
+#include <iprt/path.h>
+#include <iprt/string.h>
+#include <iprt/utf16.h>
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+static char const g_szPrefixUnc[] = "\\??\\UNC\\";
+static char const g_szPrefix[] = "\\??\\";
+static char const g_szPrefixNt3xUnc[] = "\\DosDevices\\UNC\\";
+static char const g_szPrefixNt3x[] = "\\DosDevices\\";
+
+
+/**
+ * Handles the pass thru case for UTF-8 input.
+ * Win32 path uses "\\?\" prefix which is converted to "\??\" NT prefix.
+ *
+ * @returns IPRT status code.
+ * @param pNtName Where to return the NT name.
+ * @param phRootDir Where to return the root handle, if applicable.
+ * @param pszPath The UTF-8 path.
+ */
+static int rtNtPathFromWinUtf8PassThru(struct _UNICODE_STRING *pNtName, PHANDLE phRootDir, const char *pszPath)
+{
+ PRTUTF16 pwszPath = NULL;
+ size_t cwcLen;
+ int rc = RTStrToUtf16Ex(pszPath, RTSTR_MAX, &pwszPath, 0, &cwcLen);
+ if (RT_SUCCESS(rc))
+ {
+ if (cwcLen < _32K - 1)
+ {
+ *phRootDir = NULL;
+ if (RT_MAKE_U64(RTNtCurrentPeb()->OSMinorVersion, RTNtCurrentPeb()->OSMajorVersion) >= RT_MAKE_U64(0, 4))
+ {
+ pwszPath[0] = '\\';
+ pwszPath[1] = '?';
+ pwszPath[2] = '?';
+ pwszPath[3] = '\\';
+
+ pNtName->Buffer = pwszPath;
+ pNtName->Length = (uint16_t)(cwcLen * sizeof(RTUTF16));
+ pNtName->MaximumLength = pNtName->Length + sizeof(RTUTF16);
+ return VINF_SUCCESS;
+ }
+
+ rc = RTUtf16Realloc(&pwszPath, cwcLen + sizeof(g_szPrefixNt3x));
+ if (RT_SUCCESS(rc))
+ {
+ memmove(&pwszPath[sizeof(g_szPrefixNt3x) - 1], &pwszPath[4], (cwcLen - 4 + 1) * sizeof(RTUTF16));
+ for (uint32_t i = 0; i < sizeof(g_szPrefixNt3x) - 1; i++)
+ pwszPath[i] = g_szPrefixNt3x[i];
+
+ pNtName->Buffer = pwszPath;
+ pNtName->Length = (uint16_t)((cwcLen - 4 + sizeof(g_szPrefixNt3x) - 1) * sizeof(RTUTF16));
+ pNtName->MaximumLength = pNtName->Length + sizeof(RTUTF16);
+ return VINF_SUCCESS;
+ }
+ }
+
+ RTUtf16Free(pwszPath);
+ rc = VERR_FILENAME_TOO_LONG;
+ }
+ return rc;
+}
+
+
+/**
+ * Handles the pass thru case for UTF-16 input.
+ * Win32 path uses "\\?\" prefix which is converted to "\??\" NT prefix.
+ *
+ * @returns IPRT status code.
+ * @param pNtName Where to return the NT name.
+ * @param phRootDir Stores NULL here, as we don't use it.
+ * @param pwszWinPath The UTF-16 windows-style path.
+ * @param cwcWinPath The length of the windows-style input path.
+ */
+static int rtNtPathFromWinUtf16PassThru(struct _UNICODE_STRING *pNtName, PHANDLE phRootDir,
+ PCRTUTF16 pwszWinPath, size_t cwcWinPath)
+{
+ /* Check length and allocate memory for it. */
+ int rc;
+ if (cwcWinPath < _32K - 1)
+ {
+
+ size_t const cwcExtraPrefix = RT_MAKE_U64(RTNtCurrentPeb()->OSMinorVersion, RTNtCurrentPeb()->OSMajorVersion)
+ >= RT_MAKE_U64(0, 4)
+ ? 0 : sizeof(g_szPrefixNt3x) - 1 - 4;
+ PRTUTF16 pwszNtPath = (PRTUTF16)RTUtf16Alloc((cwcExtraPrefix + cwcWinPath + 1) * sizeof(RTUTF16));
+ if (pwszNtPath)
+ {
+ /* Intialize the path. */
+ if (!cwcExtraPrefix)
+ {
+ pwszNtPath[0] = '\\';
+ pwszNtPath[1] = '?';
+ pwszNtPath[2] = '?';
+ pwszNtPath[3] = '\\';
+ }
+ else
+ for (uint32_t i = 0; i < sizeof(g_szPrefixNt3x) - 1; i++)
+ pwszNtPath[i] = g_szPrefixNt3x[i];
+ memcpy(pwszNtPath + cwcExtraPrefix + 4, pwszWinPath + 4, (cwcWinPath - 4) * sizeof(RTUTF16));
+ pwszNtPath[cwcExtraPrefix + cwcWinPath] = '\0';
+
+ /* Initialize the return values. */
+ pNtName->Buffer = pwszNtPath;
+ pNtName->Length = (uint16_t)(cwcExtraPrefix + cwcWinPath * sizeof(RTUTF16));
+ pNtName->MaximumLength = pNtName->Length + sizeof(RTUTF16);
+ *phRootDir = NULL;
+
+ rc = VINF_SUCCESS;
+ }
+ else
+ rc = VERR_NO_UTF16_MEMORY;
+ }
+ else
+ rc = VERR_FILENAME_TOO_LONG;
+ return rc;
+}
+
+
+
+
+
+/**
+ * Converts the path to UTF-16 and sets all the return values.
+ *
+ * @returns IPRT status code.
+ * @param pNtName Where to return the NT name.
+ * @param phRootDir Where to return the root handle, if applicable.
+ * @param pszPath The UTF-8 path.
+ */
+static int rtNtPathUtf8ToUniStr(struct _UNICODE_STRING *pNtName, PHANDLE phRootDir, const char *pszPath)
+{
+ PRTUTF16 pwszPath = NULL;
+ size_t cwcLen;
+ int rc = RTStrToUtf16Ex(pszPath, RTSTR_MAX, &pwszPath, 0, &cwcLen);
+ if (RT_SUCCESS(rc))
+ {
+ if (cwcLen < _32K - 1)
+ {
+ pNtName->Buffer = pwszPath;
+ pNtName->Length = (uint16_t)(cwcLen * sizeof(RTUTF16));
+ pNtName->MaximumLength = pNtName->Length + sizeof(RTUTF16);
+ *phRootDir = NULL;
+ return VINF_SUCCESS;
+ }
+
+ RTUtf16Free(pwszPath);
+ rc = VERR_FILENAME_TOO_LONG;
+ }
+ return rc;
+}
+
+
+/**
+ * Converts a windows-style path to NT format and encoding.
+ *
+ * @returns IPRT status code.
+ * @param pNtName Where to return the NT name. Free using
+ * rtTNtPathToNative.
+ * @param phRootDir Where to return the root handle, if applicable.
+ * @param pszPath The UTF-8 path.
+ */
+static int rtNtPathToNative(struct _UNICODE_STRING *pNtName, PHANDLE phRootDir, const char *pszPath)
+{
+/** @todo This code sucks a bit performance wise, esp. calling
+ * generic RTPathAbs. Too many buffers involved, I think. */
+
+ /*
+ * Very simple conversion of a win32-like path into an NT path.
+ */
+ const char *pszPrefix;
+ size_t cchPrefix;
+ if (RT_MAKE_U64(RTNtCurrentPeb()->OSMinorVersion, RTNtCurrentPeb()->OSMajorVersion) >= RT_MAKE_U64(0, 4))
+ {
+ pszPrefix = g_szPrefix;
+ cchPrefix = sizeof(g_szPrefix) - 1;
+ }
+ else
+ {
+ pszPrefix = g_szPrefixNt3x;
+ cchPrefix = sizeof(g_szPrefixNt3x) - 1;
+ }
+
+ size_t cchSkip = 0;
+ if ( RTPATH_IS_SLASH(pszPath[0])
+ && RTPATH_IS_SLASH(pszPath[1])
+ && !RTPATH_IS_SLASH(pszPath[2])
+ && pszPath[2])
+ {
+#ifdef IPRT_WITH_NT_PATH_PASSTHRU
+ /*
+ * Special trick: The path starts with RTPATH_NT_PASSTHRU_PREFIX, we will skip past the bang and pass it thru.
+ */
+ if ( pszPath[2] == ':'
+ && pszPath[3] == 'i'
+ && pszPath[4] == 'p'
+ && pszPath[5] == 'r'
+ && pszPath[6] == 't'
+ && pszPath[7] == 'n'
+ && pszPath[8] == 't'
+ && pszPath[9] == ':'
+ && RTPATH_IS_SLASH(pszPath[10]))
+ return rtNtPathUtf8ToUniStr(pNtName, phRootDir, pszPath + 10);
+#endif
+
+ if ( pszPath[2] == '?'
+ && RTPATH_IS_SLASH(pszPath[3]))
+ return rtNtPathFromWinUtf8PassThru(pNtName, phRootDir, pszPath);
+
+ if ( pszPath[2] == '.'
+ && RTPATH_IS_SLASH(pszPath[3]))
+ {
+ /*
+ * Device path.
+ * Note! I suspect \\.\stuff\..\otherstuff may be handled differently by windows.
+ */
+ cchSkip = 4;
+ }
+ else
+ {
+ /* UNC */
+ if (RT_MAKE_U64(RTNtCurrentPeb()->OSMinorVersion, RTNtCurrentPeb()->OSMajorVersion) >= RT_MAKE_U64(0, 4))
+ {
+ pszPrefix = g_szPrefixUnc;
+ cchPrefix = sizeof(g_szPrefixUnc) - 1;
+ }
+ else
+ {
+ pszPrefix = g_szPrefixNt3xUnc;
+ cchPrefix = sizeof(g_szPrefixNt3xUnc) - 1;
+ }
+ cchSkip = 2;
+ }
+ }
+
+ /*
+ * Straighten out all .. and uncessary . references and convert slashes.
+ */
+ char szAbsPathBuf[RTPATH_MAX];
+ size_t cbAbsPath = sizeof(szAbsPathBuf) - (cchPrefix - cchSkip);
+ char *pszAbsPath = szAbsPathBuf;
+ char *pszAbsPathFree = NULL;
+ int rc = RTPathAbsEx(NULL, pszPath, RTPATH_STR_F_STYLE_DOS, &pszAbsPath[cchPrefix - cchSkip], &cbAbsPath);
+ if (RT_SUCCESS(rc))
+ { /* likely */ }
+ else if (rc == VERR_BUFFER_OVERFLOW)
+ {
+ unsigned cTries = 8;
+ size_t cbAbsPathBuf = RTPATH_MAX;
+ for (;;)
+ {
+ cbAbsPathBuf = RT_MAX(RT_ALIGN_Z((cchPrefix - cchSkip) + cbAbsPath + 32, 64), cbAbsPathBuf + 256);
+ if (cTries == 1)
+ cbAbsPathBuf = RT_MAX(cbAbsPathBuf, RTPATH_BIG_MAX * 2);
+ pszAbsPathFree = pszAbsPath = (char *)RTMemTmpAlloc(cbAbsPathBuf);
+ if (!pszAbsPath)
+ return VERR_NO_TMP_MEMORY;
+
+ cbAbsPath = cbAbsPathBuf - (cchPrefix - cchSkip);
+ rc = RTPathAbsEx(NULL, pszPath, RTPATH_STR_F_STYLE_DOS, &pszAbsPath[cchPrefix - cchSkip], &cbAbsPath);
+ if (RT_SUCCESS(rc))
+ break;
+ RTMemTmpFree(pszAbsPathFree);
+ pszAbsPathFree = NULL;
+ if (rc != VERR_BUFFER_OVERFLOW)
+ return rc;
+ if (--cTries == 0)
+ return VERR_FILENAME_TOO_LONG;
+ }
+ }
+ else
+ return rc;
+
+ /*
+ * Add prefix and convert it to UTF16.
+ */
+ memcpy(pszAbsPath, pszPrefix, cchPrefix);
+ rc = rtNtPathUtf8ToUniStr(pNtName, phRootDir, pszAbsPath);
+
+ if (pszAbsPathFree)
+ RTMemTmpFree(pszAbsPathFree);
+ return rc;
+}
+
+
+/**
+ * Converts a windows-style path to NT format and encoding.
+ *
+ * @returns IPRT status code.
+ * @param pNtName Where to return the NT name. Free using
+ * RTNtPathToNative.
+ * @param phRootDir Where to return the root handle, if applicable.
+ * @param pszPath The UTF-8 path.
+ */
+RTDECL(int) RTNtPathFromWinUtf8(struct _UNICODE_STRING *pNtName, PHANDLE phRootDir, const char *pszPath)
+{
+ return rtNtPathToNative(pNtName, phRootDir, pszPath);
+}
+
+
+/**
+ * Converts a UTF-16 windows-style path to NT format.
+ *
+ * @returns IPRT status code.
+ * @param pNtName Where to return the NT name. Free using
+ * RTNtPathFree.
+ * @param phRootDir Where to return the root handle, if applicable.
+ * @param pwszWinPath The UTF-16 windows-style path.
+ * @param cwcWinPath The max length of the windows-style path in
+ * RTUTF16 units. Use RTSTR_MAX if unknown and @a
+ * pwszWinPath is correctly terminated.
+ */
+RTDECL(int) RTNtPathFromWinUtf16Ex(struct _UNICODE_STRING *pNtName, HANDLE *phRootDir, PCRTUTF16 pwszWinPath, size_t cwcWinPath)
+{
+ /*
+ * Validate the input, calculating the correct length.
+ */
+ if (cwcWinPath == 0 || *pwszWinPath == '\0')
+ return VERR_INVALID_NAME;
+
+ RTUtf16NLenEx(pwszWinPath, cwcWinPath, &cwcWinPath);
+ int rc = RTUtf16ValidateEncodingEx(pwszWinPath, cwcWinPath, 0);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * Very simple conversion of a win32-like path into an NT path.
+ */
+ const char *pszPrefix = g_szPrefix;
+ size_t cchPrefix = sizeof(g_szPrefix) - 1;
+ size_t cchSkip = 0;
+
+ if ( RTPATH_IS_SLASH(pwszWinPath[0])
+ && cwcWinPath >= 3
+ && RTPATH_IS_SLASH(pwszWinPath[1])
+ && !RTPATH_IS_SLASH(pwszWinPath[2]) )
+ {
+#ifdef IPRT_WITH_NT_PATH_PASSTHRU
+ /*
+ * Special trick: The path starts with RTPATH_NT_PASSTHRU_PREFIX, we will skip past the bang and pass it thru.
+ */
+ if ( cwcWinPath >= sizeof(RTPATH_NT_PASSTHRU_PREFIX) - 1U
+ && pwszWinPath[2] == ':'
+ && pwszWinPath[3] == 'i'
+ && pwszWinPath[4] == 'p'
+ && pwszWinPath[5] == 'r'
+ && pwszWinPath[6] == 't'
+ && pwszWinPath[7] == 'n'
+ && pwszWinPath[8] == 't'
+ && pwszWinPath[9] == ':'
+ && RTPATH_IS_SLASH(pwszWinPath[10]) )
+ {
+ pwszWinPath += 10;
+ cwcWinPath -= 10;
+ if (cwcWinPath < _32K - 1)
+ {
+ PRTUTF16 pwszNtPath = RTUtf16Alloc((cwcWinPath + 1) * sizeof(RTUTF16));
+ if (pwszNtPath)
+ {
+ memcpy(pwszNtPath, pwszWinPath, cwcWinPath * sizeof(RTUTF16));
+ pwszNtPath[cwcWinPath] = '\0';
+ pNtName->Buffer = pwszNtPath;
+ pNtName->Length = (uint16_t)(cwcWinPath * sizeof(RTUTF16));
+ pNtName->MaximumLength = pNtName->Length + sizeof(RTUTF16);
+ *phRootDir = NULL;
+ return VINF_SUCCESS;
+ }
+ rc = VERR_NO_UTF16_MEMORY;
+ }
+ else
+ rc = VERR_FILENAME_TOO_LONG;
+ return rc;
+ }
+#endif
+
+ if ( pwszWinPath[2] == '?'
+ && cwcWinPath >= 4
+ && RTPATH_IS_SLASH(pwszWinPath[3]))
+ return rtNtPathFromWinUtf16PassThru(pNtName, phRootDir, pwszWinPath, cwcWinPath);
+
+ if ( pwszWinPath[2] == '.'
+ && cwcWinPath >= 4
+ && RTPATH_IS_SLASH(pwszWinPath[3]))
+ {
+ /*
+ * Device path.
+ * Note! I suspect \\.\stuff\..\otherstuff may be handled differently by windows.
+ */
+ cchSkip = 4;
+ }
+ else
+ {
+ /* UNC */
+ pszPrefix = g_szPrefixUnc;
+ cchPrefix = sizeof(g_szPrefixUnc) - 1;
+ cchSkip = 2;
+ }
+ }
+
+ /*
+ * Straighten out all .. and unnecessary . references and convert slashes.
+ */
+ /* UTF-16 -> UTF-8 (relative path) */
+ char szRelPath[RTPATH_MAX];
+ char *pszRelPathFree = NULL;
+ char *pszRelPath = szRelPath;
+ size_t cchRelPath;
+ rc = RTUtf16ToUtf8Ex(pwszWinPath, cwcWinPath, &pszRelPath, sizeof(szRelPath), &cchRelPath);
+ if (RT_SUCCESS(rc))
+ { /* likely */ }
+ else if (rc == VERR_BUFFER_OVERFLOW)
+ {
+ pszRelPath = NULL;
+ rc = RTUtf16ToUtf8Ex(pwszWinPath, cwcWinPath, &pszRelPath, 0, &cchRelPath);
+ if (RT_SUCCESS(rc))
+ pszRelPathFree = pszRelPath;
+ }
+ if (RT_SUCCESS(rc))
+ {
+ /* Relative -> Absolute. */
+ char szAbsPathBuf[RTPATH_MAX];
+ char *pszAbsPathFree = NULL;
+ char *pszAbsPath = szAbsPathBuf;
+ size_t cbAbsPath = sizeof(szAbsPathBuf) - (cchPrefix - cchSkip);
+ rc = RTPathAbsEx(NULL, szRelPath, RTPATH_STR_F_STYLE_DOS, &pszAbsPath[cchPrefix - cchSkip], &cbAbsPath);
+ if (RT_SUCCESS(rc))
+ { /* likely */ }
+ else if (rc == VERR_BUFFER_OVERFLOW)
+ {
+ unsigned cTries = 8;
+ size_t cbAbsPathBuf = RTPATH_MAX;
+ for (;;)
+ {
+ cbAbsPathBuf = RT_MAX(RT_ALIGN_Z((cchPrefix - cchSkip) + cbAbsPath + 32, 64), cbAbsPathBuf + 256);
+ if (cTries == 1)
+ cbAbsPathBuf = RT_MAX(cbAbsPathBuf, RTPATH_BIG_MAX * 2);
+ pszAbsPathFree = pszAbsPath = (char *)RTMemTmpAlloc(cbAbsPathBuf);
+ if (!pszAbsPath)
+ {
+ rc = VERR_NO_TMP_MEMORY;
+ break;
+ }
+
+ cbAbsPath = cbAbsPathBuf - (cchPrefix - cchSkip);
+ rc = RTPathAbsEx(NULL, szRelPath, RTPATH_STR_F_STYLE_DOS, &pszAbsPath[cchPrefix - cchSkip], &cbAbsPath);
+ if (RT_SUCCESS(rc))
+ break;
+
+ RTMemTmpFree(pszAbsPathFree);
+ pszAbsPathFree = NULL;
+ if (rc != VERR_BUFFER_OVERFLOW)
+ break;
+ if (--cTries == 0)
+ {
+ rc = VERR_FILENAME_TOO_LONG;
+ break;
+ }
+ }
+
+ }
+ if (pszRelPathFree)
+ RTStrFree(pszRelPathFree);
+
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Add prefix
+ */
+ memcpy(pszAbsPath, pszPrefix, cchPrefix);
+
+ /*
+ * Remove trailing '.' that is used to specify no extension in the Win32/DOS world.
+ */
+ size_t cchAbsPath = strlen(pszAbsPath);
+ if ( cchAbsPath > 2
+ && pszAbsPath[cchAbsPath - 1] == '.')
+ {
+ char const ch = pszAbsPath[cchAbsPath - 2];
+ if ( ch != '/'
+ && ch != '\\'
+ && ch != ':'
+ && ch != '.')
+ pszAbsPath[--cchAbsPath] = '\0';
+ }
+
+ /*
+ * Finally convert to UNICODE_STRING.
+ */
+ rc = rtNtPathUtf8ToUniStr(pNtName, phRootDir, pszAbsPath);
+
+ if (pszAbsPathFree)
+ RTMemTmpFree(pszAbsPathFree);
+ }
+ }
+ return rc;
+}
+
+
+/**
+ * Ensures that the NT string has sufficient storage to hold @a cwcMin RTUTF16
+ * chars plus a terminator.
+ *
+ * The NT string must have been returned by RTNtPathFromWinUtf8 or
+ * RTNtPathFromWinUtf16Ex.
+ *
+ * @returns IPRT status code.
+ * @param pNtName The NT path string.
+ * @param cwcMin The minimum number of RTUTF16 chars. Max 32767.
+ * @sa RTNtPathFree
+ */
+RTDECL(int) RTNtPathEnsureSpace(struct _UNICODE_STRING *pNtName, size_t cwcMin)
+{
+ if (pNtName->MaximumLength / sizeof(RTUTF16) > cwcMin)
+ return VINF_SUCCESS;
+
+ AssertReturn(cwcMin < _64K / sizeof(RTUTF16), VERR_OUT_OF_RANGE);
+
+ size_t const cbMin = (cwcMin + 1) * sizeof(RTUTF16);
+ int rc = RTUtf16Realloc(&pNtName->Buffer, cbMin);
+ if (RT_SUCCESS(rc))
+ pNtName->MaximumLength = (uint16_t)cbMin;
+ return rc;
+}
+
+
+/**
+ * Gets the NT path to the object represented by the given handle.
+ *
+ * @returns IPRT status code.
+ * @param pNtName Where to return the NT path. Free using
+ * RTNtPathFree.
+ * @param hHandle The handle.
+ * @param cwcExtra How much extra space is needed.
+ */
+RTDECL(int) RTNtPathFromHandle(struct _UNICODE_STRING *pNtName, HANDLE hHandle, size_t cwcExtra)
+{
+ /*
+ * Query the name into a buffer.
+ */
+ ULONG cbBuf = _2K;
+ PUNICODE_STRING pUniStrBuf = (PUNICODE_STRING)RTMemTmpAllocZ(cbBuf);
+ if (!pUniStrBuf)
+ return VERR_NO_TMP_MEMORY;
+
+ ULONG cbNameBuf = cbBuf;
+ NTSTATUS rcNt = NtQueryObject(hHandle, ObjectNameInformation, pUniStrBuf, cbBuf, &cbNameBuf);
+ while ( rcNt == STATUS_BUFFER_OVERFLOW
+ || rcNt == STATUS_BUFFER_TOO_SMALL)
+ {
+ do
+ cbBuf *= 2;
+ while (cbBuf <= cbNameBuf);
+ RTMemTmpFree(pUniStrBuf);
+ pUniStrBuf = (PUNICODE_STRING)RTMemTmpAllocZ(cbBuf);
+ if (!pUniStrBuf)
+ return VERR_NO_TMP_MEMORY;
+
+ cbNameBuf = cbBuf;
+ rcNt = NtQueryObject(hHandle, ObjectNameInformation, pUniStrBuf, cbBuf, &cbNameBuf);
+ }
+ int rc;
+ if (NT_SUCCESS(rcNt))
+ {
+ /*
+ * Copy the result into the return string.
+ */
+ size_t cbNeeded = cwcExtra * sizeof(RTUTF16) + pUniStrBuf->Length + sizeof(RTUTF16);
+ if (cbNeeded < _64K)
+ {
+ pNtName->Length = pUniStrBuf->Length;
+ pNtName->MaximumLength = (uint16_t)cbNeeded;
+ pNtName->Buffer = RTUtf16Alloc(cbNeeded);
+ if (pNtName->Buffer)
+ {
+ memcpy(pNtName->Buffer, pUniStrBuf->Buffer, pUniStrBuf->Length);
+ pNtName->Buffer[pUniStrBuf->Length / sizeof(RTUTF16)] = '\0';
+ rc = VINF_SUCCESS;
+ }
+ else
+ rc = VERR_NO_UTF16_MEMORY;
+ }
+ else
+ rc = VERR_FILENAME_TOO_LONG;
+ }
+ else
+ rc = RTErrConvertFromNtStatus(rcNt);
+ RTMemTmpFree(pUniStrBuf);
+ return rc;
+}
+
+static int rtNtPathRelativeToAbs(struct _UNICODE_STRING *pNtName, HANDLE *phRootDir)
+{
+ int rc;
+ if (pNtName->Length == 0)
+ {
+ RTUtf16Free(pNtName->Buffer);
+ rc = RTNtPathFromHandle(pNtName, *phRootDir, pNtName->Length / sizeof(RTUTF16) + 2);
+ if (RT_SUCCESS(rc))
+ {
+ *phRootDir = NULL;
+ return VINF_SUCCESS;
+ }
+ }
+ else
+ {
+
+ UNICODE_STRING RootDir;
+ size_t const cwcAppend = pNtName->Length / sizeof(RTUTF16);
+ rc = RTNtPathFromHandle(&RootDir, *phRootDir, cwcAppend + 2);
+ if (RT_SUCCESS(rc))
+ {
+ size_t cwcRoot = RootDir.Length / sizeof(RTUTF16);
+ if (RootDir.Buffer[cwcRoot - 1] != '\\')
+ RootDir.Buffer[cwcRoot++] = '\\';
+ memcpy(&RootDir.Buffer[cwcRoot], pNtName->Buffer, cwcAppend * sizeof(RTUTF16));
+ RTUtf16Free(pNtName->Buffer);
+ pNtName->Length = (uint16_t)((cwcRoot + cwcAppend) * sizeof(RTUTF16));
+ pNtName->MaximumLength = RootDir.MaximumLength;
+ pNtName->Buffer = RootDir.Buffer;
+
+ *phRootDir = NULL;
+ return VINF_SUCCESS;
+ }
+ RTUtf16Free(pNtName->Buffer);
+ }
+ pNtName->Length = 0;
+ pNtName->MaximumLength = 0;
+ pNtName->Buffer = NULL;
+ return rc;
+}
+
+
+/**
+ * Rewinds the path back to the start of the previous component.
+ *
+ * Will preserve root slash.
+ *
+ * @returns Pointer to character after the start-of-component slash, or
+ * pwszStart.
+ * @param pwcEnd The current end of the path.
+ * @param pwszStart The start of the path.
+ */
+static PRTUTF16 rtNtPathGetPrevComponent(PRTUTF16 pwcEnd, PRTUTF16 pwszStart)
+{
+ if ((uintptr_t)pwcEnd > (uintptr_t)pwszStart)
+ {
+ RTUTF16 wc = pwcEnd[-1];
+ if ( (wc == '\\' || wc == '/')
+ && (uintptr_t)(pwcEnd - pwszStart) != 1)
+ pwcEnd--;
+
+ while ( (uintptr_t)pwcEnd > (uintptr_t)pwszStart
+ && (wc = pwcEnd[-1]) != '\\'
+ && (wc = pwcEnd[-1]) != '/')
+ pwcEnd--;
+ }
+ return pwcEnd;
+}
+
+
+/**
+ * Converts a relative windows-style path to relative NT format and encoding.
+ *
+ * @returns IPRT status code.
+ * @param pNtName Where to return the NT name. Free using
+ * rtTNtPathToNative with phRootDir set to NULL.
+ * @param phRootDir On input, the handle to the directory the path
+ * is relative to. On output, the handle to
+ * specify as root directory in the object
+ * attributes when accessing the path. If
+ * enmAscent is kRTNtPathRelativeAscent_Allow, it
+ * may have been set to NULL.
+ * @param pszPath The relative UTF-8 path.
+ * @param enmAscent How to handle ascent.
+ * @param fMustReturnAbsolute Must convert to an absolute path. This
+ * is necessary if the root dir is a NT directory
+ * object (e.g. /Devices) since they cannot parse
+ * relative paths it seems.
+ */
+RTDECL(int) RTNtPathRelativeFromUtf8(struct _UNICODE_STRING *pNtName, PHANDLE phRootDir, const char *pszPath,
+ RTNTPATHRELATIVEASCENT enmAscent, bool fMustReturnAbsolute)
+{
+ size_t cwcMax;
+ int rc = RTStrCalcUtf16LenEx(pszPath, RTSTR_MAX, &cwcMax);
+ if (RT_FAILURE(rc))
+ return rc;
+ if (cwcMax + 2 >= _32K)
+ return VERR_FILENAME_TOO_LONG;
+
+ PRTUTF16 pwszDst;
+ pNtName->Length = 0;
+ pNtName->MaximumLength = (uint16_t)((cwcMax + 2) * sizeof(RTUTF16));
+ pNtName->Buffer = pwszDst = RTUtf16Alloc((cwcMax + 2) * sizeof(RTUTF16));
+ if (!pwszDst)
+ return VERR_NO_UTF16_MEMORY;
+
+ PRTUTF16 pwszDstCur = pwszDst;
+ PRTUTF16 pwszDstComp = pwszDst;
+ for (;;)
+ {
+ RTUNICP uc;
+ rc = RTStrGetCpEx(&pszPath, &uc);
+ if (RT_SUCCESS(rc))
+ {
+ switch (uc)
+ {
+ default:
+ pwszDstCur = RTUtf16PutCp(pwszDstCur, uc);
+ break;
+
+ case '\\':
+ case '/':
+ if (pwszDstCur != pwszDstComp)
+ pwszDstComp = pwszDstCur = RTUtf16PutCp(pwszDstCur, '\\');
+ /* else: only one slash between components. */
+ break;
+
+ case '.':
+ if (pwszDstCur == pwszDstComp)
+ {
+ /*
+ * Single dot changes nothing.
+ */
+ char ch2 = *pszPath;
+ if (ch2 == '\0')
+ {
+ /* Trailing single dot means we need to drop trailing slash. */
+ if (pwszDstCur != pwszDst)
+ pwszDstCur--;
+ *pwszDstCur = '\0';
+ pNtName->Length = (uint16_t)((uintptr_t)pwszDstCur - (uintptr_t)pwszDst);
+ if (!fMustReturnAbsolute || *phRootDir == NULL)
+ return VINF_SUCCESS;
+ return rtNtPathRelativeToAbs(pNtName, phRootDir);
+ }
+
+ if (ch2 == '\\' || ch2 == '/')
+ {
+ pszPath++; /* Ignore lone dot followed but another component. */
+ break;
+ }
+
+ /*
+ * Two dots drops off the last directory component. This gets complicated
+ * when we start out without any path and we need to consult enmAscent.
+ */
+ if (ch2 == '.')
+ {
+ char ch3 = pszPath[1];
+ if ( ch3 == '\\'
+ || ch3 == '/'
+ || ch3 == '\0')
+ {
+ /* Drop a path component. */
+ if (pwszDstComp != pwszDst)
+ pwszDstComp = pwszDstCur = rtNtPathGetPrevComponent(pwszDstCur, pwszDst);
+ /* Hit the start, which is a bit complicated. */
+ else
+ switch (enmAscent)
+ {
+ case kRTNtPathRelativeAscent_Allow:
+ if (*phRootDir != NULL)
+ {
+ RTUtf16Free(pwszDst);
+ rc = RTNtPathFromHandle(pNtName, *phRootDir, cwcMax + 2);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ *phRootDir = NULL;
+ pwszDst = pNtName->Buffer;
+ pwszDstCur = &pwszDst[pNtName->Length / sizeof(RTUTF16)];
+ if ( pwszDst != pwszDstCur
+ && pwszDstCur[-1] != '\\'
+ && pwszDstCur[-1] != '/')
+ *pwszDstCur++ = '\\';
+ pwszDstComp = pwszDstCur = rtNtPathGetPrevComponent(pwszDstCur, pwszDst);
+ }
+ /* else: ignore attempt to ascend beyond the NT root (won't get here). */
+ break;
+
+ case kRTNtPathRelativeAscent_Ignore:
+ /* nothing to do here */
+ break;
+
+ default:
+ case kRTNtPathRelativeAscent_Fail:
+ RTUtf16Free(pwszDst);
+ return VERR_PATH_NOT_FOUND;
+ }
+
+ if (ch3 == '\0')
+ {
+ *pwszDstCur = '\0';
+ pNtName->Length = (uint16_t)((uintptr_t)pwszDstCur - (uintptr_t)pwszDst);
+ if (!fMustReturnAbsolute || *phRootDir == NULL)
+ return VINF_SUCCESS;
+ return rtNtPathRelativeToAbs(pNtName, phRootDir);
+ }
+ pszPath += 2;
+ break;
+ }
+ }
+ }
+
+ /* Neither '.' nor '..'. */
+ pwszDstCur = RTUtf16PutCp(pwszDstCur, '.');
+ break;
+
+ case '\0':
+ *pwszDstCur = '\0';
+ pNtName->Length = (uint16_t)((uintptr_t)pwszDstCur - (uintptr_t)pwszDst);
+ if (!fMustReturnAbsolute || *phRootDir == NULL)
+ return VINF_SUCCESS;
+ return rtNtPathRelativeToAbs(pNtName, phRootDir);
+ }
+ }
+ }
+}
+
+
+/**
+ * Frees the native path and root handle.
+ *
+ * @param pNtName The NT path after a successful rtNtPathToNative
+ * call or RTNtPathRelativeFromUtf8.
+ * @param phRootDir The root handle variable from rtNtPathToNative,
+ * but NOT RTNtPathRelativeFromUtf8.
+ */
+static void rtNtPathFreeNative(struct _UNICODE_STRING *pNtName, PHANDLE phRootDir)
+{
+ RTUtf16Free(pNtName->Buffer);
+ pNtName->Buffer = NULL;
+
+ RT_NOREF_PV(phRootDir); /* never returned by rtNtPathToNative, shouldn't be freed in connection with RTNtPathRelativeFromUtf8 */
+}
+
+
+/**
+ * Frees the native path and root handle.
+ *
+ * @param pNtName The NT path after a successful rtNtPathToNative
+ * call or RTNtPathRelativeFromUtf8.
+ * @param phRootDir The root handle variable from rtNtPathToNative,
+ */
+RTDECL(void) RTNtPathFree(struct _UNICODE_STRING *pNtName, HANDLE *phRootDir)
+{
+ rtNtPathFreeNative(pNtName, phRootDir);
+}
+
+
+/**
+ * Wrapper around NtCreateFile.
+ *
+ * @returns IPRT status code.
+ * @param pszPath The UTF-8 path.
+ * @param fDesiredAccess See NtCreateFile.
+ * @param fFileAttribs See NtCreateFile.
+ * @param fShareAccess See NtCreateFile.
+ * @param fCreateDisposition See NtCreateFile.
+ * @param fCreateOptions See NtCreateFile.
+ * @param fObjAttribs The OBJECT_ATTRIBUTES::Attributes value, see
+ * NtCreateFile and InitializeObjectAttributes.
+ * @param phHandle Where to return the handle.
+ * @param puAction Where to return the action taken. Optional.
+ */
+RTDECL(int) RTNtPathOpen(const char *pszPath, ACCESS_MASK fDesiredAccess, ULONG fFileAttribs, ULONG fShareAccess,
+ ULONG fCreateDisposition, ULONG fCreateOptions, ULONG fObjAttribs,
+ PHANDLE phHandle, PULONG_PTR puAction)
+{
+ *phHandle = RTNT_INVALID_HANDLE_VALUE;
+
+ HANDLE hRootDir;
+ UNICODE_STRING NtName;
+ int rc = rtNtPathToNative(&NtName, &hRootDir, pszPath);
+ if (RT_SUCCESS(rc))
+ {
+ HANDLE hFile = RTNT_INVALID_HANDLE_VALUE;
+ IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
+ OBJECT_ATTRIBUTES ObjAttr;
+ InitializeObjectAttributes(&ObjAttr, &NtName, fObjAttribs, hRootDir, NULL);
+
+ NTSTATUS rcNt = NtCreateFile(&hFile,
+ fDesiredAccess,
+ &ObjAttr,
+ &Ios,
+ NULL /* AllocationSize*/,
+ fFileAttribs,
+ fShareAccess,
+ fCreateDisposition,
+ fCreateOptions,
+ NULL /*EaBuffer*/,
+ 0 /*EaLength*/);
+ if (NT_SUCCESS(rcNt))
+ {
+ if (puAction)
+ *puAction = Ios.Information;
+ *phHandle = hFile;
+ rc = VINF_SUCCESS;
+ }
+ else
+ rc = RTErrConvertFromNtStatus(rcNt);
+ rtNtPathFreeNative(&NtName, &hRootDir);
+ }
+ return rc;
+}
+
+
+/**
+ * Wrapper around NtCreateFile.
+ *
+ * @returns IPRT status code.
+ * @param pszPath The UTF-8 path.
+ * @param fDesiredAccess See NtCreateFile.
+ * @param fShareAccess See NtCreateFile.
+ * @param fCreateOptions See NtCreateFile.
+ * @param fObjAttribs The OBJECT_ATTRIBUTES::Attributes value, see
+ * NtCreateFile and InitializeObjectAttributes.
+ * @param phHandle Where to return the handle.
+ * @param pfObjDir If not NULL, the variable pointed to will be set
+ * to @c true if we opened an object directory and
+ * @c false if we opened an directory file (normal
+ * directory).
+ */
+RTDECL(int) RTNtPathOpenDir(const char *pszPath, ACCESS_MASK fDesiredAccess, ULONG fShareAccess, ULONG fCreateOptions,
+ ULONG fObjAttribs, PHANDLE phHandle, bool *pfObjDir)
+{
+ *phHandle = RTNT_INVALID_HANDLE_VALUE;
+
+ HANDLE hRootDir;
+ UNICODE_STRING NtName;
+ int rc = rtNtPathToNative(&NtName, &hRootDir, pszPath);
+ if (RT_SUCCESS(rc))
+ {
+ if (pfObjDir)
+ {
+ *pfObjDir = false;
+#ifdef IPRT_WITH_NT_PATH_PASSTHRU
+ if ( !RTPATH_IS_SLASH(pszPath[0])
+ || !RTPATH_IS_SLASH(pszPath[1])
+ || pszPath[2] != ':'
+ || pszPath[3] != 'i'
+ || pszPath[4] != 'p'
+ || pszPath[5] != 'r'
+ || pszPath[6] != 't'
+ || pszPath[7] != 'n'
+ || pszPath[8] != 't'
+ || pszPath[9] != ':'
+ || !RTPATH_IS_SLASH(pszPath[10]) )
+#endif
+ pfObjDir = NULL;
+ }
+ rc = RTNtPathOpenDirEx(hRootDir, &NtName, fDesiredAccess, fShareAccess, fCreateOptions, fObjAttribs, phHandle, pfObjDir);
+ rtNtPathFreeNative(&NtName, &hRootDir);
+ }
+ return rc;
+}
+
+
+
+/**
+ * Wrapper around NtCreateFile, extended version.
+ *
+ * @returns IPRT status code.
+ * @param hRootDir The root director the path is relative to. NULL
+ * if none.
+ * @param pNtName The NT path.
+ * @param fDesiredAccess See NtCreateFile.
+ * @param fShareAccess See NtCreateFile.
+ * @param fCreateOptions See NtCreateFile.
+ * @param fObjAttribs The OBJECT_ATTRIBUTES::Attributes value, see
+ * NtCreateFile and InitializeObjectAttributes.
+ * @param phHandle Where to return the handle.
+ * @param pfObjDir If not NULL, the variable pointed to will be set
+ * to @c true if we opened an object directory and
+ * @c false if we opened an directory file (normal
+ * directory).
+ */
+RTDECL(int) RTNtPathOpenDirEx(HANDLE hRootDir, struct _UNICODE_STRING *pNtName, ACCESS_MASK fDesiredAccess, ULONG fShareAccess,
+ ULONG fCreateOptions, ULONG fObjAttribs, PHANDLE phHandle, bool *pfObjDir)
+{
+ *phHandle = RTNT_INVALID_HANDLE_VALUE;
+
+ HANDLE hFile = RTNT_INVALID_HANDLE_VALUE;
+ IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
+ OBJECT_ATTRIBUTES ObjAttr;
+ InitializeObjectAttributes(&ObjAttr, pNtName, fObjAttribs, hRootDir, NULL);
+
+ NTSTATUS rcNt = NtCreateFile(&hFile,
+ fDesiredAccess,
+ &ObjAttr,
+ &Ios,
+ NULL /* AllocationSize*/,
+ FILE_ATTRIBUTE_NORMAL,
+ fShareAccess,
+ FILE_OPEN,
+ fCreateOptions,
+ NULL /*EaBuffer*/,
+ 0 /*EaLength*/);
+ if (NT_SUCCESS(rcNt))
+ {
+ if (pfObjDir)
+ *pfObjDir = false;
+ *phHandle = hFile;
+ return VINF_SUCCESS;
+ }
+
+ /*
+ * Try add a slash in case this is a device object with a file system attached.
+ */
+ if ( rcNt == STATUS_INVALID_PARAMETER
+ && pNtName->Length < _64K - 4
+ && ( pNtName->Length == 0
+ || pNtName->Buffer[pNtName->Length / sizeof(RTUTF16)] != '\\') )
+ {
+ UNICODE_STRING NtTmp;
+ NtTmp.Length = pNtName->Length + 2;
+ NtTmp.MaximumLength = NtTmp.Length + 2;
+ NtTmp.Buffer = (PRTUTF16)RTMemTmpAlloc(NtTmp.MaximumLength);
+ if (NtTmp.Buffer)
+ {
+ memcpy(NtTmp.Buffer, pNtName->Buffer, pNtName->Length);
+ NtTmp.Buffer[pNtName->Length / sizeof(RTUTF16)] = '\\';
+ NtTmp.Buffer[pNtName->Length / sizeof(RTUTF16) + 1] = '\0';
+
+ hFile = RTNT_INVALID_HANDLE_VALUE;
+ Ios.Status = -1;
+ Ios.Information = 0;
+ ObjAttr.ObjectName = &NtTmp;
+
+ rcNt = NtCreateFile(&hFile,
+ fDesiredAccess,
+ &ObjAttr,
+ &Ios,
+ NULL /* AllocationSize*/,
+ FILE_ATTRIBUTE_NORMAL,
+ fShareAccess,
+ FILE_OPEN,
+ fCreateOptions,
+ NULL /*EaBuffer*/,
+ 0 /*EaLength*/);
+ RTMemTmpFree(NtTmp.Buffer);
+ if (NT_SUCCESS(rcNt))
+ {
+ if (pfObjDir)
+ *pfObjDir = false;
+ *phHandle = hFile;
+ return VINF_SUCCESS;
+ }
+ ObjAttr.ObjectName = pNtName;
+ }
+ }
+
+ /*
+ * Try open it as a directory object if it makes sense.
+ */
+ if ( pfObjDir
+ && ( rcNt == STATUS_OBJECT_NAME_INVALID
+ || rcNt == STATUS_OBJECT_TYPE_MISMATCH ))
+ {
+ /* Strip trailing slash. */
+ struct _UNICODE_STRING NtName2 = *pNtName;
+ if ( NtName2.Length > 2
+ && RTPATH_IS_SLASH(NtName2.Buffer[(NtName2.Length / 2) - 1]))
+ NtName2.Length -= 2;
+ ObjAttr.ObjectName = &NtName2;
+
+ /* Rought conversion of the access flags. */
+ ULONG fObjDesiredAccess = 0;
+ if ( (fDesiredAccess & GENERIC_ALL)
+ || (fDesiredAccess & STANDARD_RIGHTS_ALL) == STANDARD_RIGHTS_ALL)
+ fObjDesiredAccess = DIRECTORY_ALL_ACCESS;
+ else
+ {
+ if (fDesiredAccess & (GENERIC_WRITE | STANDARD_RIGHTS_WRITE | FILE_WRITE_DATA))
+ fObjDesiredAccess |= DIRECTORY_CREATE_OBJECT | DIRECTORY_CREATE_OBJECT | DIRECTORY_CREATE_SUBDIRECTORY;
+
+ if ( (fDesiredAccess & (GENERIC_READ | STANDARD_RIGHTS_READ | FILE_LIST_DIRECTORY))
+ || !fObjDesiredAccess)
+ fObjDesiredAccess |= DIRECTORY_QUERY;
+
+ if (fDesiredAccess & FILE_TRAVERSE)
+ fObjDesiredAccess |= DIRECTORY_TRAVERSE;
+ }
+
+ rcNt = NtOpenDirectoryObject(&hFile, fObjDesiredAccess, &ObjAttr);
+ if (NT_SUCCESS(rcNt))
+ {
+ *pfObjDir = true;
+ *phHandle = hFile;
+ return VINF_SUCCESS;
+ }
+ }
+
+ return RTErrConvertFromNtStatus(rcNt);
+}
+
+
+
+/**
+ * Closes an handled open by rtNtPathOpen.
+ *
+ * @returns IPRT status code
+ * @param hHandle The handle value.
+ */
+RTDECL(int) RTNtPathClose(HANDLE hHandle)
+{
+ NTSTATUS rcNt = NtClose(hHandle);
+ if (NT_SUCCESS(rcNt))
+ return VINF_SUCCESS;
+ return RTErrConvertFromNtStatus(rcNt);
+}
+
diff --git a/src/VBox/Runtime/r3/nt/time-nt.cpp b/src/VBox/Runtime/r3/nt/time-nt.cpp
new file mode 100644
index 00000000..f60250e6
--- /dev/null
+++ b/src/VBox/Runtime/r3/nt/time-nt.cpp
@@ -0,0 +1,228 @@
+/* $Id: time-nt.cpp $ */
+/** @file
+ * IPRT - Time, Windows.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_TIME
+#include "internal-r3-nt.h"
+
+#include <iprt/time.h>
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/errcore.h>
+#include <iprt/ldr.h>
+#include <iprt/uint128.h>
+#include "internal/time.h"
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** Whether we've tried to resolve g_pfnRtlGetSystemTimePrecise or not. */
+static bool g_fInitialized = false;
+/** Pointer to RtlGetSystemTimePrecise, added in 6.2 (windows 8). */
+static PFNRTLGETSYSTEMTIMEPRECISE g_pfnRtlGetSystemTimePrecise = NULL;
+
+
+/**
+ * Initializes globals.
+ */
+static void rtTimeNtInitialize(void)
+{
+ /*
+ * Make sure we don't recurse here when calling into RTLdr.
+ */
+ if (ASMAtomicCmpXchgBool(&g_fInitialized, true, false))
+ {
+ void *pvFunc = RTLdrGetSystemSymbol("ntdll.dll", "RtlGetSystemTimePrecise");
+ if (pvFunc)
+ ASMAtomicWritePtr((void * volatile *)&g_pfnRtlGetSystemTimePrecise, pvFunc);
+ ASMCompilerBarrier();
+ }
+}
+
+
+static uint64_t rtTimeGetSystemNanoTS(void)
+{
+ if (RT_UNLIKELY(!g_fInitialized))
+ rtTimeNtInitialize();
+
+ KUSER_SHARED_DATA volatile *pUserSharedData = (KUSER_SHARED_DATA volatile *)MM_SHARED_USER_DATA_VA;
+
+#if 1
+ /*
+ * If there is precise time, get the precise system time and calculate the
+ * interrupt time from it. (Microsoft doesn't expose interrupt time to user
+ * application, which is very unfortunate as there are a lot place where
+ * monotonic time is applicable but developer is "forced" to use wall clock.)
+ */
+ if (g_pfnRtlGetSystemTimePrecise)
+ {
+ for (;;)
+ {
+ uint64_t uUpdateLockBefore;
+ while ((uUpdateLockBefore = pUserSharedData->TimeUpdateLock) & 1)
+ ASMNopPause();
+
+ uint64_t uInterruptTime = *(uint64_t volatile *)&pUserSharedData->InterruptTime;
+ uint64_t uBaselineInterruptTimeQpc = pUserSharedData->BaselineInterruptTimeQpc;
+ uint64_t uQpcInterruptTimeIncrement = pUserSharedData->QpcInterruptTimeIncrement;
+ uint8_t uQpcInterruptTimeIncrementShift = pUserSharedData->QpcInterruptTimeIncrementShift;
+ LARGE_INTEGER QpcValue;
+ RtlQueryPerformanceCounter(&QpcValue);
+
+ if (pUserSharedData->TimeUpdateLock == uUpdateLockBefore)
+ {
+ uint64_t uQpcValue = QpcValue.QuadPart;
+ if (uQpcValue <= uBaselineInterruptTimeQpc)
+ return uInterruptTime * 100;
+
+ /* Calc QPC delta since base line. */
+ uQpcValue -= uBaselineInterruptTimeQpc;
+ uQpcValue--;
+
+ /* Multiply by 10 million. */
+ uQpcValue *= UINT32_C(10000000);
+
+ /* Multiply by QPC interrupt time increment value. */
+ RTUINT128U Tmp128;
+ RTUInt128MulU64ByU64(&Tmp128, uQpcValue, uQpcInterruptTimeIncrement);
+
+ /* Shift the upper 64 bits by the increment shift factor. */
+ uint64_t uResult = Tmp128.s.Hi >> uQpcInterruptTimeIncrementShift;
+
+ /* Add to base interrupt time value. */
+ uResult += uInterruptTime;
+
+ /* Convert from NT unit to nano seconds. */
+ return uResult * 100;
+ }
+
+ ASMNopPause();
+ }
+ }
+#endif
+
+ /*
+ * Just read interrupt time.
+ */
+#if ARCH_BITS >= 64
+ uint64_t uRet = *(uint64_t volatile *)&pUserSharedData->InterruptTime; /* This is what KeQueryInterruptTime does. */
+ uRet *= 100;
+ return uRet;
+#else
+
+ LARGE_INTEGER NtTime;
+ do
+ {
+ NtTime.HighPart = pUserSharedData->InterruptTime.High1Time;
+ NtTime.LowPart = pUserSharedData->InterruptTime.LowPart;
+ } while (pUserSharedData->InterruptTime.High2Time != NtTime.HighPart);
+
+ return (uint64_t)NtTime.QuadPart * 100;
+#endif
+}
+
+
+RTDECL(uint64_t) RTTimeSystemNanoTS(void)
+{
+ return rtTimeGetSystemNanoTS();
+}
+
+
+RTDECL(uint64_t) RTTimeSystemMilliTS(void)
+{
+ return rtTimeGetSystemNanoTS() / RT_NS_1MS;
+}
+
+
+RTDECL(PRTTIMESPEC) RTTimeNow(PRTTIMESPEC pTime)
+{
+ /*
+ * Get the precise time if possible.
+ */
+ if (RT_UNLIKELY(!g_fInitialized))
+ rtTimeNtInitialize();
+ if (g_pfnRtlGetSystemTimePrecise != NULL)
+ return RTTimeSpecSetNtTime(pTime, g_pfnRtlGetSystemTimePrecise());
+
+ /*
+ * Just read system time.
+ */
+ KUSER_SHARED_DATA volatile *pUserSharedData = (KUSER_SHARED_DATA volatile *)MM_SHARED_USER_DATA_VA;
+#ifdef RT_ARCH_AMD64
+ uint64_t uRet = *(uint64_t volatile *)&pUserSharedData->SystemTime; /* This is what KeQuerySystemTime does. */
+ return RTTimeSpecSetNtTime(pTime, uRet);
+#else
+
+ LARGE_INTEGER NtTime;
+ do
+ {
+ NtTime.HighPart = pUserSharedData->SystemTime.High1Time;
+ NtTime.LowPart = pUserSharedData->SystemTime.LowPart;
+ } while (pUserSharedData->SystemTime.High2Time != NtTime.HighPart);
+ return RTTimeSpecSetNtTime(pTime, NtTime.QuadPart);
+#endif
+}
+
+
+RTDECL(PRTTIMESPEC) RTTimeLocalNow(PRTTIMESPEC pTime)
+{
+ return RTTimeSpecAddNano(RTTimeNow(pTime), RTTimeLocalDeltaNano());
+}
+
+
+RTDECL(int64_t) RTTimeLocalDeltaNano(void)
+{
+ /*
+ * UTC = local + TimeZoneBias; The bias is given in NT units.
+ */
+ KUSER_SHARED_DATA volatile *pUserSharedData = (KUSER_SHARED_DATA volatile *)MM_SHARED_USER_DATA_VA;
+ LARGE_INTEGER Delta;
+#if ARCH_BITS == 64
+ Delta.QuadPart = *(int64_t volatile *)&pUserSharedData->TimeZoneBias;
+#else
+ do
+ {
+ Delta.HighPart = pUserSharedData->TimeZoneBias.High1Time;
+ Delta.LowPart = pUserSharedData->TimeZoneBias.LowPart;
+ } while (pUserSharedData->TimeZoneBias.High2Time != Delta.HighPart);
+#endif
+ return Delta.QuadPart * -100;
+}
+