/*++ 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; }