Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reworking the mount and unmount code. #392

Closed
lundman opened this issue Jul 10, 2024 · 29 comments
Closed

Reworking the mount and unmount code. #392

lundman opened this issue Jul 10, 2024 · 29 comments

Comments

@lundman
Copy link

lundman commented Jul 10, 2024

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
Copy link
Author

lundman commented Jul 10, 2024

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
Copy link

sskras commented Jul 10, 2024

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

@sskras
Copy link

sskras commented Jul 10, 2024

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
Copy link

sskras commented Jul 10, 2024

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
Copy link

sskras commented Jul 10, 2024

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
Copy link
Author

lundman commented Jul 10, 2024

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

@lundman
Copy link
Author

lundman commented Jul 11, 2024

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
Copy link
Author

lundman commented Jul 11, 2024

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

@lundman
Copy link
Author

lundman commented Jul 11, 2024

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
Copy link

sskras commented Jul 11, 2024

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
Copy link
Author

lundman commented Jul 11, 2024

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

@sskras
Copy link

sskras commented Jul 11, 2024

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: #389 (comment)

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
Copy link

sskras commented Jul 11, 2024

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:

  • creating a reparse point by hand
  • registering it via MountMgr (IOCTL_MOUNTMGR_VOLUME_MOUNT_POINT_CREATED)

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
Copy link

sskras commented Jul 11, 2024

Last two items:

@lundman
Copy link
Author

lundman commented Jul 11, 2024

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
Copy link
Author

lundman commented Jul 12, 2024

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
Copy link

DI555 commented Jul 13, 2024

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

  • will it be possible to mount, umount and remount without an OS restart? It would be so great!

@lundman
Copy link
Author

lundman commented Jul 13, 2024

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
Copy link
Author

lundman commented Jul 17, 2024

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
Copy link

sskras commented Jul 19, 2024

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
Copy link
Author

lundman commented Jul 26, 2024

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.

@lundman
Copy link
Author

lundman commented Sep 5, 2024

8c07c15

@lundman lundman closed this as completed Sep 5, 2024
@Anankke
Copy link

Anankke commented Sep 15, 2024

BSOD on import on 2.2.6rc4, was fine on 2.2.3-rc6

@lundman
Copy link
Author

lundman commented Sep 15, 2024

Any clues as to why? cbuf? stack?

@Anankke
Copy link

Anankke commented Sep 15, 2024


READ_ADDRESS: fffff80242b01390: Unable to get MiVisibleState
Unable to get NonPagedPoolStart
Unable to get NonPagedPoolEnd
Unable to get PagedPoolStart
Unable to get PagedPoolEnd
fffff80242a15400: Unable to get Flags value from nt!KdVersionBlock
fffff80242a15400: Unable to get Flags value from nt!KdVersionBlock
unable to get nt!MmSpecialPagesInUse
 0000002938dde0f0 

ERROR_CODE: (NTSTATUS) 0xc0000005 - 0x%p            0x%p                    %s

EXCEPTION_CODE_STR:  c0000005

EXCEPTION_PARAMETER1:  0000000000000000

EXCEPTION_PARAMETER2:  0000002938dde0f0

EXCEPTION_STR:  0xc0000005

