VirtualBox

source: vbox/trunk/src/VBox/Runtime/r3/nt/direnum-r3-nt.cpp@ 69716

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

IPRT: More work on directory relative APIs (NT mainly) and VFS; introducing RTMkDir (test) tool.

  • Added RTVfsDirCreateDir
  • Added RTVfsChainOpenParentDir and RTVfsChainSplitOffFinalPath.
  • Added new tool for testing this called RTMkDir.
  • Fixed directory traversal problem with stddir by making it okay to return VERR_FILE_NOT_FOUND as well.
  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Id Revision
檔案大小: 34.2 KB
 
1/* $Id: direnum-r3-nt.cpp 69716 2017-11-16 14:31:25Z vboxsync $ */
2/** @file
3 * IPRT - Directory Enumeration, 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_DIR
32#include "internal-r3-nt.h"
33
34#include <iprt/dir.h>
35#include <iprt/path.h>
36#include <iprt/mem.h>
37#include <iprt/string.h>
38#include <iprt/assert.h>
39#include <iprt/err.h>
40#include <iprt/file.h>
41#include <iprt/log.h>
42#include "internal/fs.h"
43#include "internal/dir.h"
44#include "internal/path.h"
45
46
47/*********************************************************************************************************************************
48* Defined Constants And Macros *
49*********************************************************************************************************************************/
50/** Whether to return a single record (TRUE) or multiple (FALSE)o. */
51#define RTDIR_NT_SINGLE_RECORD FALSE
52
53/** Go hard on record chaining (has slight performance impact). */
54#ifdef RT_STRICT
55# define RTDIR_NT_STRICT
56#endif
57
58
59/* ASSUMES FileID comes after ShortName and the structs are identical up to that point. */
60AssertCompileMembersSameSizeAndOffset(FILE_BOTH_DIR_INFORMATION, NextEntryOffset, FILE_ID_BOTH_DIR_INFORMATION, NextEntryOffset);
61AssertCompileMembersSameSizeAndOffset(FILE_BOTH_DIR_INFORMATION, FileIndex , FILE_ID_BOTH_DIR_INFORMATION, FileIndex );
62AssertCompileMembersSameSizeAndOffset(FILE_BOTH_DIR_INFORMATION, CreationTime , FILE_ID_BOTH_DIR_INFORMATION, CreationTime );
63AssertCompileMembersSameSizeAndOffset(FILE_BOTH_DIR_INFORMATION, LastAccessTime , FILE_ID_BOTH_DIR_INFORMATION, LastAccessTime );
64AssertCompileMembersSameSizeAndOffset(FILE_BOTH_DIR_INFORMATION, LastWriteTime , FILE_ID_BOTH_DIR_INFORMATION, LastWriteTime );
65AssertCompileMembersSameSizeAndOffset(FILE_BOTH_DIR_INFORMATION, ChangeTime , FILE_ID_BOTH_DIR_INFORMATION, ChangeTime );
66AssertCompileMembersSameSizeAndOffset(FILE_BOTH_DIR_INFORMATION, EndOfFile , FILE_ID_BOTH_DIR_INFORMATION, EndOfFile );
67AssertCompileMembersSameSizeAndOffset(FILE_BOTH_DIR_INFORMATION, AllocationSize , FILE_ID_BOTH_DIR_INFORMATION, AllocationSize );
68AssertCompileMembersSameSizeAndOffset(FILE_BOTH_DIR_INFORMATION, FileAttributes , FILE_ID_BOTH_DIR_INFORMATION, FileAttributes );
69AssertCompileMembersSameSizeAndOffset(FILE_BOTH_DIR_INFORMATION, FileNameLength , FILE_ID_BOTH_DIR_INFORMATION, FileNameLength );
70AssertCompileMembersSameSizeAndOffset(FILE_BOTH_DIR_INFORMATION, EaSize , FILE_ID_BOTH_DIR_INFORMATION, EaSize );
71AssertCompileMembersSameSizeAndOffset(FILE_BOTH_DIR_INFORMATION, ShortNameLength, FILE_ID_BOTH_DIR_INFORMATION, ShortNameLength);
72AssertCompileMembersSameSizeAndOffset(FILE_BOTH_DIR_INFORMATION, ShortName , FILE_ID_BOTH_DIR_INFORMATION, ShortName );
73
74
75
76size_t rtDirNativeGetStructSize(const char *pszPath)
77{
78 NOREF(pszPath);
79 return sizeof(RTDIR);
80}
81
82
83int rtDirNativeOpen(PRTDIR pDir, char *pszPathBuf, uintptr_t hRelativeDir, void *pvNativeRelative)
84{
85 /*
86 * Convert the filter to UTF-16.
87 */
88 int rc;
89 pDir->pNtFilterStr = NULL;
90 if ( pDir->cchFilter > 0
91 && pDir->enmFilter == RTDIRFILTER_WINNT)
92 {
93 PRTUTF16 pwszTmp;
94 rc = RTStrToUtf16(pDir->pszFilter, &pwszTmp);
95 if (RT_FAILURE(rc))
96 return rc;
97 pDir->NtFilterStr.Buffer = pwszTmp;
98 pDir->NtFilterStr.Length = pDir->NtFilterStr.MaximumLength = (uint16_t)(RTUtf16Len(pwszTmp) * sizeof(RTUTF16));
99 pDir->pNtFilterStr = &pDir->NtFilterStr;
100 }
101
102 /*
103 * Try open the directory
104 */
105#ifdef IPRT_WITH_NT_PATH_PASSTHRU
106 bool fObjDir = false;
107#endif
108 if (hRelativeDir == ~(uintptr_t)0 && pvNativeRelative == NULL)
109 rc = RTNtPathOpenDir(pszPathBuf,
110 FILE_LIST_DIRECTORY | FILE_READ_ATTRIBUTES | FILE_TRAVERSE | SYNCHRONIZE,
111 FILE_SHARE_READ | FILE_SHARE_WRITE,
112 FILE_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT | FILE_SYNCHRONOUS_IO_NONALERT,
113 OBJ_CASE_INSENSITIVE,
114 &pDir->hDir,
115#ifdef IPRT_WITH_NT_PATH_PASSTHRU
116 &fObjDir
117#else
118 NULL
119#endif
120 );
121 else if (pvNativeRelative != NULL)
122 rc = RTNtPathOpenDirEx((HANDLE)hRelativeDir,
123 (struct _UNICODE_STRING *)pvNativeRelative,
124 FILE_LIST_DIRECTORY | FILE_READ_ATTRIBUTES | FILE_TRAVERSE | SYNCHRONIZE,
125 FILE_SHARE_READ | FILE_SHARE_WRITE,
126 FILE_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT | FILE_SYNCHRONOUS_IO_NONALERT,
127 OBJ_CASE_INSENSITIVE,
128 &pDir->hDir,
129#ifdef IPRT_WITH_NT_PATH_PASSTHRU
130 &fObjDir
131#else
132 NULL
133#endif
134
135 );
136 else
137 {
138 pDir->hDir = (HANDLE)hRelativeDir;
139 rc = VINF_SUCCESS;
140 }
141 if (RT_SUCCESS(rc))
142 {
143 /*
144 * Init data.
145 */
146 pDir->fDataUnread = false; /* spelling it out */
147 pDir->uDirDev = 0;
148#ifdef IPRT_WITH_NT_PATH_PASSTHRU
149 if (fObjDir)
150 pDir->enmInfoClass = FileMaximumInformation; /* object directory. */
151#endif
152 }
153 return rc;
154}
155
156
157RTDECL(int) RTDirClose(PRTDIR pDir)
158{
159 /*
160 * Validate input.
161 */
162 if (!pDir)
163 return VERR_INVALID_PARAMETER;
164 if (pDir->u32Magic != RTDIR_MAGIC)
165 {
166 AssertMsgFailed(("Invalid pDir=%p\n", pDir));
167 return VERR_INVALID_PARAMETER;
168 }
169
170 /*
171 * Close the handle.
172 */
173 pDir->u32Magic = ~RTDIR_MAGIC;
174 if (pDir->hDir != RTNT_INVALID_HANDLE_VALUE)
175 {
176 int rc = RTNtPathClose(pDir->hDir);
177 AssertRC(rc);
178 pDir->hDir = RTNT_INVALID_HANDLE_VALUE;
179 }
180 RTStrFree(pDir->pszName);
181 pDir->pszName = NULL;
182 RTUtf16Free(pDir->NtFilterStr.Buffer);
183 pDir->NtFilterStr.Buffer = NULL;
184 RTMemFree(pDir->pabBuffer);
185 pDir->pabBuffer = NULL;
186 RTMemFree(pDir);
187
188 return VINF_SUCCESS;
189}
190
191
192/**
193 * Checks the validity of the current record.
194 *
195 * @returns IPRT status code
196 * @param pThis The directory instance data.
197 */
198static int rtDirNtCheckRecord(PRTDIR pThis)
199{
200#ifdef RTDIR_NT_STRICT
201# ifdef IPRT_WITH_NT_PATH_PASSTHRU
202 if (pThis->enmInfoClass != FileMaximumInformation)
203# endif
204 {
205 uintptr_t uEndAddr;
206 if (pThis->enmInfoClass == FileIdBothDirectoryInformation)
207 uEndAddr = (uintptr_t)&pThis->uCurData.pBothId->FileName[0];
208 else
209 uEndAddr = (uintptr_t)&pThis->uCurData.pBoth->FileName[0];
210 AssertReturn(uEndAddr < (uintptr_t)&pThis->pabBuffer[pThis->cbBuffer], VERR_IO_GEN_FAILURE);
211
212 AssertReturn(pThis->uCurData.pBoth->FileNameLength < _64K, VERR_FILENAME_TOO_LONG);
213 AssertReturn((pThis->uCurData.pBoth->FileNameLength & 1) == 0, VERR_IO_GEN_FAILURE);
214
215 uEndAddr += pThis->uCurData.pBoth->FileNameLength;
216 AssertReturn(uEndAddr <= (uintptr_t)&pThis->pabBuffer[pThis->cbBuffer], VERR_IO_GEN_FAILURE);
217
218 AssertReturn((unsigned)pThis->uCurData.pBoth->ShortNameLength <= sizeof(pThis->uCurData.pBoth->ShortName),
219 VERR_IO_GEN_FAILURE);
220 }
221#else
222 RT_NOREF_PV(pThis);
223#endif
224
225 return VINF_SUCCESS;
226}
227
228
229/**
230 * Advances the buffer pointer.
231 *
232 * @param pThis The directory instance data.
233 */
234static int rtDirNtAdvanceBuffer(PRTDIR pThis)
235{
236 int rc;
237
238#ifdef IPRT_WITH_NT_PATH_PASSTHRU
239 if (pThis->enmInfoClass == FileMaximumInformation)
240 {
241 pThis->uCurData.pObjDir++;
242 pThis->fDataUnread = pThis->uCurData.pObjDir->Name.Length != 0;
243 return VINF_SUCCESS;
244 }
245#endif
246
247 pThis->fDataUnread = false;
248
249 uint32_t const offNext = pThis->uCurData.pBoth->NextEntryOffset;
250 if (offNext == 0)
251 return VINF_SUCCESS;
252
253#ifdef RTDIR_NT_STRICT
254 /* Make sure the next-record offset is beyond the current record. */
255 size_t cbRec;
256 if (pThis->enmInfoClass == FileIdBothDirectoryInformation)
257 cbRec = RT_UOFFSETOF(FILE_ID_BOTH_DIR_INFORMATION, FileName);
258 else
259 cbRec = RT_UOFFSETOF(FILE_BOTH_DIR_INFORMATION, FileName);
260 cbRec += pThis->uCurData.pBoth->FileNameLength;
261 AssertReturn(offNext >= cbRec, VERR_IO_GEN_FAILURE);
262#endif
263 pThis->uCurData.u += offNext;
264
265 rc = rtDirNtCheckRecord(pThis);
266 pThis->fDataUnread = RT_SUCCESS(rc);
267 return rc;
268}
269
270
271/**
272 * Fetches more data from the file system.
273 *
274 * @returns IPRT status code
275 * @param pThis The directory instance data.
276 */
277static int rtDirNtFetchMore(PRTDIR pThis)
278{
279 Assert(!pThis->fDataUnread);
280
281 /*
282 * Allocate the buffer the first time around.
283 * We do this in lazy fashion as some users of RTDirOpen will not actually
284 * list any files, just open it for various reasons.
285 *
286 * We also reduce the buffer size for networked devices as the windows 7-8.1,
287 * server 2012, ++ CIFS servers or/and IFSes screws up buffers larger than 64KB.
288 * There is an alternative hack below, btw. We'll leave both in for now.
289 */
290 bool fFirst = false;
291 if (!pThis->pabBuffer)
292 {
293 pThis->cbBufferAlloc = _256K;
294 if (true) /** @todo skip for known local devices, like the boot device? */
295 {
296 IO_STATUS_BLOCK Ios2 = RTNT_IO_STATUS_BLOCK_INITIALIZER;
297 FILE_FS_DEVICE_INFORMATION Info = { 0, 0 };
298 NTSTATUS rcNt2 = NtQueryVolumeInformationFile(pThis->hDir, &Ios2, &Info, sizeof(Info), FileFsDeviceInformation);
299 if ( !NT_SUCCESS(rcNt2)
300 || (Info.Characteristics & FILE_REMOTE_DEVICE)
301 || Info.DeviceType == FILE_DEVICE_NETWORK
302 || Info.DeviceType == FILE_DEVICE_NETWORK_FILE_SYSTEM
303 || Info.DeviceType == FILE_DEVICE_NETWORK_REDIRECTOR
304 || Info.DeviceType == FILE_DEVICE_SMB)
305 pThis->cbBufferAlloc = _64K;
306 }
307
308 fFirst = false;
309 pThis->pabBuffer = (uint8_t *)RTMemAlloc(pThis->cbBufferAlloc);
310 if (!pThis->pabBuffer)
311 {
312 do
313 {
314 pThis->cbBufferAlloc /= 4;
315 pThis->pabBuffer = (uint8_t *)RTMemAlloc(pThis->cbBufferAlloc);
316 } while (pThis->pabBuffer == NULL && pThis->cbBufferAlloc > _4K);
317 if (!pThis->pabBuffer)
318 return VERR_NO_MEMORY;
319 }
320
321 /*
322 * Also try determining the device number.
323 */
324 PFILE_FS_VOLUME_INFORMATION pVolInfo = (PFILE_FS_VOLUME_INFORMATION)pThis->pabBuffer;
325 pVolInfo->VolumeSerialNumber = 0;
326 IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
327 NTSTATUS rcNt = NtQueryVolumeInformationFile(pThis->hDir, &Ios,
328 pVolInfo, RT_MIN(_2K, pThis->cbBufferAlloc),
329 FileFsVolumeInformation);
330 if (NT_SUCCESS(rcNt) && NT_SUCCESS(Ios.Status))
331 pThis->uDirDev = pVolInfo->VolumeSerialNumber;
332 else
333 pThis->uDirDev = 0;
334 AssertCompile(sizeof(pThis->uDirDev) == sizeof(pVolInfo->VolumeSerialNumber));
335 /** @todo Grow RTDEV to 64-bit and add low dword of VolumeCreationTime to the top of uDirDev. */
336 }
337
338 /*
339 * Read more.
340 */
341 NTSTATUS rcNt;
342 IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
343 if (pThis->enmInfoClass != (FILE_INFORMATION_CLASS)0)
344 {
345#ifdef IPRT_WITH_NT_PATH_PASSTHRU
346 if (pThis->enmInfoClass == FileMaximumInformation)
347 {
348 Ios.Information = 0;
349 Ios.Status = rcNt = NtQueryDirectoryObject(pThis->hDir,
350 pThis->pabBuffer,
351 pThis->cbBufferAlloc,
352 RTDIR_NT_SINGLE_RECORD /*ReturnSingleEntry */,
353 FALSE /*RestartScan*/,
354 &pThis->uObjDirCtx,
355 (PULONG)&Ios.Information);
356 }
357 else
358#endif
359 rcNt = NtQueryDirectoryFile(pThis->hDir,
360 NULL /* Event */,
361 NULL /* ApcRoutine */,
362 NULL /* ApcContext */,
363 &Ios,
364 pThis->pabBuffer,
365 pThis->cbBufferAlloc,
366 pThis->enmInfoClass,
367 RTDIR_NT_SINGLE_RECORD /*ReturnSingleEntry */,
368 pThis->pNtFilterStr,
369 FALSE /*RestartScan */);
370 }
371 else
372 {
373 /*
374 * The first time around we have to figure which info class we can use
375 * as well as the right buffer size. We prefer an info class which
376 * gives us file IDs (Vista+ IIRC) and we prefer large buffers (for long
377 * ReFS file names and such), but we'll settle for whatever works...
378 *
379 * The windows 7 thru 8.1 CIFS servers have been observed to have
380 * trouble with large buffers, but weirdly only when listing large
381 * directories. Seems 0x10000 is the max. (Samba does not exhibit
382 * these problems, of course.)
383 *
384 * This complicates things. The buffer size issues causes an
385 * STATUS_INVALID_PARAMETER error. Now, you would expect the lack of
386 * FileIdBothDirectoryInformation support to return
387 * STATUS_INVALID_INFO_CLASS, but I'm not entirely sure if we can 100%
388 * depend on third IFSs to get that right. Nor, am I entirely confident
389 * that we can depend on them to check the class before the buffer size.
390 *
391 * Thus the mess.
392 */
393 if (RT_MAKE_U64(RTNtCurrentPeb()->OSMinorVersion, RTNtCurrentPeb()->OSMajorVersion) > RT_MAKE_U64(0,5) /* > W2K */)
394 pThis->enmInfoClass = FileIdBothDirectoryInformation; /* Introduced in XP, from I can tell. */
395 else
396 pThis->enmInfoClass = FileBothDirectoryInformation;
397 rcNt = NtQueryDirectoryFile(pThis->hDir,
398 NULL /* Event */,
399 NULL /* ApcRoutine */,
400 NULL /* ApcContext */,
401 &Ios,
402 pThis->pabBuffer,
403 pThis->cbBufferAlloc,
404 pThis->enmInfoClass,
405 RTDIR_NT_SINGLE_RECORD /*ReturnSingleEntry */,
406 pThis->pNtFilterStr,
407 FALSE /*RestartScan */);
408 if (NT_SUCCESS(rcNt))
409 { /* likely */ }
410 else
411 {
412 bool fRestartScan = false;
413 for (unsigned iRetry = 0; iRetry < 2; iRetry++)
414 {
415 if ( rcNt == STATUS_INVALID_INFO_CLASS
416 || rcNt == STATUS_INVALID_PARAMETER_8
417 || iRetry != 0)
418 pThis->enmInfoClass = FileBothDirectoryInformation;
419
420 uint32_t cbBuffer = pThis->cbBufferAlloc;
421 if ( rcNt == STATUS_INVALID_PARAMETER
422 || rcNt == STATUS_INVALID_PARAMETER_7
423 || rcNt == STATUS_INVALID_NETWORK_RESPONSE
424 || iRetry != 0)
425 {
426 cbBuffer = RT_MIN(cbBuffer / 2, _64K);
427 fRestartScan = true;
428 }
429
430 for (;;)
431 {
432 rcNt = NtQueryDirectoryFile(pThis->hDir,
433 NULL /* Event */,
434 NULL /* ApcRoutine */,
435 NULL /* ApcContext */,
436 &Ios,
437 pThis->pabBuffer,
438 cbBuffer,
439 pThis->enmInfoClass,
440 RTDIR_NT_SINGLE_RECORD /*ReturnSingleEntry */,
441 pThis->pNtFilterStr,
442 fRestartScan);
443 if ( NT_SUCCESS(rcNt)
444 || cbBuffer == pThis->cbBufferAlloc
445 || cbBuffer <= sizeof(*pThis->uCurData.pBothId) + sizeof(WCHAR) * 260)
446 break;
447
448 /* Reduce the buffer size agressivly and try again. We fall back to
449 FindFirstFile values for the final lap. This means we'll do 4 rounds
450 with the current initial buffer size (64KB, 8KB, 1KB, 0x278/0x268). */
451 cbBuffer /= 8;
452 if (cbBuffer < 1024)
453 cbBuffer = pThis->enmInfoClass == FileIdBothDirectoryInformation
454 ? sizeof(*pThis->uCurData.pBothId) + sizeof(WCHAR) * 260
455 : sizeof(*pThis->uCurData.pBoth) + sizeof(WCHAR) * 260;
456 }
457 if (NT_SUCCESS(rcNt))
458 {
459 pThis->cbBufferAlloc = cbBuffer;
460 break;
461 }
462 }
463 }
464 }
465 if (!NT_SUCCESS(rcNt))
466 {
467 /* Note! VBoxSVR and CIFS file systems both ends up with STATUS_NO_SUCH_FILE here instead of STATUS_NO_MORE_FILES. */
468 if (rcNt == STATUS_NO_MORE_FILES || rcNt == STATUS_NO_MORE_ENTRIES || rcNt == STATUS_NO_SUCH_FILE)
469 return VERR_NO_MORE_FILES;
470 return RTErrConvertFromNtStatus(rcNt);
471 }
472 Assert(Ios.Information > sizeof(*pThis->uCurData.pBoth));
473
474 /*
475 * Set up the data members.
476 */
477 pThis->uCurData.u = (uintptr_t)pThis->pabBuffer;
478 pThis->cbBuffer = Ios.Information;
479
480 int rc = rtDirNtCheckRecord(pThis);
481 pThis->fDataUnread = RT_SUCCESS(rc);
482
483 return rc;
484}
485
486
487/**
488 * Converts the name from UTF-16 to UTF-8.
489 *
490 * Fortunately, the names are relative to the directory, so we won't have to do
491 * any sweaty path style coversion. :-)
492 *
493 * @returns IPRT status code
494 * @param pThis The directory instance data.
495 * @param cbName The file name length in bytes.
496 * @param pwsName The file name, not terminated.
497 */
498static int rtDirNtConvertName(PRTDIR pThis, uint32_t cbName, PCRTUTF16 pwsName)
499{
500 int rc = RTUtf16ToUtf8Ex(pwsName, cbName / 2, &pThis->pszName, pThis->cbNameAlloc, &pThis->cchName);
501 if (RT_SUCCESS(rc))
502 {
503 if (!pThis->cbNameAlloc)
504 pThis->cbNameAlloc = pThis->cchName + 1;
505 }
506 else if (rc == VERR_BUFFER_OVERFLOW)
507 {
508 RTStrFree(pThis->pszName);
509 pThis->pszName = NULL;
510 pThis->cbNameAlloc = 0;
511
512 rc = RTUtf16ToUtf8Ex(pwsName, cbName / 2, &pThis->pszName, pThis->cbNameAlloc, &pThis->cchName);
513 if (RT_SUCCESS(rc))
514 pThis->cbNameAlloc = pThis->cchName + 1;
515 }
516 Assert(RT_SUCCESS(rc) ? pThis->pszName != NULL : pThis->pszName == NULL);
517 return rc;
518}
519
520
521/**
522 * Converts the name of the current record.
523 *
524 * @returns IPRT status code.
525 * @param pThis The directory instance data.
526 */
527static int rtDirNtConvertCurName(PRTDIR pThis)
528{
529 switch (pThis->enmInfoClass)
530 {
531 case FileIdBothDirectoryInformation:
532 return rtDirNtConvertName(pThis, pThis->uCurData.pBothId->FileNameLength, pThis->uCurData.pBothId->FileName);
533 case FileBothDirectoryInformation:
534 return rtDirNtConvertName(pThis, pThis->uCurData.pBoth->FileNameLength, pThis->uCurData.pBoth->FileName);
535#ifdef IPRT_WITH_NT_PATH_PASSTHRU
536 case FileMaximumInformation:
537 return rtDirNtConvertName(pThis, pThis->uCurData.pObjDir->Name.Length, pThis->uCurData.pObjDir->Name.Buffer);
538#endif
539
540 default:
541 AssertFailedReturn(VERR_INTERNAL_ERROR_3);
542 }
543}
544
545
546RTDECL(int) RTDirRead(PRTDIR pDir, PRTDIRENTRY pDirEntry, size_t *pcbDirEntry)
547{
548 int rc;
549
550 /*
551 * Validate input.
552 */
553 AssertPtrReturn(pDir, VERR_INVALID_POINTER);
554 AssertReturn(pDir->u32Magic == RTDIR_MAGIC, VERR_INVALID_HANDLE);
555 AssertPtrReturn(pDirEntry, VERR_INVALID_POINTER);
556 size_t cbDirEntry = sizeof(*pDirEntry);
557 if (pcbDirEntry)
558 {
559 cbDirEntry = *pcbDirEntry;
560 AssertMsgReturn(cbDirEntry >= RT_UOFFSETOF(RTDIRENTRY, szName[2]),
561 ("Invalid *pcbDirEntry=%d (min %d)\n", *pcbDirEntry, RT_OFFSETOF(RTDIRENTRY, szName[2])),
562 VERR_INVALID_PARAMETER);
563 }
564
565 /*
566 * Fetch data?
567 */
568 if (!pDir->fDataUnread)
569 {
570 rc = rtDirNtFetchMore(pDir);
571 if (RT_FAILURE(rc))
572 return rc;
573 }
574
575 /*
576 * Convert the filename to UTF-8.
577 */
578 rc = rtDirNtConvertCurName(pDir);
579 if (RT_FAILURE(rc))
580 return rc;
581
582 /*
583 * Check if we've got enough space to return the data.
584 */
585 const char *pszName = pDir->pszName;
586 const size_t cchName = pDir->cchName;
587 const size_t cbRequired = RT_OFFSETOF(RTDIRENTRY, szName[1]) + cchName;
588 if (pcbDirEntry)
589 *pcbDirEntry = cbRequired;
590 if (cbRequired > cbDirEntry)
591 return VERR_BUFFER_OVERFLOW;
592
593 /*
594 * Setup the returned data.
595 */
596 pDirEntry->cbName = (uint16_t)cchName; Assert(pDirEntry->cbName == cchName);
597 memcpy(pDirEntry->szName, pszName, cchName + 1);
598
599 pDirEntry->INodeId = pDir->enmInfoClass == FileIdBothDirectoryInformation
600 ? pDir->uCurData.pBothId->FileId.QuadPart : 0;
601
602#ifdef IPRT_WITH_NT_PATH_PASSTHRU
603 if (pDir->enmInfoClass != FileMaximumInformation)
604#endif
605 {
606 switch ( pDir->uCurData.pBoth->FileAttributes
607 & (FILE_ATTRIBUTE_REPARSE_POINT | FILE_ATTRIBUTE_REPARSE_POINT | FILE_ATTRIBUTE_DIRECTORY))
608 {
609 default:
610 AssertFailed();
611 case 0:
612 pDirEntry->enmType = RTDIRENTRYTYPE_FILE;
613 break;
614
615 case FILE_ATTRIBUTE_DIRECTORY:
616 pDirEntry->enmType = RTDIRENTRYTYPE_DIRECTORY;
617 break;
618
619 case FILE_ATTRIBUTE_REPARSE_POINT:
620 case FILE_ATTRIBUTE_REPARSE_POINT | FILE_ATTRIBUTE_DIRECTORY:
621 /* EaSize is here reused for returning the repharse tag value. */
622 if (pDir->uCurData.pBoth->EaSize == IO_REPARSE_TAG_SYMLINK)
623 pDirEntry->enmType = RTDIRENTRYTYPE_SYMLINK;
624 break;
625 }
626 }
627#ifdef IPRT_WITH_NT_PATH_PASSTHRU
628 else
629 {
630 pDirEntry->enmType = RTDIRENTRYTYPE_UNKNOWN;
631 if (rtNtCompWideStrAndAscii(pDir->uCurData.pObjDir->TypeName.Buffer, pDir->uCurData.pObjDir->TypeName.Length,
632 RT_STR_TUPLE("Directory")))
633 pDirEntry->enmType = RTDIRENTRYTYPE_DIRECTORY;
634 else if (rtNtCompWideStrAndAscii(pDir->uCurData.pObjDir->TypeName.Buffer, pDir->uCurData.pObjDir->TypeName.Length,
635 RT_STR_TUPLE("SymbolicLink")))
636 pDirEntry->enmType = RTDIRENTRYTYPE_SYMLINK;
637 }
638#endif
639
640 return rtDirNtAdvanceBuffer(pDir);
641}
642
643
644RTDECL(int) RTDirReadEx(PRTDIR pDir, PRTDIRENTRYEX pDirEntry, size_t *pcbDirEntry,
645 RTFSOBJATTRADD enmAdditionalAttribs, uint32_t fFlags)
646{
647 int rc;
648
649 /*
650 * Validate input.
651 */
652 AssertPtrReturn(pDir, VERR_INVALID_POINTER);
653 AssertReturn(pDir->u32Magic == RTDIR_MAGIC, VERR_INVALID_HANDLE);
654 AssertPtrReturn(pDirEntry, VERR_INVALID_POINTER);
655
656 AssertReturn(enmAdditionalAttribs >= RTFSOBJATTRADD_NOTHING && enmAdditionalAttribs <= RTFSOBJATTRADD_LAST,
657 VERR_INVALID_PARAMETER);
658 AssertMsgReturn(RTPATH_F_IS_VALID(fFlags, 0), ("%#x\n", fFlags), VERR_INVALID_PARAMETER);
659
660 size_t cbDirEntry = sizeof(*pDirEntry);
661 if (pcbDirEntry)
662 {
663 cbDirEntry = *pcbDirEntry;
664 AssertMsgReturn(cbDirEntry >= RT_UOFFSETOF(RTDIRENTRYEX, szName[2]),
665 ("Invalid *pcbDirEntry=%d (min %d)\n", *pcbDirEntry, RT_OFFSETOF(RTDIRENTRYEX, szName[2])),
666 VERR_INVALID_PARAMETER);
667 }
668
669 /*
670 * Fetch data?
671 */
672 if (!pDir->fDataUnread)
673 {
674 rc = rtDirNtFetchMore(pDir);
675 if (RT_FAILURE(rc))
676 return rc;
677 }
678
679 /*
680 * Convert the filename to UTF-8.
681 */
682 rc = rtDirNtConvertCurName(pDir);
683 if (RT_FAILURE(rc))
684 return rc;
685
686 /*
687 * Check if we've got enough space to return the data.
688 */
689 const char *pszName = pDir->pszName;
690 const size_t cchName = pDir->cchName;
691 const size_t cbRequired = RT_OFFSETOF(RTDIRENTRYEX, szName[1]) + cchName;
692 if (pcbDirEntry)
693 *pcbDirEntry = cbRequired;
694 if (cbRequired > cbDirEntry)
695 return VERR_BUFFER_OVERFLOW;
696
697 /*
698 * Setup the returned data.
699 */
700 PFILE_BOTH_DIR_INFORMATION pBoth = pDir->uCurData.pBoth;
701
702 pDirEntry->cbName = (uint16_t)cchName; Assert(pDirEntry->cbName == cchName);
703 memcpy(pDirEntry->szName, pszName, cchName + 1);
704 memset(pDirEntry->wszShortName, 0, sizeof(pDirEntry->wszShortName));
705#ifdef IPRT_WITH_NT_PATH_PASSTHRU
706 if (pDir->enmInfoClass != FileMaximumInformation)
707#endif
708 {
709 uint8_t cbShort = pBoth->ShortNameLength;
710 if (cbShort > 0)
711 {
712 AssertStmt(cbShort < sizeof(pDirEntry->wszShortName), cbShort = sizeof(pDirEntry->wszShortName) - 2);
713 memcpy(pDirEntry->wszShortName, pBoth->ShortName, cbShort);
714 pDirEntry->cwcShortName = cbShort / 2;
715 }
716 else
717 pDirEntry->cwcShortName = 0;
718
719 pDirEntry->Info.cbObject = pBoth->EndOfFile.QuadPart;
720 pDirEntry->Info.cbAllocated = pBoth->AllocationSize.QuadPart;
721
722 Assert(sizeof(uint64_t) == sizeof(pBoth->CreationTime));
723 RTTimeSpecSetNtTime(&pDirEntry->Info.BirthTime, pBoth->CreationTime.QuadPart);
724 RTTimeSpecSetNtTime(&pDirEntry->Info.AccessTime, pBoth->LastAccessTime.QuadPart);
725 RTTimeSpecSetNtTime(&pDirEntry->Info.ModificationTime, pBoth->LastWriteTime.QuadPart);
726 RTTimeSpecSetNtTime(&pDirEntry->Info.ChangeTime, pBoth->ChangeTime.QuadPart);
727
728 pDirEntry->Info.Attr.fMode = rtFsModeFromDos((pBoth->FileAttributes << RTFS_DOS_SHIFT) & RTFS_DOS_MASK_NT,
729 pszName, cchName, pBoth->EaSize);
730 }
731#ifdef IPRT_WITH_NT_PATH_PASSTHRU
732 else
733 {
734 pDirEntry->cwcShortName = 0;
735 pDirEntry->Info.cbObject = 0;
736 pDirEntry->Info.cbAllocated = 0;
737 RTTimeSpecSetNtTime(&pDirEntry->Info.BirthTime, 0);
738 RTTimeSpecSetNtTime(&pDirEntry->Info.AccessTime, 0);
739 RTTimeSpecSetNtTime(&pDirEntry->Info.ModificationTime, 0);
740 RTTimeSpecSetNtTime(&pDirEntry->Info.ChangeTime, 0);
741
742 if (rtNtCompWideStrAndAscii(pDir->uCurData.pObjDir->TypeName.Buffer, pDir->uCurData.pObjDir->TypeName.Length,
743 RT_STR_TUPLE("Directory")))
744 pDirEntry->Info.Attr.fMode = RTFS_DOS_DIRECTORY | RTFS_TYPE_DIRECTORY | 0777;
745 else if (rtNtCompWideStrAndAscii(pDir->uCurData.pObjDir->TypeName.Buffer, pDir->uCurData.pObjDir->TypeName.Length,
746 RT_STR_TUPLE("SymbolicLink")))
747 pDirEntry->Info.Attr.fMode = RTFS_DOS_NT_REPARSE_POINT | RTFS_TYPE_SYMLINK | 0777;
748 else if (rtNtCompWideStrAndAscii(pDir->uCurData.pObjDir->TypeName.Buffer, pDir->uCurData.pObjDir->TypeName.Length,
749 RT_STR_TUPLE("Device")))
750 pDirEntry->Info.Attr.fMode = RTFS_DOS_NT_DEVICE | RTFS_TYPE_DEV_CHAR | 0666;
751 else
752 pDirEntry->Info.Attr.fMode = RTFS_DOS_NT_NORMAL | RTFS_TYPE_FILE | 0666;
753 }
754#endif
755
756 /*
757 * Requested attributes (we cannot provide anything actually).
758 */
759 switch (enmAdditionalAttribs)
760 {
761 case RTFSOBJATTRADD_EASIZE:
762 pDirEntry->Info.Attr.enmAdditional = RTFSOBJATTRADD_EASIZE;
763#ifdef IPRT_WITH_NT_PATH_PASSTHRU
764 if (pDir->enmInfoClass == FileMaximumInformation)
765 pDirEntry->Info.Attr.u.EASize.cb = 0;
766 else
767#endif
768 pDirEntry->Info.Attr.u.EASize.cb = pBoth->EaSize;
769 break;
770
771 case RTFSOBJATTRADD_UNIX:
772 pDirEntry->Info.Attr.enmAdditional = RTFSOBJATTRADD_UNIX;
773 pDirEntry->Info.Attr.u.Unix.uid = NIL_RTUID;
774 pDirEntry->Info.Attr.u.Unix.gid = NIL_RTGID;
775 pDirEntry->Info.Attr.u.Unix.cHardlinks = 1;
776 pDirEntry->Info.Attr.u.Unix.INodeIdDevice = pDir->uDirDev;
777 pDirEntry->Info.Attr.u.Unix.INodeId = 0;
778 if ( pDir->enmInfoClass == FileIdBothDirectoryInformation
779 && pDir->uCurData.pBothId->FileId.QuadPart != UINT64_MAX)
780 pDirEntry->Info.Attr.u.Unix.INodeId = pDir->uCurData.pBothId->FileId.QuadPart;
781 pDirEntry->Info.Attr.u.Unix.fFlags = 0;
782 pDirEntry->Info.Attr.u.Unix.GenerationId = 0;
783 pDirEntry->Info.Attr.u.Unix.Device = 0;
784 break;
785
786 case RTFSOBJATTRADD_NOTHING:
787 pDirEntry->Info.Attr.enmAdditional = RTFSOBJATTRADD_NOTHING;
788 break;
789
790 case RTFSOBJATTRADD_UNIX_OWNER:
791 pDirEntry->Info.Attr.enmAdditional = RTFSOBJATTRADD_UNIX_OWNER;
792 pDirEntry->Info.Attr.u.UnixOwner.uid = NIL_RTUID;
793 pDirEntry->Info.Attr.u.UnixOwner.szName[0] = '\0'; /** @todo return something sensible here. */
794 break;
795
796 case RTFSOBJATTRADD_UNIX_GROUP:
797 pDirEntry->Info.Attr.enmAdditional = RTFSOBJATTRADD_UNIX_GROUP;
798 pDirEntry->Info.Attr.u.UnixGroup.gid = NIL_RTGID;
799 pDirEntry->Info.Attr.u.UnixGroup.szName[0] = '\0';
800 break;
801
802 default:
803 AssertMsgFailed(("Impossible!\n"));
804 return VERR_INTERNAL_ERROR;
805 }
806
807 /*
808 * Follow links if requested.
809 */
810 if ( (fFlags & RTPATH_F_FOLLOW_LINK)
811 && RTFS_IS_SYMLINK(fFlags))
812 {
813 /** @todo Symlinks: Find[First|Next]FileW will return info about
814 the link, so RTPATH_F_FOLLOW_LINK is not handled correctly. */
815 }
816
817 /*
818 * Finally advance the buffer.
819 */
820 return rtDirNtAdvanceBuffer(pDir);
821}
822
823
824
825RTR3DECL(int) RTDirQueryInfo(PRTDIR pDir, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAdditionalAttribs)
826{
827 AssertPtrReturn(pDir, VERR_INVALID_POINTER);
828 AssertReturn(pDir->u32Magic == RTDIR_MAGIC, VERR_INVALID_HANDLE);
829 AssertReturn(enmAdditionalAttribs >= RTFSOBJATTRADD_NOTHING && enmAdditionalAttribs <= RTFSOBJATTRADD_LAST,
830 VERR_INVALID_PARAMETER);
831
832 if (pDir->enmInfoClass == FileMaximumInformation)
833 {
834 /*
835 * Directory object (see similar code above and rtPathNtQueryInfoInDirectoryObject).
836 */
837 pObjInfo->cbObject = 0;
838 pObjInfo->cbAllocated = 0;
839 RTTimeSpecSetNtTime(&pObjInfo->BirthTime, 0);
840 RTTimeSpecSetNtTime(&pObjInfo->AccessTime, 0);
841 RTTimeSpecSetNtTime(&pObjInfo->ModificationTime, 0);
842 RTTimeSpecSetNtTime(&pObjInfo->ChangeTime, 0);
843 pObjInfo->Attr.fMode = RTFS_DOS_DIRECTORY | RTFS_TYPE_DIRECTORY | 0777;
844 pObjInfo->Attr.enmAdditional = enmAdditionalAttribs;
845 switch (enmAdditionalAttribs)
846 {
847 case RTFSOBJATTRADD_NOTHING:
848 case RTFSOBJATTRADD_UNIX:
849 pObjInfo->Attr.u.Unix.uid = NIL_RTUID;
850 pObjInfo->Attr.u.Unix.gid = NIL_RTGID;
851 pObjInfo->Attr.u.Unix.cHardlinks = 1;
852 pObjInfo->Attr.u.Unix.INodeIdDevice = pDir->uDirDev;
853 pObjInfo->Attr.u.Unix.INodeId = 0;
854 pObjInfo->Attr.u.Unix.fFlags = 0;
855 pObjInfo->Attr.u.Unix.GenerationId = 0;
856 pObjInfo->Attr.u.Unix.Device = 0;
857 break;
858
859 case RTFSOBJATTRADD_EASIZE:
860 pObjInfo->Attr.u.EASize.cb = 0;
861 break;
862
863 case RTFSOBJATTRADD_UNIX_OWNER:
864 pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_UNIX_OWNER;
865 pObjInfo->Attr.u.UnixOwner.uid = NIL_RTUID;
866 pObjInfo->Attr.u.UnixOwner.szName[0] = '\0'; /** @todo return something sensible here. */
867 break;
868
869 case RTFSOBJATTRADD_UNIX_GROUP:
870 pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_UNIX_GROUP;
871 pObjInfo->Attr.u.UnixGroup.gid = NIL_RTGID;
872 pObjInfo->Attr.u.UnixGroup.szName[0] = '\0';
873 break;
874
875 default:
876 AssertMsgFailed(("Impossible!\n"));
877 return VERR_INTERNAL_ERROR_2;
878 }
879 return VINF_SUCCESS;
880 }
881
882 /*
883 * Regular directory file.
884 */
885 uint8_t abBuf[_2K];
886 return rtPathNtQueryInfoFromHandle(pDir->hDir, abBuf, sizeof(abBuf), pObjInfo, enmAdditionalAttribs, "", 0);
887}
888
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

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