xref: /reactos/base/setup/lib/utils/volutil.c (revision 0c2cdcae)
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
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
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