Windows-driver-samples/filesys/fastfat/fileinfo.c

5157 строки
140 KiB
C
Исходник Постоянная ссылка Ответственный История

Этот файл содержит невидимые символы Юникода!

Этот файл содержит невидимые символы Юникода, которые могут быть отображены не так, как показано ниже. Если это намеренно, можете спокойно проигнорировать это предупреждение. Используйте кнопку Экранировать, чтобы показать скрытые символы.

/*++
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
);
_Requires_lock_held_(_Global_critical_region_)
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
);
_Requires_lock_held_(_Global_critical_region_)
VOID
FatQueryNameInfo (
IN PIRP_CONTEXT IrpContext,
IN PFCB Fcb,
IN PCCB Ccb,
IN BOOLEAN Normalized,
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
);
_Requires_lock_held_(_Global_critical_region_)
VOID
FatQueryNetworkInfo (
IN PIRP_CONTEXT IrpContext,
IN PFCB Fcb,
IN PFILE_OBJECT FileObject,
IN OUT PFILE_NETWORK_OPEN_INFORMATION Buffer,
IN OUT PLONG Length
);
_Requires_lock_held_(_Global_critical_region_)
NTSTATUS
FatSetBasicInfo (
IN PIRP_CONTEXT IrpContext,
IN PIRP Irp,
IN PFCB Fcb,
IN PCCB Ccb
);
_Requires_lock_held_(_Global_critical_region_)
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
);
_Requires_lock_held_(_Global_critical_region_)
NTSTATUS
FatSetAllocationInfo (
IN PIRP_CONTEXT IrpContext,
IN PIRP Irp,
IN PFCB Fcb,
IN PFILE_OBJECT FileObject
);
_Requires_lock_held_(_Global_critical_region_)
NTSTATUS
FatSetEndOfFileInfo (
IN PIRP_CONTEXT IrpContext,
IN PIRP Irp,
IN PFILE_OBJECT FileObject,
IN PVCB Vcb,
IN PFCB Fcb
);
_Requires_lock_held_(_Global_critical_region_)
NTSTATUS
FatSetValidDataLengthInfo (
IN PIRP_CONTEXT IrpContext,
IN PIRP Irp,
IN PFILE_OBJECT FileObject,
IN PFCB Fcb,
IN PCCB Ccb
);
_Requires_lock_held_(_Global_critical_region_)
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, FatSetValidDataLengthInfo)
#pragma alloc_text(PAGE, FatSetPositionInfo)
#pragma alloc_text(PAGE, FatSetRenameInfo)
#pragma alloc_text(PAGE, FatDeleteFile)
#pragma alloc_text(PAGE, FatRenameEAs)
#endif
_Function_class_(IRP_MJ_QUERY_INFORMATION)
_Function_class_(DRIVER_DISPATCH)
NTSTATUS
FatFsdQueryInformation (
_In_ PVOLUME_DEVICE_OBJECT VolumeDeviceObject,
_Inout_ 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;
PAGED_CODE();
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;
}
_Function_class_(IRP_MJ_SET_INFORMATION)
_Function_class_(DRIVER_DISPATCH)
NTSTATUS
FatFsdSetInformation (
_In_ PVOLUME_DEVICE_OBJECT VolumeDeviceObject,
_Inout_ 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;
PAGED_CODE();
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;
}
_Requires_lock_held_(_Global_critical_region_)
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 = FALSE;
BOOLEAN VcbAcquired = FALSE;
PFILE_ALL_INFORMATION AllInfo;
PAGED_CODE();
//
// Get the current stack location
//
IrpSp = IoGetCurrentIrpStackLocation( Irp );
FileObject = IrpSp->FileObject;
DebugTrace(+1, Dbg, "FatCommonQueryInformation...\n", 0);
DebugTrace( 0, Dbg, "Irp = %p\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 = %p\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 );
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:
//
// NameInfo requires synchronization with deletion in order to perform
// the full filename query. A lighter-weight way to do this would be per
// directory as the full name is built up and since the multiple Fcb
// lockorder is bottom up, this is conceivable. At this time, though,
// this change is safer.
//
if (FileInformationClass == FileNameInformation ||
FileInformationClass == FileNormalizedNameInformation ||
FileInformationClass == FileAllInformation ) {
if (!FatAcquireExclusiveVcb( IrpContext, Vcb )) {
DebugTrace(0, Dbg, "Cannot acquire Vcb\n", 0);
Status = FatFsdPostRequest( IrpContext, Irp );
IrpContext = NULL;
Irp = NULL;
try_return( Status );
}
VcbAcquired = TRUE;
}
//
// Acquire shared access to the fcb, except for a paging file
// in order to avoid deadlocks with Mm.
//
// The "removable" check was added specifically for ReadyBoost,
// which opens its cache file on a removable device as a paging file and
// relies on the file system to validate its mapping information after a
// power transition.
//
if (!FlagOn( Fcb->FcbState, FCB_STATE_PAGING_FILE ) ||
FlagOn(Fcb->Vcb->VcbState, VCB_STATE_FLAG_REMOVABLE_MEDIA)) {
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, FALSE, &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, FALSE, Buffer, &Length );
break;
case FileNormalizedNameInformation:
FatQueryNameInfo( IrpContext, Fcb, Ccb, TRUE, 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 (VcbAcquired) { FatReleaseVcb( IrpContext, Vcb ); }
if (!AbnormalTermination()) {
FatCompleteRequest( IrpContext, Irp, Status );
}
DebugTrace(-1, Dbg, "FatCommonQueryInformation -> %08lx\n", Status);
}
return Status;
}
_Requires_lock_held_(_Global_critical_region_)
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 = STATUS_SUCCESS;
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;
PAGED_CODE();
//
// Get the current stack location
//
IrpSp = IoGetCurrentIrpStackLocation( Irp );
DebugTrace(+1, Dbg, "FatCommonSetInformation...\n", 0);
DebugTrace( 0, Dbg, "Irp = %p\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 = %p\n", IrpSp->Parameters.SetFile.FileObject);
DebugTrace( 0, Dbg, "->ReplaceIfExists = %08lx\n", IrpSp->Parameters.SetFile.ReplaceIfExists);
DebugTrace( 0, Dbg, "->Buffer = %p\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) ||
(FileInformationClass == FileValidDataLengthInformation))) {
//
// We check whether we can proceed
// based on the state of the file oplocks.
//
Status = FsRtlCheckOplock( FatGetFcbOplock(Fcb),
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;
//
// Make sure we haven't been called recursively by a filter inside an existing
// create request.
//
if (FlagOn( Vcb->VcbState, VCB_STATE_FLAG_CREATE_IN_PROGRESS)) {
#pragma prefast( suppress:28159, "things are seriously wrong if we get here" )
FatBugCheck( 0, 0, 0);
}
}
//
// 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.
//
// The "removable" check was added specifically for ReadyBoost,
// which opens its cache file on a removable device as a paging file and
// relies on the file system to validate its mapping information after a
// power transition.
//
if (!FlagOn( Fcb->FcbState, FCB_STATE_PAGING_FILE ) ||
FlagOn(Fcb->Vcb->VcbState, VCB_STATE_FLAG_REMOVABLE_MEDIA)) {
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 );
//
// Now that we've acquired the file, do an oplock check if the operation
// so warrants.
//
if (FatIsFileOplockable( Fcb )&&
((FileInformationClass == FileRenameInformation) ||
((FileInformationClass == FileDispositionInformation) &&
((PFILE_DISPOSITION_INFORMATION) Irp->AssociatedIrp.SystemBuffer)->DeleteFile))) {
Status = FsRtlCheckOplock( FatGetFcbOplock(Fcb),
Irp,
IrpContext,
FatOplockComplete,
NULL );
//
// Set the flag indicating if Fast I/O is possible
//
Fcb->Header.IsFastIoPossible = FatIsFastIoPossible( Fcb );
//
// 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;
}
if (!NT_SUCCESS( Status ) ||
(Status == STATUS_PENDING)) {
try_return( Status );
}
}
//
// 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;
case FileValidDataLengthInformation:
Status = FatSetValidDataLengthInfo( IrpContext, Irp, FileObject, Fcb, Ccb );
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
--*/
{
PAGED_CODE();
UNREFERENCED_PARAMETER( FileObject );
UNREFERENCED_PARAMETER( IrpContext );
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
//
_Requires_lock_held_(_Global_critical_region_)
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
--*/
{
PAGED_CODE();
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
--*/
{
PAGED_CODE();
UNREFERENCED_PARAMETER( IrpContext );
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;
PAGED_CODE();
UNREFERENCED_PARAMETER( Fcb );
UNREFERENCED_PARAMETER( IrpContext );
DebugTrace(+1, Dbg, "FatQueryEaInfo...\n", 0);
Bcb = NULL;
try {
//
// Zero out the output buffer
//
RtlZeroMemory( Buffer, sizeof(FILE_EA_INFORMATION) );
#if 0
//
// 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 );
}
}
#endif
//
// 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);
}
}
//
// 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
--*/
{
PAGED_CODE();
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
//
_Requires_lock_held_(_Global_critical_region_)
VOID
FatQueryNameInfo (
IN PIRP_CONTEXT IrpContext,
IN PFCB Fcb,
IN PCCB Ccb,
IN BOOLEAN Normalized,
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
Normalized - if true the caller wants a normalized name (w/out short names).
This means we're servicing a FileNormalizedNameInformation query.
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;
PAGED_CODE();
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, the caller isn't asking for the normalized name,
// 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 (!Normalized &&
(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;
#pragma prefast( suppress:28931, "needed for debug build" )
Status = RtlOemStringToCountedUnicodeString( &ShortName,
&Fcb->ShortName.Name.Oem,
FALSE );
NT_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;
PAGED_CODE();
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]);
#pragma prefast( suppress:28931, "needed for debug build" )
Status = RtlOemStringToCountedUnicodeString( &ShortName,
&Fcb->ShortName.Name.Oem,
FALSE );
NT_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
//
_Requires_lock_held_(_Global_critical_region_)
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
--*/
{
PAGED_CODE();
UNREFERENCED_PARAMETER( FileObject );
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
//
_Requires_lock_held_(_Global_critical_region_)
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 = {0};
UCHAR CreationMSec = 0;
FAT_TIME_STAMP LastWriteTime = {0};
FAT_TIME_STAMP LastAccessTime = {0};
FAT_DATE LastAccessDate = {0};
UCHAR Attributes;
BOOLEAN ModifyCreation = FALSE;
BOOLEAN ModifyLastWrite = FALSE;
BOOLEAN ModifyLastAccess = FALSE;
BOOLEAN ModifiedAttributes = FALSE;
LARGE_INTEGER LargeCreationTime = {0};
LARGE_INTEGER LargeLastWriteTime = {0};
LARGE_INTEGER LargeLastAccessTime = {0};
ULONG NotifyFilter = 0;
PAGED_CODE();
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
//
NT_ASSERT( Fcb->FcbCondition == FcbGood );
FatGetDirentFromFcbOrDcb( IrpContext,
Fcb,
FALSE,
&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 );
}
//
// Now, only proceed if the requested file attributes are different
// than the existing attributes.
//
if (Dirent->Attributes != Attributes) {
//
// Set the new attributes byte, and mark the bcb dirty
//
Fcb->DirentFatFlags = Attributes;
Dirent->Attributes = Attributes;
NotifyFilter |= FILE_NOTIFY_CHANGE_ATTRIBUTES;
ModifiedAttributes = TRUE;
}
}
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 );
}
#if (NTDDI_VERSION >= NTDDI_WIN8)
//
// If last-access, last-write, or any attribute bits changed, break
// parent directory oplock.
//
if ((Fcb->ParentDcb != NULL) &&
(ModifyLastAccess ||
ModifyLastWrite ||
ModifiedAttributes)) {
FsRtlCheckOplockEx( FatGetFcbOplock(Fcb->ParentDcb),
IrpContext->OriginatingIrp,
OPLOCK_FLAG_PARENT_OBJECT,
NULL,
NULL,
NULL );
}
#endif
try_exit: NOTHING;
} finally {
DebugUnwind( FatSetBasicInfo );
FatUnpinBcb( IrpContext, DirentBcb );
DebugTrace(-1, Dbg, "FatSetBasicInfo -> %08lx\n", Status);
}
return Status;
}
//
// Internal Support Routine
//
_Requires_lock_held_(_Global_critical_region_)
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;
PAGED_CODE();
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 LocalBcb = NULL;
UCHAR *LocalBuffer;
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,
&LocalBcb,
(PVOID *)&LocalBuffer );
try {
if (!CcPinMappedData( Vcb->VirtualVolumeFile,
&FatLargeZero,
BytesToMap,
BooleanFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT),
&LocalBcb )) {
//
// 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.
//
LocalBuffer += FatReservedBytes(&Vcb->Bpb);
TmpChar = LocalBuffer[0];
LocalBuffer[0] = TmpChar;
FatAddMcbEntry( Vcb, &Vcb->DirtyFatMcb,
FatReservedBytes( &Vcb->Bpb ),
FatReservedBytes( &Vcb->Bpb ),
Vcb->Bpb.BytesPerSector );
} finally {
if (AbnormalTermination() && (LocalBcb != NULL)) {
FatUnpinBcb( IrpContext, LocalBcb );
}
}
CcRepinBcb( LocalBcb );
CcSetDirtyPinnedData( LocalBcb, NULL );
CcUnpinData( LocalBcb );
DbgDoit( NT_ASSERT( IrpContext->PinCount ));
DbgDoit( IrpContext->PinCount -= 1 );
CcUnpinRepinnedBcb( LocalBcb, 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,
FALSE,
&Dirent,
&Bcb );
//
// This has to work for the usual reasons (we verified the Fcb within
// volume synch).
//
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 = FALSE;
BOOLEAN DeleteSourceDirent;
BOOLEAN DeleteTarget;
BOOLEAN NewDirentFromPool;
BOOLEAN RenamedAcrossDirectories;
BOOLEAN ReplaceIfExists;
CCB LocalCcb;
PCCB SourceCcb;
DIRENT Dirent;
NTSTATUS Status = STATUS_SUCCESS;
OEM_STRING OldOemName;
OEM_STRING NewOemName;
UCHAR OemNameBuffer[24*2];
PBCB DotDotBcb;
PBCB NewDirentBcb;
PBCB OldDirentBcb;
PBCB SecondPageBcb;
PBCB TargetDirentBcb;
PDCB TargetDcb = NULL;
PDCB OldParentDcb;
PDIRENT DotDotDirent = NULL;
PDIRENT FirstPageDirent = NULL;
PDIRENT NewDirent = NULL;
PDIRENT OldDirent = NULL;
PDIRENT SecondPageDirent = NULL;
PDIRENT ShortDirent = NULL;
PDIRENT TargetDirent = NULL;
PFCB TempFcb;
PFILE_OBJECT TargetFileObject;
PFILE_OBJECT FileObject;
PIO_STACK_LOCATION IrpSp;
PLIST_ENTRY Links;
ULONG BytesInFirstPage = 0;
ULONG DirentsInFirstPage = 0;
ULONG DirentsRequired = 0;
ULONG NewOffset = 0;
ULONG NotifyAction = 0;
ULONG SecondPageOffset = 0;
ULONG ShortDirentOffset = 0;
ULONG TargetDirentOffset = 0;
ULONG TargetLfnOffset = 0;
// NewName comes from the IRP buffer or the TargetFileObject, so we can't
// go around modifying it. Instead we modify NewNameCopy.
UNICODE_STRING NewName;
// NB: these five UNICODE_STRINGS are allocated
// from one chopped up pool allocation called UnicodeBuffer.
UNICODE_STRING NewNameCopy;
UNICODE_STRING NewUpcasedName;
UNICODE_STRING OldName;
UNICODE_STRING OldUpcasedName;
UNICODE_STRING TargetLfn;
PWCHAR UnicodeBuffer;
UNICODE_STRING TargetOrigLfn = {0};
UNICODE_STRING UniTunneledShortName;
WCHAR UniTunneledShortNameBuffer[12];
UNICODE_STRING UniTunneledLongName;
WCHAR UniTunneledLongNameBuffer[26];
LARGE_INTEGER TunneledCreationTime;
ULONG TunneledDataSize;
BOOLEAN HaveTunneledInformation = FALSE;
BOOLEAN UsingTunneledLfn = FALSE;
BOOLEAN InvalidateFcbOnRaise = FALSE;
PFILE_OBJECT DirectoryFileObject = NULL;
ULONG Flags = 0;
PAGED_CODE();
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 = (PCHAR)&OemNameBuffer[0];
OldOemName.Length = 0;
OldOemName.MaximumLength = 24;
OldOemName.Buffer = (PCHAR)&OemNameBuffer[24];
UnicodeBuffer = FsRtlAllocatePoolWithTag( PagedPool,
5 * 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];
NewNameCopy.Length = 0;
NewNameCopy.MaximumLength = MAX_LFN_CHARACTERS * sizeof(WCHAR);
NewNameCopy.Buffer = &UnicodeBuffer[MAX_LFN_CHARACTERS * 4];
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 (FatIsFileOplockable( TempFcb ) &&
(FsRtlCurrentBatchOplock( FatGetFcbOplock(TempFcb) )
#if (NTDDI_VERSION >= NTDDI_WIN7)
||
FsRtlCurrentOplockH( FatGetFcbOplock(TempFcb) )
#endif
)) {
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( FatGetFcbOplock(BatchOplockFcb),
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, Flush );
//
// 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 .... Remove the full file name
// and exact case lfn.
//
for ( TempFcb = FatGetNextFcbBottomUp(IrpContext, NULL, Fcb);
TempFcb != Fcb;
TempFcb = FatGetNextFcbBottomUp(IrpContext, TempFcb, Fcb) ) {
FatAcquireExclusiveFcb( IrpContext, TempFcb );
if (TempFcb->FullFileName.Buffer != NULL) {
ExFreePool( TempFcb->FullFileName.Buffer );
TempFcb->FullFileName.Buffer = NULL;
}
FatReleaseFcb( IrpContext, TempFcb );
}
}
//
// 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 );
}
RtlCopyUnicodeString(&NewNameCopy,&NewName);
} 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);
RtlCopyUnicodeString(&NewNameCopy,&NewName);
}
//
// 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 );
NT_ASSERT(TunneledDataSize == sizeof(LARGE_INTEGER));
//
// Now we need to determine how many dirents this new name will
// require.
//
if ((NewOemName.Length == 0) ||
(FatEvaluateNameCase( IrpContext,
&NewNameCopy,
&AllLowerComponent,
&AllLowerExtension,
&CreateLfn ),
CreateLfn)) {
DirentsRequired = FAT_LFN_DIRENTS_NEEDED(&NewNameCopy) + 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;
Flags = 0;
FatLocateDirent( IrpContext,
TargetDcb,
&LocalCcb,
0,
&Flags,
&TargetDirent,
&TargetDirentBcb,
(PVBO)&TargetDirentOffset,
NULL,
&TargetLfn,
&TargetOrigLfn );
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 (FatIsFileOplockable( TempFcb ) &&
(FsRtlCurrentBatchOplock( FatGetFcbOplock(TempFcb) )
#if (NTDDI_VERSION >= NTDDI_WIN7)
||
FsRtlCurrentOplockH( FatGetFcbOplock(TempFcb) )
#endif
)) {
//
// Do all of our cleanup now since the IrpContext
// could go away when this request is posted.
//
FatUnpinBcb( IrpContext, TargetDirentBcb );
Status = FsRtlCheckOplock( FatGetFcbOplock(TempFcb),
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(&TargetOrigLfn) *
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,
FALSE );
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 );
try {
//
// Capture a copy of the source dirent.
//
FatGetDirentFromFcbOrDcb( IrpContext, Fcb, FALSE, &OldDirent, &OldDirentBcb );
Dirent = *OldDirent;
//
// 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,
&NewNameCopy,
&NewOemName,
(HaveTunneledInformation ? &UniTunneledShortName : NULL),
&AllLowerComponent,
&AllLowerExtension,
&CreateLfn );
if (!CreateLfn && UsingTunneledLfn) {
CreateLfn = TRUE;
RtlCopyUnicodeString( &NewNameCopy, &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 );
NT_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 );
NT_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 = Dirent;
FatConstructDirent( IrpContext,
ShortDirent,
&NewOemName,
AllLowerComponent,
AllLowerExtension,
CreateLfn ? &NewNameCopy : NULL,
Dirent.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;
}
Dirent = *ShortDirent;
} 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;
}
FatUnpinBcb( IrpContext, OldDirentBcb );
FatUnpinBcb( IrpContext, TargetDirentBcb );
FatUnpinBcb( IrpContext, NewDirentBcb );
FatUnpinBcb( IrpContext, SecondPageBcb );
}
//
// 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 (NTDDI_VERSION >= NTDDI_WIN8)
//
// Break parent directory oplock on the old parent. Directory oplock
// breaks are always advisory, so we will never block/get STATUS_PENDING
// here.
//
FsRtlCheckOplockEx( FatGetFcbOplock(OldParentDcb),
IrpContext->OriginatingIrp,
OPLOCK_FLAG_PARENT_OBJECT,
NULL,
NULL,
NULL );
#endif
//
// If we renamed across directories, some cleanup is now in order.
//
if (RenamedAcrossDirectories) {
#if (NTDDI_VERSION >= NTDDI_WIN8)
//
// Break parent directory oplock on the new parent. Directory oplock
// breaks are always advisory, so we will never block/get STATUS_PENDING
// here.
//
FsRtlCheckOplockEx( FatGetFcbOplock(TargetDcb),
IrpContext->OriginatingIrp,
OPLOCK_FLAG_PARENT_OBJECT,
NULL,
NULL,
NULL );
#endif
//
// 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)) {
NT_ASSERT( NodeType(OldParentDcb) == FAT_NTC_DCB );
DirectoryFileObject = OldParentDcb->Specific.Dcb.DirectoryFile;
OldParentDcb->Specific.Dcb.DirectoryFile = NULL;
}
//
// 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 );
NT_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,
&Dirent,
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) &&
Dirent.ExtendedAttributes != 0) {
FatRenameEAs( IrpContext,
Fcb,
Dirent.ExtendedAttributes,
&OldOemName );
}
FatUnpinBcb( IrpContext, DotDotBcb );
FatUnpinRepinnedBcbs( IrpContext );
//
// 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);
}
if (NewDirentFromPool) {
ExFreePool( NewDirent );
}
FatUnpinBcb( IrpContext, TargetDirentBcb );
FatUnpinBcb( IrpContext, DotDotBcb );
//
// Uninitialize the cachemap for the source directory if we need to.
//
if (DirectoryFileObject) {
DebugTrace(0, Dbg, "Uninitialize our parent Stream Cache Map\n", 0);
CcUninitializeCacheMap( DirectoryFileObject, NULL, NULL );
ObDereferenceObject( DirectoryFileObject );
}
//
// 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;
PAGED_CODE();
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
//
_Requires_lock_held_(_Global_critical_region_)
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 = 0;
ULONG HeaderSize = 0;
BOOLEAN FileSizeTruncated = FALSE;
BOOLEAN CacheMapInitialized = FALSE;
BOOLEAN ResourceAcquired = FALSE;
ULONG OriginalFileSize = 0;
ULONG OriginalValidDataLength = 0;
ULONG OriginalValidDataToDisk = 0;
PAGED_CODE();
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)) {
NT_ASSERT( !FlagOn( FileObject->Flags, FO_CLEANUP_COMPLETE ) );
//
// Now initialize the cache map.
//
FatInitializeCacheMap( 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+HeaderSize > Fcb->Header.AllocationSize.LowPart) {
FatAddFileAllocation( IrpContext, Fcb, FileObject, NewAllocationSize+HeaderSize);
} else {
//
// Check here if we will be decreasing file size and synchonize with
// paging IO.
//
if ( Fcb->Header.FileSize.LowPart > NewAllocationSize+HeaderSize ) {
//
// The way Sections for DataScan are created and used, an AV's
// memory-mapping can come into being after we check it's safe
// to truncate a file while continuing to hold the file here.
// This leads to data corruption because Purge eventually fails
// (during call to Cc to set file sizes) and stale data continues
// to live in the cache/memory.
//
if (Fcb->PurgeFailureModeEnableCount != 0) {
try_return( Status = STATUS_PURGE_FAILED );
}
//
// 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+HeaderSize );
//
// 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
NT_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;
}
NT_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
//
_Requires_lock_held_(_Global_critical_region_)
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 = STATUS_SUCCESS;
PFILE_END_OF_FILE_INFORMATION Buffer;
ULONG NewFileSize = 0;
ULONG InitialFileSize = 0;
ULONG InitialValidDataLength = 0;
ULONG InitialValidDataToDisk = 0;
BOOLEAN CacheMapInitialized = FALSE;
BOOLEAN UnwindFileSizes = FALSE;
BOOLEAN ResourceAcquired = FALSE;
PAGED_CODE();
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) &&
(Fcb->FcbCondition == FcbGood)) {
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.
//
FatInitializeCacheMap( 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 = NULL;
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.
//
NT_ASSERT( NewFileSize <= Fcb->Header.AllocationSize.LowPart );
//
// Only advance the file size, never reduce it with this call
//
FatGetDirentFromFcbOrDcb( IrpContext,
Fcb,
FALSE,
&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 ) {
//
// The way Sections for DataScan are created and used, an AV's
// memory-mapping can come into being after we check it's safe
// to truncate a file while continuing to hold the file here.
// This leads to data corruption because Purge eventually fails
// (during call to Cc to set file sizes) and stale data continues
// to live in the cache/memory.
//
if (Fcb->PurgeFailureModeEnableCount != 0) {
try_return( Status = STATUS_PURGE_FAILED );
}
//
// 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;
}
//
// WinSE bug #307418 "Occasional data corruption when
// standby/resume while copying files to removable FAT
// formatted media".
// On system suspend FatUnpinRepinnedBcbs() can fail
// because the underlying drive is already marked with DO_VERIFY
// flag. FatUnpinRepinnedBcbs() will raise in this case and
// the file size changes will be un-rolled in FCB but the change
// to Dirent file still can make it to the disk since its BCB
// will not be purged by FatUnpinRepinnedBcbs(). In this case
// we'll also try to un-roll the change to Dirent to keep
// in-memory and on-disk metadata in sync.
//
FatSetFileSizeInDirentNoRaise( IrpContext, Fcb, NULL );
}
if (CacheMapInitialized) {
CcUninitializeCacheMap( FileObject, NULL, NULL );
}
if ( ResourceAcquired ) {
ExReleaseResourceLite( Fcb->Header.PagingIoResource );
}
DebugTrace(-1, Dbg, "FatSetEndOfFileInfo -> %08lx\n", Status);
}
return Status;
}
//
// Internal Support Routine
//
_Requires_lock_held_(_Global_critical_region_)
NTSTATUS
FatSetValidDataLengthInfo (
IN PIRP_CONTEXT IrpContext,
IN PIRP Irp,
IN PFILE_OBJECT FileObject,
IN PFCB Fcb,
IN PCCB Ccb
)
/*++
Routine Description:
This routine performs the set valid data length 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
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.
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
PFILE_VALID_DATA_LENGTH_INFORMATION Buffer;
ULONG NewValidDataLength;
BOOLEAN ResourceAcquired = FALSE;
PAGED_CODE();
UNREFERENCED_PARAMETER( IrpContext );
DebugTrace(+1, Dbg, "FatSetValidDataLengthInfo...\n", 0);
Buffer = Irp->AssociatedIrp.SystemBuffer;
try {
//
// User must have manage volume privilege to explicitly tweak the VDL
//
if ((Ccb == NULL) || !FlagOn( Ccb->Flags, CCB_FLAG_MANAGE_VOLUME_ACCESS )) {
try_return( Status = STATUS_INVALID_PARAMETER );
}
//
// Valid data length changes are only allowed on a file and not a directory
//
if (NodeType(Fcb) != FAT_NTC_FCB) {
DebugTrace(0, Dbg, "Cannot change VDL 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->ValidDataLength, 0)) {
DebugTrace(0, Dbg, "Illegal allocation size\n", 0);
try_return( Status = STATUS_DISK_FULL );
}
NewValidDataLength = Buffer->ValidDataLength.LowPart;
//
// VDL can only move forward
//
if ((NewValidDataLength < Fcb->Header.ValidDataLength.LowPart) ||
(NewValidDataLength > Fcb->Header.FileSize.LowPart)) {
try_return( Status = STATUS_INVALID_PARAMETER );
}
//
// We can't change the VDL without being able to purge. This should stay
// constant since we own everything exclusive
//
if (!MmCanFileBeTruncated( FileObject->SectionObjectPointer,
&Buffer->ValidDataLength )) {
try_return( Status = STATUS_USER_MAPPED_FILE );
}
//
// Flush old data out and purge the cache so we can see new data.
//
if (FileObject->SectionObjectPointer->DataSectionObject != NULL) {
ResourceAcquired =
ExAcquireResourceExclusiveLite( Fcb->Header.PagingIoResource, TRUE );
CcFlushCache( FileObject->SectionObjectPointer,
NULL,
0,
&Irp->IoStatus );
if (!NT_SUCCESS( Irp->IoStatus.Status )) {
try_return( Irp->IoStatus.Status );
}
CcPurgeCacheSection( FileObject->SectionObjectPointer,
NULL,
0,
FALSE );
}
//
// Set the new ValidDataLength, Likewise ValidDataToDisk.
//
Fcb->Header.ValidDataLength.LowPart = NewValidDataLength;
Fcb->ValidDataToDisk = NewValidDataLength;
DebugTrace(0, Dbg, "New VDL is 0x%08lx.\n", NewValidDataLength);
//
// We must now update the cache mapping.
//
if (FileObject->SectionObjectPointer->SharedCacheMap != NULL) {
CcSetFileSizes( FileObject,
(PCC_FILE_SIZES)&Fcb->Header.AllocationSize );
}
//
// 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;
} finally {
DebugUnwind( FatSetValidDataLengthInfo );
if (ResourceAcquired) {
ExReleaseResourceLite( Fcb->Header.PagingIoResource );
}
DebugTrace(-1, Dbg, "FatSetValidDataLengthInfo -> %08lx\n", Status);
}
return Status;
}
//
// Internal Support Routine
//
_Requires_lock_held_(_Global_critical_region_)
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;
PAGED_CODE();
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;
}
_Requires_lock_held_(_Global_critical_region_)
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;
PAGED_CODE();
//
// 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)) {
NT_ASSERT( NodeType(Fcb) == FAT_NTC_FCB );
NT_ASSERT( Fcb->LfnOffsetWithinDirectory == LfnOffset );
if ( Fcb->UncleanCount != 0 ) {
#pragma prefast( suppress:28159, "things are seriously wrong if we get here" )
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,
NULL,
FALSE,
FALSE );
Fcb->Header.FileSize.LowPart = 0;
try {
FatTruncateFileAllocation( IrpContext, Fcb, 0 );
FatDeleteDirent( IrpContext, Fcb, NULL, TRUE );
} finally {
FatDeleteFcb( IrpContext, &Fcb );
}
}