[NTFS] - In the NtfsAddFilenameToDirectory() function, rename DirectoryContext parame...
[reactos.git] / drivers / filesystems / ntfs / finfo.c
index 96143ee..1cb7dbc 100644 (file)
@@ -20,7 +20,9 @@
  * PROJECT:          ReactOS kernel
  * FILE:             drivers/filesystem/ntfs/dirctl.c
  * PURPOSE:          NTFS filesystem driver
- * PROGRAMMER:       Eric Kohl
+ * PROGRAMMERS:      Eric Kohl
+ *                   HervĂ© Poussineau (hpoussin@reactos.org)
+ *                   Pierre Schweitzer (pierre@reactos.org)
  */
 
 /* INCLUDES *****************************************************************/
@@ -44,10 +46,10 @@ NtfsGetStandardInformation(PNTFS_FCB Fcb,
 {
     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);
@@ -58,7 +60,7 @@ NtfsGetStandardInformation(PNTFS_FCB Fcb,
 
     StandardInfo->AllocationSize = Fcb->RFCB.AllocationSize;
     StandardInfo->EndOfFile = Fcb->RFCB.FileSize;
-    StandardInfo->NumberOfLinks = 0;
+    StandardInfo->NumberOfLinks = Fcb->LinkCount;
     StandardInfo->DeletePending = FALSE;
     StandardInfo->Directory = NtfsFCBIsDirectory(Fcb);
 
@@ -74,15 +76,12 @@ NtfsGetPositionInformation(PFILE_OBJECT FileObject,
                            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);
@@ -101,29 +100,19 @@ NtfsGetBasicInformation(PFILE_OBJECT FileObject,
                         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);
 
@@ -142,28 +131,40 @@ NtfsGetNameInformation(PFILE_OBJECT FileObject,
                        PFILE_NAME_INFORMATION NameInfo,
                        PULONG BufferLength)
 {
-    ULONG NameLength;
+    ULONG BytesToCopy;
 
     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);
 
-    NameLength = wcslen(Fcb->PathName) * sizeof(WCHAR);
-//  NameLength = 2;
-    if (*BufferLength < sizeof(FILE_NAME_INFORMATION) + NameLength)
-        return STATUS_BUFFER_OVERFLOW;
+    /* 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]));
 
-    NameInfo->FileNameLength = NameLength;
-    memcpy(NameInfo->FileName,
-           Fcb->PathName,
-           NameLength + sizeof(WCHAR));
-//  wcscpy(NameInfo->FileName, L"\\");
+    /* 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;
+    }
 
-    *BufferLength -= (sizeof(FILE_NAME_INFORMATION) + NameLength + sizeof(WCHAR));
+    /* We filled up as many bytes, as needed */
+    *BufferLength -= (FIELD_OFFSET(FILE_NAME_INFORMATION, FileName[0]) + NameInfo->FileNameLength);
 
     return STATUS_SUCCESS;
 }
@@ -175,30 +176,124 @@ NtfsGetInternalInformation(PNTFS_FCB Fcb,
                            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);
 
     return STATUS_SUCCESS;
 }
 
+static
+NTSTATUS
+NtfsGetNetworkOpenInformation(PNTFS_FCB Fcb,
+                              PDEVICE_EXTENSION DeviceExt,
+                              PFILE_NETWORK_OPEN_INFORMATION NetworkInfo,
+                              PULONG BufferLength)
+{
+    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_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 = 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;
@@ -206,18 +301,28 @@ NtfsFsdQueryInformation(PDEVICE_OBJECT DeviceObject,
     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:
@@ -243,10 +348,10 @@ NtfsFsdQueryInformation(PDEVICE_OBJECT DeviceObject,
 
         case FileNameInformation:
             Status = NtfsGetNameInformation(FileObject,
-                                      Fcb,
-                                      DeviceObject,
-                                      SystemBuffer,
-                                      &BufferLength);
+                                            Fcb,
+                                            DeviceObject,
+                                            SystemBuffer,
+                                            &BufferLength);
             break;
 
         case FileInternalInformation:
@@ -255,17 +360,32 @@ NtfsFsdQueryInformation(PDEVICE_OBJECT DeviceObject,
                                                 &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 %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 =
@@ -273,9 +393,267 @@ NtfsFsdQueryInformation(PDEVICE_OBJECT DeviceObject,
     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 */