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 #include <mountdev.h>
20 #include <ntddvol.h>
21 #include <ntddstor.h>
22 #include <ntdddisk.h>
23 #include <wdmguid.h>
24
25 #define IOCTL_VOLUME_IS_DYNAMIC CTL_CODE(IOCTL_VOLUME_BASE, 18, METHOD_BUFFERED, FILE_ANY_ACCESS)
26 #define IOCTL_VOLUME_POST_ONLINE CTL_CODE(IOCTL_VOLUME_BASE, 25, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
27
28 extern PDRIVER_OBJECT drvobj;
29 extern PDEVICE_OBJECT master_devobj;
30 extern PDEVICE_OBJECT busobj;
31 extern ERESOURCE pdo_list_lock;
32 extern LIST_ENTRY pdo_list;
33 extern UNICODE_STRING registry_path;
34 extern tIoUnregisterPlugPlayNotificationEx fIoUnregisterPlugPlayNotificationEx;
35
vol_create(IN PDEVICE_OBJECT DeviceObject,IN PIRP Irp)36 NTSTATUS vol_create(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
37 volume_device_extension* vde = DeviceObject->DeviceExtension;
38
39 TRACE("(%p, %p)\n", DeviceObject, Irp);
40
41 if (vde->removing)
42 return STATUS_DEVICE_NOT_READY;
43
44 Irp->IoStatus.Information = FILE_OPENED;
45 InterlockedIncrement(&vde->open_count);
46
47 return STATUS_SUCCESS;
48 }
49
free_vol(volume_device_extension * vde)50 void free_vol(volume_device_extension* vde) {
51 PDEVICE_OBJECT pdo;
52
53 vde->dead = true;
54
55 if (vde->mounted_device) {
56 device_extension* Vcb = vde->mounted_device->DeviceExtension;
57
58 Vcb->vde = NULL;
59 }
60
61 if (vde->name.Buffer)
62 ExFreePool(vde->name.Buffer);
63
64 ExDeleteResourceLite(&vde->pdode->child_lock);
65
66 if (vde->pdo->AttachedDevice)
67 IoDetachDevice(vde->pdo);
68
69 while (!IsListEmpty(&vde->pdode->children)) {
70 volume_child* vc = CONTAINING_RECORD(RemoveHeadList(&vde->pdode->children), volume_child, list_entry);
71
72 if (vc->notification_entry) {
73 if (fIoUnregisterPlugPlayNotificationEx)
74 fIoUnregisterPlugPlayNotificationEx(vc->notification_entry);
75 else
76 IoUnregisterPlugPlayNotification(vc->notification_entry);
77 }
78
79 if (vc->pnp_name.Buffer)
80 ExFreePool(vc->pnp_name.Buffer);
81
82 ExFreePool(vc);
83 }
84
85 if (no_pnp)
86 ExFreePool(vde->pdode);
87
88 pdo = vde->pdo;
89 IoDeleteDevice(vde->device);
90
91 if (!no_pnp)
92 IoDeleteDevice(pdo);
93 }
94
vol_close(IN PDEVICE_OBJECT DeviceObject,IN PIRP Irp)95 NTSTATUS vol_close(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
96 volume_device_extension* vde = DeviceObject->DeviceExtension;
97 pdo_device_extension* pdode = vde->pdode;
98
99 TRACE("(%p, %p)\n", DeviceObject, Irp);
100
101 Irp->IoStatus.Information = 0;
102
103 if (vde->dead)
104 return STATUS_SUCCESS;
105
106 ExAcquireResourceExclusiveLite(&pdo_list_lock, true);
107
108 if (vde->dead) {
109 ExReleaseResourceLite(&pdo_list_lock);
110 return STATUS_SUCCESS;
111 }
112
113 ExAcquireResourceSharedLite(&pdode->child_lock, true);
114
115 if (InterlockedDecrement(&vde->open_count) == 0 && vde->removing) {
116 ExReleaseResourceLite(&pdode->child_lock);
117
118 free_vol(vde);
119 } else
120 ExReleaseResourceLite(&pdode->child_lock);
121
122 ExReleaseResourceLite(&pdo_list_lock);
123
124 return STATUS_SUCCESS;
125 }
126
127 typedef struct {
128 IO_STATUS_BLOCK iosb;
129 KEVENT Event;
130 } vol_read_context;
131
_Function_class_(IO_COMPLETION_ROUTINE)132 _Function_class_(IO_COMPLETION_ROUTINE)
133 static NTSTATUS __stdcall vol_read_completion(PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID conptr) {
134 vol_read_context* context = conptr;
135
136 UNUSED(DeviceObject);
137
138 context->iosb = Irp->IoStatus;
139 KeSetEvent(&context->Event, 0, false);
140
141 return STATUS_MORE_PROCESSING_REQUIRED;
142 }
143
vol_read(IN PDEVICE_OBJECT DeviceObject,IN PIRP Irp)144 NTSTATUS vol_read(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
145 volume_device_extension* vde = DeviceObject->DeviceExtension;
146 pdo_device_extension* pdode = vde->pdode;
147 volume_child* vc;
148 NTSTATUS Status;
149 PIRP Irp2;
150 vol_read_context context;
151 PIO_STACK_LOCATION IrpSp, IrpSp2;
152
153 TRACE("(%p, %p)\n", DeviceObject, Irp);
154
155 ExAcquireResourceSharedLite(&pdode->child_lock, true);
156
157 if (IsListEmpty(&pdode->children)) {
158 ExReleaseResourceLite(&pdode->child_lock);
159 Status = STATUS_INVALID_DEVICE_REQUEST;
160 goto end;
161 }
162
163 vc = CONTAINING_RECORD(pdode->children.Flink, volume_child, list_entry);
164
165 // We can't use IoSkipCurrentIrpStackLocation as the device isn't in our stack
166
167 Irp2 = IoAllocateIrp(vc->devobj->StackSize, false);
168
169 if (!Irp2) {
170 ERR("IoAllocateIrp failed\n");
171 ExReleaseResourceLite(&pdode->child_lock);
172 Status = STATUS_INSUFFICIENT_RESOURCES;
173 goto end;
174 }
175
176 IrpSp = IoGetCurrentIrpStackLocation(Irp);
177 IrpSp2 = IoGetNextIrpStackLocation(Irp2);
178
179 IrpSp2->MajorFunction = IRP_MJ_READ;
180 IrpSp2->FileObject = vc->fileobj;
181
182 if (vc->devobj->Flags & DO_BUFFERED_IO) {
183 Irp2->AssociatedIrp.SystemBuffer = ExAllocatePoolWithTag(NonPagedPool, IrpSp->Parameters.Read.Length, ALLOC_TAG);
184 if (!Irp2->AssociatedIrp.SystemBuffer) {
185 ERR("out of memory\n");
186 ExReleaseResourceLite(&pdode->child_lock);
187 Status = STATUS_INSUFFICIENT_RESOURCES;
188 goto end;
189 }
190
191 Irp2->Flags |= IRP_BUFFERED_IO | IRP_DEALLOCATE_BUFFER | IRP_INPUT_OPERATION;
192
193 Irp2->UserBuffer = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority);
194 } else if (vc->devobj->Flags & DO_DIRECT_IO)
195 Irp2->MdlAddress = Irp->MdlAddress;
196 else
197 Irp2->UserBuffer = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority);
198
199 IrpSp2->Parameters.Read.Length = IrpSp->Parameters.Read.Length;
200 IrpSp2->Parameters.Read.ByteOffset.QuadPart = IrpSp->Parameters.Read.ByteOffset.QuadPart;
201
202 KeInitializeEvent(&context.Event, NotificationEvent, false);
203 Irp2->UserIosb = &context.iosb;
204
205 IoSetCompletionRoutine(Irp2, vol_read_completion, &context, true, true, true);
206
207 Status = IoCallDriver(vc->devobj, Irp2);
208
209 if (Status == STATUS_PENDING) {
210 KeWaitForSingleObject(&context.Event, Executive, KernelMode, false, NULL);
211 Status = context.iosb.Status;
212 }
213
214 ExReleaseResourceLite(&pdode->child_lock);
215
216 Irp->IoStatus.Information = context.iosb.Information;
217
218 end:
219 Irp->IoStatus.Status = Status;
220 IoCompleteRequest(Irp, IO_NO_INCREMENT);
221
222 return Status;
223 }
224
vol_write(IN PDEVICE_OBJECT DeviceObject,IN PIRP Irp)225 NTSTATUS vol_write(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
226 volume_device_extension* vde = DeviceObject->DeviceExtension;
227 pdo_device_extension* pdode = vde->pdode;
228 volume_child* vc;
229 NTSTATUS Status;
230 PIRP Irp2;
231 vol_read_context context;
232 PIO_STACK_LOCATION IrpSp, IrpSp2;
233
234 TRACE("(%p, %p)\n", DeviceObject, Irp);
235
236 ExAcquireResourceSharedLite(&pdode->child_lock, true);
237
238 if (IsListEmpty(&pdode->children)) {
239 ExReleaseResourceLite(&pdode->child_lock);
240 Status = STATUS_INVALID_DEVICE_REQUEST;
241 goto end;
242 }
243
244 vc = CONTAINING_RECORD(pdode->children.Flink, volume_child, list_entry);
245
246 if (vc->list_entry.Flink != &pdode->children) { // more than once device
247 ExReleaseResourceLite(&pdode->child_lock);
248 Status = STATUS_ACCESS_DENIED;
249 goto end;
250 }
251
252 // We can't use IoSkipCurrentIrpStackLocation as the device isn't in our stack
253
254 Irp2 = IoAllocateIrp(vc->devobj->StackSize, false);
255
256 if (!Irp2) {
257 ERR("IoAllocateIrp failed\n");
258 ExReleaseResourceLite(&pdode->child_lock);
259 Status = STATUS_INSUFFICIENT_RESOURCES;
260 goto end;
261 }
262
263 IrpSp = IoGetCurrentIrpStackLocation(Irp);
264 IrpSp2 = IoGetNextIrpStackLocation(Irp2);
265
266 IrpSp2->MajorFunction = IRP_MJ_WRITE;
267 IrpSp2->FileObject = vc->fileobj;
268
269 if (vc->devobj->Flags & DO_BUFFERED_IO) {
270 Irp2->AssociatedIrp.SystemBuffer = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority);
271
272 Irp2->Flags |= IRP_BUFFERED_IO;
273
274 Irp2->UserBuffer = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority);
275 } else if (vc->devobj->Flags & DO_DIRECT_IO)
276 Irp2->MdlAddress = Irp->MdlAddress;
277 else
278 Irp2->UserBuffer = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority);
279
280 IrpSp2->Parameters.Write.Length = IrpSp->Parameters.Write.Length;
281 IrpSp2->Parameters.Write.ByteOffset.QuadPart = IrpSp->Parameters.Write.ByteOffset.QuadPart;
282
283 KeInitializeEvent(&context.Event, NotificationEvent, false);
284 Irp2->UserIosb = &context.iosb;
285
286 IoSetCompletionRoutine(Irp2, vol_read_completion, &context, true, true, true);
287
288 Status = IoCallDriver(vc->devobj, Irp2);
289
290 if (Status == STATUS_PENDING) {
291 KeWaitForSingleObject(&context.Event, Executive, KernelMode, false, NULL);
292 Status = context.iosb.Status;
293 }
294
295 ExReleaseResourceLite(&pdode->child_lock);
296
297 Irp->IoStatus.Information = context.iosb.Information;
298
299 end:
300 Irp->IoStatus.Status = Status;
301 IoCompleteRequest(Irp, IO_NO_INCREMENT);
302
303 return Status;
304 }
305
vol_query_device_name(volume_device_extension * vde,PIRP Irp)306 static NTSTATUS vol_query_device_name(volume_device_extension* vde, PIRP Irp) {
307 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
308 PMOUNTDEV_NAME name;
309
310 if (IrpSp->FileObject && IrpSp->FileObject->FsContext)
311 return STATUS_INVALID_PARAMETER;
312
313 if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(MOUNTDEV_NAME)) {
314 Irp->IoStatus.Information = sizeof(MOUNTDEV_NAME);
315 return STATUS_BUFFER_TOO_SMALL;
316 }
317
318 name = Irp->AssociatedIrp.SystemBuffer;
319 name->NameLength = vde->name.Length;
320
321 if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength < offsetof(MOUNTDEV_NAME, Name[0]) + name->NameLength) {
322 Irp->IoStatus.Information = sizeof(MOUNTDEV_NAME);
323 return STATUS_BUFFER_OVERFLOW;
324 }
325
326 RtlCopyMemory(name->Name, vde->name.Buffer, vde->name.Length);
327
328 Irp->IoStatus.Information = offsetof(MOUNTDEV_NAME, Name[0]) + name->NameLength;
329
330 return STATUS_SUCCESS;
331 }
332
vol_query_unique_id(volume_device_extension * vde,PIRP Irp)333 static NTSTATUS vol_query_unique_id(volume_device_extension* vde, PIRP Irp) {
334 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
335 MOUNTDEV_UNIQUE_ID* mduid;
336 pdo_device_extension* pdode;
337
338 if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(MOUNTDEV_UNIQUE_ID)) {
339 Irp->IoStatus.Information = sizeof(MOUNTDEV_UNIQUE_ID);
340 return STATUS_BUFFER_TOO_SMALL;
341 }
342
343 mduid = Irp->AssociatedIrp.SystemBuffer;
344 mduid->UniqueIdLength = sizeof(BTRFS_UUID);
345
346 if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength < offsetof(MOUNTDEV_UNIQUE_ID, UniqueId[0]) + mduid->UniqueIdLength) {
347 Irp->IoStatus.Information = sizeof(MOUNTDEV_UNIQUE_ID);
348 return STATUS_BUFFER_OVERFLOW;
349 }
350
351 if (!vde->pdo)
352 return STATUS_INVALID_PARAMETER;
353
354 pdode = vde->pdode;
355
356 RtlCopyMemory(mduid->UniqueId, &pdode->uuid, sizeof(BTRFS_UUID));
357
358 Irp->IoStatus.Information = offsetof(MOUNTDEV_UNIQUE_ID, UniqueId[0]) + mduid->UniqueIdLength;
359
360 return STATUS_SUCCESS;
361 }
362
vol_is_dynamic(PIRP Irp)363 static NTSTATUS vol_is_dynamic(PIRP Irp) {
364 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
365 uint8_t* buf;
366
367 if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength == 0 || !Irp->AssociatedIrp.SystemBuffer)
368 return STATUS_INVALID_PARAMETER;
369
370 buf = (uint8_t*)Irp->AssociatedIrp.SystemBuffer;
371
372 *buf = 1;
373
374 Irp->IoStatus.Information = 1;
375
376 return STATUS_SUCCESS;
377 }
378
vol_check_verify(volume_device_extension * vde)379 static NTSTATUS vol_check_verify(volume_device_extension* vde) {
380 pdo_device_extension* pdode = vde->pdode;
381 NTSTATUS Status;
382 LIST_ENTRY* le;
383
384 ExAcquireResourceSharedLite(&pdode->child_lock, true);
385
386 le = pdode->children.Flink;
387 while (le != &pdode->children) {
388 volume_child* vc = CONTAINING_RECORD(le, volume_child, list_entry);
389
390 Status = dev_ioctl(vc->devobj, IOCTL_STORAGE_CHECK_VERIFY, NULL, 0, NULL, 0, false, NULL);
391 if (!NT_SUCCESS(Status))
392 goto end;
393
394 le = le->Flink;
395 }
396
397 Status = STATUS_SUCCESS;
398
399 end:
400 ExReleaseResourceLite(&pdode->child_lock);
401
402 return Status;
403 }
404
vol_get_disk_extents(volume_device_extension * vde,PIRP Irp)405 static NTSTATUS vol_get_disk_extents(volume_device_extension* vde, PIRP Irp) {
406 pdo_device_extension* pdode = vde->pdode;
407 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
408 LIST_ENTRY* le;
409 ULONG num_extents = 0, i, max_extents = 1;
410 NTSTATUS Status;
411 VOLUME_DISK_EXTENTS *ext, *ext3;
412
413 if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(VOLUME_DISK_EXTENTS))
414 return STATUS_BUFFER_TOO_SMALL;
415
416 ExAcquireResourceSharedLite(&pdode->child_lock, true);
417
418 le = pdode->children.Flink;
419 while (le != &pdode->children) {
420 volume_child* vc = CONTAINING_RECORD(le, volume_child, list_entry);
421 VOLUME_DISK_EXTENTS ext2;
422
423 Status = dev_ioctl(vc->devobj, IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS, NULL, 0, &ext2, sizeof(VOLUME_DISK_EXTENTS), false, NULL);
424 if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_OVERFLOW) {
425 ERR("IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS returned %08lx\n", Status);
426 goto end;
427 }
428
429 num_extents += ext2.NumberOfDiskExtents;
430
431 if (ext2.NumberOfDiskExtents > max_extents)
432 max_extents = ext2.NumberOfDiskExtents;
433
434 le = le->Flink;
435 }
436
437 ext = Irp->AssociatedIrp.SystemBuffer;
438
439 if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength < offsetof(VOLUME_DISK_EXTENTS, Extents[0]) + (num_extents * sizeof(DISK_EXTENT))) {
440 Irp->IoStatus.Information = offsetof(VOLUME_DISK_EXTENTS, Extents[0]);
441 ext->NumberOfDiskExtents = num_extents;
442 Status = STATUS_BUFFER_OVERFLOW;
443 goto end;
444 }
445
446 ext3 = ExAllocatePoolWithTag(PagedPool, offsetof(VOLUME_DISK_EXTENTS, Extents[0]) + (max_extents * sizeof(DISK_EXTENT)), ALLOC_TAG);
447 if (!ext3) {
448 ERR("out of memory\n");
449 Status = STATUS_INSUFFICIENT_RESOURCES;
450 goto end;
451 }
452
453 i = 0;
454 ext->NumberOfDiskExtents = 0;
455
456 le = pdode->children.Flink;
457 while (le != &pdode->children) {
458 volume_child* vc = CONTAINING_RECORD(le, volume_child, list_entry);
459
460 Status = dev_ioctl(vc->devobj, IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS, NULL, 0, ext3,
461 (ULONG)offsetof(VOLUME_DISK_EXTENTS, Extents[0]) + (max_extents * sizeof(DISK_EXTENT)), false, NULL);
462 if (!NT_SUCCESS(Status)) {
463 ERR("IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS returned %08lx\n", Status);
464 ExFreePool(ext3);
465 goto end;
466 }
467
468 if (i + ext3->NumberOfDiskExtents > num_extents) {
469 Irp->IoStatus.Information = offsetof(VOLUME_DISK_EXTENTS, Extents[0]);
470 ext->NumberOfDiskExtents = i + ext3->NumberOfDiskExtents;
471 Status = STATUS_BUFFER_OVERFLOW;
472 ExFreePool(ext3);
473 goto end;
474 }
475
476 RtlCopyMemory(&ext->Extents[i], ext3->Extents, sizeof(DISK_EXTENT) * ext3->NumberOfDiskExtents);
477 i += ext3->NumberOfDiskExtents;
478
479 le = le->Flink;
480 }
481
482 ExFreePool(ext3);
483
484 Status = STATUS_SUCCESS;
485
486 ext->NumberOfDiskExtents = i;
487 Irp->IoStatus.Information = offsetof(VOLUME_DISK_EXTENTS, Extents[0]) + (i * sizeof(DISK_EXTENT));
488
489 end:
490 ExReleaseResourceLite(&pdode->child_lock);
491
492 return Status;
493 }
494
vol_is_writable(volume_device_extension * vde)495 static NTSTATUS vol_is_writable(volume_device_extension* vde) {
496 pdo_device_extension* pdode = vde->pdode;
497 NTSTATUS Status;
498 LIST_ENTRY* le;
499 bool writable = false;
500
501 ExAcquireResourceSharedLite(&pdode->child_lock, true);
502
503 le = pdode->children.Flink;
504 while (le != &pdode->children) {
505 volume_child* vc = CONTAINING_RECORD(le, volume_child, list_entry);
506
507 Status = dev_ioctl(vc->devobj, IOCTL_DISK_IS_WRITABLE, NULL, 0, NULL, 0, true, NULL);
508
509 if (NT_SUCCESS(Status)) {
510 writable = true;
511 break;
512 } else if (Status != STATUS_MEDIA_WRITE_PROTECTED)
513 goto end;
514
515 le = le->Flink;
516 }
517
518 Status = writable ? STATUS_SUCCESS : STATUS_MEDIA_WRITE_PROTECTED;
519
520 end:
521 ExReleaseResourceLite(&pdode->child_lock);
522
523 return STATUS_SUCCESS;
524 }
525
vol_get_length(volume_device_extension * vde,PIRP Irp)526 static NTSTATUS vol_get_length(volume_device_extension* vde, PIRP Irp) {
527 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
528 pdo_device_extension* pdode = vde->pdode;
529 GET_LENGTH_INFORMATION* gli;
530 LIST_ENTRY* le;
531
532 if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(GET_LENGTH_INFORMATION))
533 return STATUS_BUFFER_TOO_SMALL;
534
535 gli = (GET_LENGTH_INFORMATION*)Irp->AssociatedIrp.SystemBuffer;
536
537 gli->Length.QuadPart = 0;
538
539 ExAcquireResourceSharedLite(&pdode->child_lock, true);
540
541 le = pdode->children.Flink;
542 while (le != &pdode->children) {
543 volume_child* vc = CONTAINING_RECORD(le, volume_child, list_entry);
544
545 gli->Length.QuadPart += vc->size;
546
547 le = le->Flink;
548 }
549
550 ExReleaseResourceLite(&pdode->child_lock);
551
552 Irp->IoStatus.Information = sizeof(GET_LENGTH_INFORMATION);
553
554 return STATUS_SUCCESS;
555 }
556
vol_get_drive_geometry(PDEVICE_OBJECT DeviceObject,PIRP Irp)557 static NTSTATUS vol_get_drive_geometry(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
558 volume_device_extension* vde = DeviceObject->DeviceExtension;
559 pdo_device_extension* pdode = vde->pdode;
560 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
561 DISK_GEOMETRY* geom;
562 uint64_t length;
563 LIST_ENTRY* le;
564
565 if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(DISK_GEOMETRY))
566 return STATUS_BUFFER_TOO_SMALL;
567
568 length = 0;
569
570 ExAcquireResourceSharedLite(&pdode->child_lock, true);
571
572 le = pdode->children.Flink;
573 while (le != &pdode->children) {
574 volume_child* vc = CONTAINING_RECORD(le, volume_child, list_entry);
575
576 length += vc->size;
577
578 le = le->Flink;
579 }
580
581 ExReleaseResourceLite(&pdode->child_lock);
582
583 geom = (DISK_GEOMETRY*)Irp->AssociatedIrp.SystemBuffer;
584 geom->BytesPerSector = DeviceObject->SectorSize == 0 ? 0x200 : DeviceObject->SectorSize;
585 geom->SectorsPerTrack = 0x3f;
586 geom->TracksPerCylinder = 0xff;
587 geom->Cylinders.QuadPart = length / (UInt32x32To64(geom->TracksPerCylinder, geom->SectorsPerTrack) * geom->BytesPerSector);
588 geom->MediaType = DeviceObject->Characteristics & FILE_REMOVABLE_MEDIA ? RemovableMedia : FixedMedia;
589
590 Irp->IoStatus.Information = sizeof(DISK_GEOMETRY);
591
592 return STATUS_SUCCESS;
593 }
594
vol_get_gpt_attributes(PIRP Irp)595 static NTSTATUS vol_get_gpt_attributes(PIRP Irp) {
596 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
597 VOLUME_GET_GPT_ATTRIBUTES_INFORMATION* vggai;
598
599 if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(VOLUME_GET_GPT_ATTRIBUTES_INFORMATION))
600 return STATUS_BUFFER_TOO_SMALL;
601
602 vggai = (VOLUME_GET_GPT_ATTRIBUTES_INFORMATION*)Irp->AssociatedIrp.SystemBuffer;
603
604 vggai->GptAttributes = 0;
605
606 Irp->IoStatus.Information = sizeof(VOLUME_GET_GPT_ATTRIBUTES_INFORMATION);
607
608 return STATUS_SUCCESS;
609 }
610
vol_get_device_number(volume_device_extension * vde,PIRP Irp)611 static NTSTATUS vol_get_device_number(volume_device_extension* vde, PIRP Irp) {
612 pdo_device_extension* pdode = vde->pdode;
613 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
614 volume_child* vc;
615 STORAGE_DEVICE_NUMBER* sdn;
616
617 // If only one device, return its disk number. This is needed for ejection to work.
618
619 if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(STORAGE_DEVICE_NUMBER))
620 return STATUS_BUFFER_TOO_SMALL;
621
622 ExAcquireResourceSharedLite(&pdode->child_lock, true);
623
624 if (IsListEmpty(&pdode->children) || pdode->num_children > 1) {
625 ExReleaseResourceLite(&pdode->child_lock);
626 return STATUS_INVALID_DEVICE_REQUEST;
627 }
628
629 vc = CONTAINING_RECORD(pdode->children.Flink, volume_child, list_entry);
630
631 if (vc->disk_num == 0xffffffff) {
632 ExReleaseResourceLite(&pdode->child_lock);
633 return STATUS_INVALID_DEVICE_REQUEST;
634 }
635
636 sdn = (STORAGE_DEVICE_NUMBER*)Irp->AssociatedIrp.SystemBuffer;
637
638 sdn->DeviceType = FILE_DEVICE_DISK;
639 sdn->DeviceNumber = vc->disk_num;
640 sdn->PartitionNumber = vc->part_num;
641
642 ExReleaseResourceLite(&pdode->child_lock);
643
644 Irp->IoStatus.Information = sizeof(STORAGE_DEVICE_NUMBER);
645
646 return STATUS_SUCCESS;
647 }
648
_Function_class_(IO_COMPLETION_ROUTINE)649 _Function_class_(IO_COMPLETION_ROUTINE)
650 static NTSTATUS __stdcall vol_ioctl_completion(PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID conptr) {
651 KEVENT* event = conptr;
652
653 UNUSED(DeviceObject);
654 UNUSED(Irp);
655
656 KeSetEvent(event, 0, false);
657
658 return STATUS_MORE_PROCESSING_REQUIRED;
659 }
660
vol_ioctl_passthrough(volume_device_extension * vde,PIRP Irp)661 static NTSTATUS vol_ioctl_passthrough(volume_device_extension* vde, PIRP Irp) {
662 NTSTATUS Status;
663 volume_child* vc;
664 PIRP Irp2;
665 PIO_STACK_LOCATION IrpSp, IrpSp2;
666 KEVENT Event;
667 pdo_device_extension* pdode = vde->pdode;
668
669 TRACE("(%p, %p)\n", vde, Irp);
670
671 ExAcquireResourceSharedLite(&pdode->child_lock, true);
672
673 if (IsListEmpty(&pdode->children)) {
674 ExReleaseResourceLite(&pdode->child_lock);
675 return STATUS_INVALID_DEVICE_REQUEST;
676 }
677
678 vc = CONTAINING_RECORD(pdode->children.Flink, volume_child, list_entry);
679
680 if (vc->list_entry.Flink != &pdode->children) { // more than one device
681 ExReleaseResourceLite(&pdode->child_lock);
682 return STATUS_INVALID_DEVICE_REQUEST;
683 }
684
685 Irp2 = IoAllocateIrp(vc->devobj->StackSize, false);
686
687 if (!Irp2) {
688 ERR("IoAllocateIrp failed\n");
689 ExReleaseResourceLite(&pdode->child_lock);
690 return STATUS_INSUFFICIENT_RESOURCES;
691 }
692
693 IrpSp = IoGetCurrentIrpStackLocation(Irp);
694 IrpSp2 = IoGetNextIrpStackLocation(Irp2);
695
696 IrpSp2->MajorFunction = IrpSp->MajorFunction;
697 IrpSp2->MinorFunction = IrpSp->MinorFunction;
698 IrpSp2->FileObject = vc->fileobj;
699
700 IrpSp2->Parameters.DeviceIoControl.OutputBufferLength = IrpSp->Parameters.DeviceIoControl.OutputBufferLength;
701 IrpSp2->Parameters.DeviceIoControl.InputBufferLength = IrpSp->Parameters.DeviceIoControl.InputBufferLength;
702 IrpSp2->Parameters.DeviceIoControl.IoControlCode = IrpSp->Parameters.DeviceIoControl.IoControlCode;
703 IrpSp2->Parameters.DeviceIoControl.Type3InputBuffer = IrpSp->Parameters.DeviceIoControl.Type3InputBuffer;
704
705 Irp2->AssociatedIrp.SystemBuffer = Irp->AssociatedIrp.SystemBuffer;
706 Irp2->MdlAddress = Irp->MdlAddress;
707 Irp2->UserBuffer = Irp->UserBuffer;
708 Irp2->Flags = Irp->Flags;
709
710 KeInitializeEvent(&Event, NotificationEvent, false);
711
712 IoSetCompletionRoutine(Irp2, vol_ioctl_completion, &Event, true, true, true);
713
714 Status = IoCallDriver(vc->devobj, Irp2);
715
716 if (Status == STATUS_PENDING) {
717 KeWaitForSingleObject(&Event, Executive, KernelMode, false, NULL);
718 Status = Irp2->IoStatus.Status;
719 }
720
721 Irp->IoStatus.Status = Irp2->IoStatus.Status;
722 Irp->IoStatus.Information = Irp2->IoStatus.Information;
723
724 ExReleaseResourceLite(&pdode->child_lock);
725
726 IoFreeIrp(Irp2);
727
728 return Status;
729 }
730
vol_query_stable_guid(volume_device_extension * vde,PIRP Irp)731 static NTSTATUS vol_query_stable_guid(volume_device_extension* vde, PIRP Irp) {
732 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
733 MOUNTDEV_STABLE_GUID* mdsg;
734 pdo_device_extension* pdode;
735
736 if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(MOUNTDEV_STABLE_GUID)) {
737 Irp->IoStatus.Information = sizeof(MOUNTDEV_STABLE_GUID);
738 return STATUS_BUFFER_TOO_SMALL;
739 }
740
741 mdsg = Irp->AssociatedIrp.SystemBuffer;
742
743 if (!vde->pdo)
744 return STATUS_INVALID_PARAMETER;
745
746 pdode = vde->pdode;
747
748 RtlCopyMemory(&mdsg->StableGuid, &pdode->uuid, sizeof(BTRFS_UUID));
749
750 Irp->IoStatus.Information = sizeof(MOUNTDEV_STABLE_GUID);
751
752 return STATUS_SUCCESS;
753 }
754
vol_device_control(IN PDEVICE_OBJECT DeviceObject,IN PIRP Irp)755 NTSTATUS vol_device_control(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
756 volume_device_extension* vde = DeviceObject->DeviceExtension;
757 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
758
759 TRACE("(%p, %p)\n", DeviceObject, Irp);
760
761 Irp->IoStatus.Information = 0;
762
763 switch (IrpSp->Parameters.DeviceIoControl.IoControlCode) {
764 case IOCTL_MOUNTDEV_QUERY_DEVICE_NAME:
765 return vol_query_device_name(vde, Irp);
766
767 case IOCTL_MOUNTDEV_QUERY_UNIQUE_ID:
768 return vol_query_unique_id(vde, Irp);
769
770 case IOCTL_STORAGE_GET_DEVICE_NUMBER:
771 return vol_get_device_number(vde, Irp);
772
773 case IOCTL_MOUNTDEV_QUERY_SUGGESTED_LINK_NAME:
774 TRACE("unhandled control code IOCTL_MOUNTDEV_QUERY_SUGGESTED_LINK_NAME\n");
775 break;
776
777 case IOCTL_MOUNTDEV_QUERY_STABLE_GUID:
778 return vol_query_stable_guid(vde, Irp);
779
780 case IOCTL_MOUNTDEV_LINK_CREATED:
781 TRACE("unhandled control code IOCTL_MOUNTDEV_LINK_CREATED\n");
782 break;
783
784 case IOCTL_VOLUME_GET_GPT_ATTRIBUTES:
785 return vol_get_gpt_attributes(Irp);
786
787 case IOCTL_VOLUME_IS_DYNAMIC:
788 return vol_is_dynamic(Irp);
789
790 case IOCTL_VOLUME_ONLINE:
791 Irp->IoStatus.Information = 0;
792 return STATUS_SUCCESS;
793
794 case IOCTL_VOLUME_POST_ONLINE:
795 Irp->IoStatus.Information = 0;
796 return STATUS_SUCCESS;
797
798 case IOCTL_DISK_GET_DRIVE_GEOMETRY:
799 return vol_get_drive_geometry(DeviceObject, Irp);
800
801 case IOCTL_DISK_IS_WRITABLE:
802 return vol_is_writable(vde);
803
804 case IOCTL_DISK_GET_LENGTH_INFO:
805 return vol_get_length(vde, Irp);
806
807 case IOCTL_STORAGE_CHECK_VERIFY:
808 case IOCTL_DISK_CHECK_VERIFY:
809 return vol_check_verify(vde);
810
811 case IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS:
812 return vol_get_disk_extents(vde, Irp);
813
814 default: { // pass ioctl through if only one child device
815 NTSTATUS Status = vol_ioctl_passthrough(vde, Irp);
816 #ifdef _DEBUG
817 ULONG code = IrpSp->Parameters.DeviceIoControl.IoControlCode;
818
819 if (NT_SUCCESS(Status))
820 TRACE("passing through ioctl %lx (returning %08lx)\n", code, Status);
821 else
822 WARN("passing through ioctl %lx (returning %08lx)\n", code, Status);
823 #endif
824
825 return Status;
826 }
827 }
828
829 return STATUS_INVALID_DEVICE_REQUEST;
830 }
831
mountmgr_add_drive_letter(PDEVICE_OBJECT mountmgr,PUNICODE_STRING devpath)832 NTSTATUS mountmgr_add_drive_letter(PDEVICE_OBJECT mountmgr, PUNICODE_STRING devpath) {
833 NTSTATUS Status;
834 ULONG mmdltsize;
835 MOUNTMGR_DRIVE_LETTER_TARGET* mmdlt;
836 MOUNTMGR_DRIVE_LETTER_INFORMATION mmdli;
837
838 mmdltsize = (ULONG)offsetof(MOUNTMGR_DRIVE_LETTER_TARGET, DeviceName[0]) + devpath->Length;
839
840 mmdlt = ExAllocatePoolWithTag(NonPagedPool, mmdltsize, ALLOC_TAG);
841 if (!mmdlt) {
842 ERR("out of memory\n");
843 return STATUS_INSUFFICIENT_RESOURCES;
844 }
845
846 mmdlt->DeviceNameLength = devpath->Length;
847 RtlCopyMemory(&mmdlt->DeviceName, devpath->Buffer, devpath->Length);
848 TRACE("mmdlt = %.*S\n", (int)(mmdlt->DeviceNameLength / sizeof(WCHAR)), mmdlt->DeviceName);
849
850 Status = dev_ioctl(mountmgr, IOCTL_MOUNTMGR_NEXT_DRIVE_LETTER, mmdlt, mmdltsize, &mmdli, sizeof(MOUNTMGR_DRIVE_LETTER_INFORMATION), false, NULL);
851
852 if (!NT_SUCCESS(Status))
853 ERR("IOCTL_MOUNTMGR_NEXT_DRIVE_LETTER returned %08lx\n", Status);
854 else
855 TRACE("DriveLetterWasAssigned = %u, CurrentDriveLetter = %c\n", mmdli.DriveLetterWasAssigned, mmdli.CurrentDriveLetter);
856
857 ExFreePool(mmdlt);
858
859 return Status;
860 }
861
_Function_class_(DRIVER_NOTIFICATION_CALLBACK_ROUTINE)862 _Function_class_(DRIVER_NOTIFICATION_CALLBACK_ROUTINE)
863 NTSTATUS __stdcall pnp_removal(PVOID NotificationStructure, PVOID Context) {
864 TARGET_DEVICE_REMOVAL_NOTIFICATION* tdrn = (TARGET_DEVICE_REMOVAL_NOTIFICATION*)NotificationStructure;
865 pdo_device_extension* pdode = (pdo_device_extension*)Context;
866
867 if (RtlCompareMemory(&tdrn->Event, &GUID_TARGET_DEVICE_QUERY_REMOVE, sizeof(GUID)) == sizeof(GUID)) {
868 TRACE("GUID_TARGET_DEVICE_QUERY_REMOVE\n");
869
870 if (pdode->vde && pdode->vde->mounted_device)
871 pnp_query_remove_device(pdode->vde->mounted_device, NULL);
872 }
873
874 return STATUS_SUCCESS;
875 }
876
allow_degraded_mount(BTRFS_UUID * uuid)877 static bool allow_degraded_mount(BTRFS_UUID* uuid) {
878 HANDLE h;
879 NTSTATUS Status;
880 OBJECT_ATTRIBUTES oa;
881 UNICODE_STRING path, adus;
882 uint32_t degraded = mount_allow_degraded;
883 ULONG i, j, kvfilen, retlen;
884 KEY_VALUE_FULL_INFORMATION* kvfi;
885
886 path.Length = path.MaximumLength = registry_path.Length + (37 * sizeof(WCHAR));
887 path.Buffer = ExAllocatePoolWithTag(PagedPool, path.Length, ALLOC_TAG);
888
889 if (!path.Buffer) {
890 ERR("out of memory\n");
891 return false;
892 }
893
894 RtlCopyMemory(path.Buffer, registry_path.Buffer, registry_path.Length);
895 i = registry_path.Length / sizeof(WCHAR);
896
897 path.Buffer[i] = '\\';
898 i++;
899
900 for (j = 0; j < 16; j++) {
901 path.Buffer[i] = hex_digit((uuid->uuid[j] & 0xF0) >> 4);
902 path.Buffer[i+1] = hex_digit(uuid->uuid[j] & 0xF);
903
904 i += 2;
905
906 if (j == 3 || j == 5 || j == 7 || j == 9) {
907 path.Buffer[i] = '-';
908 i++;
909 }
910 }
911
912 InitializeObjectAttributes(&oa, &path, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
913
914 kvfilen = (ULONG)offsetof(KEY_VALUE_FULL_INFORMATION, Name[0]) + (255 * sizeof(WCHAR));
915 kvfi = ExAllocatePoolWithTag(PagedPool, kvfilen, ALLOC_TAG);
916 if (!kvfi) {
917 ERR("out of memory\n");
918 ExFreePool(path.Buffer);
919 return false;
920 }
921
922 Status = ZwOpenKey(&h, KEY_QUERY_VALUE, &oa);
923 if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
924 goto end;
925 else if (!NT_SUCCESS(Status)) {
926 ERR("ZwOpenKey returned %08lx\n", Status);
927 goto end;
928 }
929
930 adus.Buffer = L"AllowDegraded";
931 adus.Length = adus.MaximumLength = sizeof(adus.Buffer) - sizeof(WCHAR);
932
933 if (NT_SUCCESS(ZwQueryValueKey(h, &adus, KeyValueFullInformation, kvfi, kvfilen, &retlen))) {
934 if (kvfi->Type == REG_DWORD && kvfi->DataLength >= sizeof(uint32_t)) {
935 uint32_t* val = (uint32_t*)((uint8_t*)kvfi + kvfi->DataOffset);
936
937 degraded = *val;
938 }
939 }
940
941 ZwClose(h);
942
943 end:
944 ExFreePool(kvfi);
945
946 ExFreePool(path.Buffer);
947
948 return degraded;
949 }
950
951 typedef struct {
952 LIST_ENTRY list_entry;
953 UNICODE_STRING name;
954 NTSTATUS Status;
955 BTRFS_UUID uuid;
956 } drive_letter_removal;
957
drive_letter_callback2(pdo_device_extension * pdode,PDEVICE_OBJECT mountmgr)958 static void drive_letter_callback2(pdo_device_extension* pdode, PDEVICE_OBJECT mountmgr) {
959 LIST_ENTRY* le;
960 LIST_ENTRY dlrlist;
961
962 InitializeListHead(&dlrlist);
963
964 ExAcquireResourceExclusiveLite(&pdode->child_lock, true);
965
966 le = pdode->children.Flink;
967
968 while (le != &pdode->children) {
969 drive_letter_removal* dlr;
970
971 volume_child* vc = CONTAINING_RECORD(le, volume_child, list_entry);
972
973 dlr = ExAllocatePoolWithTag(PagedPool, sizeof(drive_letter_removal), ALLOC_TAG);
974 if (!dlr) {
975 ERR("out of memory\n");
976
977 while (!IsListEmpty(&dlrlist)) {
978 dlr = CONTAINING_RECORD(RemoveHeadList(&dlrlist), drive_letter_removal, list_entry);
979
980 ExFreePool(dlr->name.Buffer);
981 ExFreePool(dlr);
982 }
983
984 ExReleaseResourceLite(&pdode->child_lock);
985 return;
986 }
987
988 dlr->name.Length = dlr->name.MaximumLength = vc->pnp_name.Length + (3 * sizeof(WCHAR));
989 dlr->name.Buffer = ExAllocatePoolWithTag(PagedPool, dlr->name.Length, ALLOC_TAG);
990
991 if (!dlr->name.Buffer) {
992 ERR("out of memory\n");
993
994 ExFreePool(dlr);
995
996 while (!IsListEmpty(&dlrlist)) {
997 dlr = CONTAINING_RECORD(RemoveHeadList(&dlrlist), drive_letter_removal, list_entry);
998
999 ExFreePool(dlr->name.Buffer);
1000 ExFreePool(dlr);
1001 }
1002
1003 ExReleaseResourceLite(&pdode->child_lock);
1004 return;
1005 }
1006
1007 RtlCopyMemory(dlr->name.Buffer, L"\\??", 3 * sizeof(WCHAR));
1008 RtlCopyMemory(&dlr->name.Buffer[3], vc->pnp_name.Buffer, vc->pnp_name.Length);
1009
1010 dlr->uuid = vc->uuid;
1011
1012 InsertTailList(&dlrlist, &dlr->list_entry);
1013
1014 le = le->Flink;
1015 }
1016
1017 ExReleaseResourceLite(&pdode->child_lock);
1018
1019 le = dlrlist.Flink;
1020 while (le != &dlrlist) {
1021 drive_letter_removal* dlr = CONTAINING_RECORD(le, drive_letter_removal, list_entry);
1022
1023 dlr->Status = remove_drive_letter(mountmgr, &dlr->name);
1024
1025 if (!NT_SUCCESS(dlr->Status) && dlr->Status != STATUS_NOT_FOUND)
1026 WARN("remove_drive_letter returned %08lx\n", dlr->Status);
1027
1028 le = le->Flink;
1029 }
1030
1031 // set vc->had_drive_letter
1032
1033 ExAcquireResourceExclusiveLite(&pdode->child_lock, true);
1034
1035 while (!IsListEmpty(&dlrlist)) {
1036 drive_letter_removal* dlr = CONTAINING_RECORD(RemoveHeadList(&dlrlist), drive_letter_removal, list_entry);
1037
1038 le = pdode->children.Flink;
1039
1040 while (le != &pdode->children) {
1041 volume_child* vc = CONTAINING_RECORD(le, volume_child, list_entry);
1042
1043 if (RtlCompareMemory(&vc->uuid, &dlr->uuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)) {
1044 vc->had_drive_letter = NT_SUCCESS(dlr->Status);
1045 break;
1046 }
1047
1048 le = le->Flink;
1049 }
1050
1051 ExFreePool(dlr->name.Buffer);
1052 ExFreePool(dlr);
1053 }
1054
1055 ExReleaseResourceLite(&pdode->child_lock);
1056 }
1057
_Function_class_(IO_WORKITEM_ROUTINE)1058 _Function_class_(IO_WORKITEM_ROUTINE)
1059 static void __stdcall drive_letter_callback(pdo_device_extension* pdode) {
1060 NTSTATUS Status;
1061 UNICODE_STRING mmdevpath;
1062 PDEVICE_OBJECT mountmgr;
1063 PFILE_OBJECT mountmgrfo;
1064
1065 RtlInitUnicodeString(&mmdevpath, MOUNTMGR_DEVICE_NAME);
1066 Status = IoGetDeviceObjectPointer(&mmdevpath, FILE_READ_ATTRIBUTES, &mountmgrfo, &mountmgr);
1067 if (!NT_SUCCESS(Status)) {
1068 ERR("IoGetDeviceObjectPointer returned %08lx\n", Status);
1069 return;
1070 }
1071
1072 drive_letter_callback2(pdode, mountmgr);
1073
1074 ObDereferenceObject(mountmgrfo);
1075 }
1076
add_volume_device(superblock * sb,PUNICODE_STRING devpath,uint64_t length,ULONG disk_num,ULONG part_num)1077 void add_volume_device(superblock* sb, PUNICODE_STRING devpath, uint64_t length, ULONG disk_num, ULONG part_num) {
1078 NTSTATUS Status;
1079 LIST_ENTRY* le;
1080 PDEVICE_OBJECT DeviceObject;
1081 volume_child* vc;
1082 PFILE_OBJECT FileObject;
1083 UNICODE_STRING devpath2;
1084 bool inserted = false, new_pdo = false;
1085 pdo_device_extension* pdode = NULL;
1086 PDEVICE_OBJECT pdo = NULL;
1087 bool process_drive_letters = false;
1088
1089 if (devpath->Length == 0)
1090 return;
1091
1092 ExAcquireResourceExclusiveLite(&pdo_list_lock, true);
1093
1094 le = pdo_list.Flink;
1095 while (le != &pdo_list) {
1096 pdo_device_extension* pdode2 = CONTAINING_RECORD(le, pdo_device_extension, list_entry);
1097
1098 if (RtlCompareMemory(&pdode2->uuid, &sb->uuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)) {
1099 pdode = pdode2;
1100 break;
1101 }
1102
1103 le = le->Flink;
1104 }
1105
1106 Status = IoGetDeviceObjectPointer(devpath, FILE_READ_ATTRIBUTES, &FileObject, &DeviceObject);
1107 if (!NT_SUCCESS(Status)) {
1108 ERR("IoGetDeviceObjectPointer returned %08lx\n", Status);
1109 ExReleaseResourceLite(&pdo_list_lock);
1110 return;
1111 }
1112
1113 if (!pdode) {
1114 if (no_pnp) {
1115 Status = IoReportDetectedDevice(drvobj, InterfaceTypeUndefined, 0xFFFFFFFF, 0xFFFFFFFF, NULL, NULL, 0, &pdo);
1116
1117 if (!NT_SUCCESS(Status)) {
1118 ERR("IoReportDetectedDevice returned %08lx\n", Status);
1119 ExReleaseResourceLite(&pdo_list_lock);
1120 return;
1121 }
1122
1123 pdode = ExAllocatePoolWithTag(NonPagedPool, sizeof(pdo_device_extension), ALLOC_TAG);
1124
1125 if (!pdode) {
1126 ERR("out of memory\n");
1127 ExReleaseResourceLite(&pdo_list_lock);
1128 return;
1129 }
1130 } else {
1131 Status = IoCreateDevice(drvobj, sizeof(pdo_device_extension), NULL, FILE_DEVICE_DISK,
1132 FILE_AUTOGENERATED_DEVICE_NAME | FILE_DEVICE_SECURE_OPEN, false, &pdo);
1133 if (!NT_SUCCESS(Status)) {
1134 ERR("IoCreateDevice returned %08lx\n", Status);
1135 ExReleaseResourceLite(&pdo_list_lock);
1136 goto fail;
1137 }
1138
1139 pdo->Flags |= DO_BUS_ENUMERATED_DEVICE;
1140
1141 pdode = pdo->DeviceExtension;
1142 }
1143
1144 RtlZeroMemory(pdode, sizeof(pdo_device_extension));
1145
1146 pdode->type = VCB_TYPE_PDO;
1147 pdode->pdo = pdo;
1148 pdode->uuid = sb->uuid;
1149
1150 ExInitializeResourceLite(&pdode->child_lock);
1151 InitializeListHead(&pdode->children);
1152 pdode->num_children = sb->num_devices;
1153 pdode->children_loaded = 0;
1154
1155 pdo->Flags &= ~DO_DEVICE_INITIALIZING;
1156 pdo->SectorSize = (USHORT)sb->sector_size;
1157
1158 ExAcquireResourceExclusiveLite(&pdode->child_lock, true);
1159
1160 new_pdo = true;
1161 } else {
1162 ExAcquireResourceExclusiveLite(&pdode->child_lock, true);
1163 ExConvertExclusiveToSharedLite(&pdo_list_lock);
1164
1165 le = pdode->children.Flink;
1166 while (le != &pdode->children) {
1167 volume_child* vc2 = CONTAINING_RECORD(le, volume_child, list_entry);
1168
1169 if (RtlCompareMemory(&vc2->uuid, &sb->dev_item.device_uuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)) {
1170 // duplicate, ignore
1171 ExReleaseResourceLite(&pdode->child_lock);
1172 ExReleaseResourceLite(&pdo_list_lock);
1173 goto fail;
1174 }
1175
1176 le = le->Flink;
1177 }
1178 }
1179
1180 vc = ExAllocatePoolWithTag(PagedPool, sizeof(volume_child), ALLOC_TAG);
1181 if (!vc) {
1182 ERR("out of memory\n");
1183
1184 ExReleaseResourceLite(&pdode->child_lock);
1185 ExReleaseResourceLite(&pdo_list_lock);
1186
1187 goto fail;
1188 }
1189
1190 vc->uuid = sb->dev_item.device_uuid;
1191 vc->devid = sb->dev_item.dev_id;
1192 vc->generation = sb->generation;
1193 vc->notification_entry = NULL;
1194 vc->boot_volume = false;
1195
1196 Status = IoRegisterPlugPlayNotification(EventCategoryTargetDeviceChange, 0, FileObject,
1197 drvobj, pnp_removal, pdode, &vc->notification_entry);
1198 if (!NT_SUCCESS(Status))
1199 WARN("IoRegisterPlugPlayNotification returned %08lx\n", Status);
1200
1201 vc->devobj = DeviceObject;
1202 vc->fileobj = FileObject;
1203
1204 devpath2 = *devpath;
1205
1206 // The PNP path sometimes begins \\?\ and sometimes \??\. We need to remove this prefix
1207 // so we can compare properly if the device is removed.
1208 if (devpath->Length > 4 * sizeof(WCHAR) && devpath->Buffer[0] == '\\' && (devpath->Buffer[1] == '\\' || devpath->Buffer[1] == '?') &&
1209 devpath->Buffer[2] == '?' && devpath->Buffer[3] == '\\') {
1210 devpath2.Buffer = &devpath2.Buffer[3];
1211 devpath2.Length -= 3 * sizeof(WCHAR);
1212 devpath2.MaximumLength -= 3 * sizeof(WCHAR);
1213 }
1214
1215 vc->pnp_name.Length = vc->pnp_name.MaximumLength = devpath2.Length;
1216 vc->pnp_name.Buffer = ExAllocatePoolWithTag(PagedPool, devpath2.Length, ALLOC_TAG);
1217
1218 if (vc->pnp_name.Buffer)
1219 RtlCopyMemory(vc->pnp_name.Buffer, devpath2.Buffer, devpath2.Length);
1220 else {
1221 ERR("out of memory\n");
1222 vc->pnp_name.Length = vc->pnp_name.MaximumLength = 0;
1223 }
1224
1225 vc->size = length;
1226 vc->seeding = sb->flags & BTRFS_SUPERBLOCK_FLAGS_SEEDING ? true : false;
1227 vc->disk_num = disk_num;
1228 vc->part_num = part_num;
1229 vc->had_drive_letter = false;
1230
1231 le = pdode->children.Flink;
1232 while (le != &pdode->children) {
1233 volume_child* vc2 = CONTAINING_RECORD(le, volume_child, list_entry);
1234
1235 if (vc2->generation < vc->generation) {
1236 if (le == pdode->children.Flink)
1237 pdode->num_children = sb->num_devices;
1238
1239 InsertHeadList(vc2->list_entry.Blink, &vc->list_entry);
1240 inserted = true;
1241 break;
1242 }
1243
1244 le = le->Flink;
1245 }
1246
1247 if (!inserted)
1248 InsertTailList(&pdode->children, &vc->list_entry);
1249
1250 pdode->children_loaded++;
1251
1252 if (pdode->vde && pdode->vde->mounted_device) {
1253 device_extension* Vcb = pdode->vde->mounted_device->DeviceExtension;
1254
1255 ExAcquireResourceExclusiveLite(&Vcb->tree_lock, true);
1256
1257 le = Vcb->devices.Flink;
1258 while (le != &Vcb->devices) {
1259 device* dev = CONTAINING_RECORD(le, device, list_entry);
1260
1261 if (!dev->devobj && RtlCompareMemory(&dev->devitem.device_uuid, &sb->dev_item.device_uuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)) {
1262 dev->devobj = DeviceObject;
1263 dev->disk_num = disk_num;
1264 dev->part_num = part_num;
1265 init_device(Vcb, dev, false);
1266 break;
1267 }
1268
1269 le = le->Flink;
1270 }
1271
1272 ExReleaseResourceLite(&Vcb->tree_lock);
1273 }
1274
1275 if (DeviceObject->Characteristics & FILE_REMOVABLE_MEDIA) {
1276 pdode->removable = true;
1277
1278 if (pdode->vde && pdode->vde->device)
1279 pdode->vde->device->Characteristics |= FILE_REMOVABLE_MEDIA;
1280 }
1281
1282 if (pdode->num_children == pdode->children_loaded || (pdode->children_loaded == 1 && allow_degraded_mount(&sb->uuid))) {
1283 if ((!new_pdo || !no_pnp) && pdode->vde) {
1284 Status = IoSetDeviceInterfaceState(&pdode->vde->bus_name, true);
1285 if (!NT_SUCCESS(Status))
1286 WARN("IoSetDeviceInterfaceState returned %08lx\n", Status);
1287 }
1288
1289 process_drive_letters = true;
1290 }
1291
1292 ExReleaseResourceLite(&pdode->child_lock);
1293
1294 if (new_pdo)
1295 InsertTailList(&pdo_list, &pdode->list_entry);
1296
1297 ExReleaseResourceLite(&pdo_list_lock);
1298
1299 if (process_drive_letters)
1300 drive_letter_callback(pdode);
1301
1302 if (new_pdo) {
1303 if (RtlCompareMemory(&sb->uuid, &boot_uuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID))
1304 boot_add_device(pdo);
1305 else if (no_pnp)
1306 AddDevice(drvobj, pdo);
1307 else {
1308 bus_device_extension* bde = busobj->DeviceExtension;
1309 IoInvalidateDeviceRelations(bde->buspdo, BusRelations);
1310 }
1311 }
1312
1313 return;
1314
1315 fail:
1316 ObDereferenceObject(FileObject);
1317 }
1318