openzfsonwindows / openzfs

OpenZFS on Linux and FreeBSD
https://openzfs.github.io/openzfs-docs
Other
438 stars 15 forks source link

Reworking the mount and unmount code. #392

Open lundman opened 1 month ago

lundman commented 1 month ago

Re-writing the mount/unmount code, both to try to get it more correct, and further understanding.

Now when we start a mount request, we go through the steps:


Create my new (sub)device; called PDO - PhysicalDeviceObject.

"\Device\Volume{3fe4a1af-988f-3c6a-93c0-6c75d96902db}"

    status = IoCreateDeviceSecure(WIN_DriverObject,
        sizeof (mount_t),
        &diskDeviceName,
        FILE_DEVICE_DISK,
        deviceCharacteristics,
        FALSE,
        &SDDL_DEVOBJ_SYS_ALL_ADM_RWX_WORLD_RW_RES_R,
        NULL,
        &diskDeviceObject);

    status = IoReportDetectedDevice(
        DriverObject,
        InterfaceTypeUndefined,
        0xFFFFFFFF, // 0
        0xFFFFFFFF, // 0
        NULL,
        NULL,
        FALSE,
        &pnpDeviceObject);

    IoAttachDeviceToDeviceStack(DeviceObject, 
        pnpDeviceObject);

    status = IoRegisterDeviceInterface(
        pnpDeviceObject,
        &GUID_DEVINTERFACE_DISK,
        NULL,
        &deviceInterfaceName);

    status = IoSetDeviceInterfaceState(&deviceInterfaceName, TRUE);

    status = IoRegisterDeviceInterface(
        pnpDeviceObject,
        &MOUNTDEV_MOUNTED_DEVICE_GUID,
        NULL,
        &fsInterfaceName);
    status = IoSetDeviceInterfaceState(&fsInterfaceName, TRUE);
    status = IoCreateSymbolicLink(&symbolicLinkTarget, &diskDeviceName);
    IoVerifyVolume(diskDeviceObject, FALSE);

Next up, we wait to receive the IRP_MN_MOUNT_VOLUME when we get it:

Create the VDO - Volume Device Object.

    status = IoCreateDevice(DriverObject,
        sizeof (mount_t),
        NULL,
        FILE_DEVICE_DISK_FILE_SYSTEM,
        deviceCharacteristics,
        FALSE,
        &volDeviceObject);

    attachedDevice = IoAttachDeviceToDeviceStack(volDeviceObject, DeviceToMount);

    Vpb work, set MOUNTED, set DeviceObject, ReferenceCount++.

At this point we can start talking to mountmgr to arrange the Volume driveletter, or the reparsepoint.

I have rewritten the code so that PDO and VDO are more separated, ie, the diskDispatcher() and fsDispatcher() code. When a IRP_MJ is not handled, we now pass it "down" with:

IoCallDriver(DeviceObject->AttachedDevice, Irp);

So an IRP received by the VDO, if not recognised, will pass it down which for us is the PDO. If that does not recognise it, it will pass it further down. (pnpmanager it seems). This appears to be the model that Windows uses. Not everyone bothers, but I am in too deep, so let's explore it fully.

lundman commented 1 month ago

Testing against vhd mounted on F: (NTFS) and a second mounted on F:\dataset,

regedit of MountedDevices will show F:, but not F:\dataset. So we can ignore that.

The output of mountvol however, should show both:

    \\?\Volume{6323d61f-4014-11e7-b722-806e6f6e6963}\
        F:\
    \\?\Volume{6323d63a-4014-11e7-b722-806e6f6e6963}\
        F:\dataset

whereas for ZFS, we only get E:\, no E:\dataset. Less than ideal.

    \\?\Volume{3fe4a1af-988f-3c6a-93c0-6c75d96902db}\
        E:\

Using the command mountvol E:\dataset \\?\Volume{3fe4a1af-988f-3c6a-93c0-6c75d96902db} I was initially getting Directory is not empty.

Now we return NumberOfLinks = zp->z_links; like Unix, but with Unix empty directories has 2 links. Windows expects to see 1.

So we correct it to return 1.

Now I get Access Denied. So I use an elevated CMD prompt, and the mountvol command completes successfully. But, still does not show in mountvol.

The need for elevated permissions is wrong, need to figure out why that is.

