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.
3734 lines
89 KiB
3734 lines
89 KiB
/*++
|
|
|
|
Copyright (c) 1991-1994 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
fdengine.c
|
|
|
|
Abstract:
|
|
|
|
This module contains the disk partitioning engine. The code
|
|
in this module can be compiled for either the NT platform
|
|
or the ARC platform (-DARC).
|
|
|
|
Author:
|
|
|
|
Ted Miller (tedm) Nov-1991
|
|
|
|
Revision History:
|
|
|
|
Bob Rinne (bobri) Feb-1994
|
|
Moved as actual part of Disk Administrator enlistment instead of being
|
|
copied from ArcInst. This is due to dynamic partition changes. Removed
|
|
string table that made this an internationalized file.
|
|
|
|
--*/
|
|
|
|
#include "fdisk.h"
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
|
|
// Attached disk devices.
|
|
|
|
ULONG CountOfDisks;
|
|
PCHAR *DiskNames;
|
|
|
|
// Information about attached disks.
|
|
|
|
DISKGEOM *DiskGeometryArray;
|
|
|
|
PPARTITION *PrimaryPartitions,
|
|
*LogicalVolumes;
|
|
|
|
// A 'signature' is a unique 4-byte value immediately preceeding the
|
|
// partition table in the MBR.
|
|
|
|
PULONG Signatures;
|
|
|
|
// Array keeping track of whether each disk is off line.
|
|
|
|
PBOOLEAN OffLine;
|
|
|
|
// Keeps track of whether changes have been requested
|
|
// to each disk's partition structure.
|
|
|
|
BOOLEAN *ChangesRequested;
|
|
BOOLEAN *ChangesCommitted;
|
|
|
|
|
|
// Value used to indicate that the partition entry has changed but in a non-
|
|
// destructive way (ie, made active/inactive).
|
|
|
|
#define CHANGED_DONT_ZAP ((BOOLEAN)(5))
|
|
|
|
// forward declarations
|
|
|
|
|
|
STATUS_CODE
|
|
OpenDisks(
|
|
VOID
|
|
);
|
|
|
|
VOID
|
|
CloseDisks(
|
|
VOID
|
|
);
|
|
|
|
STATUS_CODE
|
|
GetGeometry(
|
|
VOID
|
|
);
|
|
|
|
BOOLEAN
|
|
CheckIfDiskIsOffLine(
|
|
IN ULONG Disk
|
|
);
|
|
|
|
STATUS_CODE
|
|
InitializePartitionLists(
|
|
VOID
|
|
);
|
|
|
|
STATUS_CODE
|
|
GetRegions(
|
|
IN ULONG Disk,
|
|
IN PPARTITION p,
|
|
IN BOOLEAN WantUsedRegions,
|
|
IN BOOLEAN WantFreeRegions,
|
|
IN BOOLEAN WantLogicalRegions,
|
|
OUT PREGION_DESCRIPTOR *Region,
|
|
OUT ULONG *RegionCount,
|
|
IN REGION_TYPE RegionType
|
|
);
|
|
|
|
BOOLEAN
|
|
AddRegionEntry(
|
|
IN OUT PREGION_DESCRIPTOR *Regions,
|
|
IN OUT ULONG *RegionCount,
|
|
IN ULONG SizeMB,
|
|
IN REGION_TYPE RegionType,
|
|
IN PPARTITION Partition,
|
|
IN LARGE_INTEGER AlignedRegionOffset,
|
|
IN LARGE_INTEGER AlignedRegionSize
|
|
);
|
|
|
|
VOID
|
|
AddPartitionToLinkedList(
|
|
IN PARTITION **Head,
|
|
IN PARTITION *p
|
|
);
|
|
|
|
BOOLEAN
|
|
IsInLinkedList(
|
|
IN PPARTITION p,
|
|
IN PPARTITION List
|
|
);
|
|
|
|
BOOLEAN
|
|
IsInLogicalList(
|
|
IN ULONG Disk,
|
|
IN PPARTITION p
|
|
);
|
|
|
|
BOOLEAN
|
|
IsInPartitionList(
|
|
IN ULONG Disk,
|
|
IN PPARTITION p
|
|
);
|
|
|
|
LARGE_INTEGER
|
|
AlignTowardsDiskStart(
|
|
IN ULONG Disk,
|
|
IN LARGE_INTEGER Offset
|
|
);
|
|
|
|
LARGE_INTEGER
|
|
AlignTowardsDiskEnd(
|
|
IN ULONG Disk,
|
|
IN LARGE_INTEGER Offset
|
|
);
|
|
|
|
VOID
|
|
FreeLinkedPartitionList(
|
|
IN PARTITION **q
|
|
);
|
|
|
|
VOID
|
|
MergeFreePartitions(
|
|
IN PPARTITION p
|
|
);
|
|
|
|
VOID
|
|
FreePartitionInfoLinkedLists(
|
|
IN PARTITION **ListHeadArray
|
|
);
|
|
|
|
LARGE_INTEGER
|
|
DiskLengthBytes(
|
|
IN ULONG Disk
|
|
);
|
|
|
|
PPARTITION
|
|
AllocatePartitionStructure(
|
|
IN ULONG Disk,
|
|
IN LARGE_INTEGER Offset,
|
|
IN LARGE_INTEGER Length,
|
|
IN UCHAR SysID,
|
|
IN BOOLEAN Update,
|
|
IN BOOLEAN Active,
|
|
IN BOOLEAN Recognized
|
|
);
|
|
|
|
STATUS_CODE
|
|
LowFreeFdiskPathList(
|
|
IN OUT PCHAR* PathList,
|
|
IN ULONG ListLength
|
|
);
|
|
|
|
STATUS_CODE
|
|
LowQueryFdiskPathList(
|
|
OUT PCHAR **PathList,
|
|
OUT PULONG ListLength
|
|
);
|
|
|
|
|
|
STATUS_CODE
|
|
FdiskInitialize(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine initializes the partitioning engine, including allocating
|
|
arrays, determining attached disk devices, and reading their
|
|
partition tables.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
OK_STATUS or error code.
|
|
|
|
--*/
|
|
|
|
{
|
|
STATUS_CODE status;
|
|
ULONG i;
|
|
|
|
|
|
if ((status = LowQueryFdiskPathList(&DiskNames, &CountOfDisks)) != OK_STATUS) {
|
|
return status;
|
|
}
|
|
|
|
DiskGeometryArray = NULL;
|
|
PrimaryPartitions = NULL;
|
|
LogicalVolumes = NULL;
|
|
|
|
if (((DiskGeometryArray = AllocateMemory(CountOfDisks * sizeof(DISKGEOM ))) == NULL)
|
|
|| ((ChangesRequested = AllocateMemory(CountOfDisks * sizeof(BOOLEAN ))) == NULL)
|
|
|| ((ChangesCommitted = AllocateMemory(CountOfDisks * sizeof(BOOLEAN ))) == NULL)
|
|
|| ((PrimaryPartitions = AllocateMemory(CountOfDisks * sizeof(PPARTITION))) == NULL)
|
|
|| ((Signatures = AllocateMemory(CountOfDisks * sizeof(ULONG ))) == NULL)
|
|
|| ((OffLine = AllocateMemory(CountOfDisks * sizeof(BOOLEAN ))) == NULL)
|
|
|| ((LogicalVolumes = AllocateMemory(CountOfDisks * sizeof(PPARTITION))) == NULL))
|
|
{
|
|
RETURN_OUT_OF_MEMORY;
|
|
}
|
|
|
|
for (i=0; i<CountOfDisks; i++) {
|
|
PrimaryPartitions[i] = NULL;
|
|
LogicalVolumes[i] = NULL;
|
|
ChangesRequested[i] = FALSE;
|
|
ChangesCommitted[i] = FALSE;
|
|
OffLine[i] = CheckIfDiskIsOffLine(i);
|
|
}
|
|
|
|
if (((status = GetGeometry() ) != OK_STATUS)
|
|
|| ((status = InitializePartitionLists()) != OK_STATUS)) {
|
|
return status;
|
|
}
|
|
|
|
return OK_STATUS;
|
|
}
|
|
|
|
|
|
VOID
|
|
FdiskCleanUp(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine deallocates storage used by the partitioning engine.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
LowFreeFdiskPathList(DiskNames, CountOfDisks);
|
|
|
|
if (DiskGeometryArray != NULL) {
|
|
FreeMemory(DiskGeometryArray);
|
|
}
|
|
if (PrimaryPartitions != NULL) {
|
|
FreePartitionInfoLinkedLists(PrimaryPartitions);
|
|
FreeMemory(PrimaryPartitions);
|
|
}
|
|
if (LogicalVolumes != NULL) {
|
|
FreePartitionInfoLinkedLists(LogicalVolumes);
|
|
FreeMemory(LogicalVolumes);
|
|
}
|
|
if (ChangesRequested != NULL) {
|
|
FreeMemory(ChangesRequested);
|
|
}
|
|
if (ChangesCommitted != NULL) {
|
|
FreeMemory(ChangesCommitted);
|
|
}
|
|
if (OffLine != NULL) {
|
|
FreeMemory(OffLine);
|
|
}
|
|
if (Signatures != NULL) {
|
|
FreeMemory(Signatures);
|
|
}
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
CheckIfDiskIsOffLine(
|
|
IN ULONG Disk
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Determine whether a disk is off-line by attempting to open it.
|
|
If this is diskman, also attempt to read from it.
|
|
|
|
Arguments:
|
|
|
|
Disk - supplies number of the disk to check
|
|
|
|
Return Value:
|
|
|
|
TRUE if disk is off-line, FALSE is disk is on-line.
|
|
|
|
--*/
|
|
|
|
{
|
|
HANDLE_T handle;
|
|
UINT errorMode;
|
|
BOOLEAN isOffLine = TRUE;
|
|
|
|
errorMode = SetErrorMode(SEM_FAILCRITICALERRORS);
|
|
if (LowOpenDisk(GetDiskName(Disk), &handle) == OK_STATUS) {
|
|
|
|
ULONG dummy,
|
|
bps;
|
|
PVOID unalignedBuffer,
|
|
buffer;
|
|
|
|
// The open might succeed even if the disk is off line. So to be
|
|
// sure, read the first sector from the disk.
|
|
|
|
if (NT_SUCCESS(LowGetDriveGeometry(GetDiskName(Disk), &dummy, &bps, &dummy, &dummy))) {
|
|
|
|
unalignedBuffer = Malloc(2*bps);
|
|
buffer = (PVOID)(((ULONG)unalignedBuffer+bps) & ~(bps-1));
|
|
|
|
if (NT_SUCCESS(LowReadSectors(handle,bps,0,1,buffer))) {
|
|
isOffLine = FALSE;
|
|
}
|
|
|
|
Free(unalignedBuffer);
|
|
} else {
|
|
|
|
// It is possible this is a removable drive.
|
|
|
|
if (IsRemovable(Disk)) {
|
|
isOffLine = FALSE;
|
|
}
|
|
}
|
|
LowCloseDisk(handle);
|
|
}
|
|
SetErrorMode(errorMode);
|
|
|
|
return isOffLine;
|
|
}
|
|
|
|
|
|
STATUS_CODE
|
|
GetGeometry(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine determines disk geometry for each disk device.
|
|
Disk geometry includes heads, sectors per track, cylinder count,
|
|
and bytes per sector. It also includes bytes per track and
|
|
bytes per cylinder, which are calculated from the other values
|
|
for the convenience of the rest of this module.
|
|
|
|
Geometry information is placed in the DiskGeometryArray global variable.
|
|
|
|
Geometry information is undefined for an off-line disk.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
OK_STATUS or error code.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG i;
|
|
STATUS_CODE status;
|
|
ULONG TotalSectorCount,
|
|
SectorSize,
|
|
SectorsPerTrack,
|
|
Heads;
|
|
|
|
for (i=0; i<CountOfDisks; i++) {
|
|
|
|
if (OffLine[i]) {
|
|
continue;
|
|
}
|
|
|
|
if ((status = LowGetDriveGeometry(DiskNames[i],&TotalSectorCount,&SectorSize,&SectorsPerTrack,&Heads)) != OK_STATUS) {
|
|
return(status);
|
|
}
|
|
|
|
DiskGeometryArray[i].BytesPerSector = SectorSize;
|
|
DiskGeometryArray[i].SectorsPerTrack = SectorsPerTrack;
|
|
DiskGeometryArray[i].Heads = Heads;
|
|
DiskGeometryArray[i].Cylinders.QuadPart = (TotalSectorCount / (SectorsPerTrack * Heads));
|
|
DiskGeometryArray[i].BytesPerTrack = SectorsPerTrack * SectorSize;
|
|
DiskGeometryArray[i].BytesPerCylinder = SectorsPerTrack * SectorSize * Heads;
|
|
}
|
|
return(OK_STATUS);
|
|
}
|
|
|
|
|
|
#if i386
|
|
VOID
|
|
SetPartitionActiveFlag(
|
|
IN PREGION_DESCRIPTOR Region,
|
|
IN UCHAR value
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
|
|
{
|
|
PPARTITION p = ((PREGION_DATA)Region->Reserved)->Partition;
|
|
|
|
if((UCHAR)p->Active != value) {
|
|
|
|
//
|
|
// Unfortuneately, the Update flag becomes the RewritePartition flag
|
|
// at commit time. This causes us to zap the boot sector. To avoid
|
|
// this, we use a spacial non-boolean value that can be checked for
|
|
// at commit time and that will cause us NOT to zap the bootsector
|
|
// even though RewritePartition will be TRUE.
|
|
//
|
|
|
|
p->Active = value;
|
|
if(!p->Update) {
|
|
p->Update = CHANGED_DONT_ZAP;
|
|
}
|
|
ChangesRequested[p->Disk] = TRUE;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
VOID
|
|
DetermineCreateSizeAndOffset(
|
|
IN PREGION_DESCRIPTOR Region,
|
|
IN LARGE_INTEGER MinimumSize,
|
|
IN ULONG CreationSizeMB,
|
|
IN REGION_TYPE Type,
|
|
OUT PLARGE_INTEGER CreationStart,
|
|
OUT PLARGE_INTEGER CreationSize
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Determine the actual offset and size of the partition, given the
|
|
size in megabytes.
|
|
|
|
Arguments:
|
|
|
|
Region - a region descriptor returned by GetDiskRegions(). Must
|
|
be an unused region.
|
|
|
|
MinimumSize - if non-0, this is the minimum size that the partition
|
|
or logical drive can be.
|
|
|
|
CreationSizeMB - If MinimumSize is 0, size of partition to create, in MB.
|
|
|
|
Type - REGION_PRIMARY, REGION_EXTENDED, or REGION_LOGICAL, for
|
|
creating a primary partition, extended partition, or
|
|
logical volume, respectively.
|
|
|
|
CreationStart - receives the offset where the partition should be placed.
|
|
|
|
CreationSize - receives the exact size for the partition.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PREGION_DATA createData = Region->Reserved;
|
|
ULONG bpc = DiskGeometryArray[Region->Disk].BytesPerCylinder;
|
|
ULONG bpt = DiskGeometryArray[Region->Disk].BytesPerTrack;
|
|
LARGE_INTEGER cSize,
|
|
cStart,
|
|
mod;
|
|
|
|
//
|
|
// If we are creating a partition at offset 0, adjust the aligned region
|
|
// offset and the aligned region size, because no partition can actually
|
|
// start at offset 0.
|
|
//
|
|
|
|
if (!createData->AlignedRegionOffset.QuadPart) {
|
|
|
|
LARGE_INTEGER delta;
|
|
|
|
if (Type == REGION_EXTENDED) {
|
|
delta.QuadPart = bpc;
|
|
} else {
|
|
delta.QuadPart = bpt;
|
|
}
|
|
|
|
createData->AlignedRegionOffset = delta;
|
|
createData->AlignedRegionSize.QuadPart -= delta.QuadPart;
|
|
}
|
|
|
|
cStart = createData->AlignedRegionOffset;
|
|
if (!MinimumSize.QuadPart) {
|
|
cSize.QuadPart = UInt32x32To64(CreationSizeMB, ONE_MEG);
|
|
} else {
|
|
cSize = MinimumSize;
|
|
if (Type == REGION_LOGICAL) {
|
|
cSize.QuadPart += bpt;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Decide whether to align the ending cylinder up or down.
|
|
// If the offset of end of the partition is more than half way into the
|
|
// final cylinder, align towrds the disk end. Otherwise align toward
|
|
// the disk start.
|
|
//
|
|
|
|
mod.QuadPart = (cStart.QuadPart + cSize.QuadPart) % bpc;
|
|
if (mod.QuadPart) {
|
|
|
|
if ((MinimumSize.QuadPart) || (mod.QuadPart > (bpc/2))) {
|
|
cSize.QuadPart += ((LONGLONG)bpc - mod.QuadPart);
|
|
} else {
|
|
cSize.QuadPart -= mod.QuadPart; // snap downwards to cyl boundary
|
|
}
|
|
}
|
|
|
|
if (cSize.QuadPart > createData->AlignedRegionSize.QuadPart) {
|
|
|
|
//
|
|
// Space available in the free space isn't large enough to accomodate
|
|
// the request; just use the entire free space.
|
|
//
|
|
|
|
cSize = createData->AlignedRegionSize;
|
|
}
|
|
|
|
*CreationStart = cStart;
|
|
*CreationSize = cSize;
|
|
}
|
|
|
|
|
|
STATUS_CODE
|
|
CreatePartitionEx(
|
|
IN PREGION_DESCRIPTOR Region,
|
|
IN LARGE_INTEGER MinimumSize,
|
|
IN ULONG CreationSizeMB,
|
|
IN REGION_TYPE Type,
|
|
IN UCHAR SysId
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine creates a partition from a free region on the disk. The
|
|
partition is always created at the beginning of the free space, and any
|
|
left over space at the end is kept on the free space list.
|
|
|
|
Arguments:
|
|
|
|
Region - a region descriptor returned by GetDiskRegions(). Must
|
|
be an unused region.
|
|
|
|
CreationSizeMB - size of partition to create, in MB.
|
|
|
|
Type - REGION_PRIMARY, REGION_EXTENDED, or REGION_LOGICAL, for
|
|
creating a primary partition, extended pasrtition, or
|
|
logical volume, respectively.
|
|
|
|
SysId - system ID byte to be assigned to the partition
|
|
|
|
Return Value:
|
|
|
|
OK_STATUS or error code.
|
|
|
|
--*/
|
|
|
|
{
|
|
PPARTITION p1,
|
|
p2,
|
|
p3;
|
|
PREGION_DATA createData = Region->Reserved;
|
|
LARGE_INTEGER creationStart,
|
|
creationSize,
|
|
leftOver,
|
|
offset,
|
|
length;
|
|
PPARTITION *partitionList;
|
|
|
|
DetermineCreateSizeAndOffset(Region,
|
|
MinimumSize,
|
|
CreationSizeMB,
|
|
Type,
|
|
&creationStart,
|
|
&creationSize);
|
|
|
|
// now we've got the start and size of the partition to be created.
|
|
// If there's left-over at the beginning of the free space (after
|
|
// alignment), make a new PARTITION structure.
|
|
|
|
p1 = NULL;
|
|
offset = createData->Partition->Offset;
|
|
length = createData->Partition->Length;
|
|
leftOver.QuadPart = creationStart.QuadPart - offset.QuadPart;
|
|
|
|
if (leftOver.QuadPart > 0) {
|
|
|
|
p1 = AllocatePartitionStructure(Region->Disk,
|
|
createData->Partition->Offset,
|
|
leftOver,
|
|
SYSID_UNUSED,
|
|
FALSE,
|
|
FALSE,
|
|
FALSE);
|
|
if (p1 == NULL) {
|
|
RETURN_OUT_OF_MEMORY;
|
|
}
|
|
}
|
|
|
|
// make a new partition structure for space being left free as
|
|
// a result of this creation.
|
|
|
|
p2 = NULL;
|
|
leftOver.QuadPart = (offset.QuadPart + length.QuadPart) -
|
|
(creationStart.QuadPart + creationSize.QuadPart);
|
|
|
|
if (leftOver.QuadPart) {
|
|
LARGE_INTEGER temp;
|
|
|
|
temp.QuadPart = creationStart.QuadPart + creationSize.QuadPart;
|
|
p2 = AllocatePartitionStructure(Region->Disk,
|
|
temp,
|
|
leftOver,
|
|
SYSID_UNUSED,
|
|
FALSE,
|
|
FALSE,
|
|
FALSE);
|
|
if (p2 == NULL) {
|
|
RETURN_OUT_OF_MEMORY;
|
|
}
|
|
}
|
|
|
|
// adjust the free partition's fields.
|
|
|
|
createData->Partition->Offset = creationStart;
|
|
createData->Partition->Length = creationSize;
|
|
createData->Partition->SysID = SysId;
|
|
createData->Partition->Update = TRUE;
|
|
createData->Partition->Recognized = TRUE;
|
|
|
|
// if we just created an extended partition, show the whole thing
|
|
// as one free logical region.
|
|
|
|
if (Type == REGION_EXTENDED) {
|
|
|
|
p3 = AllocatePartitionStructure(Region->Disk,
|
|
creationStart,
|
|
creationSize,
|
|
SYSID_UNUSED,
|
|
FALSE,
|
|
FALSE,
|
|
FALSE);
|
|
if (p3 == NULL) {
|
|
RETURN_OUT_OF_MEMORY;
|
|
}
|
|
AddPartitionToLinkedList(&LogicalVolumes[Region->Disk], p3);
|
|
}
|
|
|
|
partitionList = (Type == REGION_LOGICAL)
|
|
? &LogicalVolumes[Region->Disk]
|
|
: &PrimaryPartitions[Region->Disk];
|
|
|
|
if (p1) {
|
|
AddPartitionToLinkedList(partitionList, p1);
|
|
}
|
|
if (p2) {
|
|
AddPartitionToLinkedList(partitionList, p2);
|
|
}
|
|
|
|
MergeFreePartitions(*partitionList);
|
|
ChangesRequested[Region->Disk] = TRUE;
|
|
return(OK_STATUS);
|
|
}
|
|
|
|
|
|
STATUS_CODE
|
|
CreatePartition(
|
|
IN PREGION_DESCRIPTOR Region,
|
|
IN ULONG CreationSizeMB,
|
|
IN REGION_TYPE Type
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Create a partition.
|
|
|
|
Arguments:
|
|
|
|
Region - A region descriptor pointer.
|
|
CreationSizeMB - the size for the new region.
|
|
Type - the type of region being created.
|
|
|
|
Return Value:
|
|
|
|
OK_STATUS or error code
|
|
|
|
--*/
|
|
|
|
{
|
|
LARGE_INTEGER zero;
|
|
|
|
zero.QuadPart = 0;
|
|
return CreatePartitionEx(Region,
|
|
zero,
|
|
CreationSizeMB,
|
|
Type,
|
|
(UCHAR)((Type == REGION_EXTENDED) ? SYSID_EXTENDED
|
|
: SYSID_BIGFAT));
|
|
}
|
|
|
|
|
|
STATUS_CODE
|
|
DeletePartition(
|
|
IN PREGION_DESCRIPTOR Region
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine deletes a partition, returning its space to the
|
|
free space on the disk. If deleting the extended partition,
|
|
all logical volumes within it are also deleted.
|
|
|
|
Arguments:
|
|
|
|
Region - a region descriptor returned by GetDiskRegions(). Must
|
|
be a used region.
|
|
|
|
Return Value:
|
|
|
|
OK_STATUS or error code.
|
|
|
|
--*/
|
|
|
|
{
|
|
PREGION_DATA RegionData = Region->Reserved;
|
|
PPARTITION *PartitionList;
|
|
|
|
if(IsExtended(Region->SysID)) {
|
|
|
|
// Deleting extended partition. Also delete all logical volumes.
|
|
|
|
FreeLinkedPartitionList(&LogicalVolumes[Region->Disk]);
|
|
}
|
|
|
|
RegionData->Partition->SysID = SYSID_UNUSED;
|
|
RegionData->Partition->Update = TRUE;
|
|
RegionData->Partition->Active = FALSE;
|
|
RegionData->Partition->OriginalPartitionNumber = 0;
|
|
|
|
PartitionList = (Region->RegionType == REGION_LOGICAL)
|
|
? &LogicalVolumes[Region->Disk]
|
|
: &PrimaryPartitions[Region->Disk];
|
|
|
|
MergeFreePartitions(*PartitionList);
|
|
ChangesRequested[Region->Disk] = TRUE;
|
|
return OK_STATUS;
|
|
}
|
|
|
|
|
|
STATUS_CODE
|
|
GetDiskRegions(
|
|
IN ULONG Disk,
|
|
IN BOOLEAN WantUsedRegions,
|
|
IN BOOLEAN WantFreeRegions,
|
|
IN BOOLEAN WantPrimaryRegions,
|
|
IN BOOLEAN WantLogicalRegions,
|
|
OUT PREGION_DESCRIPTOR *Region,
|
|
OUT ULONG *RegionCount
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine returns an array of region descriptors to the caller.
|
|
A region desscriptor describes a space on the disk, either used
|
|
or free. The caller can control which type of regions are returned.
|
|
|
|
The caller must free the returned array via FreeRegionArray().
|
|
|
|
Arguments:
|
|
|
|
Disk - index of disk whose regions are to be returned
|
|
|
|
WantUsedRegions - whether to return used disk regions
|
|
|
|
WantFreeRegions - whether to return free disk regions
|
|
|
|
WantPrimaryRegions - whether to return regions not in the
|
|
extended partition
|
|
|
|
WantLogicalRegions - whether to return regions within the
|
|
extended partition
|
|
|
|
Region - where to put a pointer to the array of regions
|
|
|
|
RegionCount - where to put the number of items in the returned
|
|
Region array
|
|
|
|
Return Value:
|
|
|
|
OK_STATUS or error code.
|
|
|
|
--*/
|
|
|
|
{
|
|
*Region = AllocateMemory(0);
|
|
*RegionCount = 0;
|
|
|
|
if (WantPrimaryRegions) {
|
|
return GetRegions(Disk,
|
|
PrimaryPartitions[Disk],
|
|
WantUsedRegions,
|
|
WantFreeRegions,
|
|
WantLogicalRegions,
|
|
Region,
|
|
RegionCount,
|
|
REGION_PRIMARY);
|
|
} else if (WantLogicalRegions) {
|
|
return GetRegions(Disk,
|
|
LogicalVolumes[Disk],
|
|
WantUsedRegions,
|
|
WantFreeRegions,
|
|
FALSE,
|
|
Region,
|
|
RegionCount,
|
|
REGION_LOGICAL);
|
|
}
|
|
return OK_STATUS;
|
|
}
|
|
|
|
|
|
// workers for GetDiskRegions
|
|
|
|
STATUS_CODE
|
|
GetRegions(
|
|
IN ULONG Disk,
|
|
IN PPARTITION p,
|
|
IN BOOLEAN WantUsedRegions,
|
|
IN BOOLEAN WantFreeRegions,
|
|
IN BOOLEAN WantLogicalRegions,
|
|
OUT PREGION_DESCRIPTOR *Region,
|
|
OUT ULONG *RegionCount,
|
|
IN REGION_TYPE RegionType
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
|
|
{
|
|
STATUS_CODE status;
|
|
LARGE_INTEGER alignedOffset,
|
|
alignedSize,
|
|
temp;
|
|
ULONG sizeMB;
|
|
|
|
while (p) {
|
|
|
|
if (p->SysID == SYSID_UNUSED) {
|
|
|
|
if (WantFreeRegions) {
|
|
|
|
alignedOffset = AlignTowardsDiskEnd(p->Disk,p->Offset);
|
|
temp.QuadPart = p->Offset.QuadPart + p->Length.QuadPart;
|
|
temp = AlignTowardsDiskStart(p->Disk, temp);
|
|
alignedSize.QuadPart = temp.QuadPart - alignedOffset.QuadPart;
|
|
sizeMB = SIZEMB(alignedSize);
|
|
|
|
// Show the space free if it is greater than 1 meg, AND
|
|
// it is not a space starting at the beginning of the disk
|
|
// and of length <= 1 cylinder.
|
|
// This prevents the user from seeing the first cylinder
|
|
// of the disk as free (could otherwise happen with an
|
|
// extended partition starting on cylinder 1 and cylinders
|
|
// of 1 megabyte or larger).
|
|
|
|
if ((alignedSize.QuadPart > 0) && sizeMB &&
|
|
((p->Offset.QuadPart) ||
|
|
(p->Length.QuadPart > (DiskGeometryArray[p->Disk].BytesPerCylinder)))) {
|
|
if (!AddRegionEntry(Region,
|
|
RegionCount,
|
|
sizeMB,
|
|
RegionType,
|
|
p,
|
|
alignedOffset,
|
|
alignedSize)) {
|
|
RETURN_OUT_OF_MEMORY;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
|
|
if (WantUsedRegions) {
|
|
|
|
alignedOffset = p->Offset;
|
|
alignedSize = p->Length;
|
|
sizeMB = SIZEMB(alignedSize);
|
|
|
|
if (!AddRegionEntry(Region,
|
|
RegionCount,
|
|
sizeMB,
|
|
RegionType,
|
|
p,
|
|
alignedOffset,
|
|
alignedSize)) {
|
|
RETURN_OUT_OF_MEMORY;
|
|
}
|
|
}
|
|
|
|
if (IsExtended(p->SysID) && WantLogicalRegions) {
|
|
status = GetRegions(Disk,
|
|
LogicalVolumes[Disk],
|
|
WantUsedRegions,
|
|
WantFreeRegions,
|
|
FALSE,
|
|
Region,
|
|
RegionCount,
|
|
REGION_LOGICAL);
|
|
if (status != OK_STATUS) {
|
|
return status;
|
|
}
|
|
}
|
|
}
|
|
p = p->Next;
|
|
}
|
|
return OK_STATUS;
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
AddRegionEntry(
|
|
OUT PREGION_DESCRIPTOR *Regions,
|
|
OUT ULONG *RegionCount,
|
|
IN ULONG SizeMB,
|
|
IN REGION_TYPE RegionType,
|
|
IN PPARTITION Partition,
|
|
IN LARGE_INTEGER AlignedRegionOffset,
|
|
IN LARGE_INTEGER AlignedRegionSize
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Allocate space for the region descriptor and copy the provided data.
|
|
|
|
Arguments:
|
|
|
|
Regions - return the pointer to the new region
|
|
RegionCount - number of regions on the disk so far
|
|
SizeMB - size of the region
|
|
RegionType - type of the region
|
|
Partition - partition structure with other related information
|
|
AlignedRegionOffset - region starting location
|
|
AlignedRegionSize - region size.
|
|
|
|
Return Value:
|
|
|
|
TRUE - The region was added successfully
|
|
FALSE - it wasn't
|
|
|
|
--*/
|
|
|
|
{
|
|
PREGION_DESCRIPTOR regionDescriptor;
|
|
PREGION_DATA regionData;
|
|
|
|
regionDescriptor = ReallocateMemory(*Regions,(((*RegionCount) + 1) * sizeof(REGION_DESCRIPTOR)) + 20);
|
|
if (regionDescriptor == NULL) {
|
|
return FALSE;
|
|
} else {
|
|
*Regions = regionDescriptor;
|
|
(*RegionCount)++;
|
|
}
|
|
|
|
regionDescriptor = &(*Regions)[(*RegionCount)-1];
|
|
|
|
if (!(regionDescriptor->Reserved = AllocateMemory(sizeof(REGION_DATA)))) {
|
|
return FALSE;
|
|
}
|
|
|
|
regionDescriptor->Disk = Partition->Disk;
|
|
regionDescriptor->SysID = Partition->SysID;
|
|
regionDescriptor->SizeMB = SizeMB;
|
|
regionDescriptor->Active = Partition->Active;
|
|
regionDescriptor->Recognized = Partition->Recognized;
|
|
regionDescriptor->PartitionNumber = Partition->PartitionNumber;
|
|
regionDescriptor->OriginalPartitionNumber = Partition->OriginalPartitionNumber;
|
|
regionDescriptor->RegionType = RegionType;
|
|
regionDescriptor->PersistentData = Partition->PersistentData;
|
|
|
|
regionData = regionDescriptor->Reserved;
|
|
|
|
regionData->Partition = Partition;
|
|
regionData->AlignedRegionOffset = AlignedRegionOffset;
|
|
regionData->AlignedRegionSize = AlignedRegionSize;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
VOID
|
|
FreeRegionArray(
|
|
IN PREGION_DESCRIPTOR Region,
|
|
IN ULONG RegionCount
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine frees a region array returned by GetDiskRegions().
|
|
|
|
Arguments:
|
|
|
|
Region - pointer to the array of regions to be freed
|
|
|
|
RegionCount - number of items in the Region array
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG i;
|
|
|
|
for (i = 0; i < RegionCount; i++) {
|
|
|
|
if (Region[i].Reserved) {
|
|
FreeMemory(Region[i].Reserved);
|
|
}
|
|
}
|
|
FreeMemory(Region);
|
|
}
|
|
|
|
|
|
VOID
|
|
AddPartitionToLinkedList(
|
|
IN OUT PARTITION **Head,
|
|
IN PARTITION *p
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine adds a PARTITION structure to a doubly-linked
|
|
list, sorted by the Offset field in ascending order.
|
|
|
|
Arguments:
|
|
|
|
Head - pointer to pointer to first element in list
|
|
p - pointer to item to be added to list
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PARTITION *cur,
|
|
*prev;
|
|
|
|
if ((cur = *Head) == NULL) {
|
|
*Head = p;
|
|
return;
|
|
}
|
|
|
|
if (p->Offset.QuadPart < cur->Offset.QuadPart) {
|
|
p->Next = cur;
|
|
cur->Prev = p;
|
|
*Head = p;
|
|
return;
|
|
}
|
|
|
|
prev = *Head;
|
|
cur = cur->Next;
|
|
|
|
while (cur) {
|
|
if (p->Offset.QuadPart < cur->Offset.QuadPart) {
|
|
|
|
p->Next = cur;
|
|
p->Prev = prev;
|
|
prev->Next = p;
|
|
cur->Prev = p;
|
|
return;
|
|
}
|
|
prev = cur;
|
|
cur = cur->Next;
|
|
}
|
|
|
|
prev->Next = p;
|
|
p->Prev = prev;
|
|
return;
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
IsInLinkedList(
|
|
IN PPARTITION p,
|
|
IN PPARTITION List
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine determines whether a PARTITION element is in
|
|
a given linked list of PARTITION elements.
|
|
|
|
Arguments:
|
|
|
|
p - pointer to element to be checked for
|
|
List - first element in list to be scanned
|
|
|
|
Return Value:
|
|
|
|
true if p found in List, false otherwise
|
|
|
|
--*/
|
|
|
|
{
|
|
while (List) {
|
|
if (p == List) {
|
|
return TRUE;
|
|
}
|
|
List = List->Next;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
IsInLogicalList(
|
|
IN ULONG Disk,
|
|
IN PPARTITION p
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine determines whether a PARTITION element is in
|
|
the logical volume list for a given disk.
|
|
|
|
Arguments:
|
|
|
|
Disk - index of disk to be checked
|
|
p - pointer to element to be checked for
|
|
|
|
Return Value:
|
|
|
|
true if p found in Disk's logical volume list, false otherwise
|
|
|
|
--*/
|
|
|
|
{
|
|
return IsInLinkedList(p, LogicalVolumes[Disk]);
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
IsInPartitionList(
|
|
IN ULONG Disk,
|
|
IN PPARTITION p
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine determines whether a PARTITION element is in
|
|
the primary partition list for a given disk.
|
|
|
|
Arguments:
|
|
|
|
Disk - index of disk to be checked
|
|
p - pointer to element to be checked for
|
|
|
|
Return Value:
|
|
|
|
true if p found in Disk's primary partition list, false otherwise
|
|
|
|
--*/
|
|
|
|
{
|
|
return IsInLinkedList(p, PrimaryPartitions[Disk]);
|
|
}
|
|
|
|
|
|
VOID
|
|
MergeFreePartitions(
|
|
IN PPARTITION p
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine merges adjacent free space elements in the
|
|
given linked list of PARTITION elements. It is designed
|
|
to be called after adding or deleting a partition.
|
|
|
|
Arguments:
|
|
|
|
p - pointer to first item in list whose free elements are to
|
|
be merged.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PPARTITION next;
|
|
|
|
while (p && p->Next) {
|
|
|
|
if ((p->SysID == SYSID_UNUSED) && (p->Next->SysID == SYSID_UNUSED)) {
|
|
|
|
next = p->Next;
|
|
p->Length.QuadPart = (next->Offset.QuadPart + next->Length.QuadPart) - p->Offset.QuadPart;
|
|
|
|
if (p->Next = next->Next) {
|
|
next->Next->Prev = p;
|
|
}
|
|
|
|
FreeMemory(next);
|
|
|
|
} else {
|
|
p = p->Next;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
PPARTITION
|
|
FindPartitionElement(
|
|
IN ULONG Disk,
|
|
IN ULONG Partition
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine locates a PARTITION element for a disk/partition
|
|
number pair. The partition number is the number that the
|
|
system assigns to the partition.
|
|
|
|
Arguments:
|
|
|
|
Disk - index of relevent disk
|
|
|
|
Partition - partition number of partition to find
|
|
|
|
Return Value:
|
|
|
|
pointer to PARTITION element, or NULL if not found.
|
|
|
|
--*/
|
|
|
|
{
|
|
PPARTITION p;
|
|
|
|
p = PrimaryPartitions[Disk];
|
|
while (p) {
|
|
if ((p->SysID != SYSID_UNUSED) && !IsExtended(p->SysID) && (p->PartitionNumber == Partition)) {
|
|
return p;
|
|
}
|
|
p = p->Next;
|
|
}
|
|
p = LogicalVolumes[Disk];
|
|
while (p) {
|
|
if ((p->SysID != SYSID_UNUSED) && (p->PartitionNumber == Partition)) {
|
|
return p;
|
|
}
|
|
p = p->Next;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
VOID
|
|
SetSysID(
|
|
IN ULONG Disk,
|
|
IN ULONG Partition,
|
|
IN UCHAR SysID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine sets the system id of the given partition
|
|
on the given disk.
|
|
|
|
Arguments:
|
|
|
|
Disk - index of relevent disk
|
|
|
|
Partition - partition number of relevent partition
|
|
|
|
SysID - new system ID for Partition on Disk
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PPARTITION p = FindPartitionElement(Disk,Partition);
|
|
|
|
if (p) {
|
|
p->SysID = SysID;
|
|
if (!p->Update) {
|
|
p->Update = CHANGED_DONT_ZAP;
|
|
}
|
|
ChangesRequested[p->Disk] = TRUE;
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
SetSysID2(
|
|
IN PREGION_DESCRIPTOR Region,
|
|
IN UCHAR SysID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
|
|
{
|
|
PPARTITION p = ((PREGION_DATA)(Region->Reserved))->Partition;
|
|
|
|
p->SysID = SysID;
|
|
if (!p->Update) {
|
|
p->Update = CHANGED_DONT_ZAP;
|
|
}
|
|
ChangesRequested[p->Disk] = TRUE;
|
|
}
|
|
|
|
|
|
VOID
|
|
FreeLinkedPartitionList(
|
|
IN OUT PPARTITION *q
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine frees a linked list of PARTITION elements. The head
|
|
pointer is set to NULL.
|
|
|
|
Arguments:
|
|
|
|
p - pointer to pointer to first element of list to free.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PARTITION *n;
|
|
PARTITION *p = *q;
|
|
|
|
while(p) {
|
|
n = p->Next;
|
|
FreeMemory(p);
|
|
p = n;
|
|
}
|
|
*q = NULL;
|
|
}
|
|
|
|
|
|
VOID
|
|
FreePartitionInfoLinkedLists(
|
|
IN PPARTITION *ListHeadArray
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine frees the linked lists of PARTITION elements
|
|
for each disk.
|
|
|
|
Arguments:
|
|
|
|
ListHeadArray - pointer to array of pointers to first elements of
|
|
PARTITION element lists.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG i;
|
|
|
|
for (i = 0; i < CountOfDisks; i++) {
|
|
|
|
FreeLinkedPartitionList(&ListHeadArray[i]);
|
|
}
|
|
}
|
|
|
|
|
|
PPARTITION
|
|
AllocatePartitionStructure(
|
|
IN ULONG Disk,
|
|
IN LARGE_INTEGER Offset,
|
|
IN LARGE_INTEGER Length,
|
|
IN UCHAR SysID,
|
|
IN BOOLEAN Update,
|
|
IN BOOLEAN Active,
|
|
IN BOOLEAN Recognized
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine allocates space for, and initializes a PARTITION
|
|
structure.
|
|
|
|
Arguments:
|
|
|
|
Disk - index of disk, one of whose regions the new PARTITION
|
|
strucure describes.
|
|
Offset - byte offset of region on the disk
|
|
Length - length in bytes of the region
|
|
SysID - system id of region, of SYSID_UNUSED of this PARTITION
|
|
is actually a free space.
|
|
Update - whether this PARTITION is dirty, ie, has changed and needs
|
|
to be written to disk.
|
|
Active - flag for the BootIndicator field in a partition table entry,
|
|
indicates to the x86 master boot program which partition
|
|
is active.
|
|
Recognized - whether the partition is a type recognized by NT
|
|
|
|
Return Value:
|
|
|
|
NULL if allocation failed, or new initialized PARTITION strucure.
|
|
|
|
--*/
|
|
|
|
{
|
|
PPARTITION p = AllocateMemory(sizeof(PARTITION));
|
|
|
|
if (p) {
|
|
p->Next = NULL;
|
|
p->Prev = NULL;
|
|
p->Offset = Offset;
|
|
p->Length = Length;
|
|
p->Disk = Disk;
|
|
p->Update = Update;
|
|
p->Active = Active;
|
|
p->Recognized = Recognized;
|
|
p->SysID = SysID;
|
|
p->OriginalPartitionNumber = 0;
|
|
p->PartitionNumber = 0;
|
|
p->PersistentData = 0;
|
|
p->CommitMirrorBreakNeeded = FALSE;
|
|
}
|
|
return(p);
|
|
}
|
|
|
|
|
|
STATUS_CODE
|
|
InitializeFreeSpace(
|
|
IN ULONG Disk,
|
|
IN PPARTITION *PartitionList, // list the free space goes in
|
|
IN LARGE_INTEGER StartOffset,
|
|
IN LARGE_INTEGER Length
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine determines all the free spaces within a given area
|
|
on a disk, allocates PARTITION structures to describe them,
|
|
and adds these structures to the relevent partition list
|
|
(primary partitions or logical volumes).
|
|
|
|
No rounding or alignment is performed here. Spaces of even one
|
|
byte will be counted and inserted in the partition list.
|
|
|
|
Arguments:
|
|
|
|
Disk - index of disk whose free spaces are being sought.
|
|
|
|
PartitionList - pointer to first element on PARTITION list that
|
|
the free spaces will go in.
|
|
|
|
StartOffset - start offset of area on disk to consider (ie, 0 for
|
|
primary spaces or the first byte of the extended
|
|
partition for logical spaces).
|
|
|
|
Length - length of area on disk to consider (ie, size of disk
|
|
for primary spaces or size of extended partition for
|
|
logical spaces).
|
|
|
|
Return Value:
|
|
|
|
OK_STATUS or error code.
|
|
|
|
--*/
|
|
|
|
{
|
|
PPARTITION p = *PartitionList,
|
|
q;
|
|
LARGE_INTEGER start,
|
|
size;
|
|
|
|
start = StartOffset;
|
|
while (p) {
|
|
|
|
size.QuadPart = p->Offset.QuadPart - start.QuadPart;
|
|
if (size.QuadPart > 0) {
|
|
if (!(q = AllocatePartitionStructure(Disk,
|
|
start,
|
|
size,
|
|
SYSID_UNUSED,
|
|
FALSE,
|
|
FALSE,
|
|
FALSE))) {
|
|
RETURN_OUT_OF_MEMORY;
|
|
}
|
|
|
|
AddPartitionToLinkedList(PartitionList, q);
|
|
}
|
|
|
|
start.QuadPart = p->Offset.QuadPart + p->Length.QuadPart;
|
|
p = p->Next;
|
|
}
|
|
|
|
size.QuadPart = (StartOffset.QuadPart + Length.QuadPart) - start.QuadPart;
|
|
if (size.QuadPart > 0) {
|
|
|
|
if (!(q = AllocatePartitionStructure(Disk,
|
|
start,
|
|
size,
|
|
SYSID_UNUSED,
|
|
FALSE,
|
|
FALSE,
|
|
FALSE))) {
|
|
RETURN_OUT_OF_MEMORY;
|
|
}
|
|
|
|
AddPartitionToLinkedList(PartitionList, q);
|
|
}
|
|
|
|
return OK_STATUS;
|
|
}
|
|
|
|
|
|
STATUS_CODE
|
|
InitializeLogicalVolumeList(
|
|
IN ULONG Disk,
|
|
IN PDRIVE_LAYOUT_INFORMATION DriveLayout
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine creates the logical volume linked list of
|
|
PARTITION structures for the given disk.
|
|
|
|
Arguments:
|
|
|
|
Disk - index of disk
|
|
|
|
DriveLayout - pointer to structure describing the raw partition
|
|
layout of the disk.
|
|
|
|
Return Value:
|
|
|
|
OK_STATUS or error code.
|
|
|
|
--*/
|
|
|
|
{
|
|
PPARTITION p,
|
|
q;
|
|
ULONG i,
|
|
j;
|
|
PPARTITION_INFORMATION d;
|
|
LARGE_INTEGER HiddenBytes;
|
|
ULONG BytesPerSector = DiskGeometryArray[Disk].BytesPerSector;
|
|
|
|
FreeLinkedPartitionList(&LogicalVolumes[Disk]);
|
|
|
|
p = PrimaryPartitions[Disk];
|
|
while (p) {
|
|
if (IsExtended(p->SysID)) {
|
|
break;
|
|
}
|
|
p = p->Next;
|
|
}
|
|
|
|
if (p) {
|
|
for (i=ENTRIES_PER_BOOTSECTOR; i<DriveLayout->PartitionCount; i+=ENTRIES_PER_BOOTSECTOR) {
|
|
|
|
for (j=i; j<i+ENTRIES_PER_BOOTSECTOR; j++) {
|
|
|
|
d = &DriveLayout->PartitionEntry[j];
|
|
|
|
if ((d->PartitionType != SYSID_UNUSED) && (d->PartitionType != SYSID_EXTENDED)) {
|
|
LARGE_INTEGER t1,
|
|
t2;
|
|
|
|
HiddenBytes.QuadPart = (LONGLONG)d->HiddenSectors * (LONGLONG)BytesPerSector;
|
|
|
|
t1.QuadPart = d->StartingOffset.QuadPart - HiddenBytes.QuadPart;
|
|
t2.QuadPart = d->PartitionLength.QuadPart + HiddenBytes.QuadPart;
|
|
if (!(q = AllocatePartitionStructure(Disk,
|
|
t1,
|
|
t2,
|
|
d->PartitionType,
|
|
FALSE,
|
|
d->BootIndicator,
|
|
d->RecognizedPartition))) {
|
|
RETURN_OUT_OF_MEMORY;
|
|
}
|
|
|
|
q->PartitionNumber =
|
|
q->OriginalPartitionNumber = d->PartitionNumber;
|
|
AddPartitionToLinkedList(&LogicalVolumes[Disk],q);
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return InitializeFreeSpace(Disk,
|
|
&LogicalVolumes[Disk],
|
|
p->Offset,
|
|
p->Length);
|
|
}
|
|
return OK_STATUS;
|
|
}
|
|
|
|
|
|
STATUS_CODE
|
|
InitializePrimaryPartitionList(
|
|
IN ULONG Disk,
|
|
IN PDRIVE_LAYOUT_INFORMATION DriveLayout
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine creates the primary partition linked list of
|
|
PARTITION structures for the given disk.
|
|
|
|
Arguments:
|
|
|
|
Disk - index of disk
|
|
|
|
DriveLayout - pointer to structure describing the raw partition
|
|
layout of the disk.
|
|
|
|
Return Value:
|
|
|
|
OK_STATUS or error code.
|
|
|
|
--*/
|
|
|
|
{
|
|
LARGE_INTEGER zero;
|
|
ULONG i;
|
|
PPARTITION p;
|
|
PPARTITION_INFORMATION d;
|
|
|
|
zero.QuadPart = 0;
|
|
FreeLinkedPartitionList(&PrimaryPartitions[Disk]);
|
|
|
|
if (DriveLayout->PartitionCount >= ENTRIES_PER_BOOTSECTOR) {
|
|
|
|
for (i=0; i<ENTRIES_PER_BOOTSECTOR; i++) {
|
|
|
|
d = &DriveLayout->PartitionEntry[i];
|
|
|
|
if (d->PartitionType != SYSID_UNUSED) {
|
|
|
|
if (!(p = AllocatePartitionStructure(Disk,
|
|
d->StartingOffset,
|
|
d->PartitionLength,
|
|
d->PartitionType,
|
|
FALSE,
|
|
d->BootIndicator,
|
|
d->RecognizedPartition))) {
|
|
RETURN_OUT_OF_MEMORY;
|
|
}
|
|
|
|
p->PartitionNumber =
|
|
p->OriginalPartitionNumber = IsExtended(p->SysID)
|
|
? 0
|
|
: d->PartitionNumber;
|
|
|
|
AddPartitionToLinkedList(&PrimaryPartitions[Disk],p);
|
|
}
|
|
}
|
|
}
|
|
return InitializeFreeSpace(Disk,
|
|
&PrimaryPartitions[Disk],
|
|
zero,
|
|
DiskLengthBytes(Disk));
|
|
}
|
|
|
|
|
|
VOID
|
|
ReconcilePartitionNumbers(
|
|
ULONG Disk,
|
|
PDRIVE_LAYOUT_INFORMATION DriveLayout
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
With dynamic partitioning, the partitions on the disk will no longer
|
|
follow sequencial numbering schemes. It will be possible for a disk
|
|
to have a partition #1 that is the last partition on the disk and a
|
|
partition #3 that is the first. This routine runs through the NT
|
|
namespace for harddisks to resolve this inconsistency.
|
|
|
|
This routine has the problem that it will not locate partitions that
|
|
are part of an FT set because the partition information for these
|
|
partitions will be modified to reflect the size of the set, not the
|
|
size of the partition.
|
|
|
|
Arguments:
|
|
|
|
Disk - the disk number
|
|
DriveLayout - the partitioning information
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
#define BUFFERSIZE 1024
|
|
NTSTATUS status;
|
|
IO_STATUS_BLOCK statusBlock;
|
|
HANDLE directoryHandle,
|
|
partitionHandle;
|
|
CLONG continueProcessing;
|
|
ULONG context = 0,
|
|
returnedLength,
|
|
index;
|
|
POBJECT_DIRECTORY_INFORMATION dirInfo;
|
|
PARTITION_INFORMATION partitionInfo;
|
|
PPARTITION_INFORMATION partitionInfoPtr;
|
|
OBJECT_ATTRIBUTES attributes;
|
|
UNICODE_STRING unicodeString;
|
|
ANSI_STRING ansiName;
|
|
PUCHAR deviceName;
|
|
PUCHAR buffer;
|
|
|
|
deviceName = Malloc(100);
|
|
if (!deviceName) {
|
|
return;
|
|
}
|
|
|
|
buffer = Malloc(BUFFERSIZE);
|
|
if (!buffer) {
|
|
Free(deviceName);
|
|
return;
|
|
}
|
|
|
|
sprintf(deviceName, "\\Device\\Harddisk%d", Disk);
|
|
RtlInitAnsiString(&ansiName, deviceName);
|
|
status = RtlAnsiStringToUnicodeString(&unicodeString, &ansiName, TRUE);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
Free(deviceName);
|
|
Free(buffer);
|
|
return;
|
|
}
|
|
InitializeObjectAttributes(&attributes,
|
|
&unicodeString,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL);
|
|
status = NtOpenDirectoryObject(&directoryHandle,
|
|
DIRECTORY_QUERY,
|
|
&attributes);
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
Free(deviceName);
|
|
Free(buffer);
|
|
return;
|
|
}
|
|
|
|
// Query the entire directory in one sweep
|
|
|
|
continueProcessing = 1;
|
|
while (continueProcessing) {
|
|
RtlZeroMemory(buffer, BUFFERSIZE);
|
|
status = NtQueryDirectoryObject(directoryHandle,
|
|
buffer,
|
|
BUFFERSIZE,
|
|
FALSE,
|
|
FALSE,
|
|
&context,
|
|
&returnedLength);
|
|
|
|
// Check the status of the operation.
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
if (status != STATUS_NO_MORE_FILES) {
|
|
break;
|
|
}
|
|
continueProcessing = 0;
|
|
}
|
|
|
|
// For every record in the buffer check for partition name
|
|
|
|
|
|
for (dirInfo = (POBJECT_DIRECTORY_INFORMATION) buffer;
|
|
TRUE;
|
|
dirInfo = (POBJECT_DIRECTORY_INFORMATION) (((PUCHAR) dirInfo) +
|
|
sizeof(OBJECT_DIRECTORY_INFORMATION))) {
|
|
|
|
// Check if there is another record. If there isn't, then get out
|
|
// of the loop now
|
|
|
|
if (dirInfo->Name.Length == 0) {
|
|
break;
|
|
}
|
|
|
|
// compare the name to see if it is a Partition
|
|
|
|
if (!_wcsnicmp(dirInfo->Name.Buffer, L"Partition", 9)) {
|
|
UCHAR digits[3];
|
|
ULONG partitionNumber;
|
|
|
|
// Located a partition. This restricts the # of partitions
|
|
// to 99.
|
|
|
|
digits[0] = (UCHAR) dirInfo->Name.Buffer[9];
|
|
digits[1] = (UCHAR) dirInfo->Name.Buffer[10];
|
|
digits[2] = 0;
|
|
partitionNumber = atoi(digits);
|
|
|
|
if (partitionNumber <= 0) {
|
|
|
|
// less than zero is really an error...
|
|
// partition zero is always the same.
|
|
|
|
continue;
|
|
}
|
|
|
|
// Have a numbered partition -- match it to the drive layout
|
|
|
|
status = LowOpenPartition(deviceName, partitionNumber, &partitionHandle);
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
// If it cannot be opened perhaps it isn't really a partition
|
|
|
|
continue;
|
|
}
|
|
|
|
status = NtDeviceIoControlFile(partitionHandle,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
&statusBlock,
|
|
IOCTL_DISK_GET_PARTITION_INFO,
|
|
NULL,
|
|
0,
|
|
&partitionInfo,
|
|
sizeof(PARTITION_INFORMATION));
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
LowCloseDisk(partitionHandle);
|
|
continue;
|
|
}
|
|
|
|
// match partition information with drive layout.
|
|
|
|
for (index = 0; index < DriveLayout->PartitionCount; index++) {
|
|
|
|
partitionInfoPtr = &DriveLayout->PartitionEntry[index];
|
|
if ((partitionInfoPtr->StartingOffset.QuadPart == partitionInfo.StartingOffset.QuadPart) &&
|
|
(partitionInfoPtr->PartitionLength.QuadPart == partitionInfo.PartitionLength.QuadPart)) {
|
|
|
|
// This is a match.
|
|
|
|
partitionInfoPtr->PartitionNumber = partitionNumber;
|
|
break;
|
|
}
|
|
}
|
|
LowCloseDisk(partitionHandle);
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
// Now close the directory object
|
|
|
|
Free(deviceName);
|
|
Free(buffer);
|
|
(VOID) NtClose(directoryHandle);
|
|
return;
|
|
}
|
|
|
|
|
|
VOID
|
|
CheckForOldDrivers(
|
|
IN ULONG Disk
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine determines if an old release 3.1 drive is in the
|
|
system. If so, it calculates the partition number for each region
|
|
on a disk. For a used region, the partition number is the number
|
|
that the system will assign to the partition. All partitions
|
|
(except the extended partition) are numbered first starting at 1,
|
|
and then all logical volumes in the extended partition.
|
|
For a free region, the partition number is the number that the
|
|
system WOULD assign to the partition if the space were to be
|
|
converted to a partition and all other regions on the disk were
|
|
left as is.
|
|
|
|
The partition numbers are stored in the PARTITION elements.
|
|
|
|
Arguments:
|
|
|
|
Disk - index of disk whose partitions are to be renumbered.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PPARTITION p = PrimaryPartitions[Disk];
|
|
ULONG n = 1;
|
|
|
|
while (p) {
|
|
if (p->SysID != SYSID_UNUSED) {
|
|
if ((!IsExtended(p->SysID)) && (IsRecognizedPartition(p->SysID))) {
|
|
|
|
// If there is already a partition number, nothing need be
|
|
// done here.
|
|
|
|
if (p->PartitionNumber) {
|
|
return;
|
|
} else {
|
|
RestartRequired = TRUE;
|
|
}
|
|
p->PartitionNumber = n;
|
|
if (p->SysID != SYSID_UNUSED) {
|
|
n++;
|
|
}
|
|
}
|
|
}
|
|
p = p->Next;
|
|
}
|
|
p = LogicalVolumes[Disk];
|
|
while (p) {
|
|
if (p->SysID != SYSID_UNUSED) {
|
|
if (p->PartitionNumber) {
|
|
return;
|
|
} else {
|
|
RestartRequired = TRUE;
|
|
}
|
|
p->PartitionNumber = n;
|
|
n++;
|
|
}
|
|
p = p->Next;
|
|
}
|
|
}
|
|
|
|
|
|
STATUS_CODE
|
|
InitializePartitionLists(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine scans the PARTITION_INFO array returned for each disk
|
|
by the OS. A linked list of PARTITION structures is layered on top
|
|
of each array; the net result is a sorted list that covers an entire
|
|
disk, because free spaces are also factored in as 'dummy' partitions.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
OK_STATUS or error code.
|
|
|
|
--*/
|
|
|
|
{
|
|
STATUS_CODE status;
|
|
ULONG disk;
|
|
PDRIVE_LAYOUT_INFORMATION driveLayout;
|
|
|
|
for (disk = 0; disk < CountOfDisks; disk++) {
|
|
|
|
if (OffLine[disk]) {
|
|
continue;
|
|
}
|
|
|
|
if ((status = LowGetDiskLayout(DiskNames[disk], &driveLayout)) != OK_STATUS) {
|
|
return status;
|
|
}
|
|
|
|
// ReconcilePartitionNumbers(disk, driveLayout);
|
|
|
|
if ((status = InitializePrimaryPartitionList(disk, driveLayout)) == OK_STATUS) {
|
|
status = InitializeLogicalVolumeList(disk, driveLayout);
|
|
}
|
|
if (status != OK_STATUS) {
|
|
FreeMemory(driveLayout);
|
|
return status;
|
|
}
|
|
|
|
Signatures[disk] = driveLayout->Signature;
|
|
FreeMemory(driveLayout);
|
|
CheckForOldDrivers(disk);
|
|
}
|
|
return OK_STATUS;
|
|
}
|
|
|
|
|
|
LARGE_INTEGER
|
|
DiskLengthBytes(
|
|
IN ULONG Disk
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine determines the disk length in bytes. This value
|
|
is calculated from the disk geometry information.
|
|
|
|
Arguments:
|
|
|
|
Disk - index of disk whose size is desired
|
|
|
|
Return Value:
|
|
|
|
Size of Disk.
|
|
|
|
--*/
|
|
|
|
{
|
|
LARGE_INTEGER result;
|
|
|
|
result.QuadPart = DiskGeometryArray[Disk].Cylinders.QuadPart *
|
|
DiskGeometryArray[Disk].BytesPerCylinder;
|
|
return result;
|
|
}
|
|
|
|
|
|
ULONG
|
|
SIZEMB(
|
|
IN LARGE_INTEGER ByteCount
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Calculate the size in megabytes of a given byte count. The value is
|
|
properly rounded (ie, not merely truncated).
|
|
|
|
This function replaces a macro of the same name that was truncating
|
|
instead of rounding.
|
|
|
|
Arguments:
|
|
|
|
ByteCount - supplies number of bytes
|
|
|
|
Return Value:
|
|
|
|
Size in MB equivalent to ByteCount.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG Remainder;
|
|
ULONG SizeMB;
|
|
|
|
SizeMB = RtlExtendedLargeIntegerDivide(ByteCount,
|
|
ONE_MEG,
|
|
&Remainder).LowPart;
|
|
|
|
if (Remainder >= ONE_MEG/2) {
|
|
SizeMB++;
|
|
}
|
|
|
|
return SizeMB;
|
|
}
|
|
|
|
|
|
ULONG
|
|
DiskSizeMB(
|
|
IN ULONG Disk
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine determines the disk length in megabytes. The returned
|
|
value is rounded down after division by 1024*1024.
|
|
|
|
Arguments:
|
|
|
|
Disk - index of disk whose size is desired
|
|
|
|
Return Value:
|
|
|
|
Size of Disk.
|
|
|
|
--*/
|
|
|
|
{
|
|
return SIZEMB(DiskLengthBytes(Disk));
|
|
}
|
|
|
|
|
|
LARGE_INTEGER
|
|
AlignTowardsDiskStart(
|
|
IN ULONG Disk,
|
|
IN LARGE_INTEGER Offset
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine snaps a byte offset to a cylinder boundary, towards
|
|
the start of the disk.
|
|
|
|
Arguments:
|
|
|
|
Disk - index of disk whose offset is to be snapped
|
|
Offset - byte offset to be aligned (snapped to cylinder boundary)
|
|
|
|
Return Value:
|
|
|
|
Aligned offset.
|
|
|
|
--*/
|
|
|
|
{
|
|
LARGE_INTEGER mod;
|
|
LARGE_INTEGER result;
|
|
|
|
mod.QuadPart = Offset.QuadPart % DiskGeometryArray[Disk].BytesPerCylinder;
|
|
result.QuadPart = Offset.QuadPart - mod.QuadPart;
|
|
return result;
|
|
}
|
|
|
|
|
|
LARGE_INTEGER
|
|
AlignTowardsDiskEnd(
|
|
IN ULONG Disk,
|
|
IN LARGE_INTEGER Offset
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine snaps a byte offset to a cylinder boundary, towards
|
|
the end of the disk.
|
|
|
|
Arguments:
|
|
|
|
Disk - index of disk whose offset is to be snapped
|
|
Offset - byte offset to be aligned (snapped to cylinder boundary)
|
|
|
|
Return Value:
|
|
|
|
Aligned offset.
|
|
|
|
--*/
|
|
|
|
{
|
|
LARGE_INTEGER mod,
|
|
temp;
|
|
|
|
mod.QuadPart = Offset.QuadPart % DiskGeometryArray[Disk].BytesPerCylinder;
|
|
if (mod.QuadPart) {
|
|
|
|
temp.QuadPart = Offset.QuadPart + DiskGeometryArray[Disk].BytesPerCylinder;
|
|
Offset = AlignTowardsDiskStart(Disk, temp);
|
|
}
|
|
return Offset;
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
IsExtended(
|
|
IN UCHAR SysID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine determines whether a given system id is for an
|
|
extended type (ie, link) entry.
|
|
|
|
Arguments:
|
|
|
|
SysID - system id to be tested.
|
|
|
|
Return Value:
|
|
|
|
true/false based on whether SysID is for an extended type.
|
|
|
|
--*/
|
|
|
|
{
|
|
return (BOOLEAN)(SysID == SYSID_EXTENDED);
|
|
}
|
|
|
|
|
|
STATUS_CODE
|
|
IsAnyCreationAllowed(
|
|
IN ULONG Disk,
|
|
IN BOOLEAN AllowMultiplePrimaries,
|
|
OUT PBOOLEAN AnyAllowed,
|
|
OUT PBOOLEAN PrimaryAllowed,
|
|
OUT PBOOLEAN ExtendedAllowed,
|
|
OUT PBOOLEAN LogicalAllowed
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine determines whether any partition may be created on a
|
|
given disk, based on three sub-queries -- whether creation is allowed
|
|
of a primary partition, an extended partition, or a logical volume.
|
|
|
|
Arguments:
|
|
|
|
Disk - index of disk to check
|
|
AllowMultiplePrimaries - whether to allow multiple primary partitions
|
|
AnyAllowed - returns whether any creation is allowed
|
|
PrimaryAllowed - returns whether creation of a primary partition
|
|
is allowed
|
|
ExtendedAllowed - returns whether creation of an extended partition
|
|
is allowed
|
|
Logical Allowed - returns whether creation of a logical volume is allowed.
|
|
|
|
Return Value:
|
|
|
|
OK_STATUS or error code
|
|
|
|
--*/
|
|
|
|
{
|
|
STATUS_CODE status;
|
|
|
|
if ((status = IsCreationOfPrimaryAllowed(Disk,AllowMultiplePrimaries,PrimaryAllowed)) != OK_STATUS) {
|
|
return status;
|
|
}
|
|
if ((status = IsCreationOfExtendedAllowed(Disk,ExtendedAllowed)) != OK_STATUS) {
|
|
return status;
|
|
}
|
|
if ((status = IsCreationOfLogicalAllowed(Disk,LogicalAllowed)) != OK_STATUS) {
|
|
return status;
|
|
}
|
|
*AnyAllowed = (BOOLEAN)(*PrimaryAllowed || *ExtendedAllowed || *LogicalAllowed);
|
|
return OK_STATUS;
|
|
}
|
|
|
|
|
|
STATUS_CODE
|
|
IsCreationOfPrimaryAllowed(
|
|
IN ULONG Disk,
|
|
IN BOOLEAN AllowMultiplePrimaries,
|
|
OUT BOOLEAN *Allowed
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine determines whether creation of a primary partition is
|
|
allowed. This is true when there is a free entry in the MBR and
|
|
there is free primary space on the disk. If multiple primaries
|
|
are not allowed, then there must also not exist any primary partitions
|
|
in order for a primary creation to be allowed.
|
|
|
|
Arguments:
|
|
|
|
Disk - index of disk to check
|
|
AllowMultiplePrimaries - whether existnace of primary partition
|
|
disallows creation of a primary partition
|
|
Allowed - returns whether creation of a primary partition
|
|
is allowed
|
|
|
|
Return Value:
|
|
|
|
OK_STATUS or error code
|
|
|
|
--*/
|
|
|
|
{
|
|
PREGION_DESCRIPTOR Regions;
|
|
ULONG RegionCount;
|
|
ULONG UsedCount,
|
|
RecogCount,
|
|
i;
|
|
STATUS_CODE status;
|
|
BOOLEAN FreeSpace = FALSE;
|
|
|
|
status = GetPrimaryDiskRegions(Disk, &Regions, &RegionCount);
|
|
if (status != OK_STATUS) {
|
|
return status;
|
|
}
|
|
|
|
for (UsedCount = RecogCount = i = 0; i<RegionCount; i++) {
|
|
if (Regions[i].SysID == SYSID_UNUSED) {
|
|
FreeSpace = TRUE;
|
|
} else {
|
|
UsedCount++;
|
|
if (!IsExtended(Regions[i].SysID) && Regions[i].Recognized) {
|
|
RecogCount++;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((UsedCount < ENTRIES_PER_BOOTSECTOR)
|
|
&& FreeSpace
|
|
&& (!RecogCount || AllowMultiplePrimaries)) {
|
|
*Allowed = TRUE;
|
|
} else {
|
|
*Allowed = FALSE;
|
|
}
|
|
|
|
FreeRegionArray(Regions, RegionCount);
|
|
return OK_STATUS;
|
|
}
|
|
|
|
|
|
STATUS_CODE
|
|
IsCreationOfExtendedAllowed(
|
|
IN ULONG Disk,
|
|
OUT BOOLEAN *Allowed
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine determines whether creation of an extended partition is
|
|
allowed. This is true when there is a free entry in the MBR,
|
|
there is free primary space on the disk, and there is no existing
|
|
extended partition.
|
|
|
|
Arguments:
|
|
|
|
Disk - index of disk to check
|
|
|
|
Allowed - returns whether creation of an extended partition
|
|
is allowed
|
|
|
|
Return Value:
|
|
|
|
OK_STATUS or error code
|
|
|
|
--*/
|
|
|
|
{
|
|
PREGION_DESCRIPTOR Regions;
|
|
ULONG RegionCount;
|
|
ULONG UsedCount,
|
|
FreeCount,
|
|
i;
|
|
STATUS_CODE status;
|
|
|
|
status = GetPrimaryDiskRegions(Disk,&Regions,&RegionCount);
|
|
if (status != OK_STATUS) {
|
|
return status;
|
|
}
|
|
|
|
for (UsedCount = FreeCount = i = 0; i<RegionCount; i++) {
|
|
if (Regions[i].SysID == SYSID_UNUSED) {
|
|
|
|
// BUGBUG should adjust the size here and see if it's non0 first
|
|
// (ie, take into account that the extended partition can't
|
|
// start on cyl 0).
|
|
|
|
FreeCount++;
|
|
} else {
|
|
UsedCount++;
|
|
if (IsExtended(Regions[i].SysID)) {
|
|
FreeRegionArray(Regions,RegionCount);
|
|
*Allowed = FALSE;
|
|
return OK_STATUS;
|
|
}
|
|
}
|
|
}
|
|
*Allowed = (BOOLEAN)((UsedCount < ENTRIES_PER_BOOTSECTOR) && FreeCount);
|
|
FreeRegionArray(Regions,RegionCount);
|
|
return OK_STATUS;
|
|
}
|
|
|
|
|
|
STATUS_CODE
|
|
IsCreationOfLogicalAllowed(
|
|
IN ULONG Disk,
|
|
OUT BOOLEAN *Allowed
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine determines whether creation of a logical volume is
|
|
allowed. This is true when there is an extended partition and
|
|
free space within it.
|
|
|
|
Arguments:
|
|
|
|
Disk - index of disk to check
|
|
|
|
Allowed - returns whether creation of a logical volume is allowed
|
|
|
|
Return Value:
|
|
|
|
OK_STATUS or error code
|
|
|
|
--*/
|
|
|
|
{
|
|
PREGION_DESCRIPTOR Regions;
|
|
ULONG RegionCount;
|
|
ULONG i;
|
|
STATUS_CODE status;
|
|
BOOLEAN ExtendedExists;
|
|
|
|
*Allowed = FALSE;
|
|
|
|
status = DoesExtendedExist(Disk,&ExtendedExists);
|
|
if (status != OK_STATUS) {
|
|
return status;
|
|
}
|
|
if (!ExtendedExists) {
|
|
return OK_STATUS;
|
|
}
|
|
|
|
status = GetLogicalDiskRegions(Disk,&Regions,&RegionCount);
|
|
if (status != OK_STATUS) {
|
|
return status;
|
|
}
|
|
|
|
for (i = 0; i<RegionCount; i++) {
|
|
if (Regions[i].SysID == SYSID_UNUSED) {
|
|
*Allowed = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
FreeRegionArray(Regions,RegionCount);
|
|
return OK_STATUS;
|
|
}
|
|
|
|
|
|
STATUS_CODE
|
|
DoesAnyPartitionExist(
|
|
IN ULONG Disk,
|
|
OUT PBOOLEAN AnyExists,
|
|
OUT PBOOLEAN PrimaryExists,
|
|
OUT PBOOLEAN ExtendedExists,
|
|
OUT PBOOLEAN LogicalExists
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine determines whether any partition exists on a given disk.
|
|
This is based on three sub queries: whether there are any primary or
|
|
extended partitions, or logical volumes on the disk.
|
|
|
|
Arguments:
|
|
|
|
Disk - index of disk to check
|
|
AnyExists - returns whether any partitions exist on Disk
|
|
PrimaryExists - returns whether any primary partitions exist on Disk
|
|
ExtendedExists - returns whether there is an extended partition on Disk
|
|
LogicalExists - returns whether any logical volumes exist on Disk
|
|
|
|
Return Value:
|
|
|
|
OK_STATUS or error code
|
|
|
|
--*/
|
|
|
|
{
|
|
STATUS_CODE status;
|
|
|
|
if ((status = DoesAnyPrimaryExist(Disk,PrimaryExists )) != OK_STATUS) {
|
|
return status;
|
|
}
|
|
if ((status = DoesExtendedExist (Disk,ExtendedExists)) != OK_STATUS) {
|
|
return status;
|
|
}
|
|
if ((status = DoesAnyLogicalExist(Disk,LogicalExists )) != OK_STATUS) {
|
|
return status;
|
|
}
|
|
*AnyExists = (BOOLEAN)(*PrimaryExists || *ExtendedExists || *LogicalExists);
|
|
return OK_STATUS;
|
|
}
|
|
|
|
|
|
STATUS_CODE
|
|
DoesAnyPrimaryExist(
|
|
IN ULONG Disk,
|
|
OUT BOOLEAN *Exists
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine determines whether any non-extended primary partition exists
|
|
on a given disk.
|
|
|
|
Arguments:
|
|
|
|
Disk - index of disk to check
|
|
Exists - returns whether any primary partitions exist on Disk
|
|
|
|
Return Value:
|
|
|
|
OK_STATUS or error code
|
|
|
|
--*/
|
|
|
|
{
|
|
PREGION_DESCRIPTOR Regions;
|
|
ULONG RegionCount,
|
|
i;
|
|
STATUS_CODE status;
|
|
|
|
status = GetUsedPrimaryDiskRegions(Disk,&Regions,&RegionCount);
|
|
if (status != OK_STATUS) {
|
|
return status;
|
|
}
|
|
|
|
*Exists = FALSE;
|
|
|
|
for (i=0; i<RegionCount; i++) {
|
|
if (!IsExtended(Regions[i].SysID)) {
|
|
*Exists = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
FreeRegionArray(Regions,RegionCount);
|
|
return OK_STATUS;
|
|
}
|
|
|
|
|
|
STATUS_CODE
|
|
DoesExtendedExist(
|
|
IN ULONG Disk,
|
|
OUT BOOLEAN *Exists
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine determines whether an extended partition exists
|
|
on a given disk.
|
|
|
|
Arguments:
|
|
|
|
Disk - index of disk to check
|
|
Exists - returns whether an extended partition exists on Disk
|
|
|
|
Return Value:
|
|
|
|
OK_STATUS or error code
|
|
|
|
--*/
|
|
|
|
{
|
|
PREGION_DESCRIPTOR Regions;
|
|
ULONG RegionCount,
|
|
i;
|
|
STATUS_CODE status;
|
|
|
|
status = GetUsedPrimaryDiskRegions(Disk,&Regions,&RegionCount);
|
|
if (status != OK_STATUS) {
|
|
return status;
|
|
}
|
|
|
|
*Exists = FALSE;
|
|
|
|
for (i=0; i<RegionCount; i++) {
|
|
if (IsExtended(Regions[i].SysID)) {
|
|
*Exists = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
FreeRegionArray(Regions,RegionCount);
|
|
return OK_STATUS;
|
|
}
|
|
|
|
|
|
STATUS_CODE
|
|
DoesAnyLogicalExist(
|
|
IN ULONG Disk,
|
|
OUT BOOLEAN *Exists
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine determines whether any logical volumes exist
|
|
on a given disk.
|
|
|
|
Arguments:
|
|
|
|
Disk - index of disk to check
|
|
Exists - returns whether any logical volumes exist on Disk
|
|
|
|
Return Value:
|
|
|
|
OK_STATUS or error code
|
|
|
|
--*/
|
|
|
|
{
|
|
PREGION_DESCRIPTOR Regions;
|
|
ULONG RegionCount;
|
|
STATUS_CODE status;
|
|
|
|
status = GetUsedLogicalDiskRegions(Disk,&Regions,&RegionCount);
|
|
if (status != OK_STATUS) {
|
|
return status;
|
|
}
|
|
|
|
*Exists = (BOOLEAN)(RegionCount != 0);
|
|
FreeRegionArray(Regions,RegionCount);
|
|
return OK_STATUS;
|
|
}
|
|
|
|
|
|
ULONG
|
|
GetDiskCount(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine returns the number of attached partitionable disk
|
|
devices. The returned value is one greater than the maximum index
|
|
allowed for Disk parameters to partitioning engine routines.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
Count of disks.
|
|
|
|
--*/
|
|
|
|
{
|
|
return CountOfDisks;
|
|
}
|
|
|
|
|
|
PCHAR
|
|
GetDiskName(
|
|
ULONG Disk
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine returns the system name for the disk device whose
|
|
index is given.
|
|
|
|
Arguments:
|
|
|
|
Disk - index of disk whose name is desired.
|
|
|
|
Return Value:
|
|
|
|
System name for the disk device. The caller must not attempt to
|
|
free this buffer or modify it.
|
|
|
|
--*/
|
|
|
|
{
|
|
return DiskNames[Disk];
|
|
}
|
|
|
|
|
|
// worker routines for WriteDriveLayout
|
|
|
|
VOID
|
|
UnusedEntryFill(
|
|
IN PPARTITION_INFORMATION pinfo,
|
|
IN ULONG EntryCount
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initialize a partition information structure.
|
|
|
|
Arguments:
|
|
|
|
pinfo - the partition information structure to fill in.
|
|
EntryCount - the number of entries in the structure to fill.
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG i;
|
|
LARGE_INTEGER zero;
|
|
|
|
zero.QuadPart = 0;
|
|
for (i = 0; i < EntryCount; i++) {
|
|
|
|
pinfo[i].StartingOffset = zero;
|
|
pinfo[i].PartitionLength = zero;
|
|
pinfo[i].HiddenSectors = 0;
|
|
pinfo[i].PartitionType = SYSID_UNUSED;
|
|
pinfo[i].BootIndicator = FALSE;
|
|
pinfo[i].RewritePartition = TRUE;
|
|
}
|
|
}
|
|
|
|
|
|
LARGE_INTEGER
|
|
MakeBootRec(
|
|
ULONG Disk,
|
|
PPARTITION_INFORMATION pInfo,
|
|
PPARTITION pLogical,
|
|
PPARTITION pNextLogical
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Disk - the disk number
|
|
pinfo - the partition information for the disk.
|
|
pLogical
|
|
pNextLogical
|
|
|
|
Return Value:
|
|
|
|
The starting offset.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG entry = 0;
|
|
LARGE_INTEGER bytesPerTrack;
|
|
LARGE_INTEGER sectorsPerTrack;
|
|
LARGE_INTEGER startingOffset;
|
|
|
|
bytesPerTrack.QuadPart = DiskGeometryArray[Disk].BytesPerTrack;
|
|
sectorsPerTrack.QuadPart = DiskGeometryArray[Disk].SectorsPerTrack;
|
|
startingOffset.QuadPart = 0;
|
|
|
|
if (pLogical) {
|
|
|
|
pInfo[entry].StartingOffset.QuadPart = pLogical->Offset.QuadPart + bytesPerTrack.QuadPart;
|
|
pInfo[entry].PartitionLength.QuadPart = pLogical->Length.QuadPart - bytesPerTrack.QuadPart;
|
|
pInfo[entry].HiddenSectors = sectorsPerTrack.LowPart;
|
|
pInfo[entry].RewritePartition = pLogical->Update;
|
|
pInfo[entry].BootIndicator = pLogical->Active;
|
|
pInfo[entry].PartitionType = pLogical->SysID;
|
|
|
|
if(pInfo[entry].RewritePartition) {
|
|
startingOffset = pInfo[entry].StartingOffset;
|
|
}
|
|
|
|
entry++;
|
|
}
|
|
|
|
if (pNextLogical) {
|
|
|
|
pInfo[entry].StartingOffset = pNextLogical->Offset;
|
|
pInfo[entry].PartitionLength = pNextLogical->Length;
|
|
pInfo[entry].HiddenSectors = 0;
|
|
pInfo[entry].RewritePartition = TRUE;
|
|
pInfo[entry].BootIndicator = FALSE;
|
|
pInfo[entry].PartitionType = SYSID_EXTENDED;
|
|
|
|
entry++;
|
|
}
|
|
|
|
UnusedEntryFill(pInfo + entry, ENTRIES_PER_BOOTSECTOR - entry);
|
|
return startingOffset;
|
|
}
|
|
|
|
|
|
STATUS_CODE
|
|
ZapSector(
|
|
ULONG Disk,
|
|
LARGE_INTEGER Offset
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine writes zeros into a sector at a given offset. This is
|
|
used to clear out a new partition's filesystem boot record, so that
|
|
no previous filesystem appears in a new partition; or to clear out the
|
|
first EBR in the extended partition if there are to be no logical vols.
|
|
|
|
Arguments:
|
|
|
|
Disk - disk to write to
|
|
|
|
Offset - byte offset to a newly created partition on Disk
|
|
|
|
Return Value:
|
|
|
|
OK_STATUS or error code.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG sectorSize = DiskGeometryArray[Disk].BytesPerSector;
|
|
ULONG i;
|
|
PCHAR sectorBuffer,
|
|
alignedSectorBuffer;
|
|
STATUS_CODE status;
|
|
HANDLE_T handle;
|
|
LARGE_INTEGER temp;
|
|
|
|
if ((sectorBuffer = AllocateMemory(2*sectorSize)) == NULL) {
|
|
RETURN_OUT_OF_MEMORY;
|
|
}
|
|
|
|
alignedSectorBuffer = (PCHAR)(((ULONG)sectorBuffer+sectorSize) & ~(sectorSize-1));
|
|
|
|
for (i=0; i<sectorSize; alignedSectorBuffer[i++] = 0);
|
|
|
|
if ((status = LowOpenDisk(GetDiskName(Disk),&handle)) != OK_STATUS) {
|
|
FreeMemory(sectorBuffer);
|
|
return status;
|
|
}
|
|
|
|
temp.QuadPart = Offset.QuadPart / sectorSize;
|
|
status = LowWriteSectors(handle,
|
|
sectorSize,
|
|
temp.LowPart,
|
|
1,
|
|
alignedSectorBuffer);
|
|
LowCloseDisk(handle);
|
|
|
|
// Now to make sure the file system really did a dismount,
|
|
// force a mount/verify of the partition. This avoids a
|
|
// problem where HPFS doesn't dismount when asked, but instead
|
|
// marks the volume for verify.
|
|
|
|
if ((status = LowOpenDisk(GetDiskName(Disk), &handle)) == OK_STATUS) {
|
|
LowCloseDisk(handle);
|
|
}
|
|
|
|
FreeMemory(sectorBuffer);
|
|
return status;
|
|
}
|
|
|
|
|
|
STATUS_CODE
|
|
WriteDriveLayout(
|
|
IN ULONG Disk
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine writes the current partition layout for a given disk
|
|
out to disk. The high-level PARTITION lists are transformed into
|
|
a DRIVE_LAYOUT_INFORMATION structure before being passed down
|
|
to the low-level partition table writing routine.
|
|
|
|
Arguments:
|
|
|
|
Disk - index of disk whose on-disk partition structure is to be updated.
|
|
|
|
Return Value:
|
|
|
|
OK_STATUS or error code.
|
|
|
|
--*/
|
|
|
|
{
|
|
#define MAX_DISKS 250
|
|
PDRIVE_LAYOUT_INFORMATION driveLayout;
|
|
PPARTITION_INFORMATION pinfo;
|
|
ULONG entryCount;
|
|
ULONG sectorSize;
|
|
STATUS_CODE status;
|
|
LARGE_INTEGER startingOffset,
|
|
extendedStartingOffset;
|
|
PPARTITION nextPartition,
|
|
partition,
|
|
partitionHash[MAX_DISKS];
|
|
|
|
extendedStartingOffset.QuadPart = 0;
|
|
memset(partitionHash, 0, sizeof(partitionHash));
|
|
|
|
// allocate a huge buffer now to avoid complicated dynamic
|
|
// reallocation schemes later.
|
|
|
|
if (!(driveLayout = AllocateMemory((MAX_DISKS + 1) * sizeof(PARTITION_INFORMATION)))) {
|
|
RETURN_OUT_OF_MEMORY;
|
|
}
|
|
|
|
pinfo = &driveLayout->PartitionEntry[0];
|
|
|
|
// first do the mbr.
|
|
|
|
entryCount = 0;
|
|
partition = PrimaryPartitions[Disk];
|
|
sectorSize = DiskGeometryArray[Disk].BytesPerSector;
|
|
|
|
while (partition) {
|
|
|
|
if (partition->SysID != SYSID_UNUSED) {
|
|
|
|
if (IsExtended(partition->SysID)) {
|
|
extendedStartingOffset = partition->Offset;
|
|
} else {
|
|
partitionHash[entryCount] = partition;
|
|
}
|
|
|
|
pinfo[entryCount].StartingOffset = partition->Offset;
|
|
pinfo[entryCount].PartitionLength = partition->Length;
|
|
pinfo[entryCount].PartitionType = partition->SysID;
|
|
pinfo[entryCount].BootIndicator = partition->Active;
|
|
pinfo[entryCount].RewritePartition = partition->Update;
|
|
pinfo[entryCount].HiddenSectors = (ULONG) (partition->Offset.QuadPart / sectorSize);
|
|
|
|
// if we're creating this partition, clear out the
|
|
// filesystem boot sector.
|
|
|
|
if (pinfo[entryCount].RewritePartition
|
|
&& (partition->Update != CHANGED_DONT_ZAP)
|
|
&& !IsExtended(pinfo[entryCount].PartitionType)) {
|
|
status = ZapSector(Disk,pinfo[entryCount].StartingOffset);
|
|
if (status != OK_STATUS) {
|
|
FreeMemory(driveLayout);
|
|
return status;
|
|
}
|
|
}
|
|
|
|
entryCount++;
|
|
}
|
|
partition = partition->Next;
|
|
}
|
|
|
|
// fill the remainder of the MBR with unused entries.
|
|
// NOTE that there will thus always be an MBR even if there
|
|
// are no partitions defined.
|
|
|
|
UnusedEntryFill(pinfo+entryCount, ENTRIES_PER_BOOTSECTOR - entryCount);
|
|
entryCount = ENTRIES_PER_BOOTSECTOR;
|
|
|
|
// now handle the logical volumes.
|
|
// first check to see whether we need a dummy EBR at the beginning
|
|
// of the extended partition. This is the case when there is
|
|
// free space at the beginning of the extended partition.
|
|
#if 0
|
|
// Also handle the case where we are creating an empty extended
|
|
// partition -- need to zap the first sector to eliminate any residue
|
|
// that might start an EBR chain.
|
|
#else
|
|
// BUGBUG 4/24/92 tedm: Currently the io subsystem returns an error
|
|
// status (status_bad_master_boot_record) if any mbr or ebr is bad.
|
|
// Zeroing the first sector of the extended partition therefore causes
|
|
// the whole disk to be seen as empty. So create a blank, but valid,
|
|
// EBR in the 'empty extended partition' case. Code is in the 'else'
|
|
// part of the #if 0, below.
|
|
#endif
|
|
|
|
if ((partition = LogicalVolumes[Disk]) && (partition->SysID == SYSID_UNUSED)) {
|
|
if (partition->Next) {
|
|
|
|
partitionHash[entryCount] = partition;
|
|
MakeBootRec(Disk, pinfo+entryCount, NULL, partition->Next);
|
|
entryCount += ENTRIES_PER_BOOTSECTOR;
|
|
partition = partition->Next;
|
|
} else {
|
|
|
|
#if 0
|
|
status = ZapSector(Disk, extendedStartingOffset);
|
|
if (status != OK_STATUS) {
|
|
FreeMemory(driveLayout);
|
|
return status;
|
|
}
|
|
#else
|
|
MakeBootRec(Disk, pinfo+entryCount, NULL, NULL);
|
|
entryCount += ENTRIES_PER_BOOTSECTOR;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
while (partition) {
|
|
if (partition->SysID != SYSID_UNUSED) {
|
|
|
|
// find the next logical volume.
|
|
|
|
nextPartition = partition->Next;
|
|
while (nextPartition) {
|
|
if (nextPartition->SysID != SYSID_UNUSED) {
|
|
break;
|
|
}
|
|
nextPartition = nextPartition->Next;
|
|
}
|
|
|
|
partitionHash[entryCount] = partition;
|
|
startingOffset = MakeBootRec(Disk, pinfo+entryCount, partition, nextPartition);
|
|
|
|
// if we're creating a volume, clear out its filesystem
|
|
// boot sector so it starts out fresh.
|
|
|
|
if ((startingOffset.QuadPart) && (partition->Update != CHANGED_DONT_ZAP)) {
|
|
status = ZapSector(Disk,startingOffset);
|
|
if (status != OK_STATUS) {
|
|
FreeMemory(driveLayout);
|
|
return status;
|
|
}
|
|
}
|
|
|
|
entryCount += ENTRIES_PER_BOOTSECTOR;
|
|
}
|
|
partition = partition->Next;
|
|
}
|
|
|
|
driveLayout->PartitionCount = entryCount;
|
|
driveLayout->Signature = Signatures[Disk];
|
|
status = LowSetDiskLayout(DiskNames[Disk], driveLayout);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
// Update the partition numbers in the region structures.
|
|
|
|
// ReconcilePartitionNumbers(Disk, driveLayout);
|
|
|
|
for (entryCount = 0; entryCount < MAX_DISKS; entryCount++) {
|
|
if (partition = partitionHash[entryCount]) {
|
|
if (partition->Update) {
|
|
pinfo = &driveLayout->PartitionEntry[entryCount];
|
|
partition->PartitionNumber = pinfo->PartitionNumber;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
FreeMemory(driveLayout);
|
|
return status;
|
|
}
|
|
|
|
|
|
STATUS_CODE
|
|
CommitPartitionChanges(
|
|
IN ULONG Disk
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is the entry point for updating the on-disk partition
|
|
structures of a disk. The disk is only written to if the partition
|
|
structure has been changed by adding or deleting partitions.
|
|
|
|
Arguments:
|
|
|
|
Disk - index of disk whose on-disk partition structure is to be updated.
|
|
|
|
Return Value:
|
|
|
|
OK_STATUS or error code.
|
|
|
|
--*/
|
|
|
|
{
|
|
PPARTITION p;
|
|
STATUS_CODE status;
|
|
|
|
if (!HavePartitionsBeenChanged(Disk)) {
|
|
return OK_STATUS;
|
|
}
|
|
|
|
if ((status = WriteDriveLayout(Disk)) != OK_STATUS) {
|
|
return status;
|
|
}
|
|
|
|
// BUGBUG for ARC and NT MIPS, update NVRAM vars so partitions are right.
|
|
// Do that here, before partition numbers are reassigned.
|
|
|
|
p = PrimaryPartitions[Disk];
|
|
while (p) {
|
|
p->Update = FALSE;
|
|
p->OriginalPartitionNumber = p->PartitionNumber;
|
|
p = p->Next;
|
|
}
|
|
p = LogicalVolumes[Disk];
|
|
while (p) {
|
|
p->Update = FALSE;
|
|
p->OriginalPartitionNumber = p->PartitionNumber;
|
|
p = p->Next;
|
|
}
|
|
|
|
ChangesRequested[Disk] = FALSE;
|
|
ChangesCommitted[Disk] = TRUE;
|
|
return OK_STATUS;
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
IsRegionCommitted(
|
|
PREGION_DESCRIPTOR RegionDescriptor
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Given a region descriptor, return TRUE if it actually exists on disk,
|
|
FALSE otherwise.
|
|
|
|
Arguments:
|
|
|
|
RegionDescriptor - the region to check
|
|
|
|
Return Value:
|
|
|
|
TRUE - if the region actually exists on disk
|
|
FALSE otherwise.
|
|
|
|
--*/
|
|
|
|
{
|
|
PPERSISTENT_REGION_DATA regionData;
|
|
|
|
regionData = PERSISTENT_DATA(RegionDescriptor);
|
|
if (!regionData) {
|
|
return FALSE;
|
|
}
|
|
return regionData->VolumeExists;
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
HavePartitionsBeenChanged(
|
|
IN ULONG Disk
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine returns TRUE if the given disk's partition structures
|
|
have been modified by adding or deleting partitions, since the
|
|
on-disk structures were last written by a call to CommitPartitionChanges
|
|
(or first read).
|
|
|
|
Arguments:
|
|
|
|
Disk - index of disk to check
|
|
|
|
Return Value:
|
|
|
|
true if Disk's partition structure has changed.
|
|
|
|
--*/
|
|
|
|
{
|
|
return ChangesRequested[Disk];
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
ChangeCommittedOnDisk(
|
|
IN ULONG Disk
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will inform the caller if a change was actually committed
|
|
to the disk given.
|
|
|
|
Arguments:
|
|
|
|
Disk - index of disk to check
|
|
|
|
Return Value:
|
|
|
|
TRUE if disk was changed
|
|
FALSE otherwise.
|
|
|
|
--*/
|
|
|
|
{
|
|
return ChangesCommitted[Disk];
|
|
}
|
|
|
|
|
|
VOID
|
|
ClearCommittedDiskInformation(
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Clear all knowledge about any changes that have occurred to the
|
|
disks.
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG i;
|
|
|
|
for (i=0; i<CountOfDisks; i++) {
|
|
ChangesCommitted[i] = FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
FdMarkDiskDirty(
|
|
IN ULONG Disk
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Remember that this disk has had some partitioning changes.
|
|
|
|
Arguments:
|
|
|
|
Disk - the disk number
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
ChangesRequested[Disk] = TRUE;
|
|
}
|
|
|
|
|
|
VOID
|
|
FdSetPersistentData(
|
|
IN PREGION_DESCRIPTOR Region,
|
|
IN ULONG Data
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Set the persistent data area for the specified region.
|
|
|
|
Arguments:
|
|
|
|
Region - the region for which the persistent data is to be set
|
|
Data - the persistent data for the region.
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
((PREGION_DATA)(Region->Reserved))->Partition->PersistentData =
|
|
(PPERSISTENT_REGION_DATA) Data;
|
|
}
|
|
|
|
|
|
ULONG
|
|
FdGetMinimumSizeMB(
|
|
IN ULONG Disk
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Return the minimum size for a partition on a given disk.
|
|
|
|
This is the rounded size of one cylinder or 1, whichever is greater.
|
|
|
|
Arguments:
|
|
|
|
Region - region describing the partition to check.
|
|
|
|
Return Value:
|
|
|
|
Actual offset
|
|
|
|
--*/
|
|
|
|
{
|
|
LARGE_INTEGER temp;
|
|
|
|
temp.QuadPart = DiskGeometryArray[Disk].BytesPerCylinder;
|
|
return max(SIZEMB(temp), 1);
|
|
}
|
|
|
|
|
|
ULONG
|
|
FdGetMaximumSizeMB(
|
|
IN PREGION_DESCRIPTOR Region,
|
|
IN REGION_TYPE CreationType
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Given a region of disk determine how much of it may be used to
|
|
create the specified partition type. This code take into consideration
|
|
the many alignment restrictions imposed by early DOS software versions.
|
|
|
|
Arguments:
|
|
|
|
Region - The affected region
|
|
CreationType - What is being created
|
|
(extended partition/primary partition)
|
|
|
|
Return Value:
|
|
|
|
The maximum size that a partition of the specified type can be
|
|
to fit within the space available in the region.
|
|
|
|
--*/
|
|
|
|
{
|
|
PREGION_DATA createData = Region->Reserved;
|
|
LARGE_INTEGER maxSize;
|
|
|
|
maxSize = createData->AlignedRegionSize;
|
|
if (!(createData->AlignedRegionOffset.QuadPart)) {
|
|
ULONG delta;
|
|
|
|
delta = (CreationType == REGION_EXTENDED)
|
|
? DiskGeometryArray[Region->Disk].BytesPerCylinder
|
|
: DiskGeometryArray[Region->Disk].BytesPerTrack;
|
|
|
|
maxSize.QuadPart -= delta;
|
|
}
|
|
|
|
return SIZEMB(maxSize);
|
|
}
|
|
|
|
|
|
LARGE_INTEGER
|
|
FdGetExactSize(
|
|
IN PREGION_DESCRIPTOR Region,
|
|
IN BOOLEAN ForExtended
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
|
|
{
|
|
PREGION_DATA regionData = Region->Reserved;
|
|
LARGE_INTEGER largeSize = regionData->AlignedRegionSize;
|
|
LARGE_INTEGER bytesPerTrack;
|
|
LARGE_INTEGER bytesPerCylinder;
|
|
|
|
bytesPerTrack.QuadPart = DiskGeometryArray[Region->Disk].BytesPerTrack;
|
|
bytesPerCylinder.QuadPart = DiskGeometryArray[Region->Disk].BytesPerCylinder;
|
|
|
|
if (Region->RegionType == REGION_LOGICAL) {
|
|
|
|
//
|
|
// The region is within the extended partition. It doesn't matter
|
|
// whether it's free space or used -- in either case, we need to
|
|
// account for the reserved EBR track.
|
|
//
|
|
|
|
largeSize.QuadPart -= bytesPerTrack.QuadPart;
|
|
|
|
} else if (Region->SysID == SYSID_UNUSED) {
|
|
|
|
//
|
|
// The region is unused space not inside the extended partition.
|
|
// We must know whether the caller will put a primary or extended
|
|
// partition there -- a primary partition can use all the space, but
|
|
// a logical volume in the extended partition won't include the first
|
|
// track. If the free space starts at offset 0 on the disk, a special
|
|
// calculation must be used to move the start of the partition to
|
|
// skip a track for a primary or a cylinder and a track for an
|
|
// extended+logical.
|
|
//
|
|
|
|
if ((!regionData->AlignedRegionOffset.QuadPart) || ForExtended) {
|
|
largeSize.QuadPart -= bytesPerTrack.QuadPart;
|
|
}
|
|
|
|
if ((!regionData->AlignedRegionOffset.QuadPart) && ForExtended) {
|
|
largeSize.QuadPart -= bytesPerCylinder.QuadPart;
|
|
}
|
|
}
|
|
|
|
return largeSize;
|
|
}
|
|
|
|
|
|
LARGE_INTEGER
|
|
FdGetExactOffset(
|
|
IN PREGION_DESCRIPTOR Region
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Determine where a given partition _actually_ starts, which may be
|
|
different than where is appears because of EBR reserved tracks, etc.
|
|
|
|
NOTE: This routine is not meant to operate on unused regions or
|
|
extended partitions. In these cases, it just returns the apparant offset.
|
|
|
|
Arguments:
|
|
|
|
Region - region describing the partition to check.
|
|
|
|
Return Value:
|
|
|
|
Actual offset
|
|
|
|
--*/
|
|
|
|
{
|
|
LARGE_INTEGER offset = ((PREGION_DATA)(Region->Reserved))->Partition->Offset;
|
|
|
|
if ((Region->SysID != SYSID_UNUSED) && (Region->RegionType == REGION_LOGICAL)) {
|
|
|
|
//
|
|
// The region is a logical volume.
|
|
// Account for the reserved EBR track.
|
|
//
|
|
|
|
offset.QuadPart += DiskGeometryArray[Region->Disk].BytesPerTrack;
|
|
}
|
|
|
|
return offset;
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
FdCrosses1024Cylinder(
|
|
IN PREGION_DESCRIPTOR Region,
|
|
IN ULONG CreationSizeMB,
|
|
IN REGION_TYPE RegionType
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Determine whether a used region corsses the 1024th cylinder, or whether
|
|
a partition created within a free space will cross the 1024th cylinder.
|
|
|
|
Arguments:
|
|
|
|
Region - region describing the partition to check.
|
|
CreationSizeMB - if the Region is for a free space, this is the size of
|
|
the partition to be checked.
|
|
RegionType - one of REGION_PRIMARY, REGION_EXTENDED, or REGION_LOGICAL
|
|
|
|
Return Value:
|
|
|
|
TRUE if the end cylinder >= 1024.
|
|
|
|
--*/
|
|
|
|
{
|
|
LARGE_INTEGER start,
|
|
size,
|
|
end,
|
|
zero;
|
|
|
|
if (Region->SysID == SYSID_UNUSED) {
|
|
|
|
// Determine the exact size and offset of the partition, according
|
|
// to how CreatePartitionEx() will do it.
|
|
|
|
zero.QuadPart = 0;
|
|
DetermineCreateSizeAndOffset(Region,
|
|
zero,
|
|
CreationSizeMB,
|
|
RegionType,
|
|
&start,
|
|
&size);
|
|
} else {
|
|
|
|
start = ((PREGION_DATA)(Region->Reserved))->Partition->Offset;
|
|
size = ((PREGION_DATA)(Region->Reserved))->Partition->Length;
|
|
}
|
|
|
|
end.QuadPart = (start.QuadPart + size.QuadPart) - 1;
|
|
|
|
// End is the last byte in the partition. Divide by the number of
|
|
// bytes in a cylinder and see whether the result is > 1023.
|
|
|
|
end.QuadPart = end.QuadPart / DiskGeometryArray[Region->Disk].BytesPerCylinder;
|
|
return (end.QuadPart > 1023);
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
IsDiskOffLine(
|
|
IN ULONG Disk
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
|
|
{
|
|
return OffLine[Disk];
|
|
}
|
|
|
|
ULONG
|
|
FdGetDiskSignature(
|
|
IN ULONG Disk
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
|
|
{
|
|
return Signatures[Disk];
|
|
}
|
|
|
|
VOID
|
|
FdSetDiskSignature(
|
|
IN ULONG Disk,
|
|
IN ULONG Signature
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
|
|
{
|
|
Signatures[Disk] = Signature;
|
|
}
|
|
|
|
BOOLEAN
|
|
SignatureIsUniqueToSystem(
|
|
IN ULONG Disk,
|
|
IN ULONG Signature
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG index;
|
|
|
|
for (index = 0; index < Disk; index++) {
|
|
if (Signatures[index] == Signature) {
|
|
return FALSE;
|
|
}
|
|
}
|
|
return TRUE;
|
|
}
|