mirror of
https://github.com/reactos/reactos.git
synced 2025-02-22 16:36:33 +00:00
Merge branch 'ntfs_rebase'
This commit is contained in:
commit
03be7587eb
17 changed files with 8259 additions and 170 deletions
|
@ -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
|
||||
|
|
|
@ -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
|
@ -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,
|
||||
|
|
2022
drivers/filesystems/ntfs/btree.c
Normal file
2022
drivers/filesystems/ntfs/btree.c
Normal file
File diff suppressed because it is too large
Load diff
|
@ -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 */
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Reference in a new issue