mirror of
https://github.com/reactos/reactos.git
synced 2025-05-01 11:39:58 +00:00
227 lines
7.4 KiB
C
227 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 */
|