[SETUPLIB] Improve the bootloader 'validity' checks -- Addendum to f06734e5 (r74512).
[reactos.git] / base / setup / lib / fsutil.c
1 /*
2 * PROJECT: ReactOS Setup Library
3 * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
4 * PURPOSE: Filesystem support functions
5 * COPYRIGHT: Copyright 2003-2018 Casper S. Hornstrup (chorns@users.sourceforge.net)
6 * Copyright 2017-2018 Hermes Belusca-Maito
7 */
8
9 //
10 // This is basically the code for listing available FileSystem providers
11 // (currently hardcoded in a list), and for performing a basic FileSystem
12 // recognition for a given disk partition.
13 //
14 // See also: https://git.reactos.org/?p=reactos.git;a=blob;f=reactos/dll/win32/fmifs/init.c;h=e895f5ef9cae4806123f6bbdd3dfed37ec1c8d33;hb=b9db9a4e377a2055f635b2fb69fef4e1750d219c
15 // for how to get FS providers in a dynamic way. In the (near) future we may
16 // consider merging some of this code with us into a fmifs / fsutil / fslib library...
17 //
18
19 /* INCLUDES *****************************************************************/
20
21 #include "precomp.h"
22
23 #include "fsutil.h"
24 #include "partlist.h"
25
26 #include <fslib/vfatlib.h>
27 #include <fslib/btrfslib.h>
28 // #include <fslib/ext2lib.h>
29 // #include <fslib/ntfslib.h>
30
31 #define NDEBUG
32 #include <debug.h>
33
34
35 FILE_SYSTEM RegisteredFileSystems[] =
36 {
37 { L"FAT" , VfatFormat, VfatChkdsk },
38 // { L"FAT32", VfatFormat, VfatChkdsk },
39 #if 0
40 { L"FATX" , VfatxFormat, VfatxChkdsk },
41 { L"NTFS" , NtfsFormat, NtfsChkdsk },
42
43 { L"EXT2" , Ext2Format, Ext2Chkdsk },
44 { L"EXT3" , Ext2Format, Ext2Chkdsk },
45 { L"EXT4" , Ext2Format, Ext2Chkdsk },
46 #endif
47 { L"BTRFS", BtrfsFormatEx, BtrfsChkdskEx },
48 #if 0
49 { L"FFS" , FfsFormat , FfsChkdsk },
50 { L"REISERFS", ReiserfsFormat, ReiserfsChkdsk },
51 #endif
52 };
53
54
55 /* FUNCTIONS ****************************************************************/
56
57 PFILE_SYSTEM
58 GetRegisteredFileSystems(OUT PULONG Count)
59 {
60 *Count = ARRAYSIZE(RegisteredFileSystems);
61 return RegisteredFileSystems;
62 }
63
64 PFILE_SYSTEM
65 GetFileSystemByName(
66 // IN PFILE_SYSTEM_LIST List,
67 IN PCWSTR FileSystemName)
68 {
69 #if 0 // Reenable when the list of registered FSes will again be dynamic
70
71 PLIST_ENTRY ListEntry;
72 PFILE_SYSTEM_ITEM Item;
73
74 ListEntry = List->ListHead.Flink;
75 while (ListEntry != &List->ListHead)
76 {
77 Item = CONTAINING_RECORD(ListEntry, FILE_SYSTEM_ITEM, ListEntry);
78 if (Item->FileSystemName && wcsicmp(FileSystemName, Item->FileSystemName) == 0)
79 return Item;
80
81 ListEntry = ListEntry->Flink;
82 }
83
84 #else
85
86 ULONG Count;
87 PFILE_SYSTEM FileSystems;
88
89 FileSystems = GetRegisteredFileSystems(&Count);
90 if (!FileSystems || Count == 0)
91 return NULL;
92
93 while (Count--)
94 {
95 if (FileSystems->FileSystemName && wcsicmp(FileSystemName, FileSystems->FileSystemName) == 0)
96 return FileSystems;
97
98 ++FileSystems;
99 }
100
101 #endif
102
103 return NULL;
104 }
105
106
107 //
108 // FileSystem recognition (using NT OS functionality)
109 //
110
111 #if 0 // FIXME: To be fully enabled when our storage stack & al. will work better!
112
113 /* NOTE: Ripped & adapted from base/system/autochk/autochk.c */
114 static NTSTATUS
115 _MyGetFileSystem(
116 IN struct _PARTENTRY* PartEntry,
117 IN OUT PWSTR FileSystemName,
118 IN SIZE_T FileSystemNameSize)
119 {
120 NTSTATUS Status;
121 HANDLE FileHandle;
122 IO_STATUS_BLOCK IoStatusBlock;
123 PFILE_FS_ATTRIBUTE_INFORMATION FileFsAttribute;
124 UCHAR Buffer[sizeof(FILE_FS_ATTRIBUTE_INFORMATION) + MAX_PATH * sizeof(WCHAR)];
125
126 OBJECT_ATTRIBUTES ObjectAttributes;
127 UNICODE_STRING PartitionRootPath;
128 WCHAR PathBuffer[MAX_PATH];
129
130 FileFsAttribute = (PFILE_FS_ATTRIBUTE_INFORMATION)Buffer;
131
132 /* Set PartitionRootPath */
133 RtlStringCchPrintfW(PathBuffer, ARRAYSIZE(PathBuffer),
134 // L"\\Device\\Harddisk%lu\\Partition%lu", // Should work! But because ReactOS sucks atm. it actually doesn't work!!
135 L"\\Device\\Harddisk%lu\\Partition%lu\\", // HACK: Use this as a temporary hack!
136 PartEntry->DiskEntry->DiskNumber,
137 PartEntry->PartitionNumber);
138 RtlInitUnicodeString(&PartitionRootPath, PathBuffer);
139 DPRINT("PartitionRootPath: %wZ\n", &PartitionRootPath);
140
141 /* Open the partition */
142 InitializeObjectAttributes(&ObjectAttributes,
143 &PartitionRootPath,
144 OBJ_CASE_INSENSITIVE,
145 NULL,
146 NULL);
147 Status = NtOpenFile(&FileHandle, // PartitionHandle,
148 FILE_GENERIC_READ /* | SYNCHRONIZE */,
149 &ObjectAttributes,
150 &IoStatusBlock,
151 FILE_SHARE_READ,
152 0 /* FILE_SYNCHRONOUS_IO_NONALERT */);
153 if (!NT_SUCCESS(Status))
154 {
155 DPRINT1("Failed to open partition %wZ, Status 0x%08lx\n", &PartitionRootPath, Status);
156 return Status;
157 }
158
159 /* Retrieve the FS attributes */
160 Status = NtQueryVolumeInformationFile(FileHandle,
161 &IoStatusBlock,
162 FileFsAttribute,
163 sizeof(Buffer),
164 FileFsAttributeInformation);
165 NtClose(FileHandle);
166
167 if (!NT_SUCCESS(Status))
168 {
169 DPRINT1("NtQueryVolumeInformationFile failed for partition %wZ, Status 0x%08lx\n", &PartitionRootPath, Status);
170 return Status;
171 }
172
173 if (FileSystemNameSize * sizeof(WCHAR) < FileFsAttribute->FileSystemNameLength + sizeof(WCHAR))
174 return STATUS_BUFFER_TOO_SMALL;
175
176 RtlCopyMemory(FileSystemName,
177 FileFsAttribute->FileSystemName,
178 FileFsAttribute->FileSystemNameLength);
179 FileSystemName[FileFsAttribute->FileSystemNameLength / sizeof(WCHAR)] = UNICODE_NULL;
180
181 return STATUS_SUCCESS;
182 }
183
184 #endif
185
186 PFILE_SYSTEM
187 GetFileSystem(
188 // IN PFILE_SYSTEM_LIST FileSystemList,
189 IN struct _PARTENTRY* PartEntry)
190 {
191 PFILE_SYSTEM CurrentFileSystem;
192 PWSTR FileSystemName = NULL;
193 #if 0 // For code temporarily disabled below
194 NTSTATUS Status;
195 WCHAR FsRecFileSystemName[MAX_PATH];
196 #endif
197
198 CurrentFileSystem = PartEntry->FileSystem;
199
200 /* We have a file system, return it */
201 if (CurrentFileSystem != NULL && CurrentFileSystem->FileSystemName != NULL)
202 return CurrentFileSystem;
203
204 DPRINT1("File system not found, try to guess one...\n");
205
206 CurrentFileSystem = NULL;
207
208 #if 0 // This is an example of old code...
209
210 if ((PartEntry->PartitionType == PARTITION_FAT_12) ||
211 (PartEntry->PartitionType == PARTITION_FAT_16) ||
212 (PartEntry->PartitionType == PARTITION_HUGE) ||
213 (PartEntry->PartitionType == PARTITION_XINT13) ||
214 (PartEntry->PartitionType == PARTITION_FAT32) ||
215 (PartEntry->PartitionType == PARTITION_FAT32_XINT13))
216 {
217 if (CheckFatFormat())
218 FileSystemName = L"FAT";
219 else
220 FileSystemName = NULL;
221 }
222 else if (PartEntry->PartitionType == PARTITION_EXT2)
223 {
224 if (CheckExt2Format())
225 FileSystemName = L"EXT2";
226 else
227 FileSystemName = NULL;
228 }
229 else if (PartEntry->PartitionType == PARTITION_IFS)
230 {
231 if (CheckNtfsFormat())
232 FileSystemName = L"NTFS";
233 else if (CheckHpfsFormat())
234 FileSystemName = L"HPFS";
235 else
236 FileSystemName = NULL;
237 }
238 else
239 {
240 FileSystemName = NULL;
241 }
242
243 #endif
244
245 #if 0 // FIXME: To be fully enabled when our storage stack & al. work better!
246
247 /*
248 * We don't have one...
249 *
250 * Try to infer one using NT file system recognition.
251 */
252 Status = _MyGetFileSystem(PartEntry, FsRecFileSystemName, ARRAYSIZE(FsRecFileSystemName));
253 if (NT_SUCCESS(Status) && *FsRecFileSystemName)
254 {
255 /* Temporary HACK: map FAT32 back to FAT */
256 if (wcscmp(FsRecFileSystemName, L"FAT32") == 0)
257 wcscpy(FsRecFileSystemName, L"FAT");
258
259 FileSystemName = FsRecFileSystemName;
260 goto Quit;
261 }
262
263 #endif
264
265 /*
266 * We don't have one...
267 *
268 * Try to infer a preferred file system for this partition, given its ID.
269 *
270 * WARNING: This is partly a hack, since partitions with the same ID can
271 * be formatted with different file systems: for example, usual Linux
272 * partitions that are formatted in EXT2/3/4, ReiserFS, etc... have the
273 * same partition ID 0x83.
274 *
275 * The proper fix is to make a function that detects the existing FS
276 * from a given partition (not based on the partition ID).
277 * On the contrary, for unformatted partitions with a given ID, the
278 * following code is OK.
279 */
280 if ((PartEntry->PartitionType == PARTITION_FAT_12) ||
281 (PartEntry->PartitionType == PARTITION_FAT_16) ||
282 (PartEntry->PartitionType == PARTITION_HUGE ) ||
283 (PartEntry->PartitionType == PARTITION_XINT13) ||
284 (PartEntry->PartitionType == PARTITION_FAT32 ) ||
285 (PartEntry->PartitionType == PARTITION_FAT32_XINT13))
286 {
287 FileSystemName = L"FAT";
288 }
289 else if (PartEntry->PartitionType == PARTITION_LINUX)
290 {
291 // WARNING: See the warning above.
292 FileSystemName = L"BTRFS";
293 }
294 else if (PartEntry->PartitionType == PARTITION_IFS)
295 {
296 // WARNING: See the warning above.
297 FileSystemName = L"NTFS"; /* FIXME: Not quite correct! */
298 // FIXME: We may have HPFS too...
299 }
300
301 #if 0
302 Quit: // For code temporarily disabled above
303 #endif
304
305 // HACK: WARNING: We cannot write on this FS yet!
306 if (FileSystemName)
307 {
308 if (PartEntry->PartitionType == PARTITION_IFS)
309 DPRINT1("Recognized file system %S that doesn't support write support yet!\n", FileSystemName);
310 }
311
312 DPRINT1("GetFileSystem -- PartitionType: 0x%02X ; FileSystemName (guessed): %S\n",
313 PartEntry->PartitionType, FileSystemName ? FileSystemName : L"None");
314
315 if (FileSystemName != NULL)
316 CurrentFileSystem = GetFileSystemByName(FileSystemName);
317
318 return CurrentFileSystem;
319 }
320
321
322 //
323 // Formatting routines
324 //
325
326 BOOLEAN
327 PreparePartitionForFormatting(
328 IN struct _PARTENTRY* PartEntry,
329 IN PFILE_SYSTEM FileSystem)
330 {
331 if (!FileSystem)
332 {
333 DPRINT1("No file system specified?\n");
334 return FALSE;
335 }
336
337 if (wcscmp(FileSystem->FileSystemName, L"FAT") == 0)
338 {
339 if (PartEntry->SectorCount.QuadPart < 8192)
340 {
341 /* FAT12 CHS partition (disk is smaller than 4.1MB) */
342 SetPartitionType(PartEntry, PARTITION_FAT_12);
343 }
344 else if (PartEntry->StartSector.QuadPart < 1450560)
345 {
346 /* Partition starts below the 8.4GB boundary ==> CHS partition */
347
348 if (PartEntry->SectorCount.QuadPart < 65536)
349 {
350 /* FAT16 CHS partition (partition size < 32MB) */
351 SetPartitionType(PartEntry, PARTITION_FAT_16);
352 }
353 else if (PartEntry->SectorCount.QuadPart < 1048576)
354 {
355 /* FAT16 CHS partition (partition size < 512MB) */
356 SetPartitionType(PartEntry, PARTITION_HUGE);
357 }
358 else
359 {
360 /* FAT32 CHS partition (partition size >= 512MB) */
361 SetPartitionType(PartEntry, PARTITION_FAT32);
362 }
363 }
364 else
365 {
366 /* Partition starts above the 8.4GB boundary ==> LBA partition */
367
368 if (PartEntry->SectorCount.QuadPart < 1048576)
369 {
370 /* FAT16 LBA partition (partition size < 512MB) */
371 SetPartitionType(PartEntry, PARTITION_XINT13);
372 }
373 else
374 {
375 /* FAT32 LBA partition (partition size >= 512MB) */
376 SetPartitionType(PartEntry, PARTITION_FAT32_XINT13);
377 }
378 }
379 }
380 else if (wcscmp(FileSystem->FileSystemName, L"BTRFS") == 0)
381 {
382 SetPartitionType(PartEntry, PARTITION_LINUX);
383 }
384 #if 0
385 else if (wcscmp(FileSystem->FileSystemName, L"EXT2") == 0)
386 {
387 SetPartitionType(PartEntry, PARTITION_EXT2);
388 }
389 else if (wcscmp(FileSystem->FileSystemName, L"NTFS") == 0)
390 {
391 SetPartitionType(PartEntry, PARTITION_IFS);
392 }
393 #endif
394 else
395 {
396 /* Unknown file system? */
397 DPRINT1("Unknown file system \"%S\"?\n", FileSystem->FileSystemName);
398 return FALSE;
399 }
400
401 //
402 // FIXME: Do this now, or after the partition was actually formatted??
403 //
404 /* Set the new partition's file system proper */
405 PartEntry->FormatState = Formatted; // Well... This may be set after the real formatting takes place (in which case we should change the FormatState to another value)
406 PartEntry->FileSystem = FileSystem;
407
408 return TRUE;
409 }
410
411 /* EOF */