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