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.
1620 lines
40 KiB
1620 lines
40 KiB
/*++
|
|
|
|
Copyright (c) 1998-1999 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
notify.c
|
|
|
|
Abstract:
|
|
|
|
This module contians the notification logic for sr
|
|
|
|
Author:
|
|
|
|
Paul McDaniel (paulmcd) 23-Jan-2000
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "precomp.h"
|
|
|
|
|
|
//
|
|
// Private constants.
|
|
//
|
|
|
|
//
|
|
// Private types.
|
|
//
|
|
|
|
//
|
|
// Private prototypes.
|
|
//
|
|
|
|
PIRP
|
|
SrDequeueIrp (
|
|
IN PSR_CONTROL_OBJECT pControlObject
|
|
);
|
|
|
|
PSR_NOTIFICATION_RECORD
|
|
SrDequeueNotifyRecord (
|
|
IN PSR_CONTROL_OBJECT pControlObject
|
|
);
|
|
|
|
VOID
|
|
SrCopyRecordToIrp (
|
|
IN PIRP pIrp,
|
|
IN SR_NOTIFICATION_TYPE NotificationType,
|
|
IN PUNICODE_STRING pVolumeName,
|
|
IN ULONG Context
|
|
);
|
|
|
|
VOID
|
|
SrCancelWaitForNotification (
|
|
IN PDEVICE_OBJECT pDeviceObject,
|
|
IN PIRP pIrp
|
|
);
|
|
|
|
VOID
|
|
SrCancelWaitForNotificationWorker (
|
|
IN PVOID pContext
|
|
);
|
|
|
|
NTSTATUS
|
|
SrLogError (
|
|
IN PSR_DEVICE_EXTENSION pExtension,
|
|
IN PUNICODE_STRING pFileName,
|
|
IN NTSTATUS ErrorStatus,
|
|
IN SR_EVENT_TYPE EventType
|
|
);
|
|
|
|
UCHAR
|
|
SrIrpCodeFromEventType (
|
|
IN SR_EVENT_TYPE EventType
|
|
);
|
|
|
|
|
|
//
|
|
// linker commands
|
|
//
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
|
|
#pragma alloc_text( PAGE, SrWaitForNotificationIoctl )
|
|
#pragma alloc_text( PAGE, SrCancelWaitForNotificationWorker )
|
|
#pragma alloc_text( PAGE, SrCopyRecordToIrp )
|
|
#pragma alloc_text( PAGE, SrDequeueIrp )
|
|
#pragma alloc_text( PAGE, SrDequeueNotifyRecord )
|
|
#pragma alloc_text( PAGE, SrFireNotification )
|
|
#pragma alloc_text( PAGE, SrUpdateBytesWritten )
|
|
#pragma alloc_text( PAGE, SrNotifyVolumeError )
|
|
#pragma alloc_text( PAGE, SrLogError )
|
|
#pragma alloc_text( PAGE, SrIrpCodeFromEventType )
|
|
|
|
#endif // ALLOW_UNLOAD
|
|
|
|
#if 0
|
|
NOT PAGEABLE -- SrCancelWaitForNotification
|
|
#endif // 0
|
|
|
|
|
|
//
|
|
// Private globals.
|
|
//
|
|
|
|
//
|
|
// Public globals.
|
|
//
|
|
|
|
//
|
|
// Public functions.
|
|
//
|
|
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
This routine enables sending notifications to user mode
|
|
|
|
Note: This is a METHOD_OUT_DIRECT IOCTL.
|
|
|
|
Arguments:
|
|
|
|
pIrp - Supplies a pointer to the IO request packet.
|
|
|
|
pIrpSp - Supplies a pointer to the IO stack location to use for this
|
|
request.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Completion status.
|
|
|
|
--***************************************************************************/
|
|
NTSTATUS
|
|
SrWaitForNotificationIoctl(
|
|
IN PIRP pIrp,
|
|
IN PIO_STACK_LOCATION pIrpSp
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
PSR_CONTROL_OBJECT pControlObject;
|
|
PSR_NOTIFICATION_RECORD pRecord;
|
|
|
|
//
|
|
// Sanity check.
|
|
//
|
|
|
|
PAGED_CODE();
|
|
|
|
SrTrace(FUNC_ENTRY, ("SR!SrWaitForNotificationIoctl\n"));
|
|
|
|
//
|
|
// grab the control object
|
|
//
|
|
|
|
pControlObject = (PSR_CONTROL_OBJECT)pIrpSp->FileObject->FsContext;
|
|
|
|
//
|
|
// make sure we really have one
|
|
//
|
|
|
|
if (pIrpSp->FileObject->FsContext2 != SR_CONTROL_OBJECT_CONTEXT ||
|
|
IS_VALID_CONTROL_OBJECT(pControlObject) == FALSE ||
|
|
pIrp->MdlAddress == NULL)
|
|
{
|
|
Status = STATUS_INVALID_DEVICE_REQUEST;
|
|
goto end;
|
|
}
|
|
|
|
//
|
|
// make sure the buffer is at least minimum size.
|
|
//
|
|
|
|
if (pIrpSp->Parameters.DeviceIoControl.OutputBufferLength <
|
|
sizeof(SR_NOTIFICATION_RECORD))
|
|
{
|
|
Status = STATUS_BUFFER_TOO_SMALL;
|
|
goto end;
|
|
}
|
|
|
|
//
|
|
// Grab the lock
|
|
//
|
|
|
|
SrAcquireGlobalLockExclusive();
|
|
|
|
//
|
|
// Do we have a queue'd record ?
|
|
//
|
|
|
|
pRecord = SrDequeueNotifyRecord(pControlObject);
|
|
if (pRecord == NULL)
|
|
{
|
|
SrTrace(NOTIFY, ("SR!SrWaitForNotificationIoctl - queue'ing IRP(%p)\n", pIrp));
|
|
|
|
//
|
|
// Nope, queue the irp up.
|
|
//
|
|
|
|
IoMarkIrpPending(pIrp);
|
|
|
|
//
|
|
// give the irp a pointer to the control object (add a refcount
|
|
// as the cancel routine runs queued, and needs to access the
|
|
// control object - even if it's later deleted).
|
|
//
|
|
|
|
pIrpSp->Parameters.DeviceIoControl.Type3InputBuffer = pControlObject;
|
|
SrReferenceControlObject(pControlObject);
|
|
|
|
//
|
|
// set to these to null just in case the cancel routine runs
|
|
//
|
|
|
|
pIrp->Tail.Overlay.ListEntry.Flink = NULL;
|
|
pIrp->Tail.Overlay.ListEntry.Blink = NULL;
|
|
|
|
IoSetCancelRoutine(pIrp, &SrCancelWaitForNotification);
|
|
|
|
//
|
|
// cancelled?
|
|
//
|
|
|
|
if (pIrp->Cancel)
|
|
{
|
|
//
|
|
// darn it, need to make sure the irp get's completed
|
|
//
|
|
|
|
if (IoSetCancelRoutine( pIrp, NULL ) != NULL)
|
|
{
|
|
//
|
|
// we are in charge of completion, IoCancelIrp didn't
|
|
// see our cancel routine (and won't). ioctl wrapper
|
|
// will complete it
|
|
//
|
|
|
|
SrReleaseGlobalLock();
|
|
|
|
pIrpSp->Parameters.DeviceIoControl.Type3InputBuffer = NULL;
|
|
SrDereferenceControlObject(pControlObject);
|
|
|
|
pIrp->IoStatus.Information = 0;
|
|
|
|
SrUnmarkIrpPending( pIrp );
|
|
Status = STATUS_CANCELLED;
|
|
goto end;
|
|
}
|
|
|
|
//
|
|
// our cancel routine will run and complete the irp,
|
|
// don't touch it
|
|
//
|
|
|
|
SrReleaseGlobalLock();
|
|
|
|
//
|
|
// STATUS_PENDING will cause the ioctl wrapper to
|
|
// not complete (or touch in any way) the irp
|
|
//
|
|
|
|
Status = STATUS_PENDING;
|
|
goto end;
|
|
}
|
|
|
|
//
|
|
// now we are safe to queue it
|
|
//
|
|
|
|
InsertTailList(
|
|
&pControlObject->IrpListHead,
|
|
&pIrp->Tail.Overlay.ListEntry
|
|
);
|
|
|
|
SrReleaseGlobalLock();
|
|
|
|
Status = STATUS_PENDING;
|
|
goto end;
|
|
}
|
|
else // if (pRecord == NULL)
|
|
{
|
|
//
|
|
// Have a queue'd record, serve it up!
|
|
//
|
|
|
|
//
|
|
// all done with the control object
|
|
//
|
|
|
|
SrReleaseGlobalLock();
|
|
|
|
SrTrace( NOTIFY, ( "SR!SrWaitForNotificationIoctl - completing IRP(%p) NotifyRecord(%d, %wZ)\n",
|
|
pIrp,
|
|
pRecord->NotificationType,
|
|
&pRecord->VolumeName ));
|
|
|
|
//
|
|
// Copy it to the irp, the routine will take ownership
|
|
// of pRecord if it is not able to copy it to the irp.
|
|
//
|
|
// it will also complete the irp so don't touch it later.
|
|
//
|
|
|
|
IoMarkIrpPending(pIrp);
|
|
|
|
//
|
|
// Copy the data and complete the irp
|
|
//
|
|
|
|
(VOID) SrCopyRecordToIrp( pIrp,
|
|
pRecord->NotificationType,
|
|
&pRecord->VolumeName,
|
|
pRecord->Context );
|
|
|
|
//
|
|
// don't touch pIrp, SrCopyRecordToIrp ALWAYS completes it.
|
|
//
|
|
|
|
pIrp = NULL;
|
|
|
|
//
|
|
// and free the record
|
|
//
|
|
|
|
SR_FREE_POOL_WITH_SIG(pRecord, SR_NOTIFICATION_RECORD_TAG);
|
|
|
|
Status = STATUS_PENDING;
|
|
goto end;
|
|
}
|
|
|
|
|
|
end:
|
|
|
|
//
|
|
// At this point if Status != PENDING, the ioctl wrapper will
|
|
// complete pIrp
|
|
//
|
|
|
|
RETURN(Status);
|
|
|
|
} // SrWaitForNotificationIoctl
|
|
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
cancels the pending user mode irp which was to receive the http request.
|
|
this routine ALWAYS results in the irp being completed.
|
|
|
|
note: we queue off to cancel in order to process the cancellation at lower
|
|
irql.
|
|
|
|
Arguments:
|
|
|
|
pDeviceObject - the device object
|
|
|
|
pIrp - the irp to cancel
|
|
|
|
--***************************************************************************/
|
|
VOID
|
|
SrCancelWaitForNotification(
|
|
IN PDEVICE_OBJECT pDeviceObject,
|
|
IN PIRP pIrp
|
|
)
|
|
{
|
|
C_ASSERT(sizeof(WORK_QUEUE_ITEM) <= sizeof(pIrp->Tail.Overlay.DriverContext));
|
|
|
|
UNREFERENCED_PARAMETER( pDeviceObject );
|
|
|
|
ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
|
|
ASSERT(pIrp != NULL);
|
|
|
|
//
|
|
// release the cancel spinlock. This means the cancel routine
|
|
// must be the one completing the irp (to avoid the race of
|
|
// completion + reuse prior to the cancel routine running).
|
|
//
|
|
|
|
IoReleaseCancelSpinLock(pIrp->CancelIrql);
|
|
|
|
//
|
|
// queue the cancel to a worker to ensure passive irql.
|
|
//
|
|
|
|
ExInitializeWorkItem( (PWORK_QUEUE_ITEM)&pIrp->Tail.Overlay.DriverContext[0],
|
|
&SrCancelWaitForNotificationWorker,
|
|
pIrp );
|
|
|
|
ExQueueWorkItem( (PWORK_QUEUE_ITEM)&pIrp->Tail.Overlay.DriverContext[0],
|
|
DelayedWorkQueue );
|
|
|
|
|
|
} // SrCancelWaitForNotification
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Actually performs the cancel for the irp.
|
|
|
|
Arguments:
|
|
|
|
pWorkItem - the work item to process.
|
|
|
|
--***************************************************************************/
|
|
VOID
|
|
SrCancelWaitForNotificationWorker(
|
|
IN PVOID pContext
|
|
)
|
|
{
|
|
PSR_CONTROL_OBJECT pControlObject;
|
|
PIRP pIrp;
|
|
|
|
//
|
|
// Sanity check.
|
|
//
|
|
|
|
PAGED_CODE();
|
|
|
|
ASSERT(pContext != NULL);
|
|
|
|
//
|
|
// grab the irp off the context
|
|
//
|
|
|
|
pIrp = (PIRP)pContext;
|
|
ASSERT(IS_VALID_IRP(pIrp));
|
|
|
|
SrTrace(CANCEL, ("SR!SrCancelWaitForNotificationWorker irp=%p\n", pIrp));
|
|
|
|
//
|
|
// grab the control object off the irp
|
|
//
|
|
|
|
pControlObject = (PSR_CONTROL_OBJECT)(
|
|
IoGetCurrentIrpStackLocation(pIrp)->Parameters.DeviceIoControl.Type3InputBuffer
|
|
);
|
|
|
|
ASSERT(IS_VALID_CONTROL_OBJECT(pControlObject));
|
|
|
|
//
|
|
// grab the lock protecting the queue
|
|
//
|
|
|
|
SrAcquireGlobalLockExclusive();
|
|
|
|
//
|
|
// does it need to be de-queue'd ?
|
|
//
|
|
|
|
if (pIrp->Tail.Overlay.ListEntry.Flink != NULL)
|
|
{
|
|
//
|
|
// remove it
|
|
//
|
|
|
|
RemoveEntryList(&pIrp->Tail.Overlay.ListEntry);
|
|
pIrp->Tail.Overlay.ListEntry.Flink = NULL;
|
|
pIrp->Tail.Overlay.ListEntry.Blink = NULL;
|
|
}
|
|
|
|
//
|
|
// let the lock go
|
|
//
|
|
|
|
SrReleaseGlobalLock();
|
|
|
|
//
|
|
// let our reference go
|
|
//
|
|
|
|
IoGetCurrentIrpStackLocation(pIrp)->Parameters.DeviceIoControl.Type3InputBuffer = NULL;
|
|
|
|
SrDereferenceControlObject(pControlObject);
|
|
|
|
//
|
|
// complete the irp
|
|
//
|
|
|
|
pIrp->IoStatus.Status = STATUS_CANCELLED;
|
|
pIrp->IoStatus.Information = 0;
|
|
|
|
IoCompleteRequest( pIrp, IO_NO_INCREMENT );
|
|
|
|
} // SrCancelWaitForNotificationWorker
|
|
|
|
|
|
|
|
/******************************************************************************
|
|
|
|
Routine Description:
|
|
|
|
this copies a record into a free irp. this routine completes the IRP!
|
|
|
|
Arguments:
|
|
|
|
pRecord - the record to copy
|
|
|
|
pIrp - the irp to copy pRecord to. this routine completes this IRP !
|
|
|
|
Return Value:
|
|
|
|
VOID - it always works.
|
|
|
|
******************************************************************************/
|
|
VOID
|
|
SrCopyRecordToIrp(
|
|
IN PIRP pIrp,
|
|
IN SR_NOTIFICATION_TYPE NotificationType,
|
|
IN PUNICODE_STRING pVolumeName,
|
|
IN ULONG Context
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
PIO_STACK_LOCATION pIrpSp;
|
|
PVOID pBuffer;
|
|
PSR_NOTIFICATION_RECORD pUserNotifyRecord;
|
|
|
|
//
|
|
// Sanity check.
|
|
//
|
|
|
|
PAGED_CODE();
|
|
|
|
ASSERT(NotificationType < SrNotificationMaximum);
|
|
ASSERT(NotificationType > SrNotificationInvalid);
|
|
ASSERT(pVolumeName != NULL);
|
|
|
|
SrTrace(FUNC_ENTRY, ("SR!SrCopyRecordToIrp\n"));
|
|
|
|
ASSERT(global->pControlObject != NULL);
|
|
|
|
//
|
|
// assume SUCCESS!
|
|
//
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
//
|
|
// Make sure this is big enough to handle the request, and
|
|
// if so copy it in.
|
|
//
|
|
|
|
pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
|
|
|
|
//
|
|
// do we have enough space?
|
|
//
|
|
|
|
if (pIrpSp->Parameters.DeviceIoControl.OutputBufferLength <
|
|
sizeof(SR_NOTIFICATION_RECORD) + pVolumeName->Length
|
|
+ sizeof(WCHAR) )
|
|
{
|
|
Status = STATUS_BUFFER_TOO_SMALL;
|
|
goto complete;
|
|
}
|
|
|
|
//
|
|
// get the system address for the buffer
|
|
//
|
|
|
|
pBuffer = MmGetSystemAddressForMdlSafe( pIrp->MdlAddress,
|
|
NormalPagePriority );
|
|
|
|
if (pBuffer == NULL)
|
|
{
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto complete;
|
|
}
|
|
|
|
//
|
|
// wipe it clean
|
|
//
|
|
|
|
RtlZeroMemory( pBuffer,
|
|
pIrpSp->Parameters.DeviceIoControl.OutputBufferLength );
|
|
|
|
//
|
|
// Fill in the user space
|
|
//
|
|
|
|
pUserNotifyRecord = (PSR_NOTIFICATION_RECORD) pBuffer;
|
|
|
|
pUserNotifyRecord->Signature = SR_NOTIFICATION_RECORD_TAG;
|
|
pUserNotifyRecord->NotificationType = NotificationType;
|
|
pUserNotifyRecord->VolumeName.Length = pVolumeName->Length;
|
|
pUserNotifyRecord->VolumeName.MaximumLength = pVolumeName->Length;
|
|
|
|
//
|
|
// put the virtual pointer in for the use by the user mode service
|
|
//
|
|
|
|
pUserNotifyRecord->VolumeName.Buffer =
|
|
(PWSTR)((PUCHAR)(MmGetMdlVirtualAddress(pIrp->MdlAddress))
|
|
+ sizeof(SR_NOTIFICATION_RECORD));
|
|
|
|
pUserNotifyRecord->Context = Context;
|
|
|
|
//
|
|
// and copy the string using the system address
|
|
//
|
|
|
|
RtlCopyMemory( pUserNotifyRecord+1,
|
|
pVolumeName->Buffer,
|
|
pVolumeName->Length );
|
|
|
|
//
|
|
// null terminate it
|
|
//
|
|
|
|
((PWSTR)(pUserNotifyRecord+1))[pVolumeName->Length/sizeof(WCHAR)]
|
|
= UNICODE_NULL;
|
|
|
|
//
|
|
// Tell everyone how much we copied
|
|
//
|
|
|
|
pIrp->IoStatus.Information = sizeof(SR_NOTIFICATION_RECORD)
|
|
+ pVolumeName->Length + sizeof(WCHAR);
|
|
|
|
//
|
|
// complete the irp
|
|
//
|
|
|
|
complete:
|
|
|
|
pIrp->IoStatus.Status = Status;
|
|
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
|
|
|
|
//
|
|
// success. we completed the irp
|
|
//
|
|
|
|
|
|
} // SrCopyRecordToIrp
|
|
|
|
|
|
/******************************************************************************
|
|
|
|
Routine Description:
|
|
|
|
this will grab a free queue'd irp off the list and return it.
|
|
|
|
Arguments:
|
|
|
|
pControlObject - the control object to grab irp's from
|
|
|
|
Return Value:
|
|
|
|
PIRP - the free irp found (can be NULL)
|
|
|
|
******************************************************************************/
|
|
PIRP
|
|
SrDequeueIrp(
|
|
IN PSR_CONTROL_OBJECT pControlObject
|
|
)
|
|
{
|
|
PIRP pIrp = NULL;
|
|
PSR_CONTROL_OBJECT pIrpControlObject;
|
|
|
|
PAGED_CODE();
|
|
|
|
ASSERT(IS_VALID_CONTROL_OBJECT(pControlObject));
|
|
|
|
//
|
|
// we are modifying the lists, better own the lock
|
|
//
|
|
|
|
ASSERT(IS_GLOBAL_LOCK_ACQUIRED());
|
|
|
|
SrTrace(FUNC_ENTRY, ("SR!SrDequeueIrp\n"));
|
|
|
|
//
|
|
// check our list
|
|
//
|
|
|
|
while (!IsListEmpty(&(pControlObject->IrpListHead)))
|
|
{
|
|
PLIST_ENTRY pEntry;
|
|
|
|
//
|
|
// Found a free irp !
|
|
//
|
|
|
|
pEntry = RemoveHeadList(&pControlObject->IrpListHead);
|
|
pEntry->Blink = pEntry->Flink = NULL;
|
|
|
|
pIrp = CONTAINING_RECORD(pEntry, IRP, Tail.Overlay.ListEntry);
|
|
|
|
//
|
|
// pop the cancel routine
|
|
//
|
|
|
|
if (IoSetCancelRoutine(pIrp, NULL) == NULL)
|
|
{
|
|
//
|
|
// IoCancelIrp pop'd it first
|
|
//
|
|
// ok to just ignore this irp, it's been pop'd off the queue
|
|
// and will be completed in the cancel routine.
|
|
//
|
|
// keep looking for a irp to use
|
|
//
|
|
|
|
pIrp = NULL;
|
|
|
|
}
|
|
else if (pIrp->Cancel)
|
|
{
|
|
|
|
//
|
|
// we pop'd it first. but the irp is being cancelled
|
|
// and our cancel routine will never run. lets be
|
|
// nice and complete the irp now (vs. using it
|
|
// then completing it - which would also be legal).
|
|
//
|
|
|
|
pIrpControlObject = (PSR_CONTROL_OBJECT)(
|
|
IoGetCurrentIrpStackLocation(pIrp)->
|
|
Parameters.DeviceIoControl.Type3InputBuffer
|
|
);
|
|
|
|
ASSERT(pIrpControlObject == pControlObject);
|
|
|
|
SrDereferenceControlObject(pControlObject);
|
|
|
|
IoGetCurrentIrpStackLocation(pIrp)->
|
|
Parameters.DeviceIoControl.Type3InputBuffer = NULL;
|
|
|
|
pIrp->IoStatus.Status = STATUS_CANCELLED;
|
|
pIrp->IoStatus.Information = 0;
|
|
|
|
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
|
|
|
|
pIrp = NULL;
|
|
}
|
|
else
|
|
{
|
|
|
|
//
|
|
// we are free to use this irp !
|
|
//
|
|
|
|
pIrpControlObject = (PSR_CONTROL_OBJECT)(
|
|
IoGetCurrentIrpStackLocation(pIrp)->
|
|
Parameters.DeviceIoControl.Type3InputBuffer
|
|
);
|
|
|
|
ASSERT(pIrpControlObject == pControlObject);
|
|
|
|
SrDereferenceControlObject(pControlObject);
|
|
|
|
IoGetCurrentIrpStackLocation(pIrp)->
|
|
Parameters.DeviceIoControl.Type3InputBuffer = NULL;
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
return pIrp;
|
|
|
|
} // SrDequeueIrp
|
|
|
|
|
|
/******************************************************************************
|
|
|
|
Routine Description:
|
|
|
|
this will grab a notify record if one has been queue'd for completion.
|
|
|
|
Arguments:
|
|
|
|
pControlObject - the control object to grab records from
|
|
|
|
Return Value:
|
|
|
|
PSR_NOTIFICATION_RECORD - the record found (can be NULL)
|
|
|
|
******************************************************************************/
|
|
PSR_NOTIFICATION_RECORD
|
|
SrDequeueNotifyRecord(
|
|
IN PSR_CONTROL_OBJECT pControlObject
|
|
)
|
|
{
|
|
PSR_NOTIFICATION_RECORD pRecord = NULL;
|
|
|
|
PAGED_CODE();
|
|
|
|
ASSERT(IS_VALID_CONTROL_OBJECT(pControlObject));
|
|
|
|
//
|
|
// we are modifying the lists, better own the lock
|
|
//
|
|
|
|
ASSERT(IS_GLOBAL_LOCK_ACQUIRED());
|
|
|
|
|
|
SrTrace(FUNC_ENTRY, ("SR!SrDequeueNotifyRecord\n"));
|
|
|
|
//
|
|
// check our list
|
|
//
|
|
|
|
if (IsListEmpty(&pControlObject->NotifyRecordListHead) == FALSE)
|
|
{
|
|
PLIST_ENTRY pEntry;
|
|
|
|
//
|
|
// Found a free record !
|
|
//
|
|
|
|
pEntry = RemoveHeadList(&pControlObject->NotifyRecordListHead);
|
|
pEntry->Blink = pEntry->Flink = NULL;
|
|
|
|
pRecord = CONTAINING_RECORD( pEntry,
|
|
SR_NOTIFICATION_RECORD,
|
|
ListEntry );
|
|
|
|
ASSERT(IS_VALID_NOTIFICATION_RECORD(pRecord));
|
|
|
|
//
|
|
// give the record to the caller
|
|
//
|
|
|
|
}
|
|
|
|
return pRecord;
|
|
|
|
} // SrDequeueNotifyRecord
|
|
|
|
|
|
|
|
/******************************************************************************
|
|
|
|
Routine Description:
|
|
|
|
this will fire a notify up to a listening usermode process.
|
|
|
|
it does nothing if nobody is listening.
|
|
|
|
it will queue the record if there are no free irp's.
|
|
|
|
Arguments:
|
|
|
|
NotificationType - the type of notification
|
|
|
|
pExtension - the volume being notified about
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - completion code
|
|
|
|
******************************************************************************/
|
|
NTSTATUS
|
|
SrFireNotification(
|
|
IN SR_NOTIFICATION_TYPE NotificationType,
|
|
IN PSR_DEVICE_EXTENSION pExtension,
|
|
IN ULONG Context OPTIONAL
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
PIRP pIrp;
|
|
BOOLEAN bReleaseLock = FALSE;
|
|
|
|
PAGED_CODE();
|
|
|
|
ASSERT(NotificationType < SrNotificationMaximum);
|
|
ASSERT(NotificationType > SrNotificationInvalid);
|
|
ASSERT(IS_VALID_SR_DEVICE_EXTENSION(pExtension));
|
|
|
|
SrTrace(FUNC_ENTRY, ("SR!SrFireNotification\n"));
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
try {
|
|
|
|
//
|
|
// grab the lock EXCLUSIVE
|
|
//
|
|
|
|
SrAcquireGlobalLockExclusive();
|
|
bReleaseLock = TRUE;
|
|
|
|
//
|
|
// do we still have a control object ? the agent could have just
|
|
// crashed or he was never there... that's ok .
|
|
//
|
|
|
|
if (global->pControlObject == NULL)
|
|
{
|
|
Status = STATUS_SUCCESS;
|
|
leave;
|
|
}
|
|
|
|
//
|
|
// find a free irp to use
|
|
//
|
|
|
|
pIrp = SrDequeueIrp(global->pControlObject);
|
|
|
|
if (pIrp != NULL)
|
|
{
|
|
|
|
//
|
|
// Found one, release the lock
|
|
//
|
|
|
|
SrReleaseGlobalLock();
|
|
bReleaseLock = FALSE;
|
|
|
|
SrTrace( NOTIFY, ("SR!SrFireNotification(%d, %wZ, %X) - completing IRP(%p)\n",
|
|
NotificationType,
|
|
&pExtension->VolumeGuid,
|
|
Context,
|
|
pIrp ));
|
|
|
|
//
|
|
// Copy the data and complete the irp
|
|
//
|
|
|
|
(VOID) SrCopyRecordToIrp( pIrp,
|
|
NotificationType,
|
|
&pExtension->VolumeGuid,
|
|
Context );
|
|
|
|
//
|
|
// don't touch pIrp, SrCopyRecordToIrp ALWAYS completes it.
|
|
//
|
|
|
|
NULLPTR( pIrp );
|
|
|
|
}
|
|
else
|
|
{
|
|
PSR_NOTIFICATION_RECORD pRecord = NULL;
|
|
|
|
SrTrace(NOTIFY, ("SR!SrFireNotification(%d, %wZ) - no IRPs; queue'ing a NOTIFY_RECORD\n",
|
|
NotificationType,
|
|
&pExtension->VolumeGuid ));
|
|
|
|
//
|
|
// need to queue a NOTIFY_RECORD and wait for a free IRP to come down
|
|
//
|
|
|
|
//
|
|
// allocate a notify record
|
|
//
|
|
|
|
pRecord = SR_ALLOCATE_STRUCT_WITH_SPACE( PagedPool,
|
|
SR_NOTIFICATION_RECORD,
|
|
pExtension->VolumeGuid.Length + sizeof(WCHAR),
|
|
SR_NOTIFICATION_RECORD_TAG );
|
|
|
|
if (NULL == pRecord)
|
|
{
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
leave;
|
|
}
|
|
|
|
RtlZeroMemory(pRecord, sizeof(SR_NOTIFICATION_RECORD));
|
|
|
|
pRecord->Signature = SR_NOTIFICATION_RECORD_TAG;
|
|
pRecord->NotificationType = NotificationType;
|
|
pRecord->VolumeName.Length = pExtension->VolumeGuid.Length;
|
|
pRecord->VolumeName.MaximumLength = pExtension->VolumeGuid.Length;
|
|
|
|
pRecord->VolumeName.Buffer = (PWSTR)(pRecord + 1);
|
|
|
|
RtlCopyMemory( pRecord->VolumeName.Buffer,
|
|
pExtension->VolumeGuid.Buffer,
|
|
pExtension->VolumeGuid.Length );
|
|
|
|
pRecord->VolumeName.Buffer
|
|
[pRecord->VolumeName.Length/sizeof(WCHAR)] = UNICODE_NULL;
|
|
|
|
pRecord->Context = Context;
|
|
|
|
//
|
|
// insert it into the list
|
|
//
|
|
|
|
InsertTailList( &global->pControlObject->NotifyRecordListHead,
|
|
&pRecord->ListEntry );
|
|
|
|
NULLPTR( pRecord );
|
|
}
|
|
} finally {
|
|
|
|
//
|
|
// release any locks we held during an error
|
|
//
|
|
|
|
if (bReleaseLock)
|
|
{
|
|
SrReleaseGlobalLock();
|
|
}
|
|
}
|
|
|
|
RETURN(Status);
|
|
|
|
} // SrFireNotification
|
|
|
|
|
|
|
|
/******************************************************************************
|
|
|
|
Routine Description:
|
|
|
|
this update the bytes written count for the volume, and potentially
|
|
fire a notification to user mode (for every 25mb).
|
|
|
|
Arguments:
|
|
|
|
pExtension - the volume being updated
|
|
|
|
BytesWritten - how much was just written
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - completion code
|
|
|
|
******************************************************************************/
|
|
NTSTATUS
|
|
SrUpdateBytesWritten(
|
|
IN PSR_DEVICE_EXTENSION pExtension,
|
|
IN ULONGLONG BytesWritten
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
PAGED_CODE();
|
|
|
|
try {
|
|
|
|
SrAcquireLogLockExclusive( pExtension );
|
|
|
|
//
|
|
// update the count
|
|
//
|
|
|
|
pExtension->BytesWritten += BytesWritten;
|
|
|
|
SrTrace( BYTES_WRITTEN, ( "SR!SrUpdateBytesWritten: (%wZ) Wrote 0x%016I64x bytes; total bytes written 0x%016I64x\n",
|
|
pExtension->pNtVolumeName,
|
|
BytesWritten,
|
|
pExtension->BytesWritten ) );
|
|
|
|
while (pExtension->BytesWritten >= SR_NOTIFY_BYTE_COUNT)
|
|
{
|
|
|
|
SrTrace( BYTES_WRITTEN, ( "SR!SrUpdateBytesWritten: (%wZ) Reached threshold -- notifying service; Total bytes written 0x%016I64x\n",
|
|
pExtension->pNtVolumeName,
|
|
pExtension->BytesWritten ) );
|
|
|
|
Status = SrFireNotification( SrNotificationVolume25MbWritten,
|
|
pExtension,
|
|
global->FileConfig.CurrentRestoreNumber );
|
|
|
|
if (NT_SUCCESS(Status) == FALSE)
|
|
leave;
|
|
|
|
pExtension->BytesWritten -= SR_NOTIFY_BYTE_COUNT;
|
|
}
|
|
|
|
//
|
|
// all done
|
|
//
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
} finally {
|
|
|
|
Status = FinallyUnwind(SrUpdateBytesWritten, Status);
|
|
|
|
SrReleaseLogLock( pExtension );
|
|
}
|
|
|
|
RETURN(Status);
|
|
|
|
} // SrUpdateBytesWritten
|
|
|
|
|
|
NTSTATUS
|
|
SrNotifyVolumeError(
|
|
IN PSR_DEVICE_EXTENSION pExtension,
|
|
IN PUNICODE_STRING pFileName OPTIONAL,
|
|
IN NTSTATUS ErrorStatus,
|
|
IN SR_EVENT_TYPE EventType OPTIONAL
|
|
)
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
PAGED_CODE();
|
|
|
|
if (!pExtension->Disabled)
|
|
{
|
|
//
|
|
// trigger the failure notification to the service
|
|
//
|
|
|
|
Status = SrFireNotification( SrNotificationVolumeError,
|
|
pExtension,
|
|
RtlNtStatusToDosErrorNoTeb(ErrorStatus));
|
|
|
|
CHECK_STATUS(Status);
|
|
|
|
//
|
|
// log the failure in our change log
|
|
//
|
|
|
|
if (pFileName != NULL &&
|
|
pExtension->pNtVolumeName != NULL &&
|
|
(pFileName->Length > pExtension->pNtVolumeName->Length))
|
|
{
|
|
Status = SrLogEvent( pExtension,
|
|
SrEventVolumeError,
|
|
NULL,
|
|
pFileName,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
0,
|
|
NULL );
|
|
|
|
CHECK_STATUS(Status);
|
|
|
|
}
|
|
|
|
//
|
|
// log the failure with nt
|
|
//
|
|
|
|
Status = SrLogError( pExtension,
|
|
pFileName ? pFileName : pExtension->pNtVolumeName,
|
|
ErrorStatus,
|
|
EventType );
|
|
|
|
CHECK_STATUS(Status);
|
|
|
|
//
|
|
// and temporarily disable the volume
|
|
//
|
|
|
|
SrTrace( VERBOSE_ERRORS,
|
|
("sr!SrNotifyVolumeError(%X): disabling \"%wZ\", error %X!\n",
|
|
EventType,
|
|
pExtension->pNtVolumeName,
|
|
ErrorStatus) );
|
|
|
|
pExtension->Disabled = TRUE;
|
|
}
|
|
|
|
RETURN(Status);
|
|
|
|
}
|
|
|
|
/******************************************************************************
|
|
|
|
Routine Description:
|
|
|
|
This routine clears out all the outstanding notification record in the
|
|
queue.
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
******************************************************************************/
|
|
VOID
|
|
SrClearOutstandingNotifications (
|
|
)
|
|
{
|
|
PSR_NOTIFICATION_RECORD pRecord;
|
|
|
|
ASSERT( !IS_GLOBAL_LOCK_ACQUIRED() );
|
|
|
|
try {
|
|
|
|
SrAcquireGlobalLockExclusive();
|
|
|
|
while (pRecord = SrDequeueNotifyRecord( _globals.pControlObject ))
|
|
{
|
|
//
|
|
// We don't care about this notification, so just free the memory.
|
|
//
|
|
|
|
SR_FREE_POOL_WITH_SIG(pRecord, SR_NOTIFICATION_RECORD_TAG);
|
|
}
|
|
|
|
} finally {
|
|
|
|
SrReleaseGlobalLock();
|
|
}
|
|
}
|
|
|
|
/******************************************************************************
|
|
|
|
Routine Description:
|
|
|
|
This routine writes an eventlog entry to the eventlog. It is way more
|
|
complicated then you would hope, as it needs to squeeze everything into
|
|
less than 104 bytes (52 characters).
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - completion code
|
|
|
|
******************************************************************************/
|
|
NTSTATUS
|
|
SrLogError(
|
|
IN PSR_DEVICE_EXTENSION pExtension,
|
|
IN PUNICODE_STRING pFileName,
|
|
IN NTSTATUS ErrorStatus,
|
|
IN SR_EVENT_TYPE EventType
|
|
)
|
|
{
|
|
C_ASSERT(sizeof(NTSTATUS) == sizeof(ULONG));
|
|
|
|
UCHAR ErrorPacketLength;
|
|
UCHAR BasePacketLength;
|
|
ULONG StringLength, ReservedLength;
|
|
PIO_ERROR_LOG_PACKET ErrorLogEntry = NULL;
|
|
PWCHAR String;
|
|
PWCHAR pToken, pFileToken, pVolumeToken;
|
|
ULONG TokenLength, FileTokenLength, VolumeTokenLength;
|
|
ULONG Count;
|
|
WCHAR ErrorString[10+1];
|
|
|
|
NTSTATUS Status;
|
|
|
|
ASSERT(IS_VALID_SR_DEVICE_EXTENSION(pExtension));
|
|
ASSERT(pExtension->pNtVolumeName != NULL);
|
|
ASSERT(pFileName != NULL);
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// get the name of just the file part
|
|
//
|
|
|
|
Status = SrFindCharReverse( pFileName->Buffer,
|
|
pFileName->Length,
|
|
L'\\',
|
|
&pFileToken,
|
|
&FileTokenLength );
|
|
|
|
if (!NT_SUCCESS_NO_DBGBREAK(Status)) {
|
|
FileTokenLength = 0;
|
|
pFileToken = NULL;
|
|
} else {
|
|
//
|
|
// skip the prefix slash
|
|
//
|
|
|
|
pFileToken += 1;
|
|
FileTokenLength -= sizeof(WCHAR);
|
|
}
|
|
|
|
//
|
|
// get the name of just the volume
|
|
//
|
|
|
|
Status = SrFindCharReverse( pExtension->pNtVolumeName->Buffer,
|
|
pExtension->pNtVolumeName->Length,
|
|
L'\\',
|
|
&pVolumeToken,
|
|
&VolumeTokenLength );
|
|
|
|
if (!NT_SUCCESS_NO_DBGBREAK(Status))
|
|
{
|
|
VolumeTokenLength = 0;
|
|
pVolumeToken = NULL;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// skip the prefix slash
|
|
//
|
|
|
|
pVolumeToken += 1;
|
|
VolumeTokenLength -= sizeof(WCHAR);
|
|
}
|
|
|
|
//
|
|
// Get our error packet, holding the string and status code.
|
|
//
|
|
|
|
BasePacketLength = sizeof(IO_ERROR_LOG_PACKET) ;
|
|
|
|
if ((BasePacketLength + sizeof(ErrorString) + VolumeTokenLength + sizeof(WCHAR) + FileTokenLength + sizeof(WCHAR)) <= ERROR_LOG_MAXIMUM_SIZE) {
|
|
ErrorPacketLength = (UCHAR)(BasePacketLength
|
|
+ sizeof(ErrorString)
|
|
+ VolumeTokenLength + sizeof(WCHAR)
|
|
+ FileTokenLength + sizeof(WCHAR) );
|
|
} else {
|
|
ErrorPacketLength = ERROR_LOG_MAXIMUM_SIZE;
|
|
}
|
|
|
|
ErrorLogEntry = (PIO_ERROR_LOG_PACKET) IoAllocateErrorLogEntry( pExtension->pDeviceObject,
|
|
ErrorPacketLength );
|
|
|
|
if (ErrorLogEntry == NULL)
|
|
{
|
|
RETURN(STATUS_INSUFFICIENT_RESOURCES);
|
|
}
|
|
|
|
//
|
|
// Fill in the nonzero members of the packet.
|
|
//
|
|
|
|
ErrorLogEntry->MajorFunctionCode = SrIrpCodeFromEventType(EventType);
|
|
ErrorLogEntry->ErrorCode = EVMSG_DISABLEDVOLUME;
|
|
|
|
//
|
|
// init the insertion strings
|
|
//
|
|
|
|
ErrorLogEntry->NumberOfStrings = 3;
|
|
ErrorLogEntry->StringOffset = BasePacketLength;
|
|
|
|
StringLength = ErrorPacketLength - BasePacketLength;
|
|
|
|
ASSERT(!(StringLength % sizeof(WCHAR)));
|
|
|
|
String = (PWCHAR) ((PUCHAR)ErrorLogEntry + BasePacketLength);
|
|
|
|
RtlZeroMemory(String, StringLength);
|
|
|
|
ASSERT(StringLength > 3 * sizeof(WCHAR));
|
|
|
|
//
|
|
// put the error code string in first
|
|
//
|
|
|
|
if (StringLength >= ((10+1)*sizeof(WCHAR)) ) {
|
|
Count = swprintf(String, L"0x%08X", ErrorStatus);
|
|
ASSERT(Count == 10);
|
|
|
|
String += (10+1);
|
|
StringLength -= (10+1) * sizeof(WCHAR);
|
|
} else {
|
|
String[0] = UNICODE_NULL;
|
|
String += 1;
|
|
StringLength -= sizeof(WCHAR);
|
|
}
|
|
|
|
//
|
|
// now put the filename token in there
|
|
//
|
|
|
|
TokenLength = FileTokenLength;
|
|
pToken = pFileToken;
|
|
|
|
//
|
|
// reserve space for the volume token
|
|
//
|
|
|
|
if (ErrorPacketLength == ERROR_LOG_MAXIMUM_SIZE) {
|
|
if (StringLength > (VolumeTokenLength + 10)) {
|
|
StringLength -= VolumeTokenLength;
|
|
ReservedLength = VolumeTokenLength;
|
|
} else {
|
|
StringLength /= 2;
|
|
ReservedLength = StringLength;
|
|
if (StringLength % 2) {
|
|
StringLength -=1;
|
|
ReservedLength += 1;
|
|
}
|
|
}
|
|
} else {
|
|
ReservedLength = 0;
|
|
}
|
|
|
|
if (TokenLength > 0)
|
|
{
|
|
//
|
|
// The filename string is appended to the end of the error log entry. We
|
|
// may have to smash the middle to fit it in the limited space.
|
|
//
|
|
|
|
//
|
|
// If the name does not fit in the packet, divide the name equally to the
|
|
// prefix and suffix, with an ellipsis " .. " (4 wide characters) to indicate
|
|
// the loss.
|
|
//
|
|
|
|
if (StringLength <= TokenLength) {
|
|
|
|
ULONG BytesToCopy, ChunkLength;
|
|
|
|
//
|
|
// take the ending NULL off the top
|
|
//
|
|
|
|
StringLength -= sizeof(WCHAR);
|
|
|
|
//
|
|
// use half the chunk, minus the 4 " .. " characters
|
|
// for the first and last half
|
|
//
|
|
|
|
ChunkLength = StringLength - 4*sizeof(WCHAR);
|
|
ChunkLength /= 2;
|
|
|
|
//
|
|
// make sure it stays even
|
|
//
|
|
|
|
if (ChunkLength % 2)
|
|
ChunkLength -= 1;
|
|
|
|
BytesToCopy = ChunkLength;
|
|
|
|
RtlCopyMemory( String,
|
|
pToken,
|
|
BytesToCopy );
|
|
|
|
String += BytesToCopy/sizeof(WCHAR);
|
|
StringLength -= BytesToCopy;
|
|
|
|
BytesToCopy = 4*sizeof(WCHAR);
|
|
|
|
RtlCopyMemory( String,
|
|
L" .. ",
|
|
BytesToCopy );
|
|
|
|
String += BytesToCopy/sizeof(WCHAR);
|
|
StringLength -= BytesToCopy;
|
|
|
|
BytesToCopy = ChunkLength;
|
|
|
|
RtlCopyMemory( String,
|
|
((PUCHAR)pToken)
|
|
+ TokenLength
|
|
- BytesToCopy,
|
|
BytesToCopy );
|
|
|
|
String += BytesToCopy/sizeof(WCHAR);
|
|
StringLength -= BytesToCopy;
|
|
|
|
String[0] = UNICODE_NULL;
|
|
String += 1;
|
|
|
|
//
|
|
// already subtracted the NULL from the top (see above)
|
|
//
|
|
|
|
} else {
|
|
RtlCopyMemory( String,
|
|
pToken,
|
|
TokenLength );
|
|
|
|
String += TokenLength/sizeof(WCHAR);
|
|
StringLength -= TokenLength;
|
|
|
|
|
|
String[0] = UNICODE_NULL;
|
|
String += 1;
|
|
StringLength -= sizeof(WCHAR);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
String[0] = UNICODE_NULL;
|
|
String += 1;
|
|
StringLength -= sizeof(WCHAR);
|
|
}
|
|
|
|
//
|
|
// put back any reserved length we kept
|
|
//
|
|
|
|
StringLength += ReservedLength;
|
|
|
|
//
|
|
// and put the volume name in there
|
|
//
|
|
|
|
TokenLength = VolumeTokenLength;
|
|
pToken = pVolumeToken;
|
|
|
|
if (TokenLength > 0)
|
|
{
|
|
//
|
|
// The filename string is appended to the end of the error log entry. We
|
|
// may have to smash the middle to fit it in the limited space.
|
|
//
|
|
|
|
//
|
|
// If the name does not fit in the packet, divide the name equally to the
|
|
// prefix and suffix, with an ellipsis " .. " (4 wide characters) to indicate
|
|
// the loss.
|
|
//
|
|
|
|
if (StringLength <= TokenLength) {
|
|
|
|
ULONG BytesToCopy, ChunkLength;
|
|
|
|
//
|
|
// take the ending NULL off the top
|
|
//
|
|
|
|
StringLength -= sizeof(WCHAR);
|
|
|
|
//
|
|
// use half the chunk, minus the 4 " .. " characters
|
|
// for the first and last half
|
|
//
|
|
|
|
ChunkLength = StringLength - 4*sizeof(WCHAR);
|
|
ChunkLength /= 2;
|
|
|
|
//
|
|
// make sure it stays even
|
|
//
|
|
|
|
if (ChunkLength % 2)
|
|
ChunkLength -= 1;
|
|
|
|
BytesToCopy = ChunkLength;
|
|
|
|
RtlCopyMemory( String,
|
|
pToken,
|
|
BytesToCopy );
|
|
|
|
String += BytesToCopy/sizeof(WCHAR);
|
|
StringLength -= BytesToCopy;
|
|
|
|
BytesToCopy = 4*sizeof(WCHAR);
|
|
|
|
RtlCopyMemory( String,
|
|
L" .. ",
|
|
BytesToCopy );
|
|
|
|
String += BytesToCopy/sizeof(WCHAR);
|
|
StringLength -= BytesToCopy;
|
|
|
|
BytesToCopy = ChunkLength;
|
|
|
|
RtlCopyMemory( String,
|
|
((PUCHAR)pToken)
|
|
+ TokenLength
|
|
- BytesToCopy,
|
|
BytesToCopy );
|
|
|
|
String += BytesToCopy/sizeof(WCHAR);
|
|
StringLength -= BytesToCopy;
|
|
|
|
String[0] = UNICODE_NULL;
|
|
String += 1;
|
|
|
|
//
|
|
// already subtracted the NULL from the top (see above)
|
|
//
|
|
|
|
} else {
|
|
RtlCopyMemory( String,
|
|
pToken,
|
|
TokenLength );
|
|
|
|
String += TokenLength/sizeof(WCHAR);
|
|
StringLength -= TokenLength;
|
|
|
|
|
|
String[0] = UNICODE_NULL;
|
|
String += 1;
|
|
StringLength -= sizeof(WCHAR);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
String[0] = UNICODE_NULL;
|
|
String += 1;
|
|
StringLength -= sizeof(WCHAR);
|
|
}
|
|
|
|
IoWriteErrorLogEntry( ErrorLogEntry );
|
|
|
|
RETURN(STATUS_SUCCESS);
|
|
|
|
} // SrLogError
|
|
|
|
|
|
UCHAR
|
|
SrIrpCodeFromEventType(
|
|
IN SR_EVENT_TYPE EventType
|
|
)
|
|
{
|
|
UCHAR Irp;
|
|
|
|
PAGED_CODE();
|
|
|
|
switch (EventType)
|
|
{
|
|
case SrEventStreamChange: Irp = IRP_MJ_WRITE; break;
|
|
case SrEventAclChange: Irp = IRP_MJ_SET_SECURITY; break;
|
|
case SrEventDirectoryCreate:
|
|
case SrEventFileCreate:
|
|
case SrEventStreamOverwrite:Irp = IRP_MJ_CREATE; break;
|
|
case SrEventFileRename:
|
|
case SrEventDirectoryDelete:
|
|
case SrEventDirectoryRename:
|
|
case SrEventFileDelete:
|
|
case SrEventAttribChange: Irp = IRP_MJ_SET_INFORMATION; break;
|
|
case SrEventMountCreate:
|
|
case SrEventMountDelete: Irp = IRP_MJ_FILE_SYSTEM_CONTROL; break;
|
|
default: Irp = IRP_MJ_DEVICE_CONTROL; break;
|
|
} // switch (EventType)
|
|
|
|
return Irp;
|
|
|
|
} // SrIrpCodeFromEventType
|
|
|