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.
17900 lines
544 KiB
17900 lines
544 KiB
/*++
|
|
|
|
Copyright (c) 1996-1998 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
clusdisk.c
|
|
|
|
Abstract:
|
|
|
|
This driver controls access to disks in an NT cluster environment.
|
|
Initially this driver will support SCSI, but other controller types
|
|
should be supported in the future.
|
|
|
|
Authors:
|
|
|
|
Rod Gamache 13-Feb-1996
|
|
|
|
Environment:
|
|
|
|
kernel mode only
|
|
|
|
Notes:
|
|
|
|
Revision History:
|
|
|
|
|
|
--*/
|
|
|
|
#define _NTDDK_
|
|
|
|
#include "initguid.h"
|
|
#include "clusdskp.h"
|
|
#include "ntddk.h"
|
|
#include "diskarbp.h"
|
|
#include "ntddft.h"
|
|
#include "clusdisk.h"
|
|
#include "scsi.h"
|
|
#include "ntddcnet.h"
|
|
#include "mountdev.h"
|
|
#include "ntddvol.h" // IOCTL_VOLUME_ONLINE
|
|
#include "wdmguid.h"
|
|
#include "clusverp.h"
|
|
#include "clusvmsg.h"
|
|
#include <ntddsnap.h> // IOCTL_VOLSNAP_QUERY_OFFLINE
|
|
#include <windef.h>
|
|
#include <partmgrp.h> // PartMgr IOCTLs
|
|
#include <strsafe.h> // Should be included last.
|
|
|
|
#if !defined(WMI_TRACING)
|
|
|
|
#define CDLOG0(Dummy)
|
|
#define CDLOG(Dummy1,Dummy2)
|
|
#define CDLOGFLG(Dummy0,Dummy1,Dummy2)
|
|
#define LOGENABLED(Dummy) FALSE
|
|
|
|
#else
|
|
|
|
#include "clusdisk.tmh"
|
|
|
|
#endif // !defined(WMI_TRACING)
|
|
|
|
|
|
extern POBJECT_TYPE *IoFileObjectType;
|
|
|
|
//
|
|
// format string for old style partition names. 10 extra chars are added
|
|
// for enough space for the disk and partition numbers
|
|
//
|
|
|
|
#define DEVICE_PARTITION_NAME L"\\Device\\Harddisk%d\\Partition%d"
|
|
#define MAX_PARTITION_NAME_LENGTH (( sizeof(DEVICE_PARTITION_NAME) / sizeof(WCHAR)) + 10 )
|
|
|
|
//
|
|
// format string for a clusdisk non-zero partition device
|
|
//
|
|
#define CLUSDISK_DEVICE_NAME L"\\Device\\ClusDisk%uPart%u"
|
|
#define MAX_CLUSDISK_DEVICE_NAME_LENGTH (( sizeof(CLUSDISK_DEVICE_NAME) / sizeof(WCHAR)) + 10 )
|
|
|
|
#define RESET_SLEEP 1 // Sleep for 1 second after bus resets.
|
|
|
|
// max # of partition entries we can handle that are returned
|
|
// by IOCTL_DISK_GET_DRIVE_LAYOUT
|
|
|
|
#define MAX_PARTITIONS 128
|
|
|
|
#define SKIP_COUNT_MAX 50
|
|
|
|
#ifndef max
|
|
#define max( a, b ) ((a) >= (b) ? (a) : (b))
|
|
#endif
|
|
|
|
#define CLUSDISK_ALLOC_TAG 'kdSC'
|
|
|
|
#ifndef ASSERT_RESERVES_STARTED
|
|
#define ASSERT_RESERVES_STARTED( _de ) \
|
|
ASSERT( _de->PerformReserves == TRUE && _de->ReserveTimer != 0 );
|
|
#endif
|
|
|
|
#ifndef ASSERT_RESERVES_STOPPED
|
|
#define ASSERT_RESERVES_STOPPED( _de ) \
|
|
ASSERT( _de->PerformReserves == FALSE || _de->ReserveTimer == 0 );
|
|
#endif
|
|
|
|
#define OFFLINE_DISK_PDO( _physDisk ) \
|
|
_physDisk->DiskState = DiskOffline; \
|
|
SendOfflineDirect( _physDisk );
|
|
|
|
#define OFFLINE_DISK( _physDisk ) \
|
|
_physDisk->DiskState = DiskOffline; \
|
|
if ( !HaltOfflineBusy ) { \
|
|
SetVolumeState( _physDisk, DiskOffline ); \
|
|
} else { \
|
|
ClusDiskPrint(( 1, "[ClusDisk] HaltOfflineBusy set, skipping DiskOffline request \n" )); \
|
|
}
|
|
|
|
#define ONLINE_DISK( _physDisk ) \
|
|
_physDisk->DiskState = DiskOnline; \
|
|
SetVolumeState( _physDisk, DiskOnline );
|
|
|
|
#define DEREFERENCE_OBJECT( _obj ) \
|
|
if ( _obj ) { \
|
|
ObDereferenceObject( _obj ); \
|
|
_obj = NULL; \
|
|
}
|
|
|
|
#define FREE_DEVICE_NAME_BUFFER( _devName ) \
|
|
if ( _devName.Buffer ) { \
|
|
ExFreePool( _devName.Buffer ); \
|
|
_devName.Buffer = NULL; \
|
|
}
|
|
|
|
#define FREE_AND_NULL_PTR( _poolPtr ) \
|
|
if ( _poolPtr ) { \
|
|
ExFreePool( _poolPtr ); \
|
|
_poolPtr = NULL; \
|
|
}
|
|
|
|
|
|
#define ACCESS_FROM_CTL_CODE(ctrlCode) (((ULONG)(ctrlCode & 0xc000)) >> 14)
|
|
|
|
#define MAX_WAIT_SECONDS_ALLOWED 3600 // Max wait is one hour
|
|
|
|
//
|
|
// Global Data
|
|
//
|
|
|
|
UNICODE_STRING ClusDiskRegistryPath;
|
|
|
|
#if DBG
|
|
ULONG ClusDiskPrintLevel = 0;
|
|
#endif
|
|
|
|
#define CLUSDISK_DEBUG 1
|
|
#if CLUSDISK_DEBUG
|
|
ULONG ClusDiskGood = TRUE;
|
|
#endif
|
|
|
|
//
|
|
// Spinlock for protecting global data.
|
|
//
|
|
KSPIN_LOCK ClusDiskSpinLock;
|
|
|
|
//
|
|
// Resource to protect the list of the device objects
|
|
// associated with the DriverObject
|
|
//
|
|
// We also use this resource to synchronize
|
|
// HoldIo and users of the OpenFileHandles function,
|
|
//
|
|
// Lock order is
|
|
// ClusDiskDeviceListLock
|
|
// CancelSpinLock
|
|
// ClusDiskSpinLock
|
|
//
|
|
|
|
ERESOURCE ClusDiskDeviceListLock;
|
|
|
|
//
|
|
// System disk signature and (SCSI?) port number
|
|
//
|
|
ULONG SystemDiskSignature = 0;
|
|
UCHAR SystemDiskPort = 0xff; // Hopefully -1 for both fields is unused
|
|
UCHAR SystemDiskPath = 0xff;
|
|
|
|
//
|
|
// The Root Device Object (clusdisk0)
|
|
//
|
|
PDEVICE_OBJECT RootDeviceObject = NULL;
|
|
|
|
//
|
|
// List of devices (signatures) that clusdisk should control.
|
|
//
|
|
PDEVICE_LIST_ENTRY ClusDiskDeviceList = NULL;
|
|
|
|
//
|
|
// Clusdisk is started at boot time vs run time (ie loaded).
|
|
//
|
|
BOOLEAN ClusDiskBootTime = TRUE;
|
|
|
|
//
|
|
// Clusdisk should rescan and previous disk count
|
|
//
|
|
BOOLEAN ClusDiskRescan = FALSE;
|
|
BOOLEAN ClusDiskRescanBusy = FALSE;
|
|
ULONG ClusDiskRescanRetry = 0;
|
|
PVOID ClusDiskNextDisk = 0;
|
|
WORK_QUEUE_ITEM ClusDiskRescanWorkItem;
|
|
#define MAX_RESCAN_RETRIES 30
|
|
|
|
PKPROCESS ClusDiskSystemProcess = NULL;
|
|
|
|
//
|
|
// Handle to ClusNet device driver.
|
|
//
|
|
HANDLE ClusNetHandle = NULL;
|
|
|
|
//
|
|
// Count of references to ClusNet.
|
|
//
|
|
ULONG ClusNetRefCount = 0;
|
|
|
|
LPCGUID ClusDiskOfflineOnlineGuid = (LPCGUID)&GUID_CLUSTER_CONTROL;
|
|
|
|
//
|
|
// Work queue item context for halt processing.
|
|
//
|
|
WORK_QUEUE_ITEM HaltWorkItem = {0};
|
|
BOOLEAN HaltBusy = FALSE; // TRUE if halt work item is busy
|
|
BOOLEAN HaltOfflineBusy = FALSE; // TRUE if offline IOCTL to volume PDOs in progress
|
|
|
|
//
|
|
// List to hold items before sent to worker routine
|
|
//
|
|
LONG ReplaceRoutineCount = 0;
|
|
LIST_ENTRY ReplaceRoutineListHead;
|
|
KSPIN_LOCK ReplaceRoutineSpinLock;
|
|
|
|
#define MAX_REPLACE_HANDLE_ROUTINES 2
|
|
|
|
#if CLUSTER_FREE_ASSERTS
|
|
|
|
LONG ClusDiskDebugVolumeNotificationQueued = 0;
|
|
LONG ClusDiskDebugVolumeNotificationEnded = 0;
|
|
LONG ClusDiskDebugVolumeNotificationSkipped = 0;
|
|
LONG ClusDiskDebugDiskNotificationQueued = 0;
|
|
LONG ClusDiskDebugDiskNotificationEnded = 0;
|
|
LONG ClusDiskDebugDiskNotificationSkipped = 0;
|
|
|
|
#endif
|
|
|
|
#if CLUSTER_FREE_ASSERTS
|
|
|
|
// 10,000,000 100 nanosecond units = 1 second
|
|
|
|
#define DBG_STALL_THREAD( _seconds ) \
|
|
{ \
|
|
LARGE_INTEGER _dbgWaitTime; \
|
|
_dbgWaitTime.QuadPart = (ULONGLONG)(-_seconds * 10000 * 1000); \
|
|
KeDelayExecutionThread( KernelMode, \
|
|
FALSE, \
|
|
&_dbgWaitTime ); \
|
|
}
|
|
|
|
#else
|
|
|
|
#define DBG_STALL_THREAD( _seconds )
|
|
|
|
#endif
|
|
|
|
//
|
|
// RemoveLock tracing
|
|
//
|
|
|
|
#if DBG
|
|
|
|
ULONG TrackRemoveLocks = 0;
|
|
ULONG TrackRemoveLocksEnableChecks = 1;
|
|
PIO_REMOVE_LOCK TrackRemoveLockSpecific = 0;
|
|
|
|
#endif
|
|
|
|
extern PARBITRATION_ID gArbitrationBuffer;
|
|
|
|
//
|
|
// Forward routines
|
|
//
|
|
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
|
|
#pragma alloc_text(INIT, DriverEntry)
|
|
#pragma alloc_text(INIT, ClusDiskInitialize)
|
|
#pragma alloc_text(INIT, GetSystemRootPort)
|
|
#pragma alloc_text(INIT, GetBootTimeSystemRoot)
|
|
#pragma alloc_text(INIT, GetRunTimeSystemRoot)
|
|
#pragma alloc_text(INIT, RegistryQueryValue)
|
|
//#pragma alloc_text(INIT, ResetScsiBusses)
|
|
|
|
// 2000/02/05: stevedz - Pageable code cannot acquire spinlocks (or call routines that do).
|
|
// ClusDiskScsiInitialize calls ClusDiskDeleteDevice which acquires a spinlock.
|
|
// #pragma alloc_text(PAGE, ClusDiskScsiInitialize)
|
|
#pragma alloc_text(PAGE, ClusDiskUnload)
|
|
|
|
#endif // ALLOC_PRAGMA
|
|
|
|
|
|
|
|
//
|
|
// INIT routines
|
|
//
|
|
|
|
|
|
NTSTATUS
|
|
RegistryQueryValue(
|
|
PVOID hKey,
|
|
LPWSTR pValueName,
|
|
PULONG pulType,
|
|
PVOID pData,
|
|
PULONG pulDataSize
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Queries a value from the registry
|
|
|
|
Arguments:
|
|
hKey - Key with value to query
|
|
pValueName - Name of value to query
|
|
pulType - Returned type of data
|
|
pData - Pointer to the data buffer to store result
|
|
pulDataSize - On entry, number of bytes in data buffer.
|
|
- On exit, number of bytes placed into buffer
|
|
|
|
Return Value:
|
|
NTSTATUS
|
|
- STATUS_BUFFER_OVERFLOW if buffer can't be allocated
|
|
|
|
--*/
|
|
|
|
{
|
|
KEY_VALUE_PARTIAL_INFORMATION *pValInfo;
|
|
UNICODE_STRING valName;
|
|
NTSTATUS ntStatus;
|
|
ULONG ulSize;
|
|
|
|
// Size of query buffer
|
|
ulSize = sizeof(KEY_VALUE_PARTIAL_INFORMATION) + *pulDataSize;
|
|
|
|
pValInfo = ExAllocatePool(NonPagedPool, ulSize );
|
|
|
|
if (pValInfo == NULL)
|
|
return(STATUS_BUFFER_OVERFLOW);
|
|
|
|
RtlInitUnicodeString(&valName, pValueName);
|
|
|
|
pValInfo->DataLength = *pulDataSize;
|
|
|
|
ntStatus = ZwQueryValueKey(hKey,
|
|
&valName,
|
|
KeyValuePartialInformation,
|
|
pValInfo,
|
|
ulSize,
|
|
&ulSize);
|
|
|
|
if ( NT_SUCCESS(ntStatus) &&
|
|
*pulDataSize >= pValInfo->DataLength ) {
|
|
// Copy the data queried into buffer
|
|
RtlCopyMemory(pData, pValInfo->Data, pValInfo->DataLength);
|
|
|
|
*pulType = pValInfo->Type;
|
|
*pulDataSize = pValInfo->DataLength;
|
|
} else {
|
|
#if 0
|
|
ClusDiskPrint((
|
|
1,
|
|
"[ClusDisk] Failed to read key %ws\n",
|
|
pValueName ));
|
|
#endif
|
|
}
|
|
|
|
ExFreePool(pValInfo);
|
|
|
|
return ntStatus;
|
|
|
|
} // RegistryQueryValue
|
|
|
|
|
|
|
|
NTSTATUS
|
|
GetBootTimeSystemRoot(
|
|
IN OUT PWCHAR Path
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Find "Partition" string in the partition name, then truncate the
|
|
string just after the "Partition" string.
|
|
|
|
Arguments:
|
|
|
|
Path - the path for the system disk.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
|
|
{
|
|
PWCHAR ptrPartition;
|
|
|
|
//
|
|
// At boot time, systemroot is init'ed using the Arcname of the
|
|
// system device. In this form, "partition" is in lower case.
|
|
//
|
|
ptrPartition = wcsstr( Path, L"partition" );
|
|
if ( ptrPartition == NULL ) {
|
|
return(STATUS_INVALID_PARAMETER);
|
|
}
|
|
|
|
ptrPartition = wcsstr( ptrPartition, L")" );
|
|
if ( ptrPartition == NULL ) {
|
|
return(STATUS_INVALID_PARAMETER);
|
|
}
|
|
|
|
ptrPartition++;
|
|
*ptrPartition = UNICODE_NULL;
|
|
|
|
return(STATUS_SUCCESS);
|
|
|
|
} // GetBootTimeSystemRoot
|
|
|
|
|
|
NTSTATUS
|
|
GetRunTimeSystemRoot(
|
|
IN OUT PWCHAR Path
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Find "Partition" string in the partition name, then truncate the
|
|
string just after the "Partition" string.
|
|
|
|
Arguments:
|
|
|
|
Path - the path for the system disk.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
|
|
{
|
|
PWCHAR ptrPartition;
|
|
|
|
//
|
|
// Once the system has booted, systemroot is changed to point to
|
|
// a string of the form \Device\HarddiskX\PartitionY\<win dir>. Note
|
|
// that "partition" is now capitalized.
|
|
//
|
|
ptrPartition = wcsstr( Path, L"Partition" );
|
|
if ( ptrPartition == NULL ) {
|
|
return(STATUS_INVALID_PARAMETER);
|
|
}
|
|
|
|
ptrPartition = wcsstr( ptrPartition, L"\\" );
|
|
if ( ptrPartition == NULL ) {
|
|
return(STATUS_INVALID_PARAMETER);
|
|
}
|
|
|
|
--ptrPartition;
|
|
*ptrPartition++ = L'0';
|
|
*ptrPartition = UNICODE_NULL;
|
|
|
|
return(STATUS_SUCCESS);
|
|
|
|
} // GetRunTimeSystemRoot
|
|
|
|
|
|
|
|
NTSTATUS
|
|
GetSystemRootPort(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Get the port number and signature for the system disk.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
|
|
{
|
|
WCHAR path[MAX_PATH] = L"SystemRoot";
|
|
WCHAR clussvcKey[] = L"\\REGISTRY\\MACHINE\\SYSTEM\\CURRENTCONTROLSET\\SERVICES\\ClusSvc\\Parameters";
|
|
UNICODE_STRING ntUnicodeString;
|
|
NTSTATUS status;
|
|
HANDLE ntFileHandle;
|
|
IO_STATUS_BLOCK ioStatus;
|
|
OBJECT_ATTRIBUTES objectAttributes;
|
|
PDRIVE_LAYOUT_INFORMATION_EX driveLayout;
|
|
ULONG driveLayoutSize;
|
|
ULONG singleBus;
|
|
SCSI_ADDRESS scsiAddress;
|
|
HANDLE eventHandle;
|
|
|
|
//
|
|
// Find the bus on which the system disk is loaded.
|
|
//
|
|
|
|
GetSymbolicLink( L"\\", path );
|
|
if ( wcslen(path) == 0 ) {
|
|
ClusDiskPrint((1, "[ClusDisk] GetSystemRootPort: couldn't find symbolic link for SystemRoot.\n"));
|
|
|
|
return(STATUS_FILE_INVALID);
|
|
}
|
|
|
|
status = GetBootTimeSystemRoot( path );
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
status = GetRunTimeSystemRoot( path );
|
|
ClusDiskBootTime = FALSE;
|
|
} // else - default is TRUE
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
ClusDiskPrint((1,
|
|
"[ClusDisk] GetSystemRootPort: unable to get system disk name ->%ws<-\n",
|
|
path));
|
|
//continue
|
|
//return(status);
|
|
}
|
|
|
|
//
|
|
// Open the device.
|
|
//
|
|
RtlInitUnicodeString( &ntUnicodeString, path );
|
|
|
|
InitializeObjectAttributes( &objectAttributes,
|
|
&ntUnicodeString,
|
|
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
|
|
NULL,
|
|
NULL );
|
|
|
|
status = ZwCreateFile( &ntFileHandle,
|
|
FILE_READ_DATA,
|
|
&objectAttributes,
|
|
&ioStatus,
|
|
NULL,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
FILE_OPEN,
|
|
FILE_SYNCHRONOUS_IO_NONALERT,
|
|
NULL,
|
|
0 );
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
ClusDiskPrint((
|
|
1,
|
|
"[ClusDisk] Failed to open device for [%ws]. Error %08X.\n",
|
|
path,
|
|
status));
|
|
|
|
return(status);
|
|
}
|
|
|
|
//
|
|
// Allocate a drive layout buffer.
|
|
//
|
|
driveLayoutSize = sizeof(DRIVE_LAYOUT_INFORMATION_EX) +
|
|
(MAX_PARTITIONS * sizeof(PARTITION_INFORMATION_EX));
|
|
driveLayout = ExAllocatePool( NonPagedPoolCacheAligned,
|
|
driveLayoutSize );
|
|
if ( driveLayout == NULL ) {
|
|
ClusDiskPrint((
|
|
1,
|
|
"[ClusDisk] Failed to allocate root drive layout structure.\n"
|
|
));
|
|
ZwClose( ntFileHandle );
|
|
return(STATUS_INSUFFICIENT_RESOURCES);
|
|
}
|
|
|
|
//
|
|
// Create event for notification.
|
|
//
|
|
status = ZwCreateEvent( &eventHandle,
|
|
EVENT_ALL_ACCESS,
|
|
NULL,
|
|
SynchronizationEvent,
|
|
FALSE );
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
ClusDiskPrint((
|
|
1,
|
|
"[ClusDisk] Failed to create event. %08X\n",
|
|
status));
|
|
|
|
ExFreePool( driveLayout );
|
|
ZwClose( ntFileHandle );
|
|
return(status);
|
|
}
|
|
|
|
//
|
|
// Get the port number for the SystemRoot disk device.
|
|
//
|
|
status = ZwDeviceIoControlFile( ntFileHandle,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&ioStatus,
|
|
IOCTL_SCSI_GET_ADDRESS,
|
|
NULL,
|
|
0,
|
|
&scsiAddress,
|
|
sizeof(SCSI_ADDRESS) );
|
|
|
|
if ( status == STATUS_PENDING ) {
|
|
status = ZwWaitForSingleObject(eventHandle,
|
|
FALSE,
|
|
NULL);
|
|
ASSERT( NT_SUCCESS(status) );
|
|
status = ioStatus.Status;
|
|
}
|
|
|
|
if ( NT_SUCCESS(status) ) {
|
|
|
|
status = ZwDeviceIoControlFile( ntFileHandle,
|
|
eventHandle,
|
|
NULL,
|
|
NULL,
|
|
&ioStatus,
|
|
IOCTL_DISK_GET_DRIVE_LAYOUT_EX,
|
|
NULL,
|
|
0,
|
|
driveLayout,
|
|
driveLayoutSize );
|
|
|
|
if ( status == STATUS_PENDING ) {
|
|
status = ZwWaitForSingleObject(eventHandle,
|
|
FALSE,
|
|
NULL);
|
|
ASSERT( NT_SUCCESS(status) );
|
|
status = ioStatus.Status;
|
|
}
|
|
}
|
|
|
|
ZwClose( ntFileHandle );
|
|
ZwClose( eventHandle );
|
|
|
|
if ( NT_SUCCESS(status) ) {
|
|
|
|
if ( PARTITION_STYLE_MBR == driveLayout->PartitionStyle ) {
|
|
SystemDiskSignature = driveLayout->Mbr.Signature;
|
|
}
|
|
|
|
SystemDiskPort = scsiAddress.PortNumber;
|
|
SystemDiskPath = scsiAddress.PathId;
|
|
|
|
//
|
|
// Check if we are allowed to have a single bus on the system.
|
|
// If disks on system bus are allowed, reset the Port and Path
|
|
// to uninitialized values. Leave the signature set so we don't
|
|
// pick the system disk.
|
|
//
|
|
|
|
singleBus = 0;
|
|
status = GetRegistryValue( &ClusDiskRegistryPath,
|
|
CLUSDISK_SINGLE_BUS_KEYNAME,
|
|
&singleBus );
|
|
|
|
if ( NT_SUCCESS(status) && singleBus ) {
|
|
ClusDiskPrint(( 1,
|
|
"[ClusDisk] ClusDiskInitialize: %ws parm found, allow use of system bus\n",
|
|
CLUSDISK_SINGLE_BUS_KEYNAME ));
|
|
|
|
SystemDiskPort = 0xff;
|
|
SystemDiskPath = 0xff;
|
|
}
|
|
status = STATUS_SUCCESS;
|
|
singleBus = 0;
|
|
|
|
RtlInitUnicodeString( &ntUnicodeString, clussvcKey );
|
|
status = GetRegistryValue( &ntUnicodeString,
|
|
CLUSSVC_VALUENAME_MANAGEDISKSONSYSTEMBUSES,
|
|
&singleBus );
|
|
|
|
if ( NT_SUCCESS(status) && singleBus ) {
|
|
ClusDiskPrint(( 1,
|
|
"[ClusDisk] ClusDiskInitialize: %ws parm found, allow use of system bus\n",
|
|
CLUSSVC_VALUENAME_MANAGEDISKSONSYSTEMBUSES ));
|
|
|
|
SystemDiskPort = 0xff;
|
|
SystemDiskPath = 0xff;
|
|
}
|
|
status = STATUS_SUCCESS;
|
|
|
|
} else {
|
|
ClusDiskPrint((
|
|
1,
|
|
"[ClusDisk] Failed to get boot device drive layout info. Error %08X.\n",
|
|
status
|
|
));
|
|
status = STATUS_SUCCESS; // Use default Port/Path of -1
|
|
}
|
|
|
|
ExFreePool( driveLayout );
|
|
|
|
return(status);
|
|
|
|
} // GetSystemRootPort
|
|
|
|
|
|
NTSTATUS
|
|
GetRegistryValue(
|
|
PUNICODE_STRING KeyName,
|
|
PWSTR ValueName,
|
|
PULONG ReturnValue
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Returns the ULONG registry value for the Value and Key specified.
|
|
|
|
Arguments:
|
|
|
|
KeyName - Unicode string indicating the registry key to use.
|
|
|
|
ValueName - String indicating the value name to return.
|
|
|
|
ReturnValue - Pointer to ULONG buffer.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
{
|
|
HANDLE parametersKey;
|
|
|
|
NTSTATUS status = STATUS_UNSUCCESSFUL;
|
|
|
|
ULONG length;
|
|
ULONG type;
|
|
|
|
OBJECT_ATTRIBUTES objectAttributes;
|
|
|
|
UNICODE_STRING keyName;
|
|
|
|
*ReturnValue = 0;
|
|
|
|
//
|
|
// Setup the object attributes for the Parameters\SingleBus key.
|
|
//
|
|
|
|
InitializeObjectAttributes(
|
|
&objectAttributes,
|
|
KeyName,
|
|
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
//
|
|
// Open Parameters key.
|
|
//
|
|
|
|
status = ZwOpenKey(
|
|
¶metersKey,
|
|
KEY_READ,
|
|
&objectAttributes
|
|
);
|
|
if ( !NT_SUCCESS(status) ) {
|
|
ClusDiskPrint(( 1,
|
|
"[ClusDisk] GetRegistryValue: Failed to open registry key: %ws. Status: %lx\n",
|
|
KeyName->Buffer,
|
|
status
|
|
));
|
|
|
|
goto FnExit;
|
|
}
|
|
|
|
RtlInitUnicodeString( &keyName, ValueName );
|
|
type = REG_DWORD;
|
|
length = sizeof(ULONG);
|
|
|
|
status = RegistryQueryValue( parametersKey,
|
|
ValueName,
|
|
&type,
|
|
ReturnValue,
|
|
&length );
|
|
|
|
ZwClose( parametersKey );
|
|
|
|
if ( !NT_SUCCESS(status) ||
|
|
(length != 4) ) {
|
|
|
|
*ReturnValue = 0;
|
|
ClusDiskPrint(( 3,
|
|
"[ClusDisk] GetRegistryValue: Failed to read registry value, status %08LX, length %u\n",
|
|
status,
|
|
length ));
|
|
goto FnExit;
|
|
}
|
|
|
|
if ( *ReturnValue ) {
|
|
ClusDiskPrint(( 1,
|
|
"[ClusDisk] GetRegistryValue: Allow use of system bus\n" ));
|
|
}
|
|
|
|
FnExit:
|
|
|
|
return status;
|
|
|
|
} // GetRegistryValue
|
|
|
|
|
|
|
|
VOID
|
|
ResetScsiBusses(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Reset all SCSI busses at once on the system.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PCONFIGURATION_INFORMATION configurationInformation;
|
|
ULONG i;
|
|
ULONG idx;
|
|
NTSTATUS status;
|
|
SCSI_ADDRESS scsiAddress;
|
|
HANDLE fileHandle;
|
|
IO_STATUS_BLOCK ioStatusBlock;
|
|
WCHAR portDeviceBuffer[64];
|
|
UNICODE_STRING portDevice;
|
|
PDEVICE_OBJECT deviceObject;
|
|
PFILE_OBJECT fileObject;
|
|
OBJECT_ATTRIBUTES objectAttributes;
|
|
LARGE_INTEGER waitTime;
|
|
|
|
RtlZeroMemory( &scsiAddress, sizeof(SCSI_ADDRESS) );
|
|
scsiAddress.Length = sizeof(SCSI_ADDRESS);
|
|
|
|
CDLOG( "ResetScsiBusses: Entry" );
|
|
|
|
//
|
|
// Get the system configuration information.
|
|
//
|
|
|
|
configurationInformation = IoGetConfigurationInformation();
|
|
|
|
//
|
|
// Reset each scsi bus
|
|
//
|
|
|
|
for ( i = 0; i < configurationInformation->ScsiPortCount; i++ ) {
|
|
|
|
if ( SystemDiskPort == i ) {
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Create device name for the physical disk.
|
|
//
|
|
|
|
if ( FAILED( StringCchPrintfW( portDeviceBuffer,
|
|
RTL_NUMBER_OF(portDeviceBuffer),
|
|
L"\\Device\\ScsiPort%d",
|
|
i ) ) ) {
|
|
continue;
|
|
}
|
|
|
|
WCSLEN_ASSERT( portDeviceBuffer );
|
|
|
|
RtlInitUnicodeString( &portDevice, portDeviceBuffer );
|
|
|
|
//
|
|
// Try to open this device to get its scsi info
|
|
//
|
|
|
|
InitializeObjectAttributes( &objectAttributes,
|
|
&portDevice,
|
|
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
|
|
NULL,
|
|
NULL );
|
|
|
|
status = ZwOpenFile( &fileHandle,
|
|
FILE_ALL_ACCESS,
|
|
&objectAttributes,
|
|
&ioStatusBlock,
|
|
0,
|
|
FILE_NON_DIRECTORY_FILE );
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
ClusDiskPrint((1,
|
|
"[ClusDisk] ResetScsiBusses, failed to open file %wZ. Error %08X.\n",
|
|
&portDevice, status ));
|
|
|
|
continue;
|
|
}
|
|
|
|
status = ObReferenceObjectByHandle( fileHandle,
|
|
0,
|
|
NULL,
|
|
KernelMode,
|
|
(PVOID *) &fileObject,
|
|
NULL );
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
ClusDiskPrint((1,
|
|
"[ClusDisk] Failed to reference object for file %wZ. Error %08X.\n",
|
|
&portDevice,
|
|
status ));
|
|
|
|
ZwClose( fileHandle );
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Get the address of the target device object. If this file represents
|
|
// a device that was opened directly, then simply use the device or its
|
|
// attached device(s) directly. Also get the address of the Fast Io
|
|
// dispatch structure.
|
|
//
|
|
|
|
if (!(fileObject->Flags & FO_DIRECT_DEVICE_OPEN)) {
|
|
deviceObject = IoGetRelatedDeviceObject( fileObject );
|
|
|
|
// Add a reference to the object so we can dereference it later.
|
|
ObReferenceObject( deviceObject );
|
|
} else {
|
|
deviceObject = IoGetAttachedDeviceReference( fileObject->DeviceObject );
|
|
}
|
|
|
|
//
|
|
// If we get a file system device object... go back and get the
|
|
// device object.
|
|
//
|
|
if ( deviceObject->DeviceType == FILE_DEVICE_DISK_FILE_SYSTEM ) {
|
|
ObDereferenceObject( deviceObject );
|
|
deviceObject = IoGetAttachedDeviceReference( fileObject->DeviceObject );
|
|
}
|
|
ASSERT( deviceObject->DeviceType != FILE_DEVICE_DISK_FILE_SYSTEM );
|
|
|
|
//
|
|
// We don't know all the paths on this HBA. Try to reset all paths.
|
|
//
|
|
|
|
for ( idx = 0; idx < 2; idx++ ) {
|
|
scsiAddress.PathId = (UCHAR)idx;
|
|
|
|
ClusDiskLogError( RootDeviceObject->DriverObject, // Use RootDeviceObject not DevObj
|
|
RootDeviceObject,
|
|
scsiAddress.PathId, // Sequence number
|
|
0, // Major function code
|
|
0, // Retry count
|
|
ID_RESET_BUSSES, // Unique error
|
|
STATUS_SUCCESS,
|
|
CLUSDISK_RESET_BUS_REQUESTED,
|
|
0,
|
|
NULL );
|
|
|
|
ResetScsiDevice( fileHandle, &scsiAddress );
|
|
}
|
|
|
|
//
|
|
// Close the scsiport handle after the break reserve IOCTL is sent.
|
|
//
|
|
|
|
ZwClose( fileHandle );
|
|
ObDereferenceObject( fileObject );
|
|
|
|
DEREFERENCE_OBJECT( deviceObject );
|
|
}
|
|
|
|
//
|
|
// Now sleep for a few seconds
|
|
//
|
|
waitTime.QuadPart = (ULONGLONG)(RESET_SLEEP * -(10000*1000));
|
|
KeDelayExecutionThread( KernelMode, FALSE, &waitTime );
|
|
CDLOG( "ResetScsiBusses: Exit" );
|
|
|
|
return;
|
|
|
|
} // ResetScsiBusses
|
|
|
|
|
|
NTSTATUS
|
|
ClusDiskGetDeviceObject(
|
|
IN PWCHAR DeviceName,
|
|
OUT PDEVICE_OBJECT *DeviceObject
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Get the device object pointer given a symbolic device name.
|
|
The device object will have reference count incremented and the
|
|
caller must decrement the count when done with the object.
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
PDEVICE_OBJECT deviceObject;
|
|
PDEVICE_OBJECT targetDevice;
|
|
PFILE_OBJECT fileObject;
|
|
UNICODE_STRING deviceName;
|
|
WCHAR path[MAX_PATH] = L"";
|
|
|
|
WCSLEN_ASSERT( DeviceName );
|
|
|
|
//DbgBreakPoint();
|
|
|
|
if ( FAILED( StringCchCopyW( path,
|
|
RTL_NUMBER_OF(path),
|
|
DeviceName ) ) ) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
GetSymbolicLink( L"", path );
|
|
if ( wcslen(path) == 0 ) {
|
|
ClusDiskPrint((
|
|
1,
|
|
"[ClusDisk] GetDeviceObject: Failed find symbolic link for %ws\n",
|
|
DeviceName ));
|
|
return(STATUS_FILE_INVALID);
|
|
}
|
|
|
|
RtlInitUnicodeString( &deviceName, path );
|
|
//DbgBreakPoint();
|
|
status = IoGetDeviceObjectPointer( &deviceName,
|
|
FILE_READ_ATTRIBUTES,
|
|
&fileObject,
|
|
&targetDevice );
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
ClusDiskPrint((
|
|
1,
|
|
"[ClusDisk] GetDeviceObject: Failed to get target devobj, %LX\n",
|
|
status ));
|
|
|
|
CDLOG( "ClusDiskGetDeviceObject: GetDevObj failed, status %!status!",
|
|
status );
|
|
|
|
} else {
|
|
if ( !(fileObject->Flags & FO_DIRECT_DEVICE_OPEN) ) {
|
|
deviceObject = IoGetRelatedDeviceObject( fileObject );
|
|
|
|
// Add a reference to the object so we can dereference it later.
|
|
ObReferenceObject( deviceObject );
|
|
|
|
//
|
|
// If we get a file system device object... go back and get the
|
|
// device object.
|
|
//
|
|
if ( deviceObject->DeviceType == FILE_DEVICE_DISK_FILE_SYSTEM ) {
|
|
ObDereferenceObject( deviceObject );
|
|
deviceObject = IoGetAttachedDeviceReference( fileObject->DeviceObject );
|
|
}
|
|
ClusDiskPrint((
|
|
3,
|
|
"[ClusDisk] GetDevObj: (DIRECT_OPEN) fileObj = %p, devObj= %p \n",
|
|
fileObject, deviceObject ));
|
|
} else {
|
|
deviceObject = IoGetAttachedDeviceReference( fileObject->DeviceObject );
|
|
ClusDiskPrint((
|
|
3,
|
|
"[ClusDisk] GetDevObj: fileObj = %p, devObj= %p \n",
|
|
fileObject, deviceObject ));
|
|
}
|
|
*DeviceObject = deviceObject;
|
|
ObDereferenceObject( fileObject );
|
|
}
|
|
|
|
ClusDiskPrint((
|
|
3,
|
|
"[ClusDisk] GetDeviceObject: target devobj = %p, status = %LX\n",
|
|
targetDevice,
|
|
status ));
|
|
|
|
return(status);
|
|
|
|
} // ClusDiskGetDeviceObject
|
|
|
|
|
|
|
|
NTSTATUS
|
|
ClusDiskDeviceChangeNotification(
|
|
IN PDEVICE_INTERFACE_CHANGE_NOTIFICATION DeviceChangeNotification,
|
|
IN PCLUS_DEVICE_EXTENSION DeviceExtension
|
|
)
|
|
{
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Handle the arrival of new disk spindles. We only want to add the signature
|
|
to the available list if it is not already a known signature.
|
|
|
|
If the signature matches one we should be controlling, we also need to
|
|
try to attach to the disk.
|
|
|
|
Arguments:
|
|
|
|
DeviceChangeNotification - the device change notification structure
|
|
|
|
DeviceExtension - the device extension for the root device
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS for this request.
|
|
|
|
--*/
|
|
|
|
CDLOG( "DeviceChangeNotification: Entry DO %p", DeviceExtension->DeviceObject );
|
|
|
|
//
|
|
// Process device arrivals only.
|
|
//
|
|
if ( IsEqualGUID( &DeviceChangeNotification->Event,
|
|
&GUID_DEVICE_INTERFACE_ARRIVAL ) ) {
|
|
|
|
ClusDiskPrint(( 3,
|
|
"[ClusDisk] Disk arrival: %ws \n",
|
|
DeviceChangeNotification->SymbolicLinkName->Buffer ));
|
|
|
|
#if CLUSTER_FREE_ASSERTS
|
|
DbgPrint("[ClusDisk] Disk arrival: %ws \n", DeviceChangeNotification->SymbolicLinkName->Buffer );
|
|
#endif
|
|
|
|
ProcessDeviceArrival( DeviceChangeNotification,
|
|
DeviceExtension,
|
|
FALSE ); // Disk arrival
|
|
|
|
}
|
|
|
|
CDLOG( "DeviceChangeNotification: Exit DO %p", DeviceExtension->DeviceObject );
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
} // ClusDiskDeviceChangeNotification
|
|
|
|
|
|
|
|
NTSTATUS
|
|
ClusDiskVolumeChangeNotification(
|
|
IN PDEVICE_INTERFACE_CHANGE_NOTIFICATION DeviceChangeNotification,
|
|
IN PCLUS_DEVICE_EXTENSION DeviceExtension
|
|
)
|
|
{
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Handle the arrival of new volumes. We only want to attach to the
|
|
volume if the signature is already in the signature list.
|
|
|
|
Arguments:
|
|
|
|
DeviceChangeNotification - the device change notification structure
|
|
|
|
DeviceExtension - the device extension for the root device
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS for this request.
|
|
|
|
--*/
|
|
|
|
CDLOG( "VolumeChangeNotification: Entry DO %p", DeviceExtension->DeviceObject );
|
|
|
|
//
|
|
// Process device arrivals only.
|
|
//
|
|
if ( IsEqualGUID( &DeviceChangeNotification->Event,
|
|
&GUID_DEVICE_INTERFACE_ARRIVAL ) ) {
|
|
|
|
ClusDiskPrint(( 3,
|
|
"[ClusDisk] Volume arrival: %ws \n",
|
|
DeviceChangeNotification->SymbolicLinkName->Buffer ));
|
|
|
|
#if CLUSTER_FREE_ASSERTS
|
|
DbgPrint("[ClusDisk] Volume arrival: %ws \n", DeviceChangeNotification->SymbolicLinkName->Buffer );
|
|
#endif
|
|
|
|
ProcessDeviceArrival( DeviceChangeNotification,
|
|
DeviceExtension,
|
|
TRUE ); // Volume arrival
|
|
}
|
|
|
|
CDLOG( "VolumeChangeNotification: Exit DO %p", DeviceExtension->DeviceObject );
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
} // ClusDiskVolumeChangeNotification
|
|
|
|
|
|
|
|
NTSTATUS
|
|
ProcessDeviceArrival(
|
|
IN PDEVICE_INTERFACE_CHANGE_NOTIFICATION DeviceChangeNotification,
|
|
IN PCLUS_DEVICE_EXTENSION DeviceExtension,
|
|
IN BOOLEAN VolumeArrival
|
|
)
|
|
{
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Handle the arrival of new disks or volumes. Queues a work-item to
|
|
process either a disk arrival or volume arrival.
|
|
|
|
WARNING: don't do anything that will result in PnP notification in
|
|
this thread or deadlock will occur. For example, locking or dismounting
|
|
a volume causes PnP notification to occur, which cause a deadlock waiting
|
|
for this pnp thread to continue.
|
|
|
|
Arguments:
|
|
|
|
DeviceChangeNotification - the device change notification structure
|
|
|
|
DeviceExtension - the device extension for the root device
|
|
|
|
VolumeArrival - TRUE if processing a volume arrival. FALSE for disk
|
|
arrival.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS for this request.
|
|
|
|
--*/
|
|
|
|
PIO_WORKITEM workItem = NULL;
|
|
PDEVICE_CHANGE_CONTEXT workContext = NULL;
|
|
PWSTR symLinkBuffer = NULL;
|
|
PDRIVE_LAYOUT_INFORMATION_EX driveLayoutInfo = NULL;
|
|
|
|
NTSTATUS status;
|
|
ULONG driveLayoutSize;
|
|
|
|
OBJECT_ATTRIBUTES objectAttributes;
|
|
HANDLE fileHandle = NULL;
|
|
HANDLE eventHandle= NULL;
|
|
HANDLE deviceHandle = NULL;
|
|
IO_STATUS_BLOCK ioStatusBlock;
|
|
STORAGE_DEVICE_NUMBER deviceNumber;
|
|
SCSI_ADDRESS scsiAddress;
|
|
|
|
UNICODE_STRING availableName;
|
|
UNICODE_STRING deviceName;
|
|
|
|
BOOLEAN cleanupRequired = TRUE;
|
|
|
|
WCHAR deviceNameBuffer[MAX_PARTITION_NAME_LENGTH];
|
|
|
|
CDLOG( "DeviceArrival: Entry DO %p", DeviceExtension->DeviceObject );
|
|
|
|
CDLOG( "DeviceArrival: Arrival %ws ",
|
|
DeviceChangeNotification->SymbolicLinkName->Buffer );
|
|
|
|
//
|
|
// Create event for notification.
|
|
//
|
|
status = ZwCreateEvent( &eventHandle,
|
|
EVENT_ALL_ACCESS,
|
|
NULL,
|
|
SynchronizationEvent,
|
|
FALSE );
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
ClusDiskPrint(( 1,
|
|
"[ClusDisk] DeviceArrival: Failed to create event, status %08X\n",
|
|
status ));
|
|
|
|
goto FnExit;
|
|
}
|
|
|
|
//
|
|
// Setup object attributes for the file to open.
|
|
//
|
|
InitializeObjectAttributes(&objectAttributes,
|
|
DeviceChangeNotification->SymbolicLinkName,
|
|
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
|
|
NULL,
|
|
NULL);
|
|
|
|
status = ZwCreateFile(&fileHandle,
|
|
SYNCHRONIZE | FILE_READ_ATTRIBUTES,
|
|
&objectAttributes,
|
|
&ioStatusBlock,
|
|
NULL,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
FILE_OPEN,
|
|
FILE_SYNCHRONOUS_IO_NONALERT,
|
|
NULL,
|
|
0 );
|
|
ASSERT( status != STATUS_PENDING );
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
ClusDiskPrint(( 1,
|
|
"[ClusDisk] DeviceArrival, failed to open file %ws. Error %08X.\n",
|
|
DeviceChangeNotification->SymbolicLinkName->Buffer,
|
|
status ));
|
|
CDLOG( "DeviceArrival: failed to open file %ws. Error %08X ",
|
|
DeviceChangeNotification->SymbolicLinkName->Buffer,
|
|
status );
|
|
|
|
goto FnExit;
|
|
}
|
|
|
|
driveLayoutSize = sizeof(DRIVE_LAYOUT_INFORMATION_EX) +
|
|
(MAX_PARTITIONS * sizeof(PARTITION_INFORMATION_EX));
|
|
|
|
driveLayoutInfo = ExAllocatePool( NonPagedPoolCacheAligned, driveLayoutSize );
|
|
|
|
if ( NULL == driveLayoutInfo ) {
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
ClusDiskPrint(( 1,
|
|
"[ClusDisk] DeviceArrival, failed to allocate drive layout structure \n" ));
|
|
CDLOG( "DeviceArrival: Unable to allocate drive layout structure " );
|
|
goto FnExit;
|
|
}
|
|
|
|
//
|
|
// Get the Signature.
|
|
//
|
|
status = ZwDeviceIoControlFile( fileHandle,
|
|
eventHandle,
|
|
NULL,
|
|
NULL,
|
|
&ioStatusBlock,
|
|
IOCTL_DISK_GET_DRIVE_LAYOUT_EX,
|
|
NULL,
|
|
0,
|
|
driveLayoutInfo,
|
|
driveLayoutSize );
|
|
|
|
if ( status == STATUS_PENDING ) {
|
|
status = ZwWaitForSingleObject(eventHandle,
|
|
FALSE,
|
|
NULL);
|
|
ASSERT( NT_SUCCESS(status) );
|
|
status = ioStatusBlock.Status;
|
|
}
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
ClusDiskPrint(( 1,
|
|
"[ClusDisk] DeviceArrival, IOCTL_DISK_GET_DRIVE_LAYOUT_EX failed %08X \n",
|
|
status ));
|
|
CDLOG( "DeviceArrival: IOCTL_DISK_GET_DRIVE_LAYOUT_EX failed %08X ",
|
|
status );
|
|
goto FnExit;
|
|
}
|
|
|
|
//
|
|
// Only process MBR disks.
|
|
//
|
|
|
|
if ( PARTITION_STYLE_MBR != driveLayoutInfo->PartitionStyle ) {
|
|
ClusDiskPrint(( 3,
|
|
"[ClusDisk] DeviceArrival, Skipping non-MBR disk \n" ));
|
|
CDLOG( "DeviceArrival: Skipping non-MBR disk " );
|
|
goto FnExit;
|
|
}
|
|
|
|
//
|
|
// No signature or system disk signature, don't add it.
|
|
//
|
|
|
|
if ( ( 0 == driveLayoutInfo->Mbr.Signature ) ||
|
|
SystemDiskSignature == driveLayoutInfo->Mbr.Signature ) {
|
|
|
|
ClusDiskPrint(( 1,
|
|
"[ClusDisk] DeviceArrival, invalid signature %08X \n",
|
|
driveLayoutInfo->Mbr.Signature ));
|
|
CDLOG( "DeviceArrival: invalid signature %08X ",
|
|
driveLayoutInfo->Mbr.Signature );
|
|
|
|
status = STATUS_SUCCESS;
|
|
goto FnExit;
|
|
}
|
|
|
|
//
|
|
// Get the SCSI address.
|
|
//
|
|
|
|
status = ZwDeviceIoControlFile( fileHandle,
|
|
eventHandle,
|
|
NULL,
|
|
NULL,
|
|
&ioStatusBlock,
|
|
IOCTL_SCSI_GET_ADDRESS,
|
|
NULL,
|
|
0,
|
|
&scsiAddress,
|
|
sizeof(SCSI_ADDRESS) );
|
|
if ( status == STATUS_PENDING ) {
|
|
status = ZwWaitForSingleObject(eventHandle,
|
|
FALSE,
|
|
NULL);
|
|
ASSERT( NT_SUCCESS(status) );
|
|
status = ioStatusBlock.Status;
|
|
}
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
ClusDiskPrint(( 1,
|
|
"[ClusDisk] DeviceArrival, IOCTL_SCSI_GET_ADDRESS failed %08X \n",
|
|
status ));
|
|
CDLOG( "DeviceArrival: IOCTL_SCSI_GET_ADDRESS failed %08X ",
|
|
status );
|
|
goto FnExit;
|
|
}
|
|
|
|
//
|
|
// Get the device and partition number.
|
|
//
|
|
status = ZwDeviceIoControlFile( fileHandle,
|
|
eventHandle,
|
|
NULL,
|
|
NULL,
|
|
&ioStatusBlock,
|
|
IOCTL_STORAGE_GET_DEVICE_NUMBER,
|
|
NULL,
|
|
0,
|
|
&deviceNumber,
|
|
sizeof(deviceNumber) );
|
|
if ( status == STATUS_PENDING ) {
|
|
status = ZwWaitForSingleObject(eventHandle,
|
|
FALSE,
|
|
NULL);
|
|
ASSERT( NT_SUCCESS(status) );
|
|
status = ioStatusBlock.Status;
|
|
}
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
ClusDiskPrint(( 1,
|
|
"[ClusDisk] DeviceArrival, IOCTL_STORAGE_GET_DEVICE_NUMBER failed %08X \n",
|
|
status ));
|
|
CDLOG( "DeviceArrival: IOCTL_STORAGE_GET_DEVICE_NUMBER failed %08X ",
|
|
status );
|
|
goto FnExit;
|
|
}
|
|
|
|
//
|
|
// If not a disk type device, we are done. For example, CD-ROM devices
|
|
// show up as volumes.
|
|
//
|
|
|
|
if ( FILE_DEVICE_DISK != deviceNumber.DeviceType ) {
|
|
status = STATUS_SUCCESS;
|
|
ClusDiskPrint(( 1,
|
|
"[ClusDisk] DeviceArrival, device is not disk type, skipping \n" ));
|
|
CDLOG( "DeviceArrival: Device is not disk type, skipping" );
|
|
goto FnExit;
|
|
}
|
|
|
|
//
|
|
// Check if signature is one we should control. If not, exit.
|
|
//
|
|
|
|
if ( !ClusDiskIsSignatureDisk( driveLayoutInfo->Mbr.Signature ) ) {
|
|
|
|
ClusDiskPrint(( 1,
|
|
"[ClusDisk] DeviceArrival, Signature %08X not in list, skipping \n",
|
|
driveLayoutInfo->Mbr.Signature ));
|
|
CDLOG( "DeviceArrival: Signature %08X not in list, skipping",
|
|
driveLayoutInfo->Mbr.Signature );
|
|
|
|
//
|
|
// Signature was not in the signature list. Add signature to the list
|
|
// if acceptable.
|
|
//
|
|
|
|
if ( (SystemDiskPort != scsiAddress.PortNumber) ||
|
|
(SystemDiskPath != scsiAddress.PathId) ) {
|
|
//
|
|
// Allocate buffer for Signatures registry key. So we can add
|
|
// the signature to the available list.
|
|
//
|
|
status = ClusDiskInitRegistryString( &availableName,
|
|
CLUSDISK_AVAILABLE_DISKS_KEYNAME,
|
|
wcslen(CLUSDISK_AVAILABLE_DISKS_KEYNAME)
|
|
);
|
|
|
|
if ( NT_SUCCESS(status) ) {
|
|
//
|
|
// Create the signature key under \Parameters\AvailableDisks
|
|
//
|
|
status = ClusDiskAddSignature( &availableName,
|
|
driveLayoutInfo->Mbr.Signature,
|
|
TRUE
|
|
);
|
|
|
|
FREE_DEVICE_NAME_BUFFER( availableName )
|
|
}
|
|
if ( NT_SUCCESS(status) ) {
|
|
ClusDiskPrint(( 3,
|
|
"[ClusDisk] DeviceArrival, added signature %08LX to available list \n",
|
|
driveLayoutInfo->Mbr.Signature ));
|
|
} else {
|
|
ClusDiskPrint(( 1,
|
|
"[ClusDisk] DeviceArrival, failed to add signature %08LX. Error %08X.\n",
|
|
driveLayoutInfo->Mbr.Signature,
|
|
status ));
|
|
}
|
|
}
|
|
|
|
goto FnExit;
|
|
}
|
|
|
|
//
|
|
// Try to open the clusdisk object. If it already exists, then we don't
|
|
// need to do anything.
|
|
//
|
|
|
|
if ( FAILED( StringCchPrintfW( deviceNameBuffer,
|
|
RTL_NUMBER_OF(deviceNameBuffer),
|
|
CLUSDISK_DEVICE_NAME,
|
|
deviceNumber.DeviceNumber,
|
|
deviceNumber.PartitionNumber ) ) ) {
|
|
goto FnExit;
|
|
}
|
|
|
|
WCSLEN_ASSERT( deviceNameBuffer );
|
|
|
|
RtlInitUnicodeString( &deviceName, deviceNameBuffer );
|
|
|
|
InitializeObjectAttributes( &objectAttributes,
|
|
&deviceName,
|
|
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
|
|
NULL,
|
|
NULL );
|
|
|
|
status = ZwOpenFile( &deviceHandle,
|
|
FILE_READ_ATTRIBUTES,
|
|
&objectAttributes,
|
|
&ioStatusBlock,
|
|
0,
|
|
FILE_NON_DIRECTORY_FILE );
|
|
|
|
if ( NT_SUCCESS(status) ) {
|
|
ClusDiskPrint(( 1,
|
|
"[ClusDisk] DeviceArrival, skipping existing clusdisk device \n" ));
|
|
CDLOG( "DeviceArrival: skipping existing clusdisk device " );
|
|
goto FnExit;
|
|
}
|
|
|
|
//
|
|
// Allocate and prepare a work item to finish the processing.
|
|
//
|
|
|
|
workItem = IoAllocateWorkItem( DeviceExtension->DeviceObject );
|
|
|
|
if ( NULL == workItem ) {
|
|
ClusDiskPrint(( 1,
|
|
"[ClusDisk] DeviceArrival: Failed to allocate WorkItem \n" ));
|
|
goto FnExit;
|
|
}
|
|
|
|
workContext = ExAllocatePool( NonPagedPool, sizeof( DEVICE_CHANGE_CONTEXT ) );
|
|
|
|
if ( !workContext ) {
|
|
ClusDiskPrint(( 1,
|
|
"[ClusDisk] DeviceArrival: Failed to allocate context \n" ));
|
|
goto FnExit;
|
|
}
|
|
|
|
RtlZeroMemory( workContext, sizeof( DEVICE_CHANGE_CONTEXT ) );
|
|
workContext->WorkItem = workItem;
|
|
workContext->DeviceExtension = DeviceExtension;
|
|
|
|
//
|
|
// We have to copy the symbolic link info as pnp thread may free the
|
|
// structures on return.
|
|
//
|
|
|
|
workContext->SymbolicLinkName.Length = 0;
|
|
workContext->SymbolicLinkName.MaximumLength = DeviceChangeNotification->SymbolicLinkName->MaximumLength +
|
|
sizeof(UNICODE_NULL);
|
|
|
|
symLinkBuffer = ExAllocatePool( PagedPool,
|
|
workContext->SymbolicLinkName.MaximumLength );
|
|
|
|
if ( !symLinkBuffer ) {
|
|
ClusDiskPrint(( 1,
|
|
"[ClusDisk] DeviceArrival: Failed to allocate symlink buffer \n" ));
|
|
goto FnExit;
|
|
}
|
|
|
|
workContext->SymbolicLinkName.Buffer = symLinkBuffer;
|
|
|
|
RtlCopyUnicodeString( &workContext->SymbolicLinkName, DeviceChangeNotification->SymbolicLinkName );
|
|
|
|
workContext->Signature = driveLayoutInfo->Mbr.Signature;
|
|
workContext->DeviceNumber = deviceNumber.DeviceNumber;
|
|
workContext->PartitionNumber = deviceNumber.PartitionNumber;
|
|
workContext->ScsiAddress = scsiAddress;
|
|
|
|
if ( VolumeArrival ) {
|
|
|
|
PDEVICE_OBJECT part0Device = NULL;
|
|
PFILE_OBJECT part0FileObject = NULL;
|
|
|
|
PCLUS_DEVICE_EXTENSION zeroExtension;
|
|
|
|
WCHAR part0Name[MAX_CLUSDISK_DEVICE_NAME_LENGTH];
|
|
|
|
UNICODE_STRING part0UnicodeString;
|
|
|
|
//
|
|
// WARNING!
|
|
// Don't use AttachedDevice() or deadlock might occur acquiring
|
|
// ClusDiskDeviceLock.
|
|
//
|
|
|
|
//
|
|
// It is possible that the Partition0 device is online: disk marked online
|
|
// then partition manager poked to bring the new volumes online. In this
|
|
// case, we should leave the volume online and create the new clusdisk volume
|
|
// object. Now we try to find out if the Partition0 device is online.
|
|
// If online, we don't need to do anything as default state is online.
|
|
// If offline, set this new volume device to offline.
|
|
//
|
|
|
|
//
|
|
// Get Partition0 device and look at the state in the device extension.
|
|
// If Partition0 online, leave the volume online.
|
|
// If we can't get the Partition0 device, then set the volume state to offline.
|
|
//
|
|
|
|
//
|
|
// Open ClusDiskXPart0 directly.
|
|
//
|
|
|
|
if ( FAILED( StringCchPrintfW( part0Name,
|
|
RTL_NUMBER_OF(part0Name),
|
|
CLUSDISK_DEVICE_NAME,
|
|
deviceNumber.DeviceNumber,
|
|
0 ) ) ) {
|
|
goto FnExit;
|
|
}
|
|
|
|
RtlInitUnicodeString( &part0UnicodeString, part0Name );
|
|
|
|
status = IoGetDeviceObjectPointer( &part0UnicodeString,
|
|
FILE_READ_ATTRIBUTES,
|
|
&part0FileObject,
|
|
&part0Device );
|
|
|
|
if ( NT_SUCCESS(status) ) {
|
|
|
|
zeroExtension = part0Device->DeviceExtension;
|
|
|
|
if ( zeroExtension ) {
|
|
|
|
#if CLUSTER_FREE_ASSERTS
|
|
DbgPrint("[ClusDisk] Part0 state: %d \n", zeroExtension->DiskState );
|
|
#endif
|
|
|
|
if ( DiskOffline == zeroExtension->DiskState ) {
|
|
|
|
ClusDiskPrint(( 3,
|
|
"[ClusDisk] DeviceArrival: DevExt for disk %d / part 0 indicates offline. \n",
|
|
deviceNumber.DeviceNumber ));
|
|
CDLOG( "DeviceArrival: DevExt disk %d / part 0 indicates offline",
|
|
deviceNumber.DeviceNumber );
|
|
|
|
//
|
|
// Mark the volume offline and we'll reset correct volume state in the worker
|
|
// routine.
|
|
//
|
|
|
|
SendFtdiskIoctlSync( NULL,
|
|
deviceNumber.DeviceNumber,
|
|
deviceNumber.PartitionNumber,
|
|
IOCTL_VOLUME_OFFLINE );
|
|
}
|
|
|
|
}
|
|
|
|
ObDereferenceObject( part0FileObject );
|
|
|
|
} else {
|
|
|
|
ClusDiskPrint(( 1,
|
|
"[ClusDisk] DeviceArrival: Failed to get devobj %ws for signature %08X status %08X \n",
|
|
part0Name,
|
|
driveLayoutInfo->Mbr.Signature,
|
|
status ));
|
|
CDLOG( "DeviceArrival: Failed to get devobj %ws for signature %08X status %08X ",
|
|
part0Name,
|
|
driveLayoutInfo->Mbr.Signature,
|
|
status );
|
|
|
|
}
|
|
|
|
status = STATUS_SUCCESS;
|
|
|
|
#if CLUSTER_FREE_ASSERTS
|
|
DbgPrint("[ClusDisk] Queuing volume: %ws \n", DeviceChangeNotification->SymbolicLinkName->Buffer );
|
|
InterlockedIncrement( &ClusDiskDebugVolumeNotificationQueued );
|
|
#endif
|
|
|
|
ClusDiskPrint(( 3,
|
|
"[ClusDisk] DeviceArrival: Queuing work item \n" ));
|
|
|
|
//
|
|
// Queue the workitem. IoQueueWorkItem will insure that the device object is
|
|
// referenced while the work-item progresses.
|
|
//
|
|
|
|
cleanupRequired = FALSE;
|
|
|
|
IoQueueWorkItem( workItem,
|
|
ClusDiskVolumeChangeNotificationWorker,
|
|
DelayedWorkQueue,
|
|
workContext );
|
|
|
|
} else {
|
|
|
|
PPARTITION_INFORMATION_EX partitionInfo;
|
|
ULONG partIndex;
|
|
|
|
// Offline all volumes on this device.
|
|
|
|
for ( partIndex = 0;
|
|
partIndex < driveLayoutInfo->PartitionCount;
|
|
partIndex++ ) {
|
|
|
|
partitionInfo = &driveLayoutInfo->PartitionEntry[partIndex];
|
|
|
|
if ( 0 == partitionInfo->PartitionNumber ) {
|
|
continue;
|
|
}
|
|
|
|
status = SendFtdiskIoctlSync( NULL,
|
|
deviceNumber.DeviceNumber,
|
|
partitionInfo->PartitionNumber,
|
|
IOCTL_VOLUME_OFFLINE );
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
ClusDiskPrint(( 1,
|
|
"[ClusDisk] DeviceArrival: Failed to set state disk%d part%d , status %08X \n",
|
|
deviceNumber.DeviceNumber,
|
|
partitionInfo->PartitionNumber,
|
|
status
|
|
));
|
|
}
|
|
|
|
}
|
|
status = STATUS_SUCCESS;
|
|
|
|
#if CLUSTER_FREE_ASSERTS
|
|
DbgPrint("[ClusDisk] Queuing disk: %ws \n", DeviceChangeNotification->SymbolicLinkName->Buffer );
|
|
InterlockedIncrement( &ClusDiskDebugDiskNotificationQueued );
|
|
#endif
|
|
|
|
ClusDiskPrint(( 3,
|
|
"[ClusDisk] DeviceArrival: Queuing work item \n" ));
|
|
|
|
//
|
|
// Queue the workitem. IoQueueWorkItem will insure that the device object is
|
|
// referenced while the work-item progresses.
|
|
//
|
|
|
|
cleanupRequired = FALSE;
|
|
|
|
IoQueueWorkItem( workItem,
|
|
ClusDiskDeviceChangeNotificationWorker,
|
|
DelayedWorkQueue,
|
|
workContext );
|
|
|
|
}
|
|
|
|
FnExit:
|
|
|
|
if ( cleanupRequired ) {
|
|
if ( workItem ) {
|
|
IoFreeWorkItem( workItem );
|
|
}
|
|
|
|
if ( workContext ) {
|
|
ExFreePool( workContext );
|
|
}
|
|
|
|
if ( symLinkBuffer ) {
|
|
ExFreePool( symLinkBuffer );
|
|
}
|
|
|
|
#if CLUSTER_FREE_ASSERTS
|
|
if ( VolumeArrival ) {
|
|
DbgPrint("[ClusDisk] Skipping volume: %ws \n", DeviceChangeNotification->SymbolicLinkName->Buffer );
|
|
InterlockedIncrement( &ClusDiskDebugVolumeNotificationSkipped );
|
|
} else {
|
|
DbgPrint("[ClusDisk] Skipping disk: %ws \n", DeviceChangeNotification->SymbolicLinkName->Buffer );
|
|
InterlockedIncrement( &ClusDiskDebugDiskNotificationSkipped );
|
|
}
|
|
#endif
|
|
|
|
}
|
|
|
|
if ( driveLayoutInfo ) {
|
|
ExFreePool( driveLayoutInfo );
|
|
}
|
|
|
|
if ( eventHandle ) {
|
|
ZwClose( eventHandle );
|
|
}
|
|
|
|
if ( fileHandle ) {
|
|
ZwClose( fileHandle );
|
|
}
|
|
|
|
if ( deviceHandle ) {
|
|
ZwClose( deviceHandle );
|
|
}
|
|
|
|
CDLOG( "DeviceArrival: Exit, DO %p", DeviceExtension->DeviceObject );
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
} // ProcessDeviceArrival
|
|
|
|
|
|
|
|
NTSTATUS
|
|
ClusDiskDeviceChangeNotificationWorker(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PVOID Context
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Handle the arrival of new disk spindles. We only want to add the signature
|
|
to the available list if it is not already a known signature.
|
|
|
|
If the signature matches one we should be controlling, we also need to
|
|
try to attach to the disk.
|
|
|
|
This routine must free the workitem structure.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - the root device object
|
|
|
|
Context - information relevant to processing this device change.
|
|
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS for this request.
|
|
|
|
--*/
|
|
{
|
|
PDEVICE_CHANGE_CONTEXT deviceChange = Context;
|
|
PCLUS_DEVICE_EXTENSION deviceExtension;
|
|
PIO_WORKITEM workItem;
|
|
PUNICODE_STRING symbolicLinkName;
|
|
NTSTATUS status;
|
|
ULONG signature;
|
|
BOOLEAN stopProcessing = FALSE;
|
|
|
|
CDLOG( "DeviceChangeWorker: Entry DO %p", DeviceObject );
|
|
|
|
deviceExtension = deviceChange->DeviceExtension;
|
|
workItem = deviceChange->WorkItem;
|
|
symbolicLinkName = &deviceChange->SymbolicLinkName;
|
|
signature = deviceChange->Signature;
|
|
|
|
ClusDiskPrint(( 3,
|
|
"[ClusDisk] DeviceChangeWorker, A new disk device arrived signature %08X \n %ws\n",
|
|
signature,
|
|
symbolicLinkName->Buffer ));
|
|
|
|
//
|
|
// Check if signature is already in signature list. If in the signature
|
|
// list, try to attach again. If we are already attached, nothing will
|
|
// happen. If we are not attached, we will attach and make sure the disk
|
|
// is offline.
|
|
//
|
|
|
|
if ( ClusDiskIsSignatureDisk( signature ) ) {
|
|
|
|
//
|
|
// Try to attach, but don't generate a reset.
|
|
//
|
|
|
|
ClusDiskAttachDevice( signature,
|
|
0,
|
|
deviceExtension->DriverObject,
|
|
FALSE, // No reset
|
|
&stopProcessing,
|
|
FALSE ); // Offline, then dismount
|
|
|
|
status = STATUS_SUCCESS;
|
|
goto FnExit;
|
|
}
|
|
|
|
FnExit:
|
|
|
|
#if CLUSTER_FREE_ASSERTS
|
|
DbgPrint("[ClusDisk] Completed disk: %ws \n", symbolicLinkName->Buffer );
|
|
InterlockedIncrement( &ClusDiskDebugDiskNotificationEnded );
|
|
#endif
|
|
|
|
ClusDiskPrint(( 3,
|
|
"[ClusDisk] DeviceChangeWorker, Exit signature %08X \n",
|
|
signature ));
|
|
|
|
CDLOG( "DeviceChangeWorker: Exit, DO %p", deviceExtension->DeviceObject );
|
|
|
|
//
|
|
// Free the work item.
|
|
//
|
|
|
|
IoFreeWorkItem( workItem );
|
|
if ( symbolicLinkName->Buffer ) {
|
|
ExFreePool( symbolicLinkName->Buffer );
|
|
}
|
|
if ( Context ) {
|
|
ExFreePool( Context );
|
|
}
|
|
|
|
return(STATUS_SUCCESS);
|
|
|
|
} // ClusDiskDeviceChangeNotificationWorker
|
|
|
|
|
|
|
|
NTSTATUS
|
|
ClusDiskVolumeChangeNotificationWorker(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PVOID Context
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Handle the arrival of new volumes. We only want to attach to the
|
|
volume if the signature is already in the signature list.
|
|
|
|
This routine must free the workitem structure.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - the root device object
|
|
|
|
Context - information relevant to processing this device change.
|
|
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS for this request.
|
|
|
|
--*/
|
|
{
|
|
PDEVICE_CHANGE_CONTEXT deviceChange = Context;
|
|
PCLUS_DEVICE_EXTENSION deviceExtension;
|
|
PCLUS_DEVICE_EXTENSION zeroExtension = NULL;
|
|
PIO_WORKITEM workItem;
|
|
PUNICODE_STRING symbolicLinkName;
|
|
PDEVICE_OBJECT part0Device = NULL;
|
|
PDEVICE_OBJECT targetDevice = NULL;
|
|
PDEVICE_OBJECT dummyDeviceObj;
|
|
PFILE_OBJECT fileObject;
|
|
|
|
NTSTATUS status;
|
|
|
|
ULONG signature;
|
|
|
|
deviceExtension = deviceChange->DeviceExtension;
|
|
workItem = deviceChange->WorkItem;
|
|
symbolicLinkName = &deviceChange->SymbolicLinkName;
|
|
signature = deviceChange->Signature;
|
|
|
|
CDLOG( "VolumeChangeWorker: Entry DO %p context %p signature %08X DevExt %p ",
|
|
DeviceObject,
|
|
Context,
|
|
signature,
|
|
deviceExtension );
|
|
ClusDiskPrint(( 3,
|
|
"[ClusDisk] VolumeChangeWorker: Entry DO %p context %p signature %08X DevExt %p \n",
|
|
DeviceObject,
|
|
Context,
|
|
signature,
|
|
deviceExtension ));
|
|
|
|
//
|
|
// Make sure the disk object is attached. If not, we are done.
|
|
//
|
|
|
|
if ( !AttachedDevice( signature,
|
|
&part0Device ) ) {
|
|
ClusDiskPrint(( 1,
|
|
"[ClusDisk] VolumeChangeWorker, partition0 not attached for signature %08X \n",
|
|
signature ));
|
|
CDLOG( "VolumeChangeWorker: partition0 not attached for signature %08X ",
|
|
signature );
|
|
|
|
status = STATUS_SUCCESS;
|
|
goto FnExit;
|
|
}
|
|
|
|
//
|
|
// Make sure the disk object doesn't go away until we are done.
|
|
//
|
|
ObReferenceObject( part0Device );
|
|
zeroExtension = part0Device->DeviceExtension;
|
|
|
|
if ( !zeroExtension ) {
|
|
ClusDiskPrint(( 1,
|
|
"[ClusDisk] VolumeChangeWorker, partition0 DE does not exist for signature %08X \n",
|
|
signature ));
|
|
CDLOG( "VolumeChangeWorker: partition0 DE does not exist for signature %08X ",
|
|
signature );
|
|
|
|
status = STATUS_SUCCESS;
|
|
goto FnExit;
|
|
}
|
|
|
|
//
|
|
// Get the target device we should attach to.
|
|
//
|
|
|
|
status = IoGetDeviceObjectPointer( symbolicLinkName,
|
|
FILE_READ_ATTRIBUTES,
|
|
&fileObject,
|
|
&dummyDeviceObj );
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
ClusDiskPrint(( 1,
|
|
"[ClusDisk] VolumeChangeWorker: Failed to get target devobj. Error %08X \n",
|
|
status ));
|
|
CDLOG( "VolumeChangeWorker: GetDevObj failed, status %!status!",
|
|
status );
|
|
goto FnExit;
|
|
}
|
|
|
|
if ( !(fileObject->Flags & FO_DIRECT_DEVICE_OPEN) ) {
|
|
targetDevice = IoGetRelatedDeviceObject( fileObject );
|
|
|
|
// Add a reference to the object so we can dereference it later.
|
|
ObReferenceObject( targetDevice );
|
|
|
|
//
|
|
// If we get a file system device object... go back and get the
|
|
// device object.
|
|
//
|
|
if ( targetDevice->DeviceType == FILE_DEVICE_DISK_FILE_SYSTEM ) {
|
|
ObDereferenceObject( targetDevice );
|
|
targetDevice = IoGetAttachedDeviceReference( fileObject->DeviceObject );
|
|
}
|
|
ClusDiskPrint(( 3,
|
|
"[ClusDisk] VolumeChangeWorker (DIRECT_OPEN) fileObj = %p, devObj= %p \n",
|
|
fileObject,
|
|
targetDevice ));
|
|
} else {
|
|
targetDevice = IoGetAttachedDeviceReference( fileObject->DeviceObject );
|
|
ClusDiskPrint(( 3,
|
|
"[ClusDisk] VolumeChangeWorker: fileObj = %p, devObj= %p \n",
|
|
fileObject,
|
|
targetDevice ));
|
|
}
|
|
ObDereferenceObject( fileObject );
|
|
|
|
//
|
|
// At this point, the target object will have a reference to it.
|
|
// Now we can create the volume object.
|
|
//
|
|
|
|
status = CreateVolumeObject( zeroExtension,
|
|
deviceChange->DeviceNumber,
|
|
deviceChange->PartitionNumber,
|
|
targetDevice );
|
|
|
|
#if 0
|
|
// Ignore returned status and reset the volume offline/online status.
|
|
|
|
//
|
|
// The only acceptable failure is name collision, which means the clusdisk
|
|
// volume already exists. For any other errors, we leave the volume state
|
|
// offline.
|
|
//
|
|
// Seen this error also returned: c000000e STATUS_NO_SUCH_DEVICE
|
|
//
|
|
|
|
if ( STATUS_OBJECT_NAME_COLLISION != status ) {
|
|
goto FnExit;
|
|
}
|
|
#endif
|
|
|
|
FnExit:
|
|
|
|
#if CLUSTER_FREE_ASSERTS
|
|
if ( !NT_SUCCESS(status) ) {
|
|
DbgPrint("[ClusDisk] Failed volume addition %08X \n", status );
|
|
if ( STATUS_OBJECT_NAME_COLLISION != status ) {
|
|
DbgBreakPoint();
|
|
}
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// Reset volume state according to partition0 state.
|
|
//
|
|
|
|
if ( zeroExtension ) {
|
|
|
|
if ( DiskOffline == zeroExtension->DiskState ) {
|
|
|
|
SendFtdiskIoctlSync( targetDevice,
|
|
deviceChange->DeviceNumber,
|
|
deviceChange->PartitionNumber,
|
|
IOCTL_VOLUME_OFFLINE );
|
|
} else {
|
|
|
|
SendFtdiskIoctlSync( targetDevice,
|
|
deviceChange->DeviceNumber,
|
|
deviceChange->PartitionNumber,
|
|
IOCTL_VOLUME_ONLINE );
|
|
}
|
|
}
|
|
|
|
if ( part0Device ) {
|
|
ObDereferenceObject( part0Device );
|
|
}
|
|
|
|
if ( targetDevice ) {
|
|
ObDereferenceObject( targetDevice );
|
|
}
|
|
|
|
#if CLUSTER_FREE_ASSERTS
|
|
DbgPrint("[ClusDisk] Completed volume: %ws \n", symbolicLinkName->Buffer );
|
|
InterlockedIncrement( &ClusDiskDebugVolumeNotificationEnded );
|
|
#endif
|
|
|
|
CDLOG( "VolumeChangeWorker: Exit, DO %p context %p",
|
|
DeviceObject,
|
|
Context );
|
|
ClusDiskPrint(( 3,
|
|
"[ClusDisk] VolumeChangeWorker: Exit DO %p context %p \n",
|
|
DeviceObject,
|
|
Context ));
|
|
|
|
//
|
|
// Free the work item.
|
|
//
|
|
|
|
IoFreeWorkItem( workItem );
|
|
if ( symbolicLinkName->Buffer ) {
|
|
ExFreePool( symbolicLinkName->Buffer );
|
|
}
|
|
if ( Context ) {
|
|
ExFreePool( Context );
|
|
}
|
|
|
|
return(STATUS_SUCCESS);
|
|
|
|
} // ClusDiskVolumeChangeNotificationWorker
|
|
|
|
|
|
|
|
NTSTATUS
|
|
ClusDiskInitialize(
|
|
IN PDRIVER_OBJECT DriverObject
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Common intialization for ClusDisk
|
|
|
|
Arguments:
|
|
|
|
DriverObject - The Cluster Disk driver object.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS for this request.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG status;
|
|
PDEVICE_OBJECT rootDevice;
|
|
PCLUS_DEVICE_EXTENSION deviceExtension;
|
|
UNICODE_STRING uniNameString;
|
|
|
|
//
|
|
// Find the bus on which the system disk is loaded.
|
|
//
|
|
status = GetSystemRootPort();
|
|
if ( !NT_SUCCESS( status )) {
|
|
return status;
|
|
}
|
|
|
|
//
|
|
// Initialize the global locks.
|
|
//
|
|
KeInitializeSpinLock(&ClusDiskSpinLock);
|
|
ExInitializeResourceLite(&ClusDiskDeviceListLock);
|
|
|
|
//
|
|
// Init halt processing work item
|
|
//
|
|
|
|
ExInitializeWorkItem( &HaltWorkItem,
|
|
(PWORKER_THREAD_ROUTINE)ClusDiskHaltProcessingWorker,
|
|
NULL );
|
|
|
|
//
|
|
// Init rescan processing work item
|
|
//
|
|
|
|
ExInitializeWorkItem( &ClusDiskRescanWorkItem,
|
|
(PWORKER_THREAD_ROUTINE)ClusDiskRescanWorker,
|
|
NULL );
|
|
|
|
//
|
|
// Reset all SCSI busses.
|
|
//
|
|
//ResetScsiBusses();
|
|
|
|
//
|
|
// Create device object for \Device\ClusDisk0
|
|
//
|
|
|
|
RtlInitUnicodeString( &uniNameString, CLUSDISK_ROOT_DEVICE );
|
|
|
|
status = IoCreateDevice(DriverObject,
|
|
sizeof(CLUS_DEVICE_EXTENSION),
|
|
&uniNameString,
|
|
FILE_DEVICE_NETWORK,
|
|
FILE_DEVICE_SECURE_OPEN,
|
|
FALSE,
|
|
&rootDevice);
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
return(status);
|
|
}
|
|
|
|
rootDevice->Flags |= DO_DIRECT_IO;
|
|
|
|
deviceExtension = rootDevice->DeviceExtension;
|
|
deviceExtension->DeviceObject = rootDevice;
|
|
deviceExtension->DiskNumber = UNINITIALIZED_DISK_NUMBER;
|
|
deviceExtension->LastPartitionNumber = 0;
|
|
deviceExtension->DriverObject = DriverObject;
|
|
deviceExtension->BusType = RootBus;
|
|
deviceExtension->DiskState = DiskOffline;
|
|
deviceExtension->AttachValid = FALSE;
|
|
deviceExtension->PerformReserves = FALSE;
|
|
deviceExtension->ReserveFailure = 0;
|
|
deviceExtension->Signature = 0xffffffff;
|
|
deviceExtension->Detached = TRUE;
|
|
deviceExtension->OfflinePending = FALSE;
|
|
InitializeListHead( &deviceExtension->WaitingIoctls );
|
|
deviceExtension->SectorSize = 0;
|
|
deviceExtension->ArbitrationSector = 12;
|
|
|
|
IoInitializeRemoveLock( &deviceExtension->RemoveLock, CLUSDISK_ALLOC_TAG, 0, 0 );
|
|
|
|
//
|
|
// Signal the worker thread running event.
|
|
//
|
|
KeInitializeEvent( &deviceExtension->Event, NotificationEvent, TRUE );
|
|
|
|
KeInitializeEvent( &deviceExtension->PagingPathCountEvent,
|
|
NotificationEvent, TRUE );
|
|
deviceExtension->PagingPathCount = 0;
|
|
deviceExtension->HibernationPathCount = 0;
|
|
deviceExtension->DumpPathCount = 0;
|
|
|
|
ExInitializeResourceLite( &deviceExtension->DriveLayoutLock );
|
|
ExInitializeResourceLite( &deviceExtension->ReserveInfoLock );
|
|
|
|
//
|
|
// Init the tick handler timer
|
|
//
|
|
IoInitializeTimer( rootDevice, ClusDiskTickHandler, NULL );
|
|
|
|
//
|
|
// This is the physical device object for \Device\ClusDisk0.
|
|
//
|
|
ObReferenceObject( rootDevice );
|
|
deviceExtension->PhysicalDevice = rootDevice;
|
|
|
|
RootDeviceObject = rootDevice;
|
|
|
|
//
|
|
// Call the initialize routine (for each bus type) for the first time.
|
|
//
|
|
// With the new PNP stuff, we should be able to remove the following call.
|
|
// It's been tried and it seems to work correctly. rodga.
|
|
//
|
|
ClusDiskScsiInitialize(DriverObject, 0, 0);
|
|
|
|
//
|
|
// Register for disk device notifications
|
|
// If we called ClusDiskScsiInitialize just above, we don't have to register for notification
|
|
// with PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES flag set (second parameter).
|
|
//
|
|
// Try setting second parm to see if we missed notifications.
|
|
|
|
status = IoRegisterPlugPlayNotification(EventCategoryDeviceInterfaceChange,
|
|
PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES,
|
|
(PVOID)&DiskClassGuid,
|
|
DriverObject,
|
|
ClusDiskDeviceChangeNotification,
|
|
deviceExtension,
|
|
&deviceExtension->DiskNotificationEntry);
|
|
if (!NT_SUCCESS(status)) {
|
|
RootDeviceObject = NULL;
|
|
ExDeleteResourceLite( &deviceExtension->DriveLayoutLock );
|
|
ExDeleteResourceLite( &deviceExtension->ReserveInfoLock );
|
|
IoDeleteDevice( rootDevice );
|
|
return status;
|
|
}
|
|
|
|
//
|
|
// Register for volume notifications
|
|
// If we called ClusDiskScsiInitialize just above, we don't have to register for notification
|
|
// with PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES flag set (second parameter).
|
|
//
|
|
// Try setting second parm to see if we missed notifications.
|
|
|
|
status = IoRegisterPlugPlayNotification(EventCategoryDeviceInterfaceChange,
|
|
PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES,
|
|
(PVOID)&VolumeClassGuid,
|
|
DriverObject,
|
|
ClusDiskVolumeChangeNotification,
|
|
deviceExtension,
|
|
&deviceExtension->VolumeNotificationEntry);
|
|
if (!NT_SUCCESS(status)) {
|
|
IoUnregisterPlugPlayNotification( &deviceExtension->DiskNotificationEntry );
|
|
RootDeviceObject = NULL;
|
|
ExDeleteResourceLite( &deviceExtension->DriveLayoutLock );
|
|
ExDeleteResourceLite( &deviceExtension->ReserveInfoLock );
|
|
IoDeleteDevice( rootDevice );
|
|
return status;
|
|
}
|
|
|
|
//
|
|
// Start the tick handler.
|
|
//
|
|
IoStartTimer( rootDevice );
|
|
|
|
#if defined(WMI_TRACING)
|
|
|
|
status = IoWMIRegistrationControl (rootDevice, WMIREG_ACTION_REGISTER);
|
|
if (!NT_SUCCESS(status)) {
|
|
ClusDiskPrint((1, "[ClusDisk] Failed to register with WMI %x.\n",status));
|
|
}
|
|
|
|
#endif // WMI_TRACING
|
|
|
|
return( STATUS_SUCCESS );
|
|
|
|
} // ClusDiskInitialize
|
|
|
|
|
|
|
|
NTSTATUS
|
|
ClusDiskPassThrough(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is a generic dispatch for all unsupported
|
|
major IRP types
|
|
|
|
Note that we don't have to worry about the RemoveLock as
|
|
we are simply passing I/O's to the next driver.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Supplies the device object.
|
|
|
|
Irp - Supplies the IO request packet.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
|
|
{
|
|
PCLUS_DEVICE_EXTENSION deviceExtension =
|
|
(PCLUS_DEVICE_EXTENSION) DeviceObject->DeviceExtension;
|
|
NTSTATUS status;
|
|
|
|
if ( deviceExtension->BusType == RootBus ) {
|
|
Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return(STATUS_INVALID_DEVICE_REQUEST);
|
|
}
|
|
|
|
//
|
|
// Make sure the device attach completed.
|
|
//
|
|
status = WaitForAttachCompletion( deviceExtension,
|
|
TRUE, // Wait
|
|
TRUE ); // Also check physical device
|
|
if ( !NT_SUCCESS( status ) ) {
|
|
Irp->IoStatus.Status = status;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return status;
|
|
}
|
|
|
|
IoSkipCurrentIrpStackLocation(Irp);
|
|
return IoCallDriver(deviceExtension->TargetDeviceObject, Irp);
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
ClusDiskPowerDispatch(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is the dispatch for the IRP_MJ_PNP_POWER.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Supplies the device object.
|
|
|
|
Irp - Supplies the IO request packet.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
|
|
{
|
|
PCLUS_DEVICE_EXTENSION deviceExtension =
|
|
(PCLUS_DEVICE_EXTENSION) DeviceObject->DeviceExtension;
|
|
PCLUS_DEVICE_EXTENSION physicalDisk =
|
|
deviceExtension->PhysicalDevice->DeviceExtension;
|
|
NTSTATUS status;
|
|
|
|
ClusDiskPrint(( 3,
|
|
"[ClusDisk] Processing Power IRP %p for device %p \n",
|
|
Irp,
|
|
DeviceObject ));
|
|
|
|
//
|
|
// Always call PoStartnextPowerIrp, even if we couldn't get the RemoveLock.
|
|
//
|
|
|
|
PoStartNextPowerIrp( Irp );
|
|
|
|
status = AcquireRemoveLock(&deviceExtension->RemoveLock, Irp);
|
|
if ( !NT_SUCCESS(status) ) {
|
|
Irp->IoStatus.Status = status;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return status;
|
|
}
|
|
|
|
status = AcquireRemoveLock(&physicalDisk->RemoveLock, Irp);
|
|
if ( !NT_SUCCESS(status) ) {
|
|
ReleaseRemoveLock(&deviceExtension->RemoveLock, Irp);
|
|
Irp->IoStatus.Status = status;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return status;
|
|
}
|
|
|
|
//
|
|
// Make sure the device attach completed.
|
|
//
|
|
status = WaitForAttachCompletion( deviceExtension,
|
|
TRUE, // Wait
|
|
TRUE ); // Also check physical device
|
|
if ( !NT_SUCCESS( status ) ) {
|
|
ReleaseRemoveLock(&physicalDisk->RemoveLock, Irp);
|
|
ReleaseRemoveLock(&deviceExtension->RemoveLock, Irp);
|
|
Irp->IoStatus.Status = status;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return status;
|
|
}
|
|
|
|
//
|
|
// Always send IRP_MJ_POWER request down the stack.
|
|
//
|
|
|
|
ReleaseRemoveLock(&physicalDisk->RemoveLock, Irp);
|
|
ReleaseRemoveLock(&deviceExtension->RemoveLock, Irp);
|
|
IoSkipCurrentIrpStackLocation(Irp);
|
|
return PoCallDriver(deviceExtension->TargetDeviceObject, Irp);
|
|
|
|
|
|
} // ClusDiskPowerDispatch
|
|
|
|
|
|
NTSTATUS
|
|
ClusDiskIrpCompletion(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID Context
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Forwarded IRP completion routine. Set an event and return
|
|
STATUS_MORE_PROCESSING_REQUIRED. Irp forwarder will wait on this
|
|
event and then re-complete the irp after cleaning up.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject is the device object of the WMI driver
|
|
Irp is the WMI irp that was just completed
|
|
Context is a PKEVENT that forwarder will wait on
|
|
|
|
Return Value:
|
|
|
|
STATUS_MORE_PORCESSING_REQUIRED
|
|
|
|
--*/
|
|
|
|
{
|
|
PKEVENT event = (PKEVENT) Context;
|
|
|
|
UNREFERENCED_PARAMETER(DeviceObject);
|
|
UNREFERENCED_PARAMETER(Irp);
|
|
|
|
//
|
|
// Don't need to release the RemoveLock as it is still held by the routine
|
|
// that set this completion routine and will be released after we set the
|
|
// event.
|
|
//
|
|
|
|
KeSetEvent( event, IO_NO_INCREMENT, FALSE );
|
|
|
|
return(STATUS_MORE_PROCESSING_REQUIRED);
|
|
|
|
} // ClusDiskIrpCompletion
|
|
|
|
|
|
|
|
NTSTATUS
|
|
ClusDiskPnpDispatch(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is the dispatch for the IRP_MJ_PNP.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Supplies the device object.
|
|
|
|
Irp - Supplies the IO request packet.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
|
|
{
|
|
PCLUS_DEVICE_EXTENSION deviceExtension =
|
|
(PCLUS_DEVICE_EXTENSION) DeviceObject->DeviceExtension;
|
|
NTSTATUS status;
|
|
PIO_STACK_LOCATION irpSp;
|
|
PDEVICE_OBJECT targetObject;
|
|
PDEVICE_LIST_ENTRY deviceEntry;
|
|
KIRQL irql;
|
|
|
|
// PAGED_CODE();
|
|
|
|
status = AcquireRemoveLock(&deviceExtension->RemoveLock, Irp);
|
|
if ( !NT_SUCCESS(status) ) {
|
|
Irp->IoStatus.Status = status;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return status;
|
|
}
|
|
|
|
irpSp = IoGetCurrentIrpStackLocation(Irp);
|
|
|
|
CDLOG( "ClusDiskPnpDispatch_Entry(%p): IrpMn %!pnpmn!", DeviceObject, irpSp->MinorFunction );
|
|
|
|
ClusDiskPrint(( 3,
|
|
"[ClusDisk] PNP IRP for devobj %p MinorFunction: %s (%lx) \n",
|
|
DeviceObject,
|
|
PnPMinorFunctionString( irpSp->MinorFunction ),
|
|
irpSp->MinorFunction ));
|
|
|
|
|
|
//
|
|
// If the driver unloads, the clusdisk0 control device could be removed.
|
|
//
|
|
if ( deviceExtension->BusType == RootBus ) {
|
|
ClusDiskPrint(( 1, "[ClusDisk] PNP IRP for root bus - failing \n" ));
|
|
ReleaseRemoveLock(&deviceExtension->RemoveLock, Irp);
|
|
Irp->IoStatus.Status = STATUS_DEVICE_BUSY;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return(STATUS_DEVICE_BUSY);
|
|
}
|
|
|
|
//
|
|
// Make sure the device attach completed.
|
|
//
|
|
status = WaitForAttachCompletion( deviceExtension,
|
|
TRUE, // Wait
|
|
FALSE ); // Don't check physical device
|
|
if ( !NT_SUCCESS( status ) ) {
|
|
ReleaseRemoveLock(&deviceExtension->RemoveLock, Irp);
|
|
Irp->IoStatus.Status = status;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return status;
|
|
}
|
|
|
|
//
|
|
// We should the following order on a remove request.
|
|
//
|
|
// 1. IRP_MN_QUERY_REMOVE_DEVICE
|
|
// Don't accept any new operations
|
|
// 2. IRP_MN_REMOVE_DEVICE if success on all drivers in stack
|
|
// Remove the device
|
|
// 3. IRP_MN_CANCEL_REMOVE_DEVICE if remove fails
|
|
// Resume activity
|
|
//
|
|
|
|
switch ( irpSp->MinorFunction ) {
|
|
|
|
case IRP_MN_QUERY_REMOVE_DEVICE:
|
|
ClusDiskPrint((1,
|
|
"[ClusDisk] QueryRemoveDevice PNP IRP on devobj %p \n",
|
|
DeviceObject));
|
|
break; // just pass it on
|
|
|
|
case IRP_MN_SURPRISE_REMOVAL: {
|
|
|
|
//
|
|
// For physical device, dismount everything.
|
|
//
|
|
|
|
if ( DeviceObject != deviceExtension->PhysicalDevice ||
|
|
DeviceObject == RootDeviceObject ) {
|
|
break;
|
|
}
|
|
|
|
#if 0
|
|
//
|
|
// Holding the RemoveLock here will result in deadlock. Referencing the
|
|
// device object will keep the device object and extension in place
|
|
// until the dismount completes.
|
|
//
|
|
|
|
//
|
|
// Acquire remove lock one more time so dismount code can run.
|
|
// If we can't get the lock, we won't dismount. The dismount code
|
|
// will release the lock.
|
|
//
|
|
|
|
status = AcquireRemoveLock( &deviceExtension->RemoveLock, deviceExtension );
|
|
if ( !NT_SUCCESS(status) ) {
|
|
// If we can't get the RemoveLock, skip this device.
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
#if CLUSTER_FREE_ASSERTS
|
|
DbgPrint("[ClusDisk] IRP_MN_SURPRISE_REMOVAL for %p \n", DeviceObject );
|
|
#endif
|
|
|
|
//
|
|
// Capture all file handles for this device.
|
|
//
|
|
|
|
ProcessDelayedWorkSynchronous( DeviceObject, ClusDiskpOpenFileHandles, NULL );
|
|
|
|
// Keep the device object around
|
|
ObReferenceObject( DeviceObject );
|
|
ClusDiskDismountVolumes( DeviceObject,
|
|
FALSE ); // Don't release RemoveLock (it is not held).
|
|
|
|
break; // pass it to the next driver
|
|
}
|
|
|
|
case IRP_MN_REMOVE_DEVICE: {
|
|
|
|
REPLACE_CONTEXT context;
|
|
|
|
ClusDiskPrint((1,
|
|
"[ClusDisk] RemoveDevice PNP IRP on devobj %p \n",
|
|
DeviceObject));
|
|
|
|
#if CLUSTER_FREE_ASSERTS
|
|
DbgPrint("[ClusDisk] IRP_MN_REMOVE_DEVICE for %p \n", DeviceObject );
|
|
#endif
|
|
|
|
//
|
|
// Flush all queued I/O.
|
|
//
|
|
|
|
ClusDiskCompletePendedIrps(deviceExtension,
|
|
NULL, // Will complete all IRPs
|
|
FALSE // Don't set the device state
|
|
);
|
|
|
|
//
|
|
// Wait for I/O to complete before removing the device.
|
|
//
|
|
|
|
ReleaseRemoveLockAndWait(&deviceExtension->RemoveLock, Irp);
|
|
|
|
// 2000/02/05: stevedz - Moved this code from the legacy unload routine.
|
|
|
|
if ( DeviceObject == RootDeviceObject ) {
|
|
|
|
IoStopTimer( DeviceObject );
|
|
|
|
IoUnregisterPlugPlayNotification( deviceExtension->DiskNotificationEntry );
|
|
IoUnregisterPlugPlayNotification( deviceExtension->VolumeNotificationEntry );
|
|
|
|
RootDeviceObject = NULL;
|
|
}
|
|
|
|
ACQUIRE_SHARED( &ClusDiskDeviceListLock );
|
|
|
|
// Release the device list entry for this device object
|
|
|
|
deviceEntry = ClusDiskDeviceList;
|
|
while ( deviceEntry ) {
|
|
if ( deviceEntry->DeviceObject == DeviceObject ) {
|
|
deviceEntry->FreePool = TRUE;
|
|
CleanupDeviceList( DeviceObject );
|
|
break;
|
|
}
|
|
deviceEntry = deviceEntry->Next;
|
|
}
|
|
|
|
targetObject = deviceExtension->TargetDeviceObject;
|
|
KeAcquireSpinLock(&ClusDiskSpinLock, &irql);
|
|
deviceExtension->Detached = TRUE;
|
|
deviceExtension->TargetDeviceObject = NULL;
|
|
KeReleaseSpinLock(&ClusDiskSpinLock, irql);
|
|
RELEASE_SHARED( &ClusDiskDeviceListLock );
|
|
|
|
context.DeviceExtension = deviceExtension;
|
|
context.NewValue = NULL; // clear the field
|
|
context.Flags = 0; // don't dismount
|
|
|
|
ProcessDelayedWorkSynchronous( DeviceObject, ClusDiskpReplaceHandleArray, &context );
|
|
|
|
//
|
|
// Free the cached drive layout (if any).
|
|
//
|
|
|
|
ACQUIRE_EXCLUSIVE( &deviceExtension->DriveLayoutLock );
|
|
|
|
if ( deviceExtension->DriveLayout ) {
|
|
ExFreePool( deviceExtension->DriveLayout );
|
|
deviceExtension->DriveLayout = NULL;
|
|
}
|
|
deviceExtension->DriveLayoutSize = 0;
|
|
|
|
RELEASE_EXCLUSIVE( &deviceExtension->DriveLayoutLock );
|
|
|
|
//
|
|
// [GorN] 10/05/1999
|
|
//
|
|
// The following lock acquisition is causing a deadlock as follows:
|
|
//
|
|
// Disk is being removed. Clustering detects that and starts dismounting of
|
|
// cluster disks, while it is doing that it acquires ClusDiskDeviceListLock in
|
|
// the shared mode. Processing a dismount request, FS reports Dismount PnP event,
|
|
// this gets blocked on PnP lock.
|
|
//
|
|
// At the same time, PnP is trying to deliver RemoveDevice, which gets blocked
|
|
// in clusdisk, when clusdisk is trying to acquire ClusDiskDeviceListLock in
|
|
// exclusive mode.
|
|
//
|
|
// [HACKHACK] It is better to defer the detaching / deletion to the worker thread
|
|
// which will be properly protected by exclusive lock
|
|
|
|
// ACQUIRE_EXCLUSIVE( &ClusDiskDeviceListLock );
|
|
|
|
ExDeleteResourceLite( &deviceExtension->DriveLayoutLock );
|
|
ExDeleteResourceLite( &deviceExtension->ReserveInfoLock );
|
|
IoDetachDevice( targetObject );
|
|
IoDeleteDevice( DeviceObject );
|
|
|
|
// RELEASE_EXCLUSIVE( &ClusDiskDeviceListLock );
|
|
|
|
CDLOG( "ClusDiskPnpDispatch: IoDeleteDevice DO %p refCount %d ", DeviceObject, DeviceObject->ReferenceCount );
|
|
|
|
// Don't release the RemoveLock as it was done just above.
|
|
|
|
IoSkipCurrentIrpStackLocation( Irp );
|
|
return( IoCallDriver( targetObject, Irp ) );
|
|
|
|
|
|
}
|
|
|
|
case IRP_MN_CANCEL_REMOVE_DEVICE:
|
|
ClusDiskPrint((1,
|
|
"[ClusDisk] CancelRemoveDevice PNP IRP on devobj %p \n",
|
|
DeviceObject));
|
|
break;
|
|
|
|
|
|
case IRP_MN_DEVICE_USAGE_NOTIFICATION: {
|
|
|
|
UNICODE_STRING availableName;
|
|
|
|
ClusDiskPrint((1,
|
|
"[ClusDisk] DeviceUsageNotification DevObj %p Type %08x InPath %08x \n",
|
|
DeviceObject,
|
|
irpSp->Parameters.UsageNotification.Type,
|
|
irpSp->Parameters.UsageNotification.InPath
|
|
));
|
|
|
|
//
|
|
// If we are adding one of the special files and the disk is clustered,
|
|
// then fail the request. We can't have these files on clustered disks.
|
|
// We will allow removal of the special files at any time (online or offline).
|
|
//
|
|
|
|
if ( irpSp->Parameters.UsageNotification.InPath &&
|
|
!deviceExtension->Detached ) {
|
|
|
|
ClusDiskPrint((1,
|
|
"[ClusDisk] DeviceUsageNotification - specified device is in cluster - failing request \n"
|
|
));
|
|
ReleaseRemoveLock(&deviceExtension->RemoveLock, Irp);
|
|
Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return (STATUS_INVALID_DEVICE_REQUEST);
|
|
}
|
|
|
|
switch ( irpSp->Parameters.UsageNotification.Type ) {
|
|
|
|
case DeviceUsageTypePaging: {
|
|
|
|
BOOLEAN setPagable;
|
|
|
|
//
|
|
// We need this event to synchonize access to the paging count.
|
|
//
|
|
|
|
status = KeWaitForSingleObject( &deviceExtension->PagingPathCountEvent,
|
|
Executive, KernelMode,
|
|
FALSE, NULL );
|
|
|
|
//
|
|
// If we are removing the last paging device, we need to set DO_POWER_PAGABLE
|
|
// bit here, and possible re-set it below on failure.
|
|
//
|
|
|
|
setPagable = FALSE;
|
|
|
|
if ( !irpSp->Parameters.UsageNotification.InPath &&
|
|
deviceExtension->PagingPathCount == 1 ) {
|
|
|
|
//
|
|
// We are removing the last paging file. We must have DO_POWER_PAGABLE bit
|
|
// set, but only if no one set the DO_POWER_INRUSH bit
|
|
//
|
|
|
|
|
|
if ( DeviceObject->Flags & DO_POWER_INRUSH ) {
|
|
ClusDiskPrint(( 2,
|
|
"[ClusDisk] Last paging file removed, but DO_POWER_INRUSH was already set, devobj %p \n",
|
|
DeviceObject ));
|
|
} else {
|
|
ClusDiskPrint(( 2,
|
|
"[ClusDisk] Last paging file removed, setting DO_POWER_INRUSH, devobj %p \n",
|
|
DeviceObject ));
|
|
DeviceObject->Flags |= DO_POWER_PAGABLE;
|
|
setPagable = TRUE;
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Forward the IRP to the drivers below before finishing handling the
|
|
// special cases.
|
|
//
|
|
|
|
status = ClusDiskForwardIrpSynchronous( DeviceObject, Irp );
|
|
|
|
//
|
|
// Now deal with the failure and success cases. Note that we are not allowed
|
|
// to fail the IRP once it is sent to the lower drivers.
|
|
//
|
|
|
|
if ( NT_SUCCESS(status) ) {
|
|
|
|
IoAdjustPagingPathCount(
|
|
&deviceExtension->PagingPathCount,
|
|
irpSp->Parameters.UsageNotification.InPath);
|
|
|
|
if ( irpSp->Parameters.UsageNotification.InPath ) {
|
|
if ( deviceExtension->PagingPathCount == 1 ) {
|
|
ClusDiskPrint(( 2,
|
|
"[ClusDisk] Clearing DO_POWER_PAGABLE, devobj %p \n",
|
|
DeviceObject ));
|
|
DeviceObject->Flags &= ~DO_POWER_PAGABLE;
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// Clean up the changes done above.
|
|
//
|
|
|
|
if ( TRUE == setPagable ) {
|
|
ClusDiskPrint(( 2,
|
|
"[ClusDisk] Clearing DO_POWER_PAGABLE due to IRP failure, devobj %p status %08x \n",
|
|
DeviceObject,
|
|
status ));
|
|
DeviceObject->Flags &= ~DO_POWER_PAGABLE;
|
|
setPagable = FALSE;
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Set the event so the next paging request can occur.
|
|
//
|
|
|
|
KeSetEvent( &deviceExtension->PagingPathCountEvent,
|
|
IO_NO_INCREMENT, FALSE );
|
|
break;
|
|
}
|
|
|
|
case DeviceUsageTypeHibernation: {
|
|
|
|
IoAdjustPagingPathCount( &deviceExtension->HibernationPathCount,
|
|
irpSp->Parameters.UsageNotification.InPath );
|
|
|
|
status = ClusDiskForwardIrpSynchronous( DeviceObject, Irp );
|
|
if ( !NT_SUCCESS(status) ) {
|
|
|
|
IoAdjustPagingPathCount( &deviceExtension->HibernationPathCount,
|
|
!irpSp->Parameters.UsageNotification.InPath );
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case DeviceUsageTypeDumpFile: {
|
|
|
|
IoAdjustPagingPathCount( &deviceExtension->DumpPathCount,
|
|
irpSp->Parameters.UsageNotification.InPath );
|
|
|
|
status = ClusDiskForwardIrpSynchronous( DeviceObject, Irp );
|
|
if ( !NT_SUCCESS(status) ) {
|
|
|
|
IoAdjustPagingPathCount( &deviceExtension->DumpPathCount,
|
|
!irpSp->Parameters.UsageNotification.InPath );
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
default: {
|
|
ClusDiskPrint(( 2,
|
|
"[ClusDisk] Unrecognized notification type, devobj %p notification %08x \n",
|
|
DeviceObject,
|
|
irpSp->Parameters.UsageNotification.Type ));
|
|
status = STATUS_INVALID_PARAMETER;
|
|
break;
|
|
}
|
|
|
|
|
|
}
|
|
|
|
//
|
|
// This debug print is outside of the synchonization, but that's OK.
|
|
//
|
|
|
|
ClusDiskPrint(( 3,
|
|
"[ClusDisk] PagingCount %08lx HibernationCount %08x DumpCount %08x\n",
|
|
deviceExtension->PagingPathCount,
|
|
deviceExtension->HibernationPathCount,
|
|
deviceExtension->DumpPathCount ));
|
|
|
|
//
|
|
// We need this event to synchonize access to the paging count.
|
|
//
|
|
|
|
status = KeWaitForSingleObject( &deviceExtension->PagingPathCountEvent,
|
|
Executive, KernelMode,
|
|
FALSE, NULL );
|
|
|
|
//
|
|
// If the device is not currently clustered and the paging count is zero,
|
|
// add the disk to Parameters\AvailableDisks list. Otherwise, remove this
|
|
// disk from the list. We can only get to this code if we already know
|
|
// the disk is not clustered (i.e. Detached is TRUE).
|
|
//
|
|
|
|
ASSERT( deviceExtension->Detached );
|
|
|
|
//
|
|
// Allocate buffer for AvailableDisks registry key.
|
|
//
|
|
|
|
status = ClusDiskInitRegistryString( &availableName,
|
|
CLUSDISK_AVAILABLE_DISKS_KEYNAME,
|
|
wcslen(CLUSDISK_AVAILABLE_DISKS_KEYNAME) );
|
|
|
|
if ( NT_SUCCESS(status) ) {
|
|
|
|
if ( 0 == deviceExtension->PagingPathCount &&
|
|
0 == deviceExtension->HibernationPathCount &&
|
|
0 == deviceExtension->DumpPathCount ) {
|
|
|
|
//
|
|
// Create the signature key under Parameters\AvailableDisks
|
|
//
|
|
|
|
ClusDiskAddSignature( &availableName,
|
|
deviceExtension->Signature,
|
|
TRUE );
|
|
|
|
} else {
|
|
|
|
//
|
|
// Delete the signature key under Parameters\AvailableDisks.
|
|
//
|
|
|
|
ClusDiskDeleteSignature( &availableName,
|
|
deviceExtension->Signature );
|
|
|
|
}
|
|
|
|
FREE_DEVICE_NAME_BUFFER( availableName );
|
|
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// Set the event so the next paging request can occur.
|
|
//
|
|
|
|
KeSetEvent( &deviceExtension->PagingPathCountEvent,
|
|
IO_NO_INCREMENT, FALSE );
|
|
|
|
|
|
//
|
|
// Complete the IRP. This IRP was already sent to the lower drivers.
|
|
//
|
|
|
|
Irp->IoStatus.Status = status;
|
|
ReleaseRemoveLock(&deviceExtension->RemoveLock, Irp);
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return status;
|
|
break;
|
|
|
|
} // IRP_MN_DEVICE_USAGE_NOTIFICATION
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
CDLOG( "ClusDiskPnpDispatch: Exit, DO %p", DeviceObject );
|
|
//
|
|
// We don't recognize this IRP - simply pass it on to next guy.
|
|
//
|
|
|
|
ReleaseRemoveLock(&deviceExtension->RemoveLock, Irp);
|
|
|
|
IoSkipCurrentIrpStackLocation(Irp);
|
|
return (IoCallDriver( deviceExtension->TargetDeviceObject,
|
|
Irp ) );
|
|
|
|
} // ClusDiskPnpDispatch
|
|
|
|
|
|
|
|
NTSTATUS
|
|
DriverEntry(
|
|
IN PDRIVER_OBJECT DriverObject,
|
|
IN PUNICODE_STRING RegistryPath
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the routine called by the system to initialize the disk
|
|
performance driver. The driver object is set up and then the
|
|
driver calls ClusDiskxxxInitialize to attach to the boot devices.
|
|
|
|
Arguments:
|
|
|
|
DriverObject - The Cluster Disk driver object.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
ULONG i;
|
|
|
|
ClusDiskRegistryPath.Buffer = NULL;
|
|
|
|
#if CLUSDISK_DEBUG
|
|
if ( !ClusDiskGood ) {
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto FnExit;
|
|
}
|
|
#endif
|
|
|
|
#if ( CLUSTER_FREE_ASSERTS )
|
|
DbgPrint( "[Clusdisk]: CLUSTER_FREE_ASSERTS defined \n");
|
|
#endif
|
|
|
|
WPP_INIT_TRACING(DriverObject, RegistryPath);
|
|
|
|
ClusDiskSystemProcess = (PKPROCESS) IoGetCurrentProcess();
|
|
|
|
//
|
|
// Set up the device driver entry points.
|
|
//
|
|
|
|
for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++) {
|
|
DriverObject->MajorFunction[i] = ClusDiskPassThrough;
|
|
}
|
|
|
|
DriverObject->MajorFunction[IRP_MJ_CREATE] = ClusDiskCreate;
|
|
DriverObject->MajorFunction[IRP_MJ_CLOSE] = ClusDiskClose;
|
|
DriverObject->MajorFunction[IRP_MJ_CLEANUP] = ClusDiskCleanup;
|
|
DriverObject->MajorFunction[IRP_MJ_READ] = ClusDiskRead;
|
|
DriverObject->MajorFunction[IRP_MJ_WRITE] = ClusDiskWrite;
|
|
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = ClusDiskDeviceControl;
|
|
DriverObject->MajorFunction[IRP_MJ_SHUTDOWN] = ClusDiskShutdownFlush;
|
|
DriverObject->MajorFunction[IRP_MJ_FLUSH_BUFFERS] = ClusDiskShutdownFlush;
|
|
DriverObject->MajorFunction[IRP_MJ_POWER] = ClusDiskPowerDispatch;
|
|
DriverObject->MajorFunction[IRP_MJ_PNP] = ClusDiskPnpDispatch;
|
|
|
|
//
|
|
// Driver is unloadable. (Not for now... the problem is that the driver
|
|
// can be called at the unload entrypoint even with open file handles.
|
|
// Until this is fixed, disable the unload.)
|
|
//
|
|
// NTRAID#72826-2000/02/05-stevedz ClusDisk.sys Unload routine not supported.
|
|
//
|
|
// This was the closest bug to this problem I could find. Until this driver
|
|
// is fully PnP or until the reference count bug is fixed, this driver cannot
|
|
// support unload.
|
|
//
|
|
// DriverObject->DriverUnload = ClusDiskUnload;
|
|
|
|
//
|
|
// make a copy of RegistryPath, appending the Parameters subkey
|
|
//
|
|
|
|
ClusDiskRegistryPath.MaximumLength = RegistryPath->MaximumLength +
|
|
sizeof( CLUSDISK_PARAMETERS_KEYNAME ) +
|
|
sizeof( UNICODE_NULL );
|
|
ClusDiskRegistryPath.Buffer = ExAllocatePool( NonPagedPool,
|
|
ClusDiskRegistryPath.MaximumLength );
|
|
|
|
if ( ClusDiskRegistryPath.Buffer == NULL ) {
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto FnExit;
|
|
}
|
|
|
|
RtlCopyUnicodeString( &ClusDiskRegistryPath, RegistryPath );
|
|
RtlAppendUnicodeToString( &ClusDiskRegistryPath, CLUSDISK_PARAMETERS_KEYNAME );
|
|
ClusDiskRegistryPath.Buffer[ ClusDiskRegistryPath.Length / sizeof( WCHAR )] = UNICODE_NULL;
|
|
|
|
InitializeListHead( &ReplaceRoutineListHead );
|
|
KeInitializeSpinLock( &ReplaceRoutineSpinLock );
|
|
|
|
status = ArbitrationInitialize();
|
|
if( !NT_SUCCESS(status) ) {
|
|
ClusDiskPrint((1,
|
|
"[ClusDisk] ArbitrationInitialize failed, error: %08X\n",
|
|
status));
|
|
|
|
goto FnExit;
|
|
}
|
|
|
|
//
|
|
// Find the bus on which the system disk is loaded.
|
|
//
|
|
|
|
status = ClusDiskInitialize( DriverObject );
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
ClusDiskPrint((1,
|
|
"[ClusDisk] Failed to initialize! %08X\n",
|
|
status));
|
|
}
|
|
|
|
FnExit:
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
|
|
if ( ClusDiskRegistryPath.Buffer ) {
|
|
ExFreePool( ClusDiskRegistryPath.Buffer );
|
|
ClusDiskRegistryPath.Buffer = NULL;
|
|
}
|
|
|
|
if ( gArbitrationBuffer ) {
|
|
ExFreePool( gArbitrationBuffer );
|
|
gArbitrationBuffer = NULL;
|
|
}
|
|
}
|
|
|
|
return(status);
|
|
|
|
} // DriverEntry
|
|
|
|
|
|
|
|
VOID
|
|
ClusDiskTickHandler(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PVOID Context
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Timer routine that handles reservations. Walk all device objects, looking
|
|
for active timers.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Supplies a pointer to the root device object.
|
|
|
|
Context - Not used.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
Notes:
|
|
|
|
We can't process the reservations at DPC level because reservation
|
|
IOCTL's invoke paged code in the SCSI subsystem.
|
|
|
|
--*/
|
|
|
|
{
|
|
PCLUS_DEVICE_EXTENSION deviceExtension;
|
|
KIRQL irql;
|
|
PDEVICE_OBJECT deviceObject = DeviceObject->DriverObject->DeviceObject;
|
|
LARGE_INTEGER currentTime;
|
|
LARGE_INTEGER deltaTime;
|
|
BOOLEAN arbitrationTickIsCalled = FALSE;
|
|
NTSTATUS status;
|
|
|
|
CDLOGF(TICK,"ClusDiskTickHandler: Entry DO %p", DeviceObject );
|
|
|
|
//
|
|
// Globally Synchronize
|
|
//
|
|
KeAcquireSpinLock(&ClusDiskSpinLock, &irql);
|
|
|
|
if ( ClusDiskRescan && !ClusDiskRescanBusy && ClusDiskRescanRetry ) {
|
|
--ClusDiskRescanRetry;
|
|
ClusDiskRescanBusy = TRUE;
|
|
ExQueueWorkItem(&ClusDiskRescanWorkItem,
|
|
CriticalWorkQueue );
|
|
}
|
|
|
|
CDLOGF(TICK,"ClusDiskTickHandler: SpinLockAcquired DO %p", DeviceObject );
|
|
|
|
//
|
|
// Loop through all device objects looking for timeouts...
|
|
//
|
|
while ( deviceObject ) {
|
|
deviceExtension = deviceObject->DeviceExtension;
|
|
|
|
//
|
|
// If we have an attached partition0 device object (with a
|
|
// reserve irp) that is is online and has a timer going.
|
|
//
|
|
if ( !deviceExtension->Detached &&
|
|
deviceExtension->PerformReserves &&
|
|
(deviceExtension->ReserveTimer != 0) ) {
|
|
|
|
//
|
|
// Countdown to next reservation.
|
|
//
|
|
|
|
KeQuerySystemTime( ¤tTime );
|
|
deltaTime.QuadPart = ( currentTime.QuadPart - deviceExtension->LastReserveStart.QuadPart ) / 10000;
|
|
|
|
#if 0
|
|
ClusDiskPrint((
|
|
1,
|
|
"[ClusDisk] Signature %08X, msec since last reserve = %u\n",
|
|
deviceExtension->Signature,
|
|
deltaTime.LowPart ));
|
|
#endif
|
|
|
|
if ( deltaTime.LowPart >= ((RESERVE_TIMER * 1000) - 500) ) {
|
|
|
|
#if 0 // we no longer rely strictly on the timer.
|
|
if ( --deviceExtension->ReserveTimer == 0 )
|
|
//
|
|
// Reset next timeout
|
|
//
|
|
deviceExtension->ReserveTimer = RESERVE_TIMER;
|
|
#endif
|
|
if (!arbitrationTickIsCalled) {
|
|
ArbitrationTick();
|
|
arbitrationTickIsCalled = TRUE;
|
|
}
|
|
|
|
CDLOGF(TICK,"ClusDiskTickHandler: DeltaTime DO %p %!delta!",
|
|
deviceObject, // LOGPTR
|
|
deltaTime.QuadPart ); // LOGULONG
|
|
|
|
//
|
|
// Check if worker thread still busy from last timeout.
|
|
//
|
|
if ( !deviceExtension->TimerBusy ) {
|
|
|
|
//
|
|
// Acquire the RemoveLock here and free it when the reserve code completes.
|
|
//
|
|
|
|
status = AcquireRemoveLock(&deviceExtension->RemoveLock, ClusDiskReservationWorker);
|
|
if ( !NT_SUCCESS(status) ) {
|
|
|
|
//
|
|
// Failed to get the RemoveLock for this device, go on to the next one.
|
|
//
|
|
deviceObject = deviceObject->NextDevice;
|
|
continue;
|
|
}
|
|
|
|
if ( deviceExtension->ReserveCount > 1 ) {
|
|
ClusDiskPrint(( 1,
|
|
"[ClusDisk] DO %p Signature %08X ReserveCount = %u \n",
|
|
deviceObject,
|
|
deviceExtension->Signature,
|
|
deviceExtension->ReserveCount ));
|
|
}
|
|
|
|
//
|
|
// Reset time since last reserve.
|
|
//
|
|
deviceExtension->LastReserveStart.QuadPart = currentTime.QuadPart;
|
|
deviceExtension->TimerBusy = TRUE;
|
|
|
|
ClusDiskPrint(( 4,
|
|
"[ClusDisk] DO %p Signature %08X, QueueWorkItem \n",
|
|
deviceObject,
|
|
deviceExtension->Signature ));
|
|
|
|
|
|
CDLOGF(TICK,"ClusDiskTickHandler: QueueWorkItem DO %p",
|
|
deviceObject );
|
|
|
|
ExQueueWorkItem(&deviceExtension->WorkItem,
|
|
CriticalWorkQueue );
|
|
} else {
|
|
|
|
CDLOGF(TICK,"ClusDiskTickHandler: TimerBusy set, skip QueueWorkItem DO %p DiskNo %u ",
|
|
deviceObject,
|
|
deviceExtension->DiskNumber );
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
//
|
|
// Walk all device objects.
|
|
//
|
|
deviceObject = deviceObject->NextDevice;
|
|
}
|
|
|
|
KeReleaseSpinLock(&ClusDiskSpinLock, irql);
|
|
|
|
} // ClusDiskTickHandler
|
|
|
|
|
|
|
|
VOID
|
|
ClusDiskReservationWorker(
|
|
IN PCLUS_DEVICE_EXTENSION DeviceExtension
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Reservation timeout worker routine. This worker queue routine
|
|
attempts a reservation on a cluster device.
|
|
|
|
The RemoveLock for this device (the one owning the device extension)
|
|
must be acquired before this routine runs.
|
|
|
|
Arguments:
|
|
|
|
DeviceExtension - The device extension for the device to reserve.
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
Notes:
|
|
|
|
The reservations must be handled here, because we can't handle them
|
|
at DPC level.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
KIRQL irql;
|
|
PLIST_ENTRY listEntry;
|
|
PIRP irp;
|
|
LARGE_INTEGER currentTime;
|
|
LARGE_INTEGER timeDelta;
|
|
LARGE_INTEGER startReserveTime;
|
|
|
|
CDLOGF(TICK,"ClusDiskReservationWorker: Entry DO %p", DeviceExtension->DeviceObject );
|
|
|
|
//
|
|
// If ReserveTimer is cleared, we should not do reservation on the device.
|
|
//
|
|
|
|
if ( RootDeviceObject == NULL || DeviceExtension->ReserveTimer == 0 ) {
|
|
|
|
goto FnExit;
|
|
}
|
|
|
|
#if 0 // Very noisy...
|
|
// Use only for really intense debugging....
|
|
|
|
ClusDiskPrint(( 3,
|
|
"[ClusDisk] Reserving: Sig %08X DevObj %p \n",
|
|
DeviceExtension->Signature,
|
|
DeviceExtension->DeviceObject ));
|
|
#endif
|
|
|
|
//
|
|
// The reserve and arbitration write are asynchronous now - don't wait for them
|
|
// to complete.
|
|
//
|
|
|
|
status = ReserveScsiDevice( DeviceExtension, NULL );
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
|
|
KeQuerySystemTime( ¤tTime );
|
|
ClusDiskPrint((
|
|
1,
|
|
"[ClusDisk] We lost our reservation for Signature %08X\n",
|
|
DeviceExtension->Signature));
|
|
timeDelta.QuadPart = ( currentTime.QuadPart - DeviceExtension->LastReserveEnd.QuadPart ) / 10000;
|
|
|
|
CDLOGF(RESERVE,"ClusDiskReservationWorker: LostReserve DO %p delta %!u! ms status %!status!",
|
|
DeviceExtension->DeviceObject,
|
|
timeDelta.LowPart,
|
|
status);
|
|
|
|
ClusDiskPrint((
|
|
1,
|
|
"[ClusDisk] Milliseconds since last reserve = %u, on Signature %08X\n",
|
|
timeDelta.LowPart,
|
|
DeviceExtension->Signature ));
|
|
|
|
OFFLINE_DISK( DeviceExtension );
|
|
|
|
IoAcquireCancelSpinLock( &irql );
|
|
KeAcquireSpinLockAtDpcLevel(&ClusDiskSpinLock);
|
|
DeviceExtension->ReserveTimer = 0;
|
|
DeviceExtension->ReserveFailure = status;
|
|
|
|
//
|
|
// Signal all waiting Irp's
|
|
//
|
|
while ( !IsListEmpty(&DeviceExtension->WaitingIoctls) ) {
|
|
listEntry = RemoveHeadList(&DeviceExtension->WaitingIoctls);
|
|
irp = CONTAINING_RECORD( listEntry,
|
|
IRP,
|
|
Tail.Overlay.ListEntry );
|
|
//irp->IoStatus.Status = status;
|
|
//IoCompleteRequest(irp, IO_NO_INCREMENT);
|
|
ClusDiskCompletePendingRequest(irp, status, DeviceExtension);
|
|
}
|
|
|
|
KeReleaseSpinLockFromDpcLevel(&ClusDiskSpinLock);
|
|
IoReleaseCancelSpinLock( irql );
|
|
} else {
|
|
//
|
|
// Arbitration write is now done after successful reservation. The reserve won't be
|
|
// stalled by a write (and a request sense).
|
|
//
|
|
|
|
ArbitrationWrite( DeviceExtension );
|
|
}
|
|
|
|
FnExit:
|
|
|
|
//
|
|
// Make sure TimerBusy is cleared. Otherwise, we will never send periodic
|
|
// reserves again!
|
|
//
|
|
|
|
DeviceExtension->TimerBusy = FALSE;
|
|
|
|
ReleaseRemoveLock(&DeviceExtension->RemoveLock, ClusDiskReservationWorker);
|
|
|
|
CDLOGF(TICK,"ClusDiskReservationWorker: Exit DO %p", DeviceExtension->DeviceObject );
|
|
|
|
return;
|
|
|
|
} // ClusDiskReservationWorker
|
|
|
|
|
|
|
|
NTSTATUS
|
|
ClusDiskRescanWorker(
|
|
IN PVOID Context
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Context - input context - not used.
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
if ( !RootDeviceObject ) {
|
|
return(STATUS_SUCCESS);
|
|
}
|
|
ClusDiskRescanBusy = FALSE;
|
|
ClusDiskScsiInitialize(RootDeviceObject->DriverObject, ClusDiskNextDisk, 1);
|
|
|
|
return(STATUS_SUCCESS);
|
|
|
|
} // ClusDiskRescanWorker
|
|
|
|
|
|
NTSTATUS
|
|
OfflineVolume(
|
|
IN ULONG DiskNumber,
|
|
IN ULONG PartitionNumber,
|
|
IN BOOLEAN ForceOffline
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Send offline IOCTL to the volume indicated by the disk number and
|
|
partition number.
|
|
|
|
If ForceOffline = TRUE
|
|
Send offline IOCTL without checking whether snapshot will be deleted.
|
|
|
|
If ForceOffline = FALSE
|
|
Check whether snapshot will be deleted when offline is sent. If
|
|
snapshot will be deleted, do not send the offline IOCTL.
|
|
|
|
Note that if there is no snapshot on the volume, IOCTL_VOLSNAP_QUERY_OFFLINE
|
|
will fail. The routine needs to be called again with ForceOffline
|
|
set to TRUE to send the volume offline.
|
|
|
|
Arguments:
|
|
|
|
DiskNumber - disk number for the volume to be offlined.
|
|
|
|
PartitionNumber - partition number for the volume to be offlined.
|
|
|
|
ForceOffline - controls whether snapshot deletion check will be made.
|
|
TRUE - don't check for snapshot deletion.
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS - one or more volumes offlined.
|
|
|
|
--*/
|
|
{
|
|
PDEVICE_OBJECT deviceObject;
|
|
PFILE_OBJECT fileObject;
|
|
PIRP irp;
|
|
|
|
NTSTATUS status = STATUS_UNSUCCESSFUL;
|
|
|
|
KEVENT event;
|
|
IO_STATUS_BLOCK ioStatus;
|
|
|
|
UNICODE_STRING ntUnicodeString;
|
|
|
|
PWCHAR ntDeviceName = NULL;
|
|
|
|
if ( PASSIVE_LEVEL != KeGetCurrentIrql() ) {
|
|
ClusDiskPrint(( 1,
|
|
"[ClusDisk] OfflineVolume: Running at invalid IRQL \n" ));
|
|
CDLOG( "OfflineVolume: Running at invalid IRQL \n" );
|
|
ASSERT(FALSE);
|
|
goto FnExit;
|
|
}
|
|
|
|
//
|
|
// Get the device object represented by the disk and partition numbers.
|
|
//
|
|
|
|
ntDeviceName = ExAllocatePool( NonPagedPool, MAX_PARTITION_NAME_LENGTH * sizeof(WCHAR) );
|
|
|
|
if ( !ntDeviceName ) {
|
|
CDLOG( "OfflineVolume: Failed to allocate device name buffer \n" );
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto FnExit;
|
|
}
|
|
|
|
if ( FAILED( StringCchPrintfW( ntDeviceName,
|
|
MAX_PARTITION_NAME_LENGTH,
|
|
DEVICE_PARTITION_NAME,
|
|
DiskNumber,
|
|
PartitionNumber ) ) ) {
|
|
CDLOG( "OfflineVolume: Failed to create device name for disk %u partition %u \n",
|
|
DiskNumber,
|
|
PartitionNumber );
|
|
goto FnExit;
|
|
}
|
|
|
|
RtlInitUnicodeString( &ntUnicodeString, ntDeviceName );
|
|
|
|
status = IoGetDeviceObjectPointer( &ntUnicodeString,
|
|
FILE_READ_ATTRIBUTES,
|
|
&fileObject,
|
|
&deviceObject );
|
|
if ( !NT_SUCCESS(status) ) {
|
|
ClusDiskPrint(( 1,
|
|
"[ClusDisk] OfflineVolume: Get devobj pointer failed for %ws, status %08X \n",
|
|
ntDeviceName,
|
|
status ));
|
|
CDLOG( "OfflineVolume: Get devobj pointer failed for %ws, status %08X \n",
|
|
ntDeviceName,
|
|
status );
|
|
|
|
goto FnExit;
|
|
}
|
|
|
|
deviceObject = IoGetAttachedDeviceReference( fileObject->DeviceObject );
|
|
|
|
//
|
|
// If caller did not specify to force the offline request, we have to
|
|
// check whether the offline will cause a snapshot deletion.
|
|
//
|
|
// If IOCTL_VOLSNAP_QUERY_OFFLINE succeeds, we can safely offline the
|
|
// volume.
|
|
//
|
|
// If IOCTL_VOLSNAP_QUERY_OFFLINE fails, then one of these conditions
|
|
// exists:
|
|
// - a snapshot will be deleted when the offline occurs
|
|
// - the IOCTL is not supported by third party driver
|
|
// - some unexpected error occured
|
|
//
|
|
// In the failure case, if ForceOffline = FALSE, we don't offline the
|
|
// volume now because this routine can be called again with ForceVolume = TRUE
|
|
// to make the offline work without the snapshot query.
|
|
//
|
|
|
|
if ( !ForceOffline ) {
|
|
|
|
KeInitializeEvent( &event, NotificationEvent, FALSE );
|
|
|
|
//
|
|
// If this IOCTL succeeds, then we can safely offline this volume.
|
|
//
|
|
|
|
irp = IoBuildDeviceIoControlRequest( IOCTL_VOLSNAP_QUERY_OFFLINE,
|
|
deviceObject,
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
0,
|
|
FALSE,
|
|
&event,
|
|
&ioStatus );
|
|
|
|
if ( !irp ) {
|
|
ObDereferenceObject( deviceObject );
|
|
ObDereferenceObject( fileObject );
|
|
|
|
ClusDiskPrint(( 1,
|
|
"[ClusDisk] OfflineVolume: Build QueryOffline IRP failed for disk %u partition %u, status %08X \n",
|
|
DiskNumber,
|
|
PartitionNumber,
|
|
status ));
|
|
CDLOG( "OfflineVolume: Build QueryOffline IRP failed for disk %u partition %u, status %08X \n",
|
|
DiskNumber,
|
|
PartitionNumber,
|
|
status );
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto FnExit;
|
|
}
|
|
|
|
status = IoCallDriver( deviceObject, irp );
|
|
|
|
if ( STATUS_PENDING == status ) {
|
|
KeWaitForSingleObject( &event, Executive, KernelMode, FALSE, NULL );
|
|
status = ioStatus.Status;
|
|
}
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
|
|
ObDereferenceObject( deviceObject );
|
|
ObDereferenceObject( fileObject );
|
|
|
|
ClusDiskPrint(( 1,
|
|
"[ClusDisk] OfflineVolume: QueryOffline IRP failed for disk %u partition %u, status %08X - skip offline \n",
|
|
DiskNumber,
|
|
PartitionNumber,
|
|
status ));
|
|
CDLOG( "OfflineVolume: QueryOffline IRP failed for disk %u partition %u, status %08X - skip offline \n",
|
|
DiskNumber,
|
|
PartitionNumber,
|
|
status );
|
|
goto FnExit;
|
|
}
|
|
|
|
//
|
|
// Fall through if the IOCTL worked. This means it is safe to offline this
|
|
// volume without snapshot deletion.
|
|
//
|
|
}
|
|
|
|
status = SendFtdiskIoctlSync( deviceObject,
|
|
DiskNumber,
|
|
PartitionNumber,
|
|
IOCTL_VOLUME_OFFLINE );
|
|
|
|
ObDereferenceObject( deviceObject );
|
|
ObDereferenceObject( fileObject );
|
|
|
|
FnExit:
|
|
|
|
if ( ntDeviceName ) {
|
|
ExFreePool( ntDeviceName );
|
|
}
|
|
|
|
return status;
|
|
|
|
} // OfflineVolume
|
|
|
|
|
|
NTSTATUS
|
|
OfflineVolumeList(
|
|
IN POFFLINE_ENTRY OfflineList
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Send volume IOCTL to all volumes in the list. Try to preserve snapshots
|
|
by first checking whether offline would cause a snapshot deletion. If
|
|
volume can be offlined without snapshot deletion, then do it. If offline
|
|
will cause snapshot deletion, skip this volume and try others in the list.
|
|
|
|
Multiple passes through the offline list will be made. When a pass through
|
|
the list occurs and no volumes are offlined, we stop processing the list.
|
|
At that point, we make one more pass through the list and force a volume
|
|
offline to any volume not yet offline.
|
|
|
|
Arguments:
|
|
|
|
OfflineList - Linked list representing all volumes to be offlined.
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS - one or more volumes offlined.
|
|
|
|
--*/
|
|
{
|
|
POFFLINE_ENTRY entry;
|
|
|
|
ULONG volumeOfflineCount;
|
|
ULONG totalOfflined = 0;
|
|
|
|
NTSTATUS status = STATUS_UNSUCCESSFUL;
|
|
|
|
if ( !OfflineList ) {
|
|
status = STATUS_SUCCESS;
|
|
goto FnExit;
|
|
}
|
|
|
|
ClusDiskPrint(( 3,
|
|
"[ClusDisk] OfflineVolumeList: First pass through offline list started \n" ));
|
|
CDLOG( "OfflineVolumeList: First pass through offline list started \n" );
|
|
|
|
entry = OfflineList;
|
|
|
|
//
|
|
// Keep walking through the offline list and checking
|
|
// whether we can offline a volume without causing snapshot
|
|
// deletion. If we walk through the entire list without
|
|
// offlining at least one volume, we are finished.
|
|
//
|
|
|
|
while ( TRUE ) {
|
|
|
|
entry = OfflineList;
|
|
volumeOfflineCount = 0;
|
|
|
|
while ( entry ) {
|
|
|
|
if ( !entry->OfflineSent ) {
|
|
|
|
status = OfflineVolume( entry->DiskNumber,
|
|
entry->PartitionNumber,
|
|
FALSE ); // Offline only if safe
|
|
|
|
if ( NT_SUCCESS(status) ) {
|
|
entry->OfflineSent = TRUE;
|
|
volumeOfflineCount++;
|
|
}
|
|
}
|
|
|
|
entry = entry->Next;
|
|
}
|
|
|
|
totalOfflined += volumeOfflineCount;
|
|
|
|
//
|
|
// If we didn't offline any volumes, then we need to stop
|
|
// processing.
|
|
//
|
|
|
|
if ( 0 == volumeOfflineCount ) {
|
|
ClusDiskPrint(( 3,
|
|
"[ClusDisk] OfflineVolumeList: First pass through offline list completed \n" ));
|
|
CDLOG( "OfflineVolumeList: First pass through offline list completed \n" );
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Walk through the list one more time and force offline anything not yet
|
|
// offlined.
|
|
//
|
|
|
|
ClusDiskPrint(( 3,
|
|
"[ClusDisk] OfflineVolumeList: Second pass through offline list started \n" ));
|
|
CDLOG( "OfflineVolumeList: Second pass through offline list started \n" );
|
|
|
|
entry = OfflineList;
|
|
|
|
while ( entry ) {
|
|
|
|
if ( !entry->OfflineSent ) {
|
|
|
|
status = OfflineVolume( entry->DiskNumber,
|
|
entry->PartitionNumber,
|
|
TRUE ); // Force offline
|
|
|
|
if ( NT_SUCCESS(status) ) {
|
|
entry->OfflineSent = TRUE;
|
|
totalOfflined++;
|
|
}
|
|
}
|
|
|
|
entry = entry->Next;
|
|
}
|
|
|
|
ClusDiskPrint(( 3,
|
|
"[ClusDisk] OfflineVolumeList: Second pass through offline list completed, %u volumes offlined \n",
|
|
totalOfflined ));
|
|
CDLOG( "OfflineVolumeList: Second pass through offline list completed, %u volumes offlined \n",
|
|
totalOfflined );
|
|
|
|
if ( totalOfflined ) {
|
|
status = STATUS_SUCCESS;
|
|
} else {
|
|
status = STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
FnExit:
|
|
|
|
return status;
|
|
|
|
} // OfflineVolumeList
|
|
|
|
|
|
NTSTATUS
|
|
AddVolumesToOfflineList(
|
|
IN PCLUS_DEVICE_EXTENSION DeviceExtension,
|
|
IN OUT POFFLINE_ENTRY *OfflineList
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Add all volumes for the specified physical disk to the offline
|
|
list. Updates the OfflineList with all volumes for this disk.
|
|
|
|
Caller is responsible to free this storage.
|
|
|
|
Arguments:
|
|
|
|
DeviceExtension - Device extension for a physical disk (partition 0).
|
|
|
|
OfflineList - Linked list representing all volumes to be offlined.
|
|
This list is for the current disk as well as others.
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS - one or more volumes added to the master list.
|
|
|
|
--*/
|
|
{
|
|
PDRIVE_LAYOUT_INFORMATION_EX driveLayoutInfo = NULL;
|
|
PPARTITION_INFORMATION_EX partitionInfo;
|
|
|
|
POFFLINE_ENTRY list = *OfflineList;
|
|
POFFLINE_ENTRY nextEntry;
|
|
|
|
ULONG partIndex;
|
|
|
|
NTSTATUS status;
|
|
|
|
status = GetDriveLayout( DeviceExtension->PhysicalDevice,
|
|
&driveLayoutInfo,
|
|
FALSE,
|
|
FALSE );
|
|
|
|
if ( !NT_SUCCESS(status) || !driveLayoutInfo ) {
|
|
ClusDiskPrint(( 1,
|
|
"[ClusDisk] AddVolumesToOfflineList: Failed to read partition info, status %08X \n",
|
|
status ));
|
|
CDLOG( "AddVolumesToOfflineList: Failed to read partition info, status %08X \n",
|
|
status );
|
|
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto FnExit;
|
|
}
|
|
|
|
for ( partIndex = 0;
|
|
partIndex < driveLayoutInfo->PartitionCount;
|
|
partIndex++ ) {
|
|
|
|
partitionInfo = &driveLayoutInfo->PartitionEntry[partIndex];
|
|
|
|
//
|
|
// First make sure this is a valid partition.
|
|
//
|
|
|
|
if ( 0 == partitionInfo->PartitionNumber ) {
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Allocate offline list entry, fill it in, and link it to the master
|
|
// list.
|
|
//
|
|
|
|
nextEntry = ExAllocatePool( NonPagedPool, sizeof(OFFLINE_ENTRY) );
|
|
|
|
if ( !nextEntry ) {
|
|
continue;
|
|
}
|
|
|
|
ClusDiskPrint(( 1,
|
|
"[ClusDisk] AddVolumesToOfflineList: Add disk %u partition %u to offline list \n",
|
|
DeviceExtension->DiskNumber,
|
|
partitionInfo->PartitionNumber ));
|
|
CDLOG( "AddVolumesToOfflineList: Add disk %u partition %u to offline list \n",
|
|
DeviceExtension->DiskNumber,
|
|
partitionInfo->PartitionNumber );
|
|
|
|
nextEntry->DiskNumber = DeviceExtension->DiskNumber;
|
|
nextEntry->PartitionNumber = partitionInfo->PartitionNumber;
|
|
nextEntry->OfflineSent = FALSE;
|
|
nextEntry->Next = NULL;
|
|
|
|
if ( list ) {
|
|
nextEntry->Next = list;
|
|
}
|
|
|
|
list = nextEntry;
|
|
}
|
|
|
|
//
|
|
// Update the caller's master list.
|
|
//
|
|
|
|
*OfflineList = list;
|
|
|
|
status = STATUS_SUCCESS;
|
|
|
|
FnExit:
|
|
|
|
if ( driveLayoutInfo ) {
|
|
ExFreePool( driveLayoutInfo );
|
|
}
|
|
|
|
return status;
|
|
|
|
} // AddVolumesToOfflineList
|
|
|
|
|
|
|
|
NTSTATUS
|
|
ClusDiskHaltProcessingWorker(
|
|
IN PVOID Context
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This worker thread processes halt notifications from the Cluster Network
|
|
driver.
|
|
|
|
Arguments:
|
|
|
|
Context - input context - not used.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS for this request.
|
|
|
|
Notes:
|
|
|
|
Halt processing must be done via a worker thread because it cannot
|
|
be done at DPC since the disks are dismounted.
|
|
|
|
--*/
|
|
|
|
{
|
|
PDEVICE_OBJECT deviceObject;
|
|
PCLUS_DEVICE_EXTENSION deviceExtension;
|
|
POFFLINE_ENTRY offlineList = NULL;
|
|
POFFLINE_ENTRY nextEntry;
|
|
|
|
NTSTATUS status;
|
|
|
|
CDLOG("HaltProcessingWorker: Entry(%p)", Context );
|
|
|
|
if ( RootDeviceObject == NULL ) {
|
|
HaltBusy = FALSE;
|
|
HaltOfflineBusy = FALSE;
|
|
return(STATUS_DEVICE_OFF_LINE);
|
|
}
|
|
|
|
ACQUIRE_SHARED( &ClusDiskDeviceListLock );
|
|
|
|
//
|
|
// First, capture file handles for all P0 devices
|
|
//
|
|
deviceObject = RootDeviceObject->DriverObject->DeviceObject;
|
|
while ( deviceObject ) {
|
|
deviceExtension = deviceObject->DeviceExtension;
|
|
|
|
// Keep the online check. OpenFile should now work with FILE_WRITE_ATTRIBUTES.
|
|
|
|
if ( !deviceExtension->Detached &&
|
|
deviceExtension->PhysicalDevice == deviceObject &&
|
|
deviceExtension->DiskState == DiskOnline )
|
|
{
|
|
//
|
|
// Disk has to be online,
|
|
// If it is offline, OpenFile will fail - not if FILE_WRITE_ATTRIBUTES used...
|
|
// It it is stalled OpenFile may stall
|
|
//
|
|
ProcessDelayedWorkSynchronous( deviceObject, ClusDiskpOpenFileHandles, NULL );
|
|
|
|
//
|
|
// Allocate storage to keep info for this disk. If allocated, link
|
|
// it to the offline list and offline all disks correctly to preserve
|
|
// snapshots. If we can't allocate the storage, then send offline
|
|
// directly to the volume PDO to block I/O.
|
|
//
|
|
|
|
status = AddVolumesToOfflineList( deviceExtension, &offlineList );
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
|
|
//
|
|
// We couldn't add this entry to the list, so send an offline IOCTL to
|
|
// the volume PDO, not all devices in the stack.
|
|
//
|
|
|
|
OFFLINE_DISK_PDO( deviceExtension );
|
|
}
|
|
}
|
|
|
|
deviceObject = deviceObject->NextDevice;
|
|
}
|
|
|
|
//
|
|
// Offline the list, preserving snapshots if possible.
|
|
//
|
|
|
|
OfflineVolumeList( offlineList );
|
|
|
|
while ( offlineList ) {
|
|
ASSERT( offlineList->OfflineSent );
|
|
nextEntry = offlineList->Next;
|
|
ExFreePool( offlineList );
|
|
offlineList = nextEntry;
|
|
}
|
|
|
|
//
|
|
// Clear the flag to indicate that normal offlines can occur now.
|
|
//
|
|
|
|
HaltOfflineBusy = FALSE;
|
|
|
|
deviceObject = RootDeviceObject->DriverObject->DeviceObject;
|
|
|
|
//
|
|
// Then, release all pended irps on all devices
|
|
// (Otherwise FSCTL_DISMOUNT will stall and cause a deadlock)
|
|
//
|
|
deviceObject = RootDeviceObject->DriverObject->DeviceObject;
|
|
while ( deviceObject ) {
|
|
deviceExtension = deviceObject->DeviceExtension;
|
|
if ( !deviceExtension->Detached )
|
|
{
|
|
ClusDiskCompletePendedIrps(
|
|
deviceExtension,
|
|
/* FileObject => */ NULL, // Will complete all irps //
|
|
/* Offline => */ TRUE);// will set device state to offline //
|
|
}
|
|
|
|
deviceObject = deviceObject->NextDevice;
|
|
}
|
|
|
|
//
|
|
// For each ClusDisk device, if we have a persistent reservation, then
|
|
// stop it.
|
|
//
|
|
deviceObject = RootDeviceObject->DriverObject->DeviceObject;
|
|
while ( deviceObject ) {
|
|
deviceExtension = deviceObject->DeviceExtension;
|
|
if ( !deviceExtension->Detached &&
|
|
deviceExtension->PhysicalDevice == deviceObject)
|
|
{
|
|
#if 0
|
|
status = AcquireRemoveLock( &deviceExtension->RemoveLock, deviceExtension );
|
|
if ( !NT_SUCCESS(status) ) {
|
|
|
|
// If we can't get the RemoveLock, skip this device.
|
|
deviceObject = deviceObject->NextDevice;
|
|
continue;
|
|
}
|
|
#endif
|
|
// Keep the device object around
|
|
ObReferenceObject( deviceObject);
|
|
ClusDiskDismountVolumes( deviceObject,
|
|
FALSE); // Don't release the RemoveLock
|
|
}
|
|
|
|
deviceObject = deviceObject->NextDevice;
|
|
}
|
|
RELEASE_SHARED( &ClusDiskDeviceListLock );
|
|
|
|
HaltBusy = FALSE;
|
|
CDLOG( "HaltProcessingWorker: Exit(%p)", Context );
|
|
|
|
return(STATUS_SUCCESS);
|
|
|
|
} // ClusDiskHaltProcessingWorker
|
|
|
|
|
|
VOID
|
|
SendOfflineDirect(
|
|
IN PCLUS_DEVICE_EXTENSION DeviceExtension
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Send IOCTL_VOLUME_OFFLINE to the PDO at the bottom of the volume stack.
|
|
This IOCTL will bypass all drivers on the stack.
|
|
|
|
To preserve volume snapshots, we have to send offline IOCTL only to PDO
|
|
at bottom of the volume stack. This prevents volsnap from inadventantly
|
|
deleting the snapshots in the "emergency offline" case. During normal
|
|
offline, disk dependency insures snapshoted volumes and the volumes with
|
|
the diff areas are offlined correctly. In the "emergency offline" case,
|
|
clusnet tells clusdisk that clussvc has terminated, and the disk dependency
|
|
is not maintained as clusdisk does not manage dependnecies.
|
|
|
|
This routine must be called at PASSIVE_LEVEL.
|
|
|
|
Arguments:
|
|
|
|
DeviceExtension - Device extension of the physical device.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
PDEVICE_OBJECT deviceObject;
|
|
PFILE_OBJECT fileObject;
|
|
PIRP irp;
|
|
PIO_STACK_LOCATION irpSp;
|
|
PDEVICE_RELATIONS deviceRelations;
|
|
|
|
PDRIVE_LAYOUT_INFORMATION_EX driveLayoutInfo = NULL;
|
|
PPARTITION_INFORMATION_EX partitionInfo;
|
|
|
|
NTSTATUS status;
|
|
ULONG partIndex;
|
|
|
|
KEVENT event;
|
|
IO_STATUS_BLOCK ioStatus;
|
|
|
|
UNICODE_STRING ntUnicodeString;
|
|
|
|
PWCHAR ntDeviceName = NULL;
|
|
|
|
if ( PASSIVE_LEVEL != KeGetCurrentIrql() ) {
|
|
ClusDiskPrint(( 1,
|
|
"[ClusDisk] SendOfflineDirect: Running at invalid IRQL \n" ));
|
|
CDLOG( "SendOfflineDirect: Running at invalid IRQL \n" );
|
|
ASSERT(FALSE);
|
|
goto FnExit;
|
|
}
|
|
|
|
status = GetDriveLayout( DeviceExtension->PhysicalDevice,
|
|
&driveLayoutInfo,
|
|
FALSE,
|
|
FALSE );
|
|
|
|
if ( !NT_SUCCESS(status) || !driveLayoutInfo ) {
|
|
ClusDiskPrint(( 1,
|
|
"[ClusDisk] SendOfflineDirect: Failed to read partition info, status %08X \n",
|
|
status ));
|
|
CDLOG( "SendOfflineDirect: Failed to read partition info, status %08X \n",
|
|
status );
|
|
|
|
goto FnExit;
|
|
}
|
|
|
|
ntDeviceName = ExAllocatePool( NonPagedPool, MAX_PARTITION_NAME_LENGTH * sizeof(WCHAR) );
|
|
|
|
if ( !ntDeviceName ) {
|
|
CDLOG( "SendOfflineDirect: Failed to allocate device name buffer \n" );
|
|
goto FnExit;
|
|
}
|
|
|
|
//
|
|
// For each volume on the disk, send offline IOCTL to the bottom of the volume
|
|
// driver stack.
|
|
//
|
|
|
|
for ( partIndex = 0;
|
|
partIndex < driveLayoutInfo->PartitionCount;
|
|
partIndex++ ) {
|
|
|
|
partitionInfo = &driveLayoutInfo->PartitionEntry[partIndex];
|
|
|
|
//
|
|
// First make sure this is a valid partition.
|
|
//
|
|
if ( 0 == partitionInfo->PartitionNumber ) {
|
|
continue;
|
|
}
|
|
|
|
if ( FAILED( StringCchPrintfW( ntDeviceName,
|
|
MAX_PARTITION_NAME_LENGTH,
|
|
DEVICE_PARTITION_NAME,
|
|
DeviceExtension->DiskNumber,
|
|
partitionInfo->PartitionNumber ) ) ) {
|
|
CDLOG( "SendOfflineDirect: Failed to create device name for disk %u partition %u \n",
|
|
DeviceExtension->DiskNumber,
|
|
partitionInfo->PartitionNumber );
|
|
continue;
|
|
}
|
|
|
|
RtlInitUnicodeString( &ntUnicodeString, ntDeviceName );
|
|
|
|
status = IoGetDeviceObjectPointer( &ntUnicodeString,
|
|
FILE_READ_ATTRIBUTES,
|
|
&fileObject,
|
|
&deviceObject );
|
|
if ( !NT_SUCCESS(status) ) {
|
|
ClusDiskPrint(( 1,
|
|
"[ClusDisk] SendOfflineDirect: Get devobj pointer failed for %ws, status %08X \n",
|
|
ntDeviceName,
|
|
status ));
|
|
CDLOG( "SendOfflineDirect: Get devobj pointer failed for %ws, status %08X \n",
|
|
ntDeviceName,
|
|
status );
|
|
|
|
continue;
|
|
}
|
|
|
|
deviceObject = IoGetAttachedDeviceReference( fileObject->DeviceObject );
|
|
|
|
KeInitializeEvent( &event, NotificationEvent, FALSE );
|
|
|
|
irp = IoBuildDeviceIoControlRequest( 0,
|
|
deviceObject,
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
0,
|
|
FALSE,
|
|
&event,
|
|
&ioStatus );
|
|
|
|
if ( !irp ) {
|
|
ObDereferenceObject( deviceObject );
|
|
ObDereferenceObject( fileObject );
|
|
|
|
ClusDiskPrint(( 1,
|
|
"[ClusDisk] SendOfflineDirect: Build PNP IRP failed for %ws, status %08X \n",
|
|
ntDeviceName,
|
|
status ));
|
|
CDLOG( "SendOfflineDirect: Build PNP IRP failed for %ws, status %08X \n",
|
|
ntDeviceName,
|
|
status );
|
|
continue;
|
|
}
|
|
|
|
irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
|
|
irp->IoStatus.Information = 0;
|
|
irpSp = IoGetNextIrpStackLocation(irp);
|
|
irpSp->MajorFunction = IRP_MJ_PNP;
|
|
irpSp->MinorFunction = IRP_MN_QUERY_DEVICE_RELATIONS;
|
|
irpSp->Parameters.QueryDeviceRelations.Type = TargetDeviceRelation;
|
|
irpSp->FileObject = fileObject;
|
|
|
|
status = IoCallDriver( deviceObject, irp );
|
|
|
|
if ( STATUS_PENDING == status ) {
|
|
KeWaitForSingleObject( &event, Executive, KernelMode, FALSE, NULL );
|
|
status = ioStatus.Status;
|
|
}
|
|
|
|
ObDereferenceObject(deviceObject);
|
|
ObDereferenceObject(fileObject);
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
|
|
ClusDiskPrint(( 1,
|
|
"[ClusDisk] SendOfflineDirect: PNP IRP failed for %ws, status %08X \n",
|
|
ntDeviceName,
|
|
status ));
|
|
CDLOG( "SendOfflineDirect: PNP IRP failed for %ws, status %08X \n",
|
|
ntDeviceName,
|
|
status );
|
|
continue;
|
|
}
|
|
|
|
deviceRelations = (PDEVICE_RELATIONS) ioStatus.Information;
|
|
if ( deviceRelations->Count < 1 ) {
|
|
ExFreePool(deviceRelations);
|
|
|
|
ClusDiskPrint(( 1,
|
|
"[ClusDisk] SendOfflineDirect: DeviceRelations->Count for %ws incorrect value %u \n",
|
|
ntDeviceName,
|
|
deviceRelations->Count ));
|
|
CDLOG( "SendOfflineDirect: DeviceRelations->Count for %ws incorrect value %u \n",
|
|
ntDeviceName,
|
|
deviceRelations->Count );
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// The bottom of the volume stack is represented by this PDO. Send
|
|
// volume offline IOCTL to the bottom of the stack, bypassing volsnap.
|
|
//
|
|
|
|
deviceObject = deviceRelations->Objects[0];
|
|
ExFreePool( deviceRelations );
|
|
|
|
ClusDiskPrint(( 3,
|
|
"[ClusDisk] SendOfflineDirect: Device %ws PDO %p \n",
|
|
ntDeviceName,
|
|
deviceObject ));
|
|
CDLOG( "SendOfflineDirect: Device %ws PDO %p \n",
|
|
ntDeviceName,
|
|
deviceObject );
|
|
|
|
SendFtdiskIoctlSync( deviceObject,
|
|
DeviceExtension->DiskNumber,
|
|
partitionInfo->PartitionNumber,
|
|
IOCTL_VOLUME_OFFLINE );
|
|
|
|
ObDereferenceObject( deviceObject );
|
|
}
|
|
|
|
FnExit:
|
|
|
|
if ( driveLayoutInfo ) {
|
|
ExFreePool( driveLayoutInfo );
|
|
}
|
|
|
|
if ( ntDeviceName ) {
|
|
ExFreePool( ntDeviceName );
|
|
}
|
|
|
|
} // SendOfflineDirect
|
|
|
|
|
|
|
|
VOID
|
|
ClusDiskCleanupDevice(
|
|
IN HANDLE FileHandle,
|
|
IN BOOLEAN Reset
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Cleanup the device by resetting the bus, and forcing a read of the
|
|
disk geometry.
|
|
|
|
Arguments:
|
|
|
|
FileHandle - the file handle to perform the operations.
|
|
|
|
Reset - TRUE if we should attempt resets to fix problems. FALSE otherwise.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
HANDLE eventHandle;
|
|
IO_STATUS_BLOCK ioStatusBlock;
|
|
DISK_GEOMETRY diskGeometry;
|
|
SCSI_ADDRESS scsiAddress;
|
|
BOOLEAN busReset = FALSE;
|
|
|
|
CDLOG( "CleanupDevice: Entry fh %p, reset=%!bool!", FileHandle, Reset );
|
|
ClusDiskPrint(( 3,
|
|
"[ClusDisk] CleanupDevice: FileHandle %p, Reset %s \n",
|
|
FileHandle,
|
|
BoolToString( Reset ) ));
|
|
|
|
//
|
|
// Create event for notification.
|
|
//
|
|
status = ZwCreateEvent( &eventHandle,
|
|
EVENT_ALL_ACCESS,
|
|
NULL,
|
|
SynchronizationEvent,
|
|
FALSE );
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
ClusDiskPrint((
|
|
1,
|
|
"[ClusDisk] CleanupDevice: Failed to create event, status %08X\n",
|
|
status ));
|
|
return;
|
|
}
|
|
|
|
if ( Reset ) {
|
|
//
|
|
// Start off by getting the SCSI address.
|
|
//
|
|
status = ZwDeviceIoControlFile( FileHandle,
|
|
eventHandle,
|
|
NULL,
|
|
NULL,
|
|
&ioStatusBlock,
|
|
IOCTL_SCSI_GET_ADDRESS,
|
|
NULL,
|
|
0,
|
|
&scsiAddress,
|
|
sizeof(SCSI_ADDRESS) );
|
|
|
|
if ( status == STATUS_PENDING ) {
|
|
status = ZwWaitForSingleObject(eventHandle,
|
|
FALSE,
|
|
NULL);
|
|
ASSERT( NT_SUCCESS(status) );
|
|
status = ioStatusBlock.Status;
|
|
}
|
|
|
|
if ( NT_SUCCESS(status) ) {
|
|
|
|
CDLOG( "CleanupDevice: BusReset fh %p", FileHandle );
|
|
|
|
ClusDiskPrint(( 3,
|
|
"[ClusDisk] CleanupDevice: Bus Reset \n"
|
|
));
|
|
|
|
//
|
|
// Now reset the bus!
|
|
//
|
|
|
|
ClusDiskLogError( RootDeviceObject->DriverObject, // Use RootDeviceObject not DevObj
|
|
RootDeviceObject,
|
|
scsiAddress.PathId, // Sequence number
|
|
IRP_MJ_CLEANUP, // Major function code
|
|
0, // Retry count
|
|
ID_CLEANUP, // Unique error
|
|
STATUS_SUCCESS,
|
|
CLUSDISK_RESET_BUS_REQUESTED,
|
|
0,
|
|
NULL );
|
|
|
|
status = ResetScsiDevice( NULL, &scsiAddress );
|
|
|
|
if ( NT_SUCCESS(status) ) {
|
|
busReset = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Next try to read the disk geometry.
|
|
//
|
|
status = ZwDeviceIoControlFile( FileHandle,
|
|
eventHandle,
|
|
NULL,
|
|
NULL,
|
|
&ioStatusBlock,
|
|
IOCTL_DISK_GET_DRIVE_GEOMETRY,
|
|
NULL,
|
|
0,
|
|
&diskGeometry,
|
|
sizeof(DISK_GEOMETRY) );
|
|
|
|
if ( status == STATUS_PENDING ) {
|
|
status = ZwWaitForSingleObject(eventHandle,
|
|
FALSE,
|
|
NULL);
|
|
ASSERT( NT_SUCCESS(status) );
|
|
status = ioStatusBlock.Status;
|
|
}
|
|
|
|
ZwClose( eventHandle );
|
|
|
|
//
|
|
// If we had to reset the bus, then wait for a few seconds.
|
|
//
|
|
if ( busReset ) {
|
|
LARGE_INTEGER waitTime;
|
|
|
|
waitTime.QuadPart = (ULONGLONG)(RESET_SLEEP * -(10000*1000));
|
|
KeDelayExecutionThread( KernelMode, FALSE, &waitTime );
|
|
}
|
|
|
|
CDLOG( "CleanupDevice: Exit fh %p", FileHandle );
|
|
|
|
return;
|
|
|
|
} // ClusDiskCleanupDevice
|
|
|
|
|
|
|
|
VOID
|
|
ClusDiskCleanupDeviceObject(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN BOOLEAN Reset
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Cleanup the device object by resetting the bus, and forcing a read of the
|
|
disk geometry.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - the device to perform the operations.
|
|
|
|
Reset - TRUE if we should attempt resets to fix problems. FALSE otherwise.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
IO_STATUS_BLOCK ioStatusBlock;
|
|
DISK_GEOMETRY diskGeometry;
|
|
SCSI_ADDRESS scsiAddress;
|
|
BOOLEAN busReset = FALSE;
|
|
PKEVENT event;
|
|
PIRP irp;
|
|
|
|
CDLOG( "CleanupDeviceObject: Entry DO %p reset=%!bool!", DeviceObject, Reset );
|
|
|
|
event = ExAllocatePool( NonPagedPool,
|
|
sizeof(KEVENT) );
|
|
if ( event == NULL ) {
|
|
ClusDiskPrint((
|
|
1,
|
|
"[ClusDisk] CleanupDeviceObject: Failed to allocate event\n" ));
|
|
return;
|
|
}
|
|
|
|
if ( Reset ) {
|
|
//
|
|
// Start off by getting the SCSI address.
|
|
//
|
|
|
|
//
|
|
// Find out if this is on a SCSI bus. Note, that if this device
|
|
// is not a SCSI device, it is expected that the following
|
|
// IOCTL will fail!
|
|
//
|
|
irp = IoBuildDeviceIoControlRequest(IOCTL_SCSI_GET_ADDRESS,
|
|
DeviceObject,
|
|
NULL,
|
|
0,
|
|
&scsiAddress,
|
|
sizeof(SCSI_ADDRESS),
|
|
FALSE,
|
|
event,
|
|
&ioStatusBlock);
|
|
|
|
if ( !irp ) {
|
|
ExFreePool( event );
|
|
ClusDiskPrint((
|
|
1,
|
|
"[ClusDisk] Failed to build IRP to read SCSI ADDRESS.\n"
|
|
));
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Set the event object to the unsignaled state.
|
|
// It will be used to signal request completion.
|
|
//
|
|
|
|
KeInitializeEvent(event,
|
|
NotificationEvent,
|
|
FALSE);
|
|
|
|
status = IoCallDriver(DeviceObject,
|
|
irp);
|
|
|
|
if (status == STATUS_PENDING) {
|
|
|
|
KeWaitForSingleObject(event,
|
|
Suspended,
|
|
KernelMode,
|
|
FALSE,
|
|
NULL);
|
|
|
|
status = ioStatusBlock.Status;
|
|
}
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
ClusDiskPrint((
|
|
1,
|
|
"[ClusDisk] Failed to read SCSI ADDRESS. %08X\n",
|
|
status
|
|
));
|
|
} else {
|
|
CDLOG( "CleanupDeviceObject: BusReset DO %p", DeviceObject );
|
|
|
|
ClusDiskLogError( RootDeviceObject->DriverObject, // Use RootDeviceObject not DevObj parm
|
|
RootDeviceObject,
|
|
scsiAddress.PathId, // Sequence number
|
|
0, // Major function code
|
|
0, // Retry count
|
|
ID_CLEANUP_DEV_OBJ, // Unique error
|
|
STATUS_SUCCESS,
|
|
CLUSDISK_RESET_BUS_REQUESTED,
|
|
0,
|
|
NULL );
|
|
|
|
status = ResetScsiDevice( NULL, &scsiAddress );
|
|
|
|
if ( NT_SUCCESS(status) ) {
|
|
busReset = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Next try to read the disk geometry.
|
|
//
|
|
irp = IoBuildDeviceIoControlRequest(IOCTL_DISK_GET_DRIVE_GEOMETRY,
|
|
DeviceObject,
|
|
NULL,
|
|
0,
|
|
&diskGeometry,
|
|
sizeof(DISK_GEOMETRY),
|
|
FALSE,
|
|
event,
|
|
&ioStatusBlock);
|
|
if ( !irp ) {
|
|
ClusDiskPrint((
|
|
1,
|
|
"[ClusDisk] Failed to build IRP to read DISK GEOMETRY.\n"
|
|
));
|
|
} else {
|
|
|
|
//
|
|
// Set the event object to the unsignaled state.
|
|
// It will be used to signal request completion.
|
|
//
|
|
KeInitializeEvent(event,
|
|
NotificationEvent,
|
|
FALSE);
|
|
|
|
status = IoCallDriver(DeviceObject,
|
|
irp);
|
|
|
|
if (status == STATUS_PENDING) {
|
|
|
|
KeWaitForSingleObject(event,
|
|
Suspended,
|
|
KernelMode,
|
|
FALSE,
|
|
NULL);
|
|
|
|
status = ioStatusBlock.Status;
|
|
}
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
busReset = FALSE;
|
|
ClusDiskPrint((
|
|
1,
|
|
"[ClusDisk] Failed to read DISK GEOMETRY. %08X\n",
|
|
status
|
|
));
|
|
}
|
|
|
|
//
|
|
// If we had to reset the bus, then wait for a few seconds.
|
|
//
|
|
if ( busReset ) {
|
|
LARGE_INTEGER waitTime;
|
|
|
|
waitTime.QuadPart = (ULONGLONG)(RESET_SLEEP * -(10000*1000));
|
|
KeDelayExecutionThread( KernelMode, FALSE, &waitTime );
|
|
}
|
|
}
|
|
|
|
ExFreePool( event );
|
|
CDLOG( "CleanupDeviceObject: Exit DO %p", DeviceObject );
|
|
|
|
return;
|
|
|
|
} // ClusDiskCleanupDeviceObject
|
|
|
|
|
|
|
|
NTSTATUS
|
|
ClusDiskGetP0TargetDevice(
|
|
OUT PDEVICE_OBJECT * DeviceObject OPTIONAL,
|
|
IN PUNICODE_STRING DeviceName,
|
|
OUT PDRIVE_LAYOUT_INFORMATION_EX * DriveLayoutInfo OPTIONAL,
|
|
OUT PSCSI_ADDRESS ScsiAddress OPTIONAL,
|
|
IN BOOLEAN Reset
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Find the target device object given the disk/partition numbers.
|
|
The device object will have reference count incremented and the
|
|
caller must decrement the count when done with the object.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - returns the device object if successful.
|
|
|
|
DeviceName - the unicode name for the device requested.
|
|
|
|
DriveLayoutInfo - returns the partition info if needed.
|
|
|
|
ScsiAddress - returns the scsi address info if needed.
|
|
|
|
Reset - TRUE if we should attempt resets to fix problems. FALSE otherwise.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS for this request.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
OBJECT_ATTRIBUTES objectAttributes;
|
|
HANDLE fileHandle;
|
|
PFILE_OBJECT fileObject;
|
|
IO_STATUS_BLOCK ioStatusBlock;
|
|
ULONG driveLayoutSize;
|
|
PDRIVE_LAYOUT_INFORMATION_EX driveLayoutInfo = NULL;
|
|
HANDLE eventHandle;
|
|
ULONG retry;
|
|
|
|
if ( DriveLayoutInfo != NULL ) {
|
|
*DriveLayoutInfo = NULL;
|
|
driveLayoutSize = sizeof(DRIVE_LAYOUT_INFORMATION_EX) +
|
|
(MAX_PARTITIONS * sizeof(PARTITION_INFORMATION_EX));
|
|
|
|
driveLayoutInfo = ExAllocatePool(NonPagedPoolCacheAligned,
|
|
driveLayoutSize);
|
|
|
|
if ( driveLayoutInfo == NULL ) {
|
|
return(STATUS_INSUFFICIENT_RESOURCES);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Setup object attributes for the file to open.
|
|
//
|
|
InitializeObjectAttributes(&objectAttributes,
|
|
DeviceName,
|
|
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
|
|
NULL,
|
|
NULL);
|
|
|
|
status = ZwCreateFile(&fileHandle,
|
|
FILE_READ_ATTRIBUTES,
|
|
&objectAttributes,
|
|
&ioStatusBlock,
|
|
NULL,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
FILE_OPEN,
|
|
FILE_SYNCHRONOUS_IO_NONALERT,
|
|
NULL,
|
|
0 );
|
|
ASSERT( status != STATUS_PENDING );
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
ClusDiskPrint((1,
|
|
"[ClusDisk] GetP0TargetDevice, failed to open file %ws. Error %08X.\n",
|
|
DeviceName->Buffer,
|
|
status ));
|
|
|
|
CDLOG( "ClusDiskGetP0TargetDevice: Open %wZ failed %!status!",
|
|
DeviceName,
|
|
status );
|
|
|
|
if ( driveLayoutInfo ) {
|
|
ExFreePool( driveLayoutInfo );
|
|
}
|
|
return(status);
|
|
}
|
|
|
|
//
|
|
// get device object if requested
|
|
//
|
|
if ( DeviceObject ) {
|
|
|
|
status = ObReferenceObjectByHandle(fileHandle,
|
|
0,
|
|
NULL,
|
|
KernelMode,
|
|
(PVOID *) &fileObject,
|
|
NULL );
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
ClusDiskPrint((1,
|
|
"[ClusDisk] GetP0TargetDevice Failed to reference object for file <%ws>. Error %08X.\n",
|
|
DeviceName->Buffer,
|
|
status ));
|
|
|
|
CDLOG( "ClusDiskGetP0TargetDevice: ObRef(%wZ) failed %!status!",
|
|
DeviceName,
|
|
status );
|
|
|
|
ZwClose( fileHandle );
|
|
if ( driveLayoutInfo ) {
|
|
ExFreePool( driveLayoutInfo );
|
|
}
|
|
return(status);
|
|
}
|
|
|
|
//
|
|
// Get the address of the target device object. If this file represents
|
|
// a device that was opened directly, then simply use the device or its
|
|
// attached device(s) directly. Also get the address of the Fast Io
|
|
// dispatch structure.
|
|
//
|
|
if (!(fileObject->Flags & FO_DIRECT_DEVICE_OPEN)) {
|
|
*DeviceObject = IoGetRelatedDeviceObject( fileObject );
|
|
// Add a reference to the object so we can dereference it later.
|
|
ObReferenceObject( *DeviceObject );
|
|
} else {
|
|
*DeviceObject = IoGetAttachedDeviceReference( fileObject->DeviceObject );
|
|
}
|
|
|
|
//
|
|
// If we get a file system device object... go back and get the
|
|
// device object.
|
|
//
|
|
if ( (*DeviceObject)->DeviceType == FILE_DEVICE_DISK_FILE_SYSTEM ) {
|
|
ObDereferenceObject( *DeviceObject );
|
|
*DeviceObject = IoGetAttachedDeviceReference( fileObject->DeviceObject );
|
|
}
|
|
ASSERT( (*DeviceObject)->DeviceType != FILE_DEVICE_DISK_FILE_SYSTEM );
|
|
|
|
ObDereferenceObject( fileObject );
|
|
}
|
|
|
|
//
|
|
// If we need to return scsi address information, do that now.
|
|
//
|
|
retry = 2;
|
|
while ( ScsiAddress &&
|
|
retry-- ) {
|
|
//
|
|
// Create event for notification.
|
|
//
|
|
status = ZwCreateEvent( &eventHandle,
|
|
EVENT_ALL_ACCESS,
|
|
NULL,
|
|
SynchronizationEvent,
|
|
FALSE );
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
ClusDiskPrint((1,
|
|
"[ClusDisk] GetP0TargetDevice: Failed to create event, status %lx\n",
|
|
status ));
|
|
} else {
|
|
// Should this routine be called GetScsiTargetDevice?
|
|
status = ZwDeviceIoControlFile( fileHandle,
|
|
eventHandle,
|
|
NULL,
|
|
NULL,
|
|
&ioStatusBlock,
|
|
IOCTL_SCSI_GET_ADDRESS,
|
|
NULL,
|
|
0,
|
|
ScsiAddress,
|
|
sizeof(SCSI_ADDRESS) );
|
|
|
|
if ( status == STATUS_PENDING ) {
|
|
status = ZwWaitForSingleObject(eventHandle,
|
|
FALSE,
|
|
NULL);
|
|
ASSERT( NT_SUCCESS(status) );
|
|
status = ioStatusBlock.Status;
|
|
}
|
|
|
|
ZwClose( eventHandle );
|
|
if ( NT_SUCCESS(status) ) {
|
|
break;
|
|
} else {
|
|
ClusDiskPrint((3,
|
|
"[ClusDisk] GetP0TargetDevice failed to read scsi address info for <%ws>, error %lx.\n",
|
|
DeviceName->Buffer,
|
|
status ));
|
|
CDLOG( "ClusDiskGetP0TargetDevice: GetScsiAddr(%wZ), failed %!status!",
|
|
DeviceName,
|
|
status );
|
|
|
|
ClusDiskCleanupDevice( fileHandle, Reset );
|
|
}
|
|
}
|
|
}
|
|
//
|
|
// If we need to return partition information, do that now.
|
|
//
|
|
status = STATUS_SUCCESS;
|
|
retry = 2;
|
|
while ( driveLayoutInfo &&
|
|
retry-- ) {
|
|
//
|
|
// Create event for notification.
|
|
//
|
|
status = ZwCreateEvent( &eventHandle,
|
|
EVENT_ALL_ACCESS,
|
|
NULL,
|
|
SynchronizationEvent,
|
|
FALSE );
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
ClusDiskPrint((
|
|
1,
|
|
"[ClusDisk] GetP0TargetDevice: Failed to create event, status %08X\n",
|
|
status ));
|
|
} else {
|
|
//
|
|
// Force storage drivers to flush cached drive layout. Get the
|
|
// drive layout even if the flush fails.
|
|
//
|
|
|
|
status = ZwDeviceIoControlFile( fileHandle,
|
|
eventHandle,
|
|
NULL,
|
|
NULL,
|
|
&ioStatusBlock,
|
|
IOCTL_DISK_UPDATE_PROPERTIES,
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
0 );
|
|
if ( status == STATUS_PENDING ) {
|
|
status = ZwWaitForSingleObject(eventHandle,
|
|
FALSE,
|
|
NULL);
|
|
ASSERT( NT_SUCCESS(status) );
|
|
status = ioStatusBlock.Status;
|
|
}
|
|
|
|
status = ZwDeviceIoControlFile( fileHandle,
|
|
eventHandle,
|
|
NULL,
|
|
NULL,
|
|
&ioStatusBlock,
|
|
IOCTL_DISK_GET_DRIVE_LAYOUT_EX,
|
|
NULL,
|
|
0,
|
|
driveLayoutInfo,
|
|
driveLayoutSize );
|
|
|
|
if ( status == STATUS_PENDING ) {
|
|
status = ZwWaitForSingleObject(eventHandle,
|
|
FALSE,
|
|
NULL);
|
|
ASSERT( NT_SUCCESS(status) );
|
|
status = ioStatusBlock.Status;
|
|
}
|
|
|
|
ZwClose( eventHandle );
|
|
if ( NT_SUCCESS(status) ) {
|
|
*DriveLayoutInfo = driveLayoutInfo;
|
|
break;
|
|
} else {
|
|
ClusDiskPrint((( status == STATUS_DEVICE_BUSY ? 3 : 1 ),
|
|
"[ClusDisk] GetP0TargetDevice failed to read partition info for <%ws>, error %lx.\n",
|
|
DeviceName->Buffer,
|
|
status ));
|
|
CDLOG( "ClusDiskGetP0TargetDevice: GetDriveLayout(%wZ) failed %!status!",
|
|
DeviceName,
|
|
status);
|
|
|
|
ClusDiskCleanupDevice( fileHandle, Reset );
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
ZwClose( fileHandle );
|
|
if ( driveLayoutInfo ) {
|
|
ExFreePool( driveLayoutInfo );
|
|
}
|
|
return(status);
|
|
}
|
|
|
|
ZwClose( fileHandle );
|
|
|
|
return(status);
|
|
|
|
} // ClusDiskGetP0TargetDevice
|
|
|
|
|
|
|
|
NTSTATUS
|
|
ClusDiskGetTargetDevice(
|
|
IN ULONG DiskNumber,
|
|
IN ULONG PartitionNumber,
|
|
OUT PDEVICE_OBJECT * DeviceObject OPTIONAL,
|
|
OUT PUNICODE_STRING DeviceName,
|
|
OUT PDRIVE_LAYOUT_INFORMATION_EX * DriveLayoutInfo OPTIONAL,
|
|
OUT PSCSI_ADDRESS ScsiAddress OPTIONAL,
|
|
IN BOOLEAN Reset
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Find the target device object given the disk/partition numbers.
|
|
|
|
Arguments:
|
|
|
|
DiskNumber - the disk number for the requested device.
|
|
|
|
PartitionNumber - the partition number for the requested device.
|
|
|
|
DeviceObject - returns a pointer to the device object if needed.
|
|
|
|
DeviceName - returns the unicode string for the device if successful.
|
|
|
|
DriveLayoutInfo - returns the partition info if needed.
|
|
|
|
ScsiAddress - returns the scsi address info if needed.
|
|
|
|
Reset - TRUE if we should attempt resets to fix problems. FALSE otherwise.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS for this request.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
NTSTATUS retStatus = STATUS_SUCCESS;
|
|
PWCHAR deviceNameBuffer;
|
|
PDEVICE_OBJECT deviceObject;
|
|
PDRIVE_LAYOUT_INFORMATION_EX driveLayoutInfo = NULL;
|
|
ULONG retry;
|
|
|
|
const ULONG deviceNameBufferChars = MAX_PARTITION_NAME_LENGTH;
|
|
|
|
DeviceName->Buffer = NULL;
|
|
|
|
//
|
|
// allocate enough space for a harddiskX partitionY string
|
|
//
|
|
deviceNameBuffer = ExAllocatePool(NonPagedPool,
|
|
deviceNameBufferChars * sizeof(WCHAR));
|
|
|
|
if ( deviceNameBuffer == NULL ) {
|
|
DeviceName->Buffer = NULL;
|
|
return(STATUS_INSUFFICIENT_RESOURCES);
|
|
}
|
|
|
|
//
|
|
// Create device name for the physical disk.
|
|
//
|
|
|
|
if ( FAILED( StringCchPrintfW( deviceNameBuffer,
|
|
deviceNameBufferChars - 1,
|
|
DEVICE_PARTITION_NAME,
|
|
DiskNumber,
|
|
PartitionNumber ) ) ) {
|
|
|
|
FREE_AND_NULL_PTR( deviceNameBuffer );
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
WCSLEN_ASSERT( deviceNameBuffer );
|
|
|
|
RtlInitUnicodeString( DeviceName, deviceNameBuffer );
|
|
|
|
if ( !PartitionNumber ) {
|
|
status = ClusDiskGetP0TargetDevice(
|
|
DeviceObject,
|
|
DeviceName,
|
|
DriveLayoutInfo,
|
|
ScsiAddress,
|
|
Reset );
|
|
if ( NT_SUCCESS(status) ) {
|
|
return(status);
|
|
}
|
|
|
|
ClusDiskPrint((
|
|
1,
|
|
"[ClusDisk] GetTargetDevice: try for just the device object.\n"
|
|
));
|
|
retStatus = status;
|
|
}
|
|
|
|
//
|
|
// Get the device object.
|
|
//
|
|
deviceObject = NULL;
|
|
status = ClusDiskGetDeviceObject( deviceNameBuffer,
|
|
&deviceObject );
|
|
if ( !NT_SUCCESS(status) ) {
|
|
ClusDiskPrint((
|
|
1,
|
|
"[ClusDisk] GetDeviceObject failed for %ws, status %08LX\n",
|
|
deviceNameBuffer,
|
|
status ));
|
|
DeviceName->Buffer = NULL;
|
|
ExFreePool( deviceNameBuffer );
|
|
return(status);
|
|
}
|
|
|
|
if ( DeviceObject ) {
|
|
*DeviceObject = deviceObject;
|
|
}
|
|
|
|
ClusDiskPrint((
|
|
3,
|
|
"[ClusDisk] GetTargetDevice, Found Device Object = %p \n",
|
|
deviceObject
|
|
));
|
|
|
|
//
|
|
// If we failed to get the P0 information, then return now with just
|
|
// the DeviceObject;
|
|
//
|
|
if ( !NT_SUCCESS(retStatus) ) {
|
|
ClusDiskPrint(( 3,
|
|
"[ClusDisk] GetTargetDevice, returning status %08LX (before ScsiAddress and DriveLayout) \n",
|
|
retStatus
|
|
));
|
|
return(retStatus);
|
|
}
|
|
|
|
//
|
|
// Try twice to get the SCSI ADDRESS or Drive Layout if requested.
|
|
//
|
|
retry = 2;
|
|
while ( (ScsiAddress || DriveLayoutInfo) &&
|
|
retry-- ) {
|
|
status = STATUS_SUCCESS;
|
|
if ( ScsiAddress ) {
|
|
status = GetScsiAddress( deviceObject,
|
|
ScsiAddress );
|
|
}
|
|
|
|
if ( NT_SUCCESS(status) &&
|
|
DriveLayoutInfo &&
|
|
!driveLayoutInfo ) {
|
|
ClusDiskPrint(( 3,
|
|
"[ClusDisk] GetTargetDevice, GetScsiAddress was successful \n"
|
|
));
|
|
status = GetDriveLayout( deviceObject,
|
|
&driveLayoutInfo, // If part0, this will be physical disk
|
|
FALSE, // Don't update cached drive layout
|
|
0 == PartitionNumber ? TRUE : FALSE ); // If part0, flush storage cached drive layout
|
|
if ( NT_SUCCESS(status) ) {
|
|
ClusDiskPrint(( 3,
|
|
"[ClusDisk] GetTargetDevice, GetDriveLayout was successful \n"
|
|
));
|
|
}
|
|
}
|
|
|
|
//
|
|
// If we have what we need, then break out now.
|
|
//
|
|
if ( NT_SUCCESS(status) ) {
|
|
break;
|
|
}
|
|
|
|
ClusDiskCleanupDeviceObject( deviceObject, Reset );
|
|
}
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
ExFreePool( deviceNameBuffer );
|
|
DeviceName->Buffer = NULL;
|
|
if ( driveLayoutInfo ) {
|
|
ExFreePool( driveLayoutInfo );
|
|
}
|
|
} else {
|
|
if ( DriveLayoutInfo ) {
|
|
*DriveLayoutInfo = driveLayoutInfo;
|
|
}
|
|
}
|
|
|
|
ClusDiskPrint(( 3,
|
|
"[ClusDisk] GetTargetDevice, returning status %08LX \n",
|
|
status
|
|
));
|
|
|
|
return(status);
|
|
|
|
} // ClusDiskGetTargetDevice
|
|
|
|
|
|
|
|
NTSTATUS
|
|
ClusDiskInitRegistryString(
|
|
OUT PUNICODE_STRING UnicodeString,
|
|
IN LPWSTR KeyName,
|
|
IN ULONG KeyNameChars
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initialize a Unicode registry key string.
|
|
|
|
Arguments:
|
|
|
|
UnicodeString - pointer to the registry string to initialize.
|
|
|
|
KeyName - the key name.
|
|
|
|
KeyNameChars - the key name WCHAR count.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS for this request.
|
|
|
|
Notes:
|
|
|
|
The UnicodeString buffer is allocated from paged pool.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG keyNameSize = KeyNameChars * sizeof(WCHAR);
|
|
|
|
//
|
|
// Allocate buffer for signatures registry keys.
|
|
//
|
|
UnicodeString->Length = 0;
|
|
UnicodeString->MaximumLength = (USHORT)(ClusDiskRegistryPath.MaximumLength +
|
|
keyNameSize +
|
|
sizeof(CLUSDISK_SIGNATURE_FIELD) +
|
|
sizeof(UNICODE_NULL));
|
|
|
|
UnicodeString->Buffer = ExAllocatePool(
|
|
PagedPool,
|
|
UnicodeString->MaximumLength
|
|
);
|
|
|
|
if ( !UnicodeString->Buffer ) {
|
|
ClusDiskPrint((
|
|
1,
|
|
"[ClusDisk] InitRegistryString, failed to allocate a KeyName buffer\n"
|
|
));
|
|
return(STATUS_INSUFFICIENT_RESOURCES);
|
|
}
|
|
|
|
//
|
|
// Zero the key name buffer.
|
|
//
|
|
RtlZeroMemory(
|
|
UnicodeString->Buffer,
|
|
UnicodeString->MaximumLength
|
|
);
|
|
|
|
//
|
|
// Initialize the string to the registry name for clusdisk.
|
|
//
|
|
RtlAppendUnicodeToString(
|
|
UnicodeString,
|
|
ClusDiskRegistryPath.Buffer
|
|
);
|
|
|
|
//
|
|
// Append the keyname.
|
|
//
|
|
RtlAppendUnicodeToString(
|
|
UnicodeString,
|
|
KeyName
|
|
);
|
|
|
|
UnicodeString->Buffer[ UnicodeString->Length / sizeof(WCHAR) ] = UNICODE_NULL;
|
|
|
|
return(STATUS_SUCCESS);
|
|
|
|
} // ClusDiskInitRegistryString
|
|
|
|
|
|
|
|
ULONG
|
|
ClusDiskIsSignatureDisk(
|
|
IN ULONG Signature
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Determine if the specified signature is in the signature list.
|
|
|
|
Arguments:
|
|
|
|
Signature - the signature for the disk of interest.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS for this request.
|
|
|
|
--*/
|
|
|
|
{
|
|
WCHAR buffer[128];
|
|
HANDLE regHandle;
|
|
OBJECT_ATTRIBUTES objectAttributes;
|
|
NTSTATUS status;
|
|
UNICODE_STRING regString;
|
|
|
|
if ( FAILED( StringCchPrintfW( buffer,
|
|
RTL_NUMBER_OF(buffer),
|
|
L"%ws\\%08lX",
|
|
CLUSDISK_SIGNATURE_KEYNAME,
|
|
Signature ) ) ) {
|
|
return FALSE;
|
|
}
|
|
|
|
status = ClusDiskInitRegistryString(
|
|
®String,
|
|
buffer,
|
|
wcslen(buffer)
|
|
);
|
|
if ( !NT_SUCCESS(status) ) {
|
|
return(FALSE);
|
|
}
|
|
|
|
InitializeObjectAttributes(
|
|
&objectAttributes,
|
|
®String,
|
|
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
status = ZwOpenKey(
|
|
®Handle,
|
|
KEY_ALL_ACCESS,
|
|
&objectAttributes
|
|
);
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
#if 0
|
|
ClusDiskPrint((
|
|
1,
|
|
"[ClusDisk] IsSignatureDisk: Error opening registry key <%wZ> for delete. Status %lx.\n",
|
|
®String,
|
|
status));
|
|
#endif
|
|
ExFreePool( regString.Buffer );
|
|
return(FALSE);
|
|
} else {
|
|
ExFreePool( regString.Buffer );
|
|
ZwClose( regHandle );
|
|
return(TRUE);
|
|
}
|
|
|
|
return(STATUS_SUCCESS);
|
|
|
|
} // ClusDiskIsSignatureDisk
|
|
|
|
|
|
|
|
NTSTATUS
|
|
ClusDiskDeleteSignatureKey(
|
|
IN PUNICODE_STRING UnicodeString,
|
|
IN LPWSTR Name
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Delete the signature from the specified list.
|
|
|
|
Arguments:
|
|
|
|
UnicodeString - pointer to the Unicode base keyname for deleting.
|
|
|
|
Name - the keyname to delete.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS for this request.
|
|
|
|
--*/
|
|
|
|
{
|
|
WCHAR buffer[128];
|
|
UNICODE_STRING nameString;
|
|
HANDLE deleteHandle;
|
|
OBJECT_ATTRIBUTES objectAttributes;
|
|
NTSTATUS status;
|
|
|
|
nameString.Length = 0;
|
|
nameString.MaximumLength = sizeof(buffer);
|
|
nameString.Buffer = buffer;
|
|
|
|
RtlCopyUnicodeString( &nameString, UnicodeString );
|
|
|
|
RtlAppendUnicodeToString(
|
|
&nameString,
|
|
L"\\"
|
|
);
|
|
|
|
RtlAppendUnicodeToString(
|
|
&nameString,
|
|
Name
|
|
);
|
|
nameString.Buffer[ nameString.Length / sizeof(WCHAR) ] = UNICODE_NULL;
|
|
|
|
InitializeObjectAttributes(
|
|
&objectAttributes,
|
|
&nameString,
|
|
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
status = ZwOpenKey(
|
|
&deleteHandle,
|
|
KEY_ALL_ACCESS,
|
|
&objectAttributes
|
|
);
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
ClusDiskPrint((
|
|
1,
|
|
"[ClusDisk] DeleteSignatureKey: Error opening registry key <%wZ> for delete. Status %lx.\n",
|
|
&nameString,
|
|
status));
|
|
|
|
|
|
return(status);
|
|
}
|
|
|
|
status = ZwDeleteKey( deleteHandle );
|
|
if ( !NT_SUCCESS(status) &&
|
|
(status != STATUS_OBJECT_NAME_NOT_FOUND) ) {
|
|
ClusDiskPrint((
|
|
1,
|
|
"[ClusDisk] DeleteSignatureKey Error deleting <%ws> registry key from <%wZ>. Status: %lx\n",
|
|
Name,
|
|
&nameString,
|
|
status));
|
|
}
|
|
|
|
ZwClose( deleteHandle );
|
|
|
|
return(STATUS_SUCCESS);
|
|
|
|
} // ClusDiskDeleteSignatureKey
|
|
|
|
|
|
|
|
NTSTATUS
|
|
ClusDiskAddSignature(
|
|
IN PUNICODE_STRING UnicodeString,
|
|
IN ULONG Signature,
|
|
IN BOOLEAN Volatile
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Add the signature to the specified list.
|
|
|
|
Arguments:
|
|
|
|
UnicodeString - pointer to the Unicode base keyname for adding.
|
|
|
|
Signature - signature to add.
|
|
|
|
Volatile - TRUE if volatile key should be created.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS for this request.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
WCHAR buffer[MAXIMUM_FILENAME_LENGTH];
|
|
UNICODE_STRING nameString;
|
|
HANDLE addHandle;
|
|
OBJECT_ATTRIBUTES objectAttributes;
|
|
OBJECT_ATTRIBUTES keyObjectAttributes;
|
|
ULONG options = 0;
|
|
UCHAR ntNameBuffer[64];
|
|
STRING ntNameString;
|
|
UNICODE_STRING ntUnicodeString;
|
|
|
|
ClusDiskPrint(( 3,
|
|
"[ClusDisk] ClusDiskAddSignature: adding signature %08X to %ws \n",
|
|
Signature,
|
|
UnicodeString->Buffer
|
|
));
|
|
|
|
if ( SystemDiskSignature == Signature ) {
|
|
ClusDiskPrint(( 3,
|
|
"[ClusDisk] ClusDiskAddSignature: skipping system disk signature %08X \n",
|
|
Signature
|
|
));
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
if ( Volatile ) {
|
|
options = REG_OPTION_VOLATILE;
|
|
}
|
|
|
|
nameString.Length = 0;
|
|
nameString.MaximumLength = sizeof( buffer );
|
|
nameString.Buffer = buffer;
|
|
|
|
//
|
|
// Create the name of the key to add.
|
|
//
|
|
RtlCopyUnicodeString( &nameString, UnicodeString );
|
|
|
|
//
|
|
// Create device name for the physical disk.
|
|
//
|
|
|
|
if ( FAILED( StringCchPrintf( ntNameBuffer,
|
|
RTL_NUMBER_OF( ntNameBuffer),
|
|
"\\%08lX",
|
|
Signature ) ) ) {
|
|
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
ASSERT( strlen(ntNameBuffer) < 64 );
|
|
|
|
RtlInitAnsiString(&ntNameString,
|
|
ntNameBuffer);
|
|
|
|
status = RtlAnsiStringToUnicodeString(&ntUnicodeString,
|
|
&ntNameString,
|
|
TRUE);
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
return status;
|
|
}
|
|
|
|
RtlAppendUnicodeToString(
|
|
&nameString,
|
|
ntUnicodeString.Buffer
|
|
);
|
|
nameString.Buffer[ nameString.Length / sizeof(WCHAR) ] = UNICODE_NULL;
|
|
|
|
RtlFreeUnicodeString( &ntUnicodeString );
|
|
|
|
//
|
|
// For opening the passed in registry key name.
|
|
//
|
|
InitializeObjectAttributes(
|
|
&keyObjectAttributes,
|
|
UnicodeString,
|
|
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
//
|
|
// Attempt to open the passed in key.
|
|
//
|
|
status = ZwOpenKey(
|
|
&addHandle,
|
|
KEY_ALL_ACCESS,
|
|
&keyObjectAttributes
|
|
);
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
//
|
|
// Assume the key doesn't exist.
|
|
//
|
|
status = ZwCreateKey(
|
|
&addHandle,
|
|
KEY_ALL_ACCESS,
|
|
&keyObjectAttributes,
|
|
0,
|
|
NULL,
|
|
options,
|
|
NULL
|
|
);
|
|
if ( !NT_SUCCESS(status) ) {
|
|
ClusDiskPrint((
|
|
1,
|
|
"[ClusDisk] AddSignature: Error creating registry key <%wZ>. Status: %lx\n",
|
|
UnicodeString,
|
|
status
|
|
));
|
|
return(status);
|
|
}
|
|
}
|
|
|
|
ZwClose( addHandle );
|
|
|
|
//
|
|
// For opening the new registry key name.
|
|
//
|
|
InitializeObjectAttributes(
|
|
&objectAttributes,
|
|
&nameString,
|
|
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
status = ZwCreateKey(
|
|
&addHandle,
|
|
KEY_ALL_ACCESS,
|
|
&objectAttributes,
|
|
0,
|
|
NULL,
|
|
options,
|
|
NULL
|
|
);
|
|
if ( !NT_SUCCESS(status) ) {
|
|
ClusDiskPrint((
|
|
1,
|
|
"[ClusDisk] AddSignature: Error creating registry key <%wZ> under <%wZ>. Status: %lx\n",
|
|
&nameString,
|
|
UnicodeString,
|
|
status
|
|
));
|
|
return(status);
|
|
}
|
|
|
|
ZwClose( addHandle );
|
|
|
|
return(STATUS_SUCCESS);
|
|
|
|
} // ClusDiskAddSignature
|
|
|
|
|
|
|
|
NTSTATUS
|
|
ClusDiskDeleteSignature(
|
|
IN PUNICODE_STRING UnicodeString,
|
|
IN ULONG Signature
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Delete the signature from the specified list.
|
|
|
|
Arguments:
|
|
|
|
UnicodeString - pointer to the Unicode base keyname for deleting.
|
|
|
|
Signature - signature to delete.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS for this request.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
WCHAR buffer[128];
|
|
UNICODE_STRING nameString;
|
|
HANDLE deleteHandle;
|
|
OBJECT_ATTRIBUTES objectAttributes;
|
|
UCHAR ntNameBuffer[64];
|
|
STRING ntNameString;
|
|
UNICODE_STRING ntUnicodeString;
|
|
|
|
ClusDiskPrint(( 3,
|
|
"[ClusDisk] ClusDiskDeleteSignature: removing signature %08X \n",
|
|
Signature
|
|
));
|
|
|
|
nameString.Length = 0;
|
|
nameString.MaximumLength = sizeof(buffer);
|
|
nameString.Buffer = buffer;
|
|
|
|
//
|
|
// Create the name of the key to delete.
|
|
//
|
|
RtlCopyUnicodeString( &nameString, UnicodeString );
|
|
|
|
//
|
|
// Create device name for the physical disk.
|
|
//
|
|
|
|
if ( FAILED( StringCchPrintf( ntNameBuffer,
|
|
RTL_NUMBER_OF(ntNameBuffer),
|
|
"\\%08lX",
|
|
Signature ) ) ) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
RtlInitAnsiString(&ntNameString,
|
|
ntNameBuffer);
|
|
|
|
status = RtlAnsiStringToUnicodeString(&ntUnicodeString,
|
|
&ntNameString,
|
|
TRUE);
|
|
if ( !NT_SUCCESS(status) ) {
|
|
return status;
|
|
}
|
|
|
|
RtlAppendUnicodeToString(
|
|
&nameString,
|
|
ntUnicodeString.Buffer
|
|
);
|
|
nameString.Buffer[ nameString.Length / sizeof(WCHAR) ] = UNICODE_NULL;
|
|
|
|
RtlFreeUnicodeString( &ntUnicodeString );
|
|
|
|
//
|
|
// Use generated name for opening.
|
|
//
|
|
InitializeObjectAttributes(
|
|
&objectAttributes,
|
|
&nameString,
|
|
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
//
|
|
// Open the key for deleting.
|
|
//
|
|
status = ZwOpenKey(
|
|
&deleteHandle,
|
|
KEY_ALL_ACCESS,
|
|
&objectAttributes
|
|
);
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
ClusDiskPrint((
|
|
1,
|
|
"[ClusDisk] DeleteSignature: Error opening registry key <%wZ> for delete. Status %lx.\n",
|
|
&nameString,
|
|
status
|
|
));
|
|
return(status);
|
|
}
|
|
|
|
status = ZwDeleteKey( deleteHandle );
|
|
if ( !NT_SUCCESS(status) &&
|
|
(status != STATUS_OBJECT_NAME_NOT_FOUND) ) {
|
|
ClusDiskPrint((
|
|
1,
|
|
"[ClusDisk] DeleteSignature: Error deleting <%s> registry key from <%wZ>. Status: %lx\n",
|
|
ntNameBuffer,
|
|
&nameString,
|
|
status
|
|
));
|
|
|
|
}
|
|
|
|
ZwClose( deleteHandle );
|
|
|
|
return(STATUS_SUCCESS);
|
|
|
|
} // ClusDiskDeleteSignature
|
|
|
|
|
|
|
|
NTSTATUS
|
|
ClusDiskAddDiskName(
|
|
IN HANDLE SignatureHandle,
|
|
IN ULONG DiskNumber
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Set the DiskName for a given signature handle.
|
|
|
|
Arguments:
|
|
|
|
SignatureHandle - the handle for the signature to write.
|
|
|
|
DiskNumber - the disk number for this signature.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS for this request.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
UNICODE_STRING name;
|
|
UCHAR ntNameBuffer[64];
|
|
STRING ntNameString;
|
|
UNICODE_STRING ntUnicodeString;
|
|
|
|
//
|
|
// Write the disk name.
|
|
//
|
|
|
|
RtlInitUnicodeString( &name, CLUSDISK_SIGNATURE_DISK_NAME );
|
|
|
|
if ( FAILED( StringCchPrintf( ntNameBuffer,
|
|
RTL_NUMBER_OF(ntNameBuffer),
|
|
"\\Device\\Harddisk%d",
|
|
DiskNumber ) ) ) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
RtlInitAnsiString(&ntNameString,
|
|
ntNameBuffer);
|
|
|
|
status = RtlAnsiStringToUnicodeString(&ntUnicodeString,
|
|
&ntNameString,
|
|
TRUE);
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
return status;
|
|
}
|
|
|
|
status = ZwSetValueKey(
|
|
SignatureHandle,
|
|
&name,
|
|
0,
|
|
REG_SZ,
|
|
ntUnicodeString.Buffer,
|
|
ntUnicodeString.Length + sizeof(UNICODE_NULL) ); // Length for this call must include the trailing NULL.
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
ClusDiskPrint((1,
|
|
"[ClusDisk] AddDiskName: Failed to set diskname for signature %wZ status: %08X\n",
|
|
&ntUnicodeString,
|
|
status));
|
|
}
|
|
|
|
RtlFreeUnicodeString( &ntUnicodeString );
|
|
|
|
return(status);
|
|
|
|
} // ClusDiskAddDiskName
|
|
|
|
|
|
|
|
NTSTATUS
|
|
ClusDiskDeleteDiskName(
|
|
IN PUNICODE_STRING KeyName,
|
|
IN LPWSTR Name
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Delete the DiskName for the given key.
|
|
|
|
Arguments:
|
|
|
|
KeyName - pointer to the Unicode base keyname for deleting.
|
|
|
|
Name - the signature key to delete the diskname.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS for this request.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
WCHAR buffer[128];
|
|
UNICODE_STRING nameString;
|
|
HANDLE deleteHandle;
|
|
OBJECT_ATTRIBUTES objectAttributes;
|
|
|
|
nameString.Length = 0;
|
|
nameString.MaximumLength = sizeof(buffer);
|
|
nameString.Buffer = buffer;
|
|
|
|
RtlCopyUnicodeString( &nameString, KeyName );
|
|
|
|
RtlAppendUnicodeToString(
|
|
&nameString,
|
|
L"\\"
|
|
);
|
|
|
|
RtlAppendUnicodeToString(
|
|
&nameString,
|
|
Name
|
|
);
|
|
|
|
nameString.Buffer[ nameString.Length / sizeof(WCHAR) ] = UNICODE_NULL;
|
|
|
|
InitializeObjectAttributes(
|
|
&objectAttributes,
|
|
&nameString,
|
|
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
status = ZwOpenKey(
|
|
&deleteHandle,
|
|
KEY_ALL_ACCESS,
|
|
&objectAttributes
|
|
);
|
|
|
|
if ( status == STATUS_OBJECT_NAME_NOT_FOUND ) {
|
|
return(STATUS_SUCCESS);
|
|
}
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
ClusDiskPrint((
|
|
1,
|
|
"[ClusDisk] DeleteDiskName: Error opening registry key <%wZ> to delete DiskName. Status %lx.\n",
|
|
&nameString,
|
|
status
|
|
));
|
|
|
|
return(status);
|
|
}
|
|
|
|
RtlInitUnicodeString( &nameString, CLUSDISK_SIGNATURE_DISK_NAME );
|
|
|
|
status = ZwDeleteValueKey(
|
|
deleteHandle,
|
|
&nameString
|
|
);
|
|
if ( !NT_SUCCESS(status) &&
|
|
(status != STATUS_OBJECT_NAME_NOT_FOUND) ) {
|
|
ClusDiskPrint((
|
|
1,
|
|
"[ClusDisk] DeleteDiskName: Error deleting DiskName value key from <%ws\\%wZ>. Status: %lx\n",
|
|
Name,
|
|
&nameString,
|
|
status
|
|
));
|
|
}
|
|
|
|
ZwClose( deleteHandle );
|
|
|
|
return(STATUS_SUCCESS);
|
|
|
|
} // ClusDiskDeleteDiskName
|
|
|
|
|
|
VOID
|
|
ClusDiskDeleteDevice(
|
|
IN PDEVICE_OBJECT DeviceObject
|
|
)
|
|
{
|
|
KIRQL irql;
|
|
PCLUS_DEVICE_EXTENSION deviceExtension;
|
|
|
|
deviceExtension = DeviceObject->DeviceExtension;
|
|
KeAcquireSpinLock(&ClusDiskSpinLock, &irql);
|
|
deviceExtension->Detached = TRUE;
|
|
if ( deviceExtension->PhysicalDevice ) {
|
|
ObDereferenceObject( deviceExtension->PhysicalDevice );
|
|
}
|
|
KeReleaseSpinLock(&ClusDiskSpinLock, irql);
|
|
ExDeleteResourceLite( &deviceExtension->DriveLayoutLock );
|
|
ExDeleteResourceLite( &deviceExtension->ReserveInfoLock );
|
|
IoDeleteDevice( DeviceObject );
|
|
return;
|
|
}
|
|
|
|
|
|
VOID
|
|
ClusDiskScsiInitialize(
|
|
IN PDRIVER_OBJECT DriverObject,
|
|
IN PVOID NextDisk,
|
|
IN ULONG Count
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Attach to new disk devices and partitions for busses defined in registry.
|
|
If this is the first time this routine is called,
|
|
then register with the IO system to be called
|
|
after all other disk device drivers have initiated.
|
|
|
|
Arguments:
|
|
|
|
DriverObject - Disk performance driver object.
|
|
|
|
NextDisk - Starting disk for this part of the initialization.
|
|
|
|
Count - Not used. Number of times this routine has been called.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
|
|
{
|
|
PCONFIGURATION_INFORMATION configurationInformation;
|
|
UNICODE_STRING targetDeviceName;
|
|
UNICODE_STRING clusdiskDeviceName;
|
|
WCHAR clusdiskDeviceBuffer[MAX_CLUSDISK_DEVICE_NAME_LENGTH];
|
|
PDEVICE_OBJECT deviceObject;
|
|
PDEVICE_OBJECT physicalDevice;
|
|
PDEVICE_OBJECT targetDevice = NULL;
|
|
PDEVICE_OBJECT attachedTargetDevice;
|
|
PCLUS_DEVICE_EXTENSION deviceExtension;
|
|
PCLUS_DEVICE_EXTENSION zeroExtension;
|
|
PDRIVE_LAYOUT_INFORMATION_EX driveLayoutInfo;
|
|
PPARTITION_INFORMATION_EX partitionInfo;
|
|
NTSTATUS status;
|
|
ULONG diskNumber;
|
|
ULONG partIndex;
|
|
ULONG enumIndex;
|
|
ULONG returnedLength;
|
|
ULONG signature;
|
|
ULONG diskCount;
|
|
ULONG skipCount;
|
|
HANDLE signatureHandle;
|
|
HANDLE availableHandle;
|
|
WCHAR signatureBuffer[64];
|
|
UNICODE_STRING signatureKeyName;
|
|
UNICODE_STRING keyName;
|
|
UNICODE_STRING availableName;
|
|
UNICODE_STRING numberString;
|
|
OBJECT_ATTRIBUTES objectAttributes;
|
|
OBJECT_ATTRIBUTES availableObjectAttributes;
|
|
UCHAR basicBuffer[MAX_BUFFER_SIZE];
|
|
PKEY_BASIC_INFORMATION keyBasicInformation;
|
|
WCHAR signatureKeyBuffer[128];
|
|
SCSI_ADDRESS scsiAddress;
|
|
|
|
// PAGED_CODE(); // 2000/02/05: stevedz - Paged code cannot grab spinlocks.
|
|
|
|
ClusDiskRescan = FALSE;
|
|
|
|
keyBasicInformation = (PKEY_BASIC_INFORMATION)basicBuffer;
|
|
|
|
RtlZeroMemory(
|
|
basicBuffer,
|
|
MAX_BUFFER_SIZE
|
|
);
|
|
|
|
//
|
|
// Get registry parameters for our device.
|
|
//
|
|
|
|
//
|
|
// Allocate buffer for signatures registry key.
|
|
//
|
|
status = ClusDiskInitRegistryString(
|
|
&keyName,
|
|
CLUSDISK_SIGNATURE_KEYNAME,
|
|
wcslen(CLUSDISK_SIGNATURE_KEYNAME)
|
|
);
|
|
if ( !NT_SUCCESS(status) ) {
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Allocate buffer for our list of available signatures,
|
|
// and form the subkey string name.
|
|
//
|
|
status = ClusDiskInitRegistryString(
|
|
&availableName,
|
|
CLUSDISK_AVAILABLE_DISKS_KEYNAME,
|
|
wcslen(CLUSDISK_AVAILABLE_DISKS_KEYNAME)
|
|
);
|
|
if ( !NT_SUCCESS(status) ) {
|
|
ExFreePool( keyName.Buffer );
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Setup the object attributes for the Parameters\Signatures key.
|
|
//
|
|
|
|
InitializeObjectAttributes(
|
|
&objectAttributes,
|
|
&keyName,
|
|
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
//
|
|
// Open Parameters\Signatures Key.
|
|
//
|
|
|
|
status = ZwOpenKey(
|
|
&signatureHandle,
|
|
KEY_READ,
|
|
&objectAttributes
|
|
);
|
|
if ( status == STATUS_OBJECT_NAME_NOT_FOUND ) {
|
|
status = ZwCreateKey(
|
|
&signatureHandle,
|
|
KEY_ALL_ACCESS,
|
|
&objectAttributes,
|
|
0,
|
|
NULL,
|
|
0,
|
|
NULL
|
|
);
|
|
}
|
|
if ( !NT_SUCCESS(status) ) {
|
|
ExFreePool( keyName.Buffer );
|
|
ExFreePool( availableName.Buffer );
|
|
ClusDiskPrint((
|
|
1,
|
|
"[ClusDisk] ScsiInit: Failed to open Signatures registry key. Status: %lx\n",
|
|
status
|
|
));
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Setup the object attributes for the Parameters\AvailableDisks key.
|
|
//
|
|
|
|
InitializeObjectAttributes(
|
|
&availableObjectAttributes,
|
|
&availableName,
|
|
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
//
|
|
// Open Parameters\AvailableDisks Key.
|
|
//
|
|
|
|
status = ZwOpenKey(
|
|
&availableHandle,
|
|
KEY_ALL_ACCESS,
|
|
&availableObjectAttributes
|
|
);
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
if ( status != STATUS_OBJECT_NAME_NOT_FOUND ) {
|
|
ClusDiskPrint((
|
|
1,
|
|
"[ClusDisk] ScsiInit: Failed to open AvailableDisks registry key. Status: %lx. Continuing.\n",
|
|
status
|
|
));
|
|
}
|
|
} else {
|
|
|
|
//
|
|
// Delete the previous list of available devices.
|
|
//
|
|
enumIndex = 0;
|
|
while ( TRUE ) {
|
|
status = ZwEnumerateKey(
|
|
availableHandle,
|
|
enumIndex,
|
|
KeyBasicInformation,
|
|
keyBasicInformation,
|
|
MAX_BUFFER_SIZE,
|
|
&returnedLength
|
|
);
|
|
|
|
enumIndex++;
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
if ( status == STATUS_NO_MORE_ENTRIES ) {
|
|
break;
|
|
} else {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
status = ClusDiskDeleteSignatureKey(
|
|
&availableName,
|
|
keyBasicInformation->Name
|
|
);
|
|
if ( !NT_SUCCESS(status) ) {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
status = ZwDeleteKey( availableHandle );
|
|
if ( !NT_SUCCESS(status) &&
|
|
(status != STATUS_OBJECT_NAME_NOT_FOUND) &&
|
|
(status != STATUS_CANNOT_DELETE) ) {
|
|
ClusDiskPrint((
|
|
1,
|
|
"[ClusDisk] ScsiInit: Failed to delete AvailableDisks registry key. Status: %lx\n",
|
|
status
|
|
));
|
|
}
|
|
|
|
ZwClose( availableHandle );
|
|
}
|
|
|
|
//
|
|
// Find out which Scsi Devices to control by enumerating all of the
|
|
// Signature keys. If we find a device that we cannot read the signature
|
|
// for, we will attach to it anyway.
|
|
//
|
|
|
|
enumIndex = 0;
|
|
while ( TRUE ) {
|
|
status = ZwEnumerateKey(
|
|
signatureHandle,
|
|
enumIndex,
|
|
KeyBasicInformation,
|
|
keyBasicInformation,
|
|
MAX_BUFFER_SIZE,
|
|
&returnedLength
|
|
);
|
|
|
|
enumIndex++;
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
if ( status == STATUS_NO_MORE_ENTRIES ) {
|
|
break;
|
|
} else {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Check that the value is reasonable. We're only looking for
|
|
// signatures (ie keys that are hex numbers).
|
|
//
|
|
|
|
//
|
|
// Check the signature. Make sure it's a number.
|
|
//
|
|
|
|
numberString.Buffer = keyBasicInformation->Name;
|
|
numberString.MaximumLength = (USHORT)keyBasicInformation->NameLength +
|
|
sizeof(UNICODE_NULL);
|
|
numberString.Length = (USHORT)keyBasicInformation->NameLength;
|
|
|
|
status = RtlUnicodeStringToInteger(
|
|
&numberString,
|
|
16,
|
|
&signature
|
|
);
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
ClusDiskPrint((1,
|
|
"[ClusDisk] ScsiInit: Failed to get a good signature for %.*ws status: %08X\n",
|
|
keyBasicInformation->NameLength/sizeof(WCHAR),
|
|
keyBasicInformation->Name,
|
|
status));
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// If this device is not in our list of attached devices, then add it!
|
|
//
|
|
if ( !MatchDevice( signature, NULL ) ) {
|
|
|
|
if ( !AddAttachedDevice( signature, NULL ) ) {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Delete the DiskName for this signature key. We do this here in
|
|
// case any of the rest of this fails.
|
|
//
|
|
|
|
// Don't delete entries for disks that we've already processed.
|
|
|
|
if ( (ULONG_PTR)NextDisk != 0 ) {
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Delete the DiskName for the signature.
|
|
//
|
|
status = ClusDiskDeleteDiskName(
|
|
&keyName,
|
|
keyBasicInformation->Name
|
|
);
|
|
}
|
|
|
|
ZwClose( signatureHandle );
|
|
|
|
//
|
|
// Get the system configuration information.
|
|
//
|
|
|
|
configurationInformation = IoGetConfigurationInformation();
|
|
diskCount = configurationInformation->DiskCount;
|
|
|
|
//
|
|
// Find ALL disk devices. There may be holes in the disk numbering,
|
|
// so skipCount will be used.
|
|
//
|
|
for ( diskNumber = (ULONG)((ULONG_PTR)NextDisk), skipCount = 0;
|
|
( diskNumber < diskCount && skipCount < SKIP_COUNT_MAX );
|
|
diskNumber++ ) {
|
|
|
|
//
|
|
// Create device name for the physical disk.
|
|
//
|
|
|
|
DEREFERENCE_OBJECT( targetDevice );
|
|
status = ClusDiskGetTargetDevice( diskNumber,
|
|
0,
|
|
&targetDevice,
|
|
&targetDeviceName,
|
|
&driveLayoutInfo,
|
|
&scsiAddress,
|
|
FALSE );
|
|
if ( !NT_SUCCESS(status) ) {
|
|
|
|
//
|
|
// If the device doesn't exist, this is likely a hole in the
|
|
// disk numbering.
|
|
//
|
|
|
|
if ( !targetDevice &&
|
|
( STATUS_FILE_INVALID == status ||
|
|
STATUS_DEVICE_DOES_NOT_EXIST == status ||
|
|
STATUS_OBJECT_PATH_NOT_FOUND == status ) ) {
|
|
skipCount++;
|
|
diskCount++;
|
|
ClusDiskPrint(( 3,
|
|
"[ClusDisk] Adjust: skipCount %d diskCount %d \n",
|
|
skipCount,
|
|
diskCount ));
|
|
CDLOG( "ClusDiskScsiInitialize: Adjust: skipCount %d diskCount %d ",
|
|
skipCount,
|
|
diskCount );
|
|
}
|
|
|
|
//
|
|
// If we didn't get a target device or we're already attached
|
|
// then skip this device.
|
|
//
|
|
if ( !targetDevice || ( targetDevice &&
|
|
ClusDiskAttached( targetDevice, diskNumber ) ) ) {
|
|
|
|
FREE_DEVICE_NAME_BUFFER( targetDeviceName );
|
|
|
|
FREE_AND_NULL_PTR( driveLayoutInfo );
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// If this device is on the system bus... then skip it.
|
|
// Also... if the media type is not FixedMedia, skip it.
|
|
//
|
|
if ( ((SystemDiskPort == scsiAddress.PortNumber) &&
|
|
(SystemDiskPath == scsiAddress.PathId)) ||
|
|
(GetMediaType( targetDevice ) != FixedMedia) ) {
|
|
|
|
FREE_DEVICE_NAME_BUFFER( targetDeviceName );
|
|
|
|
FREE_AND_NULL_PTR( driveLayoutInfo );
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Only process MBR disks.
|
|
//
|
|
|
|
if ( driveLayoutInfo &&
|
|
PARTITION_STYLE_MBR != driveLayoutInfo->PartitionStyle ) {
|
|
ClusDiskPrint(( 3,
|
|
"[ClusDisk] Skipping non-MBR disk device %ws \n",
|
|
targetDeviceName.Buffer ));
|
|
CDLOG( "ClusDiskScsiInitialize: Skipping non-MBR disk device %ws ",
|
|
targetDeviceName.Buffer );
|
|
|
|
FREE_DEVICE_NAME_BUFFER( targetDeviceName );
|
|
FREE_AND_NULL_PTR( driveLayoutInfo );
|
|
continue;
|
|
}
|
|
|
|
if ( Count &&
|
|
(status == STATUS_DEVICE_NOT_READY) ) {
|
|
ClusDiskRescan = TRUE;
|
|
ClusDiskRescanRetry = MAX_RESCAN_RETRIES;
|
|
}
|
|
|
|
//
|
|
// On failures, where we got a target device, always attach.
|
|
// Use a signature of zero.
|
|
//
|
|
signature = 0;
|
|
ClusDiskPrint((
|
|
1,
|
|
"[ClusDisk] Attach to device %ws anyway.\n",
|
|
targetDeviceName.Buffer ));
|
|
CDLOG( "ClusDiskScsiInitialize: Attach to device %ws using signature = 0 ",
|
|
targetDeviceName.Buffer );
|
|
|
|
goto Attach_Anyway;
|
|
}
|
|
|
|
if ( driveLayoutInfo == NULL ) {
|
|
FREE_DEVICE_NAME_BUFFER( targetDeviceName );
|
|
continue;
|
|
}
|
|
|
|
skipCount = 0; // Device found, reset skipCount
|
|
|
|
//
|
|
// Only process MBR disks.
|
|
//
|
|
|
|
if ( PARTITION_STYLE_MBR != driveLayoutInfo->PartitionStyle ) {
|
|
ClusDiskPrint(( 3,
|
|
"[ClusDisk] Skipping non-MBR disk device %ws \n",
|
|
targetDeviceName.Buffer ));
|
|
CDLOG( "ClusDiskScsiInitialize: Skipping non-MBR disk device %ws ",
|
|
targetDeviceName.Buffer );
|
|
|
|
FREE_DEVICE_NAME_BUFFER( targetDeviceName );
|
|
FREE_AND_NULL_PTR( driveLayoutInfo );
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Don't control disks that have no signature.
|
|
//
|
|
if ( 0 == driveLayoutInfo->Mbr.Signature ) {
|
|
FREE_DEVICE_NAME_BUFFER( targetDeviceName );
|
|
FREE_AND_NULL_PTR( driveLayoutInfo );
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// If this device is on the system bus... then skip it.
|
|
// Also... skip any device we're already attached to.
|
|
//
|
|
if ( ((SystemDiskPort == scsiAddress.PortNumber) &&
|
|
(SystemDiskPath == scsiAddress.PathId)) ||
|
|
(GetMediaType( targetDevice ) != FixedMedia) ||
|
|
ClusDiskAttached( targetDevice, diskNumber) ) {
|
|
FREE_DEVICE_NAME_BUFFER( targetDeviceName );
|
|
FREE_AND_NULL_PTR( driveLayoutInfo );
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Skip system disk.
|
|
//
|
|
|
|
if ( SystemDiskSignature == driveLayoutInfo->Mbr.Signature ) {
|
|
FREE_DEVICE_NAME_BUFFER( targetDeviceName );
|
|
FREE_AND_NULL_PTR( driveLayoutInfo );
|
|
continue;
|
|
}
|
|
|
|
#if 0
|
|
// Don't check for NTFS partitions.
|
|
|
|
//
|
|
// Look through the partition table and determine if all
|
|
// the partitions are NTFS. If not all NTFS, then we won't
|
|
// attach to this volume.
|
|
//
|
|
attachVolume = TRUE;
|
|
for (partIndex = 0;
|
|
partIndex < driveLayoutInfo->PartitionCount;
|
|
partIndex++)
|
|
{
|
|
|
|
partitionInfo = &driveLayoutInfo->PartitionEntry[partIndex];
|
|
|
|
|
|
if (!partitionInfo->Mbr.RecognizedPartition ||
|
|
partitionInfo->PartitionNumber == 0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if ( (partitionInfo->PartitionType & ~PARTITION_NTFT) != PARTITION_IFS ) {
|
|
attachVolume = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( !attachVolume ) {
|
|
FREE_DEVICE_NAME_BUFFER( targetDeviceName );
|
|
FREE_AND_NULL_PTR( driveLayoutInfo );
|
|
continue;
|
|
}
|
|
#endif
|
|
|
|
signature = driveLayoutInfo->Mbr.Signature;
|
|
|
|
Attach_Anyway:
|
|
|
|
skipCount = 0; // Device found, reset skipCount
|
|
|
|
//
|
|
// Create device object for partition 0.
|
|
//
|
|
|
|
if ( FAILED( StringCchPrintfW( clusdiskDeviceBuffer,
|
|
RTL_NUMBER_OF(clusdiskDeviceBuffer),
|
|
CLUSDISK_DEVICE_NAME,
|
|
diskNumber,
|
|
0 ) ) ) {
|
|
FREE_DEVICE_NAME_BUFFER( targetDeviceName );
|
|
FREE_AND_NULL_PTR( driveLayoutInfo );
|
|
continue;
|
|
}
|
|
|
|
RtlInitUnicodeString( &clusdiskDeviceName, clusdiskDeviceBuffer );
|
|
|
|
status = IoCreateDevice(DriverObject,
|
|
sizeof(CLUS_DEVICE_EXTENSION),
|
|
&clusdiskDeviceName,
|
|
FILE_DEVICE_DISK,
|
|
FILE_DEVICE_SECURE_OPEN,
|
|
FALSE,
|
|
&physicalDevice);
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
ClusDiskPrint((1, "[ClusDisk] ScsiInit: Failed to create device for Drive%u %08X\n",
|
|
diskNumber, status));
|
|
|
|
FREE_DEVICE_NAME_BUFFER( targetDeviceName );
|
|
FREE_AND_NULL_PTR( driveLayoutInfo );
|
|
continue;
|
|
}
|
|
|
|
CDLOG( "ClusDiskScsiInitialize: Created new device %p for disk %d partition 0 signature %08X ",
|
|
physicalDevice,
|
|
diskNumber,
|
|
signature );
|
|
|
|
physicalDevice->Flags |= DO_DIRECT_IO;
|
|
|
|
//
|
|
// Point device extension back at device object and remember
|
|
// the disk number.
|
|
//
|
|
deviceExtension = physicalDevice->DeviceExtension;
|
|
zeroExtension = deviceExtension;
|
|
deviceExtension->DeviceObject = physicalDevice;
|
|
deviceExtension->DiskNumber = diskNumber;
|
|
deviceExtension->LastPartitionNumber = 0;
|
|
deviceExtension->DriverObject = DriverObject;
|
|
deviceExtension->AttachValid = TRUE;
|
|
deviceExtension->ReserveTimer = 0;
|
|
deviceExtension->PerformReserves = TRUE;
|
|
deviceExtension->ReserveFailure = 0;
|
|
deviceExtension->Signature = signature;
|
|
deviceExtension->Detached = TRUE;
|
|
deviceExtension->OfflinePending = FALSE;
|
|
deviceExtension->ScsiAddress = scsiAddress;
|
|
deviceExtension->BusType = ScsiBus;
|
|
InitializeListHead( &deviceExtension->WaitingIoctls );
|
|
|
|
IoInitializeRemoveLock( &deviceExtension->RemoveLock, CLUSDISK_ALLOC_TAG, 0, 0 );
|
|
|
|
//
|
|
// Signal the worker thread running event.
|
|
//
|
|
KeInitializeEvent( &deviceExtension->Event, NotificationEvent, TRUE );
|
|
|
|
ExInitializeWorkItem(&deviceExtension->WorkItem,
|
|
(PWORKER_THREAD_ROUTINE)ClusDiskReservationWorker,
|
|
(PVOID)deviceExtension );
|
|
|
|
// Always mark disk offline. If disk is one we shouldn't control, then
|
|
// we will mark it online before exiting.
|
|
//
|
|
// We offline all the volumes later. For now, just mark the disk offline.
|
|
//
|
|
|
|
deviceExtension->DiskState = DiskOffline;
|
|
|
|
KeInitializeEvent( &deviceExtension->PagingPathCountEvent,
|
|
NotificationEvent, TRUE );
|
|
deviceExtension->PagingPathCount = 0;
|
|
deviceExtension->HibernationPathCount = 0;
|
|
deviceExtension->DumpPathCount = 0;
|
|
|
|
ExInitializeResourceLite( &deviceExtension->DriveLayoutLock );
|
|
ExInitializeResourceLite( &deviceExtension->ReserveInfoLock );
|
|
|
|
//
|
|
// This is the physical device object.
|
|
//
|
|
ObReferenceObject( physicalDevice );
|
|
deviceExtension->PhysicalDevice = physicalDevice;
|
|
|
|
#if 0 // Can't have a FS on partition 0
|
|
//
|
|
// Dismount any file system that might be hanging around
|
|
//
|
|
if ( targetDevice->Vpb &&
|
|
(targetDevice->Vpb->Flags & VPB_MOUNTED) ) {
|
|
|
|
status = DismountPartition( targetDevice, diskNumber, 0 );
|
|
|
|
if ( !NT_SUCCESS( status )) {
|
|
ClusDiskPrint((1,
|
|
"[ClusDisk] ScsiInit: dismount of %u/0 failed, %08X\n",
|
|
diskNumber, status));
|
|
}
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// Attach to partition0. This call links the newly created
|
|
// device to the target device, returning the target device object.
|
|
// We may not want to stay attached for long... depending on
|
|
// whether this is a device we're interested in.
|
|
//
|
|
|
|
attachedTargetDevice = IoAttachDeviceToDeviceStack(physicalDevice,
|
|
targetDevice);
|
|
|
|
#if CLUSTER_FREE_ASSERTS && CLUSTER_STALL_THREAD
|
|
DBG_STALL_THREAD( 2 ); // Defined only for debugging.
|
|
#endif
|
|
|
|
FREE_DEVICE_NAME_BUFFER( targetDeviceName );
|
|
|
|
deviceExtension->TargetDeviceObject = attachedTargetDevice;
|
|
deviceExtension->Detached = FALSE;
|
|
physicalDevice->Flags &= ~DO_DEVICE_INITIALIZING;
|
|
|
|
//
|
|
// Once attached, we always need to set this information.
|
|
//
|
|
|
|
if ( attachedTargetDevice ) {
|
|
|
|
//
|
|
// Propagate driver's alignment requirements and power flags
|
|
//
|
|
|
|
physicalDevice->AlignmentRequirement =
|
|
deviceExtension->TargetDeviceObject->AlignmentRequirement;
|
|
|
|
physicalDevice->SectorSize =
|
|
deviceExtension->TargetDeviceObject->SectorSize;
|
|
|
|
//
|
|
// The storage stack explicitly requires DO_POWER_PAGABLE to be
|
|
// set in all filter drivers *unless* DO_POWER_INRUSH is set.
|
|
// this is true even if the attached device doesn't set DO_POWER_PAGABLE.
|
|
//
|
|
if ( deviceExtension->TargetDeviceObject->Flags & DO_POWER_INRUSH) {
|
|
physicalDevice->Flags |= DO_POWER_INRUSH;
|
|
} else {
|
|
physicalDevice->Flags |= DO_POWER_PAGABLE;
|
|
}
|
|
|
|
}
|
|
|
|
if ( signature == 0 ) {
|
|
FREE_AND_NULL_PTR( driveLayoutInfo );
|
|
|
|
ClusDiskDismountDevice( diskNumber, FALSE );
|
|
|
|
//
|
|
// Tell partmgr to eject volumes to remove volumes for this disk. This
|
|
// should prevent I/O from bypassing the partition0 device when we couldn't
|
|
// get the drive layout.
|
|
//
|
|
|
|
EjectVolumes( deviceExtension->DeviceObject );
|
|
|
|
continue;
|
|
}
|
|
|
|
if ( attachedTargetDevice == NULL ) {
|
|
ClusDiskPrint((1,
|
|
"[ClusDisk] ScsiInit: Failed to attach to device Drive%u\n",
|
|
diskNumber));
|
|
|
|
ClusDiskDeleteDevice(physicalDevice);
|
|
FREE_AND_NULL_PTR( driveLayoutInfo );
|
|
continue;
|
|
}
|
|
ASSERT( attachedTargetDevice == targetDevice );
|
|
|
|
//
|
|
// If we're attaching to a file system device, then return
|
|
// now. We must do this check after the dismount!
|
|
//
|
|
if (deviceExtension->TargetDeviceObject->DeviceType == FILE_DEVICE_DISK_FILE_SYSTEM) {
|
|
goto skip_this_physical_device_with_info;
|
|
}
|
|
|
|
//
|
|
// Add this this device and bus to our list of devices/busses.
|
|
//
|
|
// This still isn't the correct place to do this. The
|
|
// available disk list is built at the end of this function by
|
|
// looking at the signatures and noting which ones are NOT in
|
|
// the device list. If so, then that signature is added to the
|
|
// available device registry key. By call AddAttachedDevice at
|
|
// this point, the signature is always present on the list. If
|
|
// we add the call after the following if clause, then the code
|
|
// at the end of the function will still fail. This should probably
|
|
// be changed to add the device to the available device list when
|
|
// the following if fails.
|
|
//
|
|
// AddAttachedDevice( deviceExtension->Signature, physicalDevice );
|
|
|
|
//
|
|
// If the signature does not match one that we should really attach
|
|
// to, then just mark it as not attached.
|
|
//
|
|
if ( !MatchDevice( deviceExtension->Signature, NULL ) ) {
|
|
|
|
ClusDiskPrint((3,
|
|
"[ClusDisk] ScsiInit: adding disk %u (%08X) to available disks list\n",
|
|
diskNumber, driveLayoutInfo->Mbr.Signature));
|
|
|
|
//
|
|
// Create the signature key using the available name.
|
|
//
|
|
status = ClusDiskAddSignature(&availableName,
|
|
driveLayoutInfo->Mbr.Signature,
|
|
TRUE);
|
|
|
|
//
|
|
// Detach from the target device. This only requires marking
|
|
// the device object as detached!
|
|
//
|
|
deviceExtension->Detached = TRUE;
|
|
|
|
//
|
|
// Make this device available again.
|
|
// Don't need to stop reserves because reserves not yet started.
|
|
//
|
|
// deviceExtension->DiskState = DiskOnline;
|
|
ONLINE_DISK( deviceExtension );
|
|
|
|
FREE_AND_NULL_PTR( driveLayoutInfo );
|
|
|
|
continue;
|
|
//goto skip_this_physical_device_with_info;
|
|
}
|
|
|
|
//
|
|
// add this disk to devices we're controlling
|
|
//
|
|
AddAttachedDevice( deviceExtension->Signature, physicalDevice );
|
|
|
|
//
|
|
// Now open the actual signature key. Using original key name.
|
|
//
|
|
|
|
signatureKeyName.Length = 0;
|
|
signatureKeyName.MaximumLength = sizeof(signatureKeyBuffer);
|
|
signatureKeyName.Buffer = signatureKeyBuffer;
|
|
|
|
RtlCopyUnicodeString( &signatureKeyName, &keyName );
|
|
|
|
//
|
|
// Create device name for the physical disk we just attached.
|
|
//
|
|
|
|
if ( FAILED( StringCchPrintfW( signatureBuffer,
|
|
RTL_NUMBER_OF(signatureBuffer),
|
|
L"\\%08lX",
|
|
deviceExtension->Signature ) ) ) {
|
|
FREE_AND_NULL_PTR( driveLayoutInfo );
|
|
continue;
|
|
}
|
|
|
|
WCSLEN_ASSERT( signatureBuffer );
|
|
|
|
RtlAppendUnicodeToString( &signatureKeyName, signatureBuffer );
|
|
signatureKeyName.Buffer[ signatureKeyName.Length / sizeof(WCHAR) ] = UNICODE_NULL;
|
|
|
|
//
|
|
// Setup the object attributes for the Parameters\Signatures\xyz key.
|
|
//
|
|
|
|
InitializeObjectAttributes(
|
|
&objectAttributes,
|
|
&signatureKeyName,
|
|
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
//
|
|
// Open Parameters\Signatures\xyz Key.
|
|
//
|
|
|
|
status = ZwOpenKey(
|
|
&signatureHandle,
|
|
KEY_READ | KEY_WRITE,
|
|
&objectAttributes
|
|
);
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
ClusDiskPrint((
|
|
1,
|
|
"[ClusDisk] ScsiInit: Failed to open %wZ registry key. Status: %lx\n",
|
|
&signatureKeyName,
|
|
status
|
|
));
|
|
FREE_AND_NULL_PTR( driveLayoutInfo );
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Write the disk name.
|
|
//
|
|
status = ClusDiskAddDiskName( signatureHandle, diskNumber );
|
|
|
|
ZwClose( signatureHandle );
|
|
|
|
//
|
|
// Offline all volumes for this disk.
|
|
// First, send offline directly to the PDO of the volume stack to block I/O.
|
|
// Before we bring the volume online, we always send an offline to all
|
|
// the volumes in the stack, so volsnap can clean up.
|
|
//
|
|
|
|
OFFLINE_DISK_PDO( deviceExtension );
|
|
|
|
//
|
|
// Dismount all volumes on this disk.
|
|
//
|
|
|
|
ClusDiskDismountDevice( diskNumber, TRUE );
|
|
|
|
#if 0 // Removed 2/27/2001
|
|
//
|
|
// Called only for physical devices (partition0).
|
|
//
|
|
|
|
EjectVolumes( deviceExtension->DeviceObject );
|
|
ReclaimVolumes( deviceExtension->DeviceObject );
|
|
#endif
|
|
|
|
//
|
|
// Now enumerate the partitions on this device in order to
|
|
// attach a ClusDisk device object to each partition device object.
|
|
//
|
|
|
|
for (partIndex = 0;
|
|
partIndex < driveLayoutInfo->PartitionCount;
|
|
partIndex++)
|
|
{
|
|
|
|
partitionInfo = &driveLayoutInfo->PartitionEntry[partIndex];
|
|
|
|
//
|
|
// Make sure that there really is a partition here.
|
|
//
|
|
|
|
if (!partitionInfo->Mbr.RecognizedPartition ||
|
|
partitionInfo->PartitionNumber == 0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
CreateVolumeObject( zeroExtension,
|
|
diskNumber,
|
|
partitionInfo->PartitionNumber,
|
|
NULL );
|
|
|
|
}
|
|
|
|
FREE_AND_NULL_PTR( driveLayoutInfo );
|
|
continue;
|
|
|
|
skip_this_physical_device_with_info:
|
|
|
|
FREE_AND_NULL_PTR( driveLayoutInfo );
|
|
|
|
//skip_this_physical_device:
|
|
|
|
deviceExtension->Detached = TRUE;
|
|
IoDetachDevice( deviceExtension->TargetDeviceObject );
|
|
ClusDiskDeleteDevice( physicalDevice );
|
|
|
|
}
|
|
|
|
ExFreePool( keyName.Buffer );
|
|
|
|
//
|
|
// Find all available disk devices. These are devices that do not reside
|
|
// on the system bus and the signature is not part of the Signatures list.
|
|
//
|
|
|
|
for (diskNumber = 0;
|
|
diskNumber < configurationInformation->DiskCount;
|
|
diskNumber++) {
|
|
|
|
//
|
|
// Create device name for the physical disk.
|
|
//
|
|
DEREFERENCE_OBJECT( targetDevice );
|
|
status = ClusDiskGetTargetDevice( diskNumber,
|
|
0,
|
|
NULL,
|
|
&targetDeviceName,
|
|
&driveLayoutInfo,
|
|
&scsiAddress,
|
|
FALSE );
|
|
if ( !NT_SUCCESS(status) ) {
|
|
FREE_DEVICE_NAME_BUFFER( targetDeviceName );
|
|
FREE_AND_NULL_PTR( driveLayoutInfo );
|
|
continue;
|
|
}
|
|
|
|
if ( driveLayoutInfo == NULL ) {
|
|
FREE_DEVICE_NAME_BUFFER( targetDeviceName );
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Don't control disks that have no signature or system disk.
|
|
//
|
|
if ( ( 0 == driveLayoutInfo->Mbr.Signature ) ||
|
|
( SystemDiskSignature == driveLayoutInfo->Mbr.Signature ) ) {
|
|
FREE_DEVICE_NAME_BUFFER( targetDeviceName );
|
|
FREE_AND_NULL_PTR( driveLayoutInfo );
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Now write the signature to the list of available disks,
|
|
// if the signature does not match one we already have and
|
|
// the device is not on the system bus.
|
|
//
|
|
if ( !MatchDevice(driveLayoutInfo->Mbr.Signature, &deviceObject) &&
|
|
((SystemDiskPort != scsiAddress.PortNumber) ||
|
|
(SystemDiskPath != scsiAddress.PathId)) ) {
|
|
|
|
ClusDiskPrint((3,
|
|
"[ClusDisk] ScsiInit: adding disk %u (%08X) to available disks list\n",
|
|
diskNumber, driveLayoutInfo->Mbr.Signature));
|
|
|
|
//
|
|
// Create the signature key. Using the available name.
|
|
//
|
|
status = ClusDiskAddSignature(&availableName,
|
|
driveLayoutInfo->Mbr.Signature,
|
|
TRUE);
|
|
|
|
//
|
|
// Make sure this device comes online.
|
|
//
|
|
if ( deviceObject ) {
|
|
deviceExtension = deviceObject->DeviceExtension;
|
|
deviceExtension->Detached = TRUE;
|
|
// deviceExtension->DiskState = DiskOnline;
|
|
ONLINE_DISK( deviceExtension );
|
|
}
|
|
}
|
|
FREE_DEVICE_NAME_BUFFER( targetDeviceName );
|
|
FREE_AND_NULL_PTR( driveLayoutInfo );
|
|
}
|
|
|
|
ExFreePool( availableName.Buffer );
|
|
|
|
DEREFERENCE_OBJECT( targetDevice );
|
|
|
|
} // ClusDiskScsiInitialize
|
|
|
|
|
|
NTSTATUS
|
|
CreateVolumeObject(
|
|
PCLUS_DEVICE_EXTENSION ZeroExtension,
|
|
ULONG DiskNumber,
|
|
ULONG PartitionNumber,
|
|
PDEVICE_OBJECT TargetDev
|
|
)
|
|
{
|
|
PDEVICE_OBJECT targetDevice = NULL;
|
|
PDEVICE_OBJECT deviceObject;
|
|
PDEVICE_OBJECT attachedTargetDevice;
|
|
|
|
PCLUS_DEVICE_EXTENSION deviceExtension;
|
|
|
|
NTSTATUS status = STATUS_UNSUCCESSFUL;
|
|
|
|
UNICODE_STRING targetDeviceName;
|
|
UNICODE_STRING clusdiskDeviceName;
|
|
|
|
WCHAR clusdiskDeviceBuffer[MAX_CLUSDISK_DEVICE_NAME_LENGTH];
|
|
|
|
CDLOG( "CreateVolumeObject: Entry ZeroExt %p disk %d/%d TargetDev %p",
|
|
ZeroExtension,
|
|
DiskNumber,
|
|
PartitionNumber,
|
|
TargetDev );
|
|
|
|
targetDeviceName.Buffer = NULL;
|
|
|
|
//
|
|
// If caller specified a target device, use it. The caller is responsible
|
|
// for taking a reference and releasing the reference when the caller is done.
|
|
//
|
|
|
|
if ( TargetDev ) {
|
|
|
|
targetDevice = TargetDev;
|
|
|
|
} else {
|
|
|
|
//
|
|
// Create device name for partition.
|
|
//
|
|
status = ClusDiskGetTargetDevice( DiskNumber,
|
|
PartitionNumber,
|
|
&targetDevice,
|
|
&targetDeviceName,
|
|
NULL,
|
|
NULL,
|
|
FALSE );
|
|
if ( !NT_SUCCESS(status) ) {
|
|
ClusDiskPrint((1,
|
|
"[ClusDisk] CreateVolumeObject: couldn't attach to disk %u/%u, status %08X\n",
|
|
DiskNumber,
|
|
PartitionNumber,
|
|
status));
|
|
|
|
CDLOG( "CreateVolumeObject: Couldn't attach to disk %d/%d, status %08X",
|
|
DiskNumber,
|
|
PartitionNumber,
|
|
status );
|
|
goto FnExit;
|
|
}
|
|
}
|
|
|
|
//
|
|
// The device name string won't be created if caller passed in the target
|
|
// device object. Don't use device name string in following logging statements.
|
|
//
|
|
|
|
#if 0
|
|
// Don't care if we are already attached. When we try to create the disk
|
|
// object, if it already exists, then we know we are already attached.
|
|
// The call to ClusDiskAttached doesn't work anyway, because the clusdisk
|
|
// disk object responds even if the clusdisk volume objects don't exist.
|
|
|
|
//
|
|
// Make sure we're not attached here!
|
|
//
|
|
if ( ClusDiskAttached( targetDevice, DiskNumber ) ) {
|
|
// really hosed!
|
|
|
|
ClusDiskPrint(( 1,
|
|
"[ClusDisk] CreateVolumeObject: Previously attached to disk %u/%u \n",
|
|
DiskNumber,
|
|
PartitionNumber ));
|
|
CDLOG( "CreateVolumeObject: Previously attached to disk %u/%u ",
|
|
DiskNumber,
|
|
PartitionNumber );
|
|
|
|
status = STATUS_UNSUCCESSFUL;
|
|
goto FnExit;
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// Check if this device is a file system device.
|
|
//
|
|
if ( targetDevice->DeviceType == FILE_DEVICE_DISK_FILE_SYSTEM ) {
|
|
|
|
//
|
|
// Can't attach to a device that is already mounted.
|
|
//
|
|
ClusDiskPrint(( 1,
|
|
"[ClusDisk] CreateVolumeObject: Attempted to attach to FS disk %u/%u \n",
|
|
DiskNumber,
|
|
PartitionNumber ));
|
|
CDLOG( "CreateVolumeObject: Attempted to attach to FS disk %u/%u ",
|
|
DiskNumber,
|
|
PartitionNumber );
|
|
|
|
status = STATUS_UNSUCCESSFUL;
|
|
goto FnExit;
|
|
}
|
|
|
|
//
|
|
// Create device object for this partition.
|
|
//
|
|
|
|
if ( FAILED( StringCchPrintfW( clusdiskDeviceBuffer,
|
|
RTL_NUMBER_OF(clusdiskDeviceBuffer),
|
|
CLUSDISK_DEVICE_NAME,
|
|
DiskNumber,
|
|
PartitionNumber ) ) ) {
|
|
status = STATUS_UNSUCCESSFUL;
|
|
goto FnExit;
|
|
}
|
|
|
|
WCSLEN_ASSERT( clusdiskDeviceBuffer );
|
|
|
|
RtlInitUnicodeString( &clusdiskDeviceName, clusdiskDeviceBuffer );
|
|
|
|
status = IoCreateDevice(ZeroExtension->DriverObject,
|
|
sizeof(CLUS_DEVICE_EXTENSION),
|
|
&clusdiskDeviceName,
|
|
FILE_DEVICE_DISK,
|
|
FILE_DEVICE_SECURE_OPEN,
|
|
FALSE,
|
|
&deviceObject);
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
|
|
//
|
|
// If the previous clusdisk volume object existed, this should correctly
|
|
// fail with c0000035 STATUS_OBJECT_NAME_COLLISION.
|
|
//
|
|
goto FnExit;
|
|
}
|
|
|
|
CDLOG( "CreateVolumeObject: Created new device %p for disk %d partition %d signature %08X ",
|
|
deviceObject,
|
|
DiskNumber,
|
|
PartitionNumber,
|
|
ZeroExtension->Signature );
|
|
|
|
deviceObject->Flags |= DO_DIRECT_IO;
|
|
|
|
//
|
|
// Point device extension back at device object and
|
|
// remember the disk number.
|
|
//
|
|
|
|
deviceExtension = deviceObject->DeviceExtension;
|
|
deviceExtension->DeviceObject = deviceObject;
|
|
deviceExtension->DiskNumber = DiskNumber;
|
|
deviceExtension->DriverObject = ZeroExtension->DriverObject;
|
|
deviceExtension->AttachValid = TRUE;
|
|
deviceExtension->BusType = ScsiBus;
|
|
deviceExtension->PerformReserves = FALSE;
|
|
deviceExtension->ReserveFailure = 0;
|
|
deviceExtension->Signature = ZeroExtension->Signature;
|
|
deviceExtension->ScsiAddress = ZeroExtension->ScsiAddress;
|
|
deviceExtension->Detached = TRUE;
|
|
deviceExtension->OfflinePending = FALSE;
|
|
deviceExtension->DiskState = ZeroExtension->DiskState;
|
|
InitializeListHead( &deviceExtension->WaitingIoctls );
|
|
|
|
IoInitializeRemoveLock( &deviceExtension->RemoveLock, CLUSDISK_ALLOC_TAG, 0, 0 );
|
|
|
|
//
|
|
// Signal the worker thread running event.
|
|
//
|
|
KeInitializeEvent( &deviceExtension->Event, NotificationEvent, TRUE );
|
|
|
|
//
|
|
// Maintain the last partition number created. Put it in
|
|
// each extension just to initialize the field.
|
|
//
|
|
|
|
deviceExtension->LastPartitionNumber = max(deviceExtension->LastPartitionNumber,
|
|
PartitionNumber);
|
|
|
|
ZeroExtension->LastPartitionNumber = deviceExtension->LastPartitionNumber;
|
|
|
|
KeInitializeEvent( &deviceExtension->PagingPathCountEvent,
|
|
NotificationEvent, TRUE );
|
|
deviceExtension->PagingPathCount = 0;
|
|
deviceExtension->HibernationPathCount = 0;
|
|
deviceExtension->DumpPathCount = 0;
|
|
|
|
ExInitializeResourceLite( &deviceExtension->DriveLayoutLock );
|
|
ExInitializeResourceLite( &deviceExtension->ReserveInfoLock );
|
|
|
|
//
|
|
// Store pointer to physical device.
|
|
//
|
|
ObReferenceObject( ZeroExtension->PhysicalDevice );
|
|
deviceExtension->PhysicalDevice = ZeroExtension->PhysicalDevice;
|
|
|
|
CDLOG( "ClusDiskInfo *** Phys DO %p DevExt %p DiskNum %d Signature %08X RemLock %p ***",
|
|
deviceObject,
|
|
deviceExtension,
|
|
deviceExtension->DiskNumber,
|
|
deviceExtension->Signature,
|
|
&deviceExtension->RemoveLock );
|
|
|
|
//
|
|
// First dismount any mounted file systems.
|
|
//
|
|
if ( targetDevice->Vpb &&
|
|
(targetDevice->Vpb->Flags & VPB_MOUNTED) ) {
|
|
|
|
status = DismountPartition( targetDevice,
|
|
DiskNumber,
|
|
PartitionNumber);
|
|
|
|
if ( !NT_SUCCESS( status )) {
|
|
ClusDiskPrint((1,
|
|
"[ClusDisk] CreateVolumeObject: dismount of disk %u/%u failed %08X\n",
|
|
DiskNumber, PartitionNumber, status));
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Attach to the partition. This call links the newly created
|
|
// device to the target device, returning the target device object.
|
|
//
|
|
ClusDiskPrint(( 3,
|
|
"[ClusDisk] CreateVolumeObject: attaching to disk %u/%u \n",
|
|
DiskNumber,
|
|
PartitionNumber ));
|
|
|
|
attachedTargetDevice = IoAttachDeviceToDeviceStack(deviceObject,
|
|
targetDevice );
|
|
|
|
#if CLUSTER_FREE_ASSERTS && CLUSTER_STALL_THREAD
|
|
DBG_STALL_THREAD( 2 ); // Defined only for debugging.
|
|
#endif
|
|
|
|
deviceExtension->Detached = ZeroExtension->Detached;
|
|
ASSERT( attachedTargetDevice == targetDevice );
|
|
|
|
if ( attachedTargetDevice == NULL ) {
|
|
ClusDiskPrint(( 1,
|
|
"[ClusDisk] CreateVolumeObject: Failed to attach to disk %u/%u \n",
|
|
DiskNumber,
|
|
PartitionNumber ));
|
|
|
|
ClusDiskDeleteDevice(deviceObject);
|
|
status = STATUS_UNSUCCESSFUL;
|
|
goto FnExit;
|
|
}
|
|
|
|
deviceExtension->TargetDeviceObject = attachedTargetDevice;
|
|
deviceExtension->Detached = FALSE;
|
|
|
|
//
|
|
// Propagate driver's alignment requirements and power flags.
|
|
//
|
|
|
|
deviceObject->AlignmentRequirement =
|
|
deviceExtension->TargetDeviceObject->AlignmentRequirement;
|
|
|
|
deviceObject->SectorSize =
|
|
deviceExtension->TargetDeviceObject->SectorSize;
|
|
|
|
//
|
|
// The storage stack explicitly requires DO_POWER_PAGABLE to be
|
|
// set in all filter drivers *unless* DO_POWER_INRUSH is set.
|
|
// this is true even if the attached device doesn't set DO_POWER_PAGABLE.
|
|
//
|
|
if ( deviceExtension->TargetDeviceObject->Flags & DO_POWER_INRUSH) {
|
|
deviceObject->Flags |= DO_POWER_INRUSH;
|
|
} else {
|
|
deviceObject->Flags |= DO_POWER_PAGABLE;
|
|
}
|
|
|
|
//
|
|
// Safe to dismount now that we're attached. This should cause
|
|
// the next IO to attach the FS to our device.
|
|
//
|
|
|
|
// Why do we dismount twice?
|
|
|
|
if ( targetDevice->Vpb ) {
|
|
if ( targetDevice->Vpb->Flags & VPB_MOUNTED ) {
|
|
|
|
ClusDiskPrint((1,
|
|
"[ClusDisk] CreateVolumeObject: disk %u/%u is Mounted!\n",
|
|
DiskNumber, PartitionNumber));
|
|
|
|
status = DismountPartition( targetDevice,
|
|
DiskNumber,
|
|
PartitionNumber);
|
|
|
|
if ( !NT_SUCCESS( status )) {
|
|
ClusDiskPrint((1,
|
|
"[ClusDisk] CreateVolumeObject: dismount of disk %u/%u failed %08X\n",
|
|
DiskNumber, PartitionNumber, status));
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Clear the flag so the device can be used.
|
|
|
|
deviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
|
|
|
|
FnExit:
|
|
|
|
//
|
|
// Only dereference the target device if the caller didn't specify it.
|
|
// The target device name is only created if caller didn't specify the
|
|
// target device.
|
|
//
|
|
|
|
if ( !TargetDev ) {
|
|
DEREFERENCE_OBJECT( targetDevice );
|
|
}
|
|
|
|
FREE_DEVICE_NAME_BUFFER( targetDeviceName );
|
|
|
|
|
|
#if CLUSTER_FREE_ASSERTS
|
|
if ( !NT_SUCCESS( status ) ) {
|
|
DbgPrint( "[ClusDisk] CreateVolumeObject failed: %08X \n", status );
|
|
|
|
//
|
|
// If we are in the volume notification thread, the TargetDev parameter
|
|
// will be non-zero. In this case, the only "acceptable" failure is
|
|
// STATUS_OBJECT_NAME_COLLISION.
|
|
//
|
|
|
|
if ( TargetDev && STATUS_OBJECT_NAME_COLLISION != status ) {
|
|
DbgBreakPoint();
|
|
}
|
|
}
|
|
#endif
|
|
|
|
CDLOG( "CreateVolumeObject: Exit ZeroExt %p disk %d/%d TargetDev %p status %08X",
|
|
ZeroExtension,
|
|
DiskNumber,
|
|
PartitionNumber,
|
|
TargetDev,
|
|
status );
|
|
|
|
ClusDiskPrint(( 3,
|
|
"[ClusDisk] CreateVolumeObject: Exit disk %d/%d status %08X \n",
|
|
DiskNumber,
|
|
PartitionNumber,
|
|
status ));
|
|
|
|
return status;
|
|
|
|
} // CreateVolumeObject
|
|
|
|
|
|
#if 0 // This code cannot be used!
|
|
|
|
VOID
|
|
ClusDiskUnload(
|
|
IN PDRIVER_OBJECT DriverObject
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine cleans up all memmory allocations and detaches from each
|
|
target device.
|
|
|
|
Arguments:
|
|
|
|
DriverObject - a pointer to the driver object to unload.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
Note - we should acquire the ClusDiskSpinLock, but then this code is NOT
|
|
pageable!
|
|
|
|
--*/
|
|
|
|
{
|
|
PCONFIGURATION_INFORMATION configurationInformation;
|
|
PDEVICE_OBJECT deviceObject = DriverObject->DeviceObject;
|
|
PCLUS_DEVICE_EXTENSION deviceExtension;
|
|
PDEVICE_LIST_ENTRY deviceEntry;
|
|
PSCSI_BUS_ENTRY scsiBusEntry;
|
|
PVOID freeBlock;
|
|
PLIST_ENTRY listEntry;
|
|
PIRP irp;
|
|
ULONG diskNumber;
|
|
NTSTATUS status;
|
|
|
|
PAGED_CODE();
|
|
|
|
#if 0 // Moved to IRP_PNP_MN_REMOVE handler
|
|
|
|
if ( RootDeviceObject ) {
|
|
deviceExtension = RootDeviceObject->DeviceExtension;
|
|
status = IoUnregisterPlugPlayNotification(
|
|
deviceExtension->DiskNotificationEntry);
|
|
|
|
RootDeviceObject = NULL;
|
|
}
|
|
|
|
//
|
|
// Free all device entries..
|
|
//
|
|
|
|
deviceEntry = ClusDiskDeviceList;
|
|
while ( deviceEntry ) {
|
|
freeBlock = deviceEntry;
|
|
deviceEntry = deviceEntry->Next;
|
|
ExFreePool( freeBlock );
|
|
}
|
|
ClusDiskDeviceList = NULL;
|
|
|
|
#endif
|
|
|
|
//
|
|
// 2000/02/04: stevedz - With PnP, the following loop is not required.
|
|
// When we get unload working, it will be removed.
|
|
//
|
|
|
|
#if 0
|
|
//
|
|
// Loop through all device objects detaching...
|
|
//
|
|
// On NT4 - Need SpinLocks! The DriverObject->DeviceObject list is not synchronized!
|
|
// On Win2000, PnP will already have cleaned this up
|
|
//
|
|
while ( deviceObject ) {
|
|
deviceExtension = deviceObject->DeviceExtension;
|
|
//
|
|
// Signal all waiting Irp's on the physical device extension.
|
|
//
|
|
while ( !IsListEmpty(&deviceExtension->WaitingIoctls) ) {
|
|
listEntry = RemoveHeadList(&deviceExtension->WaitingIoctls);
|
|
irp = CONTAINING_RECORD( listEntry,
|
|
IRP,
|
|
Tail.Overlay.ListEntry );
|
|
ClusDiskCompletePendingRequest(irp, STATUS_SUCCESS, deviceExtension);
|
|
}
|
|
|
|
if ( deviceExtension->BusType != RootBus ) {
|
|
IoDetachDevice( deviceExtension->TargetDeviceObject );
|
|
}
|
|
|
|
if ( deviceExtension->BusType == RootBus ) {
|
|
IoStopTimer( deviceObject );
|
|
}
|
|
|
|
ExDeleteResourceLite( &deviceExtension->DriveLayoutLock );
|
|
ExDeleteResourceLite( &deviceExtension->ReserveInfoLock );
|
|
IoDeleteDevice( deviceObject );
|
|
deviceObject = DriverObject->DeviceObject;
|
|
}
|
|
#endif
|
|
|
|
#if 0
|
|
//
|
|
// dismount all FS so we can free up references to our dev objs
|
|
//
|
|
|
|
configurationInformation = IoGetConfigurationInformation();
|
|
|
|
for (diskNumber = 0;
|
|
diskNumber < configurationInformation->DiskCount;
|
|
diskNumber++)
|
|
{
|
|
ClusDiskDismountDevice( diskNumber, TRUE );
|
|
}
|
|
#endif
|
|
|
|
ArbitrationDone();
|
|
ExDeleteResourceLite(&ClusDiskDeviceListLock);
|
|
|
|
if ( ClusDiskRegistryPath.Buffer ) {
|
|
ExFreePool( ClusDiskRegistryPath.Buffer );
|
|
ClusDiskRegistryPath.Buffer = NULL;
|
|
}
|
|
|
|
if ( gArbitrationBuffer ) {
|
|
ExFreePool( gArbitrationBuffer );
|
|
gArbitrationBuffer = NULL;
|
|
}
|
|
|
|
WPP_CLEANUP(DriverObject);
|
|
|
|
} // ClusDiskUnload
|
|
#endif
|
|
|
|
|
|
|
|
NTSTATUS
|
|
ClusDiskCreate(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine services open requests. It establishes
|
|
the driver's existance by returning status success.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Context for the activity.
|
|
Irp - The device control argument block.
|
|
|
|
Return Value:
|
|
|
|
NT Status
|
|
|
|
--*/
|
|
|
|
{
|
|
PCLUS_DEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
|
|
PCLUS_DEVICE_EXTENSION physicalExtension =
|
|
deviceExtension->PhysicalDevice->DeviceExtension;
|
|
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
|
|
NTSTATUS status;
|
|
|
|
CDLOGF(CREATE,"ClusDiskCreate: Entry DO %p", DeviceObject);
|
|
|
|
status = AcquireRemoveLock(&deviceExtension->RemoveLock, Irp);
|
|
if ( !NT_SUCCESS(status) ) {
|
|
Irp->IoStatus.Status = status;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return status;
|
|
}
|
|
|
|
status = AcquireRemoveLock(&physicalExtension->RemoveLock, Irp);
|
|
if ( !NT_SUCCESS(status) ) {
|
|
ReleaseRemoveLock(&deviceExtension->RemoveLock, Irp);
|
|
Irp->IoStatus.Status = status;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return status;
|
|
}
|
|
|
|
//
|
|
// Make sure that the user is not attempting to sneak around the
|
|
// security checks. Make sure that FileObject->RelatedFileObject is
|
|
// NULL and that the FileName length is zero!
|
|
//
|
|
if ( irpStack->FileObject->RelatedFileObject ||
|
|
irpStack->FileObject->FileName.Length ) {
|
|
ReleaseRemoveLock(&physicalExtension->RemoveLock, Irp);
|
|
ReleaseRemoveLock(&deviceExtension->RemoveLock, Irp);
|
|
Irp->IoStatus.Status = STATUS_ACCESS_DENIED;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return STATUS_ACCESS_DENIED;
|
|
}
|
|
|
|
//
|
|
// if our dev object for partition 0 is offline, clear the
|
|
// FS context. If we've been asked to create a directory file,
|
|
// fail the request.
|
|
//
|
|
if ( physicalExtension->DiskState == DiskOffline ) {
|
|
|
|
//
|
|
// [GORN] Why do we do this here?
|
|
// ClusDiskCreate is called when FileObject is created,
|
|
// so nobody has been able to put anything into FsContext field yet.
|
|
//
|
|
CDLOGF(CREATE,"ClusDiskCreate: RefTrack(%p)", irpStack->FileObject->FsContext );
|
|
irpStack->FileObject->FsContext = NULL;
|
|
if ( irpStack->Parameters.Create.Options & FILE_DIRECTORY_FILE ) {
|
|
ReleaseRemoveLock(&physicalExtension->RemoveLock, Irp);
|
|
ReleaseRemoveLock(&deviceExtension->RemoveLock, Irp);
|
|
Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return(STATUS_INVALID_DEVICE_REQUEST);
|
|
}
|
|
}
|
|
|
|
CDLOGF(CREATE,"ClusDiskCreate: Exit DO %p", DeviceObject );
|
|
|
|
ReleaseRemoveLock(&physicalExtension->RemoveLock, Irp);
|
|
ReleaseRemoveLock(&deviceExtension->RemoveLock, Irp);
|
|
|
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return STATUS_SUCCESS;
|
|
|
|
} // ClusDiskCreate
|
|
|
|
|
|
|
|
NTSTATUS
|
|
ClusDiskClose(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine services close commands. It destroys the file object
|
|
context.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Pointer to the device object on which the irp was received.
|
|
Irp - The IRP.
|
|
|
|
Return Value:
|
|
|
|
NT Status
|
|
|
|
--*/
|
|
|
|
{
|
|
PCLUS_DEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
|
|
PCLUS_DEVICE_EXTENSION physicalExtension =
|
|
deviceExtension->PhysicalDevice->DeviceExtension;
|
|
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
|
|
|
|
NTSTATUS status;
|
|
|
|
status = AcquireRemoveLock(&deviceExtension->RemoveLock, Irp);
|
|
if ( !NT_SUCCESS(status) ) {
|
|
Irp->IoStatus.Status = status;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return status;
|
|
}
|
|
|
|
status = AcquireRemoveLock(&physicalExtension->RemoveLock, Irp);
|
|
if ( !NT_SUCCESS(status) ) {
|
|
ReleaseRemoveLock(&deviceExtension->RemoveLock, Irp);
|
|
Irp->IoStatus.Status = status;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return status;
|
|
}
|
|
|
|
if ( physicalExtension->DiskState == DiskOffline ) {
|
|
//
|
|
// [GORN] Cleanup cleans the FsContext, by the time
|
|
// we will get here, FsContext should be already NULL
|
|
//
|
|
CDLOGF(CLOSE,"ClusDiskClose: RefTrack %p", irpStack->FileObject->FsContext );
|
|
irpStack->FileObject->FsContext = NULL;
|
|
}
|
|
|
|
CDLOGF(CLOSE,"ClusDiskClose DO %p", DeviceObject );
|
|
|
|
//
|
|
// Release the RemoveLocks with the FileObject tag, not the IRP.
|
|
//
|
|
|
|
ReleaseRemoveLock(&physicalExtension->RemoveLock, Irp);
|
|
ReleaseRemoveLock(&deviceExtension->RemoveLock, Irp);
|
|
|
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return STATUS_SUCCESS;
|
|
|
|
} // ClusDiskClose
|
|
|
|
|
|
VOID
|
|
ClusDiskCompletePendedIrps(
|
|
IN PCLUS_DEVICE_EXTENSION DeviceExtension,
|
|
IN PFILE_OBJECT FileObject OPTIONAL,
|
|
IN ULONG Offline
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine completes all pended irps belonging
|
|
to a FileObject specified. If FileObject is 0, all
|
|
irps pended on the DeviceExtension are completed.
|
|
|
|
Arguments:
|
|
|
|
DeviceExtension -
|
|
FileObject -
|
|
Offline - if TRUE, set DiskState to Offline
|
|
|
|
--*/
|
|
{
|
|
KIRQL irql;
|
|
PIRP irp;
|
|
PLIST_ENTRY listEntry;
|
|
PLIST_ENTRY nextEntry, head;
|
|
|
|
CDLOGF(UNPEND, "CompletePendedIrps: Entry DevExt %p FileObject %p",
|
|
DeviceExtension, FileObject );
|
|
|
|
if (Offline) {
|
|
|
|
PCLUS_DEVICE_EXTENSION physicalDisk = DeviceExtension->PhysicalDevice->DeviceExtension;
|
|
|
|
CDLOG( "CompletePendedIrps: StateOffline PhysDevObj %p",
|
|
physicalDisk->DeviceObject);
|
|
|
|
ClusDiskPrint(( 3,
|
|
"[ClusDisk] Pending IRPS: Offline device %p \n",
|
|
physicalDisk->DeviceObject ));
|
|
|
|
DeviceExtension->DiskState = DiskOffline;
|
|
DeviceExtension->ReserveTimer = 0;
|
|
// physicalDisk->DiskState = DiskOffline;
|
|
OFFLINE_DISK( physicalDisk );
|
|
physicalDisk->ReserveTimer = 0;
|
|
|
|
}
|
|
|
|
IoAcquireCancelSpinLock( &irql );
|
|
KeAcquireSpinLockAtDpcLevel(&ClusDiskSpinLock);
|
|
|
|
head = &DeviceExtension->WaitingIoctls;
|
|
|
|
for (listEntry = head->Flink; listEntry != head; listEntry = nextEntry) {
|
|
nextEntry = listEntry->Flink;
|
|
|
|
irp = CONTAINING_RECORD( listEntry,
|
|
IRP,
|
|
Tail.Overlay.ListEntry );
|
|
|
|
if ( FileObject == NULL ||
|
|
IoGetCurrentIrpStackLocation(irp)->FileObject == FileObject )
|
|
{
|
|
CDLOG( "CompletePendedIrps: CompleteIrp %p", irp );
|
|
RemoveEntryList( listEntry );
|
|
ClusDiskCompletePendingRequest(irp, STATUS_SUCCESS, DeviceExtension);
|
|
}
|
|
}
|
|
|
|
KeReleaseSpinLockFromDpcLevel(&ClusDiskSpinLock);
|
|
IoReleaseCancelSpinLock( irql );
|
|
|
|
CDLOGF(UNPEND, "CompletePendedIrps: Exit DevExt %p FileObj %p",
|
|
DeviceExtension, FileObject );
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
ClusDiskCleanup(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine services cleanup commands. It deactivates the reservation
|
|
threads on the device object, and takes the device offline.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Pointer to the device object on which the irp was received.
|
|
Irp - The IRP.
|
|
|
|
Return Value:
|
|
|
|
NT Status
|
|
|
|
Notes:
|
|
|
|
We don't release the reservations here, since the process may have
|
|
failed and might be restarted. Make the remote system go through full
|
|
arbitration if needed.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
PCLUS_DEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
|
|
PCLUS_DEVICE_EXTENSION physicalDisk;
|
|
PDEVICE_OBJECT targetDeviceObject;
|
|
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
|
|
KIRQL irql;
|
|
|
|
BOOLEAN phyDiskRemLockAvail = FALSE;
|
|
|
|
CDLOGF(CLEANUP,"ClusDiskCleanup: Entry DO %p", DeviceObject );
|
|
|
|
status = AcquireRemoveLock(&deviceExtension->RemoveLock, Irp);
|
|
if ( !NT_SUCCESS(status) ) {
|
|
Irp->IoStatus.Status = status;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return status;
|
|
}
|
|
|
|
//
|
|
// Make sure the device attach completed.
|
|
//
|
|
status = WaitForAttachCompletion( deviceExtension,
|
|
TRUE, // Wait
|
|
TRUE ); // Also check physical device
|
|
if ( !NT_SUCCESS( status ) ) {
|
|
ReleaseRemoveLock(&deviceExtension->RemoveLock, Irp);
|
|
Irp->IoStatus.Status = status;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return status;
|
|
}
|
|
|
|
//
|
|
// Signal all waiting Irp's on the physical device extension.
|
|
//
|
|
ClusDiskCompletePendedIrps(
|
|
deviceExtension,
|
|
irpStack->FileObject,
|
|
/* offline => */ FALSE);
|
|
|
|
if ( (deviceExtension->BusType == RootBus) &&
|
|
(irpStack->FileObject->FsContext) ) {
|
|
|
|
CDLOG("ClusDiskCleanup: StopReserve DO %p", DeviceObject );
|
|
|
|
targetDeviceObject = (PDEVICE_OBJECT)irpStack->FileObject->FsContext;
|
|
irpStack->FileObject->FsContext = NULL;
|
|
physicalDisk = targetDeviceObject->DeviceExtension;
|
|
|
|
status = AcquireRemoveLock(&physicalDisk->RemoveLock, Irp);
|
|
if ( !NT_SUCCESS(status) ) {
|
|
ReleaseRemoveLock(&deviceExtension->RemoveLock, Irp);
|
|
Irp->IoStatus.Status = status;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return status;
|
|
}
|
|
|
|
phyDiskRemLockAvail = TRUE;
|
|
|
|
//
|
|
// 2000/02/05: stevedz - RemoveLocks should resolve this problem.
|
|
//
|
|
// The following "if" only reduces the chances of AV to occur, not
|
|
// eliminates it completely. TargetDeviceObject is zeroed out by our PnP
|
|
// handler when the device is removed
|
|
//
|
|
if (physicalDisk->TargetDeviceObject == NULL) {
|
|
ClusDiskPrint((
|
|
1,
|
|
"[ClusDisk] Part0 object %p got deleted. Skip the dismount\n", targetDeviceObject));
|
|
|
|
KeAcquireSpinLock(&ClusDiskSpinLock, &irql);
|
|
DisableHaltProcessing( &irql );
|
|
KeReleaseSpinLock(&ClusDiskSpinLock, irql);
|
|
goto skip_it;
|
|
}
|
|
|
|
ACQUIRE_SHARED( &ClusDiskDeviceListLock );
|
|
|
|
#if 0 // Always get volume handles...
|
|
//
|
|
// Capture handles for the volumes before offlining the device
|
|
//
|
|
if ( physicalDisk->DiskState == DiskOnline ) {
|
|
#endif
|
|
ProcessDelayedWorkSynchronous( targetDeviceObject, ClusDiskpOpenFileHandles, NULL );
|
|
#if 0 // Always get volume handles...
|
|
}
|
|
#endif
|
|
|
|
ASSERT_RESERVES_STOPPED( physicalDisk );
|
|
// physicalDisk->DiskState = DiskOffline;
|
|
OFFLINE_DISK( physicalDisk );
|
|
|
|
KeAcquireSpinLock(&ClusDiskSpinLock, &irql);
|
|
physicalDisk->ReserveTimer = 0;
|
|
DisableHaltProcessing( &irql );
|
|
KeReleaseSpinLock(&ClusDiskSpinLock, irql);
|
|
|
|
RELEASE_SHARED( &ClusDiskDeviceListLock );
|
|
|
|
ClusDiskPrint(( 3,
|
|
"[ClusDisk] Cleanup: Signature %08X (device %p) now marked offline \n",
|
|
physicalDisk->Signature,
|
|
physicalDisk->DeviceObject ));
|
|
|
|
CDLOG( "ClusDiskCleanup: LastWrite %!datetime!",
|
|
physicalDisk->LastWriteTime.QuadPart );
|
|
|
|
physicalDisk->ReserveTimer = 0;
|
|
ReleaseScsiDevice( physicalDisk );
|
|
|
|
ClusDiskPrint((3,
|
|
"[ClusDisk] Cleanup, stop reservations on signature %lx, disk state %s \n",
|
|
physicalDisk->Signature,
|
|
DiskStateToString(physicalDisk->DiskState) ));
|
|
|
|
//
|
|
// We need to release all pended irps immediately,
|
|
// w/o relying on worker thread to do it for us.
|
|
//
|
|
ClusDiskOfflineEntireDisk( targetDeviceObject );
|
|
|
|
//
|
|
// We must use a worker item to do this work.
|
|
//
|
|
//status = ClusDiskOfflineDevice( targetDeviceObject );
|
|
|
|
if ( !KeReadStateEvent( &physicalDisk->Event ) ) {
|
|
|
|
CDLOG("ClusDiskCleanup: WorkerIsStillRunning DO %p", DeviceObject );
|
|
|
|
} else {
|
|
|
|
#if 0
|
|
//
|
|
// Acquire the RemoveLock for the target device object one more time. Since
|
|
// we are queuing a work item, we don't know when the ClusDiskDismountVolumes will
|
|
// run. When it does run, the RemoveLock will be released.
|
|
//
|
|
|
|
status = AcquireRemoveLock( &physicalDisk->RemoveLock, physicalDisk);
|
|
if ( !NT_SUCCESS(status) ) {
|
|
|
|
// Skip this device.
|
|
goto skip_it;
|
|
}
|
|
#endif
|
|
|
|
KeClearEvent( &physicalDisk->Event );
|
|
ClusDiskPrint(( 3,
|
|
"[ClusDisk] Cleanup: ClearEvent (%p)\n", &physicalDisk->Event));
|
|
|
|
//
|
|
// Keep the device object around
|
|
//
|
|
ObReferenceObject( targetDeviceObject );
|
|
|
|
//
|
|
// ClusDiskDismountVolumes will always run as a worker thread.
|
|
//
|
|
|
|
ClusDiskDismountVolumes( targetDeviceObject,
|
|
FALSE ); // Don't release the RemoveLock
|
|
|
|
}
|
|
|
|
skip_it:
|
|
|
|
CDLOG( "RootCtl: DecRef DO %p", targetDeviceObject );
|
|
ObDereferenceObject( targetDeviceObject );
|
|
}
|
|
|
|
CDLOGF(CLEANUP,"ClusDiskCleanup: Exit DO %p", DeviceObject );
|
|
|
|
if (phyDiskRemLockAvail) {
|
|
ReleaseRemoveLock(&physicalDisk->RemoveLock, Irp);
|
|
}
|
|
|
|
ReleaseRemoveLock(&deviceExtension->RemoveLock, Irp);
|
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return(STATUS_SUCCESS);
|
|
|
|
} // ClusDiskCleanup
|
|
|
|
|
|
NTSTATUS
|
|
ClusDiskOfflineEntireDisk(
|
|
IN PDEVICE_OBJECT Part0DeviceObject
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Complete all pended irps on the disk and all its volumes and
|
|
sets the state to offline.
|
|
|
|
Arguments:
|
|
|
|
Part0DeviceObject - the device to take offline.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS for the request.
|
|
|
|
--*/
|
|
{
|
|
PCLUS_DEVICE_EXTENSION Part0Extension = Part0DeviceObject->DeviceExtension;
|
|
PCLUS_DEVICE_EXTENSION deviceExtension;
|
|
PDEVICE_OBJECT deviceObject;
|
|
|
|
CDLOG( "OfflineEntireDisk: Entry DO %p", Part0DeviceObject );
|
|
|
|
ASSERT(Part0DeviceObject == Part0Extension->PhysicalDevice);
|
|
//
|
|
// Signal all waiting Irp's on the physical device extension.
|
|
//
|
|
ClusDiskCompletePendedIrps(
|
|
Part0Extension,
|
|
/* FileObject => */ NULL,
|
|
/* offline => */ TRUE);
|
|
|
|
//
|
|
// We also need to complete all the irps on all the volumes belonging to this disk
|
|
//
|
|
ACQUIRE_SHARED( &ClusDiskDeviceListLock );
|
|
|
|
//
|
|
// Get the first DeviceObject in the driver list
|
|
//
|
|
deviceObject = Part0DeviceObject->DriverObject->DeviceObject;
|
|
|
|
// First release all pended irps and set the volume state to offline
|
|
|
|
while ( deviceObject ) {
|
|
deviceExtension = deviceObject->DeviceExtension;
|
|
if ( deviceExtension->PhysicalDevice == Part0DeviceObject &&
|
|
deviceObject != Part0DeviceObject) // not P0
|
|
{
|
|
ClusDiskCompletePendedIrps(
|
|
deviceExtension,
|
|
/* FileObject => */ NULL,
|
|
/* Offline => */ TRUE);
|
|
|
|
}
|
|
deviceObject = deviceObject->NextDevice;
|
|
}
|
|
|
|
RELEASE_SHARED( &ClusDiskDeviceListLock );
|
|
CDLOG( "OfflineEntireDisk: Exit DO %p", Part0DeviceObject );
|
|
|
|
return STATUS_SUCCESS;
|
|
} // ClusDiskOfflineEntireDisk //
|
|
|
|
|
|
|
|
NTSTATUS
|
|
ClusDiskDismountVolumes(
|
|
IN PDEVICE_OBJECT Part0DeviceObject,
|
|
IN BOOLEAN RelRemLock
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Dismount the file systems on a all volumes belonging to Part0DO.
|
|
|
|
The RemoveLock for Part0DeviceObject must be acquired before this
|
|
routine runs.
|
|
|
|
The routine that does the lock and dismount must ALWAYS run as a work item
|
|
to prevent deadlock with pnp threads.
|
|
|
|
Arguments:
|
|
|
|
Part0DeviceObject - the device to take offline.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS for the request.
|
|
|
|
--*/
|
|
|
|
{
|
|
PCLUS_DEVICE_EXTENSION Part0Extension = Part0DeviceObject->DeviceExtension;
|
|
PVOID oldArray;
|
|
PREPLACE_CONTEXT context = NULL;
|
|
|
|
NTSTATUS status;
|
|
|
|
CDLOG( "ClusDiskDismountVolumes: Entry %p", Part0DeviceObject );
|
|
|
|
//
|
|
// We assume that DeviceObject is P0
|
|
//
|
|
|
|
ASSERT(Part0DeviceObject == Part0Extension->PhysicalDevice);
|
|
|
|
//
|
|
// If the current volume handle array is NULL, we don't need to do anything.
|
|
//
|
|
|
|
oldArray = InterlockedCompareExchangePointer( (VOID*)&Part0Extension->VolumeHandles,
|
|
NULL,
|
|
NULL );
|
|
|
|
if ( NULL == oldArray ) {
|
|
CDLOG( "ClusDiskDismountVolumes: HandleArray is NULL, exiting" );
|
|
|
|
KeSetEvent( &Part0Extension->Event, 0, FALSE );
|
|
if ( RelRemLock ) {
|
|
ReleaseRemoveLock(&Part0Extension->RemoveLock, Part0Extension);
|
|
}
|
|
ObDereferenceObject( Part0DeviceObject );
|
|
|
|
goto FnExit;
|
|
}
|
|
|
|
//
|
|
// Since this thread is going away, we have to allocate the replace context
|
|
// and let the worker routine free it.
|
|
//
|
|
|
|
context = ExAllocatePool( NonPagedPool, sizeof(REPLACE_CONTEXT) );
|
|
|
|
if ( !context ) {
|
|
CDLOG( "ClusDiskDismountVolumes: Unable to allocate REPLACE_CONTEXT struture" );
|
|
|
|
KeSetEvent( &Part0Extension->Event, 0, FALSE );
|
|
if ( RelRemLock ) {
|
|
ReleaseRemoveLock(&Part0Extension->RemoveLock, Part0Extension);
|
|
}
|
|
ObDereferenceObject( Part0DeviceObject );
|
|
|
|
goto FnExit;
|
|
}
|
|
|
|
RtlZeroMemory( context, sizeof(REPLACE_CONTEXT) );
|
|
|
|
context->DeviceExtension = Part0Extension;
|
|
context->NewValue = NULL; // clear the field
|
|
context->Flags = DO_DISMOUNT | CLEANUP_STORAGE | SET_PART0_EVENT ;
|
|
|
|
if ( RelRemLock ) {
|
|
context->Flags |= RELEASE_REMOVE_LOCK;
|
|
}
|
|
|
|
status = ProcessDelayedWorkAsynchronous( Part0DeviceObject, ClusDiskpReplaceHandleArray, context );
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
|
|
//
|
|
// Context is freed in async routine whether it ran or not.
|
|
//
|
|
|
|
KeSetEvent( &Part0Extension->Event, 0, FALSE );
|
|
if ( RelRemLock ) {
|
|
ReleaseRemoveLock(&Part0Extension->RemoveLock, Part0Extension);
|
|
}
|
|
ObDereferenceObject( Part0DeviceObject );
|
|
}
|
|
|
|
//
|
|
// We released the RemoveLock in ClusDiskpReplaceHandleArray.
|
|
//
|
|
// ReleaseRemoveLock(&Part0Extension->RemoveLock, Part0Extension);
|
|
|
|
FnExit:
|
|
|
|
CDLOG( "ClusDiskDismountVolumes: Exit DO %p", Part0DeviceObject );
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
} // ClusDiskDismountVolumes
|
|
|
|
|
|
|
|
NTSTATUS
|
|
ClusDiskDismountDevice(
|
|
IN ULONG DiskNumber,
|
|
IN BOOLEAN ForceDismount
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Dismount the file systems on the specified disk
|
|
|
|
Arguments:
|
|
|
|
DiskNumber - number of the disk on which to dismount all FSs.
|
|
|
|
ForceDismount - TRUE if dismount should always take place (ignore VPB)
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS for the request.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
ULONG partIndex;
|
|
WCHAR partitionNameBuffer[MAX_PARTITION_NAME_LENGTH];
|
|
PDRIVE_LAYOUT_INFORMATION_EX driveLayoutInfo;
|
|
PPARTITION_INFORMATION_EX partitionInfo;
|
|
UNICODE_STRING targetDeviceName;
|
|
PDEVICE_OBJECT targetDevice = NULL;
|
|
KIRQL irql;
|
|
|
|
CDLOG( "ClusDiskDismountDevice: Entry DiskNumber %d", DiskNumber );
|
|
|
|
ClusDiskPrint(( 3,
|
|
"[ClusDisk] DismountDevice: disknum %08X \n",
|
|
DiskNumber ));
|
|
|
|
irql = KeGetCurrentIrql();
|
|
if ( PASSIVE_LEVEL != irql ) {
|
|
ClusDiskPrint(( 1,
|
|
"[ClusDisk] ClusDiskDismountDevice: Invalid IRQL %d \n", irql ));
|
|
CDLOG( "DismountDevice: Invalid IRQL %d ", irql );
|
|
ASSERT( FALSE );
|
|
status = STATUS_UNSUCCESSFUL;
|
|
goto FnExit;
|
|
}
|
|
|
|
//
|
|
// Dismount the file system on each partition.
|
|
//
|
|
status = ClusDiskGetTargetDevice(DiskNumber,
|
|
0,
|
|
&targetDevice,
|
|
&targetDeviceName,
|
|
&driveLayoutInfo,
|
|
NULL,
|
|
FALSE );
|
|
|
|
FREE_DEVICE_NAME_BUFFER( targetDeviceName );
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
goto FnExit;
|
|
}
|
|
|
|
//
|
|
// Dismount the partition zero device object.
|
|
//
|
|
//status = DismountPartition( DiskNumber, 0 );
|
|
|
|
if ( driveLayoutInfo != NULL ) {
|
|
|
|
if ( PARTITION_STYLE_MBR != driveLayoutInfo->PartitionStyle ) {
|
|
ClusDiskPrint(( 1,
|
|
"[ClusDisk] DismountDevice: skipping non-MBR disk \n" ));
|
|
CDLOG( "ClusDiskDismountDevice: skipping non-MBR disk" );
|
|
status = STATUS_DEVICE_OFF_LINE;
|
|
|
|
FREE_AND_NULL_PTR( driveLayoutInfo );
|
|
goto FnExit;
|
|
}
|
|
|
|
for ( partIndex = 0;
|
|
partIndex < driveLayoutInfo->PartitionCount;
|
|
partIndex++ )
|
|
{
|
|
|
|
partitionInfo = &driveLayoutInfo->PartitionEntry[partIndex];
|
|
|
|
//
|
|
// Make sure this is a valid partition.
|
|
//
|
|
if ( !partitionInfo->Mbr.RecognizedPartition ||
|
|
partitionInfo->PartitionNumber == 0 )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Create device name for the physical disk.
|
|
//
|
|
|
|
if ( FAILED( StringCchPrintfW( partitionNameBuffer,
|
|
RTL_NUMBER_OF(partitionNameBuffer),
|
|
DEVICE_PARTITION_NAME,
|
|
DiskNumber,
|
|
partitionInfo->PartitionNumber ) ) ) {
|
|
continue;
|
|
}
|
|
|
|
WCSLEN_ASSERT( partitionNameBuffer );
|
|
|
|
DEREFERENCE_OBJECT( targetDevice );
|
|
|
|
status = ClusDiskGetTargetDevice( DiskNumber,
|
|
partitionInfo->PartitionNumber,
|
|
&targetDevice,
|
|
&targetDeviceName,
|
|
NULL,
|
|
NULL,
|
|
FALSE );
|
|
|
|
FREE_DEVICE_NAME_BUFFER( targetDeviceName );
|
|
|
|
if ( ForceDismount || ( targetDevice && targetDevice->Vpb &&
|
|
(targetDevice->Vpb->Flags & VPB_MOUNTED) ) ) {
|
|
|
|
status = DismountPartition( targetDevice,
|
|
DiskNumber,
|
|
partitionInfo->PartitionNumber);
|
|
}
|
|
}
|
|
|
|
FREE_AND_NULL_PTR( driveLayoutInfo );
|
|
status = STATUS_SUCCESS;
|
|
|
|
} else {
|
|
//
|
|
// This should not have failed!
|
|
//
|
|
ClusDiskPrint((1,
|
|
"[ClusDisk] DismountDevice: Failed to read partition info for \\Device\\Harddisk%u.\n",
|
|
DiskNumber));
|
|
status = STATUS_DEVICE_OFF_LINE;
|
|
}
|
|
|
|
FnExit:
|
|
|
|
DEREFERENCE_OBJECT( targetDevice );
|
|
|
|
CDLOG( "ClusDiskDismountDevice: Exit DiskNumber %d status %!status!",
|
|
DiskNumber,
|
|
status );
|
|
|
|
//
|
|
// The target device should not have any reservations.
|
|
//
|
|
return(status);
|
|
|
|
} // ClusDiskDismountDevice
|
|
|
|
|
|
|
|
NTSTATUS
|
|
ClusDiskReAttachDevice(
|
|
PDEVICE_OBJECT DeviceObject
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Re-attach to a disk device with the signature specified if it is detached.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - the device object for Partition0.
|
|
|
|
Return Value:
|
|
|
|
NT Status
|
|
|
|
Notes:
|
|
|
|
Dismount the file system if we do perform an attach.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
PCLUS_DEVICE_EXTENSION physicalExtension;
|
|
PCLUS_DEVICE_EXTENSION deviceExtension;
|
|
UNICODE_STRING signatureName;
|
|
PDRIVE_LAYOUT_INFORMATION_EX driveLayoutInfo;
|
|
PPARTITION_INFORMATION_EX partitionInfo;
|
|
PDEVICE_OBJECT deviceObject;
|
|
|
|
POFFLINE_DISK_ENTRY offlineList = NULL;
|
|
POFFLINE_DISK_ENTRY nextEntry;
|
|
|
|
OBJECT_ATTRIBUTES objectAttributes;
|
|
IO_STATUS_BLOCK ioStatus;
|
|
|
|
ULONG partIndex;
|
|
HANDLE fileHandle;
|
|
|
|
WCHAR deviceNameBuffer[MAX_PARTITION_NAME_LENGTH];
|
|
UNICODE_STRING deviceName;
|
|
|
|
KIRQL irql;
|
|
|
|
CDLOG( "ClusDiskReAttachDevice: Entry DO %p", DeviceObject );
|
|
|
|
physicalExtension = DeviceObject->DeviceExtension;
|
|
|
|
ClusDiskPrint((3,
|
|
"[ClusDisk] ReAttach entry: signature %08X, disk state %s \n",
|
|
physicalExtension->Signature,
|
|
DiskStateToString( physicalExtension->DiskState ) ));
|
|
|
|
if ( !physicalExtension->Detached ) {
|
|
CDLOG( "ClusDiskReAttachDevice_Exit2 Detached == FALSE" );
|
|
ClusDiskPrint((3,
|
|
"[ClusDisk] ReAttach: signature %08X, PerformReserves = %s, ReserveTimer = %u \n",
|
|
physicalExtension->Signature,
|
|
BoolToString( physicalExtension->PerformReserves ),
|
|
physicalExtension->ReserveTimer ));
|
|
return(STATUS_SUCCESS);
|
|
}
|
|
|
|
//
|
|
// Dismount the file systems!
|
|
//
|
|
status = ClusDiskDismountDevice( physicalExtension->DiskNumber, TRUE );
|
|
|
|
//
|
|
// Now add the signature to the signatures list!
|
|
//
|
|
|
|
//
|
|
// Allocate buffer for Signatures registry key. So we can add
|
|
// the signature.
|
|
//
|
|
status = ClusDiskInitRegistryString(
|
|
&signatureName,
|
|
CLUSDISK_SIGNATURE_KEYNAME,
|
|
wcslen(CLUSDISK_SIGNATURE_KEYNAME)
|
|
);
|
|
if ( NT_SUCCESS(status) ) {
|
|
//
|
|
// Create the signature key under \Parameters\Signatures.
|
|
//
|
|
status = ClusDiskAddSignature(
|
|
&signatureName,
|
|
physicalExtension->Signature,
|
|
FALSE
|
|
);
|
|
|
|
//
|
|
// Now write the disk name.
|
|
//
|
|
ClusDiskWriteDiskInfo( physicalExtension->Signature,
|
|
physicalExtension->DiskNumber,
|
|
CLUSDISK_SIGNATURE_KEYNAME
|
|
);
|
|
|
|
ExFreePool( signatureName.Buffer );
|
|
}
|
|
|
|
//
|
|
// Now remove the signature from the available list!
|
|
//
|
|
|
|
//
|
|
// Allocate buffer for AvailableDisks registry key. So we can
|
|
// delete the disk signature.
|
|
//
|
|
status = ClusDiskInitRegistryString(
|
|
&signatureName,
|
|
CLUSDISK_AVAILABLE_DISKS_KEYNAME,
|
|
wcslen(CLUSDISK_AVAILABLE_DISKS_KEYNAME)
|
|
);
|
|
if ( NT_SUCCESS(status) ) {
|
|
//
|
|
// Delete the signature key under \Parameters\AvailableDisks.
|
|
//
|
|
status = ClusDiskDeleteSignature(
|
|
&signatureName,
|
|
physicalExtension->Signature
|
|
);
|
|
ExFreePool( signatureName.Buffer );
|
|
}
|
|
|
|
//
|
|
// Find all related device objects and mark them as being attached now,
|
|
// and offline.
|
|
//
|
|
KeAcquireSpinLock(&ClusDiskSpinLock, &irql);
|
|
deviceObject = DeviceObject->DriverObject->DeviceObject;
|
|
while ( deviceObject ) {
|
|
deviceExtension = deviceObject->DeviceExtension;
|
|
if ( deviceExtension->Signature == physicalExtension->Signature ) {
|
|
|
|
ClusDiskPrint((3,
|
|
"[ClusDisk] ReAttach, marking signature %08X offline, old state %s \n",
|
|
deviceExtension->Signature,
|
|
DiskStateToString( deviceExtension->DiskState ) ));
|
|
|
|
deviceExtension->Detached = FALSE;
|
|
deviceExtension->ReserveTimer = 0;
|
|
deviceExtension->ReserveFailure = 0;
|
|
|
|
// [stevedz 11/06/2000] Fix NTFS corruption.
|
|
// Change to mark disk offline, rather than online (as the comments above
|
|
// originally indicated). Marking the disk offline was commented out and
|
|
// the code marked the disk online here. Don't understand why we would
|
|
// want to mark it online with no reserves running.
|
|
|
|
//
|
|
// Try to offline in sync thread after we drop the locks.
|
|
//
|
|
|
|
nextEntry = ExAllocatePool( NonPagedPool, sizeof( OFFLINE_DISK_ENTRY ) );
|
|
|
|
if ( !nextEntry ) {
|
|
// deviceExtension->DiskState = DiskOffline;
|
|
OFFLINE_DISK( deviceExtension );
|
|
} else {
|
|
|
|
nextEntry->DeviceExtension = deviceExtension;
|
|
ObReferenceObject( deviceExtension->DeviceObject );
|
|
|
|
nextEntry->Next = offlineList;
|
|
offlineList = nextEntry;
|
|
}
|
|
}
|
|
CDLOG( "ClusDiskReAttachDevice: RelatedObject %p diskstate %!diskstate!",
|
|
deviceObject,
|
|
deviceExtension->DiskState );
|
|
|
|
deviceObject = deviceObject->NextDevice;
|
|
}
|
|
KeReleaseSpinLock(&ClusDiskSpinLock, irql);
|
|
|
|
while ( offlineList ) {
|
|
nextEntry = offlineList->Next;
|
|
deviceExtension = offlineList->DeviceExtension;
|
|
OFFLINE_DISK( deviceExtension );
|
|
ObDereferenceObject( deviceExtension->DeviceObject );
|
|
ExFreePool( offlineList );
|
|
offlineList = nextEntry;
|
|
}
|
|
//
|
|
// Make sure we have clusdisk volume object attached to each partition.
|
|
//
|
|
|
|
SimpleDeviceIoControl( physicalExtension->TargetDeviceObject,
|
|
IOCTL_DISK_UPDATE_PROPERTIES,
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
0 );
|
|
|
|
driveLayoutInfo = ClusDiskGetPartitionInfo( physicalExtension );
|
|
|
|
if ( driveLayoutInfo != NULL ) {
|
|
|
|
ASSERT( driveLayoutInfo->Mbr.Signature == physicalExtension->Signature );
|
|
|
|
for ( partIndex = 0;
|
|
partIndex < driveLayoutInfo->PartitionCount;
|
|
partIndex++ )
|
|
{
|
|
partitionInfo = &driveLayoutInfo->PartitionEntry[partIndex];
|
|
|
|
//
|
|
// First make sure this is a valid partition.
|
|
//
|
|
if ( !partitionInfo->Mbr.RecognizedPartition ||
|
|
partitionInfo->PartitionNumber == 0 )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Try to open the clusdisk volume object. If it exists,
|
|
// continue checking partitions. If it doesn't exist,
|
|
// then create the clusdisk volume object.
|
|
//
|
|
|
|
if ( FAILED( StringCchPrintfW( deviceNameBuffer,
|
|
RTL_NUMBER_OF(deviceNameBuffer),
|
|
CLUSDISK_DEVICE_NAME,
|
|
physicalExtension->DiskNumber,
|
|
partitionInfo->PartitionNumber ) ) ) {
|
|
continue;
|
|
}
|
|
|
|
WCSLEN_ASSERT( deviceNameBuffer );
|
|
|
|
RtlInitUnicodeString( &deviceName, deviceNameBuffer );
|
|
|
|
InitializeObjectAttributes( &objectAttributes,
|
|
&deviceName,
|
|
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
|
|
NULL,
|
|
NULL );
|
|
|
|
fileHandle = NULL;
|
|
status = ZwOpenFile( &fileHandle,
|
|
FILE_READ_ATTRIBUTES,
|
|
&objectAttributes,
|
|
&ioStatus,
|
|
0,
|
|
FILE_NON_DIRECTORY_FILE );
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
ClusDiskPrint(( 3,
|
|
"[ClusDisk] ReAttach, open of device %ws failed %08X \n",
|
|
deviceNameBuffer,
|
|
status ));
|
|
|
|
CreateVolumeObject( physicalExtension,
|
|
physicalExtension->DiskNumber,
|
|
partitionInfo->PartitionNumber,
|
|
NULL );
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
|
|
if ( fileHandle ) {
|
|
ZwClose( fileHandle );
|
|
}
|
|
|
|
}
|
|
FREE_AND_NULL_PTR( driveLayoutInfo );
|
|
} else {
|
|
CDLOG( "ClusDiskReAttachDevice: FailedToReadPartitionInfo" );
|
|
}
|
|
|
|
CDLOG( "ClusDiskReAttachDevice: Exit status %!status!", status );
|
|
return(status);
|
|
|
|
} // ClusDiskReAttachDevice
|
|
|
|
|
|
|
|
NTSTATUS
|
|
ClusDiskTryAttachDevice(
|
|
ULONG Signature,
|
|
ULONG NextDisk,
|
|
PDRIVER_OBJECT DriverObject,
|
|
BOOLEAN InstallMode
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Attach to a disk device with the signature specified.
|
|
|
|
Arguments:
|
|
|
|
Signature - the signature for the device to attach to.
|
|
|
|
NextDisk - the start disk number.
|
|
|
|
DriverObject - the driver object for our driver.
|
|
|
|
InstallMode - Indicates whether the disk is being added as a disk
|
|
resource.
|
|
|
|
Return Value:
|
|
|
|
NT Status
|
|
|
|
Notes:
|
|
|
|
Dismount the file system for the given device - if it is mounted.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS ntStatus;
|
|
|
|
BOOLEAN stopProcessing = FALSE;
|
|
|
|
CDLOG( "TryAttachDevice: Entry Sig %08x NextDisk %d",
|
|
Signature,
|
|
NextDisk );
|
|
|
|
//
|
|
// First just try to attach to the device with no bus resets.
|
|
//
|
|
ntStatus = ClusDiskAttachDevice(
|
|
Signature,
|
|
NextDisk,
|
|
DriverObject,
|
|
FALSE,
|
|
&stopProcessing,
|
|
InstallMode );
|
|
|
|
if ( NT_SUCCESS(ntStatus) || stopProcessing ) {
|
|
CDLOG( "TryAttachDevice: FirstTrySuccess" );
|
|
goto exit_gracefully;
|
|
}
|
|
|
|
//
|
|
// Second, try to attach after reset all busses at once.
|
|
//
|
|
ResetScsiBusses();
|
|
|
|
ntStatus = ClusDiskAttachDevice(
|
|
Signature,
|
|
NextDisk,
|
|
DriverObject,
|
|
FALSE,
|
|
&stopProcessing,
|
|
InstallMode );
|
|
|
|
if ( NT_SUCCESS(ntStatus) || stopProcessing ) {
|
|
CDLOG( "TryAttachDevice: SecondTrySuccess" );
|
|
goto exit_gracefully;
|
|
}
|
|
|
|
//
|
|
// Lastly, try to attach with a bus reset after each failure.
|
|
//
|
|
ntStatus = ClusDiskAttachDevice(
|
|
Signature,
|
|
NextDisk,
|
|
DriverObject,
|
|
TRUE,
|
|
&stopProcessing,
|
|
InstallMode );
|
|
|
|
exit_gracefully:
|
|
|
|
CDLOG( "TryAttachDevice: Exit sig %08x => %!status!",
|
|
Signature,
|
|
ntStatus );
|
|
|
|
return ntStatus;
|
|
|
|
} // ClusDiskTryAttachDevice
|
|
|
|
|
|
|
|
NTSTATUS
|
|
ClusDiskAttachDevice(
|
|
ULONG Signature,
|
|
ULONG NextDisk,
|
|
PDRIVER_OBJECT DriverObject,
|
|
BOOLEAN Reset,
|
|
BOOLEAN *StopProcessing,
|
|
BOOLEAN InstallMode
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Attach to a disk device with the signature specified.
|
|
|
|
Arguments:
|
|
|
|
Signature - the signature for the device to attach to.
|
|
|
|
NextDisk - the start disk number.
|
|
|
|
DriverObject - the driver object for our driver.
|
|
|
|
Reset - Indicates whether bus reset should be performed on each I/O
|
|
|
|
StopProcessing - Indicates whether to stop trying to attach. If FALSE,
|
|
we will try a bus reset between attach attempts.
|
|
|
|
InstallMode - Indicates whether the disk is being added as a disk
|
|
resource.
|
|
|
|
Return Value:
|
|
|
|
NT Status
|
|
|
|
Notes:
|
|
|
|
Dismount the file system for the given device - if it is mounted.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
NTSTATUS finalStatus = STATUS_NO_SUCH_DEVICE;
|
|
UNICODE_STRING targetDeviceName;
|
|
ULONG diskNumber;
|
|
ULONG partIndex;
|
|
PCONFIGURATION_INFORMATION configurationInformation;
|
|
PDEVICE_OBJECT attachedTargetDevice;
|
|
PDEVICE_OBJECT physicalDevice;
|
|
PCLUS_DEVICE_EXTENSION deviceExtension;
|
|
PCLUS_DEVICE_EXTENSION zeroExtension;
|
|
PDRIVE_LAYOUT_INFORMATION_EX driveLayoutInfo;
|
|
PPARTITION_INFORMATION_EX partitionInfo;
|
|
UNICODE_STRING signatureName;
|
|
SCSI_ADDRESS scsiAddress;
|
|
ULONG busType;
|
|
ULONG diskCount;
|
|
ULONG skipCount;
|
|
PDEVICE_OBJECT targetDevice = NULL;
|
|
UNICODE_STRING clusdiskDeviceName;
|
|
WCHAR clusdiskDeviceBuffer[MAX_CLUSDISK_DEVICE_NAME_LENGTH];
|
|
KIRQL irql;
|
|
|
|
CDLOG( "ClusDiskAttachDevice: Entry sig %08x nextdisk %d reset %!bool! install %!bool!",
|
|
Signature,
|
|
NextDisk,
|
|
Reset,
|
|
InstallMode );
|
|
|
|
ClusDiskPrint(( 3,
|
|
"[ClusDisk] AttachDevice: Trying to attach to signature %08X reset = %u install = %u \n",
|
|
Signature,
|
|
Reset,
|
|
InstallMode ));
|
|
|
|
*StopProcessing = FALSE;
|
|
|
|
//
|
|
// If we're already attached, then return success.
|
|
//
|
|
if ( AttachedDevice( Signature, &physicalDevice ) ) {
|
|
|
|
CDLOG( "ClusDiskAttachDevice: AlreadyAttached DO %p", physicalDevice );
|
|
|
|
deviceExtension = physicalDevice->DeviceExtension;
|
|
|
|
if ( !deviceExtension ) {
|
|
*StopProcessing = TRUE;
|
|
return STATUS_DEVICE_REMOVED;
|
|
}
|
|
|
|
#if CLUSTER_FREE_ASSERTS
|
|
// This tells us whether there are volume objects created.
|
|
if ( 0 == deviceExtension->LastPartitionNumber ) {
|
|
DbgPrint( "[ClusDisk] ClusDiskAttachDevice: Reattach with LastPartitionNumber == 0 \n");
|
|
// DbgBreakPoint();
|
|
}
|
|
#endif
|
|
|
|
status = AcquireRemoveLock( &deviceExtension->RemoveLock, deviceExtension );
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
*StopProcessing = TRUE;
|
|
return status;
|
|
}
|
|
|
|
//
|
|
// If any of the special file counts are nonzero, don't allow the attach.
|
|
//
|
|
|
|
if ( deviceExtension->PagingPathCount ||
|
|
deviceExtension->HibernationPathCount ||
|
|
deviceExtension->DumpPathCount ) {
|
|
|
|
CDLOG( "ClusDiskAttachDevice: Exit, special file count nonzero %08X %08X %08X",
|
|
deviceExtension->PagingPathCount,
|
|
deviceExtension->HibernationPathCount,
|
|
deviceExtension->DumpPathCount );
|
|
|
|
ClusDiskPrint(( 1,
|
|
"[ClusDisk] AttachDevice: Exit, special file count nonzero %08X %08X %08X \n",
|
|
deviceExtension->PagingPathCount,
|
|
deviceExtension->HibernationPathCount,
|
|
deviceExtension->DumpPathCount ));
|
|
|
|
*StopProcessing = TRUE;
|
|
ReleaseRemoveLock( &deviceExtension->RemoveLock, deviceExtension );
|
|
return STATUS_INVALID_DEVICE_REQUEST;
|
|
}
|
|
|
|
|
|
status = ClusDiskReAttachDevice( physicalDevice );
|
|
CDLOG( "ClusDiskAttachDevice: Exit1 %!status!", status );
|
|
|
|
CDLOG( "ClusDiskInfo *** Phys DO %p DevExt %p DiskNum %d Signature %08X RemLock %p ***",
|
|
physicalDevice,
|
|
deviceExtension,
|
|
deviceExtension->DiskNumber,
|
|
deviceExtension->Signature,
|
|
&deviceExtension->RemoveLock );
|
|
|
|
ReleaseRemoveLock( &deviceExtension->RemoveLock, deviceExtension );
|
|
return status;
|
|
}
|
|
|
|
//
|
|
// Make sure the signature is NOT for the system device!
|
|
//
|
|
if ( SystemDiskSignature == Signature ) {
|
|
CDLOG( "ClusDiskAttachDevice: Exit2 SystemDiskSig %08x",
|
|
SystemDiskSignature );
|
|
*StopProcessing = TRUE;
|
|
return(STATUS_INVALID_PARAMETER);
|
|
}
|
|
|
|
//
|
|
//
|
|
// Get the configuration information.
|
|
//
|
|
|
|
configurationInformation = IoGetConfigurationInformation();
|
|
diskCount = configurationInformation->DiskCount;
|
|
|
|
CDLOG( "ClusDiskAttachDevice: diskCount %d ",
|
|
diskCount );
|
|
|
|
//
|
|
// Find ALL disk devices. We will attempt to read the partition info
|
|
// without attaching. We might already be attached and not know it.
|
|
// So once we've performed a successful read, if the device is attached
|
|
// we will know it by checking again.
|
|
//
|
|
|
|
for ( diskNumber = NextDisk, skipCount = 0;
|
|
( diskNumber < diskCount && skipCount < SKIP_COUNT_MAX );
|
|
diskNumber++ ) {
|
|
|
|
DEREFERENCE_OBJECT( targetDevice );
|
|
|
|
//
|
|
// Create device name for the physical disk.
|
|
//
|
|
status = ClusDiskGetTargetDevice( diskNumber,
|
|
0,
|
|
&targetDevice,
|
|
&targetDeviceName,
|
|
&driveLayoutInfo,
|
|
&scsiAddress,
|
|
Reset );
|
|
|
|
FREE_DEVICE_NAME_BUFFER( targetDeviceName );
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
|
|
//
|
|
// If the device doesn't exist, this is likely a hole in the
|
|
// disk numbering.
|
|
//
|
|
|
|
if ( !targetDevice &&
|
|
( STATUS_FILE_INVALID == status ||
|
|
STATUS_DEVICE_DOES_NOT_EXIST == status ||
|
|
STATUS_OBJECT_PATH_NOT_FOUND == status ) ) {
|
|
skipCount++;
|
|
diskCount++;
|
|
ClusDiskPrint(( 3,
|
|
"[ClusDisk] ClusDiskAttachDevice: Adjust: skipCount %d diskCount %d \n",
|
|
skipCount,
|
|
diskCount ));
|
|
CDLOG( "ClusDiskAttachDevice: Adjust: skipCount %d diskCount %d ",
|
|
skipCount,
|
|
diskCount );
|
|
}
|
|
|
|
FREE_AND_NULL_PTR( driveLayoutInfo );
|
|
continue;
|
|
}
|
|
|
|
if ( driveLayoutInfo == NULL ) {
|
|
continue;
|
|
}
|
|
|
|
if ( PARTITION_STYLE_MBR != driveLayoutInfo->PartitionStyle ) {
|
|
FREE_AND_NULL_PTR( driveLayoutInfo );
|
|
continue;
|
|
}
|
|
|
|
if ( Signature != driveLayoutInfo->Mbr.Signature ) {
|
|
FREE_AND_NULL_PTR( driveLayoutInfo );
|
|
continue;
|
|
}
|
|
|
|
skipCount = 0; // Device found, reset skipCount
|
|
|
|
busType = ScsiBus; // because of GetTargetDevice above!
|
|
|
|
//
|
|
// Create ClusDisk device object for partition 0, if we are not
|
|
// already attached!
|
|
//
|
|
|
|
if ( ClusDiskAttached( targetDevice, diskNumber ) ) {
|
|
physicalDevice = targetDevice;
|
|
deviceExtension = physicalDevice->DeviceExtension;
|
|
zeroExtension = deviceExtension;
|
|
deviceExtension->Detached = FALSE;
|
|
|
|
ClusDiskPrint((3,
|
|
"[ClusDisk] AttachDevice: We were already attached to device %p (disk %u), simply mark as attached.\n",
|
|
physicalDevice,
|
|
diskNumber));
|
|
|
|
CDLOG( "ClusDiskAttachDevice: Previously attached to device %p (disk %u) signature %08X ",
|
|
physicalDevice,
|
|
diskNumber,
|
|
Signature );
|
|
|
|
//
|
|
// We offline all the volumes later. For now, just mark the disk offline.
|
|
//
|
|
|
|
deviceExtension->DiskState = DiskOffline;
|
|
|
|
//
|
|
// Seen instances where the device extension signature is zero, but the entry
|
|
// in the ClusDiskDeviceList contains a valid signature. This causes a problem
|
|
// if there is a detach and then we try to attach to the same device later.
|
|
//
|
|
// The issue is that the devExt->Sig can be zero if the drive layout couldn't
|
|
// be read. When we attach and the device object was previously created,
|
|
// we don't update the devExt->Sig. When we detach, ClusDiskDetachDevice
|
|
// tries to find all the devices with devExt->Sig matching the detached
|
|
// device. When a matching signature is found, devExt->Detached is set
|
|
// to TRUE. Since some devExt->Sigs are zero (if drive layout couldn't be read
|
|
// at the time the device object was created), they don't match the detaching
|
|
// signature, and the devExt->Detached flag is still set to FALSE.
|
|
//
|
|
// Then when trying to attach to the same device later, ClusDiskAttachDevice
|
|
// can see that we still have an entry in ClusDiskDeviceList, and then
|
|
// ClusDiskReAttachDevice is called. However, since the devExt->Detached flag
|
|
// is still FALSE, ClusDiskReAttachDevice assumes we are still attached and
|
|
// doesn't do anything except return success.
|
|
//
|
|
|
|
//
|
|
// If Signature we are attaching to does not equal the devExt->Sig, write an
|
|
// error to the WMI log, and update the devExt->Sig with the attaching Signature.
|
|
//
|
|
|
|
if ( Signature != deviceExtension->Signature ) {
|
|
|
|
CDLOG( "ClusDiskAttachDevice: PreviouslyAttachedSignatureMismatch sig %08x devExtSig %08x",
|
|
Signature,
|
|
deviceExtension->Signature );
|
|
|
|
ASSERT( deviceExtension->Signature == 0 );
|
|
deviceExtension->Signature = Signature;
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// Now create a Partition zero device object to attach to
|
|
//
|
|
|
|
if ( FAILED( StringCchPrintfW( clusdiskDeviceBuffer,
|
|
RTL_NUMBER_OF(clusdiskDeviceBuffer),
|
|
CLUSDISK_DEVICE_NAME,
|
|
diskNumber,
|
|
0 ) ) ) {
|
|
FREE_AND_NULL_PTR( driveLayoutInfo );
|
|
continue;
|
|
}
|
|
|
|
RtlInitUnicodeString( &clusdiskDeviceName, clusdiskDeviceBuffer );
|
|
status = IoCreateDevice(DriverObject,
|
|
sizeof(CLUS_DEVICE_EXTENSION),
|
|
&clusdiskDeviceName,
|
|
FILE_DEVICE_DISK,
|
|
FILE_DEVICE_SECURE_OPEN,
|
|
FALSE,
|
|
&physicalDevice);
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
FREE_AND_NULL_PTR( driveLayoutInfo );
|
|
ClusDiskPrint((
|
|
1,
|
|
"[ClusDisk] AttachDevice: Failed to create device for Drive%u. %08X\n",
|
|
diskNumber,
|
|
status));
|
|
|
|
continue;
|
|
}
|
|
|
|
CDLOG( "ClusDiskAttachDevice: IoCreateDeviceP0 DO %p DiskNumber %d",
|
|
physicalDevice, diskNumber );
|
|
|
|
physicalDevice->Flags |= DO_DIRECT_IO;
|
|
|
|
//
|
|
// Point device extension back at device object and remember
|
|
// the disk number.
|
|
//
|
|
|
|
deviceExtension = physicalDevice->DeviceExtension;
|
|
zeroExtension = deviceExtension;
|
|
deviceExtension->DeviceObject = physicalDevice;
|
|
deviceExtension->DiskNumber = diskNumber;
|
|
deviceExtension->LastPartitionNumber = 0;
|
|
deviceExtension->DriverObject = DriverObject;
|
|
deviceExtension->Signature = Signature;
|
|
deviceExtension->AttachValid = TRUE;
|
|
deviceExtension->ReserveTimer = 0;
|
|
deviceExtension->PerformReserves = TRUE;
|
|
deviceExtension->ReserveFailure = 0;
|
|
deviceExtension->Detached = TRUE;
|
|
deviceExtension->OfflinePending = FALSE;
|
|
deviceExtension->ScsiAddress = scsiAddress;
|
|
deviceExtension->BusType = busType;
|
|
InitializeListHead( &deviceExtension->WaitingIoctls );
|
|
|
|
IoInitializeRemoveLock( &deviceExtension->RemoveLock, CLUSDISK_ALLOC_TAG, 0, 0 );
|
|
|
|
//
|
|
// Signal the worker thread running event.
|
|
//
|
|
KeInitializeEvent( &deviceExtension->Event, NotificationEvent, TRUE );
|
|
|
|
ExInitializeWorkItem(&deviceExtension->WorkItem,
|
|
(PWORKER_THREAD_ROUTINE)ClusDiskReservationWorker,
|
|
(PVOID)deviceExtension );
|
|
|
|
// Always set state to offline.
|
|
//
|
|
// We offline all the volumes later. For now, just mark the disk offline.
|
|
//
|
|
deviceExtension->DiskState = DiskOffline;
|
|
|
|
KeInitializeEvent( &deviceExtension->PagingPathCountEvent,
|
|
NotificationEvent, TRUE );
|
|
deviceExtension->PagingPathCount = 0;
|
|
deviceExtension->HibernationPathCount = 0;
|
|
deviceExtension->DumpPathCount = 0;
|
|
|
|
ExInitializeResourceLite( &deviceExtension->DriveLayoutLock );
|
|
ExInitializeResourceLite( &deviceExtension->ReserveInfoLock );
|
|
|
|
//
|
|
// This is the physical device object.
|
|
//
|
|
ObReferenceObject( physicalDevice );
|
|
deviceExtension->PhysicalDevice = physicalDevice;
|
|
|
|
//
|
|
// Attach to partition0. This call links the newly created
|
|
// device to the target device, returning the target device object.
|
|
// We may not want to stay attached for long... depending on
|
|
// whether this is a device we're interested in.
|
|
//
|
|
|
|
#if 0 // Why is this here?
|
|
//
|
|
// 2000/02/05: stevedz - Bug in FTDISK [note that we currently don't support FTDISK].
|
|
// There seems to be a bug in FTDISK, where the DO_DEVICE_INITIALIZING gets
|
|
// stuck when a new device is found during a RESCAN. We will unconditionally
|
|
// clear this bit for any device we are about to attach to. We could check
|
|
// if the device is an FTDISK device... but that might be work.
|
|
//
|
|
targetDevice->Flags &= ~DO_DEVICE_INITIALIZING;
|
|
#endif
|
|
|
|
attachedTargetDevice = IoAttachDeviceToDeviceStack(physicalDevice,
|
|
targetDevice);
|
|
ASSERT( attachedTargetDevice == targetDevice );
|
|
|
|
#if CLUSTER_FREE_ASSERTS && CLUSTER_STALL_THREAD
|
|
DBG_STALL_THREAD( 2 ); // Defined only for debugging.
|
|
#endif
|
|
|
|
if ( attachedTargetDevice == NULL ) {
|
|
ClusDiskPrint((1,
|
|
"[ClusDisk] AttachDevice: Failed to attach to device Drive%u.\n",
|
|
diskNumber));
|
|
|
|
FREE_AND_NULL_PTR( driveLayoutInfo );
|
|
ExDeleteResourceLite( &deviceExtension->DriveLayoutLock );
|
|
ExDeleteResourceLite( &deviceExtension->ReserveInfoLock );
|
|
IoDeleteDevice(physicalDevice);
|
|
continue;
|
|
}
|
|
|
|
deviceExtension->TargetDeviceObject = attachedTargetDevice;
|
|
deviceExtension->Detached = FALSE;
|
|
physicalDevice->Flags &= ~DO_DEVICE_INITIALIZING;
|
|
|
|
#if 0 // Can't have a FS on partition 0
|
|
if ( targetDevice->Vpb ) {
|
|
if ( targetDevice->Vpb->Flags & VPB_MOUNTED ) {
|
|
|
|
status = DismountPartition( targetDevice, diskNumber, 0 );
|
|
|
|
if ( !NT_SUCCESS( status )) {
|
|
ClusDiskPrint((1,
|
|
"[ClusDisk] AttachDevice: dismount of %u/0 failed %08X\n",
|
|
diskNumber, status));
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
CDLOG( "ClusDiskInfo *** Phys DO %p DevExt %p DiskNum %d Signature %08X RemLock %p ***",
|
|
physicalDevice,
|
|
deviceExtension,
|
|
deviceExtension->DiskNumber,
|
|
deviceExtension->Signature,
|
|
&deviceExtension->RemoveLock );
|
|
|
|
//
|
|
// make sure we haven't attached to a file system. if so, something
|
|
// whacky has occurred and consequently, we back what we just did
|
|
//
|
|
|
|
if (deviceExtension->TargetDeviceObject->DeviceType == FILE_DEVICE_DISK_FILE_SYSTEM) {
|
|
ClusDiskPrint((3,
|
|
"[ClusDisk] AttachDevice: We incorrectly attached our device %p to file system device %p \n",
|
|
physicalDevice,
|
|
deviceExtension->TargetDeviceObject));
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
KeAcquireSpinLock(&ClusDiskSpinLock, &irql);
|
|
deviceExtension->Detached = TRUE;
|
|
KeReleaseSpinLock(&ClusDiskSpinLock, irql);
|
|
ExDeleteResourceLite( &deviceExtension->DriveLayoutLock );
|
|
ExDeleteResourceLite( &deviceExtension->ReserveInfoLock );
|
|
IoDetachDevice( deviceExtension->TargetDeviceObject );
|
|
IoDeleteDevice( physicalDevice );
|
|
FREE_AND_NULL_PTR( driveLayoutInfo );
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Propagate driver's alignment requirements and power flags.
|
|
//
|
|
|
|
physicalDevice->AlignmentRequirement =
|
|
deviceExtension->TargetDeviceObject->AlignmentRequirement;
|
|
|
|
physicalDevice->SectorSize =
|
|
deviceExtension->TargetDeviceObject->SectorSize;
|
|
|
|
//
|
|
// The storage stack explicitly requires DO_POWER_PAGABLE to be
|
|
// set in all filter drivers *unless* DO_POWER_INRUSH is set.
|
|
// this is true even if the attached device doesn't set DO_POWER_PAGABLE.
|
|
//
|
|
if ( deviceExtension->TargetDeviceObject->Flags & DO_POWER_INRUSH) {
|
|
physicalDevice->Flags |= DO_POWER_INRUSH;
|
|
} else {
|
|
physicalDevice->Flags |= DO_POWER_PAGABLE;
|
|
}
|
|
|
|
//
|
|
// Add this device to our list of attached devices.
|
|
//
|
|
AddAttachedDevice( Signature, physicalDevice );
|
|
|
|
//
|
|
// Add the signature to the signatures list!
|
|
//
|
|
|
|
//
|
|
// Allocate buffer for Signatures registry key. So we can add
|
|
// the signature.
|
|
//
|
|
status = ClusDiskInitRegistryString(
|
|
&signatureName,
|
|
CLUSDISK_SIGNATURE_KEYNAME,
|
|
wcslen(CLUSDISK_SIGNATURE_KEYNAME)
|
|
);
|
|
if ( NT_SUCCESS(status) ) {
|
|
//
|
|
// Create the signature key under \Parameters\Signatures.
|
|
//
|
|
status = ClusDiskAddSignature(
|
|
&signatureName,
|
|
Signature,
|
|
FALSE
|
|
);
|
|
|
|
//
|
|
// Now write the disk name.
|
|
//
|
|
ClusDiskWriteDiskInfo( Signature,
|
|
deviceExtension->DiskNumber,
|
|
CLUSDISK_SIGNATURE_KEYNAME
|
|
);
|
|
|
|
ExFreePool( signatureName.Buffer );
|
|
}
|
|
|
|
//
|
|
// Remove the signature from the available list!
|
|
//
|
|
|
|
//
|
|
// Allocate buffer for AvailableDisks registry key. So we can
|
|
// delete the disk signature.
|
|
//
|
|
status = ClusDiskInitRegistryString(
|
|
&signatureName,
|
|
CLUSDISK_AVAILABLE_DISKS_KEYNAME,
|
|
wcslen(CLUSDISK_AVAILABLE_DISKS_KEYNAME)
|
|
);
|
|
if ( NT_SUCCESS(status) ) {
|
|
//
|
|
// Delete the signature key under \Parameters\AvailableDisks.
|
|
//
|
|
status = ClusDiskDeleteSignature(
|
|
&signatureName,
|
|
Signature
|
|
);
|
|
ExFreePool( signatureName.Buffer );
|
|
}
|
|
|
|
//
|
|
// During installation, we want to dismount and then mark the disk
|
|
// offline. The disk may have been in use before it was added
|
|
// to the cluster, and if we offline first, we see "delayed
|
|
// write" error messages. During normal attach (not install), we
|
|
// offline first, then dismount, to make sure no data is actually
|
|
// written to the disk.
|
|
//
|
|
|
|
if ( InstallMode ) {
|
|
|
|
//
|
|
// Dismount all volumes on this disk.
|
|
//
|
|
|
|
ClusDiskDismountDevice( diskNumber, TRUE );
|
|
|
|
//
|
|
// Offline all volumes on this disk.
|
|
//
|
|
|
|
OFFLINE_DISK( zeroExtension );
|
|
|
|
} else {
|
|
|
|
//
|
|
// Offline all volumes on this disk.
|
|
//
|
|
|
|
OFFLINE_DISK_PDO( zeroExtension );
|
|
OFFLINE_DISK( zeroExtension );
|
|
|
|
//
|
|
// Dismount all volumes on this disk.
|
|
//
|
|
|
|
ClusDiskDismountDevice( diskNumber, TRUE );
|
|
}
|
|
|
|
//
|
|
// Now enumerate the partitions on this device.
|
|
//
|
|
|
|
for (partIndex = 0;
|
|
partIndex < driveLayoutInfo->PartitionCount;
|
|
partIndex++)
|
|
{
|
|
|
|
partitionInfo = &driveLayoutInfo->PartitionEntry[partIndex];
|
|
|
|
//
|
|
// Make sure that there really is a partition here.
|
|
//
|
|
|
|
if ( !partitionInfo->Mbr.RecognizedPartition ||
|
|
partitionInfo->PartitionNumber == 0 )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
CreateVolumeObject( zeroExtension,
|
|
diskNumber,
|
|
partitionInfo->PartitionNumber,
|
|
NULL );
|
|
}
|
|
|
|
FREE_AND_NULL_PTR( driveLayoutInfo );
|
|
finalStatus = STATUS_SUCCESS;
|
|
break;
|
|
}
|
|
|
|
DEREFERENCE_OBJECT( targetDevice );
|
|
|
|
CDLOG( "ClusDiskAttachDevice: Exit status %!status!", finalStatus );
|
|
|
|
return(finalStatus);
|
|
|
|
} // ClusDiskAttachDevice
|
|
|
|
|
|
|
|
NTSTATUS
|
|
ClusDiskDetachDevice(
|
|
ULONG Signature,
|
|
PDRIVER_OBJECT DriverObject
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Detach from a disk device with the signature specified.
|
|
|
|
Arguments:
|
|
|
|
Signature - the signature for the device to detach from.
|
|
|
|
DriverObject - the driver object for our device.
|
|
|
|
Return Value:
|
|
|
|
NT Status
|
|
|
|
Notes:
|
|
|
|
We have to be careful with the partition0 devices. RAW doesn't support
|
|
dismount, so it is not clear that we can actually delete those device
|
|
objects, since they could be cached by FileSystems like RAW!
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
PDEVICE_LIST_ENTRY deviceEntry;
|
|
PCLUS_DEVICE_EXTENSION deviceExtension;
|
|
PCLUS_DEVICE_EXTENSION physicalDeviceExtension;
|
|
PDEVICE_OBJECT deviceObject = DriverObject->DeviceObject;
|
|
UNICODE_STRING signatureName;
|
|
UNICODE_STRING availableName;
|
|
KIRQL irql;
|
|
PCLUS_DEVICE_EXTENSION foundExtension = NULL;
|
|
PLIST_ENTRY listEntry;
|
|
PIRP irp;
|
|
|
|
//
|
|
// Find our device entry.
|
|
//
|
|
|
|
// 2000/02/05: stevedz - added synchronization.
|
|
|
|
ACQUIRE_SHARED( &ClusDiskDeviceListLock );
|
|
|
|
deviceEntry = ClusDiskDeviceList;
|
|
while ( deviceEntry ) {
|
|
if ( deviceEntry->Signature == Signature ) {
|
|
break;
|
|
}
|
|
deviceEntry = deviceEntry->Next;
|
|
}
|
|
|
|
if ( (deviceEntry == NULL) ||
|
|
!deviceEntry->Attached ) {
|
|
if ( deviceEntry ) {
|
|
ClusDiskPrint((
|
|
1,
|
|
"[ClusDisk] DetachDevice: Failed to detach signature = %lx, attached = %lx\n",
|
|
Signature, deviceEntry->Attached ));
|
|
} else {
|
|
ClusDiskPrint((
|
|
1,
|
|
"[ClusDisk] DetachDevice: Failed to detach signature = %lx\n",
|
|
Signature ));
|
|
|
|
}
|
|
|
|
RELEASE_SHARED( &ClusDiskDeviceListLock );
|
|
|
|
goto DeleteDiskSignature;
|
|
}
|
|
|
|
//
|
|
// Now find all devices that we are attached to with this signature.
|
|
//
|
|
KeAcquireSpinLock(&ClusDiskSpinLock, &irql);
|
|
while ( deviceObject ) {
|
|
deviceExtension = deviceObject->DeviceExtension;
|
|
//
|
|
// Only disable devices with our signature, but
|
|
// Don't remove the root device...
|
|
//
|
|
if ( (deviceExtension->Signature == Signature) ) {
|
|
//
|
|
// Remember one found extension - it doesn't matter which one.
|
|
//
|
|
foundExtension = deviceExtension;
|
|
|
|
//
|
|
// Detach from the target device. This only requires marking
|
|
// the device object as detached!
|
|
//
|
|
deviceExtension->Detached = TRUE;
|
|
|
|
//
|
|
// We want to make sure reserves are stopped because the disk is
|
|
// not going to be controlled by the cluster any longer.
|
|
//
|
|
ASSERT_RESERVES_STOPPED( deviceExtension );
|
|
|
|
//
|
|
// Make this device available again.
|
|
//
|
|
// deviceExtension->DiskState = DiskOnline;
|
|
ONLINE_DISK( deviceExtension );
|
|
|
|
ClusDiskPrint(( 3,
|
|
"[ClusDisk] DetachDevice: Marking signature = %lx online \n",
|
|
Signature ));
|
|
|
|
}
|
|
deviceObject = deviceObject->NextDevice;
|
|
}
|
|
KeReleaseSpinLock(&ClusDiskSpinLock, irql);
|
|
RELEASE_SHARED( &ClusDiskDeviceListLock );
|
|
|
|
//
|
|
// Delete all drive letters for this disk signature, and assign letters
|
|
// to the correct device name.
|
|
//
|
|
if ( foundExtension ) {
|
|
|
|
physicalDeviceExtension = foundExtension->PhysicalDevice->DeviceExtension;
|
|
|
|
//
|
|
// Signal all waiting Irp's on the physical device extension.
|
|
//
|
|
IoAcquireCancelSpinLock( &irql );
|
|
KeAcquireSpinLockAtDpcLevel(&ClusDiskSpinLock);
|
|
while ( !IsListEmpty(&physicalDeviceExtension->WaitingIoctls) ) {
|
|
listEntry = RemoveHeadList(&physicalDeviceExtension->WaitingIoctls);
|
|
irp = CONTAINING_RECORD( listEntry,
|
|
IRP,
|
|
Tail.Overlay.ListEntry );
|
|
ClusDiskCompletePendingRequest(irp, STATUS_SUCCESS, physicalDeviceExtension);
|
|
}
|
|
KeReleaseSpinLockFromDpcLevel(&ClusDiskSpinLock);
|
|
IoReleaseCancelSpinLock( irql );
|
|
}
|
|
|
|
DeleteDiskSignature:
|
|
|
|
//
|
|
// Allocate buffer for signatures registry key.
|
|
//
|
|
status = ClusDiskInitRegistryString(
|
|
&signatureName,
|
|
CLUSDISK_SIGNATURE_KEYNAME,
|
|
wcslen(CLUSDISK_SIGNATURE_KEYNAME)
|
|
);
|
|
if ( !NT_SUCCESS(status) ) {
|
|
return(status);
|
|
}
|
|
|
|
//
|
|
// Allocate buffer for our list of available signatures,
|
|
// and form the subkey string name.
|
|
//
|
|
status = ClusDiskInitRegistryString(
|
|
&availableName,
|
|
CLUSDISK_AVAILABLE_DISKS_KEYNAME,
|
|
wcslen(CLUSDISK_AVAILABLE_DISKS_KEYNAME)
|
|
);
|
|
if ( !NT_SUCCESS(status) ) {
|
|
ExFreePool( signatureName.Buffer );
|
|
return(STATUS_INSUFFICIENT_RESOURCES);
|
|
}
|
|
|
|
//
|
|
// Always - remove signature from signature list.
|
|
//
|
|
status = ClusDiskDeleteSignature(
|
|
&signatureName,
|
|
Signature
|
|
);
|
|
|
|
//
|
|
// always add signature to available list. This will handle the case
|
|
// where the disk signature was not found by this system in a previous
|
|
// scan of the disks.
|
|
//
|
|
|
|
ClusDiskPrint((3,
|
|
"[ClusDisk] DetachDevice: adding disk %08X to available disks list\n",
|
|
Signature));
|
|
|
|
status = ClusDiskAddSignature(
|
|
&availableName,
|
|
Signature,
|
|
TRUE
|
|
);
|
|
|
|
ExFreePool( signatureName.Buffer );
|
|
ExFreePool( availableName.Buffer );
|
|
|
|
return(STATUS_SUCCESS);
|
|
|
|
} // ClusDiskDetachDevice
|
|
|
|
|
|
|
|
NTSTATUS
|
|
ClusDiskRead(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the driver entry point for read and write requests
|
|
to disks to which the clusdisk driver has attached.
|
|
This driver collects statistics and then sets a completion
|
|
routine so that it can collect additional information when
|
|
the request completes. Then it calls the next driver below
|
|
it.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject
|
|
Irp
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
|
|
{
|
|
PCLUS_DEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
|
|
PCLUS_DEVICE_EXTENSION physicalDisk =
|
|
deviceExtension->PhysicalDevice->DeviceExtension;
|
|
PIO_STACK_LOCATION currentIrpStack = IoGetCurrentIrpStackLocation(Irp);
|
|
|
|
NTSTATUS status;
|
|
|
|
status = AcquireRemoveLock(&deviceExtension->RemoveLock, Irp);
|
|
if (!NT_SUCCESS(status)) {
|
|
Irp->IoStatus.Status = status;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return status;
|
|
}
|
|
|
|
status = AcquireRemoveLock(&physicalDisk->RemoveLock, Irp);
|
|
if (!NT_SUCCESS(status)) {
|
|
ReleaseRemoveLock(&deviceExtension->RemoveLock, Irp);
|
|
Irp->IoStatus.Status = status;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return status;
|
|
}
|
|
|
|
//
|
|
// Return error if device is our root device.
|
|
//
|
|
if ( deviceExtension->BusType == RootBus ) {
|
|
ReleaseRemoveLock(&physicalDisk->RemoveLock, Irp);
|
|
ReleaseRemoveLock(&deviceExtension->RemoveLock, Irp);
|
|
Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return(STATUS_INVALID_DEVICE_REQUEST);
|
|
}
|
|
|
|
//
|
|
// Make sure the device attach completed.
|
|
//
|
|
status = WaitForAttachCompletion( deviceExtension,
|
|
FALSE, // No wait
|
|
TRUE ); // Also check physical device
|
|
if ( !NT_SUCCESS( status ) ) {
|
|
ReleaseRemoveLock(&physicalDisk->RemoveLock, Irp);
|
|
ReleaseRemoveLock(&deviceExtension->RemoveLock, Irp);
|
|
Irp->IoStatus.Status = status;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return status;
|
|
}
|
|
|
|
//
|
|
// Return error if disk is not Online.
|
|
// ReclaimInProgress means we are trying to create the volume objects
|
|
// and reads should pass through (but not writes).
|
|
//
|
|
|
|
if ( physicalDisk->DiskState != DiskOnline )
|
|
{
|
|
CDLOG( "ClusDiskRead(%p): Irp %p Reject len %d offset %I64x",
|
|
DeviceObject,
|
|
Irp,
|
|
currentIrpStack->Parameters.Read.Length,
|
|
currentIrpStack->Parameters.Read.ByteOffset.QuadPart
|
|
);
|
|
|
|
ClusDiskPrint((
|
|
3,
|
|
"[ClusDisk] ClusDiskRead: ReadReject IRP %p for signature %p (%p) (PD) \n",
|
|
Irp,
|
|
physicalDisk->Signature,
|
|
physicalDisk));
|
|
|
|
ReleaseRemoveLock(&physicalDisk->RemoveLock, Irp);
|
|
ReleaseRemoveLock(&deviceExtension->RemoveLock, Irp);
|
|
Irp->IoStatus.Status = STATUS_DEVICE_OFF_LINE;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return(STATUS_DEVICE_OFF_LINE);
|
|
}
|
|
|
|
CDLOGF(READ,"ClusDiskRead(%p): Irp %p Read len %d offset %I64x",
|
|
DeviceObject,
|
|
Irp,
|
|
currentIrpStack->Parameters.Read.Length,
|
|
currentIrpStack->Parameters.Read.ByteOffset.QuadPart );
|
|
|
|
//
|
|
// Until we start doing something in the completion routine, just
|
|
// skip this driver. In the future, we might want to report errors
|
|
// back to the cluster software... but that could get tricky, since some
|
|
// requests should fail, but that is expected.
|
|
//
|
|
#if 0
|
|
//
|
|
// Copy current stack to next stack.
|
|
//
|
|
|
|
IoCopyCurrentIrpStackLocationToNext( Irp );
|
|
|
|
//
|
|
// Set completion routine callback.
|
|
//
|
|
|
|
IoSetCompletionRoutine(Irp,
|
|
ClusDiskIoCompletion,
|
|
NULL, // Completion context
|
|
TRUE, // Invoke on success
|
|
TRUE, // Invoke on error
|
|
TRUE); // Invoke on cancel
|
|
#else
|
|
//
|
|
// Set current stack back one.
|
|
//
|
|
|
|
IoSkipCurrentIrpStackLocation( Irp );
|
|
|
|
ReleaseRemoveLock(&physicalDisk->RemoveLock, Irp);
|
|
ReleaseRemoveLock(&deviceExtension->RemoveLock, Irp);
|
|
|
|
#endif
|
|
|
|
//
|
|
// Return the results of the call to the disk driver.
|
|
//
|
|
|
|
return IoCallDriver(deviceExtension->TargetDeviceObject,
|
|
Irp);
|
|
|
|
} // ClusDiskRead
|
|
|
|
|
|
|
|
NTSTATUS
|
|
ClusDiskWrite(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the driver entry point for read and write requests
|
|
to disks to which the clusdisk driver has attached.
|
|
This driver collects statistics and then sets a completion
|
|
routine so that it can collect additional information when
|
|
the request completes. Then it calls the next driver below
|
|
it.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject
|
|
Irp
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
|
|
{
|
|
PCLUS_DEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
|
|
PCLUS_DEVICE_EXTENSION physicalDisk =
|
|
deviceExtension->PhysicalDevice->DeviceExtension;
|
|
PIO_STACK_LOCATION currentIrpStack = IoGetCurrentIrpStackLocation(Irp);
|
|
KIRQL irql;
|
|
|
|
NTSTATUS status;
|
|
|
|
status = AcquireRemoveLock(&deviceExtension->RemoveLock, Irp);
|
|
if ( !NT_SUCCESS(status) ) {
|
|
Irp->IoStatus.Status = status;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return status;
|
|
}
|
|
|
|
status = AcquireRemoveLock(&physicalDisk->RemoveLock, Irp);
|
|
if ( !NT_SUCCESS(status) ) {
|
|
ReleaseRemoveLock(&deviceExtension->RemoveLock, Irp);
|
|
Irp->IoStatus.Status = status;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return status;
|
|
}
|
|
|
|
//
|
|
// Return error if device is our root device.
|
|
//
|
|
if ( deviceExtension->BusType == RootBus ) {
|
|
ReleaseRemoveLock(&physicalDisk->RemoveLock, Irp);
|
|
ReleaseRemoveLock(&deviceExtension->RemoveLock, Irp);
|
|
Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return(STATUS_INVALID_DEVICE_REQUEST);
|
|
}
|
|
|
|
//
|
|
// Make sure the device attach completed.
|
|
//
|
|
status = WaitForAttachCompletion( deviceExtension,
|
|
FALSE, // No wait
|
|
TRUE ); // Also check physical device
|
|
if ( !NT_SUCCESS( status ) ) {
|
|
ReleaseRemoveLock(&physicalDisk->RemoveLock, Irp);
|
|
ReleaseRemoveLock(&deviceExtension->RemoveLock, Irp);
|
|
Irp->IoStatus.Status = status;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return status;
|
|
}
|
|
|
|
//
|
|
// Return error if disk is not Online.
|
|
//
|
|
|
|
if ( physicalDisk->DiskState != DiskOnline ) {
|
|
CDLOG( "ClusDiskWrite(%p) Reject irp %p", DeviceObject, Irp );
|
|
ClusDiskPrint((
|
|
3,
|
|
"[ClusDisk] ClusDiskWrite: WriteReject IRP %p for signature %p (%p) (PD) \n",
|
|
Irp,
|
|
physicalDisk->Signature,
|
|
physicalDisk));
|
|
ReleaseRemoveLock(&physicalDisk->RemoveLock, Irp);
|
|
ReleaseRemoveLock(&deviceExtension->RemoveLock, Irp);
|
|
Irp->IoStatus.Status = STATUS_DEVICE_OFF_LINE;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return(STATUS_DEVICE_OFF_LINE);
|
|
}
|
|
|
|
//
|
|
// Trace writes to the first few sectors
|
|
//
|
|
CDLOGF(WRITE, "ClusDiskWrite(%p) Irp %p Write len %d offset %I64x",
|
|
DeviceObject,
|
|
Irp,
|
|
currentIrpStack->Parameters.Write.Length,
|
|
currentIrpStack->Parameters.Write.ByteOffset.QuadPart );
|
|
|
|
KeQuerySystemTime( &physicalDisk->LastWriteTime );
|
|
|
|
//
|
|
// Until we start doing something in the completion routine, just
|
|
// skip this driver. In the future, we might want to report errors
|
|
// back to the cluster software... but that could get tricky, since some
|
|
// requests should fail, but that is expected.
|
|
//
|
|
#if 0
|
|
//
|
|
// Copy current stack to next stack.
|
|
//
|
|
|
|
IoCopyCurrentIrpStackLocationToNext( Irp );
|
|
|
|
//
|
|
// Set completion routine callback.
|
|
//
|
|
|
|
IoSetCompletionRoutine(Irp,
|
|
ClusDiskIoCompletion,
|
|
NULL, // Completion context
|
|
TRUE, // Invoke on success
|
|
TRUE, // Invoke on error
|
|
TRUE); // Invoke on cancel
|
|
#else
|
|
//
|
|
// Set current stack back one.
|
|
//
|
|
|
|
IoSkipCurrentIrpStackLocation( Irp );
|
|
|
|
ReleaseRemoveLock(&physicalDisk->RemoveLock, Irp);
|
|
ReleaseRemoveLock(&deviceExtension->RemoveLock, Irp);
|
|
|
|
#endif
|
|
|
|
//
|
|
// Return the results of the call to the disk driver.
|
|
//
|
|
|
|
return IoCallDriver(deviceExtension->TargetDeviceObject,
|
|
Irp);
|
|
|
|
} // ClusDiskWrite
|
|
|
|
|
|
|
|
NTSTATUS
|
|
ClusDiskIoCompletion(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID Context
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will get control from the system at the completion of an IRP.
|
|
It will calculate the difference between the time the IRP was started
|
|
and the current time, and decrement the queue depth.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - for the IRP.
|
|
Irp - The I/O request that just completed.
|
|
Context - Not used.
|
|
|
|
Return Value:
|
|
|
|
The IRP status.
|
|
|
|
--*/
|
|
|
|
{
|
|
PCLUS_DEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
|
|
PCLUS_DEVICE_EXTENSION physicalDisk = deviceExtension->PhysicalDevice->DeviceExtension;
|
|
|
|
UNREFERENCED_PARAMETER(Context);
|
|
|
|
CDLOGF(WRITE, "ClusDiskIoCompletion: CompletedIrp DevObj %p Irp %p",
|
|
DeviceObject,
|
|
Irp );
|
|
|
|
if ( physicalDisk->DiskState != DiskOnline ) {
|
|
CDLOGF(WRITE,"ClusDiskIoCompletion: CompletedIrpNotOnline DevObj %p Irp %p %!diskstate!",
|
|
DeviceObject,
|
|
Irp,
|
|
physicalDisk->DiskState );
|
|
CDLOG( "ClusDiskIoCompletion: CompletedIrpNotOnline2 DevObj %p Irp %p %!diskstate!",
|
|
DeviceObject,
|
|
Irp,
|
|
physicalDisk->DiskState );
|
|
}
|
|
|
|
ReleaseRemoveLock( &deviceExtension->RemoveLock, Irp );
|
|
ReleaseRemoveLock( &physicalDisk->RemoveLock, Irp );
|
|
|
|
if (Irp->PendingReturned) {
|
|
IoMarkIrpPending(Irp);
|
|
}
|
|
return STATUS_SUCCESS;
|
|
|
|
|
|
} // ClusDiskIoCompletion
|
|
|
|
#if 0
|
|
|
|
NTSTATUS
|
|
ClusDiskUpdateDriveLayout(
|
|
IN PDEVICE_OBJECT PhysicalDeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called after an IOCTL to set drive layout completes.
|
|
It attempts to attach to each partition in the system. If it fails
|
|
then it is assumed that clusdisk has already attached. After
|
|
the attach the new device extension is set up to point to the
|
|
device extension representing the physical disk. There are no
|
|
data items or other pointers that need to be cleaned up on a
|
|
per partition basis.
|
|
|
|
Arguments:
|
|
|
|
PhysicalDeviceObject - Pointer to device object for the disk just changed.
|
|
Irp - IRP involved.
|
|
|
|
Return Value:
|
|
|
|
NT Status
|
|
|
|
--*/
|
|
|
|
{
|
|
PCLUS_DEVICE_EXTENSION physicalExtension = PhysicalDeviceObject->DeviceExtension;
|
|
ULONG partitionNumber = physicalExtension->LastPartitionNumber;
|
|
PDEVICE_OBJECT targetObject;
|
|
PDEVICE_OBJECT deviceObject;
|
|
PCLUS_DEVICE_EXTENSION deviceExtension;
|
|
WCHAR ntDeviceName[MAX_PARTITION_NAME_LENGTH];
|
|
STRING ntString;
|
|
UNICODE_STRING ntUnicodeString;
|
|
PFILE_OBJECT fileObject;
|
|
NTSTATUS status;
|
|
KIRQL irql;
|
|
|
|
//
|
|
// Attach to any new partitions created by the set layout call.
|
|
//
|
|
|
|
do {
|
|
|
|
//
|
|
// Get first/next partition. Already attached to the disk,
|
|
// otherwise control would not have been passed to this driver
|
|
// on the device I/O control.
|
|
//
|
|
|
|
partitionNumber++;
|
|
|
|
//
|
|
// Create unicode NT device name.
|
|
//
|
|
|
|
if ( FAILED( StringCchPrintfW( ntDeviceName,
|
|
RTL_NUMBER_OF(ntDeviceName),
|
|
DEVICE_PARTITION_NAME,
|
|
physicalExtension->DiskNumber,
|
|
partitionNumber ) ) ) {
|
|
continue;
|
|
}
|
|
WCSLEN_ASSERT( ntDeviceName );
|
|
|
|
RtlInitUnicodeString(&ntUnicodeString, ntDeviceName);
|
|
|
|
//
|
|
// Get target device object.
|
|
//
|
|
|
|
status = IoGetDeviceObjectPointer(&ntUnicodeString,
|
|
FILE_READ_ATTRIBUTES,
|
|
&fileObject,
|
|
&targetObject);
|
|
|
|
//
|
|
// If this fails then it is because there is no such device
|
|
// which signals completion.
|
|
//
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Dereference file object as these are the rules.
|
|
//
|
|
|
|
ObDereferenceObject(fileObject);
|
|
|
|
//
|
|
// Check if this device is already mounted.
|
|
//
|
|
|
|
if ( (targetObject->Vpb &&
|
|
(targetObject->Vpb->Flags & VPB_MOUNTED)) ) {
|
|
|
|
//
|
|
// Assume this device has already been attached.
|
|
//
|
|
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Create device object for this partition.
|
|
//
|
|
|
|
status = IoCreateDevice(physicalExtension->DriverObject,
|
|
sizeof(CLUS_DEVICE_EXTENSION),
|
|
NULL, // XXXX
|
|
FILE_DEVICE_DISK,
|
|
FILE_DEVICE_SECURE_OPEN,
|
|
FALSE,
|
|
&deviceObject);
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
continue;
|
|
}
|
|
|
|
deviceObject->Flags |= DO_DIRECT_IO;
|
|
|
|
//
|
|
// Point device extension back at device object.
|
|
//
|
|
|
|
deviceExtension = deviceObject->DeviceExtension;
|
|
deviceExtension->DeviceObject = deviceObject;
|
|
|
|
//
|
|
// Store pointer to physical device and disk/driver information.
|
|
//
|
|
ObReferenceObject( PhysicalDeviceObject );
|
|
deviceExtension->PhysicalDevice = PhysicalDeviceObject;
|
|
deviceExtension->DiskNumber = physicalExtension->DiskNumber;
|
|
deviceExtension->DriverObject = physicalExtension->DriverObject;
|
|
deviceExtension->BusType = physicalExtension->BusType;
|
|
deviceExtension->ReserveTimer = 0;
|
|
deviceExtension->PerformReserves = FALSE;
|
|
deviceExtension->ReserveFailure = 0;
|
|
deviceExtension->Signature = physicalExtension->Signature;
|
|
deviceExtension->Detached = TRUE;
|
|
deviceExtension->OfflinePending = FALSE;
|
|
InitializeListHead( &deviceExtension->WaitingIoctls );
|
|
InitializeListHead( &deviceExtension->HoldIO );
|
|
|
|
IoInitializeRemoveLock( &deviceExtension->RemoveLock, CLUSDISK_ALLOC_TAG, 0, 0 );
|
|
|
|
//
|
|
// Signal the worker thread running event.
|
|
//
|
|
KeInitializeEvent( &deviceExtension->Event, NotificationEvent, TRUE );
|
|
|
|
KeInitializeEvent( &deviceExtension->PagingPathCountEvent,
|
|
NotificationEvent, TRUE );
|
|
deviceExtension->PagingPathCount = 0;
|
|
deviceExtension->HibernationPathCount = 0;
|
|
deviceExtension->DumpPathCount = 0;
|
|
|
|
ExInitializeResourceLite( &deviceExtension->DriveLayoutLock );
|
|
ExInitializeResourceList( &deviceExtension->ReserveInfoLock );
|
|
|
|
//
|
|
// Update the highest partition number in partition zero
|
|
// and store the same value in this new extension just to initialize
|
|
// the field.
|
|
//
|
|
|
|
physicalExtension->LastPartitionNumber =
|
|
deviceExtension->LastPartitionNumber = partitionNumber;
|
|
|
|
//
|
|
// Attach to the partition. This call links the newly created
|
|
// device to the target device, returning the target device object.
|
|
//
|
|
status = IoAttachDevice(deviceObject,
|
|
&ntUnicodeString,
|
|
&deviceExtension->TargetDeviceObject);
|
|
|
|
if ( (!NT_SUCCESS(status)) || (status == STATUS_OBJECT_NAME_EXISTS) ) {
|
|
|
|
//
|
|
// Assume this device is already attached.
|
|
//
|
|
ExDeleteResourceLite( &deviceExtension->DriveLayoutLock );
|
|
ExDeleteResourceList( &deviceExtension->ReserveInfoLock );
|
|
IoDeleteDevice(deviceObject);
|
|
} else {
|
|
|
|
//
|
|
// Propagate driver's alignment requirements and power flags.
|
|
//
|
|
deviceExtension->Detached = FALSE;
|
|
|
|
deviceObject->AlignmentRequirement =
|
|
deviceExtension->TargetDeviceObject->AlignmentRequirement;
|
|
|
|
deviceObject->SectorSize =
|
|
deviceExtension->TargetDeviceObject->SectorSize;
|
|
|
|
//
|
|
// The storage stack explicitly requires DO_POWER_PAGABLE to be
|
|
// set in all filter drivers *unless* DO_POWER_INRUSH is set.
|
|
// this is true even if the attached device doesn't set DO_POWER_PAGABLE.
|
|
//
|
|
if ( deviceExtension->TargetDeviceObject->Flags & DO_POWER_INRUSH) {
|
|
deviceObject->Flags |= DO_POWER_INRUSH;
|
|
} else {
|
|
deviceObject->Flags |= DO_POWER_PAGABLE;
|
|
}
|
|
|
|
deviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
|
|
|
|
}
|
|
} while (TRUE);
|
|
|
|
return Irp->IoStatus.Status;
|
|
|
|
} // ClusDiskUpdateDriveLayout
|
|
#endif
|
|
|
|
|
|
NTSTATUS
|
|
ClusDiskGetRunningDevices(
|
|
IN PVOID Buffer,
|
|
IN ULONG BufferSize
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Find out the list of signatures for devices with active reservations.
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG bufferSize = BufferSize;
|
|
PULONG itemCount = (PULONG)Buffer;
|
|
PULONG nextSignature;
|
|
PDEVICE_OBJECT deviceObject;
|
|
PCLUS_DEVICE_EXTENSION deviceExtension;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
KIRQL irql;
|
|
|
|
if ( bufferSize < sizeof(ULONG) ) {
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
goto FnExit;
|
|
}
|
|
|
|
bufferSize -= sizeof(ULONG);
|
|
|
|
*itemCount = 0;
|
|
nextSignature = itemCount+1;
|
|
|
|
KeAcquireSpinLock(&ClusDiskSpinLock, &irql);
|
|
deviceObject = RootDeviceObject->DriverObject->DeviceObject;
|
|
|
|
//
|
|
// For each ClusDisk device, if we have a persistent reservation, then
|
|
// add it.
|
|
//
|
|
while ( deviceObject ) {
|
|
deviceExtension = deviceObject->DeviceExtension;
|
|
if ( (deviceExtension->BusType != RootBus) &&
|
|
deviceExtension->ReserveTimer &&
|
|
(deviceExtension->PhysicalDevice == deviceObject) ) {
|
|
if ( bufferSize < sizeof(ULONG) ) {
|
|
break;
|
|
}
|
|
bufferSize -= sizeof(ULONG);
|
|
*itemCount = *itemCount + 1;
|
|
*nextSignature++ = deviceExtension->Signature;
|
|
}
|
|
deviceObject = deviceObject->NextDevice;
|
|
}
|
|
KeReleaseSpinLock(&ClusDiskSpinLock, irql);
|
|
|
|
//
|
|
// If there are more objects then room in the buffer, return error.
|
|
//
|
|
if ( deviceObject ) {
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
FnExit:
|
|
|
|
return status;
|
|
|
|
} // ClusDiskGetRunningDevices
|
|
|
|
|
|
|
|
NTSTATUS
|
|
GetScsiPortNumber(
|
|
IN ULONG DiskSignature,
|
|
IN PUCHAR DiskPortNumber
|
|
)
|
|
|
|
/*--
|
|
|
|
Routine Description:
|
|
|
|
Find the Scsi Port Number for the given device signature.
|
|
|
|
Arguments:
|
|
|
|
DiskSignature - supplies the disk signature for the requested device.
|
|
|
|
DiskPortNumber - returns the corresponding Scsi Port Number if found.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG driveLayoutSize;
|
|
PDRIVE_LAYOUT_INFORMATION_EX driveLayout = NULL;
|
|
ULONG diskCount;
|
|
ULONG diskNumber;
|
|
ULONG skipCount;
|
|
WCHAR deviceNameBuffer[MAX_PARTITION_NAME_LENGTH];
|
|
UNICODE_STRING unicodeString;
|
|
NTSTATUS status = STATUS_UNSUCCESSFUL;
|
|
OBJECT_ATTRIBUTES objectAttributes;
|
|
IO_STATUS_BLOCK ioStatus;
|
|
HANDLE ntFileHandle = NULL;
|
|
SCSI_ADDRESS scsiAddress;
|
|
HANDLE eventHandle;
|
|
|
|
|
|
//
|
|
// Get the system configuration information to get number of disks.
|
|
//
|
|
diskCount = IoGetConfigurationInformation()->DiskCount;
|
|
|
|
// Allocate a drive layout buffer.
|
|
driveLayoutSize = sizeof(DRIVE_LAYOUT_INFORMATION_EX) +
|
|
(MAX_PARTITIONS * sizeof(PARTITION_INFORMATION_EX));
|
|
driveLayout = ExAllocatePool( NonPagedPoolCacheAligned,
|
|
driveLayoutSize );
|
|
if ( driveLayout == NULL ) {
|
|
ClusDiskPrint((
|
|
1,
|
|
"[ClusDisk] GetScsiPortNumber: Failed to allocate root drive layout structure.\n"
|
|
));
|
|
return(STATUS_INSUFFICIENT_RESOURCES);
|
|
}
|
|
|
|
*DiskPortNumber = 0xff;
|
|
|
|
// Find the disk with the right signature
|
|
for ( diskNumber = 0, skipCount = 0;
|
|
( diskNumber < diskCount && skipCount < SKIP_COUNT_MAX );
|
|
diskNumber++ ) {
|
|
|
|
if ( ntFileHandle ) {
|
|
ZwClose( ntFileHandle );
|
|
ntFileHandle = NULL;
|
|
}
|
|
|
|
if ( FAILED( StringCchPrintfW( deviceNameBuffer,
|
|
RTL_NUMBER_OF(deviceNameBuffer),
|
|
DEVICE_PARTITION_NAME,
|
|
diskNumber,
|
|
0 ) ) ) {
|
|
continue;
|
|
}
|
|
|
|
WCSLEN_ASSERT( deviceNameBuffer );
|
|
|
|
// Create device name for the physical disk.
|
|
RtlInitUnicodeString(&unicodeString, deviceNameBuffer);
|
|
|
|
// Setup object attributes for the file to open.
|
|
InitializeObjectAttributes(&objectAttributes,
|
|
&unicodeString,
|
|
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
|
|
NULL,
|
|
NULL);
|
|
|
|
// Open the device.
|
|
status = ZwCreateFile( &ntFileHandle,
|
|
FILE_READ_DATA,
|
|
&objectAttributes,
|
|
&ioStatus,
|
|
NULL,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
FILE_OPEN,
|
|
FILE_SYNCHRONOUS_IO_NONALERT,
|
|
NULL,
|
|
0 );
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
ClusDiskPrint((
|
|
1,
|
|
"[ClusDisk] GetScsiPortNumber: Failed to open device [%ws]. Error %lx.\n",
|
|
deviceNameBuffer,
|
|
status ));
|
|
|
|
//
|
|
// If the device doesn't exist, this is likely a hole in the
|
|
// disk numbering.
|
|
//
|
|
|
|
if ( STATUS_FILE_INVALID == status ||
|
|
STATUS_DEVICE_DOES_NOT_EXIST == status ||
|
|
STATUS_OBJECT_PATH_NOT_FOUND == status ) {
|
|
skipCount++;
|
|
diskCount++;
|
|
ClusDiskPrint(( 3,
|
|
"[ClusDisk] GetScsiPortNumber: Adjust: skipCount %d diskCount %d \n",
|
|
skipCount,
|
|
diskCount ));
|
|
CDLOG( "GetScsiPortNumber: Adjust: skipCount %d diskCount %d ",
|
|
skipCount,
|
|
diskCount );
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
skipCount = 0; // Device found, reset skipCount
|
|
|
|
ClusDiskPrint(( 3,
|
|
"[ClusDisk] GetScsiPortNumber: Open device [%ws] succeeded.\n",
|
|
deviceNameBuffer ));
|
|
CDLOG( "GetScsiPortNumber: GetScsiPortNumber: Open device [%ws] succeeded. ",
|
|
deviceNameBuffer );
|
|
|
|
// Create event for notification.
|
|
status = ZwCreateEvent( &eventHandle,
|
|
EVENT_ALL_ACCESS,
|
|
NULL,
|
|
SynchronizationEvent,
|
|
FALSE );
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
ClusDiskPrint((
|
|
1,
|
|
"[ClusDisk] GetScsiPortNumber: Failed to create event. %08X\n",
|
|
status));
|
|
|
|
ExFreePool( driveLayout );
|
|
ZwClose( ntFileHandle );
|
|
return(status);
|
|
}
|
|
|
|
//
|
|
// Force storage drivers to flush cached drive layout. Get the
|
|
// drive layout even if the flush fails.
|
|
//
|
|
|
|
status = ZwDeviceIoControlFile( ntFileHandle,
|
|
eventHandle,
|
|
NULL,
|
|
NULL,
|
|
&ioStatus,
|
|
IOCTL_DISK_UPDATE_PROPERTIES,
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
0 );
|
|
if ( status == STATUS_PENDING ) {
|
|
status = ZwWaitForSingleObject(eventHandle,
|
|
FALSE,
|
|
NULL);
|
|
ASSERT( NT_SUCCESS(status) );
|
|
status = ioStatus.Status;
|
|
}
|
|
|
|
status = ZwDeviceIoControlFile( ntFileHandle,
|
|
eventHandle,
|
|
NULL,
|
|
NULL,
|
|
&ioStatus,
|
|
IOCTL_DISK_GET_DRIVE_LAYOUT_EX,
|
|
NULL,
|
|
0,
|
|
driveLayout,
|
|
driveLayoutSize );
|
|
|
|
if ( status == STATUS_PENDING ) {
|
|
status = ZwWaitForSingleObject(eventHandle,
|
|
FALSE,
|
|
NULL);
|
|
ASSERT( NT_SUCCESS(status) );
|
|
status = ioStatus.Status;
|
|
}
|
|
|
|
ZwClose( eventHandle );
|
|
|
|
if ( NT_SUCCESS(status) ) {
|
|
|
|
if ( PARTITION_STYLE_MBR != driveLayout->PartitionStyle ) {
|
|
ClusDiskPrint(( 3,
|
|
"[ClusDisk] GetScsiPortNumber: skipping non-MBR disk \n" ));
|
|
CDLOG( "GetScsiPortNumber: GetScsiPortNumber: skipping non-MBR disk " );
|
|
continue;
|
|
}
|
|
|
|
if ( DiskSignature == driveLayout->Mbr.Signature ) {
|
|
|
|
ClusDiskPrint(( 3,
|
|
"[ClusDisk] GetScsiPortNumber: Signature match %08X \n",
|
|
DiskSignature ));
|
|
CDLOG( "GetScsiPortNumber: GetScsiPortNumber: Signature match %08X ",
|
|
DiskSignature );
|
|
|
|
// Create event for notification.
|
|
status = ZwCreateEvent( &eventHandle,
|
|
EVENT_ALL_ACCESS,
|
|
NULL,
|
|
SynchronizationEvent,
|
|
FALSE );
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
ClusDiskPrint((
|
|
1,
|
|
"[ClusDisk] GetScsiPortNumber: Failed to create event. %08X\n",
|
|
status));
|
|
|
|
ExFreePool( driveLayout );
|
|
ZwClose( ntFileHandle );
|
|
return(status);
|
|
}
|
|
|
|
// Get the port number for the SystemRoot disk device.
|
|
status = ZwDeviceIoControlFile( ntFileHandle,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&ioStatus,
|
|
IOCTL_SCSI_GET_ADDRESS,
|
|
NULL,
|
|
0,
|
|
&scsiAddress,
|
|
sizeof(SCSI_ADDRESS) );
|
|
|
|
if ( status == STATUS_PENDING ) {
|
|
status = ZwWaitForSingleObject(eventHandle,
|
|
FALSE,
|
|
NULL);
|
|
ASSERT( NT_SUCCESS(status) );
|
|
status = ioStatus.Status;
|
|
}
|
|
|
|
if ( NT_SUCCESS(status) ) {
|
|
*DiskPortNumber = scsiAddress.PortNumber;
|
|
break;
|
|
} else {
|
|
ClusDiskPrint((
|
|
1,
|
|
"[ClusDisk] GetScsiAddress FAILED. Error %lx\n",
|
|
status));
|
|
}
|
|
|
|
} else {
|
|
|
|
ClusDiskPrint(( 3,
|
|
"[ClusDisk] GetScsiPortNumber: Signature mismatch %08X \n",
|
|
DiskSignature ));
|
|
CDLOG( "GetScsiPortNumber: GetScsiPortNumber: Signature mismatch %08X ",
|
|
DiskSignature );
|
|
|
|
continue;
|
|
}
|
|
} else {
|
|
ClusDiskPrint((
|
|
1,
|
|
"[ClusDisk] GetScsiAddress, GetDriveLayout FAILED. Error %lx.\n",
|
|
status));
|
|
}
|
|
}
|
|
|
|
if ( ntFileHandle ) {
|
|
ZwClose( ntFileHandle );
|
|
}
|
|
|
|
ExFreePool( driveLayout );
|
|
|
|
return(status);
|
|
|
|
} // GetScsiPortNumber
|
|
|
|
|
|
|
|
NTSTATUS
|
|
IsDiskClusterCapable(
|
|
IN UCHAR PortNumber,
|
|
OUT PBOOLEAN IsCapable
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Check if a given SCSI port device supports cluster manageable SCSI devices.
|
|
|
|
Arguments:
|
|
|
|
PortNumber - the port number for the SCSI device.
|
|
|
|
IsCapable - returns whether the SCSI device is cluster capable. That is,
|
|
supports, RESERVE/RELEASE/BUS RESET, etc.
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
|
|
{
|
|
WCHAR deviceNameBuffer[64];
|
|
UNICODE_STRING deviceNameString;
|
|
NTSTATUS status;
|
|
OBJECT_ATTRIBUTES objectAttributes;
|
|
IO_STATUS_BLOCK ioStatus;
|
|
HANDLE ntFileHandle;
|
|
HANDLE eventHandle;
|
|
SRB_IO_CONTROL srbControl;
|
|
|
|
|
|
*IsCapable = TRUE; // Err on the side of being usable.
|
|
|
|
if ( FAILED( StringCchPrintfW( deviceNameBuffer,
|
|
RTL_NUMBER_OF(deviceNameBuffer),
|
|
L"\\Device\\Scsiport%d",
|
|
PortNumber) ) ) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
WCSLEN_ASSERT( deviceNameBuffer );
|
|
|
|
// Create device name for the scsiport driver
|
|
RtlInitUnicodeString(&deviceNameString, deviceNameBuffer);
|
|
|
|
// Setup object attributes for the file to open.
|
|
InitializeObjectAttributes(&objectAttributes,
|
|
&deviceNameString,
|
|
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
|
|
NULL,
|
|
NULL);
|
|
|
|
// Open the device.
|
|
status = ZwCreateFile( &ntFileHandle,
|
|
FILE_READ_DATA,
|
|
&objectAttributes,
|
|
&ioStatus,
|
|
NULL,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
FILE_OPEN,
|
|
FILE_SYNCHRONOUS_IO_NONALERT,
|
|
NULL,
|
|
0 );
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
ClusDiskPrint((1,
|
|
"[ClusDisk] IsDiskClusterCapable: Failed to open device [%ws]. Error %08X.\n",
|
|
deviceNameString, status ));
|
|
return(status);
|
|
}
|
|
|
|
// Create event for notification.
|
|
status = ZwCreateEvent( &eventHandle,
|
|
EVENT_ALL_ACCESS,
|
|
NULL,
|
|
SynchronizationEvent,
|
|
FALSE );
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
ClusDiskPrint((1, "[ClusDisk] IsDiskClusterCapable: Failed to create event. %08X\n",
|
|
status));
|
|
|
|
ZwClose( ntFileHandle );
|
|
return(status);
|
|
}
|
|
|
|
srbControl.HeaderLength = sizeof(SRB_IO_CONTROL);
|
|
RtlMoveMemory( srbControl.Signature, "CLUSDISK", 8 );
|
|
srbControl.Timeout = 3;
|
|
srbControl.Length = 0;
|
|
srbControl.ControlCode = IOCTL_SCSI_MINIPORT_NOT_QUORUM_CAPABLE;
|
|
|
|
status = ZwDeviceIoControlFile(ntFileHandle,
|
|
eventHandle,
|
|
NULL,
|
|
NULL,
|
|
&ioStatus,
|
|
IOCTL_SCSI_MINIPORT,
|
|
&srbControl,
|
|
sizeof(SRB_IO_CONTROL),
|
|
NULL,
|
|
0 );
|
|
|
|
if ( status == STATUS_PENDING ) {
|
|
status = ZwWaitForSingleObject(eventHandle,
|
|
FALSE,
|
|
NULL);
|
|
ASSERT( NT_SUCCESS(status) );
|
|
status = ioStatus.Status;
|
|
}
|
|
|
|
if ( NT_SUCCESS(status) ) {
|
|
*IsCapable = TRUE;
|
|
} else {
|
|
ClusDiskPrint((3,
|
|
"[ClusDisk] IsDiskClusterCapable: NOT_QUORUM_CAPABLE IOCTL FAILED. Error %08X.\n",
|
|
status));
|
|
}
|
|
|
|
ZwClose( eventHandle );
|
|
ZwClose( ntFileHandle );
|
|
|
|
return(status);
|
|
|
|
} // IsDiskClusterCapable
|
|
|
|
|
|
NTSTATUS
|
|
ClusDiskCreateHandle(
|
|
OUT PHANDLE pHandle,
|
|
IN ULONG DiskNumber,
|
|
IN ULONG PartitionNumber,
|
|
IN ACCESS_MASK DesiredAccess
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Open a file handle to self
|
|
|
|
Arguments:
|
|
|
|
DeviceObject -
|
|
DesiredAccess - access mask to be passed to create file
|
|
|
|
Return Value:
|
|
|
|
Status is returned.
|
|
|
|
--*/
|
|
{
|
|
WCHAR deviceNameBuffer[ MAX_PARTITION_NAME_LENGTH ];
|
|
UNICODE_STRING deviceName;
|
|
IO_STATUS_BLOCK ioStatus;
|
|
OBJECT_ATTRIBUTES objectAttributes;
|
|
NTSTATUS status;
|
|
|
|
*pHandle = 0;
|
|
|
|
if ( FAILED( StringCchPrintfW( deviceNameBuffer,
|
|
RTL_NUMBER_OF(deviceNameBuffer),
|
|
DEVICE_PARTITION_NAME,
|
|
DiskNumber,
|
|
PartitionNumber ) ) ) {
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto FnExit;
|
|
}
|
|
|
|
RtlInitUnicodeString( &deviceName, deviceNameBuffer );
|
|
|
|
CDLOG( "CreateHandle(%wZ): Entry", &deviceName );
|
|
|
|
InitializeObjectAttributes( &objectAttributes,
|
|
&deviceName,
|
|
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
|
|
NULL,
|
|
NULL );
|
|
|
|
status = ZwCreateFile( pHandle,
|
|
DesiredAccess,
|
|
&objectAttributes,
|
|
&ioStatus,
|
|
NULL,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
FILE_OPEN,
|
|
FILE_SYNCHRONOUS_IO_NONALERT,
|
|
NULL,
|
|
0 );
|
|
|
|
FnExit:
|
|
|
|
CDLOG( "CreateHandle: Exit status %!status! handle %p", status, *pHandle );
|
|
return status;
|
|
}
|
|
|
|
|
|
VOID
|
|
ClusDiskpReplaceHandleArray(
|
|
PDEVICE_OBJECT DeviceObject,
|
|
PWORK_CONTEXT WorkContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Replaces handle array stored in the device extension with a new one.
|
|
Use NULL as a NewValue to clean up the field.
|
|
|
|
It will close all the handles stored in the array and free up the memory block.
|
|
|
|
If DO_DISMOUNT is specified, it will call FSCTL_DISMOUNT for every handle
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Partition0 Device Object
|
|
|
|
WorkContext - General context info and routine specific context info.
|
|
|
|
Return Value:
|
|
|
|
Status is returned.
|
|
|
|
--*/
|
|
{
|
|
PREPLACE_CONTEXT Context = WorkContext->Context;
|
|
PCLUS_DEVICE_EXTENSION deviceExtension;
|
|
PHANDLE OldArray;
|
|
LONG oldCount;
|
|
LONG loopCount = 0;
|
|
BOOLEAN runAsync = ( Context->Flags & CLEANUP_STORAGE ? TRUE : FALSE );
|
|
|
|
do {
|
|
|
|
Context = WorkContext->Context;
|
|
deviceExtension = Context->DeviceExtension;
|
|
OldArray = NULL;
|
|
|
|
CDLOG( "ClusDiskpReplaceHandleArray: Entry NewValue %p Flags %x loopCount = %d",
|
|
Context->NewValue,
|
|
Context->Flags,
|
|
loopCount );
|
|
|
|
ClusDiskPrint(( 3,
|
|
"[ClusDisk] ClusDiskpReplaceHandleArray: Entry NewValue %p Flags %x loopCount = %d \n",
|
|
Context->NewValue,
|
|
Context->Flags,
|
|
loopCount++ ));
|
|
|
|
OldArray =
|
|
InterlockedExchangePointer(
|
|
(VOID*)&deviceExtension->VolumeHandles, Context->NewValue);
|
|
|
|
//
|
|
// We can release the remove lock now (in some instances). This prevents a deadlock
|
|
// between the DismountDevice routine and the Remove PnP IRP. During dismount, we
|
|
// have saved a copy of the handle array, and we no longer refer to the device object
|
|
// or extension.
|
|
//
|
|
|
|
if ( Context->Flags & RELEASE_REMOVE_LOCK ) {
|
|
ReleaseRemoveLock(&deviceExtension->RemoveLock, deviceExtension);
|
|
}
|
|
|
|
if (OldArray) {
|
|
ULONG i;
|
|
ULONG Count = PtrToUlong( OldArray[0] );
|
|
|
|
//
|
|
// We should already be running in the system process. If not, then there
|
|
// is an error and we need to exit.
|
|
//
|
|
|
|
if ( ClusDiskSystemProcess != (PKPROCESS) IoGetCurrentProcess() ) {
|
|
CDLOG( "ClusDiskpReplaceHandleArray: Not running in system process" );
|
|
ExFreePool( OldArray );
|
|
OldArray = NULL;
|
|
goto FnExit;
|
|
}
|
|
|
|
for(i = 1; i <= Count; ++i) {
|
|
if (OldArray[i]) {
|
|
|
|
#if CLUSTER_FREE_ASSERTS
|
|
// Check if device remove occurs before we release the handles.
|
|
DbgPrint("[ClusDisk] Stall before dismounting handle %x \n", OldArray[i] );
|
|
DBG_STALL_THREAD( 10 );
|
|
#endif
|
|
ClusDiskPrint(( 3,
|
|
"[ClusDisk] ClusDiskpReplaceHandleArray: Handle %x \n",
|
|
OldArray[i] ));
|
|
|
|
if (Context->Flags & DO_DISMOUNT) {
|
|
DismountDevice(OldArray[i]);
|
|
}
|
|
ZwClose(OldArray[i]);
|
|
}
|
|
}
|
|
|
|
CDLOG("ClusDiskpReplaceHandleArray: Freed oldArray %p", OldArray );
|
|
ExFreePool( OldArray );
|
|
OldArray = NULL;
|
|
}
|
|
|
|
FnExit:
|
|
|
|
CDLOG( "ClusDiskpReplaceHandleArray: Exit" );
|
|
|
|
ClusDiskPrint(( 3,
|
|
"[ClusDisk] ClusDiskpReplaceHandleArray: Returns \n" ));
|
|
|
|
//
|
|
// Set cleanup event if requested.
|
|
//
|
|
|
|
if ( Context->Flags & SET_PART0_EVENT ) {
|
|
|
|
KeSetEvent( &deviceExtension->Event, 0, FALSE );
|
|
ClusDiskPrint(( 3,
|
|
"[ClusDisk] ClusDiskpReplaceHandleArray: SetEvent (%p)\n", deviceExtension->Event ));
|
|
|
|
}
|
|
|
|
//
|
|
// If we are running asynchronously, clean up any storage that was allocated.
|
|
// This includes the work item, WorkContext, and Context. Also remove the object
|
|
// reference. The original thread did not wait for this routine to complete.
|
|
//
|
|
|
|
if ( Context->Flags & CLEANUP_STORAGE ) {
|
|
|
|
if ( WorkContext->WorkItem ) {
|
|
IoFreeWorkItem( WorkContext->WorkItem );
|
|
}
|
|
|
|
ExFreePool( Context );
|
|
Context = NULL;
|
|
|
|
ExFreePool( WorkContext );
|
|
WorkContext = NULL;
|
|
|
|
//
|
|
// Now the device object/device extension can go away
|
|
//
|
|
ObDereferenceObject( deviceExtension->DeviceObject );
|
|
|
|
} else {
|
|
|
|
//
|
|
// Release the original thread that was waiting for this work item to complete.
|
|
//
|
|
|
|
KeSetEvent( &WorkContext->CompletionEvent, IO_NO_INCREMENT, FALSE );
|
|
}
|
|
|
|
//
|
|
// Keep running only if we are in async mode and there are other
|
|
// routines queued on our private queue.
|
|
//
|
|
|
|
} while ( runAsync &&
|
|
( WorkContext = (PWORK_CONTEXT)ExInterlockedRemoveHeadList( &ReplaceRoutineListHead,
|
|
&ReplaceRoutineSpinLock ) ) );
|
|
|
|
//
|
|
// This worker routine is about to end, so we need to decrement the
|
|
// count if running asynchronously.
|
|
//
|
|
|
|
if ( runAsync ) {
|
|
oldCount = InterlockedDecrement( &ReplaceRoutineCount );
|
|
if ( oldCount < 0 ) {
|
|
|
|
CDLOG( "ClusDiskpReplaceHandleArray: Invalid ReplaceRoutineCount = %d",
|
|
oldCount );
|
|
|
|
ClusDiskPrint(( 3,
|
|
"[ClusDisk] ClusDiskpReplaceHandleArray: Invalid ReplaceRoutineCount = %d \n",
|
|
oldCount ));
|
|
|
|
}
|
|
}
|
|
|
|
} // ClusDiskpReplaceHandleArray
|
|
|
|
|
|
NTSTATUS
|
|
ProcessDelayedWorkSynchronous(
|
|
PDEVICE_OBJECT DeviceObject,
|
|
PVOID WorkerRoutine,
|
|
PVOID Context
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Call the WorkerRoutine directly if we are running in the system process. If
|
|
we are not running in the system process, check the IRQL. If the IRQL is
|
|
PASSIVE_LEVEL, queue the WorkerRoutine as a work item and wait for it to complete.
|
|
When the work item completes, it will set an event.
|
|
|
|
If the IRQL is not PASSIVE_LEVEL, then this routine will return an error.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject
|
|
|
|
WorkerRoutine - Routine to run.
|
|
|
|
Context - Context information for the WorkerRoutine.
|
|
|
|
Return Value:
|
|
|
|
Status is returned.
|
|
|
|
--*/
|
|
{
|
|
PIO_WORKITEM workItem = NULL;
|
|
PWORK_CONTEXT workContext = NULL;
|
|
|
|
NTSTATUS status = STATUS_UNSUCCESSFUL;
|
|
|
|
__try {
|
|
|
|
ClusDiskPrint(( 3,
|
|
"[ClusDisk] DelayedWorkSync: Entry \n" ));
|
|
|
|
//
|
|
// Prepare a context structure.
|
|
//
|
|
|
|
workContext = ExAllocatePool( NonPagedPool, sizeof(WORK_CONTEXT) );
|
|
|
|
if ( !workContext ) {
|
|
ClusDiskPrint(( 1,
|
|
"[ClusDisk] DelayedWorkSync: Failed to allocate WorkContext \n" ));
|
|
__leave;
|
|
}
|
|
|
|
KeInitializeEvent( &workContext->CompletionEvent, SynchronizationEvent, FALSE );
|
|
|
|
workContext->DeviceObject = DeviceObject;
|
|
workContext->FinalStatus = STATUS_SUCCESS;
|
|
workContext->Context = Context;
|
|
|
|
//
|
|
// If we are in the system process, we can call the worker routine directly.
|
|
//
|
|
|
|
if ( (PKPROCESS)IoGetCurrentProcess() == ClusDiskSystemProcess ) {
|
|
|
|
ClusDiskPrint(( 3,
|
|
"[ClusDisk] DelayedWorkSync: Calling WorkerRoutine directly \n" ));
|
|
|
|
((PIO_WORKITEM_ROUTINE)WorkerRoutine)( DeviceObject, workContext );
|
|
__leave;
|
|
}
|
|
|
|
//
|
|
// If we are not running at passive level, we cannot continue.
|
|
//
|
|
|
|
if ( PASSIVE_LEVEL != KeGetCurrentIrql() ) {
|
|
ClusDiskPrint(( 1,
|
|
"[ClusDisk] DelayedWorkSync: IRQL not PASSIVE_LEVEL \n" ));
|
|
__leave;
|
|
}
|
|
|
|
workItem = IoAllocateWorkItem( DeviceObject );
|
|
|
|
if ( NULL == workItem ) {
|
|
ClusDiskPrint(( 1,
|
|
"[ClusDisk] DelayedWorkSync: Failed to allocate WorkItem \n" ));
|
|
__leave;
|
|
}
|
|
|
|
//
|
|
// Queue the workitem. IoQueueWorkItem will insure that the device object is
|
|
// referenced while the work-item progresses.
|
|
//
|
|
|
|
CDLOG( "ProcessDelayedWorkSynchronous: Queuing work item " );
|
|
|
|
ClusDiskPrint(( 3,
|
|
"[ClusDisk] DelayedWorkSync: Queuing work item \n" ));
|
|
|
|
IoQueueWorkItem( workItem,
|
|
WorkerRoutine,
|
|
DelayedWorkQueue,
|
|
workContext );
|
|
|
|
KeWaitForSingleObject( &workContext->CompletionEvent,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,
|
|
NULL );
|
|
|
|
CDLOG( "ProcessDelayedWorkSynchronous: Work item completed" );
|
|
|
|
ClusDiskPrint(( 3,
|
|
"[ClusDisk] DelayedWorkSync: Work item completed \n" ));
|
|
|
|
|
|
} __finally {
|
|
|
|
if ( workItem) {
|
|
IoFreeWorkItem( workItem );
|
|
}
|
|
|
|
if ( workContext ) {
|
|
status = workContext->FinalStatus;
|
|
|
|
ExFreePool( workContext );
|
|
}
|
|
|
|
}
|
|
|
|
ClusDiskPrint(( 3,
|
|
"[ClusDisk] DelayedWorkSync: Returning status %08X \n", status ));
|
|
|
|
return status;
|
|
|
|
} // ProcessDelayedWorkSynchronous
|
|
|
|
|
|
NTSTATUS
|
|
ProcessDelayedWorkAsynchronous(
|
|
PDEVICE_OBJECT DeviceObject,
|
|
PVOID WorkerRoutine,
|
|
PVOID Context
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Queue a work item to do some work. Check the IRQL. If the IRQL is PASSIVE_LEVEL,
|
|
queue the WorkerRoutine as a work item - don't wait for it to complete.
|
|
|
|
If the IRQL is not PASSIVE_LEVEL, then this routine will return an error.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject
|
|
|
|
WorkerRoutine - Routine to run.
|
|
|
|
Context - Context information for the WorkerRoutine.
|
|
NOTE: this context must be allocated from nonpaged pool.
|
|
|
|
Return Value:
|
|
|
|
Status is returned.
|
|
|
|
--*/
|
|
{
|
|
PIO_WORKITEM workItem = NULL;
|
|
PWORK_CONTEXT workContext = NULL;
|
|
|
|
NTSTATUS status = STATUS_UNSUCCESSFUL;
|
|
|
|
BOOLEAN cleanupRequired = TRUE;
|
|
BOOLEAN useIoQueue;
|
|
|
|
ClusDiskPrint(( 3,
|
|
"[ClusDisk] DelayedWorkAsync: Entry \n" ));
|
|
|
|
//
|
|
// Prepare a context structure.
|
|
//
|
|
|
|
workContext = ExAllocatePool( NonPagedPool, sizeof(WORK_CONTEXT) );
|
|
|
|
if ( !workContext ) {
|
|
ClusDiskPrint(( 1,
|
|
"[ClusDisk] DelayedWorkAsync: Failed to allocate WorkContext \n" ));
|
|
goto FnExit;
|
|
}
|
|
|
|
RtlZeroMemory( workContext, sizeof(WORK_CONTEXT) );
|
|
workContext->DeviceObject = DeviceObject;
|
|
workContext->FinalStatus = STATUS_SUCCESS;
|
|
workContext->Context = Context;
|
|
|
|
//
|
|
// If we are not running at passive level, we cannot continue.
|
|
//
|
|
|
|
if ( PASSIVE_LEVEL != KeGetCurrentIrql() ) {
|
|
ClusDiskPrint(( 1,
|
|
"[ClusDisk] DelayedWorkAsync: IRQL not PASSIVE_LEVEL \n" ));
|
|
goto FnExit;
|
|
}
|
|
|
|
workItem = IoAllocateWorkItem( DeviceObject );
|
|
|
|
if ( NULL == workItem ) {
|
|
ClusDiskPrint(( 1,
|
|
"[ClusDisk] DelayedWorkAsync: Failed to allocate WorkItem \n" ));
|
|
goto FnExit;
|
|
}
|
|
|
|
cleanupRequired = FALSE;
|
|
|
|
workContext->WorkItem = workItem;
|
|
|
|
//
|
|
// Bug 466526. On systems with large number of disks, when system is
|
|
// dismounting all disks at same time, the work queues can become blocked
|
|
// trying to process ClusDiskpReplaceHandleArray requests. When these
|
|
// requests try to dismount disks, the dismount causes a call to
|
|
// IoReportTargetDeviceChange, which then blocks. The problem is that
|
|
// the pnp routine to handle device change also needs to run as a work
|
|
// item, but it cannot as all worker threads are blocked.
|
|
//
|
|
// Only queue some number of ClusDiskpReplaceHandleArray requests.
|
|
// Save the other requests in private queue and the routine(s) running
|
|
// will process the private queue.
|
|
//
|
|
|
|
useIoQueue = TRUE;
|
|
|
|
if ( ClusDiskpReplaceHandleArray == WorkerRoutine ) {
|
|
|
|
//
|
|
// Check how many replace routines are currently running.
|
|
//
|
|
|
|
if ( ReplaceRoutineCount >= MAX_REPLACE_HANDLE_ROUTINES ) {
|
|
|
|
//
|
|
// Too many worker threads may be running and blocked,
|
|
// queue this request in the private queue.
|
|
//
|
|
// The device object was referenced before this routine
|
|
// was called, so we just need to save the info on the
|
|
// private queue.
|
|
//
|
|
|
|
CDLOG( "ProcessDelayedWorkAsynchronous: Queuing work item on private list " );
|
|
|
|
ClusDiskPrint(( 3,
|
|
"[ClusDisk] DelayedWorkAsync: Queuing work item on private list \n" ));
|
|
|
|
ExInterlockedInsertTailList( &ReplaceRoutineListHead,
|
|
&workContext->ListEntry,
|
|
&ReplaceRoutineSpinLock );
|
|
|
|
useIoQueue = FALSE;
|
|
|
|
} else {
|
|
|
|
//
|
|
// We don't have too many replace routines running - this
|
|
// replace routine can be queued as a work item.
|
|
// Bump the count of replace routines running.
|
|
//
|
|
|
|
InterlockedIncrement( &ReplaceRoutineCount );
|
|
useIoQueue = TRUE;
|
|
}
|
|
}
|
|
|
|
if ( useIoQueue ) {
|
|
|
|
//
|
|
// Queue the workitem. IoQueueWorkItem will insure that the device object is
|
|
// referenced while the work-item progresses.
|
|
//
|
|
|
|
CDLOG( "ProcessDelayedWorkAsynchronous: Queuing work item " );
|
|
|
|
ClusDiskPrint(( 3,
|
|
"[ClusDisk] DelayedWorkSync: Queuing work item \n" ));
|
|
|
|
IoQueueWorkItem( workItem,
|
|
WorkerRoutine,
|
|
DelayedWorkQueue,
|
|
workContext );
|
|
|
|
}
|
|
|
|
status = STATUS_SUCCESS;
|
|
|
|
FnExit:
|
|
|
|
if ( cleanupRequired ) {
|
|
if ( workItem) {
|
|
IoFreeWorkItem( workItem );
|
|
}
|
|
if ( workContext ) {
|
|
ExFreePool( workContext );
|
|
}
|
|
|
|
if ( Context ) {
|
|
ExFreePool( Context );
|
|
}
|
|
}
|
|
|
|
ClusDiskPrint(( 3,
|
|
"[ClusDisk] DelayedWorkAsync: Returning status %08X \n", status ));
|
|
|
|
return status;
|
|
|
|
} // ProcessDelayedWorkAsynchronous
|
|
|
|
|
|
VOID
|
|
ClusDiskpOpenFileHandles(
|
|
PDEVICE_OBJECT Part0DeviceObject,
|
|
PWORK_CONTEXT WorkContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Creates file handles for all partitions on the disk.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Partition0 Device Object
|
|
|
|
WorkContext - General context info and routine specific context info.
|
|
|
|
Return Value:
|
|
|
|
Status is returned.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
NTSTATUS returnStatus = STATUS_SUCCESS;
|
|
PCLUS_DEVICE_EXTENSION deviceExtension =
|
|
Part0DeviceObject->DeviceExtension;
|
|
PDRIVE_LAYOUT_INFORMATION_EX pDriveLayout = NULL;
|
|
ULONG partitionCount;
|
|
ULONG i;
|
|
HANDLE* HandleArray = NULL;
|
|
ULONG ArraySize;
|
|
REPLACE_CONTEXT context;
|
|
BOOLEAN validHandles;
|
|
|
|
CDLOG( "OpenFileHandles(%p): Entry", Part0DeviceObject );
|
|
|
|
ASSERT( (deviceExtension->PhysicalDevice == Part0DeviceObject)
|
|
&& (Part0DeviceObject != RootDeviceObject) );
|
|
|
|
//
|
|
// Get the cached drive layout info if possible. If not,
|
|
// go out to the device and get the drive layout.
|
|
//
|
|
|
|
GetDriveLayout( Part0DeviceObject,
|
|
&pDriveLayout,
|
|
FALSE, // Don't update cached drive layout
|
|
FALSE ); // Don't flush storage drivers cached drive layout
|
|
|
|
if (NULL == pDriveLayout) {
|
|
returnStatus = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto exit_gracefully;
|
|
}
|
|
partitionCount = pDriveLayout->PartitionCount;
|
|
|
|
ExFreePool( pDriveLayout );
|
|
pDriveLayout = NULL;
|
|
|
|
ArraySize = (partitionCount + 1) * sizeof(HANDLE);
|
|
|
|
//
|
|
// If we are not running in the system process, we can't continue.
|
|
//
|
|
|
|
if ( ClusDiskSystemProcess != (PKPROCESS) IoGetCurrentProcess() ) {
|
|
CDLOG("OpenFileHandles: Not running in system process" );
|
|
returnStatus = STATUS_UNSUCCESSFUL;
|
|
goto exit_gracefully;
|
|
}
|
|
|
|
HandleArray =
|
|
ExAllocatePool(PagedPool, ArraySize );
|
|
|
|
if (!HandleArray) {
|
|
CDLOG("OpenFileHandles: AllocFailed ArraySize %d", ArraySize );
|
|
returnStatus = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto exit_gracefully;
|
|
}
|
|
|
|
//
|
|
// Store the size of the array in the first element
|
|
//
|
|
|
|
HandleArray[0] = (HANDLE)( UlongToPtr(partitionCount) );
|
|
|
|
validHandles = FALSE;
|
|
for(i = 1; i <= partitionCount; ++i) {
|
|
HANDLE FileHandle;
|
|
|
|
status = ClusDiskCreateHandle(
|
|
&FileHandle,
|
|
deviceExtension->DiskNumber,
|
|
i,
|
|
FILE_WRITE_ATTRIBUTES); // Use FILE_WRITE_ATTRIBUTES for dismount
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
HandleArray[i] = FileHandle;
|
|
validHandles = TRUE;
|
|
} else {
|
|
HandleArray[i] = 0;
|
|
returnStatus = status;
|
|
}
|
|
}
|
|
|
|
if ( !validHandles ) {
|
|
ExFreePool( HandleArray );
|
|
HandleArray = NULL;
|
|
}
|
|
|
|
context.DeviceExtension = deviceExtension;
|
|
context.NewValue = HandleArray;
|
|
context.Flags = 0; // don't dismount
|
|
|
|
ProcessDelayedWorkSynchronous( Part0DeviceObject, ClusDiskpReplaceHandleArray, &context );
|
|
|
|
exit_gracefully:
|
|
CDLOG( "OpenFileHandles: Exit => %!status!", returnStatus );
|
|
|
|
ClusDiskPrint(( 3,
|
|
"[ClusDisk] ClusDiskpOpenFileHandles: Returning status %08X \n", returnStatus ));
|
|
|
|
WorkContext->FinalStatus = returnStatus;
|
|
|
|
KeSetEvent( &WorkContext->CompletionEvent, IO_NO_INCREMENT, FALSE );
|
|
|
|
} // ClusDiskpOpenFileHandles
|
|
|
|
|
|
NTSTATUS
|
|
ClusDiskDeviceControl(
|
|
PDEVICE_OBJECT DeviceObject,
|
|
PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This device control dispatcher handles only the cluster disk
|
|
device control. All others are passed down to the disk drivers.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Context for the activity.
|
|
Irp - The device control argument block.
|
|
|
|
Return Value:
|
|
|
|
Status is returned.
|
|
|
|
--*/
|
|
|
|
{
|
|
PCLUS_DEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
|
|
PCLUS_DEVICE_EXTENSION physicalDisk =
|
|
deviceExtension->PhysicalDevice->DeviceExtension;
|
|
PIO_STACK_LOCATION currentIrpStack = IoGetCurrentIrpStackLocation(Irp);
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
|
|
//
|
|
// Note that we are going to acquire two RemoveLocks here: one for the original DO and one
|
|
// for the physical device (pointed to this DO's device extension). Whenever an IRP is
|
|
// queued, we release one RemoveLock in this routine - release the RemoveLock in the device
|
|
// extension NOT containing the queued IRP. The routine that processes the queued IRP will
|
|
// release the RemoveLock as it has the proper device extension.
|
|
//
|
|
// This should work correctly even if the DO and the physical device point to the same DO, since
|
|
// the RemoveLock is really just a counter, we will increment the counter for the DO twice and
|
|
// decrement it once here, and once when the IRP is completed.
|
|
|
|
status = AcquireRemoveLock(&deviceExtension->RemoveLock, Irp);
|
|
if ( !NT_SUCCESS(status) ) {
|
|
Irp->IoStatus.Status = status;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return status;
|
|
}
|
|
|
|
status = AcquireRemoveLock(&physicalDisk->RemoveLock, Irp);
|
|
if ( !NT_SUCCESS(status) ) {
|
|
ReleaseRemoveLock(&deviceExtension->RemoveLock, Irp);
|
|
Irp->IoStatus.Status = status;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return status;
|
|
}
|
|
|
|
//
|
|
// Make sure the device attach completed.
|
|
//
|
|
status = WaitForAttachCompletion( deviceExtension,
|
|
TRUE, // Wait
|
|
TRUE ); // Also check physical device
|
|
if ( !NT_SUCCESS( status ) ) {
|
|
ReleaseRemoveLock(&physicalDisk->RemoveLock, Irp);
|
|
ReleaseRemoveLock(&deviceExtension->RemoveLock, Irp);
|
|
Irp->IoStatus.Status = status;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return status;
|
|
}
|
|
|
|
//
|
|
// Find out if this is directed at the root device. If so, we only
|
|
// support ATTACH and DETACH.
|
|
//
|
|
if ( deviceExtension->BusType == RootBus ) {
|
|
return(ClusDiskRootDeviceControl( DeviceObject, Irp ));
|
|
}
|
|
|
|
switch (currentIrpStack->Parameters.DeviceIoControl.IoControlCode) {
|
|
|
|
//
|
|
// We can only get (not set) the current state from this code path.
|
|
// Use clusdisk0 and disk signature to set the disk state.
|
|
// To get the current disk state, send the "set state" IOCTL
|
|
// to the disk/volume object directly with output parameters only
|
|
// (no input paramters) or use the "get state" IOCTL.
|
|
//
|
|
|
|
case IOCTL_DISK_CLUSTER_SET_STATE:
|
|
case IOCTL_DISK_CLUSTER_GET_STATE:
|
|
{
|
|
PUCHAR ioDiskState = Irp->AssociatedIrp.SystemBuffer;
|
|
ULONG returnLength = 0;
|
|
|
|
ClusDiskPrint((3, "[ClusDisk] DeviceControl: PD DiskState %s, devObj DiskState %s \n",
|
|
DiskStateToString( physicalDisk->DiskState ),
|
|
DiskStateToString( deviceExtension->DiskState ) ));
|
|
|
|
//
|
|
// Check if input buffer supplied. If present, then this is a request to
|
|
// set the new disk state. Fail the request.
|
|
//
|
|
|
|
if ( ARGUMENT_PRESENT( ioDiskState) &&
|
|
currentIrpStack->Parameters.DeviceIoControl.InputBufferLength >= sizeof(UCHAR) ) {
|
|
|
|
ClusDiskPrint((3, "[ClusDisk] DeviceControl: Can't set state on disk %u (%p) \n",
|
|
physicalDisk->DiskNumber,
|
|
DeviceObject ));
|
|
|
|
status = STATUS_INVALID_DEVICE_REQUEST;
|
|
|
|
} else if ( ARGUMENT_PRESENT( ioDiskState ) &&
|
|
currentIrpStack->Parameters.DeviceIoControl.OutputBufferLength >= sizeof(UCHAR) ) {
|
|
|
|
ioDiskState[0] = (UCHAR)physicalDisk->DiskState;
|
|
returnLength = sizeof(UCHAR);
|
|
|
|
} else {
|
|
|
|
status = STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
Irp->IoStatus.Status = status;
|
|
Irp->IoStatus.Information = returnLength;
|
|
|
|
ReleaseRemoveLock(&physicalDisk->RemoveLock, Irp);
|
|
ReleaseRemoveLock(&deviceExtension->RemoveLock, Irp);
|
|
IoCompleteRequest(Irp,IO_NO_INCREMENT);
|
|
return(status);
|
|
}
|
|
|
|
// Check if low-level miniport driver support cluster devices.
|
|
|
|
case IOCTL_DISK_CLUSTER_NOT_CLUSTER_CAPABLE:
|
|
{
|
|
BOOLEAN isCapable;
|
|
|
|
//
|
|
// Make sure there is no input/output data buffer.
|
|
//
|
|
if ( Irp->AssociatedIrp.SystemBuffer != NULL ) {
|
|
ReleaseRemoveLock(&physicalDisk->RemoveLock, Irp);
|
|
ReleaseRemoveLock(&deviceExtension->RemoveLock, Irp);
|
|
Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return(STATUS_INVALID_PARAMETER);
|
|
}
|
|
|
|
//
|
|
// Verifier found a problem with the original code. The status returned
|
|
// wasn't the same as Irp->IoStatus.Status. The IRP status was always
|
|
// success, but the dispatch routine sometimes returned an error. This
|
|
// seems to work but verifier says this is invalid. When I changed the code
|
|
// to update the IRP status with the same returned status, new drives
|
|
// couldn't be seen. This is because the user mode component uses this
|
|
// IOCTL to indicate the disk is NOT cluster capable, so the user mode
|
|
// code looked for a failure to incidate a cluster disk.
|
|
//
|
|
|
|
//
|
|
// IsDiskClusterCapable always returns TRUE ???
|
|
//
|
|
|
|
status = IsDiskClusterCapable ( deviceExtension->ScsiAddress.PortNumber,
|
|
&isCapable);
|
|
|
|
//
|
|
// Fix for IBM. The Win2000 2195 code returned from this IOCTL the status
|
|
// of the SCSI miniport IOCTL. This was returned to DeviceIoControl rather
|
|
// than the status in the IRP. Changed to make the behavior the same as
|
|
// Win20000 2195.
|
|
//
|
|
|
|
//
|
|
// If the SCSI miniport IOCTL succeeds, we return success -- meaning we should
|
|
// *not* use this disk. If any of the routines failed while trying to issue
|
|
// the SCSI miniport IOCTL (including issuing the SCSI miniport IOCTL itself),
|
|
// then we return failure -- meaning we *should* use this disk.
|
|
//
|
|
|
|
Irp->IoStatus.Status = status;
|
|
ReleaseRemoveLock(&physicalDisk->RemoveLock, Irp);
|
|
ReleaseRemoveLock(&deviceExtension->RemoveLock, Irp);
|
|
IoCompleteRequest( Irp, IO_NO_INCREMENT );
|
|
|
|
return(status);
|
|
}
|
|
|
|
case IOCTL_DISK_CLUSTER_WAIT_FOR_CLEANUP:
|
|
{
|
|
LARGE_INTEGER waitTime;
|
|
ULONG waitTimeInSeconds;
|
|
|
|
if ( currentIrpStack->Parameters.DeviceIoControl.InputBufferLength < sizeof(ULONG) ||
|
|
Irp->AssociatedIrp.SystemBuffer == NULL) {
|
|
|
|
ReleaseRemoveLock(&physicalDisk->RemoveLock, Irp);
|
|
ReleaseRemoveLock(&deviceExtension->RemoveLock, Irp);
|
|
Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return(STATUS_INVALID_PARAMETER);
|
|
}
|
|
|
|
//
|
|
// Get wait time from caller, and limit it to max.
|
|
//
|
|
|
|
waitTimeInSeconds = *(PULONG)Irp->AssociatedIrp.SystemBuffer;
|
|
if ( waitTimeInSeconds > MAX_WAIT_SECONDS_ALLOWED ) {
|
|
waitTimeInSeconds = MAX_WAIT_SECONDS_ALLOWED;
|
|
}
|
|
|
|
CDLOG( "IoctlWaitForCleanup(%p): Entry waitTime %d second(s)",
|
|
DeviceObject,
|
|
waitTimeInSeconds );
|
|
|
|
waitTime.QuadPart = (LONGLONG)waitTimeInSeconds * -(10000*1000);
|
|
status = KeWaitForSingleObject(
|
|
&physicalDisk->Event,
|
|
Suspended,
|
|
KernelMode,
|
|
FALSE,
|
|
&waitTime);
|
|
//
|
|
// Reset the event in case of timeout.
|
|
// [HACKHACK] should we do this?
|
|
// No. If Offline worker is stuck somewhere, we better
|
|
// go and debug the problem, rather then return success
|
|
// and wait for the offline worker to do nasty things
|
|
// behind our backs
|
|
//
|
|
// KeSetEvent( &physicalExtension->Event, 0, FALSE );
|
|
|
|
if (status == STATUS_TIMEOUT) {
|
|
//
|
|
// NT_SUCCESS considers STATUS_TIMEOUT as a success code
|
|
// we need something stronger
|
|
//
|
|
status = STATUS_IO_TIMEOUT;
|
|
}
|
|
|
|
ReleaseRemoveLock(&physicalDisk->RemoveLock, Irp);
|
|
ReleaseRemoveLock(&deviceExtension->RemoveLock, Irp);
|
|
Irp->IoStatus.Status = status;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
|
|
CDLOG( "IoctlWaitForCleanup(%p): Exit => %!status!",
|
|
DeviceObject,
|
|
status );
|
|
|
|
return(status);
|
|
}
|
|
|
|
case IOCTL_DISK_CLUSTER_TEST:
|
|
{
|
|
CDLOG( "IoctlDiskClusterTest(%p)", DeviceObject );
|
|
|
|
ReleaseRemoveLock(&physicalDisk->RemoveLock, Irp);
|
|
ReleaseRemoveLock(&deviceExtension->RemoveLock, Irp);
|
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return(STATUS_SUCCESS);
|
|
}
|
|
|
|
case IOCTL_VOLUME_IS_CLUSTERED:
|
|
{
|
|
//
|
|
// Look at the reserve timer. If reserved, this is a clustered disk.
|
|
//
|
|
|
|
if ( physicalDisk->ReserveTimer != 0 ) {
|
|
status = STATUS_SUCCESS;
|
|
} else {
|
|
status = STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
ReleaseRemoveLock(&physicalDisk->RemoveLock, Irp);
|
|
ReleaseRemoveLock(&deviceExtension->RemoveLock, Irp);
|
|
Irp->IoStatus.Status = status;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return(status);
|
|
}
|
|
|
|
// **************************
|
|
// The following IOCTL's should not be blocked by stalled disks
|
|
// **************************
|
|
|
|
case IOCTL_SCSI_GET_ADDRESS:
|
|
case IOCTL_STORAGE_GET_HOTPLUG_INFO:
|
|
case IOCTL_STORAGE_RESET_BUS:
|
|
case IOCTL_STORAGE_BREAK_RESERVATION:
|
|
case IOCTL_STORAGE_QUERY_PROPERTY:
|
|
case IOCTL_STORAGE_GET_MEDIA_TYPES:
|
|
case IOCTL_STORAGE_GET_MEDIA_TYPES_EX:
|
|
case IOCTL_STORAGE_FIND_NEW_DEVICES:
|
|
case IOCTL_STORAGE_GET_DEVICE_NUMBER:
|
|
case IOCTL_STORAGE_MEDIA_REMOVAL:
|
|
case IOCTL_STORAGE_CHECK_VERIFY:
|
|
case IOCTL_MOUNTDEV_QUERY_UNIQUE_ID:
|
|
case IOCTL_MOUNTDEV_UNIQUE_ID_CHANGE_NOTIFY:
|
|
case IOCTL_MOUNTDEV_QUERY_DEVICE_NAME:
|
|
case IOCTL_MOUNTDEV_QUERY_SUGGESTED_LINK_NAME:
|
|
case IOCTL_MOUNTDEV_LINK_CREATED:
|
|
case IOCTL_MOUNTDEV_LINK_DELETED:
|
|
case IOCTL_DISK_GET_DRIVE_LAYOUT:
|
|
case IOCTL_DISK_GET_DRIVE_LAYOUT_EX:
|
|
case IOCTL_DISK_CHECK_VERIFY:
|
|
case IOCTL_DISK_MEDIA_REMOVAL:
|
|
case IOCTL_DISK_GET_DRIVE_GEOMETRY:
|
|
case IOCTL_DISK_GET_DRIVE_GEOMETRY_EX:
|
|
case IOCTL_DISK_GET_PARTITION_INFO:
|
|
case IOCTL_DISK_GET_PARTITION_INFO_EX:
|
|
case IOCTL_DISK_IS_WRITABLE:
|
|
case IOCTL_DISK_UPDATE_PROPERTIES:
|
|
case IOCTL_VOLUME_ONLINE:
|
|
case IOCTL_VOLUME_OFFLINE:
|
|
case IOCTL_VOLUME_IS_OFFLINE:
|
|
case IOCTL_DISK_GET_LENGTH_INFO:
|
|
case IOCTL_MOUNTDEV_QUERY_STABLE_GUID:
|
|
case IOCTL_PARTMGR_QUERY_DISK_SIGNATURE:
|
|
{
|
|
IoSkipCurrentIrpStackLocation( Irp );
|
|
|
|
ReleaseRemoveLock(&physicalDisk->RemoveLock, Irp);
|
|
ReleaseRemoveLock(&deviceExtension->RemoveLock, Irp);
|
|
|
|
return IoCallDriver(deviceExtension->TargetDeviceObject, Irp);
|
|
}
|
|
|
|
default:
|
|
{
|
|
|
|
//
|
|
// For all other requests, we must be online to process the request.
|
|
//
|
|
//
|
|
// Before returning failure, first verify device attachment. This
|
|
// means that if the device is attached check if it should be detached.
|
|
// If the device does get detached, then allow IO to go through.
|
|
//
|
|
if ( !deviceExtension->AttachValid ) {
|
|
|
|
PDEVICE_OBJECT targetDeviceObject;
|
|
|
|
targetDeviceObject = deviceExtension->TargetDeviceObject;
|
|
ClusDiskPrint((
|
|
1,
|
|
"[ClusDisk] Attach is not valid. IOCTL = %lx, check if we need to detach.\n",
|
|
currentIrpStack->Parameters.DeviceIoControl.IoControlCode));
|
|
if ( !ClusDiskVerifyAttach( DeviceObject ) ) {
|
|
ClusDiskPrint((
|
|
1,
|
|
"[ClusDisk] We detached.\n"));
|
|
IoSkipCurrentIrpStackLocation( Irp );
|
|
|
|
ReleaseRemoveLock(&physicalDisk->RemoveLock, Irp);
|
|
ReleaseRemoveLock(&deviceExtension->RemoveLock, Irp);
|
|
|
|
return IoCallDriver(targetDeviceObject,
|
|
Irp);
|
|
}
|
|
if ( deviceExtension->AttachValid ) {
|
|
ClusDiskPrint((
|
|
1,
|
|
"[ClusDisk] Attach is now valid, Signature = %08lX .\n",
|
|
deviceExtension->Signature));
|
|
}
|
|
}
|
|
|
|
if ( physicalDisk->DiskState != DiskOnline ) {
|
|
|
|
#if 0
|
|
ULONG access;
|
|
|
|
// This seems like a good idea, but needs more testing. We might miss
|
|
// setting up irp completion routine.
|
|
|
|
//
|
|
// Try cracking the control code and letting any IOCTLs through that
|
|
// do not have write access set.
|
|
//
|
|
|
|
access = ACCESS_FROM_CTL_CODE(currentIrpStack->Parameters.DeviceIoControl.IoControlCode);
|
|
|
|
if ( !(access & FILE_WRITE_ACCESS) ) {
|
|
|
|
ClusDiskPrint((
|
|
3,
|
|
"[ClusDisk] Sending IOCTL = %08lX based on access %02X, Signature = %08lX \n",
|
|
currentIrpStack->Parameters.DeviceIoControl.IoControlCode,
|
|
access,
|
|
deviceExtension->Signature));
|
|
|
|
IoSkipCurrentIrpStackLocation( Irp );
|
|
|
|
ReleaseRemoveLock(&physicalDisk->RemoveLock, Irp);
|
|
ReleaseRemoveLock(&deviceExtension->RemoveLock, Irp);
|
|
|
|
return IoCallDriver(deviceExtension->TargetDeviceObject, Irp);
|
|
|
|
}
|
|
#endif
|
|
|
|
// All other IOCTL's fall through and are failed because disk is offline.
|
|
|
|
ClusDiskPrint((
|
|
1,
|
|
"[ClusDisk] Disk not online: Rejected IOCTL = %08lX, Signature = %08lX \n",
|
|
currentIrpStack->Parameters.DeviceIoControl.IoControlCode,
|
|
deviceExtension->Signature));
|
|
|
|
CDLOG( "[ClusDisk] DeviceControl, Failing IOCTL %lx for offline device, signature %lx \n",
|
|
currentIrpStack->Parameters.DeviceIoControl.IoControlCode,
|
|
deviceExtension->Signature );
|
|
|
|
ReleaseRemoveLock(&physicalDisk->RemoveLock, Irp);
|
|
ReleaseRemoveLock(&deviceExtension->RemoveLock, Irp);
|
|
Irp->IoStatus.Status = STATUS_DEVICE_OFF_LINE;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return(STATUS_DEVICE_OFF_LINE);
|
|
}
|
|
|
|
switch (currentIrpStack->Parameters.DeviceIoControl.IoControlCode) {
|
|
|
|
case IOCTL_DISK_FIND_NEW_DEVICES:
|
|
|
|
//
|
|
// Copy current stack to next stack.
|
|
//
|
|
|
|
IoCopyCurrentIrpStackLocationToNext( Irp );
|
|
|
|
//
|
|
// Ask to be called back during request completion.
|
|
// Pass current disk count as context.
|
|
//
|
|
//
|
|
|
|
IoSetCompletionRoutine(Irp,
|
|
ClusDiskNewDiskCompletion,
|
|
(PVOID)( UlongToPtr( IoGetConfigurationInformation()->DiskCount ) ),
|
|
TRUE, // Invoke on success
|
|
TRUE, // Invoke on error
|
|
TRUE); // Invoke on cancel
|
|
|
|
//
|
|
// Call target driver.
|
|
//
|
|
|
|
// The completion routine will release the RemoveLocks.
|
|
|
|
return IoCallDriver(deviceExtension->TargetDeviceObject, Irp);
|
|
|
|
case IOCTL_DISK_SET_DRIVE_LAYOUT:
|
|
case IOCTL_DISK_SET_DRIVE_LAYOUT_EX:
|
|
|
|
CDLOG( "IoctlDiskSetDriveLayout(%p)", DeviceObject );
|
|
|
|
//
|
|
// Copy current stack to next stack.
|
|
//
|
|
|
|
IoCopyCurrentIrpStackLocationToNext( Irp );
|
|
|
|
//
|
|
// Ask to be called back during request completion.
|
|
//
|
|
|
|
IoSetCompletionRoutine(Irp,
|
|
ClusDiskSetLayoutCompletion,
|
|
DeviceObject,
|
|
TRUE, // Invoke on success
|
|
TRUE, // Invoke on error
|
|
TRUE); // Invoke on cancel
|
|
|
|
//
|
|
// Call target driver.
|
|
//
|
|
|
|
// The completion routine will release the RemoveLocks.
|
|
|
|
return IoCallDriver(deviceExtension->TargetDeviceObject, Irp);
|
|
|
|
default:
|
|
|
|
//
|
|
// Set current stack back one.
|
|
//
|
|
IoSkipCurrentIrpStackLocation( Irp );
|
|
|
|
ReleaseRemoveLock(&physicalDisk->RemoveLock, Irp);
|
|
ReleaseRemoveLock(&deviceExtension->RemoveLock, Irp);
|
|
|
|
//
|
|
// Pass unrecognized device control requests
|
|
// down to next driver layer.
|
|
//
|
|
|
|
return IoCallDriver(deviceExtension->TargetDeviceObject, Irp);
|
|
|
|
} // switch
|
|
|
|
} // default case
|
|
|
|
} // switch
|
|
|
|
} // ClusDiskDeviceControl
|
|
|
|
|
|
|
|
NTSTATUS
|
|
ClusDiskRootDeviceControl(
|
|
PDEVICE_OBJECT DeviceObject,
|
|
PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This device control dispatcher handles only the cluster disk IOCTLs
|
|
for the root device. This is ATTACH and DETACH.
|
|
|
|
Important: Two RemoveLocks will be held on entry to this function.
|
|
One RemoveLock for the original DO and one for the associated physical
|
|
device.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Context for the activity.
|
|
Irp - The device control argument block.
|
|
|
|
Return Value:
|
|
|
|
Status is returned.
|
|
|
|
--*/
|
|
|
|
{
|
|
PCLUS_DEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
|
|
PCLUS_DEVICE_EXTENSION physicalDisk =
|
|
deviceExtension->PhysicalDevice->DeviceExtension;
|
|
|
|
// Save pointers to the original RemoveLocks as the device extensions
|
|
// may change in this routine.
|
|
PCLUS_DEVICE_EXTENSION lockedDeviceExtension = deviceExtension;
|
|
PCLUS_DEVICE_EXTENSION lockedPhysicalDisk = physicalDisk;
|
|
|
|
PIO_STACK_LOCATION currentIrpStack = IoGetCurrentIrpStackLocation(Irp);
|
|
PDEVICE_OBJECT targetDeviceObject;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
ULONG signature;
|
|
PULONG inputData = Irp->AssociatedIrp.SystemBuffer;
|
|
ULONG inputSize = currentIrpStack->Parameters.DeviceIoControl.InputBufferLength;
|
|
ULONG outputSize = currentIrpStack->Parameters.DeviceIoControl.OutputBufferLength;
|
|
KIRQL irql;
|
|
BOOLEAN newPhysLockAcquired;
|
|
|
|
switch (currentIrpStack->Parameters.DeviceIoControl.IoControlCode) {
|
|
|
|
case IOCTL_DISK_CLUSTER_ARBITRATION_ESCAPE:
|
|
{
|
|
if ( ARGUMENT_PRESENT( inputData ) &&
|
|
inputSize >= sizeof(ARBITRATION_READ_WRITE_PARAMS) )
|
|
{
|
|
BOOLEAN success;
|
|
PDEVICE_OBJECT physicalDevice;
|
|
PARBITRATION_READ_WRITE_PARAMS params =
|
|
(PARBITRATION_READ_WRITE_PARAMS)inputData;
|
|
|
|
// Can't hold the spinlock and then try to acquire the resource lock or the system
|
|
// might deadlock.
|
|
// KeAcquireSpinLock(&ClusDiskSpinLock, &irql);
|
|
success = AttachedDevice( params->Signature, &physicalDevice );
|
|
// KeReleaseSpinLock(&ClusDiskSpinLock, irql);
|
|
|
|
if( success ) {
|
|
|
|
PCLUS_DEVICE_EXTENSION tempDeviceExtension = physicalDevice->DeviceExtension;
|
|
|
|
// We have a new device here, acquire the RemoveLock if possible.
|
|
|
|
status = AcquireRemoveLock(&tempDeviceExtension->RemoveLock, Irp);
|
|
if ( NT_SUCCESS(status) ) {
|
|
|
|
status = ProcessArbitrationEscape(
|
|
physicalDevice->DeviceExtension,
|
|
inputData,
|
|
inputSize,
|
|
&outputSize);
|
|
if ( NT_SUCCESS(status) ) {
|
|
Irp->IoStatus.Information = outputSize;
|
|
}
|
|
|
|
ReleaseRemoveLock(&tempDeviceExtension->RemoveLock, Irp);
|
|
}
|
|
} else {
|
|
status = STATUS_NOT_FOUND;
|
|
}
|
|
} else {
|
|
status = STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
ReleaseRemoveLock(&lockedPhysicalDisk->RemoveLock, Irp);
|
|
ReleaseRemoveLock(&lockedDeviceExtension->RemoveLock, Irp);
|
|
Irp->IoStatus.Status = status;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return(status);
|
|
}
|
|
|
|
//
|
|
// Always sets new state and optionally return the old state.
|
|
// To just get the current disk state, send the "set state" IOCTL
|
|
// to the disk/volume object directly with output parameters only
|
|
// (no input paramters), or use the "get state" IOCTL.
|
|
//
|
|
|
|
case IOCTL_DISK_CLUSTER_SET_STATE:
|
|
{
|
|
ULONG returnLength = 0;
|
|
|
|
//
|
|
// Called routine will validate all parameters.
|
|
//
|
|
|
|
status = SetDiskState( Irp->AssociatedIrp.SystemBuffer, // Input/output buffer
|
|
currentIrpStack->Parameters.DeviceIoControl.InputBufferLength, // Input buffer length
|
|
currentIrpStack->Parameters.DeviceIoControl.OutputBufferLength, // Output buffer length
|
|
&returnLength );
|
|
|
|
Irp->IoStatus.Status = status;
|
|
Irp->IoStatus.Information = returnLength;
|
|
|
|
ReleaseRemoveLock(&physicalDisk->RemoveLock, Irp);
|
|
ReleaseRemoveLock(&deviceExtension->RemoveLock, Irp);
|
|
IoCompleteRequest(Irp,IO_NO_INCREMENT);
|
|
return(status);
|
|
}
|
|
|
|
// Perform an attach to a device object, for a given signature.
|
|
case IOCTL_DISK_CLUSTER_ATTACH:
|
|
{
|
|
if ( ARGUMENT_PRESENT( inputData ) &&
|
|
inputSize >= sizeof(ULONG) ) {
|
|
|
|
signature = inputData[0];
|
|
|
|
ClusDiskPrint((1,
|
|
"[ClusDisk] RootDeviceControl: attaching signature %08X installMode TRUE \n",
|
|
signature));
|
|
|
|
CDLOG( "RootClusterAttach: sig %08x installMode TRUE ", signature );
|
|
|
|
status = ClusDiskTryAttachDevice( signature,
|
|
0,
|
|
DeviceObject->DriverObject,
|
|
TRUE ); // Dismount, then offline
|
|
} else {
|
|
status = STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
ReleaseRemoveLock(&lockedPhysicalDisk->RemoveLock, Irp);
|
|
ReleaseRemoveLock(&lockedDeviceExtension->RemoveLock, Irp);
|
|
Irp->IoStatus.Status = status;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return(status);
|
|
}
|
|
|
|
// Perform an attach to a device object, for a given signature.
|
|
// In this case, offline the disk first, then dismount.
|
|
case IOCTL_DISK_CLUSTER_ATTACH_OFFLINE:
|
|
{
|
|
if ( ARGUMENT_PRESENT( inputData ) &&
|
|
inputSize >= sizeof(ULONG) ) {
|
|
|
|
signature = inputData[0];
|
|
|
|
ClusDiskPrint((1,
|
|
"[ClusDisk] RootDeviceControl: attaching signature %08X installMode FALSE \n",
|
|
signature));
|
|
|
|
CDLOG( "RootClusterAttach: sig %08x installMode FALSE ", signature );
|
|
|
|
status = ClusDiskTryAttachDevice( signature,
|
|
0,
|
|
DeviceObject->DriverObject,
|
|
FALSE ); // Offline, then dismount
|
|
} else {
|
|
status = STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
ReleaseRemoveLock(&lockedPhysicalDisk->RemoveLock, Irp);
|
|
ReleaseRemoveLock(&lockedDeviceExtension->RemoveLock, Irp);
|
|
Irp->IoStatus.Status = status;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return(status);
|
|
}
|
|
|
|
// Perform a detach from a device object, for a given signature
|
|
|
|
case IOCTL_DISK_CLUSTER_DETACH:
|
|
{
|
|
if ( ARGUMENT_PRESENT( inputData ) &&
|
|
inputSize >= sizeof(ULONG) ) {
|
|
|
|
signature = inputData[0];
|
|
|
|
CDLOG( "RootClusterDetach: sig %08x", signature );
|
|
|
|
ClusDiskPrint((3,
|
|
"[ClusDisk] RootDeviceControl: detaching signature %08X\n",
|
|
signature));
|
|
|
|
status = ClusDiskDetachDevice( signature,
|
|
DeviceObject->DriverObject );
|
|
} else {
|
|
status = STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
ReleaseRemoveLock(&lockedPhysicalDisk->RemoveLock, Irp);
|
|
ReleaseRemoveLock(&lockedDeviceExtension->RemoveLock, Irp);
|
|
Irp->IoStatus.Status = status;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return(status);
|
|
}
|
|
|
|
case IOCTL_DISK_CLUSTER_ATTACH_LIST:
|
|
{
|
|
// The called routine will validate the input buffers.
|
|
|
|
//
|
|
// Attaches a signature list to the system. NO resets will occur. If we
|
|
// really need to make sure the device attaches, then use the normal
|
|
// attach IOCTL. This IOCTL is mainly used by cluster setup.
|
|
//
|
|
|
|
status = AttachSignatureList( DeviceObject,
|
|
inputData,
|
|
inputSize
|
|
);
|
|
|
|
ReleaseRemoveLock(&lockedPhysicalDisk->RemoveLock, Irp);
|
|
ReleaseRemoveLock(&lockedDeviceExtension->RemoveLock, Irp);
|
|
Irp->IoStatus.Status = status;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return(status);
|
|
|
|
}
|
|
|
|
// Detach from all the signatures in the list.
|
|
|
|
case IOCTL_DISK_CLUSTER_DETACH_LIST:
|
|
{
|
|
// The called routine will validate the input buffers.
|
|
|
|
status = DetachSignatureList( DeviceObject,
|
|
inputData,
|
|
inputSize
|
|
);
|
|
|
|
ReleaseRemoveLock(&lockedPhysicalDisk->RemoveLock, Irp);
|
|
ReleaseRemoveLock(&lockedDeviceExtension->RemoveLock, Irp);
|
|
Irp->IoStatus.Status = status;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return(status);
|
|
|
|
}
|
|
|
|
// Start the reservation timer
|
|
|
|
case IOCTL_DISK_CLUSTER_START_RESERVE:
|
|
{
|
|
CDLOG( "RootStartReserve(%p)", DeviceObject );
|
|
|
|
if ( RootDeviceObject == NULL ) {
|
|
ReleaseRemoveLock(&lockedPhysicalDisk->RemoveLock, Irp);
|
|
ReleaseRemoveLock(&lockedDeviceExtension->RemoveLock, Irp);
|
|
Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST;
|
|
IoCompleteRequest( Irp, IO_NO_INCREMENT );
|
|
return(STATUS_INVALID_DEVICE_REQUEST);
|
|
}
|
|
|
|
if ( currentIrpStack->FileObject->FsContext ) {
|
|
status = STATUS_DUPLICATE_OBJECTID;
|
|
} else if ( ARGUMENT_PRESENT(inputData) &&
|
|
inputSize >= sizeof(ULONG) ) {
|
|
|
|
status = VerifyArbitrationArgumentsIfAny(
|
|
inputData,
|
|
inputSize );
|
|
if (!NT_SUCCESS(status) ) {
|
|
ReleaseRemoveLock(&lockedPhysicalDisk->RemoveLock, Irp);
|
|
ReleaseRemoveLock(&lockedDeviceExtension->RemoveLock, Irp);
|
|
Irp->IoStatus.Status = status;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return(status);
|
|
}
|
|
|
|
signature = inputData[0];
|
|
|
|
|
|
// Acquire the device list lock first, then the spinlock. This will prevent deadlock.
|
|
ACQUIRE_SHARED( &ClusDiskDeviceListLock );
|
|
KeAcquireSpinLock(&ClusDiskSpinLock, &irql);
|
|
if ( MatchDevice(signature, &targetDeviceObject) &&
|
|
targetDeviceObject ) {
|
|
status = EnableHaltProcessing( &irql );
|
|
if ( NT_SUCCESS(status) ) {
|
|
|
|
physicalDisk = targetDeviceObject->DeviceExtension;
|
|
|
|
status = AcquireRemoveLock(&physicalDisk->RemoveLock, Irp);
|
|
if ( !NT_SUCCESS(status) ) {
|
|
|
|
status = STATUS_NO_SUCH_FILE;
|
|
|
|
} else {
|
|
|
|
ProcessArbitrationArgumentsIfAny(
|
|
physicalDisk,
|
|
inputData,
|
|
inputSize );
|
|
|
|
ClusDiskPrint((3,
|
|
"[ClusDisk] Start reservations on signature %lx.\n",
|
|
physicalDisk->Signature ));
|
|
|
|
currentIrpStack->FileObject->FsContext = targetDeviceObject;
|
|
CDLOG("RootCtl: IncRef(%p)", targetDeviceObject );
|
|
ObReferenceObject( targetDeviceObject );
|
|
physicalDisk->ReserveTimer = RESERVE_TIMER;
|
|
physicalDisk->ReserveFailure = 0;
|
|
physicalDisk->PerformReserves = TRUE;
|
|
status = STATUS_SUCCESS;
|
|
|
|
ReleaseRemoveLock(&physicalDisk->RemoveLock, Irp);
|
|
}
|
|
}
|
|
} else {
|
|
status = STATUS_NO_SUCH_FILE;
|
|
}
|
|
KeReleaseSpinLock(&ClusDiskSpinLock, irql);
|
|
RELEASE_SHARED( &ClusDiskDeviceListLock );
|
|
} else {
|
|
status = STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
ReleaseRemoveLock(&lockedPhysicalDisk->RemoveLock, Irp);
|
|
ReleaseRemoveLock(&lockedDeviceExtension->RemoveLock, Irp);
|
|
Irp->IoStatus.Status = status;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return(status);
|
|
|
|
}
|
|
|
|
// Stop the reservation timer
|
|
|
|
case IOCTL_DISK_CLUSTER_STOP_RESERVE:
|
|
{
|
|
PIRP irp;
|
|
PLIST_ENTRY listEntry;
|
|
|
|
CDLOG( "RootStopReserve(%p)", DeviceObject );
|
|
|
|
if ( (RootDeviceObject == NULL) ||
|
|
(deviceExtension->BusType != RootBus) ) {
|
|
ReleaseRemoveLock(&lockedPhysicalDisk->RemoveLock, Irp);
|
|
ReleaseRemoveLock(&lockedDeviceExtension->RemoveLock, Irp);
|
|
Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST;
|
|
IoCompleteRequest( Irp, IO_NO_INCREMENT );
|
|
return(STATUS_INVALID_DEVICE_REQUEST);
|
|
}
|
|
|
|
newPhysLockAcquired = FALSE;
|
|
|
|
if ( currentIrpStack->FileObject->FsContext ) {
|
|
|
|
//
|
|
// GorN Oct/13/1999. PnP can come and rip out the device object
|
|
// we stored in the FsContext. It doesn't delete it, since we have a reference
|
|
// to it, but we will not be able to use it, since the objects underneath this one
|
|
// are destroyed.
|
|
//
|
|
// Our PnpRemoveDevice handler will zero out targetDevice field of the device
|
|
// extension, this will not eliminate the race completely, but will reduces the
|
|
// chance of this happening, since, usually, device removal comes first,
|
|
// we notify resmon and then it calls stop reserve.
|
|
//
|
|
// Chances that PnpRemoveDevice will come at exact moment resmon called ClusterStopReserve
|
|
// are smaller
|
|
//
|
|
|
|
targetDeviceObject = (PDEVICE_OBJECT)currentIrpStack->FileObject->FsContext;
|
|
physicalDisk = targetDeviceObject->DeviceExtension;
|
|
|
|
CDLOG( "RootStopReserve: FsContext targetDO %p RemoveLock.IoCount %d",
|
|
targetDeviceObject,
|
|
physicalDisk->RemoveLock.Common.IoCount );
|
|
|
|
// We have a new device here, acquire the RemoveLock if possible.
|
|
|
|
status = AcquireRemoveLock(&physicalDisk->RemoveLock, Irp);
|
|
if ( !NT_SUCCESS(status) ) {
|
|
|
|
status = STATUS_INVALID_HANDLE;
|
|
|
|
} else {
|
|
|
|
ClusDiskPrint((3,
|
|
"[ClusDisk] IOCTL, stop reservations on signature %lx, disk state %s \n",
|
|
physicalDisk->Signature,
|
|
DiskStateToString( physicalDisk->DiskState ) ));
|
|
|
|
newPhysLockAcquired = TRUE;
|
|
|
|
IoAcquireCancelSpinLock( &irql );
|
|
KeAcquireSpinLockAtDpcLevel(&ClusDiskSpinLock);
|
|
physicalDisk->ReserveTimer = 0;
|
|
|
|
//
|
|
// Signal all waiting Irp's on the physical device extension.
|
|
//
|
|
while ( !IsListEmpty(&physicalDisk->WaitingIoctls) ) {
|
|
listEntry = RemoveHeadList(&physicalDisk->WaitingIoctls);
|
|
irp = CONTAINING_RECORD( listEntry,
|
|
IRP,
|
|
Tail.Overlay.ListEntry );
|
|
ClusDiskCompletePendingRequest(irp, STATUS_SUCCESS, physicalDisk);
|
|
}
|
|
|
|
KeReleaseSpinLockFromDpcLevel(&ClusDiskSpinLock);
|
|
IoReleaseCancelSpinLock( irql );
|
|
|
|
//
|
|
// This should not be done here.
|
|
// Cleaning FsContext here, will prevent ClusDiskCleanup
|
|
// from doing its work
|
|
//
|
|
// ObDereferenceObject(targetDeviceObject);
|
|
// CDLOG("RootCtl_DecRef(%p)", targetDeviceObject );
|
|
// currentIrpStack->FileObject->FsContext = NULL;
|
|
|
|
//
|
|
// Release the scsi device.
|
|
//
|
|
// [GorN] 10/04/1999. Why this release was commented out?
|
|
// [GorN] 10/13/1999. It was commented out, because it was causing an AV,
|
|
// if the device was removed by PnP
|
|
|
|
//
|
|
// 2000/02/05: stevedz - RemoveLocks should resolve this problem.
|
|
//
|
|
// The following "if" only reduces the chances of AV to occur, not
|
|
// eliminates it completely. TargetDeviceObject is zeroed out by our PnP
|
|
// handler when the device is removed
|
|
//
|
|
if (physicalDisk->TargetDeviceObject) {
|
|
ReleaseScsiDevice( physicalDisk );
|
|
}
|
|
|
|
ClusDiskPrint((3,
|
|
"[ClusDisk] IOCTL, stop reservations on signature %lx.\n",
|
|
physicalDisk->Signature ));
|
|
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
|
|
} else {
|
|
status = STATUS_INVALID_HANDLE;
|
|
}
|
|
|
|
if (newPhysLockAcquired) {
|
|
ReleaseRemoveLock(&physicalDisk->RemoveLock, Irp);
|
|
}
|
|
|
|
ReleaseRemoveLock(&lockedPhysicalDisk->RemoveLock, Irp);
|
|
ReleaseRemoveLock(&lockedDeviceExtension->RemoveLock, Irp);
|
|
Irp->IoStatus.Status = status;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return(status);
|
|
|
|
}
|
|
|
|
// Aliveness check
|
|
case IOCTL_DISK_CLUSTER_ALIVE_CHECK:
|
|
{
|
|
CDLOG( "RootAliveCheck(%p)", DeviceObject );
|
|
|
|
if ( (RootDeviceObject == NULL) ||
|
|
(deviceExtension->BusType != RootBus) ) {
|
|
ReleaseRemoveLock(&lockedPhysicalDisk->RemoveLock, Irp);
|
|
ReleaseRemoveLock(&lockedDeviceExtension->RemoveLock, Irp);
|
|
Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST;
|
|
IoCompleteRequest( Irp, IO_NO_INCREMENT );
|
|
return(STATUS_INVALID_DEVICE_REQUEST);
|
|
}
|
|
|
|
IoAcquireCancelSpinLock( &irql );
|
|
KeAcquireSpinLockAtDpcLevel( &ClusDiskSpinLock );
|
|
|
|
// Indicate that we didn't acquire a third RemoveLock for the new physical device.
|
|
|
|
newPhysLockAcquired = FALSE;
|
|
|
|
if ( currentIrpStack->FileObject->FsContext ) {
|
|
targetDeviceObject = (PDEVICE_OBJECT)currentIrpStack->FileObject->FsContext;
|
|
physicalDisk = targetDeviceObject->DeviceExtension;
|
|
|
|
// We have a new device here, acquire the RemoveLock if possible.
|
|
|
|
status = AcquireRemoveLock(&physicalDisk->RemoveLock, Irp);
|
|
if ( !NT_SUCCESS(status) ) {
|
|
|
|
status = STATUS_FILE_DELETED;
|
|
|
|
} else {
|
|
|
|
newPhysLockAcquired = TRUE;
|
|
|
|
if ( physicalDisk->TargetDeviceObject == NULL ) {
|
|
status = STATUS_FILE_DELETED;
|
|
} else
|
|
if ( physicalDisk->ReserveFailure &&
|
|
(!NT_SUCCESS(physicalDisk->ReserveFailure)) ) {
|
|
status = physicalDisk->ReserveFailure;
|
|
} else {
|
|
//
|
|
// The device does not have to be 'online' to have been
|
|
// successfully arbitrated and being defended. However,
|
|
// the quorum device really should be 'online'...
|
|
//
|
|
if ( physicalDisk->ReserveTimer == 0 ) {
|
|
#if 0
|
|
ClusDiskPrint((
|
|
1,
|
|
"[ClusDisk] RootDeviceControl, AliveCheck failed, signature %lx, state = %s, ReserveTimer = %lx.\n",
|
|
physicalDisk->Signature,
|
|
DiskStateToString( physicalDisk->DiskState ),
|
|
physicalDisk->ReserveTimer ));
|
|
#endif
|
|
status = STATUS_CANCELLED;
|
|
} else {
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
}
|
|
}
|
|
|
|
} else {
|
|
status = STATUS_INVALID_HANDLE;
|
|
}
|
|
|
|
if ( status == STATUS_SUCCESS ) {
|
|
NTSTATUS newStatus;
|
|
|
|
newStatus = ClusDiskMarkIrpPending( Irp, ClusDiskIrpCancel );
|
|
if ( NT_SUCCESS( newStatus ) ) {
|
|
InsertTailList( &physicalDisk->WaitingIoctls,
|
|
&Irp->Tail.Overlay.ListEntry );
|
|
status = STATUS_PENDING;
|
|
|
|
// Release all the RemoveLocks.
|
|
|
|
if (newPhysLockAcquired) {
|
|
ReleaseRemoveLock(&physicalDisk->RemoveLock, Irp);
|
|
}
|
|
|
|
ReleaseRemoveLock(&lockedPhysicalDisk->RemoveLock, Irp);
|
|
ReleaseRemoveLock(&lockedDeviceExtension->RemoveLock, Irp);
|
|
|
|
} else {
|
|
status = newStatus;
|
|
|
|
if (newPhysLockAcquired) {
|
|
ReleaseRemoveLock(&physicalDisk->RemoveLock, Irp);
|
|
}
|
|
ReleaseRemoveLock(&lockedPhysicalDisk->RemoveLock, Irp);
|
|
ReleaseRemoveLock(&lockedDeviceExtension->RemoveLock, Irp);
|
|
|
|
Irp->IoStatus.Status = status;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
}
|
|
|
|
KeReleaseSpinLockFromDpcLevel( &ClusDiskSpinLock );
|
|
IoReleaseCancelSpinLock( irql );
|
|
} else {
|
|
KeReleaseSpinLockFromDpcLevel( &ClusDiskSpinLock );
|
|
IoReleaseCancelSpinLock( irql );
|
|
|
|
if (newPhysLockAcquired) {
|
|
ReleaseRemoveLock(&physicalDisk->RemoveLock, Irp);
|
|
}
|
|
ReleaseRemoveLock(&lockedPhysicalDisk->RemoveLock, Irp);
|
|
ReleaseRemoveLock(&lockedDeviceExtension->RemoveLock, Irp);
|
|
|
|
Irp->IoStatus.Status = status;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
}
|
|
|
|
return(status);
|
|
}
|
|
|
|
// Check out what's happening
|
|
case IOCTL_DISK_CLUSTER_ACTIVE:
|
|
{
|
|
// The called routine will validate the input buffers.
|
|
|
|
if ( (RootDeviceObject == NULL) ||
|
|
(deviceExtension->BusType != RootBus) ) {
|
|
ReleaseRemoveLock(&lockedPhysicalDisk->RemoveLock, Irp);
|
|
ReleaseRemoveLock(&lockedDeviceExtension->RemoveLock, Irp);
|
|
Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST;
|
|
IoCompleteRequest( Irp, IO_NO_INCREMENT );
|
|
return(STATUS_INVALID_DEVICE_REQUEST);
|
|
}
|
|
|
|
status = ClusDiskGetRunningDevices(
|
|
inputData,
|
|
outputSize
|
|
);
|
|
|
|
Irp->IoStatus.Status = status;
|
|
if ( NT_SUCCESS(status) ) {
|
|
Irp->IoStatus.Information = (inputData[0] + 1) * sizeof(ULONG);
|
|
} else {
|
|
Irp->IoStatus.Information = 0;
|
|
}
|
|
|
|
ReleaseRemoveLock(&lockedPhysicalDisk->RemoveLock, Irp);
|
|
ReleaseRemoveLock(&lockedDeviceExtension->RemoveLock, Irp);
|
|
IoCompleteRequest( Irp, IO_NO_INCREMENT );
|
|
return(status);
|
|
}
|
|
|
|
// Check if device is Cluster Capable (performs normal SCSI operations)
|
|
// NB: Non-SCSI device must return success on this call!
|
|
case IOCTL_DISK_CLUSTER_NOT_CLUSTER_CAPABLE:
|
|
{
|
|
UCHAR portNumber;
|
|
BOOLEAN isCapable;
|
|
|
|
//
|
|
// Get the passed in device signature.
|
|
//
|
|
isCapable = TRUE; // Err on the side of being usable.
|
|
if ( ARGUMENT_PRESENT( inputData ) &&
|
|
inputSize >= sizeof(ULONG) ) {
|
|
|
|
signature = inputData[0];
|
|
status = GetScsiPortNumber( signature, &portNumber );
|
|
if ( NT_SUCCESS(status) ) {
|
|
|
|
if ( portNumber != 0xff ) {
|
|
status = IsDiskClusterCapable( portNumber,
|
|
&isCapable);
|
|
|
|
} else {
|
|
status = STATUS_UNSUCCESSFUL;
|
|
}
|
|
}
|
|
|
|
} else {
|
|
//
|
|
// Default is to fail this IOCTL, which allows us to use the device.
|
|
//
|
|
status = STATUS_UNSUCCESSFUL;
|
|
}
|
|
//
|
|
// Verifier found a problem with the original code. The status returned
|
|
// wasn't the same as Irp->IoStatus.Status. The IRP status was always
|
|
// success, but the dispatch routine sometimes returned an error. This
|
|
// seems to work but verifier says this is invalid. When I changed the code
|
|
// to update the IRP status with the same returned status, new drives
|
|
// couldn't be seen. This is because the user mode component uses this
|
|
// IOCTL to indicate the disk is NOT cluster capable, so the user mode
|
|
// code looked for a failure to incidate a cluster disk.
|
|
//
|
|
|
|
//
|
|
// Fix for IBM. The Win2000 2195 code returned from this IOCTL the status
|
|
// of the SCSI miniport IOCTL. This was returned to DeviceIoControl rather
|
|
// than the status in the IRP. Changed to make the behavior the same as
|
|
// Win20000 2195.
|
|
//
|
|
|
|
//
|
|
// If the SCSI miniport IOCTL succeeds, we return success. If any of the
|
|
// routines failed while trying to issue the SCSI miniport IOCTL (including
|
|
// issuing the SCSI miniport IOCTL itself), then we return failure.
|
|
//
|
|
|
|
Irp->IoStatus.Status = status;
|
|
ReleaseRemoveLock(&lockedPhysicalDisk->RemoveLock, Irp);
|
|
ReleaseRemoveLock(&lockedDeviceExtension->RemoveLock, Irp);
|
|
IoCompleteRequest( Irp, IO_NO_INCREMENT );
|
|
|
|
return(status);
|
|
}
|
|
|
|
case IOCTL_DISK_CLUSTER_RESERVE_INFO:
|
|
{
|
|
CDLOG( "IoctlDiskClusterReserveInfo(%p)", DeviceObject );
|
|
|
|
status = GetReserveInfo( inputData,
|
|
inputSize,
|
|
&outputSize );
|
|
|
|
Irp->IoStatus.Status = status;
|
|
Irp->IoStatus.Information = outputSize;
|
|
|
|
ReleaseRemoveLock(&lockedPhysicalDisk->RemoveLock, Irp);
|
|
ReleaseRemoveLock(&lockedDeviceExtension->RemoveLock, Irp);
|
|
IoCompleteRequest( Irp, IO_NO_INCREMENT );
|
|
|
|
return status;
|
|
}
|
|
|
|
default:
|
|
{
|
|
ReleaseRemoveLock(&lockedPhysicalDisk->RemoveLock, Irp);
|
|
ReleaseRemoveLock(&lockedDeviceExtension->RemoveLock, Irp);
|
|
Irp->IoStatus.Status = STATUS_ILLEGAL_FUNCTION;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return(STATUS_ILLEGAL_FUNCTION);
|
|
}
|
|
|
|
|
|
} // switch
|
|
|
|
} // ClusDiskRootDeviceControl
|
|
|
|
|
|
NTSTATUS
|
|
GetReserveInfo(
|
|
PVOID InOutBuffer,
|
|
ULONG InSize,
|
|
ULONG* OutSize
|
|
)
|
|
{
|
|
PRESERVE_INFO reserveInfo;
|
|
PDEVICE_OBJECT targetDevice;
|
|
PCLUS_DEVICE_EXTENSION targetExt;
|
|
|
|
NTSTATUS status;
|
|
|
|
ULONG signature;
|
|
|
|
*OutSize = 0;
|
|
|
|
if ( InSize < sizeof(RESERVE_INFO) ||
|
|
InOutBuffer == NULL ) {
|
|
|
|
status = STATUS_INVALID_PARAMETER;
|
|
goto FnExit;
|
|
}
|
|
|
|
//
|
|
// Make sure the user specified a signature for a valid device.
|
|
//
|
|
|
|
signature = *(PULONG)InOutBuffer;
|
|
if ( !AttachedDevice( signature, &targetDevice ) ) {
|
|
status = STATUS_NOT_FOUND;
|
|
goto FnExit;
|
|
}
|
|
|
|
targetExt = targetDevice->DeviceExtension;
|
|
|
|
//
|
|
// We have a new device here, acquire the RemoveLock if possible.
|
|
//
|
|
|
|
status = AcquireRemoveLock(&targetExt->RemoveLock, GetReserveInfo);
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
goto FnExit;
|
|
}
|
|
|
|
//
|
|
// Return the information about reserves.
|
|
//
|
|
|
|
reserveInfo = (PRESERVE_INFO)InOutBuffer;
|
|
|
|
reserveInfo->Signature = targetExt->Signature;
|
|
reserveInfo->ReserveFailure = targetExt->ReserveFailure;
|
|
|
|
ACQUIRE_SHARED( &targetExt->ReserveInfoLock );
|
|
reserveInfo->LastReserveEnd = targetExt->LastReserveEnd;
|
|
RELEASE_SHARED( &targetExt->ReserveInfoLock );
|
|
|
|
reserveInfo->ArbWriteCount = targetExt->ArbWriteCount;
|
|
reserveInfo->ReserveCount = targetExt->ReserveCount;
|
|
|
|
KeQuerySystemTime( &reserveInfo->CurrentTime );
|
|
|
|
ReleaseRemoveLock(&targetExt->RemoveLock, GetReserveInfo);
|
|
|
|
*OutSize = sizeof(RESERVE_INFO);
|
|
|
|
FnExit:
|
|
|
|
return status;
|
|
|
|
} // GetReserveInfo
|
|
|
|
|
|
NTSTATUS
|
|
SetDiskState(
|
|
PVOID InBuffer,
|
|
ULONG InBufferLength,
|
|
ULONG OutBufferLength,
|
|
ULONG *BytesReturned
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
PSET_DISK_STATE_PARAMS params = (PSET_DISK_STATE_PARAMS)InBuffer;
|
|
PUCHAR outBuffer = (PUCHAR)InBuffer;
|
|
PDEVICE_OBJECT targetDevice;
|
|
PCLUS_DEVICE_EXTENSION physicalDisk;
|
|
|
|
NTSTATUS status;
|
|
|
|
UCHAR oldDiskState;
|
|
UCHAR newDiskState;
|
|
|
|
BOOLEAN success;
|
|
BOOLEAN removeLockAcquired = FALSE;
|
|
|
|
*BytesReturned = 0;
|
|
|
|
if ( !ARGUMENT_PRESENT(params) ||
|
|
InBufferLength < sizeof(SET_DISK_STATE_PARAMS) ) {
|
|
|
|
status = STATUS_INVALID_PARAMETER;
|
|
goto FnExit;
|
|
}
|
|
|
|
//
|
|
// Make sure the user specified a signature for a valid device.
|
|
//
|
|
|
|
success = AttachedDevice( params->Signature, &targetDevice );
|
|
|
|
if ( !success ) {
|
|
status = STATUS_NOT_FOUND;
|
|
goto FnExit;
|
|
}
|
|
|
|
physicalDisk = targetDevice->DeviceExtension;
|
|
|
|
// We have a new device here, acquire the RemoveLock if possible.
|
|
|
|
status = AcquireRemoveLock(&physicalDisk->RemoveLock, SetDiskState);
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
goto FnExit;
|
|
}
|
|
|
|
removeLockAcquired = TRUE;
|
|
|
|
//
|
|
// Save the old disk state.
|
|
//
|
|
|
|
oldDiskState = (UCHAR)physicalDisk->DiskState;
|
|
|
|
newDiskState = params->NewState;
|
|
|
|
ClusDiskPrint((3, "[ClusDisk] RootDeviceControl: Setting state on disk %u (%p), state %s \n",
|
|
physicalDisk->DiskNumber,
|
|
targetDevice,
|
|
DiskStateToString( newDiskState ) ));
|
|
|
|
if ( newDiskState > DiskStateMaximum ) {
|
|
status = STATUS_INVALID_PARAMETER;
|
|
goto FnExit;
|
|
}
|
|
|
|
if ( DiskOnline == newDiskState ) {
|
|
ASSERT_RESERVES_STARTED( physicalDisk );
|
|
|
|
//
|
|
// If current state is offline, then before bringing the disk
|
|
// online, we want to dismount the file systems if they are mounted.
|
|
//
|
|
|
|
if ( DiskOffline == physicalDisk->DiskState ) {
|
|
ClusDiskDismountDevice( physicalDisk->DiskNumber, TRUE );
|
|
}
|
|
ONLINE_DISK( physicalDisk );
|
|
} else if ( DiskOffline == newDiskState ) {
|
|
OFFLINE_DISK( physicalDisk );
|
|
} else {
|
|
|
|
// There aren't really any other valid states, but this code remains...
|
|
|
|
physicalDisk->DiskState = newDiskState;
|
|
}
|
|
|
|
CDLOG( "IoctlClusterSetState(%p): old %!diskstate! => new %!diskstate!",
|
|
targetDevice,
|
|
oldDiskState,
|
|
newDiskState );
|
|
|
|
ClusDiskPrint((1, "[ClusDisk] RootDeviceControl: disk %u (%p), old %s => new %s \n",
|
|
physicalDisk->DiskNumber,
|
|
targetDevice,
|
|
DiskStateToString( oldDiskState ),
|
|
DiskStateToString( newDiskState ) ));
|
|
|
|
//
|
|
// Optionally return the old state.
|
|
//
|
|
|
|
if ( sizeof(SET_DISK_STATE_PARAMS) == OutBufferLength ) {
|
|
|
|
// Return the old state in the structure.
|
|
|
|
params->OldState = oldDiskState;
|
|
*BytesReturned = sizeof(SET_DISK_STATE_PARAMS);
|
|
|
|
} else if ( OutBufferLength >= sizeof(UCHAR) ) {
|
|
|
|
// Return the old state as a single UCHAR.
|
|
|
|
*outBuffer = (UCHAR)oldDiskState;
|
|
*BytesReturned = sizeof(UCHAR);
|
|
}
|
|
|
|
FnExit:
|
|
|
|
if ( removeLockAcquired ) {
|
|
ReleaseRemoveLock(&physicalDisk->RemoveLock, SetDiskState);
|
|
}
|
|
|
|
return status;
|
|
|
|
} // SetDiskState
|
|
|
|
|
|
|
|
|
|
|
|
|
|
NTSTATUS
|
|
ClusDiskShutdownFlush(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called for a shutdown and flush IRPs. These are sent by the
|
|
system before it actually shuts down or when the file system does a flush.
|
|
|
|
Arguments:
|
|
|
|
DriverObject - Pointer to device object to being shutdown by system.
|
|
Irp - IRP involved.
|
|
|
|
Return Value:
|
|
|
|
NT Status
|
|
|
|
--*/
|
|
|
|
{
|
|
PCLUS_DEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
|
|
PCLUS_DEVICE_EXTENSION physicalDisk =
|
|
deviceExtension->PhysicalDevice->DeviceExtension;
|
|
NTSTATUS status;
|
|
|
|
status = AcquireRemoveLock( &deviceExtension->RemoveLock, Irp);
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
Irp->IoStatus.Status = status;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return status;
|
|
}
|
|
|
|
//
|
|
// Return error if device is our root device.
|
|
//
|
|
if ( deviceExtension->BusType == RootBus ) {
|
|
ReleaseRemoveLock(&deviceExtension->RemoveLock, Irp);
|
|
Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return(STATUS_INVALID_DEVICE_REQUEST);
|
|
}
|
|
|
|
if ( physicalDisk->DiskState != DiskOnline ) {
|
|
ReleaseRemoveLock(&deviceExtension->RemoveLock, Irp);
|
|
Irp->IoStatus.Status = STATUS_DEVICE_OFF_LINE;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return(STATUS_DEVICE_OFF_LINE);
|
|
}
|
|
|
|
//
|
|
// Make sure the device attach completed.
|
|
//
|
|
status = WaitForAttachCompletion( deviceExtension,
|
|
TRUE, // Wait
|
|
TRUE ); // Also check physical device
|
|
if ( !NT_SUCCESS( status ) ) {
|
|
ReleaseRemoveLock(&deviceExtension->RemoveLock, Irp);
|
|
Irp->IoStatus.Status = status;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return status;
|
|
}
|
|
|
|
//
|
|
// Set current stack back one.
|
|
//
|
|
|
|
IoSkipCurrentIrpStackLocation( Irp );
|
|
|
|
ReleaseRemoveLock(&deviceExtension->RemoveLock, Irp);
|
|
|
|
return IoCallDriver(deviceExtension->TargetDeviceObject, Irp);
|
|
|
|
} // ClusDiskShutdownFlush()
|
|
|
|
|
|
|
|
NTSTATUS
|
|
ClusDiskNewDiskCompletion(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID Context
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the completion routine for IOCTL_DISK_FIND_NEW_DEVICES.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Pointer to device object to being shutdown by system.
|
|
Irp - IRP involved.
|
|
Context - Previous disk count.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
|
|
{
|
|
PCLUS_DEVICE_EXTENSION deviceExtension =
|
|
(PCLUS_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
|
|
PCLUS_DEVICE_EXTENSION physicalDisk =
|
|
deviceExtension->PhysicalDevice->DeviceExtension;
|
|
|
|
//
|
|
// Find new disk devices and attach to disk and all of its partitions.
|
|
//
|
|
|
|
ClusDiskNextDisk = Context;
|
|
ClusDiskScsiInitialize(DeviceObject->DriverObject, Context, 1);
|
|
|
|
// There are two RemoveLocks held. Release them both.
|
|
|
|
ReleaseRemoveLock(&physicalDisk->RemoveLock, Irp);
|
|
ReleaseRemoveLock(&deviceExtension->RemoveLock, Irp);
|
|
|
|
if (Irp->PendingReturned) {
|
|
IoMarkIrpPending(Irp);
|
|
}
|
|
return Irp->IoStatus.Status;
|
|
|
|
} // ClusDiskNewDiskCompletion
|
|
|
|
|
|
|
|
NTSTATUS
|
|
ClusDiskSetLayoutCompletion(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID Context
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the completion routine for IOCTL_SET_DRIVE_LAYOUT and
|
|
IOCTL_DISK_SET_DRIVE_LAYOUT_EX. This will routine will make sure
|
|
the cached drive layout info structure is updated.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Pointer to device object
|
|
Irp - IRP involved.
|
|
Context - Not used
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
|
|
{
|
|
PCLUS_DEVICE_EXTENSION deviceExtension =
|
|
(PCLUS_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
|
|
PCLUS_DEVICE_EXTENSION physicalDisk =
|
|
deviceExtension->PhysicalDevice->DeviceExtension;
|
|
PDRIVE_LAYOUT_INFORMATION_EX driveLayoutInfo = NULL;
|
|
|
|
//
|
|
// Update the cached drive layout.
|
|
//
|
|
|
|
GetDriveLayout( physicalDisk->DeviceObject,
|
|
&driveLayoutInfo,
|
|
TRUE, // Update cached drive layout
|
|
FALSE ); // Don't flush storage drivers cached drive layout
|
|
|
|
if ( driveLayoutInfo ) {
|
|
ExFreePool( driveLayoutInfo );
|
|
}
|
|
|
|
// There are two RemoveLocks held. Release them both.
|
|
|
|
ReleaseRemoveLock(&physicalDisk->RemoveLock, Irp);
|
|
ReleaseRemoveLock(&deviceExtension->RemoveLock, Irp);
|
|
|
|
if (Irp->PendingReturned) {
|
|
IoMarkIrpPending(Irp);
|
|
}
|
|
return Irp->IoStatus.Status;
|
|
|
|
} // ClusDiskSetLayoutCompletion
|
|
|
|
|
|
BOOLEAN
|
|
ClusDiskAttached(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN ULONG DiskNumber
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine checks if clusdisk in in the path.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - pointer the device object to check if ClusDisk is present.
|
|
|
|
DiskNumber - the disk number for this device object.
|
|
|
|
Return Value:
|
|
|
|
TRUE - if ClusDisk is attached.
|
|
|
|
FALSE - if ClusDisk is not attached.
|
|
|
|
--*/
|
|
|
|
{
|
|
PIRP irp;
|
|
PKEVENT event;
|
|
IO_STATUS_BLOCK ioStatusBlock;
|
|
NTSTATUS status;
|
|
WCHAR deviceNameBuffer[MAX_PARTITION_NAME_LENGTH];
|
|
UNICODE_STRING deviceNameString;
|
|
OBJECT_ATTRIBUTES objectAttributes;
|
|
HANDLE fileHandle;
|
|
HANDLE eventHandle;
|
|
|
|
if ( DeviceObject->DeviceType == FILE_DEVICE_DISK_FILE_SYSTEM ) {
|
|
//
|
|
// Create event for notification.
|
|
//
|
|
status = ZwCreateEvent( &eventHandle,
|
|
EVENT_ALL_ACCESS,
|
|
NULL,
|
|
SynchronizationEvent,
|
|
FALSE );
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
ClusDiskPrint((
|
|
1,
|
|
"[ClusDisk] Failed to create event, status %lx\n",
|
|
status ));
|
|
return(TRUE);
|
|
}
|
|
//
|
|
// Open a file handle and perform the request
|
|
//
|
|
|
|
if ( FAILED( StringCchPrintfW( deviceNameBuffer,
|
|
RTL_NUMBER_OF(deviceNameBuffer),
|
|
DEVICE_PARTITION_NAME,
|
|
DiskNumber,
|
|
0 ) ) ) {
|
|
ZwClose(eventHandle);
|
|
return TRUE;
|
|
}
|
|
|
|
WCSLEN_ASSERT( deviceNameBuffer );
|
|
|
|
RtlInitUnicodeString(&deviceNameString,
|
|
deviceNameBuffer);
|
|
|
|
//
|
|
// Setup object attributes for the file to open.
|
|
//
|
|
InitializeObjectAttributes(
|
|
&objectAttributes,
|
|
&deviceNameString,
|
|
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
status = ZwCreateFile( &fileHandle,
|
|
FILE_READ_DATA,
|
|
&objectAttributes,
|
|
&ioStatusBlock,
|
|
NULL,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
FILE_OPEN,
|
|
FILE_SYNCHRONOUS_IO_NONALERT,
|
|
NULL,
|
|
0 );
|
|
ASSERT( status != STATUS_PENDING );
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
ClusDiskPrint((
|
|
1,
|
|
"[ClusDisk] DiskAttached, failed to open file %ws. Error %lx.\n",
|
|
deviceNameBuffer,
|
|
status ));
|
|
ZwClose(eventHandle);
|
|
return(TRUE);
|
|
}
|
|
|
|
status = ZwDeviceIoControlFile( fileHandle,
|
|
eventHandle,
|
|
NULL,
|
|
NULL,
|
|
&ioStatusBlock,
|
|
IOCTL_DISK_CLUSTER_TEST,
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
0 );
|
|
|
|
if ( status == STATUS_PENDING ) {
|
|
status = ZwWaitForSingleObject(eventHandle,
|
|
FALSE,
|
|
NULL);
|
|
ASSERT( NT_SUCCESS(status) );
|
|
status = ioStatusBlock.Status;
|
|
}
|
|
|
|
ZwClose( fileHandle );
|
|
ZwClose( eventHandle );
|
|
return((BOOLEAN)status == STATUS_SUCCESS);
|
|
}
|
|
|
|
event = ExAllocatePool( NonPagedPool,
|
|
sizeof(KEVENT) );
|
|
if ( event == NULL ) {
|
|
return(FALSE);
|
|
}
|
|
|
|
//
|
|
// Find out if ClusDisk is already in device stack.
|
|
//
|
|
|
|
irp = IoBuildDeviceIoControlRequest(IOCTL_DISK_CLUSTER_TEST,
|
|
DeviceObject,
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
0,
|
|
FALSE,
|
|
event,
|
|
&ioStatusBlock);
|
|
|
|
if (!irp) {
|
|
ExFreePool( event );
|
|
ClusDiskPrint((
|
|
1,
|
|
"[ClusDisk] Failed to build IRP to test for ClusDisk.\n"
|
|
));
|
|
return(FALSE);
|
|
}
|
|
|
|
//
|
|
// Set the event object to the unsignaled state.
|
|
// It will be used to signal request completion.
|
|
//
|
|
|
|
KeInitializeEvent(event,
|
|
NotificationEvent,
|
|
FALSE);
|
|
|
|
status = IoCallDriver(DeviceObject,
|
|
irp);
|
|
|
|
if (status == STATUS_PENDING) {
|
|
|
|
KeWaitForSingleObject(event,
|
|
Suspended,
|
|
KernelMode,
|
|
FALSE,
|
|
NULL);
|
|
|
|
status = ioStatusBlock.Status;
|
|
}
|
|
ExFreePool( event );
|
|
|
|
if ( NT_SUCCESS(status) ) {
|
|
return(TRUE);
|
|
}
|
|
|
|
return(FALSE);
|
|
|
|
} // ClusDiskAttached
|
|
|
|
|
|
|
|
BOOLEAN
|
|
ClusDiskVerifyAttach(
|
|
PDEVICE_OBJECT DeviceObject
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine verifies if ClusDisk is attached, and whether it should be
|
|
detached.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - pointer to a ClusDisk device object to verify if it is
|
|
and should remain attached.
|
|
|
|
Return Value:
|
|
|
|
TRUE - if device is still attached.
|
|
|
|
FALSE - if device was detached.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
PDEVICE_OBJECT deviceObject;
|
|
PCLUS_DEVICE_EXTENSION deviceExtension;
|
|
PDRIVE_LAYOUT_INFORMATION_EX driveLayoutInfo;
|
|
UNICODE_STRING signatureName;
|
|
|
|
deviceExtension = DeviceObject->DeviceExtension;
|
|
|
|
//
|
|
// The following call really can't fail!
|
|
//
|
|
if ( !ClusDiskAttached( DeviceObject, deviceExtension->DiskNumber ) ) {
|
|
return(FALSE);
|
|
}
|
|
|
|
//
|
|
// Check if this device is a valid attachment.
|
|
//
|
|
if ( deviceExtension->AttachValid ) {
|
|
return(TRUE);
|
|
}
|
|
|
|
//
|
|
// Get device object for the physical (partition0) device.
|
|
//
|
|
deviceObject = deviceExtension->PhysicalDevice;
|
|
|
|
//
|
|
// Otherwise, we're not sure... verify it.
|
|
//
|
|
|
|
//
|
|
// Read the partition info to get the signature. If this device is
|
|
// a valid attachment then update the ClusDisk DeviceObject. Otherwise,
|
|
// detach or leave attached but in the UNKNOWN state.
|
|
//
|
|
|
|
driveLayoutInfo = ClusDiskGetPartitionInfo( deviceExtension );
|
|
if ( driveLayoutInfo != NULL ) {
|
|
|
|
deviceExtension->Signature = driveLayoutInfo->Mbr.Signature;
|
|
if ( MatchDevice( driveLayoutInfo->Mbr.Signature, NULL ) ) {
|
|
//
|
|
// We assume that the device object we have is for the partition0
|
|
// device object.
|
|
//
|
|
#if 0
|
|
ClusDiskPrint((
|
|
1,
|
|
"[ClusDisk] We are going to attach signature %08lx to DevObj %p \n",
|
|
driveLayoutInfo->Mbr.Signature,
|
|
DeviceObject ));
|
|
#endif
|
|
AddAttachedDevice( driveLayoutInfo->Mbr.Signature,
|
|
deviceObject );
|
|
|
|
//
|
|
// Need to write disk info into the signatures list.
|
|
//
|
|
status = ClusDiskInitRegistryString(
|
|
&signatureName,
|
|
CLUSDISK_SIGNATURE_KEYNAME,
|
|
wcslen(CLUSDISK_SIGNATURE_KEYNAME)
|
|
);
|
|
if ( NT_SUCCESS(status) ) {
|
|
ClusDiskWriteDiskInfo( driveLayoutInfo->Mbr.Signature,
|
|
deviceExtension->DiskNumber,
|
|
CLUSDISK_SIGNATURE_KEYNAME
|
|
);
|
|
ExFreePool( signatureName.Buffer );
|
|
}
|
|
} else {
|
|
ClusDiskDetachDevice( driveLayoutInfo->Mbr.Signature,
|
|
DeviceObject->DriverObject
|
|
);
|
|
}
|
|
ExFreePool( driveLayoutInfo );
|
|
}
|
|
|
|
return(TRUE);
|
|
|
|
} // ClusDiskVerifyAttach
|
|
|
|
|
|
|
|
VOID
|
|
ClusDiskWriteDiskInfo(
|
|
IN ULONG Signature,
|
|
IN ULONG DiskNumber,
|
|
IN LPWSTR SubKeyName
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Write the disk name for the given signature.
|
|
|
|
Arguments:
|
|
|
|
Signature - the signature key to store the disk name under.
|
|
|
|
DiskNumber - the disk number to assign to the given signature. It is
|
|
assumed that this is always describing partition0 on the disk.
|
|
|
|
SubKeyName - the clusdisk parameters subkey name in which to write
|
|
this information.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
UNICODE_STRING keyName;
|
|
WCHAR keyNameBuffer[MAXIMUM_FILENAME_LENGTH];
|
|
WCHAR signatureBuffer[64];
|
|
HANDLE signatureHandle;
|
|
OBJECT_ATTRIBUTES objectAttributes;
|
|
NTSTATUS status;
|
|
|
|
keyName.Length = 0;
|
|
keyName.MaximumLength = sizeof( keyNameBuffer );
|
|
keyName.Buffer = keyNameBuffer;
|
|
|
|
RtlAppendUnicodeToString( &keyName, ClusDiskRegistryPath.Buffer );
|
|
|
|
RtlAppendUnicodeToString( &keyName, SubKeyName );
|
|
|
|
if ( FAILED( StringCchPrintfW( signatureBuffer,
|
|
RTL_NUMBER_OF(signatureBuffer),
|
|
L"\\%08lX",
|
|
Signature ) ) ) {
|
|
return;
|
|
}
|
|
|
|
WCSLEN_ASSERT( signatureBuffer );
|
|
|
|
RtlAppendUnicodeToString( &keyName, signatureBuffer );
|
|
keyName.Buffer[ keyName.Length / sizeof(WCHAR) ] = UNICODE_NULL;
|
|
|
|
//
|
|
// Setup the object attributes for the Parameters\SubKeyName\xyz key.
|
|
//
|
|
|
|
InitializeObjectAttributes(
|
|
&objectAttributes,
|
|
&keyName,
|
|
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
//
|
|
// Open Parameters\SubKeyName\xyz Key.
|
|
//
|
|
|
|
status = ZwOpenKey(
|
|
&signatureHandle,
|
|
KEY_READ | KEY_WRITE,
|
|
&objectAttributes
|
|
);
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
ClusDiskPrint((
|
|
1,
|
|
"[ClusDisk] WriteDiskInfo: Failed to open %wZ registry key. Status: %lx\n",
|
|
&keyName,
|
|
status
|
|
));
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Write the disk name.
|
|
//
|
|
status = ClusDiskAddDiskName( signatureHandle, DiskNumber );
|
|
|
|
ZwClose( signatureHandle );
|
|
|
|
return;
|
|
|
|
} // ClusDiskWriteDiskInfo
|
|
|
|
|
|
|
|
NTSTATUS
|
|
GetDriveLayout(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
OUT PDRIVE_LAYOUT_INFORMATION_EX *DriveLayout,
|
|
BOOLEAN UpdateCachedLayout,
|
|
BOOLEAN FlushStorageDrivers
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Return the DRIVE_LAYOUT_INFORMATION_EX for a given device object.
|
|
Caller is responsible for freeing this drive layout buffer.
|
|
|
|
Note on PhysicalDiskObject parameter: this should only be set to TRUE
|
|
when the DeviceObject parameter is for physical disk (partition 0 device)
|
|
AND we want to tell storage drivers to flush their cached drive layout.
|
|
If should never be set to TRUE if the device object isn't a physical disk.
|
|
|
|
We could have a physical disk but still want to use the storage drivers
|
|
cached layout when we know it is current. For example, if the drive layout
|
|
was just set, we don't need to set this flag. Or we may be in a code path
|
|
that already told the storage drivers to set the drive layout, so we don't
|
|
need to do it again.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - The specific device object to return info about
|
|
|
|
BytesPerSector - The number of bytes per sector on this disk
|
|
|
|
DriveLayout - Pointer to a DRIVE_LAYOUT_INFORMATION_EX structure to return the info
|
|
|
|
UpdateCachedLayout - Update the drive layout stored in the device extension (if any)
|
|
with a fresh copy.
|
|
|
|
FlushStorageDrivers - Indicates whether storage drivers should flush their cached
|
|
drive layout data. This should only be set to true when the
|
|
DeviceObject is a physical disk (partition 0 device).
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
|
|
{
|
|
PCLUS_DEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
|
|
PCLUS_DEVICE_EXTENSION physicalDisk = deviceExtension->PhysicalDevice->DeviceExtension;
|
|
PDRIVE_LAYOUT_INFORMATION_EX driveLayoutInfo = NULL;
|
|
PDRIVE_LAYOUT_INFORMATION_EX cachedDriveLayoutInfo = NULL;
|
|
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
ULONG driveLayoutSize;
|
|
|
|
BOOLEAN cachedCopy = FALSE;
|
|
BOOLEAN freeLayouts = FALSE;
|
|
|
|
*DriveLayout = NULL;
|
|
|
|
//
|
|
// Allocate a drive layout buffer.
|
|
//
|
|
|
|
driveLayoutSize = sizeof(DRIVE_LAYOUT_INFORMATION_EX) +
|
|
(MAX_PARTITIONS * sizeof(PARTITION_INFORMATION_EX));
|
|
|
|
driveLayoutInfo = ExAllocatePool(NonPagedPoolCacheAligned,
|
|
driveLayoutSize
|
|
);
|
|
|
|
if ( !driveLayoutInfo ) {
|
|
ClusDiskPrint(( 1,
|
|
"[ClusDisk] GetDriveLayout: Failed to allocate drive layout structure. \n"
|
|
));
|
|
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto FnExit;
|
|
}
|
|
|
|
if ( UpdateCachedLayout ) {
|
|
|
|
//
|
|
// If cached buffer needs to be updated, free the existing buffer.
|
|
//
|
|
|
|
ACQUIRE_EXCLUSIVE( &physicalDisk->DriveLayoutLock );
|
|
|
|
if ( physicalDisk->DriveLayout ) {
|
|
ExFreePool( physicalDisk->DriveLayout );
|
|
physicalDisk->DriveLayout = NULL;
|
|
}
|
|
physicalDisk->DriveLayoutSize = 0;
|
|
|
|
RELEASE_EXCLUSIVE( &physicalDisk->DriveLayoutLock );
|
|
|
|
} else {
|
|
|
|
//
|
|
// If cached copy exists, use that instead of getting a new version.
|
|
//
|
|
|
|
ACQUIRE_SHARED( &physicalDisk->DriveLayoutLock );
|
|
|
|
if ( physicalDisk->DriveLayout ) {
|
|
|
|
ClusDiskPrint(( 3,
|
|
"[ClusDisk] GetDriveLayout: using cached drive layout information for DE %p \n",
|
|
physicalDisk
|
|
));
|
|
|
|
RtlCopyMemory( driveLayoutInfo,
|
|
physicalDisk->DriveLayout,
|
|
physicalDisk->DriveLayoutSize );
|
|
|
|
*DriveLayout = driveLayoutInfo;
|
|
cachedCopy = TRUE;
|
|
}
|
|
|
|
RELEASE_SHARED( &physicalDisk->DriveLayoutLock );
|
|
|
|
if ( cachedCopy ) {
|
|
goto FnExit;
|
|
}
|
|
}
|
|
|
|
freeLayouts = TRUE;
|
|
|
|
//
|
|
// Allocate a drive layout buffer for saving in device extension.
|
|
//
|
|
|
|
cachedDriveLayoutInfo = ExAllocatePool(NonPagedPoolCacheAligned,
|
|
driveLayoutSize
|
|
);
|
|
|
|
if ( !cachedDriveLayoutInfo ) {
|
|
ClusDiskPrint(( 1,
|
|
"[ClusDisk] GetDriveLayout: Failed to allocate cached drive layout structure. \n"
|
|
));
|
|
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto FnExit;
|
|
}
|
|
|
|
//
|
|
// If the device is disk (partition 0), tell the storage drivers to flush their
|
|
// cached drive layout. Sometimes we don't need to tell the storage drivers to
|
|
// flush their cache, even when we have a physical disk. For example, we may know
|
|
// that we previously told them to flush, so we don't need to do so now.
|
|
//
|
|
|
|
if ( FlushStorageDrivers ) {
|
|
SimpleDeviceIoControl( DeviceObject,
|
|
IOCTL_DISK_UPDATE_PROPERTIES,
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
0 );
|
|
}
|
|
|
|
status = SimpleDeviceIoControl(DeviceObject,
|
|
IOCTL_DISK_GET_DRIVE_LAYOUT_EX,
|
|
NULL,
|
|
0,
|
|
cachedDriveLayoutInfo,
|
|
driveLayoutSize
|
|
);
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
|
|
//
|
|
// Couldn't get the drive layout. Free the temporary buffer, set the caller's
|
|
// drive layout pointer to NULL, and return the error status to the caller.
|
|
//
|
|
|
|
ClusDiskPrint(( 1,
|
|
"[ClusDisk] GetDriveLayout: Failed to issue IoctlDiskGetDriveLayout. %08X\n",
|
|
status
|
|
));
|
|
|
|
CDLOG( "GetDriveLayout(%p): failed %!status!",
|
|
DeviceObject,
|
|
status );
|
|
|
|
*DriveLayout = NULL;
|
|
freeLayouts = TRUE;
|
|
|
|
goto FnExit;
|
|
|
|
}
|
|
|
|
//
|
|
// Successfully retrieved drive layout. Save the new layout in the device
|
|
// extension. Copy the layout into the user's buffer.
|
|
//
|
|
|
|
ClusDiskPrint(( 3,
|
|
"[ClusDisk] GetDriveLayout: updating drive layout for DE %p \n",
|
|
physicalDisk
|
|
));
|
|
|
|
CDLOG( "GetDriveLayout(%p): updating drive layout for DE %p ",
|
|
DeviceObject,
|
|
physicalDisk );
|
|
|
|
freeLayouts = FALSE;
|
|
|
|
ACQUIRE_EXCLUSIVE( &physicalDisk->DriveLayoutLock );
|
|
|
|
if ( physicalDisk->DriveLayout ) {
|
|
ExFreePool( physicalDisk->DriveLayout );
|
|
physicalDisk->DriveLayout = NULL;
|
|
}
|
|
physicalDisk->DriveLayout = cachedDriveLayoutInfo;
|
|
physicalDisk->DriveLayoutSize = driveLayoutSize;
|
|
|
|
RELEASE_EXCLUSIVE( &physicalDisk->DriveLayoutLock );
|
|
|
|
RtlCopyMemory( driveLayoutInfo,
|
|
cachedDriveLayoutInfo,
|
|
driveLayoutSize );
|
|
|
|
//
|
|
// Point the user to the drive layout buffer and return success. Caller is
|
|
// responsible for freeing the buffer when they are done with it.
|
|
//
|
|
|
|
*DriveLayout = driveLayoutInfo;
|
|
|
|
FnExit:
|
|
|
|
if ( freeLayouts ) {
|
|
|
|
if ( driveLayoutInfo ) {
|
|
ExFreePool( driveLayoutInfo );
|
|
}
|
|
|
|
if ( cachedDriveLayoutInfo ) {
|
|
ExFreePool( cachedDriveLayoutInfo );
|
|
}
|
|
}
|
|
|
|
return status;
|
|
|
|
} // GetDriveLayout
|
|
|
|
|
|
|
|
MEDIA_TYPE
|
|
GetMediaType(
|
|
IN PDEVICE_OBJECT DeviceObject
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Return the MediaType for a given device object.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - The specific device object to return info about
|
|
|
|
Return Value:
|
|
|
|
Media Type or Unknown if not known.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
MEDIA_TYPE mediaType = Unknown;
|
|
|
|
status = GetDiskGeometry( DeviceObject, &mediaType );
|
|
|
|
if ( !NT_SUCCESS( status ) ) {
|
|
mediaType = Unknown;
|
|
}
|
|
|
|
return mediaType;
|
|
|
|
} // GetMediaType
|
|
|
|
|
|
|
|
NTSTATUS
|
|
GetScsiAddress(
|
|
PDEVICE_OBJECT DeviceObject,
|
|
PSCSI_ADDRESS ScsiAddress
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Return the SCSI_ADDRESS for a given device object.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - The specific device object to return info about
|
|
|
|
ScsiAddress - Pointer to a SCSI_ADDRESS structure to return the info
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
|
|
{
|
|
PIRP irp;
|
|
PKEVENT event;
|
|
IO_STATUS_BLOCK ioStatusBlock;
|
|
NTSTATUS status;
|
|
|
|
event = ExAllocatePool( NonPagedPool,
|
|
sizeof(KEVENT) );
|
|
if ( event == NULL ) {
|
|
return( STATUS_INSUFFICIENT_RESOURCES );
|
|
}
|
|
|
|
//
|
|
// Find out if this is on a SCSI bus. Note, that if this device
|
|
// is not a SCSI device, it is expected that the following
|
|
// IOCTL will fail!
|
|
//
|
|
|
|
irp = IoBuildDeviceIoControlRequest(IOCTL_SCSI_GET_ADDRESS,
|
|
DeviceObject,
|
|
NULL,
|
|
0,
|
|
ScsiAddress,
|
|
sizeof(SCSI_ADDRESS),
|
|
FALSE,
|
|
event,
|
|
&ioStatusBlock);
|
|
|
|
if (!irp) {
|
|
ExFreePool( event );
|
|
ClusDiskPrint((
|
|
1,
|
|
"[ClusDisk] Failed to build IRP to read SCSI ADDRESS.\n"
|
|
));
|
|
return(STATUS_INSUFFICIENT_RESOURCES);
|
|
}
|
|
|
|
//
|
|
// Set the event object to the unsignaled state.
|
|
// It will be used to signal request completion.
|
|
//
|
|
|
|
KeInitializeEvent(event,
|
|
NotificationEvent,
|
|
FALSE);
|
|
|
|
status = IoCallDriver(DeviceObject,
|
|
irp);
|
|
|
|
if (status == STATUS_PENDING) {
|
|
|
|
KeWaitForSingleObject(event,
|
|
Suspended,
|
|
KernelMode,
|
|
FALSE,
|
|
NULL);
|
|
|
|
status = ioStatusBlock.Status;
|
|
}
|
|
ExFreePool( event );
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
ClusDiskPrint((
|
|
1,
|
|
"[ClusDisk] Failed to read SCSI_ADDRESS. %08X\n",
|
|
status
|
|
));
|
|
|
|
CDLOG( "GetScsiAddress(%p): failed %!status!",
|
|
DeviceObject,
|
|
status );
|
|
}
|
|
|
|
return(status);
|
|
|
|
} // GetScsiAddress
|
|
|
|
|
|
|
|
PDRIVE_LAYOUT_INFORMATION_EX
|
|
ClusDiskGetPartitionInfo(
|
|
PCLUS_DEVICE_EXTENSION DeviceExtension
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Return the Partition Layout Information for particular device extension.
|
|
|
|
Only return a valid partition layout for MBR disks - NULL for any other
|
|
kind of disk.
|
|
|
|
Arguments:
|
|
|
|
DeviceExtension - The specific device extension to return information.
|
|
|
|
Return Value:
|
|
|
|
Pointer to an allocated partition layout information structure for MBR disk.
|
|
NULL on failure.
|
|
|
|
Notes:
|
|
|
|
The caller is responsible for freeing the allocated buffer.
|
|
|
|
--*/
|
|
|
|
{
|
|
PIRP irp;
|
|
NTSTATUS status = STATUS_IO_TIMEOUT;
|
|
ULONG driveLayoutInfoSize;
|
|
PDRIVE_LAYOUT_INFORMATION_EX driveLayoutInfo;
|
|
ULONG retryCount = MAX_RETRIES;
|
|
|
|
driveLayoutInfoSize = sizeof(DRIVE_LAYOUT_INFORMATION_EX) +
|
|
(MAX_PARTITIONS * sizeof(PARTITION_INFORMATION_EX));
|
|
|
|
driveLayoutInfo = ExAllocatePool(NonPagedPoolCacheAligned,
|
|
driveLayoutInfoSize);
|
|
|
|
if ( !driveLayoutInfo ) {
|
|
ClusDiskPrint((
|
|
1,
|
|
"[ClusDisk] Failed to allocate PartitionInfo structure to read drive layout.\n"
|
|
));
|
|
return(NULL);
|
|
}
|
|
|
|
while ( retryCount-- ) {
|
|
|
|
if ( (retryCount != (MAX_RETRIES-1)) &&
|
|
(status != STATUS_DEVICE_BUSY) ) {
|
|
|
|
ClusDiskLogError( RootDeviceObject->DriverObject, // Use RootDeviceObject not DevObj
|
|
RootDeviceObject,
|
|
DeviceExtension->ScsiAddress.PathId, // Sequence number
|
|
0, // Major function code
|
|
0, // Retry count
|
|
ID_GET_PARTITION, // Unique error
|
|
STATUS_SUCCESS,
|
|
CLUSDISK_RESET_BUS_REQUESTED,
|
|
0,
|
|
NULL );
|
|
|
|
ResetScsiDevice( NULL, &DeviceExtension->ScsiAddress );
|
|
}
|
|
|
|
status = SimpleDeviceIoControl( DeviceExtension->TargetDeviceObject,
|
|
IOCTL_DISK_GET_DRIVE_LAYOUT_EX,
|
|
NULL,
|
|
0,
|
|
driveLayoutInfo,
|
|
driveLayoutInfoSize );
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
if ( (status != STATUS_DEVICE_OFF_LINE) &&
|
|
(status != STATUS_DATA_OVERRUN) ) {
|
|
ClusDiskPrint((
|
|
1,
|
|
"[ClusDisk] Failed to read PartitionInfo. Status %lx\n",
|
|
status
|
|
));
|
|
}
|
|
ClusDiskGetDiskGeometry( DeviceExtension->TargetDeviceObject );
|
|
continue;
|
|
} else {
|
|
|
|
// Return valid partition only for MBR disks. For any other
|
|
// type of partition, set an error which causes NULL to return
|
|
// from this function.
|
|
|
|
if ( PARTITION_STYLE_MBR == driveLayoutInfo->PartitionStyle ) {
|
|
DeviceExtension->Signature = driveLayoutInfo->Mbr.Signature;
|
|
} else {
|
|
ClusDiskPrint(( 1,
|
|
"[ClusDisk] GetPartitionInfo: skipping non-MBR disk \n" ));
|
|
CDLOG( "ClusDiskGetPartitionInfo: skipping non-MBR disk" );
|
|
status = STATUS_INVALID_DEVICE_REQUEST;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( NT_SUCCESS(status) ) {
|
|
return(driveLayoutInfo);
|
|
} else {
|
|
ExFreePool(driveLayoutInfo);
|
|
return(NULL);
|
|
}
|
|
|
|
} // ClusDiskGetPartitionInfo
|
|
|
|
|
|
|
|
BOOLEAN
|
|
AddAttachedDevice(
|
|
IN ULONG Signature,
|
|
IN PDEVICE_OBJECT DeviceObject
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Indicate that this device is now attached.
|
|
|
|
Arguments:
|
|
|
|
Signature - The signature for the device we just attached.
|
|
|
|
DeviceObject - The device object for the partition0 device object.
|
|
|
|
Return Value:
|
|
|
|
TRUE - Signature is already on the device list or has been added.
|
|
successfully.
|
|
|
|
FALSE - Signature was not on the device list and we failed to add it.
|
|
|
|
--*/
|
|
|
|
{
|
|
PDEVICE_LIST_ENTRY deviceEntry;
|
|
PCLUS_DEVICE_EXTENSION deviceExtension;
|
|
|
|
// 2000/02/05: stevedz - added synchronization.
|
|
|
|
ACQUIRE_EXCLUSIVE( &ClusDiskDeviceListLock );
|
|
|
|
deviceEntry = ClusDiskDeviceList;
|
|
|
|
while ( deviceEntry != NULL ) {
|
|
if ( Signature == deviceEntry->Signature ) {
|
|
if ( deviceEntry->Attached ) {
|
|
ClusDiskPrint((
|
|
1,
|
|
"[ClusDisk] Attaching to %lx more than once!\n",
|
|
Signature ));
|
|
}
|
|
if ( DeviceObject ) {
|
|
deviceExtension = DeviceObject->DeviceExtension;
|
|
ASSERT(deviceExtension->PhysicalDevice == DeviceObject);
|
|
deviceEntry->Attached = TRUE;
|
|
deviceEntry->DeviceObject = DeviceObject;
|
|
deviceExtension->AttachValid = TRUE;
|
|
deviceExtension->Signature = Signature;
|
|
}
|
|
RELEASE_EXCLUSIVE( &ClusDiskDeviceListLock );
|
|
return(TRUE);
|
|
}
|
|
deviceEntry = deviceEntry->Next;
|
|
}
|
|
|
|
deviceEntry = ExAllocatePool(
|
|
NonPagedPool,
|
|
sizeof(DEVICE_LIST_ENTRY) );
|
|
if ( deviceEntry == NULL ) {
|
|
ClusDiskPrint((1,
|
|
"[ClusDisk] Failed to allocate device entry structure for signature %08lX\n",
|
|
Signature));
|
|
RELEASE_EXCLUSIVE( &ClusDiskDeviceListLock );
|
|
return(FALSE);
|
|
}
|
|
|
|
RtlZeroMemory( deviceEntry, sizeof(DEVICE_LIST_ENTRY) );
|
|
|
|
deviceEntry->Signature = Signature;
|
|
deviceEntry->LettersAssigned = FALSE;
|
|
deviceEntry->DeviceObject = DeviceObject;
|
|
|
|
if ( DeviceObject == NULL ) {
|
|
deviceEntry->Attached = FALSE;
|
|
} else {
|
|
deviceEntry->Attached = TRUE;
|
|
}
|
|
|
|
//
|
|
// Link new entry into list.
|
|
//
|
|
deviceEntry->Next = ClusDiskDeviceList;
|
|
ClusDiskDeviceList = deviceEntry;
|
|
|
|
RELEASE_EXCLUSIVE( &ClusDiskDeviceListLock );
|
|
return(TRUE);
|
|
|
|
} // AddAttachedDevice
|
|
|
|
|
|
BOOLEAN
|
|
MatchDevice(
|
|
IN ULONG Signature,
|
|
OUT PDEVICE_OBJECT *DeviceObject
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Check to see if the Signature of the specified device is one
|
|
that we should control.
|
|
|
|
Arguments:
|
|
|
|
Signature - The signature for the device we are checking.
|
|
|
|
DeviceObject - Pointer to a return value for the device object.
|
|
|
|
Return Value:
|
|
|
|
TRUE - if this Signature is for a device we should control.
|
|
|
|
FALSE - if this Signature is NOT for a device we should control.
|
|
|
|
--*/
|
|
|
|
{
|
|
PDEVICE_LIST_ENTRY deviceEntry;
|
|
|
|
if ( SystemDiskSignature == Signature ) {
|
|
if ( ARGUMENT_PRESENT(DeviceObject) ) {
|
|
*DeviceObject = NULL;
|
|
}
|
|
return(FALSE);
|
|
}
|
|
|
|
// 2000/02/05: stevedz - added synchronization.
|
|
|
|
ACQUIRE_SHARED( &ClusDiskDeviceListLock );
|
|
|
|
deviceEntry = ClusDiskDeviceList;
|
|
|
|
while ( deviceEntry != NULL ) {
|
|
if ( Signature == deviceEntry->Signature ) {
|
|
if ( ARGUMENT_PRESENT(DeviceObject) ) {
|
|
*DeviceObject = deviceEntry->DeviceObject;
|
|
}
|
|
RELEASE_SHARED( &ClusDiskDeviceListLock );
|
|
return(TRUE);
|
|
}
|
|
deviceEntry = deviceEntry->Next;
|
|
}
|
|
|
|
if ( ARGUMENT_PRESENT(DeviceObject) ) {
|
|
*DeviceObject = NULL;
|
|
}
|
|
RELEASE_SHARED( &ClusDiskDeviceListLock );
|
|
return(FALSE);
|
|
|
|
} // MatchDevice
|
|
|
|
|
|
|
|
NTSTATUS
|
|
ClusDiskGetDiskGeometry(
|
|
PDEVICE_OBJECT DeviceObject
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Retry getting disk geometry.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - the target device object.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
NTSTATUS tmpStatus;
|
|
ULONG retryCount = 2;
|
|
SCSI_ADDRESS scsiAddress;
|
|
|
|
if ( DeviceObject->DeviceType == FILE_DEVICE_DISK_FILE_SYSTEM ) {
|
|
return(STATUS_INVALID_DEVICE_REQUEST);
|
|
}
|
|
|
|
do {
|
|
status = GetDiskGeometry( DeviceObject, NULL );
|
|
if ( status == STATUS_DATA_OVERRUN ) {
|
|
tmpStatus = GetScsiAddress( DeviceObject, &scsiAddress );
|
|
if ( NT_SUCCESS(tmpStatus) &&
|
|
(status != STATUS_DEVICE_BUSY) &&
|
|
(retryCount > 1) ) {
|
|
|
|
ClusDiskLogError( RootDeviceObject->DriverObject, // Use RootDeviceObject not DevObj
|
|
RootDeviceObject,
|
|
scsiAddress.PathId, // Sequence number
|
|
0, // Major function code
|
|
0, // Retry count
|
|
ID_GET_GEOMETRY, // Unique error
|
|
STATUS_SUCCESS,
|
|
CLUSDISK_RESET_BUS_REQUESTED,
|
|
0,
|
|
NULL );
|
|
ResetScsiDevice( NULL, &scsiAddress );
|
|
}
|
|
}
|
|
} while ( --retryCount &&
|
|
(status == STATUS_DATA_OVERRUN) );
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
ClusDiskPrint(( 1,
|
|
"[ClusDisk] Failed to read disk geometry, error %lx.\n",
|
|
status ));
|
|
}
|
|
|
|
return(status);
|
|
|
|
} // ClusDiskGetDiskGeometry
|
|
|
|
|
|
|
|
NTSTATUS
|
|
GetDiskGeometry(
|
|
PDEVICE_OBJECT DeviceObject,
|
|
PMEDIA_TYPE MediaType
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Get the disk geometry for a target device.
|
|
Returned data is thrown away.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - the target device object.
|
|
|
|
MediaType - Pointer to return device MediaType. Optional.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
|
|
{
|
|
PDISK_GEOMETRY diskGeometryBuffer;
|
|
NTSTATUS status;
|
|
PKEVENT event;
|
|
PIRP irp;
|
|
IO_STATUS_BLOCK ioStatusBlock;
|
|
|
|
if ( MediaType ) {
|
|
*MediaType = Unknown;
|
|
}
|
|
|
|
//
|
|
// Allocate DISK_GEOMETRY buffer from nonpaged pool.
|
|
//
|
|
|
|
diskGeometryBuffer = ExAllocatePool(NonPagedPoolCacheAligned,
|
|
sizeof(DISK_GEOMETRY));
|
|
|
|
if (!diskGeometryBuffer) {
|
|
return(STATUS_INSUFFICIENT_RESOURCES);
|
|
}
|
|
|
|
event = ExAllocatePool( NonPagedPool,
|
|
sizeof(KEVENT) );
|
|
if ( !event ) {
|
|
ExFreePool(diskGeometryBuffer);
|
|
return(STATUS_INSUFFICIENT_RESOURCES);
|
|
}
|
|
|
|
//
|
|
// Perform the get drive geometry synchronously.
|
|
//
|
|
|
|
KeInitializeEvent(event,
|
|
NotificationEvent,
|
|
FALSE);
|
|
|
|
irp = IoBuildDeviceIoControlRequest(IOCTL_DISK_GET_DRIVE_GEOMETRY,
|
|
DeviceObject,
|
|
NULL,
|
|
0,
|
|
diskGeometryBuffer,
|
|
sizeof(DISK_GEOMETRY),
|
|
FALSE,
|
|
event,
|
|
&ioStatusBlock);
|
|
|
|
if ( !irp ) {
|
|
ExFreePool(diskGeometryBuffer);
|
|
ExFreePool(event);
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
status = IoCallDriver(DeviceObject, irp);
|
|
|
|
if (status == STATUS_PENDING) {
|
|
KeWaitForSingleObject(event,
|
|
Suspended,
|
|
KernelMode,
|
|
FALSE,
|
|
NULL);
|
|
status = ioStatusBlock.Status;
|
|
}
|
|
|
|
ExFreePool(event);
|
|
|
|
//
|
|
// Return MediaType info if the caller requests.
|
|
//
|
|
|
|
if ( MediaType ) {
|
|
*MediaType = diskGeometryBuffer->MediaType;
|
|
}
|
|
|
|
//
|
|
// Deallocate buffer.
|
|
//
|
|
ExFreePool(diskGeometryBuffer);
|
|
|
|
return(status);
|
|
|
|
} // GetDiskGeometry
|
|
|
|
|
|
|
|
NTSTATUS
|
|
ReserveScsiDevice(
|
|
IN PCLUS_DEVICE_EXTENSION DeviceExtension,
|
|
IN PVOID Context
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Reserve a SCSI device using asynchronous I/O.
|
|
|
|
Arguments:
|
|
|
|
DeviceExtension - The device extension for the device to reserve.
|
|
|
|
Context - Optional pointer to ARB_RESERVE_COMPLETION struture.
|
|
If NULL, a new Reserve is being sent. If non-NULL,
|
|
a Reserve is being retried. In either case, a new
|
|
IRP is allocated.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
|
|
{
|
|
PIRP irp = NULL;
|
|
PIO_STACK_LOCATION irpSp;
|
|
PARB_RESERVE_COMPLETION arbContext = NULL;
|
|
|
|
NTSTATUS status;
|
|
|
|
CDLOGF( RESERVE,"ReserveScsiDevice(%p): Entry DiskNo %d Sig %08x Context %p",
|
|
DeviceExtension->DeviceObject,
|
|
DeviceExtension->DiskNumber,
|
|
DeviceExtension->Signature,
|
|
Context );
|
|
|
|
//
|
|
// Acquire remove lock for this device. If the IRP is sent, it will be
|
|
// released in the completion routine.
|
|
//
|
|
|
|
status = AcquireRemoveLock( &DeviceExtension->RemoveLock, ReserveScsiDevice );
|
|
if ( !NT_SUCCESS(status) ) {
|
|
goto FnExit;
|
|
}
|
|
|
|
//
|
|
// If context is non-null, then we are retrying this I/O. If null,
|
|
// we need to allocate a context structure for the write.
|
|
//
|
|
|
|
if ( Context ) {
|
|
|
|
ClusDiskPrint(( 1,
|
|
"[ClusDisk] Retry Reserve IRP %p for DO %p DiskNo %u Sig %08X \n",
|
|
irp,
|
|
DeviceExtension->DeviceObject,
|
|
DeviceExtension->DiskNumber,
|
|
DeviceExtension->Signature ));
|
|
|
|
arbContext = Context;
|
|
arbContext->IoEndTime.QuadPart = (ULONGLONG) 0;
|
|
|
|
} else {
|
|
|
|
arbContext = ExAllocatePool( NonPagedPool, sizeof(ARB_RESERVE_COMPLETION) );
|
|
|
|
if ( !arbContext ) {
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
ReleaseRemoveLock( &DeviceExtension->RemoveLock, ReserveScsiDevice );
|
|
goto FnExit;
|
|
}
|
|
|
|
RtlZeroMemory( arbContext, sizeof(ARB_RESERVE_COMPLETION) );
|
|
|
|
arbContext->RetriesLeft = 1;
|
|
arbContext->LockTag = ReserveScsiDevice;
|
|
arbContext->DeviceObject = DeviceExtension->DeviceObject;
|
|
arbContext->DeviceExtension = DeviceExtension;
|
|
arbContext->Type = ArbIoReserve;
|
|
arbContext->FailureRoutine = HandleReserveFailure;
|
|
arbContext->RetryRoutine = ReserveScsiDevice;
|
|
arbContext->PostCompletionRoutine = CheckReserveTiming;
|
|
}
|
|
|
|
KeQuerySystemTime( &arbContext->IoStartTime );
|
|
|
|
//
|
|
// Begin by allocating the IRP for this request. Do not charge quota to
|
|
// the current process for this IRP.
|
|
//
|
|
|
|
irp = IoAllocateIrp( DeviceExtension->TargetDeviceObject->StackSize, FALSE );
|
|
if (!irp) {
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
ExFreePool( arbContext );
|
|
ReleaseRemoveLock( &DeviceExtension->RemoveLock, ReserveScsiDevice );
|
|
goto FnExit;
|
|
}
|
|
|
|
irp->Tail.Overlay.Thread = PsGetCurrentThread();
|
|
|
|
//
|
|
// Get a pointer to the stack location of the first driver which will be
|
|
// invoked. This is where the function codes and the parameters are set.
|
|
//
|
|
|
|
irpSp = IoGetNextIrpStackLocation( irp );
|
|
|
|
irpSp->MajorFunction = IRP_MJ_DEVICE_CONTROL;
|
|
|
|
irpSp->Parameters.DeviceIoControl.OutputBufferLength = 0;
|
|
irpSp->Parameters.DeviceIoControl.InputBufferLength = 0;
|
|
irpSp->Parameters.DeviceIoControl.IoControlCode = IOCTL_STORAGE_RESERVE;
|
|
irpSp->Parameters.DeviceIoControl.Type3InputBuffer = NULL;
|
|
|
|
IoSetCompletionRoutine( irp,
|
|
ArbReserveCompletion,
|
|
arbContext,
|
|
TRUE,
|
|
TRUE,
|
|
TRUE );
|
|
|
|
InterlockedIncrement( &DeviceExtension->ReserveCount );
|
|
|
|
ClusDiskPrint(( 4,
|
|
"[ClusDisk] Reserve IRP %p for DO %p DiskNo %u Sig %08X \n",
|
|
irp,
|
|
DeviceExtension->DeviceObject,
|
|
DeviceExtension->DiskNumber,
|
|
DeviceExtension->Signature ));
|
|
|
|
status = IoCallDriver( DeviceExtension->TargetDeviceObject,
|
|
irp );
|
|
|
|
//
|
|
// If pending is returned, we need to return success so the caller
|
|
// will do the right thing.
|
|
//
|
|
|
|
if ( STATUS_PENDING == status ) {
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
|
|
FnExit:
|
|
|
|
CDLOGF(RESERVE,"ReserveScsiDevice(%p): Exit => %!status!",
|
|
DeviceExtension->DeviceObject,
|
|
status );
|
|
|
|
return status;
|
|
|
|
} // ReserveScsiDevice
|
|
|
|
|
|
NTSTATUS
|
|
CheckReserveTiming(
|
|
IN PCLUS_DEVICE_EXTENSION DeviceExtension,
|
|
IN PVOID Context
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Routine called when reserve completes successfully.
|
|
|
|
Gets the current time, logs some info to tracing file, and
|
|
saves the reserve completion time in device extension.
|
|
|
|
Arguments:
|
|
|
|
DeviceExtension - The device extension for the device to reserve.
|
|
|
|
Context - Pointer to ARB_RESERVE_COMPLETION struture.
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS
|
|
|
|
--*/
|
|
{
|
|
PARB_RESERVE_COMPLETION arbContext = Context;
|
|
|
|
KIRQL irql;
|
|
PLIST_ENTRY listEntry;
|
|
PIRP irp;
|
|
LARGE_INTEGER currentTime;
|
|
LARGE_INTEGER timeDelta;
|
|
|
|
KeQuerySystemTime( ¤tTime );
|
|
|
|
timeDelta.QuadPart = ( currentTime.QuadPart - arbContext->IoStartTime.QuadPart );
|
|
if (timeDelta.QuadPart > 500 * 10000) {
|
|
timeDelta.QuadPart /= 10000;
|
|
ClusDiskPrint(( 1,
|
|
"[ClusDisk] DiskNo %u Sig %08X, %u ms spent in ReserveScsiDevice\n",
|
|
DeviceExtension->DiskNumber,
|
|
DeviceExtension->Signature,
|
|
timeDelta.LowPart ));
|
|
CDLOGF( RESERVE, "ClusDiskReservationWorker: LongTimeInThisReserve DevObj %p DiskNo %u timeDelta %d ms",
|
|
DeviceExtension->DeviceObject,
|
|
DeviceExtension->DiskNumber,
|
|
timeDelta.LowPart );
|
|
}
|
|
timeDelta.QuadPart = ( currentTime.QuadPart - DeviceExtension->LastReserveStart.QuadPart );
|
|
if (timeDelta.QuadPart > 3500 * 10000) {
|
|
timeDelta.QuadPart /= 10000;
|
|
ClusDiskPrint(( 1,
|
|
"[ClusDisk] DiskNo %u Sig %08X, %u ms since last reserve\n",
|
|
DeviceExtension->DiskNumber,
|
|
DeviceExtension->Signature,
|
|
timeDelta.LowPart ));
|
|
CDLOGF( RESERVE, "ClusDiskReservationWorker: LongTimeBetweenReserves DevObj %p DiskNo %u timeDelta %d ms",
|
|
DeviceExtension->DeviceObject,
|
|
DeviceExtension->DiskNumber,
|
|
timeDelta.LowPart );
|
|
}
|
|
|
|
ACQUIRE_EXCLUSIVE( &DeviceExtension->ReserveInfoLock );
|
|
KeQuerySystemTime( &DeviceExtension->LastReserveEnd );
|
|
RELEASE_EXCLUSIVE( &DeviceExtension->ReserveInfoLock );
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
} // CheckReserveTiming
|
|
|
|
|
|
NTSTATUS
|
|
HandleReserveFailure(
|
|
IN PCLUS_DEVICE_EXTENSION DeviceExtension,
|
|
IN PVOID Context
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Routine called when reserve fails. Another routine handled
|
|
retrying the reserve if it qualified for a retry.
|
|
|
|
Save the failure status in the device extension.
|
|
Complete any pending I/O requests so cluster software knows
|
|
the reservation was lost.
|
|
|
|
Arguments:
|
|
|
|
DeviceExtension - The device extension for the device to reserve.
|
|
|
|
Context - Pointer to ARB_RESERVE_COMPLETION struture.
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS
|
|
|
|
--*/
|
|
{
|
|
PARB_RESERVE_COMPLETION arbContext = Context;
|
|
PIRP irp;
|
|
PLIST_ENTRY listEntry;
|
|
|
|
KIRQL irql;
|
|
|
|
ClusDiskPrint(( 1,
|
|
"[ClusDisk] Lost reservation for DiskNo %u Sig %08lx, status %lx.\n",
|
|
DeviceExtension->DiskNumber,
|
|
DeviceExtension->Signature,
|
|
arbContext->FinalStatus ));
|
|
|
|
CDLOGF( RESERVE, "ClusDiskReservationWorker: LostReserve DO %p DiskNo %u status %!status!",
|
|
DeviceExtension->DeviceObject,
|
|
DeviceExtension->DiskNumber,
|
|
arbContext->FinalStatus );
|
|
|
|
IoAcquireCancelSpinLock( &irql );
|
|
KeAcquireSpinLockAtDpcLevel(&ClusDiskSpinLock);
|
|
DeviceExtension->ReserveTimer = 0;
|
|
DeviceExtension->ReserveFailure = arbContext->FinalStatus;
|
|
|
|
//
|
|
// Signal all waiting Irp's
|
|
//
|
|
while ( !IsListEmpty( &DeviceExtension->WaitingIoctls ) ) {
|
|
listEntry = RemoveHeadList( &DeviceExtension->WaitingIoctls );
|
|
irp = CONTAINING_RECORD( listEntry,
|
|
IRP,
|
|
Tail.Overlay.ListEntry );
|
|
ClusDiskCompletePendingRequest(irp, arbContext->FinalStatus, DeviceExtension);
|
|
}
|
|
|
|
KeReleaseSpinLockFromDpcLevel(&ClusDiskSpinLock);
|
|
IoReleaseCancelSpinLock( irql );
|
|
|
|
return ( arbContext->FinalStatus );
|
|
|
|
} // HandleReserveFailure
|
|
|
|
|
|
|
|
VOID
|
|
ReleaseScsiDevice(
|
|
IN PCLUS_DEVICE_EXTENSION DeviceExtension
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Release a SCSI device (release from reservation).
|
|
|
|
Arguments:
|
|
|
|
DeviceExtension - the device extension of the device to release.
|
|
This must be the physical device (partition0).
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PIRP irp;
|
|
SCSI_PASS_THROUGH spt;
|
|
PKEVENT event;
|
|
IO_STATUS_BLOCK ioStatusBlock;
|
|
NTSTATUS status = STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
CDLOG( "ReleaseScsiDevice(%p): Entry DiskNo %d Sig %08x",
|
|
DeviceExtension->DeviceObject,
|
|
DeviceExtension->DiskNumber,
|
|
DeviceExtension->Signature );
|
|
|
|
ClusDiskPrint((3,
|
|
"[ClusDisk] Release disk number %u (sig: %08X), disk state %s \n",
|
|
DeviceExtension->DiskNumber,
|
|
DeviceExtension->Signature,
|
|
DiskStateToString( DeviceExtension->DiskState ) ));
|
|
|
|
event = ExAllocatePool( NonPagedPool,
|
|
sizeof(KEVENT) );
|
|
if ( !event ) {
|
|
return;
|
|
}
|
|
|
|
irp = IoBuildDeviceIoControlRequest(IOCTL_STORAGE_RELEASE,
|
|
DeviceExtension->TargetDeviceObject,
|
|
&spt,
|
|
sizeof(SCSI_PASS_THROUGH),
|
|
&spt,
|
|
sizeof(SCSI_PASS_THROUGH),
|
|
FALSE,
|
|
event,
|
|
&ioStatusBlock);
|
|
|
|
if (!irp) {
|
|
ExFreePool(event);
|
|
ClusDiskPrint((
|
|
1,
|
|
"[ClusDisk] Failed to Init IRP to perform a release.\n"
|
|
));
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Before release, mark disk as offline.
|
|
//
|
|
|
|
ASSERT( DeviceExtension == DeviceExtension->PhysicalDevice->DeviceExtension );
|
|
|
|
// stevedz - disable this assertion for now.
|
|
// ASSERT( DiskOffline == DeviceExtension->DiskState );
|
|
|
|
// Disk should already be offline. Only mark it offline.
|
|
DeviceExtension->DiskState = DiskOffline;
|
|
// OFFLINE_DISK( DeviceExtension );
|
|
|
|
ClusDiskPrint(( 3,
|
|
"[ClusDisk] Release %p, marking disk offline \n",
|
|
DeviceExtension->PhysicalDevice
|
|
));
|
|
|
|
//
|
|
// Set the event object to the unsignaled state.
|
|
// It will be used to signal request completion.
|
|
//
|
|
|
|
KeInitializeEvent(event,
|
|
NotificationEvent,
|
|
FALSE);
|
|
|
|
status = IoCallDriver(DeviceExtension->TargetDeviceObject,
|
|
irp);
|
|
|
|
if (status == STATUS_PENDING) {
|
|
|
|
KeWaitForSingleObject(event,
|
|
Suspended,
|
|
KernelMode,
|
|
FALSE,
|
|
NULL);
|
|
|
|
status = ioStatusBlock.Status;
|
|
}
|
|
|
|
ExFreePool(event);
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
ClusDiskPrint((
|
|
1,
|
|
"[ClusDisk] Failed to perform release. Status %lx\n",
|
|
status
|
|
));
|
|
}
|
|
|
|
CDLOG( "ReleaseScsiDevice(%p): Exit => %!status!",
|
|
DeviceExtension->DeviceObject,
|
|
status );
|
|
} // ReleaseScsiDevice
|
|
|
|
|
|
|
|
NTSTATUS
|
|
ResetScsiDevice(
|
|
IN HANDLE ScsiportHandle,
|
|
IN PSCSI_ADDRESS ScsiAddress
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Break the reservation for the device specified in SCSI address.
|
|
|
|
If the bus/device is reset, what a few seconds before returning.
|
|
|
|
Arguments:
|
|
|
|
ScsiportHandle - If specified, will use this handle to send break reservation
|
|
IOCTL. Caller is responsible for closing this handle.
|
|
If not specified, scsiport device will be opened based on the
|
|
SCSI address information port number.
|
|
|
|
ScsiAddress - Pointer to SCSI_ADDRESS structure which is for the target device.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
{
|
|
HANDLE scsiHandle = NULL;
|
|
HANDLE eventHandle = NULL;
|
|
NTSTATUS status;
|
|
IO_STATUS_BLOCK ioStatusBlock;
|
|
UNICODE_STRING portDevice;
|
|
OBJECT_ATTRIBUTES objectAttributes;
|
|
WCHAR portDeviceBuffer[64];
|
|
|
|
CDLOG( "BreakReserve: Entry Scsiport fh %p, Port %d Path %d TID %d LUN %d ",
|
|
ScsiportHandle,
|
|
ScsiAddress->PortNumber,
|
|
ScsiAddress->PathId,
|
|
ScsiAddress->TargetId,
|
|
ScsiAddress->Lun );
|
|
|
|
//
|
|
// Create event for notification.
|
|
//
|
|
status = ZwCreateEvent( &eventHandle,
|
|
EVENT_ALL_ACCESS,
|
|
NULL,
|
|
SynchronizationEvent,
|
|
FALSE );
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
ClusDiskPrint(( 1,
|
|
"[ClusDisk] BreakReserve: Failed to create event, status %08X\n",
|
|
status ));
|
|
goto FnExit;
|
|
}
|
|
|
|
//
|
|
// If caller specified a scsiport handle, use it. Otherwise
|
|
// we have to open scsiport to send the break reservation IOCTL.
|
|
//
|
|
|
|
if ( ScsiportHandle ) {
|
|
scsiHandle = ScsiportHandle;
|
|
|
|
} else {
|
|
|
|
//
|
|
// Open the scsiport device to send the break reservation.
|
|
//
|
|
|
|
(VOID) StringCchPrintfW( portDeviceBuffer,
|
|
RTL_NUMBER_OF(portDeviceBuffer),
|
|
L"\\Device\\ScsiPort%d",
|
|
ScsiAddress->PortNumber );
|
|
|
|
WCSLEN_ASSERT( portDeviceBuffer );
|
|
|
|
RtlInitUnicodeString( &portDevice, portDeviceBuffer );
|
|
|
|
InitializeObjectAttributes( &objectAttributes,
|
|
&portDevice,
|
|
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
|
|
NULL,
|
|
NULL );
|
|
|
|
status = ZwOpenFile( &scsiHandle,
|
|
FILE_ALL_ACCESS,
|
|
&objectAttributes,
|
|
&ioStatusBlock,
|
|
0,
|
|
FILE_NON_DIRECTORY_FILE );
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
ClusDiskPrint(( 1,
|
|
"[ClusDisk] BreakReserve: failed to open file %wZ. Error %08X.\n",
|
|
&portDevice, status ));
|
|
goto FnExit;
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Break reservation IOCTL uses the SCSI address to determine which
|
|
// device to reset.
|
|
//
|
|
|
|
status = ZwDeviceIoControlFile( scsiHandle,
|
|
eventHandle,
|
|
NULL,
|
|
NULL,
|
|
&ioStatusBlock,
|
|
IOCTL_STORAGE_BREAK_RESERVATION,
|
|
ScsiAddress,
|
|
sizeof(SCSI_ADDRESS),
|
|
NULL,
|
|
0 );
|
|
|
|
if ( STATUS_PENDING == status ) {
|
|
status = ZwWaitForSingleObject(eventHandle,
|
|
FALSE,
|
|
NULL);
|
|
ASSERT( NT_SUCCESS(status) );
|
|
status = ioStatusBlock.Status;
|
|
}
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
ClusDiskPrint(( 1,
|
|
"[ClusDisk] BreakReserve: Failed to reset device, error %08X.\n",
|
|
status ));
|
|
}
|
|
|
|
FnExit:
|
|
|
|
//
|
|
// Close the handle only if the caller didn't pass it in.
|
|
//
|
|
|
|
if ( !ScsiportHandle && scsiHandle ) {
|
|
ZwClose( scsiHandle );
|
|
}
|
|
|
|
if ( eventHandle ) {
|
|
ZwClose( eventHandle );
|
|
}
|
|
|
|
CDLOG( "BreakReserve: Exit Scsiport fh %p, Port %d Path %d TID %d LUN %d status %!status!",
|
|
ScsiportHandle,
|
|
ScsiAddress->PortNumber,
|
|
ScsiAddress->PathId,
|
|
ScsiAddress->TargetId,
|
|
ScsiAddress->Lun,
|
|
status );
|
|
|
|
return status;
|
|
|
|
} // ResetScsiDevice
|
|
|
|
|
|
#if 0
|
|
|
|
NTSTATUS
|
|
LockVolumes(
|
|
IN PCLUS_DEVICE_EXTENSION DeviceExtension
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Lock all volumes for this Disk.
|
|
|
|
Arguments:
|
|
|
|
DiskNumber - the disk number for the disk to lock volumes.
|
|
|
|
Return Value:
|
|
|
|
NT Status for request.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
OBJECT_ATTRIBUTES objectAttributes;
|
|
UNICODE_STRING ntUnicodeString;
|
|
HANDLE fileHandle;
|
|
WCHAR deviceNameBuffer[MAX_PARTITION_NAME_LENGTH];
|
|
IO_STATUS_BLOCK ioStatusBlock;
|
|
PDRIVE_LAYOUT_INFORMATION_EX driveLayoutInfo;
|
|
PPARTITION_INFORMATION_EX partitionInfo;
|
|
ULONG partIndex;
|
|
HANDLE eventHandle;
|
|
PFILE_OBJECT fileObject;
|
|
PDEVICE_OBJECT deviceObject;
|
|
PKEVENT event;
|
|
PIRP irp;
|
|
|
|
//
|
|
// Allocate an event structure
|
|
//
|
|
event = ExAllocatePool(
|
|
NonPagedPool,
|
|
sizeof(KEVENT)
|
|
);
|
|
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
if (!event) {
|
|
return(status);
|
|
}
|
|
|
|
driveLayoutInfo = ClusDiskGetPartitionInfo( DeviceExtension );
|
|
|
|
if ( driveLayoutInfo != NULL ) {
|
|
//
|
|
// Create event for notification.
|
|
//
|
|
status = ZwCreateEvent( &eventHandle,
|
|
EVENT_ALL_ACCESS,
|
|
NULL,
|
|
SynchronizationEvent,
|
|
FALSE );
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
ExFreePool( driveLayoutInfo );
|
|
ExFreePool( event );
|
|
return(status);
|
|
}
|
|
|
|
for ( partIndex = 0;
|
|
partIndex < driveLayoutInfo->PartitionCount;
|
|
partIndex++ )
|
|
{
|
|
|
|
partitionInfo = &driveLayoutInfo->PartitionEntry[partIndex];
|
|
|
|
//
|
|
// First make sure this is a valid partition.
|
|
//
|
|
if ( !partitionInfo->Mbr.RecognizedPartition ||
|
|
partitionInfo->PartitionNumber == 0 ) {
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Create the device name for the device.
|
|
//
|
|
|
|
if ( FAILED( StringCchPrintfW( deviceNameBuffer,
|
|
RTL_NUMBER_OF(deviceNameBuffer),
|
|
DEVICE_PARTITION_NAME,
|
|
DeviceExtension->DiskNumber,
|
|
partitionInfo->PartitionNumber ) ) ) {
|
|
continue;
|
|
}
|
|
WCSLEN_ASSERT( deviceNameBuffer );
|
|
|
|
//
|
|
// Get Unicode name
|
|
//
|
|
RtlInitUnicodeString( &ntUnicodeString, deviceNameBuffer );
|
|
|
|
//
|
|
// Try to open this device to perform a dismount
|
|
//
|
|
InitializeObjectAttributes( &objectAttributes,
|
|
&ntUnicodeString,
|
|
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
|
|
NULL,
|
|
NULL );
|
|
|
|
ClusDiskPrint((
|
|
3,
|
|
"[ClusDisk] Locking Partition %ws.\n",
|
|
deviceNameBuffer ));
|
|
|
|
status = ZwCreateFile( &fileHandle,
|
|
SYNCHRONIZE | FILE_READ_DATA | FILE_WRITE_DATA,
|
|
&objectAttributes,
|
|
&ioStatusBlock,
|
|
NULL,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
FILE_OPEN,
|
|
FILE_SYNCHRONOUS_IO_NONALERT,
|
|
NULL,
|
|
0 );
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
ClusDiskPrint((
|
|
1,
|
|
"[ClusDisk] LockVolumes failed to open file %wZ. Error %lx.\n",
|
|
&ntUnicodeString,
|
|
status ));
|
|
continue;
|
|
}
|
|
|
|
status = ZwClearEvent( eventHandle );
|
|
|
|
#if 0
|
|
status = ObReferenceObjectByHandle(
|
|
fileHandle,
|
|
FILE_WRITE_DATA,
|
|
*IoFileObjectType,
|
|
KernelMode,
|
|
&fileObject,
|
|
NULL );
|
|
if ( !NT_SUCCESS(status) ) {
|
|
ClusDiskPrint((
|
|
1,
|
|
"[ClusDisk] LockVolumes: failed to reference object %ws, status %lx\n",
|
|
deviceNameBuffer,
|
|
status ));
|
|
continue;
|
|
}
|
|
deviceObject = IoGetRelatedDeviceObject( fileObject );
|
|
ObDereferenceObject( fileObject );
|
|
ClusDiskPrint((
|
|
1,
|
|
"[ClusDisk] LockVolumes - found file/device object %p \n",
|
|
deviceObject ));
|
|
if ( !deviceObject ) {
|
|
continue;
|
|
}
|
|
|
|
KeInitializeEvent(event,
|
|
NotificationEvent,
|
|
FALSE);
|
|
|
|
|
|
irp = IoBuildSynchronousFsdRequest(
|
|
IRP_MJ_FLUSH_BUFFERS,
|
|
deviceObject,
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
event,
|
|
&ioStatusBlock );
|
|
if (!irp) {
|
|
continue;
|
|
}
|
|
|
|
if (IoCallDriver( deviceObject,
|
|
irp ) == STATUS_PENDING) {
|
|
|
|
KeWaitForSingleObject(
|
|
event,
|
|
Suspended,
|
|
KernelMode,
|
|
FALSE,
|
|
NULL
|
|
);
|
|
}
|
|
|
|
#else
|
|
status = ZwFsControlFile(
|
|
fileHandle,
|
|
eventHandle, // Event Handle
|
|
NULL, // APC Routine
|
|
NULL, // APC Context
|
|
&ioStatusBlock,
|
|
FSCTL_LOCK_VOLUME,
|
|
NULL, // InputBuffer
|
|
0, // InputBufferLength
|
|
NULL, // OutputBuffer
|
|
0 // OutputBufferLength
|
|
);
|
|
if ( status == STATUS_PENDING ) {
|
|
status = ZwWaitForSingleObject(eventHandle,
|
|
FALSE,
|
|
NULL);
|
|
ASSERT( NT_SUCCESS(status) );
|
|
status = ioStatusBlock.Status;
|
|
}
|
|
#endif
|
|
if ( !NT_SUCCESS(status) ) {
|
|
ClusDiskPrint((
|
|
1,
|
|
"[ClusDisk] Failed to flush buffers for %wZ. Error %lx.\n",
|
|
&ntUnicodeString,
|
|
status ));
|
|
} else {
|
|
ClusDiskPrint((
|
|
3,
|
|
"[ClusDisk] Flushed buffers for %wZ.\n",
|
|
&ntUnicodeString ));
|
|
}
|
|
|
|
ZwClose( fileHandle );
|
|
|
|
} // for
|
|
|
|
ExFreePool( driveLayoutInfo );
|
|
ZwClose( eventHandle );
|
|
|
|
} // if
|
|
|
|
ExFreePool( event );
|
|
|
|
return(status);
|
|
|
|
} // LockVolumes
|
|
#endif
|
|
|
|
|
|
NTSTATUS
|
|
IsVolumeMounted(
|
|
IN ULONG DiskNumber,
|
|
IN ULONG PartNumber,
|
|
OUT BOOLEAN *IsMounted
|
|
)
|
|
{
|
|
HANDLE fileHandle = NULL;
|
|
NTSTATUS status;
|
|
FILE_FS_DEVICE_INFORMATION deviceInfo;
|
|
IO_STATUS_BLOCK ioStatusBlock;
|
|
|
|
if ( PASSIVE_LEVEL != KeGetCurrentIrql() ) {
|
|
status = STATUS_UNSUCCESSFUL;
|
|
goto FnExit;
|
|
}
|
|
|
|
//
|
|
// Open the device so we can query if a volume is mounted without
|
|
// causing it to be mounted. Note that we can only specify
|
|
// FILE_READ_ATTRIBUTES | SYNCHRONIZE or a mount will occur.
|
|
//
|
|
|
|
status = ClusDiskCreateHandle( &fileHandle,
|
|
DiskNumber,
|
|
PartNumber,
|
|
FILE_READ_ATTRIBUTES | SYNCHRONIZE );
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
goto FnExit;
|
|
}
|
|
|
|
//
|
|
// Make the device info query.
|
|
//
|
|
|
|
status = ZwQueryVolumeInformationFile( fileHandle,
|
|
&ioStatusBlock,
|
|
&deviceInfo,
|
|
sizeof(deviceInfo),
|
|
FileFsDeviceInformation );
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
goto FnExit;
|
|
}
|
|
|
|
*IsMounted = (deviceInfo.Characteristics & FILE_DEVICE_IS_MOUNTED) ? TRUE : FALSE;
|
|
|
|
FnExit:
|
|
|
|
if ( fileHandle ) {
|
|
ZwClose( fileHandle );
|
|
}
|
|
|
|
return status;
|
|
|
|
} // IsVolumeMounted
|
|
|
|
|
|
|
|
NTSTATUS
|
|
DismountPartition(
|
|
IN PDEVICE_OBJECT TargetDevice,
|
|
IN ULONG DiskNumber,
|
|
IN ULONG PartNumber
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Dismount the device, given the disk and partition numbers
|
|
|
|
Arguments:
|
|
|
|
TargetDevice - the target device to send volume IOCTLs to.
|
|
|
|
DiskNumber - the disk number for the disk to dismount
|
|
|
|
PartNumber - the parition number for the disk to dismount
|
|
|
|
Return Value:
|
|
|
|
NT Status for request.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
HANDLE fileHandle = NULL;
|
|
KIRQL irql;
|
|
BOOLEAN isMounted;
|
|
|
|
CDLOG( "DismountPartition: Entry DiskNo %d PartNo %d", DiskNumber, PartNumber );
|
|
|
|
ClusDiskPrint(( 3,
|
|
"[ClusDisk] DismountPartition: Dismounting disk %d partition %d \n",
|
|
DiskNumber,
|
|
PartNumber ));
|
|
|
|
status = IsVolumeMounted( DiskNumber, PartNumber, &isMounted );
|
|
if ( NT_SUCCESS(status) ) {
|
|
ClusDiskPrint(( 1,
|
|
"[ClusDisk] DismountPartition: Volume is mounted returns: %s \n",
|
|
isMounted ? "TRUE" : "FALSE" ));
|
|
|
|
if ( FALSE == isMounted) {
|
|
|
|
// Volume not mounted, we are done. Return success.
|
|
|
|
status = STATUS_SUCCESS;
|
|
goto FnExit;
|
|
}
|
|
}
|
|
|
|
irql = KeGetCurrentIrql();
|
|
if ( PASSIVE_LEVEL != irql ) {
|
|
ClusDiskPrint(( 1,
|
|
"[ClusDisk] DismountPartition: Invalid IRQL %d \n", irql ));
|
|
CDLOG( "DismountPartition: Invalid IRQL %d ", irql );
|
|
ASSERT( FALSE );
|
|
status = STATUS_UNSUCCESSFUL;
|
|
goto FnExit;
|
|
}
|
|
|
|
#if DBG
|
|
//
|
|
// Find out if FTDISK thinks this disk is currently online or offline.
|
|
// If offline, success is returned.
|
|
// If online, c0000001 STATUS_UNSUCCESSFUL is returned.
|
|
//
|
|
|
|
status = SendFtdiskIoctlSync( TargetDevice,
|
|
DiskNumber,
|
|
PartNumber,
|
|
IOCTL_VOLUME_IS_OFFLINE );
|
|
|
|
|
|
if ( NT_SUCCESS(status) ) {
|
|
ClusDiskPrint(( 3,
|
|
"[ClusDisk] DismountPartition: IOCTL_VOLUME_IS_OFFLINE indicates volume is offline. \n" ));
|
|
CDLOG( "DismountPartition: IOCTL_VOLUME_IS_OFFLINE indicates volume is offline" );
|
|
|
|
} else {
|
|
ClusDiskPrint(( 3,
|
|
"[ClusDisk] DismountPartition: IOCTL_VOLUME_IS_OFFLINE returns %08X \n",
|
|
status ));
|
|
CDLOG( "DismountPartition: IOCTL_VOLUME_IS_OFFLINE returns %08X", status );
|
|
}
|
|
#endif
|
|
|
|
// If the disk is offline:
|
|
// if access is: FILE_READ_ATTRIBUTES | SYNCHRONIZE
|
|
//
|
|
// - DismountDevice fails with: c0000010 STATUS_INVALID_DEVICE_REQUEST
|
|
//
|
|
// if access is: FILE_READ_DATA | SYNCHRONIZE
|
|
//
|
|
// - ClusDiskCreateHandle fails with: C000000E STATUS_NO_SUCH_DEVICE
|
|
//
|
|
// if access is: FILE_WRITE_ATTRIBUTES | SYNCHRONIZE
|
|
//
|
|
// - DismountDevice works!
|
|
|
|
status = ClusDiskCreateHandle( &fileHandle,
|
|
DiskNumber,
|
|
PartNumber,
|
|
FILE_WRITE_ATTRIBUTES | SYNCHRONIZE );
|
|
|
|
//
|
|
// Check status of getting the device handle.
|
|
//
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
ClusDiskPrint(( 1,
|
|
"[ClusDisk] DismountPartition: Unable to get device handle \n" ));
|
|
CDLOG( "DismountPartition: Unable to get device handle" );
|
|
goto FnExit;
|
|
}
|
|
|
|
status = DismountDevice( fileHandle );
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
ClusDiskPrint(( 1,
|
|
"[ClusDisk] Failed to dismount disk %d partition %d. Error %08X.\n",
|
|
DiskNumber,
|
|
PartNumber,
|
|
status ));
|
|
} else {
|
|
ClusDiskPrint(( 3,
|
|
"[ClusDisk] Dismounted disk %d partition %d \n",
|
|
DiskNumber,
|
|
PartNumber ));
|
|
}
|
|
|
|
FnExit:
|
|
|
|
if ( fileHandle ) {
|
|
ZwClose( fileHandle );
|
|
}
|
|
|
|
CDLOG( "DismountPartition: Exit DiskNo %d PartNo %d => %!status!",
|
|
DiskNumber,
|
|
PartNumber,
|
|
status );
|
|
|
|
ClusDiskPrint(( 3,
|
|
"[ClusDisk] DismountPartition: Dismounting disk %d partition %d status %08X \n",
|
|
DiskNumber,
|
|
PartNumber,
|
|
status ));
|
|
|
|
return(status);
|
|
|
|
} // DismountPartition
|
|
|
|
|
|
|
|
NTSTATUS
|
|
DismountDevice(
|
|
IN HANDLE FileHandle
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Dismounts a device.
|
|
|
|
Arguments:
|
|
|
|
FileHandle - file handle to use for performing dismount.
|
|
|
|
Return Value:
|
|
|
|
NT Status for request.
|
|
|
|
--*/
|
|
|
|
{
|
|
IO_STATUS_BLOCK ioStatusBlock;
|
|
NTSTATUS status;
|
|
HANDLE eventHandle;
|
|
|
|
CDLOG( "DismountDevice: Entry handle %p", FileHandle );
|
|
|
|
ClusDiskPrint(( 3,
|
|
"[ClusDisk] DismountDevice: Entry handle %p \n",
|
|
FileHandle ));
|
|
|
|
//
|
|
// Create event for notification.
|
|
//
|
|
status = ZwCreateEvent( &eventHandle,
|
|
EVENT_ALL_ACCESS,
|
|
NULL,
|
|
SynchronizationEvent,
|
|
FALSE );
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
CDLOG( "DismountDevice: Failed to create event" );
|
|
goto FnExit;
|
|
}
|
|
|
|
//
|
|
// Lock first.
|
|
//
|
|
// If raw FS mounted, dismount will fail if lock was not done first.
|
|
// By doing the lock, we insure we don't see dismount failures on
|
|
// disks with raw mounted.
|
|
//
|
|
|
|
CDLOG( "DismountDevice: FSCTL_LOCK_VOLUME called" );
|
|
|
|
status = ZwFsControlFile(
|
|
FileHandle,
|
|
eventHandle, // Event Handle
|
|
NULL, // APC Routine
|
|
NULL, // APC Context
|
|
&ioStatusBlock,
|
|
FSCTL_LOCK_VOLUME,
|
|
NULL, // InputBuffer
|
|
0, // InputBufferLength
|
|
NULL, // OutputBuffer
|
|
0 // OutputBufferLength
|
|
);
|
|
if ( status == STATUS_PENDING ) {
|
|
status = ZwWaitForSingleObject(eventHandle,
|
|
FALSE,
|
|
NULL);
|
|
ASSERT( NT_SUCCESS(status) );
|
|
status = ioStatusBlock.Status;
|
|
}
|
|
|
|
//
|
|
// Now dismount. We don't care if the lock failed.
|
|
//
|
|
|
|
CDLOG( "DismountDevice: FSCTL_DISMOUNT_VOLUME called" );
|
|
|
|
status = ZwFsControlFile(
|
|
FileHandle,
|
|
eventHandle, // Event Handle
|
|
NULL, // APC Routine
|
|
NULL, // APC Context
|
|
&ioStatusBlock,
|
|
FSCTL_DISMOUNT_VOLUME,
|
|
NULL, // InputBuffer
|
|
0, // InputBufferLength
|
|
NULL, // OutputBuffer
|
|
0 // OutputBufferLength
|
|
);
|
|
if ( status == STATUS_PENDING ) {
|
|
status = ZwWaitForSingleObject(eventHandle,
|
|
FALSE,
|
|
NULL);
|
|
ASSERT( NT_SUCCESS(status) );
|
|
status = ioStatusBlock.Status;
|
|
}
|
|
|
|
ZwClose( eventHandle );
|
|
if ( !NT_SUCCESS(status) && status != STATUS_VOLUME_DISMOUNTED) {
|
|
ClusDiskPrint((
|
|
1,
|
|
"[ClusDisk] DismountDevice: Failed to dismount the volume. Status %08X.\n",
|
|
status
|
|
));
|
|
}
|
|
|
|
FnExit:
|
|
|
|
CDLOG( "DismountDevice: Handle %p Exit => %!status!", FileHandle, status );
|
|
|
|
ClusDiskPrint(( 3,
|
|
"[ClusDisk] DismountDevice: Exit handle %p, status %08X \n",
|
|
FileHandle,
|
|
status ));
|
|
|
|
return(status);
|
|
|
|
} // DismountDevice
|
|
|
|
|
|
|
|
BOOLEAN
|
|
AttachedDevice(
|
|
IN ULONG Signature,
|
|
OUT PDEVICE_OBJECT *DeviceObject
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Find out whether we're supposed to attach to a given disk signature.
|
|
|
|
Arguments:
|
|
|
|
Signature - The signature to decide whether to attach or not.
|
|
|
|
DeviceObject - The DeviceObject for Partition0 if it is attached.
|
|
|
|
Return Value:
|
|
|
|
TRUE - if we're supposed to attach to this signature.
|
|
FALSE - if not.
|
|
|
|
--*/
|
|
|
|
{
|
|
PDEVICE_LIST_ENTRY deviceEntry;
|
|
|
|
// 2000/02/05: stevedz - added synchronization.
|
|
|
|
ACQUIRE_SHARED( &ClusDiskDeviceListLock );
|
|
|
|
deviceEntry = ClusDiskDeviceList;
|
|
while ( deviceEntry ) {
|
|
if ( deviceEntry->Signature == Signature ) {
|
|
if ( deviceEntry->Attached ) {
|
|
*DeviceObject = deviceEntry->DeviceObject;
|
|
RELEASE_SHARED( &ClusDiskDeviceListLock );
|
|
return(TRUE);
|
|
} else {
|
|
RELEASE_SHARED( &ClusDiskDeviceListLock );
|
|
return(FALSE);
|
|
}
|
|
}
|
|
deviceEntry = deviceEntry->Next;
|
|
}
|
|
|
|
RELEASE_SHARED( &ClusDiskDeviceListLock );
|
|
return(FALSE);
|
|
|
|
} // AttachedDevice
|
|
|
|
|
|
|
|
NTSTATUS
|
|
EnableHaltProcessing(
|
|
IN KIRQL *Irql
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Enable halt processing. This routine is called every time we create
|
|
an open reference to the ClusDisk control channel. The reference count
|
|
is bumped and if it transitions from zero to non-zero, we register
|
|
for a halt notification callback with the ClusterNetwork driver.
|
|
|
|
If we do open a handle to ClusterNetwork, we must leave it open until
|
|
we're all done.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS for the request.
|
|
|
|
Notes:
|
|
|
|
This routine must be called with the ClusDiskSpinLock held.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
PCLUS_DEVICE_EXTENSION deviceExtension;
|
|
ULONG haltEnabled;
|
|
HALTPROC_CONTEXT context;
|
|
|
|
CDLOG( "EnableHaltProcessing: Entry Irql %!irql!", *Irql );
|
|
|
|
haltEnabled = ClusNetRefCount;
|
|
ClusNetRefCount++;
|
|
|
|
//
|
|
// If halt processing is already enabled, then leave now if ClusNetHandle
|
|
// is set. Otherwise, fall through, open clusnet driver, and try to save
|
|
// the handle. If multiple threads are saving the handle at the same time,
|
|
// only one will succeed - the other threads will release their handles.
|
|
// Note: the ClusNetHandle might still be null even if the reference
|
|
// count is non-zero. We might be in the process of performing the
|
|
// open on another thread. In this case, we no longer return an error.
|
|
// Instead, we fall through, and try to save the ClusNetHandle later.
|
|
//
|
|
if ( haltEnabled ) {
|
|
if ( ClusNetHandle != NULL ) {
|
|
CDLOG( "EnableHaltProcessing: ClusNetHandle already saved" );
|
|
return(STATUS_SUCCESS);
|
|
}
|
|
}
|
|
|
|
deviceExtension = RootDeviceObject->DeviceExtension;
|
|
KeReleaseSpinLock(&ClusDiskSpinLock, *Irql);
|
|
|
|
context.FileHandle = NULL;
|
|
context.DeviceExtension = deviceExtension;
|
|
|
|
status = ProcessDelayedWorkSynchronous( RootDeviceObject, EnableHaltProcessingWorker, &context );
|
|
|
|
KeAcquireSpinLock(&ClusDiskSpinLock, Irql);
|
|
|
|
if ( NT_SUCCESS(status) ) {
|
|
|
|
//
|
|
// If another thread is running concurrently and managed to save the handle, we need to close
|
|
// the one we just opened.
|
|
//
|
|
|
|
if ( ClusNetHandle != NULL ) {
|
|
|
|
//
|
|
// Don't close the handle directly - use the work queue. Release the spinlock before
|
|
// closing the handle. The context filehandle is already set.
|
|
//
|
|
|
|
KeReleaseSpinLock(&ClusDiskSpinLock, *Irql);
|
|
ProcessDelayedWorkSynchronous( RootDeviceObject, DisableHaltProcessingWorker, &context );
|
|
KeAcquireSpinLock(&ClusDiskSpinLock, Irql);
|
|
|
|
} else {
|
|
|
|
ClusNetHandle = context.FileHandle;
|
|
|
|
}
|
|
|
|
} else {
|
|
// Backout refcount
|
|
--ClusNetRefCount;
|
|
}
|
|
CDLOG( "EnableHaltProcessing: Exit Irql %!irql! => %!status!", *Irql, status );
|
|
|
|
return(status);
|
|
|
|
} // EnableHaltProcessing
|
|
|
|
|
|
VOID
|
|
EnableHaltProcessingWorker(
|
|
PDEVICE_OBJECT DeviceObject,
|
|
PWORK_CONTEXT WorkContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine runs in the system process and registers a callback with the
|
|
clusnet driver.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Unused.
|
|
|
|
WorkContext - General context info and routine specific context info.
|
|
|
|
Return Value:
|
|
|
|
Status returned in WorkContext structure.
|
|
|
|
--*/
|
|
{
|
|
|
|
PHALTPROC_CONTEXT context = WorkContext->Context;
|
|
NTSTATUS status;
|
|
HANDLE fileHandle;
|
|
IO_STATUS_BLOCK ioStatusBlock;
|
|
UNICODE_STRING ntUnicodeString;
|
|
OBJECT_ATTRIBUTES objectAttributes;
|
|
HANDLE eventHandle;
|
|
CLUSNET_SET_EVENT_MASK_REQUEST eventCallback;
|
|
|
|
//
|
|
// Create event for notification.
|
|
//
|
|
status = ZwCreateEvent( &eventHandle,
|
|
EVENT_ALL_ACCESS,
|
|
NULL,
|
|
SynchronizationEvent,
|
|
FALSE );
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
ClusDiskPrint((
|
|
1,
|
|
"[ClusDisk] Failed to create event, status %lx\n",
|
|
status ));
|
|
goto FnExit;
|
|
}
|
|
|
|
//
|
|
// Setup event mask structure.
|
|
//
|
|
eventCallback.EventMask = ClusnetEventHalt |
|
|
ClusnetEventPoisonPacketReceived;
|
|
eventCallback.KmodeEventCallback = ClusDiskEventCallback;
|
|
|
|
//
|
|
// Create device name for the ClusterNetwork device.
|
|
//
|
|
|
|
RtlInitUnicodeString(&ntUnicodeString,
|
|
DD_CLUSNET_DEVICE_NAME);
|
|
|
|
//
|
|
// Try to open ClusterNetwork device.
|
|
//
|
|
InitializeObjectAttributes( &objectAttributes,
|
|
&ntUnicodeString,
|
|
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
|
|
NULL,
|
|
NULL );
|
|
|
|
status = ZwCreateFile( &fileHandle,
|
|
SYNCHRONIZE | FILE_READ_DATA | FILE_WRITE_DATA,
|
|
&objectAttributes,
|
|
&ioStatusBlock,
|
|
0,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
FILE_OPEN_IF,
|
|
0,
|
|
NULL,
|
|
0 );
|
|
|
|
ASSERT( status != STATUS_PENDING );
|
|
|
|
if ( NT_SUCCESS(status) ) {
|
|
status = ZwDeviceIoControlFile( fileHandle,
|
|
eventHandle,
|
|
NULL,
|
|
NULL,
|
|
&ioStatusBlock,
|
|
IOCTL_CLUSNET_SET_EVENT_MASK,
|
|
&eventCallback,
|
|
sizeof(eventCallback),
|
|
NULL,
|
|
0 );
|
|
|
|
if ( status == STATUS_PENDING ) {
|
|
status = ZwWaitForSingleObject(eventHandle,
|
|
FALSE,
|
|
NULL);
|
|
ASSERT( NT_SUCCESS(status) );
|
|
status = ioStatusBlock.Status;
|
|
}
|
|
|
|
} else {
|
|
fileHandle = NULL;
|
|
}
|
|
|
|
ZwClose( eventHandle );
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
ClusDiskPrint((
|
|
1,
|
|
"[ClusDisk] Failed to register for halt processing. Error %lx.\n",
|
|
status ));
|
|
if ( fileHandle ) {
|
|
ZwClose( fileHandle );
|
|
}
|
|
} else {
|
|
|
|
context->FileHandle = fileHandle;
|
|
}
|
|
|
|
FnExit:
|
|
|
|
ClusDiskPrint(( 3,
|
|
"[ClusDisk] EnableHaltProcessingWorker: Returning status %08X \n", status ));
|
|
|
|
WorkContext->FinalStatus = status;
|
|
|
|
KeSetEvent( &WorkContext->CompletionEvent, IO_NO_INCREMENT, FALSE );
|
|
|
|
} // EnableHaltProcessingWorker
|
|
|
|
|
|
|
|
NTSTATUS
|
|
DisableHaltProcessing(
|
|
IN KIRQL *Irql
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine disables halt processing as needed.
|
|
|
|
Arguments:
|
|
|
|
Irql - pointer to the irql that we were previously running.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS of request.
|
|
|
|
Note:
|
|
|
|
The ClusDiskSpinLock must be held on entry.
|
|
|
|
--*/
|
|
|
|
{
|
|
HANDLE clusNetHandle;
|
|
PCLUS_DEVICE_EXTENSION deviceExtension;
|
|
HALTPROC_CONTEXT context;
|
|
|
|
CDLOG( "DisableHaltProcessing: Entry Irql %!irql!", *Irql );
|
|
|
|
|
|
if ( ClusNetRefCount == 0 ) {
|
|
return(STATUS_INVALID_DEVICE_STATE);
|
|
}
|
|
|
|
ASSERT( ClusNetHandle != NULL );
|
|
|
|
if ( --ClusNetRefCount == 0 ) {
|
|
clusNetHandle = ClusNetHandle;
|
|
ClusNetHandle = NULL;
|
|
deviceExtension = RootDeviceObject->DeviceExtension;
|
|
|
|
//
|
|
// We must close the ClusterNetwork handle. But first leave
|
|
// the handle null, so we can release the spinlock and
|
|
// perform the decrement syncrhonized. This is just like
|
|
// the enable case when we release the spinlock.
|
|
//
|
|
|
|
KeReleaseSpinLock(&ClusDiskSpinLock, *Irql);
|
|
context.FileHandle = clusNetHandle;
|
|
ProcessDelayedWorkSynchronous( RootDeviceObject, DisableHaltProcessingWorker, &context );
|
|
KeAcquireSpinLock(&ClusDiskSpinLock, Irql);
|
|
|
|
// [GorN 12/09/99] We can get here if EnableHaltProcessing
|
|
// occurs while we are doing ZwClose( clusNetHandle );
|
|
// Don't need this assert:
|
|
//
|
|
// ASSERT( ClusNetRefCount == 0 );
|
|
}
|
|
|
|
CDLOG( "DisableHaltProcessing: Exit Irql %!irql!", *Irql );
|
|
|
|
return(STATUS_SUCCESS);
|
|
|
|
} // DisableHaltProcessing
|
|
|
|
VOID
|
|
DisableHaltProcessingWorker(
|
|
PDEVICE_OBJECT DeviceObject,
|
|
PWORK_CONTEXT WorkContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine runs in the system process and simply closes the clusnet
|
|
file handle.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Unused.
|
|
|
|
WorkContext - General context info and routine specific context info.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
PHALTPROC_CONTEXT context = WorkContext->Context;
|
|
|
|
ZwClose( context->FileHandle );
|
|
|
|
ClusDiskPrint(( 3,
|
|
"[ClusDisk] DisableHaltProcessingWorker: Returns \n" ));
|
|
|
|
KeSetEvent( &WorkContext->CompletionEvent, IO_NO_INCREMENT, FALSE );
|
|
|
|
} // DisableHaltProcessingWorker
|
|
|
|
|
|
VOID
|
|
ClusDiskEventCallback(
|
|
IN CLUSNET_EVENT_TYPE EventType,
|
|
IN CL_NODE_ID NodeId,
|
|
IN CL_NETWORK_ID NetworkId
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PDEVICE_OBJECT deviceObject;
|
|
PCLUS_DEVICE_EXTENSION deviceExtension;
|
|
KIRQL irql;
|
|
|
|
CDLOG( "ClusDiskEventCallback: Entry EventType %d NodeId %d NetworkId %d" ,
|
|
EventType,
|
|
NodeId,
|
|
NetworkId );
|
|
|
|
ClusDiskPrint((
|
|
3,
|
|
"[ClusDisk] Halt Processing routine was called with event %lx.\n",
|
|
EventType ));
|
|
|
|
if ( RootDeviceObject == NULL ) {
|
|
return;
|
|
}
|
|
|
|
deviceObject = RootDeviceObject->DriverObject->DeviceObject;
|
|
|
|
// We need to stop reservations right now so another node can arbitrate and
|
|
// take ownership of the disks.
|
|
|
|
// We later call ClusDiskCompletePendedIrps (inside ClusDiskHaltProcessingWorker),
|
|
// to mark disk offline and complete IRPs. ClusDiskCompletePendedIrps will
|
|
// also clear the reserve flags.
|
|
|
|
//
|
|
// For each ClusDisk device, if we have a persistent reservation, then
|
|
// stop it.
|
|
//
|
|
while ( deviceObject ) {
|
|
deviceExtension = deviceObject->DeviceExtension;
|
|
if ( deviceExtension->BusType != RootBus ) {
|
|
deviceExtension->ReserveTimer = 0;
|
|
// Don't offline the device so that dismount can possibly work!
|
|
}
|
|
deviceObject = deviceObject->NextDevice;
|
|
}
|
|
|
|
//
|
|
// Now schedule a worker queue item to fully take the devices offline.
|
|
//
|
|
//
|
|
// Globally Synchronize
|
|
//
|
|
KeAcquireSpinLock(&ClusDiskSpinLock, &irql);
|
|
|
|
if ( !HaltBusy ) {
|
|
HaltBusy = TRUE;
|
|
HaltOfflineBusy = TRUE;
|
|
ExQueueWorkItem(&HaltWorkItem,
|
|
CriticalWorkQueue );
|
|
}
|
|
KeReleaseSpinLock(&ClusDiskSpinLock, irql);
|
|
|
|
CDLOG( "ClusDiskEventCallback: Exit EventType %d NodeId %d NetworkId %d" ,
|
|
EventType,
|
|
NodeId,
|
|
NetworkId );
|
|
|
|
return;
|
|
|
|
} // ClusDiskEventCallback
|
|
|
|
|
|
|
|
VOID
|
|
ClusDiskLogError(
|
|
IN PDRIVER_OBJECT DriverObject,
|
|
IN PDEVICE_OBJECT DeviceObject OPTIONAL,
|
|
IN ULONG SequenceNumber,
|
|
IN UCHAR MajorFunctionCode,
|
|
IN UCHAR RetryCount,
|
|
IN ULONG UniqueErrorValue,
|
|
IN NTSTATUS FinalStatus,
|
|
IN NTSTATUS SpecificIOStatus,
|
|
IN ULONG LengthOfText,
|
|
IN PWCHAR Text
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine allocates an error log entry, copies the supplied data
|
|
to it, and requests that it be written to the error log file.
|
|
|
|
Arguments:
|
|
|
|
DriverObject - A pointer to the driver object for the device.
|
|
|
|
DeviceObject - A pointer to the device object associated with the
|
|
device that had the error, early in initialization, one may not
|
|
yet exist.
|
|
|
|
SequenceNumber - A ulong value that is unique to an IRP over the
|
|
life of the irp in this driver - 0 generally means an error not
|
|
associated with an irp.
|
|
|
|
MajorFunctionCode - If there is an error associated with the irp,
|
|
this is the major function code of that irp.
|
|
|
|
RetryCount - The number of times a particular operation has been
|
|
retried.
|
|
|
|
UniqueErrorValue - A unique long word that identifies the particular
|
|
call to this function.
|
|
|
|
FinalStatus - The final status given to the irp that was associated
|
|
with this error. If this log entry is being made during one of
|
|
the retries this value will be STATUS_SUCCESS.
|
|
|
|
SpecificIOStatus - The IO status for a particular error.
|
|
|
|
LengthOfText - The length in bytes (including the terminating NULL)
|
|
of the text insertion string.
|
|
|
|
Text - the text to dump.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PIO_ERROR_LOG_PACKET errorLogEntry;
|
|
PVOID objectToUse;
|
|
PUCHAR ptrToText;
|
|
ULONG maxTextLength = 104;
|
|
|
|
|
|
if (ARGUMENT_PRESENT(DeviceObject)) {
|
|
|
|
objectToUse = DeviceObject;
|
|
|
|
} else {
|
|
|
|
objectToUse = DriverObject;
|
|
|
|
}
|
|
|
|
if ( LengthOfText < maxTextLength ) {
|
|
maxTextLength = LengthOfText;
|
|
}
|
|
|
|
errorLogEntry = IoAllocateErrorLogEntry(
|
|
objectToUse,
|
|
(UCHAR)(sizeof(IO_ERROR_LOG_PACKET) + maxTextLength)
|
|
);
|
|
|
|
if ( errorLogEntry != NULL ) {
|
|
|
|
errorLogEntry->ErrorCode = SpecificIOStatus;
|
|
errorLogEntry->SequenceNumber = SequenceNumber;
|
|
errorLogEntry->MajorFunctionCode = MajorFunctionCode;
|
|
errorLogEntry->RetryCount = RetryCount;
|
|
errorLogEntry->UniqueErrorValue = UniqueErrorValue;
|
|
errorLogEntry->FinalStatus = FinalStatus;
|
|
errorLogEntry->DumpDataSize = 0;
|
|
ptrToText = (PUCHAR)&errorLogEntry->DumpData[0];
|
|
|
|
if (maxTextLength) {
|
|
|
|
errorLogEntry->NumberOfStrings = 1;
|
|
errorLogEntry->StringOffset = (USHORT)(ptrToText-
|
|
(PUCHAR)errorLogEntry);
|
|
RtlCopyMemory(
|
|
ptrToText,
|
|
Text,
|
|
maxTextLength
|
|
);
|
|
|
|
}
|
|
|
|
IoWriteErrorLogEntry(errorLogEntry);
|
|
|
|
} else {
|
|
ClusDiskPrint((1,
|
|
"[ClusDisk] Failed to allocate system buffer of size %u.\n",
|
|
sizeof(IO_ERROR_LOG_PACKET) + maxTextLength ));
|
|
}
|
|
|
|
} // ClusDiskLogError
|
|
|
|
|
|
|
|
NTSTATUS
|
|
ClusDiskMarkIrpPending(
|
|
PIRP Irp,
|
|
PDRIVER_CANCEL CancelRoutine
|
|
)
|
|
/*++
|
|
|
|
Notes:
|
|
|
|
Called with IoCancelSpinLock held.
|
|
|
|
--*/
|
|
{
|
|
//
|
|
// Set up for cancellation
|
|
//
|
|
ASSERT(Irp->CancelRoutine == NULL);
|
|
|
|
if (!Irp->Cancel) {
|
|
|
|
IoMarkIrpPending(Irp);
|
|
IoSetCancelRoutine(Irp, CancelRoutine);
|
|
#if 0
|
|
ClusDiskPrint((
|
|
1,
|
|
"[ClusDisk] Pending Irp %p \n",
|
|
Irp
|
|
));
|
|
#endif
|
|
return(STATUS_SUCCESS);
|
|
}
|
|
|
|
//
|
|
// The IRP has already been cancelled.
|
|
//
|
|
|
|
ClusDiskPrint((
|
|
1,
|
|
"[ClusDisk] Irp %lx already cancelled.\n", Irp));
|
|
|
|
return(STATUS_CANCELLED);
|
|
|
|
} // ClusDiskMarkIrpPending
|
|
|
|
|
|
|
|
VOID
|
|
ClusDiskCompletePendingRequest(
|
|
IN PIRP Irp,
|
|
IN NTSTATUS Status,
|
|
PCLUS_DEVICE_EXTENSION DeviceExtension
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Completes a pending request.
|
|
|
|
Arguments:
|
|
|
|
Irp - A pointer to the IRP for this request.
|
|
Status - The final status of the request.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PIO_STACK_LOCATION irpSp;
|
|
|
|
irpSp = IoGetCurrentIrpStackLocation(Irp);
|
|
|
|
#if DBG
|
|
|
|
if (Irp->Cancel) {
|
|
ASSERT(Irp->CancelRoutine == NULL);
|
|
}
|
|
|
|
#endif // DBG
|
|
|
|
IoSetCancelRoutine(Irp, NULL);
|
|
|
|
ClusDiskPrint((
|
|
1,
|
|
"[ClusDisk] Completed waiting Irp %p \n",
|
|
Irp
|
|
));
|
|
|
|
if (Irp->Cancel) {
|
|
#if 0
|
|
ClusDiskPrint((
|
|
1,
|
|
"[ClusDisk] Completed irp %p was cancelled\n", Irp));
|
|
#endif
|
|
|
|
Status = STATUS_CANCELLED;
|
|
}
|
|
|
|
Irp->IoStatus.Status = (NTSTATUS) Status;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
|
|
return;
|
|
|
|
} // ClusDiskCompletePendingRequest
|
|
|
|
|
|
|
|
VOID
|
|
ClusDiskIrpCancel(
|
|
PDEVICE_OBJECT DeviceObject,
|
|
PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Cancellation handler pending Irps.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
Notes:
|
|
|
|
Called with cancel spinlock held.
|
|
Returns with cancel spinlock released.
|
|
|
|
--*/
|
|
|
|
{
|
|
CDLOG( "IrpCancel: Entry DO %p irp %p", DeviceObject, Irp );
|
|
|
|
ClusDiskPrint((
|
|
1,
|
|
"[ClusDisk] Cancel Irp %p, DevObj %p \n",
|
|
Irp,
|
|
DeviceObject ));
|
|
|
|
//
|
|
// If this Irp is associated with the control device object, then make
|
|
// sure the Irp is removed from the WaitingIoctls list first.
|
|
//
|
|
|
|
KeAcquireSpinLockAtDpcLevel(&ClusDiskSpinLock);
|
|
RemoveEntryList( &Irp->Tail.Overlay.ListEntry );
|
|
KeReleaseSpinLockFromDpcLevel(&ClusDiskSpinLock);
|
|
|
|
IoSetCancelRoutine(Irp, NULL);
|
|
|
|
//
|
|
// Release the cancel spinlock here, so that we are not holding the
|
|
// spinlock when we complete the Irp.
|
|
//
|
|
IoReleaseCancelSpinLock(Irp->CancelIrql);
|
|
|
|
Irp->IoStatus.Status = STATUS_CANCELLED;
|
|
Irp->IoStatus.Information = 0;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
|
|
CDLOG( "IrpCancel: Exit DO %p irp %p", DeviceObject, Irp );
|
|
|
|
return;
|
|
|
|
} // ClusDiskIrpCancel
|
|
|
|
|
|
|
|
NTSTATUS
|
|
ClusDiskForwardIrpSynchronous(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine sends the Irp to the next driver in line
|
|
when the Irp needs to be processed by the lower drivers
|
|
prior to being processed by this one.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject
|
|
Irp
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
|
|
{
|
|
PCLUS_DEVICE_EXTENSION deviceExtension;
|
|
KEVENT event;
|
|
NTSTATUS status;
|
|
|
|
KeInitializeEvent( &event, NotificationEvent, FALSE );
|
|
deviceExtension = (PCLUS_DEVICE_EXTENSION) DeviceObject->DeviceExtension;
|
|
|
|
//
|
|
// copy the irpstack for the next device
|
|
//
|
|
|
|
IoCopyCurrentIrpStackLocationToNext( Irp );
|
|
|
|
//
|
|
// set a completion routine
|
|
//
|
|
|
|
IoSetCompletionRoutine( Irp, ClusDiskIrpCompletion,
|
|
&event, TRUE, TRUE, TRUE );
|
|
|
|
//
|
|
// call the next lower device
|
|
//
|
|
|
|
status = IoCallDriver( deviceExtension->TargetDeviceObject, Irp );
|
|
|
|
//
|
|
// wait for the actual completion
|
|
//
|
|
|
|
if ( status == STATUS_PENDING ) {
|
|
KeWaitForSingleObject( &event, Executive, KernelMode, FALSE, NULL );
|
|
status = Irp->IoStatus.Status;
|
|
}
|
|
|
|
return status;
|
|
|
|
} // ClusDiskForwardIrpSynchronous()
|
|
|
|
|
|
NTSTATUS
|
|
EjectVolumes(
|
|
IN PDEVICE_OBJECT DeviceObject
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine tells the partmgr to eject all volumes. For the volume manager
|
|
to re-attach to the volumes, IOCTL_PARTMGR_CHECK_UNCLAIMED_PARTITIONS is used.
|
|
The cluster service calls IOCTL_PARTMGR_CHECK_UNCLAIMED_PARTITIONS when bringing
|
|
the disk online, so it is not necessary to make this call in the driver.
|
|
|
|
Note that this routine should only be called for physical devices (partition0)
|
|
and that no one should be using the volumes.
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
{
|
|
PIRP irp;
|
|
PKEVENT event = NULL;
|
|
PCLUS_DEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
|
|
IO_STATUS_BLOCK ioStatusBlock;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
|
|
// Looks like the IO request will bypass clusdisk at the physical disk level
|
|
// because clusdisk attached after FTDISK went looking for the top of the disk
|
|
// stack and cached the pointer.
|
|
//
|
|
// PnP race condition: occasionally, the device stack was incorrect and clustered
|
|
// disks weren't being protected properly. Since we are using PnP notification
|
|
// instead of an AddDevice routine, there was a small window where we would
|
|
// attach to the device stack correctly, but another driver was also attaching
|
|
// at the same time. So I/O bound for the device would go like this:
|
|
// NTFS -> Volsnap -> Ftdisk -> Disk
|
|
//
|
|
// even though the device stack looked like this:
|
|
// 82171a50 \Driver\ClusDisk 82171b08 ClusDisk1Part0
|
|
// 822be720 \Driver\PartMgr 822be7d8
|
|
// 822bd030 \Driver\Disk 822bd0e8 DR1
|
|
// 822c0990 \Driver\aic78xx 822c0a48 aic78xx1Port2Path0Target0Lun0
|
|
//
|
|
// So we have to tell partmgr to eject all the volume managers and then
|
|
// let the volume managers attach again. This has the effect of removing the
|
|
// cached pointer
|
|
//
|
|
// To tell the volume managers to stop using this disk:
|
|
// IOCTL_PARTMGR_EJECT_VOLUME_MANAGERS
|
|
//
|
|
// To tell the volume managers they can start using the disk:
|
|
// IOCTL_PARTMGR_CHECK_UNCLAIMED_PARTITIONS
|
|
|
|
|
|
CDLOG( "EjectVolumes: Entry DO %p", DeviceObject );
|
|
|
|
if ( !deviceExtension->TargetDeviceObject ) {
|
|
status = STATUS_INVALID_PARAMETER;
|
|
goto FnExit;
|
|
}
|
|
|
|
event = ExAllocatePool( NonPagedPool,
|
|
sizeof(KEVENT) );
|
|
if ( NULL == event ) {
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto FnExit;
|
|
}
|
|
|
|
//
|
|
// Eject the volume managers.
|
|
//
|
|
|
|
irp = IoBuildDeviceIoControlRequest( IOCTL_PARTMGR_EJECT_VOLUME_MANAGERS,
|
|
deviceExtension->TargetDeviceObject,
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
0,
|
|
FALSE,
|
|
event,
|
|
&ioStatusBlock );
|
|
|
|
if ( !irp ) {
|
|
ClusDiskPrint((
|
|
1,
|
|
"[ClusDisk] EjectVolumes: Failed to build IRP to eject volume managers. \n"
|
|
));
|
|
|
|
status = STATUS_UNSUCCESSFUL;
|
|
goto FnExit;
|
|
}
|
|
|
|
//
|
|
// Set the event object to the unsignaled state.
|
|
// It will be used to signal request completion.
|
|
//
|
|
|
|
KeInitializeEvent( event,
|
|
NotificationEvent,
|
|
FALSE );
|
|
|
|
status = IoCallDriver( deviceExtension->TargetDeviceObject,
|
|
irp );
|
|
|
|
if ( STATUS_PENDING == status ) {
|
|
|
|
KeWaitForSingleObject( event,
|
|
Suspended,
|
|
KernelMode,
|
|
FALSE,
|
|
NULL );
|
|
|
|
status = ioStatusBlock.Status;
|
|
}
|
|
|
|
ClusDiskPrint((3,
|
|
"[ClusDisk] EjectVolumes: Eject IOCTL returns %08X \n",
|
|
status
|
|
));
|
|
|
|
FnExit:
|
|
|
|
if ( event ) {
|
|
ExFreePool( event );
|
|
}
|
|
|
|
ClusDiskPrint((1,
|
|
"[ClusDisk] EjectVolumes: Target devobj %p Final status %08X \n",
|
|
DeviceObject,
|
|
status
|
|
));
|
|
|
|
CDLOG( "EjectVolumes: Exit DO %p, status %08X", DeviceObject, status );
|
|
|
|
return status;
|
|
|
|
} // EjectVolumes
|
|
|
|
|
|
#if 0 // Removed 2/27/2001
|
|
|
|
|
|
NTSTATUS
|
|
ReclaimVolumes(
|
|
IN PDEVICE_OBJECT DeviceObject
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine tells the partmgr to reclaim all volumes.
|
|
|
|
Note that this routine should only be called for physical devices (partition0)
|
|
and that no one should be using the volumes.
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
{
|
|
PCLUS_DEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
|
|
PIRP irp = NULL;
|
|
PKEVENT event = NULL;
|
|
IO_STATUS_BLOCK ioStatusBlock;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
|
|
CDLOG( "ReclaimVolumes: Entry DO %p", DeviceObject );
|
|
|
|
InterlockedIncrement( &deviceExtension->ReclaimInProgress );
|
|
|
|
if ( !deviceExtension->TargetDeviceObject ) {
|
|
status = STATUS_INVALID_PARAMETER;
|
|
goto FnExit;
|
|
}
|
|
|
|
event = ExAllocatePool( NonPagedPool,
|
|
sizeof(KEVENT) );
|
|
if ( NULL == event ) {
|
|
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto FnExit;
|
|
}
|
|
|
|
//
|
|
// Allow the volume managers to reclaim partitions.
|
|
//
|
|
|
|
irp = IoBuildDeviceIoControlRequest( IOCTL_PARTMGR_CHECK_UNCLAIMED_PARTITIONS,
|
|
deviceExtension->TargetDeviceObject,
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
0,
|
|
FALSE,
|
|
event,
|
|
&ioStatusBlock );
|
|
|
|
if ( !irp ) {
|
|
ClusDiskPrint((
|
|
1,
|
|
"[ClusDisk] Failed to build IRP to check unclaimed partitions. \n"
|
|
));
|
|
|
|
status = STATUS_UNSUCCESSFUL;
|
|
goto FnExit;
|
|
}
|
|
|
|
//
|
|
// Set the event object to the unsignaled state.
|
|
// It will be used to signal request completion.
|
|
//
|
|
|
|
KeInitializeEvent( event,
|
|
NotificationEvent,
|
|
FALSE );
|
|
|
|
|
|
status = IoCallDriver( deviceExtension->TargetDeviceObject,
|
|
irp );
|
|
|
|
if ( status == STATUS_PENDING ) {
|
|
|
|
KeWaitForSingleObject( event,
|
|
Suspended,
|
|
KernelMode,
|
|
FALSE,
|
|
NULL );
|
|
|
|
status = ioStatusBlock.Status;
|
|
}
|
|
|
|
FnExit:
|
|
|
|
if ( event ) {
|
|
ExFreePool( event );
|
|
}
|
|
|
|
ClusDiskPrint((1,
|
|
"[ClusDisk] ReclaimVolumes: Target devobj %p Final status %08X \n",
|
|
DeviceObject,
|
|
status
|
|
));
|
|
|
|
CDLOG( "ReclaimVolumes: Exit DO %p, status %08X", DeviceObject, status );
|
|
|
|
InterlockedDecrement( &deviceExtension->ReclaimInProgress );
|
|
|
|
return status;
|
|
|
|
} // ReclaimVolumes
|
|
#endif
|
|
|
|
|
|
NTSTATUS
|
|
SetVolumeState(
|
|
PCLUS_DEVICE_EXTENSION PhysicalDisk,
|
|
ULONG NewDiskState
|
|
)
|
|
{
|
|
PVOL_STATE_INFO volStateInfo = NULL;
|
|
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
|
|
//
|
|
// If this isn't the physical disk (i.e. partition 0), then exit.
|
|
//
|
|
|
|
if ( PhysicalDisk != PhysicalDisk->PhysicalDevice->DeviceExtension ) {
|
|
status = STATUS_INVALID_PARAMETER;
|
|
goto FnExit;
|
|
}
|
|
|
|
#if 0
|
|
// Don't use this...
|
|
// Optimization? If new state is equal to old state, we are done.
|
|
// Should work as long as this device extension is set AFTER the
|
|
// call to SetVolumeState. Currently the macros set the device extension
|
|
// state BEFORE calling this routine. Also, some routines set DE state
|
|
// well before calling this routine.
|
|
// More testing...
|
|
|
|
if ( NewDiskState == DeviceExtension->DiskState ) {
|
|
goto FnExit;
|
|
}
|
|
#endif
|
|
|
|
volStateInfo = ExAllocatePool( NonPagedPool, sizeof( VOL_STATE_INFO ) );
|
|
|
|
if ( !volStateInfo ) {
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto FnExit;
|
|
}
|
|
|
|
volStateInfo->NewDiskState = NewDiskState;
|
|
volStateInfo->WorkItem = NULL;
|
|
|
|
//
|
|
// If IRQL is PASSIVE_LEVEL, call the routine directly.
|
|
// We know the call is direct because the WorkItem portion will
|
|
// be NULL.
|
|
//
|
|
|
|
if ( PASSIVE_LEVEL == KeGetCurrentIrql() ) {
|
|
SetVolumeStateWorker( PhysicalDisk->DeviceObject,
|
|
volStateInfo );
|
|
goto FnExit;
|
|
}
|
|
|
|
volStateInfo->WorkItem = IoAllocateWorkItem( PhysicalDisk->DeviceObject );
|
|
|
|
if ( !volStateInfo->WorkItem ) {
|
|
ExFreePool( volStateInfo );
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto FnExit;
|
|
}
|
|
|
|
IoQueueWorkItem( volStateInfo->WorkItem,
|
|
SetVolumeStateWorker,
|
|
DelayedWorkQueue,
|
|
volStateInfo );
|
|
|
|
FnExit:
|
|
|
|
return status;
|
|
|
|
} // SetVolumeState
|
|
|
|
|
|
VOID
|
|
SetVolumeStateWorker(
|
|
PDEVICE_OBJECT DeviceObject,
|
|
PVOID Context
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Call volume manager to mark all volumes for this disk offline or online.
|
|
|
|
If the IRQL is not PASSIVE_LEVEL, then this routine will return an error.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Clustered disk to set state.
|
|
|
|
Context - Contains information whether this routine has been called directly
|
|
or via a work item. Also indicates whether this is an online or
|
|
offline request.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
PDRIVE_LAYOUT_INFORMATION_EX driveLayoutInfo = NULL;
|
|
PPARTITION_INFORMATION_EX partitionInfo;
|
|
PIO_WORKITEM workItem = ((PVOL_STATE_INFO)Context)->WorkItem;
|
|
PCLUS_DEVICE_EXTENSION PhysicalDisk = DeviceObject->DeviceExtension;
|
|
|
|
NTSTATUS status = STATUS_UNSUCCESSFUL;
|
|
|
|
ULONG ioctl;
|
|
ULONG partIndex;
|
|
ULONG newDiskState = ((PVOL_STATE_INFO)Context)->NewDiskState;
|
|
|
|
KIRQL irql;
|
|
|
|
CDLOG( "SetVolumeState: Entry DevObj %p newstate: %s workItem: %p ",
|
|
PhysicalDisk->DeviceObject,
|
|
( DiskOnline == newDiskState ? "DiskOnline" : "DiskOffline" ),
|
|
workItem );
|
|
|
|
ClusDiskPrint(( 3,
|
|
"[ClusDisk] SetVolumeState: DevObj %p newstate: %s workItem: %p \n",
|
|
PhysicalDisk->DeviceObject,
|
|
DiskStateToString( newDiskState ),
|
|
workItem ));
|
|
|
|
irql = KeGetCurrentIrql();
|
|
if ( PASSIVE_LEVEL != irql ) {
|
|
ClusDiskPrint(( 1,
|
|
"[ClusDisk] SetVolumeState: Current IRQL %d not PASSIVE_LEVEL - skipping state change \n",
|
|
irql ));
|
|
ASSERT( FALSE );
|
|
goto FnExit;
|
|
}
|
|
|
|
//
|
|
// This should never happen as it was checked earlier.
|
|
//
|
|
|
|
if ( PhysicalDisk != PhysicalDisk->PhysicalDevice->DeviceExtension ) {
|
|
ASSERT( FALSE );
|
|
PhysicalDisk = PhysicalDisk->PhysicalDevice->DeviceExtension;
|
|
ClusDiskPrint(( 1,
|
|
"[ClusDisk] SetVolumeState: Resetting physical disk pointer: DevObj %p \n",
|
|
PhysicalDisk->DeviceObject ));
|
|
CDLOG( "SetVolumeState: Resetting physical disk pointer: DevObj %p \n",
|
|
PhysicalDisk->DeviceObject );
|
|
}
|
|
|
|
if ( DiskOnline == newDiskState ) {
|
|
ioctl = IOCTL_VOLUME_ONLINE;
|
|
} else {
|
|
ioctl = IOCTL_VOLUME_OFFLINE;
|
|
}
|
|
|
|
//
|
|
// Get the drive layout for the disk.
|
|
//
|
|
// If we are bringing the disk online, get a new drive layout in
|
|
// case the drive layout changed while the disk was on another node.
|
|
//
|
|
|
|
status = GetDriveLayout( PhysicalDisk->DeviceObject,
|
|
&driveLayoutInfo,
|
|
( IOCTL_VOLUME_ONLINE == ioctl) ? TRUE : FALSE , // If online, update cached drive layout
|
|
( IOCTL_VOLUME_ONLINE == ioctl) ? TRUE : FALSE ); // If online, flush storage drivers cached drive layout
|
|
|
|
if ( !NT_SUCCESS(status) || !driveLayoutInfo ) {
|
|
ClusDiskPrint(( 1,
|
|
"[ClusDisk] SetVolumeState: Failed to read partition info, status %08X \n",
|
|
status ));
|
|
CDLOG( "SetVolumeState: Failed to read partition info, status %08X \n",
|
|
status );
|
|
goto FnExit;
|
|
}
|
|
|
|
for ( partIndex = 0;
|
|
partIndex < driveLayoutInfo->PartitionCount;
|
|
partIndex++ ) {
|
|
|
|
partitionInfo = &driveLayoutInfo->PartitionEntry[partIndex];
|
|
|
|
if ( 0 == partitionInfo->PartitionNumber ) {
|
|
continue;
|
|
}
|
|
|
|
// Online/Offline the volume.
|
|
|
|
status = SendFtdiskIoctlSync( NULL,
|
|
PhysicalDisk->DiskNumber,
|
|
partitionInfo->PartitionNumber,
|
|
ioctl );
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
ClusDiskPrint(( 1,
|
|
"[ClusDisk] SetVolumeState: Failed to set state disk%d part%d , status %08X \n",
|
|
PhysicalDisk->DiskNumber,
|
|
partitionInfo->PartitionNumber,
|
|
status
|
|
));
|
|
CDLOG( "SetVolumeState: Failed to set state disk%d part%d , status %08X \n",
|
|
PhysicalDisk->DiskNumber,
|
|
partitionInfo->PartitionNumber,
|
|
status );
|
|
}
|
|
|
|
}
|
|
|
|
FnExit:
|
|
|
|
CDLOG( "SetVolumeState: Exit DevObj %p DiskNumber %d newstate: %s status %x ",
|
|
PhysicalDisk->DeviceObject,
|
|
PhysicalDisk->DiskNumber,
|
|
( DiskOnline == newDiskState ? "DiskOnline" : "DiskOffline" ),
|
|
status );
|
|
|
|
ClusDiskPrint(( 3,
|
|
"[ClusDisk] SetVolumeState: DiskNumber %d newstate: %s final status %08X \n",
|
|
PhysicalDisk->DiskNumber,
|
|
( DiskOnline == newDiskState ? "DiskOnline" : "DiskOffline" ),
|
|
status
|
|
));
|
|
|
|
#if CLUSTER_FREE_ASSERTS
|
|
if ( !NT_SUCCESS(status) ) {
|
|
DbgPrint("[ClusDisk] SetVolumeState failed: DiskNumber %d newstate: %s final status %08X \n",
|
|
PhysicalDisk->DiskNumber,
|
|
( DiskOnline == newDiskState ? "DiskOnline" : "DiskOffline" ),
|
|
status
|
|
);
|
|
// DbgBreakPoint();
|
|
}
|
|
#endif
|
|
|
|
|
|
if ( driveLayoutInfo ) {
|
|
ExFreePool( driveLayoutInfo );
|
|
}
|
|
|
|
if ( workItem ) {
|
|
IoFreeWorkItem( workItem );
|
|
}
|
|
|
|
if ( Context ) {
|
|
ExFreePool( Context );
|
|
}
|
|
|
|
return;
|
|
|
|
} // SetVolumeStateWorker
|
|
|
|
|
|
PCHAR
|
|
VolumeIoctlToString(
|
|
ULONG Ioctl
|
|
)
|
|
{
|
|
switch ( Ioctl ) {
|
|
|
|
case IOCTL_VOLUME_ONLINE:
|
|
return "IOCTL_VOLUME_ONLINE";
|
|
|
|
case IOCTL_VOLUME_OFFLINE:
|
|
return "IOCTL_VOLUME_OFFLINE";
|
|
|
|
case IOCTL_VOLUME_IS_OFFLINE:
|
|
return "IOCTL_VOLUME_IS_OFFLINE";
|
|
|
|
default:
|
|
return "Unknown volume IOCTL";
|
|
}
|
|
|
|
} // VolumeIoctlToString
|
|
|
|
|
|
NTSTATUS
|
|
SendFtdiskIoctlSync(
|
|
PDEVICE_OBJECT TargetObject,
|
|
ULONG DiskNumber,
|
|
ULONG PartNumber,
|
|
ULONG Ioctl
|
|
)
|
|
{
|
|
PIRP irp;
|
|
PDEVICE_OBJECT targetDevice = NULL;
|
|
KEVENT event;
|
|
NTSTATUS status;
|
|
IO_STATUS_BLOCK ioStatusBlock;
|
|
|
|
WCHAR partitionNameBuffer[MAX_PARTITION_NAME_LENGTH];
|
|
UNICODE_STRING targetDeviceName;
|
|
|
|
if ( TargetObject ) {
|
|
|
|
targetDevice = TargetObject;
|
|
|
|
} else {
|
|
|
|
//
|
|
// Create device name for the physical disk.
|
|
//
|
|
|
|
if ( FAILED( StringCchPrintfW( partitionNameBuffer,
|
|
RTL_NUMBER_OF(partitionNameBuffer),
|
|
DEVICE_PARTITION_NAME,
|
|
DiskNumber,
|
|
PartNumber ) ) ) {
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto FnExit;
|
|
}
|
|
WCSLEN_ASSERT( partitionNameBuffer );
|
|
|
|
ClusDiskPrint(( 3,
|
|
"[ClusDisk] SendFtdiskIoctlSync: Device %ws \n",
|
|
partitionNameBuffer
|
|
));
|
|
|
|
status = ClusDiskGetTargetDevice( DiskNumber,
|
|
PartNumber,
|
|
&targetDevice,
|
|
&targetDeviceName,
|
|
NULL,
|
|
NULL,
|
|
FALSE );
|
|
|
|
FREE_DEVICE_NAME_BUFFER( targetDeviceName );
|
|
}
|
|
|
|
if ( !targetDevice ) {
|
|
ClusDiskPrint(( 1,
|
|
"[ClusDisk] SendFtdiskIoctlSync: Failed to get devobj for disk %d partition %d \n",
|
|
DiskNumber,
|
|
PartNumber
|
|
));
|
|
status = STATUS_OBJECT_NAME_NOT_FOUND;
|
|
goto FnExit;
|
|
}
|
|
|
|
//
|
|
// Set the event object to the unsignaled state.
|
|
// It will be used to signal request completion.
|
|
//
|
|
KeInitializeEvent( &event,
|
|
NotificationEvent,
|
|
FALSE );
|
|
|
|
irp = IoBuildDeviceIoControlRequest( Ioctl,
|
|
targetDevice,
|
|
(LPVOID)ClusDiskOfflineOnlineGuid,
|
|
sizeof(GUID),
|
|
NULL,
|
|
0,
|
|
FALSE,
|
|
&event,
|
|
&ioStatusBlock );
|
|
if ( !irp ) {
|
|
ClusDiskPrint(( 1,
|
|
"[ClusDisk] SendFtdiskIoctlSync: Failed to build IRP for IOCTL %X \n", Ioctl ));
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto FnExit;
|
|
}
|
|
|
|
status = IoCallDriver( targetDevice,
|
|
irp );
|
|
|
|
if ( STATUS_PENDING == status ) {
|
|
|
|
KeWaitForSingleObject( &event,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,
|
|
NULL );
|
|
|
|
status = ioStatusBlock.Status;
|
|
}
|
|
|
|
FnExit:
|
|
|
|
if ( !TargetObject ) {
|
|
DEREFERENCE_OBJECT( targetDevice );
|
|
}
|
|
|
|
CDLOG( "SendFtDiskIoctlSync: Exit disk %d part %d status %X IOCTL %X %s ",
|
|
DiskNumber,
|
|
PartNumber,
|
|
status,
|
|
Ioctl,
|
|
VolumeIoctlToString( Ioctl ) );
|
|
|
|
ClusDiskPrint(( 3,
|
|
"[ClusDisk] SendFtDiskIoctlSync: disk %d part %d status %08X IOCTL %X %s \n",
|
|
DiskNumber,
|
|
PartNumber,
|
|
status,
|
|
Ioctl,
|
|
VolumeIoctlToString( Ioctl ) ));
|
|
|
|
return status;
|
|
|
|
} // SendFtdiskIoctlSync
|
|
|
|
|
|
NTSTATUS
|
|
AttachSignatureList(
|
|
PDEVICE_OBJECT DeviceObject,
|
|
PULONG InBuffer,
|
|
ULONG InBufferLen
|
|
)
|
|
{
|
|
|
|
ULONG idx;
|
|
ULONG signature;
|
|
|
|
NTSTATUS status = STATUS_INVALID_PARAMETER;
|
|
NTSTATUS retVal;
|
|
|
|
BOOLEAN stopProcessing = FALSE;
|
|
|
|
UNICODE_STRING signatureName;
|
|
UNICODE_STRING availableName;
|
|
|
|
signatureName.Buffer = NULL;
|
|
availableName.Buffer = NULL;
|
|
|
|
ClusDiskPrint(( 3,
|
|
"[ClusDisk] AttachSignatureList: list at %p, length %d \n",
|
|
InBuffer,
|
|
InBufferLen ));
|
|
|
|
CDLOG( "AttachSignatureList: siglist %p, length %d ", InBuffer, InBufferLen );
|
|
|
|
if ( !InBufferLen ) {
|
|
status = STATUS_SUCCESS;
|
|
goto FnExit;
|
|
}
|
|
|
|
if ( InBuffer &&
|
|
InBufferLen &&
|
|
InBufferLen >= sizeof(ULONG) &&
|
|
( InBufferLen % sizeof(ULONG) == 0 ) ) {
|
|
|
|
//
|
|
// Allocate buffer for Signatures registry key. So we can add
|
|
// the signature.
|
|
//
|
|
status = ClusDiskInitRegistryString( &signatureName,
|
|
CLUSDISK_SIGNATURE_KEYNAME,
|
|
wcslen(CLUSDISK_SIGNATURE_KEYNAME)
|
|
);
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
goto FnExit;
|
|
}
|
|
|
|
//
|
|
// Allocate buffer for AvailableDisks registry key.
|
|
//
|
|
|
|
status = ClusDiskInitRegistryString( &availableName,
|
|
CLUSDISK_AVAILABLE_DISKS_KEYNAME,
|
|
wcslen(CLUSDISK_AVAILABLE_DISKS_KEYNAME) );
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
goto FnExit;
|
|
}
|
|
|
|
for ( idx = 0; idx < InBufferLen / sizeof(ULONG); idx++ ) {
|
|
|
|
signature = InBuffer[idx];
|
|
|
|
ClusDiskPrint((1,
|
|
"[ClusDisk] AttachSignatureList: attaching signature %08X\n",
|
|
signature));
|
|
|
|
CDLOG( "AttachSignatureList: sig %08x", signature );
|
|
|
|
//
|
|
// No signature or system disk signature, don't add it.
|
|
//
|
|
|
|
if ( ( 0 == signature ) || SystemDiskSignature == signature ) {
|
|
ClusDiskPrint((2,
|
|
"[ClusDisk] AttachSignatureList: skipping signature %08X\n",
|
|
signature));
|
|
CDLOG( "AttachSignatureList: skipping sig %08x", signature );
|
|
continue;
|
|
}
|
|
|
|
// We have to force the signature under \Parameters\Signatures because
|
|
// the disk might not be accessible (i.e. reserved by another node) and
|
|
// ClusDiskAttachDevice will only put the signature there if the
|
|
// disk can really be attached.
|
|
|
|
//
|
|
// Create the signature key under \Parameters\Signatures.
|
|
//
|
|
|
|
retVal = ClusDiskAddSignature( &signatureName,
|
|
signature,
|
|
FALSE
|
|
);
|
|
|
|
//
|
|
// Error adding this signature, save the error for return and continue
|
|
// with the signature list.
|
|
//
|
|
|
|
if ( !NT_SUCCESS(retVal) ) {
|
|
|
|
status = retVal;
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Delete the signature key under Parameters\AvailableDisks.
|
|
//
|
|
|
|
ClusDiskDeleteSignature( &availableName,
|
|
signature );
|
|
|
|
//
|
|
// Just try to attach to the device with no bus resets.
|
|
//
|
|
|
|
ClusDiskAttachDevice( signature,
|
|
0,
|
|
DeviceObject->DriverObject,
|
|
FALSE,
|
|
&stopProcessing,
|
|
TRUE ); // Installing disk resource - dismount, then offline
|
|
|
|
}
|
|
|
|
}
|
|
|
|
FnExit:
|
|
|
|
if ( signatureName.Buffer ) {
|
|
ExFreePool( signatureName.Buffer );
|
|
}
|
|
|
|
if ( availableName.Buffer ) {
|
|
ExFreePool( availableName.Buffer );
|
|
}
|
|
|
|
ClusDiskPrint(( 3,
|
|
"[ClusDisk] AttachSignatureList: returning final status %08X \n",
|
|
status ));
|
|
|
|
CDLOG( "AttachSignatureList: returning final status %x ", status );
|
|
|
|
return status;
|
|
|
|
} // AttachSignatureList
|
|
|
|
|
|
NTSTATUS
|
|
DetachSignatureList(
|
|
PDEVICE_OBJECT DeviceObject,
|
|
PULONG InBuffer,
|
|
ULONG InBufferLen
|
|
)
|
|
{
|
|
|
|
ULONG idx;
|
|
ULONG signature;
|
|
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
NTSTATUS retVal;
|
|
|
|
ClusDiskPrint(( 3,
|
|
"[ClusDisk] DetachSignatureList: list at %p, length %d \n",
|
|
InBuffer,
|
|
InBufferLen ));
|
|
|
|
CDLOG( "DetachSignatureList: list %p, length %d ", InBuffer, InBufferLen );
|
|
|
|
if ( !InBufferLen ) {
|
|
status = STATUS_SUCCESS;
|
|
goto FnExit;
|
|
}
|
|
|
|
if ( InBuffer &&
|
|
InBufferLen &&
|
|
InBufferLen >= sizeof(ULONG) &&
|
|
( InBufferLen % sizeof(ULONG) == 0 ) ) {
|
|
|
|
|
|
for ( idx = 0; idx < InBufferLen / sizeof(ULONG); idx++ ) {
|
|
|
|
signature = InBuffer[idx];
|
|
|
|
ClusDiskPrint((1,
|
|
"[ClusDisk] DetachSignatureList: detaching signature %08X\n",
|
|
signature));
|
|
|
|
CDLOG( "DetachSignatureList: sig %08x", signature );
|
|
|
|
// Skip zero signature.
|
|
if ( !signature ) {
|
|
ClusDiskPrint((2,
|
|
"[ClusDisk] DetachSignatureList: skipping signature %08X\n",
|
|
signature));
|
|
|
|
CDLOG( "DetachSignatureList: skipping sig %08x", signature );
|
|
continue;
|
|
}
|
|
|
|
retVal = ClusDiskDetachDevice( signature, DeviceObject->DriverObject );
|
|
|
|
//
|
|
// If any detach fails, return an error. If multiple detachs fail, there
|
|
// is no way to pass back information on which specific signature failed,
|
|
// so simply indicate one of the failures back to the caller.
|
|
// On failure, we still want to continue detaching, so don't break out
|
|
// of the loop.
|
|
//
|
|
|
|
if ( !NT_SUCCESS(retVal) ) {
|
|
status = retVal;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
FnExit:
|
|
|
|
ClusDiskPrint(( 3,
|
|
"[ClusDisk] DetachSignatureList: returning final status %08X \n",
|
|
status ));
|
|
|
|
CDLOG( "DetachSignatureList: returning final status %x ", status );
|
|
|
|
// Always return success.
|
|
// Cluster setup will send a NULL buffer with zero length when rejoining a node.
|
|
|
|
return status;
|
|
|
|
} // DetachSignatureList
|
|
|
|
|
|
NTSTATUS
|
|
CleanupDeviceList(
|
|
PDEVICE_OBJECT DeviceObject
|
|
)
|
|
{
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Queue a work item to remove entries from the ClusDiskDeviceList.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Device that is being removed by the system.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
|
|
PIO_WORKITEM workItem = NULL;
|
|
|
|
CDLOG( "CleanupDeviceList: Entry DO %p", DeviceObject );
|
|
|
|
workItem = IoAllocateWorkItem( DeviceObject );
|
|
|
|
if ( NULL == workItem ) {
|
|
ClusDiskPrint(( 1,
|
|
"[ClusDisk] CleanupDeviceList: Failed to allocate WorkItem \n" ));
|
|
goto FnExit;
|
|
}
|
|
|
|
//
|
|
// Queue the workitem. IoQueueWorkItem will insure that the device object is
|
|
// referenced while the work-item progresses.
|
|
//
|
|
|
|
ClusDiskPrint(( 3,
|
|
"[ClusDisk] CleanupDeviceList: Queuing work item \n" ));
|
|
|
|
IoQueueWorkItem( workItem,
|
|
CleanupDeviceListWorker,
|
|
DelayedWorkQueue,
|
|
workItem );
|
|
|
|
FnExit:
|
|
|
|
CDLOG( "CleanupDeviceList: Exit, DO %p", DeviceObject );
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
} // CleanupDeviceList
|
|
|
|
|
|
VOID
|
|
CleanupDeviceListWorker(
|
|
PDEVICE_OBJECT DeviceObject,
|
|
PVOID Context
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Work item that will walk ClusDiskDeviceList and remove any entries
|
|
that are marked as free.
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
PDEVICE_LIST_ENTRY deviceEntry;
|
|
PDEVICE_LIST_ENTRY lastEntry;
|
|
PDEVICE_LIST_ENTRY nextEntry;
|
|
|
|
ACQUIRE_EXCLUSIVE( &ClusDiskDeviceListLock );
|
|
|
|
deviceEntry = ClusDiskDeviceList;
|
|
lastEntry = NULL;
|
|
while ( deviceEntry ) {
|
|
|
|
if ( deviceEntry->FreePool ) {
|
|
if ( lastEntry == NULL ) {
|
|
ClusDiskDeviceList = deviceEntry->Next;
|
|
} else {
|
|
lastEntry->Next = deviceEntry->Next;
|
|
}
|
|
nextEntry = deviceEntry->Next;
|
|
ExFreePool( deviceEntry );
|
|
deviceEntry = nextEntry;
|
|
continue;
|
|
}
|
|
lastEntry = deviceEntry;
|
|
deviceEntry = deviceEntry->Next;
|
|
}
|
|
|
|
RELEASE_EXCLUSIVE( &ClusDiskDeviceListLock );
|
|
|
|
IoFreeWorkItem( (PIO_WORKITEM)Context );
|
|
|
|
} // CleanupDeviceListWorker
|
|
|
|
|
|
NTSTATUS
|
|
WaitForAttachCompletion(
|
|
PCLUS_DEVICE_EXTENSION DeviceExtension,
|
|
BOOLEAN WaitForInit,
|
|
BOOLEAN CheckPhysDev
|
|
)
|
|
{
|
|
PCLUS_DEVICE_EXTENSION physicalDisk;
|
|
LARGE_INTEGER waitTime;
|
|
NTSTATUS status = STATUS_NO_SUCH_DEVICE;
|
|
ULONG retryCount;
|
|
KIRQL irql;
|
|
|
|
if ( !DeviceExtension ) {
|
|
CDLOG( "WaitForAttachCompletion: DevExt %p zero DevExt ",
|
|
DeviceExtension );
|
|
goto FnExit;
|
|
}
|
|
|
|
//
|
|
// Root device is not layered above anything. Return success.
|
|
//
|
|
|
|
if ( RootBus == DeviceExtension->BusType ) {
|
|
status = STATUS_SUCCESS;
|
|
goto FnExit;
|
|
}
|
|
|
|
//
|
|
// Determine if we are checking both this device and the physical
|
|
// device, or just this device.
|
|
//
|
|
|
|
if ( CheckPhysDev ) {
|
|
|
|
//
|
|
// Check both this device and the physical device.
|
|
//
|
|
|
|
if ( !DeviceExtension->PhysicalDevice ) {
|
|
CDLOG( "WaitForAttachCompletion: DevExt %p no PhysicalDevice ",
|
|
DeviceExtension );
|
|
goto FnExit;
|
|
}
|
|
|
|
physicalDisk = DeviceExtension->PhysicalDevice->DeviceExtension;
|
|
if ( !physicalDisk ) {
|
|
CDLOG( "WaitForAttachCompletion: DevExt %p no PhysicalDevice DevExt",
|
|
DeviceExtension );
|
|
goto FnExit;
|
|
}
|
|
|
|
//
|
|
// If both this device and the physical device have a target device, return
|
|
// success.
|
|
//
|
|
|
|
if ( DeviceExtension->TargetDeviceObject && physicalDisk->TargetDeviceObject ) {
|
|
status = STATUS_SUCCESS;
|
|
goto FnExit;
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// If this device has a target device, return success.
|
|
//
|
|
|
|
if ( DeviceExtension->TargetDeviceObject ) {
|
|
status = STATUS_SUCCESS;
|
|
goto FnExit;
|
|
}
|
|
|
|
}
|
|
|
|
if ( !WaitForInit ) {
|
|
CDLOG( "WaitForAttachCompletion: DevExt %p skipping wait",
|
|
DeviceExtension );
|
|
goto FnExit;
|
|
}
|
|
|
|
irql = KeGetCurrentIrql();
|
|
|
|
if ( PASSIVE_LEVEL != irql ) {
|
|
CDLOG( "WaitForAttachCompletion: DevExt %p bad IRQL %d ",
|
|
DeviceExtension,
|
|
irql );
|
|
goto FnExit;
|
|
}
|
|
|
|
//
|
|
// Wait for 1/2 second between checking intialization completion.
|
|
// Total wait time will be 5 seconds.
|
|
//
|
|
|
|
waitTime.QuadPart = (ULONGLONG)(-5 * 1000 * 1000); // 5,000,000 100 nanosecond units = .5
|
|
retryCount = 10; // 10 * .5 = 5 seconds
|
|
|
|
#if CLUSTER_FREE_ASSERTS
|
|
DbgPrint( "[ClusDisk] WaitForAttachCompletion: DevExt %p waiting... \n", DeviceExtension );
|
|
#endif
|
|
|
|
while ( retryCount-- ) {
|
|
KeDelayExecutionThread( KernelMode,
|
|
FALSE,
|
|
&waitTime );
|
|
|
|
CDLOG( "WaitForAttachCompletion: DevExt %p retryCount %d ",
|
|
DeviceExtension,
|
|
retryCount );
|
|
|
|
#if CLUSTER_FREE_ASSERTS
|
|
DbgPrint( "[ClusDisk] WaitForAttachCompletion: DevExt %p retryCount %d \n",
|
|
DeviceExtension,
|
|
retryCount );
|
|
#endif
|
|
|
|
if ( CheckPhysDev ) {
|
|
|
|
if ( DeviceExtension->TargetDeviceObject && physicalDisk->TargetDeviceObject ) {
|
|
status = STATUS_SUCCESS;
|
|
goto FnExit;
|
|
}
|
|
|
|
} else {
|
|
|
|
if ( DeviceExtension->TargetDeviceObject ) {
|
|
status = STATUS_SUCCESS;
|
|
goto FnExit;
|
|
}
|
|
|
|
}
|
|
|
|
} // while
|
|
|
|
//
|
|
// One more check outside of the wait loop. This should succeed.
|
|
//
|
|
|
|
if ( CheckPhysDev ) {
|
|
|
|
if ( DeviceExtension->TargetDeviceObject && physicalDisk->TargetDeviceObject ) {
|
|
status = STATUS_SUCCESS;
|
|
goto FnExit;
|
|
}
|
|
|
|
} else {
|
|
|
|
if ( DeviceExtension->TargetDeviceObject ) {
|
|
status = STATUS_SUCCESS;
|
|
goto FnExit;
|
|
}
|
|
|
|
}
|
|
|
|
FnExit:
|
|
|
|
// Log message only on error.
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
CDLOG( "WaitForAttachCompletion: DevExt %p returns %08X ",
|
|
DeviceExtension,
|
|
status );
|
|
|
|
ClusDiskPrint(( 3,
|
|
"[ClusDisk] WaitForAttachCompletion: DevExt %p returns %08X \n",
|
|
DeviceExtension,
|
|
status ));
|
|
|
|
#if CLUSTER_FREE_ASSERTS
|
|
DbgPrint("[ClusDisk] Failed wait for attach %08X \n", status );
|
|
DbgBreakPoint();
|
|
#endif
|
|
|
|
}
|
|
|
|
return status;
|
|
|
|
} // WaitForAttachCompletion
|
|
|
|
|
|
VOID
|
|
ReleaseRemoveLockAndWait(
|
|
IN PIO_REMOVE_LOCK RemoveLock,
|
|
IN PVOID Tag
|
|
)
|
|
{
|
|
// Don't expect the displayed RemoveLock values to be correct on a heavily utilized system.
|
|
|
|
ClusDiskPrint(( 1,
|
|
"RELWAIT: RemoveLock %p Tag %p Removed %02X IoCount %08X \n ",
|
|
RemoveLock,
|
|
Tag,
|
|
RemoveLock->Common.Removed,
|
|
RemoveLock->Common.IoCount ));
|
|
|
|
CDLOG( "RELWAIT: RemoveLock %p Tag %p Removed %02X IoCount %08X \n ",
|
|
RemoveLock,
|
|
Tag,
|
|
RemoveLock->Common.Removed,
|
|
RemoveLock->Common.IoCount );
|
|
|
|
#if CLUSTER_FREE_ASSERTS && !DBG
|
|
DbgPrint( "RELWAIT: RemoveLock %p Tag %p Removed %02X IoCount %08X \n ",
|
|
RemoveLock,
|
|
Tag,
|
|
RemoveLock->Common.Removed,
|
|
RemoveLock->Common.IoCount );
|
|
#endif
|
|
|
|
IoReleaseRemoveLockAndWait( RemoveLock, Tag );
|
|
|
|
CDLOG( "RELWAIT: Wait complete: RemoveLock %p \n ",
|
|
RemoveLock );
|
|
|
|
ClusDiskPrint(( 1,
|
|
"RELWAIT: Wait Complete: RemoveLock %p Removed %02X IoCount %08X \n",
|
|
RemoveLock,
|
|
RemoveLock->Common.Removed,
|
|
RemoveLock->Common.IoCount ));
|
|
|
|
#if CLUSTER_FREE_ASSERTS && !DBG
|
|
DbgPrint( "RELWAIT: Wait Complete: RemoveLock %p Removed %02X IoCount %08X \n",
|
|
RemoveLock,
|
|
RemoveLock->Common.Removed,
|
|
RemoveLock->Common.IoCount );
|
|
#endif
|
|
|
|
} // ReleaseRemoveLockAndWait
|
|
|
|
|
|
NTSTATUS
|
|
AcquireRemoveLock(
|
|
IN PIO_REMOVE_LOCK RemoveLock,
|
|
IN OPTIONAL PVOID Tag
|
|
)
|
|
{
|
|
NTSTATUS status;
|
|
|
|
// Don't expect the displayed RemoveLock values to be correct on a heavily utilized system.
|
|
|
|
status = IoAcquireRemoveLock(RemoveLock, Tag);
|
|
|
|
#if DBG
|
|
if ( TrackRemoveLocks || (TrackRemoveLockSpecific == RemoveLock) ) {
|
|
|
|
ClusDiskPrint(( 1,
|
|
"ACQ: RemoveLock %p Tag %p Status %08X \n",
|
|
RemoveLock,
|
|
Tag,
|
|
status ));
|
|
|
|
ClusDiskPrint(( 1,
|
|
"ACQ: Removed %02X IoCount %08X \n",
|
|
RemoveLock->Common.Removed,
|
|
RemoveLock->Common.IoCount ));
|
|
}
|
|
#endif
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
ClusDiskPrint((
|
|
1,
|
|
"[ClusDisk] AcquireRemoveLock for lock %p tag %p failed %08X \n",
|
|
RemoveLock,
|
|
Tag,
|
|
status ));
|
|
CDLOG( "AcquireRemoveLock for lock %p tag %p failed %08X ",
|
|
RemoveLock,
|
|
Tag,
|
|
status );
|
|
}
|
|
|
|
return status;
|
|
|
|
} // AcquireRemoveLock
|
|
|
|
|
|
|
|
#if DBG
|
|
VOID
|
|
ClusDiskDebugPrint(
|
|
ULONG PrintLevel,
|
|
PCHAR DebugMessage,
|
|
...
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Debug print routine.
|
|
|
|
Arguments:
|
|
|
|
PrintLevel - The Debug Print Level.
|
|
|
|
DebugMessage - The Debug Message Format String, plus additional args.
|
|
|
|
Return:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
va_list args;
|
|
|
|
va_start( args, DebugMessage);
|
|
|
|
#if !defined(WMI_TRACING)
|
|
if ( PrintLevel <= ClusDiskPrintLevel ) {
|
|
CHAR buffer[256];
|
|
|
|
if ( SUCCEEDED( StringCchVPrintf( buffer,
|
|
RTL_NUMBER_OF(buffer),
|
|
DebugMessage,
|
|
args ) ) ) {
|
|
DbgPrint( buffer );
|
|
}
|
|
}
|
|
#else
|
|
if ( PrintLevel <= ClusDiskPrintLevel || WPP_LEVEL_ENABLED(LEGACY) ) {
|
|
CHAR buffer[256];
|
|
|
|
if ( SUCCEEDED( StringCchVPrintf( buffer,
|
|
RTL_NUMBER_OF(buffer),
|
|
DebugMessage,
|
|
args ) ) ) {
|
|
|
|
if (PrintLevel <= ClusDiskPrintLevel) {
|
|
DbgPrint( buffer );
|
|
}
|
|
CDLOGF(LEGACY,"LegacyLogging %!ARSTR!", buffer );
|
|
|
|
}
|
|
}
|
|
#endif
|
|
|
|
va_end( args );
|
|
}
|
|
#endif // DBG
|
|
|
|
|
|
#if DBG
|
|
|
|
VOID
|
|
ReleaseRemoveLock(
|
|
IN PIO_REMOVE_LOCK RemoveLock,
|
|
IN PVOID Tag
|
|
)
|
|
{
|
|
// Don't expect the displayed RemoveLock values to be correct on a heavily utilized system.
|
|
|
|
IoReleaseRemoveLock(RemoveLock, Tag);
|
|
|
|
if ( TrackRemoveLocks || (TrackRemoveLockSpecific == RemoveLock) ) {
|
|
ClusDiskPrint(( 1,
|
|
"REL: RemoveLock %p Tag %p \n ",
|
|
RemoveLock,
|
|
Tag ));
|
|
|
|
ClusDiskPrint(( 1,
|
|
"REL: Removed %02X IoCount %08X \n",
|
|
RemoveLock->Common.Removed,
|
|
RemoveLock->Common.IoCount ));
|
|
}
|
|
|
|
if (TrackRemoveLocksEnableChecks) {
|
|
|
|
//
|
|
// The IoCount should never be less than 1 (especially never a negative value).
|
|
// Is it possible that a race condition could occur because we are printing the
|
|
// value from the RemoveLock and the remove could have just occurred. RemoveLock
|
|
// IoCount should never be less than zero.
|
|
//
|
|
|
|
if ( !RemoveLock->Common.Removed ) {
|
|
ASSERTMSG( "REL: RemoveLock IoCount possibly corrupt (less than 1) ",
|
|
(RemoveLock->Common.IoCount >= 1 ) );
|
|
}
|
|
|
|
//
|
|
// IoCount == 1 is OK, it just means there is no I/O outstanding on the device.
|
|
//
|
|
|
|
// ASSERTMSG("REL: IoCount == 1 - check stack ", (RemoveLock->Common.IoCount != 1 ));
|
|
}
|
|
|
|
} // ReleaseRemoveLock
|
|
|
|
|
|
PCHAR
|
|
PnPMinorFunctionString(
|
|
UCHAR MinorFunction
|
|
)
|
|
{
|
|
switch (MinorFunction)
|
|
{
|
|
case IRP_MN_START_DEVICE:
|
|
return "IRP_MN_START_DEVICE";
|
|
case IRP_MN_QUERY_REMOVE_DEVICE:
|
|
return "IRP_MN_QUERY_REMOVE_DEVICE";
|
|
case IRP_MN_REMOVE_DEVICE:
|
|
return "IRP_MN_REMOVE_DEVICE";
|
|
case IRP_MN_CANCEL_REMOVE_DEVICE:
|
|
return "IRP_MN_CANCEL_REMOVE_DEVICE";
|
|
case IRP_MN_STOP_DEVICE:
|
|
return "IRP_MN_STOP_DEVICE";
|
|
case IRP_MN_QUERY_STOP_DEVICE:
|
|
return "IRP_MN_QUERY_STOP_DEVICE";
|
|
case IRP_MN_CANCEL_STOP_DEVICE:
|
|
return "IRP_MN_CANCEL_STOP_DEVICE";
|
|
case IRP_MN_QUERY_DEVICE_RELATIONS:
|
|
return "IRP_MN_QUERY_DEVICE_RELATIONS";
|
|
case IRP_MN_QUERY_INTERFACE:
|
|
return "IRP_MN_QUERY_INTERFACE";
|
|
case IRP_MN_QUERY_CAPABILITIES:
|
|
return "IRP_MN_QUERY_CAPABILITIES";
|
|
case IRP_MN_QUERY_RESOURCES:
|
|
return "IRP_MN_QUERY_RESOURCES";
|
|
case IRP_MN_QUERY_RESOURCE_REQUIREMENTS:
|
|
return "IRP_MN_QUERY_RESOURCE_REQUIREMENTS";
|
|
case IRP_MN_QUERY_DEVICE_TEXT:
|
|
return "IRP_MN_QUERY_DEVICE_TEXT";
|
|
case IRP_MN_FILTER_RESOURCE_REQUIREMENTS:
|
|
return "IRP_MN_FILTER_RESOURCE_REQUIREMENTS";
|
|
case IRP_MN_READ_CONFIG:
|
|
return "IRP_MN_READ_CONFIG";
|
|
case IRP_MN_WRITE_CONFIG:
|
|
return "IRP_MN_WRITE_CONFIG";
|
|
case IRP_MN_EJECT:
|
|
return "IRP_MN_EJECT";
|
|
case IRP_MN_SET_LOCK:
|
|
return "IRP_MN_SET_LOCK";
|
|
case IRP_MN_QUERY_ID:
|
|
return "IRP_MN_QUERY_ID";
|
|
case IRP_MN_QUERY_PNP_DEVICE_STATE:
|
|
return "IRP_MN_QUERY_PNP_DEVICE_STATE";
|
|
case IRP_MN_QUERY_BUS_INFORMATION:
|
|
return "IRP_MN_QUERY_BUS_INFORMATION";
|
|
case IRP_MN_DEVICE_USAGE_NOTIFICATION:
|
|
return "IRP_MN_DEVICE_USAGE_NOTIFICATION";
|
|
case IRP_MN_SURPRISE_REMOVAL:
|
|
return "IRP_MN_SURPRISE_REMOVAL";
|
|
case IRP_MN_QUERY_LEGACY_BUS_INFORMATION:
|
|
return "IRP_MN_QUERY_LEGACY_BUS_INFORMATION";
|
|
default:
|
|
return "Unknown PNP IRP";
|
|
}
|
|
|
|
} // PnPMinorFunctionString
|
|
|
|
|
|
PCHAR
|
|
BoolToString(
|
|
BOOLEAN Value
|
|
)
|
|
{
|
|
if ( Value ) {
|
|
return "TRUE";
|
|
}
|
|
return "FALSE";
|
|
|
|
} // BoolToString
|
|
|
|
|
|
PCHAR
|
|
DiskStateToString(
|
|
ULONG DiskState
|
|
)
|
|
{
|
|
switch ( DiskState ) {
|
|
|
|
case DiskOffline:
|
|
return "DiskOffline (0)";
|
|
|
|
case DiskOnline:
|
|
return "DiskOnline (1)";
|
|
|
|
case DiskFailed:
|
|
return "DiskFailed (2)";
|
|
|
|
case DiskStalled:
|
|
return "DiskStalled (3)";
|
|
|
|
case DiskOfflinePending:
|
|
return "DiskOfflinePending (4)";
|
|
|
|
default:
|
|
return "Unknown DiskState";
|
|
}
|
|
|
|
} // DiskStateToString
|
|
|
|
#endif
|
|
|