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