windows-nt/Source/XPSP1/NT/shell/ext/brfcase/filesync/linkinfo/canon.c
2020-09-26 16:20:57 +08:00

462 lines
13 KiB
C

/*
* 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 <t GETCANONICALPATHINFOOUTFLAGS> 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