/* * canon.c - Canonical path manipulation module. */ /* Headers **********/ #include "project.h" #pragma hdrstop /***************************** Private Functions *****************************/ /* Module Prototypes ********************/ PRIVATE_CODE BOOL GetCNRInfoForDevice(LPTSTR, LPTSTR, PDWORD, PDWORD); PRIVATE_CODE BOOL GetDrivePathInfo(LPTSTR, PDWORD, LPTSTR, LPTSTR *); PRIVATE_CODE BOOL GetRemotePathInfo(LPTSTR, PDWORD, LPTSTR, LPTSTR *); PRIVATE_CODE void CanonicalizeTrailingSlash(LPTSTR); #ifdef DEBUG PRIVATE_CODE BOOL CheckFullPathInfo(LPCTSTR, PDWORD, LPCTSTR, LPCTSTR *); #endif /* ** GetCNRInfoForDevice() ** ** ** ** Arguments: ** ** Returns: BOOL ** ** Side Effects: none */ PRIVATE_CODE BOOL GetCNRInfoForDevice(LPTSTR pszDeviceName, LPTSTR pszNameBuf, PDWORD pdwcbLen, PDWORD pdwOutFlags) { DWORD dwNetResult; BOOL bResult; /* "X:" + null terminator */ TCHAR rgchDrive[2 + 1]; ASSERT(IS_VALID_STRING_PTR(pszDeviceName, CSTR)); ASSERT(IS_VALID_WRITE_PTR(pdwcbLen, DWORD)); ASSERT(*pdwcbLen > 0); ASSERT(IS_VALID_WRITE_BUFFER_PTR(pszNameBuf, TCHAR, (UINT)(*pdwcbLen))); ASSERT(IS_VALID_WRITE_PTR(pdwOutFlags, DWORD)); /* WNetGetConnection requires the device name to have no trailing ** backslash. */ MyLStrCpyN(rgchDrive, pszDeviceName, ARRAYSIZE(rgchDrive)); dwNetResult = WNetGetConnection(rgchDrive, pszNameBuf, pdwcbLen); switch (dwNetResult) { case NO_ERROR: *pdwOutFlags = GCPI_OFL_REMOTE; bResult = TRUE; TRACE_OUT((TEXT("GetCNRInfoForDevice(): %s is redirected to net resource \"%s\"."), pszDeviceName, pszNameBuf)); break; case ERROR_NOT_CONNECTED: *pdwOutFlags = 0; bResult = TRUE; TRACE_OUT((TEXT("GetCNRInfoForDevice(): %s is not redirected."), pszDeviceName)); break; default: WARNING_OUT((TEXT("GetCNRInfoForDevice(): WNetGetConnection() on %s returned %lu."), pszDeviceName, dwNetResult)); bResult = FALSE; break; } ASSERT(! bResult || FLAGS_ARE_VALID(*pdwOutFlags, ALL_GCPI_OFLAGS) && (IS_FLAG_CLEAR(*pdwOutFlags, GCPI_OFL_REMOTE) || IsValidCNRName(pszNameBuf))); return(bResult); } /* ** GetDrivePathInfo() ** ** ** ** Arguments: ** ** Returns: ** ** Side Effects: none */ PRIVATE_CODE BOOL GetDrivePathInfo(LPTSTR pszDrivePath, PDWORD pdwOutFlags, LPTSTR pszNetResourceNameBuf, LPTSTR *ppszRootPathSuffix) { BOOL bResult; /* "X:\" + null terminator. */ TCHAR rgchDriveRootPath[3 + 1]; ASSERT(IsDrivePath(pszDrivePath)); ASSERT(IS_VALID_WRITE_PTR(pdwOutFlags, DWORD)); ASSERT(IS_VALID_WRITE_BUFFER_PTR(pszNetResourceNameBuf, STR, MAX_PATH_LEN)); ASSERT(IS_VALID_WRITE_PTR(ppszRootPathSuffix, LPTSTR)); ASSERT(lstrlen(pszDrivePath) >= 3); *pdwOutFlags = 0; MyLStrCpyN(rgchDriveRootPath, pszDrivePath, ARRAYSIZE(rgchDriveRootPath)); ASSERT(IsDriveRootPath(rgchDriveRootPath)); /* Do we need to get the CNR name for this drive path? */ if (GetDriveType(rgchDriveRootPath) != DRIVE_REMOTE) /* No. */ bResult = TRUE; else { DWORD dwcbBufLen = MAX_PATH_LEN; /* Yes. */ bResult = GetCNRInfoForDevice(rgchDriveRootPath, pszNetResourceNameBuf, &dwcbBufLen, pdwOutFlags); } *ppszRootPathSuffix = pszDrivePath + 3; ASSERT(! bResult || CheckFullPathInfo(pszDrivePath, pdwOutFlags, pszNetResourceNameBuf, ppszRootPathSuffix)); return(bResult); } /* ** GetRemotePathInfo() ** ** ** ** Arguments: ** ** Returns: ** ** Side Effects: none */ PRIVATE_CODE BOOL GetRemotePathInfo(LPTSTR pszRemotePath, PDWORD pdwOutFlags, LPTSTR pszNetResourceNameBuf, LPTSTR *ppszRootPathSuffix) { BOOL bResult; ASSERT(IsFullPath(pszRemotePath)); ASSERT(IS_VALID_WRITE_PTR(pdwOutFlags, DWORD)); ASSERT(IS_VALID_WRITE_BUFFER_PTR(pszNetResourceNameBuf, STR, MAX_PATH_LEN)); ASSERT(IS_VALID_WRITE_PTR(ppszRootPathSuffix, LPTSTR)); /* Is this a "\\server\share" name? */ bResult = IsUNCPath(pszRemotePath); if (bResult) { LPTSTR psz; *pdwOutFlags = 0; /* * Yes. Skip two leading slashes and look for end of \\server\share * specification. */ /* Assume (as above) that a slash cannot be a DBCS lead byte. */ for (psz = pszRemotePath + 2; ! IS_SLASH(*psz); psz = CharNext(psz)) ASSERT(*psz); ASSERT(IS_SLASH(*psz)); /* * Found first slash after double slash. Find end of string or next * slash as end of root specification. */ for (psz = CharNext(psz); *psz; psz = CharNext(psz)) { if (IS_SLASH(*psz)) break; } ASSERT(psz >= pszRemotePath); /* Add trailing slash for UNC root path. */ if (! *psz) { *psz = SLASH; *(psz + 1) = TEXT('\0'); } *ppszRootPathSuffix = (LPTSTR)psz + 1; ASSERT(! IS_SLASH(**ppszRootPathSuffix)); /* (+ 1) for null terminator. */ MyLStrCpyN(pszNetResourceNameBuf, pszRemotePath, (int)(psz - pszRemotePath + 1)); CharUpper(pszNetResourceNameBuf); SET_FLAG(*pdwOutFlags, GCPI_OFL_REMOTE); bResult = TRUE; } else /* Not a UNC path. */ SetLastError(ERROR_BAD_PATHNAME); ASSERT(! bResult || CheckFullPathInfo(pszRemotePath, pdwOutFlags, pszNetResourceNameBuf, ppszRootPathSuffix)); return(bResult); } /* ** CanonicalizeTrailingSlash() ** ** ** ** Arguments: ** ** Returns: ** ** Side Effects: none */ PRIVATE_CODE void CanonicalizeTrailingSlash(LPTSTR pszRootPathSuffix) { LPTSTR pszLast; ASSERT(IS_VALID_STRING_PTR(pszRootPathSuffix, STR)); ASSERT(! IS_SLASH(*pszRootPathSuffix)); /* No path suffix should end in a slash. */ pszLast = CharPrev(pszRootPathSuffix, pszRootPathSuffix + lstrlen(pszRootPathSuffix)); if (IS_SLASH(*pszLast)) *pszLast = TEXT('\0'); ASSERT(IsValidPathSuffix(pszRootPathSuffix)); return; } #ifdef DEBUG /* ** CheckFullPathInfo() ** ** ** ** Arguments: ** ** Returns: ** ** Side Effects: none */ PRIVATE_CODE BOOL CheckFullPathInfo(LPCTSTR pcszFullPath, PDWORD pdwOutFlags, LPCTSTR pcszNetResourceName, LPCTSTR *ppcszRootPathSuffix) { return(EVAL(IsFullPath(pcszFullPath)) && FLAGS_ARE_VALID(*pdwOutFlags, ALL_GCPI_OFLAGS) && (IS_FLAG_CLEAR(*pdwOutFlags, GCPI_OFL_REMOTE) || (EVAL(IsValidCNRName(pcszNetResourceName)) && EVAL(lstrlen(pcszNetResourceName) < MAX_PATH_LEN))) && (IS_FLAG_SET(*pdwOutFlags, GCPI_OFL_REMOTE) || EVAL(IsLocalDrivePath(pcszFullPath))) && IS_VALID_STRING_PTR(*ppcszRootPathSuffix, CSTR) && EVAL(IsStringContained(pcszFullPath, *ppcszRootPathSuffix))); } #endif /***************************** Exported Functions ****************************/ /****************************************************************************** @doc LINKINFOAPI @func BOOL | GetCanonicalPathInfo | Retrieves information about the canonical form of a path. @parm PCSTR | pcszPath | A pointer to the path string whose canonical form information is to be retrieved. @parm PSTR | pszCanonicalBuf | A pointer to a buffer to be filled in with the full canonical form of the path. This buffer must be at least MAX_PATH_LEN bytes long. @parm PDWORD | pdwOutFlags | A pointer to a DWORD bit mask of flags to be filled in with flags from the enumeration. @parm PSTR | pszNetResourceNameBuf | A pointer to a buffer to be filled in with the name of the net resource parent of the path. This buffer must be at least MAX_PATH_LEN bytes long. This buffer is only filled in if GCPI_OFL_REMOTE is set in *pdwOutFlags. @parm PSTR * | ppszRootPathSuffix | A pointer to a PSTR to be filled in with a pointer to the file system root path suffix, not including the leading slash, of the canonical path in pszCanonicalBuf's buffer. @rdesc If the function completed successfully, TRUE is returned. Otherwise, FALSE is returned. The reason for failure may be determined by calling GetLastError(). ******************************************************************************/ LINKINFOAPI BOOL WINAPI GetCanonicalPathInfo(LPCTSTR pcszPath, LPTSTR pszCanonicalBuf, PDWORD pdwOutFlags, LPTSTR pszNetResourceNameBuf, LPTSTR *ppszRootPathSuffix) { BOOL bResult; LPTSTR pszFileName; DWORD dwPathLen; ASSERT(IS_VALID_STRING_PTR(pcszPath, CSTR)); ASSERT(IS_VALID_WRITE_BUFFER_PTR(pszCanonicalBuf, STR, MAX_PATH_LEN)); ASSERT(IS_VALID_WRITE_PTR(pdwOutFlags, DWORD)); ASSERT(IS_VALID_WRITE_PTR(ppszRootPathSuffix, LPTSTR)); dwPathLen = GetFullPathName(pcszPath, MAX_PATH_LEN, pszCanonicalBuf, &pszFileName); if (dwPathLen > 0 && dwPathLen < MAX_PATH_LEN) { /* * Assume that GetFullPathName() changed all back slashes ('/') to * forward slashes ('\\'). */ ASSERT(! MyStrChr(pszCanonicalBuf, TEXT('/'), NULL)); if (IsDrivePath(pszCanonicalBuf)) bResult = GetDrivePathInfo(pszCanonicalBuf, pdwOutFlags, pszNetResourceNameBuf, ppszRootPathSuffix); else bResult = GetRemotePathInfo(pszCanonicalBuf, pdwOutFlags, pszNetResourceNameBuf, ppszRootPathSuffix); if (bResult) CanonicalizeTrailingSlash(*ppszRootPathSuffix); } else { // BOGUS ASSERT: We can also get here if the resulting full path // is bigger than MAX_PATH_LEN. // ASSERT(! dwPathLen); WARNING_OUT((TEXT("GetFullPathName() failed on path %s, returning %lu."), pcszPath, dwPathLen)); bResult = FALSE; } ASSERT(! bResult || (CheckFullPathInfo(pszCanonicalBuf, pdwOutFlags, pszNetResourceNameBuf, ppszRootPathSuffix) && IsValidPathSuffix(*ppszRootPathSuffix))); return(bResult); } #ifdef UNICODE LINKINFOAPI BOOL WINAPI GetCanonicalPathInfoA(LPCSTR pcszPath, LPSTR pszCanonicalBuf, PDWORD pdwOutFlags, LPSTR pszNetResourceNameBuf, LPSTR *ppszRootPathSuffix) { LPWSTR pcszWidePath; UINT cchPath; WCHAR szWideCanonicalBuf[MAX_PATH]; WCHAR szWideNetResourceNameBuf[MAX_PATH]; LPWSTR pszWideRootPathSuffix; UINT_PTR chOffset; BOOL fCanonical; cchPath = lstrlenA(pcszPath) + 1; pcszWidePath = (LPWSTR)_alloca(cchPath*SIZEOF(WCHAR)); if ( MultiByteToWideChar( CP_ACP, 0, pcszPath, cchPath, pcszWidePath, cchPath) == 0) { return FALSE; } fCanonical = GetCanonicalPathInfo( pcszWidePath, szWideCanonicalBuf, pdwOutFlags, szWideNetResourceNameBuf, &pszWideRootPathSuffix ); if ( fCanonical ) { if ( WideCharToMultiByte( CP_ACP, 0, szWideCanonicalBuf, -1, pszCanonicalBuf, MAX_PATH, NULL, NULL ) == 0) { return FALSE; } if ( *pdwOutFlags & GCPI_OFL_REMOTE ) { if ( WideCharToMultiByte( CP_ACP, 0, szWideNetResourceNameBuf, -1, pszNetResourceNameBuf, MAX_PATH, NULL, NULL ) == 0) { return FALSE; } } chOffset = pszWideRootPathSuffix - szWideCanonicalBuf; *ppszRootPathSuffix = pszCanonicalBuf; while ( chOffset-- ) { *ppszRootPathSuffix = CharNextA(*ppszRootPathSuffix); } } return(fCanonical); } #endif