Preserve cleanup after daemon restarts and harden OCI and tar imports against filenames that debugfs cannot encode safely. Mirror tap, loop, and dm teardown identity onto VM.Runtime, teach cleanup and reconcile to fall back to those persisted fields when handles.json is missing or corrupt, and clear the recovery state on stop, error, and delete paths. Reject debugfs-hostile entry names during flattening and in ApplyOwnership itself, then add regression coverage for corrupt handles.json recovery and unsafe import paths. Verified with targeted go tests, make lint-go, make lint-shell, and make build.
50 lines
2.3 KiB
Go
50 lines
2.3 KiB
Go
package model
|
|
|
|
// VMHandles captures the transient, per-boot kernel/process handles
|
|
// that banger obtains while starting a VM and releases when stopping
|
|
// it. Unlike VMRuntime (durable spec + identity + derived paths),
|
|
// VMHandles is the authoritative live-handle view while the daemon is
|
|
// up. On restart, the daemon rebuilds it from the OS plus the per-VM
|
|
// scratch file; teardown-critical fields are also mirrored onto
|
|
// VMRuntime so cleanup can still proceed if that scratch file is
|
|
// missing or corrupt.
|
|
//
|
|
// The daemon keeps an in-memory cache keyed by VM ID. Lifecycle
|
|
// transitions update the cache and a small `handles.json` scratch
|
|
// file in the VM's state directory; daemon startup reconciles
|
|
// by loading that file and verifying each handle against the live
|
|
// OS state. If anything is stale the VM is marked stopped and the
|
|
// cache entry is dropped.
|
|
//
|
|
// VMHandles itself never appears in the `vms` SQLite rows. Some fields
|
|
// are mirrored onto VMRuntime as crash-recovery fallback state, but the
|
|
// cache + scratch file remain the canonical live source.
|
|
type VMHandles struct {
|
|
// PID is the firecracker process PID. Zero means "not running
|
|
// (from our perspective)". Always verifiable via
|
|
// /proc/<pid>/cmdline matching the api socket path.
|
|
PID int `json:"pid,omitempty"`
|
|
|
|
// TapDevice is the kernel tap interface name (e.g. "tap-fc-0001")
|
|
// bound to the VM's virtio-net. Released on stop.
|
|
TapDevice string `json:"tap_device,omitempty"`
|
|
|
|
// BaseLoop and COWLoop are the two loop devices backing the
|
|
// dm-snapshot layer (read-only base = rootfs; read-write overlay
|
|
// = per-VM COW file). Released via losetup -d on stop.
|
|
BaseLoop string `json:"base_loop,omitempty"`
|
|
COWLoop string `json:"cow_loop,omitempty"`
|
|
|
|
// DMName is the device-mapper target name; deterministic from the
|
|
// VM ID (see dmsnap.SnapshotName). DMDev is the corresponding
|
|
// /dev/mapper/<name> path. Torn down by `dmsetup remove` on stop.
|
|
DMName string `json:"dm_name,omitempty"`
|
|
DMDev string `json:"dm_dev,omitempty"`
|
|
}
|
|
|
|
// IsZero reports whether every handle field is unset. Useful as a
|
|
// cheap "this VM has no kernel/process resources held on our behalf"
|
|
// check.
|
|
func (h VMHandles) IsZero() bool {
|
|
return h.PID == 0 && h.TapDevice == "" && h.BaseLoop == "" && h.COWLoop == "" && h.DMName == "" && h.DMDev == ""
|
|
}
|