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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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) 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