xref: /reactos/drivers/storage/mountmgr/notify.c (revision 8da0f868)
1 /*
2  *  ReactOS kernel
3  *  Copyright (C) 2011 ReactOS Team
4  *
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.
9  *
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.
14  *
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.
18  *
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)
25  */
26 
27 #include "mntmgr.h"
28 
29 #include <ioevent.h>
30 
31 #define NDEBUG
32 #include <debug.h>
33 
34 /*
35  * @implemented
36  */
37 VOID
38 SendOnlineNotification(IN PUNICODE_STRING SymbolicName)
39 {
40     PIRP Irp;
41     KEVENT Event;
42     NTSTATUS Status;
43     PFILE_OBJECT FileObject;
44     PIO_STACK_LOCATION Stack;
45     PDEVICE_OBJECT DeviceObject;
46     IO_STATUS_BLOCK IoStatusBlock;
47 
48     /* Get device object */
49     Status = IoGetDeviceObjectPointer(SymbolicName,
50                                       FILE_READ_ATTRIBUTES,
51                                       &FileObject,
52                                       &DeviceObject);
53     if (!NT_SUCCESS(Status))
54     {
55         return;
56     }
57 
58     /* And attached device object */
59     DeviceObject = IoGetAttachedDeviceReference(FileObject->DeviceObject);
60 
61     /* And send VOLUME_ONLINE */
62     KeInitializeEvent(&Event, NotificationEvent, FALSE);
63     Irp = IoBuildDeviceIoControlRequest(IOCTL_VOLUME_ONLINE,
64                                         DeviceObject,
65                                         NULL, 0,
66                                         NULL, 0,
67                                         FALSE,
68                                         &Event,
69                                         &IoStatusBlock);
70     if (!Irp)
71     {
72         goto Cleanup;
73     }
74 
75     Stack = IoGetNextIrpStackLocation(Irp);
76     Stack->FileObject = FileObject;
77 
78     Status = IoCallDriver(DeviceObject, Irp);
79     if (Status == STATUS_PENDING)
80     {
81         KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
82     }
83 
84 Cleanup:
85     ObDereferenceObject(DeviceObject);
86     ObDereferenceObject(FileObject);
87 
88     return;
89 }
90 
91 /*
92  * @implemented
93  */
94 VOID
95 NTAPI
96 SendOnlineNotificationWorker(IN PVOID Parameter)
97 {
98     KIRQL OldIrql;
99     PLIST_ENTRY Head;
100     PDEVICE_EXTENSION DeviceExtension;
101     PONLINE_NOTIFICATION_WORK_ITEM WorkItem;
102     PONLINE_NOTIFICATION_WORK_ITEM NewWorkItem;
103 
104     WorkItem = (PONLINE_NOTIFICATION_WORK_ITEM)Parameter;
105     DeviceExtension = WorkItem->DeviceExtension;
106 
107     /* First, send the notification */
108     SendOnlineNotification(&(WorkItem->SymbolicName));
109 
110     KeAcquireSpinLock(&(DeviceExtension->WorkerLock), &OldIrql);
111     /* If there are no notifications running any longer, reset event */
112     if (--DeviceExtension->OnlineNotificationCount == 0)
113     {
114         KeSetEvent(&(DeviceExtension->OnlineNotificationEvent), 0, FALSE);
115     }
116 
117     /* If there are still notifications in queue */
118     if (!IsListEmpty(&(DeviceExtension->OnlineNotificationListHead)))
119     {
120         /* Queue a new one for execution */
121         Head = RemoveHeadList(&(DeviceExtension->OnlineNotificationListHead));
122         NewWorkItem = CONTAINING_RECORD(Head, ONLINE_NOTIFICATION_WORK_ITEM, WorkItem.List);
123         KeReleaseSpinLock(&(DeviceExtension->WorkerLock), OldIrql);
124         NewWorkItem->WorkItem.List.Blink = NULL;
125         NewWorkItem->WorkItem.List.Flink = NULL;
126         ExQueueWorkItem(&NewWorkItem->WorkItem, DelayedWorkQueue);
127     }
128     else
129     {
130         /* Mark it's over */
131         DeviceExtension->OnlineNotificationWorkerActive = 0;
132         KeReleaseSpinLock(&(DeviceExtension->WorkerLock), OldIrql);
133     }
134 
135     FreePool(WorkItem->SymbolicName.Buffer);
136     FreePool(WorkItem);
137 
138     return;
139 }
140 
141 /*
142  * @implemented
143  */
144 VOID
145 PostOnlineNotification(IN PDEVICE_EXTENSION DeviceExtension,
146                        IN PUNICODE_STRING SymbolicName)
147 {
148     KIRQL OldIrql;
149     PONLINE_NOTIFICATION_WORK_ITEM WorkItem;
150 
151     /* Allocate a notification work item */
152     WorkItem = AllocatePool(sizeof(ONLINE_NOTIFICATION_WORK_ITEM));
153     if (!WorkItem)
154     {
155         return;
156     }
157 
158     ExInitializeWorkItem(&WorkItem->WorkItem, SendOnlineNotificationWorker, WorkItem);
159     WorkItem->DeviceExtension = DeviceExtension;
160     WorkItem->SymbolicName.Length = SymbolicName->Length;
161     WorkItem->SymbolicName.MaximumLength = SymbolicName->Length + sizeof(WCHAR);
162     WorkItem->SymbolicName.Buffer = AllocatePool(WorkItem->SymbolicName.MaximumLength);
163     if (!WorkItem->SymbolicName.Buffer)
164     {
165         FreePool(WorkItem);
166         return;
167     }
168 
169     RtlCopyMemory(WorkItem->SymbolicName.Buffer, SymbolicName->Buffer, SymbolicName->Length);
170     WorkItem->SymbolicName.Buffer[SymbolicName->Length / sizeof(WCHAR)] = UNICODE_NULL;
171 
172     KeAcquireSpinLock(&(DeviceExtension->WorkerLock), &OldIrql);
173     DeviceExtension->OnlineNotificationCount++;
174 
175     /* If no worker are active */
176     if (DeviceExtension->OnlineNotificationWorkerActive == 0)
177     {
178         /* Queue that one for execution */
179         DeviceExtension->OnlineNotificationWorkerActive = 1;
180         ExQueueWorkItem(&WorkItem->WorkItem, DelayedWorkQueue);
181     }
182     else
183     {
184         /* Otherwise, just put it in the queue list */
185         InsertTailList(&(DeviceExtension->OnlineNotificationListHead), &(WorkItem->WorkItem.List));
186     }
187 
188     KeReleaseSpinLock(&(DeviceExtension->WorkerLock), OldIrql);
189 
190     return;
191 }
192 
193 /*
194  * @implemented
195  */
196 VOID
197 WaitForOnlinesToComplete(IN PDEVICE_EXTENSION DeviceExtension)
198 {
199     KIRQL OldIrql;
200 
201     KeInitializeEvent(&(DeviceExtension->OnlineNotificationEvent), NotificationEvent, FALSE);
202 
203     KeAcquireSpinLock(&(DeviceExtension->WorkerLock), &OldIrql);
204 
205     /* Just wait all the worker are done */
206     if (DeviceExtension->OnlineNotificationCount != 1)
207     {
208         DeviceExtension->OnlineNotificationCount--;
209         KeReleaseSpinLock(&(DeviceExtension->WorkerLock), OldIrql);
210 
211         KeWaitForSingleObject(&(DeviceExtension->OnlineNotificationEvent),
212                               Executive,
213                               KernelMode,
214                               FALSE,
215                               NULL);
216 
217         KeAcquireSpinLock(&(DeviceExtension->WorkerLock), &OldIrql);
218         DeviceExtension->OnlineNotificationCount++;
219     }
220 
221     KeReleaseSpinLock(&(DeviceExtension->WorkerLock), OldIrql);
222 }
223 
224 /*
225  * @implemented
226  */
227 NTSTATUS
228 NTAPI
229 MountMgrTargetDeviceNotification(IN PVOID NotificationStructure,
230                                  IN PVOID Context)
231 {
232     PDEVICE_EXTENSION DeviceExtension;
233     PDEVICE_INFORMATION DeviceInformation;
234     PDEVICE_INTERFACE_CHANGE_NOTIFICATION Notification;
235 
236     DeviceInformation = Context;
237     DeviceExtension = DeviceInformation->DeviceExtension;
238     Notification = NotificationStructure;
239 
240     /* If it's to signal that removal is complete, then, execute the function */
241     if (IsEqualGUID(&(Notification->Event), &GUID_TARGET_DEVICE_REMOVE_COMPLETE))
242     {
243         MountMgrMountedDeviceRemoval(DeviceExtension, Notification->SymbolicLinkName);
244     }
245     /* It it's to signal that a volume has been mounted
246      * Verify if a database sync is required and execute it
247      */
248     else if (IsEqualGUID(&(Notification->Event), &GUID_IO_VOLUME_MOUNT))
249     {
250         /* If we were already mounted, then mark us unmounted */
251         if (InterlockedCompareExchange(&(DeviceInformation->MountState),
252                                        FALSE,
253                                        FALSE) == TRUE)
254         {
255             InterlockedDecrement(&(DeviceInformation->MountState));
256         }
257         /* Otherwise, start mounting the device and first, reconcile its DB if required */
258         else
259         {
260             if (DeviceInformation->NeedsReconcile)
261             {
262                 DeviceInformation->NeedsReconcile = FALSE;
263                 ReconcileThisDatabaseWithMaster(DeviceExtension, DeviceInformation);
264             }
265         }
266     }
267 
268     return STATUS_SUCCESS;
269 }
270 
271 /*
272  * @implemented
273  */
274 VOID
275 RegisterForTargetDeviceNotification(IN PDEVICE_EXTENSION DeviceExtension,
276                                     IN PDEVICE_INFORMATION DeviceInformation)
277 {
278     NTSTATUS Status;
279     PFILE_OBJECT FileObject;
280     PDEVICE_OBJECT DeviceObject;
281 
282     /* Get device object */
283     Status = IoGetDeviceObjectPointer(&(DeviceInformation->DeviceName),
284                                       FILE_READ_ATTRIBUTES,
285                                       &FileObject,
286                                       &DeviceObject);
287     if (!NT_SUCCESS(Status))
288     {
289         return;
290     }
291 
292     /* And simply register for notifications */
293     Status = IoRegisterPlugPlayNotification(EventCategoryTargetDeviceChange,
294                                             0, FileObject,
295                                             DeviceExtension->DriverObject,
296                                             MountMgrTargetDeviceNotification,
297                                             DeviceInformation,
298                                             &(DeviceInformation->TargetDeviceNotificationEntry));
299     if (!NT_SUCCESS(Status))
300     {
301         DeviceInformation->TargetDeviceNotificationEntry = NULL;
302     }
303 
304     ObDereferenceObject(FileObject);
305 
306     return;
307 }
308 
309 /*
310  * @implemented
311  */
312 VOID
313 MountMgrNotify(IN PDEVICE_EXTENSION DeviceExtension)
314 {
315     PIRP Irp;
316     KIRQL OldIrql;
317     LIST_ENTRY CopyList;
318     PLIST_ENTRY NextEntry;
319 
320     /* Increase the epic number */
321     DeviceExtension->EpicNumber++;
322 
323     InitializeListHead(&CopyList);
324 
325     /* Copy all the pending IRPs for notification */
326     IoAcquireCancelSpinLock(&OldIrql);
327     while (!IsListEmpty(&(DeviceExtension->IrpListHead)))
328     {
329         NextEntry = RemoveHeadList(&(DeviceExtension->IrpListHead));
330         Irp = CONTAINING_RECORD(NextEntry, IRP, Tail.Overlay.ListEntry);
331         IoSetCancelRoutine(Irp, NULL);
332         InsertTailList(&CopyList, &(Irp->Tail.Overlay.ListEntry));
333     }
334     IoReleaseCancelSpinLock(OldIrql);
335 
336     /* Then, notify them one by one */
337     while (!IsListEmpty(&CopyList))
338     {
339         NextEntry = RemoveHeadList(&CopyList);
340         Irp = CONTAINING_RECORD(NextEntry, IRP, Tail.Overlay.ListEntry);
341 
342         *((PULONG)Irp->AssociatedIrp.SystemBuffer) = DeviceExtension->EpicNumber;
343         Irp->IoStatus.Information = sizeof(DeviceExtension->EpicNumber);
344 
345         IoCompleteRequest(Irp, IO_NO_INCREMENT);
346     }
347 }
348 
349 /*
350  * @implemented
351  */
352 VOID
353 MountMgrNotifyNameChange(IN PDEVICE_EXTENSION DeviceExtension,
354                          IN PUNICODE_STRING DeviceName,
355                          IN BOOLEAN ValidateVolume)
356 {
357     PIRP Irp;
358     KEVENT Event;
359     NTSTATUS Status;
360     PLIST_ENTRY NextEntry;
361     PFILE_OBJECT FileObject;
362     PIO_STACK_LOCATION Stack;
363     PDEVICE_OBJECT DeviceObject;
364     IO_STATUS_BLOCK IoStatusBlock;
365     PDEVICE_RELATIONS DeviceRelations;
366     PDEVICE_INFORMATION DeviceInformation;
367     TARGET_DEVICE_CUSTOM_NOTIFICATION DeviceNotification;
368 
369     /* If we have to validate volume */
370     if (ValidateVolume)
371     {
372         /* Then, ensure we can find the device */
373         for (NextEntry = DeviceExtension->DeviceListHead.Flink;
374              NextEntry != &DeviceExtension->DeviceListHead;
375              NextEntry = NextEntry->Flink)
376         {
377             DeviceInformation = CONTAINING_RECORD(NextEntry, DEVICE_INFORMATION, DeviceListEntry);
378             if (RtlCompareUnicodeString(DeviceName, &(DeviceInformation->DeviceName), TRUE) == 0)
379             {
380                 break;
381             }
382         }
383 
384         /* No need to notify for a PnP device or if we didn't find the device */
385         if (NextEntry == &(DeviceExtension->DeviceListHead) ||
386             !DeviceInformation->ManuallyRegistered)
387         {
388             return;
389         }
390     }
391 
392     /* Then, get device object */
393     Status = IoGetDeviceObjectPointer(DeviceName,
394                                       FILE_READ_ATTRIBUTES,
395                                       &FileObject,
396                                       &DeviceObject);
397     if (!NT_SUCCESS(Status))
398     {
399         return;
400     }
401 
402     DeviceObject = IoGetAttachedDeviceReference(FileObject->DeviceObject);
403 
404     KeInitializeEvent(&Event, NotificationEvent, FALSE);
405 
406     /* Set up empty IRP (yes, yes!) */
407     Irp = IoBuildDeviceIoControlRequest(0,
408                                         DeviceObject,
409                                         NULL,
410                                         0,
411                                         NULL,
412                                         0,
413                                         FALSE,
414                                         &Event,
415                                         &IoStatusBlock);
416     if (!Irp)
417     {
418         ObDereferenceObject(DeviceObject);
419         ObDereferenceObject(FileObject);
420         return;
421     }
422 
423     Stack = IoGetNextIrpStackLocation(Irp);
424 
425     Irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
426     Irp->IoStatus.Information = 0;
427 
428     /* Properly set it, we want to query device relations */
429     Stack->MajorFunction = IRP_MJ_PNP;
430     Stack->MinorFunction = IRP_MN_QUERY_DEVICE_RELATIONS;
431     Stack->Parameters.QueryDeviceRelations.Type = TargetDeviceRelation;
432     Stack->FileObject = FileObject;
433 
434     /* And call driver */
435     Status = IoCallDriver(DeviceObject, Irp);
436     if (Status == STATUS_PENDING)
437     {
438         KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
439         Status = IoStatusBlock.Status;
440     }
441 
442     ObDereferenceObject(DeviceObject);
443     ObDereferenceObject(FileObject);
444 
445     if (!NT_SUCCESS(Status))
446     {
447         return;
448     }
449 
450     /* Validate device return */
451     DeviceRelations = (PDEVICE_RELATIONS)IoStatusBlock.Information;
452     if (DeviceRelations->Count < 1)
453     {
454         ExFreePool(DeviceRelations);
455         return;
456     }
457 
458     DeviceObject = DeviceRelations->Objects[0];
459     ExFreePool(DeviceRelations);
460 
461     /* Set up real notification */
462     DeviceNotification.Version = 1;
463     DeviceNotification.Size = sizeof(TARGET_DEVICE_CUSTOM_NOTIFICATION);
464     DeviceNotification.Event = GUID_IO_VOLUME_NAME_CHANGE;
465     DeviceNotification.FileObject = NULL;
466     DeviceNotification.NameBufferOffset = -1;
467 
468     /* And report */
469     IoReportTargetDeviceChangeAsynchronous(DeviceObject,
470                                            &DeviceNotification,
471                                            NULL, NULL);
472 
473     ObDereferenceObject(DeviceObject);
474 
475     return;
476 }
477 
478 /*
479  * @implemented
480  */
481 VOID
482 RemoveWorkItem(IN PUNIQUE_ID_WORK_ITEM WorkItem)
483 {
484     PDEVICE_EXTENSION DeviceExtension = WorkItem->DeviceExtension;
485 
486     KeWaitForSingleObject(&(DeviceExtension->DeviceLock), Executive, KernelMode, FALSE, NULL);
487 
488     /* If even if being worked, it's too late */
489     if (WorkItem->Event)
490     {
491         KeReleaseSemaphore(&(DeviceExtension->DeviceLock), IO_NO_INCREMENT, 1, FALSE);
492         KeSetEvent(WorkItem->Event, 0, FALSE);
493     }
494     else
495     {
496         /* Otherwise, remove it from the list, and delete it */
497         RemoveEntryList(&(WorkItem->UniqueIdWorkerItemListEntry));
498         KeReleaseSemaphore(&(DeviceExtension->DeviceLock), IO_NO_INCREMENT, 1, FALSE);
499         IoFreeIrp(WorkItem->Irp);
500         FreePool(WorkItem->DeviceName.Buffer);
501         FreePool(WorkItem->IrpBuffer);
502         FreePool(WorkItem);
503     }
504 }
505 
506 /*
507  * @implemented
508  */
509 VOID
510 NTAPI
511 UniqueIdChangeNotifyWorker(IN PDEVICE_OBJECT DeviceObject,
512                            IN PVOID Context)
513 {
514     PUNIQUE_ID_WORK_ITEM WorkItem = Context;
515     PMOUNTDEV_UNIQUE_ID OldUniqueId, NewUniqueId;
516     PMOUNTDEV_UNIQUE_ID_CHANGE_NOTIFY_OUTPUT UniqueIdChange;
517 
518     UNREFERENCED_PARAMETER(DeviceObject);
519 
520     /* Validate worker */
521     if (!NT_SUCCESS(WorkItem->Irp->IoStatus.Status))
522     {
523         RemoveWorkItem(WorkItem);
524         return;
525     }
526 
527     UniqueIdChange = WorkItem->Irp->AssociatedIrp.SystemBuffer;
528     /* Get the old unique ID */
529     OldUniqueId = AllocatePool(UniqueIdChange->OldUniqueIdLength + sizeof(MOUNTDEV_UNIQUE_ID));
530     if (!OldUniqueId)
531     {
532         RemoveWorkItem(WorkItem);
533         return;
534     }
535 
536     OldUniqueId->UniqueIdLength = UniqueIdChange->OldUniqueIdLength;
537     RtlCopyMemory(OldUniqueId->UniqueId,
538                   (PVOID)((ULONG_PTR)UniqueIdChange + UniqueIdChange->OldUniqueIdOffset),
539                   UniqueIdChange->OldUniqueIdLength);
540 
541     /* Get the new unique ID */
542     NewUniqueId = AllocatePool(UniqueIdChange->NewUniqueIdLength + sizeof(MOUNTDEV_UNIQUE_ID));
543     if (!NewUniqueId)
544     {
545         FreePool(OldUniqueId);
546         RemoveWorkItem(WorkItem);
547         return;
548     }
549 
550     NewUniqueId->UniqueIdLength = UniqueIdChange->NewUniqueIdLength;
551     RtlCopyMemory(NewUniqueId->UniqueId,
552                   (PVOID)((ULONG_PTR)UniqueIdChange + UniqueIdChange->NewUniqueIdOffset),
553                   UniqueIdChange->NewUniqueIdLength);
554 
555     /* Call the real worker */
556     MountMgrUniqueIdChangeRoutine(WorkItem->DeviceExtension, OldUniqueId, NewUniqueId);
557     IssueUniqueIdChangeNotifyWorker(WorkItem, NewUniqueId);
558 
559     FreePool(NewUniqueId);
560     FreePool(OldUniqueId);
561 
562     return;
563 }
564 
565 /*
566  * @implemented
567  */
568 NTSTATUS
569 NTAPI
570 UniqueIdChangeNotifyCompletion(IN PDEVICE_OBJECT DeviceObject,
571                                IN PIRP Irp,
572                                IN PVOID Context)
573 {
574     PUNIQUE_ID_WORK_ITEM WorkItem = Context;
575 
576     UNREFERENCED_PARAMETER(DeviceObject);
577     UNREFERENCED_PARAMETER(Irp);
578 
579     /* Simply queue the work item */
580     IoQueueWorkItem(WorkItem->WorkItem,
581                     UniqueIdChangeNotifyWorker,
582                     DelayedWorkQueue,
583                     WorkItem);
584 
585     return STATUS_MORE_PROCESSING_REQUIRED;
586 }
587 
588 /*
589  * @implemented
590  */
591 VOID
592 IssueUniqueIdChangeNotifyWorker(IN PUNIQUE_ID_WORK_ITEM WorkItem,
593                                 IN PMOUNTDEV_UNIQUE_ID UniqueId)
594 {
595     PIRP Irp;
596     NTSTATUS Status;
597     PFILE_OBJECT FileObject;
598     PIO_STACK_LOCATION Stack;
599     PDEVICE_OBJECT DeviceObject;
600 
601     /* Get the device object */
602     Status = IoGetDeviceObjectPointer(&(WorkItem->DeviceName),
603                                       FILE_READ_ATTRIBUTES,
604                                       &FileObject,
605                                       &DeviceObject);
606     if (!NT_SUCCESS(Status))
607     {
608         RemoveWorkItem(WorkItem);
609         return;
610     }
611 
612     /* And then, the attached device */
613     DeviceObject = IoGetAttachedDeviceReference(FileObject->DeviceObject);
614 
615     /* Initialize the IRP */
616     Irp = WorkItem->Irp;
617     IoInitializeIrp(Irp, IoSizeOfIrp(WorkItem->StackSize), (CCHAR)WorkItem->StackSize);
618 
619     if (InterlockedExchange((PLONG)&(WorkItem->Event), 0) != 0)
620     {
621         ObDereferenceObject(FileObject);
622         ObDereferenceObject(DeviceObject);
623         RemoveWorkItem(WorkItem);
624         return;
625     }
626 
627     Irp->AssociatedIrp.SystemBuffer = WorkItem->IrpBuffer;
628     Irp->Tail.Overlay.Thread = PsGetCurrentThread();
629     RtlCopyMemory(Irp->AssociatedIrp.SystemBuffer, UniqueId, UniqueId->UniqueIdLength + sizeof(USHORT));
630 
631     Stack = IoGetNextIrpStackLocation(Irp);
632 
633     Stack->Parameters.DeviceIoControl.InputBufferLength = UniqueId->UniqueIdLength + sizeof(USHORT);
634     Stack->Parameters.DeviceIoControl.OutputBufferLength = WorkItem->IrpBufferLength;
635     Stack->Parameters.DeviceIoControl.Type3InputBuffer = 0;
636     Stack->Parameters.DeviceIoControl.IoControlCode = IOCTL_MOUNTDEV_UNIQUE_ID_CHANGE_NOTIFY;
637     Stack->MajorFunction = IRP_MJ_DEVICE_CONTROL;
638 
639     Status = IoSetCompletionRoutineEx(WorkItem->DeviceExtension->DeviceObject,
640                                       Irp,
641                                       UniqueIdChangeNotifyCompletion,
642                                       WorkItem,
643                                       TRUE, TRUE, TRUE);
644     if (!NT_SUCCESS(Status))
645     {
646         ObDereferenceObject(FileObject);
647         ObDereferenceObject(DeviceObject);
648         RemoveWorkItem(WorkItem);
649         return;
650     }
651 
652     /* Call the driver */
653     IoCallDriver(DeviceObject, Irp);
654     ObDereferenceObject(FileObject);
655     ObDereferenceObject(DeviceObject);
656 }
657 
658 /*
659  * @implemented
660  */
661 VOID
662 IssueUniqueIdChangeNotify(IN PDEVICE_EXTENSION DeviceExtension,
663                           IN PUNICODE_STRING DeviceName,
664                           IN PMOUNTDEV_UNIQUE_ID UniqueId)
665 {
666     NTSTATUS Status;
667     PVOID IrpBuffer = NULL;
668     PFILE_OBJECT FileObject;
669     PDEVICE_OBJECT DeviceObject;
670     PUNIQUE_ID_WORK_ITEM WorkItem = NULL;
671 
672     /* Get the associated device object */
673     Status = IoGetDeviceObjectPointer(DeviceName,
674                                       FILE_READ_ATTRIBUTES,
675                                       &FileObject,
676                                       &DeviceObject);
677     if (!NT_SUCCESS(Status))
678     {
679         return;
680     }
681 
682     /* And then, get attached device */
683     DeviceObject = IoGetAttachedDeviceReference(FileObject->DeviceObject);
684 
685     ObDereferenceObject(FileObject);
686 
687     /* Allocate a work item */
688     WorkItem = AllocatePool(sizeof(UNIQUE_ID_WORK_ITEM));
689     if (!WorkItem)
690     {
691         ObDereferenceObject(DeviceObject);
692         return;
693     }
694 
695     WorkItem->Event = NULL;
696     WorkItem->WorkItem = IoAllocateWorkItem(DeviceExtension->DeviceObject);
697     if (!WorkItem->WorkItem)
698     {
699         ObDereferenceObject(DeviceObject);
700         goto Cleanup;
701     }
702 
703     WorkItem->DeviceExtension = DeviceExtension;
704     WorkItem->StackSize = DeviceObject->StackSize;
705     /* Already provide the IRP */
706     WorkItem->Irp = IoAllocateIrp(DeviceObject->StackSize, FALSE);
707 
708     ObDereferenceObject(DeviceObject);
709 
710     if (!WorkItem->Irp)
711     {
712         goto Cleanup;
713     }
714 
715     /* Ensure it has enough space */
716     IrpBuffer = AllocatePool(sizeof(MOUNTDEV_UNIQUE_ID_CHANGE_NOTIFY_OUTPUT) + 1024);
717     if (!IrpBuffer)
718     {
719         goto Cleanup;
720     }
721 
722     WorkItem->DeviceName.Length = DeviceName->Length;
723     WorkItem->DeviceName.MaximumLength = DeviceName->Length + sizeof(WCHAR);
724     WorkItem->DeviceName.Buffer = AllocatePool(WorkItem->DeviceName.MaximumLength);
725     if (!WorkItem->DeviceName.Buffer)
726     {
727         goto Cleanup;
728     }
729 
730     RtlCopyMemory(WorkItem->DeviceName.Buffer, DeviceName->Buffer, DeviceName->Length);
731     WorkItem->DeviceName.Buffer[DeviceName->Length / sizeof(WCHAR)] = UNICODE_NULL;
732 
733     WorkItem->IrpBuffer = IrpBuffer;
734     WorkItem->IrpBufferLength = sizeof(MOUNTDEV_UNIQUE_ID_CHANGE_NOTIFY_OUTPUT) + 1024;
735 
736     /* Add the worker in the list */
737     KeWaitForSingleObject(&(DeviceExtension->DeviceLock), Executive, KernelMode, FALSE, NULL);
738     InsertHeadList(&(DeviceExtension->UniqueIdWorkerItemListHead), &(WorkItem->UniqueIdWorkerItemListEntry));
739     KeReleaseSemaphore(&(DeviceExtension->DeviceLock), IO_NO_INCREMENT, 1, FALSE);
740 
741     /* And call the worker */
742     IssueUniqueIdChangeNotifyWorker(WorkItem, UniqueId);
743 
744     return;
745 
746 Cleanup:
747     if (IrpBuffer)
748     {
749         FreePool(IrpBuffer);
750     }
751 
752     if (WorkItem->Irp)
753     {
754         IoFreeIrp(WorkItem->Irp);
755     }
756 
757     if (WorkItem->WorkItem)
758     {
759         IoFreeWorkItem(WorkItem->WorkItem);
760     }
761 
762     if (WorkItem)
763     {
764         FreePool(WorkItem);
765     }
766 }
767