STACK_TEXT:  
fffffd0b`4a9570c0 fffff80d`f817640d : ffff8782`faed6d60 00000000`00000000 fffff802`427d2840 00000000`00000000 : nt!IofCallDriver+0x48
fffffd0b`4a957100 ffff8783`0244dab0 : ffff8782`fa9d6820 ffff8783`347b3a20 ffff8783`347b3a20 ffff8783`347b3a20 : OpenZFS!zfsctl_vnode_alloc+0x2cd [C:\src\openzfs\module\os\windows\zfs\zfs_ctldir.c @ 203] 
fffffd0b`4a957200 ffff8782`fa9d6820 : ffff8783`347b3a20 ffff8783`347b3a20 ffff8783`347b3a20 ffff3ade`2f8611a7 : 0xffff8783`0244dab0
fffffd0b`4a957208 ffff8783`347b3a20 : ffff8783`347b3a20 ffff8783`347b3a20 ffff3ade`2f8611a7 fffff802`42050d55 : 0xffff8782`fa9d6820
fffffd0b`4a957210 ffff8783`347b3a20 : ffff8783`347b3a20 ffff3ade`2f8611a7 fffff802`42050d55 ffff8783`22668050 : 0xffff8783`347b3a20
fffffd0b`4a957218 ffff8783`347b3a20 : ffff3ade`2f8611a7 fffff802`42050d55 ffff8783`22668050 00000000`00000000 : 0xffff8783`347b3a20
fffffd0b`4a957220 ffff3ade`2f8611a7 : fffff802`42050d55 ffff8783`22668050 00000000`00000000 00000000`00000000 : 0xffff8783`347b3a20
fffffd0b`4a957228 fffff802`42050d55 : ffff8783`22668050 00000000`00000000 00000000`00000000 ffffffff`ffffffff : 0xffff3ade`2f8611a7
fffffd0b`4a957230 fffff802`41bf4a76 : ffff8782`faed6d60 ffff8783`0244dab0 ffff8783`347b3a20 00000000`00000000 : nt!IofCallDriver+0x55
fffffd0b`4a957270 fffff802`42050d55 : ffff8783`24e27d20 00000000`00000000 00000000`00000000 ffffffff`ffffffff : FLTMGR!FltpDispatch+0xd6
fffffd0b`4a9572d0 fffff802`41bf4a76 : ffff8783`24e27d20 ffff8783`0244dab0 ffff8783`347b3a20 00000000`00000000 : nt!IofCallDriver+0x55
fffffd0b`4a957310 fffff802`42050d55 : ffff8783`24e27d20 fffff802`42060b8e 00000000`00000000 fffffd0b`4a957400 : FLTMGR!FltpDispatch+0xd6
fffffd0b`4a957370 fffff802`427b437e : ffff8783`0244db00 fffffd0b`4a9574a0 00000000`00000000 fffff802`00000000 : nt!IofCallDriver+0x55
fffffd0b`4a9573b0 fffff802`427b40ae : 00000000`00000001 ffff8782`f3b4f520 fffff802`42a27020 00000000`00000001 : nt!IopShutdownBaseFileSystems+0xca
fffffd0b`4a957430 fffff802`427ba31a : 00000000`00000002 00000000`00000002 fffff802`42a27020 ffff8783`32805b40 : nt!IoShutdownSystem+0x156
fffffd0b`4a9574b0 fffff802`42028525 : ffff8783`3837d0c0 fffff802`4207b4a0 ffff8782`f3b4f520 ffff8782`00000000 : nt!PopGracefulShutdown+0x23a
fffffd0b`4a9574f0 fffff802`4212f9a5 : ffff8783`3837d0c0 00000000`00000080 ffff8782`f3b44080 001fe067`bcbbbdff : nt!ExpWorkerThread+0x105
fffffd0b`4a957590 fffff802`4220d2a8 : fffff802`3fb65180 ffff8783`3837d0c0 fffff802`4212f950 00000000`00000246 : nt!PspSystemThreadStartup+0x55
fffffd0b`4a9575e0 00000000`00000000 : fffffd0b`4a958000 fffffd0b`4a951000 00000000`00000000 00000000`00000000 : nt!KiStartSystemThread+0x28


FAULTING_SOURCE_LINE:  C:\src\openzfs\module\os\windows\zfs\zfs_ctldir.c

FAULTING_SOURCE_FILE:  C:\src\openzfs\module\os\windows\zfs\zfs_ctldir.c

FAULTING_SOURCE_LINE_NUMBER:  203

SYMBOL_NAME:  OpenZFS!zfsctl_vnode_alloc+2cd

MODULE_NAME: OpenZFS

IMAGE_NAME:  OpenZFS.sys

STACK_COMMAND:  .cxr 0xfffffd0b4a9566c0 ; kb

BUCKET_ID_FUNC_OFFSET:  2cd

FAILURE_BUCKET_ID:  AV_OpenZFS!zfsctl_vnode_alloc

OSPLATFORM_TYPE:  x64

OSNAME:  Windows 10```

@lundman
Copy link
Author

lundman commented Sep 15, 2024

Why would IopShutdownBaseFileSystems be called on your system? This is during import?

@Anankke
Copy link

Anankke commented Sep 15, 2024

Why would IopShutdownBaseFileSystems be called on your system? This is during import?

Yes this is when I do zpool import ZFS

@lundman
Copy link
Author

lundman commented Sep 15, 2024

I can certainly fix the IRP_SHUTDOWN we receive, and look at why the snapshot code is crashing - but it is peculiar it starts a shutdown processes.

@Anankke
Copy link

Anankke commented Sep 15, 2024

Might be a trigger on my system, still need to be confirmed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants