Merge branch 'ntfs_rebase'

This commit is contained in:
Thomas Faber 2017-12-10 12:46:33 +01:00
commit 03be7587eb
No known key found for this signature in database
GPG key ID: 076E7C3D44720826
17 changed files with 8259 additions and 170 deletions

View file

@ -1605,6 +1605,8 @@ HKLM,"SYSTEM\CurrentControlSet\Services\Ntfs","Group",0x00000000,"File System"
HKLM,"SYSTEM\CurrentControlSet\Services\Ntfs","ImagePath",0x00020000,"system32\drivers\ntfs.sys"
HKLM,"SYSTEM\CurrentControlSet\Services\Ntfs","Start",0x00010001,0x00000003
HKLM,"SYSTEM\CurrentControlSet\Services\Ntfs","Type",0x00010001,0x00000002
; un-comment the line below to enable EXPERIMENTAL write-support on NTFS volumes:
;HKLM,"SYSTEM\CurrentControlSet\Services\Ntfs","MyDataDoesNotMatterSoEnableExperimentalWriteSupportForEveryNTFSVolume",0x00010001,0x00000001
; Null device driver
HKLM,"SYSTEM\CurrentControlSet\Services\Null","ErrorControl",0x00010001,0x00000000

View file

@ -2,6 +2,7 @@
list(APPEND SOURCE
attrib.c
blockdev.c
btree.c
cleanup.c
close.c
create.c

File diff suppressed because it is too large Load diff

View 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 *****************************************************************/
@ -51,7 +52,7 @@ NtfsReadDisk(IN PDEVICE_OBJECT DeviceObject,
BOOLEAN AllocatedBuffer = FALSE;
PUCHAR ReadBuffer = Buffer;
DPRINT("NtfsReadDisk(%p, %I64x, %u, %u, %p, %d)\n", DeviceObject, StartingOffset, Length, SectorSize, Buffer, Override);
DPRINT("NtfsReadDisk(%p, %I64x, %lu, %lu, %p, %d)\n", DeviceObject, StartingOffset, Length, SectorSize, Buffer, Override);
KeInitializeEvent(&Event,
NotificationEvent,
@ -129,6 +130,180 @@ NtfsReadDisk(IN PDEVICE_OBJECT DeviceObject,
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, %lu, %lu, %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
NtfsReadSectors(IN PDEVICE_OBJECT DeviceObject,
IN ULONG DiskSector,

File diff suppressed because it is too large Load diff

View file

@ -185,7 +185,7 @@ NtfsOpenFileById(PDEVICE_EXTENSION DeviceExt,
DPRINT1("NtfsOpenFileById(%p, %p, %I64x, %p)\n", DeviceExt, FileObject, MftId, FoundFCB);
ASSERT(MftId < 0x10);
ASSERT(MftId < NTFS_FILE_FIRST_USER_FILE);
if (MftId > 0xb) /* No entries are used yet beyond this */
{
return STATUS_OBJECT_NAME_NOT_FOUND;
@ -246,6 +246,7 @@ NTSTATUS
NtfsOpenFile(PDEVICE_EXTENSION DeviceExt,
PFILE_OBJECT FileObject,
PWSTR FileName,
BOOLEAN CaseSensitive,
PNTFS_FCB * FoundFCB)
{
PNTFS_FCB ParentFcb;
@ -253,7 +254,12 @@ NtfsOpenFile(PDEVICE_EXTENSION DeviceExt,
NTSTATUS Status;
PWSTR AbsFileName = NULL;
DPRINT1("NtfsOpenFile(%p, %p, %S, %p)\n", DeviceExt, FileObject, FileName, FoundFCB);
DPRINT1("NtfsOpenFile(%p, %p, %S, %s, %p)\n",
DeviceExt,
FileObject,
FileName,
CaseSensitive ? "TRUE" : "FALSE",
FoundFCB);
*FoundFCB = NULL;
@ -285,19 +291,20 @@ NtfsOpenFile(PDEVICE_EXTENSION DeviceExt,
Status = NtfsGetFCBForFile(DeviceExt,
&ParentFcb,
&Fcb,
FileName);
FileName,
CaseSensitive);
if (ParentFcb != NULL)
{
NtfsReleaseFCB(DeviceExt,
ParentFcb);
}
if (!NT_SUCCESS (Status))
if (!NT_SUCCESS(Status))
{
DPRINT("Could not make a new FCB, status: %x\n", Status);
if (AbsFileName)
ExFreePool(AbsFileName);
ExFreePoolWithTag(AbsFileName, TAG_NTFS);
return Status;
}
@ -323,7 +330,7 @@ NtfsOpenFile(PDEVICE_EXTENSION DeviceExt,
static
NTSTATUS
NtfsCreateFile(PDEVICE_OBJECT DeviceObject,
PIRP Irp)
PNTFS_IRP_CONTEXT IrpContext)
{
PDEVICE_EXTENSION DeviceExt;
PIO_STACK_LOCATION Stack;
@ -334,12 +341,13 @@ NtfsCreateFile(PDEVICE_OBJECT DeviceObject,
// PWSTR FileName;
NTSTATUS Status;
UNICODE_STRING FullPath;
PIRP Irp = IrpContext->Irp;
DPRINT1("NtfsCreateFile(%p, %p) called\n", DeviceObject, Irp);
DPRINT1("NtfsCreateFile(%p, %p) called\n", DeviceObject, IrpContext);
DeviceExt = DeviceObject->DeviceExtension;
ASSERT(DeviceExt);
Stack = IoGetCurrentIrpStackLocation (Irp);
Stack = IoGetCurrentIrpStackLocation(Irp);
ASSERT(Stack);
RequestedDisposition = ((Stack->Parameters.Create.Options >> 24) & 0xff);
@ -367,7 +375,7 @@ NtfsCreateFile(PDEVICE_OBJECT DeviceObject,
return STATUS_INVALID_PARAMETER;
MFTId = (*(PULONGLONG)FileObject->FileName.Buffer) & NTFS_MFT_MASK;
if (MFTId < 0x10)
if (MFTId < NTFS_FILE_FIRST_USER_FILE)
{
Status = NtfsOpenFileById(DeviceExt, FileObject, MFTId, &Fcb);
}
@ -411,6 +419,7 @@ NtfsCreateFile(PDEVICE_OBJECT DeviceObject,
Status = NtfsOpenFile(DeviceExt,
FileObject,
((RequestedOptions & FILE_OPEN_BY_FILE_ID) ? FullPath.Buffer : FileObject->FileName.Buffer),
BooleanFlagOn(Stack->Flags, SL_CASE_SENSITIVE),
&Fcb);
if (RequestedOptions & FILE_OPEN_BY_FILE_ID)
@ -476,26 +485,126 @@ NtfsCreateFile(PDEVICE_OBJECT DeviceObject,
return Status;
}
/* HUGLY HACK: remain RO so far... */
if (RequestedDisposition == FILE_OVERWRITE ||
RequestedDisposition == FILE_OVERWRITE_IF ||
RequestedDisposition == FILE_SUPERSEDE)
{
DPRINT1("Denying write request on NTFS volume\n");
NtfsCloseFile(DeviceExt, FileObject);
return STATUS_ACCESS_DENIED;
PFILE_RECORD_HEADER fileRecord = NULL;
PNTFS_ATTR_CONTEXT dataContext = NULL;
ULONG DataAttributeOffset;
LARGE_INTEGER Zero;
Zero.QuadPart = 0;
if (!NtfsGlobalData->EnableWriteSupport)
{
DPRINT1("NTFS write-support is EXPERIMENTAL and is disabled by default!\n");
NtfsCloseFile(DeviceExt, FileObject);
return STATUS_ACCESS_DENIED;
}
// TODO: check for appropriate access
ExAcquireResourceExclusiveLite(&(Fcb->MainResource), TRUE);
fileRecord = ExAllocatePoolWithTag(NonPagedPool,
Fcb->Vcb->NtfsInfo.BytesPerFileRecord,
TAG_NTFS);
if (fileRecord)
{
Status = ReadFileRecord(Fcb->Vcb,
Fcb->MFTIndex,
fileRecord);
if (!NT_SUCCESS(Status))
goto DoneOverwriting;
// find the data attribute and set it's length to 0 (TODO: Handle Alternate Data Streams)
Status = FindAttribute(Fcb->Vcb, fileRecord, AttributeData, L"", 0, &dataContext, &DataAttributeOffset);
if (!NT_SUCCESS(Status))
goto DoneOverwriting;
Status = SetAttributeDataLength(FileObject, Fcb, dataContext, DataAttributeOffset, fileRecord, &Zero);
}
else
{
Status = STATUS_NO_MEMORY;
}
DoneOverwriting:
if (fileRecord)
ExFreePoolWithTag(fileRecord, TAG_NTFS);
if (dataContext)
ReleaseAttributeContext(dataContext);
ExReleaseResourceLite(&(Fcb->MainResource));
if (!NT_SUCCESS(Status))
{
NtfsCloseFile(DeviceExt, FileObject);
return Status;
}
if (RequestedDisposition == FILE_SUPERSEDE)
{
Irp->IoStatus.Information = FILE_SUPERSEDED;
}
else
{
Irp->IoStatus.Information = FILE_OVERWRITTEN;
}
}
}
else
{
/* HUGLY HACK: remain RO so far... */
/* HUGLY HACK: Can't create new files yet... */
if (RequestedDisposition == FILE_CREATE ||
RequestedDisposition == FILE_OPEN_IF ||
RequestedDisposition == FILE_OVERWRITE_IF ||
RequestedDisposition == FILE_SUPERSEDE)
{
DPRINT1("Denying write request on NTFS volume\n");
return STATUS_ACCESS_DENIED;
if (!NtfsGlobalData->EnableWriteSupport)
{
DPRINT1("NTFS write-support is EXPERIMENTAL and is disabled by default!\n");
NtfsCloseFile(DeviceExt, FileObject);
return STATUS_ACCESS_DENIED;
}
// Was the user trying to create a directory?
if (RequestedOptions & FILE_DIRECTORY_FILE)
{
// Create the directory on disk
Status = NtfsCreateDirectory(DeviceExt,
FileObject,
BooleanFlagOn(Stack->Flags, SL_CASE_SENSITIVE),
BooleanFlagOn(IrpContext->Flags, IRPCONTEXT_CANWAIT));
}
else
{
// Create the file record on disk
Status = NtfsCreateFileRecord(DeviceExt,
FileObject,
BooleanFlagOn(Stack->Flags, SL_CASE_SENSITIVE),
BooleanFlagOn(IrpContext->Flags, IRPCONTEXT_CANWAIT));
}
if (!NT_SUCCESS(Status))
{
DPRINT1("ERROR: Couldn't create file record!\n");
return Status;
}
// Before we open the file/directory we just created, we need to change the disposition (upper 8 bits of ULONG)
// from create to open, since we already created the file
Stack->Parameters.Create.Options = (ULONG)FILE_OPEN << 24 | RequestedOptions;
// Now we should be able to open the file using NtfsCreateFile()
Status = NtfsCreateFile(DeviceObject, IrpContext);
if (NT_SUCCESS(Status))
{
// We need to change Irp->IoStatus.Information to reflect creation
Irp->IoStatus.Information = FILE_CREATED;
}
return Status;
}
}
@ -541,10 +650,321 @@ NtfsCreate(PNTFS_IRP_CONTEXT IrpContext)
ExAcquireResourceExclusiveLite(&DeviceExt->DirResource,
TRUE);
Status = NtfsCreateFile(DeviceObject,
IrpContext->Irp);
IrpContext);
ExReleaseResourceLite(&DeviceExt->DirResource);
return Status;
}
/**
* @name NtfsCreateDirectory()
* @implemented
*
* Creates a file record for a new directory and saves it to the MFT. Adds the filename attribute of the
* created directory to the parent directory's index.
*
* @param DeviceExt
* Points to the target disk's DEVICE_EXTENSION
*
* @param FileObject
* Pointer to a FILE_OBJECT describing the directory to be created
*
* @param CaseSensitive
* Boolean indicating if the function should operate in case-sensitive mode. This will be TRUE
* if an application created the folder with the FILE_FLAG_POSIX_SEMANTICS flag.
*
* @param CanWait
* Boolean indicating if the function is allowed to wait for exclusive access to the master file table.
* This will only be relevant if the MFT doesn't have any free file records and needs to be enlarged.
*
* @return
* STATUS_SUCCESS on success.
* STATUS_INSUFFICIENT_RESOURCES if unable to allocate memory for the file record.
* STATUS_CANT_WAIT if CanWait was FALSE and the function needed to resize the MFT but
* couldn't get immediate, exclusive access to it.
*/
NTSTATUS
NtfsCreateDirectory(PDEVICE_EXTENSION DeviceExt,
PFILE_OBJECT FileObject,
BOOLEAN CaseSensitive,
BOOLEAN CanWait)
{
NTSTATUS Status = STATUS_SUCCESS;
PFILE_RECORD_HEADER FileRecord;
PNTFS_ATTR_RECORD NextAttribute;
PFILENAME_ATTRIBUTE FilenameAttribute;
ULONGLONG ParentMftIndex;
ULONGLONG FileMftIndex;
PB_TREE Tree;
PINDEX_ROOT_ATTRIBUTE NewIndexRoot;
ULONG MaxIndexRootSize;
ULONG RootLength;
DPRINT1("NtfsCreateFileRecord(%p, %p, %s, %s)\n",
DeviceExt,
FileObject,
CaseSensitive ? "TRUE" : "FALSE",
CanWait ? "TRUE" : "FALSE");
// Start with an empty file record
FileRecord = NtfsCreateEmptyFileRecord(DeviceExt);
if (!FileRecord)
{
DPRINT1("ERROR: Unable to allocate memory for file record!\n");
return STATUS_INSUFFICIENT_RESOURCES;
}
// Set the directory flag
FileRecord->Flags |= FRH_DIRECTORY;
// find where the first attribute will be added
NextAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord + FileRecord->AttributeOffset);
// add first attribute, $STANDARD_INFORMATION
AddStandardInformation(FileRecord, NextAttribute);
// advance NextAttribute pointer to the next attribute
NextAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)NextAttribute + (ULONG_PTR)NextAttribute->Length);
// Add the $FILE_NAME attribute
AddFileName(FileRecord, NextAttribute, DeviceExt, FileObject, CaseSensitive, &ParentMftIndex);
// save a pointer to the filename attribute
FilenameAttribute = (PFILENAME_ATTRIBUTE)((ULONG_PTR)NextAttribute + NextAttribute->Resident.ValueOffset);
// advance NextAttribute pointer to the next attribute
NextAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)NextAttribute + (ULONG_PTR)NextAttribute->Length);
// Create an empty b-tree to represent our new index
Status = CreateEmptyBTree(&Tree);
if (!NT_SUCCESS(Status))
{
DPRINT1("ERROR: Failed to create empty B-Tree!\n");
ExFreePoolWithTag(FileRecord, TAG_NTFS);
return Status;
}
// Calculate maximum size of index root
MaxIndexRootSize = DeviceExt->NtfsInfo.BytesPerFileRecord
- ((ULONG_PTR)NextAttribute - (ULONG_PTR)FileRecord)
- sizeof(ULONG) * 2;
// Create a new index record from the tree
Status = CreateIndexRootFromBTree(DeviceExt,
Tree,
MaxIndexRootSize,
&NewIndexRoot,
&RootLength);
if (!NT_SUCCESS(Status))
{
DPRINT1("ERROR: Unable to create empty index root!\n");
DestroyBTree(Tree);
ExFreePoolWithTag(FileRecord, TAG_NTFS);
return Status;
}
// We're done with the B-Tree
DestroyBTree(Tree);
// add the $INDEX_ROOT attribute
Status = AddIndexRoot(DeviceExt, FileRecord, NextAttribute, NewIndexRoot, RootLength, L"$I30", 4);
if (!NT_SUCCESS(Status))
{
DPRINT1("ERROR: Failed to add index root to new file record!\n");
ExFreePoolWithTag(FileRecord, TAG_NTFS);
return Status;
}
#ifndef NDEBUG
NtfsDumpFileRecord(DeviceExt, FileRecord);
#endif
// Now that we've built the file record in memory, we need to store it in the MFT.
Status = AddNewMftEntry(FileRecord, DeviceExt, &FileMftIndex, CanWait);
if (NT_SUCCESS(Status))
{
// The highest 2 bytes should be the sequence number, unless the parent happens to be root
if (FileMftIndex == NTFS_FILE_ROOT)
FileMftIndex = FileMftIndex + ((ULONGLONG)NTFS_FILE_ROOT << 48);
else
FileMftIndex = FileMftIndex + ((ULONGLONG)FileRecord->SequenceNumber << 48);
DPRINT1("New File Reference: 0x%016I64x\n", FileMftIndex);
// Add the filename attribute to the filename-index of the parent directory
Status = NtfsAddFilenameToDirectory(DeviceExt,
ParentMftIndex,
FileMftIndex,
FilenameAttribute,
CaseSensitive);
}
ExFreePoolWithTag(NewIndexRoot, TAG_NTFS);
ExFreePoolWithTag(FileRecord, TAG_NTFS);
return Status;
}
/**
* @name NtfsCreateEmptyFileRecord
* @implemented
*
* Creates a new, empty file record, with no attributes.
*
* @param DeviceExt
* Pointer to the DEVICE_EXTENSION of the target volume the file record will be stored on.
*
* @return
* A pointer to the newly-created FILE_RECORD_HEADER if the function succeeds, NULL otherwise.
*/
PFILE_RECORD_HEADER
NtfsCreateEmptyFileRecord(PDEVICE_EXTENSION DeviceExt)
{
PFILE_RECORD_HEADER FileRecord;
PNTFS_ATTR_RECORD NextAttribute;
DPRINT1("NtfsCreateEmptyFileRecord(%p)\n", DeviceExt);
// allocate memory for file record
FileRecord = ExAllocatePoolWithTag(NonPagedPool,
DeviceExt->NtfsInfo.BytesPerFileRecord,
TAG_NTFS);
if (!FileRecord)
{
DPRINT1("ERROR: Unable to allocate memory for file record!\n");
return NULL;
}
RtlZeroMemory(FileRecord, DeviceExt->NtfsInfo.BytesPerFileRecord);
FileRecord->Ntfs.Type = NRH_FILE_TYPE;
// calculate USA offset and count
FileRecord->Ntfs.UsaOffset = FIELD_OFFSET(FILE_RECORD_HEADER, MFTRecordNumber) + sizeof(ULONG);
// size of USA (in ULONG's) will be 1 (for USA number) + 1 for every sector the file record uses
FileRecord->BytesAllocated = DeviceExt->NtfsInfo.BytesPerFileRecord;
FileRecord->Ntfs.UsaCount = (FileRecord->BytesAllocated / DeviceExt->NtfsInfo.BytesPerSector) + 1;
// setup other file record fields
FileRecord->SequenceNumber = 1;
FileRecord->AttributeOffset = FileRecord->Ntfs.UsaOffset + (2 * FileRecord->Ntfs.UsaCount);
FileRecord->AttributeOffset = ALIGN_UP_BY(FileRecord->AttributeOffset, ATTR_RECORD_ALIGNMENT);
FileRecord->Flags = FRH_IN_USE;
FileRecord->BytesInUse = FileRecord->AttributeOffset + sizeof(ULONG) * 2;
// find where the first attribute will be added
NextAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord + FileRecord->AttributeOffset);
// mark the (temporary) end of the file-record
NextAttribute->Type = AttributeEnd;
NextAttribute->Length = FILE_RECORD_END;
return FileRecord;
}
/**
* @name NtfsCreateFileRecord()
* @implemented
*
* Creates a file record and saves it to the MFT. Adds the filename attribute of the
* created file to the parent directory's index.
*
* @param DeviceExt
* Points to the target disk's DEVICE_EXTENSION
*
* @param FileObject
* Pointer to a FILE_OBJECT describing the file to be created
*
* @param CanWait
* Boolean indicating if the function is allowed to wait for exclusive access to the master file table.
* This will only be relevant if the MFT doesn't have any free file records and needs to be enlarged.
*
* @return
* STATUS_SUCCESS on success.
* STATUS_INSUFFICIENT_RESOURCES if unable to allocate memory for the file record.
* STATUS_CANT_WAIT if CanWait was FALSE and the function needed to resize the MFT but
* couldn't get immediate, exclusive access to it.
*/
NTSTATUS
NtfsCreateFileRecord(PDEVICE_EXTENSION DeviceExt,
PFILE_OBJECT FileObject,
BOOLEAN CaseSensitive,
BOOLEAN CanWait)
{
NTSTATUS Status = STATUS_SUCCESS;
PFILE_RECORD_HEADER FileRecord;
PNTFS_ATTR_RECORD NextAttribute;
PFILENAME_ATTRIBUTE FilenameAttribute;
ULONGLONG ParentMftIndex;
ULONGLONG FileMftIndex;
DPRINT1("NtfsCreateFileRecord(%p, %p, %s, %s)\n",
DeviceExt,
FileObject,
CaseSensitive ? "TRUE" : "FALSE",
CanWait ? "TRUE" : "FALSE");
// allocate memory for file record
FileRecord = NtfsCreateEmptyFileRecord(DeviceExt);
if (!FileRecord)
{
DPRINT1("ERROR: Unable to allocate memory for file record!\n");
return STATUS_INSUFFICIENT_RESOURCES;
}
// find where the first attribute will be added
NextAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord + FileRecord->AttributeOffset);
// add first attribute, $STANDARD_INFORMATION
AddStandardInformation(FileRecord, NextAttribute);
// advance NextAttribute pointer to the next attribute
NextAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)NextAttribute + (ULONG_PTR)NextAttribute->Length);
// Add the $FILE_NAME attribute
AddFileName(FileRecord, NextAttribute, DeviceExt, FileObject, CaseSensitive, &ParentMftIndex);
// save a pointer to the filename attribute
FilenameAttribute = (PFILENAME_ATTRIBUTE)((ULONG_PTR)NextAttribute + NextAttribute->Resident.ValueOffset);
// advance NextAttribute pointer to the next attribute
NextAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)NextAttribute + (ULONG_PTR)NextAttribute->Length);
// add the $DATA attribute
AddData(FileRecord, NextAttribute);
#ifndef NDEBUG
// dump file record in memory (for debugging)
NtfsDumpFileRecord(DeviceExt, FileRecord);
#endif
// Now that we've built the file record in memory, we need to store it in the MFT.
Status = AddNewMftEntry(FileRecord, DeviceExt, &FileMftIndex, CanWait);
if (NT_SUCCESS(Status))
{
// The highest 2 bytes should be the sequence number, unless the parent happens to be root
if (FileMftIndex == NTFS_FILE_ROOT)
FileMftIndex = FileMftIndex + ((ULONGLONG)NTFS_FILE_ROOT << 48);
else
FileMftIndex = FileMftIndex + ((ULONGLONG)FileRecord->SequenceNumber << 48);
DPRINT1("New File Reference: 0x%016I64x\n", FileMftIndex);
// Add the filename attribute to the filename-index of the parent directory
Status = NtfsAddFilenameToDirectory(DeviceExt,
ParentMftIndex,
FileMftIndex,
FilenameAttribute,
CaseSensitive);
}
ExFreePoolWithTag(FileRecord, TAG_NTFS);
return Status;
}
/* EOF */

