windows-nt/Source/XPSP1/NT/inetsrv/query/h/funypath.hxx
2020-09-26 16:20:57 +08:00

819 lines
21 KiB
C++

//+---------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1998 - 2000.
//
// File: FunyPath.hxx
//
// Contents: Implementation of ``Funny'' Path for long pathnames
//
// Classes: CFunnyPath
// CLowerFunnyPath
// CLowerFunnyStack
//
// Notes: This class takes in a fully qualified path. The path can be in
// two forms - C:\dir\... or \\machine\share\...
// EXCEPTION: To accomodate for scope restrictions, this class
// will also allow paths of \dir... format
// This path is converted to the "funny" form. The normal path
// takes the form of \\?\C:\dir\... and remote path takes the form
// of \\?\UNC\machine\share\... These funny paths can used by in
// functions like CreateFile to open paths > MAX_PATH
//
// History: 08-May-98 vikasman Created
//
//----------------------------------------------------------------------------
#pragma once
#include "lcase.hxx"
const WCHAR _FUNNY_PATH[] = L"\\\\?\\";
const WCHAR _UNC_FUNNY_PATH[] = L"\\\\?\\UN";
const _FUNNY_PATH_LENGTH = ( ( sizeof( _FUNNY_PATH ) / sizeof( WCHAR ) ) - 1 );
const _UNC_FUNNY_PATH_LENGTH = ( ( sizeof( _UNC_FUNNY_PATH ) / sizeof( WCHAR ) ) - 1 );
//+---------------------------------------------------------------------------
//
// Class: CFunnyPath
//
// Purpose: A path name preceeded by "\\?\" to allow file operations
// when the length of the path is > MAX_PATH.
//
// History: 08-May-98 vikasman Created
//
//----------------------------------------------------------------------------
class CFunnyPath
{
public:
// used by SetState function to set the state to funny/actual
enum PathState
{
FUNNY_PATH_STATE,
ACTUAL_PATH_STATE
};
//
// Constructors, Destructors, Operators...
//
CFunnyPath( BOOL fRemote = FALSE ) :
_fRemote( fRemote ),
_ccActualBuf( 0 )
{
if ( _fRemote )
{
_xBuf.SetSize( _UNC_FUNNY_PATH_LENGTH + 1 );
_xBuf[_UNC_FUNNY_PATH_LENGTH] = 0;
}
else
{
_xBuf.SetSize( _FUNNY_PATH_LENGTH + 1 );
_xBuf[_FUNNY_PATH_LENGTH] = 0;
}
}
CFunnyPath( const WCHAR * wcsPath, unsigned cc = 0 ) :
_fRemote( FALSE ),
_ccActualBuf( 0 )
{
SetPath( wcsPath, cc );
}
CFunnyPath( CFunnyPath const & src )
{
*this = src;
}
CFunnyPath & operator =( CFunnyPath const & src )
{
_xBuf = src._xBuf;
_fRemote = src._fRemote;
_ccActualBuf = src._ccActualBuf;
return *this;
}
//
// Sets the path "wcsPath"
//
virtual void SetPath( const WCHAR * wcsPath, unsigned cc = 0 )
{
Win4Assert( wcsPath );
_ccActualBuf = ( 0 == cc ? wcslen( wcsPath ) : cc ) ;
if ( 0 == _ccActualBuf )
{
InitBlank();
return;
}
Win4Assert( _ccActualBuf > 1 ); // at least 2 characters
WCHAR * pwcActualPath;
BOOL fInvalidPath = FALSE;
if ( L'\\' != wcsPath[0] && L':' != wcsPath[1] )
// Looks like we don't have a valid path, but still
// we will continue. It's not CFunnyPath's problem
// We will treat it as a normal path
{
ciDebugOut(( DEB_ITRACE, "CFunnyPath::SetPath - The path (%ws) is not valid.\n", wcsPath ));
fInvalidPath = TRUE;
}
if ( L':' == wcsPath[1] || fInvalidPath )
{
// normal or invalid path
_fRemote = FALSE;
_xBuf.SetSize( _ccActualBuf + _FUNNY_PATH_LENGTH + 1 );
_xBuf.SetBuf( _FUNNY_PATH, _FUNNY_PATH_LENGTH );
pwcActualPath = &_xBuf[_FUNNY_PATH_LENGTH];
}
else
{
// remote path
_fRemote = TRUE;
_xBuf.SetSize( _ccActualBuf + _UNC_FUNNY_PATH_LENGTH + 1 );
_xBuf.SetBuf( _UNC_FUNNY_PATH, _UNC_FUNNY_PATH_LENGTH );
pwcActualPath = &_xBuf[_UNC_FUNNY_PATH_LENGTH];
}
RtlCopyMemory( pwcActualPath, wcsPath, _ccActualBuf * sizeof( WCHAR ) );
pwcActualPath[_ccActualBuf] = 0;
}
//
// Appends the path "wcsPath"
//
virtual void AppendPath( const WCHAR * wcsPath, unsigned cc = 0 )
{
AssertValid();
Win4Assert( wcsPath );
if ( 0 == cc )
{
cc = wcslen( wcsPath );
}
unsigned ccTotalSizeNeeded = cc + _ccActualBuf + 1;
WCHAR * pwcAppendAt;
if ( _fRemote )
{
_xBuf.SetSize( ccTotalSizeNeeded + _UNC_FUNNY_PATH_LENGTH );
pwcAppendAt = &_xBuf[_UNC_FUNNY_PATH_LENGTH + _ccActualBuf];
}
else
{
_xBuf.SetSize( ccTotalSizeNeeded + _FUNNY_PATH_LENGTH );
pwcAppendAt = &_xBuf[_FUNNY_PATH_LENGTH + _ccActualBuf];
}
RtlCopyMemory( pwcAppendAt, wcsPath, cc * sizeof( WCHAR ) );
pwcAppendAt[cc] = 0;
_ccActualBuf += cc;
}
//
// Returns the path either as funny or unc_funny which can be used in
// functions like CreateFile. Do not keep and use the pointer from
// GetPath after calling GetActualPath.
// Reason: Both these functions(even though they are const) may
// modify the internal buffer before returning data
//
const WCHAR * GetPath() const
{
AssertValid();
if ( _fRemote )
{
// need to replace \ with C
((CFunnyPath*)this)->_xBuf[_UNC_FUNNY_PATH_LENGTH] = L'C';
}
return _xBuf.Get();
}
//
// Returns the "actual path". Do not keep and use the pointer from
// GetActualPath after calling GetPath. Reason: Both these functions
// (even though they are const) may modify the internal buffer before
// returning data
//
const WCHAR * GetActualPath() const
{
return _GetActualPath();
}
//
// Sets the state of the path to actual/funny
//
void SetState( const PathState state )
{
if ( _fRemote )
{
_xBuf[_UNC_FUNNY_PATH_LENGTH] =
( FUNNY_PATH_STATE == state ? L'C' : L'\\' );
}
}
//
// Returns the actual length of characters
//
unsigned GetActualLength() const
{
return _ccActualBuf;
}
//
// Returns the total length
//
unsigned GetLength() const
{
return ( _ccActualBuf +
(_fRemote ? _UNC_FUNNY_PATH_LENGTH : _FUNNY_PATH_LENGTH) );
}
//
// Appends back slash
//
BOOL AppendBackSlash()
{
WCHAR * pwcsActualPath = _GetActualPath();
if ( 0 == _ccActualBuf || L'\\' == pwcsActualPath[_ccActualBuf - 1] )
{
return FALSE;
}
AppendPath( L"\\", 1 );
return TRUE;
}
//
// Removes back slash
//
BOOL RemoveBackSlash()
{
WCHAR * pwcsActualPath = _GetActualPath();
if ( 0 == _ccActualBuf || L'\\' != pwcsActualPath[_ccActualBuf - 1] )
{
return FALSE;
}
pwcsActualPath[--_ccActualBuf] = 0;
return TRUE;
}
//
// Truncates the buf to ccNewLength (this is the "actual char.", excluding
// funny path prefix, length)
//
void Truncate( unsigned ccNewLength )
{
if ( ccNewLength < _ccActualBuf )
{
WCHAR * pwcActualPath = _GetActualPath();
pwcActualPath[ _ccActualBuf = ccNewLength ] = 0;
}
}
BOOL IsRemote () const
{
return _fRemote;
}
//
// returns TRUE if any component in the path looks like the short version
// of a long file name.
//
BOOL IsShortPath () const;
static BOOL IsShortName( WCHAR const * const pwszName );
//
// Returns the count of all the characters in xBuf
//
unsigned Count() const
{
return _xBuf.Count();
}
//
// Set the size of _xBuf
//
void SetSize( unsigned cc )
{
_xBuf.SetSize( cc );
}
//
// Get the internal buffer
//
const WCHAR * GetBuffer() const
{
return _xBuf.Get();
}
//
// This is a dangerous function as it exposes the internal buffer
//
WCHAR * GetBuffer()
{
return _xBuf.Get();
}
// This path initializes the internal buffer with empty actual path
void InitBlank( BOOL fRemote = FALSE )
{
_fRemote = fRemote;
if ( _fRemote )
{
_xBuf.SetBuf( _UNC_FUNNY_PATH, _UNC_FUNNY_PATH_LENGTH + 1 );
}
else
{
_xBuf.SetBuf( _FUNNY_PATH, _FUNNY_PATH_LENGTH + 1 );
}
_ccActualBuf = 0;
}
#if CIDBG==1
//
// Asserts that we are in good state
//
void AssertValid() const
{
const WCHAR * pwcsFunny = _xBuf.Get();
Win4Assert( pwcsFunny &&
L'\\' == pwcsFunny[0] && L'\\' == pwcsFunny[1] &&
L'?' == pwcsFunny[2] && L'\\' == pwcsFunny[3] );
Win4Assert( L':' == pwcsFunny[5] || L'N' == pwcsFunny[5] ||
L'n' == pwcsFunny[5] );
}
#else
void AssertValid() const {}
#endif
//
// Some static utility functions
//
enum FunnyUNC
{
NOT_FUNNY, // not funny
FUNNY_ONLY, // of type \\?\c:\dir\...
FUNNY_UNC // of type \\?\unc\...
};
static inline BOOL IsFunnyPath( WCHAR const * pwcsPath )
{
return ( pwcsPath &&
L'\\' == pwcsPath[0] &&
L'\\' == pwcsPath[1] &&
L'?' == pwcsPath[2] &&
L'\\' == pwcsPath[3] );
}
static inline FunnyUNC IsFunnyUNCPath( WCHAR const * pwcsPath )
{
// The 6th char here can also be as '\\' (instead of being c/C) as
// this class modifies that charatcter to be a '\\'.
return ( FALSE == IsFunnyPath( pwcsPath ) ?
NOT_FUNNY :
( ( (L'U' == pwcsPath[4] || L'u' == pwcsPath[4]) &&
(L'N' == pwcsPath[5] || L'n' == pwcsPath[5]) &&
(L'C' == pwcsPath[6] || L'c' == pwcsPath[6] || L'\\' == pwcsPath[6]) &&
(L'\\' == pwcsPath[7] ) ) ?
FUNNY_UNC :
FUNNY_ONLY ) );
}
protected:
WCHAR * _GetActualPath() const
{
WCHAR * pwcsActualPath;
if ( _fRemote )
{
pwcsActualPath = &(((CFunnyPath*)this)->_xBuf[_UNC_FUNNY_PATH_LENGTH]);
// need to replace C with "\"
*pwcsActualPath = L'\\';
}
else
{
pwcsActualPath = &(((CFunnyPath*)this)->_xBuf[_FUNNY_PATH_LENGTH]);
}
return pwcsActualPath;
}
BOOL _fRemote; // Is the path remote ?
unsigned _ccActualBuf; // number of actual characters in _xBuf
// (excluding _FUNNY_PATH/_UNC_FUNNY_PATH)
XGrowable<WCHAR> _xBuf;
};
//+---------------------------------------------------------------------------
//
// Method: CFunnyPath::IsShortName, static public
//
// Arguments: [pwszName] -- name to be checked (only a single path component
// is checked at a time)
//
// Returns: TRUE if file is potentially a short (8.3) name for
// a file with a long name.
//
// History: 01-Sep-1998 AlanW Created
//
//----------------------------------------------------------------------------
inline BOOL CFunnyPath::IsShortName( WCHAR const * const pwszName )
{
//
// First, see if this is possibly a short name (has ~ in file name part).
//
BOOL fTwiddle = FALSE;
unsigned cDot = 0;
for ( unsigned cchFN = 0; cchFN < 13; cchFN++ )
{
if ( pwszName[cchFN] == L'~' && cchFN >= 1 )
fTwiddle = TRUE;
else if ( pwszName[cchFN] == L'.' )
{
if (cchFN == 0 || cchFN > 8)
return FALSE; // short names can't have '.' at beginning
cDot++;
}
else if ( pwszName[cchFN] == L'\0' || pwszName[cchFN] == L'\\' )
break;
cchFN++;
}
if (fTwiddle)
{
// short names can't have more than 1 '.'
// max filename size if no extension is 8
if (cDot >= 2 || cDot == 0 && cchFN > 8)
return FALSE;
// check for min (e.g., EXT~1 for .ext) and max lengths
if (cchFN >= 5 && cchFN <= 12)
return TRUE;
}
return FALSE;
}
//+---------------------------------------------------------------------------
//
// Method: CFunnyPath::IsShortPath, public
//
// Arguments: - none -
//
// Returns: TRUE if the path name potentially contains a short (8.3) name
// for a file with a long name.
//
// History: 15-Sep-1998 AlanW Created
//
//----------------------------------------------------------------------------
inline BOOL CFunnyPath::IsShortPath() const
{
//
// Check to see if the input path name contains an 8.3 short name
//
WCHAR * pwszTilde = wcschr( GetActualPath(), L'~' );
if (pwszTilde)
{
WCHAR * pwszComponent;
for ( pwszComponent = wcschr( GetActualPath(), L'\\' );
pwszComponent;
pwszComponent = wcschr( pwszComponent, L'\\' ) )
{
pwszComponent++;
pwszTilde = wcschr( pwszComponent, L'~' );
if ( 0 == pwszTilde || pwszTilde - pwszComponent > 13)
continue;
if (IsShortName( pwszComponent ))
return TRUE;
}
}
return FALSE;
}
//+---------------------------------------------------------------------------
//
// Class: CFunnyPath
//
// Purpose: A path name preceeded by "\\?\" to allow file operations when
// the length of the path is > MAX_PATH. Keeps the FunnyPath in
// lower case.
//
// History: 08-May-98 vikasman Created
//
//----------------------------------------------------------------------------
class CLowerFunnyPath : public CFunnyPath
{
public:
CLowerFunnyPath( BOOL fRemote = FALSE ) :
CFunnyPath( fRemote )
{
}
CLowerFunnyPath( const CLowerFunnyPath & src )
{
*this = src;
}
CLowerFunnyPath( const WCHAR * wcsPath,
unsigned cc = 0,
BOOL fNoConvert = FALSE ) :
CFunnyPath()
{
SetPath( wcsPath, cc, fNoConvert );
}
CLowerFunnyPath & operator =( CLowerFunnyPath const & src )
{
CFunnyPath::operator=( (CFunnyPath&)src );
return *this;
}
virtual void SetPath( const WCHAR * wcsPath,
unsigned cc = 0,
BOOL fNoConvert = FALSE );
virtual void AppendPath( const WCHAR * wcsPath,
unsigned cc = 0,
BOOL fNoConvert = FALSE );
BOOL ConvertToLongName();
private:
//
// Converts to wcsPath of length cc to lower case and puts it in
// _xBuf starting from ccStart index (actual)
//
void ToLower( const WCHAR * wcsPath, unsigned cc, unsigned ccStart = 0 );
};
inline void CLowerFunnyPath::SetPath(
const WCHAR * wcsPath,
unsigned cc,
BOOL fNoConvert )
{
if ( fNoConvert )
{
// already lower case
CFunnyPath::SetPath( wcsPath, cc );
AssertLowerCase( GetActualPath(), GetActualLength() );
return;
}
Win4Assert( wcsPath );
cc = ( 0 == cc ? wcslen( wcsPath ) : cc ) ;
if ( 0 == cc )
{
InitBlank();
return;
}
BOOL fInvalidPath = FALSE;
if ( L'\\' != wcsPath[0] && L':' != wcsPath[1] )
{
// Looks like we don't have a valid path, but still
// we will continue. It's not CFunnyPath's problem
// We will treat it as a normal path
ciDebugOut(( DEB_ITRACE,
"CLowerFunnyPath::SetPath - path (%ws) is not valid.\n",
wcsPath ));
fInvalidPath = TRUE;
}
if ( L':' == wcsPath[1] || fInvalidPath )
{
// normal or invalid path
_fRemote = FALSE;
_xBuf.SetBuf( _FUNNY_PATH, _FUNNY_PATH_LENGTH + 1 );
}
else
{
// remote path
_fRemote = TRUE;
_xBuf.SetBuf( _UNC_FUNNY_PATH, _UNC_FUNNY_PATH_LENGTH + 1 );
}
ToLower( wcsPath, cc );
}
inline void CLowerFunnyPath::AppendPath(
const WCHAR * wcsPath,
unsigned cc,
BOOL fNoConvert )
{
if ( fNoConvert )
{
// already lower case
CFunnyPath::AppendPath( wcsPath, cc );
AssertLowerCase( GetActualPath(), GetActualLength() );
return;
}
AssertValid();
Win4Assert( wcsPath );
if ( 0 == cc )
{
cc = wcslen( wcsPath );
}
ToLower( wcsPath, cc, _ccActualBuf );
}
inline void CLowerFunnyPath::ToLower(
const WCHAR * wcsPath,
unsigned cc,
unsigned ccStart )
{
WCHAR * pwcDestActualPath;
unsigned ccDestActualCount;
Win4Assert( wcsPath );
if ( !cc )
{
return;
}
#if CIDBG == 1
// Variable to try lowercasing again due to some random
// failures with LCMapStringW
BOOL fTryAgain = TRUE;
#endif
while ( TRUE )
{
if ( _fRemote )
{
pwcDestActualPath = &_xBuf[_UNC_FUNNY_PATH_LENGTH] + ccStart;
ccDestActualCount = _xBuf.Count() - (_UNC_FUNNY_PATH_LENGTH + ccStart);
}
else
{
pwcDestActualPath = &_xBuf[_FUNNY_PATH_LENGTH] + ccStart;
ccDestActualCount = _xBuf.Count() - (_FUNNY_PATH_LENGTH + ccStart);
}
if ( ccDestActualCount < 2 )
{
// Need to allocate more memory
_xBuf.SetSize( _xBuf.Count() * 2 );
continue;
}
unsigned cchLen = LCMapStringW ( LOCALE_NEUTRAL,
LCMAP_LOWERCASE,
wcsPath,
cc,
pwcDestActualPath,
ccDestActualCount - 1 );
if ( 0 == cchLen )
{
if ( GetLastError() == ERROR_INSUFFICIENT_BUFFER )
{
_xBuf.SetSize( _xBuf.Count() * 2 );
}
else
{
ciDebugOut(( DEB_ERROR, "Error 0x%x lowercasing path\n", GetLastError() ));
#if CIDBG == 1
if ( fTryAgain )
{
fTryAgain = FALSE;
Win4Assert(( !"neutral lowercase failed. Trying again..." ));
continue;
}
#endif
Win4Assert(( !"neutral lowercase failed" ));
THROW( CException() );
}
}
else
{
Win4Assert( cchLen < ccDestActualCount );
pwcDestActualPath[ cchLen ] = 0;
_ccActualBuf = ccStart + cchLen;
break;
}
}
}
//+---------------------------------------------------------------------------
//
// Member: CLowerFunnyPath::ConvertToLongName, public
//
// Synopsis: Converts file path name components to long names.
//
// Returns: TRUE if conversion was successful.
//
// Notes: GetLongPathNameW will fail with remote funny paths because
// it will try to call FindFirstFileW on the machine name and
// share name.
//
// History: 06-Jan-98 KyleP Created
//
//----------------------------------------------------------------------------
inline BOOL CLowerFunnyPath::ConvertToLongName()
{
XGrowable<WCHAR> wcsTemp;
WCHAR * pwcsShortName;
unsigned ccFunnyChars;
if (IsRemote())
{
pwcsShortName = _GetActualPath();
ccFunnyChars = 0;
}
else
{
pwcsShortName = (WCHAR *)GetPath();
ccFunnyChars = _FUNNY_PATH_LENGTH;
}
DWORD ccOut = GetLongPathNameW( pwcsShortName, // Short name
wcsTemp.Get(), // Long name
wcsTemp.Count() - 1 );
if ( ccOut > wcsTemp.Count() - 1 )
{
// Need to grow the buffer
wcsTemp.SetSize( ccOut + 1 );
ccOut = GetLongPathNameW( pwcsShortName, // Short name
wcsTemp.Get(), // Long name
wcsTemp.Count() - 1 );
Win4Assert( ccOut <= wcsTemp.Count() - 1 );
}
if ( 0 == ccOut )
{
ciDebugOut(( DEB_WARN, "GetLongPathName( %ws ) returned %d\n",
pwcsShortName, GetLastError() ));
return FALSE;
}
SetPath( wcsTemp.Get() + ccFunnyChars, ccOut - ccFunnyChars );
return TRUE;
}
//
// Stack class for FunnyPath
//
DECL_DYNSTACK( _CLowerFunnyStack, CLowerFunnyPath )
class CLowerFunnyStack : protected _CLowerFunnyStack
{
public:
BOOL Pop( XPtr<CLowerFunnyPath> & xLowerFunnyPath )
{
if (Count() == 0)
{
return FALSE;
}
xLowerFunnyPath.Set( _CLowerFunnyStack::Pop() );
return TRUE;
}
void Push( const CLowerFunnyPath & funnyElem )
{
XPtr<CLowerFunnyPath> xLowerFunnyPath( new CLowerFunnyPath(funnyElem) );
_CLowerFunnyStack::Push( xLowerFunnyPath.GetPointer() );
xLowerFunnyPath.Acquire();
}
};