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