xref: /reactos/sdk/lib/fslib/vfatlib/fat16.c (revision d5399189)
1 /*
2  * COPYRIGHT:   See COPYING in the top level directory
3  * PROJECT:     ReactOS VFAT filesystem library
4  * FILE:        fat16.c
5  * PURPOSE:     Fat16 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 Fat16WriteBootSector(IN HANDLE FileHandle,
22                      IN PFAT16_BOOT_SECTOR BootSector,
23                      IN OUT PFORMAT_CONTEXT Context)
24 {
25     IO_STATUS_BLOCK IoStatusBlock;
26     NTSTATUS Status;
27     PFAT16_BOOT_SECTOR NewBootSector;
28     LARGE_INTEGER FileOffset;
29 
30     /* Allocate buffer for new bootsector */
31     NewBootSector = (PFAT16_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 FAT16 BPB to new bootsector */
41     memcpy(NewBootSector, BootSector,
42            FIELD_OFFSET(FAT16_BOOT_SECTOR, Res2) - FIELD_OFFSET(FAT16_BOOT_SECTOR, Jump));
43            /* FAT16 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 done:
68     /* Free the buffer */
69     RtlFreeHeap(RtlGetProcessHeap(), 0, NewBootSector);
70     return Status;
71 }
72 
73 
74 static NTSTATUS
75 Fat16WriteFAT(IN HANDLE FileHandle,
76               IN ULONG SectorOffset,
77               IN PFAT16_BOOT_SECTOR BootSector,
78               IN OUT PFORMAT_CONTEXT Context)
79 {
80     IO_STATUS_BLOCK IoStatusBlock;
81     NTSTATUS Status;
82     PUCHAR Buffer;
83     LARGE_INTEGER FileOffset;
84     ULONG i;
85     ULONG Sectors;
86 
87     /* Allocate buffer */
88     Buffer = (PUCHAR)RtlAllocateHeap(RtlGetProcessHeap(),
89                                      0,
90                                      32 * 1024);
91     if (Buffer == NULL)
92       return STATUS_INSUFFICIENT_RESOURCES;
93 
94     /* Zero the buffer */
95     RtlZeroMemory(Buffer, 32 * 1024);
96 
97     /* FAT cluster 0 */
98     Buffer[0] = 0xf8; /* Media type */
99     Buffer[1] = 0xff;
100 
101     /* FAT cluster 1 */
102     Buffer[2] = 0xff; /* Clean shutdown, no disk read/write errors, end-of-cluster (EOC) mark */
103     Buffer[3] = 0xff;
104 
105     /* Write first sector of the FAT */
106     FileOffset.QuadPart = (SectorOffset + BootSector->ReservedSectors) * BootSector->BytesPerSector;
107     Status = NtWriteFile(FileHandle,
108                          NULL,
109                          NULL,
110                          NULL,
111                          &IoStatusBlock,
112                          Buffer,
113                          BootSector->BytesPerSector,
114                          &FileOffset,
115                          NULL);
116     if (!NT_SUCCESS(Status))
117     {
118         DPRINT("NtWriteFile() failed (Status %lx)\n", Status);
119         goto done;
120     }
121 
122     UpdateProgress(Context, 1);
123 
124     /* Zero the begin of the buffer */
125     RtlZeroMemory(Buffer, 4);
126 
127     /* Zero the rest of the FAT */
128     Sectors = 32 * 1024 / BootSector->BytesPerSector;
129     for (i = 1; i < (ULONG)BootSector->FATSectors; i += Sectors)
130     {
131         /* Zero some sectors of the FAT */
132         FileOffset.QuadPart = (SectorOffset + BootSector->ReservedSectors + i) * BootSector->BytesPerSector;
133 
134         if (((ULONG)BootSector->FATSectors - i) <= Sectors)
135         {
136             Sectors = (ULONG)BootSector->FATSectors - i;
137         }
138 
139         Status = NtWriteFile(FileHandle,
140                              NULL,
141                              NULL,
142                              NULL,
143                              &IoStatusBlock,
144                              Buffer,
145                              Sectors * BootSector->BytesPerSector,
146                              &FileOffset,
147                              NULL);
148         if (!NT_SUCCESS(Status))
149         {
150             DPRINT("NtWriteFile() failed (Status %lx)\n", Status);
151             goto done;
152         }
153 
154         UpdateProgress(Context, Sectors);
155     }
156 
157 done:
158     /* Free the buffer */
159     RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer);
160     return Status;
161 }
162 
163 
164 static NTSTATUS
165 Fat16WriteRootDirectory(IN HANDLE FileHandle,
166                         IN PFAT16_BOOT_SECTOR BootSector,
167                         IN OUT PFORMAT_CONTEXT Context)
168 {
169     IO_STATUS_BLOCK IoStatusBlock;
170     NTSTATUS Status = STATUS_SUCCESS;
171     PUCHAR Buffer;
172     LARGE_INTEGER FileOffset;
173     ULONG FirstRootDirSector;
174     ULONG RootDirSectors;
175     ULONG Sectors;
176     ULONG i;
177 
178     DPRINT("BootSector->ReservedSectors = %hu\n", BootSector->ReservedSectors);
179     DPRINT("BootSector->FATSectors = %hu\n", BootSector->FATSectors);
180     DPRINT("BootSector->SectorsPerCluster = %u\n", BootSector->SectorsPerCluster);
181 
182     /* Write cluster */
183     RootDirSectors = ((BootSector->RootEntries * 32) +
184         (BootSector->BytesPerSector - 1)) / BootSector->BytesPerSector;
185     FirstRootDirSector =
186         BootSector->ReservedSectors + (BootSector->FATCount * BootSector->FATSectors);
187 
188     DPRINT("RootDirSectors = %lu\n", RootDirSectors);
189     DPRINT("FirstRootDirSector = %lu\n", FirstRootDirSector);
190 
191     /* Allocate buffer for the cluster */
192     Buffer = (PUCHAR)RtlAllocateHeap(RtlGetProcessHeap(),
193                                      0,
194                                      32 * 1024);
195     if (Buffer == NULL)
196         return STATUS_INSUFFICIENT_RESOURCES;
197 
198     /* Zero the buffer */
199     RtlZeroMemory(Buffer, 32 * 1024);
200 
201     Sectors = 32 * 1024 / BootSector->BytesPerSector;
202     for (i = 0; i < RootDirSectors; i += Sectors)
203     {
204         /* Zero some sectors of the root directory */
205         FileOffset.QuadPart = (FirstRootDirSector + i) * BootSector->BytesPerSector;
206 
207         if ((RootDirSectors - i) <= Sectors)
208         {
209             Sectors = RootDirSectors - i;
210         }
211 
212         Status = NtWriteFile(FileHandle,
213                              NULL,
214                              NULL,
215                              NULL,
216                              &IoStatusBlock,
217                              Buffer,
218                              Sectors * BootSector->BytesPerSector,
219                              &FileOffset,
220                              NULL);
221         if (!NT_SUCCESS(Status))
222         {
223             DPRINT("NtWriteFile() failed (Status %lx)\n", Status);
224             goto done;
225         }
226 
227         UpdateProgress(Context, Sectors);
228     }
229 
230 done:
231     /* Free the buffer */
232     RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer);
233     return Status;
234 }
235 
236 
237 NTSTATUS
238 Fat16Format(IN HANDLE FileHandle,
239             IN PPARTITION_INFORMATION PartitionInfo,
240             IN PDISK_GEOMETRY DiskGeometry,
241             IN PUNICODE_STRING Label,
242             IN BOOLEAN QuickFormat,
243             IN ULONG ClusterSize,
244             IN OUT PFORMAT_CONTEXT Context)
245 {
246     FAT16_BOOT_SECTOR BootSector;
247     OEM_STRING VolumeLabel;
248     ULONG SectorCount;
249     ULONG RootDirSectors;
250     ULONG TmpVal1;
251     ULONG TmpVal2;
252     ULONG TmpVal3;
253     NTSTATUS Status;
254 
255     /* Calculate cluster size */
256     if (ClusterSize == 0)
257     {
258         if (PartitionInfo->PartitionLength.QuadPart < 16LL * 1024LL * 1024LL)
259         {
260             /* Partition < 16MB ==> 1KB Cluster */
261             ClusterSize = 1024;
262         }
263         else if (PartitionInfo->PartitionLength.QuadPart < 128LL * 1024LL * 1024LL)
264         {
265             /* Partition < 128MB ==> 2KB Cluster */
266             ClusterSize = 2048;
267         }
268         else if (PartitionInfo->PartitionLength.QuadPart < 256LL * 1024LL * 1024LL)
269         {
270             /* Partition < 256MB ==> 4KB Cluster */
271             ClusterSize = 4096;
272         }
273         else
274         {
275             /* Partition >= 256MB (< 512MB) ==> 8KB Cluster */
276             ClusterSize = 8192;
277         }
278     }
279 
280     SectorCount = PartitionInfo->PartitionLength.QuadPart >>
281         GetShiftCount(DiskGeometry->BytesPerSector); /* Use shifting to avoid 64-bit division */
282 
283     RtlZeroMemory(&BootSector, sizeof(FAT16_BOOT_SECTOR));
284     memcpy(&BootSector.OEMName[0], "MSWIN4.1", 8);
285     /* FIXME: Add dummy bootloader for real */
286     BootSector.Jump[0] = 0xeb;
287     BootSector.Jump[1] = 0x3c;
288     BootSector.Jump[2] = 0x90;
289     BootSector.BytesPerSector = DiskGeometry->BytesPerSector;
290     BootSector.SectorsPerCluster = ClusterSize / BootSector.BytesPerSector;
291     BootSector.ReservedSectors = 1;
292     BootSector.FATCount = 2;
293     BootSector.RootEntries = 512;
294     BootSector.Sectors = (SectorCount < 0x10000) ? (unsigned short)SectorCount : 0;
295     BootSector.Media = 0xf8;
296     BootSector.FATSectors = 0;  /* Set later. See below. */
297     BootSector.SectorsPerTrack = DiskGeometry->SectorsPerTrack;
298     BootSector.Heads = DiskGeometry->TracksPerCylinder;
299     BootSector.HiddenSectors = PartitionInfo->HiddenSectors;
300     BootSector.SectorsHuge = (SectorCount >= 0x10000) ? (unsigned long)SectorCount : 0;
301     BootSector.Drive = (DiskGeometry->MediaType == FixedMedia) ? 0x80 : 0x00;
302     BootSector.ExtBootSignature = 0x29;
303     BootSector.VolumeID = CalcVolumeSerialNumber();
304     if ((Label == NULL) || (Label->Buffer == NULL))
305     {
306         memcpy(&BootSector.VolumeLabel[0], "NO NAME    ", 11);
307     }
308     else
309     {
310         RtlUnicodeStringToOemString(&VolumeLabel, Label, TRUE);
311         RtlFillMemory(&BootSector.VolumeLabel[0], 11, ' ');
312         memcpy(&BootSector.VolumeLabel[0], VolumeLabel.Buffer,
313                VolumeLabel.Length < 11 ? VolumeLabel.Length : 11);
314         RtlFreeOemString(&VolumeLabel);
315     }
316 
317     memcpy(&BootSector.SysType[0], "FAT16   ", 8);
318 
319     DPRINT("BootSector.SectorsHuge = %lx\n", BootSector.SectorsHuge);
320 
321     RootDirSectors = ((BootSector.RootEntries * 32) +
322         (BootSector.BytesPerSector - 1)) / BootSector.BytesPerSector;
323 
324     /* Calculate number of FAT sectors */
325     /* (BootSector.BytesPerSector / 2) FAT entries (16bit) fit into one sector */
326     TmpVal1 = SectorCount - (BootSector.ReservedSectors + RootDirSectors);
327     TmpVal2 = ((BootSector.BytesPerSector / 2) * BootSector.SectorsPerCluster) + BootSector.FATCount;
328     TmpVal3 = (TmpVal1 + (TmpVal2 - 1)) / TmpVal2;
329     BootSector.FATSectors = (unsigned short)(TmpVal3 & 0xffff);
330     DPRINT("BootSector.FATSectors = %hu\n", BootSector.FATSectors);
331 
332     /* Init context data */
333     Context->TotalSectorCount =
334         1 + (BootSector.FATSectors * 2) + RootDirSectors;
335 
336     if (!QuickFormat)
337     {
338         Context->TotalSectorCount += SectorCount;
339 
340         Status = FatWipeSectors(FileHandle,
341                                 SectorCount,
342                                 (ULONG)BootSector.SectorsPerCluster,
343                                 (ULONG)BootSector.BytesPerSector,
344                                 Context);
345         if (!NT_SUCCESS(Status))
346         {
347             DPRINT("FatWipeSectors() failed with status 0x%.08x\n", Status);
348             return Status;
349         }
350     }
351 
352     Status = Fat16WriteBootSector(FileHandle,
353                                   &BootSector,
354                                   Context);
355     if (!NT_SUCCESS(Status))
356     {
357         DPRINT("Fat16WriteBootSector() failed with status 0x%.08x\n", Status);
358         return Status;
359     }
360 
361     /* Write first FAT copy */
362     Status = Fat16WriteFAT(FileHandle,
363                            0,
364                            &BootSector,
365                            Context);
366     if (!NT_SUCCESS(Status))
367     {
368         DPRINT("Fat16WriteFAT() failed with status 0x%.08x\n", Status);
369         return Status;
370     }
371 
372     /* Write second FAT copy */
373     Status = Fat16WriteFAT(FileHandle,
374                            (ULONG)BootSector.FATSectors,
375                            &BootSector,
376                            Context);
377     if (!NT_SUCCESS(Status))
378     {
379        DPRINT("Fat16WriteFAT() failed with status 0x%.08x.\n", Status);
380         return Status;
381     }
382 
383     Status = Fat16WriteRootDirectory(FileHandle,
384                                      &BootSector,
385                                      Context);
386     if (!NT_SUCCESS(Status))
387     {
388         DPRINT("Fat16WriteRootDirectory() failed with status 0x%.08x\n", Status);
389     }
390 
391     return Status;
392 }
393