mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
11241 lines
344 KiB
11241 lines
344 KiB
/*++
|
|
|
|
Copyright (c) 1991 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
Create.c
|
|
|
|
Abstract:
|
|
|
|
This module implements the File Create routine for Ntfs called by the
|
|
dispatch driver.
|
|
|
|
Author:
|
|
|
|
Brian Andrew [BrianAn] 10-Dec-1991
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "NtfsProc.h"
|
|
|
|
//
|
|
// The local debug trace level
|
|
//
|
|
|
|
#define Dbg (DEBUG_TRACE_CREATE)
|
|
|
|
//
|
|
// Define a tag for general pool allocations from this module
|
|
//
|
|
|
|
#undef MODULE_POOL_TAG
|
|
#define MODULE_POOL_TAG ('CFtN')
|
|
|
|
//
|
|
// Check for stack usage prior to the create call.
|
|
//
|
|
|
|
#ifdef _X86_
|
|
#define OVERFLOW_CREATE_THRESHHOLD (0x1200)
|
|
#else
|
|
#define OVERFLOW_CREATE_THRESHHOLD (0x1B00)
|
|
#endif // _X86_
|
|
|
|
//
|
|
// Local macros
|
|
//
|
|
|
|
//
|
|
// BOOLEAN
|
|
// NtfsVerifyNameIsDirectory (
|
|
// IN PIRP_CONTEXT IrpContext,
|
|
// IN PUNICODE_STRING AttrName,
|
|
// IN PUNICODE_STRING AttrCodeName
|
|
// )
|
|
//
|
|
|
|
#define NtfsVerifyNameIsDirectory( IC, AN, ACN ) \
|
|
( ( ((ACN)->Length == 0) \
|
|
|| NtfsAreNamesEqual( IC->Vcb->UpcaseTable, ACN, &NtfsIndexAllocation, TRUE )) \
|
|
&& \
|
|
( ((AN)->Length == 0) \
|
|
|| NtfsAreNamesEqual( IC->Vcb->UpcaseTable, AN, &NtfsFileNameIndex, TRUE )))
|
|
|
|
//
|
|
// These are the flags used by the I/O system in deciding whether
|
|
// to apply the share access modes.
|
|
//
|
|
|
|
#define NtfsAccessDataFlags ( \
|
|
FILE_EXECUTE \
|
|
| FILE_READ_DATA \
|
|
| FILE_WRITE_DATA \
|
|
| FILE_APPEND_DATA \
|
|
| DELETE \
|
|
)
|
|
|
|
//
|
|
// Local definitions
|
|
//
|
|
|
|
typedef enum _SHARE_MODIFICATION_TYPE {
|
|
|
|
CheckShareAccess,
|
|
UpdateShareAccess,
|
|
SetShareAccess
|
|
|
|
} SHARE_MODIFICATION_TYPE, *PSHARE_MODIFICATION_TYPE;
|
|
|
|
UNICODE_STRING NtfsVolumeDasd = CONSTANT_UNICODE_STRING ( L"$Volume" );
|
|
|
|
LUID NtfsSecurityPrivilege = { SE_SECURITY_PRIVILEGE, 0 };
|
|
|
|
//
|
|
// Local support routines.
|
|
//
|
|
|
|
NTSTATUS
|
|
NtfsOpenFcbById (
|
|
IN PIRP_CONTEXT IrpContext,
|
|
IN PIRP Irp,
|
|
IN PIO_STACK_LOCATION IrpSp,
|
|
IN PVCB Vcb,
|
|
IN PLCB ParentLcb OPTIONAL,
|
|
IN OUT PFCB *CurrentFcb,
|
|
IN BOOLEAN UseCurrentFcb,
|
|
IN FILE_REFERENCE FileReference,
|
|
IN UNICODE_STRING AttrName,
|
|
IN UNICODE_STRING AttrCodeName,
|
|
IN PVOID NetworkInfo OPTIONAL,
|
|
OUT PSCB *ThisScb,
|
|
OUT PCCB *ThisCcb
|
|
);
|
|
|
|
NTSTATUS
|
|
NtfsOpenExistingPrefixFcb (
|
|
IN PIRP_CONTEXT IrpContext,
|
|
IN PIRP Irp,
|
|
IN PIO_STACK_LOCATION IrpSp,
|
|
IN PFCB ThisFcb,
|
|
IN PLCB Lcb OPTIONAL,
|
|
IN ULONG FullPathNameLength,
|
|
IN UNICODE_STRING AttrName,
|
|
IN UNICODE_STRING AttrCodeName,
|
|
IN BOOLEAN DosOnlyComponent,
|
|
IN BOOLEAN TrailingBackslash,
|
|
IN PVOID NetworkInfo OPTIONAL,
|
|
OUT PSCB *ThisScb,
|
|
OUT PCCB *ThisCcb
|
|
);
|
|
|
|
NTSTATUS
|
|
NtfsOpenTargetDirectory (
|
|
IN PIRP_CONTEXT IrpContext,
|
|
IN PIRP Irp,
|
|
IN PIO_STACK_LOCATION IrpSp,
|
|
IN PFCB ThisFcb,
|
|
IN PLCB ParentLcb OPTIONAL,
|
|
IN OUT PUNICODE_STRING FullPathName,
|
|
IN ULONG FinalNameLength,
|
|
IN BOOLEAN TargetExisted,
|
|
IN BOOLEAN DosOnlyComponent,
|
|
OUT PSCB *ThisScb,
|
|
OUT PCCB *ThisCcb
|
|
);
|
|
|
|
NTSTATUS
|
|
NtfsOpenFile (
|
|
IN PIRP_CONTEXT IrpContext,
|
|
IN PIRP Irp,
|
|
IN PIO_STACK_LOCATION IrpSp,
|
|
IN PSCB ParentScb,
|
|
IN PINDEX_ENTRY IndexEntry,
|
|
IN UNICODE_STRING FullPathName,
|
|
IN UNICODE_STRING FinalName,
|
|
IN UNICODE_STRING AttrName,
|
|
IN UNICODE_STRING AttrCodeName,
|
|
IN BOOLEAN IgnoreCase,
|
|
IN BOOLEAN OpenById,
|
|
IN PQUICK_INDEX QuickIndex,
|
|
IN BOOLEAN DosOnlyComponent,
|
|
IN BOOLEAN TrailingBackslash,
|
|
IN PVOID NetworkInfo OPTIONAL,
|
|
OUT PFCB *CurrentFcb,
|
|
OUT PLCB *LcbForTeardown,
|
|
OUT PSCB *ThisScb,
|
|
OUT PCCB *ThisCcb
|
|
);
|
|
|
|
NTSTATUS
|
|
NtfsCreateNewFile (
|
|
IN PIRP_CONTEXT IrpContext,
|
|
IN PIRP Irp,
|
|
IN PIO_STACK_LOCATION IrpSp,
|
|
IN PSCB ParentScb,
|
|
IN PFILE_NAME FileNameAttr,
|
|
IN UNICODE_STRING FullPathName,
|
|
IN UNICODE_STRING FinalName,
|
|
IN UNICODE_STRING AttrName,
|
|
IN UNICODE_STRING AttrCodeName,
|
|
IN BOOLEAN IgnoreCase,
|
|
IN BOOLEAN OpenById,
|
|
IN BOOLEAN DosOnlyComponent,
|
|
IN BOOLEAN TrailingBackslash,
|
|
OUT PFCB *CurrentFcb,
|
|
OUT PLCB *LcbForTeardown,
|
|
OUT PSCB *ThisScb,
|
|
OUT PCCB *ThisCcb
|
|
);
|
|
|
|
PLCB
|
|
NtfsOpenSubdirectory (
|
|
IN PIRP_CONTEXT IrpContext,
|
|
IN PSCB ParentScb,
|
|
IN UNICODE_STRING Name,
|
|
IN BOOLEAN TraverseAccessCheck,
|
|
OUT PFCB *CurrentFcb,
|
|
OUT PLCB *LcbForTeardown,
|
|
IN PINDEX_ENTRY IndexEntry
|
|
);
|
|
|
|
NTSTATUS
|
|
NtfsOpenAttributeInExistingFile (
|
|
IN PIRP_CONTEXT IrpContext,
|
|
IN PIRP Irp,
|
|
IN PIO_STACK_LOCATION IrpSp,
|
|
IN PLCB ThisLcb OPTIONAL,
|
|
IN PFCB ThisFcb,
|
|
IN ULONG LastFileNameOffset,
|
|
IN UNICODE_STRING AttrName,
|
|
IN ATTRIBUTE_TYPE_CODE AttrTypeCode,
|
|
IN ULONG CcbFlags,
|
|
IN BOOLEAN OpenById,
|
|
IN PVOID NetworkInfo OPTIONAL,
|
|
OUT PSCB *ThisScb,
|
|
OUT PCCB *ThisCcb
|
|
);
|
|
|
|
NTSTATUS
|
|
NtfsOpenExistingAttr (
|
|
IN PIRP_CONTEXT IrpContext,
|
|
IN PIRP Irp,
|
|
IN PIO_STACK_LOCATION IrpSp,
|
|
IN PLCB ThisLcb OPTIONAL,
|
|
IN PFCB ThisFcb,
|
|
IN ULONG LastFileNameOffset,
|
|
IN UNICODE_STRING AttrName,
|
|
IN ATTRIBUTE_TYPE_CODE AttrTypeCode,
|
|
IN ULONG CcbFlags,
|
|
IN BOOLEAN OpenById,
|
|
IN BOOLEAN DirectoryOpen,
|
|
IN PVOID NetworkInfo OPTIONAL,
|
|
OUT PSCB *ThisScb,
|
|
OUT PCCB *ThisCcb
|
|
);
|
|
|
|
NTSTATUS
|
|
NtfsOverwriteAttr (
|
|
IN PIRP_CONTEXT IrpContext,
|
|
IN PIRP Irp,
|
|
IN PIO_STACK_LOCATION IrpSp,
|
|
IN PLCB ThisLcb OPTIONAL,
|
|
IN PFCB ThisFcb,
|
|
IN BOOLEAN Supersede,
|
|
IN ULONG LastFileNameOffset,
|
|
IN UNICODE_STRING AttrName,
|
|
IN ATTRIBUTE_TYPE_CODE AttrTypeCode,
|
|
IN ULONG CcbFlags,
|
|
IN BOOLEAN OpenById,
|
|
OUT PSCB *ThisScb,
|
|
OUT PCCB *ThisCcb
|
|
);
|
|
|
|
NTSTATUS
|
|
NtfsOpenNewAttr (
|
|
IN PIRP_CONTEXT IrpContext,
|
|
IN PIRP Irp,
|
|
IN PIO_STACK_LOCATION IrpSp,
|
|
IN PLCB ThisLcb,
|
|
IN PFCB ThisFcb,
|
|
IN ULONG LastFileNameOffset,
|
|
IN UNICODE_STRING AttrName,
|
|
IN ATTRIBUTE_TYPE_CODE AttrTypeCode,
|
|
IN ULONG CcbFlags,
|
|
IN BOOLEAN LogIt,
|
|
IN BOOLEAN OpenById,
|
|
OUT PSCB *ThisScb,
|
|
OUT PCCB *ThisCcb
|
|
);
|
|
|
|
BOOLEAN
|
|
NtfsParseNameForCreate (
|
|
IN PIRP_CONTEXT IrpContext,
|
|
IN UNICODE_STRING String,
|
|
IN OUT PUNICODE_STRING FileObjectString,
|
|
IN OUT PUNICODE_STRING OriginalString,
|
|
IN OUT PUNICODE_STRING NewNameString,
|
|
OUT PUNICODE_STRING AttrName,
|
|
OUT PUNICODE_STRING AttrCodeName
|
|
);
|
|
|
|
NTSTATUS
|
|
NtfsCheckValidAttributeAccess (
|
|
IN PIO_STACK_LOCATION IrpSp,
|
|
IN PVCB Vcb,
|
|
IN PDUPLICATED_INFORMATION Info OPTIONAL,
|
|
IN UNICODE_STRING AttrName,
|
|
IN UNICODE_STRING AttrCodeName,
|
|
IN BOOLEAN TrailingBackslash,
|
|
OUT PATTRIBUTE_TYPE_CODE AttrTypeCode,
|
|
OUT PULONG CcbFlags,
|
|
OUT PBOOLEAN IndexedAttribute
|
|
);
|
|
|
|
NTSTATUS
|
|
NtfsOpenAttributeCheck (
|
|
IN PIRP_CONTEXT IrpContext,
|
|
IN PIRP Irp,
|
|
IN PIO_STACK_LOCATION IrpSp,
|
|
OUT PSCB *ThisScb,
|
|
OUT PSHARE_MODIFICATION_TYPE ShareModificationType
|
|
);
|
|
|
|
VOID
|
|
NtfsAddEa (
|
|
IN PIRP_CONTEXT IrpContext,
|
|
IN PVCB Vcb,
|
|
IN PFCB ThisFcb,
|
|
IN PFILE_FULL_EA_INFORMATION EaBuffer OPTIONAL,
|
|
IN ULONG EaLength,
|
|
OUT PIO_STATUS_BLOCK Iosb
|
|
);
|
|
|
|
VOID
|
|
NtfsInitializeFcbAndStdInfo (
|
|
IN PIRP_CONTEXT IrpContext,
|
|
IN PFCB ThisFcb,
|
|
IN BOOLEAN Directory,
|
|
IN BOOLEAN Compressed,
|
|
IN ULONG FileAttributes,
|
|
IN PLONGLONG SetCreationTime OPTIONAL
|
|
);
|
|
|
|
VOID
|
|
NtfsCreateAttribute (
|
|
IN PIRP_CONTEXT IrpContext,
|
|
IN PIO_STACK_LOCATION IrpSp,
|
|
IN OUT PFCB ThisFcb,
|
|
IN OUT PSCB ThisScb,
|
|
IN PLCB ThisLcb,
|
|
IN LONGLONG AllocationSize,
|
|
IN BOOLEAN LogIt
|
|
);
|
|
|
|
VOID
|
|
NtfsRemoveDataAttributes (
|
|
IN PIRP_CONTEXT IrpContext,
|
|
IN PFCB ThisFcb,
|
|
IN PLCB ThisLcb OPTIONAL,
|
|
IN PFILE_OBJECT FileObject,
|
|
IN ULONG LastFileNameOffset,
|
|
IN BOOLEAN OpenById
|
|
);
|
|
|
|
VOID
|
|
NtfsReplaceAttribute (
|
|
IN PIRP_CONTEXT IrpContext,
|
|
IN PIO_STACK_LOCATION IrpSp,
|
|
IN PFCB ThisFcb,
|
|
IN PSCB ThisScb,
|
|
IN PLCB ThisLcb,
|
|
IN LONGLONG AllocationSize
|
|
);
|
|
|
|
NTSTATUS
|
|
NtfsOpenAttribute (
|
|
IN PIRP_CONTEXT IrpContext,
|
|
IN PIO_STACK_LOCATION IrpSp,
|
|
IN PVCB Vcb,
|
|
IN PLCB ThisLcb OPTIONAL,
|
|
IN PFCB ThisFcb,
|
|
IN ULONG LastFileNameOffset,
|
|
IN UNICODE_STRING AttrName,
|
|
IN ATTRIBUTE_TYPE_CODE AttrTypeCode,
|
|
IN SHARE_MODIFICATION_TYPE ShareModificationType,
|
|
IN TYPE_OF_OPEN TypeOfOpen,
|
|
IN ULONG CcbFlags,
|
|
IN PVOID NetworkInfo OPTIONAL,
|
|
IN OUT PSCB *ThisScb,
|
|
OUT PCCB *ThisCcb
|
|
);
|
|
|
|
VOID
|
|
NtfsBackoutFailedOpens (
|
|
IN PIRP_CONTEXT IrpContext,
|
|
IN PFILE_OBJECT FileObject,
|
|
IN PFCB ThisFcb,
|
|
IN PSCB ThisScb OPTIONAL,
|
|
IN PCCB ThisCcb OPTIONAL
|
|
);
|
|
|
|
VOID
|
|
NtfsUpdateScbFromMemory (
|
|
IN PIRP_CONTEXT IrpContext,
|
|
IN OUT PSCB Scb,
|
|
IN POLD_SCB_SNAPSHOT ScbSizes
|
|
);
|
|
|
|
VOID
|
|
NtfsOplockPrePostIrp (
|
|
IN PVOID Context,
|
|
IN PIRP Irp
|
|
);
|
|
|
|
NTSTATUS
|
|
NtfsCheckExistingFile (
|
|
IN PIRP_CONTEXT IrpContext,
|
|
IN PIO_STACK_LOCATION IrpSp,
|
|
IN PLCB ThisLcb OPTIONAL,
|
|
IN PFCB ThisFcb,
|
|
IN ULONG CcbFlags
|
|
);
|
|
|
|
NTSTATUS
|
|
NtfsBreakBatchOplock (
|
|
IN PIRP_CONTEXT IrpContext,
|
|
IN PIRP Irp,
|
|
IN PIO_STACK_LOCATION IrpSp,
|
|
IN PFCB ThisFcb,
|
|
IN UNICODE_STRING AttrName,
|
|
IN ATTRIBUTE_TYPE_CODE AttrTypeCode,
|
|
OUT PSCB *ThisScb
|
|
);
|
|
|
|
NTSTATUS
|
|
NtfsCompleteLargeAllocation (
|
|
IN PIRP_CONTEXT IrpContext,
|
|
IN PIRP Irp,
|
|
IN PLCB Lcb,
|
|
IN PSCB Scb,
|
|
IN PCCB Ccb,
|
|
IN BOOLEAN CreateFileCase,
|
|
IN BOOLEAN DeleteOnClose
|
|
);
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(PAGE, NtfsAddEa)
|
|
#pragma alloc_text(PAGE, NtfsBackoutFailedOpens)
|
|
#pragma alloc_text(PAGE, NtfsBreakBatchOplock)
|
|
#pragma alloc_text(PAGE, NtfsCheckExistingFile)
|
|
#pragma alloc_text(PAGE, NtfsCheckValidAttributeAccess)
|
|
#pragma alloc_text(PAGE, NtfsCommonCreate)
|
|
#pragma alloc_text(PAGE, NtfsCommonVolumeOpen)
|
|
#pragma alloc_text(PAGE, NtfsCompleteLargeAllocation)
|
|
#pragma alloc_text(PAGE, NtfsCreateAttribute)
|
|
#pragma alloc_text(PAGE, NtfsCreateNewFile)
|
|
#pragma alloc_text(PAGE, NtfsFsdCreate)
|
|
#pragma alloc_text(PAGE, NtfsInitializeFcbAndStdInfo)
|
|
#pragma alloc_text(PAGE, NtfsNetworkOpenCreate)
|
|
#pragma alloc_text(PAGE, NtfsOpenAttribute)
|
|
#pragma alloc_text(PAGE, NtfsOpenAttributeCheck)
|
|
#pragma alloc_text(PAGE, NtfsOpenAttributeInExistingFile)
|
|
#pragma alloc_text(PAGE, NtfsOpenExistingAttr)
|
|
#pragma alloc_text(PAGE, NtfsOpenExistingPrefixFcb)
|
|
#pragma alloc_text(PAGE, NtfsOpenFcbById)
|
|
#pragma alloc_text(PAGE, NtfsOpenFile)
|
|
#pragma alloc_text(PAGE, NtfsOpenNewAttr)
|
|
#pragma alloc_text(PAGE, NtfsOpenSubdirectory)
|
|
#pragma alloc_text(PAGE, NtfsOpenTargetDirectory)
|
|
#pragma alloc_text(PAGE, NtfsOplockPrePostIrp)
|
|
#pragma alloc_text(PAGE, NtfsOverwriteAttr)
|
|
#pragma alloc_text(PAGE, NtfsParseNameForCreate)
|
|
#pragma alloc_text(PAGE, NtfsRemoveDataAttributes)
|
|
#pragma alloc_text(PAGE, NtfsReplaceAttribute)
|
|
#pragma alloc_text(PAGE, NtfsUpdateScbFromMemory)
|
|
#endif
|
|
|
|
|
|
NTSTATUS
|
|
NtfsFsdCreate (
|
|
IN PVOLUME_DEVICE_OBJECT VolumeDeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine implements the FSD part of Create.
|
|
|
|
Arguments:
|
|
|
|
VolumeDeviceObject - Supplies the volume device object where the
|
|
file exists
|
|
|
|
Irp - Supplies the Irp being processed
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - The FSD status for the IRP
|
|
|
|
--*/
|
|
|
|
{
|
|
TOP_LEVEL_CONTEXT TopLevelContext;
|
|
PTOP_LEVEL_CONTEXT ThreadTopLevelContext;
|
|
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
PIRP_CONTEXT IrpContext;
|
|
|
|
ASSERT_IRP( Irp );
|
|
|
|
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 (VolumeDeviceObject->DeviceObject.Size == (USHORT)sizeof(DEVICE_OBJECT)) {
|
|
|
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
Irp->IoStatus.Information = FILE_OPENED;
|
|
|
|
IoCompleteRequest( Irp, IO_DISK_INCREMENT );
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
DebugTrace( +1, Dbg, ("NtfsFsdCreate\n") );
|
|
|
|
//
|
|
// Call the common Create routine
|
|
//
|
|
|
|
IrpContext = NULL;
|
|
|
|
FsRtlEnterFileSystem();
|
|
|
|
ThreadTopLevelContext = NtfsSetTopLevelIrp( &TopLevelContext, FALSE, FALSE );
|
|
|
|
do {
|
|
|
|
try {
|
|
|
|
//
|
|
// We are either initiating this request or retrying it.
|
|
//
|
|
|
|
if (IrpContext == NULL) {
|
|
|
|
IrpContext = NtfsCreateIrpContext( Irp, CanFsdWait( Irp ) );
|
|
NtfsUpdateIrpContextWithTopLevel( IrpContext, ThreadTopLevelContext );
|
|
|
|
} else if (Status == STATUS_LOG_FILE_FULL) {
|
|
|
|
NtfsCheckpointForLogFileFull( IrpContext );
|
|
}
|
|
|
|
if (FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_DASD_OPEN )) {
|
|
|
|
Status = NtfsCommonVolumeOpen( IrpContext, Irp );
|
|
|
|
} else {
|
|
|
|
Status = NtfsCommonCreate( IrpContext, Irp, NULL );
|
|
}
|
|
break;
|
|
|
|
} except(NtfsExceptionFilter( IrpContext, GetExceptionInformation() )) {
|
|
|
|
//
|
|
// We had some trouble trying to perform the requested
|
|
// operation, so we'll abort the I/O request with
|
|
// the error status that we get back from the
|
|
// exception code
|
|
//
|
|
|
|
Status = NtfsProcessException( IrpContext, Irp, GetExceptionCode() );
|
|
}
|
|
|
|
} while (Status == STATUS_CANT_WAIT ||
|
|
Status == STATUS_LOG_FILE_FULL);
|
|
|
|
if (ThreadTopLevelContext == &TopLevelContext) {
|
|
NtfsRestoreTopLevelIrp( ThreadTopLevelContext );
|
|
}
|
|
|
|
FsRtlExitFileSystem();
|
|
|
|
//
|
|
// And return to our caller
|
|
//
|
|
|
|
DebugTrace( -1, Dbg, ("NtfsFsdCreate -> %08lx\n", Status) );
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
NtfsNetworkOpenCreate (
|
|
IN PIRP Irp,
|
|
OUT PFILE_NETWORK_OPEN_INFORMATION Buffer,
|
|
IN PDEVICE_OBJECT DeviceObject
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine implements the fast open create for path-based queries.
|
|
|
|
Arguments:
|
|
|
|
Irp - Supplies the Irp being processed
|
|
|
|
Buffer - Buffer to return the network query information
|
|
|
|
DeviceObject - Supplies the volume device object where the file exists
|
|
|
|
Return Value:
|
|
|
|
BOOLEAN - Indicates whether or not the fast path could be taken.
|
|
|
|
--*/
|
|
|
|
{
|
|
TOP_LEVEL_CONTEXT TopLevelContext;
|
|
PTOP_LEVEL_CONTEXT ThreadTopLevelContext;
|
|
BOOLEAN Result = TRUE;
|
|
BOOLEAN DasdOpen = FALSE;
|
|
|
|
NTSTATUS Status;
|
|
PIRP_CONTEXT IrpContext = NULL;
|
|
|
|
ASSERT_IRP( Irp );
|
|
|
|
UNREFERENCED_PARAMETER( DeviceObject );
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
//
|
|
// Call the common Create routine
|
|
//
|
|
|
|
FsRtlEnterFileSystem();
|
|
|
|
ThreadTopLevelContext = NtfsSetTopLevelIrp( &TopLevelContext, FALSE, FALSE );
|
|
|
|
try {
|
|
|
|
IrpContext = NtfsCreateIrpContext( Irp, TRUE );
|
|
NtfsUpdateIrpContextWithTopLevel( IrpContext, ThreadTopLevelContext );
|
|
|
|
Status = NtfsCommonCreate( IrpContext, Irp, Buffer );
|
|
|
|
} except(NtfsExceptionFilter( IrpContext, GetExceptionInformation() )) {
|
|
|
|
//
|
|
// Catch the case where someone in attempting this on a DASD open.
|
|
//
|
|
|
|
if ((IrpContext != NULL) &&
|
|
FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_DASD_OPEN )) {
|
|
|
|
DasdOpen = TRUE;
|
|
}
|
|
|
|
//
|
|
// 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
|
|
// exception code. Since there is no Irp the exception package
|
|
// will always deallocate the IrpContext so we won't do
|
|
// any retry in this path.
|
|
//
|
|
|
|
Status = NtfsProcessException( IrpContext, NULL, GetExceptionCode() );
|
|
|
|
//
|
|
// Always fail the DASD case.
|
|
//
|
|
|
|
if (DasdOpen) {
|
|
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Return STATUS_FILE_LOCK_CONFLICT for any retryable error.
|
|
//
|
|
|
|
if ((Status == STATUS_CANT_WAIT) || (Status == STATUS_LOG_FILE_FULL)) {
|
|
|
|
Result = FALSE;
|
|
Status = STATUS_FILE_LOCK_CONFLICT;
|
|
}
|
|
|
|
if (ThreadTopLevelContext == &TopLevelContext) {
|
|
NtfsRestoreTopLevelIrp( ThreadTopLevelContext );
|
|
}
|
|
|
|
FsRtlExitFileSystem();
|
|
|
|
//
|
|
// And return to our caller
|
|
//
|
|
|
|
Irp->IoStatus.Status = Status;
|
|
return Result;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
NtfsCommonCreate (
|
|
IN PIRP_CONTEXT IrpContext,
|
|
IN PIRP Irp,
|
|
OUT PFILE_NETWORK_OPEN_INFORMATION NetworkInfo OPTIONAL
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the common routine for Create called by both the fsd and fsp
|
|
threads. If this open has already been detected to be a volume open then
|
|
take we will take the volume open path instead.
|
|
|
|
Arguments:
|
|
|
|
Irp - Supplies the Irp to process
|
|
|
|
NetworkInfo - Optional buffer to return the queried data for
|
|
NetworkInformation. Its presence indicates that we should not
|
|
do a full open.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - The return status for the operation
|
|
|
|
--*/
|
|
|
|
{
|
|
PIO_STACK_LOCATION IrpSp;
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
PFILE_OBJECT RelatedFileObject;
|
|
|
|
UNICODE_STRING AttrName;
|
|
UNICODE_STRING AttrCodeName;
|
|
|
|
PVCB Vcb;
|
|
|
|
//
|
|
// The following are used to teardown any Lcb/Fcb this
|
|
// routine is responsible for.
|
|
//
|
|
|
|
PLCB LcbForTeardown = NULL;
|
|
|
|
//
|
|
// The following indicate how far down the tree we have scanned.
|
|
//
|
|
|
|
PFCB ParentFcb;
|
|
PLCB CurrentLcb;
|
|
PFCB CurrentFcb = NULL;
|
|
PSCB LastScb = NULL;
|
|
PSCB CurrentScb;
|
|
PLCB NextLcb;
|
|
|
|
BOOLEAN AcquiredVcb = FALSE;
|
|
BOOLEAN DeleteVcb = FALSE;
|
|
|
|
//
|
|
// The following are the results of open operations.
|
|
//
|
|
|
|
PSCB ThisScb = NULL;
|
|
PCCB ThisCcb = NULL;
|
|
|
|
//
|
|
// The following are the in-memory structures associated with
|
|
// the relative file object.
|
|
//
|
|
|
|
TYPE_OF_OPEN RelatedFileObjectTypeOfOpen;
|
|
PFCB RelatedFcb;
|
|
PSCB RelatedScb;
|
|
PCCB RelatedCcb;
|
|
|
|
BOOLEAN DosOnlyComponent = FALSE;
|
|
BOOLEAN CreateFileCase = FALSE;
|
|
BOOLEAN DeleteOnClose = FALSE;
|
|
BOOLEAN TrailingBackslash = FALSE;
|
|
BOOLEAN TraverseAccessCheck;
|
|
BOOLEAN IgnoreCase;
|
|
BOOLEAN OpenFileById;
|
|
UCHAR CreateDisposition;
|
|
|
|
BOOLEAN CheckForValidName;
|
|
BOOLEAN FirstPass;
|
|
|
|
BOOLEAN FoundEntry = FALSE;
|
|
|
|
PFILE_NAME FileNameAttr = NULL;
|
|
USHORT FileNameAttrLength = 0;
|
|
|
|
PINDEX_ENTRY IndexEntry;
|
|
PBCB IndexEntryBcb = NULL;
|
|
|
|
QUICK_INDEX QuickIndex;
|
|
|
|
//
|
|
// The following unicode strings are used to track the names
|
|
// during the open operation. They may point to the same
|
|
// buffer so careful checks must be done at cleanup.
|
|
//
|
|
// OriginalFileName - This is the value to restore to the file
|
|
// object on error cleanup. This will containg the
|
|
// attribute type codes and attribute names if present.
|
|
//
|
|
// FullFileName - This is the constructed string which contains
|
|
// only the name components. It may point to the same
|
|
// buffer as the original name but the length value is
|
|
// adjusted to cut off the attribute code and name.
|
|
//
|
|
// ExactCaseName - This is the version of the full filename
|
|
// exactly as given by the caller. Used to preserve the
|
|
// case given by the caller in the event we do a case
|
|
// insensitive lookup. May point to the same buffer as
|
|
// the original name.
|
|
//
|
|
// RemainingName - This is the portion of the full name still
|
|
// to parse.
|
|
//
|
|
// FinalName - This is the current component of the full name.
|
|
//
|
|
// CaseInsensitiveIndex - This is the offset in the full file
|
|
// where we performed upcasing. We need to restore the
|
|
// exact case on failures and if we are creating a file.
|
|
//
|
|
|
|
OPLOCK_CLEANUP OplockCleanup;
|
|
|
|
PUNICODE_STRING OriginalFileName = &OplockCleanup.OriginalFileName;
|
|
PUNICODE_STRING FullFileName = &OplockCleanup.FullFileName;
|
|
PUNICODE_STRING ExactCaseName = &OplockCleanup.ExactCaseName;
|
|
|
|
UNICODE_STRING RemainingName;
|
|
UNICODE_STRING FinalName;
|
|
ULONG CaseInsensitiveIndex;
|
|
|
|
ASSERT_IRP_CONTEXT( IrpContext );
|
|
ASSERT_IRP( Irp );
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Get the current Irp stack location
|
|
//
|
|
|
|
IrpSp = IoGetCurrentIrpStackLocation( Irp );
|
|
OplockCleanup.FileObject = IrpSp->FileObject;
|
|
|
|
DebugTrace( +1, Dbg, ("NtfsCommonCreate: Entered\n") );
|
|
DebugTrace( 0, Dbg, ("IrpContext = %08lx\n", IrpContext) );
|
|
DebugTrace( 0, Dbg, ("Irp = %08lx\n", Irp) );
|
|
DebugTrace( 0, Dbg, ("->Flags = %08lx\n", Irp->Flags) );
|
|
DebugTrace( 0, Dbg, ("->FileObject = %08lx\n", IrpSp->FileObject) );
|
|
DebugTrace( 0, Dbg, ("->RelatedFileObject = %08lx\n", IrpSp->FileObject->RelatedFileObject) );
|
|
DebugTrace( 0, Dbg, ("->FileName = %Z\n", &IrpSp->FileObject->FileName) );
|
|
DebugTrace( 0, Dbg, ("->AllocationSize = %08lx %08lx\n", Irp->Overlay.AllocationSize.LowPart,
|
|
Irp->Overlay.AllocationSize.HighPart ) );
|
|
DebugTrace( 0, Dbg, ("->EaBuffer = %08lx\n", Irp->AssociatedIrp.SystemBuffer) );
|
|
DebugTrace( 0, Dbg, ("->EaLength = %08lx\n", IrpSp->Parameters.Create.EaLength) );
|
|
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, ("->Directory = %04x\n", FlagOn( IrpSp->Parameters.Create.Options,
|
|
FILE_DIRECTORY_FILE )) );
|
|
DebugTrace( 0, Dbg, ("->NonDirectoryFile = %04x\n", FlagOn( IrpSp->Parameters.Create.Options,
|
|
FILE_NON_DIRECTORY_FILE )) );
|
|
DebugTrace( 0, Dbg, ("->NoIntermediateBuffering = %04x\n", FlagOn( IrpSp->Parameters.Create.Options,
|
|
FILE_NO_INTERMEDIATE_BUFFERING )) );
|
|
DebugTrace( 0, Dbg, ("->CreateDisposition = %04x\n", (IrpSp->Parameters.Create.Options >> 24) & 0x000000ff) );
|
|
DebugTrace( 0, Dbg, ("->IsPagingFile = %04x\n", FlagOn( IrpSp->Flags, SL_OPEN_PAGING_FILE )) );
|
|
DebugTrace( 0, Dbg, ("->OpenTargetDirectory = %04x\n", FlagOn( IrpSp->Flags, SL_OPEN_TARGET_DIRECTORY )) );
|
|
DebugTrace( 0, Dbg, ("->CaseSensitive = %04x\n", FlagOn( IrpSp->Flags, SL_CASE_SENSITIVE )) );
|
|
|
|
//
|
|
// Verify that we can wait and acquire the Vcb exclusively.
|
|
//
|
|
|
|
if (!FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT )) {
|
|
|
|
DebugTrace( 0, Dbg, ("Can't wait in create\n") );
|
|
|
|
Status = NtfsPostRequest( IrpContext, Irp );
|
|
|
|
DebugTrace( -1, Dbg, ("NtfsCommonCreate: Exit -> %08lx\n", Status) );
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// Update the IrpContext with the oplock cleanup structure.
|
|
//
|
|
|
|
IrpContext->Union.OplockCleanup = &OplockCleanup;
|
|
|
|
//
|
|
// Locate the volume device object and Vcb that we are trying to access.
|
|
//
|
|
|
|
Vcb = &((PVOLUME_DEVICE_OBJECT)IrpSp->DeviceObject)->Vcb;
|
|
|
|
if (FlagOn( IrpSp->Flags, SL_OPEN_PAGING_FILE )) {
|
|
|
|
SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_ACQUIRE_VCB_EX );
|
|
}
|
|
|
|
//
|
|
// Use a try-finally to facilitate cleanup.
|
|
//
|
|
|
|
try {
|
|
|
|
//
|
|
// Let's do some work here if the async close list has exceeded
|
|
// some threshold. Cast 1 to a pointer to indicate who is calling
|
|
// FspClose.
|
|
//
|
|
|
|
if (NtfsData.AsyncCloseCount > NtfsMinDelayedCloseCount) {
|
|
|
|
NtfsFspClose( (PVCB) 1 );
|
|
}
|
|
|
|
if (FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_ACQUIRE_VCB_EX )) {
|
|
|
|
NtfsAcquireExclusiveVcb( IrpContext, Vcb, TRUE );
|
|
|
|
} else {
|
|
|
|
NtfsAcquireSharedVcb( IrpContext, Vcb, TRUE );
|
|
}
|
|
|
|
AcquiredVcb = TRUE;
|
|
|
|
//
|
|
// Set up local pointers to the file name.
|
|
//
|
|
|
|
*FullFileName = *OriginalFileName = OplockCleanup.FileObject->FileName;
|
|
|
|
ExactCaseName->Buffer = NULL;
|
|
|
|
//
|
|
// If the Vcb is locked then we cannot open another file. If we have performed
|
|
// a dismount then make sure we have the Vcb acquired exclusive so we can
|
|
// check if we should dismount this volume.
|
|
//
|
|
|
|
if (FlagOn( Vcb->VcbState, VCB_STATE_LOCKED | VCB_STATE_PERFORMED_DISMOUNT )) {
|
|
|
|
DebugTrace( 0, Dbg, ("Volume is locked\n") );
|
|
|
|
if (FlagOn( Vcb->VcbState, VCB_STATE_PERFORMED_DISMOUNT ) &&
|
|
!FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_ACQUIRE_VCB_EX )) {
|
|
|
|
NtfsReleaseVcb( IrpContext, Vcb );
|
|
|
|
SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_ACQUIRE_VCB_EX );
|
|
NtfsAcquireExclusiveVcb( IrpContext, Vcb, TRUE );
|
|
}
|
|
|
|
DeleteVcb = TRUE;
|
|
|
|
try_return( Status = STATUS_ACCESS_DENIED );
|
|
}
|
|
|
|
//
|
|
// Verify that this isn't an open for a structured storage type.
|
|
//
|
|
|
|
if ((IrpSp->Parameters.Create.Options & FILE_STORAGE_TYPE_SPECIFIED) == FILE_STORAGE_TYPE_SPECIFIED) {
|
|
|
|
try_return( Status = STATUS_INVALID_PARAMETER );
|
|
}
|
|
|
|
//
|
|
// Initialize local copies of the stack values.
|
|
//
|
|
|
|
RelatedFileObject = OplockCleanup.FileObject->RelatedFileObject;
|
|
IgnoreCase = !BooleanFlagOn( IrpSp->Flags, SL_CASE_SENSITIVE );
|
|
OpenFileById = BooleanFlagOn( IrpSp->Parameters.Create.Options, FILE_OPEN_BY_FILE_ID );
|
|
|
|
//
|
|
// Acquire the paging io resource if we are superseding/overwriting a
|
|
// file or if we are opening for non-cached access.
|
|
//
|
|
|
|
CreateDisposition = (UCHAR) ((IrpSp->Parameters.Create.Options >> 24) & 0x000000ff);
|
|
|
|
if ((CreateDisposition == FILE_SUPERSEDE) ||
|
|
(CreateDisposition == FILE_OVERWRITE) ||
|
|
(CreateDisposition == FILE_OVERWRITE_IF) ||
|
|
FlagOn( IrpSp->Parameters.Create.Options, FILE_NO_INTERMEDIATE_BUFFERING )) {
|
|
|
|
SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_ACQUIRE_PAGING );
|
|
}
|
|
|
|
//
|
|
// We don't allow an open for an existing paging file. To insure that the
|
|
// delayed close Scb is not for this paging file we will unconditionally
|
|
// dereference it if this is a paging file open.
|
|
//
|
|
|
|
if (FlagOn( IrpSp->Flags, SL_OPEN_PAGING_FILE ) &&
|
|
(!IsListEmpty( &NtfsData.AsyncCloseList ) ||
|
|
!IsListEmpty( &NtfsData.DelayedCloseList ))) {
|
|
|
|
NtfsFspClose( Vcb );
|
|
}
|
|
|
|
//
|
|
// 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) {
|
|
|
|
OplockCleanup.FileObject->Vpb = RelatedFileObject->Vpb;
|
|
}
|
|
|
|
//
|
|
// Ping the volume to make sure the Vcb is still mounted. If we need
|
|
// to verify the volume then do it now, and if it comes out okay
|
|
// then clear the verify volume flag in the device object and continue
|
|
// on. If it doesn't verify okay then dismount the volume and
|
|
// either tell the I/O system to try and create again (with a new mount)
|
|
// or that the volume is wrong. This later code is returned if we
|
|
// are trying to do a relative open and the vcb is no longer mounted.
|
|
//
|
|
|
|
if (!NtfsPingVolume( IrpContext, Vcb )) {
|
|
|
|
if (!FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_ACQUIRE_VCB_EX )) {
|
|
|
|
SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_ACQUIRE_VCB_EX );
|
|
NtfsRaiseStatus( IrpContext, STATUS_CANT_WAIT, NULL, NULL );
|
|
}
|
|
|
|
if (!NtfsPerformVerifyOperation( IrpContext, Vcb )) {
|
|
|
|
NtfsPerformDismountOnVcb( IrpContext, Vcb, TRUE );
|
|
|
|
DeleteVcb = TRUE;
|
|
|
|
if (RelatedFileObject == NULL) {
|
|
|
|
Irp->IoStatus.Information = IO_REMOUNT;
|
|
NtfsRaiseStatus( IrpContext, STATUS_REPARSE, NULL, NULL );
|
|
|
|
} else {
|
|
|
|
NtfsRaiseStatus( IrpContext, STATUS_WRONG_VOLUME, NULL, NULL );
|
|
}
|
|
}
|
|
|
|
//
|
|
// The volume verified correctly so now clear the verify bit
|
|
// and continue with the create
|
|
//
|
|
|
|
ClearFlag( Vcb->Vpb->RealDevice->Flags, DO_VERIFY_VOLUME );
|
|
}
|
|
|
|
//
|
|
// Make sure there is sufficient stack to perform the create.
|
|
//
|
|
|
|
if (IoGetRemainingStackSize( ) < OVERFLOW_CREATE_THRESHHOLD) {
|
|
|
|
SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_FORCE_POST );
|
|
NtfsRaiseStatus( IrpContext, STATUS_CANT_WAIT, NULL, NULL );
|
|
}
|
|
|
|
//
|
|
// Let's handle the open by Id case immediately.
|
|
//
|
|
|
|
if (OpenFileById) {
|
|
|
|
FILE_REFERENCE FileReference;
|
|
|
|
if (OriginalFileName->Length != sizeof( FILE_REFERENCE )) {
|
|
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
|
|
try_return( Status );
|
|
}
|
|
|
|
//
|
|
// Perform a safe copy of the data to our local variable.
|
|
//
|
|
|
|
RtlCopyMemory( &FileReference,
|
|
OplockCleanup.FileObject->FileName.Buffer,
|
|
sizeof( FILE_REFERENCE ));
|
|
|
|
//
|
|
// Clear the name in the file object.
|
|
//
|
|
|
|
OplockCleanup.FileObject->FileName.Buffer = NULL;
|
|
OplockCleanup.FileObject->FileName.MaximumLength = OplockCleanup.FileObject->FileName.Length = 0;
|
|
|
|
Status = NtfsOpenFcbById( IrpContext,
|
|
Irp,
|
|
IrpSp,
|
|
Vcb,
|
|
NULL,
|
|
&CurrentFcb,
|
|
FALSE,
|
|
FileReference,
|
|
NtfsEmptyString,
|
|
NtfsEmptyString,
|
|
NetworkInfo,
|
|
&ThisScb,
|
|
&ThisCcb );
|
|
|
|
//
|
|
// Put the name back into the file object so that the IO system doesn't
|
|
// think this is a dasd handle. Leave the max length at zero so
|
|
// we know this is not a real name.
|
|
//
|
|
|
|
OplockCleanup.FileObject->FileName.Buffer = OriginalFileName->Buffer;
|
|
OplockCleanup.FileObject->FileName.Length = OriginalFileName->Length;
|
|
|
|
try_return( Status );
|
|
}
|
|
|
|
//
|
|
// Here is the "M A R K L U C O V S K Y" hack from hell.
|
|
//
|
|
// It's here because Mark says he can't avoid sending me double beginning
|
|
// backslashes win the Win32 layer.
|
|
//
|
|
|
|
if ((OplockCleanup.FileObject->FileName.Length > sizeof( WCHAR )) &&
|
|
(OplockCleanup.FileObject->FileName.Buffer[1] == L'\\') &&
|
|
(OplockCleanup.FileObject->FileName.Buffer[0] == L'\\')) {
|
|
|
|
OplockCleanup.FileObject->FileName.Length -= sizeof( WCHAR );
|
|
|
|
RtlMoveMemory( &OplockCleanup.FileObject->FileName.Buffer[0],
|
|
&OplockCleanup.FileObject->FileName.Buffer[1],
|
|
OplockCleanup.FileObject->FileName.Length );
|
|
|
|
*FullFileName = *OriginalFileName = OplockCleanup.FileObject->FileName;
|
|
|
|
//
|
|
// If there are still two beginning backslashes, the name is bogus.
|
|
//
|
|
|
|
if ((OplockCleanup.FileObject->FileName.Length > sizeof( WCHAR )) &&
|
|
(OplockCleanup.FileObject->FileName.Buffer[1] == L'\\')) {
|
|
|
|
Status = STATUS_OBJECT_NAME_INVALID;
|
|
try_return( Status );
|
|
}
|
|
}
|
|
|
|
//
|
|
// If there is a related file object, we decode it to verify that this
|
|
// is a valid relative open.
|
|
//
|
|
|
|
if (RelatedFileObject != NULL) {
|
|
|
|
PVCB DecodeVcb;
|
|
|
|
//
|
|
// Check for a valid name. The name can't begin with a backslash
|
|
// and can't end with two backslashes.
|
|
//
|
|
|
|
if (OriginalFileName->Length != 0) {
|
|
|
|
//
|
|
// Check for a leading backslash.
|
|
//
|
|
|
|
if (OriginalFileName->Buffer[0] == L'\\') {
|
|
|
|
DebugTrace( 0, Dbg, ("Invalid name for relative open\n") );
|
|
try_return( Status = STATUS_INVALID_PARAMETER );
|
|
}
|
|
|
|
//
|
|
// Trim off any trailing backslash.
|
|
//
|
|
|
|
if (OriginalFileName->Buffer[ (OriginalFileName->Length / 2) - 1 ] == L'\\') {
|
|
|
|
TrailingBackslash = TRUE;
|
|
|
|
OplockCleanup.FileObject->FileName.Length -= 2;
|
|
|
|
*OriginalFileName = *FullFileName = OplockCleanup.FileObject->FileName;
|
|
}
|
|
|
|
//
|
|
// Now check if there is a trailing backslash. Note that if
|
|
// there was already a trailing backslash then there must
|
|
// be at least one more character or we would have failed
|
|
// with the original test.
|
|
//
|
|
|
|
if (OriginalFileName->Buffer[ (OriginalFileName->Length / 2) - 1 ] == L'\\') {
|
|
|
|
Status = STATUS_OBJECT_NAME_INVALID;
|
|
try_return( Status );
|
|
}
|
|
}
|
|
|
|
RelatedFileObjectTypeOfOpen = NtfsDecodeFileObject( IrpContext,
|
|
RelatedFileObject,
|
|
&DecodeVcb,
|
|
&RelatedFcb,
|
|
&RelatedScb,
|
|
&RelatedCcb,
|
|
TRUE );
|
|
|
|
//
|
|
// The relative file has to have been opened as a file. We
|
|
// cannot do relative opens relative to an opened attribute.
|
|
//
|
|
|
|
if (!FlagOn( RelatedCcb->Flags, CCB_FLAG_OPEN_AS_FILE )) {
|
|
|
|
DebugTrace( 0, Dbg, ("Invalid File object for relative open\n") );
|
|
try_return( Status = STATUS_INVALID_PARAMETER );
|
|
}
|
|
|
|
//
|
|
// If the related Ccb is was opened by file Id, we will
|
|
// remember that for future use.
|
|
//
|
|
|
|
if (FlagOn( RelatedCcb->Flags, CCB_FLAG_OPEN_BY_FILE_ID )) {
|
|
|
|
OpenFileById = TRUE;
|
|
}
|
|
|
|
//
|
|
// Remember if the related Ccb was opened through a Dos-Only
|
|
// component.
|
|
//
|
|
|
|
if (FlagOn( RelatedCcb->Flags, CCB_FLAG_PARENT_HAS_DOS_COMPONENT )) {
|
|
|
|
DosOnlyComponent = TRUE;
|
|
}
|
|
|
|
} else {
|
|
|
|
RelatedFileObjectTypeOfOpen = UnopenedFileObject;
|
|
|
|
if ((OriginalFileName->Length > 2) &&
|
|
(OriginalFileName->Buffer[ (OriginalFileName->Length / 2) - 1 ] == L'\\')) {
|
|
|
|
TrailingBackslash = TRUE;
|
|
|
|
OplockCleanup.FileObject->FileName.Length -= 2;
|
|
|
|
*OriginalFileName = *FullFileName = OplockCleanup.FileObject->FileName;
|
|
|
|
//
|
|
// If there is still a trailing backslash on the name then
|
|
// the name is invalid.
|
|
//
|
|
|
|
|
|
if ((OriginalFileName->Length > 2) &&
|
|
(OriginalFileName->Buffer[ (OriginalFileName->Length / 2) - 1 ] == L'\\')) {
|
|
|
|
Status = STATUS_OBJECT_NAME_INVALID;
|
|
try_return( Status );
|
|
}
|
|
}
|
|
}
|
|
|
|
DebugTrace( 0, Dbg, ("Related File Object, TypeOfOpen -> %08lx\n", RelatedFileObjectTypeOfOpen) );
|
|
|
|
//
|
|
// We check if this is a user volume open in that there is no name
|
|
// specified and the related file object is valid if present. In that
|
|
// case set the correct flags in the IrpContext and raise so we can take
|
|
// the volume open path.
|
|
//
|
|
|
|
if (OriginalFileName->Length == 0
|
|
&& (RelatedFileObjectTypeOfOpen == UnopenedFileObject
|
|
|| RelatedFileObjectTypeOfOpen == UserVolumeOpen)) {
|
|
|
|
DebugTrace( 0, Dbg, ("Attempting to open entire volume\n") );
|
|
|
|
SetFlag( IrpContext->Flags,
|
|
IRP_CONTEXT_FLAG_ACQUIRE_VCB_EX | IRP_CONTEXT_FLAG_DASD_OPEN );
|
|
|
|
NtfsRaiseStatus( IrpContext, STATUS_CANT_WAIT, NULL, NULL );
|
|
}
|
|
|
|
//
|
|
// If the related file object was a volume open, then this open is
|
|
// illegal.
|
|
//
|
|
|
|
if (RelatedFileObjectTypeOfOpen == UserVolumeOpen) {
|
|
|
|
try_return( Status = STATUS_INVALID_PARAMETER );
|
|
}
|
|
|
|
//
|
|
// Remember if we need to perform any traverse access checks.
|
|
//
|
|
|
|
{
|
|
PIO_SECURITY_CONTEXT SecurityContext;
|
|
|
|
SecurityContext = IrpSp->Parameters.Create.SecurityContext;
|
|
|
|
if (!FlagOn( SecurityContext->AccessState->Flags,
|
|
TOKEN_HAS_TRAVERSE_PRIVILEGE )) {
|
|
|
|
DebugTrace( 0, Dbg, ("Performing traverse access on this open\n") );
|
|
|
|
TraverseAccessCheck = TRUE;
|
|
|
|
} else {
|
|
|
|
TraverseAccessCheck = FALSE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// We enter the loop that does the processing for the prefix lookup.
|
|
// We optimize the case where we can match a prefix hit. If there is
|
|
// no hit we will check if the name is legal or might possibly require
|
|
// parsing to handle the case where there is a named data stream.
|
|
//
|
|
|
|
FirstPass = TRUE;
|
|
CheckForValidName = TRUE;
|
|
|
|
AttrName.Length = 0;
|
|
AttrCodeName.Length = 0;
|
|
|
|
while (TRUE) {
|
|
|
|
BOOLEAN ComplexName;
|
|
PUNICODE_STRING FileObjectName;
|
|
LONG Index;
|
|
|
|
//
|
|
// Lets make sure we have acquired the starting point for our
|
|
// name search. If we have a relative file object then use
|
|
// that. Otherwise we will start from the root.
|
|
//
|
|
|
|
if (RelatedFileObject != NULL) {
|
|
|
|
CurrentFcb = RelatedFcb;
|
|
|
|
} else {
|
|
|
|
CurrentFcb = Vcb->RootIndexScb->Fcb;
|
|
}
|
|
|
|
NtfsAcquireFcbWithPaging( IrpContext, CurrentFcb, FALSE );
|
|
|
|
//
|
|
// Parse the file object name if we need to.
|
|
//
|
|
|
|
FileObjectName = &OplockCleanup.FileObject->FileName;
|
|
|
|
if (!FirstPass) {
|
|
|
|
if (!NtfsParseNameForCreate( IrpContext,
|
|
RemainingName,
|
|
FileObjectName,
|
|
OriginalFileName,
|
|
FullFileName,
|
|
&AttrName,
|
|
&AttrCodeName )) {
|
|
|
|
try_return( Status = STATUS_OBJECT_NAME_INVALID );
|
|
}
|
|
|
|
//
|
|
// If we might be creating a named stream acquire the
|
|
// paging IO as well. This will keep anyone from peeking
|
|
// at the allocation size of any other streams we are converting
|
|
// to non-resident.
|
|
//
|
|
|
|
if (!FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_ACQUIRE_PAGING ) &&
|
|
(AttrName.Length != 0) &&
|
|
((CreateDisposition == FILE_OPEN_IF) ||
|
|
(CreateDisposition == FILE_CREATE))) {
|
|
|
|
SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_ACQUIRE_PAGING );
|
|
}
|
|
CheckForValidName = FALSE;
|
|
|
|
//
|
|
// Build up the full name if this is not the open by file Id case.
|
|
//
|
|
|
|
} else if (!OpenFileById) {
|
|
|
|
//
|
|
// If we have a related file object, then we build up the
|
|
// combined name.
|
|
//
|
|
|
|
if (RelatedFileObject != NULL) {
|
|
|
|
WCHAR *CurrentPosition;
|
|
USHORT AddSeparator;
|
|
|
|
if ((FileObjectName->Length == 0) ||
|
|
(RelatedCcb->FullFileName.Length == 2) ||
|
|
(FileObjectName->Buffer[0] == L':')) {
|
|
|
|
AddSeparator = 0;
|
|
|
|
} else {
|
|
|
|
AddSeparator = sizeof( WCHAR );
|
|
}
|
|
|
|
FullFileName->Length = RelatedCcb->FullFileName.Length +
|
|
FileObjectName->Length +
|
|
AddSeparator;
|
|
|
|
FullFileName->MaximumLength = FullFileName->Length;
|
|
|
|
//
|
|
// We need to allocate a name buffer.
|
|
//
|
|
|
|
FullFileName->Buffer = FsRtlAllocatePoolWithTag(PagedPool, FullFileName->Length, MODULE_POOL_TAG);
|
|
|
|
CurrentPosition = (WCHAR *) FullFileName->Buffer;
|
|
|
|
RtlCopyMemory( CurrentPosition,
|
|
RelatedCcb->FullFileName.Buffer,
|
|
RelatedCcb->FullFileName.Length );
|
|
|
|
CurrentPosition = (WCHAR *) Add2Ptr( CurrentPosition, RelatedCcb->FullFileName.Length );
|
|
|
|
if (AddSeparator != 0) {
|
|
|
|
*CurrentPosition = L'\\';
|
|
|
|
CurrentPosition += 1;
|
|
}
|
|
|
|
if (FileObjectName->Length != 0) {
|
|
|
|
RtlCopyMemory( CurrentPosition,
|
|
FileObjectName->Buffer,
|
|
FileObjectName->Length );
|
|
}
|
|
|
|
//
|
|
// If the user specified a case sensitive comparison, then the
|
|
// case insensitive index is the full length of the resulting
|
|
// string. Otherwise it is the length of the string in
|
|
// the related file object. We adjust for the case when the
|
|
// original file name length is zero.
|
|
//
|
|
|
|
if (!IgnoreCase) {
|
|
|
|
CaseInsensitiveIndex = FullFileName->Length;
|
|
|
|
} else {
|
|
|
|
CaseInsensitiveIndex = RelatedCcb->FullFileName.Length +
|
|
AddSeparator;
|
|
}
|
|
|
|
//
|
|
// The entire name is in the FileObjectName. We check the buffer for
|
|
// validity.
|
|
//
|
|
|
|
} else {
|
|
|
|
//
|
|
// We look at the name string for detectable errors. The
|
|
// length must be non-zero and the first character must be
|
|
// '\'
|
|
//
|
|
|
|
if (FileObjectName->Length == 0) {
|
|
|
|
DebugTrace( 0, Dbg, ("There is no name to open\n") );
|
|
try_return( Status = STATUS_OBJECT_PATH_NOT_FOUND );
|
|
}
|
|
|
|
if (FileObjectName->Buffer[0] != L'\\') {
|
|
|
|
DebugTrace( 0, Dbg, ("Name does not begin with a backslash\n") );
|
|
try_return( Status = STATUS_INVALID_PARAMETER );
|
|
}
|
|
|
|
//
|
|
// If the user specified a case sensitive comparison, then the
|
|
// case insensitive index is the full length of the resulting
|
|
// string. Otherwise it is zero.
|
|
//
|
|
|
|
if (!IgnoreCase) {
|
|
|
|
CaseInsensitiveIndex = FullFileName->Length;
|
|
|
|
} else {
|
|
|
|
CaseInsensitiveIndex = 0;
|
|
}
|
|
}
|
|
|
|
} else if (IgnoreCase) {
|
|
|
|
CaseInsensitiveIndex = 0;
|
|
|
|
} else {
|
|
|
|
CaseInsensitiveIndex = FullFileName->Length;
|
|
}
|
|
|
|
//
|
|
// The remaining name is stored in the FullFileName variable.
|
|
// If we are doing a case-insensitive operation and have to
|
|
// upcase part of the remaining name then allocate a buffer
|
|
// now.
|
|
//
|
|
|
|
if (IgnoreCase &&
|
|
CaseInsensitiveIndex < FullFileName->Length) {
|
|
|
|
UNICODE_STRING StringToUpcase;
|
|
|
|
ExactCaseName->Buffer = FsRtlAllocatePoolWithTag(PagedPool, FullFileName->MaximumLength, MODULE_POOL_TAG);
|
|
ExactCaseName->MaximumLength = FullFileName->MaximumLength;
|
|
|
|
RtlCopyMemory( ExactCaseName->Buffer,
|
|
FullFileName->Buffer,
|
|
FullFileName->MaximumLength );
|
|
|
|
ExactCaseName->Length = FullFileName->Length;
|
|
|
|
StringToUpcase.Buffer = Add2Ptr( FullFileName->Buffer,
|
|
CaseInsensitiveIndex );
|
|
|
|
StringToUpcase.Length = FullFileName->Length - (USHORT) CaseInsensitiveIndex;
|
|
StringToUpcase.MaximumLength = FullFileName->MaximumLength - (USHORT) CaseInsensitiveIndex;
|
|
|
|
NtfsUpcaseName( Vcb->UpcaseTable, Vcb->UpcaseTableSize, &StringToUpcase );
|
|
}
|
|
|
|
RemainingName = *FullFileName;
|
|
|
|
//
|
|
// If this is the traverse access case or the open by file id case we start
|
|
// relative to the file object we have or the root directory.
|
|
// This is also true for the case where the file name in the file object is
|
|
// empty.
|
|
//
|
|
|
|
if (TraverseAccessCheck
|
|
|| FileObjectName->Length == 0) {
|
|
|
|
if (RelatedFileObject != NULL) {
|
|
|
|
CurrentLcb = RelatedCcb->Lcb;
|
|
CurrentScb = RelatedScb;
|
|
|
|
if (FileObjectName->Length == 0) {
|
|
|
|
RemainingName.Length = 0;
|
|
|
|
} else if (!OpenFileById) {
|
|
|
|
USHORT Increment;
|
|
|
|
Increment = RelatedCcb->FullFileName.Length
|
|
+ (RelatedCcb->FullFileName.Length == 2
|
|
? 0
|
|
: 2);
|
|
|
|
RemainingName.Buffer = (WCHAR *) Add2Ptr( RemainingName.Buffer,
|
|
Increment );
|
|
|
|
RemainingName.Length -= Increment;
|
|
}
|
|
|
|
} else {
|
|
|
|
CurrentLcb = Vcb->RootLcb;
|
|
CurrentScb = Vcb->RootIndexScb;
|
|
|
|
RemainingName.Buffer = (WCHAR *) Add2Ptr( RemainingName.Buffer, sizeof( WCHAR ));
|
|
RemainingName.Length -= sizeof( WCHAR );
|
|
}
|
|
|
|
//
|
|
// Otherwise we will try a prefix lookup.
|
|
//
|
|
|
|
} else {
|
|
|
|
if (RelatedFileObject != NULL) {
|
|
|
|
if (!OpenFileById) {
|
|
|
|
//
|
|
// Skip over the characters in the related file object.
|
|
//
|
|
|
|
RemainingName.Buffer = (WCHAR *) Add2Ptr( RemainingName.Buffer,
|
|
RelatedCcb->FullFileName.Length );
|
|
RemainingName.Length -= RelatedCcb->FullFileName.Length;
|
|
}
|
|
|
|
CurrentLcb = RelatedCcb->Lcb;
|
|
CurrentScb = RelatedScb;
|
|
|
|
} else {
|
|
|
|
CurrentLcb = Vcb->RootLcb;
|
|
CurrentScb = Vcb->RootIndexScb;
|
|
|
|
//
|
|
// Skip over the lead-in '\' character.
|
|
//
|
|
|
|
RemainingName.Buffer = (WCHAR *) Add2Ptr( RemainingName.Buffer,
|
|
sizeof( WCHAR ));
|
|
RemainingName.Length -= sizeof( WCHAR );
|
|
}
|
|
|
|
LcbForTeardown = NULL;
|
|
|
|
NextLcb = NtfsFindPrefix( IrpContext,
|
|
CurrentScb,
|
|
&CurrentFcb,
|
|
&LcbForTeardown,
|
|
RemainingName,
|
|
IgnoreCase,
|
|
&DosOnlyComponent,
|
|
&RemainingName );
|
|
|
|
//
|
|
// If we found another link then update the CurrentLcb value.
|
|
//
|
|
|
|
if (NextLcb != NULL) {
|
|
|
|
CurrentLcb = NextLcb;
|
|
}
|
|
}
|
|
|
|
if (!FirstPass
|
|
|| RemainingName.Length == 0) {
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// If we get here, it means that this is the first pass and we didn't
|
|
// have a prefix match. If there is a colon in the
|
|
// remaining name, then we need to analyze the name in more detail.
|
|
//
|
|
|
|
ComplexName = FALSE;
|
|
|
|
for (Index = (RemainingName.Length / 2) - 1, ComplexName = FALSE;
|
|
Index >= 0;
|
|
Index -= 1) {
|
|
|
|
if (RemainingName.Buffer[Index] == L':') {
|
|
|
|
ComplexName = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!ComplexName) {
|
|
|
|
break;
|
|
}
|
|
|
|
FirstPass = FALSE;
|
|
|
|
//
|
|
// Copy the exact case back into the full name and deallocate
|
|
// any buffer we may have allocated.
|
|
//
|
|
|
|
if (ExactCaseName->Buffer != NULL) {
|
|
|
|
RtlCopyMemory( FullFileName->Buffer,
|
|
ExactCaseName->Buffer,
|
|
ExactCaseName->Length );
|
|
|
|
NtfsFreePool( ExactCaseName->Buffer );
|
|
ExactCaseName->Buffer = NULL;
|
|
}
|
|
|
|
//
|
|
// Let's release the Fcb we have currently acquired.
|
|
//
|
|
|
|
NtfsReleaseFcbWithPaging( IrpContext, CurrentFcb );
|
|
LcbForTeardown = NULL;
|
|
}
|
|
|
|
//
|
|
// Check if the link or the Fcb is pending delete.
|
|
//
|
|
|
|
if ((CurrentLcb != NULL && LcbLinkIsDeleted( CurrentLcb )) ||
|
|
CurrentFcb->LinkCount == 0) {
|
|
|
|
try_return( Status = STATUS_DELETE_PENDING );
|
|
}
|
|
|
|
//
|
|
// Put the new name into the file object.
|
|
//
|
|
|
|
OplockCleanup.FileObject->FileName = *FullFileName;
|
|
|
|
//
|
|
// If the entire path was parsed, then we have access to the Fcb to
|
|
// open. We either open the parent of the prefix match or the prefix
|
|
// match itself, depending on whether the user wanted to open
|
|
// the target directory.
|
|
//
|
|
|
|
if (RemainingName.Length == 0) {
|
|
|
|
//
|
|
// Check the attribute name length.
|
|
//
|
|
|
|
if (AttrName.Length > (NTFS_MAX_ATTR_NAME_LEN * sizeof( WCHAR ))) {
|
|
|
|
try_return( Status = STATUS_OBJECT_NAME_INVALID );
|
|
}
|
|
|
|
//
|
|
// If this is a target directory we check that the open is for the
|
|
// entire file.
|
|
// We assume that the final component can only have an attribute
|
|
// which corresponds to the type of file this is. Meaning
|
|
// $INDEX_ALLOCATION for directory, $DATA (unnamed) for a file.
|
|
// We verify that the matching Lcb is not the root Lcb.
|
|
//
|
|
|
|
if (FlagOn( IrpSp->Flags, SL_OPEN_TARGET_DIRECTORY )) {
|
|
|
|
if (CurrentLcb == Vcb->RootLcb) {
|
|
|
|
DebugTrace( 0, Dbg, ("Can't open parent of root\n") );
|
|
try_return( Status = STATUS_INVALID_PARAMETER );
|
|
}
|
|
|
|
//
|
|
// We don't allow attribute names or attribute codes to
|
|
// be specified.
|
|
//
|
|
|
|
if (AttrName.Length != 0
|
|
|| AttrCodeName.Length != 0) {
|
|
|
|
DebugTrace( 0, Dbg, ("Can't specify complex name for rename\n") );
|
|
try_return( Status = STATUS_OBJECT_NAME_INVALID );
|
|
}
|
|
|
|
//
|
|
// We want to copy the exact case of the name back into the
|
|
// input buffer for this case.
|
|
//
|
|
|
|
if (ExactCaseName->Buffer != NULL) {
|
|
|
|
RtlCopyMemory( FullFileName->Buffer,
|
|
ExactCaseName->Buffer,
|
|
ExactCaseName->Length );
|
|
}
|
|
|
|
//
|
|
// Acquire the parent of the last Fcb. This is the actual file we
|
|
// are opening.
|
|
//
|
|
|
|
ParentFcb = CurrentLcb->Scb->Fcb;
|
|
NtfsAcquireFcbWithPaging( IrpContext, ParentFcb, FALSE );
|
|
|
|
//
|
|
// Call our open target directory, remembering the target
|
|
// file existed.
|
|
//
|
|
|
|
Status = NtfsOpenTargetDirectory( IrpContext,
|
|
Irp,
|
|
IrpSp,
|
|
ParentFcb,
|
|
NULL,
|
|
&OplockCleanup.FileObject->FileName,
|
|
CurrentLcb->ExactCaseLink.LinkName.Length,
|
|
TRUE,
|
|
DosOnlyComponent,
|
|
&ThisScb,
|
|
&ThisCcb );
|
|
|
|
try_return( NOTHING );
|
|
}
|
|
|
|
//
|
|
// Otherwise we simply attempt to open the Fcb we matched.
|
|
//
|
|
|
|
if (OpenFileById) {
|
|
|
|
Status = NtfsOpenFcbById( IrpContext,
|
|
Irp,
|
|
IrpSp,
|
|
Vcb,
|
|
CurrentLcb,
|
|
&CurrentFcb,
|
|
TRUE,
|
|
CurrentFcb->FileReference,
|
|
AttrName,
|
|
AttrCodeName,
|
|
NetworkInfo,
|
|
&ThisScb,
|
|
&ThisCcb );
|
|
|
|
|
|
//
|
|
// Set the maximum length in the file object name to
|
|
// zero so we know that this is not a full name.
|
|
//
|
|
|
|
OplockCleanup.FileObject->FileName.MaximumLength = 0;
|
|
|
|
} else {
|
|
|
|
//
|
|
// The current Fcb is acquired.
|
|
//
|
|
|
|
Status = NtfsOpenExistingPrefixFcb( IrpContext,
|
|
Irp,
|
|
IrpSp,
|
|
CurrentFcb,
|
|
CurrentLcb,
|
|
FullFileName->Length,
|
|
AttrName,
|
|
AttrCodeName,
|
|
DosOnlyComponent,
|
|
TrailingBackslash,
|
|
NetworkInfo,
|
|
&ThisScb,
|
|
&ThisCcb );
|
|
}
|
|
|
|
try_return( NOTHING );
|
|
}
|
|
|
|
//
|
|
// Check if the current Lcb is a Dos-Only Name.
|
|
//
|
|
|
|
if (CurrentLcb != NULL &&
|
|
CurrentLcb->FileNameAttr->Flags == FILE_NAME_DOS) {
|
|
|
|
DosOnlyComponent = TRUE;
|
|
}
|
|
|
|
//
|
|
// We have a remaining portion of the file name which was unmatched in the
|
|
// prefix table. We walk through these name components until we reach the
|
|
// last element. If necessary, we add Fcb and Scb's into the graph as we
|
|
// walk through the names.
|
|
//
|
|
|
|
FirstPass = TRUE;
|
|
|
|
while (TRUE) {
|
|
|
|
PFILE_NAME IndexFileName;
|
|
|
|
//
|
|
// We check that the last Fcb we have is in fact a directory.
|
|
//
|
|
|
|
if (!IsDirectory( &CurrentFcb->Info )) {
|
|
|
|
DebugTrace( 0, Dbg, ("Intermediate node is not a directory\n") );
|
|
try_return( Status = STATUS_OBJECT_PATH_NOT_FOUND );
|
|
}
|
|
|
|
//
|
|
// We dissect the name into the next component and the remaining name string.
|
|
// We don't need to check for a valid name if we examined the name already.
|
|
//
|
|
|
|
NtfsDissectName( RemainingName,
|
|
&FinalName,
|
|
&RemainingName );
|
|
|
|
DebugTrace( 0, Dbg, ("Final name -> %Z\n", &FinalName) );
|
|
DebugTrace( 0, Dbg, ("Remaining Name -> %Z\n", &RemainingName) );
|
|
|
|
//
|
|
// If the final name is too long then either the path or the
|
|
// name is invalid.
|
|
//
|
|
|
|
if (FinalName.Length > (NTFS_MAX_FILE_NAME_LENGTH * sizeof( WCHAR ))) {
|
|
|
|
if (RemainingName.Length == 0) {
|
|
|
|
try_return( Status = STATUS_OBJECT_NAME_INVALID );
|
|
|
|
} else {
|
|
|
|
try_return( Status = STATUS_OBJECT_PATH_NOT_FOUND );
|
|
}
|
|
}
|
|
|
|
//
|
|
// Catch single dot names (.) before scanning the index. We don't
|
|
// want to allow someone to open the self entry in the root.
|
|
//
|
|
|
|
if ((FinalName.Length == 2) &&
|
|
(FinalName.Buffer[0] == L'.')) {
|
|
|
|
if (RemainingName.Length != 0) {
|
|
|
|
DebugTrace( 0, Dbg, ("Intermediate component in path doesn't exist\n") );
|
|
try_return( Status = STATUS_OBJECT_PATH_NOT_FOUND );
|
|
|
|
//
|
|
// If the final component is illegal, then return the appropriate error.
|
|
//
|
|
|
|
} else {
|
|
|
|
try_return( Status = STATUS_OBJECT_NAME_INVALID );
|
|
}
|
|
}
|
|
|
|
//
|
|
// Get the index allocation Scb for the current Fcb.
|
|
//
|
|
|
|
//
|
|
// We need to look for the next component in the name string in the directory
|
|
// we've reached. We need to get a Scb to perform the index search.
|
|
// To do the search we need to build a filename attribute to perform the
|
|
// search with and then call the index package to perform the search.
|
|
//
|
|
|
|
CurrentScb = NtfsCreateScb( IrpContext,
|
|
CurrentFcb,
|
|
$INDEX_ALLOCATION,
|
|
&NtfsFileNameIndex,
|
|
FALSE,
|
|
NULL );
|
|
|
|
//
|
|
// If the CurrentScb does not have its normalized name and we have a valid
|
|
// parent, then update the normalized name.
|
|
//
|
|
|
|
if ((LastScb != NULL) &&
|
|
(CurrentScb->ScbType.Index.NormalizedName.Buffer == NULL) &&
|
|
(LastScb->ScbType.Index.NormalizedName.Buffer != NULL)) {
|
|
|
|
NtfsUpdateNormalizedName( IrpContext, LastScb, CurrentScb, IndexFileName, FALSE );
|
|
}
|
|
|
|
//
|
|
// Release the parent Scb if we own it.
|
|
//
|
|
|
|
if (!FirstPass) {
|
|
|
|
NtfsReleaseFcbWithPaging( IrpContext, ParentFcb );
|
|
}
|
|
|
|
LastScb = CurrentScb;
|
|
|
|
//
|
|
// If traverse access is required, we do so now before accessing the
|
|
// disk.
|
|
//
|
|
|
|
if (TraverseAccessCheck) {
|
|
|
|
NtfsTraverseCheck( IrpContext,
|
|
CurrentFcb,
|
|
Irp );
|
|
}
|
|
|
|
//
|
|
// Look on the disk to see if we can find the last component on the path.
|
|
//
|
|
|
|
NtfsUnpinBcb( &IndexEntryBcb );
|
|
|
|
//
|
|
// Check that the name is valid before scanning the disk.
|
|
//
|
|
|
|
if (CheckForValidName && !NtfsIsFileNameValid( &FinalName, FALSE )) {
|
|
|
|
DebugTrace( 0, Dbg, ("Component name is invalid\n") );
|
|
try_return( Status = STATUS_OBJECT_NAME_INVALID );
|
|
}
|
|
|
|
FoundEntry = NtfsLookupEntry( IrpContext,
|
|
CurrentScb,
|
|
IgnoreCase,
|
|
&FinalName,
|
|
&FileNameAttr,
|
|
&FileNameAttrLength,
|
|
&QuickIndex,
|
|
&IndexEntry,
|
|
&IndexEntryBcb );
|
|
|
|
//
|
|
// This call to NtfsLookupEntry may decide to push the root index.
|
|
// Create needs to free resources as it walks down the tree to prevent
|
|
// deadlocks. If there is a transaction, commit it now so we will be
|
|
// able to free this resource.
|
|
//
|
|
|
|
if (IrpContext->TransactionId != 0) {
|
|
|
|
NtfsCheckpointCurrentTransaction( IrpContext );
|
|
#ifdef _CAIRO_
|
|
//
|
|
// Go through and free any Scb's in the queue of shared
|
|
// Scb's for transactions.
|
|
//
|
|
|
|
if (IrpContext->SharedScb != NULL) {
|
|
ASSERT( IrpContext->SharedScb == NULL );
|
|
NtfsReleaseSharedResources( IrpContext );
|
|
}
|
|
|
|
#endif // _CAIRO_
|
|
|
|
}
|
|
|
|
if (FoundEntry) {
|
|
|
|
//
|
|
// Get the file name attribute so we can get the name out of it.
|
|
//
|
|
|
|
IndexFileName = (PFILE_NAME) NtfsFoundIndexEntry( IndexEntry );
|
|
|
|
if (IgnoreCase) {
|
|
|
|
RtlCopyMemory( FinalName.Buffer,
|
|
IndexFileName->FileName,
|
|
FinalName.Length );
|
|
}
|
|
}
|
|
|
|
//
|
|
// If we didn't find a matching entry in the index, we need to check if the
|
|
// name is illegal or simply isn't present on the disk.
|
|
//
|
|
|
|
if (!FoundEntry) {
|
|
|
|
if (RemainingName.Length != 0) {
|
|
|
|
DebugTrace( 0, Dbg, ("Intermediate component in path doesn't exist\n") );
|
|
try_return( Status = STATUS_OBJECT_PATH_NOT_FOUND );
|
|
}
|
|
|
|
//
|
|
// Now copy the exact case of the name specified by the user back
|
|
// in the file name buffer and file name attribute in order to
|
|
// create the name.
|
|
//
|
|
|
|
if (IgnoreCase) {
|
|
|
|
RtlCopyMemory( FinalName.Buffer,
|
|
Add2Ptr( ExactCaseName->Buffer,
|
|
ExactCaseName->Length - FinalName.Length ),
|
|
FinalName.Length );
|
|
|
|
RtlCopyMemory( FileNameAttr->FileName,
|
|
Add2Ptr( ExactCaseName->Buffer,
|
|
ExactCaseName->Length - FinalName.Length ),
|
|
FinalName.Length );
|
|
}
|
|
}
|
|
|
|
//
|
|
// If we're at the last component in the path, then this is the file
|
|
// to open or create
|
|
//
|
|
|
|
if (RemainingName.Length == 0) {
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Otherwise we create an Fcb for the subdirectory and the link between
|
|
// it and its parent Scb.
|
|
//
|
|
|
|
//
|
|
// Remember that the current values will become the parent values.
|
|
//
|
|
|
|
ParentFcb = CurrentFcb;
|
|
|
|
CurrentLcb = NtfsOpenSubdirectory( IrpContext,
|
|
CurrentScb,
|
|
FinalName,
|
|
TraverseAccessCheck,
|
|
&CurrentFcb,
|
|
&LcbForTeardown,
|
|
IndexEntry );
|
|
|
|
//
|
|
// Check that this link is a valid existing link.
|
|
//
|
|
|
|
if (LcbLinkIsDeleted( CurrentLcb ) ||
|
|
CurrentFcb->LinkCount == 0) {
|
|
|
|
try_return( Status = STATUS_DELETE_PENDING );
|
|
}
|
|
|
|
//
|
|
// Go ahead and insert this link into the splay tree.
|
|
//
|
|
|
|
NtfsInsertPrefix( CurrentLcb,
|
|
IgnoreCase );
|
|
|
|
//
|
|
// Since we have the location of this entry store the information into
|
|
// the Lcb.
|
|
//
|
|
|
|
RtlCopyMemory( &CurrentLcb->QuickIndex,
|
|
&QuickIndex,
|
|
sizeof( QUICK_INDEX ));
|
|
|
|
//
|
|
// Check if the current Lcb is a Dos-Only Name.
|
|
//
|
|
|
|
if (CurrentLcb->FileNameAttr->Flags == FILE_NAME_DOS) {
|
|
|
|
DosOnlyComponent = TRUE;
|
|
}
|
|
|
|
FirstPass = FALSE;
|
|
}
|
|
|
|
//
|
|
// We now have the parent of the file to open and know whether the file exists on
|
|
// the disk. At this point we either attempt to open the target directory or
|
|
// the file itself.
|
|
//
|
|
|
|
if (FlagOn( IrpSp->Flags, SL_OPEN_TARGET_DIRECTORY )) {
|
|
|
|
//
|
|
// We don't allow attribute names or attribute codes to
|
|
// be specified.
|
|
//
|
|
|
|
if (AttrName.Length != 0
|
|
|| AttrCodeName.Length != 0) {
|
|
|
|
DebugTrace( 0, Dbg, ("Can't specify complex name for rename\n") );
|
|
try_return( Status = STATUS_OBJECT_NAME_INVALID );
|
|
}
|
|
|
|
//
|
|
// We want to copy the exact case of the name back into the
|
|
// input buffer for this case.
|
|
//
|
|
|
|
if (ExactCaseName->Buffer != NULL) {
|
|
|
|
RtlCopyMemory( FullFileName->Buffer,
|
|
ExactCaseName->Buffer,
|
|
ExactCaseName->Length );
|
|
}
|
|
|
|
//
|
|
// Call our open target directory, remembering the target
|
|
// file existed.
|
|
//
|
|
|
|
Status = NtfsOpenTargetDirectory( IrpContext,
|
|
Irp,
|
|
IrpSp,
|
|
CurrentFcb,
|
|
CurrentLcb,
|
|
&OplockCleanup.FileObject->FileName,
|
|
FinalName.Length,
|
|
FoundEntry,
|
|
DosOnlyComponent,
|
|
&ThisScb,
|
|
&ThisCcb );
|
|
|
|
try_return( Status );
|
|
}
|
|
|
|
//
|
|
// If we didn't find an entry, we will try to create the file.
|
|
//
|
|
|
|
if (!FoundEntry) {
|
|
|
|
//
|
|
// Update our pointers to reflect that we are at the
|
|
// parent of the file we want.
|
|
//
|
|
|
|
ParentFcb = CurrentFcb;
|
|
|
|
Status = NtfsCreateNewFile( IrpContext,
|
|
Irp,
|
|
IrpSp,
|
|
CurrentScb,
|
|
FileNameAttr,
|
|
*FullFileName,
|
|
FinalName,
|
|
AttrName,
|
|
AttrCodeName,
|
|
IgnoreCase,
|
|
OpenFileById,
|
|
DosOnlyComponent,
|
|
TrailingBackslash,
|
|
&CurrentFcb,
|
|
&LcbForTeardown,
|
|
&ThisScb,
|
|
&ThisCcb );
|
|
|
|
CreateFileCase = TRUE;
|
|
|
|
//
|
|
// Otherwise we call our routine to open the file.
|
|
//
|
|
|
|
} else {
|
|
|
|
ParentFcb = CurrentFcb;
|
|
|
|
Status = NtfsOpenFile( IrpContext,
|
|
Irp,
|
|
IrpSp,
|
|
CurrentScb,
|
|
IndexEntry,
|
|
*FullFileName,
|
|
FinalName,
|
|
AttrName,
|
|
AttrCodeName,
|
|
IgnoreCase,
|
|
OpenFileById,
|
|
&QuickIndex,
|
|
DosOnlyComponent,
|
|
TrailingBackslash,
|
|
NetworkInfo,
|
|
&CurrentFcb,
|
|
&LcbForTeardown,
|
|
&ThisScb,
|
|
&ThisCcb );
|
|
}
|
|
|
|
try_exit: NOTHING;
|
|
|
|
//
|
|
// Abort transaction on err by raising.
|
|
//
|
|
|
|
if (Status != STATUS_PENDING) {
|
|
|
|
NtfsCleanupTransaction( IrpContext, Status, FALSE );
|
|
}
|
|
|
|
} finally {
|
|
|
|
DebugUnwind( NtfsCommonCreate );
|
|
|
|
//
|
|
// Unpin the index entry.
|
|
//
|
|
|
|
NtfsUnpinBcb( &IndexEntryBcb );
|
|
|
|
//
|
|
// Free the file name attribute if we allocated it.
|
|
//
|
|
|
|
if (FileNameAttr != NULL) {
|
|
|
|
NtfsFreePool( FileNameAttr );
|
|
}
|
|
|
|
//
|
|
// Capture the status code from the IrpContext if we are in the exception path.
|
|
//
|
|
|
|
if (AbnormalTermination()) {
|
|
|
|
Status = IrpContext->ExceptionStatus;
|
|
}
|
|
|
|
//
|
|
// If this is the oplock completion path then don't do any of this completion work,
|
|
// The Irp may already have been posted to another thread.
|
|
//
|
|
|
|
if (Status != STATUS_PENDING) {
|
|
|
|
//
|
|
// If we successfully opened the file, we need to update the in-memory
|
|
// structures.
|
|
//
|
|
|
|
if (NT_SUCCESS( Status ) && (Status != STATUS_REPARSE)) {
|
|
|
|
//
|
|
// If we modified the original file name, we can delete the original
|
|
// buffer.
|
|
//
|
|
|
|
if ((OriginalFileName->Buffer != NULL) &&
|
|
(OriginalFileName->Buffer != FullFileName->Buffer)) {
|
|
|
|
NtfsFreePool( OriginalFileName->Buffer );
|
|
DebugDoit( OriginalFileName->Buffer = NULL );
|
|
}
|
|
|
|
//
|
|
// Do our normal processing if this is not a Network Info query.
|
|
//
|
|
|
|
if (!ARGUMENT_PRESENT( NetworkInfo )) {
|
|
|
|
//
|
|
// Find the Lcb for this open.
|
|
//
|
|
|
|
CurrentLcb = ThisCcb->Lcb;
|
|
|
|
//
|
|
// Check if we were opening a paging file and if so then make sure that
|
|
// the internal attribute stream is all closed down
|
|
//
|
|
|
|
if (FlagOn( IrpSp->Flags, SL_OPEN_PAGING_FILE )) {
|
|
|
|
NtfsDeleteInternalAttributeStream( ThisScb, TRUE );
|
|
}
|
|
|
|
//
|
|
// If we are not done with a large allocation for a new attribute,
|
|
// then we must make sure that no one can open the file until we
|
|
// try to get it extended. Do this before dropping the Vcb.
|
|
//
|
|
|
|
if (FlagOn( IrpContext->Flags, IRP_CONTEXT_LARGE_ALLOCATION )) {
|
|
|
|
//
|
|
// For a new file, we can clear the link count and mark the
|
|
// Lcb (if there is one) delete on close.
|
|
//
|
|
|
|
if (CreateFileCase) {
|
|
|
|
CurrentFcb->LinkCount = 0;
|
|
|
|
SetFlag( CurrentLcb->LcbState, LCB_STATE_DELETE_ON_CLOSE );
|
|
|
|
//
|
|
// If we just created an attribute, then we will mark that attribute
|
|
// delete on close to prevent it from being opened.
|
|
//
|
|
|
|
} else {
|
|
|
|
SetFlag( ThisScb->ScbState, SCB_STATE_DELETE_ON_CLOSE );
|
|
}
|
|
}
|
|
|
|
//
|
|
// Remember the POSIX flag and whether we had to do any traverse
|
|
// access checking.
|
|
//
|
|
|
|
if (IgnoreCase) {
|
|
|
|
SetFlag( ThisCcb->Flags, CCB_FLAG_IGNORE_CASE );
|
|
}
|
|
|
|
if (TraverseAccessCheck) {
|
|
|
|
SetFlag( ThisCcb->Flags, CCB_FLAG_TRAVERSE_CHECK );
|
|
}
|
|
|
|
//
|
|
// We don't do "delete on close" for directories or open
|
|
// by ID files.
|
|
//
|
|
|
|
if (FlagOn( IrpSp->Parameters.Create.Options, FILE_DELETE_ON_CLOSE ) &&
|
|
!FlagOn( ThisCcb->Flags, CCB_FLAG_OPEN_BY_FILE_ID )) {
|
|
|
|
DeleteOnClose = TRUE;
|
|
|
|
//
|
|
// We modify the Scb and Lcb here only if we aren't in the
|
|
// large allocation case.
|
|
//
|
|
|
|
if (!FlagOn( IrpContext->Flags, IRP_CONTEXT_LARGE_ALLOCATION )) {
|
|
|
|
SetFlag( ThisCcb->Flags, CCB_FLAG_DELETE_ON_CLOSE );
|
|
}
|
|
}
|
|
|
|
//
|
|
// If this is a named stream open and we have set any of our notify
|
|
// flags then report the changes.
|
|
//
|
|
|
|
if ((Vcb->NotifyCount != 0) &&
|
|
!FlagOn( ThisCcb->Flags, CCB_FLAG_OPEN_BY_FILE_ID ) &&
|
|
(ThisScb->AttributeName.Length != 0) &&
|
|
NtfsIsTypeCodeUserData( ThisScb->AttributeTypeCode ) &&
|
|
FlagOn( ThisScb->ScbState,
|
|
SCB_STATE_NOTIFY_ADD_STREAM |
|
|
SCB_STATE_NOTIFY_RESIZE_STREAM |
|
|
SCB_STATE_NOTIFY_MODIFY_STREAM )) {
|
|
|
|
ULONG Filter = 0;
|
|
ULONG Action;
|
|
|
|
//
|
|
// Start by checking for an add.
|
|
//
|
|
|
|
if (FlagOn( ThisScb->ScbState, SCB_STATE_NOTIFY_ADD_STREAM )) {
|
|
|
|
Filter = FILE_NOTIFY_CHANGE_STREAM_NAME;
|
|
Action = FILE_ACTION_ADDED_STREAM;
|
|
|
|
} else {
|
|
|
|
//
|
|
// Check if the file size changed.
|
|
//
|
|
|
|
if (FlagOn( ThisScb->ScbState, SCB_STATE_NOTIFY_RESIZE_STREAM )) {
|
|
|
|
Filter = FILE_NOTIFY_CHANGE_STREAM_SIZE;
|
|
}
|
|
|
|
//
|
|
// Now check if the stream data was modified.
|
|
//
|
|
|
|
if (FlagOn( ThisScb->ScbState, SCB_STATE_NOTIFY_MODIFY_STREAM )) {
|
|
|
|
Filter |= FILE_NOTIFY_CHANGE_STREAM_WRITE;
|
|
}
|
|
|
|
Action = FILE_ACTION_MODIFIED_STREAM;
|
|
}
|
|
|
|
NtfsUnsafeReportDirNotify( IrpContext,
|
|
Vcb,
|
|
&ThisCcb->FullFileName,
|
|
ThisCcb->LastFileNameOffset,
|
|
&ThisScb->AttributeName,
|
|
((FlagOn( ThisCcb->Flags, CCB_FLAG_PARENT_HAS_DOS_COMPONENT ) &&
|
|
ThisCcb->Lcb != NULL &&
|
|
ThisCcb->Lcb->Scb->ScbType.Index.NormalizedName.Buffer != NULL) ?
|
|
&ThisCcb->Lcb->Scb->ScbType.Index.NormalizedName :
|
|
NULL),
|
|
Filter,
|
|
Action,
|
|
NULL );
|
|
}
|
|
|
|
ClearFlag( ThisScb->ScbState,
|
|
SCB_STATE_NOTIFY_ADD_STREAM |
|
|
SCB_STATE_NOTIFY_REMOVE_STREAM |
|
|
SCB_STATE_NOTIFY_RESIZE_STREAM |
|
|
SCB_STATE_NOTIFY_MODIFY_STREAM );
|
|
|
|
//
|
|
// Otherwise copy the data out of the Scb/Fcb and return to our caller.
|
|
//
|
|
|
|
} else {
|
|
|
|
RtlZeroMemory( NetworkInfo, sizeof( FILE_NETWORK_OPEN_INFORMATION ));
|
|
|
|
//
|
|
// Fill in the basic information fields
|
|
//
|
|
|
|
NetworkInfo->CreationTime.QuadPart = CurrentFcb->Info.CreationTime;
|
|
NetworkInfo->LastWriteTime.QuadPart = CurrentFcb->Info.LastModificationTime;
|
|
NetworkInfo->ChangeTime.QuadPart = CurrentFcb->Info.LastChangeTime;
|
|
|
|
NetworkInfo->LastAccessTime.QuadPart = CurrentFcb->CurrentLastAccess;
|
|
|
|
NetworkInfo->FileAttributes = CurrentFcb->Info.FileAttributes;
|
|
|
|
ClearFlag( NetworkInfo->FileAttributes,
|
|
~FILE_ATTRIBUTE_VALID_FLAGS | FILE_ATTRIBUTE_TEMPORARY );
|
|
|
|
//
|
|
// DARRYLH - Do you want the directory bit set for streams in
|
|
// the directory.
|
|
//
|
|
|
|
if (ThisScb->AttributeTypeCode == $INDEX_ALLOCATION) {
|
|
|
|
if (IsDirectory( &CurrentFcb->Info )) {
|
|
|
|
SetFlag( NetworkInfo->FileAttributes, FILE_ATTRIBUTE_DIRECTORY );
|
|
|
|
//
|
|
// If this is not the main stream then copy the compression
|
|
// value from this Scb.
|
|
//
|
|
|
|
} else if (FlagOn( ThisScb->AttributeFlags, ATTRIBUTE_FLAG_COMPRESSION_MASK )) {
|
|
|
|
SetFlag( NetworkInfo->FileAttributes, FILE_ATTRIBUTE_COMPRESSED );
|
|
|
|
} else {
|
|
|
|
ClearFlag( NetworkInfo->FileAttributes, FILE_ATTRIBUTE_COMPRESSED );
|
|
}
|
|
|
|
//
|
|
// Return a non-zero size only for data streams.
|
|
//
|
|
|
|
} else {
|
|
|
|
NetworkInfo->AllocationSize.QuadPart = ThisScb->TotalAllocated;
|
|
NetworkInfo->EndOfFile = ThisScb->Header.FileSize;
|
|
|
|
//
|
|
// If not the unnamed data stream then use the Scb
|
|
// compression value.
|
|
//
|
|
|
|
if (!FlagOn( ThisScb->ScbState, SCB_STATE_UNNAMED_DATA )) {
|
|
|
|
if (FlagOn( ThisScb->AttributeFlags, ATTRIBUTE_FLAG_COMPRESSION_MASK )) {
|
|
|
|
SetFlag( NetworkInfo->FileAttributes, FILE_ATTRIBUTE_COMPRESSED );
|
|
|
|
} else {
|
|
|
|
ClearFlag( NetworkInfo->FileAttributes, FILE_ATTRIBUTE_COMPRESSED );
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Set the temporary flag if set in the ThisScb.
|
|
//
|
|
|
|
if (FlagOn( ThisScb->ScbState, SCB_STATE_TEMPORARY )) {
|
|
|
|
SetFlag( NetworkInfo->FileAttributes, FILE_ATTRIBUTE_TEMPORARY );
|
|
}
|
|
|
|
//
|
|
// If there are no flags set then explicitly set the NORMAL flag.
|
|
//
|
|
|
|
if (NetworkInfo->FileAttributes == 0) {
|
|
|
|
NetworkInfo->FileAttributes = FILE_ATTRIBUTE_NORMAL;
|
|
}
|
|
|
|
//
|
|
// Teardown the Fcb if we should.
|
|
//
|
|
|
|
if (!ThisScb->CleanupCount && !ThisScb->Fcb->DelayedCloseCount) {
|
|
if (!NtfsAddScbToFspClose( IrpContext, ThisScb, TRUE )) {
|
|
NtfsTeardownStructures( IrpContext,
|
|
CurrentFcb,
|
|
LcbForTeardown,
|
|
(BOOLEAN) (IrpContext->TransactionId != 0),
|
|
NtfsIsExclusiveScb( Vcb->MftScb ),
|
|
NULL );
|
|
}
|
|
}
|
|
|
|
Irp->IoStatus.Information = sizeof( FILE_NETWORK_OPEN_INFORMATION );
|
|
|
|
Status = Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// Start a teardown on the last Fcb found and restore the name strings on
|
|
// a retryable error.
|
|
//
|
|
|
|
} else {
|
|
|
|
//
|
|
// Start the cleanup process if we have looked at any Fcb's.
|
|
// We tell TeardownStructures not to remove any Scb's in
|
|
// the open attribute table if there is a transaction underway.
|
|
//
|
|
|
|
if (CurrentFcb != NULL) {
|
|
|
|
NtfsTeardownStructures( IrpContext,
|
|
(ThisScb != NULL) ? (PVOID) ThisScb : CurrentFcb,
|
|
LcbForTeardown,
|
|
(BOOLEAN) (IrpContext->TransactionId != 0),
|
|
NtfsIsExclusiveScb( Vcb->MftScb ),
|
|
NULL );
|
|
|
|
//
|
|
// Someone may have tried to open the $Bitmap stream. We catch that and
|
|
// fail it but the Fcb won't be in the exclusive list to be released.
|
|
//
|
|
|
|
if (NtfsEqualMftRef( &CurrentFcb->FileReference, &BitmapFileReference )) {
|
|
|
|
NtfsReleaseFcb( IrpContext, CurrentFcb );
|
|
}
|
|
}
|
|
|
|
if ((Status == STATUS_LOG_FILE_FULL) ||
|
|
(Status == STATUS_CANT_WAIT)) {
|
|
|
|
//
|
|
// Recover the exact case name if present for a retryable condition.
|
|
//
|
|
|
|
if (ExactCaseName->Buffer != NULL) {
|
|
|
|
RtlCopyMemory( FullFileName->Buffer,
|
|
ExactCaseName->Buffer,
|
|
ExactCaseName->MaximumLength );
|
|
}
|
|
}
|
|
|
|
//
|
|
// Free any buffer we allocated.
|
|
//
|
|
|
|
if ((FullFileName->Buffer != NULL) &&
|
|
(OriginalFileName->Buffer != FullFileName->Buffer)) {
|
|
|
|
NtfsFreePool( FullFileName->Buffer );
|
|
DebugDoit( FullFileName->Buffer = NULL );
|
|
}
|
|
|
|
//
|
|
// Set the file name in the file object back to it's original value.
|
|
//
|
|
|
|
OplockCleanup.FileObject->FileName = *OriginalFileName;
|
|
|
|
//
|
|
// Always clear the LARGE_ALLOCATION flag so we don't get
|
|
// spoofed by STATUS_REPARSE.
|
|
//
|
|
|
|
ClearFlag( IrpContext->Flags, IRP_CONTEXT_LARGE_ALLOCATION );
|
|
}
|
|
}
|
|
|
|
//
|
|
// Always free the exact case name if allocated.
|
|
//
|
|
|
|
if (ExactCaseName->Buffer != NULL) {
|
|
|
|
NtfsFreePool( ExactCaseName->Buffer );
|
|
DebugDoit( ExactCaseName->Buffer = NULL );
|
|
}
|
|
|
|
//
|
|
// We always give up the Vcb.
|
|
//
|
|
|
|
if (AcquiredVcb) {
|
|
|
|
if (DeleteVcb) {
|
|
|
|
NtfsReleaseVcbCheckDelete( IrpContext, Vcb, IRP_MJ_CREATE, NULL );
|
|
|
|
} else {
|
|
|
|
NtfsReleaseVcb( IrpContext, Vcb );
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// If we didn't post this Irp then take action to complete the irp.
|
|
//
|
|
|
|
if (Status != STATUS_PENDING) {
|
|
|
|
//
|
|
// If the current status is success and there is more allocation to
|
|
// allocate then complete the allocation.
|
|
//
|
|
|
|
if (FlagOn( IrpContext->Flags, IRP_CONTEXT_LARGE_ALLOCATION ) &&
|
|
NT_SUCCESS( Status )) {
|
|
|
|
//
|
|
// If the Create was successful, but we did not get all of the space
|
|
// allocated that we wanted, we have to complete the allocation now.
|
|
// Basically what we do is commit the current transaction and call
|
|
// NtfsAddAllocation to get the rest of the space. Then if the log
|
|
// file fills up (or we are posting for other reasons) we turn the
|
|
// Irp into an Irp which is just trying to extend the file. If we
|
|
// get any other kind of error, then we just delete the file and
|
|
// return with the error from create.
|
|
//
|
|
|
|
Status = NtfsCompleteLargeAllocation( IrpContext,
|
|
Irp,
|
|
CurrentLcb,
|
|
ThisScb,
|
|
ThisCcb,
|
|
CreateFileCase,
|
|
DeleteOnClose );
|
|
}
|
|
|
|
NtfsCompleteRequest( &IrpContext,
|
|
(ARGUMENT_PRESENT( NetworkInfo ) ? NULL : &Irp),
|
|
Status );
|
|
}
|
|
|
|
DebugTrace( -1, Dbg, ("NtfsCommonCreate: Exit -> %08lx\n", Status) );
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
NtfsCommonVolumeOpen (
|
|
IN PIRP_CONTEXT IrpContext,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is opening the Volume Dasd file. We have already done all the
|
|
checks needed to verify that the user is opening the $DATA attribute.
|
|
We check the security attached to the file and take some special action
|
|
based on a volume open.
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - The result of this operation.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
PIO_STACK_LOCATION IrpSp;
|
|
|
|
PVCB Vcb;
|
|
PFCB ThisFcb;
|
|
PCCB ThisCcb;
|
|
|
|
BOOLEAN VcbAcquired = FALSE;
|
|
BOOLEAN FcbAcquired = FALSE;
|
|
BOOLEAN DeleteVcb = FALSE;
|
|
|
|
BOOLEAN SharingViolation;
|
|
BOOLEAN LockVolume = FALSE;
|
|
|
|
PAGED_CODE();
|
|
|
|
DebugTrace( +1, Dbg, ("NtfsCommonVolumeOpen: Entered\n") );
|
|
|
|
IrpSp = IoGetCurrentIrpStackLocation( Irp );
|
|
|
|
//
|
|
// Use a try-finally to facilitate cleanup.
|
|
//
|
|
|
|
try {
|
|
|
|
//
|
|
// Start by checking the create disposition. We can only open this
|
|
// file.
|
|
//
|
|
|
|
{
|
|
ULONG CreateDisposition;
|
|
|
|
CreateDisposition = (IrpSp->Parameters.Create.Options >> 24) & 0x000000ff;
|
|
|
|
if (CreateDisposition != FILE_OPEN
|
|
&& CreateDisposition != FILE_OPEN_IF) {
|
|
|
|
try_return( Status = STATUS_ACCESS_DENIED );
|
|
}
|
|
}
|
|
|
|
//
|
|
// Make sure the directory flag isn't set for the volume open.
|
|
//
|
|
|
|
if (FlagOn( IrpSp->Parameters.Create.Options, FILE_DIRECTORY_FILE )) {
|
|
|
|
try_return( Status = STATUS_INVALID_PARAMETER );
|
|
}
|
|
|
|
//
|
|
// Acquire the Vcb and verify the volume isn't locked.
|
|
//
|
|
|
|
Vcb = &((PVOLUME_DEVICE_OBJECT) IrpSp->DeviceObject)->Vcb;
|
|
NtfsAcquireExclusiveVcb( IrpContext, Vcb, TRUE );
|
|
VcbAcquired = TRUE;
|
|
|
|
if (FlagOn( Vcb->VcbState, VCB_STATE_LOCKED | VCB_STATE_PERFORMED_DISMOUNT )) {
|
|
|
|
if (FlagOn( Vcb->VcbState, VCB_STATE_PERFORMED_DISMOUNT )) {
|
|
|
|
DeleteVcb = TRUE;
|
|
}
|
|
|
|
try_return( Status = STATUS_ACCESS_DENIED );
|
|
}
|
|
|
|
//
|
|
// Ping the volume to make sure the Vcb is still mounted. If we need
|
|
// to verify the volume then do it now, and if it comes out okay
|
|
// then clear the verify volume flag in the device object and continue
|
|
// on. If it doesn't verify okay then dismount the volume and
|
|
// either tell the I/O system to try and create again (with a new mount)
|
|
// or that the volume is wrong. This later code is returned if we
|
|
// are trying to do a relative open and the vcb is no longer mounted.
|
|
//
|
|
|
|
if (!NtfsPingVolume( IrpContext, Vcb )) {
|
|
|
|
if (!NtfsPerformVerifyOperation( IrpContext, Vcb )) {
|
|
|
|
NtfsPerformDismountOnVcb( IrpContext, Vcb, TRUE );
|
|
|
|
DeleteVcb = TRUE;
|
|
NtfsRaiseStatus( IrpContext, STATUS_WRONG_VOLUME, NULL, NULL );
|
|
}
|
|
|
|
//
|
|
// The volume verified correctly so now clear the verify bit
|
|
// and continue with the create
|
|
//
|
|
|
|
ClearFlag( Vcb->Vpb->RealDevice->Flags, DO_VERIFY_VOLUME );
|
|
}
|
|
|
|
//
|
|
// Now acquire the Fcb for the VolumeDasd and verify the user has
|
|
// permission to open the volume.
|
|
//
|
|
|
|
ThisFcb = Vcb->VolumeDasdScb->Fcb;
|
|
|
|
if (ThisFcb->PagingIoResource != NULL) {
|
|
|
|
NtfsAcquireExclusivePagingIo( IrpContext, ThisFcb );
|
|
}
|
|
|
|
ExAcquireResourceExclusive( ThisFcb->Resource, TRUE );
|
|
FcbAcquired = TRUE;
|
|
|
|
NtfsOpenCheck( IrpContext, ThisFcb, NULL, Irp );
|
|
|
|
//
|
|
// 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( IrpSp->Parameters.Create.ShareAccess,
|
|
FILE_SHARE_WRITE | FILE_SHARE_DELETE )) {
|
|
|
|
//
|
|
// Do a quick test of the volume cleanup count if this opener won't
|
|
// share with anyone. We can safely examine the cleanup count without
|
|
// further synchronization because we are guaranteed to have the
|
|
// Vcb exclusive at this point.
|
|
//
|
|
|
|
if (!FlagOn( IrpSp->Parameters.Create.ShareAccess, FILE_SHARE_READ) &&
|
|
Vcb->CleanupCount != 0) {
|
|
|
|
try_return( Status = STATUS_SHARING_VIOLATION );
|
|
}
|
|
|
|
//
|
|
// Go ahead and flush and purge the volume. Then test to see if all
|
|
// of the user file objects were closed.
|
|
//
|
|
|
|
Status = NtfsFlushVolume( IrpContext, Vcb, TRUE, TRUE, TRUE, FALSE );
|
|
|
|
//
|
|
// If the flush and purge was successful but there are still file objects
|
|
// that block this open it is possible that the FspClose thread is
|
|
// blocked behind the Vcb. Drop the Fcb and Vcb to allow this thread
|
|
// to get in and then reacquire them. This will give this Dasd open
|
|
// another chance to succeed on the first try.
|
|
//
|
|
|
|
SharingViolation = FALSE;
|
|
|
|
if (FlagOn( IrpSp->Parameters.Create.ShareAccess, FILE_SHARE_READ)) {
|
|
|
|
if (Vcb->ReadOnlyCloseCount != (Vcb->CloseCount - Vcb->SystemFileCloseCount)) {
|
|
|
|
SharingViolation = TRUE;
|
|
}
|
|
|
|
} else if (Vcb->CloseCount != Vcb->SystemFileCloseCount) {
|
|
|
|
SharingViolation = TRUE;
|
|
}
|
|
|
|
if (SharingViolation && NT_SUCCESS( Status )) {
|
|
|
|
//
|
|
// We need to commit the current transaction and release any
|
|
// resources. This will release the Fcb for the volume as
|
|
// well. Explicitly release the Vcb.
|
|
//
|
|
|
|
NtfsCheckpointCurrentTransaction( IrpContext );
|
|
|
|
while (!IsListEmpty(&IrpContext->ExclusiveFcbList)) {
|
|
|
|
NtfsReleaseFcbWithPaging( IrpContext,
|
|
(PFCB)CONTAINING_RECORD(IrpContext->ExclusiveFcbList.Flink,
|
|
FCB,
|
|
ExclusiveFcbLinks ));
|
|
}
|
|
|
|
if (ThisFcb->PagingIoResource != NULL) {
|
|
|
|
NtfsReleasePagingIo( IrpContext, ThisFcb );
|
|
}
|
|
|
|
ExReleaseResource( ThisFcb->Resource );
|
|
FcbAcquired = FALSE;
|
|
|
|
NtfsReleaseVcb( IrpContext, Vcb );
|
|
VcbAcquired = FALSE;
|
|
|
|
//
|
|
// Now explicitly reacquire the Vcb and Fcb. Test that no one
|
|
// else got in to lock the volume in the meantime.
|
|
//
|
|
|
|
NtfsAcquireExclusiveVcb( IrpContext, Vcb, TRUE );
|
|
VcbAcquired = TRUE;
|
|
|
|
if (FlagOn( Vcb->VcbState, VCB_STATE_LOCKED | VCB_STATE_PERFORMED_DISMOUNT )) {
|
|
|
|
if (FlagOn( Vcb->VcbState, VCB_STATE_PERFORMED_DISMOUNT )) {
|
|
|
|
DeleteVcb = TRUE;
|
|
}
|
|
|
|
try_return( Status = STATUS_ACCESS_DENIED );
|
|
}
|
|
|
|
//
|
|
// Now acquire the Fcb for the VolumeDasd.
|
|
//
|
|
|
|
if (ThisFcb->PagingIoResource != NULL) {
|
|
|
|
NtfsAcquireExclusivePagingIo( IrpContext, ThisFcb );
|
|
}
|
|
|
|
ExAcquireResourceExclusive( ThisFcb->Resource, TRUE );
|
|
FcbAcquired = TRUE;
|
|
|
|
//
|
|
// Duplicate the flush/purge and test if there is no sharing
|
|
// violation.
|
|
//
|
|
|
|
Status = NtfsFlushVolume( IrpContext, Vcb, TRUE, TRUE, TRUE, FALSE );
|
|
|
|
SharingViolation = FALSE;
|
|
|
|
if (FlagOn( IrpSp->Parameters.Create.ShareAccess, FILE_SHARE_READ)) {
|
|
|
|
if (Vcb->ReadOnlyCloseCount != (Vcb->CloseCount - Vcb->SystemFileCloseCount)) {
|
|
|
|
SharingViolation = TRUE;
|
|
}
|
|
|
|
} else if (Vcb->CloseCount != Vcb->SystemFileCloseCount) {
|
|
|
|
SharingViolation = TRUE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Return an error if there are still conflicting file objects.
|
|
//
|
|
|
|
if (SharingViolation) {
|
|
|
|
//
|
|
// If there was an error in the flush then return it. Otherwise
|
|
// return SHARING_VIOLATION.
|
|
//
|
|
|
|
if (NT_SUCCESS( Status )) {
|
|
|
|
try_return( Status = STATUS_SHARING_VIOLATION );
|
|
|
|
} else { try_return( Status ); }
|
|
}
|
|
|
|
|
|
if (!NT_SUCCESS( Status )) {
|
|
|
|
//
|
|
// If there are no conflicts but the status indicates disk corruption
|
|
// or a section that couldn't be removed then ignore the error. We
|
|
// allow this open to succeed so that chkdsk can open the volume to
|
|
// repair the damage.
|
|
//
|
|
|
|
if ((Status == STATUS_UNABLE_TO_DELETE_SECTION) ||
|
|
(Status == STATUS_DISK_CORRUPT_ERROR) ||
|
|
(Status == STATUS_FILE_CORRUPT_ERROR)) {
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
//
|
|
// Fail this request on any other failures.
|
|
//
|
|
|
|
} else {
|
|
|
|
try_return( Status );
|
|
}
|
|
}
|
|
|
|
//
|
|
// Remember that we want to lock the volume.
|
|
//
|
|
|
|
LockVolume = TRUE;
|
|
|
|
//
|
|
// Just flush the volume data if the user requested read or write.
|
|
// No need to purge or lock the volume.
|
|
//
|
|
|
|
} else if (FlagOn( IrpSp->Parameters.Create.SecurityContext->AccessState->PreviouslyGrantedAccess,
|
|
FILE_READ_DATA | FILE_WRITE_DATA | FILE_APPEND_DATA )) {
|
|
|
|
if (!NT_SUCCESS( Status = NtfsFlushVolume( IrpContext, Vcb, TRUE, FALSE, TRUE, FALSE ))) {
|
|
|
|
try_return( Status );
|
|
}
|
|
}
|
|
|
|
//
|
|
// Put the Volume Dasd name in the file object.
|
|
//
|
|
|
|
{
|
|
PVOID Temp = IrpSp->FileObject->FileName.Buffer;
|
|
|
|
IrpSp->FileObject->FileName.Buffer = FsRtlAllocatePoolWithTag(PagedPool, 8*2, MODULE_POOL_TAG );
|
|
|
|
if (Temp != NULL) {
|
|
|
|
NtfsFreePool( Temp );
|
|
}
|
|
|
|
RtlCopyMemory( IrpSp->FileObject->FileName.Buffer, L"\\$Volume", 8*2 );
|
|
IrpSp->FileObject->FileName.MaximumLength =
|
|
IrpSp->FileObject->FileName.Length = 8*2;
|
|
}
|
|
|
|
//
|
|
// We never allow cached access to the volume file.
|
|
//
|
|
|
|
ClearFlag( IrpSp->FileObject->Flags, FO_CACHE_SUPPORTED );
|
|
SetFlag( IrpSp->FileObject->Flags, FO_NO_INTERMEDIATE_BUFFERING );
|
|
|
|
//
|
|
// Go ahead open the attribute. This should only fail if there is an
|
|
// allocation failure or share access failure.
|
|
//
|
|
|
|
if (NT_SUCCESS( Status = NtfsOpenAttribute( IrpContext,
|
|
IrpSp,
|
|
Vcb,
|
|
NULL,
|
|
ThisFcb,
|
|
2,
|
|
NtfsEmptyString,
|
|
$DATA,
|
|
(ThisFcb->CleanupCount == 0 ?
|
|
SetShareAccess :
|
|
CheckShareAccess),
|
|
UserVolumeOpen,
|
|
CCB_FLAG_OPEN_AS_FILE,
|
|
NULL,
|
|
&Vcb->VolumeDasdScb,
|
|
&ThisCcb ))) {
|
|
|
|
//
|
|
// Perform the final initialization.
|
|
//
|
|
|
|
//
|
|
// If we are locking the volume, do so now.
|
|
//
|
|
|
|
if (LockVolume) {
|
|
|
|
SetFlag( Vcb->VcbState, VCB_STATE_LOCKED );
|
|
Vcb->FileObjectWithVcbLocked = IrpSp->FileObject;
|
|
}
|
|
|
|
//
|
|
// Report that we opened the volume.
|
|
//
|
|
|
|
Irp->IoStatus.Information = FILE_OPENED;
|
|
}
|
|
|
|
try_exit: NOTHING;
|
|
|
|
NtfsCleanupTransaction( IrpContext, Status, FALSE );
|
|
|
|
//
|
|
// If we have a successful open then remove the name out of
|
|
// the file object. The IO system gets confused when it
|
|
// is there. We will deallocate the buffer with the Ccb
|
|
// when the handle is closed.
|
|
//
|
|
|
|
if (Status == STATUS_SUCCESS) {
|
|
|
|
IrpSp->FileObject->FileName.Buffer = NULL;
|
|
IrpSp->FileObject->FileName.MaximumLength =
|
|
IrpSp->FileObject->FileName.Length = 0;
|
|
|
|
SetFlag( ThisCcb->Flags, CCB_FLAG_ALLOCATED_FILE_NAME );
|
|
}
|
|
|
|
} finally {
|
|
|
|
DebugUnwind( NtfsCommonVolumeOpen );
|
|
|
|
if (VcbAcquired) {
|
|
|
|
if (DeleteVcb) {
|
|
|
|
NtfsReleaseVcbCheckDelete( IrpContext, Vcb, IRP_MJ_CREATE, NULL );
|
|
|
|
} else {
|
|
|
|
NtfsReleaseVcb( IrpContext, Vcb );
|
|
}
|
|
}
|
|
|
|
if (FcbAcquired) { ExReleaseResource( ThisFcb->Resource ); }
|
|
|
|
if (!AbnormalTermination()) {
|
|
|
|
NtfsCompleteRequest( &IrpContext, &Irp, Status );
|
|
}
|
|
|
|
DebugTrace( -1, Dbg, ("NtfsCommonVolumeOpen: Exit -> %08lx\n", Status) );
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
//
|
|
// Local support routine
|
|
//
|
|
|
|
NTSTATUS
|
|
NtfsOpenFcbById (
|
|
IN PIRP_CONTEXT IrpContext,
|
|
IN PIRP Irp,
|
|
IN PIO_STACK_LOCATION IrpSp,
|
|
IN PVCB Vcb,
|
|
IN PLCB ParentLcb OPTIONAL,
|
|
IN OUT PFCB *CurrentFcb,
|
|
IN BOOLEAN UseCurrentFcb,
|
|
IN FILE_REFERENCE FileReference,
|
|
IN UNICODE_STRING AttrName,
|
|
IN UNICODE_STRING AttrCodeName,
|
|
IN PVOID NetworkInfo OPTIONAL,
|
|
OUT PSCB *ThisScb,
|
|
OUT PCCB *ThisCcb
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called to open a file by its file Id. We need to
|
|
verify that this file Id exists and then compare the type of the
|
|
file with the requested type of open.
|
|
|
|
Arguments:
|
|
|
|
Irp - This is the Irp for this open operation.
|
|
|
|
IrpSp - This is the Irp stack pointer for the filesystem.
|
|
|
|
Vcb - Vcb for this volume.
|
|
|
|
ParentLcb - Lcb used to reach this Fcb. Only specified when opening
|
|
a file by name relative to a directory opened by file Id.
|
|
|
|
CurrentFcb - Address of Fcb pointer. It will either be the
|
|
Fcb to open or we will store the Fcb we find here.
|
|
|
|
UseCurrentFcb - Indicate in the CurrentFcb above points to the target
|
|
Fcb or if we should find it here.
|
|
|
|
FileReference - This is the file Id for the file to open.
|
|
|
|
AttrName - This is the name of the attribute to open.
|
|
|
|
AttrCodeName - This is the name of the attribute code to open.
|
|
|
|
NetworkInfo - If specified then this call is a fast open call to query
|
|
the network information. We don't update any of the in-memory structures
|
|
for this.
|
|
|
|
ThisScb - This is the address to store the Scb from this open.
|
|
|
|
ThisCcb - This is the address to store the Ccb from this open.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Indicates the result of this create file operation.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
LONGLONG MftOffset;
|
|
PFILE_RECORD_SEGMENT_HEADER FileRecord;
|
|
PBCB Bcb = NULL;
|
|
|
|
BOOLEAN IndexedAttribute;
|
|
|
|
PFCB ThisFcb;
|
|
BOOLEAN ExistingFcb = FALSE;
|
|
|
|
ULONG CcbFlags = 0;
|
|
ATTRIBUTE_TYPE_CODE AttrTypeCode;
|
|
OLD_SCB_SNAPSHOT ScbSizes;
|
|
BOOLEAN HaveScbSizes = FALSE;
|
|
BOOLEAN DecrementCloseCount = FALSE;
|
|
|
|
PSCB ParentScb = NULL;
|
|
PLCB Lcb = ParentLcb;
|
|
BOOLEAN AcquiredParentScb = FALSE;
|
|
|
|
BOOLEAN AcquiredFcbTable = FALSE;
|
|
|
|
UNREFERENCED_PARAMETER( NetworkInfo );
|
|
|
|
PAGED_CODE();
|
|
|
|
DebugTrace( +1, Dbg, ("NtfsOpenFcbById: Entered\n") );
|
|
|
|
//
|
|
// The next thing to do is to figure out what type
|
|
// of attribute the caller is trying to open. This involves the
|
|
// directory/non-directory bits, the attribute name and code strings,
|
|
// the type of file, whether he passed in an ea buffer and whether
|
|
// there was a trailing backslash.
|
|
//
|
|
|
|
if (NtfsEqualMftRef( &FileReference,
|
|
&VolumeFileReference )) {
|
|
|
|
if (AttrName.Length != 0
|
|
|| AttrCodeName.Length != 0) {
|
|
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
DebugTrace( -1, Dbg, ("NtfsOpenFcbById: Exit -> %08lx\n", Status) );
|
|
|
|
return Status;
|
|
}
|
|
|
|
SetFlag( IrpContext->Flags,
|
|
IRP_CONTEXT_FLAG_ACQUIRE_VCB_EX | IRP_CONTEXT_FLAG_DASD_OPEN );
|
|
|
|
NtfsRaiseStatus( IrpContext, STATUS_CANT_WAIT, NULL, NULL );
|
|
}
|
|
|
|
//
|
|
// Use a try-finally to facilitate cleanup.
|
|
//
|
|
|
|
try {
|
|
|
|
//
|
|
// If we don't already have the Fcb then look up the file record
|
|
// from the disk.
|
|
//
|
|
|
|
if (!UseCurrentFcb) {
|
|
|
|
//
|
|
// We start by reading the disk and checking that the file record
|
|
// sequence number matches and that the file record is in use.
|
|
// We remember whether this is a directory. We will only go to
|
|
// the file if the file Id will lie within the Mft File.
|
|
//
|
|
|
|
MftOffset = NtfsFullSegmentNumber( &FileReference );
|
|
|
|
MftOffset = Int64ShllMod32(MftOffset, Vcb->MftShift);
|
|
|
|
if (MftOffset >= Vcb->MftScb->Header.FileSize.QuadPart) {
|
|
|
|
DebugTrace( 0, Dbg, ("File Id doesn't lie within Mft\n") );
|
|
|
|
try_return( Status = STATUS_INVALID_PARAMETER );
|
|
}
|
|
|
|
NtfsReadMftRecord( IrpContext,
|
|
Vcb,
|
|
&FileReference,
|
|
&Bcb,
|
|
&FileRecord,
|
|
NULL );
|
|
|
|
//
|
|
// This file record better be in use, have a matching sequence number and
|
|
// be the primary file record for this file.
|
|
//
|
|
|
|
if (FileRecord->SequenceNumber != FileReference.SequenceNumber
|
|
|| !FlagOn( FileRecord->Flags, FILE_RECORD_SEGMENT_IN_USE )
|
|
|| (*((PLONGLONG)&FileRecord->BaseFileRecordSegment) != 0)) {
|
|
|
|
try_return( Status = STATUS_INVALID_PARAMETER );
|
|
}
|
|
|
|
//
|
|
// We perform a check to see whether we will allow the system
|
|
// files to be opened.
|
|
//
|
|
|
|
if (NtfsProtectSystemFiles) {
|
|
|
|
//
|
|
// We only allow user opens on the Volume Dasd file and the
|
|
// root directory.
|
|
//
|
|
|
|
if (NtfsSegmentNumber( &FileReference ) < FIRST_USER_FILE_NUMBER
|
|
&& NtfsSegmentNumber( &FileReference ) != VOLUME_DASD_NUMBER
|
|
&& NtfsSegmentNumber( &FileReference ) != ROOT_FILE_NAME_INDEX_NUMBER) {
|
|
|
|
Status = STATUS_ACCESS_DENIED;
|
|
DebugTrace( 0, Dbg, ("Attempting to open system files\n") );
|
|
|
|
try_return( NOTHING );
|
|
}
|
|
}
|
|
|
|
//
|
|
// If indexed then use the name for the file name index.
|
|
//
|
|
|
|
if (FlagOn( FileRecord->Flags, FILE_FILE_NAME_INDEX_PRESENT )) {
|
|
|
|
AttrName = NtfsFileNameIndex;
|
|
AttrCodeName = NtfsIndexAllocation;
|
|
}
|
|
|
|
NtfsUnpinBcb( &Bcb );
|
|
|
|
} else {
|
|
|
|
ThisFcb = *CurrentFcb;
|
|
ExistingFcb = TRUE;
|
|
}
|
|
|
|
Status = NtfsCheckValidAttributeAccess( IrpSp,
|
|
Vcb,
|
|
ExistingFcb ? &ThisFcb->Info : NULL,
|
|
AttrName,
|
|
AttrCodeName,
|
|
FALSE,
|
|
&AttrTypeCode,
|
|
&CcbFlags,
|
|
&IndexedAttribute );
|
|
|
|
if (!NT_SUCCESS( Status )) {
|
|
|
|
try_return( Status );
|
|
}
|
|
|
|
//
|
|
// If we don't have an Fcb then create one now.
|
|
//
|
|
|
|
if (!UseCurrentFcb) {
|
|
|
|
NtfsAcquireFcbTable( IrpContext, Vcb );
|
|
AcquiredFcbTable = TRUE;
|
|
|
|
//
|
|
// We know that it is safe to continue the open. We start by creating
|
|
// an Fcb for this file. It is possible that the Fcb exists.
|
|
// We create the Fcb first, if we need to update the Fcb info structure
|
|
// we copy the one from the index entry. We look at the Fcb to discover
|
|
// if it has any links, if it does then we make this the last Fcb we
|
|
// reached. If it doesn't then we have to clean it up from here.
|
|
//
|
|
|
|
ThisFcb = NtfsCreateFcb( IrpContext,
|
|
Vcb,
|
|
FileReference,
|
|
BooleanFlagOn( IrpSp->Flags, SL_OPEN_PAGING_FILE ),
|
|
TRUE,
|
|
&ExistingFcb );
|
|
|
|
ThisFcb->ReferenceCount += 1;
|
|
|
|
//
|
|
// Try to do a fast acquire, otherwise we need to release
|
|
// the Fcb table, acquire the Fcb, acquire the Fcb table to
|
|
// dereference Fcb.
|
|
//
|
|
|
|
if (!NtfsAcquireFcbWithPaging( IrpContext, ThisFcb, TRUE )) {
|
|
|
|
NtfsReleaseFcbTable( IrpContext, Vcb );
|
|
NtfsAcquireFcbWithPaging( IrpContext, ThisFcb, FALSE );
|
|
NtfsAcquireFcbTable( IrpContext, Vcb );
|
|
}
|
|
|
|
ThisFcb->ReferenceCount -= 1;
|
|
|
|
NtfsReleaseFcbTable( IrpContext, Vcb );
|
|
AcquiredFcbTable = FALSE;
|
|
|
|
//
|
|
// Store this Fcb into our caller's parameter and remember to
|
|
// to show we acquired it.
|
|
//
|
|
|
|
*CurrentFcb = ThisFcb;
|
|
}
|
|
|
|
//
|
|
// If the Fcb existed and this is a paging file then either return
|
|
// sharing violation or force the Fcb and Scb's to go away.
|
|
// Do this for the case where the user is opening a paging file
|
|
// but the Fcb is non-paged or the user is opening a non-paging
|
|
// file and the Fcb is for a paging file.
|
|
//
|
|
|
|
if (ExistingFcb &&
|
|
|
|
((FlagOn( IrpSp->Flags, SL_OPEN_PAGING_FILE ) &&
|
|
!FlagOn( ThisFcb->FcbState, FCB_STATE_PAGING_FILE )) ||
|
|
|
|
(FlagOn( ThisFcb->FcbState, FCB_STATE_PAGING_FILE ) &&
|
|
!FlagOn( IrpSp->Flags, SL_OPEN_PAGING_FILE )))) {
|
|
|
|
if (ThisFcb->CleanupCount != 0) {
|
|
|
|
try_return( Status = STATUS_SHARING_VIOLATION );
|
|
|
|
//
|
|
// If we have a persistent paging file then give up and
|
|
// return SHARING_VIOLATION.
|
|
//
|
|
|
|
} else if (FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_IN_FSP )) {
|
|
|
|
try_return( Status = STATUS_SHARING_VIOLATION );
|
|
|
|
//
|
|
// If there was an existing Fcb for a paging file we need to force
|
|
// all of the Scb's to be torn down. The easiest way to do this
|
|
// is to flush and purge all of the Scb's (saving any attribute list
|
|
// for last) and then raise LOG_FILE_FULL to allow this request to
|
|
// be posted.
|
|
//
|
|
|
|
} else {
|
|
|
|
//
|
|
// Reference the Fcb so it doesn't go away.
|
|
//
|
|
|
|
InterlockedIncrement( &ThisFcb->CloseCount );
|
|
DecrementCloseCount = TRUE;
|
|
|
|
//
|
|
// Flush and purge this Fcb.
|
|
//
|
|
|
|
NtfsFlushAndPurgeFcb( IrpContext, ThisFcb );
|
|
|
|
InterlockedDecrement( &ThisFcb->CloseCount );
|
|
DecrementCloseCount = FALSE;
|
|
|
|
//
|
|
// Force this request to be posted and then raise
|
|
// CANT_WAIT.
|
|
//
|
|
|
|
SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_FORCE_POST );
|
|
NtfsRaiseStatus( IrpContext, STATUS_CANT_WAIT, NULL, NULL );
|
|
}
|
|
}
|
|
|
|
//
|
|
// If the Fcb Info field needs to be initialized, we do so now.
|
|
// We read this information from the disk.
|
|
//
|
|
|
|
if (!FlagOn( ThisFcb->FcbState, FCB_STATE_DUP_INITIALIZED )) {
|
|
|
|
NtfsUpdateFcbInfoFromDisk( IrpContext,
|
|
TRUE,
|
|
ThisFcb,
|
|
NULL,
|
|
&ScbSizes );
|
|
|
|
HaveScbSizes = TRUE;
|
|
|
|
//
|
|
// Fix the quota for this file if necessary.
|
|
//
|
|
|
|
NtfsConditionallyFixupQuota( IrpContext, ThisFcb );
|
|
|
|
}
|
|
|
|
//
|
|
// If the link count is zero on this Fcb, then delete is pending.
|
|
//
|
|
|
|
if (ThisFcb->LinkCount == 0) {
|
|
|
|
try_return( Status = STATUS_DELETE_PENDING );
|
|
}
|
|
|
|
//
|
|
// We now call the worker routine to open an attribute on an existing file.
|
|
//
|
|
|
|
Status = NtfsOpenAttributeInExistingFile( IrpContext,
|
|
Irp,
|
|
IrpSp,
|
|
ParentLcb,
|
|
ThisFcb,
|
|
0,
|
|
AttrName,
|
|
AttrTypeCode,
|
|
CcbFlags,
|
|
TRUE,
|
|
NULL,
|
|
ThisScb,
|
|
ThisCcb );
|
|
|
|
//
|
|
// Check to see if we should update the last access time.
|
|
//
|
|
|
|
if (NT_SUCCESS( Status ) && (Status != STATUS_PENDING)) {
|
|
|
|
PSCB Scb = *ThisScb;
|
|
|
|
//
|
|
// Now look at whether we need to update the Fcb and on disk
|
|
// structures.
|
|
//
|
|
|
|
NtfsCheckLastAccess( IrpContext, ThisFcb );
|
|
|
|
//
|
|
// Perform the last bit of work. If this a user file open, we need
|
|
// to check if we initialize the Scb.
|
|
//
|
|
|
|
if (!IndexedAttribute) {
|
|
|
|
if (!FlagOn( Scb->ScbState, SCB_STATE_HEADER_INITIALIZED )) {
|
|
|
|
//
|
|
// We may have the sizes from our Fcb update call.
|
|
//
|
|
|
|
if (HaveScbSizes &&
|
|
(AttrTypeCode == $DATA) &&
|
|
(AttrName.Length == 0) &&
|
|
!FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_CREATE_MOD_SCB )) {
|
|
|
|
NtfsUpdateScbFromMemory( IrpContext, Scb, &ScbSizes );
|
|
|
|
} else {
|
|
|
|
NtfsUpdateScbFromAttribute( IrpContext, Scb, NULL );
|
|
}
|
|
}
|
|
|
|
//
|
|
// If there is a potential for a write call to be issued, then we
|
|
// need to expand the quota.
|
|
//
|
|
|
|
if (IrpSp->FileObject->WriteAccess) {
|
|
|
|
NtfsExpandQuotaToAllocationSize( IrpContext, Scb );
|
|
|
|
}
|
|
|
|
//
|
|
// Let's check if we need to set the cache bit.
|
|
//
|
|
|
|
if (!FlagOn( IrpSp->Parameters.Create.Options, FILE_NO_INTERMEDIATE_BUFFERING )) {
|
|
|
|
SetFlag( IrpSp->FileObject->Flags, FO_CACHE_SUPPORTED );
|
|
}
|
|
}
|
|
|
|
//
|
|
// If this operation was a supersede/overwrite or we created a new
|
|
// attribute stream then we want to perform the file record and
|
|
// directory update now. Otherwise we will defer the updates until
|
|
// the user closes his handle.
|
|
//
|
|
|
|
if ((Irp->IoStatus.Information == FILE_CREATED) ||
|
|
(Irp->IoStatus.Information == FILE_SUPERSEDED) ||
|
|
(Irp->IoStatus.Information == FILE_OVERWRITTEN)) {
|
|
|
|
NtfsUpdateScbFromFileObject( IrpContext, IrpSp->FileObject, *ThisScb, TRUE );
|
|
|
|
//
|
|
// Do the standard information, file sizes and then duplicate information
|
|
// if needed.
|
|
//
|
|
|
|
if (FlagOn( ThisFcb->FcbState, FCB_STATE_UPDATE_STD_INFO )) {
|
|
|
|
NtfsUpdateStandardInformation( IrpContext, ThisFcb );
|
|
}
|
|
|
|
if (FlagOn( (*ThisScb)->ScbState, SCB_STATE_CHECK_ATTRIBUTE_SIZE )) {
|
|
|
|
NtfsWriteFileSizes( IrpContext,
|
|
*ThisScb,
|
|
&(*ThisScb)->Header.ValidDataLength.QuadPart,
|
|
FALSE,
|
|
TRUE );
|
|
}
|
|
|
|
if (FlagOn( ThisFcb->InfoFlags, FCB_INFO_DUPLICATE_FLAGS )) {
|
|
|
|
NtfsPrepareForUpdateDuplicate( IrpContext, ThisFcb, &Lcb, &ParentScb, FALSE );
|
|
NtfsUpdateDuplicateInfo( IrpContext, ThisFcb, NULL, NULL );
|
|
NtfsUpdateLcbDuplicateInfo( ThisFcb, Lcb );
|
|
ThisFcb->InfoFlags = 0;
|
|
}
|
|
|
|
ClearFlag( ThisFcb->FcbState, FCB_STATE_UPDATE_STD_INFO );
|
|
|
|
NtfsAcquireFsrtlHeader( *ThisScb );
|
|
ClearFlag( (*ThisScb)->ScbState, SCB_STATE_CHECK_ATTRIBUTE_SIZE );
|
|
NtfsReleaseFsrtlHeader( *ThisScb );
|
|
}
|
|
}
|
|
|
|
try_exit: NOTHING;
|
|
} finally {
|
|
|
|
DebugUnwind( NtfsOpenFcbById );
|
|
|
|
if (AcquiredFcbTable) {
|
|
|
|
NtfsReleaseFcbTable( IrpContext, Vcb );
|
|
}
|
|
|
|
//
|
|
// If this operation was not totally successful we need to
|
|
// back out the following changes.
|
|
//
|
|
// Modifications to the Info fields in the Fcb.
|
|
// Any changes to the allocation of the Scb.
|
|
// Any changes in the open counts in the various structures.
|
|
// Changes to the share access values in the Fcb.
|
|
//
|
|
|
|
if (!NT_SUCCESS( Status ) || AbnormalTermination()) {
|
|
|
|
NtfsBackoutFailedOpens( IrpContext,
|
|
IrpSp->FileObject,
|
|
ThisFcb,
|
|
*ThisScb,
|
|
*ThisCcb );
|
|
}
|
|
|
|
if (DecrementCloseCount) {
|
|
|
|
InterlockedDecrement( &ThisFcb->CloseCount );
|
|
}
|
|
|
|
NtfsUnpinBcb( &Bcb );
|
|
|
|
DebugTrace( -1, Dbg, ("NtfsOpenFcbById: Exit -> %08lx\n", Status) );
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
//
|
|
// Local support routine
|
|
//
|
|
|
|
NTSTATUS
|
|
NtfsOpenExistingPrefixFcb (
|
|
IN PIRP_CONTEXT IrpContext,
|
|
IN PIRP Irp,
|
|
IN PIO_STACK_LOCATION IrpSp,
|
|
IN PFCB ThisFcb,
|
|
IN PLCB Lcb OPTIONAL,
|
|
IN ULONG FullPathNameLength,
|
|
IN UNICODE_STRING AttrName,
|
|
IN UNICODE_STRING AttrCodeName,
|
|
IN BOOLEAN DosOnlyComponent,
|
|
IN BOOLEAN TrailingBackslash,
|
|
IN PVOID NetworkInfo OPTIONAL,
|
|
OUT PSCB *ThisScb,
|
|
OUT PCCB *ThisCcb
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will open an attribute in a file whose Fcb was found
|
|
with a prefix search.
|
|
|
|
Arguments:
|
|
|
|
Irp - This is the Irp for this open operation.
|
|
|
|
IrpSp - This is the Irp stack pointer for the filesystem.
|
|
|
|
ThisFcb - This is the Fcb to open.
|
|
|
|
Lcb - This is the Lcb used to reach this Fcb. Not specified if this is a volume open.
|
|
|
|
FullPathNameLength - This is the length of the full path name.
|
|
|
|
AttrName - This is the name of the attribute to open.
|
|
|
|
AttrCodeName - This is the name of the attribute code to open.
|
|
|
|
DosOnlyComponent - Indicates if there is a DOS-ONLY component in an ancestor
|
|
of this open.
|
|
|
|
TrailingBackslash - Indicates if caller had a terminating backslash on the
|
|
name.
|
|
|
|
NetworkInfo - If specified then this call is a fast open call to query
|
|
the network information. We don't update any of the in-memory structures
|
|
for this.
|
|
|
|
ThisScb - This is the address to store the Scb from this open.
|
|
|
|
ThisCcb - This is the address to store the Ccb from this open.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Indicates the result of this attribute based operation.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
ATTRIBUTE_TYPE_CODE AttrTypeCode;
|
|
ULONG CcbFlags;
|
|
BOOLEAN IndexedAttribute;
|
|
BOOLEAN DecrementCloseCount = FALSE;
|
|
|
|
ULONG LastFileNameOffset;
|
|
|
|
OLD_SCB_SNAPSHOT ScbSizes;
|
|
BOOLEAN HaveScbSizes = FALSE;
|
|
|
|
ULONG CreateDisposition;
|
|
|
|
PSCB ParentScb = NULL;
|
|
PFCB ParentFcb = NULL;
|
|
BOOLEAN AcquiredParentScb = FALSE;
|
|
|
|
LONGLONG CurrentTime;
|
|
|
|
PAGED_CODE();
|
|
|
|
DebugTrace( +1, Dbg, ("NtfsOpenExistingPrefixFcb: Entered\n") );
|
|
|
|
if (DosOnlyComponent) {
|
|
|
|
CcbFlags = CCB_FLAG_PARENT_HAS_DOS_COMPONENT;
|
|
|
|
} else {
|
|
|
|
CcbFlags = 0;
|
|
}
|
|
|
|
//
|
|
// The first thing to do is to figure out what type
|
|
// of attribute the caller is trying to open. This involves the
|
|
// directory/non-directory bits, the attribute name and code strings,
|
|
// the type of file, whether he passed in an ea buffer and whether
|
|
// there was a trailing backslash.
|
|
//
|
|
|
|
if (NtfsEqualMftRef( &ThisFcb->FileReference, &VolumeFileReference )) {
|
|
|
|
if ((AttrName.Length != 0) || (AttrCodeName.Length != 0)) {
|
|
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
DebugTrace( -1, Dbg, ("NtfsOpenExistingPrefixFcb: Exit -> %08lx\n", Status) );
|
|
|
|
return Status;
|
|
}
|
|
|
|
SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_ACQUIRE_VCB_EX | IRP_CONTEXT_FLAG_DASD_OPEN );
|
|
|
|
NtfsRaiseStatus( IrpContext, STATUS_CANT_WAIT, NULL, NULL );
|
|
}
|
|
|
|
ParentScb = Lcb->Scb;
|
|
|
|
LastFileNameOffset = FullPathNameLength - Lcb->ExactCaseLink.LinkName.Length;
|
|
|
|
if (ParentScb != NULL) {
|
|
|
|
ParentFcb = ParentScb->Fcb;
|
|
}
|
|
|
|
Status = NtfsCheckValidAttributeAccess( IrpSp,
|
|
ThisFcb->Vcb,
|
|
&ThisFcb->Info,
|
|
AttrName,
|
|
AttrCodeName,
|
|
TrailingBackslash,
|
|
&AttrTypeCode,
|
|
&CcbFlags,
|
|
&IndexedAttribute );
|
|
|
|
if (!NT_SUCCESS( Status )) {
|
|
|
|
DebugTrace( -1, Dbg, ("NtfsOpenExistingPrefixFcb: Exit -> %08lx\n", Status) );
|
|
|
|
return Status;
|
|
}
|
|
|
|
CreateDisposition = (IrpSp->Parameters.Create.Options >> 24) & 0x000000ff;
|
|
|
|
//
|
|
// Use a try-finally to facilitate cleanup.
|
|
//
|
|
|
|
try {
|
|
|
|
//
|
|
// If the Fcb existed and this is a paging file then either return
|
|
// sharing violation or force the Fcb and Scb's to go away.
|
|
// Do this for the case where the user is opening a paging file
|
|
// but the Fcb is non-paged or the user is opening a non-paging
|
|
// file and the Fcb is for a paging file.
|
|
//
|
|
|
|
if ((FlagOn( IrpSp->Flags, SL_OPEN_PAGING_FILE ) &&
|
|
!FlagOn( ThisFcb->FcbState, FCB_STATE_PAGING_FILE )) ||
|
|
|
|
(FlagOn( ThisFcb->FcbState, FCB_STATE_PAGING_FILE ) &&
|
|
!FlagOn( IrpSp->Flags, SL_OPEN_PAGING_FILE ))) {
|
|
|
|
if (ThisFcb->CleanupCount != 0) {
|
|
|
|
try_return( Status = STATUS_SHARING_VIOLATION );
|
|
|
|
//
|
|
// If we have a persistent paging file then give up and
|
|
// return SHARING_VIOLATION.
|
|
//
|
|
|
|
} else if (FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_IN_FSP )) {
|
|
|
|
try_return( Status = STATUS_SHARING_VIOLATION );
|
|
|
|
//
|
|
// If there was an existing Fcb for a paging file we need to force
|
|
// all of the Scb's to be torn down. The easiest way to do this
|
|
// is to flush and purge all of the Scb's (saving any attribute list
|
|
// for last) and then raise LOG_FILE_FULL to allow this request to
|
|
// be posted.
|
|
//
|
|
|
|
} else {
|
|
|
|
//
|
|
// Make sure this Fcb won't go away as a result of purging
|
|
// the Fcb.
|
|
//
|
|
|
|
InterlockedIncrement( &ThisFcb->CloseCount );
|
|
DecrementCloseCount = TRUE;
|
|
|
|
//
|
|
// Flush and purge this Fcb.
|
|
//
|
|
|
|
NtfsFlushAndPurgeFcb( IrpContext, ThisFcb );
|
|
|
|
//
|
|
// Now decrement the close count we have already biased.
|
|
//
|
|
|
|
InterlockedDecrement( &ThisFcb->CloseCount );
|
|
DecrementCloseCount = FALSE;
|
|
|
|
//
|
|
// Force this request to be posted and then raise
|
|
// CANT_WAIT.
|
|
//
|
|
|
|
SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_FORCE_POST );
|
|
NtfsRaiseStatus( IrpContext, STATUS_CANT_WAIT, NULL, NULL );
|
|
}
|
|
}
|
|
|
|
//
|
|
// If this is a directory, it's possible that we hav an existing Fcb
|
|
// in the prefix table which needs to be initialized from the disk.
|
|
// We look in the InfoInitialized flag to know whether to go to
|
|
// disk.
|
|
//
|
|
|
|
if (!FlagOn( ThisFcb->FcbState, FCB_STATE_DUP_INITIALIZED )) {
|
|
|
|
//
|
|
// If we have a parent Fcb then make sure to acquire it.
|
|
//
|
|
|
|
if (ParentScb != NULL) {
|
|
|
|
NtfsAcquireExclusiveScb( IrpContext, ParentScb );
|
|
AcquiredParentScb = TRUE;
|
|
}
|
|
|
|
NtfsUpdateFcbInfoFromDisk( IrpContext,
|
|
TRUE,
|
|
ThisFcb,
|
|
ParentFcb,
|
|
&ScbSizes );
|
|
|
|
HaveScbSizes = TRUE;
|
|
|
|
NtfsConditionallyFixupQuota( IrpContext, ThisFcb );
|
|
}
|
|
|
|
//
|
|
// Check now whether we will need to acquire the parent to
|
|
// perform a update duplicate info. We need to acquire it
|
|
// now to enforce our locking order in case any of the
|
|
// routines below acquire the Mft Scb. Acquire it if we
|
|
// are doing a supersede/overwrite or possibly creating
|
|
// a named data stream.
|
|
//
|
|
|
|
if ((CreateDisposition == FILE_SUPERSEDE) ||
|
|
(CreateDisposition == FILE_OVERWRITE) ||
|
|
(CreateDisposition == FILE_OVERWRITE_IF) ||
|
|
((AttrName.Length != 0) &&
|
|
((CreateDisposition == FILE_OPEN_IF) ||
|
|
(CreateDisposition == FILE_CREATE)))) {
|
|
|
|
NtfsPrepareForUpdateDuplicate( IrpContext,
|
|
ThisFcb,
|
|
&Lcb,
|
|
&ParentScb,
|
|
FALSE );
|
|
}
|
|
|
|
//
|
|
// Call to open an attribute on an existing file.
|
|
// Remember we need to restore the Fcb info structure
|
|
// on errors.
|
|
//
|
|
|
|
Status = NtfsOpenAttributeInExistingFile( IrpContext,
|
|
Irp,
|
|
IrpSp,
|
|
Lcb,
|
|
ThisFcb,
|
|
LastFileNameOffset,
|
|
AttrName,
|
|
AttrTypeCode,
|
|
CcbFlags,
|
|
FALSE,
|
|
NetworkInfo,
|
|
ThisScb,
|
|
ThisCcb );
|
|
|
|
//
|
|
// Check to see if we should update the last access time.
|
|
//
|
|
|
|
if (NT_SUCCESS( Status ) && (Status != STATUS_PENDING)) {
|
|
|
|
PSCB Scb = *ThisScb;
|
|
|
|
//
|
|
// This is a rare case. There must have been an allocation failure
|
|
// to cause this but make sure the normalized name is stored.
|
|
//
|
|
|
|
if ((SafeNodeType( Scb ) == NTFS_NTC_SCB_INDEX) &&
|
|
(Scb->ScbType.Index.NormalizedName.Buffer == NULL) &&
|
|
(ParentScb != NULL) &&
|
|
(ParentScb->ScbType.Index.NormalizedName.Buffer != NULL)) {
|
|
|
|
NtfsUpdateNormalizedName( IrpContext,
|
|
ParentScb,
|
|
Scb,
|
|
NULL,
|
|
FALSE );
|
|
}
|
|
|
|
//
|
|
// Perform the last bit of work. If this a user file open, we need
|
|
// to check if we initialize the Scb.
|
|
//
|
|
|
|
if (!IndexedAttribute) {
|
|
|
|
if (!FlagOn( Scb->ScbState, SCB_STATE_HEADER_INITIALIZED )) {
|
|
|
|
//
|
|
// We may have the sizes from our Fcb update call.
|
|
//
|
|
|
|
if (HaveScbSizes &&
|
|
(AttrTypeCode == $DATA) &&
|
|
(AttrName.Length == 0) &&
|
|
!FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_CREATE_MOD_SCB )) {
|
|
|
|
NtfsUpdateScbFromMemory( IrpContext, Scb, &ScbSizes );
|
|
|
|
} else {
|
|
|
|
NtfsUpdateScbFromAttribute( IrpContext, Scb, NULL );
|
|
|
|
}
|
|
}
|
|
|
|
if (IrpSp->FileObject->WriteAccess) {
|
|
NtfsExpandQuotaToAllocationSize( IrpContext, Scb );
|
|
}
|
|
|
|
//
|
|
// Let's check if we need to set the cache bit.
|
|
//
|
|
|
|
if (!FlagOn( IrpSp->Parameters.Create.Options,
|
|
FILE_NO_INTERMEDIATE_BUFFERING )) {
|
|
|
|
SetFlag( IrpSp->FileObject->Flags, FO_CACHE_SUPPORTED );
|
|
}
|
|
}
|
|
|
|
//
|
|
// If this is the paging file, we want to be sure the allocation
|
|
// is loaded.
|
|
//
|
|
|
|
if (FlagOn( ThisFcb->FcbState, FCB_STATE_PAGING_FILE )
|
|
&& (Scb->Header.AllocationSize.QuadPart != 0)
|
|
&& !FlagOn( Scb->ScbState, SCB_STATE_ATTRIBUTE_RESIDENT )) {
|
|
|
|
LCN Lcn;
|
|
VCN Vcn;
|
|
VCN AllocatedVcns;
|
|
|
|
AllocatedVcns = Int64ShraMod32(Scb->Header.AllocationSize.QuadPart, Scb->Vcb->ClusterShift);
|
|
|
|
//
|
|
// First make sure the Mcb is loaded.
|
|
//
|
|
|
|
NtfsPreloadAllocation( IrpContext, Scb, 0, AllocatedVcns );
|
|
|
|
//
|
|
// Now make sure the allocation is correctly loaded. The last
|
|
// Vcn should correspond to the allocation size for the file.
|
|
//
|
|
|
|
if (!NtfsLookupLastNtfsMcbEntry( &Scb->Mcb,
|
|
&Vcn,
|
|
&Lcn ) ||
|
|
(Vcn + 1) != AllocatedVcns) {
|
|
|
|
NtfsRaiseStatus( IrpContext,
|
|
STATUS_FILE_CORRUPT_ERROR,
|
|
NULL,
|
|
ThisFcb );
|
|
}
|
|
}
|
|
|
|
//
|
|
// If this open is for an executable image we will want to update the
|
|
// last access time.
|
|
//
|
|
|
|
if (FlagOn( IrpSp->Parameters.Create.SecurityContext->DesiredAccess, FILE_EXECUTE ) &&
|
|
(Scb->AttributeTypeCode == $DATA)) {
|
|
|
|
SetFlag( IrpSp->FileObject->Flags, FO_FILE_FAST_IO_READ );
|
|
}
|
|
|
|
//
|
|
// If this operation was a supersede/overwrite or we created a new
|
|
// attribute stream then we want to perform the file record and
|
|
// directory update now. Otherwise we will defer the updates until
|
|
// the user closes his handle.
|
|
//
|
|
|
|
if ((Irp->IoStatus.Information == FILE_CREATED) ||
|
|
(Irp->IoStatus.Information == FILE_SUPERSEDED) ||
|
|
(Irp->IoStatus.Information == FILE_OVERWRITTEN)) {
|
|
|
|
NtfsUpdateScbFromFileObject( IrpContext, IrpSp->FileObject, *ThisScb, TRUE );
|
|
|
|
//
|
|
// Do the standard information, file sizes and then duplicate information
|
|
// if needed.
|
|
//
|
|
|
|
if (FlagOn( ThisFcb->FcbState, FCB_STATE_UPDATE_STD_INFO )) {
|
|
|
|
NtfsUpdateStandardInformation( IrpContext, ThisFcb );
|
|
}
|
|
|
|
if (FlagOn( (*ThisScb)->ScbState, SCB_STATE_CHECK_ATTRIBUTE_SIZE )) {
|
|
|
|
NtfsWriteFileSizes( IrpContext,
|
|
*ThisScb,
|
|
&(*ThisScb)->Header.ValidDataLength.QuadPart,
|
|
FALSE,
|
|
TRUE );
|
|
}
|
|
|
|
if (FlagOn( ThisFcb->InfoFlags, FCB_INFO_DUPLICATE_FLAGS )) {
|
|
|
|
ULONG FilterMatch;
|
|
|
|
NtfsUpdateDuplicateInfo( IrpContext, ThisFcb, Lcb, ParentScb );
|
|
|
|
if (ThisFcb->Vcb->NotifyCount != 0) {
|
|
|
|
//
|
|
// We map the Fcb info flags into the dir notify flags.
|
|
//
|
|
|
|
FilterMatch = NtfsBuildDirNotifyFilter( IrpContext,
|
|
ThisFcb->InfoFlags | Lcb->InfoFlags );
|
|
|
|
//
|
|
// If the filter match is non-zero, that means we also need to do a
|
|
// dir notify call.
|
|
//
|
|
|
|
if ((FilterMatch != 0) && (*ThisCcb != NULL)) {
|
|
|
|
NtfsReportDirNotify( IrpContext,
|
|
ThisFcb->Vcb,
|
|
&(*ThisCcb)->FullFileName,
|
|
(*ThisCcb)->LastFileNameOffset,
|
|
NULL,
|
|
((FlagOn( (*ThisCcb)->Flags, CCB_FLAG_PARENT_HAS_DOS_COMPONENT ) &&
|
|
(*ThisCcb)->Lcb != NULL &&
|
|
(*ThisCcb)->Lcb->Scb->ScbType.Index.NormalizedName.Buffer != NULL) ?
|
|
&(*ThisCcb)->Lcb->Scb->ScbType.Index.NormalizedName :
|
|
NULL),
|
|
FilterMatch,
|
|
FILE_ACTION_MODIFIED,
|
|
ParentFcb );
|
|
}
|
|
}
|
|
|
|
NtfsUpdateLcbDuplicateInfo( ThisFcb, Lcb );
|
|
ThisFcb->InfoFlags = 0;
|
|
}
|
|
|
|
ClearFlag( ThisFcb->FcbState, FCB_STATE_UPDATE_STD_INFO );
|
|
|
|
NtfsAcquireFsrtlHeader( *ThisScb );
|
|
ClearFlag( (*ThisScb)->ScbState, SCB_STATE_CHECK_ATTRIBUTE_SIZE );
|
|
NtfsReleaseFsrtlHeader( *ThisScb );
|
|
}
|
|
}
|
|
|
|
try_exit: NOTHING;
|
|
} finally {
|
|
|
|
DebugUnwind( NtfsOpenExistingPrefixFcb );
|
|
|
|
if (DecrementCloseCount) {
|
|
|
|
InterlockedDecrement( &ThisFcb->CloseCount );
|
|
}
|
|
|
|
//
|
|
// If this operation was not totally successful we need to
|
|
// back out the following changes.
|
|
//
|
|
// Modifications to the Info fields in the Fcb.
|
|
// Any changes to the allocation of the Scb.
|
|
// Any changes in the open counts in the various structures.
|
|
// Changes to the share access values in the Fcb.
|
|
//
|
|
|
|
if (!NT_SUCCESS( Status ) || AbnormalTermination()) {
|
|
|
|
NtfsBackoutFailedOpens( IrpContext,
|
|
IrpSp->FileObject,
|
|
ThisFcb,
|
|
*ThisScb,
|
|
*ThisCcb );
|
|
}
|
|
|
|
DebugTrace( -1, Dbg, ("NtfsOpenExistingPrefixFcb: Exit -> %08lx\n", Status) );
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
//
|
|
// Local support routine
|
|
//
|
|
|
|
NTSTATUS
|
|
NtfsOpenTargetDirectory (
|
|
IN PIRP_CONTEXT IrpContext,
|
|
IN PIRP Irp,
|
|
IN PIO_STACK_LOCATION IrpSp,
|
|
IN PFCB ThisFcb,
|
|
IN PLCB ParentLcb OPTIONAL,
|
|
IN OUT PUNICODE_STRING FullPathName,
|
|
IN ULONG FinalNameLength,
|
|
IN BOOLEAN TargetExisted,
|
|
IN BOOLEAN DosOnlyComponent,
|
|
OUT PSCB *ThisScb,
|
|
OUT PCCB *ThisCcb
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will perform the work of opening a target directory. When the
|
|
open is complete the Ccb and Lcb for this file object will be identical
|
|
to any other open. We store the full name for the rename in the
|
|
file object but set the 'Length' field to include only the
|
|
name upto the parent directory. We use the 'MaximumLength' field to
|
|
indicate the full name.
|
|
|
|
Arguments:
|
|
|
|
Irp - This is the Irp for this create operation.
|
|
|
|
IrpSp - This is the Irp stack pointer for the filesystem.
|
|
|
|
ThisFcb - This is the Fcb for the directory to open.
|
|
|
|
ParentLcb - This is the Lcb used to reach the parent directory. If not
|
|
specified, we will have to find it here. There will be no Lcb to
|
|
find if this Fcb was opened by Id.
|
|
|
|
FullPathName - This is the normalized string for open operation. It now
|
|
contains the full name as it appears on the disk for this open path.
|
|
It may not reach all the way to the root if the relative file object
|
|
was opened by Id.
|
|
|
|
FinalNameLength - This is the length of the final component in the
|
|
full path name.
|
|
|
|
TargetExisted - Indicates if the file indicated by the FinalName string
|
|
currently exists on the disk.
|
|
|
|
DosOnlyComponent - Indicates if there is a DOS-ONLY component in an ancestor
|
|
of this open.
|
|
|
|
ThisScb - This is the address to store the Scb from this open.
|
|
|
|
ThisCcb - This is the address to store the Ccb from this open.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Indicating the outcome of opening this target directory.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
ULONG CcbFlags = CCB_FLAG_OPEN_AS_FILE;
|
|
|
|
PAGED_CODE();
|
|
|
|
DebugTrace( +1, Dbg, ("NtfsOpenTargetDirectory: Entered\n") );
|
|
|
|
if (DosOnlyComponent) {
|
|
|
|
SetFlag( CcbFlags, CCB_FLAG_PARENT_HAS_DOS_COMPONENT );
|
|
}
|
|
|
|
//
|
|
// If the name doesn't begin with a backslash, remember this as
|
|
// an open by file ID.
|
|
//
|
|
|
|
if (FullPathName->Buffer[0] != L'\\') {
|
|
|
|
SetFlag( CcbFlags, CCB_FLAG_OPEN_BY_FILE_ID );
|
|
}
|
|
|
|
//
|
|
// Modify the full path name so that the Maximum length field describes
|
|
// the full name and the Length field describes the name for the
|
|
// parent.
|
|
//
|
|
|
|
FullPathName->MaximumLength = FullPathName->Length;
|
|
|
|
//
|
|
// If we don't have an Lcb, we will find it now. We look at each Lcb
|
|
// for the parent Fcb and find one which matches the component
|
|
// ahead of the last component of the full name.
|
|
//
|
|
|
|
FullPathName->Length -= (USHORT)FinalNameLength;
|
|
|
|
//
|
|
// If we are not at the root then subtract the bytes for the '\\'
|
|
// separator.
|
|
//
|
|
|
|
if (FullPathName->Length > sizeof( WCHAR )) {
|
|
|
|
FullPathName->Length -= sizeof( WCHAR );
|
|
}
|
|
|
|
if (!ARGUMENT_PRESENT( ParentLcb ) && (FullPathName->Length != 0)) {
|
|
|
|
PLIST_ENTRY Links;
|
|
PLCB NextLcb;
|
|
|
|
//
|
|
// If the length is two then the parent Lcb is the root Lcb.
|
|
//
|
|
|
|
if (FullPathName->Length == sizeof( WCHAR )
|
|
&& FullPathName->Buffer[0] == L'\\') {
|
|
|
|
ParentLcb = (PLCB) ThisFcb->Vcb->RootLcb;
|
|
|
|
} else {
|
|
|
|
for (Links = ThisFcb->LcbQueue.Flink;
|
|
Links != &ThisFcb->LcbQueue;
|
|
Links = Links->Flink) {
|
|
|
|
SHORT NameOffset;
|
|
|
|
NextLcb = CONTAINING_RECORD( Links,
|
|
LCB,
|
|
FcbLinks );
|
|
|
|
NameOffset = (SHORT) FullPathName->Length - (SHORT) NextLcb->ExactCaseLink.LinkName.Length;
|
|
|
|
if (NameOffset >= 0) {
|
|
|
|
if (RtlEqualMemory( Add2Ptr( FullPathName->Buffer,
|
|
NameOffset ),
|
|
NextLcb->ExactCaseLink.LinkName.Buffer,
|
|
NextLcb->ExactCaseLink.LinkName.Length )) {
|
|
|
|
//
|
|
// We found a matching Lcb. Remember this and exit
|
|
// the loop.
|
|
//
|
|
|
|
ParentLcb = NextLcb;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Check this open for security access.
|
|
//
|
|
|
|
NtfsOpenCheck( IrpContext, ThisFcb, NULL, Irp );
|
|
|
|
//
|
|
// Now actually open the attribute.
|
|
//
|
|
|
|
Status = NtfsOpenAttribute( IrpContext,
|
|
IrpSp,
|
|
ThisFcb->Vcb,
|
|
ParentLcb,
|
|
ThisFcb,
|
|
(ARGUMENT_PRESENT( ParentLcb )
|
|
? FullPathName->Length - ParentLcb->ExactCaseLink.LinkName.Length
|
|
: 0),
|
|
NtfsFileNameIndex,
|
|
$INDEX_ALLOCATION,
|
|
(ThisFcb->CleanupCount == 0 ? SetShareAccess : CheckShareAccess),
|
|
UserDirectoryOpen,
|
|
CcbFlags,
|
|
NULL,
|
|
ThisScb,
|
|
ThisCcb );
|
|
|
|
if (NT_SUCCESS( Status )) {
|
|
|
|
//
|
|
// If the Scb does not have a normalized name then update it now.
|
|
//
|
|
|
|
if (((*ThisScb)->ScbType.Index.NormalizedName.Buffer == NULL) ||
|
|
((*ThisScb)->ScbType.Index.NormalizedName.Length == 0)) {
|
|
|
|
NtfsBuildNormalizedName( IrpContext,
|
|
*ThisScb,
|
|
&(*ThisScb)->ScbType.Index.NormalizedName );
|
|
}
|
|
|
|
//
|
|
// If the file object name is not from the root then use the normalized name
|
|
// to obtain the full name.
|
|
//
|
|
|
|
if (FlagOn( CcbFlags, CCB_FLAG_OPEN_BY_FILE_ID )) {
|
|
|
|
USHORT BytesNeeded;
|
|
USHORT Index;
|
|
ULONG ComponentCount;
|
|
ULONG NormalizedComponentCount;
|
|
PWCHAR NewBuffer;
|
|
PWCHAR NextChar;
|
|
|
|
//
|
|
// Count the number of components in the directory portion of the
|
|
// name in the file object.
|
|
//
|
|
|
|
ComponentCount = 0;
|
|
|
|
if (FullPathName->Length != 0) {
|
|
|
|
ComponentCount = 1;
|
|
Index = (FullPathName->Length / sizeof( WCHAR )) - 1;
|
|
|
|
do {
|
|
|
|
if (FullPathName->Buffer[Index] == L'\\') {
|
|
|
|
ComponentCount += 1;
|
|
}
|
|
|
|
Index -= 1;
|
|
|
|
} while (Index != 0);
|
|
}
|
|
|
|
//
|
|
// Count back this number of components in the normalized name.
|
|
//
|
|
|
|
NormalizedComponentCount = 0;
|
|
Index = (*ThisScb)->ScbType.Index.NormalizedName.Length / sizeof( WCHAR );
|
|
|
|
//
|
|
// Special case the root to point directory to the leading backslash.
|
|
//
|
|
|
|
if (Index == 1) {
|
|
|
|
Index = 0;
|
|
}
|
|
|
|
while (NormalizedComponentCount < ComponentCount) {
|
|
|
|
Index -= 1;
|
|
while ((*ThisScb)->ScbType.Index.NormalizedName.Buffer[Index] != L'\\') {
|
|
|
|
Index -= 1;
|
|
}
|
|
|
|
NormalizedComponentCount += 1;
|
|
}
|
|
|
|
//
|
|
// Compute the size of the buffer needed for the full name. This
|
|
// will be:
|
|
//
|
|
// - Portion of normalized name used plus a separator
|
|
// - MaximumLength currently in FullPathName
|
|
//
|
|
|
|
BytesNeeded = ((Index + 1) * sizeof( WCHAR )) + FullPathName->MaximumLength;
|
|
|
|
NextChar =
|
|
NewBuffer = NtfsAllocatePool( PagedPool, BytesNeeded );
|
|
|
|
//
|
|
// Copy over the portion of the name from the normalized name.
|
|
//
|
|
|
|
if (Index != 0) {
|
|
|
|
RtlCopyMemory( NextChar,
|
|
(*ThisScb)->ScbType.Index.NormalizedName.Buffer,
|
|
Index * sizeof( WCHAR ));
|
|
|
|
NextChar += Index;
|
|
}
|
|
|
|
*NextChar = L'\\';
|
|
NextChar += 1;
|
|
|
|
//
|
|
// Now copy over the remaining part of the name from the file object.
|
|
//
|
|
|
|
RtlCopyMemory( NextChar,
|
|
FullPathName->Buffer,
|
|
FullPathName->MaximumLength );
|
|
|
|
//
|
|
// Now free the pool from the file object and update with the newly
|
|
// allocated pool. Don't forget to update the Ccb to point to this new
|
|
// buffer.
|
|
//
|
|
|
|
NtfsFreePool( FullPathName->Buffer );
|
|
|
|
FullPathName->Buffer = NewBuffer;
|
|
FullPathName->MaximumLength =
|
|
FullPathName->Length = BytesNeeded;
|
|
FullPathName->Length -= (USHORT) FinalNameLength;
|
|
|
|
if (FullPathName->Length > sizeof( WCHAR )) {
|
|
|
|
FullPathName->Length -= sizeof( WCHAR );
|
|
}
|
|
|
|
(*ThisCcb)->FullFileName = *FullPathName;
|
|
(*ThisCcb)->LastFileNameOffset = FullPathName->MaximumLength - (USHORT) FinalNameLength;
|
|
}
|
|
|
|
Irp->IoStatus.Information = (TargetExisted ? FILE_EXISTS : FILE_DOES_NOT_EXIST);
|
|
}
|
|
|
|
DebugTrace( +1, Dbg, ("NtfsOpenTargetDirectory: Exit -> %08lx\n", Status) );
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
//
|
|
// Local support routine
|
|
//
|
|
|
|
NTSTATUS
|
|
NtfsOpenFile (
|
|
IN PIRP_CONTEXT IrpContext,
|
|
IN PIRP Irp,
|
|
IN PIO_STACK_LOCATION IrpSp,
|
|
IN PSCB ParentScb,
|
|
IN PINDEX_ENTRY IndexEntry,
|
|
IN UNICODE_STRING FullPathName,
|
|
IN UNICODE_STRING FinalName,
|
|
IN UNICODE_STRING AttrName,
|
|
IN UNICODE_STRING AttrCodeName,
|
|
IN BOOLEAN IgnoreCase,
|
|
IN BOOLEAN OpenById,
|
|
IN PQUICK_INDEX QuickIndex,
|
|
IN BOOLEAN DosOnlyComponent,
|
|
IN BOOLEAN TrailingBackslash,
|
|
IN PVOID NetworkInfo OPTIONAL,
|
|
OUT PFCB *CurrentFcb,
|
|
OUT PLCB *LcbForTeardown,
|
|
OUT PSCB *ThisScb,
|
|
OUT PCCB *ThisCcb
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called when we need to open an attribute on a file
|
|
which currently exists. We have the ParentScb and the file reference
|
|
for the existing file. We will create the Fcb for this file and the
|
|
link between it and its parent directory. We will add this link to the
|
|
prefix table as well as the link for its parent Scb if specified.
|
|
|
|
On entry the caller owns the parent Scb.
|
|
|
|
Arguments:
|
|
|
|
Irp - This is the Irp for this open operation.
|
|
|
|
IrpSp - This is the Irp stack pointer for the filesystem.
|
|
|
|
ParentScb - This is the Scb for the parent directory.
|
|
|
|
IndexEntry - This is the index entry from the disk for this file.
|
|
|
|
FullPathName - This is the string containing the full path name of
|
|
this Fcb. Meaningless for an open by Id call.
|
|
|
|
FinalName - This is the string for the final component only. If the length
|
|
is zero then this is an open by Id call.
|
|
|
|
AttrName - This is the name of the attribute to open.
|
|
|
|
AttriCodeName - This is the name of the attribute code to open.
|
|
|
|
IgnoreCase - Indicates the type of open.
|
|
|
|
OpenById - Indicates if we are opening this file relative to a file opened by Id.
|
|
|
|
DosOnlyComponent - Indicates if there is a DOS-ONLY component in an ancestor
|
|
of this open.
|
|
|
|
TrailingBackslash - Indicates if caller had a terminating backslash on the
|
|
name.
|
|
|
|
NetworkInfo - If specified then this call is a fast open call to query
|
|
the network information. We don't update any of the in-memory structures
|
|
for this.
|
|
|
|
CurrentFcb - This is the address to store the Fcb if we successfully find
|
|
one in the Fcb/Scb tree.
|
|
|
|
LcbForTeardown - This is the Lcb to use in teardown if we add an Lcb
|
|
into the tree.
|
|
|
|
ThisScb - This is the address to store the Scb from this open.
|
|
|
|
ThisCcb - This is the address to store the Ccb from this open.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Indicates the result of this create file operation.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
ATTRIBUTE_TYPE_CODE AttrTypeCode;
|
|
ULONG CcbFlags = 0;
|
|
BOOLEAN IndexedAttribute;
|
|
PFILE_NAME IndexFileName;
|
|
BOOLEAN UpdateFcbInfo = FALSE;
|
|
|
|
OLD_SCB_SNAPSHOT ScbSizes;
|
|
BOOLEAN HaveScbSizes = FALSE;
|
|
|
|
PVCB Vcb = ParentScb->Vcb;
|
|
|
|
PFCB LocalFcbForTeardown = NULL;
|
|
PFCB ThisFcb;
|
|
PLCB ThisLcb;
|
|
BOOLEAN DecrementCloseCount = FALSE;
|
|
BOOLEAN ExistingFcb;
|
|
BOOLEAN AcquiredFcbTable = FALSE;
|
|
|
|
FILE_REFERENCE PreviousFileReference;
|
|
BOOLEAN DroppedParent = FALSE;
|
|
|
|
PAGED_CODE();
|
|
|
|
DebugTrace( +1, Dbg, ("NtfsOpenFile: Entered\n") );
|
|
|
|
IndexFileName = (PFILE_NAME) NtfsFoundIndexEntry( IndexEntry );
|
|
|
|
if (DosOnlyComponent) {
|
|
|
|
SetFlag( CcbFlags, CCB_FLAG_PARENT_HAS_DOS_COMPONENT );
|
|
}
|
|
|
|
//
|
|
// The first thing to do is to figure out what type
|
|
// of attribute the caller is trying to open. This involves the
|
|
// directory/non-directory bits, the attribute name and code strings,
|
|
// the type of file, whether he passed in an ea buffer and whether
|
|
// there was a trailing backslash.
|
|
//
|
|
|
|
if (NtfsEqualMftRef( &IndexEntry->FileReference,
|
|
&VolumeFileReference )) {
|
|
|
|
if (AttrName.Length != 0
|
|
|| AttrCodeName.Length != 0) {
|
|
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
DebugTrace( -1, Dbg, ("NtfsOpenExistingPrefixFcb: Exit -> %08lx\n", Status) );
|
|
|
|
return Status;
|
|
}
|
|
|
|
SetFlag( IrpContext->Flags,
|
|
IRP_CONTEXT_FLAG_ACQUIRE_VCB_EX | IRP_CONTEXT_FLAG_DASD_OPEN );
|
|
|
|
NtfsRaiseStatus( IrpContext, STATUS_CANT_WAIT, NULL, NULL );
|
|
}
|
|
|
|
Status = NtfsCheckValidAttributeAccess( IrpSp,
|
|
Vcb,
|
|
&IndexFileName->Info,
|
|
AttrName,
|
|
AttrCodeName,
|
|
TrailingBackslash,
|
|
&AttrTypeCode,
|
|
&CcbFlags,
|
|
&IndexedAttribute );
|
|
|
|
if (!NT_SUCCESS( Status )) {
|
|
|
|
DebugTrace( -1, Dbg, ("NtfsOpenFile: Exit -> %08lx\n", Status) );
|
|
|
|
return Status;
|
|
}
|
|
|
|
NtfsAcquireFcbTable( IrpContext, Vcb );
|
|
AcquiredFcbTable = TRUE;
|
|
|
|
//
|
|
// Use a try-finally to facilitate cleanup.
|
|
//
|
|
|
|
try {
|
|
|
|
//
|
|
// We know that it is safe to continue the open. We start by creating
|
|
// an Fcb and Lcb for this file. It is possible that the Fcb and Lcb
|
|
// both exist. If the Lcb exists, then the Fcb must definitely exist.
|
|
// We create the Fcb first, if we need to update the Fcb info structure
|
|
// we copy the one from the index entry. We look at the Fcb to discover
|
|
// if it has any links, if it does then we make this the last Fcb we
|
|
// reached. If it doesn't then we have to clean it up from here.
|
|
//
|
|
|
|
ThisFcb = NtfsCreateFcb( IrpContext,
|
|
ParentScb->Vcb,
|
|
IndexEntry->FileReference,
|
|
BooleanFlagOn( IrpSp->Flags, SL_OPEN_PAGING_FILE ),
|
|
BooleanFlagOn( IndexFileName->Info.FileAttributes,
|
|
DUP_FILE_NAME_INDEX_PRESENT ),
|
|
&ExistingFcb );
|
|
|
|
ThisFcb->ReferenceCount += 1;
|
|
|
|
//
|
|
// If we created this Fcb we must make sure to start teardown
|
|
// on it.
|
|
//
|
|
|
|
if (!ExistingFcb) {
|
|
|
|
LocalFcbForTeardown = ThisFcb;
|
|
|
|
} else {
|
|
|
|
*LcbForTeardown = NULL;
|
|
*CurrentFcb = ThisFcb;
|
|
}
|
|
|
|
//
|
|
// Try to do a fast acquire, otherwise we need to release
|
|
// the Fcb table, acquire the Fcb, acquire the Fcb table to
|
|
// dereference Fcb.
|
|
//
|
|
|
|
if (!NtfsAcquireFcbWithPaging( IrpContext, ThisFcb, TRUE )) {
|
|
|
|
//
|
|
// Remember the current file reference in the index entry.
|
|
// We want to be able to detect whether an entry is removed.
|
|
//
|
|
|
|
PreviousFileReference = IndexEntry->FileReference;
|
|
DroppedParent = TRUE;
|
|
|
|
ParentScb->Fcb->ReferenceCount += 1;
|
|
InterlockedIncrement( &ParentScb->CleanupCount );
|
|
|
|
//
|
|
// Set the IrpContext to acquire paging io resources if our target
|
|
// has one. This will lock the MappedPageWriter out of this file.
|
|
//
|
|
|
|
if (ThisFcb->PagingIoResource != NULL) {
|
|
|
|
SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_ACQUIRE_PAGING );
|
|
}
|
|
|
|
NtfsReleaseScbWithPaging( IrpContext, ParentScb );
|
|
NtfsReleaseFcbTable( IrpContext, Vcb );
|
|
NtfsAcquireFcbWithPaging( IrpContext, ThisFcb, FALSE );
|
|
NtfsAcquireExclusiveScb( IrpContext, ParentScb );
|
|
NtfsAcquireFcbTable( IrpContext, Vcb );
|
|
InterlockedDecrement( &ParentScb->CleanupCount );
|
|
ParentScb->Fcb->ReferenceCount -= 1;
|
|
}
|
|
|
|
ThisFcb->ReferenceCount -= 1;
|
|
|
|
NtfsReleaseFcbTable( IrpContext, Vcb );
|
|
AcquiredFcbTable = FALSE;
|
|
|
|
//
|
|
// Check if something happened to this file in the window where
|
|
// we dropped the parent.
|
|
//
|
|
|
|
if (DroppedParent) {
|
|
|
|
//
|
|
// Check if the file has been deleted.
|
|
//
|
|
|
|
if (ExistingFcb && (ThisFcb->LinkCount == 0)) {
|
|
|
|
try_return( Status = STATUS_DELETE_PENDING );
|
|
|
|
//
|
|
// Check if the link may have been deleted.
|
|
//
|
|
|
|
} else if (!NtfsEqualMftRef( &IndexEntry->FileReference,
|
|
&PreviousFileReference )) {
|
|
|
|
NtfsRaiseStatus( IrpContext, STATUS_CANT_WAIT, NULL, NULL );
|
|
}
|
|
}
|
|
|
|
//
|
|
// If the Fcb existed and this is a paging file then either return
|
|
// sharing violation or force the Fcb and Scb's to go away.
|
|
// Do this for the case where the user is opening a paging file
|
|
// but the Fcb is non-paged or the user is opening a non-paging
|
|
// file and the Fcb is for a paging file.
|
|
//
|
|
|
|
if (ExistingFcb &&
|
|
|
|
((FlagOn( IrpSp->Flags, SL_OPEN_PAGING_FILE ) &&
|
|
!FlagOn( ThisFcb->FcbState, FCB_STATE_PAGING_FILE )) ||
|
|
|
|
(FlagOn( ThisFcb->FcbState, FCB_STATE_PAGING_FILE ) &&
|
|
!FlagOn( IrpSp->Flags, SL_OPEN_PAGING_FILE )))) {
|
|
|
|
if (ThisFcb->CleanupCount != 0) {
|
|
|
|
try_return( Status = STATUS_SHARING_VIOLATION );
|
|
|
|
//
|
|
// If we have a persistent paging file then give up and
|
|
// return SHARING_VIOLATION.
|
|
//
|
|
|
|
} else if (FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_IN_FSP )) {
|
|
|
|
try_return( Status = STATUS_SHARING_VIOLATION );
|
|
|
|
//
|
|
// If there was an existing Fcb for a paging file we need to force
|
|
// all of the Scb's to be torn down. The easiest way to do this
|
|
// is to flush and purge all of the Scb's (saving any attribute list
|
|
// for last) and then raise LOG_FILE_FULL to allow this request to
|
|
// be posted.
|
|
//
|
|
|
|
} else {
|
|
|
|
//
|
|
// Reference the Fcb so it won't go away on any flushes.
|
|
//
|
|
|
|
InterlockedIncrement( &ThisFcb->CloseCount );
|
|
DecrementCloseCount = TRUE;
|
|
|
|
//
|
|
// Flush and purge this Fcb.
|
|
//
|
|
|
|
NtfsFlushAndPurgeFcb( IrpContext, ThisFcb );
|
|
|
|
InterlockedDecrement( &ThisFcb->CloseCount );
|
|
DecrementCloseCount = FALSE;
|
|
|
|
//
|
|
// Force this request to be posted and then raise
|
|
// CANT_WAIT. The Fcb should be torn down in the finally
|
|
// clause below.
|
|
//
|
|
|
|
SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_FORCE_POST );
|
|
NtfsRaiseStatus( IrpContext, STATUS_CANT_WAIT, NULL, NULL );
|
|
}
|
|
}
|
|
|
|
//
|
|
// We perform a check to see whether we will allow the system
|
|
// files to be opened.
|
|
//
|
|
|
|
if (NtfsProtectSystemFiles) {
|
|
|
|
//
|
|
// We only allow user opens on the Volume Dasd file and the
|
|
// root directory.
|
|
//
|
|
|
|
if (NtfsSegmentNumber( &ThisFcb->FileReference ) < FIRST_USER_FILE_NUMBER
|
|
&& NtfsSegmentNumber( &ThisFcb->FileReference ) != VOLUME_DASD_NUMBER
|
|
&& NtfsSegmentNumber( &ThisFcb->FileReference ) != ROOT_FILE_NAME_INDEX_NUMBER) {
|
|
|
|
Status = STATUS_ACCESS_DENIED;
|
|
DebugTrace( 0, Dbg, ("Attempting to open system files\n") );
|
|
|
|
try_return( NOTHING );
|
|
}
|
|
}
|
|
|
|
//
|
|
// If the Fcb Info field needs to be initialized, we do so now.
|
|
// We read this information from the disk as the duplicate information
|
|
// in the index entry is not guaranteed to be correct.
|
|
//
|
|
|
|
if (!FlagOn( ThisFcb->FcbState, FCB_STATE_DUP_INITIALIZED )) {
|
|
|
|
NtfsUpdateFcbInfoFromDisk( IrpContext,
|
|
TRUE,
|
|
ThisFcb,
|
|
ParentScb->Fcb,
|
|
&ScbSizes );
|
|
|
|
HaveScbSizes = TRUE;
|
|
|
|
NtfsConditionallyFixupQuota( IrpContext, ThisFcb );
|
|
}
|
|
|
|
//
|
|
// We have the actual data from the disk stored in the duplicate
|
|
// information in the Fcb. We compare this with the duplicate
|
|
// information in the DUPLICATE_INFORMATION structure in the
|
|
// filename attribute. If they don't match, we remember that
|
|
// we need to update the duplicate information.
|
|
//
|
|
|
|
if (!RtlEqualMemory( &ThisFcb->Info,
|
|
&IndexFileName->Info,
|
|
sizeof( DUPLICATED_INFORMATION ))) {
|
|
|
|
UpdateFcbInfo = TRUE;
|
|
|
|
//
|
|
// We expect this to be very rare but let's find the ones being changed.
|
|
//
|
|
|
|
if (ThisFcb->Info.CreationTime != IndexFileName->Info.CreationTime) {
|
|
|
|
SetFlag( ThisFcb->InfoFlags, FCB_INFO_CHANGED_CREATE );
|
|
}
|
|
|
|
if (ThisFcb->Info.LastModificationTime != IndexFileName->Info.LastModificationTime) {
|
|
|
|
SetFlag( ThisFcb->InfoFlags, FCB_INFO_CHANGED_LAST_MOD );
|
|
}
|
|
|
|
if (ThisFcb->Info.LastChangeTime != IndexFileName->Info.LastChangeTime) {
|
|
|
|
SetFlag( ThisFcb->InfoFlags, FCB_INFO_CHANGED_LAST_CHANGE );
|
|
}
|
|
|
|
if (ThisFcb->Info.LastAccessTime != IndexFileName->Info.LastAccessTime) {
|
|
|
|
SetFlag( ThisFcb->InfoFlags, FCB_INFO_CHANGED_LAST_ACCESS );
|
|
}
|
|
|
|
if (ThisFcb->Info.AllocatedLength != IndexFileName->Info.AllocatedLength) {
|
|
|
|
SetFlag( ThisFcb->InfoFlags, FCB_INFO_CHANGED_ALLOC_SIZE );
|
|
}
|
|
|
|
if (ThisFcb->Info.FileSize != IndexFileName->Info.FileSize) {
|
|
|
|
SetFlag( ThisFcb->InfoFlags, FCB_INFO_CHANGED_FILE_SIZE );
|
|
}
|
|
|
|
if (ThisFcb->Info.FileAttributes != IndexFileName->Info.FileAttributes) {
|
|
|
|
SetFlag( ThisFcb->InfoFlags, FCB_INFO_CHANGED_FILE_ATTR );
|
|
}
|
|
|
|
if (ThisFcb->Info.PackedEaSize != IndexFileName->Info.PackedEaSize) {
|
|
|
|
SetFlag( ThisFcb->InfoFlags, FCB_INFO_CHANGED_EA_SIZE );
|
|
}
|
|
}
|
|
|
|
//
|
|
// Now get the link for this traversal.
|
|
//
|
|
|
|
ThisLcb = NtfsCreateLcb( IrpContext,
|
|
ParentScb,
|
|
ThisFcb,
|
|
FinalName,
|
|
IndexFileName->Flags,
|
|
NULL );
|
|
|
|
//
|
|
// We now know the Fcb is linked into the tree.
|
|
//
|
|
|
|
LocalFcbForTeardown = NULL;
|
|
|
|
*LcbForTeardown = ThisLcb;
|
|
*CurrentFcb = ThisFcb;
|
|
|
|
//
|
|
// If the link has been deleted, we cut off the open.
|
|
//
|
|
|
|
if (LcbLinkIsDeleted( ThisLcb )) {
|
|
|
|
try_return( Status = STATUS_DELETE_PENDING );
|
|
}
|
|
|
|
//
|
|
// We now call the worker routine to open an attribute on an existing file.
|
|
//
|
|
|
|
Status = NtfsOpenAttributeInExistingFile( IrpContext,
|
|
Irp,
|
|
IrpSp,
|
|
ThisLcb,
|
|
ThisFcb,
|
|
(OpenById
|
|
? 0
|
|
: FullPathName.Length - FinalName.Length),
|
|
AttrName,
|
|
AttrTypeCode,
|
|
CcbFlags,
|
|
OpenById,
|
|
NetworkInfo,
|
|
ThisScb,
|
|
ThisCcb );
|
|
|
|
//
|
|
// Check to see if we should insert any prefix table entries
|
|
// and update the last access time.
|
|
//
|
|
|
|
if (NT_SUCCESS( Status ) && (Status != STATUS_PENDING)) {
|
|
|
|
PSCB Scb = *ThisScb;
|
|
|
|
//
|
|
// Now we insert the Lcb for this Fcb.
|
|
//
|
|
|
|
NtfsInsertPrefix( ThisLcb,
|
|
IgnoreCase );
|
|
|
|
//
|
|
// If this is a directory open and the normalized name is not in
|
|
// the Scb then do so now.
|
|
//
|
|
|
|
if ((SafeNodeType( *ThisScb ) == NTFS_NTC_SCB_INDEX) &&
|
|
((*ThisScb)->ScbType.Index.NormalizedName.Buffer == NULL) &&
|
|
(ParentScb->ScbType.Index.NormalizedName.Buffer != NULL)) {
|
|
|
|
NtfsUpdateNormalizedName( IrpContext,
|
|
ParentScb,
|
|
*ThisScb,
|
|
IndexFileName,
|
|
FALSE );
|
|
}
|
|
|
|
//
|
|
// Perform the last bit of work. If this a user file open, we need
|
|
// to check if we initialize the Scb.
|
|
//
|
|
|
|
if (!IndexedAttribute) {
|
|
|
|
if (!FlagOn( Scb->ScbState, SCB_STATE_HEADER_INITIALIZED )) {
|
|
|
|
//
|
|
// We may have the sizes from our Fcb update call.
|
|
//
|
|
|
|
if (HaveScbSizes &&
|
|
(AttrTypeCode == $DATA) &&
|
|
(AttrName.Length == 0) &&
|
|
!FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_CREATE_MOD_SCB )) {
|
|
|
|
NtfsUpdateScbFromMemory( IrpContext, Scb, &ScbSizes );
|
|
|
|
} else {
|
|
|
|
NtfsUpdateScbFromAttribute( IrpContext, Scb, NULL );
|
|
}
|
|
}
|
|
|
|
if (IrpSp->FileObject->WriteAccess) {
|
|
NtfsExpandQuotaToAllocationSize( IrpContext, Scb );
|
|
}
|
|
|
|
//
|
|
// Let's check if we need to set the cache bit.
|
|
//
|
|
|
|
if (!FlagOn( IrpSp->Parameters.Create.Options,
|
|
FILE_NO_INTERMEDIATE_BUFFERING )) {
|
|
|
|
SetFlag( IrpSp->FileObject->Flags, FO_CACHE_SUPPORTED );
|
|
}
|
|
}
|
|
|
|
//
|
|
// If this is the paging file, we want to be sure the allocation
|
|
// is loaded.
|
|
//
|
|
|
|
if (FlagOn( ThisFcb->FcbState, FCB_STATE_PAGING_FILE )
|
|
&& (Scb->Header.AllocationSize.QuadPart != 0)
|
|
&& !FlagOn( Scb->ScbState, SCB_STATE_ATTRIBUTE_RESIDENT )) {
|
|
|
|
LCN Lcn;
|
|
VCN Vcn;
|
|
VCN AllocatedVcns;
|
|
|
|
AllocatedVcns = Int64ShraMod32(Scb->Header.AllocationSize.QuadPart, Scb->Vcb->ClusterShift);
|
|
|
|
NtfsPreloadAllocation( IrpContext, Scb, 0, AllocatedVcns );
|
|
|
|
//
|
|
// Now make sure the allocation is correctly loaded. The last
|
|
// Vcn should correspond to the allocation size for the file.
|
|
//
|
|
|
|
if (!NtfsLookupLastNtfsMcbEntry( &Scb->Mcb,
|
|
&Vcn,
|
|
&Lcn ) ||
|
|
(Vcn + 1) != AllocatedVcns) {
|
|
|
|
NtfsRaiseStatus( IrpContext,
|
|
STATUS_FILE_CORRUPT_ERROR,
|
|
NULL,
|
|
ThisFcb );
|
|
}
|
|
}
|
|
|
|
//
|
|
// If this open is for an executable image we update the last
|
|
// access time.
|
|
//
|
|
|
|
if (FlagOn( IrpSp->Parameters.Create.SecurityContext->AccessState->PreviouslyGrantedAccess, FILE_EXECUTE ) &&
|
|
(Scb->AttributeTypeCode == $DATA)) {
|
|
|
|
SetFlag( IrpSp->FileObject->Flags, FO_FILE_FAST_IO_READ );
|
|
}
|
|
|
|
//
|
|
// Let's update the quick index information in the Lcb.
|
|
//
|
|
|
|
RtlCopyMemory( &ThisLcb->QuickIndex,
|
|
QuickIndex,
|
|
sizeof( QUICK_INDEX ));
|
|
|
|
//
|
|
// If this operation was a supersede/overwrite or we created a new
|
|
// attribute stream then we want to perform the file record and
|
|
// directory update now. Otherwise we will defer the updates until
|
|
// the user closes his handle.
|
|
//
|
|
|
|
if (UpdateFcbInfo ||
|
|
(Irp->IoStatus.Information == FILE_CREATED) ||
|
|
(Irp->IoStatus.Information == FILE_SUPERSEDED) ||
|
|
(Irp->IoStatus.Information == FILE_OVERWRITTEN)) {
|
|
|
|
NtfsUpdateScbFromFileObject( IrpContext, IrpSp->FileObject, *ThisScb, TRUE );
|
|
|
|
//
|
|
// Do the standard information, file sizes and then duplicate information
|
|
// if needed.
|
|
//
|
|
|
|
if (FlagOn( ThisFcb->FcbState, FCB_STATE_UPDATE_STD_INFO )) {
|
|
|
|
NtfsUpdateStandardInformation( IrpContext, ThisFcb );
|
|
}
|
|
|
|
if (FlagOn( (*ThisScb)->ScbState, SCB_STATE_CHECK_ATTRIBUTE_SIZE )) {
|
|
|
|
NtfsWriteFileSizes( IrpContext,
|
|
*ThisScb,
|
|
&(*ThisScb)->Header.ValidDataLength.QuadPart,
|
|
FALSE,
|
|
TRUE );
|
|
}
|
|
|
|
if (FlagOn( ThisFcb->InfoFlags, FCB_INFO_DUPLICATE_FLAGS )) {
|
|
|
|
ULONG FilterMatch;
|
|
|
|
NtfsUpdateDuplicateInfo( IrpContext, ThisFcb, *LcbForTeardown, ParentScb );
|
|
|
|
if (Vcb->NotifyCount != 0) {
|
|
|
|
//
|
|
// We map the Fcb info flags into the dir notify flags.
|
|
//
|
|
|
|
FilterMatch = NtfsBuildDirNotifyFilter( IrpContext,
|
|
ThisFcb->InfoFlags | ThisLcb->InfoFlags );
|
|
|
|
//
|
|
// If the filter match is non-zero, that means we also need to do a
|
|
// dir notify call.
|
|
//
|
|
|
|
if ((FilterMatch != 0) && (*ThisCcb != NULL)) {
|
|
|
|
NtfsReportDirNotify( IrpContext,
|
|
ThisFcb->Vcb,
|
|
&(*ThisCcb)->FullFileName,
|
|
(*ThisCcb)->LastFileNameOffset,
|
|
NULL,
|
|
((FlagOn( (*ThisCcb)->Flags, CCB_FLAG_PARENT_HAS_DOS_COMPONENT ) &&
|
|
(*ThisCcb)->Lcb != NULL &&
|
|
(*ThisCcb)->Lcb->Scb->ScbType.Index.NormalizedName.Buffer != NULL) ?
|
|
&(*ThisCcb)->Lcb->Scb->ScbType.Index.NormalizedName :
|
|
NULL),
|
|
FilterMatch,
|
|
FILE_ACTION_MODIFIED,
|
|
ParentScb->Fcb );
|
|
}
|
|
}
|
|
|
|
NtfsUpdateLcbDuplicateInfo( ThisFcb, *LcbForTeardown );
|
|
ThisFcb->InfoFlags = 0;
|
|
}
|
|
|
|
ClearFlag( ThisFcb->FcbState, FCB_STATE_UPDATE_STD_INFO );
|
|
|
|
NtfsAcquireFsrtlHeader( *ThisScb );
|
|
ClearFlag( (*ThisScb)->ScbState, SCB_STATE_CHECK_ATTRIBUTE_SIZE );
|
|
NtfsReleaseFsrtlHeader( *ThisScb );
|
|
}
|
|
}
|
|
|
|
try_exit: NOTHING;
|
|
} finally {
|
|
|
|
DebugUnwind( NtfsOpenFile );
|
|
|
|
if (AcquiredFcbTable) {
|
|
|
|
NtfsReleaseFcbTable( IrpContext, Vcb );
|
|
}
|
|
|
|
//
|
|
// If this operation was not totally successful we need to
|
|
// back out the following changes.
|
|
//
|
|
// Modifications to the Info fields in the Fcb.
|
|
// Any changes to the allocation of the Scb.
|
|
// Any changes in the open counts in the various structures.
|
|
// Changes to the share access values in the Fcb.
|
|
//
|
|
|
|
if (!NT_SUCCESS( Status ) || AbnormalTermination()) {
|
|
|
|
NtfsBackoutFailedOpens( IrpContext,
|
|
IrpSp->FileObject,
|
|
ThisFcb,
|
|
*ThisScb,
|
|
*ThisCcb );
|
|
}
|
|
|
|
if (DecrementCloseCount) {
|
|
|
|
InterlockedDecrement( &ThisFcb->CloseCount );
|
|
}
|
|
|
|
//
|
|
// If we are to cleanup the Fcb we, look to see if we created it.
|
|
// If we did we can call our teardown routine. Otherwise we
|
|
// leave it alone.
|
|
//
|
|
|
|
if ((LocalFcbForTeardown != NULL) &&
|
|
(Status != STATUS_PENDING)) {
|
|
|
|
NtfsTeardownStructures( IrpContext,
|
|
ThisFcb,
|
|
NULL,
|
|
(BOOLEAN) (IrpContext->TransactionId != 0),
|
|
FALSE,
|
|
NULL );
|
|
}
|
|
|
|
DebugTrace( -1, Dbg, ("NtfsOpenFile: Exit -> %08lx\n", Status) );
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
//
|
|
// Local support routine
|
|
//
|
|
|
|
NTSTATUS
|
|
NtfsCreateNewFile (
|
|
IN PIRP_CONTEXT IrpContext,
|
|
IN PIRP Irp,
|
|
IN PIO_STACK_LOCATION IrpSp,
|
|
IN PSCB ParentScb,
|
|
IN PFILE_NAME FileNameAttr,
|
|
IN UNICODE_STRING FullPathName,
|
|
IN UNICODE_STRING FinalName,
|
|
IN UNICODE_STRING AttrName,
|
|
IN UNICODE_STRING AttrCodeName,
|
|
IN BOOLEAN IgnoreCase,
|
|
IN BOOLEAN OpenById,
|
|
IN BOOLEAN DosOnlyComponent,
|
|
IN BOOLEAN TrailingBackslash,
|
|
OUT PFCB *CurrentFcb,
|
|
OUT PLCB *LcbForTeardown,
|
|
OUT PSCB *ThisScb,
|
|
OUT PCCB *ThisCcb
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called when we need to open an attribute on a file
|
|
which does not exist yet. We have the ParentScb and the name to use
|
|
for this create. We will attempt to create the file and necessary
|
|
attributes. This will cause us to create an Fcb and the link between
|
|
it and its parent Scb. We will add this link to the prefix table as
|
|
well as the link for its parent Scb if specified.
|
|
|
|
Arguments:
|
|
|
|
Irp - This is the Irp for this open operation.
|
|
|
|
IrpSp - This is the Irp stack pointer for the filesystem.
|
|
|
|
ParentScb - This is the Scb for the parent directory.
|
|
|
|
FileNameAttr - This is the file name attribute we used to perform the
|
|
search. The file name is correct but the other fields need to
|
|
be initialized.
|
|
|
|
FullPathName - This is the string containing the full path name of
|
|
this Fcb.
|
|
|
|
FinalName - This is the string for the final component only.
|
|
|
|
AttrName - This is the name of the attribute to open.
|
|
|
|
AttriCodeName - This is the name of the attribute code to open.
|
|
|
|
IgnoreCase - Indicates how we looked up the name.
|
|
|
|
OpenById - Indicates if we are opening this file relative to a file opened by Id.
|
|
|
|
DosOnlyComponent - Indicates if there is a DOS-ONLY component in an ancestor
|
|
of this open.
|
|
|
|
TrailingBackslash - Indicates if caller had a terminating backslash on the
|
|
name.
|
|
|
|
CurrentFcb - This is the address to store the Fcb if we successfully find
|
|
one in the Fcb/Scb tree.
|
|
|
|
LcbForTeardown - This is the Lcb to use in teardown if we add an Lcb
|
|
into the tree.
|
|
|
|
ThisScb - This is the address to store the Scb from this open.
|
|
|
|
ThisCcb - This is the address to store the Ccb from this open.
|
|
|
|
Tunnel - This is the property tunnel to search for restoration
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Indicates the result of this create file operation.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
PVCB Vcb;
|
|
|
|
ULONG CcbFlags = 0;
|
|
BOOLEAN IndexedAttribute;
|
|
ATTRIBUTE_TYPE_CODE AttrTypeCode;
|
|
|
|
BOOLEAN CleanupAttrContext = FALSE;
|
|
ATTRIBUTE_ENUMERATION_CONTEXT AttrContext;
|
|
|
|
PBCB FileRecordBcb = NULL;
|
|
LONGLONG FileRecordOffset;
|
|
FILE_REFERENCE ThisFileReference;
|
|
PFILE_RECORD_SEGMENT_HEADER FileRecord;
|
|
|
|
PSCB Scb;
|
|
PLCB ThisLcb = NULL;
|
|
PFCB ThisFcb = NULL;
|
|
BOOLEAN AcquiredFcbTable = FALSE;
|
|
BOOLEAN RemovedFcb = FALSE;
|
|
BOOLEAN DecrementCloseCount = FALSE;
|
|
BOOLEAN QuotaIndexAcquired = FALSE;
|
|
BOOLEAN SecurityStreamAcquired = FALSE;
|
|
|
|
PACCESS_STATE AccessState;
|
|
BOOLEAN ReturnedExistingFcb;
|
|
|
|
BOOLEAN LoggedFileRecord = FALSE;
|
|
|
|
BOOLEAN HaveTunneledInformation = FALSE;
|
|
NAME_PAIR NamePair;
|
|
LONGLONG TunneledCreationTime;
|
|
ULONG TunneledDataSize;
|
|
|
|
VCN Cluster;
|
|
LCN Lcn;
|
|
VCN Vcn;
|
|
|
|
UCHAR FileNameFlags;
|
|
|
|
PAGED_CODE();
|
|
|
|
DebugTrace( +1, Dbg, ("NtfsCreateNewFile: Entered\n") );
|
|
|
|
NtfsInitializeNamePair(&NamePair);
|
|
|
|
if (DosOnlyComponent) {
|
|
|
|
SetFlag( CcbFlags, CCB_FLAG_PARENT_HAS_DOS_COMPONENT );
|
|
}
|
|
|
|
//
|
|
// We will do all the checks to see if this open can fail.
|
|
// This includes checking the specified attribute names, checking
|
|
// the security access and checking the create disposition.
|
|
//
|
|
|
|
{
|
|
ULONG CreateDisposition;
|
|
|
|
CreateDisposition = (IrpSp->Parameters.Create.Options >> 24) & 0x000000ff;
|
|
|
|
if ((CreateDisposition == FILE_OPEN) ||
|
|
(CreateDisposition == FILE_OVERWRITE)) {
|
|
|
|
Status = STATUS_OBJECT_NAME_NOT_FOUND;
|
|
|
|
DebugTrace( -1, Dbg, ("NtfsCreateNewFile: Exit -> %08lx\n", Status) );
|
|
return Status;
|
|
|
|
} else if (FlagOn( IrpSp->Parameters.Create.Options,
|
|
FILE_DIRECTORY_FILE ) &&
|
|
(CreateDisposition == FILE_OVERWRITE_IF)) {
|
|
|
|
Status = STATUS_OBJECT_NAME_INVALID;
|
|
|
|
DebugTrace( -1, Dbg, ("NtfsCreateNewFile: Exit -> %08lx\n", Status) );
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
Vcb = ParentScb->Vcb;
|
|
|
|
Status = NtfsCheckValidAttributeAccess( IrpSp,
|
|
Vcb,
|
|
NULL,
|
|
AttrName,
|
|
AttrCodeName,
|
|
TrailingBackslash,
|
|
&AttrTypeCode,
|
|
&CcbFlags,
|
|
&IndexedAttribute );
|
|
|
|
if (!NT_SUCCESS( Status )) {
|
|
|
|
DebugTrace( -1, Dbg, ("NtfsCreateNewFile: Exit -> %08lx\n", Status) );
|
|
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// Fail this request if this is an indexed attribute and the TEMPORARY
|
|
// bit is set.
|
|
//
|
|
|
|
if (IndexedAttribute &&
|
|
FlagOn( IrpSp->Parameters.Create.FileAttributes, FILE_ATTRIBUTE_TEMPORARY )) {
|
|
|
|
DebugTrace( -1, Dbg, ("NtfsCreateNewFile: Exit -> %08lx\n", STATUS_INVALID_PARAMETER) );
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
//
|
|
// We won't allow someone to create a read-only file with DELETE_ON_CLOSE.
|
|
//
|
|
|
|
if (FlagOn( IrpSp->Parameters.Create.FileAttributes, FILE_ATTRIBUTE_READONLY ) &&
|
|
FlagOn( IrpSp->Parameters.Create.Options, FILE_DELETE_ON_CLOSE )) {
|
|
|
|
DebugTrace( -1, Dbg, ("NtfsCreateNewFile: Exit -> %08lx\n", STATUS_CANNOT_DELETE) );
|
|
return STATUS_CANNOT_DELETE;
|
|
}
|
|
|
|
//
|
|
// Use a try-finally to facilitate cleanup.
|
|
//
|
|
|
|
try {
|
|
|
|
//
|
|
// Now perform the security checks. The first is to check if we
|
|
// may create a file in the parent. The second checks if the user
|
|
// desires ACCESS_SYSTEM_SECURITY and has the required privilege.
|
|
//
|
|
|
|
AccessState = IrpSp->Parameters.Create.SecurityContext->AccessState;
|
|
if (!(AccessState->Flags & TOKEN_HAS_RESTORE_PRIVILEGE)) {
|
|
|
|
NtfsCreateCheck( IrpContext, ParentScb->Fcb, Irp );
|
|
}
|
|
|
|
//
|
|
// Check if the remaining privilege includes ACCESS_SYSTEM_SECURITY.
|
|
//
|
|
|
|
if (FlagOn( AccessState->RemainingDesiredAccess, ACCESS_SYSTEM_SECURITY )) {
|
|
|
|
if (!SeSinglePrivilegeCheck( NtfsSecurityPrivilege,
|
|
UserMode )) {
|
|
|
|
NtfsRaiseStatus( IrpContext, STATUS_PRIVILEGE_NOT_HELD, NULL, NULL );
|
|
}
|
|
|
|
//
|
|
// Move this privilege from the Remaining access to Granted access.
|
|
//
|
|
|
|
ClearFlag( AccessState->RemainingDesiredAccess, ACCESS_SYSTEM_SECURITY );
|
|
SetFlag( AccessState->PreviouslyGrantedAccess, ACCESS_SYSTEM_SECURITY );
|
|
}
|
|
|
|
//
|
|
// We want to allow this user maximum access to this file. We will
|
|
// use his desired access and check if he specified MAXIMUM_ALLOWED.
|
|
//
|
|
|
|
SetFlag( AccessState->PreviouslyGrantedAccess,
|
|
AccessState->RemainingDesiredAccess );
|
|
|
|
if (FlagOn( AccessState->PreviouslyGrantedAccess, MAXIMUM_ALLOWED )) {
|
|
|
|
SetFlag( AccessState->PreviouslyGrantedAccess, FILE_ALL_ACCESS );
|
|
ClearFlag( AccessState->PreviouslyGrantedAccess, MAXIMUM_ALLOWED );
|
|
}
|
|
|
|
AccessState->RemainingDesiredAccess = 0;
|
|
|
|
#ifdef _CAIRO_
|
|
|
|
//
|
|
// The security stream and quota index must be acquired before
|
|
// the mft scb is acquired.
|
|
//
|
|
|
|
NtfsAcquireSecurityStream( IrpContext, Vcb, &SecurityStreamAcquired );
|
|
|
|
if (FlagOn(Vcb->QuotaFlags, QUOTA_FLAG_TRACKING_ENABLED)) {
|
|
|
|
ASSERT(!NtfsIsExclusiveScb( Vcb->MftScb ) || NtfsIsExclusiveScb( Vcb->QuotaTableScb ));
|
|
|
|
NtfsAcquireExclusiveScb( IrpContext, Vcb->QuotaTableScb );
|
|
QuotaIndexAcquired = TRUE;
|
|
}
|
|
|
|
#endif // _CAIRO_
|
|
|
|
//
|
|
// We will now try to do all of the on-disk operations. This means first
|
|
// allocating and initializing an Mft record. After that we create
|
|
// an Fcb to use to access this record.
|
|
//
|
|
|
|
ThisFileReference = NtfsAllocateMftRecord( IrpContext,
|
|
Vcb,
|
|
FALSE );
|
|
|
|
//
|
|
// Pin the file record we need.
|
|
//
|
|
|
|
NtfsPinMftRecord( IrpContext,
|
|
Vcb,
|
|
&ThisFileReference,
|
|
TRUE,
|
|
&FileRecordBcb,
|
|
&FileRecord,
|
|
&FileRecordOffset );
|
|
|
|
//
|
|
// Initialize the file record header.
|
|
//
|
|
|
|
NtfsInitializeMftRecord( IrpContext,
|
|
Vcb,
|
|
&ThisFileReference,
|
|
FileRecord,
|
|
FileRecordBcb,
|
|
IndexedAttribute );
|
|
|
|
NtfsAcquireFcbTable( IrpContext, Vcb );
|
|
AcquiredFcbTable = TRUE;
|
|
|
|
ThisFcb = NtfsCreateFcb( IrpContext,
|
|
Vcb,
|
|
ThisFileReference,
|
|
BooleanFlagOn( IrpSp->Flags, SL_OPEN_PAGING_FILE ),
|
|
IndexedAttribute,
|
|
&ReturnedExistingFcb );
|
|
|
|
NtfsAcquireFcbWithPaging( IrpContext, ThisFcb, FALSE );
|
|
|
|
NtfsReleaseFcbTable( IrpContext, Vcb );
|
|
AcquiredFcbTable = FALSE;
|
|
|
|
//
|
|
// Reference the Fcb so it won't go away.
|
|
//
|
|
|
|
InterlockedIncrement( &ThisFcb->CloseCount );
|
|
DecrementCloseCount = TRUE;
|
|
|
|
//
|
|
// The first thing to create is the Ea's for the file. This will
|
|
// update the Ea length field in the Fcb.
|
|
// We test here that the opener is opening the entire file and
|
|
// is not Ea blind.
|
|
//
|
|
|
|
if (Irp->AssociatedIrp.SystemBuffer != NULL) {
|
|
|
|
if (FlagOn( IrpSp->Parameters.Create.Options, FILE_NO_EA_KNOWLEDGE )
|
|
|| !FlagOn( CcbFlags, CCB_FLAG_OPEN_AS_FILE )) {
|
|
|
|
try_return( Status = STATUS_ACCESS_DENIED );
|
|
}
|
|
}
|
|
|
|
//
|
|
// We're about to start creating structures on the disk, for which we'll
|
|
// possibly need to have tunneling infomation. Get it, non-POSIX only.
|
|
//
|
|
|
|
if (!IndexedAttribute && IgnoreCase) {
|
|
|
|
TunneledDataSize = sizeof(LONGLONG);
|
|
|
|
if (FsRtlFindInTunnelCache( &Vcb->Tunnel,
|
|
*(PULONGLONG)&ParentScb->Fcb->FileReference,
|
|
&FinalName,
|
|
&NamePair.Short,
|
|
&NamePair.Long,
|
|
&TunneledDataSize,
|
|
&TunneledCreationTime)) {
|
|
|
|
ASSERT(TunneledDataSize == sizeof(LONGLONG));
|
|
|
|
HaveTunneledInformation = TRUE;
|
|
}
|
|
}
|
|
|
|
#ifdef _CAIRO_
|
|
|
|
SetFlag( ThisFcb->FcbState, FCB_STATE_LARGE_STD_INFO );
|
|
|
|
//
|
|
// BUGBUG - remove this test when all volumes are CAIRO
|
|
//
|
|
|
|
if (Vcb->SecurityDescriptorStream != NULL)
|
|
{
|
|
|
|
//
|
|
// We assign the security for this object in order to generate a SecurityId
|
|
// that will be stored in the standard info.
|
|
//
|
|
|
|
NtfsAssignSecurity( IrpContext,
|
|
ParentScb->Fcb,
|
|
Irp,
|
|
ThisFcb,
|
|
NULL, // BUGBUG delete
|
|
NULL, // BUGBUG delete
|
|
0i64, // BUGBUG delete
|
|
&LoggedFileRecord );// BUGBUG delete
|
|
}
|
|
|
|
//
|
|
// If quota tracking is enabled then the quota index will have
|
|
// been acquired and a owner id should be assigned to the the
|
|
// new file.
|
|
//
|
|
|
|
if (QuotaIndexAcquired) {
|
|
|
|
PSID Sid;
|
|
BOOLEAN OwnerDefaulted;
|
|
|
|
ASSERT(ThisFcb->SharedSecurity != NULL);
|
|
|
|
//
|
|
// Extract the security id from the security descriptor.
|
|
//
|
|
|
|
Status = RtlGetOwnerSecurityDescriptor(
|
|
ThisFcb->SharedSecurity->SecurityDescriptor,
|
|
&Sid,
|
|
&OwnerDefaulted );
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
leave;
|
|
}
|
|
|
|
//
|
|
// Generate a owner id for the Fcb.
|
|
//
|
|
|
|
ThisFcb->OwnerId = NtfsGetOwnerId( IrpContext,
|
|
Sid,
|
|
NULL );
|
|
|
|
NtfsInitializeQuotaControlBlock( ThisFcb );
|
|
}
|
|
#endif // _CAIRO_
|
|
|
|
//
|
|
// The changes to make on disk are first to create a standard information
|
|
// attribute. We start by filling the Fcb with the information we
|
|
// know and creating the attribute on disk.
|
|
//
|
|
|
|
NtfsInitializeFcbAndStdInfo( IrpContext,
|
|
ThisFcb,
|
|
IndexedAttribute,
|
|
(BOOLEAN) (!FlagOn( IrpSp->Parameters.Create.Options, FILE_NO_COMPRESSION ) &&
|
|
!FlagOn( IrpSp->Flags, SL_OPEN_PAGING_FILE ) &&
|
|
FlagOn( ParentScb->ScbState, SCB_STATE_COMPRESSED )),
|
|
IrpSp->Parameters.Create.FileAttributes,
|
|
(HaveTunneledInformation? &TunneledCreationTime : NULL) );
|
|
|
|
//
|
|
// Next we create the Index for a directory or the unnamed data for
|
|
// a file if they are not explicitly being opened.
|
|
//
|
|
|
|
if (!IndexedAttribute) {
|
|
|
|
if (!FlagOn( CcbFlags, CCB_FLAG_OPEN_AS_FILE )) {
|
|
|
|
NtfsInitializeAttributeContext( &AttrContext );
|
|
CleanupAttrContext = TRUE;
|
|
|
|
NtfsCreateAttributeWithValue( IrpContext,
|
|
ThisFcb,
|
|
$DATA,
|
|
NULL,
|
|
NULL,
|
|
0,
|
|
(USHORT) ((!FlagOn( ThisFcb->FcbState, FCB_STATE_PAGING_FILE ) &&
|
|
!FlagOn( IrpSp->Parameters.Create.Options, FILE_NO_COMPRESSION )) ?
|
|
(ParentScb->AttributeFlags & ATTRIBUTE_FLAG_COMPRESSION_MASK) :
|
|
0),
|
|
NULL,
|
|
FALSE,
|
|
&AttrContext );
|
|
|
|
NtfsCleanupAttributeContext( &AttrContext );
|
|
CleanupAttrContext = FALSE;
|
|
|
|
ThisFcb->Info.AllocatedLength = 0;
|
|
ThisFcb->Info.FileSize = 0;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
NtfsCreateIndex( IrpContext,
|
|
ThisFcb,
|
|
$FILE_NAME,
|
|
COLLATION_FILE_NAME,
|
|
Vcb->DefaultBytesPerIndexAllocationBuffer,
|
|
(UCHAR)Vcb->DefaultBlocksPerIndexAllocationBuffer,
|
|
NULL,
|
|
(USHORT) (!FlagOn( IrpSp->Parameters.Create.Options,
|
|
FILE_NO_COMPRESSION ) ?
|
|
(ParentScb->AttributeFlags & ATTRIBUTE_FLAG_COMPRESSION_MASK) :
|
|
0),
|
|
TRUE,
|
|
FALSE );
|
|
}
|
|
|
|
//
|
|
// Now we create the Lcb, this means that this Fcb is in the graph.
|
|
//
|
|
|
|
ThisLcb = NtfsCreateLcb( IrpContext,
|
|
ParentScb,
|
|
ThisFcb,
|
|
FinalName,
|
|
0,
|
|
NULL );
|
|
|
|
//
|
|
// Finally we create and open the desired attribute for the user.
|
|
//
|
|
|
|
if (AttrTypeCode == $INDEX_ALLOCATION) {
|
|
|
|
Status = NtfsOpenAttribute( IrpContext,
|
|
IrpSp,
|
|
Vcb,
|
|
ThisLcb,
|
|
ThisFcb,
|
|
(OpenById
|
|
? 0
|
|
: FullPathName.Length - FinalName.Length),
|
|
NtfsFileNameIndex,
|
|
$INDEX_ALLOCATION,
|
|
SetShareAccess,
|
|
UserDirectoryOpen,
|
|
(OpenById
|
|
? CcbFlags | CCB_FLAG_OPEN_BY_FILE_ID
|
|
: CcbFlags),
|
|
NULL,
|
|
ThisScb,
|
|
ThisCcb );
|
|
|
|
} else {
|
|
|
|
Status = NtfsOpenNewAttr( IrpContext,
|
|
Irp,
|
|
IrpSp,
|
|
ThisLcb,
|
|
ThisFcb,
|
|
(OpenById
|
|
? 0
|
|
: FullPathName.Length - FinalName.Length),
|
|
AttrName,
|
|
AttrTypeCode,
|
|
CcbFlags,
|
|
FALSE,
|
|
OpenById,
|
|
ThisScb,
|
|
ThisCcb );
|
|
}
|
|
|
|
//
|
|
// If we are successful, we add the parent Lcb to the prefix table if
|
|
// desired. We will always add our link to the prefix queue.
|
|
//
|
|
|
|
if (NT_SUCCESS( Status )) {
|
|
|
|
Scb = *ThisScb;
|
|
|
|
//
|
|
// Initialize the Scb if we need to do so.
|
|
//
|
|
|
|
if (!IndexedAttribute) {
|
|
|
|
if (!FlagOn( Scb->ScbState, SCB_STATE_HEADER_INITIALIZED )) {
|
|
|
|
NtfsUpdateScbFromAttribute( IrpContext, Scb, NULL );
|
|
}
|
|
|
|
if (!FlagOn( IrpSp->Parameters.Create.Options,
|
|
FILE_NO_INTERMEDIATE_BUFFERING )) {
|
|
|
|
SetFlag( IrpSp->FileObject->Flags, FO_CACHE_SUPPORTED );
|
|
}
|
|
|
|
//
|
|
// If this is the unnamed data attribute, we store the sizes
|
|
// in the Fcb.
|
|
//
|
|
|
|
if (FlagOn( Scb->ScbState, SCB_STATE_UNNAMED_DATA )) {
|
|
|
|
ThisFcb->Info.AllocatedLength = Scb->TotalAllocated;
|
|
ThisFcb->Info.FileSize = Scb->Header.FileSize.QuadPart;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Next add this entry to parent. It is possible that this is a link,
|
|
// an Ntfs name, a DOS name or Ntfs/Dos name. We use the filename
|
|
// attribute structure from earlier, but need to add more information.
|
|
//
|
|
|
|
NtfsAddLink( IrpContext,
|
|
(BOOLEAN) !BooleanFlagOn( IrpSp->Flags, SL_CASE_SENSITIVE ),
|
|
ParentScb,
|
|
ThisFcb,
|
|
FileNameAttr,
|
|
&LoggedFileRecord,
|
|
&FileNameFlags,
|
|
&ThisLcb->QuickIndex,
|
|
(HaveTunneledInformation? &NamePair : NULL) );
|
|
|
|
//
|
|
// We created the Lcb without knowing the correct value for the
|
|
// flags. We update it now.
|
|
//
|
|
|
|
ThisLcb->FileNameAttr->Flags = FileNameFlags;
|
|
FileNameAttr->Flags = FileNameFlags;
|
|
|
|
//
|
|
// We also have to fix up the ExactCaseLink of the Lcb since we may have had
|
|
// a short name create turned into a tunneled long name create, meaning that
|
|
// it should be full uppercase. And the filename in the IRP.
|
|
//
|
|
|
|
if (FileNameFlags == FILE_NAME_DOS) {
|
|
|
|
RtlUpcaseUnicodeString(&ThisLcb->ExactCaseLink.LinkName, &ThisLcb->ExactCaseLink.LinkName, FALSE);
|
|
RtlUpcaseUnicodeString(&IrpSp->FileObject->FileName, &IrpSp->FileObject->FileName, FALSE);
|
|
}
|
|
|
|
//
|
|
// If this is a directory open and the normalized name is not in
|
|
// the Scb then do so now.
|
|
//
|
|
|
|
if ((SafeNodeType( *ThisScb ) == NTFS_NTC_SCB_INDEX) &&
|
|
((*ThisScb)->ScbType.Index.NormalizedName.Buffer == NULL) &&
|
|
(ParentScb->ScbType.Index.NormalizedName.Buffer != NULL)) {
|
|
|
|
NtfsUpdateNormalizedName( IrpContext,
|
|
ParentScb,
|
|
*ThisScb,
|
|
FileNameAttr,
|
|
FALSE );
|
|
}
|
|
|
|
//
|
|
// Clear the flags in the Fcb that indicate we need to update on
|
|
// disk structures. Also clear any file object and Ccb flags
|
|
// which also indicate we may need to do an update.
|
|
//
|
|
|
|
ThisFcb->InfoFlags = 0;
|
|
ClearFlag( ThisFcb->FcbState, FCB_STATE_UPDATE_STD_INFO );
|
|
|
|
ClearFlag( IrpSp->FileObject->Flags,
|
|
FO_FILE_MODIFIED | FO_FILE_FAST_IO_READ | FO_FILE_SIZE_CHANGED );
|
|
|
|
ClearFlag( (*ThisCcb)->Flags,
|
|
(CCB_FLAG_UPDATE_LAST_MODIFY |
|
|
CCB_FLAG_UPDATE_LAST_CHANGE |
|
|
CCB_FLAG_SET_ARCHIVE) );
|
|
|
|
//
|
|
// BUGBUG begin section to delete when all volumes are CAIRO
|
|
//
|
|
|
|
#ifdef _CAIRO_
|
|
if (Vcb->SecurityDescriptorStream == NULL)
|
|
{
|
|
#endif
|
|
//
|
|
// Next we will assign security to this new file.
|
|
//
|
|
|
|
NtfsAssignSecurity( IrpContext,
|
|
ParentScb->Fcb,
|
|
Irp,
|
|
ThisFcb,
|
|
FileRecord,
|
|
FileRecordBcb,
|
|
FileRecordOffset,
|
|
&LoggedFileRecord );
|
|
#ifdef _CAIRO_
|
|
}
|
|
#endif // _CAIRO_
|
|
|
|
//
|
|
// BUGBUG end section to delete when all volumes are CAIRO
|
|
//
|
|
|
|
//
|
|
// Log the file record.
|
|
//
|
|
|
|
FileRecord->Lsn = NtfsWriteLog( IrpContext,
|
|
Vcb->MftScb,
|
|
FileRecordBcb,
|
|
InitializeFileRecordSegment,
|
|
FileRecord,
|
|
FileRecord->FirstFreeByte,
|
|
Noop,
|
|
NULL,
|
|
0,
|
|
FileRecordOffset,
|
|
0,
|
|
0,
|
|
Vcb->BytesPerFileRecordSegment );
|
|
|
|
#if 0 // _CAIRO_
|
|
ASSERT(!NtfsPerformQuotaOperation(ThisFcb) || NtfsCalculateQuotaAdjustment( IrpContext, ThisFcb) == 0);
|
|
#endif
|
|
|
|
//
|
|
// Now add the eas for the file. We need to add them now because
|
|
// they are logged and we have to make sure we don't modify the
|
|
// attribute record after adding them.
|
|
//
|
|
|
|
if (Irp->AssociatedIrp.SystemBuffer != NULL) {
|
|
|
|
NtfsAddEa( IrpContext,
|
|
Vcb,
|
|
ThisFcb,
|
|
(PFILE_FULL_EA_INFORMATION) Irp->AssociatedIrp.SystemBuffer,
|
|
IrpSp->Parameters.Create.EaLength,
|
|
&Irp->IoStatus );
|
|
}
|
|
|
|
//
|
|
// Change the last modification time and last change time for the
|
|
// parent.
|
|
//
|
|
|
|
NtfsUpdateFcb( ParentScb->Fcb );
|
|
|
|
//
|
|
// If this is the paging file, we want to be sure the allocation
|
|
// is loaded.
|
|
//
|
|
|
|
if (FlagOn( ThisFcb->FcbState, FCB_STATE_PAGING_FILE )) {
|
|
|
|
Cluster = Int64ShraMod32(Scb->Header.AllocationSize.QuadPart, Scb->Vcb->ClusterShift);
|
|
|
|
NtfsPreloadAllocation( IrpContext, Scb, 0, Cluster );
|
|
|
|
//
|
|
// Now make sure the allocation is correctly loaded. The last
|
|
// Vcn should correspond to the allocation size for the file.
|
|
//
|
|
|
|
if (!NtfsLookupLastNtfsMcbEntry( &Scb->Mcb,
|
|
&Vcn,
|
|
&Lcn ) ||
|
|
(Vcn + 1) != Cluster) {
|
|
|
|
NtfsRaiseStatus( IrpContext,
|
|
STATUS_FILE_CORRUPT_ERROR,
|
|
NULL,
|
|
ThisFcb );
|
|
}
|
|
}
|
|
|
|
//
|
|
// We report to our parent that we created a new file.
|
|
//
|
|
|
|
if (!OpenById && (Vcb->NotifyCount != 0)) {
|
|
|
|
NtfsReportDirNotify( IrpContext,
|
|
ThisFcb->Vcb,
|
|
&(*ThisCcb)->FullFileName,
|
|
(*ThisCcb)->LastFileNameOffset,
|
|
NULL,
|
|
((FlagOn( (*ThisCcb)->Flags, CCB_FLAG_PARENT_HAS_DOS_COMPONENT ) &&
|
|
(*ThisCcb)->Lcb != NULL &&
|
|
(*ThisCcb)->Lcb->Scb->ScbType.Index.NormalizedName.Buffer != NULL) ?
|
|
&(*ThisCcb)->Lcb->Scb->ScbType.Index.NormalizedName :
|
|
NULL),
|
|
(IndexedAttribute
|
|
? FILE_NOTIFY_CHANGE_DIR_NAME
|
|
: FILE_NOTIFY_CHANGE_FILE_NAME),
|
|
FILE_ACTION_ADDED,
|
|
ParentScb->Fcb );
|
|
}
|
|
|
|
ThisFcb->InfoFlags = 0;
|
|
|
|
//
|
|
// Now we insert the Lcb for this Fcb.
|
|
//
|
|
|
|
NtfsInsertPrefix( ThisLcb,
|
|
IgnoreCase );
|
|
|
|
Irp->IoStatus.Information = FILE_CREATED;
|
|
}
|
|
|
|
try_exit: NOTHING;
|
|
} finally {
|
|
|
|
DebugUnwind( NtfsCreateNewFile );
|
|
|
|
if (AcquiredFcbTable) {
|
|
|
|
NtfsReleaseFcbTable( IrpContext, Vcb );
|
|
}
|
|
|
|
NtfsUnpinBcb( &FileRecordBcb );
|
|
|
|
NtfsReleaseQuotaIndex( IrpContext, Vcb, QuotaIndexAcquired );
|
|
NtfsReleaseSecurityStream( IrpContext, Vcb, SecurityStreamAcquired );
|
|
|
|
if (CleanupAttrContext) {
|
|
|
|
NtfsCleanupAttributeContext( &AttrContext );
|
|
}
|
|
|
|
if (DecrementCloseCount) {
|
|
|
|
InterlockedDecrement( &ThisFcb->CloseCount );
|
|
}
|
|
|
|
if (NamePair.Long.Buffer != NamePair.LongBuffer) {
|
|
|
|
NtfsFreePool(NamePair.Long.Buffer);
|
|
}
|
|
|
|
//
|
|
// We need to cleanup any changes to the in memory
|
|
// structures if there is an error.
|
|
//
|
|
|
|
if (!NT_SUCCESS( Status ) || AbnormalTermination()) {
|
|
|
|
NtfsBackoutFailedOpens( IrpContext,
|
|
IrpSp->FileObject,
|
|
ThisFcb,
|
|
*ThisScb,
|
|
*ThisCcb );
|
|
|
|
//
|
|
// Always force the Fcb to reinitialized.
|
|
//
|
|
|
|
if (ThisFcb != NULL) {
|
|
|
|
PSCB Scb;
|
|
PLIST_ENTRY Links;
|
|
|
|
ClearFlag( ThisFcb->FcbState, FCB_STATE_DUP_INITIALIZED );
|
|
|
|
//
|
|
// Mark the Fcb and all Scb's as deleted to force all subsequent
|
|
// operations to fail.
|
|
//
|
|
|
|
SetFlag( ThisFcb->FcbState, FCB_STATE_FILE_DELETED );
|
|
|
|
//
|
|
// We need to mark all of the Scbs as gone.
|
|
//
|
|
|
|
for (Links = ThisFcb->ScbQueue.Flink;
|
|
Links != &ThisFcb->ScbQueue;
|
|
Links = Links->Flink) {
|
|
|
|
Scb = CONTAINING_RECORD( Links, SCB, FcbLinks );
|
|
|
|
Scb->ValidDataToDisk =
|
|
Scb->Header.AllocationSize.QuadPart =
|
|
Scb->Header.FileSize.QuadPart =
|
|
Scb->Header.ValidDataLength.QuadPart = 0;
|
|
|
|
SetFlag( Scb->ScbState, SCB_STATE_ATTRIBUTE_DELETED );
|
|
}
|
|
|
|
//
|
|
// Clear the Scb field so our caller doesn't try to teardown
|
|
// from this point.
|
|
//
|
|
|
|
*ThisScb = NULL;
|
|
|
|
//
|
|
// If we created an Fcb then we want to check if we need to
|
|
// unwind any structure allocation. We don't want to remove any
|
|
// structures needed for the coming AbortTransaction. This
|
|
// includes the parent Scb as well as the current Fcb if we
|
|
// logged the ACL creation.
|
|
//
|
|
|
|
//
|
|
// Make sure the parent Fcb doesn't go away. Then
|
|
// start a teardown from the Fcb we just found.
|
|
//
|
|
|
|
InterlockedIncrement( &ParentScb->CleanupCount );
|
|
|
|
NtfsTeardownStructures( IrpContext,
|
|
ThisFcb,
|
|
NULL,
|
|
LoggedFileRecord,
|
|
FALSE,
|
|
&RemovedFcb );
|
|
|
|
//
|
|
// If the Fcb was removed then both the Fcb and Lcb are gone.
|
|
//
|
|
|
|
if (RemovedFcb) {
|
|
|
|
ThisFcb = NULL;
|
|
ThisLcb = NULL;
|
|
}
|
|
|
|
InterlockedDecrement( &ParentScb->CleanupCount );
|
|
}
|
|
}
|
|
|
|
//
|
|
// If the new Fcb is still present then either return it as the
|
|
// deepest Fcb encountered in this open or release it.
|
|
//
|
|
|
|
if (ThisFcb != NULL) {
|
|
|
|
//
|
|
// If the Lcb is present then this is part of the tree. Our
|
|
// caller knows to release it.
|
|
//
|
|
|
|
if (ThisLcb != NULL) {
|
|
|
|
*LcbForTeardown = ThisLcb;
|
|
*CurrentFcb = ThisFcb;
|
|
}
|
|
}
|
|
|
|
DebugTrace( -1, Dbg, ("NtfsCreateNewFile: Exit -> %08lx\n", Status) );
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
//
|
|
// Local support routine
|
|
//
|
|
|
|
PLCB
|
|
NtfsOpenSubdirectory (
|
|
IN PIRP_CONTEXT IrpContext,
|
|
IN PSCB ParentScb,
|
|
IN UNICODE_STRING Name,
|
|
IN BOOLEAN TraverseAccessCheck,
|
|
OUT PFCB *CurrentFcb,
|
|
OUT PLCB *LcbForTeardown,
|
|
IN PINDEX_ENTRY IndexEntry
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will create an Fcb for an intermediate node on an open path.
|
|
We use the ParentScb and the information in the FileName attribute returned
|
|
from the disk to create the Fcb and create a link between the Scb and Fcb.
|
|
It's possible that the Fcb and Lcb already exist but the 'CreateXcb' calls
|
|
handle that already. This routine does not expect to fail.
|
|
|
|
Arguments:
|
|
|
|
ParentScb - This is the Scb for the parent directory.
|
|
|
|
Name - This is the name for the entry.
|
|
|
|
TraverseAccessCheck - Indicates if this open is using traverse access checking.
|
|
|
|
CurrentFcb - This is the address to store the Fcb if we successfully find
|
|
one in the Fcb/Scb tree.
|
|
|
|
LcbForTeardown - This is the Lcb to use in teardown if we add an Lcb
|
|
into the tree.
|
|
|
|
IndexEntry - This is the entry found in searching the parent directory.
|
|
|
|
Return Value:
|
|
|
|
PLCB - Pointer to the Link control block between the Fcb and its parent.
|
|
|
|
--*/
|
|
|
|
{
|
|
PFCB ThisFcb;
|
|
PLCB ThisLcb;
|
|
PFCB LocalFcbForTeardown = NULL;
|
|
|
|
BOOLEAN AcquiredFcbTable = FALSE;
|
|
BOOLEAN ExistingFcb;
|
|
|
|
PVCB Vcb = ParentScb->Vcb;
|
|
|
|
PAGED_CODE();
|
|
|
|
DebugTrace( +1, Dbg, ("NtfsOpenSubdirectory: Entered\n") );
|
|
DebugTrace( 0, Dbg, ("ParentScb -> %08lx\n") );
|
|
DebugTrace( 0, Dbg, ("IndexEntry -> %08lx\n") );
|
|
|
|
//
|
|
// Use a try-finally to facilitate cleanup.
|
|
//
|
|
|
|
try {
|
|
|
|
NtfsAcquireFcbTable( IrpContext, Vcb );
|
|
AcquiredFcbTable = TRUE;
|
|
|
|
//
|
|
// The steps here are very simple create the Fcb, remembering if it
|
|
// already existed. We don't update the information in the Fcb as
|
|
// we can't rely on the information in the duplicated information.
|
|
// A subsequent open of this Fcb will need to perform that work.
|
|
//
|
|
|
|
ThisFcb = NtfsCreateFcb( IrpContext,
|
|
ParentScb->Vcb,
|
|
IndexEntry->FileReference,
|
|
FALSE,
|
|
TRUE,
|
|
&ExistingFcb );
|
|
|
|
ThisFcb->ReferenceCount += 1;
|
|
|
|
//
|
|
// If we created this Fcb we must make sure to start teardown
|
|
// on it.
|
|
//
|
|
|
|
if (!ExistingFcb) {
|
|
|
|
LocalFcbForTeardown = ThisFcb;
|
|
|
|
} else {
|
|
|
|
*CurrentFcb = ThisFcb;
|
|
*LcbForTeardown = NULL;
|
|
}
|
|
|
|
//
|
|
// Try to do a fast acquire, otherwise we need to release
|
|
// the Fcb table, acquire the Fcb, acquire the Fcb table to
|
|
// dereference Fcb.
|
|
//
|
|
|
|
if (!NtfsAcquireFcbWithPaging( IrpContext, ThisFcb, TRUE )) {
|
|
|
|
ParentScb->Fcb->ReferenceCount += 1;
|
|
InterlockedIncrement( &ParentScb->CleanupCount );
|
|
|
|
//
|
|
// Set the IrpContext to acquire paging io resources if our target
|
|
// has one. This will lock the MappedPageWriter out of this file.
|
|
//
|
|
|
|
if (ThisFcb->PagingIoResource != NULL) {
|
|
|
|
SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_ACQUIRE_PAGING );
|
|
}
|
|
|
|
NtfsReleaseScbWithPaging( IrpContext, ParentScb );
|
|
NtfsReleaseFcbTable( IrpContext, Vcb );
|
|
NtfsAcquireFcbWithPaging( IrpContext, ThisFcb, FALSE );
|
|
NtfsAcquireExclusiveScb( IrpContext, ParentScb );
|
|
NtfsAcquireFcbTable( IrpContext, Vcb );
|
|
InterlockedDecrement( &ParentScb->CleanupCount );
|
|
ParentScb->Fcb->ReferenceCount -= 1;
|
|
}
|
|
|
|
ThisFcb->ReferenceCount -= 1;
|
|
|
|
NtfsReleaseFcbTable( IrpContext, Vcb );
|
|
AcquiredFcbTable = FALSE;
|
|
|
|
//
|
|
// If this is a directory, it's possible that we hav an existing Fcb
|
|
// in the prefix table which needs to be initialized from the disk.
|
|
// We look in the InfoInitialized flag to know whether to go to
|
|
// disk.
|
|
//
|
|
|
|
ThisLcb = NtfsCreateLcb( IrpContext,
|
|
ParentScb,
|
|
ThisFcb,
|
|
Name,
|
|
((PFILE_NAME) NtfsFoundIndexEntry( IndexEntry ))->Flags,
|
|
NULL );
|
|
|
|
LocalFcbForTeardown = NULL;
|
|
|
|
*LcbForTeardown = ThisLcb;
|
|
*CurrentFcb = ThisFcb;
|
|
|
|
if (!FlagOn( ThisFcb->FcbState, FCB_STATE_DUP_INITIALIZED )) {
|
|
|
|
NtfsUpdateFcbInfoFromDisk( IrpContext,
|
|
TraverseAccessCheck,
|
|
ThisFcb,
|
|
ParentScb->Fcb,
|
|
NULL );
|
|
|
|
NtfsConditionallyFixupQuota( IrpContext, ThisFcb );
|
|
}
|
|
|
|
} finally {
|
|
|
|
DebugUnwind( NtfsOpenSubdirectory );
|
|
|
|
if (AcquiredFcbTable) {
|
|
|
|
NtfsReleaseFcbTable( IrpContext, Vcb );
|
|
}
|
|
|
|
//
|
|
// If we are to cleanup the Fcb we, look to see if we created it.
|
|
// If we did we can call our teardown routine. Otherwise we
|
|
// leave it alone.
|
|
//
|
|
|
|
if (LocalFcbForTeardown != NULL) {
|
|
|
|
NtfsTeardownStructures( IrpContext,
|
|
ThisFcb,
|
|
NULL,
|
|
FALSE,
|
|
FALSE,
|
|
NULL );
|
|
}
|
|
|
|
DebugTrace( -1, Dbg, ("NtfsOpenSubdirectory: Lcb -> %08lx\n", ThisLcb) );
|
|
}
|
|
|
|
return ThisLcb;
|
|
}
|
|
|
|
|
|
//
|
|
// Local support routine.
|
|
//
|
|
|
|
NTSTATUS
|
|
NtfsOpenAttributeInExistingFile (
|
|
IN PIRP_CONTEXT IrpContext,
|
|
IN PIRP Irp,
|
|
IN PIO_STACK_LOCATION IrpSp,
|
|
IN PLCB ThisLcb OPTIONAL,
|
|
IN PFCB ThisFcb,
|
|
IN ULONG LastFileNameOffset,
|
|
IN UNICODE_STRING AttrName,
|
|
IN ATTRIBUTE_TYPE_CODE AttrTypeCode,
|
|
IN ULONG CcbFlags,
|
|
IN BOOLEAN OpenById,
|
|
IN PVOID NetworkInfo OPTIONAL,
|
|
OUT PSCB *ThisScb,
|
|
OUT PCCB *ThisCcb
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is the worker routine for opening an attribute on an
|
|
existing file. It will handle volume opens, indexed opens, opening
|
|
or overwriting existing attributes as well as creating new attributes.
|
|
|
|
Arguments:
|
|
|
|
Irp - This is the Irp for this open operation.
|
|
|
|
IrpSp - This is the stack location for this open.
|
|
|
|
ThisLcb - This is the Lcb we used to reach this Fcb.
|
|
|
|
ThisFcb - This is the Fcb for the file being opened.
|
|
|
|
LastFileNameOffset - This is the offset in the full path name of the
|
|
final component.
|
|
|
|
AttrName - This is the attribute name in case we need to create
|
|
an Scb.
|
|
|
|
AttrTypeCode - This is the attribute type code to use to create
|
|
the Scb.
|
|
|
|
CcbFlags - This is the flag field for the Ccb.
|
|
|
|
OpenById - Indicates if this open is an open by Id.
|
|
|
|
NetworkInfo - If specified then this call is a fast open call to query
|
|
the network information. We don't update any of the in-memory structures
|
|
for this.
|
|
|
|
ThisScb - This is the address to store the Scb from this open.
|
|
|
|
ThisCcb - This is the address to store the Ccb from this open.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - The result of opening this indexed attribute.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
ULONG CreateDisposition;
|
|
|
|
PAGED_CODE();
|
|
|
|
DebugTrace( +1, Dbg, ("NtfsOpenAttributeInExistingFile: Entered\n") );
|
|
|
|
//
|
|
// If the caller is ea blind, let's check the need ea count on the
|
|
// file. We skip this check if he is accessing a named data stream.
|
|
//
|
|
|
|
if (FlagOn( IrpSp->Parameters.Create.Options, FILE_NO_EA_KNOWLEDGE )
|
|
&& FlagOn( CcbFlags, CCB_FLAG_OPEN_AS_FILE )) {
|
|
|
|
PEA_INFORMATION ThisEaInformation;
|
|
ATTRIBUTE_ENUMERATION_CONTEXT EaInfoAttrContext;
|
|
|
|
NtfsInitializeAttributeContext( &EaInfoAttrContext );
|
|
|
|
//
|
|
// Use a try-finally to facilitate cleanup.
|
|
//
|
|
|
|
try {
|
|
|
|
//
|
|
// If we find the Ea information attribute we look in there for
|
|
// Need ea count.
|
|
//
|
|
|
|
if (NtfsLookupAttributeByCode( IrpContext,
|
|
ThisFcb,
|
|
&ThisFcb->FileReference,
|
|
$EA_INFORMATION,
|
|
&EaInfoAttrContext )) {
|
|
|
|
ThisEaInformation = (PEA_INFORMATION) NtfsAttributeValue( NtfsFoundAttribute( &EaInfoAttrContext ));
|
|
|
|
if (ThisEaInformation->NeedEaCount != 0) {
|
|
|
|
Status = STATUS_ACCESS_DENIED;
|
|
}
|
|
}
|
|
|
|
} finally {
|
|
|
|
NtfsCleanupAttributeContext( &EaInfoAttrContext );
|
|
}
|
|
|
|
if (Status != STATUS_SUCCESS) {
|
|
|
|
DebugTrace( -1, Dbg, ("NtfsOpenAttributeInExistingFile: Exit\n") );
|
|
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
CreateDisposition = (IrpSp->Parameters.Create.Options >> 24) & 0x000000ff;
|
|
|
|
//
|
|
// If the result is a directory operation, then we know the attribute
|
|
// must exist.
|
|
//
|
|
|
|
if (AttrTypeCode == $INDEX_ALLOCATION) {
|
|
|
|
//
|
|
// Check the create disposition.
|
|
//
|
|
|
|
if ((CreateDisposition != FILE_OPEN) && (CreateDisposition != FILE_OPEN_IF)) {
|
|
|
|
Status = (ThisLcb == ThisFcb->Vcb->RootLcb
|
|
? STATUS_ACCESS_DENIED
|
|
: STATUS_OBJECT_NAME_COLLISION);
|
|
|
|
} else {
|
|
|
|
Status = NtfsOpenExistingAttr( IrpContext,
|
|
Irp,
|
|
IrpSp,
|
|
ThisLcb,
|
|
ThisFcb,
|
|
LastFileNameOffset,
|
|
NtfsFileNameIndex,
|
|
$INDEX_ALLOCATION,
|
|
CcbFlags,
|
|
OpenById,
|
|
TRUE,
|
|
NetworkInfo,
|
|
ThisScb,
|
|
ThisCcb );
|
|
}
|
|
|
|
} else {
|
|
|
|
BOOLEAN FoundAttribute;
|
|
|
|
//
|
|
// If it exists, we first check if the caller wanted to open that attribute.
|
|
//
|
|
|
|
if (AttrName.Length == 0
|
|
&& AttrTypeCode == $DATA) {
|
|
|
|
FoundAttribute = TRUE;
|
|
|
|
//
|
|
// Otherwise we see if the attribute exists.
|
|
//
|
|
|
|
} else {
|
|
|
|
ATTRIBUTE_ENUMERATION_CONTEXT AttrContext;
|
|
|
|
NtfsInitializeAttributeContext( &AttrContext );
|
|
|
|
//
|
|
// Use a try-finally to facilitate cleanup.
|
|
//
|
|
|
|
try {
|
|
|
|
FoundAttribute = NtfsLookupAttributeByName( IrpContext,
|
|
ThisFcb,
|
|
&ThisFcb->FileReference,
|
|
AttrTypeCode,
|
|
&AttrName,
|
|
NULL,
|
|
(BOOLEAN) !BooleanFlagOn( IrpSp->Flags, SL_CASE_SENSITIVE ),
|
|
&AttrContext );
|
|
|
|
if (FoundAttribute) {
|
|
|
|
//
|
|
// If there is an attribute name, we will copy the case of the name
|
|
// to the input attribute name.
|
|
//
|
|
|
|
PATTRIBUTE_RECORD_HEADER FoundAttribute;
|
|
|
|
FoundAttribute = NtfsFoundAttribute( &AttrContext );
|
|
|
|
RtlCopyMemory( AttrName.Buffer,
|
|
Add2Ptr( FoundAttribute, FoundAttribute->NameOffset ),
|
|
AttrName.Length );
|
|
}
|
|
|
|
} finally {
|
|
|
|
NtfsCleanupAttributeContext( &AttrContext );
|
|
}
|
|
}
|
|
|
|
if (FoundAttribute) {
|
|
|
|
//
|
|
// In this case we call our routine to open this attribute.
|
|
//
|
|
|
|
if ((CreateDisposition == FILE_OPEN) ||
|
|
(CreateDisposition == FILE_OPEN_IF)) {
|
|
|
|
Status = NtfsOpenExistingAttr( IrpContext,
|
|
Irp,
|
|
IrpSp,
|
|
ThisLcb,
|
|
ThisFcb,
|
|
LastFileNameOffset,
|
|
AttrName,
|
|
AttrTypeCode,
|
|
CcbFlags,
|
|
OpenById,
|
|
FALSE,
|
|
NetworkInfo,
|
|
ThisScb,
|
|
ThisCcb );
|
|
|
|
if ((Status != STATUS_PENDING) &&
|
|
(*ThisScb != NULL)) {
|
|
|
|
ClearFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_CREATE_MOD_SCB );
|
|
}
|
|
|
|
//
|
|
// If he wanted to overwrite this attribute, we call our overwrite routine.
|
|
//
|
|
|
|
} else if ((CreateDisposition == FILE_SUPERSEDE) ||
|
|
(CreateDisposition == FILE_OVERWRITE) ||
|
|
(CreateDisposition == FILE_OVERWRITE_IF)) {
|
|
|
|
//
|
|
// Check if mm will allow us to modify this file.
|
|
//
|
|
|
|
Status = NtfsOverwriteAttr( IrpContext,
|
|
Irp,
|
|
IrpSp,
|
|
ThisLcb,
|
|
ThisFcb,
|
|
(BOOLEAN) (CreateDisposition == FILE_SUPERSEDE),
|
|
LastFileNameOffset,
|
|
AttrName,
|
|
AttrTypeCode,
|
|
CcbFlags,
|
|
OpenById,
|
|
ThisScb,
|
|
ThisCcb );
|
|
|
|
//
|
|
// Remember that this Scb was modified.
|
|
//
|
|
|
|
if ((Status != STATUS_PENDING) &&
|
|
(*ThisScb != NULL)) {
|
|
|
|
SetFlag( IrpSp->FileObject->Flags, FO_FILE_MODIFIED );
|
|
SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_CREATE_MOD_SCB );
|
|
}
|
|
|
|
//
|
|
// Otherwise he is trying to create the attribute.
|
|
//
|
|
|
|
} else {
|
|
|
|
Status = STATUS_OBJECT_NAME_COLLISION;
|
|
}
|
|
|
|
//
|
|
// The attribute doesn't exist. If the user expected it to exist, we fail.
|
|
// Otherwise we call our routine to create an attribute.
|
|
//
|
|
|
|
} else if ((CreateDisposition == FILE_OPEN) ||
|
|
(CreateDisposition == FILE_OVERWRITE)) {
|
|
|
|
Status = STATUS_OBJECT_NAME_NOT_FOUND;
|
|
|
|
} else {
|
|
|
|
//
|
|
// Perform the open check for this existing file.
|
|
//
|
|
|
|
Status = NtfsCheckExistingFile( IrpContext,
|
|
IrpSp,
|
|
ThisLcb,
|
|
ThisFcb,
|
|
CcbFlags );
|
|
|
|
//
|
|
// If this didn't fail then attempt to create the stream.
|
|
//
|
|
|
|
if (NT_SUCCESS( Status )) {
|
|
|
|
Status = NtfsOpenNewAttr( IrpContext,
|
|
Irp,
|
|
IrpSp,
|
|
ThisLcb,
|
|
ThisFcb,
|
|
LastFileNameOffset,
|
|
AttrName,
|
|
AttrTypeCode,
|
|
CcbFlags,
|
|
TRUE,
|
|
OpenById,
|
|
ThisScb,
|
|
ThisCcb );
|
|
}
|
|
|
|
if (*ThisScb != NULL) {
|
|
|
|
if (*ThisCcb != NULL) {
|
|
|
|
SetFlag( (*ThisCcb)->Flags,
|
|
CCB_FLAG_UPDATE_LAST_CHANGE | CCB_FLAG_SET_ARCHIVE );
|
|
}
|
|
|
|
SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_CREATE_MOD_SCB );
|
|
}
|
|
}
|
|
}
|
|
|
|
DebugTrace( -1, Dbg, ("NtfsOpenAttributeInExistingFile: Exit\n") );
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
//
|
|
// Local support routine.
|
|
//
|
|
|
|
NTSTATUS
|
|
NtfsOpenExistingAttr (
|
|
IN PIRP_CONTEXT IrpContext,
|
|
IN PIRP Irp,
|
|
IN PIO_STACK_LOCATION IrpSp,
|
|
IN PLCB ThisLcb OPTIONAL,
|
|
IN PFCB ThisFcb,
|
|
IN ULONG LastFileNameOffset,
|
|
IN UNICODE_STRING AttrName,
|
|
IN ATTRIBUTE_TYPE_CODE AttrTypeCode,
|
|
IN ULONG CcbFlags,
|
|
IN BOOLEAN OpenById,
|
|
IN BOOLEAN DirectoryOpen,
|
|
IN PVOID NetworkInfo OPTIONAL,
|
|
OUT PSCB *ThisScb,
|
|
OUT PCCB *ThisCcb
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called to open an existing attribute. We check the
|
|
requested file access, the existance of
|
|
an Ea buffer and the security on this file. If these succeed then
|
|
we check the batch oplocks and regular oplocks on the file.
|
|
If we have gotten this far, we simply call our routine to open the
|
|
attribute.
|
|
|
|
Arguments:
|
|
|
|
Irp - This is the Irp for this open operation.
|
|
|
|
IrpSp - This is the Irp stack pointer for the filesystem.
|
|
|
|
ThisLcb - This is the Lcb used to reach this Fcb.
|
|
|
|
ThisFcb - This is the Fcb to open.
|
|
|
|
LastFileNameOffset - This is the offset in the full path name of the
|
|
final component.
|
|
|
|
AttrName - This is the attribute name in case we need to create
|
|
an Scb.
|
|
|
|
AttrTypeCode - This is the attribute type code to use to create
|
|
the Scb.
|
|
|
|
CcbFlags - This is the flag field for the Ccb.
|
|
|
|
OpenById - Indicates if this open is by file Id.
|
|
|
|
DirectoryOpen - Indicates whether this open is a directory open or a data stream.
|
|
|
|
NetworkInfo - If specified then this call is a fast open call to query
|
|
the network information. We don't update any of the in-memory structures
|
|
for this.
|
|
|
|
ThisScb - This is the address to store the address of the Scb.
|
|
|
|
ThisCcb - This is the address to store the address of the Ccb.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - The result of opening this indexed attribute.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
NTSTATUS OplockStatus;
|
|
|
|
SHARE_MODIFICATION_TYPE ShareModificationType;
|
|
TYPE_OF_OPEN TypeOfOpen;
|
|
|
|
PAGED_CODE();
|
|
|
|
DebugTrace( +1, Dbg, ("NtfsOpenExistingAttr: Entered\n") );
|
|
|
|
//
|
|
// For data streams we need to do a check that includes an oplock check.
|
|
// For directories we just need to figure the share modification type.
|
|
//
|
|
// We also figure the type of open and the node type code based on the
|
|
// directory flag.
|
|
//
|
|
|
|
if (DirectoryOpen) {
|
|
|
|
//
|
|
// Check for valid access on an existing file.
|
|
//
|
|
|
|
Status = NtfsCheckExistingFile( IrpContext,
|
|
IrpSp,
|
|
ThisLcb,
|
|
ThisFcb,
|
|
CcbFlags );
|
|
|
|
ShareModificationType = (ThisFcb->CleanupCount == 0 ? SetShareAccess : CheckShareAccess);
|
|
TypeOfOpen = UserDirectoryOpen;
|
|
|
|
} else {
|
|
|
|
//
|
|
// Don't break the batch oplock if opening to query the network info.
|
|
//
|
|
|
|
if (!ARGUMENT_PRESENT( NetworkInfo )) {
|
|
|
|
Status = NtfsBreakBatchOplock( IrpContext,
|
|
Irp,
|
|
IrpSp,
|
|
ThisFcb,
|
|
AttrName,
|
|
AttrTypeCode,
|
|
ThisScb );
|
|
|
|
if (Status != STATUS_PENDING) {
|
|
|
|
if (NT_SUCCESS( Status = NtfsCheckExistingFile( IrpContext,
|
|
IrpSp,
|
|
ThisLcb,
|
|
ThisFcb,
|
|
CcbFlags ))) {
|
|
|
|
Status = NtfsOpenAttributeCheck( IrpContext,
|
|
Irp,
|
|
IrpSp,
|
|
ThisScb,
|
|
&ShareModificationType );
|
|
|
|
#ifndef _CAIRO_
|
|
TypeOfOpen = UserFileOpen;
|
|
#else // _CAIRO_
|
|
TypeOfOpen =
|
|
AttrTypeCode == $DATA ? UserFileOpen : UserPropertySetOpen;
|
|
#endif // _CAIRO_
|
|
|
|
ASSERT( NtfsIsTypeCodeUserData( AttrTypeCode ));
|
|
}
|
|
}
|
|
|
|
//
|
|
// We want to perform the ACL check but not break any oplocks for the
|
|
// NetworkInformation query.
|
|
//
|
|
|
|
} else {
|
|
|
|
Status = NtfsCheckExistingFile( IrpContext,
|
|
IrpSp,
|
|
ThisLcb,
|
|
ThisFcb,
|
|
CcbFlags );
|
|
|
|
#ifndef _CAIRO_
|
|
TypeOfOpen = UserFileOpen;
|
|
#else // _CAIRO_
|
|
TypeOfOpen =
|
|
AttrTypeCode == $DATA ? UserFileOpen : UserPropertySetOpen;
|
|
#endif // _CAIRO_
|
|
|
|
ASSERT( NtfsIsTypeCodeUserData( AttrTypeCode ));
|
|
}
|
|
}
|
|
|
|
//
|
|
// If we didn't post the Irp and the operation was successful, we
|
|
// proceed with the open.
|
|
//
|
|
|
|
if (NT_SUCCESS( Status )
|
|
&& Status != STATUS_PENDING) {
|
|
|
|
//
|
|
// Now actually open the attribute.
|
|
//
|
|
|
|
OplockStatus = Status;
|
|
|
|
Status = NtfsOpenAttribute( IrpContext,
|
|
IrpSp,
|
|
ThisFcb->Vcb,
|
|
ThisLcb,
|
|
ThisFcb,
|
|
LastFileNameOffset,
|
|
AttrName,
|
|
AttrTypeCode,
|
|
ShareModificationType,
|
|
TypeOfOpen,
|
|
(OpenById
|
|
? CcbFlags | CCB_FLAG_OPEN_BY_FILE_ID
|
|
: CcbFlags),
|
|
NetworkInfo,
|
|
ThisScb,
|
|
ThisCcb );
|
|
|
|
//
|
|
// If there are no errors at this point, we set the caller's Iosb.
|
|
//
|
|
|
|
if (NT_SUCCESS( Status )) {
|
|
|
|
//
|
|
// We need to remember if the oplock break is in progress.
|
|
//
|
|
|
|
Status = OplockStatus;
|
|
|
|
Irp->IoStatus.Information = FILE_OPENED;
|
|
}
|
|
}
|
|
|
|
DebugTrace( -1, Dbg, ("NtfsOpenExistingAttr: Exit -> %08lx\n", Status) );
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
//
|
|
// Local support routine.
|
|
//
|
|
|
|
NTSTATUS
|
|
NtfsOverwriteAttr (
|
|
IN PIRP_CONTEXT IrpContext,
|
|
IN PIRP Irp,
|
|
IN PIO_STACK_LOCATION IrpSp,
|
|
IN PLCB ThisLcb OPTIONAL,
|
|
IN PFCB ThisFcb,
|
|
IN BOOLEAN Supersede,
|
|
IN ULONG LastFileNameOffset,
|
|
IN UNICODE_STRING AttrName,
|
|
IN ATTRIBUTE_TYPE_CODE AttrTypeCode,
|
|
IN ULONG CcbFlags,
|
|
IN BOOLEAN OpenById,
|
|
OUT PSCB *ThisScb,
|
|
OUT PCCB *ThisCcb
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called to overwrite an existing attribute. We do all of
|
|
the same work as opening an attribute except that we can change the
|
|
allocation of a file. This routine will handle the case where a
|
|
file is being overwritten and the case where just an attribute is
|
|
being overwritten. In the case of the former, we may change the
|
|
file attributes of the file as well as modify the Ea's on the file.
|
|
|
|
Arguments:
|
|
|
|
Irp - This is the Irp for this open operation.
|
|
|
|
IrpSp - This is the stack location for this open.
|
|
|
|
ThisLcb - This is the Lcb we used to reach this Fcb.
|
|
|
|
ThisFcb - This is the Fcb for the file being opened.
|
|
|
|
Supersede - This indicates whether this is a supersede or overwrite
|
|
operation.
|
|
|
|
LastFileNameOffset - This is the offset in the full path name of the
|
|
final component.
|
|
|
|
AttrName - This is the attribute name in case we need to create
|
|
an Scb.
|
|
|
|
AttrTypeCode - This is the attribute type code to use to create
|
|
the Scb.
|
|
|
|
CcbFlags - This is the flag field for the Ccb.
|
|
|
|
OpenById - Indicates if this open is by file Id.
|
|
|
|
ThisScb - This is the address to store the address of the Scb.
|
|
|
|
ThisCcb - This is the address to store the address of the Ccb.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - The result of opening this indexed attribute.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
NTSTATUS OplockStatus;
|
|
|
|
ULONG FileAttributes;
|
|
PACCESS_MASK DesiredAccess;
|
|
ACCESS_MASK AddedAccess = 0;
|
|
BOOLEAN MaximumRequested = FALSE;
|
|
|
|
SHARE_MODIFICATION_TYPE ShareModificationType;
|
|
|
|
PFILE_FULL_EA_INFORMATION FullEa = NULL;
|
|
ULONG FullEaLength = 0;
|
|
|
|
PAGED_CODE();
|
|
|
|
DebugTrace( +1, Dbg, ("NtfsOverwriteAttr: Entered\n") );
|
|
|
|
DesiredAccess = &IrpSp->Parameters.Create.SecurityContext->DesiredAccess;
|
|
|
|
if (FlagOn( *DesiredAccess, MAXIMUM_ALLOWED )) {
|
|
|
|
MaximumRequested = TRUE;
|
|
}
|
|
|
|
//
|
|
// Check the oplock state of this file.
|
|
//
|
|
|
|
Status = NtfsBreakBatchOplock( IrpContext,
|
|
Irp,
|
|
IrpSp,
|
|
ThisFcb,
|
|
AttrName,
|
|
AttrTypeCode,
|
|
ThisScb );
|
|
|
|
if (Status == STATUS_PENDING) {
|
|
|
|
DebugTrace( -1, Dbg, ("NtfsOverwriteAttr: Exit -> %08lx\n", Status) );
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// We first want to check that the caller's desired access and specified
|
|
// file attributes are compatible with the state of the file. There
|
|
// are the two overwrite cases to consider.
|
|
//
|
|
// OverwriteFile - The hidden and system bits passed in by the
|
|
// caller must match the current values.
|
|
//
|
|
// OverwriteAttribute - We also modify the requested desired access
|
|
// to explicitly add the implicit access needed by overwrite.
|
|
//
|
|
// We also check that for the overwrite attribute case, there isn't
|
|
// an Ea buffer specified.
|
|
//
|
|
|
|
if (FlagOn( CcbFlags, CCB_FLAG_OPEN_AS_FILE )) {
|
|
|
|
BOOLEAN Hidden;
|
|
BOOLEAN System;
|
|
|
|
//
|
|
// Get the file attributes and clear any unsupported bits.
|
|
//
|
|
|
|
FileAttributes = (ULONG) IrpSp->Parameters.Create.FileAttributes;
|
|
|
|
SetFlag( FileAttributes, FILE_ATTRIBUTE_ARCHIVE );
|
|
ClearFlag( FileAttributes,
|
|
~(FILE_ATTRIBUTE_READONLY |
|
|
FILE_ATTRIBUTE_HIDDEN |
|
|
FILE_ATTRIBUTE_SYSTEM |
|
|
FILE_ATTRIBUTE_ARCHIVE) );
|
|
|
|
DebugTrace( 0, Dbg, ("Checking hidden/system for overwrite/supersede\n") );
|
|
|
|
Hidden = BooleanIsHidden( &ThisFcb->Info );
|
|
System = BooleanIsSystem( &ThisFcb->Info );
|
|
|
|
if ((Hidden && !FlagOn(FileAttributes, FILE_ATTRIBUTE_HIDDEN)
|
|
||
|
|
System && !FlagOn(FileAttributes, FILE_ATTRIBUTE_SYSTEM))
|
|
|
|
&&
|
|
|
|
!FlagOn( IrpSp->Flags, SL_OPEN_PAGING_FILE )) {
|
|
|
|
DebugTrace( 0, Dbg, ("The hidden and/or system bits do not match\n") );
|
|
|
|
Status = STATUS_ACCESS_DENIED;
|
|
|
|
DebugTrace( -1, Dbg, ("NtfsOverwriteAttr: Exit -> %08lx\n", Status) );
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// If the user specified an Ea buffer and they are Ea blind, we deny
|
|
// access.
|
|
//
|
|
|
|
if (FlagOn( IrpSp->Parameters.Create.Options, FILE_NO_EA_KNOWLEDGE )
|
|
&& Irp->AssociatedIrp.SystemBuffer != NULL) {
|
|
|
|
DebugTrace( 0, Dbg, ("This opener cannot create Ea's\n") );
|
|
|
|
Status = STATUS_ACCESS_DENIED;
|
|
|
|
DebugTrace( -1, Dbg, ("NtfsOverwriteAttr: Exit -> %08lx\n", Status) );
|
|
return Status;
|
|
}
|
|
|
|
if (!FlagOn( IrpSp->Flags, SL_OPEN_PAGING_FILE )) {
|
|
|
|
SetFlag( AddedAccess,
|
|
(FILE_WRITE_EA | FILE_WRITE_ATTRIBUTES) & ~(*DesiredAccess) );
|
|
|
|
SetFlag( *DesiredAccess, FILE_WRITE_EA | FILE_WRITE_ATTRIBUTES );
|
|
}
|
|
|
|
} else if (Irp->AssociatedIrp.SystemBuffer != NULL) {
|
|
|
|
DebugTrace( 0, Dbg, ("Can't specifiy an Ea buffer on an attribute overwrite\n") );
|
|
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
|
|
DebugTrace( -1, Dbg, ("NtfsOverwriteAttr: Exit -> %08lx\n", Status) );
|
|
return Status;
|
|
}
|
|
|
|
if (!FlagOn( IrpSp->Flags, SL_OPEN_PAGING_FILE )) {
|
|
|
|
if (Supersede) {
|
|
|
|
SetFlag( AddedAccess,
|
|
DELETE & ~(*DesiredAccess) );
|
|
|
|
SetFlag( *DesiredAccess, DELETE );
|
|
|
|
} else {
|
|
|
|
SetFlag( AddedAccess,
|
|
FILE_WRITE_DATA & ~(*DesiredAccess) );
|
|
|
|
SetFlag( *DesiredAccess, FILE_WRITE_DATA );
|
|
}
|
|
}
|
|
|
|
//
|
|
// Check whether we can open this existing file.
|
|
//
|
|
|
|
Status = NtfsCheckExistingFile( IrpContext,
|
|
IrpSp,
|
|
ThisLcb,
|
|
ThisFcb,
|
|
CcbFlags );
|
|
|
|
//
|
|
// If we have a success status then proceed with the oplock check and
|
|
// open the attribute.
|
|
//
|
|
|
|
if (NT_SUCCESS( Status )) {
|
|
|
|
Status = NtfsOpenAttributeCheck( IrpContext,
|
|
Irp,
|
|
IrpSp,
|
|
ThisScb,
|
|
&ShareModificationType );
|
|
|
|
//
|
|
// If we biased the desired access we need to remove the same
|
|
// bits from the granted access. If maximum allowed was
|
|
// requested then we can skip this.
|
|
//
|
|
|
|
if (!MaximumRequested) {
|
|
|
|
ClearFlag( IrpSp->Parameters.Create.SecurityContext->AccessState->PreviouslyGrantedAccess,
|
|
AddedAccess );
|
|
}
|
|
|
|
//
|
|
// Also remove the bits from the desired access field so we won't
|
|
// see them if this request gets posted for any reason.
|
|
//
|
|
|
|
ClearFlag( *DesiredAccess, AddedAccess );
|
|
|
|
//
|
|
// If we didn't post the Irp and the operation was successful, we
|
|
// proceed with the open.
|
|
//
|
|
|
|
if (NT_SUCCESS( Status )
|
|
&& Status != STATUS_PENDING) {
|
|
|
|
//
|
|
// Reference the Fcb so it doesn't go away.
|
|
//
|
|
|
|
InterlockedIncrement( &ThisFcb->CloseCount );
|
|
|
|
//
|
|
// Use a try-finally to restore the close count correctly.
|
|
//
|
|
|
|
try {
|
|
|
|
//
|
|
// If we can't truncate the file size then return now.
|
|
//
|
|
|
|
if (!MmCanFileBeTruncated( &(*ThisScb)->NonpagedScb->SegmentObject,
|
|
&Li0 )) {
|
|
|
|
Status = STATUS_USER_MAPPED_FILE;
|
|
DebugTrace( -1, Dbg, ("NtfsOverwriteAttr: Exit -> %08lx\n", Status) );
|
|
|
|
try_return( Status );
|
|
}
|
|
|
|
//
|
|
// Remember the status from the oplock check.
|
|
//
|
|
|
|
OplockStatus = Status;
|
|
|
|
//
|
|
// We perform the on-disk changes. For a file overwrite, this includes
|
|
// the Ea changes and modifying the file attributes. For an attribute,
|
|
// this refers to modifying the allocation size. We need to keep the
|
|
// Fcb updated and remember which values we changed.
|
|
//
|
|
|
|
if (Irp->AssociatedIrp.SystemBuffer != NULL) {
|
|
|
|
//
|
|
// Remember the values in the Irp.
|
|
//
|
|
|
|
FullEa = (PFILE_FULL_EA_INFORMATION) Irp->AssociatedIrp.SystemBuffer;
|
|
FullEaLength = IrpSp->Parameters.Create.EaLength;
|
|
}
|
|
|
|
//
|
|
// Now do the file attributes and either remove or mark for
|
|
// delete all of the other $DATA attributes on the file.
|
|
//
|
|
|
|
if (FlagOn( CcbFlags, CCB_FLAG_OPEN_AS_FILE )) {
|
|
|
|
//
|
|
// Replace the current Ea's on the file. This operation will update
|
|
// the Fcb for the file.
|
|
//
|
|
|
|
NtfsAddEa( IrpContext,
|
|
ThisFcb->Vcb,
|
|
ThisFcb,
|
|
FullEa,
|
|
FullEaLength,
|
|
&Irp->IoStatus );
|
|
|
|
//
|
|
// Copy the directory bit from the current Info structure.
|
|
//
|
|
|
|
if (IsDirectory( &ThisFcb->Info)) {
|
|
|
|
SetFlag( FileAttributes, DUP_FILE_NAME_INDEX_PRESENT );
|
|
}
|
|
|
|
//
|
|
// Now either add to the current attributes or replace them.
|
|
//
|
|
|
|
if (Supersede) {
|
|
|
|
ThisFcb->Info.FileAttributes = FileAttributes;
|
|
|
|
} else {
|
|
|
|
ThisFcb->Info.FileAttributes |= FileAttributes;
|
|
}
|
|
|
|
//
|
|
// Get rid of any named $DATA attributes in the file.
|
|
//
|
|
|
|
NtfsRemoveDataAttributes( IrpContext,
|
|
ThisFcb,
|
|
ThisLcb,
|
|
IrpSp->FileObject,
|
|
LastFileNameOffset,
|
|
OpenById );
|
|
}
|
|
|
|
//
|
|
// Now we perform the operation of opening the attribute.
|
|
//
|
|
|
|
NtfsReplaceAttribute( IrpContext,
|
|
IrpSp,
|
|
ThisFcb,
|
|
*ThisScb,
|
|
ThisLcb,
|
|
*(PLONGLONG)&Irp->Overlay.AllocationSize );
|
|
|
|
//
|
|
// If we are overwriting a fle and the user doesn't want it marked as
|
|
// compressed, then change the attribute flag.
|
|
//
|
|
|
|
if (!FlagOn( (*ThisScb)->AttributeFlags, ATTRIBUTE_FLAG_COMPRESSION_MASK) &&
|
|
FlagOn( ThisFcb->Info.FileAttributes, FILE_ATTRIBUTE_COMPRESSED ) &&
|
|
FlagOn( CcbFlags, CCB_FLAG_OPEN_AS_FILE )) {
|
|
|
|
ClearFlag( ThisFcb->Info.FileAttributes, FILE_ATTRIBUTE_COMPRESSED );
|
|
}
|
|
|
|
//
|
|
// Now attempt to open the attribute.
|
|
//
|
|
|
|
ASSERT( NtfsIsTypeCodeUserData( AttrTypeCode ));
|
|
|
|
Status = NtfsOpenAttribute( IrpContext,
|
|
IrpSp,
|
|
ThisFcb->Vcb,
|
|
ThisLcb,
|
|
ThisFcb,
|
|
LastFileNameOffset,
|
|
AttrName,
|
|
AttrTypeCode,
|
|
ShareModificationType,
|
|
#ifndef _CAIRO
|
|
UserFileOpen,
|
|
#else // _CAIRO_
|
|
AttrTypeCode == $DATA ? UserFileOpen : UserPropertySetOpen,
|
|
#endif // _CAIRO_
|
|
(OpenById
|
|
? CcbFlags | CCB_FLAG_OPEN_BY_FILE_ID
|
|
: CcbFlags),
|
|
NULL,
|
|
ThisScb,
|
|
ThisCcb );
|
|
|
|
try_exit: NOTHING;
|
|
} finally {
|
|
|
|
InterlockedDecrement( &ThisFcb->CloseCount );
|
|
}
|
|
|
|
if (NT_SUCCESS( Status )) {
|
|
|
|
//
|
|
// Set the flag in the Scb to indicate that the size of the
|
|
// attribute has changed.
|
|
//
|
|
|
|
SetFlag( (*ThisScb)->ScbState, SCB_STATE_NOTIFY_RESIZE_STREAM );
|
|
|
|
//
|
|
// Since this is an supersede/overwrite, purge the section
|
|
// so that mappers will see zeros.
|
|
//
|
|
|
|
CcPurgeCacheSection( IrpSp->FileObject->SectionObjectPointer,
|
|
NULL,
|
|
0,
|
|
FALSE );
|
|
|
|
//
|
|
// Remember the status of the oplock in the success code.
|
|
//
|
|
|
|
Status = OplockStatus;
|
|
|
|
//
|
|
// Now update the Iosb information.
|
|
//
|
|
|
|
if (Supersede) {
|
|
|
|
Irp->IoStatus.Information = FILE_SUPERSEDED;
|
|
|
|
} else {
|
|
|
|
Irp->IoStatus.Information = FILE_OVERWRITTEN;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
DebugTrace( -1, Dbg, ("NtfsOverwriteAttr: Exit -> %08lx\n", Status) );
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
//
|
|
// Local support routine.
|
|
//
|
|
|
|
NTSTATUS
|
|
NtfsOpenNewAttr (
|
|
IN PIRP_CONTEXT IrpContext,
|
|
IN PIRP Irp,
|
|
IN PIO_STACK_LOCATION IrpSp,
|
|
IN PLCB ThisLcb,
|
|
IN PFCB ThisFcb,
|
|
IN ULONG LastFileNameOffset,
|
|
IN UNICODE_STRING AttrName,
|
|
IN ATTRIBUTE_TYPE_CODE AttrTypeCode,
|
|
IN ULONG CcbFlags,
|
|
IN BOOLEAN LogIt,
|
|
IN BOOLEAN OpenById,
|
|
OUT PSCB *ThisScb,
|
|
OUT PCCB *ThisCcb
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called to create a new attribute on the disk.
|
|
All access and security checks have been done outside of this
|
|
routine, all we do is create the attribute and open it.
|
|
We test if the attribute will fit in the Mft record. If so we
|
|
create it there. Otherwise we call the create attribute through
|
|
allocation.
|
|
|
|
We then open the attribute with our common routine. In the
|
|
resident case the Scb will have all file values set to
|
|
the allocation size. We set the valid data size back to zero
|
|
and mark the Scb as truncate on close.
|
|
|
|
Arguments:
|
|
|
|
Irp - This is the Irp for this open operation.
|
|
|
|
IrpSp - This is the stack location for this open.
|
|
|
|
ThisLcb - This is the Lcb we used to reach this Fcb.
|
|
|
|
ThisFcb - This is the Fcb for the file being opened.
|
|
|
|
LastFileNameOffset - This is the offset in the full path name of the
|
|
final component.
|
|
|
|
AttrName - This is the attribute name in case we need to create
|
|
an Scb.
|
|
|
|
AttrTypeCode - This is the attribute type code to use to create
|
|
the Scb.
|
|
|
|
CcbFlags - This is the flag field for the Ccb.
|
|
|
|
LogIt - Indicates if we need to log the create operations.
|
|
|
|
OpenById - Indicates if this open is related to a OpenByFile open.
|
|
|
|
ThisScb - This is the address to store the address of the Scb.
|
|
|
|
ThisCcb - This is the address to store the address of the Ccb.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - The result of opening this indexed attribute.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
ATTRIBUTE_ENUMERATION_CONTEXT AttrContext;
|
|
|
|
BOOLEAN ScbExisted;
|
|
|
|
PAGED_CODE();
|
|
|
|
DebugTrace( +1, Dbg, ("NtfsOpenNewAttr: Entered\n") );
|
|
|
|
NtfsInitializeAttributeContext( &AttrContext );
|
|
|
|
//
|
|
// Use a try-finally to facilitate cleanup.
|
|
//
|
|
|
|
try {
|
|
|
|
//
|
|
// We create the Scb because we will use it.
|
|
//
|
|
|
|
*ThisScb = NtfsCreateScb( IrpContext,
|
|
ThisFcb,
|
|
AttrTypeCode,
|
|
&AttrName,
|
|
FALSE,
|
|
&ScbExisted );
|
|
|
|
//
|
|
// An attribute has gone away but the Scb hasn't left yet.
|
|
// Also mark the header as unitialized.
|
|
//
|
|
|
|
ClearFlag( (*ThisScb)->ScbState, SCB_STATE_HEADER_INITIALIZED |
|
|
SCB_STATE_ATTRIBUTE_RESIDENT |
|
|
SCB_STATE_FILE_SIZE_LOADED );
|
|
|
|
//
|
|
// Create the attribute on disk and update the Scb and Fcb.
|
|
//
|
|
|
|
NtfsCreateAttribute( IrpContext,
|
|
IrpSp,
|
|
ThisFcb,
|
|
*ThisScb,
|
|
ThisLcb,
|
|
*(PLONGLONG)&Irp->Overlay.AllocationSize,
|
|
LogIt );
|
|
|
|
//
|
|
// Now actually open the attribute.
|
|
//
|
|
|
|
ASSERT( NtfsIsTypeCodeUserData( AttrTypeCode ));
|
|
|
|
Status = NtfsOpenAttribute( IrpContext,
|
|
IrpSp,
|
|
ThisFcb->Vcb,
|
|
ThisLcb,
|
|
ThisFcb,
|
|
LastFileNameOffset,
|
|
AttrName,
|
|
AttrTypeCode,
|
|
(ThisFcb->CleanupCount != 0 ? CheckShareAccess : SetShareAccess),
|
|
#ifndef _CAIRO_
|
|
UserFileOpen,
|
|
#else // _CAIRO_
|
|
AttrTypeCode == $DATA ? UserFileOpen : UserPropertySetOpen,
|
|
#endif // _CAIRO_
|
|
(CcbFlags | (OpenById ? CCB_FLAG_OPEN_BY_FILE_ID : 0)),
|
|
NULL,
|
|
ThisScb,
|
|
ThisCcb );
|
|
|
|
//
|
|
// If there are no errors at this point, we set the caller's Iosb.
|
|
//
|
|
|
|
if (NT_SUCCESS( Status )) {
|
|
|
|
//
|
|
// Read the attribute information from the disk.
|
|
//
|
|
|
|
NtfsUpdateScbFromAttribute( IrpContext, *ThisScb, NULL );
|
|
|
|
//
|
|
// Set the flag to indicate that we created a stream and also remember to
|
|
// to check if we need to truncate on close.
|
|
//
|
|
|
|
NtfsAcquireFsrtlHeader( *ThisScb );
|
|
SetFlag( (*ThisScb)->ScbState,
|
|
SCB_STATE_TRUNCATE_ON_CLOSE | SCB_STATE_NOTIFY_ADD_STREAM );
|
|
|
|
//
|
|
// If we created a temporary stream then mark the Scb.
|
|
//
|
|
|
|
if (FlagOn( IrpSp->Parameters.Create.FileAttributes, FILE_ATTRIBUTE_TEMPORARY )) {
|
|
|
|
SetFlag( (*ThisScb)->ScbState, SCB_STATE_TEMPORARY );
|
|
SetFlag( IrpSp->FileObject->Flags, FO_TEMPORARY_FILE );
|
|
}
|
|
|
|
NtfsReleaseFsrtlHeader( *ThisScb );
|
|
|
|
Irp->IoStatus.Information = FILE_CREATED;
|
|
}
|
|
|
|
} finally {
|
|
|
|
DebugUnwind( NtfsOpenNewAttr );
|
|
|
|
//
|
|
// Uninitialize the attribute context.
|
|
//
|
|
|
|
NtfsCleanupAttributeContext( &AttrContext );
|
|
|
|
DebugTrace( -1, Dbg, ("NtfsOpenNewAttr: Exit -> %08lx\n", Status) );
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
//
|
|
// Local support routine
|
|
//
|
|
|
|
BOOLEAN
|
|
NtfsParseNameForCreate (
|
|
IN PIRP_CONTEXT IrpContext,
|
|
IN UNICODE_STRING String,
|
|
IN OUT PUNICODE_STRING FileObjectString,
|
|
IN OUT PUNICODE_STRING OriginalString,
|
|
IN OUT PUNICODE_STRING NewNameString,
|
|
OUT PUNICODE_STRING AttrName,
|
|
OUT PUNICODE_STRING AttrCodeName
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine parses the input string and remove any intermediate
|
|
named attributes from intermediate nodes. It verifies that all
|
|
intermediate nodes specify the file name index attribute if any
|
|
at all. On output it will store the modified string which contains
|
|
component names only, into the file object name pointer pointer. It is legal
|
|
for the last component to have attribute strings. We pass those
|
|
back via the attribute name strings. We also construct the string to be stored
|
|
back in the file object if we need to post this request.
|
|
|
|
Arguments:
|
|
|
|
String - This is the string to normalize.
|
|
|
|
FileObjectString - We store the normalized string into this pointer, removing the
|
|
attribute and attribute code strings from all component.
|
|
|
|
OriginalString - This is the same as the file object string except we append the
|
|
attribute name and attribute code strings. We assume that the buffer for this
|
|
string is the same as the buffer for the FileObjectString.
|
|
|
|
NewNameString - This is the string which contains the full name being parsed.
|
|
If the buffer is different than the buffer for the Original string then any
|
|
character shifts will be duplicated here.
|
|
|
|
AttrName - We store the attribute name specified in the last component
|
|
in this string.
|
|
|
|
AttrCodeName - We store the attribute code name specified in the last
|
|
component in this string.
|
|
|
|
Return Value:
|
|
|
|
BOOLEAN - TRUE if the path is legal, FALSE otherwise.
|
|
|
|
--*/
|
|
|
|
{
|
|
PARSE_TERMINATION_REASON TerminationReason;
|
|
UNICODE_STRING ParsedPath;
|
|
|
|
NTFS_NAME_DESCRIPTOR NameDescript;
|
|
|
|
BOOLEAN RemovedIndexName = FALSE;
|
|
|
|
LONG FileObjectIndex;
|
|
LONG NewNameIndex;
|
|
|
|
BOOLEAN SameBuffers = (OriginalString->Buffer == NewNameString->Buffer);
|
|
|
|
PUNICODE_STRING TestAttrName;
|
|
PUNICODE_STRING TestAttrCodeName;
|
|
|
|
PAGED_CODE();
|
|
|
|
DebugTrace( +1, Dbg, ("NtfsParseNameForCreate: Entered\n") );
|
|
|
|
//
|
|
// We loop through the input string calling ParsePath to swallow the
|
|
// biggest chunk we can. The main case we want to deal with is
|
|
// when we encounter a non-simple name. If this is not the
|
|
// final component, the attribute name and code type better
|
|
// indicate that this is a directory. The only other special
|
|
// case we consider is the case where the string is an
|
|
// attribute only. This is legal only for the first component
|
|
// of the file, and then only if there is no leading backslash.
|
|
//
|
|
|
|
//
|
|
// Initialize some return values.
|
|
//
|
|
|
|
AttrName->Length = 0;
|
|
AttrCodeName->Length = 0;
|
|
|
|
//
|
|
// Set up the indexes into our starting file object string.
|
|
//
|
|
|
|
FileObjectIndex = (LONG) FileObjectString->Length - (LONG) String.Length;
|
|
NewNameIndex = (LONG) NewNameString->Length - (LONG) String.Length;
|
|
|
|
//
|
|
// We don't allow trailing colons.
|
|
//
|
|
|
|
if (String.Buffer[(String.Length / 2) - 1] == L':') {
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
if (String.Length != 0) {
|
|
|
|
while (TRUE) {
|
|
|
|
//
|
|
// Parse the next chunk in the input string.
|
|
//
|
|
|
|
TerminationReason = NtfsParsePath( String,
|
|
FALSE,
|
|
&ParsedPath,
|
|
&NameDescript,
|
|
&String );
|
|
|
|
//
|
|
// Analyze the termination reason to discover if we can abort the
|
|
// parse process.
|
|
//
|
|
|
|
switch (TerminationReason) {
|
|
|
|
case NonSimpleName :
|
|
|
|
//
|
|
// We will do the work below.
|
|
//
|
|
|
|
break;
|
|
|
|
case IllegalCharacterInName :
|
|
case VersionNumberPresent :
|
|
case MalFormedName :
|
|
|
|
//
|
|
// We simply return an error.
|
|
//
|
|
|
|
DebugTrace( -1, Dbg, ("NtfsParseNameForCreate: Illegal character\n") );
|
|
return FALSE;
|
|
|
|
case AttributeOnly :
|
|
|
|
//
|
|
// This is legal only if it is the only component of a relative open. We
|
|
// test this by checking that we are at the end of string and the file
|
|
// object name has a lead in ':' character or this is the root directory
|
|
// and the lead in characters are '\:'.
|
|
//
|
|
|
|
if ((String.Length != 0) ||
|
|
RemovedIndexName ||
|
|
(FileObjectString->Buffer[0] == L'\\' ?
|
|
FileObjectString->Buffer[1] != L':' :
|
|
FileObjectString->Buffer[0] != L':')) {
|
|
|
|
DebugTrace( -1, Dbg, ("NtfsParseNameForCreate: Illegal character\n") );
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// We can drop down to the EndOfPath case as it will copy over
|
|
// the parsed path portion.
|
|
//
|
|
|
|
case EndOfPathReached :
|
|
|
|
NOTHING;
|
|
}
|
|
|
|
//
|
|
// We add the filename part of the non-simple name to the parsed
|
|
// path. Check if we can include the separator.
|
|
//
|
|
|
|
if ((TerminationReason != EndOfPathReached)
|
|
&& (FlagOn( NameDescript.FieldsPresent, FILE_NAME_PRESENT_FLAG ))) {
|
|
|
|
if (ParsedPath.Length > 2
|
|
|| (ParsedPath.Length == 2
|
|
&& ParsedPath.Buffer[0] != L'\\')) {
|
|
|
|
ParsedPath.Length +=2;
|
|
}
|
|
|
|
ParsedPath.Length += NameDescript.FileName.Length;
|
|
}
|
|
|
|
FileObjectIndex += ParsedPath.Length;
|
|
NewNameIndex += ParsedPath.Length;
|
|
|
|
//
|
|
// If the remaining string is empty, then we remember any attributes and
|
|
// exit now.
|
|
//
|
|
|
|
if (String.Length == 0) {
|
|
|
|
//
|
|
// If the name specified either an attribute or attribute
|
|
// name, we remember them.
|
|
//
|
|
|
|
if (FlagOn( NameDescript.FieldsPresent, ATTRIBUTE_NAME_PRESENT_FLAG )) {
|
|
|
|
*AttrName = NameDescript.AttributeName;
|
|
}
|
|
|
|
if (FlagOn( NameDescript.FieldsPresent, ATTRIBUTE_TYPE_PRESENT_FLAG )) {
|
|
|
|
*AttrCodeName = NameDescript.AttributeType;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// This can only be the non-simple case. If there is more to the
|
|
// name, then the attributes better describe a directory. We also shift the
|
|
// remaining bytes of the string down.
|
|
//
|
|
|
|
ASSERT( FlagOn( NameDescript.FieldsPresent, ATTRIBUTE_NAME_PRESENT_FLAG | ATTRIBUTE_TYPE_PRESENT_FLAG ));
|
|
|
|
TestAttrName = FlagOn( NameDescript.FieldsPresent,
|
|
ATTRIBUTE_NAME_PRESENT_FLAG )
|
|
? &NameDescript.AttributeName
|
|
: &NtfsEmptyString;
|
|
|
|
TestAttrCodeName = FlagOn( NameDescript.FieldsPresent,
|
|
ATTRIBUTE_TYPE_PRESENT_FLAG )
|
|
? &NameDescript.AttributeType
|
|
: &NtfsEmptyString;
|
|
|
|
if (!NtfsVerifyNameIsDirectory( IrpContext,
|
|
TestAttrName,
|
|
TestAttrCodeName )) {
|
|
|
|
DebugTrace( -1, Dbg, ("NtfsParseNameForCreate: Invalid intermediate component\n") );
|
|
return FALSE;
|
|
}
|
|
|
|
RemovedIndexName = TRUE;
|
|
|
|
//
|
|
// We need to insert a separator and then move the rest of the string
|
|
// down.
|
|
//
|
|
|
|
FileObjectString->Buffer[FileObjectIndex / 2] = L'\\';
|
|
|
|
if (!SameBuffers) {
|
|
|
|
NewNameString->Buffer[NewNameIndex / 2] = L'\\';
|
|
}
|
|
|
|
FileObjectIndex += 2;
|
|
NewNameIndex += 2;
|
|
|
|
RtlMoveMemory( &FileObjectString->Buffer[FileObjectIndex / 2],
|
|
String.Buffer,
|
|
String.Length );
|
|
|
|
if (!SameBuffers) {
|
|
|
|
RtlMoveMemory( &NewNameString->Buffer[NewNameIndex / 2],
|
|
String.Buffer,
|
|
String.Length );
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// At this point the original string is the same as the file object string.
|
|
//
|
|
|
|
FileObjectString->Length = (USHORT) FileObjectIndex;
|
|
NewNameString->Length = (USHORT) NewNameIndex;
|
|
|
|
OriginalString->Length = FileObjectString->Length;
|
|
|
|
//
|
|
// We want to store the attribute index values in the original name
|
|
// string. We just need to extend the original name length.
|
|
//
|
|
|
|
if (AttrName->Length != 0
|
|
|| AttrCodeName->Length != 0) {
|
|
|
|
OriginalString->Length += (2 + AttrName->Length);
|
|
|
|
if (AttrCodeName->Length != 0) {
|
|
|
|
OriginalString->Length += (2 + AttrCodeName->Length);
|
|
}
|
|
}
|
|
|
|
DebugTrace( -1, Dbg, ("NtfsParseNameForCreate: Exit\n") );
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
//
|
|
// Local support routine.
|
|
//
|
|
|
|
NTSTATUS
|
|
NtfsCheckValidAttributeAccess (
|
|
IN PIO_STACK_LOCATION IrpSp,
|
|
IN PVCB Vcb,
|
|
IN PDUPLICATED_INFORMATION Info OPTIONAL,
|
|
IN UNICODE_STRING AttrName,
|
|
IN UNICODE_STRING AttrCodeName,
|
|
IN BOOLEAN TrailingBackslash,
|
|
OUT PATTRIBUTE_TYPE_CODE AttrTypeCode,
|
|
OUT PULONG CcbFlags,
|
|
OUT PBOOLEAN IndexedAttribute
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine looks at the file, the specified attribute name and
|
|
code to determine if an attribute of this file may be opened
|
|
by this user. If there is a conflict between the file type
|
|
and the attribute name and code, or the specified type of attribute
|
|
(directory/nondirectory) we will return FALSE.
|
|
We also check that the attribute code string is defined for the
|
|
volume at this time.
|
|
|
|
The final check of this routine is just whether a user is allowed
|
|
to open the particular attribute or if Ntfs will guard them.
|
|
|
|
Arguments:
|
|
|
|
IrpSp - This is the stack location for this open.
|
|
|
|
Vcb - This is the Vcb for this volume.
|
|
|
|
Info - If specified, this is the duplicated information for this file.
|
|
|
|
AttrName - This is the attribute name specified.
|
|
|
|
AttrCodeName - This is the attribute code name to use to open the attribute.
|
|
|
|
AttrTypeCode - Used to store the attribute type code determined here.
|
|
|
|
TrailingBackslash - Indicates if caller had a terminating backslash on the
|
|
name.
|
|
|
|
CcbFlags - We set the Ccb flags here to store in the Ccb later.
|
|
|
|
IndexedAttribute - Set to indicate the type of open.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - STATUS_SUCCESS if access is allowed, the status code indicating
|
|
the reason for denial otherwise.
|
|
|
|
--*/
|
|
|
|
{
|
|
BOOLEAN Indexed;
|
|
ATTRIBUTE_TYPE_CODE AttrType;
|
|
|
|
PAGED_CODE();
|
|
|
|
DebugTrace( +1, Dbg, ("NtfsCheckValidAttributeAccess: Entered\n") );
|
|
|
|
//
|
|
// If the user specified a attribute code string, we find the
|
|
// corresponding attribute. If there is no matching attribute
|
|
// type code then we report that this access is invalid.
|
|
//
|
|
|
|
if (AttrCodeName.Length != 0) {
|
|
|
|
AttrType = NtfsGetAttributeTypeCode( Vcb,
|
|
AttrCodeName );
|
|
|
|
if (AttrType == $UNUSED) {
|
|
|
|
DebugTrace( -1, Dbg, ("NtfsCheckValidAttributeAccess: Bad attribute name for index\n") );
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
//
|
|
// If the type code is Index allocation, then the name better be the filename
|
|
// index. If so then we clear the name length value to make our other
|
|
// tests work.
|
|
//
|
|
|
|
} else if (AttrType == $INDEX_ALLOCATION) {
|
|
|
|
if (AttrName.Length != 0) {
|
|
|
|
if (!NtfsAreNamesEqual( Vcb->UpcaseTable, &AttrName, &NtfsFileNameIndex, TRUE )) {
|
|
|
|
DebugTrace( -1, Dbg, ("NtfsCheckValidAttributeAccess: Bad name for index allocation\n") );
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
AttrName.Length = 0;
|
|
}
|
|
}
|
|
#ifdef _CAIRO_
|
|
//
|
|
// BUGBUG - Ntfs does not correctly handle compressing streams that
|
|
// are logged. Despite the fact that property sets can be large,
|
|
// we forcibly disable compression on these streams.
|
|
//
|
|
|
|
else if (AttrType == $PROPERTY_SET) {
|
|
SetFlag( IrpSp->Parameters.Create.Options, FILE_NO_COMPRESSION );
|
|
}
|
|
#endif // _CAIRO_
|
|
|
|
|
|
DebugTrace( 0, Dbg, ("Attribute type code -> %04x\n", AttrType) );
|
|
|
|
} else {
|
|
|
|
AttrType = $UNUSED;
|
|
}
|
|
|
|
//
|
|
// Pull some values out of the Irp and IrpSp.
|
|
//
|
|
|
|
Indexed = BooleanFlagOn( IrpSp->Parameters.Create.Options,
|
|
FILE_DIRECTORY_FILE );
|
|
|
|
//
|
|
// We need to determine whether the user expects to open an
|
|
// indexed or non-indexed attribute. If either of the
|
|
// directory/non-directory flags in the Irp stack are set,
|
|
// we will use those.
|
|
//
|
|
// Otherwise we need to examine some of the other input parameters.
|
|
// We have the following information:
|
|
//
|
|
// 1 - We may have a duplicated information structure for the file.
|
|
// (Not present on a create).
|
|
// 2 - The user specified the name with a trailing backslash.
|
|
// 3 - The user passed in an attribute name.
|
|
// 4 - The user passed in an attribute type.
|
|
//
|
|
// We first look at the attribute type code and name. If they are
|
|
// both unspecified we determine the type of access by following
|
|
// the following steps.
|
|
//
|
|
// 1 - If there is a duplicated information structure we
|
|
// set the code to $INDEX_ALLOCATION and remember
|
|
// this is indexed. Otherwise this is a $DATA/$PROPERTY_SET
|
|
// attribute.
|
|
//
|
|
// 2 - If there is a trailing backslash we assume this is
|
|
// an indexed attribute.
|
|
//
|
|
// If have an attribute code type or name, then if the code type is
|
|
// $INDEX_ALLOCATION without a name this is an indexed attribute.
|
|
// Otherwise we assume a non-indexed attribute.
|
|
//
|
|
|
|
if (!FlagOn( IrpSp->Parameters.Create.Options,
|
|
FILE_NON_DIRECTORY_FILE | FILE_DIRECTORY_FILE) &&
|
|
(AttrName.Length == 0)) {
|
|
|
|
if (AttrType == $UNUSED) {
|
|
|
|
if (ARGUMENT_PRESENT( Info )) {
|
|
|
|
Indexed = BooleanIsDirectory( Info );
|
|
|
|
} else {
|
|
|
|
Indexed = FALSE;
|
|
}
|
|
|
|
} else if (AttrType == $INDEX_ALLOCATION) {
|
|
|
|
Indexed = TRUE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If the type code was unspecified, we can assume it from the attribute
|
|
// name and the type of the file. If the file is a directory and
|
|
// there is no attribute name, we assume this is an indexed open.
|
|
// Otherwise it is a non-indexed open.
|
|
//
|
|
|
|
if (AttrType == $UNUSED) {
|
|
|
|
if (Indexed && AttrName.Length == 0) {
|
|
|
|
AttrType = $INDEX_ALLOCATION;
|
|
|
|
} else {
|
|
|
|
AttrType = $DATA;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If the user specified directory all we need to do is check the
|
|
// following condition.
|
|
//
|
|
// 1 - If the file was specified, it must be a directory.
|
|
// 2 - The attribute type code must be $INDEX_ALLOCATION with no attribute name.
|
|
// 3 - The user isn't trying to open the volume.
|
|
//
|
|
|
|
if (Indexed) {
|
|
|
|
if ((AttrType != $INDEX_ALLOCATION)
|
|
|| (AttrName.Length != 0)) {
|
|
|
|
DebugTrace( -1, Dbg, ("NtfsCheckValidAttributeAccess: Conflict in directory\n") );
|
|
return STATUS_NOT_A_DIRECTORY;
|
|
|
|
//
|
|
// If there is a current file and it is not a directory and
|
|
// the caller wanted to perform a create. We return
|
|
// STATUS_OBJECT_NAME_COLLISION, otherwise we return STATUS_NOT_A_DIRECTORY.
|
|
//
|
|
|
|
} else if (ARGUMENT_PRESENT( Info ) && !IsDirectory( Info )) {
|
|
|
|
if (((IrpSp->Parameters.Create.Options >> 24) & 0x000000ff) == FILE_CREATE) {
|
|
|
|
return STATUS_OBJECT_NAME_COLLISION;
|
|
|
|
} else {
|
|
|
|
return STATUS_NOT_A_DIRECTORY;
|
|
}
|
|
}
|
|
|
|
SetFlag( *CcbFlags, CCB_FLAG_OPEN_AS_FILE );
|
|
|
|
//
|
|
// If the user specified a non-directory that means he is opening a non-indexed
|
|
// attribute. We check for the following condition.
|
|
//
|
|
// 1 - Only the unnamed data attribute may be opened for a volume.
|
|
// 2 - We can't be opening an unnamed $INDEX_ALLOCATION attribute.
|
|
//
|
|
|
|
} else {
|
|
|
|
//
|
|
// Now determine if we are opening the entire file.
|
|
//
|
|
|
|
if ((AttrType == $DATA)
|
|
&& (AttrName.Length == 0)) {
|
|
|
|
SetFlag( *CcbFlags, CCB_FLAG_OPEN_AS_FILE );
|
|
}
|
|
|
|
if (ARGUMENT_PRESENT( Info ) &&
|
|
IsDirectory( Info ) &&
|
|
FlagOn( *CcbFlags, CCB_FLAG_OPEN_AS_FILE )) {
|
|
|
|
DebugTrace( -1, Dbg, ("NtfsCheckValidAttributeAccess: Can't open directory as file\n") );
|
|
return STATUS_FILE_IS_A_DIRECTORY;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If we make it this far, lets check that we will allow access to
|
|
// the attribute specified. Typically we only allow the user to
|
|
// access non system files. Also only the Data attributes and
|
|
// attributes created by the user may be opened. We will protect
|
|
// these with boolean flags to allow the developers to enable
|
|
// reading any attributes.
|
|
//
|
|
|
|
if (NtfsProtectSystemAttributes) {
|
|
|
|
if ((AttrType < $FIRST_USER_DEFINED_ATTRIBUTE) &&
|
|
!NtfsIsTypeCodeUserData( AttrType ) &&
|
|
((AttrType != $INDEX_ALLOCATION) || !Indexed)) {
|
|
|
|
DebugTrace( -1, Dbg, ("NtfsCheckValidAttributeAccess: System attribute code\n") );
|
|
return STATUS_ACCESS_DENIED;
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Now check if the trailing backslash is compatible with the
|
|
// file being opened.
|
|
//
|
|
|
|
if (TrailingBackslash) {
|
|
|
|
if (!Indexed ||
|
|
FlagOn( IrpSp->Parameters.Create.Options, FILE_NON_DIRECTORY_FILE )) {
|
|
|
|
return STATUS_OBJECT_NAME_INVALID;
|
|
|
|
} else {
|
|
|
|
Indexed = TRUE;
|
|
AttrType = $INDEX_ALLOCATION;
|
|
}
|
|
}
|
|
|
|
*IndexedAttribute = Indexed;
|
|
*AttrTypeCode = AttrType;
|
|
|
|
DebugTrace( -1, Dbg, ("NtfsCheckValidAttributeAccess: Exit\n") );
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
//
|
|
// Local support routine.
|
|
//
|
|
|
|
NTSTATUS
|
|
NtfsOpenAttributeCheck (
|
|
IN PIRP_CONTEXT IrpContext,
|
|
IN PIRP Irp,
|
|
IN PIO_STACK_LOCATION IrpSp,
|
|
OUT PSCB *ThisScb,
|
|
OUT PSHARE_MODIFICATION_TYPE ShareModificationType
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is a general routine which checks if an existing
|
|
non-indexed attribute may be opened. It considers only the oplock
|
|
state of the file and the current share access. In the course of
|
|
performing these checks, the Scb for the attribute may be
|
|
created and the share modification for the actual OpenAttribute
|
|
call is determined.
|
|
|
|
Arguments:
|
|
|
|
Irp - This is the Irp for this open operation.
|
|
|
|
IrpSp - This is the stack location for this open.
|
|
|
|
ThisScb - Address to store the Scb if found or created.
|
|
|
|
ShareModificationType - Address to store the share modification type
|
|
for a subsequent OpenAttribute call.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - The result of opening this indexed attribute.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
BOOLEAN DeleteOnClose;
|
|
|
|
PAGED_CODE();
|
|
|
|
DebugTrace( +1, Dbg, ("NtfsOpenAttributeCheck: Entered\n") );
|
|
|
|
//
|
|
// We should already have the Scb for this file.
|
|
//
|
|
|
|
ASSERT_SCB( *ThisScb );
|
|
|
|
//
|
|
// If there are other opens on this file, we need to check the share
|
|
// access before we check the oplocks. We remember that
|
|
// we did the share access check by simply updating the share
|
|
// access we open the attribute.
|
|
//
|
|
|
|
if ((*ThisScb)->CleanupCount != 0) {
|
|
|
|
//
|
|
// We check the share access for this file without updating it.
|
|
//
|
|
|
|
Status = IoCheckShareAccess( IrpSp->Parameters.Create.SecurityContext->AccessState->PreviouslyGrantedAccess,
|
|
IrpSp->Parameters.Create.ShareAccess,
|
|
IrpSp->FileObject,
|
|
&(*ThisScb)->ShareAccess,
|
|
FALSE );
|
|
|
|
if (!NT_SUCCESS( Status )) {
|
|
|
|
DebugTrace( -1, Dbg, ("NtfsOpenAttributeCheck: Exit -> %08lx\n", Status) );
|
|
return Status;
|
|
}
|
|
|
|
DebugTrace( 0, Dbg, ("Check oplock state of existing Scb\n") );
|
|
|
|
if (SafeNodeType( *ThisScb ) == NTFS_NTC_SCB_DATA) {
|
|
|
|
//
|
|
// If the handle count is greater than 1 then fail this
|
|
// open now if the caller wants a filter oplock.
|
|
//
|
|
|
|
if (FlagOn( IrpSp->Parameters.Create.Options, FILE_RESERVE_OPFILTER ) &&
|
|
((*ThisScb)->CleanupCount > 1)) {
|
|
|
|
NtfsRaiseStatus( IrpContext, STATUS_OPLOCK_NOT_GRANTED, NULL, NULL );
|
|
}
|
|
|
|
Status = FsRtlCheckOplock( &(*ThisScb)->ScbType.Data.Oplock,
|
|
Irp,
|
|
IrpContext,
|
|
NtfsOplockComplete,
|
|
NtfsOplockPrePostIrp );
|
|
|
|
//
|
|
// Update the FastIoField.
|
|
//
|
|
|
|
NtfsAcquireFsrtlHeader( *ThisScb );
|
|
(*ThisScb)->Header.IsFastIoPossible = NtfsIsFastIoPossible( *ThisScb );
|
|
NtfsReleaseFsrtlHeader( *ThisScb );
|
|
|
|
//
|
|
// If the return value isn't success or oplock break in progress
|
|
// the irp has been posted. We return right now.
|
|
//
|
|
|
|
if (Status == STATUS_PENDING) {
|
|
|
|
DebugTrace( 0, Dbg, ("Irp posted through oplock routine\n") );
|
|
|
|
DebugTrace( -1, Dbg, ("NtfsOpenAttributeCheck: Exit -> %08lx\n", Status) );
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
*ShareModificationType = UpdateShareAccess;
|
|
|
|
//
|
|
// If the unclean count in the Fcb is 0, we will simply set the
|
|
// share access.
|
|
//
|
|
|
|
} else {
|
|
|
|
*ShareModificationType = SetShareAccess;
|
|
}
|
|
|
|
//
|
|
// If the user wants write access access to the file make sure there
|
|
// is 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.
|
|
//
|
|
|
|
DeleteOnClose = BooleanFlagOn( IrpSp->Parameters.Create.Options,
|
|
FILE_DELETE_ON_CLOSE );
|
|
|
|
if (FlagOn( IrpSp->Parameters.Create.SecurityContext->DesiredAccess,
|
|
FILE_WRITE_DATA )
|
|
|| DeleteOnClose) {
|
|
|
|
//
|
|
// Use a try-finally to decrement the open count. This is a little
|
|
// bit of trickery to keep the scb around while we are doing the
|
|
// flush call.
|
|
//
|
|
|
|
InterlockedIncrement( &(*ThisScb)->CloseCount );
|
|
|
|
try {
|
|
|
|
//
|
|
// If there is an image section then we better have the file
|
|
// exclusively.
|
|
//
|
|
|
|
if ((*ThisScb)->NonpagedScb->SegmentObject.ImageSectionObject != NULL) {
|
|
|
|
if (!MmFlushImageSection( &(*ThisScb)->NonpagedScb->SegmentObject,
|
|
MmFlushForWrite )) {
|
|
|
|
DebugTrace( 0, Dbg, ("Couldn't flush image section\n") );
|
|
|
|
Status = DeleteOnClose ? STATUS_CANNOT_DELETE :
|
|
STATUS_SHARING_VIOLATION;
|
|
}
|
|
}
|
|
|
|
} finally {
|
|
|
|
InterlockedDecrement( &(*ThisScb)->CloseCount );
|
|
}
|
|
}
|
|
|
|
DebugTrace( -1, Dbg, ("NtfsOpenAttributeCheck: Exit -> %08lx\n", Status) );
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
//
|
|
// Local support routine.
|
|
//
|
|
|
|
VOID
|
|
NtfsAddEa (
|
|
IN PIRP_CONTEXT IrpContext,
|
|
IN PVCB Vcb,
|
|
IN PFCB ThisFcb,
|
|
IN PFILE_FULL_EA_INFORMATION EaBuffer OPTIONAL,
|
|
IN ULONG EaLength,
|
|
OUT PIO_STATUS_BLOCK Iosb
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will add an ea set to the file. It writes the attributes
|
|
to disk and updates the Fcb info structure with the packed ea size.
|
|
|
|
Arguments:
|
|
|
|
Vcb - This is the volume being opened.
|
|
|
|
ThisFcb - This is the Fcb for the file being opened.
|
|
|
|
EaBuffer - This is the buffer passed by the user.
|
|
|
|
EaLength - This is the stated length of the buffer.
|
|
|
|
Iosb - This is the Status Block to use to fill in the offset of an
|
|
offending Ea.
|
|
|
|
Return Value:
|
|
|
|
None - This routine will raise on error.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
EA_LIST_HEADER EaList;
|
|
|
|
PAGED_CODE();
|
|
|
|
DebugTrace( +1, Dbg, ("NtfsAddEa: Entered\n") );
|
|
|
|
//
|
|
// Use a try-finally to facilitate cleanup.
|
|
//
|
|
|
|
try {
|
|
|
|
//
|
|
// Initialize the EaList header.
|
|
//
|
|
|
|
EaList.PackedEaSize = 0;
|
|
EaList.NeedEaCount = 0;
|
|
EaList.UnpackedEaSize = 0;
|
|
EaList.BufferSize = 0;
|
|
EaList.FullEa = NULL;
|
|
|
|
if (ARGUMENT_PRESENT( EaBuffer )) {
|
|
|
|
//
|
|
// Check the user's buffer for validity.
|
|
//
|
|
|
|
Status = IoCheckEaBufferValidity( EaBuffer,
|
|
EaLength,
|
|
&Iosb->Information );
|
|
|
|
if (!NT_SUCCESS( Status )) {
|
|
|
|
DebugTrace( -1, Dbg, ("NtfsAddEa: Invalid ea list\n") );
|
|
NtfsRaiseStatus( IrpContext, Status, NULL, NULL );
|
|
}
|
|
|
|
//
|
|
// **** Maybe this routine should raise.
|
|
//
|
|
|
|
Status = NtfsBuildEaList( IrpContext,
|
|
Vcb,
|
|
&EaList,
|
|
EaBuffer,
|
|
&Iosb->Information );
|
|
|
|
if (!NT_SUCCESS( Status )) {
|
|
|
|
DebugTrace( -1, Dbg, ("NtfsAddEa: Couldn't build Ea list\n") );
|
|
NtfsRaiseStatus( IrpContext, Status, NULL, NULL );
|
|
}
|
|
}
|
|
|
|
//
|
|
// Now replace the existing EAs.
|
|
//
|
|
|
|
NtfsReplaceFileEas( IrpContext, ThisFcb, &EaList );
|
|
|
|
} finally {
|
|
|
|
DebugUnwind( NtfsAddEa );
|
|
|
|
//
|
|
// Free the in-memory copy of the Eas.
|
|
//
|
|
|
|
if (EaList.FullEa != NULL) {
|
|
|
|
NtfsFreePool( EaList.FullEa );
|
|
}
|
|
|
|
DebugTrace( -1, Dbg, ("NtfsAddEa: Exit -> %08lx\n", Status) );
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
//
|
|
// Local support routine.
|
|
//
|
|
|
|
VOID
|
|
NtfsInitializeFcbAndStdInfo (
|
|
IN PIRP_CONTEXT IrpContext,
|
|
IN PFCB ThisFcb,
|
|
IN BOOLEAN Directory,
|
|
IN BOOLEAN Compressed,
|
|
IN ULONG FileAttributes,
|
|
IN PLONGLONG SetCreationTime OPTIONAL
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will initialize an Fcb for a newly created file and create
|
|
the standard information attribute on disk. We assume that some information
|
|
may already have been placed in the Fcb so we don't zero it out. We will
|
|
initialize the allocation size to zero, but that may be changed later in
|
|
the create process.
|
|
|
|
Arguments:
|
|
|
|
ThisFcb - This is the Fcb for the file being opened.
|
|
|
|
Directory - Indicates if this is a directory file.
|
|
|
|
Compressed - Indicates if this is a compressed file.
|
|
|
|
FileAttributes - These are the attributes the user wants to attach to
|
|
the file. We will just clear any unsupported bits.
|
|
|
|
SetCreationTime - Optionally force the creation time to a given value
|
|
|
|
Return Value:
|
|
|
|
None - This routine will raise on error.
|
|
|
|
--*/
|
|
|
|
{
|
|
STANDARD_INFORMATION StandardInformation;
|
|
ATTRIBUTE_ENUMERATION_CONTEXT AttrContext;
|
|
|
|
PAGED_CODE();
|
|
|
|
DebugTrace( +1, Dbg, ("NtfsInitializeFcbAndStdInfo: Entered\n") );
|
|
|
|
NtfsInitializeAttributeContext( &AttrContext );
|
|
|
|
//
|
|
// Use a try-finally to facilitate cleanup.
|
|
//
|
|
|
|
try {
|
|
|
|
//
|
|
// Mask out the invalid bits of the file atributes. Then set the
|
|
// file name index bit if this is a directory.
|
|
//
|
|
|
|
if (!Directory) {
|
|
|
|
SetFlag( FileAttributes, FILE_ATTRIBUTE_ARCHIVE );
|
|
}
|
|
|
|
ClearFlag( FileAttributes,
|
|
~(FILE_ATTRIBUTE_READONLY |
|
|
FILE_ATTRIBUTE_HIDDEN |
|
|
FILE_ATTRIBUTE_SYSTEM |
|
|
FILE_ATTRIBUTE_TEMPORARY |
|
|
FILE_ATTRIBUTE_ARCHIVE) );
|
|
|
|
if (Directory) {
|
|
|
|
SetFlag( FileAttributes, DUP_FILE_NAME_INDEX_PRESENT );
|
|
}
|
|
|
|
if (Compressed) {
|
|
|
|
SetFlag( FileAttributes, FILE_ATTRIBUTE_COMPRESSED );
|
|
}
|
|
|
|
ThisFcb->Info.FileAttributes = FileAttributes;
|
|
|
|
//
|
|
// Fill in the rest of the Fcb Info structure.
|
|
//
|
|
|
|
if (SetCreationTime == NULL) {
|
|
|
|
NtfsGetCurrentTime( IrpContext, ThisFcb->Info.CreationTime );
|
|
|
|
ThisFcb->Info.LastModificationTime = ThisFcb->Info.CreationTime;
|
|
ThisFcb->Info.LastChangeTime = ThisFcb->Info.CreationTime;
|
|
ThisFcb->Info.LastAccessTime = ThisFcb->Info.CreationTime;
|
|
|
|
ThisFcb->CurrentLastAccess = ThisFcb->Info.CreationTime;
|
|
|
|
} else {
|
|
|
|
ThisFcb->Info.CreationTime = *SetCreationTime;
|
|
|
|
NtfsGetCurrentTime( IrpContext, ThisFcb->Info.LastModificationTime );
|
|
|
|
ThisFcb->Info.LastChangeTime = ThisFcb->Info.LastModificationTime;
|
|
ThisFcb->Info.LastAccessTime = ThisFcb->Info.LastModificationTime;
|
|
|
|
ThisFcb->CurrentLastAccess = ThisFcb->Info.LastModificationTime;
|
|
}
|
|
|
|
//
|
|
// We assume these sizes are zero.
|
|
//
|
|
|
|
ThisFcb->Info.AllocatedLength = 0;
|
|
ThisFcb->Info.FileSize = 0;
|
|
|
|
//
|
|
// Copy the standard information fields from the Fcb and create the
|
|
// attribute.
|
|
//
|
|
|
|
RtlZeroMemory( &StandardInformation, sizeof( STANDARD_INFORMATION ));
|
|
|
|
StandardInformation.CreationTime = ThisFcb->Info.CreationTime;
|
|
StandardInformation.LastModificationTime = ThisFcb->Info.LastModificationTime;
|
|
StandardInformation.LastChangeTime = ThisFcb->Info.LastChangeTime;
|
|
StandardInformation.LastAccessTime = ThisFcb->Info.LastAccessTime;
|
|
|
|
StandardInformation.FileAttributes = ThisFcb->Info.FileAttributes;
|
|
|
|
#ifdef _CAIRO_
|
|
StandardInformation.ClassId = ThisFcb->ClassId;
|
|
StandardInformation.OwnerId = ThisFcb->OwnerId;
|
|
StandardInformation.SecurityId = ThisFcb->SecurityId;
|
|
StandardInformation.Usn = ThisFcb->Usn;
|
|
|
|
SetFlag(ThisFcb->FcbState, FCB_STATE_LARGE_STD_INFO);
|
|
#endif
|
|
|
|
NtfsCreateAttributeWithValue( IrpContext,
|
|
ThisFcb,
|
|
$STANDARD_INFORMATION,
|
|
NULL,
|
|
&StandardInformation,
|
|
sizeof( STANDARD_INFORMATION ),
|
|
0,
|
|
NULL,
|
|
FALSE,
|
|
&AttrContext );
|
|
|
|
//
|
|
// We know that the open call will generate a single link.
|
|
// (Remember that a separate 8.3 name is not considered a link)
|
|
//
|
|
|
|
ThisFcb->LinkCount =
|
|
ThisFcb->TotalLinks = 1;
|
|
|
|
//
|
|
// Now set the header initialized flag in the Fcb.
|
|
//
|
|
|
|
SetFlag( ThisFcb->FcbState, FCB_STATE_DUP_INITIALIZED );
|
|
|
|
} finally {
|
|
|
|
DebugUnwind( NtfsInitializeFcbAndStdInfo );
|
|
|
|
NtfsCleanupAttributeContext( &AttrContext );
|
|
|
|
DebugTrace( -1, Dbg, ("NtfsInitializeFcbAndStdInfo: Exit\n") );
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
//
|
|
// Local support routine.
|
|
//
|
|
|
|
VOID
|
|
NtfsCreateAttribute (
|
|
IN PIRP_CONTEXT IrpContext,
|
|
IN PIO_STACK_LOCATION IrpSp,
|
|
IN OUT PFCB ThisFcb,
|
|
IN OUT PSCB ThisScb,
|
|
IN PLCB ThisLcb,
|
|
IN LONGLONG AllocationSize,
|
|
IN BOOLEAN LogIt
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called to create an attribute of a given size on the
|
|
disk. This path will only create non-resident attributes unless the
|
|
allocation size is zero.
|
|
|
|
The Scb will contain the attribute name and type code on entry.
|
|
|
|
Arguments:
|
|
|
|
IrpSp - Stack location in the Irp for this request.
|
|
|
|
ThisFcb - This is the Fcb for the file to create the attribute in.
|
|
|
|
ThisScb - This is the Scb for the attribute to create.
|
|
|
|
ThisLcb - This is the Lcb for propagating compression parameters
|
|
|
|
AllocationSize - This is the size of the attribute to create.
|
|
|
|
LogIt - Indicates whether we should log the creation of the attribute.
|
|
Also indicates if this is a create file operation.
|
|
|
|
Return Value:
|
|
|
|
None - This routine will raise on error.
|
|
|
|
--*/
|
|
|
|
{
|
|
ATTRIBUTE_ENUMERATION_CONTEXT AttrContext;
|
|
PATTRIBUTE_RECORD_HEADER ThisAttribute = NULL;
|
|
|
|
USHORT AttributeFlags = 0;
|
|
|
|
PAGED_CODE();
|
|
|
|
DebugTrace( +1, Dbg, ("NtfsCreateAttribute: Entered\n") );
|
|
|
|
NtfsInitializeAttributeContext( &AttrContext );
|
|
|
|
//
|
|
// Use a try-finally to facilitate cleanup.
|
|
//
|
|
|
|
try {
|
|
|
|
if (!FlagOn( ThisFcb->FcbState, FCB_STATE_PAGING_FILE ) &&
|
|
!FlagOn( IrpSp->Parameters.Create.Options, FILE_NO_COMPRESSION )) {
|
|
|
|
//
|
|
// If this is the root directory then use the Scb from the Vcb.
|
|
//
|
|
|
|
if (ThisLcb == ThisFcb->Vcb->RootLcb) {
|
|
|
|
AttributeFlags = (USHORT)(ThisFcb->Vcb->RootIndexScb->AttributeFlags & ATTRIBUTE_FLAG_COMPRESSION_MASK);
|
|
|
|
} else {
|
|
|
|
AttributeFlags = (USHORT)(ThisLcb->Scb->AttributeFlags & ATTRIBUTE_FLAG_COMPRESSION_MASK);
|
|
}
|
|
}
|
|
|
|
#ifdef _CAIRO_
|
|
|
|
if (NtfsPerformQuotaOperation( ThisFcb) &&
|
|
FlagOn( ThisScb->ScbState, SCB_STATE_SUBJECT_TO_QUOTA )) {
|
|
|
|
ASSERT( NtfsIsTypeCodeSubjectToQuota( ThisScb->AttributeTypeCode ));
|
|
|
|
//
|
|
// Since this is a new stream indicate quota is set to
|
|
// allocation size.
|
|
//
|
|
|
|
SetFlag( ThisScb->ScbState, SCB_STATE_QUOTA_ENLARGED );
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// We lookup that attribute again and it better not be there.
|
|
// We need the file record in order to know whether the attribute
|
|
// is resident or not.
|
|
//
|
|
|
|
if (AllocationSize != 0) {
|
|
|
|
DebugTrace( 0, Dbg, ("Create non-resident attribute\n") );
|
|
|
|
if (!NtfsAllocateAttribute( IrpContext,
|
|
ThisScb,
|
|
ThisScb->AttributeTypeCode,
|
|
(ThisScb->AttributeName.Length != 0
|
|
? &ThisScb->AttributeName
|
|
: NULL),
|
|
AttributeFlags,
|
|
FALSE,
|
|
LogIt,
|
|
AllocationSize,
|
|
NULL )) {
|
|
|
|
SetFlag( IrpContext->Flags, IRP_CONTEXT_LARGE_ALLOCATION );
|
|
}
|
|
|
|
SetFlag( ThisScb->ScbState, SCB_STATE_TRUNCATE_ON_CLOSE );
|
|
|
|
} else {
|
|
|
|
#ifdef _CAIRO_
|
|
|
|
//
|
|
// Update the quota if this is a user stream.
|
|
//
|
|
|
|
if ( FlagOn( ThisScb->ScbState, SCB_STATE_SUBJECT_TO_QUOTA )) {
|
|
|
|
LONGLONG Delta = NtfsResidentStreamQuota( ThisFcb->Vcb );
|
|
|
|
NtfsConditionallyUpdateQuota( IrpContext,
|
|
ThisFcb,
|
|
&Delta,
|
|
LogIt,
|
|
TRUE );
|
|
}
|
|
|
|
#endif // _CAIRO_
|
|
|
|
NtfsCreateAttributeWithValue( IrpContext,
|
|
ThisFcb,
|
|
ThisScb->AttributeTypeCode,
|
|
&ThisScb->AttributeName,
|
|
NULL,
|
|
(ULONG) AllocationSize,
|
|
AttributeFlags,
|
|
NULL,
|
|
LogIt,
|
|
&AttrContext );
|
|
|
|
ThisAttribute = NtfsFoundAttribute( &AttrContext );
|
|
|
|
}
|
|
|
|
//
|
|
// Clear the header initialized bit and read the sizes from the
|
|
// disk.
|
|
//
|
|
|
|
ClearFlag( ThisScb->ScbState, SCB_STATE_HEADER_INITIALIZED );
|
|
NtfsUpdateScbFromAttribute( IrpContext,
|
|
ThisScb,
|
|
ThisAttribute );
|
|
|
|
#if 0 // _CAIRO_
|
|
ASSERT( !NtfsPerformQuotaOperation( ThisFcb ) || NtfsCalculateQuotaAdjustment( IrpContext, ThisFcb ) == 0 );
|
|
#endif // _CAIRO_
|
|
|
|
} finally {
|
|
|
|
DebugUnwind( NtfsCreateAttribute );
|
|
|
|
NtfsCleanupAttributeContext( &AttrContext );
|
|
|
|
DebugTrace( +1, Dbg, ("NtfsCreateAttribute: Exit\n") );
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
//
|
|
// Local support routine
|
|
//
|
|
|
|
VOID
|
|
NtfsRemoveDataAttributes (
|
|
IN PIRP_CONTEXT IrpContext,
|
|
IN PFCB ThisFcb,
|
|
IN PLCB ThisLcb OPTIONAL,
|
|
IN PFILE_OBJECT FileObject,
|
|
IN ULONG LastFileNameOffset,
|
|
IN BOOLEAN OpenById
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called to remove (or mark for delete) all of the named
|
|
data or property set attributes on a file. This is done during an overwrite
|
|
or supersede operation.
|
|
|
|
Arguments:
|
|
|
|
Context - Pointer to the IrpContext to be queued to the Fsp
|
|
|
|
ThisFcb - This is the Fcb for the file in question.
|
|
|
|
ThisLcb - This is the Lcb used to reach this Fcb (if specified).
|
|
|
|
FileObject - This is the file object for the file.
|
|
|
|
LastFileNameOffset - This is the offset of the file in the full name.
|
|
|
|
OpenById - Indicates if this open is being performed by file id.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
ATTRIBUTE_ENUMERATION_CONTEXT Context;
|
|
PATTRIBUTE_RECORD_HEADER Attribute;
|
|
ATTRIBUTE_TYPE_CODE TypeCode = $DATA;
|
|
|
|
UNICODE_STRING AttributeName;
|
|
PSCB ThisScb;
|
|
|
|
BOOLEAN MoreToGo;
|
|
|
|
ASSERT_EXCLUSIVE_FCB( ThisFcb );
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Use a try-finally to facilitate cleanup.
|
|
//
|
|
|
|
try {
|
|
|
|
SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_QUOTA_DISABLE );
|
|
|
|
while (TRUE) {
|
|
|
|
NtfsInitializeAttributeContext( &Context );
|
|
|
|
//
|
|
// Enumerate all of the attributes with the matching type code
|
|
//
|
|
|
|
MoreToGo = NtfsLookupAttributeByCode( IrpContext,
|
|
ThisFcb,
|
|
&ThisFcb->FileReference,
|
|
TypeCode,
|
|
&Context );
|
|
|
|
while (MoreToGo) {
|
|
|
|
//
|
|
// Point to the current attribute.
|
|
//
|
|
|
|
Attribute = NtfsFoundAttribute( &Context );
|
|
|
|
//
|
|
// We only look at named data attributes.
|
|
//
|
|
|
|
if (Attribute->NameLength != 0) {
|
|
|
|
//
|
|
// Construct the name and find the Scb for the attribute.
|
|
//
|
|
|
|
AttributeName.Buffer = (PWSTR) Add2Ptr( Attribute, Attribute->NameOffset );
|
|
AttributeName.MaximumLength = AttributeName.Length = Attribute->NameLength * sizeof( WCHAR );
|
|
|
|
ThisScb = NtfsCreateScb( IrpContext,
|
|
ThisFcb,
|
|
TypeCode,
|
|
&AttributeName,
|
|
FALSE,
|
|
NULL );
|
|
|
|
//
|
|
// If there is an open handle on this file, we simply mark
|
|
// the Scb as delete pending.
|
|
//
|
|
|
|
if (ThisScb->CleanupCount != 0) {
|
|
|
|
SetFlag( ThisScb->ScbState, SCB_STATE_DELETE_ON_CLOSE );
|
|
|
|
//
|
|
// Otherwise we remove the attribute and mark the Scb as
|
|
// deleted. The Scb will be cleaned up when the Fcb is
|
|
// cleaned up.
|
|
//
|
|
|
|
} else {
|
|
|
|
#ifdef _CAIRO_
|
|
LONGLONG Delta;
|
|
|
|
ASSERT( !FlagOn( ThisScb->ScbState, SCB_STATE_QUOTA_ENLARGED ));
|
|
|
|
if (NtfsPerformQuotaOperation(ThisFcb)) {
|
|
|
|
if (NtfsIsAttributeResident( Attribute )) {
|
|
Delta = -(LONG) Attribute->Form.Resident.ValueLength;
|
|
} else {
|
|
Delta = -(LONGLONG) Attribute->Form.Nonresident.FileSize;
|
|
}
|
|
|
|
NtfsUpdateFileQuota( IrpContext,
|
|
ThisFcb,
|
|
&Delta,
|
|
TRUE,
|
|
FALSE );
|
|
|
|
}
|
|
#endif
|
|
|
|
NtfsDeleteAttributeRecord( IrpContext,
|
|
ThisFcb,
|
|
TRUE,
|
|
FALSE,
|
|
&Context );
|
|
|
|
SetFlag( ThisScb->ScbState, SCB_STATE_ATTRIBUTE_DELETED );
|
|
|
|
//
|
|
// If this is a named stream, then report this to the dir notify
|
|
// package.
|
|
//
|
|
|
|
if (!OpenById &&
|
|
(ThisScb->Vcb->NotifyCount != 0) &&
|
|
(ThisScb->AttributeName.Length != 0) &&
|
|
(ThisScb->AttributeTypeCode == TypeCode)) {
|
|
|
|
NtfsReportDirNotify( IrpContext,
|
|
ThisFcb->Vcb,
|
|
&FileObject->FileName,
|
|
LastFileNameOffset,
|
|
&ThisScb->AttributeName,
|
|
((ARGUMENT_PRESENT( ThisLcb ) &&
|
|
ThisLcb->Scb->ScbType.Index.NormalizedName.Buffer != NULL) ?
|
|
&ThisLcb->Scb->ScbType.Index.NormalizedName :
|
|
NULL),
|
|
FILE_NOTIFY_CHANGE_STREAM_NAME,
|
|
FILE_ACTION_REMOVED_STREAM,
|
|
NULL );
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Get the next attribute.
|
|
//
|
|
|
|
MoreToGo = NtfsLookupNextAttributeByCode( IrpContext,
|
|
ThisFcb,
|
|
TypeCode,
|
|
&Context );
|
|
}
|
|
|
|
//
|
|
// We've deleted one set of attributes. Check to see if
|
|
// we're done deleting or whether we need to start deleting
|
|
// another type code.
|
|
//
|
|
|
|
#ifndef _CAIRO_
|
|
break;
|
|
#else // _CAIRO_
|
|
if (TypeCode == $PROPERTY_SET) {
|
|
break;
|
|
} else {
|
|
|
|
NtfsCleanupAttributeContext( &Context );
|
|
TypeCode = $PROPERTY_SET;
|
|
|
|
}
|
|
#endif // _CAIRO_
|
|
}
|
|
|
|
} finally {
|
|
|
|
ClearFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_QUOTA_DISABLE );
|
|
NtfsCleanupAttributeContext( &Context );
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
//
|
|
// Local support routine.
|
|
//
|
|
|
|
VOID
|
|
NtfsReplaceAttribute (
|
|
IN PIRP_CONTEXT IrpContext,
|
|
IN PIO_STACK_LOCATION IrpSp,
|
|
IN PFCB ThisFcb,
|
|
IN PSCB ThisScb,
|
|
IN PLCB ThisLcb,
|
|
IN LONGLONG AllocationSize
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called to replace an existing attribute with
|
|
an attribute of the given allocation size. This routine will
|
|
handle the case whether the existing attribute is resident
|
|
or non-resident and the resulting attribute is resident or
|
|
non-resident.
|
|
|
|
There are two cases to consider. The first is the case where the
|
|
attribute is currently non-resident. In this case we will always
|
|
leave the attribute non-resident regardless of the new allocation
|
|
size. The argument being that the file will probably be used
|
|
as it was before. In this case we will add or delete allocation.
|
|
The second case is where the attribute is currently resident. In
|
|
This case we will remove the old attribute and add a new one.
|
|
|
|
Arguments:
|
|
|
|
IrpSp - This is the Irp stack location for this request.
|
|
|
|
ThisFcb - This is the Fcb for the file being opened.
|
|
|
|
ThisScb - This is the Scb for the given attribute.
|
|
|
|
ThisLcb - This is the Lcb via which this file is created. It
|
|
is used to propagate compression info.
|
|
|
|
AllocationSize - This is the new allocation size.
|
|
|
|
Return Value:
|
|
|
|
None. This routine will raise.
|
|
|
|
--*/
|
|
|
|
{
|
|
ATTRIBUTE_ENUMERATION_CONTEXT AttrContext;
|
|
|
|
PAGED_CODE();
|
|
|
|
DebugTrace( +1, Dbg, ("NtfsReplaceAttribute: Entered\n") );
|
|
|
|
NtfsInitializeAttributeContext( &AttrContext );
|
|
|
|
//
|
|
// Use a try-finally to facilitate cleanup.
|
|
//
|
|
|
|
try {
|
|
|
|
//
|
|
// Initialize the Scb if needed.
|
|
//
|
|
|
|
if (!FlagOn( ThisScb->ScbState, SCB_STATE_HEADER_INITIALIZED )) {
|
|
|
|
NtfsUpdateScbFromAttribute( IrpContext, ThisScb, NULL );
|
|
}
|
|
|
|
NtfsSnapshotScb( IrpContext, ThisScb );
|
|
|
|
//
|
|
// Expand quota to the expected state.
|
|
//
|
|
|
|
NtfsExpandQuotaToAllocationSize( IrpContext, ThisScb );
|
|
|
|
//
|
|
// If the attribute is resident, simply remove the old attribute and create
|
|
// a new one.
|
|
//
|
|
|
|
if (FlagOn( ThisScb->ScbState, SCB_STATE_ATTRIBUTE_RESIDENT )) {
|
|
|
|
//
|
|
// Find the attribute on the disk.
|
|
//
|
|
|
|
NtfsLookupAttributeForScb( IrpContext,
|
|
ThisScb,
|
|
NULL,
|
|
&AttrContext );
|
|
|
|
NtfsDeleteAttributeRecord( IrpContext,
|
|
ThisFcb,
|
|
TRUE,
|
|
FALSE,
|
|
&AttrContext );
|
|
|
|
//
|
|
// Set all the attribute sizes to zero.
|
|
//
|
|
|
|
ThisScb->ValidDataToDisk =
|
|
ThisScb->Header.AllocationSize.QuadPart =
|
|
ThisScb->Header.ValidDataLength.QuadPart =
|
|
ThisScb->Header.FileSize.QuadPart = 0;
|
|
ThisScb->TotalAllocated = 0;
|
|
|
|
//
|
|
// Create a stream file for the attribute in order to
|
|
// truncate the cache. Set the initialized bit in
|
|
// the Scb so we don't go to disk, but clear it afterwords.
|
|
//
|
|
|
|
NtfsCreateInternalAttributeStream( IrpContext, ThisScb, FALSE );
|
|
|
|
CcSetFileSizes( ThisScb->FileObject,
|
|
(PCC_FILE_SIZES)&ThisScb->Header.AllocationSize );
|
|
|
|
//
|
|
// Call our create attribute routine.
|
|
//
|
|
|
|
NtfsCreateAttribute( IrpContext,
|
|
IrpSp,
|
|
ThisFcb,
|
|
ThisScb,
|
|
ThisLcb,
|
|
AllocationSize,
|
|
TRUE );
|
|
|
|
//
|
|
// Otherwise the attribute will stay non-resident, we simply need to
|
|
// add or remove allocation.
|
|
//
|
|
|
|
} else {
|
|
|
|
//
|
|
// Create an internal attribute stream for the file.
|
|
//
|
|
|
|
NtfsCreateInternalAttributeStream( IrpContext,
|
|
ThisScb,
|
|
FALSE );
|
|
|
|
AllocationSize = LlClustersFromBytes( ThisScb->Vcb, AllocationSize );
|
|
AllocationSize = LlBytesFromClusters( ThisScb->Vcb, AllocationSize );
|
|
|
|
//
|
|
// Set the file size and valid data size to zero.
|
|
//
|
|
|
|
ThisScb->ValidDataToDisk = 0;
|
|
ThisScb->Header.ValidDataLength = Li0;
|
|
ThisScb->Header.FileSize = Li0;
|
|
|
|
DebugTrace( 0, Dbg, ("AllocationSize -> %016I64x\n", AllocationSize) );
|
|
|
|
//
|
|
// Write these changes to the file
|
|
//
|
|
|
|
//
|
|
// If the attribute is currently compressed then go ahead and discard
|
|
// all of the allocation.
|
|
//
|
|
|
|
if (FlagOn( ThisScb->AttributeFlags, ATTRIBUTE_FLAG_COMPRESSION_MASK )) {
|
|
|
|
NtfsDeleteAllocation( IrpContext,
|
|
ThisScb->FileObject,
|
|
ThisScb,
|
|
0,
|
|
MAXLONGLONG,
|
|
TRUE,
|
|
TRUE );
|
|
|
|
//
|
|
// Checkpoint the current transaction so we have these clusters
|
|
// available again.
|
|
//
|
|
|
|
NtfsCheckpointCurrentTransaction( IrpContext );
|
|
|
|
//
|
|
// If the user doesn't want this stream to be compressed then
|
|
// remove the entire stream and recreate it non-compressed.
|
|
//
|
|
|
|
if (FlagOn( ThisFcb->FcbState, FCB_STATE_PAGING_FILE ) ||
|
|
FlagOn( IrpSp->Parameters.Create.Options, FILE_NO_COMPRESSION )) {
|
|
|
|
NtfsLookupAttributeForScb( IrpContext,
|
|
ThisScb,
|
|
NULL,
|
|
&AttrContext );
|
|
|
|
NtfsDeleteAttributeRecord( IrpContext,
|
|
ThisFcb,
|
|
TRUE,
|
|
FALSE,
|
|
&AttrContext );
|
|
|
|
//
|
|
// Call our create attribute routine.
|
|
//
|
|
|
|
NtfsCreateAttribute( IrpContext,
|
|
IrpSp,
|
|
ThisFcb,
|
|
ThisScb,
|
|
ThisLcb,
|
|
AllocationSize,
|
|
TRUE );
|
|
}
|
|
}
|
|
|
|
//
|
|
// Now if the file allocation is being increased then we need to only add allocation
|
|
// to the attribute
|
|
//
|
|
|
|
if (ThisScb->Header.AllocationSize.QuadPart < AllocationSize) {
|
|
|
|
NtfsAddAllocation( IrpContext,
|
|
ThisScb->FileObject,
|
|
ThisScb,
|
|
LlClustersFromBytes( ThisScb->Vcb, ThisScb->Header.AllocationSize.QuadPart ),
|
|
LlClustersFromBytes( ThisScb->Vcb, AllocationSize - ThisScb->Header.AllocationSize.QuadPart ),
|
|
FALSE );
|
|
//
|
|
// Otherwise the allocation is being decreased so we need to delete some allocation
|
|
//
|
|
|
|
} else if (ThisScb->Header.AllocationSize.QuadPart > AllocationSize) {
|
|
|
|
NtfsDeleteAllocation( IrpContext,
|
|
ThisScb->FileObject,
|
|
ThisScb,
|
|
LlClustersFromBytes( ThisScb->Vcb, AllocationSize ),
|
|
MAXLONGLONG,
|
|
TRUE,
|
|
TRUE );
|
|
}
|
|
|
|
//
|
|
// We always unitialize the cache size to zero and write the new
|
|
// file size to disk.
|
|
//
|
|
|
|
NtfsWriteFileSizes( IrpContext,
|
|
ThisScb,
|
|
&ThisScb->Header.ValidDataLength.QuadPart,
|
|
FALSE,
|
|
TRUE );
|
|
|
|
NtfsCheckpointCurrentTransaction( IrpContext );
|
|
|
|
CcSetFileSizes( ThisScb->FileObject,
|
|
(PCC_FILE_SIZES)&ThisScb->Header.AllocationSize );
|
|
|
|
}
|
|
|
|
} finally {
|
|
|
|
DebugUnwind( NtfsReplaceAttribute );
|
|
|
|
NtfsCleanupAttributeContext( &AttrContext );
|
|
|
|
DebugTrace( -1, Dbg, ("NtfsReplaceAttribute: Exit\n") );
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
//
|
|
// Local support routine
|
|
//
|
|
|
|
NTSTATUS
|
|
NtfsOpenAttribute (
|
|
IN PIRP_CONTEXT IrpContext,
|
|
IN PIO_STACK_LOCATION IrpSp,
|
|
IN PVCB Vcb,
|
|
IN PLCB ThisLcb OPTIONAL,
|
|
IN PFCB ThisFcb,
|
|
IN ULONG LastFileNameOffset,
|
|
IN UNICODE_STRING AttrName,
|
|
IN ATTRIBUTE_TYPE_CODE AttrTypeCode,
|
|
IN SHARE_MODIFICATION_TYPE ShareModificationType,
|
|
IN TYPE_OF_OPEN TypeOfOpen,
|
|
IN ULONG CcbFlags,
|
|
IN PVOID NetworkInfo OPTIONAL,
|
|
IN OUT PSCB *ThisScb,
|
|
OUT PCCB *ThisCcb
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine does the work of creating the Scb and updating the
|
|
ShareAccess in the Fcb. It also initializes the Scb if neccessary
|
|
and creates Ccb. Its final job is to set the file object type of
|
|
open.
|
|
|
|
Arguments:
|
|
|
|
IrpSp - This is the stack location for this volume. We use it to get the
|
|
file object, granted access and share access for this open.
|
|
|
|
Vcb - Vcb for this volume.
|
|
|
|
ThisLcb - This is the Lcb to the Fcb for the file being opened. Not present
|
|
if this is an open by id.
|
|
|
|
ThisFcb - This is the Fcb for this file.
|
|
|
|
LastFileNameOffset - This is the offset in the full path of the final component.
|
|
|
|
AttrName - This is the attribute name to open.
|
|
|
|
AttrTypeCode - This is the type code for the attribute being opened.
|
|
|
|
ShareModificationType - This indicates how we should modify the
|
|
current share modification on the Fcb.
|
|
|
|
TypeOfOpen - This indicates how this attribute is being opened.
|
|
|
|
CcbFlags - This is the flag field for the Ccb.
|
|
|
|
NetworkInfo - If specified then this open is on behalf of a fast query
|
|
and we don't want to increment the counts or modify the share
|
|
access on the file.
|
|
|
|
ThisScb - If this points to a non-NULL value, it is the Scb to use. Otherwise we
|
|
store the Scb we create here.
|
|
|
|
ThisCcb - Address to store address of created Ccb.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Indicating the outcome of opening this attribute.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
BOOLEAN RemoveShareAccess = FALSE;
|
|
ACCESS_MASK GrantedAccess;
|
|
|
|
PAGED_CODE();
|
|
|
|
DebugTrace( +1, Dbg, ("NtfsOpenAttribute: Entered\n") );
|
|
|
|
//
|
|
// Use a try-finally to facilitate cleanup.
|
|
//
|
|
|
|
try {
|
|
|
|
//
|
|
// Remember the granted access.
|
|
//
|
|
|
|
GrantedAccess = IrpSp->Parameters.Create.SecurityContext->AccessState->PreviouslyGrantedAccess;
|
|
|
|
//
|
|
// Create the Scb for this attribute if it doesn't exist.
|
|
//
|
|
|
|
if (*ThisScb == NULL) {
|
|
|
|
DebugTrace( 0, Dbg, ("Looking for Scb\n") );
|
|
|
|
*ThisScb = NtfsCreateScb( IrpContext,
|
|
ThisFcb,
|
|
AttrTypeCode,
|
|
&AttrName,
|
|
FALSE,
|
|
NULL );
|
|
}
|
|
|
|
DebugTrace( 0, Dbg, ("ThisScb -> %08lx\n", *ThisScb) );
|
|
DebugTrace( 0, Dbg, ("ThisLcb -> %08lx\n", ThisLcb) );
|
|
|
|
//
|
|
// If this Scb is delete pending, we return an error.
|
|
//
|
|
|
|
if (FlagOn( (*ThisScb)->ScbState, SCB_STATE_DELETE_ON_CLOSE )) {
|
|
|
|
DebugTrace( 0, Dbg, ("Scb delete is pending\n") );
|
|
|
|
Status = STATUS_DELETE_PENDING;
|
|
try_return( NOTHING );
|
|
}
|
|
|
|
//
|
|
// Skip all of the operations below if the user is doing a fast
|
|
// path open.
|
|
//
|
|
|
|
if (!ARGUMENT_PRESENT( NetworkInfo )) {
|
|
|
|
//
|
|
// If this caller wanted a filter oplock and the cleanup count
|
|
// is non-zero then fail the request.
|
|
//
|
|
|
|
if (FlagOn( IrpSp->Parameters.Create.Options, FILE_RESERVE_OPFILTER )) {
|
|
|
|
if (SafeNodeType( *ThisScb ) != NTFS_NTC_SCB_DATA) {
|
|
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
try_return( NOTHING );
|
|
|
|
//
|
|
// This must be the only open on the file and the requested
|
|
// access must be FILE_READ/WRITE_ATTRIBUTES and the
|
|
// share access must share with everyone.
|
|
//
|
|
|
|
} else if (((*ThisScb)->CleanupCount != 0) ||
|
|
(FlagOn( IrpSp->Parameters.Create.SecurityContext->DesiredAccess,
|
|
~(FILE_READ_ATTRIBUTES))) ||
|
|
((IrpSp->Parameters.Create.ShareAccess &
|
|
(FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE)) !=
|
|
(FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE))) {
|
|
|
|
Status = STATUS_OPLOCK_NOT_GRANTED;
|
|
try_return( NOTHING );
|
|
}
|
|
}
|
|
|
|
//
|
|
// Update the share access structure.
|
|
//
|
|
|
|
//
|
|
// Case on the requested share modification value.
|
|
//
|
|
|
|
switch (ShareModificationType) {
|
|
|
|
case UpdateShareAccess :
|
|
|
|
DebugTrace( 0, Dbg, ("Updating share access\n") );
|
|
|
|
IoUpdateShareAccess( IrpSp->FileObject,
|
|
&(*ThisScb)->ShareAccess );
|
|
break;
|
|
|
|
case SetShareAccess :
|
|
|
|
DebugTrace( 0, Dbg, ("Setting share access\n") );
|
|
|
|
//
|
|
// This case is when this is the first open for the file
|
|
// and we simply set the share access.
|
|
//
|
|
|
|
IoSetShareAccess( GrantedAccess,
|
|
IrpSp->Parameters.Create.ShareAccess,
|
|
IrpSp->FileObject,
|
|
&(*ThisScb)->ShareAccess );
|
|
break;
|
|
|
|
default:
|
|
|
|
DebugTrace( 0, Dbg, ("Checking share access\n") );
|
|
|
|
//
|
|
// For this case we need to check the share access and
|
|
// fail this request if access is denied.
|
|
//
|
|
|
|
if (!NT_SUCCESS( Status = IoCheckShareAccess( GrantedAccess,
|
|
IrpSp->Parameters.Create.ShareAccess,
|
|
IrpSp->FileObject,
|
|
&(*ThisScb)->ShareAccess,
|
|
TRUE ))) {
|
|
|
|
try_return( NOTHING );
|
|
}
|
|
}
|
|
|
|
RemoveShareAccess = TRUE;
|
|
|
|
//
|
|
// If this happens to be the first time we see write access on this
|
|
// Scb, then we need to remember it, and check if we have a disk full
|
|
// condition.
|
|
//
|
|
|
|
if (IrpSp->FileObject->WriteAccess &&
|
|
!FlagOn((*ThisScb)->ScbState, SCB_STATE_WRITE_ACCESS_SEEN) &&
|
|
(SafeNodeType( (*ThisScb) ) == NTFS_NTC_SCB_DATA)) {
|
|
|
|
NtfsAcquireReservedClusters( Vcb );
|
|
|
|
//
|
|
// Does this Scb have reserved space that causes us to exceed the free
|
|
// space on the volume?
|
|
//
|
|
|
|
if (((*ThisScb)->ScbType.Data.TotalReserved != 0) &&
|
|
((LlClustersFromBytes(Vcb, (*ThisScb)->ScbType.Data.TotalReserved) + Vcb->TotalReserved) >
|
|
Vcb->FreeClusters)) {
|
|
|
|
NtfsReleaseReservedClusters( Vcb );
|
|
|
|
try_return( Status = STATUS_DISK_FULL );
|
|
}
|
|
|
|
//
|
|
// Otherwise tally in the reserved space now for this Scb, and
|
|
// remember that we have seen write access.
|
|
//
|
|
|
|
Vcb->TotalReserved += LlClustersFromBytes(Vcb, (*ThisScb)->ScbType.Data.TotalReserved);
|
|
SetFlag( (*ThisScb)->ScbState, SCB_STATE_WRITE_ACCESS_SEEN );
|
|
|
|
NtfsReleaseReservedClusters( Vcb );
|
|
|
|
}
|
|
|
|
//
|
|
// Create the Ccb and put the remaining name in it.
|
|
//
|
|
|
|
*ThisCcb = NtfsCreateCcb( IrpContext,
|
|
ThisFcb,
|
|
(BOOLEAN) (AttrTypeCode == $INDEX_ALLOCATION),
|
|
ThisFcb->EaModificationCount,
|
|
CcbFlags,
|
|
IrpSp->FileObject->FileName,
|
|
LastFileNameOffset );
|
|
|
|
//
|
|
// Link the Ccb into the Lcb.
|
|
//
|
|
|
|
if (ARGUMENT_PRESENT( ThisLcb )) {
|
|
|
|
NtfsLinkCcbToLcb( IrpContext, *ThisCcb, ThisLcb );
|
|
}
|
|
|
|
//
|
|
// Update the Fcb delete counts if necessary.
|
|
//
|
|
|
|
if (RemoveShareAccess) {
|
|
|
|
//
|
|
// Update the count in the Fcb and store a flag in the Ccb
|
|
// if the user is not sharing the file for deletes. We only
|
|
// set these values if the user is accessing the file
|
|
// for read/write/delete access. The I/O system ignores
|
|
// the sharing mode unless the file is opened with one
|
|
// of these accesses.
|
|
//
|
|
|
|
if (FlagOn( GrantedAccess, NtfsAccessDataFlags )
|
|
&& !FlagOn( IrpSp->Parameters.Create.ShareAccess,
|
|
FILE_SHARE_DELETE )) {
|
|
|
|
ThisFcb->FcbDenyDelete += 1;
|
|
SetFlag( (*ThisCcb)->Flags, CCB_FLAG_DENY_DELETE );
|
|
}
|
|
|
|
//
|
|
// Do the same for the file delete count for any user
|
|
// who opened the file as a file and requested delete access.
|
|
//
|
|
|
|
if (FlagOn( (*ThisCcb)->Flags, CCB_FLAG_OPEN_AS_FILE )
|
|
&& FlagOn( GrantedAccess,
|
|
DELETE )) {
|
|
|
|
ThisFcb->FcbDeleteFile += 1;
|
|
SetFlag( (*ThisCcb)->Flags, CCB_FLAG_DELETE_FILE );
|
|
}
|
|
}
|
|
|
|
//
|
|
// Let our cleanup routine undo the share access change now.
|
|
//
|
|
|
|
RemoveShareAccess = FALSE;
|
|
|
|
//
|
|
// Increment the cleanup and close counts
|
|
//
|
|
|
|
NtfsIncrementCleanupCounts( *ThisScb,
|
|
ThisLcb,
|
|
BooleanFlagOn( IrpSp->FileObject->Flags, FO_NO_INTERMEDIATE_BUFFERING ));
|
|
|
|
NtfsIncrementCloseCounts( *ThisScb,
|
|
BooleanFlagOn( ThisFcb->FcbState, FCB_STATE_PAGING_FILE ),
|
|
(BOOLEAN) IsFileObjectReadOnly( IrpSp->FileObject ));
|
|
|
|
if (TypeOfOpen != UserDirectoryOpen) {
|
|
|
|
DebugTrace( 0, Dbg, ("Updating Vcb and File object for user open\n") );
|
|
|
|
//
|
|
// Set the section object pointer if this is a data Scb
|
|
//
|
|
|
|
IrpSp->FileObject->SectionObjectPointer = &(*ThisScb)->NonpagedScb->SegmentObject;
|
|
}
|
|
|
|
//
|
|
// Set the file object type.
|
|
//
|
|
|
|
NtfsSetFileObject( IrpSp->FileObject,
|
|
TypeOfOpen,
|
|
*ThisScb,
|
|
*ThisCcb );
|
|
|
|
//
|
|
// If this is a non-cached open and there is a data section and
|
|
// there are only non-cached opens then go ahead and try to
|
|
// delete the section.
|
|
//
|
|
|
|
if (FlagOn( IrpSp->FileObject->Flags, FO_NO_INTERMEDIATE_BUFFERING ) &&
|
|
((*ThisScb)->AttributeTypeCode == $DATA) &&
|
|
((*ThisScb)->CleanupCount == (*ThisScb)->NonCachedCleanupCount) &&
|
|
((*ThisScb)->NonpagedScb->SegmentObject.DataSectionObject != NULL) &&
|
|
((*ThisScb)->NonpagedScb->SegmentObject.ImageSectionObject == NULL) &&
|
|
((*ThisScb)->CompressionUnit == 0) &&
|
|
MmCanFileBeTruncated( &(*ThisScb)->NonpagedScb->SegmentObject, NULL )) {
|
|
|
|
//
|
|
// Only do this in the Fsp so we have enough stack space for the flush.
|
|
//
|
|
|
|
if (!FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_IN_FSP )) {
|
|
|
|
SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_FORCE_POST );
|
|
NtfsRaiseStatus( IrpContext, STATUS_CANT_WAIT, NULL, NULL );
|
|
}
|
|
|
|
//
|
|
// Flush and purge the stream.
|
|
//
|
|
|
|
NtfsFlushAndPurgeScb( IrpContext,
|
|
*ThisScb,
|
|
(ARGUMENT_PRESENT( ThisLcb ) ?
|
|
ThisLcb->Scb :
|
|
NULL) );
|
|
}
|
|
|
|
//
|
|
// Check if we should request a filter oplock.
|
|
//
|
|
|
|
if (FlagOn( IrpSp->Parameters.Create.Options, FILE_RESERVE_OPFILTER )) {
|
|
|
|
FsRtlOplockFsctrl( &(*ThisScb)->ScbType.Data.Oplock,
|
|
IrpContext->OriginatingIrp,
|
|
1 );
|
|
}
|
|
|
|
//
|
|
// Mark the Scb if this is a temporary file.
|
|
//
|
|
|
|
if (FlagOn( (*ThisScb)->ScbState, SCB_STATE_TEMPORARY ) ||
|
|
(FlagOn( ThisFcb->Info.FileAttributes, FILE_ATTRIBUTE_TEMPORARY ) &&
|
|
FlagOn( (*ThisCcb)->Flags, CCB_FLAG_OPEN_AS_FILE ))) {
|
|
|
|
SetFlag( (*ThisScb)->ScbState, SCB_STATE_TEMPORARY );
|
|
SetFlag( IrpSp->FileObject->Flags, FO_TEMPORARY_FILE );
|
|
}
|
|
}
|
|
|
|
try_exit: NOTHING;
|
|
} finally {
|
|
|
|
DebugUnwind( NtfsOpenAttribute );
|
|
|
|
//
|
|
// Back out local actions on error.
|
|
//
|
|
|
|
if (AbnormalTermination()
|
|
&& RemoveShareAccess) {
|
|
|
|
IoRemoveShareAccess( IrpSp->FileObject, &(*ThisScb)->ShareAccess );
|
|
}
|
|
|
|
DebugTrace( -1, Dbg, ("NtfsOpenAttribute: Status -> %08lx\n", Status) );
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
//
|
|
// Local support routine.
|
|
//
|
|
|
|
VOID
|
|
NtfsBackoutFailedOpens (
|
|
IN PIRP_CONTEXT IrpContext,
|
|
IN PFILE_OBJECT FileObject,
|
|
IN PFCB ThisFcb,
|
|
IN PSCB ThisScb OPTIONAL,
|
|
IN PCCB ThisCcb OPTIONAL
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called during an open that has failed after
|
|
modifying in-memory structures. We will repair the following
|
|
structures.
|
|
|
|
Vcb - Decrement the open counts. Check if we locked the volume.
|
|
|
|
ThisFcb - Restore he Share Access fields and decrement open counts.
|
|
|
|
ThisScb - Decrement the open counts.
|
|
|
|
ThisCcb - Remove from the Lcb and delete.
|
|
|
|
Arguments:
|
|
|
|
FileObject - This is the file object for this open.
|
|
|
|
ThisFcb - This is the Fcb for the file being opened.
|
|
|
|
ThisScb - This is the Scb for the given attribute.
|
|
|
|
ThisCcb - This is the Ccb for this open.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PAGED_CODE();
|
|
|
|
DebugTrace( +1, Dbg, ("NtfsBackoutFailedOpens: Entered\n") );
|
|
|
|
//
|
|
// If there is an Scb and Ccb, we remove the share access from the
|
|
// Fcb. We also remove all of the open and unclean counts incremented
|
|
// by us.
|
|
//
|
|
|
|
if (ARGUMENT_PRESENT( ThisScb )
|
|
&& ARGUMENT_PRESENT( ThisCcb )) {
|
|
|
|
PLCB Lcb;
|
|
PVCB Vcb = ThisFcb->Vcb;
|
|
|
|
//
|
|
// Remove this Ccb from the Lcb.
|
|
//
|
|
|
|
Lcb = ThisCcb->Lcb;
|
|
NtfsUnlinkCcbFromLcb( IrpContext, ThisCcb );
|
|
|
|
//
|
|
// Check if we need to remove the share access for this open.
|
|
//
|
|
|
|
IoRemoveShareAccess( FileObject, &ThisScb->ShareAccess );
|
|
|
|
//
|
|
// Modify the delete counts in the Fcb.
|
|
//
|
|
|
|
if (FlagOn( ThisCcb->Flags, CCB_FLAG_DELETE_FILE )) {
|
|
|
|
ThisFcb->FcbDeleteFile -= 1;
|
|
ClearFlag( ThisCcb->Flags, CCB_FLAG_DELETE_FILE );
|
|
}
|
|
|
|
if (FlagOn( ThisCcb->Flags, CCB_FLAG_DENY_DELETE )) {
|
|
|
|
ThisFcb->FcbDenyDelete -= 1;
|
|
ClearFlag( ThisCcb->Flags, CCB_FLAG_DENY_DELETE );
|
|
}
|
|
|
|
//
|
|
// Decrement the cleanup and close counts
|
|
//
|
|
|
|
NtfsDecrementCleanupCounts( ThisScb,
|
|
Lcb,
|
|
BooleanFlagOn( FileObject->Flags, FO_NO_INTERMEDIATE_BUFFERING ));
|
|
|
|
NtfsDecrementCloseCounts( IrpContext,
|
|
ThisScb,
|
|
Lcb,
|
|
(BOOLEAN) BooleanFlagOn(ThisFcb->FcbState, FCB_STATE_PAGING_FILE),
|
|
(BOOLEAN) IsFileObjectReadOnly( FileObject ),
|
|
TRUE );
|
|
|
|
//
|
|
// Now clean up the Ccb.
|
|
//
|
|
|
|
NtfsDeleteCcb( IrpContext, ThisFcb, &ThisCcb );
|
|
}
|
|
|
|
DebugTrace( -1, Dbg, ("NtfsBackoutFailedOpens: Exit\n") );
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
//
|
|
// Local support routine
|
|
//
|
|
|
|
VOID
|
|
NtfsUpdateScbFromMemory (
|
|
IN PIRP_CONTEXT IrpContext,
|
|
IN OUT PSCB Scb,
|
|
IN POLD_SCB_SNAPSHOT ScbSizes
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
All of the information from the attribute is stored in the snapshot. We process
|
|
this data identically to NtfsUpdateScbFromAttribute.
|
|
|
|
Arguments:
|
|
|
|
Scb - This is the Scb to update.
|
|
|
|
ScbSizes - This contains the sizes to store in the scb.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PAGED_CODE();
|
|
|
|
DebugTrace( +1, Dbg, ("NtfsUpdateScbFromMemory: Entered\n") );
|
|
|
|
//
|
|
// Check whether this is resident or nonresident
|
|
//
|
|
|
|
if (ScbSizes->Resident) {
|
|
|
|
Scb->Header.AllocationSize.QuadPart = ScbSizes->FileSize;
|
|
|
|
if (!FlagOn(Scb->ScbState, SCB_STATE_FILE_SIZE_LOADED)) {
|
|
|
|
Scb->Header.ValidDataLength =
|
|
Scb->Header.FileSize = Scb->Header.AllocationSize;
|
|
}
|
|
|
|
Scb->Header.AllocationSize.LowPart =
|
|
QuadAlign( Scb->Header.AllocationSize.LowPart );
|
|
|
|
Scb->TotalAllocated = Scb->Header.AllocationSize.QuadPart;
|
|
|
|
//
|
|
// Set the resident flag in the Scb.
|
|
//
|
|
|
|
SetFlag( Scb->ScbState, SCB_STATE_ATTRIBUTE_RESIDENT );
|
|
|
|
} else {
|
|
|
|
VCN FileClusters;
|
|
VCN AllocationClusters;
|
|
|
|
if (!FlagOn(Scb->ScbState, SCB_STATE_FILE_SIZE_LOADED)) {
|
|
|
|
Scb->Header.ValidDataLength.QuadPart = ScbSizes->ValidDataLength;
|
|
Scb->Header.FileSize.QuadPart = ScbSizes->FileSize;
|
|
Scb->ValidDataToDisk = ScbSizes->ValidDataLength;
|
|
}
|
|
|
|
Scb->TotalAllocated = ScbSizes->TotalAllocated;
|
|
Scb->Header.AllocationSize.QuadPart = ScbSizes->AllocationSize;
|
|
|
|
ClearFlag( Scb->ScbState, SCB_STATE_ATTRIBUTE_RESIDENT );
|
|
|
|
//
|
|
// Get the size of the compression unit.
|
|
//
|
|
|
|
ASSERT((ScbSizes->CompressionUnit == 0) ||
|
|
(ScbSizes->CompressionUnit == NTFS_CLUSTERS_PER_COMPRESSION));
|
|
|
|
if ((ScbSizes->CompressionUnit != 0) &&
|
|
(ScbSizes->CompressionUnit < 31)) {
|
|
Scb->CompressionUnit = BytesFromClusters( Scb->Vcb,
|
|
1 << ScbSizes->CompressionUnit );
|
|
Scb->CompressionUnitShift = ScbSizes->CompressionUnit;
|
|
}
|
|
|
|
ASSERT( Scb->CompressionUnit == 0
|
|
|| Scb->AttributeTypeCode == $INDEX_ROOT
|
|
|| NtfsIsTypeCodeCompressible( Scb->AttributeTypeCode )
|
|
);
|
|
|
|
//
|
|
// Compute the clusters for the file and its allocation.
|
|
//
|
|
|
|
AllocationClusters = LlClustersFromBytes( Scb->Vcb, Scb->Header.AllocationSize.QuadPart );
|
|
|
|
if (Scb->CompressionUnit == 0) {
|
|
|
|
FileClusters = LlClustersFromBytes(Scb->Vcb, Scb->Header.FileSize.QuadPart);
|
|
|
|
} else {
|
|
|
|
FileClusters = Scb->Header.FileSize.QuadPart + Scb->CompressionUnit - 1;
|
|
FileClusters &= ~(Scb->CompressionUnit - 1);
|
|
}
|
|
|
|
//
|
|
// If allocated clusters are greater than file clusters, mark
|
|
// the Scb to truncate on close.
|
|
//
|
|
|
|
if (AllocationClusters > FileClusters) {
|
|
|
|
SetFlag( Scb->ScbState, SCB_STATE_TRUNCATE_ON_CLOSE );
|
|
}
|
|
}
|
|
|
|
Scb->AttributeFlags = ScbSizes->AttributeFlags;
|
|
|
|
if (FlagOn( Scb->AttributeFlags, ATTRIBUTE_FLAG_SPARSE )) {
|
|
|
|
NtfsRaiseStatus( IrpContext, STATUS_ACCESS_DENIED, NULL, NULL );
|
|
}
|
|
|
|
if (FlagOn(Scb->AttributeFlags, ATTRIBUTE_FLAG_COMPRESSION_MASK)) {
|
|
|
|
SetFlag( Scb->ScbState, SCB_STATE_COMPRESSED );
|
|
|
|
//
|
|
// If the attribute is resident, then we will use our current
|
|
// default.
|
|
//
|
|
|
|
if (Scb->CompressionUnit == 0) {
|
|
|
|
Scb->CompressionUnit = BytesFromClusters( Scb->Vcb, 1 << NTFS_CLUSTERS_PER_COMPRESSION );
|
|
Scb->CompressionUnitShift = NTFS_CLUSTERS_PER_COMPRESSION;
|
|
|
|
ASSERT( (Scb->AttributeTypeCode == $INDEX_ROOT) ||
|
|
NtfsIsTypeCodeCompressible( Scb->AttributeTypeCode ));
|
|
}
|
|
}
|
|
|
|
//
|
|
// If the compression unit is non-zero or this is a resident file
|
|
// then set the flag in the common header for the Modified page writer.
|
|
//
|
|
|
|
NtfsAcquireFsrtlHeader( Scb );
|
|
Scb->Header.IsFastIoPossible = NtfsIsFastIoPossible( Scb );
|
|
NtfsReleaseFsrtlHeader( Scb );
|
|
|
|
SetFlag( Scb->ScbState,
|
|
SCB_STATE_UNNAMED_DATA | SCB_STATE_FILE_SIZE_LOADED | SCB_STATE_HEADER_INITIALIZED );
|
|
|
|
DebugTrace( -1, Dbg, ("NtfsUpdateScbFromMemory: Exit\n") );
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
//
|
|
// Local support routine
|
|
//
|
|
|
|
VOID
|
|
NtfsOplockPrePostIrp (
|
|
IN PVOID Context,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine performs any neccessary work before STATUS_PENDING is
|
|
returned with the Fsd thread. This routine is called within the
|
|
filesystem and by the oplock package. This routine will update
|
|
the originating Irp in the IrpContext and release all of the Fcbs and
|
|
paging io resources in the IrpContext.
|
|
|
|
Arguments:
|
|
|
|
Context - Pointer to the IrpContext to be queued to the Fsp
|
|
|
|
Irp - I/O Request Packet
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PIRP_CONTEXT IrpContext;
|
|
POPLOCK_CLEANUP OplockCleanup;
|
|
PFCB Fcb;
|
|
|
|
PAGED_CODE();
|
|
|
|
IrpContext = (PIRP_CONTEXT) Context;
|
|
|
|
ASSERT_IRP_CONTEXT( IrpContext );
|
|
|
|
IrpContext->OriginatingIrp = Irp;
|
|
OplockCleanup = IrpContext->Union.OplockCleanup;
|
|
|
|
//
|
|
// Adjust the filename strings as needed.
|
|
//
|
|
|
|
if (OplockCleanup->ExactCaseName.Buffer != NULL) {
|
|
|
|
RtlCopyMemory( OplockCleanup->FullFileName.Buffer,
|
|
OplockCleanup->ExactCaseName.Buffer,
|
|
OplockCleanup->ExactCaseName.MaximumLength );
|
|
}
|
|
|
|
//
|
|
// Free any buffer we allocated.
|
|
//
|
|
|
|
if ((OplockCleanup->FullFileName.Buffer != NULL) &&
|
|
(OplockCleanup->OriginalFileName.Buffer != OplockCleanup->FullFileName.Buffer)) {
|
|
|
|
NtfsFreePool( OplockCleanup->FullFileName.Buffer );
|
|
OplockCleanup->FullFileName.Buffer = NULL;
|
|
}
|
|
|
|
//
|
|
// Set the file name in the file object back to it's original value.
|
|
//
|
|
|
|
OplockCleanup->FileObject->FileName = OplockCleanup->OriginalFileName;
|
|
|
|
Fcb = IrpContext->FcbWithPagingExclusive;
|
|
if (Fcb != NULL) {
|
|
|
|
if (Fcb->NodeTypeCode == NTFS_NTC_FCB) {
|
|
|
|
NtfsReleasePagingIo( IrpContext, Fcb );
|
|
}
|
|
}
|
|
|
|
//
|
|
// Release all of the Fcb's in the exlusive lists.
|
|
//
|
|
|
|
while (!IsListEmpty( &IrpContext->ExclusiveFcbList )) {
|
|
|
|
NtfsReleaseFcb( IrpContext,
|
|
(PFCB)CONTAINING_RECORD( IrpContext->ExclusiveFcbList.Flink,
|
|
FCB,
|
|
ExclusiveFcbLinks ));
|
|
}
|
|
|
|
//
|
|
// Go through and free any Scb's in the queue of shared Scb's for transactions.
|
|
//
|
|
|
|
if (IrpContext->SharedScb != NULL) {
|
|
|
|
NtfsReleaseSharedResources( IrpContext );
|
|
}
|
|
|
|
//
|
|
// Mark that we've already returned pending to the user
|
|
//
|
|
|
|
IoMarkIrpPending( Irp );
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
//
|
|
// Local support routine.
|
|
//
|
|
|
|
NTSTATUS
|
|
NtfsCheckExistingFile (
|
|
IN PIRP_CONTEXT IrpContext,
|
|
IN PIO_STACK_LOCATION IrpSp,
|
|
IN PLCB ThisLcb OPTIONAL,
|
|
IN PFCB ThisFcb,
|
|
IN ULONG CcbFlags
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called to check the desired access on an existing file
|
|
against the ACL's and the read-only status of the file. If we fail on
|
|
the access check, that routine will raise. Otherwise we will return a
|
|
status to indicate success or the failure cause. This routine will access
|
|
and update the PreviouslyGrantedAccess field in the security context.
|
|
|
|
Arguments:
|
|
|
|
IrpSp - This is the Irp stack location for this open.
|
|
|
|
ThisLcb - This is the Lcb used to reach the Fcb to open.
|
|
|
|
ThisFcb - This is the Fcb where the open will occur.
|
|
|
|
CcbFlags - This is the flag field for the Ccb.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
BOOLEAN MaximumAllowed = FALSE;
|
|
|
|
PACCESS_STATE AccessState;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Save a pointer to the access state for convenience.
|
|
//
|
|
|
|
AccessState = IrpSp->Parameters.Create.SecurityContext->AccessState;
|
|
|
|
//
|
|
// Start by checking that there are no bits in the desired access that
|
|
// conflict with the read-only state of the file.
|
|
//
|
|
|
|
if (IsReadOnly( &ThisFcb->Info )) {
|
|
|
|
if (FlagOn( IrpSp->Parameters.Create.SecurityContext->DesiredAccess,
|
|
FILE_WRITE_DATA
|
|
| FILE_APPEND_DATA
|
|
| FILE_ADD_SUBDIRECTORY
|
|
| FILE_DELETE_CHILD )) {
|
|
|
|
return STATUS_ACCESS_DENIED;
|
|
|
|
} else if (FlagOn( IrpSp->Parameters.Create.Options, FILE_DELETE_ON_CLOSE )) {
|
|
|
|
return STATUS_CANNOT_DELETE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Otherwise we need to check the requested access vs. the allowable
|
|
// access in the ACL on the file. We will want to remember if
|
|
// MAXIMUM_ALLOWED was requested and remove the invalid bits for
|
|
// a read-only file.
|
|
//
|
|
|
|
//
|
|
// Remember if maximum allowed was requested.
|
|
//
|
|
|
|
if (FlagOn( IrpSp->Parameters.Create.SecurityContext->DesiredAccess,
|
|
MAXIMUM_ALLOWED )) {
|
|
|
|
MaximumAllowed = TRUE;
|
|
}
|
|
|
|
NtfsOpenCheck( IrpContext,
|
|
ThisFcb,
|
|
(((ThisLcb != NULL) && (ThisLcb != ThisFcb->Vcb->RootLcb))
|
|
? ThisLcb->Scb->Fcb
|
|
: NULL),
|
|
IrpContext->OriginatingIrp );
|
|
|
|
//
|
|
// If this is a read-only file and we requested maximum allowed then
|
|
// remove the invalid bits.
|
|
//
|
|
|
|
if (MaximumAllowed
|
|
&& IsReadOnly( &ThisFcb->Info )) {
|
|
|
|
ClearFlag( AccessState->PreviouslyGrantedAccess,
|
|
FILE_WRITE_DATA
|
|
| FILE_APPEND_DATA
|
|
| FILE_ADD_SUBDIRECTORY
|
|
| FILE_DELETE_CHILD );
|
|
}
|
|
|
|
//
|
|
// We do a check here to see if we conflict with the delete status on the
|
|
// file. Right now we check if there is already an opener who has delete
|
|
// access on the file and this opener doesn't allow delete access.
|
|
// We can skip this test if the opener is not requesting read, write or
|
|
// delete access.
|
|
//
|
|
|
|
if (ThisFcb->FcbDeleteFile != 0
|
|
&& FlagOn( AccessState->PreviouslyGrantedAccess, NtfsAccessDataFlags )
|
|
&& !FlagOn( IrpSp->Parameters.Create.ShareAccess, FILE_SHARE_DELETE )) {
|
|
|
|
DebugTrace( -1, Dbg, ("NtfsOpenAttributeInExistingFile: Exit\n") );
|
|
return STATUS_SHARING_VIOLATION;
|
|
}
|
|
|
|
//
|
|
// We do a check here to see if we conflict with the delete status on the
|
|
// file. If we are opening the file and requesting delete, then there can
|
|
// be no current handles which deny delete.
|
|
//
|
|
|
|
if (ThisFcb->FcbDenyDelete != 0
|
|
&& FlagOn( AccessState->PreviouslyGrantedAccess, DELETE )
|
|
&& FlagOn( CcbFlags, CCB_FLAG_OPEN_AS_FILE )) {
|
|
|
|
return STATUS_SHARING_VIOLATION;
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
//
|
|
// Local support routine.
|
|
//
|
|
|
|
NTSTATUS
|
|
NtfsBreakBatchOplock (
|
|
IN PIRP_CONTEXT IrpContext,
|
|
IN PIRP Irp,
|
|
IN PIO_STACK_LOCATION IrpSp,
|
|
IN PFCB ThisFcb,
|
|
IN UNICODE_STRING AttrName,
|
|
IN ATTRIBUTE_TYPE_CODE AttrTypeCode,
|
|
OUT PSCB *ThisScb
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called for each open of an existing attribute to
|
|
check for current batch oplocks on the file. We will also check
|
|
whether we will want to flush and purge this stream in the case
|
|
where only non-cached handles remain on the file. We only want
|
|
to do that in an Fsp thread because we will require every bit
|
|
of stack we can get.
|
|
|
|
Arguments:
|
|
|
|
Irp - This is the Irp for this open operation.
|
|
|
|
IrpSp - This is the stack location for this open.
|
|
|
|
ThisFcb - This is the Fcb for the file being opened.
|
|
|
|
AttrName - This is the attribute name in case we need to create
|
|
an Scb.
|
|
|
|
AttrTypeCode - This is the attribute type code to use to create
|
|
the Scb.
|
|
|
|
ThisScb - Address to store the Scb if found or created.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Will be either STATUS_SUCCESS or STATUS_PENDING.
|
|
|
|
--*/
|
|
|
|
{
|
|
BOOLEAN ScbExisted;
|
|
PSCB NextScb;
|
|
PLIST_ENTRY Links;
|
|
|
|
PAGED_CODE();
|
|
|
|
DebugTrace( +1, Dbg, ("NtfsBreakBatchOplock: Entered\n") );
|
|
|
|
//
|
|
// In general we will just break the batch oplock for the stream we
|
|
// are trying to open. However if we are trying to delete the file
|
|
// and someone has a batch oplock on a different stream which
|
|
// will cause our open to fail then we need to try to break those
|
|
// batch oplocks. Likewise if we are opening a stream and won't share
|
|
// with a file delete then we need to break any batch oplocks on the main
|
|
// stream of the file.
|
|
//
|
|
|
|
//
|
|
// Consider the case where we are opening a stream and there is a
|
|
// batch oplock on the main data stream.
|
|
//
|
|
|
|
if (AttrName.Length != 0) {
|
|
|
|
if (ThisFcb->FcbDeleteFile != 0 &&
|
|
!FlagOn( IrpSp->Parameters.Create.ShareAccess, FILE_SHARE_DELETE )) {
|
|
|
|
Links = ThisFcb->ScbQueue.Flink;
|
|
|
|
while (Links != &ThisFcb->ScbQueue) {
|
|
|
|
NextScb = CONTAINING_RECORD( Links, SCB, FcbLinks );
|
|
|
|
if (NextScb->AttributeTypeCode == $DATA &&
|
|
NextScb->AttributeName.Length == 0) {
|
|
|
|
if (FsRtlCurrentBatchOplock( &NextScb->ScbType.Data.Oplock )) {
|
|
|
|
//
|
|
// We remember if a batch oplock break is underway for the
|
|
// case where the sharing check fails.
|
|
//
|
|
|
|
Irp->IoStatus.Information = FILE_OPBATCH_BREAK_UNDERWAY;
|
|
|
|
//
|
|
// We wait on the oplock.
|
|
//
|
|
|
|
if (FsRtlCheckOplock( &NextScb->ScbType.Data.Oplock,
|
|
Irp,
|
|
(PVOID) IrpContext,
|
|
NtfsOplockComplete,
|
|
NtfsOplockPrePostIrp ) == STATUS_PENDING) {
|
|
|
|
return STATUS_PENDING;
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
Links = Links->Flink;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Now consider the case where we are opening the main stream and want to
|
|
// delete the file but an opener on a stream is preventing us.
|
|
//
|
|
|
|
} else if (ThisFcb->FcbDenyDelete != 0 &&
|
|
FlagOn( IrpSp->Parameters.Create.SecurityContext->AccessState->RemainingDesiredAccess,
|
|
MAXIMUM_ALLOWED | DELETE )) {
|
|
|
|
//
|
|
// Find all of the other data Scb and check their oplock status.
|
|
//
|
|
|
|
Links = ThisFcb->ScbQueue.Flink;
|
|
|
|
while (Links != &ThisFcb->ScbQueue) {
|
|
|
|
NextScb = CONTAINING_RECORD( Links, SCB, FcbLinks );
|
|
|
|
if (NextScb->AttributeTypeCode == $DATA &&
|
|
NextScb->AttributeName.Length != 0) {
|
|
|
|
if (FsRtlCurrentBatchOplock( &NextScb->ScbType.Data.Oplock )) {
|
|
|
|
//
|
|
// We remember if a batch oplock break is underway for the
|
|
// case where the sharing check fails.
|
|
//
|
|
|
|
Irp->IoStatus.Information = FILE_OPBATCH_BREAK_UNDERWAY;
|
|
|
|
//
|
|
// We wait on the oplock.
|
|
//
|
|
|
|
if (FsRtlCheckOplock( &NextScb->ScbType.Data.Oplock,
|
|
Irp,
|
|
(PVOID) IrpContext,
|
|
NtfsOplockComplete,
|
|
NtfsOplockPrePostIrp ) == STATUS_PENDING) {
|
|
|
|
return STATUS_PENDING;
|
|
}
|
|
|
|
Irp->IoStatus.Information = 0;
|
|
}
|
|
}
|
|
|
|
Links = Links->Flink;
|
|
}
|
|
}
|
|
|
|
//
|
|
// We try to find the Scb for this file.
|
|
//
|
|
|
|
*ThisScb = NtfsCreateScb( IrpContext,
|
|
ThisFcb,
|
|
AttrTypeCode,
|
|
&AttrName,
|
|
FALSE,
|
|
&ScbExisted );
|
|
|
|
//
|
|
// If there was a previous Scb, we examine the oplocks.
|
|
//
|
|
|
|
if (ScbExisted &&
|
|
(SafeNodeType( *ThisScb ) == NTFS_NTC_SCB_DATA)) {
|
|
|
|
//
|
|
// If we have to flush and purge then we want to be in the Fsp.
|
|
//
|
|
|
|
if (!FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_IN_FSP ) &&
|
|
FlagOn( IrpSp->FileObject->Flags, FO_NO_INTERMEDIATE_BUFFERING ) &&
|
|
((*ThisScb)->CleanupCount == (*ThisScb)->NonCachedCleanupCount) &&
|
|
((*ThisScb)->NonpagedScb->SegmentObject.DataSectionObject != NULL)) {
|
|
|
|
SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_FORCE_POST );
|
|
NtfsRaiseStatus( IrpContext, STATUS_CANT_WAIT, NULL, NULL );
|
|
}
|
|
|
|
if (FsRtlCurrentBatchOplock( &(*ThisScb)->ScbType.Data.Oplock )) {
|
|
|
|
//
|
|
// If the handle count is greater than 1 then fail this
|
|
// open now.
|
|
//
|
|
|
|
if (FlagOn( IrpSp->Parameters.Create.Options, FILE_RESERVE_OPFILTER ) &&
|
|
((*ThisScb)->CleanupCount > 1)) {
|
|
|
|
NtfsRaiseStatus( IrpContext, STATUS_OPLOCK_NOT_GRANTED, NULL, NULL );
|
|
}
|
|
|
|
DebugTrace( 0, Dbg, ("Breaking batch oplock\n") );
|
|
|
|
//
|
|
// We remember if a batch oplock break is underway for the
|
|
// case where the sharing check fails.
|
|
//
|
|
|
|
Irp->IoStatus.Information = FILE_OPBATCH_BREAK_UNDERWAY;
|
|
|
|
if (FsRtlCheckOplock( &(*ThisScb)->ScbType.Data.Oplock,
|
|
Irp,
|
|
(PVOID) IrpContext,
|
|
NtfsOplockComplete,
|
|
NtfsOplockPrePostIrp ) == STATUS_PENDING) {
|
|
|
|
return STATUS_PENDING;
|
|
}
|
|
|
|
Irp->IoStatus.Information = 0;
|
|
}
|
|
}
|
|
|
|
DebugTrace( -1, Dbg, ("NtfsBreakBatchOplock: Exit - %08lx\n", STATUS_SUCCESS) );
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
//
|
|
// Local support routine
|
|
//
|
|
|
|
NTSTATUS
|
|
NtfsCompleteLargeAllocation (
|
|
IN PIRP_CONTEXT IrpContext,
|
|
IN PIRP Irp,
|
|
IN PLCB Lcb OPTIONAL,
|
|
IN PSCB Scb,
|
|
IN PCCB Ccb,
|
|
IN BOOLEAN CreateFileCase,
|
|
IN BOOLEAN DeleteOnClose
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called when we need to add more allocation to a stream
|
|
being opened. This stream could have been reallocated or created with
|
|
this call but we didn't allocate all of the space in the main path.
|
|
|
|
Arguments:
|
|
|
|
Irp - This is the Irp for this open operation.
|
|
|
|
Lcb - This is the Lcb used to reach the stream being opened. Won't be
|
|
specified in the open by ID case.
|
|
|
|
Scb - This is the Scb for the stream being opened.
|
|
|
|
Ccb - This is the Ccb for the this user handle.
|
|
|
|
CreateFileCase - Indicates if we reallocated or created this stream.
|
|
|
|
DeleteOnClose - Indicates if this handle requires delete on close.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - the result of this operation.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
FILE_ALLOCATION_INFORMATION AllInfo;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Commit the current transaction and free all resources.
|
|
//
|
|
|
|
NtfsCheckpointCurrentTransaction( IrpContext );
|
|
|
|
//
|
|
// Free any exclusive paging I/O resource.
|
|
//
|
|
|
|
if (IrpContext->FcbWithPagingExclusive != NULL) {
|
|
NtfsReleasePagingIo( IrpContext, IrpContext->FcbWithPagingExclusive );
|
|
}
|
|
|
|
while (!IsListEmpty( &IrpContext->ExclusiveFcbList )) {
|
|
|
|
NtfsReleaseFcb( IrpContext,
|
|
(PFCB) CONTAINING_RECORD( IrpContext->ExclusiveFcbList.Flink,
|
|
FCB,
|
|
ExclusiveFcbLinks ));
|
|
}
|
|
|
|
//
|
|
// Go through and free any Scb's in the queue of shared Scb's for transactions.
|
|
//
|
|
|
|
if (IrpContext->SharedScb != NULL) {
|
|
|
|
NtfsReleaseSharedResources( IrpContext );
|
|
}
|
|
|
|
SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_CALL_SELF );
|
|
AllInfo.AllocationSize = Irp->Overlay.AllocationSize;
|
|
Status = IoSetInformation( IoGetCurrentIrpStackLocation( Irp )->FileObject,
|
|
FileAllocationInformation,
|
|
sizeof( FILE_ALLOCATION_INFORMATION ),
|
|
&AllInfo );
|
|
|
|
//
|
|
// Success! We will reacquire the Vcb quickly to undo the
|
|
// actions taken above to block access to the new file/attribute.
|
|
//
|
|
|
|
if (NT_SUCCESS( Status )) {
|
|
|
|
NtfsAcquireExclusiveVcb( IrpContext, Scb->Vcb, TRUE );
|
|
|
|
//
|
|
// Enable access to new file.
|
|
//
|
|
|
|
if (CreateFileCase) {
|
|
|
|
Scb->Fcb->LinkCount = 1;
|
|
|
|
if (ARGUMENT_PRESENT( Lcb )) {
|
|
|
|
ClearFlag( Lcb->LcbState, LCB_STATE_DELETE_ON_CLOSE );
|
|
|
|
if (FlagOn( Lcb->FileNameAttr->Flags, FILE_NAME_DOS | FILE_NAME_NTFS )) {
|
|
|
|
ClearFlag( Scb->Fcb->FcbState, FCB_STATE_PRIMARY_LINK_DELETED );
|
|
}
|
|
}
|
|
|
|
//
|
|
// Enable access to new attribute.
|
|
//
|
|
|
|
} else {
|
|
|
|
ClearFlag( Scb->ScbState, SCB_STATE_DELETE_ON_CLOSE );
|
|
}
|
|
|
|
//
|
|
// If this is the DeleteOnClose case, we mark the Scb and Lcb
|
|
// appropriately.
|
|
//
|
|
|
|
if (DeleteOnClose) {
|
|
|
|
SetFlag( Ccb->Flags, CCB_FLAG_DELETE_ON_CLOSE );
|
|
}
|
|
|
|
NtfsReleaseVcb( IrpContext, Scb->Vcb );
|
|
|
|
//
|
|
// Else there was some sort of error, and we need to let cleanup
|
|
// and close execute, since when we complete Create with an error
|
|
// cleanup and close would otherwise never occur. Cleanup will
|
|
// delete or truncate a file or attribute as appropriate, based on
|
|
// how we left the Fcb/Lcb or Scb above.
|
|
//
|
|
|
|
} else {
|
|
|
|
NtfsIoCallSelf( IrpContext,
|
|
IoGetCurrentIrpStackLocation( Irp )->FileObject,
|
|
IRP_MJ_CLEANUP );
|
|
|
|
NtfsIoCallSelf( IrpContext,
|
|
IoGetCurrentIrpStackLocation( Irp )->FileObject,
|
|
IRP_MJ_CLOSE );
|
|
}
|
|
|
|
ClearFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_CALL_SELF );
|
|
return Status;
|
|
}
|
|
|
|
#ifdef _CAIRO_
|
|
NTSTATUS
|
|
NtfsTryOpenFcb (
|
|
IN PIRP_CONTEXT IrpContext,
|
|
IN PVCB Vcb,
|
|
OUT PFCB *CurrentFcb,
|
|
IN FILE_REFERENCE FileReference
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called to open a file by its file segment number.
|
|
We need to verify that this file Id exists. This code is
|
|
patterned after open by Id.
|
|
|
|
Arguments:
|
|
|
|
Vcb - Vcb for this volume.
|
|
|
|
CurrentFcb - Address of Fcb pointer. Store the Fcb we find here.
|
|
|
|
FileReference - This is the file Id for the file to open the
|
|
sequence number is ignored.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Indicates the result of this create file operation.
|
|
|
|
Note:
|
|
|
|
If the status is successful then the FCB is returned with its reference
|
|
count incremented and the FCB held exclusive.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
LONGLONG MftOffset;
|
|
PFILE_RECORD_SEGMENT_HEADER FileRecord;
|
|
PBCB Bcb = NULL;
|
|
|
|
PFCB ThisFcb;
|
|
|
|
BOOLEAN AcquiredFcbTable = FALSE;
|
|
BOOLEAN AcquiredMft = TRUE;
|
|
BOOLEAN ThisFcbFree = TRUE;
|
|
|
|
PAGED_CODE();
|
|
|
|
ASSERT( *CurrentFcb == NULL );
|
|
|
|
//
|
|
// Do not bother with system files.
|
|
//
|
|
|
|
//
|
|
// If this is a system fcb then return.
|
|
//
|
|
|
|
if (NtfsSegmentNumber( &FileReference ) < FIRST_USER_FILE_NUMBER &&
|
|
NtfsSegmentNumber( &FileReference ) != ROOT_FILE_NAME_INDEX_NUMBER) {
|
|
|
|
return STATUS_NOT_FOUND;
|
|
}
|
|
|
|
//
|
|
// Calculate the offset in the MFT.
|
|
//
|
|
|
|
MftOffset = NtfsSegmentNumber( &FileReference );
|
|
|
|
MftOffset = Int64ShllMod32(MftOffset, Vcb->MftShift);
|
|
|
|
//
|
|
// Acquire the MFT shared so it cannot shrink on us.
|
|
//
|
|
|
|
NtfsAcquireSharedScb( IrpContext, Vcb->MftScb );
|
|
|
|
try {
|
|
|
|
if (MftOffset >= Vcb->MftScb->Header.FileSize.QuadPart) {
|
|
|
|
DebugTrace( 0, Dbg, ("File Id doesn't lie within Mft\n") );
|
|
|
|
Status = STATUS_END_OF_FILE;
|
|
leave;
|
|
}
|
|
|
|
NtfsReadMftRecord( IrpContext,
|
|
Vcb,
|
|
&FileReference,
|
|
&Bcb,
|
|
&FileRecord,
|
|
NULL );
|
|
|
|
//
|
|
// This file record better be in use, have a matching sequence number and
|
|
// be the primary file record for this file.
|
|
//
|
|
|
|
if (!FlagOn( FileRecord->Flags, FILE_RECORD_SEGMENT_IN_USE )
|
|
|| (*((PLONGLONG)&FileRecord->BaseFileRecordSegment) != 0)) {
|
|
|
|
Status = STATUS_NOT_FOUND;
|
|
leave;
|
|
}
|
|
|
|
//
|
|
// Get the current sequence number.
|
|
//
|
|
|
|
FileReference.SequenceNumber = FileRecord->SequenceNumber;
|
|
|
|
NtfsUnpinBcb( &Bcb );
|
|
|
|
NtfsAcquireFcbTable( IrpContext, Vcb );
|
|
AcquiredFcbTable = TRUE;
|
|
|
|
//
|
|
// We know that it is safe to continue the open. We start by creating
|
|
// an Fcb for this file. It is possible that the Fcb exists.
|
|
// We create the Fcb first, if we need to update the Fcb info structure
|
|
// we copy the one from the index entry. We look at the Fcb to discover
|
|
// if it has any links, if it does then we make this the last Fcb we
|
|
// reached. If it doesn't then we have to clean it up from here.
|
|
//
|
|
|
|
ThisFcb = NtfsCreateFcb( IrpContext,
|
|
Vcb,
|
|
FileReference,
|
|
FALSE,
|
|
TRUE,
|
|
NULL );
|
|
|
|
//
|
|
// ReferenceCount the fcb so it does no go away.
|
|
//
|
|
|
|
ThisFcb->ReferenceCount += 1;
|
|
|
|
//
|
|
// Release the mft and fcb table before acquiring the FCB exclusive.
|
|
//
|
|
|
|
NtfsReleaseScb( IrpContext, Vcb->MftScb );
|
|
NtfsReleaseFcbTable( IrpContext, Vcb );
|
|
AcquiredMft = FALSE;
|
|
AcquiredFcbTable = FALSE;
|
|
|
|
NtfsAcquireFcbWithPaging( IrpContext, ThisFcb, FALSE );
|
|
ThisFcbFree = FALSE;
|
|
|
|
//
|
|
// Skip any deleted files.
|
|
//
|
|
|
|
if (FlagOn( ThisFcb->FcbState, FCB_STATE_FILE_DELETED )) {
|
|
|
|
DbgPrint( "NtfsTryOpenFcb: Deleted fcb found. Fcb = %lx\n", ThisFcb );
|
|
NtfsAcquireFcbTable( IrpContext, Vcb );
|
|
ASSERT(ThisFcb->ReferenceCount > 0);
|
|
ThisFcb->ReferenceCount--;
|
|
NtfsReleaseFcbTable( IrpContext, Vcb );
|
|
|
|
NtfsTeardownStructures( IrpContext,
|
|
ThisFcb,
|
|
NULL,
|
|
FALSE,
|
|
FALSE,
|
|
&ThisFcbFree );
|
|
|
|
//
|
|
// Release the fcb if it has not been deleted.
|
|
//
|
|
|
|
if (!ThisFcbFree) {
|
|
NtfsReleaseFcb( IrpContext, ThisFcb );
|
|
ThisFcbFree = TRUE;
|
|
}
|
|
|
|
//
|
|
// Teardown may generate a transaction clean it up.
|
|
//
|
|
|
|
NtfsCompleteRequest( &IrpContext, NULL, Status );
|
|
|
|
Status = STATUS_NOT_FOUND;
|
|
leave;
|
|
}
|
|
|
|
//
|
|
// Store this Fcb into our caller's parameter and remember to
|
|
// to show we acquired it.
|
|
//
|
|
|
|
*CurrentFcb = ThisFcb;
|
|
ThisFcbFree = TRUE;
|
|
|
|
|
|
//
|
|
// If the Fcb Info field needs to be initialized, we do so now.
|
|
// We read this information from the disk.
|
|
//
|
|
|
|
if (!FlagOn( ThisFcb->FcbState, FCB_STATE_DUP_INITIALIZED )) {
|
|
|
|
NtfsUpdateFcbInfoFromDisk( IrpContext,
|
|
TRUE,
|
|
ThisFcb,
|
|
NULL,
|
|
NULL );
|
|
|
|
}
|
|
|
|
} finally {
|
|
|
|
if (AcquiredFcbTable) {
|
|
|
|
NtfsReleaseFcbTable( IrpContext, Vcb );
|
|
}
|
|
|
|
NtfsUnpinBcb( &Bcb );
|
|
|
|
if (AcquiredMft) {
|
|
NtfsReleaseScb( IrpContext, Vcb->MftScb );
|
|
}
|
|
|
|
if (!ThisFcbFree) {
|
|
NtfsReleaseFcb( IrpContext, ThisFcb );
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
|
|
}
|
|
#endif // _CAIRO_
|