mirror of
https://github.com/reactos/reactos.git
synced 2025-02-23 17:05:46 +00:00

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.
226 lines
7.4 KiB
C
226 lines
7.4 KiB
C
/*
|
|
* 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 */
|