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 acquire_fcb_lock_exclusive(Vcb
);
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 release_fcb_lock(Vcb
);
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 acquire_fcb_lock_exclusive(Vcb
);
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
;
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
);
203 Vcb
->removing
= TRUE
;
205 if (Vcb
->need_write
&& !Vcb
->readonly
) {
206 Status
= do_write(Vcb
, Irp
);
210 if (!NT_SUCCESS(Status
)) {
211 ERR("do_write returned %08x\n", Status
);
217 Status
= STATUS_SUCCESS
;
219 release_fcb_lock(Vcb
);
221 ExReleaseResourceLite(&Vcb
->tree_lock
);
226 static NTSTATUS
pnp_remove_device(PDEVICE_OBJECT DeviceObject
) {
227 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
230 ExAcquireResourceSharedLite(&Vcb
->tree_lock
, TRUE
);
232 Status
= send_disks_pnp_message(Vcb
, IRP_MN_REMOVE_DEVICE
);
234 if (!NT_SUCCESS(Status
))
235 WARN("send_disks_pnp_message returned %08x\n", Status
);
237 ExReleaseResourceLite(&Vcb
->tree_lock
);
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
);
246 Vcb
->vde
->mounted_device
= NULL
;
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
);
254 if (Vcb
->open_files
== 0)
258 return STATUS_SUCCESS
;
261 NTSTATUS
pnp_surprise_removal(PDEVICE_OBJECT DeviceObject
, PIRP Irp
) {
262 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
264 TRACE("(%p, %p)\n", DeviceObject
, Irp
);
266 if (DeviceObject
->Vpb
->Flags
& VPB_MOUNTED
) {
267 ExAcquireResourceExclusiveLite(&Vcb
->tree_lock
, TRUE
);
270 Vcb
->vde
->mounted_device
= NULL
;
272 Vcb
->removing
= TRUE
;
273 Vcb
->Vpb
->Flags
&= ~VPB_MOUNTED
;
274 Vcb
->Vpb
->Flags
|= VPB_DIRECT_WRITES_ALLOWED
;
276 ExReleaseResourceLite(&Vcb
->tree_lock
);
278 if (Vcb
->open_files
== 0)
282 return STATUS_SUCCESS
;
285 static void bus_query_capabilities(PIRP Irp
) {
286 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
287 PDEVICE_CAPABILITIES dc
= IrpSp
->Parameters
.DeviceCapabilities
.Capabilities
;
290 dc
->SilentInstall
= TRUE
;
292 Irp
->IoStatus
.Status
= STATUS_SUCCESS
;
295 static NTSTATUS
bus_query_device_relations(PIRP Irp
) {
300 DEVICE_RELATIONS
* dr
;
302 ExAcquireResourceSharedLite(&pdo_list_lock
, TRUE
);
307 while (le
!= &pdo_list
) {
313 drsize
= offsetof(DEVICE_RELATIONS
, Objects
[0]) + (num_children
* sizeof(PDEVICE_OBJECT
));
314 dr
= ExAllocatePoolWithTag(PagedPool
, drsize
, ALLOC_TAG
);
317 ERR("out of memory\n");
318 Status
= STATUS_INSUFFICIENT_RESOURCES
;
322 dr
->Count
= num_children
;
326 while (le
!= &pdo_list
) {
327 pdo_device_extension
* pdode
= CONTAINING_RECORD(le
, pdo_device_extension
, list_entry
);
329 ObReferenceObject(pdode
->pdo
);
330 dr
->Objects
[i
] = pdode
->pdo
;
336 Irp
->IoStatus
.Information
= (ULONG_PTR
)dr
;
338 Status
= STATUS_SUCCESS
;
341 ExReleaseResourceLite(&pdo_list_lock
);
343 Irp
->IoStatus
.Status
= Status
;
344 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
349 static NTSTATUS
bus_query_hardware_ids(PIRP Irp
) {
352 static WCHAR ids
[] = L
"ROOT\\btrfs\0";
354 out
= ExAllocatePoolWithTag(PagedPool
, sizeof(ids
), ALLOC_TAG
);
356 ERR("out of memory\n");
357 return STATUS_INSUFFICIENT_RESOURCES
;
360 RtlCopyMemory(out
, ids
, sizeof(ids
));
362 Irp
->IoStatus
.Information
= (ULONG_PTR
)out
;
364 return STATUS_SUCCESS
;
367 static NTSTATUS
bus_pnp(control_device_extension
* cde
, PIRP Irp
) {
368 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
370 switch (IrpSp
->MinorFunction
) {
371 case IRP_MN_QUERY_CAPABILITIES
:
372 bus_query_capabilities(Irp
);
375 case IRP_MN_QUERY_DEVICE_RELATIONS
:
376 if (IrpSp
->Parameters
.QueryDeviceRelations
.Type
!= BusRelations
|| no_pnp
)
379 return bus_query_device_relations(Irp
);
381 case IRP_MN_QUERY_ID
:
385 if (IrpSp
->Parameters
.QueryId
.IdType
!= BusQueryHardwareIDs
)
388 Status
= bus_query_hardware_ids(Irp
);
390 Irp
->IoStatus
.Status
= Status
;
391 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
397 IoSkipCurrentIrpStackLocation(Irp
);
398 return IoCallDriver(cde
->attached_device
, Irp
);
401 static NTSTATUS
pdo_query_device_id(pdo_device_extension
* pdode
, PIRP Irp
) {
402 WCHAR name
[100], *noff
, *out
;
405 static WCHAR pref
[] = L
"Btrfs\\";
407 RtlCopyMemory(name
, pref
, wcslen(pref
) * sizeof(WCHAR
));
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
++;
414 if (i
== 3 || i
== 5 || i
== 7 || i
== 9) {
421 out
= ExAllocatePoolWithTag(PagedPool
, (wcslen(name
) + 1) * sizeof(WCHAR
), ALLOC_TAG
);
423 ERR("out of memory\n");
424 return STATUS_INSUFFICIENT_RESOURCES
;
427 RtlCopyMemory(out
, name
, (wcslen(name
) + 1) * sizeof(WCHAR
));
429 Irp
->IoStatus
.Information
= (ULONG_PTR
)out
;
431 return STATUS_SUCCESS
;
434 static NTSTATUS
pdo_query_hardware_ids(PIRP Irp
) {
437 static WCHAR ids
[] = L
"BtrfsVolume\0";
439 out
= ExAllocatePoolWithTag(PagedPool
, sizeof(ids
), ALLOC_TAG
);
441 ERR("out of memory\n");
442 return STATUS_INSUFFICIENT_RESOURCES
;
445 RtlCopyMemory(out
, ids
, sizeof(ids
));
447 Irp
->IoStatus
.Information
= (ULONG_PTR
)out
;
449 return STATUS_SUCCESS
;
452 static NTSTATUS
pdo_query_id(pdo_device_extension
* pdode
, PIRP Irp
) {
453 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
455 switch (IrpSp
->Parameters
.QueryId
.IdType
) {
456 case BusQueryDeviceID
:
457 TRACE("BusQueryDeviceID\n");
458 return pdo_query_device_id(pdode
, Irp
);
460 case BusQueryHardwareIDs
:
461 TRACE("BusQueryHardwareIDs\n");
462 return pdo_query_hardware_ids(Irp
);
468 return Irp
->IoStatus
.Status
;
471 static NTSTATUS
pdo_pnp(PDEVICE_OBJECT pdo
, PIRP Irp
) {
472 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
473 pdo_device_extension
* pdode
= pdo
->DeviceExtension
;
475 switch (IrpSp
->MinorFunction
) {
476 case IRP_MN_QUERY_ID
:
477 return pdo_query_id(pdode
, Irp
);
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
;
485 case IRP_MN_QUERY_REMOVE_DEVICE
:
486 return STATUS_UNSUCCESSFUL
;
489 return Irp
->IoStatus
.Status
;
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
;
500 FsRtlEnterFileSystem();
502 top_level
= is_top_level(Irp
);
504 if (Vcb
&& Vcb
->type
== VCB_TYPE_CONTROL
) {
505 Status
= bus_pnp(DeviceObject
->DeviceExtension
, Irp
);
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
);
512 } else if (Vcb
&& Vcb
->type
== VCB_TYPE_PDO
) {
513 Status
= pdo_pnp(DeviceObject
, Irp
);
515 } else if (!Vcb
|| Vcb
->type
!= VCB_TYPE_FS
) {
516 Status
= STATUS_INVALID_PARAMETER
;
520 Status
= STATUS_NOT_IMPLEMENTED
;
522 switch (IrpSp
->MinorFunction
) {
523 case IRP_MN_CANCEL_REMOVE_DEVICE
:
524 Status
= pnp_cancel_remove_device(DeviceObject
);
527 case IRP_MN_QUERY_REMOVE_DEVICE
:
528 Status
= pnp_query_remove_device(DeviceObject
, Irp
);
531 case IRP_MN_REMOVE_DEVICE
:
532 Status
= pnp_remove_device(DeviceObject
);
535 case IRP_MN_SURPRISE_REMOVAL
:
536 Status
= pnp_surprise_removal(DeviceObject
, Irp
);
540 TRACE("passing minor function 0x%x on\n", IrpSp
->MinorFunction
);
542 IoSkipCurrentIrpStackLocation(Irp
);
543 Status
= IoCallDriver(Vcb
->Vpb
->RealDevice
, Irp
);
548 Irp
->IoStatus
.Status
= Status
;
550 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
553 TRACE("returning %08x\n", Status
);
556 IoSetTopLevelIrp(NULL
);
558 FsRtlExitFileSystem();