xref: /reactos/sdk/lib/fslib/vfatlib/fat12.c (revision d5399189)
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, 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 Fat12WriteFAT(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 Size;
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 & 1*/
99     Buffer[0] = 0xf8; /* Media type */
100     Buffer[1] = 0xff;
101     Buffer[2] = 0xff;
102 
103     /* Write first sector of the FAT */
104     FileOffset.QuadPart = (SectorOffset + BootSector->ReservedSectors) * BootSector->BytesPerSector;
105     Status = NtWriteFile(FileHandle,
106                          NULL,
107                          NULL,
108                          NULL,
109                          &IoStatusBlock,
110                          Buffer,
111                          BootSector->BytesPerSector,
112                          &FileOffset,
113                          NULL);
114     if (!NT_SUCCESS(Status))
115     {
116         DPRINT("NtWriteFile() failed (Status %lx)\n", Status);
117         goto done;
118     }
119 
120     UpdateProgress(Context, 1);
121 
122     /* Zero the begin of the buffer */
123     RtlZeroMemory(Buffer, 3);
124 
125     /* Zero the rest of the FAT */
126     Sectors = 32 * 1024 / BootSector->BytesPerSector;
127     for (i = 1; i < (ULONG)BootSector->FATSectors; i += Sectors)
128     {
129         /* Zero some sectors of the FAT */
130         FileOffset.QuadPart = (SectorOffset + BootSector->ReservedSectors + i) * BootSector->BytesPerSector;
131         if (((ULONG)BootSector->FATSectors - i) <= Sectors)
132         {
133             Sectors = (ULONG)BootSector->FATSectors - i;
134         }
135 
136         Size = Sectors * BootSector->BytesPerSector;
137         Status = NtWriteFile(FileHandle,
138                              NULL,
139                              NULL,
140                              NULL,
141                              &IoStatusBlock,
142                              Buffer,
143                              Size,
144                              &FileOffset,
145                              NULL);
146         if (!NT_SUCCESS(Status))
147         {
148             DPRINT("NtWriteFile() failed (Status %lx)\n", Status);
149             goto done;
150         }
151 
152         UpdateProgress(Context, Sectors);
153     }
154 
155 done:
156     /* Free the buffer */
157     RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer);
158     return Status;
159 }
160 
161 
162 static NTSTATUS
163 Fat12WriteRootDirectory(IN HANDLE FileHandle,
164                         IN PFAT16_BOOT_SECTOR BootSector,
165                         IN OUT PFORMAT_CONTEXT Context)
166 {
167     IO_STATUS_BLOCK IoStatusBlock;
168     NTSTATUS Status = STATUS_SUCCESS;
169     PUCHAR Buffer;
170     LARGE_INTEGER FileOffset;
171     ULONG FirstRootDirSector;
172     ULONG RootDirSectors;
173     ULONG Sectors;
174     ULONG Size;
175     ULONG i;
176 
177     DPRINT("BootSector->ReservedSectors = %hu\n", BootSector->ReservedSectors);
178     DPRINT("BootSector->FATSectors = %hu\n", BootSector->FATSectors);
179     DPRINT("BootSector->SectorsPerCluster = %u\n", BootSector->SectorsPerCluster);
180 
181     /* Write cluster */
182     RootDirSectors = ((BootSector->RootEntries * 32) +
183         (BootSector->BytesPerSector - 1)) / BootSector->BytesPerSector;
184     FirstRootDirSector =
185         BootSector->ReservedSectors + (BootSector->FATCount * BootSector->FATSectors);
186 
187     DPRINT("RootDirSectors = %lu\n", RootDirSectors);
188     DPRINT("FirstRootDirSector = %lu\n", FirstRootDirSector);
189 
190     /* Allocate buffer for the cluster */
191     Buffer = (PUCHAR)RtlAllocateHeap(RtlGetProcessHeap(),
192                                      0,
193                                      32 * 1024);
194     if (Buffer == NULL)
195         return STATUS_INSUFFICIENT_RESOURCES;
196 
197     /* Zero the buffer */
198     RtlZeroMemory(Buffer, 32 * 1024);
199 
200     Sectors = 32 * 1024 / BootSector->BytesPerSector;
201     for (i = 0; i < RootDirSectors; i += Sectors)
202     {
203         /* Zero some sectors of the root directory */
204         FileOffset.QuadPart = (FirstRootDirSector + i) * BootSector->BytesPerSector;
205 
206         if ((RootDirSectors - i) <= Sectors)
207         {
208             Sectors = RootDirSectors - i;
209         }
210 
211         Size = Sectors * BootSector->BytesPerSector;
212 
213         Status = NtWriteFile(FileHandle,
214                              NULL,
215                              NULL,
216                              NULL,
217                              &IoStatusBlock,
218                              Buffer,
219                              Size,
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 Fat12Format(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 (DiskGeometry->MediaType == FixedMedia)
260         {
261             /* 4KB Cluster (Harddisk only) */
262             ClusterSize = 4096;
263         }
264         else
265         {
266             /* 512 byte cluster (floppy) */
267             ClusterSize = 512;
268         }
269     }
270 
271     SectorCount = PartitionInfo->PartitionLength.QuadPart >>
272         GetShiftCount(DiskGeometry->BytesPerSector); /* Use shifting to avoid 64-bit division */
273 
274     DPRINT("SectorCount = %lu\n", SectorCount);
275 
276     RtlZeroMemory(&BootSector, sizeof(FAT16_BOOT_SECTOR));
277     memcpy(&BootSector.OEMName[0], "MSWIN4.1", 8);
278     /* FIXME: Add dummy bootloader for real */
279     BootSector.Jump[0] = 0xeb;
280     BootSector.Jump[1] = 0x3c;
281     BootSector.Jump[2] = 0x90;
282     BootSector.BytesPerSector = DiskGeometry->BytesPerSector;
283     BootSector.SectorsPerCluster = ClusterSize / BootSector.BytesPerSector;
284     BootSector.ReservedSectors = 1;
285     BootSector.FATCount = 2;
286     BootSector.RootEntries = 512;
287     BootSector.Sectors = (SectorCount < 0x10000) ? (unsigned short)SectorCount : 0;
288     BootSector.Media = 0xf8;
289     BootSector.FATSectors = 0;  /* Set later. See below. */
290     BootSector.SectorsPerTrack = DiskGeometry->SectorsPerTrack;
291     BootSector.Heads = DiskGeometry->TracksPerCylinder;
292     BootSector.HiddenSectors = PartitionInfo->HiddenSectors;
293     BootSector.SectorsHuge = (SectorCount >= 0x10000) ? (unsigned long)SectorCount : 0;
294     BootSector.Drive = (DiskGeometry->MediaType == FixedMedia) ? 0x80 : 0x00;
295     BootSector.ExtBootSignature = 0x29;
296     BootSector.VolumeID = CalcVolumeSerialNumber();
297     if ((Label == NULL) || (Label->Buffer == NULL))
298     {
299         memcpy(&BootSector.VolumeLabel[0], "NO NAME    ", 11);
300     }
301     else
302     {
303         RtlUnicodeStringToOemString(&VolumeLabel, Label, TRUE);
304         RtlFillMemory(&BootSector.VolumeLabel[0], 11, ' ');
305         memcpy(&BootSector.VolumeLabel[0], VolumeLabel.Buffer,
306                VolumeLabel.Length < 11 ? VolumeLabel.Length : 11);
307         RtlFreeOemString(&VolumeLabel);
308     }
309 
310     memcpy(&BootSector.SysType[0], "FAT12   ", 8);
311 
312     RootDirSectors = ((BootSector.RootEntries * 32) +
313         (BootSector.BytesPerSector - 1)) / BootSector.BytesPerSector;
314 
315     /* Calculate number of FAT sectors */
316     /* ((BootSector.BytesPerSector * 2) / 3) FAT entries (12bit) fit into one sector */
317     TmpVal1 = SectorCount - (BootSector.ReservedSectors + RootDirSectors);
318     TmpVal2 = (((BootSector.BytesPerSector * 2) / 3) * BootSector.SectorsPerCluster) + BootSector.FATCount;
319     TmpVal3 = (TmpVal1 + (TmpVal2 - 1)) / TmpVal2;
320     BootSector.FATSectors = (unsigned short)(TmpVal3 & 0xffff);
321 
322     DPRINT("BootSector.FATSectors = %hx\n", BootSector.FATSectors);
323 
324     /* Init context data */
325     Context->TotalSectorCount =
326         1 + (BootSector.FATSectors * 2) + RootDirSectors;
327 
328     if (!QuickFormat)
329     {
330         Context->TotalSectorCount += SectorCount;
331 
332         Status = FatWipeSectors(FileHandle,
333                                 SectorCount,
334                                 (ULONG)BootSector.SectorsPerCluster,
335                                 (ULONG)BootSector.BytesPerSector,
336                                 Context);
337         if (!NT_SUCCESS(Status))
338         {
339             DPRINT("FatWipeSectors() failed with status 0x%.08x\n", Status);
340             return Status;
341         }
342     }
343 
344     Status = Fat12WriteBootSector(FileHandle,
345                                   &BootSector,
346                                   Context);
347     if (!NT_SUCCESS(Status))
348     {
349         DPRINT("Fat12WriteBootSector() failed with status 0x%.08x\n", Status);
350         return Status;
351     }
352 
353     /* Write first FAT copy */
354     Status = Fat12WriteFAT(FileHandle,
355                            0,
356                            &BootSector,
357                            Context);
358     if (!NT_SUCCESS(Status))
359     {
360         DPRINT("Fat12WriteFAT() failed with status 0x%.08x\n", Status);
361         return Status;
362     }
363 
364     /* Write second FAT copy */
365     Status = Fat12WriteFAT(FileHandle,
366                            (ULONG)BootSector.FATSectors,
367                            &BootSector,
368                            Context);
369     if (!NT_SUCCESS(Status))
370     {
371         DPRINT("Fat12WriteFAT() failed with status 0x%.08x.\n", Status);
372         return Status;
373     }
374 
375     Status = Fat12WriteRootDirectory(FileHandle,
376                                      &BootSector,
377                                      Context);
378     if (!NT_SUCCESS(Status))
379     {
380         DPRINT("Fat12WriteRootDirectory() failed with status 0x%.08x\n", Status);
381     }
382 
383     return Status;
384 }
385