1 /* $Id: fcb.c,v 1.12 2002/01/08 00:49:01 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
);
65 vfatFCBIsDirectory(PDEVICE_EXTENSION pVCB
, PVFATFCB FCB
)
67 return FCB
->entry
.Attrib
& FILE_ATTRIBUTE_DIRECTORY
;
71 vfatFCBIsRoot(PVFATFCB FCB
)
73 return wcscmp (FCB
->PathName
, L
"\\") == 0;
77 vfatGrabFCB(PDEVICE_EXTENSION pVCB
, PVFATFCB pFCB
)
81 DPRINT ("grabbing FCB at %x: %S, refCount:%d\n",
86 KeAcquireSpinLock (&pVCB
->FcbListLock
, &oldIrql
);
88 KeReleaseSpinLock (&pVCB
->FcbListLock
, oldIrql
);
92 vfatReleaseFCB(PDEVICE_EXTENSION pVCB
, PVFATFCB pFCB
)
96 DPRINT ("releasing FCB at %x: %S, refCount:%d\n",
101 KeAcquireSpinLock (&pVCB
->FcbListLock
, &oldIrql
);
103 if (pFCB
->RefCount
<= 0 && (!vfatFCBIsDirectory (pVCB
, pFCB
) || pFCB
->Flags
& FCB_DELETE_PENDING
))
105 RemoveEntryList (&pFCB
->FcbListEntry
);
106 CcRosReleaseFileCache (NULL
, pFCB
->RFCB
.Bcb
);
107 vfatDestroyFCB (pFCB
);
109 KeReleaseSpinLock (&pVCB
->FcbListLock
, oldIrql
);
113 vfatAddFCBToTable(PDEVICE_EXTENSION pVCB
, PVFATFCB pFCB
)
117 KeAcquireSpinLock (&pVCB
->FcbListLock
, &oldIrql
);
118 pFCB
->pDevExt
= pVCB
;
119 InsertTailList (&pVCB
->FcbListHead
, &pFCB
->FcbListEntry
);
120 KeReleaseSpinLock (&pVCB
->FcbListLock
, oldIrql
);
124 vfatGrabFCBFromTable(PDEVICE_EXTENSION pVCB
, PWSTR pFileName
)
128 PLIST_ENTRY current_entry
;
130 KeAcquireSpinLock (&pVCB
->FcbListLock
, &oldIrql
);
131 current_entry
= pVCB
->FcbListHead
.Flink
;
132 while (current_entry
!= &pVCB
->FcbListHead
)
134 rcFCB
= CONTAINING_RECORD (current_entry
, VFATFCB
, FcbListEntry
);
136 if (wstrcmpi (pFileName
, rcFCB
->PathName
))
139 KeReleaseSpinLock (&pVCB
->FcbListLock
, oldIrql
);
143 //FIXME: need to compare against short name in FCB here
145 current_entry
= current_entry
->Flink
;
147 KeReleaseSpinLock (&pVCB
->FcbListLock
, oldIrql
);
153 vfatFCBInitializeCache (PVCB vcb
, PVFATFCB fcb
)
156 PFILE_OBJECT fileObject
;
157 ULONG bytesPerCluster
;
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 bytesPerCluster
= vcb
->Boot
->SectorsPerCluster
* BLOCKSIZE
;
182 fileCacheQuantum
= (bytesPerCluster
>= PAGESIZE
) ?
183 bytesPerCluster
: PAGESIZE
;
185 status
= CcRosInitializeFileCache (fileObject
,
188 if (!NT_SUCCESS (status
))
190 DbgPrint ("CcRosInitializeFileCache failed\n");
194 ObDereferenceObject (fileObject
);
195 fcb
->Flags
|= FCB_CACHE_INITIALIZED
;
201 vfatMakeRootFCB(PDEVICE_EXTENSION pVCB
)
204 ULONG FirstCluster
, CurrentCluster
, Size
= 0;
205 NTSTATUS Status
= STATUS_SUCCESS
;
207 FCB
= vfatNewFCB(L
"\\");
208 memset(FCB
->entry
.Filename
, ' ', 11);
209 FCB
->entry
.FileSize
= pVCB
->rootDirectorySectors
* BLOCKSIZE
;
210 FCB
->entry
.Attrib
= FILE_ATTRIBUTE_DIRECTORY
;
211 if (pVCB
->FatType
== FAT32
)
213 CurrentCluster
= FirstCluster
= ((struct _BootSector32
*)(pVCB
->Boot
))->RootCluster
;
214 FCB
->entry
.FirstCluster
= FirstCluster
& 0xffff;
215 FCB
->entry
.FirstClusterHigh
= FirstCluster
>> 16;
216 CurrentCluster
= FirstCluster
;
218 while (CurrentCluster
!= 0xffffffff && NT_SUCCESS(Status
))
220 Size
+= pVCB
->BytesPerCluster
;
221 Status
= NextCluster (pVCB
, NULL
, FirstCluster
, &CurrentCluster
, FALSE
);
226 FCB
->entry
.FirstCluster
= 1;
227 Size
= pVCB
->rootDirectorySectors
* BLOCKSIZE
;
231 FCB
->RFCB
.FileSize
.QuadPart
= Size
;
232 FCB
->RFCB
.ValidDataLength
.QuadPart
= Size
;
233 FCB
->RFCB
.AllocationSize
.QuadPart
= Size
;
235 vfatFCBInitializeCache(pVCB
, FCB
);
236 vfatAddFCBToTable(pVCB
, FCB
);
237 vfatGrabFCB(pVCB
, FCB
);
243 vfatOpenRootFCB(PDEVICE_EXTENSION pVCB
)
247 FCB
= vfatGrabFCBFromTable (pVCB
, L
"\\");
250 FCB
= vfatMakeRootFCB (pVCB
);
257 vfatMakeFCBFromDirEntry(PVCB vcb
,
258 PVFATFCB directoryFCB
,
260 PFAT_DIR_ENTRY dirEntry
,
265 WCHAR pathName
[MAX_PATH
];
267 if (longName
[0] != 0 && wcslen (directoryFCB
->PathName
) +
268 sizeof(WCHAR
) + wcslen (longName
) > MAX_PATH
)
270 return STATUS_OBJECT_NAME_INVALID
;
272 wcscpy (pathName
, directoryFCB
->PathName
);
273 if (!vfatFCBIsRoot (directoryFCB
))
275 wcscat (pathName
, L
"\\");
277 if (longName
[0] != 0)
279 wcscat (pathName
, longName
);
283 WCHAR entryName
[MAX_PATH
];
285 vfatGetDirEntryName (dirEntry
, entryName
);
286 wcscat (pathName
, entryName
);
288 rcFCB
= vfatNewFCB (pathName
);
289 memcpy (&rcFCB
->entry
, dirEntry
, sizeof (FAT_DIR_ENTRY
));
291 if (vfatFCBIsDirectory(vcb
, rcFCB
))
293 ULONG FirstCluster
, CurrentCluster
;
296 FirstCluster
= vfatDirEntryGetFirstCluster (vcb
, &rcFCB
->entry
);
297 if (FirstCluster
== 1)
299 Size
= vcb
->rootDirectorySectors
* BLOCKSIZE
;
303 CurrentCluster
= FirstCluster
;
304 while (CurrentCluster
!= 0xffffffff)
306 Size
+= vcb
->BytesPerCluster
;
307 Status
= NextCluster (vcb
, NULL
, FirstCluster
, &CurrentCluster
, FALSE
);
313 Size
= rcFCB
->entry
.FileSize
;
315 rcFCB
->dirIndex
= dirIndex
;
316 rcFCB
->RFCB
.FileSize
.QuadPart
= Size
;
317 rcFCB
->RFCB
.ValidDataLength
.QuadPart
= Size
;
318 rcFCB
->RFCB
.AllocationSize
.QuadPart
= ROUND_UP(Size
, vcb
->BytesPerCluster
);
319 // DPRINT1("%S %d %d\n", longName, Size, (ULONG)rcFCB->RFCB.AllocationSize.QuadPart);
320 vfatFCBInitializeCache (vcb
, rcFCB
);
322 vfatAddFCBToTable (vcb
, rcFCB
);
325 return STATUS_SUCCESS
;
329 vfatAttachFCBToFileObject (PDEVICE_EXTENSION vcb
,
331 PFILE_OBJECT fileObject
)
336 newCCB
= ExAllocatePoolWithTag (NonPagedPool
, sizeof (VFATCCB
), TAG_CCB
);
339 return STATUS_INSUFFICIENT_RESOURCES
;
341 memset (newCCB
, 0, sizeof (VFATCCB
));
343 fileObject
->Flags
= fileObject
->Flags
| FO_FCB_IS_VALID
|
344 FO_DIRECT_CACHE_PAGING_READ
;
345 fileObject
->SectionObjectPointers
= &fcb
->SectionObjectPointers
;
346 fileObject
->FsContext
= (PVOID
) &fcb
->RFCB
;
347 fileObject
->FsContext2
= newCCB
;
349 newCCB
->PtrFileObject
= fileObject
;
352 if (!(fcb
->Flags
& FCB_CACHE_INITIALIZED
))
354 ULONG bytesPerCluster
;
355 ULONG fileCacheQuantum
;
357 bytesPerCluster
= vcb
->Boot
->SectorsPerCluster
* BLOCKSIZE
;
358 fileCacheQuantum
= (bytesPerCluster
>= PAGESIZE
) ? bytesPerCluster
: PAGESIZE
;
359 status
= CcRosInitializeFileCache (fileObject
,
362 if (!NT_SUCCESS (status
))
364 DbgPrint ("CcRosInitializeFileCache failed\n");
367 fcb
->Flags
|= FCB_CACHE_INITIALIZED
;
370 DPRINT ("file open: fcb:%x file size: %d\n", fcb
, fcb
->entry
.FileSize
);
372 return STATUS_SUCCESS
;
376 vfatDirFindFile (PDEVICE_EXTENSION pDeviceExt
,
377 PVFATFCB pDirectoryFCB
,
379 PVFATFCB
* pFoundFCB
)
381 BOOL finishedScanningDirectory
;
382 ULONG directoryIndex
;
384 WCHAR defaultFileName
[2];
385 WCHAR currentLongName
[256];
386 FAT_DIR_ENTRY currentDirEntry
;
387 WCHAR currentEntryName
[256];
390 assert (pDirectoryFCB
);
391 assert (pFileToFind
);
393 DPRINT ("vfatDirFindFile(VCB:%08x, dirFCB:%08x, File:%S)\n",
397 DPRINT ("Dir Path:%S\n", pDirectoryFCB
->PathName
);
399 // default to '.' if no filename specified
400 if (wcslen (pFileToFind
) == 0)
402 defaultFileName
[0] = L
'.';
403 defaultFileName
[1] = 0;
404 pFileToFind
= defaultFileName
;
408 finishedScanningDirectory
= FALSE
;
409 while (!finishedScanningDirectory
)
411 status
= vfatGetNextDirEntry (pDeviceExt
,
416 if (status
== STATUS_NO_MORE_ENTRIES
)
418 finishedScanningDirectory
= TRUE
;
421 else if (!NT_SUCCESS(status
))
426 DPRINT (" Index:%d longName:%S\n",
430 if (!vfatIsDirEntryDeleted (¤tDirEntry
)
431 && !vfatIsDirEntryVolume(¤tDirEntry
))
433 if (currentLongName
[0] != L
'\0' && wstrcmpjoki (currentLongName
, pFileToFind
))
435 DPRINT ("Match found, %S\n", currentLongName
);
436 status
= vfatMakeFCBFromDirEntry (pDeviceExt
,
446 vfatGetDirEntryName (¤tDirEntry
, currentEntryName
);
447 DPRINT (" entryName:%S\n", currentEntryName
);
449 if (wstrcmpjoki (currentEntryName
, pFileToFind
))
451 DPRINT ("Match found, %S\n", currentEntryName
);
452 status
= vfatMakeFCBFromDirEntry (pDeviceExt
,
464 return STATUS_OBJECT_NAME_NOT_FOUND
;
468 vfatGetFCBForFile (PDEVICE_EXTENSION pVCB
,
469 PVFATFCB
*pParentFCB
,
471 const PWSTR pFileName
)
474 WCHAR pathName
[MAX_PATH
];
475 WCHAR elementName
[MAX_PATH
];
476 PWCHAR currentElement
;
480 DPRINT ("vfatGetFCBForFile (%x,%x,%x,%S)\n",
486 // Trivial case, open of the root directory on volume
487 if (pFileName
[0] == L
'\0' || wcscmp (pFileName
, L
"\\") == 0)
489 DPRINT ("returning root FCB\n");
491 FCB
= vfatOpenRootFCB (pVCB
);
495 return (FCB
!= NULL
) ? STATUS_SUCCESS
: STATUS_OBJECT_PATH_NOT_FOUND
;
499 currentElement
= pFileName
+ 1;
500 wcscpy (pathName
, L
"\\");
501 FCB
= vfatOpenRootFCB (pVCB
);
505 // Parse filename and check each path element for existance and access
506 while (vfatGetNextPathElement (currentElement
) != 0)
508 // Skip blank directory levels
509 if ((vfatGetNextPathElement (currentElement
) - currentElement
) == 0)
515 DPRINT ("Parsing, currentElement:%S\n", currentElement
);
516 DPRINT (" parentFCB:%x FCB:%x\n", parentFCB
, FCB
);
518 // descend to next directory level
521 vfatReleaseFCB (pVCB
, parentFCB
);
524 // fail if element in FCB is not a directory
525 if (!vfatFCBIsDirectory (pVCB
, FCB
))
527 DPRINT ("Element in requested path is not a directory\n");
529 vfatReleaseFCB (pVCB
, FCB
);
534 return STATUS_OBJECT_PATH_NOT_FOUND
;
538 // Extract next directory level into dirName
539 vfatWSubString (pathName
,
541 vfatGetNextPathElement (currentElement
) - pFileName
);
542 DPRINT (" pathName:%S\n", pathName
);
544 FCB
= vfatGrabFCBFromTable (pVCB
, pathName
);
547 vfatWSubString (elementName
,
549 vfatGetNextPathElement (currentElement
) - currentElement
);
550 DPRINT (" elementName:%S\n", elementName
);
552 status
= vfatDirFindFile (pVCB
, parentFCB
, elementName
, &FCB
);
553 if (status
== STATUS_OBJECT_NAME_NOT_FOUND
)
555 *pParentFCB
= parentFCB
;
557 currentElement
= vfatGetNextPathElement(currentElement
);
558 if (*currentElement
== L
'\0' || vfatGetNextPathElement(currentElement
+ 1) == 0)
560 return STATUS_OBJECT_NAME_NOT_FOUND
;
564 return STATUS_OBJECT_PATH_NOT_FOUND
;
567 else if (!NT_SUCCESS (status
))
569 vfatReleaseFCB (pVCB
, parentFCB
);
576 currentElement
= vfatGetNextPathElement (currentElement
);
579 *pParentFCB
= parentFCB
;
582 return STATUS_SUCCESS
;