windows/seal: keep the config CD off D:, and win the ProfilesDirectory race

Two first-boot hazards the sealed path hits that the baked path does not,
fixed in a configMedium-only variant of the data-disk init (the shared
path is byte-identical):

- The per-VM config rides an optical drive. On the target's first boot
  the data disk is still raw and unlettered, so Windows gives the CD D:
  -- where the profile volume must go. The plain `if exist D:\` guard
  then sees the CD and skips, stranding ProfilesDirectory on read-only
  media. Now a marker (not the letter) tracks first boot, and any
  occupant of D: is parked on Y: before the data disk claims it.
- Left to Shell-Setup's FolderLocations, ProfilesDirectory can be
  evaluated before the disk exists (unordered within specialize) and
  fall back to C:. It is now written to the registry in the same step
  that just created the volume, so the volume always exists first.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0117qMyjpuXsjpVAcpJbFD8g
This commit is contained in:
Git Sagar 2026-09-16 13:11:06 -03:00
parent 702f723e6d
commit ee002586e5

View file

@ -219,7 +219,59 @@ in
# a GPT label, one full-size NTFS partition and the drive letter, while a disk # a GPT label, one full-size NTFS partition and the drive letter, while a disk
# that already holds data keeps it and only has its letter re-asserted. The # that already holds data keeps it and only has its letter re-asserted. The
# OS disk is added to QEMU first and so is always disk 0. # OS disk is added to QEMU first and so is always disk 0.
initDataDiskScript = pkgs.writeText "vmix-init-data-disk.cmd" '' initDataDiskScript = pkgs.writeText "vmix-init-data-disk.cmd" (if configMedium then ''
@echo off
:: Sealed-image variant. Two extra hazards over the baked path:
::
:: 1. The per-VM config rides an optical drive, and on the target's first
:: boot the raw data disk has no volume yet -- so Windows letters the CD
:: as ${dataDriveLetter}:, exactly where the profile volume must go. The
:: plain `if exist ${dataDriveLetter}:\` guard would then see the CD and
:: skip, leaving ProfilesDirectory pointed at read-only media. So a first
:: boot is tracked by a marker, not by the letter, and any occupant of
:: ${dataDriveLetter}: is moved aside before the data disk claims it.
:: 2. Left to Shell-Setup's FolderLocations, ProfilesDirectory can be
:: evaluated before this disk exists (unordered within specialize) and
:: silently fall back to C:. Setting it here, in the same step that just
:: created the volume, removes that race.
if exist C:\vmix-data-initialized goto :ensure
:: First boot: park whatever holds ${dataDriveLetter}: (the config CD) on Y:
:: so the data disk can take the letter. Harmless if the letter is free.
> C:\Windows\Temp\vmix-cd.txt echo select volume ${dataDriveLetter}
>> C:\Windows\Temp\vmix-cd.txt echo assign letter=Y noerr
diskpart /s C:\Windows\Temp\vmix-cd.txt > nul 2>&1
:: Lay disk 1 (the host zvol) out from scratch and give it the letter.
> C:\Windows\Temp\vmix-dd-init.txt echo select disk 1
>> C:\Windows\Temp\vmix-dd-init.txt echo clean
>> C:\Windows\Temp\vmix-dd-init.txt echo convert gpt
>> C:\Windows\Temp\vmix-dd-init.txt echo create partition primary
>> C:\Windows\Temp\vmix-dd-init.txt echo format fs=ntfs quick label="${dataLabel}"
>> C:\Windows\Temp\vmix-dd-init.txt echo assign letter=${dataDriveLetter}
diskpart /s C:\Windows\Temp\vmix-dd-init.txt
echo initialized > C:\vmix-data-initialized
goto :ensure
:ensure
:: The letter normally persists via MountedDevices; re-assert if it is gone.
if exist ${dataDriveLetter}:\ goto :profiledir
> C:\Windows\Temp\vmix-dd-assign.txt echo select disk 1
>> C:\Windows\Temp\vmix-dd-assign.txt echo select partition 1
>> C:\Windows\Temp\vmix-dd-assign.txt echo assign letter=${dataDriveLetter}
diskpart /s C:\Windows\Temp\vmix-dd-assign.txt > nul 2>&1
:profiledir
${lib.optionalString (profilesDirectory != null) ''
:: Point new profiles at the data volume, now that it exists. REG_EXPAND_SZ
:: to match Windows' own ProfilesDirectory type.
if exist ${dataDriveLetter}:\ (
if not exist "${profilesDirectory}" mkdir "${profilesDirectory}"
reg add "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList" /v ProfilesDirectory /t REG_EXPAND_SZ /d "${profilesDirectory}" /f > nul 2>&1
)''}
del /q C:\Windows\Temp\vmix-cd.txt C:\Windows\Temp\vmix-dd-init.txt C:\Windows\Temp\vmix-dd-assign.txt 2>nul
:done
'' else ''
@echo off @echo off
:: diskpart rather than the Storage cmdlets. New-Partition and :: diskpart rather than the Storage cmdlets. New-Partition and
:: Format-Volume need services that are not up yet this early in :: Format-Volume need services that are not up yet this early in
@ -248,7 +300,7 @@ in
:cleanup :cleanup
del /q C:\Windows\Temp\vmix-dd-assign.txt C:\Windows\Temp\vmix-dd-init.txt 2>nul del /q C:\Windows\Temp\vmix-dd-assign.txt C:\Windows\Temp\vmix-dd-init.txt 2>nul
:done :done
''; '');
# PowerShell in its own file, so neither cmd quoting nor Nix's '' need to be # PowerShell in its own file, so neither cmd quoting nor Nix's '' need to be
# fought. If the account's real profile got backed up to a .bak key (the # fought. If the account's real profile got backed up to a .bak key (the