|
| 1 | +#!/bin/sh |
| 2 | +# SPDX-License-Identifier: LGPL-2.1-or-later |
| 3 | +# |
| 4 | +# flipper-bls.sh - shared library for Flipper One BLS boot-entry generation. |
| 5 | +# |
| 6 | +# SOURCED, never executed. Defines the helpers used by the kernel-install plugin |
| 7 | +# (90-loaderentry.install) and the btrfs snapshot tooling (create_profile/rename_profile). |
| 8 | +# Functions read globals lazily (at call time), so callers set what they need first. |
| 9 | +# The kernel/initrd staging and devicetree(dir) handling are derived from systemd's |
| 10 | +# 90-loaderentry.install (LGPL-2.1-or-later). |
| 11 | + |
| 12 | +VENDOR=rockchip |
| 13 | +TITLE_MAX="${FLIPPER_TITLE_MAX:-26}" |
| 14 | + |
| 15 | +log() { [ "${KERNEL_INSTALL_VERBOSE:-0}" -gt 0 ] && echo "flipper-bls: $*" >&2; return 0; } |
| 16 | +die() { echo "flipper-bls: $*" >&2; exit 1; } |
| 17 | +trim() { printf '%s' "$1" | sed 's/^ *//; s/ *$//'; } |
| 18 | +# copy a file into the entry dir as root:root 0644 |
| 19 | +stage_file() { install -m 0644 -o root -g root "$1" "$2" || die "could not copy '$1'"; } |
| 20 | + |
| 21 | +# absolute path under BOOT_ROOT -> path as it must appear inside a BLS entry (relative to the |
| 22 | +# boot partition; identical to the absolute path while /boot is on the rootfs). On Flipper One |
| 23 | +# BOOT_MNT=/ (/boot is a subvol on the root partition, never a separate boot partition). |
| 24 | +boot_rel() { if [ "${BOOT_MNT:-/}" = "/" ]; then printf '%s\n' "$1"; else printf '%s\n' "${1#"${BOOT_MNT}"}"; fi; } |
| 25 | + |
| 26 | +has_config() { [ -f "$KCONFIG" ] && grep -q "^CONFIG_$1=[ym]" "$KCONFIG"; } |
| 27 | + |
| 28 | +# Last rootflags=subvol= value in a cmdline/options string (empty if none). |
| 29 | +subvol_of() { printf '%s' "$1" | tr ' ' '\n' | sed -n 's/^rootflags=subvol=//p' | tail -n1; } |
| 30 | + |
| 31 | +# BLS sort-key from os-release (IMAGE_ID, else ID); empty if no os-release. |
| 32 | +os_sort_key() { [ -r /etc/os-release ] && ( . /etc/os-release; printf '%s' "${IMAGE_ID:-$ID}" ) || :; } |
| 33 | + |
| 34 | +# Set DTB_DIR + DTB_DIRS (primary dir + modules fallback) for the current $KERNEL_VERSION. |
| 35 | +set_dtb_dirs() { DTB_DIR="/usr/lib/linux-image-$KERNEL_VERSION"; DTB_DIRS="$DTB_DIR /usr/lib/modules/$KERNEL_VERSION/dtb"; } |
| 36 | + |
| 37 | +# Kernel entry-token for a root: <NN>-flipperos-<subvol sans @>, carrying the menu band + root id. |
| 38 | +# Empty NN -> plain flipperos-<name> (sorts above the numbered bands, like an unmappable root). |
| 39 | +make_token() { # $1 = NN (may be empty) $2 = subvol |
| 40 | + _n="$(printf '%s' "$2" | tr -d '@')" |
| 41 | + [ -n "$1" ] && printf '%s-flipperos-%s' "$1" "$_n" || printf 'flipperos-%s' "$_n" |
| 42 | +} |
| 43 | + |
| 44 | +# Reflink the shared /boot kernel + initrd for $KERNEL_VERSION into this $ENTRY_TOKEN's staging |
| 45 | +# dir (/boot/$ENTRY_TOKEN/$KERNEL_VERSION); btrfs shares the extents, so per-root dirs are ~free. |
| 46 | +# Sets KERNEL_ENTRY + INITRD_ENTRIES. For paths the kernel-install plugin does not stage itself |
| 47 | +# (the build fan-out and the clone tooling). |
| 48 | +stage_kernel_reflink() { |
| 49 | + _sd="/boot/$ENTRY_TOKEN/$KERNEL_VERSION" |
| 50 | + mkdir -p "$_sd" || die "cannot create $_sd" |
| 51 | + cp -a --reflink=auto "/boot/vmlinuz-$KERNEL_VERSION" "$_sd/linux" || die "no /boot/vmlinuz-$KERNEL_VERSION" |
| 52 | + KERNEL_ENTRY="$(boot_rel "$_sd/linux")" |
| 53 | + INITRD_ENTRIES="" |
| 54 | + if [ -e "/boot/initrd.img-$KERNEL_VERSION" ]; then |
| 55 | + cp -a --reflink=auto "/boot/initrd.img-$KERNEL_VERSION" "$_sd/initrd.img-$KERNEL_VERSION" |
| 56 | + INITRD_ENTRIES="$(boot_rel "$_sd/initrd.img-$KERNEL_VERSION")" |
| 57 | + fi |
| 58 | +} |
| 59 | + |
| 60 | +# U-Boot reads the btrfs TOP LEVEL (subvolid 5), so in-entry devicetreedir/overlay paths need the |
| 61 | +# entry's own root subvol prefixed (/@<subvol>/usr/lib/...), taken from its rootflags=subvol=. |
| 62 | +# Set FDT_SUBVOL_PREFIX to override (e.g. "" if the root isn't a subvol). |
| 63 | +fdt_prefix() { # $1 = options string -> "/<rootflags-subvol>" (or $FDT_SUBVOL_PREFIX if set) |
| 64 | + if [ "${FDT_SUBVOL_PREFIX+x}" = x ]; then printf '%s' "$FDT_SUBVOL_PREFIX"; return 0; fi |
| 65 | + _sv=$(subvol_of "$1") |
| 66 | + [ -n "$_sv" ] && printf '/%s' "$_sv" |
| 67 | + return 0 |
| 68 | +} |
| 69 | + |
| 70 | +# btrfs subvol of the currently-booted root (e.g. @Desktop); empty if not a subvol. |
| 71 | +current_subvol() { findmnt -nro FSROOT / 2>/dev/null | sed 's,^/,,'; } |
| 72 | + |
| 73 | +# Remove loader entries whose options select SUBVOL and whose version == VERSION. Content-based |
| 74 | +# (token/filename-agnostic), so it leaves every other root's entries untouched. Uses $ENTRIES. |
| 75 | +remove_entries() { # $1 = subvol $2 = version |
| 76 | + [ -n "$1" ] && [ -n "$2" ] || return 0 |
| 77 | + for _c in "${ENTRIES:-/boot/loader/entries}"/*.conf; do |
| 78 | + [ -f "$_c" ] || continue |
| 79 | + [ "$(awk '$1=="version"{print $2; exit}' "$_c")" = "$2" ] || continue |
| 80 | + _es="$(awk '$1=="options"{for(i=2;i<=NF;i++) if($i ~ /^rootflags=subvol=/) s=$i} |
| 81 | + END{sub(/^rootflags=subvol=/,"",s); print s}' "$_c")" |
| 82 | + [ "$_es" = "$1" ] && rm -f "$_c" |
| 83 | + done |
| 84 | + return 0 # a non-matching last entry must not fail the loop under set -e |
| 85 | +} |
| 86 | + |
| 87 | +# Tear down a whole root: remove EVERY entry that selects SUBVOL (all versions) and each one's |
| 88 | +# /boot/<token>/ staging tree. The staging dir is derived from the entry's own 'linux' line, so it |
| 89 | +# works no matter how the token was computed. For delete_profile/rename_profile (whole-profile ops). |
| 90 | +# Uses $ENTRIES. Prints what it removes to stderr. |
| 91 | +remove_root_entries() { # $1 = subvol |
| 92 | + [ -n "$1" ] || return 0 |
| 93 | + for _c in "${ENTRIES:-/boot/loader/entries}"/*.conf; do |
| 94 | + [ -f "$_c" ] || continue |
| 95 | + _es="$(awk '$1=="options"{for(i=2;i<=NF;i++) if($i ~ /^rootflags=subvol=/) s=$i} |
| 96 | + END{sub(/^rootflags=subvol=/,"",s); print s}' "$_c")" |
| 97 | + [ "$_es" = "$1" ] || continue |
| 98 | + _lx="$(awk '$1=="linux"{print $2; exit}' "$_c")" |
| 99 | + rm -f "$_c"; echo "removed boot entry $_c" >&2 |
| 100 | + case "$_lx" in /boot/*/*/linux) |
| 101 | + _td="${_lx%/*/linux}"; [ "$_td" != /boot ] && rm -rf "$_td" ;; |
| 102 | + esac |
| 103 | + done |
| 104 | + return 0 |
| 105 | +} |
| 106 | + |
| 107 | +# Base menu slot (900,800,700,... by flipper-profiles position) for the profile $1 belongs to. |
| 108 | +# $1 is a btrfs-PARENT name. It anchors on an ACTUAL profile: the exact name (@Desktop) or its |
| 109 | +# golden base / snapshot (@Desktop_stock, @Desktop_<stamp>: profile name followed by '_'). A |
| 110 | +# hyphen-suffixed clone (@Desktop-2) must NOT match here, so origin_base_depth can walk THROUGH |
| 111 | +# it to count clone depth. Empty if none. Steps of 100 leave room for ~99 variants per band. |
| 112 | +profile_base_nn() { |
| 113 | + _want="$1"; _pf="${PROFILES:-/etc/kernel/flipper-profiles}"; [ -f "$_pf" ] || return 0 |
| 114 | + _j=0 |
| 115 | + while IFS='|' read -r _t _s _e _g _d _l; do |
| 116 | + _t="$(echo "$_t" | tr -d '[:space:]')"; case "$_t" in ''|\#*) continue ;; esac |
| 117 | + _sv="$(subvol_of "$_e" | tr -d '[:space:]')" |
| 118 | + [ -n "$_sv" ] && case "$_want" in "$_sv"|"$_sv"_*) printf '%03d' "$((900 - _j * 100))"; return 0 ;; esac |
| 119 | + _j=$((_j + 1)) |
| 120 | + done <"$_pf" |
| 121 | + return 0 |
| 122 | +} |
| 123 | + |
| 124 | +# Origin-profile base slot AND clone depth for a derived root. Clone/snapshot names are arbitrary |
| 125 | +# and can chain (a snapshot of a snapshot), so instead of matching the name we WALK the btrfs |
| 126 | +# parent_uuid chain up from the subvol at $1, counting hops, until an ancestor is a listed profile |
| 127 | +# or its golden base (profile_base_nn). Prints "BASE DEPTH" (e.g. "900 2" = desktop, grandparent); |
| 128 | +# depth is the hop count (1 = direct clone, 2 = clone of a clone, ...). Empty if none maps or no |
| 129 | +# btrfs. One `subvolume list` covers the whole fs (ancestors need not be mounted). |
| 130 | +origin_base_depth() { # $1 = mounted path (default /) |
| 131 | + command -v btrfs >/dev/null 2>&1 || return 0 |
| 132 | + _mp="${1:-/}" |
| 133 | + _cur="$(btrfs subvolume show "$_mp" 2>/dev/null | sed -n 's/.*[Pp]arent UUID:[[:space:]]*//p' | head -n1)" |
| 134 | + [ -n "$_cur" ] && [ "$_cur" != "-" ] || return 0 |
| 135 | + # fs-wide table: one row "uuid parent_uuid name" per subvolume |
| 136 | + _tbl="$(btrfs subvolume list -qu "$_mp" 2>/dev/null | awk '{ u="-"; p="-"; for(i=1;i<NF;i++){ if($i=="uuid")u=$(i+1); else if($i=="parent_uuid")p=$(i+1) } print u, p, $NF }')" |
| 137 | + _seen=" "; _depth=0 |
| 138 | + while [ -n "$_cur" ] && [ "$_cur" != "-" ]; do |
| 139 | + case "$_seen" in *" $_cur "*) break ;; esac # cycle guard |
| 140 | + _seen="$_seen$_cur " |
| 141 | + _row="$(printf '%s\n' "$_tbl" | awk -v u="$_cur" '$1==u{print $2, $3; exit}')" |
| 142 | + [ -n "$_row" ] || break |
| 143 | + _depth=$((_depth + 1)) |
| 144 | + _pn="${_row#* }"; _pn="${_pn##*/}" # this ancestor's name (basename) |
| 145 | + _bn="$(profile_base_nn "$_pn")" |
| 146 | + [ -n "$_bn" ] && { printf '%s %s' "$_bn" "$_depth"; return 0; } |
| 147 | + _cur="${_row%% *}" # follow parent_uuid to the grandparent |
| 148 | + done |
| 149 | + return 0 |
| 150 | +} |
| 151 | + |
| 152 | +# Clone menu slot for a derived root: origin base + clone-depth (direct clone -> base+1, |
| 153 | +# clone of a clone -> base+2, ...), so clones sit just above the profile (whose factory and |
| 154 | +# in-profile-install entries share the base slot). $1 = mounted path; $2 = optional origin hint |
| 155 | +# (the name it was restored FROM). Prefers the parent-chain walk (true depth); if that yields |
| 156 | +# nothing (e.g. parent_uuid left dangling by a move/receive) it falls back to the hint as a direct |
| 157 | +# clone (depth 1 -> base+1). Prints a 3-digit NN, or empty if unresolved. |
| 158 | +clone_slot() { # $1 = mounted path (default /); $2 = origin hint (optional) |
| 159 | + _cs_bd="$(origin_base_depth "${1:-/}")" |
| 160 | + if [ -n "$_cs_bd" ]; then |
| 161 | + printf '%03d' "$(( ${_cs_bd%% *} + ${_cs_bd#* } ))"; return 0 |
| 162 | + fi |
| 163 | + if [ -n "${2:-}" ]; then |
| 164 | + _cs_b="$(profile_base_nn "${2##*/}")" |
| 165 | + [ -n "$_cs_b" ] && printf '%03d' "$(( _cs_b + 1 ))" |
| 166 | + fi |
| 167 | + return 0 |
| 168 | +} |
| 169 | + |
| 170 | +# Menu titles must fit the small screen: cap total at TITLE_MAX by trimming the (long) version |
| 171 | +# while keeping the suffix intact. (Filenames + the 'version' field keep the full version.) |
| 172 | +make_title() { |
| 173 | + _suf="$1" |
| 174 | + _avail=$(( TITLE_MAX - ${#_suf} - 1 )) |
| 175 | + if [ "$_avail" -ge 1 ]; then |
| 176 | + printf '%s %s' "$(printf '%s' "$KERNEL_VERSION" | cut -c"1-$_avail")" "$_suf" |
| 177 | + else |
| 178 | + printf '%s' "$_suf" | cut -c"1-$TITLE_MAX" |
| 179 | + fi |
| 180 | +} |
| 181 | + |
| 182 | +# write_entry FILE TITLE OPTIONS FDTOVERLAYS |
| 183 | +write_entry() { |
| 184 | + _f="$1"; _title="$2"; _opts="$3"; _fdtov="$4" |
| 185 | + # devicetreedir is prefixed with THIS entry's root subvol (U-Boot reads subvolid 5) |
| 186 | + _dtdir=""; [ -n "$DEVICETREEDIR_REL" ] && _dtdir="$(fdt_prefix "$_opts")$DEVICETREEDIR_REL" |
| 187 | + { |
| 188 | + echo "# Boot Loader Specification type#1 entry (Flipper One)" |
| 189 | + echo "title $_title" |
| 190 | + echo "version $KERNEL_VERSION" |
| 191 | + [ -n "$MACHINE_ID" ] && [ "$ENTRY_TOKEN" = "$MACHINE_ID" ] && echo "machine-id $MACHINE_ID" |
| 192 | + [ -n "$SORT_KEY" ] && echo "sort-key $SORT_KEY" |
| 193 | + [ -n "$_opts" ] && echo "options $_opts" |
| 194 | + echo "linux $KERNEL_ENTRY" |
| 195 | + [ -n "$DEVICETREE_ENTRY" ] && echo "devicetree $DEVICETREE_ENTRY" |
| 196 | + [ -n "$_dtdir" ] && echo "devicetreedir $_dtdir" |
| 197 | + [ -n "$_fdtov" ] && echo "devicetree-overlay $_fdtov" |
| 198 | + for _ird in $INITRD_ENTRIES; do echo "initrd $_ird"; done |
| 199 | + } >"$_f" |
| 200 | + return 0 # don't let a trailing empty conditional's status abort `set -e` |
| 201 | +} |
| 202 | + |
| 203 | +# Echo the space-separated paths of the named DT overlays (flat, next to the DTBs). |
| 204 | +# Returns non-zero with no output if any one is missing. |
| 205 | +overlay_paths() { # $1 = subvol prefix (e.g. /@Desktop); remaining args = overlay names |
| 206 | + _pfx="$1"; shift |
| 207 | + _paths="" |
| 208 | + for _n in "$@"; do |
| 209 | + [ -f "$OVERLAY_DIR/$_n.dtbo" ] || return 1 # check the real on-disk path |
| 210 | + _paths="$_paths${_paths:+ }$_pfx$OVERLAY_DIR/$_n.dtbo" |
| 211 | + done |
| 212 | + printf '%s' "$_paths" |
| 213 | +} |
| 214 | + |
| 215 | +# Set BASE_OPTS from CONF_ROOT/cmdline (root=UUID + policy) + this kernel's console layout. |
| 216 | +# FIQ kernel -> console=ttyFIQ0 ; mainline -> console=ttyS0 + ttyS4 + fbcon=map:1. |
| 217 | +compute_base_opts() { |
| 218 | + if [ -f "$CONF_ROOT/cmdline" ]; then |
| 219 | + BASE_OPTS="$(grep -Gv '^\s*#' "$CONF_ROOT/cmdline" | tr -s '[:space:]' ' ')" |
| 220 | + elif [ -f /usr/lib/kernel/cmdline ]; then |
| 221 | + BASE_OPTS="$(grep -Gv '^\s*#' /usr/lib/kernel/cmdline | tr -s '[:space:]' ' ')" |
| 222 | + else |
| 223 | + BASE_OPTS="" |
| 224 | + fi |
| 225 | + BASE_OPTS="$(trim "$BASE_OPTS")" |
| 226 | + # Pin systemd.machine_id= only when entries are named after the machine-id (no-op otherwise). |
| 227 | + if [ -n "$MACHINE_ID" ] && [ "$ENTRY_TOKEN" = "$MACHINE_ID" ] && ! echo "$BASE_OPTS" | grep -q "systemd.machine_id="; then |
| 228 | + BASE_OPTS="$BASE_OPTS systemd.machine_id=$MACHINE_ID" |
| 229 | + fi |
| 230 | + if has_config FIQ_DEBUGGER_CONSOLE; then |
| 231 | + BASE_OPTS="$(echo " $BASE_OPTS " | sed 's/ console=ttyS0,1500000n8 / /g')" |
| 232 | + BASE_OPTS="$(trim "$BASE_OPTS") console=ttyFIQ0,1500000n8" |
| 233 | + else |
| 234 | + BASE_OPTS="$(echo " $BASE_OPTS " | sed 's/ console=ttyS0,1500000n8 / /g')" |
| 235 | + BASE_OPTS="$(trim "$BASE_OPTS") console=ttyS0,1500000n8" |
| 236 | + BASE_OPTS="$(echo " $BASE_OPTS " | sed 's/ console=ttyS4,1500000n8 / /g')" |
| 237 | + BASE_OPTS="$(trim "$BASE_OPTS") console=ttyS4,1500000n8" |
| 238 | + BASE_OPTS="$(echo " $BASE_OPTS " | sed 's/ console=ttyFIQ0,1500000n8 / /g')" |
| 239 | + BASE_OPTS="$(trim "$BASE_OPTS") fbcon=map:1" |
| 240 | + fi |
| 241 | +} |
| 242 | + |
| 243 | +# Set DEVICETREEDIR_REL + OVERLAY_DIR for $KERNEL_VERSION (needs DTB_DIR/DTB_DIRS set). Opt-in via a |
| 244 | +# /etc/kernel/devicetreedir boolean, only when no single 'devicetree' is configured. Always the |
| 245 | +# kernel's own installed DTB dir (first existing of DTB_DIRS), never inspected or borrowed; if it |
| 246 | +# ships none, no devicetreedir is written and U-Boot falls back to its control FDT. |
| 247 | +discover_devicetreedir() { |
| 248 | + DEVICETREEDIR_SRC=""; DEVICETREEDIR_REL="" |
| 249 | + if [ -z "${DEVICETREE:-}" ] && [ -f "$CONF_ROOT/devicetreedir" ]; then |
| 250 | + _v=""; read -r _v <"$CONF_ROOT/devicetreedir" || : |
| 251 | + case "$_v" in |
| 252 | + 1|[Yy]|[Yy][Ee][Ss]|[Tt]|[Tt][Rr][Uu][Ee]|[Oo][Nn]) |
| 253 | + for p in $DTB_DIRS; do |
| 254 | + [ -d "$p" ] && { DEVICETREEDIR_SRC="$p"; break; } |
| 255 | + done |
| 256 | + if [ -n "$DEVICETREEDIR_SRC" ]; then |
| 257 | + DEVICETREEDIR_REL="$(boot_rel "$DEVICETREEDIR_SRC")" # /usr/lib/...; prefixed per entry |
| 258 | + else |
| 259 | + log "$KERNEL_VERSION ships no DTB dir; omitting devicetreedir (U-Boot control FDT)" |
| 260 | + fi |
| 261 | + ;; |
| 262 | + esac |
| 263 | + fi |
| 264 | + # DT overlays sit flat next to the .dtb files (upstream layout), in the same vendor dir. |
| 265 | + OVERLAY_DIR="${DEVICETREEDIR_SRC:-$DTB_DIR}/$VENDOR" |
| 266 | +} |
| 267 | + |
| 268 | +# emit_entry SUF EXTRA GATE DTBOS -> write one BLS entry for the current $ENTRY_TOKEN. |
| 269 | +# The filename is $ENTRY_TOKEN-$KERNEL_VERSION.conf. The token is <NN>-flipperos-<subvol>, so it |
| 270 | +# LEADS with the 3-digit menu band and U-Boot's bls (filename sort, descending) orders entries by |
| 271 | +# band then version. EXTRA carries this entry's rootflags=subvol=; BASE_OPTS has none, so there is |
| 272 | +# exactly one per entry. Honours gate (skip if a CONFIG_ symbol is missing) + overlays. |
| 273 | +emit_entry() { |
| 274 | + _suf="$1"; _extra="$2"; _gate="$3"; _dtbos="$4" |
| 275 | + |
| 276 | + # gate may list several config symbols; all must be present in this kernel. |
| 277 | + for _g in $_gate; do |
| 278 | + has_config "$_g" || { log "skip $ENTRY_TOKEN (CONFIG_$_g not set for $KERNEL_VERSION)"; return 0; } |
| 279 | + done |
| 280 | + |
| 281 | + _opts="$BASE_OPTS" |
| 282 | + [ -n "$_extra" ] && _opts="$_opts $_extra" |
| 283 | + _fn="$ENTRIES/$ENTRY_TOKEN-$KERNEL_VERSION.conf" |
| 284 | + |
| 285 | + if [ -z "$_dtbos" ]; then |
| 286 | + write_entry "$_fn" "$(make_title "$_suf")" "$_opts" "" |
| 287 | + return 0 |
| 288 | + fi |
| 289 | + |
| 290 | + # A leading '!' on the overlay list = required (skip the entry if the overlays are |
| 291 | + # absent); otherwise optional (the entry is still written, sans overlay). |
| 292 | + _required=0 |
| 293 | + case "$_dtbos" in '!'*) _required=1; _dtbos="$(trim "${_dtbos#\!}")" ;; esac |
| 294 | + |
| 295 | + _paths="$(overlay_paths "$(fdt_prefix "$_opts")" $_dtbos)" || _paths="" |
| 296 | + if [ -z "$_paths" ] && [ "$_required" = 1 ]; then |
| 297 | + log "skip $ENTRY_TOKEN (overlays not found for $KERNEL_VERSION)" |
| 298 | + return 0 |
| 299 | + fi |
| 300 | + write_entry "$_fn" "$(make_title "$_suf")" "$_opts" "$_paths" |
| 301 | +} |
| 302 | + |
| 303 | +# flipper_write_entry <subvol> <mounted-path> [<origin-hint>]: write a BLS entry for an EXISTING |
| 304 | +# writable top-level subvol (a restored snapshot/clone). Token = <NN>-flipperos-<name>, NN from |
| 305 | +# clone_slot (origin base + depth; the origin hint is the dangling-parent fallback). Reflinks |
| 306 | +# the shared /boot kernel into the root's own /boot/<token>/ dir. Sourced by create_profile / |
| 307 | +# rename_profile. Returns non-zero (no exit) on a recoverable problem. |
| 308 | +flipper_write_entry() { |
| 309 | + _name="$1"; _snap="$2"; _origin="${3:-}" |
| 310 | + { [ -n "$_name" ] && [ -d "$_snap" ]; } || { echo "flipper-bls: usage: flipper_write_entry <name> <mounted-path>" >&2; return 1; } |
| 311 | + ENTRIES="${ENTRIES:-/boot/loader/entries}" |
| 312 | + CONF_ROOT="${KERNEL_INSTALL_CONF_ROOT:-/etc/kernel}" |
| 313 | + MACHINE_ID=""; DEVICETREE=""; DEVICETREE_ENTRY="" |
| 314 | + # version from the target's OWN tree, so modules + dtbs are self-consistent |
| 315 | + KERNEL_VERSION="$(ls -1d "$_snap"/usr/lib/linux-image-* 2>/dev/null | sed 's,.*/linux-image-,,' | sort -V | tail -n1)" |
| 316 | + [ -n "$KERNEL_VERSION" ] || { echo "flipper-bls: no /usr/lib/linux-image-* inside $_name" >&2; return 1; } |
| 317 | + [ -e "/boot/vmlinuz-$KERNEL_VERSION" ] || { echo "flipper-bls: kernel not in /boot: vmlinuz-$KERNEL_VERSION" >&2; return 1; } |
| 318 | + KCONFIG="/boot/config-$KERNEL_VERSION" |
| 319 | + set_dtb_dirs |
| 320 | + SORT_KEY="$(os_sort_key)" |
| 321 | + ENTRY_TOKEN="$(make_token "$(clone_slot "$_snap" "$_origin")" "$_name")" |
| 322 | + mkdir -p "$ENTRIES" || { echo "flipper-bls: cannot create $ENTRIES" >&2; return 1; } |
| 323 | + compute_base_opts |
| 324 | + stage_kernel_reflink |
| 325 | + # devicetreedir is the target root's OWN kernel dir (KERNEL_VERSION came from it, so it exists). |
| 326 | + DEVICETREEDIR_REL="/usr/lib/linux-image-$KERNEL_VERSION" |
| 327 | + OVERLAY_DIR="$DTB_DIR/$VENDOR" |
| 328 | + # pin the root's own entry-token so a later runtime apt install in it lands in the same band |
| 329 | + mkdir -p "$_snap/etc/kernel" && printf '%s\n' "$ENTRY_TOKEN" > "$_snap/etc/kernel/entry-token" |
| 330 | + emit_entry "$(printf '%s' "$_name" | tr -d '@')" "rootflags=subvol=$_name" "" "" |
| 331 | + echo "flipper-bls: wrote entry for $_name (kernel $KERNEL_VERSION, token $ENTRY_TOKEN)" |
| 332 | +} |
0 commit comments