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