xbox-kernel/private/ntos/fatx/fileinfo.c
2020-09-30 17:17:25 +02:00

1193 lines
33 KiB
C

/*++
Copyright (c) 2000-2001 Microsoft Corporation
Module Name:
fileinfo.c
Abstract:
This module implements routines related to handling
IRP_MJ_QUERY_INFORMATION and IRP_MJ_SET_INFORMATION.
--*/
#include "fatx.h"
VOID
FatxQueryNetworkOpenInformation(
IN PFILE_OBJECT FileObject,
OUT PFILE_NETWORK_OPEN_INFORMATION NetworkOpenInformation
)
/*++
Routine Description:
This routine fills the information structure with attributes about the
supplied file object.
Arguments:
FileObject - Specifies the file object to obtain the information from.
NetworkOpenInformation - Specifies the buffer to receive the file
information.
Return Value:
None.
--*/
{
PFAT_FCB Fcb;
Fcb = (PFAT_FCB)FileObject->FsContext;
ASSERT(FatxIsFlagClear(Fcb->Flags, FAT_FCB_VOLUME));
NetworkOpenInformation->CreationTime =
FatxFatTimestampToTime(&Fcb->CreationTime);
NetworkOpenInformation->LastAccessTime =
FatxFatTimestampToTime(&Fcb->LastAccessTime);
NetworkOpenInformation->LastWriteTime = FatxRoundToFatTime(&Fcb->LastWriteTime);
NetworkOpenInformation->ChangeTime = NetworkOpenInformation->LastWriteTime;
if (Fcb->FileAttributes == 0) {
NetworkOpenInformation->FileAttributes = FILE_ATTRIBUTE_NORMAL;
} else {
NetworkOpenInformation->FileAttributes = Fcb->FileAttributes;
}
if (FatxIsFlagSet(Fcb->Flags, FAT_FCB_DIRECTORY)) {
NetworkOpenInformation->AllocationSize.QuadPart = 0;
NetworkOpenInformation->EndOfFile.QuadPart = 0;
} else {
//
// AllocationSize should be filled in with the number of clusters
// actually allocated to the file, but we don't want to have to go parse
// the entire FAT chain to obtain this piece of information. We'll let
// the caller assume that the file has been allocated the number of
// bytes actually in the file.
//
NetworkOpenInformation->AllocationSize.QuadPart = (ULONGLONG)Fcb->FileSize;
NetworkOpenInformation->EndOfFile.QuadPart = (ULONGLONG)Fcb->FileSize;
}
}
NTSTATUS
FatxFsdQueryInformation(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
This routine is called by the I/O manager to handle IRP_MJ_QUERY_INFORMATION
requests.
Arguments:
DeviceObject - Specifies the device object that the I/O request is for.
Irp - Specifies the packet that describes the I/O request.
Return Value:
Status of operation.
--*/
{
NTSTATUS status;
PFAT_VOLUME_EXTENSION VolumeExtension;
PIO_STACK_LOCATION IrpSp;
PFILE_OBJECT FileObject;
PFAT_FCB Fcb;
ULONG BytesWritten;
PFILE_INTERNAL_INFORMATION InternalInformation;
PFILE_POSITION_INFORMATION PositionInformation;
VolumeExtension = (PFAT_VOLUME_EXTENSION)DeviceObject->DeviceExtension;
IrpSp = IoGetCurrentIrpStackLocation(Irp);
FileObject = IrpSp->FileObject;
Fcb = (PFAT_FCB)FileObject->FsContext;
FatxAcquireVolumeMutexShared(VolumeExtension);
//
// Check if the volume has been dismounted.
//
if (FatxIsFlagSet(VolumeExtension->Flags, FAT_VOLUME_DISMOUNTED)) {
status = STATUS_VOLUME_DISMOUNTED;
goto CleanupAndExit;
}
//
// Check if the file object has already been cleaned up. We don't allow a
// a file object to be accessed after its handle has been closed.
//
if (FatxIsFlagSet(FileObject->Flags, FO_CLEANUP_COMPLETE)) {
status = STATUS_FILE_CLOSED;
goto CleanupAndExit;
}
//
// For volume file control blocks, the only thing that can be queried is the
// current file position.
//
if (FatxIsFlagSet(Fcb->Flags, FAT_FCB_VOLUME) &&
(IrpSp->Parameters.SetFile.FileInformationClass != FilePositionInformation)) {
status = STATUS_INVALID_PARAMETER;
goto CleanupAndExit;
}
//
// Clear the output buffer.
//
RtlZeroMemory(Irp->UserBuffer, IrpSp->Parameters.QueryFile.Length);
//
// Dispatch the information class function.
//
status = STATUS_SUCCESS;
switch (IrpSp->Parameters.QueryFile.FileInformationClass) {
case FileInternalInformation:
InternalInformation = (PFILE_INTERNAL_INFORMATION)Irp->UserBuffer;
InternalInformation->IndexNumber.HighPart = PtrToUlong(VolumeExtension);
InternalInformation->IndexNumber.LowPart = PtrToUlong(Fcb);
BytesWritten = sizeof(FILE_INTERNAL_INFORMATION);
break;
case FilePositionInformation:
PositionInformation = (PFILE_POSITION_INFORMATION)Irp->UserBuffer;
PositionInformation->CurrentByteOffset = FileObject->CurrentByteOffset;
BytesWritten = sizeof(FILE_POSITION_INFORMATION);
break;
case FileNetworkOpenInformation:
FatxQueryNetworkOpenInformation(FileObject,
(PFILE_NETWORK_OPEN_INFORMATION)Irp->UserBuffer);
BytesWritten = sizeof(FILE_NETWORK_OPEN_INFORMATION);
break;
default:
BytesWritten = 0;
status = STATUS_INVALID_PARAMETER;
break;
}
//
// Fill in the number of bytes that we wrote to the user's buffer.
//
Irp->IoStatus.Information = BytesWritten;
//
// Check that we didn't overflow the user's buffer. The I/O manager does
// the initial check to make sure there's enough space for the static
// structure for a given information class, but we might overflow the buffer
// when copying in the variable length file name.
//
ASSERT(Irp->IoStatus.Information <= IrpSp->Parameters.QueryFile.Length);
CleanupAndExit:
FatxReleaseVolumeMutex(VolumeExtension);
Irp->IoStatus.Status = status;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return status;
}
NTSTATUS
FatxSetBasicInformation(
IN PFAT_VOLUME_EXTENSION VolumeExtension,
IN PIRP Irp,
IN PFILE_OBJECT FileObject,
IN PFILE_BASIC_INFORMATION BasicInformation
)
/*++
Routine Description:
This routine changes the basic information of the supplied file object.
Arguments:
VolumeExtension - Specifies the extension that the I/O request is for.
Irp - Specifies the packet that describes the I/O request.
FileObject - Specifies the file object to apply the information to.
BasicInformation - Specifies the basic information to apply to the file.
Return Value:
Status of operation.
--*/
{
NTSTATUS status;
BOOLEAN ChangesMade;
PFAT_FCB Fcb;
FAT_TIME_STAMP CreationTime;
LARGE_INTEGER LastWriteTime;
FAT_TIME_STAMP FatTimestamp;
FAT_TIME_STAMP LastAccessTime;
UCHAR FileAttributes;
ChangesMade = FALSE;
Fcb = (PFAT_FCB)FileObject->FsContext;
ASSERT(FatxIsFlagClear(Fcb->Flags, FAT_FCB_VOLUME));
ASSERT(VolumeExtension->VolumeMutexExclusiveOwner == KeGetCurrentThread());
//
// Validate all of the timestamps from the information structure. If the
// timestamp is -1, then that means that the user doesn't want the file
// system automatically updating the timestamps, such as modifying the last
// write time when writing to the file. If the timestamp is zero, then no
// change should be made to that timestamp.
//
if ((BasicInformation->CreationTime.QuadPart == -1) ||
(BasicInformation->CreationTime.QuadPart == 0)) {
//
// Don't change the creation time. Grab the current creation time from
// the file control block.
//
CreationTime = Fcb->CreationTime;
} else {
//
// Convert the NT system time to a FAT time.
//
if (!FatxTimeToFatTimestamp(&BasicInformation->CreationTime,
&CreationTime)) {
return STATUS_INVALID_PARAMETER;
}
if (CreationTime.AsULONG != Fcb->CreationTime.AsULONG) {
ChangesMade = TRUE;
}
}
if ((BasicInformation->LastWriteTime.QuadPart == -1) ||
(BasicInformation->LastWriteTime.QuadPart == 0)) {
//
// Don't change the last write time. Grab the current last write time
// from the file control block.
//
LastWriteTime = Fcb->LastWriteTime;
//
// If the caller doesn't want us updating the last write time, then
// mark the file control block.
//
if (BasicInformation->LastWriteTime.LowPart == -1) {
Fcb->Flags |= FAT_FCB_DISABLE_LAST_WRITE_TIME;
}
} else {
//
// Verify that we can convert the time to a FAT timestamp. At this
// point, we don't care about the result.
//
if (!FatxTimeToFatTimestamp(&BasicInformation->LastWriteTime,
&FatTimestamp)) {
return STATUS_INVALID_PARAMETER;
}
LastWriteTime = BasicInformation->LastWriteTime;
if (LastWriteTime.QuadPart != Fcb->LastWriteTime.QuadPart) {
ChangesMade = TRUE;
}
}
if ((BasicInformation->LastAccessTime.QuadPart == -1) ||
(BasicInformation->LastAccessTime.QuadPart == 0)) {
//
// Don't change the last access time. Grab the current last access time
// from the file control block.
//
LastAccessTime = Fcb->LastAccessTime;
} else {
//
// Convert the NT system time to a FAT time.
//
if (!FatxTimeToFatTimestamp(&BasicInformation->LastAccessTime,
&LastAccessTime)) {
return STATUS_INVALID_PARAMETER;
}
if (LastAccessTime.AsULONG != Fcb->LastAccessTime.AsULONG) {
ChangesMade = TRUE;
}
}
//
// Validate the file attributes.
//
if (BasicInformation->FileAttributes == 0) {
//
// Don't change the file attributes. Grab the current file attributes
// from the file control block.
//
FileAttributes = Fcb->FileAttributes;
} else {
FileAttributes =
(UCHAR)FatxFilterFileAttributes(BasicInformation->FileAttributes);
//
// Make sure that the directory attribute doesn't change.
//
if (FatxIsFlagSet(Fcb->Flags, FAT_FCB_DIRECTORY)) {
FileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
} else if (FatxIsFlagSet(FileAttributes, FILE_ATTRIBUTE_DIRECTORY)) {
return STATUS_INVALID_PARAMETER;
}
if (FileAttributes != Fcb->FileAttributes) {
ChangesMade = TRUE;
}
}
if (ChangesMade) {
//
// Now that we've validated all of the input from the information
// structure, apply the changes to the file control block and write out
// the changes. If the write fails, we can live with the in-memory copy
// of these attributes being different than the on-disk copies.
//
Fcb->CreationTime = CreationTime;
Fcb->LastWriteTime = LastWriteTime;
Fcb->LastAccessTime = LastAccessTime;
Fcb->FileAttributes = FileAttributes;
status = FatxUpdateDirectoryEntry(VolumeExtension, Irp, Fcb);
} else {
status = STATUS_SUCCESS;
}
return status;
}
NTSTATUS
FatxSetRenameInformation(
IN PFAT_VOLUME_EXTENSION VolumeExtension,
IN PIRP Irp
)
/*++
Routine Description:
This routine renames the supplied file object.
Arguments:
VolumeExtension - Specifies the extension that the I/O request is for.
Irp - Specifies the packet that describes the I/O request.
Return Value:
Status of operation.
--*/
{
NTSTATUS status;
PIO_STACK_LOCATION IrpSp;
PFILE_OBJECT FileObject;
PFAT_FCB Fcb;
PFILE_RENAME_INFORMATION RenameInformation;
POSTR OriginalFileNameBuffer;
UCHAR OriginalFileNameLength;
POSTR EndOfFileName;
POSTR StartOfFileName;
OBJECT_STRING TargetFileName;
POSTR TargetFileNameBuffer;
PFAT_FCB TargetDirectoryFcb;
ULONG NewPathNameLength;
DIRENT DirectoryEntry;
ULONG DirectoryByteOffset;
ULONG EmptyDirectoryByteOffset;
PFAT_FCB FoundOrNewFcb;
IrpSp = IoGetCurrentIrpStackLocation(Irp);
FileObject = IrpSp->FileObject;
Fcb = (PFAT_FCB)FileObject->FsContext;
RenameInformation = (PFILE_RENAME_INFORMATION)Irp->UserBuffer;
ASSERT(FatxIsFlagClear(Fcb->Flags, FAT_FCB_VOLUME));
ASSERT(VolumeExtension->VolumeMutexExclusiveOwner == KeGetCurrentThread());
//
// It's possible for the file control block to be NULL if a file failed to
// be moved from one directory to another. In that case,
// FatxSetRenameInformation sets the file's parent file control block to
// NULL.
//
if (Fcb->ParentFcb == NULL) {
return STATUS_FILE_CORRUPT_ERROR;
}
//
// There must be at least some characters in the file name.
//
if (RenameInformation->FileName.Length == 0) {
return STATUS_INVALID_PARAMETER;
}
//
// Save off the original file name buffer and length so that we can attempt
// to unroll errors below.
//
OriginalFileNameBuffer = Fcb->FileNameBuffer;
OriginalFileNameLength = Fcb->FileNameLength;
ASSERT(OriginalFileNameBuffer != NULL);
//
// Figure out the starting and ending (exclusive) range of the target file
// name. In a normal NT file system, the target file name would have been
// attached to the target file object, but we don't keep the file name as
// part of the file objects, so we have to go back to user's original
// buffer.
//
// For compatibility with NT, if the name ends with a backslash, ignore the
// character.
//
EndOfFileName = (POSTR)((PCHAR)RenameInformation->FileName.Buffer +
RenameInformation->FileName.Length);
if (*(EndOfFileName - 1) == OBJ_NAME_PATH_SEPARATOR) {
EndOfFileName--;
}
StartOfFileName = EndOfFileName;
while (StartOfFileName > RenameInformation->FileName.Buffer) {
if (*StartOfFileName == OBJ_NAME_PATH_SEPARATOR) {
StartOfFileName++;
break;
}
StartOfFileName--;
}
//
// Validate that this is a legal FAT file name.
//
TargetFileName.Length = (USHORT)((PCHAR)EndOfFileName - (PCHAR)StartOfFileName);
TargetFileName.MaximumLength = TargetFileName.Length;
TargetFileName.Buffer = StartOfFileName;
if (!FatxIsValidFatFileName(&TargetFileName)) {
return STATUS_OBJECT_NAME_INVALID;
}
//
// Figure out which directory the file will be renamed into.
//
if (IrpSp->Parameters.SetFile.FileObject != NULL) {
TargetDirectoryFcb =
(PFAT_FCB)IrpSp->Parameters.SetFile.FileObject->FsContext;
} else {
TargetDirectoryFcb = Fcb->ParentFcb;
}
ASSERT(FatxIsFlagSet(TargetDirectoryFcb->Flags, FAT_FCB_DIRECTORY));
//
// Verify that the path doesn't exceed the length restictions.
//
NewPathNameLength = TargetDirectoryFcb->PathNameLength +
sizeof(OBJ_NAME_PATH_SEPARATOR) + TargetFileName.Length;
if (NewPathNameLength > FAT_PATH_NAME_LIMIT * sizeof(OCHAR)) {
return STATUS_OBJECT_NAME_INVALID;
}
//
// If this is a directory, then verify that there are no open files under
// this directory.
//
if (FatxIsFlagSet(Fcb->Flags, FAT_FCB_DIRECTORY) &&
!IsListEmpty(&Fcb->Directory.ChildFcbList)) {
return STATUS_ACCESS_DENIED;
}
//
// Check if the target file name already exists.
//
status = FatxLookupElementNameInDirectory(VolumeExtension, Irp,
TargetDirectoryFcb, &TargetFileName, &DirectoryEntry,
&DirectoryByteOffset, &EmptyDirectoryByteOffset);
if (NT_SUCCESS(status)) {
//
// The target file name already exists. Check if the caller allows us
// to replace an existing file and that we didn't find a directory or
// read-only file.
//
if (!RenameInformation->ReplaceIfExists ||
FatxIsFlagSet(DirectoryEntry.FileAttributes, FILE_ATTRIBUTE_DIRECTORY) ||
FatxIsFlagSet(DirectoryEntry.FileAttributes, FILE_ATTRIBUTE_READONLY)) {
return STATUS_OBJECT_NAME_COLLISION;
}
//
// Don't overwrite a file that's already open.
//
if (FatxFindOpenChildFcb(TargetDirectoryFcb, &TargetFileName,
&FoundOrNewFcb)) {
return STATUS_ACCESS_DENIED;
}
//
// Delete the existing file.
//
status = FatxDeleteFile(VolumeExtension, Irp, TargetDirectoryFcb,
DirectoryByteOffset);
if (!NT_SUCCESS(status)) {
return status;
}
//
// We can use the old file's directory byte offset as the byte offset
// for the renamed file, if needed.
//
EmptyDirectoryByteOffset = DirectoryByteOffset;
} else if (status != STATUS_OBJECT_NAME_NOT_FOUND) {
//
// Some unknown error was returned, so bail out now.
//
return status;
}
//
// We'll need to replace the file name buffer that's currently stored in the
// file control block. Allocate a new string and copy the target file name
// into the buffer.
//
TargetFileNameBuffer = (POSTR)ExAllocatePoolWithTag(TargetFileName.Length,
'nFtF');
if (TargetFileNameBuffer == NULL) {
return STATUS_INSUFFICIENT_RESOURCES;
}
RtlCopyMemory(TargetFileNameBuffer, TargetFileName.Buffer,
TargetFileName.Length);
//
// Check if we're renaming inside the same directory. If so, change the
// file name in the file control block and commit the change. If this
// fails, back out to the original file name.
//
if (TargetDirectoryFcb == Fcb->ParentFcb) {
Fcb->FileNameBuffer = TargetFileNameBuffer;
Fcb->FileNameLength = (UCHAR)TargetFileName.Length;
status = FatxUpdateDirectoryEntry(VolumeExtension, Irp, Fcb);
if (NT_SUCCESS(status)) {
//
// If the original file name buffer didn't come from the file
// control block itself, then free it now.
//
if (FatxIsFlagSet(Fcb->Flags, FAT_FCB_FREE_FILE_NAME_BUFFER)) {
ExFreePool(OriginalFileNameBuffer);
}
//
// We're going to keep the file name that we allocated, so set a
// flag that the close code can look at to know that the file name
// buffer must be freed.
//
Fcb->Flags |= FAT_FCB_FREE_FILE_NAME_BUFFER;
} else {
Fcb->FileNameBuffer = OriginalFileNameBuffer;
Fcb->FileNameLength = OriginalFileNameLength;
}
return status;
}
//
// We're moving a file across directories.
//
//
// If there isn't an empty directory entry, then we'll need to add
// another cluster to the directory.
//
if (EmptyDirectoryByteOffset == MAXULONG) {
//
// If we found the end of the directory stream, then we must have
// established how many bytes are allocated to the file.
//
ASSERT(TargetDirectoryFcb->AllocationSize != MAXULONG);
//
// The new directory empty will be placed at the start of the new
// extension.
//
EmptyDirectoryByteOffset = TargetDirectoryFcb->AllocationSize;
//
// Attempt to add another cluster to the directory's allocation.
//
status = FatxExtendDirectoryAllocation(VolumeExtension, Irp,
TargetDirectoryFcb);
if (!NT_SUCCESS(status)) {
ExFreePool(TargetFileNameBuffer);
return status;
}
}
//
// Mark the directory entry as deleted. If the system goes down after
// successfully committing this change, then we've lost the file.
//
status = FatxMarkDirectoryEntryDeleted(VolumeExtension, Irp, Fcb);
if (!NT_SUCCESS(status)) {
ExFreePool(TargetFileNameBuffer);
return status;
}
//
// Detach the file control block from its old parent.
//
RemoveEntryList(&Fcb->SiblingFcbLink);
FatxDereferenceFcb(Fcb->ParentFcb);
//
// Attach the file control block to its new parent.
//
TargetDirectoryFcb->ReferenceCount++;
Fcb->ParentFcb = TargetDirectoryFcb;
InsertHeadList(&TargetDirectoryFcb->Directory.ChildFcbList,
&Fcb->SiblingFcbLink);
//
// Update the file control block with its new file name and directory byte
// offset.
//
Fcb->FileNameBuffer = TargetFileNameBuffer;
Fcb->FileNameLength = (UCHAR)TargetFileName.Length;
Fcb->DirectoryByteOffset = EmptyDirectoryByteOffset;
status = FatxUpdateDirectoryEntry(VolumeExtension, Irp, Fcb);
if (NT_SUCCESS(status)) {
//
// Update the path name length stored in the file control block.
//
Fcb->PathNameLength = (UCHAR)NewPathNameLength;
//
// If the original file name buffer didn't come from the file control
// block itself, then free it now.
//
if (FatxIsFlagSet(Fcb->Flags, FAT_FCB_FREE_FILE_NAME_BUFFER)) {
ExFreePool(OriginalFileNameBuffer);
}
//
// We're going to keep the file name that we allocated, so set a flag
// that the close code can look at to know that the file name buffer
// must be freed.
//
Fcb->Flags |= FAT_FCB_FREE_FILE_NAME_BUFFER;
} else {
//
// If this commit fails, then assume that we're not going to have any
// hope of restoring the original state of the on-disk structures.
// Leave the file control block in a zombie state by disconnecting it
// from any parent directory.
//
FatxDbgPrint(("FATX: failed to move file across directories\n"));
Fcb->FileNameBuffer = OriginalFileNameBuffer;
Fcb->FileNameLength = OriginalFileNameLength;
RemoveEntryList(&Fcb->SiblingFcbLink);
FatxDereferenceFcb(TargetDirectoryFcb);
Fcb->ParentFcb = NULL;
ExFreePool(TargetFileNameBuffer);
}
return status;
}
NTSTATUS
FatxSetDispositionInformation(
IN PFAT_VOLUME_EXTENSION VolumeExtension,
IN PIRP Irp,
IN PFILE_OBJECT FileObject,
IN PFILE_DISPOSITION_INFORMATION DispositionInformation
)
/*++
Routine Description:
This routine changes the disposition information of the supplied file
object.
Arguments:
VolumeExtension - Specifies the extension that the I/O request is for.
Irp - Specifies the packet that describes the I/O request.
FileObject - Specifies the file object to apply the information to.
DispositionInformation - Specifies the disposition information to apply to
the file.
Return Value:
Status of operation.
--*/
{
NTSTATUS status;
PFAT_FCB Fcb;
Fcb = (PFAT_FCB)FileObject->FsContext;
ASSERT(FatxIsFlagClear(Fcb->Flags, FAT_FCB_VOLUME));
ASSERT(VolumeExtension->VolumeMutexExclusiveOwner == KeGetCurrentThread());
//
// If the caller doesn't want to delete the file, then clear the flags from
// the file control block and the file object.
//
if (!DispositionInformation->DeleteFile) {
Fcb->Flags &= ~FAT_FCB_DELETE_ON_CLOSE;
FileObject->DeletePending = FALSE;
return STATUS_SUCCESS;
}
//
// If this is a read only file, then it cannot be deleted.
//
if (FatxIsFlagSet(Fcb->FileAttributes, FILE_ATTRIBUTE_READONLY)) {
return STATUS_CANNOT_DELETE;
}
//
// If this is a directory, then check if the directory is empty. We'll also
// allow corrupt directories to be removed from the disk.
//
if (FatxIsFlagSet(Fcb->Flags, FAT_FCB_DIRECTORY)) {
status = FatxIsDirectoryEmpty(VolumeExtension, Irp, Fcb);
if ((status != STATUS_SUCCESS) && (status != STATUS_FILE_CORRUPT_ERROR)) {
return status;
}
}
//
// Mark the file as delete pending.
//
Fcb->Flags |= FAT_FCB_DELETE_ON_CLOSE;
FileObject->DeletePending = TRUE;
return STATUS_SUCCESS;
}
NTSTATUS
FatxSetEndOfFileInformation(
IN PFAT_VOLUME_EXTENSION VolumeExtension,
IN PIRP Irp,
IN PFILE_OBJECT FileObject,
IN PFILE_END_OF_FILE_INFORMATION EndOfFileInformation
)
/*++
Routine Description:
This routine changes the end of file information of the supplied file
object.
Arguments:
VolumeExtension - Specifies the extension that the I/O request is for.
Irp - Specifies the packet that describes the I/O request.
FileObject - Specifies the file object to apply the information to.
EndOfFileInformation - Specifies the end of file information to apply to
the file.
Return Value:
Status of operation.
--*/
{
NTSTATUS status;
PFAT_FCB FileFcb;
ULONG NewFileSize;
ULONG OriginalFileSize;
FileFcb = (PFAT_FCB)FileObject->FsContext;
ASSERT(FatxIsFlagClear(FileFcb->Flags, FAT_FCB_VOLUME | FAT_FCB_DIRECTORY));
ASSERT(VolumeExtension->VolumeMutexExclusiveOwner == KeGetCurrentThread());
ASSERT(FileFcb->File.FileMutexExclusiveOwner == KeGetCurrentThread());
//
// Validate that the upper 32-bits of the end of file are zero.
//
if (EndOfFileInformation->EndOfFile.HighPart != 0) {
return STATUS_DISK_FULL;
}
//
// If the new file size and the current file size match, then there's
// nothing to do.
//
NewFileSize = EndOfFileInformation->EndOfFile.LowPart;
OriginalFileSize = FileFcb->FileSize;
if (NewFileSize == OriginalFileSize) {
return STATUS_SUCCESS;
}
//
// If the new file size is beyond the current file size, then we might need
// to extend the file allocation. Note that we want to make sure that we
// don't truncate the allocation here if the end of file is less than the
// allocated size.
//
if (NewFileSize > OriginalFileSize) {
status = FatxSetAllocationSize(VolumeExtension, Irp, FileFcb,
NewFileSize, FALSE, TRUE);
if (!NT_SUCCESS(status)) {
return status;
}
ASSERT(FileFcb->FileSize == OriginalFileSize);
}
//
// Change the file size in the file control block to the requested file
// size.
//
FileFcb->FileSize = NewFileSize;
//
// Commit the directory change. If this fails, then back out the changes to
// the file control block so that we still mirror the on-disk structure.
//
status = FatxUpdateDirectoryEntry(VolumeExtension, Irp, FileFcb);
if (!NT_SUCCESS(status)) {
FileFcb->FileSize = OriginalFileSize;
}
return status;
}
NTSTATUS
FatxFsdSetInformation(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
This routine is called by the I/O manager to handle IRP_MJ_SET_INFORMATION
requests.
Arguments:
DeviceObject - Specifies the device object that the I/O request is for.
Irp - Specifies the packet that describes the I/O request.
Return Value:
Status of operation.
--*/
{
NTSTATUS status;
PFAT_VOLUME_EXTENSION VolumeExtension;
PIO_STACK_LOCATION IrpSp;
PFILE_OBJECT FileObject;
PFAT_FCB Fcb;
PFILE_POSITION_INFORMATION PositionInformation;
PFILE_ALLOCATION_INFORMATION AllocationInformation;
VolumeExtension = (PFAT_VOLUME_EXTENSION)DeviceObject->DeviceExtension;
IrpSp = IoGetCurrentIrpStackLocation(Irp);
FileObject = IrpSp->FileObject;
Fcb = (PFAT_FCB)FileObject->FsContext;
//
// If this is a file control block for a standard file, then we need to
// acquire the file's mutex in order to synchronize access to the Flags
// field of the file control block.
//
if (FatxIsFlagClear(Fcb->Flags, FAT_FCB_VOLUME | FAT_FCB_DIRECTORY)) {
FatxAcquireFileMutexExclusive(Fcb);
}
FatxAcquireVolumeMutexExclusive(VolumeExtension);
//
// Check if the volume has been dismounted.
//
if (FatxIsFlagSet(VolumeExtension->Flags, FAT_VOLUME_DISMOUNTED)) {
status = STATUS_VOLUME_DISMOUNTED;
goto CleanupAndExit;
}
//
// Check if the file object has already been cleaned up. We don't allow a
// a file object to be accessed after its handle has been closed.
//
if (FatxIsFlagSet(FileObject->Flags, FO_CLEANUP_COMPLETE)) {
status = STATUS_FILE_CLOSED;
goto CleanupAndExit;
}
//
// Dispatch the information class function.
//
status = STATUS_SUCCESS;
switch (IrpSp->Parameters.QueryFile.FileInformationClass) {
case FileBasicInformation:
//
// We can't change the time stamps or the file attributes for the
// volume or the root directory.
//
if (FatxIsFlagSet(Fcb->Flags, FAT_FCB_VOLUME) ||
FatxIsFlagSet(Fcb->Flags, FAT_FCB_ROOT_DIRECTORY)) {
status = STATUS_INVALID_PARAMETER;
goto CleanupAndExit;
}
status = FatxSetBasicInformation(VolumeExtension, Irp, FileObject,
(PFILE_BASIC_INFORMATION)Irp->UserBuffer);
break;
case FileRenameInformation:
//
// We can't rename the volume or the root directory.
//
if (FatxIsFlagSet(Fcb->Flags, FAT_FCB_VOLUME) ||
FatxIsFlagSet(Fcb->Flags, FAT_FCB_ROOT_DIRECTORY)) {
status = STATUS_INVALID_PARAMETER;
goto CleanupAndExit;
}
status = FatxSetRenameInformation(VolumeExtension, Irp);
break;
case FileDispositionInformation:
//
// We can't delete the volume or the root directory.
//
if (FatxIsFlagSet(Fcb->Flags, FAT_FCB_VOLUME) ||
FatxIsFlagSet(Fcb->Flags, FAT_FCB_ROOT_DIRECTORY)) {
status = STATUS_INVALID_PARAMETER;
goto CleanupAndExit;
}
status = FatxSetDispositionInformation(VolumeExtension, Irp, FileObject,
(PFILE_DISPOSITION_INFORMATION)Irp->UserBuffer);
break;
case FilePositionInformation:
PositionInformation = (PFILE_POSITION_INFORMATION)Irp->UserBuffer;
//
// If the file was opened without intermediate buffering, then the
// byte offset must be sector aligned.
//
if (FatxIsFlagSet(FileObject->Flags, FO_NO_INTERMEDIATE_BUFFERING) &&
!FatxIsSectorAligned(VolumeExtension,
PositionInformation->CurrentByteOffset.LowPart)) {
status = STATUS_INVALID_PARAMETER;
goto CleanupAndExit;
}
FileObject->CurrentByteOffset = PositionInformation->CurrentByteOffset;
break;
case FileAllocationInformation:
AllocationInformation =
(PFILE_ALLOCATION_INFORMATION)Irp->UserBuffer;
//
// We can't change the allocation size for the volume or any
// directory.
//
if (FatxIsFlagSet(Fcb->Flags, FAT_FCB_VOLUME) ||
FatxIsFlagSet(Fcb->Flags, FAT_FCB_DIRECTORY)) {
status = STATUS_INVALID_PARAMETER;
goto CleanupAndExit;
}
//
// Validate that the upper 32-bits of the allocation size are zero.
//
if (AllocationInformation->AllocationSize.HighPart != 0) {
status = STATUS_DISK_FULL;
goto CleanupAndExit;
}
status = FatxSetAllocationSize(VolumeExtension, Irp, Fcb,
AllocationInformation->AllocationSize.LowPart, FALSE, FALSE);
break;
case FileEndOfFileInformation:
//
// We can't change the end of file for the volume or any directory.
//
if (FatxIsFlagSet(Fcb->Flags, FAT_FCB_VOLUME) ||
FatxIsFlagSet(Fcb->Flags, FAT_FCB_DIRECTORY)) {
status = STATUS_INVALID_PARAMETER;
goto CleanupAndExit;
}
status = FatxSetEndOfFileInformation(VolumeExtension, Irp, FileObject,
(PFILE_END_OF_FILE_INFORMATION)Irp->UserBuffer);
break;
default:
status = STATUS_INVALID_PARAMETER;
break;
}
CleanupAndExit:
FatxReleaseVolumeMutex(VolumeExtension);
if (FatxIsFlagClear(Fcb->Flags, FAT_FCB_VOLUME | FAT_FCB_DIRECTORY)) {
FatxReleaseFileMutex(Fcb);
}
Irp->IoStatus.Status = status;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return status;
}