34997cfd5925f1cb95b07428ba33b9fd56d7fc84
[reactos.git] / drivers / filesystems / btrfs / pnp.c
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 }