View file

@ -34,7 +34,6 @@
/* FUNCTIONS ****************************************************************/
ULONGLONG
NtfsGetFileSize(PDEVICE_EXTENSION DeviceExt,
PFILE_RECORD_HEADER FileRecord,
@ -47,11 +46,11 @@ NtfsGetFileSize(PDEVICE_EXTENSION DeviceExt,
NTSTATUS Status;
PNTFS_ATTR_CONTEXT DataContext;
Status = FindAttribute(DeviceExt, FileRecord, AttributeData, Stream, StreamLength, &DataContext);
Status = FindAttribute(DeviceExt, FileRecord, AttributeData, Stream, StreamLength, &DataContext, NULL);
if (NT_SUCCESS(Status))
{
Size = AttributeDataLength(&DataContext->Record);
Allocated = AttributeAllocatedLength(&DataContext->Record);
Size = AttributeDataLength(DataContext->pRecord);
Allocated = AttributeAllocatedLength(DataContext->pRecord);
ReleaseAttributeContext(DataContext);
}
@ -62,16 +61,16 @@ NtfsGetFileSize(PDEVICE_EXTENSION DeviceExt,
static NTSTATUS
NtfsGetNameInformation(PDEVICE_EXTENSION DeviceExt,
PFILE_RECORD_HEADER FileRecord,
ULONGLONG MFTIndex,
PFILE_NAMES_INFORMATION Info,
ULONG BufferLength)
NtfsGetNamesInformation(PDEVICE_EXTENSION DeviceExt,
PFILE_RECORD_HEADER FileRecord,
ULONGLONG MFTIndex,
PFILE_NAMES_INFORMATION Info,
ULONG BufferLength)
{
ULONG Length;
PFILENAME_ATTRIBUTE FileName;
DPRINT("NtfsGetNameInformation() called\n");
DPRINT("NtfsGetNamesInformation() called\n");
FileName = GetBestFileNameFromRecord(DeviceExt, FileRecord);
if (FileName == NULL)
@ -377,7 +376,8 @@ NtfsQueryDirectory(PNTFS_IRP_CONTEXT IrpContext)
&Ccb->Entry,
&FileRecord,
&MFTRecord,
Fcb->MFTIndex);
Fcb->MFTIndex,
BooleanFlagOn(Stack->Flags, SL_CASE_SENSITIVE));
if (NT_SUCCESS(Status))
{
@ -395,12 +395,12 @@ NtfsQueryDirectory(PNTFS_IRP_CONTEXT IrpContext)
switch (FileInformationClass)
{
case FileNameInformation:
Status = NtfsGetNameInformation(DeviceExtension,
FileRecord,
MFTRecord,
(PFILE_NAMES_INFORMATION)Buffer,
BufferLength);
case FileNamesInformation:
Status = NtfsGetNamesInformation(DeviceExtension,
FileRecord,
MFTRecord,
(PFILE_NAMES_INFORMATION)Buffer,
BufferLength);
break;
case FileDirectoryInformation:

View file

@ -81,6 +81,18 @@ NtfsDispatch(PNTFS_IRP_CONTEXT IrpContext)
Status = NtfsQueryInformation(IrpContext);
break;
case IRP_MJ_SET_INFORMATION:
if (!NtfsGlobalData->EnableWriteSupport)
{
DPRINT1("NTFS write-support is EXPERIMENTAL and is disabled by default!\n");
Status = STATUS_ACCESS_DENIED;
}
else
{
Status = NtfsSetInformation(IrpContext);
}
break;
case IRP_MJ_DIRECTORY_CONTROL:
Status = NtfsDirectoryControl(IrpContext);
break;
@ -94,7 +106,15 @@ NtfsDispatch(PNTFS_IRP_CONTEXT IrpContext)
break;
case IRP_MJ_WRITE:
Status = NtfsWrite(IrpContext);
if (!NtfsGlobalData->EnableWriteSupport)
{
DPRINT1("NTFS write-support is EXPERIMENTAL and is disabled by default!\n");
Status = STATUS_ACCESS_DENIED;
}
else
{
Status = NtfsWrite(IrpContext);
}
break;
case IRP_MJ_CLOSE:

View file

@ -35,8 +35,8 @@
/* FUNCTIONS ****************************************************************/
static
PWCHAR
NtfsGetNextPathElement(PWCHAR FileName)
PCWSTR
NtfsGetNextPathElement(PCWSTR FileName)
{
if (*FileName == L'\0')
{
@ -55,7 +55,7 @@ NtfsGetNextPathElement(PWCHAR FileName)
static
VOID
NtfsWSubString(PWCHAR pTarget,
const PWCHAR pSource,
PCWSTR pSource,
size_t pLength)
{
wcsncpy(pTarget, pSource, pLength);
@ -506,6 +506,7 @@ static NTSTATUS
NtfsDirFindFile(PNTFS_VCB Vcb,
PNTFS_FCB DirectoryFcb,
PWSTR FileToFind,
BOOLEAN CaseSensitive,
PNTFS_FCB *FoundFCB)
{
NTSTATUS Status;
@ -517,7 +518,12 @@ NtfsDirFindFile(PNTFS_VCB Vcb,
PNTFS_ATTR_CONTEXT DataContext;
USHORT Length = 0;
DPRINT1("NtfsDirFindFile(%p, %p, %S, %p)\n", Vcb, DirectoryFcb, FileToFind, FoundFCB);
DPRINT1("NtfsDirFindFile(%p, %p, %S, %s, %p)\n",
Vcb,
DirectoryFcb,
FileToFind,
CaseSensitive ? "TRUE" : "FALSE",
FoundFCB);
*FoundFCB = NULL;
RtlInitUnicodeString(&File, FileToFind);
@ -551,7 +557,7 @@ NtfsDirFindFile(PNTFS_VCB Vcb,
DPRINT1("Will now look for file '%wZ' with stream '%S'\n", &File, Colon);
}
Status = NtfsLookupFileAt(Vcb, &File, &FileRecord, &MFTIndex, CurrentDir);
Status = NtfsLookupFileAt(Vcb, &File, CaseSensitive, &FileRecord, &MFTIndex, CurrentDir);
if (!NT_SUCCESS(Status))
{
return Status;
@ -568,7 +574,7 @@ NtfsDirFindFile(PNTFS_VCB Vcb,
}
else if (Colon != 0)
{
Status = FindAttribute(Vcb, FileRecord, AttributeData, Colon, wcslen(Colon), &DataContext);
Status = FindAttribute(Vcb, FileRecord, AttributeData, Colon, wcslen(Colon), &DataContext, NULL);
if (!NT_SUCCESS(Status))
{
return STATUS_OBJECT_NAME_NOT_FOUND;
@ -587,20 +593,22 @@ NTSTATUS
NtfsGetFCBForFile(PNTFS_VCB Vcb,
PNTFS_FCB *pParentFCB,
PNTFS_FCB *pFCB,
const PWSTR pFileName)
PCWSTR pFileName,
BOOLEAN CaseSensitive)
{
NTSTATUS Status;
WCHAR pathName [MAX_PATH];
WCHAR elementName [MAX_PATH];
PWCHAR currentElement;
PCWSTR currentElement;
PNTFS_FCB FCB;
PNTFS_FCB parentFCB;
DPRINT("NtfsGetFCBForFile(%p, %p, %p, '%S')\n",
DPRINT("NtfsGetFCBForFile(%p, %p, %p, '%S', %s)\n",
Vcb,
pParentFCB,
pFCB,
pFileName);
pFileName,
CaseSensitive ? "TRUE" : "FALSE");
/* Dummy code */
// FCB = NtfsOpenRootFCB(Vcb);
@ -609,7 +617,7 @@ NtfsGetFCBForFile(PNTFS_VCB Vcb,
#if 1
/* Trivial case, open of the root directory on volume */
if (pFileName [0] == L'\0' || wcscmp(pFileName, L"\\") == 0)
if (pFileName[0] == L'\0' || wcscmp(pFileName, L"\\") == 0)
{
DPRINT("returning root FCB\n");
@ -677,7 +685,7 @@ NtfsGetFCBForFile(PNTFS_VCB Vcb,
NtfsGetNextPathElement(currentElement) - currentElement);
DPRINT(" elementName:%S\n", elementName);
Status = NtfsDirFindFile(Vcb, parentFCB, elementName, &FCB);
Status = NtfsDirFindFile(Vcb, parentFCB, elementName, CaseSensitive, &FCB);
if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
{
*pParentFCB = parentFCB;
@ -741,14 +749,14 @@ NtfsReadFCBAttribute(PNTFS_VCB Vcb,
return Status;
}
Status = FindAttribute(Vcb, FileRecord, Type, Name, NameLength, &AttrCtxt);
Status = FindAttribute(Vcb, FileRecord, Type, Name, NameLength, &AttrCtxt, NULL);
if (!NT_SUCCESS(Status))
{
ExFreePoolWithTag(FileRecord, TAG_NTFS);
return Status;
}
AttrLength = AttributeDataLength(&AttrCtxt->Record);
AttrLength = AttributeDataLength(AttrCtxt->pRecord);
*Data = ExAllocatePoolWithTag(NonPagedPool, AttrLength, TAG_NTFS);
if (*Data == NULL)
{

View file

@ -289,6 +289,128 @@ NtfsGetSteamInformation(PNTFS_FCB Fcb,
return Status;
}
// Convert enum value to friendly name
const PCSTR
GetInfoClassName(FILE_INFORMATION_CLASS infoClass)
{
const PCSTR fileInfoClassNames[] = { "???????",
"FileDirectoryInformation",
"FileFullDirectoryInformation",
"FileBothDirectoryInformation",
"FileBasicInformation",
"FileStandardInformation",
"FileInternalInformation",
"FileEaInformation",
"FileAccessInformation",
"FileNameInformation",
"FileRenameInformation",
"FileLinkInformation",
"FileNamesInformation",
"FileDispositionInformation",
"FilePositionInformation",
"FileFullEaInformation",
"FileModeInformation",
"FileAlignmentInformation",
"FileAllInformation",
"FileAllocationInformation",
"FileEndOfFileInformation",
"FileAlternateNameInformation",
"FileStreamInformation",
"FilePipeInformation",
"FilePipeLocalInformation",
"FilePipeRemoteInformation",
"FileMailslotQueryInformation",
"FileMailslotSetInformation",
"FileCompressionInformation",
"FileObjectIdInformation",
"FileCompletionInformation",
"FileMoveClusterInformation",
"FileQuotaInformation",
"FileReparsePointInformation",
"FileNetworkOpenInformation",
"FileAttributeTagInformation",
"FileTrackingInformation",
"FileIdBothDirectoryInformation",
"FileIdFullDirectoryInformation",
"FileValidDataLengthInformation",
"FileShortNameInformation",
"FileIoCompletionNotificationInformation",
"FileIoStatusBlockRangeInformation",
"FileIoPriorityHintInformation",
"FileSfioReserveInformation",
"FileSfioVolumeInformation",
"FileHardLinkInformation",
"FileProcessIdsUsingFileInformation",
"FileNormalizedNameInformation",
"FileNetworkPhysicalNameInformation",
"FileIdGlobalTxDirectoryInformation",
"FileIsRemoteDeviceInformation",
"FileAttributeCacheInformation",
"FileNumaNodeInformation",
"FileStandardLinkInformation",
"FileRemoteProtocolInformation",
"FileReplaceCompletionInformation",
"FileMaximumInformation",
"FileDirectoryInformation",
"FileFullDirectoryInformation",
"FileBothDirectoryInformation",
"FileBasicInformation",
"FileStandardInformation",
"FileInternalInformation",
"FileEaInformation",
"FileAccessInformation",
"FileNameInformation",
"FileRenameInformation",
"FileLinkInformation",
"FileNamesInformation",
"FileDispositionInformation",
"FilePositionInformation",
"FileFullEaInformation",
"FileModeInformation",
"FileAlignmentInformation",
"FileAllInformation",
"FileAllocationInformation",
"FileEndOfFileInformation",
"FileAlternateNameInformation",
"FileStreamInformation",
"FilePipeInformation",
"FilePipeLocalInformation",
"FilePipeRemoteInformation",
"FileMailslotQueryInformation",
"FileMailslotSetInformation",
"FileCompressionInformation",
"FileObjectIdInformation",
"FileCompletionInformation",
"FileMoveClusterInformation",
"FileQuotaInformation",
"FileReparsePointInformation",
"FileNetworkOpenInformation",
"FileAttributeTagInformation",
"FileTrackingInformation",
"FileIdBothDirectoryInformation",
"FileIdFullDirectoryInformation",
"FileValidDataLengthInformation",
"FileShortNameInformation",
"FileIoCompletionNotificationInformation",
"FileIoStatusBlockRangeInformation",
"FileIoPriorityHintInformation",
"FileSfioReserveInformation",
"FileSfioVolumeInformation",
"FileHardLinkInformation",
"FileProcessIdsUsingFileInformation",
"FileNormalizedNameInformation",
"FileNetworkPhysicalNameInformation",
"FileIdGlobalTxDirectoryInformation",
"FileIsRemoteDeviceInformation",
"FileAttributeCacheInformation",
"FileNumaNodeInformation",
"FileStandardLinkInformation",
"FileRemoteProtocolInformation",
"FileReplaceCompletionInformation",
"FileMaximumInformation" };
return fileInfoClassNames[infoClass];
}
/*
* FUNCTION: Retrieve the specified file information
*/
@ -376,12 +498,12 @@ NtfsQueryInformation(PNTFS_IRP_CONTEXT IrpContext)
case FileAlternateNameInformation:
case FileAllInformation:
DPRINT1("Unimplemented information class %u\n", FileInformationClass);
DPRINT1("Unimplemented information class: %s\n", GetInfoClassName(FileInformationClass));
Status = STATUS_NOT_IMPLEMENTED;
break;
default:
DPRINT1("Unimplemented information class %u\n", FileInformationClass);
DPRINT1("Unimplemented information class: %s\n", GetInfoClassName(FileInformationClass));
Status = STATUS_INVALID_PARAMETER;
}
@ -396,4 +518,267 @@ NtfsQueryInformation(PNTFS_IRP_CONTEXT IrpContext)
return Status;
}
/**
* @name NtfsSetEndOfFile
* @implemented
*
* Sets the end of file (file size) for a given file.
*
* @param Fcb
* Pointer to an NTFS_FCB which describes the target file. Fcb->MainResource should have been
* acquired with ExAcquireResourceSharedLite().
*
* @param FileObject
* Pointer to a FILE_OBJECT describing the target file.
*
* @param DeviceExt
* Points to the target disk's DEVICE_EXTENSION
*
* @param IrpFlags
* ULONG describing the flags of the original IRP request (Irp->Flags).
*
* @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 NewFileSize
* Pointer to a LARGE_INTEGER which indicates the new end of file (file size).
*
* @return
* STATUS_SUCCESS if successful,
* STATUS_USER_MAPPED_FILE if trying to truncate a file but MmCanFileBeTruncated() returned false,
* STATUS_OBJECT_NAME_NOT_FOUND if there was no $DATA attribute associated with the target file,
* STATUS_INVALID_PARAMETER if there was no $FILENAME attribute associated with the target file,
* STATUS_INSUFFICIENT_RESOURCES if an allocation failed,
* STATUS_ACCESS_DENIED if target file is a volume or if paging is involved.
*
* @remarks As this function sets the size of a file at the file-level
* (and not at the attribute level) it's not recommended to use this
* function alongside functions that operate on the data attribute directly.
*
*/
NTSTATUS
NtfsSetEndOfFile(PNTFS_FCB Fcb,
PFILE_OBJECT FileObject,
PDEVICE_EXTENSION DeviceExt,
ULONG IrpFlags,
BOOLEAN CaseSensitive,
PLARGE_INTEGER NewFileSize)
{
LARGE_INTEGER CurrentFileSize;
PFILE_RECORD_HEADER FileRecord;
PNTFS_ATTR_CONTEXT DataContext;
ULONG AttributeOffset;
NTSTATUS Status = STATUS_SUCCESS;
ULONGLONG AllocationSize;
PFILENAME_ATTRIBUTE FileNameAttribute;
ULONGLONG ParentMFTId;
UNICODE_STRING FileName;
// Allocate non-paged memory for the file record
FileRecord = ExAllocatePoolWithTag(NonPagedPool, DeviceExt->NtfsInfo.BytesPerFileRecord, TAG_NTFS);
if (FileRecord == NULL)
{
DPRINT1("Couldn't allocate memory for file record!");
return STATUS_INSUFFICIENT_RESOURCES;
}
// read the file record
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);
CurrentFileSize.QuadPart = NtfsGetFileSize(DeviceExt, FileRecord, L"", 0, NULL);
// Are we trying to decrease the file size?
if (NewFileSize->QuadPart < CurrentFileSize.QuadPart)
{
// Is the file mapped?
if (!MmCanFileBeTruncated(FileObject->SectionObjectPointer,
NewFileSize))
{
DPRINT1("Couldn't decrease file size!\n");
ExFreePoolWithTag(FileRecord, TAG_NTFS);
return STATUS_USER_MAPPED_FILE;
}
}
// 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))
{
DPRINT1("No '%S' data stream associated with file!\n", Fcb->Stream);
ExFreePoolWithTag(FileRecord, TAG_NTFS);
return Status;
}
// Get the size of the data attribute
CurrentFileSize.QuadPart = AttributeDataLength(DataContext->pRecord);
// Are we enlarging the attribute?
if (NewFileSize->QuadPart > CurrentFileSize.QuadPart)
{
// is increasing the stream size not allowed?
if ((Fcb->Flags & FCB_IS_VOLUME) ||
(IrpFlags & IRP_PAGING_IO))
{
// TODO - just fail for now
ReleaseAttributeContext(DataContext);
ExFreePoolWithTag(FileRecord, TAG_NTFS);
return STATUS_ACCESS_DENIED;
}
}
// set the attribute data length
Status = SetAttributeDataLength(FileObject, Fcb, DataContext, AttributeOffset, FileRecord, NewFileSize);
if (!NT_SUCCESS(Status))
{
ReleaseAttributeContext(DataContext);
ExFreePoolWithTag(FileRecord, TAG_NTFS);
return Status;
}
// now we need to update this file's size in every directory index entry that references it
// TODO: expand to work with every filename / hardlink stored in the file record.
FileNameAttribute = GetBestFileNameFromRecord(Fcb->Vcb, FileRecord);
if (FileNameAttribute == NULL)
{
DPRINT1("Unable to find FileName attribute associated with file!\n");
ReleaseAttributeContext(DataContext);
ExFreePoolWithTag(FileRecord, TAG_NTFS);
return STATUS_INVALID_PARAMETER;
}
ParentMFTId = FileNameAttribute->DirectoryFileReferenceNumber & NTFS_MFT_MASK;
FileName.Buffer = FileNameAttribute->Name;
FileName.Length = FileNameAttribute->NameLength * sizeof(WCHAR);
FileName.MaximumLength = FileName.Length;
AllocationSize = AttributeAllocatedLength(DataContext->pRecord);
Status = UpdateFileNameRecord(Fcb->Vcb,
ParentMFTId,
&FileName,
FALSE,
NewFileSize->QuadPart,
AllocationSize,
CaseSensitive);
ReleaseAttributeContext(DataContext);
ExFreePoolWithTag(FileRecord, TAG_NTFS);
return Status;
}
/**
* @name NtfsSetInformation
* @implemented
*
* Sets the specified file information.
*
* @param IrpContext
* Points to an NTFS_IRP_CONTEXT which describes the set operation
*
* @return
* STATUS_SUCCESS if successful,
* STATUS_NOT_IMPLEMENTED if trying to set an unimplemented information class,
* STATUS_USER_MAPPED_FILE if trying to truncate a file but MmCanFileBeTruncated() returned false,
* STATUS_OBJECT_NAME_NOT_FOUND if there was no $DATA attribute associated with the target file,
* STATUS_INVALID_PARAMETER if there was no $FILENAME attribute associated with the target file,
* STATUS_INSUFFICIENT_RESOURCES if an allocation failed,
* STATUS_ACCESS_DENIED if target file is a volume or if paging is involved.
*
* @remarks Called by NtfsDispatch() in response to an IRP_MJ_SET_INFORMATION request.
* Only the FileEndOfFileInformation InformationClass is fully implemented. FileAllocationInformation
* is a hack and not a true implementation, but it's enough to make SetEndOfFile() work.
* All other information classes are TODO.
*
*/
NTSTATUS
NtfsSetInformation(PNTFS_IRP_CONTEXT IrpContext)
{
FILE_INFORMATION_CLASS FileInformationClass;
PIO_STACK_LOCATION Stack;
PDEVICE_EXTENSION DeviceExt;
PFILE_OBJECT FileObject;
PNTFS_FCB Fcb;
PVOID SystemBuffer;
ULONG BufferLength;
PIRP Irp;
PDEVICE_OBJECT DeviceObject;
NTSTATUS Status = STATUS_NOT_IMPLEMENTED;
DPRINT1("NtfsSetInformation(%p)\n", IrpContext);
Irp = IrpContext->Irp;
Stack = IrpContext->Stack;
DeviceObject = IrpContext->DeviceObject;
DeviceExt = DeviceObject->DeviceExtension;
FileInformationClass = Stack->Parameters.QueryFile.FileInformationClass;
FileObject = IrpContext->FileObject;
Fcb = FileObject->FsContext;
SystemBuffer = Irp->AssociatedIrp.SystemBuffer;
BufferLength = Stack->Parameters.QueryFile.Length;
if (!ExAcquireResourceSharedLite(&Fcb->MainResource,
BooleanFlagOn(IrpContext->Flags, IRPCONTEXT_CANWAIT)))
{
return NtfsMarkIrpContextForQueue(IrpContext);
}
switch (FileInformationClass)
{
PFILE_END_OF_FILE_INFORMATION EndOfFileInfo;
/* TODO: Allocation size is not actually the same as file end for NTFS,
however, few applications are likely to make the distinction. */
case FileAllocationInformation:
DPRINT1("FIXME: Using hacky method of setting FileAllocationInformation.\n");
case FileEndOfFileInformation:
EndOfFileInfo = (PFILE_END_OF_FILE_INFORMATION)SystemBuffer;
Status = NtfsSetEndOfFile(Fcb,
FileObject,
DeviceExt,
Irp->Flags,
BooleanFlagOn(Stack->Flags, SL_CASE_SENSITIVE),
&EndOfFileInfo->EndOfFile);
break;
// TODO: all other information classes
default:
DPRINT1("FIXME: Unimplemented information class: %s\n", GetInfoClassName(FileInformationClass));
Status = STATUS_NOT_IMPLEMENTED;
}
ExReleaseResourceLite(&Fcb->MainResource);
if (NT_SUCCESS(Status))
Irp->IoStatus.Information =
Stack->Parameters.QueryFile.Length - BufferLength;
else
Irp->IoStatus.Information = 0;
return Status;
}
/* EOF */

View file

@ -295,7 +295,13 @@ NtfsGetVolumeData(PDEVICE_OBJECT DeviceObject,
return Status;
}
Status = FindAttribute(DeviceExt, DeviceExt->MasterFileTable, AttributeData, L"", 0, &DeviceExt->MFTContext);
Status = FindAttribute(DeviceExt,
DeviceExt->MasterFileTable,
AttributeData,
L"",
0,
&DeviceExt->MFTContext,
&DeviceExt->MftDataOffset);
if (!NT_SUCCESS(Status))
{
DPRINT1("Can't find data attribute for Master File Table.\n");
@ -333,11 +339,11 @@ NtfsGetVolumeData(PDEVICE_OBJECT DeviceObject,
NtfsDumpFileAttributes(DeviceExt, VolumeRecord);
/* Get volume name */
Status = FindAttribute(DeviceExt, VolumeRecord, AttributeVolumeName, L"", 0, &AttrCtxt);
Status = FindAttribute(DeviceExt, VolumeRecord, AttributeVolumeName, L"", 0, &AttrCtxt, NULL);
if (NT_SUCCESS(Status) && AttrCtxt->Record.Resident.ValueLength != 0)
if (NT_SUCCESS(Status) && AttrCtxt->pRecord->Resident.ValueLength != 0)
{
Attribute = &AttrCtxt->Record;
Attribute = AttrCtxt->pRecord;
DPRINT("Data length %lu\n", AttributeDataLength(Attribute));
NtfsInfo->VolumeLabelLength =
min (Attribute->Resident.ValueLength, MAXIMUM_VOLUME_LABEL_LENGTH);
@ -374,11 +380,11 @@ NtfsGetVolumeData(PDEVICE_OBJECT DeviceObject,
DeviceExt->VolumeFcb = VolumeFcb;
/* Get volume information */
Status = FindAttribute(DeviceExt, VolumeRecord, AttributeVolumeInformation, L"", 0, &AttrCtxt);
Status = FindAttribute(DeviceExt, VolumeRecord, AttributeVolumeInformation, L"", 0, &AttrCtxt, NULL);
if (NT_SUCCESS(Status) && AttrCtxt->Record.Resident.ValueLength != 0)
if (NT_SUCCESS(Status) && AttrCtxt->pRecord->Resident.ValueLength != 0)
{
Attribute = &AttrCtxt->Record;
Attribute = AttrCtxt->pRecord;
DPRINT("Data length %lu\n", AttributeDataLength (Attribute));
VolumeInfo = (PVOID)((ULONG_PTR)Attribute + Attribute->Resident.ValueOffset);
@ -804,7 +810,7 @@ GetVolumeBitmap(PDEVICE_EXTENSION DeviceExt,
return Status;
}
Status = FindAttribute(DeviceExt, BitmapRecord, AttributeData, L"", 0, &DataContext);
Status = FindAttribute(DeviceExt, BitmapRecord, AttributeData, L"", 0, &DataContext, NULL);
if (!NT_SUCCESS(Status))
{
DPRINT1("Failed find $DATA for bitmap: %lx\n", Status);

File diff suppressed because it is too large Load diff

View file

@ -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 */

View file

@ -58,6 +58,8 @@ DriverEntry(PDRIVER_OBJECT DriverObject,
UNICODE_STRING DeviceName = RTL_CONSTANT_STRING(DEVICE_NAME);
NTSTATUS Status;
PDEVICE_OBJECT DeviceObject;
OBJECT_ATTRIBUTES Attributes;
HANDLE DriverKey = NULL;
TRACE_(NTFS, "DriverEntry(%p, '%wZ')\n", DriverObject, RegistryPath);
@ -84,6 +86,42 @@ DriverEntry(PDRIVER_OBJECT DriverObject,
ExInitializeResourceLite(&NtfsGlobalData->Resource);
NtfsGlobalData->EnableWriteSupport = FALSE;
// Read registry to determine if write support should be enabled
InitializeObjectAttributes(&Attributes,
RegistryPath,
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
NULL,
NULL);
Status = ZwOpenKey(&DriverKey, KEY_READ, &Attributes);
if (NT_SUCCESS(Status))
{
UNICODE_STRING ValueName;
UCHAR Buffer[sizeof(KEY_VALUE_PARTIAL_INFORMATION) + sizeof(ULONG)];
PKEY_VALUE_PARTIAL_INFORMATION Value = (PKEY_VALUE_PARTIAL_INFORMATION)Buffer;
ULONG ValueLength = sizeof(Buffer);
ULONG ResultLength;
RtlInitUnicodeString(&ValueName, L"MyDataDoesNotMatterSoEnableExperimentalWriteSupportForEveryNTFSVolume");
Status = ZwQueryValueKey(DriverKey,
&ValueName,
KeyValuePartialInformation,
Value,
ValueLength,
&ResultLength);
if (NT_SUCCESS(Status) && Value->Data[0] == TRUE)
{
DPRINT1("\tEnabling write support on ALL NTFS volumes!\n");
NtfsGlobalData->EnableWriteSupport = TRUE;
}
ZwClose(DriverKey);
}
/* Keep trace of Driver Object */
NtfsGlobalData->DriverObject = DriverObject;
@ -118,7 +156,7 @@ DriverEntry(PDRIVER_OBJECT DriverObject,
IoRegisterFileSystem(NtfsGlobalData->DeviceObject);
ObReferenceObject(NtfsGlobalData->DeviceObject);
return Status;
return STATUS_SUCCESS;
}
@ -139,6 +177,7 @@ NtfsInitializeFunctionPointers(PDRIVER_OBJECT DriverObject)
DriverObject->MajorFunction[IRP_MJ_READ] = NtfsFsdDispatch;
DriverObject->MajorFunction[IRP_MJ_WRITE] = NtfsFsdDispatch;
DriverObject->MajorFunction[IRP_MJ_QUERY_INFORMATION] = NtfsFsdDispatch;
DriverObject->MajorFunction[IRP_MJ_SET_INFORMATION] = NtfsFsdDispatch;
DriverObject->MajorFunction[IRP_MJ_QUERY_VOLUME_INFORMATION] = NtfsFsdDispatch;
DriverObject->MajorFunction[IRP_MJ_SET_VOLUME_INFORMATION] = NtfsFsdDispatch;
DriverObject->MajorFunction[IRP_MJ_DIRECTORY_CONTROL] = NtfsFsdDispatch;

View file

@ -116,6 +116,7 @@ typedef struct
NTFS_INFO NtfsInfo;
ULONG MftDataOffset;
ULONG Flags;
ULONG OpenHandleCount;
@ -151,6 +152,7 @@ typedef struct
FAST_IO_DISPATCH FastIoDispatch;
NPAGED_LOOKASIDE_LIST IrpContextLookasideList;
NPAGED_LOOKASIDE_LIST FcbLookasideList;
BOOLEAN EnableWriteSupport;
} NTFS_GLOBAL_DATA, *PNTFS_GLOBAL_DATA;
@ -175,6 +177,10 @@ typedef enum
AttributeEnd = 0xFFFFFFFF
} ATTRIBUTE_TYPE, *PATTRIBUTE_TYPE;
// FILE_RECORD_END seems to follow AttributeEnd in every file record starting with $Quota.
// No clue what data is being represented here.
#define FILE_RECORD_END 0x11477982
#define NTFS_FILE_MFT 0
#define NTFS_FILE_MFTMIRR 1
#define NTFS_FILE_LOGFILE 2
@ -187,6 +193,7 @@ typedef enum
#define NTFS_FILE_QUOTA 9
#define NTFS_FILE_UPCASE 10
#define NTFS_FILE_EXTEND 11
#define NTFS_FILE_FIRST_USER_FILE 16
#define NTFS_MFT_MASK 0x0000FFFFFFFFFFFFULL
@ -201,6 +208,9 @@ typedef enum
#define INDEX_ROOT_SMALL 0x0
#define INDEX_ROOT_LARGE 0x1
#define INDEX_NODE_SMALL 0x0
#define INDEX_NODE_LARGE 0x1
#define NTFS_INDEX_ENTRY_NODE 1
#define NTFS_INDEX_ENTRY_END 2
@ -217,6 +227,9 @@ typedef enum
#define NTFS_FILE_TYPE_COMPRESSED 0x800
#define NTFS_FILE_TYPE_DIRECTORY 0x10000000
/* Indexed Flag in Resident attributes - still somewhat speculative */
#define RA_INDEXED 0x01
typedef struct
{
ULONG Type; /* Magic number 'FILE' */
@ -287,6 +300,16 @@ typedef struct
};
} NTFS_ATTR_RECORD, *PNTFS_ATTR_RECORD;
// The beginning and length of an attribute record are always aligned to an 8-byte boundary,
// relative to the beginning of the file record.
#define ATTR_RECORD_ALIGNMENT 8
// Data runs are aligned to a 4-byte boundary, relative to the start of the attribute record
#define DATA_RUN_ALIGNMENT 4
// Value offset is aligned to a 4-byte boundary, relative to the start of the attribute record
#define VALUE_OFFSET_ALIGNMENT 4
typedef struct
{
ULONGLONG CreationTime;
@ -389,6 +412,34 @@ typedef struct
FILENAME_ATTRIBUTE FileName;
} INDEX_ENTRY_ATTRIBUTE, *PINDEX_ENTRY_ATTRIBUTE;
struct _B_TREE_FILENAME_NODE;
typedef struct _B_TREE_FILENAME_NODE B_TREE_FILENAME_NODE;
// Keys are arranged in nodes as an ordered, linked list
typedef struct _B_TREE_KEY
{
struct _B_TREE_KEY *NextKey;
B_TREE_FILENAME_NODE *LesserChild; // Child-Node. All the keys in this node will be sorted before IndexEntry
PINDEX_ENTRY_ATTRIBUTE IndexEntry; // must be last member for FIELD_OFFSET
}B_TREE_KEY, *PB_TREE_KEY;
// Every Node is just an ordered list of keys.
// Sub-nodes can be found attached to a key (if they exist).
// A key's sub-node precedes that key in the ordered list.
typedef struct _B_TREE_FILENAME_NODE
{
ULONG KeyCount;
BOOLEAN HasValidVCN;
BOOLEAN DiskNeedsUpdating;
ULONGLONG VCN;
PB_TREE_KEY FirstKey;
} B_TREE_FILENAME_NODE, *PB_TREE_FILENAME_NODE;
typedef struct
{
PB_TREE_FILENAME_NODE RootNode;
} B_TREE, *PB_TREE;
typedef struct
{
ULONGLONG Unknown1;
@ -433,7 +484,9 @@ typedef struct _NTFS_ATTR_CONTEXT
ULONGLONG CacheRunLength;
LONGLONG CacheRunLastLCN;
ULONGLONG CacheRunCurrentOffset;
NTFS_ATTR_RECORD Record;
LARGE_MCB DataRunsMCB;
ULONGLONG FileMFTIndex;
PNTFS_ATTR_RECORD pRecord;
} NTFS_ATTR_CONTEXT, *PNTFS_ATTR_CONTEXT;
#define FCB_CACHE_INITIALIZED 0x0001
@ -483,8 +536,15 @@ typedef struct _FIND_ATTR_CONTXT
PNTFS_ATTR_RECORD LastAttr;
PNTFS_ATTR_RECORD NonResidentStart;
PNTFS_ATTR_RECORD NonResidentEnd;
ULONG Offset;
} FIND_ATTR_CONTXT, *PFIND_ATTR_CONTXT;
typedef struct
{
USHORT USN;
USHORT Array[];
} FIXUP_ARRAY, *PFIXUP_ARRAY;
extern PNTFS_GLOBAL_DATA NtfsGlobalData;
FORCEINLINE
@ -504,11 +564,75 @@ NtfsMarkIrpContextForQueue(PNTFS_IRP_CONTEXT IrpContext)
//VOID
//NtfsDumpAttribute(PATTRIBUTE Attribute);
NTSTATUS
AddBitmap(PNTFS_VCB Vcb,
PFILE_RECORD_HEADER FileRecord,
PNTFS_ATTR_RECORD AttributeAddress,
PCWSTR Name,
USHORT NameLength);
NTSTATUS
AddData(PFILE_RECORD_HEADER FileRecord,
PNTFS_ATTR_RECORD AttributeAddress);
NTSTATUS
AddRun(PNTFS_VCB Vcb,
PNTFS_ATTR_CONTEXT AttrContext,
ULONG AttrOffset,
PFILE_RECORD_HEADER FileRecord,
ULONGLONG NextAssignedCluster,
ULONG RunLength);
NTSTATUS
AddIndexAllocation(PNTFS_VCB Vcb,
PFILE_RECORD_HEADER FileRecord,
PNTFS_ATTR_RECORD AttributeAddress,
PCWSTR Name,
USHORT NameLength);
NTSTATUS
AddIndexRoot(PNTFS_VCB Vcb,
PFILE_RECORD_HEADER FileRecord,
PNTFS_ATTR_RECORD AttributeAddress,
PINDEX_ROOT_ATTRIBUTE NewIndexRoot,
ULONG RootLength,
PCWSTR Name,
USHORT NameLength);
NTSTATUS
AddFileName(PFILE_RECORD_HEADER FileRecord,
PNTFS_ATTR_RECORD AttributeAddress,
PDEVICE_EXTENSION DeviceExt,
PFILE_OBJECT FileObject,
BOOLEAN CaseSensitive,
PULONGLONG ParentMftIndex);
NTSTATUS
AddStandardInformation(PFILE_RECORD_HEADER FileRecord,
PNTFS_ATTR_RECORD AttributeAddress);
NTSTATUS
ConvertDataRunsToLargeMCB(PUCHAR DataRun,
PLARGE_MCB DataRunsMCB,
PULONGLONG pNextVBN);
NTSTATUS
ConvertLargeMCBToDataRuns(PLARGE_MCB DataRunsMCB,
PUCHAR RunBuffer,
ULONG MaxBufferSize,
PULONG UsedBufferSize);
PUCHAR
DecodeRun(PUCHAR DataRun,
LONGLONG *DataRunOffset,
ULONGLONG *DataRunLength);
ULONG GetFileNameAttributeLength(PFILENAME_ATTRIBUTE FileNameAttribute);
VOID
NtfsDumpDataRuns(PVOID StartOfRun,
ULONGLONG CurrentLCN);
VOID
NtfsDumpFileAttributes(PDEVICE_EXTENSION Vcb,
PFILE_RECORD_HEADER FileRecord);
@ -522,6 +646,15 @@ GetFileNameFromRecord(PDEVICE_EXTENSION Vcb,
PFILE_RECORD_HEADER FileRecord,
UCHAR NameType);
UCHAR
GetPackedByteCount(LONGLONG NumberToPack,
BOOLEAN IsSigned);
NTSTATUS
GetLastClusterInDataRun(PDEVICE_EXTENSION Vcb,
PNTFS_ATTR_RECORD Attribute,
PULONGLONG LastCluster);
PFILENAME_ATTRIBUTE
GetBestFileNameFromRecord(PDEVICE_EXTENSION Vcb,
PFILE_RECORD_HEADER FileRecord);
@ -540,6 +673,13 @@ FindNextAttribute(PFIND_ATTR_CONTXT Context,
VOID
FindCloseAttribute(PFIND_ATTR_CONTXT Context);
NTSTATUS
FreeClusters(PNTFS_VCB Vcb,
PNTFS_ATTR_CONTEXT AttrContext,
ULONG AttrOffset,
PFILE_RECORD_HEADER FileRecord,
ULONG ClustersToFree);
/* blockdev.c */
NTSTATUS
@ -550,6 +690,13 @@ NtfsReadDisk(IN PDEVICE_OBJECT DeviceObject,
IN OUT PUCHAR Buffer,
IN BOOLEAN Override);
NTSTATUS
NtfsWriteDisk(IN PDEVICE_OBJECT DeviceObject,
IN LONGLONG StartingOffset,
IN ULONG Length,
IN ULONG SectorSize,
IN const PUCHAR Buffer);
NTSTATUS
NtfsReadSectors(IN PDEVICE_OBJECT DeviceObject,
IN ULONG DiskSector,
@ -568,6 +715,98 @@ NtfsDeviceIoControl(IN PDEVICE_OBJECT DeviceObject,
IN BOOLEAN Override);
/* btree.c */
LONG
CompareTreeKeys(PB_TREE_KEY Key1,
PB_TREE_KEY Key2,
BOOLEAN CaseSensitive);
NTSTATUS
CreateBTreeFromIndex(PDEVICE_EXTENSION Vcb,
PFILE_RECORD_HEADER FileRecordWithIndex,
/*PCWSTR IndexName,*/
PNTFS_ATTR_CONTEXT IndexRootContext,
PINDEX_ROOT_ATTRIBUTE IndexRoot,
PB_TREE *NewTree);
NTSTATUS
CreateIndexRootFromBTree(PDEVICE_EXTENSION DeviceExt,
PB_TREE Tree,
ULONG MaxIndexSize,
PINDEX_ROOT_ATTRIBUTE *IndexRoot,
ULONG *Length);
NTSTATUS
DemoteBTreeRoot(PB_TREE Tree);
VOID
DestroyBTree(PB_TREE Tree);
VOID
DestroyBTreeNode(PB_TREE_FILENAME_NODE Node);
VOID
DumpBTree(PB_TREE Tree);
VOID
DumpBTreeKey(PB_TREE Tree,
PB_TREE_KEY Key,
ULONG Number,
ULONG Depth);
VOID
DumpBTreeNode(PB_TREE Tree,
PB_TREE_FILENAME_NODE Node,
ULONG Number,
ULONG Depth);
NTSTATUS
CreateEmptyBTree(PB_TREE *NewTree);
ULONGLONG
GetAllocationOffsetFromVCN(PDEVICE_EXTENSION DeviceExt,
ULONG IndexBufferSize,
ULONGLONG Vcn);
ULONGLONG
GetIndexEntryVCN(PINDEX_ENTRY_ATTRIBUTE IndexEntry);
ULONG
GetSizeOfIndexEntries(PB_TREE_FILENAME_NODE Node);
NTSTATUS
NtfsInsertKey(PB_TREE Tree,
ULONGLONG FileReference,
PFILENAME_ATTRIBUTE FileNameAttribute,
PB_TREE_FILENAME_NODE Node,
BOOLEAN CaseSensitive,
ULONG MaxIndexRootSize,
ULONG IndexRecordSize,
PB_TREE_KEY *MedianKey,
PB_TREE_FILENAME_NODE *NewRightHandSibling);
NTSTATUS
SplitBTreeNode(PB_TREE Tree,
PB_TREE_FILENAME_NODE Node,
PB_TREE_KEY *MedianKey,
PB_TREE_FILENAME_NODE *NewRightHandSibling,
BOOLEAN CaseSensitive);
NTSTATUS
UpdateIndexAllocation(PDEVICE_EXTENSION DeviceExt,
PB_TREE Tree,
ULONG IndexBufferSize,
PFILE_RECORD_HEADER FileRecord);
NTSTATUS
UpdateIndexNode(PDEVICE_EXTENSION DeviceExt,
PFILE_RECORD_HEADER FileRecord,
PB_TREE_FILENAME_NODE Node,
ULONG IndexBufferSize,
PNTFS_ATTR_CONTEXT IndexAllocationContext,
ULONG IndexAllocationOffset);
/* close.c */
NTSTATUS
@ -589,6 +828,20 @@ NtfsClose(PNTFS_IRP_CONTEXT IrpContext);
NTSTATUS
NtfsCreate(PNTFS_IRP_CONTEXT IrpContext);
NTSTATUS
NtfsCreateDirectory(PDEVICE_EXTENSION DeviceExt,
PFILE_OBJECT FileObject,
BOOLEAN CaseSensitive,
BOOLEAN CanWait);
PFILE_RECORD_HEADER
NtfsCreateEmptyFileRecord(PDEVICE_EXTENSION DeviceExt);
NTSTATUS
NtfsCreateFileRecord(PDEVICE_EXTENSION DeviceExt,
PFILE_OBJECT FileObject,
BOOLEAN CaseSensitive,
BOOLEAN CanWait);
/* devctl.c */
@ -695,7 +948,8 @@ NTSTATUS
NtfsGetFCBForFile(PNTFS_VCB Vcb,
PNTFS_FCB *pParentFCB,
PNTFS_FCB *pFCB,
const PWSTR pFileName);
PCWSTR pFileName,
BOOLEAN CaseSensitive);
NTSTATUS
NtfsReadFCBAttribute(PNTFS_VCB Vcb,
@ -720,6 +974,16 @@ NtfsMakeFCBFromDirEntry(PNTFS_VCB Vcb,
NTSTATUS
NtfsQueryInformation(PNTFS_IRP_CONTEXT IrpContext);
NTSTATUS
NtfsSetEndOfFile(PNTFS_FCB Fcb,
PFILE_OBJECT FileObject,
PDEVICE_EXTENSION DeviceExt,
ULONG IrpFlags,
BOOLEAN CaseSensitive,
PLARGE_INTEGER NewFileSize);
NTSTATUS
NtfsSetInformation(PNTFS_IRP_CONTEXT IrpContext);
/* fsctl.c */
@ -728,6 +992,22 @@ NtfsFileSystemControl(PNTFS_IRP_CONTEXT IrpContext);
/* mft.c */
NTSTATUS
NtfsAddFilenameToDirectory(PDEVICE_EXTENSION DeviceExt,
ULONGLONG DirectoryMftIndex,
ULONGLONG FileReferenceNumber,
PFILENAME_ATTRIBUTE FilenameAttribute,
BOOLEAN CaseSensitive);
NTSTATUS
AddNewMftEntry(PFILE_RECORD_HEADER FileRecord,
PDEVICE_EXTENSION DeviceExt,
PULONGLONG DestinationIndex,
BOOLEAN CanWait);
VOID
NtfsDumpData(ULONG_PTR Buffer, ULONG Length);
PNTFS_ATTR_CONTEXT
PrepareAttributeContext(PNTFS_ATTR_RECORD AttrRecord);
@ -741,24 +1021,112 @@ ReadAttribute(PDEVICE_EXTENSION Vcb,
PCHAR Buffer,
ULONG Length);
NTSTATUS
WriteAttribute(PDEVICE_EXTENSION Vcb,
PNTFS_ATTR_CONTEXT Context,
ULONGLONG Offset,
const PUCHAR Buffer,
ULONG Length,
PULONG LengthWritten,
PFILE_RECORD_HEADER FileRecord);
ULONGLONG
AttributeDataLength(PNTFS_ATTR_RECORD AttrRecord);
ULONG
NTSTATUS
InternalSetResidentAttributeLength(PDEVICE_EXTENSION DeviceExt,
PNTFS_ATTR_CONTEXT AttrContext,
PFILE_RECORD_HEADER FileRecord,
ULONG AttrOffset,
ULONG DataSize);
PNTFS_ATTR_RECORD
MoveAttributes(PDEVICE_EXTENSION DeviceExt,
PNTFS_ATTR_RECORD FirstAttributeToMove,
ULONG FirstAttributeOffset,
ULONG_PTR MoveTo);
NTSTATUS
SetAttributeDataLength(PFILE_OBJECT FileObject,
PNTFS_FCB Fcb,
PNTFS_ATTR_CONTEXT AttrContext,
ULONG AttrOffset,
PFILE_RECORD_HEADER FileRecord,
PLARGE_INTEGER DataSize);
VOID
SetFileRecordEnd(PFILE_RECORD_HEADER FileRecord,
PNTFS_ATTR_RECORD AttrEnd,
ULONG EndMarker);
NTSTATUS
SetNonResidentAttributeDataLength(PDEVICE_EXTENSION Vcb,
PNTFS_ATTR_CONTEXT AttrContext,
ULONG AttrOffset,
PFILE_RECORD_HEADER FileRecord,
PLARGE_INTEGER DataSize);
NTSTATUS
SetResidentAttributeDataLength(PDEVICE_EXTENSION Vcb,
PNTFS_ATTR_CONTEXT AttrContext,
ULONG AttrOffset,
PFILE_RECORD_HEADER FileRecord,
PLARGE_INTEGER DataSize);
ULONGLONG
AttributeAllocatedLength(PNTFS_ATTR_RECORD AttrRecord);
BOOLEAN
CompareFileName(PUNICODE_STRING FileName,
PINDEX_ENTRY_ATTRIBUTE IndexEntry,
BOOLEAN DirSearch,
BOOLEAN CaseSensitive);
NTSTATUS
UpdateMftMirror(PNTFS_VCB Vcb);
NTSTATUS
ReadFileRecord(PDEVICE_EXTENSION Vcb,
ULONGLONG index,
PFILE_RECORD_HEADER file);
NTSTATUS
UpdateIndexEntryFileNameSize(PDEVICE_EXTENSION Vcb,
PFILE_RECORD_HEADER MftRecord,
PCHAR IndexRecord,
ULONG IndexBlockSize,
PINDEX_ENTRY_ATTRIBUTE FirstEntry,
PINDEX_ENTRY_ATTRIBUTE LastEntry,
PUNICODE_STRING FileName,
PULONG StartEntry,
PULONG CurrentEntry,
BOOLEAN DirSearch,
ULONGLONG NewDataSize,
ULONGLONG NewAllocatedSize,
BOOLEAN CaseSensitive);
NTSTATUS
UpdateFileNameRecord(PDEVICE_EXTENSION Vcb,
ULONGLONG ParentMFTIndex,
PUNICODE_STRING FileName,
BOOLEAN DirSearch,
ULONGLONG NewDataSize,
ULONGLONG NewAllocationSize,
BOOLEAN CaseSensitive);
NTSTATUS
UpdateFileRecord(PDEVICE_EXTENSION Vcb,
ULONGLONG MftIndex,
PFILE_RECORD_HEADER FileRecord);
NTSTATUS
FindAttribute(PDEVICE_EXTENSION Vcb,
PFILE_RECORD_HEADER MftRecord,
ULONG Type,
PCWSTR Name,
ULONG NameLength,
PNTFS_ATTR_CONTEXT * AttrCtx);
PNTFS_ATTR_CONTEXT * AttrCtx,
PULONG Offset);
VOID
ReadVCN(PDEVICE_EXTENSION Vcb,
@ -772,6 +1140,10 @@ NTSTATUS
FixupUpdateSequenceArray(PDEVICE_EXTENSION Vcb,
PNTFS_RECORD_HEADER Record);
NTSTATUS
AddFixupArray(PDEVICE_EXTENSION Vcb,
PNTFS_RECORD_HEADER Record);
NTSTATUS
ReadLCN(PDEVICE_EXTENSION Vcb,
ULONGLONG lcn,
@ -786,23 +1158,39 @@ EnumerAttribute(PFILE_RECORD_HEADER file,
NTSTATUS
NtfsLookupFile(PDEVICE_EXTENSION Vcb,
PUNICODE_STRING PathName,
BOOLEAN CaseSensitive,
PFILE_RECORD_HEADER *FileRecord,
PULONGLONG MFTIndex);
NTSTATUS
NtfsLookupFileAt(PDEVICE_EXTENSION Vcb,
PUNICODE_STRING PathName,
BOOLEAN CaseSensitive,
PFILE_RECORD_HEADER *FileRecord,
PULONGLONG MFTIndex,
ULONGLONG CurrentMFTIndex);
VOID
NtfsDumpFileRecord(PDEVICE_EXTENSION Vcb,
PFILE_RECORD_HEADER FileRecord);
NTSTATUS
NtfsFindFileAt(PDEVICE_EXTENSION Vcb,
PUNICODE_STRING SearchPattern,
PULONG FirstEntry,
PFILE_RECORD_HEADER *FileRecord,
PULONGLONG MFTIndex,
ULONGLONG CurrentMFTIndex);
ULONGLONG CurrentMFTIndex,
BOOLEAN CaseSensitive);
NTSTATUS
NtfsFindMftRecord(PDEVICE_EXTENSION Vcb,
ULONGLONG MFTIndex,
PUNICODE_STRING FileName,
PULONG FirstEntry,
BOOLEAN DirSearch,
BOOLEAN CaseSensitive,
ULONGLONG *OutMFTIndex);
/* misc.c */
@ -817,6 +1205,21 @@ PVOID
NtfsGetUserBuffer(PIRP Irp,
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
NtfsFileFlagsToAttributes(ULONG NtfsAttributes,
PULONG FileAttributes);
@ -833,6 +1236,13 @@ NtfsWrite(PNTFS_IRP_CONTEXT IrpContext);
/* volinfo.c */
NTSTATUS
NtfsAllocateClusters(PDEVICE_EXTENSION DeviceExt,
ULONG FirstDesiredCluster,
ULONG DesiredClusters,
PULONG FirstAssignedCluster,
PULONG AssignedClusters);
ULONGLONG
NtfsGetFreeClusters(PDEVICE_EXTENSION DeviceExt);

View file

@ -22,6 +22,7 @@
* PURPOSE: NTFS filesystem driver
* PROGRAMMERS: Art Yerkes
* Pierre Schweitzer (pierre@reactos.org)
* Trevor Thompson
*/
/* INCLUDES *****************************************************************/
@ -59,7 +60,7 @@ NtfsReadFile(PDEVICE_EXTENSION DeviceExt,
PCHAR ReadBuffer = (PCHAR)Buffer;
ULONGLONG StreamSize;
DPRINT1("NtfsReadFile(%p, %p, %p, %u, %u, %x, %p)\n", DeviceExt, FileObject, Buffer, Length, ReadOffset, IrpFlags, LengthRead);
DPRINT1("NtfsReadFile(%p, %p, %p, %lu, %lu, %lx, %p)\n", DeviceExt, FileObject, Buffer, Length, ReadOffset, IrpFlags, LengthRead);
*LengthRead = 0;
@ -94,7 +95,7 @@ NtfsReadFile(PDEVICE_EXTENSION DeviceExt,
}
Status = FindAttribute(DeviceExt, FileRecord, AttributeData, Fcb->Stream, wcslen(Fcb->Stream), &DataContext);
Status = FindAttribute(DeviceExt, FileRecord, AttributeData, Fcb->Stream, wcslen(Fcb->Stream), &DataContext, NULL);
if (!NT_SUCCESS(Status))
{
NTSTATUS BrowseStatus;
@ -125,7 +126,7 @@ NtfsReadFile(PDEVICE_EXTENSION DeviceExt,
return Status;
}
StreamSize = AttributeDataLength(&DataContext->Record);
StreamSize = AttributeDataLength(DataContext->pRecord);
if (ReadOffset >= StreamSize)
{
DPRINT1("Reading beyond stream end!\n");
@ -148,7 +149,7 @@ NtfsReadFile(PDEVICE_EXTENSION DeviceExt,
/* do we need to extend RealLength by one sector? */
if (RealLength + RealReadOffset < ReadOffset + Length)
{
if (RealReadOffset + RealLength + DeviceExt->NtfsInfo.BytesPerSector <= AttributeAllocatedLength(&DataContext->Record))
if (RealReadOffset + RealLength + DeviceExt->NtfsInfo.BytesPerSector <= AttributeAllocatedLength(DataContext->pRecord))
RealLength += DeviceExt->NtfsInfo.BytesPerSector;
}
@ -164,7 +165,7 @@ NtfsReadFile(PDEVICE_EXTENSION DeviceExt,
AllocatedBuffer = TRUE;
}
DPRINT1("Effective read: %lu at %lu for stream '%S'\n", RealLength, RealReadOffset, Fcb->Stream);
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)
{
@ -183,7 +184,7 @@ NtfsReadFile(PDEVICE_EXTENSION DeviceExt,
*LengthRead = ToRead;
DPRINT1("%lu got read\n", *LengthRead);
DPRINT("%lu got read\n", *LengthRead);
if (AllocatedBuffer)
{
@ -255,14 +256,465 @@ NtfsRead(PNTFS_IRP_CONTEXT IrpContext)
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 = 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->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);
ExFreePoolWithTag(FileRecord, TAG_NTFS);
*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);
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, FileRecord);
// 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
NtfsWrite(PNTFS_IRP_CONTEXT IrpContext)
{
DPRINT("NtfsWrite(IrpContext %p)\n",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;
IrpContext->Irp->IoStatus.Information = 0;
return STATUS_NOT_SUPPORTED;
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;
// 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 */

View file

@ -62,14 +62,14 @@ NtfsGetFreeClusters(PDEVICE_EXTENSION DeviceExt)
return 0;
}
Status = FindAttribute(DeviceExt, BitmapRecord, AttributeData, L"", 0, &DataContext);
Status = FindAttribute(DeviceExt, BitmapRecord, AttributeData, L"", 0, &DataContext, NULL);
if (!NT_SUCCESS(Status))
{
ExFreePoolWithTag(BitmapRecord, TAG_NTFS);
return 0;
}
BitmapDataSize = AttributeDataLength(&DataContext->Record);
BitmapDataSize = AttributeDataLength(DataContext->pRecord);
ASSERT((BitmapDataSize * 8) >= DeviceExt->NtfsInfo.ClusterCount);
BitmapData = ExAllocatePoolWithTag(NonPagedPool, ROUND_UP(BitmapDataSize, DeviceExt->NtfsInfo.BytesPerSector), TAG_NTFS);
if (BitmapData == NULL)
@ -99,6 +99,113 @@ NtfsGetFreeClusters(PDEVICE_EXTENSION DeviceExt)
return FreeClusters;
}
/**
* NtfsAllocateClusters
* Allocates a run of clusters. The run allocated might be smaller than DesiredClusters.
*/
NTSTATUS
NtfsAllocateClusters(PDEVICE_EXTENSION DeviceExt,
ULONG FirstDesiredCluster,
ULONG DesiredClusters,
PULONG FirstAssignedCluster,
PULONG AssignedClusters)
{
NTSTATUS Status;
PFILE_RECORD_HEADER BitmapRecord;
PNTFS_ATTR_CONTEXT DataContext;
ULONGLONG BitmapDataSize;
PUCHAR BitmapData;
ULONGLONG FreeClusters = 0;
RTL_BITMAP Bitmap;
ULONG AssignedRun;
ULONG LengthWritten;
DPRINT1("NtfsAllocateClusters(%p, %lu, %lu, %p, %p)\n", DeviceExt, FirstDesiredCluster, DesiredClusters, FirstAssignedCluster, AssignedClusters);
BitmapRecord = ExAllocatePoolWithTag(NonPagedPool,
DeviceExt->NtfsInfo.BytesPerFileRecord,
TAG_NTFS);
if (BitmapRecord == NULL)
{
return STATUS_INSUFFICIENT_RESOURCES;
}
Status = ReadFileRecord(DeviceExt, NTFS_FILE_BITMAP, BitmapRecord);
if (!NT_SUCCESS(Status))
{
ExFreePoolWithTag(BitmapRecord, TAG_NTFS);
return Status;
}
Status = FindAttribute(DeviceExt, BitmapRecord, AttributeData, L"", 0, &DataContext, NULL);
if (!NT_SUCCESS(Status))
{
ExFreePoolWithTag(BitmapRecord, TAG_NTFS);
return Status;
}
BitmapDataSize = AttributeDataLength(DataContext->pRecord);
BitmapDataSize = min(BitmapDataSize, 0xffffffff);
ASSERT((BitmapDataSize * 8) >= DeviceExt->NtfsInfo.ClusterCount);
BitmapData = ExAllocatePoolWithTag(NonPagedPool, ROUND_UP(BitmapDataSize, DeviceExt->NtfsInfo.BytesPerSector), TAG_NTFS);
if (BitmapData == NULL)
{
ReleaseAttributeContext(DataContext);
ExFreePoolWithTag(BitmapRecord, TAG_NTFS);
return STATUS_INSUFFICIENT_RESOURCES;
}
DPRINT1("Total clusters: %I64x\n", DeviceExt->NtfsInfo.ClusterCount);
DPRINT1("Total clusters in bitmap: %I64x\n", BitmapDataSize * 8);
DPRINT1("Diff in size: %I64d B\n", ((BitmapDataSize * 8) - DeviceExt->NtfsInfo.ClusterCount) * DeviceExt->NtfsInfo.SectorsPerCluster * DeviceExt->NtfsInfo.BytesPerSector);
ReadAttribute(DeviceExt, DataContext, 0, (PCHAR)BitmapData, (ULONG)BitmapDataSize);
RtlInitializeBitMap(&Bitmap, (PULONG)BitmapData, DeviceExt->NtfsInfo.ClusterCount);
FreeClusters = RtlNumberOfClearBits(&Bitmap);
if (FreeClusters < DesiredClusters)
{
ReleaseAttributeContext(DataContext);
ExFreePoolWithTag(BitmapData, TAG_NTFS);
ExFreePoolWithTag(BitmapRecord, TAG_NTFS);
return STATUS_DISK_FULL;
}
// TODO: Observe MFT reservation zone
// Can we get one contiguous run?
AssignedRun = RtlFindClearBitsAndSet(&Bitmap, DesiredClusters, FirstDesiredCluster);
if (AssignedRun != 0xFFFFFFFF)
{
*FirstAssignedCluster = AssignedRun;
*AssignedClusters = DesiredClusters;
}
else
{
// we can't get one contiguous run
*AssignedClusters = RtlFindNextForwardRunClear(&Bitmap, FirstDesiredCluster, FirstAssignedCluster);
if (*AssignedClusters == 0)
{
// we couldn't find any runs starting at DesiredFirstCluster
*AssignedClusters = RtlFindLongestRunClear(&Bitmap, FirstAssignedCluster);
}
}
Status = WriteAttribute(DeviceExt, DataContext, 0, BitmapData, (ULONG)BitmapDataSize, &LengthWritten, BitmapRecord);
ReleaseAttributeContext(DataContext);
ExFreePoolWithTag(BitmapData, TAG_NTFS);
ExFreePoolWithTag(BitmapRecord, TAG_NTFS);
return Status;
}
static
NTSTATUS
NtfsGetFsVolumeInformation(PDEVICE_OBJECT DeviceObject,