sskras commented 1 month ago

Congrats with the steps forward. To my eyes that's more than impressive!

sskras commented 1 month ago

Now I get Access Denied. So I use an elevated CMD prompt, and the mountvol command completes successfully. But, still does not show in mountvol.

The need for elevated permissions is wrong, need to figure out why that is.

I am not sure I can agree. Without elevation mountvol is only able to read the volume status on my w10 Pro:

C:\Users\saukrs> ver

Microsoft Windows [Version 10.0.19044.3086]

C:\Users\saukrs> mountvol
  <...>

    \\?\Volume{776c48ee-fe39-4fa4-8e7c-9d07a3a0d176}\
        *** NO MOUNT POINTS ***

    \\?\Volume{5feefb5c-468a-4da8-9fcc-0dfba19dcab8}\
        *** NO MOUNT POINTS ***

    \\?\Volume{0869fa25-bd66-7074-6498-7f4d08d64db0}\
        E:\

But to mount a volume it needs the elevated permissions:

C:\Users\saukrs> mountvol D:\Another-volume \\?\Volume{776c48ee-fe39-4fa4-8e7c-9d07a3a0d176}
Access is denied.

C:\Users\saukrs> gsudo mountvol D:\Another-volume \\?\Volume{776c48ee-fe39-4fa4-8e7c-9d07a3a0d176}

C:\Users\saukrs> mountvol
  <...>

    \\?\Volume{776c48ee-fe39-4fa4-8e7c-9d07a3a0d176}\
        D:\Another-volume\

    \\?\Volume{5feefb5c-468a-4da8-9fcc-0dfba19dcab8}\
        *** NO MOUNT POINTS ***

    \\?\Volume{0869fa25-bd66-7074-6498-7f4d08d64db0}\
        E:\

The elevation is also needed for unmounting:

C:\Users\saukrs> mountvol D:\Another-volume /P
Access is denied.

That was ant EXT4 volume, but the same is valid for NTFS – just checked that. Volumes can be managed from Ext2 Volume Manager which also requires the elevation.

The only way I can assign drive letter without getting an UAC prompt is via diskmgmt.msc, the Disk Management consolse, handled by mmc.exe. This difference made me curious, so I made a diff.

Security properties for an unelevated cmd.exe and the mmc.exe in Process Explorer:

mmc.exe mmc.exe

At least judging by the BUILTIN\Administrators and NT AUTHORITY\Local account and member of Administrators group entries I guess MMC gets some automatic elevation. At least on my OS setup.

sskras commented 1 month ago

PS. Mounting / unmounting VHDs also requires the elevation on my OS.

An SO thread Allow non-administrators to use diskpart to mount VHDs? says:

