1 /* $Id: fcb.c,v 1.19 2002/09/07 15:12:03 chorns 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))
27 #define TAG_FCB TAG('V', 'F', 'C', 'B')
29 #define ROUND_UP(N, S) ((((N) + (S) - 1) / (S)) * (S))
31 /* -------------------------------------------------------- PUBLICS */
34 vfatNewFCB(PWCHAR pFileName
)
38 rcFCB
= ExAllocatePoolWithTag (NonPagedPool
, sizeof (VFATFCB
), TAG_FCB
);
39 memset (rcFCB
, 0, sizeof (VFATFCB
));
42 wcscpy (rcFCB
->PathName
, pFileName
);
43 if (wcsrchr (rcFCB
->PathName
, '\\') != 0)
45 rcFCB
->ObjectName
= wcsrchr (rcFCB
->PathName
, '\\');
49 rcFCB
->ObjectName
= rcFCB
->PathName
;
52 ExInitializeResourceLite(&rcFCB
->PagingIoResource
);
53 ExInitializeResourceLite(&rcFCB
->MainResource
);
58 vfatDestroyFCB(PVFATFCB pFCB
)
60 ExDeleteResourceLite(&pFCB
->PagingIoResource
);
61 ExDeleteResourceLite(&pFCB
->MainResource
);
62 if ((pFCB
->Flags
& FCB_IS_PAGE_FILE
) && pFCB
->FatChainSize
)
63 ExFreePool(pFCB
->FatChain
);
68 vfatFCBIsDirectory(PDEVICE_EXTENSION pVCB
, PVFATFCB FCB
)
70 return FCB
->entry
.Attrib
& FILE_ATTRIBUTE_DIRECTORY
;
74 vfatFCBIsRoot(PVFATFCB FCB
)
76 return wcscmp (FCB
->PathName
, L
"\\") == 0;
80 vfatGrabFCB(PDEVICE_EXTENSION pVCB
, PVFATFCB pFCB
)
84 DPRINT ("grabbing FCB at %x: %S, refCount:%d\n",
89 KeAcquireSpinLock (&pVCB
->FcbListLock
, &oldIrql
);
91 KeReleaseSpinLock (&pVCB
->FcbListLock
, oldIrql
);
95 vfatReleaseFCB(PDEVICE_EXTENSION pVCB
, PVFATFCB pFCB
)
99 DPRINT ("releasing FCB at %x: %S, refCount:%d\n",
104 KeAcquireSpinLock (&pVCB
->FcbListLock
, &oldIrql
);
106 if (pFCB
->RefCount
<= 0 && (!vfatFCBIsDirectory (pVCB
, pFCB
) || pFCB
->Flags
& FCB_DELETE_PENDING
))
108 RemoveEntryList (&pFCB
->FcbListEntry
);
109 KeReleaseSpinLock (&pVCB
->FcbListLock
, oldIrql
);
110 if (vfatFCBIsDirectory(pVCB
, pFCB
))
112 CcRosReleaseFileCache(pFCB
->FileObject
, pFCB
->RFCB
.Bcb
);
113 ExFreePool(pFCB
->FileObject
->FsContext2
);
114 pFCB
->FileObject
->FsContext2
= NULL
;
115 ObDereferenceObject(pFCB
->FileObject
);
117 vfatDestroyFCB (pFCB
);
120 KeReleaseSpinLock (&pVCB
->FcbListLock
, oldIrql
);
124 vfatAddFCBToTable(PDEVICE_EXTENSION pVCB
, PVFATFCB pFCB
)
128 KeAcquireSpinLock (&pVCB
->FcbListLock
, &oldIrql
);
129 pFCB
->pDevExt
= pVCB
;
130 InsertTailList (&pVCB
->FcbListHead
, &pFCB
->FcbListEntry
);
131 KeReleaseSpinLock (&pVCB
->FcbListLock
, oldIrql
);
135 vfatGrabFCBFromTable(PDEVICE_EXTENSION pVCB
, PWSTR pFileName
)
139 PLIST_ENTRY current_entry
;
141 KeAcquireSpinLock (&pVCB
->FcbListLock
, &oldIrql
);
142 current_entry
= pVCB
->FcbListHead
.Flink
;
143 while (current_entry
!= &pVCB
->FcbListHead
)
145 rcFCB
= CONTAINING_RECORD (current_entry
, VFATFCB
, FcbListEntry
);
147 if (wstrcmpi (pFileName
, rcFCB
->PathName
))
150 KeReleaseSpinLock (&pVCB
->FcbListLock
, oldIrql
);
154 //FIXME: need to compare against short name in FCB here
156 current_entry
= current_entry
->Flink
;
158 KeReleaseSpinLock (&pVCB
->FcbListLock
, oldIrql
);
164 vfatFCBInitializeCacheFromVolume (PVCB vcb
, PVFATFCB fcb
)
167 PFILE_OBJECT fileObject
;
168 ULONG fileCacheQuantum
;
171 fileObject
= IoCreateStreamFileObject (NULL
, vcb
->StorageDevice
);
173 newCCB
= ExAllocatePoolWithTag (NonPagedPool
, sizeof (VFATCCB
), TAG_CCB
);
176 return STATUS_INSUFFICIENT_RESOURCES
;
178 memset (newCCB
, 0, sizeof (VFATCCB
));
180 fileObject
->Flags
= fileObject
->Flags
| FO_FCB_IS_VALID
|
181 FO_DIRECT_CACHE_PAGING_READ
;
182 fileObject
->SectionObjectPointer
= &fcb
->SectionObjectPointers
;
183 fileObject
->FsContext
= (PVOID
) &fcb
->RFCB
;
184 fileObject
->FsContext2
= newCCB
;
186 newCCB
->PtrFileObject
= fileObject
;
187 fcb
->FileObject
= fileObject
;
191 fileCacheQuantum
= (vcb
->FatInfo
.BytesPerCluster
>= PAGE_SIZE
) ?
192 vcb
->FatInfo
.BytesPerCluster
: PAGE_SIZE
;
194 status
= CcRosInitializeFileCache (fileObject
,
197 if (!NT_SUCCESS (status
))
199 DbgPrint ("CcRosInitializeFileCache failed\n");
203 fcb
->Flags
|= FCB_CACHE_INITIALIZED
;
209 vfatMakeRootFCB(PDEVICE_EXTENSION pVCB
)
212 ULONG FirstCluster
, CurrentCluster
, Size
= 0;
213 NTSTATUS Status
= STATUS_SUCCESS
;
215 FCB
= vfatNewFCB(L
"\\");
216 memset(FCB
->entry
.Filename
, ' ', 11);
217 FCB
->entry
.FileSize
= pVCB
->FatInfo
.rootDirectorySectors
* pVCB
->FatInfo
.BytesPerSector
;
218 FCB
->entry
.Attrib
= FILE_ATTRIBUTE_DIRECTORY
;
219 if (pVCB
->FatInfo
.FatType
== FAT32
)
221 CurrentCluster
= FirstCluster
= pVCB
->FatInfo
.RootCluster
;
222 FCB
->entry
.FirstCluster
= FirstCluster
& 0xffff;
223 FCB
->entry
.FirstClusterHigh
= FirstCluster
>> 16;
224 CurrentCluster
= FirstCluster
;
226 while (CurrentCluster
!= 0xffffffff && NT_SUCCESS(Status
))
228 Size
+= pVCB
->FatInfo
.BytesPerCluster
;
229 Status
= NextCluster (pVCB
, NULL
, FirstCluster
, &CurrentCluster
, FALSE
);
234 FCB
->entry
.FirstCluster
= 1;
235 Size
= pVCB
->FatInfo
.rootDirectorySectors
* pVCB
->FatInfo
.BytesPerSector
;
239 FCB
->RFCB
.FileSize
.QuadPart
= Size
;
240 FCB
->RFCB
.ValidDataLength
.QuadPart
= Size
;
241 FCB
->RFCB
.AllocationSize
.QuadPart
= Size
;
243 vfatFCBInitializeCacheFromVolume(pVCB
, FCB
);
244 vfatAddFCBToTable(pVCB
, FCB
);
245 vfatGrabFCB(pVCB
, FCB
);
251 vfatOpenRootFCB(PDEVICE_EXTENSION pVCB
)
255 FCB
= vfatGrabFCBFromTable (pVCB
, L
"\\");
258 FCB
= vfatMakeRootFCB (pVCB
);
265 vfatMakeFCBFromDirEntry(PVCB vcb
,
266 PVFATFCB directoryFCB
,
268 PFAT_DIR_ENTRY dirEntry
,
273 WCHAR pathName
[MAX_PATH
];
275 if (longName
[0] != 0 && wcslen (directoryFCB
->PathName
) +
276 sizeof(WCHAR
) + wcslen (longName
) > MAX_PATH
)
278 return STATUS_OBJECT_NAME_INVALID
;
280 wcscpy (pathName
, directoryFCB
->PathName
);
281 if (!vfatFCBIsRoot (directoryFCB
))
283 wcscat (pathName
, L
"\\");
285 if (longName
[0] != 0)
287 wcscat (pathName
, longName
);
291 WCHAR entryName
[MAX_PATH
];
293 vfatGetDirEntryName (dirEntry
, entryName
);
294 wcscat (pathName
, entryName
);
296 rcFCB
= vfatNewFCB (pathName
);
297 memcpy (&rcFCB
->entry
, dirEntry
, sizeof (FAT_DIR_ENTRY
));
299 if (vfatFCBIsDirectory(vcb
, rcFCB
))
301 ULONG FirstCluster
, CurrentCluster
;
304 FirstCluster
= vfatDirEntryGetFirstCluster (vcb
, &rcFCB
->entry
);
305 if (FirstCluster
== 1)
307 Size
= vcb
->FatInfo
.rootDirectorySectors
* vcb
->FatInfo
.BytesPerSector
;
311 CurrentCluster
= FirstCluster
;
312 while (CurrentCluster
!= 0xffffffff)
314 Size
+= vcb
->FatInfo
.BytesPerCluster
;
315 Status
= NextCluster (vcb
, NULL
, FirstCluster
, &CurrentCluster
, FALSE
);
321 Size
= rcFCB
->entry
.FileSize
;
323 rcFCB
->dirIndex
= dirIndex
;
324 rcFCB
->RFCB
.FileSize
.QuadPart
= Size
;
325 rcFCB
->RFCB
.ValidDataLength
.QuadPart
= Size
;
326 rcFCB
->RFCB
.AllocationSize
.QuadPart
= ROUND_UP(Size
, vcb
->FatInfo
.BytesPerCluster
);
328 if (vfatFCBIsDirectory(vcb
, rcFCB
))
330 vfatFCBInitializeCacheFromVolume(vcb
, rcFCB
);
332 vfatAddFCBToTable (vcb
, rcFCB
);
335 return STATUS_SUCCESS
;
339 vfatAttachFCBToFileObject (PDEVICE_EXTENSION vcb
,
341 PFILE_OBJECT fileObject
)
346 newCCB
= ExAllocatePoolWithTag (NonPagedPool
, sizeof (VFATCCB
), TAG_CCB
);
349 return STATUS_INSUFFICIENT_RESOURCES
;
351 memset (newCCB
, 0, sizeof (VFATCCB
));
353 fileObject
->Flags
= fileObject
->Flags
| FO_FCB_IS_VALID
|
354 FO_DIRECT_CACHE_PAGING_READ
;
355 fileObject
->SectionObjectPointer
= &fcb
->SectionObjectPointers
;
356 fileObject
->FsContext
= (PVOID
) &fcb
->RFCB
;
357 fileObject
->FsContext2
= newCCB
;
359 newCCB
->PtrFileObject
= fileObject
;
362 DPRINT ("file open: fcb:%x file size: %d\n", fcb
, fcb
->entry
.FileSize
);
364 return STATUS_SUCCESS
;
368 vfatDirFindFile (PDEVICE_EXTENSION pDeviceExt
,
369 PVFATFCB pDirectoryFCB
,
371 PVFATFCB
* pFoundFCB
)
373 BOOL finishedScanningDirectory
;
374 ULONG directoryIndex
;
376 WCHAR defaultFileName
[2];
377 WCHAR currentLongName
[256];
378 FAT_DIR_ENTRY currentDirEntry
;
379 WCHAR currentEntryName
[256];
382 assert (pDirectoryFCB
);
383 assert (pFileToFind
);
385 DPRINT ("vfatDirFindFile(VCB:%08x, dirFCB:%08x, File:%S)\n",
389 DPRINT ("Dir Path:%S\n", pDirectoryFCB
->PathName
);
391 // default to '.' if no filename specified
392 if (wcslen (pFileToFind
) == 0)
394 defaultFileName
[0] = L
'.';
395 defaultFileName
[1] = 0;
396 pFileToFind
= defaultFileName
;
400 finishedScanningDirectory
= FALSE
;
401 while (!finishedScanningDirectory
)
403 status
= vfatGetNextDirEntry (pDeviceExt
,
408 if (status
== STATUS_NO_MORE_ENTRIES
)
410 finishedScanningDirectory
= TRUE
;
413 else if (!NT_SUCCESS(status
))
418 DPRINT (" Index:%d longName:%S\n",
422 if (!vfatIsDirEntryDeleted (¤tDirEntry
)
423 && !vfatIsDirEntryVolume(¤tDirEntry
))
425 if (currentLongName
[0] != L
'\0' && wstrcmpjoki (currentLongName
, pFileToFind
))
427 DPRINT ("Match found, %S\n", currentLongName
);
428 status
= vfatMakeFCBFromDirEntry (pDeviceExt
,
438 vfatGetDirEntryName (¤tDirEntry
, currentEntryName
);
439 DPRINT (" entryName:%S\n", currentEntryName
);
441 if (wstrcmpjoki (currentEntryName
, pFileToFind
))
443 DPRINT ("Match found, %S\n", currentEntryName
);
444 status
= vfatMakeFCBFromDirEntry (pDeviceExt
,
456 return STATUS_OBJECT_NAME_NOT_FOUND
;
460 vfatGetFCBForFile (PDEVICE_EXTENSION pVCB
,
461 PVFATFCB
*pParentFCB
,
463 const PWSTR pFileName
)
466 WCHAR pathName
[MAX_PATH
];
467 WCHAR elementName
[MAX_PATH
];
468 PWCHAR currentElement
;
472 DPRINT ("vfatGetFCBForFile (%x,%x,%x,%S)\n",
478 // Trivial case, open of the root directory on volume
479 if (pFileName
[0] == L
'\0' || wcscmp (pFileName
, L
"\\") == 0)
481 DPRINT ("returning root FCB\n");
483 FCB
= vfatOpenRootFCB (pVCB
);
487 return (FCB
!= NULL
) ? STATUS_SUCCESS
: STATUS_OBJECT_PATH_NOT_FOUND
;
491 currentElement
= pFileName
+ 1;
492 wcscpy (pathName
, L
"\\");
493 FCB
= vfatOpenRootFCB (pVCB
);
497 // Parse filename and check each path element for existance and access
498 while (vfatGetNextPathElement (currentElement
) != 0)
500 // Skip blank directory levels
501 if ((vfatGetNextPathElement (currentElement
) - currentElement
) == 0)
507 DPRINT ("Parsing, currentElement:%S\n", currentElement
);
508 DPRINT (" parentFCB:%x FCB:%x\n", parentFCB
, FCB
);
510 // descend to next directory level
513 vfatReleaseFCB (pVCB
, parentFCB
);
516 // fail if element in FCB is not a directory
517 if (!vfatFCBIsDirectory (pVCB
, FCB
))
519 DPRINT ("Element in requested path is not a directory\n");
521 vfatReleaseFCB (pVCB
, FCB
);
526 return STATUS_OBJECT_PATH_NOT_FOUND
;
530 // Extract next directory level into dirName
531 vfatWSubString (pathName
,
533 vfatGetNextPathElement (currentElement
) - pFileName
);
534 DPRINT (" pathName:%S\n", pathName
);
536 FCB
= vfatGrabFCBFromTable (pVCB
, pathName
);
539 vfatWSubString (elementName
,
541 vfatGetNextPathElement (currentElement
) - currentElement
);
542 DPRINT (" elementName:%S\n", elementName
);
544 status
= vfatDirFindFile (pVCB
, parentFCB
, elementName
, &FCB
);
545 if (status
== STATUS_OBJECT_NAME_NOT_FOUND
)
547 *pParentFCB
= parentFCB
;
549 currentElement
= vfatGetNextPathElement(currentElement
);
550 if (*currentElement
== L
'\0' || vfatGetNextPathElement(currentElement
+ 1) == 0)
552 return STATUS_OBJECT_NAME_NOT_FOUND
;
556 return STATUS_OBJECT_PATH_NOT_FOUND
;
559 else if (!NT_SUCCESS (status
))
561 vfatReleaseFCB (pVCB
, parentFCB
);
568 currentElement
= vfatGetNextPathElement (currentElement
);
571 *pParentFCB
= parentFCB
;
574 return STATUS_SUCCESS
;