[SETUPLIB][REACTOS][USETUP] Split FS-volume-specific functionality from partitions (#7258)

CORE-13525

This greatly helps in reducing code complexity in some areas: code that
previously iterated over all partitions of a given disk, just to find
which ones were partitioned and contained a valid file system, now just
have to iterate over mounted volumes.
See in particular, `lib/utils/osdetect.c` and `lib/fsutil.c` .

- Remove FORMATSTATE "Preformatted" enum value;
- Cleanup osdetect code after introducing Volume support;
- Some simplifications for FormatState.

- Differentiate between 'new' partition and 'new' volume:

  * "New" partition: it has been created and added in the cached list,
    but not yet actually written into the disk.

  * "New" volume: newly-created volume (may be backed by a partition or
    not), not yet formatted. May exist on either new, or not new partition,
    or elsewhere.

- Cache partition and volume NT device names.

  These do not change across repartitioning operations, as long as the
  partition or the filesystem volume hasn't been deleted/recreated.
  This avoids doing \Device\Harddisk%u\Partition%u sprintf's everytime
  we need to retrieve the given partition or volume device name.

  When a partition/fileysystem volume is "virtually" created (i.e. in
  the partition list, but not yet committed to disk and exposed to the
  OS), no device partition number and device name are available yet.
  In particular, validate that no manipulation of \Device\HarddiskM\Partition0
  (i.e. the whole disk) is being made.
This commit is contained in:
Hermès Bélusca-Maïto 2024-05-18 23:09:16 +02:00
parent 0f8dc6b2df
commit 6f15802af7
No known key found for this signature in database
GPG Key ID: 3B2539C65E7B93D0
15 changed files with 1017 additions and 871 deletions

View File

@ -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

View File

@ -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,

View File

@ -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);

View File

@ -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);

View File

@ -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

View File

@ -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 <hermes.belusca-maito@reactos.org>
*/
/* 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

View File

@ -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 <hermes.belusca-maito@reactos.org>
*/
#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) :

File diff suppressed because it is too large Load Diff

View File

@ -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 <hermes.belusca-maito@reactos.org>
*/
#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(

View File

@ -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 <hermes.belusca-maito@reactos.org>
*/
/* INCLUDES ******************************************************************/
#include "precomp.h"
#include "volutil.h"
#include "fsrec.h"
#include "devutils.h"
#define NDEBUG
#include <debug.h>
/* 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 */

View File

@ -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 <hermes.belusca-maito@reactos.org>
*/
#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 */

View File

@ -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))
{

View File

@ -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

View File

@ -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)",

View File

@ -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;
}
}