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.
21 * COPYRIGHT: See COPYING in the top level directory
22 * PROJECT: ReactOS kernel
23 * FILE: services/fs/cdfs/fcb.c
24 * PURPOSE: CDROM (ISO 9660) filesystem driver
25 * PROGRAMMER: Art Yerkes
29 /* INCLUDES *****************************************************************/
36 /* FUNCTIONS ****************************************************************/
38 #define ROUND_UP(N, S) ((((N) + (S) - 1) / (S)) * (S))
41 /* FUNCTIONS ****************************************************************/
44 CdfsGetNextPathElement(PWCHAR FileName
)
46 if (*FileName
== L
'\0')
51 while (*FileName
!= L
'\0' && *FileName
!= L
'\\')
61 CdfsWSubString(PWCHAR pTarget
, const PWCHAR pSource
, size_t pLength
)
63 wcsncpy (pTarget
, pSource
, pLength
);
64 pTarget
[pLength
] = L
'\0';
69 CdfsCreateFCB(PCWSTR FileName
)
73 Fcb
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(FCB
), TAG_FCB
);
76 RtlZeroMemory(Fcb
, sizeof(FCB
));
80 wcscpy(Fcb
->PathName
, FileName
);
81 if (wcsrchr(Fcb
->PathName
, '\\') != 0)
83 Fcb
->ObjectName
= wcsrchr(Fcb
->PathName
, '\\');
87 Fcb
->ObjectName
= Fcb
->PathName
;
91 ExInitializeResourceLite(&Fcb
->PagingIoResource
);
92 ExInitializeResourceLite(&Fcb
->MainResource
);
93 ExInitializeResourceLite(&Fcb
->NameListResource
);
94 Fcb
->RFCB
.PagingIoResource
= &Fcb
->PagingIoResource
;
95 Fcb
->RFCB
.Resource
= &Fcb
->MainResource
;
96 Fcb
->RFCB
.IsFastIoPossible
= FastIoIsNotPossible
;
97 InitializeListHead(&Fcb
->ShortNameList
);
104 CdfsDestroyFCB(PFCB Fcb
)
108 ExDeleteResourceLite(&Fcb
->PagingIoResource
);
109 ExDeleteResourceLite(&Fcb
->MainResource
);
111 while (!IsListEmpty(&Fcb
->ShortNameList
))
113 Entry
= Fcb
->ShortNameList
.Flink
;
114 RemoveEntryList(Entry
);
115 ExFreePoolWithTag(Entry
, TAG_FCB
);
118 ExDeleteResourceLite(&Fcb
->NameListResource
);
119 ExFreePoolWithTag(Fcb
, TAG_FCB
);
124 CdfsFCBIsDirectory(PFCB Fcb
)
126 return(Fcb
->Entry
.FileFlags
& FILE_FLAG_DIRECTORY
);
131 CdfsFCBIsRoot(PFCB Fcb
)
133 return(wcscmp(Fcb
->PathName
, L
"\\") == 0);
138 CdfsGrabFCB(PDEVICE_EXTENSION Vcb
,
143 DPRINT("grabbing FCB at %x: %S, refCount:%d\n",
148 KeAcquireSpinLock(&Vcb
->FcbListLock
, &oldIrql
);
150 KeReleaseSpinLock(&Vcb
->FcbListLock
, oldIrql
);
155 CdfsReleaseFCB(PDEVICE_EXTENSION Vcb
,
160 DPRINT("releasing FCB at %x: %S, refCount:%d\n",
165 KeAcquireSpinLock(&Vcb
->FcbListLock
, &oldIrql
);
167 if (Fcb
->RefCount
<= 0 && !CdfsFCBIsDirectory(Fcb
))
169 RemoveEntryList(&Fcb
->FcbListEntry
);
172 KeReleaseSpinLock(&Vcb
->FcbListLock
, oldIrql
);
177 CdfsAddFCBToTable(PDEVICE_EXTENSION Vcb
,
182 KeAcquireSpinLock(&Vcb
->FcbListLock
, &oldIrql
);
184 InsertTailList(&Vcb
->FcbListHead
, &Fcb
->FcbListEntry
);
185 KeReleaseSpinLock(&Vcb
->FcbListLock
, oldIrql
);
190 CdfsGrabFCBFromTable(PDEVICE_EXTENSION Vcb
,
191 PUNICODE_STRING FileName
)
195 PLIST_ENTRY current_entry
;
197 KeAcquireSpinLock(&Vcb
->FcbListLock
, &oldIrql
);
199 if (FileName
== NULL
|| FileName
->Length
== 0 || FileName
->Buffer
[0] == 0)
201 DPRINT("Return FCB for stream file object\n");
202 Fcb
= Vcb
->StreamFileObject
->FsContext
;
204 KeReleaseSpinLock(&Vcb
->FcbListLock
, oldIrql
);
208 current_entry
= Vcb
->FcbListHead
.Flink
;
209 while (current_entry
!= &Vcb
->FcbListHead
)
211 Fcb
= CONTAINING_RECORD(current_entry
, FCB
, FcbListEntry
);
213 DPRINT("Comparing '%wZ' and '%S'\n", FileName
, Fcb
->PathName
);
214 if (_wcsicmp(FileName
->Buffer
, Fcb
->PathName
) == 0)
217 KeReleaseSpinLock(&Vcb
->FcbListLock
, oldIrql
);
221 //FIXME: need to compare against short name in FCB here
223 current_entry
= current_entry
->Flink
;
225 KeReleaseSpinLock(&Vcb
->FcbListLock
, oldIrql
);
232 CdfsFCBInitializeCache(PVCB Vcb
,
235 PFILE_OBJECT FileObject
;
239 FileObject
= IoCreateStreamFileObject(NULL
, Vcb
->StorageDevice
);
241 newCCB
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(CCB
), TAG_CCB
);
244 return(STATUS_INSUFFICIENT_RESOURCES
);
246 RtlZeroMemory(newCCB
,
249 FileObject
->ReadAccess
= TRUE
;
250 FileObject
->WriteAccess
= FALSE
;
251 FileObject
->DeleteAccess
= FALSE
;
252 FileObject
->SectionObjectPointer
= &Fcb
->SectionObjectPointers
;
253 FileObject
->FsContext
= Fcb
;
254 FileObject
->FsContext2
= newCCB
;
255 newCCB
->PtrFileObject
= FileObject
;
256 Fcb
->FileObject
= FileObject
;
259 Status
= STATUS_SUCCESS
;
260 CcInitializeCacheMap(FileObject
,
261 (PCC_FILE_SIZES
)(&Fcb
->RFCB
.AllocationSize
),
263 &(CdfsGlobalData
->CacheMgrCallbacks
),
266 ObDereferenceObject(FileObject
);
267 Fcb
->Flags
|= FCB_CACHE_INITIALIZED
;
274 CdfsMakeRootFCB(PDEVICE_EXTENSION Vcb
)
278 Fcb
= CdfsCreateFCB(L
"\\");
280 Fcb
->Entry
.DataLengthL
= Vcb
->CdInfo
.RootSize
;
281 Fcb
->Entry
.ExtentLocationL
= Vcb
->CdInfo
.RootStart
;
282 Fcb
->Entry
.FileFlags
= FILE_FLAG_DIRECTORY
;
283 Fcb
->IndexNumber
.QuadPart
= 0LL;
286 Fcb
->RFCB
.FileSize
.QuadPart
= Vcb
->CdInfo
.RootSize
;
287 Fcb
->RFCB
.ValidDataLength
.QuadPart
= Vcb
->CdInfo
.RootSize
;
288 Fcb
->RFCB
.AllocationSize
.QuadPart
= Vcb
->CdInfo
.RootSize
;
290 CdfsFCBInitializeCache(Vcb
, Fcb
);
291 CdfsAddFCBToTable(Vcb
, Fcb
);
292 CdfsGrabFCB(Vcb
, Fcb
);
299 CdfsOpenRootFCB(PDEVICE_EXTENSION Vcb
)
301 UNICODE_STRING FileName
;
304 RtlInitUnicodeString(&FileName
, L
"\\");
306 Fcb
= CdfsGrabFCBFromTable(Vcb
,
310 Fcb
= CdfsMakeRootFCB(Vcb
);
318 CdfsGetDirEntryName(PDEVICE_EXTENSION DeviceExt
,
322 * FUNCTION: Retrieves the file name from a directory record.
325 if (Record
->FileIdLength
== 1 && Record
->FileId
[0] == 0)
329 else if (Record
->FileIdLength
== 1 && Record
->FileId
[0] == 1)
335 if (DeviceExt
->CdInfo
.JolietLevel
== 0)
339 for (i
= 0; i
< Record
->FileIdLength
&& Record
->FileId
[i
] != ';'; i
++)
340 Name
[i
] = (WCHAR
)Record
->FileId
[i
];
347 Record
->FileIdLength
);
351 DPRINT("Name '%S'\n", Name
);
356 CdfsMakeFCBFromDirEntry(PVCB Vcb
,
361 ULONG DirectorySector
,
362 ULONG DirectoryOffset
,
365 WCHAR pathName
[MAX_PATH
];
369 if (LongName
[0] != 0 && wcslen (DirectoryFCB
->PathName
) +
370 sizeof(WCHAR
) + wcslen (LongName
) > MAX_PATH
)
372 return(STATUS_OBJECT_NAME_INVALID
);
375 wcscpy(pathName
, DirectoryFCB
->PathName
);
376 if (!CdfsFCBIsRoot(DirectoryFCB
))
378 wcscat(pathName
, L
"\\");
381 if (LongName
[0] != 0)
383 wcscat(pathName
, LongName
);
387 WCHAR entryName
[MAX_PATH
];
389 CdfsGetDirEntryName(Vcb
, Record
, entryName
);
390 wcscat(pathName
, entryName
);
393 rcFCB
= CdfsCreateFCB(pathName
);
394 memcpy(&rcFCB
->Entry
, Record
, sizeof(DIR_RECORD
));
396 /* Copy short name into FCB */
397 rcFCB
->ShortNameU
.Length
= wcslen(ShortName
) * sizeof(WCHAR
);
398 rcFCB
->ShortNameU
.MaximumLength
= rcFCB
->ShortNameU
.Length
;
399 rcFCB
->ShortNameU
.Buffer
= rcFCB
->ShortNameBuffer
;
400 wcscpy(rcFCB
->ShortNameBuffer
, ShortName
);
402 Size
= rcFCB
->Entry
.DataLengthL
;
404 rcFCB
->RFCB
.FileSize
.QuadPart
= Size
;
405 rcFCB
->RFCB
.ValidDataLength
.QuadPart
= Size
;
406 rcFCB
->RFCB
.AllocationSize
.QuadPart
= ROUND_UP(Size
, BLOCKSIZE
);
407 if (CdfsFCBIsDirectory(rcFCB
))
409 CdfsFCBInitializeCache(Vcb
, rcFCB
);
411 rcFCB
->IndexNumber
.u
.HighPart
= DirectorySector
;
412 rcFCB
->IndexNumber
.u
.LowPart
= DirectoryOffset
;
414 CdfsAddFCBToTable(Vcb
, rcFCB
);
417 DPRINT("%S %d %I64d\n", LongName
, Size
, rcFCB
->RFCB
.AllocationSize
.QuadPart
);
419 return(STATUS_SUCCESS
);
424 CdfsAttachFCBToFileObject(PDEVICE_EXTENSION Vcb
,
426 PFILE_OBJECT FileObject
)
430 newCCB
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(CCB
), TAG_CCB
);
433 return(STATUS_INSUFFICIENT_RESOURCES
);
435 memset(newCCB
, 0, sizeof(CCB
));
437 FileObject
->SectionObjectPointer
= &Fcb
->SectionObjectPointers
;
438 FileObject
->FsContext
= Fcb
;
439 FileObject
->FsContext2
= newCCB
;
440 newCCB
->PtrFileObject
= FileObject
;
443 if (CdfsFCBIsDirectory(Fcb
))
445 CcInitializeCacheMap(FileObject
,
446 (PCC_FILE_SIZES
)(&Fcb
->RFCB
.AllocationSize
),
448 &(CdfsGlobalData
->CacheMgrCallbacks
),
450 Fcb
->Flags
|= FCB_CACHE_INITIALIZED
;
453 DPRINT("file open: fcb:%x file size: %d\n", Fcb
, Fcb
->Entry
.DataLengthL
);
455 return(STATUS_SUCCESS
);
460 CdfsDirFindFile(PDEVICE_EXTENSION DeviceExt
,
462 PUNICODE_STRING FileToFind
,
465 UNICODE_STRING TempName
;
474 LARGE_INTEGER StreamOffset
, OffsetOfEntry
;
477 WCHAR ShortNameBuffer
[13];
478 UNICODE_STRING ShortName
;
479 UNICODE_STRING LongName
;
480 UNICODE_STRING FileToFindUpcase
;
483 ASSERT(DirectoryFcb
);
486 DPRINT("CdfsDirFindFile(VCB:%p, dirFCB:%p, File:%wZ)\n",
490 DPRINT("Dir Path:%S\n", DirectoryFcb
->PathName
);
492 /* default to '.' if no filename specified */
493 if (FileToFind
->Length
== 0)
495 RtlInitUnicodeString(&TempName
, L
".");
496 FileToFind
= &TempName
;
499 DirSize
= DirectoryFcb
->Entry
.DataLengthL
;
500 StreamOffset
.QuadPart
= (LONGLONG
)DirectoryFcb
->Entry
.ExtentLocationL
* (LONGLONG
)BLOCKSIZE
;
502 if (!CcMapData(DeviceExt
->StreamFileObject
,
509 DPRINT("CcMapData() failed\n");
510 return STATUS_UNSUCCESSFUL
;
515 Record
= (PDIR_RECORD
)Block
;
517 /* Upper case the expression for FsRtlIsNameInExpression */
518 Status
= RtlUpcaseUnicodeString(&FileToFindUpcase
, FileToFind
, TRUE
);
519 if (!NT_SUCCESS(Status
))
526 if (Record
->RecordLength
== 0)
528 DPRINT("RecordLength == 0 Stopped!\n");
532 DPRINT("RecordLength %u ExtAttrRecordLength %u NameLength %u\n",
533 Record
->RecordLength
, Record
->ExtAttrRecordLength
, Record
->FileIdLength
);
535 CdfsGetDirEntryName(DeviceExt
, Record
, Name
);
536 DPRINT ("Name '%S'\n", Name
);
537 DPRINT ("Sector %lu\n", DirectoryFcb
->Entry
.ExtentLocationL
);
538 DPRINT ("Offset %lu\n", Offset
);
540 RtlInitUnicodeString(&LongName
, Name
);
541 ShortName
.Length
= 0;
542 ShortName
.MaximumLength
= 26;
543 ShortName
.Buffer
= ShortNameBuffer
;
544 memset(ShortNameBuffer
, 0, 26);
546 OffsetOfEntry
.QuadPart
= StreamOffset
.QuadPart
+ Offset
;
547 CdfsShortNameCacheGet(DirectoryFcb
, &OffsetOfEntry
, &LongName
, &ShortName
);
549 DPRINT("ShortName '%wZ'\n", &ShortName
);
551 if (FsRtlIsNameInExpression(&FileToFindUpcase
, &LongName
, TRUE
, NULL
) ||
552 FsRtlIsNameInExpression(&FileToFindUpcase
, &ShortName
, TRUE
, NULL
))
554 DPRINT("Match found, %S\n", Name
);
555 Status
= CdfsMakeFCBFromDirEntry(DeviceExt
,
560 DirectoryFcb
->Entry
.ExtentLocationL
,
564 RtlFreeUnicodeString(&FileToFindUpcase
);
565 CcUnpinData(Context
);
570 Offset
+= Record
->RecordLength
;
571 BlockOffset
+= Record
->RecordLength
;
572 Record
= (PDIR_RECORD
)((ULONG_PTR
)Block
+ BlockOffset
);
573 if (BlockOffset
>= BLOCKSIZE
|| Record
->RecordLength
== 0)
575 DPRINT("Map next sector\n");
576 CcUnpinData(Context
);
577 StreamOffset
.QuadPart
+= BLOCKSIZE
;
578 Offset
= ROUND_UP(Offset
, BLOCKSIZE
);
581 if (!CcMapData(DeviceExt
->StreamFileObject
,
586 DPRINT("CcMapData() failed\n");
587 RtlFreeUnicodeString(&FileToFindUpcase
);
588 return(STATUS_UNSUCCESSFUL
);
590 Record
= (PDIR_RECORD
)((ULONG_PTR
)Block
+ BlockOffset
);
593 if (Offset
>= DirSize
)
597 RtlFreeUnicodeString(&FileToFindUpcase
);
598 CcUnpinData(Context
);
600 return(STATUS_OBJECT_NAME_NOT_FOUND
);
605 CdfsGetFCBForFile(PDEVICE_EXTENSION Vcb
,
608 PUNICODE_STRING FileName
)
610 UNICODE_STRING PathName
;
611 UNICODE_STRING ElementName
;
613 WCHAR pathName
[MAX_PATH
];
614 WCHAR elementName
[MAX_PATH
];
615 PWCHAR currentElement
;
619 DPRINT("CdfsGetFCBForFile(%x, %x, %x, '%wZ')\n",
625 /* Trivial case, open of the root directory on volume */
626 if (FileName
->Buffer
[0] == L
'\0' || wcscmp(FileName
->Buffer
, L
"\\") == 0)
628 DPRINT("returning root FCB\n");
630 FCB
= CdfsOpenRootFCB(Vcb
);
634 return((FCB
!= NULL
) ? STATUS_SUCCESS
: STATUS_OBJECT_PATH_NOT_FOUND
);
638 currentElement
= &FileName
->Buffer
[1];
639 wcscpy (pathName
, L
"\\");
640 FCB
= CdfsOpenRootFCB (Vcb
);
644 /* Parse filename and check each path element for existance and access */
645 while (CdfsGetNextPathElement(currentElement
) != 0)
647 /* Skip blank directory levels */
648 if ((CdfsGetNextPathElement(currentElement
) - currentElement
) == 0)
654 DPRINT("Parsing, currentElement:%S\n", currentElement
);
655 DPRINT(" parentFCB:%x FCB:%x\n", parentFCB
, FCB
);
657 /* Descend to next directory level */
660 CdfsReleaseFCB(Vcb
, parentFCB
);
664 /* fail if element in FCB is not a directory */
665 if (!CdfsFCBIsDirectory(FCB
))
667 DPRINT("Element in requested path is not a directory\n");
669 CdfsReleaseFCB(Vcb
, FCB
);
674 return(STATUS_OBJECT_PATH_NOT_FOUND
);
678 /* Extract next directory level into dirName */
679 CdfsWSubString(pathName
,
681 CdfsGetNextPathElement(currentElement
) - FileName
->Buffer
);
682 DPRINT(" pathName:%S\n", pathName
);
684 RtlInitUnicodeString(&PathName
, pathName
);
686 FCB
= CdfsGrabFCBFromTable(Vcb
, &PathName
);
689 CdfsWSubString(elementName
,
691 CdfsGetNextPathElement(currentElement
) - currentElement
);
692 DPRINT(" elementName:%S\n", elementName
);
694 RtlInitUnicodeString(&ElementName
, elementName
);
695 Status
= CdfsDirFindFile(Vcb
,
699 if (Status
== STATUS_OBJECT_NAME_NOT_FOUND
)
701 *pParentFCB
= parentFCB
;
703 currentElement
= CdfsGetNextPathElement(currentElement
);
704 if (*currentElement
== L
'\0' || CdfsGetNextPathElement(currentElement
+ 1) == 0)
706 return(STATUS_OBJECT_NAME_NOT_FOUND
);
710 return(STATUS_OBJECT_PATH_NOT_FOUND
);
713 else if (!NT_SUCCESS(Status
))
715 CdfsReleaseFCB(Vcb
, parentFCB
);
722 currentElement
= CdfsGetNextPathElement(currentElement
);
725 *pParentFCB
= parentFCB
;
728 return STATUS_SUCCESS
;