From 5620e73c4d3aa0360852021cb744cc0afc033efa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20unbrot=20P=C3=A4tzold?= Date: Sat, 25 Apr 2026 16:54:47 +0200 Subject: [PATCH 01/13] Add CLAUDE.md with codebase guidance for Claude Code Co-Authored-By: Claude Sonnet 4.6 --- CLAUDE.md | 89 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 CLAUDE.md diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..c8e2d80 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,89 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## What this is + +A Fedora automated mass-installation and post-setup scripting collection. It uses an **OEMDRV** partition (BTRFS, mounted at `/opt/sys_config`) that Anaconda/Kickstart picks up automatically during Fedora installation. The system requires: +- A FreeIPA server (domain controller, KRA vault for encryption keys) +- A Nextcloud instance (config and software sync, WebDAV token auth) +- Client PCs with an OEMDRV partition prepared beforehand + +## Configuration + +Before any script runs, copy the dist file and fill in your environment: +```sh +cp /opt/sys_config/config/setup_system.conf.dist /opt/sys_config/config/setup_system.conf +# Edit setup_system.conf with your domain, server FQDNs, paths, etc. +``` + +Local per-machine overrides go in `config.d/*.conf` (gitignored). These are sourced after `setup_system.conf` and can override any exported variable (e.g. `config.d/system_defines.conf` overrides `UPGRADEBRANCH`). + +`config/skel.tar.zst` (gitignored) holds the `/etc/skel` archive deployed to new installs. The `.dist` version is the default. To modify skel: extract, edit, then repack: +```sh +cd /opt/sys_config/config +tar -I 'zstd -9' -cf skel.tar.zst skel/ # or use pack_skel.sh +``` + +## Installation lifecycle + +1. **Kickstart pre** — `ks_base_profiles/basic_pre_script.inc` runs inside Anaconda's `%pre` section. It locates the OEMDRV partition, identifies the target drive, and **deletes all other partitions on that drive** (non-interactive, no prompt). + +2. **Kickstart post** — After Fedora installs, `setup_system_full.sh install` runs (as root, non-interactive). It adjusts umask, adds the OEMDRV fstab entry, deploys `/etc/skel` via `setup_skel.sh`, and installs the `setup-system.service` systemd unit. + +3. **First boot** (`firstrun_run` mode) — The `setup-system.service` unit runs `setup_system_full.sh firstrun_run` on `/dev/tty2`. It calls `ipa_register_host` which prompts for domain credentials and joins the PC to FreeIPA via `ipa-client-install`. The service then disables itself. + +4. **User logon** — `logon_script.sh` is triggered by KDE autostart. It: + - Mounts the gocryptfs-encrypted home directory (`mount_ecrypt_home.sh`) using a key stored in the FreeIPA KRA vault (`IPAVAULTNAME`) + - Obtains a Nextcloud WebDAV app token (`get_nc_token` in `setup_system.inc.sh`) + - Checks for a matching IPA sudo rule, then calls `sync_client_software.sh install` as root (preserving env) to sync configs and run software installs + - Calls `client_software/user_run.sh` (as the logged-in user) + - Syncs Firefox and Thunderbird profiles via Nextcloud (`mozilla_starter.sh`) + +## Script roles + +| Script | Who runs it | How called | +|---|---|---| +| `system_setup/setup_system.inc.sh` | sourced, never executed directly | `source`d by all other scripts | +| `system_setup/setup_system_full.sh` | root | kickstart post, firstrun service, or manual | +| `system_setup/logon_script.sh` | domain user | KDE autostart (via `.desktop` in autostart) | +| `system_setup/sync_client_software.sh` | root (sudo, preserve-env) | called by logon_script.sh | +| `system_setup/mount_ecrypt_home.sh` | user | called by logon_script.sh | +| `system_setup/mozilla_starter.sh` | user | called by logon_script.sh; args: `firefox\|thunderbird run\|sync [profile]` | +| `system_setup/setup_skel.sh` | root | called by setup_system_full.sh or manually | +| `config/pack_skel.sh` | root | manually, to repack skel archive after editing | +| `system_setup/create_nc_package_from_sys_config.sh` | user | manually, creates `~/temp/sys_config.tar.zst` | + +## client_software layout + +`client_software/` is synced from Nextcloud (`CLIENT_SOFTWARE_SRC` → `CLIENT_SOFTWARE_DST`). Each numbered subdirectory may contain: +- `install.sh` — run as root by `client_software/install.sh` (iterates sorted dirs) +- `user_run.sh` — run as the logged-in user by `client_software/user_run.sh` + +Naming convention: directories `< 0100` are base installs, `>= 0100` are additional apps. Pass a filter string to run only matching directories: +```sh +# Run only the kwallet install: +${CLIENT_SOFTWARE_DST}/install.sh 0010_kwallet +``` + +## Kickstart files + +- `ks.cfg` — the primary kickstart used for production installs (Fedora 43, KDE, x86_64, German locale/keyboard) +- `ks_base_profiles/kde_fullsetup.cfg` — an alternate/reference profile generated by Anaconda +- `ks_base_profiles/minimal_setup.cfg`, `part_sda.cfg` — additional profile fragments +- `ks_pc_prof/` — per-machine kickstart overrides, named by system UUID suffix (e.g. `pc-9cdb93ef7c20.cfg`) + +## Sudo rule required for logon_script + +The logon script requires a FreeIPA sudo rule allowing the domain user group to run `sync_client_software.sh` as root without a password, with environment preservation. The rule must include `!authenticate` and `setenv` options. The expected command pattern: +``` +^/opt/sys_config/system_setup/sync_client_software\.sh.*$ +``` + +## gitignore notes + +The following are intentionally excluded from git and must be set up locally: +- `config/setup_system.conf` — site-specific config (copy from `.dist`) +- `config/skel.tar.zst` — skel archive (copy from `.dist` or rebuild) +- `config.d/*.conf` — local overrides +- `client_software/.sync_*.db` — Nextcloud sync DB files -- 2.52.0 From 576363cdca9e1000a634cecbad46d8393cdab4c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20unbrot=20P=C3=A4tzold?= Date: Sat, 25 Apr 2026 17:30:49 +0200 Subject: [PATCH 02/13] Fix Nextcloud app password not stored in KWallet after autoprovisioning The Flatpak autoprovisioning command does not reliably write credentials to KWallet from inside the sandbox. After provisioning, directly write both KWallet entries (user:url/:0 and user_app-password:url/:0) via qdbus, creating the Nextcloud folder first if needed. kwallet-query was tried but silently returns 0 without creating missing folders. Co-Authored-By: Claude Sonnet 4.6 --- .../0050_nextcloud_desktopclient/user_run.sh | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/client_software/0050_nextcloud_desktopclient/user_run.sh b/client_software/0050_nextcloud_desktopclient/user_run.sh index d822e8f..8657bf7 100755 --- a/client_software/0050_nextcloud_desktopclient/user_run.sh +++ b/client_software/0050_nextcloud_desktopclient/user_run.sh @@ -93,6 +93,35 @@ for i in {0..99}; do echo "Please check the above output!" exit 1 fi + # The Flatpak autoprovisioning may not successfully write the apppassword to + # KWallet from inside the sandbox, so write it directly via D-Bus. + # Nextcloud stores HTTP credentials in folder "Nextcloud" with keys: + # user:url/:0 (legacy password entry) + # user_app-password:url/:0 (app password entry, used for auth) + NC_WALLET_URL="https://${SERVERFQDN_NC}/" + NC_WALLET_APPID="logon_script" + NC_QB_CMD="qdbus-qt6" + if ! command -v ${NC_QB_CMD} >/dev/null 2>&1; then NC_QB_CMD="qdbus"; fi + NC_QB_SVC="org.kde.kwalletd" + NC_QB_PATH="/modules/kwalletd6" + if ! ( ${NC_QB_CMD} "${NC_QB_SVC}" | grep -q "${NC_QB_PATH}" ); then + NC_QB_PATH="/modules/kwalletd5" + fi + echo "Writing Nextcloud app password to KWallet via D-Bus (${NC_QB_PATH})" + NC_WALLET_HANDLE=$(${NC_QB_CMD} ${NC_QB_SVC} ${NC_QB_PATH} org.kde.KWallet.open "kdewallet" 0 "${NC_WALLET_APPID}") + if [[ -n "${NC_WALLET_HANDLE}" && "${NC_WALLET_HANDLE}" != "-1" ]]; then + HAS_FOLDER=$(${NC_QB_CMD} ${NC_QB_SVC} ${NC_QB_PATH} org.kde.KWallet.hasFolder "${NC_WALLET_HANDLE}" "Nextcloud" "${NC_WALLET_APPID}") + if [[ "${HAS_FOLDER}" != "true" ]]; then + ${NC_QB_CMD} ${NC_QB_SVC} ${NC_QB_PATH} org.kde.KWallet.createFolder "${NC_WALLET_HANDLE}" "Nextcloud" "${NC_WALLET_APPID}" >/dev/null + fi + ${NC_QB_CMD} ${NC_QB_SVC} ${NC_QB_PATH} org.kde.KWallet.writePassword "${NC_WALLET_HANDLE}" "Nextcloud" "${DAVTOKEN_USER}:${NC_WALLET_URL}:0" "${DAVTOKEN_PASS}" "${NC_WALLET_APPID}" >/dev/null + ${NC_QB_CMD} ${NC_QB_SVC} ${NC_QB_PATH} org.kde.KWallet.writePassword "${NC_WALLET_HANDLE}" "Nextcloud" "${DAVTOKEN_USER}_app-password:${NC_WALLET_URL}:0" "${DAVTOKEN_PASS}" "${NC_WALLET_APPID}" >/dev/null + ${NC_QB_CMD} ${NC_QB_SVC} ${NC_QB_PATH} org.kde.KWallet.sync "${NC_WALLET_HANDLE}" "${NC_WALLET_APPID}" >/dev/null + ${NC_QB_CMD} ${NC_QB_SVC} ${NC_QB_PATH} org.kde.KWallet.close "${NC_WALLET_HANDLE}" false "${NC_WALLET_APPID}" >/dev/null + echo "Nextcloud app password written to KWallet successfully." + else + echo "Warning: Could not open KWallet (handle: ${NC_WALLET_HANDLE}). Nextcloud may prompt for credentials on next start." + fi fi done -- 2.52.0 From 5d0c674becfdaf19e7df360737a7fd4b7a01d9fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20unbrot=20P=C3=A4tzold?= Date: Sun, 26 Apr 2026 21:14:41 +0200 Subject: [PATCH 03/13] Add install.sh bootstrap script to create OEMDRV partition Shrinks a selected ext4/btrfs partition by 4 GiB, creates a new BTRFS partition labeled OEMDRV, mounts it to /opt/sys_config and clones the repository into it. Co-Authored-By: Claude Sonnet 4.6 --- system_setup/install.sh | 339 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 339 insertions(+) create mode 100755 system_setup/install.sh diff --git a/system_setup/install.sh b/system_setup/install.sh new file mode 100755 index 0000000..47397e8 --- /dev/null +++ b/system_setup/install.sh @@ -0,0 +1,339 @@ +#!/usr/bin/env bash +# SPDX-FileCopyrightText: Daniel Pätzold +# SPDX-License-Identifier: AGPL-3.0-or-later +# +# Bootstrap script: creates an OEMDRV partition by shrinking an existing +# ext4 or btrfs partition by 4 GiB, formats the freed space as BTRFS with +# label OEMDRV, mounts it to /opt/sys_config and clones the repository. +# +# Run as root on a target machine before any Kickstart installation. + +SHRINK_MIB=4096 +OEMDRV_LABEL="OEMDRV" +MOUNT_POINT="/opt/sys_config" +MOUNT_OPTS="compress=zstd:6" +REPO_URL="https://gitea.dtext.online/obel1x/fedora-OEMDRV.git" +MIN_FREE_MIB=$(( SHRINK_MIB + 512 )) # require 512 MiB headroom above the shrink size + +# ── Helpers ─────────────────────────────────────────────────────────────────── + +die() { echo; echo "ERROR: $*" >&2; exit 1; } +info() { echo; echo ">>> $*"; } +hr() { printf '%.0s─' {1..100}; echo; } + +require_root() { + [[ "$EUID" -eq 0 ]] || die "This script must be run as root." +} + +check_tools() { + local missing=() + for tool in lsblk blkid parted partprobe mkfs.btrfs git e2fsck resize2fs tune2fs; do + command -v "$tool" >/dev/null 2>&1 || missing+=("$tool") + done + [[ ${#missing[@]} -eq 0 ]] || die "Missing required tools: ${missing[*]}" +} + +# ── Free-space helpers ──────────────────────────────────────────────────────── + +# Free MiB for a mounted device via df +mounted_free_mib() { + df --output=avail -BM "$1" 2>/dev/null | awk 'NR==2{gsub("M",""); print $1+0}' +} + +# Free MiB for an unmounted ext4 device via tune2fs (no mount needed) +ext4_free_mib() { + local dev="$1" fb bs + fb=$(tune2fs -l "$dev" 2>/dev/null | awk '/^Free blocks:/{print $3}') + bs=$(tune2fs -l "$dev" 2>/dev/null | awk '/^Block size:/{print $3}') + [[ -n "$fb" && -n "$bs" ]] || { echo 0; return; } + echo $(( fb * bs / 1048576 )) +} + +# Free MiB for a btrfs device – mounts temporarily if not already mounted +btrfs_free_mib() { + local dev="$1" mnt free tmp=0 + mnt=$(lsblk -n -o MOUNTPOINT "$dev" 2>/dev/null | grep -v '^$' | head -1) + if [[ -z "$mnt" ]]; then + mnt=$(mktemp -d /tmp/oemdrv_check.XXXXXX) + mount -o ro "$dev" "$mnt" 2>/dev/null && tmp=1 || { rmdir "$mnt"; echo 0; return; } + fi + free=$(mounted_free_mib "$mnt") + [[ $tmp -eq 1 ]] && { umount "$mnt"; rmdir "$mnt"; } + echo "${free:-0}" +} + +# ── Partition discovery ─────────────────────────────────────────────────────── + +# Parallel arrays, indexed 0..PT_IDX-1 +PT_IDX=0 +PT_DEV=() # /dev/sda2 +PT_DISK=() # /dev/sda +PT_PNUM=() # 2 +PT_SIZE=() # "100G" +PT_FS=() # ext4 | btrfs +PT_LABEL=() # home | - +PT_UUID=() # xxxxxxxx | - +PT_MNT=() # /home | - +PT_FREE=() # free MiB (integer) +PT_OK=() # 1 = shrinkable + +collect_partitions() { + local NAME SIZE FSTYPE LABEL UUID MOUNTPOINT TYPE PKNAME + + while IFS= read -r line; do + NAME="" SIZE="" FSTYPE="" LABEL="" UUID="" MOUNTPOINT="" TYPE="" PKNAME="" + eval "$line" + + [[ "$TYPE" != "part" ]] && continue + [[ "$FSTYPE" != "ext4" && "$FSTYPE" != "btrfs" ]] && continue + [[ "$LABEL" == "$OEMDRV_LABEL" ]] && continue + [[ -z "$PKNAME" ]] && continue + + local disk="/dev/$PKNAME" + local pnum + pnum=$(cat /sys/class/block/"${NAME##/dev/}"/partition 2>/dev/null) || continue + + # Free space check + local free_mib + if [[ -n "$MOUNTPOINT" ]]; then + free_mib=$(mounted_free_mib "$NAME") + elif [[ "$FSTYPE" == "ext4" ]]; then + free_mib=$(ext4_free_mib "$NAME") + else + free_mib=$(btrfs_free_mib "$NAME") + fi + free_mib=${free_mib:-0} + + # Shrinkable: not /, enough free space + local ok=0 + [[ "$MOUNTPOINT" != "/" && "$free_mib" -ge "$MIN_FREE_MIB" ]] && ok=1 + + PT_DEV[$PT_IDX]="$NAME" + PT_DISK[$PT_IDX]="$disk" + PT_PNUM[$PT_IDX]="$pnum" + PT_SIZE[$PT_IDX]="$SIZE" + PT_FS[$PT_IDX]="$FSTYPE" + PT_LABEL[$PT_IDX]="${LABEL:--}" + PT_UUID[$PT_IDX]="${UUID:--}" + PT_MNT[$PT_IDX]="${MOUNTPOINT:--}" + PT_FREE[$PT_IDX]="$free_mib" + PT_OK[$PT_IDX]="$ok" + (( PT_IDX++ )) || true + done < <(lsblk -o NAME,SIZE,FSTYPE,LABEL,UUID,MOUNTPOINT,TYPE,PKNAME -p -P -n 2>/dev/null) +} + +# ── Table display ───────────────────────────────────────────────────────────── + +show_table() { + hr + printf " %-4s %-14s %-6s %-8s %-10s %-6s %-18s %-36s %s\n" \ + "#" "Device" "Disk" "Size" "Free (MiB)" "FS" "Label" "UUID" "Mountpoint" + hr + local i mark note + for (( i=0; i/dev/null \ + | awk -F: -v p="$2" '$1==p{gsub("MiB","",$3); printf "%.0f\n",$3; exit}' +} + +part_start_mib() { + parted -s "$1" -m unit MiB print 2>/dev/null \ + | awk -F: -v p="$2" '$1==p{gsub("MiB","",$2); printf "%.0f\n",$2; exit}' +} + +# Device name for a new partition on a given disk +new_part_device() { + local disk="$1" pnum="$2" + if [[ "$disk" =~ (nvme|mmcblk) ]]; then + echo "${disk}p${pnum}" + else + echo "${disk}${pnum}" + fi +} + +# ── Main ────────────────────────────────────────────────────────────────────── + +require_root +check_tools + +info "Scanning partitions for shrinkable ext4 / btrfs volumes..." +collect_partitions + +[[ $PT_IDX -gt 0 ]] || die "No ext4 or btrfs partitions found on this system." + +show_table + +# Count shrinkable +shrink_count=0 +for (( i=0; i= 0 && local_i < PT_IDX )) || { echo " Number out of range."; continue; } + [[ "${PT_OK[$local_i]}" == "1" ]] || { echo " That partition cannot be shrunk (see marks above)."; continue; } + SEL=$local_i + break +done + +S_DEV="${PT_DEV[$SEL]}" +S_DISK="${PT_DISK[$SEL]}" +S_PNUM="${PT_PNUM[$SEL]}" +S_FS="${PT_FS[$SEL]}" +S_MNT="${PT_MNT[$SEL]}" + +echo +echo " Partition : $S_DEV (${PT_FS[$SEL]}, ${PT_SIZE[$SEL]}, ${PT_FREE[$SEL]} MiB free)" +echo " Disk : $S_DISK Partition number: $S_PNUM" +echo " Mountpoint: $S_MNT" +echo +echo " $S_DEV will be shrunk by ${SHRINK_MIB} MiB." +echo " A new ${SHRINK_MIB} MiB BTRFS partition labeled OEMDRV will be created." +echo " It will be mounted at $MOUNT_POINT and the repository cloned into it." +echo +read -r -p " Type YES (uppercase) to confirm, anything else to abort: " CONFIRM +[[ "$CONFIRM" == "YES" ]] || { echo "Aborted."; exit 0; } + +# ── Geometry ────────────────────────────────────────────────────────────────── + +info "Reading partition geometry on $S_DISK..." + +CURR_END=$(part_end_mib "$S_DISK" "$S_PNUM") +CURR_START=$(part_start_mib "$S_DISK" "$S_PNUM") +[[ -n "$CURR_END" && -n "$CURR_START" ]] || die "Could not read geometry for partition $S_PNUM on $S_DISK." + +NEW_END=$(( CURR_END - SHRINK_MIB )) +NEW_FS_MIB=$(( NEW_END - CURR_START )) +OEMDRV_START=$NEW_END +OEMDRV_END=$CURR_END + +echo " Current partition : ${CURR_START} MiB – ${CURR_END} MiB" +echo " After shrink : ${CURR_START} MiB – ${NEW_END} MiB (filesystem: ${NEW_FS_MIB} MiB)" +echo " New OEMDRV : ${OEMDRV_START} MiB – ${OEMDRV_END} MiB (${SHRINK_MIB} MiB)" + +# ── Unmount ─────────────────────────────────────────────────────────────────── + +WAS_MOUNTED=0 +ORIG_MNT="" +if [[ "$S_MNT" != "-" && -n "$S_MNT" ]]; then + info "Unmounting $S_DEV from $S_MNT..." + ORIG_MNT="$S_MNT" + umount "$S_DEV" || die "Cannot unmount $S_DEV. Close all open files and try again." + WAS_MOUNTED=1 +fi + +# ── Filesystem resize ───────────────────────────────────────────────────────── + +if [[ "$S_FS" == "ext4" ]]; then + info "Running e2fsck on $S_DEV..." + e2fsck -f "$S_DEV" || die "e2fsck found errors on $S_DEV. Fix them before retrying." + + info "Shrinking ext4 filesystem to ${NEW_FS_MIB} MiB..." + resize2fs "$S_DEV" "${NEW_FS_MIB}M" || die "resize2fs failed." + +elif [[ "$S_FS" == "btrfs" ]]; then + info "Shrinking btrfs filesystem on $S_DEV to ${NEW_FS_MIB} MiB..." + TMP_MNT=$(mktemp -d /tmp/oemdrv_btrfs.XXXXXX) + mount "$S_DEV" "$TMP_MNT" || { rmdir "$TMP_MNT"; die "Cannot mount $S_DEV for btrfs resize."; } + btrfs filesystem resize "${NEW_FS_MIB}m" "$TMP_MNT" \ + || { umount "$TMP_MNT"; rmdir "$TMP_MNT"; die "btrfs filesystem resize failed."; } + sync + umount "$TMP_MNT" && rmdir "$TMP_MNT" +fi + +# ── Partition table update ──────────────────────────────────────────────────── + +info "Shrinking partition $S_PNUM to ${NEW_END} MiB in partition table..." +parted -s "$S_DISK" resizepart "$S_PNUM" "${NEW_END}MiB" \ + || die "parted resizepart failed." + +info "Creating new OEMDRV partition (${OEMDRV_START}–${OEMDRV_END} MiB)..." +parted -s "$S_DISK" mkpart primary btrfs "${OEMDRV_START}MiB" "${OEMDRV_END}MiB" \ + || die "parted mkpart failed. Check that the target area is free space on $S_DISK." + +partprobe "$S_DISK" +sleep 1 + +# Determine new partition number (highest on the disk after partprobe) +NEW_PNUM=$(parted -s "$S_DISK" -m unit MiB print 2>/dev/null \ + | awk -F: '/^[0-9]/{n=$1} END{print n}') +[[ -n "$NEW_PNUM" ]] || die "Could not determine new partition number on $S_DISK." + +OEMDRV_DEV=$(new_part_device "$S_DISK" "$NEW_PNUM") +[[ -b "$OEMDRV_DEV" ]] || die "New partition device $OEMDRV_DEV not found after partprobe." + +info "New partition device: $OEMDRV_DEV" + +# ── Format ──────────────────────────────────────────────────────────────────── + +info "Formatting $OEMDRV_DEV as BTRFS (label: $OEMDRV_LABEL)..." +mkfs.btrfs -f -L "$OEMDRV_LABEL" "$OEMDRV_DEV" || die "mkfs.btrfs failed." + +# ── Remount original partition ──────────────────────────────────────────────── + +if [[ $WAS_MOUNTED -eq 1 && -n "$ORIG_MNT" ]]; then + info "Remounting $S_DEV to $ORIG_MNT..." + mount "$S_DEV" "$ORIG_MNT" \ + || echo "WARNING: Could not remount $S_DEV to $ORIG_MNT — remount manually." +fi + +# ── Mount OEMDRV ────────────────────────────────────────────────────────────── + +info "Mounting $OEMDRV_DEV to $MOUNT_POINT (options: $MOUNT_OPTS)..." +[[ -d "$MOUNT_POINT" ]] || mkdir -p "$MOUNT_POINT" +mount -o "$MOUNT_OPTS" "$OEMDRV_DEV" "$MOUNT_POINT" || die "mount failed." + +# ── Clone repository ────────────────────────────────────────────────────────── + +info "Cloning $REPO_URL into $MOUNT_POINT..." +cd "$MOUNT_POINT" || die "Cannot cd to $MOUNT_POINT." +git clone --progress --depth 1 "$REPO_URL" . || die "git clone failed." + +# ── Done ────────────────────────────────────────────────────────────────────── + +info "Done." +echo +echo " OEMDRV device : $OEMDRV_DEV" +echo " Mounted at : $MOUNT_POINT" +echo +echo "Next steps:" +echo " 1. cp $MOUNT_POINT/config/setup_system.conf.dist \\" +echo " $MOUNT_POINT/config/setup_system.conf" +echo " 2. Edit setup_system.conf with your domain, IPA/Nextcloud FQDNs, and paths." +echo " 3. Boot the Kickstart installer — it will detect the OEMDRV partition automatically." +echo -- 2.52.0 From 6bb7cac7ad9d29f8b7e5053289dcefbb13a856d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20unbrot=20P=C3=A4tzold?= Date: Sun, 26 Apr 2026 21:23:55 +0200 Subject: [PATCH 04/13] Add install.md with usage instructions for install.sh Co-Authored-By: Claude Sonnet 4.6 --- system_setup/install.md | 51 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 system_setup/install.md diff --git a/system_setup/install.md b/system_setup/install.md new file mode 100644 index 0000000..accee56 --- /dev/null +++ b/system_setup/install.md @@ -0,0 +1,51 @@ +# OEMDRV Bootstrap — install.sh + +`install.sh` prepares a target machine for automated Fedora deployment. It shrinks an existing partition to carve out a dedicated **OEMDRV** partition, which Anaconda/Kickstart will detect automatically during installation. + +## What it does + +1. Lists all ext4 and btrfs partitions that have enough free space to be shrunk. +2. Asks you to select one and shrinks it by **4 GiB**. +3. Creates a new 4 GiB BTRFS partition labeled `OEMDRV` in the freed space. +4. Mounts it to `/opt/sys_config` with `compress=zstd:6`. +5. Clones this repository (depth 1) into `/opt/sys_config`. + +## Prerequisites + +- Run as **root** on the target machine (live system or installed OS). +- The following tools must be present: `parted`, `e2fsck`, `resize2fs` or `btrfs-progs`, `mkfs.btrfs`, `git`, `curl`. +- The partition you want to shrink must **not** be the root filesystem (`/`) and must have at least **4.5 GiB free**. +- Network access to `gitea.dtext.online`. + +## Run directly from the repository + +```bash +bash <(curl -fsSL https://gitea.dtext.online/obel1x/fedora-OEMDRV/raw/branch/main/system_setup/install.sh) +``` + +> The script requires root. Run the command above as root, or prefix with `sudo`. + +## After the script completes + +Configure your environment before running any installation: + +```sh +cp /opt/sys_config/config/setup_system.conf.dist /opt/sys_config/config/setup_system.conf +# Edit setup_system.conf — set TLDOMAIN, SERVERFQDN_IPA, SERVERFQDN_NC, and paths. +``` + +Optionally add local per-machine overrides in `config.d/`: + +```sh +# Example: use the devel branch on this machine +echo 'export UPGRADEBRANCH="devel"' > /opt/sys_config/config.d/system_defines.conf +``` + +Once configured, boot the Fedora installer from USB — Anaconda will detect the `OEMDRV` partition and run the Kickstart automatically. + +## Supported filesystems for shrinking + +| Filesystem | Method | +|---|---| +| ext4 | `e2fsck` + `resize2fs` (offline) | +| btrfs | `btrfs filesystem resize` (temporary mount) | -- 2.52.0 From 4d9ec0d35696784ac9000967a7333dc0bba99c33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20unbrot=20P=C3=A4tzold?= Date: Mon, 27 Apr 2026 13:45:55 +0200 Subject: [PATCH 05/13] Moved install-md to top level --- system_setup/install.md => install.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename system_setup/install.md => install.md (87%) diff --git a/system_setup/install.md b/install.md similarity index 87% rename from system_setup/install.md rename to install.md index accee56..9e49a77 100644 --- a/system_setup/install.md +++ b/install.md @@ -1,6 +1,6 @@ # OEMDRV Bootstrap — install.sh -`install.sh` prepares a target machine for automated Fedora deployment. It shrinks an existing partition to carve out a dedicated **OEMDRV** partition, which Anaconda/Kickstart will detect automatically during installation. +the script `./system_setup/install.sh` prepares a target machine for automated Fedora deployment. It shrinks an existing partition to carve out a dedicated **OEMDRV** partition, which Anaconda/Kickstart will detect automatically during installation. ## What it does -- 2.52.0 From 9974facd45f8186cebdc8b2aea2087618b7de371 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20unbrot=20P=C3=A4tzold?= Date: Mon, 27 Apr 2026 14:07:02 +0200 Subject: [PATCH 06/13] Fix install.sh exiting immediately when not run as root Co-Authored-By: Claude Sonnet 4.6 --- system_setup/install.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/system_setup/install.sh b/system_setup/install.sh index 47397e8..e289bd2 100755 --- a/system_setup/install.sh +++ b/system_setup/install.sh @@ -8,6 +8,8 @@ # # Run as root on a target machine before any Kickstart installation. +[[ "$EUID" -eq 0 ]] || { echo "ERROR: This script must be run as root." >&2; exit 1; } + SHRINK_MIB=4096 OEMDRV_LABEL="OEMDRV" MOUNT_POINT="/opt/sys_config" -- 2.52.0 From 9e16162077730fe78dbf13af84c2dd2fef423598 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20unbrot=20P=C3=A4tzold?= Date: Mon, 27 Apr 2026 14:59:14 +0200 Subject: [PATCH 07/13] Add free space detection to install.sh as alternative to partition shrinking If a disk has unpartitioned space >= 4096 MiB, it is offered as a direct target for the OEMDRV partition, avoiding any filesystem resize. Co-Authored-By: Claude Sonnet 4.6 --- system_setup/install.sh | 252 +++++++++++++++++++++++++++------------- 1 file changed, 172 insertions(+), 80 deletions(-) diff --git a/system_setup/install.sh b/system_setup/install.sh index e289bd2..df3b9a8 100755 --- a/system_setup/install.sh +++ b/system_setup/install.sh @@ -79,6 +79,13 @@ PT_MNT=() # /home | - PT_FREE=() # free MiB (integer) PT_OK=() # 1 = shrinkable +# Parallel arrays for unpartitioned free space regions, indexed 0..FS_IDX-1 +FS_IDX=0 +FS_DISK=() # /dev/sda +FS_START=() # region start, MiB +FS_END=() # region end, MiB +FS_SIZE=() # region size, MiB + collect_partitions() { local NAME SIZE FSTYPE LABEL UUID MOUNTPOINT TYPE PKNAME @@ -124,6 +131,34 @@ collect_partitions() { done < <(lsblk -o NAME,SIZE,FSTYPE,LABEL,UUID,MOUNTPOINT,TYPE,PKNAME -p -P -n 2>/dev/null) } +collect_free_space() { + local disk + while IFS= read -r disk; do + [[ -b "$disk" ]] || continue + while read -r s e sz; do + FS_DISK[$FS_IDX]="$disk" + FS_START[$FS_IDX]="$s" + FS_END[$FS_IDX]="$e" + FS_SIZE[$FS_IDX]="$sz" + (( FS_IDX++ )) || true + done < <( + parted -s "$disk" -m unit MiB print free 2>/dev/null \ + | awk -F'[;:]' -v min="$SHRINK_MIB" ' + $1+0 > 0 { + for (i = 1; i <= NF; i++) { + if ($i == "free") { + start=$2; end=$3; size=$4; + gsub(/MiB/,"",start); gsub(/MiB/,"",end); gsub(/MiB/,"",size); + s=int(start+0); e=int(end+0); sz=int(size+0); + if (sz >= min) print s " " e " " sz; + break + } + } + }' + ) + done < <(lsblk -d -p -n -o NAME 2>/dev/null) +} + # ── Table display ───────────────────────────────────────────────────────────── show_table() { @@ -160,6 +195,25 @@ show_table() { echo } +show_free_table() { + [[ $FS_IDX -gt 0 ]] || return + echo + echo "Unpartitioned free space regions (>= ${SHRINK_MIB} MiB):" + hr + printf " %-4s %-14s %-12s %-12s %s\n" "#" "Disk" "Start (MiB)" "End (MiB)" "Size (MiB)" + hr + for (( i=0; i to use free space, s to shrink a partition, or q to quit: " INPUT + elif [[ $FS_IDX -gt 0 ]]; then + read -r -p "Enter number of free space region to use, or q to quit: " INPUT + [[ "$INPUT" =~ ^[0-9]+$ ]] && INPUT="f${INPUT}" + else + read -r -p "Enter number of partition to shrink, or q to quit: " INPUT + [[ "$INPUT" =~ ^[0-9]+$ ]] && INPUT="s${INPUT}" + fi + [[ "$INPUT" =~ ^[qQ]$ ]] && { echo "Aborted."; exit 0; } - [[ "$INPUT" =~ ^[0-9]+$ ]] || { echo " Please enter a number."; continue; } - local_i=$(( INPUT - 1 )) - (( local_i >= 0 && local_i < PT_IDX )) || { echo " Number out of range."; continue; } - [[ "${PT_OK[$local_i]}" == "1" ]] || { echo " That partition cannot be shrunk (see marks above)."; continue; } - SEL=$local_i - break + + if [[ "$INPUT" =~ ^[fF]([0-9]+)$ ]]; then + local_i=$(( ${BASH_REMATCH[1]} - 1 )) + (( local_i >= 0 && local_i < FS_IDX )) || { echo " Number out of range."; continue; } + MODE="freespace"; SEL=$local_i; break + + elif [[ "$INPUT" =~ ^[sS]([0-9]+)$ ]]; then + local_i=$(( ${BASH_REMATCH[1]} - 1 )) + (( local_i >= 0 && local_i < PT_IDX )) || { echo " Number out of range."; continue; } + [[ "${PT_OK[$local_i]}" == "1" ]] || { echo " That partition cannot be shrunk (see marks above)."; continue; } + MODE="shrink"; SEL=$local_i; break + + else + echo " Invalid input." + fi done -S_DEV="${PT_DEV[$SEL]}" -S_DISK="${PT_DISK[$SEL]}" -S_PNUM="${PT_PNUM[$SEL]}" -S_FS="${PT_FS[$SEL]}" -S_MNT="${PT_MNT[$SEL]}" +# ── Confirm ─────────────────────────────────────────────────────────────────── echo -echo " Partition : $S_DEV (${PT_FS[$SEL]}, ${PT_SIZE[$SEL]}, ${PT_FREE[$SEL]} MiB free)" -echo " Disk : $S_DISK Partition number: $S_PNUM" -echo " Mountpoint: $S_MNT" -echo -echo " $S_DEV will be shrunk by ${SHRINK_MIB} MiB." -echo " A new ${SHRINK_MIB} MiB BTRFS partition labeled OEMDRV will be created." -echo " It will be mounted at $MOUNT_POINT and the repository cloned into it." +if [[ "$MODE" == "freespace" ]]; then + WORK_DISK="${FS_DISK[$SEL]}" + OEMDRV_START="${FS_START[$SEL]}" + OEMDRV_END=$(( FS_START[$SEL] + SHRINK_MIB )) + echo " Disk : $WORK_DISK" + echo " Free region : ${FS_START[$SEL]} MiB – ${FS_END[$SEL]} MiB (${FS_SIZE[$SEL]} MiB available)" + echo " New OEMDRV : ${OEMDRV_START} MiB – ${OEMDRV_END} MiB (${SHRINK_MIB} MiB)" + echo " It will be mounted at $MOUNT_POINT and the repository cloned into it." +else + S_DEV="${PT_DEV[$SEL]}" + S_DISK="${PT_DISK[$SEL]}" + S_PNUM="${PT_PNUM[$SEL]}" + S_FS="${PT_FS[$SEL]}" + S_MNT="${PT_MNT[$SEL]}" + WORK_DISK="$S_DISK" + echo " Partition : $S_DEV (${PT_FS[$SEL]}, ${PT_SIZE[$SEL]}, ${PT_FREE[$SEL]} MiB free)" + echo " Disk : $S_DISK Partition number: $S_PNUM" + echo " Mountpoint: $S_MNT" + echo + echo " $S_DEV will be shrunk by ${SHRINK_MIB} MiB." + echo " A new ${SHRINK_MIB} MiB BTRFS partition labeled OEMDRV will be created." + echo " It will be mounted at $MOUNT_POINT and the repository cloned into it." +fi echo read -r -p " Type YES (uppercase) to confirm, anything else to abort: " CONFIRM [[ "$CONFIRM" == "YES" ]] || { echo "Aborted."; exit 0; } -# ── Geometry ────────────────────────────────────────────────────────────────── - -info "Reading partition geometry on $S_DISK..." - -CURR_END=$(part_end_mib "$S_DISK" "$S_PNUM") -CURR_START=$(part_start_mib "$S_DISK" "$S_PNUM") -[[ -n "$CURR_END" && -n "$CURR_START" ]] || die "Could not read geometry for partition $S_PNUM on $S_DISK." - -NEW_END=$(( CURR_END - SHRINK_MIB )) -NEW_FS_MIB=$(( NEW_END - CURR_START )) -OEMDRV_START=$NEW_END -OEMDRV_END=$CURR_END - -echo " Current partition : ${CURR_START} MiB – ${CURR_END} MiB" -echo " After shrink : ${CURR_START} MiB – ${NEW_END} MiB (filesystem: ${NEW_FS_MIB} MiB)" -echo " New OEMDRV : ${OEMDRV_START} MiB – ${OEMDRV_END} MiB (${SHRINK_MIB} MiB)" - -# ── Unmount ─────────────────────────────────────────────────────────────────── +# ── Shrink path: geometry + filesystem resize + partition table update ───────── WAS_MOUNTED=0 ORIG_MNT="" -if [[ "$S_MNT" != "-" && -n "$S_MNT" ]]; then - info "Unmounting $S_DEV from $S_MNT..." - ORIG_MNT="$S_MNT" - umount "$S_DEV" || die "Cannot unmount $S_DEV. Close all open files and try again." - WAS_MOUNTED=1 + +if [[ "$MODE" == "shrink" ]]; then + info "Reading partition geometry on $S_DISK..." + + CURR_END=$(part_end_mib "$S_DISK" "$S_PNUM") + CURR_START=$(part_start_mib "$S_DISK" "$S_PNUM") + [[ -n "$CURR_END" && -n "$CURR_START" ]] \ + || die "Could not read geometry for partition $S_PNUM on $S_DISK." + + NEW_END=$(( CURR_END - SHRINK_MIB )) + NEW_FS_MIB=$(( NEW_END - CURR_START )) + OEMDRV_START=$NEW_END + OEMDRV_END=$CURR_END + + echo " Current partition : ${CURR_START} MiB – ${CURR_END} MiB" + echo " After shrink : ${CURR_START} MiB – ${NEW_END} MiB (filesystem: ${NEW_FS_MIB} MiB)" + echo " New OEMDRV : ${OEMDRV_START} MiB – ${OEMDRV_END} MiB (${SHRINK_MIB} MiB)" + + if [[ "$S_MNT" != "-" && -n "$S_MNT" ]]; then + info "Unmounting $S_DEV from $S_MNT..." + ORIG_MNT="$S_MNT" + umount "$S_DEV" || die "Cannot unmount $S_DEV. Close all open files and try again." + WAS_MOUNTED=1 + fi + + if [[ "$S_FS" == "ext4" ]]; then + info "Running e2fsck on $S_DEV..." + e2fsck -f "$S_DEV" || die "e2fsck found errors on $S_DEV. Fix them before retrying." + + info "Shrinking ext4 filesystem to ${NEW_FS_MIB} MiB..." + resize2fs "$S_DEV" "${NEW_FS_MIB}M" || die "resize2fs failed." + + elif [[ "$S_FS" == "btrfs" ]]; then + info "Shrinking btrfs filesystem on $S_DEV to ${NEW_FS_MIB} MiB..." + TMP_MNT=$(mktemp -d /tmp/oemdrv_btrfs.XXXXXX) + mount "$S_DEV" "$TMP_MNT" || { rmdir "$TMP_MNT"; die "Cannot mount $S_DEV for btrfs resize."; } + btrfs filesystem resize "${NEW_FS_MIB}m" "$TMP_MNT" \ + || { umount "$TMP_MNT"; rmdir "$TMP_MNT"; die "btrfs filesystem resize failed."; } + sync + umount "$TMP_MNT" && rmdir "$TMP_MNT" + fi + + info "Shrinking partition $S_PNUM to ${NEW_END} MiB in partition table..." + parted -s "$S_DISK" resizepart "$S_PNUM" "${NEW_END}MiB" \ + || die "parted resizepart failed." fi -# ── Filesystem resize ───────────────────────────────────────────────────────── +# ── Create OEMDRV partition ─────────────────────────────────────────────────── -if [[ "$S_FS" == "ext4" ]]; then - info "Running e2fsck on $S_DEV..." - e2fsck -f "$S_DEV" || die "e2fsck found errors on $S_DEV. Fix them before retrying." +info "Creating new OEMDRV partition (${OEMDRV_START}–${OEMDRV_END} MiB) on $WORK_DISK..." +parted -s "$WORK_DISK" mkpart primary btrfs "${OEMDRV_START}MiB" "${OEMDRV_END}MiB" \ + || die "parted mkpart failed. Check that the target area is free space on $WORK_DISK." - info "Shrinking ext4 filesystem to ${NEW_FS_MIB} MiB..." - resize2fs "$S_DEV" "${NEW_FS_MIB}M" || die "resize2fs failed." - -elif [[ "$S_FS" == "btrfs" ]]; then - info "Shrinking btrfs filesystem on $S_DEV to ${NEW_FS_MIB} MiB..." - TMP_MNT=$(mktemp -d /tmp/oemdrv_btrfs.XXXXXX) - mount "$S_DEV" "$TMP_MNT" || { rmdir "$TMP_MNT"; die "Cannot mount $S_DEV for btrfs resize."; } - btrfs filesystem resize "${NEW_FS_MIB}m" "$TMP_MNT" \ - || { umount "$TMP_MNT"; rmdir "$TMP_MNT"; die "btrfs filesystem resize failed."; } - sync - umount "$TMP_MNT" && rmdir "$TMP_MNT" -fi - -# ── Partition table update ──────────────────────────────────────────────────── - -info "Shrinking partition $S_PNUM to ${NEW_END} MiB in partition table..." -parted -s "$S_DISK" resizepart "$S_PNUM" "${NEW_END}MiB" \ - || die "parted resizepart failed." - -info "Creating new OEMDRV partition (${OEMDRV_START}–${OEMDRV_END} MiB)..." -parted -s "$S_DISK" mkpart primary btrfs "${OEMDRV_START}MiB" "${OEMDRV_END}MiB" \ - || die "parted mkpart failed. Check that the target area is free space on $S_DISK." - -partprobe "$S_DISK" +partprobe "$WORK_DISK" sleep 1 # Determine new partition number (highest on the disk after partprobe) -NEW_PNUM=$(parted -s "$S_DISK" -m unit MiB print 2>/dev/null \ +NEW_PNUM=$(parted -s "$WORK_DISK" -m unit MiB print 2>/dev/null \ | awk -F: '/^[0-9]/{n=$1} END{print n}') -[[ -n "$NEW_PNUM" ]] || die "Could not determine new partition number on $S_DISK." +[[ -n "$NEW_PNUM" ]] || die "Could not determine new partition number on $WORK_DISK." -OEMDRV_DEV=$(new_part_device "$S_DISK" "$NEW_PNUM") +OEMDRV_DEV=$(new_part_device "$WORK_DISK" "$NEW_PNUM") [[ -b "$OEMDRV_DEV" ]] || die "New partition device $OEMDRV_DEV not found after partprobe." info "New partition device: $OEMDRV_DEV" @@ -306,7 +398,7 @@ info "New partition device: $OEMDRV_DEV" info "Formatting $OEMDRV_DEV as BTRFS (label: $OEMDRV_LABEL)..." mkfs.btrfs -f -L "$OEMDRV_LABEL" "$OEMDRV_DEV" || die "mkfs.btrfs failed." -# ── Remount original partition ──────────────────────────────────────────────── +# ── Remount original partition (shrink path only) ───────────────────────────── if [[ $WAS_MOUNTED -eq 1 && -n "$ORIG_MNT" ]]; then info "Remounting $S_DEV to $ORIG_MNT..." -- 2.52.0 From bfa5515d750ef59f6c1d9f1eddc9b0e9417f38c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20unbrot=20P=C3=A4tzold?= Date: Mon, 27 Apr 2026 16:23:51 +0200 Subject: [PATCH 08/13] Fix double /dev/ prefix bug in collect_partitions lsblk -p returns PKNAME as a full path (/dev/sda), so stripping the basename before prepending /dev/ avoids /dev//dev/sda. Co-Authored-By: Claude Sonnet 4.6 --- system_setup/install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system_setup/install.sh b/system_setup/install.sh index df3b9a8..658a156 100755 --- a/system_setup/install.sh +++ b/system_setup/install.sh @@ -98,7 +98,7 @@ collect_partitions() { [[ "$LABEL" == "$OEMDRV_LABEL" ]] && continue [[ -z "$PKNAME" ]] && continue - local disk="/dev/$PKNAME" + local disk="/dev/${PKNAME##*/}" local pnum pnum=$(cat /sys/class/block/"${NAME##/dev/}"/partition 2>/dev/null) || continue -- 2.52.0 From bf71540f66f655dc47f10053ab13e022b23099ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20unbrot=20P=C3=A4tzold?= Date: Mon, 27 Apr 2026 16:26:27 +0200 Subject: [PATCH 09/13] Fix btrfs resize: use online resize instead of unmount/remount btrfs supports live filesystem resize, so there is no need to unmount a mounted btrfs partition before shrinking it. This also avoids umount failures when the partition is busy (e.g. /home with an active SSH session). ext4 still requires offline resize. Co-Authored-By: Claude Sonnet 4.6 --- system_setup/install.sh | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/system_setup/install.sh b/system_setup/install.sh index 658a156..bc95dd3 100755 --- a/system_setup/install.sh +++ b/system_setup/install.sh @@ -345,14 +345,14 @@ if [[ "$MODE" == "shrink" ]]; then echo " After shrink : ${CURR_START} MiB – ${NEW_END} MiB (filesystem: ${NEW_FS_MIB} MiB)" echo " New OEMDRV : ${OEMDRV_START} MiB – ${OEMDRV_END} MiB (${SHRINK_MIB} MiB)" - if [[ "$S_MNT" != "-" && -n "$S_MNT" ]]; then - info "Unmounting $S_DEV from $S_MNT..." - ORIG_MNT="$S_MNT" - umount "$S_DEV" || die "Cannot unmount $S_DEV. Close all open files and try again." - WAS_MOUNTED=1 - fi - if [[ "$S_FS" == "ext4" ]]; then + # ext4 requires offline resize — unmount first + if [[ "$S_MNT" != "-" && -n "$S_MNT" ]]; then + info "Unmounting $S_DEV from $S_MNT..." + ORIG_MNT="$S_MNT" + umount "$S_DEV" || die "Cannot unmount $S_DEV. Close all open files and try again." + WAS_MOUNTED=1 + fi info "Running e2fsck on $S_DEV..." e2fsck -f "$S_DEV" || die "e2fsck found errors on $S_DEV. Fix them before retrying." @@ -360,13 +360,20 @@ if [[ "$MODE" == "shrink" ]]; then resize2fs "$S_DEV" "${NEW_FS_MIB}M" || die "resize2fs failed." elif [[ "$S_FS" == "btrfs" ]]; then + # btrfs supports online resize — use existing mount point if available, + # otherwise mount temporarily info "Shrinking btrfs filesystem on $S_DEV to ${NEW_FS_MIB} MiB..." - TMP_MNT=$(mktemp -d /tmp/oemdrv_btrfs.XXXXXX) - mount "$S_DEV" "$TMP_MNT" || { rmdir "$TMP_MNT"; die "Cannot mount $S_DEV for btrfs resize."; } - btrfs filesystem resize "${NEW_FS_MIB}m" "$TMP_MNT" \ - || { umount "$TMP_MNT"; rmdir "$TMP_MNT"; die "btrfs filesystem resize failed."; } - sync - umount "$TMP_MNT" && rmdir "$TMP_MNT" + if [[ "$S_MNT" != "-" && -n "$S_MNT" ]]; then + btrfs filesystem resize "${NEW_FS_MIB}m" "$S_MNT" \ + || die "btrfs filesystem resize failed." + else + TMP_MNT=$(mktemp -d /tmp/oemdrv_btrfs.XXXXXX) + mount "$S_DEV" "$TMP_MNT" || { rmdir "$TMP_MNT"; die "Cannot mount $S_DEV for btrfs resize."; } + btrfs filesystem resize "${NEW_FS_MIB}m" "$TMP_MNT" \ + || { umount "$TMP_MNT"; rmdir "$TMP_MNT"; die "btrfs filesystem resize failed."; } + sync + umount "$TMP_MNT" && rmdir "$TMP_MNT" + fi fi info "Shrinking partition $S_PNUM to ${NEW_END} MiB in partition table..." -- 2.52.0 From 5c2182a1c9bbe111187f348fcaeddda5b82e762c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20unbrot=20P=C3=A4tzold?= Date: Mon, 27 Apr 2026 16:30:19 +0200 Subject: [PATCH 10/13] Fix parted failing on busy partition in script mode parted -s answers confirmation prompts with "no" (conservative), causing resizepart/mkpart to fail on a mounted partition. Pipe "Yes" to parted stdin instead so busy-partition warnings are confirmed and the operation proceeds. Co-Authored-By: Claude Sonnet 4.6 --- system_setup/install.sh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/system_setup/install.sh b/system_setup/install.sh index bc95dd3..3615166 100755 --- a/system_setup/install.sh +++ b/system_setup/install.sh @@ -377,14 +377,15 @@ if [[ "$MODE" == "shrink" ]]; then fi info "Shrinking partition $S_PNUM to ${NEW_END} MiB in partition table..." - parted -s "$S_DISK" resizepart "$S_PNUM" "${NEW_END}MiB" \ + # Use printf instead of -s so parted's "partition is busy" confirmation is answered Yes + printf 'Yes\n' | parted "$S_DISK" resizepart "$S_PNUM" "${NEW_END}MiB" \ || die "parted resizepart failed." fi # ── Create OEMDRV partition ─────────────────────────────────────────────────── info "Creating new OEMDRV partition (${OEMDRV_START}–${OEMDRV_END} MiB) on $WORK_DISK..." -parted -s "$WORK_DISK" mkpart primary btrfs "${OEMDRV_START}MiB" "${OEMDRV_END}MiB" \ +printf 'Yes\n' | parted "$WORK_DISK" mkpart primary btrfs "${OEMDRV_START}MiB" "${OEMDRV_END}MiB" \ || die "parted mkpart failed. Check that the target area is free space on $WORK_DISK." partprobe "$WORK_DISK" -- 2.52.0 From 5ef48c84e5a819f405006aa36d9b056b38220aa9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20unbrot=20P=C3=A4tzold?= Date: Mon, 27 Apr 2026 16:40:18 +0200 Subject: [PATCH 11/13] Replace parted resizepart with sfdisk --force parted fails to resize a mounted partition non-interactively. sfdisk --no-reread --force writes the updated partition table directly without TTY/confirmation issues, and partprobe re-reads it afterwards. Co-Authored-By: Claude Sonnet 4.6 --- system_setup/install.sh | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/system_setup/install.sh b/system_setup/install.sh index 3615166..f0c5a5f 100755 --- a/system_setup/install.sh +++ b/system_setup/install.sh @@ -377,9 +377,16 @@ if [[ "$MODE" == "shrink" ]]; then fi info "Shrinking partition $S_PNUM to ${NEW_END} MiB in partition table..." - # Use printf instead of -s so parted's "partition is busy" confirmation is answered Yes - printf 'Yes\n' | parted "$S_DISK" resizepart "$S_PNUM" "${NEW_END}MiB" \ - || die "parted resizepart failed." + SECTOR_SIZE=$(cat /sys/block/"${S_DISK##*/}"/queue/hw_sector_size 2>/dev/null || echo 512) + NEW_END_SEC=$(( NEW_END * 1048576 / SECTOR_SIZE )) + sfdisk -d "$S_DISK" | awk -v dev="$S_DEV" -v new_end="$NEW_END_SEC" ' + $0 ~ "^" dev " : " { + match($0, /start= *([0-9]+)/, a) + sub(/size= *[0-9]+/, "size= " (new_end - a[1]+0)) + } + { print } + ' | sfdisk --no-reread --force "$S_DISK" \ + || die "sfdisk partition resize failed." fi # ── Create OEMDRV partition ─────────────────────────────────────────────────── -- 2.52.0 From 296026b66717f90374d112dc7097f76781cf1c50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20unbrot=20P=C3=A4tzold?= Date: Mon, 27 Apr 2026 16:45:16 +0200 Subject: [PATCH 12/13] Set GPT partition name to anacondainstall for OEMDRV partition Co-Authored-By: Claude Sonnet 4.6 --- system_setup/install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system_setup/install.sh b/system_setup/install.sh index f0c5a5f..6496b26 100755 --- a/system_setup/install.sh +++ b/system_setup/install.sh @@ -392,7 +392,7 @@ fi # ── Create OEMDRV partition ─────────────────────────────────────────────────── info "Creating new OEMDRV partition (${OEMDRV_START}–${OEMDRV_END} MiB) on $WORK_DISK..." -printf 'Yes\n' | parted "$WORK_DISK" mkpart primary btrfs "${OEMDRV_START}MiB" "${OEMDRV_END}MiB" \ +printf 'Yes\n' | parted "$WORK_DISK" mkpart anacondainstall btrfs "${OEMDRV_START}MiB" "${OEMDRV_END}MiB" \ || die "parted mkpart failed. Check that the target area is free space on $WORK_DISK." partprobe "$WORK_DISK" -- 2.52.0 From 1f17ef5c2f3d0bcccb518e2ea2682322a2a36045 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20unbrot=20P=C3=A4tzold?= Date: Mon, 27 Apr 2026 17:15:06 +0200 Subject: [PATCH 13/13] Update install.md: download script before running with sudo Process substitution does not survive sudo, so the script must be downloaded to a temp file first. Co-Authored-By: Claude Sonnet 4.6 --- install.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/install.md b/install.md index 9e49a77..caec2b1 100644 --- a/install.md +++ b/install.md @@ -19,11 +19,12 @@ the script `./system_setup/install.sh` prepares a target machine for automated F ## Run directly from the repository -```bash -bash <(curl -fsSL https://gitea.dtext.online/obel1x/fedora-OEMDRV/raw/branch/main/system_setup/install.sh) -``` +Download the script first, then run it as root: -> The script requires root. Run the command above as root, or prefix with `sudo`. +```bash +curl -fsSL https://gitea.dtext.online/obel1x/fedora-OEMDRV/raw/branch/main/system_setup/install.sh -o /tmp/install.sh +sudo bash /tmp/install.sh +``` ## After the script completes -- 2.52.0