mirror of
https://github.com/reactos/reactos.git
synced 2024-12-29 10:35:28 +00:00
[NTFS]
Added minimal write support from CORE-10998 along with updates as suggested by CR-90. svn path=/branches/GSoC_2016/NTFS/; revision=71224
This commit is contained in:
parent
950c86fb4c
commit
58a13831ef
6 changed files with 931 additions and 9 deletions
|
@ -20,7 +20,8 @@
|
||||||
* PROJECT: ReactOS kernel
|
* PROJECT: ReactOS kernel
|
||||||
* FILE: drivers/filesystem/ntfs/blockdev.c
|
* FILE: drivers/filesystem/ntfs/blockdev.c
|
||||||
* PURPOSE: NTFS filesystem driver
|
* PURPOSE: NTFS filesystem driver
|
||||||
* PROGRAMMER: Eric Kohl
|
* PROGRAMMERS: Eric Kohl
|
||||||
|
* Trevor Thompson
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* INCLUDES *****************************************************************/
|
/* INCLUDES *****************************************************************/
|
||||||
|
@ -129,6 +130,180 @@ NtfsReadDisk(IN PDEVICE_OBJECT DeviceObject,
|
||||||
return 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
|
||||||
|
NtfsWriteDisk(IN PDEVICE_OBJECT DeviceObject,
|
||||||
|
IN LONGLONG StartingOffset,
|
||||||
|
IN ULONG Length,
|
||||||
|
IN ULONG SectorSize,
|
||||||
|
IN const PUCHAR Buffer)
|
||||||
|
{
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
{
|
||||||
|
DPRINT1("IoBuildSynchronousFsdRequest failed\n");
|
||||||
|
|
||||||
|
if (AllocatedBuffer)
|
||||||
|
{
|
||||||
|
RtlSecureZeroMemory(TempBuffer, RealLength);
|
||||||
|
ExFreePoolWithTag(TempBuffer, TAG_NTFS);
|
||||||
|
}
|
||||||
|
|
||||||
|
return STATUS_INSUFFICIENT_RESOURCES;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (AllocatedBuffer)
|
||||||
|
{
|
||||||
|
// zero the buffer before freeing it, so private user data can't be snooped
|
||||||
|
RtlSecureZeroMemory(TempBuffer, RealLength);
|
||||||
|
|
||||||
|
ExFreePoolWithTag(TempBuffer, TAG_NTFS);
|
||||||
|
}
|
||||||
|
|
||||||
|
DPRINT("NtfsWriteDisk() done (Status %x)\n", Status);
|
||||||
|
|
||||||
|
return Status;
|
||||||
|
}
|
||||||
|
|
||||||
NTSTATUS
|
NTSTATUS
|
||||||
NtfsReadSectors(IN PDEVICE_OBJECT DeviceObject,
|
NtfsReadSectors(IN PDEVICE_OBJECT DeviceObject,
|
||||||
IN ULONG DiskSector,
|
IN ULONG DiskSector,
|
||||||
|
|
|
@ -476,26 +476,26 @@ NtfsCreateFile(PDEVICE_OBJECT DeviceObject,
|
||||||
return Status;
|
return Status;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* HUGLY HACK: remain RO so far... */
|
/* HUGLY HACK: Can't overwrite or supersede a file yet... */
|
||||||
if (RequestedDisposition == FILE_OVERWRITE ||
|
if (RequestedDisposition == FILE_OVERWRITE ||
|
||||||
RequestedDisposition == FILE_OVERWRITE_IF ||
|
RequestedDisposition == FILE_OVERWRITE_IF ||
|
||||||
RequestedDisposition == FILE_SUPERSEDE)
|
RequestedDisposition == FILE_SUPERSEDE)
|
||||||
{
|
{
|
||||||
DPRINT1("Denying write request on NTFS volume\n");
|
DPRINT1("Cannot yet perform an overwrite or supersede request on NTFS volume\n");
|
||||||
NtfsCloseFile(DeviceExt, FileObject);
|
NtfsCloseFile(DeviceExt, FileObject);
|
||||||
return STATUS_ACCESS_DENIED;
|
return STATUS_ACCESS_DENIED;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
/* HUGLY HACK: remain RO so far... */
|
/* HUGLY HACK: Can't create new files yet... */
|
||||||
if (RequestedDisposition == FILE_CREATE ||
|
if (RequestedDisposition == FILE_CREATE ||
|
||||||
RequestedDisposition == FILE_OPEN_IF ||
|
RequestedDisposition == FILE_OPEN_IF ||
|
||||||
RequestedDisposition == FILE_OVERWRITE_IF ||
|
RequestedDisposition == FILE_OVERWRITE_IF ||
|
||||||
RequestedDisposition == FILE_SUPERSEDE)
|
RequestedDisposition == FILE_SUPERSEDE)
|
||||||
{
|
{
|
||||||
DPRINT1("Denying write request on NTFS volume\n");
|
DPRINT1("Denying file creation request on NTFS volume\n");
|
||||||
return STATUS_ACCESS_DENIED;
|
return STATUS_CANNOT_MAKE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
* Valentin Verkhovsky
|
* Valentin Verkhovsky
|
||||||
* Pierre Schweitzer (pierre@reactos.org)
|
* Pierre Schweitzer (pierre@reactos.org)
|
||||||
* Hervé Poussineau (hpoussin@reactos.org)
|
* Hervé Poussineau (hpoussin@reactos.org)
|
||||||
|
* Trevor Thompson
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* INCLUDES *****************************************************************/
|
/* INCLUDES *****************************************************************/
|
||||||
|
@ -31,6 +32,7 @@
|
||||||
#include "ntfs.h"
|
#include "ntfs.h"
|
||||||
|
|
||||||
#define NDEBUG
|
#define NDEBUG
|
||||||
|
#undef NDEBUG
|
||||||
#include <debug.h>
|
#include <debug.h>
|
||||||
|
|
||||||
/* FUNCTIONS ****************************************************************/
|
/* FUNCTIONS ****************************************************************/
|
||||||
|
@ -334,6 +336,273 @@ ReadAttribute(PDEVICE_EXTENSION Vcb,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name WriteAttribute
|
||||||
|
* @implemented
|
||||||
|
*
|
||||||
|
* Writes an NTFS attribute to the disk. It presently borrows a lot of code from ReadAttribute(),
|
||||||
|
* and it still needs more documentation / cleaning up.
|
||||||
|
*
|
||||||
|
* @param Vcb
|
||||||
|
* Volume Control Block indicating which volume to write the attribute to
|
||||||
|
*
|
||||||
|
* @param Context
|
||||||
|
* Pointer to an NTFS_ATTR_CONTEXT that has information about the attribute
|
||||||
|
*
|
||||||
|
* @param Offset
|
||||||
|
* Offset, in bytes, from the beginning of the attribute indicating where to start
|
||||||
|
* writing data
|
||||||
|
*
|
||||||
|
* @param Buffer
|
||||||
|
* The data that's being written to the device
|
||||||
|
*
|
||||||
|
* @param Length
|
||||||
|
* How much data will be written, in bytes
|
||||||
|
*
|
||||||
|
* @param RealLengthWritten
|
||||||
|
* Pointer to a ULONG which will receive how much data was written, in bytes
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* STATUS_SUCCESS if successful, an error code otherwise. STATUS_NOT_IMPLEMENTED if
|
||||||
|
* writing to a sparse file.
|
||||||
|
*
|
||||||
|
* @remarks Note that in this context the word "attribute" isn't referring read-only, hidden,
|
||||||
|
* etc. - the file's data is actually stored in an attribute in NTFS parlance.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
NTSTATUS
|
||||||
|
WriteAttribute(PDEVICE_EXTENSION Vcb,
|
||||||
|
PNTFS_ATTR_CONTEXT Context,
|
||||||
|
ULONGLONG Offset,
|
||||||
|
const PUCHAR Buffer,
|
||||||
|
ULONG Length,
|
||||||
|
PULONG RealLengthWritten)
|
||||||
|
{
|
||||||
|
ULONGLONG LastLCN;
|
||||||
|
PUCHAR DataRun;
|
||||||
|
LONGLONG DataRunOffset;
|
||||||
|
ULONGLONG DataRunLength;
|
||||||
|
LONGLONG DataRunStartLCN;
|
||||||
|
ULONGLONG CurrentOffset;
|
||||||
|
ULONG WriteLength;
|
||||||
|
NTSTATUS Status;
|
||||||
|
PUCHAR SourceBuffer = Buffer;
|
||||||
|
LONGLONG StartingOffset;
|
||||||
|
|
||||||
|
DPRINT("WriteAttribute(%p, %p, %I64U, %p, %lu)\n", Vcb, Context, Offset, Buffer, Length);
|
||||||
|
|
||||||
|
// is this a resident attribute?
|
||||||
|
if (!Context->Record.IsNonResident)
|
||||||
|
{
|
||||||
|
DPRINT1("FIXME: Writing to resident NTFS records (small files) is not supported at this time.\n");
|
||||||
|
// (TODO: This should be really easy to implement)
|
||||||
|
|
||||||
|
/* LeftOver code from ReadAttribute(), may be helpful:
|
||||||
|
if (Offset > Context->Record.Resident.ValueLength)
|
||||||
|
return 0;
|
||||||
|
if (Offset + Length > Context->Record.Resident.ValueLength)
|
||||||
|
Length = (ULONG)(Context->Record.Resident.ValueLength - Offset);
|
||||||
|
RtlCopyMemory(Buffer, (PCHAR)&Context->Record + Context->Record.Resident.ValueOffset + Offset, Length);
|
||||||
|
return Length;*/
|
||||||
|
|
||||||
|
return STATUS_NOT_IMPLEMENTED; // until we implement it
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is a non-resident attribute.
|
||||||
|
|
||||||
|
// I. Find the corresponding start data run.
|
||||||
|
|
||||||
|
*RealLengthWritten = 0;
|
||||||
|
|
||||||
|
// FIXME: Cache seems to be non-working. Disable it for now
|
||||||
|
//if(Context->CacheRunOffset <= Offset && Offset < Context->CacheRunOffset + Context->CacheRunLength * Volume->ClusterSize)
|
||||||
|
/*if (0)
|
||||||
|
{
|
||||||
|
DataRun = Context->CacheRun;
|
||||||
|
LastLCN = Context->CacheRunLastLCN;
|
||||||
|
DataRunStartLCN = Context->CacheRunStartLCN;
|
||||||
|
DataRunLength = Context->CacheRunLength;
|
||||||
|
CurrentOffset = Context->CacheRunCurrentOffset;
|
||||||
|
}
|
||||||
|
else*/
|
||||||
|
{
|
||||||
|
LastLCN = 0;
|
||||||
|
DataRun = (PUCHAR)&Context->Record + Context->Record.NonResident.MappingPairsOffset;
|
||||||
|
CurrentOffset = 0;
|
||||||
|
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
DataRun = DecodeRun(DataRun, &DataRunOffset, &DataRunLength);
|
||||||
|
if (DataRunOffset != -1)
|
||||||
|
{
|
||||||
|
// Normal data run.
|
||||||
|
// DPRINT1("Writing to normal data run, LastLCN %I64u DataRunOffset %I64d\n", LastLCN, DataRunOffset);
|
||||||
|
DataRunStartLCN = LastLCN + DataRunOffset;
|
||||||
|
LastLCN = DataRunStartLCN;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Sparse data run. We can't support writing to sparse files yet
|
||||||
|
// (it may require increasing the allocation size).
|
||||||
|
DataRunStartLCN = -1;
|
||||||
|
DPRINT1("FIXME: Writing to sparse files is not supported yet!\n");
|
||||||
|
return STATUS_NOT_IMPLEMENTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Have we reached the data run we're trying to write to?
|
||||||
|
if (Offset >= CurrentOffset &&
|
||||||
|
Offset < CurrentOffset + (DataRunLength * Vcb->NtfsInfo.BytesPerCluster))
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*DataRun == 0)
|
||||||
|
{
|
||||||
|
// We reached the last assigned cluster
|
||||||
|
// TODO: assign new clusters to the end of the file.
|
||||||
|
// (Presently, this code will never be reached, the write should have already failed by now)
|
||||||
|
return STATUS_END_OF_FILE;
|
||||||
|
}
|
||||||
|
|
||||||
|
CurrentOffset += DataRunLength * Vcb->NtfsInfo.BytesPerCluster;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// II. Go through the run list and write the data
|
||||||
|
|
||||||
|
/* REVIEWME -- As adapted from NtfsReadAttribute():
|
||||||
|
We seem to be making a special case for the first applicable data run, but I'm not sure why.
|
||||||
|
Does it have something to do with (not) caching? Is this strategy equally applicable to writing? */
|
||||||
|
|
||||||
|
WriteLength = (ULONG)min(DataRunLength * Vcb->NtfsInfo.BytesPerCluster - (Offset - CurrentOffset), Length);
|
||||||
|
|
||||||
|
StartingOffset = DataRunStartLCN * Vcb->NtfsInfo.BytesPerCluster + Offset - CurrentOffset;
|
||||||
|
|
||||||
|
// Write the data to the disk
|
||||||
|
Status = NtfsWriteDisk(Vcb->StorageDevice,
|
||||||
|
StartingOffset,
|
||||||
|
WriteLength,
|
||||||
|
Vcb->NtfsInfo.BytesPerSector,
|
||||||
|
(PVOID)SourceBuffer);
|
||||||
|
|
||||||
|
// Did the write fail?
|
||||||
|
if (!NT_SUCCESS(Status))
|
||||||
|
{
|
||||||
|
Context->CacheRun = DataRun;
|
||||||
|
Context->CacheRunOffset = Offset;
|
||||||
|
Context->CacheRunStartLCN = DataRunStartLCN;
|
||||||
|
Context->CacheRunLength = DataRunLength;
|
||||||
|
Context->CacheRunLastLCN = LastLCN;
|
||||||
|
Context->CacheRunCurrentOffset = CurrentOffset;
|
||||||
|
|
||||||
|
return Status;
|
||||||
|
}
|
||||||
|
|
||||||
|
Length -= WriteLength;
|
||||||
|
SourceBuffer += WriteLength;
|
||||||
|
*RealLengthWritten += WriteLength;
|
||||||
|
|
||||||
|
// Did we write to the end of the data run?
|
||||||
|
if (WriteLength == DataRunLength * Vcb->NtfsInfo.BytesPerCluster - (Offset - CurrentOffset))
|
||||||
|
{
|
||||||
|
// Advance to the next data run
|
||||||
|
CurrentOffset += DataRunLength * Vcb->NtfsInfo.BytesPerCluster;
|
||||||
|
DataRun = DecodeRun(DataRun, &DataRunOffset, &DataRunLength);
|
||||||
|
|
||||||
|
if (DataRunOffset != (ULONGLONG)-1)
|
||||||
|
{
|
||||||
|
DataRunStartLCN = LastLCN + DataRunOffset;
|
||||||
|
LastLCN = DataRunStartLCN;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
DataRunStartLCN = -1;
|
||||||
|
|
||||||
|
if (*DataRun == 0)
|
||||||
|
{
|
||||||
|
if (Length == 0)
|
||||||
|
return STATUS_SUCCESS;
|
||||||
|
|
||||||
|
// This code shouldn't execute, because we should have extended the allocation size
|
||||||
|
// or failed the request by now. It's just a sanity check.
|
||||||
|
DPRINT1("Encountered EOF before expected!\n");
|
||||||
|
return STATUS_END_OF_FILE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do we have more data to write?
|
||||||
|
while (Length > 0)
|
||||||
|
{
|
||||||
|
// Make sure we don't write past the end of the current data run
|
||||||
|
WriteLength = (ULONG)min(DataRunLength * Vcb->NtfsInfo.BytesPerCluster, Length);
|
||||||
|
|
||||||
|
// Are we dealing with a sparse data run?
|
||||||
|
if (DataRunStartLCN == -1)
|
||||||
|
{
|
||||||
|
DPRINT1("FIXME: Don't know how to write to sparse files yet! (DataRunStartLCN == -1)\n");
|
||||||
|
return STATUS_NOT_IMPLEMENTED;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// write the data to the disk
|
||||||
|
Status = NtfsWriteDisk(Vcb->StorageDevice,
|
||||||
|
DataRunStartLCN * Vcb->NtfsInfo.BytesPerCluster,
|
||||||
|
WriteLength,
|
||||||
|
Vcb->NtfsInfo.BytesPerSector,
|
||||||
|
(PVOID)SourceBuffer);
|
||||||
|
if (!NT_SUCCESS(Status))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
Length -= WriteLength;
|
||||||
|
SourceBuffer += WriteLength;
|
||||||
|
RealLengthWritten += WriteLength;
|
||||||
|
|
||||||
|
// We finished this request, but there's still data in this data run.
|
||||||
|
if (Length == 0 && WriteLength != DataRunLength * Vcb->NtfsInfo.BytesPerCluster)
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Go to next run in the list.
|
||||||
|
|
||||||
|
if (*DataRun == 0)
|
||||||
|
{
|
||||||
|
// that was the last run
|
||||||
|
if (Length > 0)
|
||||||
|
{
|
||||||
|
// Failed sanity check.
|
||||||
|
DPRINT1("Encountered EOF before expected!\n");
|
||||||
|
return STATUS_END_OF_FILE;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Advance to the next data run
|
||||||
|
CurrentOffset += DataRunLength * Vcb->NtfsInfo.BytesPerCluster;
|
||||||
|
DataRun = DecodeRun(DataRun, &DataRunOffset, &DataRunLength);
|
||||||
|
if (DataRunOffset != -1)
|
||||||
|
{
|
||||||
|
// Normal data run.
|
||||||
|
DataRunStartLCN = LastLCN + DataRunOffset;
|
||||||
|
LastLCN = DataRunStartLCN;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Sparse data run.
|
||||||
|
DataRunStartLCN = -1;
|
||||||
|
}
|
||||||
|
} // end while (Length > 0) [more data to write]
|
||||||
|
|
||||||
|
Context->CacheRun = DataRun;
|
||||||
|
Context->CacheRunOffset = Offset + *RealLengthWritten;
|
||||||
|
Context->CacheRunStartLCN = DataRunStartLCN;
|
||||||
|
Context->CacheRunLength = DataRunLength;
|
||||||
|
Context->CacheRunLastLCN = LastLCN;
|
||||||
|
Context->CacheRunCurrentOffset = CurrentOffset;
|
||||||
|
|
||||||
|
return Status;
|
||||||
|
}
|
||||||
|
|
||||||
NTSTATUS
|
NTSTATUS
|
||||||
ReadFileRecord(PDEVICE_EXTENSION Vcb,
|
ReadFileRecord(PDEVICE_EXTENSION Vcb,
|
||||||
ULONGLONG index,
|
ULONGLONG index,
|
||||||
|
|
|
@ -130,4 +130,62 @@ NtfsGetUserBuffer(PIRP Irp,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name NtfsLockUserBuffer
|
||||||
|
* @implemented
|
||||||
|
*
|
||||||
|
* Ensures the IRP has an MDL Address.
|
||||||
|
*
|
||||||
|
* @param Irp
|
||||||
|
* Irp with the UserBuffer that needs locking
|
||||||
|
*
|
||||||
|
* @param Length
|
||||||
|
* Size of the Irp->UserBuffer, in bytes
|
||||||
|
*
|
||||||
|
* @param Operation
|
||||||
|
* What kind of access does the driver need to the buffer. Set to
|
||||||
|
* IoReadAccess, IoWriteAccess, or IoModifyAccess.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* STATUS_SUCCESS in case of success, STATUS_INSUFFICIENT_RESOURCES
|
||||||
|
* or an exception code otherwise.
|
||||||
|
*
|
||||||
|
* @remarks Trevor Thompson shamelessly ripped this from
|
||||||
|
* VfatLockUserBuffer(). Only the name was changed.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
NTSTATUS
|
||||||
|
NtfsLockUserBuffer(IN PIRP Irp,
|
||||||
|
IN ULONG Length,
|
||||||
|
IN LOCK_OPERATION Operation)
|
||||||
|
{
|
||||||
|
ASSERT(Irp);
|
||||||
|
|
||||||
|
if (Irp->MdlAddress)
|
||||||
|
{
|
||||||
|
return STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
IoAllocateMdl(Irp->UserBuffer, Length, FALSE, FALSE, Irp);
|
||||||
|
|
||||||
|
if (!Irp->MdlAddress)
|
||||||
|
{
|
||||||
|
return STATUS_INSUFFICIENT_RESOURCES;
|
||||||
|
}
|
||||||
|
|
||||||
|
_SEH2_TRY
|
||||||
|
{
|
||||||
|
MmProbeAndLockPages(Irp->MdlAddress, Irp->RequestorMode, Operation);
|
||||||
|
}
|
||||||
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
||||||
|
{
|
||||||
|
IoFreeMdl(Irp->MdlAddress);
|
||||||
|
Irp->MdlAddress = NULL;
|
||||||
|
_SEH2_YIELD(return _SEH2_GetExceptionCode());
|
||||||
|
}
|
||||||
|
_SEH2_END;
|
||||||
|
|
||||||
|
return STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
/* EOF */
|
/* EOF */
|
||||||
|
|
|
@ -550,6 +550,13 @@ NtfsReadDisk(IN PDEVICE_OBJECT DeviceObject,
|
||||||
IN OUT PUCHAR Buffer,
|
IN OUT PUCHAR Buffer,
|
||||||
IN BOOLEAN Override);
|
IN BOOLEAN Override);
|
||||||
|
|
||||||
|
NTSTATUS
|
||||||
|
NtfsWriteDisk(IN PDEVICE_OBJECT DeviceObject,
|
||||||
|
IN LONGLONG StartingOffset,
|
||||||
|
IN ULONG Length,
|
||||||
|
IN ULONG SectorSize,
|
||||||
|
IN PUCHAR Buffer);
|
||||||
|
|
||||||
NTSTATUS
|
NTSTATUS
|
||||||
NtfsReadSectors(IN PDEVICE_OBJECT DeviceObject,
|
NtfsReadSectors(IN PDEVICE_OBJECT DeviceObject,
|
||||||
IN ULONG DiskSector,
|
IN ULONG DiskSector,
|
||||||
|
@ -741,6 +748,14 @@ ReadAttribute(PDEVICE_EXTENSION Vcb,
|
||||||
PCHAR Buffer,
|
PCHAR Buffer,
|
||||||
ULONG Length);
|
ULONG Length);
|
||||||
|
|
||||||
|
NTSTATUS
|
||||||
|
WriteAttribute(PDEVICE_EXTENSION Vcb,
|
||||||
|
PNTFS_ATTR_CONTEXT Context,
|
||||||
|
ULONGLONG Offset,
|
||||||
|
const PUCHAR Buffer,
|
||||||
|
ULONG Length,
|
||||||
|
PULONG LengthWritten);
|
||||||
|
|
||||||
ULONGLONG
|
ULONGLONG
|
||||||
AttributeDataLength(PNTFS_ATTR_RECORD AttrRecord);
|
AttributeDataLength(PNTFS_ATTR_RECORD AttrRecord);
|
||||||
|
|
||||||
|
@ -817,6 +832,21 @@ PVOID
|
||||||
NtfsGetUserBuffer(PIRP Irp,
|
NtfsGetUserBuffer(PIRP Irp,
|
||||||
BOOLEAN Paging);
|
BOOLEAN Paging);
|
||||||
|
|
||||||
|
NTSTATUS
|
||||||
|
NtfsLockUserBuffer(IN PIRP Irp,
|
||||||
|
IN ULONG Length,
|
||||||
|
IN LOCK_OPERATION Operation);
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
BOOLEAN
|
||||||
|
wstrcmpjoki(PWSTR s1, PWSTR s2);
|
||||||
|
|
||||||
|
VOID
|
||||||
|
CdfsSwapString(PWCHAR Out,
|
||||||
|
PUCHAR In,
|
||||||
|
ULONG Count);
|
||||||
|
#endif
|
||||||
|
|
||||||
VOID
|
VOID
|
||||||
NtfsFileFlagsToAttributes(ULONG NtfsAttributes,
|
NtfsFileFlagsToAttributes(ULONG NtfsAttributes,
|
||||||
PULONG FileAttributes);
|
PULONG FileAttributes);
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
* PURPOSE: NTFS filesystem driver
|
* PURPOSE: NTFS filesystem driver
|
||||||
* PROGRAMMERS: Art Yerkes
|
* PROGRAMMERS: Art Yerkes
|
||||||
* Pierre Schweitzer (pierre@reactos.org)
|
* Pierre Schweitzer (pierre@reactos.org)
|
||||||
|
* Trevor Thompson
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* INCLUDES *****************************************************************/
|
/* INCLUDES *****************************************************************/
|
||||||
|
@ -255,14 +256,403 @@ NtfsRead(PNTFS_IRP_CONTEXT IrpContext)
|
||||||
return Status;
|
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 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,
|
||||||
|
PULONG LengthWritten)
|
||||||
|
{
|
||||||
|
NTSTATUS Status = STATUS_NOT_IMPLEMENTED;
|
||||||
|
PNTFS_FCB Fcb;
|
||||||
|
PFILE_RECORD_HEADER FileRecord;
|
||||||
|
PNTFS_ATTR_CONTEXT DataContext;
|
||||||
|
ULONGLONG StreamSize;
|
||||||
|
|
||||||
|
DPRINT("NtfsWriteFile(%p, %p, %p, %u, %u, %x, %p)\n", DeviceExt, FileObject, Buffer, Length, WriteOffset, IrpFlags, 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 (in the NTFS sense of the word) with the data stream for our file
|
||||||
|
DPRINT("Finding Data Attribute...\n");
|
||||||
|
Status = FindAttribute(DeviceExt, FileRecord, AttributeData, Fcb->Stream, wcslen(Fcb->Stream), &DataContext);
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
{
|
||||||
|
// TODO: allocate additional clusters as needed and expand stream
|
||||||
|
DPRINT1("WriteOffset: %lu\tLength: %lu\tStreamSize: %I64u\n", WriteOffset, Length, StreamSize);
|
||||||
|
DPRINT1("TODO: Stream embiggening (appending files) is not yet supported!\n");
|
||||||
|
ReleaseAttributeContext(DataContext);
|
||||||
|
ExFreePoolWithTag(FileRecord, TAG_NTFS);
|
||||||
|
*LengthWritten = 0; // We didn't write anything
|
||||||
|
return STATUS_ACCESS_DENIED; // temporarily; we don't change file sizes yet
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
NTSTATUS
|
||||||
NtfsWrite(PNTFS_IRP_CONTEXT IrpContext)
|
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);
|
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);
|
||||||
|
|
||||||
|
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;
|
IrpContext->Irp->IoStatus.Information = 0;
|
||||||
return STATUS_NOT_SUPPORTED;
|
|
||||||
|
// 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,
|
||||||
|
&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 */
|
/* EOF */
|
||||||
|
|
Loading…
Reference in a new issue