[NTFS] - In the NtfsAddFilenameToDirectory() function, rename DirectoryContext parame...
[reactos.git] / drivers / filesystems / ntfs / blockdev.c
index fd18c41..1ae67e5 100644 (file)
@@ -20,7 +20,8 @@
  * PROJECT:          ReactOS kernel
  * FILE:             drivers/filesystem/ntfs/blockdev.c
  * PURPOSE:          NTFS filesystem driver
- * PROGRAMMER:       Eric Kohl
+ * PROGRAMMERS:      Eric Kohl
+ *                   Trevor Thompson
  */
 
 /* INCLUDES *****************************************************************/
 #define NDEBUG
 #include <debug.h>
 
-/* GLOBALS *****************************************************************/
+/* FUNCTIONS ****************************************************************/
 
+NTSTATUS
+NtfsReadDisk(IN PDEVICE_OBJECT DeviceObject,
+             IN LONGLONG StartingOffset,
+             IN ULONG Length,
+             IN ULONG SectorSize,
+             IN OUT PUCHAR Buffer,
+             IN BOOLEAN Override)
+{
+    PIO_STACK_LOCATION Stack;
+    IO_STATUS_BLOCK IoStatus;
+    LARGE_INTEGER Offset;
+    KEVENT Event;
+    PIRP Irp;
+    NTSTATUS Status;
+    ULONGLONG RealReadOffset;
+    ULONG RealLength;
+    BOOLEAN AllocatedBuffer = FALSE;
+    PUCHAR ReadBuffer = Buffer;
 
-/* FUNCTIONS ****************************************************************/
+    DPRINT("NtfsReadDisk(%p, %I64x, %u, %u, %p, %d)\n", DeviceObject, StartingOffset, Length, SectorSize, Buffer, Override);
+
+    KeInitializeEvent(&Event,
+                      NotificationEvent,
+                      FALSE);
 
+    RealReadOffset = (ULONGLONG)StartingOffset;
+    RealLength = Length;
+
+    if ((RealReadOffset % SectorSize) != 0 || (RealLength % SectorSize) != 0)
+    {
+        RealReadOffset = ROUND_DOWN(StartingOffset, SectorSize);
+        RealLength = ROUND_UP(Length, SectorSize);
+
+        ReadBuffer = ExAllocatePoolWithTag(NonPagedPool, RealLength + SectorSize, TAG_NTFS);
+        if (ReadBuffer == NULL)
+        {
+            DPRINT1("Not enough memory!\n");
+            return STATUS_INSUFFICIENT_RESOURCES;
+        }
+        AllocatedBuffer = TRUE;
+    }
+
+    Offset.QuadPart = RealReadOffset;
+
+    DPRINT("Building synchronous FSD Request...\n");
+    Irp = IoBuildSynchronousFsdRequest(IRP_MJ_READ,
+                                       DeviceObject,
+                                       ReadBuffer,
+                                       RealLength,
+                                       &Offset,
+                                       &Event,
+                                       &IoStatus);
+    if (Irp == NULL)
+    {
+        DPRINT("IoBuildSynchronousFsdRequest failed\n");
+
+        if (AllocatedBuffer)
+        {
+            ExFreePoolWithTag(ReadBuffer, TAG_NTFS);
+        }
+
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+
+    if (Override)
+    {
+        Stack = IoGetNextIrpStackLocation(Irp);
+        Stack->Flags |= SL_OVERRIDE_VERIFY_VOLUME;
+    }
+
+    DPRINT("Calling IO Driver... with irp %p\n", Irp);
+    Status = IoCallDriver(DeviceObject, Irp);
+
+    DPRINT("Waiting for IO Operation for %p\n", Irp);
+    if (Status == STATUS_PENDING)
+    {
+        DPRINT("Operation pending\n");
+        KeWaitForSingleObject(&Event, Suspended, KernelMode, FALSE, NULL);
+        DPRINT("Getting IO Status... for %p\n", Irp);
+        Status = IoStatus.Status;
+    }
+
+    if (AllocatedBuffer)
+    {
+        if (NT_SUCCESS(Status))
+        {
+            RtlCopyMemory(Buffer, ReadBuffer + (StartingOffset - RealReadOffset), Length);
+        }
+
+        ExFreePoolWithTag(ReadBuffer, TAG_NTFS);
+    }
+
+    DPRINT("NtfsReadDisk() done (Status %x)\n", Status);
+
+    return Status;
+}
+
+/**
+* @name NtfsWriteDisk
+* @implemented
+*
+* Writes data from the given buffer to the given DeviceObject.
+*
+* @param DeviceObject
+* Device to write to
+*
+* @param StartingOffset
+* Offset, in bytes, from the start of the device object where the data will be written
+*
+* @param Length
+* How much data will be written, in bytes
+*
+* @param SectorSize
+* Size of the sector on the disk that the write must be aligned to
+*
+* @param Buffer
+* The data that's being written to the device
+*
+* @return
+* STATUS_SUCCESS in case of success, STATUS_INSUFFICIENT_RESOURCES if a memory allocation failed,
+* or whatever status IoCallDriver() sets.
+*
+* @remarks Called by NtfsWriteFile(). May perform a read-modify-write operation if the
+* requested write is not sector-aligned.
+*
+*/
 NTSTATUS
-NtfsReadSectors(IN PDEVICE_OBJECT DeviceObject,
-               IN ULONG DiskSector,
-               IN ULONG SectorCount,
-               IN ULONG SectorSize,
-               IN OUT PUCHAR Buffer,
-               IN BOOLEAN Override)
+NtfsWriteDisk(IN PDEVICE_OBJECT DeviceObject,
+              IN LONGLONG StartingOffset,
+              IN ULONG Length,
+              IN ULONG SectorSize,
+              IN const PUCHAR Buffer)
 {
-  PIO_STACK_LOCATION Stack;
-  IO_STATUS_BLOCK IoStatus;
-  LARGE_INTEGER Offset;
-  ULONG BlockSize;
-  KEVENT Event;
-  PIRP Irp;
-  NTSTATUS Status;
-
-  KeInitializeEvent(&Event,
-                   NotificationEvent,
-                   FALSE);
-
-  Offset.QuadPart = (LONGLONG)DiskSector * (LONGLONG)SectorSize;
-  BlockSize = SectorCount * SectorSize;
-
-  DPRINT("NtfsReadSectors(DeviceObject %p, DiskSector %d, Buffer %p)\n",
-        DeviceObject, DiskSector, Buffer);
-  DPRINT("Offset %I64x BlockSize %ld\n",
-        Offset.QuadPart,
-        BlockSize);
-
-  DPRINT("Building synchronous FSD Request...\n");
-  Irp = IoBuildSynchronousFsdRequest(IRP_MJ_READ,
-                                    DeviceObject,
-                                    Buffer,
-                                    BlockSize,
-                                    &Offset,
-                                    &Event,
-                                    &IoStatus);
-  if (Irp == NULL)
+    IO_STATUS_BLOCK IoStatus;
+    LARGE_INTEGER Offset;
+    KEVENT Event;
+    PIRP Irp;
+    NTSTATUS Status;
+    ULONGLONG RealWriteOffset;
+    ULONG RealLength;
+    BOOLEAN AllocatedBuffer = FALSE;
+    PUCHAR TempBuffer = NULL;
+
+    DPRINT("NtfsWriteDisk(%p, %I64x, %u, %u, %p)\n", DeviceObject, StartingOffset, Length, SectorSize, Buffer);
+
+    if (Length == 0)
+        return STATUS_SUCCESS;
+
+    RealWriteOffset = (ULONGLONG)StartingOffset;
+    RealLength = Length;
+
+    // Does the write need to be adjusted to be sector-aligned?
+    if ((RealWriteOffset % SectorSize) != 0 || (RealLength % SectorSize) != 0)
     {
-      DPRINT("IoBuildSynchronousFsdRequest failed\n");
-      return STATUS_INSUFFICIENT_RESOURCES;
+        ULONGLONG relativeOffset;
+
+        // We need to do a read-modify-write. We'll start be copying the entire 
+        // contents of every sector that will be overwritten. 
+        // TODO: Optimize (read no more than necessary)
+
+        RealWriteOffset = ROUND_DOWN(StartingOffset, SectorSize);
+        RealLength = ROUND_UP(Length, SectorSize);
+
+        // Would the end of our sector-aligned write fall short of the requested write?
+        if (RealWriteOffset + RealLength < StartingOffset + Length)
+        {
+            RealLength += SectorSize;
+        }
+
+        // Did we underestimate the memory required somehow?
+        if (RealLength + RealWriteOffset < StartingOffset + Length)
+        {
+            DPRINT1("\a\t\t\t\t\tFIXME: calculated less memory than needed!\n");
+            DPRINT1("StartingOffset: %lu\tLength: %lu\tRealWriteOffset: %lu\tRealLength: %lu\n",
+                    StartingOffset, Length, RealWriteOffset, RealLength);
+
+            RealLength += SectorSize;
+        }
+
+        // Allocate a buffer to copy the existing data to
+        TempBuffer = ExAllocatePoolWithTag(NonPagedPool, RealLength, TAG_NTFS);
+
+        // Did we fail to allocate it?
+        if (TempBuffer == NULL)
+        {
+            DPRINT1("Not enough memory!\n");
+
+            return STATUS_INSUFFICIENT_RESOURCES;
+        }
+
+        // Read the sectors we'll be overwriting into TempBuffer
+        Status = NtfsReadDisk(DeviceObject, RealWriteOffset, RealLength, SectorSize, TempBuffer, FALSE);
+
+        // Did we fail the read?
+        if (!NT_SUCCESS(Status))
+        {
+            RtlSecureZeroMemory(TempBuffer, RealLength);
+            ExFreePoolWithTag(TempBuffer, TAG_NTFS);
+            return Status;
+        }
+
+        // Calculate where the new data should be written to, relative to the start of TempBuffer
+        relativeOffset = StartingOffset - RealWriteOffset;
+
+        // Modify the tempbuffer with the data being read
+        RtlCopyMemory(TempBuffer + relativeOffset, Buffer, Length);
+
+        AllocatedBuffer = TRUE;
     }
 
-  if (Override)
+    // set the destination offset
+    Offset.QuadPart = RealWriteOffset;
+
+    // setup the notification event for the write
+    KeInitializeEvent(&Event,
+                      NotificationEvent,
+                      FALSE);
+
+    DPRINT("Building synchronous FSD Request...\n");
+
+    // Build an IRP requesting the lower-level [disk] driver to perform the write
+    // TODO: Forward the existing IRP instead
+    Irp = IoBuildSynchronousFsdRequest(IRP_MJ_WRITE,
+                                       DeviceObject,
+                                       // if we allocated a temp buffer, use that instead of the Buffer parameter                               
+                                       ((AllocatedBuffer) ? TempBuffer : Buffer),
+                                       RealLength,
+                                       &Offset,
+                                       &Event,
+                                       &IoStatus);
+    // Did we fail to build the IRP?
+    if (Irp == NULL)
     {
-      Stack = IoGetNextIrpStackLocation(Irp);
-      Stack->Flags |= SL_OVERRIDE_VERIFY_VOLUME;
+        DPRINT1("IoBuildSynchronousFsdRequest failed\n");
+
+        if (AllocatedBuffer)
+        {
+            RtlSecureZeroMemory(TempBuffer, RealLength);
+            ExFreePoolWithTag(TempBuffer, TAG_NTFS);
+        }
+
+        return STATUS_INSUFFICIENT_RESOURCES;
     }
 
-  DPRINT("Calling IO Driver... with irp %p\n", Irp);
-  Status = IoCallDriver(DeviceObject, Irp);
+    // Call the next-lower driver to perform the write
+    DPRINT("Calling IO Driver with irp %p\n", Irp);
+    Status = IoCallDriver(DeviceObject, Irp);
+
+    // Wait until the next-lower driver has completed the IRP
+    DPRINT("Waiting for IO Operation for %p\n", Irp);
+    if (Status == STATUS_PENDING)
+    {
+        DPRINT("Operation pending\n");
+        KeWaitForSingleObject(&Event, Suspended, KernelMode, FALSE, NULL);
+        DPRINT("Getting IO Status... for %p\n", Irp);
+        Status = IoStatus.Status;
+    }
 
-  DPRINT("Waiting for IO Operation for %p\n", Irp);
-  if (Status == STATUS_PENDING)
+    if (AllocatedBuffer)
     {
-      DPRINT("Operation pending\n");
-      KeWaitForSingleObject(&Event, Suspended, KernelMode, FALSE, NULL);
-      DPRINT("Getting IO Status... for %p\n", Irp);
-      Status = IoStatus.Status;
+        // zero the buffer before freeing it, so private user data can't be snooped
+        RtlSecureZeroMemory(TempBuffer, RealLength);
+
+        ExFreePoolWithTag(TempBuffer, TAG_NTFS);
     }
 
-  DPRINT("NtfsReadSectors() done (Status %x)\n", Status);
+    DPRINT("NtfsWriteDisk() done (Status %x)\n", Status);
+
+    return Status;
+}
+
+NTSTATUS
+NtfsReadSectors(IN PDEVICE_OBJECT DeviceObject,
+                IN ULONG DiskSector,
+                IN ULONG SectorCount,
+                IN ULONG SectorSize,
+                IN OUT PUCHAR Buffer,
+                IN BOOLEAN Override)
+{
+    LONGLONG Offset;
+    ULONG BlockSize;
+
+    Offset = (LONGLONG)DiskSector * (LONGLONG)SectorSize;
+    BlockSize = SectorCount * SectorSize;
 
-  return Status;
+    return NtfsReadDisk(DeviceObject, Offset, BlockSize, SectorSize, Buffer, Override);
 }
 
 
 NTSTATUS
 NtfsDeviceIoControl(IN PDEVICE_OBJECT DeviceObject,
-                   IN ULONG ControlCode,
-                   IN PVOID InputBuffer,
-                   IN ULONG InputBufferSize,
-                   IN OUT PVOID OutputBuffer,
-                   IN OUT PULONG OutputBufferSize,
-                   IN BOOLEAN Override)
+                    IN ULONG ControlCode,
+                    IN PVOID InputBuffer,
+                    IN ULONG InputBufferSize,
+                    IN OUT PVOID OutputBuffer,
+                    IN OUT PULONG OutputBufferSize,
+                    IN BOOLEAN Override)
 {
-  PIO_STACK_LOCATION Stack;
-  IO_STATUS_BLOCK IoStatus;
-  KEVENT Event;
-  PIRP Irp;
-  NTSTATUS Status;
-
-  KeInitializeEvent(&Event, NotificationEvent, FALSE);
-
-  DPRINT("Building device I/O control request ...\n");
-  Irp = IoBuildDeviceIoControlRequest(ControlCode,
-                                     DeviceObject,
-                                     InputBuffer,
-                                     InputBufferSize,
-                                     OutputBuffer,
-                                     (OutputBufferSize) ? *OutputBufferSize : 0,
-                                     FALSE,
-                                     &Event,
-                                     &IoStatus);
-  if (Irp == NULL)
+    PIO_STACK_LOCATION Stack;
+    IO_STATUS_BLOCK IoStatus;
+    KEVENT Event;
+    PIRP Irp;
+    NTSTATUS Status;
+
+    KeInitializeEvent(&Event, NotificationEvent, FALSE);
+
+    DPRINT("Building device I/O control request ...\n");
+    Irp = IoBuildDeviceIoControlRequest(ControlCode,
+                                        DeviceObject,
+                                        InputBuffer,
+                                        InputBufferSize,
+                                        OutputBuffer,
+                                        (OutputBufferSize) ? *OutputBufferSize : 0,
+                                        FALSE,
+                                        &Event,
+                                        &IoStatus);
+    if (Irp == NULL)
     {
-      DPRINT("IoBuildDeviceIoControlRequest() failed\n");
-      return STATUS_INSUFFICIENT_RESOURCES;
+        DPRINT("IoBuildDeviceIoControlRequest() failed\n");
+        return STATUS_INSUFFICIENT_RESOURCES;
     }
 
-  if (Override)
+    if (Override)
     {
-      Stack = IoGetNextIrpStackLocation(Irp);
-      Stack->Flags |= SL_OVERRIDE_VERIFY_VOLUME;
+        Stack = IoGetNextIrpStackLocation(Irp);
+        Stack->Flags |= SL_OVERRIDE_VERIFY_VOLUME;
     }
 
-  DPRINT("Calling IO Driver... with irp %p\n", Irp);
-  Status = IoCallDriver(DeviceObject, Irp);
-  if (Status == STATUS_PENDING)
+    DPRINT("Calling IO Driver... with irp %p\n", Irp);
+    Status = IoCallDriver(DeviceObject, Irp);
+    if (Status == STATUS_PENDING)
     {
-      KeWaitForSingleObject(&Event, Suspended, KernelMode, FALSE, NULL);
-      Status = IoStatus.Status;
+        KeWaitForSingleObject(&Event, Suspended, KernelMode, FALSE, NULL);
+        Status = IoStatus.Status;
     }
 
-  if (OutputBufferSize)
+    if (OutputBufferSize)
     {
-      *OutputBufferSize = IoStatus.Information;
+        *OutputBufferSize = IoStatus.Information;
     }
 
-  return Status;
+    return Status;
 }
 
 /* EOF */