The official word from Microsoft (I don't have a reference to provide, but I used to work there and this was the message I got) is that mounting and unmounting VHDs can only be performed by administrators. ISOs, on the other hand, can be mounted and unmounted by regular users.

I found a tool VHDAttach: http://www.jmedved.com/vhdattach/ Which adds a Windows Service that runs as a privileged account, that recieves messages from a little exe, and add shell extensions so that non-priv users can attach/detach VHDs.

This also reminds me about Ext2Srv, the Ext2Fsd Service Manager running in the background and doing things.

Now I noticed that VHDX files can be mounted and dismounted just from File Explorer. Still no luck from unelevated mountvol. Haven't investigated yet about what the explorer.exe does. It's clearly unelevated:

explorer.exe

I haven't investigated yet into exactly what does it do when I click the .vhdx file.

EDIT: Looks like I will need to trace the explorer.exe itself. Maybe using ProcMon for a start.

C:\Users\saukrs> reg query HKCR\Windows.VhdFile\Shell /s

HKEY_CLASSES_ROOT\Windows.VhdFile\Shell\mount
    CommandStateSync    REG_SZ
    ExplorerCommandHandler    REG_SZ    {9ab3b1c9-3225-4bb4-93b6-bfb3c0d93743}
    HasLUAShield    REG_SZ    true
    MultiSelectModel    REG_SZ    Document

HKEY_CLASSES_ROOT\Windows.VhdFile\Shell\mount\command
    (Default)    REG_EXPAND_SZ    %SystemRoot%\Explorer.exe
    DelegateExecute    REG_SZ    {9ab3b1c9-3225-4bb4-93b6-bfb3c0d93743}
sskras commented 1 month ago

A quick shot revealed me nothing, except maybe the suspicious registry paths like these:

HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\MountPoints2\CPC\Volume
HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\MountPoints2\CPC\Volume\{3001e1ae-97fa-4722-a80d-79ac3549257b}
HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\MountPoints2\{3001e1ae-97fa-4722-a80d-79ac3549257b}

... with the UUID matching output of mountvol:

C:\Users\saukrs> mountvol
  <...>

    \\?\Volume{3001e1ae-97fa-4722-a80d-79ac3549257b}\
        X:\

    \\?\Volume{5feefb5c-468a-4da8-9fcc-0dfba19dcab8}\
        *** NO MOUNT POINTS ***

    \\?\Volume{0869fa25-bd66-7074-6498-7f4d08d64db0}\
        E:\

... where X: is the drive I get after I double-click my .vhdx file (without having to interact with any UAC boxes):

image

I suppose it has something to do with AttachVirtualDisk function (virtdisk.h). MSDN writes:

This function will fail if a provider cannot be found, if the VHD or ISO image file is not valid, if the VHD image is already attached, or if the caller does not have SE_MANAGE_VOLUME_PRIVILEGE access rights.

Now I noticed: as soon as I launch "C:\Windows\explorer.exe" D:\antras.vhdx, two processes are spawned under this system process:

image

One of them uses the same image of Explorer:

Command line: C:\Windows\explorer.exe /factory,{75dff2b7-6936-4c06-a8bb-676a7b00b24b} -Embedding
Current directory: C:\Windows\System32\
Autostart Location: n/a
Started: 19:18:20   10/07/2024

And another is some COM Surrogate:

Command line: C:\Windows\system32\DllHost.exe /Processid:{51A1467F-96A2-4B1C-9632-4B4D950FE216}
Current directory: C:\Windows\System32\
Autostart Location: HKLM\System\CurrentControlSet\Services\COMSysApp
Started: 19:18:21   10/07/2024

COM Surrogate process exits soon after that.

Clearly this indicates to me about some IPC/RPC going on behind the courtain via COM technology. And reversing that will require the skillset that I currently lack, I think.

lundman commented 1 month ago

Awesome, I would like to ignore the Access Denied, and sounds like I can.

lundman commented 1 month ago

So mounting NTFS F: and F:\dataset, will also not show in mountmgr QUERY_MOUNT_POINTS

FFFF9784A6EEE040: dprintf: zfs_vnops_windows_mount.c:293:mountmgr_get_mountpoint2():    point 4: '\Device\HarddiskVolume7' '\DosDevices\F:'
FFFF9784A6EEE040: dprintf: zfs_vnops_windows_mount.c:293:mountmgr_get_mountpoint2():    point 5: '\Device\HarddiskVolume8' '\??\Volume{77687f19-37c9-4b70-b4
90-532ac3c49538}'

so that was a couple of days of debugging lost trying to get that to work.

Now using mountvol to make a dataset2 for NTFS on ZFS E:\dataset2, and the inverse ZFS on NTFS F:\dataset2.

It is interesting to note that both E:\dataset, and E:\dataset2 are "Folders" in explorer. But with F:\dataset, and F:\dataset2 they are presented as "Mounted Volumes". Which makes me think we present reparsepoints incorrectly.

Clearly the initial mount of E:\dataset is done with wrong target name.

 Directory of E:\

07/11/2024  10:28 AM    <DIR>          .
07/11/2024  10:28 AM    <DIR>          ..
07/11/2024  10:28 AM    <JUNCTION>     dataset2 [\??\Volume{cbce18b4-e707-472f-8408-14938d234438}\]
07/11/2024  10:18 AM    <JUNCTION>     dataset [\]

 Directory of F:\

07/11/2024  10:14 AM    <JUNCTION>     dataset [\??\Volume{77687f19-37c9-4b70-b490-532ac3c49538}\]
07/11/2024  10:29 AM    <JUNCTION>     dataset2 [\??\Volume{5605728c-3a86-11ef-82be-58961d57db67}\]
lundman commented 1 month ago

Ah no unrelated, shows as "Mounted Volume" after I corrected something.

lundman commented 1 month ago

