xref: /reactos/drivers/filesystems/btrfs/pnp.c (revision ba3f0743)
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         Vcb->Vpb->Flags &= ~VPB_MOUNTED;
251         Vcb->Vpb->Flags |= VPB_DIRECT_WRITES_ALLOWED;
252         ExReleaseResourceLite(&Vcb->tree_lock);
253 
254         if (Vcb->open_files == 0)
255             uninit(Vcb, FALSE);
256     }
257 
258     return STATUS_SUCCESS;
259 }
260 
261 NTSTATUS pnp_surprise_removal(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
262     device_extension* Vcb = DeviceObject->DeviceExtension;
263 
264     TRACE("(%p, %p)\n", DeviceObject, Irp);
265 
266     if (DeviceObject->Vpb->Flags & VPB_MOUNTED) {
267         ExAcquireResourceExclusiveLite(&Vcb->tree_lock, TRUE);
268 
269         if (Vcb->vde)
270             Vcb->vde->mounted_device = NULL;
271 
272         Vcb->removing = TRUE;
273         Vcb->Vpb->Flags &= ~VPB_MOUNTED;
274         Vcb->Vpb->Flags |= VPB_DIRECT_WRITES_ALLOWED;
275 
276         ExReleaseResourceLite(&Vcb->tree_lock);
277 
278         if (Vcb->open_files == 0)
279             uninit(Vcb, FALSE);
280     }
281 
282     return STATUS_SUCCESS;
283 }
284 
285 static void bus_query_capabilities(PIRP Irp) {
286     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
287     PDEVICE_CAPABILITIES dc = IrpSp->Parameters.DeviceCapabilities.Capabilities;
288 
289     dc->UniqueID = TRUE;
290     dc->SilentInstall = TRUE;
291 
292     Irp->IoStatus.Status = STATUS_SUCCESS;
293 }
294 
295 static NTSTATUS bus_query_device_relations(PIRP Irp) {
296     NTSTATUS Status;
297     ULONG num_children;
298     LIST_ENTRY* le;
299     ULONG drsize, i;
300     DEVICE_RELATIONS* dr;
301 
302     ExAcquireResourceSharedLite(&pdo_list_lock, TRUE);
303 
304     num_children = 0;
305 
306     le = pdo_list.Flink;
307     while (le != &pdo_list) {
308         num_children++;
309 
310         le = le->Flink;
311     }
312 
313     drsize = offsetof(DEVICE_RELATIONS, Objects[0]) + (num_children * sizeof(PDEVICE_OBJECT));
314     dr = ExAllocatePoolWithTag(PagedPool, drsize, ALLOC_TAG);
315 
316     if (!dr) {
317         ERR("out of memory\n");
318         Status = STATUS_INSUFFICIENT_RESOURCES;
319         goto end;
320     }
321 
322     dr->Count = num_children;
323 
324     i = 0;
325     le = pdo_list.Flink;
326     while (le != &pdo_list) {
327         pdo_device_extension* pdode = CONTAINING_RECORD(le, pdo_device_extension, list_entry);
328 
329         ObReferenceObject(pdode->pdo);
330         dr->Objects[i] = pdode->pdo;
331         i++;
332 
333         le = le->Flink;
334     }
335 
336     Irp->IoStatus.Information = (ULONG_PTR)dr;
337 
338     Status = STATUS_SUCCESS;
339 
340 end:
341     ExReleaseResourceLite(&pdo_list_lock);
342 
343     Irp->IoStatus.Status = Status;
344     IoCompleteRequest(Irp, IO_NO_INCREMENT);
345 
346     return Status;
347 }
348 
349 static NTSTATUS bus_query_hardware_ids(PIRP Irp) {
350     WCHAR* out;
351 
352     static WCHAR ids[] = L"ROOT\\btrfs\0";
353 
354     out = ExAllocatePoolWithTag(PagedPool, sizeof(ids), ALLOC_TAG);
355     if (!out) {
356         ERR("out of memory\n");
357         return STATUS_INSUFFICIENT_RESOURCES;
358     }
359 
360     RtlCopyMemory(out, ids, sizeof(ids));
361 
362     Irp->IoStatus.Information = (ULONG_PTR)out;
363 
364     return STATUS_SUCCESS;
365 }
366 
367 static NTSTATUS bus_pnp(control_device_extension* cde, PIRP Irp) {
368     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
369 
370     switch (IrpSp->MinorFunction) {
371         case IRP_MN_QUERY_CAPABILITIES:
372             bus_query_capabilities(Irp);
373             break;
374 
375         case IRP_MN_QUERY_DEVICE_RELATIONS:
376             if (IrpSp->Parameters.QueryDeviceRelations.Type != BusRelations || no_pnp)
377                 break;
378 
379             return bus_query_device_relations(Irp);
380 
381         case IRP_MN_QUERY_ID:
382         {
383             NTSTATUS Status;
384 
385             if (IrpSp->Parameters.QueryId.IdType != BusQueryHardwareIDs)
386                 break;
387 
388             Status = bus_query_hardware_ids(Irp);
389 
390             Irp->IoStatus.Status = Status;
391             IoCompleteRequest(Irp, IO_NO_INCREMENT);
392 
393             return Status;
394         }
395     }
396 
397     IoSkipCurrentIrpStackLocation(Irp);
398     return IoCallDriver(cde->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 WCHAR pref[] = L"Btrfs\\";
406 
407     RtlCopyMemory(name, pref, wcslen(pref) * sizeof(WCHAR));
408 
409     noff = &name[wcslen(pref)];
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 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 static NTSTATUS pdo_pnp(PDEVICE_OBJECT pdo, PIRP Irp) {
472     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
473     pdo_device_extension* pdode = pdo->DeviceExtension;
474 
475     switch (IrpSp->MinorFunction) {
476         case IRP_MN_QUERY_ID:
477             return pdo_query_id(pdode, Irp);
478 
479         case IRP_MN_START_DEVICE:
480         case IRP_MN_CANCEL_REMOVE_DEVICE:
481         case IRP_MN_SURPRISE_REMOVAL:
482         case IRP_MN_REMOVE_DEVICE:
483             return STATUS_SUCCESS;
484 
485         case IRP_MN_QUERY_REMOVE_DEVICE:
486             return STATUS_UNSUCCESSFUL;
487     }
488 
489     return Irp->IoStatus.Status;
490 }
491 
492 _Dispatch_type_(IRP_MJ_PNP)
493 _Function_class_(DRIVER_DISPATCH)
494 NTSTATUS drv_pnp(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
495     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
496     device_extension* Vcb = DeviceObject->DeviceExtension;
497     NTSTATUS Status;
498     BOOL top_level;
499 
500     FsRtlEnterFileSystem();
501 
502     top_level = is_top_level(Irp);
503 
504     if (Vcb && Vcb->type == VCB_TYPE_CONTROL) {
505         Status = bus_pnp(DeviceObject->DeviceExtension, Irp);
506         goto exit;
507     } else if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
508         volume_device_extension* vde = DeviceObject->DeviceExtension;
509         IoSkipCurrentIrpStackLocation(Irp);
510         Status = IoCallDriver(vde->pdo, Irp);
511         goto exit;
512     } else if (Vcb && Vcb->type == VCB_TYPE_PDO) {
513         Status = pdo_pnp(DeviceObject, Irp);
514         goto end;
515     } else if (!Vcb || Vcb->type != VCB_TYPE_FS) {
516         Status = STATUS_INVALID_PARAMETER;
517         goto end;
518     }
519 
520     Status = STATUS_NOT_IMPLEMENTED;
521 
522     switch (IrpSp->MinorFunction) {
523         case IRP_MN_CANCEL_REMOVE_DEVICE:
524             Status = pnp_cancel_remove_device(DeviceObject);
525             break;
526 
527         case IRP_MN_QUERY_REMOVE_DEVICE:
528             Status = pnp_query_remove_device(DeviceObject, Irp);
529             break;
530 
531         case IRP_MN_REMOVE_DEVICE:
532             Status = pnp_remove_device(DeviceObject);
533             break;
534 
535         case IRP_MN_SURPRISE_REMOVAL:
536             Status = pnp_surprise_removal(DeviceObject, Irp);
537             break;
538 
539         default:
540             TRACE("passing minor function 0x%x on\n", IrpSp->MinorFunction);
541 
542             IoSkipCurrentIrpStackLocation(Irp);
543             Status = IoCallDriver(Vcb->Vpb->RealDevice, Irp);
544             goto exit;
545     }
546 
547 end:
548     Irp->IoStatus.Status = Status;
549 
550     IoCompleteRequest(Irp, IO_NO_INCREMENT);
551 
552 exit:
553     TRACE("returning %08x\n", Status);
554 
555     if (top_level)
556         IoSetTopLevelIrp(NULL);
557 
558     FsRtlExitFileSystem();
559 
560     return Status;
561 }
562