reactos/drivers/filesystems/fastfat_new/fileinfo.c
Pierre Schweitzer aeadcaf515
[FASTFAT] Import the MS FastFAT sample from WXP.
Modified it so that it builds in trunk (with GCC, though).
Not to be switched for now, as it doesn't work in ReactOS (yet?).
2017-11-23 12:35:51 +01:00

4636 lines
124 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
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.
--*/
#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
NTAPI
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 );
_SEH2_TRY {
IrpContext = FatCreateIrpContext( Irp, CanFsdWait( Irp ) );
Status = FatCommonQueryInformation( IrpContext, Irp );
} _SEH2_EXCEPT(FatExceptionFilter( IrpContext, _SEH2_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, _SEH2_GetExceptionCode() );
} _SEH2_END;
if (TopLevel) { IoSetTopLevelIrp( NULL ); }
FsRtlExitFileSystem();
//
// And return to our caller
//
DebugTrace(-1, Dbg, "FatFsdQueryInformation -> %08lx\n", Status);
UNREFERENCED_PARAMETER( VolumeDeviceObject );
return Status;
}
NTSTATUS
NTAPI
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 );
_SEH2_TRY {
IrpContext = FatCreateIrpContext( Irp, CanFsdWait( Irp ) );
Status = FatCommonSetInformation( IrpContext, Irp );
} _SEH2_EXCEPT(FatExceptionFilter( IrpContext, _SEH2_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, _SEH2_GetExceptionCode() );
} _SEH2_END;
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;
_SEH2_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;
} _SEH2_FINALLY {
DebugUnwind( FatCommonQueryInformation );
if (FcbAcquired) { FatReleaseFcb( IrpContext, Fcb ); }
if (!_SEH2_AbnormalTermination()) {
FatCompleteRequest( IrpContext, Irp, Status );
}
DebugTrace(-1, Dbg, "FatCommonQueryInformation -> %08lx\n", Status);
} _SEH2_END;
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 );
_SEH2_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;
} _SEH2_FINALLY {
DebugUnwind( FatCommonSetInformation );
if (FcbAcquired) { FatReleaseFcb( IrpContext, Fcb ); }
if (VcbAcquired) { FatReleaseVcb( IrpContext, Vcb ); }
if (!_SEH2_AbnormalTermination()) {
FatCompleteRequest( IrpContext, Irp, Status );
}
DebugTrace(-1, Dbg, "FatCommonSetInformation -> %08lx\n", Status);
} _SEH2_END;
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);
_SEH2_TRY {
Buffer->IndexNumber.QuadPart = FatGenerateFileIdFromFcb( Fcb );
//
// Update the length and status output variables
//
*Length -= sizeof( FILE_INTERNAL_INFORMATION );
} _SEH2_FINALLY {
DebugUnwind( FatQueryInternalInfo );
DebugTrace( 0, Dbg, "*Length = %08lx\n", *Length);
DebugTrace(-1, Dbg, "FatQueryInternalInfo -> VOID\n", 0);
} _SEH2_END;
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;
_SEH2_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 );
} _SEH2_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);
} _SEH2_END;
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;
_SEH2_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;
} _SEH2_FINALLY {
DebugUnwind( FatSetBasicInfo );
FatUnpinBcb( IrpContext, DirentBcb );
DebugTrace(-1, Dbg, "FatSetBasicInfo -> %08lx\n", Status);
} _SEH2_END;
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 );
_SEH2_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 );
} _SEH2_FINALLY {
if (_SEH2_AbnormalTermination() && (Bcb != NULL)) {
FatUnpinBcb( IrpContext, Bcb );
}
} _SEH2_END;
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 );
_SEH2_TRY {
FatSetDirtyBcb( IrpContext, Bcb, Fcb->Vcb, TRUE );
} _SEH2_FINALLY {
FatUnpinBcb( IrpContext, Bcb );
} _SEH2_END;
}
//
// 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;
#ifndef __REACTOS__
NewOemName.Buffer = &OemNameBuffer[0];
#else
NewOemName.Buffer = (PCHAR)&OemNameBuffer[0];
#endif
OldOemName.Length = 0;
OldOemName.MaximumLength = 24;
#ifndef __REACTOS__
OldOemName.Buffer = &OemNameBuffer[24];
#else
OldOemName.Buffer = (PCHAR)&OemNameBuffer[24];
#endif
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.
//
_SEH2_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,
#ifndef __REACTOS__
&TargetDirentOffset,
#else
(PVBO)&TargetDirentOffset,
#endif
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;
} _SEH2_FINALLY {
if (!ContinueWithRename) {
//
// Undo everything from above.
//
ExFreePool( UnicodeBuffer );
FatUnpinBcb( IrpContext, TargetDirentBcb );
}
} _SEH2_END;
//
// Now, if we are already done, return here.
//
if (!ContinueWithRename) {
return Status;
}
//
// P H A S E 2: Actually perform the rename.
//
_SEH2_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;
_SEH2_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,
#ifndef __REACTOS__
&NewDirent,
#else
(PVOID *)&NewDirent,
#endif
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,
#ifndef __REACTOS__
&SecondPageDirent,
#else
(PVOID *)&SecondPageDirent,
#endif
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;
}
} _SEH2_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;
}
} _SEH2_END;
//
// 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,
#ifndef __REACTOS__
&DotDotDirent,
#else
(PVOID *)&DotDotDirent,
#endif
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;
} _SEH2_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 (_SEH2_AbnormalTermination() && InvalidateFcbOnRaise) {
Fcb->FcbCondition = FcbBad;
}
DebugTrace(-1, Dbg, "FatSetRenameInfo -> %08lx\n", Status);
} _SEH2_END;
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 );
_SEH2_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
_SEH2_TRY {
#endif
CcSetFileSizes( FileObject, (PCC_FILE_SIZES)&Fcb->Header.AllocationSize );
#if DBG
} _SEH2_EXCEPT(FatBugCheckExceptionFilter( _SEH2_GetExceptionInformation() )) {
NOTHING;
} _SEH2_END;
#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;
} _SEH2_FINALLY {
if ( _SEH2_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 );
}
} _SEH2_END;
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;
_SEH2_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 );
_SEH2_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 );
}
} _SEH2_FINALLY {
FatUnpinBcb( IrpContext, DirentBcb );
} _SEH2_END;
} 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 );
} _SEH2_FINALLY {
DebugUnwind( FatSetEndOfFileInfo );
if (_SEH2_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);
} _SEH2_END;
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;
_SEH2_TRY {
FatTruncateFileAllocation( IrpContext, Fcb, 0 );
FatDeleteDirent( IrpContext, Fcb, NULL, TRUE );
} _SEH2_FINALLY {
FatDeleteFcb( IrpContext, Fcb );
} _SEH2_END;
}
//
// 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;
_SEH2_TRY {
//
// Use a try-except to catch any errors.
//
_SEH2_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 );
}
} _SEH2_EXCEPT(FatExceptionFilter( IrpContext, _SEH2_GetExceptionInformation() )) {
//
// We catch all exceptions that Fat catches, but don't do
// anything with them.
//
} _SEH2_END;
} _SEH2_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 );
}
} _SEH2_END;
return;
}