/*++ Copyright (c) 1991-1994 Microsoft Corporation Module Name: fd_nt.c Abstract: This module wraps fdisk engine functions. This is done to avoid having files that include both the full windows and the full nt include file sets. Functions that manipulate engine structures (REGIONs, for example) are also placed here. This file is targeted at NT, not Windows. Author: Ted Miller (tedm) 5-Dec-1991 Revision History: Misc cleanup (BobRi) 22-Jan-1994 --*/ #include "fdisk.h" #include #include // These partition ID's are for systems recognized by WINDISK, // even though they don't appear in ntdddisk.h. #define PARTITION_OS2_BOOT 0xa #define PARTITION_EISA 0x12 WCHAR UnicodeSysIdName[100]; BYTE StringBuffer[100]; // Pagefile support structures. typedef struct _PAGEFILE_LOCATION { struct _PAGEFILE_LOCATION *Next; CHAR DriveLetter; } PAGEFILE_LOCATION, *PPAGEFILE_LOCATION; PPAGEFILE_LOCATION PagefileHead = NULL; // For some reason the file systems don't like being accessed shortly after // a format or lock event. #define SLEEP_TIME (1000*2) // 2 seconds PWSTR GetWideSysIDName( IN UCHAR SysID ) /*++ Routine Description: Arguments: Return Value: --*/ { ANSI_STRING ansiString; UNICODE_STRING unicodeString; DWORD stringId; // Get the name, which is a byte-string. switch (SysID) { case PARTITION_ENTRY_UNUSED: stringId = IDS_PARTITION_FREE; break; case PARTITION_XENIX_1: stringId = IDS_PARTITION_XENIX1; break; case PARTITION_XENIX_2: stringId = IDS_PARTITION_XENIX2; break; case PARTITION_OS2_BOOT: stringId = IDS_PARTITION_OS2_BOOT; break; case PARTITION_EISA: stringId = IDS_PARTITION_EISA; break; case PARTITION_UNIX: stringId = IDS_PARTITION_UNIX; break; case PARTITION_PREP: #ifdef _PPC_ stringId = IDS_PARTITION_POWERPC; #else // If not on a PPC platform, assume this is Eisa related stringId = IDS_PARTITION_EISA; #endif break; default: stringId = IDS_UNKNOWN; break; } LoadString(hModule, stringId, StringBuffer, sizeof(StringBuffer)); RtlInitAnsiString(&ansiString, StringBuffer); // // Convert to Unicode // unicodeString.Buffer = UnicodeSysIdName; unicodeString.MaximumLength = sizeof(UnicodeSysIdName); RtlAnsiStringToUnicodeString(&unicodeString, &ansiString, FALSE); return UnicodeSysIdName; } ULONG MyDiskRegistryGet( OUT PDISK_REGISTRY *DiskRegistry ) /*++ Routine Description: Allocate memory for the size of the disk registry, obtain the registry contents (if any) and return the pointer to the allocated memory. Arguments: A pointer to a disk registry pointer. Return Value: status indicating success or failure. --*/ { ULONG length; PDISK_REGISTRY diskRegistry; NTSTATUS status; while (((status = DiskRegistryGet(NULL, &length)) == STATUS_NO_MEMORY) || (status == STATUS_INSUFFICIENT_RESOURCES)) { ConfirmOutOfMemory(); } if (!NT_SUCCESS(status)) { return EC(status); } diskRegistry = Malloc(length); while (((status = DiskRegistryGet(diskRegistry, &length)) == STATUS_NO_MEMORY) || (status == STATUS_INSUFFICIENT_RESOURCES)) { ConfirmOutOfMemory(); } if (NT_SUCCESS(status)) { LOG_DISK_REGISTRY("MyDiskRegistryGet", diskRegistry); *DiskRegistry = diskRegistry; } return EC(status); } ULONG FormDiskSignature( VOID ) /*++ Routine Description: Return a ULONG disk signature. This is derived from the current system time. Arguments: None Return Value: A 32-bit signature --*/ { LARGE_INTEGER time; static ULONG baseSignature = 0; if (!baseSignature) { NtQuerySystemTime(&time); time.QuadPart = time.QuadPart >> 16; baseSignature = time.LowPart; } return baseSignature++; } BOOLEAN GetVolumeSizeMB( IN ULONG Disk, IN ULONG Partition, OUT PULONG Size ) /*++ Routine Description: Given a disk and a partition, query the "volume" to get its size. By performing the query on the 1st partition of a potential FT set, the total size of the set will be returned. If the partition isn't an FT set, it will work too. Arguments: Disk - the disk number Partition - the partition number Size - the size of the "volume" Return Value: TRUE - a size was returned. FALSE - something failed in getting the size. --*/ { BOOLEAN retValue = FALSE; IO_STATUS_BLOCK statusBlock; HANDLE handle; STATUS_CODE sc; PARTITION_INFORMATION partitionInfo; LARGE_INTEGER partitionLength; *Size = 0; sc = LowOpenPartition(GetDiskName(Disk), Partition, &handle); if (sc == OK_STATUS) { sc = NtDeviceIoControlFile(handle, 0, NULL, NULL, &statusBlock, IOCTL_DISK_GET_PARTITION_INFO, NULL, 0, &partitionInfo, sizeof(PARTITION_INFORMATION)); if (sc == OK_STATUS) { // Convert to MB partitionLength.QuadPart = partitionInfo.PartitionLength.QuadPart >> 20; *Size = partitionLength.LowPart; retValue = TRUE; } LowCloseDisk(handle); } return retValue; } ULONG GetVolumeTypeAndSize( IN ULONG Disk, IN ULONG Partition, OUT PWSTR *Label, OUT PWSTR *Type, OUT PULONG Size ) /*++ Routine Description: Given a disk and partition number, determine its size, label and file system type. This routine will allocate the space for label and file system type. It is the responsibility of the caller to free this memory. Arguments: Disk - the disk number Partition - the partition number Label - a pointer to a pointer for a WCHAR string to contain the label Type - a pointer to a pointer for a WCHAR string to contain the file system type. Size - a pointer to a ULONG for the size of the disk in KB. Return Value: OK_STATUS - everything was performed. !OK_STATUS - the error code that was returned in the process of performing this work. --*/ { IO_STATUS_BLOCK statusBlock; HANDLE handle; unsigned char buffer[256]; PWSTR label, name; ULONG length; DISK_GEOMETRY diskGeometry; STATUS_CODE sc; BOOLEAN firstTime = TRUE; PFILE_FS_VOLUME_INFORMATION labelInfo = (PFILE_FS_VOLUME_INFORMATION)buffer; PFILE_FS_ATTRIBUTE_INFORMATION info = (PFILE_FS_ATTRIBUTE_INFORMATION)buffer; while (1) { sc = LowOpenPartition(GetDiskName(Disk), Partition, &handle); if (sc == OK_STATUS) { sc = NtQueryVolumeInformationFile(handle, &statusBlock, buffer, sizeof(buffer), FileFsVolumeInformation); if (sc == OK_STATUS) { length = labelInfo->VolumeLabelLength; labelInfo->VolumeLabel[length/sizeof(WCHAR)] = 0; length = (length+1) * sizeof(WCHAR); label = Malloc(length); RtlMoveMemory(label, labelInfo->VolumeLabel, length); } else { label = Malloc(sizeof(WCHAR)); *label = 0; } *Label = label; if (sc == OK_STATUS) { sc = NtQueryVolumeInformationFile(handle, &statusBlock, buffer, sizeof(buffer), FileFsAttributeInformation); if (sc == OK_STATUS) { length = info->FileSystemNameLength; info->FileSystemName[length/sizeof(WCHAR)] = 0; length = (length+1)*sizeof(WCHAR); name = Malloc(length); RtlMoveMemory(name, info->FileSystemName, length); } else { name = Malloc(sizeof(WCHAR)); *name = 0; } *Type = name; } if (sc == OK_STATUS) { sc = NtDeviceIoControlFile(handle, 0, NULL, NULL, &statusBlock, IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0, (PVOID)&diskGeometry, sizeof(diskGeometry)); if (NT_SUCCESS(sc)) { LARGE_INTEGER sizeInBytes; ULONG cylinderBytes; cylinderBytes = diskGeometry.TracksPerCylinder * diskGeometry.SectorsPerTrack * diskGeometry.BytesPerSector; sizeInBytes.QuadPart = diskGeometry.Cylinders.QuadPart * cylinderBytes; // Now convert everything to KB sizeInBytes.QuadPart = sizeInBytes.QuadPart >> 10; *Size = (ULONG) sizeInBytes.LowPart; } } DmClose(handle); sc = OK_STATUS; break; } else { if (firstTime) { firstTime = FALSE; } else { break; } Sleep(SLEEP_TIME); } } return EC(sc); } ULONG GetVolumeLabel( IN ULONG Disk, IN ULONG Partition, OUT PWSTR *Label ) /*++ Routine Description: Given a disk number and a partition number return the volume label (if any). Arguments: Disk - the disk number Partition - the partition number Label - a pointer to a pointer for a WCHAR string to contain the label Return Value: OK_STATUS - everything was performed. !OK_STATUS - the error code that was returned in the process of performing this work. --*/ { IO_STATUS_BLOCK statusBlock; HANDLE handle; unsigned char buffer[256]; PWSTR label; ULONG length; STATUS_CODE sc; BOOLEAN firstTime = TRUE; PFILE_FS_VOLUME_INFORMATION labelInfo = (PFILE_FS_VOLUME_INFORMATION)buffer; while (1) { sc = LowOpenPartition(GetDiskName(Disk), Partition, &handle); if (sc == OK_STATUS) { sc = NtQueryVolumeInformationFile(handle, &statusBlock, buffer, sizeof(buffer), FileFsVolumeInformation); DmClose(handle); if (sc == OK_STATUS) { length = labelInfo->VolumeLabelLength; labelInfo->VolumeLabel[length/sizeof(WCHAR)] = 0; length = (length+1) * sizeof(WCHAR); label = Malloc(length); RtlMoveMemory(label, labelInfo->VolumeLabel, length); } else { label = Malloc(sizeof(WCHAR)); sc = OK_STATUS; *label = 0; } *Label = label; break; } else { if (firstTime) { firstTime = FALSE; } else { *Label = NULL; break; } Sleep(SLEEP_TIME); } } return EC(sc); } ULONG GetTypeName( IN ULONG Disk, IN ULONG Partition, OUT PWSTR *Name ) /*++ Routine Description: Given a disk number and partition number return the file system type string. Arguments: Disk - the disk number Partition - the partition number Name - a pointer to a pointer for a WCHAR string to contain the file system type. Return Value: OK_STATUS - everything was performed. !OK_STATUS - the error code that was returned in the process of performing this work. --*/ { PWSTR name; STATUS_CODE sc; HANDLE handle; unsigned char buffer[256]; IO_STATUS_BLOCK statusBlock; ULONG length; BOOLEAN firstTime = TRUE; PFILE_FS_ATTRIBUTE_INFORMATION info = (PFILE_FS_ATTRIBUTE_INFORMATION)buffer; // For some reason, the file systems believe they are locked or need // to be verified after formats and the like. Therefore this is attempted // twice before it actually gives up. while (1) { sc = LowOpenPartition(GetDiskName(Disk), Partition, &handle); if (sc == OK_STATUS) { sc = NtQueryVolumeInformationFile(handle, &statusBlock, buffer, sizeof(buffer), FileFsAttributeInformation); DmClose(handle); if (sc == OK_STATUS) { length = info->FileSystemNameLength; info->FileSystemName[length/sizeof(WCHAR)] = 0; length = (length+1)*sizeof(WCHAR); name = Malloc(length); RtlMoveMemory(name, info->FileSystemName, length); } else { name = Malloc(sizeof(WCHAR)); *name = 0; sc = OK_STATUS; } *Name = name; break; } else { if (firstTime) { firstTime = FALSE; } else { break; } Sleep(SLEEP_TIME); } } return EC(sc); } BOOLEAN IsRemovable( IN ULONG DiskNumber ) /*++ Routine Description: This function determines whether the specified physical disk is removable. Arguments: DiskNumber -- The Physical Disk Number of the disk in question. Return Value: TRUE if the disk is removable. --*/ { STATUS_CODE sc; NTSTATUS status; HANDLE handle; DISK_GEOMETRY diskGeometry; IO_STATUS_BLOCK statusBlock; PCHAR name; name = GetDiskName(DiskNumber); sc = LowOpenDisk(name, &handle); if (sc == OK_STATUS) { status = NtDeviceIoControlFile(handle, 0, NULL, NULL, &statusBlock, IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0, (PVOID)&diskGeometry, sizeof(diskGeometry)); LowCloseDisk(handle); if (NT_SUCCESS(status)) { if (diskGeometry.MediaType == RemovableMedia) { char ntDeviceName[100]; // Do a dismount/force mount sequence to make sure // the media hasn't changed since last mount. // Dismount partition 1 by lock/unlock/close. sprintf(ntDeviceName, "%s\\Partition1", name); status= LowOpenNtName(ntDeviceName, &handle); if (NT_SUCCESS(status)) { LowLockDrive(handle); LowUnlockDrive(handle); LowCloseDisk(handle); // Now force the mount by opening the device with a '\' // This is done on partition 1 of the device. sprintf(ntDeviceName, "%s\\Partition1\\", name); status= LowOpenNtName(ntDeviceName, &handle); if (NT_SUCCESS(status)) { LowCloseDisk(handle); } } return TRUE; } } } return FALSE; } ULONG GetDriveLetterLinkTarget( IN PWSTR SourceNameStr, OUT PWSTR *LinkTarget ) /*++ Routine Description: Arguments: Return Value: --*/ { static WCHAR targetNameBuffer[50]; UNICODE_STRING sourceName, targetName; NTSTATUS status; OBJECT_ATTRIBUTES attributes; HANDLE handle; RtlInitUnicodeString(&sourceName, SourceNameStr); InitializeObjectAttributes(&attributes, &sourceName, OBJ_CASE_INSENSITIVE, NULL, NULL); status = NtOpenSymbolicLinkObject(&handle, READ_CONTROL | SYMBOLIC_LINK_QUERY, &attributes); if (NT_SUCCESS(status)) { RtlZeroMemory(targetNameBuffer, 50 * sizeof(WCHAR)); targetName.Buffer = targetNameBuffer; targetName.MaximumLength = sizeof(targetNameBuffer); status = NtQuerySymbolicLinkObject(handle, &targetName, NULL); NtClose(handle); } if (NT_SUCCESS(status)) { *LinkTarget = targetName.Buffer; } else { *LinkTarget = NULL; } return EC(status); } #include "bootmbr.h" #if X86BOOTCODE_SIZE < MBOOT_CODE_SIZE #error Something is wrong with the boot code (it's too small)! #endif ULONG MasterBootCode( IN ULONG Disk, IN ULONG Signature, IN BOOLEAN SetBootCode, IN BOOLEAN SetSignature ) /*++ Routine Description: If the zero sector of the disk does not have a valid MBR signature (i.e. AA55), update it such that it has a valid MBR and fill in the disk signature and bootcode in the process. Arguments: Disk - the disk ordinal to be affected SetSignature - if TRUE update the disk signature Signature - the disk signature for the update Return Value: status --*/ { HANDLE handle; STATUS_CODE status; PUCHAR unalignedSectorBuffer, sectorBuffer; ULONG bps, dummy, i; BOOLEAN writeIt; PCHAR diskName = GetDiskName(Disk); #ifndef max #define max(a,b) ((a > b) ? a : b) #endif if (SetBootCode) { writeIt = FALSE; // allocate sector buffer status = LowGetDriveGeometry(diskName, &dummy, &bps, &dummy, &dummy); if (status != OK_STATUS) { return EC(status); } if (bps < 512) { bps = 512; } unalignedSectorBuffer = Malloc(2*bps); sectorBuffer = (PUCHAR)(((ULONG)unalignedSectorBuffer+bps) & ~(bps-1)); // open entire disk (partition 0) if ((status = LowOpenDisk(diskName, &handle)) != OK_STATUS) { return EC(status); } // read (at least) first 512 bytes status = LowReadSectors(handle, bps, 0, 1, sectorBuffer); if (status == OK_STATUS) { if ((sectorBuffer[MBOOT_SIG_OFFSET+0] != MBOOT_SIG1) || (sectorBuffer[MBOOT_SIG_OFFSET+1] != MBOOT_SIG2)) { // xfer boot code into sectorBuffer for (i=0; iSignature = Signature; LowSetDiskLayout(diskName, layout); } } return EC(status); } ULONG UpdateMasterBootCode( IN ULONG Disk ) /*++ Routine Description: This routine updates the zero sector of the disk to insure that boot code is present. Arguments: Disk - the disk number onto which to put the boot code. Return Value: status --*/ { HANDLE handle; STATUS_CODE status; PUCHAR unalignedSectorBuffer, sectorBuffer; ULONG bps, dummy, i; PCHAR diskName = GetDiskName(Disk); #ifndef max #define max(a,b) ((a > b) ? a : b) #endif // allocate sector buffer status = LowGetDriveGeometry(diskName, &dummy, &bps, &dummy, &dummy); if (status != OK_STATUS) { return EC(status); } if (bps < 512) { bps = 512; } unalignedSectorBuffer = Malloc(2*bps); sectorBuffer = (PUCHAR)(((ULONG)unalignedSectorBuffer+bps) & ~(bps-1)); // open entire disk (partition 0) if ((status = LowOpenDisk(diskName, &handle)) != OK_STATUS) { return EC(status); } // read (at least) first 512 bytes status = LowReadSectors(handle, bps, 0, 1, sectorBuffer); if (status == OK_STATUS) { // xfer boot code into sectorBuffer. This avoids changing the // disk signature and the partition table information. for (i=0; iPageFileName, TRUE); // Since the format of the pagefile name generally // looks something like "\DosDevices\h:\pagefile.sys", // just use the first character before the colon // and assume that's the drive letter. p = strchr(_strlwr(ansiPageFileName.Buffer), ':'); if ((p-- != NULL) && (*p >= 'a') && (*p <= 'z')) { pageFileListEntry = Malloc(sizeof(PAGEFILE_LOCATION)); if (pageFileListEntry) { if (PagefileHead) { pageFileListEntry->Next = PagefileHead; } else { PagefileHead = pageFileListEntry; pageFileListEntry->Next = NULL; } pageFileListEntry->DriveLetter = *p; } } RtlFreeAnsiString(&ansiPageFileName); if (pageFileInfo->NextEntryOffset == 0) { break; } pageFileInfo = (PSYSTEM_PAGEFILE_INFORMATION)((PCHAR) pageFileInfo + pageFileInfo->NextEntryOffset); } } BOOLEAN IsPagefileOnDrive( CHAR DriveLetter ) /*++ Routine Description: Walk the page file list and determine if the drive letter given has a paging file. NOTE: The assumption is that drive letters that contain paging files can never get changed during the execution of Disk Administrator. Therefore this list is never updated, but can be used during the execution of Disk Administrator. Arguments: DriveLetter - the drive in question. Return Value: TRUE if this drive contains a page file. --*/ { PPAGEFILE_LOCATION pageFileListEntry = PagefileHead; while (pageFileListEntry) { if (pageFileListEntry->DriveLetter == DriveLetter) { return TRUE; } pageFileListEntry = pageFileListEntry->Next; } return FALSE; }