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
pnp_query_remove_device(PDEVICE_OBJECT DeviceObject,PIRP Irp)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
pnp_remove_device(PDEVICE_OBJECT DeviceObject)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
pnp_surprise_removal(PDEVICE_OBJECT DeviceObject,PIRP Irp)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
bus_query_capabilities(PIRP Irp)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
bus_query_device_relations(PIRP Irp)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
bus_query_hardware_ids(PIRP Irp)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
bus_pnp(bus_device_extension * bde,PIRP Irp)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
pdo_query_device_id(pdo_device_extension * pdode,PIRP Irp)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
pdo_query_hardware_ids(PIRP Irp)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
pdo_query_id(pdo_device_extension * pdode,PIRP Irp)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
_Function_class_(IO_COMPLETION_ROUTINE)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
pdo_device_usage_notification(pdo_device_extension * pdode,PIRP Irp)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
pdo_query_device_relations(PDEVICE_OBJECT pdo,PIRP Irp)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
pdo_pnp(PDEVICE_OBJECT pdo,PIRP Irp)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
pnp_device_usage_notification(PDEVICE_OBJECT DeviceObject,PIRP Irp)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)
_Function_class_(DRIVER_DISPATCH)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