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(PCUNICODE_STRING CurrentElement
, PUNICODE_STRING NextElement
)
45 *NextElement
= *CurrentElement
;
47 if (NextElement
->Length
== 0)
50 while ((NextElement
->Length
) && (NextElement
->Buffer
[0] != L
'\\'))
52 NextElement
->Buffer
++;
53 NextElement
->Length
-= sizeof(WCHAR
);
54 NextElement
->MaximumLength
-= sizeof(WCHAR
);
62 CdfsCreateFCB(PCWSTR FileName
)
66 Fcb
= ExAllocatePoolWithTag(NonPagedPool
,
68 CDFS_NONPAGED_FCB_TAG
);
71 RtlZeroMemory(Fcb
, sizeof(FCB
));
72 RtlInitEmptyUnicodeString(&Fcb
->PathName
, Fcb
->PathNameBuffer
, sizeof(Fcb
->PathNameBuffer
));
76 RtlAppendUnicodeToString(&Fcb
->PathName
, FileName
);
77 if (wcsrchr(Fcb
->PathName
.Buffer
, '\\') != 0)
79 Fcb
->ObjectName
= wcsrchr(Fcb
->PathName
.Buffer
, '\\');
83 Fcb
->ObjectName
= Fcb
->PathName
.Buffer
;
87 ExInitializeResourceLite(&Fcb
->PagingIoResource
);
88 ExInitializeResourceLite(&Fcb
->MainResource
);
89 ExInitializeResourceLite(&Fcb
->NameListResource
);
90 Fcb
->RFCB
.PagingIoResource
= &Fcb
->PagingIoResource
;
91 Fcb
->RFCB
.Resource
= &Fcb
->MainResource
;
92 Fcb
->RFCB
.IsFastIoPossible
= FastIoIsNotPossible
;
93 InitializeListHead(&Fcb
->ShortNameList
);
94 FsRtlInitializeFileLock(&Fcb
->FileLock
, NULL
, NULL
);
101 CdfsDestroyFCB(PFCB Fcb
)
105 FsRtlUninitializeFileLock(&Fcb
->FileLock
);
106 ExDeleteResourceLite(&Fcb
->PagingIoResource
);
107 ExDeleteResourceLite(&Fcb
->MainResource
);
109 while (!IsListEmpty(&Fcb
->ShortNameList
))
111 Entry
= Fcb
->ShortNameList
.Flink
;
112 RemoveEntryList(Entry
);
113 ExFreePoolWithTag(Entry
, CDFS_SHORT_NAME_TAG
);
116 ExDeleteResourceLite(&Fcb
->NameListResource
);
117 ExFreePoolWithTag(Fcb
, CDFS_NONPAGED_FCB_TAG
);
122 CdfsFCBIsDirectory(PFCB Fcb
)
124 return(Fcb
->Entry
.FileFlags
& FILE_FLAG_DIRECTORY
);
129 CdfsFCBIsRoot(PFCB Fcb
)
131 return (Fcb
->PathName
.Length
== sizeof(WCHAR
) && Fcb
->PathName
.Buffer
[0] == L
'\\');
136 CdfsGrabFCB(PDEVICE_EXTENSION Vcb
,
141 DPRINT("grabbing FCB at %p: %wZ, refCount:%d\n",
146 KeAcquireSpinLock(&Vcb
->FcbListLock
, &oldIrql
);
148 KeReleaseSpinLock(&Vcb
->FcbListLock
, oldIrql
);
153 CdfsReleaseFCB(PDEVICE_EXTENSION Vcb
,
158 DPRINT("releasing FCB at %p: %wZ, refCount:%d\n",
163 KeAcquireSpinLock(&Vcb
->FcbListLock
, &oldIrql
);
165 if (Fcb
->RefCount
<= 0 && !CdfsFCBIsDirectory(Fcb
))
167 RemoveEntryList(&Fcb
->FcbListEntry
);
170 KeReleaseSpinLock(&Vcb
->FcbListLock
, oldIrql
);
175 CdfsAddFCBToTable(PDEVICE_EXTENSION Vcb
,
180 KeAcquireSpinLock(&Vcb
->FcbListLock
, &oldIrql
);
182 InsertTailList(&Vcb
->FcbListHead
, &Fcb
->FcbListEntry
);
183 KeReleaseSpinLock(&Vcb
->FcbListLock
, oldIrql
);
188 CdfsGrabFCBFromTable(PDEVICE_EXTENSION Vcb
,
189 PUNICODE_STRING FileName
)
193 PLIST_ENTRY current_entry
;
195 KeAcquireSpinLock(&Vcb
->FcbListLock
, &oldIrql
);
197 if (FileName
== NULL
|| FileName
->Length
== 0 || FileName
->Buffer
[0] == 0)
199 DPRINT("Return FCB for stream file object\n");
200 Fcb
= Vcb
->StreamFileObject
->FsContext
;
202 KeReleaseSpinLock(&Vcb
->FcbListLock
, oldIrql
);
206 current_entry
= Vcb
->FcbListHead
.Flink
;
207 while (current_entry
!= &Vcb
->FcbListHead
)
209 Fcb
= CONTAINING_RECORD(current_entry
, FCB
, FcbListEntry
);
211 // Disabled the DPRINT! Can't be called at DISPATCH_LEVEL!
212 //DPRINT("Comparing '%wZ' and '%wZ'\n", FileName, &Fcb->PathName);
213 if (RtlCompareUnicodeString(FileName
, &Fcb
->PathName
, TRUE
) == 0)
216 KeReleaseSpinLock(&Vcb
->FcbListLock
, oldIrql
);
220 //FIXME: need to compare against short name in FCB here
222 current_entry
= current_entry
->Flink
;
224 KeReleaseSpinLock(&Vcb
->FcbListLock
, oldIrql
);
231 CdfsFCBInitializeCache(PVCB Vcb
,
234 PFILE_OBJECT FileObject
;
237 FileObject
= IoCreateStreamFileObject(NULL
, Vcb
->StorageDevice
);
239 newCCB
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(CCB
), CDFS_CCB_TAG
);
242 return STATUS_INSUFFICIENT_RESOURCES
;
244 RtlZeroMemory(newCCB
,
247 FileObject
->ReadAccess
= TRUE
;
248 FileObject
->WriteAccess
= FALSE
;
249 FileObject
->DeleteAccess
= FALSE
;
250 FileObject
->SectionObjectPointer
= &Fcb
->SectionObjectPointers
;
251 FileObject
->FsContext
= Fcb
;
252 FileObject
->FsContext2
= newCCB
;
253 newCCB
->PtrFileObject
= FileObject
;
254 Fcb
->FileObject
= FileObject
;
259 CcInitializeCacheMap(FileObject
,
260 (PCC_FILE_SIZES
)(&Fcb
->RFCB
.AllocationSize
),
262 &(CdfsGlobalData
->CacheMgrCallbacks
),
265 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
267 FileObject
->FsContext2
= NULL
;
268 ExFreePoolWithTag(newCCB
, CDFS_CCB_TAG
);
269 ObDereferenceObject(FileObject
);
270 Fcb
->FileObject
= NULL
;
271 return _SEH2_GetExceptionCode();
275 ObDereferenceObject(FileObject
);
276 Fcb
->Flags
|= FCB_CACHE_INITIALIZED
;
278 return STATUS_SUCCESS
;
283 CdfsMakeRootFCB(PDEVICE_EXTENSION Vcb
)
287 Fcb
= CdfsCreateFCB(L
"\\");
289 Fcb
->Entry
.DataLengthL
= Vcb
->CdInfo
.RootSize
;
290 Fcb
->Entry
.ExtentLocationL
= Vcb
->CdInfo
.RootStart
;
291 Fcb
->Entry
.FileFlags
= FILE_FLAG_DIRECTORY
;
292 Fcb
->IndexNumber
.QuadPart
= 0LL;
295 Fcb
->RFCB
.FileSize
.QuadPart
= Vcb
->CdInfo
.RootSize
;
296 Fcb
->RFCB
.ValidDataLength
.QuadPart
= Vcb
->CdInfo
.RootSize
;
297 Fcb
->RFCB
.AllocationSize
.QuadPart
= Vcb
->CdInfo
.RootSize
;
299 CdfsFCBInitializeCache(Vcb
, Fcb
);
300 CdfsAddFCBToTable(Vcb
, Fcb
);
301 CdfsGrabFCB(Vcb
, Fcb
);
308 CdfsOpenRootFCB(PDEVICE_EXTENSION Vcb
)
310 UNICODE_STRING FileName
;
313 RtlInitUnicodeString(&FileName
, L
"\\");
315 Fcb
= CdfsGrabFCBFromTable(Vcb
,
319 Fcb
= CdfsMakeRootFCB(Vcb
);
327 CdfsMakeFCBFromDirEntry(PVCB Vcb
,
332 ULONG DirectorySector
,
333 ULONG DirectoryOffset
,
336 WCHAR pathName
[MAX_PATH
];
340 /* Check if the full string would overflow the pathName buffer (the additional characters are for '\\' and '\0') */
341 if ((LongName
[0] != 0) &&
342 ((DirectoryFCB
->PathName
.Length
/ sizeof(WCHAR
)) + 1 + wcslen(LongName
) + 1 > MAX_PATH
))
344 return(STATUS_OBJECT_NAME_INVALID
);
347 wcscpy(pathName
, DirectoryFCB
->PathName
.Buffer
);
348 if (!CdfsFCBIsRoot(DirectoryFCB
))
350 wcscat(pathName
, L
"\\");
353 if (LongName
[0] != 0)
355 wcscat(pathName
, LongName
);
359 WCHAR entryName
[MAX_PATH
];
361 CdfsGetDirEntryName(Vcb
, Record
, entryName
);
362 wcscat(pathName
, entryName
);
365 rcFCB
= CdfsCreateFCB(pathName
);
366 memcpy(&rcFCB
->Entry
, Record
, sizeof(DIR_RECORD
));
368 /* Copy short name into FCB */
369 rcFCB
->ShortNameU
.Length
= wcslen(ShortName
) * sizeof(WCHAR
);
370 rcFCB
->ShortNameU
.MaximumLength
= rcFCB
->ShortNameU
.Length
;
371 rcFCB
->ShortNameU
.Buffer
= rcFCB
->ShortNameBuffer
;
372 wcscpy(rcFCB
->ShortNameBuffer
, ShortName
);
374 Size
= rcFCB
->Entry
.DataLengthL
;
376 rcFCB
->RFCB
.FileSize
.QuadPart
= Size
;
377 rcFCB
->RFCB
.ValidDataLength
.QuadPart
= Size
;
378 rcFCB
->RFCB
.AllocationSize
.QuadPart
= ROUND_UP(Size
, BLOCKSIZE
);
379 if (CdfsFCBIsDirectory(rcFCB
))
381 CdfsFCBInitializeCache(Vcb
, rcFCB
);
383 rcFCB
->IndexNumber
.u
.HighPart
= DirectorySector
;
384 rcFCB
->IndexNumber
.u
.LowPart
= DirectoryOffset
;
386 CdfsAddFCBToTable(Vcb
, rcFCB
);
389 DPRINT("%S %u %I64d\n", LongName
, Size
, rcFCB
->RFCB
.AllocationSize
.QuadPart
);
391 return(STATUS_SUCCESS
);
396 CdfsAttachFCBToFileObject(PDEVICE_EXTENSION Vcb
,
398 PFILE_OBJECT FileObject
)
402 newCCB
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(CCB
), CDFS_CCB_TAG
);
405 return(STATUS_INSUFFICIENT_RESOURCES
);
407 memset(newCCB
, 0, sizeof(CCB
));
409 FileObject
->ReadAccess
= TRUE
;
410 FileObject
->WriteAccess
= FALSE
;
411 FileObject
->DeleteAccess
= FALSE
;
412 FileObject
->SectionObjectPointer
= &Fcb
->SectionObjectPointers
;
413 FileObject
->FsContext
= Fcb
;
414 FileObject
->FsContext2
= newCCB
;
415 newCCB
->PtrFileObject
= FileObject
;
418 if (CdfsFCBIsDirectory(Fcb
))
422 CcInitializeCacheMap(FileObject
,
423 (PCC_FILE_SIZES
)(&Fcb
->RFCB
.AllocationSize
),
425 &(CdfsGlobalData
->CacheMgrCallbacks
),
428 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
430 FileObject
->FsContext2
= NULL
;
431 ExFreePoolWithTag(newCCB
, CDFS_CCB_TAG
);
432 return _SEH2_GetExceptionCode();
435 Fcb
->Flags
|= FCB_CACHE_INITIALIZED
;
438 DPRINT("file open: fcb:%p file size: %u\n", Fcb
, Fcb
->Entry
.DataLengthL
);
440 return(STATUS_SUCCESS
);
445 CdfsDirFindFile(PDEVICE_EXTENSION DeviceExt
,
447 PUNICODE_STRING FileToFind
,
450 UNICODE_STRING TempName
;
459 LARGE_INTEGER StreamOffset
, OffsetOfEntry
;
462 WCHAR ShortNameBuffer
[13];
463 UNICODE_STRING ShortName
;
464 UNICODE_STRING LongName
;
465 UNICODE_STRING FileToFindUpcase
;
468 ASSERT(DirectoryFcb
);
471 DPRINT("CdfsDirFindFile(VCB:%p, dirFCB:%p, File:%wZ)\n",
475 DPRINT("Dir Path:%wZ\n", &DirectoryFcb
->PathName
);
477 /* default to '.' if no filename specified */
478 if (FileToFind
->Length
== 0)
480 RtlInitUnicodeString(&TempName
, L
".");
481 FileToFind
= &TempName
;
484 DirSize
= DirectoryFcb
->Entry
.DataLengthL
;
485 StreamOffset
.QuadPart
= (LONGLONG
)DirectoryFcb
->Entry
.ExtentLocationL
* (LONGLONG
)BLOCKSIZE
;
489 CcMapData(DeviceExt
->StreamFileObject
, &StreamOffset
, BLOCKSIZE
, MAP_WAIT
, &Context
, &Block
);
491 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
493 DPRINT("CcMapData() failed\n");
494 _SEH2_YIELD(return _SEH2_GetExceptionCode());
500 Record
= (PDIR_RECORD
)Block
;
502 /* Upper case the expression for FsRtlIsNameInExpression */
503 Status
= RtlUpcaseUnicodeString(&FileToFindUpcase
, FileToFind
, TRUE
);
504 if (!NT_SUCCESS(Status
))
511 if (Record
->RecordLength
== 0)
513 DPRINT("RecordLength == 0 Stopped!\n");
517 DPRINT("RecordLength %u ExtAttrRecordLength %u NameLength %u\n",
518 Record
->RecordLength
, Record
->ExtAttrRecordLength
, Record
->FileIdLength
);
520 if (!CdfsIsRecordValid(DeviceExt
, Record
))
522 RtlFreeUnicodeString(&FileToFindUpcase
);
523 CcUnpinData(Context
);
524 return STATUS_DISK_CORRUPT_ERROR
;
527 CdfsGetDirEntryName(DeviceExt
, Record
, Name
);
528 DPRINT ("Name '%S'\n", Name
);
529 DPRINT ("Sector %lu\n", DirectoryFcb
->Entry
.ExtentLocationL
);
530 DPRINT ("Offset %lu\n", Offset
);
532 RtlInitUnicodeString(&LongName
, Name
);
533 RtlInitEmptyUnicodeString(&ShortName
, ShortNameBuffer
, sizeof(ShortNameBuffer
));
534 RtlZeroMemory(ShortNameBuffer
, sizeof(ShortNameBuffer
));
536 OffsetOfEntry
.QuadPart
= StreamOffset
.QuadPart
+ Offset
;
537 CdfsShortNameCacheGet(DirectoryFcb
, &OffsetOfEntry
, &LongName
, &ShortName
);
539 DPRINT("ShortName '%wZ'\n", &ShortName
);
541 ASSERT(LongName
.Length
>= sizeof(WCHAR
));
542 ASSERT(ShortName
.Length
>= sizeof(WCHAR
));
543 if (FsRtlIsNameInExpression(&FileToFindUpcase
, &LongName
, TRUE
, NULL
) ||
544 FsRtlIsNameInExpression(&FileToFindUpcase
, &ShortName
, TRUE
, NULL
))
546 DPRINT("Match found, %S\n", Name
);
547 Status
= CdfsMakeFCBFromDirEntry(DeviceExt
,
552 DirectoryFcb
->Entry
.ExtentLocationL
,
556 RtlFreeUnicodeString(&FileToFindUpcase
);
557 CcUnpinData(Context
);
562 Offset
+= Record
->RecordLength
;
563 BlockOffset
+= Record
->RecordLength
;
564 Record
= (PDIR_RECORD
)((ULONG_PTR
)Block
+ BlockOffset
);
565 if (BlockOffset
>= BLOCKSIZE
|| Record
->RecordLength
== 0)
567 DPRINT("Map next sector\n");
568 CcUnpinData(Context
);
569 StreamOffset
.QuadPart
+= BLOCKSIZE
;
570 Offset
= ROUND_UP(Offset
, BLOCKSIZE
);
575 CcMapData(DeviceExt
->StreamFileObject
, &StreamOffset
, BLOCKSIZE
, MAP_WAIT
, &Context
, &Block
);
577 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
579 DPRINT("CcMapData() failed\n");
580 RtlFreeUnicodeString(&FileToFindUpcase
);
581 _SEH2_YIELD(return _SEH2_GetExceptionCode());
584 Record
= (PDIR_RECORD
)((ULONG_PTR
)Block
+ BlockOffset
);
587 if (Offset
>= DirSize
)
591 RtlFreeUnicodeString(&FileToFindUpcase
);
592 CcUnpinData(Context
);
594 return(STATUS_OBJECT_NAME_NOT_FOUND
);
599 CdfsGetFCBForFile(PDEVICE_EXTENSION Vcb
,
602 PUNICODE_STRING FileName
)
604 UNICODE_STRING PathName
;
605 UNICODE_STRING NextElement
;
606 UNICODE_STRING CurrentElement
;
611 DPRINT("CdfsGetFCBForFile(%p, %p, %p, '%wZ')\n",
617 /* Trivial case, open of the root directory on volume */
618 if (FileName
->Length
== 0 ||
619 ((FileName
->Buffer
[0] == '\\') && (FileName
->Length
== sizeof(WCHAR
))))
621 DPRINT("returning root FCB\n");
623 FCB
= CdfsOpenRootFCB(Vcb
);
627 return((FCB
!= NULL
) ? STATUS_SUCCESS
: STATUS_OBJECT_PATH_NOT_FOUND
);
631 /* Start with empty path */
632 PathName
= *FileName
;
634 CurrentElement
= *FileName
;
636 FCB
= CdfsOpenRootFCB (Vcb
);
640 /* Parse filename and check each path element for existence and access */
641 while (CdfsGetNextPathElement(&CurrentElement
, &NextElement
))
643 /* Skip blank directory levels */
644 if (CurrentElement
.Buffer
[0] == L
'\\')
646 CurrentElement
.Buffer
++;
647 CurrentElement
.Length
-= sizeof(WCHAR
);
648 CurrentElement
.MaximumLength
-= sizeof(WCHAR
);
652 DPRINT("Parsing, currentElement:%wZ\n", &CurrentElement
);
653 DPRINT(" parentFCB:%p FCB:%p\n", parentFCB
, FCB
);
655 /* Descend to next directory level */
658 CdfsReleaseFCB(Vcb
, parentFCB
);
662 /* fail if element in FCB is not a directory */
663 if (!CdfsFCBIsDirectory(FCB
))
665 DPRINT("Element in requested path is not a directory\n");
667 CdfsReleaseFCB(Vcb
, FCB
);
672 return(STATUS_OBJECT_PATH_NOT_FOUND
);
676 /* Extract next directory level */
677 PathName
.Length
= (NextElement
.Buffer
- FileName
->Buffer
) * sizeof(WCHAR
);
678 DPRINT(" PathName:%wZ\n", &PathName
);
680 FCB
= CdfsGrabFCBFromTable(Vcb
, &PathName
);
683 UNICODE_STRING ChildElement
= CurrentElement
;
684 ChildElement
.Length
= (NextElement
.Buffer
- CurrentElement
.Buffer
) * sizeof(WCHAR
);
686 Status
= CdfsDirFindFile(Vcb
,
690 if (Status
== STATUS_OBJECT_NAME_NOT_FOUND
)
692 *pParentFCB
= parentFCB
;
695 if (NextElement
.Length
== 0)
697 return(STATUS_OBJECT_NAME_NOT_FOUND
);
701 return(STATUS_OBJECT_PATH_NOT_FOUND
);
704 else if (!NT_SUCCESS(Status
))
706 CdfsReleaseFCB(Vcb
, parentFCB
);
713 CurrentElement
= NextElement
;
716 *pParentFCB
= parentFCB
;
719 return STATUS_SUCCESS
;