Monorepo for Aesthetic.Computer aesthetic.computer
4
fork

Configure Feed

Select the types of activity you want to include in your feed.

install: 'w' at boot now handles fresh blank SSDs

`auto_install_to_hd` only ever scanned for existing partitions on each
candidate disk (nvme0n1, mmcblk0, sd*) — for a totally blank SSD that
has no partition table at all, every /dev/<disk>pN access(2) failed and
the disk was silently skipped. Boot-time `w` then aborted with "no
removable install source found" even though the user just plugged in
a destination disk to install onto.

Added a blank-disk pre-pass: if the parent disk node exists but it has
ZERO partitions, dd-zero the first 16 MiB and run sfdisk to create a
single GPT ESP (type C12A7328-…). Then fall through to the existing
per-partition install loop, which now sees p1 and uses the existing
rescue-mkfs path on pass 1 to format + populate it.

Mirrors the JS-side blank-disk handling that was just added in
flash_thread_fn (in the previous commit) — both code paths now do the
same partitioning so devices-view `w` (after notepat boots) and
boot-time `w` (during the 1-3s startup fade) work on the same disks.

os.mjs: also extended devices-view `w` to derive the parent disk path
from a partition target, so `w` can wipe + reinstall on already-
formatted disks too (refuses on the boot device).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

+81 -9
+25 -9
fedac/native/pieces/os.mjs
··· 232 232 } 233 233 return; 234 234 } 235 - // 'w' to wipe + format + install onto a blank/unformatted disk. 235 + // 'w' to wipe + format + install onto a target disk. Works on: 236 + // - blank disks (target.device is already a whole disk) 237 + // - formatted disks (target.device is a partition like 238 + // /dev/nvme0n1p1; we derive the parent /dev/nvme0n1) 239 + // Refuses on the currently-booted device — wiping the disk we're 240 + // running from would brick the live system mid-flash. 241 + // 236 242 // The C flash_thread_fn detects the whole-disk target and runs 237 - // sfdisk + mkfs.vfat before the normal copy step, so this is just 238 - // an OTA update that targets the parent disk node. 243 + // sfdisk + mkfs.vfat before the normal copy step. 239 244 if (e.is("keyboard:down:w")) { 240 245 const tgt = targets[deviceIdx]; 241 - if (tgt && tgt.blank) { 242 - cloneTarget = tgt; // re-use clone-confirm UI 243 - state = "clone-confirm"; // confirm before wiping 244 - sound?.synth({ type: "sawtooth", tone: 220, duration: 0.12, volume: 0.14, attack: 0.005, decay: 0.10 }); 245 - } else { 246 - // Not a blank disk — refuse with a low buzz. 246 + if (!tgt) return; 247 + const isBoot = tgt.device === bootDev; 248 + if (isBoot) { 249 + // Refuse — can't wipe the disk we're booting from. 247 250 sound?.synth({ type: "square", tone: 110, duration: 0.15, volume: 0.10, attack: 0.005, decay: 0.12 }); 251 + return; 248 252 } 253 + // Derive whole-disk path. Partition nodes look like: 254 + // /dev/nvme0n1p1 → /dev/nvme0n1 255 + // /dev/mmcblk0p1 → /dev/mmcblk0 256 + // /dev/sda1 → /dev/sda 257 + let wholeDisk = tgt.device; 258 + if (!tgt.blank) { 259 + // Strip trailing "p<N>" (NVMe/eMMC) or trailing digits (sd*). 260 + wholeDisk = wholeDisk.replace(/p?\d+$/, ""); 261 + } 262 + cloneTarget = { ...tgt, device: wholeDisk, blank: true }; // mark as wipe-target so the prompt says so 263 + state = "clone-confirm"; 264 + sound?.synth({ type: "sawtooth", tone: 220, duration: 0.12, volume: 0.14, attack: 0.005, decay: 0.10 }); 249 265 return; 250 266 } 251 267 if (e.is("keyboard:down:escape") || e.is("keyboard:down:backspace")) {
+56
fedac/native/src/ac-native.c
··· 1193 1193 if (rem == 1) continue; // removable = USB 1194 1194 } 1195 1195 1196 + // Blank-disk pre-pass: if the parent disk node exists but it has 1197 + // ZERO partitions (truly blank — fresh SSD the user just plugged 1198 + // in), the per-partition loop below would skip it silently. Run 1199 + // sfdisk first to lay down a single GPT ESP, then fall through to 1200 + // the normal install path which will format + populate p1. 1201 + { 1202 + char parent_path[32]; 1203 + snprintf(parent_path, sizeof(parent_path), "/dev/%s", blk); 1204 + char first_part[32]; 1205 + if (blk[0] == 'n' || strncmp(blk, "mmcblk", 6) == 0) 1206 + snprintf(first_part, sizeof(first_part), "/dev/%sp1", blk); 1207 + else 1208 + snprintf(first_part, sizeof(first_part), "/dev/%s1", blk); 1209 + int parent_exists = (access(parent_path, F_OK) == 0); 1210 + int has_any_part = 0; 1211 + if (parent_exists) { 1212 + for (int p = 1; p <= 16 && !has_any_part; p++) { 1213 + char dp[32]; 1214 + if (blk[0] == 'n' || strncmp(blk, "mmcblk", 6) == 0) 1215 + snprintf(dp, sizeof(dp), "/dev/%sp%d", blk, p); 1216 + else 1217 + snprintf(dp, sizeof(dp), "/dev/%s%d", blk, p); 1218 + if (access(dp, F_OK) == 0) has_any_part = 1; 1219 + } 1220 + } 1221 + if (parent_exists && !has_any_part) { 1222 + ac_log("[install] %s is blank (no partitions) — running sfdisk\n", parent_path); 1223 + // Wipe first 16 MiB so any leftover signatures don't confuse sfdisk. 1224 + char wcmd[256]; 1225 + snprintf(wcmd, sizeof(wcmd), 1226 + "dd if=/dev/zero of=%s bs=1M count=16 conv=fsync 2>&1 | head -3", 1227 + parent_path); 1228 + system(wcmd); 1229 + // Single GPT ESP spanning the disk. 1230 + char rcmd[512]; 1231 + snprintf(rcmd, sizeof(rcmd), 1232 + "{ echo 'label: gpt'; echo 'name=ACBOOT,type=C12A7328-F81F-11D2-BA4B-00A0C93EC93B'; } | " 1233 + "sfdisk --force --no-reread %s 2>&1", 1234 + parent_path); 1235 + int srrc = system(rcmd); 1236 + ac_log("[install] sfdisk on blank %s rc=%d\n", parent_path, srrc); 1237 + sync(); 1238 + usleep(500000); 1239 + blkrrpart_with_retry(parent_path, "/tmp/install-debug.log"); 1240 + // Wait for p1 to appear (up to 5s). 1241 + for (int wait = 0; wait < 50; wait++) { 1242 + if (access(first_part, F_OK) == 0) break; 1243 + usleep(100000); 1244 + } 1245 + ac_log("[install] blank-disk: %s exists=%d after sfdisk\n", 1246 + first_part, access(first_part, F_OK) == 0); 1247 + // Fall through — the per-partition loop below will now find 1248 + // p1 and the rescue-mkfs path will format it on pass 1. 1249 + } 1250 + } 1251 + 1196 1252 // Two-pass partition scan. Pass 0: probe p1..p16 for an existing 1197 1253 // vfat partition (non-destructive) — finds the Chromebook ESP at 1198 1254 // p12 before we would otherwise reformat p1 (stateful/ext4) on