1 /* $Id: fcb.c,v 1.16 2002/08/08 17:54:12 dwelch 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 #define ROUND_UP(N, S) ((((N) + (S) - 1) / (S)) * (S))
30 /* -------------------------------------------------------- PUBLICS */
33 vfatNewFCB(PWCHAR pFileName
)
37 rcFCB
= ExAllocatePoolWithTag (NonPagedPool
, sizeof (VFATFCB
), TAG_FCB
);
38 memset (rcFCB
, 0, sizeof (VFATFCB
));
41 wcscpy (rcFCB
->PathName
, pFileName
);
42 if (wcsrchr (rcFCB
->PathName
, '\\') != 0)
44 rcFCB
->ObjectName
= wcsrchr (rcFCB
->PathName
, '\\');
48 rcFCB
->ObjectName
= rcFCB
->PathName
;
51 ExInitializeResourceLite(&rcFCB
->PagingIoResource
);
52 ExInitializeResourceLite(&rcFCB
->MainResource
);
57 vfatDestroyFCB(PVFATFCB pFCB
)
59 ExDeleteResourceLite(&pFCB
->PagingIoResource
);
60 ExDeleteResourceLite(&pFCB
->MainResource
);
61 if ((pFCB
->Flags
& FCB_IS_PAGE_FILE
) && pFCB
->FatChainSize
)
62 ExFreePool(pFCB
->FatChain
);
67 vfatFCBIsDirectory(PDEVICE_EXTENSION pVCB
, PVFATFCB FCB
)
69 return FCB
->entry
.Attrib
& FILE_ATTRIBUTE_DIRECTORY
;
73 vfatFCBIsRoot(PVFATFCB FCB
)
75 return wcscmp (FCB
->PathName
, L
"\\") == 0;
79 vfatGrabFCB(PDEVICE_EXTENSION pVCB
, PVFATFCB pFCB
)
83 DPRINT ("grabbing FCB at %x: %S, refCount:%d\n",
88 KeAcquireSpinLock (&pVCB
->FcbListLock
, &oldIrql
);
90 KeReleaseSpinLock (&pVCB
->FcbListLock
, oldIrql
);
94 vfatReleaseFCB(PDEVICE_EXTENSION pVCB
, PVFATFCB pFCB
)
98 DPRINT ("releasing FCB at %x: %S, refCount:%d\n",
103 KeAcquireSpinLock (&pVCB
->FcbListLock
, &oldIrql
);
105 if (pFCB
->RefCount
<= 0 && (!vfatFCBIsDirectory (pVCB
, pFCB
) || pFCB
->Flags
& FCB_DELETE_PENDING
))
107 RemoveEntryList (&pFCB
->FcbListEntry
);
108 vfatDestroyFCB (pFCB
);
110 KeReleaseSpinLock (&pVCB
->FcbListLock
, oldIrql
);
114 vfatAddFCBToTable(PDEVICE_EXTENSION pVCB
, PVFATFCB pFCB
)
118 KeAcquireSpinLock (&pVCB
->FcbListLock
, &oldIrql
);
119 pFCB
->pDevExt
= pVCB
;
120 InsertTailList (&pVCB
->FcbListHead
, &pFCB
->FcbListEntry
);
121 KeReleaseSpinLock (&pVCB
->FcbListLock
, oldIrql
);
125 vfatGrabFCBFromTable(PDEVICE_EXTENSION pVCB
, PWSTR pFileName
)
129 PLIST_ENTRY current_entry
;
131 KeAcquireSpinLock (&pVCB
->FcbListLock
, &oldIrql
);
132 current_entry
= pVCB
->FcbListHead
.Flink
;
133 while (current_entry
!= &pVCB
->FcbListHead
)
135 rcFCB
= CONTAINING_RECORD (current_entry
, VFATFCB
, FcbListEntry
);
137 if (wstrcmpi (pFileName
, rcFCB
->PathName
))
140 KeReleaseSpinLock (&pVCB
->FcbListLock
, oldIrql
);
144 //FIXME: need to compare against short name in FCB here
146 current_entry
= current_entry
->Flink
;
148 KeReleaseSpinLock (&pVCB
->FcbListLock
, oldIrql
);
154 vfatFCBInitializeCache (PVCB vcb
, PVFATFCB fcb
)
157 PFILE_OBJECT fileObject
;
158 ULONG fileCacheQuantum
;
161 fileObject
= IoCreateStreamFileObject (NULL
, vcb
->StorageDevice
);
163 newCCB
= ExAllocatePoolWithTag (NonPagedPool
, sizeof (VFATCCB
), TAG_CCB
);
166 return STATUS_INSUFFICIENT_RESOURCES
;
168 memset (newCCB
, 0, sizeof (VFATCCB
));
170 fileObject
->Flags
= fileObject
->Flags
| FO_FCB_IS_VALID
|
171 FO_DIRECT_CACHE_PAGING_READ
;
172 fileObject
->SectionObjectPointers
= &fcb
->SectionObjectPointers
;
173 fileObject
->FsContext
= (PVOID
) &fcb
->RFCB
;
174 fileObject
->FsContext2
= newCCB
;
176 newCCB
->PtrFileObject
= fileObject
;
177 fcb
->FileObject
= fileObject
;
181 fileCacheQuantum
= (vcb
->FatInfo
.BytesPerCluster
>= PAGESIZE
) ?
182 vcb
->FatInfo
.BytesPerCluster
: PAGESIZE
;
184 status
= CcRosInitializeFileCache (fileObject
,
187 if (!NT_SUCCESS (status
))
189 DbgPrint ("CcRosInitializeFileCache failed\n");
193 ObDereferenceObject (fileObject
);
194 fcb
->Flags
|= FCB_CACHE_INITIALIZED
;
200 vfatMakeRootFCB(PDEVICE_EXTENSION pVCB
)
203 ULONG FirstCluster
, CurrentCluster
, Size
= 0;
204 NTSTATUS Status
= STATUS_SUCCESS
;
206 FCB
= vfatNewFCB(L
"\\");
207 memset(FCB
->entry
.Filename
, ' ', 11);
208 FCB
->entry
.FileSize
= pVCB
->FatInfo
.rootDirectorySectors
* pVCB
->FatInfo
.BytesPerSector
;
209 FCB
->entry
.Attrib
= FILE_ATTRIBUTE_DIRECTORY
;
210 if (pVCB
->FatInfo
.FatType
== FAT32
)
212 CurrentCluster
= FirstCluster
= pVCB
->FatInfo
.RootCluster
;
213 FCB
->entry
.FirstCluster
= FirstCluster
& 0xffff;
214 FCB
->entry
.FirstClusterHigh
= FirstCluster
>> 16;
215 CurrentCluster
= FirstCluster
;
217 while (CurrentCluster
!= 0xffffffff && NT_SUCCESS(Status
))
219 Size
+= pVCB
->FatInfo
.BytesPerCluster
;
220 Status
= NextCluster (pVCB
, NULL
, FirstCluster
, &CurrentCluster
, FALSE
);
225 FCB
->entry
.FirstCluster
= 1;
226 Size
= pVCB
->FatInfo
.rootDirectorySectors
* pVCB
->FatInfo
.BytesPerSector
;
230 FCB
->RFCB
.FileSize
.QuadPart
= Size
;
231 FCB
->RFCB
.ValidDataLength
.QuadPart
= Size
;
232 FCB
->RFCB
.AllocationSize
.QuadPart
= Size
;
234 vfatFCBInitializeCache(pVCB
, FCB
);
235 vfatAddFCBToTable(pVCB
, FCB
);
236 vfatGrabFCB(pVCB
, FCB
);
242 vfatOpenRootFCB(PDEVICE_EXTENSION pVCB
)
246 FCB
= vfatGrabFCBFromTable (pVCB
, L
"\\");
249 FCB
= vfatMakeRootFCB (pVCB
);
256 vfatMakeFCBFromDirEntry(PVCB vcb
,
257 PVFATFCB directoryFCB
,
259 PFAT_DIR_ENTRY dirEntry
,
264 WCHAR pathName
[MAX_PATH
];
266 if (longName
[0] != 0 && wcslen (directoryFCB
->PathName
) +
267 sizeof(WCHAR
) + wcslen (longName
) > MAX_PATH
)
269 return STATUS_OBJECT_NAME_INVALID
;
271 wcscpy (pathName
, directoryFCB
->PathName
);
272 if (!vfatFCBIsRoot (directoryFCB
))
274 wcscat (pathName
, L
"\\");
276 if (longName
[0] != 0)
278 wcscat (pathName
, longName
);
282 WCHAR entryName
[MAX_PATH
];
284 vfatGetDirEntryName (dirEntry
, entryName
);
285 wcscat (pathName
, entryName
);
287 rcFCB
= vfatNewFCB (pathName
);
288 memcpy (&rcFCB
->entry
, dirEntry
, sizeof (FAT_DIR_ENTRY
));
290 if (vfatFCBIsDirectory(vcb
, rcFCB
))
292 ULONG FirstCluster
, CurrentCluster
;
295 FirstCluster
= vfatDirEntryGetFirstCluster (vcb
, &rcFCB
->entry
);
296 if (FirstCluster
== 1)
298 Size
= vcb
->FatInfo
.rootDirectorySectors
* vcb
->FatInfo
.BytesPerSector
;
302 CurrentCluster
= FirstCluster
;
303 while (CurrentCluster
!= 0xffffffff)
305 Size
+= vcb
->FatInfo
.BytesPerCluster
;
306 Status
= NextCluster (vcb
, NULL
, FirstCluster
, &CurrentCluster
, FALSE
);
312 Size
= rcFCB
->entry
.FileSize
;
314 rcFCB
->dirIndex
= dirIndex
;
315 rcFCB
->RFCB
.FileSize
.QuadPart
= Size
;
316 rcFCB
->RFCB
.ValidDataLength
.QuadPart
= Size
;
317 rcFCB
->RFCB
.AllocationSize
.QuadPart
= ROUND_UP(Size
, vcb
->FatInfo
.BytesPerCluster
);
318 // DPRINT1("%S %d %d\n", longName, Size, (ULONG)rcFCB->RFCB.AllocationSize.QuadPart);
319 vfatFCBInitializeCache (vcb
, rcFCB
);
321 vfatAddFCBToTable (vcb
, rcFCB
);
324 return STATUS_SUCCESS
;
328 vfatAttachFCBToFileObject (PDEVICE_EXTENSION vcb
,
330 PFILE_OBJECT fileObject
)
335 newCCB
= ExAllocatePoolWithTag (NonPagedPool
, sizeof (VFATCCB
), TAG_CCB
);
338 return STATUS_INSUFFICIENT_RESOURCES
;
340 memset (newCCB
, 0, sizeof (VFATCCB
));
342 fileObject
->Flags
= fileObject
->Flags
| FO_FCB_IS_VALID
|
343 FO_DIRECT_CACHE_PAGING_READ
;
344 fileObject
->SectionObjectPointers
= &fcb
->SectionObjectPointers
;
345 fileObject
->FsContext
= (PVOID
) &fcb
->RFCB
;
346 fileObject
->FsContext2
= newCCB
;
348 newCCB
->PtrFileObject
= fileObject
;
351 if (!(fcb
->Flags
& FCB_CACHE_INITIALIZED
))
353 ULONG fileCacheQuantum
;
355 fileCacheQuantum
= (vcb
->FatInfo
.BytesPerCluster
>= PAGESIZE
) ?
356 vcb
->FatInfo
.BytesPerCluster
: PAGESIZE
;
357 status
= CcRosInitializeFileCache (fileObject
,
360 if (!NT_SUCCESS (status
))
362 DbgPrint ("CcRosInitializeFileCache failed\n");
365 fcb
->Flags
|= FCB_CACHE_INITIALIZED
;
368 DPRINT ("file open: fcb:%x file size: %d\n", fcb
, fcb
->entry
.FileSize
);
370 return STATUS_SUCCESS
;
374 vfatDirFindFile (PDEVICE_EXTENSION pDeviceExt
,
375 PVFATFCB pDirectoryFCB
,
377 PVFATFCB
* pFoundFCB
)
379 BOOL finishedScanningDirectory
;
380 ULONG directoryIndex
;
382 WCHAR defaultFileName
[2];
383 WCHAR currentLongName
[256];
384 FAT_DIR_ENTRY currentDirEntry
;
385 WCHAR currentEntryName
[256];
388 assert (pDirectoryFCB
);
389 assert (pFileToFind
);
391 DPRINT ("vfatDirFindFile(VCB:%08x, dirFCB:%08x, File:%S)\n",
395 DPRINT ("Dir Path:%S\n", pDirectoryFCB
->PathName
);
397 // default to '.' if no filename specified
398 if (wcslen (pFileToFind
) == 0)
400 defaultFileName
[0] = L
'.';
401 defaultFileName
[1] = 0;
402 pFileToFind
= defaultFileName
;
406 finishedScanningDirectory
= FALSE
;
407 while (!finishedScanningDirectory
)
409 status
= vfatGetNextDirEntry (pDeviceExt
,
414 if (status
== STATUS_NO_MORE_ENTRIES
)
416 finishedScanningDirectory
= TRUE
;
419 else if (!NT_SUCCESS(status
))
424 DPRINT (" Index:%d longName:%S\n",
428 if (!vfatIsDirEntryDeleted (¤tDirEntry
)
429 && !vfatIsDirEntryVolume(¤tDirEntry
))
431 if (currentLongName
[0] != L
'\0' && wstrcmpjoki (currentLongName
, pFileToFind
))
433 DPRINT ("Match found, %S\n", currentLongName
);
434 status
= vfatMakeFCBFromDirEntry (pDeviceExt
,
444 vfatGetDirEntryName (¤tDirEntry
, currentEntryName
);
445 DPRINT (" entryName:%S\n", currentEntryName
);
447 if (wstrcmpjoki (currentEntryName
, pFileToFind
))
449 DPRINT ("Match found, %S\n", currentEntryName
);
450 status
= vfatMakeFCBFromDirEntry (pDeviceExt
,
462 return STATUS_OBJECT_NAME_NOT_FOUND
;
466 vfatGetFCBForFile (PDEVICE_EXTENSION pVCB
,
467 PVFATFCB
*pParentFCB
,
469 const PWSTR pFileName
)
472 WCHAR pathName
[MAX_PATH
];
473 WCHAR elementName
[MAX_PATH
];
474 PWCHAR currentElement
;
478 DPRINT ("vfatGetFCBForFile (%x,%x,%x,%S)\n",
484 // Trivial case, open of the root directory on volume
485 if (pFileName
[0] == L
'\0' || wcscmp (pFileName
, L
"\\") == 0)
487 DPRINT ("returning root FCB\n");
489 FCB
= vfatOpenRootFCB (pVCB
);
493 return (FCB
!= NULL
) ? STATUS_SUCCESS
: STATUS_OBJECT_PATH_NOT_FOUND
;
497 currentElement
= pFileName
+ 1;
498 wcscpy (pathName
, L
"\\");
499 FCB
= vfatOpenRootFCB (pVCB
);
503 // Parse filename and check each path element for existance and access
504 while (vfatGetNextPathElement (currentElement
) != 0)
506 // Skip blank directory levels
507 if ((vfatGetNextPathElement (currentElement
) - currentElement
) == 0)
513 DPRINT ("Parsing, currentElement:%S\n", currentElement
);
514 DPRINT (" parentFCB:%x FCB:%x\n", parentFCB
, FCB
);
516 // descend to next directory level
519 vfatReleaseFCB (pVCB
, parentFCB
);
522 // fail if element in FCB is not a directory
523 if (!vfatFCBIsDirectory (pVCB
, FCB
))
525 DPRINT ("Element in requested path is not a directory\n");
527 vfatReleaseFCB (pVCB
, FCB
);
532 return STATUS_OBJECT_PATH_NOT_FOUND
;
536 // Extract next directory level into dirName
537 vfatWSubString (pathName
,
539 vfatGetNextPathElement (currentElement
) - pFileName
);
540 DPRINT (" pathName:%S\n", pathName
);
542 FCB
= vfatGrabFCBFromTable (pVCB
, pathName
);
545 vfatWSubString (elementName
,
547 vfatGetNextPathElement (currentElement
) - currentElement
);
548 DPRINT (" elementName:%S\n", elementName
);
550 status
= vfatDirFindFile (pVCB
, parentFCB
, elementName
, &FCB
);
551 if (status
== STATUS_OBJECT_NAME_NOT_FOUND
)
553 *pParentFCB
= parentFCB
;
555 currentElement
= vfatGetNextPathElement(currentElement
);
556 if (*currentElement
== L
'\0' || vfatGetNextPathElement(currentElement
+ 1) == 0)
558 return STATUS_OBJECT_NAME_NOT_FOUND
;
562 return STATUS_OBJECT_PATH_NOT_FOUND
;
565 else if (!NT_SUCCESS (status
))
567 vfatReleaseFCB (pVCB
, parentFCB
);
574 currentElement
= vfatGetNextPathElement (currentElement
);
577 *pParentFCB
= parentFCB
;
580 return STATUS_SUCCESS
;