update for HEAD-2003091401
[reactos.git] / lib / fslib / vfatlib / fat12.c
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 (ekohl@rz-online.de)
8  * REVISIONS:
9  *   EK 05/04-2003 Created
10  */
11 #define NDEBUG
12 #include <debug.h>
13 #define NTOS_MODE_USER
14 #include <ntos.h>
15 #include <ddk/ntddscsi.h>
16 #include <mem.h>
17 #include "vfatlib.h"
18
19
20 static ULONG
21 GetShiftCount(ULONG Value)
22 {
23   ULONG i = 1;
24   while (Value > 0)
25     {
26       i++;
27       Value /= 2;
28     }
29   return i - 2;
30 }
31
32
33 static ULONG
34 CalcVolumeSerialNumber(VOID)
35 {
36   LARGE_INTEGER SystemTime;
37   TIME_FIELDS TimeFields;
38   ULONG Serial;
39   PUCHAR Buffer;
40
41   NtQuerySystemTime (&SystemTime);
42   RtlTimeToTimeFields (&SystemTime, &TimeFields);
43
44   Buffer = (PUCHAR)&Serial;
45   Buffer[0] = (UCHAR)(TimeFields.Year & 0xFF) + (UCHAR)(TimeFields.Hour & 0xFF);
46   Buffer[1] = (UCHAR)(TimeFields.Year >> 8) + (UCHAR)(TimeFields.Minute & 0xFF);
47   Buffer[2] = (UCHAR)(TimeFields.Month & 0xFF) + (UCHAR)(TimeFields.Second & 0xFF);
48   Buffer[3] = (UCHAR)(TimeFields.Day & 0xFF) + (UCHAR)(TimeFields.Milliseconds & 0xFF);
49
50   return Serial;
51 }
52
53
54 static NTSTATUS
55 Fat12WriteBootSector(IN HANDLE FileHandle,
56   IN PFAT16_BOOT_SECTOR BootSector)
57 {
58   IO_STATUS_BLOCK IoStatusBlock;
59   NTSTATUS Status;
60   PUCHAR NewBootSector;
61   LARGE_INTEGER FileOffset;
62
63   /* Allocate buffer for new bootsector */
64   NewBootSector = (PUCHAR)RtlAllocateHeap(RtlGetProcessHeap(),
65     0,
66     SECTORSIZE);
67   if (NewBootSector == NULL)
68     return(STATUS_INSUFFICIENT_RESOURCES);
69
70   /* Zero the new bootsector */
71   memset(NewBootSector, 0, SECTORSIZE);
72
73   /* Copy FAT16 BPB to new bootsector */
74   memcpy((NewBootSector + 3),
75     &BootSector->OEMName[0],
76     59); /* FAT16 BPB length (up to (not including) Res2) */
77
78   /* Write sector 0 */
79   FileOffset.QuadPart = 0ULL;
80   Status = NtWriteFile(FileHandle,
81     NULL,
82     NULL,
83     NULL,
84     &IoStatusBlock,
85     NewBootSector,
86     SECTORSIZE,
87     &FileOffset,
88     NULL);
89   if (!NT_SUCCESS(Status))
90     {
91       DPRINT("NtWriteFile() failed (Status %lx)\n", Status);
92       RtlFreeHeap(RtlGetProcessHeap(), 0, NewBootSector);
93       return(Status);
94     }
95
96   /* Free the new boot sector */
97   RtlFreeHeap(RtlGetProcessHeap(), 0, NewBootSector);
98
99   return(Status);
100 }
101
102
103 static NTSTATUS
104 Fat12WriteFAT(IN HANDLE FileHandle,
105   ULONG SectorOffset,
106   IN PFAT16_BOOT_SECTOR BootSector)
107 {
108   IO_STATUS_BLOCK IoStatusBlock;
109   NTSTATUS Status;
110   PUCHAR Buffer;
111   LARGE_INTEGER FileOffset;
112   ULONG i;
113   ULONG Size;
114   ULONG Sectors;
115
116   /* Allocate buffer */
117   Buffer = (PUCHAR)RtlAllocateHeap(RtlGetProcessHeap(),
118     0,
119     32 * 1024);
120   if (Buffer == NULL)
121     return(STATUS_INSUFFICIENT_RESOURCES);
122
123   /* Zero the buffer */
124   memset(Buffer, 0, 32 * 1024);
125
126   /* FAT cluster 0 & 1*/
127   Buffer[0] = 0xf8; /* Media type */
128   Buffer[1] = 0xff;
129   Buffer[2] = 0xff;
130
131   /* Write first sector of the FAT */
132   FileOffset.QuadPart = (SectorOffset + BootSector->ReservedSectors) * BootSector->BytesPerSector;
133   Status = NtWriteFile(FileHandle,
134     NULL,
135     NULL,
136     NULL,
137     &IoStatusBlock,
138     Buffer,
139     BootSector->BytesPerSector,
140     &FileOffset,
141     NULL);
142   if (!NT_SUCCESS(Status))
143     {
144       DPRINT("NtWriteFile() failed (Status %lx)\n", Status);
145       RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer);
146       return(Status);
147     }
148
149   /* Zero the begin of the buffer */
150   memset(Buffer, 0, 3);
151
152   /* Zero the rest of the FAT */
153   Sectors = 32 * 1024 / BootSector->BytesPerSector;
154   for (i = 1; i < (ULONG)BootSector->FATSectors; i += Sectors)
155     {
156       /* Zero some sectors of the FAT */
157       FileOffset.QuadPart = (SectorOffset + BootSector->ReservedSectors + i) * BootSector->BytesPerSector;
158       Size = (ULONG)BootSector->FATSectors - i;
159       if (Size > Sectors)
160         {
161           Size = Sectors;
162         }
163       Size *= BootSector->BytesPerSector;
164       Status = NtWriteFile(FileHandle,
165         NULL,
166         NULL,
167         NULL,
168         &IoStatusBlock,
169         Buffer,
170         Size,
171         &FileOffset,
172         NULL);
173       if (!NT_SUCCESS(Status))
174         {
175           DPRINT("NtWriteFile() failed (Status %lx)\n", Status);
176           RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer);
177           return(Status);
178         }
179     }
180
181   /* Free the buffer */
182   RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer);
183
184   return(Status);
185 }
186
187
188 static NTSTATUS
189 Fat12WriteRootDirectory(IN HANDLE FileHandle,
190   IN PFAT16_BOOT_SECTOR BootSector)
191 {
192   IO_STATUS_BLOCK IoStatusBlock;
193   NTSTATUS Status;
194   PUCHAR Buffer;
195   LARGE_INTEGER FileOffset;
196   ULONG FirstRootDirSector;
197   ULONG RootDirSectors;
198   ULONG Sectors;
199   ULONG Size;
200   ULONG i;
201
202   DPRINT("BootSector->ReservedSectors = %hu\n", BootSector->ReservedSectors);
203   DPRINT("BootSector->FATSectors = %hu\n", BootSector->FATSectors);
204   DPRINT("BootSector->SectorsPerCluster = %u\n", BootSector->SectorsPerCluster);
205
206   /* Write cluster */
207   RootDirSectors = ((BootSector->RootEntries * 32) +
208     (BootSector->BytesPerSector - 1)) / BootSector->BytesPerSector;
209   FirstRootDirSector =
210     BootSector->ReservedSectors + (BootSector->FATCount * BootSector->FATSectors);
211
212   DPRINT("RootDirSectors = %lu\n", RootDirSectors);
213   DPRINT("FirstRootDirSector = %lu\n", FirstRootDirSector);
214
215   /* Allocate buffer for the cluster */
216   Buffer = (PUCHAR)RtlAllocateHeap(RtlGetProcessHeap(),
217     0,
218     32 * 1024);
219   if (Buffer == NULL)
220     return(STATUS_INSUFFICIENT_RESOURCES);
221
222   /* Zero the buffer */
223   memset(Buffer, 0, 32 * 1024);
224
225   Sectors = 32 * 1024 / BootSector->BytesPerSector;
226   for (i = 1; i < RootDirSectors; i += Sectors)
227     {
228       /* Zero some sectors of the root directory */
229       FileOffset.QuadPart = (FirstRootDirSector + i) * BootSector->BytesPerSector;
230       Size = RootDirSectors - i;
231       if (Size > Sectors)
232         {
233           Size = Sectors;
234         }
235       Size *= BootSector->BytesPerSector;
236
237       Status = NtWriteFile(FileHandle,
238         NULL,
239         NULL,
240         NULL,
241         &IoStatusBlock,
242         Buffer,
243         Size,
244         &FileOffset,
245         NULL);
246       if (!NT_SUCCESS(Status))
247         {
248           DPRINT("NtWriteFile() failed (Status %lx)\n", Status);
249           RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer);
250           return(Status);
251         }
252     }
253
254   /* Free the buffer */
255   RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer);
256
257   return(Status);
258 }
259
260
261 NTSTATUS
262 Fat12Format (HANDLE  FileHandle,
263              PPARTITION_INFORMATION  PartitionInfo,
264              PDISK_GEOMETRY DiskGeometry,
265              PUNICODE_STRING Label,
266              BOOL  QuickFormat,
267              DWORD  ClusterSize,
268              PFMIFSCALLBACK  Callback)
269 {
270   FAT16_BOOT_SECTOR BootSector;
271   OEM_STRING VolumeLabel;
272   ULONG SectorCount;
273   ULONG RootDirSectors;
274   ULONG TmpVal1;
275   ULONG TmpVal2;
276   ULONG TmpVal3;
277   NTSTATUS Status;
278
279   /* Calculate cluster size */
280   if (ClusterSize == 0)
281     {
282       /* 4KB Cluster (Harddisk only) */
283       ClusterSize = 4096;
284     }
285
286   SectorCount = PartitionInfo->PartitionLength.QuadPart >>
287     GetShiftCount(DiskGeometry->BytesPerSector); /* Use shifting to avoid 64-bit division */
288
289   DPRINT("SectorCount = %lu\n", SectorCount);
290
291   memset(&BootSector, 0, sizeof(FAT16_BOOT_SECTOR));
292   memcpy(&BootSector.OEMName[0], "MSWIN4.1", 8);
293   BootSector.BytesPerSector = DiskGeometry->BytesPerSector;
294   BootSector.SectorsPerCluster = ClusterSize / BootSector.BytesPerSector;
295   BootSector.ReservedSectors = 1;
296   BootSector.FATCount = 2;
297   BootSector.RootEntries = 512;
298   BootSector.Sectors = (SectorCount < 0x10000) ? (unsigned short)SectorCount : 0;
299   BootSector.Media = 0xf8;
300   BootSector.FATSectors = 0;  /* Set later. See below. */
301   BootSector.SectorsPerTrack = DiskGeometry->SectorsPerTrack;
302   BootSector.Heads = DiskGeometry->TracksPerCylinder;
303   BootSector.HiddenSectors = PartitionInfo->HiddenSectors;
304   BootSector.SectorsHuge = (SectorCount >= 0x10000) ? (unsigned long)SectorCount : 0;
305   BootSector.Drive = 0xff; /* No BIOS boot drive available */
306   BootSector.ExtBootSignature = 0x29;
307   BootSector.VolumeID = CalcVolumeSerialNumber();
308   if ((Label == NULL) || (Label->Buffer == NULL))
309     {
310       memcpy(&BootSector.VolumeLabel[0], "NO NAME    ", 11);
311     }
312   else
313     {
314       RtlUnicodeStringToOemString(&VolumeLabel, Label, TRUE);
315       memset(&BootSector.VolumeLabel[0], ' ', 11);
316       memcpy(&BootSector.VolumeLabel[0], VolumeLabel.Buffer,
317         VolumeLabel.Length < 11 ? VolumeLabel.Length : 11);
318       RtlFreeOemString(&VolumeLabel);
319     }
320   memcpy(&BootSector.SysType[0], "FAT12   ", 8);
321
322   RootDirSectors = ((BootSector.RootEntries * 32) +
323     (BootSector.BytesPerSector - 1)) / BootSector.BytesPerSector;
324
325   /* Calculate number of FAT sectors */
326   /* ((BootSector.BytesPerSector * 2) / 3) FAT entries (12bit) fit into one sector */
327   TmpVal1 = SectorCount - (BootSector.ReservedSectors + RootDirSectors);
328   TmpVal2 = (((BootSector.BytesPerSector * 2) / 3) * BootSector.SectorsPerCluster) + BootSector.FATCount;
329   TmpVal3 = (TmpVal1 + (TmpVal2 - 1)) / TmpVal2;
330   BootSector.FATSectors = (unsigned short)(TmpVal3 & 0xffff);
331
332   DPRINT("BootSector.FATSectors = %hx\n", BootSector.FATSectors);
333
334   Status = Fat12WriteBootSector(FileHandle,
335     &BootSector);
336   if (!NT_SUCCESS(Status))
337     {
338       DPRINT("Fat12WriteBootSector() failed with status 0x%.08x\n", Status);
339       return Status;
340     }
341
342   /* Write first FAT copy */
343   Status = Fat12WriteFAT(FileHandle,
344     0,
345     &BootSector);
346   if (!NT_SUCCESS(Status))
347     {
348       DPRINT("Fat12WriteFAT() failed with status 0x%.08x\n", Status);
349       return Status;
350     }
351
352   /* Write second FAT copy */
353   Status = Fat12WriteFAT(FileHandle,
354     (ULONG)BootSector.FATSectors,
355     &BootSector);
356   if (!NT_SUCCESS(Status))
357     {
358       DPRINT("Fat12WriteFAT() failed with status 0x%.08x.\n", Status);
359       return Status;
360     }
361
362   Status = Fat12WriteRootDirectory(FileHandle,
363     &BootSector);
364   if (!NT_SUCCESS(Status))
365     {
366       DPRINT("Fat12WriteRootDirectory() failed with status 0x%.08x\n", Status);
367     }
368
369   return Status;
370 }