mirror of https://github.com/tongzx/nt5src
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.
670 lines
19 KiB
670 lines
19 KiB
/*++
|
|
|
|
Copyright (c) 1998 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
oplock.c
|
|
|
|
Abstract:
|
|
|
|
This module contains the SPUDCreateFile service.
|
|
|
|
Author:
|
|
|
|
John Ballard (jballard) 13-Dec-1996
|
|
|
|
Revision History:
|
|
|
|
Keith Moore (keithmo) 02-Feb-1998
|
|
Made it work, added much needed comments.
|
|
|
|
--*/
|
|
|
|
|
|
#include "spudp.h"
|
|
|
|
|
|
//
|
|
// Private prototypes.
|
|
//
|
|
|
|
NTSTATUS
|
|
SpudpOplockCompletion(
|
|
PDEVICE_OBJECT DeviceObject,
|
|
PIRP Irp,
|
|
PVOID Context
|
|
);
|
|
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text( PAGE, SPUDCreateFile )
|
|
#endif
|
|
#if 0
|
|
NOT PAGEABLE -- SpudpOplockCompletion
|
|
#endif
|
|
|
|
|
|
//
|
|
// Public routines.
|
|
//
|
|
|
|
|
|
NTSTATUS
|
|
SPUDCreateFile(
|
|
OUT PHANDLE FileHandle,
|
|
IN POBJECT_ATTRIBUTES ObjectAttributes,
|
|
OUT PIO_STATUS_BLOCK IoStatusBlock,
|
|
IN ULONG FileAttributes,
|
|
IN ULONG ShareAccess,
|
|
IN ULONG CreateOptions,
|
|
IN SECURITY_INFORMATION SecurityInformation,
|
|
OUT PSECURITY_DESCRIPTOR SecDescBuffer,
|
|
IN ULONG SecDescLength,
|
|
OUT PULONG SecDescLengthNeeded,
|
|
IN PVOID OplockContext,
|
|
IN LARGE_INTEGER OplockMaxFileSize,
|
|
OUT PBOOLEAN OplockGranted,
|
|
OUT PSPUD_FILE_INFORMATION FileInfo
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This service opens a file and queries information about the file. This
|
|
service can also optionally retrieve any security descriptor associated
|
|
with the file and issue an oplock request.
|
|
|
|
Arguments:
|
|
|
|
FileHandle - A pointer to a variable to receive the handle to the open
|
|
file.
|
|
|
|
ObjectAttributes - Supplies the attributes to be used for file object
|
|
(name, SECURITY_DESCRIPTOR, etc.)
|
|
|
|
IoStatusBlock - Specifies the address of the caller's I/O status block.
|
|
|
|
FileAttributes - Specifies the attributes that should be set on the file,
|
|
if it is created.
|
|
|
|
ShareAccess - Supplies the types of share access that the caller would
|
|
like to the file.
|
|
|
|
CreateOptions - Caller options for how to perform the create/open.
|
|
|
|
SecurityInformation - Indicates the type of security information to
|
|
retrieve.
|
|
|
|
SecDescBuffer - Supplies a buffer to receive the file's security
|
|
descriptor.
|
|
|
|
SecDescLength - Supplies the length of the security descriptor buffer.
|
|
|
|
SecDescLengthNeeded - Receives the length needed to store the security
|
|
descriptor.
|
|
|
|
OplockContext - Supplies an uninterpreted context used during oplock
|
|
break notifications. If this value is NULL, then no oplock request
|
|
is issued.
|
|
|
|
OplockMaxFileSize = If the size of the file opened is larger than
|
|
OplockMaxFileSize then no oplock request is issued.
|
|
|
|
OplockGranted - A pointer to a variable to receive the status of the
|
|
oplock request. This parameter is ignored if OplockContext is NULL.
|
|
|
|
FileInfo - Supplies a buffer to receive information about the file.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Completion status.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
NTSTATUS status;
|
|
PFILE_OBJECT fileObject;
|
|
PIRP irp;
|
|
PIO_STACK_LOCATION irpSp;
|
|
PDEVICE_OBJECT deviceObject;
|
|
HANDLE localFileHandle;
|
|
IO_STATUS_BLOCK localIoStatusBlock;
|
|
FILE_BASIC_INFORMATION basicInfo;
|
|
FILE_STANDARD_INFORMATION standardInfo;
|
|
PVOID completionPort;
|
|
|
|
//
|
|
// Sanity check.
|
|
//
|
|
|
|
PAGED_CODE();
|
|
|
|
ASSERT( SPUD_OPLOCK_BREAK_OPEN == FILE_OPLOCK_BROKEN_TO_LEVEL_2 );
|
|
ASSERT( SPUD_OPLOCK_BREAK_CLOSE == FILE_OPLOCK_BROKEN_TO_NONE );
|
|
|
|
status = SPUD_ENTER_SERVICE( "SPUDCreateFile", TRUE );
|
|
|
|
if( !NT_SUCCESS(status) ) {
|
|
return status;
|
|
}
|
|
|
|
//
|
|
// SPUD doesn't support kernel-mode callers. In fact, we don't
|
|
// even build the "system stubs" necessary to invoke SPUD from
|
|
// kernel-mode.
|
|
//
|
|
|
|
ASSERT( ExGetPreviousMode() == UserMode );
|
|
|
|
//
|
|
// Probe the generic arguments. We don't need to probe the FileHandle
|
|
// and IoStatusBlock parameters, as they will be probed by IoCreateFile().
|
|
//
|
|
|
|
try {
|
|
|
|
//
|
|
// The FileInfo parameter must be writeable by the caller.
|
|
//
|
|
|
|
ProbeForWrite( FileInfo, sizeof(*FileInfo), sizeof(ULONG) );
|
|
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
status = GetExceptionCode();
|
|
SPUD_LEAVE_SERVICE( "SPUDCreateFile", status, FALSE );
|
|
return status;
|
|
}
|
|
|
|
//
|
|
// Open the file. If successful, this will write the newly-opened
|
|
// file handle into *FileHandle.
|
|
//
|
|
|
|
status = IoCreateFile(
|
|
FileHandle, // FileHandle
|
|
GENERIC_READ // DesiredAccess
|
|
| SYNCHRONIZE //
|
|
| FILE_READ_ATTRIBUTES, //
|
|
ObjectAttributes, // ObjectAttributes
|
|
IoStatusBlock, // IoStatusBlock
|
|
NULL, // AllocationSize
|
|
FileAttributes, // FileAttributes
|
|
ShareAccess, // ShareAccess
|
|
FILE_OPEN, // Disposition
|
|
CreateOptions, // CreateOptions
|
|
NULL, // EaBuffer
|
|
0, // EaLength
|
|
CreateFileTypeNone, // CreateFileType
|
|
NULL, // ExtraCreateParameters
|
|
0 // Options
|
|
);
|
|
|
|
if( !NT_SUCCESS(status) ) {
|
|
SPUD_LEAVE_SERVICE( "SPUDCreateFile", status, FALSE );
|
|
return status;
|
|
}
|
|
|
|
//
|
|
// Snag the handle from the user-mode buffer.
|
|
//
|
|
|
|
try {
|
|
localFileHandle = *FileHandle;
|
|
} except( EXCEPTION_EXECUTE_HANDLER ) {
|
|
//
|
|
// We faulted trying to read the file handle from user-mode memory.
|
|
// The user-mode code must have mucked with the virtual address
|
|
// space after we called IoCreateFile(). Since we cannot get the
|
|
// file handle, we cannot close the file, and the user-mode code is
|
|
// going to leak the handle.
|
|
//
|
|
|
|
status = GetExceptionCode();
|
|
SPUD_LEAVE_SERVICE( "SPUDCreateFile", status, FALSE );
|
|
return status;
|
|
}
|
|
|
|
//
|
|
// Query the file attributes.
|
|
//
|
|
|
|
status = ZwQueryInformationFile(
|
|
localFileHandle, // FileHandle
|
|
&localIoStatusBlock, // IoStatusBlock
|
|
&basicInfo, // FileInformation
|
|
sizeof(basicInfo), // Length
|
|
FileBasicInformation // FileInformationClass
|
|
);
|
|
|
|
if( NT_SUCCESS(status) ) {
|
|
status = ZwQueryInformationFile(
|
|
localFileHandle, // FileHandle
|
|
&localIoStatusBlock, // IoStatusBlock
|
|
&standardInfo, // FileInformation
|
|
sizeof(standardInfo), // Length
|
|
FileStandardInformation // FileInformationClass
|
|
);
|
|
}
|
|
|
|
if( NT_SUCCESS(status) ) {
|
|
//
|
|
// Copy the file attributes to the user-mode buffer.
|
|
//
|
|
|
|
try {
|
|
RtlCopyMemory(
|
|
&FileInfo->BasicInformation,
|
|
&basicInfo,
|
|
sizeof(basicInfo)
|
|
);
|
|
|
|
RtlCopyMemory(
|
|
&FileInfo->StandardInformation,
|
|
&standardInfo,
|
|
sizeof(standardInfo)
|
|
);
|
|
} except( EXCEPTION_EXECUTE_HANDLER ) {
|
|
status = GetExceptionCode();
|
|
}
|
|
}
|
|
|
|
//
|
|
// If we failed for any reason (either we failed to query the attributes
|
|
// or we faulted trying to copy them to user-mode) then close the file
|
|
// handle and bail.
|
|
//
|
|
|
|
if( !NT_SUCCESS(status) ) {
|
|
NtClose( localFileHandle );
|
|
SPUD_LEAVE_SERVICE( "SPUDCreateFile", status, FALSE );
|
|
return status;
|
|
}
|
|
|
|
//
|
|
// If the caller passed in a security descriptor buffer, then try to
|
|
// query the security descriptor.
|
|
//
|
|
|
|
if( SecDescLength > 0 ) {
|
|
|
|
//
|
|
// Query the security descriptor from the file. Note that
|
|
// since the previous mode == UserMode, we don't need to
|
|
// probe SecDescBuffer or SecDescLengthNeeded (they will be
|
|
// probed by the NtQuerySecurityObject() API).
|
|
//
|
|
|
|
status = NtQuerySecurityObject(
|
|
localFileHandle, // Handle
|
|
SecurityInformation, // SecurityInformation
|
|
SecDescBuffer, // SecurityDescriptor
|
|
SecDescLength, // Length
|
|
SecDescLengthNeeded // LengthNeeded
|
|
);
|
|
|
|
if( !NT_SUCCESS(status) ) {
|
|
|
|
if( status == STATUS_NOT_SUPPORTED ) {
|
|
//
|
|
// This status code is returned for filesystems that don't
|
|
// support security. We'll just fake an empty descriptor.
|
|
//
|
|
|
|
try {
|
|
*SecDescLengthNeeded = 0;
|
|
status = STATUS_SUCCESS;
|
|
} except( EXCEPTION_EXECUTE_HANDLER ) {
|
|
status = GetExceptionCode();
|
|
}
|
|
} else if( status == STATUS_BUFFER_TOO_SMALL ) {
|
|
//
|
|
// Mapping STATUS_BUFFER_TOO_SMALL to STATUS_SUCCESS seems
|
|
// a bit bizarre. The intent here is to succeed the
|
|
// SPUDCreateFile() call. The fact that *SecDescLengthNeeded
|
|
// is returned with a value larger than SecDescLength is the
|
|
// user-mode code's signal that it should allocate a new
|
|
// buffer & retrieve the security descriptor "out-of-band".
|
|
//
|
|
|
|
#if DBG
|
|
try {
|
|
ASSERT( *SecDescLengthNeeded > SecDescLength );
|
|
} except( EXCEPTION_EXECUTE_HANDLER ) {
|
|
NOTHING;
|
|
}
|
|
#endif
|
|
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
|
|
if( !NT_SUCCESS(status) ) {
|
|
NtClose( localFileHandle );
|
|
SPUD_LEAVE_SERVICE( "SPUDCreateFile", status, FALSE );
|
|
return status;
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// If OplockContext == NULL then the caller is not interested in
|
|
// oplocks, so we can just return successfully right now.
|
|
//
|
|
|
|
if( OplockContext == NULL ) {
|
|
ASSERT( status == STATUS_SUCCESS );
|
|
SPUD_LEAVE_SERVICE( "SPUDCreateFile", status, FALSE );
|
|
return status;
|
|
}
|
|
|
|
//
|
|
// Probe the oplock-specific parameters.
|
|
//
|
|
|
|
try {
|
|
|
|
//
|
|
// The OplockGranted parameter must be writeable. Set it to
|
|
// FALSE until proven otherwise.
|
|
//
|
|
|
|
ProbeAndWriteBoolean( OplockGranted, FALSE );
|
|
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
status = GetExceptionCode();
|
|
NtClose( localFileHandle );
|
|
SPUD_LEAVE_SERVICE( "SPUDCreateFile", status, FALSE );
|
|
return status;
|
|
}
|
|
|
|
//
|
|
// If the entity just opened is actually a directory (rather than
|
|
// a "normal" file) then there's no point in trying to acquire the
|
|
// oplock.
|
|
//
|
|
// Note that this check must be after the OplockGranted parameter
|
|
// is probed so that we know it is set to FALSE.
|
|
//
|
|
|
|
if( standardInfo.Directory ) {
|
|
ASSERT( status == STATUS_SUCCESS );
|
|
SPUD_LEAVE_SERVICE( "SPUDCreateFile", status, FALSE );
|
|
return status;
|
|
}
|
|
|
|
//
|
|
// If the file is smaller than the size specified in the
|
|
// OplockMaxFileSize parameter, then the user doesn't want
|
|
// an oplock.
|
|
//
|
|
|
|
if ( standardInfo.EndOfFile.QuadPart > OplockMaxFileSize.QuadPart ) {
|
|
ASSERT( status == STATUS_SUCCESS );
|
|
SPUD_LEAVE_SERVICE( "SPUDCreateFile", status, FALSE );
|
|
return status;
|
|
}
|
|
|
|
|
|
//
|
|
// See if we can acquire the completion port.
|
|
//
|
|
|
|
completionPort = SpudReferenceCompletionPort();
|
|
|
|
if( completionPort == NULL ) {
|
|
status = STATUS_INVALID_DEVICE_REQUEST;
|
|
SPUD_LEAVE_SERVICE( "SPUDCreateFile", status, FALSE );
|
|
return status;
|
|
}
|
|
|
|
//
|
|
// Reference the file handle
|
|
//
|
|
|
|
status = ObReferenceObjectByHandle(
|
|
localFileHandle, // Handle
|
|
0L, // DesiredAccess
|
|
*IoFileObjectType, // ObjectType
|
|
UserMode, // AccessMode
|
|
(PVOID *)&fileObject, // Object
|
|
NULL // HandleInformation
|
|
);
|
|
|
|
if( !NT_SUCCESS(status) ) {
|
|
NtClose( localFileHandle );
|
|
SPUD_LEAVE_SERVICE( "SPUDCreateFile", status, TRUE );
|
|
return status;
|
|
}
|
|
|
|
TRACE_OB_REFERENCE( fileObject );
|
|
|
|
//
|
|
// Chase down the device object associated with this file object.
|
|
//
|
|
|
|
deviceObject = IoGetRelatedDeviceObject( fileObject );
|
|
|
|
//
|
|
// Allocate and initialize the IRP.
|
|
//
|
|
|
|
irp = IoAllocateIrp( deviceObject->StackSize, FALSE );
|
|
|
|
if( !irp ) {
|
|
TRACE_OB_DEREFERENCE( fileObject );
|
|
ObDereferenceObject( fileObject );
|
|
NtClose( localFileHandle );
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
SPUD_LEAVE_SERVICE( "SPUDCreateFile", status, TRUE );
|
|
return status;
|
|
}
|
|
|
|
irp->RequestorMode = UserMode;
|
|
irp->Tail.Overlay.OriginalFileObject = fileObject;
|
|
irp->Tail.Overlay.Thread = PsGetCurrentThread();
|
|
|
|
irpSp = IoGetNextIrpStackLocation( irp );
|
|
|
|
irpSp->MajorFunction = IRP_MJ_FILE_SYSTEM_CONTROL;
|
|
irpSp->FileObject = fileObject;
|
|
irpSp->DeviceObject = deviceObject;
|
|
|
|
// irpSp->Parameters.FileSystemControl.OutputBufferLength = 0;
|
|
// irpSp->Parameters.FileSystemControl.InputBufferLength = 0;
|
|
irpSp->Parameters.FileSystemControl.FsControlCode = FSCTL_REQUEST_BATCH_OPLOCK;
|
|
|
|
IoSetCompletionRoutine(
|
|
irp, // Irp
|
|
SpudpOplockCompletion, // CompletionRoutine
|
|
OplockContext, // Context
|
|
TRUE, // InvokeOnSuccess
|
|
TRUE, // InvokeOnError
|
|
TRUE // InvokeOnCancel
|
|
);
|
|
|
|
//
|
|
// Issue the IRP to the file system.
|
|
//
|
|
|
|
status = IoCallDriver( deviceObject, irp );
|
|
|
|
if( NT_SUCCESS(status) ) {
|
|
//
|
|
// The oplock IRP was successfully issued to the file system. We
|
|
// can now assume the IRP will complete later, therefore we will
|
|
// set the user's OplockGranted flag.
|
|
//
|
|
|
|
try {
|
|
*OplockGranted = TRUE;
|
|
} except( EXCEPTION_EXECUTE_HANDLER ) {
|
|
//
|
|
// Grr...
|
|
//
|
|
// This is a sticky situation. We've already opened the file
|
|
// and successfully acquired the oplock. Closing the file handle
|
|
// here would probably confuse the user-mode code, as it would
|
|
// see the oplock break after an unsuccessful SPUDCreateFile().
|
|
//
|
|
// The only thing we can do here is just drop the failure on the
|
|
// floor. This is not as bad as it seems, as the user-mode code
|
|
// will presumably fault when it tries to access OplockGranted.
|
|
//
|
|
}
|
|
}
|
|
|
|
//
|
|
// Regardless of the completion status of the oplock IRP, return
|
|
// STATUS_SUCCESS to the caller. Failure to acquire the oplock is
|
|
// insufficient grounds to fail the SPUDCreateFile() call.
|
|
//
|
|
|
|
status = STATUS_SUCCESS;
|
|
SPUD_LEAVE_SERVICE( "SPUDCreateFile", status, FALSE );
|
|
return status;
|
|
|
|
} // SPUDCreateFile
|
|
|
|
|
|
//
|
|
// Private routines.
|
|
//
|
|
|
|
|
|
NTSTATUS
|
|
SpudpOplockCompletion(
|
|
PDEVICE_OBJECT DeviceObject,
|
|
PIRP Irp,
|
|
PVOID Context
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Completion routine for oplock IRPs.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - The device object completing the request (unused).
|
|
|
|
Irp - The IRP being completed.
|
|
|
|
Context - The context associated with the request. This is actually
|
|
the user's OplockContext passed into SPUDCreateFile().
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Completion status.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
PFILE_OBJECT fileObject;
|
|
|
|
//
|
|
// Dereference the file object since we're done with it.
|
|
//
|
|
|
|
fileObject = Irp->Tail.Overlay.OriginalFileObject;
|
|
TRACE_OB_DEREFERENCE( fileObject );
|
|
ObDereferenceObject( fileObject );
|
|
|
|
if( NT_SUCCESS(Irp->IoStatus.Status) ) {
|
|
|
|
//
|
|
// Sanity check.
|
|
//
|
|
|
|
ASSERT( Irp->IoStatus.Information == SPUD_OPLOCK_BREAK_OPEN ||
|
|
Irp->IoStatus.Information == SPUD_OPLOCK_BREAK_CLOSE );
|
|
|
|
//
|
|
// Post the IRP to the completion port. The completion key must be
|
|
// the OplockContext passed into SPUDCreateFile().
|
|
//
|
|
// Note that NULL is used as a distinguished value in UserApcContext.
|
|
// This is an indicator to AtqpProcessContext() that an I/O
|
|
// completion is actually an oplock break.
|
|
//
|
|
|
|
Irp->Tail.CompletionKey = Context;
|
|
Irp->Overlay.AsynchronousParameters.UserApcContext = NULL;
|
|
|
|
//
|
|
// Hack-O-Rama. This is absolutely required to get the I/O completion
|
|
// port stuff to work. It turns out that the CurrentStackLocation
|
|
// field overlays the PacketType field. Since PacketType must be set
|
|
// to IopCompletionPacketIrp (which just happens to be zero), we'll
|
|
// set CurrentStackLocation to NULL. Ugly. We should really make this
|
|
// part of the kernel. Maybe something like this:
|
|
//
|
|
// VOID
|
|
// IoSetIrpCompletion(
|
|
// IN PVOID IoCompletion,
|
|
// IN PVOID KeyContext,
|
|
// IN PVOID ApcContext,
|
|
// IN PIRP Irp
|
|
// );
|
|
//
|
|
// We could then replace the following lines (and a few lines above)
|
|
// with:
|
|
//
|
|
// IoSetIrpCompletion(
|
|
// SpudCompletionPort,
|
|
// Context,
|
|
// NULL,
|
|
// Irp
|
|
// );
|
|
//
|
|
|
|
Irp->Tail.Overlay.CurrentStackLocation = NULL;
|
|
|
|
KeInsertQueue(
|
|
(PKQUEUE)SpudCompletionPort,
|
|
&Irp->Tail.Overlay.ListEntry
|
|
);
|
|
|
|
} else {
|
|
|
|
//
|
|
// The oplock IRP failed. We'll just drop this IRP on the floor and
|
|
// free it. Since we already notified the caller (through the
|
|
// OplockGranted parameter to SPUDCreateFile) that the oplock could
|
|
// not be acquired, we don't want to notify them again through the
|
|
// completion port.
|
|
//
|
|
// Also note that pending oplock IRPs are not cancelled in the
|
|
// "normal" sense (i.e. with STATUS_CANCELLED) when the file handle
|
|
// is closed. Rather, they are completed *successfully* with the
|
|
// FILE_OPLOCK_BROKEN_TO_NONE (SPUD_OPLOCK_BREAK_CLOSE) completion
|
|
// code. Ergo, cancelled oplock IRPs should never go through this
|
|
// code path.
|
|
//
|
|
|
|
IoFreeIrp( Irp );
|
|
|
|
}
|
|
|
|
//
|
|
// We're done with the completion port. Remove the reference we added
|
|
// in SPUDCreateFile().
|
|
//
|
|
|
|
SpudDereferenceCompletionPort();
|
|
|
|
//
|
|
// Tell IO to stop processing this IRP. The IRP will be freed in
|
|
// NtRemoveIoCompletion (the kernel-mode worker for the
|
|
// GetQueuedCompletionStatus() API).
|
|
//
|
|
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
|
|
} // SpudpOplockCompletion
|
|
|