﻿# Proxmox VE /init ISO Scanning — How It Works & NETSetup Workaround

## Proxmox /init ISO Detection (PVE 9.1)

The `/init` script lives inside `/boot/initrd.img` (zstd-compressed cpio) on the Proxmox ISO.

### Variables

```sh
CDID_FN=".$PRODUCT_LC-cd-id.txt"   # e.g. ".pve-cd-id.txt"
reqid="$(cat "/$CDID_FN")"          # UUID from the patched ISO, e.g. "019a985d-b08c-..."
```

### Scanning Loop

```sh
for try in $(seq 1 9); do
    for i in /sys/block/hd* /sys/block/sr* /sys/block/scd* /sys/block/sd* /sys/block/nvme*; do
        basedev="${i##*/}"
        path="/dev/$basedev"
        # Filter: removable=1 OR blkid=iso9660 OR size < ~33GB
        mount -t iso9660 -o ro "$path" /mnt
        # Validate: /mnt/$CDID_FN content == $reqid
        # On match: cdrom=$path → break
    done
done
if [ -z "$cdrom" ]; then
    # ERROR: no device with valid ISO found
fi
```

### Critical Limitation

The scanner **only checks whole block devices** (`/sys/block/*`):
- `/dev/sdb` ✓ checked
- `/dev/sr0` ✓ checked
- `/dev/sdb2` ✗ **NEVER checked** (partitions live at `/sys/block/sdb/sdb2`, not `/sys/block/sdb2`)

If the ISO is `dd`'d to a GPT partition, the scanner will never find it because:
1. `/dev/sdb` (whole device) has a GPT partition table, not iso9660
2. `/dev/sdb2` (ISO partition) is never enumerated

### answer.toml Fetch (separate concern)

The `auto-installer-mode.toml` on the ISO configures answer file fetching:
```toml
mode = "partition"
partition_label = "SYSTEM"
```
This happens AFTER ISO detection succeeds. The auto-installer looks for a partition labeled
"SYSTEM" and reads `answer.toml` from its root.

---

## NETSetup USB Boot Layout

```
USB Stick (GPT):
├── Partition 1 (gpt1): FAT32 ESP, label=SYSTEM, ~4GB
│   ├── /EFI/BOOT/BOOTX64.EFI (GRUB)
│   ├── /boot/grub/grub_void.cfg
│   ├── /boot/linux26, /boot/initrd.img (Void Linux)
│   ├── /LiveOS/squashfs.img (Void Linux rootfs)
│   ├── /NETSetup/NETSetup (binary)
│   ├── /NETSetup/Images/Proxmox/proxmox.iso
│   ├── /NETSetup/netsetup-overlay.img (cpio overlay)
│   └── /answer.toml
└── Partition 2 (gpt2): Raw ISO (dd'd by BootISOGrub2)
```

The ISO is `dd`'d to partition 2 as raw iso9660. GRUB boots the Proxmox kernel/initrd from
this partition. But the Proxmox scanner can't find it (see limitation above).

---

## NETSetup Workaround: Init Wrapper

### Overview

GRUB boots with extra kernel parameters:
```
ro ramdisk_size=16777216 rw quiet splash=silent proxmox-start-auto-installer \
    netsetup.isodev=/dev/sdb2 netsetup.os=Proxmox rdinit=/netsetup-init
```

The `rdinit=/netsetup-init` causes the kernel to run our wrapper instead of `/init`.

### Step 1: Wrapper script (`/netsetup-init`)

Delivered via cpio overlay appended to the initrd. Runs before the real `/init`:

```sh
#!/bin/sh
mount -t proc proc /proc 2>/dev/null
# Read netsetup.os= from cmdline
umount /proc 2>/dev/null

# Patch /init: insert check script after the reqid= line
# The initramfs is writable tmpfs — sed -i works
sed -i '/reqid=.*cat.*CDID_FN/r /netsetup-check.sh' /init

exec /init
```

### Step 2: Check script (`/netsetup-check.sh`)

Injected into `/init` after `reqid="$(cat "/$CDID_FN")"`. At this point `$CDID_FN` and
`$reqid` are defined, and `/proc` is mounted.

```sh
_ns_isodev=""
for _ns_par in $(cat /proc/cmdline); do
    case $_ns_par in
        netsetup.isodev=*) _ns_isodev="${_ns_par#netsetup.isodev=}" ;;
    esac
done
if [ -n "$_ns_isodev" ]; then
    echo "NETSetup: trying device '$_ns_isodev' for ISO"
    if mount -t iso9660 -o ro "$_ns_isodev" /mnt >/dev/null 2>&1; then
        if [ -r "/mnt/$CDID_FN" ] && [ "X$(cat "/mnt/$CDID_FN")" = "X$reqid" ]; then
            echo "NETSetup: found valid ISO on $_ns_isodev"
            cdrom=$_ns_isodev
        else
            echo "NETSetup: wrong cd-id on $_ns_isodev, continuing scan"
            umount /mnt
        fi
    else
        echo "NETSetup: failed to mount '$_ns_isodev', continuing scan"
    fi
fi
```

If `cdrom` is set, the scanning loop is skipped entirely.

### Why no RAM copy?

The ISO is on the USB stick. The installer wipes the **target disk**, not the USB.
The partition with the ISO stays intact throughout installation. Direct mount is sufficient.

RAM copy would only be needed if the ISO were on the same disk being installed to (non-USB path),
which is currently not supported.

### Sed anchor (verified for PVE 9.1)

Target line in `/init`:
```sh
reqid="$(cat "/$CDID_FN")"
```

Regex: `reqid=.*cat.*CDID_FN`

The `sed -i '/pattern/r file'` command inserts the contents of `/netsetup-check.sh`
immediately after the matching line. This ensures `$reqid` and `$CDID_FN` are available
when the check script runs.

### Fallback anchor

If the primary anchor is not found (future PVE version changes), the wrapper falls back to
inserting before the scanning loop: `sed -i '/for try in/r /netsetup-check.sh' /init`.
