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
This commit is contained in:
Pierre Schweitzer 2015-06-28 13:14:07 +00:00
parent ec8ef644e3
commit fbff7e0e47
5 changed files with 82 additions and 47 deletions

View file

@ -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);

View file

@ -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;

View file

@ -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;

View file

@ -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);

View file

@ -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);