Sync with trunk r63743.
[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
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 static
350 NTSTATUS
351 Fat32WipeSectors(
352 IN HANDLE FileHandle,
353 IN PFAT32_BOOT_SECTOR BootSector,
354 IN OUT PFORMAT_CONTEXT Context)
355 {
356 IO_STATUS_BLOCK IoStatusBlock;
357 PUCHAR Buffer;
358 LARGE_INTEGER FileOffset;
359 ULONGLONG Sector;
360 ULONG Length;
361 NTSTATUS Status;
362
363 /* Allocate buffer for the cluster */
364 Buffer = (PUCHAR)RtlAllocateHeap(RtlGetProcessHeap(),
365 HEAP_ZERO_MEMORY,
366 BootSector->SectorsPerCluster * BootSector->BytesPerSector);
367 if (Buffer == NULL)
368 return STATUS_INSUFFICIENT_RESOURCES;
369
370 Sector = 0;
371 Length = BootSector->SectorsPerCluster * BootSector->BytesPerSector;
372
373 while (Sector + BootSector->SectorsPerCluster < BootSector->SectorsHuge)
374 {
375 FileOffset.QuadPart = Sector * BootSector->BytesPerSector;
376
377 Status = NtWriteFile(FileHandle,
378 NULL,
379 NULL,
380 NULL,
381 &IoStatusBlock,
382 Buffer,
383 Length,
384 &FileOffset,
385 NULL);
386 if (!NT_SUCCESS(Status))
387 {
388 DPRINT("NtWriteFile() failed (Status %lx)\n", Status);
389 goto done;
390 }
391
392 UpdateProgress(Context, (ULONG)BootSector->SectorsPerCluster);
393
394 Sector += BootSector->SectorsPerCluster;
395 }
396
397 if (Sector + BootSector->SectorsPerCluster > BootSector->SectorsHuge)
398 {
399 DPRINT("Remaining sectors %lu\n", BootSector->SectorsHuge - Sector);
400
401 FileOffset.QuadPart = Sector * BootSector->BytesPerSector;
402 Length = (BootSector->SectorsHuge - Sector) * BootSector->BytesPerSector;
403
404 Status = NtWriteFile(FileHandle,
405 NULL,
406 NULL,
407 NULL,
408 &IoStatusBlock,
409 Buffer,
410 Length,
411 &FileOffset,
412 NULL);
413 if (!NT_SUCCESS(Status))
414 {
415 DPRINT("NtWriteFile() failed (Status %lx)\n", Status);
416 goto done;
417 }
418
419 UpdateProgress(Context, BootSector->SectorsHuge - Sector);
420 }
421
422 done:
423 /* Free the buffer */
424 RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer);
425
426 return Status;
427 }
428
429
430 NTSTATUS
431 Fat32Format(IN HANDLE FileHandle,
432 IN PPARTITION_INFORMATION PartitionInfo,
433 IN PDISK_GEOMETRY DiskGeometry,
434 IN PUNICODE_STRING Label,
435 IN BOOLEAN QuickFormat,
436 IN ULONG ClusterSize,
437 IN OUT PFORMAT_CONTEXT Context)
438 {
439 FAT32_BOOT_SECTOR BootSector;
440 OEM_STRING VolumeLabel;
441 ULONG TmpVal1;
442 ULONG TmpVal2;
443 NTSTATUS Status;
444
445 /* Calculate cluster size */
446 if (ClusterSize == 0)
447 {
448 if (PartitionInfo->PartitionLength.QuadPart < 8LL * 1024LL * 1024LL * 1024LL)
449 {
450 /* Partition < 8GB ==> 4KB Cluster */
451 ClusterSize = 4096;
452 }
453 else if (PartitionInfo->PartitionLength.QuadPart < 16LL * 1024LL * 1024LL * 1024LL)
454 {
455 /* Partition 8GB - 16GB ==> 8KB Cluster */
456 ClusterSize = 8192;
457 }
458 else if (PartitionInfo->PartitionLength.QuadPart < 32LL * 1024LL * 1024LL * 1024LL)
459 {
460 /* Partition 16GB - 32GB ==> 16KB Cluster */
461 ClusterSize = 16384;
462 }
463 else
464 {
465 /* Partition >= 32GB ==> 32KB Cluster */
466 ClusterSize = 32768;
467 }
468 }
469
470 memset(&BootSector, 0, sizeof(FAT32_BOOT_SECTOR));
471 memcpy(&BootSector.OEMName[0], "MSWIN4.1", 8);
472 BootSector.BytesPerSector = DiskGeometry->BytesPerSector;
473 BootSector.SectorsPerCluster = ClusterSize / BootSector.BytesPerSector;
474 BootSector.ReservedSectors = 32;
475 BootSector.FATCount = 2;
476 BootSector.RootEntries = 0;
477 BootSector.Sectors = 0;
478 BootSector.Media = 0xf8;
479 BootSector.FATSectors = 0;
480 BootSector.SectorsPerTrack = DiskGeometry->SectorsPerTrack;
481 BootSector.Heads = DiskGeometry->TracksPerCylinder;
482 BootSector.HiddenSectors = PartitionInfo->HiddenSectors;
483 BootSector.SectorsHuge = PartitionInfo->PartitionLength.QuadPart >>
484 GetShiftCount(BootSector.BytesPerSector); /* Use shifting to avoid 64-bit division */
485 BootSector.FATSectors32 = 0; /* Set later */
486 BootSector.ExtFlag = 0; /* Mirror all FATs */
487 BootSector.FSVersion = 0x0000; /* 0:0 */
488 BootSector.RootCluster = 2;
489 BootSector.FSInfoSector = 1;
490 BootSector.BootBackup = 6;
491 BootSector.Drive = (DiskGeometry->MediaType == FixedMedia) ? 0x80 : 0x00;
492 BootSector.ExtBootSignature = 0x29;
493 BootSector.VolumeID = CalcVolumeSerialNumber ();
494 if ((Label == NULL) || (Label->Buffer == NULL))
495 {
496 memcpy(&BootSector.VolumeLabel[0], "NO NAME ", 11);
497 }
498 else
499 {
500 RtlUnicodeStringToOemString(&VolumeLabel, Label, TRUE);
501 memset(&BootSector.VolumeLabel[0], ' ', 11);
502 memcpy(&BootSector.VolumeLabel[0], VolumeLabel.Buffer,
503 VolumeLabel.Length < 11 ? VolumeLabel.Length : 11);
504 RtlFreeOemString(&VolumeLabel);
505 }
506
507 memcpy(&BootSector.SysType[0], "FAT32 ", 8);
508
509 /* Calculate number of FAT sectors */
510 /* (BytesPerSector / 4) FAT entries (32bit) fit into one sector */
511 TmpVal1 = BootSector.SectorsHuge - BootSector.ReservedSectors;
512 TmpVal2 = ((BootSector.BytesPerSector / 4) * BootSector.SectorsPerCluster) + BootSector.FATCount;
513 BootSector.FATSectors32 = (TmpVal1 + (TmpVal2 - 1)) / TmpVal2;
514 DPRINT("FATSectors32 = %lu\n", BootSector.FATSectors32);
515
516 /* Init context data */
517 Context->TotalSectorCount =
518 2 + (BootSector.FATSectors32 * BootSector.FATCount) + BootSector.SectorsPerCluster;
519
520 if (!QuickFormat)
521 {
522 Context->TotalSectorCount += BootSector.SectorsHuge;
523
524 Status = Fat32WipeSectors(FileHandle,
525 &BootSector,
526 Context);
527 if (!NT_SUCCESS(Status))
528 {
529 DPRINT("Fat32WipeSectors() failed with status 0x%.08x\n", Status);
530 return Status;
531 }
532 }
533
534 Status = Fat32WriteBootSector(FileHandle,
535 &BootSector,
536 Context);
537 if (!NT_SUCCESS(Status))
538 {
539 DPRINT("Fat32WriteBootSector() failed with status 0x%.08x\n", Status);
540 return Status;
541 }
542
543 Status = Fat32WriteFsInfo(FileHandle,
544 &BootSector,
545 Context);
546 if (!NT_SUCCESS(Status))
547 {
548 DPRINT("Fat32WriteFsInfo() failed with status 0x%.08x\n", Status);
549 return Status;
550 }
551
552 /* Write first FAT copy */
553 Status = Fat32WriteFAT(FileHandle,
554 0,
555 &BootSector,
556 Context);
557 if (!NT_SUCCESS(Status))
558 {
559 DPRINT("Fat32WriteFAT() failed with status 0x%.08x\n", Status);
560 return Status;
561 }
562
563 /* Write second FAT copy */
564 Status = Fat32WriteFAT(FileHandle,
565 BootSector.FATSectors32,
566 &BootSector,
567 Context);
568 if (!NT_SUCCESS(Status))
569 {
570 DPRINT("Fat32WriteFAT() failed with status 0x%.08x.\n", Status);
571 return Status;
572 }
573
574 Status = Fat32WriteRootDirectory(FileHandle,
575 &BootSector,
576 Context);
577 if (!NT_SUCCESS(Status))
578 {
579 DPRINT("Fat32WriteRootDirectory() failed with status 0x%.08x\n", Status);
580 }
581
582 return Status;
583 }
584
585 /* EOF */