1 /* 2 * PROJECT: VFAT Filesystem 3 * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) 4 * PURPOSE: Filesystem routines 5 * COPYRIGHT: Copyright 2002-2013 Eric Kohl <eric.kohl@reactos.org> 6 * Copyright 2008-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 extern VFAT_DISPATCH FatXDispatch; 17 extern VFAT_DISPATCH FatDispatch; 18 19 /* FUNCTIONS ****************************************************************/ 20 21 #define CACHEPAGESIZE(pDeviceExt) ((pDeviceExt)->FatInfo.BytesPerCluster > PAGE_SIZE ? \ 22 (pDeviceExt)->FatInfo.BytesPerCluster : PAGE_SIZE) 23 24 static 25 NTSTATUS 26 VfatHasFileSystem( 27 PDEVICE_OBJECT DeviceToMount, 28 PBOOLEAN RecognizedFS, 29 PFATINFO pFatInfo, 30 BOOLEAN Override) 31 { 32 NTSTATUS Status; 33 PARTITION_INFORMATION PartitionInfo; 34 DISK_GEOMETRY DiskGeometry; 35 FATINFO FatInfo; 36 ULONG Size; 37 ULONG Sectors; 38 LARGE_INTEGER Offset; 39 struct _BootSector* Boot; 40 struct _BootSectorFatX* BootFatX; 41 BOOLEAN PartitionInfoIsValid = FALSE; 42 43 DPRINT("VfatHasFileSystem\n"); 44 45 *RecognizedFS = FALSE; 46 47 Size = sizeof(DISK_GEOMETRY); 48 Status = VfatBlockDeviceIoControl(DeviceToMount, 49 IOCTL_DISK_GET_DRIVE_GEOMETRY, 50 NULL, 51 0, 52 &DiskGeometry, 53 &Size, 54 Override); 55 if (!NT_SUCCESS(Status)) 56 { 57 DPRINT("VfatBlockDeviceIoControl failed (%x)\n", Status); 58 return Status; 59 } 60 61 FatInfo.FixedMedia = DiskGeometry.MediaType == FixedMedia ? TRUE : FALSE; 62 if (DiskGeometry.MediaType == FixedMedia || DiskGeometry.MediaType == RemovableMedia) 63 { 64 // We have found a hard disk 65 Size = sizeof(PARTITION_INFORMATION); 66 Status = VfatBlockDeviceIoControl(DeviceToMount, 67 IOCTL_DISK_GET_PARTITION_INFO, 68 NULL, 69 0, 70 &PartitionInfo, 71 &Size, 72 Override); 73 if (!NT_SUCCESS(Status)) 74 { 75 DPRINT("VfatBlockDeviceIoControl failed (%x)\n", Status); 76 return Status; 77 } 78 79 DPRINT("Partition Information:\n"); 80 DPRINT("StartingOffset %I64x\n", PartitionInfo.StartingOffset.QuadPart / 512); 81 DPRINT("PartitionLength %I64x\n", PartitionInfo.PartitionLength.QuadPart / 512); 82 DPRINT("HiddenSectors %u\n", PartitionInfo.HiddenSectors); 83 DPRINT("PartitionNumber %u\n", PartitionInfo.PartitionNumber); 84 DPRINT("PartitionType %u\n", PartitionInfo.PartitionType); 85 DPRINT("BootIndicator %u\n", PartitionInfo.BootIndicator); 86 DPRINT("RecognizedPartition %u\n", PartitionInfo.RecognizedPartition); 87 DPRINT("RewritePartition %u\n", PartitionInfo.RewritePartition); 88 if (PartitionInfo.PartitionType) 89 { 90 if (PartitionInfo.PartitionType == PARTITION_FAT_12 || 91 PartitionInfo.PartitionType == PARTITION_FAT_16 || 92 PartitionInfo.PartitionType == PARTITION_HUGE || 93 PartitionInfo.PartitionType == PARTITION_FAT32 || 94 PartitionInfo.PartitionType == PARTITION_FAT32_XINT13 || 95 PartitionInfo.PartitionType == PARTITION_XINT13) 96 { 97 PartitionInfoIsValid = TRUE; 98 *RecognizedFS = TRUE; 99 } 100 } 101 else if (DiskGeometry.MediaType == RemovableMedia && 102 PartitionInfo.PartitionNumber > 0 && 103 PartitionInfo.StartingOffset.QuadPart == 0 && 104 PartitionInfo.PartitionLength.QuadPart > 0) 105 { 106 /* This is possible a removable media formated as super floppy */ 107 PartitionInfoIsValid = TRUE; 108 *RecognizedFS = TRUE; 109 } 110 } 111 else 112 { 113 *RecognizedFS = TRUE; 114 } 115 116 if (*RecognizedFS) 117 { 118 Boot = ExAllocatePoolWithTag(NonPagedPool, DiskGeometry.BytesPerSector, TAG_BUFFER); 119 if (Boot == NULL) 120 { 121 return STATUS_INSUFFICIENT_RESOURCES; 122 } 123 124 Offset.QuadPart = 0; 125 126 /* Try to recognize FAT12/FAT16/FAT32 partitions */ 127 Status = VfatReadDisk(DeviceToMount, &Offset, DiskGeometry.BytesPerSector, (PUCHAR) Boot, Override); 128 if (NT_SUCCESS(Status)) 129 { 130 if (Boot->Signatur1 != 0xaa55) 131 { 132 *RecognizedFS = FALSE; 133 } 134 135 if (*RecognizedFS && 136 Boot->BytesPerSector != 512 && 137 Boot->BytesPerSector != 1024 && 138 Boot->BytesPerSector != 2048 && 139 Boot->BytesPerSector != 4096) 140 { 141 DPRINT1("BytesPerSector %u\n", Boot->BytesPerSector); 142 *RecognizedFS = FALSE; 143 } 144 145 if (*RecognizedFS && 146 Boot->FATCount != 1 && 147 Boot->FATCount != 2) 148 { 149 DPRINT1("FATCount %u\n", Boot->FATCount); 150 *RecognizedFS = FALSE; 151 } 152 153 if (*RecognizedFS && 154 Boot->Media != 0xf0 && 155 Boot->Media != 0xf8 && 156 Boot->Media != 0xf9 && 157 Boot->Media != 0xfa && 158 Boot->Media != 0xfb && 159 Boot->Media != 0xfc && 160 Boot->Media != 0xfd && 161 Boot->Media != 0xfe && 162 Boot->Media != 0xff) 163 { 164 DPRINT1("Media %02x\n", Boot->Media); 165 *RecognizedFS = FALSE; 166 } 167 168 if (*RecognizedFS && 169 Boot->SectorsPerCluster != 1 && 170 Boot->SectorsPerCluster != 2 && 171 Boot->SectorsPerCluster != 4 && 172 Boot->SectorsPerCluster != 8 && 173 Boot->SectorsPerCluster != 16 && 174 Boot->SectorsPerCluster != 32 && 175 Boot->SectorsPerCluster != 64 && 176 Boot->SectorsPerCluster != 128) 177 { 178 DPRINT1("SectorsPerCluster %02x\n", Boot->SectorsPerCluster); 179 *RecognizedFS = FALSE; 180 } 181 182 if (*RecognizedFS && 183 Boot->BytesPerSector * Boot->SectorsPerCluster > 64 * 1024) 184 { 185 DPRINT1("ClusterSize %d\n", Boot->BytesPerSector * Boot->SectorsPerCluster); 186 *RecognizedFS = FALSE; 187 } 188 189 if (*RecognizedFS) 190 { 191 FatInfo.VolumeID = Boot->VolumeID; 192 FatInfo.FATStart = Boot->ReservedSectors; 193 FatInfo.FATCount = Boot->FATCount; 194 FatInfo.FATSectors = Boot->FATSectors ? Boot->FATSectors : ((struct _BootSector32*) Boot)->FATSectors32; 195 FatInfo.BytesPerSector = Boot->BytesPerSector; 196 FatInfo.SectorsPerCluster = Boot->SectorsPerCluster; 197 FatInfo.BytesPerCluster = FatInfo.BytesPerSector * FatInfo.SectorsPerCluster; 198 FatInfo.rootDirectorySectors = ((Boot->RootEntries * 32) + Boot->BytesPerSector - 1) / Boot->BytesPerSector; 199 FatInfo.rootStart = FatInfo.FATStart + FatInfo.FATCount * FatInfo.FATSectors; 200 FatInfo.dataStart = FatInfo.rootStart + FatInfo.rootDirectorySectors; 201 FatInfo.Sectors = Sectors = Boot->Sectors ? Boot->Sectors : Boot->SectorsHuge; 202 Sectors -= Boot->ReservedSectors + FatInfo.FATCount * FatInfo.FATSectors + FatInfo.rootDirectorySectors; 203 FatInfo.NumberOfClusters = Sectors / Boot->SectorsPerCluster; 204 if (FatInfo.NumberOfClusters < 4085) 205 { 206 DPRINT("FAT12\n"); 207 FatInfo.FatType = FAT12; 208 FatInfo.RootCluster = (FatInfo.rootStart - 1) / FatInfo.SectorsPerCluster; 209 RtlCopyMemory(&FatInfo.VolumeLabel, &Boot->VolumeLabel, sizeof(FatInfo.VolumeLabel)); 210 } 211 else if (FatInfo.NumberOfClusters >= 65525) 212 { 213 DPRINT("FAT32\n"); 214 FatInfo.FatType = FAT32; 215 FatInfo.RootCluster = ((struct _BootSector32*) Boot)->RootCluster; 216 FatInfo.rootStart = FatInfo.dataStart + ((FatInfo.RootCluster - 2) * FatInfo.SectorsPerCluster); 217 FatInfo.VolumeID = ((struct _BootSector32*) Boot)->VolumeID; 218 FatInfo.FSInfoSector = ((struct _BootSector32*) Boot)->FSInfoSector; 219 RtlCopyMemory(&FatInfo.VolumeLabel, &((struct _BootSector32*)Boot)->VolumeLabel, sizeof(FatInfo.VolumeLabel)); 220 } 221 else 222 { 223 DPRINT("FAT16\n"); 224 FatInfo.FatType = FAT16; 225 FatInfo.RootCluster = FatInfo.rootStart / FatInfo.SectorsPerCluster; 226 RtlCopyMemory(&FatInfo.VolumeLabel, &Boot->VolumeLabel, sizeof(FatInfo.VolumeLabel)); 227 } 228 229 if (PartitionInfoIsValid && 230 FatInfo.Sectors > PartitionInfo.PartitionLength.QuadPart / FatInfo.BytesPerSector) 231 { 232 *RecognizedFS = FALSE; 233 } 234 235 if (pFatInfo && *RecognizedFS) 236 { 237 *pFatInfo = FatInfo; 238 } 239 } 240 } 241 242 ExFreePoolWithTag(Boot, TAG_BUFFER); 243 } 244 245 if (!*RecognizedFS && PartitionInfoIsValid) 246 { 247 BootFatX = ExAllocatePoolWithTag(NonPagedPool, sizeof(struct _BootSectorFatX), TAG_BUFFER); 248 if (BootFatX == NULL) 249 { 250 *RecognizedFS=FALSE; 251 return STATUS_INSUFFICIENT_RESOURCES; 252 } 253 254 Offset.QuadPart = 0; 255 256 /* Try to recognize FATX16/FATX32 partitions (Xbox) */ 257 Status = VfatReadDisk(DeviceToMount, &Offset, sizeof(struct _BootSectorFatX), (PUCHAR) BootFatX, Override); 258 if (NT_SUCCESS(Status)) 259 { 260 *RecognizedFS = TRUE; 261 if (BootFatX->SysType[0] != 'F' || 262 BootFatX->SysType[1] != 'A' || 263 BootFatX->SysType[2] != 'T' || 264 BootFatX->SysType[3] != 'X') 265 { 266 DPRINT1("SysType %02X%02X%02X%02X (%c%c%c%c)\n", 267 BootFatX->SysType[0], BootFatX->SysType[1], BootFatX->SysType[2], BootFatX->SysType[3], 268 isprint(BootFatX->SysType[0]) ? BootFatX->SysType[0] : '.', 269 isprint(BootFatX->SysType[1]) ? BootFatX->SysType[1] : '.', 270 isprint(BootFatX->SysType[2]) ? BootFatX->SysType[2] : '.', 271 isprint(BootFatX->SysType[3]) ? BootFatX->SysType[3] : '.'); 272 273 *RecognizedFS = FALSE; 274 } 275 276 if (*RecognizedFS && 277 BootFatX->SectorsPerCluster != 1 && 278 BootFatX->SectorsPerCluster != 2 && 279 BootFatX->SectorsPerCluster != 4 && 280 BootFatX->SectorsPerCluster != 8 && 281 BootFatX->SectorsPerCluster != 16 && 282 BootFatX->SectorsPerCluster != 32 && 283 BootFatX->SectorsPerCluster != 64 && 284 BootFatX->SectorsPerCluster != 128) 285 { 286 DPRINT1("SectorsPerCluster %lu\n", BootFatX->SectorsPerCluster); 287 *RecognizedFS=FALSE; 288 } 289 290 if (*RecognizedFS) 291 { 292 FatInfo.BytesPerSector = DiskGeometry.BytesPerSector; 293 FatInfo.SectorsPerCluster = BootFatX->SectorsPerCluster; 294 FatInfo.rootDirectorySectors = BootFatX->SectorsPerCluster; 295 FatInfo.BytesPerCluster = BootFatX->SectorsPerCluster * DiskGeometry.BytesPerSector; 296 FatInfo.Sectors = (ULONG)(PartitionInfo.PartitionLength.QuadPart / DiskGeometry.BytesPerSector); 297 if (FatInfo.Sectors / FatInfo.SectorsPerCluster < 65525) 298 { 299 DPRINT("FATX16\n"); 300 FatInfo.FatType = FATX16; 301 } 302 else 303 { 304 DPRINT("FATX32\n"); 305 FatInfo.FatType = FATX32; 306 } 307 FatInfo.VolumeID = BootFatX->VolumeID; 308 FatInfo.FATStart = sizeof(struct _BootSectorFatX) / DiskGeometry.BytesPerSector; 309 FatInfo.FATCount = BootFatX->FATCount; 310 FatInfo.FATSectors = 311 ROUND_UP(FatInfo.Sectors / FatInfo.SectorsPerCluster * (FatInfo.FatType == FATX16 ? 2 : 4), 4096) / 312 FatInfo.BytesPerSector; 313 FatInfo.rootStart = FatInfo.FATStart + FatInfo.FATCount * FatInfo.FATSectors; 314 FatInfo.RootCluster = (FatInfo.rootStart - 1) / FatInfo.SectorsPerCluster; 315 FatInfo.dataStart = FatInfo.rootStart + FatInfo.rootDirectorySectors; 316 FatInfo.NumberOfClusters = (FatInfo.Sectors - FatInfo.dataStart) / FatInfo.SectorsPerCluster; 317 318 if (pFatInfo && *RecognizedFS) 319 { 320 *pFatInfo = FatInfo; 321 } 322 } 323 } 324 ExFreePoolWithTag(BootFatX, TAG_BUFFER); 325 } 326 327 DPRINT("VfatHasFileSystem done\n"); 328 return Status; 329 } 330 331 /* 332 * FUNCTION: Read the volume label 333 * WARNING: Read this comment carefully before using it (and using it wrong) 334 * Device parameter is expected to be the lower DO is start isn't 0 335 * otherwise, it is expected to be the VCB is start is 0 336 * Start parameter is expected to be, in bytes, the beginning of the root start. 337 * Set it to 0 if you wish to use the associated FCB with caching. 338 * In that specific case, Device parameter is expected to be the VCB! 339 * VolumeLabel parameter is expected to be a preallocated UNICODE_STRING (ie, with buffer) 340 * Its buffer has to be able to contain MAXIMUM_VOLUME_LABEL_LENGTH bytes 341 */ 342 static 343 NTSTATUS 344 ReadVolumeLabel( 345 PVOID Device, 346 ULONG Start, 347 BOOLEAN IsFatX, 348 PUNICODE_STRING VolumeLabel) 349 { 350 PDEVICE_EXTENSION DeviceExt; 351 PDEVICE_OBJECT DeviceObject; 352 PVOID Context = NULL; 353 ULONG DirIndex = 0; 354 PDIR_ENTRY Entry; 355 PVFATFCB pFcb; 356 LARGE_INTEGER FileOffset; 357 ULONG SizeDirEntry; 358 ULONG EntriesPerPage; 359 OEM_STRING StringO; 360 BOOLEAN NoCache = (Start != 0); 361 PVOID Buffer; 362 NTSTATUS Status = STATUS_SUCCESS; 363 364 if (IsFatX) 365 { 366 SizeDirEntry = sizeof(FATX_DIR_ENTRY); 367 EntriesPerPage = FATX_ENTRIES_PER_PAGE; 368 } 369 else 370 { 371 SizeDirEntry = sizeof(FAT_DIR_ENTRY); 372 EntriesPerPage = FAT_ENTRIES_PER_PAGE; 373 } 374 375 FileOffset.QuadPart = Start; 376 if (!NoCache) 377 { 378 DeviceExt = Device; 379 380 /* FIXME: Check we really have a VCB 381 ASSERT(); 382 */ 383 384 ExAcquireResourceExclusiveLite(&DeviceExt->DirResource, TRUE); 385 pFcb = vfatOpenRootFCB(DeviceExt); 386 ExReleaseResourceLite(&DeviceExt->DirResource); 387 388 _SEH2_TRY 389 { 390 CcMapData(pFcb->FileObject, &FileOffset, SizeDirEntry, MAP_WAIT, &Context, (PVOID*)&Entry); 391 } 392 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 393 { 394 Status = _SEH2_GetExceptionCode(); 395 } 396 _SEH2_END; 397 } 398 else 399 { 400 DeviceObject = Device; 401 402 ASSERT(DeviceObject->Type == 3); 403 404 Buffer = ExAllocatePoolWithTag(NonPagedPool, PAGE_SIZE, TAG_DIRENT); 405 if (Buffer != NULL) 406 { 407 Status = VfatReadDisk(DeviceObject, &FileOffset, PAGE_SIZE, (PUCHAR)Buffer, TRUE); 408 if (!NT_SUCCESS(Status)) 409 { 410 ExFreePoolWithTag(Buffer, TAG_DIRENT); 411 } 412 else 413 { 414 Entry = Buffer; 415 } 416 } 417 else 418 { 419 Status = STATUS_INSUFFICIENT_RESOURCES; 420 } 421 } 422 423 if (NT_SUCCESS(Status)) 424 { 425 while (TRUE) 426 { 427 if (ENTRY_VOLUME(IsFatX, Entry)) 428 { 429 /* copy volume label */ 430 if (IsFatX) 431 { 432 StringO.Buffer = (PCHAR)Entry->FatX.Filename; 433 StringO.MaximumLength = StringO.Length = Entry->FatX.FilenameLength; 434 RtlOemStringToUnicodeString(VolumeLabel, &StringO, FALSE); 435 } 436 else 437 { 438 vfat8Dot3ToString(&Entry->Fat, VolumeLabel); 439 } 440 break; 441 } 442 if (ENTRY_END(IsFatX, Entry)) 443 { 444 break; 445 } 446 DirIndex++; 447 Entry = (PDIR_ENTRY)((ULONG_PTR)Entry + SizeDirEntry); 448 if ((DirIndex % EntriesPerPage) == 0) 449 { 450 FileOffset.u.LowPart += PAGE_SIZE; 451 452 if (!NoCache) 453 { 454 CcUnpinData(Context); 455 456 _SEH2_TRY 457 { 458 CcMapData(pFcb->FileObject, &FileOffset, SizeDirEntry, MAP_WAIT, &Context, (PVOID*)&Entry); 459 } 460 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 461 { 462 Status = _SEH2_GetExceptionCode(); 463 } 464 _SEH2_END; 465 if (!NT_SUCCESS(Status)) 466 { 467 Context = NULL; 468 break; 469 } 470 } 471 else 472 { 473 Status = VfatReadDisk(DeviceObject, &FileOffset, PAGE_SIZE, (PUCHAR)Buffer, TRUE); 474 if (!NT_SUCCESS(Status)) 475 { 476 break; 477 } 478 Entry = Buffer; 479 } 480 } 481 } 482 if (Context) 483 { 484 CcUnpinData(Context); 485 } 486 else if (NoCache) 487 { 488 ExFreePoolWithTag(Buffer, TAG_DIRENT); 489 } 490 } 491 492 if (!NoCache) 493 { 494 ExAcquireResourceExclusiveLite(&DeviceExt->DirResource, TRUE); 495 vfatReleaseFCB(DeviceExt, pFcb); 496 ExReleaseResourceLite(&DeviceExt->DirResource); 497 } 498 499 return STATUS_SUCCESS; 500 } 501 502 503 /* 504 * FUNCTION: Mount the filesystem 505 */ 506 static 507 NTSTATUS 508 VfatMount( 509 PVFAT_IRP_CONTEXT IrpContext) 510 { 511 PDEVICE_OBJECT DeviceObject = NULL; 512 PDEVICE_EXTENSION DeviceExt = NULL; 513 BOOLEAN RecognizedFS; 514 NTSTATUS Status; 515 PVFATFCB Fcb = NULL; 516 PVFATFCB VolumeFcb = NULL; 517 PDEVICE_OBJECT DeviceToMount; 518 PVPB Vpb; 519 UNICODE_STRING NameU = RTL_CONSTANT_STRING(L"\\$$Fat$$"); 520 UNICODE_STRING VolumeNameU = RTL_CONSTANT_STRING(L"\\$$Volume$$"); 521 UNICODE_STRING VolumeLabelU; 522 ULONG HashTableSize; 523 ULONG i; 524 FATINFO FatInfo; 525 BOOLEAN Dirty; 526 527 DPRINT("VfatMount(IrpContext %p)\n", IrpContext); 528 529 ASSERT(IrpContext); 530 531 if (IrpContext->DeviceObject != VfatGlobalData->DeviceObject) 532 { 533 Status = STATUS_INVALID_DEVICE_REQUEST; 534 goto ByeBye; 535 } 536 537 DeviceToMount = IrpContext->Stack->Parameters.MountVolume.DeviceObject; 538 Vpb = IrpContext->Stack->Parameters.MountVolume.Vpb; 539 540 Status = VfatHasFileSystem(DeviceToMount, &RecognizedFS, &FatInfo, FALSE); 541 if (!NT_SUCCESS(Status)) 542 { 543 goto ByeBye; 544 } 545 546 if (RecognizedFS == FALSE) 547 { 548 DPRINT("VFAT: Unrecognized Volume\n"); 549 Status = STATUS_UNRECOGNIZED_VOLUME; 550 goto ByeBye; 551 } 552 553 /* Use prime numbers for the table size */ 554 if (FatInfo.FatType == FAT12) 555 { 556 HashTableSize = 4099; // 4096 = 4 * 1024 557 } 558 else if (FatInfo.FatType == FAT16 || 559 FatInfo.FatType == FATX16) 560 { 561 HashTableSize = 16411; // 16384 = 16 * 1024 562 } 563 else 564 { 565 HashTableSize = 65537; // 65536 = 64 * 1024; 566 } 567 DPRINT("VFAT: Recognized volume\n"); 568 Status = IoCreateDevice(VfatGlobalData->DriverObject, 569 ROUND_UP(sizeof (DEVICE_EXTENSION), sizeof(ULONG)) + sizeof(HASHENTRY*) * HashTableSize, 570 NULL, 571 FILE_DEVICE_DISK_FILE_SYSTEM, 572 DeviceToMount->Characteristics, 573 FALSE, 574 &DeviceObject); 575 if (!NT_SUCCESS(Status)) 576 { 577 goto ByeBye; 578 } 579 580 DeviceExt = DeviceObject->DeviceExtension; 581 RtlZeroMemory(DeviceExt, ROUND_UP(sizeof(DEVICE_EXTENSION), sizeof(ULONG)) + sizeof(HASHENTRY*) * HashTableSize); 582 DeviceExt->FcbHashTable = (HASHENTRY**)((ULONG_PTR)DeviceExt + ROUND_UP(sizeof(DEVICE_EXTENSION), sizeof(ULONG))); 583 DeviceExt->HashTableSize = HashTableSize; 584 DeviceExt->VolumeDevice = DeviceObject; 585 586 KeInitializeSpinLock(&DeviceExt->OverflowQueueSpinLock); 587 InitializeListHead(&DeviceExt->OverflowQueue); 588 DeviceExt->OverflowQueueCount = 0; 589 DeviceExt->PostedRequestCount = 0; 590 591 /* use same vpb as device disk */ 592 DeviceObject->Vpb = Vpb; 593 DeviceToMount->Vpb = Vpb; 594 595 RtlCopyMemory(&DeviceExt->FatInfo, &FatInfo, sizeof(FATINFO)); 596 597 DPRINT("BytesPerSector: %u\n", DeviceExt->FatInfo.BytesPerSector); 598 DPRINT("SectorsPerCluster: %u\n", DeviceExt->FatInfo.SectorsPerCluster); 599 DPRINT("FATCount: %u\n", DeviceExt->FatInfo.FATCount); 600 DPRINT("FATSectors: %u\n", DeviceExt->FatInfo.FATSectors); 601 DPRINT("RootStart: %u\n", DeviceExt->FatInfo.rootStart); 602 DPRINT("DataStart: %u\n", DeviceExt->FatInfo.dataStart); 603 if (DeviceExt->FatInfo.FatType == FAT32) 604 { 605 DPRINT("RootCluster: %u\n", DeviceExt->FatInfo.RootCluster); 606 } 607 608 switch (DeviceExt->FatInfo.FatType) 609 { 610 case FAT12: 611 DeviceExt->GetNextCluster = FAT12GetNextCluster; 612 DeviceExt->FindAndMarkAvailableCluster = FAT12FindAndMarkAvailableCluster; 613 DeviceExt->WriteCluster = FAT12WriteCluster; 614 /* We don't define dirty bit functions here 615 * FAT12 doesn't have such bit and they won't get called 616 */ 617 break; 618 619 case FAT16: 620 case FATX16: 621 DeviceExt->GetNextCluster = FAT16GetNextCluster; 622 DeviceExt->FindAndMarkAvailableCluster = FAT16FindAndMarkAvailableCluster; 623 DeviceExt->WriteCluster = FAT16WriteCluster; 624 DeviceExt->GetDirtyStatus = FAT16GetDirtyStatus; 625 DeviceExt->SetDirtyStatus = FAT16SetDirtyStatus; 626 break; 627 628 case FAT32: 629 case FATX32: 630 DeviceExt->GetNextCluster = FAT32GetNextCluster; 631 DeviceExt->FindAndMarkAvailableCluster = FAT32FindAndMarkAvailableCluster; 632 DeviceExt->WriteCluster = FAT32WriteCluster; 633 DeviceExt->GetDirtyStatus = FAT32GetDirtyStatus; 634 DeviceExt->SetDirtyStatus = FAT32SetDirtyStatus; 635 break; 636 } 637 638 if (DeviceExt->FatInfo.FatType == FATX16 || 639 DeviceExt->FatInfo.FatType == FATX32) 640 { 641 DeviceExt->Flags |= VCB_IS_FATX; 642 DeviceExt->BaseDateYear = 2000; 643 RtlCopyMemory(&DeviceExt->Dispatch, &FatXDispatch, sizeof(VFAT_DISPATCH)); 644 } 645 else 646 { 647 DeviceExt->BaseDateYear = 1980; 648 RtlCopyMemory(&DeviceExt->Dispatch, &FatDispatch, sizeof(VFAT_DISPATCH)); 649 } 650 651 DeviceExt->StorageDevice = DeviceToMount; 652 DeviceExt->StorageDevice->Vpb->DeviceObject = DeviceObject; 653 DeviceExt->StorageDevice->Vpb->RealDevice = DeviceExt->StorageDevice; 654 DeviceExt->StorageDevice->Vpb->Flags |= VPB_MOUNTED; 655 DeviceObject->StackSize = DeviceExt->StorageDevice->StackSize + 1; 656 DeviceObject->Flags &= ~DO_DEVICE_INITIALIZING; 657 658 DPRINT("FsDeviceObject %p\n", DeviceObject); 659 660 /* Initialize this resource early ... it's used in VfatCleanup */ 661 ExInitializeResourceLite(&DeviceExt->DirResource); 662 663 DeviceExt->IoVPB = DeviceObject->Vpb; 664 DeviceExt->SpareVPB = ExAllocatePoolWithTag(NonPagedPool, sizeof(VPB), TAG_VPB); 665 if (DeviceExt->SpareVPB == NULL) 666 { 667 Status = STATUS_INSUFFICIENT_RESOURCES; 668 goto ByeBye; 669 } 670 671 DeviceExt->Statistics = ExAllocatePoolWithTag(NonPagedPool, 672 sizeof(STATISTICS) * VfatGlobalData->NumberProcessors, 673 TAG_STATS); 674 if (DeviceExt->Statistics == NULL) 675 { 676 Status = STATUS_INSUFFICIENT_RESOURCES; 677 goto ByeBye; 678 } 679 680 RtlZeroMemory(DeviceExt->Statistics, sizeof(STATISTICS) * VfatGlobalData->NumberProcessors); 681 for (i = 0; i < VfatGlobalData->NumberProcessors; ++i) 682 { 683 DeviceExt->Statistics[i].Base.FileSystemType = FILESYSTEM_STATISTICS_TYPE_FAT; 684 DeviceExt->Statistics[i].Base.Version = 1; 685 DeviceExt->Statistics[i].Base.SizeOfCompleteStructure = sizeof(STATISTICS); 686 } 687 688 DeviceExt->FATFileObject = IoCreateStreamFileObject(NULL, DeviceExt->StorageDevice); 689 Fcb = vfatNewFCB(DeviceExt, &NameU); 690 if (Fcb == NULL) 691 { 692 Status = STATUS_INSUFFICIENT_RESOURCES; 693 goto ByeBye; 694 } 695 696 Status = vfatAttachFCBToFileObject(DeviceExt, Fcb, DeviceExt->FATFileObject); 697 if (!NT_SUCCESS(Status)) 698 goto ByeBye; 699 700 DeviceExt->FATFileObject->PrivateCacheMap = NULL; 701 Fcb->FileObject = DeviceExt->FATFileObject; 702 703 Fcb->Flags = FCB_IS_FAT; 704 Fcb->RFCB.FileSize.QuadPart = DeviceExt->FatInfo.FATSectors * DeviceExt->FatInfo.BytesPerSector; 705 Fcb->RFCB.ValidDataLength = Fcb->RFCB.FileSize; 706 Fcb->RFCB.AllocationSize = Fcb->RFCB.FileSize; 707 708 _SEH2_TRY 709 { 710 CcInitializeCacheMap(DeviceExt->FATFileObject, 711 (PCC_FILE_SIZES)(&Fcb->RFCB.AllocationSize), 712 TRUE, 713 &VfatGlobalData->CacheMgrCallbacks, 714 Fcb); 715 } 716 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 717 { 718 Status = _SEH2_GetExceptionCode(); 719 goto ByeBye; 720 } 721 _SEH2_END; 722 723 DeviceExt->LastAvailableCluster = 2; 724 CountAvailableClusters(DeviceExt, NULL); 725 ExInitializeResourceLite(&DeviceExt->FatResource); 726 727 InitializeListHead(&DeviceExt->FcbListHead); 728 729 VolumeFcb = vfatNewFCB(DeviceExt, &VolumeNameU); 730 if (VolumeFcb == NULL) 731 { 732 Status = STATUS_INSUFFICIENT_RESOURCES; 733 goto ByeBye; 734 } 735 736 VolumeFcb->Flags = FCB_IS_VOLUME; 737 VolumeFcb->RFCB.FileSize.QuadPart = (LONGLONG) DeviceExt->FatInfo.Sectors * DeviceExt->FatInfo.BytesPerSector; 738 VolumeFcb->RFCB.ValidDataLength = VolumeFcb->RFCB.FileSize; 739 VolumeFcb->RFCB.AllocationSize = VolumeFcb->RFCB.FileSize; 740 DeviceExt->VolumeFcb = VolumeFcb; 741 742 ExAcquireResourceExclusiveLite(&VfatGlobalData->VolumeListLock, TRUE); 743 InsertHeadList(&VfatGlobalData->VolumeListHead, &DeviceExt->VolumeListEntry); 744 ExReleaseResourceLite(&VfatGlobalData->VolumeListLock); 745 746 /* read serial number */ 747 DeviceObject->Vpb->SerialNumber = DeviceExt->FatInfo.VolumeID; 748 749 /* read volume label */ 750 VolumeLabelU.Buffer = DeviceObject->Vpb->VolumeLabel; 751 VolumeLabelU.Length = 0; 752 VolumeLabelU.MaximumLength = sizeof(DeviceObject->Vpb->VolumeLabel); 753 ReadVolumeLabel(DeviceExt, 0, vfatVolumeIsFatX(DeviceExt), &VolumeLabelU); 754 Vpb->VolumeLabelLength = VolumeLabelU.Length; 755 756 /* read dirty bit status */ 757 Status = GetDirtyStatus(DeviceExt, &Dirty); 758 if (NT_SUCCESS(Status)) 759 { 760 /* The volume wasn't dirty, it was properly dismounted */ 761 if (!Dirty) 762 { 763 /* Mark it dirty now! */ 764 SetDirtyStatus(DeviceExt, TRUE); 765 VolumeFcb->Flags |= VCB_CLEAR_DIRTY; 766 } 767 else 768 { 769 DPRINT1("Mounting a dirty volume\n"); 770 } 771 } 772 773 VolumeFcb->Flags |= VCB_IS_DIRTY; 774 if (BooleanFlagOn(Vpb->RealDevice->Flags, DO_SYSTEM_BOOT_PARTITION)) 775 { 776 SetFlag(DeviceExt->Flags, VCB_IS_SYS_OR_HAS_PAGE); 777 } 778 779 /* Initialize the notify list and synchronization object */ 780 InitializeListHead(&DeviceExt->NotifyList); 781 FsRtlNotifyInitializeSync(&DeviceExt->NotifySync); 782 783 /* The VCB is OK for usage */ 784 SetFlag(DeviceExt->Flags, VCB_GOOD); 785 786 /* Send the mount notification */ 787 FsRtlNotifyVolumeEvent(DeviceExt->FATFileObject, FSRTL_VOLUME_MOUNT); 788 789 DPRINT("Mount success\n"); 790 791 Status = STATUS_SUCCESS; 792 793 ByeBye: 794 if (!NT_SUCCESS(Status)) 795 { 796 /* Cleanup */ 797 if (DeviceExt && DeviceExt->FATFileObject) 798 { 799 LARGE_INTEGER Zero = {{0,0}}; 800 PVFATCCB Ccb = (PVFATCCB)DeviceExt->FATFileObject->FsContext2; 801 802 CcUninitializeCacheMap(DeviceExt->FATFileObject, 803 &Zero, 804 NULL); 805 ObDereferenceObject(DeviceExt->FATFileObject); 806 if (Ccb) 807 vfatDestroyCCB(Ccb); 808 DeviceExt->FATFileObject = NULL; 809 } 810 if (Fcb) 811 vfatDestroyFCB(Fcb); 812 if (DeviceExt && DeviceExt->SpareVPB) 813 ExFreePoolWithTag(DeviceExt->SpareVPB, TAG_VPB); 814 if (DeviceExt && DeviceExt->Statistics) 815 ExFreePoolWithTag(DeviceExt->Statistics, TAG_STATS); 816 if (DeviceObject) 817 IoDeleteDevice(DeviceObject); 818 } 819 820 return Status; 821 } 822 823 824 /* 825 * FUNCTION: Verify the filesystem 826 */ 827 static 828 NTSTATUS 829 VfatVerify( 830 PVFAT_IRP_CONTEXT IrpContext) 831 { 832 PDEVICE_OBJECT DeviceToVerify; 833 NTSTATUS Status; 834 FATINFO FatInfo; 835 BOOLEAN RecognizedFS; 836 PDEVICE_EXTENSION DeviceExt; 837 BOOLEAN AllowRaw; 838 PVPB Vpb; 839 ULONG ChangeCount, BufSize = sizeof(ChangeCount); 840 841 DPRINT("VfatVerify(IrpContext %p)\n", IrpContext); 842 843 DeviceToVerify = IrpContext->Stack->Parameters.VerifyVolume.DeviceObject; 844 DeviceExt = DeviceToVerify->DeviceExtension; 845 Vpb = IrpContext->Stack->Parameters.VerifyVolume.Vpb; 846 AllowRaw = BooleanFlagOn(IrpContext->Stack->Flags, SL_ALLOW_RAW_MOUNT); 847 848 if (!BooleanFlagOn(Vpb->RealDevice->Flags, DO_VERIFY_VOLUME)) 849 { 850 DPRINT("Already verified\n"); 851 return STATUS_SUCCESS; 852 } 853 854 Status = VfatBlockDeviceIoControl(DeviceExt->StorageDevice, 855 IOCTL_DISK_CHECK_VERIFY, 856 NULL, 857 0, 858 &ChangeCount, 859 &BufSize, 860 TRUE); 861 if (!NT_SUCCESS(Status) && Status != STATUS_VERIFY_REQUIRED) 862 { 863 DPRINT("VfatBlockDeviceIoControl() failed (Status %lx)\n", Status); 864 Status = (AllowRaw ? STATUS_WRONG_VOLUME : Status); 865 } 866 else 867 { 868 Status = VfatHasFileSystem(DeviceExt->StorageDevice, &RecognizedFS, &FatInfo, TRUE); 869 if (!NT_SUCCESS(Status) || RecognizedFS == FALSE) 870 { 871 if (NT_SUCCESS(Status) || AllowRaw) 872 { 873 Status = STATUS_WRONG_VOLUME; 874 } 875 } 876 else if (sizeof(FATINFO) == RtlCompareMemory(&FatInfo, &DeviceExt->FatInfo, sizeof(FATINFO))) 877 { 878 WCHAR BufferU[MAXIMUM_VOLUME_LABEL_LENGTH / sizeof(WCHAR)]; 879 UNICODE_STRING VolumeLabelU; 880 UNICODE_STRING VpbLabelU; 881 882 VolumeLabelU.Buffer = BufferU; 883 VolumeLabelU.Length = 0; 884 VolumeLabelU.MaximumLength = sizeof(BufferU); 885 Status = ReadVolumeLabel(DeviceExt->StorageDevice, FatInfo.rootStart * FatInfo.BytesPerSector, (FatInfo.FatType >= FATX16), &VolumeLabelU); 886 if (!NT_SUCCESS(Status)) 887 { 888 if (AllowRaw) 889 { 890 Status = STATUS_WRONG_VOLUME; 891 } 892 } 893 else 894 { 895 VpbLabelU.Buffer = Vpb->VolumeLabel; 896 VpbLabelU.Length = Vpb->VolumeLabelLength; 897 VpbLabelU.MaximumLength = sizeof(Vpb->VolumeLabel); 898 899 if (RtlCompareUnicodeString(&VpbLabelU, &VolumeLabelU, FALSE) != 0) 900 { 901 Status = STATUS_WRONG_VOLUME; 902 } 903 else 904 { 905 DPRINT1("Same volume\n"); 906 } 907 } 908 } 909 else 910 { 911 Status = STATUS_WRONG_VOLUME; 912 } 913 } 914 915 Vpb->RealDevice->Flags &= ~DO_VERIFY_VOLUME; 916 917 return Status; 918 } 919 920 921 static 922 NTSTATUS 923 VfatGetVolumeBitmap( 924 PVFAT_IRP_CONTEXT IrpContext) 925 { 926 DPRINT("VfatGetVolumeBitmap (IrpContext %p)\n", IrpContext); 927 return STATUS_INVALID_DEVICE_REQUEST; 928 } 929 930 931 static 932 NTSTATUS 933 VfatGetRetrievalPointers( 934 PVFAT_IRP_CONTEXT IrpContext) 935 { 936 PIO_STACK_LOCATION Stack; 937 LARGE_INTEGER Vcn; 938 PRETRIEVAL_POINTERS_BUFFER RetrievalPointers; 939 PFILE_OBJECT FileObject; 940 ULONG MaxExtentCount; 941 PVFATFCB Fcb; 942 PDEVICE_EXTENSION DeviceExt; 943 ULONG FirstCluster; 944 ULONG CurrentCluster; 945 ULONG LastCluster; 946 NTSTATUS Status; 947 948 DPRINT("VfatGetRetrievalPointers(IrpContext %p)\n", IrpContext); 949 950 DeviceExt = IrpContext->DeviceExt; 951 FileObject = IrpContext->FileObject; 952 Stack = IrpContext->Stack; 953 if (Stack->Parameters.DeviceIoControl.InputBufferLength < sizeof(STARTING_VCN_INPUT_BUFFER) || 954 Stack->Parameters.DeviceIoControl.Type3InputBuffer == NULL) 955 { 956 return STATUS_INVALID_PARAMETER; 957 } 958 959 if (IrpContext->Irp->UserBuffer == NULL || 960 Stack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(RETRIEVAL_POINTERS_BUFFER)) 961 { 962 return STATUS_BUFFER_TOO_SMALL; 963 } 964 965 Fcb = FileObject->FsContext; 966 967 ExAcquireResourceSharedLite(&Fcb->MainResource, TRUE); 968 969 Vcn = ((PSTARTING_VCN_INPUT_BUFFER)Stack->Parameters.DeviceIoControl.Type3InputBuffer)->StartingVcn; 970 RetrievalPointers = IrpContext->Irp->UserBuffer; 971 972 MaxExtentCount = ((Stack->Parameters.DeviceIoControl.OutputBufferLength - sizeof(RetrievalPointers->ExtentCount) - sizeof(RetrievalPointers->StartingVcn)) / sizeof(RetrievalPointers->Extents[0])); 973 974 if (Vcn.QuadPart >= Fcb->RFCB.AllocationSize.QuadPart / DeviceExt->FatInfo.BytesPerCluster) 975 { 976 Status = STATUS_INVALID_PARAMETER; 977 goto ByeBye; 978 } 979 980 CurrentCluster = FirstCluster = vfatDirEntryGetFirstCluster(DeviceExt, &Fcb->entry); 981 Status = OffsetToCluster(DeviceExt, FirstCluster, 982 Vcn.u.LowPart * DeviceExt->FatInfo.BytesPerCluster, 983 &CurrentCluster, FALSE); 984 if (!NT_SUCCESS(Status)) 985 { 986 goto ByeBye; 987 } 988 989 RetrievalPointers->StartingVcn = Vcn; 990 RetrievalPointers->ExtentCount = 0; 991 RetrievalPointers->Extents[0].Lcn.u.HighPart = 0; 992 RetrievalPointers->Extents[0].Lcn.u.LowPart = CurrentCluster - 2; 993 LastCluster = 0; 994 while (CurrentCluster != 0xffffffff && RetrievalPointers->ExtentCount < MaxExtentCount) 995 { 996 LastCluster = CurrentCluster; 997 Status = NextCluster(DeviceExt, CurrentCluster, &CurrentCluster, FALSE); 998 Vcn.QuadPart++; 999 if (!NT_SUCCESS(Status)) 1000 { 1001 goto ByeBye; 1002 } 1003 1004 if (LastCluster + 1 != CurrentCluster) 1005 { 1006 RetrievalPointers->Extents[RetrievalPointers->ExtentCount].NextVcn = Vcn; 1007 RetrievalPointers->ExtentCount++; 1008 if (RetrievalPointers->ExtentCount < MaxExtentCount) 1009 { 1010 RetrievalPointers->Extents[RetrievalPointers->ExtentCount].Lcn.u.HighPart = 0; 1011 RetrievalPointers->Extents[RetrievalPointers->ExtentCount].Lcn.u.LowPart = CurrentCluster - 2; 1012 } 1013 } 1014 } 1015 1016 IrpContext->Irp->IoStatus.Information = sizeof(RETRIEVAL_POINTERS_BUFFER) + (sizeof(RetrievalPointers->Extents[0]) * (RetrievalPointers->ExtentCount - 1)); 1017 Status = STATUS_SUCCESS; 1018 1019 ByeBye: 1020 ExReleaseResourceLite(&Fcb->MainResource); 1021 1022 return Status; 1023 } 1024 1025 static 1026 NTSTATUS 1027 VfatMoveFile( 1028 PVFAT_IRP_CONTEXT IrpContext) 1029 { 1030 DPRINT("VfatMoveFile(IrpContext %p)\n", IrpContext); 1031 return STATUS_INVALID_DEVICE_REQUEST; 1032 } 1033 1034 static 1035 NTSTATUS 1036 VfatIsVolumeDirty( 1037 PVFAT_IRP_CONTEXT IrpContext) 1038 { 1039 PULONG Flags; 1040 1041 DPRINT("VfatIsVolumeDirty(IrpContext %p)\n", IrpContext); 1042 1043 if (IrpContext->Stack->Parameters.FileSystemControl.OutputBufferLength != sizeof(ULONG)) 1044 return STATUS_INVALID_BUFFER_SIZE; 1045 else if (!IrpContext->Irp->AssociatedIrp.SystemBuffer) 1046 return STATUS_INVALID_USER_BUFFER; 1047 1048 Flags = (PULONG)IrpContext->Irp->AssociatedIrp.SystemBuffer; 1049 *Flags = 0; 1050 1051 if (BooleanFlagOn(IrpContext->DeviceExt->VolumeFcb->Flags, VCB_IS_DIRTY) && 1052 !BooleanFlagOn(IrpContext->DeviceExt->VolumeFcb->Flags, VCB_CLEAR_DIRTY)) 1053 { 1054 *Flags |= VOLUME_IS_DIRTY; 1055 } 1056 1057 IrpContext->Irp->IoStatus.Information = sizeof(ULONG); 1058 1059 return STATUS_SUCCESS; 1060 } 1061 1062 static 1063 NTSTATUS 1064 VfatMarkVolumeDirty( 1065 PVFAT_IRP_CONTEXT IrpContext) 1066 { 1067 PDEVICE_EXTENSION DeviceExt; 1068 NTSTATUS Status = STATUS_SUCCESS; 1069 1070 DPRINT("VfatMarkVolumeDirty(IrpContext %p)\n", IrpContext); 1071 DeviceExt = IrpContext->DeviceExt; 1072 1073 if (!BooleanFlagOn(DeviceExt->VolumeFcb->Flags, VCB_IS_DIRTY)) 1074 { 1075 Status = SetDirtyStatus(DeviceExt, TRUE); 1076 } 1077 1078 DeviceExt->VolumeFcb->Flags &= ~VCB_CLEAR_DIRTY; 1079 1080 return Status; 1081 } 1082 1083 static 1084 NTSTATUS 1085 VfatLockOrUnlockVolume( 1086 PVFAT_IRP_CONTEXT IrpContext, 1087 BOOLEAN Lock) 1088 { 1089 PFILE_OBJECT FileObject; 1090 PDEVICE_EXTENSION DeviceExt; 1091 PVFATFCB Fcb; 1092 PVPB Vpb; 1093 1094 DPRINT("VfatLockOrUnlockVolume(%p, %d)\n", IrpContext, Lock); 1095 1096 DeviceExt = IrpContext->DeviceExt; 1097 FileObject = IrpContext->FileObject; 1098 Fcb = FileObject->FsContext; 1099 Vpb = DeviceExt->FATFileObject->Vpb; 1100 1101 /* Only allow locking with the volume open */ 1102 if (!BooleanFlagOn(Fcb->Flags, FCB_IS_VOLUME)) 1103 { 1104 return STATUS_ACCESS_DENIED; 1105 } 1106 1107 /* Bail out if it's already in the demanded state */ 1108 if ((BooleanFlagOn(DeviceExt->Flags, VCB_VOLUME_LOCKED) && Lock) || 1109 (!BooleanFlagOn(DeviceExt->Flags, VCB_VOLUME_LOCKED) && !Lock)) 1110 { 1111 return STATUS_ACCESS_DENIED; 1112 } 1113 1114 /* Bail out if it's already in the demanded state */ 1115 if ((BooleanFlagOn(Vpb->Flags, VPB_LOCKED) && Lock) || 1116 (!BooleanFlagOn(Vpb->Flags, VPB_LOCKED) && !Lock)) 1117 { 1118 return STATUS_ACCESS_DENIED; 1119 } 1120 1121 if (Lock) 1122 { 1123 FsRtlNotifyVolumeEvent(IrpContext->Stack->FileObject, FSRTL_VOLUME_LOCK); 1124 } 1125 1126 /* Deny locking if we're not alone */ 1127 if (Lock && DeviceExt->OpenHandleCount != 1) 1128 { 1129 PLIST_ENTRY ListEntry; 1130 1131 #if 1 1132 /* FIXME: Hack that allows locking the system volume on 1133 * boot so that autochk can run properly 1134 * That hack is, on purpose, really restrictive 1135 * it will only allow locking with two directories 1136 * open: current directory of smss and autochk. 1137 */ 1138 BOOLEAN ForceLock = TRUE; 1139 ULONG HandleCount = 0; 1140 1141 /* Only allow boot volume */ 1142 if (BooleanFlagOn(DeviceExt->Flags, VCB_IS_SYS_OR_HAS_PAGE)) 1143 { 1144 /* We'll browse all the FCB */ 1145 ListEntry = DeviceExt->FcbListHead.Flink; 1146 while (ListEntry != &DeviceExt->FcbListHead) 1147 { 1148 Fcb = CONTAINING_RECORD(ListEntry, VFATFCB, FcbListEntry); 1149 ListEntry = ListEntry->Flink; 1150 1151 /* If no handle: that FCB is no problem for locking 1152 * so ignore it 1153 */ 1154 if (Fcb->OpenHandleCount == 0) 1155 { 1156 continue; 1157 } 1158 1159 /* Not a dir? We're no longer at boot */ 1160 if (!vfatFCBIsDirectory(Fcb)) 1161 { 1162 ForceLock = FALSE; 1163 break; 1164 } 1165 1166 /* If we have cached initialized and several handles, we're 1167 not in the boot case 1168 */ 1169 if (Fcb->FileObject != NULL && Fcb->OpenHandleCount > 1) 1170 { 1171 ForceLock = FALSE; 1172 break; 1173 } 1174 1175 /* Count the handles */ 1176 HandleCount += Fcb->OpenHandleCount; 1177 /* More than two handles? Then, we're not booting anymore */ 1178 if (HandleCount > 2) 1179 { 1180 ForceLock = FALSE; 1181 break; 1182 } 1183 } 1184 } 1185 else 1186 { 1187 ForceLock = FALSE; 1188 } 1189 1190 /* Here comes the hack, ignore the failure! */ 1191 if (!ForceLock) 1192 { 1193 #endif 1194 1195 DPRINT1("Can't lock: %u opened\n", DeviceExt->OpenHandleCount); 1196 1197 ListEntry = DeviceExt->FcbListHead.Flink; 1198 while (ListEntry != &DeviceExt->FcbListHead) 1199 { 1200 Fcb = CONTAINING_RECORD(ListEntry, VFATFCB, FcbListEntry); 1201 ListEntry = ListEntry->Flink; 1202 1203 if (Fcb->OpenHandleCount > 0) 1204 { 1205 DPRINT1("Opened (%u - %u): %wZ\n", Fcb->OpenHandleCount, Fcb->RefCount, &Fcb->PathNameU); 1206 } 1207 } 1208 1209 FsRtlNotifyVolumeEvent(IrpContext->Stack->FileObject, FSRTL_VOLUME_LOCK_FAILED); 1210 1211 return STATUS_ACCESS_DENIED; 1212 1213 #if 1 1214 /* End of the hack: be verbose about its usage, 1215 * just in case we would mess up everything! 1216 */ 1217 } 1218 else 1219 { 1220 DPRINT1("HACK: Using lock-hack!\n"); 1221 } 1222 #endif 1223 } 1224 1225 /* Finally, proceed */ 1226 if (Lock) 1227 { 1228 /* Flush volume & files */ 1229 VfatFlushVolume(DeviceExt, DeviceExt->VolumeFcb); 1230 1231 /* The volume is now clean */ 1232 if (BooleanFlagOn(DeviceExt->VolumeFcb->Flags, VCB_CLEAR_DIRTY) && 1233 BooleanFlagOn(DeviceExt->VolumeFcb->Flags, VCB_IS_DIRTY)) 1234 { 1235 /* Drop the dirty bit */ 1236 if (NT_SUCCESS(SetDirtyStatus(DeviceExt, FALSE))) 1237 ClearFlag(DeviceExt->VolumeFcb->Flags, VCB_IS_DIRTY); 1238 } 1239 1240 DeviceExt->Flags |= VCB_VOLUME_LOCKED; 1241 Vpb->Flags |= VPB_LOCKED; 1242 } 1243 else 1244 { 1245 DeviceExt->Flags &= ~VCB_VOLUME_LOCKED; 1246 Vpb->Flags &= ~VPB_LOCKED; 1247 1248 FsRtlNotifyVolumeEvent(IrpContext->Stack->FileObject, FSRTL_VOLUME_UNLOCK); 1249 } 1250 1251 return STATUS_SUCCESS; 1252 } 1253 1254 static 1255 NTSTATUS 1256 VfatDismountVolume( 1257 PVFAT_IRP_CONTEXT IrpContext) 1258 { 1259 PDEVICE_EXTENSION DeviceExt; 1260 PLIST_ENTRY NextEntry; 1261 PVFATFCB Fcb; 1262 PFILE_OBJECT FileObject; 1263 1264 DPRINT("VfatDismountVolume(%p)\n", IrpContext); 1265 1266 DeviceExt = IrpContext->DeviceExt; 1267 FileObject = IrpContext->FileObject; 1268 1269 /* We HAVE to be locked. Windows also allows dismount with no lock 1270 * but we're here mainly for 1st stage, so KISS 1271 */ 1272 if (!BooleanFlagOn(DeviceExt->Flags, VCB_VOLUME_LOCKED)) 1273 { 1274 return STATUS_ACCESS_DENIED; 1275 } 1276 1277 /* Deny dismount of boot volume */ 1278 if (BooleanFlagOn(DeviceExt->Flags, VCB_IS_SYS_OR_HAS_PAGE)) 1279 { 1280 return STATUS_ACCESS_DENIED; 1281 } 1282 1283 /* Race condition? */ 1284 if (BooleanFlagOn(DeviceExt->Flags, VCB_DISMOUNT_PENDING)) 1285 { 1286 return STATUS_VOLUME_DISMOUNTED; 1287 } 1288 1289 /* Notify we'll dismount. Pass that point there's no reason we fail */ 1290 FsRtlNotifyVolumeEvent(IrpContext->Stack->FileObject, FSRTL_VOLUME_DISMOUNT); 1291 1292 ExAcquireResourceExclusiveLite(&DeviceExt->FatResource, TRUE); 1293 1294 /* Flush volume & files */ 1295 VfatFlushVolume(DeviceExt, (PVFATFCB)FileObject->FsContext); 1296 1297 /* The volume is now clean */ 1298 if (BooleanFlagOn(DeviceExt->VolumeFcb->Flags, VCB_CLEAR_DIRTY) && 1299 BooleanFlagOn(DeviceExt->VolumeFcb->Flags, VCB_IS_DIRTY)) 1300 { 1301 /* Drop the dirty bit */ 1302 if (NT_SUCCESS(SetDirtyStatus(DeviceExt, FALSE))) 1303 DeviceExt->VolumeFcb->Flags &= ~VCB_IS_DIRTY; 1304 } 1305 1306 /* Rebrowse the FCB in order to free them now */ 1307 while (!IsListEmpty(&DeviceExt->FcbListHead)) 1308 { 1309 NextEntry = RemoveTailList(&DeviceExt->FcbListHead); 1310 Fcb = CONTAINING_RECORD(NextEntry, VFATFCB, FcbListEntry); 1311 1312 if (Fcb == DeviceExt->RootFcb) 1313 DeviceExt->RootFcb = NULL; 1314 else if (Fcb == DeviceExt->VolumeFcb) 1315 DeviceExt->VolumeFcb = NULL; 1316 1317 vfatDestroyFCB(Fcb); 1318 } 1319 1320 /* We are uninitializing, the VCB cannot be used anymore */ 1321 ClearFlag(DeviceExt->Flags, VCB_GOOD); 1322 1323 /* Mark we're being dismounted */ 1324 DeviceExt->Flags |= VCB_DISMOUNT_PENDING; 1325 #ifndef ENABLE_SWAPOUT 1326 IrpContext->DeviceObject->Vpb->Flags &= ~VPB_MOUNTED; 1327 #endif 1328 1329 ExReleaseResourceLite(&DeviceExt->FatResource); 1330 1331 return STATUS_SUCCESS; 1332 } 1333 1334 static 1335 NTSTATUS 1336 VfatGetStatistics( 1337 PVFAT_IRP_CONTEXT IrpContext) 1338 { 1339 PVOID Buffer; 1340 ULONG Length; 1341 NTSTATUS Status; 1342 PDEVICE_EXTENSION DeviceExt; 1343 1344 DeviceExt = IrpContext->DeviceExt; 1345 Length = IrpContext->Stack->Parameters.FileSystemControl.OutputBufferLength; 1346 Buffer = IrpContext->Irp->AssociatedIrp.SystemBuffer; 1347 1348 if (Length < sizeof(FILESYSTEM_STATISTICS)) 1349 { 1350 return STATUS_BUFFER_TOO_SMALL; 1351 } 1352 1353 if (Buffer == NULL) 1354 { 1355 return STATUS_INVALID_USER_BUFFER; 1356 } 1357 1358 if (Length >= sizeof(STATISTICS) * VfatGlobalData->NumberProcessors) 1359 { 1360 Length = sizeof(STATISTICS) * VfatGlobalData->NumberProcessors; 1361 Status = STATUS_SUCCESS; 1362 } 1363 else 1364 { 1365 Status = STATUS_BUFFER_OVERFLOW; 1366 } 1367 1368 RtlCopyMemory(Buffer, DeviceExt->Statistics, Length); 1369 IrpContext->Irp->IoStatus.Information = Length; 1370 1371 return Status; 1372 } 1373 1374 /* 1375 * FUNCTION: File system control 1376 */ 1377 NTSTATUS 1378 VfatFileSystemControl( 1379 PVFAT_IRP_CONTEXT IrpContext) 1380 { 1381 NTSTATUS Status; 1382 1383 DPRINT("VfatFileSystemControl(IrpContext %p)\n", IrpContext); 1384 1385 ASSERT(IrpContext); 1386 ASSERT(IrpContext->Irp); 1387 ASSERT(IrpContext->Stack); 1388 1389 IrpContext->Irp->IoStatus.Information = 0; 1390 1391 switch (IrpContext->MinorFunction) 1392 { 1393 case IRP_MN_KERNEL_CALL: 1394 case IRP_MN_USER_FS_REQUEST: 1395 switch(IrpContext->Stack->Parameters.DeviceIoControl.IoControlCode) 1396 { 1397 case FSCTL_GET_VOLUME_BITMAP: 1398 Status = VfatGetVolumeBitmap(IrpContext); 1399 break; 1400 1401 case FSCTL_GET_RETRIEVAL_POINTERS: 1402 Status = VfatGetRetrievalPointers(IrpContext); 1403 break; 1404 1405 case FSCTL_MOVE_FILE: 1406 Status = VfatMoveFile(IrpContext); 1407 break; 1408 1409 case FSCTL_IS_VOLUME_DIRTY: 1410 Status = VfatIsVolumeDirty(IrpContext); 1411 break; 1412 1413 case FSCTL_MARK_VOLUME_DIRTY: 1414 Status = VfatMarkVolumeDirty(IrpContext); 1415 break; 1416 1417 case FSCTL_LOCK_VOLUME: 1418 Status = VfatLockOrUnlockVolume(IrpContext, TRUE); 1419 break; 1420 1421 case FSCTL_UNLOCK_VOLUME: 1422 Status = VfatLockOrUnlockVolume(IrpContext, FALSE); 1423 break; 1424 1425 case FSCTL_DISMOUNT_VOLUME: 1426 Status = VfatDismountVolume(IrpContext); 1427 break; 1428 1429 case FSCTL_FILESYSTEM_GET_STATISTICS: 1430 Status = VfatGetStatistics(IrpContext); 1431 break; 1432 1433 default: 1434 Status = STATUS_INVALID_DEVICE_REQUEST; 1435 } 1436 break; 1437 1438 case IRP_MN_MOUNT_VOLUME: 1439 Status = VfatMount(IrpContext); 1440 break; 1441 1442 case IRP_MN_VERIFY_VOLUME: 1443 DPRINT("VFATFS: IRP_MN_VERIFY_VOLUME\n"); 1444 Status = VfatVerify(IrpContext); 1445 break; 1446 1447 default: 1448 DPRINT("VFAT FSC: MinorFunction %u\n", IrpContext->MinorFunction); 1449 Status = STATUS_INVALID_DEVICE_REQUEST; 1450 break; 1451 } 1452 1453 return Status; 1454 } 1455