update for HEAD-2003091401
[reactos.git] / lib / fslib / vfatlib / fat16.c
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 (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 Fat16WriteBootSector(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 Fat16WriteFAT(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 */
127   Buffer[0] = 0xf8; /* Media type */
128   Buffer[1] = 0xff;
129
130   /* FAT cluster 1 */
131   Buffer[2] = 0xff; /* Clean shutdown, no disk read/write errors, end-of-cluster (EOC) mark */
132   Buffer[3] = 0xff;
133
134   /* Write first sector of the FAT */
135   FileOffset.QuadPart = (SectorOffset + BootSector->ReservedSectors) * BootSector->BytesPerSector;
136   Status = NtWriteFile(FileHandle,
137     NULL,
138     NULL,
139     NULL,
140     &IoStatusBlock,
141     Buffer,
142     BootSector->BytesPerSector,
143     &FileOffset,
144     NULL);
145   if (!NT_SUCCESS(Status))
146     {
147       DPRINT("NtWriteFile() failed (Status %lx)\n", Status);
148       RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer);
149       return(Status);
150     }
151
152   /* Zero the begin of the buffer */
153   memset(Buffer, 0, 4);
154
155   /* Zero the rest of the FAT */
156   Sectors = 32 * 1024 / BootSector->BytesPerSector;
157   for (i = 1; i < (ULONG)BootSector->FATSectors; i += Sectors)
158     {
159       /* Zero some sectors of the FAT */
160       FileOffset.QuadPart = (SectorOffset + BootSector->ReservedSectors + i) * BootSector->BytesPerSector;
161       Size = (ULONG)BootSector->FATSectors - i;
162       if (Size > Sectors)
163         {
164           Size = Sectors;
165         }
166       Size *= BootSector->BytesPerSector;
167       Status = NtWriteFile(FileHandle,
168         NULL,
169         NULL,
170         NULL,
171         &IoStatusBlock,
172         Buffer,
173         Size,
174         &FileOffset,
175         NULL);
176       if (!NT_SUCCESS(Status))
177         {
178           DPRINT("NtWriteFile() failed (Status %lx)\n", Status);
179           RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer);
180           return(Status);
181         }
182     }
183
184   /* Free the buffer */
185   RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer);
186
187   return(Status);
188 }
189
190
191 static NTSTATUS
192 Fat16WriteRootDirectory(IN HANDLE FileHandle,
193   IN PFAT16_BOOT_SECTOR BootSector)
194 {
195   IO_STATUS_BLOCK IoStatusBlock;
196   NTSTATUS Status;
197   PUCHAR Buffer;
198   LARGE_INTEGER FileOffset;
199   ULONG FirstRootDirSector;
200   ULONG RootDirSectors;
201   ULONG Sectors;
202   ULONG Size;
203   ULONG i;
204
205   DPRINT("BootSector->ReservedSectors = %hu\n", BootSector->ReservedSectors);
206   DPRINT("BootSector->FATSectors = %hu\n", BootSector->FATSectors);
207   DPRINT("BootSector->SectorsPerCluster = %u\n", BootSector->SectorsPerCluster);
208
209   /* Write cluster */
210   RootDirSectors = ((BootSector->RootEntries * 32) +
211     (BootSector->BytesPerSector - 1)) / BootSector->BytesPerSector;
212   FirstRootDirSector =
213     BootSector->ReservedSectors + (BootSector->FATCount * BootSector->FATSectors);
214
215   DPRINT("RootDirSectors = %lu\n", RootDirSectors);
216   DPRINT("FirstRootDirSector = %lu\n", FirstRootDirSector);
217
218   /* Allocate buffer for the cluster */
219   Buffer = (PUCHAR)RtlAllocateHeap(RtlGetProcessHeap(),
220     0,
221     32 * 1024);
222   if (Buffer == NULL)
223     return(STATUS_INSUFFICIENT_RESOURCES);
224
225   /* Zero the buffer */
226   memset(Buffer, 0, 32 * 1024);
227
228   Sectors = 32 * 1024 / BootSector->BytesPerSector;
229   for (i = 1; i < RootDirSectors; i += Sectors)
230     {
231       /* Zero some sectors of the root directory */
232       FileOffset.QuadPart = (FirstRootDirSector + i) * BootSector->BytesPerSector;
233       Size = RootDirSectors - i;
234       if (Size > Sectors)
235         {
236           Size = Sectors;
237         }
238       Size *= BootSector->BytesPerSector;
239
240       Status = NtWriteFile(FileHandle,
241         NULL,
242         NULL,
243         NULL,
244         &IoStatusBlock,
245         Buffer,
246         Size,
247         &FileOffset,
248         NULL);
249       if (!NT_SUCCESS(Status))
250         {
251           DPRINT("NtWriteFile() failed (Status %lx)\n", Status);
252           RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer);
253           return(Status);
254         }
255     }
256
257   /* Free the buffer */
258   RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer);
259
260   return(Status);
261 }
262
263
264 NTSTATUS
265 Fat16Format (HANDLE  FileHandle,
266              PPARTITION_INFORMATION  PartitionInfo,
267              PDISK_GEOMETRY DiskGeometry,
268              PUNICODE_STRING Label,
269              BOOL  QuickFormat,
270              DWORD  ClusterSize,
271              PFMIFSCALLBACK  Callback)
272 {
273   FAT16_BOOT_SECTOR BootSector;
274   OEM_STRING VolumeLabel;
275   ULONG SectorCount;
276   ULONG RootDirSectors;
277   ULONG TmpVal1;
278   ULONG TmpVal2;
279   ULONG TmpVal3;
280   NTSTATUS Status;
281
282   /* Calculate cluster size */
283   if (ClusterSize == 0)
284     {
285       if (PartitionInfo->PartitionLength.QuadPart < 16ULL * 1024ULL * 1024ULL)
286         {
287           /* Partition < 16MB ==> 1KB Cluster */
288           ClusterSize = 1024;
289         }
290       else if (PartitionInfo->PartitionLength.QuadPart < 128ULL * 1024ULL * 1024ULL)
291         {
292           /* Partition < 128MB ==> 2KB Cluster */
293           ClusterSize = 2048;
294         }
295       else if (PartitionInfo->PartitionLength.QuadPart < 256ULL * 1024ULL * 1024ULL)
296         {
297           /* Partition < 256MB ==> 4KB Cluster */
298           ClusterSize = 4096;
299         }
300       else
301         {
302           /* Partition >= 256MB (< 512MB) ==> 8KB Cluster */
303           ClusterSize = 8192;
304         }
305     }
306
307   SectorCount = PartitionInfo->PartitionLength.QuadPart >>
308     GetShiftCount(DiskGeometry->BytesPerSector); /* Use shifting to avoid 64-bit division */
309
310   memset(&BootSector, 0, sizeof(FAT16_BOOT_SECTOR));
311   memcpy(&BootSector.OEMName[0], "MSWIN4.1", 8);
312   BootSector.BytesPerSector = DiskGeometry->BytesPerSector;
313   BootSector.SectorsPerCluster = ClusterSize / BootSector.BytesPerSector;
314   BootSector.ReservedSectors = 1;
315   BootSector.FATCount = 2;
316   BootSector.RootEntries = 512;
317   BootSector.Sectors = (SectorCount < 0x10000) ? (unsigned short)SectorCount : 0;
318   BootSector.Media = 0xf8;
319   BootSector.FATSectors = 0;  /* Set later. See below. */
320   BootSector.SectorsPerTrack = DiskGeometry->SectorsPerTrack;
321   BootSector.Heads = DiskGeometry->TracksPerCylinder;
322   BootSector.HiddenSectors = PartitionInfo->HiddenSectors;
323   BootSector.SectorsHuge = (SectorCount >= 0x10000) ? (unsigned long)SectorCount : 0;
324   BootSector.Drive = 0xff; /* No BIOS boot drive available */
325   BootSector.ExtBootSignature = 0x29;
326   BootSector.VolumeID = CalcVolumeSerialNumber();
327   if ((Label == NULL) || (Label->Buffer == NULL))
328     {
329       memcpy(&BootSector.VolumeLabel[0], "NO NAME    ", 11);
330     }
331   else
332     {
333       RtlUnicodeStringToOemString(&VolumeLabel, Label, TRUE);
334       memset(&BootSector.VolumeLabel[0], ' ', 11);
335       memcpy(&BootSector.VolumeLabel[0], VolumeLabel.Buffer,
336         VolumeLabel.Length < 11 ? VolumeLabel.Length : 11);
337       RtlFreeOemString(&VolumeLabel);
338     }
339   memcpy(&BootSector.SysType[0], "FAT16   ", 8);
340
341   DPRINT("BootSector.SectorsHuge = %lx\n", BootSector.SectorsHuge);
342
343   RootDirSectors = ((BootSector.RootEntries * 32) +
344     (BootSector.BytesPerSector - 1)) / BootSector.BytesPerSector;
345
346   /* Calculate number of FAT sectors */
347   /* (BootSector.BytesPerSector / 2) FAT entries (16bit) fit into one sector */
348   TmpVal1 = SectorCount - (BootSector.ReservedSectors + RootDirSectors);
349   TmpVal2 = ((BootSector.BytesPerSector / 2) * BootSector.SectorsPerCluster) + BootSector.FATCount;
350   TmpVal3 = (TmpVal1 + (TmpVal2 - 1)) / TmpVal2;
351   BootSector.FATSectors = (unsigned short)(TmpVal3 & 0xffff);
352   DPRINT("BootSector.FATSectors = %hu\n", BootSector.FATSectors);
353
354   Status = Fat16WriteBootSector(FileHandle,
355     &BootSector);
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   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   if (!NT_SUCCESS(Status))
377     {
378       DPRINT("Fat16WriteFAT() failed with status 0x%.08x.\n", Status);
379       return Status;
380     }
381
382   Status = Fat16WriteRootDirectory(FileHandle,
383     &BootSector);
384   if (!NT_SUCCESS(Status))
385     {
386       DPRINT("Fat16WriteRootDirectory() failed with status 0x%.08x\n", Status);
387     }
388
389   return Status;
390 }