/* * ReactOS kernel * Copyright (C) 2002, 2014 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 St, Fifth Floor, Boston, MA 02110-1301, USA. * * COPYRIGHT: See COPYING in the top level directory * PROJECT: ReactOS kernel * FILE: drivers/filesystem/ntfs/fcb.c * PURPOSE: NTFS filesystem driver * PROGRAMMERS: Eric Kohl * Pierre Schweitzer (pierre@reactos.org) * Hervé Poussineau (hpoussin@reactos.org) */ /* INCLUDES *****************************************************************/ #include "ntfs.h" #define NDEBUG #include /* FUNCTIONS ****************************************************************/ static PCWSTR NtfsGetNextPathElement(PCWSTR FileName) { if (*FileName == L'\0') { return NULL; } while (*FileName != L'\0' && *FileName != L'\\') { FileName++; } return FileName; } static VOID NtfsWSubString(PWCHAR pTarget, PCWSTR pSource, size_t pLength) { wcsncpy(pTarget, pSource, pLength); pTarget[pLength] = L'\0'; } PNTFS_FCB NtfsCreateFCB(PCWSTR FileName, PCWSTR Stream, PNTFS_VCB Vcb) { PNTFS_FCB Fcb; ASSERT(Vcb); ASSERT(Vcb->Identifier.Type == NTFS_TYPE_VCB); Fcb = ExAllocateFromNPagedLookasideList(&NtfsGlobalData->FcbLookasideList); RtlZeroMemory(Fcb, sizeof(NTFS_FCB)); Fcb->Identifier.Type = NTFS_TYPE_FCB; Fcb->Identifier.Size = sizeof(NTFS_TYPE_FCB); Fcb->Vcb = Vcb; if (FileName) { wcscpy(Fcb->PathName, FileName); if (wcsrchr(Fcb->PathName, '\\') != 0) { Fcb->ObjectName = wcsrchr(Fcb->PathName, '\\'); } else { Fcb->ObjectName = Fcb->PathName; } } if (Stream) { wcscpy(Fcb->Stream, Stream); } else { Fcb->Stream[0] = UNICODE_NULL; } ExInitializeResourceLite(&Fcb->MainResource); Fcb->RFCB.Resource = &(Fcb->MainResource); return Fcb; } VOID NtfsDestroyFCB(PNTFS_FCB Fcb) { ASSERT(Fcb); ASSERT(Fcb->Identifier.Type == NTFS_TYPE_FCB); ExDeleteResourceLite(&Fcb->MainResource); ExFreeToNPagedLookasideList(&NtfsGlobalData->FcbLookasideList, Fcb); } BOOLEAN NtfsFCBIsDirectory(PNTFS_FCB Fcb) { return ((Fcb->Entry.FileAttributes & NTFS_FILE_TYPE_DIRECTORY) == NTFS_FILE_TYPE_DIRECTORY); } BOOLEAN NtfsFCBIsReparsePoint(PNTFS_FCB Fcb) { return ((Fcb->Entry.FileAttributes & NTFS_FILE_TYPE_REPARSE) == NTFS_FILE_TYPE_REPARSE); } BOOLEAN NtfsFCBIsCompressed(PNTFS_FCB Fcb) { return ((Fcb->Entry.FileAttributes & NTFS_FILE_TYPE_COMPRESSED) == NTFS_FILE_TYPE_COMPRESSED); } BOOLEAN NtfsFCBIsEncrypted(PNTFS_FCB Fcb) { return ((Fcb->Entry.FileAttributes & NTFS_FILE_TYPE_ENCRYPTED) == NTFS_FILE_TYPE_ENCRYPTED); } BOOLEAN NtfsFCBIsRoot(PNTFS_FCB Fcb) { return (wcscmp(Fcb->PathName, L"\\") == 0); } VOID NtfsGrabFCB(PNTFS_VCB Vcb, PNTFS_FCB Fcb) { KIRQL oldIrql; DPRINT("grabbing FCB at %p: %S, refCount:%d\n", Fcb, Fcb->PathName, Fcb->RefCount); KeAcquireSpinLock(&Vcb->FcbListLock, &oldIrql); Fcb->RefCount++; KeReleaseSpinLock(&Vcb->FcbListLock, oldIrql); } VOID NtfsReleaseFCB(PNTFS_VCB Vcb, PNTFS_FCB Fcb) { KIRQL oldIrql; DPRINT("releasing FCB at %p: %S, refCount:%d\n", Fcb, Fcb->PathName, Fcb->RefCount); KeAcquireSpinLock(&Vcb->FcbListLock, &oldIrql); Fcb->RefCount--; if (Fcb->RefCount <= 0 && !NtfsFCBIsDirectory(Fcb)) { RemoveEntryList(&Fcb->FcbListEntry); KeReleaseSpinLock(&Vcb->FcbListLock, oldIrql); CcUninitializeCacheMap(Fcb->FileObject, NULL, NULL); NtfsDestroyFCB(Fcb); } else { KeReleaseSpinLock(&Vcb->FcbListLock, oldIrql); } } VOID NtfsAddFCBToTable(PNTFS_VCB Vcb, PNTFS_FCB Fcb) { KIRQL oldIrql; KeAcquireSpinLock(&Vcb->FcbListLock, &oldIrql); Fcb->Vcb = Vcb; InsertTailList(&Vcb->FcbListHead, &Fcb->FcbListEntry); KeReleaseSpinLock(&Vcb->FcbListLock, oldIrql); } PNTFS_FCB NtfsGrabFCBFromTable(PNTFS_VCB Vcb, PCWSTR FileName) { KIRQL oldIrql; PNTFS_FCB Fcb; PLIST_ENTRY current_entry; KeAcquireSpinLock(&Vcb->FcbListLock, &oldIrql); if (FileName == NULL || *FileName == 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, NTFS_FCB, FcbListEntry); DPRINT("Comparing '%S' and '%S'\n", FileName, Fcb->PathName); if (_wcsicmp(FileName, Fcb->PathName) == 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 NtfsFCBInitializeCache(PNTFS_VCB Vcb, PNTFS_FCB Fcb) { PFILE_OBJECT FileObject; NTSTATUS Status; PNTFS_CCB newCCB; FileObject = IoCreateStreamFileObject(NULL, Vcb->StorageDevice); newCCB = ExAllocatePoolWithTag(NonPagedPool, sizeof(NTFS_CCB), TAG_CCB); if (newCCB == NULL) { return STATUS_INSUFFICIENT_RESOURCES; } RtlZeroMemory(newCCB, sizeof(NTFS_CCB)); newCCB->Identifier.Type = NTFS_TYPE_CCB; newCCB->Identifier.Size = sizeof(NTFS_TYPE_CCB); FileObject->SectionObjectPointer = &Fcb->SectionObjectPointers; FileObject->FsContext = Fcb; FileObject->FsContext2 = newCCB; newCCB->PtrFileObject = FileObject; Fcb->FileObject = FileObject; Fcb->Vcb = Vcb; Status = STATUS_SUCCESS; _SEH2_TRY { CcInitializeCacheMap(FileObject, (PCC_FILE_SIZES)(&Fcb->RFCB.AllocationSize), FALSE, &(NtfsGlobalData->CacheMgrCallbacks), Fcb); } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { FileObject->FsContext2 = NULL; ExFreePoolWithTag(newCCB, TAG_CCB); ObDereferenceObject(FileObject); Fcb->FileObject = NULL; return _SEH2_GetExceptionCode(); } _SEH2_END; ObDereferenceObject(FileObject); Fcb->Flags |= FCB_CACHE_INITIALIZED; return Status; } PNTFS_FCB NtfsMakeRootFCB(PNTFS_VCB Vcb) { PNTFS_FCB Fcb; PFILE_RECORD_HEADER MftRecord; PFILENAME_ATTRIBUTE FileName; MftRecord = ExAllocateFromNPagedLookasideList(&Vcb->FileRecLookasideList); if (MftRecord == NULL) { return NULL; } if (!NT_SUCCESS(ReadFileRecord(Vcb, NTFS_FILE_ROOT, MftRecord))) { ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, MftRecord); return NULL; } FileName = GetFileNameFromRecord(Vcb, MftRecord, NTFS_FILE_NAME_WIN32); if (!FileName) { ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, MftRecord); return NULL; } Fcb = NtfsCreateFCB(L"\\", NULL, Vcb); if (!Fcb) { ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, MftRecord); return NULL; } memcpy(&Fcb->Entry, FileName, FIELD_OFFSET(FILENAME_ATTRIBUTE, NameLength)); Fcb->Entry.NameType = FileName->NameType; Fcb->Entry.NameLength = 0; Fcb->Entry.Name[0] = UNICODE_NULL; Fcb->RefCount = 1; Fcb->DirIndex = 0; Fcb->RFCB.FileSize.QuadPart = FileName->DataSize; Fcb->RFCB.ValidDataLength.QuadPart = FileName->DataSize; Fcb->RFCB.AllocationSize.QuadPart = FileName->AllocatedSize; Fcb->MFTIndex = NTFS_FILE_ROOT; Fcb->LinkCount = MftRecord->LinkCount; NtfsFCBInitializeCache(Vcb, Fcb); NtfsAddFCBToTable(Vcb, Fcb); NtfsGrabFCB(Vcb, Fcb); ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, MftRecord); return Fcb; } PNTFS_FCB NtfsOpenRootFCB(PNTFS_VCB Vcb) { PNTFS_FCB Fcb; Fcb = NtfsGrabFCBFromTable(Vcb, L"\\"); if (Fcb == NULL) { Fcb = NtfsMakeRootFCB(Vcb); } return Fcb; } NTSTATUS NtfsMakeFCBFromDirEntry(PNTFS_VCB Vcb, PNTFS_FCB DirectoryFCB, PUNICODE_STRING Name, PCWSTR Stream, PFILE_RECORD_HEADER Record, ULONGLONG MFTIndex, PNTFS_FCB * fileFCB) { WCHAR pathName[MAX_PATH]; PFILENAME_ATTRIBUTE FileName; PSTANDARD_INFORMATION StdInfo; PNTFS_FCB rcFCB; ULONGLONG Size, AllocatedSize; DPRINT("NtfsMakeFCBFromDirEntry(%p, %p, %wZ, %p, %p, %p)\n", Vcb, DirectoryFCB, Name, Stream, Record, fileFCB); FileName = GetBestFileNameFromRecord(Vcb, Record); if (!FileName) { return STATUS_OBJECT_NAME_NOT_FOUND; // Not sure that's the best here } if (DirectoryFCB && Name) { if (Name->Buffer[0] != 0 && wcslen(DirectoryFCB->PathName) + sizeof(WCHAR) + Name->Length / sizeof(WCHAR) > MAX_PATH) { return STATUS_OBJECT_NAME_INVALID; } wcscpy(pathName, DirectoryFCB->PathName); if (!NtfsFCBIsRoot(DirectoryFCB)) { wcscat(pathName, L"\\"); } wcscat(pathName, Name->Buffer); } else { RtlCopyMemory(pathName, FileName->Name, FileName->NameLength * sizeof (WCHAR)); pathName[FileName->NameLength] = UNICODE_NULL; } Size = NtfsGetFileSize(Vcb, Record, (Stream ? Stream : L""), (Stream ? wcslen(Stream) : 0), &AllocatedSize); rcFCB = NtfsCreateFCB(pathName, Stream, Vcb); if (!rcFCB) { return STATUS_INSUFFICIENT_RESOURCES; } memcpy(&rcFCB->Entry, FileName, FIELD_OFFSET(FILENAME_ATTRIBUTE, NameLength)); rcFCB->Entry.NameType = FileName->NameType; rcFCB->RFCB.FileSize.QuadPart = Size; rcFCB->RFCB.ValidDataLength.QuadPart = Size; rcFCB->RFCB.AllocationSize.QuadPart = AllocatedSize; StdInfo = GetStandardInformationFromRecord(Vcb, Record); if (StdInfo != NULL) { rcFCB->Entry.FileAttributes |= StdInfo->FileAttribute; } NtfsFCBInitializeCache(Vcb, rcFCB); rcFCB->RefCount = 1; rcFCB->MFTIndex = MFTIndex; rcFCB->LinkCount = Record->LinkCount; NtfsAddFCBToTable(Vcb, rcFCB); *fileFCB = rcFCB; return STATUS_SUCCESS; } NTSTATUS NtfsAttachFCBToFileObject(PNTFS_VCB Vcb, PNTFS_FCB Fcb, PFILE_OBJECT FileObject) { PNTFS_CCB newCCB; newCCB = ExAllocatePoolWithTag(NonPagedPool, sizeof(NTFS_CCB), TAG_CCB); if (newCCB == NULL) { return STATUS_INSUFFICIENT_RESOURCES; } RtlZeroMemory(newCCB, sizeof(NTFS_CCB)); newCCB->Identifier.Type = NTFS_TYPE_CCB; newCCB->Identifier.Size = sizeof(NTFS_TYPE_CCB); FileObject->SectionObjectPointer = &Fcb->SectionObjectPointers; FileObject->FsContext = Fcb; FileObject->FsContext2 = newCCB; newCCB->PtrFileObject = FileObject; Fcb->Vcb = Vcb; if (!(Fcb->Flags & FCB_CACHE_INITIALIZED)) { _SEH2_TRY { CcInitializeCacheMap(FileObject, (PCC_FILE_SIZES)(&Fcb->RFCB.AllocationSize), FALSE, &(NtfsGlobalData->CacheMgrCallbacks), Fcb); } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { FileObject->FsContext2 = NULL; ExFreePoolWithTag(newCCB, TAG_CCB); return _SEH2_GetExceptionCode(); } _SEH2_END; Fcb->Flags |= FCB_CACHE_INITIALIZED; } //DPRINT("file open: fcb:%x file size: %d\n", Fcb, Fcb->Entry.DataLengthL); return STATUS_SUCCESS; } static NTSTATUS NtfsDirFindFile(PNTFS_VCB Vcb, PNTFS_FCB DirectoryFcb, PWSTR FileToFind, BOOLEAN CaseSensitive, PNTFS_FCB *FoundFCB) { NTSTATUS Status; ULONGLONG CurrentDir; UNICODE_STRING File; PFILE_RECORD_HEADER FileRecord; ULONGLONG MFTIndex; PWSTR Colon, OldColon; PNTFS_ATTR_CONTEXT DataContext; USHORT Length = 0; DPRINT("NtfsDirFindFile(%p, %p, %S, %s, %p)\n", Vcb, DirectoryFcb, FileToFind, CaseSensitive ? "TRUE" : "FALSE", FoundFCB); *FoundFCB = NULL; RtlInitUnicodeString(&File, FileToFind); CurrentDir = DirectoryFcb->MFTIndex; Colon = wcsrchr(FileToFind, L':'); if (Colon != NULL) { Length = File.Length; File.Length = (Colon - FileToFind) * sizeof(WCHAR); if (_wcsicmp(Colon + 1, L"$DATA") == 0) { OldColon = Colon; Colon[0] = UNICODE_NULL; Colon = wcsrchr(FileToFind, L':'); if (Colon != NULL) { Length = File.Length; File.Length = (Colon - FileToFind) * sizeof(WCHAR); } else { Colon = OldColon; Colon[0] = L':'; } } /* Skip colon */ ++Colon; DPRINT1("Will now look for file '%wZ' with stream '%S'\n", &File, Colon); } Status = NtfsLookupFileAt(Vcb, &File, CaseSensitive, &FileRecord, &MFTIndex, CurrentDir); if (!NT_SUCCESS(Status)) { return Status; } if (Length != 0) { File.Length = Length; } if ((FileRecord->Flags & FRH_DIRECTORY) && Colon != 0) { ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, FileRecord); return STATUS_INVALID_PARAMETER; } else if (Colon != 0) { Status = FindAttribute(Vcb, FileRecord, AttributeData, Colon, wcslen(Colon), &DataContext, NULL); if (!NT_SUCCESS(Status)) { return STATUS_OBJECT_NAME_NOT_FOUND; } ReleaseAttributeContext(DataContext); } Status = NtfsMakeFCBFromDirEntry(Vcb, DirectoryFcb, &File, Colon, FileRecord, MFTIndex, FoundFCB); ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, FileRecord); return Status; } NTSTATUS NtfsGetFCBForFile(PNTFS_VCB Vcb, PNTFS_FCB *pParentFCB, PNTFS_FCB *pFCB, PCWSTR pFileName, BOOLEAN CaseSensitive) { NTSTATUS Status; WCHAR pathName [MAX_PATH]; WCHAR elementName [MAX_PATH]; PCWSTR currentElement; PNTFS_FCB FCB; PNTFS_FCB parentFCB; DPRINT("NtfsGetFCBForFile(%p, %p, %p, '%S', %s)\n", Vcb, pParentFCB, pFCB, pFileName, CaseSensitive ? "TRUE" : "FALSE"); /* Dummy code */ // FCB = NtfsOpenRootFCB(Vcb); // *pFCB = FCB; // *pParentFCB = NULL; #if 1 /* Trivial case, open of the root directory on volume */ if (pFileName[0] == L'\0' || wcscmp(pFileName, L"\\") == 0) { DPRINT("returning root FCB\n"); FCB = NtfsOpenRootFCB(Vcb); *pFCB = FCB; *pParentFCB = NULL; return (FCB != NULL) ? STATUS_SUCCESS : STATUS_OBJECT_PATH_NOT_FOUND; } else { currentElement = pFileName + 1; wcscpy (pathName, L"\\"); FCB = NtfsOpenRootFCB (Vcb); } parentFCB = NULL; /* Parse filename and check each path element for existence and access */ while (NtfsGetNextPathElement(currentElement) != 0) { /* Skip blank directory levels */ if ((NtfsGetNextPathElement(currentElement) - currentElement) == 0) { currentElement++; continue; } DPRINT("Parsing, currentElement:%S\n", currentElement); DPRINT(" parentFCB:%p FCB:%p\n", parentFCB, FCB); /* Descend to next directory level */ if (parentFCB) { NtfsReleaseFCB(Vcb, parentFCB); parentFCB = NULL; } /* fail if element in FCB is not a directory */ if (!NtfsFCBIsDirectory(FCB)) { DPRINT("Element in requested path is not a directory\n"); NtfsReleaseFCB(Vcb, FCB); FCB = 0; *pParentFCB = NULL; *pFCB = NULL; return STATUS_OBJECT_PATH_NOT_FOUND; } parentFCB = FCB; /* Extract next directory level into dirName */ NtfsWSubString(pathName, pFileName, NtfsGetNextPathElement(currentElement) - pFileName); DPRINT(" pathName:%S\n", pathName); FCB = NtfsGrabFCBFromTable(Vcb, pathName); if (FCB == NULL) { NtfsWSubString(elementName, currentElement, NtfsGetNextPathElement(currentElement) - currentElement); DPRINT(" elementName:%S\n", elementName); Status = NtfsDirFindFile(Vcb, parentFCB, elementName, CaseSensitive, &FCB); if (Status == STATUS_OBJECT_NAME_NOT_FOUND) { *pParentFCB = parentFCB; *pFCB = NULL; currentElement = NtfsGetNextPathElement(currentElement); if (*currentElement == L'\0' || NtfsGetNextPathElement(currentElement + 1) == 0) { return STATUS_OBJECT_NAME_NOT_FOUND; } else { return STATUS_OBJECT_PATH_NOT_FOUND; } } else if (!NT_SUCCESS(Status)) { NtfsReleaseFCB(Vcb, parentFCB); *pParentFCB = NULL; *pFCB = NULL; return Status; } } currentElement = NtfsGetNextPathElement(currentElement); } *pParentFCB = parentFCB; *pFCB = FCB; #endif return STATUS_SUCCESS; } NTSTATUS NtfsReadFCBAttribute(PNTFS_VCB Vcb, PNTFS_FCB pFCB, ULONG Type, PCWSTR Name, ULONG NameLength, PVOID * Data) { NTSTATUS Status; PFILE_RECORD_HEADER FileRecord; PNTFS_ATTR_CONTEXT AttrCtxt; ULONGLONG AttrLength; FileRecord = ExAllocateFromNPagedLookasideList(&Vcb->FileRecLookasideList); if (FileRecord == NULL) { return STATUS_INSUFFICIENT_RESOURCES; } Status = ReadFileRecord(Vcb, pFCB->MFTIndex, FileRecord); if (!NT_SUCCESS(Status)) { ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, FileRecord); return Status; } Status = FindAttribute(Vcb, FileRecord, Type, Name, NameLength, &AttrCtxt, NULL); if (!NT_SUCCESS(Status)) { ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, FileRecord); return Status; } AttrLength = AttributeDataLength(AttrCtxt->pRecord); *Data = ExAllocatePoolWithTag(NonPagedPool, AttrLength, TAG_NTFS); if (*Data == NULL) { ReleaseAttributeContext(AttrCtxt); ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, FileRecord); return STATUS_INSUFFICIENT_RESOURCES; } ReadAttribute(Vcb, AttrCtxt, 0, *Data, AttrLength); ReleaseAttributeContext(AttrCtxt); ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, FileRecord); return STATUS_SUCCESS; } /* EOF */