mirror of
https://github.com/reactos/reactos.git
synced 2024-08-19 18:02:55 +00:00
787 lines
28 KiB
C
787 lines
28 KiB
C
/*
|
|
* ReactOS kernel
|
|
* Copyright (C) 2002 ReactOS Team
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*
|
|
* COPYRIGHT: See COPYING in the top level directory
|
|
* PROJECT: ReactOS kernel
|
|
* FILE: drivers/filesystem/ntfs/dirctl.c
|
|
* PURPOSE: NTFS filesystem driver
|
|
* PROGRAMMERS: Eric Kohl
|
|
* Hervé Poussineau (hpoussin@reactos.org)
|
|
* Pierre Schweitzer (pierre@reactos.org)
|
|
*/
|
|
|
|
/* INCLUDES *****************************************************************/
|
|
|
|
#include "ntfs.h"
|
|
|
|
#define NDEBUG
|
|
#include <debug.h>
|
|
|
|
/* FUNCTIONS ****************************************************************/
|
|
|
|
/*
|
|
* FUNCTION: Retrieve the standard file information
|
|
*/
|
|
static
|
|
NTSTATUS
|
|
NtfsGetStandardInformation(PNTFS_FCB Fcb,
|
|
PDEVICE_OBJECT DeviceObject,
|
|
PFILE_STANDARD_INFORMATION StandardInfo,
|
|
PULONG BufferLength)
|
|
{
|
|
UNREFERENCED_PARAMETER(DeviceObject);
|
|
|
|
DPRINT("NtfsGetStandardInformation(%p, %p, %p, %p)\n", Fcb, DeviceObject, StandardInfo, BufferLength);
|
|
|
|
if (*BufferLength < sizeof(FILE_STANDARD_INFORMATION))
|
|
return STATUS_BUFFER_TOO_SMALL;
|
|
|
|
/* PRECONDITION */
|
|
ASSERT(StandardInfo != NULL);
|
|
ASSERT(Fcb != NULL);
|
|
|
|
RtlZeroMemory(StandardInfo,
|
|
sizeof(FILE_STANDARD_INFORMATION));
|
|
|
|
StandardInfo->AllocationSize = Fcb->RFCB.AllocationSize;
|
|
StandardInfo->EndOfFile = Fcb->RFCB.FileSize;
|
|
StandardInfo->NumberOfLinks = Fcb->LinkCount;
|
|
StandardInfo->DeletePending = FALSE;
|
|
StandardInfo->Directory = NtfsFCBIsDirectory(Fcb);
|
|
|
|
*BufferLength -= sizeof(FILE_STANDARD_INFORMATION);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
static
|
|
NTSTATUS
|
|
NtfsGetPositionInformation(PFILE_OBJECT FileObject,
|
|
PFILE_POSITION_INFORMATION PositionInfo,
|
|
PULONG BufferLength)
|
|
{
|
|
DPRINT1("NtfsGetPositionInformation(%p, %p, %p)\n", FileObject, PositionInfo, BufferLength);
|
|
|
|
if (*BufferLength < sizeof(FILE_POSITION_INFORMATION))
|
|
return STATUS_BUFFER_TOO_SMALL;
|
|
|
|
PositionInfo->CurrentByteOffset.QuadPart = FileObject->CurrentByteOffset.QuadPart;
|
|
|
|
DPRINT("Getting position %I64x\n",
|
|
PositionInfo->CurrentByteOffset.QuadPart);
|
|
|
|
*BufferLength -= sizeof(FILE_POSITION_INFORMATION);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
static
|
|
NTSTATUS
|
|
NtfsGetBasicInformation(PFILE_OBJECT FileObject,
|
|
PNTFS_FCB Fcb,
|
|
PDEVICE_OBJECT DeviceObject,
|
|
PFILE_BASIC_INFORMATION BasicInfo,
|
|
PULONG BufferLength)
|
|
{
|
|
PFILENAME_ATTRIBUTE FileName = &Fcb->Entry;
|
|
|
|
DPRINT("NtfsGetBasicInformation(%p, %p, %p, %p, %p)\n", FileObject, Fcb, DeviceObject, BasicInfo, BufferLength);
|
|
|
|
if (*BufferLength < sizeof(FILE_BASIC_INFORMATION))
|
|
return STATUS_BUFFER_TOO_SMALL;
|
|
|
|
RtlZeroMemory(BasicInfo, sizeof(FILE_BASIC_INFORMATION));
|
|
|
|
BasicInfo->CreationTime.QuadPart = FileName->CreationTime;
|
|
BasicInfo->LastAccessTime.QuadPart = FileName->LastAccessTime;
|
|
BasicInfo->LastWriteTime.QuadPart = FileName->LastWriteTime;
|
|
BasicInfo->ChangeTime.QuadPart = FileName->ChangeTime;
|
|
|
|
NtfsFileFlagsToAttributes(FileName->FileAttributes, &BasicInfo->FileAttributes);
|
|
|
|
*BufferLength -= sizeof(FILE_BASIC_INFORMATION);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
/*
|
|
* FUNCTION: Retrieve the file name information
|
|
*/
|
|
static
|
|
NTSTATUS
|
|
NtfsGetNameInformation(PFILE_OBJECT FileObject,
|
|
PNTFS_FCB Fcb,
|
|
PDEVICE_OBJECT DeviceObject,
|
|
PFILE_NAME_INFORMATION NameInfo,
|
|
PULONG BufferLength)
|
|
{
|
|
ULONG BytesToCopy;
|
|
|
|
UNREFERENCED_PARAMETER(FileObject);
|
|
UNREFERENCED_PARAMETER(DeviceObject);
|
|
|
|
DPRINT("NtfsGetNameInformation(%p, %p, %p, %p, %p)\n", FileObject, Fcb, DeviceObject, NameInfo, BufferLength);
|
|
|
|
ASSERT(NameInfo != NULL);
|
|
ASSERT(Fcb != NULL);
|
|
|
|
/* If buffer can't hold at least the file name length, bail out */
|
|
if (*BufferLength < (ULONG)FIELD_OFFSET(FILE_NAME_INFORMATION, FileName[0]))
|
|
return STATUS_BUFFER_TOO_SMALL;
|
|
|
|
/* Save file name length, and as much file len, as buffer length allows */
|
|
NameInfo->FileNameLength = wcslen(Fcb->PathName) * sizeof(WCHAR);
|
|
|
|
/* Calculate amount of bytes to copy not to overflow the buffer */
|
|
BytesToCopy = min(NameInfo->FileNameLength,
|
|
*BufferLength - FIELD_OFFSET(FILE_NAME_INFORMATION, FileName[0]));
|
|
|
|
/* Fill in the bytes */
|
|
RtlCopyMemory(NameInfo->FileName, Fcb->PathName, BytesToCopy);
|
|
|
|
/* Check if we could write more but are not able to */
|
|
if (*BufferLength < NameInfo->FileNameLength + (ULONG)FIELD_OFFSET(FILE_NAME_INFORMATION, FileName[0]))
|
|
{
|
|
/* Return number of bytes written */
|
|
*BufferLength -= FIELD_OFFSET(FILE_NAME_INFORMATION, FileName[0]) + BytesToCopy;
|
|
return STATUS_BUFFER_OVERFLOW;
|
|
}
|
|
|
|
/* We filled up as many bytes, as needed */
|
|
*BufferLength -= (FIELD_OFFSET(FILE_NAME_INFORMATION, FileName[0]) + NameInfo->FileNameLength);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
static
|
|
NTSTATUS
|
|
NtfsGetInternalInformation(PNTFS_FCB Fcb,
|
|
PFILE_INTERNAL_INFORMATION InternalInfo,
|
|
PULONG BufferLength)
|
|
{
|
|
DPRINT1("NtfsGetInternalInformation(%p, %p, %p)\n", Fcb, InternalInfo, BufferLength);
|
|
|
|
ASSERT(InternalInfo);
|
|
ASSERT(Fcb);
|
|
|
|
if (*BufferLength < sizeof(FILE_INTERNAL_INFORMATION))
|
|
return STATUS_BUFFER_TOO_SMALL;
|
|
|
|
InternalInfo->IndexNumber.QuadPart = Fcb->MFTIndex;
|
|
|
|
*BufferLength -= sizeof(FILE_INTERNAL_INFORMATION);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
static
|
|
NTSTATUS
|
|
NtfsGetNetworkOpenInformation(PNTFS_FCB Fcb,
|
|
PDEVICE_EXTENSION DeviceExt,
|
|
PFILE_NETWORK_OPEN_INFORMATION NetworkInfo,
|
|
PULONG BufferLength)
|
|
{
|
|
PFILENAME_ATTRIBUTE FileName = &Fcb->Entry;
|
|
|
|
DPRINT("NtfsGetNetworkOpenInformation(%p, %p, %p, %p)\n", Fcb, DeviceExt, NetworkInfo, BufferLength);
|
|
|
|
if (*BufferLength < sizeof(FILE_NETWORK_OPEN_INFORMATION))
|
|
return STATUS_BUFFER_TOO_SMALL;
|
|
|
|
NetworkInfo->CreationTime.QuadPart = FileName->CreationTime;
|
|
NetworkInfo->LastAccessTime.QuadPart = FileName->LastAccessTime;
|
|
NetworkInfo->LastWriteTime.QuadPart = FileName->LastWriteTime;
|
|
NetworkInfo->ChangeTime.QuadPart = FileName->ChangeTime;
|
|
|
|
NetworkInfo->EndOfFile = Fcb->RFCB.FileSize;
|
|
NetworkInfo->AllocationSize = Fcb->RFCB.AllocationSize;
|
|
|
|
NtfsFileFlagsToAttributes(FileName->FileAttributes, &NetworkInfo->FileAttributes);
|
|
|
|
*BufferLength -= sizeof(FILE_NETWORK_OPEN_INFORMATION);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
static
|
|
NTSTATUS
|
|
NtfsGetSteamInformation(PNTFS_FCB Fcb,
|
|
PDEVICE_EXTENSION DeviceExt,
|
|
PFILE_STREAM_INFORMATION StreamInfo,
|
|
PULONG BufferLength)
|
|
{
|
|
ULONG CurrentSize;
|
|
FIND_ATTR_CONTXT Context;
|
|
PNTFS_ATTR_RECORD Attribute;
|
|
NTSTATUS Status, BrowseStatus;
|
|
PFILE_RECORD_HEADER FileRecord;
|
|
PFILE_STREAM_INFORMATION CurrentInfo = StreamInfo, Previous = NULL;
|
|
|
|
if (*BufferLength < sizeof(FILE_STREAM_INFORMATION))
|
|
return STATUS_BUFFER_TOO_SMALL;
|
|
|
|
FileRecord = ExAllocateFromNPagedLookasideList(&DeviceExt->FileRecLookasideList);
|
|
if (FileRecord == NULL)
|
|
{
|
|
DPRINT1("Not enough memory!\n");
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
Status = ReadFileRecord(DeviceExt, Fcb->MFTIndex, FileRecord);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("Can't find record!\n");
|
|
ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, FileRecord);
|
|
return Status;
|
|
}
|
|
|
|
BrowseStatus = FindFirstAttribute(&Context, DeviceExt, FileRecord, FALSE, &Attribute);
|
|
while (NT_SUCCESS(BrowseStatus))
|
|
{
|
|
if (Attribute->Type == AttributeData)
|
|
{
|
|
CurrentSize = FIELD_OFFSET(FILE_STREAM_INFORMATION, StreamName) + Attribute->NameLength * sizeof(WCHAR) + wcslen(L"::$DATA") * sizeof(WCHAR);
|
|
|
|
if (CurrentSize > *BufferLength)
|
|
{
|
|
Status = STATUS_BUFFER_OVERFLOW;
|
|
break;
|
|
}
|
|
|
|
CurrentInfo->NextEntryOffset = 0;
|
|
CurrentInfo->StreamNameLength = (Attribute->NameLength + wcslen(L"::$DATA")) * sizeof(WCHAR);
|
|
CurrentInfo->StreamSize.QuadPart = AttributeDataLength(Attribute);
|
|
CurrentInfo->StreamAllocationSize.QuadPart = AttributeAllocatedLength(Attribute);
|
|
CurrentInfo->StreamName[0] = L':';
|
|
RtlMoveMemory(&CurrentInfo->StreamName[1], (PWCHAR)((ULONG_PTR)Attribute + Attribute->NameOffset), CurrentInfo->StreamNameLength);
|
|
RtlMoveMemory(&CurrentInfo->StreamName[Attribute->NameLength + 1], L":$DATA", sizeof(L":$DATA") - sizeof(UNICODE_NULL));
|
|
|
|
if (Previous != NULL)
|
|
{
|
|
Previous->NextEntryOffset = (ULONG_PTR)CurrentInfo - (ULONG_PTR)Previous;
|
|
}
|
|
Previous = CurrentInfo;
|
|
CurrentInfo = (PFILE_STREAM_INFORMATION)((ULONG_PTR)CurrentInfo + CurrentSize);
|
|
*BufferLength -= CurrentSize;
|
|
}
|
|
|
|
BrowseStatus = FindNextAttribute(&Context, &Attribute);
|
|
}
|
|
|
|
FindCloseAttribute(&Context);
|
|
ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, FileRecord);
|
|
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
|
|
*/
|
|
NTSTATUS
|
|
NtfsQueryInformation(PNTFS_IRP_CONTEXT IrpContext)
|
|
{
|
|
FILE_INFORMATION_CLASS FileInformationClass;
|
|
PIO_STACK_LOCATION Stack;
|
|
PFILE_OBJECT FileObject;
|
|
PNTFS_FCB Fcb;
|
|
PVOID SystemBuffer;
|
|
ULONG BufferLength;
|
|
PIRP Irp;
|
|
PDEVICE_OBJECT DeviceObject;
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
DPRINT1("NtfsQueryInformation(%p)\n", IrpContext);
|
|
|
|
Irp = IrpContext->Irp;
|
|
Stack = IrpContext->Stack;
|
|
DeviceObject = IrpContext->DeviceObject;
|
|
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)
|
|
{
|
|
case FileStandardInformation:
|
|
Status = NtfsGetStandardInformation(Fcb,
|
|
DeviceObject,
|
|
SystemBuffer,
|
|
&BufferLength);
|
|
break;
|
|
|
|
case FilePositionInformation:
|
|
Status = NtfsGetPositionInformation(FileObject,
|
|
SystemBuffer,
|
|
&BufferLength);
|
|
break;
|
|
|
|
case FileBasicInformation:
|
|
Status = NtfsGetBasicInformation(FileObject,
|
|
Fcb,
|
|
DeviceObject,
|
|
SystemBuffer,
|
|
&BufferLength);
|
|
break;
|
|
|
|
case FileNameInformation:
|
|
Status = NtfsGetNameInformation(FileObject,
|
|
Fcb,
|
|
DeviceObject,
|
|
SystemBuffer,
|
|
&BufferLength);
|
|
break;
|
|
|
|
case FileInternalInformation:
|
|
Status = NtfsGetInternalInformation(Fcb,
|
|
SystemBuffer,
|
|
&BufferLength);
|
|
break;
|
|
|
|
case FileNetworkOpenInformation:
|
|
Status = NtfsGetNetworkOpenInformation(Fcb,
|
|
DeviceObject->DeviceExtension,
|
|
SystemBuffer,
|
|
&BufferLength);
|
|
break;
|
|
|
|
case FileStreamInformation:
|
|
Status = NtfsGetSteamInformation(Fcb,
|
|
DeviceObject->DeviceExtension,
|
|
SystemBuffer,
|
|
&BufferLength);
|
|
break;
|
|
|
|
case FileAlternateNameInformation:
|
|
case FileAllInformation:
|
|
DPRINT1("Unimplemented information class: %s\n", GetInfoClassName(FileInformationClass));
|
|
Status = STATUS_NOT_IMPLEMENTED;
|
|
break;
|
|
|
|
default:
|
|
DPRINT1("Unimplemented information class: %s\n", GetInfoClassName(FileInformationClass));
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
ExReleaseResourceLite(&Fcb->MainResource);
|
|
|
|
if (NT_SUCCESS(Status))
|
|
Irp->IoStatus.Information =
|
|
Stack->Parameters.QueryFile.Length - BufferLength;
|
|
else
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
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 = ExAllocateFromNPagedLookasideList(&DeviceExt->FileRecLookasideList);
|
|
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);
|
|
ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, FileRecord);
|
|
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");
|
|
ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, FileRecord);
|
|
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);
|
|
ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, FileRecord);
|
|
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);
|
|
ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, FileRecord);
|
|
return STATUS_ACCESS_DENIED;
|
|
}
|
|
}
|
|
|
|
// set the attribute data length
|
|
Status = SetAttributeDataLength(FileObject, Fcb, DataContext, AttributeOffset, FileRecord, NewFileSize);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
ReleaseAttributeContext(DataContext);
|
|
ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, FileRecord);
|
|
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);
|
|
ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, FileRecord);
|
|
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);
|
|
ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, FileRecord);
|
|
|
|
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;
|
|
|
|
DPRINT("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 */
|