1 /*
2 * PROJECT: ReactOS Setup Library
3 * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
4 * PURPOSE: Volume utility functions
5 * COPYRIGHT: Copyright 2024 Hermès Bélusca-Maïto <hermes.belusca-maito@reactos.org>
6 */
7
8 /* INCLUDES ******************************************************************/
9
10 #include "precomp.h"
11
12 #include "volutil.h"
13 #include "fsrec.h"
14 #include "devutils.h"
15
16 #define NDEBUG
17 #include <debug.h>
18
19
20 /* FUNCTIONS *****************************************************************/
21
22 NTSTATUS
MountVolume(_Inout_ PVOLINFO Volume,_In_opt_ UCHAR MbrPartitionType)23 MountVolume(
24 _Inout_ PVOLINFO Volume,
25 _In_opt_ UCHAR MbrPartitionType)
26 {
27 NTSTATUS Status;
28 HANDLE VolumeHandle;
29
30 /* If the volume is already mounted, just return success */
31 if (*Volume->FileSystem)
32 return STATUS_SUCCESS;
33
34 /* Try to open the volume so as to mount it */
35 VolumeHandle = NULL;
36 Status = pOpenDevice(Volume->DeviceName, &VolumeHandle);
37 if (!NT_SUCCESS(Status))
38 {
39 DPRINT1("pOpenDevice() failed, Status 0x%08lx\n", Status);
40
41 /* We failed, reset some data and bail out */
42 Volume->DriveLetter = UNICODE_NULL;
43 Volume->VolumeLabel[0] = UNICODE_NULL;
44 Volume->FileSystem[0] = UNICODE_NULL;
45
46 return Status;
47 }
48 ASSERT(VolumeHandle);
49
50 /* We don't have a FS, try to guess one */
51 Status = InferFileSystem(NULL, VolumeHandle,
52 Volume->FileSystem,
53 sizeof(Volume->FileSystem));
54 if (!NT_SUCCESS(Status))
55 DPRINT1("InferFileSystem() failed, Status 0x%08lx\n", Status);
56
57 if (*Volume->FileSystem)
58 {
59 /*
60 * Handle volume mounted with RawFS: it is
61 * either unformatted or has an unknown format.
62 */
63 if (IsUnformatted(Volume)) // FileSystem is "RAW"
64 {
65 /*
66 * True unformatted partitions on NT are created with their
67 * partition type set to either one of the following values,
68 * and are mounted with RawFS. This is done this way since we
69 * are assured to have FAT support, which is the only FS that
70 * uses these partition types. Therefore, having a partition
71 * mounted with RawFS and with these partition types means that
72 * the FAT FS was unable to mount it beforehand and thus the
73 * partition is unformatted.
74 * However, any partition mounted by RawFS that does NOT have
75 * any of these partition types must be considered as having
76 * an unknown format.
77 */
78 if (MbrPartitionType == PARTITION_FAT_12 ||
79 MbrPartitionType == PARTITION_FAT_16 ||
80 MbrPartitionType == PARTITION_HUGE ||
81 MbrPartitionType == PARTITION_XINT13 ||
82 MbrPartitionType == PARTITION_FAT32 ||
83 MbrPartitionType == PARTITION_FAT32_XINT13)
84 {
85 /* The volume is unformatted */
86 }
87 else
88 {
89 /* Close the volume before dismounting */
90 NtClose(VolumeHandle);
91 VolumeHandle = NULL;
92 /*
93 * Dismount the volume since RawFS owns it, and reset its
94 * format (it is unknown, may or may not be actually formatted).
95 */
96 DismountVolume(Volume, TRUE);
97 Volume->FileSystem[0] = UNICODE_NULL;
98 }
99 }
100 /* Else, the volume is formatted */
101 }
102 /* Else, the volume has an unknown format */
103
104 /* Retrieve the volume label */
105 if (VolumeHandle)
106 {
107 IO_STATUS_BLOCK IoStatusBlock;
108 struct
109 {
110 FILE_FS_VOLUME_INFORMATION;
111 WCHAR Data[255];
112 } LabelInfo;
113
114 Status = NtQueryVolumeInformationFile(VolumeHandle,
115 &IoStatusBlock,
116 &LabelInfo,
117 sizeof(LabelInfo),
118 FileFsVolumeInformation);
119 if (NT_SUCCESS(Status))
120 {
121 /* Copy the (possibly truncated) volume label and NULL-terminate it */
122 RtlStringCbCopyNW(Volume->VolumeLabel, sizeof(Volume->VolumeLabel),
123 LabelInfo.VolumeLabel, LabelInfo.VolumeLabelLength);
124 }
125 else
126 {
127 DPRINT1("NtQueryVolumeInformationFile() failed, Status 0x%08lx\n", Status);
128 }
129 }
130
131 /* Close the volume */
132 if (VolumeHandle)
133 NtClose(VolumeHandle);
134
135 return STATUS_SUCCESS;
136 }
137
138 /**
139 * @brief
140 * Attempts to dismount the designated volume.
141 *
142 * @param[in,out] Volume
143 * The volume to dismount.
144 *
145 * @param[in] Force
146 * Whether the volume is forcibly dismounted, even
147 * if there are open handles to files on this volume.
148 *
149 * @return An NTSTATUS code indicating success or failure.
150 **/
151 NTSTATUS
DismountVolume(_Inout_ PVOLINFO Volume,_In_ BOOLEAN Force)152 DismountVolume(
153 _Inout_ PVOLINFO Volume,
154 _In_ BOOLEAN Force)
155 {
156 NTSTATUS Status, LockStatus;
157 IO_STATUS_BLOCK IoStatusBlock;
158 HANDLE VolumeHandle;
159
160 /* If the volume is not mounted, just return success */
161 if (!*Volume->FileSystem)
162 return STATUS_SUCCESS;
163
164 /* Open the volume */
165 Status = pOpenDeviceEx(Volume->DeviceName, &VolumeHandle,
166 GENERIC_READ | GENERIC_WRITE,
167 FILE_SHARE_READ | FILE_SHARE_WRITE);
168 if (!NT_SUCCESS(Status))
169 {
170 DPRINT1("ERROR: Cannot open volume %S for dismounting! (Status 0x%lx)\n",
171 Volume->DeviceName, Status);
172 return Status;
173 }
174
175 /* Lock the volume (succeeds only if there are no open handles to files) */
176 LockStatus = NtFsControlFile(VolumeHandle,
177 NULL, NULL, NULL,
178 &IoStatusBlock,
179 FSCTL_LOCK_VOLUME,
180 NULL, 0,
181 NULL, 0);
182 if (!NT_SUCCESS(LockStatus))
183 DPRINT1("WARNING: Failed to lock volume (Status 0x%lx)\n", LockStatus);
184
185 /* Dismount the volume (succeeds even when lock fails and there are open handles) */
186 Status = STATUS_ACCESS_DENIED; // Suppose dismount failure.
187 if (NT_SUCCESS(LockStatus) || Force)
188 {
189 Status = NtFsControlFile(VolumeHandle,
190 NULL, NULL, NULL,
191 &IoStatusBlock,
192 FSCTL_DISMOUNT_VOLUME,
193 NULL, 0,
194 NULL, 0);
195 if (!NT_SUCCESS(Status))
196 DPRINT1("Failed to unmount volume (Status 0x%lx)\n", Status);
197 }
198
199 /* Unlock the volume */
200 if (NT_SUCCESS(LockStatus))
201 {
202 LockStatus = NtFsControlFile(VolumeHandle,
203 NULL, NULL, NULL,
204 &IoStatusBlock,
205 FSCTL_UNLOCK_VOLUME,
206 NULL, 0,
207 NULL, 0);
208 if (!NT_SUCCESS(LockStatus))
209 DPRINT1("Failed to unlock volume (Status 0x%lx)\n", LockStatus);
210 }
211
212 /* Close the volume */
213 NtClose(VolumeHandle);
214
215 /* Reset some data only if dismount succeeded */
216 if (NT_SUCCESS(Status))
217 {
218 Volume->DriveLetter = UNICODE_NULL;
219 Volume->VolumeLabel[0] = UNICODE_NULL;
220 Volume->FileSystem[0] = UNICODE_NULL;
221 }
222
223 return Status;
224 }
225
226 /* EOF */
227