mirror of
https://github.com/reactos/reactos.git
synced 2024-08-26 05:08:39 +00:00
5187 lines
141 KiB
C
5187 lines
141 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
|
||
NTAPI
|
||
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 );
|
||
|
||
_SEH2_TRY {
|
||
|
||
IrpContext = FatCreateIrpContext( Irp, CanFsdWait( Irp ) );
|
||
|
||
Status = FatCommonQueryInformation( IrpContext, Irp );
|
||
|
||
} _SEH2_EXCEPT(FatExceptionFilter( IrpContext, _SEH2_GetExceptionInformation() )) {
|
||
|
||
//
|
||
// We had some trouble trying to perform the requested
|
||
// operation, so we'll abort the I/O request with
|
||
// the error status that we get back from the
|
||
// execption code
|
||
//
|
||
|
||
Status = FatProcessException( IrpContext, Irp, _SEH2_GetExceptionCode() );
|
||
} _SEH2_END;
|
||
|
||
if (TopLevel) { IoSetTopLevelIrp( NULL ); }
|
||
|
||
FsRtlExitFileSystem();
|
||
|
||
//
|
||
// And return to our caller
|
||
//
|
||
|
||
DebugTrace(-1, Dbg, "FatFsdQueryInformation -> %08lx\n", Status);
|
||
|
||
UNREFERENCED_PARAMETER( VolumeDeviceObject );
|
||
|
||
return Status;
|
||
}
|
||
|
||
|
||
_Function_class_(IRP_MJ_SET_INFORMATION)
|
||
_Function_class_(DRIVER_DISPATCH)
|
||
NTSTATUS
|
||
NTAPI
|
||
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 );
|
||
|
||
_SEH2_TRY {
|
||
|
||
IrpContext = FatCreateIrpContext( Irp, CanFsdWait( Irp ) );
|
||
|
||
Status = FatCommonSetInformation( IrpContext, Irp );
|
||
|
||
} _SEH2_EXCEPT(FatExceptionFilter( IrpContext, _SEH2_GetExceptionInformation() )) {
|
||
|
||
//
|
||
// We had some trouble trying to perform the requested
|
||
// operation, so we'll abort the I/O request with
|
||
// the error status that we get back from the
|
||
// execption code
|
||
//
|
||
|
||
Status = FatProcessException( IrpContext, Irp, _SEH2_GetExceptionCode() );
|
||
} _SEH2_END;
|
||
|
||
if (TopLevel) { IoSetTopLevelIrp( NULL ); }
|
||
|
||
FsRtlExitFileSystem();
|
||
|
||
//
|
||
// And return to our caller
|
||
//
|
||
|
||
DebugTrace(-1, Dbg, "FatFsdSetInformation -> %08lx\n", Status);
|
||
|
||
UNREFERENCED_PARAMETER( VolumeDeviceObject );
|
||
|
||
return Status;
|
||
}
|
||
|
||
|
||
_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;
|
||
|
||
_SEH2_TRY {
|
||
|
||
//
|
||
// Case on the type of open we're dealing with
|
||
//
|
||
|
||
switch (TypeOfOpen) {
|
||
|
||
case UserVolumeOpen:
|
||
|
||
//
|
||
// We cannot query the user volume open.
|
||
//
|
||
|
||
Status = STATUS_INVALID_PARAMETER;
|
||
break;
|
||
|
||
case UserFileOpen:
|
||
case UserDirectoryOpen:
|
||
case DirectoryFile:
|
||
|
||
|
||
//
|
||
// 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 ||
|
||
#if (NTDDI_VERSION >= NTDDI_VISTA)
|
||
FileInformationClass == FileNormalizedNameInformation ||
|
||
#endif
|
||
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;
|
||
|
||
#if (NTDDI_VERSION >= NTDDI_VISTA)
|
||
case FileNormalizedNameInformation:
|
||
|
||
FatQueryNameInfo( IrpContext, Fcb, Ccb, TRUE, Buffer, &Length );
|
||
break;
|
||
#endif
|
||
|
||
case FileAlternateNameInformation:
|
||
|
||
FatQueryShortNameInfo( IrpContext, Fcb, Buffer, &Length );
|
||
break;
|
||
|
||
case FileNetworkOpenInformation:
|
||
|
||
FatQueryNetworkInfo( IrpContext, Fcb, FileObject, Buffer, &Length );
|
||
break;
|
||
|
||
default:
|
||
|
||
Status = STATUS_INVALID_PARAMETER;
|
||
break;
|
||
}
|
||
|
||
break;
|
||
|
||
default:
|
||
|
||
KdPrintEx((DPFLTR_FASTFAT_ID,
|
||
DPFLTR_INFO_LEVEL,
|
||
"FATQueryFile, Illegal TypeOfOpen = %08lx\n",
|
||
TypeOfOpen));
|
||
|
||
Status = STATUS_INVALID_PARAMETER;
|
||
break;
|
||
}
|
||
|
||
//
|
||
// If we overflowed the buffer, set the length to 0 and change the
|
||
// status to STATUS_BUFFER_OVERFLOW.
|
||
//
|
||
|
||
if ( Length < 0 ) {
|
||
|
||
Status = STATUS_BUFFER_OVERFLOW;
|
||
|
||
Length = 0;
|
||
}
|
||
|
||
//
|
||
// Set the information field to the number of bytes actually filled in
|
||
// and then complete the request
|
||
//
|
||
|
||
Irp->IoStatus.Information = IrpSp->Parameters.QueryFile.Length - Length;
|
||
|
||
try_exit: NOTHING;
|
||
} _SEH2_FINALLY {
|
||
|
||
DebugUnwind( FatCommonQueryInformation );
|
||
|
||
if (FcbAcquired) { FatReleaseFcb( IrpContext, Fcb ); }
|
||
if (VcbAcquired) { FatReleaseVcb( IrpContext, Vcb ); }
|
||
|
||
if (!_SEH2_AbnormalTermination()) {
|
||
|
||
FatCompleteRequest( IrpContext, Irp, Status );
|
||
}
|
||
|
||
DebugTrace(-1, Dbg, "FatCommonQueryInformation -> %08lx\n", Status);
|
||
} _SEH2_END;
|
||
|
||
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 );
|
||
|
||
_SEH2_TRY {
|
||
|
||
//
|
||
// Case on the type of open we're dealing with
|
||
//
|
||
|
||
switch (TypeOfOpen) {
|
||
|
||
case UserVolumeOpen:
|
||
|
||
//
|
||
// We cannot query the user volume open.
|
||
//
|
||
|
||
try_return( Status = STATUS_INVALID_PARAMETER );
|
||
break;
|
||
|
||
case UserFileOpen:
|
||
|
||
if (!FlagOn( Fcb->FcbState, FCB_STATE_PAGING_FILE ) &&
|
||
((FileInformationClass == FileEndOfFileInformation) ||
|
||
(FileInformationClass == FileAllocationInformation) ||
|
||
(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)) {
|
||
|
||
#ifdef _MSC_VER
|
||
#pragma prefast( suppress:28159, "things are seriously wrong if we get here" )
|
||
#endif
|
||
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;
|
||
} _SEH2_FINALLY {
|
||
|
||
DebugUnwind( FatCommonSetInformation );
|
||
|
||
if (FcbAcquired) { FatReleaseFcb( IrpContext, Fcb ); }
|
||
|
||
if (VcbAcquired) { FatReleaseVcb( IrpContext, Vcb ); }
|
||
|
||
if (!_SEH2_AbnormalTermination()) {
|
||
|
||
FatCompleteRequest( IrpContext, Irp, Status );
|
||
}
|
||
|
||
DebugTrace(-1, Dbg, "FatCommonSetInformation -> %08lx\n", Status);
|
||
} _SEH2_END;
|
||
|
||
return Status;
|
||
}
|
||
|
||
|
||
//
|
||
// Internal Support Routine
|
||
//
|
||
|
||
VOID
|
||
FatQueryBasicInfo (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PFCB Fcb,
|
||
IN PFILE_OBJECT FileObject,
|
||
IN OUT PFILE_BASIC_INFORMATION Buffer,
|
||
IN OUT PLONG Length
|
||
)
|
||
|
||
/*++
|
||
Description:
|
||
|
||
This routine performs the query basic information function for fat.
|
||
|
||
Arguments:
|
||
|
||
Fcb - Supplies the Fcb being queried, it has been verified
|
||
|
||
FileObject - Supplies the flag bit that indicates the file was modified.
|
||
|
||
Buffer - Supplies a pointer to the buffer where the information is to
|
||
be returned
|
||
|
||
Length - Supplies the length of the buffer in bytes, and receives the
|
||
remaining bytes free in the buffer upon return.
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
|
||
{
|
||
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);
|
||
|
||
_SEH2_TRY {
|
||
|
||
Buffer->IndexNumber.QuadPart = FatGenerateFileIdFromFcb( Fcb );
|
||
|
||
//
|
||
// Update the length and status output variables
|
||
//
|
||
|
||
*Length -= sizeof( FILE_INTERNAL_INFORMATION );
|
||
|
||
} _SEH2_FINALLY {
|
||
|
||
DebugUnwind( FatQueryInternalInfo );
|
||
|
||
DebugTrace( 0, Dbg, "*Length = %08lx\n", *Length);
|
||
|
||
DebugTrace(-1, Dbg, "FatQueryInternalInfo -> VOID\n", 0);
|
||
} _SEH2_END;
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
//
|
||
// Internal Support Routine
|
||
//
|
||
|
||
VOID
|
||
FatQueryEaInfo (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PFCB Fcb,
|
||
IN OUT PFILE_EA_INFORMATION Buffer,
|
||
IN OUT PLONG Length
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine performs the query Ea information function for fat.
|
||
|
||
Arguments:
|
||
|
||
Fcb - Supplies the Fcb being queried, it has been verified
|
||
|
||
Buffer - Supplies a pointer to the buffer where the information is to
|
||
be returned
|
||
|
||
Length - Supplies the length of the buffer in bytes, and receives the
|
||
remaining bytes free in the buffer upon return.
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
|
||
{
|
||
PBCB Bcb;
|
||
|
||
PAGED_CODE();
|
||
|
||
UNREFERENCED_PARAMETER( Fcb );
|
||
UNREFERENCED_PARAMETER( IrpContext );
|
||
|
||
DebugTrace(+1, Dbg, "FatQueryEaInfo...\n", 0);
|
||
|
||
Bcb = NULL;
|
||
|
||
_SEH2_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 );
|
||
|
||
} _SEH2_FINALLY {
|
||
|
||
DebugUnwind( FatQueryEaInfo );
|
||
|
||
//
|
||
// Unpin the dirent if pinned.
|
||
//
|
||
|
||
FatUnpinBcb( IrpContext, Bcb );
|
||
|
||
DebugTrace( 0, Dbg, "*Length = %08lx\n", *Length);
|
||
|
||
DebugTrace(-1, Dbg, "FatQueryEaInfo -> VOID\n", 0);
|
||
} _SEH2_END;
|
||
}
|
||
|
||
|
||
//
|
||
// 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;
|
||
|
||
#ifdef _MSC_VER
|
||
#pragma prefast( suppress:28931, "needed for debug build" )
|
||
#endif
|
||
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]);
|
||
|
||
#ifdef _MSC_VER
|
||
#pragma prefast( suppress:28931, "needed for debug build" )
|
||
#endif
|
||
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;
|
||
|
||
#if (NTDDI_VERSION >= NTDDI_WIN8)
|
||
BOOLEAN ModifiedAttributes = FALSE;
|
||
#endif
|
||
|
||
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;
|
||
|
||
_SEH2_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;
|
||
|
||
#if (NTDDI_VERSION >= NTDDI_WIN8)
|
||
ModifiedAttributes = TRUE;
|
||
#endif
|
||
}
|
||
}
|
||
|
||
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;
|
||
} _SEH2_FINALLY {
|
||
|
||
DebugUnwind( FatSetBasicInfo );
|
||
|
||
FatUnpinBcb( IrpContext, DirentBcb );
|
||
|
||
DebugTrace(-1, Dbg, "FatSetBasicInfo -> %08lx\n", Status);
|
||
} _SEH2_END;
|
||
|
||
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 );
|
||
|
||
_SEH2_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 );
|
||
|
||
} _SEH2_FINALLY {
|
||
|
||
if (_SEH2_AbnormalTermination() && (LocalBcb != NULL)) {
|
||
|
||
FatUnpinBcb( IrpContext, LocalBcb );
|
||
}
|
||
} _SEH2_END;
|
||
|
||
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).
|
||
//
|
||
|
||
_SEH2_TRY {
|
||
|
||
FatSetDirtyBcb( IrpContext, Bcb, Fcb->Vcb, TRUE );
|
||
|
||
} _SEH2_FINALLY {
|
||
|
||
FatUnpinBcb( IrpContext, Bcb );
|
||
} _SEH2_END;
|
||
}
|
||
|
||
//
|
||
// At this point either we have a file or an empty directory
|
||
// so we know the delete can proceed.
|
||
//
|
||
|
||
SetFlag( Fcb->FcbState, FCB_STATE_DELETE_ON_CLOSE );
|
||
FileObject->DeletePending = TRUE;
|
||
|
||
//
|
||
// If this is a directory then report this delete pending to
|
||
// the dir notify package.
|
||
//
|
||
|
||
if (NodeType(Fcb) == FAT_NTC_DCB) {
|
||
|
||
FsRtlNotifyFullChangeDirectory( Fcb->Vcb->NotifySync,
|
||
&Fcb->Vcb->DirNotifyList,
|
||
FileObject->FsContext,
|
||
NULL,
|
||
FALSE,
|
||
FALSE,
|
||
0,
|
||
NULL,
|
||
NULL,
|
||
NULL );
|
||
}
|
||
} else {
|
||
|
||
//
|
||
// The user doesn't want to delete the file so clear
|
||
// the delete on close bit
|
||
//
|
||
|
||
DebugTrace(0, Dbg, "User want to not delete file\n", 0);
|
||
|
||
ClearFlag( Fcb->FcbState, FCB_STATE_DELETE_ON_CLOSE );
|
||
FileObject->DeletePending = FALSE;
|
||
}
|
||
|
||
DebugTrace(-1, Dbg, "FatSetDispositionInfo -> STATUS_SUCCESS\n", 0);
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
|
||
//
|
||
// Internal Support Routine
|
||
//
|
||
|
||
NTSTATUS
|
||
FatSetRenameInfo (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PIRP Irp,
|
||
IN PVCB Vcb,
|
||
IN PFCB Fcb,
|
||
IN PCCB Ccb
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine performs the set name information for fat. It either
|
||
completes the request or enqueues it off to the fsp.
|
||
|
||
Arguments:
|
||
|
||
Irp - Supplies the irp being processed
|
||
|
||
Vcb - Supplies the Vcb being processed
|
||
|
||
Fcb - Supplies the Fcb or Dcb being processed, already known not to
|
||
be the root dcb
|
||
|
||
Ccb - Supplies the Ccb corresponding to the handle opening the source
|
||
file
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - The result of this operation if it completes without
|
||
an exception.
|
||
|
||
--*/
|
||
|
||
{
|
||
BOOLEAN AllLowerComponent;
|
||
BOOLEAN AllLowerExtension;
|
||
BOOLEAN CaseOnlyRename;
|
||
BOOLEAN ContinueWithRename;
|
||
BOOLEAN CreateLfn = 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.
|
||
//
|
||
|
||
_SEH2_TRY {
|
||
|
||
//
|
||
// Can't rename the root directory
|
||
//
|
||
|
||
if ( NodeType(Fcb) == FAT_NTC_ROOT_DCB ) {
|
||
|
||
try_return( Status = STATUS_INVALID_PARAMETER );
|
||
}
|
||
|
||
//
|
||
// Check that we were not given a dcb with open handles beneath
|
||
// it. If there are only UncleanCount == 0 Fcbs beneath us, then
|
||
// remove them from the prefix table, and they will just close
|
||
// and go away naturally.
|
||
//
|
||
|
||
if (NodeType(Fcb) == FAT_NTC_DCB) {
|
||
|
||
PFCB BatchOplockFcb;
|
||
ULONG BatchOplockCount;
|
||
|
||
//
|
||
// Loop until there are no batch oplocks in the subtree below
|
||
// this directory.
|
||
//
|
||
|
||
while (TRUE) {
|
||
|
||
BatchOplockFcb = NULL;
|
||
BatchOplockCount = 0;
|
||
|
||
//
|
||
// First look for any UncleanCount != 0 Fcbs, and fail if we
|
||
// find any.
|
||
//
|
||
|
||
for ( TempFcb = FatGetNextFcbBottomUp(IrpContext, NULL, Fcb);
|
||
TempFcb != Fcb;
|
||
TempFcb = FatGetNextFcbBottomUp(IrpContext, TempFcb, Fcb) ) {
|
||
|
||
if ( TempFcb->UncleanCount != 0 ) {
|
||
|
||
//
|
||
// If there is a batch oplock on this file then
|
||
// increment our count and remember the Fcb if
|
||
// this is the first.
|
||
//
|
||
|
||
if (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;
|
||
|
||
} _SEH2_FINALLY {
|
||
|
||
if (!ContinueWithRename) {
|
||
|
||
//
|
||
// Undo everything from above.
|
||
//
|
||
|
||
ExFreePool( UnicodeBuffer );
|
||
FatUnpinBcb( IrpContext, TargetDirentBcb );
|
||
|
||
}
|
||
} _SEH2_END;
|
||
|
||
//
|
||
// Now, if we are already done, return here.
|
||
//
|
||
|
||
if (!ContinueWithRename) {
|
||
|
||
return Status;
|
||
}
|
||
|
||
//
|
||
// P H A S E 2: Actually perform the rename.
|
||
//
|
||
|
||
_SEH2_TRY {
|
||
|
||
//
|
||
// Report the fact that we are going to remove this entry.
|
||
// If we renamed within the same directory and the new name for the
|
||
// file did not previously exist, we report this as a rename old
|
||
// name. Otherwise this is a removed file.
|
||
//
|
||
|
||
if (!RenamedAcrossDirectories && !DeleteTarget) {
|
||
|
||
NotifyAction = FILE_ACTION_RENAMED_OLD_NAME;
|
||
|
||
} else {
|
||
|
||
NotifyAction = FILE_ACTION_REMOVED;
|
||
}
|
||
|
||
FatNotifyReportChange( IrpContext,
|
||
Vcb,
|
||
Fcb,
|
||
((NodeType( Fcb ) == FAT_NTC_FCB)
|
||
? FILE_NOTIFY_CHANGE_FILE_NAME
|
||
: FILE_NOTIFY_CHANGE_DIR_NAME ),
|
||
NotifyAction );
|
||
|
||
_SEH2_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,
|
||
#ifndef __REACTOS__
|
||
&NewDirent,
|
||
#else
|
||
(PVOID *)&NewDirent,
|
||
#endif
|
||
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,
|
||
#ifndef __REACTOS__
|
||
&SecondPageDirent,
|
||
#else
|
||
(PVOID *)&SecondPageDirent,
|
||
#endif
|
||
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;
|
||
|
||
} _SEH2_FINALLY {
|
||
|
||
//
|
||
// Remove the entry from the splay table, and then remove the
|
||
// full file name and exact case lfn. It is important that we
|
||
// always remove the name from the prefix table regardless of
|
||
// other errors.
|
||
//
|
||
|
||
FatRemoveNames( IrpContext, Fcb );
|
||
|
||
if (Fcb->FullFileName.Buffer != NULL) {
|
||
|
||
ExFreePool( Fcb->FullFileName.Buffer );
|
||
Fcb->FullFileName.Buffer = NULL;
|
||
}
|
||
|
||
if (Fcb->ExactCaseLongName.Buffer) {
|
||
|
||
ExFreePool( Fcb->ExactCaseLongName.Buffer );
|
||
Fcb->ExactCaseLongName.Buffer = NULL;
|
||
}
|
||
|
||
FatUnpinBcb( IrpContext, OldDirentBcb );
|
||
FatUnpinBcb( IrpContext, TargetDirentBcb );
|
||
FatUnpinBcb( IrpContext, NewDirentBcb );
|
||
FatUnpinBcb( IrpContext, SecondPageBcb );
|
||
} _SEH2_END;
|
||
|
||
//
|
||
// Now we need to update the location of the file's directory
|
||
// offset and move the fcb from its current parent dcb to
|
||
// the target dcb.
|
||
//
|
||
|
||
Fcb->LfnOffsetWithinDirectory = NewOffset;
|
||
Fcb->DirentOffsetWithinDirectory = ShortDirentOffset;
|
||
|
||
RemoveEntryList( &Fcb->ParentDcbLinks );
|
||
|
||
//
|
||
// There is a deep reason we put files on the tail, others on the head,
|
||
// which is to allow us to easily enumerate all child directories before
|
||
// child files. This is important to let us maintain whole-volume lockorder
|
||
// via BottomUp enumeration.
|
||
//
|
||
|
||
if (NodeType(Fcb) == FAT_NTC_FCB) {
|
||
|
||
InsertTailList( &TargetDcb->Specific.Dcb.ParentDcbQueue,
|
||
&Fcb->ParentDcbLinks );
|
||
|
||
} else {
|
||
|
||
InsertHeadList( &TargetDcb->Specific.Dcb.ParentDcbQueue,
|
||
&Fcb->ParentDcbLinks );
|
||
}
|
||
|
||
OldParentDcb = Fcb->ParentDcb;
|
||
Fcb->ParentDcb = TargetDcb;
|
||
|
||
#if (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,
|
||
#ifndef __REACTOS__
|
||
&DotDotDirent,
|
||
#else
|
||
(PVOID *)&DotDotDirent,
|
||
#endif
|
||
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;
|
||
|
||
} _SEH2_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 (_SEH2_AbnormalTermination() && InvalidateFcbOnRaise) {
|
||
|
||
Fcb->FcbCondition = FcbBad;
|
||
}
|
||
|
||
DebugTrace(-1, Dbg, "FatSetRenameInfo -> %08lx\n", Status);
|
||
} _SEH2_END;
|
||
|
||
return Status;
|
||
}
|
||
|
||
|
||
//
|
||
// Internal Support Routine
|
||
//
|
||
|
||
NTSTATUS
|
||
FatSetPositionInfo (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PIRP Irp,
|
||
IN PFILE_OBJECT FileObject
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine performs the set position information for fat. It either
|
||
completes the request or enqueues it off to the fsp.
|
||
|
||
Arguments:
|
||
|
||
Irp - Supplies the irp being processed
|
||
|
||
FileObject - Supplies the file object being processed
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - The result of this operation if it completes without
|
||
an exception.
|
||
|
||
--*/
|
||
|
||
{
|
||
PFILE_POSITION_INFORMATION Buffer;
|
||
|
||
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 );
|
||
|
||
_SEH2_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
|
||
_SEH2_TRY {
|
||
#endif
|
||
|
||
CcSetFileSizes( FileObject, (PCC_FILE_SIZES)&Fcb->Header.AllocationSize );
|
||
|
||
#if DBG
|
||
} _SEH2_EXCEPT(FatBugCheckExceptionFilter( _SEH2_GetExceptionInformation() )) {
|
||
|
||
NOTHING;
|
||
} _SEH2_END;
|
||
#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;
|
||
|
||
} _SEH2_FINALLY {
|
||
|
||
if ( _SEH2_AbnormalTermination() && FileSizeTruncated ) {
|
||
|
||
Fcb->Header.FileSize.LowPart = OriginalFileSize;
|
||
Fcb->Header.ValidDataLength.LowPart = OriginalValidDataLength;
|
||
Fcb->ValidDataToDisk = OriginalValidDataToDisk;
|
||
|
||
//
|
||
// Make sure Cc knows the right filesize.
|
||
//
|
||
|
||
if (FileObject->SectionObjectPointer->SharedCacheMap != NULL) {
|
||
|
||
*CcGetFileSizePointer(FileObject) = Fcb->Header.FileSize;
|
||
}
|
||
|
||
NT_ASSERT( Fcb->Header.FileSize.LowPart <= Fcb->Header.AllocationSize.LowPart );
|
||
}
|
||
|
||
if (CacheMapInitialized) {
|
||
|
||
CcUninitializeCacheMap( FileObject, NULL, NULL );
|
||
}
|
||
|
||
if (ResourceAcquired) {
|
||
|
||
ExReleaseResourceLite( Fcb->Header.PagingIoResource );
|
||
|
||
}
|
||
|
||
} _SEH2_END;
|
||
|
||
DebugTrace(-1, Dbg, "FatSetAllocationInfo -> %08lx\n", STATUS_SUCCESS);
|
||
|
||
return Status;
|
||
}
|
||
|
||
|
||
//
|
||
// Internal Support Routine
|
||
//
|
||
|
||
_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;
|
||
|
||
_SEH2_TRY {
|
||
|
||
//
|
||
// File Size changes are only allowed on a file and not a directory
|
||
//
|
||
|
||
if (NodeType(Fcb) != FAT_NTC_FCB) {
|
||
|
||
DebugTrace(0, Dbg, "Cannot change size of a directory\n", 0);
|
||
|
||
try_return( Status = STATUS_INVALID_DEVICE_REQUEST );
|
||
}
|
||
|
||
|
||
//
|
||
// Check that the new file size is legal
|
||
//
|
||
|
||
|
||
if (!FatIsIoRangeValid( Fcb->Vcb, Buffer->EndOfFile, 0)) {
|
||
|
||
DebugTrace(0, Dbg, "Illegal allocation size\n", 0);
|
||
|
||
try_return( Status = STATUS_DISK_FULL );
|
||
}
|
||
|
||
NewFileSize = Buffer->EndOfFile.LowPart;
|
||
|
||
|
||
//
|
||
// If we haven't yet looked up the correct AllocationSize, do so.
|
||
//
|
||
|
||
if ((Fcb->Header.AllocationSize.QuadPart == FCB_LOOKUP_ALLOCATIONSIZE_HINT) &&
|
||
(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 );
|
||
_SEH2_TRY {
|
||
|
||
|
||
if ( NewFileSize > Dirent->FileSize ) {
|
||
Dirent->FileSize = NewFileSize;
|
||
|
||
FatSetDirtyBcb( IrpContext, DirentBcb, Fcb->Vcb, TRUE );
|
||
|
||
//
|
||
// Report that we just changed the file size.
|
||
//
|
||
|
||
FatNotifyReportChange( IrpContext,
|
||
Vcb,
|
||
Fcb,
|
||
FILE_NOTIFY_CHANGE_SIZE,
|
||
FILE_ACTION_MODIFIED );
|
||
|
||
}
|
||
|
||
} _SEH2_FINALLY {
|
||
|
||
FatUnpinBcb( IrpContext, DirentBcb );
|
||
} _SEH2_END;
|
||
|
||
} else {
|
||
|
||
DebugTrace(0, Dbg, "Cannot set size on deleted file.\n", 0);
|
||
}
|
||
|
||
try_return( Status = STATUS_SUCCESS );
|
||
}
|
||
|
||
//
|
||
// Check if the new file size is greater than the current
|
||
// allocation size. If it is then we need to increase the
|
||
// allocation size.
|
||
//
|
||
|
||
|
||
if ( (NewFileSize) > Fcb->Header.AllocationSize.LowPart ) {
|
||
|
||
//
|
||
// Change the file allocation
|
||
//
|
||
|
||
FatAddFileAllocation( IrpContext, Fcb, FileObject, NewFileSize );
|
||
}
|
||
|
||
|
||
//
|
||
// At this point we have enough allocation for the file.
|
||
// So check if we are really changing the file size
|
||
//
|
||
|
||
if (Fcb->Header.FileSize.LowPart != NewFileSize) {
|
||
|
||
if ( NewFileSize < Fcb->Header.FileSize.LowPart ) {
|
||
|
||
//
|
||
// 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 );
|
||
|
||
} _SEH2_FINALLY {
|
||
|
||
DebugUnwind( FatSetEndOfFileInfo );
|
||
|
||
if (_SEH2_AbnormalTermination() && UnwindFileSizes) {
|
||
|
||
Fcb->Header.FileSize.LowPart = InitialFileSize;
|
||
Fcb->Header.ValidDataLength.LowPart = InitialValidDataLength;
|
||
Fcb->ValidDataToDisk = InitialValidDataToDisk;
|
||
|
||
if (FileObject->SectionObjectPointer->SharedCacheMap != NULL) {
|
||
|
||
*CcGetFileSizePointer(FileObject) = Fcb->Header.FileSize;
|
||
}
|
||
|
||
//
|
||
// 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);
|
||
} _SEH2_END;
|
||
|
||
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;
|
||
|
||
_SEH2_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;
|
||
|
||
} _SEH2_FINALLY {
|
||
|
||
DebugUnwind( FatSetValidDataLengthInfo );
|
||
|
||
if (ResourceAcquired) {
|
||
|
||
ExReleaseResourceLite( Fcb->Header.PagingIoResource );
|
||
}
|
||
|
||
DebugTrace(-1, Dbg, "FatSetValidDataLengthInfo -> %08lx\n", Status);
|
||
} _SEH2_END;
|
||
|
||
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;
|
||
|
||
_SEH2_TRY {
|
||
|
||
//
|
||
// Use a try-except to catch any errors.
|
||
//
|
||
|
||
_SEH2_TRY {
|
||
|
||
|
||
//
|
||
// Try to get the Ea file object. Return FALSE on failure.
|
||
//
|
||
|
||
FatGetEaFile( IrpContext,
|
||
Vcb,
|
||
&EaDirent,
|
||
&EaBcb,
|
||
FALSE,
|
||
FALSE );
|
||
|
||
LockedEaFcb = TRUE;
|
||
|
||
//
|
||
// If we didn't get the file because it doesn't exist, then the
|
||
// disk is corrupted. We do nothing here.
|
||
//
|
||
|
||
if (Vcb->VirtualEaFile != NULL) {
|
||
|
||
//
|
||
// Try to pin down the Ea set header for the index in the
|
||
// dirent. If the operation doesn't complete, return FALSE
|
||
// from this routine.
|
||
//
|
||
|
||
FatReadEaSet( IrpContext,
|
||
Vcb,
|
||
ExtendedAttributes,
|
||
OldOemName,
|
||
FALSE,
|
||
&EaSetRange );
|
||
|
||
EaSetHeader = (PEA_SET_HEADER) EaSetRange.Data;
|
||
|
||
//
|
||
// We now have the Ea set header for this file. We simply
|
||
// overwrite the owning file name.
|
||
//
|
||
|
||
RtlZeroMemory( EaSetHeader->OwnerFileName, 14 );
|
||
|
||
RtlCopyMemory( EaSetHeader->OwnerFileName,
|
||
Fcb->ShortName.Name.Oem.Buffer,
|
||
Fcb->ShortName.Name.Oem.Length );
|
||
|
||
FatMarkEaRangeDirty( IrpContext, Vcb->VirtualEaFile, &EaSetRange );
|
||
FatUnpinEaRange( IrpContext, &EaSetRange );
|
||
|
||
CcFlushCache( Vcb->VirtualEaFile->SectionObjectPointer, NULL, 0, NULL );
|
||
}
|
||
|
||
} _SEH2_EXCEPT(FatExceptionFilter( IrpContext, _SEH2_GetExceptionInformation() )) {
|
||
|
||
//
|
||
// We catch all exceptions that Fat catches, but don't do
|
||
// anything with them.
|
||
//
|
||
} _SEH2_END;
|
||
|
||
} _SEH2_FINALLY {
|
||
|
||
//
|
||
// Unpin the EaDirent and the EaSetHeader if pinned.
|
||
//
|
||
|
||
FatUnpinBcb( IrpContext, EaBcb );
|
||
FatUnpinEaRange( IrpContext, &EaSetRange );
|
||
|
||
//
|
||
// Release the Fcb for the Ea file if locked.
|
||
//
|
||
|
||
if (LockedEaFcb) {
|
||
|
||
FatReleaseFcb( IrpContext, Vcb->EaFcb );
|
||
}
|
||
} _SEH2_END;
|
||
|
||
return;
|
||
}
|
||
|
||
_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 ) {
|
||
|
||
#ifdef _MSC_VER
|
||
#pragma prefast( suppress:28159, "things are seriously wrong if we get here" )
|
||
#endif
|
||
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;
|
||
|
||
_SEH2_TRY {
|
||
|
||
FatTruncateFileAllocation( IrpContext, Fcb, 0 );
|
||
|
||
FatDeleteDirent( IrpContext, Fcb, NULL, TRUE );
|
||
|
||
} _SEH2_FINALLY {
|
||
|
||
FatDeleteFcb( IrpContext, &Fcb );
|
||
} _SEH2_END;
|
||
}
|
||
|
||
|