VirtualBox

Ignore:
Timestamp:
Nov 15, 2017 4:42:59 PM (8 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.
File:
1 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
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