2521 lines
62 KiB
C
2521 lines
62 KiB
C
|
/*++
|
|||
|
|
|||
|
Copyright (c) 1996-1997 Microsoft Corporation
|
|||
|
|
|||
|
Module Name:
|
|||
|
|
|||
|
scsi.c
|
|||
|
|
|||
|
Abstract:
|
|||
|
|
|||
|
Common routines for dealing with SCSI disks, usable
|
|||
|
by both raw disks and FT sets
|
|||
|
|
|||
|
Author:
|
|||
|
|
|||
|
John Vert (jvert) 11/6/1996
|
|||
|
|
|||
|
Revision History:
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
#include <nt.h>
|
|||
|
#include <ntdef.h>
|
|||
|
#include <ntrtl.h>
|
|||
|
#include <nturtl.h>
|
|||
|
#include <windows.h>
|
|||
|
#include <ntddstor.h> // IOCTL_STORAGE_QUERY_PROPERTY
|
|||
|
|
|||
|
#include "disksp.h"
|
|||
|
#include "newmount.h"
|
|||
|
#include <string.h>
|
|||
|
#include <shlwapi.h> // SHDeleteKey
|
|||
|
#include <ntddvol.h> // IOCTL_VOLUME_QUERY_FAILOVER_SET
|
|||
|
#include <setupapi.h>
|
|||
|
|
|||
|
//
|
|||
|
// The registry key containing the system partition
|
|||
|
//
|
|||
|
const CHAR DISKS_REGKEY_SETUP[] = "SYSTEM\\SETUP";
|
|||
|
const CHAR DISKS_REGVALUE_SYSTEM_PARTITION[] = "SystemPartition";
|
|||
|
|
|||
|
extern PWCHAR DEVICE_HARDDISK_PARTITION_FMT; // L"\\Device\\Harddisk%u\\Partition%u" //
|
|||
|
extern PWCHAR GLOBALROOT_HARDDISK_PARTITION_FMT; // L"\\\\\?\\GLOBALROOT\\Device\\Harddisk%u\\Partition%u\\"
|
|||
|
|
|||
|
extern DWORD SystemDiskAddressFound;
|
|||
|
extern PSCSI_ADDRESS_ENTRY SysDiskAddrList;
|
|||
|
|
|||
|
#define INVALID_SCSIADDRESS_VALUE (DWORD)-1
|
|||
|
|
|||
|
typedef struct _SCSI_PASS_THROUGH_WITH_SENSE {
|
|||
|
SCSI_PASS_THROUGH Spt;
|
|||
|
UCHAR SenseBuf[32];
|
|||
|
} SCSI_PASS_THROUGH_WITH_SENSE, *PSCSI_PASS_THROUGH_WITH_SENSE;
|
|||
|
|
|||
|
|
|||
|
typedef struct _UPDATE_AVAIL_DISKS {
|
|||
|
HKEY AvailDisksKey;
|
|||
|
HKEY SigKey;
|
|||
|
DWORD EnableSanBoot;
|
|||
|
BOOL SigKeyIsEmpty;
|
|||
|
} UPDATE_AVAIL_DISKS, *PUPDATE_AVAIL_DISKS;
|
|||
|
|
|||
|
typedef struct _SCSI_INFO {
|
|||
|
DWORD Signature;
|
|||
|
DWORD DiskNumber;
|
|||
|
DWORD ScsiAddress;
|
|||
|
} SCSI_INFO, *PSCSI_INFO;
|
|||
|
|
|||
|
typedef struct _SERIAL_INFO {
|
|||
|
DWORD Signature;
|
|||
|
DWORD Error;
|
|||
|
LPWSTR SerialNumber;
|
|||
|
} SERIAL_INFO, *PSERIAL_INFO;
|
|||
|
|
|||
|
typedef
|
|||
|
DWORD
|
|||
|
(*LPDISK_ENUM_CALLBACK) (
|
|||
|
HANDLE DeviceHandle,
|
|||
|
DWORD Index,
|
|||
|
PVOID Param1
|
|||
|
);
|
|||
|
|
|||
|
//
|
|||
|
// Local Routines
|
|||
|
//
|
|||
|
|
|||
|
|
|||
|
DWORD
|
|||
|
AddSignatureToRegistry(
|
|||
|
HKEY RegKey,
|
|||
|
DWORD Signature
|
|||
|
);
|
|||
|
|
|||
|
BOOL
|
|||
|
IsClusterCapable(
|
|||
|
IN DWORD Signature
|
|||
|
);
|
|||
|
|
|||
|
BOOL
|
|||
|
IsSignatureInRegistry(
|
|||
|
HKEY RegKey,
|
|||
|
DWORD Signature
|
|||
|
);
|
|||
|
|
|||
|
DWORD
|
|||
|
UpdateAvailableDisks(
|
|||
|
);
|
|||
|
|
|||
|
DWORD
|
|||
|
UpdateAvailableDisksCallback(
|
|||
|
HANDLE DeviceHandle,
|
|||
|
DWORD Index,
|
|||
|
PVOID Param1
|
|||
|
);
|
|||
|
|
|||
|
DWORD
|
|||
|
AddScsiAddressToList(
|
|||
|
PSCSI_ADDRESS ScsiAddress
|
|||
|
);
|
|||
|
|
|||
|
DWORD
|
|||
|
GetVolumeFailoverSet(
|
|||
|
IN HANDLE DevHandle,
|
|||
|
OUT PVOLUME_FAILOVER_SET *FailoverSet
|
|||
|
);
|
|||
|
|
|||
|
DWORD
|
|||
|
BuildScsiListFromFailover(
|
|||
|
IN HANDLE DevHandle
|
|||
|
);
|
|||
|
|
|||
|
HANDLE
|
|||
|
OpenNtldrDisk(
|
|||
|
);
|
|||
|
|
|||
|
HANDLE
|
|||
|
OpenOSDisk(
|
|||
|
);
|
|||
|
|
|||
|
DWORD
|
|||
|
EnumerateDisks(
|
|||
|
LPDISK_ENUM_CALLBACK DiskEnumCallback,
|
|||
|
PVOID Param1
|
|||
|
);
|
|||
|
|
|||
|
DWORD
|
|||
|
GetScsiAddressCallback(
|
|||
|
HANDLE DevHandle,
|
|||
|
DWORD Index,
|
|||
|
PVOID Param1
|
|||
|
);
|
|||
|
|
|||
|
DWORD
|
|||
|
GetSerialNumberCallback(
|
|||
|
HANDLE DevHandle,
|
|||
|
DWORD Index,
|
|||
|
PVOID Param1
|
|||
|
);
|
|||
|
|
|||
|
DWORD
|
|||
|
GetSigFromSerNumCallback(
|
|||
|
HANDLE DevHandle,
|
|||
|
DWORD Index,
|
|||
|
PVOID Param1
|
|||
|
);
|
|||
|
|
|||
|
DWORD
|
|||
|
RetrieveSerialNumber(
|
|||
|
HANDLE DevHandle,
|
|||
|
LPWSTR *SerialNumber
|
|||
|
);
|
|||
|
|
|||
|
|
|||
|
|
|||
|
DWORD
|
|||
|
ScsiIsAlive(
|
|||
|
IN HANDLE DiskHandle
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Sends a SCSI passthrough command down to the disk to see if
|
|||
|
it is still there.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DiskHandle - Supplies a handle to the disk.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
ERROR_SUCCESS if successful
|
|||
|
|
|||
|
Win32 error code otherwise
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
BOOL success;
|
|||
|
DWORD bytesReturned;
|
|||
|
|
|||
|
success = DeviceIoControl( DiskHandle,
|
|||
|
IOCTL_DISK_CLUSTER_ALIVE_CHECK,
|
|||
|
NULL,
|
|||
|
0,
|
|||
|
NULL,
|
|||
|
0,
|
|||
|
&bytesReturned,
|
|||
|
FALSE );
|
|||
|
|
|||
|
if ( !success ) {
|
|||
|
return(GetLastError());
|
|||
|
}
|
|||
|
return(ERROR_SUCCESS);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
DWORD
|
|||
|
ClusDiskGetAvailableDisks(
|
|||
|
OUT PVOID OutBuffer,
|
|||
|
IN DWORD OutBufferSize,
|
|||
|
OUT LPDWORD BytesReturned,
|
|||
|
IN BOOL FtSet
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Enumerate and build a list of available disks on this system.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
OutBuffer - pointer to the output buffer to return the data.
|
|||
|
|
|||
|
OutBufferSize - size of the output buffer.
|
|||
|
|
|||
|
BytesReturned - the actual number of bytes that were returned (or
|
|||
|
the number of bytes that should have been returned if
|
|||
|
OutBufferSize is too small).
|
|||
|
|
|||
|
FtSet - TRUE if FT Set info wanted, FALSE otherwise.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
ERROR_SUCCESS if successful.
|
|||
|
|
|||
|
A Win32 error on failure.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
DWORD status;
|
|||
|
HKEY resKey;
|
|||
|
DWORD ival;
|
|||
|
DWORD signature;
|
|||
|
DWORD bytesReturned = 0;
|
|||
|
DWORD totalBytesReturned = 0;
|
|||
|
DWORD dataLength;
|
|||
|
DWORD outBufferSize = OutBufferSize;
|
|||
|
PVOID ptrBuffer = OutBuffer;
|
|||
|
CHAR signatureName[9];
|
|||
|
PCLUSPROP_SYNTAX ptrSyntax;
|
|||
|
|
|||
|
//
|
|||
|
// Make sure the AvailableDisks key is current.
|
|||
|
//
|
|||
|
|
|||
|
UpdateAvailableDisks();
|
|||
|
|
|||
|
*BytesReturned = 0;
|
|||
|
|
|||
|
status = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
|
|||
|
CLUSDISK_REGISTRY_AVAILABLE_DISKS,
|
|||
|
0,
|
|||
|
KEY_READ,
|
|||
|
&resKey );
|
|||
|
|
|||
|
if ( status != ERROR_SUCCESS ) {
|
|||
|
|
|||
|
// If the key wasn't found, return an empty list.
|
|||
|
if ( status == ERROR_FILE_NOT_FOUND ) {
|
|||
|
|
|||
|
// Add the endmark.
|
|||
|
bytesReturned += sizeof(CLUSPROP_SYNTAX);
|
|||
|
if ( bytesReturned <= outBufferSize ) {
|
|||
|
ptrSyntax = ptrBuffer;
|
|||
|
ptrSyntax->dw = CLUSPROP_SYNTAX_ENDMARK;
|
|||
|
ptrSyntax++;
|
|||
|
ptrBuffer = ptrSyntax;
|
|||
|
status = ERROR_SUCCESS;
|
|||
|
} else {
|
|||
|
status = ERROR_MORE_DATA;
|
|||
|
}
|
|||
|
|
|||
|
*BytesReturned = bytesReturned;
|
|||
|
}
|
|||
|
// We can't log an error, we have no resource handle!
|
|||
|
return(status);
|
|||
|
}
|
|||
|
|
|||
|
totalBytesReturned = bytesReturned;
|
|||
|
bytesReturned = 0;
|
|||
|
|
|||
|
for ( ival = 0; ; ival++ ) {
|
|||
|
dataLength = sizeof(signatureName);
|
|||
|
status = RegEnumKey( resKey,
|
|||
|
ival,
|
|||
|
signatureName,
|
|||
|
dataLength );
|
|||
|
if ( status == ERROR_NO_MORE_ITEMS ) {
|
|||
|
status = ERROR_SUCCESS;
|
|||
|
break;
|
|||
|
} else if ( status != ERROR_SUCCESS ) {
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
dataLength = sscanf( signatureName, "%x", &signature );
|
|||
|
if ( dataLength != 1 ) {
|
|||
|
status = ERROR_INVALID_DATA;
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If not cluster capable, then skip it.
|
|||
|
//
|
|||
|
if ( !IsClusterCapable(signature) ) {
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
GetDiskInfo( signature,
|
|||
|
&ptrBuffer,
|
|||
|
outBufferSize,
|
|||
|
&bytesReturned,
|
|||
|
FtSet );
|
|||
|
if ( outBufferSize > bytesReturned ) {
|
|||
|
outBufferSize -= bytesReturned;
|
|||
|
} else {
|
|||
|
outBufferSize = 0;
|
|||
|
}
|
|||
|
totalBytesReturned += bytesReturned;
|
|||
|
bytesReturned = 0;
|
|||
|
}
|
|||
|
|
|||
|
RegCloseKey( resKey );
|
|||
|
|
|||
|
bytesReturned = totalBytesReturned;
|
|||
|
|
|||
|
// Add the endmark.
|
|||
|
bytesReturned += sizeof(CLUSPROP_SYNTAX);
|
|||
|
if ( bytesReturned <= outBufferSize ) {
|
|||
|
ptrSyntax = ptrBuffer;
|
|||
|
ptrSyntax->dw = CLUSPROP_SYNTAX_ENDMARK;
|
|||
|
ptrSyntax++;
|
|||
|
ptrBuffer = ptrSyntax;
|
|||
|
}
|
|||
|
|
|||
|
if ( bytesReturned > OutBufferSize ) {
|
|||
|
status = ERROR_MORE_DATA;
|
|||
|
}
|
|||
|
|
|||
|
*BytesReturned = bytesReturned;
|
|||
|
|
|||
|
return(status);
|
|||
|
|
|||
|
} // ClusDiskGetAvailableDisks
|
|||
|
|
|||
|
|
|||
|
|
|||
|
DWORD
|
|||
|
GetScsiAddress(
|
|||
|
IN DWORD Signature,
|
|||
|
OUT LPDWORD ScsiAddress,
|
|||
|
OUT LPDWORD DiskNumber
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Find the SCSI addressing for a given signature.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Signature - the signature to find.
|
|||
|
|
|||
|
ScsiAddress - pointer to a DWORD to return the SCSI address information.
|
|||
|
|
|||
|
DiskNumber - the disk number associated with the signature.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
ERROR_SUCCESS if successful.
|
|||
|
|
|||
|
A Win32 error on failure.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
DWORD dwError;
|
|||
|
|
|||
|
SCSI_INFO scsiInfo;
|
|||
|
|
|||
|
ZeroMemory( &scsiInfo, sizeof(scsiInfo) );
|
|||
|
|
|||
|
scsiInfo.Signature = Signature;
|
|||
|
|
|||
|
dwError = EnumerateDisks( GetScsiAddressCallback, &scsiInfo );
|
|||
|
|
|||
|
//
|
|||
|
// If the SCSI address was not set, we know the disk was not found.
|
|||
|
//
|
|||
|
|
|||
|
if ( INVALID_SCSIADDRESS_VALUE == scsiInfo.ScsiAddress ) {
|
|||
|
dwError = ERROR_FILE_NOT_FOUND;
|
|||
|
goto FnExit;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// The callback routine will use ERROR_POPUP_ALREADY_ACTIVE to stop
|
|||
|
// the disk enumeration. Reset the value to success if that special
|
|||
|
// value is returned.
|
|||
|
//
|
|||
|
|
|||
|
if ( ERROR_POPUP_ALREADY_ACTIVE == dwError ) {
|
|||
|
dwError = ERROR_SUCCESS;
|
|||
|
}
|
|||
|
|
|||
|
*ScsiAddress = scsiInfo.ScsiAddress;
|
|||
|
*DiskNumber = scsiInfo.DiskNumber;
|
|||
|
|
|||
|
FnExit:
|
|||
|
|
|||
|
return dwError;
|
|||
|
|
|||
|
} // GetScsiAddress
|
|||
|
|
|||
|
|
|||
|
DWORD
|
|||
|
GetScsiAddressCallback(
|
|||
|
HANDLE DevHandle,
|
|||
|
DWORD Index,
|
|||
|
PVOID Param1
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Find the SCSI address and disk number for a given signature.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DevHandle - open handle to a physical disk. Do not close
|
|||
|
the handle on exit.
|
|||
|
|
|||
|
Index - current disk count. Not used.
|
|||
|
|
|||
|
Param1 - pointer to PSCSI_INFO structure.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
ERROR_SUCCESS to continue disk enumeration.
|
|||
|
|
|||
|
ERROR_POPUP_ALREADY_ACTIVE to stop disk enumeration. This
|
|||
|
value will be changed to ERROR_SUCCESS by GetScsiAddress.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
PSCSI_INFO scsiInfo = Param1;
|
|||
|
|
|||
|
PDRIVE_LAYOUT_INFORMATION driveLayout = NULL;
|
|||
|
|
|||
|
// Always return success to keep enumerating disks. Any
|
|||
|
// error value will stop the disk enumeration.
|
|||
|
|
|||
|
DWORD dwError = ERROR_SUCCESS;
|
|||
|
DWORD bytesReturned;
|
|||
|
|
|||
|
BOOL success;
|
|||
|
|
|||
|
SCSI_ADDRESS scsiAddress;
|
|||
|
STORAGE_DEVICE_NUMBER deviceNumber;
|
|||
|
CLUSPROP_SCSI_ADDRESS clusScsiAddress;
|
|||
|
|
|||
|
scsiInfo->ScsiAddress = INVALID_SCSIADDRESS_VALUE;
|
|||
|
|
|||
|
success = ClRtlGetDriveLayoutTable( DevHandle,
|
|||
|
&driveLayout,
|
|||
|
NULL );
|
|||
|
|
|||
|
if ( !success || !driveLayout ||
|
|||
|
( driveLayout->Signature != scsiInfo->Signature ) ) {
|
|||
|
goto FnExit;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// We have a signature match. Now get the scsi address.
|
|||
|
//
|
|||
|
|
|||
|
ZeroMemory( &scsiAddress, sizeof(scsiAddress) );
|
|||
|
success = DeviceIoControl( DevHandle,
|
|||
|
IOCTL_SCSI_GET_ADDRESS,
|
|||
|
NULL,
|
|||
|
0,
|
|||
|
&scsiAddress,
|
|||
|
sizeof(scsiAddress),
|
|||
|
&bytesReturned,
|
|||
|
FALSE );
|
|||
|
|
|||
|
if ( !success ) {
|
|||
|
dwError = GetLastError();
|
|||
|
goto FnExit;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Get the disk number.
|
|||
|
//
|
|||
|
|
|||
|
ZeroMemory( &deviceNumber, sizeof(deviceNumber) );
|
|||
|
|
|||
|
success = DeviceIoControl( DevHandle,
|
|||
|
IOCTL_STORAGE_GET_DEVICE_NUMBER,
|
|||
|
NULL,
|
|||
|
0,
|
|||
|
&deviceNumber,
|
|||
|
sizeof(deviceNumber),
|
|||
|
&bytesReturned,
|
|||
|
NULL );
|
|||
|
|
|||
|
if ( !success ) {
|
|||
|
dwError = GetLastError();
|
|||
|
goto FnExit;
|
|||
|
}
|
|||
|
|
|||
|
clusScsiAddress.PortNumber = scsiAddress.PortNumber;
|
|||
|
clusScsiAddress.PathId = scsiAddress.PathId;
|
|||
|
clusScsiAddress.TargetId = scsiAddress.TargetId;
|
|||
|
clusScsiAddress.Lun = scsiAddress.Lun;
|
|||
|
|
|||
|
scsiInfo->ScsiAddress = clusScsiAddress.dw;
|
|||
|
scsiInfo->DiskNumber = deviceNumber.DeviceNumber;
|
|||
|
|
|||
|
dwError = ERROR_POPUP_ALREADY_ACTIVE;
|
|||
|
|
|||
|
FnExit:
|
|||
|
|
|||
|
if ( driveLayout ) {
|
|||
|
LocalFree( driveLayout );
|
|||
|
}
|
|||
|
|
|||
|
return dwError;
|
|||
|
|
|||
|
} // GetScsiAddressCallback
|
|||
|
|
|||
|
|
|||
|
|
|||
|
BOOL
|
|||
|
IsClusterCapable(
|
|||
|
IN DWORD Signature
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Check if the device is cluster capable. If this function cannot read
|
|||
|
the disk information, then it will assume that the device is cluster
|
|||
|
capable!
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Signature - the signature to find.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
TRUE if the device is cluster capable, FALSE otherwise.
|
|||
|
|
|||
|
Notes:
|
|||
|
|
|||
|
On failures... we err on the side of being cluster capable.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
NTSTATUS ntStatus;
|
|||
|
HANDLE fileHandle;
|
|||
|
ANSI_STRING objName;
|
|||
|
UNICODE_STRING unicodeName;
|
|||
|
OBJECT_ATTRIBUTES objAttributes;
|
|||
|
IO_STATUS_BLOCK ioStatusBlock;
|
|||
|
BOOL success;
|
|||
|
DWORD bytesReturned;
|
|||
|
PWCHAR clusDiskControlDevice = L"\\Device\\ClusDisk0";
|
|||
|
|
|||
|
RtlInitUnicodeString( &unicodeName, clusDiskControlDevice );
|
|||
|
|
|||
|
InitializeObjectAttributes( &objAttributes,
|
|||
|
&unicodeName,
|
|||
|
OBJ_CASE_INSENSITIVE,
|
|||
|
NULL,
|
|||
|
NULL );
|
|||
|
|
|||
|
ntStatus = NtCreateFile( &fileHandle,
|
|||
|
SYNCHRONIZE | FILE_READ_DATA | FILE_WRITE_DATA,
|
|||
|
&objAttributes,
|
|||
|
&ioStatusBlock,
|
|||
|
NULL,
|
|||
|
FILE_ATTRIBUTE_NORMAL,
|
|||
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|||
|
FILE_OPEN,
|
|||
|
0,
|
|||
|
NULL,
|
|||
|
0 );
|
|||
|
|
|||
|
if ( !NT_SUCCESS(ntStatus) ) {
|
|||
|
return(TRUE);
|
|||
|
}
|
|||
|
|
|||
|
success = DeviceIoControl( fileHandle,
|
|||
|
IOCTL_DISK_CLUSTER_NOT_CLUSTER_CAPABLE,
|
|||
|
&Signature,
|
|||
|
sizeof(DWORD),
|
|||
|
NULL,
|
|||
|
0,
|
|||
|
&bytesReturned,
|
|||
|
FALSE );
|
|||
|
NtClose( fileHandle );
|
|||
|
|
|||
|
return(!success);
|
|||
|
|
|||
|
} // IsClusterCapable
|
|||
|
|
|||
|
|
|||
|
|
|||
|
DWORD
|
|||
|
GetDiskInfo(
|
|||
|
IN DWORD Signature,
|
|||
|
OUT PVOID *OutBuffer,
|
|||
|
IN DWORD OutBufferSize,
|
|||
|
OUT LPDWORD BytesReturned,
|
|||
|
IN BOOL FtSet
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Gets all of the disk information for a given signature.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Signature - the signature of the disk to return info.
|
|||
|
|
|||
|
OutBuffer - pointer to the output buffer to return the data.
|
|||
|
|
|||
|
OutBufferSize - size of the output buffer.
|
|||
|
|
|||
|
BytesReturned - the actual number of bytes that were returned (or
|
|||
|
the number of bytes that should have been returned if
|
|||
|
OutBufferSize is too small).
|
|||
|
|
|||
|
FtSet - TRUE if FT Set info wanted, FALSE otherwise.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
ERROR_SUCCESS if successful.
|
|||
|
|
|||
|
A Win32 error on failure.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
DWORD status;
|
|||
|
DWORD bytesReturned = *BytesReturned;
|
|||
|
PVOID ptrBuffer = *OutBuffer;
|
|||
|
PCLUSPROP_DWORD ptrDword;
|
|||
|
PCLUSPROP_SCSI_ADDRESS ptrScsiAddress;
|
|||
|
PCLUSPROP_DISK_NUMBER ptrDiskNumber;
|
|||
|
PCLUSPROP_PARTITION_INFO ptrPartitionInfo;
|
|||
|
PCLUSPROP_SZ ptrSerialNumber;
|
|||
|
DWORD cbSzSize;
|
|||
|
DWORD cbDataSize;
|
|||
|
DWORD scsiAddress;
|
|||
|
DWORD diskNumber;
|
|||
|
|
|||
|
NTSTATUS ntStatus;
|
|||
|
CHAR driveLetter;
|
|||
|
DWORD i;
|
|||
|
MOUNTIE_INFO mountieInfo;
|
|||
|
PMOUNTIE_PARTITION entry;
|
|||
|
PWCHAR serialNumber = NULL;
|
|||
|
|
|||
|
WCHAR szDiskPartName[MAX_PATH];
|
|||
|
WCHAR szGlobalDiskPartName[MAX_PATH];
|
|||
|
|
|||
|
LONGLONG llCurrentMinUsablePartLength = 0x7FFFFFFFFFFFFFFF;
|
|||
|
PCLUSPROP_PARTITION_INFO ptrMinUsablePartitionInfo = NULL;
|
|||
|
|
|||
|
// Return the signature - a DWORD
|
|||
|
bytesReturned += sizeof(CLUSPROP_DWORD);
|
|||
|
if ( bytesReturned <= OutBufferSize ) {
|
|||
|
ptrDword = ptrBuffer;
|
|||
|
ptrDword->Syntax.dw = CLUSPROP_SYNTAX_DISK_SIGNATURE;
|
|||
|
ptrDword->cbLength = sizeof(DWORD);
|
|||
|
ptrDword->dw = Signature;
|
|||
|
ptrDword++;
|
|||
|
ptrBuffer = ptrDword;
|
|||
|
}
|
|||
|
|
|||
|
// Return the SCSI_ADDRESS - a DWORD
|
|||
|
status = GetScsiAddress( Signature,
|
|||
|
&scsiAddress,
|
|||
|
&diskNumber );
|
|||
|
|
|||
|
if ( status == ERROR_SUCCESS ) {
|
|||
|
bytesReturned += sizeof(CLUSPROP_SCSI_ADDRESS);
|
|||
|
if ( bytesReturned <= OutBufferSize ) {
|
|||
|
ptrScsiAddress = ptrBuffer;
|
|||
|
ptrScsiAddress->Syntax.dw = CLUSPROP_SYNTAX_SCSI_ADDRESS;
|
|||
|
ptrScsiAddress->cbLength = sizeof(DWORD);
|
|||
|
ptrScsiAddress->dw = scsiAddress;
|
|||
|
ptrScsiAddress++;
|
|||
|
ptrBuffer = ptrScsiAddress;
|
|||
|
}
|
|||
|
|
|||
|
// Return the DISK NUMBER - a DWORD
|
|||
|
bytesReturned += sizeof(CLUSPROP_DISK_NUMBER);
|
|||
|
if ( bytesReturned <= OutBufferSize ) {
|
|||
|
ptrDiskNumber = ptrBuffer;
|
|||
|
ptrDiskNumber->Syntax.dw = CLUSPROP_SYNTAX_DISK_NUMBER;
|
|||
|
ptrDiskNumber->cbLength = sizeof(DWORD);
|
|||
|
ptrDiskNumber->dw = diskNumber;
|
|||
|
ptrDiskNumber++;
|
|||
|
ptrBuffer = ptrDiskNumber;
|
|||
|
}
|
|||
|
} else {
|
|||
|
return( status);
|
|||
|
}
|
|||
|
|
|||
|
// Get the disk serial number.
|
|||
|
|
|||
|
status = GetSerialNumber( Signature,
|
|||
|
&serialNumber );
|
|||
|
|
|||
|
if ( ERROR_SUCCESS == status && serialNumber ) {
|
|||
|
|
|||
|
cbSzSize = (wcslen( serialNumber ) + 1) * sizeof(WCHAR);
|
|||
|
cbDataSize = sizeof(CLUSPROP_SZ) + ALIGN_CLUSPROP( cbSzSize );
|
|||
|
|
|||
|
bytesReturned += cbDataSize;
|
|||
|
|
|||
|
if ( bytesReturned <= OutBufferSize ) {
|
|||
|
ptrSerialNumber = ptrBuffer;
|
|||
|
ZeroMemory( ptrSerialNumber, cbDataSize );
|
|||
|
ptrSerialNumber->Syntax.dw = CLUSPROP_SYNTAX_DISK_SERIALNUMBER;
|
|||
|
ptrSerialNumber->cbLength = cbSzSize;
|
|||
|
wcsncpy( ptrSerialNumber->sz, serialNumber, wcslen(serialNumber) );
|
|||
|
ptrBuffer = (PCHAR)ptrBuffer + cbDataSize;
|
|||
|
}
|
|||
|
|
|||
|
if ( serialNumber ) {
|
|||
|
LocalFree( serialNumber );
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// Get all the valid partitions on the disk. We must free the
|
|||
|
// volume info structure later.
|
|||
|
|
|||
|
status = MountieFindPartitionsForDisk( diskNumber,
|
|||
|
&mountieInfo
|
|||
|
);
|
|||
|
|
|||
|
if ( ERROR_SUCCESS == status ) {
|
|||
|
|
|||
|
// For each partition, build a Property List.
|
|||
|
|
|||
|
for ( i = 0; i < MountiePartitionCount( &mountieInfo ); ++i ) {
|
|||
|
|
|||
|
entry = MountiePartition( &mountieInfo, i );
|
|||
|
|
|||
|
if ( !entry ) {
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
// Always update the bytesReturned, even if there is more data than the
|
|||
|
// caller requested. On return, the caller will see that there is more
|
|||
|
// data available.
|
|||
|
|
|||
|
bytesReturned += sizeof(CLUSPROP_PARTITION_INFO);
|
|||
|
|
|||
|
if ( bytesReturned <= OutBufferSize ) {
|
|||
|
ptrPartitionInfo = ptrBuffer;
|
|||
|
ZeroMemory( ptrPartitionInfo, sizeof(CLUSPROP_PARTITION_INFO) );
|
|||
|
ptrPartitionInfo->Syntax.dw = CLUSPROP_SYNTAX_PARTITION_INFO;
|
|||
|
ptrPartitionInfo->cbLength = sizeof(CLUSPROP_PARTITION_INFO) - sizeof(CLUSPROP_VALUE);
|
|||
|
|
|||
|
// Create a name that can be used with some of our routines.
|
|||
|
// Don't use the drive letter as the name because we might be using
|
|||
|
// partitions without drive letters assigned.
|
|||
|
|
|||
|
_snwprintf( szDiskPartName,
|
|||
|
sizeof(szDiskPartName)/sizeof(WCHAR),
|
|||
|
DEVICE_HARDDISK_PARTITION_FMT,
|
|||
|
diskNumber,
|
|||
|
entry->PartitionNumber );
|
|||
|
|
|||
|
//
|
|||
|
// Create a global DiskPart name that we can use with the Win32
|
|||
|
// GetVolumeInformationW call. This name must have a trailing
|
|||
|
// backslash to work correctly.
|
|||
|
//
|
|||
|
|
|||
|
_snwprintf( szGlobalDiskPartName,
|
|||
|
sizeof(szGlobalDiskPartName)/sizeof(WCHAR),
|
|||
|
GLOBALROOT_HARDDISK_PARTITION_FMT,
|
|||
|
diskNumber,
|
|||
|
entry->PartitionNumber );
|
|||
|
|
|||
|
|
|||
|
// If partition has a drive letter assigned, return this info.
|
|||
|
// If no drive letter assigned, need a system-wide (i.e. across nodes)
|
|||
|
// way of identifying the device.
|
|||
|
|
|||
|
ntStatus = GetAssignedLetter( szDiskPartName, &driveLetter );
|
|||
|
|
|||
|
if ( NT_SUCCESS(status) && driveLetter ) {
|
|||
|
|
|||
|
// Return the drive letter as the device name.
|
|||
|
|
|||
|
_snwprintf( ptrPartitionInfo->szDeviceName,
|
|||
|
sizeof(ptrPartitionInfo->szDeviceName),
|
|||
|
L"%hc:",
|
|||
|
driveLetter );
|
|||
|
|
|||
|
ptrPartitionInfo->dwFlags |= CLUSPROP_PIFLAG_STICKY;
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
// Return a physical device name.
|
|||
|
|
|||
|
#if 0
|
|||
|
|
|||
|
// Return string name:
|
|||
|
// Signature xxxxxxxx Partition YYY
|
|||
|
|
|||
|
_snwprintf( ptrPartitionInfo->szDeviceName,
|
|||
|
sizeof(ptrPartitionInfo->szDeviceName),
|
|||
|
L"Signature %X Partition %u",
|
|||
|
Signature,
|
|||
|
entry->PartitionNumber );
|
|||
|
#endif
|
|||
|
|
|||
|
// Return string name:
|
|||
|
// DiskXXXPartionYYY
|
|||
|
|
|||
|
_snwprintf( ptrPartitionInfo->szDeviceName,
|
|||
|
sizeof(ptrPartitionInfo->szDeviceName),
|
|||
|
L"Disk%uPartition%u",
|
|||
|
diskNumber,
|
|||
|
entry->PartitionNumber );
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Call GetVolumeInformationW with the GlobalName we have created.
|
|||
|
//
|
|||
|
|
|||
|
if ( !GetVolumeInformationW ( szGlobalDiskPartName,
|
|||
|
ptrPartitionInfo->szVolumeLabel,
|
|||
|
sizeof(ptrPartitionInfo->szVolumeLabel)/sizeof(WCHAR),
|
|||
|
&ptrPartitionInfo->dwSerialNumber,
|
|||
|
&ptrPartitionInfo->rgdwMaximumComponentLength,
|
|||
|
&ptrPartitionInfo->dwFileSystemFlags,
|
|||
|
ptrPartitionInfo->szFileSystem,
|
|||
|
sizeof(ptrPartitionInfo->szFileSystem)/sizeof(WCHAR) ) ) {
|
|||
|
|
|||
|
ptrPartitionInfo->szVolumeLabel[0] = L'\0';
|
|||
|
|
|||
|
} else if ( CompareStringW( LOCALE_INVARIANT,
|
|||
|
NORM_IGNORECASE,
|
|||
|
ptrPartitionInfo->szFileSystem,
|
|||
|
-1,
|
|||
|
L"NTFS",
|
|||
|
-1
|
|||
|
) == CSTR_EQUAL ) {
|
|||
|
|
|||
|
// Only NTFS drives are currently supported.
|
|||
|
|
|||
|
ptrPartitionInfo->dwFlags |= CLUSPROP_PIFLAG_USABLE;
|
|||
|
|
|||
|
//
|
|||
|
// Find the minimum size partition that is larger than MIN_QUORUM_PARTITION_LENGTH
|
|||
|
//
|
|||
|
if ( ( entry->PartitionLength.QuadPart >= MIN_USABLE_QUORUM_PARTITION_LENGTH ) &&
|
|||
|
( entry->PartitionLength.QuadPart < llCurrentMinUsablePartLength ) )
|
|||
|
{
|
|||
|
ptrMinUsablePartitionInfo = ptrPartitionInfo;
|
|||
|
llCurrentMinUsablePartLength = entry->PartitionLength.QuadPart;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
ptrPartitionInfo++;
|
|||
|
ptrBuffer = ptrPartitionInfo;
|
|||
|
}
|
|||
|
|
|||
|
} // for
|
|||
|
|
|||
|
// Free the volume information.
|
|||
|
|
|||
|
MountieCleanup( &mountieInfo );
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If we managed to find a default quorum partition, change the flags to indicate this.
|
|||
|
//
|
|||
|
if ( ptrMinUsablePartitionInfo != NULL )
|
|||
|
{
|
|||
|
ptrMinUsablePartitionInfo->dwFlags |= CLUSPROP_PIFLAG_DEFAULT_QUORUM;
|
|||
|
}
|
|||
|
|
|||
|
*OutBuffer = ptrBuffer;
|
|||
|
*BytesReturned = bytesReturned;
|
|||
|
|
|||
|
return(status);
|
|||
|
|
|||
|
} // GetDiskInfo
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
DWORD
|
|||
|
UpdateAvailableDisks(
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
ERROR_SUCCESS if successful.
|
|||
|
|
|||
|
A Win32 error on failure.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
HKEY availDisksKey;
|
|||
|
HKEY sigKey;
|
|||
|
|
|||
|
DWORD dwError = NO_ERROR;
|
|||
|
DWORD enableSanBoot;
|
|||
|
|
|||
|
BOOL availDisksOpened = FALSE;
|
|||
|
BOOL sigKeyOpened = FALSE;
|
|||
|
BOOL sigKeyIsEmpty = FALSE;
|
|||
|
|
|||
|
UPDATE_AVAIL_DISKS updateDisks;
|
|||
|
|
|||
|
__try {
|
|||
|
|
|||
|
//
|
|||
|
// If we haven't yet determined the system disk information, don't do anything else.
|
|||
|
//
|
|||
|
|
|||
|
if ( !SystemDiskAddressFound ) {
|
|||
|
|
|||
|
__leave;
|
|||
|
}
|
|||
|
|
|||
|
enableSanBoot = 0;
|
|||
|
GetRegDwordValue( CLUSREG_KEYNAME_CLUSSVC_PARAMETERS,
|
|||
|
CLUSREG_VALUENAME_MANAGEDISKSONSYSTEMBUSES,
|
|||
|
&enableSanBoot );
|
|||
|
|
|||
|
//
|
|||
|
// Delete the old AvailableDisks key. This will remove any stale information.
|
|||
|
//
|
|||
|
|
|||
|
SHDeleteKey( HKEY_LOCAL_MACHINE, CLUSDISK_REGISTRY_AVAILABLE_DISKS );
|
|||
|
|
|||
|
//
|
|||
|
// Open the AvailableDisks key. If the key doesn't exist, it will be created.
|
|||
|
//
|
|||
|
|
|||
|
dwError = RegCreateKeyEx( HKEY_LOCAL_MACHINE,
|
|||
|
CLUSDISK_REGISTRY_AVAILABLE_DISKS,
|
|||
|
0,
|
|||
|
NULL,
|
|||
|
REG_OPTION_NON_VOLATILE,
|
|||
|
KEY_CREATE_SUB_KEY,
|
|||
|
NULL,
|
|||
|
&availDisksKey,
|
|||
|
NULL );
|
|||
|
|
|||
|
if ( NO_ERROR != dwError) {
|
|||
|
__leave;
|
|||
|
}
|
|||
|
|
|||
|
availDisksOpened = TRUE;
|
|||
|
|
|||
|
//
|
|||
|
// Open the Signatures key.
|
|||
|
//
|
|||
|
|
|||
|
dwError = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
|
|||
|
CLUSDISK_REGISTRY_SIGNATURES,
|
|||
|
0,
|
|||
|
KEY_READ,
|
|||
|
&sigKey );
|
|||
|
|
|||
|
//
|
|||
|
// If Signatures key does not exist, save all valid signatures
|
|||
|
// in the AvailableDisks key.
|
|||
|
//
|
|||
|
|
|||
|
if ( ERROR_FILE_NOT_FOUND == dwError ) {
|
|||
|
dwError = NO_ERROR;
|
|||
|
sigKeyIsEmpty = TRUE;
|
|||
|
} else if ( NO_ERROR != dwError) {
|
|||
|
__leave;
|
|||
|
} else {
|
|||
|
sigKeyOpened = TRUE;
|
|||
|
}
|
|||
|
|
|||
|
ZeroMemory( &updateDisks, sizeof(updateDisks) );
|
|||
|
updateDisks.EnableSanBoot = enableSanBoot;
|
|||
|
updateDisks.SigKeyIsEmpty = sigKeyIsEmpty;
|
|||
|
updateDisks.SigKey = sigKey;
|
|||
|
updateDisks.AvailDisksKey = availDisksKey;
|
|||
|
|
|||
|
EnumerateDisks( UpdateAvailableDisksCallback, &updateDisks );
|
|||
|
|
|||
|
} __finally {
|
|||
|
|
|||
|
if ( availDisksOpened ) {
|
|||
|
RegCloseKey( availDisksKey );
|
|||
|
}
|
|||
|
|
|||
|
if ( sigKeyOpened ) {
|
|||
|
RegCloseKey( sigKey );
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return dwError;
|
|||
|
|
|||
|
} // UpdateAvailableDisks
|
|||
|
|
|||
|
|
|||
|
DWORD
|
|||
|
UpdateAvailableDisksCallback(
|
|||
|
HANDLE DeviceHandle,
|
|||
|
DWORD Index,
|
|||
|
PVOID Param1
|
|||
|
)
|
|||
|
{
|
|||
|
PDRIVE_LAYOUT_INFORMATION driveLayout = NULL;
|
|||
|
PUPDATE_AVAIL_DISKS updateDisks = Param1;
|
|||
|
|
|||
|
DWORD bytesReturned;
|
|||
|
DWORD enableSanBoot;
|
|||
|
|
|||
|
BOOL success;
|
|||
|
|
|||
|
SCSI_ADDRESS scsiAddress;
|
|||
|
|
|||
|
//
|
|||
|
// Look at all disks on the system. For each valid signature, add it to the
|
|||
|
// AvailableList if it is not already in the Signature key.
|
|||
|
//
|
|||
|
success = ClRtlGetDriveLayoutTable( DeviceHandle,
|
|||
|
&driveLayout,
|
|||
|
NULL );
|
|||
|
|
|||
|
if ( success && driveLayout &&
|
|||
|
( 0 != driveLayout->Signature ) ) {
|
|||
|
|
|||
|
//
|
|||
|
// Get SCSI address info.
|
|||
|
//
|
|||
|
|
|||
|
if ( DeviceIoControl( DeviceHandle,
|
|||
|
IOCTL_SCSI_GET_ADDRESS,
|
|||
|
NULL,
|
|||
|
0,
|
|||
|
&scsiAddress,
|
|||
|
sizeof(scsiAddress),
|
|||
|
&bytesReturned,
|
|||
|
NULL ) ) {
|
|||
|
|
|||
|
if ( !updateDisks->EnableSanBoot ) {
|
|||
|
|
|||
|
//
|
|||
|
// Add signature to AvailableDisks key if:
|
|||
|
// - the signature is for a disk not on system bus
|
|||
|
// - the signature is not already in the Signatures key
|
|||
|
//
|
|||
|
|
|||
|
if ( !IsDiskOnSystemBus( &scsiAddress ) &&
|
|||
|
( updateDisks->SigKeyIsEmpty ||
|
|||
|
!IsSignatureInRegistry( updateDisks->SigKey, driveLayout->Signature ) ) ) {
|
|||
|
|
|||
|
AddSignatureToRegistry( updateDisks->AvailDisksKey,
|
|||
|
driveLayout->Signature );
|
|||
|
}
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
// Allow disks on system bus to be added to cluster.
|
|||
|
|
|||
|
//
|
|||
|
// Add signature to AvailableDisks key if:
|
|||
|
// - the signature is not for the system disk
|
|||
|
// - the signature is not already in the Signatures key
|
|||
|
//
|
|||
|
|
|||
|
if ( !IsDiskSystemDisk( &scsiAddress ) &&
|
|||
|
( updateDisks->SigKeyIsEmpty ||
|
|||
|
!IsSignatureInRegistry( updateDisks->SigKey, driveLayout->Signature ) ) ) {
|
|||
|
|
|||
|
AddSignatureToRegistry( updateDisks->AvailDisksKey,
|
|||
|
driveLayout->Signature );
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
if ( driveLayout ) {
|
|||
|
LocalFree( driveLayout );
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Always return success so all disks are enumerated.
|
|||
|
//
|
|||
|
|
|||
|
return ERROR_SUCCESS;
|
|||
|
|
|||
|
} // UpdateAvailableDisksCallback
|
|||
|
|
|||
|
|
|||
|
DWORD
|
|||
|
AddSignatureToRegistry(
|
|||
|
HKEY RegKey,
|
|||
|
DWORD Signature
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Add the specified disk signature to the ClusDisk registry subkey.
|
|||
|
The disk signatures are subkeys of the ClusDisk\Parameters\AvailableDisks
|
|||
|
and ClusDisk\Parameters\Signatures keys.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
RegKey - Previously opened ClusDisk registry subkey
|
|||
|
|
|||
|
Signature - Signature value to add
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
Win32 error on failure.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
HKEY subKey;
|
|||
|
DWORD dwError;
|
|||
|
|
|||
|
CHAR signatureName[MAX_PATH];
|
|||
|
|
|||
|
ZeroMemory( signatureName, sizeof(signatureName) );
|
|||
|
_snprintf( signatureName, 8, "%08X", Signature );
|
|||
|
|
|||
|
//
|
|||
|
// Try and create the key. If it exists, the existing key will be opened.
|
|||
|
//
|
|||
|
|
|||
|
dwError = RegCreateKeyEx( RegKey,
|
|||
|
signatureName,
|
|||
|
0,
|
|||
|
NULL,
|
|||
|
REG_OPTION_NON_VOLATILE,
|
|||
|
KEY_WRITE,
|
|||
|
NULL,
|
|||
|
&subKey,
|
|||
|
NULL );
|
|||
|
|
|||
|
//
|
|||
|
// If the key exists, ERROR_SUCCESS is still returned.
|
|||
|
//
|
|||
|
|
|||
|
if ( ERROR_SUCCESS == dwError ) {
|
|||
|
RegCloseKey( subKey );
|
|||
|
}
|
|||
|
|
|||
|
return dwError;
|
|||
|
|
|||
|
} // AddSignatureToRegistry
|
|||
|
|
|||
|
|
|||
|
BOOL
|
|||
|
IsSignatureInRegistry(
|
|||
|
HKEY RegKey,
|
|||
|
DWORD Signature
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Check if the specified disk signature is in the ClusDisk registry subkey.
|
|||
|
The disk signatures are subkeys of the ClusDisk\Parameters\AvailableDisks
|
|||
|
and ClusDisk\Parameters\Signatures keys.
|
|||
|
|
|||
|
On error, assume the key is in the registry so it is not recreated.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
RegKey - Previously opened ClusDisk registry subkey
|
|||
|
|
|||
|
Signature - Signature value to check
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
TRUE - Signature is in registry
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
DWORD ival;
|
|||
|
DWORD sig;
|
|||
|
DWORD dataLength;
|
|||
|
DWORD dwError;
|
|||
|
|
|||
|
BOOL retVal = FALSE;
|
|||
|
|
|||
|
CHAR signatureName[9];
|
|||
|
|
|||
|
for ( ival = 0; ; ival++ ) {
|
|||
|
dataLength = sizeof(signatureName);
|
|||
|
|
|||
|
dwError = RegEnumKey( RegKey,
|
|||
|
ival,
|
|||
|
signatureName,
|
|||
|
dataLength );
|
|||
|
|
|||
|
// If the list is exhausted, return FALSE.
|
|||
|
|
|||
|
if ( ERROR_NO_MORE_ITEMS == dwError ) {
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
// If some other type of error, return TRUE.
|
|||
|
|
|||
|
if ( ERROR_SUCCESS != dwError ) {
|
|||
|
retVal = TRUE;
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
dataLength = sscanf( signatureName, "%x", &sig );
|
|||
|
if ( dataLength != 1 ) {
|
|||
|
retVal = TRUE;
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
// If signature is a subkey, return TRUE.
|
|||
|
|
|||
|
if ( sig == Signature ) {
|
|||
|
retVal = TRUE;
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return retVal;
|
|||
|
|
|||
|
} // IsSignatureInRegistry
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
GetSystemBusInfo(
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Need to find out where the NTLDR files reside (the "system disk") and where the
|
|||
|
OS files reside (the "boot disk"). We will call all these disks the "system disk".
|
|||
|
|
|||
|
There may be more than one system disk if the disks are mirrored. So if the NTLDR
|
|||
|
files are on a different disk than the OS files, and each of these disks is
|
|||
|
mirrored, we could be looking at 4 different disks.
|
|||
|
|
|||
|
Find all the system disks and save the information in a list we can look at later.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
HANDLE hOsDevice = INVALID_HANDLE_VALUE;
|
|||
|
HANDLE hNtldrDevice = INVALID_HANDLE_VALUE;
|
|||
|
|
|||
|
DWORD dwError;
|
|||
|
|
|||
|
//
|
|||
|
// Open the disk with the OS files on it.
|
|||
|
//
|
|||
|
|
|||
|
hOsDevice = OpenOSDisk();
|
|||
|
|
|||
|
if ( INVALID_HANDLE_VALUE == hOsDevice ) {
|
|||
|
goto FnExit;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Find all mirrored disks and add them to the SCSI address list.
|
|||
|
//
|
|||
|
|
|||
|
if ( ERROR_SUCCESS != BuildScsiListFromFailover( hOsDevice ) ) {
|
|||
|
goto FnExit;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Now find the disks with NTLDR on it. Disk could be mirrored.
|
|||
|
//
|
|||
|
|
|||
|
hNtldrDevice = OpenNtldrDisk();
|
|||
|
|
|||
|
if ( INVALID_HANDLE_VALUE == hNtldrDevice ) {
|
|||
|
goto FnExit;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Find all mirrored disks and add them to the SCSI address list.
|
|||
|
//
|
|||
|
|
|||
|
if ( ERROR_SUCCESS != BuildScsiListFromFailover( hNtldrDevice ) ) {
|
|||
|
goto FnExit;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Indicate we went through this code one more time.
|
|||
|
//
|
|||
|
|
|||
|
SystemDiskAddressFound++;
|
|||
|
|
|||
|
FnExit:
|
|||
|
|
|||
|
if ( INVALID_HANDLE_VALUE != hOsDevice ) {
|
|||
|
CloseHandle( hOsDevice );
|
|||
|
}
|
|||
|
if ( INVALID_HANDLE_VALUE != hNtldrDevice ) {
|
|||
|
CloseHandle( hNtldrDevice );
|
|||
|
}
|
|||
|
|
|||
|
return;
|
|||
|
|
|||
|
} // GetSystemBusInfo
|
|||
|
|
|||
|
|
|||
|
HANDLE
|
|||
|
OpenOSDisk(
|
|||
|
)
|
|||
|
{
|
|||
|
PCHAR systemDir = NULL;
|
|||
|
|
|||
|
HANDLE hDevice = INVALID_HANDLE_VALUE;
|
|||
|
DWORD len;
|
|||
|
|
|||
|
CHAR systemPath[] = "\\\\.\\?:";
|
|||
|
|
|||
|
//
|
|||
|
// First find the disks with OS files. Disk could be mirrored.
|
|||
|
//
|
|||
|
|
|||
|
systemDir = LocalAlloc( LPTR, MAX_PATH );
|
|||
|
|
|||
|
if ( !systemDir ) {
|
|||
|
goto FnExit;
|
|||
|
}
|
|||
|
|
|||
|
len = GetSystemDirectory( systemDir,
|
|||
|
MAX_PATH );
|
|||
|
|
|||
|
if ( !len || len < 3 ) {
|
|||
|
goto FnExit;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If system path doesn't start with a drive letter, exit.
|
|||
|
// c:\windows ==> c:
|
|||
|
|
|||
|
if ( ':' != systemDir[1] ) {
|
|||
|
goto FnExit;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Stuff the drive letter in the system path.
|
|||
|
//
|
|||
|
|
|||
|
systemPath[4] = systemDir[0];
|
|||
|
|
|||
|
//
|
|||
|
// Now open the device.
|
|||
|
//
|
|||
|
|
|||
|
hDevice = CreateFile( systemPath,
|
|||
|
GENERIC_READ | GENERIC_WRITE,
|
|||
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|||
|
NULL,
|
|||
|
OPEN_EXISTING,
|
|||
|
FILE_ATTRIBUTE_NORMAL,
|
|||
|
NULL );
|
|||
|
|
|||
|
FnExit:
|
|||
|
|
|||
|
if ( systemDir ) {
|
|||
|
LocalFree( systemDir );
|
|||
|
}
|
|||
|
|
|||
|
return hDevice;
|
|||
|
|
|||
|
} // OpenOSDisk
|
|||
|
|
|||
|
|
|||
|
HANDLE
|
|||
|
OpenNtldrDisk(
|
|||
|
)
|
|||
|
{
|
|||
|
PSTR systemPartition = NULL;
|
|||
|
|
|||
|
HANDLE hDevice = INVALID_HANDLE_VALUE;
|
|||
|
|
|||
|
HKEY regKey = NULL;
|
|||
|
|
|||
|
NTSTATUS ntStatus;
|
|||
|
|
|||
|
DWORD dwError;
|
|||
|
DWORD cbSystemPartition;
|
|||
|
DWORD cbDeviceName;
|
|||
|
DWORD type = 0;
|
|||
|
|
|||
|
ANSI_STRING objName;
|
|||
|
UNICODE_STRING unicodeName;
|
|||
|
OBJECT_ATTRIBUTES objAttributes;
|
|||
|
IO_STATUS_BLOCK ioStatusBlock;
|
|||
|
|
|||
|
//
|
|||
|
// Open the reg key to find the system partition
|
|||
|
//
|
|||
|
|
|||
|
dwError = RegOpenKeyEx( HKEY_LOCAL_MACHINE, // hKey
|
|||
|
DISKS_REGKEY_SETUP, // lpSubKey
|
|||
|
0, // ulOptions--Reserved, must be 0
|
|||
|
KEY_READ, // samDesired
|
|||
|
®Key // phkResult
|
|||
|
);
|
|||
|
|
|||
|
if ( ERROR_SUCCESS != dwError ) {
|
|||
|
goto FnExit;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Allocate a reasonably sized buffer for the system partition, to
|
|||
|
// start off with. If this isn't big enough, we'll re-allocate as
|
|||
|
// needed.
|
|||
|
//
|
|||
|
|
|||
|
cbSystemPartition = MAX_PATH + 1;
|
|||
|
systemPartition = LocalAlloc( LPTR, cbSystemPartition );
|
|||
|
|
|||
|
if ( !systemPartition ) {
|
|||
|
goto FnExit;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Get the system partition device Name. This is of the form
|
|||
|
// \Device\Harddisk0\Partition1 (basic disks)
|
|||
|
// \Device\HarddiskDmVolumes\DgName\Volume1 (dynamic disks)
|
|||
|
//
|
|||
|
|
|||
|
dwError = RegQueryValueEx( regKey,
|
|||
|
DISKS_REGVALUE_SYSTEM_PARTITION,
|
|||
|
NULL,
|
|||
|
&type,
|
|||
|
(LPBYTE)systemPartition,
|
|||
|
&cbSystemPartition // \0 is included
|
|||
|
);
|
|||
|
|
|||
|
while ( ERROR_MORE_DATA == dwError ) {
|
|||
|
|
|||
|
//
|
|||
|
// Our buffer wasn't big enough, cbSystemPartition contains
|
|||
|
// the required size.
|
|||
|
//
|
|||
|
|
|||
|
LocalFree( systemPartition );
|
|||
|
systemPartition = NULL;
|
|||
|
|
|||
|
systemPartition = LocalAlloc( LPTR, cbSystemPartition );
|
|||
|
|
|||
|
if ( !systemPartition ) {
|
|||
|
goto FnExit;
|
|||
|
}
|
|||
|
|
|||
|
dwError = RegQueryValueEx( regKey,
|
|||
|
DISKS_REGVALUE_SYSTEM_PARTITION,
|
|||
|
NULL,
|
|||
|
&type,
|
|||
|
(LPBYTE)systemPartition,
|
|||
|
&cbSystemPartition // \0 is included
|
|||
|
);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
RtlInitString( &objName, systemPartition );
|
|||
|
ntStatus = RtlAnsiStringToUnicodeString( &unicodeName,
|
|||
|
&objName,
|
|||
|
TRUE );
|
|||
|
|
|||
|
if ( !NT_SUCCESS(ntStatus) ) {
|
|||
|
goto FnExit;
|
|||
|
}
|
|||
|
|
|||
|
InitializeObjectAttributes( &objAttributes,
|
|||
|
&unicodeName,
|
|||
|
OBJ_CASE_INSENSITIVE,
|
|||
|
NULL,
|
|||
|
NULL );
|
|||
|
|
|||
|
ntStatus = NtCreateFile( &hDevice,
|
|||
|
SYNCHRONIZE | FILE_READ_DATA | FILE_WRITE_DATA,
|
|||
|
&objAttributes,
|
|||
|
&ioStatusBlock,
|
|||
|
NULL,
|
|||
|
FILE_ATTRIBUTE_NORMAL,
|
|||
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|||
|
FILE_OPEN,
|
|||
|
0,
|
|||
|
NULL,
|
|||
|
0 );
|
|||
|
|
|||
|
RtlFreeUnicodeString( &unicodeName );
|
|||
|
|
|||
|
FnExit:
|
|||
|
|
|||
|
if ( regKey ) {
|
|||
|
RegCloseKey( regKey );
|
|||
|
}
|
|||
|
|
|||
|
if ( systemPartition ) {
|
|||
|
LocalFree( systemPartition );
|
|||
|
}
|
|||
|
|
|||
|
return hDevice;
|
|||
|
|
|||
|
} // OpenNtldrDisk
|
|||
|
|
|||
|
|
|||
|
DWORD
|
|||
|
BuildScsiListFromFailover(
|
|||
|
IN HANDLE DevHandle
|
|||
|
)
|
|||
|
{
|
|||
|
PVOLUME_FAILOVER_SET failoverSet = NULL;
|
|||
|
|
|||
|
HANDLE hDevice;
|
|||
|
|
|||
|
DWORD dwError = ERROR_SUCCESS;
|
|||
|
DWORD idx;
|
|||
|
DWORD bytesReturned;
|
|||
|
|
|||
|
SCSI_ADDRESS scsiAddress;
|
|||
|
|
|||
|
PCHAR deviceName = NULL;
|
|||
|
|
|||
|
deviceName = LocalAlloc( LPTR, MAX_PATH+1 );
|
|||
|
|
|||
|
if ( !deviceName ) {
|
|||
|
dwError = GetLastError();
|
|||
|
goto FnExit;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Find out how many physical disks are represented by this device.
|
|||
|
//
|
|||
|
|
|||
|
dwError = GetVolumeFailoverSet( DevHandle, &failoverSet );
|
|||
|
|
|||
|
if ( ERROR_SUCCESS != dwError || !failoverSet ) {
|
|||
|
goto FnExit;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// For each physical disk, get the scsi address and add it to the list.
|
|||
|
//
|
|||
|
|
|||
|
for ( idx = 0; idx < failoverSet->NumberOfDisks; idx++ ) {
|
|||
|
|
|||
|
ZeroMemory( deviceName, MAX_PATH );
|
|||
|
_snprintf( deviceName,
|
|||
|
MAX_PATH,
|
|||
|
"\\\\.\\\\PhysicalDrive%d",
|
|||
|
failoverSet->DiskNumbers[idx] );
|
|||
|
|
|||
|
hDevice = CreateFile( deviceName,
|
|||
|
GENERIC_READ | GENERIC_WRITE,
|
|||
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|||
|
NULL,
|
|||
|
OPEN_EXISTING,
|
|||
|
FILE_ATTRIBUTE_NORMAL,
|
|||
|
NULL );
|
|||
|
|
|||
|
if ( INVALID_HANDLE_VALUE == hDevice ) {
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
ZeroMemory( &scsiAddress, sizeof( scsiAddress ) );
|
|||
|
if ( !DeviceIoControl( hDevice,
|
|||
|
IOCTL_SCSI_GET_ADDRESS,
|
|||
|
NULL,
|
|||
|
0,
|
|||
|
&scsiAddress,
|
|||
|
sizeof(scsiAddress),
|
|||
|
&bytesReturned,
|
|||
|
NULL ) ) {
|
|||
|
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
CloseHandle( hDevice );
|
|||
|
hDevice = INVALID_HANDLE_VALUE;
|
|||
|
|
|||
|
AddScsiAddressToList( &scsiAddress );
|
|||
|
}
|
|||
|
|
|||
|
FnExit:
|
|||
|
|
|||
|
if ( failoverSet ) {
|
|||
|
LocalFree( failoverSet );
|
|||
|
}
|
|||
|
|
|||
|
if ( deviceName ) {
|
|||
|
LocalFree( deviceName );
|
|||
|
}
|
|||
|
|
|||
|
return dwError;
|
|||
|
|
|||
|
} // BuildScsiListFromFailover
|
|||
|
|
|||
|
|
|||
|
DWORD
|
|||
|
GetVolumeFailoverSet(
|
|||
|
IN HANDLE DevHandle,
|
|||
|
OUT PVOLUME_FAILOVER_SET *FailoverSet
|
|||
|
)
|
|||
|
{
|
|||
|
PVOLUME_FAILOVER_SET failover = NULL;
|
|||
|
|
|||
|
DWORD dwError = ERROR_SUCCESS;
|
|||
|
DWORD bytesReturned;
|
|||
|
DWORD sizeFailover;
|
|||
|
|
|||
|
BOOL result;
|
|||
|
|
|||
|
if ( !FailoverSet ) {
|
|||
|
goto FnExit;
|
|||
|
}
|
|||
|
|
|||
|
sizeFailover = ( sizeof(VOLUME_FAILOVER_SET) + 10 * sizeof(ULONG) );
|
|||
|
|
|||
|
failover = (PVOLUME_FAILOVER_SET) LocalAlloc( LPTR, sizeFailover );
|
|||
|
|
|||
|
if ( !failover ) {
|
|||
|
dwError = GetLastError();
|
|||
|
goto FnExit;
|
|||
|
}
|
|||
|
|
|||
|
result = DeviceIoControl( DevHandle,
|
|||
|
IOCTL_VOLUME_QUERY_FAILOVER_SET,
|
|||
|
NULL,
|
|||
|
0,
|
|||
|
failover,
|
|||
|
sizeFailover,
|
|||
|
&bytesReturned,
|
|||
|
NULL );
|
|||
|
|
|||
|
//
|
|||
|
// We're doing this in a while loop because if the disk configuration
|
|||
|
// changes in the small interval between when we get the reqd buffer
|
|||
|
// size and when we send the ioctl again with a buffer of the "reqd"
|
|||
|
// size, we may still end up with a buffer that isn't big enough.
|
|||
|
//
|
|||
|
|
|||
|
while ( !result ) {
|
|||
|
|
|||
|
dwError = GetLastError();
|
|||
|
|
|||
|
if ( ERROR_MORE_DATA == dwError ) {
|
|||
|
//
|
|||
|
// The buffer was too small, reallocate the requested size.
|
|||
|
//
|
|||
|
|
|||
|
dwError = ERROR_SUCCESS;
|
|||
|
|
|||
|
sizeFailover = ( sizeof(VOLUME_FAILOVER_SET) +
|
|||
|
((failover->NumberOfDisks) * sizeof(ULONG)) );
|
|||
|
|
|||
|
LocalFree( failover );
|
|||
|
|
|||
|
failover = (PVOLUME_FAILOVER_SET) LocalAlloc( LPTR, sizeFailover );
|
|||
|
|
|||
|
if ( !failover ) {
|
|||
|
dwError = GetLastError();
|
|||
|
goto FnExit;
|
|||
|
}
|
|||
|
|
|||
|
result = DeviceIoControl( DevHandle,
|
|||
|
IOCTL_VOLUME_QUERY_FAILOVER_SET,
|
|||
|
NULL,
|
|||
|
0,
|
|||
|
failover,
|
|||
|
sizeFailover,
|
|||
|
&bytesReturned,
|
|||
|
NULL );
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
// Some other error, return error.
|
|||
|
|
|||
|
goto FnExit;
|
|||
|
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
*FailoverSet = failover;
|
|||
|
|
|||
|
FnExit:
|
|||
|
|
|||
|
if ( ERROR_SUCCESS != dwError && failover ) {
|
|||
|
LocalFree( failover );
|
|||
|
}
|
|||
|
|
|||
|
return dwError;
|
|||
|
|
|||
|
} // GetVolumeFailoverSet
|
|||
|
|
|||
|
|
|||
|
DWORD
|
|||
|
AddScsiAddressToList(
|
|||
|
PSCSI_ADDRESS ScsiAddress
|
|||
|
)
|
|||
|
{
|
|||
|
PSCSI_ADDRESS_ENTRY entry;
|
|||
|
|
|||
|
DWORD dwError = ERROR_SUCCESS;
|
|||
|
|
|||
|
//
|
|||
|
// Optimization: don't add the SCSI address if it already
|
|||
|
// matches one in the list.
|
|||
|
//
|
|||
|
|
|||
|
if ( IsDiskSystemDisk( ScsiAddress ) ) {
|
|||
|
goto FnExit;
|
|||
|
}
|
|||
|
|
|||
|
entry = LocalAlloc( LPTR, sizeof( SCSI_ADDRESS_ENTRY ) );
|
|||
|
|
|||
|
if ( !entry ) {
|
|||
|
dwError = GetLastError();
|
|||
|
goto FnExit;
|
|||
|
}
|
|||
|
|
|||
|
entry->ScsiAddress.Length = ScsiAddress->Length;
|
|||
|
entry->ScsiAddress.PortNumber = ScsiAddress->PortNumber;
|
|||
|
entry->ScsiAddress.PathId = ScsiAddress->PathId;
|
|||
|
entry->ScsiAddress.TargetId = ScsiAddress->TargetId;
|
|||
|
entry->ScsiAddress.Lun = ScsiAddress->Lun;
|
|||
|
|
|||
|
if ( SysDiskAddrList ) {
|
|||
|
|
|||
|
entry->Next = SysDiskAddrList;
|
|||
|
SysDiskAddrList = entry;
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
SysDiskAddrList = entry;
|
|||
|
}
|
|||
|
|
|||
|
FnExit:
|
|||
|
|
|||
|
return dwError;
|
|||
|
|
|||
|
} // AddScsiAddressToList
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
CleanupSystemBusInfo(
|
|||
|
)
|
|||
|
{
|
|||
|
PSCSI_ADDRESS_ENTRY entry;
|
|||
|
PSCSI_ADDRESS_ENTRY next;
|
|||
|
|
|||
|
entry = SysDiskAddrList;
|
|||
|
|
|||
|
while ( entry ) {
|
|||
|
next = entry->Next;
|
|||
|
LocalFree( entry );
|
|||
|
entry = next;
|
|||
|
}
|
|||
|
|
|||
|
} // CleanupSystemBusInfo
|
|||
|
|
|||
|
|
|||
|
BOOL
|
|||
|
IsDiskSystemDisk(
|
|||
|
PSCSI_ADDRESS DiskAddr
|
|||
|
)
|
|||
|
{
|
|||
|
PSCSI_ADDRESS_ENTRY entry = SysDiskAddrList;
|
|||
|
PSCSI_ADDRESS_ENTRY next;
|
|||
|
|
|||
|
while ( entry ) {
|
|||
|
next = entry->Next;
|
|||
|
|
|||
|
if ( DiskAddr->PortNumber == entry->ScsiAddress.PortNumber &&
|
|||
|
DiskAddr->PathId == entry->ScsiAddress.PathId &&
|
|||
|
DiskAddr->TargetId == entry->ScsiAddress.TargetId &&
|
|||
|
DiskAddr->Lun == entry->ScsiAddress.Lun ) {
|
|||
|
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
|
|||
|
entry = next;
|
|||
|
}
|
|||
|
|
|||
|
return FALSE;
|
|||
|
|
|||
|
} // IsDiskSystemDisk
|
|||
|
|
|||
|
|
|||
|
BOOL
|
|||
|
IsDiskOnSystemBus(
|
|||
|
PSCSI_ADDRESS DiskAddr
|
|||
|
)
|
|||
|
{
|
|||
|
PSCSI_ADDRESS_ENTRY entry = SysDiskAddrList;
|
|||
|
PSCSI_ADDRESS_ENTRY next;
|
|||
|
|
|||
|
while ( entry ) {
|
|||
|
next = entry->Next;
|
|||
|
|
|||
|
if ( DiskAddr->PortNumber == entry->ScsiAddress.PortNumber &&
|
|||
|
DiskAddr->PathId == entry->ScsiAddress.PathId ) {
|
|||
|
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
|
|||
|
entry = next;
|
|||
|
}
|
|||
|
|
|||
|
return FALSE;
|
|||
|
|
|||
|
} // IsDiskOnSystemBus
|
|||
|
|
|||
|
|
|||
|
DWORD
|
|||
|
EnumerateDisks(
|
|||
|
LPDISK_ENUM_CALLBACK DiskEnumCallback,
|
|||
|
PVOID Param1
|
|||
|
)
|
|||
|
{
|
|||
|
PSP_DEVICE_INTERFACE_DETAIL_DATA pDiDetail = NULL;
|
|||
|
|
|||
|
HANDLE hDevice;
|
|||
|
|
|||
|
DWORD dwError = ERROR_SUCCESS;
|
|||
|
DWORD count;
|
|||
|
DWORD sizeDiDetail;
|
|||
|
|
|||
|
BOOL result;
|
|||
|
|
|||
|
HDEVINFO hdevInfo;
|
|||
|
|
|||
|
SP_DEVICE_INTERFACE_DATA devInterfaceData;
|
|||
|
SP_DEVINFO_DATA devInfoData;
|
|||
|
|
|||
|
if ( !DiskEnumCallback ) {
|
|||
|
dwError = ERROR_INVALID_PARAMETER;
|
|||
|
goto FnExit;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Get a device interface set which includes all Disk devices
|
|||
|
// present on the machine. DiskClassGuid is a predefined GUID that
|
|||
|
// will return all disk-type device interfaces
|
|||
|
//
|
|||
|
|
|||
|
hdevInfo = SetupDiGetClassDevs( &DiskClassGuid,
|
|||
|
NULL,
|
|||
|
NULL,
|
|||
|
DIGCF_PRESENT | DIGCF_DEVICEINTERFACE );
|
|||
|
|
|||
|
if ( !hdevInfo ) {
|
|||
|
dwError = GetLastError();
|
|||
|
goto FnExit;
|
|||
|
}
|
|||
|
|
|||
|
ZeroMemory( &devInterfaceData, sizeof( SP_DEVICE_INTERFACE_DATA) );
|
|||
|
|
|||
|
//
|
|||
|
// Iterate over all devices interfaces in the set
|
|||
|
//
|
|||
|
|
|||
|
for ( count = 0; ; count++ ) {
|
|||
|
|
|||
|
// must set size first
|
|||
|
devInterfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
|
|||
|
|
|||
|
//
|
|||
|
// Retrieve the device interface data for each device interface
|
|||
|
//
|
|||
|
|
|||
|
result = SetupDiEnumDeviceInterfaces( hdevInfo,
|
|||
|
NULL,
|
|||
|
&DiskClassGuid,
|
|||
|
count,
|
|||
|
&devInterfaceData );
|
|||
|
|
|||
|
if ( !result ) {
|
|||
|
|
|||
|
//
|
|||
|
// If we retrieved the last item, break
|
|||
|
//
|
|||
|
|
|||
|
dwError = GetLastError();
|
|||
|
|
|||
|
if ( ERROR_NO_MORE_ITEMS == dwError ) {
|
|||
|
dwError = ERROR_SUCCESS;
|
|||
|
break;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Some other error occurred. Stop processing.
|
|||
|
//
|
|||
|
|
|||
|
goto FnExit;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Get the required buffer-size for the device path. Note that
|
|||
|
// this call is expected to fail with insufficient buffer error.
|
|||
|
//
|
|||
|
|
|||
|
result = SetupDiGetDeviceInterfaceDetail( hdevInfo,
|
|||
|
&devInterfaceData,
|
|||
|
NULL,
|
|||
|
0,
|
|||
|
&sizeDiDetail,
|
|||
|
NULL
|
|||
|
);
|
|||
|
|
|||
|
if ( !result ) {
|
|||
|
|
|||
|
dwError = GetLastError();
|
|||
|
|
|||
|
//
|
|||
|
// If a value other than "insufficient buffer" is returned,
|
|||
|
// we have to skip this device.
|
|||
|
//
|
|||
|
|
|||
|
if ( ERROR_INSUFFICIENT_BUFFER != dwError ) {
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// The call should have failed since we're getting the
|
|||
|
// required buffer size. If it doesn't fail, something bad
|
|||
|
// happened.
|
|||
|
//
|
|||
|
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Allocate memory for the device interface detail.
|
|||
|
//
|
|||
|
|
|||
|
pDiDetail = LocalAlloc( LPTR, sizeDiDetail );
|
|||
|
|
|||
|
if ( !pDiDetail ) {
|
|||
|
dwError = GetLastError();
|
|||
|
goto FnExit;
|
|||
|
}
|
|||
|
|
|||
|
// must set the struct's size member
|
|||
|
|
|||
|
pDiDetail->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
|
|||
|
devInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
|
|||
|
|
|||
|
//
|
|||
|
// Finally, retrieve the device interface detail info.
|
|||
|
//
|
|||
|
|
|||
|
result = SetupDiGetDeviceInterfaceDetail( hdevInfo,
|
|||
|
&devInterfaceData,
|
|||
|
pDiDetail,
|
|||
|
sizeDiDetail,
|
|||
|
NULL,
|
|||
|
&devInfoData
|
|||
|
);
|
|||
|
|
|||
|
if ( !result ) {
|
|||
|
|
|||
|
dwError = GetLastError();
|
|||
|
|
|||
|
//
|
|||
|
// Shouldn't fail, if it does, try the next device.
|
|||
|
//
|
|||
|
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Open a handle to the device.
|
|||
|
//
|
|||
|
|
|||
|
hDevice = CreateFile( pDiDetail->DevicePath,
|
|||
|
GENERIC_READ | GENERIC_WRITE,
|
|||
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|||
|
NULL,
|
|||
|
OPEN_EXISTING,
|
|||
|
FILE_ATTRIBUTE_NORMAL,
|
|||
|
NULL );
|
|||
|
|
|||
|
LocalFree( pDiDetail );
|
|||
|
pDiDetail = NULL;
|
|||
|
|
|||
|
if ( INVALID_HANDLE_VALUE == hDevice ) {
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Call the specified callback routine. An error returned stops the
|
|||
|
// disk enumeration.
|
|||
|
//
|
|||
|
|
|||
|
dwError = (*DiskEnumCallback)( hDevice, count, Param1 );
|
|||
|
|
|||
|
CloseHandle( hDevice );
|
|||
|
|
|||
|
if ( ERROR_SUCCESS != dwError ) {
|
|||
|
goto FnExit;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
FnExit:
|
|||
|
|
|||
|
if ( pDiDetail ) {
|
|||
|
LocalFree( pDiDetail );
|
|||
|
}
|
|||
|
|
|||
|
return dwError;
|
|||
|
|
|||
|
} // EnumerateDisks
|
|||
|
|
|||
|
|
|||
|
|
|||
|
DWORD
|
|||
|
GetSerialNumber(
|
|||
|
IN DWORD Signature,
|
|||
|
OUT LPWSTR *SerialNumber
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Find the disk serial number for a given signature.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Signature - the signature to find.
|
|||
|
|
|||
|
SerialNumber - pointer to allocated buffer holding the returned serial
|
|||
|
number. The caller must free this buffer.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
ERROR_SUCCESS if successful.
|
|||
|
|
|||
|
A Win32 error on failure.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
DWORD dwError;
|
|||
|
|
|||
|
SERIAL_INFO serialInfo;
|
|||
|
|
|||
|
if ( !Signature || !SerialNumber ) {
|
|||
|
dwError = ERROR_INVALID_PARAMETER;
|
|||
|
goto FnExit;
|
|||
|
}
|
|||
|
|
|||
|
*SerialNumber = NULL;
|
|||
|
|
|||
|
ZeroMemory( &serialInfo, sizeof(serialInfo) );
|
|||
|
|
|||
|
serialInfo.Signature = Signature;
|
|||
|
serialInfo.Error = ERROR_SUCCESS;
|
|||
|
|
|||
|
dwError = EnumerateDisks( GetSerialNumberCallback, &serialInfo );
|
|||
|
|
|||
|
//
|
|||
|
// The callback routine will use ERROR_POPUP_ALREADY_ACTIVE to stop
|
|||
|
// the disk enumeration. Reset the value to the value returned
|
|||
|
// in the SERIAL_INFO structure.
|
|||
|
//
|
|||
|
|
|||
|
if ( ERROR_POPUP_ALREADY_ACTIVE == dwError ) {
|
|||
|
dwError = serialInfo.Error;
|
|||
|
}
|
|||
|
|
|||
|
// This will either be NULL or an allocated buffer. Caller is responsible
|
|||
|
// to free.
|
|||
|
|
|||
|
*SerialNumber = serialInfo.SerialNumber;
|
|||
|
|
|||
|
FnExit:
|
|||
|
|
|||
|
return dwError;
|
|||
|
|
|||
|
} // GetSerialNumber
|
|||
|
|
|||
|
|
|||
|
DWORD
|
|||
|
GetSerialNumberCallback(
|
|||
|
HANDLE DevHandle,
|
|||
|
DWORD Index,
|
|||
|
PVOID Param1
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Find the disk serial number for a given signature.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DevHandle - open handle to a physical disk. Do not close
|
|||
|
the handle on exit.
|
|||
|
|
|||
|
Index - current disk count. Not used.
|
|||
|
|
|||
|
Param1 - pointer to PSERIAL_INFO structure.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
ERROR_SUCCESS to continue disk enumeration.
|
|||
|
|
|||
|
ERROR_POPUP_ALREADY_ACTIVE to stop disk enumeration. This
|
|||
|
value will be changed to ERROR_SUCCESS by GetScsiAddress.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
PSERIAL_INFO serialInfo = Param1;
|
|||
|
|
|||
|
PDRIVE_LAYOUT_INFORMATION driveLayout = NULL;
|
|||
|
|
|||
|
DWORD dwError = ERROR_SUCCESS;
|
|||
|
|
|||
|
BOOL success;
|
|||
|
|
|||
|
// Always return success to keep enumerating disks. Any
|
|||
|
// error value will stop the disk enumeration.
|
|||
|
|
|||
|
STORAGE_PROPERTY_QUERY propQuery;
|
|||
|
|
|||
|
success = ClRtlGetDriveLayoutTable( DevHandle,
|
|||
|
&driveLayout,
|
|||
|
NULL );
|
|||
|
|
|||
|
if ( !success || !driveLayout ||
|
|||
|
( driveLayout->Signature != serialInfo->Signature ) ) {
|
|||
|
goto FnExit;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// At this point, we have a signature match. Now this function
|
|||
|
// must return this error value to stop the disk enumeration. The
|
|||
|
// error value for the serial number retrieval will be returned in
|
|||
|
// the SERIAL_INFO structure.
|
|||
|
//
|
|||
|
|
|||
|
dwError = ERROR_POPUP_ALREADY_ACTIVE;
|
|||
|
|
|||
|
serialInfo->Error = RetrieveSerialNumber( DevHandle, &serialInfo->SerialNumber );
|
|||
|
|
|||
|
FnExit:
|
|||
|
|
|||
|
if ( driveLayout ) {
|
|||
|
LocalFree( driveLayout );
|
|||
|
}
|
|||
|
|
|||
|
if ( serialInfo->Error != ERROR_SUCCESS && serialInfo->SerialNumber ) {
|
|||
|
LocalFree( serialInfo->SerialNumber );
|
|||
|
}
|
|||
|
|
|||
|
return dwError;
|
|||
|
|
|||
|
} // GetSerialNumberCallback
|
|||
|
|
|||
|
|
|||
|
DWORD
|
|||
|
GetSignatureFromSerialNumber(
|
|||
|
IN LPWSTR SerialNumber,
|
|||
|
OUT LPDWORD Signature
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Find the disk signature for the given serial number.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
SerialNumber - pointer to allocated buffer holding the serial number.
|
|||
|
|
|||
|
Signature - pointer to location to hold the signature.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
ERROR_SUCCESS if successful.
|
|||
|
|
|||
|
A Win32 error on failure.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
DWORD dwError;
|
|||
|
|
|||
|
SERIAL_INFO serialInfo;
|
|||
|
|
|||
|
if ( !Signature || !SerialNumber ) {
|
|||
|
dwError = ERROR_INVALID_PARAMETER;
|
|||
|
goto FnExit;
|
|||
|
}
|
|||
|
|
|||
|
*Signature = 0;
|
|||
|
|
|||
|
ZeroMemory( &serialInfo, sizeof(serialInfo) );
|
|||
|
|
|||
|
serialInfo.SerialNumber = SerialNumber;
|
|||
|
serialInfo.Error = ERROR_SUCCESS;
|
|||
|
|
|||
|
dwError = EnumerateDisks( GetSigFromSerNumCallback, &serialInfo );
|
|||
|
|
|||
|
//
|
|||
|
// The callback routine will use ERROR_POPUP_ALREADY_ACTIVE to stop
|
|||
|
// the disk enumeration. Reset the value to the value returned
|
|||
|
// in the SERIAL_INFO structure.
|
|||
|
//
|
|||
|
|
|||
|
if ( ERROR_POPUP_ALREADY_ACTIVE == dwError ) {
|
|||
|
dwError = serialInfo.Error;
|
|||
|
}
|
|||
|
|
|||
|
// This signature will either be zero or valid.
|
|||
|
|
|||
|
*Signature = serialInfo.Signature;
|
|||
|
|
|||
|
FnExit:
|
|||
|
|
|||
|
return dwError;
|
|||
|
|
|||
|
} // GetSignatureFromSerialNumber
|
|||
|
|
|||
|
|
|||
|
DWORD
|
|||
|
GetSigFromSerNumCallback(
|
|||
|
HANDLE DevHandle,
|
|||
|
DWORD Index,
|
|||
|
PVOID Param1
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Find the disk signature for a given serial number.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DevHandle - open handle to a physical disk. Do not close
|
|||
|
the handle on exit.
|
|||
|
|
|||
|
Index - current disk count. Not used.
|
|||
|
|
|||
|
Param1 - pointer to PSERIAL_INFO structure.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
ERROR_SUCCESS to continue disk enumeration.
|
|||
|
|
|||
|
ERROR_POPUP_ALREADY_ACTIVE to stop disk enumeration. This
|
|||
|
value will be changed to ERROR_SUCCESS by GetScsiAddress.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
PSERIAL_INFO serialInfo = Param1;
|
|||
|
LPWSTR serialNum = NULL;
|
|||
|
PDRIVE_LAYOUT_INFORMATION driveLayout = NULL;
|
|||
|
|
|||
|
DWORD dwError = ERROR_SUCCESS;
|
|||
|
DWORD oldLen;
|
|||
|
DWORD newLen;
|
|||
|
|
|||
|
BOOL success;
|
|||
|
|
|||
|
// Always return success to keep enumerating disks. Any
|
|||
|
// error value will stop the disk enumeration.
|
|||
|
|
|||
|
dwError = RetrieveSerialNumber( DevHandle, &serialNum );
|
|||
|
|
|||
|
if ( NO_ERROR != dwError || !serialNum ) {
|
|||
|
dwError = ERROR_SUCCESS;
|
|||
|
goto FnExit;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// We have a serial number, now try to match it.
|
|||
|
//
|
|||
|
|
|||
|
newLen = wcslen( serialNum );
|
|||
|
oldLen = wcslen( serialInfo->SerialNumber );
|
|||
|
|
|||
|
if ( newLen != oldLen ||
|
|||
|
0 != wcsncmp( serialNum, serialInfo->SerialNumber, newLen ) ) {
|
|||
|
goto FnExit;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// At this point, we have a serial number match. Now this function
|
|||
|
// must return this error value to stop the disk enumeration. The
|
|||
|
// error value for the signature retrieval will be returned in
|
|||
|
// the SERIAL_INFO structure.
|
|||
|
//
|
|||
|
|
|||
|
dwError = ERROR_POPUP_ALREADY_ACTIVE;
|
|||
|
|
|||
|
success = ClRtlGetDriveLayoutTable( DevHandle,
|
|||
|
&driveLayout,
|
|||
|
NULL );
|
|||
|
|
|||
|
if ( !success || !driveLayout ) {
|
|||
|
serialInfo->Error = ERROR_INVALID_DATA;
|
|||
|
goto FnExit;
|
|||
|
}
|
|||
|
|
|||
|
serialInfo->Signature = driveLayout->Signature;
|
|||
|
serialInfo->Error = NO_ERROR;
|
|||
|
|
|||
|
FnExit:
|
|||
|
|
|||
|
if ( driveLayout ) {
|
|||
|
LocalFree( driveLayout );
|
|||
|
}
|
|||
|
|
|||
|
if ( serialNum ) {
|
|||
|
LocalFree( serialNum );
|
|||
|
}
|
|||
|
|
|||
|
return dwError;
|
|||
|
|
|||
|
} // GetSigFromSerNumCallback
|
|||
|
|
|||
|
|
|||
|
DWORD
|
|||
|
RetrieveSerialNumber(
|
|||
|
HANDLE DevHandle,
|
|||
|
LPWSTR *SerialNumber
|
|||
|
)
|
|||
|
{
|
|||
|
PSTORAGE_DEVICE_DESCRIPTOR descriptor = NULL;
|
|||
|
|
|||
|
PWCHAR wSerNum = NULL;
|
|||
|
PCHAR sigString;
|
|||
|
|
|||
|
DWORD dwError = ERROR_SUCCESS;
|
|||
|
DWORD bytesReturned;
|
|||
|
DWORD descriptorSize;
|
|||
|
|
|||
|
size_t count1;
|
|||
|
size_t count2;
|
|||
|
|
|||
|
BOOL success;
|
|||
|
|
|||
|
STORAGE_PROPERTY_QUERY propQuery;
|
|||
|
|
|||
|
if ( !SerialNumber ) {
|
|||
|
dwError = ERROR_INVALID_PARAMETER;
|
|||
|
goto FnExit;
|
|||
|
}
|
|||
|
|
|||
|
*SerialNumber = NULL;
|
|||
|
|
|||
|
descriptorSize = sizeof( STORAGE_DEVICE_DESCRIPTOR) + 4096;
|
|||
|
|
|||
|
descriptor = LocalAlloc( LPTR, descriptorSize );
|
|||
|
|
|||
|
if ( !descriptor ) {
|
|||
|
dwError = GetLastError();
|
|||
|
goto FnExit;
|
|||
|
}
|
|||
|
|
|||
|
ZeroMemory( &propQuery, sizeof( propQuery ) );
|
|||
|
|
|||
|
propQuery.PropertyId = StorageDeviceProperty;
|
|||
|
propQuery.QueryType = PropertyStandardQuery;
|
|||
|
|
|||
|
success = DeviceIoControl( DevHandle,
|
|||
|
IOCTL_STORAGE_QUERY_PROPERTY,
|
|||
|
&propQuery,
|
|||
|
sizeof(propQuery),
|
|||
|
descriptor,
|
|||
|
descriptorSize,
|
|||
|
&bytesReturned,
|
|||
|
NULL );
|
|||
|
|
|||
|
if ( !success ) {
|
|||
|
dwError = GetLastError();
|
|||
|
goto FnExit;
|
|||
|
}
|
|||
|
|
|||
|
if ( !bytesReturned ) {
|
|||
|
dwError = ERROR_INVALID_DATA;
|
|||
|
goto FnExit;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Make sure the offset is reasonable.
|
|||
|
// IA64 sometimes returns -1 for SerialNumberOffset.
|
|||
|
//
|
|||
|
|
|||
|
if ( 0 == descriptor->SerialNumberOffset ||
|
|||
|
descriptor->SerialNumberOffset > descriptor->Size ) {
|
|||
|
dwError = ERROR_INVALID_DATA;
|
|||
|
goto FnExit;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Serial number string is a zero terminated ASCII string.
|
|||
|
//
|
|||
|
// The header ntddstor.h says the for devices with no serial number,
|
|||
|
// the offset will be zero. This doesn't seem to be true.
|
|||
|
//
|
|||
|
// For devices with no serial number, it looks like a string with a single
|
|||
|
// null character '\0' is returned.
|
|||
|
//
|
|||
|
|
|||
|
sigString = (PCHAR)descriptor + (DWORD)descriptor->SerialNumberOffset;
|
|||
|
|
|||
|
if ( strlen(sigString) == 0) {
|
|||
|
dwError = ERROR_INVALID_DATA;
|
|||
|
goto FnExit;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Convert ASCII string to WCHAR.
|
|||
|
//
|
|||
|
|
|||
|
// Figure out how big the WCHAR buffer should be. Allocate the WCHAR
|
|||
|
// buffer and copy the string into it.
|
|||
|
|
|||
|
count1 = mbstowcs( NULL, sigString, strlen(sigString) );
|
|||
|
|
|||
|
if ( -1 == count1 || 0 == count1 ) {
|
|||
|
dwError = ERROR_INVALID_DATA;
|
|||
|
goto FnExit;
|
|||
|
}
|
|||
|
|
|||
|
wSerNum = LocalAlloc( LPTR, ( count1 * sizeof(WCHAR) + sizeof(UNICODE_NULL) ) );
|
|||
|
|
|||
|
if ( !wSerNum ) {
|
|||
|
dwError = GetLastError();
|
|||
|
goto FnExit;
|
|||
|
}
|
|||
|
|
|||
|
count2 = mbstowcs( wSerNum, sigString, strlen(sigString) );
|
|||
|
|
|||
|
if ( count1 != count2 ) {
|
|||
|
dwError = ERROR_INVALID_DATA;
|
|||
|
goto FnExit;
|
|||
|
}
|
|||
|
|
|||
|
*SerialNumber = wSerNum;
|
|||
|
dwError = NO_ERROR;
|
|||
|
|
|||
|
FnExit:
|
|||
|
|
|||
|
if ( descriptor ) {
|
|||
|
LocalFree( descriptor );
|
|||
|
}
|
|||
|
|
|||
|
return dwError;
|
|||
|
|
|||
|
} // RetrieveSerialNumber
|
|||
|
|