From fbff7e0e47c120917b142224af19df0afbb171dc Mon Sep 17 00:00:00 2001 From: Pierre Schweitzer Date: Sun, 28 Jun 2015 13:14:07 +0000 Subject: [PATCH] [NTFS] Implement support for NTFS $DATA streams: - The driver is now able to read various streams for a same file, using the same syntax as Windows. - This fixes to read (in general) files with multiple streams where reading unnamed stream was leading to read beyond file end - Also fix reading small files which are smaller than a sector For demo, see: http://www.heisspiter.net/~Pierre/rostests/NTFS_Streams.png svn path=/trunk/; revision=68302 --- reactos/drivers/filesystems/ntfs/create.c | 2 +- reactos/drivers/filesystems/ntfs/fcb.c | 45 +++++++++++--- reactos/drivers/filesystems/ntfs/fsctl.c | 4 +- reactos/drivers/filesystems/ntfs/ntfs.h | 3 + reactos/drivers/filesystems/ntfs/rw.c | 75 ++++++++++++----------- 5 files changed, 82 insertions(+), 47 deletions(-) diff --git a/reactos/drivers/filesystems/ntfs/create.c b/reactos/drivers/filesystems/ntfs/create.c index 4d2c9fc4af0..efb9c81cde4 100644 --- a/reactos/drivers/filesystems/ntfs/create.c +++ b/reactos/drivers/filesystems/ntfs/create.c @@ -218,7 +218,7 @@ NtfsOpenFileById(PDEVICE_EXTENSION DeviceExt, UNICODE_STRING Name; RtlInitUnicodeString(&Name, MftIdToName[MftId]); - Status = NtfsMakeFCBFromDirEntry(DeviceExt, NULL, &Name, MftRecord, MftId, &FCB); + Status = NtfsMakeFCBFromDirEntry(DeviceExt, NULL, &Name, NULL, MftRecord, MftId, &FCB); if (!NT_SUCCESS(Status)) { ExFreePoolWithTag(MftRecord, TAG_NTFS); diff --git a/reactos/drivers/filesystems/ntfs/fcb.c b/reactos/drivers/filesystems/ntfs/fcb.c index 088a4e740e5..de4c5f9e0a3 100644 --- a/reactos/drivers/filesystems/ntfs/fcb.c +++ b/reactos/drivers/filesystems/ntfs/fcb.c @@ -65,6 +65,7 @@ NtfsWSubString(PWCHAR pTarget, PNTFS_FCB NtfsCreateFCB(PCWSTR FileName, + PCWSTR Stream, PNTFS_VCB Vcb) { PNTFS_FCB Fcb; @@ -93,6 +94,15 @@ NtfsCreateFCB(PCWSTR FileName, } } + if (Stream) + { + wcscpy(Fcb->Stream, Stream); + } + else + { + Fcb->Stream[0] = UNICODE_NULL; + } + ExInitializeResourceLite(&Fcb->MainResource); Fcb->RFCB.Resource = &(Fcb->MainResource); @@ -316,7 +326,7 @@ NtfsMakeRootFCB(PNTFS_VCB Vcb) return NULL; } - Fcb = NtfsCreateFCB(L"\\", Vcb); + Fcb = NtfsCreateFCB(L"\\", NULL, Vcb); if (!Fcb) { ExFreePoolWithTag(MftRecord, TAG_NTFS); @@ -400,18 +410,19 @@ NtfsGetDirEntryName(PDEVICE_EXTENSION DeviceExt, NTSTATUS NtfsMakeFCBFromDirEntry(PNTFS_VCB Vcb, - PNTFS_FCB DirectoryFCB, - PUNICODE_STRING Name, - PFILE_RECORD_HEADER Record, + PNTFS_FCB DirectoryFCB, + PUNICODE_STRING Name, + PCWSTR Stream, + PFILE_RECORD_HEADER Record, ULONGLONG MFTIndex, - PNTFS_FCB * fileFCB) + PNTFS_FCB * fileFCB) { WCHAR pathName[MAX_PATH]; PFILENAME_ATTRIBUTE FileName; PSTANDARD_INFORMATION StdInfo; PNTFS_FCB rcFCB; - DPRINT1("NtfsMakeFCBFromDirEntry(%p, %p, %wZ, %p, %p)\n", Vcb, DirectoryFCB, Name, Record, fileFCB); + DPRINT1("NtfsMakeFCBFromDirEntry(%p, %p, %wZ, %p, %p, %p)\n", Vcb, DirectoryFCB, Name, Stream, Record, fileFCB); FileName = GetBestFileNameFromRecord(Record); if (!FileName) @@ -440,7 +451,7 @@ NtfsMakeFCBFromDirEntry(PNTFS_VCB Vcb, pathName[FileName->NameLength] = UNICODE_NULL; } - rcFCB = NtfsCreateFCB(pathName, Vcb); + rcFCB = NtfsCreateFCB(pathName, Stream, Vcb); if (!rcFCB) { return STATUS_INSUFFICIENT_RESOURCES; @@ -531,6 +542,8 @@ NtfsDirFindFile(PNTFS_VCB Vcb, UNICODE_STRING File; PFILE_RECORD_HEADER FileRecord; ULONGLONG MFTIndex; + PWSTR Colon; + USHORT Length = 0; DPRINT1("NtfsDirFindFile(%p, %p, %S, %p)\n", Vcb, DirectoryFcb, FileToFind, FoundFCB); @@ -538,13 +551,29 @@ NtfsDirFindFile(PNTFS_VCB Vcb, RtlInitUnicodeString(&File, FileToFind); CurrentDir = DirectoryFcb->MFTIndex; + Colon = wcsrchr(FileToFind, L':'); + if (Colon != NULL) + { + Length = File.Length; + File.Length = (Colon - FileToFind) * sizeof(WCHAR); + + /* Skip colon */ + ++Colon; + DPRINT1("Will now look for file '%wZ' with stream '%S'\n", &File, Colon); + } + Status = NtfsLookupFileAt(Vcb, &File, &FileRecord, &MFTIndex, CurrentDir); if (!NT_SUCCESS(Status)) { return Status; } - Status = NtfsMakeFCBFromDirEntry(Vcb, DirectoryFcb, &File, FileRecord, MFTIndex, FoundFCB); + if (Length != 0) + { + File.Length = Length; + } + + Status = NtfsMakeFCBFromDirEntry(Vcb, DirectoryFcb, &File, Colon, FileRecord, MFTIndex, FoundFCB); ExFreePoolWithTag(FileRecord, TAG_NTFS); return Status; diff --git a/reactos/drivers/filesystems/ntfs/fsctl.c b/reactos/drivers/filesystems/ntfs/fsctl.c index 1949d96b88e..572fa3d3231 100644 --- a/reactos/drivers/filesystems/ntfs/fsctl.c +++ b/reactos/drivers/filesystems/ntfs/fsctl.c @@ -352,7 +352,7 @@ NtfsGetVolumeData(PDEVICE_OBJECT DeviceObject, VolumeNameU = L"\0"; } - VolumeFcb = NtfsCreateFCB(VolumeNameU, DeviceExt); + VolumeFcb = NtfsCreateFCB(VolumeNameU, NULL, DeviceExt); if (VolumeFcb == NULL) { DPRINT1("Failed allocating volume FCB\n"); @@ -456,7 +456,7 @@ NtfsMountVolume(PDEVICE_OBJECT DeviceObject, InitializeListHead(&Vcb->FcbListHead); - Fcb = NtfsCreateFCB(NULL, Vcb); + Fcb = NtfsCreateFCB(NULL, NULL, Vcb); if (Fcb == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; diff --git a/reactos/drivers/filesystems/ntfs/ntfs.h b/reactos/drivers/filesystems/ntfs/ntfs.h index 795e86209c5..c4556e3fa29 100644 --- a/reactos/drivers/filesystems/ntfs/ntfs.h +++ b/reactos/drivers/filesystems/ntfs/ntfs.h @@ -440,6 +440,7 @@ typedef struct _FCB PFILE_OBJECT FileObject; PNTFS_VCB Vcb; + WCHAR Stream[MAX_PATH]; WCHAR *ObjectName; /* point on filename (250 chars max) in PathName */ WCHAR PathName[MAX_PATH]; /* path+filename 260 max */ @@ -586,6 +587,7 @@ FAST_IO_WRITE NtfsFastIoWrite; PNTFS_FCB NtfsCreateFCB(PCWSTR FileName, + PCWSTR Stream, PNTFS_VCB Vcb); VOID @@ -649,6 +651,7 @@ NTSTATUS NtfsMakeFCBFromDirEntry(PNTFS_VCB Vcb, PNTFS_FCB DirectoryFCB, PUNICODE_STRING Name, + PCWSTR Stream, PFILE_RECORD_HEADER Record, ULONGLONG MFTIndex, PNTFS_FCB * fileFCB); diff --git a/reactos/drivers/filesystems/ntfs/rw.c b/reactos/drivers/filesystems/ntfs/rw.c index 65d3a4f5eb8..336e107d666 100644 --- a/reactos/drivers/filesystems/ntfs/rw.c +++ b/reactos/drivers/filesystems/ntfs/rw.c @@ -57,6 +57,7 @@ NtfsReadFile(PDEVICE_EXTENSION DeviceExt, ULONG ToRead; BOOLEAN AllocatedBuffer = FALSE; PCHAR ReadBuffer = (PCHAR)Buffer; + ULONGLONG StreamSize; DPRINT1("NtfsReadFile(%p, %p, %p, %u, %u, %x, %p)\n", DeviceExt, FileObject, Buffer, Length, ReadOffset, IrpFlags, LengthRead); @@ -70,33 +71,6 @@ NtfsReadFile(PDEVICE_EXTENSION DeviceExt, Fcb = (PNTFS_FCB)FileObject->FsContext; - if (ReadOffset >= Fcb->Entry.AllocatedSize) - { - DPRINT1("Reading beyond file end!\n"); - return STATUS_END_OF_FILE; - } - - ToRead = Length; - if (ReadOffset + Length > Fcb->Entry.AllocatedSize) - ToRead = Fcb->Entry.AllocatedSize - ReadOffset; - - RealReadOffset = ReadOffset; - RealLength = ToRead; - - if ((ReadOffset % DeviceExt->NtfsInfo.BytesPerSector) != 0 || (ToRead % DeviceExt->NtfsInfo.BytesPerSector) != 0) - { - RealReadOffset = ROUND_DOWN(ReadOffset, DeviceExt->NtfsInfo.BytesPerSector); - RealLength = ROUND_UP(ToRead, DeviceExt->NtfsInfo.BytesPerSector); - - ReadBuffer = ExAllocatePoolWithTag(NonPagedPool, RealLength + DeviceExt->NtfsInfo.BytesPerSector, TAG_NTFS); - if (ReadBuffer == NULL) - { - DPRINT1("Not enough memory!\n"); - return STATUS_INSUFFICIENT_RESOURCES; - } - AllocatedBuffer = TRUE; - } - FileRecord = ExAllocatePoolWithTag(NonPagedPool, DeviceExt->NtfsInfo.BytesPerFileRecord, TAG_NTFS); if (FileRecord == NULL) { @@ -120,12 +94,13 @@ NtfsReadFile(PDEVICE_EXTENSION DeviceExt, return Status; } - Status = FindAttribute(DeviceExt, FileRecord, AttributeData, L"", 0, &DataContext); + + Status = FindAttribute(DeviceExt, FileRecord, AttributeData, Fcb->Stream, wcslen(Fcb->Stream), &DataContext); if (!NT_SUCCESS(Status)) { PNTFS_ATTR_RECORD Attribute; - DPRINT1("No unnamed data stream associated with file!\n"); + DPRINT1("No '%S' data stream associated with file!\n", Fcb->Stream); Attribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord + FileRecord->AttributeOffset); while (Attribute < (PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord + FileRecord->BytesInUse) && @@ -135,7 +110,6 @@ NtfsReadFile(PDEVICE_EXTENSION DeviceExt, { UNICODE_STRING Name; - ASSERT(Attribute->NameLength != 0); Name.Length = Attribute->NameLength * sizeof(WCHAR); Name.MaximumLength = Name.Length; Name.Buffer = (PWCHAR)((ULONG_PTR)Attribute + Attribute->NameOffset); @@ -145,17 +119,46 @@ NtfsReadFile(PDEVICE_EXTENSION DeviceExt, Attribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)Attribute + Attribute->Length); } + ReleaseAttributeContext(DataContext); ExFreePoolWithTag(FileRecord, TAG_NTFS); - if (AllocatedBuffer) - { - ExFreePoolWithTag(ReadBuffer, TAG_NTFS); - } return Status; } - DPRINT1("Effective read: %lu at %lu\n", RealLength, RealReadOffset); + StreamSize = AttributeDataLength(&DataContext->Record); + if (ReadOffset >= StreamSize) + { + DPRINT1("Reading beyond stream end!\n"); + ReleaseAttributeContext(DataContext); + ExFreePoolWithTag(FileRecord, TAG_NTFS); + return STATUS_END_OF_FILE; + } + + ToRead = Length; + if (ReadOffset + Length > StreamSize) + ToRead = StreamSize - ReadOffset; + + RealReadOffset = ReadOffset; + RealLength = ToRead; + + if ((ReadOffset % DeviceExt->NtfsInfo.BytesPerSector) != 0 || (ToRead % DeviceExt->NtfsInfo.BytesPerSector) != 0) + { + RealReadOffset = ROUND_DOWN(ReadOffset, DeviceExt->NtfsInfo.BytesPerSector); + RealLength = ROUND_UP(ToRead, DeviceExt->NtfsInfo.BytesPerSector); + + ReadBuffer = ExAllocatePoolWithTag(NonPagedPool, RealLength + DeviceExt->NtfsInfo.BytesPerSector, TAG_NTFS); + if (ReadBuffer == NULL) + { + DPRINT1("Not enough memory!\n"); + ReleaseAttributeContext(DataContext); + ExFreePoolWithTag(FileRecord, TAG_NTFS); + return STATUS_INSUFFICIENT_RESOURCES; + } + AllocatedBuffer = TRUE; + } + + DPRINT1("Effective read: %lu at %lu for stream '%S'\n", RealLength, RealReadOffset, Fcb->Stream); RealLengthRead = ReadAttribute(DeviceExt, DataContext, RealReadOffset, (PCHAR)ReadBuffer, RealLength); - if (RealLengthRead != RealLength) + if (RealLengthRead == 0) { DPRINT1("Read failure!\n"); ReleaseAttributeContext(DataContext);