Archived
fix(server): prevent zfs-init-tank from wiping pool on udev race
Check NixOS configurations / eval-hosts (pull_request) Successful in 10m46s
Check NixOS configurations / eval-hosts (pull_request) Successful in 10m46s
zpool create -f was called if `zpool import -d /dev/disk/by-id` failed, which could happen due to a race with systemd-udev-settle. The disk would then be visible by the time zpool create ran, silently destroying all data on an otherwise-intact pool. Fix: locate the data disk first, retry the import directly against it as a fallback, then check zdb -l for existing ZFS label metadata before concluding the disk is blank. Remove -f so zpool create refuses rather than overwrites if a pool is present. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ULXzafSDwGhmFGnn3LtDSQ
This commit is contained in:
@@ -44,14 +44,8 @@ in
|
|||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Pool exists on a device but not yet imported — let the standard
|
# Locate the data disk first — used for both the fallback import
|
||||||
# zfs-import-${poolName}.service handle it normally.
|
# attempt and, only if the disk is genuinely blank, pool creation.
|
||||||
if zpool import -d /dev/disk/by-id -N "${poolName}" 2>/dev/null; then
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
# No pool found at all. Create it on the Proxmox data disk (scsi1),
|
|
||||||
# which appears as /dev/disk/by-id/scsi-*drive-scsi1 inside the VM.
|
|
||||||
DATA_DISK=""
|
DATA_DISK=""
|
||||||
for candidate in /dev/disk/by-id/scsi-*drive-scsi1; do
|
for candidate in /dev/disk/by-id/scsi-*drive-scsi1; do
|
||||||
[[ "$candidate" == *-part* ]] && continue
|
[[ "$candidate" == *-part* ]] && continue
|
||||||
@@ -63,8 +57,31 @@ in
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Try importing via the by-id symlink directory first (normal path),
|
||||||
|
# then fall back to scanning the disk directly. The two-step exists
|
||||||
|
# because of a udev race: systemd-udev-settle.service can clear before
|
||||||
|
# /dev/disk/by-id/ entries are fully populated, causing the first
|
||||||
|
# import to fail even when the pool is intact on the disk.
|
||||||
|
if zpool import -d /dev/disk/by-id -N "${poolName}" 2>/dev/null; then
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
if zpool import -d "$DATA_DISK" -N "${poolName}" 2>/dev/null; then
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Both import attempts failed. Before creating a new pool, verify the
|
||||||
|
# disk is genuinely blank — if ZFS label metadata is present the import
|
||||||
|
# failed for some other reason and we must not clobber existing data.
|
||||||
|
if zdb -l "$DATA_DISK" 2>/dev/null | grep -q "name: '${poolName}'"; then
|
||||||
|
echo "zfs-init-${poolName}: $DATA_DISK has ZFS pool '${poolName}' metadata but import failed — refusing to overwrite existing data. Run 'zpool import -d $DATA_DISK ${poolName}' manually to investigate." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Disk is genuinely blank: create the pool. -f is intentionally
|
||||||
|
# omitted so that if we somehow reach this point with an existing pool
|
||||||
|
# on the disk, zpool refuses rather than silently destroying data.
|
||||||
echo "zfs-init-${poolName}: creating pool on $DATA_DISK"
|
echo "zfs-init-${poolName}: creating pool on $DATA_DISK"
|
||||||
zpool create -f "${poolName}" "$DATA_DISK"
|
zpool create "${poolName}" "$DATA_DISK"
|
||||||
${lib.concatMapStrings (ds: ''
|
${lib.concatMapStrings (ds: ''
|
||||||
zfs create "${poolName}/${ds}"
|
zfs create "${poolName}/${ds}"
|
||||||
'') poolDatasets}
|
'') poolDatasets}
|
||||||
|
|||||||
Reference in New Issue
Block a user