* PURPOSE: NTFS filesystem driver
* PROGRAMMERS: Eric Kohl
* Hervé Poussineau (hpoussin@reactos.org)
+ * Pierre Schweitzer (pierre@reactos.org)
*/
/* INCLUDES *****************************************************************/
{
UNREFERENCED_PARAMETER(DeviceObject);
- DPRINT("NtfsGetStandardInformation() called\n");
+ DPRINT1("NtfsGetStandardInformation(%p, %p, %p, %p)\n", Fcb, DeviceObject, StandardInfo, BufferLength);
if (*BufferLength < sizeof(FILE_STANDARD_INFORMATION))
- return STATUS_BUFFER_OVERFLOW;
+ return STATUS_BUFFER_TOO_SMALL;
/* PRECONDITION */
ASSERT(StandardInfo != NULL);
StandardInfo->AllocationSize = Fcb->RFCB.AllocationSize;
StandardInfo->EndOfFile = Fcb->RFCB.FileSize;
- StandardInfo->NumberOfLinks = 0;
+ StandardInfo->NumberOfLinks = Fcb->LinkCount;
StandardInfo->DeletePending = FALSE;
StandardInfo->Directory = NtfsFCBIsDirectory(Fcb);
PFILE_POSITION_INFORMATION PositionInfo,
PULONG BufferLength)
{
- UNREFERENCED_PARAMETER(FileObject);
-
- DPRINT("NtfsGetPositionInformation() called\n");
+ DPRINT1("NtfsGetPositionInformation(%p, %p, %p)\n", FileObject, PositionInfo, BufferLength);
if (*BufferLength < sizeof(FILE_POSITION_INFORMATION))
- return STATUS_BUFFER_OVERFLOW;
+ return STATUS_BUFFER_TOO_SMALL;
- PositionInfo->CurrentByteOffset.QuadPart = 0;
-// FileObject->CurrentByteOffset.QuadPart;
+ PositionInfo->CurrentByteOffset.QuadPart = FileObject->CurrentByteOffset.QuadPart;
DPRINT("Getting position %I64x\n",
PositionInfo->CurrentByteOffset.QuadPart);
PFILE_BASIC_INFORMATION BasicInfo,
PULONG BufferLength)
{
- DPRINT("NtfsGetBasicInformation() called\n");
+ PFILENAME_ATTRIBUTE FileName = &Fcb->Entry;
+
+ DPRINT1("NtfsGetBasicInformation(%p, %p, %p, %p, %p)\n", FileObject, Fcb, DeviceObject, BasicInfo, BufferLength);
if (*BufferLength < sizeof(FILE_BASIC_INFORMATION))
- return STATUS_BUFFER_OVERFLOW;
+ return STATUS_BUFFER_TOO_SMALL;
-#if 0
- CdfsDateTimeToFileTime(Fcb,
- &BasicInfo->CreationTime);
- CdfsDateTimeToFileTime(Fcb,
- &BasicInfo->LastAccessTime);
- CdfsDateTimeToFileTime(Fcb,
- &BasicInfo->LastWriteTime);
- CdfsDateTimeToFileTime(Fcb,
- &BasicInfo->ChangeTime);
-
- CdfsFileFlagsToAttributes(Fcb,
- &BasicInfo->FileAttributes);
-#else
- UNREFERENCED_PARAMETER(FileObject);
- UNREFERENCED_PARAMETER(Fcb);
- UNREFERENCED_PARAMETER(DeviceObject);
- UNREFERENCED_PARAMETER(BasicInfo);
-#endif
+ 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);
UNREFERENCED_PARAMETER(FileObject);
UNREFERENCED_PARAMETER(DeviceObject);
- DPRINT("NtfsGetNameInformation() called\n");
+ DPRINT1("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_OVERFLOW;
+ 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);
PFILE_INTERNAL_INFORMATION InternalInfo,
PULONG BufferLength)
{
- DPRINT("NtfsGetInternalInformation() called\n");
+ DPRINT1("NtfsGetInternalInformation(%p, %p, %p)\n", Fcb, InternalInfo, BufferLength);
ASSERT(InternalInfo);
ASSERT(Fcb);
if (*BufferLength < sizeof(FILE_INTERNAL_INFORMATION))
- return STATUS_BUFFER_OVERFLOW;
+ return STATUS_BUFFER_TOO_SMALL;
- /* FIXME: get a real index, that can be used in a create operation */
- InternalInfo->IndexNumber.QuadPart = 0;
+ InternalInfo->IndexNumber.QuadPart = Fcb->MFTIndex;
*BufferLength -= sizeof(FILE_INTERNAL_INFORMATION);
{
PFILENAME_ATTRIBUTE FileName = &Fcb->Entry;
+ DPRINT1("NtfsGetNetworkOpenInformation(%p, %p, %p, %p)\n", Fcb, DeviceExt, NetworkInfo, BufferLength);
+
if (*BufferLength < sizeof(FILE_NETWORK_OPEN_INFORMATION))
- return(STATUS_BUFFER_OVERFLOW);
+ 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.QuadPart = FileName->AllocatedSize;
- NetworkInfo->AllocationSize.QuadPart = ROUND_UP(FileName->AllocatedSize, DeviceExt->NtfsInfo.BytesPerCluster);
+ NetworkInfo->EndOfFile = Fcb->RFCB.FileSize;
+ NetworkInfo->AllocationSize = Fcb->RFCB.AllocationSize;
NtfsFileFlagsToAttributes(FileName->FileAttributes, &NetworkInfo->FileAttributes);
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 = ExAllocatePoolWithTag(NonPagedPool, DeviceExt->NtfsInfo.BytesPerFileRecord, TAG_NTFS);
+ 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");
+ ExFreePoolWithTag(FileRecord, TAG_NTFS);
+ 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);
+ ExFreePoolWithTag(FileRecord, TAG_NTFS);
+ return Status;
+}
+
/*
* FUNCTION: Retrieve the specified file information
*/
NTSTATUS
-NTAPI
-NtfsFsdQueryInformation(PDEVICE_OBJECT DeviceObject,
- PIRP Irp)
+NtfsQueryInformation(PNTFS_IRP_CONTEXT IrpContext)
{
FILE_INFORMATION_CLASS FileInformationClass;
PIO_STACK_LOCATION Stack;
PNTFS_FCB Fcb;
PVOID SystemBuffer;
ULONG BufferLength;
+ PIRP Irp;
+ PDEVICE_OBJECT DeviceObject;
NTSTATUS Status = STATUS_SUCCESS;
- DPRINT("NtfsQueryInformation() called\n");
+ DPRINT1("NtfsQueryInformation(%p)\n", IrpContext);
- Stack = IoGetCurrentIrpStackLocation(Irp);
+ Irp = IrpContext->Irp;
+ Stack = IrpContext->Stack;
+ DeviceObject = IrpContext->DeviceObject;
FileInformationClass = Stack->Parameters.QueryFile.FileInformationClass;
- FileObject = Stack->FileObject;
+ 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:
case FileNameInformation:
Status = NtfsGetNameInformation(FileObject,
- Fcb,
- DeviceObject,
- SystemBuffer,
- &BufferLength);
+ Fcb,
+ DeviceObject,
+ SystemBuffer,
+ &BufferLength);
break;
case FileInternalInformation:
&BufferLength);
break;
+ case FileStreamInformation:
+ Status = NtfsGetSteamInformation(Fcb,
+ DeviceObject->DeviceExtension,
+ SystemBuffer,
+ &BufferLength);
+ break;
+
case FileAlternateNameInformation:
case FileAllInformation:
+ DPRINT1("Unimplemented information class %u\n", FileInformationClass);
Status = STATUS_NOT_IMPLEMENTED;
break;
default:
- DPRINT("Unimplemented information class %u\n", FileInformationClass);
+ DPRINT1("Unimplemented information class %u\n", FileInformationClass);
Status = STATUS_INVALID_PARAMETER;
}
- Irp->IoStatus.Status = Status;
+ ExReleaseResourceLite(&Fcb->MainResource);
if (NT_SUCCESS(Status))
Irp->IoStatus.Information =
else
Irp->IoStatus.Information = 0;
- IoCompleteRequest(Irp, IO_NO_INCREMENT);
+ 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, (PULONGLONG)&CurrentFileSize);
+
+ // 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");
+ 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->Record);
+
+ // 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");
+ 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 = ROUND_UP(NewFileSize->QuadPart, Fcb->Vcb->NtfsInfo.BytesPerCluster);
+
+ 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,
+ (Stack->Flags & SL_CASE_SENSITIVE),
+ &EndOfFileInfo->EndOfFile);
+ break;
+
+ // TODO: all other information classes
+
+ default:
+ DPRINT1("FIXME: Unimplemented information class %u\n", 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 */