windows-nt/Source/XPSP1/NT/base/fs/utils/fdisk/fd_nt.c

1085 lines
26 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
Copyright (c) 1991-1994 Microsoft Corporation
Module Name:
fd_nt.c
Abstract:
This module wraps fdisk engine functions. This is done
to avoid having files that include both the full windows
and the full nt include file sets.
Functions that manipulate engine structures (REGIONs, for example)
are also placed here.
This file is targeted at NT, not Windows.
Author:
Ted Miller (tedm) 5-Dec-1991
Revision History:
Misc cleanup (BobRi) 22-Jan-1994
--*/
#include "fdisk.h"
#include <string.h>
#include <stdio.h>
// These partition ID's are for systems recognized by WINDISK,
// even though they don't appear in ntdddisk.h.
#define PARTITION_OS2_BOOT 0xa
#define PARTITION_EISA 0x12
WCHAR UnicodeSysIdName[100];
BYTE StringBuffer[100];
// Pagefile support structures.
typedef struct _PAGEFILE_LOCATION {
struct _PAGEFILE_LOCATION *Next;
CHAR DriveLetter;
} PAGEFILE_LOCATION, *PPAGEFILE_LOCATION;
PPAGEFILE_LOCATION PagefileHead = NULL;
// For some reason the file systems don't like being accessed shortly after
// a format or lock event.
#define SLEEP_TIME (1000*2) // 2 seconds
PWSTR
GetWideSysIDName(
IN UCHAR SysID
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
ANSI_STRING ansiString;
UNICODE_STRING unicodeString;
DWORD stringId;
// Get the name, which is a byte-string.
switch (SysID) {
case PARTITION_ENTRY_UNUSED:
stringId = IDS_PARTITION_FREE;
break;
case PARTITION_XENIX_1:
stringId = IDS_PARTITION_XENIX1;
break;
case PARTITION_XENIX_2:
stringId = IDS_PARTITION_XENIX2;
break;
case PARTITION_OS2_BOOT:
stringId = IDS_PARTITION_OS2_BOOT;
break;
case PARTITION_EISA:
stringId = IDS_PARTITION_EISA;
break;
case PARTITION_UNIX:
stringId = IDS_PARTITION_UNIX;
break;
case PARTITION_PREP:
#ifdef _PPC_
stringId = IDS_PARTITION_POWERPC;
#else
// If not on a PPC platform, assume this is Eisa related
stringId = IDS_PARTITION_EISA;
#endif
break;
default:
stringId = IDS_UNKNOWN;
break;
}
LoadString(hModule, stringId, StringBuffer, sizeof(StringBuffer));
RtlInitAnsiString(&ansiString, StringBuffer);
//
// Convert to Unicode
//
unicodeString.Buffer = UnicodeSysIdName;
unicodeString.MaximumLength = sizeof(UnicodeSysIdName);
RtlAnsiStringToUnicodeString(&unicodeString, &ansiString, FALSE);
return UnicodeSysIdName;
}
ULONG
MyDiskRegistryGet(
OUT PDISK_REGISTRY *DiskRegistry
)
/*++
Routine Description:
Allocate memory for the size of the disk registry, obtain
the registry contents (if any) and return the pointer to the
allocated memory.
Arguments:
A pointer to a disk registry pointer.
Return Value:
status indicating success or failure.
--*/
{
ULONG length;
PDISK_REGISTRY diskRegistry;
NTSTATUS status;
while (((status = DiskRegistryGet(NULL, &length)) == STATUS_NO_MEMORY)
|| (status == STATUS_INSUFFICIENT_RESOURCES))
{
ConfirmOutOfMemory();
}
if (!NT_SUCCESS(status)) {
return EC(status);
}
diskRegistry = Malloc(length);
while (((status = DiskRegistryGet(diskRegistry, &length)) == STATUS_NO_MEMORY)
|| (status == STATUS_INSUFFICIENT_RESOURCES))
{
ConfirmOutOfMemory();
}
if (NT_SUCCESS(status)) {
LOG_DISK_REGISTRY("MyDiskRegistryGet", diskRegistry);
*DiskRegistry = diskRegistry;
}
return EC(status);
}
ULONG
FormDiskSignature(
VOID
)
/*++
Routine Description:
Return a ULONG disk signature. This is derived from the current
system time.
Arguments:
None
Return Value:
A 32-bit signature
--*/
{
LARGE_INTEGER time;
static ULONG baseSignature = 0;
if (!baseSignature) {
NtQuerySystemTime(&time);
time.QuadPart = time.QuadPart >> 16;
baseSignature = time.LowPart;
}
return baseSignature++;
}
BOOLEAN
GetVolumeSizeMB(
IN ULONG Disk,
IN ULONG Partition,
OUT PULONG Size
)
/*++
Routine Description:
Given a disk and a partition, query the "volume" to get its size.
By performing the query on the 1st partition of a potential FT set,
the total size of the set will be returned. If the partition isn't
an FT set, it will work too.
Arguments:
Disk - the disk number
Partition - the partition number
Size - the size of the "volume"
Return Value:
TRUE - a size was returned.
FALSE - something failed in getting the size.
--*/
{
BOOLEAN retValue = FALSE;
IO_STATUS_BLOCK statusBlock;
HANDLE handle;
STATUS_CODE sc;
PARTITION_INFORMATION partitionInfo;
LARGE_INTEGER partitionLength;
*Size = 0;
sc = LowOpenPartition(GetDiskName(Disk), Partition, &handle);
if (sc == OK_STATUS) {
sc = NtDeviceIoControlFile(handle,
0,
NULL,
NULL,
&statusBlock,
IOCTL_DISK_GET_PARTITION_INFO,
NULL,
0,
&partitionInfo,
sizeof(PARTITION_INFORMATION));
if (sc == OK_STATUS) {
// Convert to MB
partitionLength.QuadPart = partitionInfo.PartitionLength.QuadPart >> 20;
*Size = partitionLength.LowPart;
retValue = TRUE;
}
LowCloseDisk(handle);
}
return retValue;
}
ULONG
GetVolumeTypeAndSize(
IN ULONG Disk,
IN ULONG Partition,
OUT PWSTR *Label,
OUT PWSTR *Type,
OUT PULONG Size
)
/*++
Routine Description:
Given a disk and partition number, determine its size, label and file
system type. This routine will allocate the space for label and file
system type. It is the responsibility of the caller to free this memory.
Arguments:
Disk - the disk number
Partition - the partition number
Label - a pointer to a pointer for a WCHAR string to contain the label
Type - a pointer to a pointer for a WCHAR string to contain the file system
type.
Size - a pointer to a ULONG for the size of the disk in KB.
Return Value:
OK_STATUS - everything was performed.
!OK_STATUS - the error code that was returned in the process of performing
this work.
--*/
{
IO_STATUS_BLOCK statusBlock;
HANDLE handle;
unsigned char buffer[256];
PWSTR label,
name;
ULONG length;
DISK_GEOMETRY diskGeometry;
STATUS_CODE sc;
BOOLEAN firstTime = TRUE;
PFILE_FS_VOLUME_INFORMATION labelInfo = (PFILE_FS_VOLUME_INFORMATION)buffer;
PFILE_FS_ATTRIBUTE_INFORMATION info = (PFILE_FS_ATTRIBUTE_INFORMATION)buffer;
while (1) {
sc = LowOpenPartition(GetDiskName(Disk), Partition, &handle);
if (sc == OK_STATUS) {
sc = NtQueryVolumeInformationFile(handle,
&statusBlock,
buffer,
sizeof(buffer),
FileFsVolumeInformation);
if (sc == OK_STATUS) {
length = labelInfo->VolumeLabelLength;
labelInfo->VolumeLabel[length/sizeof(WCHAR)] = 0;
length = (length+1) * sizeof(WCHAR);
label = Malloc(length);
RtlMoveMemory(label, labelInfo->VolumeLabel, length);
} else {
label = Malloc(sizeof(WCHAR));
*label = 0;
}
*Label = label;
if (sc == OK_STATUS) {
sc = NtQueryVolumeInformationFile(handle,
&statusBlock,
buffer,
sizeof(buffer),
FileFsAttributeInformation);
if (sc == OK_STATUS) {
length = info->FileSystemNameLength;
info->FileSystemName[length/sizeof(WCHAR)] = 0;
length = (length+1)*sizeof(WCHAR);
name = Malloc(length);
RtlMoveMemory(name, info->FileSystemName, length);
} else {
name = Malloc(sizeof(WCHAR));
*name = 0;
}
*Type = name;
}
if (sc == OK_STATUS) {
sc = NtDeviceIoControlFile(handle,
0,
NULL,
NULL,
&statusBlock,
IOCTL_DISK_GET_DRIVE_GEOMETRY,
NULL,
0,
(PVOID)&diskGeometry,
sizeof(diskGeometry));
if (NT_SUCCESS(sc)) {
LARGE_INTEGER sizeInBytes;
ULONG cylinderBytes;
cylinderBytes = diskGeometry.TracksPerCylinder *
diskGeometry.SectorsPerTrack *
diskGeometry.BytesPerSector;
sizeInBytes.QuadPart = diskGeometry.Cylinders.QuadPart * cylinderBytes;
// Now convert everything to KB
sizeInBytes.QuadPart = sizeInBytes.QuadPart >> 10;
*Size = (ULONG) sizeInBytes.LowPart;
}
}
DmClose(handle);
sc = OK_STATUS;
break;
} else {
if (firstTime) {
firstTime = FALSE;
} else {
break;
}
Sleep(SLEEP_TIME);
}
}
return EC(sc);
}
ULONG
GetVolumeLabel(
IN ULONG Disk,
IN ULONG Partition,
OUT PWSTR *Label
)
/*++
Routine Description:
Given a disk number and a partition number return the volume label (if
any).
Arguments:
Disk - the disk number
Partition - the partition number
Label - a pointer to a pointer for a WCHAR string to contain the label
Return Value:
OK_STATUS - everything was performed.
!OK_STATUS - the error code that was returned in the process of performing
this work.
--*/
{
IO_STATUS_BLOCK statusBlock;
HANDLE handle;
unsigned char buffer[256];
PWSTR label;
ULONG length;
STATUS_CODE sc;
BOOLEAN firstTime = TRUE;
PFILE_FS_VOLUME_INFORMATION labelInfo = (PFILE_FS_VOLUME_INFORMATION)buffer;
while (1) {
sc = LowOpenPartition(GetDiskName(Disk), Partition, &handle);
if (sc == OK_STATUS) {
sc = NtQueryVolumeInformationFile(handle,
&statusBlock,
buffer,
sizeof(buffer),
FileFsVolumeInformation);
DmClose(handle);
if (sc == OK_STATUS) {
length = labelInfo->VolumeLabelLength;
labelInfo->VolumeLabel[length/sizeof(WCHAR)] = 0;
length = (length+1) * sizeof(WCHAR);
label = Malloc(length);
RtlMoveMemory(label, labelInfo->VolumeLabel, length);
} else {
label = Malloc(sizeof(WCHAR));
sc = OK_STATUS;
*label = 0;
}
*Label = label;
break;
} else {
if (firstTime) {
firstTime = FALSE;
} else {
*Label = NULL;
break;
}
Sleep(SLEEP_TIME);
}
}
return EC(sc);
}
ULONG
GetTypeName(
IN ULONG Disk,
IN ULONG Partition,
OUT PWSTR *Name
)
/*++
Routine Description:
Given a disk number and partition number return the file system type
string.
Arguments:
Disk - the disk number
Partition - the partition number
Name - a pointer to a pointer for a WCHAR string to contain the file system
type.
Return Value:
OK_STATUS - everything was performed.
!OK_STATUS - the error code that was returned in the process of performing
this work.
--*/
{
PWSTR name;
STATUS_CODE sc;
HANDLE handle;
unsigned char buffer[256];
IO_STATUS_BLOCK statusBlock;
ULONG length;
BOOLEAN firstTime = TRUE;
PFILE_FS_ATTRIBUTE_INFORMATION info = (PFILE_FS_ATTRIBUTE_INFORMATION)buffer;
// For some reason, the file systems believe they are locked or need
// to be verified after formats and the like. Therefore this is attempted
// twice before it actually gives up.
while (1) {
sc = LowOpenPartition(GetDiskName(Disk), Partition, &handle);
if (sc == OK_STATUS) {
sc = NtQueryVolumeInformationFile(handle,
&statusBlock,
buffer,
sizeof(buffer),
FileFsAttributeInformation);
DmClose(handle);
if (sc == OK_STATUS) {
length = info->FileSystemNameLength;
info->FileSystemName[length/sizeof(WCHAR)] = 0;
length = (length+1)*sizeof(WCHAR);
name = Malloc(length);
RtlMoveMemory(name, info->FileSystemName, length);
} else {
name = Malloc(sizeof(WCHAR));
*name = 0;
sc = OK_STATUS;
}
*Name = name;
break;
} else {
if (firstTime) {
firstTime = FALSE;
} else {
break;
}
Sleep(SLEEP_TIME);
}
}
return EC(sc);
}
BOOLEAN
IsRemovable(
IN ULONG DiskNumber
)
/*++
Routine Description:
This function determines whether the specified physical
disk is removable.
Arguments:
DiskNumber -- The Physical Disk Number of the disk in question.
Return Value:
TRUE if the disk is removable.
--*/
{
STATUS_CODE sc;
NTSTATUS status;
HANDLE handle;
DISK_GEOMETRY diskGeometry;
IO_STATUS_BLOCK statusBlock;
PCHAR name;
name = GetDiskName(DiskNumber);
sc = LowOpenDisk(name, &handle);
if (sc == OK_STATUS) {
status = NtDeviceIoControlFile(handle,
0,
NULL,
NULL,
&statusBlock,
IOCTL_DISK_GET_DRIVE_GEOMETRY,
NULL,
0,
(PVOID)&diskGeometry,
sizeof(diskGeometry));
LowCloseDisk(handle);
if (NT_SUCCESS(status)) {
if (diskGeometry.MediaType == RemovableMedia) {
char ntDeviceName[100];
// Do a dismount/force mount sequence to make sure
// the media hasn't changed since last mount.
// Dismount partition 1 by lock/unlock/close.
sprintf(ntDeviceName, "%s\\Partition1", name);
status= LowOpenNtName(ntDeviceName, &handle);
if (NT_SUCCESS(status)) {
LowLockDrive(handle);
LowUnlockDrive(handle);
LowCloseDisk(handle);
// Now force the mount by opening the device with a '\'
// This is done on partition 1 of the device.
sprintf(ntDeviceName, "%s\\Partition1\\", name);
status= LowOpenNtName(ntDeviceName, &handle);
if (NT_SUCCESS(status)) {
LowCloseDisk(handle);
}
}
return TRUE;
}
}
}
return FALSE;
}
ULONG
GetDriveLetterLinkTarget(
IN PWSTR SourceNameStr,
OUT PWSTR *LinkTarget
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
static WCHAR targetNameBuffer[50];
UNICODE_STRING sourceName,
targetName;
NTSTATUS status;
OBJECT_ATTRIBUTES attributes;
HANDLE handle;
RtlInitUnicodeString(&sourceName, SourceNameStr);
InitializeObjectAttributes(&attributes, &sourceName, OBJ_CASE_INSENSITIVE, NULL, NULL);
status = NtOpenSymbolicLinkObject(&handle, READ_CONTROL | SYMBOLIC_LINK_QUERY, &attributes);
if (NT_SUCCESS(status)) {
RtlZeroMemory(targetNameBuffer, 50 * sizeof(WCHAR));
targetName.Buffer = targetNameBuffer;
targetName.MaximumLength = sizeof(targetNameBuffer);
status = NtQuerySymbolicLinkObject(handle, &targetName, NULL);
NtClose(handle);
}
if (NT_SUCCESS(status)) {
*LinkTarget = targetName.Buffer;
} else {
*LinkTarget = NULL;
}
return EC(status);
}
#include "bootmbr.h"
#if X86BOOTCODE_SIZE < MBOOT_CODE_SIZE
#error Something is wrong with the boot code (it's too small)!
#endif
ULONG
MasterBootCode(
IN ULONG Disk,
IN ULONG Signature,
IN BOOLEAN SetBootCode,
IN BOOLEAN SetSignature
)
/*++
Routine Description:
If the zero sector of the disk does not have a valid MBR
signature (i.e. AA55), update it such that it has a valid
MBR and fill in the disk signature and bootcode in the
process.
Arguments:
Disk - the disk ordinal to be affected
SetSignature - if TRUE update the disk signature
Signature - the disk signature for the update
Return Value:
status
--*/
{
HANDLE handle;
STATUS_CODE status;
PUCHAR unalignedSectorBuffer,
sectorBuffer;
ULONG bps,
dummy,
i;
BOOLEAN writeIt;
PCHAR diskName = GetDiskName(Disk);
#ifndef max
#define max(a,b) ((a > b) ? a : b)
#endif
if (SetBootCode) {
writeIt = FALSE;
// allocate sector buffer
status = LowGetDriveGeometry(diskName, &dummy, &bps, &dummy, &dummy);
if (status != OK_STATUS) {
return EC(status);
}
if (bps < 512) {
bps = 512;
}
unalignedSectorBuffer = Malloc(2*bps);
sectorBuffer = (PUCHAR)(((ULONG)unalignedSectorBuffer+bps) & ~(bps-1));
// open entire disk (partition 0)
if ((status = LowOpenDisk(diskName, &handle)) != OK_STATUS) {
return EC(status);
}
// read (at least) first 512 bytes
status = LowReadSectors(handle, bps, 0, 1, sectorBuffer);
if (status == OK_STATUS) {
if ((sectorBuffer[MBOOT_SIG_OFFSET+0] != MBOOT_SIG1)
|| (sectorBuffer[MBOOT_SIG_OFFSET+1] != MBOOT_SIG2)) {
// xfer boot code into sectorBuffer
for (i=0; i<MBOOT_CODE_SIZE; i++) {
sectorBuffer[i] = x86BootCode[i];
}
// wipe partition table
for (i=MBOOT_CODE_SIZE; i<MBOOT_SIG_OFFSET; i++) {
sectorBuffer[i] = 0;
}
// set the signature
sectorBuffer[MBOOT_SIG_OFFSET+0] = MBOOT_SIG1;
sectorBuffer[MBOOT_SIG_OFFSET+1] = MBOOT_SIG2;
writeIt = TRUE;
}
if (writeIt) {
status = LowWriteSectors(handle, bps, 0, 1, sectorBuffer);
}
}
LowCloseDisk(handle);
Free(unalignedSectorBuffer);
}
if (SetSignature) {
PDRIVE_LAYOUT_INFORMATION layout;
// Use the IOCTL to set the signature. This code really does
// not know where the MBR exists. (ezDrive extensions).
status = LowGetDiskLayout(diskName, &layout);
if (status == OK_STATUS) {
layout->Signature = Signature;
LowSetDiskLayout(diskName, layout);
}
}
return EC(status);
}
ULONG
UpdateMasterBootCode(
IN ULONG Disk
)
/*++
Routine Description:
This routine updates the zero sector of the disk to insure that boot
code is present.
Arguments:
Disk - the disk number onto which to put the boot code.
Return Value:
status
--*/
{
HANDLE handle;
STATUS_CODE status;
PUCHAR unalignedSectorBuffer,
sectorBuffer;
ULONG bps,
dummy,
i;
PCHAR diskName = GetDiskName(Disk);
#ifndef max
#define max(a,b) ((a > b) ? a : b)
#endif
// allocate sector buffer
status = LowGetDriveGeometry(diskName, &dummy, &bps, &dummy, &dummy);
if (status != OK_STATUS) {
return EC(status);
}
if (bps < 512) {
bps = 512;
}
unalignedSectorBuffer = Malloc(2*bps);
sectorBuffer = (PUCHAR)(((ULONG)unalignedSectorBuffer+bps) & ~(bps-1));
// open entire disk (partition 0)
if ((status = LowOpenDisk(diskName, &handle)) != OK_STATUS) {
return EC(status);
}
// read (at least) first 512 bytes
status = LowReadSectors(handle, bps, 0, 1, sectorBuffer);
if (status == OK_STATUS) {
// xfer boot code into sectorBuffer. This avoids changing the
// disk signature and the partition table information.
for (i=0; i<MBOOT_CODE_SIZE; i++) {
sectorBuffer[i] = x86BootCode[i];
}
status = LowWriteSectors(handle, bps, 0, 1, sectorBuffer);
}
LowCloseDisk(handle);
// free the sector buffer
Free(unalignedSectorBuffer);
return EC(status);
}
#if i386
VOID
MakePartitionActive(
IN PREGION_DESCRIPTOR DiskRegionArray,
IN ULONG RegionCount,
IN ULONG RegionIndex
)
/*++
Routine Description:
Update the information in the internal structures to indicate
that the specified partition is active.
Arguments:
DiskRegionArray
RegionCount
RegionIndex
Return Value:
None
--*/
{
unsigned i;
for (i=0; i<RegionCount; i++) {
if (DiskRegionArray[i].RegionType == REGION_PRIMARY) {
DiskRegionArray[i].Active = FALSE;
SetPartitionActiveFlag(&DiskRegionArray[i], FALSE);
}
}
DiskRegionArray[RegionIndex].Active = (BOOLEAN)0x80;
SetPartitionActiveFlag(&DiskRegionArray[RegionIndex], 0x80);
}
#endif
VOID
LoadExistingPageFileInfo(
IN VOID
)
/*++
Routine Description:
This routine finds all pagefiles in the system and updates the internal
structures.
Arguments:
None
Return Values:
None
--*/
{
NTSTATUS status;
SYSTEM_INFO sysInfo;
UCHAR genericBuffer[0x10000];
PSYSTEM_PAGEFILE_INFORMATION pageFileInfo;
ANSI_STRING ansiPageFileName;
PPAGEFILE_LOCATION pageFileListEntry;
PCHAR p;
GetSystemInfo(&sysInfo);
status = NtQuerySystemInformation(SystemPageFileInformation,
genericBuffer,
sizeof(genericBuffer),
NULL);
if (!NT_SUCCESS(status)) {
// It's possible that this call will fail if the
// the system is running without ANY paging files.
return;
}
pageFileInfo = (PSYSTEM_PAGEFILE_INFORMATION) genericBuffer;
for (;;) {
RtlUnicodeStringToAnsiString(&ansiPageFileName,
&pageFileInfo->PageFileName,
TRUE);
// Since the format of the pagefile name generally
// looks something like "\DosDevices\h:\pagefile.sys",
// just use the first character before the colon
// and assume that's the drive letter.
p = strchr(_strlwr(ansiPageFileName.Buffer), ':');
if ((p-- != NULL) && (*p >= 'a') && (*p <= 'z')) {
pageFileListEntry = Malloc(sizeof(PAGEFILE_LOCATION));
if (pageFileListEntry) {
if (PagefileHead) {
pageFileListEntry->Next = PagefileHead;
} else {
PagefileHead = pageFileListEntry;
pageFileListEntry->Next = NULL;
}
pageFileListEntry->DriveLetter = *p;
}
}
RtlFreeAnsiString(&ansiPageFileName);
if (pageFileInfo->NextEntryOffset == 0) {
break;
}
pageFileInfo = (PSYSTEM_PAGEFILE_INFORMATION)((PCHAR) pageFileInfo
+ pageFileInfo->NextEntryOffset);
}
}
BOOLEAN
IsPagefileOnDrive(
CHAR DriveLetter
)
/*++
Routine Description:
Walk the page file list and determine if the drive letter given has
a paging file. NOTE: The assumption is that drive letters that
contain paging files can never get changed during the execution of
Disk Administrator. Therefore this list is never updated, but
can be used during the execution of Disk Administrator.
Arguments:
DriveLetter - the drive in question.
Return Value:
TRUE if this drive contains a page file.
--*/
{
PPAGEFILE_LOCATION pageFileListEntry = PagefileHead;
while (pageFileListEntry) {
if (pageFileListEntry->DriveLetter == DriveLetter) {
return TRUE;
}
pageFileListEntry = pageFileListEntry->Next;
}
return FALSE;
}