VirtualBox

source: vbox/trunk/src/VBox/Runtime/r3/dir.cpp@ 95512

Last change on this file since 95512 was 93115, checked in by vboxsync, 3 years ago

scm --update-copyright-year

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 28.2 KB
Line 
1/* $Id: dir.cpp 93115 2022-01-01 11:31:46Z vboxsync $ */
2/** @file
3 * IPRT - Directory Manipulation, Part 1.
4 */
5
6/*
7 * Copyright (C) 2006-2022 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 <iprt/dir.h>
33#include "internal/iprt.h"
34
35#include <iprt/alloca.h>
36#include <iprt/assert.h>
37#include <iprt/file.h>
38#include <iprt/err.h>
39#include <iprt/log.h>
40#include <iprt/mem.h>
41#include <iprt/param.h>
42#include <iprt/path.h>
43#include <iprt/string.h>
44#include <iprt/uni.h>
45#define RTDIR_AGNOSTIC
46#include "internal/dir.h"
47#include "internal/path.h"
48
49
50static DECLCALLBACK(bool) rtDirFilterWinNtMatch(PRTDIRINTERNAL pDir, const char *pszName);
51static DECLCALLBACK(bool) rtDirFilterWinNtMatchNoWildcards(PRTDIRINTERNAL pDir, const char *pszName);
52DECLINLINE(bool) rtDirFilterWinNtMatchEon(PCRTUNICP puszFilter);
53static bool rtDirFilterWinNtMatchDosStar(unsigned iDepth, RTUNICP uc, const char *pszNext, PCRTUNICP puszFilter);
54static bool rtDirFilterWinNtMatchStar(unsigned iDepth, RTUNICP uc, const char *pszNext, PCRTUNICP puszFilter);
55static bool rtDirFilterWinNtMatchBase(unsigned iDepth, const char *pszName, PCRTUNICP puszFilter);
56
57
58
59RTDECL(int) RTDirCreateFullPath(const char *pszPath, RTFMODE fMode)
60{
61 return RTDirCreateFullPathEx(pszPath, fMode, 0);
62}
63
64
65RTDECL(int) RTDirCreateFullPathEx(const char *pszPath, RTFMODE fMode, uint32_t fFlags)
66{
67 /*
68 * Resolve the path.
69 */
70 char *pszAbsPath = RTPathAbsDup(pszPath);
71 if (!pszAbsPath)
72 return VERR_NO_TMP_MEMORY;
73
74 /*
75 * Iterate the path components making sure each of them exists.
76 */
77 /* skip volume name */
78 char *psz = &pszAbsPath[rtPathVolumeSpecLen(pszAbsPath)];
79
80 /* skip the root slash if any */
81 if (RTPATH_IS_SLASH(*psz))
82 psz++;
83
84 /* iterate over path components. */
85 int rc = VINF_SUCCESS;
86 do
87 {
88 /* the next component is NULL, stop iterating */
89 if (!*psz)
90 break;
91#if RTPATH_STYLE == RTPATH_STR_F_STYLE_DOS
92 char *psz2 = strchr(psz, '/');
93 psz = strchr(psz, RTPATH_SLASH);
94 if (psz2 && (!psz || (uintptr_t)psz2 < (uintptr_t)psz))
95 psz = psz;
96#else
97 psz = strchr(psz, RTPATH_SLASH);
98#endif
99 if (psz)
100 *psz = '\0';
101
102 /*
103 * ASSUME that RTDirCreate will return VERR_ALREADY_EXISTS and not VERR_ACCESS_DENIED in those cases
104 * where the directory exists but we don't have write access to the parent directory.
105 */
106 rc = RTDirCreate(pszAbsPath, fMode, fFlags);
107 if (rc == VERR_ALREADY_EXISTS)
108 rc = VINF_SUCCESS;
109
110 if (!psz)
111 break;
112 *psz++ = RTPATH_DELIMITER;
113 } while (RT_SUCCESS(rc));
114
115 RTStrFree(pszAbsPath);
116 return rc;
117}
118
119
120/**
121 * Filter a the filename in the against a filter.
122 *
123 * @returns true if the name matches the filter.
124 * @returns false if the name doesn't match filter.
125 * @param pDir The directory handle.
126 * @param pszName The path to match to the filter.
127 */
128static DECLCALLBACK(bool) rtDirFilterWinNtMatchNoWildcards(PRTDIRINTERNAL pDir, const char *pszName)
129{
130 /*
131 * Walk the string and compare.
132 */
133 PCRTUNICP pucFilter = pDir->puszFilter;
134 const char *psz = pszName;
135 RTUNICP uc;
136 do
137 {
138 int rc = RTStrGetCpEx(&psz, &uc);
139 AssertRCReturn(rc, false);
140 RTUNICP ucFilter = *pucFilter++;
141 if ( uc != ucFilter
142 && RTUniCpToUpper(uc) != ucFilter)
143 return false;
144 } while (uc);
145 return true;
146}
147
148
149/**
150 * Matches end of name.
151 */
152DECLINLINE(bool) rtDirFilterWinNtMatchEon(PCRTUNICP puszFilter)
153{
154 RTUNICP ucFilter;
155 while ( (ucFilter = *puszFilter) == '>'
156 || ucFilter == '<'
157 || ucFilter == '*'
158 || ucFilter == '"')
159 puszFilter++;
160 return !ucFilter;
161}
162
163
164/**
165 * Recursive star matching.
166 * Practically the same as normal star, except that the dos star stops
167 * when hitting the last dot.
168 *
169 * @returns true on match.
170 * @returns false on miss.
171 */
172static bool rtDirFilterWinNtMatchDosStar(unsigned iDepth, RTUNICP uc, const char *pszNext, PCRTUNICP puszFilter)
173{
174 AssertReturn(iDepth++ < 256, false);
175
176 /*
177 * If there is no dos star, we should work just like the NT star.
178 * Since that's generally faster algorithms, we jump down to there if we can.
179 */
180 const char *pszDosDot = strrchr(pszNext, '.');
181 if (!pszDosDot && uc == '.')
182 pszDosDot = pszNext - 1;
183 if (!pszDosDot)
184 return rtDirFilterWinNtMatchStar(iDepth, uc, pszNext, puszFilter);
185
186 /*
187 * Inspect the next filter char(s) until we find something to work on.
188 */
189 RTUNICP ucFilter = *puszFilter++;
190 switch (ucFilter)
191 {
192 /*
193 * The star expression is the last in the pattern.
194 * We're fine if the name ends with a dot.
195 */
196 case '\0':
197 return !pszDosDot[1];
198
199 /*
200 * Simplified by brute force.
201 */
202 case '>': /* dos question mark */
203 case '?':
204 case '*':
205 case '<': /* dos star */
206 case '"': /* dos dot */
207 {
208 puszFilter--;
209 const char *pszStart = pszNext;
210 do
211 {
212 if (rtDirFilterWinNtMatchBase(iDepth, pszNext, puszFilter))
213 return true;
214 int rc = RTStrGetCpEx(&pszNext, &uc); AssertRCReturn(rc, false);
215 } while ((intptr_t)pszDosDot - (intptr_t)pszNext >= -1);
216
217 /* backtrack and do the current char. */
218 pszNext = RTStrPrevCp(NULL, pszStart); AssertReturn(pszNext, false);
219 return rtDirFilterWinNtMatchBase(iDepth, pszNext, puszFilter);
220 }
221
222 /*
223 * Ok, we've got zero or more characters.
224 * We'll try match starting at each occurrence of this character.
225 */
226 default:
227 {
228 if ( RTUniCpToUpper(uc) == ucFilter
229 && rtDirFilterWinNtMatchBase(iDepth, pszNext, puszFilter))
230 return true;
231 do
232 {
233 int rc = RTStrGetCpEx(&pszNext, &uc); AssertRCReturn(rc, false);
234 if ( RTUniCpToUpper(uc) == ucFilter
235 && rtDirFilterWinNtMatchBase(iDepth, pszNext, puszFilter))
236 return true;
237 } while ((intptr_t)pszDosDot - (intptr_t)pszNext >= -1);
238 return false;
239 }
240 }
241 /* won't ever get here! */
242}
243
244
245/**
246 * Recursive star matching.
247 *
248 * @returns true on match.
249 * @returns false on miss.
250 */
251static bool rtDirFilterWinNtMatchStar(unsigned iDepth, RTUNICP uc, const char *pszNext, PCRTUNICP puszFilter)
252{
253 AssertReturn(iDepth++ < 256, false);
254
255 /*
256 * Inspect the next filter char(s) until we find something to work on.
257 */
258 for (;;)
259 {
260 RTUNICP ucFilter = *puszFilter++;
261 switch (ucFilter)
262 {
263 /*
264 * The star expression is the last in the pattern.
265 * Cool, that means we're done!
266 */
267 case '\0':
268 return true;
269
270 /*
271 * Just in case (doubt we ever get here), just merge it with the current one.
272 */
273 case '*':
274 break;
275
276 /*
277 * Skip a fixed number of chars.
278 * Figure out how many by walking the filter ignoring '*'s.
279 */
280 case '?':
281 {
282 unsigned cQms = 1;
283 while ((ucFilter = *puszFilter) == '*' || ucFilter == '?')
284 {
285 cQms += ucFilter == '?';
286 puszFilter++;
287 }
288 do
289 {
290 if (!uc)
291 return false;
292 int rc = RTStrGetCpEx(&pszNext, &uc); AssertRCReturn(rc, false);
293 } while (--cQms > 0);
294 /* done? */
295 if (!ucFilter)
296 return true;
297 break;
298 }
299
300 /*
301 * The simple way is to try char by char and match the remaining
302 * expression. If it's trailing we're done.
303 */
304 case '>': /* dos question mark */
305 {
306 if (rtDirFilterWinNtMatchEon(puszFilter))
307 return true;
308 const char *pszStart = pszNext;
309 do
310 {
311 if (rtDirFilterWinNtMatchBase(iDepth, pszNext, puszFilter))
312 return true;
313 int rc = RTStrGetCpEx(&pszNext, &uc); AssertRCReturn(rc, false);
314 } while (uc);
315
316 /* backtrack and do the current char. */
317 pszNext = RTStrPrevCp(NULL, pszStart); AssertReturn(pszNext, false);
318 return rtDirFilterWinNtMatchBase(iDepth, pszNext, puszFilter);
319 }
320
321 /*
322 * This bugger is interesting.
323 * Time for brute force. Iterate the name char by char.
324 */
325 case '<':
326 {
327 do
328 {
329 if (rtDirFilterWinNtMatchDosStar(iDepth, uc, pszNext, puszFilter))
330 return true;
331 int rc = RTStrGetCpEx(&pszNext, &uc); AssertRCReturn(rc, false);
332 } while (uc);
333 return false;
334 }
335
336 /*
337 * This guy matches a '.' or the end of the name.
338 * It's very simple if the rest of the filter expression also matches eon.
339 */
340 case '"':
341 if (rtDirFilterWinNtMatchEon(puszFilter))
342 return true;
343 ucFilter = '.';
344 RT_FALL_THRU();
345
346 /*
347 * Ok, we've got zero or more characters.
348 * We'll try match starting at each occurrence of this character.
349 */
350 default:
351 {
352 do
353 {
354 if ( RTUniCpToUpper(uc) == ucFilter
355 && rtDirFilterWinNtMatchBase(iDepth, pszNext, puszFilter))
356 return true;
357 int rc = RTStrGetCpEx(&pszNext, &uc); AssertRCReturn(rc, false);
358 } while (uc);
359 return false;
360 }
361 }
362 } /* for (;;) */
363
364 /* won't ever get here! */
365}
366
367
368/**
369 * Filter a the filename in the against a filter.
370 *
371 * The rules are as follows:
372 * '?' Matches exactly one char.
373 * '*' Matches zero or more chars.
374 * '<' The dos star, matches zero or more chars except the DOS dot.
375 * '>' The dos question mark, matches one char, but dots and end-of-name eats them.
376 * '"' The dos dot, matches a dot or end-of-name.
377 *
378 * @returns true if the name matches the filter.
379 * @returns false if the name doesn't match filter.
380 * @param iDepth The recursion depth.
381 * @param pszName The path to match to the filter.
382 * @param puszFilter The filter string.
383 */
384static bool rtDirFilterWinNtMatchBase(unsigned iDepth, const char *pszName, PCRTUNICP puszFilter)
385{
386 AssertReturn(iDepth++ < 256, false);
387
388 /*
389 * Walk the string and match it up char by char.
390 */
391 RTUNICP uc;
392 do
393 {
394 RTUNICP ucFilter = *puszFilter++;
395 int rc = RTStrGetCpEx(&pszName, &uc); AssertRCReturn(rc, false);
396 switch (ucFilter)
397 {
398 /* Exactly one char. */
399 case '?':
400 if (!uc)
401 return false;
402 break;
403
404 /* One char, but the dos dot and end-of-name eats '>' and '<'. */
405 case '>': /* dos ? */
406 if (!uc)
407 return rtDirFilterWinNtMatchEon(puszFilter);
408 if (uc == '.')
409 {
410 while ((ucFilter = *puszFilter) == '>' || ucFilter == '<')
411 puszFilter++;
412 if (ucFilter == '"' || ucFilter == '.') /* not 100% sure about the last dot */
413 ++puszFilter;
414 else /* the does question mark doesn't match '.'s, so backtrack. */
415 pszName = RTStrPrevCp(NULL, pszName);
416 }
417 break;
418
419 /* Match a dot or the end-of-name. */
420 case '"': /* dos '.' */
421 if (uc != '.')
422 {
423 if (uc)
424 return false;
425 return rtDirFilterWinNtMatchEon(puszFilter);
426 }
427 break;
428
429 /* zero or more */
430 case '*':
431 return rtDirFilterWinNtMatchStar(iDepth, uc, pszName, puszFilter);
432 case '<': /* dos '*' */
433 return rtDirFilterWinNtMatchDosStar(iDepth, uc, pszName, puszFilter);
434
435
436 /* uppercased match */
437 default:
438 {
439 if (RTUniCpToUpper(uc) != ucFilter)
440 return false;
441 break;
442 }
443 }
444 } while (uc);
445
446 return true;
447}
448
449
450/**
451 * Filter a the filename in the against a filter.
452 *
453 * @returns true if the name matches the filter.
454 * @returns false if the name doesn't match filter.
455 * @param pDir The directory handle.
456 * @param pszName The path to match to the filter.
457 */
458static DECLCALLBACK(bool) rtDirFilterWinNtMatch(PRTDIRINTERNAL pDir, const char *pszName)
459{
460 return rtDirFilterWinNtMatchBase(0, pszName, pDir->puszFilter);
461}
462
463
464/**
465 * Initializes a WinNt like wildcard filter.
466 *
467 * @returns Pointer to the filter function.
468 * @returns NULL if the filter doesn't filter out anything.
469 * @param pDir The directory handle (not yet opened).
470 */
471static PFNRTDIRFILTER rtDirFilterWinNtInit(PRTDIRINTERNAL pDir)
472{
473 /*
474 * Check for the usual * and <"< (*.* in DOS language) patterns.
475 */
476 if ( (pDir->cchFilter == 1 && pDir->pszFilter[0] == '*')
477 || (pDir->cchFilter == 3 && !memcmp(pDir->pszFilter, "<\".>", 3))
478 )
479 return NULL;
480
481 /*
482 * Uppercase the expression, also do a little optimizations when possible.
483 */
484 bool fHaveWildcards = false;
485 unsigned iRead = 0;
486 unsigned iWrite = 0;
487 while (iRead < pDir->cucFilter)
488 {
489 RTUNICP uc = pDir->puszFilter[iRead++];
490 if (uc == '*')
491 {
492 fHaveWildcards = true;
493 /* remove extra stars. */
494 RTUNICP uc2;
495 while ((uc2 = pDir->puszFilter[iRead + 1]) == '*')
496 iRead++;
497 }
498 else if (uc == '?' || uc == '>' || uc == '<' || uc == '"')
499 fHaveWildcards = true;
500 else
501 uc = RTUniCpToUpper(uc);
502 pDir->puszFilter[iWrite++] = uc;
503 }
504 pDir->puszFilter[iWrite] = 0;
505 pDir->cucFilter = iWrite;
506
507 return fHaveWildcards
508 ? rtDirFilterWinNtMatch
509 : rtDirFilterWinNtMatchNoWildcards;
510}
511
512
513/**
514 * Common worker for opening a directory.
515 *
516 * @returns IPRT status code.
517 * @param phDir Where to store the directory handle.
518 * @param pszPath The specified path.
519 * @param pszFilter Pointer to where the filter start in the path.
520 * NULL if no filter.
521 * @param enmFilter The type of filter to apply.
522 * @param fFlags RTDIR_F_XXX.
523 * @param hRelativeDir The directory @a pvNativeRelative is relative
524 * to, ~(uintptr_t)0 if absolute.
525 * @param pvNativeRelative The native relative path. NULL if absolute or
526 * we're to use (consume) hRelativeDir.
527 */
528static int rtDirOpenCommon(RTDIR *phDir, const char *pszPath, const char *pszFilter, RTDIRFILTER enmFilter,
529 uint32_t fFlags, uintptr_t hRelativeDir, void *pvNativeRelative)
530{
531 /*
532 * Expand the path.
533 *
534 * The purpose of this exercise to have the abs path around
535 * for querying extra information about the objects we list.
536 * As a sideeffect we also validate the path here.
537 *
538 * Note! The RTDIR_F_NO_ABS_PATH mess is there purely for allowing us to
539 * work around PATH_MAX using CWD on linux and other unixy systems.
540 */
541 char *pszAbsPath;
542 size_t cbFilter; /* includes '\0' (thus cb and not cch). */
543 size_t cucFilter0; /* includes U+0. */
544 bool fDirSlash = false;
545 if (!pszFilter)
546 {
547 if (*pszPath != '\0')
548 {
549 const char *pszLast = strchr(pszPath, '\0') - 1;
550 if (RTPATH_IS_SLASH(*pszLast))
551 fDirSlash = true;
552 }
553
554 cbFilter = cucFilter0 = 0;
555 if (!(fFlags & RTDIR_F_NO_ABS_PATH))
556 pszAbsPath = RTPathAbsExDup(NULL, pszPath, RTPATHABS_F_ENSURE_TRAILING_SLASH);
557 else
558 {
559 size_t cchTmp = strlen(pszPath);
560 pszAbsPath = RTStrAlloc(cchTmp + 2);
561 if (pszAbsPath)
562 {
563 memcpy(pszAbsPath, pszPath, cchTmp);
564 pszAbsPath[cchTmp] = RTPATH_SLASH;
565 pszAbsPath[cchTmp + 1 - fDirSlash] = '\0';
566 }
567 }
568 }
569 else
570 {
571 cbFilter = strlen(pszFilter) + 1;
572 cucFilter0 = RTStrUniLen(pszFilter) + 1;
573
574 if (pszFilter != pszPath)
575 {
576 /* yea, I'm lazy. sue me. */
577 char *pszTmp = RTStrDup(pszPath);
578 if (!pszTmp)
579 return VERR_NO_MEMORY;
580 pszTmp[pszFilter - pszPath] = '\0';
581 if (!(fFlags & RTDIR_F_NO_ABS_PATH))
582 {
583 pszAbsPath = RTPathAbsExDup(NULL, pszTmp, RTPATHABS_F_ENSURE_TRAILING_SLASH);
584 RTStrFree(pszTmp);
585 }
586 else
587 {
588 pszAbsPath = pszTmp;
589 RTPathEnsureTrailingSeparator(pszAbsPath, strlen(pszPath) + 1);
590 }
591 }
592 else if (!(fFlags & RTDIR_F_NO_ABS_PATH))
593 pszAbsPath = RTPathAbsExDup(NULL, ".", RTPATHABS_F_ENSURE_TRAILING_SLASH);
594 else
595 pszAbsPath = RTStrDup("." RTPATH_SLASH_STR);
596 fDirSlash = true;
597 }
598 if (!pszAbsPath)
599 return VERR_NO_MEMORY;
600 Assert(strchr(pszAbsPath, '\0')[-1] == RTPATH_SLASH);
601
602 /*
603 * Allocate and initialize the directory handle.
604 *
605 * The posix definition of Data.d_name allows it to be < NAME_MAX + 1,
606 * thus the horrible ugliness here. Solaris uses d_name[1] for instance.
607 */
608 size_t const cchAbsPath = strlen(pszAbsPath);
609 size_t const cbDir = rtDirNativeGetStructSize(pszAbsPath);
610 size_t const cbAllocated = cbDir
611 + cucFilter0 * sizeof(RTUNICP)
612 + cbFilter
613 + cchAbsPath + 1 + 4;
614 PRTDIRINTERNAL pDir = (PRTDIRINTERNAL)RTMemAllocZ(cbAllocated);
615 if (!pDir)
616 {
617 RTStrFree(pszAbsPath);
618 return VERR_NO_MEMORY;
619 }
620 uint8_t *pb = (uint8_t *)pDir + cbDir;
621
622 /* initialize it */
623 pDir->u32Magic = RTDIR_MAGIC;
624 pDir->cbSelf = cbDir;
625 if (cbFilter)
626 {
627 pDir->puszFilter = (PRTUNICP)pb;
628 int rc2 = RTStrToUniEx(pszFilter, RTSTR_MAX, &pDir->puszFilter, cucFilter0, &pDir->cucFilter);
629 AssertRC(rc2);
630 pb += cucFilter0 * sizeof(RTUNICP);
631 pDir->pszFilter = (char *)memcpy(pb, pszFilter, cbFilter);
632 pDir->cchFilter = cbFilter - 1;
633 pb += cbFilter;
634 }
635 else
636 {
637 pDir->puszFilter = NULL;
638 pDir->cucFilter = 0;
639 pDir->pszFilter = NULL;
640 pDir->cchFilter = 0;
641 }
642 pDir->enmFilter = enmFilter;
643 switch (enmFilter)
644 {
645 default:
646 case RTDIRFILTER_NONE:
647 pDir->pfnFilter = NULL;
648 break;
649 case RTDIRFILTER_WINNT:
650 pDir->pfnFilter = rtDirFilterWinNtInit(pDir);
651 break;
652 case RTDIRFILTER_UNIX:
653 pDir->pfnFilter = NULL;
654 break;
655 case RTDIRFILTER_UNIX_UPCASED:
656 pDir->pfnFilter = NULL;
657 break;
658 }
659 pDir->cchPath = cchAbsPath;
660 pDir->pszPath = (char *)memcpy(pb, pszAbsPath, cchAbsPath);
661 pb[cchAbsPath] = '\0';
662 Assert(pb - (uint8_t *)pDir + cchAbsPath + 1 <= cbAllocated);
663 pDir->pszName = NULL;
664 pDir->cchName = 0;
665 pDir->fFlags = fFlags;
666 pDir->fDirSlash = fDirSlash;
667 pDir->fDataUnread = false;
668
669 /*
670 * Hand it over to the native part.
671 */
672 int rc = rtDirNativeOpen(pDir, hRelativeDir, pvNativeRelative);
673 if (RT_SUCCESS(rc))
674 *phDir = pDir;
675 else
676 RTMemFree(pDir);
677 RTStrFree(pszAbsPath);
678 return rc;
679}
680
681
682RTDECL(int) RTDirOpen(RTDIR *phDir, const char *pszPath)
683{
684 /*
685 * Validate input.
686 */
687 AssertPtrReturn(phDir, VERR_INVALID_POINTER);
688 AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
689
690 /*
691 * Take common cause with RTDirOpenFiltered().
692 */
693 int rc = rtDirOpenCommon(phDir, pszPath, NULL, RTDIRFILTER_NONE, 0 /*fFlags*/, ~(uintptr_t)0, NULL);
694 LogFlow(("RTDirOpen(%p:{%p}, %p:{%s}): return %Rrc\n", phDir, *phDir, pszPath, pszPath, rc));
695 return rc;
696}
697
698
699DECLHIDDEN(int) rtDirOpenRelativeOrHandle(RTDIR *phDir, const char *pszPath, RTDIRFILTER enmFilter, uint32_t fFlags,
700 uintptr_t hRelativeDir, void *pvNativeRelative)
701{
702 /*
703 * Validate input.
704 */
705 AssertPtrReturn(phDir, VERR_INVALID_POINTER);
706 AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
707 AssertReturn(!(fFlags & ~RTDIR_F_VALID_MASK), VERR_INVALID_FLAGS);
708 switch (enmFilter)
709 {
710 case RTDIRFILTER_UNIX:
711 case RTDIRFILTER_UNIX_UPCASED:
712 AssertMsgFailed(("%d is not implemented!\n", enmFilter));
713 return VERR_NOT_IMPLEMENTED;
714 case RTDIRFILTER_NONE:
715 case RTDIRFILTER_WINNT:
716 break;
717 default:
718 AssertMsgFailedReturn(("%d\n", enmFilter), VERR_INVALID_PARAMETER);
719 }
720
721 /*
722 * Find the last component, i.e. where the filter criteria starts and the dir name ends.
723 */
724 const char *pszFilter;
725 if (enmFilter == RTDIRFILTER_NONE)
726 pszFilter = NULL;
727 else
728 {
729 pszFilter = RTPathFilename(pszPath);
730 if (!pszFilter) /* trailing slash => directory to read => no filter. */
731 enmFilter = RTDIRFILTER_NONE;
732 }
733
734 /*
735 * Call worker common with RTDirOpen which will verify the path, allocate
736 * and initialize the handle, and finally call the backend.
737 */
738 int rc = rtDirOpenCommon(phDir, pszPath, pszFilter, enmFilter, fFlags, hRelativeDir, pvNativeRelative);
739
740 LogFlow(("RTDirOpenFiltered(%p:{%p}, %p:{%s}, %d, %#x, %p, %p): return %Rrc\n",
741 phDir,*phDir, pszPath, pszPath, enmFilter, fFlags, hRelativeDir, pvNativeRelative, rc));
742 return rc;
743}
744
745
746RTDECL(int) RTDirOpenFiltered(RTDIR *phDir, const char *pszPath, RTDIRFILTER enmFilter, uint32_t fFlags)
747{
748 return rtDirOpenRelativeOrHandle(phDir, pszPath, enmFilter, fFlags, ~(uintptr_t)0, NULL);
749}
750
751
752RTDECL(bool) RTDirIsValid(RTDIR hDir)
753{
754 return RT_VALID_PTR(hDir)
755 && hDir->u32Magic == RTDIR_MAGIC;
756}
757
758
759RTDECL(int) RTDirFlushParent(const char *pszChild)
760{
761 char *pszPath;
762 char *pszPathFree = NULL;
763 size_t const cchChild = strlen(pszChild);
764 if (cchChild < RTPATH_MAX)
765 pszPath = (char *)alloca(cchChild + 1);
766 else
767 {
768 pszPathFree = pszPath = (char *)RTMemTmpAlloc(cchChild + 1);
769 if (!pszPath)
770 return VERR_NO_TMP_MEMORY;
771 }
772 memcpy(pszPath, pszChild, cchChild);
773 pszPath[cchChild] = '\0';
774 RTPathStripFilename(pszPath);
775
776 int rc = RTDirFlush(pszPath);
777
778 if (pszPathFree)
779 RTMemTmpFree(pszPathFree);
780 return rc;
781}
782
783
784RTDECL(int) RTDirQueryUnknownTypeEx(const char *pszComposedName, bool fFollowSymlinks,
785 RTDIRENTRYTYPE *penmType, PRTFSOBJINFO pObjInfo)
786{
787 int rc = RTPathQueryInfoEx(pszComposedName, pObjInfo, RTFSOBJATTRADD_NOTHING,
788 fFollowSymlinks ? RTPATH_F_FOLLOW_LINK : RTPATH_F_ON_LINK);
789 if (RT_FAILURE(rc))
790 return rc;
791
792 if (RTFS_IS_DIRECTORY(pObjInfo->Attr.fMode))
793 *penmType = RTDIRENTRYTYPE_DIRECTORY;
794 else if (RTFS_IS_FILE(pObjInfo->Attr.fMode))
795 *penmType = RTDIRENTRYTYPE_FILE;
796 else if (RTFS_IS_SYMLINK(pObjInfo->Attr.fMode))
797 *penmType = RTDIRENTRYTYPE_SYMLINK;
798 else if (RTFS_IS_FIFO(pObjInfo->Attr.fMode))
799 *penmType = RTDIRENTRYTYPE_FIFO;
800 else if (RTFS_IS_DEV_CHAR(pObjInfo->Attr.fMode))
801 *penmType = RTDIRENTRYTYPE_DEV_CHAR;
802 else if (RTFS_IS_DEV_BLOCK(pObjInfo->Attr.fMode))
803 *penmType = RTDIRENTRYTYPE_DEV_BLOCK;
804 else if (RTFS_IS_SOCKET(pObjInfo->Attr.fMode))
805 *penmType = RTDIRENTRYTYPE_SOCKET;
806 else if (RTFS_IS_WHITEOUT(pObjInfo->Attr.fMode))
807 *penmType = RTDIRENTRYTYPE_WHITEOUT;
808 else
809 *penmType = RTDIRENTRYTYPE_UNKNOWN;
810
811 return VINF_SUCCESS;
812}
813
814
815RTDECL(int) RTDirQueryUnknownType(const char *pszComposedName, bool fFollowSymlinks, RTDIRENTRYTYPE *penmType)
816{
817 if ( *penmType != RTDIRENTRYTYPE_UNKNOWN
818 && ( !fFollowSymlinks
819 || *penmType != RTDIRENTRYTYPE_SYMLINK))
820 return VINF_SUCCESS;
821
822 RTFSOBJINFO ObjInfo;
823 return RTDirQueryUnknownTypeEx(pszComposedName, fFollowSymlinks, penmType, &ObjInfo);
824}
825
826
827RTDECL(bool) RTDirEntryIsStdDotLink(PRTDIRENTRY pDirEntry)
828{
829 if (pDirEntry->szName[0] != '.')
830 return false;
831 if (pDirEntry->cbName == 1)
832 return true;
833 if (pDirEntry->cbName != 2)
834 return false;
835 return pDirEntry->szName[1] == '.';
836}
837
838
839RTDECL(bool) RTDirEntryExIsStdDotLink(PCRTDIRENTRYEX pDirEntryEx)
840{
841 if (pDirEntryEx->szName[0] != '.')
842 return false;
843 if (pDirEntryEx->cbName == 1)
844 return true;
845 if (pDirEntryEx->cbName != 2)
846 return false;
847 return pDirEntryEx->szName[1] == '.';
848}
849
850
851RTDECL(int) RTDirReadExA(RTDIR hDir, PRTDIRENTRYEX *ppDirEntry, size_t *pcbDirEntry, RTFSOBJATTRADD enmAddAttr, uint32_t fFlags)
852{
853 PRTDIRENTRYEX pDirEntry = *ppDirEntry;
854 size_t cbDirEntry = *pcbDirEntry;
855 if (pDirEntry != NULL && cbDirEntry >= sizeof(RTDIRENTRYEX))
856 { /* likely */ }
857 else
858 {
859 Assert(pDirEntry == NULL);
860 Assert(cbDirEntry == 0);
861
862 cbDirEntry = RT_ALIGN_Z(sizeof(RTDIRENTRYEX), 16);
863 *ppDirEntry = pDirEntry = (PRTDIRENTRYEX)RTMemTmpAlloc(cbDirEntry);
864 if (pDirEntry)
865 *pcbDirEntry = cbDirEntry;
866 else
867 {
868 *pcbDirEntry = 0;
869 return VERR_NO_TMP_MEMORY;
870 }
871 }
872
873 for (;;)
874 {
875 int rc = RTDirReadEx(hDir, pDirEntry, &cbDirEntry, enmAddAttr, fFlags);
876 if (rc != VERR_BUFFER_OVERFLOW)
877 return rc;
878
879 /* Grow the buffer. */
880 RTMemTmpFree(pDirEntry);
881 cbDirEntry = RT_MAX(RT_ALIGN_Z(cbDirEntry, 64), *pcbDirEntry + 64);
882 *ppDirEntry = pDirEntry = (PRTDIRENTRYEX)RTMemTmpAlloc(cbDirEntry);
883 if (pDirEntry)
884 *pcbDirEntry = cbDirEntry;
885 else
886 {
887 *pcbDirEntry = 0;
888 return VERR_NO_TMP_MEMORY;
889 }
890 }
891}
892
893
894RTDECL(void) RTDirReadExAFree(PRTDIRENTRYEX *ppDirEntry, size_t *pcbDirEntry)
895{
896 PRTDIRENTRYEX pDirEntry = *ppDirEntry;
897 if (pDirEntry != NULL && *pcbDirEntry >= sizeof(*pcbDirEntry))
898 RTMemTmpFree(pDirEntry);
899 else
900 {
901 Assert(pDirEntry == NULL);
902 Assert(*pcbDirEntry == 0);
903 }
904 *ppDirEntry = NULL;
905 *pcbDirEntry = 0;
906}
907
Note: See TracBrowser for help on using the repository browser.

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