xref: /reactos/sdk/lib/fslib/vfatlib/fat12.c (revision 595b846d)
1 /*
2  * COPYRIGHT:   See COPYING in the top level directory
3  * PROJECT:     ReactOS VFAT filesystem library
4  * FILE:        fat12.c
5  * PURPOSE:     Fat12 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 Fat12WriteBootSector(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 Fat12WriteFAT(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 Size;
87     ULONG Sectors;
88 
89     /* Allocate buffer */
90     Buffer = (PUCHAR)RtlAllocateHeap(RtlGetProcessHeap(),
91                                      0,
92                                      32 * 1024);
93     if (Buffer == NULL)
94         return STATUS_INSUFFICIENT_RESOURCES;
95 
96     /* Zero the buffer */
97     RtlZeroMemory(Buffer, 32 * 1024);
98 
99     /* FAT cluster 0 & 1*/
100     Buffer[0] = 0xf8; /* Media type */
101     Buffer[1] = 0xff;
102     Buffer[2] = 0xff;
103 
104     /* Write first sector of the FAT */
105     FileOffset.QuadPart = (SectorOffset + BootSector->ReservedSectors) * BootSector->BytesPerSector;
106     Status = NtWriteFile(FileHandle,
107                          NULL,
108                          NULL,
109                          NULL,
110                          &IoStatusBlock,
111                          Buffer,
112                          BootSector->BytesPerSector,
113                          &FileOffset,
114                          NULL);
115     if (!NT_SUCCESS(Status))
116     {
117         DPRINT("NtWriteFile() failed (Status %lx)\n", Status);
118         goto done;
119     }
120 
121     UpdateProgress(Context, 1);
122 
123     /* Zero the begin of the buffer */
124     RtlZeroMemory(Buffer, 3);
125 
126     /* Zero the rest of the FAT */
127     Sectors = 32 * 1024 / BootSector->BytesPerSector;
128     for (i = 1; i < (ULONG)BootSector->FATSectors; i += Sectors)
129     {
130         /* Zero some sectors of the FAT */
131         FileOffset.QuadPart = (SectorOffset + BootSector->ReservedSectors + i) * BootSector->BytesPerSector;
132         if (((ULONG)BootSector->FATSectors - i) <= Sectors)
133         {
134             Sectors = (ULONG)BootSector->FATSectors - i;
135         }
136 
137         Size = Sectors * BootSector->BytesPerSector;
138         Status = NtWriteFile(FileHandle,
139                              NULL,
140                              NULL,
141                              NULL,
142                              &IoStatusBlock,
143                              Buffer,
144                              Size,
145                              &FileOffset,
146                              NULL);
147         if (!NT_SUCCESS(Status))
148         {
149             DPRINT("NtWriteFile() failed (Status %lx)\n", Status);
150             goto done;
151         }
152 
153         UpdateProgress(Context, Sectors);
154     }
155 
156 done:
157     /* Free the buffer */
158     RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer);
159     return Status;
160 }
161 
162 
163 static NTSTATUS
164 Fat12WriteRootDirectory(IN HANDLE FileHandle,
165                         IN PFAT16_BOOT_SECTOR BootSector,
166                         IN OUT PFORMAT_CONTEXT Context)
167 {
168     IO_STATUS_BLOCK IoStatusBlock;
169     NTSTATUS Status = STATUS_SUCCESS;
170     PUCHAR Buffer;
171     LARGE_INTEGER FileOffset;
172     ULONG FirstRootDirSector;
173     ULONG RootDirSectors;
174     ULONG Sectors;
175     ULONG Size;
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         Size = Sectors * BootSector->BytesPerSector;
213 
214         Status = NtWriteFile(FileHandle,
215                              NULL,
216                              NULL,
217                              NULL,
218                              &IoStatusBlock,
219                              Buffer,
220                              Size,
221                              &FileOffset,
222                              NULL);
223         if (!NT_SUCCESS(Status))
224         {
225             DPRINT("NtWriteFile() failed (Status %lx)\n", Status);
226             goto done;
227         }
228 
229         UpdateProgress(Context, Sectors);
230     }
231 
232 done:
233     /* Free the buffer */
234     RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer);
235     return Status;
236 }
237 
238 
239 NTSTATUS
240 Fat12Format(IN HANDLE FileHandle,
241             IN PPARTITION_INFORMATION PartitionInfo,
242             IN PDISK_GEOMETRY DiskGeometry,
243             IN PUNICODE_STRING Label,
244             IN BOOLEAN QuickFormat,
245             IN ULONG ClusterSize,
246             IN OUT PFORMAT_CONTEXT Context)
247 {
248     FAT16_BOOT_SECTOR BootSector;
249     OEM_STRING VolumeLabel;
250     ULONG SectorCount;
251     ULONG RootDirSectors;
252     ULONG TmpVal1;
253     ULONG TmpVal2;
254     ULONG TmpVal3;
255     NTSTATUS Status;
256 
257     /* Calculate cluster size */
258     if (ClusterSize == 0)
259     {
260         if (DiskGeometry->MediaType == FixedMedia)
261         {
262             /* 4KB Cluster (Harddisk only) */
263             ClusterSize = 4096;
264         }
265         else
266         {
267             /* 512 byte cluster (floppy) */
268             ClusterSize = 512;
269         }
270     }
271 
272     SectorCount = PartitionInfo->PartitionLength.QuadPart >>
273         GetShiftCount(DiskGeometry->BytesPerSector); /* Use shifting to avoid 64-bit division */
274 
275     DPRINT("SectorCount = %lu\n", SectorCount);
276 
277     RtlZeroMemory(&BootSector, sizeof(FAT16_BOOT_SECTOR));
278     memcpy(&BootSector.OEMName[0], "MSWIN4.1", 8);
279     /* FIXME: Add dummy bootloader for real */
280     BootSector.Jump[0] = 0xeb;
281     BootSector.Jump[1] = 0x3c;
282     BootSector.Jump[2] = 0x90;
283     BootSector.BytesPerSector = DiskGeometry->BytesPerSector;
284     BootSector.SectorsPerCluster = ClusterSize / BootSector.BytesPerSector;
285     BootSector.ReservedSectors = 1;
286     BootSector.FATCount = 2;
287     BootSector.RootEntries = 512;
288     BootSector.Sectors = (SectorCount < 0x10000) ? (unsigned short)SectorCount : 0;
289     BootSector.Media = 0xf8;
290     BootSector.FATSectors = 0;  /* Set later. See below. */
291     BootSector.SectorsPerTrack = DiskGeometry->SectorsPerTrack;
292     BootSector.Heads = DiskGeometry->TracksPerCylinder;
293     BootSector.HiddenSectors = PartitionInfo->HiddenSectors;
294     BootSector.SectorsHuge = (SectorCount >= 0x10000) ? (unsigned long)SectorCount : 0;
295     BootSector.Drive = (DiskGeometry->MediaType == FixedMedia) ? 0x80 : 0x00;
296     BootSector.ExtBootSignature = 0x29;
297     BootSector.VolumeID = CalcVolumeSerialNumber();
298     if ((Label == NULL) || (Label->Buffer == NULL))
299     {
300         memcpy(&BootSector.VolumeLabel[0], "NO NAME    ", 11);
301     }
302     else
303     {
304         RtlUnicodeStringToOemString(&VolumeLabel, Label, TRUE);
305         RtlFillMemory(&BootSector.VolumeLabel[0], 11, ' ');
306         memcpy(&BootSector.VolumeLabel[0], VolumeLabel.Buffer,
307                VolumeLabel.Length < 11 ? VolumeLabel.Length : 11);
308         RtlFreeOemString(&VolumeLabel);
309     }
310 
311     memcpy(&BootSector.SysType[0], "FAT12   ", 8);
312 
313     RootDirSectors = ((BootSector.RootEntries * 32) +
314         (BootSector.BytesPerSector - 1)) / BootSector.BytesPerSector;
315 
316     /* Calculate number of FAT sectors */
317     /* ((BootSector.BytesPerSector * 2) / 3) FAT entries (12bit) fit into one sector */
318     TmpVal1 = SectorCount - (BootSector.ReservedSectors + RootDirSectors);
319     TmpVal2 = (((BootSector.BytesPerSector * 2) / 3) * BootSector.SectorsPerCluster) + BootSector.FATCount;
320     TmpVal3 = (TmpVal1 + (TmpVal2 - 1)) / TmpVal2;
321     BootSector.FATSectors = (unsigned short)(TmpVal3 & 0xffff);
322 
323     DPRINT("BootSector.FATSectors = %hx\n", BootSector.FATSectors);
324 
325     /* Init context data */
326     Context->TotalSectorCount =
327         1 + (BootSector.FATSectors * 2) + RootDirSectors;
328 
329     if (!QuickFormat)
330     {
331         Context->TotalSectorCount += SectorCount;
332 
333         Status = FatWipeSectors(FileHandle,
334                                 SectorCount,
335                                 (ULONG)BootSector.SectorsPerCluster,
336                                 (ULONG)BootSector.BytesPerSector,
337                                 Context);
338         if (!NT_SUCCESS(Status))
339         {
340             DPRINT("FatWipeSectors() failed with status 0x%.08x\n", Status);
341             return Status;
342         }
343     }
344 
345     Status = Fat12WriteBootSector(FileHandle,
346                                   &BootSector,
347                                   Context);
348     if (!NT_SUCCESS(Status))
349     {
350         DPRINT("Fat12WriteBootSector() failed with status 0x%.08x\n", Status);
351         return Status;
352     }
353 
354     /* Write first FAT copy */
355     Status = Fat12WriteFAT(FileHandle,
356                            0,
357                            &BootSector,
358                            Context);
359     if (!NT_SUCCESS(Status))
360     {
361         DPRINT("Fat12WriteFAT() failed with status 0x%.08x\n", Status);
362         return Status;
363     }
364 
365     /* Write second FAT copy */
366     Status = Fat12WriteFAT(FileHandle,
367                            (ULONG)BootSector.FATSectors,
368                            &BootSector,
369                            Context);
370     if (!NT_SUCCESS(Status))
371     {
372         DPRINT("Fat12WriteFAT() failed with status 0x%.08x.\n", Status);
373         return Status;
374     }
375 
376     Status = Fat12WriteRootDirectory(FileHandle,
377                                      &BootSector,
378                                      Context);
379     if (!NT_SUCCESS(Status))
380     {
381         DPRINT("Fat12WriteRootDirectory() failed with status 0x%.08x\n", Status);
382     }
383 
384     return Status;
385 }
386