I'm confused by all the missing information here, between F: (NTFS) and E: (ZFS).

Screenshot 2024-07-11 at 16 31 13 (2)
sskras commented 1 month ago

I'm confused by all the missing information here, between F: (NTFS) and E: (ZFS).

True, a lot is missing. Can you share both outputs here + the command that generated them, please? I would like to make a comparison using my own way :P

lundman commented 1 month ago

I used PowerShell Get-WmiObject -Query "SELECT * FROM Win32_Volume"

sskras commented 1 month ago

Thanks. This seems to be equal to Get-WmiObject Win32_Volume.

Meanwhile I constructed a bit different PowerShell composite for easier reading:

C:\Users\saukrs> powershell "get-wmiobject Win32_Volume | ft @{Label = 'Mount'; Expression = {$_.Name -replace '^\\.+', ''}}, Capacity, FileSystem, Label, DeviceID"

Mount            Capacity FileSystem Label             DeviceID
-----            -------- ---------- -----             --------
  ...
F:\            4320129024 NTFS       NTFS Volume       \\?\Volume{c27eb7f0-3284-43ef-9f12-c3405d3bcd64}\
F:\dataset\    1073741824 EXT4       An EXT4 Vol       \\?\Volume{776c48ee-fe39-4fa4-8e7c-9d07a3a0d176}\
               1073737728 NTFS       Inner NTFS Volume \\?\Volume{fa93785d-d1f8-4ff7-b562-f2f2cbd7665a}\
  ...
B:\           41527803904 Btrfs      A Btrfs Vol       \\?\Volume{0869fa25-bd66-7074-6498-7f4d08d64db0}\

Just noticed that Windows sees my EXT4 volume as mounted only if I mount it under NTFS folder: https://github.com/openzfsonwindows/openzfs/issues/389#issuecomment-2217844926

If I mount EXT4 by default (as a drive letter), the location/mount info is lost:

C:\Users\saukrs> powershell "get-wmiobject Win32_Volume | ft @{Label = 'Mount'; Expression = {$_.Name -replace '^\\.+', ''}}, Capacity, FileSystem, Label, DeviceID"

Mount            Capacity FileSystem Label             DeviceID
-----            -------- ---------- -----             --------
  ...
F:\            4320129024 NTFS       NTFS Volume       \\?\Volume{c27eb7f0-3284-43ef-9f12-c3405d3bcd64}\
               1073741824 EXT4       An EXT4 Vol       \\?\Volume{776c48ee-fe39-4fa4-8e7c-9d07a3a0d176}\
F:\dataset\    1073737728 NTFS       Inner NTFS Volume \\?\Volume{fa93785d-d1f8-4ff7-b562-f2f2cbd7665a}\
  ...
B:\           41527803904 Btrfs      A Btrfs Vol       \\?\Volume{0869fa25-bd66-7074-6498-7f4d08d64db0}\

(It's replaced with \\?\Volume{776c48ee-fe39-4fa4-8e7c-9d07a3a0d176}\, but I clear this out from Name for the brevity)

So in that regard Ext4FSD project is broken – it registers only directory-based mountpoints. But it's broken in a different way from Btrfs which registers only drive letters (and no directory mountpoints).

It would be nice to combine both partial mechanics into one :)


EDIT: I stand corrected – EXT4 drive letter is omitted from the mount list only when assigned using the Ext2 Volume Manager tool.

When I assign it the Windows way, via Disk Management console, the mounted EXT4 drive letter appears as expected:

C:\Users\saukrs> powershell "get-wmiobject Win32_Volume | ft @{Label = 'Mount'; Expression = {$_.Name -replace '^\\.+', ''}}, Capacity, FileSystem, Label, DeviceID"

Mount            Capacity FileSystem Label             DeviceID
-----            -------- ---------- -----             --------
  ...
F:\            4320129024 NTFS       NTFS Volume       \\?\Volume{c27eb7f0-3284-43ef-9f12-c3405d3bcd64}\
E:\            1073741824 EXT4       An EXT4 Vol       \\?\Volume{776c48ee-fe39-4fa4-8e7c-9d07a3a0d176}\
F:\dataset\    1073737728 NTFS       Inner NTFS Volume \\?\Volume{fa93785d-d1f8-4ff7-b562-f2f2cbd7665a}\
  ...
