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>
23 /* -------------------------------------------------------- DEFINES */
25 #define TAG(A, B, C, D) (ULONG)(((A)<<0) + ((B)<<8) + ((C)<<16) + ((D)<<24))
26 #define TAG_FCB TAG('V', 'F', 'C', 'B')
28 #define ROUND_UP(N, S) ((((N) + (S) - 1) / (S)) * (S))
30 /* -------------------------------------------------------- PUBLICS */
32 ULONG vfatNameHash(ULONG hash, PWCHAR name)
38 hash = (hash + (c << 4) + (c >> 4)) * 11;
44 vfatNewFCB(PWCHAR pFileName)
48 rcFCB = ExAllocateFromNPagedLookasideList(&VfatGlobalData->FcbLookasideList);
49 memset (rcFCB, 0, sizeof (VFATFCB));
52 wcscpy (rcFCB->PathName, pFileName);
53 rcFCB->ObjectName = wcsrchr(rcFCB->PathName, L'\\');
54 if (rcFCB->ObjectName == NULL)
56 rcFCB->ObjectName = rcFCB->PathName;
58 rcFCB->Hash.Hash = vfatNameHash(0, rcFCB->PathName);
59 DPRINT("%08x (%03x) '%S'\n", rcFCB->Hash.Hash, rcFCB->Hash.Hash % FCB_HASH_TABLE_SIZE, pFileName);
61 rcFCB->Hash.self = rcFCB;
62 rcFCB->ShortHash.self = rcFCB;
63 ExInitializeResourceLite(&rcFCB->PagingIoResource);
64 ExInitializeResourceLite(&rcFCB->MainResource);
65 FsRtlInitializeFileLock(&rcFCB->FileLock, NULL, NULL);
70 vfatDestroyCCB(PVFATCCB pCcb)
72 if (pCcb->DirectorySearchPattern)
74 ExFreePool(pCcb->DirectorySearchPattern);
76 ExFreeToNPagedLookasideList(&VfatGlobalData->CcbLookasideList, pCcb);
80 vfatDestroyFCB(PVFATFCB pFCB)
82 FsRtlUninitializeFileLock(&pFCB->FileLock);
83 ExDeleteResourceLite(&pFCB->PagingIoResource);
84 ExDeleteResourceLite(&pFCB->MainResource);
85 if ((pFCB->Flags & FCB_IS_PAGE_FILE) && pFCB->FatChainSize)
86 ExFreePool(pFCB->FatChain);
87 ExFreeToNPagedLookasideList(&VfatGlobalData->FcbLookasideList, pFCB);
91 vfatFCBIsDirectory(PVFATFCB FCB)
93 return FCB->entry.Attrib & FILE_ATTRIBUTE_DIRECTORY;
97 vfatFCBIsRoot(PVFATFCB FCB)
99 return FCB->PathName[0] == L'\\' && FCB->PathName[1] == 0 ? TRUE : FALSE;
103 vfatReleaseFCB(PDEVICE_EXTENSION pVCB, PVFATFCB pFCB)
111 DPRINT ("releasing FCB at %x: %S, refCount:%d\n",
118 Index = pFCB->Hash.Hash % FCB_HASH_TABLE_SIZE;
119 ShortIndex = pFCB->ShortHash.Hash % FCB_HASH_TABLE_SIZE;
120 KeAcquireSpinLock (&pVCB->FcbListLock, &oldIrql);
122 if (pFCB->RefCount <= 0 && (!vfatFCBIsDirectory (pFCB) || pFCB->Flags & FCB_DELETE_PENDING))
124 tmpFcb = pFCB->parentFcb;
125 RemoveEntryList (&pFCB->FcbListEntry);
126 if (pFCB->Hash.Hash != pFCB->ShortHash.Hash)
128 entry = pVCB->FcbHashTable[ShortIndex];
129 if (entry->self == pFCB)
131 pVCB->FcbHashTable[ShortIndex] = entry->next;
135 while (entry->next->self != pFCB)
139 entry->next = pFCB->ShortHash.next;
142 entry = pVCB->FcbHashTable[Index];
143 if (entry->self == pFCB)
145 pVCB->FcbHashTable[Index] = entry->next;
149 while (entry->next->self != pFCB)
153 entry->next = pFCB->Hash.next;
155 KeReleaseSpinLock (&pVCB->FcbListLock, oldIrql);
156 if (vfatFCBIsDirectory(pFCB))
158 /* Uninitialize file cache if initialized for this file object. */
159 if (pFCB->FileObject->SectionObjectPointers->SharedCacheMap)
161 CcRosReleaseFileCache(pFCB->FileObject);
163 vfatDestroyCCB(pFCB->FileObject->FsContext2);
164 pFCB->FileObject->FsContext2 = NULL;
165 pFCB->FileObject->FsContext = NULL;
166 ObDereferenceObject(pFCB->FileObject);
168 vfatDestroyFCB (pFCB);
172 KeReleaseSpinLock (&pVCB->FcbListLock, oldIrql);
180 vfatAddFCBToTable(PDEVICE_EXTENSION pVCB, PVFATFCB pFCB)
186 Index = pFCB->Hash.Hash % FCB_HASH_TABLE_SIZE;
187 ShortIndex = pFCB->ShortHash.Hash % FCB_HASH_TABLE_SIZE;
188 KeAcquireSpinLock (&pVCB->FcbListLock, &oldIrql);
189 InsertTailList (&pVCB->FcbListHead, &pFCB->FcbListEntry);
191 pFCB->Hash.next = pVCB->FcbHashTable[Index];
192 pVCB->FcbHashTable[Index] = &pFCB->Hash;
193 if (pFCB->Hash.Hash != pFCB->ShortHash.Hash)
195 pFCB->ShortHash.next = pVCB->FcbHashTable[ShortIndex];
196 pVCB->FcbHashTable[ShortIndex] = &pFCB->ShortHash;
200 pFCB->parentFcb->RefCount++;
202 KeReleaseSpinLock (&pVCB->FcbListLock, oldIrql);
206 vfatGrabFCBFromTable(PDEVICE_EXTENSION pVCB, PWSTR pFileName)
210 PLIST_ENTRY current_entry;
212 PWCHAR ObjectName = NULL;
219 Hash = vfatNameHash(0, pFileName);
221 KeAcquireSpinLock (&pVCB->FcbListLock, &oldIrql);
222 entry = pVCB->FcbHashTable[Hash % FCB_HASH_TABLE_SIZE];
226 if (entry->Hash == Hash)
229 if (rcFCB->Hash.Hash == Hash)
231 /* compare the long name */
232 if (!_wcsicmp(pFileName, rcFCB->PathName))
235 KeReleaseSpinLock (&pVCB->FcbListLock, oldIrql);
241 len = rcFCB->ObjectName - rcFCB->PathName + 1;
242 if (ObjectName == NULL)
244 ObjectName = wcsrchr(pFileName, L'\\');
245 if (ObjectName == NULL)
247 ObjectName = pFileName;
255 /* compare the short name and the directory */
256 if (!_wcsicmp(ObjectName, rcFCB->ShortName) && !_wcsnicmp(pFileName, rcFCB->PathName, len))
259 KeReleaseSpinLock (&pVCB->FcbListLock, oldIrql);
266 KeReleaseSpinLock (&pVCB->FcbListLock, oldIrql);
272 vfatFCBInitializeCacheFromVolume (PVCB vcb, PVFATFCB fcb)
275 PFILE_OBJECT fileObject;
276 ULONG fileCacheQuantum;
279 fileObject = IoCreateStreamFileObject (NULL, vcb->StorageDevice);
281 newCCB = ExAllocateFromNPagedLookasideList(&VfatGlobalData->CcbLookasideList);
284 return STATUS_INSUFFICIENT_RESOURCES;
286 memset (newCCB, 0, sizeof (VFATCCB));
288 fileObject->Flags |= FO_FCB_IS_VALID | FO_DIRECT_CACHE_PAGING_READ;
289 fileObject->SectionObjectPointers = &fcb->SectionObjectPointers;
290 fileObject->FsContext = fcb;
291 fileObject->FsContext2 = newCCB;
292 fcb->FileObject = fileObject;
295 fileCacheQuantum = (vcb->FatInfo.BytesPerCluster >= PAGE_SIZE) ?
296 vcb->FatInfo.BytesPerCluster : PAGE_SIZE;
298 status = CcRosInitializeFileCache (fileObject,
300 if (!NT_SUCCESS (status))
302 DbgPrint ("CcRosInitializeFileCache failed\n");
306 fcb->Flags |= FCB_CACHE_INITIALIZED;
312 vfatMakeRootFCB(PDEVICE_EXTENSION pVCB)
315 ULONG FirstCluster, CurrentCluster, Size = 0;
316 NTSTATUS Status = STATUS_SUCCESS;
318 FCB = vfatNewFCB(L"\\");
319 memset(FCB->entry.Filename, ' ', 11);
320 FCB->ShortName[0] = L'\\';
321 FCB->ShortName[1] = 0;
322 FCB->ShortHash.Hash = FCB->Hash.Hash;
323 FCB->entry.FileSize = pVCB->FatInfo.rootDirectorySectors * pVCB->FatInfo.BytesPerSector;
324 FCB->entry.Attrib = FILE_ATTRIBUTE_DIRECTORY;
325 if (pVCB->FatInfo.FatType == FAT32)
327 CurrentCluster = FirstCluster = pVCB->FatInfo.RootCluster;
328 FCB->entry.FirstCluster = FirstCluster & 0xffff;
329 FCB->entry.FirstClusterHigh = FirstCluster >> 16;
331 while (CurrentCluster != 0xffffffff && NT_SUCCESS(Status))
333 Size += pVCB->FatInfo.BytesPerCluster;
334 Status = NextCluster (pVCB, NULL, FirstCluster, &CurrentCluster, FALSE);
339 FCB->entry.FirstCluster = 1;
340 Size = pVCB->FatInfo.rootDirectorySectors * pVCB->FatInfo.BytesPerSector;
344 FCB->RFCB.FileSize.QuadPart = Size;
345 FCB->RFCB.ValidDataLength.QuadPart = Size;
346 FCB->RFCB.AllocationSize.QuadPart = Size;
348 vfatFCBInitializeCacheFromVolume(pVCB, FCB);
349 vfatAddFCBToTable(pVCB, FCB);
355 vfatOpenRootFCB(PDEVICE_EXTENSION pVCB)
359 FCB = vfatGrabFCBFromTable (pVCB, L"\\");
362 FCB = vfatMakeRootFCB (pVCB);
369 vfatMakeFCBFromDirEntry(PVCB vcb,
370 PVFATFCB directoryFCB,
372 PFAT_DIR_ENTRY dirEntry,
378 WCHAR pathName [MAX_PATH];
379 WCHAR entryName [14];
383 if (longName [0] != 0 && wcslen (directoryFCB->PathName) +
384 sizeof(WCHAR) + wcslen (longName) > MAX_PATH)
386 return STATUS_OBJECT_NAME_INVALID;
388 wcscpy (pathName, directoryFCB->PathName);
389 if (!vfatFCBIsRoot (directoryFCB))
391 wcscat (pathName, L"\\");
393 hash = vfatNameHash(0, pathName);
394 vfatGetDirEntryName (dirEntry, entryName);
395 if (longName [0] != 0)
397 wcscat (pathName, longName);
401 wcscat (pathName, entryName);
403 rcFCB = vfatNewFCB (pathName);
404 memcpy (&rcFCB->entry, dirEntry, sizeof (FAT_DIR_ENTRY));
405 wcscpy(rcFCB->ShortName, entryName);
406 rcFCB->ShortHash.Hash = vfatNameHash(hash, entryName);
408 if (vfatFCBIsDirectory(rcFCB))
410 ULONG FirstCluster, CurrentCluster;
413 FirstCluster = vfatDirEntryGetFirstCluster (vcb, &rcFCB->entry);
414 if (FirstCluster == 1)
416 Size = vcb->FatInfo.rootDirectorySectors * vcb->FatInfo.BytesPerSector;
420 CurrentCluster = FirstCluster;
421 while (CurrentCluster != 0xffffffff)
423 Size += vcb->FatInfo.BytesPerCluster;
424 Status = NextCluster (vcb, NULL, FirstCluster, &CurrentCluster, FALSE);
430 Size = rcFCB->entry.FileSize;
432 rcFCB->dirIndex = dirIndex;
433 rcFCB->startIndex = startIndex;
434 rcFCB->RFCB.FileSize.QuadPart = Size;
435 rcFCB->RFCB.ValidDataLength.QuadPart = Size;
436 rcFCB->RFCB.AllocationSize.QuadPart = ROUND_UP(Size, vcb->FatInfo.BytesPerCluster);
438 if (vfatFCBIsDirectory(rcFCB))
440 vfatFCBInitializeCacheFromVolume(vcb, rcFCB);
442 rcFCB->parentFcb = directoryFCB;
443 vfatAddFCBToTable (vcb, rcFCB);
446 return STATUS_SUCCESS;
450 vfatAttachFCBToFileObject (PDEVICE_EXTENSION vcb,
452 PFILE_OBJECT fileObject)
457 newCCB = ExAllocateFromNPagedLookasideList(&VfatGlobalData->CcbLookasideList);
460 return STATUS_INSUFFICIENT_RESOURCES;
462 memset (newCCB, 0, sizeof (VFATCCB));
464 fileObject->Flags = fileObject->Flags | FO_FCB_IS_VALID |
465 FO_DIRECT_CACHE_PAGING_READ;
466 fileObject->SectionObjectPointers = &fcb->SectionObjectPointers;
467 fileObject->FsContext = fcb;
468 fileObject->FsContext2 = newCCB;
469 DPRINT ("file open: fcb:%x file size: %d\n", fcb, fcb->entry.FileSize);
471 return STATUS_SUCCESS;
475 vfatDirFindFile (PDEVICE_EXTENSION pDeviceExt,
476 PVFATFCB pDirectoryFCB,
478 PVFATFCB * pFoundFCB)
480 ULONG directoryIndex;
483 WCHAR defaultFileName [2];
484 WCHAR currentLongName [256];
485 FAT_DIR_ENTRY currentDirEntry;
486 WCHAR currentEntryName [256];
487 PVOID Context = NULL;
491 assert (pDirectoryFCB);
492 assert (pFileToFind);
494 DPRINT ("vfatDirFindFile(VCB:%08x, dirFCB:%08x, File:%S)\n",
498 DPRINT ("Dir Path:%S\n", pDirectoryFCB->PathName);
500 // default to '.' if no filename specified
501 if (wcslen (pFileToFind) == 0)
503 defaultFileName [0] = L'.';
504 defaultFileName [1] = 0;
505 pFileToFind = defaultFileName;
511 status = vfatGetNextDirEntry(&Context,
518 if (status == STATUS_NO_MORE_ENTRIES)
520 return STATUS_OBJECT_NAME_NOT_FOUND;
523 DPRINT (" Index:%d longName:%S\n",
527 if (!vfatIsDirEntryVolume(¤tDirEntry))
529 if (currentLongName [0] != L'\0' && wstrcmpjoki (currentLongName, pFileToFind))
531 DPRINT ("Match found, %S\n", currentLongName);
532 status = vfatMakeFCBFromDirEntry (pDeviceExt,
539 CcUnpinData(Context);
544 vfatGetDirEntryName (¤tDirEntry, currentEntryName);
545 DPRINT (" entryName:%S\n", currentEntryName);
547 if (wstrcmpjoki (currentEntryName, pFileToFind))
549 DPRINT ("Match found, %S\n", currentEntryName);
550 status = vfatMakeFCBFromDirEntry (pDeviceExt,
557 CcUnpinData(Context);
565 return STATUS_OBJECT_NAME_NOT_FOUND;
569 vfatGetFCBForFile (PDEVICE_EXTENSION pVCB,
570 PVFATFCB *pParentFCB,
572 const PWSTR pFileName)
575 WCHAR pathName [MAX_PATH];
576 WCHAR elementName [MAX_PATH];
577 PWCHAR currentElement;
581 DPRINT ("vfatGetFCBForFile (%x,%x,%x,%S)\n",
587 // Trivial case, open of the root directory on volume
588 if (pFileName [0] == L'\0' || wcscmp (pFileName, L"\\") == 0)
590 DPRINT ("returning root FCB\n");
592 FCB = vfatOpenRootFCB (pVCB);
596 return (FCB != NULL) ? STATUS_SUCCESS : STATUS_OBJECT_PATH_NOT_FOUND;
599 currentElement = wcsrchr(pFileName, L'\\');
600 wcsncpy(pathName, pFileName, currentElement - pFileName);
601 pathName[currentElement - pFileName] = L'\0';
604 FCB = vfatGrabFCBFromTable(pVCB, pathName);
607 currentElement = pFileName + 1;
608 wcscpy (pathName, L"\\");
609 FCB = vfatOpenRootFCB (pVCB);
613 // Parse filename and check each path element for existance and access
614 while (vfatGetNextPathElement (currentElement) != 0)
616 // Skip blank directory levels
617 if ((vfatGetNextPathElement (currentElement) - currentElement) == 0)
623 DPRINT ("Parsing, currentElement:%S\n", currentElement);
624 DPRINT (" parentFCB:%x FCB:%x\n", parentFCB, FCB);
626 // descend to next directory level
629 vfatReleaseFCB (pVCB, parentFCB);
632 // fail if element in FCB is not a directory
633 if (!vfatFCBIsDirectory (FCB))
635 DPRINT ("Element in requested path is not a directory\n");
637 vfatReleaseFCB (pVCB, FCB);
642 return STATUS_OBJECT_PATH_NOT_FOUND;
646 // Extract next directory level into dirName
647 vfatWSubString (pathName,
649 vfatGetNextPathElement (currentElement) - pFileName);
650 DPRINT (" pathName:%S\n", pathName);
652 FCB = vfatGrabFCBFromTable (pVCB, pathName);
655 vfatWSubString (elementName,
657 vfatGetNextPathElement (currentElement) - currentElement);
658 DPRINT (" elementName:%S\n", elementName);
660 status = vfatDirFindFile (pVCB, parentFCB, elementName, &FCB);
661 if (status == STATUS_OBJECT_NAME_NOT_FOUND)
663 *pParentFCB = parentFCB;
665 currentElement = vfatGetNextPathElement(currentElement);
666 if (*currentElement == L'\0' || vfatGetNextPathElement(currentElement + 1) == 0)
668 return STATUS_OBJECT_NAME_NOT_FOUND;
672 return STATUS_OBJECT_PATH_NOT_FOUND;
675 else if (!NT_SUCCESS (status))
677 vfatReleaseFCB (pVCB, parentFCB);
684 currentElement = vfatGetNextPathElement (currentElement);
687 *pParentFCB = parentFCB;
690 return STATUS_SUCCESS;