VirtualBox

source: vbox/trunk/src/VBox/Runtime/r3/nt/RTPathQueryInfo-nt.cpp@ 95110

Last change on this file since 95110 was 95110, checked in by vboxsync, 3 years ago

IPRT/RTPathQueryInfo-nt.cpp: The g_pfnNtQueryFullAttributesFile type must match the init-win.cpp version exactly (noexcept/c++17).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 31.3 KB
Line 
1/* $Id: RTPathQueryInfo-nt.cpp 95110 2022-05-25 20:38:50Z vboxsync $ */
2/** @file
3 * IPRT - RTPathQueryInfo[Ex], Native NT.
4 */
5
6/*
7 * Copyright (C) 2006-2022 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*********************************************************************************************************************************
29* Header Files *
30*********************************************************************************************************************************/
31#define LOG_GROUP RTLOGGROUP_FILE
32#include "internal-r3-nt.h"
33
34#include <iprt/path.h>
35#include <iprt/err.h>
36#include <iprt/time.h>
37#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)
48
49
50/*********************************************************************************************************************************
51* Global Variables *
52*********************************************************************************************************************************/
53typedef NTSTATUS (NTAPI *PFNNTQUERYFULLATTRIBUTESFILE)(struct _OBJECT_ATTRIBUTES *, struct _FILE_NETWORK_OPEN_INFORMATION *);
54extern PFNNTQUERYFULLATTRIBUTESFILE g_pfnNtQueryFullAttributesFile; /* init-win.cpp */
55
56
57/* ASSUMES FileID comes after ShortName and the structs are identical up to that point. */
58AssertCompileMembersSameSizeAndOffset(FILE_BOTH_DIR_INFORMATION, NextEntryOffset, FILE_ID_BOTH_DIR_INFORMATION, NextEntryOffset);
59AssertCompileMembersSameSizeAndOffset(FILE_BOTH_DIR_INFORMATION, FileIndex , FILE_ID_BOTH_DIR_INFORMATION, FileIndex );
60AssertCompileMembersSameSizeAndOffset(FILE_BOTH_DIR_INFORMATION, CreationTime , FILE_ID_BOTH_DIR_INFORMATION, CreationTime );
61AssertCompileMembersSameSizeAndOffset(FILE_BOTH_DIR_INFORMATION, LastAccessTime , FILE_ID_BOTH_DIR_INFORMATION, LastAccessTime );
62AssertCompileMembersSameSizeAndOffset(FILE_BOTH_DIR_INFORMATION, LastWriteTime , FILE_ID_BOTH_DIR_INFORMATION, LastWriteTime );
63AssertCompileMembersSameSizeAndOffset(FILE_BOTH_DIR_INFORMATION, ChangeTime , FILE_ID_BOTH_DIR_INFORMATION, ChangeTime );
64AssertCompileMembersSameSizeAndOffset(FILE_BOTH_DIR_INFORMATION, EndOfFile , FILE_ID_BOTH_DIR_INFORMATION, EndOfFile );
65AssertCompileMembersSameSizeAndOffset(FILE_BOTH_DIR_INFORMATION, AllocationSize , FILE_ID_BOTH_DIR_INFORMATION, AllocationSize );
66AssertCompileMembersSameSizeAndOffset(FILE_BOTH_DIR_INFORMATION, FileAttributes , FILE_ID_BOTH_DIR_INFORMATION, FileAttributes );
67AssertCompileMembersSameSizeAndOffset(FILE_BOTH_DIR_INFORMATION, FileNameLength , FILE_ID_BOTH_DIR_INFORMATION, FileNameLength );
68AssertCompileMembersSameSizeAndOffset(FILE_BOTH_DIR_INFORMATION, EaSize , FILE_ID_BOTH_DIR_INFORMATION, EaSize );
69AssertCompileMembersSameSizeAndOffset(FILE_BOTH_DIR_INFORMATION, ShortNameLength, FILE_ID_BOTH_DIR_INFORMATION, ShortNameLength);
70AssertCompileMembersSameSizeAndOffset(FILE_BOTH_DIR_INFORMATION, ShortName , FILE_ID_BOTH_DIR_INFORMATION, ShortName );
71
72
73
74/**
75 * Splits up an NT path into directory and filename.
76 *
77 * @param pNtName The path to split.
78 * @param pNtParent Where to return the directory path.
79 * @param pNtFilename Where to return the filename part.
80 * @param fNoParentDirSlash Whether to make sure the directory path doesn't
81 * end with a slash (except root).
82 */
83static void ntPathNtSplitName(UNICODE_STRING const *pNtName, UNICODE_STRING *pNtParent, UNICODE_STRING *pNtFilename,
84 bool fNoParentDirSlash)
85{
86 PRTUTF16 pwszBuffer = pNtName->Buffer;
87 size_t off = pNtName->Length / sizeof(RTUTF16);
88
89 /* Skip trailing slash if present. */
90 if ( off > 0
91 && pwszBuffer[off - 1] == '\\')
92 off--;
93
94 /* Find the slash before that. */
95 RTUTF16 wc;
96 while ( off > 0
97 && (wc = pwszBuffer[off - 1]) != '\\'
98 && wc != '/')
99 off--;
100 if (off != 0)
101 {
102 pNtParent->Buffer = pwszBuffer;
103 pNtParent->MaximumLength = pNtParent->Length = (USHORT)(off * sizeof(RTUTF16));
104 }
105 else
106 {
107 AssertFailed(); /* This is impossible and won't work (NT doesn't know '.' or '..'). */
108 /** @todo query the whole path as it is possible relative. Use the buffer for
109 * temporary name storage. */
110 pNtParent->Buffer = L".";
111 pNtParent->Length = 1 * sizeof(RTUTF16);
112 pNtParent->MaximumLength = 2 * sizeof(RTUTF16);
113 }
114
115 pNtFilename->Buffer = &pwszBuffer[off];
116 pNtFilename->Length = pNtName->Length - (USHORT)(off * sizeof(RTUTF16));
117 pNtFilename->MaximumLength = pNtName->MaximumLength - (USHORT)(off * sizeof(RTUTF16));
118
119 while ( fNoParentDirSlash
120 && pNtParent->Length > sizeof(RTUTF16)
121 && pNtParent->Buffer[pNtParent->Length / sizeof(RTUTF16) - 1] == '\\')
122 pNtParent->Length -= sizeof(RTUTF16);
123}
124
125
126/**
127 * Deals with enmAddAttr != RTFSOBJATTRADD_UNIX.
128 *
129 * @returns IPRT status code (usually @a rc).
130 * @param rc The return code.
131 * @param pObjInfo The info to complete.
132 * @param enmAddAttr What to complete it with. Caller should fill in
133 * RTFSOBJATTRADD_UNIX.
134 */
135static int rtPathNtQueryInfoFillInDummyData(int rc, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
136{
137 switch (enmAddAttr)
138 {
139 case RTFSOBJATTRADD_UNIX:
140 pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_UNIX;
141 break;
142
143 case RTFSOBJATTRADD_NOTHING:
144 pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_NOTHING;
145 break;
146
147 case RTFSOBJATTRADD_UNIX_OWNER:
148 pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_UNIX_OWNER;
149 pObjInfo->Attr.u.UnixOwner.uid = NIL_RTUID;
150 pObjInfo->Attr.u.UnixOwner.szName[0] = '\0'; /** @todo return something sensible here. */
151 break;
152
153 case RTFSOBJATTRADD_UNIX_GROUP:
154 pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_UNIX_GROUP;
155 pObjInfo->Attr.u.UnixGroup.gid = NIL_RTGID;
156 pObjInfo->Attr.u.UnixGroup.szName[0] = '\0';
157 break;
158
159 case RTFSOBJATTRADD_EASIZE:
160 pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_EASIZE;
161 pObjInfo->Attr.u.EASize.cb = 0;
162 break;
163
164 default:
165 AssertMsgFailed(("Impossible!\n"));
166 rc = VERR_INTERNAL_ERROR;
167 }
168 return rc;
169}
170
171
172/**
173 * Deal with getting info about something that could be in a directory object.
174 *
175 * @returns IPRT status code
176 * @param pObjAttr The NT object attribute.
177 * @param pObjInfo Where to return the info.
178 * @param enmAddAttr Which extra attributes to get (/fake).
179 * @param fFlags The flags.
180 * @param pvBuf Query buffer space.
181 * @param cbBuf Size of the buffer. ASSUMES lots of space.
182 * @param rcNtCaller The status code that got us here.
183 */
184static int rtPathNtQueryInfoInDirectoryObject(OBJECT_ATTRIBUTES *pObjAttr, PRTFSOBJINFO pObjInfo,
185 RTFSOBJATTRADD enmAddAttr, uint32_t fFlags,
186 void *pvBuf, size_t cbBuf, NTSTATUS rcNtCaller)
187{
188 RT_NOREF(fFlags);
189
190 /*
191 * Special case: Root dir.
192 */
193 if ( pObjAttr->RootDirectory == NULL
194 && pObjAttr->ObjectName->Length == sizeof(RTUTF16)
195 && pObjAttr->ObjectName->Buffer[0] == '\\')
196 {
197 pObjInfo->cbObject = 0;
198 pObjInfo->cbAllocated = 0;
199 RTTimeSpecSetNtTime(&pObjInfo->BirthTime, 0);
200 RTTimeSpecSetNtTime(&pObjInfo->AccessTime, 0);
201 RTTimeSpecSetNtTime(&pObjInfo->ModificationTime, 0);
202 RTTimeSpecSetNtTime(&pObjInfo->ChangeTime, 0);
203 pObjInfo->Attr.fMode = RTFS_DOS_DIRECTORY | RTFS_TYPE_DIRECTORY | 0777;
204 return rtPathNtQueryInfoFillInDummyData(VINF_SUCCESS, pObjInfo, enmAddAttr);
205 }
206
207 /*
208 * We must open and scan the parent directory object.
209 */
210 UNICODE_STRING NtDirName;
211 UNICODE_STRING NtDirEntry;
212 ntPathNtSplitName(pObjAttr->ObjectName, &NtDirName, &NtDirEntry, true /*fNoParentDirSlash*/);
213
214 while ( NtDirEntry.Length > sizeof(RTUTF16)
215 && NtDirEntry.Buffer[NtDirEntry.Length / sizeof(RTUTF16) - 1] == '\\')
216 NtDirEntry.Length -= sizeof(RTUTF16);
217
218 pObjAttr->ObjectName = &NtDirName;
219 HANDLE hDir = RTNT_INVALID_HANDLE_VALUE;
220 NTSTATUS rcNt = NtOpenDirectoryObject(&hDir, DIRECTORY_QUERY | DIRECTORY_TRAVERSE, pObjAttr);
221 if (NT_SUCCESS(rcNt))
222 {
223 ULONG uObjDirCtx = 0;
224 for (;;)
225 {
226 ULONG cbReturned = 0;
227 rcNt = NtQueryDirectoryObject(hDir,
228 pvBuf,
229 (ULONG)cbBuf,
230 FALSE /*ReturnSingleEntry */,
231 FALSE /*RestartScan*/,
232 &uObjDirCtx,
233 &cbReturned);
234 if (!NT_SUCCESS(rcNt))
235 break;
236
237 for (POBJECT_DIRECTORY_INFORMATION pObjDir = (POBJECT_DIRECTORY_INFORMATION)pvBuf;
238 pObjDir->Name.Length != 0;
239 pObjDir++)
240 {
241 if ( pObjDir->Name.Length == NtDirEntry.Length
242 && memcmp(pObjDir->Name.Buffer, NtDirEntry.Buffer, NtDirEntry.Length) == 0)
243 {
244 /*
245 * Find it. Fill in the info we've got and return (see similar code in direnum-r3-nt.cpp).
246 */
247 NtClose(hDir);
248
249 pObjInfo->cbObject = 0;
250 pObjInfo->cbAllocated = 0;
251 RTTimeSpecSetNtTime(&pObjInfo->BirthTime, 0);
252 RTTimeSpecSetNtTime(&pObjInfo->AccessTime, 0);
253 RTTimeSpecSetNtTime(&pObjInfo->ModificationTime, 0);
254 RTTimeSpecSetNtTime(&pObjInfo->ChangeTime, 0);
255
256 if (ARE_UNICODE_STRINGS_EQUAL(&pObjDir->TypeName, L"Directory"))
257 pObjInfo->Attr.fMode = RTFS_DOS_DIRECTORY | RTFS_TYPE_DIRECTORY | 0777;
258 else if (ARE_UNICODE_STRINGS_EQUAL(&pObjDir->TypeName, L"SymbolicLink"))
259 pObjInfo->Attr.fMode = RTFS_DOS_NT_REPARSE_POINT | RTFS_TYPE_SYMLINK | 0777;
260 else if (ARE_UNICODE_STRINGS_EQUAL(&pObjDir->TypeName, L"Device"))
261 pObjInfo->Attr.fMode = RTFS_DOS_NT_DEVICE | RTFS_TYPE_DEV_CHAR | 0666;
262 else
263 pObjInfo->Attr.fMode = RTFS_DOS_NT_NORMAL | RTFS_TYPE_FILE | 0666;
264
265 pObjInfo->Attr.enmAdditional = enmAddAttr;
266 return rtPathNtQueryInfoFillInDummyData(VINF_SUCCESS, pObjInfo, enmAddAttr);
267 }
268 }
269 }
270
271 NtClose(hDir);
272 if (rcNt == STATUS_NO_MORE_FILES || rcNt == STATUS_NO_MORE_ENTRIES || rcNt == STATUS_NO_SUCH_FILE)
273 return VERR_FILE_NOT_FOUND;
274 }
275 else
276 return RTErrConvertFromNtStatus(rcNtCaller);
277 return RTErrConvertFromNtStatus(rcNt);
278}
279
280
281/**
282 * Queries information from a file or directory handle.
283 *
284 * This is shared between the RTPathQueryInfo, RTFileQueryInfo and
285 * RTDirQueryInfo code.
286 *
287 * @returns IPRT status code.
288 * @param hFile The handle to query information from. Must have
289 * the necessary privileges.
290 * @param pvBuf Pointer to a scratch buffer.
291 * @param cbBuf The size of the buffer. This must be large
292 * enough to hold a FILE_ALL_INFORMATION struct.
293 * @param pObjInfo Where to return information about the handle.
294 * @param enmAddAttr What extra info to return.
295 * @param pszPath The path if this is a file (for exe detect).
296 * @param uReparseTag The reparse tag number (0 if not applicable) for
297 * symlink detection/whatnot.
298 */
299DECLHIDDEN(int) rtPathNtQueryInfoFromHandle(HANDLE hFile, void *pvBuf, size_t cbBuf, PRTFSOBJINFO pObjInfo,
300 RTFSOBJATTRADD enmAddAttr, const char *pszPath, ULONG uReparseTag)
301{
302 Assert(cbBuf >= sizeof(FILE_ALL_INFORMATION));
303
304 /** @todo Try optimize this for when RTFSOBJATTRADD_UNIX isn't set? */
305 IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
306 NTSTATUS rcNt = NtQueryInformationFile(hFile, &Ios, pvBuf, sizeof(FILE_ALL_INFORMATION), FileAllInformation);
307 if ( NT_SUCCESS(rcNt)
308 || rcNt == STATUS_BUFFER_OVERFLOW)
309 {
310 FILE_ALL_INFORMATION *pAllInfo = (FILE_ALL_INFORMATION *)pvBuf;
311 pObjInfo->cbObject = pAllInfo->StandardInformation.EndOfFile.QuadPart;
312 pObjInfo->cbAllocated = pAllInfo->StandardInformation.AllocationSize.QuadPart;
313 RTTimeSpecSetNtTime(&pObjInfo->BirthTime, pAllInfo->BasicInformation.CreationTime.QuadPart);
314 RTTimeSpecSetNtTime(&pObjInfo->AccessTime, pAllInfo->BasicInformation.LastAccessTime.QuadPart);
315 RTTimeSpecSetNtTime(&pObjInfo->ModificationTime, pAllInfo->BasicInformation.LastWriteTime.QuadPart);
316 RTTimeSpecSetNtTime(&pObjInfo->ChangeTime, pAllInfo->BasicInformation.ChangeTime.QuadPart);
317 pObjInfo->Attr.fMode = rtFsModeFromDos( (pAllInfo->BasicInformation.FileAttributes << RTFS_DOS_SHIFT)
318 & RTFS_DOS_MASK_NT,
319 pszPath, pszPath ? strlen(pszPath) : 0, uReparseTag, 0);
320 pObjInfo->Attr.enmAdditional = enmAddAttr;
321 if (enmAddAttr == RTFSOBJATTRADD_UNIX)
322 {
323 pObjInfo->Attr.u.Unix.uid = ~0U;
324 pObjInfo->Attr.u.Unix.gid = ~0U;
325 pObjInfo->Attr.u.Unix.cHardlinks = RT_MAX(1, pAllInfo->StandardInformation.NumberOfLinks);
326 pObjInfo->Attr.u.Unix.INodeIdDevice = 0; /* below */
327 pObjInfo->Attr.u.Unix.INodeId = pAllInfo->InternalInformation.IndexNumber.QuadPart;
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, pvBuf, (ULONG)RT_MIN(cbBuf, _2K), FileFsVolumeInformation);
334 if (NT_SUCCESS(rcNt) || rcNt == STATUS_BUFFER_OVERFLOW)
335 {
336 FILE_FS_VOLUME_INFORMATION *pVolInfo = (FILE_FS_VOLUME_INFORMATION *)pvBuf;
337 pObjInfo->Attr.u.Unix.INodeIdDevice = pVolInfo->VolumeSerialNumber;
338 }
339 }
340
341 return rtPathNtQueryInfoFillInDummyData(VINF_SUCCESS, pObjInfo, enmAddAttr);
342 }
343 return RTErrConvertFromNtStatus(rcNt);
344}
345
346
347/**
348 * Worker for RTPathQueryInfoEx and RTDirRelPathQueryInfo.
349 *
350 * @returns IPRT status code.
351 * @param hRootDir The root directory that pNtName is relative to.
352 * @param pNtName The NT path which we want to query info for.
353 * @param pObjInfo Where to return the info.
354 * @param enmAddAttr What additional info to get/fake.
355 * @param fFlags Query flags (RTPATH_F_XXX).
356 * @param pszPath The path for detecting executables and such.
357 * Pass empty string if not applicable/available.
358 */
359DECLHIDDEN(int) rtPathNtQueryInfoWorker(HANDLE hRootDir, UNICODE_STRING *pNtName, PRTFSOBJINFO pObjInfo,
360 RTFSOBJATTRADD enmAddAttr, uint32_t fFlags, const char *pszPath)
361{
362 /*
363 * There are a three different ways of doing this:
364 * 1. Use NtQueryFullAttributesFile to the get basic file info.
365 * 2. Open whatever the path points to and use NtQueryInformationFile.
366 * 3. Open the parent directory and use NtQueryDirectoryFile like RTDirReadEx.
367 *
368 * The first two options may fail with sharing violations or access denied,
369 * in which case we must use the last one as fallback.
370 */
371 HANDLE hFile = RTNT_INVALID_HANDLE_VALUE;
372 IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
373 NTSTATUS rcNt;
374 OBJECT_ATTRIBUTES ObjAttr;
375 union
376 {
377 FILE_NETWORK_OPEN_INFORMATION NetOpenInfo;
378 FILE_ALL_INFORMATION AllInfo;
379 FILE_FS_VOLUME_INFORMATION VolInfo;
380 FILE_BOTH_DIR_INFORMATION Both;
381 FILE_ID_BOTH_DIR_INFORMATION BothId;
382 uint8_t abPadding[sizeof(FILE_ID_BOTH_DIR_INFORMATION) + RTPATH_MAX * sizeof(wchar_t)];
383 } uBuf;
384
385 /*
386 * We can only use the first option if no additional UNIX attribs are
387 * requested and it isn't a symbolic link. NT directory object
388 */
389 int rc = VINF_TRY_AGAIN;
390 if ( enmAddAttr != RTFSOBJATTRADD_UNIX
391 && g_pfnNtQueryFullAttributesFile)
392 {
393 InitializeObjectAttributes(&ObjAttr, pNtName, OBJ_CASE_INSENSITIVE, hRootDir, NULL);
394 rcNt = g_pfnNtQueryFullAttributesFile(&ObjAttr, &uBuf.NetOpenInfo);
395 if (NT_SUCCESS(rcNt))
396 {
397 if (!(uBuf.NetOpenInfo.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT))
398 {
399 pObjInfo->cbObject = uBuf.NetOpenInfo.EndOfFile.QuadPart;
400 pObjInfo->cbAllocated = uBuf.NetOpenInfo.AllocationSize.QuadPart;
401 RTTimeSpecSetNtTime(&pObjInfo->BirthTime, uBuf.NetOpenInfo.CreationTime.QuadPart);
402 RTTimeSpecSetNtTime(&pObjInfo->AccessTime, uBuf.NetOpenInfo.LastAccessTime.QuadPart);
403 RTTimeSpecSetNtTime(&pObjInfo->ModificationTime, uBuf.NetOpenInfo.LastWriteTime.QuadPart);
404 RTTimeSpecSetNtTime(&pObjInfo->ChangeTime, uBuf.NetOpenInfo.ChangeTime.QuadPart);
405 pObjInfo->Attr.fMode = rtFsModeFromDos((uBuf.NetOpenInfo.FileAttributes << RTFS_DOS_SHIFT) & RTFS_DOS_MASK_NT,
406 pszPath, strlen(pszPath), 0 /*uReparseTag*/, 0);
407 pObjInfo->Attr.enmAdditional = enmAddAttr;
408
409 return rtPathNtQueryInfoFillInDummyData(VINF_SUCCESS, pObjInfo, enmAddAttr);
410 }
411 }
412 else if ( rcNt == STATUS_OBJECT_TYPE_MISMATCH
413 || rcNt == STATUS_OBJECT_NAME_INVALID
414 || rcNt == STATUS_INVALID_PARAMETER)
415 {
416 rc = rtPathNtQueryInfoInDirectoryObject(&ObjAttr, pObjInfo, enmAddAttr, fFlags, &uBuf, sizeof(uBuf), rcNt);
417 if (RT_SUCCESS(rc))
418 return rc;
419 }
420 else if ( rcNt != STATUS_ACCESS_DENIED
421 && rcNt != STATUS_SHARING_VIOLATION)
422 rc = RTErrConvertFromNtStatus(rcNt);
423 else
424 RTNT_IO_STATUS_BLOCK_REINIT(&Ios);
425 }
426
427 /*
428 * Try the 2nd option. We might have to redo this if not following symbolic
429 * links and the reparse point isn't a symbolic link but a mount point or similar.
430 * We want to return information about the mounted root directory if we can, not
431 * the directory in which it was mounted.
432 */
433 if (rc == VINF_TRY_AGAIN)
434 {
435 static int volatile g_fReparsePoints = -1;
436 uint32_t fOptions = FILE_OPEN_FOR_BACKUP_INTENT | FILE_SYNCHRONOUS_IO_NONALERT;
437 int fReparsePoints = g_fReparsePoints;
438 if (fReparsePoints != 0 && !(fFlags & RTPATH_F_FOLLOW_LINK))
439 fOptions |= FILE_OPEN_REPARSE_POINT;
440
441 InitializeObjectAttributes(&ObjAttr, pNtName, OBJ_CASE_INSENSITIVE, hRootDir, NULL);
442 rcNt = NtCreateFile(&hFile,
443 FILE_READ_ATTRIBUTES | SYNCHRONIZE,
444 &ObjAttr,
445 &Ios,
446 NULL /*pcbFile*/,
447 FILE_ATTRIBUTE_NORMAL,
448 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
449 FILE_OPEN,
450 fOptions,
451 NULL /*pvEaBuffer*/,
452 0 /*cbEa*/);
453 if ( ( rcNt == STATUS_INVALID_PARAMETER
454 || rcNt == STATUS_INVALID_PARAMETER_9)
455 && fReparsePoints == -1
456 && (fOptions & FILE_OPEN_REPARSE_POINT))
457 {
458 fOptions &= ~FILE_OPEN_REPARSE_POINT;
459 rcNt = NtCreateFile(&hFile,
460 FILE_READ_ATTRIBUTES | SYNCHRONIZE,
461 &ObjAttr,
462 &Ios,
463 NULL /*pcbFile*/,
464 FILE_ATTRIBUTE_NORMAL,
465 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
466 FILE_OPEN,
467 fOptions,
468 NULL /*pvEaBuffer*/,
469 0 /*cbEa*/);
470 if (rcNt != STATUS_INVALID_PARAMETER)
471 g_fReparsePoints = fReparsePoints = 0;
472 }
473 if (NT_SUCCESS(rcNt))
474 {
475 /* Query tag information first in order to try re-open non-symlink reparse points. */
476 FILE_ATTRIBUTE_TAG_INFORMATION TagInfo;
477 rcNt = NtQueryInformationFile(hFile, &Ios, &TagInfo, sizeof(TagInfo), FileAttributeTagInformation);
478 if (!NT_SUCCESS(rcNt))
479 TagInfo.FileAttributes = TagInfo.ReparseTag = 0;
480 if ( !(TagInfo.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)
481 || TagInfo.ReparseTag == IO_REPARSE_TAG_SYMLINK
482 || (fFlags & RTPATH_F_FOLLOW_LINK))
483 { /* likely */ }
484 else
485 {
486 /* Reparse point that isn't a symbolic link, try follow the reparsing. */
487 HANDLE hFile2;
488 RTNT_IO_STATUS_BLOCK_REINIT(&Ios);
489 rcNt = NtCreateFile(&hFile2,
490 FILE_READ_ATTRIBUTES | SYNCHRONIZE,
491 &ObjAttr,
492 &Ios,
493 NULL /*pcbFile*/,
494 FILE_ATTRIBUTE_NORMAL,
495 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
496 FILE_OPEN,
497 FILE_OPEN_FOR_BACKUP_INTENT | FILE_SYNCHRONOUS_IO_NONALERT,
498 NULL /*pvEaBuffer*/,
499 0 /*cbEa*/);
500 if (NT_SUCCESS(rcNt))
501 {
502 NtClose(hFile);
503 hFile = hFile2;
504 TagInfo.FileAttributes = TagInfo.ReparseTag = 0;
505 }
506 }
507
508 /*
509 * Get the information we need and convert it.
510 */
511 rc = rtPathNtQueryInfoFromHandle(hFile, &uBuf, sizeof(uBuf), pObjInfo, enmAddAttr, pszPath, TagInfo.ReparseTag);
512 NtClose(hFile);
513 if (RT_SUCCESS(rc))
514 return rc;
515
516 if (RT_FAILURE(rc))
517 rc = VINF_TRY_AGAIN;
518 }
519 else if ( rcNt == STATUS_OBJECT_TYPE_MISMATCH
520 || rcNt == STATUS_OBJECT_NAME_INVALID
521 /*|| rcNt == STATUS_INVALID_PARAMETER*/)
522 {
523 rc = rtPathNtQueryInfoInDirectoryObject(&ObjAttr, pObjInfo, enmAddAttr, fFlags, &uBuf, sizeof(uBuf), rcNt);
524 if (RT_SUCCESS(rc))
525 return rc;
526 }
527 else if ( rcNt != STATUS_ACCESS_DENIED
528 && rcNt != STATUS_SHARING_VIOLATION)
529 rc = RTErrConvertFromNtStatus(rcNt);
530 else
531 RTNT_IO_STATUS_BLOCK_REINIT(&Ios);
532 }
533
534 /*
535 * Try the 3rd option if none of the other worked.
536 * If none of the above worked, try open the directory and enumerate
537 * the file we're after. This
538 */
539 if (rc == VINF_TRY_AGAIN)
540 {
541 /* Split up the name into parent directory path and filename. */
542 UNICODE_STRING NtDirName;
543 UNICODE_STRING NtFilter;
544 ntPathNtSplitName(pNtName, &NtDirName, &NtFilter, false /*fNoParentDirSlash*/);
545
546 /* Try open the directory. */
547 InitializeObjectAttributes(&ObjAttr, &NtDirName, OBJ_CASE_INSENSITIVE, hRootDir, NULL);
548 rcNt = NtCreateFile(&hFile,
549 FILE_LIST_DIRECTORY | SYNCHRONIZE,
550 &ObjAttr,
551 &Ios,
552 NULL /*pcbFile*/,
553 FILE_ATTRIBUTE_NORMAL,
554 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
555 FILE_OPEN,
556 FILE_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT | FILE_SYNCHRONOUS_IO_NONALERT,
557 NULL /*pvEaBuffer*/,
558 0 /*cbEa*/);
559 if (NT_SUCCESS(rcNt))
560 {
561 FILE_INFORMATION_CLASS enmInfoClass;
562 if (RT_MAKE_U64(RTNtCurrentPeb()->OSMinorVersion, RTNtCurrentPeb()->OSMajorVersion) > RT_MAKE_U64(0,5) /* > W2K */)
563 enmInfoClass = FileIdBothDirectoryInformation; /* Introduced in XP, from I can tell. */
564 else
565 enmInfoClass = FileBothDirectoryInformation;
566 rcNt = NtQueryDirectoryFile(hFile,
567 NULL /* Event */,
568 NULL /* ApcRoutine */,
569 NULL /* ApcContext */,
570 &Ios,
571 &uBuf,
572 RT_MIN(sizeof(uBuf), 0xfff0),
573 enmInfoClass,
574 TRUE /*ReturnSingleEntry */,
575 &NtFilter,
576 FALSE /*RestartScan */);
577 if (NT_SUCCESS(rcNt))
578 {
579 pObjInfo->cbObject = uBuf.Both.EndOfFile.QuadPart;
580 pObjInfo->cbAllocated = uBuf.Both.AllocationSize.QuadPart;
581
582 RTTimeSpecSetNtTime(&pObjInfo->BirthTime, uBuf.Both.CreationTime.QuadPart);
583 RTTimeSpecSetNtTime(&pObjInfo->AccessTime, uBuf.Both.LastAccessTime.QuadPart);
584 RTTimeSpecSetNtTime(&pObjInfo->ModificationTime, uBuf.Both.LastWriteTime.QuadPart);
585 RTTimeSpecSetNtTime(&pObjInfo->ChangeTime, uBuf.Both.ChangeTime.QuadPart);
586
587 pObjInfo->Attr.fMode = rtFsModeFromDos((uBuf.Both.FileAttributes << RTFS_DOS_SHIFT) & RTFS_DOS_MASK_NT,
588 pszPath, strlen(pszPath), uBuf.Both.EaSize, 0);
589
590 pObjInfo->Attr.enmAdditional = enmAddAttr;
591 if (enmAddAttr == RTFSOBJATTRADD_UNIX)
592 {
593 pObjInfo->Attr.u.Unix.uid = ~0U;
594 pObjInfo->Attr.u.Unix.gid = ~0U;
595 pObjInfo->Attr.u.Unix.cHardlinks = 1;
596 pObjInfo->Attr.u.Unix.INodeIdDevice = 0; /* below */
597 pObjInfo->Attr.u.Unix.INodeId = enmInfoClass == FileIdBothDirectoryInformation
598 ? uBuf.BothId.FileId.QuadPart : 0;
599 pObjInfo->Attr.u.Unix.fFlags = 0;
600 pObjInfo->Attr.u.Unix.GenerationId = 0;
601 pObjInfo->Attr.u.Unix.Device = 0;
602
603 /* Get the serial number. */
604 rcNt = NtQueryVolumeInformationFile(hFile, &Ios, &uBuf, RT_MIN(sizeof(uBuf), _2K),
605 FileFsVolumeInformation);
606 if (NT_SUCCESS(rcNt))
607 pObjInfo->Attr.u.Unix.INodeIdDevice = uBuf.VolInfo.VolumeSerialNumber;
608 }
609
610 rc = rtPathNtQueryInfoFillInDummyData(VINF_SUCCESS, pObjInfo, enmAddAttr);
611 }
612 else
613 rc = RTErrConvertFromNtStatus(rcNt);
614
615 NtClose(hFile);
616 }
617 /*
618 * Quite possibly a object directory.
619 */
620 else if ( rcNt == STATUS_OBJECT_NAME_INVALID /* with trailing slash */
621 || rcNt == STATUS_OBJECT_TYPE_MISMATCH /* without trailing slash */ )
622 {
623 InitializeObjectAttributes(&ObjAttr, pNtName, OBJ_CASE_INSENSITIVE, hRootDir, NULL);
624 rc = rtPathNtQueryInfoInDirectoryObject(&ObjAttr, pObjInfo, enmAddAttr, fFlags, &uBuf, sizeof(uBuf), rcNt);
625 if (RT_FAILURE(rc))
626 rc = RTErrConvertFromNtStatus(rcNt);
627 }
628 else
629 rc = RTErrConvertFromNtStatus(rcNt);
630 }
631
632 return rc;
633}
634
635
636RTR3DECL(int) RTPathQueryInfoEx(const char *pszPath, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAdditionalAttribs, uint32_t fFlags)
637{
638 /*
639 * Validate input.
640 */
641 AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
642 AssertReturn(*pszPath, VERR_INVALID_PARAMETER);
643 AssertPtrReturn(pObjInfo, VERR_INVALID_POINTER);
644 AssertMsgReturn( enmAdditionalAttribs >= RTFSOBJATTRADD_NOTHING
645 && enmAdditionalAttribs <= RTFSOBJATTRADD_LAST,
646 ("Invalid enmAdditionalAttribs=%p\n", enmAdditionalAttribs),
647 VERR_INVALID_PARAMETER);
648 AssertMsgReturn(RTPATH_F_IS_VALID(fFlags, 0), ("%#x\n", fFlags), VERR_INVALID_PARAMETER);
649
650
651 /*
652 * Convert the input path and call common worker.
653 */
654 HANDLE hRootDir;
655 UNICODE_STRING NtName;
656 int rc = RTNtPathFromWinUtf8(&NtName, &hRootDir, pszPath);
657 if (RT_SUCCESS(rc))
658 {
659 rc = rtPathNtQueryInfoWorker(hRootDir, &NtName, pObjInfo, enmAdditionalAttribs, fFlags, pszPath);
660 RTNtPathFree(&NtName, &hRootDir);
661 }
662 return rc;
663}
664
665
666RTR3DECL(int) RTPathQueryInfo(const char *pszPath, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAdditionalAttribs)
667{
668 return RTPathQueryInfoEx(pszPath, pObjInfo, enmAdditionalAttribs, RTPATH_F_ON_LINK);
669}
670
Note: See TracBrowser for help on using the repository browser.

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