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