1 /* 2 * PROJECT: VFAT Filesystem 3 * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) 4 * PURPOSE: File Allocation Table routines 5 * COPYRIGHT: Copyright 1998 Jason Filby <jasonfilby@yahoo.com> 6 * Copyright 2015-2018 Pierre Schweitzer <pierre@reactos.org> 7 */ 8 9 /* INCLUDES *****************************************************************/ 10 11 #include "vfat.h" 12 13 #define NDEBUG 14 #include <debug.h> 15 16 /* GLOBALS ******************************************************************/ 17 18 #define CACHEPAGESIZE(pDeviceExt) ((pDeviceExt)->FatInfo.BytesPerCluster > PAGE_SIZE ? \ 19 (pDeviceExt)->FatInfo.BytesPerCluster : PAGE_SIZE) 20 21 /* FUNCTIONS ****************************************************************/ 22 23 /* 24 * FUNCTION: Retrieve the next FAT32 cluster from the FAT table via a physical 25 * disk read 26 */ 27 NTSTATUS 28 FAT32GetNextCluster( 29 PDEVICE_EXTENSION DeviceExt, 30 ULONG CurrentCluster, 31 PULONG NextCluster) 32 { 33 NTSTATUS Status = STATUS_SUCCESS; 34 PVOID BaseAddress; 35 ULONG FATOffset; 36 ULONG ChunkSize; 37 PVOID Context = NULL; 38 LARGE_INTEGER Offset; 39 40 ChunkSize = CACHEPAGESIZE(DeviceExt); 41 FATOffset = CurrentCluster * sizeof(ULONG); 42 Offset.QuadPart = ROUND_DOWN(FATOffset, ChunkSize); 43 _SEH2_TRY 44 { 45 if (!CcMapData(DeviceExt->FATFileObject, &Offset, ChunkSize, MAP_WAIT, &Context, &BaseAddress)) 46 { 47 NT_ASSERT(FALSE); 48 return STATUS_UNSUCCESSFUL; 49 } 50 } 51 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 52 { 53 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 54 } 55 _SEH2_END; 56 57 CurrentCluster = (*(PULONG)((char*)BaseAddress + (FATOffset % ChunkSize))) & 0x0fffffff; 58 if (CurrentCluster >= 0xffffff8 && CurrentCluster <= 0xfffffff) 59 CurrentCluster = 0xffffffff; 60 61 if (CurrentCluster == 0) 62 { 63 DPRINT1("WARNING: File system corruption detected. You may need to run a disk repair utility.\n"); 64 Status = STATUS_FILE_CORRUPT_ERROR; 65 if (VfatGlobalData->Flags & VFAT_BREAK_ON_CORRUPTION) 66 ASSERT(CurrentCluster != 0); 67 } 68 CcUnpinData(Context); 69 *NextCluster = CurrentCluster; 70 return Status; 71 } 72 73 /* 74 * FUNCTION: Retrieve the next FAT16 cluster from the FAT table 75 */ 76 NTSTATUS 77 FAT16GetNextCluster( 78 PDEVICE_EXTENSION DeviceExt, 79 ULONG CurrentCluster, 80 PULONG NextCluster) 81 { 82 NTSTATUS Status = STATUS_SUCCESS; 83 PVOID BaseAddress; 84 ULONG FATOffset; 85 ULONG ChunkSize; 86 PVOID Context; 87 LARGE_INTEGER Offset; 88 89 ChunkSize = CACHEPAGESIZE(DeviceExt); 90 FATOffset = CurrentCluster * 2; 91 Offset.QuadPart = ROUND_DOWN(FATOffset, ChunkSize); 92 _SEH2_TRY 93 { 94 CcMapData(DeviceExt->FATFileObject, &Offset, ChunkSize, MAP_WAIT, &Context, &BaseAddress); 95 } 96 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 97 { 98 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 99 } 100 _SEH2_END; 101 102 CurrentCluster = *((PUSHORT)((char*)BaseAddress + (FATOffset % ChunkSize))); 103 if (CurrentCluster >= 0xfff8 && CurrentCluster <= 0xffff) 104 CurrentCluster = 0xffffffff; 105 106 if (CurrentCluster == 0) 107 { 108 DPRINT1("WARNING: File system corruption detected. You may need to run a disk repair utility.\n"); 109 Status = STATUS_FILE_CORRUPT_ERROR; 110 if (VfatGlobalData->Flags & VFAT_BREAK_ON_CORRUPTION) 111 ASSERT(CurrentCluster != 0); 112 } 113 114 CcUnpinData(Context); 115 *NextCluster = CurrentCluster; 116 return Status; 117 } 118 119 /* 120 * FUNCTION: Retrieve the next FAT12 cluster from the FAT table 121 */ 122 NTSTATUS 123 FAT12GetNextCluster( 124 PDEVICE_EXTENSION DeviceExt, 125 ULONG CurrentCluster, 126 PULONG NextCluster) 127 { 128 PUSHORT CBlock; 129 ULONG Entry; 130 PVOID BaseAddress; 131 PVOID Context; 132 LARGE_INTEGER Offset; 133 134 *NextCluster = 0; 135 136 Offset.QuadPart = 0; 137 _SEH2_TRY 138 { 139 CcMapData(DeviceExt->FATFileObject, &Offset, DeviceExt->FatInfo.FATSectors * DeviceExt->FatInfo.BytesPerSector, MAP_WAIT, &Context, &BaseAddress); 140 } 141 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 142 { 143 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 144 } 145 _SEH2_END; 146 147 CBlock = (PUSHORT)((char*)BaseAddress + (CurrentCluster * 12) / 8); 148 if ((CurrentCluster % 2) == 0) 149 { 150 Entry = *CBlock & 0x0fff; 151 } 152 else 153 { 154 Entry = *CBlock >> 4; 155 } 156 157 // DPRINT("Entry %x\n",Entry); 158 if (Entry >= 0xff8 && Entry <= 0xfff) 159 Entry = 0xffffffff; 160 161 // DPRINT("Returning %x\n",Entry); 162 ASSERT(Entry != 0); 163 *NextCluster = Entry; 164 CcUnpinData(Context); 165 // return Entry == 0xffffffff ? STATUS_END_OF_FILE : STATUS_SUCCESS; 166 return STATUS_SUCCESS; 167 } 168 169 /* 170 * FUNCTION: Finds the first available cluster in a FAT16 table 171 */ 172 NTSTATUS 173 FAT16FindAndMarkAvailableCluster( 174 PDEVICE_EXTENSION DeviceExt, 175 PULONG Cluster) 176 { 177 ULONG FatLength; 178 ULONG StartCluster; 179 ULONG i, j; 180 PVOID BaseAddress; 181 ULONG ChunkSize; 182 PVOID Context = 0; 183 LARGE_INTEGER Offset; 184 PUSHORT Block; 185 PUSHORT BlockEnd; 186 187 ChunkSize = CACHEPAGESIZE(DeviceExt); 188 FatLength = (DeviceExt->FatInfo.NumberOfClusters + 2); 189 *Cluster = 0; 190 StartCluster = DeviceExt->LastAvailableCluster; 191 192 for (j = 0; j < 2; j++) 193 { 194 for (i = StartCluster; i < FatLength;) 195 { 196 Offset.QuadPart = ROUND_DOWN(i * 2, ChunkSize); 197 _SEH2_TRY 198 { 199 CcPinRead(DeviceExt->FATFileObject, &Offset, ChunkSize, PIN_WAIT, &Context, &BaseAddress); 200 } 201 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 202 { 203 DPRINT1("CcPinRead(Offset %x, Length %u) failed\n", (ULONG)Offset.QuadPart, ChunkSize); 204 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 205 } 206 _SEH2_END; 207 208 Block = (PUSHORT)((ULONG_PTR)BaseAddress + (i * 2) % ChunkSize); 209 BlockEnd = (PUSHORT)((ULONG_PTR)BaseAddress + ChunkSize); 210 211 /* Now process the whole block */ 212 while (Block < BlockEnd && i < FatLength) 213 { 214 if (*Block == 0) 215 { 216 DPRINT("Found available cluster 0x%x\n", i); 217 DeviceExt->LastAvailableCluster = *Cluster = i; 218 *Block = 0xffff; 219 CcSetDirtyPinnedData(Context, NULL); 220 CcUnpinData(Context); 221 if (DeviceExt->AvailableClustersValid) 222 InterlockedDecrement((PLONG)&DeviceExt->AvailableClusters); 223 return STATUS_SUCCESS; 224 } 225 226 Block++; 227 i++; 228 } 229 230 CcUnpinData(Context); 231 } 232 233 FatLength = StartCluster; 234 StartCluster = 2; 235 } 236 237 return STATUS_DISK_FULL; 238 } 239 240 /* 241 * FUNCTION: Finds the first available cluster in a FAT12 table 242 */ 243 NTSTATUS 244 FAT12FindAndMarkAvailableCluster( 245 PDEVICE_EXTENSION DeviceExt, 246 PULONG Cluster) 247 { 248 ULONG FatLength; 249 ULONG StartCluster; 250 ULONG Entry; 251 PUSHORT CBlock; 252 ULONG i, j; 253 PVOID BaseAddress; 254 PVOID Context; 255 LARGE_INTEGER Offset; 256 257 FatLength = DeviceExt->FatInfo.NumberOfClusters + 2; 258 *Cluster = 0; 259 StartCluster = DeviceExt->LastAvailableCluster; 260 Offset.QuadPart = 0; 261 _SEH2_TRY 262 { 263 CcPinRead(DeviceExt->FATFileObject, &Offset, DeviceExt->FatInfo.FATSectors * DeviceExt->FatInfo.BytesPerSector, PIN_WAIT, &Context, &BaseAddress); 264 } 265 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 266 { 267 DPRINT1("CcPinRead(Offset %x, Length %u) failed\n", (ULONG)Offset.QuadPart, DeviceExt->FatInfo.FATSectors * DeviceExt->FatInfo.BytesPerSector); 268 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 269 } 270 _SEH2_END; 271 272 for (j = 0; j < 2; j++) 273 { 274 for (i = StartCluster; i < FatLength; i++) 275 { 276 CBlock = (PUSHORT)((char*)BaseAddress + (i * 12) / 8); 277 if ((i % 2) == 0) 278 { 279 Entry = *CBlock & 0xfff; 280 } 281 else 282 { 283 Entry = *CBlock >> 4; 284 } 285 286 if (Entry == 0) 287 { 288 DPRINT("Found available cluster 0x%x\n", i); 289 DeviceExt->LastAvailableCluster = *Cluster = i; 290 if ((i % 2) == 0) 291 *CBlock = (*CBlock & 0xf000) | 0xfff; 292 else 293 *CBlock = (*CBlock & 0xf) | 0xfff0; 294 CcSetDirtyPinnedData(Context, NULL); 295 CcUnpinData(Context); 296 if (DeviceExt->AvailableClustersValid) 297 InterlockedDecrement((PLONG)&DeviceExt->AvailableClusters); 298 return STATUS_SUCCESS; 299 } 300 } 301 FatLength = StartCluster; 302 StartCluster = 2; 303 } 304 CcUnpinData(Context); 305 return STATUS_DISK_FULL; 306 } 307 308 /* 309 * FUNCTION: Finds the first available cluster in a FAT32 table 310 */ 311 NTSTATUS 312 FAT32FindAndMarkAvailableCluster( 313 PDEVICE_EXTENSION DeviceExt, 314 PULONG Cluster) 315 { 316 ULONG FatLength; 317 ULONG StartCluster; 318 ULONG i, j; 319 PVOID BaseAddress; 320 ULONG ChunkSize; 321 PVOID Context; 322 LARGE_INTEGER Offset; 323 PULONG Block; 324 PULONG BlockEnd; 325 326 ChunkSize = CACHEPAGESIZE(DeviceExt); 327 FatLength = (DeviceExt->FatInfo.NumberOfClusters + 2); 328 *Cluster = 0; 329 StartCluster = DeviceExt->LastAvailableCluster; 330 331 for (j = 0; j < 2; j++) 332 { 333 for (i = StartCluster; i < FatLength;) 334 { 335 Offset.QuadPart = ROUND_DOWN(i * 4, ChunkSize); 336 _SEH2_TRY 337 { 338 CcPinRead(DeviceExt->FATFileObject, &Offset, ChunkSize, PIN_WAIT, &Context, &BaseAddress); 339 } 340 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 341 { 342 DPRINT1("CcPinRead(Offset %x, Length %u) failed\n", (ULONG)Offset.QuadPart, ChunkSize); 343 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 344 } 345 _SEH2_END; 346 Block = (PULONG)((ULONG_PTR)BaseAddress + (i * 4) % ChunkSize); 347 BlockEnd = (PULONG)((ULONG_PTR)BaseAddress + ChunkSize); 348 349 /* Now process the whole block */ 350 while (Block < BlockEnd && i < FatLength) 351 { 352 if ((*Block & 0x0fffffff) == 0) 353 { 354 DPRINT("Found available cluster 0x%x\n", i); 355 DeviceExt->LastAvailableCluster = *Cluster = i; 356 *Block = 0x0fffffff; 357 CcSetDirtyPinnedData(Context, NULL); 358 CcUnpinData(Context); 359 if (DeviceExt->AvailableClustersValid) 360 InterlockedDecrement((PLONG)&DeviceExt->AvailableClusters); 361 return STATUS_SUCCESS; 362 } 363 364 Block++; 365 i++; 366 } 367 368 CcUnpinData(Context); 369 } 370 FatLength = StartCluster; 371 StartCluster = 2; 372 } 373 return STATUS_DISK_FULL; 374 } 375 376 /* 377 * FUNCTION: Counts free cluster in a FAT12 table 378 */ 379 static 380 NTSTATUS 381 FAT12CountAvailableClusters( 382 PDEVICE_EXTENSION DeviceExt) 383 { 384 ULONG Entry; 385 PVOID BaseAddress; 386 ULONG ulCount = 0; 387 ULONG i; 388 ULONG numberofclusters; 389 LARGE_INTEGER Offset; 390 PVOID Context; 391 PUSHORT CBlock; 392 393 Offset.QuadPart = 0; 394 _SEH2_TRY 395 { 396 CcMapData(DeviceExt->FATFileObject, &Offset, DeviceExt->FatInfo.FATSectors * DeviceExt->FatInfo.BytesPerSector, MAP_WAIT, &Context, &BaseAddress); 397 } 398 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 399 { 400 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 401 } 402 _SEH2_END; 403 404 numberofclusters = DeviceExt->FatInfo.NumberOfClusters + 2; 405 406 for (i = 2; i < numberofclusters; i++) 407 { 408 CBlock = (PUSHORT)((char*)BaseAddress + (i * 12) / 8); 409 if ((i % 2) == 0) 410 { 411 Entry = *CBlock & 0x0fff; 412 } 413 else 414 { 415 Entry = *CBlock >> 4; 416 } 417 418 if (Entry == 0) 419 ulCount++; 420 } 421 422 CcUnpinData(Context); 423 DeviceExt->AvailableClusters = ulCount; 424 DeviceExt->AvailableClustersValid = TRUE; 425 426 return STATUS_SUCCESS; 427 } 428 429 430 /* 431 * FUNCTION: Counts free clusters in a FAT16 table 432 */ 433 static 434 NTSTATUS 435 FAT16CountAvailableClusters( 436 PDEVICE_EXTENSION DeviceExt) 437 { 438 PUSHORT Block; 439 PUSHORT BlockEnd; 440 PVOID BaseAddress = NULL; 441 ULONG ulCount = 0; 442 ULONG i; 443 ULONG ChunkSize; 444 PVOID Context = NULL; 445 LARGE_INTEGER Offset; 446 ULONG FatLength; 447 448 ChunkSize = CACHEPAGESIZE(DeviceExt); 449 FatLength = (DeviceExt->FatInfo.NumberOfClusters + 2); 450 451 for (i = 2; i < FatLength; ) 452 { 453 Offset.QuadPart = ROUND_DOWN(i * 2, ChunkSize); 454 _SEH2_TRY 455 { 456 CcMapData(DeviceExt->FATFileObject, &Offset, ChunkSize, MAP_WAIT, &Context, &BaseAddress); 457 } 458 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 459 { 460 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 461 } 462 _SEH2_END; 463 Block = (PUSHORT)((ULONG_PTR)BaseAddress + (i * 2) % ChunkSize); 464 BlockEnd = (PUSHORT)((ULONG_PTR)BaseAddress + ChunkSize); 465 466 /* Now process the whole block */ 467 while (Block < BlockEnd && i < FatLength) 468 { 469 if (*Block == 0) 470 ulCount++; 471 Block++; 472 i++; 473 } 474 475 CcUnpinData(Context); 476 } 477 478 DeviceExt->AvailableClusters = ulCount; 479 DeviceExt->AvailableClustersValid = TRUE; 480 481 return STATUS_SUCCESS; 482 } 483 484 485 /* 486 * FUNCTION: Counts free clusters in a FAT32 table 487 */ 488 static 489 NTSTATUS 490 FAT32CountAvailableClusters( 491 PDEVICE_EXTENSION DeviceExt) 492 { 493 PULONG Block; 494 PULONG BlockEnd; 495 PVOID BaseAddress = NULL; 496 ULONG ulCount = 0; 497 ULONG i; 498 ULONG ChunkSize; 499 PVOID Context = NULL; 500 LARGE_INTEGER Offset; 501 ULONG FatLength; 502 503 ChunkSize = CACHEPAGESIZE(DeviceExt); 504 FatLength = (DeviceExt->FatInfo.NumberOfClusters + 2); 505 506 for (i = 2; i < FatLength; ) 507 { 508 Offset.QuadPart = ROUND_DOWN(i * 4, ChunkSize); 509 _SEH2_TRY 510 { 511 CcMapData(DeviceExt->FATFileObject, &Offset, ChunkSize, MAP_WAIT, &Context, &BaseAddress); 512 } 513 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 514 { 515 DPRINT1("CcMapData(Offset %x, Length %u) failed\n", (ULONG)Offset.QuadPart, ChunkSize); 516 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 517 } 518 _SEH2_END; 519 Block = (PULONG)((ULONG_PTR)BaseAddress + (i * 4) % ChunkSize); 520 BlockEnd = (PULONG)((ULONG_PTR)BaseAddress + ChunkSize); 521 522 /* Now process the whole block */ 523 while (Block < BlockEnd && i < FatLength) 524 { 525 if ((*Block & 0x0fffffff) == 0) 526 ulCount++; 527 Block++; 528 i++; 529 } 530 531 CcUnpinData(Context); 532 } 533 534 DeviceExt->AvailableClusters = ulCount; 535 DeviceExt->AvailableClustersValid = TRUE; 536 537 return STATUS_SUCCESS; 538 } 539 540 NTSTATUS 541 CountAvailableClusters( 542 PDEVICE_EXTENSION DeviceExt, 543 PLARGE_INTEGER Clusters) 544 { 545 NTSTATUS Status = STATUS_SUCCESS; 546 ExAcquireResourceExclusiveLite (&DeviceExt->FatResource, TRUE); 547 if (!DeviceExt->AvailableClustersValid) 548 { 549 if (DeviceExt->FatInfo.FatType == FAT12) 550 Status = FAT12CountAvailableClusters(DeviceExt); 551 else if (DeviceExt->FatInfo.FatType == FAT16 || DeviceExt->FatInfo.FatType == FATX16) 552 Status = FAT16CountAvailableClusters(DeviceExt); 553 else 554 Status = FAT32CountAvailableClusters(DeviceExt); 555 } 556 if (Clusters != NULL) 557 { 558 Clusters->QuadPart = DeviceExt->AvailableClusters; 559 } 560 ExReleaseResourceLite (&DeviceExt->FatResource); 561 562 return Status; 563 } 564 565 566 /* 567 * FUNCTION: Writes a cluster to the FAT12 physical and in-memory tables 568 */ 569 NTSTATUS 570 FAT12WriteCluster( 571 PDEVICE_EXTENSION DeviceExt, 572 ULONG ClusterToWrite, 573 ULONG NewValue, 574 PULONG OldValue) 575 { 576 ULONG FATOffset; 577 PUCHAR CBlock; 578 PVOID BaseAddress; 579 PVOID Context; 580 LARGE_INTEGER Offset; 581 582 Offset.QuadPart = 0; 583 _SEH2_TRY 584 { 585 CcPinRead(DeviceExt->FATFileObject, &Offset, DeviceExt->FatInfo.FATSectors * DeviceExt->FatInfo.BytesPerSector, PIN_WAIT, &Context, &BaseAddress); 586 } 587 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 588 { 589 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 590 } 591 _SEH2_END; 592 CBlock = (PUCHAR)BaseAddress; 593 594 FATOffset = (ClusterToWrite * 12) / 8; 595 DPRINT("Writing 0x%x for 0x%x at 0x%x\n", 596 NewValue, ClusterToWrite, FATOffset); 597 if ((ClusterToWrite % 2) == 0) 598 { 599 *OldValue = CBlock[FATOffset] + ((CBlock[FATOffset + 1] & 0x0f) << 8); 600 CBlock[FATOffset] = (UCHAR)NewValue; 601 CBlock[FATOffset + 1] &= 0xf0; 602 CBlock[FATOffset + 1] |= (NewValue & 0xf00) >> 8; 603 } 604 else 605 { 606 *OldValue = (CBlock[FATOffset] >> 4) + (CBlock[FATOffset + 1] << 4); 607 CBlock[FATOffset] &= 0x0f; 608 CBlock[FATOffset] |= (NewValue & 0xf) << 4; 609 CBlock[FATOffset + 1] = (UCHAR)(NewValue >> 4); 610 } 611 /* Write the changed FAT sector(s) to disk */ 612 CcSetDirtyPinnedData(Context, NULL); 613 CcUnpinData(Context); 614 return STATUS_SUCCESS; 615 } 616 617 /* 618 * FUNCTION: Writes a cluster to the FAT16 physical and in-memory tables 619 */ 620 NTSTATUS 621 FAT16WriteCluster( 622 PDEVICE_EXTENSION DeviceExt, 623 ULONG ClusterToWrite, 624 ULONG NewValue, 625 PULONG OldValue) 626 { 627 PVOID BaseAddress; 628 ULONG FATOffset; 629 ULONG ChunkSize; 630 PVOID Context; 631 LARGE_INTEGER Offset; 632 PUSHORT Cluster; 633 634 ChunkSize = CACHEPAGESIZE(DeviceExt); 635 FATOffset = ClusterToWrite * 2; 636 Offset.QuadPart = ROUND_DOWN(FATOffset, ChunkSize); 637 _SEH2_TRY 638 { 639 CcPinRead(DeviceExt->FATFileObject, &Offset, ChunkSize, PIN_WAIT, &Context, &BaseAddress); 640 } 641 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 642 { 643 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 644 } 645 _SEH2_END; 646 647 DPRINT("Writing 0x%x for offset 0x%x 0x%x\n", NewValue, FATOffset, 648 ClusterToWrite); 649 Cluster = ((PUSHORT)((char*)BaseAddress + (FATOffset % ChunkSize))); 650 *OldValue = *Cluster; 651 *Cluster = (USHORT)NewValue; 652 CcSetDirtyPinnedData(Context, NULL); 653 CcUnpinData(Context); 654 return STATUS_SUCCESS; 655 } 656 657 /* 658 * FUNCTION: Writes a cluster to the FAT32 physical tables 659 */ 660 NTSTATUS 661 FAT32WriteCluster( 662 PDEVICE_EXTENSION DeviceExt, 663 ULONG ClusterToWrite, 664 ULONG NewValue, 665 PULONG OldValue) 666 { 667 PVOID BaseAddress; 668 ULONG FATOffset; 669 ULONG ChunkSize; 670 PVOID Context; 671 LARGE_INTEGER Offset; 672 PULONG Cluster; 673 674 ChunkSize = CACHEPAGESIZE(DeviceExt); 675 676 FATOffset = (ClusterToWrite * 4); 677 Offset.QuadPart = ROUND_DOWN(FATOffset, ChunkSize); 678 _SEH2_TRY 679 { 680 CcPinRead(DeviceExt->FATFileObject, &Offset, ChunkSize, PIN_WAIT, &Context, &BaseAddress); 681 } 682 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 683 { 684 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 685 } 686 _SEH2_END; 687 688 DPRINT("Writing 0x%x for offset 0x%x 0x%x\n", NewValue, FATOffset, 689 ClusterToWrite); 690 Cluster = ((PULONG)((char*)BaseAddress + (FATOffset % ChunkSize))); 691 *OldValue = *Cluster & 0x0fffffff; 692 *Cluster = (*Cluster & 0xf0000000) | (NewValue & 0x0fffffff); 693 694 CcSetDirtyPinnedData(Context, NULL); 695 CcUnpinData(Context); 696 697 return STATUS_SUCCESS; 698 } 699 700 701 /* 702 * FUNCTION: Write a changed FAT entry 703 */ 704 NTSTATUS 705 WriteCluster( 706 PDEVICE_EXTENSION DeviceExt, 707 ULONG ClusterToWrite, 708 ULONG NewValue) 709 { 710 NTSTATUS Status; 711 ULONG OldValue; 712 713 ExAcquireResourceExclusiveLite (&DeviceExt->FatResource, TRUE); 714 Status = DeviceExt->WriteCluster(DeviceExt, ClusterToWrite, NewValue, &OldValue); 715 if (DeviceExt->AvailableClustersValid) 716 { 717 if (OldValue && NewValue == 0) 718 InterlockedIncrement((PLONG)&DeviceExt->AvailableClusters); 719 else if (OldValue == 0 && NewValue) 720 InterlockedDecrement((PLONG)&DeviceExt->AvailableClusters); 721 } 722 ExReleaseResourceLite(&DeviceExt->FatResource); 723 return Status; 724 } 725 726 /* 727 * FUNCTION: Converts the cluster number to a sector number for this physical 728 * device 729 */ 730 ULONGLONG 731 ClusterToSector( 732 PDEVICE_EXTENSION DeviceExt, 733 ULONG Cluster) 734 { 735 return DeviceExt->FatInfo.dataStart + 736 ((ULONGLONG)(Cluster - 2) * DeviceExt->FatInfo.SectorsPerCluster); 737 738 } 739 740 /* 741 * FUNCTION: Retrieve the next cluster depending on the FAT type 742 */ 743 NTSTATUS 744 GetNextCluster( 745 PDEVICE_EXTENSION DeviceExt, 746 ULONG CurrentCluster, 747 PULONG NextCluster) 748 { 749 NTSTATUS Status; 750 751 DPRINT("GetNextCluster(DeviceExt %p, CurrentCluster %x)\n", 752 DeviceExt, CurrentCluster); 753 754 if (CurrentCluster == 0) 755 { 756 DPRINT1("WARNING: File system corruption detected. You may need to run a disk repair utility.\n"); 757 if (VfatGlobalData->Flags & VFAT_BREAK_ON_CORRUPTION) 758 ASSERT(CurrentCluster != 0); 759 return STATUS_FILE_CORRUPT_ERROR; 760 } 761 762 ExAcquireResourceSharedLite(&DeviceExt->FatResource, TRUE); 763 Status = DeviceExt->GetNextCluster(DeviceExt, CurrentCluster, NextCluster); 764 ExReleaseResourceLite(&DeviceExt->FatResource); 765 766 return Status; 767 } 768 769 /* 770 * FUNCTION: Retrieve the next cluster depending on the FAT type 771 */ 772 NTSTATUS 773 GetNextClusterExtend( 774 PDEVICE_EXTENSION DeviceExt, 775 ULONG CurrentCluster, 776 PULONG NextCluster) 777 { 778 ULONG NewCluster; 779 NTSTATUS Status; 780 781 DPRINT("GetNextClusterExtend(DeviceExt %p, CurrentCluster %x)\n", 782 DeviceExt, CurrentCluster); 783 784 ExAcquireResourceExclusiveLite(&DeviceExt->FatResource, TRUE); 785 /* 786 * If the file hasn't any clusters allocated then we need special 787 * handling 788 */ 789 if (CurrentCluster == 0) 790 { 791 Status = DeviceExt->FindAndMarkAvailableCluster(DeviceExt, &NewCluster); 792 if (!NT_SUCCESS(Status)) 793 { 794 ExReleaseResourceLite(&DeviceExt->FatResource); 795 return Status; 796 } 797 798 *NextCluster = NewCluster; 799 ExReleaseResourceLite(&DeviceExt->FatResource); 800 return STATUS_SUCCESS; 801 } 802 803 Status = DeviceExt->GetNextCluster(DeviceExt, CurrentCluster, NextCluster); 804 805 if ((*NextCluster) == 0xFFFFFFFF) 806 { 807 /* We are after last existing cluster, we must add one to file */ 808 /* Firstly, find the next available open allocation unit and 809 mark it as end of file */ 810 Status = DeviceExt->FindAndMarkAvailableCluster(DeviceExt, &NewCluster); 811 if (!NT_SUCCESS(Status)) 812 { 813 ExReleaseResourceLite(&DeviceExt->FatResource); 814 return Status; 815 } 816 817 /* Now, write the AU of the LastCluster with the value of the newly 818 found AU */ 819 WriteCluster(DeviceExt, CurrentCluster, NewCluster); 820 *NextCluster = NewCluster; 821 } 822 823 ExReleaseResourceLite(&DeviceExt->FatResource); 824 return Status; 825 } 826 827 /* 828 * FUNCTION: Retrieve the dirty status 829 */ 830 NTSTATUS 831 GetDirtyStatus( 832 PDEVICE_EXTENSION DeviceExt, 833 PBOOLEAN DirtyStatus) 834 { 835 NTSTATUS Status; 836 837 DPRINT("GetDirtyStatus(DeviceExt %p)\n", DeviceExt); 838 839 /* FAT12 has no dirty bit */ 840 if (DeviceExt->FatInfo.FatType == FAT12) 841 { 842 *DirtyStatus = FALSE; 843 return STATUS_SUCCESS; 844 } 845 846 /* Not really in the FAT, but share the lock because 847 * we're really low-level and shouldn't happent that often 848 * And call the appropriate function 849 */ 850 ExAcquireResourceSharedLite(&DeviceExt->FatResource, TRUE); 851 Status = DeviceExt->GetDirtyStatus(DeviceExt, DirtyStatus); 852 ExReleaseResourceLite(&DeviceExt->FatResource); 853 854 return Status; 855 } 856 857 NTSTATUS 858 FAT16GetDirtyStatus( 859 PDEVICE_EXTENSION DeviceExt, 860 PBOOLEAN DirtyStatus) 861 { 862 LARGE_INTEGER Offset; 863 ULONG Length; 864 #ifdef VOLUME_IS_NOT_CACHED_WORK_AROUND_IT 865 NTSTATUS Status; 866 #else 867 PVOID Context; 868 #endif 869 struct _BootSector * Sector; 870 871 /* We'll read the bootsector at 0 */ 872 Offset.QuadPart = 0; 873 Length = DeviceExt->FatInfo.BytesPerSector; 874 #ifndef VOLUME_IS_NOT_CACHED_WORK_AROUND_IT 875 /* Go through Cc for this */ 876 _SEH2_TRY 877 { 878 CcPinRead(DeviceExt->VolumeFcb->FileObject, &Offset, Length, PIN_WAIT, &Context, (PVOID *)&Sector); 879 } 880 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 881 { 882 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 883 } 884 _SEH2_END; 885 #else 886 /* No Cc, do it the old way: 887 * - Allocate a big enough buffer 888 * - And read the disk 889 */ 890 Sector = ExAllocatePoolWithTag(NonPagedPool, Length, TAG_BUFFER); 891 if (Sector == NULL) 892 { 893 *DirtyStatus = TRUE; 894 return STATUS_INSUFFICIENT_RESOURCES; 895 } 896 897 Status = VfatReadDisk(DeviceExt->StorageDevice, &Offset, Length, (PUCHAR)Sector, FALSE); 898 if (!NT_SUCCESS(Status)) 899 { 900 *DirtyStatus = TRUE; 901 ExFreePoolWithTag(Sector, TAG_BUFFER); 902 return Status; 903 } 904 #endif 905 906 /* Make sure we have a boot sector... 907 * FIXME: This check is a bit lame and should be improved 908 */ 909 if (Sector->Signatur1 != 0xaa55) 910 { 911 /* Set we are dirty so that we don't attempt anything */ 912 *DirtyStatus = TRUE; 913 #ifndef VOLUME_IS_NOT_CACHED_WORK_AROUND_IT 914 CcUnpinData(Context); 915 #else 916 ExFreePoolWithTag(Sector, TAG_BUFFER); 917 #endif 918 return STATUS_DISK_CORRUPT_ERROR; 919 } 920 921 /* Return the status of the dirty bit */ 922 if (Sector->Res1 & FAT_DIRTY_BIT) 923 *DirtyStatus = TRUE; 924 else 925 *DirtyStatus = FALSE; 926 927 #ifndef VOLUME_IS_NOT_CACHED_WORK_AROUND_IT 928 CcUnpinData(Context); 929 #else 930 ExFreePoolWithTag(Sector, TAG_BUFFER); 931 #endif 932 return STATUS_SUCCESS; 933 } 934 935 NTSTATUS 936 FAT32GetDirtyStatus( 937 PDEVICE_EXTENSION DeviceExt, 938 PBOOLEAN DirtyStatus) 939 { 940 LARGE_INTEGER Offset; 941 ULONG Length; 942 #ifdef VOLUME_IS_NOT_CACHED_WORK_AROUND_IT 943 NTSTATUS Status; 944 #else 945 PVOID Context; 946 #endif 947 struct _BootSector32 * Sector; 948 949 /* We'll read the bootsector at 0 */ 950 Offset.QuadPart = 0; 951 Length = DeviceExt->FatInfo.BytesPerSector; 952 #ifndef VOLUME_IS_NOT_CACHED_WORK_AROUND_IT 953 /* Go through Cc for this */ 954 _SEH2_TRY 955 { 956 CcPinRead(DeviceExt->VolumeFcb->FileObject, &Offset, Length, PIN_WAIT, &Context, (PVOID *)&Sector); 957 } 958 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 959 { 960 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 961 } 962 _SEH2_END; 963 #else 964 /* No Cc, do it the old way: 965 * - Allocate a big enough buffer 966 * - And read the disk 967 */ 968 Sector = ExAllocatePoolWithTag(NonPagedPool, Length, TAG_BUFFER); 969 if (Sector == NULL) 970 { 971 *DirtyStatus = TRUE; 972 return STATUS_INSUFFICIENT_RESOURCES; 973 } 974 975 Status = VfatReadDisk(DeviceExt->StorageDevice, &Offset, Length, (PUCHAR)Sector, FALSE); 976 if (!NT_SUCCESS(Status)) 977 { 978 *DirtyStatus = TRUE; 979 ExFreePoolWithTag(Sector, TAG_BUFFER); 980 return Status; 981 } 982 #endif 983 984 /* Make sure we have a boot sector... 985 * FIXME: This check is a bit lame and should be improved 986 */ 987 if (Sector->Signature1 != 0xaa55) 988 { 989 /* Set we are dirty so that we don't attempt anything */ 990 *DirtyStatus = TRUE; 991 #ifndef VOLUME_IS_NOT_CACHED_WORK_AROUND_IT 992 CcUnpinData(Context); 993 #else 994 ExFreePoolWithTag(Sector, TAG_BUFFER); 995 #endif 996 return STATUS_DISK_CORRUPT_ERROR; 997 } 998 999 /* Return the status of the dirty bit */ 1000 if (Sector->Res4 & FAT_DIRTY_BIT) 1001 *DirtyStatus = TRUE; 1002 else 1003 *DirtyStatus = FALSE; 1004 1005 #ifndef VOLUME_IS_NOT_CACHED_WORK_AROUND_IT 1006 CcUnpinData(Context); 1007 #else 1008 ExFreePoolWithTag(Sector, TAG_BUFFER); 1009 #endif 1010 return STATUS_SUCCESS; 1011 } 1012 1013 /* 1014 * FUNCTION: Set the dirty status 1015 */ 1016 NTSTATUS 1017 SetDirtyStatus( 1018 PDEVICE_EXTENSION DeviceExt, 1019 BOOLEAN DirtyStatus) 1020 { 1021 NTSTATUS Status; 1022 1023 DPRINT("SetDirtyStatus(DeviceExt %p, DirtyStatus %d)\n", DeviceExt, DirtyStatus); 1024 1025 /* FAT12 has no dirty bit */ 1026 if (DeviceExt->FatInfo.FatType == FAT12) 1027 { 1028 return STATUS_SUCCESS; 1029 } 1030 1031 /* Not really in the FAT, but share the lock because 1032 * we're really low-level and shouldn't happent that often 1033 * And call the appropriate function 1034 * Acquire exclusive because we will modify ondisk value 1035 */ 1036 ExAcquireResourceExclusiveLite(&DeviceExt->FatResource, TRUE); 1037 Status = DeviceExt->SetDirtyStatus(DeviceExt, DirtyStatus); 1038 ExReleaseResourceLite(&DeviceExt->FatResource); 1039 1040 return Status; 1041 } 1042 1043 NTSTATUS 1044 FAT16SetDirtyStatus( 1045 PDEVICE_EXTENSION DeviceExt, 1046 BOOLEAN DirtyStatus) 1047 { 1048 LARGE_INTEGER Offset; 1049 ULONG Length; 1050 #ifdef VOLUME_IS_NOT_CACHED_WORK_AROUND_IT 1051 NTSTATUS Status; 1052 #else 1053 PVOID Context; 1054 #endif 1055 struct _BootSector * Sector; 1056 1057 /* We'll read (and then write) the bootsector at 0 */ 1058 Offset.QuadPart = 0; 1059 Length = DeviceExt->FatInfo.BytesPerSector; 1060 #ifndef VOLUME_IS_NOT_CACHED_WORK_AROUND_IT 1061 /* Go through Cc for this */ 1062 _SEH2_TRY 1063 { 1064 CcPinRead(DeviceExt->VolumeFcb->FileObject, &Offset, Length, PIN_WAIT, &Context, (PVOID *)&Sector); 1065 } 1066 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 1067 { 1068 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 1069 } 1070 _SEH2_END; 1071 #else 1072 /* No Cc, do it the old way: 1073 * - Allocate a big enough buffer 1074 * - And read the disk 1075 */ 1076 Sector = ExAllocatePoolWithTag(NonPagedPool, Length, TAG_BUFFER); 1077 if (Sector == NULL) 1078 { 1079 return STATUS_INSUFFICIENT_RESOURCES; 1080 } 1081 1082 Status = VfatReadDisk(DeviceExt->StorageDevice, &Offset, Length, (PUCHAR)Sector, FALSE); 1083 if (!NT_SUCCESS(Status)) 1084 { 1085 ExFreePoolWithTag(Sector, TAG_BUFFER); 1086 return Status; 1087 } 1088 #endif 1089 1090 /* Make sure we have a boot sector... 1091 * FIXME: This check is a bit lame and should be improved 1092 */ 1093 if (Sector->Signatur1 != 0xaa55) 1094 { 1095 #ifndef VOLUME_IS_NOT_CACHED_WORK_AROUND_IT 1096 CcUnpinData(Context); 1097 #else 1098 ExFreePoolWithTag(Sector, TAG_BUFFER); 1099 #endif 1100 return STATUS_DISK_CORRUPT_ERROR; 1101 } 1102 1103 /* Modify the dirty bit status according 1104 * to caller needs 1105 */ 1106 if (!DirtyStatus) 1107 { 1108 Sector->Res1 &= ~FAT_DIRTY_BIT; 1109 } 1110 else 1111 { 1112 Sector->Res1 |= FAT_DIRTY_BIT; 1113 } 1114 1115 #ifndef VOLUME_IS_NOT_CACHED_WORK_AROUND_IT 1116 /* Mark boot sector dirty so that it gets written to the disk */ 1117 CcSetDirtyPinnedData(Context, NULL); 1118 CcUnpinData(Context); 1119 return STATUS_SUCCESS; 1120 #else 1121 /* Write back the boot sector to the disk */ 1122 Status = VfatWriteDisk(DeviceExt->StorageDevice, &Offset, Length, (PUCHAR)Sector, FALSE); 1123 ExFreePoolWithTag(Sector, TAG_BUFFER); 1124 return Status; 1125 #endif 1126 } 1127 1128 NTSTATUS 1129 FAT32SetDirtyStatus( 1130 PDEVICE_EXTENSION DeviceExt, 1131 BOOLEAN DirtyStatus) 1132 { 1133 LARGE_INTEGER Offset; 1134 ULONG Length; 1135 #ifdef VOLUME_IS_NOT_CACHED_WORK_AROUND_IT 1136 NTSTATUS Status; 1137 #else 1138 PVOID Context; 1139 #endif 1140 struct _BootSector32 * Sector; 1141 1142 /* We'll read (and then write) the bootsector at 0 */ 1143 Offset.QuadPart = 0; 1144 Length = DeviceExt->FatInfo.BytesPerSector; 1145 #ifndef VOLUME_IS_NOT_CACHED_WORK_AROUND_IT 1146 /* Go through Cc for this */ 1147 _SEH2_TRY 1148 { 1149 CcPinRead(DeviceExt->VolumeFcb->FileObject, &Offset, Length, PIN_WAIT, &Context, (PVOID *)&Sector); 1150 } 1151 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 1152 { 1153 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 1154 } 1155 _SEH2_END; 1156 #else 1157 /* No Cc, do it the old way: 1158 * - Allocate a big enough buffer 1159 * - And read the disk 1160 */ 1161 Sector = ExAllocatePoolWithTag(NonPagedPool, Length, TAG_BUFFER); 1162 if (Sector == NULL) 1163 { 1164 return STATUS_INSUFFICIENT_RESOURCES; 1165 } 1166 1167 Status = VfatReadDisk(DeviceExt->StorageDevice, &Offset, Length, (PUCHAR)Sector, FALSE); 1168 if (!NT_SUCCESS(Status)) 1169 { 1170 ExFreePoolWithTag(Sector, TAG_BUFFER); 1171 return Status; 1172 } 1173 #endif 1174 1175 /* Make sure we have a boot sector... 1176 * FIXME: This check is a bit lame and should be improved 1177 */ 1178 if (Sector->Signature1 != 0xaa55) 1179 { 1180 ASSERT(FALSE); 1181 #ifndef VOLUME_IS_NOT_CACHED_WORK_AROUND_IT 1182 CcUnpinData(Context); 1183 #else 1184 ExFreePoolWithTag(Sector, TAG_BUFFER); 1185 #endif 1186 return STATUS_DISK_CORRUPT_ERROR; 1187 } 1188 1189 /* Modify the dirty bit status according 1190 * to caller needs 1191 */ 1192 if (!DirtyStatus) 1193 { 1194 Sector->Res4 &= ~FAT_DIRTY_BIT; 1195 } 1196 else 1197 { 1198 Sector->Res4 |= FAT_DIRTY_BIT; 1199 } 1200 1201 #ifndef VOLUME_IS_NOT_CACHED_WORK_AROUND_IT 1202 /* Mark boot sector dirty so that it gets written to the disk */ 1203 CcSetDirtyPinnedData(Context, NULL); 1204 CcUnpinData(Context); 1205 return STATUS_SUCCESS; 1206 #else 1207 /* Write back the boot sector to the disk */ 1208 Status = VfatWriteDisk(DeviceExt->StorageDevice, &Offset, Length, (PUCHAR)Sector, FALSE); 1209 ExFreePoolWithTag(Sector, TAG_BUFFER); 1210 return Status; 1211 #endif 1212 } 1213 1214 NTSTATUS 1215 FAT32UpdateFreeClustersCount( 1216 PDEVICE_EXTENSION DeviceExt) 1217 { 1218 LARGE_INTEGER Offset; 1219 ULONG Length; 1220 #ifdef VOLUME_IS_NOT_CACHED_WORK_AROUND_IT 1221 NTSTATUS Status; 1222 #else 1223 PVOID Context; 1224 #endif 1225 struct _FsInfoSector * Sector; 1226 1227 if (!DeviceExt->AvailableClustersValid) 1228 { 1229 return STATUS_INVALID_PARAMETER; 1230 } 1231 1232 /* We'll read (and then write) the fsinfo sector */ 1233 Offset.QuadPart = DeviceExt->FatInfo.FSInfoSector * DeviceExt->FatInfo.BytesPerSector; 1234 Length = DeviceExt->FatInfo.BytesPerSector; 1235 #ifndef VOLUME_IS_NOT_CACHED_WORK_AROUND_IT 1236 /* Go through Cc for this */ 1237 _SEH2_TRY 1238 { 1239 CcPinRead(DeviceExt->VolumeFcb->FileObject, &Offset, Length, PIN_WAIT, &Context, (PVOID *)&Sector); 1240 } 1241 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 1242 { 1243 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 1244 } 1245 _SEH2_END; 1246 #else 1247 /* No Cc, do it the old way: 1248 * - Allocate a big enough buffer 1249 * - And read the disk 1250 */ 1251 Sector = ExAllocatePoolWithTag(NonPagedPool, Length, TAG_BUFFER); 1252 if (Sector == NULL) 1253 { 1254 return STATUS_INSUFFICIENT_RESOURCES; 1255 } 1256 1257 Status = VfatReadDisk(DeviceExt->StorageDevice, &Offset, Length, (PUCHAR)Sector, FALSE); 1258 if (!NT_SUCCESS(Status)) 1259 { 1260 ExFreePoolWithTag(Sector, TAG_BUFFER); 1261 return Status; 1262 } 1263 #endif 1264 1265 /* Make sure we have a FSINFO sector */ 1266 if (Sector->ExtBootSignature2 != 0x41615252 || 1267 Sector->FSINFOSignature != 0x61417272 || 1268 Sector->Signatur2 != 0xaa550000) 1269 { 1270 ASSERT(FALSE); 1271 #ifndef VOLUME_IS_NOT_CACHED_WORK_AROUND_IT 1272 CcUnpinData(Context); 1273 #else 1274 ExFreePoolWithTag(Sector, TAG_BUFFER); 1275 #endif 1276 return STATUS_DISK_CORRUPT_ERROR; 1277 } 1278 1279 /* Update the free clusters count */ 1280 Sector->FreeCluster = InterlockedCompareExchange((PLONG)&DeviceExt->AvailableClusters, 0, 0); 1281 1282 #ifndef VOLUME_IS_NOT_CACHED_WORK_AROUND_IT 1283 /* Mark FSINFO sector dirty so that it gets written to the disk */ 1284 CcSetDirtyPinnedData(Context, NULL); 1285 CcUnpinData(Context); 1286 return STATUS_SUCCESS; 1287 #else 1288 /* Write back the FSINFO sector to the disk */ 1289 Status = VfatWriteDisk(DeviceExt->StorageDevice, &Offset, Length, (PUCHAR)Sector, FALSE); 1290 ExFreePoolWithTag(Sector, TAG_BUFFER); 1291 return Status; 1292 #endif 1293 } 1294 1295 /* EOF */ 1296