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