xref: /reactos/base/setup/lib/fsutil.c (revision 279107d5)
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 // See also: https://git.reactos.org/?p=reactos.git;a=blob;f=reactos/dll/win32/fmifs/init.c;h=e895f5ef9cae4806123f6bbdd3dfed37ec1c8d33;hb=b9db9a4e377a2055f635b2fb69fef4e1750d219c
11 // for how to get FS providers in a dynamic way. In the (near) future we may
12 // consider merging some of this code with us into a fmifs / fsutil / fslib library...
13 //
14 
15 /* INCLUDES *****************************************************************/
16 
17 #include "precomp.h"
18 
19 #include "fsutil.h"
20 #include "partlist.h"
21 
22 #include <fslib/vfatlib.h>
23 #include <fslib/btrfslib.h>
24 // #include <fslib/ext2lib.h>
25 // #include <fslib/ntfslib.h>
26 
27 #define NDEBUG
28 #include <debug.h>
29 
30 
31 /* GLOBALS ******************************************************************/
32 
33 /* The list of file systems on which we can install ReactOS */
34 FILE_SYSTEM RegisteredFileSystems[] =
35 {
36     /* NOTE: The FAT formatter automatically determines
37      * whether it will use FAT-16 or FAT-32. */
38     { L"FAT"  , VfatFormat, VfatChkdsk },
39 #if 0
40     { L"FAT32", VfatFormat, VfatChkdsk }, // Do we support specific FAT sub-formats specifications?
41     { L"FATX" , VfatxFormat, VfatxChkdsk },
42     { L"NTFS" , NtfsFormat, NtfsChkdsk },
43 #endif
44     { L"BTRFS", BtrfsFormatEx, BtrfsChkdskEx },
45 #if 0
46     { L"EXT2" , Ext2Format, Ext2Chkdsk },
47     { L"EXT3" , Ext2Format, Ext2Chkdsk },
48     { L"EXT4" , Ext2Format, Ext2Chkdsk },
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 /* NOTE: Ripped & adapted from base/system/autochk/autochk.c */
112 static NTSTATUS
113 _MyGetFileSystem(
114     IN struct _PARTENTRY* PartEntry,
115     IN OUT PWSTR FileSystemName,
116     IN SIZE_T FileSystemNameSize)
117 {
118     NTSTATUS Status;
119     UNICODE_STRING PartitionRootPath;
120     OBJECT_ATTRIBUTES ObjectAttributes;
121     HANDLE FileHandle;
122     IO_STATUS_BLOCK IoStatusBlock;
123     WCHAR PathBuffer[MAX_PATH];
124     UCHAR Buffer[sizeof(FILE_FS_ATTRIBUTE_INFORMATION) + MAX_PATH * sizeof(WCHAR)];
125     PFILE_FS_ATTRIBUTE_INFORMATION FileFsAttribute = (PFILE_FS_ATTRIBUTE_INFORMATION)Buffer;
126 
127     /* Set PartitionRootPath */
128     RtlStringCchPrintfW(PathBuffer, ARRAYSIZE(PathBuffer),
129                         L"\\Device\\Harddisk%lu\\Partition%lu",
130                         PartEntry->DiskEntry->DiskNumber,
131                         PartEntry->PartitionNumber);
132     RtlInitUnicodeString(&PartitionRootPath, PathBuffer);
133     DPRINT("PartitionRootPath: %wZ\n", &PartitionRootPath);
134 
135     /* Open the partition */
136     InitializeObjectAttributes(&ObjectAttributes,
137                                &PartitionRootPath,
138                                OBJ_CASE_INSENSITIVE,
139                                NULL,
140                                NULL);
141     Status = NtOpenFile(&FileHandle, // PartitionHandle,
142                         FILE_GENERIC_READ /* | SYNCHRONIZE */,
143                         &ObjectAttributes,
144                         &IoStatusBlock,
145                         FILE_SHARE_READ | FILE_SHARE_WRITE,
146                         0 /* FILE_SYNCHRONOUS_IO_NONALERT */);
147     if (!NT_SUCCESS(Status))
148     {
149         DPRINT1("Failed to open partition '%wZ', Status 0x%08lx\n", &PartitionRootPath, Status);
150         return Status;
151     }
152 
153     /* Retrieve the FS attributes */
154     Status = NtQueryVolumeInformationFile(FileHandle,
155                                           &IoStatusBlock,
156                                           FileFsAttribute,
157                                           sizeof(Buffer),
158                                           FileFsAttributeInformation);
159     NtClose(FileHandle);
160 
161     if (!NT_SUCCESS(Status))
162     {
163         DPRINT1("NtQueryVolumeInformationFile failed for partition '%wZ', Status 0x%08lx\n",
164                 &PartitionRootPath, Status);
165         return Status;
166     }
167 
168     if (FileSystemNameSize < FileFsAttribute->FileSystemNameLength + sizeof(WCHAR))
169         return STATUS_BUFFER_TOO_SMALL;
170 
171     return RtlStringCbCopyNW(FileSystemName, FileSystemNameSize,
172                              FileFsAttribute->FileSystemName,
173                              FileFsAttribute->FileSystemNameLength);
174 }
175 
176 PFILE_SYSTEM
177 GetFileSystem(
178     // IN PFILE_SYSTEM_LIST FileSystemList,
179     IN struct _PARTENTRY* PartEntry)
180 {
181     PFILE_SYSTEM CurrentFileSystem;
182     NTSTATUS Status;
183     PWSTR FileSystemName = NULL;
184     WCHAR FsRecFileSystemName[MAX_PATH];
185 
186     CurrentFileSystem = PartEntry->FileSystem;
187 
188     /* We have a file system, return it */
189     if (CurrentFileSystem != NULL && CurrentFileSystem->FileSystemName != NULL)
190         return CurrentFileSystem;
191 
192     DPRINT1("File system not found, try to guess one...\n");
193 
194     CurrentFileSystem = NULL;
195 
196     /*
197      * We don't have one...
198      *
199      * Try to infer a file system using NT file system recognition.
200      */
201     Status = _MyGetFileSystem(PartEntry, FsRecFileSystemName, sizeof(FsRecFileSystemName));
202     if (NT_SUCCESS(Status) && *FsRecFileSystemName)
203     {
204         /* Temporary HACK: map FAT32 back to FAT */
205         if (wcscmp(FsRecFileSystemName, L"FAT32") == 0)
206             RtlStringCbCopyW(FsRecFileSystemName, sizeof(FsRecFileSystemName), L"FAT");
207 
208         FileSystemName = FsRecFileSystemName;
209         goto Quit;
210     }
211 
212     /*
213      * We don't have one...
214      *
215      * Try to infer a preferred file system for this partition, given its ID.
216      *
217      * WARNING: This is partly a hack, since partitions with the same ID can
218      * be formatted with different file systems: for example, usual Linux
219      * partitions that are formatted in EXT2/3/4, ReiserFS, etc... have the
220      * same partition ID 0x83.
221      *
222      * The proper fix is to make a function that detects the existing FS
223      * from a given partition (not based on the partition ID).
224      * On the contrary, for unformatted partitions with a given ID, the
225      * following code is OK.
226      */
227     if ((PartEntry->PartitionType == PARTITION_FAT_12) ||
228         (PartEntry->PartitionType == PARTITION_FAT_16) ||
229         (PartEntry->PartitionType == PARTITION_HUGE  ) ||
230         (PartEntry->PartitionType == PARTITION_XINT13) ||
231         (PartEntry->PartitionType == PARTITION_FAT32 ) ||
232         (PartEntry->PartitionType == PARTITION_FAT32_XINT13))
233     {
234         FileSystemName = L"FAT";
235     }
236     else if (PartEntry->PartitionType == PARTITION_LINUX)
237     {
238         // WARNING: See the warning above.
239         /* Could also be EXT2/3/4, ReiserFS, ... */
240         FileSystemName = L"BTRFS";
241     }
242     else if (PartEntry->PartitionType == PARTITION_IFS)
243     {
244         // WARNING: See the warning above.
245         /* Could also be HPFS */
246         FileSystemName = L"NTFS";
247     }
248 
249 Quit:
250     // HACK: WARNING: We cannot write on this FS yet!
251     if (FileSystemName)
252     {
253         if (PartEntry->PartitionType == PARTITION_IFS)
254             DPRINT1("Recognized file system %S that doesn't support write support yet!\n", FileSystemName);
255     }
256 
257     DPRINT1("GetFileSystem -- PartitionType: 0x%02X ; FileSystemName (guessed): %S\n",
258             PartEntry->PartitionType, FileSystemName ? FileSystemName : L"None");
259 
260     if (FileSystemName)
261         CurrentFileSystem = GetFileSystemByName(FileSystemName);
262 
263     return CurrentFileSystem;
264 }
265 
266 
267 //
268 // Formatting routines
269 //
270 
271 BOOLEAN
272 PreparePartitionForFormatting(
273     IN struct _PARTENTRY* PartEntry,
274     IN PFILE_SYSTEM FileSystem)
275 {
276     if (!FileSystem)
277     {
278         DPRINT1("No file system specified?\n");
279         return FALSE;
280     }
281 
282     if (wcscmp(FileSystem->FileSystemName, L"FAT") == 0)
283     {
284         if (PartEntry->SectorCount.QuadPart < 8192)
285         {
286             /* FAT12 CHS partition (disk is smaller than 4.1MB) */
287             SetPartitionType(PartEntry, PARTITION_FAT_12);
288         }
289         else if (PartEntry->StartSector.QuadPart < 1450560)
290         {
291             /* Partition starts below the 8.4GB boundary ==> CHS partition */
292 
293             if (PartEntry->SectorCount.QuadPart < 65536)
294             {
295                 /* FAT16 CHS partition (partition size < 32MB) */
296                 SetPartitionType(PartEntry, PARTITION_FAT_16);
297             }
298             else if (PartEntry->SectorCount.QuadPart < 1048576)
299             {
300                 /* FAT16 CHS partition (partition size < 512MB) */
301                 SetPartitionType(PartEntry, PARTITION_HUGE);
302             }
303             else
304             {
305                 /* FAT32 CHS partition (partition size >= 512MB) */
306                 SetPartitionType(PartEntry, PARTITION_FAT32);
307             }
308         }
309         else
310         {
311             /* Partition starts above the 8.4GB boundary ==> LBA partition */
312 
313             if (PartEntry->SectorCount.QuadPart < 1048576)
314             {
315                 /* FAT16 LBA partition (partition size < 512MB) */
316                 SetPartitionType(PartEntry, PARTITION_XINT13);
317             }
318             else
319             {
320                 /* FAT32 LBA partition (partition size >= 512MB) */
321                 SetPartitionType(PartEntry, PARTITION_FAT32_XINT13);
322             }
323         }
324     }
325     else if (wcscmp(FileSystem->FileSystemName, L"BTRFS") == 0)
326     {
327         SetPartitionType(PartEntry, PARTITION_LINUX);
328     }
329 #if 0
330     else if (wcscmp(FileSystem->FileSystemName, L"EXT2") == 0)
331     {
332         SetPartitionType(PartEntry, PARTITION_LINUX);
333     }
334     else if (wcscmp(FileSystem->FileSystemName, L"NTFS") == 0)
335     {
336         SetPartitionType(PartEntry, PARTITION_IFS);
337     }
338 #endif
339     else
340     {
341         /* Unknown file system? */
342         DPRINT1("Unknown file system \"%S\"?\n", FileSystem->FileSystemName);
343         return FALSE;
344     }
345 
346 //
347 // FIXME: Do this now, or after the partition was actually formatted??
348 //
349     /* Set the new partition's file system proper */
350     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)
351     PartEntry->FileSystem  = FileSystem;
352 
353     return TRUE;
354 }
355 
356 /* EOF */
357