This commit was manufactured by cvs2svn to create branch 'captive'.
[reactos.git] / lib / fslib / vfatlib / fat32.c
1 /*
2  * COPYRIGHT:   See COPYING in the top level directory
3  * PROJECT:     ReactOS VFAT filesystem library
4  * FILE:        fat32.c
5  * PURPOSE:     Fat32 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 Fat32WriteBootSector(IN HANDLE FileHandle,
56   IN PFAT32_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 FAT32 BPB to new bootsector */
74   memcpy((NewBootSector + 3),
75     &BootSector->OEMName[0],
76     87); /* FAT32 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   /* Write backup boot sector */
97   if (BootSector->BootBackup != 0x0000)
98     {
99       FileOffset.QuadPart = (ULONGLONG)((ULONG) BootSector->BootBackup * SECTORSIZE);
100       Status = NtWriteFile(FileHandle,
101         NULL,
102         NULL,
103         NULL,
104         &IoStatusBlock,
105         NewBootSector,
106         SECTORSIZE,
107         &FileOffset,
108         NULL);
109       if (!NT_SUCCESS(Status))
110         {
111           DPRINT("NtWriteFile() failed (Status %lx)\n", Status);
112           RtlFreeHeap(RtlGetProcessHeap(), 0, NewBootSector);
113           return(Status);
114         }
115     }
116
117   /* Free the new boot sector */
118   RtlFreeHeap(RtlGetProcessHeap(), 0, NewBootSector);
119
120   return(Status);
121 }
122
123
124 static NTSTATUS
125 Fat32WriteFsInfo(IN HANDLE FileHandle,
126   IN PFAT32_BOOT_SECTOR BootSector)
127 {
128   IO_STATUS_BLOCK IoStatusBlock;
129   NTSTATUS Status;
130   PFAT32_FSINFO FsInfo;
131   LARGE_INTEGER FileOffset;
132
133   /* Allocate buffer for new sector */
134   FsInfo = (PFAT32_FSINFO)RtlAllocateHeap(RtlGetProcessHeap(),
135     0,
136     BootSector->BytesPerSector);
137   if (FsInfo == NULL)
138     return(STATUS_INSUFFICIENT_RESOURCES);
139
140   /* Zero the new sector */
141   memset(FsInfo, 0, BootSector->BytesPerSector);
142
143   FsInfo->LeadSig = 0x41615252;
144   FsInfo->StrucSig = 0x61417272;
145   FsInfo->FreeCount = 0xffffffff;
146   FsInfo->NextFree = 0xffffffff;
147   FsInfo->TrailSig = 0xaa550000;
148
149   /* Write sector */
150   FileOffset.QuadPart = BootSector->FSInfoSector * BootSector->BytesPerSector;
151   Status = NtWriteFile(FileHandle,
152     NULL,
153     NULL,
154     NULL,
155     &IoStatusBlock,
156     FsInfo,
157     BootSector->BytesPerSector,
158     &FileOffset,
159     NULL);
160   if (!NT_SUCCESS(Status))
161     {
162       DPRINT("NtWriteFile() failed (Status %lx)\n", Status);
163       RtlFreeHeap(RtlGetProcessHeap(), 0, FsInfo);
164       return(Status);
165     }
166
167   /* Free the new sector buffer */
168   RtlFreeHeap(RtlGetProcessHeap(), 0, FsInfo);
169
170   return(Status);
171 }
172
173
174 static NTSTATUS
175 Fat32WriteFAT(IN HANDLE FileHandle,
176   ULONG SectorOffset,
177   IN PFAT32_BOOT_SECTOR BootSector)
178 {
179   IO_STATUS_BLOCK IoStatusBlock;
180   NTSTATUS Status;
181   PUCHAR Buffer;
182   LARGE_INTEGER FileOffset;
183   ULONG i;
184   ULONG Size;
185   ULONG Sectors;
186
187   /* Allocate buffer */
188   Buffer = (PUCHAR)RtlAllocateHeap(RtlGetProcessHeap(),
189     0,
190     64 * 1024);
191   if (Buffer == NULL)
192     return(STATUS_INSUFFICIENT_RESOURCES);
193
194   /* Zero the buffer */
195   memset(Buffer, 0, 64 * 1024);
196
197   /* FAT cluster 0 */
198   Buffer[0] = 0xf8; /* Media type */
199   Buffer[1] = 0xff;
200   Buffer[2] = 0xff;
201   Buffer[3] = 0x0f;
202   /* FAT cluster 1 */
203   Buffer[4] = 0xff; /* Clean shutdown, no disk read/write errors, end-of-cluster (EOC) mark */
204   Buffer[5] = 0xff;
205   Buffer[6] = 0xff;
206   Buffer[7] = 0x0f;
207   /* FAT cluster 2 */
208   Buffer[8] = 0xff; /* End of root directory */
209   Buffer[9] = 0xff;
210   Buffer[10] = 0xff;
211   Buffer[11] = 0x0f;
212
213   /* Write first sector of the FAT */
214   FileOffset.QuadPart = (SectorOffset + BootSector->ReservedSectors) * BootSector->BytesPerSector;
215   Status = NtWriteFile(FileHandle,
216     NULL,
217     NULL,
218     NULL,
219     &IoStatusBlock,
220     Buffer,
221     BootSector->BytesPerSector,
222     &FileOffset,
223     NULL);
224   if (!NT_SUCCESS(Status))
225     {
226       DPRINT("NtWriteFile() failed (Status %lx)\n", Status);
227       RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer);
228       return(Status);
229     }
230
231   /* Zero the begin of the buffer */
232   memset(Buffer, 0, 12);
233
234   /* Zero the rest of the FAT */
235   Sectors = 64 * 1024 / BootSector->BytesPerSector;
236   for (i = 1; i < BootSector->FATSectors32; i += Sectors)
237     {
238       /* Zero some sectors of the FAT */
239       FileOffset.QuadPart = (SectorOffset + BootSector->ReservedSectors + i) * BootSector->BytesPerSector;
240       Size = BootSector->FATSectors32 - i;
241       if (Size > Sectors)
242         {
243           Size = Sectors;
244         }
245       Size *= BootSector->BytesPerSector;
246       Status = NtWriteFile(FileHandle,
247         NULL,
248         NULL,
249         NULL,
250         &IoStatusBlock,
251         Buffer,
252         Size,
253         &FileOffset,
254         NULL);
255       if (!NT_SUCCESS(Status))
256         {
257           DPRINT("NtWriteFile() failed (Status %lx)\n", Status);
258           RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer);
259           return(Status);
260         }
261     }
262
263   /* Free the buffer */
264   RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer);
265
266   return(Status);
267 }
268
269
270 static NTSTATUS
271 Fat32WriteRootDirectory(IN HANDLE FileHandle,
272   IN PFAT32_BOOT_SECTOR BootSector)
273 {
274   IO_STATUS_BLOCK IoStatusBlock;
275   NTSTATUS Status;
276   PUCHAR Buffer;
277   LARGE_INTEGER FileOffset;
278   ULONGLONG FirstDataSector;
279   ULONGLONG FirstRootDirSector;
280
281   /* Allocate buffer for the cluster */
282   Buffer = (PUCHAR)RtlAllocateHeap(RtlGetProcessHeap(),
283     0,
284     BootSector->SectorsPerCluster * BootSector->BytesPerSector);
285   if (Buffer == NULL)
286     return(STATUS_INSUFFICIENT_RESOURCES);
287
288   /* Zero the buffer */
289   memset(Buffer, 0, BootSector->SectorsPerCluster * BootSector->BytesPerSector);
290
291   DPRINT("BootSector->ReservedSectors = %lu\n", BootSector->ReservedSectors);
292   DPRINT("BootSector->FATSectors32 = %lu\n", BootSector->FATSectors32);
293   DPRINT("BootSector->RootCluster = %lu\n", BootSector->RootCluster);
294   DPRINT("BootSector->SectorsPerCluster = %lu\n", BootSector->SectorsPerCluster);
295
296   /* Write cluster */
297   FirstDataSector = BootSector->ReservedSectors +
298     (BootSector->FATCount * BootSector->FATSectors32) + 0 /* RootDirSectors */;
299
300   DPRINT("FirstDataSector = %lu\n", FirstDataSector);
301
302   FirstRootDirSector = ((BootSector->RootCluster - 2) * BootSector->SectorsPerCluster) + FirstDataSector;
303   FileOffset.QuadPart = FirstRootDirSector * BootSector->BytesPerSector;
304
305   DPRINT("FirstRootDirSector = %lu\n", FirstRootDirSector);
306   DPRINT("FileOffset = %lu\n", FileOffset.QuadPart);
307
308   Status = NtWriteFile(FileHandle,
309     NULL,
310     NULL,
311     NULL,
312     &IoStatusBlock,
313     Buffer,
314     BootSector->SectorsPerCluster * BootSector->BytesPerSector,
315     &FileOffset,
316     NULL);
317   if (!NT_SUCCESS(Status))
318     {
319       DPRINT("NtWriteFile() failed (Status %lx)\n", Status);
320       RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer);
321       return(Status);
322     }
323
324   /* Free the buffer */
325   RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer);
326
327   return(Status);
328 }
329
330
331 NTSTATUS
332 Fat32Format (HANDLE  FileHandle,
333              PPARTITION_INFORMATION  PartitionInfo,
334              PDISK_GEOMETRY DiskGeometry,
335              PUNICODE_STRING Label,
336              BOOL  QuickFormat,
337              DWORD  ClusterSize,
338              PFMIFSCALLBACK  Callback)
339 {
340   FAT32_BOOT_SECTOR BootSector;
341   OEM_STRING VolumeLabel;
342   ULONG RootDirSectors;
343   ULONG TmpVal1;
344   ULONG TmpVal2;
345   NTSTATUS Status;
346
347   /* Calculate cluster size */
348   if (ClusterSize == 0)
349     {
350       if (PartitionInfo->PartitionLength.QuadPart < 8ULL * 1024ULL * 1024ULL * 1024ULL)
351         {
352           /* Partition < 8GB ==> 4KB Cluster */
353           ClusterSize = 4096;
354         }
355       else if (PartitionInfo->PartitionLength.QuadPart < 16ULL * 1024ULL * 1024ULL * 1024ULL)
356         {
357           /* Partition 8GB - 16GB ==> 8KB Cluster */
358           ClusterSize = 8192;
359         }
360       else if (PartitionInfo->PartitionLength.QuadPart < 32ULL * 1024ULL * 1024ULL * 1024ULL)
361         {
362           /* Partition 16GB - 32GB ==> 16KB Cluster */
363           ClusterSize = 16384;
364         }
365       else
366         {
367           /* Partition >= 32GB ==> 32KB Cluster */
368           ClusterSize = 32768;
369         }
370     }
371
372   memset(&BootSector, 0, sizeof(FAT32_BOOT_SECTOR));
373   memcpy(&BootSector.OEMName[0], "MSWIN4.1", 8);
374   BootSector.BytesPerSector = DiskGeometry->BytesPerSector;
375   BootSector.SectorsPerCluster = ClusterSize / BootSector.BytesPerSector;
376   BootSector.ReservedSectors = 32;
377   BootSector.FATCount = 2;
378   BootSector.RootEntries = 0;
379   BootSector.Sectors = 0;
380   BootSector.Media = 0xf8;
381   BootSector.FATSectors = 0;
382   BootSector.SectorsPerTrack = DiskGeometry->SectorsPerTrack;
383   BootSector.Heads = DiskGeometry->TracksPerCylinder;
384   BootSector.HiddenSectors = PartitionInfo->HiddenSectors;
385   BootSector.SectorsHuge = PartitionInfo->PartitionLength.QuadPart >>
386     GetShiftCount(BootSector.BytesPerSector); /* Use shifting to avoid 64-bit division */
387   BootSector.FATSectors32 = 0; /* Set later */
388   BootSector.ExtFlag = 0; /* Mirror all FATs */
389   BootSector.FSVersion = 0x0000; /* 0:0 */
390   BootSector.RootCluster = 2;
391   BootSector.FSInfoSector = 1;
392   BootSector.BootBackup = 6;
393   BootSector.Drive = 0xff; /* No BIOS boot drive available */
394   BootSector.ExtBootSignature = 0x29;
395   BootSector.VolumeID = CalcVolumeSerialNumber ();
396   if ((Label == NULL) || (Label->Buffer == NULL))
397     {
398       memcpy(&BootSector.VolumeLabel[0], "NO NAME    ", 11);
399     }
400   else
401     {
402       RtlUnicodeStringToOemString(&VolumeLabel, Label, TRUE);
403       memset(&BootSector.VolumeLabel[0], ' ', 11);
404       memcpy(&BootSector.VolumeLabel[0], VolumeLabel.Buffer,
405         VolumeLabel.Length < 11 ? VolumeLabel.Length : 11);
406       RtlFreeOemString(&VolumeLabel);
407     }
408   memcpy(&BootSector.SysType[0], "FAT32   ", 8);
409
410   RootDirSectors = ((BootSector.RootEntries * 32) +
411     (BootSector.BytesPerSector - 1)) / BootSector.BytesPerSector;
412
413   /* Calculate number of FAT sectors */
414   /* (BytesPerSector / 4) FAT entries (32bit) fit into one sector */
415   TmpVal1 = BootSector.SectorsHuge - BootSector.ReservedSectors;
416   TmpVal2 = ((BootSector.BytesPerSector / 4) * BootSector.SectorsPerCluster) + BootSector.FATCount;
417   BootSector.FATSectors32 = (TmpVal1 + (TmpVal2 - 1)) / TmpVal2;
418   DPRINT("FATSectors32 = %lu\n", BootSector.FATSectors32);
419
420   Status = Fat32WriteBootSector(FileHandle,
421     &BootSector);
422   if (!NT_SUCCESS(Status))
423     {
424       DPRINT("Fat32WriteBootSector() failed with status 0x%.08x\n", Status);
425       return Status;
426     }
427
428   Status = Fat32WriteFsInfo(FileHandle,
429     &BootSector);
430   if (!NT_SUCCESS(Status))
431     {
432       DPRINT("Fat32WriteFsInfo() failed with status 0x%.08x\n", Status);
433       return Status;
434     }
435
436   /* Write first FAT copy */
437   Status = Fat32WriteFAT(FileHandle,
438     0,
439     &BootSector);
440   if (!NT_SUCCESS(Status))
441     {
442       DPRINT("Fat32WriteFAT() failed with status 0x%.08x\n", Status);
443       return Status;
444     }
445
446   /* Write second FAT copy */
447   Status = Fat32WriteFAT(FileHandle,
448     BootSector.FATSectors32,
449     &BootSector);
450   if (!NT_SUCCESS(Status))
451     {
452       DPRINT("Fat32WriteFAT() failed with status 0x%.08x.\n", Status);
453       return Status;
454     }
455
456   Status = Fat32WriteRootDirectory(FileHandle,
457     &BootSector);
458   if (!NT_SUCCESS(Status))
459     {
460       DPRINT("Fat32WriteRootDirectory() failed with status 0x%.08x\n", Status);
461     }
462
463   return Status;
464 }