3 * Copyright (C) 2011 ReactOS Team
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
19 * COPYRIGHT: See COPYING in the top level directory
20 * PROJECT: ReactOS kernel
21 * FILE: drivers/filesystem/mountmgr/notify.c
22 * PURPOSE: Mount Manager - Notifications handlers
23 * PROGRAMMER: Pierre Schweitzer (pierre.schweitzer@reactos.org)
24 * Alex Ionescu (alex.ionescu@reactos.org)
27 /* INCLUDES *****************************************************************/
38 SendOnlineNotification(IN PUNICODE_STRING SymbolicName
)
43 PFILE_OBJECT FileObject
;
44 PIO_STACK_LOCATION Stack
;
45 PDEVICE_OBJECT DeviceObject
;
46 IO_STATUS_BLOCK IoStatusBlock
;
48 /* Get device object */
49 Status
= IoGetDeviceObjectPointer(SymbolicName
,
53 if (!NT_SUCCESS(Status
))
58 /* And attached device object */
59 DeviceObject
= IoGetAttachedDeviceReference(FileObject
->DeviceObject
);
61 /* And send VOLUME_ONLINE */
62 KeInitializeEvent(&Event
, NotificationEvent
, FALSE
);
63 Irp
= IoBuildDeviceIoControlRequest(IOCTL_VOLUME_ONLINE
,
75 Stack
= IoGetNextIrpStackLocation(Irp
);
76 Stack
->FileObject
= FileObject
;
78 Status
= IoCallDriver(DeviceObject
, Irp
);
79 if (Status
== STATUS_PENDING
)
81 KeWaitForSingleObject(&Event
, Executive
, KernelMode
, FALSE
, NULL
);
85 ObDereferenceObject(DeviceObject
);
86 ObDereferenceObject(FileObject
);
96 SendOnlineNotificationWorker(IN PVOID Parameter
)
100 PDEVICE_EXTENSION DeviceExtension
;
101 PONLINE_NOTIFICATION_WORK_ITEM WorkItem
;
102 PONLINE_NOTIFICATION_WORK_ITEM NewWorkItem
;
104 WorkItem
= (PONLINE_NOTIFICATION_WORK_ITEM
)Parameter
;
105 DeviceExtension
= WorkItem
->DeviceExtension
;
107 /* First, send the notification */
108 SendOnlineNotification(&(WorkItem
->SymbolicName
));
110 KeAcquireSpinLock(&(DeviceExtension
->WorkerLock
), &OldIrql
);
111 /* If there are no notifications running any longer, reset event */
112 if (--DeviceExtension
->OnlineNotificationCount
== 0)
114 KeSetEvent(&(DeviceExtension
->OnlineNotificationEvent
), 0, FALSE
);
117 /* If there are still notifications in queue */
118 if (!IsListEmpty(&(DeviceExtension
->OnlineNotificationListHead
)))
120 /* Queue a new one for execution */
121 Head
= RemoveHeadList(&(DeviceExtension
->OnlineNotificationListHead
));
122 NewWorkItem
= CONTAINING_RECORD(Head
, ONLINE_NOTIFICATION_WORK_ITEM
, List
);
123 KeReleaseSpinLock(&(DeviceExtension
->WorkerLock
), OldIrql
);
124 NewWorkItem
->List
.Blink
= NULL
;
125 NewWorkItem
->List
.Flink
= NULL
;
126 ExQueueWorkItem((PWORK_QUEUE_ITEM
)NewWorkItem
, DelayedWorkQueue
);
131 DeviceExtension
->OnlineNotificationWorkerActive
= 0;
132 KeReleaseSpinLock(&(DeviceExtension
->WorkerLock
), OldIrql
);
135 FreePool(WorkItem
->SymbolicName
.Buffer
);
145 PostOnlineNotification(IN PDEVICE_EXTENSION DeviceExtension
,
146 IN PUNICODE_STRING SymbolicName
)
149 PONLINE_NOTIFICATION_WORK_ITEM WorkItem
;
151 /* Allocate a notification work item */
152 WorkItem
= AllocatePool(sizeof(ONLINE_NOTIFICATION_WORK_ITEM
));
158 WorkItem
->List
.Flink
= NULL
;
159 WorkItem
->DeviceExtension
= DeviceExtension
;
160 WorkItem
->WorkerRoutine
= SendOnlineNotificationWorker
;
161 WorkItem
->Parameter
= WorkItem
;
162 WorkItem
->SymbolicName
.Length
= SymbolicName
->Length
;
163 WorkItem
->SymbolicName
.MaximumLength
= SymbolicName
->Length
+ sizeof(WCHAR
);
164 WorkItem
->SymbolicName
.Buffer
= AllocatePool(WorkItem
->SymbolicName
.MaximumLength
);
165 if (!WorkItem
->SymbolicName
.Buffer
)
171 RtlCopyMemory(WorkItem
->SymbolicName
.Buffer
, SymbolicName
->Buffer
, SymbolicName
->Length
);
172 WorkItem
->SymbolicName
.Buffer
[SymbolicName
->Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
174 KeAcquireSpinLock(&(DeviceExtension
->WorkerLock
), &OldIrql
);
175 DeviceExtension
->OnlineNotificationCount
++;
177 /* If no worker are active */
178 if (DeviceExtension
->OnlineNotificationWorkerActive
== 0)
180 /* Queue that one for execution */
181 DeviceExtension
->OnlineNotificationWorkerActive
= 1;
182 ExQueueWorkItem((PWORK_QUEUE_ITEM
)WorkItem
, DelayedWorkQueue
);
186 /* Otherwise, just put it in the queue list */
187 InsertTailList(&(DeviceExtension
->OnlineNotificationListHead
), &(WorkItem
->List
));
190 KeReleaseSpinLock(&(DeviceExtension
->WorkerLock
), OldIrql
);
199 WaitForOnlinesToComplete(IN PDEVICE_EXTENSION DeviceExtension
)
203 KeInitializeEvent(&(DeviceExtension
->OnlineNotificationEvent
), NotificationEvent
, FALSE
);
205 KeAcquireSpinLock(&(DeviceExtension
->WorkerLock
), &OldIrql
);
207 /* Just wait all the worker are done */
208 if (DeviceExtension
->OnlineNotificationCount
!= 1)
210 DeviceExtension
->OnlineNotificationCount
--;
211 KeReleaseSpinLock(&(DeviceExtension
->WorkerLock
), OldIrql
);
213 KeWaitForSingleObject(&(DeviceExtension
->OnlineNotificationEvent
),
219 KeAcquireSpinLock(&(DeviceExtension
->WorkerLock
), &OldIrql
);
220 DeviceExtension
->OnlineNotificationCount
++;
223 KeReleaseSpinLock(&(DeviceExtension
->WorkerLock
), OldIrql
);
231 MountMgrTargetDeviceNotification(IN PVOID NotificationStructure
,
234 PDEVICE_EXTENSION DeviceExtension
;
235 PDEVICE_INFORMATION DeviceInformation
;
236 PDEVICE_INTERFACE_CHANGE_NOTIFICATION Notification
;
238 DeviceInformation
= Context
;
239 DeviceExtension
= DeviceInformation
->DeviceExtension
;
240 Notification
= NotificationStructure
;
242 /* If it's to signal that removal is complete, then, execute the function */
243 if (IsEqualGUID(&(Notification
->Event
), &GUID_TARGET_DEVICE_REMOVE_COMPLETE
))
245 MountMgrMountedDeviceRemoval(DeviceExtension
, Notification
->SymbolicLinkName
);
247 /* It it's to signal that a volume has been mounted
248 * Verify if a database sync is required and execute it
250 else if (IsEqualGUID(&(Notification
->Event
), &GUID_IO_VOLUME_MOUNT
))
252 if (InterlockedCompareExchange(&(DeviceInformation
->MountState
),
256 InterlockedDecrement(&(DeviceInformation
->MountState
));
260 if (DeviceInformation
->NeedsReconcile
)
262 DeviceInformation
->NeedsReconcile
= FALSE
;
263 ReconcileThisDatabaseWithMaster(DeviceExtension
, DeviceInformation
);
268 return STATUS_SUCCESS
;
275 RegisterForTargetDeviceNotification(IN PDEVICE_EXTENSION DeviceExtension
,
276 IN PDEVICE_INFORMATION DeviceInformation
)
279 PFILE_OBJECT FileObject
;
280 PDEVICE_OBJECT DeviceObject
;
282 /* Get device object */
283 Status
= IoGetDeviceObjectPointer(&(DeviceInformation
->DeviceName
),
284 FILE_READ_ATTRIBUTES
,
287 if (!NT_SUCCESS(Status
))
292 /* And simply register for notifications */
293 Status
= IoRegisterPlugPlayNotification(EventCategoryTargetDeviceChange
,
295 DeviceExtension
->DriverObject
,
296 MountMgrTargetDeviceNotification
,
298 &(DeviceInformation
->TargetDeviceNotificationEntry
));
299 if (!NT_SUCCESS(Status
))
301 DeviceInformation
->TargetDeviceNotificationEntry
= NULL
;
304 ObDereferenceObject(FileObject
);
313 MountMgrNotify(IN PDEVICE_EXTENSION DeviceExtension
)
318 PLIST_ENTRY NextEntry
;
320 /* Increase the epic number */
321 DeviceExtension
->EpicNumber
++;
323 InitializeListHead(&CopyList
);
325 /* Copy all the pending IRPs for notification */
326 IoAcquireCancelSpinLock(&OldIrql
);
327 while (!IsListEmpty(&(DeviceExtension
->IrpListHead
)))
329 NextEntry
= RemoveHeadList(&(DeviceExtension
->IrpListHead
));
330 Irp
= CONTAINING_RECORD(NextEntry
, IRP
, Tail
.Overlay
.ListEntry
);
331 InsertTailList(&CopyList
, &(Irp
->Tail
.Overlay
.ListEntry
));
333 IoReleaseCancelSpinLock(OldIrql
);
335 /* Then, notifiy them one by one */
336 while (!IsListEmpty(&CopyList
))
338 NextEntry
= RemoveHeadList(&CopyList
);
339 Irp
= CONTAINING_RECORD(NextEntry
, IRP
, Tail
.Overlay
.ListEntry
);
341 *((PULONG
)Irp
->AssociatedIrp
.SystemBuffer
) = DeviceExtension
->EpicNumber
;
342 Irp
->IoStatus
.Information
= sizeof(DeviceExtension
->EpicNumber
);
344 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
352 MountMgrNotifyNameChange(IN PDEVICE_EXTENSION DeviceExtension
,
353 IN PUNICODE_STRING DeviceName
,
354 IN BOOLEAN ValidateVolume
)
359 PLIST_ENTRY NextEntry
;
360 PFILE_OBJECT FileObject
;
361 PIO_STACK_LOCATION Stack
;
362 PDEVICE_OBJECT DeviceObject
;
363 IO_STATUS_BLOCK IoStatusBlock
;
364 PDEVICE_RELATIONS DeviceRelations
;
365 PDEVICE_INFORMATION DeviceInformation
;
366 TARGET_DEVICE_CUSTOM_NOTIFICATION DeviceNotification
;
368 /* If we have to validate volume */
371 /* Then, ensure we can find the device */
372 NextEntry
= DeviceExtension
->DeviceListHead
.Flink
;
373 while (NextEntry
!= &(DeviceExtension
->DeviceListHead
))
375 DeviceInformation
= CONTAINING_RECORD(NextEntry
, DEVICE_INFORMATION
, DeviceListEntry
);
376 if (RtlCompareUnicodeString(DeviceName
, &(DeviceInformation
->DeviceName
), TRUE
) == 0)
382 if (NextEntry
== &(DeviceExtension
->DeviceListHead
) ||
383 !DeviceInformation
->Volume
)
389 /* Then, get device object */
390 Status
= IoGetDeviceObjectPointer(DeviceName
,
391 FILE_READ_ATTRIBUTES
,
394 if (!NT_SUCCESS(Status
))
399 DeviceObject
= IoGetAttachedDeviceReference(FileObject
->DeviceObject
);
401 KeInitializeEvent(&Event
, NotificationEvent
, FALSE
);
403 /* Set up empty IRP (yes, yes!) */
404 Irp
= IoBuildDeviceIoControlRequest(0,
415 ObDereferenceObject(DeviceObject
);
416 ObDereferenceObject(FileObject
);
419 Stack
= IoGetNextIrpStackLocation(Irp
);
421 Irp
->IoStatus
.Status
= STATUS_NOT_SUPPORTED
;
422 Irp
->IoStatus
.Information
= 0;
424 /* Properly set it, we want to query device relations */
425 Stack
->MajorFunction
= IRP_MJ_PNP
;
426 Stack
->MinorFunction
= IRP_MN_QUERY_DEVICE_RELATIONS
;
427 Stack
->Parameters
.QueryDeviceRelations
.Type
= TargetDeviceRelation
;
428 Stack
->FileObject
= FileObject
;
430 /* And call driver */
431 Status
= IoCallDriver(DeviceObject
, Irp
);
432 if (Status
== STATUS_PENDING
)
434 KeWaitForSingleObject(&Event
, Executive
, KernelMode
, FALSE
, NULL
);
435 Status
= IoStatusBlock
.Status
;
438 ObDereferenceObject(DeviceObject
);
439 ObDereferenceObject(FileObject
);
441 if (!NT_SUCCESS(Status
))
446 /* Validate device return */
447 DeviceRelations
= (PDEVICE_RELATIONS
)IoStatusBlock
.Information
;
448 if (DeviceRelations
->Count
< 1)
450 ExFreePool(DeviceRelations
);
454 DeviceObject
= DeviceRelations
->Objects
[0];
455 ExFreePool(DeviceRelations
);
457 /* Set up real notification */
458 DeviceNotification
.Version
= 1;
459 DeviceNotification
.Size
= sizeof(TARGET_DEVICE_CUSTOM_NOTIFICATION
);
460 DeviceNotification
.Event
= GUID_IO_VOLUME_NAME_CHANGE
;
461 DeviceNotification
.FileObject
= NULL
;
462 DeviceNotification
.NameBufferOffset
= -1;
465 IoReportTargetDeviceChangeAsynchronous(DeviceObject
,
469 ObDereferenceObject(DeviceObject
);
478 RemoveWorkItem(IN PUNIQUE_ID_WORK_ITEM WorkItem
)
480 PDEVICE_EXTENSION DeviceExtension
= WorkItem
->DeviceExtension
;
482 KeWaitForSingleObject(&(DeviceExtension
->DeviceLock
), Executive
, KernelMode
, FALSE
, NULL
);
484 /* If even if being worked, it's too late */
487 KeReleaseSemaphore(&(DeviceExtension
->DeviceLock
), IO_NO_INCREMENT
, 1, FALSE
);
488 KeSetEvent(WorkItem
->Event
, 0, FALSE
);
492 /* Otherwise, remove it from the list, and delete it */
493 RemoveEntryList(&(WorkItem
->UniqueIdWorkerItemListEntry
));
494 KeReleaseSemaphore(&(DeviceExtension
->DeviceLock
), IO_NO_INCREMENT
, 1, FALSE
);
495 IoFreeIrp(WorkItem
->Irp
);
496 FreePool(WorkItem
->DeviceName
.Buffer
);
497 FreePool(WorkItem
->IrpBuffer
);
507 UniqueIdChangeNotifyWorker(IN PDEVICE_OBJECT DeviceObject
,
510 PUNIQUE_ID_WORK_ITEM WorkItem
= Context
;
511 PMOUNTDEV_UNIQUE_ID OldUniqueId
, NewUniqueId
;
512 PMOUNTDEV_UNIQUE_ID_CHANGE_NOTIFY_OUTPUT UniqueIdChange
;
514 /* Validate worker */
515 if (!NT_SUCCESS(WorkItem
->Irp
->IoStatus
.Status
))
517 RemoveWorkItem(WorkItem
);
521 UniqueIdChange
= WorkItem
->Irp
->AssociatedIrp
.SystemBuffer
;
522 /* Get the old unique ID */
523 OldUniqueId
= AllocatePool(UniqueIdChange
->OldUniqueIdLength
+ sizeof(MOUNTDEV_UNIQUE_ID
));
526 RemoveWorkItem(WorkItem
);
530 OldUniqueId
->UniqueIdLength
= UniqueIdChange
->OldUniqueIdLength
;
531 RtlCopyMemory(OldUniqueId
->UniqueId
,
532 (PVOID
)((ULONG_PTR
)UniqueIdChange
+ UniqueIdChange
->OldUniqueIdOffset
),
533 UniqueIdChange
->OldUniqueIdLength
);
535 /* Get the new unique ID */
536 NewUniqueId
= AllocatePool(UniqueIdChange
->NewUniqueIdLength
+ sizeof(MOUNTDEV_UNIQUE_ID
));
539 FreePool(OldUniqueId
);
540 RemoveWorkItem(WorkItem
);
544 NewUniqueId
->UniqueIdLength
= UniqueIdChange
->NewUniqueIdLength
;
545 RtlCopyMemory(NewUniqueId
->UniqueId
,
546 (PVOID
)((ULONG_PTR
)UniqueIdChange
+ UniqueIdChange
->NewUniqueIdOffset
),
547 UniqueIdChange
->NewUniqueIdLength
);
549 /* Call the real worker */
550 MountMgrUniqueIdChangeRoutine(WorkItem
->DeviceExtension
, OldUniqueId
, NewUniqueId
);
551 IssueUniqueIdChangeNotifyWorker(WorkItem
, NewUniqueId
);
553 FreePool(NewUniqueId
);
554 FreePool(OldUniqueId
);
564 UniqueIdChangeNotifyCompletion(IN PDEVICE_OBJECT DeviceObject
,
568 PUNIQUE_ID_WORK_ITEM WorkItem
= Context
;
570 /* Simply queue the work item */
571 IoQueueWorkItem(WorkItem
->WorkItem
,
572 UniqueIdChangeNotifyWorker
,
576 return STATUS_MORE_PROCESSING_REQUIRED
;
583 IssueUniqueIdChangeNotifyWorker(IN PUNIQUE_ID_WORK_ITEM WorkItem
,
584 IN PMOUNTDEV_UNIQUE_ID UniqueId
)
588 PFILE_OBJECT FileObject
;
589 PIO_STACK_LOCATION Stack
;
590 PDEVICE_OBJECT DeviceObject
;
592 /* Get the device object */
593 Status
= IoGetDeviceObjectPointer(&(WorkItem
->DeviceName
),
594 FILE_READ_ATTRIBUTES
,
597 if (!NT_SUCCESS(Status
))
599 RemoveWorkItem(WorkItem
);
603 /* And then, the attached device */
604 DeviceObject
= IoGetAttachedDeviceReference(FileObject
->DeviceObject
);
606 /* Initialize the IRP */
608 IoInitializeIrp(Irp
, IoSizeOfIrp(WorkItem
->StackSize
), (CCHAR
)WorkItem
->StackSize
);
610 if (InterlockedExchange((PLONG
)&(WorkItem
->Event
), 0) != 0)
612 ObDereferenceObject(FileObject
);
613 ObDereferenceObject(DeviceObject
);
614 RemoveWorkItem(WorkItem
);
618 Irp
->AssociatedIrp
.SystemBuffer
= WorkItem
->IrpBuffer
;
619 Irp
->Tail
.Overlay
.Thread
= PsGetCurrentThread();
620 RtlCopyMemory(Irp
->AssociatedIrp
.SystemBuffer
, UniqueId
, UniqueId
->UniqueIdLength
+ sizeof(USHORT
));
622 Stack
= IoGetNextIrpStackLocation(Irp
);
624 Stack
->Parameters
.DeviceIoControl
.InputBufferLength
= UniqueId
->UniqueIdLength
+ sizeof(USHORT
);
625 Stack
->Parameters
.DeviceIoControl
.OutputBufferLength
= WorkItem
->IrpBufferLength
;
626 Stack
->Parameters
.DeviceIoControl
.Type3InputBuffer
= 0;
627 Stack
->Parameters
.DeviceIoControl
.IoControlCode
= IOCTL_MOUNTDEV_UNIQUE_ID_CHANGE_NOTIFY
;
628 Stack
->MajorFunction
= IRP_MJ_DEVICE_CONTROL
;
630 Status
= IoSetCompletionRoutineEx(WorkItem
->DeviceExtension
->DeviceObject
,
632 UniqueIdChangeNotifyCompletion
,
635 if (!NT_SUCCESS(Status
))
637 ObDereferenceObject(FileObject
);
638 ObDereferenceObject(DeviceObject
);
639 RemoveWorkItem(WorkItem
);
643 /* Call the driver */
644 IoCallDriver(DeviceObject
, Irp
);
645 ObDereferenceObject(FileObject
);
646 ObDereferenceObject(DeviceObject
);
653 IssueUniqueIdChangeNotify(IN PDEVICE_EXTENSION DeviceExtension
,
654 IN PUNICODE_STRING DeviceName
,
655 IN PMOUNTDEV_UNIQUE_ID UniqueId
)
658 PVOID IrpBuffer
= NULL
;
659 PFILE_OBJECT FileObject
;
660 PDEVICE_OBJECT DeviceObject
;
661 PUNIQUE_ID_WORK_ITEM WorkItem
= NULL
;
663 /* Get the associated device object */
664 Status
= IoGetDeviceObjectPointer(DeviceName
,
665 FILE_READ_ATTRIBUTES
,
668 if (!NT_SUCCESS(Status
))
673 /* And then, get attached device */
674 DeviceObject
= IoGetAttachedDeviceReference(FileObject
->DeviceObject
);
676 ObDereferenceObject(FileObject
);
678 /* Allocate a work item */
679 WorkItem
= AllocatePool(sizeof(UNIQUE_ID_WORK_ITEM
));
682 ObDereferenceObject(DeviceObject
);
686 WorkItem
->Event
= NULL
;
687 WorkItem
->WorkItem
= IoAllocateWorkItem(DeviceExtension
->DeviceObject
);
688 if (!WorkItem
->WorkItem
)
690 ObDereferenceObject(DeviceObject
);
694 WorkItem
->DeviceExtension
= DeviceExtension
;
695 WorkItem
->StackSize
= DeviceObject
->StackSize
;
696 /* Already provide the IRP */
697 WorkItem
->Irp
= IoAllocateIrp(DeviceObject
->StackSize
, FALSE
);
699 ObDereferenceObject(DeviceObject
);
706 /* Ensure it has enough space */
707 IrpBuffer
= AllocatePool(sizeof(MOUNTDEV_UNIQUE_ID_CHANGE_NOTIFY_OUTPUT
) + 1024);
713 WorkItem
->DeviceName
.Length
= DeviceName
->Length
;
714 WorkItem
->DeviceName
.MaximumLength
= DeviceName
->Length
+ sizeof(WCHAR
);
715 WorkItem
->DeviceName
.Buffer
= AllocatePool(WorkItem
->DeviceName
.MaximumLength
);
716 if (!WorkItem
->DeviceName
.Buffer
)
721 RtlCopyMemory(WorkItem
->DeviceName
.Buffer
, DeviceName
->Buffer
, DeviceName
->Length
);
722 WorkItem
->DeviceName
.Buffer
[DeviceName
->Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
724 WorkItem
->IrpBuffer
= IrpBuffer
;
725 WorkItem
->IrpBufferLength
= sizeof(MOUNTDEV_UNIQUE_ID_CHANGE_NOTIFY_OUTPUT
) + 1024;
727 /* Add the worker in the list */
728 KeWaitForSingleObject(&(DeviceExtension
->DeviceLock
), Executive
, KernelMode
, FALSE
, NULL
);
729 InsertHeadList(&(DeviceExtension
->UniqueIdWorkerItemListHead
), &(WorkItem
->UniqueIdWorkerItemListEntry
));
730 KeReleaseSemaphore(&(DeviceExtension
->DeviceLock
), IO_NO_INCREMENT
, 1, FALSE
);
732 /* And call the worker */
733 IssueUniqueIdChangeNotifyWorker(WorkItem
, UniqueId
);
745 IoFreeIrp(WorkItem
->Irp
);
748 if (WorkItem
->WorkItem
)
750 IoFreeWorkItem(WorkItem
->WorkItem
);