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.
 
 
 
 
 
 

1267 lines
36 KiB

/*++
Copyright (c) 2000 Microsoft Corporation
Module Name:
asrclus.c
Abstract:
This module contains ASR routines specifically
implemented for clusters.
Notes:
Naming conventions:
_AsrpXXX private ASR Macros
AsrpXXX private ASR routines
AsrXXX Publically defined and documented routines
Author:
Guhan Suriyanarayanan (guhans) 27-May-2000
Environment:
User-mode only.
Revision History:
27-May-2000 guhans
Moved cluster-related routines from asr.c to asrclus.c
01-Mar-2000 guhans
Initial implementation for cluster-specific routines
in asr.c
--*/
#include "setupp.h"
#pragma hdrstop
#include <mountmgr.h> // mountmgr ioctls
#include <clusstor.h> // Cluster API's
#include <resapi.h> // Cluster ResUtilEnumResources
#include <clusdisk.h>
#define THIS_MODULE 'C'
#include "asrpriv.h" // Private ASR definitions and routines
//
// --------
// typedef's local to this module
// --------
//
//
// The cluster resource related typdefs
//
typedef DWORD (* PFN_CLUSTER_RESOURCE_CONTROL) (
IN HRESOURCE hResource,
IN OPTIONAL HNODE hHostNode,
IN DWORD dwControlCode,
IN LPVOID lpInBuffer,
IN DWORD cbInBufferSize,
OUT LPVOID lpOutBuffer,
IN DWORD cbOutBufferSize,
OUT LPDWORD lpcbBytesReturned
);
typedef DWORD (* PFN_RES_UTIL_ENUM_RESOURCES) (
IN HRESOURCE hSelf,
IN LPCWSTR lpszResTypeName,
IN LPRESOURCE_CALLBACK pResCallBack,
IN PVOID pParameter
);
//
// ---------
// global variables used within this module.
// --------
//
PFN_CLUSTER_RESOURCE_CONTROL pfnClusterResourceControl;
//
// ---------
// constants used within this module.
// --------
//
const WCHAR ASR_CLUSTER_PHYSICAL_DISK[] = L"Physical Disk";
const WCHAR ASR_CLUSTER_CLUSAPI_DLL_NAME[] = L"clusapi.dll";
const WCHAR ASR_CLUSTER_RESUTILS_DLL_NAME[] = L"resutils.dll";
//
// The following must be single-byte ansi chars
//
const CHAR ASR_CLUSTER_DLL_MODULE_NAME[] = "%SystemRoot%\\system32\\syssetup.dll";
const CHAR ASR_CLUSTER_DLL_PROC_NAME[] = "AsrpGetLocalDiskInfo";
const CHAR ASR_CLUSTER_CLUSAPI_PROC_NAME[] = "ClusterResourceControl";
const CHAR ASR_CLUSTER_RESUTILS_PROC_NAME[] = "ResUtilEnumResources";
//
// --------
// function implementations
// --------
//
//
// --- AsrpGetLocalVolumeInfo and related helper functions
//
//
// The disk info struct we get back from the remote nodes on the cluster will have
// offsets instead of pointers--we can convert this back to pointers by just adding
// back the base address. We also mark that this struct is packed--so we should just
// free the entire struct instead of freeing each pointer in the struct.
//
BOOL
AsrpUnPackDiskInfo(
IN PVOID InBuffer,
IN CONST DWORD dwSizeBuffer
)
{
PASR_DISK_INFO pBuffer = (PASR_DISK_INFO) InBuffer;
DWORD dwNextOffset = 0;
/* if (!((pBuffer->pDriveLayoutEx) && (pBuffer->pDiskGeometry) && (pBuffer->pPartition0Ex))) {
return FALSE;
}
*/
//
// Do some sanity checks to ensure that the offsets in the structure make sense
//
if (pBuffer->pDriveLayoutEx) {
//
// Make sure the buffer is big enough to hold this struct, and
// that the entire struct fits within the buffer
//
if ((dwSizeBuffer < sizeof(DRIVE_LAYOUT_INFORMATION_EX)) ||
(PtrToUlong(pBuffer->pDriveLayoutEx) > (dwSizeBuffer - sizeof(DRIVE_LAYOUT_INFORMATION_EX)))) {
return FALSE;
}
//
// Set the minimum value for the next struct
//
dwNextOffset = PtrToUlong(pBuffer->pDriveLayoutEx) + sizeof(DRIVE_LAYOUT_INFORMATION_EX);
}
if (pBuffer->pDiskGeometry) {
//
// Make sure this struct doesn't overlap with the previous struct
//
if (PtrToUlong(pBuffer->pDiskGeometry) < dwNextOffset) {
return FALSE;
}
//
// Make sure we haven't run off the end
//
if (dwNextOffset > dwSizeBuffer) {
return FALSE;
}
//
// Make sure the rest of buffer is big enough to hold this struct, and
// that the entire struct fits within the buffer
//
if (((dwSizeBuffer - dwNextOffset) < sizeof(DISK_GEOMETRY)) ||
(PtrToUlong(pBuffer->pDiskGeometry) > (dwSizeBuffer - sizeof(DISK_GEOMETRY)))) {
return FALSE;
}
//
// Set the minimum value for the next struct
//
dwNextOffset = PtrToUlong(pBuffer->pDiskGeometry) + sizeof(DISK_GEOMETRY);
}
if (pBuffer->pPartition0Ex) {
//
// Make sure this struct doesn't overlap with the previous struct
//
if (PtrToUlong(pBuffer->pPartition0Ex) < dwNextOffset) {
return FALSE;
}
//
// Make sure we haven't run off the end
//
if (dwNextOffset > dwSizeBuffer) {
return FALSE;
}
//
// Make sure the rest of buffer is big enough to hold this struct, and
// that the entire struct fits within the buffer
//
if (((dwSizeBuffer - dwNextOffset) < sizeof(PARTITION_INFORMATION_EX)) ||
(PtrToUlong(pBuffer->pPartition0Ex) > (dwSizeBuffer - sizeof(PARTITION_INFORMATION_EX)))) {
return FALSE;
}
//
// Set the minimum value for the next struct
//
dwNextOffset = PtrToUlong(pBuffer->pPartition0Ex) + sizeof(PARTITION_INFORMATION_EX);
}
if (pBuffer->PartitionInfoTable) {
//
// Make sure this struct doesn't overlap with the previous struct
//
if (PtrToUlong(pBuffer->PartitionInfoTable) < dwNextOffset) {
return FALSE;
}
//
// Make sure we haven't run off the end
//
if (dwNextOffset > dwSizeBuffer) {
return FALSE;
}
//
// Make sure the rest of buffer is big enough to hold this struct, and
// that the entire struct fits within the buffer
//
if (((dwSizeBuffer - dwNextOffset) < sizeof(ASR_PTN_INFO)) ||
(PtrToUlong(pBuffer->PartitionInfoTable) > (dwSizeBuffer - sizeof(ASR_PTN_INFO)))) {
return FALSE;
}
//
// Set the minimum value for the next struct
//
dwNextOffset = PtrToUlong(pBuffer->PartitionInfoTable) + sizeof(ASR_PTN_INFO);
}
if (pBuffer->pScsiAddress) {
//
// Make sure this struct doesn't overlap with the previous struct
//
if (PtrToUlong(pBuffer->pScsiAddress) < dwNextOffset) {
return FALSE;
}
//
// Make sure we haven't run off the end
//
if (dwNextOffset > dwSizeBuffer) {
return FALSE;
}
//
// Make sure the rest of buffer is big enough to hold this struct, and
// that the entire struct fits within the buffer
//
if (((dwSizeBuffer - dwNextOffset) < sizeof(SCSI_ADDRESS)) ||
(PtrToUlong(pBuffer->pScsiAddress) > (dwSizeBuffer - sizeof(SCSI_ADDRESS)))) {
return FALSE;
}
//
// Set the minimum value for the next struct
//
dwNextOffset = PtrToUlong(pBuffer->pScsiAddress) + sizeof(SCSI_ADDRESS);
}
//
// Make sure we haven't run off the end
//
if (dwNextOffset > dwSizeBuffer) {
return FALSE;
}
pBuffer->IsPacked = TRUE;
//
// Convert the offsets to pointers
//
if (pBuffer->pDriveLayoutEx) {
pBuffer->pDriveLayoutEx = (PDRIVE_LAYOUT_INFORMATION_EX) ((LPBYTE)pBuffer + PtrToUlong(pBuffer->pDriveLayoutEx));
}
if (pBuffer->pDiskGeometry) {
pBuffer->pDiskGeometry = (PDISK_GEOMETRY) ((LPBYTE)pBuffer + PtrToUlong((LPBYTE)pBuffer->pDiskGeometry));
}
if (pBuffer->pPartition0Ex) {
pBuffer->pPartition0Ex = (PPARTITION_INFORMATION_EX) ((LPBYTE)pBuffer + PtrToUlong(pBuffer->pPartition0Ex));
}
if (pBuffer->PartitionInfoTable) {
pBuffer->PartitionInfoTable = (PASR_PTN_INFO) ((LPBYTE)pBuffer + PtrToUlong(pBuffer->PartitionInfoTable));
}
if (pBuffer->pScsiAddress) {
pBuffer->pScsiAddress = (PSCSI_ADDRESS) ((LPBYTE)pBuffer + PtrToUlong(pBuffer->pScsiAddress));
}
return TRUE;
}
//
// Copies the info in pLocalDisk to a flat buffer pointed to by lpOutBuffer.
// The pointers are changed to offsets from the start of the buffer.
//
DWORD
AsrpPackDiskInfo(
IN PASR_DISK_INFO pLocalDisk,
OUT PVOID lpOutBuffer,
IN DWORD nOutBufferSize,
OUT LPDWORD lpBytesReturned
)
{
DWORD reqdSize = 0;
PASR_DISK_INFO pBuffer = NULL;
DWORD offset = 0;
MYASSERT(pLocalDisk);
//
// Calculate required size
//
reqdSize = sizeof (ASR_DISK_INFO) +
pLocalDisk->sizeDriveLayoutEx +
pLocalDisk->sizeDiskGeometry +
pLocalDisk->sizePartition0Ex +
pLocalDisk->sizePartitionInfoTable;
if (pLocalDisk->pScsiAddress) {
reqdSize += sizeof(SCSI_ADDRESS);
}
if (lpBytesReturned) {
*lpBytesReturned = reqdSize;
}
if (reqdSize > nOutBufferSize) {
return ERROR_INSUFFICIENT_BUFFER;
}
//
// Copy the ASR_DISK_INFO struct over to outBuffer
//
memcpy(lpOutBuffer, pLocalDisk, sizeof(ASR_DISK_INFO));
pBuffer = (PASR_DISK_INFO) lpOutBuffer;
offset = sizeof(ASR_DISK_INFO); // offset where the next struct will be copied
//
// Now, we go through the buffer and convert all pointers to offsets,
// and copy over the structs they were pointing to.
//
//
// First pointer: PWSTR DevicePath;
// Since the DevicePath makes sense only in the context of the local node,
// we return NULL to the remote node.
//
pBuffer->DevicePath = NULL;
//
// Next pointer: PDRIVE_LAYOUT_INFORMATION_EX pDriveLayoutEx;
//
if (pLocalDisk->pDriveLayoutEx) {
memcpy(((LPBYTE)lpOutBuffer + offset),
pLocalDisk->pDriveLayoutEx,
pLocalDisk->sizeDriveLayoutEx
);
pBuffer->pDriveLayoutEx = (PDRIVE_LAYOUT_INFORMATION_EX) UlongToPtr(offset);
offset += pLocalDisk->sizeDriveLayoutEx;
}
//
// Next pointer: PDISK_GEOMETRY pDiskGeometry;
//
if (pLocalDisk->pDiskGeometry) {
memcpy(((LPBYTE)lpOutBuffer + offset),
pLocalDisk->pDiskGeometry,
pLocalDisk->sizeDiskGeometry
);
pBuffer->pDiskGeometry = (PDISK_GEOMETRY) UlongToPtr(offset);
offset += pLocalDisk->sizeDiskGeometry;
}
//
// Next pointer: PPARTITION_INFORMATION_EX pPartition0Ex;
//
if (pLocalDisk->pPartition0Ex) {
memcpy(((LPBYTE)lpOutBuffer + offset),
pLocalDisk->pPartition0Ex,
pLocalDisk->sizePartition0Ex
);
pBuffer->pPartition0Ex= (PPARTITION_INFORMATION_EX) UlongToPtr(offset);
offset += pLocalDisk->sizePartition0Ex;
}
//
// Next pointer: PASR_PTN_INFO PartitionInfoTable;
//
if (pLocalDisk->PartitionInfoTable) {
memcpy(((LPBYTE)lpOutBuffer + offset),
pLocalDisk->PartitionInfoTable,
pLocalDisk->sizePartitionInfoTable
);
pBuffer->PartitionInfoTable = (PASR_PTN_INFO) UlongToPtr(offset);
offset += pLocalDisk->sizePartitionInfoTable;
}
//
// Last pointer: PSCSI_ADDRESS pScsiAddress;
//
if (pLocalDisk->pScsiAddress) {
memcpy(((LPBYTE)lpOutBuffer + offset),
pLocalDisk->pScsiAddress,
sizeof(SCSI_ADDRESS)
);
pBuffer->pScsiAddress = (PSCSI_ADDRESS) UlongToPtr(offset);
offset += sizeof(SCSI_ADDRESS);
}
MYASSERT(offset <= nOutBufferSize);
return ERROR_SUCCESS;
}
DWORD
WINAPI
AsrpGetLocalDiskInfo(
IN LPSTR lpszDeviceName,
IN LPSTR lpszContextString, // not used
OUT PVOID lpOutBuffer,
IN DWORD nOutBufferSize,
OUT LPDWORD lpBytesReturned
)
{
PASR_DISK_INFO pLocalDisk = NULL;
HANDLE heapHandle = NULL;
DWORD status = ERROR_SUCCESS;
BOOL result = FALSE;
ULONG MaxDeviceNumber = 0;
DWORD cchReqdSize = 0;
heapHandle = GetProcessHeap();
//
// Either the BytesReturned must be non-null (he's getting the required size),
// or the lpOutBuffer must be non-null (he's getting the data).
//
_AsrpErrExitCode(!(lpOutBuffer || lpBytesReturned), status, ERROR_INVALID_PARAMETER);
if (lpBytesReturned) {
*lpBytesReturned = 0;
}
pLocalDisk = (PASR_DISK_INFO) HeapAlloc(
heapHandle,
HEAP_ZERO_MEMORY,
sizeof (ASR_DISK_INFO)
);
_AsrpErrExitCode(!pLocalDisk, status, ERROR_NOT_ENOUGH_MEMORY);
cchReqdSize = MultiByteToWideChar(CP_ACP,
0,
lpszDeviceName,
-1,
NULL,
0
);
pLocalDisk->DevicePath = (PWSTR) HeapAlloc(
heapHandle,
HEAP_ZERO_MEMORY,
(cchReqdSize + 1) * (sizeof(WCHAR))
);
_AsrpErrExitCode(!(pLocalDisk->DevicePath), status, ERROR_NOT_ENOUGH_MEMORY);
result = MultiByteToWideChar(CP_ACP,
0,
lpszDeviceName,
-1,
pLocalDisk->DevicePath,
(cchReqdSize + 1)
);
_AsrpErrExitCode(!result, status, ERROR_INVALID_PARAMETER);
//
// Get the disk layout information
//
result = AsrpInitLayoutInformation(NULL, pLocalDisk, &MaxDeviceNumber, TRUE, TRUE);
_AsrpErrExitCode(!result, status, GetLastError());
// _AsrpErrExitCode(result && GetLastErr what if createfile fails?
result = AsrpFreeNonFixedMedia(&pLocalDisk);
_AsrpErrExitCode(!result, status, GetLastError());
_AsrpErrExitCode(!pLocalDisk, status, ERROR_SUCCESS);
//
// Copy it to the out buffer without any pointers
//
status = AsrpPackDiskInfo(pLocalDisk, lpOutBuffer, nOutBufferSize, lpBytesReturned);
EXIT:
AsrpFreeStateInformation(&pLocalDisk, NULL);
return status;
}
BOOL
AsrpIsOfflineClusteredDisk(
IN CONST HANDLE hDisk
)
/*++
Routine Description:
Utility to check if the current disk is a shared cluster disk that's owned
by a different node (and is hence inaccessible).
Based on code-snippet graciously donated by SteveDz.
Arguments:
hDisk - Supplies a handle (no access required) to the disk of interest.
Return Value:
If the function succeeds and the disk is a shared cluster disk that is
offline on the current node, the return value is a nonzero value.
If the function fails, or if the disk is not a shared cluster disk that
is offline on the current node (ie, is a local unshared disk, or
a shared disk that online to the current node) the return value
is zero.
--*/
{
BOOL result = FALSE;
DWORD bytesReturned = 0;
DiskState diskState = DiskOffline;
if ((!hDisk) || (INVALID_HANDLE_VALUE == hDisk)) {
//
// We couldn't open the disk--let's assume that it's not an offline
// clustered disk.
//
return FALSE;
}
//
// To get current disk state, don't specify input buffer.
// Current disk state returned in output buffer.
//
result = DeviceIoControl(hDisk,
IOCTL_DISK_CLUSTER_GET_STATE,
NULL,
0,
&diskState,
sizeof(DiskState),
&bytesReturned,
FALSE
);
if ((result) && (DiskOffline == diskState)) {
return TRUE;
}
else {
return FALSE;
}
}
//
// ---- AsrpInitClusterSharedDisks and related helper functions
//
BOOL
AsrpIsClusteredDiskSame(
IN PASR_DISK_INFO currentDisk,
IN PASR_DISK_INFO clusterDisk
)
{
if (!clusterDisk || !currentDisk) {
MYASSERT(0 && L"Invalid parameter, Disk is NULL");
return FALSE;
}
if (currentDisk->Style != clusterDisk->Style) {
return FALSE;
}
if (PARTITION_STYLE_MBR == clusterDisk->Style) { // currently always true
if (clusterDisk->pDriveLayoutEx) {
if (currentDisk->pDriveLayoutEx) {
return (currentDisk->pDriveLayoutEx->Mbr.Signature == clusterDisk->pDriveLayoutEx->Mbr.Signature);
}
else {
return (currentDisk->TempSignature == clusterDisk->pDriveLayoutEx->Mbr.Signature);
}
}
else {
MYASSERT(0 && L"Cluster disk drive layout is NULL");
return FALSE;
}
}
else {
if (clusterDisk->pDriveLayoutEx && currentDisk->pDriveLayoutEx) {
return (IsEqualGUID(&(currentDisk->pDriveLayoutEx->Gpt.DiskId), &(clusterDisk->pDriveLayoutEx->Gpt.DiskId)));
}
else {
return FALSE;
}
}
return FALSE;
}
DWORD
AsrpResourceCallBack(
RESOURCE_HANDLE hOriginal,
RESOURCE_HANDLE hResource,
PVOID lpParams
)
{
DISK_DLL_EXTENSION_INFO inBuffer;
PBYTE outBuffer = NULL;
DWORD sizeOutBuffer = 0,
bytesReturned = 0;
DWORD status = ERROR_SUCCESS;
PASR_DISK_INFO currentDisk = (PASR_DISK_INFO) lpParams,
clusterDisk = NULL,
prevDisk = NULL;
BOOL bResult = FALSE;
HANDLE heapHandle = NULL;
BOOL done = FALSE;
if (!lpParams) {
//
// The system must have at least one disk that has been enumerated
// already (the system disk, at least!), so our disk list shouldn't be NULL.
//
return ERROR_INVALID_PARAMETER;
}
heapHandle = GetProcessHeap();
MYASSERT(heapHandle);
//
// Allocate a reasonably-sized memory for the out buffer. If this isn't
// big enough, we'll re-allocate.
//
sizeOutBuffer = ASR_BUFFER_SIZE;
outBuffer = (PBYTE) HeapAlloc(
heapHandle,
HEAP_ZERO_MEMORY,
sizeOutBuffer
);
_AsrpErrExitCode(!outBuffer, status, ERROR_NOT_ENOUGH_MEMORY);
//
// Call AsrpGetLocalDiskInfo on the node owning this disk resource
//
ZeroMemory(&inBuffer, sizeof(inBuffer));
inBuffer.MajorVersion = NT5_MAJOR_VERSION;
strcpy(inBuffer.DllModuleName, ASR_CLUSTER_DLL_MODULE_NAME);
strcpy(inBuffer.DllProcName, ASR_CLUSTER_DLL_PROC_NAME);
status = (pfnClusterResourceControl) (hResource,
NULL,
CLUSCTL_RESOURCE_STORAGE_DLL_EXTENSION,
&inBuffer,
sizeof(DISK_DLL_EXTENSION_INFO),
(PVOID) outBuffer,
sizeOutBuffer,
&bytesReturned
);
if (ERROR_INSUFFICIENT_BUFFER == status) {
//
// The buffer wasn't big enough, re-allocate as needed
//
_AsrpHeapFree(outBuffer);
sizeOutBuffer = bytesReturned;
outBuffer = (PBYTE) HeapAlloc(
heapHandle,
HEAP_ZERO_MEMORY,
sizeOutBuffer
);
_AsrpErrExitCode(!outBuffer, status, ERROR_NOT_ENOUGH_MEMORY);
status = (pfnClusterResourceControl) (
hResource,
NULL,
CLUSCTL_RESOURCE_STORAGE_DLL_EXTENSION,
&inBuffer,
sizeof(DISK_DLL_EXTENSION_INFO),
(PVOID) outBuffer,
sizeOutBuffer,
&bytesReturned
);
}
_AsrpErrExitCode((ERROR_SUCCESS != status), status, status);
//
// outBuffer has a packed disk info struct (ie the pointers are offsets).
//
bResult = AsrpUnPackDiskInfo(outBuffer, sizeOutBuffer);
_AsrpErrExitCode(!bResult, status, ERROR_INVALID_DATA);
clusterDisk = (PASR_DISK_INFO) outBuffer;
clusterDisk->IsClusterShared = TRUE;
clusterDisk->IsPacked = TRUE; // so that we free this properly
//
// Check if clusterDisk already has info in our list (ie is owned
// locally)
//
// Note that for now, clusterDisk is always MBR (since clusters don't
// support shared GPT disks). We don't care here, we handle GPT as well.
//
done = FALSE;
prevDisk = NULL;
while (currentDisk && !done) {
if (AsrpIsClusteredDiskSame(currentDisk, clusterDisk)) {
if (currentDisk->pDriveLayoutEx) {
//
// This disk is owned by the local node (correct?), since
// we would not have gotten the pDriveLayout otherwise
//
currentDisk->IsClusterShared = TRUE;
currentDisk->IsPacked = FALSE;
//
// We don't need the info returned by clusterDisk, we have
// it in currentDisk already.
//
_AsrpHeapFree(clusterDisk); // it's packed.
}
else {
//
// This disk is owned by a remote node. So we add clusterDisk
// in to our list now. We'll remove currentDisk from our
// list later (in RemoveNonFixedDevices).
//
// First though, we copy over DevicePath and DeviceNumber
// from currentDisk, since these are relative to the local
// machine
//
if (currentDisk->DevicePath) {
clusterDisk->DevicePath = (PWSTR) HeapAlloc(
heapHandle,
HEAP_ZERO_MEMORY,
sizeof(WCHAR) * (wcslen(currentDisk->DevicePath) + 1)
);
wcscpy(clusterDisk->DevicePath, currentDisk->DevicePath);
}
clusterDisk->DeviceNumber = currentDisk->DeviceNumber;
//
// Don't bother freeing currentDisk, it'll get taken care
// of in RemoveNonFixedDevices.
//
clusterDisk->pNext = currentDisk->pNext;
currentDisk->pNext = clusterDisk;
currentDisk = clusterDisk; // move forward by one (don't really need to since done will be set to TRUE and we'll get out of the loop)
}
done = TRUE;
}
prevDisk = currentDisk;
currentDisk = currentDisk->pNext;
}
if (!done) {
//
// This disk was not found in our list (strange), let's add
// it in at the end
//
// MYASSERT(0 && L"Clustered disk not found in OriginalDiskList, adding it to the end");
clusterDisk->pNext = NULL;
prevDisk->pNext = clusterDisk;
}
EXIT:
//
// Free up outBuffer on failure. On success, outBuffer shouldn't
// be freed, it will either be part of OriginalDiskList or already
// be freed.
//
if (ERROR_SUCCESS != status) {
_AsrpHeapFree(outBuffer);
}
return status;
}
BOOL
AsrpInitClusterSharedDisks(
IN PASR_DISK_INFO OriginalDiskList
)
{
DWORD status = ERROR_SUCCESS,
dwOldError;
HMODULE hClusApi = NULL,
hResUtils = NULL;
PFN_RES_UTIL_ENUM_RESOURCES pfnResUtilEnumResources = NULL;
dwOldError = GetLastError();
if (!OriginalDiskList) {
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
hClusApi = LoadLibraryW(ASR_CLUSTER_CLUSAPI_DLL_NAME);
_AsrpErrExitCode(!hClusApi, status, GetLastError());
pfnClusterResourceControl = (PFN_CLUSTER_RESOURCE_CONTROL) GetProcAddress(
hClusApi,
ASR_CLUSTER_CLUSAPI_PROC_NAME
);
_AsrpErrExitCode(!pfnClusterResourceControl, status, GetLastError());
hResUtils = LoadLibraryW(ASR_CLUSTER_RESUTILS_DLL_NAME);
_AsrpErrExitCode(!hResUtils, status, GetLastError());
pfnResUtilEnumResources = (PFN_RES_UTIL_ENUM_RESOURCES) GetProcAddress(
hResUtils,
ASR_CLUSTER_RESUTILS_PROC_NAME
);
_AsrpErrExitCode(!pfnResUtilEnumResources, status, GetLastError());
status = (pfnResUtilEnumResources) (NULL,
ASR_CLUSTER_PHYSICAL_DISK,
AsrpResourceCallBack,
OriginalDiskList
);
EXIT:
if (hClusApi) {
FreeLibrary(hClusApi);
}
if (hResUtils) {
FreeLibrary(hResUtils);
}
// ResUtil will fail if we aren't on a cluster, but that's fine.
SetLastError(dwOldError);
return TRUE;
}
//
// --- AsrpGetLocalVolumeInfo and related helper functions
//
//
// The following two definitions are from asr_fmt:dr_state.cpp. This MUST be
// kept in sync.
//
typedef struct _ASRFMT_CLUSTER_VOLUME_INFO {
UINT driveType;
DWORD PartitionNumber;
ULONG FsNameOffset;
USHORT FsNameLength;
ULONG LabelOffset;
USHORT LabelLength;
ULONG SymbolicNamesOffset;
USHORT SymbolicNamesLength;
DWORD dwClusterSize;
} ASRFMT_CLUSTER_VOLUME_INFO, *PASRFMT_CLUSTER_VOLUME_INFO;
typedef struct _ASRFMT_CLUSTER_VOLUMES_TABLE {
DWORD DiskSignature;
DWORD NumberOfEntries;
ASRFMT_CLUSTER_VOLUME_INFO VolumeInfoEntry[1];
} ASRFMT_CLUSTER_VOLUMES_TABLE, *PASRFMT_CLUSTER_VOLUMES_TABLE;
BOOL
AsrpFmtGetVolumeDetails(
IN PWSTR lpVolumeGuid,
OUT PWSTR lpFsName,
IN DWORD cchFsName,
OUT PWSTR lpVolumeLabel,
IN DWORD cchVolumeLabel,
OUT LPDWORD lpClusterSize
)
{
DWORD dwFSFlags = 0,
dwSectorsPerCluster = 0,
dwBytesPerSector = 0,
dwNumFreeClusters = 0,
dwTotalNumClusters = 0;
BOOL result1 = TRUE,
result2 = TRUE;
*lpFsName = 0;
*lpVolumeLabel = 0;
*lpClusterSize = 0;
SetErrorMode(SEM_FAILCRITICALERRORS);
result1 = GetVolumeInformationW(lpVolumeGuid,
lpVolumeLabel,
cchVolumeLabel,
NULL, // no need for serial number
NULL, // max file name length
&dwFSFlags, // !! we might need to store some of this ...
lpFsName,
cchFsName
);
result2 = GetDiskFreeSpaceW(lpVolumeGuid,
&dwSectorsPerCluster,
&dwBytesPerSector,
&dwNumFreeClusters,
&dwTotalNumClusters
);
*lpClusterSize = dwSectorsPerCluster * dwBytesPerSector;
return (result1 && result2);
}
DWORD
WINAPI
AsrpGetLocalVolumeInfo(
IN LPSTR lpszDeviceName,
IN LPSTR lpszContextString, // not used
OUT PVOID lpOutBuffer,
IN DWORD nOutBufferSize,
OUT LPDWORD lpBytesReturned
)
{
PASR_DISK_INFO pLocalDisk = NULL;
HANDLE heapHandle = NULL;
DWORD status = ERROR_SUCCESS;
BOOL result = FALSE;
ULONG MaxDeviceNumber = 0;
DWORD cchReqdSize = 0,
cchGuid = 0,
offset = 0,
index = 0,
i = 0;
USHORT
cbFsName = 0,
cbLabel = 0,
cbLinks = 0;
PMOUNTMGR_MOUNT_POINTS mountPointsOut = NULL;
WCHAR devicePath[MAX_PATH + 1];
WCHAR volumeGuid[MAX_PATH + 1];
WCHAR fileSystemName[MAX_PATH + 1];
WCHAR volumeLabel[MAX_PATH + 1];
UINT driveType = DRIVE_UNKNOWN;
DWORD clusterSize = 0;
BOOL bufferFull = FALSE,
foundGuid = FALSE;
PPARTITION_INFORMATION_EX currentPartitionEx = NULL;
PASRFMT_CLUSTER_VOLUMES_TABLE pTable = NULL;
heapHandle = GetProcessHeap();
//
// Either the BytesReturned must be non-null (he's getting the required size),
// or the lpOutBuffer must be non-null (he's getting the data).
//
_AsrpErrExitCode(!(lpOutBuffer || lpBytesReturned), status, ERROR_INVALID_PARAMETER);
if (lpBytesReturned) {
*lpBytesReturned = 0;
}
//
// Zero the out buffer
//
if ((lpOutBuffer) && (nOutBufferSize > 0)) {
ZeroMemory(lpOutBuffer, nOutBufferSize);
}
pLocalDisk = (PASR_DISK_INFO) HeapAlloc(
heapHandle,
HEAP_ZERO_MEMORY,
sizeof (ASR_DISK_INFO)
);
_AsrpErrExitCode(!pLocalDisk, status, ERROR_NOT_ENOUGH_MEMORY);
cchReqdSize = MultiByteToWideChar(CP_ACP,
0,
lpszDeviceName,
-1,
NULL,
0
);
pLocalDisk->DevicePath = (PWSTR) HeapAlloc(
heapHandle,
HEAP_ZERO_MEMORY,
(cchReqdSize + 1) * (sizeof(WCHAR))
);
_AsrpErrExitCode(!(pLocalDisk->DevicePath), status, ERROR_NOT_ENOUGH_MEMORY);
result = MultiByteToWideChar(CP_ACP,
0,
lpszDeviceName,
-1,
pLocalDisk->DevicePath,
(cchReqdSize + 1)
);
_AsrpErrExitCode(!result, status, ERROR_INVALID_PARAMETER);
//
// Get the disk layout information
//
result = AsrpInitLayoutInformation(NULL, pLocalDisk, &MaxDeviceNumber, FALSE, FALSE); // basic info will suffice
_AsrpErrExitCode(!result, status, GetLastError());
_AsrpErrExitCode(!(pLocalDisk->pDriveLayoutEx), status, ERROR_SUCCESS);
//
//
//
offset = sizeof(ASRFMT_CLUSTER_VOLUMES_TABLE) +
(sizeof(ASRFMT_CLUSTER_VOLUME_INFO) * (pLocalDisk->pDriveLayoutEx->PartitionCount - 1));
pTable = (PASRFMT_CLUSTER_VOLUMES_TABLE) lpOutBuffer;
if ((!lpOutBuffer) || (offset > nOutBufferSize)) {
bufferFull = TRUE;
}
if (!bufferFull) {
if (PARTITION_STYLE_MBR == pLocalDisk->pDriveLayoutEx->PartitionStyle) {
pTable->DiskSignature = pLocalDisk->pDriveLayoutEx->Mbr.Signature;
}
else {
//
// At the moment, only MBR disks are cluster shared disks, and so
// we don't handle GPT disks here. If GPT disks are allowed to
// be on a shared bus in a cluster, change this.
//
_AsrpErrExitCode(FALSE, status, ERROR_SUCCESS);
}
pTable->NumberOfEntries = pLocalDisk->pDriveLayoutEx->PartitionCount;
}
for (index = 0; index < pLocalDisk->pDriveLayoutEx->PartitionCount; index++) {
currentPartitionEx = &(pLocalDisk->pDriveLayoutEx->PartitionEntry[index]);
mountPointsOut = NULL;
foundGuid = FALSE;
//
// For each partition, AsrpGetMountPoints gives a list of all mount points,
// then use that to AsrpFmtGetVolumeDetails
//
// get the volumeGuid
if (!(currentPartitionEx->PartitionNumber)) {
//
// Container partitions have partitionNumber = 0, and have no volume Guids.
//
continue;
}
memset(volumeGuid, 0, (MAX_PATH + 1) * sizeof(WCHAR));
swprintf(devicePath,
ASR_WSZ_DEVICE_PATH_FORMAT,
pLocalDisk->DeviceNumber,
currentPartitionEx->PartitionNumber
);
result = AsrpGetMountPoints(
devicePath,
(wcslen(devicePath) + 1)* sizeof(WCHAR), // including \0, in bytes
&mountPointsOut
);
if (!result || !(mountPointsOut)) {
continue;
}
//
// Go through the list of mount points, and pick out one that
// looks like a volume Guid (starts with \??\Volume)
//
cbLinks = sizeof(WCHAR); // \0 at the end
for (i = 0; i < mountPointsOut->NumberOfMountPoints; i++) {
PWSTR linkName = (PWSTR) (
((LPBYTE) mountPointsOut) +
mountPointsOut->MountPoints[i].SymbolicLinkNameOffset
);
USHORT sizeLinkName = (UINT) (mountPointsOut->MountPoints[i].SymbolicLinkNameLength);
if (!wcsncmp(ASR_WSZ_VOLUME_PREFIX, linkName, wcslen(ASR_WSZ_VOLUME_PREFIX)) &&
!foundGuid) {
wcsncpy(volumeGuid, linkName, sizeLinkName / sizeof(WCHAR));
foundGuid = TRUE;
}
cbLinks += sizeLinkName + (USHORT) sizeof(WCHAR);
}
//
// GetDriveType needs the volume guid in the dos-name-space, while the
// mount manager gives the volume guid in the nt-name-space. Convert
// the name by changing the \??\ at the beginning to \\?\, and adding
// a back-slash at the end.
//
cchGuid = wcslen(volumeGuid);
volumeGuid[1] = L'\\';
volumeGuid[cchGuid] = L'\\'; // Trailing back-slash
volumeGuid[cchGuid+1] = L'\0';
driveType = GetDriveTypeW(volumeGuid);
//
// Get the FS Label, cluster size, and so on.
//
result = AsrpFmtGetVolumeDetails(volumeGuid,
fileSystemName,
MAX_PATH + 1,
volumeLabel,
MAX_PATH + 1,
&clusterSize
);
if (!result) {
continue;
}
cbFsName = wcslen(fileSystemName) * sizeof(WCHAR);
cbLabel = wcslen(volumeLabel) * sizeof(WCHAR);
if (bufferFull) {
offset += (cbFsName + cbLabel + cbLinks);
}
else {
if (offset + cbFsName + cbLabel + cbLinks > nOutBufferSize) {
bufferFull = TRUE;
}
else {
if (cbFsName) {
CopyMemory(((LPBYTE)lpOutBuffer + offset),
fileSystemName,
cbFsName
);
pTable->VolumeInfoEntry[index].FsNameOffset = offset;
pTable->VolumeInfoEntry[index].FsNameLength = cbFsName;
offset += cbFsName;
}
if (cbLabel) {
CopyMemory(((LPBYTE)lpOutBuffer + offset),
volumeLabel,
cbLabel
);
pTable->VolumeInfoEntry[index].LabelOffset = offset;
pTable->VolumeInfoEntry[index].LabelLength = cbLabel;
offset += cbLabel;
}
//
// Copy the symbolic links, separated by zeroes
//
if (mountPointsOut->NumberOfMountPoints > 0) {
pTable->VolumeInfoEntry[index].SymbolicNamesOffset = offset;
}
for (i = 0; i < mountPointsOut->NumberOfMountPoints; i++) {
PWSTR linkName = (PWSTR) (
((LPBYTE) mountPointsOut) +
mountPointsOut->MountPoints[i].SymbolicLinkNameOffset
);
UINT sizeLinkName = (UINT) (mountPointsOut->MountPoints[i].SymbolicLinkNameLength);
CopyMemory(((LPBYTE)lpOutBuffer + offset),
linkName,
sizeLinkName
);
offset += (sizeLinkName + sizeof(WCHAR));
}
offset += sizeof(WCHAR); // second \0 at the end
pTable->VolumeInfoEntry[index].SymbolicNamesLength = cbLinks;
pTable->VolumeInfoEntry[index].driveType = driveType;
pTable->VolumeInfoEntry[index].PartitionNumber = currentPartitionEx->PartitionNumber;
pTable->VolumeInfoEntry[index].dwClusterSize = clusterSize;
}
}
_AsrpHeapFree(mountPointsOut);
}
if (lpBytesReturned) {
*lpBytesReturned = offset;
}
EXIT:
AsrpFreeStateInformation(&pLocalDisk, NULL);
return status;
}