Monorepo for Aesthetic.Computer aesthetic.computer
4
fork

Configure Feed

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

feat: add hybrid Intel Mac boot media

+545 -143
+1 -1
fedac/native/Dockerfile.builder
··· 21 21 iwlwifi-mvm-firmware wireless-regdb \ 22 22 linux-firmware linux-firmware-whence \ 23 23 intel-gpu-firmware \ 24 - busybox mtools dosfstools \ 24 + busybox mtools dosfstools hfsplus-tools hfsutils gdisk \ 25 25 mesa-libgbm mesa-libgbm-devel \ 26 26 mesa-libEGL mesa-libEGL-devel \ 27 27 mesa-libGLES mesa-libGLES-devel \
+13
fedac/native/Dockerfile.flash-helper
··· 1 + FROM fedora:43 2 + 3 + RUN dnf install -y --setopt=install_weak_deps=False \ 4 + dosfstools util-linux mtools hfsplus-tools mactel-boot \ 5 + xorriso gdisk jq \ 6 + && dnf clean all && rm -rf /var/cache/dnf 7 + 8 + COPY fedac/native/scripts/media-layout.sh /usr/local/lib/ac-media-layout.sh 9 + COPY fedac/native/scripts/flash-helper-runner.sh /usr/local/bin/ac-os-flash-helper 10 + 11 + RUN chmod +x /usr/local/bin/ac-os-flash-helper 12 + 13 + ENTRYPOINT ["/usr/local/bin/ac-os-flash-helper"]
+86 -107
fedac/native/ac-os
··· 14 14 BUILD_DIR="${SCRIPT_DIR}/build" 15 15 VMLINUZ="${BUILD_DIR}/vmlinuz" 16 16 INITRAMFS_ROOT="${BUILD_DIR}/initramfs-root" 17 + AC_MEDIA_NATIVE_DIR="${SCRIPT_DIR}" 18 + MEDIA_LAYOUT_LIB="${SCRIPT_DIR}/scripts/media-layout.sh" 19 + MEDIA_HELPER_IMAGE="${AC_MEDIA_HELPER_IMAGE:-ac-os-media-helper:latest}" 20 + MEDIA_HELPER_DOCKERFILE="${SCRIPT_DIR}/Dockerfile.flash-helper" 17 21 18 22 CMD="${1:-build}" 19 23 ··· 24 28 25 29 log() { echo -e "\033[0;36m[ac-os]\033[0m $*"; } 26 30 err() { echo -e "\033[0;31m[ac-os]\033[0m $*" >&2; } 31 + 32 + # shellcheck source=/workspaces/aesthetic-computer/fedac/native/scripts/media-layout.sh 33 + source "${MEDIA_LAYOUT_LIB}" 27 34 28 35 # Load env vars (MongoDB, etc.) if not already set 29 36 if [ -z "${MONGODB_CONNECTION_STRING:-}" ] && [ -f "/home/me/envs/devcontainer.env" ]; then ··· 592 599 } 593 600 594 601 find_usb_dev() { 602 + if [ -n "${AC_USB_DEV:-}" ]; then 603 + if [ ! -b "${AC_USB_DEV}" ]; then 604 + err "AC_USB_DEV is not a block device: ${AC_USB_DEV}" 605 + return 1 606 + fi 607 + echo "${AC_USB_DEV}" 608 + return 0 609 + fi 610 + 595 611 # Find removable USB block device via sysfs 596 - for dev in sda sdb sdc; do 612 + for dev in sda sdb sdc sdd sde; do 597 613 if [ -e "/sys/block/${dev}/removable" ]; then 598 614 local rem=$(cat "/sys/block/${dev}/removable" 2>/dev/null) 599 615 if [ "${rem}" = "1" ] && [ -e "/sys/block/${dev}" ]; then ··· 602 618 fi 603 619 fi 604 620 done 605 - # Fallback: check sysfs even if not marked removable 606 - if [ -e "/sys/block/sda" ]; then 607 - echo "/dev/sda" 621 + return 1 622 + } 623 + 624 + ensure_media_helper_image() { 625 + if sudo docker image inspect "${MEDIA_HELPER_IMAGE}" >/dev/null 2>&1; then 608 626 return 0 609 627 fi 610 - return 1 628 + 629 + log "Building media helper image (${MEDIA_HELPER_IMAGE})..." 630 + local repo_root 631 + repo_root="$(cd "${SCRIPT_DIR}/../.." && pwd)" 632 + sudo docker build \ 633 + -t "${MEDIA_HELPER_IMAGE}" \ 634 + -f "${MEDIA_HELPER_DOCKERFILE}" \ 635 + "${repo_root}" >/dev/null 611 636 } 612 637 613 638 require_login() { ··· 639 664 export AC_USER_SUB="${USER_SUB}" 640 665 export AC_USER_HANDLE="${USER_HANDLE}" 641 666 export AC_USER_EMAIL="${USER_EMAIL}" 642 - log "Authenticated: @${USER_HANDLE} (${USER_SUB})" 667 + log "Authenticated: @${USER_HANDLE}" 643 668 } 644 669 645 670 flash_usb() { 646 671 local USB_DEV 672 + local USB_CONFIG 673 + local AC_ACCESS_TOKEN 674 + local CLAUDE_CREDS="" 675 + local CLAUDE_STATE="" 676 + local MEDIA_TMP 677 + local CONFIG_FILE 678 + local STAGE_ROOT 679 + local CONTAINER 680 + 647 681 USB_DEV="$(find_usb_dev)" || { err "No USB device found"; exit 1; } 682 + log "Flashing to ${USB_DEV}..." 648 683 649 - log "Flashing to ${USB_DEV}1..." 684 + MEDIA_TMP="$(mktemp -d /tmp/ac-media.XXXXXX)" 685 + CONFIG_FILE="${MEDIA_TMP}/config.json" 686 + STAGE_ROOT="${MEDIA_TMP}/staged-root" 687 + cleanup_flash_usb() { 688 + if [ -n "${CONTAINER:-}" ]; then 689 + sudo docker rm -f "${CONTAINER}" >/dev/null 2>&1 || true 690 + fi 691 + rm -rf "${MEDIA_TMP}" 692 + } 650 693 651 - # Build config.json from authenticated user (or preserve existing) 652 - local USB_CONFIG 694 + # Build config.json from authenticated user. 653 695 if [ -n "${AC_USER_HANDLE:-}" ]; then 654 - local AC_ACCESS_TOKEN=$(node -e "const t=JSON.parse(require('fs').readFileSync('${HOME}/.ac-token','utf8')); console.log(t.access_token || '')" 2>/dev/null) 655 - # Read Claude Code OAuth credentials + state if available 656 - local CLAUDE_CREDS="" 657 - local CLAUDE_STATE="" 696 + AC_ACCESS_TOKEN=$(node -e "const t=JSON.parse(require('fs').readFileSync('${HOME}/.ac-token','utf8')); console.log(t.access_token || '')" 2>/dev/null) 658 697 if [ -f "${HOME}/.claude/.credentials.json" ]; then 659 698 CLAUDE_CREDS=$(cat "${HOME}/.claude/.credentials.json" 2>/dev/null) 660 699 fi ··· 678 717 console.log(JSON.stringify(cfg)); 679 718 " "${CLAUDE_CREDS}" "${CLAUDE_STATE}" 2>/dev/null) 680 719 else 681 - USB_CONFIG='{"handle": "unknown"}' 720 + USB_CONFIG='{"handle":"unknown"}' 682 721 fi 683 - log "USB config: ${USB_CONFIG}" 722 + printf '%s' "${USB_CONFIG}" > "${CONFIG_FILE}" 723 + log "USB config: $(ac_media_summarize_config_file "${CONFIG_FILE}")" 684 724 685 - # Write config to temp file (avoids shell quoting issues with apostrophes in JSON) 686 - echo "${USB_CONFIG}" > /tmp/usb-config.json 725 + ac_media_stage_boot_tree "${STAGE_ROOT}" "${VMLINUZ}" "${CONFIG_FILE}" 687 726 688 - # Use docker cp + privileged container (bind mounts fail in devcontainers) 689 - local CONTAINER 690 - CONTAINER=$(sudo docker create --rm --privileged -v /dev:/dev alpine:latest sh -c " 691 - set -e 692 - apk add --quiet dosfstools util-linux mtools 693 - 694 - # Preserve config.json if it exists on the USB 695 - CONFIG_BAK='' 696 - mkdir -p /mnt/usb 697 - if mount ${USB_DEV}1 /mnt/usb 2>/dev/null; then 698 - if [ -f /mnt/usb/config.json ]; then 699 - CONFIG_BAK=\$(cat /mnt/usb/config.json) 700 - echo \"Saved config.json\" 701 - fi 702 - umount /mnt/usb 703 - fi 704 - 705 - # Always clean-slate: wipe partition table, create fresh GPT + EFI partition 706 - echo 'Wiping and re-partitioning ${USB_DEV}...' 707 - dd if=/dev/zero of=${USB_DEV} bs=1M count=10 2>/dev/null 708 - sfdisk --force ${USB_DEV} <<PARTEOF 709 - label: gpt 710 - type=C12A7328-F81F-11D2-BA4B-00A0C93EC93B, size=1024M 711 - PARTEOF 712 - sleep 2 713 - partprobe ${USB_DEV} 2>/dev/null || true 714 - sleep 1 715 - 716 - # Format fresh FAT32 717 - mkfs.vfat -F 32 -n ACBOOT ${USB_DEV}1 718 - 719 - # Mount and copy kernel 720 - mount ${USB_DEV}1 /mnt/usb 721 - mkdir -p /mnt/usb/EFI/BOOT 722 - cp /tmp/vmlinuz /mnt/usb/EFI/BOOT/BOOTX64.EFI 723 - 724 - # Write config.json with authenticated user data 725 - cat /tmp/usb-config.json > /mnt/usb/config.json 726 - echo \"Wrote config.json\" 727 - 728 - sync && sleep 2 && sync 729 - echo '=== Verify ===' 730 - sha256sum /mnt/usb/EFI/BOOT/BOOTX64.EFI /tmp/vmlinuz 731 - cat /mnt/usb/config.json 732 - umount /mnt/usb 733 - echo 'Flashed!' 734 - ") 735 - sudo docker cp "${VMLINUZ}" "${CONTAINER}:/tmp/vmlinuz" 736 - sudo docker cp /tmp/usb-config.json "${CONTAINER}:/tmp/usb-config.json" 737 - sudo docker start -a "${CONTAINER}" 2>&1 727 + ensure_media_helper_image 728 + CONTAINER=$(sudo docker create --rm --privileged -v /dev:/dev \ 729 + --entrypoint /bin/bash "${MEDIA_HELPER_IMAGE}" \ 730 + -lc "exec /usr/local/bin/ac-os-flash-helper '${USB_DEV}' /tmp/staged-root") 731 + sudo docker cp "${STAGE_ROOT}" "${CONTAINER}:/tmp/" 732 + if sudo docker start -a "${CONTAINER}"; then 733 + : 734 + else 735 + local rc=$? 736 + cleanup_flash_usb 737 + return "${rc}" 738 + fi 739 + log "Verified config: $(ac_media_summarize_config_file "${CONFIG_FILE}")" 740 + cleanup_flash_usb 738 741 } 739 742 740 743 load_vault_creds() { ··· 804 807 } 805 808 806 809 generate_template_iso() { 807 - # Create an EFI-bootable hybrid ISO with the kernel + default config. 808 - # Uses xorriso with El Torito EFI boot — compatible with Fedora Media Writer, 809 - # Balena Etcher, dd, and standard ISO mounting. 810 - # The config.json is padded to 4096 bytes for byte-patching by oven. 810 + # Create a hybrid ISO from the staged chainloader-first boot tree. 811 + # The config.json uses the existing 32KB identity block so oven can patch it. 811 812 local ISO="/tmp/ac-native.iso" 812 813 local STAGING="/tmp/ac-iso-staging" 813 814 local EFI_IMG="/tmp/ac-efi.img" 814 - 815 - local KERNEL_SIZE=$(stat -c%s "${VMLINUZ}") 816 - # EFI image needs to fit kernel + config + FAT overhead 817 - local EFI_MB=$(( (KERNEL_SIZE / 1048576) + 4 )) 815 + local CONFIG_TMP="/tmp/ac-identity-config.json" 818 816 819 817 log "Generating template .iso..." >&2 820 818 821 819 # Clean staging area 822 - rm -rf "${STAGING}" "${EFI_IMG}" "${ISO}" 823 - mkdir -p "${STAGING}" 820 + rm -rf "${STAGING}" "${EFI_IMG}" "${ISO}" "${CONFIG_TMP}" 824 821 825 822 # Create identity block (32KB, zero-padded) in ISO root. 826 823 # The block starts with a marker so the edge worker can verify alignment, 827 824 # followed by JSON config, zero-padded to exactly 32768 bytes. 828 - local IDENTITY_SIZE=32768 829 - local IDENTITY_MARKER="AC_IDENTITY_BLOCK_V1" 830 - local CONFIG_TMP="${STAGING}/config.json" 831 - # Marker (20 bytes) + newline + JSON + zero-padding to 32KB 832 - printf '%s\n' "${IDENTITY_MARKER}" > "${CONFIG_TMP}" 833 - printf '{"handle":"","piece":"notepat","sub":"","email":""}' >> "${CONFIG_TMP}" 834 - local CURRENT_SIZE=$(stat -c%s "${CONFIG_TMP}") 835 - local PAD_SIZE=$((IDENTITY_SIZE - CURRENT_SIZE)) 836 - dd if=/dev/zero bs=1 count=${PAD_SIZE} 2>/dev/null >> "${CONFIG_TMP}" 825 + local IDENTITY_SIZE 826 + local IDENTITY_MARKER 827 + IDENTITY_SIZE="$(ac_media_identity_size)" 828 + IDENTITY_MARKER="$(ac_media_identity_marker)" 837 829 838 - # Create FAT32 EFI System Partition image for El Torito boot 839 - dd if=/dev/zero of="${EFI_IMG}" bs=1M count=${EFI_MB} status=none 840 - mkfs.vfat -F 32 -n AC-NATIVE "${EFI_IMG}" >/dev/null 2>&1 841 - export MTOOLS_SKIP_CHECK=1 842 - mmd -i "${EFI_IMG}" ::EFI ::EFI/BOOT 2>/dev/null 843 - mcopy -o -i "${EFI_IMG}" "${VMLINUZ}" ::EFI/BOOT/BOOTX64.EFI 844 - mcopy -o -i "${EFI_IMG}" "${CONFIG_TMP}" ::config.json 830 + ac_media_write_identity_config "${CONFIG_TMP}" 831 + ac_media_stage_boot_tree "${STAGING}" "${VMLINUZ}" "${CONFIG_TMP}" 832 + ac_media_create_fat_image "${STAGING}" "${EFI_IMG}" "AC_NATIVE" 833 + ac_media_build_hybrid_iso "${STAGING}" "${EFI_IMG}" "${ISO}" "AC_NATIVE" 845 834 846 - # Build hybrid ISO with xorriso (EFI boot via El Torito + GPT for dd/USB) 847 - xorriso -as mkisofs \ 848 - -o "${ISO}" \ 849 - -V "AC_NATIVE" \ 850 - -J -joliet-long \ 851 - -append_partition 2 0xef "${EFI_IMG}" \ 852 - -e --interval:appended_partition_2:all:: \ 853 - -no-emul-boot \ 854 - -isohybrid-gpt-basdat \ 855 - "${STAGING}" 2>&1 | grep -v "^xorriso\|^Drive\|^Media" >&2 || true 856 - 857 - rm -rf "${STAGING}" "${EFI_IMG}" 835 + rm -rf "${STAGING}" "${EFI_IMG}" "${CONFIG_TMP}" 858 836 859 837 local ISO_BYTES=$(stat -c%s "${ISO}") 860 838 local ISO_MB=$(( ISO_BYTES / 1048576 )) ··· 1128 1106 VERSION_INFO=$(curl -sf "${VERSION_URL}") || { err "Failed to fetch version from CDN"; exit 1; } 1129 1107 local BUILD_NAME=$(echo "${VERSION_INFO}" | head -1) 1130 1108 local KERNEL_SIZE=$(echo "${VERSION_INFO}" | tail -1) 1131 - log "Latest OTA: ${AC_BUILD_NAME} (${KERNEL_SIZE} bytes)" 1109 + log "Latest OTA: ${BUILD_NAME} (${KERNEL_SIZE} bytes)" 1132 1110 1133 1111 # Download kernel (use /tmp — build dir may be a broken symlink in devcontainers) 1134 1112 local PULL_DIR="/tmp/ac-os-pull" ··· 1155 1133 # Override VMLINUZ and build name so flash_usb uses the downloaded kernel 1156 1134 # (not the locally-generated name from scripts/build-name.sh) 1157 1135 VMLINUZ="${PULLED_KERNEL}" 1158 - export AC_BUILD_NAME="${AC_BUILD_NAME}" 1136 + export AC_BUILD_NAME="${BUILD_NAME}" 1159 1137 log "Ready to flash: ${AC_BUILD_NAME}" 1160 1138 } 1161 1139 ··· 1248 1226 log "Smoke test passed — USB flashed!" 1249 1227 ;; 1250 1228 pull) 1229 + require_login 1251 1230 pull_ota 1252 1231 flash_usb 1253 1232 ;;
+13 -27
fedac/native/docker-build.sh
··· 10 10 BUILD="$NATIVE/build" 11 11 KVER="${KERNEL_VERSION:-6.19.9}" 12 12 KMAJOR="${KVER%%.*}" 13 + MEDIA_LAYOUT_LIB="$NATIVE/scripts/media-layout.sh" 13 14 14 15 log() { echo -e "\033[0;36m[ac-os]\033[0m $*"; } 15 16 err() { echo -e "\033[0;31m[ac-os]\033[0m $*" >&2; } 17 + 18 + # shellcheck source=/workspaces/aesthetic-computer/fedac/native/scripts/media-layout.sh 19 + source "$MEDIA_LAYOUT_LIB" 16 20 17 21 # Always build in /tmp inside the container to avoid bind-mount permission issues 18 22 BUILD="/tmp/ac-build" ··· 571 575 ISO_DIR="$BUILD/iso-root" 572 576 EFI_IMG="$BUILD/efi.img" 573 577 ISO_OUT="$BUILD/ac-os.iso" 578 + CONFIG_TMP="$BUILD/identity-config.json" 574 579 575 - rm -rf "$ISO_DIR" "$EFI_IMG" 576 - mkdir -p "$ISO_DIR/EFI/BOOT" 577 - 578 - # Copy kernel as UEFI boot binary 579 - cp "$BUILD/vmlinuz" "$ISO_DIR/EFI/BOOT/BOOTX64.EFI" 580 - 581 - # Create config.json with patchable marker (browser replaces this) 582 - CONFIG_MARKER='{"handle":"","piece":"notepat","sub":"","email":""}' 583 - CONFIG_PAD=4096 584 - printf "%-${CONFIG_PAD}s" "$CONFIG_MARKER" > "$ISO_DIR/config.json" 580 + rm -rf "$ISO_DIR" "$EFI_IMG" "$CONFIG_TMP" 585 581 586 - # Create FAT32 EFI boot image 587 - EFI_SIZE_MB=$(( (VMLINUZ_SIZE + CONFIG_PAD + 1048576) / 1048576 + 4 )) 588 - dd if=/dev/zero of="$EFI_IMG" bs=1M count=$EFI_SIZE_MB 2>/dev/null 589 - mkfs.fat -F 32 "$EFI_IMG" >/dev/null 2>&1 590 - mmd -i "$EFI_IMG" ::EFI ::EFI/BOOT 2>/dev/null 591 - mcopy -i "$EFI_IMG" "$BUILD/vmlinuz" ::EFI/BOOT/BOOTX64.EFI 592 - mcopy -i "$EFI_IMG" "$ISO_DIR/config.json" ::config.json 582 + ac_media_write_identity_config "$CONFIG_TMP" 583 + ac_media_stage_boot_tree "$ISO_DIR" "$BUILD/vmlinuz" "$CONFIG_TMP" 584 + ac_media_create_fat_image "$ISO_DIR" "$EFI_IMG" "AC_NATIVE" 593 585 594 586 # Build hybrid ISO with xorriso (EFI boot via El Torito + GPT for dd/USB) 595 - # Uses -append_partition to attach the EFI image, matching ac-os generate_template_iso(). 587 + # Uses the same chainloader-first staged tree as ac-os generate_template_iso(). 596 588 if command -v xorriso &>/dev/null; then 597 - xorriso -as mkisofs \ 598 - -o "$ISO_OUT" \ 599 - -V "AC_NATIVE" \ 600 - -J -joliet-long \ 601 - -append_partition 2 0xef "$EFI_IMG" \ 602 - -e --interval:appended_partition_2:all:: \ 603 - -no-emul-boot \ 604 - -isohybrid-gpt-basdat \ 605 - "$ISO_DIR" 2>&1 | grep -v "^xorriso\|^Drive\|^Media" || true 589 + ac_media_build_hybrid_iso "$ISO_DIR" "$EFI_IMG" "$ISO_OUT" "AC_NATIVE" 606 590 else 607 591 # Fallback: raw EFI disk image (dd-able) 608 592 log " No xorriso — creating raw disk image" 609 593 cp "$EFI_IMG" "$ISO_OUT" 610 594 fi 595 + 596 + rm -f "$CONFIG_TMP" 611 597 612 598 if [ -f "$ISO_OUT" ]; then 613 599 ISO_SIZE=$(stat -c%s "$ISO_OUT")
+198
fedac/native/scripts/flash-helper-runner.sh
··· 1 + #!/bin/bash 2 + set -euo pipefail 3 + 4 + source /usr/local/lib/ac-media-layout.sh 5 + 6 + USB_DEV="${1:?usage: flash-helper-runner.sh <usb-dev> <staged-root>}" 7 + STAGED_ROOT="${2:?usage: flash-helper-runner.sh <usb-dev> <staged-root>}" 8 + 9 + log() { echo "[flash-helper] $*"; } 10 + err() { echo "[flash-helper] $*" >&2; } 11 + 12 + part_path() { 13 + local dev="$1" 14 + local idx="$2" 15 + if [[ "${dev}" =~ [0-9]$ ]]; then 16 + printf '%sp%s\n' "${dev}" "${idx}" 17 + else 18 + printf '%s%s\n' "${dev}" "${idx}" 19 + fi 20 + } 21 + 22 + mount_vfat_partition() { 23 + local dev="$1" 24 + local mountpoint="$2" 25 + mkdir -p "${mountpoint}" 26 + mount -t vfat "${dev}" "${mountpoint}" 27 + } 28 + 29 + mount_hfs_partition() { 30 + local dev="$1" 31 + local mountpoint="$2" 32 + mkdir -p "${mountpoint}" 33 + if mount -t hfsplus "${dev}" "${mountpoint}" 2>/dev/null; then 34 + return 0 35 + fi 36 + err "Failed to mount ${dev} as hfsplus" 37 + err "Available filesystems:" 38 + cat /proc/filesystems >&2 || true 39 + return 1 40 + } 41 + 42 + wait_for_partition() { 43 + local part="$1" 44 + for _ in $(seq 1 40); do 45 + if [ -b "${part}" ]; then 46 + return 0 47 + fi 48 + sleep 0.25 49 + done 50 + err "Partition did not appear: ${part}" 51 + return 1 52 + } 53 + 54 + cleanup() { 55 + umount /mnt/ac-main 2>/dev/null || true 56 + umount /mnt/ac-efi 2>/dev/null || true 57 + umount /mnt/ac-mac 2>/dev/null || true 58 + } 59 + trap cleanup EXIT 60 + 61 + copy_boot_tree_to_vfat() { 62 + local dev="$1" 63 + local mountpoint="$2" 64 + local include_config="${3:-no}" 65 + 66 + mount_vfat_partition "${dev}" "${mountpoint}" 67 + mkdir -p "${mountpoint}/EFI/BOOT" 68 + cp "${STAGED_ROOT}/EFI/BOOT/BOOTX64.EFI" "${mountpoint}/EFI/BOOT/BOOTX64.EFI" 69 + cp "${STAGED_ROOT}/EFI/BOOT/KERNEL.EFI" "${mountpoint}/EFI/BOOT/KERNEL.EFI" 70 + if [ "${include_config}" = "yes" ]; then 71 + cp "${STAGED_ROOT}/config.json" "${mountpoint}/config.json" 72 + fi 73 + sync 74 + umount "${mountpoint}" 75 + } 76 + 77 + populate_mac_partition() { 78 + local dev="$1" 79 + local mountpoint="$2" 80 + 81 + mount_hfs_partition "${dev}" "${mountpoint}" 82 + 83 + mkdir -p "${mountpoint}/EFI/BOOT" 84 + mkdir -p "${mountpoint}/System/Library/CoreServices" 85 + 86 + cp "${STAGED_ROOT}/EFI/BOOT/BOOTX64.EFI" "${mountpoint}/EFI/BOOT/BOOTX64.EFI" 87 + cp "${STAGED_ROOT}/EFI/BOOT/KERNEL.EFI" "${mountpoint}/EFI/BOOT/KERNEL.EFI" 88 + cp "${STAGED_ROOT}/EFI/BOOT/BOOTX64.EFI" "${mountpoint}/System/Library/CoreServices/boot.efi" 89 + 90 + if [ -f /boot/efi/System/Library/CoreServices/SystemVersion.plist ]; then 91 + cp /boot/efi/System/Library/CoreServices/SystemVersion.plist \ 92 + "${mountpoint}/System/Library/CoreServices/SystemVersion.plist" 93 + fi 94 + if [ -f /boot/efi/.VolumeIcon.icns ]; then 95 + cp /boot/efi/.VolumeIcon.icns "${mountpoint}/.VolumeIcon.icns" 96 + fi 97 + if [ -f /boot/efi/mach_kernel ]; then 98 + cp /boot/efi/mach_kernel "${mountpoint}/mach_kernel" 99 + fi 100 + 101 + hfs-bless "${mountpoint}/EFI/BOOT/BOOTX64.EFI" 102 + sync 103 + umount "${mountpoint}" 104 + } 105 + 106 + verify_partition_layout() { 107 + log "Partition layout:" 108 + fdisk -l "${USB_DEV}" 2>/dev/null || true 109 + blkid "${USB_DEV}"* 2>/dev/null || true 110 + sgdisk --print-mbr "${USB_DEV}" 2>/dev/null || true 111 + } 112 + 113 + verify_written_media() { 114 + local main_part="$1" 115 + local efi_part="$2" 116 + local mac_part="$3" 117 + 118 + mount_vfat_partition "${main_part}" /mnt/ac-main 119 + log "Main config: $(ac_media_summarize_config_file /mnt/ac-main/config.json || echo config=unreadable)" 120 + sha256sum /mnt/ac-main/EFI/BOOT/KERNEL.EFI "${STAGED_ROOT}/EFI/BOOT/KERNEL.EFI" 121 + umount /mnt/ac-main 122 + 123 + mount_vfat_partition "${efi_part}" /mnt/ac-efi 124 + sha256sum /mnt/ac-efi/EFI/BOOT/KERNEL.EFI "${STAGED_ROOT}/EFI/BOOT/KERNEL.EFI" 125 + umount /mnt/ac-efi 126 + 127 + mount_hfs_partition "${mac_part}" /mnt/ac-mac 128 + sha256sum /mnt/ac-mac/EFI/BOOT/KERNEL.EFI "${STAGED_ROOT}/EFI/BOOT/KERNEL.EFI" 129 + test -f /mnt/ac-mac/System/Library/CoreServices/boot.efi 130 + umount /mnt/ac-mac 131 + } 132 + 133 + if [ ! -b "${USB_DEV}" ]; then 134 + err "${USB_DEV} is not a block device" 135 + exit 1 136 + fi 137 + 138 + if [ ! -d "${STAGED_ROOT}/EFI/BOOT" ] || [ ! -f "${STAGED_ROOT}/config.json" ]; then 139 + err "Staged boot tree missing expected files at ${STAGED_ROOT}" 140 + exit 1 141 + fi 142 + 143 + MAIN_PART="$(part_path "${USB_DEV}" 1)" 144 + EFI_PART="$(part_path "${USB_DEV}" 2)" 145 + MAC_PART="$(part_path "${USB_DEV}" 3)" 146 + 147 + STAGE_MB=$(ac_media_stage_tree_size_mib "${STAGED_ROOT}") 148 + EFI_MB=$(( STAGE_MB + 96 )) 149 + MAC_MB=$(( STAGE_MB + 128 )) 150 + DISK_MB=$(( $(blockdev --getsize64 "${USB_DEV}") / 1048576 )) 151 + MAIN_MB=$(( DISK_MB - EFI_MB - MAC_MB - 32 )) 152 + 153 + if [ "${MAIN_MB}" -lt $(( STAGE_MB + 128 )) ]; then 154 + err "USB device is too small for hybrid layout (${DISK_MB}MB total)" 155 + exit 1 156 + fi 157 + 158 + log "Preparing hybrid Intel Mac media on ${USB_DEV}" 159 + log "Sizes: main=${MAIN_MB}MB efi=${EFI_MB}MB mac=${MAC_MB}MB" 160 + 161 + wipefs -a "${USB_DEV}" >/dev/null 2>&1 || true 162 + sgdisk --zap-all "${USB_DEV}" >/dev/null 2>&1 || true 163 + dd if=/dev/zero of="${USB_DEV}" bs=1M count=16 status=none 164 + 165 + cat <<PART_EOF | sfdisk --force "${USB_DEV}" 166 + label: gpt 167 + size=${MAIN_MB}M, type=EBD0A0A2-B9E5-4433-87C0-68B6B72699C7, name="ACBOOT" 168 + size=${EFI_MB}M, type=C12A7328-F81F-11D2-BA4B-00A0C93EC93B, name="ACEFI" 169 + size=${MAC_MB}M, type=48465300-0000-11AA-AA11-00306543ECAC, name="AC-MAC" 170 + PART_EOF 171 + 172 + partprobe "${USB_DEV}" 2>/dev/null || true 173 + sleep 2 174 + 175 + wait_for_partition "${MAIN_PART}" 176 + wait_for_partition "${EFI_PART}" 177 + wait_for_partition "${MAC_PART}" 178 + 179 + mkfs.vfat -F 32 -n ACBOOT "${MAIN_PART}" >/dev/null 180 + mkfs.vfat -F 32 -n ACEFI "${EFI_PART}" >/dev/null 181 + mkfs.hfsplus -v AC-MAC "${MAC_PART}" >/dev/null 182 + 183 + sgdisk --attributes=1:set:2 "${USB_DEV}" >/dev/null 2>&1 || true 184 + sgdisk --hybrid 1:2:3 "${USB_DEV}" >/dev/null 2>&1 || true 185 + partprobe "${USB_DEV}" 2>/dev/null || true 186 + 187 + copy_boot_tree_to_vfat "${MAIN_PART}" /mnt/ac-main yes 188 + copy_boot_tree_to_vfat "${EFI_PART}" /mnt/ac-efi no 189 + populate_mac_partition "${MAC_PART}" /mnt/ac-mac 190 + 191 + sync 192 + sleep 2 193 + sync 194 + 195 + verify_partition_layout 196 + verify_written_media "${MAIN_PART}" "${EFI_PART}" "${MAC_PART}" 197 + 198 + echo "Flashed!"
+196
fedac/native/scripts/media-layout.sh
··· 1 + #!/bin/bash 2 + 3 + # Shared helpers for AC Native boot media layouts. 4 + 5 + MEDIA_LAYOUT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" 6 + MEDIA_LAYOUT_ROOT="$(cd "${MEDIA_LAYOUT_DIR}/.." && pwd)" 7 + 8 + ac_media_identity_marker() { 9 + printf '%s\n' "AC_IDENTITY_BLOCK_V1" 10 + } 11 + 12 + ac_media_identity_size() { 13 + printf '%s\n' "32768" 14 + } 15 + 16 + ac_media_default_identity_json() { 17 + printf '%s' '{"handle":"","piece":"notepat","sub":"","email":""}' 18 + } 19 + 20 + ac_media_bootloader_path() { 21 + local splash="${AC_SPLASH_EFI:-${MEDIA_LAYOUT_ROOT}/bootloader/splash.efi}" 22 + if [ ! -f "${splash}" ]; then 23 + echo "Missing splash bootloader: ${splash}" >&2 24 + return 1 25 + fi 26 + printf '%s\n' "${splash}" 27 + } 28 + 29 + ac_media_stage_tree_size_mib() { 30 + local stage_root="$1" 31 + local size_mb 32 + size_mb=$(du -sm "${stage_root}" 2>/dev/null | awk '{print $1}') 33 + printf '%s\n' "${size_mb:-1}" 34 + } 35 + 36 + ac_stage_tree_size_mb() { 37 + ac_media_stage_tree_size_mib "$@" 38 + } 39 + 40 + ac_media_file_mib() { 41 + local file_path="$1" 42 + local bytes 43 + bytes=$(stat -c%s "${file_path}") 44 + printf '%s\n' $(( (bytes + 1048575) / 1048576 )) 45 + } 46 + 47 + ac_write_identity_block_config() { 48 + local out_path="$1" 49 + local marker="$2" 50 + local total_bytes="$3" 51 + local json_payload="$4" 52 + 53 + mkdir -p "$(dirname "${out_path}")" 54 + printf '%s\n' "${marker}" > "${out_path}" 55 + printf '%s' "${json_payload}" >> "${out_path}" 56 + 57 + local current_size 58 + current_size=$(stat -c%s "${out_path}") 59 + if [ "${current_size}" -gt "${total_bytes}" ]; then 60 + echo "Identity block payload exceeds ${total_bytes} bytes" >&2 61 + return 1 62 + fi 63 + 64 + local pad_size=$(( total_bytes - current_size )) 65 + dd if=/dev/zero bs=1 count="${pad_size}" status=none >> "${out_path}" 66 + } 67 + 68 + ac_media_write_identity_config() { 69 + local out_path="$1" 70 + local json_payload="${2:-$(ac_media_default_identity_json)}" 71 + 72 + ac_write_identity_block_config \ 73 + "${out_path}" \ 74 + "$(ac_media_identity_marker)" \ 75 + "$(ac_media_identity_size)" \ 76 + "${json_payload}" 77 + } 78 + 79 + ac_media_stage_boot_tree() { 80 + local stage_root="$1" 81 + local kernel_path="$2" 82 + local config_path="$3" 83 + local bootloader_path 84 + 85 + if [ ! -f "${kernel_path}" ]; then 86 + echo "Missing kernel: ${kernel_path}" >&2 87 + return 1 88 + fi 89 + if [ ! -f "${config_path}" ]; then 90 + echo "Missing config: ${config_path}" >&2 91 + return 1 92 + fi 93 + 94 + bootloader_path="$(ac_media_bootloader_path)" || return 1 95 + 96 + rm -rf "${stage_root}" 97 + mkdir -p "${stage_root}/EFI/BOOT" 98 + 99 + cp "${bootloader_path}" "${stage_root}/EFI/BOOT/BOOTX64.EFI" 100 + cp "${kernel_path}" "${stage_root}/EFI/BOOT/KERNEL.EFI" 101 + cp "${config_path}" "${stage_root}/config.json" 102 + } 103 + 104 + ac_stage_boot_media_tree() { 105 + ac_media_stage_boot_tree "$@" 106 + } 107 + 108 + ac_media_create_fat_image() { 109 + local stage_root="$1" 110 + local image_path="$2" 111 + local label="$3" 112 + local size_mb="${4:-}" 113 + 114 + if [ -z "${size_mb}" ]; then 115 + size_mb=$(( $(ac_media_stage_tree_size_mib "${stage_root}") + 64 )) 116 + fi 117 + 118 + dd if=/dev/zero of="${image_path}" bs=1M count="${size_mb}" status=none 119 + mkfs.vfat -F 32 -n "${label}" "${image_path}" >/dev/null 2>&1 120 + 121 + export MTOOLS_SKIP_CHECK=1 122 + mmd -i "${image_path}" ::EFI ::EFI/BOOT 2>/dev/null || true 123 + if [ -f "${stage_root}/config.json" ]; then 124 + mcopy -o -i "${image_path}" "${stage_root}/config.json" ::config.json 125 + fi 126 + mcopy -o -i "${image_path}" "${stage_root}/EFI/BOOT/BOOTX64.EFI" ::EFI/BOOT/BOOTX64.EFI 127 + mcopy -o -i "${image_path}" "${stage_root}/EFI/BOOT/KERNEL.EFI" ::EFI/BOOT/KERNEL.EFI 128 + } 129 + 130 + ac_create_fat_boot_image() { 131 + ac_media_create_fat_image "$@" 132 + } 133 + 134 + ac_media_build_hybrid_iso() { 135 + local stage_root="$1" 136 + local efi_img="$2" 137 + local iso_out="$3" 138 + local volume_id="${4:-AC_NATIVE}" 139 + 140 + if ! command -v xorriso >/dev/null 2>&1; then 141 + echo "xorriso not found" >&2 142 + return 1 143 + fi 144 + 145 + xorriso -as mkisofs \ 146 + -o "${iso_out}" \ 147 + -V "${volume_id}" \ 148 + -r -J -joliet-long \ 149 + -hfsplus \ 150 + -hfs-bless-by intel_bootfile /EFI/BOOT/BOOTX64.EFI \ 151 + -append_partition 2 0xef "${efi_img}" \ 152 + -appended_part_as_apm \ 153 + -eltorito-alt-boot \ 154 + -e --interval:appended_partition_2:all:: \ 155 + -no-emul-boot \ 156 + "${stage_root}" 2>&1 | grep -v "^xorriso\|^Drive\|^Media" || true 157 + 158 + [ -f "${iso_out}" ] 159 + } 160 + 161 + ac_media_summarize_config_file() { 162 + local config_path="$1" 163 + 164 + if [ ! -f "${config_path}" ]; then 165 + echo "config=missing" 166 + return 1 167 + fi 168 + 169 + if command -v jq >/dev/null 2>&1; then 170 + jq -r ' 171 + def yesno($v): if $v then "yes" else "no" end; 172 + "handle=@\(.handle // "unknown") ac_token=\(yesno((((.token // "") | tostring) | length) > 0)) claude_creds=\(yesno(.claudeCreds? != null)) claude_state=\(yesno(.claudeState? != null))" 173 + ' "${config_path}" 2>/dev/null && return 0 174 + fi 175 + 176 + if command -v node >/dev/null 2>&1; then 177 + node -e " 178 + const fs = require('fs'); 179 + const cfg = JSON.parse(fs.readFileSync(process.argv[1], 'utf8')); 180 + const parts = [ 181 + 'handle=@' + (cfg.handle || 'unknown'), 182 + 'ac_token=' + ((cfg.token || '').length > 0 ? 'yes' : 'no'), 183 + 'claude_creds=' + (cfg.claudeCreds ? 'yes' : 'no'), 184 + 'claude_state=' + (cfg.claudeState ? 'yes' : 'no'), 185 + ]; 186 + console.log(parts.join(' ')); 187 + " "${config_path}" 2>/dev/null && return 0 188 + fi 189 + 190 + echo "config=unreadable" 191 + return 1 192 + } 193 + 194 + ac_summarize_config_file() { 195 + ac_media_summarize_config_file "$@" 196 + }
+38 -8
fedac/native/src/ac-native.c
··· 729 729 char source_dev[32] = ""; 730 730 int source_mounted_tmp = 0; 731 731 char kernel_src[96] = ""; 732 + char bootloader_src[96] = ""; 733 + char chain_kernel_src[96] = ""; 734 + char install_kernel_src[96] = ""; 732 735 char config_src[64] = ""; 733 736 734 737 ac_log("[install] auto_install_to_hd starting\n"); 735 738 if (display) 736 739 draw_boot_status(graph, screen, display, "installing to disk...", pixel_scale); 737 740 738 - // Detect source layout: monolithic (BOOTX64.EFI is kernel) or 739 - // systemd-boot (BOOTX64.EFI is bootloader, kernel at EFI/Linux/*) 741 + // Detect source layout: monolithic (BOOTX64.EFI is kernel), chainloader 742 + // (BOOTX64.EFI boots KERNEL.EFI), or systemd-boot (kernel at EFI/Linux/*). 740 743 int systemd_boot_layout = 0; 744 + int chainloader_layout = 0; 741 745 742 746 // Prefer current /mnt only when it is removable and has a bootable layout. 743 747 if (log_dev[0]) { ··· 745 749 get_parent_block(log_dev + 5, blk, sizeof(blk)); 746 750 if (blk[0] && is_removable(blk) == 1 && 747 751 (access("/mnt/EFI/BOOT/BOOTX64.EFI", F_OK) == 0 || 752 + access("/mnt/EFI/BOOT/KERNEL.EFI", F_OK) == 0 || 748 753 access("/mnt/EFI/Linux/vmlinuz-ac-native", F_OK) == 0)) { 749 754 strncpy(source_dev, log_dev, sizeof(source_dev) - 1); 750 755 source_dev[sizeof(source_dev) - 1] = '\0'; ··· 764 769 if (!blk[0] || is_removable(blk) != 1) continue; 765 770 if (mount(src_candidates[i], "/tmp/src", "vfat", 0, NULL) != 0) continue; 766 771 if (access("/tmp/src/EFI/BOOT/BOOTX64.EFI", F_OK) == 0 || 772 + access("/tmp/src/EFI/BOOT/KERNEL.EFI", F_OK) == 0 || 767 773 access("/tmp/src/EFI/Linux/vmlinuz-ac-native", F_OK) == 0) { 768 774 strncpy(source_dev, src_candidates[i], sizeof(source_dev) - 1); 769 775 source_dev[sizeof(source_dev) - 1] = '\0'; ··· 786 792 } 787 793 } 788 794 795 + snprintf(bootloader_src, sizeof(bootloader_src), "%s/EFI/BOOT/BOOTX64.EFI", source_mount); 796 + snprintf(chain_kernel_src, sizeof(chain_kernel_src), "%s/EFI/BOOT/KERNEL.EFI", source_mount); 789 797 snprintf(kernel_src, sizeof(kernel_src), "%s/EFI/BOOT/BOOTX64.EFI", source_mount); 790 798 snprintf(config_src, sizeof(config_src), "%s/config.json", source_mount); 791 - if (!source_dev[0] || (access(kernel_src, F_OK) != 0 && !systemd_boot_layout)) { 799 + if (!systemd_boot_layout && access(chain_kernel_src, F_OK) == 0) { 800 + chainloader_layout = 1; 801 + strncpy(install_kernel_src, chain_kernel_src, sizeof(install_kernel_src) - 1); 802 + install_kernel_src[sizeof(install_kernel_src) - 1] = '\0'; 803 + ac_log("[install] Detected chainloader layout\n"); 804 + } else if (systemd_boot_layout) { 805 + snprintf(install_kernel_src, sizeof(install_kernel_src), "%s/EFI/Linux/vmlinuz-ac-native", 806 + source_mount); 807 + } else { 808 + strncpy(install_kernel_src, kernel_src, sizeof(install_kernel_src) - 1); 809 + install_kernel_src[sizeof(install_kernel_src) - 1] = '\0'; 810 + } 811 + 812 + if (!source_dev[0] || 813 + ((access(kernel_src, F_OK) != 0 && access(chain_kernel_src, F_OK) != 0) && 814 + !systemd_boot_layout)) { 792 815 ac_log("[install] No removable install source with kernel found\n"); 793 816 // Log available block devices for diagnostics 794 817 ac_log("[install] Block devices:\n"); ··· 868 891 struct stat ksrc_st; 869 892 struct statvfs hd_vfs; 870 893 long kernel_bytes = 0, free_mb = 0; 871 - if (stat(kernel_src, &ksrc_st) == 0) kernel_bytes = ksrc_st.st_size; 894 + if (stat(install_kernel_src, &ksrc_st) == 0) kernel_bytes = ksrc_st.st_size; 872 895 if (statvfs("/tmp/hd", &hd_vfs) == 0) 873 896 free_mb = (long)hd_vfs.f_bavail * (long)hd_vfs.f_bsize / 1048576; 874 897 ac_log("[install] kernel=%ldMB free=%ldMB on %s\n", ··· 930 953 long sz = 0; 931 954 if (systemd_boot_layout) { 932 955 // systemd-boot: copy bootloader, kernel, initramfs, and loader config 933 - char src[256], dst[256]; 956 + char src[256]; 934 957 935 958 // Copy bootloader as BOOTX64.EFI 936 959 snprintf(src, sizeof(src), "%s/EFI/BOOT/BOOTX64.EFI", source_mount); ··· 957 980 copy_file(src, "/tmp/hd/loader/entries/ac-native.conf"); 958 981 959 982 sz = ksz > 0 ? ksz : sz; // use kernel size as success indicator 983 + } else if (chainloader_layout) { 984 + long bsz = copy_file(bootloader_src, "/tmp/hd/EFI/BOOT/BOOTX64.EFI"); 985 + long ksz = copy_file(chain_kernel_src, "/tmp/hd/EFI/BOOT/KERNEL.EFI"); 986 + ac_log("[install] chainloader: %ld bytes\n", bsz); 987 + ac_log("[install] kernel: %ld bytes\n", ksz); 988 + sz = ksz > 0 ? ksz : bsz; 960 989 } else { 961 990 // Monolithic: single BOOTX64.EFI is the kernel 962 991 sz = copy_file(kernel_src, "/tmp/hd/EFI/BOOT/BOOTX64.EFI"); ··· 973 1002 if (!systemd_boot_layout && free_bytes > sz + 1048576) { 974 1003 mkdir("/tmp/hd/EFI/Microsoft", 0755); 975 1004 mkdir("/tmp/hd/EFI/Microsoft/Boot", 0755); 976 - copy_file(kernel_src, "/tmp/hd/EFI/Microsoft/Boot/bootmgfw.efi"); 1005 + copy_file(chainloader_layout ? bootloader_src : kernel_src, 1006 + "/tmp/hd/EFI/Microsoft/Boot/bootmgfw.efi"); 977 1007 } 978 1008 979 1009 // Preserve user config + wifi creds on installed disk ··· 997 1027 998 1028 sync(); 999 1029 installed = 1; 1000 - ac_log("[install] Installed from %s to %s (systemd-boot=%d)\n", 1001 - source_dev, devpath, systemd_boot_layout); 1030 + ac_log("[install] Installed from %s to %s (systemd-boot=%d chainloader=%d)\n", 1031 + source_dev, devpath, systemd_boot_layout, chainloader_layout); 1002 1032 } 1003 1033 1004 1034 umount("/tmp/hd");