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.
19 /* $Id: fcb.c,v 1.18 2004/03/08 08:51:26 ekohl Exp $
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 *****************************************************************/
31 #include <ddk/ntddk.h>
32 #include <ntos/kefuncs.h>
40 /* MACROS *******************************************************************/
42 #define TAG(A, B, C, D) (ULONG)(((A)<<0) + ((B)<<8) + ((C)<<16) + ((D)<<24))
43 #define TAG_FCB TAG('I', 'F', 'C', 'B')
45 #define ROUND_UP(N, S) ((((N) + (S) - 1) / (S)) * (S))
48 /* FUNCTIONS ****************************************************************/
51 CdfsGetNextPathElement(PWCHAR FileName
)
53 if (*FileName
== L
'\0')
58 while (*FileName
!= L
'\0' && *FileName
!= L
'\\')
68 CdfsWSubString(PWCHAR pTarget
, const PWCHAR pSource
, size_t pLength
)
70 wcsncpy (pTarget
, pSource
, pLength
);
71 pTarget
[pLength
] = L
'\0';
76 CdfsCreateFCB(PWSTR FileName
)
80 Fcb
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(FCB
), TAG_FCB
);
81 RtlZeroMemory(Fcb
, sizeof(FCB
));
85 wcscpy(Fcb
->PathName
, FileName
);
86 if (wcsrchr(Fcb
->PathName
, '\\') != 0)
88 Fcb
->ObjectName
= wcsrchr(Fcb
->PathName
, '\\');
92 Fcb
->ObjectName
= Fcb
->PathName
;
96 ExInitializeResourceLite(&Fcb
->MainResource
);
103 CdfsDestroyFCB(PFCB Fcb
)
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
,
183 PLIST_ENTRY current_entry
;
185 KeAcquireSpinLock(&Vcb
->FcbListLock
, &oldIrql
);
187 if (FileName
== NULL
|| *FileName
== 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 '%S' and '%S'\n", FileName
, Fcb
->PathName
);
202 if (_wcsicmp(FileName
, 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
->Flags
= FileObject
->Flags
| FO_FCB_IS_VALID
|
238 FO_DIRECT_CACHE_PAGING_READ
;
239 FileObject
->SectionObjectPointer
= &Fcb
->SectionObjectPointers
;
240 FileObject
->FsContext
= Fcb
;
241 FileObject
->FsContext2
= newCCB
;
242 newCCB
->PtrFileObject
= FileObject
;
243 Fcb
->FileObject
= FileObject
;
246 Status
= CcRosInitializeFileCache(FileObject
,
248 if (!NT_SUCCESS(Status
))
250 DbgPrint("CcRosInitializeFileCache failed\n");
254 ObDereferenceObject(FileObject
);
255 Fcb
->Flags
|= FCB_CACHE_INITIALIZED
;
262 CdfsMakeRootFCB(PDEVICE_EXTENSION Vcb
)
266 Fcb
= CdfsCreateFCB(L
"\\");
268 Fcb
->Entry
.DataLengthL
= Vcb
->CdInfo
.RootSize
;
269 Fcb
->Entry
.ExtentLocationL
= Vcb
->CdInfo
.RootStart
;
270 Fcb
->Entry
.FileFlags
= FILE_FLAG_DIRECTORY
;
271 Fcb
->IndexNumber
.QuadPart
= 0LL;
274 Fcb
->RFCB
.FileSize
.QuadPart
= Vcb
->CdInfo
.RootSize
;
275 Fcb
->RFCB
.ValidDataLength
.QuadPart
= Vcb
->CdInfo
.RootSize
;
276 Fcb
->RFCB
.AllocationSize
.QuadPart
= Vcb
->CdInfo
.RootSize
;
278 CdfsFCBInitializeCache(Vcb
, Fcb
);
279 CdfsAddFCBToTable(Vcb
, Fcb
);
280 CdfsGrabFCB(Vcb
, Fcb
);
287 CdfsOpenRootFCB(PDEVICE_EXTENSION Vcb
)
291 Fcb
= CdfsGrabFCBFromTable(Vcb
, L
"\\");
294 Fcb
= CdfsMakeRootFCB(Vcb
);
302 CdfsGetDirEntryName(PDEVICE_EXTENSION DeviceExt
,
306 * FUNCTION: Retrieves the file name from a directory record.
309 if (Record
->FileIdLength
== 1 && Record
->FileId
[0] == 0)
313 else if (Record
->FileIdLength
== 1 && Record
->FileId
[0] == 1)
319 if (DeviceExt
->CdInfo
.JolietLevel
== 0)
323 for (i
= 0; i
< Record
->FileIdLength
&& Record
->FileId
[i
] != ';'; i
++)
324 Name
[i
] = (WCHAR
)Record
->FileId
[i
];
331 Record
->FileIdLength
);
335 DPRINT("Name '%S'\n", Name
);
340 CdfsMakeFCBFromDirEntry(PVCB Vcb
,
345 ULONG DirectorySector
,
346 ULONG DirectoryOffset
,
349 WCHAR pathName
[MAX_PATH
];
353 if (LongName
[0] != 0 && wcslen (DirectoryFCB
->PathName
) +
354 sizeof(WCHAR
) + wcslen (LongName
) > MAX_PATH
)
356 return(STATUS_OBJECT_NAME_INVALID
);
359 wcscpy(pathName
, DirectoryFCB
->PathName
);
360 if (!CdfsFCBIsRoot(DirectoryFCB
))
362 wcscat(pathName
, L
"\\");
365 if (LongName
[0] != 0)
367 wcscat(pathName
, LongName
);
371 WCHAR entryName
[MAX_PATH
];
373 CdfsGetDirEntryName(Vcb
, Record
, entryName
);
374 wcscat(pathName
, entryName
);
377 rcFCB
= CdfsCreateFCB(pathName
);
378 memcpy(&rcFCB
->Entry
, Record
, sizeof(DIR_RECORD
));
380 /* Copy short name into FCB */
381 rcFCB
->ShortNameLength
= wcslen(ShortName
) * sizeof(WCHAR
);
382 wcscpy(rcFCB
->ShortName
, ShortName
);
384 Size
= rcFCB
->Entry
.DataLengthL
;
386 rcFCB
->RFCB
.FileSize
.QuadPart
= Size
;
387 rcFCB
->RFCB
.ValidDataLength
.QuadPart
= Size
;
388 rcFCB
->RFCB
.AllocationSize
.QuadPart
= ROUND_UP(Size
, BLOCKSIZE
);
389 if (CdfsFCBIsDirectory(rcFCB
))
391 CdfsFCBInitializeCache(Vcb
, rcFCB
);
393 rcFCB
->IndexNumber
.u
.HighPart
= DirectorySector
;
394 rcFCB
->IndexNumber
.u
.LowPart
= DirectoryOffset
;
396 CdfsAddFCBToTable(Vcb
, rcFCB
);
399 DPRINT("%S %d %I64d\n", LongName
, Size
, rcFCB
->RFCB
.AllocationSize
.QuadPart
);
401 return(STATUS_SUCCESS
);
406 CdfsAttachFCBToFileObject(PDEVICE_EXTENSION Vcb
,
408 PFILE_OBJECT FileObject
)
413 newCCB
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(CCB
), TAG_CCB
);
416 return(STATUS_INSUFFICIENT_RESOURCES
);
418 memset(newCCB
, 0, sizeof(CCB
));
420 FileObject
->Flags
= FileObject
->Flags
| FO_FCB_IS_VALID
|
421 FO_DIRECT_CACHE_PAGING_READ
;
422 FileObject
->SectionObjectPointer
= &Fcb
->SectionObjectPointers
;
423 FileObject
->FsContext
= Fcb
;
424 FileObject
->FsContext2
= newCCB
;
425 newCCB
->PtrFileObject
= FileObject
;
428 if (CdfsFCBIsDirectory(Fcb
))
430 Status
= CcRosInitializeFileCache(FileObject
,
432 if (!NT_SUCCESS(Status
))
434 DbgPrint("CcRosInitializeFileCache failed\n");
437 Fcb
->Flags
|= FCB_CACHE_INITIALIZED
;
440 DPRINT("file open: fcb:%x file size: %d\n", Fcb
, Fcb
->Entry
.DataLengthL
);
442 return(STATUS_SUCCESS
);
447 CdfsDirFindFile(PDEVICE_EXTENSION DeviceExt
,
461 LARGE_INTEGER StreamOffset
;
464 WCHAR ShortNameBuffer
[13];
465 UNICODE_STRING ShortName
;
466 UNICODE_STRING LongName
;
468 GENERATE_NAME_CONTEXT NameContext
;
472 assert(DirectoryFcb
);
475 DPRINT("CdfsDirFindFile(VCB:%08x, dirFCB:%08x, File:%S)\n",
479 DPRINT("Dir Path:%S\n", DirectoryFcb
->PathName
);
481 /* default to '.' if no filename specified */
482 if (wcslen(FileToFind
) == 0)
486 FileToFind
= TempName
;
489 DirSize
= DirectoryFcb
->Entry
.DataLengthL
;
490 StreamOffset
.QuadPart
= (LONGLONG
)DirectoryFcb
->Entry
.ExtentLocationL
* (LONGLONG
)BLOCKSIZE
;
492 if(!CcMapData(DeviceExt
->StreamFileObject
, &StreamOffset
,
493 BLOCKSIZE
, TRUE
, &Context
, &Block
))
495 DPRINT("CcMapData() failed\n");
496 return(STATUS_UNSUCCESSFUL
);
501 Record
= (PDIR_RECORD
)Block
;
504 if (Record
->RecordLength
== 0)
506 DPRINT("RecordLength == 0 Stopped!\n");
510 DPRINT("RecordLength %u ExtAttrRecordLength %u NameLength %u\n",
511 Record
->RecordLength
, Record
->ExtAttrRecordLength
, Record
->FileIdLength
);
513 CdfsGetDirEntryName(DeviceExt
, Record
, Name
);
514 DPRINT ("Name '%S'\n", Name
);
515 DPRINT ("Sector %lu\n", DirectoryFcb
->Entry
.ExtentLocationL
);
516 DPRINT ("Offset %lu\n", Offset
);
518 RtlInitUnicodeString(&LongName
, Name
);
519 ShortName
.Length
= 0;
520 ShortName
.MaximumLength
= 26;
521 ShortName
.Buffer
= ShortNameBuffer
;
522 memset(ShortNameBuffer
, 0, 26);
524 if ((RtlIsNameLegalDOS8Dot3(&LongName
, NULL
, &HasSpaces
) == FALSE
) ||
527 /* Build short name */
528 RtlGenerate8dot3Name(&LongName
,
535 /* copy short name */
536 RtlUpcaseUnicodeString(&ShortName
,
541 DPRINT("ShortName '%wZ'\n", &ShortName
);
543 if (wstrcmpjoki(Name
, FileToFind
) || wstrcmpjoki(ShortNameBuffer
, FileToFind
))
545 DPRINT("Match found, %S\n", Name
);
546 Status
= CdfsMakeFCBFromDirEntry(DeviceExt
,
551 DirectoryFcb
->Entry
.ExtentLocationL
,
555 CcUnpinData(Context
);
560 Offset
+= Record
->RecordLength
;
561 BlockOffset
+= Record
->RecordLength
;
562 Record
= (PDIR_RECORD
)(Block
+ BlockOffset
);
563 if (BlockOffset
>= BLOCKSIZE
|| Record
->RecordLength
== 0)
565 DPRINT("Map next sector\n");
566 CcUnpinData(Context
);
567 StreamOffset
.QuadPart
+= BLOCKSIZE
;
568 Offset
= ROUND_UP(Offset
, BLOCKSIZE
);
571 if (!CcMapData(DeviceExt
->StreamFileObject
,
576 DPRINT("CcMapData() failed\n");
577 return(STATUS_UNSUCCESSFUL
);
579 Record
= (PDIR_RECORD
)(Block
+ BlockOffset
);
582 if (Offset
>= DirSize
)
586 CcUnpinData(Context
);
588 return(STATUS_OBJECT_NAME_NOT_FOUND
);
593 CdfsGetFCBForFile(PDEVICE_EXTENSION Vcb
,
596 const PWSTR pFileName
)
599 WCHAR pathName
[MAX_PATH
];
600 WCHAR elementName
[MAX_PATH
];
601 PWCHAR currentElement
;
605 DPRINT("CdfsGetFCBForFile(%x, %x, %x, '%S')\n",
611 /* Trivial case, open of the root directory on volume */
612 if (pFileName
[0] == L
'\0' || wcscmp(pFileName
, L
"\\") == 0)
614 DPRINT("returning root FCB\n");
616 FCB
= CdfsOpenRootFCB(Vcb
);
620 return((FCB
!= NULL
) ? STATUS_SUCCESS
: STATUS_OBJECT_PATH_NOT_FOUND
);
624 currentElement
= pFileName
+ 1;
625 wcscpy (pathName
, L
"\\");
626 FCB
= CdfsOpenRootFCB (Vcb
);
630 /* Parse filename and check each path element for existance and access */
631 while (CdfsGetNextPathElement(currentElement
) != 0)
633 /* Skip blank directory levels */
634 if ((CdfsGetNextPathElement(currentElement
) - currentElement
) == 0)
640 DPRINT("Parsing, currentElement:%S\n", currentElement
);
641 DPRINT(" parentFCB:%x FCB:%x\n", parentFCB
, FCB
);
643 /* Descend to next directory level */
646 CdfsReleaseFCB(Vcb
, parentFCB
);
650 /* fail if element in FCB is not a directory */
651 if (!CdfsFCBIsDirectory(FCB
))
653 DPRINT("Element in requested path is not a directory\n");
655 CdfsReleaseFCB(Vcb
, FCB
);
660 return(STATUS_OBJECT_PATH_NOT_FOUND
);
664 /* Extract next directory level into dirName */
665 CdfsWSubString(pathName
,
667 CdfsGetNextPathElement(currentElement
) - pFileName
);
668 DPRINT(" pathName:%S\n", pathName
);
670 FCB
= CdfsGrabFCBFromTable(Vcb
, pathName
);
673 CdfsWSubString(elementName
,
675 CdfsGetNextPathElement(currentElement
) - currentElement
);
676 DPRINT(" elementName:%S\n", elementName
);
678 Status
= CdfsDirFindFile(Vcb
, parentFCB
, elementName
, &FCB
);
679 if (Status
== STATUS_OBJECT_NAME_NOT_FOUND
)
681 *pParentFCB
= parentFCB
;
683 currentElement
= CdfsGetNextPathElement(currentElement
);
684 if (*currentElement
== L
'\0' || CdfsGetNextPathElement(currentElement
+ 1) == 0)
686 return(STATUS_OBJECT_NAME_NOT_FOUND
);
690 return(STATUS_OBJECT_PATH_NOT_FOUND
);
693 else if (!NT_SUCCESS(Status
))
695 CdfsReleaseFCB(Vcb
, parentFCB
);
702 currentElement
= CdfsGetNextPathElement(currentElement
);
705 *pParentFCB
= parentFCB
;
708 return(STATUS_SUCCESS
);