mirror of https://github.com/tongzx/nt5src
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.
1517 lines
32 KiB
1517 lines
32 KiB
/*++
|
|
|
|
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 <ntddk.h>
|
|
}
|
|
|
|
#include <ftdisk.h>
|
|
|
|
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;
|
|
}
|