1088 lines
24 KiB
C
1088 lines
24 KiB
C
/*++
|
||
|
||
Copyright (c) 1991-1994 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
ntlow.c
|
||
|
||
Abstract:
|
||
|
||
This file contains the low-level I/O routines, implemented
|
||
to run on NT.
|
||
|
||
Author:
|
||
|
||
Ted Miller (tedm) 8-Nov-1991
|
||
|
||
Revision History:
|
||
|
||
Bob Rinne (bobri) 2-Feb-1994
|
||
Dynamic partitioning changes.
|
||
|
||
--*/
|
||
|
||
|
||
#include "fdisk.h"
|
||
#include <stdio.h>
|
||
#include <string.h>
|
||
|
||
|
||
STATUS_CODE
|
||
LowQueryFdiskPathList(
|
||
OUT PCHAR **PathList,
|
||
OUT PULONG ListLength
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine determines how many drives are present in the
|
||
system and returns a list of Ascii strings for the names of
|
||
each of the drives found.
|
||
|
||
When a drive is located, a check is made to insure that the
|
||
associated DosName for the physical drive is also present in
|
||
the system.
|
||
|
||
Arguments:
|
||
|
||
PathList - pointer to a pointer for the list
|
||
ListLength - the number of entries returned in the list
|
||
|
||
Return Value:
|
||
|
||
Error status if there is a problem.
|
||
|
||
--*/
|
||
|
||
{
|
||
HANDLE dummyHandle;
|
||
STATUS_CODE status;
|
||
ULONG count = 0;
|
||
ULONG i;
|
||
char buffer[100];
|
||
PCHAR *pathArray;
|
||
|
||
while (1) {
|
||
|
||
sprintf(buffer, "\\device\\harddisk%u", count);
|
||
status = LowOpenDisk(buffer, &dummyHandle);
|
||
|
||
// Only STATUS_OBJECT_PATH_NOT_FOUND can terminate the count.
|
||
|
||
if (NT_SUCCESS(status)) {
|
||
char dosNameBuffer[80];
|
||
|
||
LowCloseDisk(dummyHandle);
|
||
|
||
// Insure that the physicaldrive name is present
|
||
|
||
sprintf(dosNameBuffer, "\\dosdevices\\PhysicalDrive%u", count);
|
||
status = LowOpenNtName(dosNameBuffer, &dummyHandle);
|
||
if (NT_SUCCESS(status)) {
|
||
LowCloseDisk(dummyHandle);
|
||
} else {
|
||
|
||
// Not there, create it.
|
||
|
||
sprintf(buffer, "\\device\\harddisk%u\\Partition0", count);
|
||
DefineDosDevice(DDD_RAW_TARGET_PATH, (LPCTSTR) dosNameBuffer, (LPCTSTR) buffer);
|
||
}
|
||
} else if (status == STATUS_OBJECT_PATH_NOT_FOUND) {
|
||
|
||
break;
|
||
} else if (status == STATUS_ACCESS_DENIED) {
|
||
|
||
return status;
|
||
}
|
||
count++;
|
||
}
|
||
|
||
pathArray = Malloc(count * sizeof(PCHAR));
|
||
|
||
for (i=0; i<count; i++) {
|
||
|
||
sprintf(buffer, "\\device\\harddisk%u", i);
|
||
pathArray[i] = Malloc(lstrlenA(buffer)+1);
|
||
strcpy(pathArray[i], buffer);
|
||
}
|
||
|
||
*PathList = pathArray;
|
||
*ListLength = count;
|
||
return OK_STATUS;
|
||
}
|
||
|
||
|
||
STATUS_CODE
|
||
LowFreeFdiskPathList(
|
||
IN OUT PCHAR* PathList,
|
||
IN ULONG ListLength
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Walk the provided list up to its length and free the memory
|
||
allocated. Upon completion, free the memory for the list
|
||
itself.
|
||
|
||
Arguments:
|
||
|
||
PathList - pointer to base of path list
|
||
ListLength - number of entries in the list
|
||
|
||
Return Value:
|
||
|
||
Always OK_STATUS
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONG i;
|
||
|
||
for (i=0; i<ListLength; i++) {
|
||
FreeMemory(PathList[i]);
|
||
}
|
||
FreeMemory(PathList);
|
||
return OK_STATUS;
|
||
}
|
||
|
||
|
||
STATUS_CODE
|
||
LowOpenNtName(
|
||
IN PCHAR Name,
|
||
IN HANDLE_PT Handle
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This is an internal "Low" routine to handle open requests.
|
||
|
||
Arguments:
|
||
|
||
Name - pointer to the NT name for the open.
|
||
Handle - pointer for the handle returned.
|
||
|
||
Return Value:
|
||
|
||
NT status
|
||
|
||
--*/
|
||
|
||
{
|
||
OBJECT_ATTRIBUTES oa;
|
||
NTSTATUS status;
|
||
IO_STATUS_BLOCK statusBlock;
|
||
ANSI_STRING ansiName;
|
||
UNICODE_STRING unicodeName;
|
||
|
||
RtlInitAnsiString(&ansiName, Name);
|
||
status = RtlAnsiStringToUnicodeString(&unicodeName, &ansiName, TRUE);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
return status;
|
||
}
|
||
|
||
memset(&oa, 0, sizeof(OBJECT_ATTRIBUTES));
|
||
oa.Length = sizeof(OBJECT_ATTRIBUTES);
|
||
oa.ObjectName = &unicodeName;
|
||
oa.Attributes = OBJ_CASE_INSENSITIVE;
|
||
|
||
status = DmOpenFile(Handle,
|
||
SYNCHRONIZE | FILE_READ_DATA | FILE_WRITE_DATA,
|
||
&oa,
|
||
&statusBlock,
|
||
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||
FILE_SYNCHRONOUS_IO_ALERT);
|
||
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
|
||
FDLOG((1,"LowOpen: 1st open failed with 0x%x\n", status));
|
||
|
||
// try a 2nd time to get around an FS glitch or a test
|
||
// bug where this doesn't work on an attempt to delete a
|
||
// partition
|
||
|
||
Sleep(500);
|
||
status = DmOpenFile(Handle,
|
||
SYNCHRONIZE | FILE_READ_DATA | FILE_WRITE_DATA,
|
||
&oa,
|
||
&statusBlock,
|
||
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||
FILE_SYNCHRONOUS_IO_ALERT);
|
||
FDLOG((1,"LowOpen: 2nd open 0x%x\n", status));
|
||
}
|
||
RtlFreeUnicodeString(&unicodeName);
|
||
return status;
|
||
}
|
||
|
||
|
||
STATUS_CODE
|
||
LowOpenDriveLetter(
|
||
IN CHAR DriveLetter,
|
||
IN HANDLE_PT Handle
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Given a drive letter, open it and return a handle.
|
||
|
||
Arguments:
|
||
|
||
DriveLetter - the letter to open
|
||
Handle - a pointer to a handle
|
||
|
||
Return Value:
|
||
|
||
NT status
|
||
|
||
--*/
|
||
|
||
{
|
||
char ntDeviceName[100];
|
||
|
||
sprintf(ntDeviceName,
|
||
"\\DosDevices\\%c:",
|
||
DriveLetter);
|
||
return LowOpenNtName(ntDeviceName, Handle);
|
||
}
|
||
|
||
|
||
STATUS_CODE
|
||
LowOpenPartition(
|
||
IN PCHAR DevicePath,
|
||
IN ULONG Partition,
|
||
OUT HANDLE_PT Handle
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Construct the NT device name for the Partition value given
|
||
and perform the NT APIs to open the device.
|
||
|
||
Arguments:
|
||
|
||
DevicePath - the string to the device without the partition
|
||
portion of the name. This is constructed using
|
||
the Partition value passed
|
||
Partition - the partion desired
|
||
Handle - pointer to a handle pointer for the result
|
||
|
||
Return Value:
|
||
|
||
NT status
|
||
|
||
--*/
|
||
|
||
{
|
||
char ntDeviceName[100];
|
||
|
||
sprintf(ntDeviceName,
|
||
"%s\\partition%u",
|
||
DevicePath,
|
||
Partition);
|
||
return LowOpenNtName(ntDeviceName, Handle);
|
||
}
|
||
|
||
|
||
STATUS_CODE
|
||
LowOpenDisk(
|
||
IN PCHAR DevicePath,
|
||
OUT HANDLE_PT DiskId
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Perform the NT actions to open a device.
|
||
|
||
Arguments:
|
||
|
||
DevicePath - Ascii device name
|
||
DiskId - pointer to a handle pointer for the returned
|
||
handle value
|
||
|
||
Return Value:
|
||
|
||
NT status
|
||
|
||
--*/
|
||
|
||
{
|
||
return(LowOpenPartition(DevicePath, 0, DiskId));
|
||
}
|
||
|
||
|
||
STATUS_CODE
|
||
LowCloseDisk(
|
||
IN HANDLE_T DiskId
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Close a disk handle.
|
||
|
||
Arguments:
|
||
|
||
DiskId - the actual NT handle
|
||
|
||
Return Value:
|
||
|
||
NT status
|
||
|
||
--*/
|
||
|
||
{
|
||
return(DmClose(DiskId));
|
||
}
|
||
|
||
|
||
STATUS_CODE
|
||
LowLockDrive(
|
||
IN HANDLE_T DiskId
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Perform the NT API to cause a volume to be locked.
|
||
This is a File System device control.
|
||
|
||
Arguments:
|
||
|
||
DiskId - the actual NT handle to the drive
|
||
|
||
Return Value:
|
||
|
||
NT status
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS status;
|
||
IO_STATUS_BLOCK statusBlock;
|
||
|
||
status = NtFsControlFile(DiskId,
|
||
0,
|
||
NULL,
|
||
NULL,
|
||
&statusBlock,
|
||
FSCTL_LOCK_VOLUME,
|
||
NULL,
|
||
0,
|
||
NULL,
|
||
0);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
FDLOG((1, "LowLock: failed with 0x%x\n", status));
|
||
}
|
||
return status;
|
||
}
|
||
|
||
|
||
STATUS_CODE
|
||
LowUnlockDrive(
|
||
IN HANDLE_T DiskId
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Perform the NT API to cause a volume to be unlocked.
|
||
This is a File System device control.
|
||
|
||
Arguments:
|
||
|
||
DiskId - the actual NT handle to the drive
|
||
|
||
Return Value:
|
||
|
||
NT status
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS status;
|
||
IO_STATUS_BLOCK statusBlock;
|
||
|
||
status = NtFsControlFile(DiskId,
|
||
0,
|
||
NULL,
|
||
NULL,
|
||
&statusBlock,
|
||
FSCTL_DISMOUNT_VOLUME,
|
||
NULL,
|
||
0,
|
||
NULL,
|
||
0);
|
||
status = NtFsControlFile(DiskId,
|
||
0,
|
||
NULL,
|
||
NULL,
|
||
&statusBlock,
|
||
FSCTL_UNLOCK_VOLUME,
|
||
NULL,
|
||
0,
|
||
NULL,
|
||
0);
|
||
return status;
|
||
}
|
||
|
||
|
||
STATUS_CODE
|
||
LowGetDriveGeometry(
|
||
IN PCHAR Path,
|
||
OUT PULONG TotalSectorCount,
|
||
OUT PULONG SectorSize,
|
||
OUT PULONG SectorsPerTrack,
|
||
OUT PULONG Heads
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Routine collects information concerning the geometry
|
||
of a specific drive.
|
||
|
||
Arguments:
|
||
|
||
Path - Ascii path name to get to disk object
|
||
this is not a full path, but rather
|
||
a path without the Partition indicator
|
||
\device\harddiskX
|
||
TotalSectorCount- pointer to ULONG for result
|
||
SectorSize - pointer to ULONG for result
|
||
SectorsPerTrack - pointer to ULONG for result
|
||
Heads - pointer to ULONG for result
|
||
|
||
Return Value:
|
||
|
||
NT status
|
||
|
||
--*/
|
||
|
||
{
|
||
IO_STATUS_BLOCK statusBlock;
|
||
DISK_GEOMETRY diskGeometry;
|
||
STATUS_CODE status;
|
||
HANDLE handle;
|
||
|
||
if ((status = LowOpenDisk(Path, &handle)) != OK_STATUS) {
|
||
return status;
|
||
}
|
||
|
||
status = NtDeviceIoControlFile(handle,
|
||
0,
|
||
NULL,
|
||
NULL,
|
||
&statusBlock,
|
||
IOCTL_DISK_GET_DRIVE_GEOMETRY,
|
||
NULL,
|
||
0,
|
||
&diskGeometry,
|
||
sizeof(DISK_GEOMETRY));
|
||
if (!NT_SUCCESS(status)) {
|
||
return (STATUS_CODE)status;
|
||
}
|
||
LowCloseDisk(handle);
|
||
|
||
*SectorSize = diskGeometry.BytesPerSector;
|
||
*SectorsPerTrack = diskGeometry.SectorsPerTrack;
|
||
*Heads = diskGeometry.TracksPerCylinder;
|
||
*TotalSectorCount = (RtlExtendedIntegerMultiply(diskGeometry.Cylinders,
|
||
*SectorsPerTrack * *Heads)).LowPart;
|
||
return(OK_STATUS);
|
||
}
|
||
|
||
|
||
STATUS_CODE
|
||
LowGetDiskLayout(
|
||
IN PCHAR Path,
|
||
OUT PDRIVE_LAYOUT_INFORMATION *DriveLayout
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Perform the necessary NT API calls to get the drive
|
||
layout from the disk and return it in a memory buffer
|
||
allocated by this routine.
|
||
|
||
Arguments:
|
||
|
||
Path - Ascii path name to get to disk object
|
||
this is not a full path, but rather
|
||
a path without the Partition indicator
|
||
\device\harddiskX
|
||
|
||
DriveLayout - pointer to pointer for the drive layout result
|
||
|
||
Return Value:
|
||
|
||
NT status
|
||
|
||
--*/
|
||
|
||
{
|
||
PDRIVE_LAYOUT_INFORMATION layout;
|
||
IO_STATUS_BLOCK statusBlock;
|
||
STATUS_CODE status;
|
||
ULONG bufferSize;
|
||
HANDLE handle;
|
||
|
||
bufferSize = sizeof(DRIVE_LAYOUT_INFORMATION)
|
||
+ (500 * sizeof(PARTITION_INFORMATION));
|
||
|
||
if ((layout = AllocateMemory(bufferSize)) == NULL) {
|
||
RETURN_OUT_OF_MEMORY;
|
||
}
|
||
|
||
if ((status = LowOpenDisk(Path, &handle)) != OK_STATUS) {
|
||
FreeMemory(layout);
|
||
return status;
|
||
}
|
||
|
||
status = NtDeviceIoControlFile(handle,
|
||
0,
|
||
NULL,
|
||
NULL,
|
||
&statusBlock,
|
||
IOCTL_DISK_GET_DRIVE_LAYOUT,
|
||
NULL,
|
||
0,
|
||
layout,
|
||
bufferSize);
|
||
LowCloseDisk(handle);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
if (status == STATUS_BAD_MASTER_BOOT_RECORD) {
|
||
|
||
FDLOG((1,"LowGetDiskLayout: Disk device %s has bad MBR\n",Path));
|
||
|
||
// Zero the drive layout information for the fdengine to process.
|
||
|
||
RtlZeroMemory(layout, bufferSize);
|
||
} else {
|
||
FDLOG((0,"LowGetDiskLayout: Status %lx getting layout for disk device %s\n",status,Path));
|
||
FreeMemory(layout);
|
||
return status;
|
||
}
|
||
} else {
|
||
|
||
FDLOG((2,"LowGetDiskLayout: layout received from ioctl for %s follows:\n",Path));
|
||
LOG_DRIVE_LAYOUT(layout);
|
||
}
|
||
|
||
// Check to insure that the drive supports dynamic partitioning.
|
||
|
||
*DriveLayout = layout;
|
||
return OK_STATUS;
|
||
}
|
||
|
||
|
||
STATUS_CODE
|
||
LowSetDiskLayout(
|
||
IN PCHAR Path,
|
||
IN PDRIVE_LAYOUT_INFORMATION DriveLayout
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Perform the NT API actions of opening Partition0 for
|
||
the specified drive and setting the drive layout.
|
||
|
||
Arguments:
|
||
|
||
Path - Ascii path name to get to disk object
|
||
this is not a full path, but rather
|
||
a path without the Partition indicator
|
||
\device\harddiskX
|
||
|
||
DriveLayout - new layout to set
|
||
|
||
Return Value:
|
||
|
||
NT status
|
||
|
||
--*/
|
||
|
||
{
|
||
IO_STATUS_BLOCK statusBlock;
|
||
STATUS_CODE status;
|
||
HANDLE handle;
|
||
ULONG bufferSize;
|
||
|
||
if ((status = LowOpenDisk(Path, &handle)) != OK_STATUS) {
|
||
|
||
return status;
|
||
} else {
|
||
|
||
FDLOG((2, "LowSetDiskLayout: calling ioctl for %s, layout follows:\n", Path));
|
||
LOG_DRIVE_LAYOUT(DriveLayout);
|
||
}
|
||
|
||
bufferSize = sizeof(DRIVE_LAYOUT_INFORMATION)
|
||
+ ( (DriveLayout->PartitionCount - 1)
|
||
* sizeof(PARTITION_INFORMATION));
|
||
status = NtDeviceIoControlFile(handle,
|
||
0,
|
||
NULL,
|
||
NULL,
|
||
&statusBlock,
|
||
IOCTL_DISK_SET_DRIVE_LAYOUT,
|
||
DriveLayout,
|
||
bufferSize,
|
||
DriveLayout,
|
||
bufferSize);
|
||
LowCloseDisk(handle);
|
||
return status;
|
||
}
|
||
|
||
|
||
STATUS_CODE
|
||
LowWriteSectors(
|
||
IN HANDLE_T VolumeId,
|
||
IN ULONG SectorSize,
|
||
IN ULONG StartingSector,
|
||
IN ULONG NumberOfSectors,
|
||
IN PVOID Buffer
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Routine to write to a volume handle. This routine
|
||
insulates the NT issues concerning the call from the
|
||
caller.
|
||
|
||
Arguments:
|
||
|
||
VolumeId - actually the NT handle.
|
||
SectorSize - used to calculate starting byte offset for I/O
|
||
StartingSector - starting sector for write.
|
||
NumberOfSectors - size of I/O in sectors
|
||
Buffer - the location for the data
|
||
|
||
Return Value:
|
||
|
||
Standard NT status values
|
||
|
||
--*/
|
||
|
||
{
|
||
IO_STATUS_BLOCK statusBlock;
|
||
LARGE_INTEGER byteOffset;
|
||
|
||
byteOffset = RtlExtendedIntegerMultiply(RtlConvertUlongToLargeInteger(StartingSector), (LONG)SectorSize);
|
||
|
||
statusBlock.Status = 0;
|
||
statusBlock.Information = 0;
|
||
return(NtWriteFile(VolumeId,
|
||
0,
|
||
NULL,
|
||
NULL,
|
||
&statusBlock,
|
||
Buffer,
|
||
NumberOfSectors * SectorSize,
|
||
&byteOffset,
|
||
NULL));
|
||
}
|
||
|
||
|
||
STATUS_CODE
|
||
LowReadSectors(
|
||
IN HANDLE_T VolumeId,
|
||
IN ULONG SectorSize,
|
||
IN ULONG StartingSector,
|
||
IN ULONG NumberOfSectors,
|
||
IN PVOID Buffer
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Routine to read from a volume handle. This routine
|
||
insulates the NT issues concerning the call from the
|
||
caller.
|
||
|
||
Arguments:
|
||
|
||
VolumeId - actually the NT handle.
|
||
SectorSize - used to calculate starting byte offset for I/O
|
||
StartingSector - starting sector for write.
|
||
NumberOfSectors - size of I/O in sectors
|
||
Buffer - the location for the data
|
||
|
||
Return Value:
|
||
|
||
Standard NT status values
|
||
|
||
--*/
|
||
|
||
{
|
||
IO_STATUS_BLOCK statusBlock;
|
||
LARGE_INTEGER byteOffset;
|
||
|
||
byteOffset = RtlExtendedIntegerMultiply(RtlConvertUlongToLargeInteger(StartingSector), (LONG)SectorSize);
|
||
|
||
statusBlock.Status = 0;
|
||
statusBlock.Information = 0;
|
||
return(NtReadFile(VolumeId,
|
||
0,
|
||
NULL,
|
||
NULL,
|
||
&statusBlock,
|
||
Buffer,
|
||
NumberOfSectors * SectorSize,
|
||
&byteOffset,
|
||
NULL));
|
||
}
|
||
|
||
|
||
STATUS_CODE
|
||
LowFtVolumeStatus(
|
||
IN ULONG Disk,
|
||
IN ULONG Partition,
|
||
IN PFT_SET_STATUS FtStatus,
|
||
IN PULONG NumberOfMembers
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Open the requested partition and query the FT state.
|
||
|
||
Arguments:
|
||
|
||
DriveLetter - the letter for the current state
|
||
FtState - a pointer to a location to return state
|
||
NumberOfMembers - a pointer to a ULONG for number of members
|
||
in the FT set.
|
||
|
||
Return Value:
|
||
|
||
Standard NT status values
|
||
|
||
--*/
|
||
|
||
{
|
||
HANDLE handle;
|
||
STATUS_CODE status;
|
||
IO_STATUS_BLOCK statusBlock;
|
||
FT_SET_INFORMATION setInfo;
|
||
|
||
status = LowOpenPartition(GetDiskName(Disk),
|
||
Partition,
|
||
&handle);
|
||
|
||
if (status == OK_STATUS) {
|
||
|
||
status = NtDeviceIoControlFile(handle,
|
||
0,
|
||
NULL,
|
||
NULL,
|
||
&statusBlock,
|
||
FT_QUERY_SET_STATE,
|
||
NULL,
|
||
0,
|
||
&setInfo,
|
||
sizeof(setInfo));
|
||
LowCloseDisk(handle);
|
||
|
||
if (status == OK_STATUS) {
|
||
switch (setInfo.SetState) {
|
||
case FtStateOk:
|
||
*FtStatus = FtSetHealthy;
|
||
break;
|
||
|
||
case FtHasOrphan:
|
||
switch (setInfo.Type) {
|
||
case Mirror:
|
||
*FtStatus = FtSetBroken;
|
||
break;
|
||
case StripeWithParity:
|
||
*FtStatus = FtSetRecoverable;
|
||
break;
|
||
}
|
||
break;
|
||
|
||
case FtRegenerating:
|
||
*FtStatus = FtSetRegenerating;
|
||
break;
|
||
|
||
case FtCheckParity:
|
||
*FtStatus = FtSetInitializationFailed;
|
||
break;
|
||
|
||
case FtInitializing:
|
||
*FtStatus = FtSetInitializing;
|
||
break;
|
||
|
||
case FtDisabled:
|
||
|
||
// This will never happen.
|
||
|
||
*FtStatus = FtSetDisabled;
|
||
break;
|
||
|
||
case FtNoCheckData:
|
||
default:
|
||
|
||
// BUGBUG: there is no mapping here.
|
||
|
||
*FtStatus = FtSetHealthy;
|
||
break;
|
||
}
|
||
*NumberOfMembers = setInfo.NumberOfMembers;
|
||
}
|
||
} else {
|
||
|
||
// If the FT set could not be opened, then it must be
|
||
// disabled if the return code is "No such device".
|
||
|
||
if (status == 0xc000000e) {
|
||
*FtStatus = FtSetDisabled;
|
||
status = OK_STATUS;
|
||
}
|
||
}
|
||
|
||
// Always update the state to the caller.
|
||
|
||
return status;
|
||
}
|
||
|
||
|
||
STATUS_CODE
|
||
LowFtVolumeStatusByLetter(
|
||
IN CHAR DriveLetter,
|
||
IN PFT_SET_STATUS FtStatus,
|
||
IN PULONG NumberOfMembers
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Open the requested drive letter and query the FT state.
|
||
|
||
Arguments:
|
||
|
||
DriveLetter - the letter for the current state
|
||
FtState - a pointer to a location to return state
|
||
NumberOfMembers - a pointer to a ULONG for number of members
|
||
in the FT set.
|
||
|
||
Return Value:
|
||
|
||
Standard NT status values
|
||
|
||
--*/
|
||
|
||
{
|
||
HANDLE handle;
|
||
STATUS_CODE status;
|
||
IO_STATUS_BLOCK statusBlock;
|
||
FT_SET_INFORMATION setInfo;
|
||
|
||
*NumberOfMembers = 1;
|
||
status = LowOpenDriveLetter(DriveLetter,
|
||
&handle);
|
||
|
||
if (status == OK_STATUS) {
|
||
|
||
status = NtDeviceIoControlFile(handle,
|
||
0,
|
||
NULL,
|
||
NULL,
|
||
&statusBlock,
|
||
FT_QUERY_SET_STATE,
|
||
NULL,
|
||
0,
|
||
&setInfo,
|
||
sizeof(setInfo));
|
||
LowCloseDisk(handle);
|
||
|
||
if (status == OK_STATUS) {
|
||
switch (setInfo.SetState) {
|
||
case FtStateOk:
|
||
*FtStatus = FtSetHealthy;
|
||
break;
|
||
|
||
case FtHasOrphan:
|
||
switch (setInfo.Type) {
|
||
case Mirror:
|
||
*FtStatus = FtSetBroken;
|
||
break;
|
||
case StripeWithParity:
|
||
*FtStatus = FtSetRecoverable;
|
||
break;
|
||
}
|
||
break;
|
||
|
||
case FtRegenerating:
|
||
*FtStatus = FtSetRegenerating;
|
||
break;
|
||
|
||
case FtCheckParity:
|
||
*FtStatus = FtSetInitializationFailed;
|
||
break;
|
||
|
||
case FtInitializing:
|
||
*FtStatus = FtSetInitializing;
|
||
break;
|
||
|
||
case FtDisabled:
|
||
|
||
// This will never happen.
|
||
|
||
*FtStatus = FtSetDisabled;
|
||
break;
|
||
|
||
case FtNoCheckData:
|
||
default:
|
||
|
||
// BUGBUG: there is no mapping here.
|
||
|
||
*FtStatus = FtSetHealthy;
|
||
break;
|
||
}
|
||
*NumberOfMembers = setInfo.NumberOfMembers;
|
||
}
|
||
} else {
|
||
|
||
// If the FT set could not be opened, then it must be
|
||
// disabled if the return code is "No such device".
|
||
|
||
if (status == 0xc000000e) {
|
||
*FtStatus = FtSetDisabled;
|
||
status = OK_STATUS;
|
||
}
|
||
}
|
||
|
||
// Always update the state to the caller.
|
||
|
||
return status;
|
||
}
|
||
|
||
|
||
|
||
#define NUMBER_OF_HANDLES_TRACKED 500
|
||
HANDLE OpenHandleArray[NUMBER_OF_HANDLES_TRACKED];
|
||
BOOLEAN DmFirstTime = TRUE;
|
||
ULONG HandleHighWaterMark = 0;
|
||
|
||
NTSTATUS
|
||
DmOpenFile(
|
||
OUT PHANDLE FileHandle,
|
||
IN ACCESS_MASK DesiredAccess,
|
||
IN POBJECT_ATTRIBUTES ObjectAttributes,
|
||
OUT PIO_STATUS_BLOCK IoStatusBlock,
|
||
IN ULONG ShareAccess,
|
||
IN ULONG OpenOptions
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
A debugging aid to track open and closes of partitions.
|
||
|
||
Arguments:
|
||
|
||
Same as for NtOpenFile()
|
||
|
||
Return Value:
|
||
|
||
Same as for NtOpenFile()
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONG index;
|
||
NTSTATUS status;
|
||
|
||
if (DmFirstTime) {
|
||
DmFirstTime = FALSE;
|
||
for (index = 0; index < NUMBER_OF_HANDLES_TRACKED; index++) {
|
||
OpenHandleArray[index] = (HANDLE) 0;
|
||
}
|
||
}
|
||
|
||
status = NtOpenFile(FileHandle,
|
||
DesiredAccess,
|
||
ObjectAttributes,
|
||
IoStatusBlock,
|
||
ShareAccess,
|
||
OpenOptions);
|
||
if (NT_SUCCESS(status)) {
|
||
for (index = 0; index < NUMBER_OF_HANDLES_TRACKED; index++) {
|
||
if (OpenHandleArray[index] == (HANDLE) 0) {
|
||
OpenHandleArray[index] = *FileHandle;
|
||
|
||
if (index > HandleHighWaterMark) {
|
||
HandleHighWaterMark = index;
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
return status;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
DmClose(
|
||
IN HANDLE Handle
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
A debugging aid for tracking open and closes
|
||
|
||
Arguments:
|
||
|
||
Same as for NtClose()
|
||
|
||
Return Value:
|
||
|
||
Same as for NtClose()
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONG index;
|
||
|
||
for (index = 0; index < NUMBER_OF_HANDLES_TRACKED; index++) {
|
||
if (OpenHandleArray[index] == Handle) {
|
||
OpenHandleArray[index] = (HANDLE) 0;
|
||
break;
|
||
}
|
||
}
|
||
|
||
return NtClose(Handle);
|
||
}
|