reactos/drivers/storage/mountmgr/notify.c

742 lines
22 KiB
C

/*
* ReactOS kernel
* Copyright (C) 2011 ReactOS Team
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS kernel
* FILE: drivers/filesystem/mountmgr/notify.c
* PURPOSE: Mount Manager - Notifications handlers
* PROGRAMMER: Pierre Schweitzer (pierre.schweitzer@reactos.org)
* Alex Ionescu (alex.ionescu@reactos.org)
*/
#include "mntmgr.h"
#include <ioevent.h>
#define NDEBUG
#include <debug.h>
/*
* @implemented
*/
VOID
SendOnlineNotification(IN PUNICODE_STRING SymbolicName)
{
NTSTATUS Status;
PFILE_OBJECT FileObject;
PDEVICE_OBJECT DeviceObject;
/* Get device object */
Status = IoGetDeviceObjectPointer(SymbolicName,
FILE_READ_ATTRIBUTES,
&FileObject,
&DeviceObject);
if (!NT_SUCCESS(Status))
return;
/* And attached device object */
DeviceObject = IoGetAttachedDeviceReference(FileObject->DeviceObject);
/* And send VOLUME_ONLINE */
Status = MountMgrSendSyncDeviceIoCtl(IOCTL_VOLUME_ONLINE,
DeviceObject,
NULL, 0,
NULL, 0,
FileObject);
UNREFERENCED_PARAMETER(Status);
ObDereferenceObject(DeviceObject);
ObDereferenceObject(FileObject);
return;
}
/*
* @implemented
*/
VOID
NTAPI
SendOnlineNotificationWorker(IN PVOID Parameter)
{
KIRQL OldIrql;
PLIST_ENTRY Head;
PDEVICE_EXTENSION DeviceExtension;
PONLINE_NOTIFICATION_WORK_ITEM WorkItem;
PONLINE_NOTIFICATION_WORK_ITEM NewWorkItem;
WorkItem = (PONLINE_NOTIFICATION_WORK_ITEM)Parameter;
DeviceExtension = WorkItem->DeviceExtension;
/* First, send the notification */
SendOnlineNotification(&(WorkItem->SymbolicName));
KeAcquireSpinLock(&(DeviceExtension->WorkerLock), &OldIrql);
/* If there are no notifications running any longer, reset event */
if (--DeviceExtension->OnlineNotificationCount == 0)
{
KeSetEvent(&(DeviceExtension->OnlineNotificationEvent), 0, FALSE);
}
/* If there are still notifications in queue */
if (!IsListEmpty(&(DeviceExtension->OnlineNotificationListHead)))
{
/* Queue a new one for execution */
Head = RemoveHeadList(&(DeviceExtension->OnlineNotificationListHead));
NewWorkItem = CONTAINING_RECORD(Head, ONLINE_NOTIFICATION_WORK_ITEM, WorkItem.List);
KeReleaseSpinLock(&(DeviceExtension->WorkerLock), OldIrql);
NewWorkItem->WorkItem.List.Blink = NULL;
NewWorkItem->WorkItem.List.Flink = NULL;
ExQueueWorkItem(&NewWorkItem->WorkItem, DelayedWorkQueue);
}
else
{
/* Mark it's over */
DeviceExtension->OnlineNotificationWorkerActive = 0;
KeReleaseSpinLock(&(DeviceExtension->WorkerLock), OldIrql);
}
FreePool(WorkItem->SymbolicName.Buffer);
FreePool(WorkItem);
return;
}
/*
* @implemented
*/
VOID
PostOnlineNotification(IN PDEVICE_EXTENSION DeviceExtension,
IN PUNICODE_STRING SymbolicName)
{
KIRQL OldIrql;
PONLINE_NOTIFICATION_WORK_ITEM WorkItem;
/* Allocate a notification work item */
WorkItem = AllocatePool(sizeof(ONLINE_NOTIFICATION_WORK_ITEM));
if (!WorkItem)
{
return;
}
ExInitializeWorkItem(&WorkItem->WorkItem, SendOnlineNotificationWorker, WorkItem);
WorkItem->DeviceExtension = DeviceExtension;
WorkItem->SymbolicName.Length = SymbolicName->Length;
WorkItem->SymbolicName.MaximumLength = SymbolicName->Length + sizeof(WCHAR);
WorkItem->SymbolicName.Buffer = AllocatePool(WorkItem->SymbolicName.MaximumLength);
if (!WorkItem->SymbolicName.Buffer)
{
FreePool(WorkItem);
return;
}
RtlCopyMemory(WorkItem->SymbolicName.Buffer, SymbolicName->Buffer, SymbolicName->Length);
WorkItem->SymbolicName.Buffer[SymbolicName->Length / sizeof(WCHAR)] = UNICODE_NULL;
KeAcquireSpinLock(&(DeviceExtension->WorkerLock), &OldIrql);
DeviceExtension->OnlineNotificationCount++;
/* If no worker are active */
if (DeviceExtension->OnlineNotificationWorkerActive == 0)
{
/* Queue that one for execution */
DeviceExtension->OnlineNotificationWorkerActive = 1;
ExQueueWorkItem(&WorkItem->WorkItem, DelayedWorkQueue);
}
else
{
/* Otherwise, just put it in the queue list */
InsertTailList(&(DeviceExtension->OnlineNotificationListHead), &(WorkItem->WorkItem.List));
}
KeReleaseSpinLock(&(DeviceExtension->WorkerLock), OldIrql);
return;
}
/*
* @implemented
*/
VOID
WaitForOnlinesToComplete(IN PDEVICE_EXTENSION DeviceExtension)
{
KIRQL OldIrql;
KeInitializeEvent(&(DeviceExtension->OnlineNotificationEvent), NotificationEvent, FALSE);
KeAcquireSpinLock(&(DeviceExtension->WorkerLock), &OldIrql);
/* Just wait all the worker are done */
if (DeviceExtension->OnlineNotificationCount != 1)
{
DeviceExtension->OnlineNotificationCount--;
KeReleaseSpinLock(&(DeviceExtension->WorkerLock), OldIrql);
KeWaitForSingleObject(&(DeviceExtension->OnlineNotificationEvent),
Executive,
KernelMode,
FALSE,
NULL);
KeAcquireSpinLock(&(DeviceExtension->WorkerLock), &OldIrql);
DeviceExtension->OnlineNotificationCount++;
}
KeReleaseSpinLock(&(DeviceExtension->WorkerLock), OldIrql);
}
/*
* @implemented
*/
NTSTATUS
NTAPI
MountMgrTargetDeviceNotification(IN PVOID NotificationStructure,
IN PVOID Context)
{
PDEVICE_EXTENSION DeviceExtension;
PDEVICE_INFORMATION DeviceInformation;
PTARGET_DEVICE_CUSTOM_NOTIFICATION Notification;
DeviceInformation = Context;
DeviceExtension = DeviceInformation->DeviceExtension;
Notification = NotificationStructure;
/* The notification have to be unregistered already (in device interface change handler) */
ASSERT(!IsEqualGUID(&Notification->Event, &GUID_TARGET_DEVICE_REMOVE_COMPLETE));
/* It it's to signal that a volume has been mounted
* Verify if a database sync is required and execute it
*/
if (IsEqualGUID(&(Notification->Event), &GUID_IO_VOLUME_MOUNT))
{
/* If we were already mounted, then mark us unmounted */
if (InterlockedCompareExchange(&(DeviceInformation->MountState),
FALSE,
FALSE) == TRUE)
{
InterlockedDecrement(&(DeviceInformation->MountState));
}
/* Otherwise, start mounting the device and first, reconcile its DB if required */
else
{
if (DeviceInformation->NeedsReconcile)
{
DeviceInformation->NeedsReconcile = FALSE;
ReconcileThisDatabaseWithMaster(DeviceExtension, DeviceInformation);
}
}
}
return STATUS_SUCCESS;
}
/*
* @implemented
*/
VOID
RegisterForTargetDeviceNotification(IN PDEVICE_EXTENSION DeviceExtension,
IN PDEVICE_INFORMATION DeviceInformation)
{
NTSTATUS Status;
PFILE_OBJECT FileObject;
PDEVICE_OBJECT DeviceObject;
/* Get device object */
Status = IoGetDeviceObjectPointer(&(DeviceInformation->DeviceName),
FILE_READ_ATTRIBUTES,
&FileObject,
&DeviceObject);
if (!NT_SUCCESS(Status))
{
return;
}
/* And simply register for notifications */
Status = IoRegisterPlugPlayNotification(EventCategoryTargetDeviceChange,
0, FileObject,
DeviceExtension->DriverObject,
MountMgrTargetDeviceNotification,
DeviceInformation,
&(DeviceInformation->TargetDeviceNotificationEntry));
if (!NT_SUCCESS(Status))
{
DeviceInformation->TargetDeviceNotificationEntry = NULL;
}
ObDereferenceObject(FileObject);
return;
}
/*
* @implemented
*/
VOID
MountMgrNotify(IN PDEVICE_EXTENSION DeviceExtension)
{
PIRP Irp;
KIRQL OldIrql;
LIST_ENTRY CopyList;
PLIST_ENTRY NextEntry;
/* Increase the epic number */
DeviceExtension->EpicNumber++;
InitializeListHead(&CopyList);
/* Copy all the pending IRPs for notification */
IoAcquireCancelSpinLock(&OldIrql);
while (!IsListEmpty(&(DeviceExtension->IrpListHead)))
{
NextEntry = RemoveHeadList(&(DeviceExtension->IrpListHead));
Irp = CONTAINING_RECORD(NextEntry, IRP, Tail.Overlay.ListEntry);
IoSetCancelRoutine(Irp, NULL);
InsertTailList(&CopyList, &(Irp->Tail.Overlay.ListEntry));
}
IoReleaseCancelSpinLock(OldIrql);
/* Then, notify them one by one */
while (!IsListEmpty(&CopyList))
{
NextEntry = RemoveHeadList(&CopyList);
Irp = CONTAINING_RECORD(NextEntry, IRP, Tail.Overlay.ListEntry);
*((PULONG)Irp->AssociatedIrp.SystemBuffer) = DeviceExtension->EpicNumber;
Irp->IoStatus.Information = sizeof(DeviceExtension->EpicNumber);
IoCompleteRequest(Irp, IO_NO_INCREMENT);
}
}
/*
* @implemented
*/
VOID
MountMgrNotifyNameChange(IN PDEVICE_EXTENSION DeviceExtension,
IN PUNICODE_STRING DeviceName,
IN BOOLEAN ValidateVolume)
{
PIRP Irp;
KEVENT Event;
NTSTATUS Status;
PLIST_ENTRY NextEntry;
PFILE_OBJECT FileObject;
PIO_STACK_LOCATION Stack;
PDEVICE_OBJECT DeviceObject;
IO_STATUS_BLOCK IoStatusBlock;
PDEVICE_RELATIONS DeviceRelations;
PDEVICE_INFORMATION DeviceInformation;
TARGET_DEVICE_CUSTOM_NOTIFICATION DeviceNotification;
/* If we have to validate volume */
if (ValidateVolume)
{
/* Then, ensure we can find the device */
for (NextEntry = DeviceExtension->DeviceListHead.Flink;
NextEntry != &DeviceExtension->DeviceListHead;
NextEntry = NextEntry->Flink)
{
DeviceInformation = CONTAINING_RECORD(NextEntry, DEVICE_INFORMATION, DeviceListEntry);
if (RtlCompareUnicodeString(DeviceName, &(DeviceInformation->DeviceName), TRUE) == 0)
{
break;
}
}
/* No need to notify for a PnP device or if we didn't find the device */
if (NextEntry == &(DeviceExtension->DeviceListHead) ||
!DeviceInformation->ManuallyRegistered)
{
return;
}
}
/* Then, get device object */
Status = IoGetDeviceObjectPointer(DeviceName,
FILE_READ_ATTRIBUTES,
&FileObject,
&DeviceObject);
if (!NT_SUCCESS(Status))
{
return;
}
DeviceObject = IoGetAttachedDeviceReference(FileObject->DeviceObject);
KeInitializeEvent(&Event, NotificationEvent, FALSE);
/* Set up empty IRP (yes, yes!) */
Irp = IoBuildDeviceIoControlRequest(0,
DeviceObject,
NULL,
0,
NULL,
0,
FALSE,
&Event,
&IoStatusBlock);
if (!Irp)
{
ObDereferenceObject(DeviceObject);
ObDereferenceObject(FileObject);
return;
}
Stack = IoGetNextIrpStackLocation(Irp);
Irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
Irp->IoStatus.Information = 0;
/* Properly set it, we want to query device relations */
Stack->MajorFunction = IRP_MJ_PNP;
Stack->MinorFunction = IRP_MN_QUERY_DEVICE_RELATIONS;
Stack->Parameters.QueryDeviceRelations.Type = TargetDeviceRelation;
Stack->FileObject = FileObject;
/* And call driver */
Status = IoCallDriver(DeviceObject, Irp);
if (Status == STATUS_PENDING)
{
KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
Status = IoStatusBlock.Status;
}
ObDereferenceObject(DeviceObject);
ObDereferenceObject(FileObject);
if (!NT_SUCCESS(Status))
{
return;
}
/* Validate device return */
DeviceRelations = (PDEVICE_RELATIONS)IoStatusBlock.Information;
if (DeviceRelations->Count < 1)
{
ExFreePool(DeviceRelations);
return;
}
DeviceObject = DeviceRelations->Objects[0];
ExFreePool(DeviceRelations);
/* Set up real notification */
DeviceNotification.Version = 1;
DeviceNotification.Size = sizeof(TARGET_DEVICE_CUSTOM_NOTIFICATION);
DeviceNotification.Event = GUID_IO_VOLUME_NAME_CHANGE;
DeviceNotification.FileObject = NULL;
DeviceNotification.NameBufferOffset = -1;
/* And report */
IoReportTargetDeviceChangeAsynchronous(DeviceObject,
&DeviceNotification,
NULL, NULL);
ObDereferenceObject(DeviceObject);
return;
}
/*
* @implemented
*/
VOID
RemoveWorkItem(IN PUNIQUE_ID_WORK_ITEM WorkItem)
{
PDEVICE_EXTENSION DeviceExtension = WorkItem->DeviceExtension;
KeWaitForSingleObject(&(DeviceExtension->DeviceLock), Executive, KernelMode, FALSE, NULL);
/* If even if being worked, it's too late */
if (WorkItem->Event)
{
KeReleaseSemaphore(&(DeviceExtension->DeviceLock), IO_NO_INCREMENT, 1, FALSE);
KeSetEvent(WorkItem->Event, 0, FALSE);
}
else
{
/* Otherwise, remove it from the list, and delete it */
RemoveEntryList(&(WorkItem->UniqueIdWorkerItemListEntry));
KeReleaseSemaphore(&(DeviceExtension->DeviceLock), IO_NO_INCREMENT, 1, FALSE);
IoFreeIrp(WorkItem->Irp);
FreePool(WorkItem->DeviceName.Buffer);
FreePool(WorkItem->IrpBuffer);
FreePool(WorkItem);
}
}
/*
* @implemented
*/
VOID
NTAPI
UniqueIdChangeNotifyWorker(IN PDEVICE_OBJECT DeviceObject,
IN PVOID Context)
{
PUNIQUE_ID_WORK_ITEM WorkItem = Context;
PMOUNTDEV_UNIQUE_ID OldUniqueId, NewUniqueId;
PMOUNTDEV_UNIQUE_ID_CHANGE_NOTIFY_OUTPUT UniqueIdChange;
UNREFERENCED_PARAMETER(DeviceObject);
/* Validate worker */
if (!NT_SUCCESS(WorkItem->Irp->IoStatus.Status))
{
RemoveWorkItem(WorkItem);
return;
}
UniqueIdChange = WorkItem->Irp->AssociatedIrp.SystemBuffer;
/* Get the old unique ID */
OldUniqueId = AllocatePool(UniqueIdChange->OldUniqueIdLength + sizeof(MOUNTDEV_UNIQUE_ID));
if (!OldUniqueId)
{
RemoveWorkItem(WorkItem);
return;
}
OldUniqueId->UniqueIdLength = UniqueIdChange->OldUniqueIdLength;
RtlCopyMemory(OldUniqueId->UniqueId,
(PVOID)((ULONG_PTR)UniqueIdChange + UniqueIdChange->OldUniqueIdOffset),
UniqueIdChange->OldUniqueIdLength);
/* Get the new unique ID */
NewUniqueId = AllocatePool(UniqueIdChange->NewUniqueIdLength + sizeof(MOUNTDEV_UNIQUE_ID));
if (!NewUniqueId)
{
FreePool(OldUniqueId);
RemoveWorkItem(WorkItem);
return;
}
NewUniqueId->UniqueIdLength = UniqueIdChange->NewUniqueIdLength;
RtlCopyMemory(NewUniqueId->UniqueId,
(PVOID)((ULONG_PTR)UniqueIdChange + UniqueIdChange->NewUniqueIdOffset),
UniqueIdChange->NewUniqueIdLength);
/* Call the real worker */
MountMgrUniqueIdChangeRoutine(WorkItem->DeviceExtension, OldUniqueId, NewUniqueId);
IssueUniqueIdChangeNotifyWorker(WorkItem, NewUniqueId);
FreePool(NewUniqueId);
FreePool(OldUniqueId);
return;
}
/*
* @implemented
*/
NTSTATUS
NTAPI
UniqueIdChangeNotifyCompletion(IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context)
{
PUNIQUE_ID_WORK_ITEM WorkItem = Context;
UNREFERENCED_PARAMETER(DeviceObject);
UNREFERENCED_PARAMETER(Irp);
/* Simply queue the work item */
IoQueueWorkItem(WorkItem->WorkItem,
UniqueIdChangeNotifyWorker,
DelayedWorkQueue,
WorkItem);
return STATUS_MORE_PROCESSING_REQUIRED;
}
/*
* @implemented
*/
VOID
IssueUniqueIdChangeNotifyWorker(IN PUNIQUE_ID_WORK_ITEM WorkItem,
IN PMOUNTDEV_UNIQUE_ID UniqueId)
{
PIRP Irp;
NTSTATUS Status;
PFILE_OBJECT FileObject;
PIO_STACK_LOCATION Stack;
PDEVICE_OBJECT DeviceObject;
/* Get the device object */
Status = IoGetDeviceObjectPointer(&(WorkItem->DeviceName),
FILE_READ_ATTRIBUTES,
&FileObject,
&DeviceObject);
if (!NT_SUCCESS(Status))
{
RemoveWorkItem(WorkItem);
return;
}
/* And then, the attached device */
DeviceObject = IoGetAttachedDeviceReference(FileObject->DeviceObject);
/* Initialize the IRP */
Irp = WorkItem->Irp;
IoInitializeIrp(Irp, IoSizeOfIrp(WorkItem->StackSize), (CCHAR)WorkItem->StackSize);
if (InterlockedExchange((PLONG)&(WorkItem->Event), 0) != 0)
{
ObDereferenceObject(FileObject);
ObDereferenceObject(DeviceObject);
RemoveWorkItem(WorkItem);
return;
}
Irp->AssociatedIrp.SystemBuffer = WorkItem->IrpBuffer;
Irp->Tail.Overlay.Thread = PsGetCurrentThread();
RtlCopyMemory(Irp->AssociatedIrp.SystemBuffer, UniqueId, UniqueId->UniqueIdLength + sizeof(USHORT));
Stack = IoGetNextIrpStackLocation(Irp);
Stack->Parameters.DeviceIoControl.InputBufferLength = UniqueId->UniqueIdLength + sizeof(USHORT);
Stack->Parameters.DeviceIoControl.OutputBufferLength = WorkItem->IrpBufferLength;
Stack->Parameters.DeviceIoControl.Type3InputBuffer = 0;
Stack->Parameters.DeviceIoControl.IoControlCode = IOCTL_MOUNTDEV_UNIQUE_ID_CHANGE_NOTIFY;
Stack->MajorFunction = IRP_MJ_DEVICE_CONTROL;
Status = IoSetCompletionRoutineEx(WorkItem->DeviceExtension->DeviceObject,
Irp,
UniqueIdChangeNotifyCompletion,
WorkItem,
TRUE, TRUE, TRUE);
if (!NT_SUCCESS(Status))
{
ObDereferenceObject(FileObject);
ObDereferenceObject(DeviceObject);
RemoveWorkItem(WorkItem);
return;
}
/* Call the driver */
IoCallDriver(DeviceObject, Irp);
ObDereferenceObject(FileObject);
ObDereferenceObject(DeviceObject);
}
/*
* @implemented
*/
VOID
IssueUniqueIdChangeNotify(IN PDEVICE_EXTENSION DeviceExtension,
IN PUNICODE_STRING DeviceName,
IN PMOUNTDEV_UNIQUE_ID UniqueId)
{
NTSTATUS Status;
PVOID IrpBuffer = NULL;
PFILE_OBJECT FileObject;
PDEVICE_OBJECT DeviceObject;
PUNIQUE_ID_WORK_ITEM WorkItem = NULL;
/* Get the associated device object */
Status = IoGetDeviceObjectPointer(DeviceName,
FILE_READ_ATTRIBUTES,
&FileObject,
&DeviceObject);
if (!NT_SUCCESS(Status))
{
return;
}
/* And then, get attached device */
DeviceObject = IoGetAttachedDeviceReference(FileObject->DeviceObject);
ObDereferenceObject(FileObject);
/* Allocate a work item */
WorkItem = AllocatePool(sizeof(UNIQUE_ID_WORK_ITEM));
if (!WorkItem)
{
ObDereferenceObject(DeviceObject);
return;
}
WorkItem->Event = NULL;
WorkItem->WorkItem = IoAllocateWorkItem(DeviceExtension->DeviceObject);
if (!WorkItem->WorkItem)
{
ObDereferenceObject(DeviceObject);
goto Cleanup;
}
WorkItem->DeviceExtension = DeviceExtension;
WorkItem->StackSize = DeviceObject->StackSize;
/* Already provide the IRP */
WorkItem->Irp = IoAllocateIrp(DeviceObject->StackSize, FALSE);
ObDereferenceObject(DeviceObject);
if (!WorkItem->Irp)
{
goto Cleanup;
}
/* Ensure it has enough space */
IrpBuffer = AllocatePool(sizeof(MOUNTDEV_UNIQUE_ID_CHANGE_NOTIFY_OUTPUT) + 1024);
if (!IrpBuffer)
{
goto Cleanup;
}
WorkItem->DeviceName.Length = DeviceName->Length;
WorkItem->DeviceName.MaximumLength = DeviceName->Length + sizeof(WCHAR);
WorkItem->DeviceName.Buffer = AllocatePool(WorkItem->DeviceName.MaximumLength);
if (!WorkItem->DeviceName.Buffer)
{
goto Cleanup;
}
RtlCopyMemory(WorkItem->DeviceName.Buffer, DeviceName->Buffer, DeviceName->Length);
WorkItem->DeviceName.Buffer[DeviceName->Length / sizeof(WCHAR)] = UNICODE_NULL;
WorkItem->IrpBuffer = IrpBuffer;
WorkItem->IrpBufferLength = sizeof(MOUNTDEV_UNIQUE_ID_CHANGE_NOTIFY_OUTPUT) + 1024;
/* Add the worker in the list */
KeWaitForSingleObject(&(DeviceExtension->DeviceLock), Executive, KernelMode, FALSE, NULL);
InsertHeadList(&(DeviceExtension->UniqueIdWorkerItemListHead), &(WorkItem->UniqueIdWorkerItemListEntry));
KeReleaseSemaphore(&(DeviceExtension->DeviceLock), IO_NO_INCREMENT, 1, FALSE);
/* And call the worker */
IssueUniqueIdChangeNotifyWorker(WorkItem, UniqueId);
return;
Cleanup:
if (IrpBuffer)
{
FreePool(IrpBuffer);
}
if (WorkItem->Irp)
{
IoFreeIrp(WorkItem->Irp);
}
if (WorkItem->WorkItem)
{
IoFreeWorkItem(WorkItem->WorkItem);
}
if (WorkItem)
{
FreePool(WorkItem);
}
}