xref: /reactos/drivers/storage/mountmgr/notify.c (revision 53221834)
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     PTARGET_DEVICE_CUSTOM_NOTIFICATION Notification;
235 
236     DeviceInformation = Context;
237     DeviceExtension = DeviceInformation->DeviceExtension;
238     Notification = NotificationStructure;
239 
240     /* The notification have to be unregistered already (in device interface change handler) */
241     ASSERT(!IsEqualGUID(&Notification->Event, &GUID_TARGET_DEVICE_REMOVE_COMPLETE));
242 
243     /* It it's to signal that a volume has been mounted
244      * Verify if a database sync is required and execute it
245      */
246     if (IsEqualGUID(&(Notification->Event), &GUID_IO_VOLUME_MOUNT))
247     {
248         /* If we were already mounted, then mark us unmounted */
249         if (InterlockedCompareExchange(&(DeviceInformation->MountState),
250                                        FALSE,
251                                        FALSE) == TRUE)
252         {
253             InterlockedDecrement(&(DeviceInformation->MountState));
254         }
255         /* Otherwise, start mounting the device and first, reconcile its DB if required */
256         else
257         {
258             if (DeviceInformation->NeedsReconcile)
259             {
260                 DeviceInformation->NeedsReconcile = FALSE;
261                 ReconcileThisDatabaseWithMaster(DeviceExtension, DeviceInformation);
262             }
263         }
264     }
265 
266     return STATUS_SUCCESS;
267 }
268 
269 /*
270  * @implemented
271  */
272 VOID
273 RegisterForTargetDeviceNotification(IN PDEVICE_EXTENSION DeviceExtension,
274                                     IN PDEVICE_INFORMATION DeviceInformation)
275 {
276     NTSTATUS Status;
277     PFILE_OBJECT FileObject;
278     PDEVICE_OBJECT DeviceObject;
279 
280     /* Get device object */
281     Status = IoGetDeviceObjectPointer(&(DeviceInformation->DeviceName),
282                                       FILE_READ_ATTRIBUTES,
283                                       &FileObject,
284                                       &DeviceObject);
285     if (!NT_SUCCESS(Status))
286     {
287         return;
288     }
289 
290     /* And simply register for notifications */
291     Status = IoRegisterPlugPlayNotification(EventCategoryTargetDeviceChange,
292                                             0, FileObject,
293                                             DeviceExtension->DriverObject,
294                                             MountMgrTargetDeviceNotification,
295                                             DeviceInformation,
296                                             &(DeviceInformation->TargetDeviceNotificationEntry));
297     if (!NT_SUCCESS(Status))
298     {
299         DeviceInformation->TargetDeviceNotificationEntry = NULL;
300     }
301 
302     ObDereferenceObject(FileObject);
303 
304     return;
305 }
306 
307 /*
308  * @implemented
309  */
310 VOID
311 MountMgrNotify(IN PDEVICE_EXTENSION DeviceExtension)
312 {
313     PIRP Irp;
314     KIRQL OldIrql;
315     LIST_ENTRY CopyList;
316     PLIST_ENTRY NextEntry;
317 
318     /* Increase the epic number */
319     DeviceExtension->EpicNumber++;
320 
321     InitializeListHead(&CopyList);
322 
323     /* Copy all the pending IRPs for notification */
324     IoAcquireCancelSpinLock(&OldIrql);
325     while (!IsListEmpty(&(DeviceExtension->IrpListHead)))
326     {
327         NextEntry = RemoveHeadList(&(DeviceExtension->IrpListHead));
328         Irp = CONTAINING_RECORD(NextEntry, IRP, Tail.Overlay.ListEntry);
329         IoSetCancelRoutine(Irp, NULL);
330         InsertTailList(&CopyList, &(Irp->Tail.Overlay.ListEntry));
331     }
332     IoReleaseCancelSpinLock(OldIrql);
333 
334     /* Then, notify them one by one */
335     while (!IsListEmpty(&CopyList))
336     {
337         NextEntry = RemoveHeadList(&CopyList);
338         Irp = CONTAINING_RECORD(NextEntry, IRP, Tail.Overlay.ListEntry);
339 
340         *((PULONG)Irp->AssociatedIrp.SystemBuffer) = DeviceExtension->EpicNumber;
341         Irp->IoStatus.Information = sizeof(DeviceExtension->EpicNumber);
342 
343         IoCompleteRequest(Irp, IO_NO_INCREMENT);
344     }
345 }
346 
347 /*
348  * @implemented
349  */
350 VOID
351 MountMgrNotifyNameChange(IN PDEVICE_EXTENSION DeviceExtension,
352                          IN PUNICODE_STRING DeviceName,
353                          IN BOOLEAN ValidateVolume)
354 {
355     PIRP Irp;
356     KEVENT Event;
357     NTSTATUS Status;
358     PLIST_ENTRY NextEntry;
359     PFILE_OBJECT FileObject;
360     PIO_STACK_LOCATION Stack;
361     PDEVICE_OBJECT DeviceObject;
362     IO_STATUS_BLOCK IoStatusBlock;
363     PDEVICE_RELATIONS DeviceRelations;
364     PDEVICE_INFORMATION DeviceInformation;
365     TARGET_DEVICE_CUSTOM_NOTIFICATION DeviceNotification;
366 
367     /* If we have to validate volume */
368     if (ValidateVolume)
369     {
370         /* Then, ensure we can find the device */
371         for (NextEntry = DeviceExtension->DeviceListHead.Flink;
372              NextEntry != &DeviceExtension->DeviceListHead;
373              NextEntry = NextEntry->Flink)
374         {
375             DeviceInformation = CONTAINING_RECORD(NextEntry, DEVICE_INFORMATION, DeviceListEntry);
376             if (RtlCompareUnicodeString(DeviceName, &(DeviceInformation->DeviceName), TRUE) == 0)
377             {
378                 break;
379             }
380         }
381 
382         /* No need to notify for a PnP device or if we didn't find the device */
383         if (NextEntry == &(DeviceExtension->DeviceListHead) ||
384             !DeviceInformation->ManuallyRegistered)
385         {
386             return;
387         }
388     }
389 
390     /* Then, get device object */
391     Status = IoGetDeviceObjectPointer(DeviceName,
392                                       FILE_READ_ATTRIBUTES,
393                                       &FileObject,
394                                       &DeviceObject);
395     if (!NT_SUCCESS(Status))
396     {
397         return;
398     }
399 
400     DeviceObject = IoGetAttachedDeviceReference(FileObject->DeviceObject);
401 
402     KeInitializeEvent(&Event, NotificationEvent, FALSE);
403 
404     /* Set up empty IRP (yes, yes!) */
405     Irp = IoBuildDeviceIoControlRequest(0,
406                                         DeviceObject,
407                                         NULL,
408                                         0,
409                                         NULL,
410                                         0,
411                                         FALSE,
412                                         &Event,
413                                         &IoStatusBlock);
414     if (!Irp)
415     {
416         ObDereferenceObject(DeviceObject);
417         ObDereferenceObject(FileObject);
418         return;
419     }
420 
421     Stack = IoGetNextIrpStackLocation(Irp);
422 
423     Irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
424     Irp->IoStatus.Information = 0;
425 
426     /* Properly set it, we want to query device relations */
427     Stack->MajorFunction = IRP_MJ_PNP;
428     Stack->MinorFunction = IRP_MN_QUERY_DEVICE_RELATIONS;
429     Stack->Parameters.QueryDeviceRelations.Type = TargetDeviceRelation;
430     Stack->FileObject = FileObject;
431 
432     /* And call driver */
433     Status = IoCallDriver(DeviceObject, Irp);
434     if (Status == STATUS_PENDING)
435     {
436         KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
437         Status = IoStatusBlock.Status;
438     }
439 
440     ObDereferenceObject(DeviceObject);
441     ObDereferenceObject(FileObject);
442 
443     if (!NT_SUCCESS(Status))
444     {
445         return;
446     }
447 
448     /* Validate device return */
449     DeviceRelations = (PDEVICE_RELATIONS)IoStatusBlock.Information;
450     if (DeviceRelations->Count < 1)
451     {
452         ExFreePool(DeviceRelations);
453         return;
454     }
455 
456     DeviceObject = DeviceRelations->Objects[0];
457     ExFreePool(DeviceRelations);
458 
459     /* Set up real notification */
460     DeviceNotification.Version = 1;
461     DeviceNotification.Size = sizeof(TARGET_DEVICE_CUSTOM_NOTIFICATION);
462     DeviceNotification.Event = GUID_IO_VOLUME_NAME_CHANGE;
463     DeviceNotification.FileObject = NULL;
464     DeviceNotification.NameBufferOffset = -1;
465 
466     /* And report */
467     IoReportTargetDeviceChangeAsynchronous(DeviceObject,
468                                            &DeviceNotification,
469                                            NULL, NULL);
470 
471     ObDereferenceObject(DeviceObject);
472 
473     return;
474 }
475 
476 /*
477  * @implemented
478  */
479 VOID
480 RemoveWorkItem(IN PUNIQUE_ID_WORK_ITEM WorkItem)
481 {
482     PDEVICE_EXTENSION DeviceExtension = WorkItem->DeviceExtension;
483 
484     KeWaitForSingleObject(&(DeviceExtension->DeviceLock), Executive, KernelMode, FALSE, NULL);
485 
486     /* If even if being worked, it's too late */
487     if (WorkItem->Event)
488     {
489         KeReleaseSemaphore(&(DeviceExtension->DeviceLock), IO_NO_INCREMENT, 1, FALSE);
490         KeSetEvent(WorkItem->Event, 0, FALSE);
491     }
492     else
493     {
494         /* Otherwise, remove it from the list, and delete it */
495         RemoveEntryList(&(WorkItem->UniqueIdWorkerItemListEntry));
496         KeReleaseSemaphore(&(DeviceExtension->DeviceLock), IO_NO_INCREMENT, 1, FALSE);
497         IoFreeIrp(WorkItem->Irp);
498         FreePool(WorkItem->DeviceName.Buffer);
499         FreePool(WorkItem->IrpBuffer);
500         FreePool(WorkItem);
501     }
502 }
503 
504 /*
505  * @implemented
506  */
507 VOID
508 NTAPI
509 UniqueIdChangeNotifyWorker(IN PDEVICE_OBJECT DeviceObject,
510                            IN PVOID Context)
511 {
512     PUNIQUE_ID_WORK_ITEM WorkItem = Context;
513     PMOUNTDEV_UNIQUE_ID OldUniqueId, NewUniqueId;
514     PMOUNTDEV_UNIQUE_ID_CHANGE_NOTIFY_OUTPUT UniqueIdChange;
515 
516     UNREFERENCED_PARAMETER(DeviceObject);
517 
518     /* Validate worker */
519     if (!NT_SUCCESS(WorkItem->Irp->IoStatus.Status))
520     {
521         RemoveWorkItem(WorkItem);
522         return;
523     }
524 
525     UniqueIdChange = WorkItem->Irp->AssociatedIrp.SystemBuffer;
526     /* Get the old unique ID */
527     OldUniqueId = AllocatePool(UniqueIdChange->OldUniqueIdLength + sizeof(MOUNTDEV_UNIQUE_ID));
528     if (!OldUniqueId)
529     {
530         RemoveWorkItem(WorkItem);
531         return;
532     }
533 
534     OldUniqueId->UniqueIdLength = UniqueIdChange->OldUniqueIdLength;
535     RtlCopyMemory(OldUniqueId->UniqueId,
536                   (PVOID)((ULONG_PTR)UniqueIdChange + UniqueIdChange->OldUniqueIdOffset),
537                   UniqueIdChange->OldUniqueIdLength);
538 
539     /* Get the new unique ID */
540     NewUniqueId = AllocatePool(UniqueIdChange->NewUniqueIdLength + sizeof(MOUNTDEV_UNIQUE_ID));
541     if (!NewUniqueId)
542     {
543         FreePool(OldUniqueId);
544         RemoveWorkItem(WorkItem);
545         return;
546     }
547 
548     NewUniqueId->UniqueIdLength = UniqueIdChange->NewUniqueIdLength;
549     RtlCopyMemory(NewUniqueId->UniqueId,
550                   (PVOID)((ULONG_PTR)UniqueIdChange + UniqueIdChange->NewUniqueIdOffset),
551                   UniqueIdChange->NewUniqueIdLength);
552 
553     /* Call the real worker */
554     MountMgrUniqueIdChangeRoutine(WorkItem->DeviceExtension, OldUniqueId, NewUniqueId);
555     IssueUniqueIdChangeNotifyWorker(WorkItem, NewUniqueId);
556 
557     FreePool(NewUniqueId);
558     FreePool(OldUniqueId);
559 
560     return;
561 }
562 
563 /*
564  * @implemented
565  */
566 NTSTATUS
567 NTAPI
568 UniqueIdChangeNotifyCompletion(IN PDEVICE_OBJECT DeviceObject,
569                                IN PIRP Irp,
570                                IN PVOID Context)
571 {
572     PUNIQUE_ID_WORK_ITEM WorkItem = Context;
573 
574     UNREFERENCED_PARAMETER(DeviceObject);
575     UNREFERENCED_PARAMETER(Irp);
576 
577     /* Simply queue the work item */
578     IoQueueWorkItem(WorkItem->WorkItem,
579                     UniqueIdChangeNotifyWorker,
580                     DelayedWorkQueue,
581                     WorkItem);
582 
583     return STATUS_MORE_PROCESSING_REQUIRED;
584 }
585 
586 /*
587  * @implemented
588  */
589 VOID
590 IssueUniqueIdChangeNotifyWorker(IN PUNIQUE_ID_WORK_ITEM WorkItem,
591                                 IN PMOUNTDEV_UNIQUE_ID UniqueId)
592 {
593     PIRP Irp;
594     NTSTATUS Status;
595     PFILE_OBJECT FileObject;
596     PIO_STACK_LOCATION Stack;
597     PDEVICE_OBJECT DeviceObject;
598 
599     /* Get the device object */
600     Status = IoGetDeviceObjectPointer(&(WorkItem->DeviceName),
601                                       FILE_READ_ATTRIBUTES,
602                                       &FileObject,
603                                       &DeviceObject);
604     if (!NT_SUCCESS(Status))
605     {
606         RemoveWorkItem(WorkItem);
607         return;
608     }
609 
610     /* And then, the attached device */
611     DeviceObject = IoGetAttachedDeviceReference(FileObject->DeviceObject);
612 
613     /* Initialize the IRP */
614     Irp = WorkItem->Irp;
615     IoInitializeIrp(Irp, IoSizeOfIrp(WorkItem->StackSize), (CCHAR)WorkItem->StackSize);
616 
617     if (InterlockedExchange((PLONG)&(WorkItem->Event), 0) != 0)
618     {
619         ObDereferenceObject(FileObject);
620         ObDereferenceObject(DeviceObject);
621         RemoveWorkItem(WorkItem);
622         return;
623     }
624 
625     Irp->AssociatedIrp.SystemBuffer = WorkItem->IrpBuffer;
626     Irp->Tail.Overlay.Thread = PsGetCurrentThread();
627     RtlCopyMemory(Irp->AssociatedIrp.SystemBuffer, UniqueId, UniqueId->UniqueIdLength + sizeof(USHORT));
628 
629     Stack = IoGetNextIrpStackLocation(Irp);
630 
631     Stack->Parameters.DeviceIoControl.InputBufferLength = UniqueId->UniqueIdLength + sizeof(USHORT);
632     Stack->Parameters.DeviceIoControl.OutputBufferLength = WorkItem->IrpBufferLength;
633     Stack->Parameters.DeviceIoControl.Type3InputBuffer = 0;
634     Stack->Parameters.DeviceIoControl.IoControlCode = IOCTL_MOUNTDEV_UNIQUE_ID_CHANGE_NOTIFY;
635     Stack->MajorFunction = IRP_MJ_DEVICE_CONTROL;
636 
637     Status = IoSetCompletionRoutineEx(WorkItem->DeviceExtension->DeviceObject,
638                                       Irp,
639                                       UniqueIdChangeNotifyCompletion,
640                                       WorkItem,
641                                       TRUE, TRUE, TRUE);
642     if (!NT_SUCCESS(Status))
643     {
644         ObDereferenceObject(FileObject);
645         ObDereferenceObject(DeviceObject);
646         RemoveWorkItem(WorkItem);
647         return;
648     }
649 
650     /* Call the driver */
651     IoCallDriver(DeviceObject, Irp);
652     ObDereferenceObject(FileObject);
653     ObDereferenceObject(DeviceObject);
654 }
655 
656 /*
657  * @implemented
658  */
659 VOID
660 IssueUniqueIdChangeNotify(IN PDEVICE_EXTENSION DeviceExtension,
661                           IN PUNICODE_STRING DeviceName,
662                           IN PMOUNTDEV_UNIQUE_ID UniqueId)
663 {
664     NTSTATUS Status;
665     PVOID IrpBuffer = NULL;
666     PFILE_OBJECT FileObject;
667     PDEVICE_OBJECT DeviceObject;
668     PUNIQUE_ID_WORK_ITEM WorkItem = NULL;
669 
670     /* Get the associated device object */
671     Status = IoGetDeviceObjectPointer(DeviceName,
672                                       FILE_READ_ATTRIBUTES,
673                                       &FileObject,
674                                       &DeviceObject);
675     if (!NT_SUCCESS(Status))
676     {
677         return;
678     }
679 
680     /* And then, get attached device */
681     DeviceObject = IoGetAttachedDeviceReference(FileObject->DeviceObject);
682 
683     ObDereferenceObject(FileObject);
684 
685     /* Allocate a work item */
686     WorkItem = AllocatePool(sizeof(UNIQUE_ID_WORK_ITEM));
687     if (!WorkItem)
688     {
689         ObDereferenceObject(DeviceObject);
690         return;
691     }
692 
693     WorkItem->Event = NULL;
694     WorkItem->WorkItem = IoAllocateWorkItem(DeviceExtension->DeviceObject);
695     if (!WorkItem->WorkItem)
696     {
697         ObDereferenceObject(DeviceObject);
698         goto Cleanup;
699     }
700 
701     WorkItem->DeviceExtension = DeviceExtension;
702     WorkItem->StackSize = DeviceObject->StackSize;
703     /* Already provide the IRP */
704     WorkItem->Irp = IoAllocateIrp(DeviceObject->StackSize, FALSE);
705 
706     ObDereferenceObject(DeviceObject);
707 
708     if (!WorkItem->Irp)
709     {
710         goto Cleanup;
711     }
712 
713     /* Ensure it has enough space */
714     IrpBuffer = AllocatePool(sizeof(MOUNTDEV_UNIQUE_ID_CHANGE_NOTIFY_OUTPUT) + 1024);
715     if (!IrpBuffer)
716     {
717         goto Cleanup;
718     }
719 
720     WorkItem->DeviceName.Length = DeviceName->Length;
721     WorkItem->DeviceName.MaximumLength = DeviceName->Length + sizeof(WCHAR);
722     WorkItem->DeviceName.Buffer = AllocatePool(WorkItem->DeviceName.MaximumLength);
723     if (!WorkItem->DeviceName.Buffer)
724     {
725         goto Cleanup;
726     }
727 
728     RtlCopyMemory(WorkItem->DeviceName.Buffer, DeviceName->Buffer, DeviceName->Length);
729     WorkItem->DeviceName.Buffer[DeviceName->Length / sizeof(WCHAR)] = UNICODE_NULL;
730 
731     WorkItem->IrpBuffer = IrpBuffer;
732     WorkItem->IrpBufferLength = sizeof(MOUNTDEV_UNIQUE_ID_CHANGE_NOTIFY_OUTPUT) + 1024;
733 
734     /* Add the worker in the list */
735     KeWaitForSingleObject(&(DeviceExtension->DeviceLock), Executive, KernelMode, FALSE, NULL);
736     InsertHeadList(&(DeviceExtension->UniqueIdWorkerItemListHead), &(WorkItem->UniqueIdWorkerItemListEntry));
737     KeReleaseSemaphore(&(DeviceExtension->DeviceLock), IO_NO_INCREMENT, 1, FALSE);
738 
739     /* And call the worker */
740     IssueUniqueIdChangeNotifyWorker(WorkItem, UniqueId);
741 
742     return;
743 
744 Cleanup:
745     if (IrpBuffer)
746     {
747         FreePool(IrpBuffer);
748     }
749 
750     if (WorkItem->Irp)
751     {
752         IoFreeIrp(WorkItem->Irp);
753     }
754 
755     if (WorkItem->WorkItem)
756     {
757         IoFreeWorkItem(WorkItem->WorkItem);
758     }
759 
760     if (WorkItem)
761     {
762         FreePool(WorkItem);
763     }
764 }
765