VirtualBox

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

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

iprt/dir: Morphing PRTDIR into a handle named RTDIR. (Been wanting to correct this for years. Don't know why I makde it a pointer rather than an abstrct handle like everything else.)

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

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