/*++

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;
}