VirtualBox

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

Last change on this file since 78403 was 78207, checked in by vboxsync, 6 years ago

IPRT/RTDirFlushParent: Missing +1 in yesterday's changes. bugref:9172

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