VirtualBox

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

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

*: scm cleanup run.

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

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