reactos/drivers/filesystems/ntfs/finfo.c

787 lines
28 KiB
C
Raw Normal View History

/*
* 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)
[NTFS] Totally rewrite the way MFT records attributes are handled. Up to now, we were having really similar loops, only looking at the resident part of the attribute list, not really caring about how the loop was going. This was leading to some issues: - In case the attribute we were looking for was stored in the non-resident part of the attribute list, we would miss it (excepted in the case of FindAttribute() which was properly browsing the whole attribute list). - In the specific case of FindAttribute(), one would have been able to setup a broken MFT record with the resident attribute list pointing on the non resident attribute list which itself would point to the resident attribute list. In such case, the driver would loop forever caught on the loop, allocating tones of memory. It was possible to trigger this by user space, from a non-privileged user, just by browsing the right directory entry. - In the case of the other loops (non FindAttribute()), another issue (other than missing attributes) was present, one would have been able to setup a broken MFT record with an attribute of null-length. This would have caused the driver to loop forever on the attribute list. This could be triggered from usermode too. And could be triggered by a non-privileged user. This commit introduces a new set of functions for attributes browsing: FindFirstAttribute(), FindNextAttribute(), FindCloseAttribute(). It allows safely browsing attributes and handles broken cases. It also performs reading of the attribute list when present and makes sure there's only one read. This method should be the only one to use to browse the attributes. The whole NTFS code base has been converted to use this newly set of functions. This really simplifies the implementation of FindAttribute(), and prevent unsafe code duplication. CORE-10037 #resolve #comment Fixed with r68829 svn path=/trunk/; revision=68829
2015-08-26 18:20:04 +00:00
* 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;
[NTFS] Totally rewrite the way MFT records attributes are handled. Up to now, we were having really similar loops, only looking at the resident part of the attribute list, not really caring about how the loop was going. This was leading to some issues: - In case the attribute we were looking for was stored in the non-resident part of the attribute list, we would miss it (excepted in the case of FindAttribute() which was properly browsing the whole attribute list). - In the specific case of FindAttribute(), one would have been able to setup a broken MFT record with the resident attribute list pointing on the non resident attribute list which itself would point to the resident attribute list. In such case, the driver would loop forever caught on the loop, allocating tones of memory. It was possible to trigger this by user space, from a non-privileged user, just by browsing the right directory entry. - In the case of the other loops (non FindAttribute()), another issue (other than missing attributes) was present, one would have been able to setup a broken MFT record with an attribute of null-length. This would have caused the driver to loop forever on the attribute list. This could be triggered from usermode too. And could be triggered by a non-privileged user. This commit introduces a new set of functions for attributes browsing: FindFirstAttribute(), FindNextAttribute(), FindCloseAttribute(). It allows safely browsing attributes and handles broken cases. It also performs reading of the attribute list when present and makes sure there's only one read. This method should be the only one to use to browse the attributes. The whole NTFS code base has been converted to use this newly set of functions. This really simplifies the implementation of FindAttribute(), and prevent unsafe code duplication. CORE-10037 #resolve #comment Fixed with r68829 svn path=/trunk/; revision=68829
2015-08-26 18:20:04 +00:00
FIND_ATTR_CONTXT Context;
PNTFS_ATTR_RECORD Attribute;
[NTFS] Totally rewrite the way MFT records attributes are handled. Up to now, we were having really similar loops, only looking at the resident part of the attribute list, not really caring about how the loop was going. This was leading to some issues: - In case the attribute we were looking for was stored in the non-resident part of the attribute list, we would miss it (excepted in the case of FindAttribute() which was properly browsing the whole attribute list). - In the specific case of FindAttribute(), one would have been able to setup a broken MFT record with the resident attribute list pointing on the non resident attribute list which itself would point to the resident attribute list. In such case, the driver would loop forever caught on the loop, allocating tones of memory. It was possible to trigger this by user space, from a non-privileged user, just by browsing the right directory entry. - In the case of the other loops (non FindAttribute()), another issue (other than missing attributes) was present, one would have been able to setup a broken MFT record with an attribute of null-length. This would have caused the driver to loop forever on the attribute list. This could be triggered from usermode too. And could be triggered by a non-privileged user. This commit introduces a new set of functions for attributes browsing: FindFirstAttribute(), FindNextAttribute(), FindCloseAttribute(). It allows safely browsing attributes and handles broken cases. It also performs reading of the attribute list when present and makes sure there's only one read. This method should be the only one to use to browse the attributes. The whole NTFS code base has been converted to use this newly set of functions. This really simplifies the implementation of FindAttribute(), and prevent unsafe code duplication. CORE-10037 #resolve #comment Fixed with r68829 svn path=/trunk/; revision=68829
2015-08-26 18:20:04 +00:00
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;
}
[NTFS] Totally rewrite the way MFT records attributes are handled. Up to now, we were having really similar loops, only looking at the resident part of the attribute list, not really caring about how the loop was going. This was leading to some issues: - In case the attribute we were looking for was stored in the non-resident part of the attribute list, we would miss it (excepted in the case of FindAttribute() which was properly browsing the whole attribute list). - In the specific case of FindAttribute(), one would have been able to setup a broken MFT record with the resident attribute list pointing on the non resident attribute list which itself would point to the resident attribute list. In such case, the driver would loop forever caught on the loop, allocating tones of memory. It was possible to trigger this by user space, from a non-privileged user, just by browsing the right directory entry. - In the case of the other loops (non FindAttribute()), another issue (other than missing attributes) was present, one would have been able to setup a broken MFT record with an attribute of null-length. This would have caused the driver to loop forever on the attribute list. This could be triggered from usermode too. And could be triggered by a non-privileged user. This commit introduces a new set of functions for attributes browsing: FindFirstAttribute(), FindNextAttribute(), FindCloseAttribute(). It allows safely browsing attributes and handles broken cases. It also performs reading of the attribute list when present and makes sure there's only one read. This method should be the only one to use to browse the attributes. The whole NTFS code base has been converted to use this newly set of functions. This really simplifies the implementation of FindAttribute(), and prevent unsafe code duplication. CORE-10037 #resolve #comment Fixed with r68829 svn path=/trunk/; revision=68829
2015-08-26 18:20:04 +00:00
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;
}
[NTFS] Totally rewrite the way MFT records attributes are handled. Up to now, we were having really similar loops, only looking at the resident part of the attribute list, not really caring about how the loop was going. This was leading to some issues: - In case the attribute we were looking for was stored in the non-resident part of the attribute list, we would miss it (excepted in the case of FindAttribute() which was properly browsing the whole attribute list). - In the specific case of FindAttribute(), one would have been able to setup a broken MFT record with the resident attribute list pointing on the non resident attribute list which itself would point to the resident attribute list. In such case, the driver would loop forever caught on the loop, allocating tones of memory. It was possible to trigger this by user space, from a non-privileged user, just by browsing the right directory entry. - In the case of the other loops (non FindAttribute()), another issue (other than missing attributes) was present, one would have been able to setup a broken MFT record with an attribute of null-length. This would have caused the driver to loop forever on the attribute list. This could be triggered from usermode too. And could be triggered by a non-privileged user. This commit introduces a new set of functions for attributes browsing: FindFirstAttribute(), FindNextAttribute(), FindCloseAttribute(). It allows safely browsing attributes and handles broken cases. It also performs reading of the attribute list when present and makes sure there's only one read. This method should be the only one to use to browse the attributes. The whole NTFS code base has been converted to use this newly set of functions. This really simplifies the implementation of FindAttribute(), and prevent unsafe code duplication. CORE-10037 #resolve #comment Fixed with r68829 svn path=/trunk/; revision=68829
2015-08-26 18:20:04 +00:00
BrowseStatus = FindNextAttribute(&Context, &Attribute);
}
[NTFS] Totally rewrite the way MFT records attributes are handled. Up to now, we were having really similar loops, only looking at the resident part of the attribute list, not really caring about how the loop was going. This was leading to some issues: - In case the attribute we were looking for was stored in the non-resident part of the attribute list, we would miss it (excepted in the case of FindAttribute() which was properly browsing the whole attribute list). - In the specific case of FindAttribute(), one would have been able to setup a broken MFT record with the resident attribute list pointing on the non resident attribute list which itself would point to the resident attribute list. In such case, the driver would loop forever caught on the loop, allocating tones of memory. It was possible to trigger this by user space, from a non-privileged user, just by browsing the right directory entry. - In the case of the other loops (non FindAttribute()), another issue (other than missing attributes) was present, one would have been able to setup a broken MFT record with an attribute of null-length. This would have caused the driver to loop forever on the attribute list. This could be triggered from usermode too. And could be triggered by a non-privileged user. This commit introduces a new set of functions for attributes browsing: FindFirstAttribute(), FindNextAttribute(), FindCloseAttribute(). It allows safely browsing attributes and handles broken cases. It also performs reading of the attribute list when present and makes sure there's only one read. This method should be the only one to use to browse the attributes. The whole NTFS code base has been converted to use this newly set of functions. This really simplifies the implementation of FindAttribute(), and prevent unsafe code duplication. CORE-10037 #resolve #comment Fixed with r68829 svn path=/trunk/; revision=68829
2015-08-26 18:20:04 +00:00
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 */