[BTRFS] Upgrade to 1.2.1
[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 ExAcquireResourceExclusiveLite(&Vcb->fileref_lock, TRUE);
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 ExReleaseResourceLite(&Vcb->fileref_lock);
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 if (Vcb->root_fileref && Vcb->root_fileref->fcb && (Vcb->root_fileref->open_count > 0 || has_open_children(Vcb->root_fileref))) {
191 Status = STATUS_ACCESS_DENIED;
192 goto end;
193 }
194
195 Status = send_disks_pnp_message(Vcb, IRP_MN_QUERY_REMOVE_DEVICE);
196 if (!NT_SUCCESS(Status)) {
197 WARN("send_disks_pnp_message returned %08x\n", Status);
198 goto end;
199 }
200
201 Vcb->removing = TRUE;
202
203 if (Vcb->need_write && !Vcb->readonly) {
204 Status = do_write(Vcb, Irp);
205
206 free_trees(Vcb);
207
208 if (!NT_SUCCESS(Status)) {
209 ERR("do_write returned %08x\n", Status);
210 goto end;
211 }
212 }
213
214
215 Status = STATUS_SUCCESS;
216 end:
217 ExReleaseResourceLite(&Vcb->tree_lock);
218
219 return Status;
220 }
221
222 static NTSTATUS pnp_remove_device(PDEVICE_OBJECT DeviceObject) {
223 device_extension* Vcb = DeviceObject->DeviceExtension;
224 NTSTATUS Status;
225
226 ExAcquireResourceSharedLite(&Vcb->tree_lock, TRUE);
227
228 Status = send_disks_pnp_message(Vcb, IRP_MN_REMOVE_DEVICE);
229
230 if (!NT_SUCCESS(Status))
231 WARN("send_disks_pnp_message returned %08x\n", Status);
232
233 ExReleaseResourceLite(&Vcb->tree_lock);
234
235 if (DeviceObject->Vpb->Flags & VPB_MOUNTED) {
236 Status = FsRtlNotifyVolumeEvent(Vcb->root_file, FSRTL_VOLUME_DISMOUNT);
237 if (!NT_SUCCESS(Status)) {
238 WARN("FsRtlNotifyVolumeEvent returned %08x\n", Status);
239 }
240
241 if (Vcb->vde)
242 Vcb->vde->mounted_device = NULL;
243
244 ExAcquireResourceExclusiveLite(&Vcb->tree_lock, TRUE);
245 Vcb->removing = TRUE;
246 ExReleaseResourceLite(&Vcb->tree_lock);
247
248 if (Vcb->open_files == 0)
249 uninit(Vcb);
250 }
251
252 return STATUS_SUCCESS;
253 }
254
255 NTSTATUS pnp_surprise_removal(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
256 device_extension* Vcb = DeviceObject->DeviceExtension;
257
258 TRACE("(%p, %p)\n", DeviceObject, Irp);
259
260 if (DeviceObject->Vpb->Flags & VPB_MOUNTED) {
261 ExAcquireResourceExclusiveLite(&Vcb->tree_lock, TRUE);
262
263 if (Vcb->vde)
264 Vcb->vde->mounted_device = NULL;
265
266 Vcb->removing = TRUE;
267
268 ExReleaseResourceLite(&Vcb->tree_lock);
269
270 if (Vcb->open_files == 0)
271 uninit(Vcb);
272 }
273
274 return STATUS_SUCCESS;
275 }
276
277 static void bus_query_capabilities(PIRP Irp) {
278 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
279 PDEVICE_CAPABILITIES dc = IrpSp->Parameters.DeviceCapabilities.Capabilities;
280
281 dc->UniqueID = TRUE;
282 dc->SilentInstall = TRUE;
283
284 Irp->IoStatus.Status = STATUS_SUCCESS;
285 }
286
287 static NTSTATUS bus_query_device_relations(PIRP Irp) {
288 NTSTATUS Status;
289 ULONG num_children;
290 LIST_ENTRY* le;
291 ULONG drsize, i;
292 DEVICE_RELATIONS* dr;
293
294 ExAcquireResourceSharedLite(&pdo_list_lock, TRUE);
295
296 num_children = 0;
297
298 le = pdo_list.Flink;
299 while (le != &pdo_list) {
300 num_children++;
301
302 le = le->Flink;
303 }
304
305 drsize = offsetof(DEVICE_RELATIONS, Objects[0]) + (num_children * sizeof(PDEVICE_OBJECT));
306 dr = ExAllocatePoolWithTag(PagedPool, drsize, ALLOC_TAG);
307
308 if (!dr) {
309 ERR("out of memory\n");
310 Status = STATUS_INSUFFICIENT_RESOURCES;
311 goto end;
312 }
313
314 dr->Count = num_children;
315
316 i = 0;
317 le = pdo_list.Flink;
318 while (le != &pdo_list) {
319 pdo_device_extension* pdode = CONTAINING_RECORD(le, pdo_device_extension, list_entry);
320
321 ObReferenceObject(pdode->pdo);
322 dr->Objects[i] = pdode->pdo;
323 i++;
324
325 le = le->Flink;
326 }
327
328 Irp->IoStatus.Information = (ULONG_PTR)dr;
329
330 Status = STATUS_SUCCESS;
331
332 end:
333 ExReleaseResourceLite(&pdo_list_lock);
334
335 Irp->IoStatus.Status = Status;
336 IoCompleteRequest(Irp, IO_NO_INCREMENT);
337
338 return Status;
339 }
340
341 static NTSTATUS bus_query_hardware_ids(PIRP Irp) {
342 WCHAR* out;
343
344 static const WCHAR ids[] = L"ROOT\\btrfs\0";
345
346 out = ExAllocatePoolWithTag(PagedPool, sizeof(ids), ALLOC_TAG);
347 if (!out) {
348 ERR("out of memory\n");
349 return STATUS_INSUFFICIENT_RESOURCES;
350 }
351
352 RtlCopyMemory(out, ids, sizeof(ids));
353
354 Irp->IoStatus.Information = (ULONG_PTR)out;
355
356 return STATUS_SUCCESS;
357 }
358
359 static NTSTATUS bus_pnp(control_device_extension* cde, PIRP Irp) {
360 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
361
362 switch (IrpSp->MinorFunction) {
363 case IRP_MN_QUERY_CAPABILITIES:
364 bus_query_capabilities(Irp);
365 break;
366
367 case IRP_MN_QUERY_DEVICE_RELATIONS:
368 if (IrpSp->Parameters.QueryDeviceRelations.Type != BusRelations || no_pnp)
369 break;
370
371 return bus_query_device_relations(Irp);
372
373 case IRP_MN_QUERY_ID:
374 {
375 NTSTATUS Status;
376
377 if (IrpSp->Parameters.QueryId.IdType != BusQueryHardwareIDs)
378 break;
379
380 Status = bus_query_hardware_ids(Irp);
381
382 Irp->IoStatus.Status = Status;
383 IoCompleteRequest(Irp, IO_NO_INCREMENT);
384
385 return Status;
386 }
387 }
388
389 IoSkipCurrentIrpStackLocation(Irp);
390 return IoCallDriver(cde->attached_device, Irp);
391 }
392
393 static NTSTATUS pdo_query_device_id(pdo_device_extension* pdode, PIRP Irp) {
394 WCHAR name[100], *noff, *out;
395 int i;
396
397 static const WCHAR pref[] = L"Btrfs\\";
398
399 RtlCopyMemory(name, pref, sizeof(pref) - sizeof(WCHAR));
400
401 noff = &name[(sizeof(pref) / sizeof(WCHAR)) - 1];
402 for (i = 0; i < 16; i++) {
403 *noff = hex_digit(pdode->uuid.uuid[i] >> 4); noff++;
404 *noff = hex_digit(pdode->uuid.uuid[i] & 0xf); noff++;
405
406 if (i == 3 || i == 5 || i == 7 || i == 9) {
407 *noff = '-';
408 noff++;
409 }
410 }
411 *noff = 0;
412
413 out = ExAllocatePoolWithTag(PagedPool, (wcslen(name) + 1) * sizeof(WCHAR), ALLOC_TAG);
414 if (!out) {
415 ERR("out of memory\n");
416 return STATUS_INSUFFICIENT_RESOURCES;
417 }
418
419 RtlCopyMemory(out, name, (wcslen(name) + 1) * sizeof(WCHAR));
420
421 Irp->IoStatus.Information = (ULONG_PTR)out;
422
423 return STATUS_SUCCESS;
424 }
425
426 static NTSTATUS pdo_query_hardware_ids(PIRP Irp) {
427 WCHAR* out;
428
429 static const WCHAR ids[] = L"BtrfsVolume\0";
430
431 out = ExAllocatePoolWithTag(PagedPool, sizeof(ids), ALLOC_TAG);
432 if (!out) {
433 ERR("out of memory\n");
434 return STATUS_INSUFFICIENT_RESOURCES;
435 }
436
437 RtlCopyMemory(out, ids, sizeof(ids));
438
439 Irp->IoStatus.Information = (ULONG_PTR)out;
440
441 return STATUS_SUCCESS;
442 }
443
444 static NTSTATUS pdo_query_id(pdo_device_extension* pdode, PIRP Irp) {
445 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
446
447 switch (IrpSp->Parameters.QueryId.IdType) {
448 case BusQueryDeviceID:
449 TRACE("BusQueryDeviceID\n");
450 return pdo_query_device_id(pdode, Irp);
451
452 case BusQueryHardwareIDs:
453 TRACE("BusQueryHardwareIDs\n");
454 return pdo_query_hardware_ids(Irp);
455
456 default:
457 break;
458 }
459
460 return Irp->IoStatus.Status;
461 }
462
463 static NTSTATUS pdo_pnp(PDEVICE_OBJECT pdo, PIRP Irp) {
464 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
465 pdo_device_extension* pdode = pdo->DeviceExtension;
466
467 switch (IrpSp->MinorFunction) {
468 case IRP_MN_QUERY_ID:
469 return pdo_query_id(pdode, Irp);
470
471 case IRP_MN_START_DEVICE:
472 case IRP_MN_CANCEL_REMOVE_DEVICE:
473 case IRP_MN_SURPRISE_REMOVAL:
474 case IRP_MN_REMOVE_DEVICE:
475 return STATUS_SUCCESS;
476
477 case IRP_MN_QUERY_REMOVE_DEVICE:
478 return STATUS_UNSUCCESSFUL;
479 }
480
481 return Irp->IoStatus.Status;
482 }
483
484 _Dispatch_type_(IRP_MJ_PNP)
485 _Function_class_(DRIVER_DISPATCH)
486 NTSTATUS NTAPI drv_pnp(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
487 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
488 device_extension* Vcb = DeviceObject->DeviceExtension;
489 NTSTATUS Status;
490 BOOL top_level;
491
492 FsRtlEnterFileSystem();
493
494 top_level = is_top_level(Irp);
495
496 if (Vcb && Vcb->type == VCB_TYPE_CONTROL) {
497 Status = bus_pnp(DeviceObject->DeviceExtension, Irp);
498 goto exit;
499 } else if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
500 volume_device_extension* vde = DeviceObject->DeviceExtension;
501 IoSkipCurrentIrpStackLocation(Irp);
502 Status = IoCallDriver(vde->pdo, Irp);
503 goto exit;
504 } else if (Vcb && Vcb->type == VCB_TYPE_PDO) {
505 Status = pdo_pnp(DeviceObject, Irp);
506 goto end;
507 } else if (!Vcb || Vcb->type != VCB_TYPE_FS) {
508 Status = STATUS_INVALID_PARAMETER;
509 goto end;
510 }
511
512 Status = STATUS_NOT_IMPLEMENTED;
513
514 switch (IrpSp->MinorFunction) {
515 case IRP_MN_CANCEL_REMOVE_DEVICE:
516 Status = pnp_cancel_remove_device(DeviceObject);
517 break;
518
519 case IRP_MN_QUERY_REMOVE_DEVICE:
520 Status = pnp_query_remove_device(DeviceObject, Irp);
521 break;
522
523 case IRP_MN_REMOVE_DEVICE:
524 Status = pnp_remove_device(DeviceObject);
525 break;
526
527 case IRP_MN_SURPRISE_REMOVAL:
528 Status = pnp_surprise_removal(DeviceObject, Irp);
529 break;
530
531 default:
532 TRACE("passing minor function 0x%x on\n", IrpSp->MinorFunction);
533
534 IoSkipCurrentIrpStackLocation(Irp);
535 Status = IoCallDriver(Vcb->Vpb->RealDevice, Irp);
536 goto exit;
537 }
538
539 end:
540 Irp->IoStatus.Status = Status;
541
542 IoCompleteRequest(Irp, IO_NO_INCREMENT);
543
544 exit:
545 TRACE("returning %08x\n", Status);
546
547 if (top_level)
548 IoSetTopLevelIrp(NULL);
549
550 FsRtlExitFileSystem();
551
552 return Status;
553 }