[NTFS] - In the NtfsAddFilenameToDirectory() function, rename DirectoryContext parame...
[reactos.git] / drivers / filesystems / ntfs / finfo.c
index 50604f8..1cb7dbc 100644 (file)
@@ -396,4 +396,264 @@ NtfsQueryInformation(PNTFS_IRP_CONTEXT IrpContext)
     return Status;
 }
 
+/**
+* @name NtfsSetEndOfFile
+* @implemented
+*
+* Sets the end of file (file size) for a given file.
+*
+* @param Fcb
+* Pointer to an NTFS_FCB which describes the target file. Fcb->MainResource should have been
+* acquired with ExAcquireResourceSharedLite().
+*
+* @param FileObject
+* Pointer to a FILE_OBJECT describing the target file.
+*
+* @param DeviceExt
+* Points to the target disk's DEVICE_EXTENSION
+*
+* @param IrpFlags
+* ULONG describing the flags of the original IRP request (Irp->Flags).
+*
+* @param CaseSensitive
+* Boolean indicating if the function should operate in case-sensitive mode. This will be TRUE
+* if an application opened the file with the FILE_FLAG_POSIX_SEMANTICS flag.
+*
+* @param NewFileSize
+* Pointer to a LARGE_INTEGER which indicates the new end of file (file size).
+*
+* @return
+* STATUS_SUCCESS if successful,
+* STATUS_USER_MAPPED_FILE if trying to truncate a file but MmCanFileBeTruncated() returned false,
+* STATUS_OBJECT_NAME_NOT_FOUND if there was no $DATA attribute associated with the target file,
+* STATUS_INVALID_PARAMETER if there was no $FILENAME attribute associated with the target file,
+* STATUS_INSUFFICIENT_RESOURCES if an allocation failed,
+* STATUS_ACCESS_DENIED if target file is a volume or if paging is involved.
+*
+* @remarks As this function sets the size of a file at the file-level 
+* (and not at the attribute level) it's not recommended to use this 
+* function alongside functions that operate on the data attribute directly.
+*
+*/
+NTSTATUS
+NtfsSetEndOfFile(PNTFS_FCB Fcb,
+                 PFILE_OBJECT FileObject,
+                 PDEVICE_EXTENSION DeviceExt,
+                 ULONG IrpFlags,
+                 BOOLEAN CaseSensitive,
+                 PLARGE_INTEGER NewFileSize)
+{
+    LARGE_INTEGER CurrentFileSize;
+    PFILE_RECORD_HEADER FileRecord;
+    PNTFS_ATTR_CONTEXT DataContext;
+    ULONG AttributeOffset;
+    NTSTATUS Status = STATUS_SUCCESS;
+    ULONGLONG AllocationSize;
+    PFILENAME_ATTRIBUTE fileNameAttribute;
+    ULONGLONG ParentMFTId;
+    UNICODE_STRING filename;
+
+
+    // Allocate non-paged memory for the file record
+    FileRecord = ExAllocatePoolWithTag(NonPagedPool, DeviceExt->NtfsInfo.BytesPerFileRecord, TAG_NTFS);
+    if (FileRecord == NULL)
+    {
+        DPRINT1("Couldn't allocate memory for file record!");
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+
+    // read the file record
+    DPRINT("Reading file record...\n");
+    Status = ReadFileRecord(DeviceExt, Fcb->MFTIndex, FileRecord);
+    if (!NT_SUCCESS(Status))
+    {
+        // We couldn't get the file's record. Free the memory and return the error
+        DPRINT1("Can't find record for %wS!\n", Fcb->ObjectName);
+        ExFreePoolWithTag(FileRecord, TAG_NTFS);
+        return Status;
+    }
+
+    DPRINT("Found record for %wS\n", Fcb->ObjectName);
+
+    CurrentFileSize.QuadPart = NtfsGetFileSize(DeviceExt, FileRecord, L"", 0, (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 */