diff --git a/base/setup/lib/CMakeLists.txt b/base/setup/lib/CMakeLists.txt index 57eae6b3a6e..836c9ef5967 100644 --- a/base/setup/lib/CMakeLists.txt +++ b/base/setup/lib/CMakeLists.txt @@ -21,6 +21,7 @@ list(APPEND SOURCE utils/partinfo.c utils/partlist.c utils/regutil.c + utils/volutil.c bootcode.c bootsup.c fsutil.c diff --git a/base/setup/lib/fsutil.c b/base/setup/lib/fsutil.c index c4a1f02fa16..875e8ec7f9d 100644 --- a/base/setup/lib/fsutil.c +++ b/base/setup/lib/fsutil.c @@ -753,6 +753,30 @@ Quit: // Formatting routines // +NTSTATUS +ChkdskVolume( + _In_ PVOLINFO Volume, + _In_ BOOLEAN FixErrors, + _In_ BOOLEAN Verbose, + _In_ BOOLEAN CheckOnlyIfDirty, + _In_ BOOLEAN ScanDrive, + _In_opt_ PFMIFSCALLBACK Callback) +{ + /* Do not check a volume with an unknown file system */ + if (!*Volume->FileSystem) + return STATUS_UNRECOGNIZED_VOLUME; + + /* Check the volume */ + DPRINT("Volume->DeviceName: %S\n", Volume->DeviceName); + return ChkdskFileSystem(Volume->DeviceName, + Volume->FileSystem, + FixErrors, + Verbose, + CheckOnlyIfDirty, + ScanDrive, + Callback); +} + NTSTATUS ChkdskPartition( _In_ PPARTENTRY PartEntry, @@ -762,31 +786,56 @@ ChkdskPartition( _In_ BOOLEAN ScanDrive, _In_opt_ PFMIFSCALLBACK Callback) { - PDISKENTRY DiskEntry = PartEntry->DiskEntry; - // UNICODE_STRING PartitionRootPath; - WCHAR PartitionRootPath[MAX_PATH]; // PathBuffer - ASSERT(PartEntry->IsPartitioned && PartEntry->PartitionNumber != 0); + ASSERT(PartEntry->Volume); - /* Do not check a partition with an unknown file system */ - if (!*PartEntry->FileSystem) - return STATUS_UNRECOGNIZED_VOLUME; // STATUS_NOT_SUPPORTED; + // if (!PartEntry->Volume) { check_raw_sectors(); } else { check_FS(); } - /* Set PartitionRootPath */ - RtlStringCchPrintfW(PartitionRootPath, ARRAYSIZE(PartitionRootPath), - L"\\Device\\Harddisk%lu\\Partition%lu", - DiskEntry->DiskNumber, - PartEntry->PartitionNumber); - DPRINT("PartitionRootPath: %S\n", PartitionRootPath); + /* Check the associated volume */ + return ChkdskVolume(&PartEntry->Volume->Info, + FixErrors, + Verbose, + CheckOnlyIfDirty, + ScanDrive, + Callback); +} - /* Check the partition */ - return ChkdskFileSystem(PartitionRootPath, - PartEntry->FileSystem, - FixErrors, - Verbose, - CheckOnlyIfDirty, - ScanDrive, - Callback); +NTSTATUS +FormatVolume( + _In_ PVOLINFO Volume, + _In_ PCWSTR FileSystemName, + _In_ FMIFS_MEDIA_FLAG MediaFlag, + _In_opt_ PCWSTR Label, + _In_ BOOLEAN QuickFormat, + _In_ ULONG ClusterSize, + _In_opt_ PFMIFSCALLBACK Callback) +{ + NTSTATUS Status; + + if (!FileSystemName || !*FileSystemName) + { + DPRINT1("No file system specified\n"); + return STATUS_UNRECOGNIZED_VOLUME; + } + + /* Format the volume */ + DPRINT("Volume->DeviceName: %S\n", Volume->DeviceName); + Status = FormatFileSystem(Volume->DeviceName, + FileSystemName, + MediaFlag, + Label, + QuickFormat, + ClusterSize, + Callback); + if (!NT_SUCCESS(Status)) + return Status; + + /* Set the new volume's file system and label */ + RtlStringCbCopyW(Volume->FileSystem, sizeof(Volume->FileSystem), FileSystemName); + if (!Label) Label = L""; + RtlStringCbCopyW(Volume->VolumeLabel, sizeof(Volume->VolumeLabel), Label); + + return STATUS_SUCCESS; } NTSTATUS @@ -802,14 +851,12 @@ FormatPartition( NTSTATUS Status; PDISKENTRY DiskEntry = PartEntry->DiskEntry; UCHAR PartitionType; - // UNICODE_STRING PartitionRootPath; - WCHAR PartitionRootPath[MAX_PATH]; // PathBuffer ASSERT(PartEntry->IsPartitioned && PartEntry->PartitionNumber != 0); if (!FileSystemName || !*FileSystemName) { - DPRINT1("No file system specified?\n"); + DPRINT1("No file system specified\n"); return STATUS_UNRECOGNIZED_VOLUME; } @@ -867,37 +914,22 @@ FormatPartition( return STATUS_PARTITION_FAILURE; } - /* Set PartitionRootPath */ - RtlStringCchPrintfW(PartitionRootPath, ARRAYSIZE(PartitionRootPath), - L"\\Device\\Harddisk%lu\\Partition%lu", - DiskEntry->DiskNumber, - PartEntry->PartitionNumber); - DPRINT("PartitionRootPath: %S\n", PartitionRootPath); + /* We must have an associated volume now */ + ASSERT(PartEntry->Volume); - /* Format the partition */ - Status = FormatFileSystem(PartitionRootPath, - FileSystemName, - MediaFlag, - Label, - QuickFormat, - ClusterSize, - Callback); + /* Format the associated volume */ + Status = FormatVolume(&PartEntry->Volume->Info, + FileSystemName, + MediaFlag, + Label, + QuickFormat, + ClusterSize, + Callback); if (!NT_SUCCESS(Status)) return Status; -// -// TODO: Here, call a partlist.c function that update the actual -// FS name and the label fields of the volume. -// - PartEntry->FormatState = Formatted; - - /* Set the new partition's file system proper */ - RtlStringCbCopyW(PartEntry->FileSystem, - sizeof(PartEntry->FileSystem), - FileSystemName); - - PartEntry->New = FALSE; - + PartEntry->Volume->FormatState = Formatted; + PartEntry->Volume->New = FALSE; return STATUS_SUCCESS; } @@ -908,17 +940,19 @@ FormatPartition( static FSVOL_OP DoFormatting( - _In_ PPARTENTRY PartEntry, + _In_ PVOLENTRY Volume, _In_opt_ PVOID Context, _In_opt_ PFSVOL_CALLBACK FsVolCallback) { FSVOL_OP Result; NTSTATUS Status = STATUS_SUCCESS; + PPARTENTRY PartEntry; FORMAT_VOLUME_INFO FmtInfo = {0}; - ASSERT(PartEntry->IsPartitioned && PartEntry->PartitionNumber != 0); + PartEntry = Volume->PartEntry; + ASSERT(PartEntry && (PartEntry->Volume == Volume)); - FmtInfo.PartEntry = PartEntry; + FmtInfo.Volume = Volume; RetryFormat: Result = FsVolCallback(Context, @@ -964,7 +998,7 @@ EndFormat: static FSVOL_OP DoChecking( - _In_ PPARTENTRY PartEntry, + _In_ PVOLENTRY Volume, _In_opt_ PVOID Context, _In_opt_ PFSVOL_CALLBACK FsVolCallback) { @@ -972,11 +1006,9 @@ DoChecking( NTSTATUS Status = STATUS_SUCCESS; CHECK_VOLUME_INFO ChkInfo = {0}; - ASSERT(PartEntry->IsPartitioned && PartEntry->PartitionNumber != 0); + ASSERT(*Volume->Info.FileSystem); - ASSERT(*PartEntry->FileSystem); - - ChkInfo.PartEntry = PartEntry; + ChkInfo.Volume = Volume; RetryCheck: Result = FsVolCallback(Context, @@ -986,18 +1018,18 @@ RetryCheck: if (Result != FSVOL_DOIT) goto EndCheck; - /* Check the partition */ - Status = ChkdskPartition(PartEntry, - ChkInfo.FixErrors, - ChkInfo.Verbose, - ChkInfo.CheckOnlyIfDirty, - ChkInfo.ScanDrive, - ChkInfo.Callback); + /* Check the volume */ + Status = ChkdskVolume(&Volume->Info, + ChkInfo.FixErrors, + ChkInfo.Verbose, + ChkInfo.CheckOnlyIfDirty, + ChkInfo.ScanDrive, + ChkInfo.Callback); /* If volume checking succeeded, or if it is not supported * with the current file system, disable checks on the volume */ if (NT_SUCCESS(Status) || (Status == STATUS_NOT_SUPPORTED)) - PartEntry->NeedsCheck = FALSE; + Volume->NeedsCheck = FALSE; if (!NT_SUCCESS(Status)) { @@ -1012,7 +1044,7 @@ RetryCheck: goto RetryCheck; // else if (Result == FSVOL_ABORT || Result == FSVOL_SKIP), stop. - // PartEntry->NeedsCheck = FALSE; + // Volume->NeedsCheck = FALSE; } EndCheck: @@ -1025,125 +1057,44 @@ EndCheck: return Result; } -static BOOLEAN -GetNextUnformattedPartition( - IN PPARTLIST List, - OUT PPARTENTRY *pPartEntry) +static +PVOLENTRY +GetNextUnformattedVolume( + _In_ PPARTLIST List, + _In_opt_ PVOLENTRY Volume) { - PLIST_ENTRY Entry1, Entry2; - PDISKENTRY DiskEntry; - PPARTENTRY PartEntry; + PLIST_ENTRY Entry; - for (Entry1 = List->DiskListHead.Flink; - Entry1 != &List->DiskListHead; - Entry1 = Entry1->Flink) + for (;;) { - DiskEntry = CONTAINING_RECORD(Entry1, - DISKENTRY, - ListEntry); + /* If we have a current volume, get the next one, otherwise get the first */ + Entry = (Volume ? &Volume->ListEntry : &List->VolumesList); + Entry = Entry->Flink; - if (DiskEntry->DiskStyle == PARTITION_STYLE_GPT) - { - DPRINT("GPT-partitioned disk detected, not currently supported by SETUP!\n"); - continue; - } + if (Entry == &List->VolumesList) + return NULL; - for (Entry2 = DiskEntry->PrimaryPartListHead.Flink; - Entry2 != &DiskEntry->PrimaryPartListHead; - Entry2 = Entry2->Flink) + Volume = CONTAINING_RECORD(Entry, VOLENTRY, ListEntry); + if (Volume->New && (Volume->FormatState == Unformatted)) { - PartEntry = CONTAINING_RECORD(Entry2, PARTENTRY, ListEntry); - if (PartEntry->IsPartitioned && PartEntry->New) - { - ASSERT(DiskEntry == PartEntry->DiskEntry); - *pPartEntry = PartEntry; - return TRUE; - } - } - - for (Entry2 = DiskEntry->LogicalPartListHead.Flink; - Entry2 != &DiskEntry->LogicalPartListHead; - Entry2 = Entry2->Flink) - { - PartEntry = CONTAINING_RECORD(Entry2, PARTENTRY, ListEntry); - if (PartEntry->IsPartitioned && PartEntry->New) - { - ASSERT(DiskEntry == PartEntry->DiskEntry); - *pPartEntry = PartEntry; - return TRUE; - } + /* Found a candidate, return it */ + return Volume; } } - - *pPartEntry = NULL; - return FALSE; -} - -static BOOLEAN -GetNextUncheckedPartition( - IN PPARTLIST List, - OUT PPARTENTRY *pPartEntry) -{ - PLIST_ENTRY Entry1, Entry2; - PDISKENTRY DiskEntry; - PPARTENTRY PartEntry; - - for (Entry1 = List->DiskListHead.Flink; - Entry1 != &List->DiskListHead; - Entry1 = Entry1->Flink) - { - DiskEntry = CONTAINING_RECORD(Entry1, - DISKENTRY, - ListEntry); - - if (DiskEntry->DiskStyle == PARTITION_STYLE_GPT) - { - DPRINT("GPT-partitioned disk detected, not currently supported by SETUP!\n"); - continue; - } - - for (Entry2 = DiskEntry->PrimaryPartListHead.Flink; - Entry2 != &DiskEntry->PrimaryPartListHead; - Entry2 = Entry2->Flink) - { - PartEntry = CONTAINING_RECORD(Entry2, PARTENTRY, ListEntry); - if (PartEntry->IsPartitioned && PartEntry->NeedsCheck) - { - ASSERT(DiskEntry == PartEntry->DiskEntry); - *pPartEntry = PartEntry; - return TRUE; - } - } - - for (Entry2 = DiskEntry->LogicalPartListHead.Flink; - Entry2 != &DiskEntry->LogicalPartListHead; - Entry2 = Entry2->Flink) - { - PartEntry = CONTAINING_RECORD(Entry2, PARTENTRY, ListEntry); - if (PartEntry->IsPartitioned && PartEntry->NeedsCheck) - { - ASSERT(DiskEntry == PartEntry->DiskEntry); - *pPartEntry = PartEntry; - return TRUE; - } - } - } - - *pPartEntry = NULL; - return FALSE; } BOOLEAN FsVolCommitOpsQueue( _In_ PPARTLIST PartitionList, - _In_ PPARTENTRY SystemPartition, - _In_ PPARTENTRY InstallPartition, + _In_ PVOLENTRY SystemVolume, + _In_ PVOLENTRY InstallVolume, _In_opt_ PFSVOL_CALLBACK FsVolCallback, _In_opt_ PVOID Context) { BOOLEAN Success = TRUE; // Suppose success originally. FSVOL_OP Result; - PPARTENTRY PartEntry; + PLIST_ENTRY Entry; + PVOLENTRY Volume; /* Machine state for the format step */ typedef enum _FORMATMACHINESTATE @@ -1163,7 +1114,7 @@ FsVolCommitOpsQueue( "FormatDone" }; - ASSERT(PartitionList && InstallPartition && SystemPartition); + ASSERT(PartitionList && SystemVolume && InstallVolume); /* Commit all partition changes to all the disks */ if (!WritePartitionsToDisk(PartitionList)) @@ -1184,8 +1135,8 @@ FsVolCommitOpsQueue( * we must perform a file system check of both the system and the * installation volumes. */ - SystemPartition->NeedsCheck = TRUE; - InstallPartition->NeedsCheck = TRUE; + SystemVolume->NeedsCheck = TRUE; + InstallVolume->NeedsCheck = TRUE; Result = FsVolCallback(Context, FSVOLNOTIFY_STARTQUEUE, @@ -1210,8 +1161,8 @@ FsVolCommitOpsQueue( /* Reset the formatter machine state */ FormatState = Start; + Volume = NULL; NextFormat: - PartEntry = NULL; OldFormatState = FormatState; switch (FormatState) { @@ -1223,12 +1174,11 @@ NextFormat: * volume. Otherwise we just require a file system check on it, * and start by formatting the installation volume instead. */ - ASSERT(SystemPartition->IsPartitioned); - if (SystemPartition != InstallPartition) + if (SystemVolume != InstallVolume) { - PartEntry = SystemPartition; + Volume = SystemVolume; - if (PartEntry->FormatState == Unformatted) + if (Volume->FormatState == Unformatted) { // TODO: Should we let the user use a custom file system, // or should we always use FAT(32) for it? @@ -1241,18 +1191,17 @@ NextFormat: } /* The system volume is separate, so it had better be formatted! */ - ASSERT((PartEntry->FormatState == Preformatted) || - (PartEntry->FormatState == Formatted)); + ASSERT(Volume->FormatState == Formatted); /* Require a file system check on the system volume too */ - PartEntry->NeedsCheck = TRUE; + Volume->NeedsCheck = TRUE; } __fallthrough; } case FormatSystemVolume: { - PartEntry = InstallPartition; + Volume = InstallVolume; FormatState = FormatInstallVolume; DPRINT1("FormatState: %s --> %s\n", @@ -1261,15 +1210,16 @@ NextFormat: } case FormatInstallVolume: + /* Restart volume enumeration */ + Volume = NULL; case FormatOtherVolume: { - BOOLEAN Found = GetNextUnformattedPartition(PartitionList, &PartEntry); - if (Found) ASSERT(PartEntry); + Volume = GetNextUnformattedVolume(PartitionList, Volume); - FormatState = (PartEntry ? FormatOtherVolume : FormatDone); + FormatState = (Volume ? FormatOtherVolume : FormatDone); DPRINT1("FormatState: %s --> %s\n", FormatStateNames[OldFormatState], FormatStateNames[FormatState]); - if (Found) + if (Volume) break; __fallthrough; } @@ -1283,15 +1233,14 @@ NextFormat: DEFAULT_UNREACHABLE; } - ASSERT(PartEntry); - Result = DoFormatting(PartEntry, Context, FsVolCallback); + Result = DoFormatting(Volume, Context, FsVolCallback); if (Result == FSVOL_ABORT) { Success = FALSE; goto Quit; } /* Schedule a check for this volume */ - PartEntry->NeedsCheck = TRUE; + Volume->NeedsCheck = TRUE; /* Go to the next volume to be formatted */ goto NextFormat; @@ -1314,24 +1263,27 @@ StartCheckQueue: if (Result == FSVOL_ABORT) return FALSE; -NextCheck: - if (!GetNextUncheckedPartition(PartitionList, &PartEntry)) + /* Loop through each unchecked volume and do the check */ + for (Entry = PartitionList->VolumesList.Flink; + Entry != &PartitionList->VolumesList; + Entry = Entry->Flink) { - Success = TRUE; - goto EndCheck; - } + Volume = CONTAINING_RECORD(Entry, VOLENTRY, ListEntry); + if (!Volume->NeedsCheck) + continue; - ASSERT(PartEntry); - Result = DoChecking(PartEntry, Context, FsVolCallback); - if (Result == FSVOL_ABORT) - { - Success = FALSE; - goto Quit; + /* Found a candidate */ + ASSERT(Volume->FormatState == Formatted); + Result = DoChecking(Volume, Context, FsVolCallback); + if (Result == FSVOL_ABORT) + { + Success = FALSE; + goto Quit; + } + /* Go to the next volume to be checked */ } - /* Go to the next volume to be checked */ - goto NextCheck; + Success = TRUE; -EndCheck: FsVolCallback(Context, FSVOLNOTIFY_ENDSUBQUEUE, FSVOL_CHECK, diff --git a/base/setup/lib/fsutil.h b/base/setup/lib/fsutil.h index b132148a1f2..5af0a4265f5 100644 --- a/base/setup/lib/fsutil.h +++ b/base/setup/lib/fsutil.h @@ -163,7 +163,7 @@ typedef enum _FSVOL_OP typedef struct _FORMAT_VOLUME_INFO { - PPARTENTRY PartEntry; + PVOLENTRY Volume; // PCWSTR NtPathPartition; NTSTATUS ErrorStatus; @@ -179,7 +179,7 @@ typedef struct _FORMAT_VOLUME_INFO typedef struct _CHECK_VOLUME_INFO { - PPARTENTRY PartEntry; + PVOLENTRY Volume; // PCWSTR NtPathPartition; NTSTATUS ErrorStatus; @@ -202,8 +202,8 @@ typedef FSVOL_OP BOOLEAN FsVolCommitOpsQueue( _In_ PPARTLIST PartitionList, - _In_ PPARTENTRY SystemPartition, - _In_ PPARTENTRY InstallPartition, + _In_ PVOLENTRY SystemVolume, + _In_ PVOLENTRY InstallVolume, _In_opt_ PFSVOL_CALLBACK FsVolCallback, _In_opt_ PVOID Context); diff --git a/base/setup/lib/setuplib.c b/base/setup/lib/setuplib.c index ea190cece3c..f8a961ab3c1 100644 --- a/base/setup/lib/setuplib.c +++ b/base/setup/lib/setuplib.c @@ -695,7 +695,8 @@ InitSystemPartition( * In all cases, whether or not we are going to perform a formatting, * we must perform a filesystem check of the system partition. */ - SystemPartition->NeedsCheck = TRUE; + if (SystemPartition->Volume) + SystemPartition->Volume->NeedsCheck = TRUE; return TRUE; } @@ -794,23 +795,21 @@ IsValidInstallDirectory( NTSTATUS InitDestinationPaths( - IN OUT PUSETUP_DATA pSetupData, - IN PCWSTR InstallationDir, - IN PPARTENTRY PartEntry) // FIXME: HACK! + _Inout_ PUSETUP_DATA pSetupData, + _In_ PCWSTR InstallationDir, + _In_ PVOLENTRY Volume) { NTSTATUS Status; + PPARTENTRY PartEntry = Volume->PartEntry; PDISKENTRY DiskEntry = PartEntry->DiskEntry; - WCHAR PathBuffer[MAX_PATH]; + WCHAR PathBuffer[RTL_NUMBER_OF_FIELD(VOLINFO, DeviceName) + 1]; ASSERT(PartEntry->IsPartitioned && PartEntry->PartitionNumber != 0); /* Create 'pSetupData->DestinationRootPath' string */ RtlFreeUnicodeString(&pSetupData->DestinationRootPath); - Status = RtlStringCchPrintfW(PathBuffer, ARRAYSIZE(PathBuffer), - L"\\Device\\Harddisk%lu\\Partition%lu\\", - DiskEntry->DiskNumber, - PartEntry->PartitionNumber); - + Status = RtlStringCchPrintfW(PathBuffer, _countof(PathBuffer), + L"%s\\", Volume->Info.DeviceName); if (!NT_SUCCESS(Status)) { DPRINT1("RtlStringCchPrintfW() failed with status 0x%08lx\n", Status); diff --git a/base/setup/lib/setuplib.h b/base/setup/lib/setuplib.h index 99c02ba86a1..02abdedc658 100644 --- a/base/setup/lib/setuplib.h +++ b/base/setup/lib/setuplib.h @@ -204,9 +204,9 @@ IsValidInstallDirectory( NTSTATUS InitDestinationPaths( - IN OUT PUSETUP_DATA pSetupData, - IN PCWSTR InstallationDir, - IN PPARTENTRY PartEntry); // FIXME: HACK! + _Inout_ PUSETUP_DATA pSetupData, + _In_ PCWSTR InstallationDir, + _In_ PVOLENTRY Volume); // NTSTATUS ERROR_NUMBER diff --git a/base/setup/lib/utils/osdetect.c b/base/setup/lib/utils/osdetect.c index 2512cfbe26f..1a4abd5cb6a 100644 --- a/base/setup/lib/utils/osdetect.c +++ b/base/setup/lib/utils/osdetect.c @@ -1,9 +1,9 @@ /* * PROJECT: ReactOS Setup Library - * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+) + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) * PURPOSE: NT 5.x family (MS Windows <= 2003, and ReactOS) * operating systems detection code. - * COPYRIGHT: Copyright 2017-2018 Hermes Belusca-Maito + * COPYRIGHT: Copyright 2017-2024 Hermès Bélusca-Maïto */ /* INCLUDES *****************************************************************/ @@ -46,22 +46,20 @@ FindExistingNTOSInstall( static PNTOS_INSTALLATION AddNTOSInstallation( - IN PGENERIC_LIST List, - IN PCWSTR InstallationName, - IN USHORT Machine, - IN PCWSTR VendorName, - IN PCWSTR SystemRootArcPath, - IN PUNICODE_STRING SystemRootNtPath, // or PCWSTR ? - IN PCWSTR PathComponent, // Pointer inside SystemRootNtPath buffer - IN ULONG DiskNumber, - IN ULONG PartitionNumber, - IN PPARTENTRY PartEntry OPTIONAL); + _In_ PGENERIC_LIST List, + _In_ PCWSTR InstallationName, + _In_ USHORT Machine, + _In_ PCWSTR VendorName, + _In_ PCWSTR SystemRootArcPath, + _In_ PUNICODE_STRING SystemRootNtPath, // or PCWSTR ? + _In_ PCWSTR PathComponent, // Pointer inside SystemRootNtPath buffer + _In_ ULONG DiskNumber, + _In_ ULONG PartitionNumber); typedef struct _ENUM_INSTALLS_DATA { - IN OUT PGENERIC_LIST List; - IN PPARTLIST PartList; - // IN PPARTENTRY PartEntry; + _Inout_ PGENERIC_LIST List; + _In_ PPARTLIST PartList; } ENUM_INSTALLS_DATA, *PENUM_INSTALLS_DATA; // PENUM_BOOT_ENTRIES_ROUTINE @@ -78,7 +76,6 @@ EnumerateInstallations( ULONG DiskNumber = 0, PartitionNumber = 0; PCWSTR PathComponent = NULL; - PPARTENTRY PartEntry = NULL; UNICODE_STRING SystemRootPath; WCHAR SystemRoot[MAX_PATH]; @@ -136,9 +133,9 @@ EnumerateInstallations( } /* - * Convert the ARC path into an NT path, from which we will deduce - * the real disk drive & partition on which the candidate installation - * resides, as well verifying whether it is indeed an NTOS installation. + * Convert the ARC path into an NT path, from which we will deduce the + * real disk & partition on which the candidate installation resides, + * as well as verifying whether it is indeed an NTOS installation. */ RtlInitEmptyUnicodeString(&SystemRootPath, SystemRoot, sizeof(SystemRoot)); if (!ArcPathToNtPath(&SystemRootPath, Options->OsLoadPath, Data->PartList)) @@ -182,14 +179,6 @@ EnumerateInstallations( { DPRINT("SystemRootPath = '%wZ' points to disk #%d, partition #%d, path '%S'\n", &SystemRootPath, DiskNumber, PartitionNumber, PathComponent); - - /* Retrieve the corresponding partition */ - PartEntry = SelectPartition(Data->PartList, DiskNumber, PartitionNumber); - if (!PartEntry) - { - DPRINT1("SelectPartition(disk #%d, partition #%d) failed\n", - DiskNumber, PartitionNumber); - } } else { @@ -197,13 +186,24 @@ EnumerateInstallations( } /* Add the discovered NTOS installation into the list */ - AddNTOSInstallation(Data->List, - BootEntry->FriendlyName, - Machine, - VendorName.Buffer, // FIXME: What if it's not NULL-terminated? - Options->OsLoadPath, - &SystemRootPath, PathComponent, - DiskNumber, PartitionNumber, PartEntry); + NtOsInstall = AddNTOSInstallation(Data->List, + BootEntry->FriendlyName, + Machine, + VendorName.Buffer, // FIXME: What if it's not NULL-terminated? + Options->OsLoadPath, + &SystemRootPath, PathComponent, + DiskNumber, PartitionNumber); + if (NtOsInstall) + { + /* Retrieve the volume corresponding to the disk and partition numbers */ + PPARTENTRY PartEntry = SelectPartition(Data->PartList, DiskNumber, PartitionNumber); + if (!PartEntry) + { + DPRINT1("SelectPartition(disk #%d, partition #%d) failed\n", + DiskNumber, PartitionNumber); + } + NtOsInstall->Volume = (PartEntry ? PartEntry->Volume : NULL); + } /* Continue the enumeration */ return STATUS_SUCCESS; @@ -607,16 +607,15 @@ FindExistingNTOSInstall( static PNTOS_INSTALLATION AddNTOSInstallation( - IN PGENERIC_LIST List, - IN PCWSTR InstallationName, - IN USHORT Machine, - IN PCWSTR VendorName, - IN PCWSTR SystemRootArcPath, - IN PUNICODE_STRING SystemRootNtPath, // or PCWSTR ? - IN PCWSTR PathComponent, // Pointer inside SystemRootNtPath buffer - IN ULONG DiskNumber, - IN ULONG PartitionNumber, - IN PPARTENTRY PartEntry OPTIONAL) + _In_ PGENERIC_LIST List, + _In_ PCWSTR InstallationName, + _In_ USHORT Machine, + _In_ PCWSTR VendorName, + _In_ PCWSTR SystemRootArcPath, + _In_ PUNICODE_STRING SystemRootNtPath, // or PCWSTR ? + _In_ PCWSTR PathComponent, // Pointer inside SystemRootNtPath buffer + _In_ ULONG DiskNumber, + _In_ ULONG PartitionNumber) { PNTOS_INSTALLATION NtOsInstall; SIZE_T ArcPathLength, NtPathLength; @@ -648,8 +647,6 @@ AddNTOSInstallation( NtOsInstall->DiskNumber = DiskNumber; NtOsInstall->PartitionNumber = PartitionNumber; - NtOsInstall->PartEntry = PartEntry; - NtOsInstall->Machine = Machine; RtlInitEmptyUnicodeString(&NtOsInstall->SystemArcPath, @@ -680,39 +677,34 @@ AddNTOSInstallation( static VOID FindNTOSInstallations( - IN OUT PGENERIC_LIST List, - IN PPARTLIST PartList, - IN PPARTENTRY PartEntry) + _Inout_ PGENERIC_LIST List, + _In_ PPARTLIST PartList, + _In_ PVOLENTRY Volume) { NTSTATUS Status; - ULONG DiskNumber = PartEntry->DiskEntry->DiskNumber; - ULONG PartitionNumber = PartEntry->PartitionNumber; - HANDLE PartitionDirectoryHandle; + HANDLE VolumeRootDirHandle; OBJECT_ATTRIBUTES ObjectAttributes; IO_STATUS_BLOCK IoStatusBlock; - UNICODE_STRING PartitionRootPath; + UNICODE_STRING VolumeRootPath; BOOT_STORE_TYPE Type; PVOID BootStoreHandle; ENUM_INSTALLS_DATA Data; ULONG Version; - WCHAR PathBuffer[MAX_PATH]; + WCHAR PathBuffer[RTL_NUMBER_OF_FIELD(VOLINFO, DeviceName) + 1]; - ASSERT(PartEntry->IsPartitioned && PartEntry->PartitionNumber != 0); + /* Set VolumeRootPath */ + RtlStringCchPrintfW(PathBuffer, _countof(PathBuffer), + L"%s\\", Volume->Info.DeviceName); + RtlInitUnicodeString(&VolumeRootPath, PathBuffer); + DPRINT("FindNTOSInstallations(%wZ)\n", &VolumeRootPath); - /* Set PartitionRootPath */ - RtlStringCchPrintfW(PathBuffer, ARRAYSIZE(PathBuffer), - L"\\Device\\Harddisk%lu\\Partition%lu\\", - DiskNumber, PartitionNumber); - RtlInitUnicodeString(&PartitionRootPath, PathBuffer); - DPRINT("FindNTOSInstallations: PartitionRootPath: '%wZ'\n", &PartitionRootPath); - - /* Open the partition */ + /* Open the volume */ InitializeObjectAttributes(&ObjectAttributes, - &PartitionRootPath, + &VolumeRootPath, OBJ_CASE_INSENSITIVE, NULL, NULL); - Status = NtOpenFile(&PartitionDirectoryHandle, + Status = NtOpenFile(&VolumeRootDirHandle, FILE_LIST_DIRECTORY | FILE_TRAVERSE | SYNCHRONIZE, &ObjectAttributes, &IoStatusBlock, @@ -720,7 +712,7 @@ FindNTOSInstallations( FILE_SYNCHRONOUS_IO_NONALERT | FILE_DIRECTORY_FILE); if (!NT_SUCCESS(Status)) { - DPRINT1("Failed to open partition '%wZ', Status 0x%08lx\n", &PartitionRootPath, Status); + DPRINT1("Failed to open volume '%wZ', Status 0x%08lx\n", &VolumeRootPath, Status); return; } @@ -730,7 +722,7 @@ FindNTOSInstallations( /* Try to see whether we recognize some NT boot loaders */ for (Type = FreeLdr; Type < BldrTypeMax; ++Type) { - Status = FindBootStore(PartitionDirectoryHandle, Type, &Version); + Status = FindBootStore(VolumeRootDirHandle, Type, &Version); if (!NT_SUCCESS(Status)) { /* The loader does not exist, continue with another one */ @@ -740,10 +732,12 @@ FindNTOSInstallations( } /* The loader exists, try to enumerate its boot entries */ - DPRINT("Analyze the OS installations for loader type '%d' in disk #%d, partition #%d\n", - Type, DiskNumber, PartitionNumber); + DPRINT("Analyze the OS installations for loader type '%d' in Volume %wZ (Disk #%d, Partition #%d)\n", + Type, &VolumeRootPath, + Volume->PartEntry->DiskEntry->DiskNumber, + Volume->PartEntry->PartitionNumber); - Status = OpenBootStoreByHandle(&BootStoreHandle, PartitionDirectoryHandle, Type, + Status = OpenBootStoreByHandle(&BootStoreHandle, VolumeRootDirHandle, Type, BS_OpenExisting, BS_ReadAccess); if (!NT_SUCCESS(Status)) { @@ -755,90 +749,63 @@ FindNTOSInstallations( CloseBootStore(BootStoreHandle); } - /* Close the partition */ - NtClose(PartitionDirectoryHandle); -} - -// static -FORCEINLINE BOOLEAN -ShouldICheckThisPartition( - IN PPARTENTRY PartEntry) -{ - if (!PartEntry) - return FALSE; - - return PartEntry->IsPartitioned && - !IsContainerPartition(PartEntry->PartitionType) /* alternatively: PartEntry->PartitionNumber != 0 */ && - !PartEntry->New && - (PartEntry->FormatState == Preformatted /* || PartEntry->FormatState == Formatted */); + /* Close the volume */ + NtClose(VolumeRootDirHandle); } +/** + * @brief + * Create a list of available NT OS installations on the computer, + * by searching for recognized ones on each recognized storage volume. + **/ // EnumerateNTOSInstallations PGENERIC_LIST CreateNTOSInstallationsList( - IN PPARTLIST PartList) + _In_ PPARTLIST PartList) { PGENERIC_LIST List; - PLIST_ENTRY Entry, Entry2; - PDISKENTRY DiskEntry; - PPARTENTRY PartEntry; + PLIST_ENTRY Entry; + PVOLENTRY Volume; + BOOLEAN CheckVolume; List = CreateGenericList(); - if (List == NULL) + if (!List) return NULL; - /* Loop each available disk ... */ - Entry = PartList->DiskListHead.Flink; - while (Entry != &PartList->DiskListHead) + /* Loop each available volume */ + for (Entry = PartList->VolumesList.Flink; + Entry != &PartList->VolumesList; + Entry = Entry->Flink) { - DiskEntry = CONTAINING_RECORD(Entry, DISKENTRY, ListEntry); - Entry = Entry->Flink; + Volume = CONTAINING_RECORD(Entry, VOLENTRY, ListEntry); + /* Valid OS installations can be found only on basic volumes */ + if (!Volume->PartEntry) // TODO: In the future: (!Volume->IsSimpleVolume) + continue; - DPRINT("Disk #%d\n", DiskEntry->DiskNumber); + CheckVolume = (!Volume->New && (Volume->FormatState == Formatted)); - /* ... and for each disk, loop each available partition */ - - /* First, the primary partitions */ - Entry2 = DiskEntry->PrimaryPartListHead.Flink; - while (Entry2 != &DiskEntry->PrimaryPartListHead) +#ifndef NDEBUG { - PartEntry = CONTAINING_RECORD(Entry2, PARTENTRY, ListEntry); - Entry2 = Entry2->Flink; - - ASSERT(PartEntry->DiskEntry == DiskEntry); - - DPRINT(" Primary Partition #%d, index %d - Type 0x%02x, IsLogical = %s, IsPartitioned = %s, IsNew = %s, FormatState = %lu -- Should I check it? %s\n", - PartEntry->PartitionNumber, PartEntry->PartitionIndex, - PartEntry->PartitionType, PartEntry->LogicalPartition ? "TRUE" : "FALSE", - PartEntry->IsPartitioned ? "TRUE" : "FALSE", - PartEntry->New ? "Yes" : "No", - PartEntry->FormatState, - ShouldICheckThisPartition(PartEntry) ? "YES!" : "NO!"); - - if (ShouldICheckThisPartition(PartEntry)) - FindNTOSInstallations(List, PartList, PartEntry); + PPARTENTRY PartEntry = Volume->PartEntry; + ASSERT(PartEntry->Volume == Volume); + DPRINT("Volume %S (%c%c) on Disk #%d, Partition #%d (%s), " + "index %d - Type 0x%02x, IsVolNew = %s, FormatState = %lu -- Should I check it? %s\n", + Volume->Info.DeviceName, + !Volume->Info.DriveLetter ? '-' : (CHAR)Volume->Info.DriveLetter, + !Volume->Info.DriveLetter ? '-' : ':', + PartEntry->DiskEntry->DiskNumber, + PartEntry->PartitionNumber, + PartEntry->LogicalPartition ? "Logical" : "Primary", + PartEntry->PartitionIndex, + PartEntry->PartitionType, + Volume->New ? "Yes" : "No", + Volume->FormatState, + CheckVolume ? "YES!" : "NO!"); } +#endif - /* Then, the logical partitions (present in the extended partition) */ - Entry2 = DiskEntry->LogicalPartListHead.Flink; - while (Entry2 != &DiskEntry->LogicalPartListHead) - { - PartEntry = CONTAINING_RECORD(Entry2, PARTENTRY, ListEntry); - Entry2 = Entry2->Flink; - - ASSERT(PartEntry->DiskEntry == DiskEntry); - - DPRINT(" Logical Partition #%d, index %d - Type 0x%02x, IsLogical = %s, IsPartitioned = %s, IsNew = %s, FormatState = %lu -- Should I check it? %s\n", - PartEntry->PartitionNumber, PartEntry->PartitionIndex, - PartEntry->PartitionType, PartEntry->LogicalPartition ? "TRUE" : "FALSE", - PartEntry->IsPartitioned ? "TRUE" : "FALSE", - PartEntry->New ? "Yes" : "No", - PartEntry->FormatState, - ShouldICheckThisPartition(PartEntry) ? "YES!" : "NO!"); - - if (ShouldICheckThisPartition(PartEntry)) - FindNTOSInstallations(List, PartList, PartEntry); - } + if (CheckVolume) + FindNTOSInstallations(List, PartList, Volume); } #ifndef NDEBUG diff --git a/base/setup/lib/utils/osdetect.h b/base/setup/lib/utils/osdetect.h index e8b54496011..e329b1c32f4 100644 --- a/base/setup/lib/utils/osdetect.h +++ b/base/setup/lib/utils/osdetect.h @@ -1,9 +1,9 @@ /* * PROJECT: ReactOS Setup Library - * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+) + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) * PURPOSE: NT 5.x family (MS Windows <= 2003, and ReactOS) * operating systems detection code. - * COPYRIGHT: Copyright 2017-2018 Hermes Belusca-Maito + * COPYRIGHT: Copyright 2017-2024 Hermès Bélusca-Maïto */ #pragma once @@ -22,7 +22,7 @@ typedef struct _NTOS_INSTALLATION PCWSTR PathComponent; // Pointer inside SystemNtPath.Buffer ULONG DiskNumber; ULONG PartitionNumber; - PPARTENTRY PartEntry; + PVOLENTRY Volume; // PVOLINFO WCHAR InstallationName[MAX_PATH]; WCHAR VendorName[MAX_PATH]; // CHAR Data[ANYSIZE_ARRAY]; @@ -31,7 +31,7 @@ typedef struct _NTOS_INSTALLATION // EnumerateNTOSInstallations PGENERIC_LIST CreateNTOSInstallationsList( - IN PPARTLIST List); + _In_ PPARTLIST PartList); /* * FindSubStrI(PCWSTR str, PCWSTR strSearch) : diff --git a/base/setup/lib/utils/partlist.c b/base/setup/lib/utils/partlist.c index 914fe69ccb3..466a1826979 100644 --- a/base/setup/lib/utils/partlist.c +++ b/base/setup/lib/utils/partlist.c @@ -1,31 +1,31 @@ /* * PROJECT: ReactOS Setup Library - * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+) + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) * PURPOSE: Partition list functions * COPYRIGHT: Copyright 2003-2019 Casper S. Hornstrup (chorns@users.sourceforge.net) - * Copyright 2018-2019 Hermes Belusca-Maito + * Copyright 2018-2024 Hermès Bélusca-Maïto */ #include "precomp.h" #include #include "partlist.h" -#include "fsrec.h" +#include "volutil.h" +#include "fsrec.h" // For FileSystemToMBRPartitionType() + #include "registry.h" #define NDEBUG #include -//#define DUMP_PARTITION_TABLE +// #define DUMP_PARTITION_TABLE #include - typedef struct _REG_DISK_MOUNT_INFO { ULONG Signature; - LARGE_INTEGER StartingOffset; + ULONGLONG StartingOffset; } REG_DISK_MOUNT_INFO, *PREG_DISK_MOUNT_INFO; - #include @@ -159,22 +159,17 @@ AssignDriveLetters( { PartEntry = CONTAINING_RECORD(Entry2, PARTENTRY, ListEntry); - PartEntry->DriveLetter = 0; + if (!PartEntry->Volume) + continue; + PartEntry->Volume->Info.DriveLetter = UNICODE_NULL; if (PartEntry->IsPartitioned && - !IsContainerPartition(PartEntry->PartitionType)) + !IsContainerPartition(PartEntry->PartitionType) && + (IsRecognizedPartition(PartEntry->PartitionType) || + PartEntry->SectorCount.QuadPart != 0LL)) { - ASSERT(PartEntry->PartitionType != PARTITION_ENTRY_UNUSED); - - if (IsRecognizedPartition(PartEntry->PartitionType) || - PartEntry->SectorCount.QuadPart != 0LL) - { - if (Letter <= L'Z') - { - PartEntry->DriveLetter = Letter; - Letter++; - } - } + if (Letter <= L'Z') + PartEntry->Volume->Info.DriveLetter = Letter++; } } } @@ -192,21 +187,16 @@ AssignDriveLetters( { PartEntry = CONTAINING_RECORD(Entry2, PARTENTRY, ListEntry); - PartEntry->DriveLetter = 0; + if (!PartEntry->Volume) + continue; + PartEntry->Volume->Info.DriveLetter = UNICODE_NULL; - if (PartEntry->IsPartitioned) + if (PartEntry->IsPartitioned && + (IsRecognizedPartition(PartEntry->PartitionType) || + PartEntry->SectorCount.QuadPart != 0LL)) { - ASSERT(PartEntry->PartitionType != PARTITION_ENTRY_UNUSED); - - if (IsRecognizedPartition(PartEntry->PartitionType) || - PartEntry->SectorCount.QuadPart != 0LL) - { - if (Letter <= L'Z') - { - PartEntry->DriveLetter = Letter; - Letter++; - } - } + if (Letter <= L'Z') + PartEntry->Volume->Info.DriveLetter = Letter++; } } } @@ -564,8 +554,8 @@ IsSuperFloppy( /* - * Inserts the disk region represented by PartEntry into either the primary - * or the logical partition list of the given disk. + * Inserts the disk region represented by PartEntry into either + * the primary or the logical partition list of the given disk. * The lists are kept sorted by increasing order of start sectors. * Of course no disk region should overlap at all with one another. */ @@ -649,7 +639,7 @@ CreateInsertBlankRegion( NewPartEntry = RtlAllocateHeap(ProcessHeap, HEAP_ZERO_MEMORY, sizeof(PARTENTRY)); - if (NewPartEntry == NULL) + if (!NewPartEntry) return NULL; NewPartEntry->DiskEntry = DiskEntry; @@ -660,8 +650,7 @@ CreateInsertBlankRegion( NewPartEntry->LogicalPartition = LogicalSpace; NewPartEntry->IsPartitioned = FALSE; NewPartEntry->PartitionType = PARTITION_ENTRY_UNUSED; - NewPartEntry->FormatState = Unformatted; - NewPartEntry->FileSystem[0] = L'\0'; + NewPartEntry->Volume = NULL; DPRINT1("First Sector : %I64u\n", NewPartEntry->StartSector.QuadPart); DPRINT1("Last Sector : %I64u\n", NewPartEntry->StartSector.QuadPart + NewPartEntry->SectorCount.QuadPart - 1); @@ -673,6 +662,17 @@ CreateInsertBlankRegion( return NewPartEntry; } +static +VOID +DestroyRegion( + _Inout_ PPARTENTRY PartEntry) +{ + // RemoveEntryList(&PartEntry->Volume->ListEntry); + if (PartEntry->Volume) + RtlFreeHeap(ProcessHeap, 0, PartEntry->Volume); + RtlFreeHeap(ProcessHeap, 0, PartEntry); +} + static VOID AddLogicalDiskSpace( @@ -719,6 +719,7 @@ InitializePartitionEntry( /* The entry must not be already partitioned and not be void */ ASSERT(!PartEntry->IsPartitioned); ASSERT(PartEntry->SectorCount.QuadPart); + ASSERT(!PartEntry->Volume); /* Either we create a primary/logical partition, or we create an * extended partition but the entry must not be logical space */ @@ -780,7 +781,7 @@ InitializePartitionEntry( PartEntry->SectorCount.QuadPart = StartSector - PartEntry->StartSector.QuadPart; } - /* Convert the partition entry to 'New (Unformatted)' */ + /* Convert to a new partition entry */ PartEntry->New = TRUE; PartEntry->IsPartitioned = TRUE; @@ -819,9 +820,6 @@ InitializePartitionEntry( } ASSERT(PartEntry->PartitionType != PARTITION_ENTRY_UNUSED); - PartEntry->FormatState = Unformatted; - PartEntry->FileSystem[0] = L'\0'; - if (isContainer) { DiskEntry->ExtendedPartition = PartEntry; @@ -835,6 +833,90 @@ InitializePartitionEntry( return TRUE; } +static +VOID +InitPartitionDeviceName( + _Inout_ PPARTENTRY PartEntry) +{ + NTSTATUS Status; + + /* Ignore if this is a container partition */ + if (IsContainerPartition(PartEntry->PartitionType)) + return; + ASSERT(PartEntry->IsPartitioned && PartEntry->PartitionNumber != 0); + + /* Make a device name for the partition */ + Status = RtlStringCchPrintfW(PartEntry->DeviceName, + _countof(PartEntry->DeviceName), + L"\\Device\\Harddisk%lu\\Partition%lu", + PartEntry->DiskEntry->DiskNumber, + PartEntry->PartitionNumber); + ASSERT(NT_SUCCESS(Status)); +} + +static +VOID +InitVolumeDeviceName( + _Inout_ PVOLENTRY Volume) +{ + NTSTATUS Status; + PPARTENTRY PartEntry; + + /* If we already have a volume device name, do nothing more */ + if (*Volume->Info.DeviceName) + return; + + /* Use the partition device name as a temporary volume device name */ + // TODO: Ask instead the MOUNTMGR for the name. + PartEntry = Volume->PartEntry; + ASSERT(PartEntry); + ASSERT(PartEntry->IsPartitioned && PartEntry->PartitionNumber != 0); + + /* Copy the volume device name */ + Status = RtlStringCchCopyW(Volume->Info.DeviceName, + _countof(Volume->Info.DeviceName), + PartEntry->DeviceName); + ASSERT(NT_SUCCESS(Status)); +} + +static +PVOLENTRY +InitVolume( + _In_ PPARTLIST List, + _In_opt_ PPARTENTRY PartEntry) +{ + PVOLENTRY Volume; + + Volume = RtlAllocateHeap(ProcessHeap, + HEAP_ZERO_MEMORY, + sizeof(VOLENTRY)); + if (!Volume) + return NULL; + + /* Reset some volume information */ + + /* No device name for now */ + Volume->Info.DeviceName[0] = UNICODE_NULL; + // Volume->Info.VolumeName[0] = UNICODE_NULL; + + /* Initialize the volume letter and label */ + Volume->Info.DriveLetter = UNICODE_NULL; + Volume->Info.VolumeLabel[0] = UNICODE_NULL; + + /* Specify the volume as initially unformatted */ + Volume->Info.FileSystem[0] = UNICODE_NULL; + Volume->FormatState = Unformatted; + Volume->NeedsCheck = FALSE; + Volume->New = TRUE; + + if (PartEntry) + { + ASSERT(PartEntry->DiskEntry->PartList == List); + Volume->PartEntry = PartEntry; + } + InsertTailList(&List->VolumesList, &Volume->ListEntry); + return Volume; +} static VOID @@ -844,29 +926,21 @@ AddPartitionToDisk( IN ULONG PartitionIndex, IN BOOLEAN LogicalPartition) { - NTSTATUS Status; PPARTITION_INFORMATION PartitionInfo; PPARTENTRY PartEntry; - HANDLE PartitionHandle; - OBJECT_ATTRIBUTES ObjectAttributes; - IO_STATUS_BLOCK IoStatusBlock; - WCHAR PathBuffer[MAX_PATH]; - UNICODE_STRING Name; - UCHAR LabelBuffer[sizeof(FILE_FS_VOLUME_INFORMATION) + 256 * sizeof(WCHAR)]; - PFILE_FS_VOLUME_INFORMATION LabelInfo = (PFILE_FS_VOLUME_INFORMATION)LabelBuffer; PartitionInfo = &DiskEntry->LayoutBuffer->PartitionEntry[PartitionIndex]; - if (PartitionInfo->PartitionType == PARTITION_ENTRY_UNUSED || - ((LogicalPartition != FALSE) && IsContainerPartition(PartitionInfo->PartitionType))) - { + /* Ignore empty partitions */ + if (PartitionInfo->PartitionType == PARTITION_ENTRY_UNUSED) return; - } + /* Request must be consistent, though! */ + ASSERT(!(LogicalPartition && IsContainerPartition(PartitionInfo->PartitionType))); PartEntry = RtlAllocateHeap(ProcessHeap, HEAP_ZERO_MEMORY, sizeof(PARTENTRY)); - if (PartEntry == NULL) + if (!PartEntry) return; PartEntry->DiskEntry = DiskEntry; @@ -882,146 +956,73 @@ AddPartitionToDisk( PartEntry->OnDiskPartitionNumber = PartitionInfo->PartitionNumber; PartEntry->PartitionNumber = PartitionInfo->PartitionNumber; PartEntry->PartitionIndex = PartitionIndex; + InitPartitionDeviceName(PartEntry); - /* Specify the partition as initially unformatted */ - PartEntry->FormatState = Unformatted; - PartEntry->FileSystem[0] = L'\0'; - - /* Initialize the partition volume label */ - RtlZeroMemory(PartEntry->VolumeLabel, sizeof(PartEntry->VolumeLabel)); + /* No volume initially */ + PartEntry->Volume = NULL; if (IsContainerPartition(PartEntry->PartitionType)) { - PartEntry->FormatState = Unformatted; - - if (LogicalPartition == FALSE && DiskEntry->ExtendedPartition == NULL) + if (!LogicalPartition && DiskEntry->ExtendedPartition == NULL) DiskEntry->ExtendedPartition = PartEntry; } - else if (IsRecognizedPartition(PartEntry->PartitionType)) + else if (IsRecognizedPartition(PartEntry->PartitionType) || // PartitionInfo->RecognizedPartition + IsOEMPartition(PartEntry->PartitionType)) { - ASSERT(PartitionInfo->RecognizedPartition); - ASSERT(PartEntry->IsPartitioned && PartEntry->PartitionNumber != 0); + PVOLENTRY Volume; + NTSTATUS Status; - /* Try to open the volume so as to mount it */ - RtlStringCchPrintfW(PathBuffer, ARRAYSIZE(PathBuffer), - L"\\Device\\Harddisk%lu\\Partition%lu", - DiskEntry->DiskNumber, - PartEntry->PartitionNumber); - RtlInitUnicodeString(&Name, PathBuffer); + ASSERT(PartEntry->PartitionNumber != 0); - InitializeObjectAttributes(&ObjectAttributes, - &Name, - OBJ_CASE_INSENSITIVE, - NULL, - NULL); + /* The PARTMGR should have notified the MOUNTMGR that a volume + * associated with this partition had to be created */ + Volume = InitVolume(DiskEntry->PartList, PartEntry); + if (!Volume) + { + DPRINT1("Couldn't allocate a volume for device '%S'\n", + PartEntry->DeviceName); + goto SkipVolume; + } + PartEntry->Volume = Volume; + InitVolumeDeviceName(Volume); + Volume->New = FALSE; - PartitionHandle = NULL; - Status = NtOpenFile(&PartitionHandle, - FILE_READ_DATA | SYNCHRONIZE, - &ObjectAttributes, - &IoStatusBlock, - FILE_SHARE_READ | FILE_SHARE_WRITE, - FILE_SYNCHRONOUS_IO_NONALERT); + /* Attach and mount the volume */ + Status = MountVolume(&Volume->Info, PartEntry->PartitionType); if (!NT_SUCCESS(Status)) { - DPRINT1("NtOpenFile() failed, Status 0x%08lx\n", Status); + DPRINT1("Failed to mount volume '%S', Status 0x%08lx\n", + Volume->Info.DeviceName, Status); } - if (PartitionHandle) - { - ASSERT(NT_SUCCESS(Status)); - - /* We don't have a FS, try to guess one */ - Status = InferFileSystem(NULL, PartitionHandle, - PartEntry->FileSystem, - sizeof(PartEntry->FileSystem)); - if (!NT_SUCCESS(Status)) - DPRINT1("InferFileSystem() failed, Status 0x%08lx\n", Status); - } - if (*PartEntry->FileSystem) - { - ASSERT(PartitionHandle); - - /* - * Handle partition mounted with RawFS: it is - * either unformatted or has an unknown format. - */ - if (wcsicmp(PartEntry->FileSystem, L"RAW") == 0) - { - /* - * True unformatted partitions on NT are created with their - * partition type set to either one of the following values, - * and are mounted with RawFS. This is done this way since we - * are assured to have FAT support, which is the only FS that - * uses these partition types. Therefore, having a partition - * mounted with RawFS and with these partition types means that - * the FAT FS was unable to mount it beforehand and thus the - * partition is unformatted. - * However, any partition mounted by RawFS that does NOT have - * any of these partition types must be considered as having - * an unknown format. - */ - if (PartEntry->PartitionType == PARTITION_FAT_12 || - PartEntry->PartitionType == PARTITION_FAT_16 || - PartEntry->PartitionType == PARTITION_HUGE || - PartEntry->PartitionType == PARTITION_XINT13 || - PartEntry->PartitionType == PARTITION_FAT32 || - PartEntry->PartitionType == PARTITION_FAT32_XINT13) - { - PartEntry->FormatState = Unformatted; - } - else - { - /* Close the partition before dismounting */ - NtClose(PartitionHandle); - PartitionHandle = NULL; - /* - * Dismount the partition since RawFS owns it, and set its - * format to unknown (may or may not be actually formatted). - */ - DismountVolume(PartEntry); - PartEntry->FormatState = UnknownFormat; - PartEntry->FileSystem[0] = L'\0'; - } - } - else - { - PartEntry->FormatState = Preformatted; - } - } - else - { - PartEntry->FormatState = UnknownFormat; - } - - /* Retrieve the partition volume label */ - if (PartitionHandle) - { - Status = NtQueryVolumeInformationFile(PartitionHandle, - &IoStatusBlock, - &LabelBuffer, - sizeof(LabelBuffer), - FileFsVolumeInformation); - if (NT_SUCCESS(Status)) - { - /* Copy the (possibly truncated) volume label and NULL-terminate it */ - RtlStringCbCopyNW(PartEntry->VolumeLabel, sizeof(PartEntry->VolumeLabel), - LabelInfo->VolumeLabel, LabelInfo->VolumeLabelLength); - } - else - { - DPRINT1("NtQueryVolumeInformationFile() failed, Status 0x%08lx\n", Status); - } - } - - /* Close the partition */ - if (PartitionHandle) - NtClose(PartitionHandle); + // + // FIXME: TEMP Backward-compatibility: Set the FormatState + // flag in accordance with the FileSystem volume value. + // + /* + * MountVolume() determines whether the given volume is actually + * unformatted, if it was mounted with RawFS and the partition + * type has specific values for FAT volumes. If so, the volume + * stays mounted with RawFS (the FileSystem is "RAW"). However, + * if the partition type has different values, the volume is + * considered as having an unknown format (it may or may not be + * formatted) and the FileSystem value has been emptied. + */ + if (IsUnknown(&Volume->Info)) + Volume->FormatState = UnknownFormat; + else if (IsUnformatted(&Volume->Info)) // FileSystem is "RAW" + Volume->FormatState = Unformatted; + else // !IsUnknown && !IsUnformatted == IsFormatted + Volume->FormatState = Formatted; +SkipVolume:; } else { - /* Unknown partition, hence unknown format (may or may not be actually formatted) */ - PartEntry->FormatState = UnknownFormat; + /* Unknown partition (may or may not be actually formatted): + * the partition is hidden, hence no volume */ + DPRINT1("Disk %lu Partition %lu is not recognized (Type 0x%02x)\n", + DiskEntry->DiskNumber, PartEntry->PartitionNumber, + PartEntry->PartitionType); } InsertDiskRegion(DiskEntry, PartEntry, LogicalPartition); @@ -1262,7 +1263,7 @@ SetDiskSignature( /* Check if the signature already exist */ /* FIXME: * Check also signatures from disks, which are - * not visible (bootable) by the bios. + * not visible (bootable) by the BIOS. */ for (Entry2 = List->DiskListHead.Flink; Entry2 != &List->DiskListHead; @@ -1569,7 +1570,7 @@ AddDiskToList( { BiosDiskEntry = CONTAINING_RECORD(ListEntry, BIOSDISKENTRY, ListEntry); /* FIXME: - * Compare the size from bios and the reported size from driver. + * Compare the size from BIOS and the reported size from driver. * If we have more than one disk with a zero or with the same signature * we must create new signatures and reboot. After the reboot, * it is possible to identify the disks. @@ -1898,12 +1899,13 @@ GetActiveDiskPartition( /* Yes, we've found it */ ASSERT(DiskEntry == PartEntry->DiskEntry); ASSERT(PartEntry->IsPartitioned); + ASSERT(PartEntry->Volume); ActivePartition = PartEntry; DPRINT1("Found active system partition %lu in disk %lu, drive letter %C\n", PartEntry->PartitionNumber, DiskEntry->DiskNumber, - (PartEntry->DriveLetter == 0) ? L'-' : PartEntry->DriveLetter); + !PartEntry->Volume->Info.DriveLetter ? L'-' : PartEntry->Volume->Info.DriveLetter); break; } } @@ -1937,13 +1939,14 @@ CreatePartitionList(VOID) List = (PPARTLIST)RtlAllocateHeap(ProcessHeap, 0, sizeof(PARTLIST)); - if (List == NULL) + if (!List) return NULL; List->SystemPartition = NULL; InitializeListHead(&List->DiskListHead); InitializeListHead(&List->BiosDiskListHead); + InitializeListHead(&List->VolumesList); /* * Enumerate the disks seen by the BIOS; this will be used later @@ -2026,8 +2029,7 @@ DestroyPartitionList( { Entry = RemoveHeadList(&DiskEntry->PrimaryPartListHead); PartEntry = CONTAINING_RECORD(Entry, PARTENTRY, ListEntry); - - RtlFreeHeap(ProcessHeap, 0, PartEntry); + DestroyRegion(PartEntry); } /* Release logical partition list */ @@ -2035,8 +2037,7 @@ DestroyPartitionList( { Entry = RemoveHeadList(&DiskEntry->LogicalPartListHead); PartEntry = CONTAINING_RECORD(Entry, PARTENTRY, ListEntry); - - RtlFreeHeap(ProcessHeap, 0, PartEntry); + DestroyRegion(PartEntry); } /* Release layout buffer */ @@ -2047,12 +2048,11 @@ DestroyPartitionList( RtlFreeHeap(ProcessHeap, 0, DiskEntry); } - /* Release the bios disk info */ + /* Release the BIOS disk info */ while (!IsListEmpty(&List->BiosDiskListHead)) { Entry = RemoveHeadList(&List->BiosDiskListHead); BiosDiskEntry = CONTAINING_RECORD(Entry, BIOSDISKENTRY, ListEntry); - RtlFreeHeap(ProcessHeap, 0, BiosDiskEntry); } @@ -2540,7 +2540,7 @@ UpdateDiskLayout( } /* Resize the layout buffer if necessary */ - if (ReAllocateLayoutBuffer(DiskEntry) == FALSE) + if (!ReAllocateLayoutBuffer(DiskEntry)) { DPRINT("ReAllocateLayoutBuffer() failed.\n"); return; @@ -2561,7 +2561,7 @@ UpdateDiskLayout( PartitionInfo = &DiskEntry->LayoutBuffer->PartitionEntry[Index]; PartEntry->PartitionIndex = Index; - /* Reset the current partition number only for newly-created (unmounted) partitions */ + /* Reset the current partition number only for not-yet written partitions */ if (PartEntry->New) PartEntry->PartitionNumber = 0; @@ -2606,7 +2606,7 @@ UpdateDiskLayout( PartitionInfo = &DiskEntry->LayoutBuffer->PartitionEntry[Index]; PartEntry->PartitionIndex = Index; - /* Reset the current partition number only for newly-created (unmounted) partitions */ + /* Reset the current partition number only for not-yet written partitions */ if (PartEntry->New) PartEntry->PartitionNumber = 0; @@ -2849,6 +2849,7 @@ CreatePartition( { ERROR_NUMBER Error; BOOLEAN isContainer = IsContainerPartition((UCHAR)PartitionInfo); + PDISKENTRY DiskEntry; PCSTR mainType = "Primary"; if (isContainer) @@ -2878,39 +2879,42 @@ CreatePartition( if (!InitializePartitionEntry(PartEntry, SizeBytes, PartitionInfo)) return FALSE; - if (isContainer) + DiskEntry = PartEntry->DiskEntry; + UpdateDiskLayout(DiskEntry); + + ASSERT(!PartEntry->Volume); + if (!isContainer) { - // FIXME? Possibly to make GetNextUnformattedPartition work (i.e. skip the extended partition container) - PartEntry->New = FALSE; - PartEntry->FormatState = Formatted; + /* We create a primary/logical partition: initialize a new basic + * volume entry. When the partition will actually be written onto + * the disk, the PARTMGR will notify the MOUNTMGR that a volume + * associated with this partition has to be created. */ + PartEntry->Volume = InitVolume(DiskEntry->PartList, PartEntry); + ASSERT(PartEntry->Volume); } - UpdateDiskLayout(PartEntry->DiskEntry); AssignDriveLetters(List); return TRUE; } -NTSTATUS -DismountVolume( - IN PPARTENTRY PartEntry) +static NTSTATUS +DismountPartition( + _In_ PPARTLIST List, + _In_ PPARTENTRY PartEntry) { NTSTATUS Status; - NTSTATUS LockStatus; - UNICODE_STRING Name; - OBJECT_ATTRIBUTES ObjectAttributes; - IO_STATUS_BLOCK IoStatusBlock; - HANDLE PartitionHandle; - WCHAR Buffer[MAX_PATH]; + PVOLENTRY Volume = PartEntry->Volume; + + ASSERT(PartEntry->DiskEntry->PartList == List); /* Check whether the partition is valid and was mounted by the system */ if (!PartEntry->IsPartitioned || IsContainerPartition(PartEntry->PartitionType) || !IsRecognizedPartition(PartEntry->PartitionType) || - PartEntry->FormatState == UnknownFormat || + !Volume || Volume->FormatState == UnknownFormat || // NOTE: If FormatState == Unformatted but *FileSystem != 0 this means // it has been usually mounted with RawFS and thus needs to be dismounted. - !*PartEntry->FileSystem || PartEntry->PartitionNumber == 0) { /* The partition is not mounted, so just return success */ @@ -2918,83 +2922,14 @@ DismountVolume( } ASSERT(PartEntry->PartitionType != PARTITION_ENTRY_UNUSED); + ASSERT(Volume->PartEntry == PartEntry); - /* Open the volume */ - RtlStringCchPrintfW(Buffer, ARRAYSIZE(Buffer), - L"\\Device\\Harddisk%lu\\Partition%lu", - PartEntry->DiskEntry->DiskNumber, - PartEntry->PartitionNumber); - RtlInitUnicodeString(&Name, Buffer); - - InitializeObjectAttributes(&ObjectAttributes, - &Name, - OBJ_CASE_INSENSITIVE, - NULL, - NULL); - - Status = NtOpenFile(&PartitionHandle, - GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE, - &ObjectAttributes, - &IoStatusBlock, - FILE_SHARE_READ | FILE_SHARE_WRITE, - FILE_SYNCHRONOUS_IO_NONALERT); - if (!NT_SUCCESS(Status)) - { - DPRINT1("ERROR: Cannot open volume %wZ for dismounting! (Status 0x%lx)\n", &Name, Status); - return Status; - } - - /* Lock the volume */ - LockStatus = NtFsControlFile(PartitionHandle, - NULL, - NULL, - NULL, - &IoStatusBlock, - FSCTL_LOCK_VOLUME, - NULL, - 0, - NULL, - 0); - if (!NT_SUCCESS(LockStatus)) - { - DPRINT1("WARNING: Failed to lock volume! Operations may fail! (Status 0x%lx)\n", LockStatus); - } - - /* Dismount the volume */ - Status = NtFsControlFile(PartitionHandle, - NULL, - NULL, - NULL, - &IoStatusBlock, - FSCTL_DISMOUNT_VOLUME, - NULL, - 0, - NULL, - 0); - if (!NT_SUCCESS(Status)) - { - DPRINT1("Failed to unmount volume (Status 0x%lx)\n", Status); - } - - /* Unlock the volume */ - LockStatus = NtFsControlFile(PartitionHandle, - NULL, - NULL, - NULL, - &IoStatusBlock, - FSCTL_UNLOCK_VOLUME, - NULL, - 0, - NULL, - 0); - if (!NT_SUCCESS(LockStatus)) - { - DPRINT1("Failed to unlock volume (Status 0x%lx)\n", LockStatus); - } - - /* Close the volume */ - NtClose(PartitionHandle); - + /* Unlink the basic volume from the volumes list and dismount it */ + PartEntry->Volume = NULL; + Volume->PartEntry = NULL; + RemoveEntryList(&Volume->ListEntry); + Status = DismountVolume(&Volume->Info, TRUE); + RtlFreeHeap(ProcessHeap, 0, Volume); return Status; } @@ -3016,6 +2951,7 @@ DeletePartition( return FALSE; } + ASSERT(PartEntry->DiskEntry->PartList == List); ASSERT(PartEntry->PartitionType != PARTITION_ENTRY_UNUSED); /* Clear the system partition if it is being deleted */ @@ -3036,19 +2972,17 @@ DeletePartition( Entry = RemoveHeadList(&DiskEntry->LogicalPartListHead); LogicalPartEntry = CONTAINING_RECORD(Entry, PARTENTRY, ListEntry); - /* Dismount the logical partition */ - DismountVolume(LogicalPartEntry); - - /* Delete it */ - RtlFreeHeap(ProcessHeap, 0, LogicalPartEntry); + /* Dismount the logical partition and delete it */ + DismountPartition(List, LogicalPartEntry); + DestroyRegion(LogicalPartEntry); } DiskEntry->ExtendedPartition = NULL; } else { - /* A primary partition is being deleted: dismount it */ - DismountVolume(PartEntry); + /* A primary/logical partition is being deleted: dismount it */ + DismountPartition(List, PartEntry); } /* Adjust the unpartitioned disk space entries */ @@ -3066,9 +3000,9 @@ DeletePartition( /* Remove the current and next entries */ RemoveEntryList(&PartEntry->ListEntry); - RtlFreeHeap(ProcessHeap, 0, PartEntry); + DestroyRegion(PartEntry); RemoveEntryList(&NextPartEntry->ListEntry); - RtlFreeHeap(ProcessHeap, 0, NextPartEntry); + DestroyRegion(NextPartEntry); /* Optionally return the freed region */ if (FreeRegion) @@ -3083,7 +3017,7 @@ DeletePartition( /* Remove the current entry */ RemoveEntryList(&PartEntry->ListEntry); - RtlFreeHeap(ProcessHeap, 0, PartEntry); + DestroyRegion(PartEntry); /* Optionally return the freed region */ if (FreeRegion) @@ -3099,7 +3033,7 @@ DeletePartition( /* Remove the current entry */ RemoveEntryList(&PartEntry->ListEntry); - RtlFreeHeap(ProcessHeap, 0, PartEntry); + DestroyRegion(PartEntry); /* Optionally return the freed region */ if (FreeRegion) @@ -3108,16 +3042,21 @@ DeletePartition( else { /* Nothing to merge but change the current entry */ + PartEntry->New = FALSE; PartEntry->IsPartitioned = FALSE; + PartEntry->PartitionType = PARTITION_ENTRY_UNUSED; PartEntry->OnDiskPartitionNumber = 0; PartEntry->PartitionNumber = 0; // PartEntry->PartitionIndex = 0; PartEntry->BootIndicator = FALSE; - PartEntry->PartitionType = PARTITION_ENTRY_UNUSED; - PartEntry->FormatState = Unformatted; - PartEntry->FileSystem[0] = L'\0'; - PartEntry->DriveLetter = 0; - RtlZeroMemory(PartEntry->VolumeLabel, sizeof(PartEntry->VolumeLabel)); + PartEntry->DeviceName[0] = UNICODE_NULL; + + if (PartEntry->Volume) + { + RemoveEntryList(&PartEntry->Volume->ListEntry); + RtlFreeHeap(ProcessHeap, 0, PartEntry->Volume); + } + PartEntry->Volume = NULL; /* Optionally return the freed region */ if (FreeRegion) @@ -3135,6 +3074,8 @@ BOOLEAN IsSupportedActivePartition( IN PPARTENTRY PartEntry) { + PVOLENTRY Volume; + /* Check the type and the file system of this partition */ /* @@ -3148,6 +3089,13 @@ IsSupportedActivePartition( return FALSE; } + Volume = PartEntry->Volume; + if (!Volume) + { + /* Still no recognizable volume mounted: partition not supported */ + return FALSE; + } + /* * ADDITIONAL CHECKS / BIG HACK: * @@ -3164,21 +3112,20 @@ IsSupportedActivePartition( * NOTE also that for those architectures looking for a * partition boot indicator is insufficient. */ - if (PartEntry->FormatState == Unformatted) + if (Volume->FormatState == Unformatted) { /* If this partition is mounted, it would use RawFS ("RAW") */ return TRUE; } - else if ((PartEntry->FormatState == Preformatted) || - (PartEntry->FormatState == Formatted)) + else if (Volume->FormatState == Formatted) { - ASSERT(*PartEntry->FileSystem); + ASSERT(*Volume->Info.FileSystem); /* NOTE: Please keep in sync with the RegisteredFileSystems list! */ - if (wcsicmp(PartEntry->FileSystem, L"FAT") == 0 || - wcsicmp(PartEntry->FileSystem, L"FAT32") == 0 || - // wcsicmp(PartEntry->FileSystem, L"NTFS") == 0 || - wcsicmp(PartEntry->FileSystem, L"BTRFS") == 0) + if (wcsicmp(Volume->Info.FileSystem, L"FAT") == 0 || + wcsicmp(Volume->Info.FileSystem, L"FAT32") == 0 || + // wcsicmp(Volume->Info.FileSystem, L"NTFS") == 0 || + wcsicmp(Volume->Info.FileSystem, L"BTRFS") == 0) { return TRUE; } @@ -3186,13 +3133,13 @@ IsSupportedActivePartition( { // WARNING: We cannot write on this FS yet! DPRINT1("Recognized file system '%S' that doesn't have write support yet!\n", - PartEntry->FileSystem); + Volume->Info.FileSystem); return FALSE; } } - else // if (PartEntry->FormatState == UnknownFormat) + else // if (Volume->FormatState == UnknownFormat) { - ASSERT(!*PartEntry->FileSystem); + ASSERT(!*Volume->Info.FileSystem); DPRINT1("System partition %lu in disk %lu with no or unknown FS?!\n", PartEntry->PartitionNumber, PartEntry->DiskEntry->DiskNumber); @@ -3204,7 +3151,7 @@ IsSupportedActivePartition( if (PartEntry->PartitionType == PARTITION_IFS) { DPRINT1("Recognized file system '%S' that doesn't have write support yet!\n", - PartEntry->FileSystem); + Volume->Info.FileSystem); return FALSE; } @@ -3278,7 +3225,7 @@ FindSupportedSystemPartition( DPRINT1("Use the current system partition %lu in disk %lu, drive letter %C\n", CandidatePartition->PartitionNumber, CandidatePartition->DiskEntry->DiskNumber, - (CandidatePartition->DriveLetter == 0) ? L'-' : CandidatePartition->DriveLetter); + !CandidatePartition->Volume->Info.DriveLetter ? L'-' : CandidatePartition->Volume->Info.DriveLetter); /* Return the candidate system partition */ return CandidatePartition; @@ -3427,7 +3374,7 @@ UseAlternativeDisk: DPRINT1("Use new first active system partition %lu in disk %lu, drive letter %C\n", CandidatePartition->PartitionNumber, CandidatePartition->DiskEntry->DiskNumber, - (CandidatePartition->DriveLetter == 0) ? L'-' : CandidatePartition->DriveLetter); + !CandidatePartition->Volume->Info.DriveLetter ? L'-' : CandidatePartition->Volume->Info.DriveLetter); /* Return the candidate system partition */ return CandidatePartition; @@ -3467,7 +3414,7 @@ UseAlternativeDisk: DPRINT1("Use first active system partition %lu in disk %lu, drive letter %C\n", CandidatePartition->PartitionNumber, CandidatePartition->DiskEntry->DiskNumber, - (CandidatePartition->DriveLetter == 0) ? L'-' : CandidatePartition->DriveLetter); + !CandidatePartition->Volume->Info.DriveLetter ? L'-' : CandidatePartition->Volume->Info.DriveLetter); /* Return the candidate system partition */ return CandidatePartition; @@ -3505,7 +3452,7 @@ UseAlternativePartition: DPRINT1("Use alternative active system partition %lu in disk %lu, drive letter %C\n", CandidatePartition->PartitionNumber, CandidatePartition->DiskEntry->DiskNumber, - (CandidatePartition->DriveLetter == 0) ? L'-' : CandidatePartition->DriveLetter); + !CandidatePartition->Volume->Info.DriveLetter ? L'-' : CandidatePartition->Volume->Info.DriveLetter); /* Return the candidate system partition */ return CandidatePartition; @@ -3543,7 +3490,7 @@ SetActivePartition( /* * If the user provided an old active partition hint, verify that it is - * indeeed active and belongs to the same disk where the new partition + * indeed active and belongs to the same disk where the new partition * belongs. Otherwise determine the current active partition on the disk * where the new partition belongs. */ @@ -3633,7 +3580,9 @@ WritePartitions( /* Save the original partition count to be restored later (see comment below) */ PartitionCount = DiskEntry->LayoutBuffer->PartitionCount; - /* Set the new disk layout and retrieve its updated version with possibly modified partition numbers */ + /* Set the new disk layout and retrieve its updated version with + * new partition numbers for the new partitions. The PARTMGR will + * automatically notify the MOUNTMGR of new or deleted volumes. */ BufferSize = sizeof(DRIVE_LAYOUT_INFORMATION) + ((PartitionCount - 1) * sizeof(PARTITION_INFORMATION)); Status = NtDeviceIoControlFile(FileHandle, @@ -3668,7 +3617,7 @@ WritePartitions( DumpPartitionTable(DiskEntry); #endif - /* Update the partition numbers */ + /* Update the partition numbers and device names */ /* Update the primary partition table */ for (ListEntry = DiskEntry->PrimaryPartListHead.Flink; @@ -3676,13 +3625,25 @@ WritePartitions( ListEntry = ListEntry->Flink) { PartEntry = CONTAINING_RECORD(ListEntry, PARTENTRY, ListEntry); + if (!PartEntry->IsPartitioned) + continue; + ASSERT(PartEntry->PartitionType != PARTITION_ENTRY_UNUSED); - if (PartEntry->IsPartitioned) + /* + * Initialize the partition's number and its device name only + * if the partition was new. Note that the partition number + * should not change if this partition has not been deleted + * during repartitioning. + */ + // FIXME: Our PartMgr currently returns modified numbers + // in the layout, this needs to be investigated and fixed. + if (PartEntry->New) { - ASSERT(PartEntry->PartitionType != PARTITION_ENTRY_UNUSED); PartitionInfo = &DiskEntry->LayoutBuffer->PartitionEntry[PartEntry->PartitionIndex]; PartEntry->PartitionNumber = PartitionInfo->PartitionNumber; + InitPartitionDeviceName(PartEntry); } + PartEntry->New = FALSE; } /* Update the logical partition table */ @@ -3691,13 +3652,18 @@ WritePartitions( ListEntry = ListEntry->Flink) { PartEntry = CONTAINING_RECORD(ListEntry, PARTENTRY, ListEntry); + if (!PartEntry->IsPartitioned) + continue; + ASSERT(PartEntry->PartitionType != PARTITION_ENTRY_UNUSED); - if (PartEntry->IsPartitioned) + /* See comment above */ + if (PartEntry->New) { - ASSERT(PartEntry->PartitionType != PARTITION_ENTRY_UNUSED); PartitionInfo = &DiskEntry->LayoutBuffer->PartitionEntry[PartEntry->PartitionIndex]; PartEntry->PartitionNumber = PartitionInfo->PartitionNumber; + InitPartitionDeviceName(PartEntry); } + PartEntry->New = FALSE; } // @@ -3726,10 +3692,12 @@ WritePartitionsToDisk( NTSTATUS Status; PLIST_ENTRY Entry; PDISKENTRY DiskEntry; + PVOLENTRY Volume; - if (List == NULL) + if (!List) return TRUE; + /* Write all the partitions to all the disks */ for (Entry = List->DiskListHead.Flink; Entry != &List->DiskListHead; Entry = Entry->Flink) @@ -3753,26 +3721,48 @@ WritePartitionsToDisk( } } + /* The PARTMGR should have notified the MOUNTMGR that new volumes + * associated with the new partitions had to be created */ + + /* Assign valid device names to new volumes */ + for (Entry = List->VolumesList.Flink; + Entry != &List->VolumesList; + Entry = Entry->Flink) + { + Volume = CONTAINING_RECORD(Entry, VOLENTRY, ListEntry); + InitVolumeDeviceName(Volume); + } + return TRUE; } -BOOLEAN + +/** + * @brief + * Assign a "\DosDevices\#:" mount point drive letter to a disk partition or + * volume, specified by a given disk signature and starting partition offset. + **/ +static BOOLEAN SetMountedDeviceValue( - IN WCHAR Letter, - IN ULONG Signature, - IN LARGE_INTEGER StartingOffset) + _In_ PVOLENTRY Volume) { + PPARTENTRY PartEntry = Volume->PartEntry; + WCHAR Letter = Volume->Info.DriveLetter; NTSTATUS Status; OBJECT_ATTRIBUTES ObjectAttributes; UNICODE_STRING KeyName = RTL_CONSTANT_STRING(L"SYSTEM\\MountedDevices"); UNICODE_STRING ValueName; - WCHAR ValueNameBuffer[16]; + WCHAR Buffer[16]; HANDLE KeyHandle; REG_DISK_MOUNT_INFO MountInfo; - RtlStringCchPrintfW(ValueNameBuffer, ARRAYSIZE(ValueNameBuffer), + /* Ignore no letter */ + if (!Letter) + return TRUE; + + RtlStringCchPrintfW(Buffer, _countof(Buffer), L"\\DosDevices\\%c:", Letter); - RtlInitUnicodeString(&ValueName, ValueNameBuffer); + RtlInitUnicodeString(&ValueName, Buffer); InitializeObjectAttributes(&ObjectAttributes, &KeyName, @@ -3799,8 +3789,8 @@ SetMountedDeviceValue( return FALSE; } - MountInfo.Signature = Signature; - MountInfo.StartingOffset = StartingOffset; + MountInfo.Signature = PartEntry->DiskEntry->LayoutBuffer->Signature; + MountInfo.StartingOffset = GetPartEntryOffsetInBytes(PartEntry); Status = NtSetValueKey(KeyHandle, &ValueName, 0, @@ -3819,75 +3809,23 @@ SetMountedDeviceValue( BOOLEAN SetMountedDeviceValues( - IN PPARTLIST List) + _In_ PPARTLIST List) { - PLIST_ENTRY Entry1, Entry2; - PDISKENTRY DiskEntry; - PPARTENTRY PartEntry; - LARGE_INTEGER StartingOffset; + PLIST_ENTRY Entry; + PVOLENTRY Volume; - if (List == NULL) + if (!List) return FALSE; - for (Entry1 = List->DiskListHead.Flink; - Entry1 != &List->DiskListHead; - Entry1 = Entry1->Flink) + for (Entry = List->VolumesList.Flink; + Entry != &List->VolumesList; + Entry = Entry->Flink) { - DiskEntry = CONTAINING_RECORD(Entry1, - DISKENTRY, - ListEntry); + Volume = CONTAINING_RECORD(Entry, VOLENTRY, ListEntry); - if (DiskEntry->DiskStyle == PARTITION_STYLE_GPT) - { - DPRINT("GPT-partitioned disk detected, not currently supported by SETUP!\n"); - continue; - } - - for (Entry2 = DiskEntry->PrimaryPartListHead.Flink; - Entry2 != &DiskEntry->PrimaryPartListHead; - Entry2 = Entry2->Flink) - { - PartEntry = CONTAINING_RECORD(Entry2, PARTENTRY, ListEntry); - if (PartEntry->IsPartitioned) // && !IsContainerPartition(PartEntry->PartitionType) - { - ASSERT(PartEntry->PartitionType != PARTITION_ENTRY_UNUSED); - - /* Assign a "\DosDevices\#:" mount point to this partition */ - if (PartEntry->DriveLetter) - { - StartingOffset.QuadPart = GetPartEntryOffsetInBytes(PartEntry); - if (!SetMountedDeviceValue(PartEntry->DriveLetter, - DiskEntry->LayoutBuffer->Signature, - StartingOffset)) - { - return FALSE; - } - } - } - } - - for (Entry2 = DiskEntry->LogicalPartListHead.Flink; - Entry2 != &DiskEntry->LogicalPartListHead; - Entry2 = Entry2->Flink) - { - PartEntry = CONTAINING_RECORD(Entry2, PARTENTRY, ListEntry); - if (PartEntry->IsPartitioned) // && !IsContainerPartition(PartEntry->PartitionType) - { - ASSERT(PartEntry->PartitionType != PARTITION_ENTRY_UNUSED); - - /* Assign a "\DosDevices\#:" mount point to this partition */ - if (PartEntry->DriveLetter) - { - StartingOffset.QuadPart = GetPartEntryOffsetInBytes(PartEntry); - if (!SetMountedDeviceValue(PartEntry->DriveLetter, - DiskEntry->LayoutBuffer->Signature, - StartingOffset)) - { - return FALSE; - } - } - } - } + /* Assign a "\DosDevices\#:" mount point to this volume */ + if (!SetMountedDeviceValue(Volume)) + return FALSE; } return TRUE; @@ -3902,6 +3840,12 @@ SetMBRPartitionType( ASSERT(DiskEntry->DiskStyle == PARTITION_STYLE_MBR); + /* Nothing to do if we assign the same type */ + if (PartitionType == PartEntry->PartitionType) + return; + + // TODO: We might need to remount the associated basic volume... + PartEntry->PartitionType = PartitionType; DiskEntry->Dirty = TRUE; diff --git a/base/setup/lib/utils/partlist.h b/base/setup/lib/utils/partlist.h index 9654baf3839..967c8385b4d 100644 --- a/base/setup/lib/utils/partlist.h +++ b/base/setup/lib/utils/partlist.h @@ -1,9 +1,9 @@ /* * PROJECT: ReactOS Setup Library - * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+) + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) * PURPOSE: Partition list functions * COPYRIGHT: Copyright 2003-2019 Casper S. Hornstrup (chorns@users.sourceforge.net) - * Copyright 2018-2019 Hermes Belusca-Maito + * Copyright 2018-2024 Hermès Bélusca-Maïto */ #pragma once @@ -34,10 +34,30 @@ typedef enum _FORMATSTATE Unformatted, UnformattedOrDamaged, UnknownFormat, - Preformatted, Formatted } FORMATSTATE, *PFORMATSTATE; +#include "volutil.h" + +typedef struct _PARTENTRY PARTENTRY, *PPARTENTRY; +typedef struct _VOLENTRY +{ + LIST_ENTRY ListEntry; ///< Entry in VolumesList + + VOLINFO Info; + FORMATSTATE FormatState; + + /* Volume must be checked */ + BOOLEAN NeedsCheck; + /* Volume is new and has not yet been actually formatted and mounted */ + BOOLEAN New; + + // union { + // PVOLUME_DISK_EXTENTS pExtents; + PPARTENTRY PartEntry; + // }; +} VOLENTRY, *PVOLENTRY; + typedef struct _PARTENTRY { LIST_ENTRY ListEntry; @@ -54,24 +74,25 @@ typedef struct _PARTENTRY ULONG OnDiskPartitionNumber; /* Enumerated partition number (primary partitions first, excluding the extended partition container, then the logical partitions) */ ULONG PartitionNumber; /* Current partition number, only valid for the currently running NTOS instance */ ULONG PartitionIndex; /* Index in the LayoutBuffer->PartitionEntry[] cached array of the corresponding DiskEntry */ - - WCHAR DriveLetter; - WCHAR VolumeLabel[20]; - WCHAR FileSystem[MAX_PATH+1]; - FORMATSTATE FormatState; + WCHAR DeviceName[MAX_PATH]; ///< NT device name: "\Device\HarddiskM\PartitionN" BOOLEAN LogicalPartition; /* Partition is partitioned disk space */ BOOLEAN IsPartitioned; -/** The following three properties may be replaced by flags **/ - /* Partition is new, table does not exist on disk yet */ BOOLEAN New; - /* Partition must be checked */ - BOOLEAN NeedsCheck; + /* + * Volume-related properties: + * NULL: No volume is associated to this partition (either because it is + * an empty disk region, or the partition type is unrecognized). + * 0x1 : TBD. + * Valid pointer: A basic volume associated to this partition is (or will) + * be mounted by the PARTMGR and enumerated by the MOUNTMGR. + */ + PVOLENTRY Volume; } PARTENTRY, *PPARTENTRY; @@ -162,9 +183,12 @@ typedef struct _PARTLIST LIST_ENTRY DiskListHead; LIST_ENTRY BiosDiskListHead; + /* (Basic) Volumes management */ + LIST_ENTRY VolumesList; + } PARTLIST, *PPARTLIST; -#define PARTITION_TBL_SIZE 4 +#define PARTITION_TBL_SIZE 4 #define PARTITION_MAGIC 0xAA55 @@ -307,10 +331,6 @@ CreatePartition( _In_opt_ ULONGLONG SizeBytes, _In_opt_ ULONG_PTR PartitionInfo); -NTSTATUS -DismountVolume( - IN PPARTENTRY PartEntry); - BOOLEAN DeletePartition( _In_ PPARTLIST List, @@ -338,15 +358,9 @@ BOOLEAN WritePartitionsToDisk( IN PPARTLIST List); -BOOLEAN -SetMountedDeviceValue( - IN WCHAR Letter, - IN ULONG Signature, - IN LARGE_INTEGER StartingOffset); - BOOLEAN SetMountedDeviceValues( - IN PPARTLIST List); + _In_ PPARTLIST List); VOID SetMBRPartitionType( diff --git a/base/setup/lib/utils/volutil.c b/base/setup/lib/utils/volutil.c new file mode 100644 index 00000000000..153488542a0 --- /dev/null +++ b/base/setup/lib/utils/volutil.c @@ -0,0 +1,226 @@ +/* + * PROJECT: ReactOS Setup Library + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: Volume utility functions + * COPYRIGHT: Copyright 2024 Hermès Bélusca-Maïto + */ + +/* INCLUDES ******************************************************************/ + +#include "precomp.h" + +#include "volutil.h" +#include "fsrec.h" +#include "devutils.h" + +#define NDEBUG +#include + + +/* FUNCTIONS *****************************************************************/ + +NTSTATUS +MountVolume( + _Inout_ PVOLINFO Volume, + _In_opt_ UCHAR MbrPartitionType) +{ + NTSTATUS Status; + HANDLE VolumeHandle; + + /* If the volume is already mounted, just return success */ + if (*Volume->FileSystem) + return STATUS_SUCCESS; + + /* Try to open the volume so as to mount it */ + VolumeHandle = NULL; + Status = pOpenDevice(Volume->DeviceName, &VolumeHandle); + if (!NT_SUCCESS(Status)) + { + DPRINT1("pOpenDevice() failed, Status 0x%08lx\n", Status); + + /* We failed, reset some data and bail out */ + Volume->DriveLetter = UNICODE_NULL; + Volume->VolumeLabel[0] = UNICODE_NULL; + Volume->FileSystem[0] = UNICODE_NULL; + + return Status; + } + ASSERT(VolumeHandle); + + /* We don't have a FS, try to guess one */ + Status = InferFileSystem(NULL, VolumeHandle, + Volume->FileSystem, + sizeof(Volume->FileSystem)); + if (!NT_SUCCESS(Status)) + DPRINT1("InferFileSystem() failed, Status 0x%08lx\n", Status); + + if (*Volume->FileSystem) + { + /* + * Handle volume mounted with RawFS: it is + * either unformatted or has an unknown format. + */ + if (IsUnformatted(Volume)) // FileSystem is "RAW" + { + /* + * True unformatted partitions on NT are created with their + * partition type set to either one of the following values, + * and are mounted with RawFS. This is done this way since we + * are assured to have FAT support, which is the only FS that + * uses these partition types. Therefore, having a partition + * mounted with RawFS and with these partition types means that + * the FAT FS was unable to mount it beforehand and thus the + * partition is unformatted. + * However, any partition mounted by RawFS that does NOT have + * any of these partition types must be considered as having + * an unknown format. + */ + if (MbrPartitionType == PARTITION_FAT_12 || + MbrPartitionType == PARTITION_FAT_16 || + MbrPartitionType == PARTITION_HUGE || + MbrPartitionType == PARTITION_XINT13 || + MbrPartitionType == PARTITION_FAT32 || + MbrPartitionType == PARTITION_FAT32_XINT13) + { + /* The volume is unformatted */ + } + else + { + /* Close the volume before dismounting */ + NtClose(VolumeHandle); + VolumeHandle = NULL; + /* + * Dismount the volume since RawFS owns it, and reset its + * format (it is unknown, may or may not be actually formatted). + */ + DismountVolume(Volume, TRUE); + Volume->FileSystem[0] = UNICODE_NULL; + } + } + /* Else, the volume is formatted */ + } + /* Else, the volume has an unknown format */ + + /* Retrieve the volume label */ + if (VolumeHandle) + { + IO_STATUS_BLOCK IoStatusBlock; + struct + { + FILE_FS_VOLUME_INFORMATION; + WCHAR Data[255]; + } LabelInfo; + + Status = NtQueryVolumeInformationFile(VolumeHandle, + &IoStatusBlock, + &LabelInfo, + sizeof(LabelInfo), + FileFsVolumeInformation); + if (NT_SUCCESS(Status)) + { + /* Copy the (possibly truncated) volume label and NULL-terminate it */ + RtlStringCbCopyNW(Volume->VolumeLabel, sizeof(Volume->VolumeLabel), + LabelInfo.VolumeLabel, LabelInfo.VolumeLabelLength); + } + else + { + DPRINT1("NtQueryVolumeInformationFile() failed, Status 0x%08lx\n", Status); + } + } + + /* Close the volume */ + if (VolumeHandle) + NtClose(VolumeHandle); + + return STATUS_SUCCESS; +} + +/** + * @brief + * Attempts to dismount the designated volume. + * + * @param[in,out] Volume + * The volume to dismount. + * + * @param[in] Force + * Whether the volume is forcibly dismounted, even + * if there are open handles to files on this volume. + * + * @return An NTSTATUS code indicating success or failure. + **/ +NTSTATUS +DismountVolume( + _Inout_ PVOLINFO Volume, + _In_ BOOLEAN Force) +{ + NTSTATUS Status, LockStatus; + IO_STATUS_BLOCK IoStatusBlock; + HANDLE VolumeHandle; + + /* If the volume is not mounted, just return success */ + if (!*Volume->FileSystem) + return STATUS_SUCCESS; + + /* Open the volume */ + Status = pOpenDeviceEx(Volume->DeviceName, &VolumeHandle, + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE); + if (!NT_SUCCESS(Status)) + { + DPRINT1("ERROR: Cannot open volume %S for dismounting! (Status 0x%lx)\n", + Volume->DeviceName, Status); + return Status; + } + + /* Lock the volume (succeeds only if there are no open handles to files) */ + LockStatus = NtFsControlFile(VolumeHandle, + NULL, NULL, NULL, + &IoStatusBlock, + FSCTL_LOCK_VOLUME, + NULL, 0, + NULL, 0); + if (!NT_SUCCESS(LockStatus)) + DPRINT1("WARNING: Failed to lock volume (Status 0x%lx)\n", LockStatus); + + /* Dismount the volume (succeeds even when lock fails and there are open handles) */ + Status = STATUS_ACCESS_DENIED; // Suppose dismount failure. + if (NT_SUCCESS(LockStatus) || Force) + { + Status = NtFsControlFile(VolumeHandle, + NULL, NULL, NULL, + &IoStatusBlock, + FSCTL_DISMOUNT_VOLUME, + NULL, 0, + NULL, 0); + if (!NT_SUCCESS(Status)) + DPRINT1("Failed to unmount volume (Status 0x%lx)\n", Status); + } + + /* Unlock the volume */ + if (NT_SUCCESS(LockStatus)) + { + LockStatus = NtFsControlFile(VolumeHandle, + NULL, NULL, NULL, + &IoStatusBlock, + FSCTL_UNLOCK_VOLUME, + NULL, 0, + NULL, 0); + if (!NT_SUCCESS(LockStatus)) + DPRINT1("Failed to unlock volume (Status 0x%lx)\n", LockStatus); + } + + /* Close the volume */ + NtClose(VolumeHandle); + + /* Reset some data only if dismount succeeded */ + if (NT_SUCCESS(Status)) + { + Volume->DriveLetter = UNICODE_NULL; + Volume->VolumeLabel[0] = UNICODE_NULL; + Volume->FileSystem[0] = UNICODE_NULL; + } + + return Status; +} + +/* EOF */ diff --git a/base/setup/lib/utils/volutil.h b/base/setup/lib/utils/volutil.h new file mode 100644 index 00000000000..2763bb89844 --- /dev/null +++ b/base/setup/lib/utils/volutil.h @@ -0,0 +1,48 @@ +/* + * PROJECT: ReactOS Setup Library + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: Volume utility functions + * COPYRIGHT: Copyright 2024 Hermès Bélusca-Maïto + */ + +#pragma once + +typedef struct _VOLINFO +{ + // WCHAR VolumeName[MAX_PATH]; ///< Name in the DOS/Win32 namespace: "\??\Volume{GUID}\" + WCHAR DeviceName[MAX_PATH]; ///< NT device name: "\Device\HarddiskVolumeN" + + WCHAR DriveLetter; + WCHAR VolumeLabel[20]; + WCHAR FileSystem[MAX_PATH+1]; + + // VOLUME_TYPE VolumeType; + // ULARGE_INTEGER Size; + // PVOLUME_DISK_EXTENTS Extents; +} VOLINFO, *PVOLINFO; + +/* RawFS "RAW" file system name */ +#define IS_RAWFS(fs) \ + ((fs)[0] == 'R' && (fs)[1] == 'A' && (fs)[2] == 'W' && (fs)[3] == 0) + +#define IsUnknown(VolInfo) \ + (!*(VolInfo)->FileSystem) + +#define IsUnformatted(VolInfo) \ + IS_RAWFS((VolInfo)->FileSystem) + +#define IsFormatted(VolInfo) \ + (!IsUnknown(VolInfo) && !IsUnformatted(VolInfo)) + + +NTSTATUS +MountVolume( + _Inout_ PVOLINFO Volume, + _In_opt_ UCHAR MbrPartitionType); + +NTSTATUS +DismountVolume( + _Inout_ PVOLINFO Volume, + _In_ BOOLEAN Force); + +/* EOF */ diff --git a/base/setup/reactos/drivepage.c b/base/setup/reactos/drivepage.c index 320e0c62fa3..6c9bcc2a963 100644 --- a/base/setup/reactos/drivepage.c +++ b/base/setup/reactos/drivepage.c @@ -571,6 +571,7 @@ PrintPartitionData( IN PDISKENTRY DiskEntry, IN PPARTENTRY PartEntry) { + PVOLINFO VolInfo = (PartEntry->Volume ? &PartEntry->Volume->Info : NULL); LARGE_INTEGER PartSize; HTLITEM htiPart; CHAR PartTypeString[32]; @@ -589,9 +590,9 @@ PrintPartitionData( StringCchPrintfW(LineBuffer, ARRAYSIZE(LineBuffer), // MUIGetString(STRING_HDDINFOUNK5), L"%s (%c%c)", - *PartEntry->VolumeLabel ? PartEntry->VolumeLabel : L"Partition", - (PartEntry->DriveLetter == 0) ? L'-' : PartEntry->DriveLetter, - (PartEntry->DriveLetter == 0) ? L'-' : L':'); + (VolInfo && *VolInfo->VolumeLabel) ? VolInfo->VolumeLabel : L"Partition", + !(VolInfo && VolInfo->DriveLetter) ? L'-' : VolInfo->DriveLetter, + !(VolInfo && VolInfo->DriveLetter) ? L'-' : L':'); } htiPart = TreeListAddItem(hWndList, htiParent, LineBuffer, @@ -1001,8 +1002,8 @@ DriveDlgProc( if (PartEntry->IsPartitioned && !IsContainerPartition(PartEntry->PartitionType) /* alternatively: PartEntry->PartitionNumber != 0 */ && - // !PartEntry->New && - (PartEntry->FormatState == Preformatted /* || PartEntry->FormatState == Formatted */)) + PartEntry->Volume && // !PartEntry->Volume->New && + (PartEntry->Volume->FormatState == Formatted)) { PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_BACK | PSWIZB_NEXT); } @@ -1079,11 +1080,12 @@ DisableWizNext: DiskEntry.HwFixedDiskNumber = 0; PartEntry.DiskEntry = &DiskEntry; PartEntry.PartitionNumber = 1; // 4; + PartEntry.Volume = NULL; /****/ Status = InitDestinationPaths(&pSetupData->USetupData, NULL, // pSetupData->USetupData.InstallationDirectory, - &PartEntry); + PartEntry.Volume); if (!NT_SUCCESS(Status)) { diff --git a/base/setup/reactos/reactos.c b/base/setup/reactos/reactos.c index 67034f93ea5..ee4f3cc8a2b 100644 --- a/base/setup/reactos/reactos.c +++ b/base/setup/reactos/reactos.c @@ -596,14 +596,14 @@ AddNTOSInstallationItem( IN SIZE_T cchBufferSize) { PNTOS_INSTALLATION NtOsInstall = (PNTOS_INSTALLATION)GetListEntryData(Entry); - PPARTENTRY PartEntry = NtOsInstall->PartEntry; + PVOLINFO VolInfo = (NtOsInstall->Volume ? &NtOsInstall->Volume->Info : NULL); - if (PartEntry && PartEntry->DriveLetter) + if (VolInfo && VolInfo->DriveLetter) { /* We have retrieved a partition that is mounted */ StringCchPrintfW(Buffer, cchBufferSize, L"%c:%s", - PartEntry->DriveLetter, + VolInfo->DriveLetter, NtOsInstall->PathComponent); } else diff --git a/base/setup/usetup/partlist.c b/base/setup/usetup/partlist.c index 570008638b2..a7eb50a8b59 100644 --- a/base/setup/usetup/partlist.c +++ b/base/setup/usetup/partlist.c @@ -154,6 +154,7 @@ PartitionDescription( size_t cchBufferSize = cchBuffer; ULONGLONG PartSize; PCSTR Unit; + PVOLINFO VolInfo = (PartEntry->Volume ? &PartEntry->Volume->Info : NULL); /* Get the partition size */ PartSize = GetPartEntrySizeInBytes(PartEntry); @@ -204,8 +205,8 @@ PartitionDescription( RtlStringCchPrintfExA(pBuffer, cchBufferSize, &pBuffer, &cchBufferSize, 0, "%c%c %c %s(%lu) ", - (PartEntry->DriveLetter == 0) ? '-' : (CHAR)PartEntry->DriveLetter, - (PartEntry->DriveLetter == 0) ? '-' : ':', + !(VolInfo && VolInfo->DriveLetter) ? '-' : (CHAR)VolInfo->DriveLetter, + !(VolInfo && VolInfo->DriveLetter) ? '-' : ':', PartEntry->BootIndicator ? '*' : ' ', PartEntry->LogicalPartition ? " " : "", // Optional indentation PartEntry->PartitionNumber); @@ -215,16 +216,15 @@ PartitionDescription( * (if any) and the file system name. Otherwise, display the partition * type if it's not a new partition. */ - if (!PartEntry->New && *PartEntry->FileSystem && - _wcsicmp(PartEntry->FileSystem, L"RAW") != 0) + if (VolInfo && IsFormatted(VolInfo)) { size_t cchLabelSize = 0; - if (*PartEntry->VolumeLabel) + if (*VolInfo->VolumeLabel) { RtlStringCchPrintfExA(pBuffer, cchBufferSize, &pBuffer, &cchLabelSize, 0, "\"%-.11S\" ", - PartEntry->VolumeLabel); + VolInfo->VolumeLabel); cchLabelSize = cchBufferSize - cchLabelSize; // Actual length of the label part. cchBufferSize -= cchLabelSize; // And reset cchBufferSize to what it should be. } @@ -237,7 +237,7 @@ PartitionDescription( /* The minimum length can be at most 11 since * cchLabelSize can be at most == 11 + 3 == 14 */ 25 - min(cchLabelSize, 25), - PartEntry->FileSystem); + VolInfo->FileSystem); } else { @@ -275,7 +275,7 @@ PartitionDescription( /* Show the remaining free space only if a FS is mounted */ // FIXME: We don't support that yet! #if 0 - if (*PartEntry->FileSystem) + if (VolInfo && *VolInfo->FileSystem) { RtlStringCchPrintfA(pBuffer, cchBufferSize, "%*s%6I64u %s (%6I64u %s %s)", diff --git a/base/setup/usetup/usetup.c b/base/setup/usetup/usetup.c index fb6de2eb614..7dd30bf468f 100644 --- a/base/setup/usetup/usetup.c +++ b/base/setup/usetup/usetup.c @@ -46,6 +46,8 @@ static USETUP_DATA USetupData; /* The partition where to perform the installation */ static PPARTENTRY InstallPartition = NULL; +// static PVOLENTRY InstallVolume = NULL; +#define InstallVolume (InstallPartition->Volume) /* * The system partition we will actually use. It can be different from * PartitionList->SystemPartition in case we don't support it, or we install @@ -57,6 +59,8 @@ static PPARTENTRY InstallPartition = NULL; * operation on them). */ static PPARTENTRY SystemPartition = NULL; +// static PVOLENTRY SystemVolume = NULL; +#define SystemVolume (SystemPartition->Volume) /* OTHER Stuff *****/ @@ -77,8 +81,8 @@ static enum { PartTypeExtended // MBR-disk container } PartCreateType = PartTypeData; -/* Flag set in PARTENTRY::New when a partition is created automatically */ -#define PARTITION_NEW_AUTOCREATE 0x80 +/* Flag set in VOLENTRY::New when a partition/volume is created automatically */ +#define VOLUME_NEW_AUTOCREATE 0x80 /* List of supported file systems for the partition to be formatted */ static PFILE_SYSTEM_LIST FileSystemList = NULL; @@ -508,14 +512,14 @@ GetNTOSInstallationName( IN SIZE_T cchBufferSize) { PNTOS_INSTALLATION NtOsInstall = (PNTOS_INSTALLATION)GetListEntryData(Entry); - PPARTENTRY PartEntry = NtOsInstall->PartEntry; + PVOLINFO VolInfo = (NtOsInstall->Volume ? &NtOsInstall->Volume->Info : NULL); - if (PartEntry && PartEntry->DriveLetter) + if (VolInfo && VolInfo->DriveLetter) { /* We have retrieved a partition that is mounted */ return RtlStringCchPrintfA(Buffer, cchBufferSize, "%C:%S \"%S\"", - PartEntry->DriveLetter, + VolInfo->DriveLetter, NtOsInstall->PathComponent, NtOsInstall->InstallationName); } @@ -1568,7 +1572,6 @@ SelectPartitionPage(PINPUT_RECORD Ir) DPRINT1("RepairUpdateFlag == TRUE, SelectPartition() returned FALSE, assert!\n"); ASSERT(FALSE); } - ASSERT(!IsContainerPartition(InstallPartition->PartitionType)); return START_PARTITION_OPERATIONS_PAGE; } @@ -1603,7 +1606,8 @@ SelectPartitionPage(PINPUT_RECORD Ir) CurrentPartition, 0ULL, 0); - CurrentPartition->New |= PARTITION_NEW_AUTOCREATE; + if (CurrentPartition->Volume) + CurrentPartition->Volume->New |= VOLUME_NEW_AUTOCREATE; // FIXME?? Aren't we going to enter an infinite loop, if this test fails?? if (!IsPartitionLargeEnough(CurrentPartition)) @@ -1735,7 +1739,8 @@ SelectPartitionPage(PINPUT_RECORD Ir) CurrentPartition, 0ULL, 0); - CurrentPartition->New |= PARTITION_NEW_AUTOCREATE; + if (CurrentPartition->Volume) + CurrentPartition->Volume->New |= VOLUME_NEW_AUTOCREATE; } if (!IsPartitionLargeEnough(CurrentPartition)) @@ -1781,28 +1786,23 @@ SelectPartitionPage(PINPUT_RECORD Ir) } else if (Ir->Event.KeyEvent.wVirtualKeyCode == 'D') /* D */ { - UNICODE_STRING CurrentPartitionU; - WCHAR PathBuffer[MAX_PATH]; - ASSERT(CurrentPartition != NULL); /* Ignore deletion in case this is not a partitioned entry */ if (!CurrentPartition->IsPartitioned) - { continue; - } // TODO: Do something similar before trying to format the partition? - if (!CurrentPartition->New && - !IsContainerPartition(CurrentPartition->PartitionType) && - CurrentPartition->FormatState != Unformatted) + if (CurrentPartition->Volume && !CurrentPartition->Volume->New && + (CurrentPartition->Volume->FormatState != Unformatted)) { + UNICODE_STRING CurrentPartitionU; + WCHAR PathBuffer[RTL_NUMBER_OF_FIELD(VOLINFO, DeviceName) + 1]; + ASSERT(CurrentPartition->PartitionNumber != 0); - RtlStringCchPrintfW(PathBuffer, ARRAYSIZE(PathBuffer), - L"\\Device\\Harddisk%lu\\Partition%lu\\", - CurrentPartition->DiskEntry->DiskNumber, - CurrentPartition->PartitionNumber); + RtlStringCchPrintfW(PathBuffer, _countof(PathBuffer), + L"%s\\", CurrentPartition->Volume->Info.DeviceName); RtlInitUnicodeString(&CurrentPartitionU, PathBuffer); /* @@ -2286,16 +2286,16 @@ StartPartitionOperationsPage(PINPUT_RECORD Ir) // /* Set the AUTOCREATE flag if the system partition was automatically created */ - if (SystemPartition->New) - SystemPartition->New |= PARTITION_NEW_AUTOCREATE; + if (SystemPartition->New && SystemVolume) + SystemVolume->New |= VOLUME_NEW_AUTOCREATE; CONSOLE_ClearScreen(); CONSOLE_Flush(); /* Apply all pending operations on partitions: formatting and checking */ Success = FsVolCommitOpsQueue(PartitionList, - SystemPartition, - InstallPartition, + SystemVolume, + InstallVolume, FsVolCallback, &FsVolContext); if (!Success) @@ -2358,41 +2358,43 @@ ResetFileSystemList(VOID) static FSVOL_OP SelectFileSystemPage( - IN PFSVOL_CONTEXT FsVolContext, - IN PPARTENTRY PartEntry) + _In_ PFSVOL_CONTEXT FsVolContext, + _In_ PVOLENTRY Volume) { PINPUT_RECORD Ir = FsVolContext->Ir; + PPARTENTRY PartEntry = Volume->PartEntry; PDISKENTRY DiskEntry = PartEntry->DiskEntry; PCWSTR DefaultFs; + BOOLEAN ForceFormat; CHAR LineBuffer[100]; DPRINT("SelectFileSystemPage()\n"); - ASSERT(PartEntry->IsPartitioned && PartEntry->PartitionNumber != 0); + ForceFormat = (Volume->New || Volume->FormatState == Unformatted); Restart: - /* Reset the file system list for each partition that is to be formatted */ + /* Reset the file system list for each volume that is to be formatted */ ResetFileSystemList(); CONSOLE_ClearScreen(); CONSOLE_Flush(); MUIDisplayPage(SELECT_FILE_SYSTEM_PAGE); - if (PartEntry->New & PARTITION_NEW_AUTOCREATE) + if (Volume->New & VOLUME_NEW_AUTOCREATE) { - PartEntry->New &= ~PARTITION_NEW_AUTOCREATE; + Volume->New &= ~VOLUME_NEW_AUTOCREATE; CONSOLE_SetTextXY(6, 8, MUIGetString(STRING_NEWPARTITION)); } - else if (PartEntry->New) + else if (Volume->New) { ULONG uID; - if (PartEntry == SystemPartition) // FormatSystemPartition + if (Volume == SystemVolume) uID = STRING_NONFORMATTEDSYSTEMPART; - else if (PartEntry == InstallPartition) // FormatInstallPartition + else if (Volume == InstallVolume) uID = STRING_NONFORMATTEDPART; - else // FormatOtherPartition + else uID = STRING_NONFORMATTEDOTHERPART; CONSOLE_SetTextXY(6, 8, MUIGetString(uID)); @@ -2410,7 +2412,7 @@ Restart: LineBuffer); /* Show "This Partition will be formatted next" only if it is unformatted */ - if (PartEntry->New || PartEntry->FormatState == Unformatted) + if (ForceFormat) CONSOLE_SetTextXY(6, 14, MUIGetString(STRING_PARTFORMAT)); ASSERT(!FileSystemList); @@ -2439,11 +2441,8 @@ Restart: } /* Create the file system list */ - // TODO: Display only the FSes compatible with the selected partition! - FileSystemList = CreateFileSystemList(6, 26, - PartEntry->New || - PartEntry->FormatState == Unformatted, - DefaultFs); + // TODO: Display only the FSes compatible with the selected volume! + FileSystemList = CreateFileSystemList(6, 26, ForceFormat, DefaultFs); if (!FileSystemList) { /* FIXME: show an error dialog */ @@ -2493,23 +2492,21 @@ Restart: { if (!FileSystemList->Selected->FileSystem) { - ASSERT(!PartEntry->New && PartEntry->FormatState != Unformatted); + /* The 'Keep existing filesystem' entry was chosen, + * the volume must be already formatted */ + ASSERT(!ForceFormat); - /* - * Skip formatting this partition. We will also ignore + /* Skip formatting this volume. We will also ignore * file system checks on it, unless it is either the - * system or the installation partition. - */ - if (PartEntry != SystemPartition && - PartEntry != InstallPartition) - { - PartEntry->NeedsCheck = FALSE; - } + * system or the installation volume. */ + if ((Volume != SystemVolume) && (Volume != InstallVolume)) + Volume->NeedsCheck = FALSE; + return FSVOL_SKIP; } else { - /* Format this partition */ + /* Format this volume */ return FSVOL_DOIT; } } @@ -2520,10 +2517,11 @@ Restart: static FSVOL_OP FormatPartitionPage( - IN PFSVOL_CONTEXT FsVolContext, - IN PPARTENTRY PartEntry) + _In_ PFSVOL_CONTEXT FsVolContext, + _In_ PVOLENTRY Volume) { PINPUT_RECORD Ir = FsVolContext->Ir; + PPARTENTRY PartEntry = Volume->PartEntry; PDISKENTRY DiskEntry = PartEntry->DiskEntry; CHAR LineBuffer[100]; @@ -2571,8 +2569,9 @@ Restart: static VOID CheckFileSystemPage( - IN PPARTENTRY PartEntry) + _In_ PVOLENTRY Volume) { + PPARTENTRY PartEntry = Volume->PartEntry; PDISKENTRY DiskEntry = PartEntry->DiskEntry; CHAR LineBuffer[100]; @@ -2693,7 +2692,8 @@ FsVolCallback( if (FmtInfo->ErrorStatus == STATUS_UNRECOGNIZED_VOLUME) { /* FIXME: show an error dialog */ - // MUIDisplayError(ERROR_FORMATTING_PARTITION, Ir, POPUP_WAIT_ANY_KEY, PathBuffer); + // MUIDisplayError(ERROR_FORMATTING_PARTITION, Ir, POPUP_WAIT_ANY_KEY, + // FmtInfo->Volume->Info.DeviceName); FsVolContext->NextPageOnAbort = QUIT_PAGE; return FSVOL_ABORT; } @@ -2737,16 +2737,9 @@ FsVolCallback( } else if (!NT_SUCCESS(FmtInfo->ErrorStatus)) { - WCHAR PathBuffer[MAX_PATH]; - - /** HACK!! **/ - RtlStringCchPrintfW(PathBuffer, ARRAYSIZE(PathBuffer), - L"\\Device\\Harddisk%lu\\Partition%lu", - FmtInfo->PartEntry->DiskEntry->DiskNumber, - FmtInfo->PartEntry->PartitionNumber); - DPRINT1("FormatPartition() failed: Status 0x%08lx\n", FmtInfo->ErrorStatus); - MUIDisplayError(ERROR_FORMATTING_PARTITION, Ir, POPUP_WAIT_ANY_KEY, PathBuffer); + MUIDisplayError(ERROR_FORMATTING_PARTITION, Ir, POPUP_WAIT_ANY_KEY, + FmtInfo->Volume->Info.DeviceName); FsVolContext->NextPageOnAbort = QUIT_PAGE; return FSVOL_ABORT; } @@ -2766,7 +2759,7 @@ FsVolCallback( "\n" " \x07 Press ENTER to continue Setup.\n" " \x07 Press F3 to quit Setup.", - ChkInfo->PartEntry->FileSystem); + ChkInfo->Volume->Info.FileSystem); PopupError(Buffer, MUIGetString(STRING_QUITCONTINUE), @@ -2820,12 +2813,12 @@ FsVolCallback( ASSERT((FSVOL_OP)Param2 == FSVOL_FORMAT); /* Select the file system */ - Result = SelectFileSystemPage(FsVolContext, FmtInfo->PartEntry); + Result = SelectFileSystemPage(FsVolContext, FmtInfo->Volume); if (Result != FSVOL_DOIT) return Result; /* Display the formatting page */ - Result = FormatPartitionPage(FsVolContext, FmtInfo->PartEntry); + Result = FormatPartitionPage(FsVolContext, FmtInfo->Volume); if (Result != FSVOL_DOIT) return Result; @@ -2849,7 +2842,7 @@ FsVolCallback( ASSERT((FSVOL_OP)Param2 == FSVOL_CHECK); - CheckFileSystemPage(ChkInfo->PartEntry); + CheckFileSystemPage(ChkInfo->Volume); StartCheck(ChkInfo); return FSVOL_DOIT; } @@ -2907,7 +2900,7 @@ InstallDirectoryPage(PINPUT_RECORD Ir) */ if ((RepairUpdateFlag || IsUnattendedSetup) && IsValidInstallDirectory(InstallDir)) { - Status = InitDestinationPaths(&USetupData, InstallDir, InstallPartition); + Status = InitDestinationPaths(&USetupData, InstallDir, InstallVolume); if (!NT_SUCCESS(Status)) { DPRINT1("InitDestinationPaths() failed: Status 0x%lx\n", Status); @@ -3019,7 +3012,7 @@ InstallDirectoryPage(PINPUT_RECORD Ir) return INSTALL_DIRECTORY_PAGE; } - Status = InitDestinationPaths(&USetupData, InstallDir, InstallPartition); + Status = InitDestinationPaths(&USetupData, InstallDir, InstallVolume); if (!NT_SUCCESS(Status)) { DPRINT1("InitDestinationPaths() failed: Status 0x%lx\n", Status); @@ -3426,7 +3419,7 @@ RegistryPage(PINPUT_RECORD Ir) Error = UpdateRegistry(&USetupData, RepairUpdateFlag, PartitionList, - InstallPartition->DriveLetter, + InstallVolume->Info.DriveLetter, SelectedLanguageId, RegistryStatus, &s_SubstSettings); @@ -3667,11 +3660,11 @@ BootLoaderHardDiskPage(PINPUT_RECORD Ir) Status = InstallVBRToPartition(&USetupData.SystemRootPath, &USetupData.SourceRootPath, &USetupData.DestinationArcPath, - SystemPartition->FileSystem); + SystemVolume->Info.FileSystem); if (!NT_SUCCESS(Status)) { MUIDisplayError(ERROR_WRITE_BOOT, Ir, POPUP_WAIT_ENTER, - SystemPartition->FileSystem); + SystemVolume->Info.FileSystem); return FALSE; } @@ -3697,11 +3690,11 @@ BootLoaderHardDiskPage(PINPUT_RECORD Ir) Status = InstallVBRToPartition(&USetupData.SystemRootPath, &USetupData.SourceRootPath, &USetupData.DestinationArcPath, - SystemPartition->FileSystem); + SystemVolume->Info.FileSystem); if (!NT_SUCCESS(Status)) { MUIDisplayError(ERROR_WRITE_BOOT, Ir, POPUP_WAIT_ENTER, - SystemPartition->FileSystem); + SystemVolume->Info.FileSystem); return FALSE; } }