mirror of
https://github.com/reactos/reactos.git
synced 2024-08-14 15:16:27 +00:00
6961 lines
206 KiB
C
6961 lines
206 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
|
||
NTAPI
|
||
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 );
|
||
|
||
_SEH2_TRY {
|
||
|
||
IrpContext = FatCreateIrpContext( Irp, TRUE );
|
||
|
||
#if (NTDDI_VERSION >= NTDDI_WINTHRESHOLD)
|
||
Status = FatCommonCreateOnNewStack( IrpContext, Irp );
|
||
#else
|
||
Status = FatCommonCreate( IrpContext, Irp );
|
||
#endif
|
||
|
||
} _SEH2_EXCEPT(FatExceptionFilter( IrpContext, _SEH2_GetExceptionInformation() )) {
|
||
|
||
//
|
||
// We had some trouble trying to perform the requested
|
||
// operation, so we'll abort the I/O request with
|
||
// the error status that we get back from the
|
||
// execption code
|
||
//
|
||
|
||
Status = FatProcessException( IrpContext, Irp, _SEH2_GetExceptionCode() );
|
||
ExceptionCompletedIrp = TRUE;
|
||
} _SEH2_END;
|
||
|
||
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)) {
|
||
|
||
#ifdef _MSC_VER
|
||
#pragma prefast( suppress:28159, "this is a serious programming error if it happens" )
|
||
#endif
|
||
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;
|
||
|
||
_SEH2_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.
|
||
//
|
||
|
||
_SEH2_TRY {
|
||
|
||
FatVerifyFcb( IrpContext, Fcb );
|
||
|
||
} _SEH2_EXCEPT( (_SEH2_GetExceptionCode() == STATUS_FILE_INVALID) ?
|
||
EXCEPTION_EXECUTE_HANDLER :
|
||
EXCEPTION_CONTINUE_SEARCH ) {
|
||
|
||
FatResetExceptionState( IrpContext );
|
||
} _SEH2_END;
|
||
|
||
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
|
||
//
|
||
|
||
#ifdef _MSC_VER
|
||
#pragma prefast( suppress:28159, "this is a serious corruption if it happens" )
|
||
#endif
|
||
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 );
|
||
}
|
||
}
|
||
|
||
} _SEH2_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 (_SEH2_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 (_SEH2_AbnormalTermination()) {
|
||
|
||
FatReleaseVcb( IrpContext, Vcb );
|
||
}
|
||
|
||
//
|
||
// Free up any string buffers we allocated
|
||
//
|
||
|
||
FatFreeStringBuffer( &OemFinalName);
|
||
|
||
FatFreeStringBuffer( &UpcasedFinalName);
|
||
|
||
FatFreeStringBuffer( &Lfn);
|
||
} _SEH2_END;
|
||
|
||
//
|
||
// 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.
|
||
//
|
||
|
||
_SEH2_TRY {
|
||
|
||
if (PostIrp) {
|
||
|
||
//
|
||
// If the Irp hasn't already been posted, do it now.
|
||
//
|
||
|
||
if (!OplockPostIrp) {
|
||
|
||
Iosb.Status = FatFsdPostRequest( IrpContext, Irp );
|
||
}
|
||
|
||
} else {
|
||
|
||
FatUnpinRepinnedBcbs( IrpContext );
|
||
}
|
||
|
||
} _SEH2_FINALLY {
|
||
|
||
DebugUnwind( FatCommonCreate-in-FatCompleteRequest );
|
||
|
||
if (_SEH2_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)) {
|
||
|
||
#ifdef _MSC_VER
|
||
#pragma prefast( suppress:28112, "this should be safe" )
|
||
#endif
|
||
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.
|
||
//
|
||
|
||
#ifndef __REACTOS__
|
||
InterlockedCompareExchangePointer( &FatReserveMdl, ReserveMdl, NULL );
|
||
#else
|
||
InterlockedCompareExchangePointer( (void * volatile*)&FatReserveMdl, ReserveMdl, NULL );
|
||
#endif
|
||
|
||
#ifdef _MSC_VER
|
||
#pragma prefast( suppress:28112, "this should be safe" )
|
||
#endif
|
||
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);
|
||
} _SEH2_END;
|
||
|
||
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;
|
||
|
||
#ifndef __REACTOS__
|
||
IO_STATUS_BLOCK Iosb = {0,0};
|
||
#else
|
||
IO_STATUS_BLOCK Iosb = {{0}};
|
||
#endif
|
||
|
||
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);
|
||
|
||
_SEH2_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;
|
||
} _SEH2_FINALLY {
|
||
|
||
DebugUnwind( FatOpenVolume );
|
||
|
||
//
|
||
// If this is an abnormal termination then undo our work
|
||
//
|
||
|
||
if (_SEH2_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);
|
||
} _SEH2_END;
|
||
|
||
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;
|
||
|
||
_SEH2_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;
|
||
} _SEH2_FINALLY {
|
||
|
||
DebugUnwind( FatOpenRootDcb );
|
||
|
||
//
|
||
// If this is an abnormal termination then undo our work
|
||
//
|
||
|
||
if (_SEH2_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);
|
||
} _SEH2_END;
|
||
|
||
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;
|
||
|
||
_SEH2_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;
|
||
} _SEH2_FINALLY {
|
||
|
||
DebugUnwind( FatOpenExistingDcb );
|
||
|
||
//
|
||
// Unpin the Dirent Bcb if pinned.
|
||
//
|
||
|
||
FatUnpinBcb( IrpContext, DirentBcb );
|
||
|
||
//
|
||
// If this is an abnormal termination then undo our work
|
||
//
|
||
|
||
if (_SEH2_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);
|
||
} _SEH2_END;
|
||
|
||
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;
|
||
|
||
_SEH2_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
|
||
//
|
||
|
||
#ifdef _MSC_VER
|
||
#pragma prefast( suppress:28159, "things are seriously wrong if we get here" )
|
||
#endif
|
||
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 );
|
||
|
||
#ifdef _MSC_VER
|
||
#pragma prefast( suppress:28931, "it needs to be there for debug assert" );
|
||
#endif
|
||
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 );
|
||
}
|
||
}
|
||
|
||
|
||
}
|
||
|
||
} _SEH2_FINALLY {
|
||
|
||
DebugUnwind( FatOpenExistingFcb );
|
||
|
||
//
|
||
// Unpin the Dirent Bcb if pinned.
|
||
//
|
||
|
||
FatUnpinBcb( IrpContext, DirentBcb );
|
||
|
||
//
|
||
// If this is an abnormal termination then undo our work
|
||
//
|
||
|
||
if (_SEH2_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);
|
||
} _SEH2_END;
|
||
|
||
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;
|
||
|
||
_SEH2_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;
|
||
} _SEH2_FINALLY {
|
||
|
||
DebugUnwind( FatOpenTargetDirectory );
|
||
|
||
//
|
||
// If this is an abnormal termination then undo our work
|
||
//
|
||
|
||
if (_SEH2_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);
|
||
} _SEH2_END;
|
||
|
||
return Iosb;
|
||
}
|
||
|
||
|
||
|
||
//
|
||
// Internal support routine
|
||
//
|
||
_Success_(return.Status == STATUS_SUCCESS)
|
||
_Requires_lock_held_(_Global_critical_region_)
|
||
IO_STATUS_BLOCK
|
||
#ifdef _MSC_VER
|
||
#pragma warning(suppress:6101) // bug in PREFast means the _Success_ annotation is not correctly applied
|
||
#endif
|
||
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);
|
||
|
||
_SEH2_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;
|
||
} _SEH2_FINALLY {
|
||
|
||
DebugUnwind( FatOpenExistingDirectory );
|
||
|
||
//
|
||
// If this is an abnormal termination then undo our work
|
||
//
|
||
|
||
if (_SEH2_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);
|
||
} _SEH2_END;
|
||
|
||
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);
|
||
|
||
_SEH2_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);
|
||
|
||
#ifdef _MSC_VER
|
||
#pragma prefast( suppress:28159, "things are seriously wrong if we get here" )
|
||
#endif
|
||
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 );
|
||
}
|
||
|
||
}
|
||
}
|
||
|
||
|
||
} _SEH2_FINALLY {
|
||
|
||
DebugUnwind( FatOpenExistingFile );
|
||
|
||
//
|
||
// If this is an abnormal termination then undo our work
|
||
//
|
||
|
||
if (_SEH2_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);
|
||
} _SEH2_END;
|
||
|
||
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 );
|
||
_SEH2_TRY {
|
||
|
||
FatPrepareWriteDirectoryFile( IrpContext,
|
||
ParentDcb,
|
||
DirentByteOffset,
|
||
sizeof(DIRENT),
|
||
&DirentBcb,
|
||
#ifndef __REACTOS__
|
||
&Dirent,
|
||
#else
|
||
(PVOID *)&Dirent,
|
||
#endif
|
||
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,
|
||
#ifndef __REACTOS__
|
||
&SecondPageDirent,
|
||
#else
|
||
(PVOID *)&SecondPageDirent,
|
||
#endif
|
||
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;
|
||
|
||
} _SEH2_EXCEPT(FatExceptionFilter( IrpContext, _SEH2_GetExceptionInformation() )) {
|
||
|
||
//
|
||
// We'll catch all exceptions and handle them below.
|
||
//
|
||
|
||
Iosb.Status = IrpContext->ExceptionStatus;
|
||
} _SEH2_END;
|
||
|
||
//
|
||
// 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 );
|
||
}
|
||
|
||
#ifdef _MSC_VER
|
||
#pragma prefast( suppress: 28924, "prefast thinks this test is redundant, but DCB can be NULL depending on where we raise" )
|
||
#endif
|
||
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 )) {
|
||
|
||
#ifdef _MSC_VER
|
||
#pragma prefast( suppress: 28924, "prefast thinks this test is redundant, but DCB can be NULL depending on where we raise" )
|
||
#endif
|
||
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) {
|
||
|
||
_SEH2_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 );
|
||
|
||
} _SEH2_EXCEPT(FatExceptionFilter( IrpContext, _SEH2_GetExceptionInformation() )) {
|
||
|
||
//
|
||
// We catch all exceptions that Fat catches, but don't do
|
||
// anything with them.
|
||
//
|
||
} _SEH2_END;
|
||
}
|
||
|
||
if (DirectoryFileObject != NULL) {
|
||
|
||
FatSyncUninitializeCacheMap( IrpContext,
|
||
DirectoryFileObject );
|
||
}
|
||
|
||
|
||
_SEH2_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 );
|
||
|
||
} _SEH2_EXCEPT(FatExceptionFilter( IrpContext, _SEH2_GetExceptionInformation() )) {
|
||
|
||
//
|
||
// We catch all exceptions that Fat catches, but don't do
|
||
// anything with them.
|
||
//
|
||
} _SEH2_END;
|
||
|
||
//
|
||
// 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.
|
||
//
|
||
|
||
#ifdef _MSC_VER
|
||
#pragma prefast( suppress: 28924, "prefast thinks this test is redundant, but FileObject can be NULL depending on where we raise" )
|
||
#endif
|
||
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 );
|
||
|
||
_SEH2_TRY {
|
||
|
||
FatPrepareWriteDirectoryFile( IrpContext,
|
||
ParentDcb,
|
||
DirentByteOffset,
|
||
sizeof(DIRENT),
|
||
&DirentBcb,
|
||
#ifndef __REACTOS__
|
||
&Dirent,
|
||
#else
|
||
(PVOID *)&Dirent,
|
||
#endif
|
||
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,
|
||
#ifndef __REACTOS__
|
||
&SecondPageDirent,
|
||
#else
|
||
(PVOID *)&SecondPageDirent,
|
||
#endif
|
||
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 );
|
||
}
|
||
|
||
}
|
||
|
||
|
||
} _SEH2_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 = _SEH2_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 ...
|
||
//
|
||
|
||
_SEH2_TRY {
|
||
|
||
if (LocalAbnormalTermination) {
|
||
if (UnwindAllocation) {
|
||
FatTruncateFileAllocation( IrpContext, Fcb, 0 );
|
||
}
|
||
}
|
||
|
||
} _SEH2_FINALLY {
|
||
|
||
_SEH2_TRY {
|
||
|
||
if (LocalAbnormalTermination) {
|
||
if (UnwindFcb != NULL) {
|
||
FatDeleteDirent( IrpContext, UnwindFcb, NULL, TRUE );
|
||
}
|
||
}
|
||
|
||
} _SEH2_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 );
|
||
}
|
||
|
||
} _SEH2_END;
|
||
} _SEH2_END;
|
||
|
||
DebugTrace(-1, Dbg, "FatCreateNewFile -> Iosb.Status = %08lx\n", Iosb.Status);
|
||
} _SEH2_END;
|
||
|
||
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;
|
||
}
|
||
|
||
_SEH2_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;
|
||
|
||
_SEH2_TRY {
|
||
|
||
FatDeleteEa( IrpContext,
|
||
Fcb->Vcb,
|
||
Dirent->ExtendedAttributes,
|
||
&Fcb->ShortName.Name.Oem );
|
||
|
||
} _SEH2_EXCEPT( EXCEPTION_EXECUTE_HANDLER ) {
|
||
|
||
FatResetExceptionState( IrpContext );
|
||
} _SEH2_END;
|
||
}
|
||
|
||
//
|
||
// 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;
|
||
} _SEH2_FINALLY {
|
||
|
||
DebugUnwind( FatSupersedeOfOverwriteFile );
|
||
|
||
if (ReleasePaging) { ExReleaseResourceLite( Fcb->Header.PagingIoResource ); }
|
||
|
||
//
|
||
// If this is an abnormal termination then undo our work.
|
||
//
|
||
|
||
if (_SEH2_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);
|
||
} _SEH2_END;
|
||
|
||
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;
|
||
}
|