#!/usr/bin/env bash # Copyright (C) 2016 Paul Kocialkowski # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . BUILD_SYSTEM="libreboot" PROJECTS="projects" SOURCES="sources" BUILD="build" INSTALL="install" RELEASE="release" SYSTEMS="systems" IMAGES="images" TOOLS="tools" CONFIGS="configs" PATCHES="patches" TARGETS="targets" REVISION="revision" BLOBS="blobs" BLOBS_IGNORE="blobs-ignore" BLOBS_DISCOVER="blobs-discover" DOTEPOCH=".epoch" DOTRNDSEED=".rndseed" DOTVERSION=".version" DOTREVISION=".revision" DOTTARFILES=".tarfiles" ARCHIVE="tar.xz" CHECKSUM="sha256sum" DSIG="asc" CONFIG_SHELL="${CONFIG_SHELL:-$(which bash)}" EDITOR="${EDITOR:-$(which vi || true)}" TASKS="${TASKS:-1}" function_check() { local function=$1 declare -f -F "$function" > /dev/null } variable_check() { local variable=$1 test ! -z "${!variable}" } arguments_list() { local argument for argument in "$@" do printf '%s\n' "$argument" done } download_wrapper() { local download_dir="$1" shift local uris=($@) local wget_options=( '--config=/dev/null' '--secure-protocol=PFS' "--directory-prefix=$download_dir" '--continue' '--' ) local curl_options=( '-q' '--continue-at -' '--remote-name' '--retry 20' '--ssl' '--tlsv1.2' '--' ) if hash wget > /dev/null 2>&1; then wget "${wget_options[@]}" "${uris[@]}" elif hash curl > /dev/null 2>&1; then ( cd "$download_dir" curl "${curl_options[@]}" "${uris[@]}" ) else printf '\n%s\n\n' 'Error: Neither wget nor curl were found' 1>&2 return 1 fi } diff_patch_file() { local repository_path="$1" local patch_file_path="$2" # TODO: Improve handling of filenames to avoid gotchas w/ \n, \t, etc. local filename_in_diff="$(sed -rne 's/^-{3}\s+([^ \r\n]*).*/\1/p' "$patch_file_path")" local source_file_path if ! ( grep -E '^-{3}.*/' "$patch_file_path" >/dev/null 2>&1 ); then source_file_path="$repository_path/$filename_in_diff" else source_file_path="$repository_path/${filename_in_diff##*/}" fi patch "$source_file_path" "$patch_file_path" } path_wildcard_expand() { local path=$@ # Evaluation fails with unescaped whitespaces. path=$( printf '%s\n' "$path" | sed "s/ /\\\ /g" ) eval "arguments_list "$path"" } file_checksum_create() { local path=$1 local checksum_path="$path.$CHECKSUM" local name=$( basename "$path" ) local directory_path=$( dirname "$path" ) ( cd "$directory_path" sha256sum "$name" > "$checksum_path" ) } file_checksum_check() { local path=$1 local checksum_path="$path.$CHECKSUM" local name=$( basename "$path" ) local directory_path=$( dirname "$path" ) if ! [[ -f "$checksum_path" ]] then printf 1>&2 '%s\n' 'Could not verify file checksum!' return 1 fi ( cd "$directory_path" sha256sum -c "$checksum_path" ) } file_signature_create() { local path=$1 local signature_path="$path.$DSIG" if [[ -z "$RELEASE_KEY" ]] then return 0 fi gpg --default-key "$RELEASE_KEY" --armor --output "$signature_path" --detach-sign --yes "$path" } file_signature_check() { local path=$1 local signature_path="$path.$DSIG" if ! [[ -f "$signature_path" ]] then printf 1>&2 '%s\n' 'Could not verify file signature!' return 1 fi gpg --armor --verify "$signature_path" "$path" } file_verification_create() { local path=$1 file_checksum_create "$path" file_signature_create "$path" } file_verification_check() { local path=$1 file_checksum_check "$path" file_signature_check "$path" } file_exists_check() { local path=$1 test -f "$path" } directory_filled_check() { local path=$1 if [[ -z "$( ls -A "$path" 2> /dev/null )" ]] then return 1 else return 0 fi } archive_files_create() { local source_path="$1" local directory="$(basename "$source_path")" local tarfiles_path="$source_path/$DOTTARFILES" local revision_path="$source_path/$DOTREVISION" local version_path="$source_path/$DOTVERSION" local epoch_path="$source_path/$DOTEPOCH" local rnd_seed_path="$source_path/$DOTRNDSEED" # Files in "$tarfiles_path" are NUL terminated. # `tr '\0' '\n'` for human-readable output. if git_check "$source_path"; then git_files "$source_path" > "$tarfiles_path" printf '%s\0' "$DOTTARFILES" >> "$tarfiles_path" else find "$source_path" -print0 | env LC_ALL='C.UTF-8' sort -z | sed -z "1d;s,^$source_path/\\?,,;/^$DOTTARFILES\$/d" > "$tarfiles_path" fi for dotfile in "$revision_path" \ "$version_path" \ "$epoch_path" \ "$rnd_seed_path" do if [[ -f "$dotfile" ]]; then printf '%s\0' ".${dotfile##*.}" >> "$tarfiles_path" fi done } archive_files_date() { local source_path="$1" local epoch_path="$source_path/$DOTEPOCH" if [[ -n "$SOURCE_DATE_EPOCH" ]]; then find "$source_path" -execdir touch --no-dereference --date="@$SOURCE_DATE_EPOCH" {} + fi } archive_create() { local archive_path="$1" local source_path="$2" local directory="$3" local tarfiles_path="$source_path/$DOTTARFILES" local directory_path="$(dirname "$archive_path")" mkdir -p "$directory_path" if [[ -z "$directory" ]]; then directory="$(basename "$source_path")" fi archive_files_create "$source_path" archive_files_date "$source_path" local tar_options=( --create --xz --file="$archive_path" --files-from="$tarfiles_path" --transform="s,^,$directory/,S" --no-recursion --warning=no-filename-with-nuls --null --owner=0 --group=0 --numeric-owner ) ( cd "$source_path" tar "${tar_options[@]}" ) } archive_extract() { local archive_path="$1" local destination_path="$2" if [[ -z "$destination_path" ]]; then destination_path="$(dirname "$archive_path")" fi tar -xf "$archive_path" -ps -C "$destination_path" } rootfs_files_create() { local source_path="$1" local directory="$(basename "$source_path")" local tarfiles_path="$source_path/$DOTTARFILES" # Files in "$tarfiles_path" are NUL terminated. # `tr '\0' '\n'` for human-readable output. execute_root find "$source_path" -print0 | env LC_ALL='C.UTF-8' sort -z | sed -z "1d;s,^$source_path/\\?,,;/^$DOTTARFILES\$/d" > "$tarfiles_path" } rootfs_files_date() { local source_path="$1" local epoch_path="$source_path/$DOTEPOCH" if [[ -n "$SOURCE_DATE_EPOCH" ]]; then execute_root find "$source_path" -execdir touch --no-dereference --date="@$SOURCE_DATE_EPOCH" {} + fi } rootfs_create() { local rootfs_path="$1" local source_path="$2" local directory="$3" local tarfiles_path="$source_path/$DOTTARFILES" local directory_path="$(dirname "$rootfs_path")" mkdir -p "$directory_path" if [[ -z "$directory" ]]; then directory="$(basename "$source_path")" fi rootfs_files_create "$source_path" rootfs_files_date "$source_path" local tar_options=( --create --xz --file="$rootfs_path" --files-from="$tarfiles_path" --no-recursion --warning=no-filename-with-nuls --null --owner=0 --group=0 --numeric-owner ) ( cd "$source_path" execute_root tar "${tar_options[@]}" ) execute_root chmod 644 "$rootfs_path" execute_root chown "$USER:$USER" "$rootfs_path" } requirements() { local requirement local requirement_path for requirement in "$@" do requirement_path=$( which "$requirement" || true ) if [[ -z "$requirement_path" ]] then printf 1>&2 '%s\n' "Missing requirement: $requirement" exit 1 fi done } requirements_root() { local requirement local requirement_path for requirement in "$@" do # We need to keep stdout output to show the command. requirement_path=$( execute_root which "$requirement" || true ) if [[ -z "$requirement_path" ]] then printf 1>&2 '%s\n' "Missing requirement: $requirement" exit 1 fi done } arguments_concat() { local delimiter=$1 shift local concat for argument in "$@" do if [[ -n "$concat" ]] then concat="$concat""$delimiter""$argument" else concat="$argument" fi done printf '%s\n' "$concat" } execute_root() { local sudo=$( which sudo 2> /dev/null || true ) local arguments printf 1>&2 '%s' 'Running command as root: ' printf 1>&2 '%b\n' "$*" if [[ -n "$sudo" ]] then sudo "$@" else # Quote arguments for eval through su. arguments=$( printf '%q ' "$@" ) su -c "$arguments" fi }