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
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, 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 TAG_FCB TAG('I', 'F', 'C', 'B')
40 #define ROUND_UP(N, S) ((((N) + (S) - 1) / (S)) * (S))
43 /* FUNCTIONS ****************************************************************/
46 CdfsGetNextPathElement(PWCHAR FileName
)
48 if (*FileName
== L
'\0')
53 while (*FileName
!= L
'\0' && *FileName
!= L
'\\')
63 CdfsWSubString(PWCHAR pTarget
, const PWCHAR pSource
, size_t pLength
)
65 wcsncpy (pTarget
, pSource
, pLength
);
66 pTarget
[pLength
] = L
'\0';
71 CdfsCreateFCB(PCWSTR FileName
)
75 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 Fcb
->RFCB
.PagingIoResource
= &Fcb
->PagingIoResource
;
94 Fcb
->RFCB
.Resource
= &Fcb
->MainResource
;
95 Fcb
->RFCB
.IsFastIoPossible
= FastIoIsNotPossible
;
102 CdfsDestroyFCB(PFCB Fcb
)
104 ExDeleteResourceLite(&Fcb
->PagingIoResource
);
105 ExDeleteResourceLite(&Fcb
->MainResource
);
112 CdfsFCBIsDirectory(PFCB Fcb
)
114 return(Fcb
->Entry
.FileFlags
& FILE_FLAG_DIRECTORY
);
119 CdfsFCBIsRoot(PFCB Fcb
)
121 return(wcscmp(Fcb
->PathName
, L
"\\") == 0);
126 CdfsGrabFCB(PDEVICE_EXTENSION Vcb
,
131 DPRINT("grabbing FCB at %x: %S, refCount:%d\n",
136 KeAcquireSpinLock(&Vcb
->FcbListLock
, &oldIrql
);
138 KeReleaseSpinLock(&Vcb
->FcbListLock
, oldIrql
);
143 CdfsReleaseFCB(PDEVICE_EXTENSION Vcb
,
148 DPRINT("releasing FCB at %x: %S, refCount:%d\n",
153 KeAcquireSpinLock(&Vcb
->FcbListLock
, &oldIrql
);
155 if (Fcb
->RefCount
<= 0 && !CdfsFCBIsDirectory(Fcb
))
157 RemoveEntryList(&Fcb
->FcbListEntry
);
160 KeReleaseSpinLock(&Vcb
->FcbListLock
, oldIrql
);
165 CdfsAddFCBToTable(PDEVICE_EXTENSION Vcb
,
170 KeAcquireSpinLock(&Vcb
->FcbListLock
, &oldIrql
);
172 InsertTailList(&Vcb
->FcbListHead
, &Fcb
->FcbListEntry
);
173 KeReleaseSpinLock(&Vcb
->FcbListLock
, oldIrql
);
178 CdfsGrabFCBFromTable(PDEVICE_EXTENSION Vcb
,
179 PUNICODE_STRING FileName
)
183 PLIST_ENTRY current_entry
;
185 KeAcquireSpinLock(&Vcb
->FcbListLock
, &oldIrql
);
187 if (FileName
== NULL
|| FileName
->Length
== 0 || FileName
->Buffer
[0] == 0)
189 DPRINT("Return FCB for stream file object\n");
190 Fcb
= Vcb
->StreamFileObject
->FsContext
;
192 KeReleaseSpinLock(&Vcb
->FcbListLock
, oldIrql
);
196 current_entry
= Vcb
->FcbListHead
.Flink
;
197 while (current_entry
!= &Vcb
->FcbListHead
)
199 Fcb
= CONTAINING_RECORD(current_entry
, FCB
, FcbListEntry
);
201 DPRINT("Comparing '%wZ' and '%S'\n", FileName
, Fcb
->PathName
);
202 if (_wcsicmp(FileName
->Buffer
, Fcb
->PathName
) == 0)
205 KeReleaseSpinLock(&Vcb
->FcbListLock
, oldIrql
);
209 //FIXME: need to compare against short name in FCB here
211 current_entry
= current_entry
->Flink
;
213 KeReleaseSpinLock(&Vcb
->FcbListLock
, oldIrql
);
220 CdfsFCBInitializeCache(PVCB Vcb
,
223 PFILE_OBJECT FileObject
;
227 FileObject
= IoCreateStreamFileObject(NULL
, Vcb
->StorageDevice
);
229 newCCB
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(CCB
), TAG_CCB
);
232 return(STATUS_INSUFFICIENT_RESOURCES
);
234 RtlZeroMemory(newCCB
,
237 FileObject
->SectionObjectPointer
= &Fcb
->SectionObjectPointers
;
238 FileObject
->FsContext
= Fcb
;
239 FileObject
->FsContext2
= newCCB
;
240 newCCB
->PtrFileObject
= FileObject
;
241 Fcb
->FileObject
= FileObject
;
244 Status
= STATUS_SUCCESS
;
245 CcInitializeCacheMap(FileObject
,
246 (PCC_FILE_SIZES
)(&Fcb
->RFCB
.AllocationSize
),
248 &(CdfsGlobalData
->CacheMgrCallbacks
),
251 ObDereferenceObject(FileObject
);
252 Fcb
->Flags
|= FCB_CACHE_INITIALIZED
;
259 CdfsMakeRootFCB(PDEVICE_EXTENSION Vcb
)
263 Fcb
= CdfsCreateFCB(L
"\\");
265 Fcb
->Entry
.DataLengthL
= Vcb
->CdInfo
.RootSize
;
266 Fcb
->Entry
.ExtentLocationL
= Vcb
->CdInfo
.RootStart
;
267 Fcb
->Entry
.FileFlags
= FILE_FLAG_DIRECTORY
;
268 Fcb
->IndexNumber
.QuadPart
= 0LL;
271 Fcb
->RFCB
.FileSize
.QuadPart
= Vcb
->CdInfo
.RootSize
;
272 Fcb
->RFCB
.ValidDataLength
.QuadPart
= Vcb
->CdInfo
.RootSize
;
273 Fcb
->RFCB
.AllocationSize
.QuadPart
= Vcb
->CdInfo
.RootSize
;
275 CdfsFCBInitializeCache(Vcb
, Fcb
);
276 CdfsAddFCBToTable(Vcb
, Fcb
);
277 CdfsGrabFCB(Vcb
, Fcb
);
284 CdfsOpenRootFCB(PDEVICE_EXTENSION Vcb
)
286 UNICODE_STRING FileName
;
289 RtlInitUnicodeString(&FileName
, L
"\\");
291 Fcb
= CdfsGrabFCBFromTable(Vcb
,
295 Fcb
= CdfsMakeRootFCB(Vcb
);
303 CdfsGetDirEntryName(PDEVICE_EXTENSION DeviceExt
,
307 * FUNCTION: Retrieves the file name from a directory record.
310 if (Record
->FileIdLength
== 1 && Record
->FileId
[0] == 0)
314 else if (Record
->FileIdLength
== 1 && Record
->FileId
[0] == 1)
320 if (DeviceExt
->CdInfo
.JolietLevel
== 0)
324 for (i
= 0; i
< Record
->FileIdLength
&& Record
->FileId
[i
] != ';'; i
++)
325 Name
[i
] = (WCHAR
)Record
->FileId
[i
];
332 Record
->FileIdLength
);
336 DPRINT("Name '%S'\n", Name
);
341 CdfsMakeFCBFromDirEntry(PVCB Vcb
,
346 ULONG DirectorySector
,
347 ULONG DirectoryOffset
,
350 WCHAR pathName
[MAX_PATH
];
354 if (LongName
[0] != 0 && wcslen (DirectoryFCB
->PathName
) +
355 sizeof(WCHAR
) + wcslen (LongName
) > MAX_PATH
)
357 return(STATUS_OBJECT_NAME_INVALID
);
360 wcscpy(pathName
, DirectoryFCB
->PathName
);
361 if (!CdfsFCBIsRoot(DirectoryFCB
))
363 wcscat(pathName
, L
"\\");
366 if (LongName
[0] != 0)
368 wcscat(pathName
, LongName
);
372 WCHAR entryName
[MAX_PATH
];
374 CdfsGetDirEntryName(Vcb
, Record
, entryName
);
375 wcscat(pathName
, entryName
);
378 rcFCB
= CdfsCreateFCB(pathName
);
379 memcpy(&rcFCB
->Entry
, Record
, sizeof(DIR_RECORD
));
381 /* Copy short name into FCB */
382 rcFCB
->ShortNameU
.Length
= wcslen(ShortName
) * sizeof(WCHAR
);
383 rcFCB
->ShortNameU
.MaximumLength
= rcFCB
->ShortNameU
.Length
;
384 rcFCB
->ShortNameU
.Buffer
= rcFCB
->ShortNameBuffer
;
385 wcscpy(rcFCB
->ShortNameBuffer
, ShortName
);
387 Size
= rcFCB
->Entry
.DataLengthL
;
389 rcFCB
->RFCB
.FileSize
.QuadPart
= Size
;
390 rcFCB
->RFCB
.ValidDataLength
.QuadPart
= Size
;
391 rcFCB
->RFCB
.AllocationSize
.QuadPart
= ROUND_UP(Size
, BLOCKSIZE
);
392 if (CdfsFCBIsDirectory(rcFCB
))
394 CdfsFCBInitializeCache(Vcb
, rcFCB
);
396 rcFCB
->IndexNumber
.u
.HighPart
= DirectorySector
;
397 rcFCB
->IndexNumber
.u
.LowPart
= DirectoryOffset
;
399 CdfsAddFCBToTable(Vcb
, rcFCB
);
402 DPRINT("%S %d %I64d\n", LongName
, Size
, rcFCB
->RFCB
.AllocationSize
.QuadPart
);
404 return(STATUS_SUCCESS
);
409 CdfsAttachFCBToFileObject(PDEVICE_EXTENSION Vcb
,
411 PFILE_OBJECT FileObject
)
415 newCCB
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(CCB
), TAG_CCB
);
418 return(STATUS_INSUFFICIENT_RESOURCES
);
420 memset(newCCB
, 0, sizeof(CCB
));
422 FileObject
->SectionObjectPointer
= &Fcb
->SectionObjectPointers
;
423 FileObject
->FsContext
= Fcb
;
424 FileObject
->FsContext2
= newCCB
;
425 newCCB
->PtrFileObject
= FileObject
;
428 if (CdfsFCBIsDirectory(Fcb
))
430 CcInitializeCacheMap(FileObject
,
431 (PCC_FILE_SIZES
)(&Fcb
->RFCB
.AllocationSize
),
433 &(CdfsGlobalData
->CacheMgrCallbacks
),
435 Fcb
->Flags
|= FCB_CACHE_INITIALIZED
;
438 DPRINT("file open: fcb:%x file size: %d\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
;
462 WCHAR ShortNameBuffer
[13];
463 UNICODE_STRING ShortName
;
464 UNICODE_STRING LongName
;
466 GENERATE_NAME_CONTEXT NameContext
;
470 ASSERT(DirectoryFcb
);
473 DPRINT("CdfsDirFindFile(VCB:%p, dirFCB:%p, File:%wZ)\n",
477 DPRINT("Dir Path:%S\n", DirectoryFcb
->PathName
);
479 /* default to '.' if no filename specified */
480 if (FileToFind
->Length
== 0)
482 RtlInitUnicodeString(&TempName
, L
".");
483 FileToFind
= &TempName
;
486 DirSize
= DirectoryFcb
->Entry
.DataLengthL
;
487 StreamOffset
.QuadPart
= (LONGLONG
)DirectoryFcb
->Entry
.ExtentLocationL
* (LONGLONG
)BLOCKSIZE
;
489 if (!CcMapData(DeviceExt
->StreamFileObject
,
496 DPRINT("CcMapData() failed\n");
497 return STATUS_UNSUCCESSFUL
;
502 Record
= (PDIR_RECORD
)Block
;
505 if (Record
->RecordLength
== 0)
507 DPRINT("RecordLength == 0 Stopped!\n");
511 DPRINT("RecordLength %u ExtAttrRecordLength %u NameLength %u\n",
512 Record
->RecordLength
, Record
->ExtAttrRecordLength
, Record
->FileIdLength
);
514 CdfsGetDirEntryName(DeviceExt
, Record
, Name
);
515 DPRINT ("Name '%S'\n", Name
);
516 DPRINT ("Sector %lu\n", DirectoryFcb
->Entry
.ExtentLocationL
);
517 DPRINT ("Offset %lu\n", Offset
);
519 RtlInitUnicodeString(&LongName
, Name
);
520 ShortName
.Length
= 0;
521 ShortName
.MaximumLength
= 26;
522 ShortName
.Buffer
= ShortNameBuffer
;
523 memset(ShortNameBuffer
, 0, 26);
525 if ((RtlIsNameLegalDOS8Dot3(&LongName
, NULL
, &HasSpaces
) == FALSE
) ||
528 /* Build short name */
529 RtlGenerate8dot3Name(&LongName
,
536 /* copy short name */
537 RtlUpcaseUnicodeString(&ShortName
,
542 DPRINT("ShortName '%wZ'\n", &ShortName
);
544 if (FsRtlIsNameInExpression(FileToFind
, &LongName
, TRUE
, NULL
) ||
545 FsRtlIsNameInExpression(FileToFind
, &ShortName
, TRUE
, NULL
))
547 DPRINT("Match found, %S\n", Name
);
548 Status
= CdfsMakeFCBFromDirEntry(DeviceExt
,
553 DirectoryFcb
->Entry
.ExtentLocationL
,
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
);
573 if (!CcMapData(DeviceExt
->StreamFileObject
,
578 DPRINT("CcMapData() failed\n");
579 return(STATUS_UNSUCCESSFUL
);
581 Record
= (PDIR_RECORD
)((ULONG_PTR
)Block
+ BlockOffset
);
584 if (Offset
>= DirSize
)
588 CcUnpinData(Context
);
590 return(STATUS_OBJECT_NAME_NOT_FOUND
);
595 CdfsGetFCBForFile(PDEVICE_EXTENSION Vcb
,
598 PUNICODE_STRING FileName
)
600 UNICODE_STRING PathName
;
601 UNICODE_STRING ElementName
;
603 WCHAR pathName
[MAX_PATH
];
604 WCHAR elementName
[MAX_PATH
];
605 PWCHAR currentElement
;
609 DPRINT("CdfsGetFCBForFile(%x, %x, %x, '%wZ')\n",
615 /* Trivial case, open of the root directory on volume */
616 if (FileName
->Buffer
[0] == L
'\0' || wcscmp(FileName
->Buffer
, L
"\\") == 0)
618 DPRINT("returning root FCB\n");
620 FCB
= CdfsOpenRootFCB(Vcb
);
624 return((FCB
!= NULL
) ? STATUS_SUCCESS
: STATUS_OBJECT_PATH_NOT_FOUND
);
628 currentElement
= &FileName
->Buffer
[1];
629 wcscpy (pathName
, L
"\\");
630 FCB
= CdfsOpenRootFCB (Vcb
);
634 /* Parse filename and check each path element for existance and access */
635 while (CdfsGetNextPathElement(currentElement
) != 0)
637 /* Skip blank directory levels */
638 if ((CdfsGetNextPathElement(currentElement
) - currentElement
) == 0)
644 DPRINT("Parsing, currentElement:%S\n", currentElement
);
645 DPRINT(" parentFCB:%x FCB:%x\n", parentFCB
, FCB
);
647 /* Descend to next directory level */
650 CdfsReleaseFCB(Vcb
, parentFCB
);
654 /* fail if element in FCB is not a directory */
655 if (!CdfsFCBIsDirectory(FCB
))
657 DPRINT("Element in requested path is not a directory\n");
659 CdfsReleaseFCB(Vcb
, FCB
);
664 return(STATUS_OBJECT_PATH_NOT_FOUND
);
668 /* Extract next directory level into dirName */
669 CdfsWSubString(pathName
,
671 CdfsGetNextPathElement(currentElement
) - FileName
->Buffer
);
672 DPRINT(" pathName:%S\n", pathName
);
674 RtlInitUnicodeString(&PathName
, pathName
);
676 FCB
= CdfsGrabFCBFromTable(Vcb
, &PathName
);
679 CdfsWSubString(elementName
,
681 CdfsGetNextPathElement(currentElement
) - currentElement
);
682 DPRINT(" elementName:%S\n", elementName
);
684 RtlInitUnicodeString(&ElementName
, elementName
);
685 Status
= CdfsDirFindFile(Vcb
,
689 if (Status
== STATUS_OBJECT_NAME_NOT_FOUND
)
691 *pParentFCB
= parentFCB
;
693 currentElement
= CdfsGetNextPathElement(currentElement
);
694 if (*currentElement
== L
'\0' || CdfsGetNextPathElement(currentElement
+ 1) == 0)
696 return(STATUS_OBJECT_NAME_NOT_FOUND
);
700 return(STATUS_OBJECT_PATH_NOT_FOUND
);
703 else if (!NT_SUCCESS(Status
))
705 CdfsReleaseFCB(Vcb
, parentFCB
);
712 currentElement
= CdfsGetNextPathElement(currentElement
);
715 *pParentFCB
= parentFCB
;
718 return STATUS_SUCCESS
;