xref: /reactos/drivers/filesystems/btrfs/pnp.c (revision ebaf247c)
1 /* Copyright (c) Mark Harmstone 2016-17
2  *
3  * This file is part of WinBtrfs.
4  *
5  * WinBtrfs is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU Lesser General Public Licence as published by
7  * the Free Software Foundation, either version 3 of the Licence, or
8  * (at your option) any later version.
9  *
10  * WinBtrfs 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 Lesser General Public Licence for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public Licence
16  * along with WinBtrfs.  If not, see <http://www.gnu.org/licenses/>. */
17 
18 #include "btrfs_drv.h"
19 
20 struct pnp_context;
21 
22 typedef struct {
23     struct pnp_context* context;
24     PIRP Irp;
25     IO_STATUS_BLOCK iosb;
26     NTSTATUS Status;
27     device* dev;
28 } pnp_stripe;
29 
30 typedef struct {
31     KEVENT Event;
32     NTSTATUS Status;
33     LONG left;
34     pnp_stripe* stripes;
35 } pnp_context;
36 
37 extern ERESOURCE pdo_list_lock;
38 extern LIST_ENTRY pdo_list;
39 
40 _Function_class_(IO_COMPLETION_ROUTINE)
41 static NTSTATUS __stdcall pnp_completion(PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID conptr) {
42     pnp_stripe* stripe = conptr;
43     pnp_context* context = (pnp_context*)stripe->context;
44 
45     UNUSED(DeviceObject);
46 
47     stripe->Status = Irp->IoStatus.Status;
48 
49     InterlockedDecrement(&context->left);
50 
51     if (context->left == 0)
52         KeSetEvent(&context->Event, 0, false);
53 
54     return STATUS_MORE_PROCESSING_REQUIRED;
55 }
56 
57 static NTSTATUS send_disks_pnp_message(device_extension* Vcb, UCHAR minor) {
58     pnp_context context;
59     ULONG num_devices, i;
60     NTSTATUS Status;
61     LIST_ENTRY* le;
62 
63     RtlZeroMemory(&context, sizeof(pnp_context));
64     KeInitializeEvent(&context.Event, NotificationEvent, false);
65 
66     num_devices = (ULONG)min(0xffffffff, Vcb->superblock.num_devices);
67 
68     context.stripes = ExAllocatePoolWithTag(NonPagedPool, sizeof(pnp_stripe) * num_devices, ALLOC_TAG);
69     if (!context.stripes) {
70         ERR("out of memory\n");
71         return STATUS_INSUFFICIENT_RESOURCES;
72     }
73 
74     RtlZeroMemory(context.stripes, sizeof(pnp_stripe) * num_devices);
75 
76     i = 0;
77     le = Vcb->devices.Flink;
78 
79     while (le != &Vcb->devices) {
80         PIO_STACK_LOCATION IrpSp;
81         device* dev = CONTAINING_RECORD(le, device, list_entry);
82 
83         if (dev->devobj) {
84             context.stripes[i].context = (struct pnp_context*)&context;
85 
86             context.stripes[i].Irp = IoAllocateIrp(dev->devobj->StackSize, false);
87 
88             if (!context.stripes[i].Irp) {
89                 uint64_t j;
90 
91                 ERR("IoAllocateIrp failed\n");
92 
93                 for (j = 0; j < i; j++) {
94                     if (context.stripes[j].dev->devobj) {
95                         IoFreeIrp(context.stripes[j].Irp);
96                     }
97                 }
98                 ExFreePool(context.stripes);
99 
100                 return STATUS_INSUFFICIENT_RESOURCES;
101             }
102 
103             IrpSp = IoGetNextIrpStackLocation(context.stripes[i].Irp);
104             IrpSp->MajorFunction = IRP_MJ_PNP;
105             IrpSp->MinorFunction = minor;
106             IrpSp->FileObject = dev->fileobj;
107 
108             context.stripes[i].Irp->UserIosb = &context.stripes[i].iosb;
109 
110             IoSetCompletionRoutine(context.stripes[i].Irp, pnp_completion, &context.stripes[i], true, true, true);
111 
112             context.stripes[i].Irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
113             context.stripes[i].dev = dev;
114 
115             context.left++;
116         }
117 
118         le = le->Flink;
119     }
120 
121     if (context.left == 0) {
122         Status = STATUS_SUCCESS;
123         goto end;
124     }
125 
126     for (i = 0; i < num_devices; i++) {
127         if (context.stripes[i].Irp) {
128             IoCallDriver(context.stripes[i].dev->devobj, context.stripes[i].Irp);
129         }
130     }
131 
132     KeWaitForSingleObject(&context.Event, Executive, KernelMode, false, NULL);
133 
134     Status = STATUS_SUCCESS;
135 
136     for (i = 0; i < num_devices; i++) {
137         if (context.stripes[i].Irp) {
138             if (context.stripes[i].Status != STATUS_SUCCESS)
139                 Status = context.stripes[i].Status;
140         }
141     }
142 
143 end:
144     for (i = 0; i < num_devices; i++) {
145         if (context.stripes[i].Irp) {
146             IoFreeIrp(context.stripes[i].Irp);
147         }
148     }
149 
150     ExFreePool(context.stripes);
151 
152     return Status;
153 }
154 
155 static NTSTATUS pnp_cancel_remove_device(PDEVICE_OBJECT DeviceObject) {
156     device_extension* Vcb = DeviceObject->DeviceExtension;
157     NTSTATUS Status;
158 
159     ExAcquireResourceSharedLite(&Vcb->tree_lock, true);
160 
161     ExAcquireResourceExclusiveLite(&Vcb->fileref_lock, true);
162 
163     if (Vcb->root_fileref && Vcb->root_fileref->fcb && (Vcb->root_fileref->open_count > 0 || has_open_children(Vcb->root_fileref))) {
164         Status = STATUS_ACCESS_DENIED;
165         goto end;
166     }
167 
168     Status = send_disks_pnp_message(Vcb, IRP_MN_CANCEL_REMOVE_DEVICE);
169     if (!NT_SUCCESS(Status)) {
170         WARN("send_disks_pnp_message returned %08x\n", Status);
171         goto end;
172     }
173 
174 end:
175     ExReleaseResourceLite(&Vcb->fileref_lock);
176     ExReleaseResourceLite(&Vcb->tree_lock);
177 
178     return STATUS_SUCCESS;
179 }
180 
181 NTSTATUS pnp_query_remove_device(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
182     device_extension* Vcb = DeviceObject->DeviceExtension;
183     NTSTATUS Status;
184 
185     ExAcquireResourceExclusiveLite(&Vcb->tree_lock, true);
186 
187     if (Vcb->root_fileref && Vcb->root_fileref->fcb && (Vcb->root_fileref->open_count > 0 || has_open_children(Vcb->root_fileref))) {
188         ExReleaseResourceLite(&Vcb->tree_lock);
189         return STATUS_ACCESS_DENIED;
190     }
191 
192     Status = send_disks_pnp_message(Vcb, IRP_MN_QUERY_REMOVE_DEVICE);
193     if (!NT_SUCCESS(Status)) {
194         WARN("send_disks_pnp_message returned %08x\n", Status);
195         ExReleaseResourceLite(&Vcb->tree_lock);
196         return Status;
197     }
198 
199     Vcb->removing = true;
200 
201     if (Vcb->need_write && !Vcb->readonly) {
202         Status = do_write(Vcb, Irp);
203 
204         free_trees(Vcb);
205 
206         if (!NT_SUCCESS(Status)) {
207             ERR("do_write returned %08x\n", Status);
208             ExReleaseResourceLite(&Vcb->tree_lock);
209             return Status;
210         }
211     }
212 
213     ExReleaseResourceLite(&Vcb->tree_lock);
214 
215     if (Vcb->open_files == 0)
216         uninit(Vcb);
217 
218     return STATUS_SUCCESS;
219 }
220 
221 static NTSTATUS pnp_remove_device(PDEVICE_OBJECT DeviceObject) {
222     device_extension* Vcb = DeviceObject->DeviceExtension;
223     NTSTATUS Status;
224 
225     ExAcquireResourceSharedLite(&Vcb->tree_lock, true);
226 
227     Status = send_disks_pnp_message(Vcb, IRP_MN_REMOVE_DEVICE);
228 
229     if (!NT_SUCCESS(Status))
230         WARN("send_disks_pnp_message returned %08x\n", Status);
231 
232     ExReleaseResourceLite(&Vcb->tree_lock);
233 
234     if (DeviceObject->Vpb->Flags & VPB_MOUNTED) {
235         Status = FsRtlNotifyVolumeEvent(Vcb->root_file, FSRTL_VOLUME_DISMOUNT);
236         if (!NT_SUCCESS(Status)) {
237             WARN("FsRtlNotifyVolumeEvent returned %08x\n", Status);
238         }
239 
240         if (Vcb->vde)
241             Vcb->vde->mounted_device = NULL;
242 
243         ExAcquireResourceExclusiveLite(&Vcb->tree_lock, true);
244         Vcb->removing = true;
245         ExReleaseResourceLite(&Vcb->tree_lock);
246 
247         if (Vcb->open_files == 0)
248             uninit(Vcb);
249     }
250 
251     return STATUS_SUCCESS;
252 }
253 
254 NTSTATUS pnp_surprise_removal(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
255     device_extension* Vcb = DeviceObject->DeviceExtension;
256 
257     TRACE("(%p, %p)\n", DeviceObject, Irp);
258 
259     if (DeviceObject->Vpb->Flags & VPB_MOUNTED) {
260         ExAcquireResourceExclusiveLite(&Vcb->tree_lock, true);
261 
262         if (Vcb->vde)
263             Vcb->vde->mounted_device = NULL;
264 
265         Vcb->removing = true;
266 
267         ExReleaseResourceLite(&Vcb->tree_lock);
268 
269         if (Vcb->open_files == 0)
270             uninit(Vcb);
271     }
272 
273     return STATUS_SUCCESS;
274 }
275 
276 static NTSTATUS bus_query_capabilities(PIRP Irp) {
277     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
278     PDEVICE_CAPABILITIES dc = IrpSp->Parameters.DeviceCapabilities.Capabilities;
279 
280     dc->UniqueID = true;
281     dc->SilentInstall = true;
282 
283     return STATUS_SUCCESS;
284 }
285 
286 static NTSTATUS bus_query_device_relations(PIRP Irp) {
287     NTSTATUS Status;
288     ULONG num_children;
289     LIST_ENTRY* le;
290     ULONG drsize, i;
291     DEVICE_RELATIONS* dr;
292 
293     ExAcquireResourceSharedLite(&pdo_list_lock, true);
294 
295     num_children = 0;
296 
297     le = pdo_list.Flink;
298     while (le != &pdo_list) {
299         pdo_device_extension* pdode = CONTAINING_RECORD(le, pdo_device_extension, list_entry);
300 
301         if (!pdode->dont_report)
302             num_children++;
303 
304         le = le->Flink;
305     }
306 
307     drsize = offsetof(DEVICE_RELATIONS, Objects[0]) + (num_children * sizeof(PDEVICE_OBJECT));
308     dr = ExAllocatePoolWithTag(PagedPool, drsize, ALLOC_TAG);
309 
310     if (!dr) {
311         ERR("out of memory\n");
312         Status = STATUS_INSUFFICIENT_RESOURCES;
313         goto end;
314     }
315 
316     dr->Count = num_children;
317 
318     i = 0;
319     le = pdo_list.Flink;
320     while (le != &pdo_list) {
321         pdo_device_extension* pdode = CONTAINING_RECORD(le, pdo_device_extension, list_entry);
322 
323         if (!pdode->dont_report) {
324             ObReferenceObject(pdode->pdo);
325             dr->Objects[i] = pdode->pdo;
326             i++;
327         }
328 
329         le = le->Flink;
330     }
331 
332     Irp->IoStatus.Information = (ULONG_PTR)dr;
333 
334     Status = STATUS_SUCCESS;
335 
336 end:
337     ExReleaseResourceLite(&pdo_list_lock);
338 
339     return Status;
340 }
341 
342 static NTSTATUS bus_query_hardware_ids(PIRP Irp) {
343     WCHAR* out;
344 
345     static const WCHAR ids[] = L"ROOT\\btrfs\0";
346 
347     out = ExAllocatePoolWithTag(PagedPool, sizeof(ids), ALLOC_TAG);
348     if (!out) {
349         ERR("out of memory\n");
350         return STATUS_INSUFFICIENT_RESOURCES;
351     }
352 
353     RtlCopyMemory(out, ids, sizeof(ids));
354 
355     Irp->IoStatus.Information = (ULONG_PTR)out;
356 
357     return STATUS_SUCCESS;
358 }
359 
360 static NTSTATUS bus_pnp(bus_device_extension* bde, PIRP Irp) {
361     NTSTATUS Status = Irp->IoStatus.Status;
362     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
363     bool handled = false;
364 
365     switch (IrpSp->MinorFunction) {
366         case IRP_MN_QUERY_CAPABILITIES:
367             Status = bus_query_capabilities(Irp);
368             handled = true;
369             break;
370 
371         case IRP_MN_QUERY_DEVICE_RELATIONS:
372             if (IrpSp->Parameters.QueryDeviceRelations.Type != BusRelations || no_pnp)
373                 break;
374 
375             Status = bus_query_device_relations(Irp);
376             handled = true;
377             break;
378 
379         case IRP_MN_QUERY_ID:
380             if (IrpSp->Parameters.QueryId.IdType != BusQueryHardwareIDs)
381                 break;
382 
383             Status = bus_query_hardware_ids(Irp);
384             handled = true;
385             break;
386     }
387 
388     if (!NT_SUCCESS(Status) && handled) {
389         Irp->IoStatus.Status = Status;
390         IoCompleteRequest(Irp, IO_NO_INCREMENT);
391 
392         return Status;
393     }
394 
395     Irp->IoStatus.Status = Status;
396 
397     IoSkipCurrentIrpStackLocation(Irp);
398     return IoCallDriver(bde->attached_device, Irp);
399 }
400 
401 static NTSTATUS pdo_query_device_id(pdo_device_extension* pdode, PIRP Irp) {
402     WCHAR name[100], *noff, *out;
403     int i;
404 
405     static const WCHAR pref[] = L"Btrfs\\";
406 
407     RtlCopyMemory(name, pref, sizeof(pref) - sizeof(WCHAR));
408 
409     noff = &name[(sizeof(pref) / sizeof(WCHAR)) - 1];
410     for (i = 0; i < 16; i++) {
411         *noff = hex_digit(pdode->uuid.uuid[i] >> 4); noff++;
412         *noff = hex_digit(pdode->uuid.uuid[i] & 0xf); noff++;
413 
414         if (i == 3 || i == 5 || i == 7 || i == 9) {
415             *noff = '-';
416             noff++;
417         }
418     }
419     *noff = 0;
420 
421     out = ExAllocatePoolWithTag(PagedPool, (wcslen(name) + 1) * sizeof(WCHAR), ALLOC_TAG);
422     if (!out) {
423         ERR("out of memory\n");
424         return STATUS_INSUFFICIENT_RESOURCES;
425     }
426 
427     RtlCopyMemory(out, name, (wcslen(name) + 1) * sizeof(WCHAR));
428 
429     Irp->IoStatus.Information = (ULONG_PTR)out;
430 
431     return STATUS_SUCCESS;
432 }
433 
434 static NTSTATUS pdo_query_hardware_ids(PIRP Irp) {
435     WCHAR* out;
436 
437     static const WCHAR ids[] = L"BtrfsVolume\0";
438 
439     out = ExAllocatePoolWithTag(PagedPool, sizeof(ids), ALLOC_TAG);
440     if (!out) {
441         ERR("out of memory\n");
442         return STATUS_INSUFFICIENT_RESOURCES;
443     }
444 
445     RtlCopyMemory(out, ids, sizeof(ids));
446 
447     Irp->IoStatus.Information = (ULONG_PTR)out;
448 
449     return STATUS_SUCCESS;
450 }
451 
452 static NTSTATUS pdo_query_id(pdo_device_extension* pdode, PIRP Irp) {
453     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
454 
455     switch (IrpSp->Parameters.QueryId.IdType) {
456         case BusQueryDeviceID:
457             TRACE("BusQueryDeviceID\n");
458             return pdo_query_device_id(pdode, Irp);
459 
460         case BusQueryHardwareIDs:
461             TRACE("BusQueryHardwareIDs\n");
462             return pdo_query_hardware_ids(Irp);
463 
464         default:
465             break;
466     }
467 
468     return Irp->IoStatus.Status;
469 }
470 
471 typedef struct {
472     IO_STATUS_BLOCK iosb;
473     KEVENT Event;
474     NTSTATUS Status;
475 } device_usage_context;
476 
477 _Function_class_(IO_COMPLETION_ROUTINE)
478 static NTSTATUS __stdcall device_usage_completion(PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID conptr) {
479     device_usage_context* context = conptr;
480 
481     UNUSED(DeviceObject);
482 
483     context->Status = Irp->IoStatus.Status;
484 
485     KeSetEvent(&context->Event, 0, false);
486 
487     return STATUS_MORE_PROCESSING_REQUIRED;
488 }
489 
490 static NTSTATUS pdo_device_usage_notification(pdo_device_extension* pdode, PIRP Irp) {
491     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
492     LIST_ENTRY* le;
493 
494     TRACE("(%p, %p)\n", pdode, Irp);
495 
496     ExAcquireResourceSharedLite(&pdode->child_lock, true);
497 
498     le = pdode->children.Flink;
499 
500     while (le != &pdode->children) {
501         volume_child* vc = CONTAINING_RECORD(le, volume_child, list_entry);
502 
503         if (vc->devobj) {
504             PIRP Irp2;
505             PIO_STACK_LOCATION IrpSp2;
506             device_usage_context context;
507 
508             Irp2 = IoAllocateIrp(vc->devobj->StackSize, false);
509             if (!Irp2) {
510                 ERR("out of memory\n");
511                 ExReleaseResourceLite(&pdode->child_lock);
512                 return STATUS_INSUFFICIENT_RESOURCES;
513             }
514 
515             IrpSp2 = IoGetNextIrpStackLocation(Irp2);
516             IrpSp2->MajorFunction = IRP_MJ_PNP;
517             IrpSp2->MinorFunction = IRP_MN_DEVICE_USAGE_NOTIFICATION;
518             IrpSp2->Parameters.UsageNotification = IrpSp->Parameters.UsageNotification;
519             IrpSp2->FileObject = vc->fileobj;
520 
521             context.iosb.Status = STATUS_SUCCESS;
522             Irp2->UserIosb = &context.iosb;
523 
524             KeInitializeEvent(&context.Event, NotificationEvent, false);
525             Irp2->UserEvent = &context.Event;
526 
527             IoSetCompletionRoutine(Irp2, device_usage_completion, &context, true, true, true);
528 
529             context.Status = IoCallDriver(vc->devobj, Irp2);
530 
531             if (context.Status == STATUS_PENDING)
532                 KeWaitForSingleObject(&context.Event, Executive, KernelMode, false, NULL);
533 
534             if (!NT_SUCCESS(context.Status)) {
535                 ERR("IoCallDriver returned %08x\n", context.Status);
536                 ExReleaseResourceLite(&pdode->child_lock);
537                 return context.Status;
538             }
539         }
540 
541         le = le->Flink;
542     }
543 
544     ExReleaseResourceLite(&pdode->child_lock);
545 
546     return STATUS_SUCCESS;
547 }
548 
549 static NTSTATUS pdo_query_device_relations(PDEVICE_OBJECT pdo, PIRP Irp) {
550     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
551     PDEVICE_RELATIONS device_relations;
552 
553     if (IrpSp->Parameters.QueryDeviceRelations.Type != TargetDeviceRelation)
554         return Irp->IoStatus.Status;
555 
556     device_relations = ExAllocatePoolWithTag(PagedPool, sizeof(DEVICE_RELATIONS), ALLOC_TAG);
557     if (!device_relations) {
558         ERR("out of memory\n");
559         return STATUS_INSUFFICIENT_RESOURCES;
560     }
561 
562     device_relations->Count = 1;
563     device_relations->Objects[0] = pdo;
564 
565     ObReferenceObject(pdo);
566 
567     Irp->IoStatus.Information = (ULONG_PTR)device_relations;
568 
569     return STATUS_SUCCESS;
570 }
571 
572 static NTSTATUS pdo_pnp(PDEVICE_OBJECT pdo, PIRP Irp) {
573     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
574     pdo_device_extension* pdode = pdo->DeviceExtension;
575 
576     switch (IrpSp->MinorFunction) {
577         case IRP_MN_QUERY_ID:
578             return pdo_query_id(pdode, Irp);
579 
580         case IRP_MN_START_DEVICE:
581         case IRP_MN_CANCEL_REMOVE_DEVICE:
582         case IRP_MN_SURPRISE_REMOVAL:
583         case IRP_MN_REMOVE_DEVICE:
584             return STATUS_SUCCESS;
585 
586         case IRP_MN_QUERY_REMOVE_DEVICE:
587             return STATUS_UNSUCCESSFUL;
588 
589         case IRP_MN_DEVICE_USAGE_NOTIFICATION:
590             return pdo_device_usage_notification(pdode, Irp);
591 
592         case IRP_MN_QUERY_DEVICE_RELATIONS:
593             return pdo_query_device_relations(pdo, Irp);
594     }
595 
596     return Irp->IoStatus.Status;
597 }
598 
599 static NTSTATUS pnp_device_usage_notification(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
600     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
601     device_extension* Vcb = DeviceObject->DeviceExtension;
602 
603     if (IrpSp->Parameters.UsageNotification.InPath) {
604         switch (IrpSp->Parameters.UsageNotification.Type) {
605             case DeviceUsageTypePaging:
606             case DeviceUsageTypeHibernation:
607             case DeviceUsageTypeDumpFile:
608                 IoAdjustPagingPathCount(&Vcb->page_file_count, IrpSp->Parameters.UsageNotification.InPath);
609                 break;
610 
611             default:
612                 break;
613         }
614     }
615 
616     IoSkipCurrentIrpStackLocation(Irp);
617     return IoCallDriver(Vcb->Vpb->RealDevice, Irp);
618 }
619 
620 _Dispatch_type_(IRP_MJ_PNP)
621 _Function_class_(DRIVER_DISPATCH)
622 NTSTATUS __stdcall drv_pnp(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
623     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
624     device_extension* Vcb = DeviceObject->DeviceExtension;
625     NTSTATUS Status;
626     bool top_level;
627 
628     FsRtlEnterFileSystem();
629 
630     top_level = is_top_level(Irp);
631 
632     if (Vcb && Vcb->type == VCB_TYPE_BUS) {
633         Status = bus_pnp(DeviceObject->DeviceExtension, Irp);
634         goto exit;
635     } else if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
636         volume_device_extension* vde = DeviceObject->DeviceExtension;
637         IoSkipCurrentIrpStackLocation(Irp);
638         Status = IoCallDriver(vde->attached_device, Irp);
639         goto exit;
640     } else if (Vcb && Vcb->type == VCB_TYPE_PDO) {
641         Status = pdo_pnp(DeviceObject, Irp);
642         goto end;
643     } else if (!Vcb || Vcb->type != VCB_TYPE_FS) {
644         Status = STATUS_INVALID_PARAMETER;
645         goto end;
646     }
647 
648     Status = STATUS_NOT_IMPLEMENTED;
649 
650     switch (IrpSp->MinorFunction) {
651         case IRP_MN_CANCEL_REMOVE_DEVICE:
652             Status = pnp_cancel_remove_device(DeviceObject);
653             break;
654 
655         case IRP_MN_QUERY_REMOVE_DEVICE:
656             Status = pnp_query_remove_device(DeviceObject, Irp);
657             break;
658 
659         case IRP_MN_REMOVE_DEVICE:
660             Status = pnp_remove_device(DeviceObject);
661             break;
662 
663         case IRP_MN_SURPRISE_REMOVAL:
664             Status = pnp_surprise_removal(DeviceObject, Irp);
665             break;
666 
667         case IRP_MN_DEVICE_USAGE_NOTIFICATION:
668             Status = pnp_device_usage_notification(DeviceObject, Irp);
669             goto exit;
670 
671         default:
672             TRACE("passing minor function 0x%x on\n", IrpSp->MinorFunction);
673 
674             IoSkipCurrentIrpStackLocation(Irp);
675             Status = IoCallDriver(Vcb->Vpb->RealDevice, Irp);
676             goto exit;
677     }
678 
679 end:
680     Irp->IoStatus.Status = Status;
681 
682     IoCompleteRequest(Irp, IO_NO_INCREMENT);
683 
684 exit:
685     TRACE("returning %08x\n", Status);
686 
687     if (top_level)
688         IoSetTopLevelIrp(NULL);
689 
690     FsRtlExitFileSystem();
691 
692     return Status;
693 }
694