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->RFCB.Bcb != NULL)
161 CcRosReleaseFileCache(pFCB->FileObject, pFCB->RFCB.Bcb);
163 vfatDestroyCCB(pFCB->FileObject->FsContext2);
164 pFCB->FileObject->FsContext2 = 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 pFCB->pDevExt = pVCB;
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 = (PVOID) &fcb->RFCB;
291 fileObject->FsContext2 = newCCB;
293 newCCB->PtrFileObject = fileObject;
294 fcb->FileObject = fileObject;
298 fileCacheQuantum = (vcb->FatInfo.BytesPerCluster >= PAGE_SIZE) ?
299 vcb->FatInfo.BytesPerCluster : PAGE_SIZE;
301 status = CcRosInitializeFileCache (fileObject,
304 if (!NT_SUCCESS (status))
306 DbgPrint ("CcRosInitializeFileCache failed\n");
310 fcb->Flags |= FCB_CACHE_INITIALIZED;
316 vfatMakeRootFCB(PDEVICE_EXTENSION pVCB)
319 ULONG FirstCluster, CurrentCluster, Size = 0;
320 NTSTATUS Status = STATUS_SUCCESS;
322 FCB = vfatNewFCB(L"\\");
323 memset(FCB->entry.Filename, ' ', 11);
324 FCB->ShortName[0] = L'\\';
325 FCB->ShortName[1] = 0;
326 FCB->ShortHash.Hash = FCB->Hash.Hash;
327 FCB->entry.FileSize = pVCB->FatInfo.rootDirectorySectors * pVCB->FatInfo.BytesPerSector;
328 FCB->entry.Attrib = FILE_ATTRIBUTE_DIRECTORY;
329 if (pVCB->FatInfo.FatType == FAT32)
331 CurrentCluster = FirstCluster = pVCB->FatInfo.RootCluster;
332 FCB->entry.FirstCluster = FirstCluster & 0xffff;
333 FCB->entry.FirstClusterHigh = FirstCluster >> 16;
334 CurrentCluster = FirstCluster;
336 while (CurrentCluster != 0xffffffff && NT_SUCCESS(Status))
338 Size += pVCB->FatInfo.BytesPerCluster;
339 Status = NextCluster (pVCB, NULL, FirstCluster, &CurrentCluster, FALSE);
344 FCB->entry.FirstCluster = 1;
345 Size = pVCB->FatInfo.rootDirectorySectors * pVCB->FatInfo.BytesPerSector;
349 FCB->RFCB.FileSize.QuadPart = Size;
350 FCB->RFCB.ValidDataLength.QuadPart = Size;
351 FCB->RFCB.AllocationSize.QuadPart = Size;
353 vfatFCBInitializeCacheFromVolume(pVCB, FCB);
354 vfatAddFCBToTable(pVCB, FCB);
360 vfatOpenRootFCB(PDEVICE_EXTENSION pVCB)
364 FCB = vfatGrabFCBFromTable (pVCB, L"\\");
367 FCB = vfatMakeRootFCB (pVCB);
374 vfatMakeFCBFromDirEntry(PVCB vcb,
375 PVFATFCB directoryFCB,
377 PFAT_DIR_ENTRY dirEntry,
383 WCHAR pathName [MAX_PATH];
384 WCHAR entryName [14];
388 if (longName [0] != 0 && wcslen (directoryFCB->PathName) +
389 sizeof(WCHAR) + wcslen (longName) > MAX_PATH)
391 return STATUS_OBJECT_NAME_INVALID;
393 wcscpy (pathName, directoryFCB->PathName);
394 if (!vfatFCBIsRoot (directoryFCB))
396 wcscat (pathName, L"\\");
398 hash = vfatNameHash(0, pathName);
399 vfatGetDirEntryName (dirEntry, entryName);
400 if (longName [0] != 0)
402 wcscat (pathName, longName);
406 wcscat (pathName, entryName);
408 rcFCB = vfatNewFCB (pathName);
409 memcpy (&rcFCB->entry, dirEntry, sizeof (FAT_DIR_ENTRY));
410 wcscpy(rcFCB->ShortName, entryName);
411 rcFCB->ShortHash.Hash = vfatNameHash(hash, entryName);
413 if (vfatFCBIsDirectory(rcFCB))
415 ULONG FirstCluster, CurrentCluster;
418 FirstCluster = vfatDirEntryGetFirstCluster (vcb, &rcFCB->entry);
419 if (FirstCluster == 1)
421 Size = vcb->FatInfo.rootDirectorySectors * vcb->FatInfo.BytesPerSector;
425 CurrentCluster = FirstCluster;
426 while (CurrentCluster != 0xffffffff)
428 Size += vcb->FatInfo.BytesPerCluster;
429 Status = NextCluster (vcb, NULL, FirstCluster, &CurrentCluster, FALSE);
435 Size = rcFCB->entry.FileSize;
437 rcFCB->dirIndex = dirIndex;
438 rcFCB->startIndex = startIndex;
439 rcFCB->RFCB.FileSize.QuadPart = Size;
440 rcFCB->RFCB.ValidDataLength.QuadPart = Size;
441 rcFCB->RFCB.AllocationSize.QuadPart = ROUND_UP(Size, vcb->FatInfo.BytesPerCluster);
443 if (vfatFCBIsDirectory(rcFCB))
445 vfatFCBInitializeCacheFromVolume(vcb, rcFCB);
447 rcFCB->parentFcb = directoryFCB;
448 vfatAddFCBToTable (vcb, rcFCB);
451 return STATUS_SUCCESS;
455 vfatAttachFCBToFileObject (PDEVICE_EXTENSION vcb,
457 PFILE_OBJECT fileObject)
462 newCCB = ExAllocateFromNPagedLookasideList(&VfatGlobalData->CcbLookasideList);
465 return STATUS_INSUFFICIENT_RESOURCES;
467 memset (newCCB, 0, sizeof (VFATCCB));
469 fileObject->Flags = fileObject->Flags | FO_FCB_IS_VALID |
470 FO_DIRECT_CACHE_PAGING_READ;
471 fileObject->SectionObjectPointers = &fcb->SectionObjectPointers;
472 fileObject->FsContext = (PVOID) &fcb->RFCB;
473 fileObject->FsContext2 = newCCB;
475 newCCB->PtrFileObject = fileObject;
477 DPRINT ("file open: fcb:%x file size: %d\n", fcb, fcb->entry.FileSize);
479 return STATUS_SUCCESS;
483 vfatDirFindFile (PDEVICE_EXTENSION pDeviceExt,
484 PVFATFCB pDirectoryFCB,
486 PVFATFCB * pFoundFCB)
488 ULONG directoryIndex;
491 WCHAR defaultFileName [2];
492 WCHAR currentLongName [256];
493 FAT_DIR_ENTRY currentDirEntry;
494 WCHAR currentEntryName [256];
495 PVOID Context = NULL;
499 assert (pDirectoryFCB);
500 assert (pFileToFind);
502 DPRINT ("vfatDirFindFile(VCB:%08x, dirFCB:%08x, File:%S)\n",
506 DPRINT ("Dir Path:%S\n", pDirectoryFCB->PathName);
508 // default to '.' if no filename specified
509 if (wcslen (pFileToFind) == 0)
511 defaultFileName [0] = L'.';
512 defaultFileName [1] = 0;
513 pFileToFind = defaultFileName;
519 status = vfatGetNextDirEntry(&Context,
526 if (status == STATUS_NO_MORE_ENTRIES)
528 return STATUS_OBJECT_NAME_NOT_FOUND;
531 DPRINT (" Index:%d longName:%S\n",
535 if (!vfatIsDirEntryVolume(¤tDirEntry))
537 if (currentLongName [0] != L'\0' && wstrcmpjoki (currentLongName, pFileToFind))
539 DPRINT ("Match found, %S\n", currentLongName);
540 status = vfatMakeFCBFromDirEntry (pDeviceExt,
547 CcUnpinData(Context);
552 vfatGetDirEntryName (¤tDirEntry, currentEntryName);
553 DPRINT (" entryName:%S\n", currentEntryName);
555 if (wstrcmpjoki (currentEntryName, pFileToFind))
557 DPRINT ("Match found, %S\n", currentEntryName);
558 status = vfatMakeFCBFromDirEntry (pDeviceExt,
565 CcUnpinData(Context);
573 return STATUS_OBJECT_NAME_NOT_FOUND;
577 vfatGetFCBForFile (PDEVICE_EXTENSION pVCB,
578 PVFATFCB *pParentFCB,
580 const PWSTR pFileName)
583 WCHAR pathName [MAX_PATH];
584 WCHAR elementName [MAX_PATH];
585 PWCHAR currentElement;
589 DPRINT ("vfatGetFCBForFile (%x,%x,%x,%S)\n",
595 // Trivial case, open of the root directory on volume
596 if (pFileName [0] == L'\0' || wcscmp (pFileName, L"\\") == 0)
598 DPRINT ("returning root FCB\n");
600 FCB = vfatOpenRootFCB (pVCB);
604 return (FCB != NULL) ? STATUS_SUCCESS : STATUS_OBJECT_PATH_NOT_FOUND;
607 currentElement = wcsrchr(pFileName, L'\\');
608 wcsncpy(pathName, pFileName, currentElement - pFileName);
609 pathName[currentElement - pFileName] = L'\0';
612 FCB = vfatGrabFCBFromTable(pVCB, pathName);
615 currentElement = pFileName + 1;
616 wcscpy (pathName, L"\\");
617 FCB = vfatOpenRootFCB (pVCB);
621 // Parse filename and check each path element for existance and access
622 while (vfatGetNextPathElement (currentElement) != 0)
624 // Skip blank directory levels
625 if ((vfatGetNextPathElement (currentElement) - currentElement) == 0)
631 DPRINT ("Parsing, currentElement:%S\n", currentElement);
632 DPRINT (" parentFCB:%x FCB:%x\n", parentFCB, FCB);
634 // descend to next directory level
637 vfatReleaseFCB (pVCB, parentFCB);
640 // fail if element in FCB is not a directory
641 if (!vfatFCBIsDirectory (FCB))
643 DPRINT ("Element in requested path is not a directory\n");
645 vfatReleaseFCB (pVCB, FCB);
650 return STATUS_OBJECT_PATH_NOT_FOUND;
654 // Extract next directory level into dirName
655 vfatWSubString (pathName,
657 vfatGetNextPathElement (currentElement) - pFileName);
658 DPRINT (" pathName:%S\n", pathName);
660 FCB = vfatGrabFCBFromTable (pVCB, pathName);
663 vfatWSubString (elementName,
665 vfatGetNextPathElement (currentElement) - currentElement);
666 DPRINT (" elementName:%S\n", elementName);
668 status = vfatDirFindFile (pVCB, parentFCB, elementName, &FCB);
669 if (status == STATUS_OBJECT_NAME_NOT_FOUND)
671 *pParentFCB = parentFCB;
673 currentElement = vfatGetNextPathElement(currentElement);
674 if (*currentElement == L'\0' || vfatGetNextPathElement(currentElement + 1) == 0)
676 return STATUS_OBJECT_NAME_NOT_FOUND;
680 return STATUS_OBJECT_PATH_NOT_FOUND;
683 else if (!NT_SUCCESS (status))
685 vfatReleaseFCB (pVCB, parentFCB);
692 currentElement = vfatGetNextPathElement (currentElement);
695 *pParentFCB = parentFCB;
698 return STATUS_SUCCESS;