[NTFS]
authorPierre Schweitzer <pierre@reactos.org>
Sun, 28 Jun 2015 13:14:07 +0000 (13:14 +0000)
committerPierre Schweitzer <pierre@reactos.org>
Sun, 28 Jun 2015 13:14:07 +0000 (13:14 +0000)
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
reactos/drivers/filesystems/ntfs/fcb.c
reactos/drivers/filesystems/ntfs/fsctl.c
reactos/drivers/filesystems/ntfs/ntfs.h
reactos/drivers/filesystems/ntfs/rw.c

index 4d2c9fc..efb9c81 100644 (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);
index 088a4e7..de4c5f9 100644 (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;
index 1949d96..572fa3d 100644 (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;
index 795e862..c4556e3 100644 (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);
index 65d3a4f..336e107 100644 (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)
+        return Status;
+    }
+
+    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)
         {
-            ExFreePoolWithTag(ReadBuffer, TAG_NTFS);
+            DPRINT1("Not enough memory!\n");
+            ReleaseAttributeContext(DataContext);
+            ExFreePoolWithTag(FileRecord, TAG_NTFS);
+            return STATUS_INSUFFICIENT_RESOURCES;
         }
-        return Status;
+        AllocatedBuffer = TRUE;
     }
 
-    DPRINT1("Effective read: %lu at %lu\n", RealLength, RealReadOffset);
+    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);