Source code of Windows XP (NT5)
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.
 
 
 
 
 
 

1087 lines
24 KiB

/*++
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);
}