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