5 * PURPOSE: Routines to manipulate FCBs.
6 * COPYRIGHT: See COPYING in the top level directory
7 * PROJECT: ReactOS kernel
8 * PROGRAMMER: Jason Filby (jasonfilby@yahoo.com)
9 * Rex Jolliff (rex@lvcablemodem.com)
12 /* ------------------------------------------------------- INCLUDES */
14 #include <ddk/ntddk.h>
24 /* -------------------------------------------------------- DEFINES */
26 #define TAG(A, B, C, D) (ULONG)(((A)<<0) + ((B)<<8) + ((C)<<16) + ((D)<<24))
27 #define TAG_FCB TAG('V', 'F', 'C', 'B')
29 #define ROUND_UP(N, S) ((((N) + (S) - 1) / (S)) * (S))
31 /* -------------------------------------------------------- PUBLICS */
33 ULONG vfatNameHash(ULONG hash, PWCHAR name)
39 hash = (hash + (c << 4) + (c >> 4)) * 11;
45 vfatNewFCB(PWCHAR pFileName)
49 rcFCB = ExAllocateFromNPagedLookasideList(&VfatGlobalData->FcbLookasideList);
50 memset (rcFCB, 0, sizeof (VFATFCB));
53 wcscpy (rcFCB->PathName, pFileName);
54 rcFCB->ObjectName = wcsrchr(rcFCB->PathName, L'\\');
55 if (rcFCB->ObjectName == NULL)
57 rcFCB->ObjectName = rcFCB->PathName;
59 rcFCB->Hash.Hash = vfatNameHash(0, rcFCB->PathName);
60 DPRINT("%08x (%03x) '%S'\n", rcFCB->Hash.Hash, rcFCB->Hash.Hash % FCB_HASH_TABLE_SIZE, pFileName);
62 rcFCB->Hash.self = rcFCB;
63 rcFCB->ShortHash.self = rcFCB;
64 ExInitializeResourceLite(&rcFCB->PagingIoResource);
65 ExInitializeResourceLite(&rcFCB->MainResource);
66 FsRtlInitializeFileLock(&rcFCB->FileLock, NULL, NULL);
71 vfatDestroyCCB(PVFATCCB pCcb)
73 if (pCcb->DirectorySearchPattern)
75 ExFreePool(pCcb->DirectorySearchPattern);
77 ExFreeToNPagedLookasideList(&VfatGlobalData->CcbLookasideList, pCcb);
81 vfatDestroyFCB(PVFATFCB pFCB)
83 FsRtlUninitializeFileLock(&pFCB->FileLock);
84 ExDeleteResourceLite(&pFCB->PagingIoResource);
85 ExDeleteResourceLite(&pFCB->MainResource);
86 ExFreeToNPagedLookasideList(&VfatGlobalData->FcbLookasideList, pFCB);
90 vfatFCBIsDirectory(PVFATFCB FCB)
92 return FCB->entry.Attrib & FILE_ATTRIBUTE_DIRECTORY;
96 vfatFCBIsRoot(PVFATFCB FCB)
98 return FCB->PathName[0] == L'\\' && FCB->PathName[1] == 0 ? TRUE : FALSE;
102 vfatReleaseFCB(PDEVICE_EXTENSION pVCB, PVFATFCB pFCB)
110 DPRINT ("releasing FCB at %x: %S, refCount:%d\n",
117 Index = pFCB->Hash.Hash % FCB_HASH_TABLE_SIZE;
118 ShortIndex = pFCB->ShortHash.Hash % FCB_HASH_TABLE_SIZE;
119 KeAcquireSpinLock (&pVCB->FcbListLock, &oldIrql);
121 if (pFCB->RefCount <= 0 && (!vfatFCBIsDirectory (pFCB) || pFCB->Flags & FCB_DELETE_PENDING))
123 tmpFcb = pFCB->parentFcb;
124 RemoveEntryList (&pFCB->FcbListEntry);
125 if (pFCB->Hash.Hash != pFCB->ShortHash.Hash)
127 entry = pVCB->FcbHashTable[ShortIndex];
128 if (entry->self == pFCB)
130 pVCB->FcbHashTable[ShortIndex] = entry->next;
134 while (entry->next->self != pFCB)
138 entry->next = pFCB->ShortHash.next;
141 entry = pVCB->FcbHashTable[Index];
142 if (entry->self == pFCB)
144 pVCB->FcbHashTable[Index] = entry->next;
148 while (entry->next->self != pFCB)
152 entry->next = pFCB->Hash.next;
154 KeReleaseSpinLock (&pVCB->FcbListLock, oldIrql);
155 if (vfatFCBIsDirectory(pFCB))
157 /* Uninitialize file cache if initialized for this file object. */
158 if (pFCB->FileObject->SectionObjectPointer->SharedCacheMap)
160 CcRosReleaseFileCache(pFCB->FileObject);
162 vfatDestroyCCB(pFCB->FileObject->FsContext2);
163 pFCB->FileObject->FsContext2 = NULL;
164 pFCB->FileObject->FsContext = NULL;
165 ObDereferenceObject(pFCB->FileObject);
167 vfatDestroyFCB (pFCB);
171 KeReleaseSpinLock (&pVCB->FcbListLock, oldIrql);
179 vfatAddFCBToTable(PDEVICE_EXTENSION pVCB, PVFATFCB pFCB)
185 Index = pFCB->Hash.Hash % FCB_HASH_TABLE_SIZE;
186 ShortIndex = pFCB->ShortHash.Hash % FCB_HASH_TABLE_SIZE;
187 KeAcquireSpinLock (&pVCB->FcbListLock, &oldIrql);
188 InsertTailList (&pVCB->FcbListHead, &pFCB->FcbListEntry);
190 pFCB->Hash.next = pVCB->FcbHashTable[Index];
191 pVCB->FcbHashTable[Index] = &pFCB->Hash;
192 if (pFCB->Hash.Hash != pFCB->ShortHash.Hash)
194 pFCB->ShortHash.next = pVCB->FcbHashTable[ShortIndex];
195 pVCB->FcbHashTable[ShortIndex] = &pFCB->ShortHash;
199 pFCB->parentFcb->RefCount++;
201 KeReleaseSpinLock (&pVCB->FcbListLock, oldIrql);
205 vfatGrabFCBFromTable(PDEVICE_EXTENSION pVCB, PWSTR pFileName)
210 PWCHAR ObjectName = NULL;
215 Hash = vfatNameHash(0, pFileName);
217 KeAcquireSpinLock (&pVCB->FcbListLock, &oldIrql);
218 entry = pVCB->FcbHashTable[Hash % FCB_HASH_TABLE_SIZE];
222 if (entry->Hash == Hash)
225 if (rcFCB->Hash.Hash == Hash)
227 /* compare the long name */
228 if (!_wcsicmp(pFileName, rcFCB->PathName))
231 KeReleaseSpinLock (&pVCB->FcbListLock, oldIrql);
237 len = rcFCB->ObjectName - rcFCB->PathName + 1;
238 if (ObjectName == NULL)
240 ObjectName = wcsrchr(pFileName, L'\\');
241 if (ObjectName == NULL)
243 ObjectName = pFileName;
251 /* compare the short name and the directory */
252 if (!_wcsicmp(ObjectName, rcFCB->ShortName) && !_wcsnicmp(pFileName, rcFCB->PathName, len))
255 KeReleaseSpinLock (&pVCB->FcbListLock, oldIrql);
262 KeReleaseSpinLock (&pVCB->FcbListLock, oldIrql);
268 vfatFCBInitializeCacheFromVolume (PVCB vcb, PVFATFCB fcb)
271 PFILE_OBJECT fileObject;
272 ULONG fileCacheQuantum;
275 fileObject = IoCreateStreamFileObject (NULL, vcb->StorageDevice);
277 newCCB = ExAllocateFromNPagedLookasideList(&VfatGlobalData->CcbLookasideList);
280 return STATUS_INSUFFICIENT_RESOURCES;
282 memset (newCCB, 0, sizeof (VFATCCB));
284 fileObject->Flags |= FO_FCB_IS_VALID | FO_DIRECT_CACHE_PAGING_READ;
285 fileObject->SectionObjectPointer = &fcb->SectionObjectPointers;
286 fileObject->FsContext = fcb;
287 fileObject->FsContext2 = newCCB;
288 fcb->FileObject = fileObject;
291 fileCacheQuantum = (vcb->FatInfo.BytesPerCluster >= PAGE_SIZE) ?
292 vcb->FatInfo.BytesPerCluster : PAGE_SIZE;
294 status = CcRosInitializeFileCache (fileObject,
296 if (!NT_SUCCESS (status))
298 DbgPrint ("CcRosInitializeFileCache failed\n");
302 fcb->Flags |= FCB_CACHE_INITIALIZED;
308 vfatMakeRootFCB(PDEVICE_EXTENSION pVCB)
311 ULONG FirstCluster, CurrentCluster, Size = 0;
312 NTSTATUS Status = STATUS_SUCCESS;
314 FCB = vfatNewFCB(L"\\");
315 memset(FCB->entry.Filename, ' ', 11);
316 FCB->ShortName[0] = L'\\';
317 FCB->ShortName[1] = 0;
318 FCB->ShortHash.Hash = FCB->Hash.Hash;
319 FCB->entry.FileSize = pVCB->FatInfo.rootDirectorySectors * pVCB->FatInfo.BytesPerSector;
320 FCB->entry.Attrib = FILE_ATTRIBUTE_DIRECTORY;
321 if (pVCB->FatInfo.FatType == FAT32)
323 CurrentCluster = FirstCluster = pVCB->FatInfo.RootCluster;
324 FCB->entry.FirstCluster = (unsigned short)(FirstCluster & 0xffff);
325 FCB->entry.FirstClusterHigh = (unsigned short)(FirstCluster >> 16);
327 while (CurrentCluster != 0xffffffff && NT_SUCCESS(Status))
329 Size += pVCB->FatInfo.BytesPerCluster;
330 Status = NextCluster (pVCB, FirstCluster, &CurrentCluster, FALSE);
335 FCB->entry.FirstCluster = 1;
336 Size = pVCB->FatInfo.rootDirectorySectors * pVCB->FatInfo.BytesPerSector;
340 FCB->RFCB.FileSize.QuadPart = Size;
341 FCB->RFCB.ValidDataLength.QuadPart = Size;
342 FCB->RFCB.AllocationSize.QuadPart = Size;
344 vfatFCBInitializeCacheFromVolume(pVCB, FCB);
345 vfatAddFCBToTable(pVCB, FCB);
351 vfatOpenRootFCB(PDEVICE_EXTENSION pVCB)
355 FCB = vfatGrabFCBFromTable (pVCB, L"\\");
358 FCB = vfatMakeRootFCB (pVCB);
365 vfatMakeFCBFromDirEntry(PVCB vcb,
366 PVFATFCB directoryFCB,
368 PFAT_DIR_ENTRY dirEntry,
374 WCHAR pathName [MAX_PATH];
375 WCHAR entryName [14];
379 if (longName [0] != 0 && wcslen (directoryFCB->PathName) +
380 sizeof(WCHAR) + wcslen (longName) > MAX_PATH)
382 return STATUS_OBJECT_NAME_INVALID;
384 wcscpy (pathName, directoryFCB->PathName);
385 if (!vfatFCBIsRoot (directoryFCB))
387 wcscat (pathName, L"\\");
389 hash = vfatNameHash(0, pathName);
390 vfatGetDirEntryName (dirEntry, entryName);
391 if (longName [0] != 0)
393 wcscat (pathName, longName);
397 wcscat (pathName, entryName);
399 rcFCB = vfatNewFCB (pathName);
400 memcpy (&rcFCB->entry, dirEntry, sizeof (FAT_DIR_ENTRY));
401 wcscpy(rcFCB->ShortName, entryName);
402 rcFCB->ShortHash.Hash = vfatNameHash(hash, entryName);
404 if (vfatFCBIsDirectory(rcFCB))
406 ULONG FirstCluster, CurrentCluster;
409 FirstCluster = vfatDirEntryGetFirstCluster (vcb, &rcFCB->entry);
410 if (FirstCluster == 1)
412 Size = vcb->FatInfo.rootDirectorySectors * vcb->FatInfo.BytesPerSector;
416 CurrentCluster = FirstCluster;
417 while (CurrentCluster != 0xffffffff)
419 Size += vcb->FatInfo.BytesPerCluster;
420 Status = NextCluster (vcb, FirstCluster, &CurrentCluster, FALSE);
426 Size = rcFCB->entry.FileSize;
428 rcFCB->dirIndex = dirIndex;
429 rcFCB->startIndex = startIndex;
430 rcFCB->RFCB.FileSize.QuadPart = Size;
431 rcFCB->RFCB.ValidDataLength.QuadPart = Size;
432 rcFCB->RFCB.AllocationSize.QuadPart = ROUND_UP(Size, vcb->FatInfo.BytesPerCluster);
434 if (vfatFCBIsDirectory(rcFCB))
436 vfatFCBInitializeCacheFromVolume(vcb, rcFCB);
438 rcFCB->parentFcb = directoryFCB;
439 vfatAddFCBToTable (vcb, rcFCB);
442 return STATUS_SUCCESS;
446 vfatAttachFCBToFileObject (PDEVICE_EXTENSION vcb,
448 PFILE_OBJECT fileObject)
452 newCCB = ExAllocateFromNPagedLookasideList(&VfatGlobalData->CcbLookasideList);
455 return STATUS_INSUFFICIENT_RESOURCES;
457 memset (newCCB, 0, sizeof (VFATCCB));
459 fileObject->Flags = fileObject->Flags | FO_FCB_IS_VALID |
460 FO_DIRECT_CACHE_PAGING_READ;
461 fileObject->SectionObjectPointer = &fcb->SectionObjectPointers;
462 fileObject->FsContext = fcb;
463 fileObject->FsContext2 = newCCB;
464 DPRINT ("file open: fcb:%x file size: %d\n", fcb, fcb->entry.FileSize);
466 return STATUS_SUCCESS;
470 vfatDirFindFile (PDEVICE_EXTENSION pDeviceExt,
471 PVFATFCB pDirectoryFCB,
473 PVFATFCB * pFoundFCB)
475 ULONG directoryIndex;
478 WCHAR defaultFileName [2];
479 WCHAR currentLongName [256];
480 FAT_DIR_ENTRY currentDirEntry;
481 WCHAR currentEntryName [256];
482 PVOID Context = NULL;
486 assert (pDirectoryFCB);
487 assert (pFileToFind);
489 DPRINT ("vfatDirFindFile(VCB:%08x, dirFCB:%08x, File:%S)\n",
493 DPRINT ("Dir Path:%S\n", pDirectoryFCB->PathName);
495 // default to '.' if no filename specified
496 if (wcslen (pFileToFind) == 0)
498 defaultFileName [0] = L'.';
499 defaultFileName [1] = 0;
500 pFileToFind = defaultFileName;
506 status = vfatGetNextDirEntry(&Context,
513 if (status == STATUS_NO_MORE_ENTRIES)
515 return STATUS_OBJECT_NAME_NOT_FOUND;
518 DPRINT (" Index:%d longName:%S\n",
522 if (!vfatIsDirEntryVolume(¤tDirEntry))
524 if (currentLongName [0] != L'\0' && wstrcmpjoki (currentLongName, pFileToFind))
526 DPRINT ("Match found, %S\n", currentLongName);
527 status = vfatMakeFCBFromDirEntry (pDeviceExt,
534 CcUnpinData(Context);
539 vfatGetDirEntryName (¤tDirEntry, currentEntryName);
540 DPRINT (" entryName:%S\n", currentEntryName);
542 if (wstrcmpjoki (currentEntryName, pFileToFind))
544 DPRINT ("Match found, %S\n", currentEntryName);
545 status = vfatMakeFCBFromDirEntry (pDeviceExt,
552 CcUnpinData(Context);
560 return STATUS_OBJECT_NAME_NOT_FOUND;
564 vfatGetFCBForFile (PDEVICE_EXTENSION pVCB,
565 PVFATFCB *pParentFCB,
567 const PWSTR pFileName)
570 WCHAR pathName [MAX_PATH];
571 WCHAR elementName [MAX_PATH];
572 PWCHAR currentElement;
576 DPRINT ("vfatGetFCBForFile (%x,%x,%x,%S)\n",
582 // Trivial case, open of the root directory on volume
583 if (pFileName [0] == L'\0' || wcscmp (pFileName, L"\\") == 0)
585 DPRINT ("returning root FCB\n");
587 FCB = vfatOpenRootFCB (pVCB);
591 return (FCB != NULL) ? STATUS_SUCCESS : STATUS_OBJECT_PATH_NOT_FOUND;
594 currentElement = wcsrchr(pFileName, L'\\');
595 wcsncpy(pathName, pFileName, currentElement - pFileName);
596 pathName[currentElement - pFileName] = L'\0';
599 FCB = vfatGrabFCBFromTable(pVCB, pathName);
602 currentElement = pFileName + 1;
603 wcscpy (pathName, L"\\");
604 FCB = vfatOpenRootFCB (pVCB);
608 // Parse filename and check each path element for existance and access
609 while (vfatGetNextPathElement (currentElement) != 0)
611 // Skip blank directory levels
612 if ((vfatGetNextPathElement (currentElement) - currentElement) == 0)
618 DPRINT ("Parsing, currentElement:%S\n", currentElement);
619 DPRINT (" parentFCB:%x FCB:%x\n", parentFCB, FCB);
621 // descend to next directory level
624 vfatReleaseFCB (pVCB, parentFCB);
627 // fail if element in FCB is not a directory
628 if (!vfatFCBIsDirectory (FCB))
630 DPRINT ("Element in requested path is not a directory\n");
632 vfatReleaseFCB (pVCB, FCB);
637 return STATUS_OBJECT_PATH_NOT_FOUND;
641 // Extract next directory level into dirName
642 vfatWSubString (pathName,
644 vfatGetNextPathElement (currentElement) - pFileName);
645 DPRINT (" pathName:%S\n", pathName);
647 FCB = vfatGrabFCBFromTable (pVCB, pathName);
650 vfatWSubString (elementName,
652 vfatGetNextPathElement (currentElement) - currentElement);
653 DPRINT (" elementName:%S\n", elementName);
655 status = vfatDirFindFile (pVCB, parentFCB, elementName, &FCB);
656 if (status == STATUS_OBJECT_NAME_NOT_FOUND)
658 *pParentFCB = parentFCB;
660 currentElement = vfatGetNextPathElement(currentElement);
661 if (*currentElement == L'\0' || vfatGetNextPathElement(currentElement + 1) == 0)
663 return STATUS_OBJECT_NAME_NOT_FOUND;
667 return STATUS_OBJECT_PATH_NOT_FOUND;
670 else if (!NT_SUCCESS (status))
672 vfatReleaseFCB (pVCB, parentFCB);
679 currentElement = vfatGetNextPathElement (currentElement);
682 *pParentFCB = parentFCB;
685 return STATUS_SUCCESS;