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, 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 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 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 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 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