VirtualBox

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

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

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