VirtualBox

Changeset 69705 in vbox for trunk/src/VBox/Runtime/r3/nt


Ignore:
Timestamp:
Nov 15, 2017 4:42:59 PM (7 years ago)
Author:
vboxsync
svn:sync-xref-src-repo-rev:
119080
Message:

IPRT: VFS and NT path handling fixes.

  • Rewrote RTDirQueryInfo for NT. When RTDirOpen* now opens directories, it will request read-attribute access in additions to listing.
  • Major adjustment of the VFS path parser. It now accepts both slashes and will deal differently with '..' in operations on directories.
  • Implemented native RTDirRelPathQueryInfo for NT.
  • NT directory object (NT namespace objects, not file system dirs) fixes for NT specific RTDirRel APIs.
Location:
trunk/src/VBox/Runtime/r3/nt
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Runtime/r3/nt/RTPathQueryInfo-nt.cpp

    r69111 r69705  
    3636#include <iprt/time.h>
    3737#include "internal/fs.h"
     38#include "internal/path.h"
     39
     40
     41/*********************************************************************************************************************************
     42*   Defined Constants And Macros                                                                                                 *
     43*********************************************************************************************************************************/
     44/** Helper for comparing a UNICODE_STRING with a string litteral. */
     45#define ARE_UNICODE_STRINGS_EQUAL(a_UniStr, a_wszType) \
     46    (   (a_UniStr)->Length == sizeof(a_wszType) - sizeof(RTUTF16) \
     47     && memcmp((a_UniStr)->Buffer, a_wszType, sizeof(a_wszType) - sizeof(RTUTF16)) == 0)
    3848
    3949
     
    5565
    5666
    57 RTR3DECL(int) RTPathQueryInfo(const char *pszPath, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAdditionalAttribs)
     67/**
     68 * Splits up an NT path into directory and filename.
     69 *
     70 * @param   pNtName             The path to split.
     71 * @param   pNtParent           Where to return the directory path.
     72 * @param   pNtFilename         Where to return the filename part.
     73 * @param   fNoParentDirSlash   Whether to make sure the directory path doesn't
     74 *                              end with a slash (except root).
     75 */
     76static void ntPathNtSplitName(UNICODE_STRING const *pNtName, UNICODE_STRING *pNtParent, UNICODE_STRING *pNtFilename,
     77                              bool fNoParentDirSlash)
    5878{
    59     return RTPathQueryInfoEx(pszPath, pObjInfo, enmAdditionalAttribs, RTPATH_F_ON_LINK);
     79    PRTUTF16 pwszBuffer = pNtName->Buffer;
     80    size_t   off        = pNtName->Length / sizeof(RTUTF16);
     81
     82    /* Skip trailing slash if present. */
     83    if (   off > 0
     84        && pwszBuffer[off - 1] == '\\')
     85        off--;
     86
     87    /* Find the slash before that. */
     88    RTUTF16  wc;
     89    while (   off > 0
     90           && (wc = pwszBuffer[off - 1]) != '\\'
     91           && wc != '/')
     92        off--;
     93    if (off != 0)
     94    {
     95        pNtParent->Buffer        = pwszBuffer;
     96        pNtParent->MaximumLength = pNtParent->Length = (USHORT)(off * sizeof(RTUTF16));
     97    }
     98    else
     99    {
     100        AssertFailed(); /* This is impossible and won't work (NT doesn't know '.' or '..').  */
     101        /** @todo query the whole path as it is possible relative. Use the buffer for
     102         *        temporary name storage. */
     103        pNtParent->Buffer        = L".";
     104        pNtParent->Length        = 1 * sizeof(RTUTF16);
     105        pNtParent->MaximumLength = 2 * sizeof(RTUTF16);
     106    }
     107
     108    pNtFilename->Buffer        = &pwszBuffer[off];
     109    pNtFilename->Length        = pNtName->Length        - (USHORT)(off * sizeof(RTUTF16));
     110    pNtFilename->MaximumLength = pNtName->MaximumLength - (USHORT)(off * sizeof(RTUTF16));
     111
     112    while (   fNoParentDirSlash
     113           && pNtParent->Length > sizeof(RTUTF16)
     114           && pNtParent->Buffer[pNtParent->Length / sizeof(RTUTF16) - 1] == '\\')
     115        pNtParent->Length -= sizeof(RTUTF16);
    60116}
    61117
    62 #if 1
     118
     119/**
     120 * Deals with enmAddAttr != RTFSOBJATTRADD_UNIX.
     121 *
     122 * @returns IPRT status code (usually @a rc).
     123 * @param   rc                  The return code.
     124 * @param   pObjInfo            The info to complete.
     125 * @param   enmAddAttr          What to complete it with.  Caller should fill in
     126 *                              RTFSOBJATTRADD_UNIX.
     127 */
     128static int rtPathNtQueryInfoFillInDummyData(int rc, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
     129{
     130    switch (enmAddAttr)
     131    {
     132        case RTFSOBJATTRADD_UNIX:
     133            pObjInfo->Attr.enmAdditional          = RTFSOBJATTRADD_UNIX;
     134            break;
     135
     136        case RTFSOBJATTRADD_NOTHING:
     137            pObjInfo->Attr.enmAdditional          = RTFSOBJATTRADD_NOTHING;
     138            break;
     139
     140        case RTFSOBJATTRADD_UNIX_OWNER:
     141            pObjInfo->Attr.enmAdditional          = RTFSOBJATTRADD_UNIX_OWNER;
     142            pObjInfo->Attr.u.UnixOwner.uid        = NIL_RTUID;
     143            pObjInfo->Attr.u.UnixOwner.szName[0]  = '\0'; /** @todo return something sensible here. */
     144            break;
     145
     146        case RTFSOBJATTRADD_UNIX_GROUP:
     147            pObjInfo->Attr.enmAdditional          = RTFSOBJATTRADD_UNIX_GROUP;
     148            pObjInfo->Attr.u.UnixGroup.gid        = NIL_RTGID;
     149            pObjInfo->Attr.u.UnixGroup.szName[0]  = '\0';
     150            break;
     151
     152        case RTFSOBJATTRADD_EASIZE:
     153            pObjInfo->Attr.enmAdditional          = RTFSOBJATTRADD_EASIZE;
     154            pObjInfo->Attr.u.EASize.cb            = 0;
     155            break;
     156
     157        default:
     158            AssertMsgFailed(("Impossible!\n"));
     159            rc = VERR_INTERNAL_ERROR;
     160    }
     161    return rc;
     162}
     163
     164
     165/**
     166 * Deal with getting info about something that could be in a directory object.
     167 *
     168 * @returns IPRT status code
     169 * @param   pObjAttr        The NT object attribute.
     170 * @param   pObjInfo        Where to return the info.
     171 * @param   enmAddAttr      Which extra attributes to get (/fake).
     172 * @param   fFlags          The flags.
     173 * @param   pvBuf           Query buffer space.
     174 * @param   cbBuf           Size of the buffer.  ASSUMES lots of space.
     175 */
     176static int rtPathNtQueryInfoInDirectoryObject(OBJECT_ATTRIBUTES *pObjAttr, PRTFSOBJINFO pObjInfo,
     177                                              RTFSOBJATTRADD enmAddAttr, uint32_t fFlags,
     178                                              void *pvBuf, size_t cbBuf)
     179{
     180    RT_NOREF(fFlags);
     181
     182    /*
     183     * Special case: Root dir.
     184     */
     185    if (   pObjAttr->RootDirectory == NULL
     186        && pObjAttr->ObjectName->Length == sizeof(RTUTF16)
     187        && pObjAttr->ObjectName->Buffer[0] == '\\')
     188    {
     189        pObjInfo->cbObject    = 0;
     190        pObjInfo->cbAllocated = 0;
     191        RTTimeSpecSetNtTime(&pObjInfo->BirthTime,         0);
     192        RTTimeSpecSetNtTime(&pObjInfo->AccessTime,        0);
     193        RTTimeSpecSetNtTime(&pObjInfo->ModificationTime,  0);
     194        RTTimeSpecSetNtTime(&pObjInfo->ChangeTime,        0);
     195        pObjInfo->Attr.fMode = RTFS_DOS_DIRECTORY | RTFS_TYPE_DIRECTORY | 0777;
     196        return rtPathNtQueryInfoFillInDummyData(VINF_SUCCESS, pObjInfo, enmAddAttr);
     197    }
     198
     199    /*
     200     * We must open and scan the parent directory object.
     201     */
     202    UNICODE_STRING NtDirName;
     203    UNICODE_STRING NtDirEntry;
     204    ntPathNtSplitName(pObjAttr->ObjectName, &NtDirName, &NtDirEntry, true /*fNoParentDirSlash*/);
     205
     206    while (   NtDirEntry.Length > sizeof(RTUTF16)
     207           && NtDirEntry.Buffer[NtDirEntry.Length / sizeof(RTUTF16) - 1] == '\\')
     208        NtDirEntry.Length -= sizeof(RTUTF16);
     209
     210    pObjAttr->ObjectName = &NtDirName;
     211    HANDLE   hDir = RTNT_INVALID_HANDLE_VALUE;
     212    NTSTATUS rcNt = NtOpenDirectoryObject(&hDir, DIRECTORY_QUERY | DIRECTORY_TRAVERSE, pObjAttr);
     213    if (NT_SUCCESS(rcNt))
     214    {
     215        ULONG uObjDirCtx = 0;
     216        for (;;)
     217        {
     218            ULONG cbReturned = 0;
     219            rcNt = NtQueryDirectoryObject(hDir,
     220                                          pvBuf,
     221                                          (ULONG)cbBuf,
     222                                          FALSE /*ReturnSingleEntry */,
     223                                          FALSE /*RestartScan*/,
     224                                          &uObjDirCtx,
     225                                          &cbReturned);
     226            if (!NT_SUCCESS(rcNt))
     227                break;
     228
     229            for (POBJECT_DIRECTORY_INFORMATION pObjDir = (POBJECT_DIRECTORY_INFORMATION)pvBuf;
     230                 pObjDir->Name.Length != 0;
     231                 pObjDir++)
     232            {
     233                if (   pObjDir->Name.Length == NtDirEntry.Length
     234                    && memcmp(pObjDir->Name.Buffer, NtDirEntry.Buffer, NtDirEntry.Length) == 0)
     235                {
     236                    /*
     237                     * Find it.  Fill in the info we've got and return (see similar code in direnum-r3-nt.cpp).
     238                     */
     239                    NtClose(hDir);
     240
     241                    pObjInfo->cbObject    = 0;
     242                    pObjInfo->cbAllocated = 0;
     243                    RTTimeSpecSetNtTime(&pObjInfo->BirthTime,         0);
     244                    RTTimeSpecSetNtTime(&pObjInfo->AccessTime,        0);
     245                    RTTimeSpecSetNtTime(&pObjInfo->ModificationTime,  0);
     246                    RTTimeSpecSetNtTime(&pObjInfo->ChangeTime,        0);
     247
     248                    if (ARE_UNICODE_STRINGS_EQUAL(&pObjDir->TypeName, L"Directory"))
     249                        pObjInfo->Attr.fMode = RTFS_DOS_DIRECTORY | RTFS_TYPE_DIRECTORY | 0777;
     250                    else if (ARE_UNICODE_STRINGS_EQUAL(&pObjDir->TypeName, L"SymbolicLink"))
     251                        pObjInfo->Attr.fMode = RTFS_DOS_NT_REPARSE_POINT | RTFS_TYPE_SYMLINK | 0777;
     252                    else if (ARE_UNICODE_STRINGS_EQUAL(&pObjDir->TypeName, L"Device"))
     253                        pObjInfo->Attr.fMode = RTFS_DOS_NT_DEVICE | RTFS_TYPE_DEV_CHAR | 0666;
     254                    else
     255                        pObjInfo->Attr.fMode = RTFS_DOS_NT_NORMAL | RTFS_TYPE_FILE | 0666;
     256
     257                    pObjInfo->Attr.enmAdditional = enmAddAttr;
     258                    return rtPathNtQueryInfoFillInDummyData(VINF_SUCCESS, pObjInfo, enmAddAttr);
     259                }
     260            }
     261        }
     262
     263        NtClose(hDir);
     264        if (rcNt == STATUS_NO_MORE_FILES || rcNt == STATUS_NO_MORE_ENTRIES || rcNt == STATUS_NO_SUCH_FILE)
     265            return VERR_FILE_NOT_FOUND;
     266    }
     267    return RTErrConvertFromNtStatus(rcNt);
     268}
     269
     270
     271/**
     272 * Queries information from a file or directory handle.
     273 *
     274 * This is shared between the RTPathQueryInfo, RTFileQueryInfo and
     275 * RTDirQueryInfo code.
     276 *
     277 * @returns IPRT status code.
     278 * @param   hFile               The handle to query information from.  Must have
     279 *                              the necessary privileges.
     280 * @param   pvBuf               Pointer to a scratch buffer.
     281 * @param   cbBuf               The size of the buffer.  This must be large
     282 *                              enough to hold a FILE_ALL_INFORMATION struct.
     283 * @param   pObjInfo            Where to return information about the handle.
     284 * @param   enmAddAttr          What extra info to return.
     285 * @param   pszPath             The path if this is a file (for exe detect).
     286 * @param   uReparseTag         The reparse tag number (0 if not applicable) for
     287 *                              symlink detection/whatnot.
     288 */
     289DECLHIDDEN(int) rtPathNtQueryInfoFromHandle(HANDLE hFile, void *pvBuf, size_t cbBuf, PRTFSOBJINFO pObjInfo,
     290                                            RTFSOBJATTRADD enmAddAttr, const char *pszPath, ULONG uReparseTag)
     291{
     292    Assert(cbBuf >= sizeof(FILE_ALL_INFORMATION));
     293
     294    /** @todo Try optimize this for when RTFSOBJATTRADD_UNIX isn't set? */
     295    IO_STATUS_BLOCK  Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
     296    NTSTATUS rcNt = NtQueryInformationFile(hFile, &Ios, pvBuf, sizeof(FILE_ALL_INFORMATION), FileAllInformation);
     297    if (   NT_SUCCESS(rcNt)
     298        || rcNt == STATUS_BUFFER_OVERFLOW)
     299    {
     300        FILE_ALL_INFORMATION *pAllInfo = (FILE_ALL_INFORMATION *)pvBuf;
     301        pObjInfo->cbObject    = pAllInfo->StandardInformation.EndOfFile.QuadPart;
     302        pObjInfo->cbAllocated = pAllInfo->StandardInformation.AllocationSize.QuadPart;
     303        RTTimeSpecSetNtTime(&pObjInfo->BirthTime,         pAllInfo->BasicInformation.CreationTime.QuadPart);
     304        RTTimeSpecSetNtTime(&pObjInfo->AccessTime,        pAllInfo->BasicInformation.LastAccessTime.QuadPart);
     305        RTTimeSpecSetNtTime(&pObjInfo->ModificationTime,  pAllInfo->BasicInformation.LastWriteTime.QuadPart);
     306        RTTimeSpecSetNtTime(&pObjInfo->ChangeTime,        pAllInfo->BasicInformation.ChangeTime.QuadPart);
     307        pObjInfo->Attr.fMode = rtFsModeFromDos(  (pAllInfo->BasicInformation.FileAttributes << RTFS_DOS_SHIFT)
     308                                               & RTFS_DOS_MASK_NT,
     309                                               pszPath, pszPath ? strlen(pszPath) : 0, uReparseTag);
     310        pObjInfo->Attr.enmAdditional = enmAddAttr;
     311        if (enmAddAttr == RTFSOBJATTRADD_UNIX)
     312        {
     313            pObjInfo->Attr.u.Unix.uid             = ~0U;
     314            pObjInfo->Attr.u.Unix.gid             = ~0U;
     315            pObjInfo->Attr.u.Unix.cHardlinks      = RT_MAX(1, pAllInfo->StandardInformation.NumberOfLinks);
     316            pObjInfo->Attr.u.Unix.INodeIdDevice   = 0; /* below */
     317            pObjInfo->Attr.u.Unix.INodeId         = pAllInfo->InternalInformation.IndexNumber.QuadPart;
     318            pObjInfo->Attr.u.Unix.fFlags          = 0;
     319            pObjInfo->Attr.u.Unix.GenerationId    = 0;
     320            pObjInfo->Attr.u.Unix.Device          = 0;
     321
     322            /* Get the serial number. */
     323            rcNt = NtQueryVolumeInformationFile(hFile, &Ios, pvBuf, (ULONG)RT_MIN(cbBuf, _2K), FileFsVolumeInformation);
     324            if (NT_SUCCESS(rcNt) || rcNt == STATUS_BUFFER_OVERFLOW)
     325            {
     326                FILE_FS_VOLUME_INFORMATION *pVolInfo = (FILE_FS_VOLUME_INFORMATION *)pvBuf;
     327                pObjInfo->Attr.u.Unix.INodeIdDevice = pVolInfo->VolumeSerialNumber;
     328            }
     329        }
     330
     331        return rtPathNtQueryInfoFillInDummyData(VINF_SUCCESS, pObjInfo, enmAddAttr);
     332    }
     333    return RTErrConvertFromNtStatus(rcNt);
     334}
     335
     336
     337/**
     338 * Worker for RTPathQueryInfoEx and RTDirRelPathQueryInfo.
     339 *
     340 * @returns IPRT status code.
     341 * @param   hRootDir            The root directory that pNtName is relative to.
     342 * @param   pNtName             The NT path which we want to query info for.
     343 * @param   pObjInfo            Where to return the info.
     344 * @param   enmAddAttr          What additional info to get/fake.
     345 * @param   fFlags              Query flags (RTPATH_F_XXX).
     346 * @param   pszPath             The path for detecting executables and such.
     347 *                              Pass empty string if not applicable/available.
     348 */
     349DECLHIDDEN(int) rtPathNtQueryInfoWorker(HANDLE hRootDir, UNICODE_STRING *pNtName, PRTFSOBJINFO pObjInfo,
     350                                        RTFSOBJATTRADD enmAddAttr, uint32_t fFlags, const char *pszPath)
     351{
     352    /*
     353     * There are a three different ways of doing this:
     354     *   1. Use NtQueryFullAttributesFile to the get basic file info.
     355     *   2. Open whatever the path points to and use NtQueryInformationFile.
     356     *   3. Open the parent directory and use NtQueryDirectoryFile like RTDirReadEx.
     357     *
     358     * The first two options may fail with sharing violations or access denied,
     359     * in which case we must use the last one as fallback.
     360     */
     361    HANDLE              hFile = RTNT_INVALID_HANDLE_VALUE;
     362    IO_STATUS_BLOCK     Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
     363    NTSTATUS            rcNt;
     364    OBJECT_ATTRIBUTES   ObjAttr;
     365    union
     366    {
     367        FILE_NETWORK_OPEN_INFORMATION   NetOpenInfo;
     368        FILE_ALL_INFORMATION            AllInfo;
     369        FILE_FS_VOLUME_INFORMATION      VolInfo;
     370        FILE_BOTH_DIR_INFORMATION       Both;
     371        FILE_ID_BOTH_DIR_INFORMATION    BothId;
     372        uint8_t                         abPadding[sizeof(FILE_ID_BOTH_DIR_INFORMATION) + RTPATH_MAX * sizeof(wchar_t)];
     373    } uBuf;
     374
     375    /*
     376     * We can only use the first option if no additional UNIX attribs are
     377     * requested and it isn't a symbolic link.  NT directory object
     378     */
     379    int rc = VINF_TRY_AGAIN;
     380    if (enmAddAttr != RTFSOBJATTRADD_UNIX)
     381    {
     382        InitializeObjectAttributes(&ObjAttr, pNtName, OBJ_CASE_INSENSITIVE, hRootDir, NULL);
     383        rcNt = NtQueryFullAttributesFile(&ObjAttr, &uBuf.NetOpenInfo);
     384        if (NT_SUCCESS(rcNt))
     385        {
     386            if (!(uBuf.NetOpenInfo.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT))
     387            {
     388                pObjInfo->cbObject    = uBuf.NetOpenInfo.EndOfFile.QuadPart;
     389                pObjInfo->cbAllocated = uBuf.NetOpenInfo.AllocationSize.QuadPart;
     390                RTTimeSpecSetNtTime(&pObjInfo->BirthTime,         uBuf.NetOpenInfo.CreationTime.QuadPart);
     391                RTTimeSpecSetNtTime(&pObjInfo->AccessTime,        uBuf.NetOpenInfo.LastAccessTime.QuadPart);
     392                RTTimeSpecSetNtTime(&pObjInfo->ModificationTime,  uBuf.NetOpenInfo.LastWriteTime.QuadPart);
     393                RTTimeSpecSetNtTime(&pObjInfo->ChangeTime,        uBuf.NetOpenInfo.ChangeTime.QuadPart);
     394                pObjInfo->Attr.fMode = rtFsModeFromDos((uBuf.NetOpenInfo.FileAttributes << RTFS_DOS_SHIFT) & RTFS_DOS_MASK_NT,
     395                                                       pszPath, strlen(pszPath), 0 /*uReparseTag*/);
     396                pObjInfo->Attr.enmAdditional = enmAddAttr;
     397
     398                return rtPathNtQueryInfoFillInDummyData(VINF_SUCCESS, pObjInfo, enmAddAttr);
     399            }
     400        }
     401        else if (   rcNt == STATUS_OBJECT_TYPE_MISMATCH
     402                 || rcNt == STATUS_OBJECT_NAME_INVALID
     403                 || rcNt == STATUS_INVALID_PARAMETER)
     404        {
     405            rc = rtPathNtQueryInfoInDirectoryObject(&ObjAttr, pObjInfo, enmAddAttr, fFlags, &uBuf, sizeof(uBuf));
     406            if (RT_SUCCESS(rc))
     407                return rc;
     408            rc = RTErrConvertFromNtStatus(rcNt);
     409        }
     410        else if (   rcNt != STATUS_ACCESS_DENIED
     411                 && rcNt != STATUS_SHARING_VIOLATION)
     412            rc = RTErrConvertFromNtStatus(rcNt);
     413        else
     414            RTNT_IO_STATUS_BLOCK_REINIT(&Ios);
     415    }
     416
     417    /*
     418     * Try the 2nd option.  We might have to redo this if not following symbolic
     419     * links and the reparse point isn't a symbolic link but a mount point or similar.
     420     * We want to return information about the mounted root directory if we can, not
     421     * the directory in which it was mounted.
     422     */
     423    if (rc == VINF_TRY_AGAIN)
     424    {
     425        InitializeObjectAttributes(&ObjAttr, pNtName, OBJ_CASE_INSENSITIVE, hRootDir, NULL);
     426        rcNt = NtCreateFile(&hFile,
     427                            FILE_READ_ATTRIBUTES | SYNCHRONIZE,
     428                            &ObjAttr,
     429                            &Ios,
     430                            NULL /*pcbFile*/,
     431                            FILE_ATTRIBUTE_NORMAL,
     432                            FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
     433                            FILE_OPEN,
     434                            FILE_OPEN_FOR_BACKUP_INTENT | FILE_SYNCHRONOUS_IO_NONALERT
     435                            | (fFlags & RTPATH_F_FOLLOW_LINK ? 0 : FILE_OPEN_REPARSE_POINT),
     436                            NULL /*pvEaBuffer*/,
     437                            0 /*cbEa*/);
     438        if (NT_SUCCESS(rcNt))
     439        {
     440            /* Query tag information first in order to try re-open non-symlink reparse points. */
     441            FILE_ATTRIBUTE_TAG_INFORMATION TagInfo;
     442            rcNt = NtQueryInformationFile(hFile, &Ios, &TagInfo, sizeof(TagInfo), FileAttributeTagInformation);
     443            if (!NT_SUCCESS(rcNt))
     444                TagInfo.FileAttributes = TagInfo.ReparseTag = 0;
     445            if (   !(TagInfo.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)
     446                || TagInfo.ReparseTag == IO_REPARSE_TAG_SYMLINK
     447                || (fFlags & RTPATH_F_FOLLOW_LINK))
     448            { /* likely */ }
     449            else
     450            {
     451                /* Reparse point that isn't a symbolic link, try follow the reparsing. */
     452                HANDLE hFile2;
     453                RTNT_IO_STATUS_BLOCK_REINIT(&Ios);
     454                rcNt = NtCreateFile(&hFile2,
     455                                    FILE_READ_ATTRIBUTES | SYNCHRONIZE,
     456                                    &ObjAttr,
     457                                    &Ios,
     458                                    NULL /*pcbFile*/,
     459                                    FILE_ATTRIBUTE_NORMAL,
     460                                    FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
     461                                    FILE_OPEN,
     462                                    FILE_OPEN_FOR_BACKUP_INTENT | FILE_SYNCHRONOUS_IO_NONALERT,
     463                                    NULL /*pvEaBuffer*/,
     464                                    0 /*cbEa*/);
     465                if (NT_SUCCESS(rcNt))
     466                {
     467                    NtClose(hFile);
     468                    hFile = hFile2;
     469                    TagInfo.FileAttributes = TagInfo.ReparseTag = 0;
     470                }
     471            }
     472
     473            /*
     474             * Get the information we need and convert it.
     475             */
     476            rc = rtPathNtQueryInfoFromHandle(hFile, &uBuf, sizeof(uBuf), pObjInfo, enmAddAttr, pszPath, TagInfo.ReparseTag);
     477            NtClose(hFile);
     478            if (RT_SUCCESS(rc))
     479                return rc;
     480
     481            if (RT_FAILURE(rc))
     482                rc = VERR_TRY_AGAIN;
     483        }
     484        else if (   rcNt != STATUS_ACCESS_DENIED
     485                 && rcNt != STATUS_SHARING_VIOLATION)
     486            rc = RTErrConvertFromNtStatus(rcNt);
     487        else
     488            RTNT_IO_STATUS_BLOCK_REINIT(&Ios);
     489    }
     490
     491    /*
     492     * Try the 3rd option if none of the other worked.
     493     * If none of the above worked, try open the directory and enumerate
     494     * the file we're after.  This
     495     */
     496    if (rc == VINF_TRY_AGAIN)
     497    {
     498        /* Split up the name into parent directory path and filename. */
     499        UNICODE_STRING NtDirName;
     500        UNICODE_STRING NtFilter;
     501        ntPathNtSplitName(pNtName, &NtDirName, &NtFilter, false /*fNoParentDirSlash*/);
     502
     503        /* Try open the directory. */
     504        InitializeObjectAttributes(&ObjAttr, &NtDirName, OBJ_CASE_INSENSITIVE, hRootDir, NULL);
     505        rcNt = NtCreateFile(&hFile,
     506                            FILE_LIST_DIRECTORY | SYNCHRONIZE,
     507                            &ObjAttr,
     508                            &Ios,
     509                            NULL /*pcbFile*/,
     510                            FILE_ATTRIBUTE_NORMAL,
     511                            FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
     512                            FILE_OPEN,
     513                            FILE_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT | FILE_SYNCHRONOUS_IO_NONALERT,
     514                            NULL /*pvEaBuffer*/,
     515                            0 /*cbEa*/);
     516        if (NT_SUCCESS(rcNt))
     517        {
     518            FILE_INFORMATION_CLASS enmInfoClass;
     519            if (RT_MAKE_U64(RTNtCurrentPeb()->OSMinorVersion, RTNtCurrentPeb()->OSMajorVersion) > RT_MAKE_U64(0,5) /* > W2K */)
     520                enmInfoClass = FileIdBothDirectoryInformation; /* Introduced in XP, from I can tell. */
     521            else
     522                enmInfoClass = FileBothDirectoryInformation;
     523            rcNt = NtQueryDirectoryFile(hFile,
     524                                        NULL /* Event */,
     525                                        NULL /* ApcRoutine */,
     526                                        NULL /* ApcContext */,
     527                                        &Ios,
     528                                        &uBuf,
     529                                        RT_MIN(sizeof(uBuf), 0xfff0),
     530                                        enmInfoClass,
     531                                        TRUE /*ReturnSingleEntry */,
     532                                        &NtFilter,
     533                                        FALSE /*RestartScan */);
     534            if (NT_SUCCESS(rcNt))
     535            {
     536                pObjInfo->cbObject    = uBuf.Both.EndOfFile.QuadPart;
     537                pObjInfo->cbAllocated = uBuf.Both.AllocationSize.QuadPart;
     538
     539                RTTimeSpecSetNtTime(&pObjInfo->BirthTime,         uBuf.Both.CreationTime.QuadPart);
     540                RTTimeSpecSetNtTime(&pObjInfo->AccessTime,        uBuf.Both.LastAccessTime.QuadPart);
     541                RTTimeSpecSetNtTime(&pObjInfo->ModificationTime,  uBuf.Both.LastWriteTime.QuadPart);
     542                RTTimeSpecSetNtTime(&pObjInfo->ChangeTime,        uBuf.Both.ChangeTime.QuadPart);
     543
     544                pObjInfo->Attr.fMode  = rtFsModeFromDos((uBuf.Both.FileAttributes << RTFS_DOS_SHIFT) & RTFS_DOS_MASK_NT,
     545                                                        pszPath, strlen(pszPath), uBuf.Both.EaSize);
     546
     547                pObjInfo->Attr.enmAdditional = enmAddAttr;
     548                if (enmAddAttr == RTFSOBJATTRADD_UNIX)
     549                {
     550                    pObjInfo->Attr.u.Unix.uid             = ~0U;
     551                    pObjInfo->Attr.u.Unix.gid             = ~0U;
     552                    pObjInfo->Attr.u.Unix.cHardlinks      = 1;
     553                    pObjInfo->Attr.u.Unix.INodeIdDevice   = 0; /* below */
     554                    pObjInfo->Attr.u.Unix.INodeId         = enmInfoClass == FileIdBothDirectoryInformation
     555                                                          ? uBuf.BothId.FileId.QuadPart : 0;
     556                    pObjInfo->Attr.u.Unix.fFlags          = 0;
     557                    pObjInfo->Attr.u.Unix.GenerationId    = 0;
     558                    pObjInfo->Attr.u.Unix.Device          = 0;
     559
     560                    /* Get the serial number. */
     561                    rcNt = NtQueryVolumeInformationFile(hFile, &Ios, &uBuf, RT_MIN(sizeof(uBuf), _2K),
     562                                                        FileFsVolumeInformation);
     563                    if (NT_SUCCESS(rcNt))
     564                        pObjInfo->Attr.u.Unix.INodeIdDevice = uBuf.VolInfo.VolumeSerialNumber;
     565                }
     566
     567                rc = rtPathNtQueryInfoFillInDummyData(VINF_SUCCESS, pObjInfo, enmAddAttr);
     568            }
     569            else
     570                rc = RTErrConvertFromNtStatus(rcNt);
     571
     572            NtClose(hFile);
     573        }
     574        else
     575            rc = RTErrConvertFromNtStatus(rcNt);
     576    }
     577
     578    return rc;
     579}
     580
     581
    63582RTR3DECL(int) RTPathQueryInfoEx(const char *pszPath, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAdditionalAttribs, uint32_t fFlags)
    64583{
     
    77596
    78597    /*
    79      * Convert the input path.
     598     * Convert the input path and call common worker.
    80599     */
    81600    HANDLE         hRootDir;
     
    84603    if (RT_SUCCESS(rc))
    85604    {
    86         /*
    87          * There are a three different ways of doing this:
    88          *   1. Use NtQueryFullAttributesFile to the get basic file info.
    89          *   2. Open whatever the path points to and use NtQueryInformationFile.
    90          *   3. Open the parent directory and use NtQueryDirectoryFile like RTDirReadEx.
    91          *
    92          * The first two options may fail with sharing violations or access denied,
    93          * in which case we must use the last one as fallback.
    94          */
    95         HANDLE              hFile = RTNT_INVALID_HANDLE_VALUE;
    96         IO_STATUS_BLOCK     Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
    97         NTSTATUS            rcNt;
    98         OBJECT_ATTRIBUTES   ObjAttr;
    99         union
    100         {
    101             FILE_NETWORK_OPEN_INFORMATION   NetOpenInfo;
    102             FILE_ALL_INFORMATION            AllInfo;
    103             FILE_FS_VOLUME_INFORMATION      VolInfo;
    104             FILE_BOTH_DIR_INFORMATION       Both;
    105             FILE_ID_BOTH_DIR_INFORMATION    BothId;
    106             uint8_t                         abPadding[sizeof(FILE_ID_BOTH_DIR_INFORMATION) + RTPATH_MAX * sizeof(wchar_t)];
    107         } uBuf;
    108 
    109         /*
    110          * We can only use the first option if no additional UNIX attribs are
    111          * requested and it isn't a symbolic link.
    112          */
    113         rc = VINF_TRY_AGAIN;
    114         if (enmAdditionalAttribs != RTFSOBJATTRADD_UNIX)
    115         {
    116             InitializeObjectAttributes(&ObjAttr, &NtName, OBJ_CASE_INSENSITIVE, hRootDir, NULL);
    117             rcNt = NtQueryFullAttributesFile(&ObjAttr, &uBuf.NetOpenInfo);
    118             if (NT_SUCCESS(rcNt))
    119             {
    120                 if (!(uBuf.NetOpenInfo.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT))
    121                 {
    122                     pObjInfo->cbObject    = uBuf.NetOpenInfo.EndOfFile.QuadPart;
    123                     pObjInfo->cbAllocated = uBuf.NetOpenInfo.AllocationSize.QuadPart;
    124                     RTTimeSpecSetNtTime(&pObjInfo->BirthTime,         uBuf.NetOpenInfo.CreationTime.QuadPart);
    125                     RTTimeSpecSetNtTime(&pObjInfo->AccessTime,        uBuf.NetOpenInfo.LastAccessTime.QuadPart);
    126                     RTTimeSpecSetNtTime(&pObjInfo->ModificationTime,  uBuf.NetOpenInfo.LastWriteTime.QuadPart);
    127                     RTTimeSpecSetNtTime(&pObjInfo->ChangeTime,        uBuf.NetOpenInfo.ChangeTime.QuadPart);
    128                     pObjInfo->Attr.fMode = rtFsModeFromDos((uBuf.NetOpenInfo.FileAttributes << RTFS_DOS_SHIFT) & RTFS_DOS_MASK_NT,
    129                                                            pszPath, strlen(pszPath), 0 /*uReparseTag*/);
    130                     pObjInfo->Attr.enmAdditional = enmAdditionalAttribs;
    131                     rc = VINF_SUCCESS;
    132                 }
    133             }
    134             else if (   rcNt != STATUS_ACCESS_DENIED
    135                      && rcNt != STATUS_SHARING_VIOLATION)
    136                 rc = RTErrConvertFromNtStatus(rcNt);
    137             else
    138                 RTNT_IO_STATUS_BLOCK_REINIT(&Ios);
    139         }
    140 
    141         /*
    142          * Try the 2nd option.  We might have to redo this if not following symbolic
    143          * links and the reparse point isn't a symbolic link but a mount point or similar.
    144          * We want to return information about the mounted root directory if we can, not
    145          * the directory in which it was mounted.
    146          */
    147         if (rc == VINF_TRY_AGAIN)
    148         {
    149             InitializeObjectAttributes(&ObjAttr, &NtName, OBJ_CASE_INSENSITIVE, hRootDir, NULL);
    150             rcNt = NtCreateFile(&hFile,
    151                                 FILE_READ_ATTRIBUTES | SYNCHRONIZE,
    152                                 &ObjAttr,
    153                                 &Ios,
    154                                 NULL /*pcbFile*/,
    155                                 FILE_ATTRIBUTE_NORMAL,
    156                                 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
    157                                 FILE_OPEN,
    158                                 FILE_OPEN_FOR_BACKUP_INTENT | FILE_SYNCHRONOUS_IO_NONALERT
    159                                 | (fFlags & RTPATH_F_FOLLOW_LINK ? 0 : FILE_OPEN_REPARSE_POINT),
    160                                 NULL /*pvEaBuffer*/,
    161                                 0 /*cbEa*/);
    162             if (NT_SUCCESS(rcNt))
    163             {
    164                 /* Query tag information first in order to try re-open non-symlink reparse points. */
    165                 FILE_ATTRIBUTE_TAG_INFORMATION TagInfo;
    166                 rcNt = NtQueryInformationFile(hFile, &Ios, &TagInfo, sizeof(TagInfo), FileAttributeTagInformation);
    167                 if (!NT_SUCCESS(rcNt))
    168                     TagInfo.FileAttributes = TagInfo.ReparseTag = 0;
    169                 if (   !(TagInfo.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)
    170                     || TagInfo.ReparseTag == IO_REPARSE_TAG_SYMLINK
    171                     || (fFlags & RTPATH_F_FOLLOW_LINK))
    172                 { /* likely */ }
    173                 else
    174                 {
    175                     /* Reparse point that isn't a symbolic link, try follow the reparsing. */
    176                     HANDLE hFile2;
    177                     RTNT_IO_STATUS_BLOCK_REINIT(&Ios);
    178                     rcNt = NtCreateFile(&hFile2,
    179                                         FILE_READ_ATTRIBUTES | SYNCHRONIZE,
    180                                         &ObjAttr,
    181                                         &Ios,
    182                                         NULL /*pcbFile*/,
    183                                         FILE_ATTRIBUTE_NORMAL,
    184                                         FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
    185                                         FILE_OPEN,
    186                                         FILE_OPEN_FOR_BACKUP_INTENT | FILE_SYNCHRONOUS_IO_NONALERT,
    187                                         NULL /*pvEaBuffer*/,
    188                                         0 /*cbEa*/);
    189                     if (NT_SUCCESS(rcNt))
    190                     {
    191                         NtClose(hFile);
    192                         hFile = hFile2;
    193                         TagInfo.FileAttributes = TagInfo.ReparseTag = 0;
    194                     }
    195                 }
    196 
    197                 /*
    198                  * Get the information we need and convert it.
    199                  */
    200                 /** @todo Try optimize this for when RTFSOBJATTRADD_UNIX isn't set? */
    201                 RTNT_IO_STATUS_BLOCK_REINIT(&Ios);
    202                 rcNt = NtQueryInformationFile(hFile, &Ios, &uBuf.AllInfo, sizeof(uBuf.AllInfo), FileAllInformation);
    203                 if (NT_SUCCESS(rcNt) || rcNt == STATUS_BUFFER_OVERFLOW)
    204                 {
    205                     pObjInfo->cbObject    = uBuf.AllInfo.StandardInformation.EndOfFile.QuadPart;
    206                     pObjInfo->cbAllocated = uBuf.AllInfo.StandardInformation.AllocationSize.QuadPart;
    207                     RTTimeSpecSetNtTime(&pObjInfo->BirthTime,         uBuf.AllInfo.BasicInformation.CreationTime.QuadPart);
    208                     RTTimeSpecSetNtTime(&pObjInfo->AccessTime,        uBuf.AllInfo.BasicInformation.LastAccessTime.QuadPart);
    209                     RTTimeSpecSetNtTime(&pObjInfo->ModificationTime,  uBuf.AllInfo.BasicInformation.LastWriteTime.QuadPart);
    210                     RTTimeSpecSetNtTime(&pObjInfo->ChangeTime,        uBuf.AllInfo.BasicInformation.ChangeTime.QuadPart);
    211                     pObjInfo->Attr.fMode = rtFsModeFromDos(  (uBuf.AllInfo.BasicInformation.FileAttributes << RTFS_DOS_SHIFT)
    212                                                            & RTFS_DOS_MASK_NT,
    213                                                            pszPath, strlen(pszPath), TagInfo.ReparseTag);
    214                     pObjInfo->Attr.enmAdditional = enmAdditionalAttribs;
    215                     if (enmAdditionalAttribs == RTFSOBJATTRADD_UNIX)
    216                     {
    217                         pObjInfo->Attr.u.Unix.uid             = ~0U;
    218                         pObjInfo->Attr.u.Unix.gid             = ~0U;
    219                         pObjInfo->Attr.u.Unix.cHardlinks      = RT_MAX(1, uBuf.AllInfo.StandardInformation.NumberOfLinks);
    220                         pObjInfo->Attr.u.Unix.INodeIdDevice   = 0; /* below */
    221                         pObjInfo->Attr.u.Unix.INodeId         = uBuf.AllInfo.InternalInformation.IndexNumber.QuadPart;
    222                         pObjInfo->Attr.u.Unix.fFlags          = 0;
    223                         pObjInfo->Attr.u.Unix.GenerationId    = 0;
    224                         pObjInfo->Attr.u.Unix.Device          = 0;
    225 
    226                         /* Get the serial number. */
    227                         rcNt = NtQueryVolumeInformationFile(hFile, &Ios, &uBuf, RT_MIN(sizeof(uBuf), _2K),
    228                                                             FileFsVolumeInformation);
    229                         if (NT_SUCCESS(rcNt) || rcNt == STATUS_BUFFER_OVERFLOW)
    230                             pObjInfo->Attr.u.Unix.INodeIdDevice = uBuf.VolInfo.VolumeSerialNumber;
    231                     }
    232 
    233                     rc = VINF_SUCCESS;
    234                 }
    235 
    236                 NtClose(hFile);
    237             }
    238             else if (   rcNt != STATUS_ACCESS_DENIED
    239                      && rcNt != STATUS_SHARING_VIOLATION)
    240                 rc = RTErrConvertFromNtStatus(rcNt);
    241             else
    242                 RTNT_IO_STATUS_BLOCK_REINIT(&Ios);
    243         }
    244 
    245         /*
    246          * Try the 3rd option if none of the other worked.
    247          * If none of the above worked, try open the directory and enumerate
    248          * the file we're after.  This
    249          */
    250         if (rc == VINF_TRY_AGAIN)
    251         {
    252             /* Drop the name from NtPath. */
    253             UNICODE_STRING NtDirName = NtName;
    254             size_t off = NtName.Length / sizeof(NtName.Buffer[0]);
    255             wchar_t wc;
    256             while (   off > 0
    257                    && (wc = NtName.Buffer[off - 1]) != '\\'
    258                    && wc != '/')
    259                 off--;
    260             if (off != 0)
    261                 NtDirName.Length = (USHORT)(off * sizeof(NtName.Buffer[0]));
    262             else
    263             {
    264                 AssertFailed(); /* This is impossible and won't work (NT doesn't know '.' or '..').  */
    265                 NtDirName.Buffer = L".";
    266                 NtDirName.Length = sizeof(NtName.Buffer[0]);
    267                 NtDirName.MaximumLength = 2 * sizeof(NtName.Buffer[0]);
    268             }
    269 
    270             UNICODE_STRING NtFilter;
    271             NtFilter.Buffer        = &NtName.Buffer[off];
    272             NtFilter.Length        = NtName.Length        - (USHORT)(off * sizeof(NtName.Buffer[0]));
    273             NtFilter.MaximumLength = NtName.MaximumLength - (USHORT)(off * sizeof(NtName.Buffer[0]));
    274 
    275             /* Try open the directory. */
    276             InitializeObjectAttributes(&ObjAttr, &NtDirName, OBJ_CASE_INSENSITIVE, hRootDir, NULL);
    277             rcNt = NtCreateFile(&hFile,
    278                                 FILE_LIST_DIRECTORY | SYNCHRONIZE,
    279                                 &ObjAttr,
    280                                 &Ios,
    281                                 NULL /*pcbFile*/,
    282                                 FILE_ATTRIBUTE_NORMAL,
    283                                 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
    284                                 FILE_OPEN,
    285                                 FILE_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT | FILE_SYNCHRONOUS_IO_NONALERT,
    286                                 NULL /*pvEaBuffer*/,
    287                                 0 /*cbEa*/);
    288             if (NT_SUCCESS(rcNt))
    289             {
    290                 FILE_INFORMATION_CLASS enmInfoClass;
    291                 if (RT_MAKE_U64(RTNtCurrentPeb()->OSMinorVersion, RTNtCurrentPeb()->OSMajorVersion) > RT_MAKE_U64(0,5) /* > W2K */)
    292                     enmInfoClass = FileIdBothDirectoryInformation; /* Introduced in XP, from I can tell. */
    293                 else
    294                     enmInfoClass = FileBothDirectoryInformation;
    295                 rcNt = NtQueryDirectoryFile(hFile,
    296                                             NULL /* Event */,
    297                                             NULL /* ApcRoutine */,
    298                                             NULL /* ApcContext */,
    299                                             &Ios,
    300                                             &uBuf,
    301                                             RT_MIN(sizeof(uBuf), 0xfff0),
    302                                             enmInfoClass,
    303                                             TRUE /*ReturnSingleEntry */,
    304                                             &NtFilter,
    305                                             FALSE /*RestartScan */);
    306                 if (NT_SUCCESS(rcNt))
    307                 {
    308                     pObjInfo->cbObject    = uBuf.Both.EndOfFile.QuadPart;
    309                     pObjInfo->cbAllocated = uBuf.Both.AllocationSize.QuadPart;
    310 
    311                     RTTimeSpecSetNtTime(&pObjInfo->BirthTime,         uBuf.Both.CreationTime.QuadPart);
    312                     RTTimeSpecSetNtTime(&pObjInfo->AccessTime,        uBuf.Both.LastAccessTime.QuadPart);
    313                     RTTimeSpecSetNtTime(&pObjInfo->ModificationTime,  uBuf.Both.LastWriteTime.QuadPart);
    314                     RTTimeSpecSetNtTime(&pObjInfo->ChangeTime,        uBuf.Both.ChangeTime.QuadPart);
    315 
    316                     pObjInfo->Attr.fMode  = rtFsModeFromDos((uBuf.Both.FileAttributes << RTFS_DOS_SHIFT) & RTFS_DOS_MASK_NT,
    317                                                             pszPath, strlen(pszPath), uBuf.Both.EaSize);
    318 
    319                     pObjInfo->Attr.enmAdditional = enmAdditionalAttribs;
    320                     if (enmAdditionalAttribs == RTFSOBJATTRADD_UNIX)
    321                     {
    322                         pObjInfo->Attr.u.Unix.uid             = ~0U;
    323                         pObjInfo->Attr.u.Unix.gid             = ~0U;
    324                         pObjInfo->Attr.u.Unix.cHardlinks      = 1;
    325                         pObjInfo->Attr.u.Unix.INodeIdDevice   = 0; /* below */
    326                         pObjInfo->Attr.u.Unix.INodeId         = enmInfoClass == FileIdBothDirectoryInformation
    327                                                               ? uBuf.BothId.FileId.QuadPart : 0;
    328                         pObjInfo->Attr.u.Unix.fFlags          = 0;
    329                         pObjInfo->Attr.u.Unix.GenerationId    = 0;
    330                         pObjInfo->Attr.u.Unix.Device          = 0;
    331 
    332                         /* Get the serial number. */
    333                         rcNt = NtQueryVolumeInformationFile(hFile, &Ios, &uBuf, RT_MIN(sizeof(uBuf), _2K),
    334                                                             FileFsVolumeInformation);
    335                         if (NT_SUCCESS(rcNt))
    336                             pObjInfo->Attr.u.Unix.INodeIdDevice = uBuf.VolInfo.VolumeSerialNumber;
    337                     }
    338 
    339                     rc = VINF_SUCCESS;
    340 
    341                 }
    342                 else
    343                     rc = RTErrConvertFromNtStatus(rcNt);
    344 
    345                 NtClose(hFile);
    346             }
    347             else
    348                 rc = RTErrConvertFromNtStatus(rcNt);
    349         }
    350 
    351         /*
    352          * Fill in dummy additional attributes for the non-UNIX requests.
    353          */
    354         switch (enmAdditionalAttribs)
    355         {
    356             case RTFSOBJATTRADD_UNIX:
    357                 break;
    358 
    359             case RTFSOBJATTRADD_NOTHING:
    360                 pObjInfo->Attr.enmAdditional          = RTFSOBJATTRADD_NOTHING;
    361                 break;
    362 
    363             case RTFSOBJATTRADD_UNIX_OWNER:
    364                 pObjInfo->Attr.enmAdditional          = RTFSOBJATTRADD_UNIX_OWNER;
    365                 pObjInfo->Attr.u.UnixOwner.uid        = ~0U;
    366                 pObjInfo->Attr.u.UnixOwner.szName[0]  = '\0'; /** @todo return something sensible here. */
    367                 break;
    368 
    369             case RTFSOBJATTRADD_UNIX_GROUP:
    370                 pObjInfo->Attr.enmAdditional          = RTFSOBJATTRADD_UNIX_GROUP;
    371                 pObjInfo->Attr.u.UnixGroup.gid        = ~0U;
    372                 pObjInfo->Attr.u.UnixGroup.szName[0]  = '\0';
    373                 break;
    374 
    375             case RTFSOBJATTRADD_EASIZE:
    376                 pObjInfo->Attr.enmAdditional          = RTFSOBJATTRADD_EASIZE;
    377                 pObjInfo->Attr.u.EASize.cb            = 0;
    378                 break;
    379 
    380             default:
    381                 AssertMsgFailed(("Impossible!\n"));
    382                 rc = VERR_INTERNAL_ERROR;
    383         }
    384 
     605        rc = rtPathNtQueryInfoWorker(hRootDir, &NtName, pObjInfo, enmAdditionalAttribs, fFlags, pszPath);
    385606        RTNtPathFree(&NtName, &hRootDir);
    386607    }
    387 
    388608    return rc;
    389609}
    390 #endif
    391 
     610
     611
     612RTR3DECL(int) RTPathQueryInfo(const char *pszPath, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAdditionalAttribs)
     613{
     614    return RTPathQueryInfoEx(pszPath, pObjInfo, enmAdditionalAttribs, RTPATH_F_ON_LINK);
     615}
     616
  • trunk/src/VBox/Runtime/r3/nt/direnum-r3-nt.cpp

    r69691 r69705  
    4242#include "internal/fs.h"
    4343#include "internal/dir.h"
     44#include "internal/path.h"
    4445
    4546
     
    107108    if (pvNativeRelative == NULL)
    108109        rc = RTNtPathOpenDir(pszPathBuf,
    109                              FILE_LIST_DIRECTORY | FILE_TRAVERSE | SYNCHRONIZE,
     110                             FILE_LIST_DIRECTORY | FILE_READ_ATTRIBUTES | FILE_TRAVERSE | SYNCHRONIZE,
    110111                             FILE_SHARE_READ | FILE_SHARE_WRITE,
    111112                             FILE_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT | FILE_SYNCHRONOUS_IO_NONALERT,
     
    121122        rc = RTNtPathOpenDirEx((HANDLE)hRelativeDir,
    122123                               (struct _UNICODE_STRING *)pvNativeRelative,
    123                                FILE_LIST_DIRECTORY | FILE_TRAVERSE | SYNCHRONIZE,
     124                               FILE_LIST_DIRECTORY | FILE_READ_ATTRIBUTES | FILE_TRAVERSE | SYNCHRONIZE,
    124125                               FILE_SHARE_READ | FILE_SHARE_WRITE,
    125126                               FILE_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT | FILE_SYNCHRONOUS_IO_NONALERT,
    126127                               OBJ_CASE_INSENSITIVE,
    127128                               &pDir->hDir,
    128                                NULL);
     129#ifdef IPRT_WITH_NT_PATH_PASSTHRU
     130                               &fObjDir
     131#else
     132                               NULL
     133#endif
     134
     135                               );
    129136    if (RT_SUCCESS(rc))
    130137    {
     
    759766        case RTFSOBJATTRADD_UNIX:
    760767            pDirEntry->Info.Attr.enmAdditional          = RTFSOBJATTRADD_UNIX;
    761             pDirEntry->Info.Attr.u.Unix.uid             = ~0U;
    762             pDirEntry->Info.Attr.u.Unix.gid             = ~0U;
     768            pDirEntry->Info.Attr.u.Unix.uid             = NIL_RTUID;
     769            pDirEntry->Info.Attr.u.Unix.gid             = NIL_RTGID;
    763770            pDirEntry->Info.Attr.u.Unix.cHardlinks      = 1;
    764771            pDirEntry->Info.Attr.u.Unix.INodeIdDevice   = pDir->uDirDev;
     
    778785        case RTFSOBJATTRADD_UNIX_OWNER:
    779786            pDirEntry->Info.Attr.enmAdditional          = RTFSOBJATTRADD_UNIX_OWNER;
    780             pDirEntry->Info.Attr.u.UnixOwner.uid        = ~0U;
     787            pDirEntry->Info.Attr.u.UnixOwner.uid        = NIL_RTUID;
    781788            pDirEntry->Info.Attr.u.UnixOwner.szName[0]  = '\0'; /** @todo return something sensible here. */
    782789            break;
     
    784791        case RTFSOBJATTRADD_UNIX_GROUP:
    785792            pDirEntry->Info.Attr.enmAdditional          = RTFSOBJATTRADD_UNIX_GROUP;
    786             pDirEntry->Info.Attr.u.UnixGroup.gid        = ~0U;
     793            pDirEntry->Info.Attr.u.UnixGroup.gid        = NIL_RTGID;
    787794            pDirEntry->Info.Attr.u.UnixGroup.szName[0]  = '\0';
    788795            break;
     
    809816}
    810817
     818
     819
     820RTR3DECL(int) RTDirQueryInfo(PRTDIR pDir, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAdditionalAttribs)
     821{
     822    AssertPtrReturn(pDir, VERR_INVALID_POINTER);
     823    AssertReturn(pDir->u32Magic == RTDIR_MAGIC, VERR_INVALID_HANDLE);
     824    AssertReturn(enmAdditionalAttribs >= RTFSOBJATTRADD_NOTHING && enmAdditionalAttribs <= RTFSOBJATTRADD_LAST,
     825                 VERR_INVALID_PARAMETER);
     826
     827    if (pDir->enmInfoClass == FileMaximumInformation)
     828    {
     829        /*
     830         * Directory object (see similar code above and rtPathNtQueryInfoInDirectoryObject).
     831         */
     832        pObjInfo->cbObject    = 0;
     833        pObjInfo->cbAllocated = 0;
     834        RTTimeSpecSetNtTime(&pObjInfo->BirthTime,         0);
     835        RTTimeSpecSetNtTime(&pObjInfo->AccessTime,        0);
     836        RTTimeSpecSetNtTime(&pObjInfo->ModificationTime,  0);
     837        RTTimeSpecSetNtTime(&pObjInfo->ChangeTime,        0);
     838        pObjInfo->Attr.fMode = RTFS_DOS_DIRECTORY | RTFS_TYPE_DIRECTORY | 0777;
     839        pObjInfo->Attr.enmAdditional = enmAdditionalAttribs;
     840        switch (enmAdditionalAttribs)
     841        {
     842            case RTFSOBJATTRADD_NOTHING:
     843            case RTFSOBJATTRADD_UNIX:
     844                pObjInfo->Attr.u.Unix.uid             = NIL_RTUID;
     845                pObjInfo->Attr.u.Unix.gid             = NIL_RTGID;
     846                pObjInfo->Attr.u.Unix.cHardlinks      = 1;
     847                pObjInfo->Attr.u.Unix.INodeIdDevice   = pDir->uDirDev;
     848                pObjInfo->Attr.u.Unix.INodeId         = 0;
     849                pObjInfo->Attr.u.Unix.fFlags          = 0;
     850                pObjInfo->Attr.u.Unix.GenerationId    = 0;
     851                pObjInfo->Attr.u.Unix.Device          = 0;
     852                break;
     853
     854            case RTFSOBJATTRADD_EASIZE:
     855                pObjInfo->Attr.u.EASize.cb            = 0;
     856                break;
     857
     858            case RTFSOBJATTRADD_UNIX_OWNER:
     859                pObjInfo->Attr.enmAdditional          = RTFSOBJATTRADD_UNIX_OWNER;
     860                pObjInfo->Attr.u.UnixOwner.uid        = NIL_RTUID;
     861                pObjInfo->Attr.u.UnixOwner.szName[0]  = '\0'; /** @todo return something sensible here. */
     862                break;
     863
     864            case RTFSOBJATTRADD_UNIX_GROUP:
     865                pObjInfo->Attr.enmAdditional          = RTFSOBJATTRADD_UNIX_GROUP;
     866                pObjInfo->Attr.u.UnixGroup.gid        = NIL_RTGID;
     867                pObjInfo->Attr.u.UnixGroup.szName[0]  = '\0';
     868                break;
     869
     870            default:
     871                AssertMsgFailed(("Impossible!\n"));
     872                return VERR_INTERNAL_ERROR_2;
     873        }
     874        return VINF_SUCCESS;
     875    }
     876
     877    /*
     878     * Regular directory file.
     879     */
     880    uint8_t abBuf[_2K];
     881    return rtPathNtQueryInfoFromHandle(pDir->hDir, abBuf, sizeof(abBuf), pObjInfo, enmAdditionalAttribs, "", 0);
     882}
     883
  • trunk/src/VBox/Runtime/r3/nt/dirrel-r3-nt.cpp

    r69694 r69705  
    4242#include "internal/file.h"
    4343#include "internal/fs.h"
     44#include "internal/path.h"
    4445
    4546
     
    139140        UNICODE_STRING NtName;
    140141        HANDLE hRoot = pThis->hDir;
    141         rc = RTNtPathRelativeFromUtf8(&NtName, &hRoot, pszRelFilename, RTDIRREL_NT_GET_ASCENT(pThis));
     142        rc = RTNtPathRelativeFromUtf8(&NtName, &hRoot, pszRelFilename, RTDIRREL_NT_GET_ASCENT(pThis),
     143                                      pThis->enmInfoClass == FileMaximumInformation);
    142144        if (RT_SUCCESS(rc))
    143145        {
     
    204206    UNICODE_STRING NtName;
    205207    HANDLE hRoot = pThis->hDir;
    206     int rc = RTNtPathRelativeFromUtf8(&NtName, &hRoot, pszDirAndFilter, RTDIRREL_NT_GET_ASCENT(pThis));
     208    int rc = RTNtPathRelativeFromUtf8(&NtName, &hRoot, pszDirAndFilter, RTDIRREL_NT_GET_ASCENT(pThis),
     209                                      pThis->enmInfoClass == FileMaximumInformation);
    207210    if (RT_SUCCESS(rc))
    208211    {
     
    234237    int rc = rtDirRelBuildFullPath(pThis, szPath, sizeof(szPath), pszRelPath);
    235238    if (RT_SUCCESS(rc))
     239    {
     240RTAssertMsg2("DBG: RTDirRelDirCreate(%s)...\n", szPath);
    236241        rc = RTDirCreate(szPath, fMode, fCreate);
     242    }
    237243    return rc;
    238244}
     
    257263    int rc = rtDirRelBuildFullPath(pThis, szPath, sizeof(szPath), pszRelPath);
    258264    if (RT_SUCCESS(rc))
     265    {
     266RTAssertMsg2("DBG: RTDirRelDirRemove(%s)...\n", szPath);
    259267        rc = RTDirRemove(szPath);
     268    }
    260269    return rc;
    261270}
     
    272281
    273282
    274 /**
    275  * Query information about a file system object relative to @a hDir.
    276  *
    277  * @returns IPRT status code.
    278  * @retval  VINF_SUCCESS if the object exists, information returned.
    279  * @retval  VERR_PATH_NOT_FOUND if any but the last component in the specified
    280  *          path was not found or was not a directory.
    281  * @retval  VERR_FILE_NOT_FOUND if the object does not exist (but path to the
    282  *          parent directory exists).
    283  *
    284  * @param   hDir            The directory @a pszRelPath is relative to.
    285  * @param   pszRelPath      The relative path to the file system object.
    286  * @param   pObjInfo        Object information structure to be filled on successful
    287  *                          return.
    288  * @param   enmAddAttr      Which set of additional attributes to request.
    289  *                          Use RTFSOBJATTRADD_NOTHING if this doesn't matter.
    290  * @param   fFlags          RTPATH_F_ON_LINK or RTPATH_F_FOLLOW_LINK.
    291  *
    292  * @sa      RTPathQueryInfoEx
    293  */
    294283RTDECL(int) RTDirRelPathQueryInfo(PRTDIR hDir, const char *pszRelPath, PRTFSOBJINFO pObjInfo,
    295284                                  RTFSOBJATTRADD enmAddAttr, uint32_t fFlags)
     
    299288    AssertReturn(pThis->u32Magic == RTDIR_MAGIC, VERR_INVALID_HANDLE);
    300289
    301     char szPath[RTPATH_MAX];
    302     int rc = rtDirRelBuildFullPath(pThis, szPath, sizeof(szPath), pszRelPath);
    303     if (RT_SUCCESS(rc))
    304         rc = RTPathQueryInfoEx(szPath, pObjInfo, enmAddAttr, fFlags);
     290    /*
     291     * Validate and convert flags.
     292     */
     293    UNICODE_STRING NtName;
     294    HANDLE hRoot = pThis->hDir;
     295    int rc = RTNtPathRelativeFromUtf8(&NtName, &hRoot, pszRelPath, RTDIRREL_NT_GET_ASCENT(pThis),
     296                                      pThis->enmInfoClass == FileMaximumInformation);
     297    if (RT_SUCCESS(rc))
     298    {
     299        rc = rtPathNtQueryInfoWorker(hRoot, &NtName, pObjInfo, enmAddAttr, fFlags, pszRelPath);
     300        RTNtPathFree(&NtName, NULL);
     301    }
    305302    return rc;
    306303}
     
    332329    if (RT_SUCCESS(rc))
    333330    {
     331RTAssertMsg2("DBG: RTDirRelPathSetMode(%s)...\n", szPath);
    334332#ifndef RT_OS_WINDOWS
    335333        rc = RTPathSetMode(szPath, fMode); /** @todo fFlags is currently ignored. */
     
    379377    int rc = rtDirRelBuildFullPath(pThis, szPath, sizeof(szPath), pszRelPath);
    380378    if (RT_SUCCESS(rc))
     379    {
     380RTAssertMsg2("DBG: RTDirRelPathSetTimes(%s)...\n", szPath);
    381381        rc = RTPathSetTimesEx(szPath, pAccessTime, pModificationTime, pChangeTime, pBirthTime, fFlags);
     382    }
    382383    return rc;
    383384}
     
    408409    if (RT_SUCCESS(rc))
    409410    {
     411RTAssertMsg2("DBG: RTDirRelPathSetOwner(%s)...\n", szPath);
    410412#ifndef RT_OS_WINDOWS
    411413        rc = RTPathSetOwnerEx(szPath, uid, gid, fFlags);
     
    454456        rc = rtDirRelBuildFullPath(pThis, szDstPath, sizeof(szDstPath), pszDst);
    455457        if (RT_SUCCESS(rc))
    456             rc = RTPathRename(pszSrc, pszDst, fRename);
     458        {
     459RTAssertMsg2("DBG: RTDirRelPathRename(%s,%s)...\n", szSrcPath, szDstPath);
     460            rc = RTPathRename(szSrcPath, szDstPath, fRename);
     461        }
    457462    }
    458463    return rc;
     
    479484    int rc = rtDirRelBuildFullPath(pThis, szPath, sizeof(szPath), pszRelPath);
    480485    if (RT_SUCCESS(rc))
     486    {
     487RTAssertMsg2("DBG: RTDirRelPathUnlink(%s)...\n", szPath);
    481488        rc = RTPathUnlink(szPath, fUnlink);
     489    }
    482490    return rc;
    483491}
     
    522530    int rc = rtDirRelBuildFullPath(pThis, szPath, sizeof(szPath), pszSymlink);
    523531    if (RT_SUCCESS(rc))
     532    {
     533RTAssertMsg2("DBG: RTDirRelSymlinkCreate(%s)...\n", szPath);
    524534        rc = RTSymlinkCreate(szPath, pszTarget, enmType, fCreate);
     535    }
    525536    return rc;
    526537}
     
    554565    int rc = rtDirRelBuildFullPath(pThis, szPath, sizeof(szPath), pszSymlink);
    555566    if (RT_SUCCESS(rc))
     567    {
     568RTAssertMsg2("DBG: RTDirRelSymlinkRead(%s)...\n", szPath);
    556569        rc = RTSymlinkRead(szPath, pszTarget, cbTarget, fRead);
    557     return rc;
    558 }
    559 
     570    }
     571    return rc;
     572}
     573
  • trunk/src/VBox/Runtime/r3/nt/pathint-nt.cpp

    r69691 r69705  
    447447         * Copy the result into the return string.
    448448         */
    449         size_t cbNeeded = cwcExtra * sizeof(RTUTF16) + pNtName->Length + sizeof(RTUTF16);
     449        size_t cbNeeded = cwcExtra * sizeof(RTUTF16) + pUniStrBuf->Length + sizeof(RTUTF16);
    450450        if (cbNeeded < _64K)
    451451        {
     
    471471}
    472472
     473static int rtNtPathRelativeToAbs(struct _UNICODE_STRING *pNtName, HANDLE *phRootDir)
     474{
     475    int rc;
     476    if (pNtName->Length == 0)
     477    {
     478        RTUtf16Free(pNtName->Buffer);
     479        rc = rtNtPathFromHandle(pNtName, *phRootDir, pNtName->Length / sizeof(RTUTF16) + 2);
     480        if (RT_SUCCESS(rc))
     481        {
     482            *phRootDir = NULL;
     483            return VINF_SUCCESS;
     484        }
     485    }
     486    else
     487    {
     488
     489        UNICODE_STRING RootDir;
     490        size_t const   cwcAppend = pNtName->Length / sizeof(RTUTF16);
     491        rc = rtNtPathFromHandle(&RootDir, *phRootDir, cwcAppend + 2);
     492        if (RT_SUCCESS(rc))
     493        {
     494            size_t cwcRoot = RootDir.Length / sizeof(RTUTF16);
     495            if (RootDir.Buffer[cwcRoot - 1] != '\\')
     496                RootDir.Buffer[cwcRoot++] = '\\';
     497            memcpy(&RootDir.Buffer[cwcRoot], pNtName->Buffer, cwcAppend * sizeof(RTUTF16));
     498            RTUtf16Free(pNtName->Buffer);
     499            pNtName->Length        = (uint16_t)((cwcRoot + cwcAppend) * sizeof(RTUTF16));
     500            pNtName->MaximumLength = RootDir.MaximumLength;
     501            pNtName->Buffer        = RootDir.Buffer;
     502
     503            *phRootDir = NULL;
     504            return VINF_SUCCESS;
     505        }
     506        RTUtf16Free(pNtName->Buffer);
     507    }
     508    pNtName->Length        = 0;
     509    pNtName->MaximumLength = 0;
     510    pNtName->Buffer        = NULL;
     511    return rc;
     512}
     513
    473514
    474515/**
     
    514555 * @param   pszPath             The relative UTF-8 path.
    515556 * @param   enmAscent           How to handle ascent.
     557 * @param   fMustReturnAbsolute Must convert to an absolute path.  This
     558 *                              is necessary if the root dir is a NT directory
     559 *                              object (e.g. /Devices) since they cannot parse
     560 *                              relative paths it seems.
    516561 */
    517562RTDECL(int) RTNtPathRelativeFromUtf8(struct _UNICODE_STRING *pNtName, PHANDLE phRootDir, const char *pszPath,
    518                                      RTNTPATHRELATIVEASCENT enmAscent)
     563                                     RTNTPATHRELATIVEASCENT enmAscent, bool fMustReturnAbsolute)
    519564{
    520565    size_t cwcMax;
     
    543588            {
    544589                default:
    545                     pwszDstCur = RTUtf16PutCp(pwszDst, uc);
     590                    pwszDstCur = RTUtf16PutCp(pwszDstCur, uc);
    546591                    break;
    547592
     
    566611                                pwszDstCur--;
    567612                            *pwszDstCur = '\0';
    568                             pNtName->Length = (uint16_t)(pwszDstCur - pwszDst);
    569                             return VINF_SUCCESS;
     613                            pNtName->Length = (uint16_t)((uintptr_t)pwszDstCur - (uintptr_t)pwszDst);
     614                            if (!fMustReturnAbsolute || *phRootDir == NULL)
     615                                return VINF_SUCCESS;
     616                            return rtNtPathRelativeToAbs(pNtName, phRootDir);
    570617                        }
    571618
     
    595642                                    {
    596643                                        case kRTNtPathRelativeAscent_Allow:
    597                                             if (*phRootDir != RTNT_INVALID_HANDLE_VALUE)
     644                                            if (*phRootDir != NULL)
    598645                                            {
    599646                                                RTUtf16Free(pwszDst);
     
    602649                                                    return rc;
    603650
    604                                                 *phRootDir = RTNT_INVALID_HANDLE_VALUE;
     651                                                *phRootDir = NULL;
    605652                                                pwszDst    = pNtName->Buffer;
    606653                                                pwszDstCur = &pwszDst[pNtName->Length / sizeof(RTUTF16)];
     
    627674                                {
    628675                                    *pwszDstCur = '\0';
    629                                     pNtName->Length = (uint16_t)(pwszDstCur - pwszDst);
    630                                     return VINF_SUCCESS;
     676                                    pNtName->Length = (uint16_t)((uintptr_t)pwszDstCur - (uintptr_t)pwszDst);
     677                                    if (!fMustReturnAbsolute || *phRootDir == NULL)
     678                                        return VINF_SUCCESS;
     679                                    return rtNtPathRelativeToAbs(pNtName, phRootDir);
    631680                                }
    632681                                pszPath += 2;
     
    642691                case '\0':
    643692                    *pwszDstCur = '\0';
    644                     pNtName->Length = (uint16_t)(pwszDstCur - pwszDst);
    645                     return VINF_SUCCESS;
     693                    pNtName->Length = (uint16_t)((uintptr_t)pwszDstCur - (uintptr_t)pwszDst);
     694                    if (!fMustReturnAbsolute || *phRootDir == NULL)
     695                        return VINF_SUCCESS;
     696                    return rtNtPathRelativeToAbs(pNtName, phRootDir);
    646697            }
    647698        }
     
    829880    }
    830881
     882    /*
     883     * Try add a slash in case this is a device object with a file system attached.
     884     */
     885    if (   rcNt == STATUS_INVALID_PARAMETER
     886        && pNtName->Length < _64K - 4
     887        && (   pNtName->Length == 0
     888            || pNtName->Buffer[pNtName->Length / sizeof(RTUTF16)] != '\\') )
     889    {
     890        UNICODE_STRING NtTmp;
     891        NtTmp.Length        = pNtName->Length + 2;
     892        NtTmp.MaximumLength = NtTmp.Length + 2;
     893        NtTmp.Buffer        = (PRTUTF16)RTMemTmpAlloc(NtTmp.MaximumLength);
     894        if (NtTmp.Buffer)
     895        {
     896            memcpy(NtTmp.Buffer, pNtName->Buffer, pNtName->Length);
     897            NtTmp.Buffer[pNtName->Length / sizeof(RTUTF16)] = '\\';
     898            NtTmp.Buffer[pNtName->Length / sizeof(RTUTF16) + 1] = '\0';
     899
     900            hFile = RTNT_INVALID_HANDLE_VALUE;
     901            Ios.Status = -1;
     902            Ios.Information = 0;
     903            ObjAttr.ObjectName = &NtTmp;
     904
     905            rcNt = NtCreateFile(&hFile,
     906                                fDesiredAccess,
     907                                &ObjAttr,
     908                                &Ios,
     909                                NULL /* AllocationSize*/,
     910                                FILE_ATTRIBUTE_NORMAL,
     911                                fShareAccess,
     912                                FILE_OPEN,
     913                                fCreateOptions,
     914                                NULL /*EaBuffer*/,
     915                                0 /*EaLength*/);
     916            RTMemTmpFree(NtTmp.Buffer);
     917            if (NT_SUCCESS(rcNt))
     918            {
     919                if (pfObjDir)
     920                    *pfObjDir = false;
     921                *phHandle = hFile;
     922                return VINF_SUCCESS;
     923            }
     924            ObjAttr.ObjectName = pNtName;
     925        }
     926    }
     927
     928    /*
     929     * Try open it as a directory object if it makes sense.
     930     */
    831931    if (   pfObjDir
    832         && (rcNt == STATUS_OBJECT_NAME_INVALID || rcNt == STATUS_OBJECT_TYPE_MISMATCH))
     932        && (   rcNt == STATUS_OBJECT_NAME_INVALID
     933            || rcNt == STATUS_OBJECT_TYPE_MISMATCH ))
    833934    {
    834935        /* Strip trailing slash. */
Note: See TracChangeset for help on using the changeset viewer.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette