[1] | 1 | /* $Id: dir.cpp 98103 2023-01-17 14:15:46Z vboxsync $ */
|
---|
| 2 | /** @file
|
---|
[34015] | 3 | * IPRT - Directory Manipulation, Part 1.
|
---|
[1] | 4 | */
|
---|
| 5 |
|
---|
| 6 | /*
|
---|
[98103] | 7 | * Copyright (C) 2006-2023 Oracle and/or its affiliates.
|
---|
[1] | 8 | *
|
---|
[96407] | 9 | * This file is part of VirtualBox base platform packages, as
|
---|
| 10 | * available from https://www.virtualbox.org.
|
---|
[5999] | 11 | *
|
---|
[96407] | 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 | *
|
---|
[5999] | 25 | * The contents of this file may alternatively be used under the terms
|
---|
| 26 | * of the Common Development and Distribution License Version 1.0
|
---|
[96407] | 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
|
---|
[5999] | 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.
|
---|
[96407] | 33 | *
|
---|
| 34 | * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
|
---|
[1] | 35 | */
|
---|
| 36 |
|
---|
| 37 |
|
---|
[57358] | 38 | /*********************************************************************************************************************************
|
---|
| 39 | * Header Files *
|
---|
| 40 | *********************************************************************************************************************************/
|
---|
[1] | 41 | #define LOG_GROUP RTLOGGROUP_DIR
|
---|
| 42 | #include <iprt/dir.h>
|
---|
[28688] | 43 | #include "internal/iprt.h"
|
---|
| 44 |
|
---|
[78178] | 45 | #include <iprt/alloca.h>
|
---|
[28688] | 46 | #include <iprt/assert.h>
|
---|
[19211] | 47 | #include <iprt/file.h>
|
---|
[28688] | 48 | #include <iprt/err.h>
|
---|
[1] | 49 | #include <iprt/log.h>
|
---|
[28688] | 50 | #include <iprt/mem.h>
|
---|
[1] | 51 | #include <iprt/param.h>
|
---|
[28688] | 52 | #include <iprt/path.h>
|
---|
[1] | 53 | #include <iprt/string.h>
|
---|
| 54 | #include <iprt/uni.h>
|
---|
[47535] | 55 | #define RTDIR_AGNOSTIC
|
---|
[1] | 56 | #include "internal/dir.h"
|
---|
[21675] | 57 | #include "internal/path.h"
|
---|
[1] | 58 |
|
---|
| 59 |
|
---|
[69753] | 60 | static DECLCALLBACK(bool) rtDirFilterWinNtMatch(PRTDIRINTERNAL pDir, const char *pszName);
|
---|
| 61 | static DECLCALLBACK(bool) rtDirFilterWinNtMatchNoWildcards(PRTDIRINTERNAL pDir, const char *pszName);
|
---|
[1] | 62 | DECLINLINE(bool) rtDirFilterWinNtMatchEon(PCRTUNICP puszFilter);
|
---|
| 63 | static bool rtDirFilterWinNtMatchDosStar(unsigned iDepth, RTUNICP uc, const char *pszNext, PCRTUNICP puszFilter);
|
---|
| 64 | static bool rtDirFilterWinNtMatchStar(unsigned iDepth, RTUNICP uc, const char *pszNext, PCRTUNICP puszFilter);
|
---|
| 65 | static bool rtDirFilterWinNtMatchBase(unsigned iDepth, const char *pszName, PCRTUNICP puszFilter);
|
---|
| 66 |
|
---|
| 67 |
|
---|
| 68 |
|
---|
| 69 | RTDECL(int) RTDirCreateFullPath(const char *pszPath, RTFMODE fMode)
|
---|
| 70 | {
|
---|
[79421] | 71 | return RTDirCreateFullPathEx(pszPath, fMode, 0);
|
---|
| 72 | }
|
---|
| 73 |
|
---|
| 74 |
|
---|
| 75 | RTDECL(int) RTDirCreateFullPathEx(const char *pszPath, RTFMODE fMode, uint32_t fFlags)
|
---|
| 76 | {
|
---|
[1] | 77 | /*
|
---|
| 78 | * Resolve the path.
|
---|
| 79 | */
|
---|
[79421] | 80 | char *pszAbsPath = RTPathAbsDup(pszPath);
|
---|
| 81 | if (!pszAbsPath)
|
---|
| 82 | return VERR_NO_TMP_MEMORY;
|
---|
[1] | 83 |
|
---|
| 84 | /*
|
---|
[3364] | 85 | * Iterate the path components making sure each of them exists.
|
---|
[1] | 86 | */
|
---|
| 87 | /* skip volume name */
|
---|
[79421] | 88 | char *psz = &pszAbsPath[rtPathVolumeSpecLen(pszAbsPath)];
|
---|
[1] | 89 |
|
---|
| 90 | /* skip the root slash if any */
|
---|
[79421] | 91 | if (RTPATH_IS_SLASH(*psz))
|
---|
[1] | 92 | psz++;
|
---|
| 93 |
|
---|
| 94 | /* iterate over path components. */
|
---|
[79421] | 95 | int rc = VINF_SUCCESS;
|
---|
[1] | 96 | do
|
---|
| 97 | {
|
---|
| 98 | /* the next component is NULL, stop iterating */
|
---|
| 99 | if (!*psz)
|
---|
| 100 | break;
|
---|
[79421] | 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;
|
---|
[1] | 106 | #else
|
---|
[79421] | 107 | psz = strchr(psz, RTPATH_SLASH);
|
---|
[1] | 108 | #endif
|
---|
| 109 | if (psz)
|
---|
| 110 | *psz = '\0';
|
---|
[79421] | 111 |
|
---|
[1] | 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 | */
|
---|
[79421] | 116 | rc = RTDirCreate(pszAbsPath, fMode, fFlags);
|
---|
[1] | 117 | if (rc == VERR_ALREADY_EXISTS)
|
---|
| 118 | rc = VINF_SUCCESS;
|
---|
[79421] | 119 |
|
---|
[1] | 120 | if (!psz)
|
---|
| 121 | break;
|
---|
| 122 | *psz++ = RTPATH_DELIMITER;
|
---|
| 123 | } while (RT_SUCCESS(rc));
|
---|
| 124 |
|
---|
[79421] | 125 | RTStrFree(pszAbsPath);
|
---|
[1] | 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 | */
|
---|
[69753] | 138 | static DECLCALLBACK(bool) rtDirFilterWinNtMatchNoWildcards(PRTDIRINTERNAL pDir, const char *pszName)
|
---|
[1] | 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 | */
|
---|
| 162 | DECLINLINE(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 | */
|
---|
| 182 | static 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.
|
---|
[33540] | 234 | * We'll try match starting at each occurrence of this character.
|
---|
[1] | 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 | */
|
---|
| 261 | static 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 = '.';
|
---|
[69046] | 354 | RT_FALL_THRU();
|
---|
[1] | 355 |
|
---|
| 356 | /*
|
---|
| 357 | * Ok, we've got zero or more characters.
|
---|
[33540] | 358 | * We'll try match starting at each occurrence of this character.
|
---|
[1] | 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 | */
|
---|
| 394 | static 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 | */
|
---|
[69753] | 468 | static DECLCALLBACK(bool) rtDirFilterWinNtMatch(PRTDIRINTERNAL pDir, const char *pszName)
|
---|
[1] | 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 | */
|
---|
[69753] | 481 | static PFNRTDIRFILTER rtDirFilterWinNtInit(PRTDIRINTERNAL pDir)
|
---|
[1] | 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.
|
---|
[69753] | 527 | * @param phDir Where to store the directory handle.
|
---|
[69691] | 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.
|
---|
[69716] | 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.
|
---|
[1] | 537 | */
|
---|
[69753] | 538 | static int rtDirOpenCommon(RTDIR *phDir, const char *pszPath, const char *pszFilter, RTDIRFILTER enmFilter,
|
---|
| 539 | uint32_t fFlags, uintptr_t hRelativeDir, void *pvNativeRelative)
|
---|
[1] | 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.
|
---|
[78186] | 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.
|
---|
[1] | 550 | */
|
---|
[78050] | 551 | char *pszAbsPath;
|
---|
[9355] | 552 | size_t cbFilter; /* includes '\0' (thus cb and not cch). */
|
---|
| 553 | size_t cucFilter0; /* includes U+0. */
|
---|
[70890] | 554 | bool fDirSlash = false;
|
---|
[1] | 555 | if (!pszFilter)
|
---|
| 556 | {
|
---|
[70890] | 557 | if (*pszPath != '\0')
|
---|
| 558 | {
|
---|
| 559 | const char *pszLast = strchr(pszPath, '\0') - 1;
|
---|
| 560 | if (RTPATH_IS_SLASH(*pszLast))
|
---|
| 561 | fDirSlash = true;
|
---|
| 562 | }
|
---|
| 563 |
|
---|
[9355] | 564 | cbFilter = cucFilter0 = 0;
|
---|
[78186] | 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 | }
|
---|
[1] | 578 | }
|
---|
| 579 | else
|
---|
| 580 | {
|
---|
[9355] | 581 | cbFilter = strlen(pszFilter) + 1;
|
---|
| 582 | cucFilter0 = RTStrUniLen(pszFilter) + 1;
|
---|
[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';
|
---|
[78186] | 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 | }
|
---|
[1] | 601 | }
|
---|
[78186] | 602 | else if (!(fFlags & RTDIR_F_NO_ABS_PATH))
|
---|
| 603 | pszAbsPath = RTPathAbsExDup(NULL, ".", RTPATHABS_F_ENSURE_TRAILING_SLASH);
|
---|
[1] | 604 | else
|
---|
[78186] | 605 | pszAbsPath = RTStrDup("." RTPATH_SLASH_STR);
|
---|
[70890] | 606 | fDirSlash = true;
|
---|
[1] | 607 | }
|
---|
[78050] | 608 | if (!pszAbsPath)
|
---|
| 609 | return VERR_NO_MEMORY;
|
---|
[78153] | 610 | Assert(strchr(pszAbsPath, '\0')[-1] == RTPATH_SLASH);
|
---|
[1] | 611 |
|
---|
| 612 | /*
|
---|
| 613 | * Allocate and initialize the directory handle.
|
---|
[9355] | 614 | *
|
---|
| 615 | * The posix definition of Data.d_name allows it to be < NAME_MAX + 1,
|
---|
[33540] | 616 | * thus the horrible ugliness here. Solaris uses d_name[1] for instance.
|
---|
[1] | 617 | */
|
---|
[78050] | 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
|
---|
[78153] | 623 | + cchAbsPath + 1 + 4;
|
---|
[69753] | 624 | PRTDIRINTERNAL pDir = (PRTDIRINTERNAL)RTMemAllocZ(cbAllocated);
|
---|
[1] | 625 | if (!pDir)
|
---|
[78050] | 626 | {
|
---|
| 627 | RTStrFree(pszAbsPath);
|
---|
[1] | 628 | return VERR_NO_MEMORY;
|
---|
[78050] | 629 | }
|
---|
[9355] | 630 | uint8_t *pb = (uint8_t *)pDir + cbDir;
|
---|
[1] | 631 |
|
---|
| 632 | /* initialize it */
|
---|
| 633 | pDir->u32Magic = RTDIR_MAGIC;
|
---|
[47535] | 634 | pDir->cbSelf = cbDir;
|
---|
[9355] | 635 | if (cbFilter)
|
---|
[1] | 636 | {
|
---|
[9355] | 637 | pDir->puszFilter = (PRTUNICP)pb;
|
---|
[78050] | 638 | int rc2 = RTStrToUniEx(pszFilter, RTSTR_MAX, &pDir->puszFilter, cucFilter0, &pDir->cucFilter);
|
---|
| 639 | AssertRC(rc2);
|
---|
[9355] | 640 | pb += cucFilter0 * sizeof(RTUNICP);
|
---|
| 641 | pDir->pszFilter = (char *)memcpy(pb, pszFilter, cbFilter);
|
---|
| 642 | pDir->cchFilter = cbFilter - 1;
|
---|
| 643 | pb += cbFilter;
|
---|
[1] | 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 | }
|
---|
[78153] | 669 | pDir->cchPath = cchAbsPath;
|
---|
[78050] | 670 | pDir->pszPath = (char *)memcpy(pb, pszAbsPath, cchAbsPath);
|
---|
[78153] | 671 | pb[cchAbsPath] = '\0';
|
---|
| 672 | Assert(pb - (uint8_t *)pDir + cchAbsPath + 1 <= cbAllocated);
|
---|
[70890] | 673 | pDir->pszName = NULL;
|
---|
| 674 | pDir->cchName = 0;
|
---|
| 675 | pDir->fFlags = fFlags;
|
---|
| 676 | pDir->fDirSlash = fDirSlash;
|
---|
| 677 | pDir->fDataUnread = false;
|
---|
[1] | 678 |
|
---|
| 679 | /*
|
---|
| 680 | * Hand it over to the native part.
|
---|
| 681 | */
|
---|
[78050] | 682 | int rc = rtDirNativeOpen(pDir, hRelativeDir, pvNativeRelative);
|
---|
[1] | 683 | if (RT_SUCCESS(rc))
|
---|
[69753] | 684 | *phDir = pDir;
|
---|
[1] | 685 | else
|
---|
| 686 | RTMemFree(pDir);
|
---|
[78050] | 687 | RTStrFree(pszAbsPath);
|
---|
[1] | 688 | return rc;
|
---|
| 689 | }
|
---|
| 690 |
|
---|
| 691 |
|
---|
[69753] | 692 | RTDECL(int) RTDirOpen(RTDIR *phDir, const char *pszPath)
|
---|
[1] | 693 | {
|
---|
| 694 | /*
|
---|
| 695 | * Validate input.
|
---|
| 696 | */
|
---|
[90781] | 697 | AssertPtrReturn(phDir, VERR_INVALID_POINTER);
|
---|
| 698 | AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
|
---|
[1] | 699 |
|
---|
| 700 | /*
|
---|
| 701 | * Take common cause with RTDirOpenFiltered().
|
---|
| 702 | */
|
---|
[69753] | 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));
|
---|
[1] | 705 | return rc;
|
---|
| 706 | }
|
---|
| 707 |
|
---|
| 708 |
|
---|
[69753] | 709 | DECLHIDDEN(int) rtDirOpenRelativeOrHandle(RTDIR *phDir, const char *pszPath, RTDIRFILTER enmFilter, uint32_t fFlags,
|
---|
[69716] | 710 | uintptr_t hRelativeDir, void *pvNativeRelative)
|
---|
[1] | 711 | {
|
---|
| 712 | /*
|
---|
| 713 | * Validate input.
|
---|
| 714 | */
|
---|
[90781] | 715 | AssertPtrReturn(phDir, VERR_INVALID_POINTER);
|
---|
| 716 | AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
|
---|
[69674] | 717 | AssertReturn(!(fFlags & ~RTDIR_F_VALID_MASK), VERR_INVALID_FLAGS);
|
---|
[1] | 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 | */
|
---|
[21486] | 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 | }
|
---|
[1] | 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 | */
|
---|
[69753] | 748 | int rc = rtDirOpenCommon(phDir, pszPath, pszFilter, enmFilter, fFlags, hRelativeDir, pvNativeRelative);
|
---|
[1] | 749 |
|
---|
[69691] | 750 | LogFlow(("RTDirOpenFiltered(%p:{%p}, %p:{%s}, %d, %#x, %p, %p): return %Rrc\n",
|
---|
[69753] | 751 | phDir,*phDir, pszPath, pszPath, enmFilter, fFlags, hRelativeDir, pvNativeRelative, rc));
|
---|
[1] | 752 | return rc;
|
---|
| 753 | }
|
---|
| 754 |
|
---|
[19211] | 755 |
|
---|
[69753] | 756 | RTDECL(int) RTDirOpenFiltered(RTDIR *phDir, const char *pszPath, RTDIRFILTER enmFilter, uint32_t fFlags)
|
---|
[69691] | 757 | {
|
---|
[69753] | 758 | return rtDirOpenRelativeOrHandle(phDir, pszPath, enmFilter, fFlags, ~(uintptr_t)0, NULL);
|
---|
[69691] | 759 | }
|
---|
| 760 |
|
---|
| 761 |
|
---|
[69753] | 762 | RTDECL(bool) RTDirIsValid(RTDIR hDir)
|
---|
[69674] | 763 | {
|
---|
| 764 | return RT_VALID_PTR(hDir)
|
---|
| 765 | && hDir->u32Magic == RTDIR_MAGIC;
|
---|
| 766 | }
|
---|
| 767 |
|
---|
| 768 |
|
---|
[28688] | 769 | RTDECL(int) RTDirFlushParent(const char *pszChild)
|
---|
| 770 | {
|
---|
[78178] | 771 | char *pszPath;
|
---|
| 772 | char *pszPathFree = NULL;
|
---|
| 773 | size_t const cchChild = strlen(pszChild);
|
---|
| 774 | if (cchChild < RTPATH_MAX)
|
---|
[78207] | 775 | pszPath = (char *)alloca(cchChild + 1);
|
---|
[78178] | 776 | else
|
---|
[28688] | 777 | {
|
---|
[78178] | 778 | pszPathFree = pszPath = (char *)RTMemTmpAlloc(cchChild + 1);
|
---|
| 779 | if (!pszPath)
|
---|
| 780 | return VERR_NO_TMP_MEMORY;
|
---|
[28688] | 781 | }
|
---|
[78178] | 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);
|
---|
[28688] | 790 | return rc;
|
---|
| 791 | }
|
---|
| 792 |
|
---|
[46008] | 793 |
|
---|
[46014] | 794 | RTDECL(int) RTDirQueryUnknownTypeEx(const char *pszComposedName, bool fFollowSymlinks,
|
---|
[46013] | 795 | RTDIRENTRYTYPE *penmType, PRTFSOBJINFO pObjInfo)
|
---|
[46008] | 796 | {
|
---|
[46013] | 797 | int rc = RTPathQueryInfoEx(pszComposedName, pObjInfo, RTFSOBJATTRADD_NOTHING,
|
---|
[46014] | 798 | fFollowSymlinks ? RTPATH_F_FOLLOW_LINK : RTPATH_F_ON_LINK);
|
---|
[46008] | 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 |
|
---|
[46014] | 825 | RTDECL(int) RTDirQueryUnknownType(const char *pszComposedName, bool fFollowSymlinks, RTDIRENTRYTYPE *penmType)
|
---|
[46008] | 826 | {
|
---|
[46014] | 827 | if ( *penmType != RTDIRENTRYTYPE_UNKNOWN
|
---|
| 828 | && ( !fFollowSymlinks
|
---|
| 829 | || *penmType != RTDIRENTRYTYPE_SYMLINK))
|
---|
[46008] | 830 | return VINF_SUCCESS;
|
---|
| 831 |
|
---|
| 832 | RTFSOBJINFO ObjInfo;
|
---|
[46014] | 833 | return RTDirQueryUnknownTypeEx(pszComposedName, fFollowSymlinks, penmType, &ObjInfo);
|
---|
[46008] | 834 | }
|
---|
| 835 |
|
---|
| 836 |
|
---|
| 837 | RTDECL(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 |
|
---|
| 849 | RTDECL(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 |
|
---|
[69757] | 860 |
|
---|
| 861 | RTDECL(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 |
|
---|
| 904 | RTDECL(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 |
|
---|