[disk,device] Move BootDrive as Boot property
[people/sha0/winvblock.git] / src / winvblock / driver.c
1 /**
2  * Copyright (C) 2009-2010, Shao Miller <shao.miller@yrdsb.edu.on.ca>.
3  * Copyright 2006-2008, V.
4  * For WinAoE contact information, see http://winaoe.org/
5  *
6  * This file is part of WinVBlock, derived from WinAoE.
7  *
8  * WinVBlock is free software: you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation, either version 3 of the License, or
11  * (at your option) any later version.
12  *
13  * WinVBlock is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with WinVBlock.  If not, see <http://www.gnu.org/licenses/>.
20  */
21
22 /**
23  * @file
24  *
25  * Driver specifics.
26  */
27
28 #include <stdio.h>
29 #include <ntddk.h>
30 #include <scsi.h>
31
32 #include "winvblock.h"
33 #include "wv_stdlib.h"
34 #include "wv_string.h"
35 #include "portable.h"
36 #include "driver.h"
37 #include "device.h"
38 #include "disk.h"
39 #include "registry.h"
40 #include "mount.h"
41 #include "bus.h"
42 #include "filedisk.h"
43 #include "ramdisk.h"
44 #include "debug.h"
45
46 /* Names for the main bus. */
47 #define WV_M_BUS_NAME_ (L"\\Device\\" winvblock__literal_w)
48 #define WV_M_BUS_DOSNAME_ (L"\\DosDevices\\" winvblock__literal_w)
49
50 /* Exported. */
51 PDRIVER_OBJECT WvDriverObj = NULL;
52
53 /* Globals. */
54 static void * WvDriverStateHandle_;
55 static winvblock__bool WvDriverStarted_ = FALSE;
56 static PDEVICE_OBJECT WvDriverBusFdo_ = NULL;
57 static KSPIN_LOCK WvDriverBusFdoLock_;
58 static UNICODE_STRING WvDriverBusName_ = {
59     sizeof WV_M_BUS_NAME_,
60     sizeof WV_M_BUS_NAME_,
61     WV_M_BUS_NAME_
62   };
63 static UNICODE_STRING WvDriverBusDosname_ = {
64     sizeof WV_M_BUS_DOSNAME_,
65     sizeof WV_M_BUS_DOSNAME_,
66     WV_M_BUS_DOSNAME_
67   };
68 /* The main bus. */
69 static WV_S_BUS_T WvDriverBus_ = {0};
70 /* Contains TXTSETUP.SIF/BOOT.INI-style OsLoadOptions parameters. */
71 static LPWSTR WvDriverOsLoadOpts_ = NULL;
72
73 /* Forward declarations. */
74 static driver__dispatch_func driver__dispatch_not_supported_;
75 static driver__dispatch_func driver__dispatch_power_;
76 static driver__dispatch_func driver__dispatch_create_close_;
77 static driver__dispatch_func driver__dispatch_sys_ctl_;
78 static driver__dispatch_func driver__dispatch_dev_ctl_;
79 static driver__dispatch_func driver__dispatch_scsi_;
80 static driver__dispatch_func driver__dispatch_pnp_;
81 static void STDCALL driver__unload_(IN PDRIVER_OBJECT);
82 static WV_F_DEV_DISPATCH WvDriverBusSysCtl_;
83 static WV_F_DEV_CTL WvDriverBusDevCtl_;
84 static WV_F_DEV_DISPATCH WvDriverBusPower_;
85 static WV_F_DEV_PNP WvDriverBusPnp_;
86
87 static LPWSTR STDCALL get_opt(IN LPWSTR opt_name) {
88     LPWSTR our_opts, the_opt;
89     WCHAR our_sig[] = L"WINVBLOCK=";
90     /* To produce constant integer expressions. */
91     enum {
92         our_sig_len_bytes = sizeof ( our_sig ) - sizeof ( WCHAR ),
93         our_sig_len = our_sig_len_bytes / sizeof ( WCHAR )
94       };
95     size_t opt_name_len, opt_name_len_bytes;
96
97     if (!WvDriverOsLoadOpts_ || !opt_name)
98       return NULL;
99
100     /* Find /WINVBLOCK= options. */
101     our_opts = WvDriverOsLoadOpts_;
102     while (*our_opts != L'\0') {
103         if (!wv_memcmpeq(our_opts, our_sig, our_sig_len_bytes)) {
104             our_opts++;
105             continue;
106           }
107         our_opts += our_sig_len;
108         break;
109       }
110
111     /* Search for the specific option. */
112     the_opt = our_opts;
113     opt_name_len = wcslen(opt_name);
114     opt_name_len_bytes = opt_name_len * sizeof (WCHAR);
115     while (*the_opt != L'\0' && *the_opt != L' ') {
116         if (!wv_memcmpeq(the_opt, opt_name, opt_name_len_bytes)) {
117             while (*the_opt != L'\0' && *the_opt != L' ' && *the_opt != L',')
118               the_opt++;
119             continue;
120           }
121         the_opt += opt_name_len;
122         break;
123       }
124
125     if (*the_opt == L'\0' || *the_opt == L' ')
126       return NULL;
127
128     /* Next should come "=". */
129     if (*the_opt != L'=')
130       return NULL;
131
132     /*
133      * And finally our option's value.  The caller needs
134      * to worry about looking past the end of the option.
135      */
136     the_opt++;
137     if (*the_opt == L'\0' || *the_opt == L' ')
138       return NULL;
139     return the_opt;
140   }
141
142 static NTSTATUS STDCALL driver__attach_fdo_(
143     IN PDRIVER_OBJECT DriverObject,
144     IN PDEVICE_OBJECT PhysicalDeviceObject
145   ) {
146     KIRQL irql;
147     NTSTATUS status;
148     PLIST_ENTRY walker;
149     PUNICODE_STRING dev_name = NULL;
150     PDEVICE_OBJECT fdo = NULL;
151
152     DBG("Entry\n");
153     /* Do we alreay have our main bus? */
154     if (WvDriverBusFdo_) {
155         DBG("Already have the main bus.  Refusing.\n");
156         status = STATUS_NOT_SUPPORTED;
157         goto err_already_established;
158       }
159     /* Initialize the bus. */
160     WvBusInit(&WvDriverBus_);
161     /* Create the bus FDO. */
162     status = IoCreateDevice(
163         DriverObject,
164         sizeof (driver__dev_ext),
165         &WvDriverBusName_,
166         FILE_DEVICE_CONTROLLER,
167         FILE_DEVICE_SECURE_OPEN,
168         FALSE,
169         &fdo
170       );
171     if (!NT_SUCCESS(status)) {
172         DBG("IoCreateDevice() failed!\n");
173         goto err_fdo;
174       }
175     /* DosDevice symlink. */
176     status = IoCreateSymbolicLink(
177         &WvDriverBusDosname_,
178         &WvDriverBusName_
179       );
180     if (!NT_SUCCESS(status)) {
181         DBG("IoCreateSymbolicLink() failed!\n");
182         goto err_dos_symlink;
183       }
184     /* Set associations for the bus, device, FDO, PDO. */
185     WvDevForDevObj(fdo, &WvDriverBus_.Dev);
186     WvDriverBus_.Dev.Self = WvDriverBus_.Fdo = fdo;
187     WvDriverBus_.Dev.IsBus = TRUE;
188     WvDriverBus_.Dev.IrpMj->SysCtl = WvDriverBusSysCtl_;
189     WvDriverBus_.Dev.IrpMj->DevCtl = WvDriverBusDevCtl_;
190     WvDriverBus_.Dev.IrpMj->Power = WvDriverBusPower_;
191     WvDriverBus_.Dev.IrpMj->Pnp = WvDriverBusPnp_;
192     WvDriverBus_.PhysicalDeviceObject = PhysicalDeviceObject;
193     fdo->Flags |= DO_DIRECT_IO;         /* FIXME? */
194     fdo->Flags |= DO_POWER_INRUSH;      /* FIXME? */
195     /* Attach the FDO to the PDO. */
196     WvDriverBus_.LowerDeviceObject = IoAttachDeviceToDeviceStack(
197         fdo,
198         PhysicalDeviceObject
199       );
200     if (WvDriverBus_.LowerDeviceObject == NULL) {
201         status = STATUS_NO_SUCH_DEVICE;
202         DBG("IoAttachDeviceToDeviceStack() failed!\n");
203         goto err_attach;
204       }
205     status = WvBusStartThread(&WvDriverBus_);
206     if (!NT_SUCCESS(status)) {
207         DBG("Couldn't start bus thread!\n");
208         goto err_thread;
209       }
210     /* Ok! */
211     KeAcquireSpinLock(&WvDriverBusFdoLock_, &irql);
212     if (WvDriverBusFdo_) {
213         KeReleaseSpinLock(&WvDriverBusFdoLock_, irql);
214         DBG("Beaten to it!\n");
215         status = STATUS_NOT_SUPPORTED;
216         goto err_race_failed;
217       }
218     fdo->Flags &= ~DO_DEVICE_INITIALIZING;
219     #ifdef RIS
220     WvDriverBus_.Dev.State = Started;
221     #endif
222     WvDriverBusFdo_ = fdo;
223     KeReleaseSpinLock(&WvDriverBusFdoLock_, irql);
224     DBG("Exit\n");
225     return STATUS_SUCCESS;
226
227     err_race_failed:
228
229     err_thread:
230
231     err_attach:
232
233     IoDeleteSymbolicLink(&WvDriverBusDosname_);
234     err_dos_symlink:
235
236     IoDeleteDevice(fdo);
237     err_fdo:
238
239     err_already_established:
240
241     DBG("Exit with failure\n");
242     return status;
243   }
244
245 /* Create the root-enumerated, main bus device. */
246 NTSTATUS STDCALL driver__create_bus_(void) {
247     WV_SP_BUS_T bus;
248     NTSTATUS status;
249     PDEVICE_OBJECT bus_pdo = NULL;
250
251     /* Create the PDO. */
252     IoReportDetectedDevice(
253         WvDriverObj,
254         InterfaceTypeUndefined,
255         -1,
256         -1,
257         NULL,
258         NULL,
259         FALSE,
260         &bus_pdo
261       );
262     if (bus_pdo == NULL) {
263         DBG("IoReportDetectedDevice() went wrong!  Exiting.\n");
264         status = STATUS_UNSUCCESSFUL;
265         goto err_driver_bus;
266       }
267     /* Attach FDO to PDO. */
268     status = driver__attach_fdo_(WvDriverObj, bus_pdo);
269     if (!NT_SUCCESS(status)) {
270         DBG("driver__attach_fdo_() went wrong!\n");
271         goto err_add_dev;
272       }
273     /* PDO created, FDO attached.  All done. */
274     return STATUS_SUCCESS;
275
276     err_add_dev:
277
278     IoDeleteDevice(bus_pdo);
279     err_driver_bus:
280
281     return status;
282   }
283
284 /*
285  * Note the exception to the function naming convention.
286  * TODO: See if a Makefile change is good enough.
287  */
288 NTSTATUS STDCALL DriverEntry(
289     IN PDRIVER_OBJECT DriverObject,
290     IN PUNICODE_STRING RegistryPath
291   ) {
292     NTSTATUS status;
293     int i;
294
295     DBG("Entry\n");
296     if (WvDriverObj) {
297         DBG("Re-entry not allowed!\n");
298         return STATUS_NOT_SUPPORTED;
299       }
300     WvDriverObj = DriverObject;
301     if (WvDriverStarted_)
302       return STATUS_SUCCESS;
303     Debug_Initialize();
304     status = registry__note_os_load_opts(&WvDriverOsLoadOpts_);
305     if (!NT_SUCCESS(status))
306       return Error("registry__note_driver__os_load_opts", status);
307
308     WvDriverStateHandle_ = NULL;
309     KeInitializeSpinLock(&WvDriverBusFdoLock_);
310
311     if ((WvDriverStateHandle_ = PoRegisterSystemState(
312         NULL,
313         ES_CONTINUOUS
314       )) == NULL) {
315         DBG("Could not set system state to ES_CONTINUOUS!!\n");
316       }
317     /*
318      * Set up IRP MajorFunction function table for devices
319      * this driver handles.
320      */
321     for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++)
322       DriverObject->MajorFunction[i] = driver__dispatch_not_supported_;
323     DriverObject->MajorFunction[IRP_MJ_PNP] = driver__dispatch_pnp_;
324     DriverObject->MajorFunction[IRP_MJ_POWER] = driver__dispatch_power_;
325     DriverObject->MajorFunction[IRP_MJ_CREATE] = driver__dispatch_create_close_;
326     DriverObject->MajorFunction[IRP_MJ_CLOSE] = driver__dispatch_create_close_;
327     DriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] =
328       driver__dispatch_sys_ctl_;
329     DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] =
330       driver__dispatch_dev_ctl_;
331     DriverObject->MajorFunction[IRP_MJ_SCSI] = driver__dispatch_scsi_;
332     /* Set the driver Unload callback. */
333     DriverObject->DriverUnload = driver__unload_;
334     /* Set the driver AddDevice callback. */
335     DriverObject->DriverExtension->AddDevice = driver__attach_fdo_;
336     /* Initialize various modules. */
337     disk__module_init();        /* TODO: Check for error. */
338     filedisk__module_init();    /* TODO: Check for error. */
339     ramdisk__module_init();     /* TODO: Check for error. */
340
341     /*
342      * Always create the root-enumerated, main bus device.
343      * This is required in order to boot from a WinVBlock disk.
344      */
345     status = driver__create_bus_();
346     if(!NT_SUCCESS(status))
347       goto err_bus;
348
349     WvDriverStarted_ = TRUE;
350     DBG("Exit\n");
351     return STATUS_SUCCESS;
352
353     err_bus:
354
355     driver__unload_(DriverObject);
356     DBG("Exit due to failure\n");
357     return status;
358   }
359
360 static NTSTATUS STDCALL driver__dispatch_not_supported_(
361     IN PDEVICE_OBJECT dev_obj,
362     IN PIRP irp
363   ) {
364     irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
365     IoCompleteRequest(irp, IO_NO_INCREMENT);
366     return irp->IoStatus.Status;
367   }
368
369 /**
370  * Common IRP completion routine.
371  *
372  * @v irp               Points to the IRP to complete.
373  * @v info              Number of bytes returned for the IRP, or 0.
374  * @v status            Status for the IRP to complete.
375  * @ret NTSTATUS        Returns the status value, as passed.
376  */
377 winvblock__lib_func NTSTATUS STDCALL driver__complete_irp(
378     IN PIRP irp,
379     IN ULONG_PTR info,
380     IN NTSTATUS status
381   ) {
382     irp->IoStatus.Information = info;
383     irp->IoStatus.Status = status;
384     IoCompleteRequest(irp, IO_NO_INCREMENT);
385     #ifdef DEBUGIRPS
386     Debug_IrpEnd(irp, status);
387     #endif
388     return status;
389   }
390
391 /* Handle a power IRP. */
392 static NTSTATUS driver__dispatch_power_(
393     IN PDEVICE_OBJECT dev_obj,
394     IN PIRP irp
395   ) {
396     /* WvDevFromDevObj() checks for a NULL dev_obj */
397     WV_SP_DEV_T dev = WvDevFromDevObj(dev_obj);
398
399     #ifdef DEBUGIRPS
400     Debug_IrpStart(dev_obj, irp);
401     #endif
402     /* Check that the device exists. */
403     if (!dev || dev->State == WvDevStateDeleted) {
404         /* Even if it doesn't, a power IRP is important! */
405         PoStartNextPowerIrp(irp);
406         return driver__complete_irp(irp, 0, STATUS_NO_SUCH_DEVICE);
407       }
408     /* Call the particular device's power handler. */
409     if (dev->IrpMj && dev->IrpMj->Power)
410       return dev->IrpMj->Power(dev, irp);
411     /* Otherwise, we don't support the IRP. */
412     return driver__complete_irp(irp, 0, STATUS_NOT_SUPPORTED);
413   }
414
415 /* Handle an IRP_MJ_CREATE or IRP_MJ_CLOSE IRP. */
416 static NTSTATUS driver__dispatch_create_close_(
417     IN PDEVICE_OBJECT dev_obj,
418     IN PIRP irp
419   ) {
420     /* WvDevFromDevObj() checks for a NULL dev_obj */
421     WV_SP_DEV_T dev = WvDevFromDevObj(dev_obj);
422
423     #ifdef DEBUGIRPS
424     Debug_IrpStart(dev_obj, irp);
425     #endif
426     /* Check that the device exists. */
427     if (!dev || dev->State == WvDevStateDeleted)
428       return driver__complete_irp(irp, 0, STATUS_NO_SUCH_DEVICE);
429     /* Always succeed with nothing to do. */
430     return driver__complete_irp(irp, 0, STATUS_SUCCESS);
431   }
432
433 /* Handle an IRP_MJ_SYSTEM_CONTROL IRP. */
434 static NTSTATUS driver__dispatch_sys_ctl_(
435     IN PDEVICE_OBJECT dev_obj,
436     IN PIRP irp
437   ) {
438     /* WvDevFromDevObj() checks for a NULL dev_obj */
439     WV_SP_DEV_T dev = WvDevFromDevObj(dev_obj);
440
441     #ifdef DEBUGIRPS
442     Debug_IrpStart(dev_obj, irp);
443     #endif
444     /* Check that the device exists. */
445     if (!dev || dev->State == WvDevStateDeleted)
446       return driver__complete_irp(irp, 0, STATUS_NO_SUCH_DEVICE);
447     /* Call the particular device's power handler. */
448     if (dev->IrpMj && dev->IrpMj->SysCtl)
449       return dev->IrpMj->SysCtl(dev, irp);
450     /* Otherwise, we don't support the IRP. */
451     return driver__complete_irp(irp, 0, STATUS_NOT_SUPPORTED);
452   }
453
454 /* Handle an IRP_MJ_DEVICE_CONTROL IRP. */
455 static NTSTATUS driver__dispatch_dev_ctl_(
456     IN PDEVICE_OBJECT dev_obj,
457     IN PIRP irp
458   ) {
459     /* WvDevFromDevObj() checks for a NULL dev_obj */
460     WV_SP_DEV_T dev = WvDevFromDevObj(dev_obj);
461     PIO_STACK_LOCATION io_stack_loc = IoGetCurrentIrpStackLocation(irp);
462
463     #ifdef DEBUGIRPS
464     Debug_IrpStart(dev_obj, irp);
465     #endif
466     /* Check that the device exists. */
467     if (!dev || dev->State == WvDevStateDeleted)
468       return driver__complete_irp(irp, 0, STATUS_NO_SUCH_DEVICE);
469     /* Call the particular device's power handler. */
470     if (dev->IrpMj && dev->IrpMj->DevCtl) {
471         return dev->IrpMj->DevCtl(
472             dev,
473             irp,
474             io_stack_loc->Parameters.DeviceIoControl.IoControlCode
475           );
476       }
477     /* Otherwise, we don't support the IRP. */
478     return driver__complete_irp(irp, 0, STATUS_NOT_SUPPORTED);
479   }
480
481 /* Handle an IRP_MJ_SCSI IRP. */
482 static NTSTATUS driver__dispatch_scsi_(
483     IN PDEVICE_OBJECT dev_obj,
484     IN PIRP irp
485   ) {
486     /* WvDevFromDevObj() checks for a NULL dev_obj */
487     WV_SP_DEV_T dev = WvDevFromDevObj(dev_obj);
488     PIO_STACK_LOCATION io_stack_loc = IoGetCurrentIrpStackLocation(irp);
489
490     #ifdef DEBUGIRPS
491     Debug_IrpStart(dev_obj, irp);
492     #endif
493     /* Check that the device exists. */
494     if (!dev || dev->State == WvDevStateDeleted)
495       return driver__complete_irp(irp, 0, STATUS_NO_SUCH_DEVICE);
496     /* Call the particular device's power handler. */
497     if (dev->IrpMj && dev->IrpMj->Scsi) {
498         return dev->IrpMj->Scsi(
499             dev,
500             irp,
501             io_stack_loc->Parameters.Scsi.Srb->Function
502           );
503       }
504     /* Otherwise, we don't support the IRP. */
505     return driver__complete_irp(irp, 0, STATUS_NOT_SUPPORTED);
506   }
507
508 /* Handle an IRP_MJ_PNP IRP. */
509 static NTSTATUS driver__dispatch_pnp_(
510     IN PDEVICE_OBJECT dev_obj,
511     IN PIRP irp
512   ) {
513     /* WvDevFromDevObj() checks for a NULL dev_obj */
514     WV_SP_DEV_T dev = WvDevFromDevObj(dev_obj);
515     PIO_STACK_LOCATION io_stack_loc = IoGetCurrentIrpStackLocation(irp);
516
517     #ifdef DEBUGIRPS
518     Debug_IrpStart(dev_obj, irp);
519     #endif
520     /* Check that the device exists. */
521     if (!dev || dev->State == WvDevStateDeleted)
522       return driver__complete_irp(irp, 0, STATUS_NO_SUCH_DEVICE);
523     /* Call the particular device's power handler. */
524     if (dev->IrpMj && dev->IrpMj->Pnp) {
525         return dev->IrpMj->Pnp(
526             dev,
527             irp,
528             io_stack_loc->MinorFunction
529           );
530       }
531     /* Otherwise, we don't support the IRP. */
532     return driver__complete_irp(irp, 0, STATUS_NOT_SUPPORTED);
533   }
534
535 static void STDCALL driver__unload_(IN PDRIVER_OBJECT DriverObject) {
536     DBG("Unloading...\n");
537     if (WvDriverStateHandle_ != NULL)
538       PoUnregisterSystemState(WvDriverStateHandle_);
539     IoDeleteSymbolicLink(&WvDriverBusDosname_);
540     WvDriverBusFdo_ = NULL;
541     wv_free(WvDriverOsLoadOpts_);
542     WvDriverStarted_ = FALSE;
543     DBG("Done\n");
544   }
545
546 winvblock__lib_func void STDCALL WvDriverCompletePendingIrp(IN PIRP Irp) {
547     #ifdef DEBUGIRPS
548     Debug_IrpEnd(Irp, Irp->IoStatus.Status);
549     #endif
550     IoCompleteRequest(Irp, IO_NO_INCREMENT);
551   }
552
553 /* Note the exception to the function naming convention. */
554 winvblock__lib_func NTSTATUS STDCALL Error(
555     IN PCHAR Message,
556     IN NTSTATUS Status
557   ) {
558     DBG("%s: 0x%08x\n", Message, Status);
559     return Status;
560   }
561
562 /**
563  * Get a pointer to the driver bus device.
564  *
565  * @ret         A pointer to the driver bus, or NULL.
566  */
567 winvblock__lib_func WV_SP_BUS_T driver__bus(void) {
568     if (!WvDriverBusFdo_) {
569         DBG("No driver bus device!\n");
570         return NULL;
571       }
572     return WvBusFromDev(WvDevFromDevObj(WvDriverBusFdo_));
573   }
574
575 /* Pass an IRP_MJ_SYSTEM_CONTROL IRP to the bus. */
576 static NTSTATUS STDCALL WvDriverBusSysCtl_(IN WV_SP_DEV_T dev, IN PIRP irp) {
577     WV_SP_BUS_T bus = WvBusFromDev(dev);
578
579     return WvBusSysCtl(bus, irp);
580   }
581
582 /* Pass a power IRP to the bus. */
583 static NTSTATUS STDCALL WvDriverBusPower_(IN WV_SP_DEV_T dev, IN PIRP irp) {
584     WV_SP_BUS_T bus = WvBusFromDev(dev);
585
586     return WvBusPower(bus, irp);
587   }
588
589 /* Pass an IRP_MJ_PNP to the bus. */
590 static NTSTATUS STDCALL WvDriverBusPnp_(
591     IN WV_SP_DEV_T dev,
592     IN PIRP irp,
593     IN UCHAR code
594   ) {
595     WV_SP_BUS_T bus = WvBusFromDev(dev);
596
597     return WvBusPnp(bus, irp, code);
598   }
599     
600 /**
601  * Add a child node to the bus.
602  *
603  * @v Dev               Points to the child device to add.
604  * @ret                 TRUE for success, FALSE for failure.
605  */
606 winvblock__lib_func winvblock__bool STDCALL WvDriverBusAddDev(
607     IN OUT WV_SP_DEV_T Dev
608   ) {
609     /* The new node's device object. */
610     PDEVICE_OBJECT dev_obj;
611
612     DBG("Entry\n");
613     if (!WvDriverBusFdo_ || !Dev) {
614         DBG("No bus or no device!\n");
615         return FALSE;
616       }
617     /* Create the child device. */
618     dev_obj = WvDevCreatePdo(Dev);
619     if (!dev_obj) {
620         DBG("PDO creation failed!\n");
621         return FALSE;
622       }
623     /* Create a node.  TODO: Put the node somewhere better. */
624     Dev->BusNode = wv_malloc(sizeof *(Dev->BusNode));
625     if (!Dev->BusNode) {
626         DBG("Couldn't allocate node storage!\n");
627         IoDeleteDevice(dev_obj);
628         return FALSE;
629       }
630     WvBusInitNode(Dev->BusNode, dev_obj);
631     /* Associate the parent bus. */
632     Dev->Parent = WvDriverBus_.Dev.Self;
633     /*
634      * Initialize the device.  For disks, this routine is responsible for
635      * determining the disk's geometry appropriately for AoE/RAM/file disks.
636      */
637     Dev->Ops.Init(Dev);
638     dev_obj->Flags &= ~DO_DEVICE_INITIALIZING;
639     /* Add the new PDO device to the bus' list of children. */
640     WvBusAddNode(&WvDriverBus_, Dev->BusNode);
641
642     DBG("Exit\n");
643     return TRUE;
644   }
645
646 static NTSTATUS STDCALL WvDriverBusDevCtlDiskDetach_(
647     IN WV_SP_DEV_T dev,
648     IN PIRP irp
649   ) {
650     winvblock__uint8_ptr buffer = irp->AssociatedIrp.SystemBuffer;
651     winvblock__uint32 disk_num = *(winvblock__uint32_ptr) buffer;
652     WV_SP_DEV_T dev_walker;
653     WV_SP_DISK_T disk_walker = NULL, prev_disk_walker;
654     WV_SP_BUS_T bus;
655
656     DBG("Request to detach disk: %d\n", disk_num);
657     bus = WvBusFromDev(dev);
658     dev_walker = bus->first_child;
659     if (dev_walker != NULL)
660       disk_walker = disk__get_ptr(dev_walker);
661     prev_disk_walker = disk_walker;
662     while ((disk_walker != NULL) && (dev_walker->DevNum != disk_num)) {
663         prev_disk_walker = disk_walker;
664         dev_walker = dev_walker->next_sibling_ptr;
665         if (dev_walker != NULL)
666           disk_walker = disk__get_ptr(dev_walker);
667       }
668     if (disk_walker != NULL) {
669         if (disk_walker->Dev->Boot) {
670             DBG("Cannot unmount a boot drive.\n");
671             irp->IoStatus.Information = 0;
672             return STATUS_INVALID_DEVICE_REQUEST;
673           }
674         DBG("Deleting disk %d\n", dev_walker->DevNum);
675         if (disk_walker == disk__get_ptr(bus->first_child))
676           bus->first_child = dev_walker->next_sibling_ptr;
677           else {
678             prev_disk_walker->Dev->next_sibling_ptr =
679               dev_walker->next_sibling_ptr;
680           }
681         disk_walker->Unmount = TRUE;
682         dev_walker->next_sibling_ptr = NULL;
683         if (bus->PhysicalDeviceObject != NULL)
684           IoInvalidateDeviceRelations(bus->PhysicalDeviceObject, BusRelations);
685       }
686     bus->Children--;
687     irp->IoStatus.Information = 0;
688     return STATUS_SUCCESS;
689   }
690
691 NTSTATUS STDCALL WvDriverBusDevCtl_(
692     IN WV_SP_DEV_T dev,
693     IN PIRP irp,
694     IN ULONG POINTER_ALIGNMENT code
695   ) {
696     NTSTATUS status;
697
698     switch (code) {
699         case IOCTL_FILE_ATTACH:
700           status = filedisk__attach(dev, irp);
701           break;
702
703         case IOCTL_FILE_DETACH:
704           status = WvDriverBusDevCtlDiskDetach_(dev, irp);
705           break;
706
707         default:
708           irp->IoStatus.Information = 0;
709           status = STATUS_INVALID_DEVICE_REQUEST;
710       }
711
712     irp->IoStatus.Status = status;
713     IoCompleteRequest(irp, IO_NO_INCREMENT);
714     return status;
715   }