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>
39 /* MACROS *******************************************************************/
41 #define TAG(A, B, C, D) (ULONG)(((A)<<0) + ((B)<<8) + ((C)<<16) + ((D)<<24))
42 #define TAG_FCB TAG('I', 'F', 'C', 'B')
44 #define ROUND_UP(N, S) ((((N) + (S) - 1) / (S)) * (S))
47 /* FUNCTIONS ****************************************************************/
50 CdfsGetNextPathElement(PWCHAR FileName)
52 if (*FileName == L'\0')
57 while (*FileName != L'\0' && *FileName != L'\\')
67 CdfsWSubString(PWCHAR pTarget, const PWCHAR pSource, size_t pLength)
69 wcsncpy (pTarget, pSource, pLength);
70 pTarget [pLength] = L'\0';
75 CdfsCreateFCB(PWSTR FileName)
79 Fcb = ExAllocatePoolWithTag(NonPagedPool, sizeof(FCB), TAG_FCB);
80 RtlZeroMemory(Fcb, sizeof(FCB));
84 wcscpy(Fcb->PathName, FileName);
85 if (wcsrchr(Fcb->PathName, '\\') != 0)
87 Fcb->ObjectName = wcsrchr(Fcb->PathName, '\\');
91 Fcb->ObjectName = Fcb->PathName;
95 ExInitializeResourceLite(&Fcb->MainResource);
102 CdfsDestroyFCB(PFCB Fcb)
104 ExDeleteResourceLite(&Fcb->MainResource);
111 CdfsFCBIsDirectory(PFCB Fcb)
113 // return(Fcb->entry.Attrib & FILE_ATTRIBUTE_DIRECTORY);
114 return(Fcb->Entry.FileFlags & 0x02);
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 = ((PCCB)Vcb->StreamFileObject->FsContext2)->Fcb;
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->SectionObjectPointers = &Fcb->SectionObjectPointers;
240 FileObject->FsContext = (PVOID) &Fcb->RFCB;
241 FileObject->FsContext2 = newCCB;
243 newCCB->PtrFileObject = FileObject;
244 Fcb->FileObject = FileObject;
247 Status = CcRosInitializeFileCache(FileObject,
250 if (!NT_SUCCESS(Status))
252 DbgPrint("CcRosInitializeFileCache failed\n");
256 ObDereferenceObject(FileObject);
257 Fcb->Flags |= FCB_CACHE_INITIALIZED;
264 CdfsMakeRootFCB(PDEVICE_EXTENSION Vcb)
268 Fcb = CdfsCreateFCB(L"\\");
270 Fcb->Entry.DataLengthL = Vcb->CdInfo.RootSize;
271 Fcb->Entry.ExtentLocationL = Vcb->CdInfo.RootStart;
272 Fcb->Entry.FileFlags = 0x02; // FILE_ATTRIBUTE_DIRECTORY;
275 Fcb->RFCB.FileSize.QuadPart = Vcb->CdInfo.RootSize;
276 Fcb->RFCB.ValidDataLength.QuadPart = Vcb->CdInfo.RootSize;
277 Fcb->RFCB.AllocationSize.QuadPart = Vcb->CdInfo.RootSize;
279 CdfsFCBInitializeCache(Vcb, Fcb);
280 CdfsAddFCBToTable(Vcb, Fcb);
281 CdfsGrabFCB(Vcb, Fcb);
288 CdfsOpenRootFCB(PDEVICE_EXTENSION Vcb)
292 Fcb = CdfsGrabFCBFromTable(Vcb, L"\\");
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,
348 WCHAR pathName[MAX_PATH];
352 if (LongName [0] != 0 && wcslen (DirectoryFCB->PathName) +
353 sizeof(WCHAR) + wcslen (LongName) > MAX_PATH)
355 return(STATUS_OBJECT_NAME_INVALID);
358 wcscpy(pathName, DirectoryFCB->PathName);
359 if (!CdfsFCBIsRoot(DirectoryFCB))
361 wcscat(pathName, L"\\");
364 if (LongName[0] != 0)
366 wcscat(pathName, LongName);
370 WCHAR entryName[MAX_PATH];
372 CdfsGetDirEntryName(Vcb, Record, entryName);
373 wcscat(pathName, entryName);
376 rcFCB = CdfsCreateFCB(pathName);
377 memcpy(&rcFCB->Entry, Record, sizeof(DIR_RECORD));
379 /* Copy short name into FCB */
380 rcFCB->ShortNameLength = wcslen(ShortName) * sizeof(WCHAR);
381 wcscpy(rcFCB->ShortName, ShortName);
383 Size = rcFCB->Entry.DataLengthL;
385 rcFCB->RFCB.FileSize.QuadPart = Size;
386 rcFCB->RFCB.ValidDataLength.QuadPart = Size;
387 rcFCB->RFCB.AllocationSize.QuadPart = ROUND_UP(Size, BLOCKSIZE);
388 if (CdfsFCBIsDirectory(rcFCB))
390 CdfsFCBInitializeCache(Vcb, rcFCB);
393 CdfsAddFCBToTable(Vcb, rcFCB);
396 DPRINT("%S %d %I64d\n", LongName, Size, rcFCB->RFCB.AllocationSize.QuadPart);
398 return(STATUS_SUCCESS);
403 CdfsAttachFCBToFileObject(PDEVICE_EXTENSION Vcb,
405 PFILE_OBJECT FileObject)
410 newCCB = ExAllocatePoolWithTag(NonPagedPool, sizeof(CCB), TAG_CCB);
413 return(STATUS_INSUFFICIENT_RESOURCES);
415 memset(newCCB, 0, sizeof(CCB));
417 FileObject->Flags = FileObject->Flags | FO_FCB_IS_VALID |
418 FO_DIRECT_CACHE_PAGING_READ;
419 FileObject->SectionObjectPointers = &Fcb->SectionObjectPointers;
420 FileObject->FsContext = (PVOID)&Fcb->RFCB;
421 FileObject->FsContext2 = newCCB;
423 newCCB->PtrFileObject = FileObject;
426 if (CdfsFCBIsDirectory(Fcb))
428 Status = CcRosInitializeFileCache(FileObject,
431 if (!NT_SUCCESS(Status))
433 DbgPrint("CcRosInitializeFileCache failed\n");
436 Fcb->Flags |= FCB_CACHE_INITIALIZED;
439 DPRINT("file open: fcb:%x file size: %d\n", Fcb, Fcb->Entry.DataLengthL);
441 return(STATUS_SUCCESS);
446 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);
516 RtlInitUnicodeString(&LongName, Name);
517 ShortName.Length = 0;
518 ShortName.MaximumLength = 26;
519 ShortName.Buffer = ShortNameBuffer;
520 memset(ShortNameBuffer, 0, 26);
522 if ((RtlIsNameLegalDOS8Dot3(&LongName, NULL, &HasSpaces) == FALSE) ||
525 /* Build short name */
526 RtlGenerate8dot3Name(&LongName,
533 /* copy short name */
534 RtlUpcaseUnicodeString(&ShortName,
539 DPRINT("ShortName '%wZ'\n", &ShortName);
541 if (wstrcmpjoki(Name, FileToFind) || wstrcmpjoki(ShortNameBuffer, FileToFind))
543 DPRINT("Match found, %S\n", Name);
544 Status = CdfsMakeFCBFromDirEntry(DeviceExt,
551 CcUnpinData(Context);
556 Offset += Record->RecordLength;
557 BlockOffset += Record->RecordLength;
558 Record = (PDIR_RECORD)(Block + BlockOffset);
559 if (BlockOffset >= BLOCKSIZE || Record->RecordLength == 0)
561 DPRINT("Map next sector\n");
562 CcUnpinData(Context);
563 StreamOffset.QuadPart += BLOCKSIZE;
564 Offset = ROUND_UP(Offset, BLOCKSIZE);
567 if (!CcMapData(DeviceExt->StreamFileObject,
572 DPRINT("CcMapData() failed\n");
573 return(STATUS_UNSUCCESSFUL);
575 Record = (PDIR_RECORD)(Block + BlockOffset);
578 if (Offset >= DirSize)
582 CcUnpinData(Context);
584 return(STATUS_OBJECT_NAME_NOT_FOUND);
589 CdfsGetFCBForFile(PDEVICE_EXTENSION Vcb,
592 const PWSTR pFileName)
595 WCHAR pathName [MAX_PATH];
596 WCHAR elementName [MAX_PATH];
597 PWCHAR currentElement;
601 DPRINT("CdfsGetFCBForFile(%x, %x, %x, '%S')\n",
607 /* Trivial case, open of the root directory on volume */
608 if (pFileName [0] == L'\0' || wcscmp(pFileName, L"\\") == 0)
610 DPRINT("returning root FCB\n");
612 FCB = CdfsOpenRootFCB(Vcb);
616 return((FCB != NULL) ? STATUS_SUCCESS : STATUS_OBJECT_PATH_NOT_FOUND);
620 currentElement = pFileName + 1;
621 wcscpy (pathName, L"\\");
622 FCB = CdfsOpenRootFCB (Vcb);
626 /* Parse filename and check each path element for existance and access */
627 while (CdfsGetNextPathElement(currentElement) != 0)
629 /* Skip blank directory levels */
630 if ((CdfsGetNextPathElement(currentElement) - currentElement) == 0)
636 DPRINT("Parsing, currentElement:%S\n", currentElement);
637 DPRINT(" parentFCB:%x FCB:%x\n", parentFCB, FCB);
639 /* Descend to next directory level */
642 CdfsReleaseFCB(Vcb, parentFCB);
646 /* fail if element in FCB is not a directory */
647 if (!CdfsFCBIsDirectory(FCB))
649 DPRINT("Element in requested path is not a directory\n");
651 CdfsReleaseFCB(Vcb, FCB);
656 return(STATUS_OBJECT_PATH_NOT_FOUND);
660 /* Extract next directory level into dirName */
661 CdfsWSubString(pathName,
663 CdfsGetNextPathElement(currentElement) - pFileName);
664 DPRINT(" pathName:%S\n", pathName);
666 FCB = CdfsGrabFCBFromTable(Vcb, pathName);
669 CdfsWSubString(elementName,
671 CdfsGetNextPathElement(currentElement) - currentElement);
672 DPRINT(" elementName:%S\n", elementName);
674 Status = CdfsDirFindFile(Vcb, parentFCB, elementName, &FCB);
675 if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
677 *pParentFCB = parentFCB;
679 currentElement = CdfsGetNextPathElement(currentElement);
680 if (*currentElement == L'\0' || CdfsGetNextPathElement(currentElement + 1) == 0)
682 return(STATUS_OBJECT_NAME_NOT_FOUND);
686 return(STATUS_OBJECT_PATH_NOT_FOUND);
689 else if (!NT_SUCCESS(Status))
691 CdfsReleaseFCB(Vcb, parentFCB);
698 currentElement = CdfsGetNextPathElement(currentElement);
701 *pParentFCB = parentFCB;
704 return(STATUS_SUCCESS);