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.
4933 lines
132 KiB
4933 lines
132 KiB
/*++
|
|
|
|
Copyright (c) 2000 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
mountpoints.c
|
|
|
|
Abstract:
|
|
|
|
This module processes mount point information for the disk resource DLL.
|
|
|
|
Author:
|
|
|
|
Steve Dziok (stevedz) 15-May-2000
|
|
|
|
Environment:
|
|
|
|
User Mode
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#define UNICODE 1
|
|
|
|
#include "disksp.h"
|
|
|
|
#include "arbitrat.h"
|
|
#include "newdisks.h"
|
|
#include "newmount.h"
|
|
#include <mountmgr.h>
|
|
#include <strsafe.h> // Should be included last.
|
|
|
|
#define SPACE_CHAR L' '
|
|
|
|
#define MAX_OFFSET_CHARS 80 // Maximum number of chars allowed in offset string
|
|
#define MP_ALLOC_SIZE 1024
|
|
|
|
#define LOG_CURRENT_MODULE LOG_MODULE_DISK
|
|
|
|
extern PWCHAR g_DiskResource; // L"rtPhysical Disk"
|
|
#define RESOURCE_TYPE ((RESOURCE_HANDLE)g_DiskResource)
|
|
|
|
#ifndef ClusterHashGuid
|
|
|
|
//
|
|
// Hash a GUID to a ULONG value.
|
|
//
|
|
|
|
#define ClusterHashGuid(_guid) ( ((PULONG) &(_guid))[0] ^ ((PULONG) &(_guid))[1] ^ \
|
|
((PULONG) &(_guid))[2] ^ ((PULONG) &(_guid))[3] )
|
|
|
|
#endif
|
|
|
|
#define MPS_ENABLED 0x00000001
|
|
#define MPS_DELETE_INVALID_MPS 0x00000002 // Not currently used
|
|
#define MPS_NONCLUSTERED_TO_CLUSTERED_MPS 0x00000010 // Not currently used
|
|
#define MPS_KEEP_EXISTING_MPS 0x00000020 // Not currently used
|
|
#define MPS_IGNORE_MAX_VOLGUIDS 0x00000100
|
|
|
|
#define MAX_ALLOWED_VOLGUID_ENTRIES_PER_DISK 100
|
|
|
|
#define STOP_CLUSTER_ENUMERATIONS ERROR_INVALID_PRINTER_COMMAND
|
|
#define PHYSICAL_DISK_WSTR L"Physical Disk"
|
|
#define CREATE_FILE_PREFIX L"\\\\.\\"
|
|
|
|
#define MAX_GROUP_NAME_LENGTH MAX_PATH * 2
|
|
|
|
#define VOL_GUID_STRING_LEN 48
|
|
|
|
#define MOUNTDEV_WSZ_VOLUME_GUID_PREFIX L"\\??\\Volume{" // Forms: \??\Volume{
|
|
#define MOUNTDEV_CWCHAR_VOLUME_GUID_PREFIX 11
|
|
#define MOUNTDEV_CB_VOLUME_GUID_PREFIX MOUNTDEV_CWCHAR_VOLUME_GUID_PREFIX * sizeof(WCHAR)
|
|
|
|
#define MOUNTDEV_LOOKS_LIKE_VOLUME_GUID( name, charCount ) \
|
|
( ( charCount > MOUNTDEV_CWCHAR_VOLUME_GUID_PREFIX ) && \
|
|
( !memcmp( name, MOUNTDEV_WSZ_VOLUME_GUID_PREFIX, MOUNTDEV_CB_VOLUME_GUID_PREFIX ) ) )
|
|
|
|
#define MOUNTDEV_WSZ_ALT_VOLUME_GUID_PREFIX L"\\\\?\\Volume{" // Forms: \\?\Volume{
|
|
#define MOUNTDEV_CWCHAR_ALT_VOLUME_GUID_PREFIX 11
|
|
#define MOUNTDEV_CB_ALT_VOLUME_GUID_PREFIX MOUNTDEV_CWCHAR_ALT_VOLUME_GUID_PREFIX * sizeof(WCHAR)
|
|
|
|
#define MOUNTDEV_LOOKS_LIKE_ALT_VOLUME_GUID( name, charCount ) \
|
|
( ( charCount > MOUNTDEV_CWCHAR_ALT_VOLUME_GUID_PREFIX ) && \
|
|
( !memcmp( name, MOUNTDEV_WSZ_ALT_VOLUME_GUID_PREFIX, MOUNTDEV_CB_ALT_VOLUME_GUID_PREFIX ) ) )
|
|
|
|
#define MOUNTDEV_CWCHAR_DISK_PATH_PREFIX 3
|
|
#define MOUNTDEV_CB_DISK_PATH_PREFIX MOUNTDEV_CWCHAR_DISK_PATH_PREFIX * sizeof(WCHAR)
|
|
|
|
#define MOUNTDEV_LOOKS_LIKE_DISK_PATH( name, charCount ) \
|
|
( ( charCount > MOUNTDEV_CWCHAR_DISK_PATH_PREFIX ) && \
|
|
( (WCHAR)*( name + 1 ) == L':' ) && \
|
|
( (WCHAR)*( name + 2 ) == L'\\' ) )
|
|
|
|
|
|
#define INPUT_BUFFER_LEN 2048
|
|
#define OUTPUT_BUFFER_LEN 2048
|
|
|
|
|
|
typedef struct _SIG_INFO {
|
|
PDISK_RESOURCE ResourceEntry;
|
|
DWORD Signature;
|
|
BOOL Clustered;
|
|
PWSTR GroupName;
|
|
} SIG_INFO, *PSIG_INFO;
|
|
|
|
typedef struct _DEPENDENCY_INFO {
|
|
PDISK_RESOURCE ResourceEntry;
|
|
DWORD SrcSignature;
|
|
DWORD TargetSignature;
|
|
BOOL DependencyCorrect;
|
|
} DEPENDENCY_INFO, *PDEPENDENCY_INFO;
|
|
|
|
|
|
typedef struct _STR_LIST {
|
|
LPWSTR MultiSzList; // REG_MULTI_SZ string
|
|
DWORD ListBytes; // Number of bytes, not number of WCHARs!
|
|
} STR_LIST, *PSTR_LIST;
|
|
|
|
typedef struct _OFFSET_LIST {
|
|
LARGE_INTEGER Offset;
|
|
struct _OFFSET_LIST *Next;
|
|
} OFFSET_LIST, *POFFSET_LIST;
|
|
|
|
|
|
DWORD
|
|
AddStrToList(
|
|
IN PDISK_RESOURCE ResourceEntry,
|
|
IN PWSTR NewStr,
|
|
IN DWORD PartitionNumber,
|
|
IN OUT PSTR_LIST StrList
|
|
);
|
|
|
|
DWORD
|
|
AssignDevice(
|
|
HANDLE MountMgrHandle,
|
|
PWCHAR MountName,
|
|
PWCHAR VolumeDevName
|
|
);
|
|
|
|
DWORD
|
|
CheckDependencies(
|
|
PDISK_RESOURCE ResourceEntry,
|
|
DWORD SrcSignature,
|
|
DWORD TargetSignature,
|
|
PBOOL DependencyCorrect
|
|
);
|
|
|
|
VOID
|
|
CheckMPsForVolume(
|
|
IN OUT PDISK_RESOURCE ResourceEntry,
|
|
IN PWSTR VolumeName
|
|
);
|
|
|
|
DWORD
|
|
CheckMPsOnVolume(
|
|
IN OUT PDISK_RESOURCE ResourceEntry,
|
|
IN PWSTR SrcVolName
|
|
);
|
|
|
|
DWORD
|
|
CheckSignatureClustered(
|
|
PDISK_RESOURCE ResourceEntry,
|
|
DWORD Signature,
|
|
PBOOL IsClustered,
|
|
PWSTR *GroupName
|
|
);
|
|
|
|
DWORD
|
|
CreateVolGuidList(
|
|
IN OUT PDISK_RESOURCE ResourceEntry
|
|
);
|
|
|
|
DWORD
|
|
DeleteVolGuidList(
|
|
PDISK_RESOURCE ResourceEntry
|
|
);
|
|
|
|
DWORD
|
|
DependencyCallback(
|
|
RESOURCE_HANDLE hOriginal,
|
|
RESOURCE_HANDLE hResource,
|
|
PVOID lpParams
|
|
);
|
|
|
|
#if DBG
|
|
VOID
|
|
DumpDiskInfoParams(
|
|
PDISK_RESOURCE ResourceEntry
|
|
);
|
|
#endif
|
|
|
|
DWORD
|
|
EnumSigDependencies(
|
|
PDISK_RESOURCE ResourceEntry,
|
|
RESOURCE_HANDLE DependentResource,
|
|
DWORD DependsOnSignature,
|
|
PBOOL DependencyCorrect
|
|
);
|
|
|
|
DWORD
|
|
GetBestVolGuid(
|
|
PDISK_RESOURCE ResourceEntry,
|
|
PWSTR GlobalDiskPartName,
|
|
PWSTR VolumeName,
|
|
DWORD VolumeNameChars
|
|
);
|
|
|
|
DWORD
|
|
GetMountPoints(
|
|
PWSTR VolumeName,
|
|
PWSTR *VolumePaths
|
|
);
|
|
|
|
BOOL
|
|
GetOffsetFromPartNo(
|
|
DWORD PartitionNo,
|
|
PMOUNTIE_INFO Info,
|
|
PLARGE_INTEGER Offset
|
|
);
|
|
|
|
BOOL
|
|
GetPartNoFromOffset(
|
|
PLARGE_INTEGER Offset,
|
|
PMOUNTIE_INFO Info,
|
|
PDWORD PartitionNumber
|
|
);
|
|
|
|
DWORD
|
|
GetSignatureForVolume(
|
|
PDISK_RESOURCE ResourceEntry,
|
|
PWSTR Volume,
|
|
PDWORD Signature
|
|
);
|
|
|
|
DWORD
|
|
GetSignatureFromRegistry(
|
|
PDISK_RESOURCE ResourceEntry,
|
|
RESOURCE_HANDLE hResource,
|
|
DWORD *dwSignature
|
|
);
|
|
|
|
BOOL
|
|
IsMountPointAllowed(
|
|
PWSTR MpName,
|
|
PWSTR SourceVol,
|
|
PWSTR TargetVol,
|
|
PDISK_RESOURCE ResourceEntry
|
|
);
|
|
|
|
BOOL
|
|
MPIsDriveLetter(
|
|
IN PWSTR MountPoint
|
|
);
|
|
|
|
DWORD
|
|
OffsetListAdd(
|
|
POFFSET_LIST *OffsetList,
|
|
PLARGE_INTEGER Offset
|
|
);
|
|
|
|
DWORD
|
|
OffsetListCleanup(
|
|
POFFSET_LIST OffsetList
|
|
);
|
|
|
|
VOID
|
|
PrintStrList(
|
|
PDISK_RESOURCE ResourceEntry,
|
|
LPWSTR MultiSzList,
|
|
DWORD ListBytes
|
|
);
|
|
|
|
DWORD
|
|
ProcessVolGuidList(
|
|
IN OUT PDISK_RESOURCE ResourceEntry
|
|
);
|
|
|
|
DWORD
|
|
RemoveExcessVolGuids(
|
|
HANDLE MountMgrHandle,
|
|
PWCHAR MountName,
|
|
PWCHAR VolumeDevName
|
|
);
|
|
|
|
DWORD
|
|
RemoveVolGuid(
|
|
HANDLE MountMgrHandle,
|
|
PWCHAR VolGuid,
|
|
USHORT VolGuidSize
|
|
);
|
|
|
|
static
|
|
DWORD
|
|
SetMPListThread(
|
|
LPVOID lpThreadParameter
|
|
);
|
|
|
|
DWORD
|
|
SetupVolGuids(
|
|
IN OUT PDISK_RESOURCE ResourceEntry
|
|
);
|
|
|
|
DWORD
|
|
SigInfoCallback(
|
|
RESOURCE_HANDLE hOriginal,
|
|
RESOURCE_HANDLE hResource,
|
|
PVOID lpParams
|
|
);
|
|
|
|
DWORD
|
|
ValidateListOffsets(
|
|
IN OUT PDISK_RESOURCE ResourceEntry,
|
|
IN PWSTR MasterList
|
|
);
|
|
|
|
DWORD
|
|
ValidateMountPoints(
|
|
IN OUT PDISK_RESOURCE ResourceEntry
|
|
);
|
|
|
|
|
|
|
|
DWORD
|
|
DisksProcessMountPointInfo(
|
|
IN OUT PDISK_RESOURCE ResourceEntry
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
During online processing, find all mount points directed towards this volume
|
|
(identified by the ResourceEntry), and process the VolGuid list for this
|
|
volume.
|
|
|
|
If the VolGuid list exists in the cluster database, use it. Otherwise,
|
|
get the current VolGuid and add it to the VolGuid list. Each sector offset
|
|
can only be listed once as the VolGuid will be the same on each node.
|
|
|
|
VolGuid list is of the form:
|
|
|
|
SectorOffset1 VolGuid1
|
|
SectorOffset2 VolGuid2
|
|
SectorOffset3 VolGuid3
|
|
... ...
|
|
|
|
There are three possible mount point configurations involving clustered disks (we
|
|
are not concerned about nonshared disks pointing to nonshared disks):
|
|
|
|
Source --> Target
|
|
----------------- -----------------
|
|
1. clustered disk clustered disk
|
|
2. nonclustered disk clustered disk
|
|
3. clustered disk nonclustered disk
|
|
|
|
Only configuration (1) is supported. Configurations (2) and (3) are not supported.
|
|
|
|
Arguments:
|
|
|
|
ResourceEntry - Pointer to the DISK_RESOURCE structure.
|
|
|
|
Return Value:
|
|
|
|
ERROR_NOT_READY - MPInfo structure not yet initialized.
|
|
|
|
Win32 error code.
|
|
|
|
--*/
|
|
{
|
|
DWORD dwError = NO_ERROR;
|
|
|
|
//
|
|
// Mount point structures not initialized (i.e. critical section). Don't continue.
|
|
//
|
|
|
|
if ( !ResourceEntry->MPInfo.Initialized ) {
|
|
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_WARNING,
|
|
L"DisksProcessMountPointInfo: Mount point information not initialized. \n" );
|
|
|
|
return ERROR_NOT_READY;
|
|
|
|
}
|
|
|
|
#if USEMOUNTPOINTS_KEY
|
|
//
|
|
// Mount point support disabled, don't do anything.
|
|
//
|
|
|
|
if ( !( ResourceEntry->DiskInfo.Params.UseMountPoints & MPS_ENABLED ) ) {
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"DisksProcessMountPointInfo: Mount point processing disabled via registry \n" );
|
|
|
|
//
|
|
// Delete the VolGuid list if it exists and remove this info
|
|
// from the cluster database.
|
|
//
|
|
|
|
dwError = DeleteVolGuidList( ResourceEntry );
|
|
|
|
if ( ERROR_SHARING_PAUSED == dwError ) {
|
|
PostMPInfoIntoRegistry( ResourceEntry );
|
|
}
|
|
|
|
dwError = NO_ERROR;
|
|
return dwError;
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// Check if we are currently processing mount point info. If so, exit with an error.
|
|
//
|
|
|
|
if ( InterlockedCompareExchange(
|
|
&ResourceEntry->MPInfo.MPListCreateInProcess,
|
|
1, 0 ) ) {
|
|
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_WARNING,
|
|
L"DisksProcessMountPointInfo: MPList creation in process, bypassing request \n" );
|
|
return ERROR_BUSY;
|
|
}
|
|
|
|
__try {
|
|
|
|
dwError = ProcessVolGuidList( ResourceEntry );
|
|
|
|
ValidateMountPoints( ResourceEntry );
|
|
|
|
// Fall through...
|
|
|
|
#if 0
|
|
|
|
// Add code similar to this when MPs from nonclustered to clustered disks is supported.
|
|
|
|
if ( ( ResourceEntry->DiskInfo.Params.UseMountPoints & MPS_ENABLED ) &&
|
|
( ResourceEntry->DiskInfo.Params.UseMountPoints & MPS_NONCLUSTERED_TO_CLUSTERED_MPS ) ) {
|
|
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"DisksProcessMountPointInfo: ProcessMPList \n" );
|
|
|
|
dwError = ProcessMPListConfig2( ResourceEntry );
|
|
#endif
|
|
|
|
} __finally {
|
|
|
|
InterlockedExchange( &ResourceEntry->MPInfo.MPListCreateInProcess, 0 );
|
|
}
|
|
|
|
return dwError;
|
|
|
|
} // DisksProcessMountPointInfo
|
|
|
|
|
|
DWORD
|
|
ProcessVolGuidList(
|
|
IN OUT PDISK_RESOURCE ResourceEntry
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Main routine to create a new VolGuid list or to process an existing VolGuid list.
|
|
|
|
Arguments:
|
|
|
|
ResourceEntry - Pointer to the DISK_RESOURCE structure.
|
|
|
|
Return Value:
|
|
|
|
Win32 error code.
|
|
|
|
--*/
|
|
{
|
|
DWORD dwError = NO_ERROR;
|
|
|
|
__try {
|
|
|
|
//
|
|
// If the list is empty, create it.
|
|
//
|
|
|
|
if ( !ResourceEntry->DiskInfo.Params.MPVolGuids ||
|
|
0 == ResourceEntry->DiskInfo.Params.MPVolGuidsSize ) {
|
|
|
|
dwError = CreateVolGuidList( ResourceEntry );
|
|
if ( NO_ERROR != dwError ) {
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_WARNING,
|
|
L"ProcessVolGuidList: Stop processing VolGuid list, Create failed %1!u! \n",
|
|
dwError );
|
|
__leave;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If the list is still empty (it shouldn't be), then exit with an error.
|
|
//
|
|
|
|
if ( !ResourceEntry->DiskInfo.Params.MPVolGuids ||
|
|
0 == ResourceEntry->DiskInfo.Params.MPVolGuidsSize ) {
|
|
|
|
dwError = ERROR_INVALID_DATA;
|
|
__leave;
|
|
}
|
|
|
|
PrintStrList( ResourceEntry,
|
|
ResourceEntry->DiskInfo.Params.MPVolGuids,
|
|
ResourceEntry->DiskInfo.Params.MPVolGuidsSize );
|
|
|
|
//
|
|
// Make sure the offsets are correct in the VolGuid list.
|
|
// Note that it is possible for the list to be deleted and
|
|
// recreated after this validation, but that is not a problem (because
|
|
// when they are recreated they will have the correct offsets).
|
|
//
|
|
|
|
dwError = ValidateListOffsets( ResourceEntry,
|
|
ResourceEntry->DiskInfo.Params.MPVolGuids );
|
|
|
|
if ( ERROR_INVALID_DATA == dwError ) {
|
|
|
|
//
|
|
// At least one of the offsets is invalid. Possibly, the partition
|
|
// layout on the disk has been changed. Delete the existing
|
|
// list, and create a new one.
|
|
//
|
|
// This code should run infrequently...
|
|
//
|
|
// The partition layout might change if ASR runs and creates new partitions
|
|
// that don't match the previous system exactly. Since NTBACKUP saves the
|
|
// cluster DB information, the mount point list will be restored but won't
|
|
// match the actual "new" partition layout. ASR will insure that all the
|
|
// mount points and VolGuids on the system are created, so we should be able
|
|
// to simply delete and recreate the mount point list.
|
|
//
|
|
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_WARNING,
|
|
L"ProcessVolGuidList: Problem with existing VolGuid list. Deleting and recreating. \n" );
|
|
|
|
DeleteVolGuidList( ResourceEntry );
|
|
|
|
dwError = CreateVolGuidList( ResourceEntry );
|
|
|
|
if ( NO_ERROR != dwError ) {
|
|
__leave;
|
|
}
|
|
|
|
// Fall through and call SetupVolGuids...
|
|
|
|
} else if ( ERROR_INSUFFICIENT_BUFFER == dwError ) {
|
|
//
|
|
// The Volguid list is too large and likely corrupt. We cannot
|
|
// proceed.
|
|
//
|
|
|
|
__leave;
|
|
}
|
|
|
|
//
|
|
// For every VolGuid in the list, make sure they are assigned to the correct
|
|
// volumes on this system.
|
|
//
|
|
|
|
dwError = SetupVolGuids( ResourceEntry );
|
|
|
|
} __finally {
|
|
|
|
}
|
|
|
|
return dwError;
|
|
|
|
} // ProcessVolGuidList
|
|
|
|
|
|
DWORD
|
|
CreateVolGuidList(
|
|
IN OUT PDISK_RESOURCE ResourceEntry
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Create the VolGuid list. The list must be empty when this routine runs.
|
|
|
|
For each partition on this disk (identified by the ResourceEntry), get the byte
|
|
offset of that partition. Get the best VolGuid for that partition, and add
|
|
it to the list.
|
|
|
|
Arguments:
|
|
|
|
ResourceEntry - Pointer to the DISK_RESOURCE structure.
|
|
|
|
Return Value:
|
|
|
|
ERROR_INVALID_DATA - partition info in disk resource is invalid
|
|
|
|
Win32 error code.
|
|
|
|
--*/
|
|
{
|
|
PMOUNTIE_PARTITION entry;
|
|
|
|
HANDLE mountMgrHandle = INVALID_HANDLE_VALUE;
|
|
|
|
DWORD dwError = ERROR_SUCCESS;
|
|
DWORD nPartitions = MountiePartitionCount( &ResourceEntry->MountieInfo );
|
|
DWORD physicalDrive = ResourceEntry->DiskInfo.PhysicalDrive;
|
|
DWORD idx;
|
|
DWORD volumeNameLenChars; // Number of characters
|
|
DWORD newStrListLenBytes; // Number of bytes
|
|
|
|
WCHAR szGlobalDiskPartName[MAX_PATH];
|
|
WCHAR szVolumeName[MAX_PATH];
|
|
|
|
STR_LIST newStrList;
|
|
|
|
__try {
|
|
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"CreateVolGuidList: Creating new VolGuid list \n" );
|
|
|
|
EnterCriticalSection( &ResourceEntry->MPInfo.MPLock );
|
|
|
|
if ( ResourceEntry->DiskInfo.Params.MPVolGuids ||
|
|
0 != ResourceEntry->DiskInfo.Params.MPVolGuidsSize ) {
|
|
|
|
LeaveCriticalSection( &ResourceEntry->MPInfo.MPLock );
|
|
|
|
dwError = ERROR_INVALID_PARAMETER;
|
|
__leave;
|
|
}
|
|
|
|
LeaveCriticalSection( &ResourceEntry->MPInfo.MPLock );
|
|
|
|
ZeroMemory( &newStrList, sizeof(STR_LIST) );
|
|
|
|
dwError = DevfileOpen( &mountMgrHandle, MOUNTMGR_DEVICE_NAME );
|
|
|
|
if ( dwError != NO_ERROR ) {
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_WARNING,
|
|
L"CreateVolGuidList: Failed to open MountMgr %1!u! \n",
|
|
dwError );
|
|
__leave;
|
|
}
|
|
|
|
//
|
|
// Check each interesting partition. Since only "valid" partitions are
|
|
// saved in the MountieInfo structure, we will only look at those (ignoring those
|
|
// partitions that are not NTFS).
|
|
//
|
|
|
|
for ( idx = 0; idx < nPartitions; ++idx ) {
|
|
|
|
entry = MountiePartition( &ResourceEntry->MountieInfo, idx );
|
|
|
|
#if DBG
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"CreateVolGuidList: index %1!u! entry %2!x! \n", idx, entry );
|
|
#endif
|
|
|
|
if ( !entry ) {
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_WARNING,
|
|
L"CreateVolGuidList: no partition entry for index %1!u! \n", idx );
|
|
|
|
//
|
|
// Something bad happened to our data structures.
|
|
//
|
|
|
|
dwError = ERROR_INVALID_DATA;
|
|
__leave;
|
|
}
|
|
|
|
//
|
|
// Create the device name of the form:
|
|
// \\?\GLOBALROOT\Device\HarddiskX\PartitionY\ (uses trailing backslash)
|
|
//
|
|
|
|
(VOID) StringCchPrintf( szGlobalDiskPartName,
|
|
RTL_NUMBER_OF( szGlobalDiskPartName ),
|
|
GLOBALROOT_HARDDISK_PARTITION_FMT,
|
|
physicalDrive,
|
|
entry->PartitionNumber );
|
|
|
|
#if DBG
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"CreateVolGuidList: Using name (%1!ws!) \n",
|
|
szGlobalDiskPartName );
|
|
#endif
|
|
|
|
dwError = GetBestVolGuid( ResourceEntry,
|
|
szGlobalDiskPartName,
|
|
szVolumeName,
|
|
RTL_NUMBER_OF(szVolumeName) );
|
|
|
|
if ( NO_ERROR != dwError ) {
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_WARNING,
|
|
L"CreateVolGuidList: Unable to get valid VolGuid for (%1!ws!), error %2!u!\n ",
|
|
szGlobalDiskPartName,
|
|
dwError );
|
|
|
|
// Try next partition.
|
|
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Fix current VolGuid name.
|
|
//
|
|
// GetVolumeNameForVolumeMountPoint returns name of the form:
|
|
// \\?\Volume{-GUID-}\
|
|
//
|
|
// But we need the name to be in the form:
|
|
// \??\Volume{-GUID-}
|
|
//
|
|
|
|
volumeNameLenChars = wcslen( szVolumeName );
|
|
if ( !(MOUNTDEV_LOOKS_LIKE_ALT_VOLUME_GUID( szVolumeName, volumeNameLenChars ) ) ) {
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_WARNING,
|
|
L"CreateVolGuidList: Improper volume name format (%1!ws!) \n",
|
|
szVolumeName );
|
|
|
|
// Try next partition.
|
|
|
|
continue;
|
|
}
|
|
|
|
szVolumeName[1] = L'?';
|
|
|
|
if ( L'\\' == szVolumeName[volumeNameLenChars-1]) {
|
|
szVolumeName[volumeNameLenChars-1] = L'\0';
|
|
}
|
|
|
|
#if DBG
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"CreateVolGuidList: Fixed volume name (%1!ws!) \n",
|
|
szVolumeName );
|
|
#endif
|
|
|
|
//
|
|
// Add the new string to the list. If the new string is already in the list, this
|
|
// routine won't do anything and will return NO_ERROR.
|
|
//
|
|
|
|
dwError = AddStrToList( ResourceEntry,
|
|
szVolumeName,
|
|
entry->PartitionNumber,
|
|
&newStrList );
|
|
|
|
if ( NO_ERROR != dwError ) {
|
|
__leave;
|
|
}
|
|
|
|
}
|
|
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"CreateVolGuidList: Saving new VolGuid list \n" );
|
|
|
|
//
|
|
// Update the ResourceEntry with the new list information,
|
|
// and update the cluster database.
|
|
//
|
|
|
|
EnterCriticalSection( &ResourceEntry->MPInfo.MPLock );
|
|
|
|
if ( 0 == ResourceEntry->DiskInfo.Params.MPVolGuids &&
|
|
0 == ResourceEntry->DiskInfo.Params.MPVolGuidsSize ) {
|
|
|
|
ResourceEntry->DiskInfo.Params.MPVolGuids = newStrList.MultiSzList;
|
|
ResourceEntry->DiskInfo.Params.MPVolGuidsSize = newStrList.ListBytes;
|
|
} else {
|
|
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_WARNING,
|
|
L"CreateVolGuidList: VolGuid list is not empty - not saving... \n" );
|
|
|
|
LocalFree( newStrList.MultiSzList );
|
|
}
|
|
|
|
LeaveCriticalSection( &ResourceEntry->MPInfo.MPLock );
|
|
|
|
PrintStrList( ResourceEntry,
|
|
ResourceEntry->DiskInfo.Params.MPVolGuids,
|
|
ResourceEntry->DiskInfo.Params.MPVolGuidsSize );
|
|
|
|
dwError = PostMPInfoIntoRegistry( ResourceEntry );
|
|
|
|
} __finally {
|
|
|
|
if ( INVALID_HANDLE_VALUE != mountMgrHandle ) {
|
|
CloseHandle( mountMgrHandle );
|
|
}
|
|
|
|
}
|
|
|
|
return dwError;
|
|
|
|
} // CreateVolGuidList
|
|
|
|
|
|
DWORD
|
|
GetBestVolGuid(
|
|
PDISK_RESOURCE ResourceEntry,
|
|
PWSTR GlobalDiskPartName,
|
|
PWSTR VolumeName,
|
|
DWORD VolumeNameChars
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Get the best VolGuid for the specified device.
|
|
|
|
If the disk has no mountpoints, then just save the current VolGuid.
|
|
|
|
If the disk has mountpoints on it, then save the VolGuid
|
|
from the disk. All the mountpoints on the disk should have
|
|
the same VolGuid.
|
|
|
|
Arguments:
|
|
|
|
ResourceEntry - Pointer to the DISK_RESOURCE structure.
|
|
|
|
GlobalDiskPartName - device to get the VolGuid from.
|
|
|
|
\\?\GLOBALROOT\Device\HarddiskX\PartitionY\ (uses trailing backslash)
|
|
|
|
VolumeName - buffer to return the VolGuid.
|
|
|
|
VolumeNameChars - number of CHARS in VolumeName buffer.
|
|
|
|
Return Value:
|
|
|
|
NO_ERROR - valid VolGuid is returned.
|
|
|
|
Win32 error code - VolGuid could not be retrieved.
|
|
|
|
--*/
|
|
{
|
|
PWCHAR fullName = NULL;
|
|
PWCHAR mpOnDisk = NULL;
|
|
PWCHAR mpVolGuid = NULL;
|
|
|
|
HANDLE hVol;
|
|
|
|
DWORD dwError = NO_ERROR;
|
|
DWORD fullNameChars;
|
|
DWORD mpOnDiskChars;
|
|
DWORD mpVolGuidChars;
|
|
|
|
mpOnDiskChars = MAX_PATH * sizeof(WCHAR);
|
|
mpOnDisk = LocalAlloc( LPTR, mpOnDiskChars );
|
|
|
|
if ( !mpOnDisk ) {
|
|
dwError = GetLastError();
|
|
goto FnExit;
|
|
}
|
|
|
|
fullNameChars = MAX_PATH * sizeof(WCHAR) * 2;
|
|
fullName = LocalAlloc( LPTR, fullNameChars );
|
|
|
|
if ( !fullName ) {
|
|
dwError = GetLastError();
|
|
goto FnExit;
|
|
}
|
|
|
|
mpVolGuidChars = MAX_PATH * sizeof(WCHAR);
|
|
mpVolGuid = LocalAlloc( LPTR, mpVolGuidChars );
|
|
|
|
if ( !mpVolGuid ) {
|
|
dwError = GetLastError();
|
|
goto FnExit;
|
|
}
|
|
|
|
//
|
|
// Get the current VolGuid for this partition.
|
|
//
|
|
|
|
if ( !GetVolumeNameForVolumeMountPointW( GlobalDiskPartName,
|
|
VolumeName,
|
|
VolumeNameChars )) {
|
|
|
|
dwError = GetLastError();
|
|
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_WARNING,
|
|
L"GetBestVolGuid: GetVolumeNameForVolumeMountPoint for (%1!ws!) returned %2!u!\n",
|
|
GlobalDiskPartName,
|
|
dwError );
|
|
|
|
goto FnExit;
|
|
}
|
|
|
|
//
|
|
// See if there are mountpoints on the disk. Note
|
|
// we use the volume name we just received for this
|
|
// device.
|
|
//
|
|
|
|
hVol = FindFirstVolumeMountPoint( VolumeName,
|
|
mpOnDisk,
|
|
mpOnDiskChars );
|
|
|
|
if ( INVALID_HANDLE_VALUE == hVol ) {
|
|
|
|
//
|
|
// No mountpoints, just use the standard VolGuid.
|
|
//
|
|
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"GetBestVolGuid: Using standard volguid (%1!ws!) \n",
|
|
VolumeName );
|
|
|
|
goto FnExit;
|
|
}
|
|
|
|
//
|
|
// Disk has mountpoints on it. Now get the VolGuid from this
|
|
// mountpoint.
|
|
//
|
|
|
|
(VOID) StringCchCopy( fullName, fullNameChars, VolumeName );
|
|
(VOID) StringCchCat( fullName, fullNameChars, mpOnDisk );
|
|
|
|
if ( !GetVolumeNameForVolumeMountPointW( GlobalDiskPartName,
|
|
mpVolGuid,
|
|
mpVolGuidChars )) {
|
|
|
|
|
|
dwError = GetLastError();
|
|
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_WARNING,
|
|
L"CreateVolGuidList: Can't get volguid for (%1!ws!), error %2!u! \n",
|
|
fullName,
|
|
dwError );
|
|
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"GetBestVolGuid: Using standard volguid (%1!ws!) \n",
|
|
VolumeName );
|
|
|
|
//
|
|
// We couldn't get the VolGuid for the mountpoint, return success
|
|
// and use the standard VolGuid we got earlier.
|
|
//
|
|
|
|
dwError = ERROR_SUCCESS;
|
|
goto FnExit;
|
|
}
|
|
|
|
FindVolumeMountPointClose( hVol );
|
|
|
|
//
|
|
// Use the VolGuid based on the mountpoint.
|
|
//
|
|
|
|
(VOID) StringCchCopy( VolumeName, VolumeNameChars, mpVolGuid );
|
|
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"GetBestVolGuid: Using mountpoint volguid (%1!ws!) \n",
|
|
VolumeName );
|
|
|
|
FnExit:
|
|
|
|
LocalFree( fullName );
|
|
LocalFree( mpOnDisk );
|
|
LocalFree( mpVolGuid );
|
|
|
|
return dwError;
|
|
|
|
|
|
} // GetBestVolGuid
|
|
|
|
|
|
DWORD
|
|
AddStrToList(
|
|
IN PDISK_RESOURCE ResourceEntry,
|
|
IN PWSTR NewStr,
|
|
IN DWORD PartitionNumber,
|
|
IN OUT PSTR_LIST StrList
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Add the string to the MULTI_SZ list. Convert the partition number to a byte offset
|
|
so we don't rely on partition numbers.
|
|
|
|
List format will be:
|
|
|
|
ByteOffset1 Str1
|
|
ByteOffset1 Str2
|
|
ByteOffset1 Str3
|
|
ByteOffset2 Str1
|
|
ByteOffset2 Str2
|
|
ByteOffset3 Str1
|
|
... ...
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
PWCHAR listEntry = NULL;
|
|
|
|
DWORD listEntrySizeChars;
|
|
|
|
DWORD lenChars;
|
|
DWORD newStrLenChars;
|
|
DWORD listChars;
|
|
DWORD remainingLen;
|
|
DWORD dwError = ERROR_INVALID_DATA;
|
|
|
|
LARGE_INTEGER offset;
|
|
|
|
#if DBG
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"AddStrToList: Adding str (%1!ws!) \n", NewStr );
|
|
#endif
|
|
|
|
newStrLenChars = wcslen( NewStr );
|
|
|
|
if ( 0 == newStrLenChars ) {
|
|
|
|
//
|
|
// Something wrong with the string length.
|
|
//
|
|
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_WARNING,
|
|
L"AddStrToList: Invalid length: NewStrLen = %1!u! \n",
|
|
newStrLenChars );
|
|
|
|
goto FnExit;
|
|
}
|
|
|
|
//
|
|
// Indicate an error unless we can allocate and copy the info
|
|
// into the list. Calculate the minimum size needed, then get
|
|
// larger buffer. This buffer is temporary and freed later.
|
|
//
|
|
|
|
listEntrySizeChars = ( newStrLenChars + // Char length of parameter string
|
|
MAX_OFFSET_CHARS + // Char length of offset string
|
|
1 + // Room to change end of offset string to space and extend it
|
|
1 ) // Unicode NULL
|
|
* 2; // Make sure buffer is large enough
|
|
|
|
listEntry = LocalAlloc( LPTR, listEntrySizeChars * sizeof(WCHAR) );
|
|
|
|
if ( !listEntry ) {
|
|
dwError = GetLastError();
|
|
goto FnExit;
|
|
}
|
|
|
|
//
|
|
// Get the offset for the specified partition.
|
|
//
|
|
|
|
if ( !GetOffsetFromPartNo( PartitionNumber,
|
|
&ResourceEntry->MountieInfo,
|
|
&offset ) ) {
|
|
|
|
//
|
|
// Can't get the offset for the specified partition.
|
|
//
|
|
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_WARNING,
|
|
L"AddStrToList: GetOffsetFromPartNo failed \n" );
|
|
|
|
goto FnExit;
|
|
|
|
}
|
|
|
|
//
|
|
// Convert the offset into a string. Put the offset into listEntry.
|
|
//
|
|
|
|
_ui64tow( offset.QuadPart, listEntry, 16 );
|
|
lenChars = wcslen( listEntry );
|
|
|
|
if ( 0 == lenChars || lenChars >= MAX_OFFSET_CHARS ) {
|
|
|
|
//
|
|
// The length of the offset string is invalid.
|
|
//
|
|
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_WARNING,
|
|
L"AddStrToList: Invalid offset string length = %1!u! \n",
|
|
lenChars );
|
|
|
|
goto FnExit;
|
|
|
|
}
|
|
|
|
// Format will be:
|
|
// ByteOffset1 Str1
|
|
// ByteOffset1 Str2
|
|
// ByteOffset1 Str3
|
|
// ByteOffset2 Str1
|
|
// ByteOffset2 Str2
|
|
// ByteOffset3 Str1
|
|
// ... ...
|
|
|
|
//
|
|
// Change the end of the offset string to another character. Move the end of string
|
|
// out one character. This extra space was included when we allocated the buffer.
|
|
//
|
|
|
|
listEntry[lenChars+1] = UNICODE_NULL;
|
|
listEntry[lenChars] = SPACE_CHAR;
|
|
|
|
//
|
|
// One more check. Make sure enough space remaining for adding string.
|
|
//
|
|
|
|
remainingLen = listEntrySizeChars - wcslen( listEntry ) - 1;
|
|
|
|
#if DBG
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"AddStrToList: New string length %1!u! Remaining list entry length %2!u! \n",
|
|
newStrLenChars,
|
|
remainingLen );
|
|
#endif
|
|
|
|
if ( newStrLenChars >= remainingLen ) {
|
|
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_WARNING,
|
|
L"AddStrToList: New string length %1!u! larger than remaining list entry length %2!u! \n",
|
|
newStrLenChars,
|
|
remainingLen );
|
|
goto FnExit;
|
|
}
|
|
|
|
//
|
|
// Put the rest of the string in list entry.
|
|
//
|
|
|
|
wcsncat( listEntry, NewStr, remainingLen );
|
|
|
|
//
|
|
// If the string is already in the list, skip it.
|
|
//
|
|
|
|
if ( ClRtlMultiSzScan( ResourceEntry->DiskInfo.Params.MPVolGuids, listEntry ) ) {
|
|
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"AddStrToList: Skipping duplicate entry (%1!ws!) \n",
|
|
listEntry );
|
|
dwError = NO_ERROR;
|
|
goto FnExit;
|
|
}
|
|
|
|
//
|
|
// Note that ClRtlMultiSzAppend updates the number of CHARACTERS, but we
|
|
// need to have the number of BYTES in the property table. We will adjust
|
|
// this value later.
|
|
//
|
|
|
|
listChars = StrList->ListBytes / sizeof(WCHAR);
|
|
|
|
#if DBG
|
|
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"AddStrToList: StrList->MultiSzList at %1!p!, numBytes %2!u! \n",
|
|
StrList->MultiSzList,
|
|
StrList->ListBytes );
|
|
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"AddStrToList: Adding str entry: (%1!ws!) numChars %2!u! \n",
|
|
listEntry,
|
|
listChars );
|
|
#endif
|
|
|
|
dwError = ClRtlMultiSzAppend( &(StrList->MultiSzList),
|
|
&listChars,
|
|
listEntry );
|
|
|
|
//
|
|
// Convert the number of CHARACTERS back to bytes.
|
|
//
|
|
|
|
StrList->ListBytes = listChars * sizeof(WCHAR);
|
|
|
|
if ( ERROR_SUCCESS == dwError) {
|
|
#if DBG
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"AddStrToList: Added str, numBytes %1!u! numChars %2!u! \n",
|
|
StrList->ListBytes,
|
|
listChars );
|
|
#endif
|
|
|
|
} else {
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_WARNING,
|
|
L"AddStrToList: Unable to append MultiSz string for (%1!ws!), failed %x \n",
|
|
NewStr,
|
|
dwError );
|
|
}
|
|
|
|
FnExit:
|
|
|
|
if ( listEntry ) {
|
|
LocalFree( listEntry );
|
|
}
|
|
|
|
return dwError;
|
|
|
|
} // AddStrToList
|
|
|
|
|
|
DWORD
|
|
SetupVolGuids(
|
|
IN OUT PDISK_RESOURCE ResourceEntry
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Add every VolGuid in the existing MULTI_SZ VolGuid list to the system.
|
|
Each volume will only have one VolGuid, and the VolGuid will be the
|
|
same for all nodes.
|
|
|
|
Remove any other VolGuids currently assigned to the volumes.
|
|
|
|
Arguments:
|
|
|
|
ResourceEntry - Pointer to the DISK_RESOURCE structure.
|
|
|
|
Return Value:
|
|
|
|
Win32 error code.
|
|
|
|
--*/
|
|
{
|
|
PWCHAR currentStr;
|
|
PWCHAR volGuid;
|
|
|
|
HANDLE mountMgrHandle = INVALID_HANDLE_VALUE;
|
|
|
|
DWORD physicalDrive = ResourceEntry->DiskInfo.PhysicalDrive;
|
|
DWORD currentStrLenChars = 0;
|
|
DWORD dwError = NO_ERROR;
|
|
DWORD partitionNo;
|
|
DWORD count;
|
|
|
|
LARGE_INTEGER offset;
|
|
|
|
WCHAR szDiskPartName[MAX_PATH];
|
|
|
|
__try {
|
|
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"SetupVolGuids: Processing VolGuid list \n" );
|
|
|
|
EnterCriticalSection( &ResourceEntry->MPInfo.MPLock );
|
|
|
|
dwError = DevfileOpen( &mountMgrHandle, MOUNTMGR_DEVICE_NAME );
|
|
|
|
if ( dwError != NO_ERROR ) {
|
|
__leave;
|
|
}
|
|
|
|
//
|
|
// Parse through the list.
|
|
//
|
|
|
|
for ( currentStr = (PWCHAR)ResourceEntry->DiskInfo.Params.MPVolGuids,
|
|
currentStrLenChars = wcslen( currentStr ) ;
|
|
currentStrLenChars ;
|
|
currentStr += currentStrLenChars + 1,
|
|
currentStrLenChars = wcslen( currentStr ) ) {
|
|
|
|
offset.QuadPart = 0;
|
|
|
|
#if DBG
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"SetupVolGuids: CurrentStr (%1!ws!), numChars %2!u! \n",
|
|
currentStr,
|
|
currentStrLenChars );
|
|
#endif
|
|
|
|
//
|
|
// Convert the offset from a string to a large integer value.
|
|
//
|
|
|
|
count = swscanf( currentStr, L"%I64x ", &offset.QuadPart );
|
|
|
|
if ( 0 == count ) {
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_WARNING,
|
|
L"SetupVolGuids: Unable to parse offset from currentStr (%1!ws!) \n",
|
|
currentStr );
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Convert the offset to a partition number.
|
|
//
|
|
|
|
if ( !GetPartNoFromOffset( &offset, &ResourceEntry->MountieInfo, &partitionNo ) ) {
|
|
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_WARNING,
|
|
L"SetupVolGuids: Unable to convert offset ( %1!08X!%2!08X! ) to partition number \n",
|
|
offset.HighPart,
|
|
offset.LowPart ); // couldn't get !I64X! to work...
|
|
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Get a pointer to the VolGuid data, just after the byte offset.
|
|
//
|
|
|
|
volGuid = wcsstr( currentStr, MOUNTDEV_WSZ_VOLUME_GUID_PREFIX );
|
|
|
|
if ( !volGuid ) {
|
|
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_WARNING,
|
|
L"SetupVolGuids: Unable to find volume string in current list entry (%1!ws) \n",
|
|
currentStr );
|
|
|
|
continue;
|
|
}
|
|
|
|
#if DBG
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"SetupVolGuids: Using VolGuid (%1!ws!) \n",
|
|
volGuid );
|
|
#endif
|
|
|
|
//
|
|
// Create the device name of the form:
|
|
// \Device\HarddiskX\PartitionY (no trailing backslash)
|
|
//
|
|
|
|
(VOID) StringCchPrintf( szDiskPartName,
|
|
RTL_NUMBER_OF( szDiskPartName ),
|
|
DEVICE_HARDDISK_PARTITION_FMT,
|
|
physicalDrive,
|
|
partitionNo );
|
|
|
|
#if DBG
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"SetupVolGuids: Using device name (%1!ws!) \n",
|
|
szDiskPartName );
|
|
#endif
|
|
|
|
dwError = AssignDevice( mountMgrHandle, volGuid, szDiskPartName );
|
|
|
|
if ( NO_ERROR != dwError &&
|
|
STATUS_OBJECT_NAME_COLLISION != dwError ) {
|
|
|
|
// Assign device will return: 0xC0000035 STATUS_OBJECT_NAME_COLLISION
|
|
// if we are setting a VolGuid that was previously set. This is not
|
|
// a problem.
|
|
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_WARNING,
|
|
L"SetupVolGuids: Unable to assign VolGuid to device, error %1!u! \n",
|
|
dwError );
|
|
|
|
// Continue processing with error...
|
|
|
|
} else {
|
|
|
|
//
|
|
// Only remove the other VolGuids if this assignment worked.
|
|
//
|
|
|
|
RemoveExcessVolGuids( mountMgrHandle, volGuid, szDiskPartName );
|
|
}
|
|
|
|
dwError = STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
} __finally {
|
|
|
|
if ( INVALID_HANDLE_VALUE != mountMgrHandle ) {
|
|
CloseHandle( mountMgrHandle );
|
|
}
|
|
|
|
LeaveCriticalSection( &ResourceEntry->MPInfo.MPLock );
|
|
|
|
}
|
|
|
|
return dwError;
|
|
|
|
} // SetupVolGuids
|
|
|
|
|
|
DWORD
|
|
AssignDevice(
|
|
HANDLE MountMgrHandle,
|
|
PWCHAR MountName,
|
|
PWCHAR VolumeDevName
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Put the specified MountName (i.e. mount point name) into the mount manager's internal table
|
|
of mount points.
|
|
|
|
Inputs:
|
|
|
|
MountMgrHandle - Handle to the mount manager. The caller is responsible for
|
|
opening and closing this handle.
|
|
|
|
MountName - Mountpoint name of the form:
|
|
|
|
\??\Volume{-GUID-} - note prefix "\??\" and no trailing backslash.
|
|
\DosDevices\X: - works if a drive letter is not already assigned
|
|
|
|
VolumeDevName - Volume device name. Can be one of the following forms (note that case is
|
|
important). The "#" is a zero-based device number (and partition number
|
|
as appropriate).
|
|
|
|
\Device\CdRom#
|
|
\Device\Floppy#
|
|
\Device\HarddiskVolume#
|
|
\Device\Harddisk#\Partition#
|
|
|
|
Return value:
|
|
|
|
A Win32 error code.
|
|
|
|
--*/
|
|
{
|
|
PMOUNTMGR_CREATE_POINT_INPUT input;
|
|
|
|
DWORD status;
|
|
|
|
USHORT mountNameLenBytes;
|
|
USHORT volumeDevNameLenBytes;
|
|
|
|
USHORT inputlengthBytes;
|
|
|
|
if ( INVALID_HANDLE_VALUE == MountMgrHandle ) {
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
mountNameLenBytes = wcslen( MountName ) * sizeof(WCHAR);
|
|
volumeDevNameLenBytes = wcslen( VolumeDevName ) * sizeof(WCHAR);
|
|
|
|
inputlengthBytes = sizeof(MOUNTMGR_CREATE_POINT_INPUT) + mountNameLenBytes + volumeDevNameLenBytes;
|
|
|
|
input = (PMOUNTMGR_CREATE_POINT_INPUT)LocalAlloc( LPTR, inputlengthBytes );
|
|
|
|
if ( !input ) {
|
|
return ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
|
|
input->SymbolicLinkNameOffset = sizeof(MOUNTMGR_CREATE_POINT_INPUT);
|
|
input->SymbolicLinkNameLength = mountNameLenBytes;
|
|
input->DeviceNameOffset = input->SymbolicLinkNameOffset +
|
|
input->SymbolicLinkNameLength;
|
|
input->DeviceNameLength = volumeDevNameLenBytes;
|
|
|
|
RtlCopyMemory((PCHAR)input + input->SymbolicLinkNameOffset,
|
|
MountName, mountNameLenBytes);
|
|
RtlCopyMemory((PCHAR)input + input->DeviceNameOffset,
|
|
VolumeDevName, volumeDevNameLenBytes);
|
|
|
|
status = DevfileIoctl( MountMgrHandle,
|
|
IOCTL_MOUNTMGR_CREATE_POINT,
|
|
input,
|
|
inputlengthBytes,
|
|
NULL,
|
|
0,
|
|
NULL );
|
|
|
|
LocalFree( input );
|
|
|
|
return status;
|
|
|
|
} // AssignDevice
|
|
|
|
|
|
DWORD
|
|
RemoveExcessVolGuids(
|
|
HANDLE MountMgrHandle,
|
|
PWCHAR MountName,
|
|
PWCHAR VolumeDevName
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Remove all VolGuids except the specified MountName (VolGuid) from the
|
|
mount manager's internal table of mount points.
|
|
|
|
Inputs:
|
|
|
|
MountMgrHandle - Handle to the mount manager. The caller is responsible for
|
|
opening and closing this handle.
|
|
|
|
MountName - VolGuid to keep. Format:
|
|
|
|
\??\Volume{-GUID-} - note prefix "\??\" and no trailing backslash.
|
|
|
|
VolumeDevName - Volume device name. Can be one of the following forms (note that case is
|
|
important). The "#" is a zero-based device number (and partition number
|
|
as appropriate).
|
|
|
|
\Device\CdRom#
|
|
\Device\Floppy#
|
|
\Device\HarddiskVolume#
|
|
\Device\Harddisk#\Partition#
|
|
|
|
Return value:
|
|
|
|
A Win32 error code.
|
|
|
|
--*/
|
|
{
|
|
DWORD dwError = NO_ERROR;
|
|
|
|
PMOUNTMGR_MOUNT_POINT input = NULL;
|
|
PMOUNTMGR_MOUNT_POINTS mountPoints = NULL;
|
|
PMOUNTMGR_MOUNT_POINT point;
|
|
|
|
DWORD len = wcslen( VolumeDevName ) * sizeof(WCHAR);
|
|
DWORD bytesReturned;
|
|
DWORD idx;
|
|
|
|
DWORD outputLen;
|
|
DWORD inputLen;
|
|
DWORD pointLen;
|
|
|
|
inputLen = INPUT_BUFFER_LEN + len;
|
|
input = LocalAlloc( LPTR, inputLen );
|
|
|
|
if ( !input ) {
|
|
dwError = GetLastError();
|
|
goto FnExit;
|
|
}
|
|
|
|
input->SymbolicLinkNameOffset = 0;
|
|
input->SymbolicLinkNameLength = 0;
|
|
input->UniqueIdOffset = 0;
|
|
input->UniqueIdLength = 0;
|
|
input->DeviceNameOffset = sizeof(MOUNTMGR_MOUNT_POINT);
|
|
input->DeviceNameLength = (USHORT) len;
|
|
|
|
CopyMemory((PCHAR)input + input->DeviceNameOffset, VolumeDevName, len );
|
|
|
|
if ( VolumeDevName[1] == L'\\' ) {
|
|
// convert Dos name to NT name
|
|
((PWCHAR)(input + input->DeviceNameOffset))[1] = L'?';
|
|
}
|
|
|
|
outputLen = OUTPUT_BUFFER_LEN;
|
|
mountPoints = LocalAlloc( LPTR, outputLen );
|
|
|
|
if ( !mountPoints ) {
|
|
dwError = GetLastError();
|
|
goto FnExit;
|
|
}
|
|
|
|
if ( !DeviceIoControl( MountMgrHandle,
|
|
IOCTL_MOUNTMGR_QUERY_POINTS,
|
|
input,
|
|
inputLen,
|
|
mountPoints,
|
|
outputLen,
|
|
&bytesReturned,
|
|
NULL ) ) {
|
|
|
|
dwError = GetLastError();
|
|
|
|
if ( STATUS_BUFFER_OVERFLOW != dwError ) {
|
|
goto FnExit;
|
|
}
|
|
|
|
dwError = NO_ERROR;
|
|
outputLen = mountPoints->Size;
|
|
LocalFree( mountPoints );
|
|
|
|
mountPoints = LocalAlloc( LPTR, outputLen );
|
|
|
|
if ( !mountPoints ) {
|
|
dwError = GetLastError();
|
|
goto FnExit;
|
|
}
|
|
|
|
if ( !DeviceIoControl( MountMgrHandle,
|
|
IOCTL_MOUNTMGR_QUERY_POINTS,
|
|
input,
|
|
inputLen,
|
|
mountPoints,
|
|
outputLen,
|
|
&bytesReturned,
|
|
NULL ) ) {
|
|
dwError = GetLastError();
|
|
}
|
|
}
|
|
|
|
if ( NO_ERROR != dwError ) {
|
|
(DiskpLogEvent)(
|
|
RESOURCE_TYPE,
|
|
LOG_WARNING,
|
|
L"RemoveExcessVolGuids: mountmgr query returns error %1!u! \n",
|
|
dwError );
|
|
goto FnExit;
|
|
}
|
|
|
|
for ( idx = 0; idx < mountPoints->NumberOfMountPoints; ++idx ) {
|
|
point = &mountPoints->MountPoints[idx];
|
|
|
|
//
|
|
// Delete any VolGuids that mountmgr has that don't match the
|
|
// MountName (VolGuid) parameter.
|
|
//
|
|
|
|
if ( VOL_GUID_STRING_LEN * sizeof(WCHAR) == point->SymbolicLinkNameLength &&
|
|
0 != memcmp( (PCHAR)mountPoints + point->SymbolicLinkNameOffset,
|
|
MountName,
|
|
point->SymbolicLinkNameLength ) ) {
|
|
|
|
RemoveVolGuid( MountMgrHandle,
|
|
(PWCHAR)((PCHAR)mountPoints + point->SymbolicLinkNameOffset),
|
|
point->SymbolicLinkNameLength );
|
|
|
|
}
|
|
}
|
|
|
|
FnExit:
|
|
|
|
LocalFree( mountPoints );
|
|
LocalFree( input );
|
|
|
|
return dwError;
|
|
|
|
} // RemoveExcessVolGuids
|
|
|
|
|
|
DWORD
|
|
RemoveVolGuid(
|
|
HANDLE MountMgrHandle,
|
|
PWCHAR VolGuid,
|
|
USHORT VolGuidSize
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Delete the specified VolGuid from mount manager's internal
|
|
table of mount points.
|
|
|
|
Inputs:
|
|
|
|
MountMgrHandle - Handle to the mount manager. The caller is responsible for
|
|
opening and closing this handle.
|
|
|
|
VolGuid - volume GUID to be deleted. Not NULL terminated.
|
|
|
|
\??\Volume{-GUID-} - note prefix "\??\" and no trailing backslash.
|
|
|
|
VolGuidSize - size (in bytes) of volume GUID.
|
|
|
|
Return value:
|
|
|
|
A Win32 error code.
|
|
|
|
--*/
|
|
{
|
|
DWORD dwError = NO_ERROR;
|
|
|
|
PMOUNTMGR_MOUNT_POINT input = NULL;
|
|
PMOUNTMGR_MOUNT_POINTS output = NULL;
|
|
|
|
DWORD bytesReturned;
|
|
|
|
DWORD inputLen;
|
|
DWORD outputLen;
|
|
|
|
//
|
|
// Allocate extra space so the VolGuid string will be null
|
|
// terminated in the input structure. This will allow us
|
|
// to display the VolGuid in the cluster log. We don't have
|
|
// to explictly null terminate the string because the input
|
|
// buffer is zeroed at allocation time.
|
|
//
|
|
|
|
inputLen = INPUT_BUFFER_LEN + VolGuidSize + sizeof(WCHAR);
|
|
input = LocalAlloc( LPTR, inputLen );
|
|
|
|
if ( !input ) {
|
|
dwError = GetLastError();
|
|
goto FnExit;
|
|
}
|
|
|
|
outputLen = sizeof(MOUNTMGR_MOUNT_POINTS) + 3*MAX_PATH*sizeof(WCHAR);
|
|
output = LocalAlloc( LPTR, outputLen );
|
|
|
|
input->UniqueIdOffset = 0;
|
|
input->UniqueIdLength = 0;
|
|
input->DeviceNameOffset = 0;
|
|
input->DeviceNameLength = 0;
|
|
input->SymbolicLinkNameOffset = sizeof(MOUNTMGR_MOUNT_POINT);
|
|
input->SymbolicLinkNameLength = VolGuidSize;
|
|
|
|
CopyMemory((PCHAR)input + input->SymbolicLinkNameOffset, VolGuid, VolGuidSize );
|
|
|
|
if ( !DeviceIoControl( MountMgrHandle,
|
|
IOCTL_MOUNTMGR_DELETE_POINTS,
|
|
input,
|
|
inputLen,
|
|
output,
|
|
outputLen,
|
|
&bytesReturned,
|
|
NULL ) ) {
|
|
|
|
dwError = GetLastError();
|
|
|
|
(DiskpLogEvent)(
|
|
RESOURCE_TYPE,
|
|
LOG_WARNING,
|
|
L"RemoveVolGuid: deleting VolGuid %1!ws! returns error %2!u! \n",
|
|
(PWCHAR)((PCHAR)input + input->SymbolicLinkNameOffset),
|
|
dwError );
|
|
|
|
} else {
|
|
|
|
(DiskpLogEvent)(
|
|
RESOURCE_TYPE,
|
|
LOG_INFORMATION,
|
|
L"RemoveVolGuid: deleted VolGuid %1!ws! \n",
|
|
(PWCHAR)((PCHAR)input + input->SymbolicLinkNameOffset) );
|
|
|
|
}
|
|
|
|
FnExit:
|
|
|
|
LocalFree( input );
|
|
LocalFree( output );
|
|
|
|
return dwError;
|
|
|
|
} // RemoveVolGuid
|
|
|
|
|
|
DWORD
|
|
DeleteVolGuidList(
|
|
PDISK_RESOURCE ResourceEntry
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Delete the list from the DISK_RESOURCE structure, if it exists (free the
|
|
memmory). Also deletes the information from the cluster database.
|
|
|
|
Arguments:
|
|
|
|
ResourceEntry - Pointer to the DISK_RESOURCE structure.
|
|
|
|
Return Value:
|
|
|
|
NO_ERROR - The list was deleted.
|
|
|
|
ERROR_NOT_READY - The mount point information was not yet initialized.
|
|
|
|
Win32 error code.
|
|
|
|
--*/
|
|
{
|
|
DWORD dwError = NO_ERROR;
|
|
|
|
//
|
|
// Mount point structures not initialized (i.e. critical section). Don't continue.
|
|
//
|
|
|
|
if ( !ResourceEntry->MPInfo.Initialized ) {
|
|
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_WARNING,
|
|
L"DeleteVolGuidList: Mount point info not initialized. List not deleted. \n" );
|
|
|
|
dwError = ERROR_NOT_READY;
|
|
goto FnExit;
|
|
}
|
|
|
|
EnterCriticalSection( &ResourceEntry->MPInfo.MPLock );
|
|
|
|
//
|
|
// If existing list, free it.
|
|
//
|
|
|
|
if ( ResourceEntry->DiskInfo.Params.MPVolGuids ) {
|
|
LocalFree( ResourceEntry->DiskInfo.Params.MPVolGuids );
|
|
ResourceEntry->DiskInfo.Params.MPVolGuidsSize = 0;
|
|
ResourceEntry->DiskInfo.Params.MPVolGuids = NULL;
|
|
|
|
dwError = ClusterRegDeleteValue( ResourceEntry->ResourceParametersKey,
|
|
CLUSREG_NAME_PHYSDISK_MPVOLGUIDS );
|
|
|
|
//
|
|
// If the update failed and the disk is not yet online, it will fail with
|
|
// ERROR_SHARING_PAUSED. Just return the error. If the caller really,
|
|
// really, really wants the cluster database cleaned up, they can
|
|
// use the PostMPInfoIntoRegistry call to create a thread to do this
|
|
// work.
|
|
|
|
if ( NO_ERROR != dwError ) {
|
|
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_WARNING,
|
|
L"DeleteVolGuidList: Unable to delete VolGuid from cluster database %1!u! \n",
|
|
dwError );
|
|
}
|
|
}
|
|
|
|
LeaveCriticalSection( &ResourceEntry->MPInfo.MPLock );
|
|
|
|
FnExit:
|
|
|
|
#if DBG
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"DeleteVolGuidList: returns %1!u! \n",
|
|
dwError );
|
|
#endif
|
|
|
|
return dwError;
|
|
|
|
} // DeleteVolGuidList
|
|
|
|
|
|
BOOL
|
|
IsMountPointAllowed(
|
|
PWSTR MpName,
|
|
PWSTR SourceVol,
|
|
PWSTR TargetVol,
|
|
PDISK_RESOURCE ResourceEntry
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Verify that the mount point is allowed. There are several reasons why the mount
|
|
point would not be allowed.
|
|
|
|
At this point, the source volume will be accessible. If the source were offline,
|
|
we wouldn't even know about it, and we wouldn't even get to this routine. The
|
|
fact that the source is accessible allows us to do some things differently (i.e. we
|
|
can talk to the disk if needed).
|
|
|
|
|
|
Dependencies:
|
|
|
|
If source disk S has a mount point to target disk T ( S:\tdir --> T: ), then
|
|
target T is dependent on source S and source S must be brought online before
|
|
target T is online.
|
|
|
|
Quorum drive restrictions:
|
|
|
|
Generally, the quorum drive can have a mount point to target disk T as long as
|
|
target disk T is in same group as the quorum disk.
|
|
|
|
A mount point from clustered source S to quorum target Q is not allowed because
|
|
the quorum drive Q cannot be dependent on another resource.
|
|
|
|
A mount point from quorum source Q to a nonclustered target C is invalid.
|
|
|
|
Mount points to non-clustered disks:
|
|
|
|
These types of mountpoints should not be used.
|
|
|
|
Configurations supported:
|
|
|
|
C is a non-clustered disk.
|
|
X, Y are clustered disks, not quorum disks.
|
|
Q is quorum disk.
|
|
|
|
Source Target Status
|
|
------ ------ ------------------------------------------------------------
|
|
C --> Q Not supported. Log error to system event log.
|
|
C --> X Not supported. Log error to system event log.
|
|
X --> C Not supported. We never process non-clustered target C.
|
|
X --> Q Invalid. Quorum drive cannot be dependent on another resource.
|
|
X --> Y Valid if drive Y is dependent on drive X (X online first).
|
|
Q --> X Valid if drive X is dependent on drive Q (Q online first).
|
|
Q --> C Not supported. We never process target C.
|
|
|
|
Arguments:
|
|
|
|
MpName - Possible mount point. This will either be a mount point or a drive letter
|
|
(which is actually a mount point). Format can be:
|
|
|
|
x:\ [Note trailing backslash!]
|
|
x:\some-mp-name\ [Note trailing backslash!]
|
|
x:\some-dir\some-mp-name\ [Note trailing backslash!]
|
|
\\?\Volume{GUID}\some-mp-name\ [Note trailing backslash!]
|
|
|
|
SourceVol - Mount point target volume name. Optional.
|
|
Format:
|
|
\\?\Volume{GUID}\ [Note trailing backslash!]
|
|
|
|
TargetVol - Mount point target volume name. Must always be specified.
|
|
Format:
|
|
\\?\Volume{GUID}\ [Note trailing backslash!]
|
|
|
|
ResourceEntry - Pointer to the DISK_RESOURCE structure for either the
|
|
source or target volume. If SourceVol is specified,
|
|
will be source structure. If SourceVol is not specified,
|
|
will be target structure.
|
|
|
|
Return Value:
|
|
|
|
TRUE if the mount point is allowed.
|
|
|
|
--*/
|
|
{
|
|
PWSTR srcGroup = NULL;
|
|
PWSTR targetGroup = NULL;
|
|
|
|
DWORD srcSignature = 0;
|
|
DWORD targetSignature = 0;
|
|
DWORD quorumSignature;
|
|
|
|
DWORD dwError = NO_ERROR;
|
|
DWORD messageId = 0;
|
|
DWORD srcGroupChars;
|
|
DWORD targetGroupChars;
|
|
|
|
BOOL mpAllowed = TRUE;
|
|
BOOL sigIsClustered;
|
|
BOOL dependencyCorrect;
|
|
BOOL sameGroup;
|
|
|
|
#if DBG
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"IsMountPointAllowed: MP Name (%1!ws!) \n",
|
|
MpName );
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"IsMountPointAllowed: SourceVolume (%1!ws!) \n",
|
|
SourceVol );
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"IsMountPointAllowed: TargetVolume (%1!ws!) \n",
|
|
TargetVol );
|
|
#endif
|
|
|
|
//
|
|
// Since the drive letter is also a mountpoint, a drive letter is valid.
|
|
//
|
|
|
|
if ( MPIsDriveLetter( MpName ) ) {
|
|
#if DBG
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"IsMountPointAllowed: Valid MP: MP is a drive letter \n" );
|
|
#endif
|
|
mpAllowed = TRUE;
|
|
goto FnExit;
|
|
}
|
|
|
|
if ( SourceVol ) {
|
|
|
|
srcSignature = ResourceEntry->DiskInfo.Params.Signature;
|
|
|
|
//
|
|
// Get the signature of the target drive.
|
|
// If this fails, we can't use the mountpoint.
|
|
//
|
|
|
|
dwError = GetSignatureForVolume( ResourceEntry, TargetVol, &targetSignature );
|
|
|
|
} else {
|
|
|
|
targetSignature = ResourceEntry->DiskInfo.Params.Signature;
|
|
|
|
//
|
|
// Get the signature of the source drive. This drive is accessible (or
|
|
// we wouldn't even have the mount point info yet) but we cannot assume it
|
|
// is a clustered drive. If this fails, we can't use the mountpoint.
|
|
//
|
|
|
|
dwError = GetSignatureForVolume( ResourceEntry, MpName, &srcSignature );
|
|
}
|
|
|
|
|
|
if ( NO_ERROR != dwError || !srcSignature || !targetSignature ) {
|
|
//
|
|
// If we are checking source mount points and target is not online (i.e. the
|
|
// dependency might be correct), we don't log an error if we can't get the
|
|
// signature. In this case, when we bring the target online, we'll check
|
|
// the dependencies at that time.
|
|
//
|
|
|
|
if ( SourceVol ) {
|
|
mpAllowed = TRUE;
|
|
} else {
|
|
mpAllowed = FALSE;
|
|
messageId = RES_DISK_INVALID_MP_SIG_UNAVAILABLE;
|
|
}
|
|
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
(mpAllowed ? LOG_INFORMATION : LOG_WARNING),
|
|
L"IsMountPointAllowed: Unable to get signature from volume, error %1!u! \n",
|
|
dwError );
|
|
|
|
goto FnExit;
|
|
}
|
|
|
|
//
|
|
// If source points back to target, this mount point is not allowed. Even though
|
|
// the mount point code seems to allow this, there are some strange circular
|
|
// dependencies that show up.
|
|
//
|
|
|
|
if ( srcSignature == targetSignature ) {
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_WARNING,
|
|
L"IsMountPointAllowed: Invalid MP: Source and target volumes are the same device \n" );
|
|
mpAllowed = FALSE;
|
|
messageId = RES_DISK_INVALID_MP_SOURCE_EQUAL_TARGET;
|
|
goto FnExit;
|
|
}
|
|
|
|
//
|
|
// Get quorum signature. If this fails, assume the mount point is not allowed.
|
|
//
|
|
|
|
dwError = GetQuorumSignature( &quorumSignature );
|
|
|
|
if ( NO_ERROR != dwError ) {
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_WARNING,
|
|
L"IsMountPointAllowed: Unable to get quorum signature, error %1!u! \n",
|
|
dwError );
|
|
mpAllowed = FALSE;
|
|
messageId = RES_DISK_INVALID_MP_QUORUM_SIG_UNAVAILABLE;
|
|
goto FnExit;
|
|
}
|
|
|
|
//
|
|
// If we can't enumerate the cluster disk signatures, assume that this mount
|
|
// point is not allowed. If SourceVol was specified, then we need to check if
|
|
// the target is clustered. If SourceVol was not specified, then we are
|
|
// processing the clustered target and need to insure the source is clustered.
|
|
//
|
|
|
|
if ( SourceVol ) {
|
|
dwError = CheckSignatureClustered( ResourceEntry,
|
|
targetSignature,
|
|
&sigIsClustered,
|
|
&targetGroup );
|
|
|
|
} else{
|
|
dwError = CheckSignatureClustered( ResourceEntry,
|
|
srcSignature,
|
|
&sigIsClustered,
|
|
&srcGroup );
|
|
}
|
|
|
|
if ( NO_ERROR != dwError ) {
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_WARNING,
|
|
L"IsMountPointAllowed: Unable to enumerate disk signatures, error %1!u! \n",
|
|
dwError );
|
|
mpAllowed = FALSE;
|
|
messageId = RES_DISK_INVALID_MP_SIG_ENUMERATION_FAILED;
|
|
goto FnExit;
|
|
}
|
|
|
|
//
|
|
// Insure that neither the source or target of the mount point is non-clustered.
|
|
// This check eliminates these configurations:
|
|
// C --> X
|
|
// C --> Q
|
|
// X --> C
|
|
// Q --> C
|
|
//
|
|
|
|
if ( !sigIsClustered ) {
|
|
|
|
if ( SourceVol ) {
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_WARNING,
|
|
L"IsMountPointAllowed: Invalid MP: Target volume is non-clustered \n" );
|
|
messageId = RES_DISK_INVALID_MP_TARGET_NOT_CLUSTERED;
|
|
|
|
} else {
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_WARNING,
|
|
L"IsMountPointAllowed: Invalid MP: Source volume is non-clustered \n" );
|
|
messageId = RES_DISK_INVALID_MP_SOURCE_NOT_CLUSTERED;
|
|
}
|
|
mpAllowed = FALSE;
|
|
goto FnExit;
|
|
}
|
|
|
|
//
|
|
// Have to check whether we retrieved the group names after checking
|
|
// whether signatures are clustered.
|
|
//
|
|
|
|
if ( (!targetGroup && !srcGroup) ) {
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_WARNING,
|
|
L"IsMountPointAllowed: Unable to enumerate disk signatures, error %1!u! \n",
|
|
dwError );
|
|
mpAllowed = FALSE;
|
|
messageId = RES_DISK_INVALID_MP_SIG_ENUMERATION_FAILED;
|
|
goto FnExit;
|
|
}
|
|
|
|
//
|
|
// If the target is the quorum drive, the mount point is not allowed because
|
|
// the quorum cannot be dependent on another disk resource. We already know
|
|
// that the source and target are different devices from an ealier check.
|
|
//
|
|
|
|
if ( quorumSignature == targetSignature ) {
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_WARNING,
|
|
L"IsMountPointAllowed: Invalid MP: target sig %1!x! is quorum disk, source sig %2!x! is clustered \n",
|
|
targetSignature,
|
|
srcSignature );
|
|
mpAllowed = FALSE;
|
|
messageId = RES_DISK_INVALID_MP_TARGET_IS_QUORUM;
|
|
goto FnExit;
|
|
}
|
|
|
|
//
|
|
// Check that the source and target are in the same group. If not, then we don't
|
|
// need to check the dependencies. If source/target is quorum, we can't go through
|
|
// the dependency check code because a deadlock can occur during quorum online.
|
|
//
|
|
// Similar to what we did previously, but note that if SourceVol was passed in,
|
|
// we specify srcSignature to CheckSignatureClustered. This will return the group
|
|
// name for the source volume. We do similar call for target volume.
|
|
//
|
|
|
|
if ( SourceVol ) {
|
|
dwError = CheckSignatureClustered( ResourceEntry,
|
|
srcSignature,
|
|
&sigIsClustered,
|
|
&srcGroup );
|
|
|
|
} else{
|
|
dwError = CheckSignatureClustered( ResourceEntry,
|
|
targetSignature,
|
|
&sigIsClustered,
|
|
&targetGroup );
|
|
}
|
|
|
|
if ( NO_ERROR != dwError || !targetGroup || !srcGroup ) {
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_WARNING,
|
|
L"IsMountPointAllowed: Unable to enumerate disk signatures, error %1!u! \n",
|
|
dwError );
|
|
mpAllowed = FALSE;
|
|
messageId = RES_DISK_INVALID_MP_SIG_ENUMERATION_FAILED;
|
|
goto FnExit;
|
|
}
|
|
|
|
#if DBG
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"IsMountPointAllowed: Source group: %1!ws! \n",
|
|
srcGroup );
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"IsMountPointAllowed: Target group: %1!ws! \n",
|
|
targetGroup );
|
|
#endif
|
|
|
|
//
|
|
// If source and target in different groups, we are done.
|
|
//
|
|
|
|
srcGroupChars = wcslen( srcGroup );
|
|
targetGroupChars = wcslen( targetGroup );
|
|
|
|
if ( srcGroupChars == targetGroupChars &&
|
|
0 == wcsncmp( srcGroup, targetGroup, srcGroupChars ) ) {
|
|
|
|
sameGroup = TRUE;
|
|
} else {
|
|
sameGroup = FALSE;
|
|
}
|
|
|
|
//
|
|
// If source and target are not in the same group, just display
|
|
// the invalid dependency message and exit.
|
|
//
|
|
|
|
if ( !sameGroup ) {
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_WARNING,
|
|
L"IsMountPointAllowed: Invalid MP: Source and target are in different groups \n",
|
|
dwError );
|
|
mpAllowed = FALSE;
|
|
messageId = RES_DISK_INVALID_MP_INVALID_DEPENDENCIES;
|
|
goto FnExit;
|
|
}
|
|
|
|
//
|
|
// If source and target are in the same group and source is quorum disk,
|
|
// then the mount point is allowed since the quorum disk always comes
|
|
// online first. So even though dependencies might not be correct for
|
|
// quorum source pointed to clustered target (Q --> X) case, we won't
|
|
// display an error message.
|
|
//
|
|
|
|
if ( quorumSignature == srcSignature && sameGroup ) {
|
|
#if DBG
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"IsMountPointAllowed: Valid MP: source sig %1!x! is quorum, target sig %2!x! is clustered \n",
|
|
srcSignature,
|
|
targetSignature );
|
|
#endif
|
|
mpAllowed = TRUE;
|
|
goto FnExit;
|
|
}
|
|
|
|
//
|
|
// We have one possibility left:
|
|
// X --> Y
|
|
//
|
|
// This is valid only if the dependencies are set up correctly.
|
|
//
|
|
|
|
dependencyCorrect = FALSE;
|
|
dwError = CheckDependencies( ResourceEntry,
|
|
srcSignature,
|
|
targetSignature,
|
|
&dependencyCorrect );
|
|
|
|
if ( NO_ERROR != dwError ) {
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_WARNING,
|
|
L"IsMountPointAllowed: Unable to enumerate disk dependencies, error %1!u! \n",
|
|
dwError );
|
|
mpAllowed = FALSE;
|
|
messageId = RES_DISK_INVALID_MP_ENUM_DISK_DEP_FAILED;
|
|
goto FnExit;
|
|
}
|
|
|
|
if ( dependencyCorrect ) {
|
|
#if DBG
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"IsMountPointAllowed: Valid MP: Dependencies are correct \n" );
|
|
#endif
|
|
mpAllowed = TRUE;
|
|
goto FnExit;
|
|
}
|
|
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_WARNING,
|
|
L"IsMountPointAllowed: Invalid MP: Dependencies are incorrect \n",
|
|
MpName );
|
|
|
|
//
|
|
// If we get here, the mount point is not allowed.
|
|
//
|
|
|
|
mpAllowed = FALSE;
|
|
messageId = RES_DISK_INVALID_MP_INVALID_DEPENDENCIES;
|
|
|
|
FnExit:
|
|
|
|
if ( !mpAllowed && messageId ) {
|
|
|
|
// Log event...
|
|
|
|
ClusResLogSystemEventByKey2(ResourceEntry->ResourceKey,
|
|
LOG_UNUSUAL,
|
|
messageId,
|
|
MpName,
|
|
TargetVol);
|
|
}
|
|
|
|
if ( srcGroup ) {
|
|
LocalFree( srcGroup );
|
|
}
|
|
|
|
if ( targetGroup ) {
|
|
LocalFree( targetGroup );
|
|
}
|
|
|
|
return mpAllowed;
|
|
|
|
|
|
} // IsMountPointAllowed
|
|
|
|
|
|
DWORD
|
|
CheckDependencies(
|
|
PDISK_RESOURCE ResourceEntry,
|
|
DWORD SrcSignature,
|
|
DWORD TargetSignature,
|
|
PBOOL DependencyCorrect
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Determine if the dependency between the source volume and target volume is set up
|
|
correctly. Since we are using the cluster APIs, they should insure that the
|
|
resources are in the same group and have dependencies set.
|
|
|
|
Arguments:
|
|
|
|
ResourceEntry - Pointer to the DISK_RESOURCE structure.
|
|
|
|
SrcSignature - Disk signature of the source volume.
|
|
|
|
TargetSignature - Disk signature of the targe volume.
|
|
|
|
DependencyCorrect - Indicates whether the dependency is set up correctly between the
|
|
source and target. If set up correctly, this will be returned
|
|
as TRUE.
|
|
|
|
Return Value:
|
|
|
|
Win32 error code.
|
|
|
|
--*/
|
|
{
|
|
DWORD dwError = NO_ERROR;
|
|
|
|
DEPENDENCY_INFO dependInfo;
|
|
|
|
ZeroMemory( &dependInfo, sizeof(DEPENDENCY_INFO) );
|
|
|
|
//
|
|
// We need to find the source's resources first.
|
|
//
|
|
|
|
dependInfo.ResourceEntry = ResourceEntry;
|
|
dependInfo.SrcSignature = SrcSignature;
|
|
dependInfo.TargetSignature = TargetSignature;
|
|
dependInfo.DependencyCorrect = FALSE;
|
|
|
|
//
|
|
// Worst case assume that the dependency is invalid.
|
|
//
|
|
|
|
*DependencyCorrect = FALSE;
|
|
|
|
dwError = ResUtilEnumResources( NULL,
|
|
PHYSICAL_DISK_WSTR,
|
|
DependencyCallback,
|
|
&dependInfo
|
|
);
|
|
|
|
//
|
|
// STOP_CLUSTER_ENUMERATIONS is our way to indicate that the
|
|
// enumerations stopped (possibly early). Check for this return
|
|
// value and also if the DependencyCorrect flag was set to indicate
|
|
// status to the caller.
|
|
//
|
|
|
|
if ( STOP_CLUSTER_ENUMERATIONS == dwError ) {
|
|
dwError = NO_ERROR;
|
|
|
|
if ( dependInfo.DependencyCorrect ) {
|
|
*DependencyCorrect = TRUE;
|
|
}
|
|
}
|
|
|
|
|
|
return dwError;
|
|
|
|
} // CheckDependencies
|
|
|
|
|
|
DWORD
|
|
DependencyCallback(
|
|
RESOURCE_HANDLE hOriginal,
|
|
RESOURCE_HANDLE hResource,
|
|
PVOID lpParams
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
For each enumerated disk resource, get the signature and see if it matches the
|
|
mount point target signature (passed in the DEPENDENCY_INFO structure). If it
|
|
does not match, return success so that the disk enumeration continues.
|
|
|
|
If the enumerated resource signature matches the mount point target signature,
|
|
then check the cluster dependencies and make sure they are correct. Once we
|
|
have had a match on the signatures, we need to return an error to stop the
|
|
disk enumeration, so we use STOP_CLUSTER_ENUMERATIONS as that special error
|
|
value.
|
|
|
|
If the cluster dependencies are acceptable, the DependencyCorrect flag will be
|
|
set to TRUE in the DEPENDENCY_INFO structure.
|
|
|
|
Arguments:
|
|
|
|
hOriginal - Handle to the original resource. Not used.
|
|
|
|
hResource - Handle to a cluster resource of type PHYSICAL_DISK.
|
|
|
|
lpParams - Pointer to DEPENDENCY_INFO structure.
|
|
|
|
Return Value:
|
|
|
|
STOP_CLUSTER_ENUMERATIONS - Special flag to stop the enumeration process.
|
|
|
|
Win32 error code.
|
|
|
|
--*/
|
|
{
|
|
PDEPENDENCY_INFO dependInfo = lpParams;
|
|
|
|
DWORD dwSignature;
|
|
DWORD dwError = NO_ERROR;
|
|
|
|
//
|
|
// Get the disk info and parse the signature from it.
|
|
//
|
|
|
|
dwError = GetSignatureFromRegistry( dependInfo->ResourceEntry,
|
|
hResource,
|
|
&dwSignature );
|
|
|
|
|
|
if ( NO_ERROR != dwError ) {
|
|
return dwError;
|
|
}
|
|
|
|
//
|
|
// This code checks that the mount point SOURCE is online before
|
|
// the mount point TARGET.
|
|
//
|
|
// Check if we have a resource handle to the target disk or to
|
|
// a different disk. If the resource is the target disk,
|
|
// enumerate the dependencies and make sure they are correct.
|
|
//
|
|
|
|
if ( dwSignature == dependInfo->TargetSignature ) {
|
|
|
|
dwError = EnumSigDependencies( dependInfo->ResourceEntry,
|
|
hResource,
|
|
dependInfo->SrcSignature,
|
|
&dependInfo->DependencyCorrect );
|
|
|
|
//
|
|
// If the dependency check did not get an error, set a fake
|
|
// error to make the disk enumeration stop.
|
|
//
|
|
|
|
if ( NO_ERROR == dwError ) {
|
|
dwError = STOP_CLUSTER_ENUMERATIONS;
|
|
}
|
|
}
|
|
|
|
return dwError;
|
|
|
|
} // DependencyCallback
|
|
|
|
|
|
DWORD
|
|
EnumSigDependencies(
|
|
PDISK_RESOURCE ResourceEntry,
|
|
RESOURCE_HANDLE DependentResource,
|
|
DWORD DependsOnSignature,
|
|
PBOOL DependencyCorrect
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Check that the cluster disk dependencies are correct between the source and
|
|
target of the mount point.
|
|
|
|
To do this, we open the dependent resource and use the cluster APIs to enumerate
|
|
all the disk resources dependencies. For each dependency found, check for a
|
|
match of the DependsOnSignature. If the signatures match, the dependency is
|
|
correct and we are done. Otherwise, keep checking all the dependencies until
|
|
we exhaust the list or find a match.
|
|
|
|
Note: Dependency is brought online before the DependentResource.
|
|
|
|
Arguments:
|
|
|
|
ResourceEntry - Pointer to the DISK_RESOURCE structure.
|
|
|
|
DependentResource - Resource Handle to check all the dependencies.
|
|
|
|
DependsOnSignature - Signature of a possibly dependent disk. This disk must be
|
|
brought online before the DependentResource.
|
|
|
|
DependencyCorrect - Flag set to TRUE when the cluster dependencies between
|
|
the DependentResource and it's dependency (identified by the
|
|
DependsOnSignature) are correct.
|
|
|
|
Return Value:
|
|
|
|
Win32 error code.
|
|
|
|
--*/
|
|
{
|
|
HRESENUM resEnum = NULL;
|
|
HCLUSTER hCluster = NULL;
|
|
HRESOURCE dependsOnResource = NULL;
|
|
|
|
DWORD idx;
|
|
DWORD dwError = NO_ERROR;
|
|
DWORD enumType;
|
|
DWORD nameLen;
|
|
DWORD signature;
|
|
|
|
WCHAR enumNameW[MAX_PATH * 2];
|
|
|
|
__try {
|
|
|
|
hCluster = OpenCluster( NULL );
|
|
|
|
if ( NULL == hCluster ) {
|
|
dwError = GetLastError();
|
|
__leave;
|
|
}
|
|
|
|
//
|
|
// Open an enumerator for iterating through the resources.
|
|
//
|
|
|
|
resEnum = ClusterResourceOpenEnum( DependentResource,
|
|
CLUSTER_RESOURCE_ENUM_DEPENDS );
|
|
|
|
if ( !resEnum ) {
|
|
dwError = GetLastError();
|
|
__leave;
|
|
}
|
|
|
|
//
|
|
// Iterate through the dependencies.
|
|
//
|
|
|
|
idx = 0;
|
|
while ( TRUE ) {
|
|
|
|
nameLen = MAX_PATH;
|
|
ZeroMemory( enumNameW, sizeof(enumNameW) );
|
|
|
|
dwError = ClusterResourceEnum( resEnum,
|
|
idx,
|
|
&enumType,
|
|
enumNameW,
|
|
&nameLen );
|
|
|
|
if ( ERROR_NO_MORE_ITEMS == dwError ) {
|
|
|
|
//
|
|
// The list is exhausted. Indicate no error and leave. This
|
|
// just means we checked all the dependencies and we didn't find
|
|
// a match.
|
|
//
|
|
|
|
dwError = NO_ERROR;
|
|
__leave;
|
|
}
|
|
|
|
if ( ERROR_SUCCESS != dwError ) {
|
|
|
|
//
|
|
// Some type of error, we have to stop processing.
|
|
//
|
|
__leave;
|
|
}
|
|
|
|
//
|
|
// Now we have the name (in the form of a string) of a resource we are
|
|
// dependent on. We need to get the signature and compare to the
|
|
// signature passed in.
|
|
//
|
|
|
|
dependsOnResource = OpenClusterResource( hCluster,
|
|
enumNameW );
|
|
|
|
if ( NULL == dependsOnResource ) {
|
|
dwError = GetLastError();
|
|
__leave;
|
|
|
|
}
|
|
|
|
//
|
|
// Get the disk signature from the resources disk info.
|
|
//
|
|
|
|
dwError = GetSignatureFromRegistry( ResourceEntry,
|
|
dependsOnResource,
|
|
&signature );
|
|
|
|
//
|
|
// If the signature passed in matches the signature we are dependent on,
|
|
// then the dependency is correct. Otherwise, we have to keep looking.
|
|
//
|
|
|
|
if ( NO_ERROR == dwError && signature == DependsOnSignature ) {
|
|
*DependencyCorrect = TRUE;
|
|
dwError = NO_ERROR;
|
|
__leave;
|
|
}
|
|
|
|
//
|
|
// Look at the next enumeration resource.
|
|
//
|
|
|
|
CloseClusterResource( dependsOnResource );
|
|
dependsOnResource = NULL;
|
|
idx++;
|
|
}
|
|
|
|
} __finally {
|
|
|
|
if ( dependsOnResource ) {
|
|
CloseClusterResource( dependsOnResource );
|
|
}
|
|
|
|
if ( resEnum ) {
|
|
ClusterResourceCloseEnum( resEnum );
|
|
}
|
|
|
|
if ( hCluster ) {
|
|
CloseCluster( hCluster );
|
|
}
|
|
}
|
|
|
|
return dwError;
|
|
|
|
|
|
} // EnumSigDependencies
|
|
|
|
|
|
DWORD
|
|
CheckSignatureClustered(
|
|
PDISK_RESOURCE ResourceEntry,
|
|
DWORD Signature,
|
|
PBOOL IsClustered,
|
|
PWSTR *GroupName
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Determine if the specified disk signature belongs to a clustered disk.
|
|
Enumerates the cluster physical disks and tries to find a signature
|
|
match.
|
|
|
|
The enumeration returns STOP_CLUSTER_ENUMERATIONS when it has found
|
|
a matching signature. This special error code is to stop the disk
|
|
enumeration.
|
|
|
|
If the disk is clustered, the disk name will be returned in the
|
|
GroupName parameter. The caller is responsible for freeing this
|
|
storage.
|
|
|
|
Arguments:
|
|
|
|
ResourceEntry - Pointer to the DISK_RESOURCE structure.
|
|
|
|
Signature - Disk signature to be checked.
|
|
|
|
IsClustered - Flag indicating disk is clustered. If TRUE, disk is a
|
|
clustered disk.
|
|
|
|
GroupName - If signature represents a clustered disk, this parameter
|
|
will point to the group name (NULL terminated Unicode string).
|
|
The caller is responsible for freeing this buffer.
|
|
|
|
Return Value:
|
|
|
|
Win32 error code.
|
|
|
|
--*/
|
|
{
|
|
DWORD dwError = NO_ERROR;
|
|
|
|
SIG_INFO sigInfo;
|
|
|
|
ZeroMemory( &sigInfo, sizeof(SIG_INFO) );
|
|
|
|
sigInfo.ResourceEntry = ResourceEntry;
|
|
sigInfo.Signature = Signature;
|
|
sigInfo.Clustered = FALSE;
|
|
sigInfo.GroupName = NULL;
|
|
|
|
*IsClustered = FALSE;
|
|
*GroupName = NULL;
|
|
|
|
dwError = ResUtilEnumResources( NULL,
|
|
PHYSICAL_DISK_WSTR,
|
|
SigInfoCallback,
|
|
&sigInfo
|
|
);
|
|
|
|
if ( STOP_CLUSTER_ENUMERATIONS == dwError && sigInfo.Clustered ) {
|
|
dwError = NO_ERROR;
|
|
*IsClustered = TRUE;
|
|
*GroupName = sigInfo.GroupName;
|
|
}
|
|
|
|
return dwError;
|
|
|
|
} // CheckSignatureClustered
|
|
|
|
|
|
DWORD
|
|
SigInfoCallback(
|
|
RESOURCE_HANDLE hOriginal,
|
|
RESOURCE_HANDLE hResource,
|
|
PVOID lpParams
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
For each enumerated disk resource, get the signature and see if it matches the
|
|
specified disk signature (passed in the SIG_INFO structure). If it does not
|
|
match, return success so that the disk enumeration continues.
|
|
|
|
If the enumerated resource signature matches the mount point source signature,
|
|
sets the Clustered flag in the SIG_INFO structure to TRUE.
|
|
|
|
Return the resource group name information.
|
|
|
|
Arguments:
|
|
|
|
hOriginal - Handle to the original resource. Not used.
|
|
|
|
hResource - Handle to a cluster resource of type PHYSICAL_DISK.
|
|
|
|
lpParams - Pointer to SIGN_INFO structure.
|
|
|
|
Return Value:
|
|
|
|
STOP_CLUSTER_ENUMERATIONS - Special flag to stop the enumeration process.
|
|
|
|
Win32 error code.
|
|
|
|
--*/
|
|
{
|
|
PSIG_INFO sigInfo = lpParams;
|
|
PWSTR groupName;
|
|
|
|
DWORD dwSignature;
|
|
DWORD dwError = NO_ERROR;
|
|
DWORD groupNameChars;
|
|
|
|
CLUSTER_RESOURCE_STATE resState;
|
|
|
|
|
|
//
|
|
// Get the disk info and parse the signature from it.
|
|
//
|
|
|
|
dwError = GetSignatureFromRegistry( sigInfo->ResourceEntry,
|
|
hResource,
|
|
&dwSignature );
|
|
|
|
|
|
if ( NO_ERROR != dwError ) {
|
|
return dwError;
|
|
}
|
|
|
|
if ( dwSignature == sigInfo->Signature ) {
|
|
sigInfo->Clustered = TRUE;
|
|
|
|
groupNameChars = MAX_GROUP_NAME_LENGTH;
|
|
groupName = LocalAlloc( LPTR, groupNameChars * sizeof(WCHAR) );
|
|
|
|
if ( groupName ) {
|
|
|
|
//
|
|
// Get the group name for this resource.
|
|
//
|
|
|
|
resState = GetClusterResourceState( hResource,
|
|
NULL,
|
|
0,
|
|
groupName,
|
|
&groupNameChars );
|
|
|
|
//
|
|
// If we can't get the group name, we don't need to report
|
|
// an error. For error case, just free the group name
|
|
// buffer.
|
|
//
|
|
|
|
if ( ClusterResourceStateUnknown == resState ) {
|
|
LocalFree( groupName );
|
|
sigInfo->GroupName = NULL;
|
|
} else {
|
|
sigInfo->GroupName = groupName;
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Return an error to stop the enumeration.
|
|
//
|
|
|
|
dwError = STOP_CLUSTER_ENUMERATIONS;
|
|
|
|
}
|
|
|
|
return dwError;
|
|
|
|
} // SigInfoCallback
|
|
|
|
|
|
DWORD
|
|
GetSignatureFromRegistry(
|
|
PDISK_RESOURCE ResourceEntry,
|
|
RESOURCE_HANDLE hResource,
|
|
DWORD *dwSignature
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Get the signature for the given volume from the cluster from the registry.
|
|
|
|
Arguments:
|
|
|
|
ResourceEntry - Pointer to the DISK_RESOURCE structure.
|
|
|
|
hResource - Handle to a cluster resource of type PHYSICAL_DISK.
|
|
|
|
Signature - On success, the signature is returned into this pointer.
|
|
|
|
Return Value:
|
|
|
|
Win32 error code.
|
|
|
|
--*/
|
|
{
|
|
|
|
DWORD dwError = NO_ERROR;
|
|
DWORD signature = 0;
|
|
|
|
HKEY hKey = NULL;
|
|
HKEY hParamKey = NULL;
|
|
|
|
*dwSignature = 0;
|
|
|
|
hKey = GetClusterResourceKey( hResource, KEY_READ );
|
|
|
|
if ( !hKey ) {
|
|
dwError = GetLastError();
|
|
goto FnExit;
|
|
}
|
|
|
|
dwError = ClusterRegOpenKey( hKey,
|
|
CLUSREG_KEYNAME_PARAMETERS,
|
|
KEY_READ,
|
|
&hParamKey );
|
|
|
|
if ( NO_ERROR != dwError ) {
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_WARNING,
|
|
L"GetSignatureFromRegistry: DiskRes %1!p! ClusterRegOpenKey failed %2!u! \n",
|
|
ResourceEntry,
|
|
dwError );
|
|
goto FnExit;
|
|
}
|
|
|
|
dwError = ResUtilGetDwordValue( hParamKey,
|
|
CLUSREG_NAME_PHYSDISK_SIGNATURE,
|
|
&signature,
|
|
0 );
|
|
|
|
if ( NO_ERROR != dwError ) {
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_WARNING,
|
|
L"GetSignatureFromRegistry: DiskRes %1!p! ResUtilGetDwordValue failed %2!u! \n",
|
|
ResourceEntry,
|
|
dwError );
|
|
goto FnExit;
|
|
}
|
|
|
|
*dwSignature = signature;
|
|
|
|
FnExit:
|
|
|
|
if ( hParamKey ) {
|
|
ClusterRegCloseKey( hParamKey );
|
|
}
|
|
|
|
if ( hKey ) {
|
|
ClusterRegCloseKey( hKey );
|
|
}
|
|
|
|
return dwError;
|
|
|
|
} // GetSignatureFromRegistry
|
|
|
|
|
|
|
|
DWORD
|
|
GetSignatureForVolume(
|
|
PDISK_RESOURCE ResourceEntry,
|
|
PWSTR MpName,
|
|
PDWORD Signature
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Get the signature for the given volume. The signature is found by issuing
|
|
IOCTL_DISK_GET_DRIVE_LAYOUT_EX or IOCTL_DISK_GET_DRIVE_LAYOUT.
|
|
|
|
The volume must be online for this to work and not reserved by another node.
|
|
|
|
Arguments:
|
|
|
|
ResourceEntry - Pointer to the DISK_RESOURCE structure.
|
|
|
|
MpName - Possible mount point. This will either be a mount point or a drive letter
|
|
(which is actually a mount point). Can also be a simple VolGUID.
|
|
Format can be:
|
|
|
|
x:\ [Note trailing backslash!]
|
|
x:\some-mp-name\ [Note trailing backslash!]
|
|
x:\some-dir\some-mp-name\ [Note trailing backslash!]
|
|
\\?\Volume{GUID}\some-mp-name\ [Note trailing backslash!]
|
|
\\?\Volume{GUID}\ [Note trailing backslash!]
|
|
|
|
Signature - On success, the signature is returned into this pointer.
|
|
|
|
Return Value:
|
|
|
|
Win32 error code.
|
|
|
|
--*/
|
|
{
|
|
PDRIVE_LAYOUT_INFORMATION_EX layoutEx = NULL;
|
|
PDRIVE_LAYOUT_INFORMATION layout = NULL;
|
|
|
|
PWCHAR deviceName = NULL;
|
|
|
|
HANDLE handle = NULL;
|
|
DWORD bytesReturned;
|
|
DWORD dwError = NO_ERROR;
|
|
DWORD deviceNameChars = 0;
|
|
|
|
if ( !MpName || !Signature ) {
|
|
dwError = ERROR_INVALID_PARAMETER;
|
|
goto FnExit;
|
|
}
|
|
|
|
*Signature = 0;
|
|
|
|
#if DBG
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"GetSignatureForVolume: Checking mount point (%1!ws!) \n",
|
|
MpName );
|
|
|
|
#endif
|
|
|
|
//
|
|
// Make copy of mount point for passing to CreateFile.
|
|
//
|
|
|
|
deviceNameChars = MAX_PATH;
|
|
deviceName = LocalAlloc( LPTR, deviceNameChars * sizeof(WCHAR) );
|
|
|
|
if ( !deviceName ) {
|
|
dwError = GetLastError();
|
|
goto FnExit;
|
|
}
|
|
|
|
//
|
|
// Create device name according to the mount point type.
|
|
//
|
|
|
|
if ( MOUNTDEV_LOOKS_LIKE_VOLUME_GUID( MpName, wcslen( MpName ) ) ) {
|
|
|
|
//
|
|
// Need to change the \??\Volume{ to \\?\Volume{ and truncate everything
|
|
// after the VolGUID. No trailing backslash.
|
|
//
|
|
|
|
wcsncpy( deviceName, MpName, VOL_GUID_STRING_LEN );
|
|
(WCHAR)*( deviceName + 1 ) = L'\\';
|
|
|
|
} else if ( MOUNTDEV_LOOKS_LIKE_ALT_VOLUME_GUID( MpName, wcslen( MpName ) ) ) {
|
|
|
|
//
|
|
// Form is acceptable as-is. Just truncate everything after the VolGUID.
|
|
// No trailing backslash.
|
|
//
|
|
|
|
wcsncpy( deviceName, MpName, VOL_GUID_STRING_LEN );
|
|
|
|
} else if ( MOUNTDEV_LOOKS_LIKE_DISK_PATH( MpName, wcslen( MpName ) ) ) {
|
|
|
|
//
|
|
// Make the name into the form: \\?\x: [Note: no trailing backslash!]
|
|
//
|
|
|
|
wcsncpy( deviceName, CREATE_FILE_PREFIX, wcslen( CREATE_FILE_PREFIX ) );
|
|
wcsncat( deviceName, MpName, 2 );
|
|
|
|
} else {
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"GetSignatureForVolume: Invalid mount point specified (%1!ws!) \n",
|
|
MpName );
|
|
dwError = ERROR_INVALID_PARAMETER;
|
|
goto FnExit;
|
|
}
|
|
|
|
#if DBG
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"GetSignatureForVolume: CreateFile using %1!ws! \n",
|
|
deviceName );
|
|
|
|
#endif
|
|
|
|
handle = CreateFileW( deviceName,
|
|
GENERIC_READ,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
NULL );
|
|
|
|
if ( INVALID_HANDLE_VALUE == handle ) {
|
|
dwError = GetLastError();
|
|
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_WARNING,
|
|
L"GetSignatureForVolume: CreateFile for device (%1!ws!) failed, error %2!u! \n",
|
|
deviceName,
|
|
dwError );
|
|
|
|
goto FnExit;
|
|
}
|
|
|
|
//
|
|
// Try IOCTL_DISK_GET_DRIVE_LAYOUT_EX first. If it fails, try with
|
|
// IOCTL_DISK_GET_DRIVE_LAYOUT.
|
|
//
|
|
|
|
layoutEx = DoIoctlAndAllocate( handle,
|
|
IOCTL_DISK_GET_DRIVE_LAYOUT_EX,
|
|
0,
|
|
0,
|
|
&bytesReturned );
|
|
|
|
if ( layoutEx ) {
|
|
|
|
//
|
|
// Get the signature from the returned structure and return it to
|
|
// the caller.
|
|
//
|
|
|
|
if ( PARTITION_STYLE_MBR == layoutEx->PartitionStyle ) {
|
|
*Signature = layoutEx->Mbr.Signature;
|
|
|
|
} else if ( PARTITION_STYLE_GPT == layoutEx->PartitionStyle ) {
|
|
|
|
//
|
|
// Since our signatures won't handle the GPT GUID, we have to
|
|
// simulate a signature.
|
|
//
|
|
|
|
*Signature = ClusterHashGuid(layoutEx->Gpt.DiskId);
|
|
}
|
|
|
|
goto FnExit;
|
|
}
|
|
|
|
//
|
|
// Fall through and try the old IOCTL.
|
|
//
|
|
|
|
dwError = GetLastError();
|
|
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_WARNING,
|
|
L"GetSignatureForVolume: IOCTL_DISK_GET_DRIVE_LAYOUT_EX failed, error %1!u! \n",
|
|
dwError );
|
|
|
|
dwError = NO_ERROR;
|
|
|
|
layout = DoIoctlAndAllocate( handle,
|
|
IOCTL_DISK_GET_DRIVE_LAYOUT,
|
|
0,
|
|
0,
|
|
&bytesReturned );
|
|
|
|
if ( !layout ) {
|
|
|
|
dwError = GetLastError();
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_WARNING,
|
|
L"GetSignatureForVolume: IOCTL_DISK_GET_DRIVE_LAYOUT failed, error %1!u! \n",
|
|
dwError );
|
|
|
|
goto FnExit;
|
|
}
|
|
|
|
//
|
|
// Get the signature from the returned structure and return it to
|
|
// the caller.
|
|
//
|
|
|
|
*Signature = layout->Signature;
|
|
|
|
FnExit:
|
|
|
|
if ( layoutEx ) {
|
|
free( layoutEx );
|
|
}
|
|
|
|
if ( layout ) {
|
|
free( layout );
|
|
}
|
|
|
|
if ( deviceName ) {
|
|
LocalFree( deviceName );
|
|
}
|
|
|
|
if ( handle ) {
|
|
CloseHandle( handle );
|
|
}
|
|
|
|
#if DBG
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"GetSignatureForVolume: Returning signature %1!x! for mount point (%2!ws!) \n",
|
|
*Signature,
|
|
MpName );
|
|
#endif
|
|
|
|
return dwError;
|
|
|
|
} // GetSignatureForVolume
|
|
|
|
|
|
BOOL
|
|
GetOffsetFromPartNo(
|
|
DWORD PartitionNo,
|
|
PMOUNTIE_INFO Info,
|
|
PLARGE_INTEGER Offset
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Given the partition number and the drive layout, return the byte offset for the
|
|
specified partition.
|
|
|
|
Arguments:
|
|
|
|
PartitionNo - Supplies the partition number. Zero is invalid since partition zero
|
|
represents the entire disk.
|
|
|
|
Info - Pointer to MOUNTIE_INFO based on drive layout information.
|
|
|
|
Offset - Pointer to hold the returned byte offset for the partition. Space is
|
|
allocated by the caller.
|
|
|
|
Return Value:
|
|
|
|
TRUE if successful.
|
|
|
|
--*/
|
|
{
|
|
PMOUNTIE_PARTITION entry;
|
|
DWORD idx;
|
|
DWORD partitionCount;
|
|
BOOL retVal = FALSE;
|
|
|
|
if ( !PartitionNo || !Info || !Offset ) {
|
|
goto FnExit;
|
|
}
|
|
|
|
if ( 0 == Info->Volume ) {
|
|
goto FnExit;
|
|
}
|
|
|
|
#if DBG
|
|
(DiskpLogEvent)(
|
|
RESOURCE_TYPE,
|
|
LOG_INFORMATION,
|
|
L"GetOffsetFromPartNo: partition %1!u! \n",
|
|
PartitionNo );
|
|
#endif
|
|
|
|
Offset->QuadPart = 0; // Offset of zero is invalid. This will indicate an error.
|
|
|
|
partitionCount = Info->Volume->PartitionCount;
|
|
entry = Info->Volume->Partition;
|
|
|
|
for ( idx = 0; idx < partitionCount; ++idx, ++entry) {
|
|
|
|
#if DBG
|
|
(DiskpLogEvent)(
|
|
RESOURCE_TYPE,
|
|
LOG_INFORMATION,
|
|
L"GetOffsetFromPartNo: index %1!u! offset %2!x! \n",
|
|
idx,
|
|
entry->StartingOffset.LowPart );
|
|
#endif
|
|
|
|
if ( entry->PartitionNumber == PartitionNo ) {
|
|
|
|
Offset->QuadPart = entry->StartingOffset.QuadPart;
|
|
retVal = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
FnExit:
|
|
|
|
return retVal;
|
|
|
|
} // GetOffsetFromPartNo
|
|
|
|
|
|
|
|
BOOL
|
|
GetPartNoFromOffset(
|
|
PLARGE_INTEGER Offset,
|
|
PMOUNTIE_INFO Info,
|
|
PDWORD PartitionNumber
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Given the offset and the drive layout, return the partition number for the specified offset.
|
|
|
|
Arguments:
|
|
|
|
Offset - Pointer to the byte offset.
|
|
|
|
Info - Pointer to MOUNTIE_INFO based on drive layout information.
|
|
|
|
PartitionNo - Pointer to hold the returned partition number. Space is allocated by the
|
|
caller.
|
|
|
|
|
|
Return Value:
|
|
|
|
TRUE if successful.
|
|
|
|
--*/
|
|
{
|
|
PMOUNTIE_PARTITION entry;
|
|
DWORD idx;
|
|
DWORD partitionCount;
|
|
BOOL retVal = FALSE;
|
|
|
|
if ( !Offset->QuadPart || !Info || !PartitionNumber) {
|
|
goto FnExit;
|
|
}
|
|
|
|
if ( 0 == Info->Volume ) {
|
|
goto FnExit;
|
|
}
|
|
|
|
*PartitionNumber = 0; // Partition zero is invalid. This will indicate an error.
|
|
|
|
partitionCount = Info->Volume->PartitionCount;
|
|
entry = Info->Volume->Partition;
|
|
|
|
for ( idx = 0; idx < partitionCount; ++idx, ++entry ) {
|
|
|
|
if ( entry->StartingOffset.QuadPart == Offset->QuadPart ) {
|
|
*PartitionNumber = entry->PartitionNumber;
|
|
retVal = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
FnExit:
|
|
|
|
return retVal;
|
|
|
|
} // GetPartNoFromOffset
|
|
|
|
|
|
VOID
|
|
PrintStrList(
|
|
PDISK_RESOURCE ResourceEntry,
|
|
LPWSTR MultiSzList,
|
|
DWORD ListBytes
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Display the list in the cluster log.
|
|
|
|
Arguments:
|
|
|
|
ResourceEntry - Pointer to the DISK_RESOURCE structure.
|
|
|
|
MultiSzList - REG_MULTI_SZ string
|
|
|
|
ListBytes - Number of bytes in MultiSzList, not number of WCHARs!
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
PWSTR currentStr;
|
|
PWCHAR data;
|
|
|
|
LARGE_INTEGER offset;
|
|
|
|
DWORD currentStrLenChars = 0;
|
|
DWORD count;
|
|
|
|
if ( !ResourceEntry || !MultiSzList || 0 == ListBytes ) {
|
|
return;
|
|
}
|
|
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L" Offset String \n" );
|
|
|
|
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"================ ====================================== \n" );
|
|
|
|
|
|
EnterCriticalSection( &ResourceEntry->MPInfo.MPLock );
|
|
|
|
currentStr = (PWCHAR)MultiSzList;
|
|
currentStrLenChars = wcslen( currentStr );
|
|
|
|
while ( currentStrLenChars ) {
|
|
|
|
data = NULL;
|
|
offset.QuadPart = 0;
|
|
|
|
//
|
|
// Convert the offset from a string to a large integer value.
|
|
//
|
|
|
|
count = swscanf( currentStr, L"%I64x ", &offset.QuadPart );
|
|
|
|
if ( 0 == count ) {
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_WARNING,
|
|
L"Error: Unable to parse offset from currentStr (%1!ws!) \n",
|
|
currentStr );
|
|
|
|
// Stop processing the list...
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Data starts just after the first space.
|
|
//
|
|
|
|
data = wcschr( currentStr, SPACE_CHAR );
|
|
|
|
if ( !data || wcslen(data) < 3 ) {
|
|
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"Error: Unable to get mount point str from currentStr %1!ws! \n",
|
|
currentStr );
|
|
|
|
// Stop processing the list...
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Skip past the space character. Note that the length was previously validated.
|
|
//
|
|
|
|
if ( SPACE_CHAR == *data ) {
|
|
data++;
|
|
}
|
|
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"%1!08X!%2!08X! %3!ws! \n", // couldn't get !I64X! to work...
|
|
offset.HighPart,
|
|
offset.LowPart,
|
|
data );
|
|
|
|
currentStr += currentStrLenChars + 1;
|
|
currentStrLenChars = wcslen( currentStr );
|
|
}
|
|
|
|
LeaveCriticalSection( &ResourceEntry->MPInfo.MPLock );
|
|
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"*** End of list *** \n" );
|
|
|
|
} // PrintStrList
|
|
|
|
|
|
static
|
|
DWORD
|
|
SetMPListThread(
|
|
LPVOID lpThreadParameter
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Mount point list update thread. Updates the cluster data base.
|
|
|
|
Arguments:
|
|
|
|
lpThreadParameter - stores ResourceEntry.
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD dwError;
|
|
PDISK_RESOURCE ResourceEntry = lpThreadParameter;
|
|
DWORD idx;
|
|
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"SetMPListThread: started.\n");
|
|
|
|
//
|
|
// Will die in 10 minutes if unsuccessful
|
|
//
|
|
|
|
for ( idx = 0; idx < 300; ++idx ) {
|
|
|
|
//
|
|
// Wait for either the terminate event or the timeout
|
|
//
|
|
|
|
dwError = WaitForSingleObject( DisksTerminateEvent, 2000 );
|
|
|
|
if ( WAIT_TIMEOUT == dwError ) {
|
|
|
|
EnterCriticalSection( &ResourceEntry->MPInfo.MPLock );
|
|
|
|
#if DBG
|
|
DumpDiskInfoParams( ResourceEntry );
|
|
#endif
|
|
|
|
//
|
|
// Bug in ResUtilSetPropertyParameterBlock. It will update the cluster
|
|
// database with updated MULTI_SZ data, but it doesn't clear the values
|
|
// appropriately. Use ClusterRegDeleteValue to make sure the lists are
|
|
// cleared if they have been deleted.
|
|
//
|
|
|
|
if ( !ResourceEntry->DiskInfo.Params.MPVolGuids &&
|
|
0 == ResourceEntry->DiskInfo.Params.MPVolGuidsSize ) {
|
|
|
|
dwError = ClusterRegDeleteValue( ResourceEntry->ResourceParametersKey,
|
|
CLUSREG_NAME_PHYSDISK_MPVOLGUIDS );
|
|
}
|
|
|
|
//
|
|
// Timer expired. Update the cluster database.
|
|
//
|
|
|
|
dwError = ResUtilSetPropertyParameterBlock( ResourceEntry->ResourceParametersKey,
|
|
DiskResourcePrivateProperties,
|
|
NULL,
|
|
(LPBYTE) &ResourceEntry->DiskInfo.Params,
|
|
NULL,
|
|
0,
|
|
NULL );
|
|
|
|
LeaveCriticalSection( &ResourceEntry->MPInfo.MPLock );
|
|
|
|
if ( ERROR_SUCCESS == dwError ) {
|
|
|
|
//
|
|
// We're done.
|
|
//
|
|
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"SetMPListThread: mount point info updated in cluster data base \n" );
|
|
|
|
break;
|
|
|
|
} else if ( ERROR_SHARING_PAUSED != dwError ) {
|
|
|
|
//
|
|
// If the drive is not yet online, we should have seen ERROR_SHARING_PAUSED. If
|
|
// we see any other error, something bad happened.
|
|
//
|
|
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_WARNING,
|
|
L"SetMPListThread: Failed to update cluster data base, error = %1!u! \n",
|
|
dwError );
|
|
break;
|
|
}
|
|
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"SetMPListThread: Wait again for event or timeout, count %1!u! \n",
|
|
idx );
|
|
|
|
} else {
|
|
|
|
//
|
|
// The terminate event is possibly set.
|
|
//
|
|
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_WARNING,
|
|
L"SetMPListThread: WaitForSingleObject returned error = %1!u! \n",
|
|
dwError );
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Thread ending, clear the flag.
|
|
//
|
|
|
|
InterlockedExchange( &ResourceEntry->MPInfo.MPUpdateThreadIsActive, 0 );
|
|
|
|
return(ERROR_SUCCESS);
|
|
|
|
} // SetMPListThread
|
|
|
|
|
|
DWORD
|
|
PostMPInfoIntoRegistry(
|
|
PDISK_RESOURCE ResourceEntry
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Set the DiskResourcePrivateProperties in the cluster database. If the disk
|
|
is not yet online, create a thread to update the cluster database. The disk
|
|
might not be fully online if we are in the process of bringing the quorum disk
|
|
online and trying to update the mount point information.
|
|
|
|
Arguments:
|
|
|
|
ResourceEntry - Pointer to the DISK_RESOURCE structure.
|
|
|
|
Return Value:
|
|
|
|
Win32 error code.
|
|
|
|
--*/
|
|
{
|
|
DWORD dwError;
|
|
|
|
//
|
|
// Update the cluster database.
|
|
//
|
|
|
|
EnterCriticalSection( &ResourceEntry->MPInfo.MPLock );
|
|
|
|
#if DBG
|
|
DumpDiskInfoParams( ResourceEntry );
|
|
#endif
|
|
|
|
//
|
|
// Bug in ResUtilSetPropertyParameterBlock. It will update the cluster
|
|
// database with updated MULTI_SZ data, but it doesn't clear the values
|
|
// appropriately. Use ClusterRegDeleteValue to make sure the lists are
|
|
// cleared if they have been deleted.
|
|
//
|
|
|
|
if ( !ResourceEntry->DiskInfo.Params.MPVolGuids &&
|
|
0 == ResourceEntry->DiskInfo.Params.MPVolGuidsSize ) {
|
|
|
|
dwError = ClusterRegDeleteValue( ResourceEntry->ResourceParametersKey,
|
|
CLUSREG_NAME_PHYSDISK_MPVOLGUIDS );
|
|
}
|
|
|
|
dwError = ResUtilSetPropertyParameterBlock( ResourceEntry->ResourceParametersKey,
|
|
DiskResourcePrivateProperties,
|
|
NULL,
|
|
(LPBYTE) &ResourceEntry->DiskInfo.Params,
|
|
NULL,
|
|
0,
|
|
NULL );
|
|
|
|
LeaveCriticalSection( &ResourceEntry->MPInfo.MPLock );
|
|
|
|
//
|
|
// If the update failed and the disk is not yet online, it will fail with
|
|
// ERROR_SHARING_PAUSED. In this case, create a thread to update the cluster
|
|
// data base. Any other error or success should simply continue.
|
|
//
|
|
|
|
if ( ERROR_SHARING_PAUSED == dwError ) {
|
|
|
|
//
|
|
// Check if the thread is already active. If it is, exit with an error.
|
|
//
|
|
|
|
if ( InterlockedCompareExchange(
|
|
&ResourceEntry->MPInfo.MPUpdateThreadIsActive,
|
|
1, 0 ) ) {
|
|
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_WARNING,
|
|
L"PostMPInfoIntoRegistry: MountPoint thread is already running \n" );
|
|
|
|
dwError = ERROR_ALREADY_EXISTS;
|
|
|
|
} else {
|
|
HANDLE thread;
|
|
DWORD threadId;
|
|
|
|
thread = CreateThread( NULL,
|
|
0,
|
|
SetMPListThread,
|
|
ResourceEntry,
|
|
0,
|
|
&threadId );
|
|
|
|
if ( NULL == thread ) {
|
|
|
|
//
|
|
// Thread creation failed. Log error, clear thread active flag,
|
|
// and return.
|
|
//
|
|
|
|
dwError = GetLastError();
|
|
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_WARNING,
|
|
L"PostMPInfoIntoRegistry: CreateThread failed, error %1!u!\n",
|
|
dwError );
|
|
|
|
InterlockedExchange( &ResourceEntry->MPInfo.MPUpdateThreadIsActive, 0 );
|
|
|
|
} else {
|
|
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"PostMPInfoIntoRegistry: Thread created \n" );
|
|
|
|
//
|
|
// Thread created. Indicate no error.
|
|
//
|
|
|
|
CloseHandle( thread );
|
|
dwError = ERROR_SUCCESS;
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
( NO_ERROR == dwError ? LOG_INFORMATION : LOG_WARNING ),
|
|
L"PostMPInfoIntoRegistry: ResUtilSetPropertyParameterBlock returned %1!u! \n",
|
|
dwError );
|
|
}
|
|
|
|
return dwError;
|
|
|
|
} // PostMpInfoIntoRegistry
|
|
|
|
|
|
VOID
|
|
DisksMountPointCleanup(
|
|
PDISK_RESOURCE ResourceEntry
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Cleanup everything the mount point code used.
|
|
This routine should be called in DisksClose.
|
|
|
|
Arguments:
|
|
|
|
ResourceEntry - Pointer to the DISK_RESOURCE structure.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"DisksMountPointCleanup: Cleanup mount point information \n" );
|
|
|
|
//
|
|
// If existing MPVolGuids list, free it.
|
|
//
|
|
|
|
if ( ResourceEntry->DiskInfo.Params.MPVolGuids ) {
|
|
LocalFree( ResourceEntry->DiskInfo.Params.MPVolGuids );
|
|
ResourceEntry->DiskInfo.Params.MPVolGuidsSize = 0;
|
|
ResourceEntry->DiskInfo.Params.MPVolGuids = NULL;
|
|
}
|
|
|
|
ResourceEntry->MPInfo.Initialized = FALSE;
|
|
|
|
DeleteCriticalSection( &ResourceEntry->MPInfo.MPLock );
|
|
|
|
} // DisksMountPointCleanup
|
|
|
|
|
|
VOID
|
|
DisksMountPointInitialize(
|
|
PDISK_RESOURCE ResourceEntry
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Prepare the mount point structures in the ResourceEntry for use.
|
|
This routine should be called in DisksOpen.
|
|
|
|
Arguments:
|
|
|
|
ResourceEntry - Pointer to the DISK_RESOURCE structure.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
InitializeCriticalSection( &ResourceEntry->MPInfo.MPLock );
|
|
|
|
ResourceEntry->MPInfo.Initialized = TRUE;
|
|
|
|
InterlockedExchange( &ResourceEntry->MPInfo.MPUpdateThreadIsActive, 0 );
|
|
InterlockedExchange( &ResourceEntry->MPInfo.MPListCreateInProcess, 0 );
|
|
|
|
} // DisksMountPointInitialize
|
|
|
|
|
|
|
|
DWORD
|
|
DisksUpdateMPList(
|
|
PDISK_RESOURCE ResourceEntry
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Validate the mount points.
|
|
|
|
Arguments:
|
|
|
|
ResourceEntry - Pointer to the DISK_RESOURCE structure.
|
|
|
|
Return Value:
|
|
|
|
Win32 error code.
|
|
|
|
--*/
|
|
{
|
|
DWORD dwError = NO_ERROR;
|
|
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"DisksUpdateMPList: Processing PNP mountpoint notification \n" );
|
|
|
|
//
|
|
// Check if the MPList is in process of being updated. If it is, exit with an error.
|
|
//
|
|
|
|
if ( InterlockedCompareExchange(
|
|
&ResourceEntry->MPInfo.MPListCreateInProcess,
|
|
1, 0 ) ) {
|
|
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"DisksUpdateMPList: Update in process, bypassing PNP notification \n" );
|
|
return ERROR_BUSY;
|
|
}
|
|
|
|
dwError = ValidateMountPoints( ResourceEntry );
|
|
|
|
InterlockedExchange( &ResourceEntry->MPInfo.MPListCreateInProcess, 0 );
|
|
|
|
return dwError;
|
|
|
|
} // DisksUpdateMPList
|
|
|
|
|
|
DWORD
|
|
DisksProcessMPControlCode(
|
|
PDISK_RESOURCE ResourceEntry,
|
|
DWORD ControlCode
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Process the disk mount point control code. Since we are in the thread
|
|
that handed us the control code (DisksResourceControl), we can't do
|
|
much except a separate thread to do the bulk of the mount point
|
|
processing.
|
|
|
|
Arguments:
|
|
|
|
ResourceEntry - Pointer to the DISK_RESOURCE structure.
|
|
|
|
ControlCode - Cluster resource control for mount point processing.
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
HANDLE thread;
|
|
DWORD threadId;
|
|
DWORD dwError = NO_ERROR;
|
|
|
|
__try {
|
|
|
|
//
|
|
// Create a thread to update the mount point list. We don't need to
|
|
// copy the ResourceEntry as this pointer will be valid when the thread
|
|
// runs.
|
|
//
|
|
|
|
thread = CreateThread( NULL,
|
|
0,
|
|
DisksUpdateMPList,
|
|
ResourceEntry,
|
|
0,
|
|
&threadId );
|
|
|
|
if ( NULL == thread ) {
|
|
dwError = GetLastError();
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_WARNING,
|
|
L"DisksProcessMPControlCode: CreateThread failed %1!u! \n",
|
|
dwError );
|
|
__leave;
|
|
}
|
|
|
|
//
|
|
// Thread created. Indicate no error.
|
|
//
|
|
|
|
CloseHandle( thread );
|
|
dwError = NO_ERROR;
|
|
|
|
#if DBG
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_WARNING,
|
|
L"DisksProcessMPControlCode: Created thread to process control code \n" );
|
|
#endif
|
|
|
|
} __finally {
|
|
|
|
}
|
|
|
|
return dwError;
|
|
|
|
} // DisksProcessMPControlCode
|
|
|
|
|
|
DWORD
|
|
ValidateMountPoints(
|
|
IN OUT PDISK_RESOURCE ResourceEntry
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
For each partition on this disk, get the mountpoints directed toward this
|
|
partition. Check each mountpoint to make sure it is allowed. For those
|
|
mountpoints not allowed, write a message to system event log indicating
|
|
why it is an invalid mountpoint.
|
|
|
|
Arguments:
|
|
|
|
ResourceEntry - Pointer to the DISK_RESOURCE structure.
|
|
|
|
Return Value:
|
|
|
|
ERROR_INVALID_DATA - Partition info stored in MountieInfo is invalid.
|
|
|
|
Win32 error code.
|
|
|
|
--*/
|
|
{
|
|
PMOUNTIE_PARTITION entry;
|
|
|
|
DWORD dwError = ERROR_SUCCESS;
|
|
DWORD nPartitions = MountiePartitionCount( &ResourceEntry->MountieInfo );
|
|
DWORD physicalDrive = ResourceEntry->DiskInfo.PhysicalDrive;
|
|
DWORD idx;
|
|
|
|
WCHAR szGlobalDiskPartName[MAX_PATH];
|
|
WCHAR szVolumeName[MAX_PATH];
|
|
|
|
//
|
|
// Check each interesting partition. Since only "valid" partitions are
|
|
// saved in the MountieInfo structure, we will only look at those (ignoring those
|
|
// partitions that are not NTFS).
|
|
//
|
|
|
|
for ( idx = 0; idx < nPartitions; ++idx ) {
|
|
|
|
entry = MountiePartition( &ResourceEntry->MountieInfo, idx );
|
|
|
|
#if DBG
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"ValidateMountPoints: index %1!u! entry %2!x! \n", idx, entry );
|
|
#endif
|
|
|
|
if ( !entry ) {
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_WARNING,
|
|
L"ValidateMountPoints: no partition entry for index %1!u! \n", idx );
|
|
|
|
//
|
|
// Something bad happened to our data structures.
|
|
//
|
|
|
|
dwError = ERROR_INVALID_DATA;
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Create the device name of the form:
|
|
// \\?\GLOBALROOT\Device\HarddiskX\PartitionY\ (uses trailing backslash)
|
|
//
|
|
|
|
(VOID) StringCchPrintf( szGlobalDiskPartName,
|
|
RTL_NUMBER_OF( szGlobalDiskPartName ),
|
|
GLOBALROOT_HARDDISK_PARTITION_FMT,
|
|
physicalDrive,
|
|
entry->PartitionNumber );
|
|
|
|
#if DBG
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"ValidateMountPoints: Using name (%1!ws!) \n",
|
|
szGlobalDiskPartName );
|
|
#endif
|
|
|
|
if ( !GetVolumeNameForVolumeMountPointW( szGlobalDiskPartName,
|
|
szVolumeName,
|
|
RTL_NUMBER_OF(szVolumeName) )) {
|
|
|
|
dwError = GetLastError();
|
|
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_WARNING,
|
|
L"ValidateMountPoints: GetVolumeNameForVolumeMountPoint for (%1!ws!) returned %2!u!\n",
|
|
szGlobalDiskPartName,
|
|
dwError );
|
|
|
|
break;
|
|
}
|
|
|
|
#if DBG
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"ValidateMountPoints: Returned volume name (%1!ws!) \n",
|
|
szVolumeName );
|
|
#endif
|
|
|
|
CheckMPsForVolume( ResourceEntry,
|
|
szVolumeName );
|
|
|
|
CheckMPsOnVolume( ResourceEntry,
|
|
szVolumeName );
|
|
|
|
}
|
|
|
|
return dwError;
|
|
|
|
} // ValidateMountPoints
|
|
|
|
|
|
DWORD
|
|
CheckMPsOnVolume(
|
|
IN OUT PDISK_RESOURCE ResourceEntry,
|
|
IN PWSTR SrcVolName
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
For each the specified source volume, find all mount points hosted by this
|
|
volume. Find the target of each mount point, then make sure the depenedencies
|
|
are correct between the source and target volumes.
|
|
|
|
Arguments:
|
|
|
|
ResourceEntry - Pointer to the DISK_RESOURCE structure.
|
|
|
|
SrcVolName - Volume GUID name with trailing backslash.
|
|
\\?\Volume{GUID}\ [Note trailing backslash!]
|
|
|
|
Return Value:
|
|
|
|
Win32 error code.
|
|
|
|
--*/
|
|
{
|
|
PWSTR mpBuffer = NULL;
|
|
PWSTR targetVolName = NULL;
|
|
PWSTR mpFullPath = NULL;
|
|
|
|
HANDLE hMP = INVALID_HANDLE_VALUE;
|
|
|
|
DWORD dwError = NO_ERROR;
|
|
DWORD mpBufferChars = MP_ALLOC_SIZE;
|
|
DWORD targetVolNameChars = MAX_PATH;
|
|
DWORD mpFullPathChars = MAX_PATH;
|
|
DWORD srcSignature = ResourceEntry->DiskInfo.Params.Signature;
|
|
|
|
//
|
|
// Buffer to hold mount points hosted on this volume.
|
|
//
|
|
|
|
mpBuffer = LocalAlloc( LPTR, mpBufferChars * sizeof(WCHAR) );
|
|
if ( !mpBuffer ) {
|
|
dwError = GetLastError();
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_WARNING,
|
|
L"CheckMPsOnVolume: Unable to allocate MP buffer for (%1!ws!), error %2!u!\n",
|
|
SrcVolName,
|
|
dwError );
|
|
goto FnExit;
|
|
}
|
|
|
|
hMP = FindFirstVolumeMountPoint( SrcVolName,
|
|
mpBuffer,
|
|
mpBufferChars );
|
|
|
|
if ( INVALID_HANDLE_VALUE == hMP ) {
|
|
|
|
//
|
|
// There might be no mount points on this volume, which is acceptable.
|
|
// Only log other types of errors.
|
|
//
|
|
|
|
dwError = GetLastError();
|
|
|
|
if ( ERROR_NO_MORE_FILES != dwError ) {
|
|
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_WARNING,
|
|
L"CheckMPsOnVolume: FindFirstVolumeMountPoint for (%1!ws!), error %2!u!\n",
|
|
SrcVolName,
|
|
dwError );
|
|
}
|
|
|
|
goto FnExit;
|
|
}
|
|
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"CheckMPsOnVolume: Checking volume (%1!ws!) source signature %2!08X! \n",
|
|
SrcVolName,
|
|
srcSignature );
|
|
|
|
//
|
|
// Buffer to hold the mount point target volume.
|
|
//
|
|
|
|
targetVolName = LocalAlloc( LPTR, targetVolNameChars * sizeof(WCHAR) );
|
|
if ( !targetVolName ) {
|
|
dwError = GetLastError();
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_WARNING,
|
|
L"CheckMPsOnVolume: Unable to allocate Volume Name buffer for (%1!ws!), error %2!u!\n",
|
|
SrcVolName,
|
|
dwError );
|
|
goto FnExit;
|
|
}
|
|
|
|
//
|
|
// Buffer to hold the full mount point name. This will be the source
|
|
// volume with the mount point appended to it.
|
|
// \\?\Volume{GUID}\some-mount-point\ [Note trailing backslash!]
|
|
//
|
|
|
|
mpFullPath = LocalAlloc( LPTR, mpFullPathChars * sizeof(WCHAR) );
|
|
if ( !mpFullPath ) {
|
|
dwError = GetLastError();
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_WARNING,
|
|
L"CheckMPsOnVolume: Unable to allocate Volume Name buffer for (%1!ws!), error %2!u!\n",
|
|
SrcVolName,
|
|
dwError );
|
|
goto FnExit;
|
|
}
|
|
|
|
while ( TRUE ) {
|
|
|
|
(VOID) StringCchCopy( mpFullPath, mpFullPathChars, SrcVolName );
|
|
(VOID) StringCchCat( mpFullPath, mpFullPathChars, mpBuffer );
|
|
|
|
//
|
|
// Given the source volume and mount point on the source volume,
|
|
// find the target of the mount point.
|
|
//
|
|
|
|
if ( !GetVolumeNameForVolumeMountPoint( mpFullPath,
|
|
targetVolName,
|
|
targetVolNameChars ) ) {
|
|
dwError = GetLastError();
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_WARNING,
|
|
L"CheckMPsOnVolume: GetVolumeNameForVolumeMountPoint failed for (%1!ws!), error %2!u!\n",
|
|
mpBuffer,
|
|
dwError );
|
|
|
|
//
|
|
// Fall through to get next mount point...
|
|
//
|
|
|
|
} else {
|
|
|
|
//
|
|
// Check dependencies between source and target volumes.
|
|
//
|
|
|
|
IsMountPointAllowed( mpFullPath,
|
|
SrcVolName, // Source VolGUID
|
|
targetVolName, // Target VolGUID
|
|
ResourceEntry );
|
|
|
|
}
|
|
|
|
//
|
|
// Keep looking for mount points on the source volume until there
|
|
// are no more.
|
|
//
|
|
|
|
if ( !FindNextVolumeMountPoint( hMP,
|
|
mpBuffer,
|
|
mpBufferChars ) ) {
|
|
|
|
dwError = GetLastError();
|
|
|
|
if ( ERROR_NO_MORE_FILES == dwError ) {
|
|
dwError = NO_ERROR;
|
|
} else {
|
|
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_WARNING,
|
|
L"CheckMPsOnVolume: FindNextVolumeMountPoint failed for (%1!ws!), error %2!u!\n",
|
|
SrcVolName,
|
|
dwError );
|
|
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
FnExit:
|
|
|
|
if ( mpBuffer ) {
|
|
LocalFree( mpBuffer );
|
|
}
|
|
|
|
if ( targetVolName ) {
|
|
LocalFree( targetVolName );
|
|
}
|
|
|
|
if ( mpFullPath ) {
|
|
LocalFree( mpFullPath );
|
|
}
|
|
|
|
if ( INVALID_HANDLE_VALUE != hMP ) {
|
|
FindVolumeMountPointClose( hMP );
|
|
}
|
|
|
|
return dwError;
|
|
|
|
} // CheckMPsOnVolume
|
|
|
|
|
|
VOID
|
|
CheckMPsForVolume(
|
|
IN OUT PDISK_RESOURCE ResourceEntry,
|
|
IN PWSTR VolumeName
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
For the specified volume, find all mount points directed towards this volume.
|
|
For each mountpoint, make sure it is allowed.
|
|
|
|
Arguments:
|
|
|
|
ResourceEntry - Pointer to the DISK_RESOURCE structure.
|
|
|
|
VolumeName - Target volume for the mount point. Format is:
|
|
\\?\Volume{GUID}\ [Note trailing backslash!]
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
PWSTR volumePaths = NULL;
|
|
PWSTR currentMP;
|
|
|
|
DWORD dwError;
|
|
|
|
__try {
|
|
|
|
//
|
|
// GetMountPoints will allocate a MultiSz buffer with
|
|
// all the mount points for this target volume.
|
|
//
|
|
|
|
dwError = GetMountPoints( VolumeName, &volumePaths );
|
|
|
|
if ( NO_ERROR != dwError || !volumePaths ) {
|
|
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_WARNING,
|
|
L"CheckMPsForVolume: GetMountPoints returns %1!u! \n", dwError );
|
|
|
|
__leave;
|
|
}
|
|
|
|
//
|
|
// Loop through each mount point in the list.
|
|
//
|
|
// Each mount point will either be a mount point or a drive letter
|
|
// (which is actually a mount point). Format can be:
|
|
//
|
|
// x:\ [Note trailing backslash!]
|
|
// x:\some-mp-name\ [Note trailing backslash!]
|
|
// x:\some-dir\some-mp-name\ [Note trailing backslash!]
|
|
//
|
|
|
|
currentMP = volumePaths;
|
|
|
|
for (;;) {
|
|
|
|
IsMountPointAllowed( currentMP,
|
|
NULL, // Source VolGUID
|
|
VolumeName, // Target VolGUID
|
|
ResourceEntry );
|
|
|
|
//
|
|
// Skip through current mount point to end of string.
|
|
//
|
|
|
|
while (*currentMP++);
|
|
|
|
//
|
|
// If next mount point is empty, the list is exhausted.
|
|
//
|
|
|
|
if (!*currentMP) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
} __finally {
|
|
|
|
if ( volumePaths ) {
|
|
LocalFree( volumePaths );
|
|
}
|
|
}
|
|
|
|
} // CheckMPsForVolume
|
|
|
|
|
|
DWORD
|
|
GetMountPoints(
|
|
PWSTR VolumeName,
|
|
PWSTR *VolumePaths
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
For the specified volume, find all mount points directed towards this volume.
|
|
|
|
The mount point buffer will be allocated by this routine and must be freed by
|
|
the caller.
|
|
|
|
Arguments:
|
|
|
|
VolumeName - Target volume for the mount point. Format is:
|
|
\\?\Volume{GUID}\ [Note trailing backslash!]
|
|
|
|
VolumePaths - Pointer to a MultiSz string containing all mount points directed
|
|
toward this volume. If there are no mount points, this pointer will
|
|
be set to NULL. The caller is responsible for freeing this buffer.
|
|
|
|
Return Value:
|
|
|
|
Win32 error code.
|
|
|
|
--*/
|
|
{
|
|
DWORD lenChars;
|
|
PWSTR paths = NULL;
|
|
|
|
DWORD dwError;
|
|
|
|
if ( !VolumeName || !VolumePaths ) {
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
*VolumePaths = NULL;
|
|
|
|
//
|
|
// Determine the size of the buffer we need.
|
|
//
|
|
|
|
if ( !GetVolumePathNamesForVolumeName( VolumeName, NULL, 0, &lenChars ) ) {
|
|
dwError = GetLastError();
|
|
if ( ERROR_MORE_DATA != dwError ) {
|
|
return dwError;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Allocate the mount point buffer.
|
|
//
|
|
|
|
paths = LocalAlloc( 0, lenChars * sizeof(WCHAR) );
|
|
if ( !paths ) {
|
|
dwError = GetLastError();
|
|
return dwError;
|
|
}
|
|
|
|
//
|
|
// Get the mount points.
|
|
//
|
|
|
|
if ( !GetVolumePathNamesForVolumeName( VolumeName, paths, lenChars, NULL ) ) {
|
|
dwError = GetLastError();
|
|
LocalFree( paths );
|
|
return dwError;
|
|
}
|
|
|
|
//
|
|
// If no mount points, free the buffer and return to the caller.
|
|
//
|
|
|
|
if ( !paths[0] ) {
|
|
LocalFree(paths);
|
|
|
|
//
|
|
// If no mount points for this volume, return no error and a NULL
|
|
// pointer to the mount point list.
|
|
//
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
*VolumePaths = paths;
|
|
|
|
return NO_ERROR;
|
|
|
|
} // GetMountPoints
|
|
|
|
|
|
DWORD
|
|
ValidateListOffsets(
|
|
IN OUT PDISK_RESOURCE ResourceEntry,
|
|
IN PWSTR MasterList
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Verify each entry in the list to make sure the byte offset
|
|
is valid. Also, count the number of entries to make sure
|
|
there are not too many entries saved (there should be one
|
|
VolGuid per node times the number of volumes on the disk).
|
|
|
|
Finally, make sure each offset is listed only once in the
|
|
list.
|
|
|
|
Arguments:
|
|
|
|
ResourceEntry - Pointer to the DISK_RESOURCE structure.
|
|
|
|
MasterList - REG_MULTI_SZ list to be checked.
|
|
|
|
Return Value:
|
|
|
|
ERROR_INVALID_DATA - List contains at least one invalid byte offset
|
|
value, possibly more.
|
|
|
|
ERROR_INSUFFICIENT_BUFFER - List possibly corrupt as it contains too
|
|
many entries.
|
|
|
|
Win32 error code.
|
|
|
|
--*/
|
|
{
|
|
PWCHAR currentStr;
|
|
POFFSET_LIST offsetList = NULL;
|
|
|
|
DWORD currentStrLenChars = 0;
|
|
DWORD dwError = NO_ERROR;
|
|
DWORD partitionNo;
|
|
DWORD numberOfEntries = 0;
|
|
DWORD count;
|
|
|
|
LARGE_INTEGER offset;
|
|
|
|
BOOL invalidOffset = FALSE;
|
|
|
|
EnterCriticalSection( &ResourceEntry->MPInfo.MPLock );
|
|
|
|
//
|
|
// Parse through the list.
|
|
//
|
|
|
|
for ( currentStr = (PWCHAR)MasterList,
|
|
currentStrLenChars = wcslen( currentStr ) ;
|
|
currentStrLenChars ;
|
|
currentStr += currentStrLenChars + 1,
|
|
currentStrLenChars = wcslen( currentStr ) ) {
|
|
|
|
offset.QuadPart = 0;
|
|
|
|
#if DBG
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"ValidateListOffsets: CurrentStr (%1!ws!), numChars %2!u! \n",
|
|
currentStr,
|
|
currentStrLenChars );
|
|
#endif
|
|
|
|
//
|
|
// Convert the offset from a string to a large integer value.
|
|
//
|
|
|
|
count = swscanf( currentStr, L"%I64x ", &offset.QuadPart );
|
|
|
|
if ( 0 == count ) {
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_WARNING,
|
|
L"ValidateListOffsets: Unable to parse offset from currentStr (%1!ws!) \n",
|
|
currentStr );
|
|
numberOfEntries++;
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Check if offset was already seen. If seen, exit with error.
|
|
// If not seen, add it.
|
|
//
|
|
|
|
dwError = OffsetListAdd( &offsetList, &offset );
|
|
|
|
if ( ERROR_INVALID_DATA == dwError ) {
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_WARNING,
|
|
L"ValidateListOffsets: Offset ( %1!08X!%2!08X! ) in list multiple times \n",
|
|
offset.HighPart,
|
|
offset.LowPart ); // couldn't get !I64X! to work...
|
|
|
|
invalidOffset = TRUE;
|
|
|
|
// As soon as we find a duplicate offset, we are done.
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Convert the offset to a partition number.
|
|
//
|
|
|
|
if ( !GetPartNoFromOffset( &offset, &ResourceEntry->MountieInfo, &partitionNo ) ) {
|
|
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_WARNING,
|
|
L"ValidateListOffsets: Unable to convert offset ( %1!08X!%2!08X! ) to partition number \n",
|
|
offset.HighPart,
|
|
offset.LowPart ); // couldn't get !I64X! to work...
|
|
|
|
invalidOffset = TRUE;
|
|
|
|
// As soon as we find an invalid partition number, we are done.
|
|
|
|
break;
|
|
}
|
|
|
|
numberOfEntries++;
|
|
}
|
|
|
|
if ( invalidOffset ) {
|
|
dwError = ERROR_INVALID_DATA;
|
|
|
|
} else if ( numberOfEntries > MAX_ALLOWED_VOLGUID_ENTRIES_PER_DISK ) {
|
|
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_WARNING,
|
|
L"ValidateListOffset: VolGuid list too large, %1!u! entries \n",
|
|
numberOfEntries );
|
|
|
|
//
|
|
// Return an error so the list is deleted and recreated.
|
|
//
|
|
|
|
dwError = ERROR_INVALID_DATA;
|
|
|
|
#if USEMOUNTPOINTS_KEY
|
|
//
|
|
// See if the user wants to ignore the number of entries in VolGuid list.
|
|
//
|
|
|
|
if ( !(ResourceEntry->DiskInfo.Params.UseMountPoints & MPS_IGNORE_MAX_VOLGUIDS) ) {
|
|
|
|
//
|
|
// Log an error to system event log.
|
|
//
|
|
|
|
ClusResLogSystemEventByKey(ResourceEntry->ResourceKey,
|
|
LOG_UNUSUAL,
|
|
RES_DISK_MP_VOLGUID_LIST_EXCESSIVE );
|
|
|
|
dwError = ERROR_INSUFFICIENT_BUFFER;
|
|
}
|
|
#endif
|
|
|
|
}
|
|
|
|
LeaveCriticalSection( &ResourceEntry->MPInfo.MPLock );
|
|
|
|
OffsetListCleanup( offsetList );
|
|
|
|
return dwError;
|
|
|
|
} // ValidateListOffsets
|
|
|
|
|
|
DWORD
|
|
OffsetListAdd(
|
|
POFFSET_LIST *OffsetList,
|
|
PLARGE_INTEGER Offset
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Add specified offset to the offset list.
|
|
|
|
Arguments:
|
|
|
|
OffsetList - pointer to offset list.
|
|
|
|
Offset - pointer to partition offset value.
|
|
|
|
Return Value:
|
|
|
|
NO_ERROR - Offset was not in list and was successfully added.
|
|
|
|
ERROR_INVALID_DATA - Offset was in the list previously.
|
|
|
|
Win32 error code - failed to add offset to the list.
|
|
|
|
--*/
|
|
{
|
|
POFFSET_LIST next = NULL;
|
|
POFFSET_LIST entry = *OffsetList;
|
|
|
|
DWORD dwError = NO_ERROR;
|
|
|
|
while ( entry ) {
|
|
|
|
next = entry->Next;
|
|
|
|
if ( Offset->LowPart == entry->Offset.LowPart &&
|
|
Offset->HighPart == entry->Offset.HighPart ) {
|
|
|
|
//
|
|
// Offset is already in the list. Return a
|
|
// unique error value.
|
|
//
|
|
|
|
dwError = ERROR_INVALID_DATA;
|
|
goto FnExit;
|
|
}
|
|
|
|
entry = next;
|
|
}
|
|
|
|
//
|
|
// If we got to this point, either the offset list was
|
|
// empty or we walked the entire list and the offset
|
|
// was not in the list. Add it now.
|
|
//
|
|
|
|
entry = LocalAlloc( LPTR, sizeof(OFFSET_LIST) );
|
|
|
|
if ( !entry ) {
|
|
dwError = GetLastError();
|
|
goto FnExit;
|
|
}
|
|
|
|
entry->Offset.LowPart = Offset->LowPart;
|
|
entry->Offset.HighPart = Offset->HighPart;
|
|
|
|
if ( *OffsetList ) {
|
|
entry->Next = *OffsetList;
|
|
}
|
|
|
|
*OffsetList = entry;
|
|
|
|
FnExit:
|
|
|
|
return dwError;
|
|
|
|
} // OffsetListAdd
|
|
|
|
|
|
DWORD
|
|
OffsetListCleanup(
|
|
POFFSET_LIST OffsetList
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Cleanup any storage allocated in the offset list.
|
|
|
|
Arguments:
|
|
|
|
OffsetList - pointer to offset list.
|
|
|
|
Return Value:
|
|
|
|
NO_ERROR
|
|
|
|
--*/
|
|
{
|
|
POFFSET_LIST next = NULL;
|
|
POFFSET_LIST entry = OffsetList;
|
|
|
|
while ( entry ) {
|
|
next = entry->Next;
|
|
LocalFree( entry );
|
|
entry = next;
|
|
}
|
|
|
|
return NO_ERROR;
|
|
|
|
} // OffsetListCleanup
|
|
|
|
|
|
BOOL
|
|
MPIsDriveLetter(
|
|
IN PWSTR MountPoint
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Determine if the mount point string is a drive letter. A drive letter will be
|
|
represented by a string of the form "x:\" with a length of 3.
|
|
|
|
Arguments:
|
|
|
|
MountPoint - Mount point string to be verified.
|
|
|
|
Return Value:
|
|
|
|
TRUE if the mount point string represents a drive letter.
|
|
|
|
--*/
|
|
{
|
|
DWORD lenChars;
|
|
|
|
lenChars = wcslen( MountPoint );
|
|
|
|
if ( 3 == lenChars &&
|
|
L':' == MountPoint[1] &&
|
|
L'\\' == MountPoint[2] &&
|
|
iswalpha( MountPoint[0] ) ) {
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
|
|
} // MPIsDriveLetter
|
|
|
|
|
|
#if DBG
|
|
|
|
//
|
|
// Debug helper routine
|
|
//
|
|
|
|
VOID
|
|
DumpDiskInfoParams(
|
|
PDISK_RESOURCE ResourceEntry
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Display in the cluster log interesting mountpoint information.
|
|
|
|
Arguments:
|
|
|
|
ResourceEntry - Pointer to the DISK_RESOURCE structure.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
|
|
--*/
|
|
{
|
|
#if 0 // Drive is not currently stored
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"SetMPListThread: Signature %1!x! Drive (%2!ws!) \n",
|
|
ResourceEntry->DiskInfo.Params.Signature,
|
|
ResourceEntry->DiskInfo.Params.Drive );
|
|
#endif
|
|
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"SetMPListThread: Signature %1!x! \n",
|
|
ResourceEntry->DiskInfo.Params.Signature );
|
|
|
|
|
|
#if USEMOUNTPOINTS_KEY
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"SetMPListThread: SkipChkdsk %1!x! ConditionalMount %2!x! UseMountPoints %3!x! \n",
|
|
ResourceEntry->DiskInfo.Params.SkipChkdsk,
|
|
ResourceEntry->DiskInfo.Params.ConditionalMount,
|
|
ResourceEntry->DiskInfo.Params.UseMountPoints );
|
|
#else
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"SetMPListThread: SkipChkdsk %1!x! ConditionalMount %2!x! \n",
|
|
ResourceEntry->DiskInfo.Params.SkipChkdsk,
|
|
ResourceEntry->DiskInfo.Params.ConditionalMount );
|
|
#endif
|
|
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"SetMPListThread: VolGuid list %1!x! VolGuid size %2!u! \n",
|
|
ResourceEntry->DiskInfo.Params.MPVolGuids,
|
|
ResourceEntry->DiskInfo.Params.MPVolGuidsSize );
|
|
|
|
} // DumpDiskInfoParams
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|