Leaked source code of windows server 2003
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.
 
 
 
 
 
 

3650 lines
98 KiB

/*++
Copyright (c) 1998-1999 Microsoft Corporation
Module Name:
dispatch.c
Abstract:
this is the major function code dispatch filter layer.
Author:
Paul McDaniel (paulmcd) 23-Jan-2000
Revision History:
--*/
#include "precomp.h"
//
// Private constants.
//
#if DBG
PWSTR IrpMjCodes[] =
{
L"IRP_MJ_CREATE",
L"IRP_MJ_CREATE_NAMED_PIPE",
L"IRP_MJ_CLOSE",
L"IRP_MJ_READ",
L"IRP_MJ_WRITE",
L"IRP_MJ_QUERY_INFORMATION",
L"IRP_MJ_SET_INFORMATION",
L"IRP_MJ_QUERY_EA",
L"IRP_MJ_SET_EA",
L"IRP_MJ_FLUSH_BUFFERS",
L"IRP_MJ_QUERY_VOLUME_INFORMATION",
L"IRP_MJ_SET_VOLUME_INFORMATION",
L"IRP_MJ_DIRECTORY_CONTROL",
L"IRP_MJ_FILE_SYSTEM_CONTROL",
L"IRP_MJ_DEVICE_CONTROL",
L"IRP_MJ_INTERNAL_DEVICE_CONTROL",
L"IRP_MJ_SHUTDOWN",
L"IRP_MJ_LOCK_CONTROL",
L"IRP_MJ_CLEANUP",
L"IRP_MJ_CREATE_MAILSLOT",
L"IRP_MJ_QUERY_SECURITY",
L"IRP_MJ_SET_SECURITY",
L"IRP_MJ_POWER",
L"IRP_MJ_SYSTEM_CONTROL",
L"IRP_MJ_DEVICE_CHANGE",
L"IRP_MJ_QUERY_QUOTA",
L"IRP_MJ_SET_QUOTA",
L"IRP_MJ_PNP",
L"IRP_MJ_MAXIMUM_FUNCTION",
};
#endif // DBG
//
// Private types.
//
//
// Private prototypes.
//
NTSTATUS
SrCreateRestorePointIoctl (
IN PIRP pIrp,
IN PIO_STACK_LOCATION IrpSp
);
NTSTATUS
SrGetNextSeqNumIoctl (
IN PIRP pIrp,
IN PIO_STACK_LOCATION IrpSp
);
NTSTATUS
SrReloadConfigurationIoctl (
IN PIRP pIrp,
IN PIO_STACK_LOCATION IrpSp
);
NTSTATUS
SrSwitchAllLogsIoctl (
IN PIRP pIrp,
IN PIO_STACK_LOCATION IrpSp
);
NTSTATUS
SrDisableVolumeIoctl (
IN PIRP pIrp,
IN PIO_STACK_LOCATION IrpSp
);
NTSTATUS
SrStartMonitoringIoctl (
IN PIRP pIrp,
IN PIO_STACK_LOCATION IrpSp
);
NTSTATUS
SrStopMonitoringIoctl (
IN PIRP pIrp,
IN PIO_STACK_LOCATION IrpSp
);
NTSTATUS
SrDismountCompletion(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
);
//
// linker commands
//
#ifdef ALLOC_PRAGMA
#pragma alloc_text( PAGE, SrMajorFunction )
#pragma alloc_text( PAGE, SrCleanup )
#pragma alloc_text( PAGE, SrCreate )
#pragma alloc_text( PAGE, SrSetInformation )
#pragma alloc_text( PAGE, SrSetHardLink )
#pragma alloc_text( PAGE, SrSetSecurity )
#pragma alloc_text( PAGE, SrCreateRestorePointIoctl )
#pragma alloc_text( PAGE, SrFsControl )
#pragma alloc_text( PAGE, SrFsControlReparsePoint )
#pragma alloc_text( PAGE, SrFsControlMount )
#pragma alloc_text( PAGE, SrFsControlLockOrDismount)
#pragma alloc_text( PAGE, SrFsControlWriteRawEncrypted )
#pragma alloc_text( PAGE, SrFsControlSetSparse )
#pragma alloc_text( PAGE, SrPnp )
#pragma alloc_text( PAGE, SrGetNextSeqNumIoctl )
#pragma alloc_text( PAGE, SrReloadConfigurationIoctl )
#pragma alloc_text( PAGE, SrSwitchAllLogsIoctl )
#pragma alloc_text( PAGE, SrDisableVolumeIoctl )
#pragma alloc_text( PAGE, SrStartMonitoringIoctl )
#pragma alloc_text( PAGE, SrStopMonitoringIoctl )
#pragma alloc_text( PAGE, SrShutdown )
#endif // ALLOC_PRAGMA
#if 0
NOT PAGEABLE -- SrPassThrough
NOT PAGEABLE -- SrWrite
#endif // 0
//
// Private globals.
//
//
// Lookup table to verify incoming IOCTL codes.
//
typedef
NTSTATUS
(NTAPI * PFN_IOCTL_HANDLER)(
IN PIRP pIrp,
IN PIO_STACK_LOCATION IrpSp
);
typedef struct _SR_IOCTL_TABLE
{
ULONG IoControlCode;
PFN_IOCTL_HANDLER Handler;
} SR_IOCTL_TABLE, *PSR_IOCTL_TABLE;
SR_IOCTL_TABLE SrIoctlTable[] =
{
{ IOCTL_SR_CREATE_RESTORE_POINT, &SrCreateRestorePointIoctl },
{ IOCTL_SR_RELOAD_CONFIG, &SrReloadConfigurationIoctl },
{ IOCTL_SR_START_MONITORING, &SrStartMonitoringIoctl },
{ IOCTL_SR_STOP_MONITORING, &SrStopMonitoringIoctl },
{ IOCTL_SR_WAIT_FOR_NOTIFICATION, &SrWaitForNotificationIoctl },
{ IOCTL_SR_SWITCH_LOG, &SrSwitchAllLogsIoctl },
{ IOCTL_SR_DISABLE_VOLUME, &SrDisableVolumeIoctl },
{ IOCTL_SR_GET_NEXT_SEQUENCE_NUM, &SrGetNextSeqNumIoctl }
};
C_ASSERT( SR_NUM_IOCTLS == DIMENSION(SrIoctlTable) );
//
// Public globals.
//
//
// Public functions.
//
/***************************************************************************++
Routine Description:
Does any pre or post work for the IRP then passes it through to the
lower layer driver.
NOTE: This routine is NOT pageable
Arguments:
Return Value:
NTSTATUS - Status code.
--***************************************************************************/
NTSTATUS
SrPassThrough(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP pIrp
)
{
PSR_DEVICE_EXTENSION pExtension;
//
// this is NonPaged code!
//
ASSERT(KeGetCurrentIrql() <= APC_LEVEL);
ASSERT(IS_VALID_DEVICE_OBJECT(DeviceObject));
ASSERT(IS_VALID_IRP(pIrp));
//
// Is this a function for our Control Device Object?
//
if (DeviceObject == _globals.pControlDevice)
{
return SrMajorFunction(DeviceObject, pIrp);
}
//
// else it is a device we've attached to , grab our extension
//
ASSERT(IS_SR_DEVICE_OBJECT(DeviceObject));
pExtension = DeviceObject->DeviceExtension;
//
// Now call the appropriate file system driver with the request.
//
IoSkipCurrentIrpStackLocation(pIrp);
return IoCallDriver(pExtension->pTargetDevice, pIrp);
} // SrPassThrough
/***************************************************************************++
Routine Description:
Handles IRPs for the actual device control object vs. the sub-level
fsd we are attached to .
Arguments:
Return Value:
NTSTATUS - Status code.
--***************************************************************************/
NTSTATUS
SrMajorFunction(
IN PDEVICE_OBJECT pDeviceObject,
IN PIRP pIrp
)
{
NTSTATUS Status;
PIO_STACK_LOCATION pIrpSp;
PSR_CONTROL_OBJECT pControlObject;
ULONG Code;
ULONG FunctionCode;
PFILE_FULL_EA_INFORMATION pEaBuffer;
PSR_OPEN_PACKET pOpenPacket;
UNREFERENCED_PARAMETER( pDeviceObject );
ASSERT(IS_VALID_DEVICE_OBJECT(pDeviceObject));
ASSERT(IS_VALID_IRP(pIrp));
ASSERT(pDeviceObject == _globals.pControlDevice);
//
// < dispatch!
//
PAGED_CODE();
SrTrace(FUNC_ENTRY, (
"SR!SrMajorFunction(Function=%ls)\n",
IrpMjCodes[IoGetCurrentIrpStackLocation(pIrp)->MajorFunction]
));
Status = STATUS_SUCCESS;
pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
switch (pIrpSp->MajorFunction)
{
//
// IRP_MJ_CREATE is called to create a new HANDLE on
// SR_CONTROL_DEVICE_NAME
//
case IRP_MJ_CREATE:
//
// Find and validate the open packet.
//
pEaBuffer = (PFILE_FULL_EA_INFORMATION)
(pIrp->AssociatedIrp.SystemBuffer);
if (pEaBuffer == NULL ||
pEaBuffer->EaValueLength != sizeof(*pOpenPacket) ||
pEaBuffer->EaNameLength != SR_OPEN_PACKET_NAME_LENGTH ||
strcmp( pEaBuffer->EaName, SR_OPEN_PACKET_NAME ) )
{
Status = STATUS_REVISION_MISMATCH;
goto CompleteTheIrp;
}
pOpenPacket =
(PSR_OPEN_PACKET)( pEaBuffer->EaName + pEaBuffer->EaNameLength + 1 );
ASSERT( (((ULONG_PTR)pOpenPacket) & 7) == 0 );
//
// For now, we'll fail if the incoming version doesn't EXACTLY match
// the expected version. In future, we may need to be a bit more
// flexible to allow down-level clients.
//
if (pOpenPacket->MajorVersion != SR_INTERFACE_VERSION_MAJOR ||
pOpenPacket->MinorVersion != SR_INTERFACE_VERSION_MINOR)
{
Status = STATUS_REVISION_MISMATCH;
goto CompleteTheIrp;
}
if (_globals.pControlObject != NULL)
{
Status = STATUS_DEVICE_ALREADY_ATTACHED;
goto CompleteTheIrp;
}
try {
//
// grab the lock
//
SrAcquireGlobalLockExclusive();
//
// Double check to make sure that the ControlObject hasn't
// been created while we were waiting to get the lock.
//
if (_globals.pControlObject != NULL)
{
Status = STATUS_DEVICE_ALREADY_ATTACHED;
leave;
}
//
// Create a new OBJECT
//
Status = SrCreateControlObject(&pControlObject, 0);
if (!NT_SUCCESS(Status))
{
leave;
}
ASSERT(IS_VALID_CONTROL_OBJECT(pControlObject));
//
// store the object in the file
//
pIrpSp->FileObject->FsContext = pControlObject;
pIrpSp->FileObject->FsContext2 = SR_CONTROL_OBJECT_CONTEXT;
//
// and keep a global copy
//
_globals.pControlObject = pControlObject;
} finally {
SrReleaseGlobalLock();
}
if (!NT_SUCCESS( Status )) {
goto CompleteTheIrp;
}
break;
//
// IRP_MJ_CLOSE is called when all references are gone.
// Note: this operation can not be failed. It must succeed.
//
case IRP_MJ_CLOSE:
pControlObject = pIrpSp->FileObject->FsContext;
ASSERT(_globals.pControlObject == pControlObject);
ASSERT(IS_VALID_CONTROL_OBJECT(pControlObject));
ASSERT(pIrpSp->FileObject->FsContext2 == SR_CONTROL_OBJECT_CONTEXT);
try {
SrAcquireGlobalLockExclusive();
//
// delete the control object
//
Status = SrDeleteControlObject(pControlObject);
if (!NT_SUCCESS(Status))
{
leave;
}
pIrpSp->FileObject->FsContext2 = NULL;
pIrpSp->FileObject->FsContext = NULL;
//
// clear out the global
//
_globals.pControlObject = NULL;
} finally {
SrReleaseGlobalLock();
}
break;
//
// IRP_MJ_DEVICE_CONTROL is how most user-mode api's drop into here
//
case IRP_MJ_DEVICE_CONTROL:
//
// Extract the IOCTL control code and process the request.
//
Code = pIrpSp->Parameters.DeviceIoControl.IoControlCode;
FunctionCode = IoGetFunctionCodeFromCtlCode(Code);
if (FunctionCode < SR_NUM_IOCTLS &&
SrIoctlTable[FunctionCode].IoControlCode == Code)
{
#if DBG
KIRQL oldIrql = KeGetCurrentIrql();
#endif // DBG
Status = (SrIoctlTable[FunctionCode].Handler)( pIrp, pIrpSp );
ASSERT( KeGetCurrentIrql() == oldIrql );
if (!NT_SUCCESS(Status)) {
goto CompleteTheIrp;
}
}
else
{
//
// If we made it this far, then the ioctl is invalid.
//
Status = STATUS_INVALID_DEVICE_REQUEST;
goto CompleteTheIrp;
}
break;
//
// IRP_MJ_CLEANUP is called when all handles are closed
// Note: this operation can not be failed. It must succeed.
//
case IRP_MJ_CLEANUP:
pControlObject = pIrpSp->FileObject->FsContext;
ASSERT(_globals.pControlObject == pControlObject);
ASSERT(IS_VALID_CONTROL_OBJECT(pControlObject));
ASSERT(pIrpSp->FileObject->FsContext2 == SR_CONTROL_OBJECT_CONTEXT);
try {
SrAcquireGlobalLockExclusive();
//
// cancel all IO on this object
//
Status = SrCancelControlIo(pControlObject);
CHECK_STATUS(Status);
} finally {
SrReleaseGlobalLock();
}
break;
default:
//
// unsupported!
//
Status = STATUS_INVALID_DEVICE_REQUEST;
break;
}
//
// Complete the request if we are DONE.
//
CompleteTheIrp:
if (Status != STATUS_PENDING)
{
pIrp->IoStatus.Status = Status;
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
NULLPTR(pIrp);
}
ASSERT(Status != SR_STATUS_VOLUME_DISABLED);
#if DBG
if (Status == STATUS_INVALID_DEVICE_REQUEST ||
Status == STATUS_DEVICE_ALREADY_ATTACHED ||
Status == STATUS_REVISION_MISMATCH)
{
//
// don't DbgBreak on this, test tools pass garbage in normally
// to test this code path out.
//
return Status;
}
#endif
RETURN(Status);
} // SrMajorFunction
/***************************************************************************++
Routine Description:
Arguments:
Handle WRITE Irps.
NOTE: This routine is NOT pageable.
Return Value:
NTSTATUS - Status code.
--***************************************************************************/
NTSTATUS
SrWrite(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP pIrp
)
{
PSR_DEVICE_EXTENSION pExtension;
PIO_STACK_LOCATION pIrpSp;
PSR_STREAM_CONTEXT pFileContext;
NTSTATUS eventStatus;
//
// This cannot be paged because it is called from
// the paging path.
//
ASSERT(IS_VALID_DEVICE_OBJECT(DeviceObject));
ASSERT(IS_VALID_IRP(pIrp));
//
// Is this a function for our control device object (vs an attachee)?
//
if (DeviceObject == _globals.pControlDevice)
{
return SrMajorFunction(DeviceObject, pIrp);
}
//
// else it is a device we've attached to , grab our extension
//
ASSERT(IS_SR_DEVICE_OBJECT(DeviceObject));
pExtension = DeviceObject->DeviceExtension;
//
// See if logging is enabled and we don't care about this type of IO
// to the file systems' control device objects.
//
if (!SR_LOGGING_ENABLED(pExtension) ||
SR_IS_FS_CONTROL_DEVICE(pExtension))
{
goto CompleteTheIrp;
}
//
// ignore all paging i/o for now. we catch all write's prior to
// the cache manager even seeing them.
//
pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
if (FlagOn(pIrp->Flags, IRP_PAGING_IO))
{
goto CompleteTheIrp;
}
//
// Ignore files with no name
//
if (FILE_OBJECT_IS_NOT_POTENTIALLY_INTERESTING( pIrpSp->FileObject ) ||
FILE_OBJECT_DOES_NOT_HAVE_VPB( pIrpSp->FileObject ))
{
goto CompleteTheIrp;
}
//
// Get the context now so we can determine if this is a
// directory or not
//
eventStatus = SrGetContext( pExtension,
pIrpSp->FileObject,
SrEventStreamChange,
&pFileContext );
if (!NT_SUCCESS( eventStatus ))
{
goto CompleteTheIrp;
}
//
// If this is a directory don't bother logging because the
// operation will fail.
//
if (FlagOn(pFileContext->Flags,CTXFL_IsInteresting) && !FlagOn(pFileContext->Flags,CTXFL_IsDirectory))
{
SrHandleEvent( pExtension,
SrEventStreamChange,
pIrpSp->FileObject,
pFileContext,
NULL,
NULL );
}
//
// Release the context
//
SrReleaseContext( pFileContext );
//
// call the AttachedTo driver
//
CompleteTheIrp:
IoSkipCurrentIrpStackLocation(pIrp);
return IoCallDriver(pExtension->pTargetDevice, pIrp);
} // SrWrite
/***************************************************************************++
Routine Description:
Handle Cleanup IRPs
Arguments:
Return Value:
NTSTATUS - Status code.
--***************************************************************************/
NTSTATUS
SrCleanup(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP pIrp
)
{
PSR_DEVICE_EXTENSION pExtension;
PIO_STACK_LOCATION pIrpSp;
//
// < dispatch!
//
PAGED_CODE();
ASSERT(IS_VALID_DEVICE_OBJECT(DeviceObject));
ASSERT(IS_VALID_IRP(pIrp));
//
// Is this a function for our control device object (vs an attachee)?
//
if (DeviceObject == _globals.pControlDevice)
{
return SrMajorFunction(DeviceObject, pIrp);
}
//
// else it is a device we've attached to, grab our extension
//
ASSERT(IS_SR_DEVICE_OBJECT(DeviceObject));
pExtension = DeviceObject->DeviceExtension;
//
// See if logging is enabled and we don't care about this type of IO
// to the file systems' control device objects.
//
if (!SR_LOGGING_ENABLED(pExtension) ||
SR_IS_FS_CONTROL_DEVICE(pExtension))
{
goto CompleteTheIrp;
}
pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
//
// does this file have a name? skip unnamed files
//
if (FILE_OBJECT_IS_NOT_POTENTIALLY_INTERESTING( pIrpSp->FileObject ) ||
FILE_OBJECT_DOES_NOT_HAVE_VPB( pIrpSp->FileObject ))
{
goto CompleteTheIrp;
}
//
// is this file about to be deleted ? we do this here as file's can
// be marked for deletion throughout their lifetime via
// IRP_MJ_SET_INFORMATION .
//
//
// for delete we only clean the FCB, not the CCB delete_on_close.
// this was handled in SrCreate.
//
if (pIrpSp->FileObject->DeletePending)
{
NTSTATUS eventStatus;
PSR_STREAM_CONTEXT pFileContext;
//
// Get the context now so we can determine if this is a directory or not
//
eventStatus = SrGetContext( pExtension,
pIrpSp->FileObject,
SrEventFileDelete,
&pFileContext );
if (!NT_SUCCESS( eventStatus ))
{
goto CompleteTheIrp;
}
//
// If interesting, log it
//
if (FlagOn(pFileContext->Flags,CTXFL_IsInteresting))
{
SrHandleEvent( pExtension,
FlagOn(pFileContext->Flags,CTXFL_IsDirectory) ?
SrEventDirectoryDelete :
SrEventFileDelete,
pIrpSp->FileObject,
pFileContext,
NULL,
NULL);
}
//
// Release the context
//
SrReleaseContext( pFileContext );
}
//
// call on to the next filter
//
CompleteTheIrp:
IoSkipCurrentIrpStackLocation(pIrp);
return IoCallDriver(pExtension->pTargetDevice, pIrp);
} // SrCleanup
/***************************************************************************++
Routine Description:
Handle Create IRPS
Arguments:
Return Value:
NTSTATUS - Status code.
--***************************************************************************/
NTSTATUS
SrCreate(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP pIrp
)
{
PSR_DEVICE_EXTENSION pExtension;
PIO_STACK_LOCATION pIrpSp;
NTSTATUS eventStatus;
NTSTATUS IrpStatus;
ULONG CreateDisposition;
ULONG CreateOptions;
USHORT FileAttributes;
SR_OVERWRITE_INFO OverwriteInfo;
KEVENT waitEvent;
PFILE_OBJECT pFileObject;
PSR_STREAM_CONTEXT pFileContext = NULL;
BOOLEAN willCreateUnnamedStream = TRUE;
PAGED_CODE();
ASSERT(IS_VALID_DEVICE_OBJECT(DeviceObject));
ASSERT(IS_VALID_IRP(pIrp));
//
// Is this a function for our control device object (vs an attachee)?
//
if (DeviceObject == _globals.pControlDevice)
{
return SrMajorFunction(DeviceObject, pIrp);
}
//
// else it is a device we've attached to, grab our extension
//
ASSERT(IS_SR_DEVICE_OBJECT(DeviceObject));
pExtension = DeviceObject->DeviceExtension;
//
// See if logging is enabled and we don't care about this type of IO
// to the file systems' control device objects.
//
if (!SR_LOGGING_ENABLED(pExtension) ||
SR_IS_FS_CONTROL_DEVICE(pExtension))
{
goto CompleteTheIrpAndReturn;
}
//
// Finish Initialization
//
pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
pFileObject = pIrpSp->FileObject;
//
// does this file have a name? skip unnamed files. Also skip paging
// files. (NULL Vpb is normal - the file is not open yet)
//
if (FILE_OBJECT_IS_NOT_POTENTIALLY_INTERESTING( pFileObject ) ||
FlagOn(pIrpSp->Flags,SL_OPEN_PAGING_FILE))
{
goto CompleteTheIrpAndReturn;
}
//
// Finish initialization and save some information
//
RtlZeroMemory( &OverwriteInfo, sizeof(OverwriteInfo) );
OverwriteInfo.Signature = SR_OVERWRITE_INFO_TAG;
CreateOptions = pIrpSp->Parameters.Create.Options & FILE_VALID_OPTION_FLAGS;
CreateDisposition = pIrpSp->Parameters.Create.Options >> 24;
FileAttributes = pIrpSp->Parameters.Create.FileAttributes;
//
// Handle OVERWRITE and SUPERSEEDE cases.
//
if ((CreateDisposition == FILE_OVERWRITE) ||
(CreateDisposition == FILE_OVERWRITE_IF) ||
(CreateDisposition == FILE_SUPERSEDE))
{
SR_EVENT_TYPE event;
//
// The file may be changed by this open so save a copy before going
// down to the filesystem.
//
// First get the context to determine if this is interesting. Since
// this is in the PRE-Create stage we can not tell if this file
// has a context or not (the FsContext field is not initialized yet).
// We will always create a context. Then in the Post-Create section
// we will see if a context was already defined. If not we will add
// this context to the system. If so then we will free this
// context.
//
// Note: If a user opens a directory with any of these
// CreateDisposition flags set, we will go down this path, treating
// the directory name like a file. If the directory name is
// interesting, we will try to back it up and at that point we will
// realize that it is a directory and bail.
//
event = SrEventStreamOverwrite|SrEventIsNotDirectory|SrEventInPreCreate;
if (FlagOn( CreateOptions, FILE_OPEN_BY_FILE_ID ))
{
event |= SrEventOpenById;
}
eventStatus = SrCreateContext( pExtension,
pFileObject,
event,
FileAttributes,
&pFileContext );
if (!NT_SUCCESS_NO_DBGBREAK(eventStatus))
{
goto CompleteTheIrpAndReturn;
}
SrTrace( CONTEXT_LOG, ("Sr!SrCreate: Created (%p) Event=%06x Fl=%03x Use=%d \"%.*S\"\n",
pFileContext,
SrEventStreamOverwrite|SrEventIsNotDirectory,
pFileContext->Flags,
pFileContext->UseCount,
(pFileContext->FileName.Length+
pFileContext->StreamNameLength)/
sizeof(WCHAR),
pFileContext->FileName.Buffer) );
//
// If the file is interesting then handle it
//
if (FlagOn(pFileContext->Flags,CTXFL_IsInteresting))
{
OverwriteInfo.pIrp = pIrp;
eventStatus = SrHandleEvent( pExtension,
SrEventStreamOverwrite,
pFileObject,
pFileContext,
&OverwriteInfo,
NULL );
OverwriteInfo.pIrp = NULL;
if (!NT_SUCCESS(eventStatus))
{
//
// This context has never been linked into a list so nobody
// else can be refrencing it. Release it (which will delete
// it since the use count is at 1.
//
ASSERT(pFileContext != NULL);
ASSERT(pFileContext->UseCount == 1);
SrReleaseContext( pFileContext );
pFileContext = NULL;
goto CompleteTheIrpAndReturn;
}
}
}
//
// As long as the file is not marked delete on close, if the file is simply
// opened, we can do not need to set a completion routine. Otherwise,
// we need a completion routine so that we can see the result of the
// create before we do any logging work.
//
if (!FlagOn( CreateOptions, FILE_DELETE_ON_CLOSE ) &&
CreateDisposition == FILE_OPEN)
{
goto CompleteTheIrpAndReturn;
}
//
// If this is a CREATE operation that could result in the creation of
// a named data stream on a file (FILE_OPEN and FILE_OVERWRITE will never
// create a new file), we need to see if the non-named data
// stream of this file already exists. If the file already exists,
// then so does the non-named data stream.
//
if (((CreateDisposition == FILE_CREATE) ||
(CreateDisposition == FILE_OPEN_IF) ||
(CreateDisposition == FILE_SUPERSEDE) ||
(CreateDisposition == FILE_OVERWRITE_IF)) &&
SrFileNameContainsStream( pExtension, pFileObject, pFileContext ))
{
if (SrFileAlreadyExists( pExtension, pFileObject, pFileContext ))
{
willCreateUnnamedStream = FALSE;
}
}
//
// It is an operation we may care about, go to the completion routine
// to handle what happened.
//
KeInitializeEvent( &waitEvent, SynchronizationEvent, FALSE );
IoCopyCurrentIrpStackLocationToNext(pIrp);
IoSetCompletionRoutine( pIrp,
SrStopProcessingCompletion,
&waitEvent,
TRUE,
TRUE,
TRUE );
IrpStatus = IoCallDriver(pExtension->pTargetDevice, pIrp);
//
// Wait for the completion routine to be called
//
if (STATUS_PENDING == IrpStatus)
{
NTSTATUS localStatus = KeWaitForSingleObject(&waitEvent, Executive, KernelMode, FALSE, NULL);
ASSERT(STATUS_SUCCESS == localStatus);
}
//=======================================================================
//
// The create operation is completed and we have re-syncronized back
// to the dispatch routine from the completion routine. Handle
// post-create operations.
//
//=======================================================================
//
// Load status of the operation. We need to remember this status in
// IrpStatus so that we can return it from this dispatch routine. Status
// we get the status of our event handling routines as we do our post-
// CREATE operation work.
//
IrpStatus = pIrp->IoStatus.Status;
//
// Handle the File Overwrite/Supersede cases
//
if ((CreateDisposition == FILE_OVERWRITE) ||
(CreateDisposition == FILE_OVERWRITE_IF) ||
(CreateDisposition == FILE_SUPERSEDE))
{
ASSERT(pFileContext != NULL);
ASSERT(pFileContext->UseCount == 1);
//
// See if it was successful (do not change this to NU_SUCCESS macro
// because STATUS_REPARSE is a success code)
//
if (STATUS_SUCCESS == IrpStatus)
{
//
// Now that the create is completed (and we have context state in
// the file object) insert this context into the context hash
// table. This routine will look to see if a context structure
// already exists for this file. If so, it will free this
// structure and return the one that already existed. It will
// properly ref count the context
//
ASSERT(pFileContext != NULL);
ASSERT(pFileContext->UseCount == 1);
//
// Check to see if we need to be concerned that this name was
// tunneled. If this context is temporary and we are not going
// to need to use this context to log any operations, there is
// not need to go through this extra work.
//
if (!FlagOn( pFileContext->Flags, CTXFL_Temporary ) ||
(FILE_CREATED == pIrp->IoStatus.Information))
{
//
// We are in a case where name tunneling could affect the
// correctness of the name we log.
//
eventStatus = SrCheckForNameTunneling( pExtension,
&pFileContext );
if (!NT_SUCCESS( eventStatus ))
{
goto AfterCompletionCleanup;
}
}
SrLinkContext( pExtension,
pFileObject,
&pFileContext );
SrTrace( CONTEXT_LOG, ("Sr!SrCreate: Link (%p) Event=%06x Fl=%03x Use=%d \"%.*S\"\n",
pFileContext,
SrEventStreamOverwrite|SrEventIsNotDirectory,
pFileContext->Flags,
pFileContext->UseCount,
(pFileContext->FileName.Length+
pFileContext->StreamNameLength)/
sizeof(WCHAR),
pFileContext->FileName.Buffer));
//
// Handle if the file was actually created
//
if (FILE_CREATED == pIrp->IoStatus.Information)
{
//
// If the file is interesting, log it
//
if (FlagOn(pFileContext->Flags,CTXFL_IsInteresting))
{
SrHandleEvent( pExtension,
((willCreateUnnamedStream) ?
SrEventFileCreate :
SrEventStreamCreate),
pFileObject,
pFileContext,
NULL,
NULL);
}
}
//
// make sure it didn't succeed when we thought it would fail
//
else if (!OverwriteInfo.RenamedFile &&
!OverwriteInfo.CopiedFile &&
OverwriteInfo.IgnoredFile )
{
//
// ouch, the caller's create worked, but we didn't think
// it would. this is a bad bug. nothing we can do now, as
// the file is gone.
//
ASSERT(!"sr!SrCreate(post complete): overwrite succeeded with NO BACKUP");
//
// trigger the failure notification to the service
//
SrNotifyVolumeError( pExtension,
&pFileContext->FileName,
STATUS_FILE_INVALID,
SrEventStreamOverwrite );
}
}
else
{
//
// handle it failing when we thought it would succeed
//
if (OverwriteInfo.RenamedFile)
{
//
// the call failed (or returned some weird info code)
// but we renamed the file! we need to fix it.
//
eventStatus = SrHandleOverwriteFailure( pExtension,
&pFileContext->FileName,
OverwriteInfo.CreateFileAttributes,
OverwriteInfo.pRenameInformation );
ASSERTMSG("sr!SrCreate(post complete): failed to correct a failed overwrite!\n", NT_SUCCESS(eventStatus));
}
//
// The create failed, the releaseContext below will free
// the structure since we didn't link it into any lists
//
}
}
//
// If it did not work, return now. Don't bother getting a context
//
else if ((STATUS_REPARSE == IrpStatus) ||
!NT_SUCCESS_NO_DBGBREAK(IrpStatus))
{
ASSERT(pFileContext == NULL);
}
//
// is this open for DELETE_ON_CLOSE? if so, handle the delete now,
// we won't have any other chance until MJ_CLEANUP, and it's hard
// to manipulate the object during cleanup. we do not perform any
// optimization on deletes in this manner. kernel32!deletefile does
// not use FILE_DELETE_ON_CLOSE so this should be rare if ever seen.
//
else if (FlagOn(CreateOptions, FILE_DELETE_ON_CLOSE))
{
//
// Get the context so we can see if this is a directory or not
//
ASSERT(pFileContext == NULL);
eventStatus = SrGetContext( pExtension,
pFileObject,
SrEventFileDelete,
&pFileContext );
if (!NT_SUCCESS(eventStatus))
{
goto AfterCompletionCleanup;
}
//
// Log the operation. If this is a file, we want to make sure that
// we don't try to rename the file into the store since it will be
// deleted when it is closed. On a directory delete, we don't have
// this problem since we only log an entry for a directory delete
// and don't need to actually backup anything.
//
SrHandleEvent( pExtension,
(FlagOn(pFileContext->Flags,CTXFL_IsDirectory) ?
SrEventDirectoryDelete :
(SrEventFileDelete | SrEventNoOptimization)) ,
pFileObject,
pFileContext,
NULL,
NULL );
}
//
// was a brand new file just created?
//
else if ((CreateDisposition == FILE_CREATE) ||
(pIrp->IoStatus.Information == FILE_CREATED))
{
ASSERT(pFileContext == NULL);
//
// LOG the create
//
SrHandleEvent( pExtension,
(FlagOn( CreateOptions, FILE_DIRECTORY_FILE ) ?
SrEventDirectoryCreate|SrEventIsDirectory :
(willCreateUnnamedStream ?
SrEventFileCreate|SrEventIsNotDirectory :
SrEventStreamCreate|SrEventIsNotDirectory)),
pFileObject,
NULL,
NULL,
NULL );
}
//
// This is for doing any cleanup that occured after we synced with
// the completion routine
//
AfterCompletionCleanup:
if (OverwriteInfo.pRenameInformation != NULL)
{
SR_FREE_POOL( OverwriteInfo.pRenameInformation,
SR_RENAME_BUFFER_TAG );
NULLPTR(OverwriteInfo.pRenameInformation);
}
if (NULL != pFileContext)
{
SrReleaseContext( pFileContext );
NULLPTR(pFileContext);
}
//
// Complete the request and return status
//
IoCompleteRequest( pIrp, IO_NO_INCREMENT );
return IrpStatus;
//
// We come here if we got an error before the completion routine. This
// means we don't need to wait for the completion routine.
//
CompleteTheIrpAndReturn:
IoSkipCurrentIrpStackLocation(pIrp);
return IoCallDriver(pExtension->pTargetDevice, pIrp);
} // SrCreate
/***************************************************************************++
Routine Description:
Handle SetSecurit IRPS
Arguments:
Return Value:
NTSTATUS - Status code.
--***************************************************************************/
NTSTATUS
SrSetSecurity(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP pIrp
)
{
PSR_DEVICE_EXTENSION pExtension;
PIO_STACK_LOCATION pIrpSp;
//
// < dispatch!
//
PAGED_CODE();
ASSERT(IS_VALID_DEVICE_OBJECT(DeviceObject));
ASSERT(IS_VALID_IRP(pIrp));
//
// Is this a function for our device (vs an attachee) .
//
if (DeviceObject == _globals.pControlDevice)
{
return SrMajorFunction(DeviceObject, pIrp);
}
//
// else it is a device we've attached to, grab our extension
//
ASSERT(IS_SR_DEVICE_OBJECT(DeviceObject));
pExtension = DeviceObject->DeviceExtension;
//
// See if logging is enabled and we don't care about this type of IO
// to the file systems' control device objects.
//
if (!SR_LOGGING_ENABLED(pExtension)||
SR_IS_FS_CONTROL_DEVICE(pExtension))
{
goto CompleteTheIrp;
}
//
// does this file have a name? skip unnamed files
//
pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
if (FILE_OBJECT_IS_NOT_POTENTIALLY_INTERESTING( pIrpSp->FileObject ) ||
FILE_OBJECT_DOES_NOT_HAVE_VPB( pIrpSp->FileObject ))
{
goto CompleteTheIrp;
}
//
// log the change
//
SrHandleEvent( pExtension,
SrEventAclChange,
pIrpSp->FileObject,
NULL,
NULL,
NULL);
//
// call the AttachedTo driver
//
CompleteTheIrp:
IoSkipCurrentIrpStackLocation(pIrp);
return IoCallDriver(pExtension->pTargetDevice, pIrp);
} // SrSetSecurity
/***************************************************************************++
Routine Description:
handles IRP_MJ_FILE_SYSTEM_CONTROL. the main thing we watch for here
are set reparse points to monitor volume mounts.
Arguments:
DeviceObject - the device object being processed
pIrp - the irp
Return Value:
NTSTATUS - Status code.
--***************************************************************************/
NTSTATUS
SrFsControl(
IN PDEVICE_OBJECT pDeviceObject,
IN PIRP pIrp
)
{
PSR_DEVICE_EXTENSION pExtension = NULL;
PIO_STACK_LOCATION pIrpSp;
NTSTATUS Status = STATUS_SUCCESS;
ULONG FsControlCode;
PIO_COMPLETION_ROUTINE pCompletionRoutine = NULL;
PAGED_CODE();
ASSERT(KeGetCurrentIrql() <= APC_LEVEL);
ASSERT(IS_VALID_DEVICE_OBJECT(pDeviceObject));
ASSERT(IS_VALID_IRP(pIrp));
//
// Is this a function for our control device object (vs an attachee)?
//
if (pDeviceObject == _globals.pControlDevice)
{
return SrMajorFunction(pDeviceObject, pIrp);
}
//
// else it is a device we've attached to , grab our extension
//
ASSERT(IS_SR_DEVICE_OBJECT(pDeviceObject));
pExtension = pDeviceObject->DeviceExtension;
//
// Begin by determining the minor function code for this file
// system control function.
//
pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
if ( pIrpSp->MinorFunction == IRP_MN_MOUNT_VOLUME )
{
if (SR_IS_SUPPORTED_REAL_DEVICE(pIrpSp->Parameters.MountVolume.Vpb->RealDevice)) {
//
// We mount devices even if we are disabled right now so that the
// filter can be enabled later and already be attached to each
// device at the appropriate location in the stack.
//
return SrFsControlMount( pDeviceObject, pExtension, pIrp );
} else {
//
// We don't care about this type of device so jump down to where
// we take SR out of the stack and pass the IO through.
//
goto SrFsControl_Skip;
}
}
else if (pIrpSp->MinorFunction == IRP_MN_USER_FS_REQUEST)
{
//
// See if logging is enabled and we don't care about this type of IO
// to the file systems' control device objects.
//
if (!SR_LOGGING_ENABLED(pExtension) ||
SR_IS_FS_CONTROL_DEVICE(pExtension))
{
goto SrFsControl_Skip;
}
FsControlCode = pIrpSp->Parameters.FileSystemControl.FsControlCode;
switch (FsControlCode) {
case FSCTL_SET_REPARSE_POINT:
case FSCTL_DELETE_REPARSE_POINT:
//
// In this case, we need to do work after the IO has completed
// and we have synchronized back to this thread, so
// SrFsControlReparsePoint contains the call to IoCallDriver and
// we just want to return the status of this routine.
//
return SrFsControlReparsePoint(pExtension, pIrp);
case FSCTL_LOCK_VOLUME:
SrTrace( NOTIFY, ("sr!SrFsControl:FSCTL_LOCK_VOLUME(%wZ)\n",
pExtension->pNtVolumeName ));
SrFsControlLockOrDismount(pExtension, pIrp);
//
// Jump down to where take SR out of the stack and pass this
// IO through.
//
goto SrFsControl_Skip;
case FSCTL_DISMOUNT_VOLUME:
SrTrace( NOTIFY, ("sr!SrFsControl:FSCTL_DISMOUNT_VOLUME(%wZ)\n",
pExtension->pNtVolumeName ));
//
// First, disable the log while we shutdown the log context
// and wait for the filesystem to handle the dismount. If
// the dismount fails, we will reenable the volume.
//
pExtension->Disabled = TRUE;
//
// Stop the logging on the volume.
//
SrFsControlLockOrDismount(pExtension, pIrp);
//
// We need to see the completion of this operation so we
// can see the final status. If we see that the dismount has
// failed, we need to reenable the volume.
//
pCompletionRoutine = SrDismountCompletion;
goto SrFsControl_SetCompletion;
case FSCTL_WRITE_RAW_ENCRYPTED:
SrFsControlWriteRawEncrypted(pExtension, pIrp);
//
// Jump down to where take SR out of the stack and pass this
// IO through.
//
goto SrFsControl_Skip;
case FSCTL_SET_SPARSE:
SrFsControlSetSparse( pExtension, pIrp );
//
// Jump down to where take SR out of the stack and pass this
// IO through.
//
goto SrFsControl_Skip;
default:
//
// For all other FSCTL just skip the current IRP stack location.
//
//
// Jump down to where take SR out of the stack and pass this
// IO through.
//
goto SrFsControl_Skip;
} // switch (FsControlCode)
} // else if (pIrpSp->MinorFunction == IRP_MN_USER_FS_REQUEST)
else
{
//
// We don't care about any other operations so simply get out of
// the stack.
//
goto SrFsControl_Skip;
}
SrFsControl_SetCompletion:
ASSERT( pCompletionRoutine != NULL );
IoCopyCurrentIrpStackLocationToNext(pIrp);
IoSetCompletionRoutine( pIrp,
pCompletionRoutine,
NULL, // CompletionContext
TRUE,
TRUE,
TRUE );
return IoCallDriver( pExtension->pTargetDevice, pIrp );
SrFsControl_Skip:
ASSERT( pCompletionRoutine == NULL );
IoSkipCurrentIrpStackLocation( pIrp );
return IoCallDriver( pExtension->pTargetDevice, pIrp );
} // SrFsControl
/***************************************************************************++
Routine Description:
Arguments:
Return Value:
NTSTATUS - Status code.
--***************************************************************************/
NTSTATUS
SrFsControlReparsePoint (
IN PSR_DEVICE_EXTENSION pExtension,
IN PIRP pIrp
)
{
PREPARSE_DATA_BUFFER pReparseHeader;
PUNICODE_STRING pMountVolume = NULL;
PFILE_OBJECT pFileObject = NULL;
ULONG TotalLength;
PIO_STACK_LOCATION pIrpSp;
KEVENT EventToWaitOn;
NTSTATUS IrpStatus;
NTSTATUS eventStatus;
ULONG FsControlCode;
PSR_STREAM_CONTEXT pFileContext = NULL;
BOOLEAN isFile = FALSE;
#if DBG
//
// This is to verify that the original request gets the same error
// we got when querying the reparse point.
//
BOOLEAN ExpectError = FALSE;
NTSTATUS ExpectedErrorCode = STATUS_SUCCESS;
#endif
PAGED_CODE();
pIrpSp = IoGetCurrentIrpStackLocation( pIrp );
FsControlCode = pIrpSp->Parameters.FileSystemControl.FsControlCode;
//
// See if it has a name
//
if (FILE_OBJECT_IS_NOT_POTENTIALLY_INTERESTING( pIrpSp->FileObject ) ||
FILE_OBJECT_DOES_NOT_HAVE_VPB( pIrpSp->FileObject ))
{
goto SrFsControlReparsePoint_SkipFilter;
}
//
// Get the context now so we can determine if this is a directory or not
//
eventStatus = SrGetContext( pExtension,
pIrpSp->FileObject,
SrEventInvalid,
&pFileContext );
if (!NT_SUCCESS( eventStatus ))
{
goto SrFsControlReparsePoint_SkipFilter;
}
//
// If it is not a directory, return
//
if (!FlagOn(pFileContext->Flags,CTXFL_IsDirectory))
{
isFile = TRUE;
goto SrFsControlReparsePoint_SkipFilter;
}
//
// is there enough space for the header?
//
pReparseHeader = pIrp->AssociatedIrp.SystemBuffer;
if (pReparseHeader == NULL ||
pIrpSp->Parameters.DeviceIoControl.InputBufferLength <
REPARSE_DATA_BUFFER_HEADER_SIZE)
{
goto SrFsControlReparsePoint_SkipFilter;
}
//
// is this a mount point?
//
if (pReparseHeader->ReparseTag != IO_REPARSE_TAG_MOUNT_POINT)
{
goto SrFsControlReparsePoint_SkipFilter;
}
//
// keep a copy for post processing
//
pFileObject = pIrpSp->FileObject;
ObReferenceObject(pFileObject);
//
// now let's see what we have to do
//
if (FsControlCode == FSCTL_SET_REPARSE_POINT)
{
//
// If there is no data this is invalid
//
if (pReparseHeader->ReparseDataLength <= 0)
{
goto SrFsControlReparsePoint_SkipFilter;
}
//
// is there enough space for the header + data?
// (according to him - not trusted)
//
//
if (pIrpSp->Parameters.DeviceIoControl.InputBufferLength <
pReparseHeader->ReparseDataLength + ((ULONG)REPARSE_DATA_BUFFER_HEADER_SIZE))
{
goto SrFsControlReparsePoint_SkipFilter;
}
//
// did he lie about the length of the string?
//
TotalLength = DIFF( (((PUCHAR)pReparseHeader->MountPointReparseBuffer.PathBuffer)
+ pReparseHeader->MountPointReparseBuffer.SubstituteNameLength)
- ((PUCHAR)pReparseHeader) );
if (TotalLength >
pIrpSp->Parameters.DeviceIoControl.InputBufferLength)
{
goto SrFsControlReparsePoint_SkipFilter;
}
//
// grab the volume name
//
eventStatus = SrAllocateFileNameBuffer( pReparseHeader->MountPointReparseBuffer.SubstituteNameLength,
&pMountVolume );
if (!NT_SUCCESS(eventStatus))
{
goto SrFsControlReparsePoint_VolumeError;
}
RtlCopyMemory( pMountVolume->Buffer,
pReparseHeader->MountPointReparseBuffer.PathBuffer,
pReparseHeader->MountPointReparseBuffer.SubstituteNameLength );
pMountVolume->Length = pReparseHeader->MountPointReparseBuffer.SubstituteNameLength;
}
else
{
ASSERT(FsControlCode == FSCTL_DELETE_REPARSE_POINT);
//
// it's a delete, get the old mount location for logging
//
eventStatus = SrGetMountVolume( pFileObject,
&pMountVolume );
if (eventStatus == STATUS_INSUFFICIENT_RESOURCES)
{
//
// Must notify service of volume error and shut down
// before passing the IO through.
//
goto SrFsControlReparsePoint_VolumeError;
}
#if DBG
if (!NT_SUCCESS_NO_DBGBREAK( eventStatus ))
{
ExpectError = TRUE;
ExpectedErrorCode = eventStatus;
goto SrFsControlReparsePoint_SkipFilter;
}
#else
if (!NT_SUCCESS( eventStatus ))
{
goto SrFsControlReparsePoint_SkipFilter;
}
#endif
}
//
// If we get to this point, this is a reparse point we care about
// so set a completion routine so that we can see the result of this
// operation.
//
KeInitializeEvent( &EventToWaitOn, NotificationEvent, FALSE );
IoCopyCurrentIrpStackLocationToNext( pIrp );
IoSetCompletionRoutine( pIrp,
SrStopProcessingCompletion,
&EventToWaitOn,
TRUE,
TRUE,
TRUE );
IrpStatus = IoCallDriver( pExtension->pTargetDevice, pIrp );
if (STATUS_PENDING == IrpStatus )
{
NTSTATUS localStatus = KeWaitForSingleObject( &EventToWaitOn,
Executive,
KernelMode,
FALSE,
NULL );
ASSERT(STATUS_SUCCESS == localStatus);
}
//
// The Irp is still good since we have returned
// STATUS_MORE_PROCESSING_REQUIRED from the completion
// routine.
//
//
// If the status in the IRP was STATUS_PENDING,
// we want to change the status to STATUS_SUCCESS
// since we have just performed the necessary synchronization
// with the orginating thread.
//
if (pIrp->IoStatus.Status == STATUS_PENDING)
{
ASSERT(!"I want to see if this ever happens");
pIrp->IoStatus.Status = STATUS_SUCCESS;
}
IrpStatus = pIrp->IoStatus.Status;
//
// We are done with the Irp, so complete the Irp.
//
IoCompleteRequest( pIrp, IO_NO_INCREMENT );
//
// Now these pointers are no longer valid.
//
NULLPTR(pIrp);
NULLPTR(pIrpSp);
//
// Check to make sure the operation successfully
// completed.
//
if (!NT_SUCCESS_NO_DBGBREAK(IrpStatus))
{
goto SrFsControlReparsePoint_Exit;
}
//
// The reparse point change occurred successfully, so
// log the reparse point change.
//
ASSERT(pFileObject != NULL);
ASSERT(pFileContext != NULL);
ASSERT(FlagOn(pFileContext->Flags,CTXFL_IsDirectory));
ASSERT(FsControlCode == FSCTL_DELETE_REPARSE_POINT ||
FsControlCode == FSCTL_SET_REPARSE_POINT);
SrHandleEvent( pExtension,
((FSCTL_SET_REPARSE_POINT == FsControlCode) ?
SrEventMountCreate :
SrEventMountDelete),
pFileObject,
pFileContext,
NULL,
pMountVolume );
goto SrFsControlReparsePoint_Exit;
SrFsControlReparsePoint_VolumeError:
//
// We've gotten a volume error sometime before we passed the IRP
// along to the base file system. Do the right thing to shut down
// the volume logging.
//
SrNotifyVolumeError( pExtension,
NULL,
eventStatus,
SrNotificationVolumeError );
//
// We will now fall through to skip our filter as we pass the IO
// down to the remaining filters and file system.
//
SrFsControlReparsePoint_SkipFilter:
//
// If this was a file, we need to clear out our context on this file
// since we don't want to monitor files with Reparse Points. On the
// next access to this file, we will requery this information.
//
if (isFile)
{
ASSERT( pFileContext != NULL );
SrDeleteContext( pExtension, pFileContext );
}
//
// We don't need a completion routine, call to next driver
//
IoSkipCurrentIrpStackLocation( pIrp );
IrpStatus = IoCallDriver( pExtension->pTargetDevice, pIrp );
NULLPTR(pIrp);
NULLPTR(pIrpSp);
ASSERT(!ExpectError || ((ExpectedErrorCode == IrpStatus) ||
(STATUS_PENDING == IrpStatus )));
//
// Cleanup state
//
SrFsControlReparsePoint_Exit:
if (NULL != pMountVolume)
{
SrFreeFileNameBuffer( pMountVolume );
NULLPTR(pMountVolume);
}
if (NULL != pFileObject)
{
ObDereferenceObject( pFileObject );
NULLPTR(pFileObject);
}
if (NULL != pFileContext)
{
SrReleaseContext( pFileContext );
NULLPTR(pFileContext);
}
return IrpStatus;
}
/***************************************************************************++
Routine Description:
Arguments:
Return Value:
NTSTATUS - Status code.
--***************************************************************************/
NTSTATUS
SrFsControlMount (
IN PDEVICE_OBJECT pDeviceObject,
IN PSR_DEVICE_EXTENSION pExtension,
IN PIRP pIrp
)
{
PIO_STACK_LOCATION pIrpSp;
PDEVICE_OBJECT pNewDeviceObject = NULL;
KEVENT EventToWaitOn;
PVPB pVpb = NULL;
PDEVICE_OBJECT pRealDevice;
NTSTATUS Status;
BOOLEAN ReleaseLock = FALSE;
PAGED_CODE();
ASSERT( SR_IS_FS_CONTROL_DEVICE(pExtension) );
//
// create our device we are going to attach to this new volume
//
pIrpSp = IoGetCurrentIrpStackLocation( pIrp );
pRealDevice = pIrpSp->Parameters.MountVolume.Vpb->RealDevice;
Status = SrCreateAttachmentDevice( pRealDevice,
pDeviceObject,
&pNewDeviceObject );
if (!NT_SUCCESS( Status ))
{
IoSkipCurrentIrpStackLocation( pIrp );
return IoCallDriver( pExtension->pTargetDevice, pIrp );
}
//
// If we get here, we need to set our completion routine then
// wait for it to signal us before we continue with the post processing
// of the mount.
//
KeInitializeEvent( &EventToWaitOn, NotificationEvent, FALSE );
IoCopyCurrentIrpStackLocationToNext(pIrp);
IoSetCompletionRoutine( pIrp,
SrStopProcessingCompletion,
&EventToWaitOn, // CompletionContext
TRUE,
TRUE,
TRUE );
pIrpSp->Parameters.MountVolume.DeviceObject =
pIrpSp->Parameters.MountVolume.Vpb->RealDevice;
Status = IoCallDriver( pExtension->pTargetDevice, pIrp );
if (STATUS_PENDING == Status)
{
NTSTATUS localStatus = KeWaitForSingleObject( &EventToWaitOn,
Executive,
KernelMode,
FALSE,
NULL );
ASSERT( NT_SUCCESS( localStatus ) );
}
//
// skip out if the mount failed
//
if (!NT_SUCCESS_NO_DBGBREAK(pIrp->IoStatus.Status))
{
goto SrFsControlMount_Error;
}
//
// Note that the VPB must be picked up from the real device object
// so that we can see the DeviceObject that the file system created
// to represent this newly mounted volume.
//
pVpb = pRealDevice->Vpb;
ASSERT(pVpb != NULL);
//
// SrFsControl made sure that we support this volume type
//
ASSERT(SR_IS_SUPPORTED_VOLUME(pVpb));
//
// Are we already attached to this device? We need to hold the
// AttachToVolumeLock while we do this to ensure that we don't hit
// a race condition with once of the other paths that can cause us to
// attach to a volume.
//
SrAcquireAttachToVolumeLock();
ReleaseLock = TRUE;
if (NT_SUCCESS( pIrp->IoStatus.Status ) &&
(SrGetFilterDevice(pVpb->DeviceObject) == NULL))
{
//
// now attach to the new volume
//
Status = SrAttachToDevice( pVpb->RealDevice,
pVpb->DeviceObject,
pNewDeviceObject,
NULL );
if (NT_SUCCESS(Status))
{
goto SrFsControlMount_Exit;
}
}
SrFsControlMount_Error:
ASSERT( pNewDeviceObject != NULL );
SrDeleteAttachmentDevice( pNewDeviceObject );
SrFsControlMount_Exit:
if (ReleaseLock)
{
SrReleaseAttachToVolumeLock();
}
Status = pIrp->IoStatus.Status;
IoCompleteRequest( pIrp, IO_NO_INCREMENT );
return Status;
}
/***************************************************************************++
Routine Description:
Arguments:
Return Value:
NTSTATUS - Status code.
--***************************************************************************/
NTSTATUS
SrFsControlLockOrDismount (
IN PSR_DEVICE_EXTENSION pExtension,
IN PIRP pIrp
)
{
NTSTATUS Status = STATUS_SUCCESS;
UNREFERENCED_PARAMETER( pIrp );
PAGED_CODE();
try {
//
// close our log file handle on this volume , it's being
// locked. it's ok if the lock attempt fails, we will open
// our handle again automatically since DriveChecked is also
// being cleared.
//
SrAcquireActivityLockExclusive( pExtension);
if (pExtension->pLogContext != NULL)
{
Status = SrLogStop( pExtension, TRUE );
CHECK_STATUS( Status );
}
} finally {
SrReleaseActivityLock(pExtension);
}
return Status;
}
/***************************************************************************++
Routine Description:
Arguments:
Return Value:
NTSTATUS - Status code.
--***************************************************************************/
VOID
SrFsControlWriteRawEncrypted (
IN PSR_DEVICE_EXTENSION pExtension,
IN PIRP pIrp
)
{
PIO_STACK_LOCATION pIrpSp;
NTSTATUS Status;
PSR_STREAM_CONTEXT pFileContext = NULL;
PAGED_CODE();
pIrpSp = IoGetCurrentIrpStackLocation( pIrp );
if (FILE_OBJECT_IS_NOT_POTENTIALLY_INTERESTING( pIrpSp->FileObject ) ||
FILE_OBJECT_DOES_NOT_HAVE_VPB( pIrpSp->FileObject )) {
return;
}
//
// Look up the context for this file object so that we can figure out
// if this is a file or a directory. If this is a directory, the
// file system will fail the operation, so there is no need to try to
// back it up.
//
Status = SrGetContext( pExtension,
pIrpSp->FileObject,
SrEventStreamChange,
&pFileContext );
if (!NT_SUCCESS( Status ))
{
//
// We hit some error trying to get the context. If this should
// generate a volume error, it has already been taken care of inside
// SrGetContext. Otherwise, this just means that the actual operation
// will fail, so there is no work for us to do here.
//
return;
}
ASSERT( NULL != pFileContext );
//
// Make sure that we have an interesting file. This operation
// is invalid on directories.
//
if (FlagOn( pFileContext->Flags, CTXFL_IsInteresting )&&
!FlagOn( pFileContext->Flags, CTXFL_IsDirectory ))
{
SrHandleEvent( pExtension,
SrEventStreamChange,
pIrpSp->FileObject,
pFileContext,
NULL,
NULL );
}
//
// We are all done with this context, so now release it.
//
ASSERT( NULL != pFileContext );
SrReleaseContext( pFileContext );
NULLPTR(pFileContext);
return;
}
/***************************************************************************++
Routine Description:
When a file is set to sparse, we need to clear out our context for this
file. On the next interesting operation for this file, we will regenerate
a correct context.
This work is done since SR doesn't want to monitor files that are SPARSE.
Arguments:
Return Value:
None.
--***************************************************************************/
VOID
SrFsControlSetSparse (
IN PSR_DEVICE_EXTENSION pExtension,
IN PIRP pIrp
)
{
PIO_STACK_LOCATION irpSp;
PFILE_OBJECT pFileObject;
PSR_STREAM_CONTEXT pFileContext = NULL;
PAGED_CODE();
irpSp = IoGetCurrentIrpStackLocation( pIrp );
pFileObject = irpSp->FileObject;
pFileContext = SrFindExistingContext( pExtension, pFileObject );
if (pFileContext != NULL)
{
SrDeleteContext( pExtension, pFileContext );
SrReleaseContext( pFileContext );
}
return;
}
/***************************************************************************++
Routine Description:
handles IRP_MJ_PNP. SR needs to close its handle to the log when it sees
that the volume is going away and reopen it when the drive reappears.
Arguments:
DeviceObject - the device object being processed
pIrp - the irp
Return Value:
NTSTATUS - Status code.
--***************************************************************************/
NTSTATUS
SrPnp (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
{
PSR_DEVICE_EXTENSION pExtension;
PIO_STACK_LOCATION irpSp;
PAGED_CODE();
ASSERT(IS_VALID_DEVICE_OBJECT(DeviceObject));
ASSERT(IS_VALID_IRP(Irp));
//
// Get this driver out of the driver stack and get to the next driver as
// quickly as possible.
//
//
// Is this a function for our device (vs an attachee) .
//
if (DeviceObject == _globals.pControlDevice)
{
return SrMajorFunction(DeviceObject, Irp);
}
//
// else it is a device we've attached to, grab our extension
//
ASSERT( IS_SR_DEVICE_OBJECT( DeviceObject ) );
pExtension = DeviceObject->DeviceExtension;
irpSp = IoGetCurrentIrpStackLocation( Irp );
switch ( irpSp->MinorFunction ) {
case IRP_MN_QUERY_REMOVE_DEVICE:
SrTrace( PNP, ( "SR!SrPnp: QUERY_REMOVE_DEVICE [%wZ]\n",
pExtension->pNtVolumeName ) );
//
// If this is a SURPRISE_REMOVAL, the device has already gone away
// and we are not going to see any more operations to this volume, but
// the OS won't call us to detach and delete our device object until
// all the handles that are outstanding on this volume are closed. Do
// our part by closing down the handle to our log.
//
try {
SrAcquireActivityLockExclusive( pExtension );
pExtension->Disabled = TRUE;
if (pExtension->pLogContext != NULL)
{
SrLogStop( pExtension, TRUE );
}
} finally {
SrReleaseActivityLock( pExtension );
}
break;
case IRP_MN_SURPRISE_REMOVAL:
SrTrace( PNP, ( "SR!SrPnp: SURPRISE_REMOVAL [%wZ]\n",
pExtension->pNtVolumeName ) );
//
// If this is a SURPRISE_REMOVAL, the device has already gone away
// and we are not going to see any more operations to this volume, but
// the OS won't call us to detach and delete our device object until
// all the handles that are outstanding on this volume are closed. Do
// our part by closing down the handle to our log.
//
try {
SrAcquireActivityLockExclusive( pExtension );
pExtension->Disabled = TRUE;
if (pExtension->pLogContext != NULL)
{
SrLogStop( pExtension, TRUE );
}
} finally {
SrReleaseActivityLock( pExtension );
}
break;
case IRP_MN_CANCEL_REMOVE_DEVICE:
SrTrace( PNP, ( "SR!SrPnp: CANCEL_REMOVE_DEVICE [%wZ]\n",
pExtension->pNtVolumeName ) );
//
// The removal is not going to happen, so reenable the device and
// the log will be restarted on the next interesting operation.
//
if (pExtension->Disabled) {
try {
SrAcquireActivityLockExclusive( pExtension );
pExtension->Disabled = FALSE;
} finally {
SrReleaseActivityLock( pExtension );
}
}
break;
default:
//
// All PNP minor codes we don't care about, so just pass
// the IO through.
//
break;
}
//
// If we have gotten here, we don't need to wait to see the result of this
// operation, so just call the appropriate file system driver with
// the request.
//
IoSkipCurrentIrpStackLocation( Irp );
return IoCallDriver( pExtension->pTargetDevice, Irp );
}
/***************************************************************************++
Routine Description:
this does the actual work for creating a new restore point.
this is called by the user mode SrCreateRestorePoint .
this IOCTL is METHOD_BUFFERED !
Arguments:
pIrp - the irp
pIrpSp - the irp stack
Return Value:
NTSTATUS - Status code.
--***************************************************************************/
NTSTATUS
SrCreateRestorePointIoctl(
IN PIRP pIrp,
IN PIO_STACK_LOCATION pIrpSp
)
{
NTSTATUS Status;
PUNICODE_STRING pVolumeName = NULL;
PLIST_ENTRY pListEntry;
PSR_DEVICE_EXTENSION pExtension;
BOOLEAN releaseActivityLocks = TRUE;
PSR_DEVICE_EXTENSION pSystemVolumeExtension = NULL;
PAGED_CODE();
ASSERT(IS_VALID_IRP(pIrp));
SrTrace( IOCTL, ("SR!SrCreateRestorePointIoctl -- ENTER\n") );
try {
//
// Grab the device extension list lock since we are
// going to have to pause all the volume activity.
//
SrAcquireDeviceExtensionListLockShared();
//
// We've got the device extension lock, so now try to pause
// activity on all the volumes.
//
Status = SrPauseVolumeActivity();
if (!NT_SUCCESS( Status )) {
releaseActivityLocks = FALSE;
leave;
}
try {
SrAcquireGlobalLockExclusive();
//
// make sure we've loaded the config file
//
if (!_globals.FileConfigLoaded)
{
Status = SrReadConfigFile();
if (!NT_SUCCESS(Status))
leave;
_globals.FileConfigLoaded = TRUE;
}
} finally {
SrReleaseGlobalLock();
}
if (!NT_SUCCESS( Status )) {
leave;
}
//
// Clear the volumes' DriveChecked flag so that we check the volumes
// again. this will create the restore point directories.
//
// also stop logging on all volumes. new log files will be created in
// the restore locations.
//
// We need to do this before we increment the current restore point
// counter.
//
for (pListEntry = _globals.DeviceExtensionListHead.Flink;
pListEntry != &_globals.DeviceExtensionListHead;
pListEntry = pListEntry->Flink)
{
pExtension = CONTAINING_RECORD( pListEntry,
SR_DEVICE_EXTENSION,
ListEntry );
ASSERT(IS_VALID_SR_DEVICE_EXTENSION(pExtension));
//
// We only have to do work if this is a volume device object,
// not if this is a device object that is attached to a file
// system's control device object.
//
if (FlagOn( pExtension->FsType, SrFsControlDeviceObject ))
{
continue;
}
//
// stop logging for this volume
//
if (pExtension->pLogContext != NULL)
{
Status = SrLogStop( pExtension, FALSE );
CHECK_STATUS( Status );
}
//
// make sure to enable all of the volumes again. If the user
// has disabled the volume, this is tracked in the blob info.
//
pExtension->Disabled = FALSE;
//
// make sure the drive is checked again for the new restore point
//
pExtension->DriveChecked = FALSE;
//
// reset the byte count, it's a new restore point
//
pExtension->BytesWritten = 0;
//
// clear out the backup history so that we start backing
// up files again
//
Status = SrResetBackupHistory(pExtension, NULL, 0, SrEventInvalid);
if (!NT_SUCCESS(Status))
leave;
}
try {
SrAcquireGlobalLockExclusive();
//
// bump up the restore point number
//
_globals.FileConfig.CurrentRestoreNumber += 1;
SrTrace( INIT, ("sr!SrCreateRestorePointIoctl: RestorePoint=%d\n",
_globals.FileConfig.CurrentRestoreNumber ));
//
// save out the config file
//
Status = SrWriteConfigFile();
if (!NT_SUCCESS(Status))
leave;
} finally {
SrReleaseGlobalLock();
}
if (!NT_SUCCESS( Status )) {
leave;
}
//
// allocate space for a filename
//
Status = SrAllocateFileNameBuffer(SR_MAX_FILENAME_LENGTH, &pVolumeName);
if (!NT_SUCCESS(Status))
leave;
//
// get the location of the system volume
//
Status = SrGetSystemVolume( pVolumeName,
&pSystemVolumeExtension,
SR_FILENAME_BUFFER_LENGTH );
//
// This should only happen if there was some problem with SR attaching
// in the mount path. This check was added to make SR more robust to
// busted filters above us. If other filters cause us to get mounted,
// we won't have an extension to return here. While those filters are
// broken, we don't want to AV.
//
if (pSystemVolumeExtension == NULL)
{
Status = STATUS_UNSUCCESSFUL;
leave;
}
if (!NT_SUCCESS(Status))
leave;
ASSERT( IS_VALID_SR_DEVICE_EXTENSION( pSystemVolumeExtension ) );
//
// create the restore point dir on the system volume
//
Status = SrCreateRestoreLocation( pSystemVolumeExtension );
if (!NT_SUCCESS(Status))
leave;
//
// return the restore point number
//
if (pIrpSp->Parameters.DeviceIoControl.OutputBufferLength >=
sizeof(ULONG))
{
RtlCopyMemory( pIrp->AssociatedIrp.SystemBuffer,
&_globals.FileConfig.CurrentRestoreNumber,
sizeof(ULONG) );
pIrp->IoStatus.Information = sizeof(ULONG);
}
//
// all done
//
} finally {
Status = FinallyUnwind(SrCreateRestorePointIoctl, Status);
if (releaseActivityLocks) {
SrResumeVolumeActivity ();
}
SrReleaseDeviceExtensionListLock();
if (pVolumeName != NULL)
{
SrFreeFileNameBuffer(pVolumeName);
pVolumeName = NULL;
}
}
SrTrace( IOCTL, ("SR!SrCreateRestorePointIoctl -- EXIT -- status 0x%08lx\n",
Status));
//
// At this point if Status != PENDING, the ioctl wrapper will
// complete pIrp
//
RETURN(Status);
} // SrCreateRestorePointIoctl
/***************************************************************************++
Routine Description:
this does the actual work for getting the next seq number from the filter
this is called by the user mode SrGetNextSequenceNum .
this IOCTL is METHOD_BUFFERED !
Arguments:
pIrp - the irp
pIrpSp - the irp stack
Return Value:
NTSTATUS - Status code.
--***************************************************************************/
NTSTATUS
SrGetNextSeqNumIoctl(
IN PIRP pIrp,
IN PIO_STACK_LOCATION pIrpSp
)
{
NTSTATUS Status;
PAGED_CODE();
ASSERT(IS_VALID_IRP(pIrp));
SrTrace( IOCTL, ("SR!SrGetNextSeqNumIoctl -- ENTER\n") );
try
{
INT64 SeqNum = 0;
//
// grab the global lock
//
SrAcquireGlobalLockExclusive();
//
// make sure we've loaded the config file
//
if (!_globals.FileConfigLoaded)
{
Status = SrReadConfigFile();
if (!NT_SUCCESS(Status))
leave;
_globals.FileConfigLoaded = TRUE;
}
//
// Get the next sequence number
//
Status = SrGetNextSeqNumber(&SeqNum);
if (NT_SUCCESS(Status))
{
//
// return the restore point number
//
if (pIrpSp->Parameters.DeviceIoControl.OutputBufferLength >=
sizeof(INT64))
{
RtlCopyMemory( pIrp->AssociatedIrp.SystemBuffer,
&SeqNum,
sizeof(INT64) );
pIrp->IoStatus.Information = sizeof(INT64);
}
}
}
finally
{
Status = FinallyUnwind(SrGetNextSeqNumIoctl, Status);
SrReleaseGlobalLock();
}
SrTrace( IOCTL, ("SR!SrGetNextSeqNumIoctl -- EXIT -- status 0x%08lx\n",
Status) );
//
// At this point if Status != PENDING, the ioctl wrapper will
// complete pIrp
//
RETURN(Status);
} // SrGetNextSeqNumIoctl
NTSTATUS
SrReloadConfigurationIoctl(
IN PIRP pIrp,
IN PIO_STACK_LOCATION IrpSp
)
{
NTSTATUS Status = STATUS_UNSUCCESSFUL;
PUNICODE_STRING pFileName = NULL;
ULONG CharCount;
PLIST_ENTRY pListEntry;
PSR_DEVICE_EXTENSION pExtension;
BOOLEAN releaseDeviceExtensionListLock = FALSE;
PSR_DEVICE_EXTENSION pSystemVolumeExtension = NULL;
UNREFERENCED_PARAMETER( pIrp );
UNREFERENCED_PARAMETER( IrpSp );
PAGED_CODE();
SrTrace( IOCTL, ("SR!SrReloadConfigurationIoctl -- ENTER\n") );
try {
//
// allocate space for a filename
//
Status = SrAllocateFileNameBuffer(SR_MAX_FILENAME_LENGTH, &pFileName);
if (!NT_SUCCESS(Status))
leave;
//
// get the location of the system volume
//
Status = SrGetSystemVolume( pFileName,
&pSystemVolumeExtension,
SR_FILENAME_BUFFER_LENGTH );
//
// This should only happen if there was some problem with SR attaching
// in the mount path. This check was added to make SR more robust to
// busted filters above us. If other filters cause us to get mounted,
// we won't have an extension to return here. While those filters are
// broken, we don't want to AV.
//
if (pSystemVolumeExtension == NULL)
{
Status = STATUS_UNSUCCESSFUL;
leave;
}
if (!NT_SUCCESS(Status))
leave;
ASSERT( IS_VALID_SR_DEVICE_EXTENSION( pSystemVolumeExtension ) );
//
// load the file list config data
//
CharCount = swprintf( &pFileName->Buffer[pFileName->Length/sizeof(WCHAR)],
RESTORE_FILELIST_LOCATION,
_globals.MachineGuid );
pFileName->Length += (USHORT)CharCount * sizeof(WCHAR);
Status = SrReloadLookupBlob( pFileName,
pSystemVolumeExtension->pTargetDevice,
&_globals.BlobInfo );
if (!NT_SUCCESS(Status))
{
leave;
}
//
// flush our volume configuration, it needs to be reconfigured as to
// which drives are enabled or not
//
//
// loop over all volumes reseting their disabled config
//
SrAcquireDeviceExtensionListLockShared();
releaseDeviceExtensionListLock = TRUE;
for (pListEntry = _globals.DeviceExtensionListHead.Flink;
pListEntry != &_globals.DeviceExtensionListHead;
pListEntry = pListEntry->Flink)
{
pExtension = CONTAINING_RECORD( pListEntry,
SR_DEVICE_EXTENSION,
ListEntry );
ASSERT(IS_VALID_SR_DEVICE_EXTENSION(pExtension));
try {
SrAcquireActivityLockExclusive( pExtension );
pExtension->Disabled = FALSE;
} finally {
SrReleaseActivityLock( pExtension );
}
}
} finally {
//
// check for unhandled exceptions
//
Status = FinallyUnwind(SrReloadConfigurationIoctl, Status);
if (releaseDeviceExtensionListLock) {
SrReleaseDeviceExtensionListLock();
}
if (pFileName != NULL)
{
SrFreeFileNameBuffer(pFileName);
pFileName = NULL;
}
}
SrTrace( IOCTL, ("SR!SrReloadConfigurationIoctl -- EXIT -- status 0x%08lx\n",
Status));
RETURN(Status);
} // SrReloadConfigurationIoctl
NTSTATUS
SrSwitchAllLogsIoctl(
IN PIRP pIrp,
IN PIO_STACK_LOCATION IrpSp
)
{
NTSTATUS Status;
PAGED_CODE();
UNREFERENCED_PARAMETER( pIrp );
UNREFERENCED_PARAMETER( IrpSp );
SrTrace( IOCTL, ("SR!SrSwitchAllLogsIoctl -- ENTER\n") );
Status = SrLoggerSwitchLogs(_globals.pLogger);
SrTrace( IOCTL, ("SR!SrSwitchAllLogsIoctl -- EXIT -- status 0x%08lx\n",
Status));
RETURN(Status);
} // SrSwitchAllLogsIoctl
NTSTATUS
SrDisableVolumeIoctl(
IN PIRP pIrp,
IN PIO_STACK_LOCATION pIrpSp
)
{
NTSTATUS Status;
PSR_DEVICE_EXTENSION pExtension;
UNICODE_STRING VolumeName;
PAGED_CODE();
ASSERT(IS_VALID_IRP(pIrp));
SrTrace( IOCTL, ("SR!SrDisableVolumeIoctl -- ENTER\n") );
if (pIrp->AssociatedIrp.SystemBuffer == NULL ||
pIrpSp->Parameters.DeviceIoControl.InputBufferLength <= sizeof(WCHAR) ||
pIrpSp->Parameters.DeviceIoControl.InputBufferLength > SR_MAX_FILENAME_LENGTH)
{
RETURN ( STATUS_INVALID_DEVICE_REQUEST );
}
//
// get the volume name out
//
VolumeName.Buffer = pIrp->AssociatedIrp.SystemBuffer;
VolumeName.Length = (USHORT)(pIrpSp->Parameters.DeviceIoControl.InputBufferLength - sizeof(WCHAR));
VolumeName.MaximumLength = VolumeName.Length;
//
// attach to it. it will check for a previous attachement and do the
// right thing .
//
Status = SrAttachToVolumeByName(&VolumeName, &pExtension);
if (!NT_SUCCESS(Status)) {
RETURN( Status );
}
ASSERT(IS_VALID_SR_DEVICE_EXTENSION(pExtension));
try {
SrAcquireActivityLockExclusive( pExtension );
//
// now turn it off
//
pExtension->Disabled = TRUE;
//
// stop logging on the volume
//
if (pExtension->pLogContext != NULL)
{
SrLogStop( pExtension, TRUE );
}
else
{
ASSERT(!pExtension->DriveChecked);
}
//
// Reset the backup history since the information stored there
// is no longer valid.
//
Status = SrResetBackupHistory(pExtension, NULL, 0, SrEventInvalid);
} finally {
//
// check for unhandled exceptions
//
Status = FinallyUnwind(SrDisableVolumeIoctl, Status);
SrReleaseActivityLock( pExtension );
//
// At this point if Status != PENDING, the ioctl wrapper will
// complete pIrp
//
}
SrTrace( IOCTL, ("SR!SrDisableVolumeIoctl -- EXIT -- status 0x%08lx\n",
Status));
RETURN(Status);
} // SrDisableVolumeIoctl
NTSTATUS
SrStartMonitoringIoctl(
IN PIRP pIrp,
IN PIO_STACK_LOCATION IrpSp
)
{
NTSTATUS Status = STATUS_SUCCESS;
UNREFERENCED_PARAMETER( pIrp );
UNREFERENCED_PARAMETER( IrpSp );
PAGED_CODE();
SrTrace( IOCTL, ("SR!SrStartMonitoringIoctl -- ENTER\n") );
ASSERT(IS_VALID_IRP(pIrp));
//
// no locks better be held, the registry hits the disk with it's own
// locks held so we deadlock .
//
ASSERT(!IS_GLOBAL_LOCK_ACQUIRED());
//
// reload the registry information, on firstrun, we would have
// no valid machine guid until we are started manually by the service
//
Status = SrReadRegistry(_globals.pRegistryLocation, FALSE);
if (!NT_SUCCESS(Status))
{
goto SrStartMonitoringIoctl_Exit;
}
//
// Before we enable, we should clear our all old notifications.
//
SrClearOutstandingNotifications();
//
// now turn us on
//
_globals.Disabled = FALSE;
SrStartMonitoringIoctl_Exit:
SrTrace( IOCTL, ("SR!SrStartMonitoringIoctl -- EXIT -- status 0x%08lx\n",
Status));
RETURN(Status);
} // SrStartMonitoringIoctl
NTSTATUS
SrStopMonitoringIoctl(
IN PIRP pIrp,
IN PIO_STACK_LOCATION IrpSp
)
{
NTSTATUS Status;
PLIST_ENTRY pListEntry;
PSR_DEVICE_EXTENSION pExtension;
UNREFERENCED_PARAMETER( pIrp );
UNREFERENCED_PARAMETER( IrpSp );
PAGED_CODE();
SrTrace( IOCTL, ("SR!SrStopMonitoringIoctl -- ENTER\n") );
ASSERT(IS_VALID_IRP(pIrp));
try {
//
// Disable the driver before we start shutting down each volume
// so that a volume isn't reenabled while we are shutting down
// other volumes.
//
_globals.Disabled = TRUE;
//
// Stop logging on all volumes
//
SrAcquireDeviceExtensionListLockShared();
for (pListEntry = _globals.DeviceExtensionListHead.Flink;
pListEntry != &_globals.DeviceExtensionListHead;
pListEntry = pListEntry->Flink)
{
pExtension = CONTAINING_RECORD( pListEntry,
SR_DEVICE_EXTENSION,
ListEntry );
ASSERT(IS_VALID_SR_DEVICE_EXTENSION(pExtension));
//
// We only have to do work if this is a volume device object,
// not if this is a device object that is attached to a file
// system's control device object.
//
if (FlagOn( pExtension->FsType, SrFsControlDeviceObject ))
{
continue;
}
try {
//
// Take a reference on the DeviceObject associated with this
// extension to ensure that the DeviceObject won't get detached
// until we return from SrLogStop. SrLogStop could have the
// last open handle on this volume, so during shutdown, closing
// this handle could cause the base file system to initiate
// the tearing down of the filter stack. If this happens,
// without this extra reference, we will call SrFastIoDetach
// before we return from SrLogStop. This will cause the
// machine to deadlock on the device extension list lock (we
// currently have the device extension list lock shared and
// SrFastIoDetach needs to acquire it exclusive).
//
ObReferenceObject( pExtension->pDeviceObject );
SrAcquireActivityLockExclusive( pExtension );
pExtension->Disabled = FALSE;
if (pExtension->pLogContext != NULL)
{
Status = SrLogStop( pExtension, TRUE );
CHECK_STATUS( Status );
}
} finally {
SrReleaseActivityLock( pExtension );
ObDereferenceObject( pExtension->pDeviceObject );
}
}
//
// check logger status
//
ASSERT( _globals.pLogger->ActiveContexts == 0 );
//
// Unload the blob config -- SrFreeLookupBlock acquires the appropriate
// locks.
//
Status = SrFreeLookupBlob(&_globals.BlobInfo);
if (!NT_SUCCESS(Status))
leave;
ASSERT(!_globals.BlobInfoLoaded);
Status = STATUS_SUCCESS;
} finally {
Status = FinallyUnwind(SrStopMonitoringIoctl, Status);
SrReleaseDeviceExtensionListLock();
}
SrTrace( IOCTL, ("SR!SrStopMonitoringIoctl -- EXIT -- status 0x%08lx\n",
Status));
RETURN(Status);
} // SrStopMonitoringIoctl
/***************************************************************************++
Routine Description:
This is a generic completion routine that signals the event passed in
then returns STATUS_MORE_PROCESSING_REQUIRED so that the dispatch routine
that it is synchronizing with can still access the Irp. The dispatch
routine is responsible for restarting the completion processing.
Arguments:
DeviceObject - Pointer to this driver's device object.
Irp - Pointer to the IRP that was just completed.
EventToSignal - Pointer to the event to signal.
Return Value:
The return value is always STATUS_MORE_PROCESSING_REQUIRED.
--***************************************************************************/
NTSTATUS
SrDismountCompletion (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
)
{
PSR_DEVICE_EXTENSION pExtension;
UNREFERENCED_PARAMETER( Context );
ASSERT(IS_SR_DEVICE_OBJECT( DeviceObject ));
pExtension = DeviceObject->DeviceExtension;
if (!NT_SUCCESS_NO_DBGBREAK(Irp->IoStatus.Status)) {
//
// The volume failed to dismount, so we want to enable this
// extension so that the log will get reinitialized on the
// first interesting operation.
//
pExtension->Disabled = FALSE;
}
//
// Propogate the pending flag as needed.
//
if (Irp->PendingReturned) {
IoMarkIrpPending( Irp );
}
return STATUS_SUCCESS;
} // SrStopProcessingCompletion
/***************************************************************************++
Routine Description:
This is a generic completion routine that signals the event passed in
then returns STATUS_MORE_PROCESSING_REQUIRED so that the dispatch routine
that it is synchronizing with can still access the Irp. The dispatch
routine is responsible for restarting the completion processing.
Arguments:
DeviceObject - Pointer to this driver's device object.
Irp - Pointer to the IRP that was just completed.
EventToSignal - Pointer to the event to signal.
Return Value:
The return value is always STATUS_MORE_PROCESSING_REQUIRED.
--***************************************************************************/
NTSTATUS
SrStopProcessingCompletion (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PKEVENT EventToSignal
)
{
UNREFERENCED_PARAMETER( Irp );
UNREFERENCED_PARAMETER( DeviceObject );
ASSERT( IS_SR_DEVICE_OBJECT( DeviceObject ) );
ASSERT(NULL != EventToSignal);
KeSetEvent( EventToSignal, IO_NO_INCREMENT, FALSE );
//
// We don't propagate the pending flag here since
// we are doing the synchronization with the originating
// thread.
//
//
// By return STATUS_MORE_PROCESSING_REQUIRED, we stop all further
// processing of the IRP by the IO Manager. This means that the IRP
// will still be good when the thread waiting on the above event.
// The waiting thread needs the IRP to check and update the
// Irp->IoStatus.Status as appropriate.
//
return STATUS_MORE_PROCESSING_REQUIRED;
} // SrStopProcessingCompletion
/***************************************************************************++
Routine Description:
shutdown is happening. flushes our config file to the disk.
Arguments:
Return Value:
NTSTATUS - Status code.
--***************************************************************************/
NTSTATUS
SrShutdown(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP pIrp
)
{
PSR_DEVICE_EXTENSION pExtension = NULL;
//
// < dispatch!
//
PAGED_CODE();
ASSERT( IS_VALID_DEVICE_OBJECT( DeviceObject ) );
ASSERT( IS_VALID_IRP( pIrp ) );
ASSERT( IS_SR_DEVICE_OBJECT( DeviceObject ) );
pExtension = DeviceObject->DeviceExtension;
SrTrace(INIT, ( "SR!SrShutdown:%p,%wZ [%wZ]\n",
DeviceObject,
&pExtension->pTargetDevice->DriverObject->DriverName,
pExtension->pNtVolumeName ));
//
// Get this driver out of the driver stack and get to the next driver as
// quickly as possible.
//
//
// Is this a function for our device (vs an attachee) .
//
if (DeviceObject == _globals.pControlDevice)
{
return SrMajorFunction(DeviceObject, pIrp);
}
//
// We get SHUTDOWN irp directed at each file system control device
// object that we are attached to when the system is shutting down.
//
// At this time, we need to loop through the SR device objects and
// find all the SR device objects associated with volumes that are running
// this file system. We use the FsType field in the device extension
// to figure this out.
//
// We need to shutdown the log for all volumes that use this file system
// because after this operation gets to the file system, all volumes
// using this file system will no longer be able to satify write operations
// from us.
//
ASSERT(SR_IS_FS_CONTROL_DEVICE(pExtension));
//
// SR's extensions that are attached to control device objects should
// never get disabled.
//
ASSERT( !pExtension->Disabled );
try {
PLIST_ENTRY pListEntry;
SR_FILESYSTEM_TYPE interestingFsType;
PSR_DEVICE_EXTENSION pCurrentExtension;
interestingFsType = pExtension->FsType;
ClearFlag( interestingFsType, SrFsControlDeviceObject );
SrAcquireDeviceExtensionListLockShared();
for (pListEntry = _globals.DeviceExtensionListHead.Flink;
pListEntry != &(_globals.DeviceExtensionListHead);
pListEntry = pListEntry->Flink ) {
pCurrentExtension = CONTAINING_RECORD( pListEntry,
SR_DEVICE_EXTENSION,
ListEntry );
if (pCurrentExtension->FsType == interestingFsType) {
try {
SrAcquireActivityLockExclusive( pCurrentExtension );
//
// Disable this drive so that we do not log any more
// activity on it.
//
pCurrentExtension->Disabled = TRUE;
//
// Now cleanup the log on this volume so that the log
// we get flushed to the disk before the file system
// shuts down.
//
if (pCurrentExtension->pLogContext != NULL)
{
SrLogStop( pCurrentExtension, TRUE );
}
} finally {
SrReleaseActivityLock( pCurrentExtension );
}
}
}
} finally {
SrReleaseDeviceExtensionListLock();
}
//
// time to update our configuration file ?
//
try {
SrAcquireGlobalLockExclusive();
if (_globals.FileConfigLoaded)
{
//
// write our the real next file number (not the +1000)
//
_globals.FileConfig.FileSeqNumber = _globals.LastSeqNumber;
_globals.FileConfig.FileNameNumber = _globals.LastFileNameNumber;
SrWriteConfigFile();
//
// only need to do this once
//
_globals.FileConfigLoaded = FALSE;
}
} finally {
SrReleaseGlobalLock();
}
//
// Now pass this operation to the next device in the stack. We don't
// need a completion routine, so just skip our current stack location.
//
IoSkipCurrentIrpStackLocation(pIrp);
return IoCallDriver(pExtension->pTargetDevice, pIrp);
} // SrShutdown