1 /* $Id: fcb.c,v 1.7 2001/07/25 17:39:34 hbirr Exp $
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 /* -------------------------------------------------------- PUBLICS */
31 vfatNewFCB(PWCHAR pFileName
)
35 rcFCB
= ExAllocatePoolWithTag (NonPagedPool
, sizeof (VFATFCB
), TAG_FCB
);
36 memset (rcFCB
, 0, sizeof (VFATFCB
));
39 wcscpy (rcFCB
->PathName
, pFileName
);
40 if (wcsrchr (rcFCB
->PathName
, '\\') != 0)
42 rcFCB
->ObjectName
= wcsrchr (rcFCB
->PathName
, '\\');
46 rcFCB
->ObjectName
= rcFCB
->PathName
;
54 vfatDestroyFCB(PVFATFCB pFCB
)
60 vfatFCBIsDirectory(PDEVICE_EXTENSION pVCB
, PVFATFCB FCB
)
62 return FCB
->entry
.Attrib
& FILE_ATTRIBUTE_DIRECTORY
;
66 vfatFCBIsRoot(PVFATFCB FCB
)
68 return wcscmp (FCB
->PathName
, L
"\\") == 0;
72 vfatGrabFCB(PDEVICE_EXTENSION pVCB
, PVFATFCB pFCB
)
76 DPRINT ("grabbing FCB at %x: %S, refCount:%d\n",
81 KeAcquireSpinLock (&pVCB
->FcbListLock
, &oldIrql
);
83 KeReleaseSpinLock (&pVCB
->FcbListLock
, oldIrql
);
87 vfatReleaseFCB(PDEVICE_EXTENSION pVCB
, PVFATFCB pFCB
)
91 DPRINT ("releasing FCB at %x: %S, refCount:%d\n",
96 KeAcquireSpinLock (&pVCB
->FcbListLock
, &oldIrql
);
98 if (pFCB
->RefCount
<= 0 && !vfatFCBIsDirectory (pVCB
, pFCB
))
100 RemoveEntryList (&pFCB
->FcbListEntry
);
101 CcRosReleaseFileCache (NULL
, pFCB
->RFCB
.Bcb
);
102 vfatDestroyFCB (pFCB
);
104 KeReleaseSpinLock (&pVCB
->FcbListLock
, oldIrql
);
108 vfatAddFCBToTable(PDEVICE_EXTENSION pVCB
, PVFATFCB pFCB
)
112 KeAcquireSpinLock (&pVCB
->FcbListLock
, &oldIrql
);
113 pFCB
->pDevExt
= pVCB
;
114 InsertTailList (&pVCB
->FcbListHead
, &pFCB
->FcbListEntry
);
115 KeReleaseSpinLock (&pVCB
->FcbListLock
, oldIrql
);
119 vfatGrabFCBFromTable(PDEVICE_EXTENSION pVCB
, PWSTR pFileName
)
123 PLIST_ENTRY current_entry
;
125 KeAcquireSpinLock (&pVCB
->FcbListLock
, &oldIrql
);
126 current_entry
= pVCB
->FcbListHead
.Flink
;
127 while (current_entry
!= &pVCB
->FcbListHead
)
129 rcFCB
= CONTAINING_RECORD (current_entry
, VFATFCB
, FcbListEntry
);
131 if (wstrcmpi (pFileName
, rcFCB
->PathName
))
134 KeReleaseSpinLock (&pVCB
->FcbListLock
, oldIrql
);
138 //FIXME: need to compare against short name in FCB here
140 current_entry
= current_entry
->Flink
;
142 KeReleaseSpinLock (&pVCB
->FcbListLock
, oldIrql
);
148 vfatFCBInitializeCache (PVCB vcb
, PVFATFCB fcb
)
151 PFILE_OBJECT fileObject
;
152 ULONG bytesPerCluster
;
153 ULONG fileCacheQuantum
;
156 fileObject
= IoCreateStreamFileObject (NULL
, vcb
->StorageDevice
);
158 newCCB
= ExAllocatePoolWithTag (NonPagedPool
, sizeof (VFATCCB
), TAG_CCB
);
161 return STATUS_INSUFFICIENT_RESOURCES
;
163 memset (newCCB
, 0, sizeof (VFATCCB
));
165 fileObject
->Flags
= fileObject
->Flags
| FO_FCB_IS_VALID
|
166 FO_DIRECT_CACHE_PAGING_READ
;
167 fileObject
->SectionObjectPointers
= &fcb
->SectionObjectPointers
;
168 fileObject
->FsContext
= (PVOID
) &fcb
->RFCB
;
169 fileObject
->FsContext2
= newCCB
;
171 newCCB
->PtrFileObject
= fileObject
;
174 bytesPerCluster
= vcb
->Boot
->SectorsPerCluster
* BLOCKSIZE
;
175 fileCacheQuantum
= (bytesPerCluster
>= PAGESIZE
) ?
176 bytesPerCluster
: PAGESIZE
;
177 status
= CcRosInitializeFileCache (fileObject
,
180 if (!NT_SUCCESS (status
))
182 DbgPrint ("CcRosInitializeFileCache failed\n");
185 ObDereferenceObject (fileObject
);
186 fcb
->isCacheInitialized
= TRUE
;
192 vfatRequestAndValidateRegion(PDEVICE_EXTENSION pDeviceExt
,
196 PCACHE_SEGMENT
* pCacheSegment
,
202 ULONG currentCluster
;
205 status
= CcRosRequestCacheSegment(pFCB
->RFCB
.Bcb
,
210 if (!NT_SUCCESS (status
))
215 isRoot
= vfatFCBIsRoot (pFCB
);
218 currentCluster
= vfatDirEntryGetFirstCluster (pDeviceExt
, &pFCB
->entry
);
219 status
= OffsetToCluster (pDeviceExt
,
220 vfatDirEntryGetFirstCluster (pDeviceExt
, &pFCB
->entry
),
224 if (!NT_SUCCESS (status
))
229 if (PAGESIZE
> pDeviceExt
->BytesPerCluster
)
231 for (i
= 0; i
< (PAGESIZE
/ pDeviceExt
->BytesPerCluster
); i
++)
233 status
= VfatRawReadCluster (pDeviceExt
,
234 vfatDirEntryGetFirstCluster (pDeviceExt
, &pFCB
->entry
),
236 (i
* pDeviceExt
->BytesPerCluster
),
238 if (!NT_SUCCESS (status
))
240 CcRosReleaseCacheSegment(pFCB
->RFCB
.Bcb
, *pCacheSegment
, FALSE
);
243 status
= NextCluster (pDeviceExt
,
244 vfatDirEntryGetFirstCluster (pDeviceExt
, &pFCB
->entry
),
247 if (!NT_SUCCESS (status
))
249 CcRosReleaseCacheSegment(pFCB
->RFCB
.Bcb
, *pCacheSegment
, FALSE
);
252 if ((currentCluster
) == 0xFFFFFFFF)
260 status
= VfatRawReadCluster (pDeviceExt
,
261 vfatDirEntryGetFirstCluster (pDeviceExt
, &pFCB
->entry
),
264 if (!NT_SUCCESS (status
))
266 CcRosReleaseCacheSegment(pFCB
->RFCB
.Bcb
, *pCacheSegment
, FALSE
);
272 return STATUS_SUCCESS
;
276 vfatReleaseRegion (PDEVICE_EXTENSION pDeviceExt
,
278 PCACHE_SEGMENT pCacheSegment
)
280 return CcRosReleaseCacheSegment (pFCB
->RFCB
.Bcb
, pCacheSegment
, TRUE
);
284 vfatMakeRootFCB(PDEVICE_EXTENSION pVCB
)
288 FCB
= vfatNewFCB(L
"\\");
289 memset(FCB
->entry
.Filename
, ' ', 11);
290 FCB
->entry
.FileSize
= pVCB
->rootDirectorySectors
* BLOCKSIZE
;
291 FCB
->entry
.Attrib
= FILE_ATTRIBUTE_DIRECTORY
;
292 if (pVCB
->FatType
== FAT32
)
294 FCB
->entry
.FirstCluster
= ((struct _BootSector32
*)(pVCB
->Boot
))->RootCluster
& 0xffff;
295 FCB
->entry
.FirstClusterHigh
= ((struct _BootSector32
*)(pVCB
->Boot
))->RootCluster
>> 16;
299 FCB
->entry
.FirstCluster
= 1;
303 vfatFCBInitializeCache(pVCB
, FCB
);
304 vfatAddFCBToTable(pVCB
, FCB
);
305 vfatGrabFCB(pVCB
, FCB
);
311 vfatOpenRootFCB(PDEVICE_EXTENSION pVCB
)
315 FCB
= vfatGrabFCBFromTable (pVCB
, L
"\\");
318 FCB
= vfatMakeRootFCB (pVCB
);
325 vfatMakeFCBFromDirEntry(PVCB vcb
,
326 PVFATFCB directoryFCB
,
328 PFAT_DIR_ENTRY dirEntry
,
332 WCHAR pathName
[MAX_PATH
];
334 if (longName
[0] != 0 && wcslen (directoryFCB
->PathName
) +
335 sizeof(WCHAR
) + wcslen (longName
) > MAX_PATH
)
337 return STATUS_OBJECT_NAME_INVALID
;
339 wcscpy (pathName
, directoryFCB
->PathName
);
340 if (!vfatFCBIsRoot (directoryFCB
))
342 wcscat (pathName
, L
"\\");
344 if (longName
[0] != 0)
346 wcscat (pathName
, longName
);
350 WCHAR entryName
[MAX_PATH
];
352 vfatGetDirEntryName (dirEntry
, entryName
);
353 wcscat (pathName
, entryName
);
355 rcFCB
= vfatNewFCB (pathName
);
356 memcpy (&rcFCB
->entry
, dirEntry
, sizeof (FAT_DIR_ENTRY
));
358 vfatFCBInitializeCache (vcb
, rcFCB
);
359 vfatAddFCBToTable (vcb
, rcFCB
);
360 vfatGrabFCB (vcb
, rcFCB
);
363 return STATUS_SUCCESS
;
367 vfatAttachFCBToFileObject (PDEVICE_EXTENSION vcb
,
369 PFILE_OBJECT fileObject
)
374 newCCB
= ExAllocatePoolWithTag (NonPagedPool
, sizeof (VFATCCB
), TAG_CCB
);
377 return STATUS_INSUFFICIENT_RESOURCES
;
379 memset (newCCB
, 0, sizeof (VFATCCB
));
381 fileObject
->Flags
= fileObject
->Flags
| FO_FCB_IS_VALID
|
382 FO_DIRECT_CACHE_PAGING_READ
;
383 fileObject
->SectionObjectPointers
= &fcb
->SectionObjectPointers
;
384 fileObject
->FsContext
= (PVOID
) &fcb
->RFCB
;
385 fileObject
->FsContext2
= newCCB
;
387 newCCB
->PtrFileObject
= fileObject
;
390 if (!fcb
->isCacheInitialized
)
392 ULONG bytesPerCluster
;
393 ULONG fileCacheQuantum
;
395 bytesPerCluster
= vcb
->Boot
->SectorsPerCluster
* BLOCKSIZE
;
396 fileCacheQuantum
= (bytesPerCluster
>= PAGESIZE
) ? bytesPerCluster
:
398 status
= CcRosInitializeFileCache (fileObject
,
401 if (!NT_SUCCESS (status
))
403 DbgPrint ("CcRosInitializeFileCache failed\n");
406 fcb
->isCacheInitialized
= TRUE
;
409 DPRINT ("file open: fcb:%x file size: %d\n", fcb
, fcb
->entry
.FileSize
);
411 return STATUS_SUCCESS
;
415 vfatDirFindFile (PDEVICE_EXTENSION pDeviceExt
,
416 PVFATFCB pDirectoryFCB
,
418 PVFATFCB
* pFoundFCB
)
420 BOOL finishedScanningDirectory
;
421 ULONG directoryIndex
;
423 WCHAR defaultFileName
[2];
424 WCHAR currentLongName
[256];
425 FAT_DIR_ENTRY currentDirEntry
;
426 WCHAR currentEntryName
[256];
429 assert (pDirectoryFCB
);
430 assert (pFileToFind
);
432 DPRINT ("vfatDirFindFile(VCB:%08x, dirFCB:%08x, File:%S)\n",
436 DPRINT ("Dir Path:%S\n", pDirectoryFCB
->PathName
);
438 // default to '.' if no filename specified
439 if (wcslen (pFileToFind
) == 0)
441 defaultFileName
[0] = L
'.';
442 defaultFileName
[1] = 0;
443 pFileToFind
= defaultFileName
;
447 finishedScanningDirectory
= FALSE
;
448 while (!finishedScanningDirectory
)
450 status
= vfatGetNextDirEntry (pDeviceExt
,
455 if (status
== STATUS_NO_MORE_ENTRIES
)
457 finishedScanningDirectory
= TRUE
;
460 else if (!NT_SUCCESS(status
))
465 DPRINT (" Index:%d longName:%S\n",
469 if (!vfatIsDirEntryDeleted (¤tDirEntry
))
471 if (currentLongName
[0] != L
'\0' && wstrcmpjoki (currentLongName
, pFileToFind
))
473 DPRINT ("Match found, %S\n", currentLongName
);
474 status
= vfatMakeFCBFromDirEntry (pDeviceExt
,
483 vfatGetDirEntryName (¤tDirEntry
, currentEntryName
);
484 DPRINT (" entryName:%S\n", currentEntryName
);
486 if (wstrcmpjoki (currentEntryName
, pFileToFind
))
488 DPRINT ("Match found, %S\n", currentEntryName
);
489 status
= vfatMakeFCBFromDirEntry (pDeviceExt
,
500 return STATUS_OBJECT_NAME_NOT_FOUND
;
504 vfatGetFCBForFile (PDEVICE_EXTENSION pVCB
,
505 PVFATFCB
*pParentFCB
,
507 const PWSTR pFileName
)
510 WCHAR pathName
[MAX_PATH
];
511 WCHAR elementName
[MAX_PATH
];
512 PWCHAR currentElement
;
516 DPRINT ("vfatGetFCBForFile (%x,%x,%x,%S)\n",
522 // Trivial case, open of the root directory on volume
523 if (pFileName
[0] == L
'\0' || wcscmp (pFileName
, L
"\\") == 0)
525 DPRINT ("returning root FCB\n");
527 FCB
= vfatOpenRootFCB (pVCB
);
531 return (FCB
!= NULL
) ? STATUS_SUCCESS
: STATUS_OBJECT_PATH_NOT_FOUND
;
535 currentElement
= pFileName
+ 1;
536 wcscpy (pathName
, L
"\\");
537 FCB
= vfatOpenRootFCB (pVCB
);
541 // Parse filename and check each path element for existance and access
542 while (vfatGetNextPathElement (currentElement
) != 0)
544 // Skip blank directory levels
545 if ((vfatGetNextPathElement (currentElement
) - currentElement
) == 0)
551 DPRINT ("Parsing, currentElement:%S\n", currentElement
);
552 DPRINT (" parentFCB:%x FCB:%x\n", parentFCB
, FCB
);
554 // descend to next directory level
557 vfatReleaseFCB (pVCB
, parentFCB
);
560 // fail if element in FCB is not a directory
561 if (!vfatFCBIsDirectory (pVCB
, FCB
))
563 DPRINT ("Element in requested path is not a directory\n");
565 vfatReleaseFCB (pVCB
, FCB
);
570 return STATUS_OBJECT_PATH_NOT_FOUND
;
574 // Extract next directory level into dirName
575 vfatWSubString (pathName
,
577 vfatGetNextPathElement (currentElement
) - pFileName
);
578 DPRINT (" pathName:%S\n", pathName
);
580 FCB
= vfatGrabFCBFromTable (pVCB
, pathName
);
583 vfatWSubString (elementName
,
585 vfatGetNextPathElement (currentElement
) - currentElement
);
586 DPRINT (" elementName:%S\n", elementName
);
588 status
= vfatDirFindFile (pVCB
, parentFCB
, elementName
, &FCB
);
589 if (status
== STATUS_OBJECT_NAME_NOT_FOUND
)
591 *pParentFCB
= parentFCB
;
593 currentElement
= vfatGetNextPathElement(currentElement
);
594 if (*currentElement
== L
'\0' || vfatGetNextPathElement(currentElement
+ 1) == 0)
596 return STATUS_OBJECT_NAME_NOT_FOUND
;
600 return STATUS_OBJECT_PATH_NOT_FOUND
;
603 else if (!NT_SUCCESS (status
))
605 vfatReleaseFCB (pVCB
, parentFCB
);
612 currentElement
= vfatGetNextPathElement (currentElement
);
615 *pParentFCB
= parentFCB
;
618 return STATUS_SUCCESS
;