xref: /reactos/base/setup/lib/utils/fsrec.c (revision 40462c92)
1 /*
2  * PROJECT:     ReactOS Setup Library
3  * LICENSE:     GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
4  * PURPOSE:     Filesystem Recognition support functions,
5  *              using NT OS functionality.
6  * COPYRIGHT:   Copyright 2017-2020 Hermes Belusca-Maito
7  */
8 
9 /* INCLUDES *****************************************************************/
10 
11 #include "precomp.h"
12 
13 #include "fsrec.h"
14 
15 #define NDEBUG
16 #include <debug.h>
17 
18 
19 /* FUNCTIONS ****************************************************************/
20 
21 /* NOTE: Ripped & adapted from base/system/autochk/autochk.c */
22 NTSTATUS
23 GetFileSystemNameByHandle(
24     IN HANDLE PartitionHandle,
25     IN OUT PWSTR FileSystemName,
26     IN SIZE_T FileSystemNameSize)
27 {
28     NTSTATUS Status;
29     IO_STATUS_BLOCK IoStatusBlock;
30     UCHAR Buffer[sizeof(FILE_FS_ATTRIBUTE_INFORMATION) + MAX_PATH * sizeof(WCHAR)];
31     PFILE_FS_ATTRIBUTE_INFORMATION FileFsAttribute = (PFILE_FS_ATTRIBUTE_INFORMATION)Buffer;
32 
33     /* Retrieve the FS attributes */
34     Status = NtQueryVolumeInformationFile(PartitionHandle,
35                                           &IoStatusBlock,
36                                           FileFsAttribute,
37                                           sizeof(Buffer),
38                                           FileFsAttributeInformation);
39     if (!NT_SUCCESS(Status))
40     {
41         DPRINT1("NtQueryVolumeInformationFile failed, Status 0x%08lx\n", Status);
42         return Status;
43     }
44 
45     if (FileSystemNameSize < FileFsAttribute->FileSystemNameLength + sizeof(WCHAR))
46         return STATUS_BUFFER_TOO_SMALL;
47 
48     return RtlStringCbCopyNW(FileSystemName, FileSystemNameSize,
49                              FileFsAttribute->FileSystemName,
50                              FileFsAttribute->FileSystemNameLength);
51 }
52 
53 NTSTATUS
54 GetFileSystemName_UStr(
55     IN PUNICODE_STRING PartitionPath,
56     IN OUT PWSTR FileSystemName,
57     IN SIZE_T FileSystemNameSize)
58 {
59     NTSTATUS Status;
60     OBJECT_ATTRIBUTES ObjectAttributes;
61     HANDLE PartitionHandle;
62     IO_STATUS_BLOCK IoStatusBlock;
63 
64     /* Open the partition */
65     InitializeObjectAttributes(&ObjectAttributes,
66                                PartitionPath,
67                                OBJ_CASE_INSENSITIVE,
68                                NULL,
69                                NULL);
70     Status = NtOpenFile(&PartitionHandle,
71                         FILE_GENERIC_READ /* | SYNCHRONIZE */,
72                         &ObjectAttributes,
73                         &IoStatusBlock,
74                         FILE_SHARE_READ | FILE_SHARE_WRITE,
75                         0 /* FILE_SYNCHRONOUS_IO_NONALERT */);
76     if (!NT_SUCCESS(Status))
77     {
78         DPRINT1("Failed to open partition '%wZ', Status 0x%08lx\n", PartitionPath, Status);
79         return Status;
80     }
81 
82     /* Retrieve the FS attributes */
83     Status = GetFileSystemNameByHandle(PartitionHandle, FileSystemName, FileSystemNameSize);
84     if (!NT_SUCCESS(Status))
85     {
86         DPRINT1("GetFileSystemNameByHandle() failed for partition '%wZ', Status 0x%08lx\n",
87                 PartitionPath, Status);
88     }
89 
90     /* Close the partition */
91     NtClose(PartitionHandle);
92 
93     return Status;
94 }
95 
96 NTSTATUS
97 GetFileSystemName(
98     IN PCWSTR Partition,
99     IN OUT PWSTR FileSystemName,
100     IN SIZE_T FileSystemNameSize)
101 {
102     UNICODE_STRING PartitionPath;
103 
104     RtlInitUnicodeString(&PartitionPath, Partition);
105     return GetFileSystemName_UStr(&PartitionPath,
106                                   FileSystemName,
107                                   FileSystemNameSize);
108 }
109 
110 NTSTATUS
111 InferFileSystemByHandle(
112     IN HANDLE PartitionHandle,
113     IN UCHAR PartitionType,
114     IN OUT PWSTR FileSystemName,
115     IN SIZE_T FileSystemNameSize)
116 {
117     NTSTATUS Status;
118 
119     if (FileSystemNameSize < sizeof(WCHAR))
120         return STATUS_BUFFER_TOO_SMALL;
121 
122     *FileSystemName = L'\0';
123 
124     /* Try to infer a file system using NT file system recognition */
125     Status = GetFileSystemNameByHandle(PartitionHandle,
126                                        FileSystemName,
127                                        FileSystemNameSize);
128     if (NT_SUCCESS(Status) && *FileSystemName)
129     {
130         goto Quit;
131     }
132 
133     /*
134      * Try to infer a preferred file system for this partition, given its ID.
135      *
136      * WARNING: This is partly a hack, since partitions with the same ID can
137      * be formatted with different file systems: for example, usual Linux
138      * partitions that are formatted in EXT2/3/4, ReiserFS, etc... have the
139      * same partition ID 0x83.
140      *
141      * The proper fix is to make a function that detects the existing FS
142      * from a given partition (not based on the partition ID).
143      * On the contrary, for unformatted partitions with a given ID, the
144      * following code is OK.
145      */
146     if ((PartitionType == PARTITION_FAT_12) ||
147         (PartitionType == PARTITION_FAT_16) ||
148         (PartitionType == PARTITION_HUGE  ) ||
149         (PartitionType == PARTITION_XINT13))
150     {
151         /* FAT12 or FAT16 */
152         Status = RtlStringCbCopyW(FileSystemName, FileSystemNameSize, L"FAT");
153     }
154     else if ((PartitionType == PARTITION_FAT32) ||
155              (PartitionType == PARTITION_FAT32_XINT13))
156     {
157         Status = RtlStringCbCopyW(FileSystemName, FileSystemNameSize, L"FAT32");
158     }
159     else if (PartitionType == PARTITION_LINUX)
160     {
161         // WARNING: See the warning above.
162         /* Could also be EXT2/3/4, ReiserFS, ... */
163         Status = RtlStringCbCopyW(FileSystemName, FileSystemNameSize, L"BTRFS");
164     }
165     else if (PartitionType == PARTITION_IFS)
166     {
167         // WARNING: See the warning above.
168         /* Could also be HPFS */
169         Status = RtlStringCbCopyW(FileSystemName, FileSystemNameSize, L"NTFS");
170     }
171 
172 Quit:
173     if (*FileSystemName)
174     {
175         // WARNING: We cannot write on this FS yet!
176         if (PartitionType == PARTITION_IFS)
177         {
178             DPRINT1("Recognized file system '%S' that doesn't have write support yet!\n",
179                     FileSystemName);
180         }
181     }
182 
183     DPRINT1("InferFileSystem -- PartitionType: 0x%02X ; FileSystem (guessed): %S\n",
184             PartitionType, *FileSystemName ? FileSystemName : L"None");
185 
186     return Status;
187 }
188 
189 NTSTATUS
190 InferFileSystem(
191     IN PCWSTR Partition,
192     IN UCHAR PartitionType,
193     IN OUT PWSTR FileSystemName,
194     IN SIZE_T FileSystemNameSize)
195 {
196     NTSTATUS Status;
197     UNICODE_STRING PartitionPath;
198     OBJECT_ATTRIBUTES ObjectAttributes;
199     HANDLE PartitionHandle;
200     IO_STATUS_BLOCK IoStatusBlock;
201 
202     /* Open the partition */
203     RtlInitUnicodeString(&PartitionPath, Partition);
204     InitializeObjectAttributes(&ObjectAttributes,
205                                &PartitionPath,
206                                OBJ_CASE_INSENSITIVE,
207                                NULL,
208                                NULL);
209     Status = NtOpenFile(&PartitionHandle,
210                         FILE_GENERIC_READ /* | SYNCHRONIZE */,
211                         &ObjectAttributes,
212                         &IoStatusBlock,
213                         FILE_SHARE_READ | FILE_SHARE_WRITE,
214                         0 /* FILE_SYNCHRONOUS_IO_NONALERT */);
215     if (!NT_SUCCESS(Status))
216     {
217         DPRINT1("Failed to open partition '%wZ', Status 0x%08lx\n", &PartitionPath, Status);
218         return Status;
219     }
220 
221     /* Retrieve the FS */
222     Status = InferFileSystemByHandle(PartitionHandle,
223                                      PartitionType,
224                                      FileSystemName,
225                                      FileSystemNameSize);
226 
227     /* Close the partition */
228     NtClose(PartitionHandle);
229 
230     return Status;
231 }
232 
233 UCHAR
234 FileSystemToPartitionType(
235     IN PCWSTR FileSystem,
236     IN PULARGE_INTEGER StartSector,
237     IN PULARGE_INTEGER SectorCount)
238 {
239     ASSERT(FileSystem && StartSector && SectorCount);
240 
241     if (wcsicmp(FileSystem, L"FAT")   == 0 ||
242         wcsicmp(FileSystem, L"FAT32") == 0 ||
243         wcsicmp(FileSystem, L"RAW")   == 0)
244     {
245         if (SectorCount->QuadPart < 8192)
246         {
247             /* FAT12 CHS partition (disk is smaller than 4.1MB) */
248             return PARTITION_FAT_12;
249         }
250         else if (StartSector->QuadPart < 1450560)
251         {
252             /* Partition starts below the 8.4GB boundary ==> CHS partition */
253 
254             if (SectorCount->QuadPart < 65536)
255             {
256                 /* FAT16 CHS partition (partition size < 32MB) */
257                 return PARTITION_FAT_16;
258             }
259             else if (SectorCount->QuadPart < 1048576)
260             {
261                 /* FAT16 CHS partition (partition size < 512MB) */
262                 return PARTITION_HUGE;
263             }
264             else
265             {
266                 /* FAT32 CHS partition (partition size >= 512MB) */
267                 return PARTITION_FAT32;
268             }
269         }
270         else
271         {
272             /* Partition starts above the 8.4GB boundary ==> LBA partition */
273 
274             if (SectorCount->QuadPart < 1048576)
275             {
276                 /* FAT16 LBA partition (partition size < 512MB) */
277                 return PARTITION_XINT13;
278             }
279             else
280             {
281                 /* FAT32 LBA partition (partition size >= 512MB) */
282                 return PARTITION_FAT32_XINT13;
283             }
284         }
285     }
286     else if (wcsicmp(FileSystem, L"NTFS") == 0)
287     {
288         return PARTITION_IFS;
289     }
290     else if (wcsicmp(FileSystem, L"BTRFS") == 0 ||
291              wcsicmp(FileSystem, L"EXT2")  == 0 ||
292              wcsicmp(FileSystem, L"EXT3")  == 0 ||
293              wcsicmp(FileSystem, L"EXT4")  == 0 ||
294              wcsicmp(FileSystem, L"FFS")   == 0 ||
295              wcsicmp(FileSystem, L"REISERFS") == 0)
296     {
297         return PARTITION_LINUX;
298     }
299     else
300     {
301         /* Unknown file system */
302         DPRINT1("Unknown file system '%S'\n", FileSystem);
303         return PARTITION_ENTRY_UNUSED;
304     }
305 }
306 
307 /* EOF */
308