|
|
/*++
Copyright (c) 1989-2000 Microsoft Corporation
Module Name:
FileInfo.c
Abstract:
This module implements the File Information routines for Fat called by the dispatch driver.
// @@BEGIN_DDKSPLIT
Author:
Gary Kimura [GaryKi] 22-Oct-1990
Revision History:
// @@END_DDKSPLIT
--*/
#include "FatProcs.h"
//
// The Bug check file id for this module
//
#define BugCheckFileId (FAT_BUG_CHECK_FILEINFO)
//
// The local debug trace level
//
#define Dbg (DEBUG_TRACE_FILEINFO)
VOID FatQueryBasicInfo ( IN PIRP_CONTEXT IrpContext, IN PFCB Fcb, IN PFILE_OBJECT FileObject, IN OUT PFILE_BASIC_INFORMATION Buffer, IN OUT PLONG Length );
VOID FatQueryStandardInfo ( IN PIRP_CONTEXT IrpContext, IN PFCB Fcb, IN OUT PFILE_STANDARD_INFORMATION Buffer, IN OUT PLONG Length );
VOID FatQueryInternalInfo ( IN PIRP_CONTEXT IrpContext, IN PFCB Fcb, IN OUT PFILE_INTERNAL_INFORMATION Buffer, IN OUT PLONG Length );
VOID FatQueryEaInfo ( IN PIRP_CONTEXT IrpContext, IN PFCB Fcb, IN OUT PFILE_EA_INFORMATION Buffer, IN OUT PLONG Length );
VOID FatQueryPositionInfo ( IN PIRP_CONTEXT IrpContext, IN PFILE_OBJECT FileObject, IN OUT PFILE_POSITION_INFORMATION Buffer, IN OUT PLONG Length );
VOID FatQueryNameInfo ( IN PIRP_CONTEXT IrpContext, IN PFCB Fcb, IN PCCB Ccb, IN OUT PFILE_NAME_INFORMATION Buffer, IN OUT PLONG Length );
VOID FatQueryShortNameInfo ( IN PIRP_CONTEXT IrpContext, IN PFCB Fcb, IN OUT PFILE_NAME_INFORMATION Buffer, IN OUT PLONG Length );
VOID FatQueryNetworkInfo ( IN PIRP_CONTEXT IrpContext, IN PFCB Fcb, IN PFILE_OBJECT FileObject, IN OUT PFILE_NETWORK_OPEN_INFORMATION Buffer, IN OUT PLONG Length );
NTSTATUS FatSetBasicInfo ( IN PIRP_CONTEXT IrpContext, IN PIRP Irp, IN PFCB Fcb, IN PCCB Ccb );
NTSTATUS FatSetDispositionInfo ( IN PIRP_CONTEXT IrpContext, IN PIRP Irp, IN PFILE_OBJECT FileObject, IN PFCB Fcb );
NTSTATUS FatSetRenameInfo ( IN PIRP_CONTEXT IrpContext, IN PIRP Irp, IN PVCB Vcb, IN PFCB Fcb, IN PCCB Ccb );
NTSTATUS FatSetPositionInfo ( IN PIRP_CONTEXT IrpContext, IN PIRP Irp, IN PFILE_OBJECT FileObject );
NTSTATUS FatSetAllocationInfo ( IN PIRP_CONTEXT IrpContext, IN PIRP Irp, IN PFCB Fcb, IN PFILE_OBJECT FileObject );
NTSTATUS FatSetEndOfFileInfo ( IN PIRP_CONTEXT IrpContext, IN PIRP Irp, IN PFILE_OBJECT FileObject, IN PVCB Vcb, IN PFCB Fcb );
VOID FatDeleteFile ( IN PIRP_CONTEXT IrpContext, IN PDCB TargetDcb, IN ULONG LfnOffset, IN ULONG DirentOffset, IN PDIRENT Dirent, IN PUNICODE_STRING Lfn );
VOID FatRenameEAs ( IN PIRP_CONTEXT IrpContext, IN PFCB Fcb, IN USHORT ExtendedAttributes, IN POEM_STRING OldOemName );
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, FatCommonQueryInformation)
#pragma alloc_text(PAGE, FatCommonSetInformation)
#pragma alloc_text(PAGE, FatFsdQueryInformation)
#pragma alloc_text(PAGE, FatFsdSetInformation)
#pragma alloc_text(PAGE, FatQueryBasicInfo)
#pragma alloc_text(PAGE, FatQueryEaInfo)
#pragma alloc_text(PAGE, FatQueryInternalInfo)
#pragma alloc_text(PAGE, FatQueryNameInfo)
#pragma alloc_text(PAGE, FatQueryNetworkInfo)
#pragma alloc_text(PAGE, FatQueryShortNameInfo)
#pragma alloc_text(PAGE, FatQueryPositionInfo)
#pragma alloc_text(PAGE, FatQueryStandardInfo)
#pragma alloc_text(PAGE, FatSetAllocationInfo)
#pragma alloc_text(PAGE, FatSetBasicInfo)
#pragma alloc_text(PAGE, FatSetDispositionInfo)
#pragma alloc_text(PAGE, FatSetEndOfFileInfo)
#pragma alloc_text(PAGE, FatSetPositionInfo)
#pragma alloc_text(PAGE, FatSetRenameInfo)
#pragma alloc_text(PAGE, FatDeleteFile)
#pragma alloc_text(PAGE, FatRenameEAs)
#endif
NTSTATUS FatFsdQueryInformation ( IN PVOLUME_DEVICE_OBJECT VolumeDeviceObject, IN PIRP Irp )
/*++
Routine Description:
This routine implements the Fsd part of the NtQueryInformationFile API call.
Arguments:
VolumeDeviceObject - Supplies the volume device object where the file being queried exists.
Irp - Supplies the Irp being processed.
Return Value:
NTSTATUS - The FSD status for the Irp.
--*/
{ NTSTATUS Status; PIRP_CONTEXT IrpContext = NULL;
BOOLEAN TopLevel;
DebugTrace(+1, Dbg, "FatFsdQueryInformation\n", 0);
//
// Call the common query routine, with blocking allowed if synchronous
//
FsRtlEnterFileSystem();
TopLevel = FatIsIrpTopLevel( Irp );
try {
IrpContext = FatCreateIrpContext( Irp, CanFsdWait( Irp ) );
Status = FatCommonQueryInformation( IrpContext, Irp );
} except(FatExceptionFilter( IrpContext, GetExceptionInformation() )) {
//
// We had some trouble trying to perform the requested
// operation, so we'll abort the I/O request with
// the error status that we get back from the
// execption code
//
Status = FatProcessException( IrpContext, Irp, GetExceptionCode() ); }
if (TopLevel) { IoSetTopLevelIrp( NULL ); }
FsRtlExitFileSystem();
//
// And return to our caller
//
DebugTrace(-1, Dbg, "FatFsdQueryInformation -> %08lx\n", Status);
UNREFERENCED_PARAMETER( VolumeDeviceObject );
return Status; }
NTSTATUS FatFsdSetInformation ( IN PVOLUME_DEVICE_OBJECT VolumeDeviceObject, IN PIRP Irp )
/*++
Routine Description:
This routine implements the FSD part of the NtSetInformationFile API call.
Arguments:
VolumeDeviceObject - Supplies the volume device object where the file being set exists.
Irp - Supplies the Irp being processed.
Return Value:
NTSTATUS - The FSD status for the Irp.
--*/
{ NTSTATUS Status; PIRP_CONTEXT IrpContext = NULL;
BOOLEAN TopLevel;
DebugTrace(+1, Dbg, "FatFsdSetInformation\n", 0);
//
// Call the common set routine, with blocking allowed if synchronous
//
FsRtlEnterFileSystem();
TopLevel = FatIsIrpTopLevel( Irp );
try {
IrpContext = FatCreateIrpContext( Irp, CanFsdWait( Irp ) );
Status = FatCommonSetInformation( IrpContext, Irp );
} except(FatExceptionFilter( IrpContext, GetExceptionInformation() )) {
//
// We had some trouble trying to perform the requested
// operation, so we'll abort the I/O request with
// the error status that we get back from the
// execption code
//
Status = FatProcessException( IrpContext, Irp, GetExceptionCode() ); }
if (TopLevel) { IoSetTopLevelIrp( NULL ); }
FsRtlExitFileSystem();
//
// And return to our caller
//
DebugTrace(-1, Dbg, "FatFsdSetInformation -> %08lx\n", Status);
UNREFERENCED_PARAMETER( VolumeDeviceObject );
return Status; }
NTSTATUS FatCommonQueryInformation ( IN PIRP_CONTEXT IrpContext, IN PIRP Irp )
/*++
Routine Description:
This is the common routine for querying file information called by both the fsd and fsp threads.
Arguments:
Irp - Supplies the Irp being processed
Return Value:
NTSTATUS - The return status for the operation
--*/
{ NTSTATUS Status;
PIO_STACK_LOCATION IrpSp;
PFILE_OBJECT FileObject;
LONG Length; FILE_INFORMATION_CLASS FileInformationClass; PVOID Buffer;
TYPE_OF_OPEN TypeOfOpen; PVCB Vcb; PFCB Fcb; PCCB Ccb;
BOOLEAN FcbAcquired;
PFILE_ALL_INFORMATION AllInfo;
//
// Get the current stack location
//
IrpSp = IoGetCurrentIrpStackLocation( Irp );
FileObject = IrpSp->FileObject;
DebugTrace(+1, Dbg, "FatCommonQueryInformation...\n", 0); DebugTrace( 0, Dbg, "Irp = %08lx\n", Irp); DebugTrace( 0, Dbg, "->Length = %08lx\n", IrpSp->Parameters.QueryFile.Length); DebugTrace( 0, Dbg, "->FileInformationClass = %08lx\n", IrpSp->Parameters.QueryFile.FileInformationClass); DebugTrace( 0, Dbg, "->Buffer = %08lx\n", Irp->AssociatedIrp.SystemBuffer);
//
// Reference our input parameters to make things easier
//
Length = (LONG)IrpSp->Parameters.QueryFile.Length; FileInformationClass = IrpSp->Parameters.QueryFile.FileInformationClass; Buffer = Irp->AssociatedIrp.SystemBuffer;
//
// Decode the file object
//
TypeOfOpen = FatDecodeFileObject( FileObject, &Vcb, &Fcb, &Ccb );
FcbAcquired = FALSE; Status = STATUS_SUCCESS;
try {
//
// Case on the type of open we're dealing with
//
switch (TypeOfOpen) {
case UserVolumeOpen:
//
// We cannot query the user volume open.
//
Status = STATUS_INVALID_PARAMETER; break;
case UserFileOpen:
case UserDirectoryOpen:
case DirectoryFile:
//
// Acquire shared access to the fcb, except for a paging file
// in order to avoid deadlocks with Mm.
//
if (!FlagOn( Fcb->FcbState, FCB_STATE_PAGING_FILE )) {
if (!FatAcquireSharedFcb( IrpContext, Fcb )) {
DebugTrace(0, Dbg, "Cannot acquire Fcb\n", 0); Status = FatFsdPostRequest( IrpContext, Irp ); IrpContext = NULL; Irp = NULL;
try_return( Status ); }
FcbAcquired = TRUE; }
//
// Make sure the Fcb is in a usable condition. This
// will raise an error condition if the fcb is unusable
//
FatVerifyFcb( IrpContext, Fcb );
//
// Based on the information class we'll do different
// actions. Each of hte procedures that we're calling fills
// up the output buffer, if possible. They will raise the
// status STATUS_BUFFER_OVERFLOW for an insufficient buffer.
// This is considered a somewhat unusual case and is handled
// more cleanly with the exception mechanism rather than
// testing a return status value for each call.
//
switch (FileInformationClass) {
case FileAllInformation:
//
// For the all information class we'll typecast a local
// pointer to the output buffer and then call the
// individual routines to fill in the buffer.
//
AllInfo = Buffer; Length -= (sizeof(FILE_ACCESS_INFORMATION) + sizeof(FILE_MODE_INFORMATION) + sizeof(FILE_ALIGNMENT_INFORMATION));
FatQueryBasicInfo( IrpContext, Fcb, FileObject, &AllInfo->BasicInformation, &Length ); FatQueryStandardInfo( IrpContext, Fcb, &AllInfo->StandardInformation, &Length ); FatQueryInternalInfo( IrpContext, Fcb, &AllInfo->InternalInformation, &Length ); FatQueryEaInfo( IrpContext, Fcb, &AllInfo->EaInformation, &Length ); FatQueryPositionInfo( IrpContext, FileObject, &AllInfo->PositionInformation, &Length ); FatQueryNameInfo( IrpContext, Fcb, Ccb, &AllInfo->NameInformation, &Length );
break;
case FileBasicInformation:
FatQueryBasicInfo( IrpContext, Fcb, FileObject, Buffer, &Length ); break;
case FileStandardInformation:
FatQueryStandardInfo( IrpContext, Fcb, Buffer, &Length ); break;
case FileInternalInformation:
FatQueryInternalInfo( IrpContext, Fcb, Buffer, &Length ); break;
case FileEaInformation:
FatQueryEaInfo( IrpContext, Fcb, Buffer, &Length ); break;
case FilePositionInformation:
FatQueryPositionInfo( IrpContext, FileObject, Buffer, &Length ); break;
case FileNameInformation:
FatQueryNameInfo( IrpContext, Fcb, Ccb, Buffer, &Length ); break;
case FileAlternateNameInformation:
FatQueryShortNameInfo( IrpContext, Fcb, Buffer, &Length ); break;
case FileNetworkOpenInformation:
FatQueryNetworkInfo( IrpContext, Fcb, FileObject, Buffer, &Length ); break;
default:
Status = STATUS_INVALID_PARAMETER; break; }
break; default:
KdPrintEx((DPFLTR_FASTFAT_ID, DPFLTR_INFO_LEVEL, "FATQueryFile, Illegal TypeOfOpen = %08lx\n", TypeOfOpen));
Status = STATUS_INVALID_PARAMETER; break; }
//
// If we overflowed the buffer, set the length to 0 and change the
// status to STATUS_BUFFER_OVERFLOW.
//
if ( Length < 0 ) {
Status = STATUS_BUFFER_OVERFLOW;
Length = 0; }
//
// Set the information field to the number of bytes actually filled in
// and then complete the request
//
Irp->IoStatus.Information = IrpSp->Parameters.QueryFile.Length - Length;
try_exit: NOTHING; } finally {
DebugUnwind( FatCommonQueryInformation );
if (FcbAcquired) { FatReleaseFcb( IrpContext, Fcb ); }
if (!AbnormalTermination()) {
FatCompleteRequest( IrpContext, Irp, Status ); }
DebugTrace(-1, Dbg, "FatCommonQueryInformation -> %08lx\n", Status); }
return Status; }
NTSTATUS FatCommonSetInformation ( IN PIRP_CONTEXT IrpContext, IN PIRP Irp )
/*++
Routine Description:
This is the common routine for setting file information called by both the fsd and fsp threads.
Arguments:
Irp - Supplies the Irp being processed
Return Value:
NTSTATUS - The return status for the operation
--*/
{ NTSTATUS Status;
PIO_STACK_LOCATION IrpSp;
PFILE_OBJECT FileObject; FILE_INFORMATION_CLASS FileInformationClass;
TYPE_OF_OPEN TypeOfOpen; PVCB Vcb; PFCB Fcb; PCCB Ccb;
BOOLEAN VcbAcquired = FALSE; BOOLEAN FcbAcquired = FALSE;
//
// Get the current stack location
//
IrpSp = IoGetCurrentIrpStackLocation( Irp );
DebugTrace(+1, Dbg, "FatCommonSetInformation...\n", 0); DebugTrace( 0, Dbg, "Irp = %08lx\n", Irp); DebugTrace( 0, Dbg, "->Length = %08lx\n", IrpSp->Parameters.SetFile.Length); DebugTrace( 0, Dbg, "->FileInformationClass = %08lx\n", IrpSp->Parameters.SetFile.FileInformationClass); DebugTrace( 0, Dbg, "->FileObject = %08lx\n", IrpSp->Parameters.SetFile.FileObject); DebugTrace( 0, Dbg, "->ReplaceIfExists = %08lx\n", IrpSp->Parameters.SetFile.ReplaceIfExists); DebugTrace( 0, Dbg, "->Buffer = %08lx\n", Irp->AssociatedIrp.SystemBuffer);
//
// Reference our input parameters to make things easier
//
FileInformationClass = IrpSp->Parameters.SetFile.FileInformationClass; FileObject = IrpSp->FileObject;
//
// Decode the file object
//
TypeOfOpen = FatDecodeFileObject( FileObject, &Vcb, &Fcb, &Ccb );
try {
//
// Case on the type of open we're dealing with
//
switch (TypeOfOpen) {
case UserVolumeOpen:
//
// We cannot query the user volume open.
//
try_return( Status = STATUS_INVALID_PARAMETER ); break;
case UserFileOpen:
if (!FlagOn( Fcb->FcbState, FCB_STATE_PAGING_FILE ) && ((FileInformationClass == FileEndOfFileInformation) || (FileInformationClass == FileAllocationInformation))) {
//
// We check whether we can proceed
// based on the state of the file oplocks.
//
Status = FsRtlCheckOplock( &Fcb->Specific.Fcb.Oplock, Irp, IrpContext, NULL, NULL );
if (Status != STATUS_SUCCESS) {
try_return( Status ); }
//
// Set the flag indicating if Fast I/O is possible
//
Fcb->Header.IsFastIoPossible = FatIsFastIoPossible( Fcb ); } break;
case UserDirectoryOpen:
break;
default:
try_return( Status = STATUS_INVALID_PARAMETER ); }
//
// We can only do a set on a nonroot dcb, so we do the test
// and then fall through to the user file open code.
//
if (NodeType(Fcb) == FAT_NTC_ROOT_DCB) {
if (FileInformationClass == FileDispositionInformation) {
try_return( Status = STATUS_CANNOT_DELETE ); }
try_return( Status = STATUS_INVALID_PARAMETER ); }
//
// In the following two cases, we cannot have creates occuring
// while we are here, so acquire the volume exclusive.
//
if ((FileInformationClass == FileDispositionInformation) || (FileInformationClass == FileRenameInformation)) {
if (!FatAcquireExclusiveVcb( IrpContext, Vcb )) {
DebugTrace(0, Dbg, "Cannot acquire Vcb\n", 0);
Status = FatFsdPostRequest( IrpContext, Irp ); Irp = NULL; IrpContext = NULL;
try_return( Status ); }
VcbAcquired = TRUE; }
//
// We need to look here to check whether the oplock state
// will allow us to continue. We may have to loop to prevent
// an oplock being granted between the time we check the oplock
// and obtain the Fcb.
//
//
// Acquire exclusive access to the Fcb, We use exclusive
// because it is probable that one of the subroutines
// that we call will need to monkey with file allocation,
// create/delete extra fcbs. So we're willing to pay the
// cost of exclusive Fcb access.
//
// Note that we do not acquire the resource for paging file
// operations in order to avoid deadlock with Mm.
//
if (!FlagOn( Fcb->FcbState, FCB_STATE_PAGING_FILE )) {
if (!FatAcquireExclusiveFcb( IrpContext, Fcb )) {
DebugTrace(0, Dbg, "Cannot acquire Fcb\n", 0);
Status = FatFsdPostRequest( IrpContext, Irp ); Irp = NULL; IrpContext = NULL;
try_return( Status ); }
FcbAcquired = TRUE; }
Status = STATUS_SUCCESS;
//
// Make sure the Fcb is in a usable condition. This
// will raise an error condition if the fcb is unusable
//
FatVerifyFcb( IrpContext, Fcb );
//
// Based on the information class we'll do different
// actions. Each of the procedures that we're calling will either
// complete the request of send the request off to the fsp
// to do the work.
//
switch (FileInformationClass) {
case FileBasicInformation:
Status = FatSetBasicInfo( IrpContext, Irp, Fcb, Ccb ); break;
case FileDispositionInformation:
//
// If this is on deferred flush media, we have to be able to wait.
//
if ( FlagOn(Vcb->VcbState, VCB_STATE_FLAG_DEFERRED_FLUSH) && !FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT) ) {
Status = FatFsdPostRequest( IrpContext, Irp ); Irp = NULL; IrpContext = NULL;
} else {
Status = FatSetDispositionInfo( IrpContext, Irp, FileObject, Fcb ); }
break;
case FileRenameInformation:
//
// We proceed with this operation only if we can wait
//
if (!FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT)) {
Status = FatFsdPostRequest( IrpContext, Irp ); Irp = NULL; IrpContext = NULL;
} else {
Status = FatSetRenameInfo( IrpContext, Irp, Vcb, Fcb, Ccb );
//
// If STATUS_PENDING is returned it means the oplock
// package has the Irp. Don't complete the request here.
//
if (Status == STATUS_PENDING) { Irp = NULL; IrpContext = NULL; } }
break;
case FilePositionInformation:
Status = FatSetPositionInfo( IrpContext, Irp, FileObject ); break;
case FileLinkInformation:
Status = STATUS_INVALID_DEVICE_REQUEST; break;
case FileAllocationInformation:
Status = FatSetAllocationInfo( IrpContext, Irp, Fcb, FileObject ); break;
case FileEndOfFileInformation:
Status = FatSetEndOfFileInfo( IrpContext, Irp, FileObject, Vcb, Fcb ); break;
default:
Status = STATUS_INVALID_PARAMETER; break; }
if ( IrpContext != NULL ) {
FatUnpinRepinnedBcbs( IrpContext ); }
try_exit: NOTHING; } finally {
DebugUnwind( FatCommonSetInformation );
if (FcbAcquired) { FatReleaseFcb( IrpContext, Fcb ); } if (VcbAcquired) { FatReleaseVcb( IrpContext, Vcb ); }
if (!AbnormalTermination()) {
FatCompleteRequest( IrpContext, Irp, Status ); }
DebugTrace(-1, Dbg, "FatCommonSetInformation -> %08lx\n", Status); }
return Status; }
//
// Internal Support Routine
//
VOID FatQueryBasicInfo ( IN PIRP_CONTEXT IrpContext, IN PFCB Fcb, IN PFILE_OBJECT FileObject, IN OUT PFILE_BASIC_INFORMATION Buffer, IN OUT PLONG Length )
/*++
Description:
This routine performs the query basic information function for fat.
Arguments:
Fcb - Supplies the Fcb being queried, it has been verified
FileObject - Supplies the flag bit that indicates the file was modified.
Buffer - Supplies a pointer to the buffer where the information is to be returned
Length - Supplies the length of the buffer in bytes, and receives the remaining bytes free in the buffer upon return.
Return Value:
None
--*/
{ DebugTrace(+1, Dbg, "FatQueryBasicInfo...\n", 0);
//
// Zero out the output buffer, and set it to indicate that
// the query is a normal file. Later we might overwrite the
// attribute.
//
RtlZeroMemory( Buffer, sizeof(FILE_BASIC_INFORMATION) );
//
// Extract the data and fill in the non zero fields of the output
// buffer
//
if (Fcb->Header.NodeTypeCode == FAT_NTC_ROOT_DCB) {
//
// We have to munge a lie on the fly. Every time we have to
// use 1/1/80 we need to convert to GMT since the TZ may have
// changed on us.
//
ExLocalTimeToSystemTime( &FatJanOne1980, &Buffer->LastWriteTime ); Buffer->CreationTime = Buffer->LastAccessTime = Buffer->LastWriteTime;
} else {
Buffer->LastWriteTime = Fcb->LastWriteTime; Buffer->CreationTime = Fcb->CreationTime; Buffer->LastAccessTime = Fcb->LastAccessTime; }
Buffer->FileAttributes = Fcb->DirentFatFlags;
//
// If the temporary flag is set, then set it in the buffer.
//
if (FlagOn( Fcb->FcbState, FCB_STATE_TEMPORARY )) {
SetFlag( Buffer->FileAttributes, FILE_ATTRIBUTE_TEMPORARY ); }
//
// If no attributes were set, set the normal bit.
//
if (Buffer->FileAttributes == 0) {
Buffer->FileAttributes = FILE_ATTRIBUTE_NORMAL; }
//
// Update the length and status output variables
//
*Length -= sizeof( FILE_BASIC_INFORMATION );
DebugTrace( 0, Dbg, "*Length = %08lx\n", *Length);
DebugTrace(-1, Dbg, "FatQueryBasicInfo -> VOID\n", 0);
return; }
//
// Internal Support Routine
//
VOID FatQueryStandardInfo ( IN PIRP_CONTEXT IrpContext, IN PFCB Fcb, IN OUT PFILE_STANDARD_INFORMATION Buffer, IN OUT PLONG Length )
/*++
Routine Description:
This routine performs the query standard information function for fat.
Arguments:
Fcb - Supplies the Fcb being queried, it has been verified
Buffer - Supplies a pointer to the buffer where the information is to be returned
Length - Supplies the length of the buffer in bytes, and receives the remaining bytes free in the buffer upon return.
Return Value:
None
--*/
{ DebugTrace(+1, Dbg, "FatQueryStandardInfo...\n", 0);
//
// Zero out the output buffer, and fill in the number of links
// and the delete pending flag.
//
RtlZeroMemory( Buffer, sizeof(FILE_STANDARD_INFORMATION) );
Buffer->NumberOfLinks = 1; Buffer->DeletePending = BooleanFlagOn( Fcb->FcbState, FCB_STATE_DELETE_ON_CLOSE );
//
// Case on whether this is a file or a directory, and extract
// the information and fill in the fcb/dcb specific parts
// of the output buffer
//
if (NodeType(Fcb) == FAT_NTC_FCB) {
if (Fcb->Header.AllocationSize.QuadPart == FCB_LOOKUP_ALLOCATIONSIZE_HINT) {
FatLookupFileAllocationSize( IrpContext, Fcb ); }
Buffer->AllocationSize = Fcb->Header.AllocationSize; Buffer->EndOfFile = Fcb->Header.FileSize;
Buffer->Directory = FALSE;
} else {
Buffer->Directory = TRUE; }
//
// Update the length and status output variables
//
*Length -= sizeof( FILE_STANDARD_INFORMATION );
DebugTrace( 0, Dbg, "*Length = %08lx\n", *Length);
DebugTrace(-1, Dbg, "FatQueryStandardInfo -> VOID\n", 0);
return; }
//
// Internal Support Routine
//
VOID FatQueryInternalInfo ( IN PIRP_CONTEXT IrpContext, IN PFCB Fcb, IN OUT PFILE_INTERNAL_INFORMATION Buffer, IN OUT PLONG Length )
/*++
Routine Description:
This routine performs the query internal information function for fat.
Arguments:
Fcb - Supplies the Fcb being queried, it has been verified
Buffer - Supplies a pointer to the buffer where the information is to be returned
Length - Supplies the length of the buffer in bytes, and receives the remaining bytes free in the buffer upon return.
Return Value:
None
--*/
{ DebugTrace(+1, Dbg, "FatQueryInternalInfo...\n", 0);
try {
Buffer->IndexNumber.QuadPart = FatGenerateFileIdFromFcb( Fcb );
//
// Update the length and status output variables
//
*Length -= sizeof( FILE_INTERNAL_INFORMATION );
} finally {
DebugUnwind( FatQueryInternalInfo );
DebugTrace( 0, Dbg, "*Length = %08lx\n", *Length);
DebugTrace(-1, Dbg, "FatQueryInternalInfo -> VOID\n", 0); }
return; }
//
// Internal Support Routine
//
VOID FatQueryEaInfo ( IN PIRP_CONTEXT IrpContext, IN PFCB Fcb, IN OUT PFILE_EA_INFORMATION Buffer, IN OUT PLONG Length )
/*++
Routine Description:
This routine performs the query Ea information function for fat.
Arguments:
Fcb - Supplies the Fcb being queried, it has been verified
Buffer - Supplies a pointer to the buffer where the information is to be returned
Length - Supplies the length of the buffer in bytes, and receives the remaining bytes free in the buffer upon return.
Return Value:
None
--*/
{ PBCB Bcb;
DebugTrace(+1, Dbg, "FatQueryEaInfo...\n", 0);
Bcb = NULL;
try {
//
// Zero out the output buffer
//
RtlZeroMemory( Buffer, sizeof(FILE_EA_INFORMATION) );
//
// The Root dcb does not have any EAs so don't look for any. Fat32
// doesn't have any, either.
//
if ( NodeType( Fcb ) != FAT_NTC_ROOT_DCB && !FatIsFat32( Fcb->Vcb )) {
PDIRENT Dirent;
//
// Try to get the dirent for this file.
//
FatGetDirentFromFcbOrDcb( IrpContext, Fcb, &Dirent, &Bcb ); if (Dirent != NULL) { //
// Get a the size needed to store the full eas for the file.
//
FatGetEaLength( IrpContext, Fcb->Vcb, Dirent, &Buffer->EaSize ); } }
//
// Update the length and status output variables
//
*Length -= sizeof( FILE_EA_INFORMATION );
} finally {
DebugUnwind( FatQueryEaInfo );
//
// Unpin the dirent if pinned.
//
FatUnpinBcb( IrpContext, Bcb );
DebugTrace( 0, Dbg, "*Length = %08lx\n", *Length);
DebugTrace(-1, Dbg, "FatQueryEaInfo -> VOID\n", 0); }
return; }
//
// Internal Support Routine
//
VOID FatQueryPositionInfo ( IN PIRP_CONTEXT IrpContext, IN PFILE_OBJECT FileObject, IN OUT PFILE_POSITION_INFORMATION Buffer, IN OUT PLONG Length )
/*++
Routine Description:
This routine performs the query position information function for fat.
Arguments:
FileObject - Supplies the File object being queried
Buffer - Supplies a pointer to the buffer where the information is to be returned
Length - Supplies the length of the buffer in bytes, and receives the remaining bytes free in the buffer upon return.
Return Value:
None
--*/
{ DebugTrace(+1, Dbg, "FatQueryPositionInfo...\n", 0);
//
// Get the current position found in the file object.
//
Buffer->CurrentByteOffset = FileObject->CurrentByteOffset;
//
// Update the length and status output variables
//
*Length -= sizeof( FILE_POSITION_INFORMATION );
DebugTrace( 0, Dbg, "*Length = %08lx\n", *Length);
DebugTrace(-1, Dbg, "FatQueryPositionInfo -> VOID\n", 0);
UNREFERENCED_PARAMETER( IrpContext );
return; }
//
// Internal Support Routine
//
VOID FatQueryNameInfo ( IN PIRP_CONTEXT IrpContext, IN PFCB Fcb, IN PCCB Ccb, IN OUT PFILE_NAME_INFORMATION Buffer, IN OUT PLONG Length )
/*++
Routine Description:
This routine performs the query name information function for fat.
Arguments:
Fcb - Supplies the Fcb being queried, it has been verified
Ccb - Supplies the Ccb for the context of the user open
Buffer - Supplies a pointer to the buffer where the information is to be returned
Length - Supplies the length of the buffer in bytes, and receives the remaining bytes free in the buffer upon return.
Return Value:
None
--*/
{ ULONG BytesToCopy; LONG TrimLength; BOOLEAN Overflow = FALSE;
DebugTrace(+1, Dbg, "FatQueryNameInfo...\n", 0);
//
// Convert the name to UNICODE
//
*Length -= FIELD_OFFSET(FILE_NAME_INFORMATION, FileName[0]);
//
// Use the full filename to build the path up. If we wanted to be
// slick in the future, we'd just build the path directly into the
// return buffer and avoid constructing the full filename, but since
// the full filename winds up being required so often lets not
// over optimize this case yet.
//
if (Fcb->FullFileName.Buffer == NULL) {
FatSetFullFileNameInFcb( IrpContext, Fcb ); }
//
// Here is where it gets a smidge tricky. FinalNameLength is the length
// of the LFN element if it exists, and since the long name is always used
// to build FullFileName, we have two cases:
//
// 1) short name: use FinalNameLength to tear off the path from FullFileName
// and append the UNICODE converted short name.
// 2) long name: just use FullFileName
//
// We bias to the name the user thinks they opened by. This winds
// up fixing some oddball tunneling cases where intermediate filters
// translate operations like delete into renames - this lets them
// do the operation in the context of the name the user was using.
//
// It also matches what NTFS does, and so we have the definition of
// correct behavior.
//
//
//
// Assume there is no long name and we are just going to use
// FullFileName.
//
TrimLength = 0;
//
// If a LongName exists and the original open was by the short name
// then set TrimLength to point to the place where the short name goes.
//
//
// Note: The Ccb can be NULL. The lazy writer calls to get the name of
// a DirectoryOpen FILE_OBJECT that it wants to display in the lost
// delayed write popup. Handle this case by just using the FileFullName.
//
if (Fcb->LongName.Unicode.Name.Unicode.Buffer != NULL) {
if ((Ccb != NULL) && FlagOn(Ccb->Flags, CCB_FLAG_OPENED_BY_SHORTNAME)) {
TrimLength = Fcb->FinalNameLength;
}
}
if (*Length < Fcb->FullFileName.Length - TrimLength) {
BytesToCopy = *Length; Overflow = TRUE;
} else {
BytesToCopy = Fcb->FullFileName.Length - TrimLength; *Length -= BytesToCopy; }
RtlCopyMemory( &Buffer->FileName[0], Fcb->FullFileName.Buffer, BytesToCopy );
//
// Note that this is just the amount of name we've copied so far. It'll
// either be all of it (long) or the path element including the \ (short).
//
Buffer->FileNameLength = Fcb->FullFileName.Length - TrimLength; //
// If we trimmed off the name element, this is the short name case. Pick
// up the UNICODE conversion and append it.
//
if (TrimLength != 0) {
UNICODE_STRING ShortName; WCHAR ShortNameBuffer[12]; NTSTATUS Status;
//
// Convert the short name to UNICODE and figure out how much
// of it can fit. Again, we always bump the returned length
// to indicate how much is available even if we can't return it.
//
ShortName.Length = 0; ShortName.MaximumLength = sizeof(ShortNameBuffer); ShortName.Buffer = ShortNameBuffer;
Status = RtlOemStringToCountedUnicodeString( &ShortName, &Fcb->ShortName.Name.Oem, FALSE );
ASSERT( Status == STATUS_SUCCESS ); if (!Overflow) { if (*Length < ShortName.Length) {
BytesToCopy = *Length; Overflow = TRUE; } else {
BytesToCopy = ShortName.Length; *Length -= BytesToCopy; }
RtlCopyMemory( (PUCHAR)&Buffer->FileName[0] + Buffer->FileNameLength, ShortName.Buffer, BytesToCopy ); }
Buffer->FileNameLength += ShortName.Length; }
if (Overflow) { *Length = -1; } //
// Return to caller
//
DebugTrace( 0, Dbg, "*Length = %08lx\n", *Length);
DebugTrace(-1, Dbg, "FatQueryNameInfo -> VOID\n", 0);
UNREFERENCED_PARAMETER( IrpContext );
return; }
//
// Internal Support Routine
//
VOID FatQueryShortNameInfo ( IN PIRP_CONTEXT IrpContext, IN PFCB Fcb, IN OUT PFILE_NAME_INFORMATION Buffer, IN OUT PLONG Length )
/*++
Routine Description:
This routine queries the short name of the file.
Arguments:
Fcb - Supplies the Fcb being queried, it has been verified
Buffer - Supplies a pointer to the buffer where the information is to be returned
Length - Supplies the length of the buffer in bytes, and receives the remaining bytes free in the buffer upon return.
Return Value:
None
--*/
{ NTSTATUS Status;
ULONG BytesToCopy; WCHAR ShortNameBuffer[12]; UNICODE_STRING ShortName;
DebugTrace(+1, Dbg, "FatQueryNameInfo...\n", 0);
//
// Convert the name to UNICODE
//
ShortName.Length = 0; ShortName.MaximumLength = sizeof(ShortNameBuffer); ShortName.Buffer = ShortNameBuffer;
*Length -= FIELD_OFFSET(FILE_NAME_INFORMATION, FileName[0]);
Status = RtlOemStringToCountedUnicodeString( &ShortName, &Fcb->ShortName.Name.Oem, FALSE );
ASSERT( Status == STATUS_SUCCESS );
//
// If we overflow, set *Length to -1 as a flag.
//
if (*Length < ShortName.Length) {
BytesToCopy = *Length; *Length = -1;
} else {
BytesToCopy = ShortName.Length; *Length -= ShortName.Length; }
RtlCopyMemory( &Buffer->FileName[0], &ShortName.Buffer[0], BytesToCopy );
Buffer->FileNameLength = ShortName.Length;
//
// Return to caller
//
DebugTrace( 0, Dbg, "*Length = %08lx\n", *Length);
DebugTrace(-1, Dbg, "FatQueryNameInfo -> VOID\n", 0);
UNREFERENCED_PARAMETER( IrpContext );
return; }
//
// Internal Support Routine
//
VOID FatQueryNetworkInfo ( IN PIRP_CONTEXT IrpContext, IN PFCB Fcb, IN PFILE_OBJECT FileObject, IN OUT PFILE_NETWORK_OPEN_INFORMATION Buffer, IN OUT PLONG Length )
/*++
Description:
This routine performs the query network open information function for fat.
Arguments:
Fcb - Supplies the Fcb being queried, it has been verified
FileObject - Supplies the flag bit that indicates the file was modified.
Buffer - Supplies a pointer to the buffer where the information is to be returned
Length - Supplies the length of the buffer in bytes, and receives the remaining bytes free in the buffer upon return.
Return Value:
None
--*/
{ DebugTrace(+1, Dbg, "FatQueryNetworkInfo...\n", 0);
//
// Zero out the output buffer, and set it to indicate that
// the query is a normal file. Later we might overwrite the
// attribute.
//
RtlZeroMemory( Buffer, sizeof(FILE_NETWORK_OPEN_INFORMATION) );
//
// Extract the data and fill in the non zero fields of the output
// buffer
//
if (Fcb->Header.NodeTypeCode == FAT_NTC_ROOT_DCB) {
//
// We have to munge a lie on the fly. Every time we have to
// use 1/1/80 we need to convert to GMT since the TZ may have
// changed on us.
//
ExLocalTimeToSystemTime( &FatJanOne1980, &Buffer->LastWriteTime ); Buffer->CreationTime = Buffer->LastAccessTime = Buffer->LastWriteTime;
} else {
Buffer->LastWriteTime.QuadPart = Fcb->LastWriteTime.QuadPart; Buffer->CreationTime.QuadPart = Fcb->CreationTime.QuadPart; Buffer->LastAccessTime.QuadPart = Fcb->LastAccessTime.QuadPart; }
Buffer->FileAttributes = Fcb->DirentFatFlags;
//
// If the temporary flag is set, then set it in the buffer.
//
if (FlagOn( Fcb->FcbState, FCB_STATE_TEMPORARY )) {
SetFlag( Buffer->FileAttributes, FILE_ATTRIBUTE_TEMPORARY ); }
//
// If no attributes were set, set the normal bit.
//
if (Buffer->FileAttributes == 0) {
Buffer->FileAttributes = FILE_ATTRIBUTE_NORMAL; } //
// Case on whether this is a file or a directory, and extract
// the information and fill in the fcb/dcb specific parts
// of the output buffer
//
if (NodeType(Fcb) == FAT_NTC_FCB) {
if (Fcb->Header.AllocationSize.QuadPart == FCB_LOOKUP_ALLOCATIONSIZE_HINT) {
FatLookupFileAllocationSize( IrpContext, Fcb ); }
Buffer->AllocationSize.QuadPart = Fcb->Header.AllocationSize.QuadPart; Buffer->EndOfFile.QuadPart = Fcb->Header.FileSize.QuadPart; }
//
// Update the length and status output variables
//
*Length -= sizeof( FILE_NETWORK_OPEN_INFORMATION );
DebugTrace( 0, Dbg, "*Length = %08lx\n", *Length);
DebugTrace(-1, Dbg, "FatQueryNetworkInfo -> VOID\n", 0);
return; }
//
// Internal Support routine
//
NTSTATUS FatSetBasicInfo ( IN PIRP_CONTEXT IrpContext, IN PIRP Irp, IN PFCB Fcb, IN PCCB Ccb )
/*++
Routine Description:
This routine performs the set basic information for fat. It either completes the request or enqueues it off to the fsp.
Arguments:
Irp - Supplies the irp being processed
Fcb - Supplies the Fcb or Dcb being processed, already known not to be the root dcb
Ccb - Supplies the flag bit that control updating the last modify time on cleanup.
Return Value:
NTSTATUS - The result of this operation if it completes without an exception.
--*/
{ NTSTATUS Status;
PFILE_BASIC_INFORMATION Buffer;
PDIRENT Dirent; PBCB DirentBcb;
FAT_TIME_STAMP CreationTime; UCHAR CreationMSec; FAT_TIME_STAMP LastWriteTime; FAT_TIME_STAMP LastAccessTime; FAT_DATE LastAccessDate; UCHAR Attributes;
BOOLEAN ModifyCreation = FALSE; BOOLEAN ModifyLastWrite = FALSE; BOOLEAN ModifyLastAccess = FALSE;
LARGE_INTEGER LargeCreationTime; LARGE_INTEGER LargeLastWriteTime; LARGE_INTEGER LargeLastAccessTime;
ULONG NotifyFilter = 0;
DebugTrace(+1, Dbg, "FatSetBasicInfo...\n", 0);
Buffer = Irp->AssociatedIrp.SystemBuffer;
//
// If the user is specifying -1 for a field, that means
// we should leave that field unchanged, even if we might
// have otherwise set it ourselves. We'll set the Ccb flag
// saying that the user set the field so that we
// don't do our default updating.
//
// We set the field to 0 then so we know not to actually
// set the field to the user-specified (and in this case,
// illegal) value.
//
if (Buffer->LastWriteTime.QuadPart == -1) {
SetFlag( Ccb->Flags, CCB_FLAG_USER_SET_LAST_WRITE ); Buffer->LastWriteTime.QuadPart = 0; }
if (Buffer->LastAccessTime.QuadPart == -1) {
SetFlag( Ccb->Flags, CCB_FLAG_USER_SET_LAST_ACCESS ); Buffer->LastAccessTime.QuadPart = 0; }
if (Buffer->CreationTime.QuadPart == -1) {
SetFlag( Ccb->Flags, CCB_FLAG_USER_SET_CREATION ); Buffer->CreationTime.QuadPart = 0; }
DirentBcb = NULL;
Status = STATUS_SUCCESS;
try {
LARGE_INTEGER FatLocalDecThirtyOne1979; LARGE_INTEGER FatLocalJanOne1980;
ExLocalTimeToSystemTime( &FatDecThirtyOne1979, &FatLocalDecThirtyOne1979 );
ExLocalTimeToSystemTime( &FatJanOne1980, &FatLocalJanOne1980 );
//
// Get a pointer to the dirent
//
ASSERT( Fcb->FcbCondition == FcbGood ); FatGetDirentFromFcbOrDcb( IrpContext, Fcb, &Dirent, &DirentBcb );
ASSERT( Dirent && DirentBcb );
//
// Check if the user specified a non-zero creation time
//
if (FatData.ChicagoMode && (Buffer->CreationTime.QuadPart != 0)) {
LargeCreationTime = Buffer->CreationTime;
//
// Convert the Nt time to a Fat time
//
if ( !FatNtTimeToFatTime( IrpContext, &LargeCreationTime, FALSE, &CreationTime, &CreationMSec )) {
//
// Special case the value 12/31/79 and treat this as 1/1/80.
// This '79 value can happen because of time zone issues.
//
if ((LargeCreationTime.QuadPart >= FatLocalDecThirtyOne1979.QuadPart) && (LargeCreationTime.QuadPart < FatLocalJanOne1980.QuadPart)) {
CreationTime = FatTimeJanOne1980; LargeCreationTime = FatLocalJanOne1980;
} else {
DebugTrace(0, Dbg, "Invalid CreationTime\n", 0); try_return( Status = STATUS_INVALID_PARAMETER ); }
//
// Don't worry about CreationMSec
//
CreationMSec = 0; }
ModifyCreation = TRUE; }
//
// Check if the user specified a non-zero last access time
//
if (FatData.ChicagoMode && (Buffer->LastAccessTime.QuadPart != 0)) {
LargeLastAccessTime = Buffer->LastAccessTime;
//
// Convert the Nt time to a Fat time
//
if ( !FatNtTimeToFatTime( IrpContext, &LargeLastAccessTime, TRUE, &LastAccessTime, NULL )) {
//
// Special case the value 12/31/79 and treat this as 1/1/80.
// This '79 value can happen because of time zone issues.
//
if ((LargeLastAccessTime.QuadPart >= FatLocalDecThirtyOne1979.QuadPart) && (LargeLastAccessTime.QuadPart < FatLocalJanOne1980.QuadPart)) {
LastAccessTime = FatTimeJanOne1980; LargeLastAccessTime = FatLocalJanOne1980;
} else {
DebugTrace(0, Dbg, "Invalid LastAccessTime\n", 0); try_return( Status = STATUS_INVALID_PARAMETER ); } }
LastAccessDate = LastAccessTime.Date; ModifyLastAccess = TRUE; }
//
// Check if the user specified a non-zero last write time
//
if (Buffer->LastWriteTime.QuadPart != 0) {
//
// First do a quick check here if the this time is the same
// time as LastAccessTime.
//
if (ModifyLastAccess && (Buffer->LastWriteTime.QuadPart == Buffer->LastAccessTime.QuadPart)) {
ModifyLastWrite = TRUE; LastWriteTime = LastAccessTime; LargeLastWriteTime = LargeLastAccessTime;
} else {
LargeLastWriteTime = Buffer->LastWriteTime;
//
// Convert the Nt time to a Fat time
//
if ( !FatNtTimeToFatTime( IrpContext, &LargeLastWriteTime, TRUE, &LastWriteTime, NULL )) {
//
// Special case the value 12/31/79 and treat this as 1/1/80.
// This '79 value can happen because of time zone issues.
//
if ((LargeLastWriteTime.QuadPart >= FatLocalDecThirtyOne1979.QuadPart) && (LargeLastWriteTime.QuadPart < FatLocalJanOne1980.QuadPart)) {
LastWriteTime = FatTimeJanOne1980; LargeLastWriteTime = FatLocalJanOne1980;
} else {
DebugTrace(0, Dbg, "Invalid LastWriteTime\n", 0); try_return( Status = STATUS_INVALID_PARAMETER ); } }
ModifyLastWrite = TRUE; } }
//
// Check if the user specified a non zero file attributes byte
//
if (Buffer->FileAttributes != 0) {
//
// Only permit the attributes that FAT understands. The rest are silently
// dropped on the floor.
//
Attributes = (UCHAR)(Buffer->FileAttributes & (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_ARCHIVE));
//
// Make sure that for a file the directory bit is not set
// and that for a directory the bit is set.
//
if (NodeType(Fcb) == FAT_NTC_FCB) {
if (FlagOn(Buffer->FileAttributes, FILE_ATTRIBUTE_DIRECTORY)) {
DebugTrace(0, Dbg, "Attempt to set dir attribute on file\n", 0); try_return( Status = STATUS_INVALID_PARAMETER ); }
} else {
Attributes |= FAT_DIRENT_ATTR_DIRECTORY; }
//
// Mark the FcbState temporary flag correctly.
//
if (FlagOn(Buffer->FileAttributes, FILE_ATTRIBUTE_TEMPORARY)) {
//
// Don't allow the temporary bit to be set on directories.
//
if (NodeType(Fcb) == FAT_NTC_DCB) {
DebugTrace(0, Dbg, "No temporary directories\n", 0); try_return( Status = STATUS_INVALID_PARAMETER ); }
SetFlag( Fcb->FcbState, FCB_STATE_TEMPORARY );
SetFlag( IoGetCurrentIrpStackLocation(Irp)->FileObject->Flags, FO_TEMPORARY_FILE );
} else {
ClearFlag( Fcb->FcbState, FCB_STATE_TEMPORARY );
ClearFlag( IoGetCurrentIrpStackLocation(Irp)->FileObject->Flags, FO_TEMPORARY_FILE ); }
//
// Set the new attributes byte, and mark the bcb dirty
//
Fcb->DirentFatFlags = Attributes;
Dirent->Attributes = Attributes;
NotifyFilter |= FILE_NOTIFY_CHANGE_ATTRIBUTES; }
if ( ModifyCreation ) {
//
// Set the new last write time in the dirent, and mark
// the bcb dirty
//
Fcb->CreationTime = LargeCreationTime; Dirent->CreationTime = CreationTime; Dirent->CreationMSec = CreationMSec;
NotifyFilter |= FILE_NOTIFY_CHANGE_CREATION; //
// Now we have to round the time in the Fcb up to the
// nearest tem msec.
//
Fcb->CreationTime.QuadPart =
((Fcb->CreationTime.QuadPart + AlmostTenMSec) / TenMSec) * TenMSec;
//
// Now because the user just set the creation time we
// better not set the creation time on close
//
SetFlag( Ccb->Flags, CCB_FLAG_USER_SET_CREATION ); }
if ( ModifyLastAccess ) {
//
// Set the new last write time in the dirent, and mark
// the bcb dirty
//
Fcb->LastAccessTime = LargeLastAccessTime; Dirent->LastAccessDate = LastAccessDate;
NotifyFilter |= FILE_NOTIFY_CHANGE_LAST_ACCESS;
//
// Now we have to truncate the time in the Fcb down to the
// current day. This has to be in LocalTime though, so first
// convert to local, trunacate, then set back to GMT.
//
ExSystemTimeToLocalTime( &Fcb->LastAccessTime, &Fcb->LastAccessTime );
Fcb->LastAccessTime.QuadPart =
(Fcb->LastAccessTime.QuadPart / FatOneDay.QuadPart) * FatOneDay.QuadPart;
ExLocalTimeToSystemTime( &Fcb->LastAccessTime, &Fcb->LastAccessTime );
//
// Now because the user just set the last access time we
// better not set the last access time on close
//
SetFlag( Ccb->Flags, CCB_FLAG_USER_SET_LAST_ACCESS ); }
if ( ModifyLastWrite ) {
//
// Set the new last write time in the dirent, and mark
// the bcb dirty
//
Fcb->LastWriteTime = LargeLastWriteTime; Dirent->LastWriteTime = LastWriteTime;
NotifyFilter |= FILE_NOTIFY_CHANGE_LAST_WRITE;
//
// Now we have to round the time in the Fcb up to the
// nearest two seconds.
//
Fcb->LastWriteTime.QuadPart =
((Fcb->LastWriteTime.QuadPart + AlmostTwoSeconds) / TwoSeconds) * TwoSeconds;
//
// Now because the user just set the last write time we
// better not set the last write time on close
//
SetFlag( Ccb->Flags, CCB_FLAG_USER_SET_LAST_WRITE ); }
//
// If we modified any of the values, we report this to the notify
// package.
//
// We also take this opportunity to set the current file size and
// first cluster in the Dirent in order to support a server hack.
//
if (NotifyFilter != 0) {
if (NodeType(Fcb) == FAT_NTC_FCB) {
Dirent->FileSize = Fcb->Header.FileSize.LowPart;
Dirent->FirstClusterOfFile = (USHORT)Fcb->FirstClusterOfFile;
if (FatIsFat32(Fcb->Vcb)) {
Dirent->FirstClusterOfFileHi = (USHORT)(Fcb->FirstClusterOfFile >> 16); } }
FatNotifyReportChange( IrpContext, Fcb->Vcb, Fcb, NotifyFilter, FILE_ACTION_MODIFIED );
FatSetDirtyBcb( IrpContext, DirentBcb, Fcb->Vcb, TRUE ); }
try_exit: NOTHING; } finally {
DebugUnwind( FatSetBasicInfo );
FatUnpinBcb( IrpContext, DirentBcb );
DebugTrace(-1, Dbg, "FatSetBasicInfo -> %08lx\n", Status); }
return Status; }
//
// Internal Support Routine
//
NTSTATUS FatSetDispositionInfo ( IN PIRP_CONTEXT IrpContext, IN PIRP Irp, IN PFILE_OBJECT FileObject, IN PFCB Fcb )
/*++
Routine Description:
This routine performs the set disposition information for fat. It either completes the request or enqueues it off to the fsp.
Arguments:
Irp - Supplies the irp being processed
FileObject - Supplies the file object being processed
Fcb - Supplies the Fcb or Dcb being processed, already known not to be the root dcb
Return Value:
NTSTATUS - The result of this operation if it completes without an exception.
--*/
{ PFILE_DISPOSITION_INFORMATION Buffer; PBCB Bcb; PDIRENT Dirent;
DebugTrace(+1, Dbg, "FatSetDispositionInfo...\n", 0);
Buffer = Irp->AssociatedIrp.SystemBuffer;
//
// Check if the user wants to delete the file or not delete
// the file
//
if (Buffer->DeleteFile) {
//
// Check if the file is marked read only
//
if (FlagOn(Fcb->DirentFatFlags, FAT_DIRENT_ATTR_READ_ONLY)) {
DebugTrace(-1, Dbg, "Cannot delete readonly file\n", 0);
return STATUS_CANNOT_DELETE; }
//
// Make sure there is no process mapping this file as an image.
//
if (!MmFlushImageSection( &Fcb->NonPaged->SectionObjectPointers, MmFlushForDelete )) {
DebugTrace(-1, Dbg, "Cannot delete user mapped image\n", 0);
return STATUS_CANNOT_DELETE; }
//
// Check if this is a dcb and if so then only allow
// the request if the directory is empty.
//
if (NodeType(Fcb) == FAT_NTC_ROOT_DCB) {
DebugTrace(-1, Dbg, "Cannot delete root Directory\n", 0);
return STATUS_CANNOT_DELETE; }
if (NodeType(Fcb) == FAT_NTC_DCB) {
DebugTrace(-1, Dbg, "User wants to delete a directory\n", 0);
//
// Check if the directory is empty
//
if ( !FatIsDirectoryEmpty(IrpContext, Fcb) ) {
DebugTrace(-1, Dbg, "Directory is not empty\n", 0);
return STATUS_DIRECTORY_NOT_EMPTY; } }
//
// If this is a floppy, touch the volume so to verify that it
// is not write protected.
//
if ( FlagOn(Fcb->Vcb->Vpb->RealDevice->Characteristics, FILE_FLOPPY_DISKETTE)) {
PVCB Vcb; PBCB Bcb = NULL; UCHAR *Buffer; UCHAR TmpChar; ULONG BytesToMap;
IO_STATUS_BLOCK Iosb;
Vcb = Fcb->Vcb;
BytesToMap = Vcb->AllocationSupport.FatIndexBitSize == 12 ? FatReservedBytes(&Vcb->Bpb) + FatBytesPerFat(&Vcb->Bpb):PAGE_SIZE;
FatReadVolumeFile( IrpContext, Vcb, 0, BytesToMap, &Bcb, (PVOID *)&Buffer );
try {
if (!CcPinMappedData( Vcb->VirtualVolumeFile, &FatLargeZero, BytesToMap, BooleanFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT), &Bcb )) {
//
// Could not pin the data without waiting (cache miss).
//
FatRaiseStatus( IrpContext, STATUS_CANT_WAIT ); }
//
// Make Mm, myself, and Cc think the byte is dirty, and then
// force a writethrough.
//
Buffer += FatReservedBytes(&Vcb->Bpb);
TmpChar = Buffer[0]; Buffer[0] = TmpChar;
FatAddMcbEntry( Vcb, &Vcb->DirtyFatMcb, FatReservedBytes( &Vcb->Bpb ), FatReservedBytes( &Vcb->Bpb ), Vcb->Bpb.BytesPerSector );
} finally {
if (AbnormalTermination() && (Bcb != NULL)) {
FatUnpinBcb( IrpContext, Bcb ); } }
CcRepinBcb( Bcb ); CcSetDirtyPinnedData( Bcb, NULL ); CcUnpinData( Bcb ); DbgDoit( ASSERT( IrpContext->PinCount )); DbgDoit( IrpContext->PinCount -= 1 ); CcUnpinRepinnedBcb( Bcb, TRUE, &Iosb );
//
// If this was not successful, raise the status.
//
if ( !NT_SUCCESS(Iosb.Status) ) {
FatNormalizeAndRaiseStatus( IrpContext, Iosb.Status ); }
} else {
//
// Just set a Bcb dirty here. The above code was only there to
// detect a write protected floppy, while the below code works
// for any write protected media and only takes a hit when the
// volume in clean.
//
FatGetDirentFromFcbOrDcb( IrpContext, Fcb, &Dirent, &Bcb );
//
// This has to work for the usual reasons (we verified the Fcb within
// volume synch).
//
ASSERT( Bcb != NULL );
try {
FatSetDirtyBcb( IrpContext, Bcb, Fcb->Vcb, TRUE );
} finally {
FatUnpinBcb( IrpContext, Bcb ); } }
//
// At this point either we have a file or an empty directory
// so we know the delete can proceed.
//
SetFlag( Fcb->FcbState, FCB_STATE_DELETE_ON_CLOSE ); FileObject->DeletePending = TRUE;
//
// If this is a directory then report this delete pending to
// the dir notify package.
//
if (NodeType(Fcb) == FAT_NTC_DCB) {
FsRtlNotifyFullChangeDirectory( Fcb->Vcb->NotifySync, &Fcb->Vcb->DirNotifyList, FileObject->FsContext, NULL, FALSE, FALSE, 0, NULL, NULL, NULL ); } } else {
//
// The user doesn't want to delete the file so clear
// the delete on close bit
//
DebugTrace(0, Dbg, "User want to not delete file\n", 0);
ClearFlag( Fcb->FcbState, FCB_STATE_DELETE_ON_CLOSE ); FileObject->DeletePending = FALSE; }
DebugTrace(-1, Dbg, "FatSetDispositionInfo -> STATUS_SUCCESS\n", 0);
return STATUS_SUCCESS; }
//
// Internal Support Routine
//
NTSTATUS FatSetRenameInfo ( IN PIRP_CONTEXT IrpContext, IN PIRP Irp, IN PVCB Vcb, IN PFCB Fcb, IN PCCB Ccb )
/*++
Routine Description:
This routine performs the set name information for fat. It either completes the request or enqueues it off to the fsp.
Arguments:
Irp - Supplies the irp being processed
Vcb - Supplies the Vcb being processed
Fcb - Supplies the Fcb or Dcb being processed, already known not to be the root dcb
Ccb - Supplies the Ccb corresponding to the handle opening the source file
Return Value:
NTSTATUS - The result of this operation if it completes without an exception.
--*/
{ BOOLEAN AllLowerComponent; BOOLEAN AllLowerExtension; BOOLEAN CaseOnlyRename; BOOLEAN ContinueWithRename; BOOLEAN CreateLfn; BOOLEAN DeleteSourceDirent; BOOLEAN DeleteTarget; BOOLEAN NewDirentFromPool; BOOLEAN RenamedAcrossDirectories; BOOLEAN ReplaceIfExists;
CCB LocalCcb; PCCB SourceCcb;
DIRENT SourceDirent;
NTSTATUS Status;
OEM_STRING OldOemName; OEM_STRING NewOemName; UCHAR OemNameBuffer[24*2];
PBCB DotDotBcb; PBCB NewDirentBcb; PBCB OldDirentBcb; PBCB SecondPageBcb; PBCB TargetDirentBcb;
PDCB TargetDcb; PDCB OldParentDcb;
PDIRENT DotDotDirent; PDIRENT FirstPageDirent; PDIRENT NewDirent; PDIRENT OldDirent; PDIRENT SecondPageDirent; PDIRENT ShortDirent; PDIRENT TargetDirent;
PFCB TempFcb;
PFILE_OBJECT TargetFileObject; PFILE_OBJECT FileObject;
PIO_STACK_LOCATION IrpSp;
PLIST_ENTRY Links;
ULONG BytesInFirstPage; ULONG DirentsInFirstPage; ULONG DirentsRequired; ULONG NewOffset; ULONG NotifyAction; ULONG SecondPageOffset; ULONG ShortDirentOffset; ULONG TargetDirentOffset; ULONG TargetLfnOffset;
UNICODE_STRING NewName; UNICODE_STRING NewUpcasedName; UNICODE_STRING OldName; UNICODE_STRING OldUpcasedName; UNICODE_STRING TargetLfn;
PWCHAR UnicodeBuffer;
UNICODE_STRING UniTunneledShortName; WCHAR UniTunneledShortNameBuffer[12]; UNICODE_STRING UniTunneledLongName; WCHAR UniTunneledLongNameBuffer[26]; LARGE_INTEGER TunneledCreationTime; ULONG TunneledDataSize; BOOLEAN HaveTunneledInformation; BOOLEAN UsingTunneledLfn = FALSE;
BOOLEAN InvalidateFcbOnRaise = FALSE;
DebugTrace(+1, Dbg, "FatSetRenameInfo...\n", 0);
//
// P H A S E 0: Initialize some variables.
//
CaseOnlyRename = FALSE; ContinueWithRename = FALSE; DeleteSourceDirent = FALSE; DeleteTarget = FALSE; NewDirentFromPool = FALSE; RenamedAcrossDirectories = FALSE;
DotDotBcb = NULL; NewDirentBcb = NULL; OldDirentBcb = NULL; SecondPageBcb = NULL; TargetDirentBcb = NULL;
NewOemName.Length = 0; NewOemName.MaximumLength = 24; NewOemName.Buffer = &OemNameBuffer[0];
OldOemName.Length = 0; OldOemName.MaximumLength = 24; OldOemName.Buffer = &OemNameBuffer[24];
UnicodeBuffer = FsRtlAllocatePoolWithTag( PagedPool, 4 * MAX_LFN_CHARACTERS * sizeof(WCHAR), TAG_FILENAME_BUFFER );
NewUpcasedName.Length = 0; NewUpcasedName.MaximumLength = MAX_LFN_CHARACTERS * sizeof(WCHAR); NewUpcasedName.Buffer = &UnicodeBuffer[0];
OldName.Length = 0; OldName.MaximumLength = MAX_LFN_CHARACTERS * sizeof(WCHAR); OldName.Buffer = &UnicodeBuffer[MAX_LFN_CHARACTERS];
OldUpcasedName.Length = 0; OldUpcasedName.MaximumLength = MAX_LFN_CHARACTERS * sizeof(WCHAR); OldUpcasedName.Buffer = &UnicodeBuffer[MAX_LFN_CHARACTERS * 2];
TargetLfn.Length = 0; TargetLfn.MaximumLength = MAX_LFN_CHARACTERS * sizeof(WCHAR); TargetLfn.Buffer = &UnicodeBuffer[MAX_LFN_CHARACTERS * 3];
UniTunneledShortName.Length = 0; UniTunneledShortName.MaximumLength = sizeof(UniTunneledShortNameBuffer); UniTunneledShortName.Buffer = &UniTunneledShortNameBuffer[0];
UniTunneledLongName.Length = 0; UniTunneledLongName.MaximumLength = sizeof(UniTunneledLongNameBuffer); UniTunneledLongName.Buffer = &UniTunneledLongNameBuffer[0];
//
// Remember the name in case we have to modify the name
// value in the ea.
//
RtlCopyMemory( OldOemName.Buffer, Fcb->ShortName.Name.Oem.Buffer, OldOemName.Length );
//
// Get the current stack location
//
IrpSp = IoGetCurrentIrpStackLocation( Irp );
//
// Extract information from the Irp to make our life easier
//
FileObject = IrpSp->FileObject; SourceCcb = FileObject->FsContext2; TargetFileObject = IrpSp->Parameters.SetFile.FileObject; ReplaceIfExists = IrpSp->Parameters.SetFile.ReplaceIfExists;
RtlZeroMemory( &LocalCcb, sizeof(CCB) );
//
// P H A S E 1:
//
// Test if rename is legal. Only small side-effects are not undone.
//
try {
//
// Can't rename the root directory
//
if ( NodeType(Fcb) == FAT_NTC_ROOT_DCB ) {
try_return( Status = STATUS_INVALID_PARAMETER ); }
//
// Check that we were not given a dcb with open handles beneath
// it. If there are only UncleanCount == 0 Fcbs beneath us, then
// remove them from the prefix table, and they will just close
// and go away naturally.
//
if (NodeType(Fcb) == FAT_NTC_DCB) {
PFCB BatchOplockFcb; ULONG BatchOplockCount;
//
// Loop until there are no batch oplocks in the subtree below
// this directory.
//
while (TRUE) {
BatchOplockFcb = NULL; BatchOplockCount = 0;
//
// First look for any UncleanCount != 0 Fcbs, and fail if we
// find any.
//
for ( TempFcb = FatGetNextFcbBottomUp(IrpContext, NULL, Fcb); TempFcb != Fcb; TempFcb = FatGetNextFcbBottomUp(IrpContext, TempFcb, Fcb) ) {
if ( TempFcb->UncleanCount != 0 ) {
//
// If there is a batch oplock on this file then
// increment our count and remember the Fcb if
// this is the first.
//
if ( (NodeType(TempFcb) == FAT_NTC_FCB) && FsRtlCurrentBatchOplock( &TempFcb->Specific.Fcb.Oplock ) ) {
BatchOplockCount += 1; if ( BatchOplockFcb == NULL ) {
BatchOplockFcb = TempFcb; }
} else {
try_return( Status = STATUS_ACCESS_DENIED ); } } }
//
// If this is not the first pass for rename and the number
// of batch oplocks has not decreased then give up.
//
if ( BatchOplockFcb != NULL ) {
if ( (Irp->IoStatus.Information != 0) && (BatchOplockCount >= Irp->IoStatus.Information) ) {
try_return( Status = STATUS_ACCESS_DENIED ); }
//
// Try to break this batch oplock.
//
Irp->IoStatus.Information = BatchOplockCount; Status = FsRtlCheckOplock( &BatchOplockFcb->Specific.Fcb.Oplock, Irp, IrpContext, FatOplockComplete, NULL );
//
// If the oplock was already broken then look for more
// batch oplocks.
//
if (Status == STATUS_SUCCESS) {
continue; }
//
// Otherwise the oplock package will post or complete the
// request.
//
try_return( Status = STATUS_PENDING ); }
break; }
//
// Now try to get as many of these file object, and thus Fcbs
// to go away as possible, flushing first, of course.
//
FatPurgeReferencedFileObjects( IrpContext, Fcb, TRUE );
//
// OK, so there are no UncleanCount != 0, Fcbs. Infact, there
// shouldn't really be any Fcbs left at all, except obstinate
// ones from user mapped sections ....oh well, he shouldn't have
// closed his handle if he wanted the file to stick around. So
// remove any Fcbs beneath us from the splay table and mark them
// DELETE_ON_CLOSE so that any future operations will fail.
//
for ( TempFcb = FatGetNextFcbBottomUp(IrpContext, NULL, Fcb); TempFcb != Fcb; TempFcb = FatGetNextFcbBottomUp(IrpContext, TempFcb, Fcb) ) {
FatRemoveNames( IrpContext, TempFcb );
SetFlag( TempFcb->FcbState, FCB_STATE_DELETE_ON_CLOSE ); } }
//
// Check if this is a simple rename or a fully-qualified rename
// In both cases we need to figure out what the TargetDcb, and
// NewName are.
//
if (TargetFileObject == NULL) {
//
// In the case of a simple rename the target dcb is the
// same as the source file's parent dcb, and the new file name
// is taken from the system buffer
//
PFILE_RENAME_INFORMATION Buffer;
Buffer = Irp->AssociatedIrp.SystemBuffer;
TargetDcb = Fcb->ParentDcb;
NewName.Length = (USHORT) Buffer->FileNameLength; NewName.Buffer = (PWSTR) &Buffer->FileName;
//
// Make sure the name is of legal length.
//
if (NewName.Length >= 255*sizeof(WCHAR)) {
try_return( Status = STATUS_OBJECT_NAME_INVALID ); }
} else {
//
// For a fully-qualified rename the target dcb is taken from
// the target file object, which must be on the same vcb as
// the source.
//
PVCB TargetVcb; PCCB TargetCcb;
if ((FatDecodeFileObject( TargetFileObject, &TargetVcb, &TargetDcb, &TargetCcb ) != UserDirectoryOpen) || (TargetVcb != Vcb)) {
try_return( Status = STATUS_INVALID_PARAMETER ); }
//
// This name is by definition legal.
//
NewName = *((PUNICODE_STRING)&TargetFileObject->FileName); }
//
// We will need an upcased version of the unicode name and the
// old name as well.
//
Status = RtlUpcaseUnicodeString( &NewUpcasedName, &NewName, FALSE );
if (!NT_SUCCESS(Status)) {
try_return( Status ); }
FatGetUnicodeNameFromFcb( IrpContext, Fcb, &OldName );
Status = RtlUpcaseUnicodeString( &OldUpcasedName, &OldName, FALSE );
if (!NT_SUCCESS(Status)) { try_return(Status); }
//
// Check if the current name and new name are equal, and the
// DCBs are equal. If they are then our work is already done.
//
if (TargetDcb == Fcb->ParentDcb) {
//
// OK, now if we found something then check if it was an exact
// match or just a case match. If it was an exact match, then
// we can bail here.
//
if (FsRtlAreNamesEqual( &NewName, &OldName, FALSE, NULL )) {
try_return( Status = STATUS_SUCCESS ); }
//
// Check now for a case only rename.
//
if (FsRtlAreNamesEqual( &NewUpcasedName, &OldUpcasedName, FALSE, NULL )) {
CaseOnlyRename = TRUE; }
} else {
RenamedAcrossDirectories = TRUE; }
//
// Upcase the name and convert it to the Oem code page.
//
// If the new UNICODE name is already more than 12 characters,
// then we know the Oem name will not be valid
//
if (NewName.Length <= 12*sizeof(WCHAR)) {
FatUnicodeToUpcaseOem( IrpContext, &NewOemName, &NewName );
//
// If the name is not valid 8.3, zero the length.
//
if (FatSpaceInName( IrpContext, &NewName ) || !FatIsNameShortOemValid( IrpContext, NewOemName, FALSE, FALSE, FALSE)) {
NewOemName.Length = 0; }
} else {
NewOemName.Length = 0; }
//
// Look in the tunnel cache for names and timestamps to restore
//
TunneledDataSize = sizeof(LARGE_INTEGER); HaveTunneledInformation = FsRtlFindInTunnelCache( &Vcb->Tunnel, FatDirectoryKey(TargetDcb), &NewName, &UniTunneledShortName, &UniTunneledLongName, &TunneledDataSize, &TunneledCreationTime ); ASSERT(TunneledDataSize == sizeof(LARGE_INTEGER));
//
// Now we need to determine how many dirents this new name will
// require.
//
if ((NewOemName.Length == 0) || (FatEvaluateNameCase( IrpContext, &NewName, &AllLowerComponent, &AllLowerExtension, &CreateLfn ), CreateLfn)) {
DirentsRequired = FAT_LFN_DIRENTS_NEEDED(&NewName) + 1;
} else {
//
// The user-given name is a short name, but we might still have
// a tunneled long name we want to use. See if we can.
//
if (UniTunneledLongName.Length && !FatLfnDirentExists(IrpContext, TargetDcb, &UniTunneledLongName, &TargetLfn)) {
UsingTunneledLfn = CreateLfn = TRUE; DirentsRequired = FAT_LFN_DIRENTS_NEEDED(&UniTunneledLongName) + 1;
} else {
//
// This really is a simple dirent. Note that the two AllLower BOOLEANs
// are correctly set now.
//
DirentsRequired = 1; } }
//
// Do some extra checks here if we are not in Chicago mode.
//
if (!FatData.ChicagoMode) {
//
// If the name was not 8.3 valid, fail the rename.
//
if (NewOemName.Length == 0) {
try_return( Status = STATUS_OBJECT_NAME_INVALID ); }
//
// Don't use the magic bits.
//
AllLowerComponent = FALSE; AllLowerExtension = FALSE; CreateLfn = FALSE; UsingTunneledLfn = FALSE; }
if (!CaseOnlyRename) {
//
// Check if the new name already exists, wait is known to be
// true.
//
if (NewOemName.Length != 0) {
FatStringTo8dot3( IrpContext, NewOemName, &LocalCcb.OemQueryTemplate.Constant );
} else {
SetFlag( LocalCcb.Flags, CCB_FLAG_SKIP_SHORT_NAME_COMPARE ); }
LocalCcb.UnicodeQueryTemplate = NewUpcasedName; LocalCcb.ContainsWildCards = FALSE;
FatLocateDirent( IrpContext, TargetDcb, &LocalCcb, 0, &TargetDirent, &TargetDirentBcb, &TargetDirentOffset, NULL, &TargetLfn);
if (TargetDirent != NULL) {
//
// The name already exists, check if the user wants
// to overwrite the name, and has access to do the overwrite
// We cannot overwrite a directory.
//
if ((!ReplaceIfExists) || (FlagOn(TargetDirent->Attributes, FAT_DIRENT_ATTR_DIRECTORY)) || (FlagOn(TargetDirent->Attributes, FAT_DIRENT_ATTR_READ_ONLY))) {
try_return( Status = STATUS_OBJECT_NAME_COLLISION ); }
//
// Check that the file has no open user handles, if it does
// then we will deny access. We do the check by searching
// down the list of fcbs opened under our parent Dcb, and making
// sure none of the maching Fcbs have a non-zero unclean count or
// outstanding image sections.
//
for (Links = TargetDcb->Specific.Dcb.ParentDcbQueue.Flink; Links != &TargetDcb->Specific.Dcb.ParentDcbQueue; ) {
TempFcb = CONTAINING_RECORD( Links, FCB, ParentDcbLinks );
//
// Advance now. The image section flush may cause the final
// close, which will recursively happen underneath of us here.
// It would be unfortunate if we looked through free memory.
//
Links = Links->Flink;
if ((TempFcb->DirentOffsetWithinDirectory == TargetDirentOffset) && ((TempFcb->UncleanCount != 0) || !MmFlushImageSection( &TempFcb->NonPaged->SectionObjectPointers, MmFlushForDelete))) {
//
// If there are batch oplocks on this file then break the
// oplocks before failing the rename.
//
Status = STATUS_ACCESS_DENIED;
if ((NodeType(TempFcb) == FAT_NTC_FCB) && FsRtlCurrentBatchOplock( &TempFcb->Specific.Fcb.Oplock )) {
//
// Do all of our cleanup now since the IrpContext
// could go away when this request is posted.
//
FatUnpinBcb( IrpContext, TargetDirentBcb );
Status = FsRtlCheckOplock( &TempFcb->Specific.Fcb.Oplock, Irp, IrpContext, FatOplockComplete, NULL );
if (Status != STATUS_PENDING) {
Status = STATUS_ACCESS_DENIED; } }
try_return( NOTHING ); } }
//
// OK, this target is toast. Remember the Lfn offset.
//
TargetLfnOffset = TargetDirentOffset - FAT_LFN_DIRENTS_NEEDED(&TargetLfn) * sizeof(DIRENT);
DeleteTarget = TRUE; } }
//
// If we will need more dirents than we have, allocate them now.
//
if ((TargetDcb != Fcb->ParentDcb) || (DirentsRequired != (Fcb->DirentOffsetWithinDirectory - Fcb->LfnOffsetWithinDirectory) / sizeof(DIRENT) + 1)) {
//
// Get some new allocation
//
NewOffset = FatCreateNewDirent( IrpContext, TargetDcb, DirentsRequired );
DeleteSourceDirent = TRUE;
} else {
NewOffset = Fcb->LfnOffsetWithinDirectory; }
ContinueWithRename = TRUE;
try_exit: NOTHING;
} finally {
if (!ContinueWithRename) {
//
// Undo everything from above.
//
ExFreePool( UnicodeBuffer ); FatUnpinBcb( IrpContext, TargetDirentBcb ); } }
//
// Now, if we are already done, return here.
//
if (!ContinueWithRename) {
return Status; }
//
// P H A S E 2: Actually perform the rename.
//
try {
//
// Report the fact that we are going to remove this entry.
// If we renamed within the same directory and the new name for the
// file did not previously exist, we report this as a rename old
// name. Otherwise this is a removed file.
//
if (!RenamedAcrossDirectories && !DeleteTarget) {
NotifyAction = FILE_ACTION_RENAMED_OLD_NAME;
} else {
NotifyAction = FILE_ACTION_REMOVED; }
FatNotifyReportChange( IrpContext, Vcb, Fcb, ((NodeType( Fcb ) == FAT_NTC_FCB) ? FILE_NOTIFY_CHANGE_FILE_NAME : FILE_NOTIFY_CHANGE_DIR_NAME ), NotifyAction );
//
// Capture a copy of the source dirent.
//
FatGetDirentFromFcbOrDcb( IrpContext, Fcb, &OldDirent, &OldDirentBcb ); SourceDirent = *OldDirent;
try {
//
// Tunnel the source Fcb - the names are disappearing regardless of
// whether the dirent allocation physically changed
//
FatTunnelFcbOrDcb( Fcb, SourceCcb );
//
// From here until very nearly the end of the operation, if we raise there
// is no reasonable way to suppose we'd be able to undo the damage. Not
// being a transactional filesystem, FAT is at the mercy of a lot of things
// (as the astute reader has no doubt realized by now).
//
InvalidateFcbOnRaise = TRUE;
//
// Delete our current dirent(s) if we got a new one.
//
if (DeleteSourceDirent) {
FatDeleteDirent( IrpContext, Fcb, NULL, FALSE ); }
//
// Delete a target conflict if we were meant to.
//
if (DeleteTarget) {
FatDeleteFile( IrpContext, TargetDcb, TargetLfnOffset, TargetDirentOffset, TargetDirent, &TargetLfn ); }
//
// We need to evaluate any short names required. If there were any
// conflicts in existing short names, they would have been deleted above.
//
// It isn't neccesary to worry about the UsingTunneledLfn case. Since we
// actually already know whether CreateLfn will be set either NewName is
// an Lfn and !UsingTunneledLfn is implied or NewName is a short name and
// we can handle that externally.
//
FatSelectNames( IrpContext, TargetDcb, &NewOemName, &NewName, &NewOemName, (HaveTunneledInformation ? &UniTunneledShortName : NULL), &AllLowerComponent, &AllLowerExtension, &CreateLfn );
if (!CreateLfn && UsingTunneledLfn) {
CreateLfn = TRUE; NewName = UniTunneledLongName;
//
// Short names are always upcase if an LFN exists
//
AllLowerComponent = FALSE; AllLowerExtension = FALSE; }
//
// OK, now setup the new dirent(s) for the new name.
//
FatPrepareWriteDirectoryFile( IrpContext, TargetDcb, NewOffset, sizeof(DIRENT), &NewDirentBcb, &NewDirent, FALSE, TRUE, &Status );
ASSERT( NT_SUCCESS( Status ) );
//
// Deal with the special case of an LFN + Dirent structure crossing
// a page boundry.
//
if ((NewOffset / PAGE_SIZE) != ((NewOffset + (DirentsRequired - 1) * sizeof(DIRENT)) / PAGE_SIZE)) {
SecondPageOffset = (NewOffset & ~(PAGE_SIZE - 1)) + PAGE_SIZE;
BytesInFirstPage = SecondPageOffset - NewOffset;
DirentsInFirstPage = BytesInFirstPage / sizeof(DIRENT);
FatPrepareWriteDirectoryFile( IrpContext, TargetDcb, SecondPageOffset, sizeof(DIRENT), &SecondPageBcb, &SecondPageDirent, FALSE, TRUE, &Status );
ASSERT( NT_SUCCESS( Status ) );
FirstPageDirent = NewDirent;
NewDirent = FsRtlAllocatePoolWithTag( PagedPool, DirentsRequired * sizeof(DIRENT), TAG_DIRENT );
NewDirentFromPool = TRUE; }
//
// Bump up Dirent and DirentOffset
//
ShortDirent = NewDirent + DirentsRequired - 1; ShortDirentOffset = NewOffset + (DirentsRequired - 1) * sizeof(DIRENT);
//
// Fill in the fields of the dirent.
//
*ShortDirent = SourceDirent;
FatConstructDirent( IrpContext, ShortDirent, &NewOemName, AllLowerComponent, AllLowerExtension, CreateLfn ? &NewName : NULL, SourceDirent.Attributes, FALSE, (HaveTunneledInformation ? &TunneledCreationTime : NULL) );
if (HaveTunneledInformation) {
//
// Need to go in and fix the timestamps in the FCB. Note that we can't use
// the TunneledCreationTime since the conversions may have failed.
//
Fcb->CreationTime = FatFatTimeToNtTime(IrpContext, ShortDirent->CreationTime, ShortDirent->CreationMSec); Fcb->LastWriteTime = FatFatTimeToNtTime(IrpContext, ShortDirent->LastWriteTime, 0); Fcb->LastAccessTime = FatFatDateToNtTime(IrpContext, ShortDirent->LastAccessDate); }
//
// If the dirent crossed pages, split the contents of the
// temporary pool between the two pages.
//
if (NewDirentFromPool) {
RtlCopyMemory( FirstPageDirent, NewDirent, BytesInFirstPage );
RtlCopyMemory( SecondPageDirent, NewDirent + DirentsInFirstPage, DirentsRequired*sizeof(DIRENT) - BytesInFirstPage );
ShortDirent = SecondPageDirent + (DirentsRequired - DirentsInFirstPage) - 1; }
} finally {
//
// Remove the entry from the splay table, and then remove the
// full file name and exact case lfn. It is important that we
// always remove the name from the prefix table regardless of
// other errors.
//
FatRemoveNames( IrpContext, Fcb );
if (Fcb->FullFileName.Buffer != NULL) {
ExFreePool( Fcb->FullFileName.Buffer ); Fcb->FullFileName.Buffer = NULL; }
if (Fcb->ExactCaseLongName.Buffer) {
ExFreePool( Fcb->ExactCaseLongName.Buffer ); Fcb->ExactCaseLongName.Buffer = NULL; } }
//
// Now we need to update the location of the file's directory
// offset and move the fcb from its current parent dcb to
// the target dcb.
//
Fcb->LfnOffsetWithinDirectory = NewOffset; Fcb->DirentOffsetWithinDirectory = ShortDirentOffset;
RemoveEntryList( &Fcb->ParentDcbLinks );
//
// There is a deep reason we put files on the tail, others on the head,
// which is to allow us to easily enumerate all child directories before
// child files. This is important to let us maintain whole-volume lockorder
// via BottomUp enumeration.
//
if (NodeType(Fcb) == FAT_NTC_FCB) {
InsertTailList( &TargetDcb->Specific.Dcb.ParentDcbQueue, &Fcb->ParentDcbLinks );
} else {
InsertHeadList( &TargetDcb->Specific.Dcb.ParentDcbQueue, &Fcb->ParentDcbLinks ); }
OldParentDcb = Fcb->ParentDcb; Fcb->ParentDcb = TargetDcb;
//
// If we renamed across directories, some cleanup is now in order.
//
if (RenamedAcrossDirectories) {
//
// See if we need to uninitialize the cachemap for the source directory.
// Do this now in case we get unlucky and raise trying to finalize the
// operation.
//
if (IsListEmpty(&OldParentDcb->Specific.Dcb.ParentDcbQueue) && (OldParentDcb->OpenCount == 0) && (OldParentDcb->Specific.Dcb.DirectoryFile != NULL)) {
PFILE_OBJECT DirectoryFileObject;
ASSERT( NodeType(OldParentDcb) == FAT_NTC_DCB );
DirectoryFileObject = OldParentDcb->Specific.Dcb.DirectoryFile;
DebugTrace(0, Dbg, "Uninitialize our parent Stream Cache Map\n", 0);
CcUninitializeCacheMap( DirectoryFileObject, NULL, NULL );
OldParentDcb->Specific.Dcb.DirectoryFile = NULL;
ObDereferenceObject( DirectoryFileObject ); }
//
// If we move a directory across directories, we have to change
// the cluster number in its .. entry
//
if (NodeType(Fcb) == FAT_NTC_DCB) {
FatPrepareWriteDirectoryFile( IrpContext, Fcb, sizeof(DIRENT), sizeof(DIRENT), &DotDotBcb, &DotDotDirent, FALSE, TRUE, &Status );
ASSERT( NT_SUCCESS( Status ) );
DotDotDirent->FirstClusterOfFile = (USHORT) ( NodeType(TargetDcb) == FAT_NTC_ROOT_DCB ? 0 : TargetDcb->FirstClusterOfFile);
if (FatIsFat32( Vcb )) {
DotDotDirent->FirstClusterOfFileHi = (USHORT) ( NodeType( TargetDcb ) == FAT_NTC_ROOT_DCB ? 0 : (TargetDcb->FirstClusterOfFile >> 16)); } } }
//
// Now we need to setup the splay table and the name within
// the fcb. Free the old short name at this point.
//
ExFreePool( Fcb->ShortName.Name.Oem.Buffer ); Fcb->ShortName.Name.Oem.Buffer = NULL;
FatConstructNamesInFcb( IrpContext, Fcb, ShortDirent, CreateLfn ? &NewName : NULL );
FatSetFullNameInFcb( IrpContext, Fcb, &NewName );
//
// The rest of the actions taken are not related to correctness of
// the in-memory structures, so we shouldn't toast the Fcb if we
// raise from here to the end.
//
InvalidateFcbOnRaise = FALSE;
//
// If a file, set the file as modified so that the archive bit
// is set. We prevent this from adjusting the write time by
// indicating the user flag in the ccb.
//
if (Fcb->Header.NodeTypeCode == FAT_NTC_FCB) {
SetFlag( FileObject->Flags, FO_FILE_MODIFIED ); SetFlag( Ccb->Flags, CCB_FLAG_USER_SET_LAST_WRITE ); }
//
// We have three cases to report.
//
// 1. If we overwrote an existing file, we report this as
// a modified file.
//
// 2. If we renamed to a new directory, then we added a file.
//
// 3. If we renamed in the same directory, then we report the
// the renamednewname.
//
if (DeleteTarget) {
FatNotifyReportChange( IrpContext, Vcb, Fcb, FILE_NOTIFY_CHANGE_ATTRIBUTES | FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_LAST_ACCESS | FILE_NOTIFY_CHANGE_CREATION | FILE_NOTIFY_CHANGE_EA, FILE_ACTION_MODIFIED );
} else if (RenamedAcrossDirectories) {
FatNotifyReportChange( IrpContext, Vcb, Fcb, ((NodeType( Fcb ) == FAT_NTC_FCB) ? FILE_NOTIFY_CHANGE_FILE_NAME : FILE_NOTIFY_CHANGE_DIR_NAME ), FILE_ACTION_ADDED );
} else {
FatNotifyReportChange( IrpContext, Vcb, Fcb, ((NodeType( Fcb ) == FAT_NTC_FCB) ? FILE_NOTIFY_CHANGE_FILE_NAME : FILE_NOTIFY_CHANGE_DIR_NAME ), FILE_ACTION_RENAMED_NEW_NAME ); }
//
// We need to update the file name in the dirent. This value
// is never used elsewhere, so we don't concern ourselves
// with any error we may encounter. We let chkdsk fix the
// disk at some later time.
//
if (!FatIsFat32(Vcb) && ShortDirent->ExtendedAttributes != 0) {
FatRenameEAs( IrpContext, Fcb, ShortDirent->ExtendedAttributes, &OldOemName ); }
//
// Set our final status
//
Status = STATUS_SUCCESS;
} finally {
DebugUnwind( FatSetRenameInfo );
ExFreePool( UnicodeBuffer );
if (UniTunneledLongName.Buffer != UniTunneledLongNameBuffer) {
//
// Free pool if the buffer was grown on tunneling lookup
//
ExFreePool(UniTunneledLongName.Buffer); }
FatUnpinBcb( IrpContext, OldDirentBcb ); FatUnpinBcb( IrpContext, TargetDirentBcb ); FatUnpinBcb( IrpContext, NewDirentBcb ); FatUnpinBcb( IrpContext, SecondPageBcb ); FatUnpinBcb( IrpContext, DotDotBcb );
//
// If this was an abnormal termination, then we are in trouble.
// Should the operation have been in a sensitive state there is
// nothing we can do but invalidate the Fcb.
//
if (AbnormalTermination() && InvalidateFcbOnRaise) {
Fcb->FcbCondition = FcbBad; }
DebugTrace(-1, Dbg, "FatSetRenameInfo -> %08lx\n", Status); }
return Status; }
//
// Internal Support Routine
//
NTSTATUS FatSetPositionInfo ( IN PIRP_CONTEXT IrpContext, IN PIRP Irp, IN PFILE_OBJECT FileObject )
/*++
Routine Description:
This routine performs the set position information for fat. It either completes the request or enqueues it off to the fsp.
Arguments:
Irp - Supplies the irp being processed
FileObject - Supplies the file object being processed
Return Value:
NTSTATUS - The result of this operation if it completes without an exception.
--*/
{ PFILE_POSITION_INFORMATION Buffer;
DebugTrace(+1, Dbg, "FatSetPositionInfo...\n", 0);
Buffer = Irp->AssociatedIrp.SystemBuffer;
//
// Check if the file does not use intermediate buffering. If it
// does not use intermediate buffering then the new position we're
// supplied must be aligned properly for the device
//
if (FlagOn( FileObject->Flags, FO_NO_INTERMEDIATE_BUFFERING )) {
PDEVICE_OBJECT DeviceObject;
DeviceObject = IoGetCurrentIrpStackLocation( Irp )->DeviceObject;
if ((Buffer->CurrentByteOffset.LowPart & DeviceObject->AlignmentRequirement) != 0) {
DebugTrace(0, Dbg, "Cannot set position due to aligment conflict\n", 0); DebugTrace(-1, Dbg, "FatSetPositionInfo -> %08lx\n", STATUS_INVALID_PARAMETER);
return STATUS_INVALID_PARAMETER; } }
//
// The input parameter is fine so set the current byte offset and
// complete the request
//
DebugTrace(0, Dbg, "Set the new position to %08lx\n", Buffer->CurrentByteOffset);
FileObject->CurrentByteOffset = Buffer->CurrentByteOffset;
DebugTrace(-1, Dbg, "FatSetPositionInfo -> %08lx\n", STATUS_SUCCESS);
UNREFERENCED_PARAMETER( IrpContext );
return STATUS_SUCCESS; }
//
// Internal Support Routine
//
NTSTATUS FatSetAllocationInfo ( IN PIRP_CONTEXT IrpContext, IN PIRP Irp, IN PFCB Fcb, IN PFILE_OBJECT FileObject )
/*++
Routine Description:
This routine performs the set Allocation information for fat. It either completes the request or enqueues it off to the fsp.
Arguments:
Irp - Supplies the irp being processed
Fcb - Supplies the Fcb or Dcb being processed, already known not to be the root dcb
FileObject - Supplies the FileObject being processed, already known not to be the root dcb
Return Value:
NTSTATUS - The result of this operation if it completes without an exception.
--*/
{ NTSTATUS Status = STATUS_SUCCESS; PFILE_ALLOCATION_INFORMATION Buffer; ULONG NewAllocationSize;
BOOLEAN FileSizeTruncated = FALSE; BOOLEAN CacheMapInitialized = FALSE; BOOLEAN ResourceAcquired = FALSE; ULONG OriginalFileSize; ULONG OriginalValidDataLength; ULONG OriginalValidDataToDisk;
Buffer = Irp->AssociatedIrp.SystemBuffer;
NewAllocationSize = Buffer->AllocationSize.LowPart;
DebugTrace(+1, Dbg, "FatSetAllocationInfo.. to %08lx\n", NewAllocationSize);
//
// Allocation is only allowed on a file and not a directory
//
if (NodeType(Fcb) == FAT_NTC_DCB) {
DebugTrace(-1, Dbg, "Cannot change allocation of a directory\n", 0);
return STATUS_INVALID_DEVICE_REQUEST; }
//
// Check that the new file allocation is legal
//
if (!FatIsIoRangeValid( Fcb->Vcb, Buffer->AllocationSize, 0 )) {
DebugTrace(-1, Dbg, "Illegal allocation size\n", 0);
return STATUS_DISK_FULL; }
//
// If we haven't yet looked up the correct AllocationSize, do so.
//
if (Fcb->Header.AllocationSize.QuadPart == FCB_LOOKUP_ALLOCATIONSIZE_HINT) {
FatLookupFileAllocationSize( IrpContext, Fcb ); }
//
// This is kinda gross, but if the file is not cached, but there is
// a data section, we have to cache the file to avoid a bunch of
// extra work.
//
if ((FileObject->SectionObjectPointer->DataSectionObject != NULL) && (FileObject->SectionObjectPointer->SharedCacheMap == NULL) && !FlagOn(Irp->Flags, IRP_PAGING_IO)) {
ASSERT( !FlagOn( FileObject->Flags, FO_CLEANUP_COMPLETE ) );
//
// Now initialize the cache map.
//
CcInitializeCacheMap( FileObject, (PCC_FILE_SIZES)&Fcb->Header.AllocationSize, FALSE, &FatData.CacheManagerCallbacks, Fcb );
CacheMapInitialized = TRUE; }
//
// Now mark the fact that the file needs to be truncated on close
//
Fcb->FcbState |= FCB_STATE_TRUNCATE_ON_CLOSE;
//
// Now mark that the time on the dirent needs to be updated on close.
//
SetFlag( FileObject->Flags, FO_FILE_MODIFIED );
try {
//
// Increase or decrease the allocation size.
//
if (NewAllocationSize > Fcb->Header.AllocationSize.LowPart) {
FatAddFileAllocation( IrpContext, Fcb, FileObject, NewAllocationSize);
} else {
//
// Check here if we will be decreasing file size and synchonize with
// paging IO.
//
if ( Fcb->Header.FileSize.LowPart > NewAllocationSize ) {
//
// Before we actually truncate, check to see if the purge
// is going to fail.
//
if (!MmCanFileBeTruncated( FileObject->SectionObjectPointer, &Buffer->AllocationSize )) {
try_return( Status = STATUS_USER_MAPPED_FILE ); }
FileSizeTruncated = TRUE;
OriginalFileSize = Fcb->Header.FileSize.LowPart; OriginalValidDataLength = Fcb->Header.ValidDataLength.LowPart; OriginalValidDataToDisk = Fcb->ValidDataToDisk;
(VOID)ExAcquireResourceExclusiveLite( Fcb->Header.PagingIoResource, TRUE ); ResourceAcquired = TRUE;
Fcb->Header.FileSize.LowPart = NewAllocationSize;
//
// If we reduced the file size to less than the ValidDataLength,
// adjust the VDL. Likewise ValidDataToDisk.
//
if (Fcb->Header.ValidDataLength.LowPart > Fcb->Header.FileSize.LowPart) {
Fcb->Header.ValidDataLength.LowPart = Fcb->Header.FileSize.LowPart; } if (Fcb->ValidDataToDisk > Fcb->Header.FileSize.LowPart) {
Fcb->ValidDataToDisk = Fcb->Header.FileSize.LowPart; }
}
//
// Now that File Size is down, actually do the truncate.
//
FatTruncateFileAllocation( IrpContext, Fcb, NewAllocationSize);
//
// Now check if we needed to decrease the file size accordingly.
//
if ( FileSizeTruncated ) {
//
// Tell the cache manager we reduced the file size.
// The call is unconditional, because MM always wants to know.
//
#if DBG
try { #endif
CcSetFileSizes( FileObject, (PCC_FILE_SIZES)&Fcb->Header.AllocationSize );
#if DBG
} except(FatBugCheckExceptionFilter( GetExceptionInformation() )) {
NOTHING; } #endif
ASSERT( FileObject->DeleteAccess || FileObject->WriteAccess );
//
// There is no going back from this. If we run into problems updating
// the dirent we will have to live with the consequences. Not sending
// the notifies is likewise pretty benign compared to failing the entire
// operation and trying to back out everything, which could fail for the
// same reasons.
//
// If you want a transacted filesystem, use NTFS ...
//
FileSizeTruncated = FALSE;
FatSetFileSizeInDirent( IrpContext, Fcb, NULL );
//
// Report that we just reduced the file size.
//
FatNotifyReportChange( IrpContext, Fcb->Vcb, Fcb, FILE_NOTIFY_CHANGE_SIZE, FILE_ACTION_MODIFIED ); } }
try_exit: NOTHING;
} finally {
if ( AbnormalTermination() && FileSizeTruncated ) {
Fcb->Header.FileSize.LowPart = OriginalFileSize; Fcb->Header.ValidDataLength.LowPart = OriginalValidDataLength; Fcb->ValidDataToDisk = OriginalValidDataToDisk;
//
// Make sure Cc knows the right filesize.
//
if (FileObject->SectionObjectPointer->SharedCacheMap != NULL) {
*CcGetFileSizePointer(FileObject) = Fcb->Header.FileSize; }
ASSERT( Fcb->Header.FileSize.LowPart <= Fcb->Header.AllocationSize.LowPart ); }
if (CacheMapInitialized) {
CcUninitializeCacheMap( FileObject, NULL, NULL ); }
if (ResourceAcquired) {
ExReleaseResourceLite( Fcb->Header.PagingIoResource );
} }
DebugTrace(-1, Dbg, "FatSetAllocationInfo -> %08lx\n", STATUS_SUCCESS);
return Status; }
//
// Internal Support Routine
//
NTSTATUS FatSetEndOfFileInfo ( IN PIRP_CONTEXT IrpContext, IN PIRP Irp, IN PFILE_OBJECT FileObject, IN PVCB Vcb, IN PFCB Fcb )
/*++
Routine Description:
This routine performs the set End of File information for fat. It either completes the request or enqueues it off to the fsp.
Arguments:
Irp - Supplies the irp being processed
FileObject - Supplies the file object being processed
Vcb - Supplies the Vcb being processed
Fcb - Supplies the Fcb or Dcb being processed, already known not to be the root dcb
Return Value:
NTSTATUS - The result of this operation if it completes without an exception.
--*/
{ NTSTATUS Status;
PFILE_END_OF_FILE_INFORMATION Buffer;
ULONG NewFileSize; ULONG InitialFileSize; ULONG InitialValidDataLength; ULONG InitialValidDataToDisk;
BOOLEAN CacheMapInitialized = FALSE; BOOLEAN UnwindFileSizes = FALSE; BOOLEAN ResourceAcquired = FALSE;
DebugTrace(+1, Dbg, "FatSetEndOfFileInfo...\n", 0);
Buffer = Irp->AssociatedIrp.SystemBuffer;
try {
//
// File Size changes are only allowed on a file and not a directory
//
if (NodeType(Fcb) != FAT_NTC_FCB) {
DebugTrace(0, Dbg, "Cannot change size of a directory\n", 0);
try_return( Status = STATUS_INVALID_DEVICE_REQUEST ); }
//
// Check that the new file size is legal
//
if (!FatIsIoRangeValid( Fcb->Vcb, Buffer->EndOfFile, 0 )) {
DebugTrace(0, Dbg, "Illegal allocation size\n", 0);
try_return( Status = STATUS_DISK_FULL ); }
NewFileSize = Buffer->EndOfFile.LowPart;
//
// If we haven't yet looked up the correct AllocationSize, do so.
//
if (Fcb->Header.AllocationSize.QuadPart == FCB_LOOKUP_ALLOCATIONSIZE_HINT) {
FatLookupFileAllocationSize( IrpContext, Fcb ); }
//
// This is kinda gross, but if the file is not cached, but there is
// a data section, we have to cache the file to avoid a bunch of
// extra work.
//
if ((FileObject->SectionObjectPointer->DataSectionObject != NULL) && (FileObject->SectionObjectPointer->SharedCacheMap == NULL) && !FlagOn(Irp->Flags, IRP_PAGING_IO)) {
if (FlagOn( FileObject->Flags, FO_CLEANUP_COMPLETE )) {
//
// This IRP has raced (and lost) with a close (=>cleanup)
// on the same fileobject. We don't want to reinitialise the
// cachemap here now because we'll leak it (unless we do so &
// then tear it down again here, which is too much of a change at
// this stage). So we'll just say the file is closed - which
// is arguably the right thing to do anyway, since a caller
// racing operations in this way is broken. The only stumbling
// block is possibly filters - do they operate on cleaned
// up fileobjects?
//
FatRaiseStatus( IrpContext, STATUS_FILE_CLOSED); }
//
// Now initialize the cache map.
//
CcInitializeCacheMap( FileObject, (PCC_FILE_SIZES)&Fcb->Header.AllocationSize, FALSE, &FatData.CacheManagerCallbacks, Fcb );
CacheMapInitialized = TRUE; }
//
// Do a special case here for the lazy write of file sizes.
//
if (IoGetCurrentIrpStackLocation(Irp)->Parameters.SetFile.AdvanceOnly) {
//
// Only attempt this if the file hasn't been "deleted on close" and
// this is a good FCB.
//
if (!IsFileDeleted( IrpContext, Fcb ) && (Fcb->FcbCondition == FcbGood)) {
PDIRENT Dirent; PBCB DirentBcb;
//
// Never have the dirent filesize larger than the fcb filesize
//
if (NewFileSize >= Fcb->Header.FileSize.LowPart) {
NewFileSize = Fcb->Header.FileSize.LowPart; }
//
// Make sure we don't set anything higher than the alloc size.
//
ASSERT( NewFileSize <= Fcb->Header.AllocationSize.LowPart );
//
// Only advance the file size, never reduce it with this call
//
FatGetDirentFromFcbOrDcb( IrpContext, Fcb, &Dirent, &DirentBcb );
ASSERT( Dirent && DirentBcb );
try {
if ( NewFileSize > Dirent->FileSize ) {
Dirent->FileSize = NewFileSize;
FatSetDirtyBcb( IrpContext, DirentBcb, Fcb->Vcb, TRUE );
//
// Report that we just changed the file size.
//
FatNotifyReportChange( IrpContext, Vcb, Fcb, FILE_NOTIFY_CHANGE_SIZE, FILE_ACTION_MODIFIED ); }
} finally {
FatUnpinBcb( IrpContext, DirentBcb ); }
} else {
DebugTrace(0, Dbg, "Cannot set size on deleted file.\n", 0); }
try_return( Status = STATUS_SUCCESS ); }
//
// Check if the new file size is greater than the current
// allocation size. If it is then we need to increase the
// allocation size.
//
if ( NewFileSize > Fcb->Header.AllocationSize.LowPart ) {
//
// Change the file allocation
//
FatAddFileAllocation( IrpContext, Fcb, FileObject, NewFileSize ); }
//
// At this point we have enough allocation for the file.
// So check if we are really changing the file size
//
if (Fcb->Header.FileSize.LowPart != NewFileSize) {
if ( NewFileSize < Fcb->Header.FileSize.LowPart ) {
//
// Before we actually truncate, check to see if the purge
// is going to fail.
//
if (!MmCanFileBeTruncated( FileObject->SectionObjectPointer, &Buffer->EndOfFile )) {
try_return( Status = STATUS_USER_MAPPED_FILE ); }
//
// This call is unconditional, because MM always wants to know.
// Also serialize here with paging io since we are truncating
// the file size.
//
ResourceAcquired = ExAcquireResourceExclusiveLite( Fcb->Header.PagingIoResource, TRUE ); }
//
// Set the new file size
//
InitialFileSize = Fcb->Header.FileSize.LowPart; InitialValidDataLength = Fcb->Header.ValidDataLength.LowPart; InitialValidDataToDisk = Fcb->ValidDataToDisk; UnwindFileSizes = TRUE;
Fcb->Header.FileSize.LowPart = NewFileSize;
//
// If we reduced the file size to less than the ValidDataLength,
// adjust the VDL. Likewise ValidDataToDisk.
//
if (Fcb->Header.ValidDataLength.LowPart > NewFileSize) {
Fcb->Header.ValidDataLength.LowPart = NewFileSize; }
if (Fcb->ValidDataToDisk > NewFileSize) {
Fcb->ValidDataToDisk = NewFileSize; }
DebugTrace(0, Dbg, "New file size is 0x%08lx.\n", NewFileSize);
//
// We must now update the cache mapping (benign if not cached).
//
CcSetFileSizes( FileObject, (PCC_FILE_SIZES)&Fcb->Header.AllocationSize );
FatSetFileSizeInDirent( IrpContext, Fcb, NULL );
//
// Report that we just changed the file size.
//
FatNotifyReportChange( IrpContext, Vcb, Fcb, FILE_NOTIFY_CHANGE_SIZE, FILE_ACTION_MODIFIED );
//
// Mark the fact that the file will need to checked for
// truncation on cleanup.
//
SetFlag( Fcb->FcbState, FCB_STATE_TRUNCATE_ON_CLOSE ); }
//
// Set this handle as having modified the file
//
FileObject->Flags |= FO_FILE_MODIFIED;
//
// Set our return status to success
//
Status = STATUS_SUCCESS;
try_exit: NOTHING;
FatUnpinRepinnedBcbs( IrpContext );
} finally {
DebugUnwind( FatSetEndOfFileInfo );
if (AbnormalTermination() && UnwindFileSizes) {
Fcb->Header.FileSize.LowPart = InitialFileSize; Fcb->Header.ValidDataLength.LowPart = InitialValidDataLength; Fcb->ValidDataToDisk = InitialValidDataToDisk;
if (FileObject->SectionObjectPointer->SharedCacheMap != NULL) {
*CcGetFileSizePointer(FileObject) = Fcb->Header.FileSize; } }
if (CacheMapInitialized) {
CcUninitializeCacheMap( FileObject, NULL, NULL ); }
if ( ResourceAcquired ) {
ExReleaseResourceLite( Fcb->Header.PagingIoResource ); }
DebugTrace(-1, Dbg, "FatSetEndOfFileInfo -> %08lx\n", Status); }
return Status; }
//
// Internal Support Routine
//
VOID FatDeleteFile ( IN PIRP_CONTEXT IrpContext, IN PDCB TargetDcb, IN ULONG LfnOffset, IN ULONG DirentOffset, IN PDIRENT Dirent, IN PUNICODE_STRING Lfn ) { PFCB Fcb; PLIST_ENTRY Links;
//
// We can do the replace by removing the other Fcb(s) from
// the prefix table.
//
for (Links = TargetDcb->Specific.Dcb.ParentDcbQueue.Flink; Links != &TargetDcb->Specific.Dcb.ParentDcbQueue; Links = Links->Flink) {
Fcb = CONTAINING_RECORD( Links, FCB, ParentDcbLinks );
if (FlagOn(Fcb->FcbState, FCB_STATE_NAMES_IN_SPLAY_TREE) && (Fcb->DirentOffsetWithinDirectory == DirentOffset)) {
ASSERT( NodeType(Fcb) == FAT_NTC_FCB ); ASSERT( Fcb->LfnOffsetWithinDirectory == LfnOffset );
if ( Fcb->UncleanCount != 0 ) {
FatBugCheck(0,0,0);
} else {
PERESOURCE Resource;
//
// Make this fcb "appear" deleted, synchronizing with
// paging IO.
//
FatRemoveNames( IrpContext, Fcb );
Resource = Fcb->Header.PagingIoResource;
(VOID)ExAcquireResourceExclusiveLite( Resource, TRUE );
SetFlag(Fcb->FcbState, FCB_STATE_DELETE_ON_CLOSE);
Fcb->ValidDataToDisk = 0; Fcb->Header.FileSize.QuadPart = Fcb->Header.ValidDataLength.QuadPart = 0;
Fcb->FirstClusterOfFile = 0;
ExReleaseResourceLite( Resource ); } } }
//
// The file is not currently opened so we can delete the file
// that is being overwritten. To do the operation we dummy
// up an fcb, truncate allocation, delete the fcb, and delete
// the dirent.
//
Fcb = FatCreateFcb( IrpContext, TargetDcb->Vcb, TargetDcb, LfnOffset, DirentOffset, Dirent, Lfn, FALSE, FALSE );
Fcb->Header.FileSize.LowPart = 0;
try {
FatTruncateFileAllocation( IrpContext, Fcb, 0 );
FatDeleteDirent( IrpContext, Fcb, NULL, TRUE );
} finally {
FatDeleteFcb( IrpContext, Fcb ); } }
//
// Internal Support Routine
//
VOID FatRenameEAs ( IN PIRP_CONTEXT IrpContext, IN PFCB Fcb, IN USHORT ExtendedAttributes, IN POEM_STRING OldOemName ) { BOOLEAN LockedEaFcb = FALSE;
PBCB EaBcb = NULL; PDIRENT EaDirent; EA_RANGE EaSetRange; PEA_SET_HEADER EaSetHeader;
PVCB Vcb;
RtlZeroMemory( &EaSetRange, sizeof( EA_RANGE ));
Vcb = Fcb->Vcb;
try {
//
// Use a try-except to catch any errors.
//
try {
//
// Try to get the Ea file object. Return FALSE on failure.
//
FatGetEaFile( IrpContext, Vcb, &EaDirent, &EaBcb, FALSE, FALSE );
LockedEaFcb = TRUE;
//
// If we didn't get the file because it doesn't exist, then the
// disk is corrupted. We do nothing here.
//
if (Vcb->VirtualEaFile != NULL) {
//
// Try to pin down the Ea set header for the index in the
// dirent. If the operation doesn't complete, return FALSE
// from this routine.
//
FatReadEaSet( IrpContext, Vcb, ExtendedAttributes, OldOemName, FALSE, &EaSetRange );
EaSetHeader = (PEA_SET_HEADER) EaSetRange.Data;
//
// We now have the Ea set header for this file. We simply
// overwrite the owning file name.
//
RtlZeroMemory( EaSetHeader->OwnerFileName, 14 );
RtlCopyMemory( EaSetHeader->OwnerFileName, Fcb->ShortName.Name.Oem.Buffer, Fcb->ShortName.Name.Oem.Length );
FatMarkEaRangeDirty( IrpContext, Vcb->VirtualEaFile, &EaSetRange ); FatUnpinEaRange( IrpContext, &EaSetRange );
CcFlushCache( Vcb->VirtualEaFile->SectionObjectPointer, NULL, 0, NULL ); }
} except(FatExceptionFilter( IrpContext, GetExceptionInformation() )) {
//
// We catch all exceptions that Fat catches, but don't do
// anything with them.
//
}
} finally {
//
// Unpin the EaDirent and the EaSetHeader if pinned.
//
FatUnpinBcb( IrpContext, EaBcb ); FatUnpinEaRange( IrpContext, &EaSetRange );
//
// Release the Fcb for the Ea file if locked.
//
if (LockedEaFcb) {
FatReleaseFcb( IrpContext, Vcb->EaFcb ); } }
return; }
|