reactos/drivers/filesystems/ntfs/rw.c

720 lines
23 KiB
C

/*
* ReactOS kernel
* Copyright (C) 2002, 2014 ReactOS Team
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS kernel
* FILE: drivers/filesystem/ntfs/rw.c
* PURPOSE: NTFS filesystem driver
* PROGRAMMERS: Art Yerkes
* Pierre Schweitzer (pierre@reactos.org)
* Trevor Thompson
*/
/* INCLUDES *****************************************************************/
#include <ntddk.h>
#include "ntfs.h"
#define NDEBUG
#include <debug.h>
/* FUNCTIONS ****************************************************************/
/*
* FUNCTION: Reads data from a file
*/
static
NTSTATUS
NtfsReadFile(PDEVICE_EXTENSION DeviceExt,
PFILE_OBJECT FileObject,
PUCHAR Buffer,
ULONG Length,
ULONG ReadOffset,
ULONG IrpFlags,
PULONG LengthRead)
{
NTSTATUS Status = STATUS_SUCCESS;
PNTFS_FCB Fcb;
PFILE_RECORD_HEADER FileRecord;
PNTFS_ATTR_CONTEXT DataContext;
ULONG RealLength;
ULONG RealReadOffset;
ULONG RealLengthRead;
ULONG ToRead;
BOOLEAN AllocatedBuffer = FALSE;
PCHAR ReadBuffer = (PCHAR)Buffer;
ULONGLONG StreamSize;
DPRINT("NtfsReadFile(%p, %p, %p, %lu, %lu, %lx, %p)\n", DeviceExt, FileObject, Buffer, Length, ReadOffset, IrpFlags, LengthRead);
*LengthRead = 0;
if (Length == 0)
{
DPRINT1("Null read!\n");
return STATUS_SUCCESS;
}
Fcb = (PNTFS_FCB)FileObject->FsContext;
if (NtfsFCBIsCompressed(Fcb))
{
DPRINT1("Compressed file!\n");
UNIMPLEMENTED;
return STATUS_NOT_IMPLEMENTED;
}
FileRecord = ExAllocateFromNPagedLookasideList(&DeviceExt->FileRecLookasideList);
if (FileRecord == NULL)
{
DPRINT1("Not enough memory!\n");
return STATUS_INSUFFICIENT_RESOURCES;
}
Status = ReadFileRecord(DeviceExt, Fcb->MFTIndex, FileRecord);
if (!NT_SUCCESS(Status))
{
DPRINT1("Can't find record!\n");
ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, FileRecord);
return Status;
}
Status = FindAttribute(DeviceExt, FileRecord, AttributeData, Fcb->Stream, wcslen(Fcb->Stream), &DataContext, NULL);
if (!NT_SUCCESS(Status))
{
NTSTATUS BrowseStatus;
FIND_ATTR_CONTXT Context;
PNTFS_ATTR_RECORD Attribute;
DPRINT1("No '%S' data stream associated with file!\n", Fcb->Stream);
BrowseStatus = FindFirstAttribute(&Context, DeviceExt, FileRecord, FALSE, &Attribute);
while (NT_SUCCESS(BrowseStatus))
{
if (Attribute->Type == AttributeData)
{
UNICODE_STRING Name;
Name.Length = Attribute->NameLength * sizeof(WCHAR);
Name.MaximumLength = Name.Length;
Name.Buffer = (PWCHAR)((ULONG_PTR)Attribute + Attribute->NameOffset);
DPRINT1("Data stream: '%wZ' available\n", &Name);
}
BrowseStatus = FindNextAttribute(&Context, &Attribute);
}
FindCloseAttribute(&Context);
ReleaseAttributeContext(DataContext);
ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, FileRecord);
return Status;
}
StreamSize = AttributeDataLength(DataContext->pRecord);
if (ReadOffset >= StreamSize)
{
DPRINT1("Reading beyond stream end!\n");
ReleaseAttributeContext(DataContext);
ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, FileRecord);
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);
/* do we need to extend RealLength by one sector? */
if (RealLength + RealReadOffset < ReadOffset + Length)
{
if (RealReadOffset + RealLength + DeviceExt->NtfsInfo.BytesPerSector <= AttributeAllocatedLength(DataContext->pRecord))
RealLength += DeviceExt->NtfsInfo.BytesPerSector;
}
ReadBuffer = ExAllocatePoolWithTag(NonPagedPool, RealLength, TAG_NTFS);
if (ReadBuffer == NULL)
{
DPRINT1("Not enough memory!\n");
ReleaseAttributeContext(DataContext);
ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, FileRecord);
return STATUS_INSUFFICIENT_RESOURCES;
}
AllocatedBuffer = TRUE;
}
DPRINT("Effective read: %lu at %lu for stream '%S'\n", RealLength, RealReadOffset, Fcb->Stream);
RealLengthRead = ReadAttribute(DeviceExt, DataContext, RealReadOffset, (PCHAR)ReadBuffer, RealLength);
if (RealLengthRead == 0)
{
DPRINT1("Read failure!\n");
ReleaseAttributeContext(DataContext);
ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, FileRecord);
if (AllocatedBuffer)
{
ExFreePoolWithTag(ReadBuffer, TAG_NTFS);
}
return Status;
}
ReleaseAttributeContext(DataContext);
ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, FileRecord);
*LengthRead = ToRead;
DPRINT("%lu got read\n", *LengthRead);
if (AllocatedBuffer)
{
RtlCopyMemory(Buffer, ReadBuffer + (ReadOffset - RealReadOffset), ToRead);
}
if (ToRead != Length)
{
RtlZeroMemory(Buffer + ToRead, Length - ToRead);
}
if (AllocatedBuffer)
{
ExFreePoolWithTag(ReadBuffer, TAG_NTFS);
}
return STATUS_SUCCESS;
}
NTSTATUS
NtfsRead(PNTFS_IRP_CONTEXT IrpContext)
{
PDEVICE_EXTENSION DeviceExt;
PIO_STACK_LOCATION Stack;
PFILE_OBJECT FileObject;
PVOID Buffer;
ULONG ReadLength;
LARGE_INTEGER ReadOffset;
ULONG ReturnedReadLength = 0;
NTSTATUS Status = STATUS_SUCCESS;
PIRP Irp;
PDEVICE_OBJECT DeviceObject;
DPRINT("NtfsRead(IrpContext %p)\n", IrpContext);
DeviceObject = IrpContext->DeviceObject;
Irp = IrpContext->Irp;
Stack = IrpContext->Stack;
FileObject = IrpContext->FileObject;
DeviceExt = DeviceObject->DeviceExtension;
ReadLength = Stack->Parameters.Read.Length;
ReadOffset = Stack->Parameters.Read.ByteOffset;
Buffer = NtfsGetUserBuffer(Irp, BooleanFlagOn(Irp->Flags, IRP_PAGING_IO));
Status = NtfsReadFile(DeviceExt,
FileObject,
Buffer,
ReadLength,
ReadOffset.u.LowPart,
Irp->Flags,
&ReturnedReadLength);
if (NT_SUCCESS(Status))
{
if (FileObject->Flags & FO_SYNCHRONOUS_IO)
{
FileObject->CurrentByteOffset.QuadPart =
ReadOffset.QuadPart + ReturnedReadLength;
}
Irp->IoStatus.Information = ReturnedReadLength;
}
else
{
Irp->IoStatus.Information = 0;
}
return Status;
}
/**
* @name NtfsWriteFile
* @implemented
*
* Writes a file to the disk. It presently borrows a lot of code from NtfsReadFile() and
* VFatWriteFileData(). It needs some more work before it will be complete; it won't handle
* page files, asnyc io, cached writes, etc.
*
* @param DeviceExt
* Points to the target disk's DEVICE_EXTENSION
*
* @param FileObject
* Pointer to a FILE_OBJECT describing the target file
*
* @param Buffer
* The data that's being written to the file
*
* @Param Length
* The size of the data buffer being written, in bytes
*
* @param WriteOffset
* Offset, in bytes, from the beginning of the file. Indicates where to start
* writing data.
*
* @param IrpFlags
* TODO: flags are presently ignored in code.
*
* @param CaseSensitive
* Boolean indicating if the function should operate in case-sensitive mode. This will be TRUE
* if an application opened the file with the FILE_FLAG_POSIX_SEMANTICS flag.
*
* @param LengthWritten
* Pointer to a ULONG. This ULONG will be set to the number of bytes successfully written.
*
* @return
* STATUS_SUCCESS if successful, STATUS_NOT_IMPLEMENTED if a required feature isn't implemented,
* STATUS_INSUFFICIENT_RESOURCES if an allocation failed, STATUS_ACCESS_DENIED if the write itself fails,
* STATUS_PARTIAL_COPY or STATUS_UNSUCCESSFUL if ReadFileRecord() fails, or
* STATUS_OBJECT_NAME_NOT_FOUND if the file's data stream could not be found.
*
* @remarks Called by NtfsWrite(). It may perform a read-modify-write operation if the requested write is
* not sector-aligned. LengthWritten only refers to how much of the requested data has been written;
* extra data that needs to be written to make the write sector-aligned will not affect it.
*
*/
NTSTATUS NtfsWriteFile(PDEVICE_EXTENSION DeviceExt,
PFILE_OBJECT FileObject,
const PUCHAR Buffer,
ULONG Length,
ULONG WriteOffset,
ULONG IrpFlags,
BOOLEAN CaseSensitive,
PULONG LengthWritten)
{
NTSTATUS Status = STATUS_NOT_IMPLEMENTED;
PNTFS_FCB Fcb;
PFILE_RECORD_HEADER FileRecord;
PNTFS_ATTR_CONTEXT DataContext;
ULONG AttributeOffset;
ULONGLONG StreamSize;
DPRINT("NtfsWriteFile(%p, %p, %p, %lu, %lu, %x, %s, %p)\n",
DeviceExt,
FileObject,
Buffer,
Length,
WriteOffset,
IrpFlags,
(CaseSensitive ? "TRUE" : "FALSE"),
LengthWritten);
*LengthWritten = 0;
ASSERT(DeviceExt);
if (Length == 0)
{
if (Buffer == NULL)
return STATUS_SUCCESS;
else
return STATUS_INVALID_PARAMETER;
}
// get the File control block
Fcb = (PNTFS_FCB)FileObject->FsContext;
ASSERT(Fcb);
DPRINT("Fcb->PathName: %wS\n", Fcb->PathName);
DPRINT("Fcb->ObjectName: %wS\n", Fcb->ObjectName);
// we don't yet handle compression
if (NtfsFCBIsCompressed(Fcb))
{
DPRINT("Compressed file!\n");
UNIMPLEMENTED;
return STATUS_NOT_IMPLEMENTED;
}
// allocate non-paged memory for the FILE_RECORD_HEADER
FileRecord = ExAllocateFromNPagedLookasideList(&DeviceExt->FileRecLookasideList);
if (FileRecord == NULL)
{
DPRINT1("Not enough memory! Can't write %wS!\n", Fcb->PathName);
return STATUS_INSUFFICIENT_RESOURCES;
}
// read the FILE_RECORD_HEADER from the drive (or cache)
DPRINT("Reading file record...\n");
Status = ReadFileRecord(DeviceExt, Fcb->MFTIndex, FileRecord);
if (!NT_SUCCESS(Status))
{
// We couldn't get the file's record. Free the memory and return the error
DPRINT1("Can't find record for %wS!\n", Fcb->ObjectName);
ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, FileRecord);
return Status;
}
DPRINT("Found record for %wS\n", Fcb->ObjectName);
// Find the attribute with the data stream for our file
DPRINT("Finding Data Attribute...\n");
Status = FindAttribute(DeviceExt, FileRecord, AttributeData, Fcb->Stream, wcslen(Fcb->Stream), &DataContext,
&AttributeOffset);
// Did we fail to find the attribute?
if (!NT_SUCCESS(Status))
{
NTSTATUS BrowseStatus;
FIND_ATTR_CONTXT Context;
PNTFS_ATTR_RECORD Attribute;
DPRINT1("No '%S' data stream associated with file!\n", Fcb->Stream);
// Couldn't find the requested data stream; print a list of streams available
BrowseStatus = FindFirstAttribute(&Context, DeviceExt, FileRecord, FALSE, &Attribute);
while (NT_SUCCESS(BrowseStatus))
{
if (Attribute->Type == AttributeData)
{
UNICODE_STRING Name;
Name.Length = Attribute->NameLength * sizeof(WCHAR);
Name.MaximumLength = Name.Length;
Name.Buffer = (PWCHAR)((ULONG_PTR)Attribute + Attribute->NameOffset);
DPRINT1("Data stream: '%wZ' available\n", &Name);
}
BrowseStatus = FindNextAttribute(&Context, &Attribute);
}
FindCloseAttribute(&Context);
ReleaseAttributeContext(DataContext);
ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, FileRecord);
return Status;
}
// Get the size of the stream on disk
StreamSize = AttributeDataLength(DataContext->pRecord);
DPRINT("WriteOffset: %lu\tStreamSize: %I64u\n", WriteOffset, StreamSize);
// Are we trying to write beyond the end of the stream?
if (WriteOffset + Length > StreamSize)
{
// is increasing the stream size allowed?
if (!(Fcb->Flags & FCB_IS_VOLUME) &&
!(IrpFlags & IRP_PAGING_IO))
{
LARGE_INTEGER DataSize;
ULONGLONG AllocationSize;
PFILENAME_ATTRIBUTE fileNameAttribute;
ULONGLONG ParentMFTId;
UNICODE_STRING filename;
DataSize.QuadPart = WriteOffset + Length;
// set the attribute data length
Status = SetAttributeDataLength(FileObject, Fcb, DataContext, AttributeOffset, FileRecord, &DataSize);
if (!NT_SUCCESS(Status))
{
ReleaseAttributeContext(DataContext);
ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, FileRecord);
*LengthWritten = 0;
return Status;
}
AllocationSize = AttributeAllocatedLength(DataContext->pRecord);
// now we need to update this file's size in every directory index entry that references it
// TODO: put this code in its own function and adapt it to work with every filename / hardlink
// stored in the file record.
fileNameAttribute = GetBestFileNameFromRecord(Fcb->Vcb, FileRecord);
ASSERT(fileNameAttribute);
ParentMFTId = fileNameAttribute->DirectoryFileReferenceNumber & NTFS_MFT_MASK;
filename.Buffer = fileNameAttribute->Name;
filename.Length = fileNameAttribute->NameLength * sizeof(WCHAR);
filename.MaximumLength = filename.Length;
Status = UpdateFileNameRecord(Fcb->Vcb,
ParentMFTId,
&filename,
FALSE,
DataSize.QuadPart,
AllocationSize,
CaseSensitive);
}
else
{
// TODO - just fail for now
ReleaseAttributeContext(DataContext);
ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, FileRecord);
*LengthWritten = 0;
return STATUS_ACCESS_DENIED;
}
}
DPRINT("Length: %lu\tWriteOffset: %lu\tStreamSize: %I64u\n", Length, WriteOffset, StreamSize);
// Write the data to the attribute
Status = WriteAttribute(DeviceExt, DataContext, WriteOffset, Buffer, Length, LengthWritten, FileRecord);
// Did the write fail?
if (!NT_SUCCESS(Status))
{
DPRINT1("Write failure!\n");
ReleaseAttributeContext(DataContext);
ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, FileRecord);
return Status;
}
// This should never happen:
if (*LengthWritten != Length)
{
DPRINT1("\a\tNTFS DRIVER ERROR: length written (%lu) differs from requested (%lu), but no error was indicated!\n",
*LengthWritten, Length);
Status = STATUS_UNEXPECTED_IO_ERROR;
}
ReleaseAttributeContext(DataContext);
ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, FileRecord);
return Status;
}
/**
* @name NtfsWrite
* @implemented
*
* Handles IRP_MJ_WRITE I/O Request Packets for NTFS. This code borrows a lot from
* VfatWrite, and needs a lot of cleaning up. It also needs a lot more of the code
* from VfatWrite integrated.
*
* @param IrpContext
* Points to an NTFS_IRP_CONTEXT which describes the write
*
* @return
* STATUS_SUCCESS if successful,
* STATUS_INSUFFICIENT_RESOURCES if an allocation failed,
* STATUS_INVALID_DEVICE_REQUEST if called on the main device object,
* STATUS_NOT_IMPLEMENTED or STATUS_ACCESS_DENIED if a required feature isn't implemented.
* STATUS_PARTIAL_COPY, STATUS_UNSUCCESSFUL, or STATUS_OBJECT_NAME_NOT_FOUND if NtfsWriteFile() fails.
*
* @remarks Called by NtfsDispatch() in response to an IRP_MJ_WRITE request. Page files are not implemented.
* Support for large files (>4gb) is not implemented. Cached writes, file locks, transactions, etc - not implemented.
*
*/
NTSTATUS
NtfsWrite(PNTFS_IRP_CONTEXT IrpContext)
{
PNTFS_FCB Fcb;
PERESOURCE Resource = NULL;
LARGE_INTEGER ByteOffset;
PUCHAR Buffer;
NTSTATUS Status = STATUS_SUCCESS;
ULONG Length = 0;
ULONG ReturnedWriteLength = 0;
PDEVICE_OBJECT DeviceObject = NULL;
PDEVICE_EXTENSION DeviceExt = NULL;
PFILE_OBJECT FileObject = NULL;
PIRP Irp = NULL;
ULONG BytesPerSector;
DPRINT("NtfsWrite(IrpContext %p)\n", IrpContext);
ASSERT(IrpContext);
// get the I/O request packet
Irp = IrpContext->Irp;
// This request is not allowed on the main device object
if (IrpContext->DeviceObject == NtfsGlobalData->DeviceObject)
{
DPRINT1("\t\t\t\tNtfsWrite is called with the main device object.\n");
Irp->IoStatus.Information = 0;
return STATUS_INVALID_DEVICE_REQUEST;
}
// get the File control block
Fcb = (PNTFS_FCB)IrpContext->FileObject->FsContext;
ASSERT(Fcb);
DPRINT("About to write %wS\n", Fcb->ObjectName);
DPRINT("NTFS Version: %d.%d\n", Fcb->Vcb->NtfsInfo.MajorVersion, Fcb->Vcb->NtfsInfo.MinorVersion);
// setup some more locals
FileObject = IrpContext->FileObject;
DeviceObject = IrpContext->DeviceObject;
DeviceExt = DeviceObject->DeviceExtension;
BytesPerSector = DeviceExt->StorageDevice->SectorSize;
Length = IrpContext->Stack->Parameters.Write.Length;
// get the file offset we'll be writing to
ByteOffset = IrpContext->Stack->Parameters.Write.ByteOffset;
if (ByteOffset.u.LowPart == FILE_WRITE_TO_END_OF_FILE &&
ByteOffset.u.HighPart == -1)
{
ByteOffset.QuadPart = Fcb->RFCB.FileSize.QuadPart;
}
DPRINT("ByteOffset: %I64u\tLength: %lu\tBytes per sector: %lu\n", ByteOffset.QuadPart,
Length, BytesPerSector);
if (ByteOffset.u.HighPart && !(Fcb->Flags & FCB_IS_VOLUME))
{
// TODO: Support large files
DPRINT1("FIXME: Writing to large files is not yet supported at this time.\n");
return STATUS_INVALID_PARAMETER;
}
// Is this a non-cached write? A non-buffered write?
if (IrpContext->Irp->Flags & (IRP_PAGING_IO | IRP_NOCACHE) || (Fcb->Flags & FCB_IS_VOLUME) ||
IrpContext->FileObject->Flags & FILE_NO_INTERMEDIATE_BUFFERING)
{
// non-cached and non-buffered writes must be sector aligned
if (ByteOffset.u.LowPart % BytesPerSector != 0 || Length % BytesPerSector != 0)
{
DPRINT1("Non-cached writes and non-buffered writes must be sector aligned!\n");
return STATUS_INVALID_PARAMETER;
}
}
if (Length == 0)
{
DPRINT1("Null write!\n");
IrpContext->Irp->IoStatus.Information = 0;
// FIXME: Doesn't accurately detect when a user passes NULL to WriteFile() for the buffer
if (Irp->UserBuffer == NULL && Irp->MdlAddress == NULL)
{
// FIXME: Update last write time
return STATUS_SUCCESS;
}
return STATUS_INVALID_PARAMETER;
}
// get the Resource
if (Fcb->Flags & FCB_IS_VOLUME)
{
Resource = &DeviceExt->DirResource;
}
else if (IrpContext->Irp->Flags & IRP_PAGING_IO)
{
Resource = &Fcb->PagingIoResource;
}
else
{
Resource = &Fcb->MainResource;
}
// acquire exclusive access to the Resource
if (!ExAcquireResourceExclusiveLite(Resource, BooleanFlagOn(IrpContext->Flags, IRPCONTEXT_CANWAIT)))
{
return STATUS_CANT_WAIT;
}
/* From VfatWrite(). Todo: Handle file locks
if (!(IrpContext->Irp->Flags & IRP_PAGING_IO) &&
FsRtlAreThereCurrentFileLocks(&Fcb->FileLock))
{
if (!FsRtlCheckLockForWriteAccess(&Fcb->FileLock, IrpContext->Irp))
{
Status = STATUS_FILE_LOCK_CONFLICT;
goto ByeBye;
}
}*/
// Is this an async request to a file?
if (!(IrpContext->Flags & IRPCONTEXT_CANWAIT) && !(Fcb->Flags & FCB_IS_VOLUME))
{
DPRINT1("FIXME: Async writes not supported in NTFS!\n");
ExReleaseResourceLite(Resource);
return STATUS_NOT_IMPLEMENTED;
}
// get the buffer of data the user is trying to write
Buffer = NtfsGetUserBuffer(Irp, BooleanFlagOn(Irp->Flags, IRP_PAGING_IO));
ASSERT(Buffer);
// lock the buffer
Status = NtfsLockUserBuffer(Irp, Length, IoReadAccess);
// were we unable to lock the buffer?
if (!NT_SUCCESS(Status))
{
DPRINT1("Unable to lock user buffer!\n");
ExReleaseResourceLite(Resource);
return Status;
}
DPRINT("Existing File Size(Fcb->RFCB.FileSize.QuadPart): %I64u\n", Fcb->RFCB.FileSize.QuadPart);
DPRINT("About to write the data. Length: %lu\n", Length);
// TODO: handle HighPart of ByteOffset (large files)
// write the file
Status = NtfsWriteFile(DeviceExt,
FileObject,
Buffer,
Length,
ByteOffset.LowPart,
Irp->Flags,
BooleanFlagOn(IrpContext->Stack->Flags, SL_CASE_SENSITIVE),
&ReturnedWriteLength);
IrpContext->Irp->IoStatus.Status = Status;
// was the write successful?
if (NT_SUCCESS(Status))
{
// TODO: Update timestamps
if (FileObject->Flags & FO_SYNCHRONOUS_IO)
{
// advance the file pointer
FileObject->CurrentByteOffset.QuadPart = ByteOffset.QuadPart + ReturnedWriteLength;
}
IrpContext->PriorityBoost = IO_DISK_INCREMENT;
}
else
{
DPRINT1("Write not Succesful!\tReturned length: %lu\n", ReturnedWriteLength);
}
Irp->IoStatus.Information = ReturnedWriteLength;
// Note: We leave the user buffer that we locked alone, it's up to the I/O manager to unlock and free it
ExReleaseResourceLite(Resource);
return Status;
}
/* EOF */