3 * Copyright (C) 2002, 2004 ReactOS Team
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 * COPYRIGHT: See COPYING in the top level directory
21 * PROJECT: ReactOS kernel
22 * FILE: drivers/filesystems/cdfs/fcb.c
23 * PURPOSE: CDROM (ISO 9660) filesystem driver
24 * PROGRAMMER: Art Yerkes
28 /* INCLUDES *****************************************************************/
35 /* FUNCTIONS ****************************************************************/
37 #define ROUND_UP(N, S) ((((N) + (S) - 1) / (S)) * (S))
40 /* FUNCTIONS ****************************************************************/
43 CdfsGetNextPathElement(PWCHAR FileName
)
45 if (*FileName
== L
'\0')
50 while (*FileName
!= L
'\0' && *FileName
!= L
'\\')
60 CdfsWSubString(LPWSTR pTarget
, LPCWSTR pSource
, size_t pLength
)
62 wcsncpy (pTarget
, pSource
, pLength
);
63 pTarget
[pLength
] = L
'\0';
68 CdfsCreateFCB(PCWSTR FileName
)
72 Fcb
= ExAllocatePoolWithTag(NonPagedPool
,
74 CDFS_NONPAGED_FCB_TAG
);
77 RtlZeroMemory(Fcb
, sizeof(FCB
));
78 RtlInitEmptyUnicodeString(&Fcb
->PathName
, Fcb
->PathNameBuffer
, sizeof(Fcb
->PathNameBuffer
));
82 RtlAppendUnicodeToString(&Fcb
->PathName
, FileName
);
83 if (wcsrchr(Fcb
->PathName
.Buffer
, '\\') != 0)
85 Fcb
->ObjectName
= wcsrchr(Fcb
->PathName
.Buffer
, '\\');
89 Fcb
->ObjectName
= Fcb
->PathName
.Buffer
;
93 ExInitializeResourceLite(&Fcb
->PagingIoResource
);
94 ExInitializeResourceLite(&Fcb
->MainResource
);
95 ExInitializeResourceLite(&Fcb
->NameListResource
);
96 Fcb
->RFCB
.PagingIoResource
= &Fcb
->PagingIoResource
;
97 Fcb
->RFCB
.Resource
= &Fcb
->MainResource
;
98 Fcb
->RFCB
.IsFastIoPossible
= FastIoIsNotPossible
;
99 InitializeListHead(&Fcb
->ShortNameList
);
100 FsRtlInitializeFileLock(&Fcb
->FileLock
, NULL
, NULL
);
107 CdfsDestroyFCB(PFCB Fcb
)
111 FsRtlUninitializeFileLock(&Fcb
->FileLock
);
112 ExDeleteResourceLite(&Fcb
->PagingIoResource
);
113 ExDeleteResourceLite(&Fcb
->MainResource
);
115 while (!IsListEmpty(&Fcb
->ShortNameList
))
117 Entry
= Fcb
->ShortNameList
.Flink
;
118 RemoveEntryList(Entry
);
119 ExFreePoolWithTag(Entry
, CDFS_SHORT_NAME_TAG
);
122 ExDeleteResourceLite(&Fcb
->NameListResource
);
123 ExFreePoolWithTag(Fcb
, CDFS_NONPAGED_FCB_TAG
);
128 CdfsFCBIsDirectory(PFCB Fcb
)
130 return(Fcb
->Entry
.FileFlags
& FILE_FLAG_DIRECTORY
);
135 CdfsFCBIsRoot(PFCB Fcb
)
137 return (Fcb
->PathName
.Length
== sizeof(WCHAR
) && Fcb
->PathName
.Buffer
[0] == L
'\\');
142 CdfsGrabFCB(PDEVICE_EXTENSION Vcb
,
147 DPRINT("grabbing FCB at %p: %S, refCount:%d\n",
152 KeAcquireSpinLock(&Vcb
->FcbListLock
, &oldIrql
);
154 KeReleaseSpinLock(&Vcb
->FcbListLock
, oldIrql
);
159 CdfsReleaseFCB(PDEVICE_EXTENSION Vcb
,
164 DPRINT("releasing FCB at %p: %wZ, refCount:%d\n",
169 KeAcquireSpinLock(&Vcb
->FcbListLock
, &oldIrql
);
171 if (Fcb
->RefCount
<= 0 && !CdfsFCBIsDirectory(Fcb
))
173 RemoveEntryList(&Fcb
->FcbListEntry
);
176 KeReleaseSpinLock(&Vcb
->FcbListLock
, oldIrql
);
181 CdfsAddFCBToTable(PDEVICE_EXTENSION Vcb
,
186 KeAcquireSpinLock(&Vcb
->FcbListLock
, &oldIrql
);
188 InsertTailList(&Vcb
->FcbListHead
, &Fcb
->FcbListEntry
);
189 KeReleaseSpinLock(&Vcb
->FcbListLock
, oldIrql
);
194 CdfsGrabFCBFromTable(PDEVICE_EXTENSION Vcb
,
195 PUNICODE_STRING FileName
)
199 PLIST_ENTRY current_entry
;
201 KeAcquireSpinLock(&Vcb
->FcbListLock
, &oldIrql
);
203 if (FileName
== NULL
|| FileName
->Length
== 0 || FileName
->Buffer
[0] == 0)
205 DPRINT("Return FCB for stream file object\n");
206 Fcb
= Vcb
->StreamFileObject
->FsContext
;
208 KeReleaseSpinLock(&Vcb
->FcbListLock
, oldIrql
);
212 current_entry
= Vcb
->FcbListHead
.Flink
;
213 while (current_entry
!= &Vcb
->FcbListHead
)
215 Fcb
= CONTAINING_RECORD(current_entry
, FCB
, FcbListEntry
);
217 DPRINT("Comparing '%wZ' and '%wZ'\n", FileName
, &Fcb
->PathName
);
218 if (RtlCompareUnicodeString(FileName
, &Fcb
->PathName
, TRUE
) == 0)
221 KeReleaseSpinLock(&Vcb
->FcbListLock
, oldIrql
);
225 //FIXME: need to compare against short name in FCB here
227 current_entry
= current_entry
->Flink
;
229 KeReleaseSpinLock(&Vcb
->FcbListLock
, oldIrql
);
236 CdfsFCBInitializeCache(PVCB Vcb
,
239 PFILE_OBJECT FileObject
;
242 FileObject
= IoCreateStreamFileObject(NULL
, Vcb
->StorageDevice
);
244 newCCB
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(CCB
), CDFS_CCB_TAG
);
247 return STATUS_INSUFFICIENT_RESOURCES
;
249 RtlZeroMemory(newCCB
,
252 FileObject
->ReadAccess
= TRUE
;
253 FileObject
->WriteAccess
= FALSE
;
254 FileObject
->DeleteAccess
= FALSE
;
255 FileObject
->SectionObjectPointer
= &Fcb
->SectionObjectPointers
;
256 FileObject
->FsContext
= Fcb
;
257 FileObject
->FsContext2
= newCCB
;
258 newCCB
->PtrFileObject
= FileObject
;
259 Fcb
->FileObject
= FileObject
;
264 CcInitializeCacheMap(FileObject
,
265 (PCC_FILE_SIZES
)(&Fcb
->RFCB
.AllocationSize
),
267 &(CdfsGlobalData
->CacheMgrCallbacks
),
270 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
272 FileObject
->FsContext2
= NULL
;
273 ExFreePoolWithTag(newCCB
, CDFS_CCB_TAG
);
274 ObDereferenceObject(FileObject
);
275 Fcb
->FileObject
= NULL
;
276 return _SEH2_GetExceptionCode();
280 ObDereferenceObject(FileObject
);
281 Fcb
->Flags
|= FCB_CACHE_INITIALIZED
;
283 return STATUS_SUCCESS
;
288 CdfsMakeRootFCB(PDEVICE_EXTENSION Vcb
)
292 Fcb
= CdfsCreateFCB(L
"\\");
294 Fcb
->Entry
.DataLengthL
= Vcb
->CdInfo
.RootSize
;
295 Fcb
->Entry
.ExtentLocationL
= Vcb
->CdInfo
.RootStart
;
296 Fcb
->Entry
.FileFlags
= FILE_FLAG_DIRECTORY
;
297 Fcb
->IndexNumber
.QuadPart
= 0LL;
300 Fcb
->RFCB
.FileSize
.QuadPart
= Vcb
->CdInfo
.RootSize
;
301 Fcb
->RFCB
.ValidDataLength
.QuadPart
= Vcb
->CdInfo
.RootSize
;
302 Fcb
->RFCB
.AllocationSize
.QuadPart
= Vcb
->CdInfo
.RootSize
;
304 CdfsFCBInitializeCache(Vcb
, Fcb
);
305 CdfsAddFCBToTable(Vcb
, Fcb
);
306 CdfsGrabFCB(Vcb
, Fcb
);
313 CdfsOpenRootFCB(PDEVICE_EXTENSION Vcb
)
315 UNICODE_STRING FileName
;
318 RtlInitUnicodeString(&FileName
, L
"\\");
320 Fcb
= CdfsGrabFCBFromTable(Vcb
,
324 Fcb
= CdfsMakeRootFCB(Vcb
);
332 CdfsMakeFCBFromDirEntry(PVCB Vcb
,
337 ULONG DirectorySector
,
338 ULONG DirectoryOffset
,
341 WCHAR pathName
[MAX_PATH
];
345 /* Check if the full string would overflow the pathName buffer (the additional characters are for '\\' and '\0') */
346 if ((LongName
[0] != 0) &&
347 ((DirectoryFCB
->PathName
.Length
/ sizeof(WCHAR
)) + 1 + wcslen(LongName
) + 1 > MAX_PATH
))
349 return(STATUS_OBJECT_NAME_INVALID
);
352 wcscpy(pathName
, DirectoryFCB
->PathName
.Buffer
);
353 if (!CdfsFCBIsRoot(DirectoryFCB
))
355 wcscat(pathName
, L
"\\");
358 if (LongName
[0] != 0)
360 wcscat(pathName
, LongName
);
364 WCHAR entryName
[MAX_PATH
];
366 CdfsGetDirEntryName(Vcb
, Record
, entryName
);
367 wcscat(pathName
, entryName
);
370 rcFCB
= CdfsCreateFCB(pathName
);
371 memcpy(&rcFCB
->Entry
, Record
, sizeof(DIR_RECORD
));
373 /* Copy short name into FCB */
374 rcFCB
->ShortNameU
.Length
= wcslen(ShortName
) * sizeof(WCHAR
);
375 rcFCB
->ShortNameU
.MaximumLength
= rcFCB
->ShortNameU
.Length
;
376 rcFCB
->ShortNameU
.Buffer
= rcFCB
->ShortNameBuffer
;
377 wcscpy(rcFCB
->ShortNameBuffer
, ShortName
);
379 Size
= rcFCB
->Entry
.DataLengthL
;
381 rcFCB
->RFCB
.FileSize
.QuadPart
= Size
;
382 rcFCB
->RFCB
.ValidDataLength
.QuadPart
= Size
;
383 rcFCB
->RFCB
.AllocationSize
.QuadPart
= ROUND_UP(Size
, BLOCKSIZE
);
384 if (CdfsFCBIsDirectory(rcFCB
))
386 CdfsFCBInitializeCache(Vcb
, rcFCB
);
388 rcFCB
->IndexNumber
.u
.HighPart
= DirectorySector
;
389 rcFCB
->IndexNumber
.u
.LowPart
= DirectoryOffset
;
391 CdfsAddFCBToTable(Vcb
, rcFCB
);
394 DPRINT("%S %u %I64d\n", LongName
, Size
, rcFCB
->RFCB
.AllocationSize
.QuadPart
);
396 return(STATUS_SUCCESS
);
401 CdfsAttachFCBToFileObject(PDEVICE_EXTENSION Vcb
,
403 PFILE_OBJECT FileObject
)
407 newCCB
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(CCB
), CDFS_CCB_TAG
);
410 return(STATUS_INSUFFICIENT_RESOURCES
);
412 memset(newCCB
, 0, sizeof(CCB
));
414 FileObject
->ReadAccess
= TRUE
;
415 FileObject
->WriteAccess
= FALSE
;
416 FileObject
->DeleteAccess
= FALSE
;
417 FileObject
->SectionObjectPointer
= &Fcb
->SectionObjectPointers
;
418 FileObject
->FsContext
= Fcb
;
419 FileObject
->FsContext2
= newCCB
;
420 newCCB
->PtrFileObject
= FileObject
;
423 if (CdfsFCBIsDirectory(Fcb
))
427 CcInitializeCacheMap(FileObject
,
428 (PCC_FILE_SIZES
)(&Fcb
->RFCB
.AllocationSize
),
430 &(CdfsGlobalData
->CacheMgrCallbacks
),
433 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
435 FileObject
->FsContext2
= NULL
;
436 ExFreePoolWithTag(newCCB
, CDFS_CCB_TAG
);
437 return _SEH2_GetExceptionCode();
440 Fcb
->Flags
|= FCB_CACHE_INITIALIZED
;
443 DPRINT("file open: fcb:%p file size: %u\n", Fcb
, Fcb
->Entry
.DataLengthL
);
445 return(STATUS_SUCCESS
);
450 CdfsDirFindFile(PDEVICE_EXTENSION DeviceExt
,
452 PUNICODE_STRING FileToFind
,
455 UNICODE_STRING TempName
;
464 LARGE_INTEGER StreamOffset
, OffsetOfEntry
;
467 WCHAR ShortNameBuffer
[13];
468 UNICODE_STRING ShortName
;
469 UNICODE_STRING LongName
;
470 UNICODE_STRING FileToFindUpcase
;
473 ASSERT(DirectoryFcb
);
476 DPRINT("CdfsDirFindFile(VCB:%p, dirFCB:%p, File:%wZ)\n",
480 DPRINT("Dir Path:%wZ\n", &DirectoryFcb
->PathName
);
482 /* default to '.' if no filename specified */
483 if (FileToFind
->Length
== 0)
485 RtlInitUnicodeString(&TempName
, L
".");
486 FileToFind
= &TempName
;
489 DirSize
= DirectoryFcb
->Entry
.DataLengthL
;
490 StreamOffset
.QuadPart
= (LONGLONG
)DirectoryFcb
->Entry
.ExtentLocationL
* (LONGLONG
)BLOCKSIZE
;
494 CcMapData(DeviceExt
->StreamFileObject
, &StreamOffset
, BLOCKSIZE
, MAP_WAIT
, &Context
, &Block
);
496 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
498 DPRINT("CcMapData() failed\n");
499 _SEH2_YIELD(return _SEH2_GetExceptionCode());
505 Record
= (PDIR_RECORD
)Block
;
507 /* Upper case the expression for FsRtlIsNameInExpression */
508 Status
= RtlUpcaseUnicodeString(&FileToFindUpcase
, FileToFind
, TRUE
);
509 if (!NT_SUCCESS(Status
))
516 if (Record
->RecordLength
== 0)
518 DPRINT("RecordLength == 0 Stopped!\n");
522 DPRINT("RecordLength %u ExtAttrRecordLength %u NameLength %u\n",
523 Record
->RecordLength
, Record
->ExtAttrRecordLength
, Record
->FileIdLength
);
525 if (!CdfsIsRecordValid(DeviceExt
, Record
))
527 RtlFreeUnicodeString(&FileToFindUpcase
);
528 CcUnpinData(Context
);
529 return STATUS_DISK_CORRUPT_ERROR
;
532 CdfsGetDirEntryName(DeviceExt
, Record
, Name
);
533 DPRINT ("Name '%S'\n", Name
);
534 DPRINT ("Sector %lu\n", DirectoryFcb
->Entry
.ExtentLocationL
);
535 DPRINT ("Offset %lu\n", Offset
);
537 RtlInitUnicodeString(&LongName
, Name
);
538 RtlInitEmptyUnicodeString(&ShortName
, ShortNameBuffer
, sizeof(ShortNameBuffer
));
539 RtlZeroMemory(ShortNameBuffer
, sizeof(ShortNameBuffer
));
541 OffsetOfEntry
.QuadPart
= StreamOffset
.QuadPart
+ Offset
;
542 CdfsShortNameCacheGet(DirectoryFcb
, &OffsetOfEntry
, &LongName
, &ShortName
);
544 DPRINT("ShortName '%wZ'\n", &ShortName
);
546 ASSERT(LongName
.Length
>= sizeof(WCHAR
));
547 ASSERT(ShortName
.Length
>= sizeof(WCHAR
));
548 if (FsRtlIsNameInExpression(&FileToFindUpcase
, &LongName
, TRUE
, NULL
) ||
549 FsRtlIsNameInExpression(&FileToFindUpcase
, &ShortName
, TRUE
, NULL
))
551 DPRINT("Match found, %S\n", Name
);
552 Status
= CdfsMakeFCBFromDirEntry(DeviceExt
,
557 DirectoryFcb
->Entry
.ExtentLocationL
,
561 RtlFreeUnicodeString(&FileToFindUpcase
);
562 CcUnpinData(Context
);
567 Offset
+= Record
->RecordLength
;
568 BlockOffset
+= Record
->RecordLength
;
569 Record
= (PDIR_RECORD
)((ULONG_PTR
)Block
+ BlockOffset
);
570 if (BlockOffset
>= BLOCKSIZE
|| Record
->RecordLength
== 0)
572 DPRINT("Map next sector\n");
573 CcUnpinData(Context
);
574 StreamOffset
.QuadPart
+= BLOCKSIZE
;
575 Offset
= ROUND_UP(Offset
, BLOCKSIZE
);
580 CcMapData(DeviceExt
->StreamFileObject
, &StreamOffset
, BLOCKSIZE
, MAP_WAIT
, &Context
, &Block
);
582 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
584 DPRINT("CcMapData() failed\n");
585 RtlFreeUnicodeString(&FileToFindUpcase
);
586 _SEH2_YIELD(return _SEH2_GetExceptionCode());
589 Record
= (PDIR_RECORD
)((ULONG_PTR
)Block
+ BlockOffset
);
592 if (Offset
>= DirSize
)
596 RtlFreeUnicodeString(&FileToFindUpcase
);
597 CcUnpinData(Context
);
599 return(STATUS_OBJECT_NAME_NOT_FOUND
);
604 CdfsGetFCBForFile(PDEVICE_EXTENSION Vcb
,
607 PUNICODE_STRING FileName
)
609 UNICODE_STRING PathName
;
610 UNICODE_STRING ElementName
;
612 WCHAR pathName
[MAX_PATH
];
613 WCHAR elementName
[MAX_PATH
];
614 PWCHAR currentElement
;
618 DPRINT("CdfsGetFCBForFile(%p, %p, %p, '%wZ')\n",
624 /* Trivial case, open of the root directory on volume */
625 if (FileName
->Buffer
[0] == L
'\0' || wcscmp(FileName
->Buffer
, L
"\\") == 0)
627 DPRINT("returning root FCB\n");
629 FCB
= CdfsOpenRootFCB(Vcb
);
633 return((FCB
!= NULL
) ? STATUS_SUCCESS
: STATUS_OBJECT_PATH_NOT_FOUND
);
637 currentElement
= &FileName
->Buffer
[1];
638 wcscpy (pathName
, L
"\\");
639 FCB
= CdfsOpenRootFCB (Vcb
);
643 /* Parse filename and check each path element for existance and access */
644 while (CdfsGetNextPathElement(currentElement
) != 0)
646 /* Skip blank directory levels */
647 if ((CdfsGetNextPathElement(currentElement
) - currentElement
) == 0)
653 DPRINT("Parsing, currentElement:%S\n", currentElement
);
654 DPRINT(" parentFCB:%p FCB:%p\n", parentFCB
, FCB
);
656 /* Descend to next directory level */
659 CdfsReleaseFCB(Vcb
, parentFCB
);
663 /* fail if element in FCB is not a directory */
664 if (!CdfsFCBIsDirectory(FCB
))
666 DPRINT("Element in requested path is not a directory\n");
668 CdfsReleaseFCB(Vcb
, FCB
);
673 return(STATUS_OBJECT_PATH_NOT_FOUND
);
677 /* Extract next directory level into dirName */
678 CdfsWSubString(pathName
,
680 CdfsGetNextPathElement(currentElement
) - FileName
->Buffer
);
681 DPRINT(" pathName:%S\n", pathName
);
683 RtlInitUnicodeString(&PathName
, pathName
);
685 FCB
= CdfsGrabFCBFromTable(Vcb
, &PathName
);
688 CdfsWSubString(elementName
,
690 CdfsGetNextPathElement(currentElement
) - currentElement
);
691 DPRINT(" elementName:%S\n", elementName
);
693 RtlInitUnicodeString(&ElementName
, elementName
);
694 Status
= CdfsDirFindFile(Vcb
,
698 if (Status
== STATUS_OBJECT_NAME_NOT_FOUND
)
700 *pParentFCB
= parentFCB
;
702 currentElement
= CdfsGetNextPathElement(currentElement
);
703 if (*currentElement
== L
'\0' || CdfsGetNextPathElement(currentElement
+ 1) == 0)
705 return(STATUS_OBJECT_NAME_NOT_FOUND
);
709 return(STATUS_OBJECT_PATH_NOT_FOUND
);
712 else if (!NT_SUCCESS(Status
))
714 CdfsReleaseFCB(Vcb
, parentFCB
);
721 currentElement
= CdfsGetNextPathElement(currentElement
);
724 *pParentFCB
= parentFCB
;
727 return STATUS_SUCCESS
;