VirtualBox

source: kBuild/trunk/src/lib/nt/ntdir.c@ 3005

Last change on this file since 3005 was 3005, checked in by bird, 9 years ago

fts-nt.c: Wide char support, part 4.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 19.7 KB
Line 
1/* $Id: ntdir.c 3005 2016-11-06 00:07:37Z bird $ */
2/** @file
3 * MSC + NT opendir, readdir, telldir, seekdir, and closedir.
4 */
5
6/*
7 * Copyright (c) 2005-2016 knut st. osmundsen <[email protected]>
8 *
9 * Permission is hereby granted, free of charge, to any person obtaining a
10 * copy of this software and associated documentation files (the "Software"),
11 * to deal in the Software without restriction, including without limitation
12 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
13 * and/or sell copies of the Software, and to permit persons to whom the
14 * Software is furnished to do so, subject to the following conditions:
15 *
16 * The above copyright notice and this permission notice shall be included
17 * in all copies or substantial portions of the Software.
18 *
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
24 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
25 * IN THE SOFTWARE.
26 *
27 * Alternatively, the content of this file may be used under the terms of the
28 * GPL version 2 or later, or LGPL version 2.1 or later.
29 */
30
31
32/*******************************************************************************
33* Header Files *
34*******************************************************************************/
35#include <stdio.h>
36#include <errno.h>
37#include <malloc.h>
38#include <assert.h>
39
40#include "ntstuff.h"
41#include "nthlp.h"
42#include "ntdir.h"
43
44
45/**
46 * Implements opendir.
47 */
48BirdDir_T *birdDirOpen(const char *pszPath)
49{
50 HANDLE hDir = birdOpenFile(pszPath,
51 FILE_READ_DATA | SYNCHRONIZE,
52 FILE_ATTRIBUTE_NORMAL,
53 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
54 FILE_OPEN,
55 FILE_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT | FILE_SYNCHRONOUS_IO_NONALERT,
56 OBJ_CASE_INSENSITIVE);
57 if (hDir != INVALID_HANDLE_VALUE)
58 {
59 BirdDir_T *pDir = birdDirOpenFromHandle((void *)hDir, NULL, BIRDDIR_F_CLOSE_HANDLE);
60 if (pDir)
61 return pDir;
62 birdCloseFile(hDir);
63 }
64 return NULL;
65}
66
67
68/**
69 * Alternative opendir, with extra stat() info returned by readdir().
70 */
71BirdDir_T *birdDirOpenExtraInfo(const char *pszPath)
72{
73 HANDLE hDir = birdOpenFile(pszPath,
74 FILE_READ_DATA | SYNCHRONIZE,
75 FILE_ATTRIBUTE_NORMAL,
76 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
77 FILE_OPEN,
78 FILE_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT | FILE_SYNCHRONOUS_IO_NONALERT,
79 OBJ_CASE_INSENSITIVE);
80 if (hDir != INVALID_HANDLE_VALUE)
81 {
82 BirdDir_T *pDir = birdDirOpenFromHandle((void *)hDir, NULL, BIRDDIR_F_CLOSE_HANDLE | BIRDDIR_F_EXTRA_INFO);
83 if (pDir)
84 return pDir;
85 birdCloseFile(hDir);
86 }
87 return NULL;
88}
89
90
91BirdDir_T *birdDirOpenExW(void *hRoot, const wchar_t *pwszPath, const wchar_t *pwszFilter, unsigned fFlags)
92{
93 HANDLE hDir = birdOpenFileExW((HANDLE)hRoot,
94 pwszPath,
95 FILE_READ_DATA | SYNCHRONIZE,
96 FILE_ATTRIBUTE_NORMAL,
97 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
98 FILE_OPEN,
99 FILE_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT | FILE_SYNCHRONOUS_IO_NONALERT,
100 OBJ_CASE_INSENSITIVE);
101 if (hDir != INVALID_HANDLE_VALUE)
102 {
103 BirdDir_T *pDir = birdDirOpenFromHandle((void *)hDir, pwszFilter, fFlags | BIRDDIR_F_CLOSE_HANDLE);
104 if (pDir)
105 return pDir;
106 birdCloseFile(hDir);
107 }
108 return NULL;
109}
110
111
112/**
113 * Internal worker for birdStatModTimeOnly.
114 */
115BirdDir_T *birdDirOpenFromHandle(void *pvHandle, const void *pvReserved, unsigned fFlags)
116{
117 if (!pvReserved && !(fFlags & BIRDDIR_F_STATIC_ALLOC))
118 {
119 /*
120 * Allocate and initialize the directory enum handle.
121 */
122 BirdDir_T *pDir = (BirdDir_T *)birdMemAlloc(sizeof(*pDir));
123 if (pDir)
124 {
125 pDir->uMagic = BIRD_DIR_MAGIC;
126 pDir->fFlags = fFlags;
127 pDir->pvHandle = pvHandle;
128 pDir->uDev = 0;
129 pDir->offPos = 0;
130 pDir->fHaveData = 0;
131 pDir->fFirst = 1;
132 pDir->iInfoClass = fFlags & BIRDDIR_F_EXTRA_INFO ? MyFileIdFullDirectoryInformation : MyFileNamesInformation;
133 pDir->offBuf = 0;
134 pDir->cbBuf = 0;
135 pDir->pabBuf = NULL;
136 return pDir;
137 }
138 }
139 else
140 {
141 assert(!(fFlags & BIRDDIR_F_STATIC_ALLOC));
142 assert(pvReserved == NULL);
143 }
144 birdSetErrnoToInvalidArg();
145 return NULL;
146}
147
148
149/**
150 * Special API that takes a preallocated BirdDir_T and can be called again
151 * without involving birdDirClose.
152 *
153 *
154 */
155BirdDir_T *birdDirOpenFromHandleWithReuse(BirdDir_T *pDir, void *pvHandle, const void *pvReserved, unsigned fFlags)
156{
157 if (!pvReserved)
158 {
159 /*
160 * Allocate and initialize the directory enum handle.
161 */
162 if (pDir)
163 {
164 if (pDir->uMagic == BIRD_DIR_MAGIC)
165 {
166 if ( (pDir->fFlags & BIRDDIR_F_CLOSE_HANDLE)
167 && pDir->pvHandle != INVALID_HANDLE_VALUE)
168 birdCloseFile((HANDLE)pDir->pvHandle);
169 }
170 else
171 {
172 pDir->cbBuf = 0;
173 pDir->pabBuf = NULL;
174 pDir->uMagic = BIRD_DIR_MAGIC;
175 }
176 pDir->pvHandle = pvHandle;
177 pDir->fFlags = fFlags;
178 pDir->uDev = 0;
179 pDir->offPos = 0;
180 pDir->fHaveData = 0;
181 pDir->fFirst = 1;
182 pDir->iInfoClass = fFlags & BIRDDIR_F_EXTRA_INFO ? MyFileIdFullDirectoryInformation : MyFileNamesInformation;
183 pDir->offBuf = 0;
184 return pDir;
185 }
186 }
187 else
188 assert(pvReserved == NULL);
189 birdSetErrnoToInvalidArg();
190 return NULL;
191}
192
193
194static int birdDirReadMore(BirdDir_T *pDir)
195{
196 MY_NTSTATUS rcNt;
197 MY_IO_STATUS_BLOCK Ios;
198 int fFirst;
199
200 /*
201 * Retrieve the volume serial number + creation time and create the
202 * device number the first time around. Also allocate a buffer.
203 */
204 fFirst = pDir->fFirst;
205 if (fFirst)
206 {
207 union
208 {
209 MY_FILE_FS_VOLUME_INFORMATION VolInfo;
210 unsigned char abBuf[1024];
211 } uBuf;
212
213 Ios.Information = 0;
214 Ios.u.Status = -1;
215 rcNt = g_pfnNtQueryVolumeInformationFile((HANDLE)pDir->pvHandle, &Ios, &uBuf, sizeof(uBuf), MyFileFsVolumeInformation);
216 if (MY_NT_SUCCESS(rcNt))
217 rcNt = Ios.u.Status;
218 if (MY_NT_SUCCESS(rcNt))
219 pDir->uDev = uBuf.VolInfo.VolumeSerialNumber
220 | (uBuf.VolInfo.VolumeCreationTime.QuadPart << 32);
221 else
222 pDir->uDev = 0;
223
224 if (!pDir->pabBuf)
225 {
226 /*
227 * Allocate a buffer.
228 *
229 * Better not exceed 64KB or CIFS may throw a fit. Also, on win10/64
230 * here there is a noticable speedup when going one byte below 64KB.
231 */
232 pDir->cbBuf = 0xffe0;
233 pDir->pabBuf = birdMemAlloc(pDir->cbBuf);
234 if (!pDir->pabBuf)
235 return birdSetErrnoToNoMem();
236 }
237
238 pDir->fFirst = 0;
239 }
240
241 /*
242 * Read another buffer full.
243 */
244 Ios.Information = 0;
245 Ios.u.Status = -1;
246
247 rcNt = g_pfnNtQueryDirectoryFile((HANDLE)pDir->pvHandle,
248 NULL, /* hEvent */
249 NULL, /* pfnApcComplete */
250 NULL, /* pvApcCompleteCtx */
251 &Ios,
252 pDir->pabBuf,
253 pDir->cbBuf,
254 (MY_FILE_INFORMATION_CLASS)pDir->iInfoClass,
255 FALSE, /* fReturnSingleEntry */
256 NULL, /* Filter / restart pos. */
257 pDir->fFlags & BIRDDIR_F_RESTART_SCAN ? TRUE : FALSE); /* fRestartScan */
258 if (!MY_NT_SUCCESS(rcNt))
259 {
260 int rc;
261 if (rcNt == MY_STATUS_NO_MORE_FILES)
262 rc = 0;
263 else
264 rc = birdSetErrnoFromNt(rcNt);
265 pDir->fHaveData = 0;
266 pDir->offBuf = pDir->cbBuf;
267 return rc;
268 }
269
270 pDir->offBuf = 0;
271 pDir->fHaveData = 1;
272 pDir->fFlags &= ~BIRDDIR_F_RESTART_SCAN;
273
274 return 0;
275}
276
277
278static int birdDirCopyNameToEntry(WCHAR const *pwcName, ULONG cbName, BirdDirEntry_T *pEntry)
279{
280 int cchOut = WideCharToMultiByte(CP_ACP, 0,
281 pwcName, cbName / sizeof(WCHAR),
282 pEntry->d_name, sizeof(pEntry->d_name) - 1,
283 NULL, NULL);
284 if (cchOut > 0)
285 {
286 pEntry->d_name[cchOut] = '\0';
287 pEntry->d_namlen = (unsigned __int16)cchOut;
288 pEntry->d_reclen = (unsigned __int16)((size_t)&pEntry->d_name[cchOut + 1] - (size_t)pEntry);
289 return 0;
290 }
291 return -1;
292}
293
294
295
296/**
297 * Implements readdir_r().
298 *
299 * @remarks birdDirReadReentrantW is a copy of this. Keep them in sync!
300 */
301int birdDirReadReentrant(BirdDir_T *pDir, BirdDirEntry_T *pEntry, BirdDirEntry_T **ppResult)
302{
303 int fSkipEntry;
304
305 *ppResult = NULL;
306
307 if (!pDir || pDir->uMagic != BIRD_DIR_MAGIC)
308 return birdSetErrnoToBadFileNo();
309
310 do
311 {
312 ULONG offNext;
313 ULONG cbMinCur;
314
315 /*
316 * Read more?
317 */
318 if (!pDir->fHaveData)
319 {
320 if (birdDirReadMore(pDir) != 0)
321 return -1;
322 if (!pDir->fHaveData)
323 return 0;
324 }
325
326 /*
327 * Convert the NT data to the unixy output structure.
328 */
329 fSkipEntry = 0;
330 switch (pDir->iInfoClass)
331 {
332 case MyFileNamesInformation:
333 {
334 MY_FILE_NAMES_INFORMATION *pInfo = (MY_FILE_NAMES_INFORMATION *)&pDir->pabBuf[pDir->offBuf];
335 if ( pDir->offBuf >= pDir->cbBuf - MIN_SIZEOF_MY_FILE_NAMES_INFORMATION
336 || pInfo->FileNameLength >= pDir->cbBuf
337 || pDir->offBuf + pInfo->FileNameLength + MIN_SIZEOF_MY_FILE_NAMES_INFORMATION > pDir->cbBuf)
338 {
339 fSkipEntry = 1;
340 pDir->fHaveData = 0;
341 continue;
342 }
343
344 memset(&pEntry->d_stat, 0, sizeof(pEntry->d_stat));
345 pEntry->d_stat.st_mode = S_IFMT;
346 pEntry->d_type = DT_UNKNOWN;
347 pEntry->d_reclen = 0;
348 pEntry->d_namlen = 0;
349 if (birdDirCopyNameToEntry(pInfo->FileName, pInfo->FileNameLength, pEntry) != 0)
350 fSkipEntry = 1;
351
352 cbMinCur = MIN_SIZEOF_MY_FILE_NAMES_INFORMATION + pInfo->FileNameLength;
353 offNext = pInfo->NextEntryOffset;
354 break;
355 }
356
357 case MyFileIdFullDirectoryInformation:
358 {
359 MY_FILE_ID_FULL_DIR_INFORMATION *pInfo = (MY_FILE_ID_FULL_DIR_INFORMATION *)&pDir->pabBuf[pDir->offBuf];
360 if ( pDir->offBuf >= pDir->cbBuf - MIN_SIZEOF_MY_FILE_ID_FULL_DIR_INFORMATION
361 || pInfo->FileNameLength >= pDir->cbBuf
362 || pDir->offBuf + pInfo->FileNameLength + MIN_SIZEOF_MY_FILE_ID_FULL_DIR_INFORMATION > pDir->cbBuf)
363 {
364 fSkipEntry = 1;
365 pDir->fHaveData = 0;
366 continue;
367 }
368
369 pEntry->d_type = DT_UNKNOWN;
370 pEntry->d_reclen = 0;
371 pEntry->d_namlen = 0;
372 if (birdDirCopyNameToEntry(pInfo->FileName, pInfo->FileNameLength, pEntry) != 0)
373 fSkipEntry = 1;
374 birdStatFillFromFileIdFullDirInfo(&pEntry->d_stat, pInfo, pEntry->d_name);
375 pEntry->d_stat.st_dev = pDir->uDev;
376 switch (pEntry->d_stat.st_mode & S_IFMT)
377 {
378 case S_IFREG: pEntry->d_type = DT_REG; break;
379 case S_IFDIR: pEntry->d_type = DT_DIR; break;
380 case S_IFLNK: pEntry->d_type = DT_LNK; break;
381 case S_IFIFO: pEntry->d_type = DT_FIFO; break;
382 case S_IFCHR: pEntry->d_type = DT_CHR; break;
383 default:
384#ifndef NDEBUG
385 __debugbreak();
386#endif
387 pEntry->d_type = DT_UNKNOWN;
388 break;
389 }
390
391 cbMinCur = MIN_SIZEOF_MY_FILE_ID_FULL_DIR_INFORMATION + pInfo->FileNameLength;
392 offNext = pInfo->NextEntryOffset;
393 break;
394 }
395
396 default:
397 return birdSetErrnoToBadFileNo();
398 }
399
400 /*
401 * Advance.
402 */
403 if ( offNext >= cbMinCur
404 && offNext < pDir->cbBuf)
405 pDir->offBuf += offNext;
406 else
407 {
408 pDir->fHaveData = 0;
409 pDir->offBuf = pDir->cbBuf;
410 }
411 pDir->offPos++;
412 } while (fSkipEntry);
413
414
415 /*
416 * Successful return.
417 */
418 *ppResult = pEntry;
419 return 0;
420}
421
422
423/**
424 * Implements readdir().
425 */
426BirdDirEntry_T *birdDirRead(BirdDir_T *pDir)
427{
428 BirdDirEntry_T *pRet = NULL;
429 birdDirReadReentrant(pDir, &pDir->u.DirEntry, &pRet);
430 return pRet;
431}
432
433
434static int birdDirCopyNameToEntryW(WCHAR const *pwcName, ULONG cbName, BirdDirEntryW_T *pEntry)
435{
436 ULONG cwcName = cbName / sizeof(wchar_t);
437 if (cwcName < sizeof(pEntry->d_name))
438 {
439 memcpy(pEntry->d_name, pwcName, cbName);
440 pEntry->d_name[cwcName] = '\0';
441 pEntry->d_namlen = (unsigned __int16)cwcName;
442 pEntry->d_reclen = (unsigned __int16)((size_t)&pEntry->d_name[cwcName + 1] - (size_t)pEntry);
443 return 0;
444 }
445 return -1;
446}
447
448
449/**
450 * Implements readdir_r(), UTF-16 version.
451 *
452 * @remarks This is a copy of birdDirReadReentrant where only the name handling
453 * and entry type differs. Remember to keep them in sync!
454 */
455int birdDirReadReentrantW(BirdDir_T *pDir, BirdDirEntryW_T *pEntry, BirdDirEntryW_T **ppResult)
456{
457 int fSkipEntry;
458
459 *ppResult = NULL;
460
461 if (!pDir || pDir->uMagic != BIRD_DIR_MAGIC)
462 return birdSetErrnoToBadFileNo();
463
464 do
465 {
466 ULONG offNext;
467 ULONG cbMinCur;
468
469 /*
470 * Read more?
471 */
472 if (!pDir->fHaveData)
473 {
474 if (birdDirReadMore(pDir) != 0)
475 return -1;
476 if (!pDir->fHaveData)
477 return 0;
478 }
479
480 /*
481 * Convert the NT data to the unixy output structure.
482 */
483 fSkipEntry = 0;
484 switch (pDir->iInfoClass)
485 {
486 case MyFileNamesInformation:
487 {
488 MY_FILE_NAMES_INFORMATION *pInfo = (MY_FILE_NAMES_INFORMATION *)&pDir->pabBuf[pDir->offBuf];
489 if ( pDir->offBuf >= pDir->cbBuf - MIN_SIZEOF_MY_FILE_NAMES_INFORMATION
490 || pInfo->FileNameLength >= pDir->cbBuf
491 || pDir->offBuf + pInfo->FileNameLength + MIN_SIZEOF_MY_FILE_NAMES_INFORMATION > pDir->cbBuf)
492 {
493 fSkipEntry = 1;
494 pDir->fHaveData = 0;
495 continue;
496 }
497
498 memset(&pEntry->d_stat, 0, sizeof(pEntry->d_stat));
499 pEntry->d_stat.st_mode = S_IFMT;
500 pEntry->d_type = DT_UNKNOWN;
501 pEntry->d_reclen = 0;
502 pEntry->d_namlen = 0;
503 if (birdDirCopyNameToEntryW(pInfo->FileName, pInfo->FileNameLength, pEntry) != 0)
504 fSkipEntry = 1;
505
506 cbMinCur = MIN_SIZEOF_MY_FILE_NAMES_INFORMATION + pInfo->FileNameLength;
507 offNext = pInfo->NextEntryOffset;
508 break;
509 }
510
511 case MyFileIdFullDirectoryInformation:
512 {
513 MY_FILE_ID_FULL_DIR_INFORMATION *pInfo = (MY_FILE_ID_FULL_DIR_INFORMATION *)&pDir->pabBuf[pDir->offBuf];
514 if ( pDir->offBuf >= pDir->cbBuf - MIN_SIZEOF_MY_FILE_ID_FULL_DIR_INFORMATION
515 || pInfo->FileNameLength >= pDir->cbBuf
516 || pDir->offBuf + pInfo->FileNameLength + MIN_SIZEOF_MY_FILE_ID_FULL_DIR_INFORMATION > pDir->cbBuf)
517 {
518 fSkipEntry = 1;
519 pDir->fHaveData = 0;
520 continue;
521 }
522
523 pEntry->d_type = DT_UNKNOWN;
524 pEntry->d_reclen = 0;
525 pEntry->d_namlen = 0;
526 if (birdDirCopyNameToEntryW(pInfo->FileName, pInfo->FileNameLength, pEntry) != 0)
527 fSkipEntry = 1;
528 birdStatFillFromFileIdFullDirInfo(&pEntry->d_stat, pInfo, NULL);
529 pEntry->d_stat.st_dev = pDir->uDev;
530 switch (pEntry->d_stat.st_mode & S_IFMT)
531 {
532 case S_IFREG: pEntry->d_type = DT_REG; break;
533 case S_IFDIR: pEntry->d_type = DT_DIR; break;
534 case S_IFLNK: pEntry->d_type = DT_LNK; break;
535 case S_IFIFO: pEntry->d_type = DT_FIFO; break;
536 case S_IFCHR: pEntry->d_type = DT_CHR; break;
537 default:
538#ifndef NDEBUG
539 __debugbreak();
540#endif
541 pEntry->d_type = DT_UNKNOWN;
542 break;
543 }
544
545 cbMinCur = MIN_SIZEOF_MY_FILE_ID_FULL_DIR_INFORMATION + pInfo->FileNameLength;
546 offNext = pInfo->NextEntryOffset;
547 break;
548 }
549
550 default:
551 return birdSetErrnoToBadFileNo();
552 }
553
554 /*
555 * Advance.
556 */
557 if ( offNext >= cbMinCur
558 && offNext < pDir->cbBuf)
559 pDir->offBuf += offNext;
560 else
561 {
562 pDir->fHaveData = 0;
563 pDir->offBuf = pDir->cbBuf;
564 }
565 pDir->offPos++;
566 } while (fSkipEntry);
567
568
569 /*
570 * Successful return.
571 */
572 *ppResult = pEntry;
573 return 0;
574}
575
576/**
577 * Implements readdir().
578 */
579BirdDirEntryW_T *birdDirReadW(BirdDir_T *pDir)
580{
581 BirdDirEntryW_T *pRet = NULL;
582 birdDirReadReentrantW(pDir, &pDir->u.DirEntryW, &pRet);
583 return pRet;
584}
585
586
587/**
588 * Implements telldir().
589 */
590long birdDirTell(BirdDir_T *pDir)
591{
592 if (!pDir || pDir->uMagic != BIRD_DIR_MAGIC)
593 return birdSetErrnoToBadFileNo();
594 return pDir->offPos;
595}
596
597
598void birdDirSeek(BirdDir_T *pDir, long offDir);
599
600
601/**
602 * Implements closedir().
603 */
604int birdDirClose(BirdDir_T *pDir)
605{
606 if (!pDir || pDir->uMagic != BIRD_DIR_MAGIC)
607 return birdSetErrnoToBadFileNo();
608
609 pDir->uMagic++;
610 if (pDir->fFlags & BIRDDIR_F_CLOSE_HANDLE)
611 birdCloseFile((HANDLE)pDir->pvHandle);
612 pDir->pvHandle = (void *)INVALID_HANDLE_VALUE;
613 birdMemFree(pDir->pabBuf);
614 pDir->pabBuf = NULL;
615 if (!(pDir->fFlags & BIRDDIR_F_STATIC_ALLOC))
616 birdMemFree(pDir);
617
618 return 0;
619}
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