xref: /reactos/sdk/lib/fslib/vfatlib/fat32.c (revision bae2bac6)
1 /*
2  * COPYRIGHT:   See COPYING in the top level directory
3  * PROJECT:     ReactOS VFAT filesystem library
4  * FILE:        fat32.c
5  * PURPOSE:     Fat32 support
6  * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net)
7  *              Eric Kohl
8  */
9 
10 /* INCLUDES *******************************************************************/
11 
12 #include "vfatlib.h"
13 
14 #define NDEBUG
15 #include <debug.h>
16 
17 
18 /* FUNCTIONS ******************************************************************/
19 
20 static NTSTATUS
21 Fat32WriteBootSector(IN HANDLE FileHandle,
22                      IN PFAT32_BOOT_SECTOR BootSector,
23                      IN OUT PFORMAT_CONTEXT Context)
24 {
25     IO_STATUS_BLOCK IoStatusBlock;
26     NTSTATUS Status;
27     PFAT32_BOOT_SECTOR NewBootSector;
28     LARGE_INTEGER FileOffset;
29 
30     /* Allocate buffer for new bootsector */
31     NewBootSector = (PFAT32_BOOT_SECTOR)RtlAllocateHeap(RtlGetProcessHeap(),
32                                             0,
33                                             BootSector->BytesPerSector);
34     if (NewBootSector == NULL)
35         return STATUS_INSUFFICIENT_RESOURCES;
36 
37     /* Zero the new bootsector */
38     RtlZeroMemory(NewBootSector, BootSector->BytesPerSector);
39 
40     /* Copy FAT32 BPB to new bootsector */
41     memcpy(&NewBootSector->Jump[0],
42            &BootSector->Jump[0],
43            FIELD_OFFSET(FAT32_BOOT_SECTOR, Res2) - FIELD_OFFSET(FAT32_BOOT_SECTOR, Jump));
44            /* FAT32 BPB length (up to (not including) Res2) */
45 
46     /* Write the boot sector signature */
47     NewBootSector->Signature1 = 0xAA550000;
48 
49     /* Write sector 0 */
50     FileOffset.QuadPart = 0ULL;
51     Status = NtWriteFile(FileHandle,
52                          NULL,
53                          NULL,
54                          NULL,
55                          &IoStatusBlock,
56                          NewBootSector,
57                          BootSector->BytesPerSector,
58                          &FileOffset,
59                          NULL);
60     if (!NT_SUCCESS(Status))
61     {
62         DPRINT("NtWriteFile() failed (Status %lx)\n", Status);
63         goto done;
64     }
65 
66     UpdateProgress(Context, 1);
67 
68     /* Write backup boot sector */
69     if (BootSector->BootBackup != 0x0000)
70     {
71         FileOffset.QuadPart = (ULONGLONG)((ULONG)BootSector->BootBackup * BootSector->BytesPerSector);
72         Status = NtWriteFile(FileHandle,
73                              NULL,
74                              NULL,
75                              NULL,
76                              &IoStatusBlock,
77                              NewBootSector,
78                              BootSector->BytesPerSector,
79                              &FileOffset,
80                              NULL);
81         if (!NT_SUCCESS(Status))
82         {
83             DPRINT("NtWriteFile() failed (Status %lx)\n", Status);
84             goto done;
85         }
86 
87         UpdateProgress(Context, 1);
88     }
89 
90 done:
91     /* Free the buffer */
92     RtlFreeHeap(RtlGetProcessHeap(), 0, NewBootSector);
93     return Status;
94 }
95 
96 
97 static NTSTATUS
98 Fat32WriteFsInfo(IN HANDLE FileHandle,
99                  IN PFAT32_BOOT_SECTOR BootSector,
100                  IN OUT PFORMAT_CONTEXT Context)
101 {
102     IO_STATUS_BLOCK IoStatusBlock;
103     NTSTATUS Status;
104     PFAT32_FSINFO FsInfo;
105     LARGE_INTEGER FileOffset;
106     ULONGLONG FirstDataSector;
107 
108     /* Allocate buffer for new sector */
109     FsInfo = (PFAT32_FSINFO)RtlAllocateHeap(RtlGetProcessHeap(),
110                                             0,
111                                             BootSector->BytesPerSector);
112     if (FsInfo == NULL)
113         return STATUS_INSUFFICIENT_RESOURCES;
114 
115     /* Zero the first FsInfo sector */
116     RtlZeroMemory(FsInfo, BootSector->BytesPerSector);
117 
118     FirstDataSector = BootSector->ReservedSectors +
119         (BootSector->FATCount * BootSector->FATSectors32) + 0 /* RootDirSectors */;
120 
121     FsInfo->LeadSig   = FSINFO_SECTOR_BEGIN_SIGNATURE;
122     FsInfo->StrucSig  = FSINFO_SIGNATURE;
123     FsInfo->FreeCount = (BootSector->SectorsHuge - FirstDataSector) / BootSector->SectorsPerCluster - 1;
124     FsInfo->NextFree  = 0xffffffff;
125     FsInfo->TrailSig  = FSINFO_SECTOR_END_SIGNATURE;
126 
127     /* Write the first FsInfo sector */
128     FileOffset.QuadPart = BootSector->FSInfoSector * BootSector->BytesPerSector;
129     Status = NtWriteFile(FileHandle,
130                          NULL,
131                          NULL,
132                          NULL,
133                          &IoStatusBlock,
134                          FsInfo,
135                          BootSector->BytesPerSector,
136                          &FileOffset,
137                          NULL);
138     if (!NT_SUCCESS(Status))
139     {
140         DPRINT("NtWriteFile() failed (Status %lx)\n", Status);
141         goto done;
142     }
143 
144     UpdateProgress(Context, 1);
145 
146     /* Write backup of the first FsInfo sector */
147     if (BootSector->BootBackup != 0x0000)
148     {
149         /* Reset the free cluster count for the backup */
150         FsInfo->FreeCount = 0xffffffff;
151 
152         FileOffset.QuadPart = (ULONGLONG)(((ULONG)BootSector->BootBackup + (ULONG)BootSector->FSInfoSector) * BootSector->BytesPerSector);
153         Status = NtWriteFile(FileHandle,
154                              NULL,
155                              NULL,
156                              NULL,
157                              &IoStatusBlock,
158                              FsInfo,
159                              BootSector->BytesPerSector,
160                              &FileOffset,
161                              NULL);
162         if (!NT_SUCCESS(Status))
163         {
164             DPRINT("NtWriteFile() failed (Status %lx)\n", Status);
165             goto done;
166         }
167 
168         UpdateProgress(Context, 1);
169     }
170 
171     /* Zero the second FsInfo sector */
172     RtlZeroMemory(FsInfo, BootSector->BytesPerSector);
173     FsInfo->TrailSig = FSINFO_SECTOR_END_SIGNATURE;
174 
175     /* Write the second FsInfo sector */
176     FileOffset.QuadPart = (BootSector->FSInfoSector + 1) * BootSector->BytesPerSector;
177     Status = NtWriteFile(FileHandle,
178                          NULL,
179                          NULL,
180                          NULL,
181                          &IoStatusBlock,
182                          FsInfo,
183                          BootSector->BytesPerSector,
184                          &FileOffset,
185                          NULL);
186     if (!NT_SUCCESS(Status))
187     {
188         DPRINT("NtWriteFile() failed (Status %lx)\n", Status);
189         goto done;
190     }
191 
192     UpdateProgress(Context, 1);
193 
194     /* Write backup of the second FsInfo sector */
195     if (BootSector->BootBackup != 0x0000)
196     {
197         FileOffset.QuadPart = (ULONGLONG)(((ULONG)BootSector->BootBackup + (ULONG)BootSector->FSInfoSector + 1) * BootSector->BytesPerSector);
198         Status = NtWriteFile(FileHandle,
199                              NULL,
200                              NULL,
201                              NULL,
202                              &IoStatusBlock,
203                              FsInfo,
204                              BootSector->BytesPerSector,
205                              &FileOffset,
206                              NULL);
207         if (!NT_SUCCESS(Status))
208         {
209             DPRINT("NtWriteFile() failed (Status %lx)\n", Status);
210             goto done;
211         }
212 
213         UpdateProgress(Context, 1);
214     }
215 
216 done:
217     /* Free the buffer */
218     RtlFreeHeap(RtlGetProcessHeap(), 0, FsInfo);
219     return Status;
220 }
221 
222 
223 static NTSTATUS
224 Fat32WriteFAT(IN HANDLE FileHandle,
225               IN ULONG SectorOffset,
226               IN PFAT32_BOOT_SECTOR BootSector,
227               IN OUT PFORMAT_CONTEXT Context)
228 {
229     IO_STATUS_BLOCK IoStatusBlock;
230     NTSTATUS Status;
231     PUCHAR Buffer;
232     LARGE_INTEGER FileOffset;
233     ULONG i;
234     ULONG Sectors;
235 
236     /* Allocate buffer */
237     Buffer = (PUCHAR)RtlAllocateHeap(RtlGetProcessHeap(),
238                                      0,
239                                      64 * 1024);
240     if (Buffer == NULL)
241         return STATUS_INSUFFICIENT_RESOURCES;
242 
243     /* Zero the buffer */
244     RtlZeroMemory(Buffer, 64 * 1024);
245 
246     /* FAT cluster 0 */
247     Buffer[0] = 0xf8; /* Media type */
248     Buffer[1] = 0xff;
249     Buffer[2] = 0xff;
250     Buffer[3] = 0x0f;
251 
252     /* FAT cluster 1 */
253     Buffer[4] = 0xff; /* Clean shutdown, no disk read/write errors, end-of-cluster (EOC) mark */
254     Buffer[5] = 0xff;
255     Buffer[6] = 0xff;
256     Buffer[7] = 0x0f;
257 
258     /* FAT cluster 2 */
259     Buffer[8] = 0xff; /* End of root directory */
260     Buffer[9] = 0xff;
261     Buffer[10] = 0xff;
262     Buffer[11] = 0x0f;
263 
264     /* Write first sector of the FAT */
265     FileOffset.QuadPart = (SectorOffset + BootSector->ReservedSectors) * BootSector->BytesPerSector;
266     Status = NtWriteFile(FileHandle,
267                          NULL,
268                          NULL,
269                          NULL,
270                          &IoStatusBlock,
271                          Buffer,
272                          BootSector->BytesPerSector,
273                          &FileOffset,
274                          NULL);
275     if (!NT_SUCCESS(Status))
276     {
277         DPRINT("NtWriteFile() failed (Status %lx)\n", Status);
278         goto done;
279     }
280 
281     UpdateProgress(Context, 1);
282 
283     /* Zero the begin of the buffer */
284     RtlZeroMemory(Buffer, 12);
285 
286     /* Zero the rest of the FAT */
287     Sectors = 64 * 1024 / BootSector->BytesPerSector;
288     for (i = 1; i < BootSector->FATSectors32; i += Sectors)
289     {
290         /* Zero some sectors of the FAT */
291         FileOffset.QuadPart = (SectorOffset + BootSector->ReservedSectors + i) * BootSector->BytesPerSector;
292 
293         if ((BootSector->FATSectors32 - i) <= Sectors)
294         {
295             Sectors = BootSector->FATSectors32 - i;
296         }
297 
298         Status = NtWriteFile(FileHandle,
299                              NULL,
300                              NULL,
301                              NULL,
302                              &IoStatusBlock,
303                              Buffer,
304                              Sectors * BootSector->BytesPerSector,
305                              &FileOffset,
306                              NULL);
307         if (!NT_SUCCESS(Status))
308         {
309             DPRINT("NtWriteFile() failed (Status %lx)\n", Status);
310             goto done;
311         }
312 
313         UpdateProgress(Context, Sectors);
314     }
315 
316 done:
317     /* Free the buffer */
318     RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer);
319     return Status;
320 }
321 
322 
323 static NTSTATUS
324 Fat32WriteRootDirectory(IN HANDLE FileHandle,
325                         IN PFAT32_BOOT_SECTOR BootSector,
326                         IN OUT PFORMAT_CONTEXT Context)
327 {
328     IO_STATUS_BLOCK IoStatusBlock;
329     NTSTATUS Status;
330     PUCHAR Buffer;
331     LARGE_INTEGER FileOffset;
332     ULONGLONG FirstDataSector;
333     ULONGLONG FirstRootDirSector;
334 
335     /* Allocate buffer for the cluster */
336     Buffer = (PUCHAR)RtlAllocateHeap(RtlGetProcessHeap(),
337                                      0,
338                                      BootSector->SectorsPerCluster * BootSector->BytesPerSector);
339     if (Buffer == NULL)
340         return STATUS_INSUFFICIENT_RESOURCES;
341 
342     /* Zero the buffer */
343     RtlZeroMemory(Buffer, BootSector->SectorsPerCluster * BootSector->BytesPerSector);
344 
345     DPRINT("BootSector->ReservedSectors = %lu\n", BootSector->ReservedSectors);
346     DPRINT("BootSector->FATSectors32 = %lu\n", BootSector->FATSectors32);
347     DPRINT("BootSector->RootCluster = %lu\n", BootSector->RootCluster);
348     DPRINT("BootSector->SectorsPerCluster = %lu\n", BootSector->SectorsPerCluster);
349 
350     /* Write cluster */
351     FirstDataSector = BootSector->ReservedSectors +
352         (BootSector->FATCount * BootSector->FATSectors32) + 0 /* RootDirSectors */;
353 
354     DPRINT("FirstDataSector = %lu\n", FirstDataSector);
355 
356     FirstRootDirSector = ((BootSector->RootCluster - 2) * BootSector->SectorsPerCluster) + FirstDataSector;
357     FileOffset.QuadPart = FirstRootDirSector * BootSector->BytesPerSector;
358 
359     DPRINT("FirstRootDirSector = %lu\n", FirstRootDirSector);
360     DPRINT("FileOffset = %lu\n", FileOffset.QuadPart);
361 
362     Status = NtWriteFile(FileHandle,
363                          NULL,
364                          NULL,
365                          NULL,
366                          &IoStatusBlock,
367                          Buffer,
368                          BootSector->SectorsPerCluster * BootSector->BytesPerSector,
369                          &FileOffset,
370                          NULL);
371     if (!NT_SUCCESS(Status))
372     {
373         DPRINT("NtWriteFile() failed (Status %lx)\n", Status);
374         goto done;
375     }
376 
377     UpdateProgress(Context, (ULONG)BootSector->SectorsPerCluster);
378 
379 done:
380     /* Free the buffer */
381     RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer);
382     return Status;
383 }
384 
385 
386 NTSTATUS
387 Fat32Format(IN HANDLE FileHandle,
388             IN PPARTITION_INFORMATION PartitionInfo,
389             IN PDISK_GEOMETRY DiskGeometry,
390             IN PUNICODE_STRING Label,
391             IN BOOLEAN QuickFormat,
392             IN ULONG ClusterSize,
393             IN OUT PFORMAT_CONTEXT Context)
394 {
395     FAT32_BOOT_SECTOR BootSector;
396     OEM_STRING VolumeLabel;
397     ULONG TmpVal1;
398     ULONG TmpVal2;
399     NTSTATUS Status;
400     ULONG UsableFatEntries;
401     ULONG FirstDataSector;
402     ULONG DataClusters;
403 
404     /* Calculate cluster size */
405     if (ClusterSize == 0)
406     {
407         if (PartitionInfo->PartitionLength.QuadPart < 8LL * 1024LL * 1024LL * 1024LL)
408         {
409             /* Partition < 8GB ==> 4KB Cluster */
410             ClusterSize = 4096;
411         }
412         else if (PartitionInfo->PartitionLength.QuadPart < 16LL * 1024LL * 1024LL * 1024LL)
413         {
414             /* Partition 8GB - 16GB ==> 8KB Cluster */
415             ClusterSize = 8192;
416         }
417         else if (PartitionInfo->PartitionLength.QuadPart < 32LL * 1024LL * 1024LL * 1024LL)
418         {
419             /* Partition 16GB - 32GB ==> 16KB Cluster */
420             ClusterSize = 16384;
421         }
422         else
423         {
424             /* Partition >= 32GB ==> 32KB Cluster */
425             ClusterSize = 32768;
426         }
427     }
428 
429     RtlZeroMemory(&BootSector, sizeof(FAT32_BOOT_SECTOR));
430     memcpy(&BootSector.OEMName[0], "MSWIN4.1", 8);
431     /* FIXME: Add dummy bootloader for real */
432     BootSector.Jump[0] = 0xeb;
433     BootSector.Jump[1] = 0x58;
434     BootSector.Jump[2] = 0x90;
435     BootSector.BytesPerSector = DiskGeometry->BytesPerSector;
436     BootSector.SectorsPerCluster = ClusterSize / BootSector.BytesPerSector;
437     BootSector.ReservedSectors = 32;
438     BootSector.FATCount = 2;
439     BootSector.RootEntries = 0;
440     BootSector.Sectors = 0;
441     BootSector.Media = 0xf8;
442     BootSector.FATSectors = 0;
443     BootSector.SectorsPerTrack = DiskGeometry->SectorsPerTrack;
444     BootSector.Heads = DiskGeometry->TracksPerCylinder;
445     BootSector.HiddenSectors = PartitionInfo->HiddenSectors;
446     BootSector.SectorsHuge = PartitionInfo->PartitionLength.QuadPart >>
447         GetShiftCount(BootSector.BytesPerSector); /* Use shifting to avoid 64-bit division */
448     BootSector.FATSectors32 = 0; /* Set later */
449     BootSector.ExtFlag = 0; /* Mirror all FATs */
450     BootSector.FSVersion = 0x0000; /* 0:0 */
451     BootSector.RootCluster = 2;
452     BootSector.FSInfoSector = 1;
453     BootSector.BootBackup = 6;
454     BootSector.Drive = (DiskGeometry->MediaType == FixedMedia) ? 0x80 : 0x00;
455     BootSector.ExtBootSignature = 0x29;
456     BootSector.VolumeID = CalcVolumeSerialNumber();
457     if ((Label == NULL) || (Label->Buffer == NULL))
458     {
459         memcpy(&BootSector.VolumeLabel[0], "NO NAME    ", 11);
460     }
461     else
462     {
463         RtlUnicodeStringToOemString(&VolumeLabel, Label, TRUE);
464         RtlFillMemory(&BootSector.VolumeLabel[0], 11, ' ');
465         memcpy(&BootSector.VolumeLabel[0], VolumeLabel.Buffer,
466                VolumeLabel.Length < 11 ? VolumeLabel.Length : 11);
467         RtlFreeOemString(&VolumeLabel);
468     }
469 
470     memcpy(&BootSector.SysType[0], "FAT32   ", 8);
471 
472     /* Calculate number of FAT sectors */
473     /* (BytesPerSector / 4) FAT entries (32bit) fit into one sector */
474     TmpVal1 = BootSector.SectorsHuge - BootSector.ReservedSectors;
475     TmpVal2 = ((BootSector.BytesPerSector / 4) * BootSector.SectorsPerCluster) + BootSector.FATCount;
476     BootSector.FATSectors32 = (TmpVal1 + (TmpVal2 - 1)) / TmpVal2;
477     DPRINT("FATSectors32 = %lu\n", BootSector.FATSectors32);
478 
479     /* edge case: first 2 fat entries are not usable, so the calculation might need a correction */
480     UsableFatEntries = BootSector.FATSectors32 * (BootSector.BytesPerSector / 4) - 2;
481     FirstDataSector = BootSector.ReservedSectors + BootSector.FATCount * BootSector.FATSectors32;
482     DataClusters = (BootSector.SectorsHuge - FirstDataSector) / BootSector.SectorsPerCluster;
483     if (DataClusters > UsableFatEntries)
484     {
485         /* Need more fat entries */
486         BootSector.FATSectors32 += (DataClusters - UsableFatEntries);
487 
488         DPRINT("UsableFatEntries = %lu\n", UsableFatEntries);
489         DPRINT("DataClusters = %lu\n", DataClusters);
490         DPRINT("BootSector.FATSectors32 incremented to %lu\n", BootSector.FATSectors32);
491     }
492 
493     /* Init context data */
494     Context->TotalSectorCount =
495         2 + (BootSector.FATSectors32 * BootSector.FATCount) + BootSector.SectorsPerCluster;
496 
497     if (!QuickFormat)
498     {
499         Context->TotalSectorCount += BootSector.SectorsHuge;
500 
501         Status = FatWipeSectors(FileHandle,
502                                 BootSector.SectorsHuge,
503                                 (ULONG)BootSector.SectorsPerCluster,
504                                 (ULONG)BootSector.BytesPerSector,
505                                 Context);
506         if (!NT_SUCCESS(Status))
507         {
508             DPRINT("FatWipeSectors() failed with status 0x%.08x\n", Status);
509             return Status;
510         }
511     }
512 
513     Status = Fat32WriteBootSector(FileHandle,
514                                   &BootSector,
515                                   Context);
516     if (!NT_SUCCESS(Status))
517     {
518         DPRINT("Fat32WriteBootSector() failed with status 0x%.08x\n", Status);
519         return Status;
520     }
521 
522     Status = Fat32WriteFsInfo(FileHandle,
523                               &BootSector,
524                               Context);
525     if (!NT_SUCCESS(Status))
526     {
527         DPRINT("Fat32WriteFsInfo() failed with status 0x%.08x\n", Status);
528         return Status;
529     }
530 
531     /* Write first FAT copy */
532     Status = Fat32WriteFAT(FileHandle,
533                            0,
534                            &BootSector,
535                            Context);
536     if (!NT_SUCCESS(Status))
537     {
538       DPRINT("Fat32WriteFAT() failed with status 0x%.08x\n", Status);
539       return Status;
540     }
541 
542     /* Write second FAT copy */
543     Status = Fat32WriteFAT(FileHandle,
544                            BootSector.FATSectors32,
545                            &BootSector,
546                            Context);
547     if (!NT_SUCCESS(Status))
548     {
549         DPRINT("Fat32WriteFAT() failed with status 0x%.08x.\n", Status);
550         return Status;
551     }
552 
553     Status = Fat32WriteRootDirectory(FileHandle,
554                                      &BootSector,
555                                      Context);
556     if (!NT_SUCCESS(Status))
557     {
558         DPRINT("Fat32WriteRootDirectory() failed with status 0x%.08x\n", Status);
559     }
560 
561     return Status;
562 }
563 
564 /* EOF */
565