/*++ Copyright (c) 1990-1993 Microsoft Corporation Module Name: fdmisc.c Abstract: Miscellaneous routines for NT fdisk. Author: Ted Miller (tedm) 7-Jan-1992 Modifications: 13-Dec-1993 (bobri) CdRom initialization support. --*/ #include "fdisk.h" #include extern HWND InitDlg; extern BOOLEAN StartedAsIcon; BOOL AllDisksOffLine( VOID ) /*++ Routine Description: Determine whether all hard disks are off line. Arguments: None. Return Value: TRUE if all disks off-line, false otherwise. --*/ { ULONG i; FDASSERT(DiskCount); for (i=0; i= MSG_FIRST_FDISK_MSG ? FORMAT_MESSAGE_FROM_HMODULE : FORMAT_MESSAGE_FROM_SYSTEM, NULL, Msg, 0, Buffer, BufferSize, &arglist); if (!x) { // couldn't find message LoadString(hModule, Msg >= MSG_FIRST_FDISK_MSG ? IDS_NOT_IN_APP_MSG_FILE : IDS_NOT_IN_SYS_MSG_FILE, text, sizeof(text)/sizeof(TCHAR)); wsprintf(Buffer, text, Msg); } } DWORD CommonDialog( IN DWORD MsgCode, IN LPTSTR Caption, IN DWORD Flags, IN va_list arglist ) /*++ Routine Description: Simple dialog routine to get dialogs out of the resource for the program and run them as a message box. Arguments: MsgCode - dialog message code Caption - message box caption Flags - standard message box flags arglist - list to be given when pulling the message text Return Value: The MessageBox() return value --*/ { TCHAR MsgBuf[MESSAGE_BUFFER_SIZE]; if (!StartedAsIcon) { // Flags |= MB_SETFOREGROUND; } if (InitDlg) { PostMessage(InitDlg, (WM_USER + 1), 0, 0); InitDlg = (HWND) 0; } _RetreiveAndFormatMessage(MsgCode, MsgBuf, sizeof(MsgBuf), arglist); return MessageBox(GetActiveWindow(), MsgBuf, Caption, Flags); } VOID ErrorDialog( IN DWORD ErrorCode, ... ) /*++ -Routine Description: This routine retreives a message from the app or system message file and displays it in a message box. Arguments: ErrorCode - number of message ... - strings for insertion into message Return Value: None. --*/ { va_list arglist; va_start(arglist, ErrorCode); CommonDialog(ErrorCode, NULL, MB_ICONHAND | MB_OK | MB_SYSTEMMODAL, arglist); va_end(arglist); } VOID WarningDialog( IN DWORD MsgCode, ... ) /*++ Routine Description: This routine retreives a message from the app or system message file and displays it in a message box. Arguments: MsgCode - number of message ... - strings for insertion into message Return Value: None. --*/ { TCHAR Caption[100]; va_list arglist; va_start(arglist, MsgCode); LoadString(hModule, IDS_APPNAME, Caption, sizeof(Caption)/sizeof(TCHAR)); CommonDialog(MsgCode, Caption, MB_ICONEXCLAMATION | MB_OK | MB_TASKMODAL, arglist); va_end(arglist); } DWORD ConfirmationDialog( IN DWORD MsgCode, IN DWORD Flags, ... ) /*++ Routine Description: Support for a simple confirmation dialog Arguments: MsgCode - resource code for message Flags - dialog flags Return Value: Result from the CommonDialog() performed. --*/ { TCHAR Caption[100]; DWORD x; va_list arglist; va_start(arglist, Flags); LoadString(hModule, IDS_CONFIRM, Caption, sizeof(Caption)/sizeof(TCHAR)); x = CommonDialog(MsgCode, Caption, Flags | MB_TASKMODAL, arglist); va_end(arglist); return x; } VOID InfoDialog( IN DWORD MsgCode, ... ) /*++ Routine Description: This routine retreives a message from the app or system message file and displays it in a message box. Arguments: MsgCode - number of message ... - strings for insertion into message Return Value: None. --*/ { TCHAR Caption[100]; va_list arglist; va_start(arglist, MsgCode); LoadString(hModule, IDS_APPNAME, Caption, sizeof(Caption)/sizeof(TCHAR)); CommonDialog(MsgCode, Caption, MB_ICONINFORMATION | MB_OK | MB_TASKMODAL, arglist); va_end(arglist); } PREGION_DESCRIPTOR LocateRegionForFtObject( IN PFT_OBJECT FtObject ) /*++ Routine Description: Given an FtObject, find the associated region descriptor Arguments: FtObject - the ft object to search for. Return Value: NULL - no descriptor found !NULL - a pointer to the region descriptor for the FT object ++*/ { PDISKSTATE diskState; PREGION_DESCRIPTOR regionDescriptor; DWORD disk, region; PPERSISTENT_REGION_DATA regionData; for (disk = 0; disk < DiskCount; disk++) { diskState = Disks[disk]; for (region = 0; region < diskState->RegionCount; region++) { regionDescriptor = &diskState->RegionArray[region]; regionData = PERSISTENT_DATA(regionDescriptor); if (regionData) { if (regionData->FtObject == FtObject) { return regionDescriptor; } } } } return NULL; } VOID InitVolumeLabelsAndTypeNames( VOID ) /*++ Routine Description: Determine the volume label and type name for each significant (non-extended, non-free, recognized) partition. Assumes that persistent data has already been set up, and drive letters determined. Arguments: None. Return Value: None. --*/ { DWORD disk, region; PDISKSTATE ds; PREGION_DESCRIPTOR rd; PPERSISTENT_REGION_DATA regionData; WCHAR diskName[4]; WCHAR volumeLabel[100]; WCHAR typeName[100]; UINT errorMode; lstrcpyW(diskName, L"x:\\"); for (disk=0; diskRegionCount; region++) { rd = &ds->RegionArray[region]; if (DmSignificantRegion(rd)) { // If the region has a drive letter, use the drive letter // to get the info via the Windows API. Otherwise we'll // have to use the NT API. regionData = PERSISTENT_DATA(rd); if (!regionData) { continue; } if ((regionData->DriveLetter == NO_DRIVE_LETTER_YET) || (regionData->DriveLetter == NO_DRIVE_LETTER_EVER)) { PWSTR tempLabel, tempName; // No drive letter. Use NT API. // If this is an FT set use the zero member disk for the // call so all members get the right type and label if (regionData->FtObject) { PFT_OBJECT searchFtObject; // Want to get rd pointing to the zeroth member searchFtObject = regionData->FtObject->Set->Member0; // Now search regions for this match rd = LocateRegionForFtObject(searchFtObject); if (!rd) { continue; } } if (GetVolumeLabel(rd->Disk, rd->PartitionNumber, &tempLabel) == NO_ERROR) { lstrcpyW(volumeLabel, tempLabel); Free(tempLabel); } else { *volumeLabel = 0; } if (GetTypeName(rd->Disk, rd->PartitionNumber, &tempName) == NO_ERROR) { lstrcpyW(typeName, tempName); Free(tempName); } else { lstrcpyW(typeName, wszUnknown); } } else { // Use Windows API. diskName[0] = (WCHAR)(UCHAR)regionData->DriveLetter; errorMode = SetErrorMode(SEM_FAILCRITICALERRORS); if (!GetVolumeInformationW(diskName, volumeLabel, sizeof(volumeLabel)/2, NULL, NULL, NULL, typeName, sizeof(typeName)/2)) { lstrcpyW(typeName, wszUnknown); *volumeLabel = 0; } SetErrorMode(errorMode); } if (!lstrcmpiW(typeName, L"raw")) { lstrcpyW(typeName, wszUnknown); } regionData->TypeName = Malloc((lstrlenW(typeName) + 1) * sizeof(WCHAR)); regionData->VolumeLabel = Malloc((lstrlenW(volumeLabel) + 1) * sizeof(WCHAR)); lstrcpyW(regionData->TypeName, typeName); lstrcpyW(regionData->VolumeLabel, volumeLabel); } } } } VOID DetermineRegionInfo( IN PREGION_DESCRIPTOR Region, OUT PWSTR *TypeName, OUT PWSTR *VolumeLabel, OUT PWCH DriveLetter ) /*++ Routine Description: For a given region, fetch the persistent data, appropriately modified depending on whether the region is used or free, recognized, etc. Arguments: Region - supplies a pointer to the region whose data is to be fetched. TypeName - receives a pointer to the type name. If the region is unrecognized, the type is determined based on the system id of the partition. VolumeLabel - receives a pointer to the volume label. If the region is free space or unrecognized, the volume label is "". DriveLetter - receices the drive letter. If the region is free space or unrecognized, the drive letter is ' ' (space). Return Value: None. --*/ { PWSTR typeName, volumeLabel; WCHAR driveLetter; PPERSISTENT_REGION_DATA regionData = PERSISTENT_DATA(Region); if (DmSignificantRegion(Region)) { typeName = regionData->TypeName; volumeLabel = regionData->VolumeLabel; driveLetter = (WCHAR)(UCHAR)regionData->DriveLetter; if ((driveLetter == NO_DRIVE_LETTER_YET) || (driveLetter == NO_DRIVE_LETTER_EVER)) { driveLetter = L' '; } } else { typeName = GetWideSysIDName(Region->SysID); volumeLabel = L""; driveLetter = L' '; } *TypeName = typeName; *VolumeLabel = volumeLabel; *DriveLetter = driveLetter; } PREGION_DESCRIPTOR RegionFromFtObject( IN PFT_OBJECT FtObject ) /*++ Routine Description: Given an ft object, determine which region it belongs to. The algorithm is brute force -- look at each region on each disk until a match is found. Arguments: FtObject - ft member whose region is to be located. Return Value: pointer to region descriptor --*/ { PREGION_DESCRIPTOR reg; DWORD region, disk; PDISKSTATE ds; for (disk=0; diskRegionCount; region++) { reg = &ds->RegionArray[region]; if (DmSignificantRegion(reg) && (GET_FT_OBJECT(reg) == FtObject)) { return reg; } } } return NULL; } // // For each drive letter, these arrays hold the disk and partition number // the the drive letter is linked to. // #define M1 ((unsigned)(-1)) unsigned DriveLetterDiskNumbers[26] = { M1,M1,M1,M1,M1,M1,M1,M1,M1,M1,M1,M1, M1,M1,M1,M1,M1,M1,M1,M1,M1,M1,M1,M1 }, DriveLetterPartitionNumbers[24] = { M1,M1,M1,M1,M1,M1,M1,M1,M1,M1,M1,M1, M1,M1,M1,M1,M1,M1,M1,M1,M1,M1,M1,M1 }; #undef M1 // // Drive letter usage map. Bit n set means that drive letter 'C'+n is in use. // ULONG DriveLetterUsageMap = 0; #define DRIVELETTERBIT(letter) (1 << (unsigned)((UCHAR)letter-(UCHAR)'C')) #define SetDriveLetterUsed(letter) DriveLetterUsageMap |= DRIVELETTERBIT(letter) #define SetDriveLetterFree(letter) DriveLetterUsageMap &= (~DRIVELETTERBIT(letter)) #define IsDriveLetterUsed(letter) (DriveLetterUsageMap & DRIVELETTERBIT(letter)) CHAR GetAvailableDriveLetter( VOID ) /*++ Routine Description: Scan the drive letter usage bitmap and return the next available drive letter. May also mark the drivee letter used. Arguments: None. Return Value: The next available drive letter, or 0 if all are used. --*/ { CHAR driveLetter; FDASSERT(!(DriveLetterUsageMap & 0xff000000)); for (driveLetter = 'C'; driveLetter <= 'Z'; driveLetter++) { if (!IsDriveLetterUsed(driveLetter)) { return driveLetter; } } return 0; } VOID MarkDriveLetterUsed( IN CHAR DriveLetter ) /*++ Routine Description: Given an ASCII drive letter, mark it in the usage map as being used. Arguments: DriveLetter - the letter to mark Return Value: None --*/ { FDASSERT(!(DriveLetterUsageMap & 0xff000000)); if ((DriveLetter != NO_DRIVE_LETTER_YET) && (DriveLetter != NO_DRIVE_LETTER_EVER)) { SetDriveLetterUsed(DriveLetter); } } VOID MarkDriveLetterFree( IN CHAR DriveLetter ) /*++ Routine Description: Given a drive letter, remove it from the usage map, thereby making it available for reuse. Arguments: Drive Letter - the letter to free Return Value: None --*/ { FDASSERT(!(DriveLetterUsageMap & 0xff000000)); if ((DriveLetter != NO_DRIVE_LETTER_YET) && (DriveLetter != NO_DRIVE_LETTER_EVER)) { SetDriveLetterFree(DriveLetter); } } BOOL DriveLetterIsAvailable( IN CHAR DriveLetter ) /*++ Routine Description: Determine if the drive letter given is available for use. Arguments: DriveLetter - the letter to check in the usage map Return Value: TRUE if it is free and can be used. FALSE if it is currently in use. --*/ { FDASSERT(!(DriveLetterUsageMap & 0xff000000)); FDASSERT((DriveLetter != NO_DRIVE_LETTER_YET) && (DriveLetter != NO_DRIVE_LETTER_EVER)); return !IsDriveLetterUsed(DriveLetter); } BOOL AllDriveLettersAreUsed( VOID ) /*++ Routine Description: Determine if all possible drive letters are in use. Arguments: None Return Value: TRUE if all letters are in use - FALSE otherwise --*/ { FDASSERT(!(DriveLetterUsageMap & 0xff000000)); return(DriveLetterUsageMap == 0x00ffffff); } ULONG GetDiskNumberFromDriveLetter( IN CHAR DriveLetter ) /*++ Routine Description: Given a drive letter return the disk number that contains the partition that is the drive letter. Arguments: DriveLetter - the drive letter to check. Return Value: -1 if the letter is invalid. The disk number for the drive letter if it is valid. --*/ { DriveLetter = toupper( DriveLetter ); if (DriveLetter >= 'C' && DriveLetter <= 'Z') { return DriveLetterDiskNumbers[ DriveLetter - 'C' ]; } else { return (ULONG)(-1); } } ULONG GetPartitionNumberFromDriveLetter( IN CHAR DriveLetter ) /*++ Routine Description: Given a drive letter return the numeric value for the partition that the letter is associated with. Arguments: DriveLetter - the letter in question. Return Value: -1 if letter is invalid Partition number for partition that is the drive letter --*/ { DriveLetter = toupper( DriveLetter ); if (DriveLetter >= 'C' && DriveLetter <= 'Z') { return DriveLetterPartitionNumbers[ DriveLetter - 'C' ]; } else { return (ULONG)(-1); } } CHAR LocateDriveLetterFromDiskAndPartition( IN ULONG Disk, IN ULONG Partition ) /*++ Routine Description: Given a disk and partition number return the drive letter assigned to it. Arguments: Disk - the disk index Partition - the partition index Return Value: The drive letter for the specific partition or NO_DRIVE_LETTER_YET if it is not assigned a letter. --*/ { unsigned i; for (i=0; i<24; i++) { if (Disk == DriveLetterDiskNumbers[i] && Partition == DriveLetterPartitionNumbers[i]) { return((CHAR)(i+(unsigned)(UCHAR)'C')); } } return NO_DRIVE_LETTER_YET; } CHAR LocateDriveLetter( IN PREGION_DESCRIPTOR Region ) /*++ Routine Description: Return the drive letter associated to a region. Arguments: Region - the region wanted. Return Value: The drive letter or NO_DRIVE_LETTER_YET --*/ { PPERSISTENT_REGION_DATA regionData = PERSISTENT_DATA(Region); if (regionData) { if (regionData->FtObject) { if (regionData->DriveLetter) { return regionData->DriveLetter; } } } return LocateDriveLetterFromDiskAndPartition(Region->Disk, Region->OriginalPartitionNumber); } #define IsDigitW(digit) (((digit) >= L'0') && ((digit) <= L'9')) VOID InitializeDriveLetterInfo( VOID ) /*++ Routine Description: Initialze all of the external support structures for drive letter maintainence. Arguments: None Return Value: None --*/ { WCHAR DriveLetterW; CHAR DriveLetter = '\0'; PWSTR LinkTarget; WCHAR DosDevicesName[sizeof(L"\\DosDevices\\A:")]; int DiskNo, PartNo; PWSTR Pattern, String; DWORD x, ec; PFT_OBJECT FtObject; PFT_OBJECT_SET FtSet; PREGION_DESCRIPTOR Region; // Construct list of drives with pagefiles LoadExistingPageFileInfo(); // Initialize network information. NetworkInitialize(); // For each drive letter c-z, query the symbolic link. for (DriveLetterW=L'C'; DriveLetterW<=L'Z'; DriveLetterW++) { wsprintfW(DosDevicesName, L"%ws%wc:", L"\\DosDevices\\", DriveLetterW); if ((ec = GetDriveLetterLinkTarget(DosDevicesName, &LinkTarget)) == NO_ERROR) { // Check if it is a Cdrom if (_wcsnicmp(LinkTarget, L"\\Device\\CdRom", 13) == 0) { // Save the information on this CdRom away CdRomAddDevice(LinkTarget, DriveLetterW); } // The drive letter is used because it is linked to something, // even if we can't figure out what. So mark it used here. SetDriveLetterUsed(DriveLetterW); CharUpperW(LinkTarget); Pattern = L"\\DEVICE\\HARDDISK"; String = LinkTarget; // Attempt to match the '\device\harddisk' part for (x=0; x < (sizeof(L"\\DEVICE\\HARDDISK") / sizeof(WCHAR)) - 1; x++) { if (*Pattern++ != *String++) { goto next_letter; } } // Now get the hard disk # if (!IsDigitW(*String)) { continue; } DiskNo = 0; while (IsDigitW(*String)) { DiskNo = (DiskNo * 10) + (*String - L'0'); *String++; } // Attempt to match the '\partition' part Pattern = L"\\PARTITION"; for (x=0; x < (sizeof(L"\\PARTITION") / sizeof(WCHAR)) - 1; x++) { if (*Pattern++ != *String++) { goto next_letter; } } // Now get the partition #, which cannot be 0 PartNo = 0; while (IsDigitW(*String)) { PartNo = (PartNo * 10) + (*String - L'0'); *String++; } if (!PartNo) { continue; } // Make sure there is nothing left in the link target's name if (*String) { continue; } // We understand the link target. Store the disk and partition. DriveLetterDiskNumbers[DriveLetterW-L'C'] = DiskNo; DriveLetterPartitionNumbers[DriveLetterW-L'C'] = PartNo; } else { if (ec == ERROR_ACCESS_DENIED) { ErrorDialog(MSG_ACCESS_DENIED); // BUGBUG When system and workstation manager are the same // thing, then we'd never have gotten here. We can't just // send a WM_DESTROY message to hwndFrame because we're not // in the message loop here -- we end up doing a bunch of // processing before the quit message is pulled our of the // queue. So just exit. SendMessage(hwndFrame,WM_DESTROY,0,0); exit(1); } } next_letter: {} } // Now for each non-ft, significant region on each disk, figure out its // drive letter. for (x=0; xRegionCount; reg++) { PREGION_DESCRIPTOR region = &ds->RegionArray[reg]; if (DmSignificantRegion(region)) { // Handle drive letters for FT sets specially. if (!GET_FT_OBJECT(region)) { PERSISTENT_DATA(region)->DriveLetter = LocateDriveLetter(region); } } } // If this is a removable disk, record the reserved drive // letter for that disk. if (IsDiskRemovable[x]) { RemovableDiskReservedDriveLetters[x] = LocateDriveLetterFromDiskAndPartition(x, 1); } else { RemovableDiskReservedDriveLetters[x] = NO_DRIVE_LETTER_YET; } } // Now handle ft sets. For each set, loop through the objects twice. // On the first pass, figure out which object actually is linked to the // drive letter. On the second pass, assign the drive letter found to // each of the objects in the set. for (FtSet = FtObjects; FtSet; FtSet = FtSet->Next) { for (FtObject = FtSet->Members; FtObject; FtObject = FtObject->Next) { Region = RegionFromFtObject(FtObject); if (Region) { if ((DriveLetter = LocateDriveLetter(Region)) != NO_DRIVE_LETTER_YET) { break; } } } for (FtObject = FtSet->Members; FtObject; FtObject = FtObject->Next) { Region = RegionFromFtObject(FtObject); if (Region) { PERSISTENT_DATA(Region)->DriveLetter = DriveLetter; } } } } #if DBG VOID FdiskAssertFailedRoutine( IN char *Expression, IN char *FileName, IN int LineNumber ) /*++ Routine Description: Routine that is called when an assertion fails in the debug version. Throw up a list box giving appriopriate information and terminate the program. Arguments: Source - source (ansi ascii) string Dest - destination string or wide string Return Value: None. --*/ { char text[500]; wsprintf(text, "Line #%u in File '%s'\n[%s]\n\nClick OK to exit.", LineNumber, FileName, Expression ); MessageBoxA(NULL,text,"Assertion Failure",MB_TASKMODAL | MB_OK); exit(1); } #include PVOID LogFile; int LoggingLevel = 1000; VOID InitLogging( VOID ) /*++ Routine Description: Open the log file for debug logging. Arguments: None Return Value: None --*/ { LogFile = (PVOID)fopen("c:\\fdisk.log","wt"); if(LogFile == NULL) { MessageBox(GetActiveWindow(),"Can't open log file; logging turned off","DEBUG",MB_SYSTEMMODAL|MB_OK); LoggingLevel = -1; } } VOID FdLog( IN int Level, IN PCHAR FormatString, ... ) /*++ Routine Description: Write a line into the log file for debugging. Arguments: Debug level and "printf" like argument string. Return Value: None --*/ { va_list arglist; if(Level <= LoggingLevel) { va_start(arglist,FormatString); if(vfprintf((FILE *)LogFile,FormatString,arglist) < 0) { LoggingLevel = -1; MessageBox(GetActiveWindow(),"Error writing to log file; logging turned off","DEBUG",MB_SYSTEMMODAL|MB_OK); fclose((FILE *)LogFile); } else { fflush((FILE *)LogFile); } va_end(arglist); } } VOID LOG_DISK_REGISTRY( IN PCHAR RoutineName, IN PDISK_REGISTRY DiskRegistry ) /*++ Routine Description: Log what was in the disk registry into the debugging log file. Arguments: RoutineName - calling routines name DiskRegistry - registry information for disks Return Value: None --*/ { ULONG i; PDISK_DESCRIPTION diskDesc; FDLOG((2,"%s: %u disks; registry info follows:\n",RoutineName,DiskRegistry->NumberOfDisks)); diskDesc = DiskRegistry->Disks; for(i=0; iNumberOfDisks; i++) { LOG_ONE_DISK_REGISTRY_DISK_ENTRY(NULL,diskDesc); diskDesc = (PDISK_DESCRIPTION)&diskDesc->Partitions[diskDesc->NumberOfPartitions]; } } VOID LOG_ONE_DISK_REGISTRY_DISK_ENTRY( IN PCHAR RoutineName OPTIONAL, IN PDISK_DESCRIPTION DiskDescription ) /*++ Routine Description: This routine walks through the partition information from the registry for a single disk and writes lines in the debugging log file. Arguments: RoutineName - the name of the calling routine DiskDescription - the disk description portion of the registry Return Value: None --*/ { USHORT j; PDISK_PARTITION partDesc; PDISK_DESCRIPTION diskDesc = DiskDescription; if(ARGUMENT_PRESENT(RoutineName)) { FDLOG((2,"%s: disk registry entry follows:\n",RoutineName)); } FDLOG((2," Disk signature : %08lx\n",diskDesc->Signature)); FDLOG((2," Partition count: %u\n",diskDesc->NumberOfPartitions)); if(diskDesc->NumberOfPartitions) { FDLOG((2," # Dr FtTyp FtGrp FtMem Start Length\n")); } for(j=0; jNumberOfPartitions; j++) { CHAR dr1,dr2; partDesc = &diskDesc->Partitions[j]; if(partDesc->AssignDriveLetter) { if(partDesc->DriveLetter) { dr1 = partDesc->DriveLetter; dr2 = ':'; } else { dr1 = dr2 = ' '; } } else { dr1 = 'n'; dr2 = 'o'; } FDLOG((2, " %02u %c%c %-5u %-5u %-5u %08lx:%08lx %08lx:%08lx\n", partDesc->LogicalNumber, dr1,dr2, partDesc->FtType, partDesc->FtGroup, partDesc->FtMember, partDesc->StartingOffset.HighPart, partDesc->StartingOffset.LowPart, partDesc->Length.HighPart, partDesc->Length.LowPart )); } } VOID LOG_DRIVE_LAYOUT( IN PDRIVE_LAYOUT_INFORMATION DriveLayout ) /*++ Routine Description: Write the drive layout into the debugging log file. Arguments: DriveLayout - the layout to write Return Value: None --*/ { ULONG i; FDLOG((2," Disk signature : %08lx\n",DriveLayout->Signature)); FDLOG((2," Partition count: %u\n",DriveLayout->PartitionCount)); for(i=0; iPartitionCount; i++) { if(!i) { FDLOG((2," ID Active Recog Start Size Hidden\n")); } FDLOG((2, " %02x %s %s %08lx:%08lx %08lx:%08lx %08lx\n", DriveLayout->PartitionEntry[i].PartitionType, DriveLayout->PartitionEntry[i].BootIndicator ? "yes" : "no ", DriveLayout->PartitionEntry[i].RecognizedPartition ? "yes" : "no ", DriveLayout->PartitionEntry[i].StartingOffset.HighPart, DriveLayout->PartitionEntry[i].StartingOffset.LowPart, DriveLayout->PartitionEntry[i].PartitionLength.HighPart, DriveLayout->PartitionEntry[i].PartitionLength.LowPart, DriveLayout->PartitionEntry[i].HiddenSectors )); } } #endif