B:\           41527803904 Btrfs      A Btrfs Vol       \\?\Volume{0869fa25-bd66-7074-6498-7f4d08d64db0}\

This probably means that Ext2 Volume Manager (its' mounting operation sequence) is also incomplete. Sorry for the noise, digging further.

sskras commented 1 month ago

FWIW, found a tool which assigns a drive letter for my EXT4 volume correctly, it's ChangeLetter.

With it, the letter is seen by WMI (via PowerShell). But the tool doesn't handle directory mounts.

It's written in C# and seems to be only using SetVolumeMountPoint(): https://github.com/medo64/ChangeLetter/blob/d198f3c/Source/ChangeLetter/Volume.cs#L52-L60

According to Creating Mounted Folders, this is enough:

It is not necessary to manipulate reparse points to use mounted folders; functions such as SetVolumeMountPoint handle all the reparse point details for you.

Then I found an Inside Mountvol.exe article and its' tool that does only directory mount for volumes.

The code aims at older Windows versions (circa 2008). Maybe because of that it also describes two additional things:

Anyway, it mounts my EXT4 volume on an empty NTFS dir just fine.

Registering MountMgr

Creating a reparse point is enought to build a mount point. But this mount point doesn't appear using MOUNTVOL.EXE. It semms to mean that the mount is not registered with the mount manager. I found the information in the DDK. The mount manager is responsible for managing volume names. For each volume, it stores a name that is unique and is permanently identified with the volume.

OpenZFS already seems to be sending that IOCTL, but I guess it's done in kernel mode: https://github.com/openzfsonwindows/openzfs/blob/bab1d06/module/os/windows/zfs/zfs_vnops_windows_mount.c#L581-L583

I am guessing if the userland-vs-kernel mode difference is insignificant for registration.

I probably should stop flooding the ticket and migrate to discussions. Anyways, putting my findings here for now.

sskras commented 1 month ago

Last two items:

lundman commented 1 month ago

No need to survive reboots, at least not that the moment. I have noticed that VHD mounts do create DISK and PARTITION types, so perhaps it is time to add PARTITION to the testing. Everyone says you don't need it, but might as well try everything. I am concerned my E: doesn't have "driveletter" assigned, so potentially perhaps I have 2 broken things, and I should work on one at a time

lundman commented 1 month ago

OK so chatgpt suggests:

To use PnPUtil, open a Command Prompt with administrative privileges and use the following command:
    pnputil /enum-devices /connected
OK having just created my first volume to attempt to mount, and pnputil says:
Instance ID:                ROOT\OpenZFS\0000
Device Description:         Unknown
Class Name:                 Unknown
Class GUID:                 Unknown
Manufacturer Name:          Unknown
Status:                     Problem
Problem Code:               28 (0x1C) [CM_PROB_FAILED_INSTALL]
Check the Event Viewer for any error messages or warnings related to your driver or device installation.
The EventViewer has:
Driver Name: oem3.inf
Class Guid: {71a27cdd-812a-11d0-bec7-08002be2092f}
Service: OpenZFS
Lower Filters: 
Upper Filters: 
Problem: 0x0
Problem Status: 0xC00000E5
The 0xC00000E5 status code is STATUS_IO_TIMEOUT, indicating that a time-out occurred while attempting to install
the driver. This suggests that there might be an issue during the initialization or setup of the device, 
particularly when creating the DeviceObject and preparing it for use as a volume.
DI555 commented 1 month ago

@lundman , thank you very much for such a useful masterpiece! Just wanted to ask, because it means so much,

lundman commented 1 month ago

That is the goal :) Actually, unmouting is working much better with the new work. It's just that the mounts are only half recognised.

lundman commented 1 month ago

Hah ok, so today I found out BusQueryHardwareIDs does not return a string. Only once you point that out, does chatgpt happily point out you need to handle it. Thanks. </Sarcasm>

Yes, for the BusQueryHardwareIDs type in the IRP_MN_QUERY_ID request, 
you need to return a MULTI_SZ string, which is a double-null-terminated 
list of null-terminated strings.

I have finally had some small progress, I can load the Driver without pnputil complaining, so we can start moving forwards. There have been many small little bugs to tweaks.

sskras commented 1 month ago

