VirtualBox

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

最後變更 在這個檔案從69753是 69705,由 vboxsync 提交於 7 年 前

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.
  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Author Date Id Revision
檔案大小: 28.6 KB
 
1/* $Id: RTPathQueryInfo-nt.cpp 69705 2017-11-15 16:42:59Z vboxsync $ */
2/** @file
3 * IPRT - RTPathQueryInfo[Ex], Native NT.
4 */
5
6/*
7 * Copyright (C) 2006-2017 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.alldomusa.eu.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/* ASSUMES FileID comes after ShortName and the structs are identical up to that point. */
51AssertCompileMembersSameSizeAndOffset(FILE_BOTH_DIR_INFORMATION, NextEntryOffset, FILE_ID_BOTH_DIR_INFORMATION, NextEntryOffset);
52AssertCompileMembersSameSizeAndOffset(FILE_BOTH_DIR_INFORMATION, FileIndex , FILE_ID_BOTH_DIR_INFORMATION, FileIndex );
53AssertCompileMembersSameSizeAndOffset(FILE_BOTH_DIR_INFORMATION, CreationTime , FILE_ID_BOTH_DIR_INFORMATION, CreationTime );
54AssertCompileMembersSameSizeAndOffset(FILE_BOTH_DIR_INFORMATION, LastAccessTime , FILE_ID_BOTH_DIR_INFORMATION, LastAccessTime );
55AssertCompileMembersSameSizeAndOffset(FILE_BOTH_DIR_INFORMATION, LastWriteTime , FILE_ID_BOTH_DIR_INFORMATION, LastWriteTime );
56AssertCompileMembersSameSizeAndOffset(FILE_BOTH_DIR_INFORMATION, ChangeTime , FILE_ID_BOTH_DIR_INFORMATION, ChangeTime );
57AssertCompileMembersSameSizeAndOffset(FILE_BOTH_DIR_INFORMATION, EndOfFile , FILE_ID_BOTH_DIR_INFORMATION, EndOfFile );
58AssertCompileMembersSameSizeAndOffset(FILE_BOTH_DIR_INFORMATION, AllocationSize , FILE_ID_BOTH_DIR_INFORMATION, AllocationSize );
59AssertCompileMembersSameSizeAndOffset(FILE_BOTH_DIR_INFORMATION, FileAttributes , FILE_ID_BOTH_DIR_INFORMATION, FileAttributes );
60AssertCompileMembersSameSizeAndOffset(FILE_BOTH_DIR_INFORMATION, FileNameLength , FILE_ID_BOTH_DIR_INFORMATION, FileNameLength );
61AssertCompileMembersSameSizeAndOffset(FILE_BOTH_DIR_INFORMATION, EaSize , FILE_ID_BOTH_DIR_INFORMATION, EaSize );
62AssertCompileMembersSameSizeAndOffset(FILE_BOTH_DIR_INFORMATION, ShortNameLength, FILE_ID_BOTH_DIR_INFORMATION, ShortNameLength);
63AssertCompileMembersSameSizeAndOffset(FILE_BOTH_DIR_INFORMATION, ShortName , FILE_ID_BOTH_DIR_INFORMATION, ShortName );
64
65
66
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)
78{
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);
116}
117
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
582RTR3DECL(int) RTPathQueryInfoEx(const char *pszPath, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAdditionalAttribs, uint32_t fFlags)
583{
584 /*
585 * Validate input.
586 */
587 AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
588 AssertReturn(*pszPath, VERR_INVALID_PARAMETER);
589 AssertPtrReturn(pObjInfo, VERR_INVALID_POINTER);
590 AssertMsgReturn( enmAdditionalAttribs >= RTFSOBJATTRADD_NOTHING
591 && enmAdditionalAttribs <= RTFSOBJATTRADD_LAST,
592 ("Invalid enmAdditionalAttribs=%p\n", enmAdditionalAttribs),
593 VERR_INVALID_PARAMETER);
594 AssertMsgReturn(RTPATH_F_IS_VALID(fFlags, 0), ("%#x\n", fFlags), VERR_INVALID_PARAMETER);
595
596
597 /*
598 * Convert the input path and call common worker.
599 */
600 HANDLE hRootDir;
601 UNICODE_STRING NtName;
602 int rc = RTNtPathFromWinUtf8(&NtName, &hRootDir, pszPath);
603 if (RT_SUCCESS(rc))
604 {
605 rc = rtPathNtQueryInfoWorker(hRootDir, &NtName, pObjInfo, enmAdditionalAttribs, fFlags, pszPath);
606 RTNtPathFree(&NtName, &hRootDir);
607 }
608 return rc;
609}
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
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

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