/*++ Copyright (C) 1991-5 Microsoft Corporation Module Name: partitio.cxx Abstract: This module contains the code specific to partitions for the fault tolerance driver. Author: Bob Rinne (bobri) 2-Feb-1992 Mike Glass (mglass) Norbert Kusters 2-Feb-1995 Environment: kernel mode only Notes: Revision History: --*/ extern "C" { #include } #include class REPLACE_BAD_SECTOR_CONTEXT : public WORK_QUEUE_ITEM { public: PDEVICE_OBJECT TargetObject; PIRP Irp; }; typedef REPLACE_BAD_SECTOR_CONTEXT *PREPLACE_BAD_SECTOR_CONTEXT; NTSTATUS PartitionBroadcastIrpCompletionRoutine( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID CompletionContext ); #ifdef ALLOC_PRAGMA #pragma code_seg("PAGE") #endif NTSTATUS PARTITION::Initialize( IN OUT PROOT_EXTENSION RootExtension, IN FT_LOGICAL_DISK_ID LogicalDiskId, IN OUT PDEVICE_OBJECT TargetObject, IN OUT PDEVICE_OBJECT WholeDiskPdo ) /*++ Routine Description: Initialize routine for FT_VOLUME of type PARTITION. Arguments: RootExtension - Supplies the root device extension. LogicalDiskId - Supplies the logical disk id for this volume. TargetObject - Supplies the partition to which transfer requests are forwarded to. WholeDiskPdo - Supplies the whole disk for this partition. Return Value: None. --*/ { KEVENT event; PIRP irp; DISK_GEOMETRY geometry; IO_STATUS_BLOCK ioStatus; NTSTATUS status; ULONG diskNumber, otherDiskNumber; LONGLONG offset, partitionSize; FT_VOLUME::Initialize(RootExtension, LogicalDiskId); _targetObject = TargetObject; _wholeDiskPdo = WholeDiskPdo; KeInitializeEvent(&event, NotificationEvent, FALSE); irp = IoBuildDeviceIoControlRequest(IOCTL_DISK_GET_DRIVE_GEOMETRY, TargetObject, NULL, 0, &geometry, sizeof(geometry), FALSE, &event, &ioStatus); if (!irp) { return STATUS_INSUFFICIENT_RESOURCES; } status = IoCallDriver(TargetObject, irp); if (status == STATUS_PENDING) { KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL); status = ioStatus.Status; } if (!NT_SUCCESS(status)) { return status; } _sectorSize = geometry.BytesPerSector; status = FtpQueryPartitionInformation(RootExtension, TargetObject, &diskNumber, &_partitionOffset, NULL, NULL, &_partitionLength, NULL, NULL, NULL, NULL); if (!NT_SUCCESS(status)) { return status; } if (!_diskInfoSet->QueryFtPartitionInformation(LogicalDiskId, &offset, NULL, &otherDiskNumber, NULL, &partitionSize)) { return STATUS_INVALID_PARAMETER; } if (partitionSize > 0 && partitionSize <= _partitionLength) { _partitionLength = partitionSize; } if (offset != _partitionOffset || diskNumber != otherDiskNumber) { return STATUS_INVALID_PARAMETER; } _emergencyIrp = IoAllocateIrp(_targetObject->StackSize, FALSE); if (!_emergencyIrp) { return STATUS_INSUFFICIENT_RESOURCES; } _emergencyIrpInUse = FALSE; InitializeListHead(&_emergencyIrpQueue); return STATUS_SUCCESS; } FT_LOGICAL_DISK_TYPE PARTITION::QueryLogicalDiskType( ) /*++ Routine Description: This routine returns the type of the logical disk. Arguments: None. Return Value: The type of the logical disk. --*/ { return FtPartition; } NTSTATUS PARTITION::OrphanMember( IN USHORT MemberNumber, IN FT_COMPLETION_ROUTINE CompletionRoutine, IN PVOID Context ) /*++ Routine Description: This routine tries to orphan the given member of this logical disk. A completion routine will be called if and only if this attempt is successful. Arguments: MemberNumber - Supplies the member number to orphan. CompletionRoutine - Supplies the completion routine. Context - Supplies the completion routine context. Return Value: NTSTATUS --*/ { return STATUS_INVALID_PARAMETER; } NTSTATUS PARTITION::RegenerateMember( IN USHORT MemberNumber, IN OUT PFT_VOLUME NewMember, IN FT_COMPLETION_ROUTINE CompletionRoutine, IN PVOID Context ) /*++ Routine Description: This routine regenerates the given member of this volume with the given volume. Arguments: MemberNumber - Supplies the member number to regenerate. NewMember - Supplies the new member to regenerate to. CompletionRoutine - Supplies the completion routine. Context - Supplies the completion routine context. Return Value: NTSTATUS --*/ { return STATUS_INVALID_PARAMETER; } VOID PartitionReplaceBadSectorWorker( IN PVOID Context ) { PREPLACE_BAD_SECTOR_CONTEXT context = (PREPLACE_BAD_SECTOR_CONTEXT) Context; IoCallDriver(context->TargetObject, context->Irp); } VOID PARTITION::StopSyncOperations( ) /*++ Routine Description: This routine stops all sync operations. Arguments: None. Return Value: None. --*/ { } VOID PARTITION::BroadcastIrp( IN PIRP Irp, IN FT_COMPLETION_ROUTINE CompletionRoutine, IN PVOID Context ) /*++ Routine Description: This routine broadcasts a copy of the given IRP to every partition that is a member of the logical disk. Arguments: Irp - Supplies the I/O request packet. CompletionRoutine - Supplies the routine to be called when the operation completes. Context - Supplies the completion routine context. Return Value: None. --*/ { PIRP irp; PIO_STACK_LOCATION irpSp, sp; PFT_COMPLETION_ROUTINE_CONTEXT completionContext; irp = IoAllocateIrp(_targetObject->StackSize, FALSE); if (!irp) { CompletionRoutine(Context, STATUS_INSUFFICIENT_RESOURCES); return; } completionContext = (PFT_COMPLETION_ROUTINE_CONTEXT) ExAllocatePool(NonPagedPool, sizeof(FT_COMPLETION_ROUTINE_CONTEXT)); if (!completionContext) { IoFreeIrp(irp); CompletionRoutine(Context, STATUS_INSUFFICIENT_RESOURCES); return; } completionContext->CompletionRoutine = CompletionRoutine; completionContext->Context = Context; irpSp = IoGetNextIrpStackLocation(irp); sp = IoGetCurrentIrpStackLocation(Irp); *irpSp = *sp; IoSetCompletionRoutine(irp, PartitionBroadcastIrpCompletionRoutine, completionContext, TRUE, TRUE, TRUE); IoCallDriver(_targetObject, irp); } PFT_VOLUME PARTITION::GetParentLogicalDisk( IN PFT_VOLUME Volume ) /*++ Routine Description: This routine returns the parent of the given logical disk within this volume. Arguments: Volume - Supplies the sub-volume of which we are looking for the parent. Return Value: The parent volume or NULL; --*/ { return NULL; } VOID PARTITION::SetDirtyBit( IN BOOLEAN IsDirty, IN FT_COMPLETION_ROUTINE CompletionRoutine, IN PVOID Context ) /*++ Routine Description: This routine sets the dirty bit on the volume. This bit is used at startup to determine whether or not there was a clean shutdown. Arguments: IsDirty - Supplies the value of the dirty bit. Return Value: None. --*/ { if (CompletionRoutine) { CompletionRoutine(Context, STATUS_SUCCESS); } } NTSTATUS PARTITION::CheckIo( OUT PBOOLEAN IsIoOk ) /*++ Routine Description: This routine returns whether or not IO is possible on the given partition. Arguments: IsIoOk - Returns the state of IO. Return Value: NTSTATUS --*/ { PVOID buffer; LARGE_INTEGER offset; KEVENT event; PIRP irp; IO_STATUS_BLOCK ioStatus; NTSTATUS status; PIO_STACK_LOCATION irpSp; buffer = ExAllocatePool(NonPagedPoolCacheAligned, PAGE_SIZE); if (!buffer) { return STATUS_INSUFFICIENT_RESOURCES; } offset.QuadPart = 0; KeInitializeEvent(&event, NotificationEvent, FALSE); irp = IoBuildSynchronousFsdRequest(IRP_MJ_READ, _targetObject, buffer, PAGE_SIZE, &offset, &event, &ioStatus); if (!irp) { ExFreePool(buffer); return STATUS_INSUFFICIENT_RESOURCES; } irpSp = IoGetNextIrpStackLocation(irp); irpSp->Flags = SL_OVERRIDE_VERIFY_VOLUME; status = IoCallDriver(_targetObject, irp); if (status == STATUS_PENDING) { KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL); status = ioStatus.Status; } if (FsRtlIsTotalDeviceFailure(status)) { *IsIoOk = FALSE; } else { *IsIoOk = TRUE; } ExFreePool(buffer); return STATUS_SUCCESS; } NTSTATUS PARTITION::SetPartitionType( IN UCHAR PartitionType ) /*++ Routine Description: This routine sets the partition type on all the members of the FT set. Arguments: PartitionType - Supplies the partition type. Return Value: NTSTATUS --*/ { KEVENT event; SET_PARTITION_INFORMATION partInfo; PIRP irp; IO_STATUS_BLOCK ioStatus; NTSTATUS status; KeInitializeEvent(&event, NotificationEvent, FALSE); partInfo.PartitionType = (PartitionType | 0x80); irp = IoBuildDeviceIoControlRequest(IOCTL_DISK_SET_PARTITION_INFO, _targetObject, &partInfo, sizeof(partInfo), NULL, 0, FALSE, &event, &ioStatus); if (!irp) { return STATUS_INSUFFICIENT_RESOURCES; } status = IoCallDriver(_targetObject, irp); if (status == STATUS_PENDING) { KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL); status = ioStatus.Status; } return status; } UCHAR PARTITION::QueryPartitionType( ) /*++ Routine Description: This routine queries the partition type. Arguments: None. Return Value: The partition type. --*/ { KEVENT event; PIRP irp; PARTITION_INFORMATION partInfo; IO_STATUS_BLOCK ioStatus; NTSTATUS status; KeInitializeEvent(&event, NotificationEvent, FALSE); irp = IoBuildDeviceIoControlRequest(IOCTL_DISK_GET_PARTITION_INFO, _targetObject, NULL, 0, &partInfo, sizeof(partInfo), FALSE, &event, &ioStatus); if (!irp) { return 0; } status = IoCallDriver(_targetObject, irp); if (status == STATUS_PENDING) { KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL); status = ioStatus.Status; } if (!NT_SUCCESS(status)) { return 0; } return partInfo.PartitionType; } UCHAR PARTITION::QueryStackSize( ) /*++ Routine Description: This routine queries IRP stack size. Arguments: None. Return Value: The IRP stack size. --*/ { return _targetObject->StackSize; } VOID PARTITION::CreateLegacyNameLinks( IN PUNICODE_STRING DeviceName ) /*++ Routine Description: This routine creates the \Device\HarddiskN\PartitionM links for this object to the given device name. Arguments: DeviceName - Supplies the device name. Return Value: None. --*/ { NTSTATUS status; ULONG diskNumber, partitionNumber; WCHAR buf[80]; UNICODE_STRING symName; status = FtpQueryPartitionInformation(_rootExtension, _targetObject, &diskNumber, NULL, &partitionNumber, NULL, NULL, NULL, NULL, NULL, NULL); if (!NT_SUCCESS(status)) { return; } swprintf(buf, L"\\Device\\Harddisk%d\\Partition%d", diskNumber, partitionNumber); RtlInitUnicodeString(&symName, buf); IoDeleteSymbolicLink(&symName); if (DeviceName) { IoCreateSymbolicLink(&symName, DeviceName); } } NTSTATUS PARTITION::QueryPhysicalOffsets( IN LONGLONG LogicalOffset, OUT PVOLUME_PHYSICAL_OFFSET* PhysicalOffsets, OUT PULONG NumberOfPhysicalOffsets ) /*++ Routine Description: This routine returns physical disk and offset for a given volume logical offset. Arguments: LogicalOffset - Supplies the logical offset PhysicalOffsets - Returns the physical offsets NumberOfPhysicalOffsets - Returns the number of physical offsets Return Value: NTSTATUS --*/ { NTSTATUS status; ULONG diskNumber; PVOLUME_PHYSICAL_OFFSET physicalOffset; status = FtpQueryPartitionInformation(_rootExtension, _targetObject, &diskNumber, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); if (!NT_SUCCESS(status)) { return status; } if (LogicalOffset < 0 || _partitionLength <= LogicalOffset) { return STATUS_INVALID_PARAMETER; } physicalOffset = (PVOLUME_PHYSICAL_OFFSET) ExAllocatePool(PagedPool, sizeof(VOLUME_PHYSICAL_OFFSET)); if (!physicalOffset) { return STATUS_INSUFFICIENT_RESOURCES; } physicalOffset->DiskNumber = diskNumber; physicalOffset->Offset = _partitionOffset + LogicalOffset; *PhysicalOffsets = physicalOffset; *NumberOfPhysicalOffsets = 1; return status; } NTSTATUS PARTITION::QueryLogicalOffset( IN PVOLUME_PHYSICAL_OFFSET PhysicalOffset, OUT PLONGLONG LogicalOffset ) /*++ Routine Description: This routine returns the volume logical offset for a given disk number and physical offset. Arguments: PhysicalOffset - Supplies the physical offset LogicalOffset - Returns the logical offset Return Value: NTSTATUS --*/ { NTSTATUS status; ULONG diskNumber; PVOLUME_PHYSICAL_OFFSET physicalOffset; status = FtpQueryPartitionInformation(_rootExtension, _targetObject, &diskNumber, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); if (!NT_SUCCESS(status)) { return status; } if (PhysicalOffset->DiskNumber != diskNumber || PhysicalOffset->Offset < _partitionOffset || _partitionOffset + _partitionLength <= PhysicalOffset->Offset) { return STATUS_INVALID_PARAMETER; } *LogicalOffset = PhysicalOffset->Offset - _partitionOffset; return status; } #ifdef ALLOC_PRAGMA #pragma code_seg("PAGELK") #endif PARTITION::~PARTITION( ) { if (_emergencyIrp) { IoFreeIrp(_emergencyIrp); _emergencyIrp = NULL; } } USHORT PARTITION::QueryNumberOfMembers( ) /*++ Routine Description: This routine returns the number of members in this volume. Arguments: None. Return Value: 0 - A volume of type partition has no members. --*/ { return 0; } PFT_VOLUME PARTITION::GetMember( IN USHORT MemberNumber ) /*++ Routine Description: This routine returns the 'MemberNumber'th member of this volume. Arguments: MemberNumber - Supplies the zero based member number desired. Return Value: A pointer to the 'MemberNumber'th member or NULL if no such member. --*/ { ASSERT(FALSE); return NULL; } NTSTATUS PartitionTransferCompletionRoutine( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID TransferPacket ) /*++ Routine Description: Completion routine for PARTITION::Transfer function. Arguments: Irp - Supplies the IRP. TransferPacket - Supplies the transfer packet. Return Value: STATUS_MORE_PROCESSING_REQUIRED --*/ { PTRANSFER_PACKET transferPacket = (PTRANSFER_PACKET) TransferPacket; PPARTITION t = (PPARTITION) transferPacket->TargetVolume; KIRQL irql; PLIST_ENTRY l; PIRP irp; PTRANSFER_PACKET p; PIO_STACK_LOCATION irpSp; transferPacket->IoStatus = Irp->IoStatus; if (Irp == transferPacket->OriginalIrp) { transferPacket->CompletionRoutine(transferPacket); return STATUS_MORE_PROCESSING_REQUIRED; } if (Irp->AssociatedIrp.SystemBuffer) { ExFreePool(Irp->AssociatedIrp.SystemBuffer); } if (Irp == t->_emergencyIrp) { for (;;) { KeAcquireSpinLock(&t->_spinLock, &irql); if (IsListEmpty(&t->_emergencyIrpQueue)) { t->_emergencyIrpInUse = FALSE; KeReleaseSpinLock(&t->_spinLock, irql); break; } l = RemoveHeadList(&t->_emergencyIrpQueue); KeReleaseSpinLock(&t->_spinLock, irql); irp = IoAllocateIrp(t->_targetObject->StackSize, FALSE); if (!irp) { irp = t->_emergencyIrp; IoReuseIrp(irp, STATUS_SUCCESS); } p = CONTAINING_RECORD(l, TRANSFER_PACKET, QueueEntry); irpSp = IoGetNextIrpStackLocation(irp); irp->MdlAddress = p->Mdl; irpSp->Parameters.Write.ByteOffset.QuadPart = p->Offset; irpSp->Parameters.Write.Length = p->Length; if (p->ReadPacket) { irpSp->MajorFunction = IRP_MJ_READ; } else { irpSp->MajorFunction = IRP_MJ_WRITE; } irpSp->DeviceObject = t->_targetObject; irp->Tail.Overlay.Thread = p->Thread; irpSp->Flags = p->IrpFlags; IoSetCompletionRoutine(irp, PartitionTransferCompletionRoutine, p, TRUE, TRUE, TRUE); if (irp == Irp) { IoCallDriver(t->_targetObject, irp); break; } else { IoCallDriver(t->_targetObject, irp); } } } else { IoFreeIrp(Irp); } transferPacket->CompletionRoutine(transferPacket); return STATUS_MORE_PROCESSING_REQUIRED; } VOID PARTITION::Transfer( IN OUT PTRANSFER_PACKET TransferPacket ) /*++ Routine Description: Transfer routine for PARTITION type FT_VOLUME. Basically, just pass the request down to the target object. Arguments: TransferPacket - Supplies the transfer packet. Return Value: None. --*/ { KIRQL irql; PIRP irp; PIO_STACK_LOCATION irpSp; PVERIFY_INFORMATION verifyInfo; irp = TransferPacket->OriginalIrp; if (!irp) { irp = IoAllocateIrp(_targetObject->StackSize, FALSE); if (!irp) { if (!TransferPacket->Mdl) { TransferPacket->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; TransferPacket->IoStatus.Information = 0; TransferPacket->CompletionRoutine(TransferPacket); return; } KeAcquireSpinLock(&_spinLock, &irql); if (_emergencyIrpInUse) { InsertTailList(&_emergencyIrpQueue, &TransferPacket->QueueEntry); KeReleaseSpinLock(&_spinLock, irql); return; } _emergencyIrpInUse = TRUE; KeReleaseSpinLock(&_spinLock, irql); irp = _emergencyIrp; IoReuseIrp(irp, STATUS_SUCCESS); } } irpSp = IoGetNextIrpStackLocation(irp); if (TransferPacket->Mdl) { irp->MdlAddress = TransferPacket->Mdl; irpSp->Parameters.Write.ByteOffset.QuadPart = TransferPacket->Offset; irpSp->Parameters.Write.Length = TransferPacket->Length; if (TransferPacket->ReadPacket) { irpSp->MajorFunction = IRP_MJ_READ; } else { irpSp->MajorFunction = IRP_MJ_WRITE; } } else { // Since there is no MDL, this is a verify request. verifyInfo = (PVERIFY_INFORMATION) ExAllocatePool(NonPagedPool, sizeof(VERIFY_INFORMATION)); if (!verifyInfo) { IoFreeIrp(irp); TransferPacket->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; TransferPacket->IoStatus.Information = 0; TransferPacket->CompletionRoutine(TransferPacket); return; } verifyInfo->StartingOffset.QuadPart = TransferPacket->Offset; verifyInfo->Length = TransferPacket->Length; irp->AssociatedIrp.SystemBuffer = verifyInfo; irpSp->Parameters.DeviceIoControl.OutputBufferLength = 0; irpSp->Parameters.DeviceIoControl.InputBufferLength = sizeof(VERIFY_INFORMATION); irpSp->Parameters.DeviceIoControl.IoControlCode = IOCTL_DISK_VERIFY; irpSp->Parameters.DeviceIoControl.Type3InputBuffer = NULL; irpSp->MajorFunction = IRP_MJ_DEVICE_CONTROL; } irpSp->DeviceObject = _targetObject; irp->Tail.Overlay.Thread = TransferPacket->Thread; irpSp->Flags = TransferPacket->IrpFlags; IoSetCompletionRoutine(irp, PartitionTransferCompletionRoutine, TransferPacket, TRUE, TRUE, TRUE); IoCallDriver(_targetObject, irp); } VOID PARTITION::ReplaceBadSector( IN OUT PTRANSFER_PACKET TransferPacket ) /*++ Routine Description: This routine attempts to fix the given bad sector by performing a reassign blocks ioctl. Arguments: TransferPacket - Supplies the transfer packet. Return Value: None. --*/ { PIRP irp; PIO_STACK_LOCATION irpSp; PREASSIGN_BLOCKS badBlock; ULONG n, size, first, i; PREPLACE_BAD_SECTOR_CONTEXT context; irp = IoAllocateIrp(_targetObject->StackSize, FALSE); if (!irp) { TransferPacket->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; TransferPacket->IoStatus.Information = 0; TransferPacket->CompletionRoutine(TransferPacket); return; } n = TransferPacket->Length/_sectorSize; size = FIELD_OFFSET(REASSIGN_BLOCKS, BlockNumber) + n*sizeof(ULONG); badBlock = (PREASSIGN_BLOCKS) ExAllocatePool(NonPagedPool, size); if (!badBlock) { IoFreeIrp(irp); TransferPacket->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; TransferPacket->IoStatus.Information = 0; TransferPacket->CompletionRoutine(TransferPacket); return; } badBlock->Reserved = 0; badBlock->Count = 1; first = (ULONG) ((TransferPacket->Offset + _partitionOffset)/_sectorSize); for (i = 0; i < n; i++) { badBlock->BlockNumber[i] = first + i; } irp->AssociatedIrp.SystemBuffer = badBlock; irpSp = IoGetNextIrpStackLocation(irp); irpSp->Parameters.DeviceIoControl.OutputBufferLength = 0; irpSp->Parameters.DeviceIoControl.InputBufferLength = size; irpSp->Parameters.DeviceIoControl.IoControlCode = IOCTL_DISK_REASSIGN_BLOCKS; irpSp->Parameters.DeviceIoControl.Type3InputBuffer = NULL; irpSp->MajorFunction = IRP_MJ_DEVICE_CONTROL; irpSp->DeviceObject = _targetObject; irp->Tail.Overlay.Thread = TransferPacket->Thread; irpSp->Flags = TransferPacket->IrpFlags; IoSetCompletionRoutine(irp, PartitionTransferCompletionRoutine, TransferPacket, TRUE, TRUE, TRUE); context = (PREPLACE_BAD_SECTOR_CONTEXT) ExAllocatePool(NonPagedPool, sizeof(REPLACE_BAD_SECTOR_CONTEXT)); if (!context) { ExFreePool(badBlock); IoFreeIrp(irp); TransferPacket->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; TransferPacket->IoStatus.Information = 0; TransferPacket->CompletionRoutine(TransferPacket); return; } ExInitializeWorkItem(context, PartitionReplaceBadSectorWorker, context); context->TargetObject = _targetObject; context->Irp = irp; FtpQueueWorkItem(_rootExtension, context); } VOID PARTITION::StartSyncOperations( IN BOOLEAN RegenerateOrphans, IN FT_COMPLETION_ROUTINE CompletionRoutine, IN PVOID Context ) /*++ Routine Description: This routine restarts any regenerate or initialize requests that were suspended because of a reboot. The volume examines the member state of all of its constituents and restarts any regenerations pending. Arguments: RegenerateOrphans - Supplies whether or not to try and regenerate orphaned members. CompletionRoutine - Supplies the completion routine. Context - Supplies the context for the completion routine. Return Value: None. --*/ { CompletionRoutine(Context, STATUS_SUCCESS); } NTSTATUS PartitionBroadcastIrpCompletionRoutine( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID CompletionContext ) /*++ Routine Description: Completion routine for PARTITION::BroadcastIrp functions. Arguments: Irp - Supplies the IRP. CompletionContext - Supplies the completion context. Return Value: STATUS_MORE_PROCESSING_REQUIRED --*/ { PFT_COMPLETION_ROUTINE_CONTEXT completionContext; completionContext = (PFT_COMPLETION_ROUTINE_CONTEXT) CompletionContext; completionContext->CompletionRoutine(completionContext->Context, Irp->IoStatus.Status); IoFreeIrp(Irp); ExFreePool(CompletionContext); return STATUS_MORE_PROCESSING_REQUIRED; } ULONG PARTITION::QuerySectorSize( ) /*++ Routine Description: Returns the sector size for the volume. Arguments: None. Return Value: The volume sector size in bytes. --*/ { return _sectorSize; } LONGLONG PARTITION::QueryVolumeSize( ) /*++ Routine Description: Returns the number of bytes on the entire volume. Arguments: None. Return Value: The volume size in bytes. --*/ { return _partitionLength; } PFT_VOLUME PARTITION::GetContainedLogicalDisk( IN FT_LOGICAL_DISK_ID LogicalDiskId ) /*++ Routine Description: This routine returns TRUE if the given logical disk id represents this logical disk or if this logical disk contains the given logical disk id either directly or indirectly. Arguments: LogicalDiskId - Supplies the logical disk id that we are searching for. Return Value: FALSE - The given logical disk id is not contained in this logical disk. TRUE - The given logical disk id is contained in this logical disk. --*/ { if (LogicalDiskId == QueryLogicalDiskId()) { return this; } return NULL; } PFT_VOLUME PARTITION::GetContainedLogicalDisk( IN PDEVICE_OBJECT TargetObject ) /*++ Routine Description: This routine returns TRUE if the given logical disk id represents this logical disk or if this logical disk contains the given logical disk id either directly or indirectly. Arguments: TargetObject - Supplies the target object. Return Value: FALSE - The given logical disk id is not contained in this logical disk. TRUE - The given logical disk id is contained in this logical disk. --*/ { if (TargetObject == _targetObject) { return this; } return NULL; } PFT_VOLUME PARTITION::GetContainedLogicalDisk( IN ULONG Signature, IN LONGLONG Offset ) /*++ Routine Description: This routine returns TRUE if the given logical disk id represents this logical disk or if this logical disk contains the given logical disk id either directly or indirectly. Arguments: Signature - Supplies the signature. Offset - Supplies the partition offset. Return Value: FALSE - The given logical disk id is not contained in this logical disk. TRUE - The given logical disk id is contained in this logical disk. --*/ { if (Offset != _partitionOffset) { return NULL; } if (Signature == FtpQueryDiskSignature(_wholeDiskPdo)) { return this; } return NULL; } VOID PARTITION::SetMember( IN USHORT MemberNumber, IN PFT_VOLUME Member ) /*++ Routine Description: This routine sets the given member in this volume. Arguments: MemberNumber - Supplies the member number. Member - Supplies the member. Return Value: None. --*/ { ASSERT(FALSE); } BOOLEAN PARTITION::IsComplete( IN BOOLEAN IoPending ) /*++ Routine Description: This routine computes whether or not this volume has either all (if IoPending is FALSE) of its members or enough (if IoPending is TRUE) of its members. Arguments: IoPending - Supplies whether or not there is IO pending. Return Value: None. --*/ { return TRUE; } VOID PARTITION::CompleteNotification( IN BOOLEAN IoPending ) /*++ Routine Description: This routine is called to notify the volume that it is complete and to therefore prepare for incoming requests. Arguments: IoPending - Supplies whether or not there is IO pending. Return Value: None. --*/ { } ULONG PARTITION::QueryNumberOfPartitions( ) /*++ Routine Description: This routine returns the number of partitions covered by this volume set. Arguments: None. Return Value: The number of partitions covered by this volume set. --*/ { return 1; } PDEVICE_OBJECT PARTITION::GetLeftmostPartitionObject( ) { return _targetObject; } NTSTATUS PARTITION::QueryDiskExtents( OUT PDISK_EXTENT* DiskExtents, OUT PULONG NumberOfDiskExtents ) /*++ Routine Description: This routine returns an array of disk extents that describe the location of this volume. Arguments: DiskExtents - Returns the disk extents. NumberOfDiskExtents - Returns the number of disk extents. Return Value: NTSTATUS --*/ { NTSTATUS status; ULONG diskNumber; PDISK_EXTENT diskExtent; status = FtpQueryPartitionInformation(_rootExtension, _targetObject, &diskNumber, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); if (!NT_SUCCESS(status)) { return status; } diskExtent = (PDISK_EXTENT) ExAllocatePool(PagedPool, sizeof(DISK_EXTENT)); if (!diskExtent) { return STATUS_INSUFFICIENT_RESOURCES; } diskExtent->DiskNumber = diskNumber; diskExtent->StartingOffset.QuadPart = _partitionOffset; diskExtent->ExtentLength.QuadPart = _partitionLength; *DiskExtents = diskExtent; *NumberOfDiskExtents = 1; return status; } BOOLEAN PARTITION::QueryVolumeState( IN PFT_VOLUME Volume, OUT PFT_MEMBER_STATE State ) /*++ Routine Description: This routine returns the state of the given volume considered as a member of this volume. Arguments: Volume - Supplies the volume to query the state for. State - Returns the state. Return Value: FALSE - The given Volume is not a member of this volume. TRUE - The state was successfully computed. --*/ { if (Volume != this) { return FALSE; } *State = FtMemberHealthy; return TRUE; }