xref: /reactos/drivers/filesystems/btrfs/pnp.c (revision 09dde2cf)
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 extern ERESOURCE pdo_list_lock;
21 extern LIST_ENTRY pdo_list;
22 
23 NTSTATUS pnp_query_remove_device(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
24     device_extension* Vcb = DeviceObject->DeviceExtension;
25     NTSTATUS Status;
26 
27     // We might be going away imminently - do a flush so we're not caught out
28 
29     ExAcquireResourceExclusiveLite(&Vcb->tree_lock, true);
30 
31     if (Vcb->root_fileref && Vcb->root_fileref->fcb && (Vcb->root_fileref->open_count > 0 || has_open_children(Vcb->root_fileref))) {
32         ExReleaseResourceLite(&Vcb->tree_lock);
33         return STATUS_ACCESS_DENIED;
34     }
35 
36     if (Vcb->need_write && !Vcb->readonly) {
37         Status = do_write(Vcb, Irp);
38 
39         free_trees(Vcb);
40 
41         if (!NT_SUCCESS(Status)) {
42             ERR("do_write returned %08lx\n", Status);
43             ExReleaseResourceLite(&Vcb->tree_lock);
44             return Status;
45         }
46     }
47 
48     ExReleaseResourceLite(&Vcb->tree_lock);
49 
50     return STATUS_UNSUCCESSFUL;
51 }
52 
53 static NTSTATUS pnp_remove_device(PDEVICE_OBJECT DeviceObject) {
54     device_extension* Vcb = DeviceObject->DeviceExtension;
55     NTSTATUS Status;
56 
57     if (DeviceObject->Vpb->Flags & VPB_MOUNTED) {
58         Status = FsRtlNotifyVolumeEvent(Vcb->root_file, FSRTL_VOLUME_DISMOUNT);
59         if (!NT_SUCCESS(Status)) {
60             WARN("FsRtlNotifyVolumeEvent returned %08lx\n", Status);
61         }
62 
63         if (Vcb->vde)
64             Vcb->vde->mounted_device = NULL;
65 
66         ExAcquireResourceExclusiveLite(&Vcb->tree_lock, true);
67         Vcb->removing = true;
68         ExReleaseResourceLite(&Vcb->tree_lock);
69 
70         if (Vcb->open_files == 0)
71             uninit(Vcb);
72     }
73 
74     return STATUS_SUCCESS;
75 }
76 
77 NTSTATUS pnp_surprise_removal(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
78     device_extension* Vcb = DeviceObject->DeviceExtension;
79 
80     TRACE("(%p, %p)\n", DeviceObject, Irp);
81 
82     UNUSED(Irp);
83 
84     if (DeviceObject->Vpb->Flags & VPB_MOUNTED) {
85         ExAcquireResourceExclusiveLite(&Vcb->tree_lock, true);
86 
87         if (Vcb->vde)
88             Vcb->vde->mounted_device = NULL;
89 
90         Vcb->removing = true;
91 
92         ExReleaseResourceLite(&Vcb->tree_lock);
93 
94         if (Vcb->open_files == 0)
95             uninit(Vcb);
96     }
97 
98     return STATUS_SUCCESS;
99 }
100 
101 static NTSTATUS bus_query_capabilities(PIRP Irp) {
102     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
103     PDEVICE_CAPABILITIES dc = IrpSp->Parameters.DeviceCapabilities.Capabilities;
104 
105     dc->UniqueID = true;
106     dc->SilentInstall = true;
107 
108     return STATUS_SUCCESS;
109 }
110 
111 static NTSTATUS bus_query_device_relations(PIRP Irp) {
112     NTSTATUS Status;
113     ULONG num_children;
114     LIST_ENTRY* le;
115     ULONG drsize, i;
116     DEVICE_RELATIONS* dr;
117 
118     ExAcquireResourceSharedLite(&pdo_list_lock, true);
119 
120     num_children = 0;
121 
122     le = pdo_list.Flink;
123     while (le != &pdo_list) {
124         pdo_device_extension* pdode = CONTAINING_RECORD(le, pdo_device_extension, list_entry);
125 
126         if (!pdode->dont_report)
127             num_children++;
128 
129         le = le->Flink;
130     }
131 
132     drsize = offsetof(DEVICE_RELATIONS, Objects[0]) + (num_children * sizeof(PDEVICE_OBJECT));
133     dr = ExAllocatePoolWithTag(PagedPool, drsize, ALLOC_TAG);
134 
135     if (!dr) {
136         ERR("out of memory\n");
137         Status = STATUS_INSUFFICIENT_RESOURCES;
138         goto end;
139     }
140 
141     dr->Count = num_children;
142 
143     i = 0;
144     le = pdo_list.Flink;
145     while (le != &pdo_list) {
146         pdo_device_extension* pdode = CONTAINING_RECORD(le, pdo_device_extension, list_entry);
147 
148         if (!pdode->dont_report) {
149             ObReferenceObject(pdode->pdo);
150             dr->Objects[i] = pdode->pdo;
151             i++;
152         }
153 
154         le = le->Flink;
155     }
156 
157     Irp->IoStatus.Information = (ULONG_PTR)dr;
158 
159     Status = STATUS_SUCCESS;
160 
161 end:
162     ExReleaseResourceLite(&pdo_list_lock);
163 
164     return Status;
165 }
166 
167 static NTSTATUS bus_query_hardware_ids(PIRP Irp) {
168     WCHAR* out;
169 
170     static const WCHAR ids[] = L"ROOT\\btrfs\0";
171 
172     out = ExAllocatePoolWithTag(PagedPool, sizeof(ids), ALLOC_TAG);
173     if (!out) {
174         ERR("out of memory\n");
175         return STATUS_INSUFFICIENT_RESOURCES;
176     }
177 
178     RtlCopyMemory(out, ids, sizeof(ids));
179 
180     Irp->IoStatus.Information = (ULONG_PTR)out;
181 
182     return STATUS_SUCCESS;
183 }
184 
185 static NTSTATUS bus_pnp(bus_device_extension* bde, PIRP Irp) {
186     NTSTATUS Status = Irp->IoStatus.Status;
187     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
188     bool handled = false;
189 
190     switch (IrpSp->MinorFunction) {
191         case IRP_MN_START_DEVICE:
192         case IRP_MN_CANCEL_REMOVE_DEVICE:
193         case IRP_MN_SURPRISE_REMOVAL:
194         case IRP_MN_REMOVE_DEVICE:
195             Status = STATUS_SUCCESS;
196             handled = true;
197             break;
198 
199         case IRP_MN_QUERY_REMOVE_DEVICE:
200             Status = STATUS_UNSUCCESSFUL;
201             handled = true;
202             break;
203 
204         case IRP_MN_QUERY_CAPABILITIES:
205             Status = bus_query_capabilities(Irp);
206             handled = true;
207             break;
208 
209         case IRP_MN_QUERY_DEVICE_RELATIONS:
210             if (IrpSp->Parameters.QueryDeviceRelations.Type != BusRelations || no_pnp)
211                 break;
212 
213             Status = bus_query_device_relations(Irp);
214             handled = true;
215             break;
216 
217         case IRP_MN_QUERY_ID:
218             if (IrpSp->Parameters.QueryId.IdType != BusQueryHardwareIDs)
219                 break;
220 
221             Status = bus_query_hardware_ids(Irp);
222             handled = true;
223             break;
224     }
225 
226     if (!NT_SUCCESS(Status) && handled) {
227         Irp->IoStatus.Status = Status;
228         IoCompleteRequest(Irp, IO_NO_INCREMENT);
229 
230         return Status;
231     }
232 
233     Irp->IoStatus.Status = Status;
234 
235     IoSkipCurrentIrpStackLocation(Irp);
236     return IoCallDriver(bde->attached_device, Irp);
237 }
238 
239 static NTSTATUS pdo_query_device_id(pdo_device_extension* pdode, PIRP Irp) {
240     WCHAR name[100], *noff, *out;
241     int i;
242 
243     static const WCHAR pref[] = L"Btrfs\\";
244 
245     RtlCopyMemory(name, pref, sizeof(pref) - sizeof(WCHAR));
246 
247     noff = &name[(sizeof(pref) / sizeof(WCHAR)) - 1];
248     for (i = 0; i < 16; i++) {
249         *noff = hex_digit(pdode->uuid.uuid[i] >> 4); noff++;
250         *noff = hex_digit(pdode->uuid.uuid[i] & 0xf); noff++;
251 
252         if (i == 3 || i == 5 || i == 7 || i == 9) {
253             *noff = '-';
254             noff++;
255         }
256     }
257     *noff = 0;
258 
259     out = ExAllocatePoolWithTag(PagedPool, (wcslen(name) + 1) * sizeof(WCHAR), ALLOC_TAG);
260     if (!out) {
261         ERR("out of memory\n");
262         return STATUS_INSUFFICIENT_RESOURCES;
263     }
264 
265     RtlCopyMemory(out, name, (wcslen(name) + 1) * sizeof(WCHAR));
266 
267     Irp->IoStatus.Information = (ULONG_PTR)out;
268 
269     return STATUS_SUCCESS;
270 }
271 
272 static NTSTATUS pdo_query_hardware_ids(PIRP Irp) {
273     WCHAR* out;
274 
275     static const WCHAR ids[] = L"BtrfsVolume\0";
276 
277     out = ExAllocatePoolWithTag(PagedPool, sizeof(ids), ALLOC_TAG);
278     if (!out) {
279         ERR("out of memory\n");
280         return STATUS_INSUFFICIENT_RESOURCES;
281     }
282 
283     RtlCopyMemory(out, ids, sizeof(ids));
284 
285     Irp->IoStatus.Information = (ULONG_PTR)out;
286 
287     return STATUS_SUCCESS;
288 }
289 
290 static NTSTATUS pdo_query_id(pdo_device_extension* pdode, PIRP Irp) {
291     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
292 
293     switch (IrpSp->Parameters.QueryId.IdType) {
294         case BusQueryDeviceID:
295             TRACE("BusQueryDeviceID\n");
296             return pdo_query_device_id(pdode, Irp);
297 
298         case BusQueryHardwareIDs:
299             TRACE("BusQueryHardwareIDs\n");
300             return pdo_query_hardware_ids(Irp);
301 
302         default:
303             break;
304     }
305 
306     return Irp->IoStatus.Status;
307 }
308 
309 typedef struct {
310     IO_STATUS_BLOCK iosb;
311     KEVENT Event;
312     NTSTATUS Status;
313 } device_usage_context;
314 
315 _Function_class_(IO_COMPLETION_ROUTINE)
316 static NTSTATUS __stdcall device_usage_completion(PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID conptr) {
317     device_usage_context* context = conptr;
318 
319     UNUSED(DeviceObject);
320 
321     context->Status = Irp->IoStatus.Status;
322 
323     KeSetEvent(&context->Event, 0, false);
324 
325     return STATUS_MORE_PROCESSING_REQUIRED;
326 }
327 
328 static NTSTATUS pdo_device_usage_notification(pdo_device_extension* pdode, PIRP Irp) {
329     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
330     LIST_ENTRY* le;
331 
332     TRACE("(%p, %p)\n", pdode, Irp);
333 
334     ExAcquireResourceSharedLite(&pdode->child_lock, true);
335 
336     le = pdode->children.Flink;
337 
338     while (le != &pdode->children) {
339         volume_child* vc = CONTAINING_RECORD(le, volume_child, list_entry);
340 
341         if (vc->devobj) {
342             PIRP Irp2;
343             PIO_STACK_LOCATION IrpSp2;
344             device_usage_context context;
345 
346             Irp2 = IoAllocateIrp(vc->devobj->StackSize, false);
347             if (!Irp2) {
348                 ERR("out of memory\n");
349                 ExReleaseResourceLite(&pdode->child_lock);
350                 return STATUS_INSUFFICIENT_RESOURCES;
351             }
352 
353             IrpSp2 = IoGetNextIrpStackLocation(Irp2);
354             IrpSp2->MajorFunction = IRP_MJ_PNP;
355             IrpSp2->MinorFunction = IRP_MN_DEVICE_USAGE_NOTIFICATION;
356             IrpSp2->Parameters.UsageNotification = IrpSp->Parameters.UsageNotification;
357             IrpSp2->FileObject = vc->fileobj;
358 
359             context.iosb.Status = STATUS_SUCCESS;
360             Irp2->UserIosb = &context.iosb;
361 
362             KeInitializeEvent(&context.Event, NotificationEvent, false);
363             Irp2->UserEvent = &context.Event;
364 
365             IoSetCompletionRoutine(Irp2, device_usage_completion, &context, true, true, true);
366 
367             context.Status = IoCallDriver(vc->devobj, Irp2);
368 
369             if (context.Status == STATUS_PENDING)
370                 KeWaitForSingleObject(&context.Event, Executive, KernelMode, false, NULL);
371 
372             if (!NT_SUCCESS(context.Status)) {
373                 ERR("IoCallDriver returned %08lx\n", context.Status);
374                 ExReleaseResourceLite(&pdode->child_lock);
375                 return context.Status;
376             }
377         }
378 
379         le = le->Flink;
380     }
381 
382     ExReleaseResourceLite(&pdode->child_lock);
383 
384     return STATUS_SUCCESS;
385 }
386 
387 static NTSTATUS pdo_query_device_relations(PDEVICE_OBJECT pdo, PIRP Irp) {
388     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
389     PDEVICE_RELATIONS device_relations;
390 
391     if (IrpSp->Parameters.QueryDeviceRelations.Type != TargetDeviceRelation)
392         return Irp->IoStatus.Status;
393 
394     device_relations = ExAllocatePoolWithTag(PagedPool, sizeof(DEVICE_RELATIONS), ALLOC_TAG);
395     if (!device_relations) {
396         ERR("out of memory\n");
397         return STATUS_INSUFFICIENT_RESOURCES;
398     }
399 
400     device_relations->Count = 1;
401     device_relations->Objects[0] = pdo;
402 
403     ObReferenceObject(pdo);
404 
405     Irp->IoStatus.Information = (ULONG_PTR)device_relations;
406 
407     return STATUS_SUCCESS;
408 }
409 
410 static NTSTATUS pdo_pnp(PDEVICE_OBJECT pdo, PIRP Irp) {
411     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
412     pdo_device_extension* pdode = pdo->DeviceExtension;
413 
414     switch (IrpSp->MinorFunction) {
415         case IRP_MN_QUERY_ID:
416             return pdo_query_id(pdode, Irp);
417 
418         case IRP_MN_START_DEVICE:
419         case IRP_MN_CANCEL_REMOVE_DEVICE:
420         case IRP_MN_SURPRISE_REMOVAL:
421         case IRP_MN_REMOVE_DEVICE:
422             return STATUS_SUCCESS;
423 
424         case IRP_MN_QUERY_REMOVE_DEVICE:
425             return STATUS_UNSUCCESSFUL;
426 
427         case IRP_MN_DEVICE_USAGE_NOTIFICATION:
428             return pdo_device_usage_notification(pdode, Irp);
429 
430         case IRP_MN_QUERY_DEVICE_RELATIONS:
431             return pdo_query_device_relations(pdo, Irp);
432     }
433 
434     return Irp->IoStatus.Status;
435 }
436 
437 static NTSTATUS pnp_device_usage_notification(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
438     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
439     device_extension* Vcb = DeviceObject->DeviceExtension;
440 
441     if (IrpSp->Parameters.UsageNotification.InPath) {
442         switch (IrpSp->Parameters.UsageNotification.Type) {
443             case DeviceUsageTypePaging:
444             case DeviceUsageTypeHibernation:
445             case DeviceUsageTypeDumpFile:
446                 IoAdjustPagingPathCount(&Vcb->page_file_count, IrpSp->Parameters.UsageNotification.InPath);
447                 break;
448 
449             default:
450                 break;
451         }
452     }
453 
454     IoSkipCurrentIrpStackLocation(Irp);
455     return IoCallDriver(Vcb->Vpb->RealDevice, Irp);
456 }
457 
458 _Dispatch_type_(IRP_MJ_PNP)
459 _Function_class_(DRIVER_DISPATCH)
460 NTSTATUS __stdcall drv_pnp(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
461     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
462     device_extension* Vcb = DeviceObject->DeviceExtension;
463     NTSTATUS Status;
464     bool top_level;
465 
466     FsRtlEnterFileSystem();
467 
468     top_level = is_top_level(Irp);
469 
470     if (Vcb && Vcb->type == VCB_TYPE_BUS) {
471         Status = bus_pnp(DeviceObject->DeviceExtension, Irp);
472         goto exit;
473     } else if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
474         volume_device_extension* vde = DeviceObject->DeviceExtension;
475         IoSkipCurrentIrpStackLocation(Irp);
476         Status = IoCallDriver(vde->attached_device, Irp);
477         goto exit;
478     } else if (Vcb && Vcb->type == VCB_TYPE_PDO) {
479         Status = pdo_pnp(DeviceObject, Irp);
480         goto end;
481     } else if (!Vcb || Vcb->type != VCB_TYPE_FS) {
482         Status = STATUS_INVALID_PARAMETER;
483         goto end;
484     }
485 
486     Status = STATUS_NOT_IMPLEMENTED;
487 
488     switch (IrpSp->MinorFunction) {
489         case IRP_MN_CANCEL_REMOVE_DEVICE:
490             Status = STATUS_SUCCESS;
491             break;
492 
493         case IRP_MN_QUERY_REMOVE_DEVICE:
494             Status = pnp_query_remove_device(DeviceObject, Irp);
495             break;
496 
497         case IRP_MN_REMOVE_DEVICE:
498             Status = pnp_remove_device(DeviceObject);
499             break;
500 
501         case IRP_MN_SURPRISE_REMOVAL:
502             Status = pnp_surprise_removal(DeviceObject, Irp);
503             break;
504 
505         case IRP_MN_DEVICE_USAGE_NOTIFICATION:
506             Status = pnp_device_usage_notification(DeviceObject, Irp);
507             goto exit;
508 
509         default:
510             TRACE("passing minor function 0x%x on\n", IrpSp->MinorFunction);
511 
512             IoSkipCurrentIrpStackLocation(Irp);
513             Status = IoCallDriver(Vcb->Vpb->RealDevice, Irp);
514             goto exit;
515     }
516 
517 end:
518     Irp->IoStatus.Status = Status;
519 
520     IoCompleteRequest(Irp, IO_NO_INCREMENT);
521 
522 exit:
523     TRACE("returning %08lx\n", Status);
524 
525     if (top_level)
526         IoSetTopLevelIrp(NULL);
527 
528     FsRtlExitFileSystem();
529 
530     return Status;
531 }
532