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