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->Jump[0], 42 &BootSector->Jump[0], 43 FIELD_OFFSET(FAT32_BOOT_SECTOR, Res2) - FIELD_OFFSET(FAT32_BOOT_SECTOR, Jump)); 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 ULONG UsableFatEntries; 401 ULONG FirstDataSector; 402 ULONG DataClusters; 403 404 /* Calculate cluster size */ 405 if (ClusterSize == 0) 406 { 407 if (PartitionInfo->PartitionLength.QuadPart < 8LL * 1024LL * 1024LL * 1024LL) 408 { 409 /* Partition < 8GB ==> 4KB Cluster */ 410 ClusterSize = 4096; 411 } 412 else if (PartitionInfo->PartitionLength.QuadPart < 16LL * 1024LL * 1024LL * 1024LL) 413 { 414 /* Partition 8GB - 16GB ==> 8KB Cluster */ 415 ClusterSize = 8192; 416 } 417 else if (PartitionInfo->PartitionLength.QuadPart < 32LL * 1024LL * 1024LL * 1024LL) 418 { 419 /* Partition 16GB - 32GB ==> 16KB Cluster */ 420 ClusterSize = 16384; 421 } 422 else 423 { 424 /* Partition >= 32GB ==> 32KB Cluster */ 425 ClusterSize = 32768; 426 } 427 } 428 429 RtlZeroMemory(&BootSector, sizeof(FAT32_BOOT_SECTOR)); 430 memcpy(&BootSector.OEMName[0], "MSWIN4.1", 8); 431 /* FIXME: Add dummy bootloader for real */ 432 BootSector.Jump[0] = 0xeb; 433 BootSector.Jump[1] = 0x58; 434 BootSector.Jump[2] = 0x90; 435 BootSector.BytesPerSector = DiskGeometry->BytesPerSector; 436 BootSector.SectorsPerCluster = ClusterSize / BootSector.BytesPerSector; 437 BootSector.ReservedSectors = 32; 438 BootSector.FATCount = 2; 439 BootSector.RootEntries = 0; 440 BootSector.Sectors = 0; 441 BootSector.Media = 0xf8; 442 BootSector.FATSectors = 0; 443 BootSector.SectorsPerTrack = DiskGeometry->SectorsPerTrack; 444 BootSector.Heads = DiskGeometry->TracksPerCylinder; 445 BootSector.HiddenSectors = PartitionInfo->HiddenSectors; 446 BootSector.SectorsHuge = PartitionInfo->PartitionLength.QuadPart >> 447 GetShiftCount(BootSector.BytesPerSector); /* Use shifting to avoid 64-bit division */ 448 BootSector.FATSectors32 = 0; /* Set later */ 449 BootSector.ExtFlag = 0; /* Mirror all FATs */ 450 BootSector.FSVersion = 0x0000; /* 0:0 */ 451 BootSector.RootCluster = 2; 452 BootSector.FSInfoSector = 1; 453 BootSector.BootBackup = 6; 454 BootSector.Drive = (DiskGeometry->MediaType == FixedMedia) ? 0x80 : 0x00; 455 BootSector.ExtBootSignature = 0x29; 456 BootSector.VolumeID = CalcVolumeSerialNumber(); 457 if ((Label == NULL) || (Label->Buffer == NULL)) 458 { 459 memcpy(&BootSector.VolumeLabel[0], "NO NAME ", 11); 460 } 461 else 462 { 463 RtlUnicodeStringToOemString(&VolumeLabel, Label, TRUE); 464 RtlFillMemory(&BootSector.VolumeLabel[0], 11, ' '); 465 memcpy(&BootSector.VolumeLabel[0], VolumeLabel.Buffer, 466 VolumeLabel.Length < 11 ? VolumeLabel.Length : 11); 467 RtlFreeOemString(&VolumeLabel); 468 } 469 470 memcpy(&BootSector.SysType[0], "FAT32 ", 8); 471 472 /* Calculate number of FAT sectors */ 473 /* (BytesPerSector / 4) FAT entries (32bit) fit into one sector */ 474 TmpVal1 = BootSector.SectorsHuge - BootSector.ReservedSectors; 475 TmpVal2 = ((BootSector.BytesPerSector / 4) * BootSector.SectorsPerCluster) + BootSector.FATCount; 476 BootSector.FATSectors32 = (TmpVal1 + (TmpVal2 - 1)) / TmpVal2; 477 DPRINT("FATSectors32 = %lu\n", BootSector.FATSectors32); 478 479 /* edge case: first 2 fat entries are not usable, so the calculation might need a correction */ 480 UsableFatEntries = BootSector.FATSectors32 * (BootSector.BytesPerSector / 4) - 2; 481 FirstDataSector = BootSector.ReservedSectors + BootSector.FATCount * BootSector.FATSectors32; 482 DataClusters = (BootSector.SectorsHuge - FirstDataSector) / BootSector.SectorsPerCluster; 483 if (DataClusters > UsableFatEntries) 484 { 485 /* Need more fat entries */ 486 BootSector.FATSectors32 += (DataClusters - UsableFatEntries); 487 488 DPRINT("UsableFatEntries = %lu\n", UsableFatEntries); 489 DPRINT("DataClusters = %lu\n", DataClusters); 490 DPRINT("BootSector.FATSectors32 incremented to %lu\n", BootSector.FATSectors32); 491 } 492 493 /* Init context data */ 494 Context->TotalSectorCount = 495 2 + (BootSector.FATSectors32 * BootSector.FATCount) + BootSector.SectorsPerCluster; 496 497 if (!QuickFormat) 498 { 499 Context->TotalSectorCount += BootSector.SectorsHuge; 500 501 Status = FatWipeSectors(FileHandle, 502 BootSector.SectorsHuge, 503 (ULONG)BootSector.SectorsPerCluster, 504 (ULONG)BootSector.BytesPerSector, 505 Context); 506 if (!NT_SUCCESS(Status)) 507 { 508 DPRINT("FatWipeSectors() failed with status 0x%.08x\n", Status); 509 return Status; 510 } 511 } 512 513 Status = Fat32WriteBootSector(FileHandle, 514 &BootSector, 515 Context); 516 if (!NT_SUCCESS(Status)) 517 { 518 DPRINT("Fat32WriteBootSector() failed with status 0x%.08x\n", Status); 519 return Status; 520 } 521 522 Status = Fat32WriteFsInfo(FileHandle, 523 &BootSector, 524 Context); 525 if (!NT_SUCCESS(Status)) 526 { 527 DPRINT("Fat32WriteFsInfo() failed with status 0x%.08x\n", Status); 528 return Status; 529 } 530 531 /* Write first FAT copy */ 532 Status = Fat32WriteFAT(FileHandle, 533 0, 534 &BootSector, 535 Context); 536 if (!NT_SUCCESS(Status)) 537 { 538 DPRINT("Fat32WriteFAT() failed with status 0x%.08x\n", Status); 539 return Status; 540 } 541 542 /* Write second FAT copy */ 543 Status = Fat32WriteFAT(FileHandle, 544 BootSector.FATSectors32, 545 &BootSector, 546 Context); 547 if (!NT_SUCCESS(Status)) 548 { 549 DPRINT("Fat32WriteFAT() failed with status 0x%.08x.\n", Status); 550 return Status; 551 } 552 553 Status = Fat32WriteRootDirectory(FileHandle, 554 &BootSector, 555 Context); 556 if (!NT_SUCCESS(Status)) 557 { 558 DPRINT("Fat32WriteRootDirectory() failed with status 0x%.08x\n", Status); 559 } 560 561 return Status; 562 } 563 564 /* EOF */ 565