# Test Case: Missing, Invalid, and Orphaned External Device

**Test ID**: TC118-device-not-found  
**Category**: Multi-Device / Error Handling
**Priority**: Critical

## Objective

Verify exact missing/short/duplicate/path errors, forced GEOM orphan behavior,
concurrent provider ownership, and complete vnode/cdev/consumer cleanup.

## Fixture qualification

Use the fresh `multi2` set.  The manifest must show `/cold-slot2.bin` has one
chunk with device ID 2; do not read it before the orphan step.
`multi2-slot2-short.blob` is exactly one block below slot 2's declared size.

## Mount-time errors

Set up the test directory and load the exact KLD once:

```sh
I=/root/repo22-g6/images
mkdir -p /mnt/g6 /mnt/g6b
kldload /root/repo22-g6/erofs.ko
```

Run each case separately with `truss`, detaching all attached md units after
each failure:

1. Split primary with no `device.N`: `ENXIO`.
2. Only `device.1`: `ENXIO`.
3. Valid slot 1 plus `multi2-slot2-short.blob`: `ENXIO`.
4. The same md91 assigned to slots 1 and 2: `EINVAL`.
5. Primary md90 also assigned as slot 1, with valid md92 for slot 2: `EINVAL`.

The short-media command is:

```sh
mdconfig -a -t vnode -f "$I/multi2-primary.erofs" -u 90
mdconfig -a -t vnode -f "$I/multi2-slot1.blob" -u 91
mdconfig -a -t vnode -f "$I/multi2-slot2-short.blob" -u 92
truss -f -o /tmp/tc118-short.truss mount -t erofs -o ro \
  -o device.1=/dev/md91 -o device.2=/dev/md92 /dev/md90 /mnt/g6
tail -20 /tmp/tc118-short.truss
mdconfig -d -u 92
mdconfig -d -u 91
mdconfig -d -u 90
```

After every case, `mount -p | awk '$3 == "erofs"'` and matching
`kern.geom.conftxt` consumer output must be empty.

## Forced orphan

Attach fresh providers and mount without reading `cold-slot2.bin`:

```sh
mdconfig -a -t vnode -f "$I/multi2-primary.erofs" -u 90
mdconfig -a -t vnode -f "$I/multi2-slot1.blob" -u 91
mdconfig -a -t vnode -f "$I/multi2-slot2.blob" -u 92
mount -t erofs -o ro -o device.1=/dev/md91 -o device.2=/dev/md92 \
  /dev/md90 /mnt/g6
truss -f -o /tmp/tc118-ebusy.truss mdconfig -d -u 92
tail -20 /tmp/tc118-ebusy.truss
mdconfig -d -u 92 -o force
truss -f -o /tmp/tc118-orphan-read.truss sha256 \
  /mnt/g6/cold-slot2.bin
tail -20 /tmp/tc118-orphan-read.truss
```

Normal detach must return `EBUSY`; forced detach must complete; the first cold
slot-2 read must return exactly `ENXIO` without panic, hang, or leaked vnode.

```sh
umount /mnt/g6
mdconfig -d -u 91
mdconfig -d -u 90
```

## Concurrent mount ownership

```sh
mdconfig -a -t vnode -f "$I/multi2-primary.erofs" -u 90
mdconfig -a -t vnode -f "$I/multi2-slot1.blob" -u 91
mdconfig -a -t vnode -f "$I/multi2-slot2.blob" -u 92
mount -t erofs -o ro -o device.1=/dev/md91 -o device.2=/dev/md92 \
  /dev/md90 /mnt/g6
truss -f -o /tmp/tc118-concurrent.truss mount -t erofs -o ro \
  -o device.1=/dev/md91 -o device.2=/dev/md92 /dev/md90 /mnt/g6b
tail -20 /tmp/tc118-concurrent.truss
sha256 /mnt/g6/tc006.bin
```

The second mount must return exactly `EBUSY`, and the first mount must remain
readable.  Record a kernel failure and do not edit `src/` if FreeBSD permits a
second mount of an already-open provider set.

```sh
umount /mnt/g6
mdconfig -d -u 92
mdconfig -d -u 91
mdconfig -d -u 90
```

## Pathname preservation

```sh
truss -f -o /tmp/tc118-primary-enoent.truss mount -t erofs -o ro \
  /no/such/repo22-g6-primary /mnt/g6
tail -20 /tmp/tc118-primary-enoent.truss
mdconfig -a -t vnode -f "$I/multi2-primary.erofs" -u 90
mdconfig -a -t vnode -f "$I/multi2-slot1.blob" -u 91
truss -f -o /tmp/tc118-slot-enoent.truss mount -t erofs -o ro \
  -o device.1=/dev/md91 -o device.2=/no/such/repo22-g6-slot2 \
  /dev/md90 /mnt/g6
tail -20 /tmp/tc118-slot-enoent.truss
```

Both operations must preserve exactly `ENOENT`; the external failure must also
release the already-open slot-1 consumer.

## Cleanup and zero-state check

```sh
mdconfig -d -u 91
mdconfig -d -u 90
kldunload erofs
mount -p | awk '$3 == "erofs" { print }'
mdconfig -l
kldstat -n erofs 2>/dev/null || true
sysctl -n kern.geom.conftxt | grep -E 'md9[0-2]|erofs' || true
dmesg | tail -120
```

All four final lifecycle outputs must be empty.  The dmesg suffix must contain
no panic, trap, assertion, watchdog, or unexpected EROFS error.
