3 * Copyright (C) 2002 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 *****************************************************************/
31 #include <ddk/ntddk.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.Attrib & FILE_ATTRIBUTE_DIRECTORY);
115 return(Fcb->Entry.FileFlags & 0x02);
120 CdfsFCBIsRoot(PFCB Fcb)
122 return(wcscmp(Fcb->PathName, L"\\") == 0);
127 CdfsGrabFCB(PDEVICE_EXTENSION Vcb,
132 DPRINT("grabbing FCB at %x: %S, refCount:%d\n",
137 KeAcquireSpinLock(&Vcb->FcbListLock, &oldIrql);
139 KeReleaseSpinLock(&Vcb->FcbListLock, oldIrql);
144 CdfsReleaseFCB(PDEVICE_EXTENSION Vcb,
149 DPRINT("releasing FCB at %x: %S, refCount:%d\n",
154 KeAcquireSpinLock(&Vcb->FcbListLock, &oldIrql);
156 if (Fcb->RefCount <= 0 && !CdfsFCBIsDirectory(Fcb))
158 RemoveEntryList(&Fcb->FcbListEntry);
161 KeReleaseSpinLock(&Vcb->FcbListLock, oldIrql);
166 CdfsAddFCBToTable(PDEVICE_EXTENSION Vcb,
171 KeAcquireSpinLock(&Vcb->FcbListLock, &oldIrql);
173 InsertTailList(&Vcb->FcbListHead, &Fcb->FcbListEntry);
174 KeReleaseSpinLock(&Vcb->FcbListLock, oldIrql);
179 CdfsGrabFCBFromTable(PDEVICE_EXTENSION Vcb,
184 PLIST_ENTRY current_entry;
186 KeAcquireSpinLock(&Vcb->FcbListLock, &oldIrql);
188 if (FileName == NULL || *FileName == 0)
190 DPRINT("Return FCB for stream file object\n");
191 Fcb = Vcb->StreamFileObject->FsContext;
193 KeReleaseSpinLock(&Vcb->FcbListLock, oldIrql);
197 current_entry = Vcb->FcbListHead.Flink;
198 while (current_entry != &Vcb->FcbListHead)
200 Fcb = CONTAINING_RECORD(current_entry, FCB, FcbListEntry);
202 DPRINT("Comparing '%S' and '%S'\n", FileName, Fcb->PathName);
203 if (_wcsicmp(FileName, Fcb->PathName) == 0)
206 KeReleaseSpinLock(&Vcb->FcbListLock, oldIrql);
210 //FIXME: need to compare against short name in FCB here
212 current_entry = current_entry->Flink;
214 KeReleaseSpinLock(&Vcb->FcbListLock, oldIrql);
221 CdfsFCBInitializeCache(PVCB Vcb,
224 PFILE_OBJECT FileObject;
228 FileObject = IoCreateStreamFileObject(NULL, Vcb->StorageDevice);
230 newCCB = ExAllocatePoolWithTag(NonPagedPool, sizeof(CCB), TAG_CCB);
233 return(STATUS_INSUFFICIENT_RESOURCES);
235 RtlZeroMemory(newCCB,
238 FileObject->Flags = FileObject->Flags | FO_FCB_IS_VALID |
239 FO_DIRECT_CACHE_PAGING_READ;
240 FileObject->SectionObjectPointer = &Fcb->SectionObjectPointers;
241 FileObject->FsContext = Fcb;
242 FileObject->FsContext2 = newCCB;
243 newCCB->PtrFileObject = FileObject;
244 Fcb->FileObject = FileObject;
247 Status = CcRosInitializeFileCache(FileObject,
249 if (!NT_SUCCESS(Status))
251 DbgPrint("CcRosInitializeFileCache failed\n");
255 ObDereferenceObject(FileObject);
256 Fcb->Flags |= FCB_CACHE_INITIALIZED;
263 CdfsMakeRootFCB(PDEVICE_EXTENSION Vcb)
267 Fcb = CdfsCreateFCB(L"\\");
269 Fcb->Entry.DataLengthL = Vcb->CdInfo.RootSize;
270 Fcb->Entry.ExtentLocationL = Vcb->CdInfo.RootStart;
271 Fcb->Entry.FileFlags = 0x02; // FILE_ATTRIBUTE_DIRECTORY;
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,
347 WCHAR pathName[MAX_PATH];
351 if (LongName [0] != 0 && wcslen (DirectoryFCB->PathName) +
352 sizeof(WCHAR) + wcslen (LongName) > MAX_PATH)
354 return(STATUS_OBJECT_NAME_INVALID);
357 wcscpy(pathName, DirectoryFCB->PathName);
358 if (!CdfsFCBIsRoot(DirectoryFCB))
360 wcscat(pathName, L"\\");
363 if (LongName[0] != 0)
365 wcscat(pathName, LongName);
369 WCHAR entryName[MAX_PATH];
371 CdfsGetDirEntryName(Vcb, Record, entryName);
372 wcscat(pathName, entryName);
375 rcFCB = CdfsCreateFCB(pathName);
376 memcpy(&rcFCB->Entry, Record, sizeof(DIR_RECORD));
378 /* Copy short name into FCB */
379 rcFCB->ShortNameLength = wcslen(ShortName) * sizeof(WCHAR);
380 wcscpy(rcFCB->ShortName, ShortName);
382 Size = rcFCB->Entry.DataLengthL;
384 rcFCB->RFCB.FileSize.QuadPart = Size;
385 rcFCB->RFCB.ValidDataLength.QuadPart = Size;
386 rcFCB->RFCB.AllocationSize.QuadPart = ROUND_UP(Size, BLOCKSIZE);
387 if (CdfsFCBIsDirectory(rcFCB))
389 CdfsFCBInitializeCache(Vcb, rcFCB);
392 CdfsAddFCBToTable(Vcb, rcFCB);
395 DPRINT("%S %d %I64d\n", LongName, Size, rcFCB->RFCB.AllocationSize.QuadPart);
397 return(STATUS_SUCCESS);
402 CdfsAttachFCBToFileObject(PDEVICE_EXTENSION Vcb,
404 PFILE_OBJECT FileObject)
409 newCCB = ExAllocatePoolWithTag(NonPagedPool, sizeof(CCB), TAG_CCB);
412 return(STATUS_INSUFFICIENT_RESOURCES);
414 memset(newCCB, 0, sizeof(CCB));
416 FileObject->Flags = FileObject->Flags | FO_FCB_IS_VALID |
417 FO_DIRECT_CACHE_PAGING_READ;
418 FileObject->SectionObjectPointer = &Fcb->SectionObjectPointers;
419 FileObject->FsContext = Fcb;
420 FileObject->FsContext2 = newCCB;
421 newCCB->PtrFileObject = FileObject;
424 if (CdfsFCBIsDirectory(Fcb))
426 Status = CcRosInitializeFileCache(FileObject,
428 if (!NT_SUCCESS(Status))
430 DbgPrint("CcRosInitializeFileCache failed\n");
433 Fcb->Flags |= FCB_CACHE_INITIALIZED;
436 DPRINT("file open: fcb:%x file size: %d\n", Fcb, Fcb->Entry.DataLengthL);
438 return(STATUS_SUCCESS);
443 CdfsDirFindFile(PDEVICE_EXTENSION DeviceExt,
458 LARGE_INTEGER StreamOffset;
461 WCHAR ShortNameBuffer[13];
462 UNICODE_STRING ShortName;
463 UNICODE_STRING LongName;
465 GENERATE_NAME_CONTEXT NameContext;
469 assert(DirectoryFcb);
472 DPRINT("CdfsDirFindFile(VCB:%08x, dirFCB:%08x, File:%S)\n",
476 DPRINT("Dir Path:%S\n", DirectoryFcb->PathName);
478 /* default to '.' if no filename specified */
479 if (wcslen(FileToFind) == 0)
483 FileToFind = TempName;
486 DirSize = DirectoryFcb->Entry.DataLengthL;
487 StreamOffset.QuadPart = (LONGLONG)DirectoryFcb->Entry.ExtentLocationL * (LONGLONG)BLOCKSIZE;
489 if(!CcMapData(DeviceExt->StreamFileObject, &StreamOffset,
490 BLOCKSIZE, TRUE, &Context, &Block))
492 DPRINT("CcMapData() failed\n");
493 return(STATUS_UNSUCCESSFUL);
498 Record = (PDIR_RECORD)Block;
501 if (Record->RecordLength == 0)
503 DPRINT("RecordLength == 0 Stopped!\n");
507 DPRINT("RecordLength %u ExtAttrRecordLength %u NameLength %u\n",
508 Record->RecordLength, Record->ExtAttrRecordLength, Record->FileIdLength);
510 CdfsGetDirEntryName(DeviceExt, Record, Name);
511 DPRINT("Name '%S'\n", Name);
513 RtlInitUnicodeString(&LongName, Name);
514 ShortName.Length = 0;
515 ShortName.MaximumLength = 26;
516 ShortName.Buffer = ShortNameBuffer;
517 memset(ShortNameBuffer, 0, 26);
519 if ((RtlIsNameLegalDOS8Dot3(&LongName, NULL, &HasSpaces) == FALSE) ||
522 /* Build short name */
523 RtlGenerate8dot3Name(&LongName,
530 /* copy short name */
531 RtlUpcaseUnicodeString(&ShortName,
536 DPRINT("ShortName '%wZ'\n", &ShortName);
538 if (wstrcmpjoki(Name, FileToFind) || wstrcmpjoki(ShortNameBuffer, FileToFind))
540 DPRINT("Match found, %S\n", Name);
541 Status = CdfsMakeFCBFromDirEntry(DeviceExt,
548 CcUnpinData(Context);
553 Offset += Record->RecordLength;
554 BlockOffset += Record->RecordLength;
555 Record = (PDIR_RECORD)(Block + BlockOffset);
556 if (BlockOffset >= BLOCKSIZE || Record->RecordLength == 0)
558 DPRINT("Map next sector\n");
559 CcUnpinData(Context);
560 StreamOffset.QuadPart += BLOCKSIZE;
561 Offset = ROUND_UP(Offset, BLOCKSIZE);
564 if (!CcMapData(DeviceExt->StreamFileObject,
569 DPRINT("CcMapData() failed\n");
570 return(STATUS_UNSUCCESSFUL);
572 Record = (PDIR_RECORD)(Block + BlockOffset);
575 if (Offset >= DirSize)
579 CcUnpinData(Context);
581 return(STATUS_OBJECT_NAME_NOT_FOUND);
586 CdfsGetFCBForFile(PDEVICE_EXTENSION Vcb,
589 const PWSTR pFileName)
592 WCHAR pathName [MAX_PATH];
593 WCHAR elementName [MAX_PATH];
594 PWCHAR currentElement;
598 DPRINT("CdfsGetFCBForFile(%x, %x, %x, '%S')\n",
604 /* Trivial case, open of the root directory on volume */
605 if (pFileName [0] == L'\0' || wcscmp(pFileName, L"\\") == 0)
607 DPRINT("returning root FCB\n");
609 FCB = CdfsOpenRootFCB(Vcb);
613 return((FCB != NULL) ? STATUS_SUCCESS : STATUS_OBJECT_PATH_NOT_FOUND);
617 currentElement = pFileName + 1;
618 wcscpy (pathName, L"\\");
619 FCB = CdfsOpenRootFCB (Vcb);
623 /* Parse filename and check each path element for existance and access */
624 while (CdfsGetNextPathElement(currentElement) != 0)
626 /* Skip blank directory levels */
627 if ((CdfsGetNextPathElement(currentElement) - currentElement) == 0)
633 DPRINT("Parsing, currentElement:%S\n", currentElement);
634 DPRINT(" parentFCB:%x FCB:%x\n", parentFCB, FCB);
636 /* Descend to next directory level */
639 CdfsReleaseFCB(Vcb, parentFCB);
643 /* fail if element in FCB is not a directory */
644 if (!CdfsFCBIsDirectory(FCB))
646 DPRINT("Element in requested path is not a directory\n");
648 CdfsReleaseFCB(Vcb, FCB);
653 return(STATUS_OBJECT_PATH_NOT_FOUND);
657 /* Extract next directory level into dirName */
658 CdfsWSubString(pathName,
660 CdfsGetNextPathElement(currentElement) - pFileName);
661 DPRINT(" pathName:%S\n", pathName);
663 FCB = CdfsGrabFCBFromTable(Vcb, pathName);
666 CdfsWSubString(elementName,
668 CdfsGetNextPathElement(currentElement) - currentElement);
669 DPRINT(" elementName:%S\n", elementName);
671 Status = CdfsDirFindFile(Vcb, parentFCB, elementName, &FCB);
672 if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
674 *pParentFCB = parentFCB;
676 currentElement = CdfsGetNextPathElement(currentElement);
677 if (*currentElement == L'\0' || CdfsGetNextPathElement(currentElement + 1) == 0)
679 return(STATUS_OBJECT_NAME_NOT_FOUND);
683 return(STATUS_OBJECT_PATH_NOT_FOUND);
686 else if (!NT_SUCCESS(Status))
688 CdfsReleaseFCB(Vcb, parentFCB);
695 currentElement = CdfsGetNextPathElement(currentElement);
698 *pParentFCB = parentFCB;
701 return(STATUS_SUCCESS);