summaryrefslogtreecommitdiffstats
path: root/private/utils/fdisk/fdengine.c
diff options
context:
space:
mode:
Diffstat (limited to 'private/utils/fdisk/fdengine.c')
-rw-r--r--private/utils/fdisk/fdengine.c3734
1 files changed, 3734 insertions, 0 deletions
diff --git a/private/utils/fdisk/fdengine.c b/private/utils/fdisk/fdengine.c
new file mode 100644
index 000000000..8aaa91564
--- /dev/null
+++ b/private/utils/fdisk/fdengine.c
@@ -0,0 +1,3734 @@
+/*++
+
+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;
+}