windows-nt/Source/XPSP1/NT/com/svcdlls/trksvcs/common/sharenum.cxx
2020-09-26 16:20:57 +08:00

861 lines
26 KiB
C++

// Copyright (c) 1996-2002 Microsoft Corporation
//+-------------------------------------------------------------------------
//
// Microsoft Windows
//
// File: sharenum.cxx
//
// Contents: The CShareEnumerator class implementation.
//
// Classes: CShareEnumerator
//
// History: 28-Jan-98 MikeHill Created
//
// Notes:
//
//--------------------------------------------------------------------------
#include "pch.cxx"
#pragma hdrstop
#include "trklib.hxx"
//+-------------------------------------------------------------------
//
// Function: CShareEnumerator::Initialize
//
// Synopsis: Initializes this enumeration by calling
// NetShareEnum.
//
// Arguments: [IDL_handle] (in)
// The RPC binding handle to the client on whom's
// behalf we're acting.
//
// [ptszMachineName] (in)
// The machine on which shares are to be enumerated.
// If this value is NULL, the local machine is assumed.
//
//
// Returns: None.
//
// Raises: On error.
//
//--------------------------------------------------------------------
VOID
CShareEnumerator::Initialize( RPC_BINDING_HANDLE IDL_handle, const TCHAR *ptszMachineName )
{
NET_API_STATUS netstatus;
NETRESOURCE netresourceMachine;
DWORD dwTotalEntries;
TrkAssert( !_fInitialized );
// Start with a clean state
_ClearCache();
_iCurrentEntry = static_cast<ULONG>(-1);
_fInitialized = TRUE;
_IDL_handle = IDL_handle;
// Keep the machine name, retrieving it if necessary.
_tcscpy( _tszMachineName, TEXT("\\\\") );
if( NULL != ptszMachineName )
_tcscpy( &_tszMachineName[2], ptszMachineName );
else
{
DWORD cbMachineName = sizeof(_tszMachineName) - 2;
if( !GetComputerName( &_tszMachineName[2], &cbMachineName ))
TrkRaiseLastError();
}
// Start the enumeration. We'll simply get all the share information
// at once, instead of using a resume handle and making repeated RPC
// calls to the Server service.
netstatus = NetShareEnum( (TCHAR*) ptszMachineName, // Server (we must de-const it)
502, // Info level
(LPBYTE*) &_prgshare_info, // Return buffer
MAX_PREFERRED_LENGTH, // Get everything
&_cEntries, // Entries read
&dwTotalEntries, // Total entries avail
NULL ); // Resume handle
if( STATUS_SUCCESS != netstatus )
TrkRaiseWin32Error( HRESULT_FROM_WIN32(netstatus) );
TrkAssert( _cEntries == dwTotalEntries );
return;
}
//+-------------------------------------------------------------------
//
// Function: CShareEnumerator::UnInitialize
//
// Synopsis: Free any resources.
//
// Arguments: None.
//
// Returns: None.
//
// Raises: No
//
//--------------------------------------------------------------------
VOID
CShareEnumerator::UnInitialize()
{
if( _fInitialized )
{
if( NULL != _prgshare_info )
NetApiBufferFree( _prgshare_info );
InitLocals();
}
CTrkRpcConfig::UnInitialize();
}
//+-------------------------------------------------------------------
//
// Function: CShareEnumerator::_ClearCache
//
// Synopsis: Clear the data members of the CShareEnumerator
// which are cached information about the current share.
// (This is done in preparation to move on to the next
// share in the enumeration.)
//
// Arguments: None.
//
// Returns: None.
//
// Raises: No
//
//--------------------------------------------------------------------
VOID
CShareEnumerator::_ClearCache()
{
// Clear the cached information about the
// current share.
_cchSharePath = (ULONG) -1;
_ulMerit = 0;
_enumHiddenState = HS_UNKNOWN;
}
//+-------------------------------------------------------------------
//
// Function: CShareEnumerator::Next
//
// Synopsis: Moves the enumerator on to the next share in the
// the enumeration.
//
// Arguments: None.
//
// Returns: TRUE if there was another element in the enumeration,
// FALSE if there are no more shares to enumerate.
//
// Raises: No.
//
//--------------------------------------------------------------------
BOOL
CShareEnumerator::Next()
{
TrkAssert( _fInitialized );
if( _iCurrentEntry + 1 < _cEntries )
{
_ClearCache();
_iCurrentEntry++;
return( TRUE );
}
else
return( FALSE );
}
//+-------------------------------------------------------------------
//
// Function: CShareEnumerator::CoversDrivePath
//
// Synopsis: Determines if the current share in the enumeration
// "covers" a given drive path. E.g., a share to
// "c:\docs" covers the drive path "c:\docs\mydoc.doc".
//
// Arguments: [ptszDrivePath]
// The drive-based path to check for coverage.
//
// Returns: TRUE if the current share covers the given path,
// FALSE otherwise.
//
// Raises: No
//
//--------------------------------------------------------------------
BOOL
CShareEnumerator::CoversDrivePath( const TCHAR *ptszDrivePath )
{
TrkAssert( _fInitialized );
TrkAssert( TEXT(':') == ptszDrivePath[1] );
// Does the current share path cover the file? It does if it compares
// successfully with the local path, up to the entire length of the
// share path.
if( _IsValidShare()
&&
QueryCCHSharePath() <= _tcslen(ptszDrivePath)
&&
!_tcsnicmp( GetSharePath(), ptszDrivePath, QueryCCHSharePath() )
)
return TRUE;
else
return FALSE;
}
//+-------------------------------------------------------------------
//
// Function: CShareEnumerator::_IsHiddenShare
//
// Synopsis: Determines if the current share is "hidden"
// (i.e., the share name ends in a '$').
//
// The first time this method is called, the share
// named is checked for hidden-ness. The result is
// cached for subsequent calls.
//
// Arguments: None.
//
// Returns: TRUE if the current share is hidden,
// FALSE if it is visible.
//
// Raises: On error.
//
//--------------------------------------------------------------------
BOOL
CShareEnumerator::_IsHiddenShare()
{
TrkAssert( _fInitialized );
TrkAssert( _IsValidShare() );
TrkAssert( NULL != GetShareName() );
TrkAssert( TEXT('\0') != GetShareName()[0] );
if( _enumHiddenState == HS_UNKNOWN )
{
_enumHiddenState = TEXT('$') == GetShareName()[ _tcslen(GetShareName()) - 1 ]
? HS_HIDDEN
: HS_VISIBLE;
}
return( HS_HIDDEN == _enumHiddenState );
}
//+-------------------------------------------------------------------
//
// Function: CShareEnumerator::_IsAdminShare
//
// Synopsis: Determines if the current share is Admin share.
// Admin shares are automatically created by the Server
// service during initialization. They're hard-coded
// to be A$, B$, C$, etc., for each of the drives,
// and ADMIN$ for the %windir% directory.
//
// Arguments: None.
//
// Returns: TRUE if the current share is an auto-generated admin share,
// FALSE otherwise.
//
//--------------------------------------------------------------------
BOOL
CShareEnumerator::_IsAdminShare()
{
TCHAR tcDriveLetter;
TrkAssert( _fInitialized );
TrkAssert( _IsValidShare() );
TrkAssert( NULL != GetShareName() );
TrkAssert( TEXT('\0') != GetShareName()[0] );
tcDriveLetter = TrkCharUpper( GetShareName()[0] );
// Check for the admin share characteristics.
if( 2 == _tcslen(GetShareName())
&&
TEXT('$') == GetShareName()[ 1 ]
&&
TEXT('A') <= tcDriveLetter
&&
TEXT('Z') >= tcDriveLetter
)
{
return( TRUE ); // It's a drive share
}
else if( !_tcsicmp( TEXT("admin$"), GetShareName() ))
return( TRUE ); // It's the %windir% share
else
return( FALSE );
}
//+----------------------------------------------------------------------------
//
// Method: _IsValidShare
//
// Synopsis: Returns True if the current share is valid. A share
// valid it if it is in the form "<drive letter>:\\". E.g.
// "IPC$" isn't a valid share.
//
// Arguments: None
//
// Returns: None
//
//+----------------------------------------------------------------------------
inline BOOL
CShareEnumerator::_IsValidShare()
{
TCHAR tcFirstChar;
// There should be a path, and it should be at least 3 characters
// (e.g. "D:\")
if( NULL == GetSharePath() || 3 > QueryCCHSharePath() )
return FALSE;
tcFirstChar = TrkCharUpper( GetSharePath()[0] );
// Make sure that the share path begins with "<Drive>:\\".
if( TEXT('A') <= tcFirstChar && tcFirstChar <= TEXT('Z')
&&
TEXT(':') == GetSharePath()[1]
&&
TEXT('\\') == GetSharePath()[2]
)
{
return( TRUE );
}
else
return( FALSE );
}
//+-------------------------------------------------------------------
//
// Function: CShareEnumerator::GetMerit
//
// Synopsis: Returns the linear (ULONG) merit of the current path.
// The greater this merit value, the more useful the share
// is. This is calculated on the first call to this method,
// and cached for use in subsequent calls.
//
// Arguments: None.
//
// Returns: None.
//
// Raises: On error.
//
//--------------------------------------------------------------------
ULONG
CShareEnumerator::GetMerit()
{
TrkAssert( _fInitialized );
// The merit of a share is the bitwise-OR of the share's
// enumAccessLevel, its hidden-ness, and the length of
// the covered path.
//
// Shorter paths are more mertious than longer paths
// (because they cover more of the volume), so we subtract
// the path length from the max-value; thus giving shorter
// paths more weight.
if( 0 == _ulMerit && _IsValidShare() )
{
_ulMerit = ( _GetAccessLevel() )
|
( SPC_MAX_COVERAGE - QueryCCHSharePath() )
|
( _IsHiddenShare() ? HS_HIDDEN : HS_VISIBLE );
}
TrkLog(( TRKDBG_MEND, TEXT("Score %d for %s (%s)"),
_ulMerit,
_prgshare_info[ _iCurrentEntry ].shi502_netname,
_prgshare_info[ _iCurrentEntry ].shi502_path ));
return( _ulMerit );
}
static void SDAllocHelper( BYTE **ppb, ULONG cbCurrent, ULONG cbRequired )
{
if( cbRequired <= cbCurrent )
return;
*ppb = new BYTE[ cbRequired ];
if( NULL == *ppb )
{
TrkLog((TRKDBG_ERROR, TEXT("Failed alloc in SDAllocHelper")));
TrkRaiseWin32Error( ERROR_NOT_ENOUGH_MEMORY );
}
return;
}
void
CShareEnumerator::_AbsoluteSDHelper( const PSECURITY_DESCRIPTOR pSDRelative,
PSECURITY_DESCRIPTOR *ppSDAbs, ULONG *pcbSDAbs,
PACL *ppDaclAbs, ULONG *pcbDaclAbs,
PACL *ppSaclAbs, ULONG *pcbSaclAbs,
PSID *ppSidOwnerAbs, ULONG *pcbSidOwnerAbs,
PSID *ppSidGroupAbs, ULONG *pcbSidGroupAbs )
{
ULONG cbSDAbs = *pcbSDAbs;
ULONG cbDaclAbs = *pcbDaclAbs;
ULONG cbSaclAbs = *pcbSaclAbs;
ULONG cbSidOwnerAbs = *pcbSidOwnerAbs;
ULONG cbSidGroupAbs = *pcbSidGroupAbs;
for( int i = 0; i < 2; i++ )
{
if( !MakeAbsoluteSD( pSDRelative,
*ppSDAbs, pcbSDAbs,
*ppDaclAbs, pcbDaclAbs,
*ppSaclAbs, pcbSaclAbs,
*ppSidOwnerAbs, pcbSidOwnerAbs,
*ppSidGroupAbs, pcbSidGroupAbs ))
{
if( i > 0 || ERROR_INSUFFICIENT_BUFFER != GetLastError() )
{
TrkLog((TRKDBG_ERROR, TEXT("Couldn't make absolute SD")));
TrkRaiseLastError( );
}
TrkLog(( TRKDBG_MEND, TEXT("Realloc _AbsoluteSDHelper") ));
SDAllocHelper( (BYTE**) ppSDAbs, cbSDAbs, *pcbSDAbs );
SDAllocHelper( (BYTE**) ppDaclAbs, cbDaclAbs, *pcbDaclAbs );
SDAllocHelper( (BYTE**) ppSaclAbs, cbSaclAbs, *pcbSaclAbs );
SDAllocHelper( (BYTE**) ppSidOwnerAbs, cbSidOwnerAbs, *pcbSidOwnerAbs );
SDAllocHelper( (BYTE**) ppSidGroupAbs, cbSidGroupAbs, *pcbSidGroupAbs );
}
}
}
//+-------------------------------------------------------------------
//
// Function: CShareEnumerator::_GetAccessLevel
//
// Synopsis: Determines the "access level" of the current share.
// The definition of an access level is provided by
// the enumAccessLevels enumeration in PShareMerit.
//
// Once calculated, this access level is not cached for
// subsequent calls, because this method is only called
// by GetMerit, which provides its own cacheing.
//
// *** Note: This is a temporary solution. This should
// be replaced with a solution that simply checks security
// on the share, without actually attempting to open the
// file.
//
// Arguments: None.
//
// Returns: An access level in the form of an enumAccessLevels.
//
// Raises: On error.
//
//--------------------------------------------------------------------
PShareMerit::enumAccessLevels
CShareEnumerator::_GetAccessLevel()
{
enumAccessLevels AccessLevel = AL_NO_ACCESS;
static const int StackBufferSizes = 256;
TCHAR tszUNCPath[ MAX_PATH + 1 ];
HANDLE hFile = INVALID_HANDLE_VALUE;
int iAttempt;
DWORD rgAccess[] = { GENERIC_READ | GENERIC_WRITE, // => AL_READWRITE_ACCESS
GENERIC_READ, // => AL_READ_ACCESS
GENERIC_WRITE }; // => AL_WRITE_ACCESS
HANDLE hAccessToken;
BOOL fAccessToken = FALSE;
RPC_STATUS rpc_status;
DWORD dwStatus;
DWORD cbActual;
BOOL fImpersonating = FALSE;
BYTE rgbTokenUser[StackBufferSizes];
ULONG cbTokenUser = sizeof(rgbTokenUser);
TOKEN_USER *pTokenUser = (TOKEN_USER*) rgbTokenUser;
BYTE rgbTokenGroups[ 4 * StackBufferSizes ];
ULONG cbTokenGroups = sizeof(rgbTokenGroups);
TOKEN_GROUPS *pTokenGroups = (TOKEN_GROUPS*) rgbTokenGroups;
BYTE rgbSDAbs[ StackBufferSizes ];
ULONG cbSDAbs = sizeof(rgbSDAbs);
PSECURITY_DESCRIPTOR pSDAbs = (PSECURITY_DESCRIPTOR) rgbSDAbs;
BYTE rgbDaclAbs[ StackBufferSizes ];
ULONG cbDaclAbs = sizeof(rgbDaclAbs);
PACL pDaclAbs = (PACL) rgbDaclAbs;
BYTE rgbSaclAbs[ StackBufferSizes ];
ULONG cbSaclAbs = sizeof(rgbSaclAbs);
PACL pSaclAbs = (PACL) rgbSaclAbs;
BYTE rgbSidOwnerAbs[ StackBufferSizes ];
ULONG cbSidOwnerAbs = sizeof(rgbSidOwnerAbs);
PSID pSidOwnerAbs = (PSID) rgbSidOwnerAbs;
BYTE rgbSidGroupAbs[ StackBufferSizes ];
ULONG cbSidGroupAbs = sizeof(rgbSidGroupAbs);
PSID pSidGroupAbs = (PSID) rgbSidGroupAbs;
GENERIC_MAPPING Generic_Mapping = { FILE_GENERIC_READ, FILE_GENERIC_WRITE, FILE_GENERIC_EXECUTE, FILE_ALL_ACCESS };
PRIVILEGE_SET rgPrivilegeSet[ 10 ];
ULONG cbPrivilegeSet = sizeof(rgPrivilegeSet);
PRIVILEGE_SET *pPrivilegeSet = rgPrivilegeSet;
DWORD dwGrantedAccess;
BOOL fAccessStatus;
CSID csidAdministrators;
CSecDescriptor csdAdministrators;
PSECURITY_DESCRIPTOR psdCheck = NULL;
// If there is no security descriptor and this isn't an admin share,
// it means that Everyone has 'full control'.
if( NULL == _prgshare_info[ _iCurrentEntry ].shi502_security_descriptor && !_IsAdminShare() )
return( AL_FULL_ACCESS );
// Otherwise, we'll look at the share's DACL ...
__try
{
// Impersonate the client
TrkAssert( NULL != _IDL_handle );
if( RpcSecurityEnabled() )
{
rpc_status = RpcImpersonateClient( _IDL_handle );
if( S_OK != rpc_status )
{
TrkLog((TRKDBG_ERROR, TEXT("Couldn't impersonate client")));
TrkRaiseWin32Error( rpc_status );
}
fImpersonating = TRUE;
}
else
{
if( !ImpersonateSelf( SecurityImpersonation ) )
{
TrkLog((TRKDBG_ERROR, TEXT("Couldn't impersonate self")));
TrkRaiseLastError( );
}
fImpersonating = TRUE;
}
// Get the client's access token
if( !OpenThreadToken( GetCurrentThread(),
TOKEN_READ, //TOKEN_ALL_ACCESS,
TRUE, // Open as self
&hAccessToken ))
{
TrkLog((TRKDBG_ERROR, TEXT("Failed OpenThreadToken")));
TrkRaiseLastError( );
}
fAccessToken = TRUE;
// Get the client's owner SID
for( int i = 0; i < 2; i++ )
{
if( !GetTokenInformation( hAccessToken,
TokenUser,
(LPVOID) pTokenUser,
cbTokenUser,
&cbActual ))
{
if( i > 0 || ERROR_INSUFFICIENT_BUFFER != GetLastError() )
{
TrkLog((TRKDBG_ERROR, TEXT("Failed GetTokenInformation (TokenUser)")));
TrkRaiseLastError( );
}
TrkLog(( TRKDBG_MEND, TEXT("Realloc pTokenUser") ));
cbTokenUser = cbActual;
pTokenUser = (TOKEN_USER*) new BYTE[ cbTokenUser ];
if( NULL == pTokenUser )
TrkRaiseWin32Error( ERROR_NOT_ENOUGH_MEMORY );
}
}
// Get the client's group SID
for( i = 0; i < 2; i++ )
{
if( !GetTokenInformation( hAccessToken,
TokenGroups,
(LPVOID) pTokenGroups,
cbTokenGroups,
&cbActual ))
{
if( i > 0 || ERROR_INSUFFICIENT_BUFFER != GetLastError() )
{
TrkLog((TRKDBG_ERROR, TEXT("Failed GetTokenInformation (TokenGroups)")));
TrkRaiseLastError( );
}
TrkLog(( TRKDBG_MEND, TEXT("Realloc pTokenGroups") ));
cbTokenGroups = cbActual;
pTokenGroups = (TOKEN_GROUPS*) new BYTE[ cbTokenGroups ];
if( NULL == pTokenGroups )
TrkRaiseWin32Error( ERROR_NOT_ENOUGH_MEMORY );
}
}
// Get a pointer to the security descriptor we want to check against.
if( _IsAdminShare() )
{
// For admin shares, we don't get a security descriptor in _prgshare_info
// from the NetShareEnum call. But, we know what the ACLs on admin shares
// should be, so we'll craft up an SD.
csidAdministrators.Initialize( CSID::CSID_NT_AUTHORITY, SECURITY_BUILTIN_DOMAIN_RID,
DOMAIN_ALIAS_RID_ADMINS );
csdAdministrators.Initialize();
csdAdministrators.AddAce( CSecDescriptor::ACL_IS_DACL, CSecDescriptor::AT_ACCESS_ALLOWED,
FILE_ALL_ACCESS, csidAdministrators );
psdCheck = csdAdministrators;
}
else
{
// Convert the share's Security Descriptor into absolute form.
_AbsoluteSDHelper( _prgshare_info[ _iCurrentEntry ].shi502_security_descriptor,
&pSDAbs, &cbSDAbs,
&pDaclAbs, &cbDaclAbs,
&pSaclAbs, &cbSaclAbs,
&pSidOwnerAbs, &cbSidOwnerAbs,
&pSidGroupAbs, &cbSidGroupAbs );
psdCheck = pSDAbs;
}
// The appropriate security descriptor is now in 'psdCheck'
// Put the client's owner SID in the Security Descriptor.
if( !SetSecurityDescriptorOwner( psdCheck,
pTokenUser->User.Sid,
FALSE ))
{
TrkLog((TRKDBG_ERROR, TEXT("Couldn't add user to SD")));
TrkRaiseLastError( );
}
// Put the client's group SID in the Security Descriptor.
// (Perf) Why? I think it's because GetEffectiveRightsFromAcl wasn't working,
// so we had to use AccessCheck. But to use that call I think we had to pass
// in an SD with an owner/group, and the one returned from shi502_security_descriptor
// didn't have them.
if( !SetSecurityDescriptorGroup( psdCheck,
pTokenGroups->Groups->Sid,
FALSE ))
{
TrkLog((TRKDBG_ERROR, TEXT("Couldn't add group to SD")));
TrkRaiseLastError( );
}
// We have to stop impersonating in order to make the AccessCheck call.
fImpersonating = FALSE;
if( RpcSecurityEnabled() )
RpcRevertToSelf();
else
RevertToSelf();
// Check the access that this user has to this share. If this returns
// false, it means that NtAccessCheck returned an error. If this returns
// true, but fAccessStatus is false, it means that NtAccessCheck succeeded,
// but it returned an error in its RealStatus parameter. In either case,
// we need to check GetLastError.
for( i = 0; i < 2; i++ )
{
if( !AccessCheck( psdCheck,
hAccessToken, MAXIMUM_ALLOWED, &Generic_Mapping,
pPrivilegeSet, &cbPrivilegeSet,
&dwGrantedAccess, &fAccessStatus )
||
!fAccessStatus )
{
if( i > 0 || ERROR_INSUFFICIENT_BUFFER != GetLastError() )
{
TrkLog((TRKDBG_ERROR, TEXT("Couldn't perform AccessCheck for %s (%08x)"),
GetShareName(), HRESULT_FROM_WIN32(GetLastError()) ));
TrkRaiseLastError( );
}
TrkLog(( TRKDBG_MEND, TEXT("Realloc pPrivilegeSet") ));
pPrivilegeSet = (PRIVILEGE_SET*) new BYTE[ cbPrivilegeSet ];
if( NULL == pPrivilegeSet )
{
TrkLog(( TRKDBG_ERROR, TEXT("Couldn't alloc pPrivilegeSet") ));
TrkRaiseWin32Error( ERROR_NOT_ENOUGH_MEMORY );
}
}
}
// Reduce this complete list of accesses to a synopsis
if( AreAllAccessesGranted( dwGrantedAccess, FILE_ALL_ACCESS ))
AccessLevel = AL_FULL_ACCESS;
else if( AreAllAccessesGranted( dwGrantedAccess, FILE_GENERIC_READ | FILE_GENERIC_WRITE ))
AccessLevel = AL_READ_WRITE_ACCESS;
else if( AreAllAccessesGranted( dwGrantedAccess, FILE_GENERIC_READ ))
AccessLevel = AL_READ_ACCESS;
else if( AreAllAccessesGranted( dwGrantedAccess, FILE_GENERIC_WRITE ))
AccessLevel = AL_WRITE_ACCESS;
else
AccessLevel = AL_NO_ACCESS;
}
__finally
{
csdAdministrators.UnInitialize();
csidAdministrators.UnInitialize();
if( fAccessToken )
CloseHandle( hAccessToken );
if( fImpersonating )
{
if( RpcSecurityEnabled() )
RpcRevertToSelf();
else
RevertToSelf();
}
if( rgPrivilegeSet != pPrivilegeSet )
delete[] pPrivilegeSet;
if( rgbTokenUser != (BYTE*) pTokenUser )
delete[] pTokenUser;
if( rgbTokenGroups != (BYTE*) pTokenGroups )
delete[] pTokenGroups;
if( rgbSDAbs != (BYTE*) pSDAbs )
delete[] pSDAbs;
if( rgbDaclAbs != (BYTE*) pDaclAbs )
delete[] pDaclAbs;
if( rgbSaclAbs != (BYTE*) pSaclAbs )
delete[] pSaclAbs;
if( rgbSidOwnerAbs != (BYTE*) pSidOwnerAbs )
delete[] pSidOwnerAbs;
if( rgbSidGroupAbs != (BYTE*) pSidGroupAbs )
delete[] pSidGroupAbs;
}
return( AccessLevel );
}
//+-------------------------------------------------------------------
//
// Function: CShareEnumerator::GenerateUNCPath
//
// Synopsis: Generate a UNC path to the give drive-based path
// WRT to the current share.
//
// Arguments: [ptszUNCPath] (out)
// Filled with the generated UNC path. This
// is assumed to be at least MAX_PATH+1 characters.
//
// [ptszDrivePath] (in)
// The drive-based path to the file.
// E.g. "c:\my documents\wordfile.doc".
//
// Returns: FALSE if the current share doesn't cover the
// file, TRUE otherwise.
//
// Raises: On error.
//
//--------------------------------------------------------------------
BOOL
CShareEnumerator::GenerateUNCPath( TCHAR *ptszUNCPath, const TCHAR * ptszDrivePath )
{
TrkAssert( _fInitialized );
TrkAssert( TEXT(':') == ptszDrivePath[1] );
// Ensure that this share covers the drive-based path.
if( !CoversDrivePath( ptszDrivePath ))
return( FALSE );
// Start out the UNC name with the \\machine\share.
_tcscpy( ptszUNCPath, _tszMachineName );
_tcscat( ptszUNCPath, TEXT("\\") );
_tcscat( ptszUNCPath, GetShareName() );
_tcscat( ptszUNCPath, TEXT("\\") );
// Finish the UNC name with the portion of the
// volume path which is under the share path.
_tcscat( ptszUNCPath, &ptszDrivePath[ QueryCCHSharePath() ] );
return( TRUE );
}