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