1 /* Copyright (c) Mark Harmstone 2016-17
3 * This file is part of WinBtrfs.
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.
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.
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/>. */
18 #include "btrfs_drv.h"
23 struct pnp_context
* context
;
37 extern ERESOURCE pdo_list_lock
;
38 extern LIST_ENTRY pdo_list
;
40 _Function_class_(IO_COMPLETION_ROUTINE
)
42 static NTSTATUS NTAPI
pnp_completion(PDEVICE_OBJECT DeviceObject
, PIRP Irp
, PVOID conptr
) {
44 static NTSTATUS
pnp_completion(PDEVICE_OBJECT DeviceObject
, PIRP Irp
, PVOID conptr
) {
46 pnp_stripe
* stripe
= conptr
;
47 pnp_context
* context
= (pnp_context
*)stripe
->context
;
51 stripe
->Status
= Irp
->IoStatus
.Status
;
53 InterlockedDecrement(&context
->left
);
55 if (context
->left
== 0)
56 KeSetEvent(&context
->Event
, 0, FALSE
);
58 return STATUS_MORE_PROCESSING_REQUIRED
;
61 static NTSTATUS
send_disks_pnp_message(device_extension
* Vcb
, UCHAR minor
) {
67 RtlZeroMemory(&context
, sizeof(pnp_context
));
68 KeInitializeEvent(&context
.Event
, NotificationEvent
, FALSE
);
70 num_devices
= (ULONG
)min(0xffffffff, Vcb
->superblock
.num_devices
);
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
;
78 RtlZeroMemory(context
.stripes
, sizeof(pnp_stripe
) * num_devices
);
81 le
= Vcb
->devices
.Flink
;
83 while (le
!= &Vcb
->devices
) {
84 PIO_STACK_LOCATION IrpSp
;
85 device
* dev
= CONTAINING_RECORD(le
, device
, list_entry
);
88 context
.stripes
[i
].context
= (struct pnp_context
*)&context
;
90 context
.stripes
[i
].Irp
= IoAllocateIrp(dev
->devobj
->StackSize
, FALSE
);
92 if (!context
.stripes
[i
].Irp
) {
95 ERR("IoAllocateIrp failed\n");
97 for (j
= 0; j
< i
; j
++) {
98 if (context
.stripes
[j
].dev
->devobj
) {
99 IoFreeIrp(context
.stripes
[j
].Irp
);
102 ExFreePool(context
.stripes
);
104 return STATUS_INSUFFICIENT_RESOURCES
;
107 IrpSp
= IoGetNextIrpStackLocation(context
.stripes
[i
].Irp
);
108 IrpSp
->MajorFunction
= IRP_MJ_PNP
;
109 IrpSp
->MinorFunction
= minor
;
111 context
.stripes
[i
].Irp
->UserIosb
= &context
.stripes
[i
].iosb
;
113 IoSetCompletionRoutine(context
.stripes
[i
].Irp
, pnp_completion
, &context
.stripes
[i
], TRUE
, TRUE
, TRUE
);
115 context
.stripes
[i
].Irp
->IoStatus
.Status
= STATUS_NOT_SUPPORTED
;
116 context
.stripes
[i
].dev
= dev
;
124 if (context
.left
== 0) {
125 Status
= STATUS_SUCCESS
;
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
);
135 KeWaitForSingleObject(&context
.Event
, Executive
, KernelMode
, FALSE
, NULL
);
137 Status
= STATUS_SUCCESS
;
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
;
147 for (i
= 0; i
< num_devices
; i
++) {
148 if (context
.stripes
[i
].Irp
) {
149 IoFreeIrp(context
.stripes
[i
].Irp
);
153 ExFreePool(context
.stripes
);
158 static NTSTATUS
pnp_cancel_remove_device(PDEVICE_OBJECT DeviceObject
) {
159 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
162 ExAcquireResourceSharedLite(&Vcb
->tree_lock
, TRUE
);
164 ExAcquireResourceExclusiveLite(&Vcb
->fileref_lock
, TRUE
);
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
;
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
);
178 ExReleaseResourceLite(&Vcb
->fileref_lock
);
179 ExReleaseResourceLite(&Vcb
->tree_lock
);
181 return STATUS_SUCCESS
;
184 NTSTATUS
pnp_query_remove_device(PDEVICE_OBJECT DeviceObject
, PIRP Irp
) {
185 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
188 ExAcquireResourceExclusiveLite(&Vcb
->tree_lock
, TRUE
);
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
;
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
);
201 Vcb
->removing
= TRUE
;
203 if (Vcb
->need_write
&& !Vcb
->readonly
) {
204 Status
= do_write(Vcb
, Irp
);
208 if (!NT_SUCCESS(Status
)) {
209 ERR("do_write returned %08x\n", Status
);
215 Status
= STATUS_SUCCESS
;
217 ExReleaseResourceLite(&Vcb
->tree_lock
);
222 static NTSTATUS
pnp_remove_device(PDEVICE_OBJECT DeviceObject
) {
223 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
226 ExAcquireResourceSharedLite(&Vcb
->tree_lock
, TRUE
);
228 Status
= send_disks_pnp_message(Vcb
, IRP_MN_REMOVE_DEVICE
);
230 if (!NT_SUCCESS(Status
))
231 WARN("send_disks_pnp_message returned %08x\n", Status
);
233 ExReleaseResourceLite(&Vcb
->tree_lock
);
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
);
242 Vcb
->vde
->mounted_device
= NULL
;
244 ExAcquireResourceExclusiveLite(&Vcb
->tree_lock
, TRUE
);
245 Vcb
->removing
= TRUE
;
246 ExReleaseResourceLite(&Vcb
->tree_lock
);
248 if (Vcb
->open_files
== 0)
252 return STATUS_SUCCESS
;
255 NTSTATUS
pnp_surprise_removal(PDEVICE_OBJECT DeviceObject
, PIRP Irp
) {
256 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
258 TRACE("(%p, %p)\n", DeviceObject
, Irp
);
260 if (DeviceObject
->Vpb
->Flags
& VPB_MOUNTED
) {
261 ExAcquireResourceExclusiveLite(&Vcb
->tree_lock
, TRUE
);
264 Vcb
->vde
->mounted_device
= NULL
;
266 Vcb
->removing
= TRUE
;
268 ExReleaseResourceLite(&Vcb
->tree_lock
);
270 if (Vcb
->open_files
== 0)
274 return STATUS_SUCCESS
;
277 static void bus_query_capabilities(PIRP Irp
) {
278 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
279 PDEVICE_CAPABILITIES dc
= IrpSp
->Parameters
.DeviceCapabilities
.Capabilities
;
282 dc
->SilentInstall
= TRUE
;
284 Irp
->IoStatus
.Status
= STATUS_SUCCESS
;
287 static NTSTATUS
bus_query_device_relations(PIRP Irp
) {
292 DEVICE_RELATIONS
* dr
;
294 ExAcquireResourceSharedLite(&pdo_list_lock
, TRUE
);
299 while (le
!= &pdo_list
) {
305 drsize
= offsetof(DEVICE_RELATIONS
, Objects
[0]) + (num_children
* sizeof(PDEVICE_OBJECT
));
306 dr
= ExAllocatePoolWithTag(PagedPool
, drsize
, ALLOC_TAG
);
309 ERR("out of memory\n");
310 Status
= STATUS_INSUFFICIENT_RESOURCES
;
314 dr
->Count
= num_children
;
318 while (le
!= &pdo_list
) {
319 pdo_device_extension
* pdode
= CONTAINING_RECORD(le
, pdo_device_extension
, list_entry
);
321 ObReferenceObject(pdode
->pdo
);
322 dr
->Objects
[i
] = pdode
->pdo
;
328 Irp
->IoStatus
.Information
= (ULONG_PTR
)dr
;
330 Status
= STATUS_SUCCESS
;
333 ExReleaseResourceLite(&pdo_list_lock
);
335 Irp
->IoStatus
.Status
= Status
;
336 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
341 static NTSTATUS
bus_query_hardware_ids(PIRP Irp
) {
344 static const WCHAR ids
[] = L
"ROOT\\btrfs\0";
346 out
= ExAllocatePoolWithTag(PagedPool
, sizeof(ids
), ALLOC_TAG
);
348 ERR("out of memory\n");
349 return STATUS_INSUFFICIENT_RESOURCES
;
352 RtlCopyMemory(out
, ids
, sizeof(ids
));
354 Irp
->IoStatus
.Information
= (ULONG_PTR
)out
;
356 return STATUS_SUCCESS
;
359 static NTSTATUS
bus_pnp(control_device_extension
* cde
, PIRP Irp
) {
360 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
362 switch (IrpSp
->MinorFunction
) {
363 case IRP_MN_QUERY_CAPABILITIES
:
364 bus_query_capabilities(Irp
);
367 case IRP_MN_QUERY_DEVICE_RELATIONS
:
368 if (IrpSp
->Parameters
.QueryDeviceRelations
.Type
!= BusRelations
|| no_pnp
)
371 return bus_query_device_relations(Irp
);
373 case IRP_MN_QUERY_ID
:
377 if (IrpSp
->Parameters
.QueryId
.IdType
!= BusQueryHardwareIDs
)
380 Status
= bus_query_hardware_ids(Irp
);
382 Irp
->IoStatus
.Status
= Status
;
383 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
389 IoSkipCurrentIrpStackLocation(Irp
);
390 return IoCallDriver(cde
->attached_device
, Irp
);
393 static NTSTATUS
pdo_query_device_id(pdo_device_extension
* pdode
, PIRP Irp
) {
394 WCHAR name
[100], *noff
, *out
;
397 static const WCHAR pref
[] = L
"Btrfs\\";
399 RtlCopyMemory(name
, pref
, sizeof(pref
) - sizeof(WCHAR
));
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
++;
406 if (i
== 3 || i
== 5 || i
== 7 || i
== 9) {
413 out
= ExAllocatePoolWithTag(PagedPool
, (wcslen(name
) + 1) * sizeof(WCHAR
), ALLOC_TAG
);
415 ERR("out of memory\n");
416 return STATUS_INSUFFICIENT_RESOURCES
;
419 RtlCopyMemory(out
, name
, (wcslen(name
) + 1) * sizeof(WCHAR
));
421 Irp
->IoStatus
.Information
= (ULONG_PTR
)out
;
423 return STATUS_SUCCESS
;
426 static NTSTATUS
pdo_query_hardware_ids(PIRP Irp
) {
429 static const WCHAR ids
[] = L
"BtrfsVolume\0";
431 out
= ExAllocatePoolWithTag(PagedPool
, sizeof(ids
), ALLOC_TAG
);
433 ERR("out of memory\n");
434 return STATUS_INSUFFICIENT_RESOURCES
;
437 RtlCopyMemory(out
, ids
, sizeof(ids
));
439 Irp
->IoStatus
.Information
= (ULONG_PTR
)out
;
441 return STATUS_SUCCESS
;
444 static NTSTATUS
pdo_query_id(pdo_device_extension
* pdode
, PIRP Irp
) {
445 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
447 switch (IrpSp
->Parameters
.QueryId
.IdType
) {
448 case BusQueryDeviceID
:
449 TRACE("BusQueryDeviceID\n");
450 return pdo_query_device_id(pdode
, Irp
);
452 case BusQueryHardwareIDs
:
453 TRACE("BusQueryHardwareIDs\n");
454 return pdo_query_hardware_ids(Irp
);
460 return Irp
->IoStatus
.Status
;
463 static NTSTATUS
pdo_pnp(PDEVICE_OBJECT pdo
, PIRP Irp
) {
464 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
465 pdo_device_extension
* pdode
= pdo
->DeviceExtension
;
467 switch (IrpSp
->MinorFunction
) {
468 case IRP_MN_QUERY_ID
:
469 return pdo_query_id(pdode
, Irp
);
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
;
477 case IRP_MN_QUERY_REMOVE_DEVICE
:
478 return STATUS_UNSUCCESSFUL
;
481 return Irp
->IoStatus
.Status
;
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
;
492 FsRtlEnterFileSystem();
494 top_level
= is_top_level(Irp
);
496 if (Vcb
&& Vcb
->type
== VCB_TYPE_CONTROL
) {
497 Status
= bus_pnp(DeviceObject
->DeviceExtension
, Irp
);
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
);
504 } else if (Vcb
&& Vcb
->type
== VCB_TYPE_PDO
) {
505 Status
= pdo_pnp(DeviceObject
, Irp
);
507 } else if (!Vcb
|| Vcb
->type
!= VCB_TYPE_FS
) {
508 Status
= STATUS_INVALID_PARAMETER
;
512 Status
= STATUS_NOT_IMPLEMENTED
;
514 switch (IrpSp
->MinorFunction
) {
515 case IRP_MN_CANCEL_REMOVE_DEVICE
:
516 Status
= pnp_cancel_remove_device(DeviceObject
);
519 case IRP_MN_QUERY_REMOVE_DEVICE
:
520 Status
= pnp_query_remove_device(DeviceObject
, Irp
);
523 case IRP_MN_REMOVE_DEVICE
:
524 Status
= pnp_remove_device(DeviceObject
);
527 case IRP_MN_SURPRISE_REMOVAL
:
528 Status
= pnp_surprise_removal(DeviceObject
, Irp
);
532 TRACE("passing minor function 0x%x on\n", IrpSp
->MinorFunction
);
534 IoSkipCurrentIrpStackLocation(Irp
);
535 Status
= IoCallDriver(Vcb
->Vpb
->RealDevice
, Irp
);
540 Irp
->IoStatus
.Status
= Status
;
542 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
545 TRACE("returning %08x\n", Status
);
548 IoSetTopLevelIrp(NULL
);
550 FsRtlExitFileSystem();