Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

744 lines
23 KiB

//////////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 1996-2002 Microsoft Corporation
//
// Module Name:
// QuorumUtils.cpp
//
// Description:
// Utility functions for retrieving the root path from a cluster.
//
// Maintained By:
// George Potts (GPotts) 22-OCT-2001
//
//////////////////////////////////////////////////////////////////////////////
#include <windows.h>
#include <StrSafe.h>
#include "QuorumUtils.h"
/////////////////////////////////////////////////////////////////////////////
//++
//
// SplitRootPath
//
// Routine Description:
// Take the current quorum path (from GetClusterQuorumResource) and compare
// it to the device names returned from the resource. From this take the
// additional path from the quorum path and set that as our root path.
//
// It is expected that the IN buffers are at least of size _MAX_PATH.
//
// Arguments:
// hClusterIn Handle to the cluster.
//
// pszPartitionNameOut Partition name buffer to fill.
//
// pcchPartitionIn Max char count of buffer.
//
// pszRootPathOut Root path buffer to fill.
//
// pcchRootPathIn Max char count of buffer.
//
// Return Value:
// ERROR_SUCCESS on success.
//
// ERROR_MORE_DATA
// pcchPartitionInout and pcchRootPathInout will contain the
// minimum sizes needed for the buffers.
//
// Win32 Error code on failure.
//
//--
/////////////////////////////////////////////////////////////////////////////
DWORD SplitRootPath(
HCLUSTER hClusterIn
, WCHAR * pszPartitionNameOut
, DWORD * pcchPartitionInout
, WCHAR * pszRootPathOut
, DWORD * pcchRootPathInout
)
{
HRESOURCE hQuorumResource = NULL;
WCHAR * pszResourceName = NULL;
WCHAR * pszQuorumPath = NULL;
WCHAR * pszDeviceTemp = NULL;
WCHAR * pszTemp = NULL;
CLUSPROP_BUFFER_HELPER buf;
DWORD cbData;
DWORD cchDeviceName;
WCHAR * pszDevice;
DWORD dwVal;
DWORD sc;
PVOID pbDiskInfo = NULL;
DWORD cbDiskInfo = 0;
HRESULT hr;
//
// Validate parameters.
//
if ( hClusterIn == NULL ||
pszPartitionNameOut == NULL || pcchPartitionInout == NULL ||
pszRootPathOut == NULL || pcchRootPathInout == NULL )
{
sc = ERROR_INVALID_PARAMETER;
goto Cleanup;
}
//
// Get the info about the quorum resource.
//
sc = WrapGetClusterQuorumResource( hClusterIn, &pszResourceName, &pszQuorumPath, NULL );
if ( sc != ERROR_SUCCESS )
{
goto Cleanup;
}
//
// Open a handle to the quorum resource to interrogate it.
//
hQuorumResource = OpenClusterResource( hClusterIn, pszResourceName );
if ( sc != ERROR_SUCCESS )
{
goto Cleanup;
}
//
// Get the disk info from the resource.
//
sc = ScWrapClusterResourceControlGet(
hQuorumResource
, NULL
, CLUSCTL_RESOURCE_STORAGE_GET_DISK_INFO
, NULL
, 0
, &pbDiskInfo
, &cbDiskInfo
);
if ( sc != ERROR_SUCCESS )
{
goto Cleanup;
}
//
// Cycle through the buffer looking for the first partition.
//
buf.pb = (BYTE*)pbDiskInfo;
while (buf.pSyntax->dw != CLUSPROP_SYNTAX_ENDMARK)
{
// Calculate the size of the value.
cbData = sizeof(*buf.pValue) + ALIGN_CLUSPROP(buf.pValue->cbLength);
// Parse the value.
if (buf.pSyntax->dw == CLUSPROP_SYNTAX_PARTITION_INFO)
{
//
// A resource may have multiple partitions defined - make sure that ours matches the quorum path.
// For any partition that is an SMB share we have to be careful - the quorum path may differ from the device name
// by the first 8 characters - "\\" vs. "\\?\UNC\". If it's an SMB path do special parsing, otherwise compare
// the beginning of the quorum path against the full device name. The reason for this is
// that SetClusterQuorumResource will convert any given SMB path to a UNC path.
//
// Make it easier to follow.
pszDevice = buf.pPartitionInfoValue->szDeviceName;
if ( (wcslen( pszDevice ) >= 2) && (ClRtlStrNICmp( L"\\\\", pszDevice, 2 ) == 0 ) )
{
//
// We found an SMB/UNC match.
//
//
// SMB and UNC paths always lead off with two leading backslashes - remove these from the
// partition name since a compare of "\\<part>" and "\\?\UNC\<part>" will never match.
// Instead, we'll just search for "<part>" in the quorum path.
//
// Allocate a new buffer to copy the trimmed code to.
// You can use the same buffer for both params of TrimLeft and TrimRight.
pszDeviceTemp = (WCHAR *) LocalAlloc( LMEM_ZEROINIT, ( wcslen( pszDevice ) + 1 ) * sizeof( WCHAR ) );
if ( pszDeviceTemp == NULL )
{
sc = ERROR_OUTOFMEMORY;
goto Cleanup;
}
// This will remove all leading backslashes.
dwVal = TrimLeft( pszDevice, L"\\", pszDeviceTemp );
// It may end with a \ - remove this if present.
dwVal = TrimRight( pszDeviceTemp, L"\\", pszDeviceTemp );
// Find out if pszDeviceTemp is a substring of pszQuorumPath.
pszTemp = wcsstr( pszQuorumPath, pszDeviceTemp );
if ( pszTemp != NULL )
{
// We found a match, now find the offset of the root path.
pszTemp += wcslen( pszDeviceTemp );
// Make sure our buffers are big enough.
if ( wcslen( pszDevice ) >= *pcchPartitionInout )
{
sc = ERROR_MORE_DATA;
}
if ( wcslen( pszTemp ) >= *pcchRootPathInout )
{
sc = ERROR_MORE_DATA;
}
*pcchPartitionInout = static_cast< DWORD >( wcslen( pszDevice ) + 1 );
*pcchRootPathInout = static_cast< DWORD >( wcslen( pszTemp ) + 1 );
if ( sc != ERROR_SUCCESS )
{
goto Cleanup;
}
// Copy the partition and NULL terminate it.
hr = StringCchCopyW( pszPartitionNameOut, *pcchPartitionInout, pszDevice );
if ( FAILED( hr ) )
{
sc = HRESULT_CODE( hr );
break;
}
// Copy the root path and NULL terminate it.
hr = StringCchCopyW( pszRootPathOut, *pcchRootPathInout, pszTemp );
if ( FAILED( hr ) )
{
sc = HRESULT_CODE( hr );
break;
}
break;
} // if: pszDeviceTemp is a substring of pszQuorumPath
} // if: SMB or UNC path
else if ( ClRtlStrNICmp( pszQuorumPath, pszDevice, wcslen( pszDevice )) == 0 )
{
// We found a non-SMB match match - pszDevice is a substring of pszQuorumPath.
cchDeviceName = static_cast< DWORD >( wcslen( pszDevice ) );
if ( cchDeviceName >= *pcchPartitionInout )
{
sc = ERROR_MORE_DATA;
}
if ( wcslen( &(pszQuorumPath[cchDeviceName]) ) >= *pcchRootPathInout )
{
sc = ERROR_MORE_DATA;
}
*pcchPartitionInout = cchDeviceName + 1;
*pcchRootPathInout = static_cast< DWORD >( wcslen( &(pszQuorumPath[cchDeviceName]) ) + 1 );
if ( sc != ERROR_SUCCESS )
{
goto Cleanup;
}
hr = StringCchCopyW( pszPartitionNameOut, *pcchPartitionInout, pszDevice );
if ( FAILED( hr ) )
{
sc = HRESULT_CODE( hr );
break;
}
hr = StringCchCopyW( pszRootPathOut, *pcchRootPathInout, &(pszQuorumPath[cchDeviceName]) );
if ( FAILED( hr ) )
{
sc = HRESULT_CODE( hr );
break;
}
break;
} // if: same partition
} // if: partition info
// Advance the buffer pointer
buf.pb += cbData;
} // while: more values
//
// Something failed - we weren't able to find a partition. Default to the quorum path
// and a single backslash.
//
if ( wcslen( pszPartitionNameOut ) == 0 )
{
hr = StringCchCopyW( pszPartitionNameOut, *pcchPartitionInout, pszQuorumPath );
if ( FAILED( hr ) )
{
sc = HRESULT_CODE( hr );
goto Cleanup;
}
}
if ( wcslen( pszRootPathOut ) == 0 )
{
hr = StringCchCopyW( pszRootPathOut, *pcchRootPathInout, L"\\" );
if ( FAILED( hr ) )
{
sc = HRESULT_CODE( hr );
goto Cleanup;
}
}
Cleanup:
LocalFree( pszResourceName );
LocalFree( pszQuorumPath );
LocalFree( pbDiskInfo );
if ( hQuorumResource != NULL )
{
CloseClusterResource( hQuorumResource );
}
return sc;
} // *** SplitRootPath()
/////////////////////////////////////////////////////////////////////////////
//++
//
// ConstructQuorumPath
//
// Routine Description:
// Construct a quorum path to pass to SetClusterQuorumResource given
// the parsed root path. This function enumerates the resources
// partitions and the first one that it finds it takes the device name
// and appends the rootpath to it.
//
// Arguments:
// hResourceIn Resource that is going to become the quorum.
//
// pszRootPathIn Root path to append to one of the resource's partitions.
//
// pszQuorumPathOut Buffer to receive the constructed quorum path.
//
// pcchQuorumPathInout Count of characters in pszQuorumPathOut.
//
//
// Return Value:
// ERROR_SUCCESS on success.
// The number of characters written (including NULL) is in
// pcchQuorumPathInout.
//
// ERROR_MORE_DATA
// pszQuorumPathOut is too small. The necessary buffer size
// in characters (including NULL) is in pcchQuorumPathInout.
//
// Win32 Error code on failure.
//
//--
/////////////////////////////////////////////////////////////////////////////
DWORD ConstructQuorumPath(
HRESOURCE hResourceIn
, const WCHAR * pszRootPathIn
, WCHAR * pszQuorumPathOut
, DWORD * pcchQuorumPathInout
)
{
DWORD sc = ERROR_SUCCESS;
PVOID pbDiskInfo = NULL;
DWORD cbDiskInfo = 0;
DWORD cbData = 0;
WCHAR * pszDevice = NULL;
size_t cchNeeded = 0;
HRESULT hr;
CLUSPROP_BUFFER_HELPER buf;
//
// Check params.
//
if ( pszRootPathIn == NULL || pszQuorumPathOut == NULL || pcchQuorumPathInout == NULL )
{
sc = ERROR_INVALID_PARAMETER;
goto Cleanup;
}
//
// Get the disk info from the resource.
//
sc = ScWrapClusterResourceControlGet(
hResourceIn
, NULL
, CLUSCTL_RESOURCE_STORAGE_GET_DISK_INFO
, NULL
, 0
, &pbDiskInfo
, &cbDiskInfo
);
if ( sc != ERROR_SUCCESS )
{
goto Cleanup;
}
buf.pb = (BYTE*) pbDiskInfo;
while (buf.pSyntax->dw != CLUSPROP_SYNTAX_ENDMARK)
{
// Calculate the size of the value.
cbData = sizeof(*buf.pValue) + ALIGN_CLUSPROP(buf.pValue->cbLength);
//
// See if this property contains partition info. We grab the first partition.
//
if (buf.pSyntax->dw == CLUSPROP_SYNTAX_PARTITION_INFO)
{
pszDevice = buf.pPartitionInfoValue->szDeviceName;
//
// Calculate the size of the buffer that we need.
//
cchNeeded = wcslen( pszDevice ) + 1;
cchNeeded += wcslen( pszRootPathIn );
if ( pszDevice[ wcslen( pszDevice ) - 1 ] == L'\\' && pszRootPathIn[ 0 ] == L'\\' )
{
//
// We'd have two backslashes if we concatenated them. Prune one of them off.
//
// Decrement one for the removed backslash.
cchNeeded--;
if ( cchNeeded > *pcchQuorumPathInout )
{
sc = ERROR_MORE_DATA;
*pcchQuorumPathInout = static_cast< DWORD >( cchNeeded );
goto Cleanup;
}
//
// Construct the path.
//
hr = StringCchPrintfW( pszQuorumPathOut, *pcchQuorumPathInout, L"%ws%ws", pszDevice, &pszRootPathIn[ 1 ] );
if ( FAILED( hr ) )
{
sc = HRESULT_CODE( hr );
goto Cleanup;
}
} // if: concatenating would introduce double backslashes
else if( pszDevice[ wcslen( pszDevice ) - 1 ] != L'\\' && pszRootPathIn[ 0 ] != L'\\' )
{
//
// We need to insert a backslash between the concatenated strings.
//
// Increment by one for the added backslash.
cchNeeded++;
if ( cchNeeded > *pcchQuorumPathInout )
{
sc = ERROR_MORE_DATA;
*pcchQuorumPathInout = static_cast< DWORD >( cchNeeded );
goto Cleanup;
}
//
// Construct the path.
//
hr = StringCchPrintfW( pszQuorumPathOut, *pcchQuorumPathInout, L"%s\\%s", pszDevice, pszRootPathIn );
if ( FAILED( hr ) )
{
sc = HRESULT_CODE( hr );
goto Cleanup;
}
} // if: we need to introduce a backslash between the concatenation
else
{
//
// We're fine to just construct the path.
//
if ( cchNeeded > *pcchQuorumPathInout )
{
sc = ERROR_MORE_DATA;
*pcchQuorumPathInout = static_cast< DWORD >( cchNeeded );
goto Cleanup;
}
//
// Construct the path.
//
hr = StringCchPrintfW( pszQuorumPathOut, *pcchQuorumPathInout, L"%s%s", pszDevice, pszRootPathIn );
if ( FAILED( hr ) )
{
sc = HRESULT_CODE( hr );
goto Cleanup;
}
} // if: we can just concatenate the strings
//
// Return the number of bytes that we needed in the buffer.
//
*pcchQuorumPathInout = static_cast< DWORD >( cchNeeded );
break;
} // if: partition info
// Advance the buffer pointer
buf.pb += cbData;
} // while: more values
Cleanup:
LocalFree( pbDiskInfo );
return sc;
} //*** ConstructQuorumPath
/////////////////////////////////////////////////////////////////////////////
//++
//
// TrimLeft
//
// Routine Description:
// Trim all leading whitespace as well as any leading characters specified.
//
// Arguments:
// pszTargetIn String to trim characters from.
//
// pszCharsIn List of characters to remove in addition to white space.
//
// pszTrimmedOut Target buffer in which the trimmed string will be
// placed. This may be the same buffer as pszTargetIn.
// This buffer is expected to be at least the size of
// pszTargetIn (in case no characters are removed).
//
// Return Value:
// The count of characters trimmed.
//
// -1. Call GetLastError for more information.
//
//--
/////////////////////////////////////////////////////////////////////////////
DWORD TrimLeft(
const WCHAR * pszTargetIn
, const WCHAR * pszCharsIn
, WCHAR * pszTrimmedOut
)
{
const WCHAR * pszTargetPtr = pszTargetIn;
const WCHAR * pszTemp = NULL;
BOOL fContinue;
DWORD sc = ERROR_SUCCESS;
DWORD cchTrimmed = 0; // Number of characters trimmed.
if ( pszTargetIn == NULL || pszTrimmedOut == NULL )
{
sc = ERROR_INVALID_PARAMETER;
goto Cleanup;
}
//
// Loop until we find non-whitespace or a char not in pszCharsIn or
// we've reached the end of the string.
//
fContinue = TRUE;
while ( *pszTargetPtr != L'\0' && fContinue == TRUE )
{
fContinue = FALSE;
//
// Is the character white space?
//
if ( 0 == iswspace( pszTargetPtr[0] ) )
{
//
// No, it's not. Does it match CharsIn?
//
for( pszTemp = pszCharsIn; pszTemp != NULL && *pszTemp != L'\0'; pszTemp++ )
{
if ( pszTargetPtr[ 0 ] == pszTemp[ 0 ] )
{
//
// We've got a match - trim it and loop on the next character.
//
fContinue = TRUE;
cchTrimmed++;
pszTargetPtr++;
break;
} // if:
} // for:
} // if:
else
{
//
// We've got some whitespace - trim it.
//
fContinue = TRUE;
cchTrimmed++;
pszTargetPtr++;
} // else:
} // while:
//
// Copy the truncated string to the pszTrimmedOut buffer.
// If we truncated everything from the string make sure
// we NULL terminate the string.
//
if ( wcslen( pszTargetPtr ) == 0 )
{
*pszTrimmedOut = L'\0';
}
else
{
// Use memmove because the caller may have passed in the same buffer for both variables.
memmove( pszTrimmedOut, pszTargetPtr, ( wcslen( pszTargetPtr ) + 1 ) * sizeof( WCHAR ) );
}
Cleanup:
if ( sc != ERROR_SUCCESS )
{
cchTrimmed = static_cast< DWORD >( -1 );
}
SetLastError( sc );
return cchTrimmed;
} //*** TrimLeft
/////////////////////////////////////////////////////////////////////////////
//++
//
// TrimRight
//
// Routine Description:
// Trim all trailing whitespace as well as any trailing characters specified.
//
// Arguments:
// pszTargetIn String to trim characters from.
//
// pszCharsIn List of characters to remove in addition to white space.
//
// pszTrimmedOut Target buffer in which the trimmed string will be
// placed. This may be the same buffer as pszTargetIn.
// This buffer is expected to be at least the size of
// pszTargetIn (in case no characters are removed).
//
// Return Value:
// The count of characters trimmed.
//
// -1. Call GetLastError for more information.
//
//--
/////////////////////////////////////////////////////////////////////////////
DWORD TrimRight(
const WCHAR * pszTargetIn
, const WCHAR * pszCharsIn
, WCHAR * pszTrimmedOut
)
{
const WCHAR * pszTargetPtr = pszTargetIn;
const WCHAR * pszTemp = NULL;
BOOL fContinue;
DWORD sc = ERROR_SUCCESS;
DWORD cchTrimmed = 0; // Number of characters trimmed.
size_t cchLen = 0;
if ( pszTargetIn == NULL || pszTrimmedOut == NULL )
{
sc = ERROR_INVALID_PARAMETER;
goto Cleanup;
}
cchLen = wcslen( pszTargetIn );
if ( cchLen == 0 )
{
//
// We've got an empty string.
//
pszTargetPtr = pszTargetIn;
}
else
{
//
// Point to the last character in the string.
//
pszTargetPtr = &(pszTargetIn[ cchLen - 1 ] );
}
//
// Loop until we find non-whitespace or a char not in pszCharsIn or
// we've reached the beginning of the string.
//
fContinue = TRUE;
while ( pszTargetPtr >= pszTargetIn && fContinue == TRUE )
{
fContinue = FALSE;
//
// Is the character white space?
//
if ( 0 == iswspace( pszTargetPtr[0] ) )
{
//
// No, it's not. Does it match CharsIn?
//
for( pszTemp = pszCharsIn; pszTemp != NULL && *pszTemp != L'\0'; pszTemp++ )
{
if ( pszTargetPtr[ 0 ] == pszTemp[ 0 ] )
{
//
// We've got a match - trim it and loop on the next character.
//
fContinue = TRUE;
cchTrimmed++;
pszTargetPtr--;
break;
} // if:
} // for:
} // if:
else
{
//
// We've got some whitespace - trim it.
//
fContinue = TRUE;
cchTrimmed++;
pszTargetPtr--;
} // else:
} // while:
//
// Copy the truncated string to the pszTrimmedOut buffer.
// If we truncated everything from the string make sure
// we NULL terminate the string.
//
if ( wcslen( pszTargetPtr ) == 0 )
{
*pszTrimmedOut = L'\0';
}
else
{
// Use memmove because they may have passed in the same buffer for both variables.
memmove( pszTrimmedOut, pszTargetIn, ( cchLen - cchTrimmed ) * sizeof( WCHAR ) );
pszTrimmedOut[ cchLen - cchTrimmed ] = L'\0';
}
Cleanup:
if ( sc != ERROR_SUCCESS )
{
cchTrimmed = static_cast< DWORD >( -1 );
}
SetLastError( sc );
return cchTrimmed;
} //*** TrimRight