[VFATLIB]
[reactos.git] / reactos / 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
8 * REVISIONS:
9 * EK 05/04-2003 Created
10 */
11 #include "vfatlib.h"
12
13 #define NDEBUG
14 #include <debug.h>
15
16 static ULONG
17 GetShiftCount(IN ULONG Value)
18 {
19 ULONG i = 1;
20
21 while (Value > 0)
22 {
23 i++;
24 Value /= 2;
25 }
26
27 return i - 2;
28 }
29
30
31 static ULONG
32 CalcVolumeSerialNumber(VOID)
33 {
34 LARGE_INTEGER SystemTime;
35 TIME_FIELDS TimeFields;
36 ULONG Serial;
37 PUCHAR Buffer;
38
39 NtQuerySystemTime (&SystemTime);
40 RtlTimeToTimeFields (&SystemTime, &TimeFields);
41
42 Buffer = (PUCHAR)&Serial;
43 Buffer[0] = (UCHAR)(TimeFields.Year & 0xFF) + (UCHAR)(TimeFields.Hour & 0xFF);
44 Buffer[1] = (UCHAR)(TimeFields.Year >> 8) + (UCHAR)(TimeFields.Minute & 0xFF);
45 Buffer[2] = (UCHAR)(TimeFields.Month & 0xFF) + (UCHAR)(TimeFields.Second & 0xFF);
46 Buffer[3] = (UCHAR)(TimeFields.Day & 0xFF) + (UCHAR)(TimeFields.Milliseconds & 0xFF);
47
48 return Serial;
49 }
50
51
52 static NTSTATUS
53 Fat32WriteBootSector(IN HANDLE FileHandle,
54 IN PFAT32_BOOT_SECTOR BootSector,
55 IN OUT PFORMAT_CONTEXT Context)
56 {
57 IO_STATUS_BLOCK IoStatusBlock;
58 NTSTATUS Status;
59 PFAT32_BOOT_SECTOR NewBootSector;
60 LARGE_INTEGER FileOffset;
61
62 /* Allocate buffer for new bootsector */
63 NewBootSector = (PFAT32_BOOT_SECTOR)RtlAllocateHeap(RtlGetProcessHeap(),
64 0,
65 BootSector->BytesPerSector);
66 if (NewBootSector == NULL)
67 return STATUS_INSUFFICIENT_RESOURCES;
68
69 /* Zero the new bootsector */
70 memset(NewBootSector, 0, BootSector->BytesPerSector);
71
72 /* Copy FAT32 BPB to new bootsector */
73 memcpy(&NewBootSector->OEMName[0],
74 &BootSector->OEMName[0],
75 FIELD_OFFSET(FAT32_BOOT_SECTOR, Res2) - FIELD_OFFSET(FAT32_BOOT_SECTOR, OEMName)); /* FAT32 BPB length (up to (not including) Res2) */
76
77 /* Write the boot sector signature */
78 NewBootSector->Signature1 = 0xAA550000;
79
80 /* Write sector 0 */
81 FileOffset.QuadPart = 0ULL;
82 Status = NtWriteFile(FileHandle,
83 NULL,
84 NULL,
85 NULL,
86 &IoStatusBlock,
87 NewBootSector,
88 BootSector->BytesPerSector,
89 &FileOffset,
90 NULL);
91 if (!NT_SUCCESS(Status))
92 {
93 DPRINT("NtWriteFile() failed (Status %lx)\n", Status);
94 RtlFreeHeap(RtlGetProcessHeap(), 0, NewBootSector);
95 return Status;
96 }
97
98 UpdateProgress(Context, 1);
99
100 /* Write backup boot sector */
101 if (BootSector->BootBackup != 0x0000)
102 {
103 FileOffset.QuadPart = (ULONGLONG)((ULONG)BootSector->BootBackup * BootSector->BytesPerSector);
104 Status = NtWriteFile(FileHandle,
105 NULL,
106 NULL,
107 NULL,
108 &IoStatusBlock,
109 NewBootSector,
110 BootSector->BytesPerSector,
111 &FileOffset,
112 NULL);
113 if (!NT_SUCCESS(Status))
114 {
115 DPRINT("NtWriteFile() failed (Status %lx)\n", Status);
116 RtlFreeHeap(RtlGetProcessHeap(), 0, NewBootSector);
117 return Status;
118 }
119
120 UpdateProgress(Context, 1);
121 }
122
123 /* Free the new boot sector */
124 RtlFreeHeap(RtlGetProcessHeap(), 0, NewBootSector);
125
126 return Status;
127 }
128
129
130 static NTSTATUS
131 Fat32WriteFsInfo(IN HANDLE FileHandle,
132 IN PFAT32_BOOT_SECTOR BootSector,
133 IN OUT PFORMAT_CONTEXT Context)
134 {
135 IO_STATUS_BLOCK IoStatusBlock;
136 NTSTATUS Status;
137 PFAT32_FSINFO FsInfo;
138 LARGE_INTEGER FileOffset;
139
140 /* Allocate buffer for new sector */
141 FsInfo = (PFAT32_FSINFO)RtlAllocateHeap(RtlGetProcessHeap(),
142 0,
143 BootSector->BytesPerSector);
144 if (FsInfo == NULL)
145 return STATUS_INSUFFICIENT_RESOURCES;
146
147 /* Zero the new sector */
148 memset(FsInfo, 0, BootSector->BytesPerSector);
149
150 FsInfo->LeadSig = 0x41615252;
151 FsInfo->StrucSig = 0x61417272;
152 FsInfo->FreeCount = 0xffffffff;
153 FsInfo->NextFree = 0xffffffff;
154 FsInfo->TrailSig = 0xaa550000;
155
156 /* Write sector */
157 FileOffset.QuadPart = BootSector->FSInfoSector * BootSector->BytesPerSector;
158 Status = NtWriteFile(FileHandle,
159 NULL,
160 NULL,
161 NULL,
162 &IoStatusBlock,
163 FsInfo,
164 BootSector->BytesPerSector,
165 &FileOffset,
166 NULL);
167 if (!NT_SUCCESS(Status))
168 {
169 DPRINT("NtWriteFile() failed (Status %lx)\n", Status);
170 RtlFreeHeap(RtlGetProcessHeap(), 0, FsInfo);
171 return Status;
172 }
173
174 UpdateProgress(Context, 1);
175
176 /* Free the new sector buffer */
177 RtlFreeHeap(RtlGetProcessHeap(), 0, FsInfo);
178
179 return Status;
180 }
181
182
183 static NTSTATUS
184 Fat32WriteFAT(IN HANDLE FileHandle,
185 IN ULONG SectorOffset,
186 IN PFAT32_BOOT_SECTOR BootSector,
187 IN OUT PFORMAT_CONTEXT Context)
188 {
189 IO_STATUS_BLOCK IoStatusBlock;
190 NTSTATUS Status;
191 PUCHAR Buffer;
192 LARGE_INTEGER FileOffset;
193 ULONG i;
194 ULONG Sectors;
195
196 /* Allocate buffer */
197 Buffer = (PUCHAR)RtlAllocateHeap(RtlGetProcessHeap(),
198 0,
199 64 * 1024);
200 if (Buffer == NULL)
201 return STATUS_INSUFFICIENT_RESOURCES;
202
203 /* Zero the buffer */
204 memset(Buffer, 0, 64 * 1024);
205
206 /* FAT cluster 0 */
207 Buffer[0] = 0xf8; /* Media type */
208 Buffer[1] = 0xff;
209 Buffer[2] = 0xff;
210 Buffer[3] = 0x0f;
211
212 /* FAT cluster 1 */
213 Buffer[4] = 0xff; /* Clean shutdown, no disk read/write errors, end-of-cluster (EOC) mark */
214 Buffer[5] = 0xff;
215 Buffer[6] = 0xff;
216 Buffer[7] = 0x0f;
217
218 /* FAT cluster 2 */
219 Buffer[8] = 0xff; /* End of root directory */
220 Buffer[9] = 0xff;
221 Buffer[10] = 0xff;
222 Buffer[11] = 0x0f;
223
224 /* Write first sector of the FAT */
225 FileOffset.QuadPart = (SectorOffset + BootSector->ReservedSectors) * BootSector->BytesPerSector;
226 Status = NtWriteFile(FileHandle,
227 NULL,
228 NULL,
229 NULL,
230 &IoStatusBlock,
231 Buffer,
232 BootSector->BytesPerSector,
233 &FileOffset,
234 NULL);
235 if (!NT_SUCCESS(Status))
236 {
237 DPRINT("NtWriteFile() failed (Status %lx)\n", Status);
238 RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer);
239 return Status;
240 }
241
242 UpdateProgress(Context, 1);
243
244 /* Zero the begin of the buffer */
245 memset(Buffer, 0, 12);
246
247 /* Zero the rest of the FAT */
248 Sectors = 64 * 1024 / BootSector->BytesPerSector;
249 for (i = 1; i < BootSector->FATSectors32; i += Sectors)
250 {
251 /* Zero some sectors of the FAT */
252 FileOffset.QuadPart = (SectorOffset + BootSector->ReservedSectors + i) * BootSector->BytesPerSector;
253
254 if ((BootSector->FATSectors32 - i) <= Sectors)
255 {
256 Sectors = BootSector->FATSectors32 - i;
257 }
258
259 Status = NtWriteFile(FileHandle,
260 NULL,
261 NULL,
262 NULL,
263 &IoStatusBlock,
264 Buffer,
265 Sectors * BootSector->BytesPerSector,
266 &FileOffset,
267 NULL);
268 if (!NT_SUCCESS(Status))
269 {
270 DPRINT("NtWriteFile() failed (Status %lx)\n", Status);
271 RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer);
272 return Status;
273 }
274
275 UpdateProgress(Context, Sectors);
276 }
277
278 /* Free the buffer */
279 RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer);
280
281 return Status;
282 }
283
284
285 static NTSTATUS
286 Fat32WriteRootDirectory(IN HANDLE FileHandle,
287 IN PFAT32_BOOT_SECTOR BootSector,
288 IN OUT PFORMAT_CONTEXT Context)
289 {
290 IO_STATUS_BLOCK IoStatusBlock;
291 NTSTATUS Status;
292 PUCHAR Buffer;
293 LARGE_INTEGER FileOffset;
294 ULONGLONG FirstDataSector;
295 ULONGLONG FirstRootDirSector;
296
297 /* Allocate buffer for the cluster */
298 Buffer = (PUCHAR)RtlAllocateHeap(RtlGetProcessHeap(),
299 0,
300 BootSector->SectorsPerCluster * BootSector->BytesPerSector);
301 if (Buffer == NULL)
302 return STATUS_INSUFFICIENT_RESOURCES;
303
304 /* Zero the buffer */
305 memset(Buffer, 0, BootSector->SectorsPerCluster * BootSector->BytesPerSector);
306
307 DPRINT("BootSector->ReservedSectors = %lu\n", BootSector->ReservedSectors);
308 DPRINT("BootSector->FATSectors32 = %lu\n", BootSector->FATSectors32);
309 DPRINT("BootSector->RootCluster = %lu\n", BootSector->RootCluster);
310 DPRINT("BootSector->SectorsPerCluster = %lu\n", BootSector->SectorsPerCluster);
311
312 /* Write cluster */
313 FirstDataSector = BootSector->ReservedSectors +
314 (BootSector->FATCount * BootSector->FATSectors32) + 0 /* RootDirSectors */;
315
316 DPRINT("FirstDataSector = %lu\n", FirstDataSector);
317
318 FirstRootDirSector = ((BootSector->RootCluster - 2) * BootSector->SectorsPerCluster) + FirstDataSector;
319 FileOffset.QuadPart = FirstRootDirSector * BootSector->BytesPerSector;
320
321 DPRINT("FirstRootDirSector = %lu\n", FirstRootDirSector);
322 DPRINT("FileOffset = %lu\n", FileOffset.QuadPart);
323
324 Status = NtWriteFile(FileHandle,
325 NULL,
326 NULL,
327 NULL,
328 &IoStatusBlock,
329 Buffer,
330 BootSector->SectorsPerCluster * BootSector->BytesPerSector,
331 &FileOffset,
332 NULL);
333 if (!NT_SUCCESS(Status))
334 {
335 DPRINT("NtWriteFile() failed (Status %lx)\n", Status);
336 RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer);
337 return Status;
338 }
339
340 UpdateProgress(Context, (ULONG)BootSector->SectorsPerCluster);
341
342 /* Free the buffer */
343 RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer);
344
345 return Status;
346 }
347
348
349 NTSTATUS
350 Fat32Format(IN HANDLE FileHandle,
351 IN PPARTITION_INFORMATION PartitionInfo,
352 IN PDISK_GEOMETRY DiskGeometry,
353 IN PUNICODE_STRING Label,
354 IN BOOLEAN QuickFormat,
355 IN ULONG ClusterSize,
356 IN OUT PFORMAT_CONTEXT Context)
357 {
358 FAT32_BOOT_SECTOR BootSector;
359 OEM_STRING VolumeLabel;
360 ULONG TmpVal1;
361 ULONG TmpVal2;
362 NTSTATUS Status;
363
364 /* Calculate cluster size */
365 if (ClusterSize == 0)
366 {
367 if (PartitionInfo->PartitionLength.QuadPart < 8LL * 1024LL * 1024LL * 1024LL)
368 {
369 /* Partition < 8GB ==> 4KB Cluster */
370 ClusterSize = 4096;
371 }
372 else if (PartitionInfo->PartitionLength.QuadPart < 16LL * 1024LL * 1024LL * 1024LL)
373 {
374 /* Partition 8GB - 16GB ==> 8KB Cluster */
375 ClusterSize = 8192;
376 }
377 else if (PartitionInfo->PartitionLength.QuadPart < 32LL * 1024LL * 1024LL * 1024LL)
378 {
379 /* Partition 16GB - 32GB ==> 16KB Cluster */
380 ClusterSize = 16384;
381 }
382 else
383 {
384 /* Partition >= 32GB ==> 32KB Cluster */
385 ClusterSize = 32768;
386 }
387 }
388
389 memset(&BootSector, 0, sizeof(FAT32_BOOT_SECTOR));
390 memcpy(&BootSector.OEMName[0], "MSWIN4.1", 8);
391 BootSector.BytesPerSector = DiskGeometry->BytesPerSector;
392 BootSector.SectorsPerCluster = ClusterSize / BootSector.BytesPerSector;
393 BootSector.ReservedSectors = 32;
394 BootSector.FATCount = 2;
395 BootSector.RootEntries = 0;
396 BootSector.Sectors = 0;
397 BootSector.Media = 0xf8;
398 BootSector.FATSectors = 0;
399 BootSector.SectorsPerTrack = DiskGeometry->SectorsPerTrack;
400 BootSector.Heads = DiskGeometry->TracksPerCylinder;
401 BootSector.HiddenSectors = PartitionInfo->HiddenSectors;
402 BootSector.SectorsHuge = PartitionInfo->PartitionLength.QuadPart >>
403 GetShiftCount(BootSector.BytesPerSector); /* Use shifting to avoid 64-bit division */
404 BootSector.FATSectors32 = 0; /* Set later */
405 BootSector.ExtFlag = 0; /* Mirror all FATs */
406 BootSector.FSVersion = 0x0000; /* 0:0 */
407 BootSector.RootCluster = 2;
408 BootSector.FSInfoSector = 1;
409 BootSector.BootBackup = 6;
410 BootSector.Drive = DiskGeometry->MediaType == FixedMedia ? 0x80 : 0x00;
411 BootSector.ExtBootSignature = 0x29;
412 BootSector.VolumeID = CalcVolumeSerialNumber ();
413 if ((Label == NULL) || (Label->Buffer == NULL))
414 {
415 memcpy(&BootSector.VolumeLabel[0], "NO NAME ", 11);
416 }
417 else
418 {
419 RtlUnicodeStringToOemString(&VolumeLabel, Label, TRUE);
420 memset(&BootSector.VolumeLabel[0], ' ', 11);
421 memcpy(&BootSector.VolumeLabel[0], VolumeLabel.Buffer,
422 VolumeLabel.Length < 11 ? VolumeLabel.Length : 11);
423 RtlFreeOemString(&VolumeLabel);
424 }
425
426 memcpy(&BootSector.SysType[0], "FAT32 ", 8);
427
428 /* Calculate number of FAT sectors */
429 /* (BytesPerSector / 4) FAT entries (32bit) fit into one sector */
430 TmpVal1 = BootSector.SectorsHuge - BootSector.ReservedSectors;
431 TmpVal2 = ((BootSector.BytesPerSector / 4) * BootSector.SectorsPerCluster) + BootSector.FATCount;
432 BootSector.FATSectors32 = (TmpVal1 + (TmpVal2 - 1)) / TmpVal2;
433 DPRINT("FATSectors32 = %lu\n", BootSector.FATSectors32);
434
435 /* Init context data */
436 Context->TotalSectorCount =
437 2 + (BootSector.FATSectors32 * BootSector.FATCount) + BootSector.SectorsPerCluster;
438
439 Status = Fat32WriteBootSector(FileHandle,
440 &BootSector,
441 Context);
442 if (!NT_SUCCESS(Status))
443 {
444 DPRINT("Fat32WriteBootSector() failed with status 0x%.08x\n", Status);
445 return Status;
446 }
447
448 Status = Fat32WriteFsInfo(FileHandle,
449 &BootSector,
450 Context);
451 if (!NT_SUCCESS(Status))
452 {
453 DPRINT("Fat32WriteFsInfo() failed with status 0x%.08x\n", Status);
454 return Status;
455 }
456
457 /* Write first FAT copy */
458 Status = Fat32WriteFAT(FileHandle,
459 0,
460 &BootSector,
461 Context);
462 if (!NT_SUCCESS(Status))
463 {
464 DPRINT("Fat32WriteFAT() failed with status 0x%.08x\n", Status);
465 return Status;
466 }
467
468 /* Write second FAT copy */
469 Status = Fat32WriteFAT(FileHandle,
470 BootSector.FATSectors32,
471 &BootSector,
472 Context);
473 if (!NT_SUCCESS(Status))
474 {
475 DPRINT("Fat32WriteFAT() failed with status 0x%.08x.\n", Status);
476 return Status;
477 }
478
479 Status = Fat32WriteRootDirectory(FileHandle,
480 &BootSector,
481 Context);
482 if (!NT_SUCCESS(Status))
483 {
484 DPRINT("Fat32WriteRootDirectory() failed with status 0x%.08x\n", Status);
485 }
486
487 if (!QuickFormat)
488 {
489 /* FIXME: Fill remaining sectors */
490 }
491
492 return Status;
493 }
494
495 /* EOF */