reactos/drivers/filesystems/cdfs/fcb.c

723 lines
19 KiB
C

/*
* 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 <debug.h>
/* 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 */