/* * ReactOS kernel * Copyright (C) 2002, 2004 ReactOS Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ /* * COPYRIGHT: See COPYING in the top level directory * PROJECT: ReactOS kernel * FILE: drivers/filesystems/cdfs/fcb.c * PURPOSE: CDROM (ISO 9660) filesystem driver * PROGRAMMER: Art Yerkes * UPDATE HISTORY: */ /* INCLUDES *****************************************************************/ #include "cdfs.h" #define NDEBUG #include /* FUNCTIONS ****************************************************************/ #define ROUND_UP(N, S) ((((N) + (S) - 1) / (S)) * (S)) /* FUNCTIONS ****************************************************************/ static BOOLEAN CdfsGetNextPathElement(PCUNICODE_STRING CurrentElement, PUNICODE_STRING NextElement) { *NextElement = *CurrentElement; if (NextElement->Length == 0) return FALSE; while ((NextElement->Length) && (NextElement->Buffer[0] != L'\\')) { NextElement->Buffer++; NextElement->Length -= sizeof(WCHAR); NextElement->MaximumLength -= sizeof(WCHAR); } return TRUE; } PFCB CdfsCreateFCB(PCWSTR FileName) { PFCB Fcb; Fcb = ExAllocatePoolWithTag(NonPagedPool, sizeof(FCB), CDFS_NONPAGED_FCB_TAG); if(!Fcb) return NULL; RtlZeroMemory(Fcb, sizeof(FCB)); RtlInitEmptyUnicodeString(&Fcb->PathName, Fcb->PathNameBuffer, sizeof(Fcb->PathNameBuffer)); if (FileName) { RtlAppendUnicodeToString(&Fcb->PathName, FileName); if (wcsrchr(Fcb->PathName.Buffer, '\\') != 0) { Fcb->ObjectName = wcsrchr(Fcb->PathName.Buffer, '\\'); } else { Fcb->ObjectName = Fcb->PathName.Buffer; } } ExInitializeResourceLite(&Fcb->PagingIoResource); ExInitializeResourceLite(&Fcb->MainResource); ExInitializeResourceLite(&Fcb->NameListResource); Fcb->RFCB.PagingIoResource = &Fcb->PagingIoResource; Fcb->RFCB.Resource = &Fcb->MainResource; Fcb->RFCB.IsFastIoPossible = FastIoIsNotPossible; InitializeListHead(&Fcb->ShortNameList); FsRtlInitializeFileLock(&Fcb->FileLock, NULL, NULL); return(Fcb); } VOID CdfsDestroyFCB(PFCB Fcb) { PLIST_ENTRY Entry; FsRtlUninitializeFileLock(&Fcb->FileLock); ExDeleteResourceLite(&Fcb->PagingIoResource); ExDeleteResourceLite(&Fcb->MainResource); while (!IsListEmpty(&Fcb->ShortNameList)) { Entry = Fcb->ShortNameList.Flink; RemoveEntryList(Entry); ExFreePoolWithTag(Entry, CDFS_SHORT_NAME_TAG); } ExDeleteResourceLite(&Fcb->NameListResource); ExFreePoolWithTag(Fcb, CDFS_NONPAGED_FCB_TAG); } BOOLEAN CdfsFCBIsDirectory(PFCB Fcb) { return(Fcb->Entry.FileFlags & FILE_FLAG_DIRECTORY); } BOOLEAN CdfsFCBIsRoot(PFCB Fcb) { return (Fcb->PathName.Length == sizeof(WCHAR) && Fcb->PathName.Buffer[0] == L'\\'); } VOID CdfsGrabFCB(PDEVICE_EXTENSION Vcb, PFCB Fcb) { KIRQL oldIrql; DPRINT("grabbing FCB at %p: %wZ, refCount:%d\n", Fcb, &Fcb->PathName, Fcb->RefCount); KeAcquireSpinLock(&Vcb->FcbListLock, &oldIrql); Fcb->RefCount++; KeReleaseSpinLock(&Vcb->FcbListLock, oldIrql); } VOID CdfsReleaseFCB(PDEVICE_EXTENSION Vcb, PFCB Fcb) { KIRQL oldIrql; DPRINT("releasing FCB at %p: %wZ, refCount:%d\n", Fcb, &Fcb->PathName, Fcb->RefCount); KeAcquireSpinLock(&Vcb->FcbListLock, &oldIrql); Fcb->RefCount--; if (Fcb->RefCount <= 0 && !CdfsFCBIsDirectory(Fcb)) { RemoveEntryList(&Fcb->FcbListEntry); CdfsDestroyFCB(Fcb); } KeReleaseSpinLock(&Vcb->FcbListLock, oldIrql); } VOID CdfsAddFCBToTable(PDEVICE_EXTENSION Vcb, PFCB Fcb) { KIRQL oldIrql; KeAcquireSpinLock(&Vcb->FcbListLock, &oldIrql); Fcb->DevExt = Vcb; InsertTailList(&Vcb->FcbListHead, &Fcb->FcbListEntry); KeReleaseSpinLock(&Vcb->FcbListLock, oldIrql); } PFCB CdfsGrabFCBFromTable(PDEVICE_EXTENSION Vcb, PUNICODE_STRING FileName) { KIRQL oldIrql; PFCB Fcb; PLIST_ENTRY current_entry; KeAcquireSpinLock(&Vcb->FcbListLock, &oldIrql); if (FileName == NULL || FileName->Length == 0 || FileName->Buffer[0] == 0) { DPRINT("Return FCB for stream file object\n"); Fcb = Vcb->StreamFileObject->FsContext; Fcb->RefCount++; KeReleaseSpinLock(&Vcb->FcbListLock, oldIrql); return(Fcb); } current_entry = Vcb->FcbListHead.Flink; while (current_entry != &Vcb->FcbListHead) { Fcb = CONTAINING_RECORD(current_entry, FCB, FcbListEntry); // Disabled the DPRINT! Can't be called at DISPATCH_LEVEL! //DPRINT("Comparing '%wZ' and '%wZ'\n", FileName, &Fcb->PathName); if (RtlCompareUnicodeString(FileName, &Fcb->PathName, TRUE) == 0) { Fcb->RefCount++; KeReleaseSpinLock(&Vcb->FcbListLock, oldIrql); return(Fcb); } //FIXME: need to compare against short name in FCB here current_entry = current_entry->Flink; } KeReleaseSpinLock(&Vcb->FcbListLock, oldIrql); return(NULL); } NTSTATUS CdfsFCBInitializeCache(PVCB Vcb, PFCB Fcb) { PFILE_OBJECT FileObject; PCCB newCCB; FileObject = IoCreateStreamFileObject(NULL, Vcb->StorageDevice); newCCB = ExAllocatePoolWithTag(NonPagedPool, sizeof(CCB), CDFS_CCB_TAG); if (newCCB == NULL) { return STATUS_INSUFFICIENT_RESOURCES; } RtlZeroMemory(newCCB, sizeof(CCB)); FileObject->ReadAccess = TRUE; FileObject->WriteAccess = FALSE; FileObject->DeleteAccess = FALSE; FileObject->SectionObjectPointer = &Fcb->SectionObjectPointers; FileObject->FsContext = Fcb; FileObject->FsContext2 = newCCB; newCCB->PtrFileObject = FileObject; Fcb->FileObject = FileObject; Fcb->DevExt = Vcb; _SEH2_TRY { CcInitializeCacheMap(FileObject, (PCC_FILE_SIZES)(&Fcb->RFCB.AllocationSize), FALSE, &(CdfsGlobalData->CacheMgrCallbacks), Fcb); } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { FileObject->FsContext2 = NULL; ExFreePoolWithTag(newCCB, CDFS_CCB_TAG); ObDereferenceObject(FileObject); Fcb->FileObject = NULL; return _SEH2_GetExceptionCode(); } _SEH2_END; ObDereferenceObject(FileObject); Fcb->Flags |= FCB_CACHE_INITIALIZED; return STATUS_SUCCESS; } PFCB CdfsMakeRootFCB(PDEVICE_EXTENSION Vcb) { PFCB Fcb; Fcb = CdfsCreateFCB(L"\\"); Fcb->Entry.DataLengthL = Vcb->CdInfo.RootSize; Fcb->Entry.ExtentLocationL = Vcb->CdInfo.RootStart; Fcb->Entry.FileFlags = FILE_FLAG_DIRECTORY; Fcb->IndexNumber.QuadPart = 0LL; Fcb->RefCount = 1; Fcb->DirIndex = 0; Fcb->RFCB.FileSize.QuadPart = Vcb->CdInfo.RootSize; Fcb->RFCB.ValidDataLength.QuadPart = Vcb->CdInfo.RootSize; Fcb->RFCB.AllocationSize.QuadPart = Vcb->CdInfo.RootSize; CdfsFCBInitializeCache(Vcb, Fcb); CdfsAddFCBToTable(Vcb, Fcb); CdfsGrabFCB(Vcb, Fcb); return(Fcb); } PFCB CdfsOpenRootFCB(PDEVICE_EXTENSION Vcb) { UNICODE_STRING FileName; PFCB Fcb; RtlInitUnicodeString(&FileName, L"\\"); Fcb = CdfsGrabFCBFromTable(Vcb, &FileName); if (Fcb == NULL) { Fcb = CdfsMakeRootFCB(Vcb); } return(Fcb); } NTSTATUS CdfsMakeFCBFromDirEntry(PVCB Vcb, PFCB DirectoryFCB, PWSTR LongName, PWSTR ShortName, PDIR_RECORD Record, ULONG DirectorySector, ULONG DirectoryOffset, PFCB * fileFCB) { WCHAR pathName[MAX_PATH]; PFCB rcFCB; ULONG Size; /* Check if the full string would overflow the pathName buffer (the additional characters are for '\\' and '\0') */ if ((LongName[0] != 0) && ((DirectoryFCB->PathName.Length / sizeof(WCHAR)) + 1 + wcslen(LongName) + 1 > MAX_PATH)) { return(STATUS_OBJECT_NAME_INVALID); } wcscpy(pathName, DirectoryFCB->PathName.Buffer); if (!CdfsFCBIsRoot(DirectoryFCB)) { wcscat(pathName, L"\\"); } if (LongName[0] != 0) { wcscat(pathName, LongName); } else { WCHAR entryName[MAX_PATH]; CdfsGetDirEntryName(Vcb, Record, entryName); wcscat(pathName, entryName); } rcFCB = CdfsCreateFCB(pathName); memcpy(&rcFCB->Entry, Record, sizeof(DIR_RECORD)); /* Copy short name into FCB */ rcFCB->ShortNameU.Length = wcslen(ShortName) * sizeof(WCHAR); rcFCB->ShortNameU.MaximumLength = rcFCB->ShortNameU.Length; rcFCB->ShortNameU.Buffer = rcFCB->ShortNameBuffer; wcscpy(rcFCB->ShortNameBuffer, ShortName); Size = rcFCB->Entry.DataLengthL; rcFCB->RFCB.FileSize.QuadPart = Size; rcFCB->RFCB.ValidDataLength.QuadPart = Size; rcFCB->RFCB.AllocationSize.QuadPart = ROUND_UP(Size, BLOCKSIZE); if (CdfsFCBIsDirectory(rcFCB)) { CdfsFCBInitializeCache(Vcb, rcFCB); } rcFCB->IndexNumber.u.HighPart = DirectorySector; rcFCB->IndexNumber.u.LowPart = DirectoryOffset; rcFCB->RefCount++; CdfsAddFCBToTable(Vcb, rcFCB); *fileFCB = rcFCB; DPRINT("%S %u %I64d\n", LongName, Size, rcFCB->RFCB.AllocationSize.QuadPart); return(STATUS_SUCCESS); } NTSTATUS CdfsAttachFCBToFileObject(PDEVICE_EXTENSION Vcb, PFCB Fcb, PFILE_OBJECT FileObject) { PCCB newCCB; newCCB = ExAllocatePoolWithTag(NonPagedPool, sizeof(CCB), CDFS_CCB_TAG); if (newCCB == NULL) { return(STATUS_INSUFFICIENT_RESOURCES); } memset(newCCB, 0, sizeof(CCB)); FileObject->ReadAccess = TRUE; FileObject->WriteAccess = FALSE; FileObject->DeleteAccess = FALSE; FileObject->SectionObjectPointer = &Fcb->SectionObjectPointers; FileObject->FsContext = Fcb; FileObject->FsContext2 = newCCB; newCCB->PtrFileObject = FileObject; Fcb->DevExt = Vcb; if (CdfsFCBIsDirectory(Fcb)) { _SEH2_TRY { CcInitializeCacheMap(FileObject, (PCC_FILE_SIZES)(&Fcb->RFCB.AllocationSize), FALSE, &(CdfsGlobalData->CacheMgrCallbacks), Fcb); } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { FileObject->FsContext2 = NULL; ExFreePoolWithTag(newCCB, CDFS_CCB_TAG); return _SEH2_GetExceptionCode(); } _SEH2_END; Fcb->Flags |= FCB_CACHE_INITIALIZED; } DPRINT("file open: fcb:%p file size: %u\n", Fcb, Fcb->Entry.DataLengthL); return(STATUS_SUCCESS); } NTSTATUS CdfsDirFindFile(PDEVICE_EXTENSION DeviceExt, PFCB DirectoryFcb, PUNICODE_STRING FileToFind, PFCB *FoundFCB) { UNICODE_STRING TempName; WCHAR Name[256]; PVOID Block; ULONG DirSize; PDIR_RECORD Record; ULONG Offset; ULONG BlockOffset; NTSTATUS Status; LARGE_INTEGER StreamOffset, OffsetOfEntry; PVOID Context; WCHAR ShortNameBuffer[13]; UNICODE_STRING ShortName; UNICODE_STRING LongName; UNICODE_STRING FileToFindUpcase; ASSERT(DeviceExt); ASSERT(DirectoryFcb); ASSERT(FileToFind); DPRINT("CdfsDirFindFile(VCB:%p, dirFCB:%p, File:%wZ)\n", DeviceExt, DirectoryFcb, FileToFind); DPRINT("Dir Path:%wZ\n", &DirectoryFcb->PathName); /* default to '.' if no filename specified */ if (FileToFind->Length == 0) { RtlInitUnicodeString(&TempName, L"."); FileToFind = &TempName; } DirSize = DirectoryFcb->Entry.DataLengthL; StreamOffset.QuadPart = (LONGLONG)DirectoryFcb->Entry.ExtentLocationL * (LONGLONG)BLOCKSIZE; _SEH2_TRY { CcMapData(DeviceExt->StreamFileObject, &StreamOffset, BLOCKSIZE, MAP_WAIT, &Context, &Block); } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { DPRINT("CcMapData() failed\n"); _SEH2_YIELD(return _SEH2_GetExceptionCode()); } _SEH2_END; Offset = 0; BlockOffset = 0; Record = (PDIR_RECORD)Block; /* Upper case the expression for FsRtlIsNameInExpression */ Status = RtlUpcaseUnicodeString(&FileToFindUpcase, FileToFind, TRUE); if (!NT_SUCCESS(Status)) { return Status; } while(TRUE) { if (Record->RecordLength == 0) { DPRINT("RecordLength == 0 Stopped!\n"); break; } DPRINT("RecordLength %u ExtAttrRecordLength %u NameLength %u\n", Record->RecordLength, Record->ExtAttrRecordLength, Record->FileIdLength); if (!CdfsIsRecordValid(DeviceExt, Record)) { RtlFreeUnicodeString(&FileToFindUpcase); CcUnpinData(Context); return STATUS_DISK_CORRUPT_ERROR; } CdfsGetDirEntryName(DeviceExt, Record, Name); DPRINT ("Name '%S'\n", Name); DPRINT ("Sector %lu\n", DirectoryFcb->Entry.ExtentLocationL); DPRINT ("Offset %lu\n", Offset); RtlInitUnicodeString(&LongName, Name); RtlInitEmptyUnicodeString(&ShortName, ShortNameBuffer, sizeof(ShortNameBuffer)); RtlZeroMemory(ShortNameBuffer, sizeof(ShortNameBuffer)); OffsetOfEntry.QuadPart = StreamOffset.QuadPart + Offset; CdfsShortNameCacheGet(DirectoryFcb, &OffsetOfEntry, &LongName, &ShortName); DPRINT("ShortName '%wZ'\n", &ShortName); ASSERT(LongName.Length >= sizeof(WCHAR)); ASSERT(ShortName.Length >= sizeof(WCHAR)); if (FsRtlIsNameInExpression(&FileToFindUpcase, &LongName, TRUE, NULL) || FsRtlIsNameInExpression(&FileToFindUpcase, &ShortName, TRUE, NULL)) { DPRINT("Match found, %S\n", Name); Status = CdfsMakeFCBFromDirEntry(DeviceExt, DirectoryFcb, Name, ShortNameBuffer, Record, DirectoryFcb->Entry.ExtentLocationL, Offset, FoundFCB); RtlFreeUnicodeString(&FileToFindUpcase); CcUnpinData(Context); return(Status); } Offset += Record->RecordLength; BlockOffset += Record->RecordLength; Record = (PDIR_RECORD)((ULONG_PTR)Block + BlockOffset); if (BlockOffset >= BLOCKSIZE || Record->RecordLength == 0) { DPRINT("Map next sector\n"); CcUnpinData(Context); StreamOffset.QuadPart += BLOCKSIZE; Offset = ROUND_UP(Offset, BLOCKSIZE); BlockOffset = 0; _SEH2_TRY { CcMapData(DeviceExt->StreamFileObject, &StreamOffset, BLOCKSIZE, MAP_WAIT, &Context, &Block); } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { DPRINT("CcMapData() failed\n"); RtlFreeUnicodeString(&FileToFindUpcase); _SEH2_YIELD(return _SEH2_GetExceptionCode()); } _SEH2_END; Record = (PDIR_RECORD)((ULONG_PTR)Block + BlockOffset); } if (Offset >= DirSize) break; } RtlFreeUnicodeString(&FileToFindUpcase); CcUnpinData(Context); return(STATUS_OBJECT_NAME_NOT_FOUND); } NTSTATUS CdfsGetFCBForFile(PDEVICE_EXTENSION Vcb, PFCB *pParentFCB, PFCB *pFCB, PUNICODE_STRING FileName) { UNICODE_STRING PathName; UNICODE_STRING NextElement; UNICODE_STRING CurrentElement; NTSTATUS Status; PFCB FCB; PFCB parentFCB; DPRINT("CdfsGetFCBForFile(%p, %p, %p, '%wZ')\n", Vcb, pParentFCB, pFCB, FileName); /* Trivial case, open of the root directory on volume */ if (FileName->Length == 0 || ((FileName->Buffer[0] == '\\') && (FileName->Length == sizeof(WCHAR)))) { DPRINT("returning root FCB\n"); FCB = CdfsOpenRootFCB(Vcb); *pFCB = FCB; *pParentFCB = NULL; return((FCB != NULL) ? STATUS_SUCCESS : STATUS_OBJECT_PATH_NOT_FOUND); } else { /* Start with empty path */ PathName = *FileName; PathName.Length = 0; CurrentElement = *FileName; FCB = CdfsOpenRootFCB (Vcb); } parentFCB = NULL; /* Parse filename and check each path element for existence and access */ while (CdfsGetNextPathElement(&CurrentElement, &NextElement)) { /* Skip blank directory levels */ if (CurrentElement.Buffer[0] == L'\\') { CurrentElement.Buffer++; CurrentElement.Length -= sizeof(WCHAR); CurrentElement.MaximumLength -= sizeof(WCHAR); continue; } DPRINT("Parsing, currentElement:%wZ\n", &CurrentElement); DPRINT(" parentFCB:%p FCB:%p\n", parentFCB, FCB); /* Descend to next directory level */ if (parentFCB) { CdfsReleaseFCB(Vcb, parentFCB); parentFCB = NULL; } /* fail if element in FCB is not a directory */ if (!CdfsFCBIsDirectory(FCB)) { DPRINT("Element in requested path is not a directory\n"); CdfsReleaseFCB(Vcb, FCB); FCB = 0; *pParentFCB = NULL; *pFCB = NULL; return(STATUS_OBJECT_PATH_NOT_FOUND); } parentFCB = FCB; /* Extract next directory level */ PathName.Length = (NextElement.Buffer - FileName->Buffer) * sizeof(WCHAR); DPRINT(" PathName:%wZ\n", &PathName); FCB = CdfsGrabFCBFromTable(Vcb, &PathName); if (FCB == NULL) { UNICODE_STRING ChildElement = CurrentElement; ChildElement.Length = (NextElement.Buffer - CurrentElement.Buffer) * sizeof(WCHAR); Status = CdfsDirFindFile(Vcb, parentFCB, &ChildElement, &FCB); if (Status == STATUS_OBJECT_NAME_NOT_FOUND) { *pParentFCB = parentFCB; *pFCB = NULL; if (NextElement.Length == 0) { return(STATUS_OBJECT_NAME_NOT_FOUND); } else { return(STATUS_OBJECT_PATH_NOT_FOUND); } } else if (!NT_SUCCESS(Status)) { CdfsReleaseFCB(Vcb, parentFCB); *pParentFCB = NULL; *pFCB = NULL; return(Status); } } CurrentElement = NextElement; } *pParentFCB = parentFCB; *pFCB = FCB; return STATUS_SUCCESS; } /* EOF */