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