123 lines
3.7 KiB
Bash
Executable File
123 lines
3.7 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# cxos/vendor/linux/fetch.sh — download + verify + extract Linux LTS tarball.
|
|
#
|
|
# Reads cxos/vendor/linux/PINNED.json. Hard-fails if SHA-256 mismatches or
|
|
# (when --gpg is passed) the GPG signature does not validate against any of
|
|
# the pinned signing keys.
|
|
#
|
|
# Usage:
|
|
# cxos/vendor/linux/fetch.sh # SHA-256 only
|
|
# cxos/vendor/linux/fetch.sh --gpg # require valid GPG signature
|
|
# cxos/vendor/linux/fetch.sh --dry # print plan; no network
|
|
#
|
|
# Outputs:
|
|
# cxos/vendor/linux/linux-<ver>.tar.xz (cached, gitignored)
|
|
# cxos/vendor/linux/src/linux-<ver>/ (extracted, gitignored)
|
|
set -euo pipefail
|
|
|
|
DRY=0
|
|
GPG=0
|
|
for arg in "$@"; do
|
|
case "$arg" in
|
|
--dry) DRY=1 ;;
|
|
--gpg) GPG=1 ;;
|
|
-h|--help)
|
|
sed -n '2,15p' "$0" | sed 's/^# \{0,1\}//'
|
|
exit 0 ;;
|
|
*)
|
|
echo "fetch.sh: unknown arg: $arg" >&2
|
|
exit 2 ;;
|
|
esac
|
|
done
|
|
|
|
HERE="$(cd "$(dirname "$0")" && pwd)"
|
|
PINNED="$HERE/PINNED.json"
|
|
|
|
if [[ ! -f "$PINNED" ]]; then
|
|
echo "fetch.sh: missing $PINNED" >&2
|
|
exit 1
|
|
fi
|
|
|
|
# Parse PINNED.json with python3 (always available in the dev container).
|
|
read -r VERSION TARBALL_URL SIG_URL SHA256 EXTRACTED_DIR < <(
|
|
python3 - "$PINNED" <<'PY'
|
|
import json, sys
|
|
d = json.load(open(sys.argv[1]))
|
|
print(d["version"], d["tarball_url"], d["signature_url"],
|
|
d["sha256"], d["extracted_dir"])
|
|
PY
|
|
)
|
|
|
|
TARBALL="$HERE/linux-${VERSION}.tar.xz"
|
|
SIGNATURE="$HERE/linux-${VERSION}.tar.sign"
|
|
SRC_DIR="$HERE/src"
|
|
|
|
echo "==> linux ${VERSION}"
|
|
echo " tarball: ${TARBALL_URL}"
|
|
echo " sha256 : ${SHA256}"
|
|
echo " extract: ${SRC_DIR}/${EXTRACTED_DIR}"
|
|
|
|
if [[ "$DRY" == "1" ]]; then
|
|
echo "==> dry-run; nothing fetched"
|
|
exit 0
|
|
fi
|
|
|
|
mkdir -p "$SRC_DIR"
|
|
|
|
# 1. Tarball ----------------------------------------------------------------
|
|
if [[ ! -f "$TARBALL" ]]; then
|
|
echo "==> downloading $(basename "$TARBALL")"
|
|
curl -fsSL --retry 3 -o "$TARBALL" "$TARBALL_URL"
|
|
fi
|
|
|
|
# 2. SHA-256 ----------------------------------------------------------------
|
|
echo "==> verifying sha256"
|
|
ACTUAL_SHA="$(sha256sum "$TARBALL" | awk '{print $1}')"
|
|
if [[ "$ACTUAL_SHA" != "$SHA256" ]]; then
|
|
echo "fetch.sh: SHA-256 mismatch" >&2
|
|
echo " expected: $SHA256" >&2
|
|
echo " actual : $ACTUAL_SHA" >&2
|
|
rm -f "$TARBALL"
|
|
exit 1
|
|
fi
|
|
echo " ok"
|
|
|
|
# 3. GPG (optional) ---------------------------------------------------------
|
|
if [[ "$GPG" == "1" ]]; then
|
|
if ! command -v gpg >/dev/null 2>&1; then
|
|
echo "fetch.sh: --gpg requested but gpg not installed" >&2
|
|
exit 1
|
|
fi
|
|
if [[ ! -f "$SIGNATURE" ]]; then
|
|
echo "==> downloading $(basename "$SIGNATURE")"
|
|
curl -fsSL --retry 3 -o "$SIGNATURE" "$SIG_URL"
|
|
fi
|
|
XZ_TMP="$(mktemp -d)"
|
|
trap 'rm -rf "$XZ_TMP"' EXIT
|
|
xz -dk -c "$TARBALL" > "$XZ_TMP/linux-${VERSION}.tar"
|
|
echo "==> verifying gpg signature"
|
|
# Pull the pinned keys' fingerprints; require at least one matching sig.
|
|
FINGERPRINTS="$(python3 -c '
|
|
import json,sys
|
|
for k in json.load(open(sys.argv[1]))["gpg_signing_keys"]:
|
|
print(k["fingerprint"])' "$PINNED")"
|
|
for fp in $FINGERPRINTS; do
|
|
gpg --keyserver hkps://keys.openpgp.org --recv-keys "$fp" \
|
|
>/dev/null 2>&1 || true
|
|
done
|
|
if ! gpg --verify "$SIGNATURE" "$XZ_TMP/linux-${VERSION}.tar" 2>&1 \
|
|
| grep -qE 'Good signature.*('"$(echo "$FINGERPRINTS" | tr '\n' '|' | sed 's/|$//')"')'; then
|
|
echo "fetch.sh: GPG verification failed" >&2
|
|
exit 1
|
|
fi
|
|
echo " ok"
|
|
fi
|
|
|
|
# 4. Extract ----------------------------------------------------------------
|
|
if [[ ! -d "$SRC_DIR/$EXTRACTED_DIR" ]]; then
|
|
echo "==> extracting"
|
|
tar -xf "$TARBALL" -C "$SRC_DIR"
|
|
fi
|
|
|
|
echo "==> linux source ready: $SRC_DIR/$EXTRACTED_DIR"
|