1 /* 2 * PROJECT: VFAT Filesystem 3 * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) 4 * PURPOSE: Routines to manipulate FCBs 5 * COPYRIGHT: Copyright 1998 Jason Filby <jasonfilby@yahoo.com> 6 * Copyright 2001 Rex Jolliff <rex@lvcablemodem.com> 7 * Copyright 2005-2022 Hervé Poussineau <hpoussin@reactos.org> 8 * Copyright 2008-2018 Pierre Schweitzer <pierre@reactos.org> 9 */ 10 11 /* ------------------------------------------------------- INCLUDES */ 12 13 #include "vfat.h" 14 15 #define NDEBUG 16 #include <debug.h> 17 18 #ifdef __GNUC__ 19 #include <wctype.h> /* towlower prototype */ 20 #endif 21 22 /* -------------------------------------------------------- DEFINES */ 23 24 #ifdef KDBG 25 extern UNICODE_STRING DebugFile; 26 #endif 27 28 /* -------------------------------------------------------- PUBLICS */ 29 30 static 31 ULONG 32 vfatNameHash( 33 ULONG hash, 34 PUNICODE_STRING NameU) 35 { 36 PWCHAR last; 37 PWCHAR curr; 38 register WCHAR c; 39 40 // LFN could start from "." 41 //ASSERT(NameU->Buffer[0] != L'.'); 42 curr = NameU->Buffer; 43 last = NameU->Buffer + NameU->Length / sizeof(WCHAR); 44 45 while(curr < last) 46 { 47 c = towlower(*curr++); 48 hash = (hash + (c << 4) + (c >> 4)) * 11; 49 } 50 return hash; 51 } 52 53 VOID 54 vfatSplitPathName( 55 PUNICODE_STRING PathNameU, 56 PUNICODE_STRING DirNameU, 57 PUNICODE_STRING FileNameU) 58 { 59 PWCHAR pName; 60 USHORT Length = 0; 61 pName = PathNameU->Buffer + PathNameU->Length / sizeof(WCHAR) - 1; 62 while (*pName != L'\\' && pName >= PathNameU->Buffer) 63 { 64 pName--; 65 Length++; 66 } 67 ASSERT(*pName == L'\\' || pName < PathNameU->Buffer); 68 if (FileNameU) 69 { 70 FileNameU->Buffer = pName + 1; 71 FileNameU->Length = FileNameU->MaximumLength = Length * sizeof(WCHAR); 72 } 73 if (DirNameU) 74 { 75 DirNameU->Buffer = PathNameU->Buffer; 76 DirNameU->Length = (pName + 1 - PathNameU->Buffer) * sizeof(WCHAR); 77 DirNameU->MaximumLength = DirNameU->Length; 78 } 79 } 80 81 static 82 VOID 83 vfatInitFcb( 84 PVFATFCB Fcb, 85 PUNICODE_STRING NameU) 86 { 87 USHORT PathNameBufferLength; 88 89 if (NameU) 90 PathNameBufferLength = NameU->Length + sizeof(WCHAR); 91 else 92 PathNameBufferLength = 0; 93 94 Fcb->PathNameBuffer = ExAllocatePoolWithTag(NonPagedPool, PathNameBufferLength, TAG_FCB); 95 if (!Fcb->PathNameBuffer) 96 { 97 /* FIXME: what to do if no more memory? */ 98 DPRINT1("Unable to initialize FCB for filename '%wZ'\n", NameU); 99 KeBugCheckEx(FAT_FILE_SYSTEM, (ULONG_PTR)Fcb, (ULONG_PTR)NameU, 0, 0); 100 } 101 102 Fcb->RFCB.NodeTypeCode = NODE_TYPE_FCB; 103 Fcb->RFCB.NodeByteSize = sizeof(VFATFCB); 104 105 Fcb->PathNameU.Length = 0; 106 Fcb->PathNameU.Buffer = Fcb->PathNameBuffer; 107 Fcb->PathNameU.MaximumLength = PathNameBufferLength; 108 Fcb->ShortNameU.Length = 0; 109 Fcb->ShortNameU.Buffer = Fcb->ShortNameBuffer; 110 Fcb->ShortNameU.MaximumLength = sizeof(Fcb->ShortNameBuffer); 111 Fcb->DirNameU.Buffer = Fcb->PathNameU.Buffer; 112 if (NameU && NameU->Length) 113 { 114 RtlCopyUnicodeString(&Fcb->PathNameU, NameU); 115 vfatSplitPathName(&Fcb->PathNameU, &Fcb->DirNameU, &Fcb->LongNameU); 116 } 117 else 118 { 119 Fcb->DirNameU.Buffer = Fcb->LongNameU.Buffer = NULL; 120 Fcb->DirNameU.MaximumLength = Fcb->DirNameU.Length = 0; 121 Fcb->LongNameU.MaximumLength = Fcb->LongNameU.Length = 0; 122 } 123 RtlZeroMemory(&Fcb->FCBShareAccess, sizeof(SHARE_ACCESS)); 124 Fcb->OpenHandleCount = 0; 125 } 126 127 PVFATFCB 128 vfatNewFCB( 129 PDEVICE_EXTENSION pVCB, 130 PUNICODE_STRING pFileNameU) 131 { 132 PVFATFCB rcFCB; 133 134 DPRINT("'%wZ'\n", pFileNameU); 135 136 rcFCB = ExAllocateFromNPagedLookasideList(&VfatGlobalData->FcbLookasideList); 137 if (rcFCB == NULL) 138 { 139 return NULL; 140 } 141 RtlZeroMemory(rcFCB, sizeof(VFATFCB)); 142 vfatInitFcb(rcFCB, pFileNameU); 143 if (vfatVolumeIsFatX(pVCB)) 144 rcFCB->Attributes = &rcFCB->entry.FatX.Attrib; 145 else 146 rcFCB->Attributes = &rcFCB->entry.Fat.Attrib; 147 rcFCB->Hash.Hash = vfatNameHash(0, &rcFCB->PathNameU); 148 rcFCB->Hash.self = rcFCB; 149 rcFCB->ShortHash.self = rcFCB; 150 ExInitializeResourceLite(&rcFCB->PagingIoResource); 151 ExInitializeResourceLite(&rcFCB->MainResource); 152 FsRtlInitializeFileLock(&rcFCB->FileLock, NULL, NULL); 153 ExInitializeFastMutex(&rcFCB->LastMutex); 154 rcFCB->RFCB.PagingIoResource = &rcFCB->PagingIoResource; 155 rcFCB->RFCB.Resource = &rcFCB->MainResource; 156 rcFCB->RFCB.IsFastIoPossible = FastIoIsNotPossible; 157 InitializeListHead(&rcFCB->ParentListHead); 158 159 return rcFCB; 160 } 161 162 static 163 VOID 164 vfatDelFCBFromTable( 165 PDEVICE_EXTENSION pVCB, 166 PVFATFCB pFCB) 167 { 168 ULONG Index; 169 ULONG ShortIndex; 170 HASHENTRY* entry; 171 172 Index = pFCB->Hash.Hash % pVCB->HashTableSize; 173 ShortIndex = pFCB->ShortHash.Hash % pVCB->HashTableSize; 174 175 if (pFCB->Hash.Hash != pFCB->ShortHash.Hash) 176 { 177 entry = pVCB->FcbHashTable[ShortIndex]; 178 if (entry->self == pFCB) 179 { 180 pVCB->FcbHashTable[ShortIndex] = entry->next; 181 } 182 else 183 { 184 while (entry->next->self != pFCB) 185 { 186 entry = entry->next; 187 } 188 entry->next = pFCB->ShortHash.next; 189 } 190 } 191 entry = pVCB->FcbHashTable[Index]; 192 if (entry->self == pFCB) 193 { 194 pVCB->FcbHashTable[Index] = entry->next; 195 } 196 else 197 { 198 while (entry->next->self != pFCB) 199 { 200 entry = entry->next; 201 } 202 entry->next = pFCB->Hash.next; 203 } 204 205 RemoveEntryList(&pFCB->FcbListEntry); 206 } 207 208 static 209 NTSTATUS 210 vfatMakeFullName( 211 PVFATFCB directoryFCB, 212 PUNICODE_STRING LongNameU, 213 PUNICODE_STRING ShortNameU, 214 PUNICODE_STRING NameU) 215 { 216 PWCHAR PathNameBuffer; 217 USHORT PathNameLength; 218 219 PathNameLength = directoryFCB->PathNameU.Length + max(LongNameU->Length, ShortNameU->Length); 220 if (!vfatFCBIsRoot(directoryFCB)) 221 { 222 PathNameLength += sizeof(WCHAR); 223 } 224 225 if (PathNameLength > LONGNAME_MAX_LENGTH * sizeof(WCHAR)) 226 { 227 return STATUS_OBJECT_NAME_INVALID; 228 } 229 PathNameBuffer = ExAllocatePoolWithTag(NonPagedPool, PathNameLength + sizeof(WCHAR), TAG_FCB); 230 if (!PathNameBuffer) 231 { 232 return STATUS_INSUFFICIENT_RESOURCES; 233 } 234 NameU->Buffer = PathNameBuffer; 235 NameU->Length = 0; 236 NameU->MaximumLength = PathNameLength; 237 238 RtlCopyUnicodeString(NameU, &directoryFCB->PathNameU); 239 if (!vfatFCBIsRoot(directoryFCB)) 240 { 241 RtlAppendUnicodeToString(NameU, L"\\"); 242 } 243 if (LongNameU->Length > 0) 244 { 245 RtlAppendUnicodeStringToString(NameU, LongNameU); 246 } 247 else 248 { 249 RtlAppendUnicodeStringToString(NameU, ShortNameU); 250 } 251 NameU->Buffer[NameU->Length / sizeof(WCHAR)] = 0; 252 253 return STATUS_SUCCESS; 254 } 255 256 VOID 257 vfatDestroyCCB( 258 PVFATCCB pCcb) 259 { 260 if (pCcb->SearchPattern.Buffer) 261 { 262 ExFreePoolWithTag(pCcb->SearchPattern.Buffer, TAG_SEARCH); 263 } 264 ExFreeToNPagedLookasideList(&VfatGlobalData->CcbLookasideList, pCcb); 265 } 266 267 VOID 268 vfatDestroyFCB( 269 PVFATFCB pFCB) 270 { 271 #ifdef KDBG 272 if (DebugFile.Buffer != NULL && FsRtlIsNameInExpression(&DebugFile, &pFCB->LongNameU, FALSE, NULL)) 273 { 274 DPRINT1("Destroying: %p (%wZ) %d\n", pFCB, &pFCB->PathNameU, pFCB->RefCount); 275 } 276 #endif 277 278 FsRtlUninitializeFileLock(&pFCB->FileLock); 279 280 if (!vfatFCBIsRoot(pFCB) && 281 !BooleanFlagOn(pFCB->Flags, FCB_IS_FAT) && !BooleanFlagOn(pFCB->Flags, FCB_IS_VOLUME)) 282 { 283 RemoveEntryList(&pFCB->ParentListEntry); 284 } 285 ExFreePool(pFCB->PathNameBuffer); 286 ExDeleteResourceLite(&pFCB->PagingIoResource); 287 ExDeleteResourceLite(&pFCB->MainResource); 288 ASSERT(IsListEmpty(&pFCB->ParentListHead)); 289 ExFreeToNPagedLookasideList(&VfatGlobalData->FcbLookasideList, pFCB); 290 } 291 292 BOOLEAN 293 vfatFCBIsRoot( 294 PVFATFCB FCB) 295 { 296 return FCB->PathNameU.Length == sizeof(WCHAR) && FCB->PathNameU.Buffer[0] == L'\\' ? TRUE : FALSE; 297 } 298 299 VOID 300 #ifndef KDBG 301 vfatGrabFCB( 302 #else 303 _vfatGrabFCB( 304 #endif 305 PDEVICE_EXTENSION pVCB, 306 PVFATFCB pFCB 307 #ifdef KDBG 308 , 309 PCSTR File, 310 ULONG Line, 311 PCSTR Func 312 #endif 313 ) 314 { 315 #ifdef KDBG 316 if (DebugFile.Buffer != NULL && FsRtlIsNameInExpression(&DebugFile, &pFCB->LongNameU, FALSE, NULL)) 317 { 318 DPRINT1("Inc ref count (%d, oc: %d) for: %p (%wZ) at: %s(%d) %s\n", pFCB->RefCount, pFCB->OpenHandleCount, pFCB, &pFCB->PathNameU, File, Line, Func); 319 } 320 #else 321 DPRINT("Grabbing FCB at %p: %wZ, refCount:%d\n", 322 pFCB, &pFCB->PathNameU, pFCB->RefCount); 323 #endif 324 325 ASSERT(ExIsResourceAcquiredExclusive(&pVCB->DirResource)); 326 327 ASSERT(!BooleanFlagOn(pFCB->Flags, FCB_IS_FAT)); 328 ASSERT(pFCB != pVCB->VolumeFcb && !BooleanFlagOn(pFCB->Flags, FCB_IS_VOLUME)); 329 ASSERT(pFCB->RefCount > 0); 330 ++pFCB->RefCount; 331 } 332 333 VOID 334 #ifndef KDBG 335 vfatReleaseFCB( 336 #else 337 _vfatReleaseFCB( 338 #endif 339 PDEVICE_EXTENSION pVCB, 340 PVFATFCB pFCB 341 #ifdef KDBG 342 , 343 PCSTR File, 344 ULONG Line, 345 PCSTR Func 346 #endif 347 ) 348 { 349 PVFATFCB tmpFcb; 350 351 #ifdef KDBG 352 if (DebugFile.Buffer != NULL && FsRtlIsNameInExpression(&DebugFile, &pFCB->LongNameU, FALSE, NULL)) 353 { 354 DPRINT1("Dec ref count (%d, oc: %d) for: %p (%wZ) at: %s(%d) %s\n", pFCB->RefCount, pFCB->OpenHandleCount, pFCB, &pFCB->PathNameU, File, Line, Func); 355 } 356 #else 357 DPRINT("Releasing FCB at %p: %wZ, refCount:%d\n", 358 pFCB, &pFCB->PathNameU, pFCB->RefCount); 359 #endif 360 361 ASSERT(ExIsResourceAcquiredExclusive(&pVCB->DirResource)); 362 363 while (pFCB) 364 { 365 ULONG RefCount; 366 367 ASSERT(!BooleanFlagOn(pFCB->Flags, FCB_IS_FAT)); 368 ASSERT(pFCB != pVCB->VolumeFcb && !BooleanFlagOn(pFCB->Flags, FCB_IS_VOLUME)); 369 ASSERT(pFCB->RefCount > 0); 370 RefCount = --pFCB->RefCount; 371 372 if (RefCount == 1 && BooleanFlagOn(pFCB->Flags, FCB_CACHE_INITIALIZED)) 373 { 374 PFILE_OBJECT tmpFileObject; 375 tmpFileObject = pFCB->FileObject; 376 377 pFCB->FileObject = NULL; 378 CcUninitializeCacheMap(tmpFileObject, NULL, NULL); 379 ClearFlag(pFCB->Flags, FCB_CACHE_INITIALIZED); 380 ObDereferenceObject(tmpFileObject); 381 } 382 383 if (RefCount == 0) 384 { 385 ASSERT(pFCB->OpenHandleCount == 0); 386 tmpFcb = pFCB->parentFcb; 387 vfatDelFCBFromTable(pVCB, pFCB); 388 vfatDestroyFCB(pFCB); 389 } 390 else 391 { 392 tmpFcb = NULL; 393 } 394 pFCB = tmpFcb; 395 } 396 } 397 398 static 399 VOID 400 vfatAddFCBToTable( 401 PDEVICE_EXTENSION pVCB, 402 PVFATFCB pFCB) 403 { 404 ULONG Index; 405 ULONG ShortIndex; 406 407 ASSERT(pFCB->Hash.Hash == vfatNameHash(0, &pFCB->PathNameU)); 408 Index = pFCB->Hash.Hash % pVCB->HashTableSize; 409 ShortIndex = pFCB->ShortHash.Hash % pVCB->HashTableSize; 410 411 InsertTailList(&pVCB->FcbListHead, &pFCB->FcbListEntry); 412 413 pFCB->Hash.next = pVCB->FcbHashTable[Index]; 414 pVCB->FcbHashTable[Index] = &pFCB->Hash; 415 if (pFCB->Hash.Hash != pFCB->ShortHash.Hash) 416 { 417 pFCB->ShortHash.next = pVCB->FcbHashTable[ShortIndex]; 418 pVCB->FcbHashTable[ShortIndex] = &pFCB->ShortHash; 419 } 420 if (pFCB->parentFcb) 421 { 422 vfatGrabFCB(pVCB, pFCB->parentFcb); 423 } 424 } 425 426 static 427 VOID 428 vfatInitFCBFromDirEntry( 429 PDEVICE_EXTENSION Vcb, 430 PVFATFCB ParentFcb, 431 PVFATFCB Fcb, 432 PVFAT_DIRENTRY_CONTEXT DirContext) 433 { 434 ULONG Size; 435 436 RtlCopyMemory(&Fcb->entry, &DirContext->DirEntry, sizeof (DIR_ENTRY)); 437 RtlCopyUnicodeString(&Fcb->ShortNameU, &DirContext->ShortNameU); 438 Fcb->Hash.Hash = vfatNameHash(0, &Fcb->PathNameU); 439 if (vfatVolumeIsFatX(Vcb)) 440 { 441 Fcb->ShortHash.Hash = Fcb->Hash.Hash; 442 } 443 else 444 { 445 Fcb->ShortHash.Hash = vfatNameHash(0, &Fcb->DirNameU); 446 Fcb->ShortHash.Hash = vfatNameHash(Fcb->ShortHash.Hash, &Fcb->ShortNameU); 447 } 448 449 if (vfatFCBIsDirectory(Fcb)) 450 { 451 ULONG FirstCluster, CurrentCluster; 452 NTSTATUS Status = STATUS_SUCCESS; 453 Size = 0; 454 FirstCluster = vfatDirEntryGetFirstCluster(Vcb, &Fcb->entry); 455 if (FirstCluster == 1) 456 { 457 Size = Vcb->FatInfo.rootDirectorySectors * Vcb->FatInfo.BytesPerSector; 458 } 459 else if (FirstCluster != 0) 460 { 461 CurrentCluster = FirstCluster; 462 while (CurrentCluster != 0xffffffff && NT_SUCCESS(Status)) 463 { 464 Size += Vcb->FatInfo.BytesPerCluster; 465 Status = NextCluster(Vcb, FirstCluster, &CurrentCluster, FALSE); 466 } 467 } 468 } 469 else if (vfatVolumeIsFatX(Vcb)) 470 { 471 Size = Fcb->entry.FatX.FileSize; 472 } 473 else 474 { 475 Size = Fcb->entry.Fat.FileSize; 476 } 477 Fcb->dirIndex = DirContext->DirIndex; 478 Fcb->startIndex = DirContext->StartIndex; 479 Fcb->parentFcb = ParentFcb; 480 if (vfatVolumeIsFatX(Vcb) && !vfatFCBIsRoot(ParentFcb)) 481 { 482 ASSERT(DirContext->DirIndex >= 2 && DirContext->StartIndex >= 2); 483 Fcb->dirIndex = DirContext->DirIndex-2; 484 Fcb->startIndex = DirContext->StartIndex-2; 485 } 486 Fcb->RFCB.FileSize.QuadPart = Size; 487 Fcb->RFCB.ValidDataLength.QuadPart = Size; 488 Fcb->RFCB.AllocationSize.QuadPart = ROUND_UP_64(Size, Vcb->FatInfo.BytesPerCluster); 489 } 490 491 NTSTATUS 492 vfatSetFCBNewDirName( 493 PDEVICE_EXTENSION pVCB, 494 PVFATFCB Fcb, 495 PVFATFCB ParentFcb) 496 { 497 NTSTATUS Status; 498 UNICODE_STRING NewNameU; 499 500 /* Get full path name */ 501 Status = vfatMakeFullName(ParentFcb, &Fcb->LongNameU, &Fcb->ShortNameU, &NewNameU); 502 if (!NT_SUCCESS(Status)) 503 { 504 return Status; 505 } 506 507 /* Delete old name */ 508 if (Fcb->PathNameBuffer) 509 { 510 ExFreePoolWithTag(Fcb->PathNameBuffer, TAG_FCB); 511 } 512 Fcb->PathNameU = NewNameU; 513 514 /* Delete from table */ 515 vfatDelFCBFromTable(pVCB, Fcb); 516 517 /* Split it properly */ 518 Fcb->PathNameBuffer = Fcb->PathNameU.Buffer; 519 Fcb->DirNameU.Buffer = Fcb->PathNameU.Buffer; 520 vfatSplitPathName(&Fcb->PathNameU, &Fcb->DirNameU, &Fcb->LongNameU); 521 Fcb->Hash.Hash = vfatNameHash(0, &Fcb->PathNameU); 522 if (vfatVolumeIsFatX(pVCB)) 523 { 524 Fcb->ShortHash.Hash = Fcb->Hash.Hash; 525 } 526 else 527 { 528 Fcb->ShortHash.Hash = vfatNameHash(0, &Fcb->DirNameU); 529 Fcb->ShortHash.Hash = vfatNameHash(Fcb->ShortHash.Hash, &Fcb->ShortNameU); 530 } 531 532 vfatAddFCBToTable(pVCB, Fcb); 533 vfatReleaseFCB(pVCB, ParentFcb); 534 535 return STATUS_SUCCESS; 536 } 537 538 NTSTATUS 539 vfatUpdateFCB( 540 PDEVICE_EXTENSION pVCB, 541 PVFATFCB Fcb, 542 PVFAT_DIRENTRY_CONTEXT DirContext, 543 PVFATFCB ParentFcb) 544 { 545 NTSTATUS Status; 546 PVFATFCB OldParent; 547 548 DPRINT("vfatUpdateFCB(%p, %p, %p, %p)\n", pVCB, Fcb, DirContext, ParentFcb); 549 550 /* Get full path name */ 551 Status = vfatMakeFullName(ParentFcb, &DirContext->LongNameU, &DirContext->ShortNameU, &Fcb->PathNameU); 552 if (!NT_SUCCESS(Status)) 553 { 554 return Status; 555 } 556 557 /* Delete old name */ 558 if (Fcb->PathNameBuffer) 559 { 560 ExFreePoolWithTag(Fcb->PathNameBuffer, TAG_FCB); 561 } 562 563 /* Delete from table */ 564 vfatDelFCBFromTable(pVCB, Fcb); 565 566 /* Split it properly */ 567 Fcb->PathNameBuffer = Fcb->PathNameU.Buffer; 568 Fcb->DirNameU.Buffer = Fcb->PathNameU.Buffer; 569 vfatSplitPathName(&Fcb->PathNameU, &Fcb->DirNameU, &Fcb->LongNameU); 570 571 /* Save old parent */ 572 OldParent = Fcb->parentFcb; 573 RemoveEntryList(&Fcb->ParentListEntry); 574 575 /* Reinit FCB */ 576 vfatInitFCBFromDirEntry(pVCB, ParentFcb, Fcb, DirContext); 577 578 if (vfatFCBIsDirectory(Fcb)) 579 { 580 CcFlushCache(&Fcb->SectionObjectPointers, NULL, 0, NULL); 581 } 582 InsertTailList(&ParentFcb->ParentListHead, &Fcb->ParentListEntry); 583 vfatAddFCBToTable(pVCB, Fcb); 584 585 /* If we moved across directories, dereference our old parent 586 * We also dereference in case we're just renaming since AddFCBToTable references it 587 */ 588 vfatReleaseFCB(pVCB, OldParent); 589 590 return STATUS_SUCCESS; 591 } 592 593 PVFATFCB 594 vfatGrabFCBFromTable( 595 PDEVICE_EXTENSION pVCB, 596 PUNICODE_STRING PathNameU) 597 { 598 PVFATFCB rcFCB; 599 ULONG Hash; 600 UNICODE_STRING DirNameU; 601 UNICODE_STRING FileNameU; 602 PUNICODE_STRING FcbNameU; 603 604 HASHENTRY* entry; 605 606 DPRINT("'%wZ'\n", PathNameU); 607 608 ASSERT(PathNameU->Length >= sizeof(WCHAR) && PathNameU->Buffer[0] == L'\\'); 609 Hash = vfatNameHash(0, PathNameU); 610 611 entry = pVCB->FcbHashTable[Hash % pVCB->HashTableSize]; 612 if (entry) 613 { 614 vfatSplitPathName(PathNameU, &DirNameU, &FileNameU); 615 } 616 617 while (entry) 618 { 619 if (entry->Hash == Hash) 620 { 621 rcFCB = entry->self; 622 DPRINT("'%wZ' '%wZ'\n", &DirNameU, &rcFCB->DirNameU); 623 if (RtlEqualUnicodeString(&DirNameU, &rcFCB->DirNameU, TRUE)) 624 { 625 if (rcFCB->Hash.Hash == Hash) 626 { 627 FcbNameU = &rcFCB->LongNameU; 628 } 629 else 630 { 631 FcbNameU = &rcFCB->ShortNameU; 632 } 633 /* compare the file name */ 634 DPRINT("'%wZ' '%wZ'\n", &FileNameU, FcbNameU); 635 if (RtlEqualUnicodeString(&FileNameU, FcbNameU, TRUE)) 636 { 637 vfatGrabFCB(pVCB, rcFCB); 638 return rcFCB; 639 } 640 } 641 } 642 entry = entry->next; 643 } 644 return NULL; 645 } 646 647 PVFATFCB 648 vfatMakeRootFCB( 649 PDEVICE_EXTENSION pVCB) 650 { 651 PVFATFCB FCB; 652 ULONG FirstCluster, CurrentCluster, Size = 0; 653 NTSTATUS Status = STATUS_SUCCESS; 654 UNICODE_STRING NameU = RTL_CONSTANT_STRING(L"\\"); 655 656 ASSERT(pVCB->RootFcb == NULL); 657 658 FCB = vfatNewFCB(pVCB, &NameU); 659 if (vfatVolumeIsFatX(pVCB)) 660 { 661 memset(FCB->entry.FatX.Filename, ' ', 42); 662 FCB->entry.FatX.FileSize = pVCB->FatInfo.rootDirectorySectors * pVCB->FatInfo.BytesPerSector; 663 FCB->entry.FatX.Attrib = FILE_ATTRIBUTE_DIRECTORY; 664 FCB->entry.FatX.FirstCluster = 1; 665 Size = pVCB->FatInfo.rootDirectorySectors * pVCB->FatInfo.BytesPerSector; 666 } 667 else 668 { 669 memset(FCB->entry.Fat.ShortName, ' ', 11); 670 FCB->entry.Fat.FileSize = pVCB->FatInfo.rootDirectorySectors * pVCB->FatInfo.BytesPerSector; 671 FCB->entry.Fat.Attrib = FILE_ATTRIBUTE_DIRECTORY; 672 if (pVCB->FatInfo.FatType == FAT32) 673 { 674 CurrentCluster = FirstCluster = pVCB->FatInfo.RootCluster; 675 FCB->entry.Fat.FirstCluster = (unsigned short)(FirstCluster & 0xffff); 676 FCB->entry.Fat.FirstClusterHigh = (unsigned short)(FirstCluster >> 16); 677 678 while (CurrentCluster != 0xffffffff && NT_SUCCESS(Status)) 679 { 680 Size += pVCB->FatInfo.BytesPerCluster; 681 Status = NextCluster (pVCB, FirstCluster, &CurrentCluster, FALSE); 682 } 683 } 684 else 685 { 686 FCB->entry.Fat.FirstCluster = 1; 687 Size = pVCB->FatInfo.rootDirectorySectors * pVCB->FatInfo.BytesPerSector; 688 } 689 } 690 FCB->ShortHash.Hash = FCB->Hash.Hash; 691 FCB->RefCount = 2; 692 FCB->dirIndex = 0; 693 FCB->RFCB.FileSize.QuadPart = Size; 694 FCB->RFCB.ValidDataLength.QuadPart = Size; 695 FCB->RFCB.AllocationSize.QuadPart = Size; 696 FCB->RFCB.IsFastIoPossible = FastIoIsNotPossible; 697 698 vfatFCBInitializeCacheFromVolume(pVCB, FCB); 699 vfatAddFCBToTable(pVCB, FCB); 700 701 /* Cache it */ 702 pVCB->RootFcb = FCB; 703 704 return FCB; 705 } 706 707 PVFATFCB 708 vfatOpenRootFCB( 709 PDEVICE_EXTENSION pVCB) 710 { 711 PVFATFCB FCB; 712 UNICODE_STRING NameU = RTL_CONSTANT_STRING(L"\\"); 713 714 FCB = vfatGrabFCBFromTable(pVCB, &NameU); 715 if (FCB == NULL) 716 { 717 FCB = vfatMakeRootFCB(pVCB); 718 } 719 ASSERT(FCB == pVCB->RootFcb); 720 721 return FCB; 722 } 723 724 NTSTATUS 725 vfatMakeFCBFromDirEntry( 726 PVCB vcb, 727 PVFATFCB directoryFCB, 728 PVFAT_DIRENTRY_CONTEXT DirContext, 729 PVFATFCB *fileFCB) 730 { 731 PVFATFCB rcFCB; 732 UNICODE_STRING NameU; 733 NTSTATUS Status; 734 735 Status = vfatMakeFullName(directoryFCB, &DirContext->LongNameU, &DirContext->ShortNameU, &NameU); 736 if (!NT_SUCCESS(Status)) 737 { 738 return Status; 739 } 740 741 rcFCB = vfatNewFCB(vcb, &NameU); 742 vfatInitFCBFromDirEntry(vcb, directoryFCB, rcFCB, DirContext); 743 744 rcFCB->RefCount = 1; 745 InsertTailList(&directoryFCB->ParentListHead, &rcFCB->ParentListEntry); 746 vfatAddFCBToTable(vcb, rcFCB); 747 *fileFCB = rcFCB; 748 749 ExFreePoolWithTag(NameU.Buffer, TAG_FCB); 750 return STATUS_SUCCESS; 751 } 752 753 NTSTATUS 754 vfatAttachFCBToFileObject( 755 PDEVICE_EXTENSION vcb, 756 PVFATFCB fcb, 757 PFILE_OBJECT fileObject) 758 { 759 PVFATCCB newCCB; 760 761 #ifdef KDBG 762 if (DebugFile.Buffer != NULL && FsRtlIsNameInExpression(&DebugFile, &fcb->LongNameU, FALSE, NULL)) 763 { 764 DPRINT1("Attaching %p to %p (%d)\n", fcb, fileObject, fcb->RefCount); 765 } 766 #endif 767 768 newCCB = ExAllocateFromNPagedLookasideList(&VfatGlobalData->CcbLookasideList); 769 if (newCCB == NULL) 770 { 771 return STATUS_INSUFFICIENT_RESOURCES; 772 } 773 RtlZeroMemory(newCCB, sizeof (VFATCCB)); 774 775 fileObject->SectionObjectPointer = &fcb->SectionObjectPointers; 776 fileObject->FsContext = fcb; 777 fileObject->FsContext2 = newCCB; 778 fileObject->Vpb = vcb->IoVPB; 779 DPRINT("file open: fcb:%p PathName:%wZ\n", fcb, &fcb->PathNameU); 780 781 #ifdef KDBG 782 fcb->Flags &= ~FCB_CLEANED_UP; 783 fcb->Flags &= ~FCB_CLOSED; 784 #endif 785 786 return STATUS_SUCCESS; 787 } 788 789 NTSTATUS 790 vfatDirFindFile( 791 PDEVICE_EXTENSION pDeviceExt, 792 PVFATFCB pDirectoryFCB, 793 PUNICODE_STRING FileToFindU, 794 PVFATFCB *pFoundFCB) 795 { 796 NTSTATUS status; 797 PVOID Context = NULL; 798 PVOID Page = NULL; 799 BOOLEAN First = TRUE; 800 VFAT_DIRENTRY_CONTEXT DirContext; 801 /* This buffer must have a size of 260 characters, because 802 vfatMakeFCBFromDirEntry can copy 20 name entries with 13 characters. */ 803 WCHAR LongNameBuffer[260]; 804 WCHAR ShortNameBuffer[13]; 805 BOOLEAN FoundLong = FALSE; 806 BOOLEAN FoundShort = FALSE; 807 BOOLEAN IsFatX = vfatVolumeIsFatX(pDeviceExt); 808 809 ASSERT(pDeviceExt); 810 ASSERT(pDirectoryFCB); 811 ASSERT(FileToFindU); 812 813 DPRINT("vfatDirFindFile(VCB:%p, dirFCB:%p, File:%wZ)\n", 814 pDeviceExt, pDirectoryFCB, FileToFindU); 815 DPRINT("Dir Path:%wZ\n", &pDirectoryFCB->PathNameU); 816 817 DirContext.DirIndex = 0; 818 DirContext.LongNameU.Buffer = LongNameBuffer; 819 DirContext.LongNameU.Length = 0; 820 DirContext.LongNameU.MaximumLength = sizeof(LongNameBuffer); 821 DirContext.ShortNameU.Buffer = ShortNameBuffer; 822 DirContext.ShortNameU.Length = 0; 823 DirContext.ShortNameU.MaximumLength = sizeof(ShortNameBuffer); 824 DirContext.DeviceExt = pDeviceExt; 825 826 while (TRUE) 827 { 828 status = VfatGetNextDirEntry(pDeviceExt, 829 &Context, 830 &Page, 831 pDirectoryFCB, 832 &DirContext, 833 First); 834 First = FALSE; 835 if (status == STATUS_NO_MORE_ENTRIES) 836 { 837 return STATUS_OBJECT_NAME_NOT_FOUND; 838 } 839 if (!NT_SUCCESS(status)) 840 { 841 return status; 842 } 843 844 DPRINT(" Index:%u longName:%wZ\n", 845 DirContext.DirIndex, &DirContext.LongNameU); 846 847 if (!ENTRY_VOLUME(IsFatX, &DirContext.DirEntry)) 848 { 849 if (DirContext.LongNameU.Length == 0 || 850 DirContext.ShortNameU.Length == 0) 851 { 852 DPRINT1("WARNING: File system corruption detected. You may need to run a disk repair utility.\n"); 853 if (VfatGlobalData->Flags & VFAT_BREAK_ON_CORRUPTION) 854 { 855 ASSERT(DirContext.LongNameU.Length != 0 && 856 DirContext.ShortNameU.Length != 0); 857 } 858 DirContext.DirIndex++; 859 continue; 860 } 861 FoundLong = RtlEqualUnicodeString(FileToFindU, &DirContext.LongNameU, TRUE); 862 if (FoundLong == FALSE) 863 { 864 FoundShort = RtlEqualUnicodeString(FileToFindU, &DirContext.ShortNameU, TRUE); 865 } 866 if (FoundLong || FoundShort) 867 { 868 status = vfatMakeFCBFromDirEntry(pDeviceExt, 869 pDirectoryFCB, 870 &DirContext, 871 pFoundFCB); 872 CcUnpinData(Context); 873 return status; 874 } 875 } 876 DirContext.DirIndex++; 877 } 878 879 return STATUS_OBJECT_NAME_NOT_FOUND; 880 } 881 882 NTSTATUS 883 vfatGetFCBForFile( 884 PDEVICE_EXTENSION pVCB, 885 PVFATFCB *pParentFCB, 886 PVFATFCB *pFCB, 887 PUNICODE_STRING pFileNameU) 888 { 889 NTSTATUS status; 890 PVFATFCB FCB = NULL; 891 PVFATFCB parentFCB; 892 UNICODE_STRING NameU; 893 UNICODE_STRING RootNameU = RTL_CONSTANT_STRING(L"\\"); 894 UNICODE_STRING FileNameU; 895 WCHAR NameBuffer[260]; 896 PWCHAR curr, prev, last; 897 ULONG Length; 898 899 DPRINT("vfatGetFCBForFile (%p,%p,%p,%wZ)\n", 900 pVCB, pParentFCB, pFCB, pFileNameU); 901 902 RtlInitEmptyUnicodeString(&FileNameU, NameBuffer, sizeof(NameBuffer)); 903 904 parentFCB = *pParentFCB; 905 906 if (parentFCB == NULL) 907 { 908 /* Passed-in name is the full name */ 909 RtlCopyUnicodeString(&FileNameU, pFileNameU); 910 911 // Trivial case, open of the root directory on volume 912 if (RtlEqualUnicodeString(&FileNameU, &RootNameU, FALSE)) 913 { 914 DPRINT("returning root FCB\n"); 915 916 FCB = vfatOpenRootFCB(pVCB); 917 *pFCB = FCB; 918 *pParentFCB = NULL; 919 920 return (FCB != NULL) ? STATUS_SUCCESS : STATUS_OBJECT_PATH_NOT_FOUND; 921 } 922 923 /* Check for an existing FCB */ 924 FCB = vfatGrabFCBFromTable(pVCB, &FileNameU); 925 if (FCB) 926 { 927 *pFCB = FCB; 928 *pParentFCB = FCB->parentFcb; 929 vfatGrabFCB(pVCB, *pParentFCB); 930 return STATUS_SUCCESS; 931 } 932 933 last = curr = FileNameU.Buffer + FileNameU.Length / sizeof(WCHAR) - 1; 934 while (*curr != L'\\' && curr > FileNameU.Buffer) 935 { 936 curr--; 937 } 938 939 if (curr > FileNameU.Buffer) 940 { 941 NameU.Buffer = FileNameU.Buffer; 942 NameU.MaximumLength = NameU.Length = (curr - FileNameU.Buffer) * sizeof(WCHAR); 943 FCB = vfatGrabFCBFromTable(pVCB, &NameU); 944 if (FCB) 945 { 946 Length = (curr - FileNameU.Buffer) * sizeof(WCHAR); 947 if (Length != FCB->PathNameU.Length) 948 { 949 if (FileNameU.Length + FCB->PathNameU.Length - Length > FileNameU.MaximumLength) 950 { 951 vfatReleaseFCB(pVCB, FCB); 952 return STATUS_OBJECT_NAME_INVALID; 953 } 954 RtlMoveMemory(FileNameU.Buffer + FCB->PathNameU.Length / sizeof(WCHAR), 955 curr, FileNameU.Length - Length); 956 FileNameU.Length += (USHORT)(FCB->PathNameU.Length - Length); 957 curr = FileNameU.Buffer + FCB->PathNameU.Length / sizeof(WCHAR); 958 last = FileNameU.Buffer + FileNameU.Length / sizeof(WCHAR) - 1; 959 } 960 RtlCopyMemory(FileNameU.Buffer, FCB->PathNameU.Buffer, FCB->PathNameU.Length); 961 } 962 } 963 else 964 { 965 FCB = NULL; 966 } 967 968 if (FCB == NULL) 969 { 970 FCB = vfatOpenRootFCB(pVCB); 971 curr = FileNameU.Buffer; 972 } 973 974 parentFCB = NULL; 975 prev = curr; 976 } 977 else 978 { 979 /* Make absolute path */ 980 RtlCopyUnicodeString(&FileNameU, &parentFCB->PathNameU); 981 curr = FileNameU.Buffer + FileNameU.Length / sizeof(WCHAR) - 1; 982 if (*curr != L'\\') 983 { 984 RtlAppendUnicodeToString(&FileNameU, L"\\"); 985 curr++; 986 } 987 ASSERT(*curr == L'\\'); 988 RtlAppendUnicodeStringToString(&FileNameU, pFileNameU); 989 990 FCB = parentFCB; 991 parentFCB = NULL; 992 prev = curr; 993 last = FileNameU.Buffer + FileNameU.Length / sizeof(WCHAR) - 1; 994 } 995 996 while (curr <= last) 997 { 998 if (parentFCB) 999 { 1000 vfatReleaseFCB(pVCB, parentFCB); 1001 parentFCB = NULL; 1002 } 1003 // fail if element in FCB is not a directory 1004 if (!vfatFCBIsDirectory(FCB)) 1005 { 1006 DPRINT ("Element in requested path is not a directory\n"); 1007 1008 vfatReleaseFCB(pVCB, FCB); 1009 FCB = NULL; 1010 *pParentFCB = NULL; 1011 *pFCB = NULL; 1012 1013 return STATUS_OBJECT_PATH_NOT_FOUND; 1014 } 1015 parentFCB = FCB; 1016 if (prev < curr) 1017 { 1018 Length = (curr - prev) * sizeof(WCHAR); 1019 if (Length != parentFCB->LongNameU.Length) 1020 { 1021 if (FileNameU.Length + parentFCB->LongNameU.Length - Length > FileNameU.MaximumLength) 1022 { 1023 vfatReleaseFCB(pVCB, parentFCB); 1024 *pParentFCB = NULL; 1025 *pFCB = NULL; 1026 return STATUS_OBJECT_NAME_INVALID; 1027 } 1028 RtlMoveMemory(prev + parentFCB->LongNameU.Length / sizeof(WCHAR), curr, 1029 FileNameU.Length - (curr - FileNameU.Buffer) * sizeof(WCHAR)); 1030 FileNameU.Length += (USHORT)(parentFCB->LongNameU.Length - Length); 1031 curr = prev + parentFCB->LongNameU.Length / sizeof(WCHAR); 1032 last = FileNameU.Buffer + FileNameU.Length / sizeof(WCHAR) - 1; 1033 } 1034 RtlCopyMemory(prev, parentFCB->LongNameU.Buffer, parentFCB->LongNameU.Length); 1035 } 1036 curr++; 1037 prev = curr; 1038 while (*curr != L'\\' && curr <= last) 1039 { 1040 curr++; 1041 } 1042 NameU.Buffer = FileNameU.Buffer; 1043 NameU.Length = (curr - NameU.Buffer) * sizeof(WCHAR); 1044 NameU.MaximumLength = FileNameU.MaximumLength; 1045 DPRINT("%wZ\n", &NameU); 1046 FCB = vfatGrabFCBFromTable(pVCB, &NameU); 1047 if (FCB == NULL) 1048 { 1049 NameU.Buffer = prev; 1050 NameU.MaximumLength = NameU.Length = (curr - prev) * sizeof(WCHAR); 1051 status = vfatDirFindFile(pVCB, parentFCB, &NameU, &FCB); 1052 if (status == STATUS_OBJECT_NAME_NOT_FOUND) 1053 { 1054 *pFCB = NULL; 1055 if (curr > last) 1056 { 1057 *pParentFCB = parentFCB; 1058 return STATUS_OBJECT_NAME_NOT_FOUND; 1059 } 1060 else 1061 { 1062 vfatReleaseFCB(pVCB, parentFCB); 1063 *pParentFCB = NULL; 1064 return STATUS_OBJECT_PATH_NOT_FOUND; 1065 } 1066 } 1067 else if (!NT_SUCCESS(status)) 1068 { 1069 vfatReleaseFCB(pVCB, parentFCB); 1070 *pParentFCB = NULL; 1071 *pFCB = NULL; 1072 1073 return status; 1074 } 1075 } 1076 } 1077 1078 *pParentFCB = parentFCB; 1079 *pFCB = FCB; 1080 1081 return STATUS_SUCCESS; 1082 } 1083