VirtualBox

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

Last change on this file since 70419 was 70419, checked in by vboxsync, 7 years ago

iprt/direnum-r3-nt.cpp: Workaround for buggy fat driver on NT 3.1 (and possibly later). Fixed assertion.

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

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