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