From 32afa0efb3a05f7f18e0dd37f3cbe3eae76a8864 Mon Sep 17 00:00:00 2001 From: Pierre Schweitzer Date: Fri, 26 Sep 2014 13:57:29 +0000 Subject: [PATCH] [NTFS] - Import structures and defines from FreeLdr NTFS driver header - Import lot of code from FreeLdr NTFS driver to handle MFT & attributes - Remove code that has been overtaken by FreeLdr code - Adapt & port code from FreeLdr (allocation, status, data structures) - Adapt old code to the new functions defined from FreeLdr code - Open the MFT during volume mount and keep in VCB for further use - Implement a function to lookup a file in the MFT given its full path svn path=/trunk/; revision=64313 --- reactos/drivers/filesystems/ntfs/attrib.c | 158 ++--- reactos/drivers/filesystems/ntfs/fsctl.c | 66 +- reactos/drivers/filesystems/ntfs/mft.c | 768 ++++++++++++++++------ reactos/drivers/filesystems/ntfs/ntfs.h | 195 +++--- 4 files changed, 794 insertions(+), 393 deletions(-) diff --git a/reactos/drivers/filesystems/ntfs/attrib.c b/reactos/drivers/filesystems/ntfs/attrib.c index 5ccbe212f4e..572f6aadffa 100644 --- a/reactos/drivers/filesystems/ntfs/attrib.c +++ b/reactos/drivers/filesystems/ntfs/attrib.c @@ -33,127 +33,106 @@ /* FUNCTIONS ****************************************************************/ -static -ULONG -RunLength(PUCHAR run) -{ - return(*run & 0x0f) + ((*run >> 4) & 0x0f) + 1; -} - - -static -LONGLONG -RunLCN(PUCHAR run) +PUCHAR +DecodeRun(PUCHAR DataRun, + LONGLONG *DataRunOffset, + ULONGLONG *DataRunLength) { - UCHAR n1 = *run & 0x0f; - UCHAR n2 = (*run >> 4) & 0x0f; - LONGLONG lcn = (n2 == 0) ? 0 : (CHAR)(run[n1 + n2]); - LONG i = 0; - - for (i = n1 +n2 - 1; i > n1; i--) - lcn = (lcn << 8) + run[i]; - return lcn; -} + UCHAR DataRunOffsetSize; + UCHAR DataRunLengthSize; + CHAR i; + + DataRunOffsetSize = (*DataRun >> 4) & 0xF; + DataRunLengthSize = *DataRun & 0xF; + *DataRunOffset = 0; + *DataRunLength = 0; + DataRun++; + for (i = 0; i < DataRunLengthSize; i++) + { + *DataRunLength += ((ULONG64)*DataRun) << (i * 8); + DataRun++; + } + /* NTFS 3+ sparse files */ + if (DataRunOffsetSize == 0) + { + *DataRunOffset = -1; + } + else + { + for (i = 0; i < DataRunOffsetSize - 1; i++) + { + *DataRunOffset += ((ULONG64)*DataRun) << (i * 8); + DataRun++; + } + /* The last byte contains sign so we must process it different way. */ + *DataRunOffset = ((LONG64)(CHAR)(*(DataRun++)) << (i * 8)) + *DataRunOffset; + } -static -ULONGLONG -RunCount(PUCHAR run) -{ - UCHAR n = *run & 0xf; - ULONGLONG count = 0; - ULONG i = 0; + DPRINT("DataRunOffsetSize: %x\n", DataRunOffsetSize); + DPRINT("DataRunLengthSize: %x\n", DataRunLengthSize); + DPRINT("DataRunOffset: %x\n", *DataRunOffset); + DPRINT("DataRunLength: %x\n", *DataRunLength); - for (i = n; i > 0; i--) - count = (count << 8) + run[i]; - return count; + return DataRun; } - BOOLEAN -FindRun(PNONRESIDENT_ATTRIBUTE NresAttr, +FindRun(PNTFS_ATTR_RECORD NresAttr, ULONGLONG vcn, PULONGLONG lcn, PULONGLONG count) { - PUCHAR run; - ULONGLONG base = NresAttr->StartVcn; - - if (vcn < NresAttr->StartVcn || vcn > NresAttr->LastVcn) + if (vcn < NresAttr->NonResident.LowestVCN || vcn > NresAttr->NonResident.HighestVCN) return FALSE; - *lcn = 0; - - for (run = (PUCHAR)((ULONG_PTR)NresAttr + NresAttr->RunArrayOffset); - *run != 0; run += RunLength(run)) - { - *lcn += RunLCN(run); - *count = RunCount(run); + DecodeRun((PUCHAR)((ULONG_PTR)NresAttr + NresAttr->NonResident.MappingPairsOffset), (PLONGLONG)lcn, count); - if (base <= vcn && vcn < base + *count) - { - *lcn = (RunLCN(run) == 0) ? 0 : *lcn + vcn - base; - *count -= (ULONG)(vcn - base); - - return TRUE; - } - else - { - base += *count; - } - } - - return FALSE; + return TRUE; } static VOID -NtfsDumpFileNameAttribute(PATTRIBUTE Attribute) +NtfsDumpFileNameAttribute(PNTFS_ATTR_RECORD Attribute) { - PRESIDENT_ATTRIBUTE ResAttr; PFILENAME_ATTRIBUTE FileNameAttr; DbgPrint(" $FILE_NAME "); - ResAttr = (PRESIDENT_ATTRIBUTE)Attribute; -// DbgPrint(" Length %lu Offset %hu ", ResAttr->ValueLength, ResAttr->ValueOffset); +// DbgPrint(" Length %lu Offset %hu ", Attribute->Resident.ValueLength, Attribute->Resident.ValueOffset); - FileNameAttr = (PFILENAME_ATTRIBUTE)((ULONG_PTR)ResAttr + ResAttr->ValueOffset); + FileNameAttr = (PFILENAME_ATTRIBUTE)((ULONG_PTR)Attribute + Attribute->Resident.ValueOffset); DbgPrint(" '%.*S' ", FileNameAttr->NameLength, FileNameAttr->Name); } static VOID -NtfsDumpVolumeNameAttribute(PATTRIBUTE Attribute) +NtfsDumpVolumeNameAttribute(PNTFS_ATTR_RECORD Attribute) { - PRESIDENT_ATTRIBUTE ResAttr; PWCHAR VolumeName; DbgPrint(" $VOLUME_NAME "); - ResAttr = (PRESIDENT_ATTRIBUTE)Attribute; -// DbgPrint(" Length %lu Offset %hu ", ResAttr->ValueLength, ResAttr->ValueOffset); +// DbgPrint(" Length %lu Offset %hu ", Attribute->Resident.ValueLength, Attribute->Resident.ValueOffset); - VolumeName = (PWCHAR)((ULONG_PTR)ResAttr + ResAttr->ValueOffset); - DbgPrint(" '%.*S' ", ResAttr->ValueLength / sizeof(WCHAR), VolumeName); + VolumeName = (PWCHAR)((ULONG_PTR)Attribute + Attribute->Resident.ValueOffset); + DbgPrint(" '%.*S' ", Attribute->Resident.ValueLength / sizeof(WCHAR), VolumeName); } static VOID -NtfsDumpVolumeInformationAttribute(PATTRIBUTE Attribute) +NtfsDumpVolumeInformationAttribute(PNTFS_ATTR_RECORD Attribute) { - PRESIDENT_ATTRIBUTE ResAttr; PVOLINFO_ATTRIBUTE VolInfoAttr; DbgPrint(" $VOLUME_INFORMATION "); - ResAttr = (PRESIDENT_ATTRIBUTE)Attribute; -// DbgPrint(" Length %lu Offset %hu ", ResAttr->ValueLength, ResAttr->ValueOffset); +// DbgPrint(" Length %lu Offset %hu ", Attribute->Resident.ValueLength, Attribute->Resident.ValueOffset); - VolInfoAttr = (PVOLINFO_ATTRIBUTE)((ULONG_PTR)ResAttr + ResAttr->ValueOffset); + VolInfoAttr = (PVOLINFO_ATTRIBUTE)((ULONG_PTR)Attribute + Attribute->Resident.ValueOffset); DbgPrint(" NTFS Version %u.%u Flags 0x%04hx ", VolInfoAttr->MajorVersion, VolInfoAttr->MinorVersion, @@ -163,13 +142,11 @@ NtfsDumpVolumeInformationAttribute(PATTRIBUTE Attribute) static VOID -NtfsDumpIndexRootAttribute(PATTRIBUTE Attribute) +NtfsDumpIndexRootAttribute(PNTFS_ATTR_RECORD Attribute) { - PRESIDENT_ATTRIBUTE ResAttr; PINDEX_ROOT_ATTRIBUTE IndexRootAttr; - ResAttr = (PRESIDENT_ATTRIBUTE)Attribute; - IndexRootAttr = (PINDEX_ROOT_ATTRIBUTE)((ULONG_PTR)ResAttr + ResAttr->ValueOffset); + IndexRootAttr = (PINDEX_ROOT_ATTRIBUTE)((ULONG_PTR)Attribute + Attribute->Resident.ValueOffset); if (IndexRootAttr->AttributeType == AttributeFileName) ASSERT(IndexRootAttr->CollationRule == COLLATION_FILE_NAME); @@ -190,15 +167,14 @@ NtfsDumpIndexRootAttribute(PATTRIBUTE Attribute) static VOID -NtfsDumpAttribute (PATTRIBUTE Attribute) +NtfsDumpAttribute(PNTFS_ATTR_RECORD Attribute) { - PNONRESIDENT_ATTRIBUTE NresAttr; UNICODE_STRING Name; ULONGLONG lcn = 0; ULONGLONG runcount = 0; - switch (Attribute->AttributeType) + switch (Attribute->Type) { case AttributeFileName: NtfsDumpFileNameAttribute(Attribute); @@ -267,7 +243,7 @@ NtfsDumpAttribute (PATTRIBUTE Attribute) default: DbgPrint(" Attribute %lx ", - Attribute->AttributeType); + Attribute->Type); break; } @@ -281,16 +257,14 @@ NtfsDumpAttribute (PATTRIBUTE Attribute) } DbgPrint("(%s)\n", - Attribute->Nonresident ? "non-resident" : "resident"); + Attribute->IsNonResident ? "non-resident" : "resident"); - if (Attribute->Nonresident) + if (Attribute->IsNonResident) { - NresAttr = (PNONRESIDENT_ATTRIBUTE)Attribute; - - FindRun(NresAttr,0,&lcn, &runcount); + FindRun(Attribute,0,&lcn, &runcount); DbgPrint(" AllocatedSize %I64u DataSize %I64u\n", - NresAttr->AllocatedSize, NresAttr->DataSize); + Attribute->NonResident.AllocatedSize, Attribute->NonResident.DataSize); DbgPrint(" logical clusters: %I64u - %I64u\n", lcn, lcn + runcount - 1); } @@ -300,15 +274,15 @@ NtfsDumpAttribute (PATTRIBUTE Attribute) VOID NtfsDumpFileAttributes(PFILE_RECORD_HEADER FileRecord) { - PATTRIBUTE Attribute; + PNTFS_ATTR_RECORD Attribute; - Attribute = (PATTRIBUTE)((ULONG_PTR)FileRecord + FileRecord->AttributeOffset); - while (Attribute < (PATTRIBUTE)((ULONG_PTR)FileRecord + FileRecord->BytesInUse) && - Attribute->AttributeType != (ATTRIBUTE_TYPE)-1) + Attribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord + FileRecord->AttributeOffset); + while (Attribute < (PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord + FileRecord->BytesInUse) && + Attribute->Type != (ATTRIBUTE_TYPE)-1) { NtfsDumpAttribute(Attribute); - Attribute = (PATTRIBUTE)((ULONG_PTR)Attribute + Attribute->Length); + Attribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)Attribute + Attribute->Length); } } diff --git a/reactos/drivers/filesystems/ntfs/fsctl.c b/reactos/drivers/filesystems/ntfs/fsctl.c index 611074140b8..c822709c161 100644 --- a/reactos/drivers/filesystems/ntfs/fsctl.c +++ b/reactos/drivers/filesystems/ntfs/fsctl.c @@ -34,6 +34,8 @@ #define NDEBUG #include +UNICODE_STRING EmptyName = RTL_CONSTANT_STRING(L""); + /* FUNCTIONS ****************************************************************/ /* @@ -173,14 +175,14 @@ NtfsGetVolumeData(PDEVICE_OBJECT DeviceObject, PDEVICE_EXTENSION DeviceExt) { DISK_GEOMETRY DiskGeometry; - PFILE_RECORD_HEADER MftRecord; PFILE_RECORD_HEADER VolumeRecord; PVOLINFO_ATTRIBUTE VolumeInfo; PBOOT_SECTOR BootSector; - PATTRIBUTE Attribute; ULONG Size; PNTFS_INFO NtfsInfo = &DeviceExt->NtfsInfo; NTSTATUS Status; + PNTFS_ATTR_CONTEXT AttrCtxt; + PNTFS_ATTR_RECORD Attribute; PNTFS_FCB VolumeFcb; PWSTR VolumeNameU; @@ -251,10 +253,10 @@ NtfsGetVolumeData(PDEVICE_OBJECT DeviceObject, ExFreePool(BootSector); - MftRecord = ExAllocatePoolWithTag(NonPagedPool, - NtfsInfo->BytesPerFileRecord, - TAG_NTFS); - if (MftRecord == NULL) + DeviceExt->MasterFileTable = ExAllocatePoolWithTag(NonPagedPool, + NtfsInfo->BytesPerFileRecord, + TAG_NTFS); + if (DeviceExt->MasterFileTable == NULL) { return STATUS_INSUFFICIENT_RESOURCES; } @@ -263,11 +265,20 @@ NtfsGetVolumeData(PDEVICE_OBJECT DeviceObject, NtfsInfo->MftStart.u.LowPart * NtfsInfo->SectorsPerCluster, NtfsInfo->BytesPerFileRecord / NtfsInfo->BytesPerSector, NtfsInfo->BytesPerSector, - (PVOID)MftRecord, + (PVOID)DeviceExt->MasterFileTable, TRUE); if (!NT_SUCCESS(Status)) { - ExFreePool(MftRecord); + DPRINT1("Failed reading MFT.\n"); + ExFreePool(DeviceExt->MasterFileTable); + return Status; + } + + Status = FindAttribute(DeviceExt, DeviceExt->MasterFileTable, AttributeData, &EmptyName, &DeviceExt->MFTContext); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Can't find data attribute for Master File Table.\n"); + ExFreePool(DeviceExt->MasterFileTable); return Status; } @@ -276,40 +287,41 @@ NtfsGetVolumeData(PDEVICE_OBJECT DeviceObject, TAG_NTFS); if (VolumeRecord == NULL) { - ExFreePool(MftRecord); + DPRINT1("Allocation failed for volume record\n"); + ExFreePool(DeviceExt->MasterFileTable); return STATUS_INSUFFICIENT_RESOURCES; } /* Read Volume File (MFT index 3) */ DeviceExt->StorageDevice = DeviceObject; Status = ReadFileRecord(DeviceExt, - 3, - VolumeRecord, - MftRecord); + NTFS_FILE_VOLUME, + VolumeRecord); if (!NT_SUCCESS(Status)) { + DPRINT1("Failed reading volume file\n"); ExFreePool(VolumeRecord); - ExFreePool(MftRecord); + ExFreePool(DeviceExt->MasterFileTable); return Status; } /* Enumerate attributes */ - NtfsDumpFileAttributes (MftRecord); + NtfsDumpFileAttributes(DeviceExt->MasterFileTable); /* Enumerate attributes */ - NtfsDumpFileAttributes (VolumeRecord); + NtfsDumpFileAttributes(VolumeRecord); /* Get volume name */ - Attribute = FindAttribute (VolumeRecord, AttributeVolumeName, NULL); - DPRINT("Attribute %p\n", Attribute); + Status = FindAttribute(DeviceExt, VolumeRecord, AttributeVolumeName, &EmptyName, &AttrCtxt); - if (Attribute != NULL && ((PRESIDENT_ATTRIBUTE)Attribute)->ValueLength != 0) + if (NT_SUCCESS(Status) && AttrCtxt->Record.Resident.ValueLength != 0) { - DPRINT("Data length %lu\n", AttributeDataLength (Attribute)); + Attribute = &AttrCtxt->Record; + DPRINT("Data length %lu\n", AttributeDataLength(Attribute)); NtfsInfo->VolumeLabelLength = - min (((PRESIDENT_ATTRIBUTE)Attribute)->ValueLength, MAXIMUM_VOLUME_LABEL_LENGTH); + min (Attribute->Resident.ValueLength, MAXIMUM_VOLUME_LABEL_LENGTH); RtlCopyMemory(NtfsInfo->VolumeLabel, - (PVOID)((ULONG_PTR)Attribute + ((PRESIDENT_ATTRIBUTE)Attribute)->ValueOffset), + (PVOID)((ULONG_PTR)Attribute + Attribute->Resident.ValueOffset), NtfsInfo->VolumeLabelLength); VolumeNameU = NtfsInfo->VolumeLabel; } @@ -322,8 +334,9 @@ NtfsGetVolumeData(PDEVICE_OBJECT DeviceObject, VolumeFcb = NtfsCreateFCB(VolumeNameU, DeviceExt); if (VolumeFcb == NULL) { + DPRINT1("Failed allocating volume FCB\n"); ExFreePool(VolumeRecord); - ExFreePool(MftRecord); + ExFreePool(DeviceExt->MasterFileTable); return STATUS_INSUFFICIENT_RESOURCES; } @@ -334,20 +347,19 @@ NtfsGetVolumeData(PDEVICE_OBJECT DeviceObject, DeviceExt->VolumeFcb = VolumeFcb; /* Get volume information */ - Attribute = FindAttribute (VolumeRecord, AttributeVolumeInformation, NULL); - DPRINT("Attribute %p\n", Attribute); + Status = FindAttribute(DeviceExt, VolumeRecord, AttributeVolumeInformation, &EmptyName, &AttrCtxt); - if (Attribute != NULL && ((PRESIDENT_ATTRIBUTE)Attribute)->ValueLength != 0) + if (NT_SUCCESS(Status) && AttrCtxt->Record.Resident.ValueLength != 0) { + Attribute = &AttrCtxt->Record; DPRINT("Data length %lu\n", AttributeDataLength (Attribute)); - VolumeInfo = (PVOID)((ULONG_PTR)Attribute + ((PRESIDENT_ATTRIBUTE)Attribute)->ValueOffset); + VolumeInfo = (PVOID)((ULONG_PTR)Attribute + Attribute->Resident.ValueOffset); NtfsInfo->MajorVersion = VolumeInfo->MajorVersion; NtfsInfo->MinorVersion = VolumeInfo->MinorVersion; NtfsInfo->Flags = VolumeInfo->Flags; } - ExFreePool(MftRecord); ExFreePool(VolumeRecord); return Status; diff --git a/reactos/drivers/filesystems/ntfs/mft.c b/reactos/drivers/filesystems/ntfs/mft.c index d40d91994e7..7e99ab2fa6d 100644 --- a/reactos/drivers/filesystems/ntfs/mft.c +++ b/reactos/drivers/filesystems/ntfs/mft.c @@ -31,296 +31,668 @@ #define NDEBUG #include +UNICODE_STRING IndexOfFileNames = RTL_CONSTANT_STRING(L"$I30"); + /* FUNCTIONS ****************************************************************/ -NTSTATUS -NtfsOpenMft(PDEVICE_EXTENSION Vcb) +PNTFS_ATTR_CONTEXT +PrepareAttributeContext(PNTFS_ATTR_RECORD AttrRecord) { -// PVOID Bitmap; - PFILE_RECORD_HEADER MftRecord; - PFILE_RECORD_HEADER FileRecord; -// PATTRIBUTE Attribute; -// PATTRIBUTE AttrData; -// PRESIDENT_ATTRIBUTE ResAttr; - - NTSTATUS Status; - ULONG BytesPerFileRecord; - ULONG n; - ULONG i; + PNTFS_ATTR_CONTEXT Context; - DPRINT1("NtfsOpenMft() called\n"); - - BytesPerFileRecord = Vcb->NtfsInfo.BytesPerFileRecord; - - MftRecord = ExAllocatePoolWithTag(NonPagedPool, - BytesPerFileRecord, - TAG_NTFS); - if (MftRecord == NULL) + Context = ExAllocatePoolWithTag(NonPagedPool, + FIELD_OFFSET(NTFS_ATTR_CONTEXT, Record) + AttrRecord->Length, + TAG_NTFS); + RtlCopyMemory(&Context->Record, AttrRecord, AttrRecord->Length); + if (AttrRecord->IsNonResident) { - return STATUS_INSUFFICIENT_RESOURCES; + LONGLONG DataRunOffset; + ULONGLONG DataRunLength; + + Context->CacheRun = (PUCHAR)&Context->Record + Context->Record.NonResident.MappingPairsOffset; + Context->CacheRunOffset = 0; + Context->CacheRun = DecodeRun(Context->CacheRun, &DataRunOffset, &DataRunLength); + Context->CacheRunLength = DataRunLength; + if (DataRunOffset != -1) + { + /* Normal run. */ + Context->CacheRunStartLCN = + Context->CacheRunLastLCN = DataRunOffset; + } + else + { + /* Sparse run. */ + Context->CacheRunStartLCN = -1; + Context->CacheRunLastLCN = 0; + } + Context->CacheRunCurrentOffset = 0; } - Status = NtfsReadSectors(Vcb->StorageDevice, - Vcb->NtfsInfo.MftStart.u.LowPart * Vcb->NtfsInfo.SectorsPerCluster, - BytesPerFileRecord / Vcb->NtfsInfo.BytesPerSector, - Vcb->NtfsInfo.BytesPerSector, - (PVOID)MftRecord, - FALSE); - if (!NT_SUCCESS(Status)) - { - ExFreePool(MftRecord); - return Status; - } + return Context; +} - FixupUpdateSequenceArray(MftRecord); -// Attribute = FindAttribute(MftRecord, AttributeBitmap, 0); +VOID +ReleaseAttributeContext(PNTFS_ATTR_CONTEXT Context) +{ + ExFreePoolWithTag(Context, TAG_NTFS); +} - /* Get number of file records*/ - n = AttributeDataLength(FindAttribute(MftRecord, AttributeData, 0)) / BytesPerFileRecord; - FileRecord = ExAllocatePoolWithTag(NonPagedPool, - BytesPerFileRecord, - TAG_NTFS); - if (FileRecord == NULL) - { - ExFreePool(MftRecord); - return STATUS_INSUFFICIENT_RESOURCES; - } +PNTFS_ATTR_CONTEXT +FindAttributeHelper(PDEVICE_EXTENSION Vcb, + PNTFS_ATTR_RECORD AttrRecord, + PNTFS_ATTR_RECORD AttrRecordEnd, + ULONG Type, + const WCHAR *Name, + ULONG NameLength) +{ + DPRINT("FindAttributeHelper(%p, %p, %p, 0x%x, %s, %u)\n", Vcb, AttrRecord, AttrRecordEnd, Type, Name, NameLength); - /* Enumerate MFT Records */ - DPRINT("Enumerate MFT records\n"); - for (i = 0; i < n; i++) + while (AttrRecord < AttrRecordEnd) { - ReadFileRecord(Vcb, - i, - FileRecord, - MftRecord); + DPRINT("AttrRecord->Type = 0x%x\n", AttrRecord->Type); + + if (AttrRecord->Type == AttributeEnd) + break; + + if (AttrRecord->Type == AttributeAttributeList) + { + PNTFS_ATTR_CONTEXT Context; + PNTFS_ATTR_CONTEXT ListContext; + PVOID ListBuffer; + ULONGLONG ListSize; + PNTFS_ATTR_RECORD ListAttrRecord; + PNTFS_ATTR_RECORD ListAttrRecordEnd; + + // Do not handle non-resident yet + ASSERT(!(AttrRecord->IsNonResident & 1)); + + ListContext = PrepareAttributeContext(AttrRecord); + + ListSize = AttributeDataLength(&ListContext->Record); + if(ListSize <= 0xFFFFFFFF) + ListBuffer = ExAllocatePoolWithTag(NonPagedPool, (ULONG)ListSize, TAG_NTFS); + else + ListBuffer = NULL; + + if(!ListBuffer) + { + DPRINT("Failed to allocate memory: %x\n", (ULONG)ListSize); + continue; + } + + ListAttrRecord = (PNTFS_ATTR_RECORD)ListBuffer; + ListAttrRecordEnd = (PNTFS_ATTR_RECORD)((PCHAR)ListBuffer + ListSize); + + if (ReadAttribute(Vcb, ListContext, 0, ListBuffer, (ULONG)ListSize) == ListSize) + { + Context = FindAttributeHelper(Vcb, ListAttrRecord, ListAttrRecordEnd, + Type, Name, NameLength); + + ReleaseAttributeContext(ListContext); + ExFreePoolWithTag(ListBuffer, TAG_NTFS); + + if (Context != NULL) + { + DPRINT("Found context = %p\n", Context); + return Context; + } + } + } - if (FileRecord->Ntfs.Type == NRH_FILE_TYPE && - (FileRecord->Flags & FRH_IN_USE)) + if (AttrRecord->Type == Type) { - DPRINT("\nFile %lu\n\n", i); + DPRINT("%d, %d\n", AttrRecord->NameLength, NameLength); + if (AttrRecord->NameLength == NameLength) + { + PWCHAR AttrName; + + AttrName = (PWCHAR)((PCHAR)AttrRecord + AttrRecord->NameOffset); + DPRINT("%s, %s\n", AttrName, Name); + if (RtlCompareMemory(AttrName, Name, NameLength << 1) == (NameLength << 1)) + { + /* Found it, fill up the context and return. */ + DPRINT("Found context\n"); + return PrepareAttributeContext(AttrRecord); + } + } + } - /* Enumerate attributtes */ - NtfsDumpFileAttributes (FileRecord); - DbgPrint("\n\n"); + if (AttrRecord->Length == 0) + { + DPRINT("Null length attribute record\n"); + return NULL; } + AttrRecord = (PNTFS_ATTR_RECORD)((PCHAR)AttrRecord + AttrRecord->Length); } - ExFreePool(FileRecord); - ExFreePool(MftRecord); - - return Status; + DPRINT("Ended\n"); + return NULL; } -PATTRIBUTE -FindAttribute(PFILE_RECORD_HEADER FileRecord, - ATTRIBUTE_TYPE Type, - PWSTR name) +NTSTATUS +FindAttribute(PDEVICE_EXTENSION Vcb, + PFILE_RECORD_HEADER MftRecord, + ULONG Type, + PUNICODE_STRING Name, + PNTFS_ATTR_CONTEXT * AttrCtx) { - PATTRIBUTE Attribute; + PNTFS_ATTR_RECORD AttrRecord; + PNTFS_ATTR_RECORD AttrRecordEnd; - UNREFERENCED_PARAMETER(name); + DPRINT("NtfsFindAttribute(%p, %p, %u, %s)\n", Vcb, MftRecord, Type, Name); - Attribute = (PATTRIBUTE)((ULONG_PTR)FileRecord + FileRecord->AttributeOffset); - while (Attribute < (PATTRIBUTE)((ULONG_PTR)FileRecord + FileRecord->BytesInUse) && - Attribute->AttributeType != (ATTRIBUTE_TYPE)-1) - { - if (Attribute->AttributeType == Type) - { - return Attribute; - } + AttrRecord = (PNTFS_ATTR_RECORD)((PCHAR)MftRecord + MftRecord->AttributeOffset); + AttrRecordEnd = (PNTFS_ATTR_RECORD)((PCHAR)MftRecord + Vcb->NtfsInfo.BytesPerFileRecord); - Attribute = (PATTRIBUTE)((ULONG_PTR)Attribute + Attribute->Length); + *AttrCtx = FindAttributeHelper(Vcb, AttrRecord, AttrRecordEnd, Type, Name->Buffer, Name->Length); + if (*AttrCtx == NULL) + { + return STATUS_OBJECT_NAME_NOT_FOUND; } - return NULL; + return STATUS_SUCCESS; } ULONG -AttributeAllocatedLength(PATTRIBUTE Attribute) +AttributeAllocatedLength(PNTFS_ATTR_RECORD AttrRecord) { - if (Attribute->Nonresident) - { - return ((PNONRESIDENT_ATTRIBUTE)Attribute)->AllocatedSize; - } + if (AttrRecord->IsNonResident) + return AttrRecord->NonResident.AllocatedSize; + else + return AttrRecord->Resident.ValueLength; +} + - return ((PRESIDENT_ATTRIBUTE)Attribute)->ValueLength; +ULONGLONG +AttributeDataLength(PNTFS_ATTR_RECORD AttrRecord) +{ + if (AttrRecord->IsNonResident) + return AttrRecord->NonResident.DataSize; + else + return AttrRecord->Resident.ValueLength; } ULONG -AttributeDataLength(PATTRIBUTE Attribute) +ReadAttribute(PDEVICE_EXTENSION Vcb, + PNTFS_ATTR_CONTEXT Context, + ULONGLONG Offset, + PCHAR Buffer, + ULONG Length) { - if (Attribute->Nonresident) + ULONGLONG LastLCN; + PUCHAR DataRun; + LONGLONG DataRunOffset; + ULONGLONG DataRunLength; + LONGLONG DataRunStartLCN; + ULONGLONG CurrentOffset; + ULONG ReadLength; + ULONG AlreadyRead; + NTSTATUS Status; + + if (!Context->Record.IsNonResident) { - return ((PNONRESIDENT_ATTRIBUTE)Attribute)->DataSize; + if (Offset > Context->Record.Resident.ValueLength) + return 0; + if (Offset + Length > Context->Record.Resident.ValueLength) + Length = (ULONG)(Context->Record.Resident.ValueLength - Offset); + RtlCopyMemory(Buffer, (PCHAR)&Context->Record + Context->Record.Resident.ValueOffset + Offset, Length); + return Length; } - return ((PRESIDENT_ATTRIBUTE)Attribute)->ValueLength; -} + /* + * Non-resident attribute + */ + /* + * I. Find the corresponding start data run. + */ -VOID -ReadAttribute(PATTRIBUTE attr, - PVOID buffer, - PDEVICE_EXTENSION Vcb, - PDEVICE_OBJECT DeviceObject) -{ - PNONRESIDENT_ATTRIBUTE NresAttr = (PNONRESIDENT_ATTRIBUTE)attr; + AlreadyRead = 0; - UNREFERENCED_PARAMETER(DeviceObject); - - if (attr->Nonresident == FALSE) + // FIXME: Cache seems to be non-working. Disable it for now + //if(Context->CacheRunOffset <= Offset && Offset < Context->CacheRunOffset + Context->CacheRunLength * Volume->ClusterSize) + if (0) + { + DataRun = Context->CacheRun; + LastLCN = Context->CacheRunLastLCN; + DataRunStartLCN = Context->CacheRunStartLCN; + DataRunLength = Context->CacheRunLength; + CurrentOffset = Context->CacheRunCurrentOffset; + } + else { - memcpy(buffer, - (PVOID)((ULONG_PTR)attr + ((PRESIDENT_ATTRIBUTE)attr)->ValueOffset), - ((PRESIDENT_ATTRIBUTE)attr)->ValueLength); + LastLCN = 0; + DataRun = (PUCHAR)&Context->Record + Context->Record.NonResident.MappingPairsOffset; + CurrentOffset = 0; + + while (1) + { + DataRun = DecodeRun(DataRun, &DataRunOffset, &DataRunLength); + if (DataRunOffset != -1) + { + /* Normal data run. */ + DataRunStartLCN = LastLCN + DataRunOffset; + LastLCN = DataRunStartLCN; + } + else + { + /* Sparse data run. */ + DataRunStartLCN = -1; + } + + if (Offset >= CurrentOffset && + Offset < CurrentOffset + (DataRunLength * Vcb->NtfsInfo.BytesPerCluster)) + { + break; + } + + if (*DataRun == 0) + { + return AlreadyRead; + } + + CurrentOffset += DataRunLength * Vcb->NtfsInfo.BytesPerCluster; + } } - ReadExternalAttribute(Vcb, - NresAttr, - 0, - (ULONG)(NresAttr->LastVcn) + 1, - buffer); + /* + * II. Go through the run list and read the data + */ + + ReadLength = (ULONG)min(DataRunLength * Vcb->NtfsInfo.BytesPerCluster - (Offset - CurrentOffset), Length); + if (DataRunStartLCN == -1) + RtlZeroMemory(Buffer, ReadLength); + Status = NtfsReadDisk(Vcb->StorageDevice, + DataRunStartLCN * Vcb->NtfsInfo.BytesPerCluster + Offset - CurrentOffset, + ReadLength, + (PVOID)Buffer, + FALSE); + if (NT_SUCCESS(Status)) + { + Length -= ReadLength; + Buffer += ReadLength; + AlreadyRead += ReadLength; + + if (ReadLength == DataRunLength * Vcb->NtfsInfo.BytesPerCluster - (Offset - CurrentOffset)) + { + CurrentOffset += DataRunLength * Vcb->NtfsInfo.BytesPerCluster; + DataRun = DecodeRun(DataRun, &DataRunOffset, &DataRunLength); + if (DataRunLength != (ULONGLONG)-1) + { + DataRunStartLCN = LastLCN + DataRunOffset; + LastLCN = DataRunStartLCN; + } + else + DataRunStartLCN = -1; + + if (*DataRun == 0) + return AlreadyRead; + } + + while (Length > 0) + { + ReadLength = (ULONG)min(DataRunLength * Vcb->NtfsInfo.BytesPerCluster, Length); + if (DataRunStartLCN == -1) + RtlZeroMemory(Buffer, ReadLength); + else + { + Status = NtfsReadDisk(Vcb->StorageDevice, + DataRunStartLCN * Vcb->NtfsInfo.BytesPerCluster, + ReadLength, + (PVOID)Buffer, + FALSE); + if (!NT_SUCCESS(Status)) + break; + } + + Length -= ReadLength; + Buffer += ReadLength; + AlreadyRead += ReadLength; + + /* We finished this request, but there still data in this data run. */ + if (Length == 0 && ReadLength != DataRunLength * Vcb->NtfsInfo.BytesPerCluster) + break; + + /* + * Go to next run in the list. + */ + + if (*DataRun == 0) + break; + CurrentOffset += DataRunLength * Vcb->NtfsInfo.BytesPerCluster; + DataRun = DecodeRun(DataRun, &DataRunOffset, &DataRunLength); + if (DataRunOffset != -1) + { + /* Normal data run. */ + DataRunStartLCN = LastLCN + DataRunOffset; + LastLCN = DataRunStartLCN; + } + else + { + /* Sparse data run. */ + DataRunStartLCN = -1; + } + } /* while */ + + } /* if Disk */ + + Context->CacheRun = DataRun; + Context->CacheRunOffset = Offset + AlreadyRead; + Context->CacheRunStartLCN = DataRunStartLCN; + Context->CacheRunLength = DataRunLength; + Context->CacheRunLastLCN = LastLCN; + Context->CacheRunCurrentOffset = CurrentOffset; + + return AlreadyRead; } NTSTATUS ReadFileRecord(PDEVICE_EXTENSION Vcb, - ULONG index, - PFILE_RECORD_HEADER file, - PFILE_RECORD_HEADER Mft) + ULONGLONG index, + PFILE_RECORD_HEADER file) { - PVOID p; - ULONG BytesPerFileRecord = Vcb->NtfsInfo.BytesPerFileRecord; - ULONG clusters = max(BytesPerFileRecord / Vcb->NtfsInfo.BytesPerCluster, 1); - ULONGLONG vcn = index * BytesPerFileRecord / Vcb->NtfsInfo.BytesPerCluster; - LONG m = (Vcb->NtfsInfo.BytesPerCluster / BytesPerFileRecord) - 1; - ULONG n = m > 0 ? (index & m) : 0; + ULONGLONG BytesRead; - p = ExAllocatePoolWithTag(NonPagedPool, - clusters * Vcb->NtfsInfo.BytesPerCluster, - TAG_NTFS); + BytesRead = ReadAttribute(Vcb, Vcb->MFTContext, index * Vcb->NtfsInfo.BytesPerFileRecord, (PCHAR)file, Vcb->NtfsInfo.BytesPerFileRecord); + if (BytesRead != Vcb->NtfsInfo.BytesPerFileRecord) + { + DPRINT1("ReadFileRecord failed: %u read, %u expected\n", BytesRead, Vcb->NtfsInfo.BytesPerFileRecord); + return STATUS_PARTIAL_COPY; + } - ReadVCN (Vcb, Mft, AttributeData, vcn, clusters, p); + /* Apply update sequence array fixups. */ + return FixupUpdateSequenceArray(Vcb, &file->Ntfs); +} - memcpy(file, - (PVOID)((ULONG_PTR)p + n * BytesPerFileRecord), - BytesPerFileRecord); - ExFreePool(p); +NTSTATUS +FixupUpdateSequenceArray(PDEVICE_EXTENSION Vcb, + PNTFS_RECORD_HEADER Record) +{ + USHORT *USA; + USHORT USANumber; + USHORT USACount; + USHORT *Block; + + USA = (USHORT*)((PCHAR)Record + Record->UsaOffset); + USANumber = *(USA++); + USACount = Record->UsaCount - 1; /* Exclude the USA Number. */ + Block = (USHORT*)((PCHAR)Record + Vcb->NtfsInfo.BytesPerSector - 2); - FixupUpdateSequenceArray(file); + while (USACount) + { + if (*Block != USANumber) + { + DPRINT1("Mismatch with USA: %u read, %u expected\n" , *Block, USANumber); + return STATUS_UNSUCCESSFUL; + } + *Block = *(USA++); + Block = (USHORT*)((PCHAR)Block + Vcb->NtfsInfo.BytesPerSector); + USACount--; + } return STATUS_SUCCESS; } -VOID -ReadExternalAttribute(PDEVICE_EXTENSION Vcb, - PNONRESIDENT_ATTRIBUTE NresAttr, - ULONGLONG vcn, - ULONG count, - PVOID buffer) +NTSTATUS +ReadLCN(PDEVICE_EXTENSION Vcb, + ULONGLONG lcn, + ULONG count, + PVOID buffer) { - ULONGLONG lcn; - ULONGLONG runcount; - ULONG readcount; - ULONG left; - ULONG n; - - PUCHAR bytes = (PUCHAR)buffer; + LARGE_INTEGER DiskSector; - for (left = count; left > 0; left -= readcount) - { - FindRun(NresAttr, vcn, &lcn, &runcount); + DiskSector.QuadPart = lcn; -// readcount = (ULONG)(__min(runcount, left)); - readcount = (ULONG)min(runcount, left); + return NtfsReadSectors(Vcb->StorageDevice, + DiskSector.u.LowPart * Vcb->NtfsInfo.SectorsPerCluster, + count * Vcb->NtfsInfo.SectorsPerCluster, + Vcb->NtfsInfo.BytesPerSector, + buffer, + FALSE); +} - n = readcount * Vcb->NtfsInfo.BytesPerCluster; +BOOLEAN +CompareFileName(PUNICODE_STRING FileName, + PINDEX_ENTRY_ATTRIBUTE IndexEntry) +{ + UNICODE_STRING EntryName; - if (lcn == 0) - memset(bytes, 0, n); - else - ReadLCN(Vcb, lcn, readcount, bytes); + EntryName.Buffer = IndexEntry->FileName.Name; + EntryName.Length = + EntryName.MaximumLength = IndexEntry->FileName.NameLength; - vcn += readcount; - bytes += n; - } + return (RtlCompareUnicodeString(FileName, &EntryName, !!(IndexEntry->FileName.NameType != NTFS_FILE_NAME_POSIX)) == TRUE); } -VOID -ReadVCN(PDEVICE_EXTENSION Vcb, - PFILE_RECORD_HEADER file, - ATTRIBUTE_TYPE type, - ULONGLONG vcn, - ULONG count, - PVOID buffer) +NTSTATUS +NtfsFindMftRecord(PDEVICE_EXTENSION Vcb, ULONGLONG MFTIndex, PUNICODE_STRING FileName, ULONGLONG *OutMFTIndex) { - PNONRESIDENT_ATTRIBUTE NresAttr; - PATTRIBUTE attr; + PFILE_RECORD_HEADER MftRecord; + //ULONG Magic; + PNTFS_ATTR_CONTEXT IndexRootCtx; + PNTFS_ATTR_CONTEXT IndexBitmapCtx; + PNTFS_ATTR_CONTEXT IndexAllocationCtx; + PINDEX_ROOT_ATTRIBUTE IndexRoot; + ULONGLONG BitmapDataSize; + ULONGLONG IndexAllocationSize; + PCHAR BitmapData; + PCHAR IndexRecord; + PINDEX_ENTRY_ATTRIBUTE IndexEntry, IndexEntryEnd; + ULONG RecordOffset; + ULONG IndexBlockSize; + NTSTATUS Status; - attr = FindAttribute(file, type, 0); + MftRecord = ExAllocatePoolWithTag(NonPagedPool, + Vcb->NtfsInfo.BytesPerFileRecord, + TAG_NTFS); + if (MftRecord == NULL) + { + return STATUS_INSUFFICIENT_RESOURCES; + } + + if (ReadFileRecord(Vcb, MFTIndex, MftRecord)) + { + //Magic = MftRecord->Magic; + + Status = FindAttribute(Vcb, MftRecord, AttributeIndexRoot, &IndexOfFileNames, &IndexRootCtx); + if (!NT_SUCCESS(Status)) + { + ExFreePoolWithTag(MftRecord, TAG_NTFS); + return Status; + } + + IndexRecord = ExAllocatePoolWithTag(NonPagedPool, Vcb->NtfsInfo.BytesPerIndexRecord, TAG_NTFS); + if (IndexRecord == NULL) + { + ExFreePoolWithTag(MftRecord, TAG_NTFS); + return STATUS_INSUFFICIENT_RESOURCES; + } + + ReadAttribute(Vcb, IndexRootCtx, 0, IndexRecord, Vcb->NtfsInfo.BytesPerIndexRecord); + IndexRoot = (PINDEX_ROOT_ATTRIBUTE)IndexRecord; + IndexEntry = (PINDEX_ENTRY_ATTRIBUTE)((PCHAR)&IndexRoot->Header + IndexRoot->Header.FirstEntryOffset); + /* Index root is always resident. */ + IndexEntryEnd = (PINDEX_ENTRY_ATTRIBUTE)(IndexRecord + IndexRootCtx->Record.Resident.ValueLength); + ReleaseAttributeContext(IndexRootCtx); - NresAttr = (PNONRESIDENT_ATTRIBUTE) attr; + DPRINT("IndexRecordSize: %x IndexBlockSize: %x\n", Vcb->NtfsInfo.BytesPerIndexRecord, IndexRoot->SizeOfEntry); + + while (IndexEntry < IndexEntryEnd && + !(IndexEntry->Flags & NTFS_INDEX_ENTRY_END)) + { + if (CompareFileName(FileName, IndexEntry)) + { + *OutMFTIndex = IndexEntry->Data.Directory.IndexedFile; + ExFreePoolWithTag(IndexRecord, TAG_NTFS); + ExFreePoolWithTag(MftRecord, TAG_NTFS); + return STATUS_SUCCESS; + } + IndexEntry = (PINDEX_ENTRY_ATTRIBUTE)((PCHAR)IndexEntry + IndexEntry->Length); + } + + if (IndexRoot->Header.Flags & INDEX_ROOT_LARGE) + { + DPRINT("Large Index!\n"); + + IndexBlockSize = IndexRoot->SizeOfEntry; + + Status = FindAttribute(Vcb, MftRecord, AttributeBitmap, &IndexOfFileNames, &IndexBitmapCtx); + if (!NT_SUCCESS(Status)) + { + DPRINT("Corrupted filesystem!\n"); + ExFreePoolWithTag(MftRecord, TAG_NTFS); + return Status; + } + BitmapDataSize = AttributeDataLength(&IndexBitmapCtx->Record); + DPRINT("BitmapDataSize: %x\n", (ULONG)BitmapDataSize); + if(BitmapDataSize <= 0xFFFFFFFF) + BitmapData = ExAllocatePoolWithTag(NonPagedPool, (ULONG)BitmapDataSize, TAG_NTFS); + else + BitmapData = NULL; + + if (BitmapData == NULL) + { + ExFreePoolWithTag(IndexRecord, TAG_NTFS); + ExFreePoolWithTag(MftRecord, TAG_NTFS); + return STATUS_INSUFFICIENT_RESOURCES; + } + ReadAttribute(Vcb, IndexBitmapCtx, 0, BitmapData, (ULONG)BitmapDataSize); + ReleaseAttributeContext(IndexBitmapCtx); + + Status = FindAttribute(Vcb, MftRecord, AttributeIndexAllocation, &IndexOfFileNames, &IndexAllocationCtx); + if (!NT_SUCCESS(Status)) + { + DPRINT("Corrupted filesystem!\n"); + ExFreePoolWithTag(BitmapData, TAG_NTFS); + ExFreePoolWithTag(IndexRecord, TAG_NTFS); + ExFreePoolWithTag(MftRecord, TAG_NTFS); + return Status; + } + IndexAllocationSize = AttributeDataLength(&IndexAllocationCtx->Record); + + RecordOffset = 0; + + for (;;) + { + DPRINT("RecordOffset: %x IndexAllocationSize: %x\n", RecordOffset, IndexAllocationSize); + for (; RecordOffset < IndexAllocationSize;) + { + UCHAR Bit = 1 << ((RecordOffset / IndexBlockSize) & 7); + ULONG Byte = (RecordOffset / IndexBlockSize) >> 3; + if ((BitmapData[Byte] & Bit)) + break; + RecordOffset += IndexBlockSize; + } + + if (RecordOffset >= IndexAllocationSize) + { + break; + } + + ReadAttribute(Vcb, IndexAllocationCtx, RecordOffset, IndexRecord, IndexBlockSize); + + if (!FixupUpdateSequenceArray(Vcb, &((PFILE_RECORD_HEADER)IndexRecord)->Ntfs)) + { + break; + } + + /* FIXME */ + IndexEntry = (PINDEX_ENTRY_ATTRIBUTE)(IndexRecord + 0x18 + *(USHORT *)(IndexRecord + 0x18)); + IndexEntryEnd = (PINDEX_ENTRY_ATTRIBUTE)(IndexRecord + IndexBlockSize); + + while (IndexEntry < IndexEntryEnd && + !(IndexEntry->Flags & NTFS_INDEX_ENTRY_END)) + { + if (CompareFileName(FileName, IndexEntry)) + { + DPRINT("File found\n"); + *OutMFTIndex = IndexEntry->Data.Directory.IndexedFile; + ExFreePoolWithTag(BitmapData, TAG_NTFS); + ExFreePoolWithTag(IndexRecord, TAG_NTFS); + ExFreePoolWithTag(MftRecord, TAG_NTFS); + ReleaseAttributeContext(IndexAllocationCtx); + return STATUS_SUCCESS; + } + IndexEntry = (PINDEX_ENTRY_ATTRIBUTE)((PCHAR)IndexEntry + IndexEntry->Length); + } + + RecordOffset += IndexBlockSize; + } + + ReleaseAttributeContext(IndexAllocationCtx); + ExFreePoolWithTag(BitmapData, TAG_NTFS); + } - if (NresAttr == 0 || (vcn < NresAttr->StartVcn ||vcn > NresAttr->LastVcn)) + ExFreePoolWithTag(IndexRecord, TAG_NTFS); + } + else { -// PATTRIBUTE attrList = FindAttribute(file,AttributeAttributeList,0); - DbgPrint("Exeption \n"); -// KeDebugCheck(0); + DPRINT("Can't read MFT record\n"); } + ExFreePoolWithTag(MftRecord, TAG_NTFS); - ReadExternalAttribute(Vcb, NresAttr, vcn, count, buffer); + return STATUS_OBJECT_PATH_NOT_FOUND; } - -#if 0 -BOOL bitset(PUCHAR bitmap, ULONG i) +NTSTATUS +NtfsLookupFile(PDEVICE_EXTENSION Vcb, + PUNICODE_STRING PathName, + PFILE_RECORD_HEADER *FileRecord, + PNTFS_ATTR_CONTEXT *DataContext) { - return (bitmap[i>>3] & (1 << (i & 7))) !=0; -} -#endif + ULONGLONG CurrentMFTIndex; + UNICODE_STRING Current, Remaining; + NTSTATUS Status; + DPRINT1("NtfsLookupFile(%p, %wZ, %p)\n", Vcb, PathName, FileRecord); -VOID -FixupUpdateSequenceArray(PFILE_RECORD_HEADER file) -{ - PUSHORT usa = (PUSHORT)((ULONG_PTR)file + file->Ntfs.UsaOffset); - PUSHORT sector = (PUSHORT)file; - ULONG i; + CurrentMFTIndex = NTFS_FILE_ROOT; + FsRtlDissectName(*PathName, &Current, &Remaining); - for (i = 1; i < file->Ntfs.UsaCount; i++) + while (Current.Length != 0) { - sector[255] = usa[i]; - sector += 256; + DPRINT1("Lookup: %wZ\n", &Current); + + Status = NtfsFindMftRecord(Vcb, CurrentMFTIndex, &Current, &CurrentMFTIndex); + if (!NT_SUCCESS(Status)) + { + return Status; + } + + FsRtlDissectName(*PathName, &Current, &Remaining); } -} + *FileRecord = ExAllocatePoolWithTag(NonPagedPool, Vcb->NtfsInfo.BytesPerFileRecord, TAG_NTFS); + if (*FileRecord == NULL) + { + DPRINT("NtfsLookupFile: Can't allocate MFT record\n"); + return STATUS_INSUFFICIENT_RESOURCES; + } -NTSTATUS -ReadLCN(PDEVICE_EXTENSION Vcb, - ULONGLONG lcn, - ULONG count, - PVOID buffer) -{ - LARGE_INTEGER DiskSector; + Status = ReadFileRecord(Vcb, CurrentMFTIndex, *FileRecord); + if (!NT_SUCCESS(Status)) + { + DPRINT("NtfsLookupFile: Can't read MFT record\n"); + return Status; + } - DiskSector.QuadPart = lcn; + Status = FindAttribute(Vcb, *FileRecord, AttributeData, PathName, DataContext); + if (!NT_SUCCESS(Status)) + { + DPRINT("NtfsLookupFile: Can't find data attribute\n"); + return Status; + } - return NtfsReadSectors(Vcb->StorageDevice, - DiskSector.u.LowPart * Vcb->NtfsInfo.SectorsPerCluster, - count * Vcb->NtfsInfo.SectorsPerCluster, - Vcb->NtfsInfo.BytesPerSector, - buffer, - FALSE); + return STATUS_SUCCESS; } - /* EOF */ diff --git a/reactos/drivers/filesystems/ntfs/ntfs.h b/reactos/drivers/filesystems/ntfs/ntfs.h index 0da7175b644..abd3a4553e5 100644 --- a/reactos/drivers/filesystems/ntfs/ntfs.h +++ b/reactos/drivers/filesystems/ntfs/ntfs.h @@ -86,7 +86,6 @@ typedef struct ULONG Size; } NTFSIDENTIFIER, *PNTFSIDENTIFIER; - typedef struct { NTFSIDENTIFIER Identifier; @@ -101,6 +100,8 @@ typedef struct PDEVICE_OBJECT StorageDevice; PFILE_OBJECT StreamFileObject; + struct _NTFS_ATTR_CONTEXT* MFTContext; + struct _FILE_RECORD_HEADER* MasterFileTable; struct _FCB *VolumeFcb; NTFS_INFO NtfsInfo; @@ -186,9 +187,23 @@ typedef enum AttributeEAInformation = 0xD0, AttributeEA = 0xE0, AttributePropertySet = 0xF0, - AttributeLoggedUtilityStream = 0x100 + AttributeLoggedUtilityStream = 0x100, + AttributeEnd = 0xFFFFFFFF } ATTRIBUTE_TYPE, *PATTRIBUTE_TYPE; +#define NTFS_FILE_MFT 0 +#define NTFS_FILE_MFTMIRR 1 +#define NTFS_FILE_LOGFILE 2 +#define NTFS_FILE_VOLUME 3 +#define NTFS_FILE_ATTRDEF 4 +#define NTFS_FILE_ROOT 5 +#define NTFS_FILE_BITMAP 6 +#define NTFS_FILE_BOOT 7 +#define NTFS_FILE_BADCLUS 8 +#define NTFS_FILE_QUOTA 9 +#define NTFS_FILE_UPCASE 10 +#define NTFS_FILE_EXTEND 11 + #define COLLATION_BINARY 0x00 #define COLLATION_FILE_NAME 0x01 #define COLLATION_UNICODE_STRING 0x02 @@ -200,6 +215,14 @@ typedef enum #define INDEX_ROOT_SMALL 0x0 #define INDEX_ROOT_LARGE 0x1 +#define NTFS_INDEX_ENTRY_NODE 1 +#define NTFS_INDEX_ENTRY_END 2 + +#define NTFS_FILE_NAME_POSIX 0 +#define NTFS_FILE_NAME_WIN32 1 +#define NTFS_FILE_NAME_DOS 2 +#define NTFS_FILE_NAME_WIN32_AND_DOS 3 + typedef struct { ULONG Type; /* Magic number 'FILE' */ @@ -212,7 +235,7 @@ typedef struct #define NRH_FILE_TYPE 0x454C4946 /* 'FILE' */ -typedef struct +typedef struct _FILE_RECORD_HEADER { NTFS_RECORD_HEADER Ntfs; USHORT SequenceNumber; /* Sequence number */ @@ -236,39 +259,38 @@ typedef struct typedef struct { - ATTRIBUTE_TYPE AttributeType; - ULONG Length; - BOOLEAN Nonresident; - UCHAR NameLength; - USHORT NameOffset; - USHORT Flags; - USHORT AttributeNumber; -} ATTRIBUTE, *PATTRIBUTE; - -typedef struct -{ - ATTRIBUTE Attribute; - ULONG ValueLength; - USHORT ValueOffset; - UCHAR Flags; -// UCHAR Padding0; -} RESIDENT_ATTRIBUTE, *PRESIDENT_ATTRIBUTE; - -typedef struct -{ - ATTRIBUTE Attribute; - ULONGLONG StartVcn; // LowVcn - ULONGLONG LastVcn; // HighVcn - USHORT RunArrayOffset; - USHORT CompressionUnit; - ULONG Padding0; - UCHAR IndexedFlag; - ULONGLONG AllocatedSize; - ULONGLONG DataSize; - ULONGLONG InitializedSize; - ULONGLONG CompressedSize; -} NONRESIDENT_ATTRIBUTE, *PNONRESIDENT_ATTRIBUTE; - + ULONG Type; + ULONG Length; + UCHAR IsNonResident; + UCHAR NameLength; + USHORT NameOffset; + USHORT Flags; + USHORT Instance; + union + { + // Resident attributes + struct + { + ULONG ValueLength; + USHORT ValueOffset; + UCHAR Flags; + UCHAR Reserved; + } Resident; + // Non-resident attributes + struct + { + ULONGLONG LowestVCN; + ULONGLONG HighestVCN; + USHORT MappingPairsOffset; + USHORT CompressionUnit; + UCHAR Reserved[4]; + LONGLONG AllocatedSize; + LONGLONG DataSize; + LONGLONG InitializedSize; + LONGLONG CompressedSize; + } NonResident; + }; +} NTFS_ATTR_RECORD, *PNTFS_ATTR_RECORD; typedef struct { @@ -335,6 +357,28 @@ typedef struct INDEX_HEADER_ATTRIBUTE Header; } INDEX_ROOT_ATTRIBUTE, *PINDEX_ROOT_ATTRIBUTE; +typedef struct +{ + union + { + struct + { + ULONGLONG IndexedFile; + } Directory; + struct + { + USHORT DataOffset; + USHORT DataLength; + ULONG Reserved; + } ViewIndex; + } Data; + USHORT Length; + USHORT KeyLength; + USHORT Flags; + USHORT Reserved; + FILENAME_ATTRIBUTE FileName; +} INDEX_ENTRY_ATTRIBUTE, *PINDEX_ENTRY_ATTRIBUTE; + typedef struct { ULONGLONG Unknown1; @@ -357,6 +401,16 @@ typedef struct NTSTATUS SavedExceptionCode; } NTFS_IRP_CONTEXT, *PNTFS_IRP_CONTEXT; +typedef struct _NTFS_ATTR_CONTEXT +{ + PUCHAR CacheRun; + ULONGLONG CacheRunOffset; + LONGLONG CacheRunStartLCN; + ULONGLONG CacheRunLength; + LONGLONG CacheRunLastLCN; + ULONGLONG CacheRunCurrentOffset; + NTFS_ATTR_RECORD Record; +} NTFS_ATTR_CONTEXT, *PNTFS_ATTR_CONTEXT; extern PNTFS_GLOBAL_DATA NtfsGlobalData; @@ -369,15 +423,10 @@ extern PNTFS_GLOBAL_DATA NtfsGlobalData; //VOID //NtfsDumpAttribute(PATTRIBUTE Attribute); -//LONGLONG RunLCN(PUCHAR run); - -//ULONG RunLength(PUCHAR run); - -BOOLEAN -FindRun(PNONRESIDENT_ATTRIBUTE NresAttr, - ULONGLONG vcn, - PULONGLONG lcn, - PULONGLONG count); +PUCHAR +DecodeRun(PUCHAR DataRun, + LONGLONG *DataRunOffset, + ULONGLONG *DataRunLength); VOID NtfsDumpFileAttributes(PFILE_RECORD_HEADER FileRecord); @@ -529,35 +578,30 @@ NtfsFsdFileSystemControl(PDEVICE_OBJECT DeviceObject, /* mft.c */ -NTSTATUS -NtfsOpenMft(PDEVICE_EXTENSION Vcb); - - -VOID -ReadAttribute(PATTRIBUTE attr, - PVOID buffer, - PDEVICE_EXTENSION Vcb, - PDEVICE_OBJECT DeviceObject); - ULONG -AttributeDataLength(PATTRIBUTE attr); +ReadAttribute(PDEVICE_EXTENSION Vcb, + PNTFS_ATTR_CONTEXT Context, + ULONGLONG Offset, + PCHAR Buffer, + ULONG Length); + +ULONGLONG +AttributeDataLength(PNTFS_ATTR_RECORD AttrRecord); ULONG -AttributeAllocatedLength(PATTRIBUTE Attribute); +AttributeAllocatedLength(PNTFS_ATTR_RECORD AttrRecord); NTSTATUS ReadFileRecord(PDEVICE_EXTENSION Vcb, - ULONG index, - PFILE_RECORD_HEADER file, - PFILE_RECORD_HEADER Mft); + ULONGLONG index, + PFILE_RECORD_HEADER file); -PATTRIBUTE -FindAttribute(PFILE_RECORD_HEADER file, - ATTRIBUTE_TYPE type, - PWSTR name); - -ULONG -AttributeLengthAllocated(PATTRIBUTE attr); +NTSTATUS +FindAttribute(PDEVICE_EXTENSION Vcb, + PFILE_RECORD_HEADER MftRecord, + ULONG Type, + PUNICODE_STRING Name, + PNTFS_ATTR_CONTEXT * AttrCtx); VOID ReadVCN(PDEVICE_EXTENSION Vcb, @@ -567,15 +611,9 @@ ReadVCN(PDEVICE_EXTENSION Vcb, ULONG count, PVOID buffer); -VOID -FixupUpdateSequenceArray(PFILE_RECORD_HEADER file); - -VOID -ReadExternalAttribute(PDEVICE_EXTENSION Vcb, - PNONRESIDENT_ATTRIBUTE NresAttr, - ULONGLONG vcn, - ULONG count, - PVOID buffer); +NTSTATUS +FixupUpdateSequenceArray(PDEVICE_EXTENSION Vcb, + PNTFS_RECORD_HEADER Record); NTSTATUS ReadLCN(PDEVICE_EXTENSION Vcb, @@ -588,6 +626,11 @@ EnumerAttribute(PFILE_RECORD_HEADER file, PDEVICE_EXTENSION Vcb, PDEVICE_OBJECT DeviceObject); +NTSTATUS +NtfsLookupFile(PDEVICE_EXTENSION Vcb, + PUNICODE_STRING PathName, + PFILE_RECORD_HEADER *FileRecord, + PNTFS_ATTR_CONTEXT *DataContext); /* misc.c */ -- 2.17.1