3230 lines
79 KiB
C
3230 lines
79 KiB
C
/*++
|
|
|
|
Copyright (c) 1991 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:
|
|
|
|
--*/
|
|
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
|
|
#define ARCDBG
|
|
|
|
// Attached disk devices.
|
|
|
|
ULONG CountOfDisks;
|
|
PCHAR *DiskNames;
|
|
|
|
// Information about attached disks.
|
|
|
|
DISKGEOM *DiskGeometryArray;
|
|
|
|
PPARTITION *PrimaryPartitions,
|
|
*LogicalVolumes;
|
|
|
|
|
|
// Array keeping track of whether each disk is off line.
|
|
|
|
PBOOLEAN OffLine;
|
|
|
|
// Keeps track of whether changes have been made
|
|
// to each disk's partition structure.
|
|
|
|
BOOLEAN *ChangesMade;
|
|
|
|
|
|
|
|
// 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
|
|
|
|
|
|
ARC_STATUS OpenDisks(VOID);
|
|
VOID CloseDisks(VOID);
|
|
ARC_STATUS GetGeometry(VOID);
|
|
BOOLEAN CheckIfDiskIsOffLine(IN ULONG Disk);
|
|
ARC_STATUS InitializePartitionLists(VOID);
|
|
|
|
ARC_STATUS
|
|
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 RenumberPartitions(ULONG Disk);
|
|
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
|
|
);
|
|
|
|
|
|
|
|
ARC_STATUS FdiskInitialize(VOID)
|
|
/*++
|
|
Routine Description:
|
|
This routine initializes the partitioning engine, including allocating arrays, determining attached disk devices, and reading their partition tables.
|
|
Return Value:
|
|
OK_STATUS or error code.
|
|
--*/
|
|
{
|
|
ARC_STATUS status;
|
|
ULONG i;
|
|
|
|
if((status = LowQueryFdiskPathList(&DiskNames,&CountOfDisks)) != OK_STATUS) {
|
|
return(status);
|
|
}
|
|
|
|
#if 0
|
|
#ifdef ARCDBG
|
|
AlPrint("Disk count = %u\r\n",CountOfDisks);
|
|
for(i=0; i<CountOfDisks; i++) {
|
|
AlPrint("Disk %u: %s\r\n",i,DiskNames[i]);
|
|
}
|
|
WaitKey();
|
|
#endif
|
|
#endif
|
|
|
|
DiskGeometryArray = NULL;
|
|
PrimaryPartitions = NULL;
|
|
LogicalVolumes = NULL;
|
|
|
|
if(((DiskGeometryArray = AllocateMemory(CountOfDisks * sizeof(DISKGEOM ))) == NULL)
|
|
|| ((ChangesMade = AllocateMemory(CountOfDisks * sizeof(BOOLEAN ))) == NULL)
|
|
|| ((PrimaryPartitions = AllocateMemory(CountOfDisks * sizeof(PPARTITION))) == 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;
|
|
ChangesMade[i] = FALSE;
|
|
OffLine[i] = CheckIfDiskIsOffLine(i);
|
|
if(OffLine[i]) {
|
|
return(ENODEV);
|
|
}
|
|
}
|
|
|
|
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.
|
|
--*/
|
|
{
|
|
LowFreeFdiskPathList(DiskNames,CountOfDisks);
|
|
|
|
if(DiskGeometryArray != NULL) {
|
|
FreeMemory(DiskGeometryArray);
|
|
}
|
|
if(PrimaryPartitions != NULL) {
|
|
FreePartitionInfoLinkedLists(PrimaryPartitions);
|
|
FreeMemory(PrimaryPartitions);
|
|
}
|
|
if(LogicalVolumes != NULL) {
|
|
FreePartitionInfoLinkedLists(LogicalVolumes);
|
|
FreeMemory(LogicalVolumes);
|
|
}
|
|
if(ChangesMade != NULL) {
|
|
FreeMemory(ChangesMade);
|
|
}
|
|
if(OffLine != NULL) {
|
|
FreeMemory(OffLine);
|
|
}
|
|
}
|
|
|
|
|
|
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.
|
|
--*/
|
|
{
|
|
ULONG Handle;
|
|
BOOLEAN IsOffLine;
|
|
|
|
if(LowOpenDisk(GetDiskName(Disk),&Handle) == OK_STATUS) {
|
|
IsOffLine = FALSE;
|
|
LowCloseDisk(Handle);
|
|
} else {
|
|
IsOffLine = TRUE;
|
|
}
|
|
|
|
return(IsOffLine);
|
|
}
|
|
|
|
|
|
ARC_STATUS 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.
|
|
|
|
Return Value:
|
|
OK_STATUS or error code.
|
|
--*/
|
|
{
|
|
ULONG i;
|
|
ARC_STATUS 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)
|
|
{
|
|
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;
|
|
}
|
|
ChangesMade[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.
|
|
--*/
|
|
{
|
|
PREGION_DATA CreateData = Region->Reserved;
|
|
LARGE_INTEGER CSize,CStart;
|
|
LARGE_INTEGER Mod;
|
|
ULONG bpc = DiskGeometryArray[Region->Disk].BytesPerCylinder;
|
|
ULONG bpt = DiskGeometryArray[Region->Disk].BytesPerTrack;
|
|
|
|
ASRT(Region->SysID == SYSID_UNUSED);
|
|
|
|
|
|
// 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 == 0) {
|
|
LARGE_INTEGER Delta;
|
|
|
|
if(Type == REGION_EXTENDED) {
|
|
Delta.QuadPart = bpc;
|
|
} else {
|
|
ASRT(Type == REGION_PRIMARY);
|
|
Delta.QuadPart = bpt;
|
|
}
|
|
|
|
CreateData->AlignedRegionOffset = Delta;
|
|
CreateData->AlignedRegionSize.QuadPart = (CreateData->AlignedRegionSize.QuadPart - Delta.QuadPart);
|
|
}
|
|
|
|
CStart = CreateData->AlignedRegionOffset;
|
|
if(MinimumSize.QuadPart == 0) {
|
|
CSize.QuadPart = UInt32x32To64(CreationSizeMB,ONE_MEG);
|
|
} else {
|
|
CSize = MinimumSize;
|
|
if(Type == REGION_LOGICAL) {
|
|
CSize.QuadPart = 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 != 0) {
|
|
if((MinimumSize.QuadPart != 0) || (Mod.QuadPart > bpc/2)) {
|
|
CSize.QuadPart = (CSize.QuadPart + (bpc - Mod.QuadPart));
|
|
} else {
|
|
CSize.QuadPart = (CSize.QuadPart - Mod.QuadPart); // snap downwards tp cyl boundary
|
|
}
|
|
}
|
|
|
|
if(CSize.QuadPart > CreateData->AlignedRegionSize.QuadPart) {
|
|
// Space available in the free space isn't large enough to accomodate
|
|
// the request in its entirety; just use the entire free space.
|
|
CSize = CreateData->AlignedRegionSize;
|
|
}
|
|
|
|
ASRT(CStart.QuadPart != 0);
|
|
ASRT(CSize.QuadPart != 0);
|
|
|
|
*CreationStart = CStart;
|
|
*CreationSize = CSize;
|
|
}
|
|
|
|
|
|
ARC_STATUS
|
|
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;
|
|
PPARTITION *PartitionList;
|
|
|
|
ASRT(Region->SysID == SYSID_UNUSED);
|
|
|
|
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;
|
|
LeftOver.QuadPart = (CreationStart.QuadPart - CreateData->Partition->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 = (CreateData->Partition->Offset.QuadPart + CreateData->Partition->Length.QuadPart) -
|
|
(CreationStart.QuadPart + CreationSize.QuadPart);
|
|
|
|
if(LeftOver.QuadPart != 0) {
|
|
|
|
LARGE_INTEGER TmpResult;
|
|
|
|
TmpResult.QuadPart = CreationStart.QuadPart + CreationSize.QuadPart;
|
|
p2 = AllocatePartitionStructure(Region->Disk,
|
|
TmpResult,
|
|
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) {
|
|
|
|
ASRT(LogicalVolumes[Region->Disk] == NULL);
|
|
|
|
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);
|
|
RenumberPartitions(Region->Disk);
|
|
|
|
ChangesMade[Region->Disk] = TRUE;
|
|
|
|
return(OK_STATUS);
|
|
}
|
|
|
|
|
|
ARC_STATUS
|
|
CreatePartition(
|
|
IN PREGION_DESCRIPTOR Region,
|
|
IN ULONG CreationSizeMB,
|
|
IN REGION_TYPE Type
|
|
)
|
|
{
|
|
LARGE_INTEGER LargeZero;
|
|
|
|
LargeZero.QuadPart = 0;
|
|
return(CreatePartitionEx(Region,
|
|
LargeZero,
|
|
CreationSizeMB,
|
|
Type,
|
|
(UCHAR)((Type == REGION_EXTENDED) ? SYSID_EXTENDED
|
|
: SYSID_BIGFAT
|
|
)
|
|
)
|
|
);
|
|
}
|
|
|
|
|
|
|
|
ARC_STATUS
|
|
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;
|
|
|
|
ASRT( IsInPartitionList(Region->Disk,RegionData->Partition)
|
|
|| IsInLogicalList (Region->Disk,RegionData->Partition)
|
|
);
|
|
|
|
if(IsExtended(Region->SysID)) {
|
|
|
|
ASRT(IsInPartitionList(Region->Disk,RegionData->Partition));
|
|
|
|
// 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);
|
|
RenumberPartitions(Region->Disk);
|
|
|
|
ChangesMade[Region->Disk] = TRUE;
|
|
|
|
return(OK_STATUS);
|
|
}
|
|
|
|
|
|
ARC_STATUS
|
|
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
|
|
|
|
ARC_STATUS
|
|
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
|
|
)
|
|
{
|
|
ARC_STATUS status;
|
|
LARGE_INTEGER AlignedOffset,AlignedSize;
|
|
ULONG SizeMB;
|
|
|
|
while(p) {
|
|
|
|
if(p->SysID == SYSID_UNUSED) {
|
|
|
|
if(WantFreeRegions) {
|
|
|
|
LARGE_INTEGER Result1, Result2;
|
|
|
|
AlignedOffset = AlignTowardsDiskEnd(p->Disk,p->Offset);
|
|
|
|
Result2.QuadPart = p->Offset.QuadPart + p->Length.QuadPart;
|
|
Result1 = AlignTowardsDiskStart(p->Disk,Result2);
|
|
AlignedSize.QuadPart = Result1.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 != 0)
|
|
|| ( 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
|
|
)
|
|
{
|
|
PREGION_DESCRIPTOR p;
|
|
PREGION_DATA data;
|
|
|
|
p = ReallocateMemory(*Regions,((*RegionCount) + 1) * sizeof(REGION_DESCRIPTOR));
|
|
if(p == NULL) {
|
|
return(FALSE);
|
|
} else {
|
|
*Regions = p;
|
|
(*RegionCount)++;
|
|
}
|
|
|
|
p = &(*Regions)[(*RegionCount)-1];
|
|
|
|
if(!(p->Reserved = AllocateMemory(sizeof(REGION_DATA)))) {
|
|
return(FALSE);
|
|
}
|
|
|
|
p->Disk = Partition->Disk;
|
|
p->SysID = Partition->SysID;
|
|
p->SizeMB = SizeMB;
|
|
p->Active = Partition->Active;
|
|
p->Recognized = Partition->Recognized;
|
|
p->PartitionNumber = Partition->PartitionNumber;
|
|
p->OriginalPartitionNumber = Partition->OriginalPartitionNumber;
|
|
p->RegionType = RegionType;
|
|
p->PersistentData = Partition->PersistentData;
|
|
|
|
data = p->Reserved;
|
|
|
|
data->Partition = Partition;
|
|
data->AlignedRegionOffset = AlignedRegionOffset;
|
|
data->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;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
RenumberPartitions(
|
|
IN ULONG Disk
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine determines 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(!IsExtended(p->SysID)) {
|
|
p->PartitionNumber = n;
|
|
if(p->SysID != SYSID_UNUSED) {
|
|
n++;
|
|
}
|
|
}
|
|
p = p->Next;
|
|
}
|
|
p = LogicalVolumes[Disk];
|
|
while(p) {
|
|
p->PartitionNumber = n;
|
|
if(p->SysID != SYSID_UNUSED) {
|
|
n++;
|
|
}
|
|
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;
|
|
|
|
ASRT(Partition);
|
|
|
|
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);
|
|
|
|
ASRT(p);
|
|
|
|
if(p) {
|
|
p->SysID = SysID;
|
|
if(!p->Update) {
|
|
p->Update = CHANGED_DONT_ZAP;
|
|
}
|
|
ChangesMade[p->Disk] = TRUE;
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
SetSysID2(
|
|
IN PREGION_DESCRIPTOR Region,
|
|
IN UCHAR SysID
|
|
)
|
|
{
|
|
PPARTITION p = ((PREGION_DATA)(Region->Reserved))->Partition;
|
|
|
|
p->SysID = SysID;
|
|
if(!p->Update) {
|
|
p->Update = CHANGED_DONT_ZAP;
|
|
}
|
|
ChangesMade[p->Disk] = TRUE;
|
|
}
|
|
|
|
|
|
ULONG
|
|
GetHiddenSectorCount(
|
|
IN ULONG Disk,
|
|
IN ULONG Partition
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine determines the hidden sector count for a
|
|
partition. This value is used by format and is placed in
|
|
the BPB of a FAT partition when the partition is formatted.
|
|
|
|
Arguments:
|
|
|
|
Disk - index of relevent disk
|
|
|
|
Partition - partition number of relevent partition
|
|
|
|
Return Value:
|
|
|
|
The number of hidden sectors (will be 0 if the partition
|
|
does not exist).
|
|
|
|
--*/
|
|
|
|
{
|
|
PPARTITION p = FindPartitionElement(Disk,Partition);
|
|
ULONG HiddenSectorCount = 0;
|
|
LARGE_INTEGER Result;
|
|
|
|
ASRT(p);
|
|
|
|
if(p) {
|
|
|
|
if(IsInLogicalList(Disk,p)) {
|
|
HiddenSectorCount = DiskGeometryArray[Disk].SectorsPerTrack;
|
|
} else {
|
|
ASRT(IsInPartitionList(Disk,p));
|
|
Result.QuadPart = p->Offset.QuadPart / DiskGeometryArray[Disk].BytesPerSector;
|
|
HiddenSectorCount = LOWPART(Result);
|
|
}
|
|
}
|
|
return(HiddenSectorCount);
|
|
}
|
|
|
|
|
|
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;
|
|
}
|
|
return(p);
|
|
}
|
|
|
|
|
|
ARC_STATUS
|
|
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);
|
|
}
|
|
|
|
|
|
ARC_STATUS
|
|
InitializeLogicalVolumeList(
|
|
IN ULONG Disk,
|
|
IN PDRIVE_LAYOUT_INFORMATION DriveLayout,
|
|
IN ULONG PartitionNumber
|
|
)
|
|
|
|
/*++
|
|
|
|
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.
|
|
|
|
PartitionNumber - number to assign to the first logical volume
|
|
on 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;
|
|
LARGE_INTEGER Result1, Result2;
|
|
|
|
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)) {
|
|
|
|
HiddenBytes.QuadPart = UInt32x32To64(d->HiddenSectors,BytesPerSector);
|
|
|
|
Result1.QuadPart = d->StartingOffset.QuadPart - HiddenBytes.QuadPart;
|
|
Result2.QuadPart = d->PartitionLength.QuadPart + HiddenBytes.QuadPart;
|
|
if(!(q = AllocatePartitionStructure(Disk,
|
|
Result1,
|
|
Result2,
|
|
d->PartitionType,
|
|
FALSE,
|
|
d->BootIndicator,
|
|
d->RecognizedPartition
|
|
)
|
|
)
|
|
)
|
|
{
|
|
RETURN_OUT_OF_MEMORY;
|
|
}
|
|
|
|
q->OriginalPartitionNumber = PartitionNumber++;
|
|
AddPartitionToLinkedList(&LogicalVolumes[Disk],q);
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return(InitializeFreeSpace(Disk,
|
|
&LogicalVolumes[Disk],
|
|
p->Offset,
|
|
p->Length
|
|
)
|
|
);
|
|
}
|
|
return(OK_STATUS);
|
|
}
|
|
|
|
|
|
ARC_STATUS
|
|
InitializePrimaryPartitionList(
|
|
IN ULONG Disk,
|
|
IN PDRIVE_LAYOUT_INFORMATION DriveLayout,
|
|
OUT PULONG NextPartitionNumber
|
|
)
|
|
|
|
/*++
|
|
|
|
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.
|
|
|
|
NextPartitionNumber - where to put partition number that should be
|
|
assigned to the first logical volume on the
|
|
disk.
|
|
|
|
Return Value:
|
|
|
|
OK_STATUS or error code.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG i;
|
|
PPARTITION p;
|
|
PPARTITION_INFORMATION d;
|
|
ULONG PartitionNumber = 1;
|
|
LARGE_INTEGER LargeZero;
|
|
|
|
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->OriginalPartitionNumber = IsExtended(p->SysID)
|
|
? 0
|
|
: PartitionNumber++;
|
|
|
|
AddPartitionToLinkedList(&PrimaryPartitions[Disk],p);
|
|
}
|
|
}
|
|
}
|
|
*NextPartitionNumber = PartitionNumber;
|
|
LargeZero.QuadPart = 0;
|
|
return(InitializeFreeSpace(Disk,
|
|
&PrimaryPartitions[Disk],
|
|
LargeZero,
|
|
DiskLengthBytes(Disk)
|
|
)
|
|
);
|
|
}
|
|
|
|
|
|
ARC_STATUS
|
|
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.
|
|
|
|
--*/
|
|
|
|
{
|
|
ARC_STATUS status;
|
|
ULONG Disk;
|
|
PDRIVE_LAYOUT_INFORMATION DriveLayout;
|
|
ULONG PNum;
|
|
|
|
|
|
for(Disk=0; Disk<CountOfDisks; Disk++) {
|
|
|
|
if(OffLine[Disk]) {
|
|
continue;
|
|
}
|
|
|
|
if((status = LowGetDiskLayout(DiskNames[Disk],&DriveLayout)) != OK_STATUS) {
|
|
return(status);
|
|
}
|
|
|
|
if((status = InitializePrimaryPartitionList(Disk,DriveLayout,&PNum)) != OK_STATUS) {
|
|
FreeMemory(DriveLayout);
|
|
return(status);
|
|
}
|
|
if((status = InitializeLogicalVolumeList(Disk,DriveLayout,PNum)) != OK_STATUS) {
|
|
FreeMemory(DriveLayout);
|
|
return(status);
|
|
}
|
|
|
|
FreeMemory(DriveLayout);
|
|
RenumberPartitions(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 l;
|
|
|
|
l.QuadPart = (ULONGLONG)DiskGeometryArray[Disk].Cylinders.QuadPart
|
|
* (ULONGLONG)DiskGeometryArray[Disk].BytesPerCylinder;
|
|
|
|
return(l);
|
|
}
|
|
|
|
|
|
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 = (ULONG)((ULONGLONG)ByteCount.QuadPart / (ULONGLONG)ONE_MEG);
|
|
Remainder = (ULONG)((ULONGLONG)ByteCount.QuadPart % (ULONGLONG)ONE_MEG);
|
|
|
|
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;
|
|
LARGE_INTEGER LargeInteger;
|
|
|
|
mod.QuadPart = Offset.QuadPart % DiskGeometryArray[Disk].BytesPerCylinder;
|
|
if(mod.QuadPart != 0) {
|
|
|
|
LargeInteger.QuadPart = Offset.QuadPart + DiskGeometryArray[Disk].BytesPerCylinder;
|
|
Offset = AlignTowardsDiskStart(Disk,
|
|
LargeInteger
|
|
);
|
|
}
|
|
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));
|
|
}
|
|
|
|
|
|
ARC_STATUS
|
|
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
|
|
|
|
--*/
|
|
|
|
{
|
|
ARC_STATUS 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);
|
|
}
|
|
|
|
|
|
ARC_STATUS
|
|
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;
|
|
ARC_STATUS status;
|
|
BOOLEAN FreeSpace = FALSE;
|
|
|
|
status = GetPrimaryDiskRegions(Disk,&Regions,&RegionCount);
|
|
if(status != OK_STATUS) {
|
|
return(status);
|
|
}
|
|
|
|
for(UsedCount = RecogCount = i = 0; i<RegionCount; i++) {
|
|
ASRT(Regions[i].RegionType != REGION_LOGICAL);
|
|
if(Regions[i].SysID == SYSID_UNUSED) {
|
|
FreeSpace = TRUE;
|
|
} else {
|
|
UsedCount++;
|
|
if(!IsExtended(Regions[i].SysID) && Regions[i].Recognized) {
|
|
RecogCount++;
|
|
}
|
|
}
|
|
}
|
|
ASRT(UsedCount <= ENTRIES_PER_BOOTSECTOR);
|
|
ASRT(RecogCount <= ENTRIES_PER_BOOTSECTOR);
|
|
ASRT(RecogCount <= UsedCount);
|
|
|
|
if((UsedCount < ENTRIES_PER_BOOTSECTOR)
|
|
&& FreeSpace
|
|
&& (!RecogCount || AllowMultiplePrimaries))
|
|
{
|
|
*Allowed = TRUE;
|
|
} else {
|
|
*Allowed = FALSE;
|
|
}
|
|
|
|
FreeRegionArray(Regions,RegionCount);
|
|
return(OK_STATUS);
|
|
}
|
|
|
|
|
|
ARC_STATUS
|
|
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;
|
|
ARC_STATUS status;
|
|
|
|
status = GetPrimaryDiskRegions(Disk,&Regions,&RegionCount);
|
|
if(status != OK_STATUS) {
|
|
return(status);
|
|
}
|
|
|
|
for(UsedCount = FreeCount = i = 0; i<RegionCount; i++) {
|
|
ASRT(Regions[i].RegionType != REGION_LOGICAL);
|
|
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);
|
|
}
|
|
|
|
|
|
ARC_STATUS
|
|
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;
|
|
ARC_STATUS 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++) {
|
|
ASRT(Regions[i].RegionType == REGION_LOGICAL);
|
|
if(Regions[i].SysID == SYSID_UNUSED) {
|
|
*Allowed = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
FreeRegionArray(Regions,RegionCount);
|
|
return(OK_STATUS);
|
|
}
|
|
|
|
|
|
|
|
ARC_STATUS
|
|
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
|
|
|
|
--*/
|
|
|
|
{
|
|
ARC_STATUS 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);
|
|
}
|
|
|
|
|
|
ARC_STATUS
|
|
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;
|
|
ARC_STATUS status;
|
|
|
|
status = GetUsedPrimaryDiskRegions(Disk,&Regions,&RegionCount);
|
|
if(status != OK_STATUS) {
|
|
return(status);
|
|
}
|
|
|
|
*Exists = FALSE;
|
|
|
|
for(i=0; i<RegionCount; i++) {
|
|
ASRT(Regions[i].RegionType != REGION_LOGICAL);
|
|
ASRT(Regions[i].SysID != SYSID_UNUSED);
|
|
if(!IsExtended(Regions[i].SysID)) {
|
|
*Exists = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
FreeRegionArray(Regions,RegionCount);
|
|
return(OK_STATUS);
|
|
}
|
|
|
|
|
|
ARC_STATUS
|
|
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;
|
|
ARC_STATUS status;
|
|
|
|
status = GetUsedPrimaryDiskRegions(Disk,&Regions,&RegionCount);
|
|
if(status != OK_STATUS) {
|
|
return(status);
|
|
}
|
|
|
|
*Exists = FALSE;
|
|
|
|
for(i=0; i<RegionCount; i++) {
|
|
ASRT(Regions[i].RegionType != REGION_LOGICAL);
|
|
ASRT(Regions[i].SysID != SYSID_UNUSED);
|
|
if(IsExtended(Regions[i].SysID)) {
|
|
*Exists = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
FreeRegionArray(Regions,RegionCount);
|
|
return(OK_STATUS);
|
|
}
|
|
|
|
|
|
ARC_STATUS
|
|
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;
|
|
ARC_STATUS 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]);
|
|
}
|
|
|
|
|
|
// sys ID type names
|
|
|
|
char UNKNOWN[] = "Unknown type";
|
|
|
|
PCHAR SysIDStrings[256] = { "Free Space","FAT","XENIX1","XENIX2", // 00-03
|
|
"FAT", "Extended", "FAT", "HPFS/NTFS",// 04-07
|
|
UNKNOWN, UNKNOWN, // 08-09
|
|
"OS/2 Boot Manager", UNKNOWN, // 0a-0b
|
|
UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, // 0c-0f
|
|
UNKNOWN, UNKNOWN, // 10-11
|
|
"EISA Utilities", UNKNOWN, // 12-13
|
|
UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, // 14-17
|
|
UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, // 18-1b
|
|
UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, // 1c-1f
|
|
UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, // 20-23
|
|
UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, // 24-27
|
|
UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, // 28-2b
|
|
UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, // 2c-2f
|
|
UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, // 30-33
|
|
UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, // 34-37
|
|
UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, // 38-3b
|
|
UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, // 3c-3f
|
|
UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, // 40-43
|
|
UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, // 44-47
|
|
UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, // 48-4b
|
|
UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, // 4c-4f
|
|
UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, // 50-53
|
|
UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, // 54-57
|
|
UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, // 58-5b
|
|
UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, // 5c-5f
|
|
UNKNOWN, UNKNOWN, UNKNOWN, "Unix", // 60-63
|
|
UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, // 64-67
|
|
UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, // 68-6b
|
|
UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, // 6c-6f
|
|
UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, // 70-73
|
|
UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, // 74-77
|
|
UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, // 78-7b
|
|
UNKNOWN, UNKNOWN, UNKNOWN, "Unused", // 7c-7f
|
|
UNKNOWN, "Windows NT Fault Tolerance",// 80-81
|
|
UNKNOWN, UNKNOWN, // 82-83
|
|
"Windows NT Fault Tolerance", UNKNOWN,// 84-85
|
|
"Windows NT Fault Tolerance", // 86-86
|
|
"Windows NT Fault Tolerance", // 87-87
|
|
UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, // 88-8b
|
|
UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, // 8c-8f
|
|
UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, // 90-93
|
|
UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, // 94-97
|
|
UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, // 98-9b
|
|
UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, // 9c-9f
|
|
UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, // a0-a3
|
|
UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, // a4-a7
|
|
UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, // a8-ab
|
|
UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, // ac-af
|
|
UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, // b0-b3
|
|
UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, // b4-b7
|
|
UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, // b8-bb
|
|
UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, // bc-bf
|
|
UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, // c0-c3
|
|
UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, // c4-c7
|
|
UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, // c8-cb
|
|
UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, // cc-cf
|
|
UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, // d0-d3
|
|
UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, // d4-d7
|
|
UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, // d8-db
|
|
UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, // dc-df
|
|
UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, // e0-e3
|
|
UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, // e4-e7
|
|
UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, // e8-eb
|
|
UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, // ec-ef
|
|
UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, // f0-f3
|
|
UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, // f4-f7
|
|
UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, // f8-fb
|
|
UNKNOWN, UNKNOWN, UNKNOWN, "Table", // fc-ff
|
|
};
|
|
|
|
|
|
PCHAR
|
|
GetSysIDName(
|
|
UCHAR SysID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine returns the name for a given system ID.
|
|
|
|
Arguments:
|
|
|
|
SysID - system id in question
|
|
|
|
Return Value:
|
|
|
|
Name of system id. The caller must not attempt to free or
|
|
modify this buffer.
|
|
|
|
--*/
|
|
|
|
{
|
|
return(SysIDStrings[SysID]);
|
|
}
|
|
|
|
|
|
// worker routines for WriteDriveLayout
|
|
|
|
VOID
|
|
UnusedEntryFill(
|
|
IN PPARTITION_INFORMATION pinfo,
|
|
IN ULONG EntryCount
|
|
)
|
|
{
|
|
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
|
|
)
|
|
{
|
|
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;
|
|
|
|
// ASRT(pLogical || pNextLogical);
|
|
|
|
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);
|
|
}
|
|
|
|
|
|
ARC_STATUS
|
|
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;
|
|
ARC_STATUS status;
|
|
ULONG Handle;
|
|
LARGE_INTEGER LargeInteger;
|
|
|
|
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);
|
|
}
|
|
|
|
LargeInteger.QuadPart = Offset.QuadPart / SectorSize;
|
|
status = LowWriteSectors(Handle,
|
|
SectorSize,
|
|
LOWPART(LargeInteger),
|
|
1,
|
|
AlignedSectorBuffer
|
|
);
|
|
|
|
LowCloseDisk(Handle);
|
|
FreeMemory(SectorBuffer);
|
|
|
|
return(status);
|
|
}
|
|
|
|
|
|
ARC_STATUS
|
|
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.
|
|
|
|
--*/
|
|
|
|
{
|
|
PDRIVE_LAYOUT_INFORMATION DriveLayout;
|
|
PPARTITION_INFORMATION pinfo;
|
|
PPARTITION n,p;
|
|
ULONG EntryCount;
|
|
ARC_STATUS status;
|
|
LARGE_INTEGER StartingOffset;
|
|
LARGE_INTEGER ExtendedStartingOffset;
|
|
|
|
ExtendedStartingOffset.QuadPart = 0;
|
|
|
|
// allocate a huge buffer now to avoid complicated dynamic
|
|
// reallocation schemes later.
|
|
|
|
if(!(DriveLayout = AllocateMemory(250 * sizeof(PARTITION_INFORMATION)))) {
|
|
RETURN_OUT_OF_MEMORY;
|
|
}
|
|
|
|
pinfo = &DriveLayout->PartitionEntry[0];
|
|
|
|
// first do the mbr.
|
|
|
|
EntryCount=0;
|
|
p = PrimaryPartitions[Disk];
|
|
|
|
while(p) {
|
|
|
|
if(p->SysID != SYSID_UNUSED) {
|
|
|
|
ASRT(EntryCount < ENTRIES_PER_BOOTSECTOR);
|
|
|
|
if(IsExtended(p->SysID)) {
|
|
ExtendedStartingOffset = p->Offset;
|
|
}
|
|
|
|
pinfo[EntryCount].StartingOffset = p->Offset;
|
|
pinfo[EntryCount].PartitionLength = p->Length;
|
|
pinfo[EntryCount].PartitionType = p->SysID;
|
|
pinfo[EntryCount].BootIndicator = p->Active;
|
|
pinfo[EntryCount].RewritePartition = p->Update;
|
|
|
|
// BUGBUG we know that this field is not used by the
|
|
// set drive layout IOCTL or the ARC stub.
|
|
|
|
pinfo[EntryCount].HiddenSectors = 0;
|
|
|
|
// if we're creating this partition, clear out the
|
|
// filesystem boot sector.
|
|
|
|
if(pinfo[EntryCount].RewritePartition
|
|
&& (p->Update != CHANGED_DONT_ZAP)
|
|
&& !IsExtended(pinfo[EntryCount].PartitionType))
|
|
{
|
|
status = ZapSector(Disk,pinfo[EntryCount].StartingOffset);
|
|
if(status != OK_STATUS) {
|
|
FreeMemory(DriveLayout);
|
|
return(status);
|
|
}
|
|
}
|
|
|
|
EntryCount++;
|
|
}
|
|
p = p->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((p = LogicalVolumes[Disk]) && (p->SysID == SYSID_UNUSED)) {
|
|
if(p->Next) {
|
|
ASRT(p->Next->SysID != SYSID_UNUSED);
|
|
|
|
MakeBootRec(Disk,pinfo+EntryCount,NULL,p->Next);
|
|
EntryCount += ENTRIES_PER_BOOTSECTOR;
|
|
p = p->Next;
|
|
} else {
|
|
ASRT(ExtendedStartingOffset.QuadPart != 0);
|
|
#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(p) {
|
|
if(p->SysID != SYSID_UNUSED) {
|
|
|
|
// find the next logical volume.
|
|
|
|
n = p->Next;
|
|
while(n) {
|
|
if(n->SysID != SYSID_UNUSED) {
|
|
break;
|
|
}
|
|
n = n->Next;
|
|
}
|
|
|
|
StartingOffset = MakeBootRec(Disk,pinfo+EntryCount,p,n);
|
|
|
|
// if we're creating a volume, clear out its filesystem
|
|
// boot sector so it starts out fresh.
|
|
|
|
if((StartingOffset.QuadPart != 0) && (p->Update != CHANGED_DONT_ZAP)) {
|
|
status = ZapSector(Disk,StartingOffset);
|
|
if(status != OK_STATUS) {
|
|
FreeMemory(DriveLayout);
|
|
return(status);
|
|
}
|
|
}
|
|
|
|
EntryCount += ENTRIES_PER_BOOTSECTOR;
|
|
}
|
|
p = p->Next;
|
|
}
|
|
|
|
DriveLayout->PartitionCount = EntryCount;
|
|
status = LowSetDiskLayout(DiskNames[Disk],DriveLayout);
|
|
|
|
FreeMemory(DriveLayout);
|
|
|
|
return(status);
|
|
}
|
|
|
|
|
|
ARC_STATUS
|
|
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;
|
|
ARC_STATUS status;
|
|
|
|
ASRT(!OffLine[Disk]);
|
|
|
|
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;
|
|
}
|
|
|
|
ChangesMade[Disk] = FALSE;
|
|
return(OK_STATUS);
|
|
}
|
|
|
|
|
|
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.
|
|
|
|
--*/
|
|
|
|
{
|
|
if(OffLine[Disk]) {
|
|
ASRT(!ChangesMade[Disk]);
|
|
}
|
|
return(ChangesMade[Disk]);
|
|
}
|
|
|
|
VOID
|
|
FdMarkDiskDirty(
|
|
IN ULONG Disk
|
|
)
|
|
{
|
|
ASRT(!OffLine[Disk]);
|
|
|
|
ChangesMade[Disk] = TRUE;
|
|
}
|
|
|
|
VOID
|
|
FdSetPersistentData(
|
|
IN PREGION_DESCRIPTOR Region,
|
|
IN ULONG Data
|
|
)
|
|
{
|
|
((PREGION_DATA)(Region->Reserved))->Partition->PersistentData = 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 LargeInteger;
|
|
|
|
LargeInteger.QuadPart = DiskGeometryArray[Disk].BytesPerCylinder;
|
|
return(max(SIZEMB(LargeInteger),1));
|
|
}
|
|
|
|
|
|
ULONG
|
|
FdGetMaximumSizeMB(
|
|
IN PREGION_DESCRIPTOR Region,
|
|
IN REGION_TYPE CreationType
|
|
)
|
|
{
|
|
PREGION_DATA CreateData = Region->Reserved;
|
|
LARGE_INTEGER MaxSize = CreateData->AlignedRegionSize;
|
|
|
|
ASRT(Region->SysID == SYSID_UNUSED);
|
|
|
|
if(CreateData->AlignedRegionOffset.QuadPart == 0) {
|
|
|
|
ULONG Delta;
|
|
|
|
ASRT((CreationType == REGION_EXTENDED) || (CreationType == REGION_PRIMARY))
|
|
|
|
Delta = (CreationType == REGION_EXTENDED)
|
|
? DiskGeometryArray[Region->Disk].BytesPerCylinder
|
|
: DiskGeometryArray[Region->Disk].BytesPerTrack;
|
|
|
|
MaxSize.QuadPart = MaxSize.QuadPart - Delta;
|
|
}
|
|
|
|
return(SIZEMB(MaxSize));
|
|
}
|
|
|
|
|
|
LARGE_INTEGER
|
|
FdGetExactSize(
|
|
IN PREGION_DESCRIPTOR Region,
|
|
IN BOOLEAN ForExtended
|
|
)
|
|
{
|
|
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 = 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 == 0) || ForExtended) {
|
|
LargeSize.QuadPart = LargeSize.QuadPart - BytesPerTrack.QuadPart;
|
|
}
|
|
|
|
if((RegionData->AlignedRegionOffset.QuadPart == 0) && ForExtended) {
|
|
LargeSize.QuadPart = 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 = 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, LargeZero;
|
|
|
|
if(Region->SysID == SYSID_UNUSED) {
|
|
|
|
|
|
// Determine the exact size and offset of the partition, according
|
|
// to how CreatePartitionEx() will do it.
|
|
|
|
|
|
LargeZero.QuadPart = 0;
|
|
DetermineCreateSizeAndOffset( Region,
|
|
LargeZero,
|
|
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.
|
|
|
|
|
|
return( (End.QuadPart / DiskGeometryArray[Region->Disk].BytesPerCylinder) > 1023 );
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
IsDiskOffLine(
|
|
IN ULONG Disk
|
|
)
|
|
{
|
|
return(OffLine[Disk]);
|
|
}
|