diff options
Diffstat (limited to 'private/utils/fdisk/fdengine.c')
-rw-r--r-- | private/utils/fdisk/fdengine.c | 3734 |
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; +} |