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

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

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

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

/*++
Copyright (c) 1989-2000 Microsoft Corporation
Module Name:
Create.c
Abstract:
This module implements the File Create routine for Fat called by the
dispatch driver.
--*/
#include "FatProcs.h"
//
// The Bug check file id for this module
//
#define BugCheckFileId (FAT_BUG_CHECK_CREATE)
//
// The debug trace level
//
#define Dbg (DEBUG_TRACE_CREATE)
//
// Macros for incrementing performance counters.
//
#define CollectCreateHitStatistics(VCB) { \
PFILE_SYSTEM_STATISTICS Stats = &(VCB)->Statistics[KeGetCurrentProcessorNumber() % FatData.NumberProcessors]; \
Stats->Fat.CreateHits += 1; \
}
#define CollectCreateStatistics(VCB,STATUS) { \
PFILE_SYSTEM_STATISTICS Stats = &(VCB)->Statistics[KeGetCurrentProcessorNumber() % FatData.NumberProcessors]; \
if ((STATUS) == STATUS_SUCCESS) { \
Stats->Fat.SuccessfulCreates += 1; \
} else { \
Stats->Fat.FailedCreates += 1; \
} \
}
LUID FatSecurityPrivilege = { SE_SECURITY_PRIVILEGE, 0 };
//
// local procedure prototypes
//
_Requires_lock_held_(_Global_critical_region_)
IO_STATUS_BLOCK
FatOpenVolume (
_In_ PIRP_CONTEXT IrpContext,
_Inout_ PFILE_OBJECT FileObject,
_Inout_ PVCB Vcb,
_In_ PACCESS_MASK DesiredAccess,
_In_ USHORT ShareAccess,
_In_ ULONG CreateDisposition
);
_Requires_lock_held_(_Global_critical_region_)
IO_STATUS_BLOCK
FatOpenRootDcb (
_In_ PIRP_CONTEXT IrpContext,
_Inout_ PFILE_OBJECT FileObject,
_Inout_ PVCB Vcb,
_In_ PACCESS_MASK DesiredAccess,
_In_ USHORT ShareAccess,
_In_ ULONG CreateDisposition
);
_Requires_lock_held_(_Global_critical_region_)
IO_STATUS_BLOCK
FatOpenExistingDcb (
_In_ PIRP_CONTEXT IrpContext,
_In_ PIO_STACK_LOCATION IrpSp,
_Inout_ PFILE_OBJECT FileObject,
_Inout_ PVCB Vcb,
_Inout_ PDCB Dcb,
_In_ PACCESS_MASK DesiredAccess,
_In_ USHORT ShareAccess,
_In_ ULONG CreateDisposition,
_In_ BOOLEAN NoEaKnowledge,
_In_ BOOLEAN DeleteOnClose,
_In_ BOOLEAN OpenRequiringOplock,
_In_ BOOLEAN FileNameOpenedDos,
_Out_ PBOOLEAN OplockPostIrp
);
_Requires_lock_held_(_Global_critical_region_)
IO_STATUS_BLOCK
FatOpenExistingFcb (
_In_ PIRP_CONTEXT IrpContext,
_In_ PIO_STACK_LOCATION IrpSp,
_Inout_ PFILE_OBJECT FileObject,
_Inout_ PVCB Vcb,
_Inout_ PFCB Fcb,
_In_ PACCESS_MASK DesiredAccess,
_In_ USHORT ShareAccess,
_In_ ULONG AllocationSize,
_In_ PFILE_FULL_EA_INFORMATION EaBuffer,
_In_ ULONG EaLength,
_In_ USHORT FileAttributes,
_In_ ULONG CreateDisposition,
_In_ BOOLEAN NoEaKnowledge,
_In_ BOOLEAN DeleteOnClose,
_In_ BOOLEAN OpenRequiringOplock,
_In_ BOOLEAN FileNameOpenedDos,
_Out_ PBOOLEAN OplockPostIrp
);
_Requires_lock_held_(_Global_critical_region_)
IO_STATUS_BLOCK
FatOpenTargetDirectory (
_In_ PIRP_CONTEXT IrpContext,
_Inout_ PFILE_OBJECT FileObject,
_Inout_ PDCB Dcb,
_In_ PACCESS_MASK DesiredAccess,
_In_ USHORT ShareAccess,
_In_ BOOLEAN DoesNameExist,
_In_ BOOLEAN FileNameOpenedDos
);
_Success_(return.Status == STATUS_SUCCESS)
_Requires_lock_held_(_Global_critical_region_)
IO_STATUS_BLOCK
FatOpenExistingDirectory (
_In_ PIRP_CONTEXT IrpContext,
_In_ PIO_STACK_LOCATION IrpSp,
_Inout_ PFILE_OBJECT FileObject,
_Inout_ PVCB Vcb,
_Outptr_result_maybenull_ PDCB *Dcb,
_In_ PDCB ParentDcb,
_In_ PDIRENT Dirent,
_In_ ULONG LfnByteOffset,
_In_ ULONG DirentByteOffset,
_In_ PUNICODE_STRING Lfn,
_In_ PACCESS_MASK DesiredAccess,
_In_ USHORT ShareAccess,
_In_ ULONG CreateDisposition,
_In_ BOOLEAN NoEaKnowledge,
_In_ BOOLEAN DeleteOnClose,
_In_ BOOLEAN FileNameOpenedDos,
_In_ BOOLEAN OpenRequiringOplock
);
_Requires_lock_held_(_Global_critical_region_)
IO_STATUS_BLOCK
FatOpenExistingFile (
_In_ PIRP_CONTEXT IrpContext,
_Inout_ PFILE_OBJECT FileObject,
_Inout_ PVCB Vcb,
_Outptr_result_maybenull_ PFCB *Fcb,
_In_ PDCB ParentDcb,
_In_ PDIRENT Dirent,
_In_ ULONG LfnByteOffset,
_In_ ULONG DirentByteOffset,
_In_ PUNICODE_STRING Lfn,
_In_ PUNICODE_STRING OrigLfn,
_In_ PACCESS_MASK DesiredAccess,
_In_ USHORT ShareAccess,
_In_ ULONG AllocationSize,
_In_ PFILE_FULL_EA_INFORMATION EaBuffer,
_In_ ULONG EaLength,
_In_ USHORT FileAttributes,
_In_ ULONG CreateDisposition,
_In_ BOOLEAN IsPagingFile,
_In_ BOOLEAN NoEaKnowledge,
_In_ BOOLEAN DeleteOnClose,
_In_ BOOLEAN OpenRequiringOplock,
_In_ BOOLEAN FileNameOpenedDos
);
_Requires_lock_held_(_Global_critical_region_)
IO_STATUS_BLOCK
FatCreateNewDirectory (
_In_ PIRP_CONTEXT IrpContext,
_In_ PIO_STACK_LOCATION IrpSp,
_Inout_ PFILE_OBJECT FileObject,
_Inout_ PVCB Vcb,
_Inout_ PDCB ParentDcb,
_In_ POEM_STRING OemName,
_In_ PUNICODE_STRING UnicodeName,
_In_ PACCESS_MASK DesiredAccess,
_In_ USHORT ShareAccess,
_In_ PFILE_FULL_EA_INFORMATION EaBuffer,
_In_ ULONG EaLength,
_In_ USHORT FileAttributes,
_In_ BOOLEAN NoEaKnowledge,
_In_ BOOLEAN DeleteOnClose,
_In_ BOOLEAN OpenRequiringOplock
);
_Requires_lock_held_(_Global_critical_region_)
IO_STATUS_BLOCK
FatCreateNewFile (
_In_ PIRP_CONTEXT IrpContext,
_In_ PIO_STACK_LOCATION IrpSp,
_Inout_ PFILE_OBJECT FileObject,
_Inout_ PVCB Vcb,
_Inout_ PDCB ParentDcb,
_In_ POEM_STRING OemName,
_In_ PUNICODE_STRING UnicodeName,
_In_ PACCESS_MASK DesiredAccess,
_In_ USHORT ShareAccess,
_In_ ULONG AllocationSize,
_In_ PFILE_FULL_EA_INFORMATION EaBuffer,
_In_ ULONG EaLength,
_In_ USHORT FileAttributes,
_In_ PUNICODE_STRING LfnBuffer,
_In_ BOOLEAN IsPagingFile,
_In_ BOOLEAN NoEaKnowledge,
_In_ BOOLEAN DeleteOnClose,
_In_ BOOLEAN OpenRequiringOplock,
_In_ BOOLEAN TemporaryFile
);
_Requires_lock_held_(_Global_critical_region_)
IO_STATUS_BLOCK
FatSupersedeOrOverwriteFile (
_In_ PIRP_CONTEXT IrpContext,
_Inout_ PFILE_OBJECT FileObject,
_Inout_ PFCB Fcb,
_In_ ULONG AllocationSize,
_In_ PFILE_FULL_EA_INFORMATION EaBuffer,
_In_ ULONG EaLength,
_In_ USHORT FileAttributes,
_In_ ULONG CreateDisposition,
_In_ BOOLEAN NoEaKnowledge
);
NTSTATUS
FatCheckSystemSecurityAccess (
_In_ PIRP_CONTEXT IrpContext
);
NTSTATUS
FatCheckShareAccess (
_In_ PIRP_CONTEXT IrpContext,
_In_ PFILE_OBJECT FileObject,
_In_ PFCB Fcb,
_In_ PACCESS_MASK DesiredAccess,
_In_ ULONG ShareAccess
);
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, FatCheckShareAccess)
#pragma alloc_text(PAGE, FatCheckSystemSecurityAccess)
#pragma alloc_text(PAGE, FatCommonCreate)
#if (NTDDI_VERSION >= NTDDI_WINTHRESHOLD)
#pragma alloc_text(PAGE, FatCommonCreateOnNewStack)
#pragma alloc_text(PAGE, FatCommonCreateCallout)
#endif
#pragma alloc_text(PAGE, FatCreateNewDirectory)
#pragma alloc_text(PAGE, FatCreateNewFile)
#pragma alloc_text(PAGE, FatFsdCreate)
#pragma alloc_text(PAGE, FatOpenExistingDcb)
#pragma alloc_text(PAGE, FatOpenExistingDirectory)
#pragma alloc_text(PAGE, FatOpenExistingFcb)
#pragma alloc_text(PAGE, FatOpenExistingFile)
#pragma alloc_text(PAGE, FatOpenRootDcb)
#pragma alloc_text(PAGE, FatOpenTargetDirectory)
#pragma alloc_text(PAGE, FatOpenVolume)
#pragma alloc_text(PAGE, FatSupersedeOrOverwriteFile)
#pragma alloc_text(PAGE, FatSetFullNameInFcb)
#endif
_Function_class_(IRP_MJ_CREATE)
_Function_class_(DRIVER_DISPATCH)
NTSTATUS
FatFsdCreate (
_In_ PVOLUME_DEVICE_OBJECT VolumeDeviceObject,
_Inout_ PIRP Irp
)
/*++
Routine Description:
This routine implements the FSD part of the NtCreateFile and NtOpenFile
API calls.
Arguments:
VolumeDeviceObject - Supplies the volume device object where the
file/directory exists that we are trying to open/create
Irp - Supplies the Irp being processed
Return Value:
NTSTATUS - The Fsd status for the Irp
--*/
{
NTSTATUS Status;
PIRP_CONTEXT IrpContext = NULL;
BOOLEAN TopLevel;
BOOLEAN ExceptionCompletedIrp = FALSE;
PAGED_CODE();
//
// If we were called with our file system device object instead of a
// volume device object, just complete this request with STATUS_SUCCESS
//
if ( FatDeviceIsFatFsdo( VolumeDeviceObject)) {
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = FILE_OPENED;
IoCompleteRequest( Irp, IO_DISK_INCREMENT );
return STATUS_SUCCESS;
}
TimerStart(Dbg);
DebugTrace(+1, Dbg, "FatFsdCreate\n", 0);
//
// Call the common create routine, with block allowed if the operation
// is synchronous.
//
FsRtlEnterFileSystem();
TopLevel = FatIsIrpTopLevel( Irp );
try {
IrpContext = FatCreateIrpContext( Irp, TRUE );
#if (NTDDI_VERSION >= NTDDI_WINTHRESHOLD)
Status = FatCommonCreateOnNewStack( IrpContext, Irp );
#else
Status = FatCommonCreate( IrpContext, Irp );
#endif
} except(FatExceptionFilter( IrpContext, GetExceptionInformation() )) {
//
// We had some trouble trying to perform the requested
// operation, so we'll abort the I/O request with
// the error status that we get back from the
// execption code
//
Status = FatProcessException( IrpContext, Irp, GetExceptionCode() );
ExceptionCompletedIrp = TRUE;
}
if (TopLevel) { IoSetTopLevelIrp( NULL ); }
FsRtlExitFileSystem();
//
// Complete the request, unless we had an exception, in which case it
// was completed in FatProcessException (and the IrpContext freed).
//
// IrpContext is freed inside FatCompleteRequest.
//
if (!ExceptionCompletedIrp && Status != STATUS_PENDING) {
FatCompleteRequest( IrpContext, Irp, Status );
}
//
// And return to our caller
//
DebugTrace(-1, Dbg, "FatFsdCreate -> %08lx\n", Status );
TimerStop(Dbg,"FatFsdCreate");
UNREFERENCED_PARAMETER( VolumeDeviceObject );
return Status;
}
#if (NTDDI_VERSION >= NTDDI_WINTHRESHOLD)
_Requires_lock_held_(_Global_critical_region_)
VOID
FatCommonCreateCallout (
_In_ PFAT_CALLOUT_PARAMETERS CalloutParameters
)
/*++
Routine Description:
This function is the callout routine that will execute on a new stack when
processing a create. It simply calls FatCommonCreate() with the parameters
in the context and stores the return value in the context.
Arguments:
Context - Supplies an opaque pointer to this function's context. It is actually
an FAT_CALLOUT_PARAMETERS structure.
Return Value:
None.
--*/
{
PAGED_CODE();
//
// Call FatCommonCreate() with the passed parameters and store the result.
// Exceptions cannot be raised across stack boundaries, so we need to catch
// exceptions here and deal with them.
//
try {
CalloutParameters->IrpStatus = FatCommonCreate( CalloutParameters->Create.IrpContext,
CalloutParameters->Create.Irp );
} except (FatExceptionFilter( CalloutParameters->Create.IrpContext, GetExceptionInformation() )) {
//
// Return the resulting status.
//
CalloutParameters->ExceptionStatus = GetExceptionCode();
}
}
_Requires_lock_held_(_Global_critical_region_)
NTSTATUS
FatCommonCreateOnNewStack (
_In_ PIRP_CONTEXT IrpContext,
_In_ PIRP Irp
)
/*++
Routine Description:
This routine sets up a switch to a new stack and call to FatCommonCreate().
Arguments:
IrpContext - Supplies the context structure for the overall request.
Irp - Supplies the IRP being processed.
CreateContext - Supplies a pointer on the old stack that is used to
store context information for the create itself.
Return Value:
NTSTATUS - The status from FatCommonCreate().
--*/
{
FAT_CALLOUT_PARAMETERS CalloutParameters;
NTSTATUS status;
PAGED_CODE();
//
// Create requests consume a lot of stack space. As such, we always switch to a
// new stack when processing a create. Setup the callout parameters and make the
// call. Note that this cannot fail, since we pass a stack context for a reserve stack.
//
CalloutParameters.Create.IrpContext = IrpContext;
CalloutParameters.Create.Irp = Irp;
CalloutParameters.ExceptionStatus = CalloutParameters.IrpStatus = STATUS_SUCCESS;
//
// Mark that we are swapping the stack
//
SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_SWAPPED_STACK );
status = KeExpandKernelStackAndCalloutEx( FatCommonCreateCallout,
&CalloutParameters,
KERNEL_STACK_SIZE,
FALSE,
NULL );
//
// Mark that the stack is no longer swapped. Note that there are paths
// that may clear this flag before returning.
//
if (status != STATUS_PENDING) {
ClearFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_SWAPPED_STACK );
}
//
// If we had an exception occur, re-raise the exception.
//
if (!NT_SUCCESS( CalloutParameters.ExceptionStatus )) {
FatRaiseStatus( IrpContext, CalloutParameters.ExceptionStatus );
}
//
// If the call to KeExpandKernelStackAndCalloutEx returns an error this
// means that the callout routine (FatCommonCreateCallout) was never
// called. Translate that error, and return it.
//
if (!NT_SUCCESS( status )) {
//
// Translate to an expected error value
//
if (status == STATUS_NO_MEMORY) {
status = STATUS_INSUFFICIENT_RESOURCES;
}
return status;
}
//
// Return the status given to us by the callout.
//
return CalloutParameters.IrpStatus;
}
#endif
_Requires_lock_held_(_Global_critical_region_)
NTSTATUS
FatCommonCreate (
_Inout_ PIRP_CONTEXT IrpContext,
_Inout_ PIRP Irp
)
/*++
Routine Description:
This is the common routine for creating/opening a file called by
both the fsd and fsp threads.
Arguments:
Irp - Supplies the Irp to process
Return Value:
NTSTATUS - the return status for the operation
--*/
{
NTSTATUS Status;
IO_STATUS_BLOCK Iosb = {0};
PIO_STACK_LOCATION IrpSp;
PFILE_OBJECT FileObject;
PFILE_OBJECT RelatedFileObject;
UNICODE_STRING FileName;
ULONG AllocationSize;
PFILE_FULL_EA_INFORMATION EaBuffer;
PACCESS_MASK DesiredAccess;
ULONG Options;
USHORT FileAttributes;
USHORT ShareAccess;
ULONG EaLength;
BOOLEAN CreateDirectory;
BOOLEAN NoIntermediateBuffering;
BOOLEAN OpenDirectory;
BOOLEAN IsPagingFile;
BOOLEAN OpenTargetDirectory;
BOOLEAN DirectoryFile;
BOOLEAN NonDirectoryFile;
BOOLEAN NoEaKnowledge;
BOOLEAN DeleteOnClose;
BOOLEAN OpenRequiringOplock;
BOOLEAN TemporaryFile;
BOOLEAN FileNameOpenedDos = FALSE;
ULONG CreateDisposition;
PVCB Vcb;
PFCB Fcb = NULL;
PCCB Ccb;
PDCB ParentDcb;
PDCB FinalDcb = NULL;
UNICODE_STRING FinalName = {0};
UNICODE_STRING RemainingPart;
UNICODE_STRING NextRemainingPart = {0};
UNICODE_STRING UpcasedFinalName;
WCHAR UpcasedBuffer[ FAT_CREATE_INITIAL_NAME_BUF_SIZE];
OEM_STRING OemFinalName;
UCHAR OemBuffer[ FAT_CREATE_INITIAL_NAME_BUF_SIZE*2];
PDIRENT Dirent;
PBCB DirentBcb = NULL;
ULONG LfnByteOffset;
ULONG DirentByteOffset;
BOOLEAN PostIrp = FALSE;
BOOLEAN OplockPostIrp = FALSE;
BOOLEAN TrailingBackslash;
BOOLEAN FirstLoop = TRUE;
ULONG MatchFlags = 0;
CCB LocalCcb;
UNICODE_STRING Lfn;
UNICODE_STRING OrigLfn = {0};
WCHAR LfnBuffer[ FAT_CREATE_INITIAL_NAME_BUF_SIZE];
PAGED_CODE();
//
// Get the current IRP stack location
//
IrpSp = IoGetCurrentIrpStackLocation( Irp );
DebugTrace(+1, Dbg, "FatCommonCreate\n", 0 );
DebugTrace( 0, Dbg, "Irp = %p\n", Irp );
DebugTrace( 0, Dbg, "->Flags = %08lx\n", Irp->Flags );
DebugTrace( 0, Dbg, "->FileObject = %p\n", IrpSp->FileObject );
DebugTrace( 0, Dbg, " ->RelatedFileObject = %p\n", IrpSp->FileObject->RelatedFileObject );
DebugTrace( 0, Dbg, " ->FileName = %wZ\n", &IrpSp->FileObject->FileName );
DebugTrace( 0, Dbg, " ->FileName.Length = 0n%d\n", IrpSp->FileObject->FileName.Length );
DebugTrace( 0, Dbg, "->AllocationSize.LowPart = %08lx\n", Irp->Overlay.AllocationSize.LowPart );
DebugTrace( 0, Dbg, "->AllocationSize.HighPart = %08lx\n", Irp->Overlay.AllocationSize.HighPart );
DebugTrace( 0, Dbg, "->SystemBuffer = %p\n", Irp->AssociatedIrp.SystemBuffer );
DebugTrace( 0, Dbg, "->DesiredAccess = %08lx\n", IrpSp->Parameters.Create.SecurityContext->DesiredAccess );
DebugTrace( 0, Dbg, "->Options = %08lx\n", IrpSp->Parameters.Create.Options );
DebugTrace( 0, Dbg, "->FileAttributes = %04x\n", IrpSp->Parameters.Create.FileAttributes );
DebugTrace( 0, Dbg, "->ShareAccess = %04x\n", IrpSp->Parameters.Create.ShareAccess );
DebugTrace( 0, Dbg, "->EaLength = %08lx\n", IrpSp->Parameters.Create.EaLength );
//
// This is here because the Win32 layer can't avoid sending me double
// beginning backslashes.
//
if ((IrpSp->FileObject->FileName.Length > sizeof(WCHAR)) &&
(IrpSp->FileObject->FileName.Buffer[1] == L'\\') &&
(IrpSp->FileObject->FileName.Buffer[0] == L'\\')) {
IrpSp->FileObject->FileName.Length -= sizeof(WCHAR);
RtlMoveMemory( &IrpSp->FileObject->FileName.Buffer[0],
&IrpSp->FileObject->FileName.Buffer[1],
IrpSp->FileObject->FileName.Length );
//
// If there are still two beginning backslashes, the name is bogus.
//
if ((IrpSp->FileObject->FileName.Length > sizeof(WCHAR)) &&
(IrpSp->FileObject->FileName.Buffer[1] == L'\\') &&
(IrpSp->FileObject->FileName.Buffer[0] == L'\\')) {
DebugTrace(-1, Dbg, "FatCommonCreate -> STATUS_OBJECT_NAME_INVALID\n", 0);
return STATUS_OBJECT_NAME_INVALID;
}
}
//
// Reference our input parameters to make things easier
//
NT_ASSERT( IrpSp->Parameters.Create.SecurityContext != NULL );
FileObject = IrpSp->FileObject;
FileName = FileObject->FileName;
RelatedFileObject = FileObject->RelatedFileObject;
AllocationSize = Irp->Overlay.AllocationSize.LowPart;
EaBuffer = Irp->AssociatedIrp.SystemBuffer;
DesiredAccess = &IrpSp->Parameters.Create.SecurityContext->DesiredAccess;
Options = IrpSp->Parameters.Create.Options;
FileAttributes = IrpSp->Parameters.Create.FileAttributes & ~FILE_ATTRIBUTE_NORMAL;
ShareAccess = IrpSp->Parameters.Create.ShareAccess;
EaLength = IrpSp->Parameters.Create.EaLength;
//
// Set up the file object's Vpb pointer in case anything happens.
// This will allow us to get a reasonable pop-up.
//
if ( RelatedFileObject != NULL ) {
FileObject->Vpb = RelatedFileObject->Vpb;
}
//
// Force setting the archive bit in the attributes byte to follow OS/2,
// & DOS semantics. Also mask out any extraneous bits, note that
// we can't use the ATTRIBUTE_VALID_FLAGS constant because that has
// the control and normal flags set.
//
// Delay setting ARCHIVE in case this is a directory: 2/16/95
//
FileAttributes &= (FILE_ATTRIBUTE_READONLY |
FILE_ATTRIBUTE_HIDDEN |
FILE_ATTRIBUTE_SYSTEM |
FILE_ATTRIBUTE_ARCHIVE |
FILE_ATTRIBUTE_ENCRYPTED);
//
// Locate the volume device object and Vcb that we are trying to access
//
Vcb = &((PVOLUME_DEVICE_OBJECT)IrpSp->DeviceObject)->Vcb;
//
// Decipher Option flags and values
//
//
// If this is an open by fileid operation, just fail it explicitly. FAT's
// source of fileids is not reversible for open operations.
//
if (BooleanFlagOn( Options, FILE_OPEN_BY_FILE_ID )) {
return STATUS_NOT_IMPLEMENTED;
}
DirectoryFile = BooleanFlagOn( Options, FILE_DIRECTORY_FILE );
NonDirectoryFile = BooleanFlagOn( Options, FILE_NON_DIRECTORY_FILE );
NoIntermediateBuffering = BooleanFlagOn( Options, FILE_NO_INTERMEDIATE_BUFFERING );
NoEaKnowledge = BooleanFlagOn( Options, FILE_NO_EA_KNOWLEDGE );
DeleteOnClose = BooleanFlagOn( Options, FILE_DELETE_ON_CLOSE );
#if (NTDDI_VERSION >= NTDDI_WIN7)
OpenRequiringOplock = BooleanFlagOn( Options, FILE_OPEN_REQUIRING_OPLOCK );
#else
OpenRequiringOplock = FALSE;
#endif
TemporaryFile = BooleanFlagOn( IrpSp->Parameters.Create.FileAttributes,
FILE_ATTRIBUTE_TEMPORARY );
CreateDisposition = (Options >> 24) & 0x000000ff;
IsPagingFile = BooleanFlagOn( IrpSp->Flags, SL_OPEN_PAGING_FILE );
OpenTargetDirectory = BooleanFlagOn( IrpSp->Flags, SL_OPEN_TARGET_DIRECTORY );
CreateDirectory = (BOOLEAN)(DirectoryFile &&
((CreateDisposition == FILE_CREATE) ||
(CreateDisposition == FILE_OPEN_IF)));
OpenDirectory = (BOOLEAN)(DirectoryFile &&
((CreateDisposition == FILE_OPEN) ||
(CreateDisposition == FILE_OPEN_IF)));
//
// Make sure the input large integer is valid and that the dir/nondir
// indicates a storage type we understand.
//
if (Irp->Overlay.AllocationSize.HighPart != 0 ||
(DirectoryFile && NonDirectoryFile)) {
DebugTrace(-1, Dbg, "FatCommonCreate -> STATUS_INVALID_PARAMETER\n", 0);
return STATUS_INVALID_PARAMETER;
}
//
// Acquire exclusive access to the vcb, and enqueue the Irp if
// we didn't get it.
//
if (!FatAcquireExclusiveVcb( IrpContext, Vcb )) {
DebugTrace(0, Dbg, "Cannot acquire Vcb\n", 0);
Iosb.Status = FatFsdPostRequest( IrpContext, Irp );
DebugTrace(-1, Dbg, "FatCommonCreate -> %08lx\n", Iosb.Status );
return Iosb.Status;
}
//
// Make sure we haven't been called recursively by a filter inside an existing
// create request.
//
if (FlagOn( Vcb->VcbState, VCB_STATE_FLAG_CREATE_IN_PROGRESS)) {
#pragma prefast( suppress:28159, "this is a serious programming error if it happens" )
FatBugCheck( 0, 0, 0);
}
//
// Initialize the DirentBcb to null
//
DirentBcb = NULL;
//
// Initialize our temp strings with their stack buffers.
//
OemFinalName.Length = 0;
OemFinalName.MaximumLength = sizeof( OemBuffer);
OemFinalName.Buffer = (PCHAR)OemBuffer;
UpcasedFinalName.Length = 0;
UpcasedFinalName.MaximumLength = sizeof( UpcasedBuffer);
UpcasedFinalName.Buffer = UpcasedBuffer;
Lfn.Length = 0;
Lfn.MaximumLength = sizeof( LfnBuffer);
Lfn.Buffer = LfnBuffer;
try {
//
// Make sure the vcb is in a usable condition. This will raise
// and error condition if the volume is unusable
//
FatVerifyVcb( IrpContext, Vcb );
//
// If the Vcb is locked then we cannot open another file
//
if (FlagOn(Vcb->VcbState, VCB_STATE_FLAG_LOCKED)) {
DebugTrace(0, Dbg, "Volume is locked\n", 0);
Status = STATUS_ACCESS_DENIED;
if (Vcb->VcbCondition != VcbGood) {
Status = STATUS_VOLUME_DISMOUNTED;
}
try_return( Iosb.Status = Status );
}
//
// Don't allow the DELETE_ON_CLOSE option if the volume is
// write-protected.
//
if (DeleteOnClose && FlagOn(Vcb->VcbState, VCB_STATE_FLAG_WRITE_PROTECTED)) {
//
// Set the real device for the pop-up info, and set the verify
// bit in the device object, so that we will force a verify
// in case the user put the correct media back in.
//
IoSetHardErrorOrVerifyDevice( IrpContext->OriginatingIrp,
Vcb->Vpb->RealDevice );
SetFlag(Vcb->Vpb->RealDevice->Flags, DO_VERIFY_VOLUME);
FatRaiseStatus( IrpContext, STATUS_MEDIA_WRITE_PROTECTED );
}
//
// If this is a fat32 volume, EA's are not supported.
//
if (EaBuffer != NULL) {
try_return( Iosb.Status = STATUS_EAS_NOT_SUPPORTED );
}
//
// Check if we are opening the volume and not a file/directory.
// We are opening the volume if the name is empty and there
// isn't a related file object. If there is a related file object
// then it is the Vcb itself.
//
if (FileName.Length == 0) {
PVCB DecodeVcb = NULL;
if (RelatedFileObject == NULL ||
FatDecodeFileObject( RelatedFileObject,
&DecodeVcb,
&Fcb,
&Ccb ) == UserVolumeOpen) {
NT_ASSERT( RelatedFileObject == NULL || Vcb == DecodeVcb );
//
// Check if we were to open a directory
//
if (DirectoryFile) {
DebugTrace(0, Dbg, "Cannot open volume as a directory\n", 0);
try_return( Iosb.Status = STATUS_NOT_A_DIRECTORY );
}
//
// Can't open the TargetDirectory of the DASD volume.
//
if (OpenTargetDirectory) {
try_return( Iosb.Status = STATUS_INVALID_PARAMETER );
}
DebugTrace(0, Dbg, "Opening the volume, Vcb = %p\n", Vcb);
CollectCreateHitStatistics(Vcb);
Iosb = FatOpenVolume( IrpContext,
FileObject,
Vcb,
DesiredAccess,
ShareAccess,
CreateDisposition );
Irp->IoStatus.Information = Iosb.Information;
try_return( Iosb.Status );
}
}
//
// If there is a related file object then this is a relative open.
// The related file object is the directory to start our search at.
// Return an error if it is not a directory.
//
if (RelatedFileObject != NULL) {
PVCB RelatedVcb;
PDCB RelatedDcb;
PCCB RelatedCcb;
TYPE_OF_OPEN TypeOfOpen;
TypeOfOpen = FatDecodeFileObject( RelatedFileObject,
&RelatedVcb,
&RelatedDcb,
&RelatedCcb );
if (TypeOfOpen != UserFileOpen &&
TypeOfOpen != UserDirectoryOpen) {
DebugTrace(0, Dbg, "Invalid related file object\n", 0);
try_return( Iosb.Status = STATUS_OBJECT_PATH_NOT_FOUND );
}
//
// A relative open must be via a relative path.
//
if (FileName.Length != 0 &&
FileName.Buffer[0] == L'\\') {
try_return( Iosb.Status = STATUS_OBJECT_NAME_INVALID );
}
//
// Set up the file object's Vpb pointer in case anything happens.
//
NT_ASSERT( Vcb == RelatedVcb );
FileObject->Vpb = RelatedFileObject->Vpb;
//
// Now verify the related Fcb so we don't get in trouble later
// by assuming its in good shape.
//
FatVerifyFcb( IrpContext, RelatedDcb );
ParentDcb = RelatedDcb;
} else {
//
// This is not a relative open, so check if we're
// opening the root dcb
//
if ((FileName.Length == sizeof(WCHAR)) &&
(FileName.Buffer[0] == L'\\')) {
//
// Check if we were not supposed to open a directory
//
if (NonDirectoryFile) {
DebugTrace(0, Dbg, "Cannot open root directory as a file\n", 0);
try_return( Iosb.Status = STATUS_FILE_IS_A_DIRECTORY );
}
//
// Can't open the TargetDirectory of the root directory.
//
if (OpenTargetDirectory) {
try_return( Iosb.Status = STATUS_INVALID_PARAMETER );
}
//
// Not allowed to delete root directory.
//
if (DeleteOnClose) {
try_return( Iosb.Status = STATUS_CANNOT_DELETE );
}
DebugTrace(0, Dbg, "Opening root dcb\n", 0);
CollectCreateHitStatistics(Vcb);
Iosb = FatOpenRootDcb( IrpContext,
FileObject,
Vcb,
DesiredAccess,
ShareAccess,
CreateDisposition );
Irp->IoStatus.Information = Iosb.Information;
try_return( Iosb.Status );
}
//
// Nope, we will be opening relative to the root directory.
//
ParentDcb = Vcb->RootDcb;
//
// Now verify the root Dcb so we don't get in trouble later
// by assuming its in good shape.
//
FatVerifyFcb( IrpContext, ParentDcb );
}
//
// FatCommonCreate(): trailing backslash check
//
if ((FileName.Length != 0) &&
(FileName.Buffer[FileName.Length/sizeof(WCHAR)-1] == L'\\')) {
FileName.Length -= sizeof(WCHAR);
TrailingBackslash = TRUE;
} else {
TrailingBackslash = FALSE;
}
//
// Check for max path. We might want to tighten this down to DOS MAX_PATH
// for maximal interchange with non-NT platforms, but for now defer to the
// possibility of something depending on it.
//
if (ParentDcb->FullFileName.Buffer == NULL) {
FatSetFullFileNameInFcb( IrpContext, ParentDcb );
}
if ((USHORT) (ParentDcb->FullFileName.Length + sizeof(WCHAR) + FileName.Length) <= FileName.Length) {
try_return( Iosb.Status = STATUS_OBJECT_NAME_INVALID );
}
//
// We loop here until we land on an Fcb that is in a good
// condition. This way we can reopen files that have stale handles
// to files of the same name but are now different.
//
while ( TRUE ) {
Fcb = ParentDcb;
RemainingPart = FileName;
//
// Now walk down the Dcb tree looking for the longest prefix.
// This one exit condition in the while() is to handle a
// special case condition (relative NULL name open), the main
// exit conditions are at the bottom of the loop.
//
while (RemainingPart.Length != 0) {
PFCB NextFcb;
FsRtlDissectName( RemainingPart,
&FinalName,
&NextRemainingPart );
//
// If RemainingPart starts with a backslash the name is
// invalid.
// Check for no more than 255 characters in FinalName
//
if (((NextRemainingPart.Length != 0) && (NextRemainingPart.Buffer[0] == L'\\')) ||
(FinalName.Length > 255*sizeof(WCHAR))) {
try_return( Iosb.Status = STATUS_OBJECT_NAME_INVALID );
}
//
// Now, try to convert this one component into Oem and search
// the splay tree. If it works then that's great, otherwise
// we have to try with the UNICODE name instead.
//
FatEnsureStringBufferEnough( &OemFinalName,
FinalName.Length);
Status = RtlUpcaseUnicodeStringToCountedOemString( &OemFinalName, &FinalName, FALSE );
if (NT_SUCCESS(Status)) {
NextFcb = FatFindFcb( IrpContext,
&Fcb->Specific.Dcb.RootOemNode,
(PSTRING)&OemFinalName,
&FileNameOpenedDos );
} else {
NextFcb = NULL;
OemFinalName.Length = 0;
if (Status != STATUS_UNMAPPABLE_CHARACTER) {
try_return( Iosb.Status = Status );
}
}
//
// If we didn't find anything searching the Oem space, we
// have to try the Unicode space. To save cycles in the
// common case that this tree is empty, we do a quick check
// here.
//
if ((NextFcb == NULL) && Fcb->Specific.Dcb.RootUnicodeNode) {
//
// First downcase, then upcase the string, because this
// is what happens when putting names into the tree (see
// strucsup.c, FatConstructNamesInFcb()).
//
FatEnsureStringBufferEnough( &UpcasedFinalName,
FinalName.Length);
Status = RtlDowncaseUnicodeString(&UpcasedFinalName, &FinalName, FALSE );
NT_ASSERT( NT_SUCCESS( Status ));
Status = RtlUpcaseUnicodeString( &UpcasedFinalName, &UpcasedFinalName, FALSE );
NT_ASSERT( NT_SUCCESS( Status ));
NextFcb = FatFindFcb( IrpContext,
&Fcb->Specific.Dcb.RootUnicodeNode,
(PSTRING)&UpcasedFinalName,
&FileNameOpenedDos );
}
//
// If we got back an Fcb then we consumed the FinalName
// legitimately, so the remaining name is now RemainingPart.
//
if (NextFcb != NULL) {
Fcb = NextFcb;
RemainingPart = NextRemainingPart;
}
if ((NextFcb == NULL) ||
(NodeType(NextFcb) == FAT_NTC_FCB) ||
(NextRemainingPart.Length == 0)) {
break;
}
}
//
// Remaining name cannot start with a backslash
//
if (RemainingPart.Length && (RemainingPart.Buffer[0] == L'\\')) {
RemainingPart.Length -= sizeof(WCHAR);
RemainingPart.Buffer += 1;
}
//
// Now verify that everybody up to the longest found prefix is valid.
//
try {
FatVerifyFcb( IrpContext, Fcb );
} except( (GetExceptionCode() == STATUS_FILE_INVALID) ?
EXCEPTION_EXECUTE_HANDLER :
EXCEPTION_CONTINUE_SEARCH ) {
FatResetExceptionState( IrpContext );
}
if ( Fcb->FcbCondition == FcbGood ) {
//
// If we are trying to open a paging file and have happened
// upon the DelayedCloseFcb, make it go away, and try again.
//
if (IsPagingFile && FirstLoop &&
(NodeType(Fcb) == FAT_NTC_FCB) &&
(!IsListEmpty( &FatData.AsyncCloseList ) ||
!IsListEmpty( &FatData.DelayedCloseList ))) {
FatFspClose(Vcb);
FirstLoop = FALSE;
continue;
} else {
break;
}
} else {
FatRemoveNames( IrpContext, Fcb );
}
}
NT_ASSERT( Fcb->FcbCondition == FcbGood );
//
// If there is already an Fcb for a paging file open and
// it was not already opened as a paging file, we cannot
// continue as it is too difficult to move a live Fcb to
// non-paged pool.
//
if (IsPagingFile) {
if (NodeType(Fcb) == FAT_NTC_FCB &&
!FlagOn( Fcb->FcbState, FCB_STATE_PAGING_FILE )) {
try_return( Iosb.Status = STATUS_SHARING_VIOLATION );
}
//
// Check for a system file.
//
} else if (FlagOn( Fcb->FcbState, FCB_STATE_SYSTEM_FILE )) {
try_return( Iosb.Status = STATUS_ACCESS_DENIED );
}
//
// If the longest prefix is pending delete (either the file or
// some higher level directory), we cannot continue.
//
if (FlagOn( Fcb->FcbState, FCB_STATE_DELETE_ON_CLOSE )) {
try_return( Iosb.Status = STATUS_DELETE_PENDING );
}
//
// Now that we've found the longest matching prefix we'll
// check if there isn't any remaining part because that means
// we've located an existing fcb/dcb to open and we can do the open
// without going to the disk
//
if (RemainingPart.Length == 0) {
//
// First check if the user wanted to open the target directory
// and if so then call the subroutine to finish the open.
//
if (OpenTargetDirectory) {
CollectCreateHitStatistics(Vcb);
Iosb = FatOpenTargetDirectory( IrpContext,
FileObject,
Fcb->ParentDcb,
DesiredAccess,
ShareAccess,
TRUE,
FileNameOpenedDos );
Irp->IoStatus.Information = Iosb.Information;
try_return( Iosb.Status );
}
//
// We can open an existing fcb/dcb, now we only need to case
// on which type to open.
//
if (NodeType(Fcb) == FAT_NTC_DCB || NodeType(Fcb) == FAT_NTC_ROOT_DCB) {
//
// This is a directory we're opening up so check if
// we were not to open a directory
//
if (NonDirectoryFile) {
DebugTrace(0, Dbg, "Cannot open directory as a file\n", 0);
try_return( Iosb.Status = STATUS_FILE_IS_A_DIRECTORY );
}
DebugTrace(0, Dbg, "Open existing dcb, Dcb = %p\n", Fcb);
CollectCreateHitStatistics(Vcb);
Iosb = FatOpenExistingDcb( IrpContext,
IrpSp,
FileObject,
Vcb,
(PDCB)Fcb,
DesiredAccess,
ShareAccess,
CreateDisposition,
NoEaKnowledge,
DeleteOnClose,
OpenRequiringOplock,
FileNameOpenedDos,
&OplockPostIrp );
if (Iosb.Status != STATUS_PENDING) {
Irp->IoStatus.Information = Iosb.Information;
} else {
DebugTrace(0, Dbg, "Enqueue Irp to FSP\n", 0);
PostIrp = TRUE;
}
try_return( Iosb.Status );
}
//
// Check if we're trying to open an existing Fcb and that
// the user didn't want to open a directory. Note that this
// call might actually come back with status_pending because
// the user wanted to supersede or overwrite the file and we
// cannot block. If it is pending then we do not complete the
// request, and we fall through the bottom to the code that
// dispatches the request to the fsp.
//
if (NodeType(Fcb) == FAT_NTC_FCB) {
//
// Check if we were only to open a directory
//
if (OpenDirectory) {
DebugTrace(0, Dbg, "Cannot open file as directory\n", 0);
try_return( Iosb.Status = STATUS_NOT_A_DIRECTORY );
}
DebugTrace(0, Dbg, "Open existing fcb, Fcb = %p\n", Fcb);
if ( TrailingBackslash ) {
try_return( Iosb.Status = STATUS_OBJECT_NAME_INVALID );
}
CollectCreateHitStatistics(Vcb);
Iosb = FatOpenExistingFcb( IrpContext,
IrpSp,
FileObject,
Vcb,
Fcb,
DesiredAccess,
ShareAccess,
AllocationSize,
EaBuffer,
EaLength,
FileAttributes,
CreateDisposition,
NoEaKnowledge,
DeleteOnClose,
OpenRequiringOplock,
FileNameOpenedDos,
&OplockPostIrp );
if (Iosb.Status != STATUS_PENDING) {
//
// Check if we need to set the cache support flag in
// the file object
//
if (NT_SUCCESS( Iosb.Status) && !NoIntermediateBuffering) {
FileObject->Flags |= FO_CACHE_SUPPORTED;
}
Irp->IoStatus.Information = Iosb.Information;
} else {
DebugTrace(0, Dbg, "Enqueue Irp to FSP\n", 0);
PostIrp = TRUE;
}
try_return( Iosb.Status );
}
//
// Not and Fcb or a Dcb so we bug check
//
#pragma prefast( suppress:28159, "this is a serious corruption if it happens" )
FatBugCheck( NodeType(Fcb), (ULONG_PTR) Fcb, 0 );
}
//
// There is more in the name to parse than we have in existing
// fcbs/dcbs. So now make sure that fcb we got for the largest
// matching prefix is really a dcb otherwise we can't go any
// further
//
if ((NodeType(Fcb) != FAT_NTC_DCB) && (NodeType(Fcb) != FAT_NTC_ROOT_DCB)) {
DebugTrace(0, Dbg, "Cannot open file as subdirectory, Fcb = %p\n", Fcb);
try_return( Iosb.Status = STATUS_OBJECT_PATH_NOT_FOUND );
}
//
// Otherwise we continue on processing the Irp and allowing ourselves
// to block for I/O as necessary. Find/create additional dcb's for
// the one we're trying to open. We loop until either remaining part
// is empty or we get a bad filename. When we exit FinalName is
// the last name in the string we're after, and ParentDcb is the
// parent directory that will contain the opened/created
// file/directory.
//
// Make sure the rest of the name is valid in at least the LFN
// character set (which just happens to be that of HPFS).
//
// If we are not in ChicagoMode, use FAT semantics.
//
ParentDcb = Fcb;
FirstLoop = TRUE;
while (TRUE) {
//
// We do one little optimization here on the first iteration of
// the loop since we know that we have already tried to convert
// FinalOemName from the original UNICODE.
//
if (FirstLoop) {
FirstLoop = FALSE;
RemainingPart = NextRemainingPart;
Status = OemFinalName.Length ? STATUS_SUCCESS : STATUS_UNMAPPABLE_CHARACTER;
} else {
//
// Dissect the remaining part.
//
DebugTrace(0, Dbg, "Dissecting the name %wZ\n", &RemainingPart);
FsRtlDissectName( RemainingPart,
&FinalName,
&RemainingPart );
//
// If RemainingPart starts with a backslash the name is
// invalid.
// Check for no more than 255 characters in FinalName
//
if (((RemainingPart.Length != 0) && (RemainingPart.Buffer[0] == L'\\')) ||
(FinalName.Length > 255*sizeof(WCHAR))) {
try_return( Iosb.Status = STATUS_OBJECT_NAME_INVALID );
}
//
// Now, try to convert this one component into Oem. If it works
// then that's great, otherwise we have to try with the UNICODE
// name instead.
//
FatEnsureStringBufferEnough( &OemFinalName,
FinalName.Length);
Status = RtlUpcaseUnicodeStringToCountedOemString( &OemFinalName, &FinalName, FALSE );
}
if (NT_SUCCESS(Status)) {
//
// We'll start by trying to locate the dirent for the name. Note
// that we already know that there isn't an Fcb/Dcb for the file
// otherwise we would have found it when we did our prefix lookup.
//
if (FatIsNameShortOemValid( IrpContext, OemFinalName, FALSE, FALSE, FALSE )) {
FatStringTo8dot3( IrpContext,
OemFinalName,
&LocalCcb.OemQueryTemplate.Constant );
LocalCcb.Flags = 0;
} else {
LocalCcb.Flags = CCB_FLAG_SKIP_SHORT_NAME_COMPARE;
}
} else {
LocalCcb.Flags = CCB_FLAG_SKIP_SHORT_NAME_COMPARE;
if (Status != STATUS_UNMAPPABLE_CHARACTER) {
try_return( Iosb.Status = Status );
}
}
//
// Now we know a lot about the final name, so do legal name
// checking here.
//
if (FatData.ChicagoMode) {
if (!FatIsNameLongUnicodeValid( IrpContext, &FinalName, FALSE, FALSE, FALSE )) {
try_return( Iosb.Status = STATUS_OBJECT_NAME_INVALID );
}
} else {
if (FlagOn(LocalCcb.Flags, CCB_FLAG_SKIP_SHORT_NAME_COMPARE)) {
try_return( Iosb.Status = STATUS_OBJECT_NAME_INVALID );
}
}
DebugTrace(0, Dbg, "FinalName is %wZ\n", &FinalName);
DebugTrace(0, Dbg, "RemainingPart is %wZ\n", &RemainingPart);
FatEnsureStringBufferEnough( &UpcasedFinalName,
FinalName.Length);
if (!NT_SUCCESS(Status = RtlUpcaseUnicodeString( &UpcasedFinalName, &FinalName, FALSE))) {
try_return( Iosb.Status = Status );
}
LocalCcb.UnicodeQueryTemplate = UpcasedFinalName;
LocalCcb.ContainsWildCards = FALSE;
Lfn.Length = 0;
FatLocateDirent( IrpContext,
ParentDcb,
&LocalCcb,
0,
&MatchFlags,
&Dirent,
&DirentBcb,
(PVBO)&DirentByteOffset,
&FileNameOpenedDos,
&Lfn,
&OrigLfn);
//
// Remember we read this Dcb for error recovery.
//
FinalDcb = ParentDcb;
//
// If the remaining part is now empty then this is the last name
// in the string and the one we want to open
//
if (RemainingPart.Length == 0) {
break;
}
//
// We didn't find a dirent, bail.
//
if (Dirent == NULL) {
Iosb.Status = STATUS_OBJECT_PATH_NOT_FOUND;
try_return( Iosb.Status );
}
//
// We now have a dirent, make sure it is a directory
//
if (!FlagOn( Dirent->Attributes, FAT_DIRENT_ATTR_DIRECTORY )) {
Iosb.Status = STATUS_OBJECT_PATH_NOT_FOUND;
try_return( Iosb.Status );
}
//
// Compute the LfnByteOffset.
//
LfnByteOffset = DirentByteOffset -
FAT_LFN_DIRENTS_NEEDED(&OrigLfn) * sizeof(LFN_DIRENT);
//
// Create a dcb for the new directory
//
ParentDcb = FatCreateDcb( IrpContext,
Vcb,
ParentDcb,
LfnByteOffset,
DirentByteOffset,
Dirent,
&Lfn );
//
// Remember we created this Dcb for error recovery.
//
FinalDcb = ParentDcb;
FatSetFullNameInFcb( IrpContext, ParentDcb, &FinalName );
}
//
// First check if the user wanted to open the target directory
// and if so then call the subroutine to finish the open.
//
if (OpenTargetDirectory) {
Iosb = FatOpenTargetDirectory( IrpContext,
FileObject,
ParentDcb,
DesiredAccess,
ShareAccess,
Dirent ? TRUE : FALSE,
FileNameOpenedDos);
Irp->IoStatus.Information = Iosb.Information;
try_return( Iosb.Status );
}
if (Dirent != NULL) {
//
// Compute the LfnByteOffset.
//
LfnByteOffset = DirentByteOffset -
FAT_LFN_DIRENTS_NEEDED(&OrigLfn) * sizeof(LFN_DIRENT);
//
// We were able to locate an existing dirent entry, so now
// see if it is a directory that we're trying to open.
//
if (FlagOn( Dirent->Attributes, FAT_DIRENT_ATTR_DIRECTORY )) {
//
// Make sure its okay to open a directory
//
if (NonDirectoryFile) {
DebugTrace(0, Dbg, "Cannot open directory as a file\n", 0);
try_return( Iosb.Status = STATUS_FILE_IS_A_DIRECTORY );
}
DebugTrace(0, Dbg, "Open existing directory\n", 0);
Iosb = FatOpenExistingDirectory( IrpContext,
IrpSp,
FileObject,
Vcb,
&Fcb,
ParentDcb,
Dirent,
LfnByteOffset,
DirentByteOffset,
&Lfn,
DesiredAccess,
ShareAccess,
CreateDisposition,
NoEaKnowledge,
DeleteOnClose,
FileNameOpenedDos,
OpenRequiringOplock );
Irp->IoStatus.Information = Iosb.Information;
try_return( Iosb.Status );
}
//
// Otherwise we're trying to open and existing file, and we
// need to check if the user only wanted to open a directory.
//
if (OpenDirectory) {
DebugTrace(0, Dbg, "Cannot open file as directory\n", 0);
try_return( Iosb.Status = STATUS_NOT_A_DIRECTORY );
}
DebugTrace(0, Dbg, "Open existing file\n", 0);
if ( TrailingBackslash ) {
try_return( Iosb.Status = STATUS_OBJECT_NAME_INVALID );
}
Iosb = FatOpenExistingFile( IrpContext,
FileObject,
Vcb,
&Fcb,
ParentDcb,
Dirent,
LfnByteOffset,
DirentByteOffset,
&Lfn,
&OrigLfn,
DesiredAccess,
ShareAccess,
AllocationSize,
EaBuffer,
EaLength,
FileAttributes,
CreateDisposition,
IsPagingFile,
NoEaKnowledge,
DeleteOnClose,
OpenRequiringOplock,
FileNameOpenedDos );
//
// Check if we need to set the cache support flag in
// the file object
//
if (NT_SUCCESS(Iosb.Status) && !NoIntermediateBuffering) {
FileObject->Flags |= FO_CACHE_SUPPORTED;
}
Irp->IoStatus.Information = Iosb.Information;
try_return( Iosb.Status );
}
//
// We can't locate a dirent so this is a new file.
//
//
// Now check to see if we wanted to only open an existing file.
// And then case on whether we wanted to create a file or a directory.
//
if ((CreateDisposition == FILE_OPEN) ||
(CreateDisposition == FILE_OVERWRITE)) {
DebugTrace( 0, Dbg, "Cannot open nonexisting file\n", 0);
try_return( Iosb.Status = STATUS_OBJECT_NAME_NOT_FOUND );
}
//
// Skip a few cycles later if we know now that the Oem name is not
// valid 8.3.
//
if (FlagOn(LocalCcb.Flags, CCB_FLAG_SKIP_SHORT_NAME_COMPARE)) {
OemFinalName.Length = 0;
}
//
// Determine the granted access for this operation now.
//
if (!NT_SUCCESS( Iosb.Status = FatCheckSystemSecurityAccess( IrpContext ))) {
try_return( Iosb );
}
if (CreateDirectory) {
DebugTrace(0, Dbg, "Create new directory\n", 0);
//
// If this media is write protected, don't even try the create.
//
if (FlagOn(Vcb->VcbState, VCB_STATE_FLAG_WRITE_PROTECTED)) {
//
// Set the real device for the pop-up info, and set the verify
// bit in the device object, so that we will force a verify
// in case the user put the correct media back in.
//
IoSetHardErrorOrVerifyDevice( IrpContext->OriginatingIrp,
Vcb->Vpb->RealDevice );
SetFlag(Vcb->Vpb->RealDevice->Flags, DO_VERIFY_VOLUME);
FatRaiseStatus( IrpContext, STATUS_MEDIA_WRITE_PROTECTED );
}
//
// Don't allow people to create directories with the
// temporary bit set.
//
if (TemporaryFile) {
try_return( Iosb.Status = STATUS_INVALID_PARAMETER );
}
Iosb = FatCreateNewDirectory( IrpContext,
IrpSp,
FileObject,
Vcb,
ParentDcb,
&OemFinalName,
&FinalName,
DesiredAccess,
ShareAccess,
EaBuffer,
EaLength,
FileAttributes,
NoEaKnowledge,
DeleteOnClose,
OpenRequiringOplock );
Irp->IoStatus.Information = Iosb.Information;
try_return( Iosb.Status );
}
DebugTrace(0, Dbg, "Create new file\n", 0);
if ( TrailingBackslash ) {
try_return( Iosb.Status = STATUS_OBJECT_NAME_INVALID );
}
//
// If this media is write protected, don't even try the create.
//
if (FlagOn(Vcb->VcbState, VCB_STATE_FLAG_WRITE_PROTECTED)) {
//
// Set the real device for the pop-up info, and set the verify
// bit in the device object, so that we will force a verify
// in case the user put the correct media back in.
//
IoSetHardErrorOrVerifyDevice( IrpContext->OriginatingIrp,
Vcb->Vpb->RealDevice );
SetFlag(Vcb->Vpb->RealDevice->Flags, DO_VERIFY_VOLUME);
FatRaiseStatus( IrpContext, STATUS_MEDIA_WRITE_PROTECTED );
}
Iosb = FatCreateNewFile( IrpContext,
IrpSp,
FileObject,
Vcb,
ParentDcb,
&OemFinalName,
&FinalName,
DesiredAccess,
ShareAccess,
AllocationSize,
EaBuffer,
EaLength,
FileAttributes,
&Lfn,
IsPagingFile,
NoEaKnowledge,
DeleteOnClose,
OpenRequiringOplock,
TemporaryFile );
//
// Check if we need to set the cache support flag in
// the file object
//
if (NT_SUCCESS(Iosb.Status) && !NoIntermediateBuffering) {
FileObject->Flags |= FO_CACHE_SUPPORTED;
}
Irp->IoStatus.Information = Iosb.Information;
try_exit: NOTHING;
//
// This is a Beta Fix. Do this at a better place later.
//
if (NT_SUCCESS(Iosb.Status) && !OpenTargetDirectory) {
PFCB LocalFcb;
//
// If there is an Fcb/Dcb, set the long file name.
//
LocalFcb = FileObject->FsContext;
if (LocalFcb &&
((NodeType(LocalFcb) == FAT_NTC_FCB) ||
(NodeType(LocalFcb) == FAT_NTC_DCB)) &&
(LocalFcb->FullFileName.Buffer == NULL)) {
FatSetFullNameInFcb( IrpContext, LocalFcb, &FinalName );
}
}
} finally {
DebugUnwind( FatCommonCreate );
#if (NTDDI_VERSION >= NTDDI_WIN7)
//
// If we're not getting out with success, and if the caller wanted
// atomic create-with-oplock semantics make sure we back out any
// oplock that may have been granted.
//
if ((AbnormalTermination() ||
!NT_SUCCESS( Iosb.Status )) &&
OpenRequiringOplock &&
(Iosb.Status != STATUS_CANNOT_BREAK_OPLOCK) &&
(IrpContext->ExceptionStatus != STATUS_CANNOT_BREAK_OPLOCK) &&
(Fcb != NULL) &&
FatIsFileOplockable( Fcb )) {
FsRtlCheckOplockEx( FatGetFcbOplock(Fcb),
IrpContext->OriginatingIrp,
OPLOCK_FLAG_BACK_OUT_ATOMIC_OPLOCK,
NULL,
NULL,
NULL );
}
#endif
//
// There used to be a test here - the ASSERT replaces it. We will
// never have begun enumerating directories if we post the IRP for
// oplock reasons.
//
NT_ASSERT( !OplockPostIrp || DirentBcb == NULL );
FatUnpinBcb( IrpContext, DirentBcb );
//
// If we are in an error path, check for any created subdir Dcbs that
// have to be unwound. Don't whack the root directory.
//
// Note this will leave a branch of Dcbs dangling if the directory file
// had not been built on the leaf (case: opening path which has an
// element containing an invalid character name).
//
if (AbnormalTermination() || !NT_SUCCESS(Iosb.Status)) {
ULONG SavedFlags;
//
// Before doing the uninitialize, we have to unpin anything
// that has been repinned, but disable writethrough first. We
// disable raise from unpin-repin since we're already failing.
//
SavedFlags = IrpContext->Flags;
SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_DISABLE_RAISE |
IRP_CONTEXT_FLAG_DISABLE_WRITE_THROUGH );
FatUnpinRepinnedBcbs( IrpContext );
if ((FinalDcb != NULL) &&
(NodeType(FinalDcb) == FAT_NTC_DCB) &&
IsListEmpty(&FinalDcb->Specific.Dcb.ParentDcbQueue) &&
(FinalDcb->OpenCount == 0) &&
(FinalDcb->Specific.Dcb.DirectoryFile != NULL)) {
PFILE_OBJECT DirectoryFileObject;
DirectoryFileObject = FinalDcb->Specific.Dcb.DirectoryFile;
FinalDcb->Specific.Dcb.DirectoryFile = NULL;
CcUninitializeCacheMap( DirectoryFileObject, NULL, NULL );
ObDereferenceObject( DirectoryFileObject );
}
IrpContext->Flags = SavedFlags;
}
if (AbnormalTermination()) {
FatReleaseVcb( IrpContext, Vcb );
}
//
// Free up any string buffers we allocated
//
FatFreeStringBuffer( &OemFinalName);
FatFreeStringBuffer( &UpcasedFinalName);
FatFreeStringBuffer( &Lfn);
}
//
// The following code is only executed if we are exiting the
// procedure through a normal termination. We complete the request
// and if for any reason that bombs out then we need to unreference
// and possibly delete the fcb and ccb.
//
try {
if (PostIrp) {
//
// If the Irp hasn't already been posted, do it now.
//
if (!OplockPostIrp) {
Iosb.Status = FatFsdPostRequest( IrpContext, Irp );
}
} else {
FatUnpinRepinnedBcbs( IrpContext );
}
} finally {
DebugUnwind( FatCommonCreate-in-FatCompleteRequest );
if (AbnormalTermination() ) {
PVCB LocalVcb;
PFCB LocalFcb;
PCCB LocalCcb2;
PFILE_OBJECT DirectoryFileObject;
//
// Unwind all of our counts. Note that if a write failed, then
// the volume has been marked for verify, and all volume
// structures will be cleaned up automatically.
//
(VOID) FatDecodeFileObject( FileObject, &LocalVcb, &LocalFcb, &LocalCcb2 );
LocalFcb->UncleanCount -= 1;
LocalFcb->OpenCount -= 1;
LocalVcb->OpenFileCount -= 1;
if (IsFileObjectReadOnly(FileObject)) { LocalVcb->ReadOnlyCount -= 1; }
//
// WinSE #307418 "Occasional data corruption when standby/resume
// while copying files to removable FAT formatted media".
// If new file creation request was interrupted by system suspend
// operation we should revert the changes we made to the parent
// directory and to the allocation table.
//
if (IrpContext->ExceptionStatus == STATUS_VERIFY_REQUIRED &&
NodeType( LocalFcb ) == FAT_NTC_FCB) {
FatTruncateFileAllocation( IrpContext, LocalFcb, 0 );
FatDeleteDirent( IrpContext, LocalFcb, NULL, TRUE );
}
//
// If we leafed out on a new Fcb we should get rid of it at this point.
//
// Since the object isn't being opened, we have to do all of the teardown
// here. Our close path will not occur for this fileobject. Note this
// will leave a branch of Dcbs dangling since we do it by hand and don't
// chase to the root.
//
if (LocalFcb->OpenCount == 0 &&
(NodeType( LocalFcb ) == FAT_NTC_FCB ||
IsListEmpty(&LocalFcb->Specific.Dcb.ParentDcbQueue))) {
NT_ASSERT( NodeType( LocalFcb ) != FAT_NTC_ROOT_DCB );
if ( (NodeType( LocalFcb ) == FAT_NTC_DCB) &&
(LocalFcb->Specific.Dcb.DirectoryFile != NULL) ) {
DirectoryFileObject = LocalFcb->Specific.Dcb.DirectoryFile;
LocalFcb->Specific.Dcb.DirectoryFile = NULL;
CcUninitializeCacheMap( DirectoryFileObject,
&FatLargeZero,
NULL );
ObDereferenceObject( DirectoryFileObject );
} else {
if (ARGUMENT_PRESENT( FileObject )) {
FileObject->SectionObjectPointer = NULL;
}
FatDeleteFcb( IrpContext, &LocalFcb );
}
}
FatDeleteCcb( IrpContext, &LocalCcb2 );
FatReleaseVcb( IrpContext, LocalVcb );
} else {
FatReleaseVcb( IrpContext, Vcb );
if ( !PostIrp ) {
//
// If this request is successful and the file was opened
// for FILE_EXECUTE access, then set the FileObject bit.
//
NT_ASSERT( IrpSp->Parameters.Create.SecurityContext != NULL );
if (FlagOn( *DesiredAccess, FILE_EXECUTE )) {
SetFlag( FileObject->Flags, FO_FILE_FAST_IO_READ );
}
//
// Lock volume in drive if we opened a paging file, allocating a
// reserve MDL to guarantee paging file operations can always
// go forward.
//
if (IsPagingFile && NT_SUCCESS(Iosb.Status)) {
#pragma prefast( suppress:28112, "this should be safe" )
if (!FatReserveMdl) {
PMDL ReserveMdl = IoAllocateMdl( NULL,
FAT_RESERVE_MDL_SIZE * PAGE_SIZE,
TRUE,
FALSE,
NULL );
//
// Stash the MDL, and if it turned out there was already one there
// just free what we got.
//
InterlockedCompareExchangePointer( &FatReserveMdl, ReserveMdl, NULL );
#pragma prefast( suppress:28112, "this should be safe" )
if (FatReserveMdl != ReserveMdl) {
IoFreeMdl( ReserveMdl );
}
}
SetFlag(Vcb->VcbState, VCB_STATE_FLAG_BOOT_OR_PAGING_FILE);
if (FlagOn(Vcb->VcbState, VCB_STATE_FLAG_REMOVABLE_MEDIA)) {
FatToggleMediaEjectDisable( IrpContext, Vcb, TRUE );
}
}
}
}
DebugTrace(-1, Dbg, "FatCommonCreate -> %08lx\n", Iosb.Status);
}
CollectCreateStatistics(Vcb, Iosb.Status);
return Iosb.Status;
}
//
// Internal support routine
//
_Requires_lock_held_(_Global_critical_region_)
IO_STATUS_BLOCK
FatOpenVolume (
_In_ PIRP_CONTEXT IrpContext,
_Inout_ PFILE_OBJECT FileObject,
_Inout_ PVCB Vcb,
_In_ PACCESS_MASK DesiredAccess,
_In_ USHORT ShareAccess,
_In_ ULONG CreateDisposition
)
/*++
Routine Description:
This routine opens the specified volume for DASD access
Arguments:
FileObject - Supplies the File object
Vcb - Supplies the Vcb denoting the volume being opened
DesiredAccess - Supplies the desired access of the caller
ShareAccess - Supplies the share access of the caller
CreateDisposition - Supplies the create disposition for this operation
Return Value:
IO_STATUS_BLOCK - Returns the completion status for the operation
--*/
{
NTSTATUS Status;
PIO_STACK_LOCATION IrpSp;
IO_STATUS_BLOCK Iosb = {0,0};
BOOLEAN CleanedVolume = FALSE;
//
// The following variables are for abnormal termination
//
BOOLEAN UnwindShareAccess = FALSE;
PCCB UnwindCcb = NULL;
BOOLEAN UnwindCounts = FALSE;
BOOLEAN UnwindVolumeLock = FALSE;
PAGED_CODE();
DebugTrace(+1, Dbg, "FatOpenVolume...\n", 0);
try {
//
// Check for proper desired access and rights
//
if ((CreateDisposition != FILE_OPEN) &&
(CreateDisposition != FILE_OPEN_IF)) {
try_return( Iosb.Status = STATUS_ACCESS_DENIED );
}
//
// If the user does not want to share write or delete then we will try
// and take out a lock on the volume.
//
if (!FlagOn(ShareAccess, FILE_SHARE_WRITE) &&
!FlagOn(ShareAccess, FILE_SHARE_DELETE)) {
#if (NTDDI_VERSION >= NTDDI_VISTA)
//
// See if the user has requested write access. If so, they cannot share
// read. There is one exception to this. We allow autochk to get an
// implicit lock on the volume while still allowing readers. Once the
// the system is booted, though, we do not allow this type of access.
//
if (FlagOn( *DesiredAccess, (FILE_WRITE_DATA | FILE_APPEND_DATA) ) &&
FsRtlAreVolumeStartupApplicationsComplete()) {
ClearFlag( ShareAccess, FILE_SHARE_READ );
}
#endif
//
// Do a quick check here for handles on exclusive open.
//
if (!FlagOn(ShareAccess, FILE_SHARE_READ) &&
!FatIsHandleCountZero( IrpContext, Vcb )) {
try_return( Iosb.Status = STATUS_SHARING_VIOLATION );
}
//
// Force Mm to get rid of its referenced file objects.
//
FatFlushFat( IrpContext, Vcb );
FatPurgeReferencedFileObjects( IrpContext, Vcb->RootDcb, Flush );
//
// If the user also does not want to share read then we check
// if anyone is already using the volume, and if so then we
// deny the access. If the user wants to share read then
// we allow the current opens to stay provided they are only
// readonly opens and deny further opens.
//
if (!FlagOn(ShareAccess, FILE_SHARE_READ)) {
if (Vcb->OpenFileCount != 0) {
try_return( Iosb.Status = STATUS_SHARING_VIOLATION );
}
} else {
if (Vcb->ReadOnlyCount != Vcb->OpenFileCount) {
try_return( Iosb.Status = STATUS_SHARING_VIOLATION );
}
}
//
// Lock the volume
//
Vcb->VcbState |= VCB_STATE_FLAG_LOCKED;
Vcb->FileObjectWithVcbLocked = FileObject;
UnwindVolumeLock = TRUE;
//
// Clean the volume
//
CleanedVolume = TRUE;
} else if (FlagOn( *DesiredAccess, FILE_READ_DATA | FILE_WRITE_DATA | FILE_APPEND_DATA )) {
//
// Flush the volume and let ourselves push the clean bit out if everything
// worked.
//
if (NT_SUCCESS( FatFlushVolume( IrpContext, Vcb, Flush ))) {
CleanedVolume = TRUE;
}
}
//
// Clean the volume if we believe it safe and reasonable.
//
if (CleanedVolume &&
FlagOn( Vcb->VcbState, VCB_STATE_FLAG_VOLUME_DIRTY ) &&
!FlagOn( Vcb->VcbState, VCB_STATE_FLAG_MOUNTED_DIRTY ) &&
!CcIsThereDirtyData(Vcb->Vpb)) {
//
// Cancel any pending clean volumes.
//
(VOID)KeCancelTimer( &Vcb->CleanVolumeTimer );
(VOID)KeRemoveQueueDpc( &Vcb->CleanVolumeDpc );
FatMarkVolume( IrpContext, Vcb, VolumeClean );
ClearFlag( Vcb->VcbState, VCB_STATE_FLAG_VOLUME_DIRTY );
//
// Unlock the volume if it is removable.
//
if (FlagOn(Vcb->VcbState, VCB_STATE_FLAG_REMOVABLE_MEDIA) &&
!FlagOn(Vcb->VcbState, VCB_STATE_FLAG_BOOT_OR_PAGING_FILE)) {
FatToggleMediaEjectDisable( IrpContext, Vcb, FALSE );
}
}
//
// If the volume is already opened by someone then we need to check
// the share access
//
if (Vcb->DirectAccessOpenCount > 0) {
if (!NT_SUCCESS(Iosb.Status = IoCheckShareAccess( *DesiredAccess,
ShareAccess,
FileObject,
&Vcb->ShareAccess,
TRUE ))) {
try_return( Iosb.Status );
}
} else {
IoSetShareAccess( *DesiredAccess,
ShareAccess,
FileObject,
&Vcb->ShareAccess );
}
UnwindShareAccess = TRUE;
//
// Set up the context and section object pointers, and update
// our reference counts
//
FatSetFileObject( FileObject,
UserVolumeOpen,
Vcb,
UnwindCcb = FatCreateCcb( IrpContext ));
FileObject->SectionObjectPointer = &Vcb->SectionObjectPointers;
Vcb->DirectAccessOpenCount += 1;
Vcb->OpenFileCount += 1;
if (IsFileObjectReadOnly(FileObject)) { Vcb->ReadOnlyCount += 1; }
UnwindCounts = TRUE;
FileObject->Flags |= FO_NO_INTERMEDIATE_BUFFERING;
//
// At this point the open will succeed, so check if the user is getting explicit access
// to the device. If not, we will note this so we can deny modifying FSCTL to it.
//
IrpSp = IoGetCurrentIrpStackLocation( IrpContext->OriginatingIrp );
Status = FatExplicitDeviceAccessGranted( IrpContext,
Vcb->Vpb->RealDevice,
IrpSp->Parameters.Create.SecurityContext->AccessState,
(KPROCESSOR_MODE)( FlagOn( IrpSp->Flags, SL_FORCE_ACCESS_CHECK ) ?
UserMode :
IrpContext->OriginatingIrp->RequestorMode ));
if (NT_SUCCESS( Status )) {
SetFlag( UnwindCcb->Flags, CCB_FLAG_MANAGE_VOLUME_ACCESS );
}
//
// And set our status to success
//
Iosb.Status = STATUS_SUCCESS;
Iosb.Information = FILE_OPENED;
try_exit: NOTHING;
} finally {
DebugUnwind( FatOpenVolume );
//
// If this is an abnormal termination then undo our work
//
if (AbnormalTermination() || !NT_SUCCESS(Iosb.Status)) {
if (UnwindCounts) {
Vcb->DirectAccessOpenCount -= 1;
Vcb->OpenFileCount -= 1;
if (IsFileObjectReadOnly(FileObject)) { Vcb->ReadOnlyCount -= 1; }
}
if (UnwindCcb != NULL) { FatDeleteCcb( IrpContext, &UnwindCcb ); }
if (UnwindShareAccess) { IoRemoveShareAccess( FileObject, &Vcb->ShareAccess ); }
if (UnwindVolumeLock) { Vcb->VcbState &= ~VCB_STATE_FLAG_LOCKED; }
}
DebugTrace(-1, Dbg, "FatOpenVolume -> Iosb.Status = %08lx\n", Iosb.Status);
}
return Iosb;
}
//
// Internal support routine
//
_Requires_lock_held_(_Global_critical_region_)
IO_STATUS_BLOCK
FatOpenRootDcb (
_In_ PIRP_CONTEXT IrpContext,
_Inout_ PFILE_OBJECT FileObject,
_Inout_ PVCB Vcb,
_In_ PACCESS_MASK DesiredAccess,
_In_ USHORT ShareAccess,
_In_ ULONG CreateDisposition
)
/*++
Routine Description:
This routine opens the root dcb for the volume
Arguments:
FileObject - Supplies the File object
Vcb - Supplies the Vcb denoting the volume whose dcb is being opened.
DesiredAccess - Supplies the desired access of the caller
ShareAccess - Supplies the share access of the caller
CreateDisposition - Supplies the create disposition for this operation
Return Value:
IO_STATUS_BLOCK - Returns the completion status for the operation
Arguments:
--*/
{
PDCB RootDcb;
IO_STATUS_BLOCK Iosb = {0};
//
// The following variables are for abnormal termination
//
BOOLEAN UnwindShareAccess = FALSE;
PCCB UnwindCcb = NULL;
BOOLEAN UnwindCounts = FALSE;
BOOLEAN RootDcbAcquired = FALSE;
PAGED_CODE();
DebugTrace(+1, Dbg, "FatOpenRootDcb...\n", 0);
//
// Locate the root dcb
//
RootDcb = Vcb->RootDcb;
//
// Get the Dcb exlcusive. This is important as cleanup does not
// acquire the Vcb.
//
(VOID)FatAcquireExclusiveFcb( IrpContext, RootDcb );
RootDcbAcquired = TRUE;
try {
//
// Check the create disposition and desired access
//
if ((CreateDisposition != FILE_OPEN) &&
(CreateDisposition != FILE_OPEN_IF)) {
Iosb.Status = STATUS_ACCESS_DENIED;
try_return( Iosb );
}
if (!FatCheckFileAccess( IrpContext,
RootDcb->DirentFatFlags,
DesiredAccess)) {
Iosb.Status = STATUS_ACCESS_DENIED;
try_return( Iosb );
}
//
// If the Root dcb is already opened by someone then we need
// to check the share access
//
if (RootDcb->OpenCount > 0) {
if (!NT_SUCCESS(Iosb.Status = IoCheckShareAccess( *DesiredAccess,
ShareAccess,
FileObject,
&RootDcb->ShareAccess,
TRUE ))) {
try_return( Iosb );
}
} else {
IoSetShareAccess( *DesiredAccess,
ShareAccess,
FileObject,
&RootDcb->ShareAccess );
}
UnwindShareAccess = TRUE;
//
// Setup the context and section object pointers, and update
// our reference counts
//
FatSetFileObject( FileObject,
UserDirectoryOpen,
RootDcb,
UnwindCcb = FatCreateCcb( IrpContext ));
RootDcb->UncleanCount += 1;
RootDcb->OpenCount += 1;
Vcb->OpenFileCount += 1;
if (IsFileObjectReadOnly(FileObject)) { Vcb->ReadOnlyCount += 1; }
UnwindCounts = TRUE;
//
// And set our status to success
//
Iosb.Status = STATUS_SUCCESS;
Iosb.Information = FILE_OPENED;
try_exit: NOTHING;
} finally {
DebugUnwind( FatOpenRootDcb );
//
// If this is an abnormal termination then undo our work
//
if (AbnormalTermination()) {
if (UnwindCounts) {
RootDcb->UncleanCount -= 1;
RootDcb->OpenCount -= 1;
Vcb->OpenFileCount -= 1;
if (IsFileObjectReadOnly(FileObject)) { Vcb->ReadOnlyCount -= 1; }
}
if (UnwindCcb != NULL) { FatDeleteCcb( IrpContext, &UnwindCcb ); }
if (UnwindShareAccess) { IoRemoveShareAccess( FileObject, &RootDcb->ShareAccess ); }
}
if (RootDcbAcquired) {
FatReleaseFcb( IrpContext, RootDcb );
}
DebugTrace(-1, Dbg, "FatOpenRootDcb -> Iosb.Status = %08lx\n", Iosb.Status);
}
return Iosb;
}
//
// Internal support routine
//
_Requires_lock_held_(_Global_critical_region_)
IO_STATUS_BLOCK
FatOpenExistingDcb (
_In_ PIRP_CONTEXT IrpContext,
_In_ PIO_STACK_LOCATION IrpSp,
_Inout_ PFILE_OBJECT FileObject,
_Inout_ PVCB Vcb,
_Inout_ PDCB Dcb,
_In_ PACCESS_MASK DesiredAccess,
_In_ USHORT ShareAccess,
_In_ ULONG CreateDisposition,
_In_ BOOLEAN NoEaKnowledge,
_In_ BOOLEAN DeleteOnClose,
_In_ BOOLEAN OpenRequiringOplock,
_In_ BOOLEAN FileNameOpenedDos,
_Out_ PBOOLEAN OplockPostIrp
)
/*++
Routine Description:
This routine opens the specified existing dcb
Arguments:
FileObject - Supplies the File object
Vcb - Supplies the Vcb denoting the volume containing the dcb
Dcb - Supplies the already existing dcb
DesiredAccess - Supplies the desired access of the caller
ShareAccess - Supplies the share access of the caller
CreateDisposition - Supplies the create disposition for this operation
NoEaKnowledge - This opener doesn't understand Ea's and we fail this
open if the file has NeedEa's.
DeleteOnClose - The caller wants the file gone when the handle is closed
Return Value:
IO_STATUS_BLOCK - Returns the completion status for the operation
--*/
{
IO_STATUS_BLOCK Iosb = {0};
PBCB DirentBcb = NULL;
PDIRENT Dirent;
//
// The following variables are for abnormal termination
//
BOOLEAN UnwindShareAccess = FALSE;
PCCB UnwindCcb = NULL;
BOOLEAN DcbAcquired = FALSE;
#if (NTDDI_VERSION <= NTDDI_WIN7)
UNREFERENCED_PARAMETER( OpenRequiringOplock );
#endif
UNREFERENCED_PARAMETER( IrpSp );
PAGED_CODE();
DebugTrace(+1, Dbg, "FatOpenExistingDcb...\n", 0);
//
// Get the Dcb exlcusive. This is important as cleanup does not
// acquire the Vcb.
//
(VOID)FatAcquireExclusiveFcb( IrpContext, Dcb );
DcbAcquired = TRUE;
try {
*OplockPostIrp = FALSE;
//
// Before spending any noticeable effort, see if we have the odd case
// of someone trying to delete-on-close the root dcb. This will only
// happen if we're hit with a null-filename relative open via the root.
//
if (NodeType(Dcb) == FAT_NTC_ROOT_DCB && DeleteOnClose) {
Iosb.Status = STATUS_CANNOT_DELETE;
try_return( Iosb );
}
#if (NTDDI_VERSION >= NTDDI_WIN8)
//
// Let's make sure that if the caller provided an oplock key that it
// gets stored in the file object.
//
Iosb.Status = FsRtlCheckOplockEx( FatGetFcbOplock(Dcb),
IrpContext->OriginatingIrp,
OPLOCK_FLAG_OPLOCK_KEY_CHECK_ONLY,
NULL,
NULL,
NULL );
if (Iosb.Status != STATUS_SUCCESS) {
try_return( NOTHING );
}
#endif
//
// If the caller has no Ea knowledge, we immediately check for
// Need Ea's on the file. We don't need to check for ea's on the
// root directory, because it never has any. Fat32 doesn't have
// any, either.
//
if (NoEaKnowledge && NodeType(Dcb) != FAT_NTC_ROOT_DCB &&
!FatIsFat32(Vcb)) {
ULONG NeedEaCount;
//
// Get the dirent for the file and then check that the need
// ea count is 0.
//
FatGetDirentFromFcbOrDcb( IrpContext,
Dcb,
FALSE,
&Dirent,
&DirentBcb );
FatGetNeedEaCount( IrpContext,
Vcb,
Dirent,
&NeedEaCount );
FatUnpinBcb( IrpContext, DirentBcb );
if (NeedEaCount != 0) {
Iosb.Status = STATUS_ACCESS_DENIED;
try_return( Iosb );
}
}
//
// Check the create disposition and desired access
//
if ((CreateDisposition != FILE_OPEN) &&
(CreateDisposition != FILE_OPEN_IF)) {
Iosb.Status = STATUS_OBJECT_NAME_COLLISION;
try_return( Iosb );
}
if (!FatCheckFileAccess( IrpContext,
Dcb->DirentFatFlags,
DesiredAccess)) {
Iosb.Status = STATUS_ACCESS_DENIED;
try_return( Iosb );
}
//
// If the dcb is already opened by someone then we need
// to check the share access
//
if (Dcb->OpenCount > 0) {
if (!NT_SUCCESS(Iosb.Status = FatCheckShareAccess( IrpContext,
FileObject,
Dcb,
DesiredAccess,
ShareAccess ))) {
#if (NTDDI_VERSION >= NTDDI_WIN8)
NTSTATUS OplockBreakStatus = STATUS_SUCCESS;
//
// If we got a sharing violation try to break outstanding handle
// oplocks and retry the sharing check. If the caller specified
// FILE_COMPLETE_IF_OPLOCKED we don't bother breaking the oplock;
// we just return the sharing violation.
//
if ((Iosb.Status == STATUS_SHARING_VIOLATION) &&
!FlagOn( IrpSp->Parameters.Create.Options, FILE_COMPLETE_IF_OPLOCKED )) {
OplockBreakStatus = FsRtlOplockBreakH( FatGetFcbOplock(Dcb),
IrpContext->OriginatingIrp,
0,
IrpContext,
FatOplockComplete,
FatPrePostIrp );
//
// If FsRtlOplockBreakH returned STATUS_PENDING, then the IRP
// has been posted and we need to stop working.
//
if (OplockBreakStatus == STATUS_PENDING) {
Iosb.Status = STATUS_PENDING;
*OplockPostIrp = TRUE;
try_return( NOTHING );
//
// If FsRtlOplockBreakH returned an error we want to return that now.
//
} else if (!NT_SUCCESS( OplockBreakStatus )) {
Iosb.Status = OplockBreakStatus;
try_return( Iosb );
//
// Otherwise FsRtlOplockBreakH returned STATUS_SUCCESS, indicating
// that there is no oplock to be broken. The sharing violation is
// returned in that case.
//
} else {
NT_ASSERT( OplockBreakStatus == STATUS_SUCCESS );
try_return( Iosb );
}
//
// The initial sharing check failed with something other than sharing
// violation (which should never happen, but let's be future-proof),
// or we *did* get a sharing violation and the caller specified
// FILE_COMPLETE_IF_OPLOCKED. Either way this create is over.
//
} else {
try_return( Iosb );
}
#else
try_return( Iosb );
#endif
}
}
#if (NTDDI_VERSION >= NTDDI_WIN8)
//
// Now check that we can continue based on the oplock state of the
// directory. If there are no open handles yet we don't need to do
// this check; oplocks can only exist when there are handles.
//
if (Dcb->UncleanCount != 0) {
Iosb.Status = FsRtlCheckOplock( FatGetFcbOplock(Dcb),
IrpContext->OriginatingIrp,
IrpContext,
FatOplockComplete,
FatPrePostIrp );
}
//
// if FsRtlCheckOplock returns STATUS_PENDING the IRP has been posted
// to service an oplock break and we need to leave now.
//
if (Iosb.Status == STATUS_PENDING) {
*OplockPostIrp = TRUE;
try_return( NOTHING );
}
//
// If the caller wants atomic create-with-oplock semantics, tell
// the oplock package. We haven't incremented the Fcb's UncleanCount
// for this create yet, so add that in on the call.
//
if (OpenRequiringOplock &&
(Iosb.Status == STATUS_SUCCESS)) {
Iosb.Status = FsRtlOplockFsctrl( FatGetFcbOplock(Dcb),
IrpContext->OriginatingIrp,
(Dcb->UncleanCount + 1) );
}
//
// If we've encountered a failure we need to leave. FsRtlCheckOplock
// will have returned STATUS_OPLOCK_BREAK_IN_PROGRESS if it initiated
// and oplock break and the caller specified FILE_COMPLETE_IF_OPLOCKED
// on the create call. That's an NT_SUCCESS code, so we need to keep
// going.
//
if ((Iosb.Status != STATUS_SUCCESS) &&
(Iosb.Status != STATUS_OPLOCK_BREAK_IN_PROGRESS)) {
try_return( NOTHING );
}
#endif
//
// Now that we're done with the oplock work update the share counts.
// If the Dcb isn't yet opened we just set the share access rather than
// update it.
//
if (Dcb->OpenCount > 0) {
IoUpdateShareAccess( FileObject, &Dcb->ShareAccess );
} else {
IoSetShareAccess( *DesiredAccess,
ShareAccess,
FileObject,
&Dcb->ShareAccess );
}
UnwindShareAccess = TRUE;
//
// Setup the context and section object pointers, and update
// our reference counts
//
FatSetFileObject( FileObject,
UserDirectoryOpen,
Dcb,
UnwindCcb = FatCreateCcb( IrpContext ));
Dcb->UncleanCount += 1;
Dcb->OpenCount += 1;
Vcb->OpenFileCount += 1;
if (IsFileObjectReadOnly(FileObject)) { Vcb->ReadOnlyCount += 1; }
//
// Mark the delete on close bit if the caller asked for that.
//
{
PCCB Ccb = (PCCB)FileObject->FsContext2;
if (DeleteOnClose) {
SetFlag( Ccb->Flags, CCB_FLAG_DELETE_ON_CLOSE );
}
if (FileNameOpenedDos) {
SetFlag( Ccb->Flags, CCB_FLAG_OPENED_BY_SHORTNAME );
}
}
//
// In case this was set, clear it now.
//
ClearFlag(Dcb->FcbState, FCB_STATE_DELAY_CLOSE);
//
// And set our status to success
//
Iosb.Status = STATUS_SUCCESS;
Iosb.Information = FILE_OPENED;
try_exit: NOTHING;
} finally {
DebugUnwind( FatOpenExistingDcb );
//
// Unpin the Dirent Bcb if pinned.
//
FatUnpinBcb( IrpContext, DirentBcb );
//
// If this is an abnormal termination then undo our work
//
if (AbnormalTermination()) {
if (UnwindCcb != NULL) { FatDeleteCcb( IrpContext, &UnwindCcb ); }
if (UnwindShareAccess) { IoRemoveShareAccess( FileObject, &Dcb->ShareAccess ); }
}
if (DcbAcquired) {
FatReleaseFcb( IrpContext, Dcb );
}
DebugTrace(-1, Dbg, "FatOpenExistingDcb -> Iosb.Status = %08lx\n", Iosb.Status);
}
return Iosb;
}
//
// Internal support routine
//
_Requires_lock_held_(_Global_critical_region_)
IO_STATUS_BLOCK
FatOpenExistingFcb (
_In_ PIRP_CONTEXT IrpContext,
_In_ PIO_STACK_LOCATION IrpSp,
_Inout_ PFILE_OBJECT FileObject,
_Inout_ PVCB Vcb,
_Inout_ PFCB Fcb,
_In_ PACCESS_MASK DesiredAccess,
_In_ USHORT ShareAccess,
_In_ ULONG AllocationSize,
_In_ PFILE_FULL_EA_INFORMATION EaBuffer,
_In_ ULONG EaLength,
_In_ USHORT FileAttributes,
_In_ ULONG CreateDisposition,
_In_ BOOLEAN NoEaKnowledge,
_In_ BOOLEAN DeleteOnClose,
_In_ BOOLEAN OpenRequiringOplock,
_In_ BOOLEAN FileNameOpenedDos,
_Out_ PBOOLEAN OplockPostIrp
)
/*++
Routine Description:
This routine opens the specified existing fcb
Arguments:
FileObject - Supplies the File object
Vcb - Supplies the Vcb denoting the volume containing the Fcb
Fcb - Supplies the already existing fcb
DesiredAccess - Supplies the desired access of the caller
ShareAccess - Supplies the share access of the caller
AllocationSize - Supplies the initial allocation if the file is being
superseded or overwritten
EaBuffer - Supplies the Ea set if the file is being superseded or
overwritten
EaLength - Supplies the size, in byte, of the EaBuffer
FileAttributes - Supplies file attributes to use if the file is being
superseded or overwritten
CreateDisposition - Supplies the create disposition for this operation
NoEaKnowledge - This opener doesn't understand Ea's and we fail this
open if the file has NeedEa's.
DeleteOnClose - The caller wants the file gone when the handle is closed
OpenRequiringOplock - The caller provided the FILE_OPEN_REQUIRING_OPLOCK option.
FileNameOpenedDos - The caller hit the short side of the name pair finding
this file
OplockPostIrp - Address to store boolean indicating if the Irp needs to
be posted to the Fsp.
Return Value:
IO_STATUS_BLOCK - Returns the completion status for the operation
--*/
{
IO_STATUS_BLOCK Iosb = {0};
PBCB DirentBcb = NULL;
PDIRENT Dirent;
ACCESS_MASK AddedAccess = 0;
//
// The following variables are for abnormal termination
//
BOOLEAN UnwindShareAccess = FALSE;
PCCB UnwindCcb = NULL;
BOOLEAN DecrementFcbOpenCount = FALSE;
BOOLEAN FcbAcquired = FALSE;
PAGED_CODE();
DebugTrace(+1, Dbg, "FatOpenExistingFcb...\n", 0);
//
// Get the Fcb exlcusive. This is important as cleanup does not
// acquire the Vcb.
//
(VOID)FatAcquireExclusiveFcb( IrpContext, Fcb );
FcbAcquired = TRUE;
try {
*OplockPostIrp = FALSE;
#if (NTDDI_VERSION >= NTDDI_WIN7)
//
// Let's make sure that if the caller provided an oplock key that it
// gets stored in the file object.
//
Iosb.Status = FsRtlCheckOplockEx( FatGetFcbOplock(Fcb),
IrpContext->OriginatingIrp,
OPLOCK_FLAG_OPLOCK_KEY_CHECK_ONLY,
NULL,
NULL,
NULL );
if (Iosb.Status != STATUS_SUCCESS) {
try_return( NOTHING );
}
#endif
//
// Take special action if there is a current batch oplock or
// batch oplock break in process on the Fcb.
//
if (FsRtlCurrentBatchOplock( FatGetFcbOplock(Fcb) )) {
//
// We remember if a batch oplock break is underway for the
// case where the sharing check fails.
//
Iosb.Information = FILE_OPBATCH_BREAK_UNDERWAY;
Iosb.Status = FsRtlCheckOplock( FatGetFcbOplock(Fcb),
IrpContext->OriginatingIrp,
IrpContext,
FatOplockComplete,
FatPrePostIrp );
//
// if FsRtlCheckOplock returns STATUS_PENDING the IRP has been posted
// to service an oplock break and we need to leave now.
//
if (Iosb.Status == STATUS_PENDING) {
*OplockPostIrp = TRUE;
try_return( NOTHING );
}
}
//
// Check if the user wanted to create the file, also special case
// the supersede and overwrite options. Those add additional,
// possibly only implied, desired accesses to the caller, which
// we must be careful to pull back off if the caller did not actually
// request them.
//
// In other words, check against the implied access, but do not modify
// share access as a result.
//
if (CreateDisposition == FILE_CREATE) {
Iosb.Status = STATUS_OBJECT_NAME_COLLISION;
try_return( Iosb );
} else if (CreateDisposition == FILE_SUPERSEDE) {
SetFlag( AddedAccess,
DELETE & ~(*DesiredAccess) );
*DesiredAccess |= DELETE;
} else if ((CreateDisposition == FILE_OVERWRITE) ||
(CreateDisposition == FILE_OVERWRITE_IF)) {
SetFlag( AddedAccess,
(FILE_WRITE_DATA | FILE_WRITE_EA | FILE_WRITE_ATTRIBUTES) & ~(*DesiredAccess) );
*DesiredAccess |= FILE_WRITE_DATA | FILE_WRITE_EA | FILE_WRITE_ATTRIBUTES;
}
//
// Check the desired access
//
if (!FatCheckFileAccess( IrpContext,
Fcb->DirentFatFlags,
DesiredAccess )) {
Iosb.Status = STATUS_ACCESS_DENIED;
try_return( Iosb );
}
//
// Check for trying to delete a read only file.
//
if (DeleteOnClose &&
FlagOn( Fcb->DirentFatFlags, FAT_DIRENT_ATTR_READ_ONLY )) {
Iosb.Status = STATUS_CANNOT_DELETE;
try_return( Iosb );
}
//
// If we are asked to do an overwrite or supersede operation then
// deny access for files where the file attributes for system and
// hidden do not match
//
if ((CreateDisposition == FILE_SUPERSEDE) ||
(CreateDisposition == FILE_OVERWRITE) ||
(CreateDisposition == FILE_OVERWRITE_IF)) {
BOOLEAN Hidden;
BOOLEAN System;
Hidden = BooleanFlagOn(Fcb->DirentFatFlags, FAT_DIRENT_ATTR_HIDDEN );
System = BooleanFlagOn(Fcb->DirentFatFlags, FAT_DIRENT_ATTR_SYSTEM );
if ((Hidden && !FlagOn(FileAttributes, FILE_ATTRIBUTE_HIDDEN)) ||
(System && !FlagOn(FileAttributes, FILE_ATTRIBUTE_SYSTEM))) {
DebugTrace(0, Dbg, "The hidden and/or system bits do not match\n", 0);
Iosb.Status = STATUS_ACCESS_DENIED;
try_return( Iosb );
}
//
// If this media is write protected, don't even try the create.
//
if (FlagOn(Vcb->VcbState, VCB_STATE_FLAG_WRITE_PROTECTED)) {
//
// Set the real device for the pop-up info, and set the verify
// bit in the device object, so that we will force a verify
// in case the user put the correct media back in.
//
IoSetHardErrorOrVerifyDevice( IrpContext->OriginatingIrp,
Vcb->Vpb->RealDevice );
SetFlag(Vcb->Vpb->RealDevice->Flags, DO_VERIFY_VOLUME);
FatRaiseStatus( IrpContext, STATUS_MEDIA_WRITE_PROTECTED );
}
}
//
// Check if the Fcb has the proper share access. This routine will also
// check for writable user secions if the user did not allow write sharing.
//
if (!NT_SUCCESS(Iosb.Status = FatCheckShareAccess( IrpContext,
FileObject,
Fcb,
DesiredAccess,
ShareAccess ))) {
#if (NTDDI_VERSION >= NTDDI_WIN7)
NTSTATUS OplockBreakStatus = STATUS_SUCCESS;
//
// If we got a sharing violation try to break outstanding handle
// oplocks and retry the sharing check. If the caller specified
// FILE_COMPLETE_IF_OPLOCKED we don't bother breaking the oplock;
// we just return the sharing violation.
//
if ((Iosb.Status == STATUS_SHARING_VIOLATION) &&
!FlagOn( IrpSp->Parameters.Create.Options, FILE_COMPLETE_IF_OPLOCKED )) {
OplockBreakStatus = FsRtlOplockBreakH( FatGetFcbOplock(Fcb),
IrpContext->OriginatingIrp,
0,
IrpContext,
FatOplockComplete,
FatPrePostIrp );
//
// If FsRtlOplockBreakH returned STATUS_PENDING, then the IRP
// has been posted and we need to stop working.
//
if (OplockBreakStatus == STATUS_PENDING) {
Iosb.Status = STATUS_PENDING;
*OplockPostIrp = TRUE;
try_return( NOTHING );
//
// If FsRtlOplockBreakH returned an error we want to return that now.
//
} else if (!NT_SUCCESS( OplockBreakStatus )) {
Iosb.Status = OplockBreakStatus;
try_return( Iosb );
//
// Otherwise FsRtlOplockBreakH returned STATUS_SUCCESS, indicating
// that there is no oplock to be broken. The sharing violation is
// returned in that case.
//
} else {
NT_ASSERT( OplockBreakStatus == STATUS_SUCCESS );
try_return( Iosb );
}
//
// The initial sharing check failed with something other than sharing
// violation (which should never happen, but let's be future-proof),
// or we *did* get a sharing violation and the caller specified
// FILE_COMPLETE_IF_OPLOCKED. Either way this create is over.
//
} else {
try_return( Iosb );
}
#else
try_return( Iosb );
#endif
}
//
// Now check that we can continue based on the oplock state of the
// file. If there are no open handles yet we don't need to do this
// check; oplocks can only exist when there are handles.
//
// It is important that we modified the DesiredAccess in place so
// that the Oplock check proceeds against any added access we had
// to give the caller.
//
if (Fcb->UncleanCount != 0) {
Iosb.Status = FsRtlCheckOplock( FatGetFcbOplock(Fcb),
IrpContext->OriginatingIrp,
IrpContext,
FatOplockComplete,
FatPrePostIrp );
}
//
// if FsRtlCheckOplock returns STATUS_PENDING the IRP has been posted
// to service an oplock break and we need to leave now.
//
if (Iosb.Status == STATUS_PENDING) {
*OplockPostIrp = TRUE;
try_return( NOTHING );
}
//
// If the caller wants atomic create-with-oplock semantics, tell
// the oplock package. We haven't incremented the Fcb's UncleanCount
// for this create yet, so add that in on the call.
//
if (OpenRequiringOplock &&
(Iosb.Status == STATUS_SUCCESS)) {
Iosb.Status = FsRtlOplockFsctrl( FatGetFcbOplock(Fcb),
IrpContext->OriginatingIrp,
(Fcb->UncleanCount + 1) );
}
//
// If we've encountered a failure we need to leave. FsRtlCheckOplock
// will have returned STATUS_OPLOCK_BREAK_IN_PROGRESS if it initiated
// and oplock break and the caller specified FILE_COMPLETE_IF_OPLOCKED
// on the create call. That's an NT_SUCCESS code, so we need to keep
// going.
//
if ((Iosb.Status != STATUS_SUCCESS) &&
(Iosb.Status != STATUS_OPLOCK_BREAK_IN_PROGRESS)) {
try_return( NOTHING );
}
//
// Set the flag indicating if Fast I/O is possible
//
Fcb->Header.IsFastIoPossible = FatIsFastIoPossible( Fcb );
//
// If the user wants write access access to the file make sure there
// is not a process mapping this file as an image. Any attempt to
// delete the file will be stopped in fileinfo.c
//
// If the user wants to delete on close, we must check at this
// point though.
//
if (FlagOn(*DesiredAccess, FILE_WRITE_DATA) || DeleteOnClose) {
Fcb->OpenCount += 1;
DecrementFcbOpenCount = TRUE;
if (!MmFlushImageSection( &Fcb->NonPaged->SectionObjectPointers,
MmFlushForWrite )) {
Iosb.Status = DeleteOnClose ? STATUS_CANNOT_DELETE :
STATUS_SHARING_VIOLATION;
try_return( Iosb );
}
}
//
// If this is a non-cached open on a non-paging file, and there
// are no open cached handles, but there is a still a data
// section, attempt a flush and purge operation to avoid cache
// coherency overhead later. We ignore any I/O errors from
// the flush.
//
// We set the CREATE_IN_PROGRESS flag to prevent the Fcb from
// going away out from underneath us.
//
if (FlagOn( FileObject->Flags, FO_NO_INTERMEDIATE_BUFFERING ) &&
(Fcb->UncleanCount == Fcb->NonCachedUncleanCount) &&
(Fcb->NonPaged->SectionObjectPointers.DataSectionObject != NULL) &&
!FlagOn( Fcb->FcbState, FCB_STATE_PAGING_FILE )) {
SetFlag(Fcb->Vcb->VcbState, VCB_STATE_FLAG_CREATE_IN_PROGRESS);
CcFlushCache( &Fcb->NonPaged->SectionObjectPointers, NULL, 0, NULL );
//
// Grab and release PagingIo to serialize ourselves with the lazy writer.
// This will work to ensure that all IO has completed on the cached
// data and we will succesfully tear away the cache section.
//
ExAcquireResourceExclusiveLite( Fcb->Header.PagingIoResource, TRUE);
ExReleaseResourceLite( Fcb->Header.PagingIoResource );
CcPurgeCacheSection( &Fcb->NonPaged->SectionObjectPointers,
NULL,
0,
FALSE );
ClearFlag(Fcb->Vcb->VcbState, VCB_STATE_FLAG_CREATE_IN_PROGRESS);
}
//
// Check if the user only wanted to open the file
//
if ((CreateDisposition == FILE_OPEN) ||
(CreateDisposition == FILE_OPEN_IF)) {
DebugTrace(0, Dbg, "Doing open operation\n", 0);
//
// If the caller has no Ea knowledge, we immediately check for
// Need Ea's on the file.
//
if (NoEaKnowledge && !FatIsFat32(Vcb)) {
ULONG NeedEaCount;
//
// Get the dirent for the file and then check that the need
// ea count is 0.
//
FatGetDirentFromFcbOrDcb( IrpContext,
Fcb,
FALSE,
&Dirent,
&DirentBcb );
FatGetNeedEaCount( IrpContext,
Vcb,
Dirent,
&NeedEaCount );
FatUnpinBcb( IrpContext, DirentBcb );
if (NeedEaCount != 0) {
Iosb.Status = STATUS_ACCESS_DENIED;
try_return( Iosb );
}
}
//
// Everything checks out okay, so setup the context and
// section object pointers.
//
FatSetFileObject( FileObject,
UserFileOpen,
Fcb,
UnwindCcb = FatCreateCcb( IrpContext ));
FileObject->SectionObjectPointer = &Fcb->NonPaged->SectionObjectPointers;
//
// Fill in the information field, the status field is already
// set.
//
Iosb.Information = FILE_OPENED;
try_return( Iosb );
}
//
// Check if we are to supersede/overwrite the file, we can wait for
// any I/O at this point
//
if ((CreateDisposition == FILE_SUPERSEDE) ||
(CreateDisposition == FILE_OVERWRITE) ||
(CreateDisposition == FILE_OVERWRITE_IF)) {
NTSTATUS OldStatus;
DebugTrace(0, Dbg, "Doing supersede/overwrite operation\n", 0);
//
// We remember the previous status code because it may contain
// information about the oplock status.
//
OldStatus = Iosb.Status;
//
// Determine the granted access for this operation now.
//
if (!NT_SUCCESS( Iosb.Status = FatCheckSystemSecurityAccess( IrpContext ))) {
try_return( Iosb );
}
//
// And overwrite the file.
//
Iosb = FatSupersedeOrOverwriteFile( IrpContext,
FileObject,
Fcb,
AllocationSize,
EaBuffer,
EaLength,
FileAttributes,
CreateDisposition,
NoEaKnowledge );
if (Iosb.Status == STATUS_SUCCESS) {
Iosb.Status = OldStatus;
}
try_return( Iosb );
}
//
// If we ever get here then the I/O system gave us some bad input
//
#pragma prefast( suppress:28159, "things are seriously wrong if we get here" )
FatBugCheck( CreateDisposition, 0, 0 );
try_exit: NOTHING;
//
// Update the share access and counts if successful
//
if ((Iosb.Status != STATUS_PENDING) && NT_SUCCESS(Iosb.Status)) {
//
// Now, we may have added some access bits above to indicate the access
// this caller would conflict with (as opposed to what they get) in order
// to perform the overwrite/supersede. We need to make a call to that will
// recalculate the bits in the fileobject to reflect the real access they
// will get.
//
if (AddedAccess) {
NTSTATUS Status;
ClearFlag( *DesiredAccess, AddedAccess );
#pragma prefast( suppress:28931, "it needs to be there for debug assert" );
Status = IoCheckShareAccess( *DesiredAccess,
ShareAccess,
FileObject,
&Fcb->ShareAccess,
TRUE );
//
// It must be the case that we are really asking for less access, so
// any conflict must have been detected before this point.
//
NT_ASSERT( Status == STATUS_SUCCESS );
} else {
IoUpdateShareAccess( FileObject, &Fcb->ShareAccess );
}
UnwindShareAccess = TRUE;
//
// In case this was set, clear it now.
//
ClearFlag(Fcb->FcbState, FCB_STATE_DELAY_CLOSE);
Fcb->UncleanCount += 1;
Fcb->OpenCount += 1;
if (FlagOn(FileObject->Flags, FO_NO_INTERMEDIATE_BUFFERING)) {
Fcb->NonCachedUncleanCount += 1;
}
Vcb->OpenFileCount += 1;
if (IsFileObjectReadOnly(FileObject)) { Vcb->ReadOnlyCount += 1; }
{
PCCB Ccb;
Ccb = (PCCB)FileObject->FsContext2;
//
// Mark the DeleteOnClose bit if the operation was successful.
//
if ( DeleteOnClose ) {
SetFlag( Ccb->Flags, CCB_FLAG_DELETE_ON_CLOSE );
}
//
// Mark the OpenedByShortName bit if the operation was successful.
//
if ( FileNameOpenedDos ) {
SetFlag( Ccb->Flags, CCB_FLAG_OPENED_BY_SHORTNAME );
}
//
// Mark the ManageVolumeAccess bit if the privilege is held.
//
if (FatCheckManageVolumeAccess( IrpContext,
IrpSp->Parameters.Create.SecurityContext->AccessState,
(KPROCESSOR_MODE)( FlagOn( IrpSp->Flags, SL_FORCE_ACCESS_CHECK ) ?
UserMode :
IrpContext->OriginatingIrp->RequestorMode ))) {
SetFlag( Ccb->Flags, CCB_FLAG_MANAGE_VOLUME_ACCESS );
}
}
}
} finally {
DebugUnwind( FatOpenExistingFcb );
//
// Unpin the Dirent Bcb if pinned.
//
FatUnpinBcb( IrpContext, DirentBcb );
//
// If this is an abnormal termination then undo our work
//
if (AbnormalTermination()) {
if (UnwindCcb != NULL) { FatDeleteCcb( IrpContext, &UnwindCcb ); }
if (UnwindShareAccess) { IoRemoveShareAccess( FileObject, &Fcb->ShareAccess ); }
}
if (DecrementFcbOpenCount) {
Fcb->OpenCount -= 1;
if (Fcb->OpenCount == 0) {
if (ARGUMENT_PRESENT( FileObject )) {
FileObject->SectionObjectPointer = NULL;
}
FatDeleteFcb( IrpContext, &Fcb );
FcbAcquired = FALSE;
}
}
if (FcbAcquired) {
FatReleaseFcb( IrpContext, Fcb );
}
DebugTrace(-1, Dbg, "FatOpenExistingFcb -> Iosb.Status = %08lx\n", Iosb.Status);
}
return Iosb;
}
//
// Internal support routine
//
_Requires_lock_held_(_Global_critical_region_)
IO_STATUS_BLOCK
FatOpenTargetDirectory (
_In_ PIRP_CONTEXT IrpContext,
_Inout_ PFILE_OBJECT FileObject,
_Inout_ PDCB Dcb,
_In_ PACCESS_MASK DesiredAccess,
_In_ USHORT ShareAccess,
_In_ BOOLEAN DoesNameExist,
_In_ BOOLEAN FileNameOpenedDos
)
/*++
Routine Description:
This routine opens the target directory and replaces the name in the
file object with the remaining name.
Arguments:
FileObject - Supplies the File object
Dcb - Supplies an already existing dcb that we are going to open
DesiredAccess - Supplies the desired access of the caller
ShareAccess - Supplies the share access of the caller
DoesNameExist - Indicates if the file name already exists in the
target directory.
Return Value:
IO_STATUS_BLOCK - Returns the completion status for the operation
--*/
{
IO_STATUS_BLOCK Iosb = {0};
//
// The following variables are for abnormal termination
//
BOOLEAN UnwindShareAccess = FALSE;
PCCB UnwindCcb = NULL;
BOOLEAN DcbAcquired = FALSE;
PAGED_CODE();
DebugTrace(+1, Dbg, "FatOpenTargetDirectory...\n", 0);
//
// Get the Dcb exlcusive. This is important as cleanup does not
// acquire the Vcb.
//
(VOID)FatAcquireExclusiveFcb( IrpContext, Dcb );
DcbAcquired = TRUE;
try {
ULONG i;
//
// If the Dcb is already opened by someone then we need
// to check the share access
//
if (Dcb->OpenCount > 0) {
if (!NT_SUCCESS(Iosb.Status = IoCheckShareAccess( *DesiredAccess,
ShareAccess,
FileObject,
&Dcb->ShareAccess,
TRUE ))) {
try_return( Iosb );
}
} else {
IoSetShareAccess( *DesiredAccess,
ShareAccess,
FileObject,
&Dcb->ShareAccess );
}
UnwindShareAccess = TRUE;
//
// Setup the context and section object pointers, and update
// our reference counts
//
FatSetFileObject( FileObject,
UserDirectoryOpen,
Dcb,
UnwindCcb = FatCreateCcb( IrpContext ));
Dcb->UncleanCount += 1;
Dcb->OpenCount += 1;
Dcb->Vcb->OpenFileCount += 1;
if (IsFileObjectReadOnly(FileObject)) { Dcb->Vcb->ReadOnlyCount += 1; }
//
// Update the name in the file object, by definition the remaining
// part must be shorter than the original file name so we'll just
// overwrite the file name.
//
i = FileObject->FileName.Length/sizeof(WCHAR) - 1;
//
// Get rid of a trailing backslash
//
if (FileObject->FileName.Buffer[i] == L'\\') {
NT_ASSERT(i != 0);
FileObject->FileName.Length -= sizeof(WCHAR);
i -= 1;
}
//
// Find the first non-backslash character. i will be its index.
//
while (TRUE) {
if (FileObject->FileName.Buffer[i] == L'\\') {
i += 1;
break;
}
if (i == 0) {
break;
}
i--;
}
if (i) {
FileObject->FileName.Length -= (USHORT)(i * sizeof(WCHAR));
RtlMoveMemory( &FileObject->FileName.Buffer[0],
&FileObject->FileName.Buffer[i],
FileObject->FileName.Length );
}
//
// And set our status to success
//
Iosb.Status = STATUS_SUCCESS;
Iosb.Information = (DoesNameExist ? FILE_EXISTS : FILE_DOES_NOT_EXIST);
if ( ( NT_SUCCESS(Iosb.Status) ) && ( DoesNameExist ) ) {
PCCB Ccb;
Ccb = (PCCB)FileObject->FsContext2;
//
// Mark the OpenedByShortName bit if the operation was successful.
//
if ( FileNameOpenedDos ) {
SetFlag( Ccb->Flags, CCB_FLAG_OPENED_BY_SHORTNAME );
}
}
try_exit: NOTHING;
} finally {
DebugUnwind( FatOpenTargetDirectory );
//
// If this is an abnormal termination then undo our work
//
if (AbnormalTermination()) {
if (UnwindCcb != NULL) { FatDeleteCcb( IrpContext, &UnwindCcb ); }
if (UnwindShareAccess) { IoRemoveShareAccess( FileObject, &Dcb->ShareAccess ); }
}
if (DcbAcquired) {
FatReleaseFcb( IrpContext, Dcb );
}
DebugTrace(-1, Dbg, "FatOpenTargetDirectory -> Iosb.Status = %08lx\n", Iosb.Status);
}
return Iosb;
}
//
// Internal support routine
//
_Success_(return.Status == STATUS_SUCCESS)
_Requires_lock_held_(_Global_critical_region_)
IO_STATUS_BLOCK
#pragma warning(suppress:6101) // bug in PREFast means the _Success_ annotation is not correctly applied
FatOpenExistingDirectory (
_In_ PIRP_CONTEXT IrpContext,
_In_ PIO_STACK_LOCATION IrpSp,
_Inout_ PFILE_OBJECT FileObject,
_Inout_ PVCB Vcb,
_Outptr_result_maybenull_ PDCB *Dcb,
_In_ PDCB ParentDcb,
_In_ PDIRENT Dirent,
_In_ ULONG LfnByteOffset,
_In_ ULONG DirentByteOffset,
_In_ PUNICODE_STRING Lfn,
_In_ PACCESS_MASK DesiredAccess,
_In_ USHORT ShareAccess,
_In_ ULONG CreateDisposition,
_In_ BOOLEAN NoEaKnowledge,
_In_ BOOLEAN DeleteOnClose,
_In_ BOOLEAN FileNameOpenedDos,
_In_ BOOLEAN OpenRequiringOplock
)
/*++
Routine Description:
This routine opens the specified directory. The directory has not
previously been opened.
Arguments:
FileObject - Supplies the File object
Vcb - Supplies the Vcb denoting the volume containing the dcb
Dcb - Returns the newly-created DCB for the file.
ParentDcb - Supplies the parent directory containing the subdirectory
to be opened
DirectoryName - Supplies the file name of the directory being opened.
Dirent - Supplies the dirent for the directory being opened
LfnByteOffset - Tells where the Lfn begins. If there is no Lfn
this field is the same as DirentByteOffset.
DirentByteOffset - Supplies the Vbo of the dirent within its parent
directory
Lfn - May supply a long name for the file.
DesiredAccess - Supplies the desired access of the caller
ShareAccess - Supplies the share access of the caller
CreateDisposition - Supplies the create disposition for this operation
NoEaKnowledge - This opener doesn't understand Ea's and we fail this
open if the file has NeedEa's.
DeleteOnClose - The caller wants the file gone when the handle is closed
OpenRequiringOplock - The caller provided the FILE_OPEN_REQUIRING_OPLOCK option.
Return Value:
IO_STATUS_BLOCK - Returns the completion status for the operation
--*/
{
IO_STATUS_BLOCK Iosb = {0};
//
// The following variables are for abnormal termination
//
PDCB UnwindDcb = NULL;
PCCB UnwindCcb = NULL;
BOOLEAN CountsIncremented = FALSE;
UNREFERENCED_PARAMETER( DeleteOnClose );
#if (NTDDI_VERSION <= NTDDI_WIN7)
UNREFERENCED_PARAMETER( OpenRequiringOplock );
#endif
UNREFERENCED_PARAMETER( IrpSp );
PAGED_CODE();
DebugTrace(+1, Dbg, "FatOpenExistingDirectory...\n", 0);
try {
//
// If the caller has no Ea knowledge, we immediately check for
// Need Ea's on the file.
//
if (NoEaKnowledge && !FatIsFat32(Vcb)) {
ULONG NeedEaCount;
FatGetNeedEaCount( IrpContext,
Vcb,
Dirent,
&NeedEaCount );
if (NeedEaCount != 0) {
Iosb.Status = STATUS_ACCESS_DENIED;
try_return( Iosb );
}
}
//
// Check the create disposition and desired access
//
if ((CreateDisposition != FILE_OPEN) &&
(CreateDisposition != FILE_OPEN_IF)) {
Iosb.Status = STATUS_OBJECT_NAME_COLLISION;
try_return( Iosb );
}
if (!FatCheckFileAccess( IrpContext,
Dirent->Attributes,
DesiredAccess)) {
Iosb.Status = STATUS_ACCESS_DENIED;
try_return( Iosb );
}
//
// Create a new dcb for the directory
//
*Dcb = UnwindDcb = FatCreateDcb( IrpContext,
Vcb,
ParentDcb,
LfnByteOffset,
DirentByteOffset,
Dirent,
Lfn );
#if (NTDDI_VERSION >= NTDDI_WIN8)
//
// Let's make sure that if the caller provided an oplock key that it
// gets stored in the file object.
//
Iosb.Status = FsRtlCheckOplockEx( FatGetFcbOplock(*Dcb),
IrpContext->OriginatingIrp,
OPLOCK_FLAG_OPLOCK_KEY_CHECK_ONLY,
NULL,
NULL,
NULL );
//
// If the caller wants atomic create-with-oplock semantics, tell
// the oplock package. We haven't incremented the Fcb's UncleanCount
// for this create yet, so add that in on the call.
//
if (OpenRequiringOplock &&
(Iosb.Status == STATUS_SUCCESS)) {
Iosb.Status = FsRtlOplockFsctrl( FatGetFcbOplock(*Dcb),
IrpContext->OriginatingIrp,
((*Dcb)->UncleanCount + 1) );
}
//
// Get out if either of the above calls failed. Raise to trigger
// cleanup of the new Dcb.
//
if (Iosb.Status != STATUS_SUCCESS) {
NT_ASSERT( Iosb.Status != STATUS_PENDING );
FatRaiseStatus( IrpContext, Iosb.Status );
}
#endif
//
// Setup our share access
//
IoSetShareAccess( *DesiredAccess,
ShareAccess,
FileObject,
&(*Dcb)->ShareAccess );
//
// Setup the context and section object pointers, and update
// our reference counts
//
FatSetFileObject( FileObject,
UserDirectoryOpen,
(*Dcb),
UnwindCcb = FatCreateCcb( IrpContext ));
(*Dcb)->UncleanCount += 1;
(*Dcb)->OpenCount += 1;
Vcb->OpenFileCount += 1;
if (IsFileObjectReadOnly(FileObject)) { Vcb->ReadOnlyCount += 1; }
CountsIncremented = TRUE;
//
// And set our status to success
//
Iosb.Status = STATUS_SUCCESS;
Iosb.Information = FILE_OPENED;
if ( NT_SUCCESS(Iosb.Status) ) {
PCCB Ccb;
Ccb = (PCCB)FileObject->FsContext2;
//
// Mark the OpenedByShortName bit if the operation was successful.
//
if ( FileNameOpenedDos ) {
SetFlag( Ccb->Flags, CCB_FLAG_OPENED_BY_SHORTNAME );
}
}
try_exit: NOTHING;
} finally {
DebugUnwind( FatOpenExistingDirectory );
//
// If this is an abnormal termination then undo our work
//
if (AbnormalTermination()) {
if (CountsIncremented) {
(*Dcb)->UncleanCount -= 1;
(*Dcb)->OpenCount -= 1;
Vcb->OpenFileCount -= 1;
if (IsFileObjectReadOnly(FileObject)) { Vcb->ReadOnlyCount -= 1; }
}
if (UnwindDcb != NULL) {
if (ARGUMENT_PRESENT( FileObject )) {
FileObject->SectionObjectPointer = NULL;
}
FatDeleteFcb( IrpContext, &UnwindDcb );
*Dcb = NULL;
}
if (UnwindCcb != NULL) { FatDeleteCcb( IrpContext, &UnwindCcb ); }
}
DebugTrace(-1, Dbg, "FatOpenExistingDirectory -> Iosb.Status = %08lx\n", Iosb.Status);
}
return Iosb;
}
//
// Internal support routine
//
_Requires_lock_held_(_Global_critical_region_)
IO_STATUS_BLOCK
FatOpenExistingFile (
_In_ PIRP_CONTEXT IrpContext,
_Inout_ PFILE_OBJECT FileObject,
_Inout_ PVCB Vcb,
_Outptr_result_maybenull_ PFCB *Fcb,
_In_ PDCB ParentDcb,
_In_ PDIRENT Dirent,
_In_ ULONG LfnByteOffset,
_In_ ULONG DirentByteOffset,
_In_ PUNICODE_STRING Lfn,
_In_ PUNICODE_STRING OrigLfn,
_In_ PACCESS_MASK DesiredAccess,
_In_ USHORT ShareAccess,
_In_ ULONG AllocationSize,
_In_ PFILE_FULL_EA_INFORMATION EaBuffer,
_In_ ULONG EaLength,
_In_ USHORT FileAttributes,
_In_ ULONG CreateDisposition,
_In_ BOOLEAN IsPagingFile,
_In_ BOOLEAN NoEaKnowledge,
_In_ BOOLEAN DeleteOnClose,
_In_ BOOLEAN OpenRequiringOplock,
_In_ BOOLEAN FileNameOpenedDos
)
/*++
Routine Description:
This routine opens the specified file. The file has not previously
been opened.
Arguments:
FileObject - Supplies the File object
Vcb - Supplies the Vcb denoting the volume containing the file
Fcb - Returns the newly-created FCB for the file.
ParentDcb - Supplies the parent directory containing the file to be
opened
Dirent - Supplies the dirent for the file being opened
LfnByteOffset - Tells where the Lfn begins. If there is no Lfn
this field is the same as DirentByteOffset.
DirentByteOffset - Supplies the Vbo of the dirent within its parent
directory
Lfn - May supply a long name for the file.
DesiredAccess - Supplies the desired access of the caller
ShareAccess - Supplies the share access of the caller
AllocationSize - Supplies the initial allocation if the file is being
superseded, overwritten, or created.
EaBuffer - Supplies the Ea set if the file is being superseded,
overwritten, or created.
EaLength - Supplies the size, in byte, of the EaBuffer
FileAttributes - Supplies file attributes to use if the file is being
superseded, overwritten, or created
CreateDisposition - Supplies the create disposition for this operation
IsPagingFile - Indicates if this is the paging file being opened.
NoEaKnowledge - This opener doesn't understand Ea's and we fail this
open if the file has NeedEa's.
DeleteOnClose - The caller wants the file gone when the handle is closed
OpenRequiringOplock - The caller provided the FILE_OPEN_REQUIRING_OPLOCK option.
FileNameOpenedDos - The caller opened this file by hitting the 8.3 side
of the Lfn/8.3 pair
Return Value:
IO_STATUS_BLOCK - Returns the completion status for the operation
--*/
{
IO_STATUS_BLOCK Iosb = {0};
ACCESS_MASK AddedAccess = 0;
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( IrpContext->OriginatingIrp );
//
// The following variables are for abnormal termination
//
PFCB UnwindFcb = NULL;
PCCB UnwindCcb = NULL;
BOOLEAN CountsIncremented = FALSE;
#if (NTDDI_VERSION < NTDDI_WIN7)
UNREFERENCED_PARAMETER( OpenRequiringOplock );
#endif
PAGED_CODE();
DebugTrace(+1, Dbg, "FatOpenExistingFile...\n", 0);
try {
//
// Check if the user wanted to create the file or if access is
// denied
//
if (CreateDisposition == FILE_CREATE) {
Iosb.Status = STATUS_OBJECT_NAME_COLLISION;
try_return( Iosb );
} else if ((CreateDisposition == FILE_SUPERSEDE) && !IsPagingFile) {
SetFlag( AddedAccess,
DELETE & ~(*DesiredAccess) );
*DesiredAccess |= DELETE;
} else if (((CreateDisposition == FILE_OVERWRITE) ||
(CreateDisposition == FILE_OVERWRITE_IF)) && !IsPagingFile) {
SetFlag( AddedAccess,
(FILE_WRITE_DATA | FILE_WRITE_EA | FILE_WRITE_ATTRIBUTES) & ~(*DesiredAccess) );
*DesiredAccess |= FILE_WRITE_DATA | FILE_WRITE_EA | FILE_WRITE_ATTRIBUTES;
}
if (!FatCheckFileAccess( IrpContext,
Dirent->Attributes,
DesiredAccess)) {
Iosb.Status = STATUS_ACCESS_DENIED;
try_return( Iosb );
}
//
// Check for trying to delete a read only file.
//
if (DeleteOnClose &&
FlagOn( Dirent->Attributes, FAT_DIRENT_ATTR_READ_ONLY )) {
Iosb.Status = STATUS_CANNOT_DELETE;
try_return( Iosb );
}
//
// IF we are asked to do an overwrite or supersede operation then
// deny access for files where the file attributes for system and
// hidden do not match
//
if ((CreateDisposition == FILE_SUPERSEDE) ||
(CreateDisposition == FILE_OVERWRITE) ||
(CreateDisposition == FILE_OVERWRITE_IF)) {
BOOLEAN Hidden;
BOOLEAN System;
Hidden = BooleanFlagOn(Dirent->Attributes, FAT_DIRENT_ATTR_HIDDEN );
System = BooleanFlagOn(Dirent->Attributes, FAT_DIRENT_ATTR_SYSTEM );
if ((Hidden && !FlagOn(FileAttributes, FILE_ATTRIBUTE_HIDDEN)) ||
(System && !FlagOn(FileAttributes, FILE_ATTRIBUTE_SYSTEM))) {
DebugTrace(0, Dbg, "The hidden and/or system bits do not match\n", 0);
if ( !IsPagingFile ) {
Iosb.Status = STATUS_ACCESS_DENIED;
try_return( Iosb );
}
}
//
// If this media is write protected, don't even try the create.
//
if (FlagOn(Vcb->VcbState, VCB_STATE_FLAG_WRITE_PROTECTED)) {
//
// Set the real device for the pop-up info, and set the verify
// bit in the device object, so that we will force a verify
// in case the user put the correct media back in.
//
IoSetHardErrorOrVerifyDevice( IrpContext->OriginatingIrp,
Vcb->Vpb->RealDevice );
SetFlag(Vcb->Vpb->RealDevice->Flags, DO_VERIFY_VOLUME);
FatRaiseStatus( IrpContext, STATUS_MEDIA_WRITE_PROTECTED );
}
}
//
// Create a new Fcb for the file, and set the file size in
// the fcb.
//
*Fcb = UnwindFcb = FatCreateFcb( IrpContext,
Vcb,
ParentDcb,
LfnByteOffset,
DirentByteOffset,
Dirent,
Lfn,
OrigLfn,
IsPagingFile,
FALSE );
(*Fcb)->Header.ValidDataLength.LowPart = (*Fcb)->Header.FileSize.LowPart;
//
// If this is a paging file, lookup the allocation size so that
// the Mcb is always valid
//
if (IsPagingFile) {
FatLookupFileAllocationSize( IrpContext, *Fcb );
}
#if (NTDDI_VERSION >= NTDDI_WIN7)
//
// Let's make sure that if the caller provided an oplock key that it
// gets stored in the file object.
//
Iosb.Status = FsRtlCheckOplockEx( FatGetFcbOplock(*Fcb),
IrpContext->OriginatingIrp,
OPLOCK_FLAG_OPLOCK_KEY_CHECK_ONLY,
NULL,
NULL,
NULL );
//
// If the caller wants atomic create-with-oplock semantics, tell
// the oplock package. We haven't incremented the Fcb's UncleanCount
// for this create yet, so add that in on the call.
//
if (OpenRequiringOplock &&
(Iosb.Status == STATUS_SUCCESS)) {
Iosb.Status = FsRtlOplockFsctrl( FatGetFcbOplock(*Fcb),
IrpContext->OriginatingIrp,
((*Fcb)->UncleanCount + 1) );
}
//
// Get out if either of the above calls failed. Raise to trigger
// cleanup of the new Fcb.
//
if (Iosb.Status != STATUS_SUCCESS) {
NT_ASSERT( Iosb.Status != STATUS_PENDING );
FatRaiseStatus( IrpContext, Iosb.Status );
}
#endif
//
// Now case on whether we are to simply open, supersede, or
// overwrite the file.
//
switch (CreateDisposition) {
case FILE_OPEN:
case FILE_OPEN_IF:
DebugTrace(0, Dbg, "Doing only an open operation\n", 0);
//
// If the caller has no Ea knowledge, we immediately check for
// Need Ea's on the file.
//
if (NoEaKnowledge && !FatIsFat32(Vcb)) {
ULONG NeedEaCount;
FatGetNeedEaCount( IrpContext,
Vcb,
Dirent,
&NeedEaCount );
if (NeedEaCount != 0) {
FatRaiseStatus( IrpContext, STATUS_ACCESS_DENIED );
}
}
//
// Setup the context and section object pointers.
//
FatSetFileObject( FileObject,
UserFileOpen,
*Fcb,
UnwindCcb = FatCreateCcb( IrpContext ));
FileObject->SectionObjectPointer = &(*Fcb)->NonPaged->SectionObjectPointers;
Iosb.Status = STATUS_SUCCESS;
Iosb.Information = FILE_OPENED;
break;
case FILE_SUPERSEDE:
case FILE_OVERWRITE:
case FILE_OVERWRITE_IF:
DebugTrace(0, Dbg, "Doing supersede/overwrite operation\n", 0);
//
// Determine the granted access for this operation now.
//
if (!NT_SUCCESS( Iosb.Status = FatCheckSystemSecurityAccess( IrpContext ))) {
try_return( Iosb );
}
Iosb = FatSupersedeOrOverwriteFile( IrpContext,
FileObject,
*Fcb,
AllocationSize,
EaBuffer,
EaLength,
FileAttributes,
CreateDisposition,
NoEaKnowledge );
break;
default:
DebugTrace(0, Dbg, "Illegal Create Disposition\n", 0);
#pragma prefast( suppress:28159, "things are seriously wrong if we get here" )
FatBugCheck( CreateDisposition, 0, 0 );
break;
}
try_exit: NOTHING;
//
// Setup our share access and counts if things were successful.
//
if ((Iosb.Status != STATUS_PENDING) && NT_SUCCESS( Iosb.Status )) {
//
// Remove any virtual access the caller needed to check against, but will
// not really receive. Overwrite/supersede is a bit of a special case.
//
ClearFlag( *DesiredAccess, AddedAccess );
IoSetShareAccess( *DesiredAccess,
ShareAccess,
FileObject,
&(*Fcb)->ShareAccess );
(*Fcb)->UncleanCount += 1;
(*Fcb)->OpenCount += 1;
if (FlagOn(FileObject->Flags, FO_NO_INTERMEDIATE_BUFFERING)) {
(*Fcb)->NonCachedUncleanCount += 1;
}
Vcb->OpenFileCount += 1;
if (IsFileObjectReadOnly(FileObject)) { Vcb->ReadOnlyCount += 1; }
CountsIncremented = TRUE;
}
{
PCCB Ccb;
Ccb = (PCCB)FileObject->FsContext2;
if ( NT_SUCCESS(Iosb.Status) ) {
//
// Mark the DeleteOnClose bit if the operation was successful.
//
if ( DeleteOnClose ) {
SetFlag( Ccb->Flags, CCB_FLAG_DELETE_ON_CLOSE );
}
//
// Mark the OpenedByShortName bit if the operation was successful.
//
if ( FileNameOpenedDos ) {
SetFlag( Ccb->Flags, CCB_FLAG_OPENED_BY_SHORTNAME );
}
//
// Mark the ManageVolumeAccess bit if the privilege is held.
//
if (FatCheckManageVolumeAccess( IrpContext,
IrpSp->Parameters.Create.SecurityContext->AccessState,
(KPROCESSOR_MODE)( FlagOn( IrpSp->Flags, SL_FORCE_ACCESS_CHECK ) ?
UserMode :
IrpContext->OriginatingIrp->RequestorMode ))) {
SetFlag( Ccb->Flags, CCB_FLAG_MANAGE_VOLUME_ACCESS );
}
}
}
} finally {
DebugUnwind( FatOpenExistingFile );
//
// If this is an abnormal termination then undo our work
//
if (AbnormalTermination()) {
if (CountsIncremented) {
(*Fcb)->UncleanCount -= 1;
(*Fcb)->OpenCount -= 1;
if (FlagOn(FileObject->Flags, FO_NO_INTERMEDIATE_BUFFERING)) {
(*Fcb)->NonCachedUncleanCount -= 1;
}
Vcb->OpenFileCount -= 1;
if (IsFileObjectReadOnly(FileObject)) { Vcb->ReadOnlyCount -= 1; }
}
if (UnwindFcb != NULL) {
if (ARGUMENT_PRESENT( FileObject )) {
FileObject->SectionObjectPointer = NULL;
}
FatDeleteFcb( IrpContext, &UnwindFcb );
*Fcb = NULL;
}
if (UnwindCcb != NULL) { FatDeleteCcb( IrpContext, &UnwindCcb ); }
}
DebugTrace(-1, Dbg, "FatOpenExistingFile -> Iosb.Status = %08lx\n", Iosb.Status);
}
return Iosb;
}
//
// Internal support routine
//
_Requires_lock_held_(_Global_critical_region_)
IO_STATUS_BLOCK
FatCreateNewDirectory (
_In_ PIRP_CONTEXT IrpContext,
_In_ PIO_STACK_LOCATION IrpSp,
_Inout_ PFILE_OBJECT FileObject,
_Inout_ PVCB Vcb,
_Inout_ PDCB ParentDcb,
_In_ POEM_STRING OemName,
_In_ PUNICODE_STRING UnicodeName,
_In_ PACCESS_MASK DesiredAccess,
_In_ USHORT ShareAccess,
_In_ PFILE_FULL_EA_INFORMATION EaBuffer,
_In_ ULONG EaLength,
_In_ USHORT FileAttributes,
_In_ BOOLEAN NoEaKnowledge,
_In_ BOOLEAN DeleteOnClose,
_In_ BOOLEAN OpenRequiringOplock
)
/*++
Routine Description:
This routine creates a new directory. The directory has already been
verified not to exist yet.
Arguments:
FileObject - Supplies the file object for the newly created directory
Vcb - Supplies the Vcb denote the volume to contain the new directory
ParentDcb - Supplies the parent directory containg the newly created
directory
OemName - Supplies the Oem name for the newly created directory. It may
or maynot be 8.3 complient, but will be upcased.
UnicodeName - Supplies the Unicode name for the newly created directory.
It may or maynot be 8.3 complient. This name contains the original
case information.
DesiredAccess - Supplies the desired access of the caller
ShareAccess - Supplies the shared access of the caller
EaBuffer - Supplies the Ea set for the newly created directory
EaLength - Supplies the length, in bytes, of EaBuffer
FileAttributes - Supplies the file attributes for the newly created
directory.
NoEaKnowledge - This opener doesn't understand Ea's and we fail this
open if the file has NeedEa's.
DeleteOnClose - The caller wants the file gone when the handle is closed
OpenRequiringOplock - The caller provided the FILE_OPEN_REQUIRING_OPLOCK option.
Return Value:
IO_STATUS_BLOCK - Returns the completion status for the operation
--*/
{
IO_STATUS_BLOCK Iosb;
PDCB Dcb = NULL;
PCCB Ccb = NULL;
PDIRENT Dirent = NULL;
PBCB DirentBcb = NULL;
ULONG DirentsNeeded;
ULONG DirentByteOffset;
PDIRENT ShortDirent;
ULONG ShortDirentByteOffset;
USHORT EaHandle;
BOOLEAN AllLowerComponent;
BOOLEAN AllLowerExtension;
BOOLEAN CreateLfn;
ULONG BytesInFirstPage = 0;
ULONG DirentsInFirstPage = 0;
PDIRENT FirstPageDirent = 0;
PBCB SecondPageBcb = NULL;
ULONG SecondPageOffset;
PDIRENT SecondPageDirent = NULL;
BOOLEAN DirentFromPool = FALSE;
OEM_STRING ShortName;
UCHAR ShortNameBuffer[12];
#if (NTDDI_VERSION <= NTDDI_WIN7)
UNREFERENCED_PARAMETER( OpenRequiringOplock );
#endif
UNREFERENCED_PARAMETER( IrpSp );
PAGED_CODE();
DebugTrace(+1, Dbg, "FatCreateNewDirectory...\n", 0);
ShortName.Length = 0;
ShortName.MaximumLength = 12;
ShortName.Buffer = (PCHAR)&ShortNameBuffer[0];
EaHandle = 0;
//
// We fail this operation if the caller doesn't understand Ea's.
//
if (NoEaKnowledge
&& EaLength > 0) {
Iosb.Status = STATUS_ACCESS_DENIED;
DebugTrace(-1, Dbg, "FatCreateNewDirectory -> Iosb.Status = %08lx\n", Iosb.Status);
return Iosb;
}
//
// DeleteOnClose and ReadOnly are not compatible.
//
if (DeleteOnClose && FlagOn(FileAttributes, FAT_DIRENT_ATTR_READ_ONLY)) {
Iosb.Status = STATUS_CANNOT_DELETE;
return Iosb;
}
// Now get the names that we will be using.
//
FatSelectNames( IrpContext,
ParentDcb,
OemName,
UnicodeName,
&ShortName,
NULL,
&AllLowerComponent,
&AllLowerExtension,
&CreateLfn );
//
// If we are not in Chicago mode, ignore the magic bits.
//
if (!FatData.ChicagoMode) {
AllLowerComponent = FALSE;
AllLowerExtension = FALSE;
CreateLfn = FALSE;
}
//
// Create/allocate a new dirent
//
DirentsNeeded = CreateLfn ? FAT_LFN_DIRENTS_NEEDED(UnicodeName) + 1 : 1;
DirentByteOffset = FatCreateNewDirent( IrpContext,
ParentDcb,
DirentsNeeded,
FALSE );
try {
FatPrepareWriteDirectoryFile( IrpContext,
ParentDcb,
DirentByteOffset,
sizeof(DIRENT),
&DirentBcb,
&Dirent,
FALSE,
TRUE,
&Iosb.Status );
NT_ASSERT( NT_SUCCESS( Iosb.Status ) && DirentBcb && Dirent );
//
// Deal with the special case of an LFN + Dirent structure crossing
// a page boundry.
//
if ((DirentByteOffset / PAGE_SIZE) !=
((DirentByteOffset + (DirentsNeeded - 1) * sizeof(DIRENT)) / PAGE_SIZE)) {
SecondPageBcb;
SecondPageOffset;
SecondPageDirent;
SecondPageOffset = (DirentByteOffset & ~(PAGE_SIZE - 1)) + PAGE_SIZE;
BytesInFirstPage = SecondPageOffset - DirentByteOffset;
DirentsInFirstPage = BytesInFirstPage / sizeof(DIRENT);
FatPrepareWriteDirectoryFile( IrpContext,
ParentDcb,
SecondPageOffset,
sizeof(DIRENT),
&SecondPageBcb,
&SecondPageDirent,
FALSE,
TRUE,
&Iosb.Status );
NT_ASSERT( NT_SUCCESS( Iosb.Status ) && SecondPageBcb && SecondPageDirent );
FirstPageDirent = Dirent;
Dirent = FsRtlAllocatePoolWithTag( PagedPool,
DirentsNeeded * sizeof(DIRENT),
TAG_DIRENT );
DirentFromPool = TRUE;
}
//
// Bump up Dirent and DirentByteOffset
//
ShortDirent = Dirent + DirentsNeeded - 1;
ShortDirentByteOffset = DirentByteOffset +
(DirentsNeeded - 1) * sizeof(DIRENT);
NT_ASSERT( NT_SUCCESS( Iosb.Status ));
//
// Fill in the fields of the dirent.
//
FatConstructDirent( IrpContext,
ShortDirent,
&ShortName,
AllLowerComponent,
AllLowerExtension,
CreateLfn ? UnicodeName : NULL,
FileAttributes | FAT_DIRENT_ATTR_DIRECTORY,
TRUE,
NULL );
//
// If the dirent crossed pages, we have to do some real gross stuff.
//
if (DirentFromPool) {
RtlCopyMemory( FirstPageDirent, Dirent, BytesInFirstPage );
RtlCopyMemory( SecondPageDirent,
Dirent + DirentsInFirstPage,
DirentsNeeded*sizeof(DIRENT) - BytesInFirstPage );
ShortDirent = SecondPageDirent + (DirentsNeeded - DirentsInFirstPage) - 1;
}
//
// Create a new dcb for the directory.
//
Dcb = FatCreateDcb( IrpContext,
Vcb,
ParentDcb,
DirentByteOffset,
ShortDirentByteOffset,
ShortDirent,
CreateLfn ? UnicodeName : NULL );
#if (NTDDI_VERSION >= NTDDI_WIN8)
//
// The next three FsRtl calls are for oplock work. We deliberately
// do these here so that if either call fails we will be able to
// clean up without adding a bunch of code to unwind counts, fix
// the file object, etc.
//
//
// Let's make sure that if the caller provided an oplock key that it
// gets stored in the file object.
//
Iosb.Status = FsRtlCheckOplockEx( FatGetFcbOplock(Dcb),
IrpContext->OriginatingIrp,
OPLOCK_FLAG_OPLOCK_KEY_CHECK_ONLY,
NULL,
NULL,
NULL );
//
// If the caller wants atomic create-with-oplock semantics, tell
// the oplock package. We haven't incremented the Dcb's UncleanCount
// for this create yet, so add that in on the call.
//
if (OpenRequiringOplock &&
(Iosb.Status == STATUS_SUCCESS)) {
Iosb.Status = FsRtlOplockFsctrl( FatGetFcbOplock(Dcb),
IrpContext->OriginatingIrp,
(Dcb->UncleanCount + 1) );
}
//
// Break parent directory oplock. Directory oplock breaks are always
// advisory, so we will never block/get STATUS_PENDING here. On the
// off chance this fails with INSUFFICIENT_RESOURCES we do it here
// where we can still tolerate a failure.
//
if (Iosb.Status == STATUS_SUCCESS) {
Iosb.Status = FsRtlCheckOplockEx( FatGetFcbOplock(ParentDcb),
IrpContext->OriginatingIrp,
OPLOCK_FLAG_PARENT_OBJECT,
NULL,
NULL,
NULL );
}
//
// Get out if any of the oplock calls failed.
//
if (Iosb.Status != STATUS_SUCCESS) {
FatRaiseStatus( IrpContext, Iosb.Status );
}
#endif
//
// Tentatively add the new Ea's,
//
if (EaLength > 0) {
//
// This returns false if we are trying to create a file
// with Need Ea's and don't understand EA's.
//
FatCreateEa( IrpContext,
Dcb->Vcb,
(PUCHAR) EaBuffer,
EaLength,
&Dcb->ShortName.Name.Oem,
&EaHandle );
}
if (!FatIsFat32(Dcb->Vcb)) {
ShortDirent->ExtendedAttributes = EaHandle;
}
//
// After this point we cannot just simply mark the dirent deleted,
// we have to deal with the directory file object.
//
//
// Make the dirent into a directory. Note that even if this call
// raises because of disk space, the diectory file object has been
// created.
//
FatInitializeDirectoryDirent( IrpContext, Dcb, ShortDirent );
//
// Setup the context and section object pointers, and update
// our reference counts. Note that this call cannot fail.
//
FatSetFileObject( FileObject,
UserDirectoryOpen,
Dcb,
Ccb = FatCreateCcb( IrpContext ) );
//
// Initialize the LongFileName if it has not already been set, so that
// FatNotify below won't have to. If there are filesystem filters
// attached to FAT, the LongFileName could have gotten set if the
// filter queried for name information on this file object while
// watching the IO needed in FatInitializeDirectoryDirent.
//
if (Dcb->FullFileName.Buffer == NULL) {
FatSetFullNameInFcb( IrpContext, Dcb, UnicodeName );
}
//
// We call the notify package to report that the
// we added a file.
//
FatNotifyReportChange( IrpContext,
Vcb,
Dcb,
FILE_NOTIFY_CHANGE_DIR_NAME,
FILE_ACTION_ADDED );
//
// Setup our share access
//
IoSetShareAccess( *DesiredAccess,
ShareAccess,
FileObject,
&Dcb->ShareAccess );
//
// From this point on, nothing can raise.
//
Dcb->UncleanCount += 1;
Dcb->OpenCount += 1;
Vcb->OpenFileCount += 1;
if (IsFileObjectReadOnly(FileObject)) { Vcb->ReadOnlyCount += 1; }
if (DeleteOnClose) {
SetFlag( Ccb->Flags, CCB_FLAG_DELETE_ON_CLOSE );
}
//
// And set our return status
//
Iosb.Status = STATUS_SUCCESS;
Iosb.Information = FILE_CREATED;
} except(FatExceptionFilter( IrpContext, GetExceptionInformation() )) {
//
// We'll catch all exceptions and handle them below.
//
Iosb.Status = IrpContext->ExceptionStatus;
}
//
// If we failed then undo our work.
//
if (!NT_SUCCESS( Iosb.Status )) {
//
// We always have to delete the Ccb if we created one.
//
if ( Ccb != NULL ) {
FatDeleteCcb( IrpContext, &Ccb );
}
#pragma prefast( suppress: 28924, "prefast thinks this test is redundant, but DCB can be NULL depending on where we raise" )
if ( Dcb == NULL) {
NT_ASSERT( (ParentDcb->Specific.Dcb.UnusedDirentVbo == 0xffffffff) ||
RtlAreBitsSet( &ParentDcb->Specific.Dcb.FreeDirentBitmap,
DirentByteOffset / sizeof(DIRENT),
DirentsNeeded ) );
RtlClearBits( &ParentDcb->Specific.Dcb.FreeDirentBitmap,
DirentByteOffset / sizeof(DIRENT),
DirentsNeeded );
//
// Mark the dirents deleted. The codes is complex because of
// dealing with an LFN than crosses a page boundry.
//
if (Dirent != NULL) {
ULONG i;
//
// We failed before creating a directory file object.
// We can just mark the dirent deleted and exit.
//
for (i = 0; i < DirentsNeeded; i++) {
if (DirentFromPool == FALSE) {
//
// Simple case.
//
Dirent[i].FileName[0] = FAT_DIRENT_DELETED;
} else {
//
// If the second CcPreparePinWrite failed, we have
// to stop early.
//
if ((SecondPageBcb == NULL) &&
(i == DirentsInFirstPage)) {
break;
}
//
// Now conditionally update either page.
//
if (i < DirentsInFirstPage) {
FirstPageDirent[i].FileName[0] = FAT_DIRENT_DELETED;
} else {
SecondPageDirent[i - DirentsInFirstPage].FileName[0] = FAT_DIRENT_DELETED;
}
}
}
}
}
}
//
// Just drop the Bcbs we have in the parent right now so if we
// failed to create the directory and we take the path to rip apart
// the partially created child, when we sync-uninit we won't cause
// a lazy writer processing the parent to block on us. This would
// consume one of the lazy writers, one of which must be running free
// in order for us to come back from the sync-uninit.
//
// Neat, huh?
//
// Granted, the delete dirent below will be marginally less efficient
// since the Bcb may be reclaimed by the time it executes. Life is
// tough.
//
FatUnpinBcb( IrpContext, DirentBcb );
FatUnpinBcb( IrpContext, SecondPageBcb );
if (DirentFromPool) {
ExFreePool( Dirent );
}
if (!NT_SUCCESS( Iosb.Status )) {
#pragma prefast( suppress: 28924, "prefast thinks this test is redundant, but DCB can be NULL depending on where we raise" )
if (Dcb != NULL) {
//
// We have created the Dcb. If an error occurred while
// creating the Ea's, there will be no directory file
// object.
//
PFILE_OBJECT DirectoryFileObject;
DirectoryFileObject = Dcb->Specific.Dcb.DirectoryFile;
//
// Knock down all of the repinned data so we can begin to destroy
// this failed child. We don't care about any raising here - we're
// already got a fire going.
//
// Note that if we failed to do this, the repinned initial pieces
// of the child would cause the sync-uninit to block forever.
//
// A previous spin on this fix had us not make the ./.. creation
// "reversible" (bad term) and thus avoid having the Bcb still
// outstanding. This wound up causing very bad things to happen
// on DMF floppies when we tried to do a similar yank-down in the
// create path - we want the purge it does to make sure we never
// try to write the bytes out ... it is just a lot cleaner to
// unpinrepin. I'll leave the reversible logic in place if it ever
// proves useful.
//
//
// There is a possibility that this may be a generally good idea
// for "live" finally clauses - set in ExceptionFilter, clear in
// ProcessException. Think about this.
//
SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_DISABLE_RAISE );
FatUnpinRepinnedBcbs( IrpContext );
ClearFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_DISABLE_RAISE );
if (Dcb->FirstClusterOfFile != 0) {
try {
Dcb->Header.FileSize.LowPart = 0;
CcSetFileSizes( Dcb->Specific.Dcb.DirectoryFile,
(PCC_FILE_SIZES)&Dcb->Header.AllocationSize );
//
// Now zap the allocation backing it.
//
FatTruncateFileAllocation( IrpContext, Dcb, 0 );
} except(FatExceptionFilter( IrpContext, GetExceptionInformation() )) {
//
// We catch all exceptions that Fat catches, but don't do
// anything with them.
//
}
}
if (DirectoryFileObject != NULL) {
FatSyncUninitializeCacheMap( IrpContext,
DirectoryFileObject );
}
try {
//
// Remove the directory entry we made in the parent Dcb.
//
FatDeleteDirent( IrpContext, Dcb, NULL, TRUE );
//
// FatDeleteDirent can pin and dirty BCBs, so lets unrepin again.
//
FatUnpinRepinnedBcbs( IrpContext );
} except(FatExceptionFilter( IrpContext, GetExceptionInformation() )) {
//
// We catch all exceptions that Fat catches, but don't do
// anything with them.
//
}
//
// Finaly, dereference the directory file object. This will
// cause a close Irp to be processed, blowing away the Fcb.
//
if (DirectoryFileObject != NULL) {
//
// Dereference the file object for this DCB. The DCB will
// go away when this file object is closed.
//
Dcb->Specific.Dcb.DirectoryFile = NULL;
ObDereferenceObject( DirectoryFileObject );
} else {
//
// This was also a PDK fix. If the stream file exists, this would
// be done during the dereference file object operation. Otherwise
// we have to remove the Dcb and check if we should remove the parent.
// For now we will just leave the parent lying around.
//
#pragma prefast( suppress: 28924, "prefast thinks this test is redundant, but FileObject can be NULL depending on where we raise" )
if (ARGUMENT_PRESENT( FileObject )) {
FileObject->SectionObjectPointer = NULL;
}
FatDeleteFcb( IrpContext, &Dcb );
}
}
DebugTrace(-1, Dbg, "FatCreateNewDirectory -> Iosb.Status = %08lx\n", Iosb.Status);
FatRaiseStatus( IrpContext, Iosb.Status );
}
UNREFERENCED_PARAMETER( EaBuffer );
UNREFERENCED_PARAMETER( EaLength );
return Iosb;
}
//
// Internal support routine
//
_Requires_lock_held_(_Global_critical_region_)
IO_STATUS_BLOCK
FatCreateNewFile (
_In_ PIRP_CONTEXT IrpContext,
_In_ PIO_STACK_LOCATION IrpSp,
_Inout_ PFILE_OBJECT FileObject,
_Inout_ PVCB Vcb,
_Inout_ PDCB ParentDcb,
_In_ POEM_STRING OemName,
_In_ PUNICODE_STRING UnicodeName,
_In_ PACCESS_MASK DesiredAccess,
_In_ USHORT ShareAccess,
_In_ ULONG AllocationSize,
_In_ PFILE_FULL_EA_INFORMATION EaBuffer,
_In_ ULONG EaLength,
_In_ USHORT FileAttributes,
_In_ PUNICODE_STRING LfnBuffer,
_In_ BOOLEAN IsPagingFile,
_In_ BOOLEAN NoEaKnowledge,
_In_ BOOLEAN DeleteOnClose,
_In_ BOOLEAN OpenRequiringOplock,
_In_ BOOLEAN TemporaryFile
)
/*++
Routine Description:
This routine creates a new file. The file has already been verified
not to exist yet.
Arguments:
FileObject - Supplies the file object for the newly created file
Vcb - Supplies the Vcb denote the volume to contain the new file
ParentDcb - Supplies the parent directory containg the newly created
File
OemName - Supplies the Oem name for the newly created file. It may
or maynot be 8.3 complient, but will be upcased.
UnicodeName - Supplies the Unicode name for the newly created file.
It may or maynot be 8.3 complient. This name contains the original
case information.
DesiredAccess - Supplies the desired access of the caller
ShareAccess - Supplies the shared access of the caller
AllocationSize - Supplies the initial allocation size for the file
EaBuffer - Supplies the Ea set for the newly created file
EaLength - Supplies the length, in bytes, of EaBuffer
FileAttributes - Supplies the file attributes for the newly created
file
LfnBuffer - A MAX_LFN sized buffer for directory searching
IsPagingFile - Indicates if this is the paging file being created
NoEaKnowledge - This opener doesn't understand Ea's and we fail this
open if the file has NeedEa's.
DeleteOnClose - The caller wants the file gone when the handle is closed
OpenRequiringOplock - The caller provided the FILE_OPEN_REQUIRING_OPLOCK option.
TemporaryFile - Signals the lazywriter to not write dirty data unless
absolutely has to.
Return Value:
IO_STATUS_BLOCK - Returns the completion status for the operation
--*/
{
IO_STATUS_BLOCK Iosb = {0};
PFCB Fcb = NULL;
PDIRENT Dirent = NULL;
PBCB DirentBcb = NULL;
ULONG DirentsNeeded;
ULONG DirentByteOffset;
PDIRENT ShortDirent;
ULONG ShortDirentByteOffset;
USHORT EaHandle;
BOOLEAN AllLowerComponent;
BOOLEAN AllLowerExtension;
BOOLEAN CreateLfn;
ULONG BytesInFirstPage = 0;
ULONG DirentsInFirstPage = 0;
PDIRENT FirstPageDirent = NULL;
PBCB SecondPageBcb = NULL;
ULONG SecondPageOffset;
PDIRENT SecondPageDirent = NULL;
BOOLEAN DirentFromPool = FALSE;
OEM_STRING ShortName;
UCHAR ShortNameBuffer[12];
UNICODE_STRING UniTunneledShortName;
WCHAR UniTunneledShortNameBuffer[12];
UNICODE_STRING UniTunneledLongName;
WCHAR UniTunneledLongNameBuffer[26];
LARGE_INTEGER TunneledCreationTime;
ULONG TunneledDataSize;
BOOLEAN HaveTunneledInformation;
BOOLEAN UsingTunneledLfn = FALSE;
PUNICODE_STRING RealUnicodeName;
//
// The following variables are for abnormal termination
//
PDIRENT UnwindDirent = NULL;
PFCB UnwindFcb = NULL;
BOOLEAN UnwindAllocation = FALSE;
BOOLEAN CountsIncremented = FALSE;
PCCB UnwindCcb = NULL;
ULONG LocalAbnormalTermination = FALSE;
#if (NTDDI_VERSION < NTDDI_WIN7)
UNREFERENCED_PARAMETER( OpenRequiringOplock );
#endif
PAGED_CODE();
DebugTrace(+1, Dbg, "FatCreateNewFile...\n", 0);
ShortName.Length = 0;
ShortName.MaximumLength = sizeof(ShortNameBuffer);
ShortName.Buffer = (PCHAR)&ShortNameBuffer[0];
UniTunneledShortName.Length = 0;
UniTunneledShortName.MaximumLength = sizeof(UniTunneledShortNameBuffer);
UniTunneledShortName.Buffer = &UniTunneledShortNameBuffer[0];
UniTunneledLongName.Length = 0;
UniTunneledLongName.MaximumLength = sizeof(UniTunneledLongNameBuffer);
UniTunneledLongName.Buffer = &UniTunneledLongNameBuffer[0];
EaHandle = 0;
//
// We fail this operation if the caller doesn't understand Ea's.
//
if (NoEaKnowledge
&& EaLength > 0) {
Iosb.Status = STATUS_ACCESS_DENIED;
DebugTrace(-1, Dbg, "FatCreateNewFile -> Iosb.Status = %08lx\n", Iosb.Status);
return Iosb;
}
//
// DeleteOnClose and ReadOnly are not compatible.
//
if (DeleteOnClose && FlagOn(FileAttributes, FAT_DIRENT_ATTR_READ_ONLY)) {
Iosb.Status = STATUS_CANNOT_DELETE;
return Iosb;
}
//
// Look in the tunnel cache for names and timestamps to restore
//
TunneledDataSize = sizeof(LARGE_INTEGER);
HaveTunneledInformation = FsRtlFindInTunnelCache( &Vcb->Tunnel,
FatDirectoryKey(ParentDcb),
UnicodeName,
&UniTunneledShortName,
&UniTunneledLongName,
&TunneledDataSize,
&TunneledCreationTime );
NT_ASSERT(TunneledDataSize == sizeof(LARGE_INTEGER));
//
// Now get the names that we will be using.
//
FatSelectNames( IrpContext,
ParentDcb,
OemName,
UnicodeName,
&ShortName,
(HaveTunneledInformation? &UniTunneledShortName : NULL),
&AllLowerComponent,
&AllLowerExtension,
&CreateLfn );
//
// If we are not in Chicago mode, ignore the magic bits.
//
RealUnicodeName = UnicodeName;
if (!FatData.ChicagoMode) {
AllLowerComponent = FALSE;
AllLowerExtension = FALSE;
CreateLfn = FALSE;
} else {
//
// If the Unicode name was legal for a short name and we got
// a tunneling hit which had a long name associated which is
// avaliable for use, use it.
//
if (!CreateLfn &&
UniTunneledLongName.Length &&
!FatLfnDirentExists(IrpContext, ParentDcb, &UniTunneledLongName, LfnBuffer)) {
UsingTunneledLfn = TRUE;
CreateLfn = TRUE;
RealUnicodeName = &UniTunneledLongName;
//
// Short names are always upcase if an LFN exists
//
AllLowerComponent = FALSE;
AllLowerExtension = FALSE;
}
}
//
// Create/allocate a new dirent
//
DirentsNeeded = CreateLfn ? FAT_LFN_DIRENTS_NEEDED(RealUnicodeName) + 1 : 1;
DirentByteOffset = FatCreateNewDirent( IrpContext,
ParentDcb,
DirentsNeeded,
FALSE );
try {
FatPrepareWriteDirectoryFile( IrpContext,
ParentDcb,
DirentByteOffset,
sizeof(DIRENT),
&DirentBcb,
&Dirent,
FALSE,
TRUE,
&Iosb.Status );
NT_ASSERT( NT_SUCCESS( Iosb.Status ) );
UnwindDirent = Dirent;
//
// Deal with the special case of an LFN + Dirent structure crossing
// a page boundry.
//
if ((DirentByteOffset / PAGE_SIZE) !=
((DirentByteOffset + (DirentsNeeded - 1) * sizeof(DIRENT)) / PAGE_SIZE)) {
SecondPageBcb;
SecondPageOffset;
SecondPageDirent;
SecondPageOffset = (DirentByteOffset & ~(PAGE_SIZE - 1)) + PAGE_SIZE;
BytesInFirstPage = SecondPageOffset - DirentByteOffset;
DirentsInFirstPage = BytesInFirstPage / sizeof(DIRENT);
FatPrepareWriteDirectoryFile( IrpContext,
ParentDcb,
SecondPageOffset,
sizeof(DIRENT),
&SecondPageBcb,
&SecondPageDirent,
FALSE,
TRUE,
&Iosb.Status );
NT_ASSERT( NT_SUCCESS( Iosb.Status ) );
FirstPageDirent = Dirent;
Dirent = FsRtlAllocatePoolWithTag( PagedPool,
DirentsNeeded * sizeof(DIRENT),
TAG_DIRENT );
DirentFromPool = TRUE;
}
//
// Bump up Dirent and DirentByteOffset
//
ShortDirent = Dirent + DirentsNeeded - 1;
ShortDirentByteOffset = DirentByteOffset +
(DirentsNeeded - 1) * sizeof(DIRENT);
NT_ASSERT( NT_SUCCESS( Iosb.Status ));
//
// Fill in the fields of the dirent.
//
FatConstructDirent( IrpContext,
ShortDirent,
&ShortName,
AllLowerComponent,
AllLowerExtension,
CreateLfn ? RealUnicodeName : NULL,
(FileAttributes | FILE_ATTRIBUTE_ARCHIVE),
TRUE,
(HaveTunneledInformation ? &TunneledCreationTime : NULL) );
//
// If the dirent crossed pages, we have to do some real gross stuff.
//
if (DirentFromPool) {
RtlCopyMemory( FirstPageDirent, Dirent, BytesInFirstPage );
RtlCopyMemory( SecondPageDirent,
Dirent + DirentsInFirstPage,
DirentsNeeded*sizeof(DIRENT) - BytesInFirstPage );
ShortDirent = SecondPageDirent + (DirentsNeeded - DirentsInFirstPage) - 1;
}
//
// Create a new Fcb for the file. Once the Fcb is created we
// will not need to unwind dirent because delete dirent will
// now do the work.
//
Fcb = UnwindFcb = FatCreateFcb( IrpContext,
Vcb,
ParentDcb,
DirentByteOffset,
ShortDirentByteOffset,
ShortDirent,
CreateLfn ? RealUnicodeName : NULL,
CreateLfn ? RealUnicodeName : NULL,
IsPagingFile,
FALSE );
UnwindDirent = NULL;
#if (NTDDI_VERSION >= NTDDI_WIN7)
//
// The next three FsRtl calls are for oplock work. We deliberately
// do these here so that if either call fails we will be able to
// clean up without adding a bunch of code to unwind counts, fix
// the file object, etc.
//
//
// Let's make sure that if the caller provided an oplock key that it
// gets stored in the file object.
//
Iosb.Status = FsRtlCheckOplockEx( FatGetFcbOplock(Fcb),
IrpContext->OriginatingIrp,
OPLOCK_FLAG_OPLOCK_KEY_CHECK_ONLY,
NULL,
NULL,
NULL );
//
// If the caller wants atomic create-with-oplock semantics, tell
// the oplock package. We haven't incremented the Fcb's UncleanCount
// for this create yet, so add that in on the call.
//
if (OpenRequiringOplock &&
(Iosb.Status == STATUS_SUCCESS)) {
Iosb.Status = FsRtlOplockFsctrl( FatGetFcbOplock(Fcb),
IrpContext->OriginatingIrp,
(Fcb->UncleanCount + 1) );
}
#endif
#if (NTDDI_VERSION >= NTDDI_WIN8)
//
// Break parent directory oplock. Directory oplock breaks are always
// advisory, so we will never block/get STATUS_PENDING here. On the
// off chance this fails with INSUFFICIENT_RESOURCES we do it here
// where we can still tolerate a failure.
//
if (Iosb.Status == STATUS_SUCCESS) {
Iosb.Status = FsRtlCheckOplockEx( FatGetFcbOplock(ParentDcb),
IrpContext->OriginatingIrp,
OPLOCK_FLAG_PARENT_OBJECT,
NULL,
NULL,
NULL );
}
//
// Get out if any of the oplock calls failed. We raise to provoke
// abnormal termination and ensure that the newly-created Fcb gets
// deleted.
//
if (Iosb.Status != STATUS_SUCCESS) {
FatRaiseStatus( IrpContext, Iosb.Status );
}
#endif
//
// If this is a temporary file, note it in the FcbState
//
if (TemporaryFile) {
SetFlag( Fcb->FcbState, FCB_STATE_TEMPORARY );
}
//
// Add some initial file allocation
//
FatAddFileAllocation( IrpContext, Fcb, FileObject, AllocationSize );
UnwindAllocation = TRUE;
Fcb->FcbState |= FCB_STATE_TRUNCATE_ON_CLOSE;
//
// Tentatively add the new Ea's
//
if ( EaLength > 0 ) {
FatCreateEa( IrpContext,
Fcb->Vcb,
(PUCHAR) EaBuffer,
EaLength,
&Fcb->ShortName.Name.Oem,
&EaHandle );
}
if (!FatIsFat32(Fcb->Vcb)) {
ShortDirent->ExtendedAttributes = EaHandle;
}
//
// Initialize the LongFileName right now so that FatNotify
// below won't have to.
//
if (Fcb->FullFileName.Buffer == NULL) {
FatSetFullNameInFcb( IrpContext, Fcb, RealUnicodeName );
}
//
// Setup the context and section object pointers, and update
// our reference counts
//
FatSetFileObject( FileObject,
UserFileOpen,
Fcb,
UnwindCcb = FatCreateCcb( IrpContext ));
FileObject->SectionObjectPointer = &Fcb->NonPaged->SectionObjectPointers;
//
// We call the notify package to report that the
// we added a file.
//
FatNotifyReportChange( IrpContext,
Vcb,
Fcb,
FILE_NOTIFY_CHANGE_FILE_NAME,
FILE_ACTION_ADDED );
//
// Setup our share access
//
IoSetShareAccess( *DesiredAccess,
ShareAccess,
FileObject,
&Fcb->ShareAccess );
Fcb->UncleanCount += 1;
Fcb->OpenCount += 1;
if (FlagOn(FileObject->Flags, FO_NO_INTERMEDIATE_BUFFERING)) {
Fcb->NonCachedUncleanCount += 1;
}
Vcb->OpenFileCount += 1;
if (IsFileObjectReadOnly(FileObject)) { Vcb->ReadOnlyCount += 1; }
CountsIncremented = TRUE;
//
// And set our return status
//
Iosb.Status = STATUS_SUCCESS;
Iosb.Information = FILE_CREATED;
if ( NT_SUCCESS(Iosb.Status) ) {
//
// Mark the DeleteOnClose bit if the operation was successful.
//
if ( DeleteOnClose ) {
SetFlag( UnwindCcb->Flags, CCB_FLAG_DELETE_ON_CLOSE );
}
//
// Mark the OpenedByShortName bit if the operation was successful.
// If we created an Lfn, we have some sort of generated short name
// and thus don't consider ourselves to have opened it - though we
// may have had a case mix Lfn "Foo.bar" and generated "FOO.BAR"
//
// Unless, of course, we wanted to create a short name and hit an
// associated Lfn in the tunnel cache
//
if ( !CreateLfn && !UsingTunneledLfn ) {
SetFlag( UnwindCcb->Flags, CCB_FLAG_OPENED_BY_SHORTNAME );
}
//
// Mark the ManageVolumeAccess bit if the privilege is held.
//
if (FatCheckManageVolumeAccess( IrpContext,
IrpSp->Parameters.Create.SecurityContext->AccessState,
(KPROCESSOR_MODE)( FlagOn( IrpSp->Flags, SL_FORCE_ACCESS_CHECK ) ?
UserMode :
IrpContext->OriginatingIrp->RequestorMode ))) {
SetFlag( UnwindCcb->Flags, CCB_FLAG_MANAGE_VOLUME_ACCESS );
}
}
} finally {
DebugUnwind( FatCreateNewFile );
if (UniTunneledLongName.Buffer != UniTunneledLongNameBuffer) {
//
// Tunneling package grew the buffer from pool
//
ExFreePool( UniTunneledLongName.Buffer );
}
//
// If this is an abnormal termination then undo our work.
//
// The extra exception handling here is complex. We've got
// two places here where an exception can be thrown again.
//
LocalAbnormalTermination = AbnormalTermination();
if (LocalAbnormalTermination) {
if (CountsIncremented) {
Fcb->UncleanCount -= 1;
Fcb->OpenCount -= 1;
if (FlagOn(FileObject->Flags, FO_NO_INTERMEDIATE_BUFFERING)) {
Fcb->NonCachedUncleanCount -= 1;
}
Vcb->OpenFileCount -= 1;
if (IsFileObjectReadOnly(FileObject)) { Vcb->ReadOnlyCount -= 1; }
}
if (UnwindFcb == NULL) {
NT_ASSERT( (ParentDcb->Specific.Dcb.UnusedDirentVbo == 0xffffffff) ||
RtlAreBitsSet( &ParentDcb->Specific.Dcb.FreeDirentBitmap,
DirentByteOffset / sizeof(DIRENT),
DirentsNeeded ) );
RtlClearBits( &ParentDcb->Specific.Dcb.FreeDirentBitmap,
DirentByteOffset / sizeof(DIRENT),
DirentsNeeded );
}
//
// Mark the dirents deleted. The code is complex because of
// dealing with an LFN that crosses a page boundary.
//
if (UnwindDirent != NULL) {
ULONG i;
for (i = 0; i < DirentsNeeded; i++) {
if (DirentFromPool == FALSE) {
//
// Simple case.
//
Dirent[i].FileName[0] = FAT_DIRENT_DELETED;
} else {
//
// If the second CcPreparePinWrite failed, we have
// to stop early.
//
if ((SecondPageBcb == NULL) &&
(i == DirentsInFirstPage)) {
break;
}
//
// Now conditionally update either page.
//
if (i < DirentsInFirstPage) {
FirstPageDirent[i].FileName[0] = FAT_DIRENT_DELETED;
} else {
SecondPageDirent[i - DirentsInFirstPage].FileName[0] = FAT_DIRENT_DELETED;
}
}
}
}
}
//
// We must handle exceptions in the following fragments and plow on with the
// unwind of this create operation. This is basically inverted from the
// previous state of the code. Since AbnormalTermination() changes when we
// enter a new enclosure, we cached the original state ...
//
try {
if (LocalAbnormalTermination) {
if (UnwindAllocation) {
FatTruncateFileAllocation( IrpContext, Fcb, 0 );
}
}
} finally {
try {
if (LocalAbnormalTermination) {
if (UnwindFcb != NULL) {
FatDeleteDirent( IrpContext, UnwindFcb, NULL, TRUE );
}
}
} finally {
if (LocalAbnormalTermination) {
if (UnwindFcb != NULL) {
if (ARGUMENT_PRESENT( FileObject )) {
FileObject->SectionObjectPointer = NULL;
}
FatDeleteFcb( IrpContext, &UnwindFcb );
}
if (UnwindCcb != NULL) { FatDeleteCcb( IrpContext, &UnwindCcb ); }
}
//
// This is the normal cleanup code.
//
FatUnpinBcb( IrpContext, DirentBcb );
FatUnpinBcb( IrpContext, SecondPageBcb );
if (DirentFromPool) {
ExFreePool( Dirent );
}
}
}
DebugTrace(-1, Dbg, "FatCreateNewFile -> Iosb.Status = %08lx\n", Iosb.Status);
}
return Iosb;
}
//
// Internal support routine
//
_Requires_lock_held_(_Global_critical_region_)
IO_STATUS_BLOCK
FatSupersedeOrOverwriteFile (
_In_ PIRP_CONTEXT IrpContext,
_Inout_ PFILE_OBJECT FileObject,
_Inout_ PFCB Fcb,
_In_ ULONG AllocationSize,
_In_ PFILE_FULL_EA_INFORMATION EaBuffer,
_In_ ULONG EaLength,
_In_ USHORT FileAttributes,
_In_ ULONG CreateDisposition,
_In_ BOOLEAN NoEaKnowledge
)
/*++
Routine Description:
This routine performs a file supersede or overwrite operation.
Arguments:
FileObject - Supplies a pointer to the file object
Fcb - Supplies a pointer to the Fcb
AllocationSize - Supplies an initial allocation size
EaBuffer - Supplies the Ea set for the superseded/overwritten file
EaLength - Supplies the length, in bytes, of EaBuffer
FileAttributes - Supplies the supersede/overwrite file attributes
CreateDisposition - Supplies the create disposition for the file
It must be either supersede, overwrite, or overwrite if.
NoEaKnowledge - This opener doesn't understand Ea's and we fail this
open if the file has NeedEa's.
Return Value:
IO_STATUS_BLOCK - Returns the completion status for the operation
--*/
{
IO_STATUS_BLOCK Iosb = {0};
PDIRENT Dirent;
PBCB DirentBcb;
USHORT EaHandle = 0;
BOOLEAN EaChange = FALSE;
BOOLEAN ReleasePaging = FALSE;
PCCB Ccb;
ULONG NotifyFilter;
ULONG HeaderSize = 0;
LARGE_INTEGER AllocSize = {0};
//
// The following variables are for abnormal termination
//
PCCB UnwindCcb = NULL;
USHORT UnwindEa = 0;
PAGED_CODE();
DebugTrace(+1, Dbg, "FatSupersedeOrOverwriteFile...\n", 0);
DirentBcb = NULL;
//
// We fail this operation if the caller doesn't understand Ea's.
//
if (NoEaKnowledge
&& EaLength > 0) {
Iosb.Status = STATUS_ACCESS_DENIED;
DebugTrace(-1, Dbg, "FatSupersedeOrOverwriteFile -> Iosb.Status = %08lx\n", Iosb.Status);
return Iosb;
}
try {
//
// Before we actually truncate, check to see if the purge
// is going to fail.
//
if (!MmCanFileBeTruncated( &Fcb->NonPaged->SectionObjectPointers,
&FatLargeZero )) {
try_return( Iosb.Status = STATUS_USER_MAPPED_FILE );
}
//
// Setup the context and section object pointers, and update
// our reference counts
//
FatSetFileObject( FileObject,
UserFileOpen,
Fcb,
Ccb = UnwindCcb = FatCreateCcb( IrpContext ));
FileObject->SectionObjectPointer = &Fcb->NonPaged->SectionObjectPointers;
//
// Since this is an supersede/overwrite, purge the section so
// that mappers will see zeros. We set the CREATE_IN_PROGRESS flag
// to prevent the Fcb from going away out from underneath us.
//
SetFlag(Fcb->Vcb->VcbState, VCB_STATE_FLAG_CREATE_IN_PROGRESS);
CcPurgeCacheSection( &Fcb->NonPaged->SectionObjectPointers, NULL, 0, FALSE );
//
// Tentatively add the new Ea's
//
if (EaLength > 0) {
FatCreateEa( IrpContext,
Fcb->Vcb,
(PUCHAR) EaBuffer,
EaLength,
&Fcb->ShortName.Name.Oem,
&EaHandle );
UnwindEa = EaHandle;
EaChange = TRUE;
}
#if (NTDDI_VERSION >= NTDDI_WIN8)
//
// Break parent directory oplock. Directory oplock breaks are always
// advisory, so we will never block/get STATUS_PENDING here. On the
// off chance this fails with INSUFFICIENT_RESOURCES we do it here
// where we can still tolerate a failure.
//
Iosb.Status = FsRtlCheckOplockEx( FatGetFcbOplock(Fcb->ParentDcb),
IrpContext->OriginatingIrp,
OPLOCK_FLAG_PARENT_OBJECT,
NULL,
NULL,
NULL );
if (Iosb.Status != STATUS_SUCCESS) {
FatRaiseStatus( IrpContext, Iosb.Status );
}
#endif
//
// Now set the new allocation size, we do that by first
// zeroing out the current file size. Then we truncate and
// allocate up to the new allocation size
//
(VOID)ExAcquireResourceExclusiveLite( Fcb->Header.PagingIoResource, TRUE );
ReleasePaging = TRUE;
Fcb->Header.FileSize.LowPart = 0;
Fcb->Header.ValidDataLength.LowPart = 0;
Fcb->ValidDataToDisk = 0;
//
// Validate that the allocation size will work.
//
AllocSize.QuadPart = AllocationSize;
if (!FatIsIoRangeValid( Fcb->Vcb, AllocSize, 0 )) {
DebugTrace(-1, Dbg, "Illegal allocation size\n", 0);
FatRaiseStatus( IrpContext, STATUS_DISK_FULL );
}
//
// Tell the cache manager the size went to zero
// This call is unconditional, because MM always wants to know.
//
CcSetFileSizes( FileObject,
(PCC_FILE_SIZES)&Fcb->Header.AllocationSize );
FatTruncateFileAllocation( IrpContext, Fcb, AllocationSize+HeaderSize );
ExReleaseResourceLite( Fcb->Header.PagingIoResource );
ReleasePaging = FALSE;
FatAddFileAllocation( IrpContext, Fcb, FileObject, AllocationSize+HeaderSize );
Fcb->FcbState |= FCB_STATE_TRUNCATE_ON_CLOSE;
//
// Modify the attributes and time of the file, by first reading
// in the dirent for the file and then updating its attributes
// and time fields. Note that for supersede we replace the file
// attributes as opposed to adding to them.
//
FatGetDirentFromFcbOrDcb( IrpContext,
Fcb,
FALSE,
&Dirent,
&DirentBcb );
//
// We should get the dirent since this Fcb is in good condition, verified as
// we crawled down the prefix tree.
//
// Update the appropriate dirent fields, and the fcb fields
//
Dirent->FileSize = 0;
FileAttributes |= FILE_ATTRIBUTE_ARCHIVE;
if (CreateDisposition == FILE_SUPERSEDE) {
Dirent->Attributes = (UCHAR)FileAttributes;
} else {
Dirent->Attributes |= (UCHAR)FileAttributes;
}
Fcb->DirentFatFlags = Dirent->Attributes;
KeQuerySystemTime( &Fcb->LastWriteTime );
(VOID)FatNtTimeToFatTime( IrpContext,
&Fcb->LastWriteTime,
TRUE,
&Dirent->LastWriteTime,
NULL );
if (FatData.ChicagoMode) {
Dirent->LastAccessDate = Dirent->LastWriteTime.Date;
}
NotifyFilter = FILE_NOTIFY_CHANGE_LAST_WRITE
| FILE_NOTIFY_CHANGE_ATTRIBUTES
| FILE_NOTIFY_CHANGE_SIZE;
//
// And now delete the previous Ea set if there was one.
//
if (!FatIsFat32(Fcb->Vcb) && Dirent->ExtendedAttributes != 0) {
//
// **** SDK fix, we won't fail this if there is
// an error in the Ea's, we'll just leave
// the orphaned Ea's in the file.
//
EaChange = TRUE;
try {
FatDeleteEa( IrpContext,
Fcb->Vcb,
Dirent->ExtendedAttributes,
&Fcb->ShortName.Name.Oem );
} except( EXCEPTION_EXECUTE_HANDLER ) {
FatResetExceptionState( IrpContext );
}
}
//
// Update the extended attributes handle in the dirent.
//
if (EaChange) {
NT_ASSERT(!FatIsFat32(Fcb->Vcb));
Dirent->ExtendedAttributes = EaHandle;
NotifyFilter |= FILE_NOTIFY_CHANGE_EA;
}
//
// Now update the dirent to the new ea handle and set the bcb dirty
// Once we do this we can no longer back out the Ea
//
FatSetDirtyBcb( IrpContext, DirentBcb, Fcb->Vcb, TRUE );
UnwindEa = 0;
//
// Indicate that the Eas for this file have changed.
//
Ccb->EaModificationCount += Fcb->EaModificationCount;
//
// Check to see if we need to notify outstanding Irps for full
// changes only (i.e., we haven't added, deleted, or renamed the file).
//
FatNotifyReportChange( IrpContext,
Fcb->Vcb,
Fcb,
NotifyFilter,
FILE_ACTION_MODIFIED );
//
// And set our status to success
//
Iosb.Status = STATUS_SUCCESS;
if (CreateDisposition == FILE_SUPERSEDE) {
Iosb.Information = FILE_SUPERSEDED;
} else {
Iosb.Information = FILE_OVERWRITTEN;
}
try_exit: NOTHING;
} finally {
DebugUnwind( FatSupersedeOfOverwriteFile );
if (ReleasePaging) { ExReleaseResourceLite( Fcb->Header.PagingIoResource ); }
//
// If this is an abnormal termination then undo our work.
//
if (AbnormalTermination()) {
if (UnwindEa != 0) { FatDeleteEa( IrpContext, Fcb->Vcb, UnwindEa, &Fcb->ShortName.Name.Oem ); }
if (UnwindCcb != NULL) { FatDeleteCcb( IrpContext, &UnwindCcb ); }
}
FatUnpinBcb( IrpContext, DirentBcb );
ClearFlag(Fcb->Vcb->VcbState, VCB_STATE_FLAG_CREATE_IN_PROGRESS);
DebugTrace(-1, Dbg, "FatSupersedeOrOverwriteFile -> Iosb.Status = %08lx\n", Iosb.Status);
}
return Iosb;
}
VOID
FatSetFullNameInFcb (
_In_ PIRP_CONTEXT IrpContext,
_Inout_ PFCB Fcb,
_In_ PUNICODE_STRING FinalName
)
/*++
Routine Description:
This routine attempts a quick form of the full FatSetFullFileNameInFcb
operation.
NOTE: this routine is probably not worth the code duplication involved,
and is not equipped to handle the cases where the parent doesn't have
the full name set up.
Arguments:
Fcb - Supplies a pointer to the Fcb
FinalName - Supplies the last component of the path to this Fcb's dirent
Return Value:
None. May silently fail.
--*/
{
PAGED_CODE();
UNREFERENCED_PARAMETER( IrpContext );
NT_ASSERT( Fcb->FullFileName.Buffer == NULL );
//
// Prefer the ExactCaseLongName of the file for this operation, if set. In
// this way we avoid building the fullname with a short filename. Several
// operations assume this - the FinalNameLength in particular is the Lfn
// (if existant) length, and we use this to crack the fullname in paths
// such as the FsRtlNotify caller.
//
// If the caller specified a particular name and it is short, it is the
// case that the long name was set up.
//
if (Fcb->ExactCaseLongName.Buffer) {
NT_ASSERT( Fcb->ExactCaseLongName.Length != 0 );
FinalName = &Fcb->ExactCaseLongName;
}
//
// Special case the root.
//
if (NodeType(Fcb->ParentDcb) == FAT_NTC_ROOT_DCB) {
Fcb->FullFileName.Length =
Fcb->FullFileName.MaximumLength = sizeof(WCHAR) + FinalName->Length;
Fcb->FullFileName.Buffer = FsRtlAllocatePoolWithTag( PagedPool,
Fcb->FullFileName.Length,
TAG_FILENAME_BUFFER );
Fcb->FullFileName.Buffer[0] = L'\\';
RtlCopyMemory( &Fcb->FullFileName.Buffer[1],
&FinalName->Buffer[0],
FinalName->Length );
} else {
PUNICODE_STRING Prefix;
Prefix = &Fcb->ParentDcb->FullFileName;
//
// It is possible our parent's full filename is not set. Simply fail
// this attempt.
//
if (Prefix->Buffer == NULL) {
return;
}
Fcb->FullFileName.Length =
Fcb->FullFileName.MaximumLength = Prefix->Length + sizeof(WCHAR) + FinalName->Length;
Fcb->FullFileName.Buffer = FsRtlAllocatePoolWithTag( PagedPool,
Fcb->FullFileName.Length,
TAG_FILENAME_BUFFER );
RtlCopyMemory( &Fcb->FullFileName.Buffer[0],
&Prefix->Buffer[0],
Prefix->Length );
Fcb->FullFileName.Buffer[Prefix->Length / sizeof(WCHAR)] = L'\\';
RtlCopyMemory( &Fcb->FullFileName.Buffer[(Prefix->Length / sizeof(WCHAR)) + 1],
&FinalName->Buffer[0],
FinalName->Length );
}
}
NTSTATUS
FatCheckSystemSecurityAccess (
_In_ PIRP_CONTEXT IrpContext
)
{
PACCESS_STATE AccessState;
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( IrpContext->OriginatingIrp );
PAGED_CODE();
//
// We check if the caller wants ACCESS_SYSTEM_SECURITY access on this
// object and fail the request if he does.
//
NT_ASSERT( IrpSp->Parameters.Create.SecurityContext != NULL );
AccessState = IrpSp->Parameters.Create.SecurityContext->AccessState;
//
// Check if the remaining privilege includes ACCESS_SYSTEM_SECURITY.
//
if (FlagOn( AccessState->RemainingDesiredAccess, ACCESS_SYSTEM_SECURITY )) {
if (!SeSinglePrivilegeCheck( FatSecurityPrivilege,
UserMode )) {
return STATUS_ACCESS_DENIED;
}
//
// Move this privilege from the Remaining access to Granted access.
//
ClearFlag( AccessState->RemainingDesiredAccess, ACCESS_SYSTEM_SECURITY );
SetFlag( AccessState->PreviouslyGrantedAccess, ACCESS_SYSTEM_SECURITY );
}
return STATUS_SUCCESS;
}
NTSTATUS
FatCheckShareAccess (
_In_ PIRP_CONTEXT IrpContext,
_In_ PFILE_OBJECT FileObject,
_In_ PFCB FcbOrDcb,
_In_ PACCESS_MASK DesiredAccess,
_In_ ULONG ShareAccess
)
/*++
Routine Description:
This routine checks conditions that may result in a sharing violation.
Arguments:
FileObject - Pointer to the file object of the current open request.
FcbOrDcb - Supplies a pointer to the Fcb/Dcb.
DesiredAccess - Desired access of current open request.
ShareAccess - Shared access requested by current open request.
Return Value:
If the accessor has access to the file, STATUS_SUCCESS is returned.
Otherwise, STATUS_SHARING_VIOLATION is returned.
--*/
{
PAGED_CODE();
#if (NTDDI_VERSION >= NTDDI_VISTA)
//
// Do an extra test for writeable user sections if the user did not allow
// write sharing - this is neccessary since a section may exist with no handles
// open to the file its based against.
//
if ((NodeType( FcbOrDcb ) == FAT_NTC_FCB) &&
!FlagOn( ShareAccess, FILE_SHARE_WRITE ) &&
FlagOn( *DesiredAccess, FILE_EXECUTE | FILE_READ_DATA | FILE_WRITE_DATA | FILE_APPEND_DATA | DELETE | MAXIMUM_ALLOWED ) &&
MmDoesFileHaveUserWritableReferences( &FcbOrDcb->NonPaged->SectionObjectPointers )) {
return STATUS_SHARING_VIOLATION;
}
#endif
//
// Check if the Fcb has the proper share access.
//
return IoCheckShareAccess( *DesiredAccess,
ShareAccess,
FileObject,
&FcbOrDcb->ShareAccess,
FALSE );
UNREFERENCED_PARAMETER( IrpContext );
}
//
// Lifted from NTFS.
//
NTSTATUS
FatCallSelfCompletionRoutine (
__in PDEVICE_OBJECT DeviceObject,
__in PIRP Irp,
__in PVOID Contxt
)
{
//
// Set the event so that our call will wake up.
//
KeSetEvent( (PKEVENT)Contxt, 0, FALSE );
UNREFERENCED_PARAMETER( DeviceObject );
UNREFERENCED_PARAMETER( Irp );
//
// If we change this return value then FatIoCallSelf needs to reference the
// file object.
//
return STATUS_MORE_PROCESSING_REQUIRED;
}