/** * Checks quickly if this is an correct root specification. * Root specs ends with a slash of some kind. * * @returns indicator. * @param pszFsPath Path to check. */ static bool rtFsIsRoot(const char *pszFsPath) { /* * UNC has exactly two slashes.. * * Anything else starting with slashe(s) requires * expansion and will have to take the long road. */ if (RTPATH_IS_SLASH(pszFsPath[0])) { if ( !RTPATH_IS_SLASH(pszFsPath[1]) || RTPATH_IS_SLASH(pszFsPath[2])) return false; /* end of machine name */ const char *pszSlash = strpbrk(pszFsPath + 2, "\\/"); if (!pszSlash) return false; /* end of service name. */ pszSlash = strpbrk(pszSlash + 1, "\\/"); if (!pszSlash) return false; return pszSlash[1] == '\0'; } /* * Ok the other alternative is driver letter. */ return pszFsPath[0] >= 'A' && pszFsPath[0] <= 'Z' && pszFsPath[1] == ':' && RTPATH_IS_SLASH(pszFsPath[2]) && !pszFsPath[3]; }
/** * Check the given UTF-8 path for root escapes. * * Verify that the path is within the root directory of the mapping. Count '..' * and other path components and check that we do not go over the root. * * @returns VBox status code. * @retval VINF_SUCCESS * @retval VERR_INVALID_NAME * * @param pszPath The (UTF-8) path to check. Slashes has been convert * to host slashes by this time. * * @remarks This function assumes that the path will be appended to the root * directory of the shared folder mapping. Keep that in mind when * checking absolute paths! */ static int vbsfPathCheckRootEscape(const char *pszPath) { /* * Walk the path, component by component and check for escapes. */ int cComponents = 0; /* How many normal path components. */ int cParentDirs = 0; /* How many '..' components. */ for (;;) { char ch; /* Skip leading path delimiters. */ do ch = *pszPath++; while (RTPATH_IS_SLASH(ch)); if (ch == '\0') return VINF_SUCCESS; /* Check if that is a dot component. */ int cDots = 0; while (ch == '.') { cDots++; ch = *pszPath++; } if ( cDots >= 1 && (ch == '\0' || RTPATH_IS_SLASH(ch)) ) { if (cDots >= 2) /* Consider all multidots sequences as a 'parent dir'. */ { cParentDirs++; /* Escaping? */ if (cParentDirs > cComponents) return VERR_INVALID_NAME; } /* else: Single dot, nothing changes. */ } else { /* Not a dot component, skip to the end of it. */ while (ch != '\0' && !RTPATH_IS_SLASH(ch)) ch = *pszPath++; cComponents++; } Assert(cComponents >= cParentDirs); /* The end? */ Assert(ch == '\0' || RTPATH_IS_SLASH(ch)); if (ch == '\0') return VINF_SUCCESS; } }
/** * Finds the root of the specified volume. * * @returns iprt status code. * @param pszFsPath Path within the filesystem. Verified as one byte or more. * @param ppwszFsRoot Where to store the returned string. Free with rtFsFreeRoot(), */ static int rtFsGetRoot(const char *pszFsPath, PRTUTF16 *ppwszFsRoot) { /* * Do straight forward stuff first, */ if (rtFsIsRoot(pszFsPath)) return RTStrToUtf16(pszFsPath, ppwszFsRoot); /* * Expand and add slash (if required). */ char szFullPath[RTPATH_MAX]; int rc = RTPathAbs(pszFsPath, szFullPath, sizeof(szFullPath)); if (RT_FAILURE(rc)) return rc; size_t cb = strlen(szFullPath); if (!RTPATH_IS_SLASH(szFullPath[cb - 1])) { AssertReturn(cb + 1 < RTPATH_MAX, VERR_FILENAME_TOO_LONG); szFullPath[cb] = '\\'; szFullPath[++cb] = '\0'; } /* * Convert the path. */ rc = RTStrToUtf16(szFullPath, ppwszFsRoot); if (RT_FAILURE(rc)) return rc == VERR_BUFFER_OVERFLOW ? VERR_FILENAME_TOO_LONG : rc; /* * Walk the path until our proper API is happy or there is no more path left. */ PRTUTF16 pwszStart = *ppwszFsRoot; if (!GetVolumeInformationW(pwszStart, NULL, 0, NULL, NULL, 0, NULL, 0)) { PRTUTF16 pwszEnd = pwszStart + RTUtf16Len(pwszStart); PRTUTF16 pwszMin = pwszStart + 2; do { /* Strip off the last path component. */ while (pwszEnd-- > pwszMin) if (RTPATH_IS_SLASH(*pwszEnd)) break; AssertReturn(pwszEnd >= pwszMin, VERR_INTERNAL_ERROR); /* leaks, but that's irrelevant for an internal error. */ pwszEnd[1] = '\0'; } while (!GetVolumeInformationW(pwszStart, NULL, 0, NULL, NULL, 0, NULL, 0)); } return VINF_SUCCESS; }
/** * Get the absolute path (no symlinks, no . or .. components), assuming the * given base path as the current directory. The resulting path doesn't have * to exist. * * @returns iprt status code. * @param pszBase The base path to act like a current directory. * When NULL, the actual cwd is used (i.e. the call * is equivalent to RTPathAbs(pszPath, ...). * @param pszPath The path to resolve. * @param pszAbsPath Where to store the absolute path. * @param cchAbsPath Size of the buffer. */ RTDECL(int) RTPathAbsEx(const char *pszBase, const char *pszPath, char *pszAbsPath, size_t cchAbsPath) { if ( pszBase && pszPath && !rtPathVolumeSpecLen(pszPath) ) { #if defined(RT_OS_WINDOWS) /* The format for very long paths is not supported. */ if ( RTPATH_IS_SLASH(pszBase[0]) && RTPATH_IS_SLASH(pszBase[1]) && pszBase[2] == '?' && RTPATH_IS_SLASH(pszBase[3]) ) return VERR_INVALID_NAME; #endif /** @todo there are a couple of things which isn't 100% correct, although the * current code will have to do for now, no time to fix. * * 1) On Windows & OS/2 we confuse '/' with an abspath spec and will * not necessarily resolve it on the right drive. * 2) A trailing slash in the base might cause UNC names to be created. */ size_t const cchPath = strlen(pszPath); char szTmpPath[RTPATH_MAX]; if (RTPATH_IS_SLASH(pszPath[0])) { /* join the disk name from base and the path (DOS systems only) */ size_t const cchVolSpec = rtPathVolumeSpecLen(pszBase); if (cchVolSpec + cchPath + 1 > sizeof(szTmpPath)) return VERR_FILENAME_TOO_LONG; memcpy(szTmpPath, pszBase, cchVolSpec); memcpy(&szTmpPath[cchVolSpec], pszPath, cchPath + 1); } else { /* join the base path and the path */ size_t const cchBase = strlen(pszBase); if (cchBase + 1 + cchPath + 1 > sizeof(szTmpPath)) return VERR_FILENAME_TOO_LONG; memcpy(szTmpPath, pszBase, cchBase); szTmpPath[cchBase] = RTPATH_DELIMITER; memcpy(&szTmpPath[cchBase + 1], pszPath, cchPath + 1); } return RTPathAbs(szTmpPath, pszAbsPath, cchAbsPath); } /* Fallback to the non *Ex version */ return RTPathAbs(pszPath, pszAbsPath, cchAbsPath); }
RTDECL(size_t) RTPathCountComponents(const char *pszPath) { size_t off = rtPathRootSpecLen(pszPath); size_t c = off != 0; while (pszPath[off]) { c++; while (!RTPATH_IS_SLASH(pszPath[off]) && pszPath[off]) off++; while (RTPATH_IS_SLASH(pszPath[off])) off++; } return c; }
/** * Get the absolute path (no symlinks, no . or .. components), doesn't have to exit. * * @returns iprt status code. * @param pszPath The path to resolve. * @param pszAbsPath Where to store the absolute path. * @param cchAbsPath Size of the buffer. */ RTDECL(int) RTPathAbs(const char *pszPath, char *pszAbsPath, size_t cchAbsPath) { /* * Validation. */ AssertPtr(pszAbsPath); AssertPtr(pszPath); if (RT_UNLIKELY(!*pszPath)) return VERR_INVALID_PARAMETER; /* * Convert to UTF-16, call Win32 API, convert back. */ LPWSTR pwszPath; int rc = RTStrToUtf16(pszPath, &pwszPath); if (!RT_SUCCESS(rc)) return (rc); LPWSTR pwszFile; /* Ignored */ RTUTF16 wsz[RTPATH_MAX]; rc = GetFullPathNameW(pwszPath, RT_ELEMENTS(wsz), &wsz[0], &pwszFile); if (rc > 0 && rc < RT_ELEMENTS(wsz)) { size_t cch; rc = RTUtf16ToUtf8Ex(&wsz[0], RTSTR_MAX, &pszAbsPath, cchAbsPath, &cch); if (RT_SUCCESS(rc)) { # if 1 /** @todo This code is completely bonkers. */ /* * Remove trailing slash if the path may be pointing to a directory. * (See posix variant.) */ if ( cch > 1 && RTPATH_IS_SLASH(pszAbsPath[cch - 1]) && !RTPATH_IS_VOLSEP(pszAbsPath[cch - 2]) && !RTPATH_IS_SLASH(pszAbsPath[cch - 2])) pszAbsPath[cch - 1] = '\0'; # endif } } else if (rc <= 0) rc = RTErrConvertFromWin32(GetLastError()); else rc = VERR_FILENAME_TOO_LONG; RTUtf16Free(pwszPath); return rc; }
/** * Figures the length of the root part of the path. * * @returns length of the root specifier. * @retval 0 if none. * * @param pszPath The path to investigate. * * @remarks Unnecessary root slashes will not be counted. The caller will have * to deal with it where it matters. (Unlike rtPathRootSpecLen which * counts them.) */ static size_t rtPathRootSpecLen2(const char *pszPath) { /* fend of wildlife. */ if (!pszPath) return 0; /* Root slash? */ if (RTPATH_IS_SLASH(pszPath[0])) { #if defined (RT_OS_OS2) || defined (RT_OS_WINDOWS) /* UNC? */ if ( RTPATH_IS_SLASH(pszPath[1]) && pszPath[2] != '\0' && !RTPATH_IS_SLASH(pszPath[2])) { /* Find the end of the server name. */ const char *pszEnd = pszPath + 2; pszEnd += 2; while ( *pszEnd != '\0' && !RTPATH_IS_SLASH(*pszEnd)) pszEnd++; if (RTPATH_IS_SLASH(*pszEnd)) { pszEnd++; while (RTPATH_IS_SLASH(*pszEnd)) pszEnd++; /* Find the end of the share name */ while ( *pszEnd != '\0' && !RTPATH_IS_SLASH(*pszEnd)) pszEnd++; if (RTPATH_IS_SLASH(*pszEnd)) pszEnd++; return pszPath - pszEnd; } } #endif return 1; } #if defined (RT_OS_OS2) || defined (RT_OS_WINDOWS) /* Drive specifier? */ if ( pszPath[0] != '\0' && pszPath[1] == ':' && RT_C_IS_ALPHA(pszPath[0])) { if (RTPATH_IS_SLASH(pszPath[2])) return 3; return 2; } #endif return 0; }
/** * Validates the specified file or directory. * * @returns IPRT status code, errors go via rtPathRmError. * @param pOpts The RM options. * @param pszPath The path to the file, directory, whatever. */ static int rtPathRmOneValidate(PRTPATHRMCMDOPTS pOpts, const char *pszPath) { /* * RTPathFilename doesn't do the trailing slash thing the way we need it to. * E.g. both '..' and '../' should be rejected. */ size_t cchPath = strlen(pszPath); while (cchPath > 0 && RTPATH_IS_SLASH(pszPath[cchPath - 1])) cchPath--; if ( ( cchPath == 0 || 0 /** @todo drive letter + UNC crap */) && pOpts->fPreserveRoot) return rtPathRmError(pOpts, pszPath, VERR_CANT_DELETE_DIRECTORY, "Cannot remove root directory ('%s').\n", pszPath); size_t offLast = cchPath - 1; while (offLast > 0 && !RTPATH_IS_SEP(pszPath[offLast - 1])) offLast--; size_t cchLast = cchPath - offLast; if ( pszPath[offLast] == '.' && ( cchLast == 1 || (cchLast == 2 && pszPath[offLast + 1] == '.'))) return rtPathRmError(pOpts, pszPath, VERR_CANT_DELETE_DIRECTORY, "Cannot remove special directory '%s'.\n", pszPath); return VINF_SUCCESS; }
RTDECL(int) RTPathCopyComponents(char *pszDst, size_t cbDst, const char *pszSrc, size_t cComponents) { /* * Quick input validation. */ AssertPtr(pszDst); AssertPtr(pszSrc); if (cbDst == 0) return VERR_BUFFER_OVERFLOW; /* * Fend of the simple case where nothing is wanted. */ if (cComponents == 0) { *pszDst = '\0'; return VINF_SUCCESS; } /* * Parse into the path until we've counted the desired number of objects * or hit the end. */ size_t off = rtPathRootSpecLen(pszSrc); size_t c = off != 0; while (c < cComponents && pszSrc[off]) { c++; while (!RTPATH_IS_SLASH(pszSrc[off]) && pszSrc[off]) off++; while (RTPATH_IS_SLASH(pszSrc[off])) off++; } /* * Copy up to but not including 'off'. */ if (off >= cbDst) return VERR_BUFFER_OVERFLOW; memcpy(pszDst, pszSrc, off); pszDst[off] = '\0'; return VINF_SUCCESS; }
/** * Figures out the length of the root (or drive) specifier in @a pszPath. * * For UNC names, we consider the root specifier to include both the server and * share names. * * @returns The length including all slashes. 0 if relative path. * * @param pszPath The path to examine. */ DECLHIDDEN(size_t) rtPathRootSpecLen(const char *pszPath) { /* * If it's an absolute path, threat the root or volume specification as * component 0. UNC is making this extra fun on OS/2 and Windows as usual. */ size_t off = 0; if (RTPATH_IS_SLASH(pszPath[0])) { #if defined(RT_OS_OS2) || defined(RT_OS_WINDOWS) if ( RTPATH_IS_SLASH(pszPath[1]) && !RTPATH_IS_SLASH(pszPath[2]) && pszPath[2]) { /* UNC server name */ off = 2; while (!RTPATH_IS_SLASH(pszPath[off]) && pszPath[off]) off++; while (RTPATH_IS_SLASH(pszPath[off])) off++; /* UNC share */ while (!RTPATH_IS_SLASH(pszPath[off]) && pszPath[off]) off++; } else #endif { off = 1; } while (RTPATH_IS_SLASH(pszPath[off])) off++; } #if defined(RT_OS_OS2) || defined(RT_OS_WINDOWS) else if (RT_C_IS_ALPHA(pszPath[0]) && pszPath[1] == ':') { off = 2; while (RTPATH_IS_SLASH(pszPath[off])) off++; } #endif Assert(!RTPATH_IS_SLASH(pszPath[off])); return off; }
/** * Cleans up a path specifier a little bit. * * This includes removing duplicate slashes, unnecessary single dots, and * trailing slashes. Also, replaces all slash characters with RTPATH_SLASH. * * @returns Length of the cleaned path (in chars). * @param pszPath The path to cleanup. */ static int fsCleanPath(char *pszPath) { char *pszSrc = pszPath; char *pszTrg = pszPath; /* * On windows, you either use on or two slashes at the head of a path, * seems like it treats additional slashes as part of the UNC server name. * Just change slashes to RTPATH_SLASH and skip them. */ /** @todo check how OS/2 treats unnecessary leading slashes */ int cchIgnoreLeading = 0; #ifdef HAVE_UNC if ( RTPATH_IS_SLASH(pszSrc[0]) && RTPATH_IS_SLASH(pszSrc[1])) { pszTrg[0] = RTPATH_SLASH; pszTrg[1] = RTPATH_SLASH; pszTrg += 2; pszSrc += 2; cchIgnoreLeading = 1; while (RTPATH_IS_SLASH(*pszSrc)) { cchIgnoreLeading++; pszSrc++; *pszTrg++ = RTPATH_SLASH; } } #endif /* * Change slashes to RTPATH_SLASH and remove duplicates. */ for (;;) { char ch = *pszSrc++; if (RTPATH_IS_SLASH(ch)) { *pszTrg++ = RTPATH_SLASH; for (;;) { do ch = *pszSrc++; while (RTPATH_IS_SLASH(ch)); /* Remove '/./' and '/.'. */ if ( ch != '.' || (*pszSrc && !RTPATH_IS_SLASH(*pszSrc))) break; } } *pszTrg = ch; if (!ch) break; pszTrg++; } return pszTrg - pszPath; }
/** * Cleans up a path specifier a little bit. * This includes removing duplicate slashes, unnecessary single dots, and * trailing slashes. Also, replaces all RTPATH_SLASH characters with '/'. * * @returns Number of bytes in the clean path. * @param pszPath The path to cleanup. */ static int fsCleanPath(char *pszPath) { /* * Change to '/' and remove duplicates. */ char *pszSrc = pszPath; char *pszTrg = pszPath; #ifdef HAVE_UNC int fUnc = 0; if ( RTPATH_IS_SLASH(pszPath[0]) && RTPATH_IS_SLASH(pszPath[1])) { /* Skip first slash in a unc path. */ pszSrc++; *pszTrg++ = '/'; fUnc = 1; } #endif for (;;) { char ch = *pszSrc++; if (RTPATH_IS_SLASH(ch)) { *pszTrg++ = '/'; for (;;) { do ch = *pszSrc++; while (RTPATH_IS_SLASH(ch)); /* Remove '/./' and '/.'. */ if (ch != '.' || (*pszSrc && !RTPATH_IS_SLASH(*pszSrc))) break; } } *pszTrg = ch; if (!ch) break; pszTrg++; } /* * Remove trailing slash if the path may be pointing to a directory. */ int cch = pszTrg - pszPath; if ( cch > 1 && RTPATH_IS_SLASH(pszTrg[-1]) #ifdef HAVE_DRIVE && !RTPATH_IS_VOLSEP(pszTrg[-2]) #endif && !RTPATH_IS_SLASH(pszTrg[-2])) pszPath[--cch] = '\0'; return cch; }
/** * Adds a image bundle of some sort. * * @returns IPRT status code. * @param pszPath Path to the bundle. This a RTPATH_MAX size * buffer that we can write to when creating the * path to the file inside the bundle that we're * interested in. * @param cchPath The length of the path up to the bundle name. * @param cchName The length of the bundle name. * @param pDirEntry The directory entry buffer, for handling bundle * within bundle recursion. * @param pCfg The configuration. */ static int rtDbgSymCacheAddImageBundle(char *pszPath, size_t cchPath, size_t cchName, PRTDIRENTRYEX pDirEntry, PCRTDBGSYMCACHEADDCFG pCfg) { /* * Assuming these are kexts or simple applications, we only add the image * file itself to the cache. No Info.plist or other files. */ /** @todo consider looking for Frameworks and handling framework bundles. */ int rc = rtDbgSymCacheConstructBundlePath(pszPath, cchPath, cchName, "Contents/MacOS/", g_apszBundleSuffixes); if (RT_SUCCESS(rc)) rc = rtDbgSymCacheAddImageFile(pszPath, NULL, RTDBG_CACHE_UUID_MAP_DIR_IMAGES, pCfg); /* * Look for plugins and other sub-bundles. */ if (pCfg->fRecursive) { static char const * const s_apszSubBundleDirs[] = { "Contents/Plugins/", /** @todo Frameworks ++ */ }; for (uint32_t i = 0; i < RT_ELEMENTS(s_apszSubBundleDirs); i++) { pszPath[cchPath + cchName] = '\0'; int rc2 = RTPathAppend(pszPath, RTPATH_MAX - 1, s_apszSubBundleDirs[i]); if (RT_SUCCESS(rc2)) { if (RTDirExists(pszPath)) { size_t cchPath2 = strlen(pszPath); if (!RTPATH_IS_SLASH(pszPath[cchPath2 - 1])) { pszPath[cchPath2++] = RTPATH_SLASH; pszPath[cchPath2] = '\0'; } rc2 = rtDbgSymCacheAddDirWorker(pszPath, cchPath2, pDirEntry, pCfg); } } else { pszPath[cchPath + cchName] = '\0'; RTMsgError("Error constructing bundle subdir path for '%s' + '%s': %Rrc", pszPath, s_apszSubBundleDirs[i], rc); } if (RT_FAILURE(rc2) && RT_SUCCESS(rc)) rc = rc2; } } return rc; }
/** * Checks if a path starts with the given parent path. * * This means that either the path and the parent path matches completely, or * that the path is to some file or directory residing in the tree given by the * parent directory. * * The path comparison takes platform-dependent details into account, * see RTPathCompare() for details. * * @returns |true| when \a pszPath starts with \a pszParentPath (or when they * are identical), or |false| otherwise. * * @param pszPath Path to check, must be an absolute path. * @param pszParentPath Parent path, must be an absolute path. * No trailing directory slash! */ RTDECL(bool) RTPathStartsWith(const char *pszPath, const char *pszParentPath) { if (pszPath == pszParentPath) return true; if (!pszPath || !pszParentPath) return false; if (rtPathCompare(pszPath, pszParentPath, true /* limited by path 2 */) != 0) return false; const size_t cchParentPath = strlen(pszParentPath); if (RTPATH_IS_SLASH(pszPath[cchParentPath])) return true; if (pszPath[cchParentPath] == '\0') return true; /* Deal with pszParentPath = root (or having a trailing slash). */ if ( cchParentPath > 0 && RTPATH_IS_SLASH(pszParentPath[cchParentPath - 1]) && RTPATH_IS_SLASH(pszPath[cchParentPath - 1])) return true; return false; }
/** * Strips the filename from a path. Truncates the given string in-place by overwriting the * last path separator character with a null byte in a platform-neutral way. * * @param pszPath Path from which filename should be extracted, will be truncated. * If the string contains no path separator, it will be changed to a "." string. */ RTDECL(void) RTPathStripFilename(char *pszPath) { char *psz = pszPath; char *pszLastSep = NULL; for (;; psz++) { switch (*psz) { /* handle separators. */ #if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2) case ':': pszLastSep = psz + 1; if (RTPATH_IS_SLASH(psz[1])) pszPath = psz + 1; else pszPath = psz; break; case '\\': #endif case '/': pszLastSep = psz; break; /* the end */ case '\0': if (!pszLastSep) { /* no directory component */ pszPath[0] = '.'; pszPath[1] = '\0'; } else if (pszLastSep == pszPath) { /* only root. */ pszLastSep[1] = '\0'; } else pszLastSep[0] = '\0'; return; } } /* will never get here */ }
RTDECL(int) RTPathGetCurrentDrive(char *pszPath, size_t cbPath) { #ifdef HAVE_DRIVE /* * Query the current directroy and extract the wanted information from it. */ int rc = RTPathGetCurrent(pszPath, cbPath); if (RT_SUCCESS(rc)) { /* * Drive letter? Chop off at root slash. */ if (pszPath[0] && RTPATH_IS_VOLSEP(pszPath[1])) { pszPath[2] = '\0'; return rc; } /* * UNC? Chop off after share. */ if ( RTPATH_IS_SLASH(pszPath[0]) && RTPATH_IS_SLASH(pszPath[1]) && !RTPATH_IS_SLASH(pszPath[2]) && pszPath[2]) { /* Work thru the server name. */ size_t off = 3; while (!RTPATH_IS_SLASH(pszPath[off]) && pszPath[off]) off++; size_t offServerSlash = off; /* Is there a share name? */ if (RTPATH_IS_SLASH(pszPath[off])) { while (RTPATH_IS_SLASH(pszPath[off])) off++; if (pszPath[off]) { /* Work thru the share name. */ while (!RTPATH_IS_SLASH(pszPath[off]) && pszPath[off]) off++; } /* No share name, chop at server name. */ else off = offServerSlash; } } return VERR_INTERNAL_ERROR_4; } return rc; #else /* !HAVE_DRIVE */ /* * No drive letters on this system, return empty string. */ if (cbPath > 0) { *pszPath = '\0'; return VINF_SUCCESS; } return VERR_BUFFER_OVERFLOW; #endif /* !HAVE_DRIVE */ }
/** * Create one directory and any missing parent directories. * * @returns exit code * @param pOpts The mkdir option. * @param pszDir The path to the new directory. */ static int rtCmdRmDirOneWithParents(RTCMDRMDIROPTS const *pOpts, const char *pszDir) { /* We need a copy we can work with here. */ char *pszCopy = RTStrDup(pszDir); if (!pszCopy) return RTMsgErrorExitFailure("Out of string memory!"); int rc; if (!pOpts->fAlwaysUseChainApi && !RTVfsChainIsSpec(pszDir) ) { size_t cchCopy = strlen(pszCopy); do { rc = RTDirRemove(pszCopy); if (RT_SUCCESS(rc)) { if (pOpts->fVerbose) RTPrintf("%s\n", pszCopy); } else if ((rc == VERR_PATH_NOT_FOUND || rc == VERR_FILE_NOT_FOUND) && pOpts->fIgnoreNonExisting) rc = VINF_SUCCESS; else { if ((rc == VERR_DIR_NOT_EMPTY || rc == VERR_SHARING_VIOLATION) && pOpts->fIgnoreNotEmpty) rc = VINF_SUCCESS; else RTMsgError("Failed to remove directory '%s': %Rrc", pszCopy, rc); break; } /* Strip off a component. */ while (cchCopy > 0 && RTPATH_IS_SLASH(pszCopy[cchCopy - 1])) cchCopy--; while (cchCopy > 0 && !RTPATH_IS_SLASH(pszCopy[cchCopy - 1])) cchCopy--; while (cchCopy > 0 && RTPATH_IS_SLASH(pszCopy[cchCopy - 1])) cchCopy--; pszCopy[cchCopy] = '\0'; } while (cchCopy > 0); } else { /* * Strip the final path element from the pszDir spec. */ char *pszFinalPath; char *pszSpec; uint32_t offError; rc = RTVfsChainSplitOffFinalPath(pszCopy, &pszSpec, &pszFinalPath, &offError); if (RT_SUCCESS(rc)) { /* * Open the root director/whatever. */ RTERRINFOSTATIC ErrInfo; RTVFSDIR hVfsBaseDir; if (pszSpec) { rc = RTVfsChainOpenDir(pszSpec, 0 /*fOpen*/, &hVfsBaseDir, &offError, RTErrInfoInitStatic(&ErrInfo)); if (RT_FAILURE(rc)) RTVfsChainMsgError("RTVfsChainOpenDir", pszSpec, rc, offError, &ErrInfo.Core); else if (!pszFinalPath) pszFinalPath = RTStrEnd(pszSpec, RTSTR_MAX); } else if (!RTPathStartsWithRoot(pszFinalPath)) { rc = RTVfsDirOpenNormal(".", 0 /*fOpen*/, &hVfsBaseDir); if (RT_FAILURE(rc)) RTMsgError("Failed to open '.' (for %s): %Rrc", rc, pszFinalPath); } else { char *pszRoot = pszFinalPath; pszFinalPath = RTPathSkipRootSpec(pszFinalPath); char const chSaved = *pszFinalPath; *pszFinalPath = '\0'; rc = RTVfsDirOpenNormal(pszRoot, 0 /*fOpen*/, &hVfsBaseDir); *pszFinalPath = chSaved; if (RT_FAILURE(rc)) RTMsgError("Failed to open root dir for '%s': %Rrc", rc, pszRoot); } /* * Walk the path component by component, starting at the end. */ if (RT_SUCCESS(rc)) { size_t cchFinalPath = strlen(pszFinalPath); while (RT_SUCCESS(rc) && cchFinalPath > 0) { rc = RTVfsDirRemoveDir(hVfsBaseDir, pszFinalPath, 0 /*fFlags*/); if (RT_SUCCESS(rc)) { if (pOpts->fVerbose) RTPrintf("%s\n", pszCopy); } else if ((rc == VERR_PATH_NOT_FOUND || rc == VERR_FILE_NOT_FOUND) && pOpts->fIgnoreNonExisting) rc = VINF_SUCCESS; else { if ((rc == VERR_DIR_NOT_EMPTY || rc == VERR_SHARING_VIOLATION) && pOpts->fIgnoreNotEmpty) rc = VINF_SUCCESS; else if (pszSpec) RTMsgError("Failed to remove directory '%s:%s': %Rrc", pszSpec, pszFinalPath, rc); else RTMsgError("Failed to remove directory '%s': %Rrc", pszFinalPath, rc); break; } /* Strip off a component. */ while (cchFinalPath > 0 && RTPATH_IS_SLASH(pszFinalPath[cchFinalPath - 1])) cchFinalPath--; while (cchFinalPath > 0 && !RTPATH_IS_SLASH(pszFinalPath[cchFinalPath - 1])) cchFinalPath--; while (cchFinalPath > 0 && RTPATH_IS_SLASH(pszFinalPath[cchFinalPath - 1])) cchFinalPath--; pszFinalPath[cchFinalPath] = '\0'; } RTVfsDirRelease(hVfsBaseDir); } } else RTVfsChainMsgError("RTVfsChainOpenParentDir", pszCopy, rc, offError, NULL); } RTStrFree(pszCopy); return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE; }
/** * Recursion worker for RTDirRemoveRecursive. * * @returns IPRT status code. * @param pszBuf The path buffer. Contains the abs path to the * directory to recurse into. Trailing slash. * @param cchDir The length of the directory we're cursing into, * including the trailing slash. * @param pDirEntry The dir entry buffer. (Shared to save stack.) * @param pObjInfo The object info buffer. (ditto) */ static int rtDirRemoveRecursiveSub(char *pszBuf, size_t cchDir, PRTDIRENTRY pDirEntry, PRTFSOBJINFO pObjInfo) { AssertReturn(RTPATH_IS_SLASH(pszBuf[cchDir - 1]), VERR_INTERNAL_ERROR_4); /* * Enumerate the directory content and dispose of it. */ PRTDIR pDir; int rc = RTDirOpen(&pDir, pszBuf); if (RT_FAILURE(rc)) return rc; while (RT_SUCCESS(rc = RTDirRead(pDir, pDirEntry, NULL))) { if ( pDirEntry->szName[0] != '.' || pDirEntry->cbName > 2 || ( pDirEntry->cbName == 2 && pDirEntry->szName[1] != '.') ) { /* Construct the full name of the entry. */ if (cchDir + pDirEntry->cbName + 1 /* dir slash */ >= RTPATH_MAX) { rc = VERR_FILENAME_TOO_LONG; break; } memcpy(&pszBuf[cchDir], pDirEntry->szName, pDirEntry->cbName + 1); /* Deal with the unknown type. */ if (pDirEntry->enmType == RTDIRENTRYTYPE_UNKNOWN) { rc = RTPathQueryInfoEx(pszBuf, pObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK); if (RT_SUCCESS(rc) && RTFS_IS_DIRECTORY(pObjInfo->Attr.fMode)) pDirEntry->enmType = RTDIRENTRYTYPE_DIRECTORY; else if (RT_SUCCESS(rc) && RTFS_IS_FILE(pObjInfo->Attr.fMode)) pDirEntry->enmType = RTDIRENTRYTYPE_FILE; else if (RT_SUCCESS(rc) && RTFS_IS_SYMLINK(pObjInfo->Attr.fMode)) pDirEntry->enmType = RTDIRENTRYTYPE_SYMLINK; } /* Try the delete the fs object. */ switch (pDirEntry->enmType) { case RTDIRENTRYTYPE_FILE: rc = RTFileDelete(pszBuf); break; case RTDIRENTRYTYPE_DIRECTORY: { size_t cchSubDir = cchDir + pDirEntry->cbName; pszBuf[cchSubDir++] = '/'; pszBuf[cchSubDir] = '\0'; rc = rtDirRemoveRecursiveSub(pszBuf, cchSubDir, pDirEntry, pObjInfo); if (RT_SUCCESS(rc)) { pszBuf[cchSubDir] = '\0'; rc = RTDirRemove(pszBuf); } break; } //case RTDIRENTRYTYPE_SYMLINK: // rc = RTSymlinkDelete(pszBuf, 0); // break; default: /** @todo not implemented yet. */ rc = VINF_SUCCESS; break; } if (RT_FAILURE(rc)) break; } } if (rc == VERR_NO_MORE_FILES) rc = VINF_SUCCESS; RTDirClose(pDir); return rc; }
int vbsfPathGuestToHost(SHFLCLIENTDATA *pClient, SHFLROOT hRoot, PCSHFLSTRING pGuestString, uint32_t cbGuestString, char **ppszHostPath, uint32_t *pcbHostPathRoot, uint32_t fu32Options, uint32_t *pfu32PathFlags) { #ifdef VBOX_STRICT /* * Check that the pGuestPath has correct size and encoding. */ if (ShflStringIsValidIn(pGuestString, cbGuestString, RT_BOOL(pClient->fu32Flags & SHFL_CF_UTF8)) == false) { LogFunc(("Invalid input string\n")); return VERR_INTERNAL_ERROR; } #else NOREF(cbGuestString); #endif /* * Resolve the root handle into a string. */ uint32_t cbRootLen = 0; const char *pszRoot = NULL; int rc = vbsfMappingsQueryHostRootEx(hRoot, &pszRoot, &cbRootLen); if (RT_FAILURE(rc)) { LogFunc(("invalid root\n")); return rc; } AssertReturn(cbRootLen > 0, VERR_INTERNAL_ERROR_2); /* vbsfMappingsQueryHostRootEx ensures this. */ /* * Get the UTF8 string with the relative path provided by the guest. * If guest uses UTF-16 then convert it to UTF-8. */ uint32_t cbGuestPath = 0; /* Shut up MSC */ const char *pchGuestPath = NULL; /* Ditto. */ char *pchGuestPathAllocated = NULL; /* Converted from UTF-16. */ if (BIT_FLAG(pClient->fu32Flags, SHFL_CF_UTF8)) { /* UTF-8 */ cbGuestPath = pGuestString->u16Length; pchGuestPath = pGuestString->String.ach; } else { /* UTF-16 */ #ifdef RT_OS_DARWIN /* Misplaced hack! See todo! */ uint32_t cwcSrc = 0; PRTUTF16 pwszSrc = NULL; rc = vbsfNormalizeStringDarwin(&pGuestString->String.ucs2[0], pGuestString->u16Length / sizeof(RTUTF16), &pwszSrc, &cwcSrc); #else uint32_t const cwcSrc = pGuestString->u16Length / sizeof(RTUTF16); PCRTUTF16 const pwszSrc = &pGuestString->String.ucs2[0]; #endif if (RT_SUCCESS(rc)) { size_t cbPathAsUtf8 = RTUtf16CalcUtf8Len(pwszSrc); if (cbPathAsUtf8 >= cwcSrc) { /* Allocate buffer that will be able to contain the converted UTF-8 string. */ pchGuestPathAllocated = (char *)RTMemAlloc(cbPathAsUtf8 + 1); if (RT_LIKELY(pchGuestPathAllocated != NULL)) { if (RT_LIKELY(cbPathAsUtf8)) { size_t cchActual; char *pszDst = pchGuestPathAllocated; rc = RTUtf16ToUtf8Ex(pwszSrc, cwcSrc, &pszDst, cbPathAsUtf8 + 1, &cchActual); AssertRC(rc); AssertStmt(RT_FAILURE(rc) || cchActual == cbPathAsUtf8, rc = VERR_INTERNAL_ERROR_4); Assert(strlen(pszDst) == cbPathAsUtf8); } if (RT_SUCCESS(rc)) { /* Terminate the string. */ pchGuestPathAllocated[cbPathAsUtf8] = '\0'; cbGuestPath = (uint32_t)cbPathAsUtf8; Assert(cbGuestPath == cbPathAsUtf8); pchGuestPath = pchGuestPathAllocated; } } else { rc = VERR_NO_MEMORY; } } else { AssertFailed(); rc = VERR_INTERNAL_ERROR_3; } #ifdef RT_OS_DARWIN RTMemFree(pwszSrc); #endif } } char *pszFullPath = NULL; if (RT_SUCCESS(rc)) { LogFlowFunc(("Root %s path %.*s\n", pszRoot, cbGuestPath, pchGuestPath)); /* * Allocate enough memory to build the host full path from the root and the relative path. */ const uint32_t cbFullPathAlloc = cbRootLen + 1 + cbGuestPath + 1; /* root + possible_slash + relative + 0 */ pszFullPath = (char *)RTMemAlloc(cbFullPathAlloc); if (RT_LIKELY(pszFullPath != NULL)) { /* Buffer for the verified guest path. */ char *pchVerifiedPath = (char *)RTMemAlloc(cbGuestPath + 1); if (RT_LIKELY(pchVerifiedPath != NULL)) { /* Init the pointer for the guest relative path. */ uint32_t cbSrc = cbGuestPath; const char *pchSrc = pchGuestPath; /* Strip leading delimiters from the path the guest specified. */ while ( cbSrc > 0 && *pchSrc == pClient->PathDelimiter) { ++pchSrc; --cbSrc; } /* * Iterate the guest path components, verify each of them replacing delimiters with the host slash. */ char *pchDst = pchVerifiedPath; bool fLastComponentHasWildcard = false; for (; cbSrc > 0; --cbSrc, ++pchSrc) { if (RT_LIKELY(*pchSrc != pClient->PathDelimiter)) { if (RT_LIKELY(vbsfPathIsValidNameChar(*pchSrc))) { if (pfu32PathFlags && vbsfPathIsWildcardChar(*pchSrc)) { fLastComponentHasWildcard = true; } *pchDst++ = *pchSrc; } else { rc = VERR_INVALID_NAME; break; } } else { /* Replace with the host slash. */ *pchDst++ = RTPATH_SLASH; if (pfu32PathFlags && fLastComponentHasWildcard && cbSrc > 1) { /* Processed component has a wildcard and there are more characters in the path. */ *pfu32PathFlags |= VBSF_F_PATH_HAS_WILDCARD_IN_PREFIX; } fLastComponentHasWildcard = false; } } if (RT_SUCCESS(rc)) { *pchDst++ = 0; /* Construct the full host path removing '.' and '..'. */ rc = vbsfPathAbs(pszRoot, pchVerifiedPath, pszFullPath, cbFullPathAlloc); if (RT_SUCCESS(rc)) { if (pfu32PathFlags && fLastComponentHasWildcard) { *pfu32PathFlags |= VBSF_F_PATH_HAS_WILDCARD_IN_LAST; } /* Check if the full path is still within the shared folder. */ if (fu32Options & VBSF_O_PATH_CHECK_ROOT_ESCAPE) { if (!RTPathStartsWith(pszFullPath, pszRoot)) { rc = VERR_INVALID_NAME; } } if (RT_SUCCESS(rc)) { /* * If the host file system is case sensitive and the guest expects * a case insensitive fs, then correct the path components casing. */ if ( vbsfIsHostMappingCaseSensitive(hRoot) && !vbsfIsGuestMappingCaseSensitive(hRoot)) { const bool fWildCard = RT_BOOL(fu32Options & VBSF_O_PATH_WILDCARD); const bool fPreserveLastComponent = RT_BOOL(fu32Options & VBSF_O_PATH_PRESERVE_LAST_COMPONENT); rc = vbsfCorrectPathCasing(pClient, pszFullPath, strlen(pszFullPath), fWildCard, fPreserveLastComponent); } if (RT_SUCCESS(rc)) { LogFlowFunc(("%s\n", pszFullPath)); /* Return the full host path. */ *ppszHostPath = pszFullPath; if (pcbHostPathRoot) { /* Return the length of the root path without the trailing slash. */ *pcbHostPathRoot = RTPATH_IS_SLASH(pszFullPath[cbRootLen - 1]) ? cbRootLen - 1 : /* pszRoot already had the trailing slash. */ cbRootLen; /* pszRoot did not have the trailing slash. */ } } } } else { LogFunc(("vbsfPathAbs %Rrc\n", rc)); } } RTMemFree(pchVerifiedPath); } else { rc = VERR_NO_MEMORY; } } else { rc = VERR_NO_MEMORY; } } /* * Cleanup. */ RTMemFree(pchGuestPathAllocated); if (RT_SUCCESS(rc)) { return rc; } /* * Cleanup on failure. */ RTMemFree(pszFullPath); LogFunc(("%Rrc\n", rc)); return rc; }
/** * Parses a path. * * It figures the length of the directory component, the offset of * the file name and the location of the suffix dot. * * @returns The path length. * * @param pszPath Path to find filename in. * @param pcchDir Where to put the length of the directory component. If * no directory, this will be 0. Optional. * @param poffName Where to store the filename offset. * If empty string or if it's ending with a slash this * will be set to -1. Optional. * @param poffSuff Where to store the suffix offset (the last dot). * If empty string or if it's ending with a slash this * will be set to -1. Optional. */ RTDECL(size_t) RTPathParseSimple(const char *pszPath, size_t *pcchDir, ssize_t *poffName, ssize_t *poffSuff) { const char *psz = pszPath; ssize_t offRoot = 0; const char *pszName = pszPath; const char *pszLastDot = NULL; for (;; psz++) { switch (*psz) { /* handle separators. */ #if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2) case ':': pszName = psz + 1; offRoot = pszName - psz; break; case '\\': #endif case '/': pszName = psz + 1; break; case '.': pszLastDot = psz; break; /* * The end. Complete the results. */ case '\0': { ssize_t offName = *pszName != '\0' ? pszName - pszPath : -1; if (poffName) *poffName = offName; if (poffSuff) { ssize_t offSuff = -1; if (pszLastDot) { offSuff = pszLastDot - pszPath; if (offSuff <= offName) offSuff = -1; } *poffSuff = offSuff; } if (pcchDir) { ssize_t off = offName - 1; while (off >= offRoot && RTPATH_IS_SLASH(pszPath[off])) off--; *pcchDir = RT_MAX(off, offRoot) + 1; } return psz - pszPath; } } } /* will never get here */ return 0; }
RTDECL(int) RTSymlinkCreate(const char *pszSymlink, const char *pszTarget, RTSYMLINKTYPE enmType, uint32_t fCreate) { /* * Validate the input. */ AssertReturn(enmType > RTSYMLINKTYPE_INVALID && enmType < RTSYMLINKTYPE_END, VERR_INVALID_PARAMETER); AssertPtrReturn(pszSymlink, VERR_INVALID_POINTER); AssertPtrReturn(pszTarget, VERR_INVALID_POINTER); /* * Resolve the API. */ typedef BOOLEAN (WINAPI *PFNCREATESYMBOLICLINKW)(LPCWSTR, LPCWSTR, DWORD); static PFNCREATESYMBOLICLINKW s_pfnCreateSymbolicLinkW = NULL; static bool s_fTried = FALSE; if (!s_fTried) { HMODULE hmod = LoadLibrary("KERNEL32.DLL"); if (hmod) { PFNCREATESYMBOLICLINKW pfn = (PFNCREATESYMBOLICLINKW)GetProcAddress(hmod, "CreateSymbolicLinkW"); if (pfn) s_pfnCreateSymbolicLinkW = pfn; } s_fTried = true; } if (!s_pfnCreateSymbolicLinkW) { LogFlow(("RTSymlinkCreate(%p={%s}, %p={%s}, %d, %#x): returns VERR_NOT_SUPPORTED - Windows API not found\n", pszSymlink, pszSymlink, pszTarget, pszTarget, enmType, fCreate)); return VERR_NOT_SUPPORTED; } /* * Convert the paths. */ PRTUTF16 pwszNativeSymlink; int rc = RTStrToUtf16(pszSymlink, &pwszNativeSymlink); if (RT_SUCCESS(rc)) { PRTUTF16 pwszNativeTarget; rc = RTStrToUtf16(pszTarget, &pwszNativeTarget); if (RT_SUCCESS(rc)) { /* * Massage the target path, determin the link type. */ size_t cchTarget = strlen(pszTarget); size_t cchVolSpecTarget = rtPathVolumeSpecLen(pszTarget); #if 0 /* looks like this isn't needed after all. That makes everything much simper :-) */ if ( cchTarget > RT_MIN(cchVolSpecTarget, 1) && RTPATH_IS_SLASH(pszTarget[cchTarget - 1])) { size_t cwcNativeTarget = RTUtf16Len(pwszNativeTarget); size_t offFromEnd = 1; while ( offFromEnd < cchTarget && cchTarget - offFromEnd >= cchVolSpecTarget && RTPATH_IS_SLASH(pszTarget[cchTarget - offFromEnd])) { Assert(offFromEnd < cwcNativeTarget); pwszNativeTarget[cwcNativeTarget - offFromEnd] = 0; offFromEnd++; } } #endif if (enmType == RTSYMLINKTYPE_UNKNOWN) { if ( cchTarget > cchVolSpecTarget && RTPATH_IS_SLASH(pszTarget[cchTarget - 1])) enmType = RTSYMLINKTYPE_DIR; else if (cchVolSpecTarget) { /** @todo this is subject to sharing violations. */ DWORD dwAttr = GetFileAttributesW(pwszNativeTarget); if ( dwAttr != INVALID_FILE_ATTRIBUTES && (dwAttr & FILE_ATTRIBUTE_DIRECTORY)) enmType = RTSYMLINKTYPE_DIR; } else { /** @todo Join the symlink directory with the target and * look up the attributes on that. -lazy bird. */ } } /* * Create the link. */ if (s_pfnCreateSymbolicLinkW(pwszNativeSymlink, pwszNativeTarget, enmType == RTSYMLINKTYPE_DIR)) rc = VINF_SUCCESS; else rc = RTErrConvertFromWin32(GetLastError()); RTUtf16Free(pwszNativeTarget); } RTUtf16Free(pwszNativeSymlink); } LogFlow(("RTSymlinkCreate(%p={%s}, %p={%s}, %d, %#x): returns %Rrc\n", pszSymlink, pszSymlink, pszTarget, pszTarget, enmType, fCreate, rc)); return rc; }
/** * Recursively delete a directory. * * @returns IPRT status code, errors go via rtPathRmError. * @param pOpts The RM options. * @param pszPath Pointer to a writable buffer holding the path to * the directory. * @param cchPath The length of the path (avoid strlen). * @param pDirEntry Pointer to a directory entry buffer that is * RTPATHRM_DIR_MAX_ENTRY_SIZE bytes big. */ static int rtPathRmRecursive(PRTPATHRMCMDOPTS pOpts, char *pszPath, size_t cchPath, PRTDIRENTRYEX pDirEntry) { /* * Make sure the path ends with a slash. */ if (!cchPath || !RTPATH_IS_SLASH(pszPath[cchPath - 1])) { if (cchPath + 1 >= RTPATH_MAX) return rtPathRmError(pOpts, pszPath, VERR_BUFFER_OVERFLOW, "Buffer overflow fixing up '%s'.\n", pszPath); pszPath[cchPath++] = RTPATH_SLASH; pszPath[cchPath] = '\0'; } /* * Traverse the directory. */ PRTDIR hDir; int rc = RTDirOpen(&hDir, pszPath); if (RT_FAILURE(rc)) return rtPathRmError(pOpts, pszPath, rc, "Error opening directory '%s': %Rrc", pszPath, rc); int rcRet = VINF_SUCCESS; for (;;) { /* * Read the next entry, constructing an full path for it. */ size_t cbEntry = RTPATHRM_DIR_MAX_ENTRY_SIZE; rc = RTDirReadEx(hDir, pDirEntry, &cbEntry, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK); if (rc == VERR_NO_MORE_FILES) { /* * Reached the end of the directory. */ pszPath[cchPath] = '\0'; rc = RTDirClose(hDir); if (RT_FAILURE(rc)) return rtPathRmError(pOpts, pszPath, rc, "Error closing directory '%s': %Rrc", pszPath, rc); /* Delete the directory. */ int rc2 = rtPathRmOneDir(pOpts, pszPath); if (RT_FAILURE(rc2) && RT_SUCCESS(rcRet)) return rc2; return rcRet; } if (RT_FAILURE(rc)) { rc = rtPathRmError(pOpts, pszPath, rc, "Error reading directory '%s': %Rrc", pszPath, rc); break; } /* Skip '.' and '..'. */ if ( pDirEntry->szName[0] == '.' && ( pDirEntry->cbName == 1 || ( pDirEntry->cbName == 2 && pDirEntry->szName[1] == '.'))) continue; /* Construct full path. */ if (cchPath + pDirEntry->cbName >= RTPATH_MAX) { pszPath[cchPath] = '\0'; rc = rtPathRmError(pOpts, pszPath, VERR_BUFFER_OVERFLOW, "Path buffer overflow in directory '%s'.", pszPath); break; } memcpy(pszPath + cchPath, pDirEntry->szName, pDirEntry->cbName + 1); /* * Take action according to the type. */ switch (pDirEntry->Info.Attr.fMode & RTFS_TYPE_MASK) { case RTFS_TYPE_FILE: rc = rtPathRmOneFile(pOpts, pszPath, &pDirEntry->Info); break; case RTFS_TYPE_DIRECTORY: rc = rtPathRmRecursive(pOpts, pszPath, cchPath + pDirEntry->cbName, pDirEntry); break; case RTFS_TYPE_SYMLINK: rc = rtPathRmOneSymlink(pOpts, pszPath); break; case RTFS_TYPE_FIFO: case RTFS_TYPE_DEV_CHAR: case RTFS_TYPE_DEV_BLOCK: case RTFS_TYPE_SOCKET: rc = rtPathRmOneFile(pOpts, pszPath, &pDirEntry->Info); break; case RTFS_TYPE_WHITEOUT: default: rc = rtPathRmError(pOpts, pszPath, VERR_UNEXPECTED_FS_OBJ_TYPE, "Object '%s' has an unknown file type: %o\n", pszPath, pDirEntry->Info.Attr.fMode & RTFS_TYPE_MASK); break; } if (RT_FAILURE(rc) && RT_SUCCESS(rcRet)) rcRet = rc; } /* * Some error occured, close and return. */ RTDirClose(hDir); return rc; }
RTDECL(int) RTPathAppendEx(char *pszPath, size_t cbPathDst, const char *pszAppend, size_t cchAppendMax) { char *pszPathEnd = RTStrEnd(pszPath, cbPathDst); AssertReturn(pszPathEnd, VERR_INVALID_PARAMETER); /* * Special cases. */ if (!pszAppend) return VINF_SUCCESS; size_t cchAppend = RTStrNLen(pszAppend, cchAppendMax); if (!cchAppend) return VINF_SUCCESS; if (pszPathEnd == pszPath) { if (cchAppend >= cbPathDst) return VERR_BUFFER_OVERFLOW; memcpy(pszPath, pszAppend, cchAppend); pszPath[cchAppend] = '\0'; return VINF_SUCCESS; } /* * Balance slashes and check for buffer overflow. */ if (!RTPATH_IS_SLASH(pszPathEnd[-1])) { if (!RTPATH_IS_SLASH(pszAppend[0])) { #if defined (RT_OS_OS2) || defined (RT_OS_WINDOWS) if ( (size_t)(pszPathEnd - pszPath) == 2 && pszPath[1] == ':' && RT_C_IS_ALPHA(pszPath[0])) { if ((size_t)(pszPathEnd - pszPath) + cchAppend >= cbPathDst) return VERR_BUFFER_OVERFLOW; } else #endif { if ((size_t)(pszPathEnd - pszPath) + 1 + cchAppend >= cbPathDst) return VERR_BUFFER_OVERFLOW; *pszPathEnd++ = RTPATH_SLASH; } } else { /* One slash is sufficient at this point. */ while (cchAppend > 1 && RTPATH_IS_SLASH(pszAppend[1])) pszAppend++, cchAppend--; if ((size_t)(pszPathEnd - pszPath) + cchAppend >= cbPathDst) return VERR_BUFFER_OVERFLOW; } } else { /* No slashes needed in the appended bit. */ while (cchAppend && RTPATH_IS_SLASH(*pszAppend)) pszAppend++, cchAppend--; /* In the leading path we can skip unnecessary trailing slashes, but be sure to leave one. */ size_t const cchRoot = rtPathRootSpecLen2(pszPath); while ( (size_t)(pszPathEnd - pszPath) > RT_MAX(1, cchRoot) && RTPATH_IS_SLASH(pszPathEnd[-2])) pszPathEnd--; if ((size_t)(pszPathEnd - pszPath) + cchAppend >= cbPathDst) return VERR_BUFFER_OVERFLOW; } /* * What remains now is the just the copying. */ memcpy(pszPathEnd, pszAppend, cchAppend); pszPathEnd[cchAppend] = '\0'; return VINF_SUCCESS; }