3651 lines
95 KiB
C
3651 lines
95 KiB
C
/*++
|
|
|
|
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;
|
|
|
|
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?
|
|
//
|
|
|
|
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:
|
|
|
|
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, FALSE );
|
|
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, FALSE );
|
|
}
|
|
|
|
} 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, FALSE );
|
|
}
|
|
|
|
} 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, TRUE );
|
|
CHECK_STATUS( Status );
|
|
}
|
|
else
|
|
{
|
|
ASSERT(!pExtension->DriveChecked);
|
|
Status = SrLogNormalize( pExtension );
|
|
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, 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, TRUE );
|
|
CHECK_STATUS( Status );
|
|
}
|
|
else
|
|
{
|
|
ASSERT(!pExtension->DriveChecked);
|
|
Status = SrLogNormalize( pExtension );
|
|
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, FALSE );
|
|
}
|
|
} 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
|