VirtualBox

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

Last change on this file since 68246 was 64646, checked in by vboxsync, 8 years ago

RTPathQueryInfo-nt.cpp: Synchronous I/O operations, forgot.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 20.0 KB
Line 
1/* $Id: RTPathQueryInfo-nt.cpp 64646 2016-11-10 20:08:28Z vboxsync $ */
2/** @file
3 * IPRT - RTPathQueryInfo[Ex], Native NT.
4 */
5
6/*
7 * Copyright (C) 2006-2016 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
39
40/* ASSUMES FileID comes after ShortName and the structs are identical up to that point. */
41AssertCompileMembersSameSizeAndOffset(FILE_BOTH_DIR_INFORMATION, NextEntryOffset, FILE_ID_BOTH_DIR_INFORMATION, NextEntryOffset);
42AssertCompileMembersSameSizeAndOffset(FILE_BOTH_DIR_INFORMATION, FileIndex , FILE_ID_BOTH_DIR_INFORMATION, FileIndex );
43AssertCompileMembersSameSizeAndOffset(FILE_BOTH_DIR_INFORMATION, CreationTime , FILE_ID_BOTH_DIR_INFORMATION, CreationTime );
44AssertCompileMembersSameSizeAndOffset(FILE_BOTH_DIR_INFORMATION, LastAccessTime , FILE_ID_BOTH_DIR_INFORMATION, LastAccessTime );
45AssertCompileMembersSameSizeAndOffset(FILE_BOTH_DIR_INFORMATION, LastWriteTime , FILE_ID_BOTH_DIR_INFORMATION, LastWriteTime );
46AssertCompileMembersSameSizeAndOffset(FILE_BOTH_DIR_INFORMATION, ChangeTime , FILE_ID_BOTH_DIR_INFORMATION, ChangeTime );
47AssertCompileMembersSameSizeAndOffset(FILE_BOTH_DIR_INFORMATION, EndOfFile , FILE_ID_BOTH_DIR_INFORMATION, EndOfFile );
48AssertCompileMembersSameSizeAndOffset(FILE_BOTH_DIR_INFORMATION, AllocationSize , FILE_ID_BOTH_DIR_INFORMATION, AllocationSize );
49AssertCompileMembersSameSizeAndOffset(FILE_BOTH_DIR_INFORMATION, FileAttributes , FILE_ID_BOTH_DIR_INFORMATION, FileAttributes );
50AssertCompileMembersSameSizeAndOffset(FILE_BOTH_DIR_INFORMATION, FileNameLength , FILE_ID_BOTH_DIR_INFORMATION, FileNameLength );
51AssertCompileMembersSameSizeAndOffset(FILE_BOTH_DIR_INFORMATION, EaSize , FILE_ID_BOTH_DIR_INFORMATION, EaSize );
52AssertCompileMembersSameSizeAndOffset(FILE_BOTH_DIR_INFORMATION, ShortNameLength, FILE_ID_BOTH_DIR_INFORMATION, ShortNameLength);
53AssertCompileMembersSameSizeAndOffset(FILE_BOTH_DIR_INFORMATION, ShortName , FILE_ID_BOTH_DIR_INFORMATION, ShortName );
54
55
56
57RTR3DECL(int) RTPathQueryInfo(const char *pszPath, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAdditionalAttribs)
58{
59 return RTPathQueryInfoEx(pszPath, pObjInfo, enmAdditionalAttribs, RTPATH_F_ON_LINK);
60}
61
62#if 1
63RTR3DECL(int) RTPathQueryInfoEx(const char *pszPath, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAdditionalAttribs, uint32_t fFlags)
64{
65 /*
66 * Validate input.
67 */
68 AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
69 AssertReturn(*pszPath, VERR_INVALID_PARAMETER);
70 AssertPtrReturn(pObjInfo, VERR_INVALID_POINTER);
71 AssertMsgReturn( enmAdditionalAttribs >= RTFSOBJATTRADD_NOTHING
72 && enmAdditionalAttribs <= RTFSOBJATTRADD_LAST,
73 ("Invalid enmAdditionalAttribs=%p\n", enmAdditionalAttribs),
74 VERR_INVALID_PARAMETER);
75 AssertMsgReturn(RTPATH_F_IS_VALID(fFlags, 0), ("%#x\n", fFlags), VERR_INVALID_PARAMETER);
76
77
78 /*
79 * Convert the input path.
80 */
81 HANDLE hRootDir;
82 UNICODE_STRING NtName;
83 int rc = RTNtPathFromWinUtf8(&NtName, &hRootDir, pszPath);
84 if (RT_SUCCESS(rc))
85 {
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
385 RTNtPathFree(&NtName, &hRootDir);
386 }
387
388 return rc;
389}
390#endif
391
Note: See TracBrowser for help on using the repository browser.

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