I wonder if there are any book written that could help. (Not that I am sure @lundman likes reading books at all, just a thought)

lundman commented 1 month ago

OK going to jot down what I have learned while I still remember.

Using WinObj.exe to inspect the objects created, in the "\" category, we have

\Ntfs
\Fat
\Btrfs

So naturally, we also create \OpenZFS. As a separate an unrelated note, we do create \Device\ZFSCTL and \DosDevices\ZFS for userland to ioctl() with the kernel. Old name kept for compatibility with old tools. This may change.

Problem 1.

I noticed that in WinObj you can right-click \Ntfs and select Properties (for some rather pointless properties), but with \OpenZFS it would say The system cannot find the specified path.

Turns out the dispatcher() handler for the \OpenZFS device needs to handle IRP_MJ_CREATE, IRP_MJ_CLOSE and IRP_MJ_CLEANUP. Does not need to do anything more that return STATUS_SUCCESS that I can see. I do not assign FsContext etc, nor are we given FileObject->FileName. Now it works the same.

After that, I can confirm the symlink: \DosDevices\OpenZFS works the same as \DosDevices\Ntfs. (Path in WinObj is \GLOBAL??\OpenZFS and \GLOBAL??\Ntfs.

Problem 2.

We should also get an entry \FileSystem\Ntfs but we do not.

This turns out to be automatically created by calling IoRegisterFileSystem(), if your DeviceType is that of FILE_SYSTEM_DRIVER. iomgr internally checks that against your HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\OpenZFS\Type. Which is automatically created when you install your driver based on OpenZFS.inf.

For the longest time we had: ServiceType = 1 which needs to be ServiceType = 2 for FILE_SYSTEM_DRIVER.

Now we get a \FileSystem\OpenZFS just like Ntfs.

It could very well be we need to do two .INF files, like Btrfs does. Presumably they do that for a reason. We can follow their example eventually. (Second INF appears to be for the Volumes, ie our ZVOLs)

After creating the \OpenZFS DeviceObject, the Driver's AddDevice() function is called, ie, DriverObject->DriverExtension->AddDevice = MyAddDevice;.

In here, you are given the PhysicalDeviceObject that applies to your \OpenZFS DeviceObject. You should attach DeviceObject to the PhysicalDeviceObject by calling IoAttachDeviceToDeviceStack().

This completes the DriverLoad work, and we are up and humming.

You can run things like pnputil /enum-devices /problem and OpenZFS should not show, and also pnputil /enum-devices /connected should show something like

Instance ID:                ROOT\VOLUME\0000
Device Description:         OpenZFS
Class Name:                 Volume
Class GUID:                 {71a27cdd-812a-11d0-bec7-08002be2092f}
Manufacturer Name:          Jorgen Lundman
Status:                     Started
Driver Name:                oem3.inf

The VOLUME in ROOT\VOLUME\0000 appears to be because OpenZFS.inf has Class=Volume in it, and Registry is created with HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\OpenZFS\Enum\0 as ROOT\VOLUME\0000. It is probably as expected.

However, using Event Viewer and traversing down to Application and Services / Microsoft / Windows / Kernel-PnP / Device Configuration when OpenZFS is loaded, we get one Infromation entry which looks good, then a Error entry with Status 0xc00000e5. Is this fixable? Do other drivers like Btrfs also get this?

Problem 3

Mounting a OpenZFS dataset. Gets a bit vague, murky and hand wavey now.

Create a new DeviceObject of type FILE_DEVICE_DISK, often called DCB.

Call IoInvalidateDeviceRelations(pdo, BusRelations); to buzz the bus. AddDevice() is called, and for some weird reason has the same PhysicalDeviceObject - hope that doesn't matter,

IoRegisterDeviceInterface(GUID_DEVINTERFACE_VOLUME ..., &volumeInterfaceName) to make it also be a Volume type. This returns a new string, which you have to enable by calling IoSetDeviceInterfaceState(&volumeInterfaceName, TRUE);

I guess VCB then too? If I call IoAttachDeviceToDeviceStack() here I create a loop, so don't do that. I guess because it is the same PhysicalDeviceObject. Update: If diskDeviceObject is the FILE_DEVICE_DISK object created, then in AddDevice() is called with PhysicalDeviceObject == diskDeviceObject. Which you can obviously not attach to. Possibly the original PDO is what is needed here.

Next up, IRP_MN_MOUNT_VOLUME is called. First make sure it is our FILE_DEVICE_DISK DeviceObject which generally means finding the lowest device. Most drivers loop down, or you can call IoGetDeviceAttachmentBaseRef() and don't forget to call ObDereferenceObject() when done with it.

Now create a new DeviceObject type FILE_DEVICE_DISK_FILE_SYSTEM, called a FCB. Attach this to the FILE_DEVICE_DISK DeviceObject. I believe they mean the DeviceObject given in IRP_MN_MOUNT_VOLUME, as opposed to the DeviceToMount lowest DeviceObject you found - but maybe not, lets try all combinations.

Setup the VPB passed along with the request, to point Vpb->DeviceObject to FILE_DEVICE_DISK_FILE_SYSTEM device, have a reference count, and set VPB_MOUNTED. Interestingly, iomgr does this after the call to IRP_MN_MOUNT_VOLUME so it feels redundant, but perhaps it didn't used to with older Windows. The Vpb->DeviceObject is what is used to know the relationship to your disk DeviceObject.

Now we can talk to MountMgr about where to stick it. We should also announce the volume by calling MountMgr with IOCTL_MOUNTMGR_VOLUME_ARRIVAL_NOTIFICATION, or so says chatgpt.

But even then, our volume does seem "mostly created". I am uncertain if the differences are just that we are a Volume, compared to if you create a VHD, format as NTFS. There VHD creates a DISK, attaches PARTITION, then .. somethingsomething, mounts.

<PowersHell> Get-Volume 
DriveLetter FriendlyName FileSystemType DriveType HealthStatus OperationalStatus SizeRemaining     Size
----------- ------------ -------------- --------- ------------ ----------------- -------------     ----
E           BOOM         NTFS           Removable Healthy      OK                       9.2 GB   9.2 GB
F           New Volume   NTFS           Fixed     Healthy      OK                    991.41 MB  1006 MB

Comparing the Objects in WinObj after a ZFS E: mount, versus a NTFS F: mount.

WinObj


\Device\HarddiskVolume8 Device
\Device\Volume{0b1bb601-af0b-32e8-a1d2-54c167af6277} Device

\Device\Harddisk2\Partition2 SymbolicLink \Device\HarddiskVolume8

\GLOBAL??\E: SymbolicLink \Device\Volume{0b1bb601-af0b-32e8-a1d2-54c167af6277}
\GLOBAL??\F: SymbolicLink \Device\HarddiskVolume8

\GLOBAL??\Harddisk2Partition2 SymbolicLink \Device\HarddiskVolume8
\GLOBAL??\HarddiskVolume8 SymbolicLink \Device\HarddiskVolume8
\GLOBAL??\OpenZFS#0b1bb601-af0b-32e8-a1d2-54c167af6277#1&112354b5&0&0000#{53f5630d-b6bf-11d0-94f2-00a0c91efb8b} SymbolicLink  \Device\Volume{0b1bb601-af0b-32e8-a1d2-54c167af6277}
\GLOBAL??\STORAGE#Volume#{56057687-3a86-11ef-82be-58961d57db67}#0000000001000000#{53f5630d-b6bf-11d0-94f2-00a0c91efb8b} SymbolicLink  \Device\HarddiskVolume8
\GLOBAL??\Volume{0b1bb601-af0b-32e8-a1d2-54c167af6277} SymbolicLink \Device\Volume{0b1bb601-af0b-32e8-a1d2-54c167af6277}
\GLOBAL??\Volume{c97506b9-126b-4c61-8dfd-92f72a5567eb} SymbolicLink \Device\HarddiskVolume8

\ArcName\OpenZFS(0b1bb601-af0b-32e8-a1d2-54c167af6277) SymbolicLink \Device\Volume{0b1bb601-af0b-32e8-a1d2-54c167af6277}

So it looks like we have all required entries and symboliclinks, at least if we remember we do not attach partitions. The ArcName seems to be related to boot support for later.

Here is where it looks a bit wrong:

$ wmic volume list brief
Capacity     DriveType  FileSystem  FreeSpace    Label  Name
85103472640  3          NTFS        19006451712         C:\
                                                        E:\
             5                                          D:\

Mostly empty? Just like CDrom.