VirtualBox

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

Last change on this file was 98103, checked in by vboxsync, 16 months ago

Copyright year updates by scm.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use