diff options
author | Lorry Tar Creator <lorry-tar-importer@baserock.org> | 2014-03-26 19:21:20 +0000 |
---|---|---|
committer | <> | 2014-05-08 15:03:54 +0000 |
commit | fb123f93f9f5ce42c8e5785d2f8e0edaf951740e (patch) | |
tree | c2103d76aec5f1f10892cd1d3a38e24f665ae5db /src/VBox/Runtime/r3/nt | |
parent | 58ed4748338f9466599adfc8a9171280ed99e23f (diff) | |
download | VirtualBox-master.tar.gz |
Imported from /home/lorry/working-area/delta_VirtualBox/VirtualBox-4.3.10.tar.bz2.HEADVirtualBox-4.3.10master
Diffstat (limited to 'src/VBox/Runtime/r3/nt')
-rw-r--r-- | src/VBox/Runtime/r3/nt/Makefile.kup | 0 | ||||
-rw-r--r-- | src/VBox/Runtime/r3/nt/direnum-r3-nt.cpp | 697 | ||||
-rw-r--r-- | src/VBox/Runtime/r3/nt/fs-nt.cpp | 284 | ||||
-rw-r--r-- | src/VBox/Runtime/r3/nt/internal-r3-nt.h | 140 | ||||
-rw-r--r-- | src/VBox/Runtime/r3/nt/pathint-nt.cpp | 356 |
5 files changed, 1477 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/direnum-r3-nt.cpp b/src/VBox/Runtime/r3/nt/direnum-r3-nt.cpp new file mode 100644 index 00000000..f93f6e4e --- /dev/null +++ b/src/VBox/Runtime/r3/nt/direnum-r3-nt.cpp @@ -0,0 +1,697 @@ +/* $Id: direnum-r3-nt.cpp $ */ +/** @file + * IPRT - Directory Enumeration, Native NT. + */ + +/* + * Copyright (C) 2006-2013 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE 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. + */ + + +/******************************************************************************* +* 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 "internal/fs.h" +#include "internal/dir.h" + + +/******************************************************************************* +* Defined Constants And Macros * +*******************************************************************************/ +/** Whether to return a single record (TRUE) or multiple (FALSE)o. */ +#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 structus 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(RTDIR); +} + + +int rtDirNativeOpen(PRTDIR pDir, char *pszPathBuf) +{ + /* + * 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; +#endif + rc = rtNtPathOpenDir(pszPathBuf, + FILE_READ_DATA | SYNCHRONIZE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + FILE_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT | FILE_SYNCHRONOUS_IO_NONALERT, + OBJ_CASE_INSENSITIVE, + &pDir->hDir, +#ifdef IPRT_WITH_NT_PATH_PASSTHRU + &fObjDir +#else + NULL +#endif + ); + if (RT_SUCCESS(rc)) + { + /* + * Init data. + */ + pDir->fDataUnread = false; /* spelling it out */ +#ifdef IPRT_WITH_NT_PATH_PASSTHRU + if (fObjDir) + pDir->enmInfoClass = FileMaximumInformation; /* object directory. */ +#endif + } + return rc; +} + + +RTDECL(int) RTDirClose(PRTDIR pDir) +{ + /* + * 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 != MY_INVALID_HANDLE_VALUE) + { + int rc = rtNtPathClose(pDir->hDir); + AssertRC(rc); + pDir->hDir = MY_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(PRTDIR pThis) +{ +#ifdef RTDIR_NT_STRICT +# 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]; + 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 + + return VINF_SUCCESS; +} + + +/** + * Advances the buffer pointer. + * + * @param pThis The directory instance data. + */ +static int rtDirNtAdvanceBuffer(PRTDIR 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(PRTDIR 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. + */ + bool fFirst = false; + if (!pThis->pabBuffer) + { + fFirst = false; + pThis->cbBufferAlloc = _256K; + 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; + } + } + + /* + * Read more. + */ + NTSTATUS rcNt; + IO_STATUS_BLOCK Ios = MY_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 */, + FALSE /*RestartScan*/, + &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, + FALSE /*RestartScan */); + } + else + { + /* + * The first time around we have figure which info class we can use. + * We prefer one which gives us file IDs, but we'll settle for less. + */ + pThis->enmInfoClass = FileIdBothDirectoryInformation; + rcNt = NtQueryDirectoryFile(pThis->hDir, + NULL /* Event */, + NULL /* ApcRoutine */, + NULL /* ApcContext */, + &Ios, + pThis->pabBuffer, + pThis->cbBufferAlloc, + pThis->enmInfoClass, + RTDIR_NT_SINGLE_RECORD /*ReturnSingleEntry */, + pThis->pNtFilterStr, + FALSE /*RestartScan */); + if (!NT_SUCCESS(rcNt)) + { + 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, + FALSE /*RestartScan */); + } + } + if (!NT_SUCCESS(rcNt)) + { + if (rcNt == STATUS_NO_MORE_FILES || rcNt == STATUS_NO_MORE_ENTRIES) + return VERR_NO_MORE_FILES; + return RTErrConvertFromNtStatus(rcNt); + } + Assert(Ios.Information > sizeof(*pThis->uCurData.pBoth)); + + /* + * 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(PRTDIR 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(PRTDIR 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(PRTDIR pDir, PRTDIRENTRY pDirEntry, size_t *pcbDirEntry) +{ + 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=%d (min %d)\n", *pcbDirEntry, RT_OFFSETOF(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_OFFSETOF(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: + 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(PRTDIR pDir, PRTDIRENTRYEX pDirEntry, size_t *pcbDirEntry, + RTFSOBJATTRADD enmAdditionalAttribs, uint32_t fFlags) +{ + 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=%d (min %d)\n", *pcbDirEntry, RT_OFFSETOF(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_OFFSETOF(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); + } +#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 = ~0U; + pDirEntry->Info.Attr.u.Unix.gid = ~0U; + pDirEntry->Info.Attr.u.Unix.cHardlinks = 1; + pDirEntry->Info.Attr.u.Unix.INodeIdDevice = 0; /** @todo Use the volume serial number (see GetFileInformationByHandle). */ + pDirEntry->Info.Attr.u.Unix.INodeId = 0; /** @todo Use the fileid (see GetFileInformationByHandle). */ + 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 = ~0U; + 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 = ~0U; + 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); +} + 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..314059de --- /dev/null +++ b/src/VBox/Runtime/r3/nt/fs-nt.cpp @@ -0,0 +1,284 @@ +/* $Id: fs-nt.cpp $ */ +/** @file + * IPRT - File System, Native NT. + */ + +/* + * Copyright (C) 2006-2013 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE 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. + */ + + +/******************************************************************************* +* Header Files * +*******************************************************************************/ +#define LOG_GROUP RTLOGGROUP_FS +#include "internal-r3-nt.h" + +#include <iprt/fs.h> +#include <iprt/path.h> +#include <iprt/string.h> +#include <iprt/param.h> +#include <iprt/err.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)) + { + /* + * Get the volume information. + */ + FILE_FS_SIZE_INFORMATION FsSizeInfo; + IO_STATUS_BLOCK Ios = MY_IO_STATUS_BLOCK_INITIALIZER; + NTSTATUS rcNt = NtQueryVolumeInformationFile(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; + } + + 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); + + 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 = MY_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 = MY_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(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)) + { + /* + * 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 = MY_IO_STATUS_BLOCK_INITIALIZER; + NTSTATUS rcNt = NtQueryVolumeInformationFile(hFile, &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("VBoxSharedFolderFS")) + *penmType = RTFSTYPE_VBOXSHF; +#undef IS_FS + } + else + rc = RTErrConvertFromNtStatus(rcNt); + + 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..0a970965 --- /dev/null +++ b/src/VBox/Runtime/r3/nt/internal-r3-nt.h @@ -0,0 +1,140 @@ +/* $Id: internal-r3-nt.h $ */ +/** @file + * IPRT - Internal Header for the Native NT code. + */ + +/* + * Copyright (C) 2010-2012 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE 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. + */ + + +#ifndef ___internal_r3_nt_h___ +#define ___internal_r3_nt_h___ + + +/******************************************************************************* +* Header Files * +*******************************************************************************/ +#include <ntstatus.h> +#ifdef IPRT_NT_USE_WINTERNL +# define WIN32_NO_STATUS +# include <windef.h> +# include <winnt.h> +# include <winternl.h> +# define IPRT_NT_NEED_API_GROUP_1 + +#elif defined(IPRT_NT_USE_WDM) +# include <wdm.h> +# define IPRT_NT_NEED_API_GROUP_1 + +#else +# include <ntifs.h> +#endif +#include "internal/iprt.h" + + +/******************************************************************************* +* Defined Constants And Macros * +*******************************************************************************/ +/** Indicates that we're targetting native NT in the current source. */ +#define RT_USE_NATIVE_NT 1 +/** Initializes a IO_STATUS_BLOCK. */ +#define MY_IO_STATUS_BLOCK_INITIALIZER { STATUS_FAILED_DRIVER_ENTRY, ~(uintptr_t)42 } +/** Similar to INVALID_HANDLE_VALUE in the Windows environment. */ +#define MY_INVALID_HANDLE_VALUE ( (HANDLE)~(uintptr_t)0 ) + +#ifdef DEBUG_bird +/** Enables the "\\!\" NT path pass thru as well as hacks for listing NT object + * directories. */ +# define IPRT_WITH_NT_PATH_PASSTHRU 1 +#endif + + +/******************************************************************************* +* Internal Functions * +*******************************************************************************/ +int rtNtPathOpen(const char *pszPath, ACCESS_MASK fDesiredAccess, ULONG fFileAttribs, ULONG fShareAccess, + ULONG fCreateDisposition, ULONG fCreateOptions, ULONG fObjAttribs, + PHANDLE phHandle, PULONG_PTR puDisposition); +int rtNtPathOpenDir(const char *pszPath, ACCESS_MASK fDesiredAccess, ULONG fShareAccess, ULONG fCreateOptions, + ULONG fObjAttribs, PHANDLE phHandle, bool *pfObjDir); +int rtNtPathClose(HANDLE hHandle); + + +/** + * 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 cb1 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; +} + + +/******************************************************************************* +* NT APIs * +*******************************************************************************/ + +RT_C_DECLS_BEGIN + +#ifdef IPRT_NT_NEED_API_GROUP_1 + +typedef struct _FILE_FS_ATTRIBUTE_INFORMATION +{ + ULONG FileSystemAttributes; + LONG MaximumComponentNameLength; + ULONG FileSystemNameLength; + WCHAR FileSystemName[1]; +} FILE_FS_ATTRIBUTE_INFORMATION; +typedef FILE_FS_ATTRIBUTE_INFORMATION *PFILE_FS_ATTRIBUTE_INFORMATION; +extern "C" NTSTATUS NTAPI NtQueryVolumeInformationFile(HANDLE, PIO_STATUS_BLOCK, PVOID, ULONG, FS_INFORMATION_CLASS); + +#endif + +NTSTATUS NTAPI NtOpenDirectoryObject(PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES); + +typedef struct _OBJECT_DIRECTORY_INFORMATION +{ + UNICODE_STRING Name; + UNICODE_STRING TypeName; +} OBJECT_DIRECTORY_INFORMATION; +typedef OBJECT_DIRECTORY_INFORMATION *POBJECT_DIRECTORY_INFORMATION; + +NTSTATUS NTAPI NtQueryDirectoryObject(HANDLE, PVOID, ULONG, BOOLEAN, BOOLEAN, PULONG, PULONG); + + +RT_C_DECLS_END + +#endif + 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..a68f0597 --- /dev/null +++ b/src/VBox/Runtime/r3/nt/pathint-nt.cpp @@ -0,0 +1,356 @@ +/* $Id: pathint-nt.cpp $ */ +/** @file + * IPRT - Native NT, Internal Path stuff. + */ + +/* + * Copyright (C) 2006-2013 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE 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. + */ + + +/******************************************************************************* +* Header Files * +*******************************************************************************/ +#define LOG_GROUP RTLOGGROUP_FS +#include "internal-r3-nt.h" + +#include <iprt/path.h> +#include <iprt/string.h> +#include <iprt/err.h> +#include <iprt/assert.h> + + + +/** + * Handles the pass thru case. + * + * @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 rtNtPathToNativePassThruWin(struct _UNICODE_STRING *pNtName, PHANDLE phRootDir, const char *pszPath) +{ + PRTUTF16 pwszPath = NULL; + size_t cwcLen; + int rc = RTStrToUtf16Ex(pszPath + 1, RTSTR_MAX, &pwszPath, 0, &cwcLen); + if (RT_SUCCESS(rc)) + { + if (cwcLen < _32K - 1) + { + pwszPath[0] = '\\'; + pwszPath[1] = '.'; + pwszPath[2] = '\\'; + + pNtName->Buffer = pwszPath; + pNtName->MaximumLength = pNtName->Length = (uint16_t)(cwcLen * 2); + *phRootDir = NULL; + return VINF_SUCCESS; + } + + RTUtf16Free(pwszPath); + 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 rtNtPathToNativeToUtf16(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->MaximumLength = pNtName->Length = (uint16_t)(cwcLen * 2); + *phRootDir = NULL; + return VINF_SUCCESS; + } + + RTUtf16Free(pwszPath); + rc = VERR_FILENAME_TOO_LONG; + } + return rc; +} + + +/** + * Converts a path to NT format and encoding. + * + * @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 rtNtPathToNative(struct _UNICODE_STRING *pNtName, PHANDLE phRootDir, const char *pszPath) +{ + static char const s_szPrefixUnc[] = "\\??\\UNC\\"; + static char const s_szPrefix[] = "\\??\\"; + + /* + * Very simple conversion of a win32-like path into an NT path. + */ + const char *pszPrefix = s_szPrefix; + size_t cchPrefix = sizeof(s_szPrefix) - 1; + size_t cchSkip = 0; + + if ( RTPATH_IS_SLASH(pszPath[0]) + && RTPATH_IS_SLASH(pszPath[1]) + && !RTPATH_IS_SLASH(pszPath[2]) + && pszPath[2]) + { + if ( pszPath[2] == '?' + && RTPATH_IS_SLASH(pszPath[3])) + return rtNtPathToNativePassThruWin(pNtName, phRootDir, pszPath); + +#ifdef IPRT_WITH_NT_PATH_PASSTHRU + /* Special hack: The path starts with "\\\\!\\", we will skip past the bang and pass it thru. */ + if ( pszPath[2] == '!' + && RTPATH_IS_SLASH(pszPath[3])) + return rtNtPathToNativeToUtf16(pNtName, phRootDir, pszPath + 3); +#endif + + 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 */ + pszPrefix = s_szPrefixUnc; + cchPrefix = sizeof(s_szPrefixUnc) - 1; + cchSkip = 2; + } + } + + /* + * Straighten out all .. and uncessary . references and convert slashes. + */ + char szPath[RTPATH_MAX]; + int rc = RTPathAbs(pszPath, &szPath[cchPrefix - cchSkip], sizeof(szPath) - (cchPrefix - cchSkip)); + if (RT_FAILURE(rc)) + return rc; + + /* + * Add prefix and convert it to UTF16. + */ + memcpy(szPath, pszPrefix, cchPrefix); + return rtNtPathToNativeToUtf16(pNtName, phRootDir, szPath); +} + + +/** + * Frees the native path and root handle. + * + * @param pNtName The NT path after a successful rtNtPathToNative + * call. + * @param phRootDir The root handle variable after a + * rtNtPathToNative. + */ +void rtNtPathFreeNative(struct _UNICODE_STRING *pNtName, PHANDLE phRootDir) +{ + RTUtf16Free(pNtName->Buffer); + pNtName->Buffer = NULL; +} + + +/** + * 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. + */ +int rtNtPathOpen(const char *pszPath, ACCESS_MASK fDesiredAccess, ULONG fFileAttribs, ULONG fShareAccess, + ULONG fCreateDisposition, ULONG fCreateOptions, ULONG fObjAttribs, + PHANDLE phHandle, PULONG_PTR puAction) +{ + *phHandle = MY_INVALID_HANDLE_VALUE; + + HANDLE hRootDir; + UNICODE_STRING NtName; + int rc = rtNtPathToNative(&NtName, &hRootDir, pszPath); + if (RT_SUCCESS(rc)) + { + HANDLE hFile = MY_INVALID_HANDLE_VALUE; + IO_STATUS_BLOCK Ios = MY_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 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 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). + */ +int rtNtPathOpenDir(const char *pszPath, ACCESS_MASK fDesiredAccess, ULONG fShareAccess, ULONG fCreateOptions, + ULONG fObjAttribs, PHANDLE phHandle, bool *pfObjDir) +{ + *phHandle = MY_INVALID_HANDLE_VALUE; + + HANDLE hRootDir; + UNICODE_STRING NtName; + int rc = rtNtPathToNative(&NtName, &hRootDir, pszPath); + if (RT_SUCCESS(rc)) + { + HANDLE hFile = MY_INVALID_HANDLE_VALUE; + IO_STATUS_BLOCK Ios = MY_IO_STATUS_BLOCK_INITIALIZER; + OBJECT_ATTRIBUTES ObjAttr; + InitializeObjectAttributes(&ObjAttr, &NtName, 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; + rc = VINF_SUCCESS; + } +#ifdef IPRT_WITH_NT_PATH_PASSTHRU + else if ( pfObjDir + && (rcNt == STATUS_OBJECT_NAME_INVALID || rcNt == STATUS_OBJECT_TYPE_MISMATCH) + && RTPATH_IS_SLASH(pszPath[0]) + && RTPATH_IS_SLASH(pszPath[1]) + && pszPath[2] == '!' + && RTPATH_IS_SLASH(pszPath[3])) + { + /* Strip trailing slash. */ + if ( NtName.Length > 2 + && RTPATH_IS_SLASH(NtName.Buffer[(NtName.Length / 2) - 1])) + NtName.Length -= 2; + + /* Rought conversion of the access flags. */ + ULONG fObjDesiredAccess = 0; + if (fDesiredAccess & (GENERIC_ALL | STANDARD_RIGHTS_ALL)) + fObjDesiredAccess = DIRECTORY_ALL_ACCESS; + else + { + if (fDesiredAccess & (FILE_GENERIC_WRITE | GENERIC_WRITE | STANDARD_RIGHTS_WRITE)) + fObjDesiredAccess |= DIRECTORY_CREATE_OBJECT | DIRECTORY_CREATE_OBJECT; + if ( (fDesiredAccess & (FILE_LIST_DIRECTORY | FILE_GENERIC_READ | GENERIC_READ | STANDARD_RIGHTS_READ)) + || !fObjDesiredAccess) + fObjDesiredAccess |= DIRECTORY_QUERY | FILE_LIST_DIRECTORY; + } + + rcNt = NtOpenDirectoryObject(&hFile, fObjDesiredAccess, &ObjAttr); + if (NT_SUCCESS(rcNt)) + { + *pfObjDir = true; + *phHandle = hFile; + rc = VINF_SUCCESS; + } + else + rc = RTErrConvertFromNtStatus(rcNt); + } +#endif + else + rc = RTErrConvertFromNtStatus(rcNt); + rtNtPathFreeNative(&NtName, &hRootDir); + } + return rc; +} + + +/** + * Closes an handled open by rtNtPathOpen. + * + * @returns IPRT status code + * @param hHandle The handle value. + */ +int rtNtPathClose(HANDLE hHandle) +{ + NTSTATUS rcNt = NtClose(hHandle); + if (NT_SUCCESS(rcNt)) + return VINF_SUCCESS; + return RTErrConvertFromNtStatus(rcNt); +} + |