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