VirtualBox

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

Last change on this file since 30470 was 28918, checked in by vboxsync, 15 years ago

iprt: dropped RT_DONT_CONVERT_FILENAMES, we've never needed it so far...

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