/*
* ReactOS kernel
- * Copyright (C) 2002 ReactOS Team
+ * 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
* PROJECT: ReactOS kernel
* FILE: drivers/filesystem/ntfs/rw.c
* PURPOSE: NTFS filesystem driver
- * PROGRAMMER: Art Yerkes
- * UPDATE HISTORY:
+ * PROGRAMMERS: Art Yerkes
+ * Pierre Schweitzer (pierre@reactos.org)
+ * Trevor Thompson
*/
/* INCLUDES *****************************************************************/
#define NDEBUG
#include <debug.h>
-/* GLOBALS *******************************************************************/
-
-#define ROUND_UP(N, S) ((((N) + (S) - 1) / (S)) * (S))
-#define ROUND_DOWN(N, S) ((N) - ((N) % (S)))
-
/* FUNCTIONS ****************************************************************/
/*
ULONG IrpFlags,
PULONG LengthRead)
{
-#if 0
- NTSTATUS Status = STATUS_SUCCESS;
- PUCHAR TempBuffer;
- ULONG TempLength;
- PCCB Ccb;
- PFCB Fcb;
+ 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;
+
+ DPRINT1("NtfsReadFile(%p, %p, %p, %u, %u, %x, %p)\n", DeviceExt, FileObject, Buffer, Length, ReadOffset, IrpFlags, LengthRead);
- DPRINT("CdfsReadFile(ReadOffset %lu Length %lu)\n", ReadOffset, Length);
+ *LengthRead = 0;
- *LengthRead = 0;
+ if (Length == 0)
+ {
+ DPRINT1("Null read!\n");
+ return STATUS_SUCCESS;
+ }
- if (Length == 0)
- return(STATUS_SUCCESS);
+ Fcb = (PNTFS_FCB)FileObject->FsContext;
- Ccb = (PCCB)FileObject->FsContext2;
- Fcb = (PFCB)FileObject->FsContext;
+ if (NtfsFCBIsCompressed(Fcb))
+ {
+ DPRINT1("Compressed file!\n");
+ UNIMPLEMENTED;
+ return STATUS_NOT_IMPLEMENTED;
+ }
+
+ FileRecord = ExAllocatePoolWithTag(NonPagedPool, DeviceExt->NtfsInfo.BytesPerFileRecord, TAG_NTFS);
+ if (FileRecord == NULL)
+ {
+ DPRINT1("Not enough memory!\n");
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
- if (ReadOffset >= Fcb->Entry.DataLengthL)
- return(STATUS_END_OF_FILE);
+ Status = ReadFileRecord(DeviceExt, Fcb->MFTIndex, FileRecord);
+ if (!NT_SUCCESS(Status))
+ {
+ DPRINT1("Can't find record!\n");
+ ExFreePoolWithTag(FileRecord, TAG_NTFS);
+ return Status;
+ }
- DPRINT("Reading %d bytes at %d\n", Length, ReadOffset);
- if (!(IrpFlags & (IRP_NOCACHE|IRP_PAGING_IO)))
+ Status = FindAttribute(DeviceExt, FileRecord, AttributeData, Fcb->Stream, wcslen(Fcb->Stream), &DataContext, NULL);
+ if (!NT_SUCCESS(Status))
{
- LARGE_INTEGER FileOffset;
- IO_STATUS_BLOCK IoStatus;
+ NTSTATUS BrowseStatus;
+ FIND_ATTR_CONTXT Context;
+ PNTFS_ATTR_RECORD Attribute;
+
+ DPRINT1("No '%S' data stream associated with file!\n", Fcb->Stream);
- if (ReadOffset + Length > Fcb->Entry.DataLengthL)
- Length = Fcb->Entry.DataLengthL - ReadOffset;
- if (FileObject->PrivateCacheMap == NULL)
- {
- CcRosInitializeFileCache(FileObject, PAGE_SIZE);
- }
+ 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);
+ }
- FileOffset.QuadPart = (LONGLONG)ReadOffset;
- CcCopyRead(FileObject,
- &FileOffset,
- Length,
- TRUE,
- Buffer,
- &IoStatus);
- *LengthRead = IoStatus.Information;
+ BrowseStatus = FindNextAttribute(&Context, &Attribute);
+ }
+ FindCloseAttribute(&Context);
- return(IoStatus.Status);
+ ReleaseAttributeContext(DataContext);
+ ExFreePoolWithTag(FileRecord, TAG_NTFS);
+ return Status;
}
- if ((ReadOffset % BLOCKSIZE) != 0 || (Length % BLOCKSIZE) != 0)
+ StreamSize = AttributeDataLength(&DataContext->Record);
+ if (ReadOffset >= StreamSize)
{
- return STATUS_INVALID_PARAMETER;
+ DPRINT1("Reading beyond stream end!\n");
+ ReleaseAttributeContext(DataContext);
+ ExFreePoolWithTag(FileRecord, TAG_NTFS);
+ return STATUS_END_OF_FILE;
}
- if (ReadOffset + Length > ROUND_UP(Fcb->Entry.DataLengthL, BLOCKSIZE))
- Length = ROUND_UP(Fcb->Entry.DataLengthL, BLOCKSIZE) - ReadOffset;
- Status = CdfsReadSectors(DeviceExt->StorageDevice,
- Fcb->Entry.ExtentLocationL + (ReadOffset / BLOCKSIZE),
- Length / BLOCKSIZE,
- Buffer);
- if (NT_SUCCESS(Status))
+ ToRead = Length;
+ if (ReadOffset + Length > StreamSize)
+ ToRead = StreamSize - ReadOffset;
+
+ RealReadOffset = ReadOffset;
+ RealLength = ToRead;
+
+ if ((ReadOffset % DeviceExt->NtfsInfo.BytesPerSector) != 0 || (ToRead % DeviceExt->NtfsInfo.BytesPerSector) != 0)
{
- *LengthRead = Length;
- if (Length + ReadOffset > Fcb->Entry.DataLengthL)
- {
- memset(Buffer + Fcb->Entry.DataLengthL - ReadOffset,
- 0, Length + ReadOffset - Fcb->Entry.DataLengthL);
- }
+ 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->Record))
+ RealLength += DeviceExt->NtfsInfo.BytesPerSector;
+ }
+
+
+ ReadBuffer = ExAllocatePoolWithTag(NonPagedPool, RealLength, TAG_NTFS);
+ if (ReadBuffer == NULL)
+ {
+ DPRINT1("Not enough memory!\n");
+ ReleaseAttributeContext(DataContext);
+ ExFreePoolWithTag(FileRecord, TAG_NTFS);
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+ AllocatedBuffer = TRUE;
}
- return(Status);
-#else
- UNREFERENCED_PARAMETER(DeviceExt);
- UNREFERENCED_PARAMETER(FileObject);
- UNREFERENCED_PARAMETER(Buffer);
- UNREFERENCED_PARAMETER(Length);
- UNREFERENCED_PARAMETER(ReadOffset);
- UNREFERENCED_PARAMETER(IrpFlags);
- *LengthRead = 0;
- return STATUS_END_OF_FILE;
-#endif
+ 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);
+ ExFreePoolWithTag(FileRecord, TAG_NTFS);
+ if (AllocatedBuffer)
+ {
+ ExFreePoolWithTag(ReadBuffer, TAG_NTFS);
+ }
+ return Status;
+ }
+
+ ReleaseAttributeContext(DataContext);
+ ExFreePoolWithTag(FileRecord, TAG_NTFS);
+
+ *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
-NTAPI
-NtfsFsdRead(PDEVICE_OBJECT DeviceObject,
- PIRP Irp)
+NtfsRead(PNTFS_IRP_CONTEXT IrpContext)
{
PDEVICE_EXTENSION DeviceExt;
PIO_STACK_LOCATION Stack;
LARGE_INTEGER ReadOffset;
ULONG ReturnedReadLength = 0;
NTSTATUS Status = STATUS_SUCCESS;
+ PIRP Irp;
+ PDEVICE_OBJECT DeviceObject;
- DPRINT("NtfsRead(DeviceObject %x, Irp %x)\n",DeviceObject,Irp);
+ DPRINT("NtfsRead(IrpContext %p)\n", IrpContext);
- DeviceExt = DeviceObject->DeviceExtension;
- Stack = IoGetCurrentIrpStackLocation(Irp);
- FileObject = Stack->FileObject;
+ 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 = MmGetSystemAddressForMdl(Irp->MdlAddress);
+ Buffer = NtfsGetUserBuffer(Irp, BooleanFlagOn(Irp->Flags, IRP_PAGING_IO));
Status = NtfsReadFile(DeviceExt,
FileObject,
Irp->IoStatus.Information = 0;
}
- Irp->IoStatus.Status = Status;
- IoCompleteRequest(Irp,IO_NO_INCREMENT);
-
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, %u, %u, %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 = ExAllocatePoolWithTag(NonPagedPool, DeviceExt->NtfsInfo.BytesPerFileRecord, TAG_NTFS);
+ 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);
+ ExFreePoolWithTag(FileRecord, TAG_NTFS);
+ 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);
+ ExFreePoolWithTag(FileRecord, TAG_NTFS);
+ return Status;
+ }
+
+ // Get the size of the stream on disk
+ StreamSize = AttributeDataLength(&DataContext->Record);
+
+ 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;
+
+ AllocationSize = ROUND_UP(DataSize.QuadPart, Fcb->Vcb->NtfsInfo.BytesPerCluster);
+
+ // set the attribute data length
+ Status = SetAttributeDataLength(FileObject, Fcb, DataContext, AttributeOffset, FileRecord, &DataSize);
+
+ if (!NT_SUCCESS(Status))
+ {
+ ReleaseAttributeContext(DataContext);
+ ExFreePoolWithTag(FileRecord, TAG_NTFS);
+ *LengthWritten = 0;
+ return Status;
+ }
+
+ // 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);
+ ExFreePoolWithTag(FileRecord, TAG_NTFS);
+ *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);
+
+ // Did the write fail?
+ if (!NT_SUCCESS(Status))
+ {
+ DPRINT1("Write failure!\n");
+ ReleaseAttributeContext(DataContext);
+ ExFreePoolWithTag(FileRecord, TAG_NTFS);
+
+ 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);
+ ExFreePoolWithTag(FileRecord, TAG_NTFS);
+
+ 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
-NTAPI
-NtfsFsdWrite(PDEVICE_OBJECT DeviceObject,
- PIRP Irp)
+NtfsWrite(PNTFS_IRP_CONTEXT IrpContext)
{
- DPRINT("NtfwWrite(DeviceObject %x Irp %x)\n",DeviceObject,Irp);
+ 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);
+
+ // 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 I/O request packet
+ Irp = IrpContext->Irp;
+
+ // get the File control block
+ Fcb = (PNTFS_FCB)IrpContext->FileObject->FsContext;
+ ASSERT(Fcb);
- Irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
- Irp->IoStatus.Information = 0;
- return STATUS_NOT_SUPPORTED;
+ 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,
+ (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 */