#!/bin/sh # # cowpacman2srm - Create SystemRescueModules (SRM) from pacman packages installed into the COW space # # Authors: Gerd v. Egidy and Francois Dupoux # SPDX-License-Identifier: GPL-3.0-or-later # # This script is meant to help creating SystemRescueModules (SRM) for SystemRescue. # More info about SRMs and this script can be found at: # https://www.system-rescue.org/Modules/ # # To use first install all packages you want to have in your SRM with pacman. # Default COW (Copy-On-Write) space is a ramdisk, so you usually don't have to do anything # special except provide enough RAM. # You can also enable any systemd services that are in these packages. # # Then call: # cowpacman2srm [-s subcmd] [-c compalg] [-l complevel] [targetfile.srm] # # This script runs in two stages: # 1) During the "prepare" stage all files belonging to pacman packages manually installed # are being copied to a temporary directory # 2) During the "create" stage the SRM file (which is a squash file system) is being created # with the contents stored in the temporary directory # You can either run both stages in a single run (this is the default) or one stage at a time. # If you do not specify any sub-command the script is going to run the two stages in one run. # If you want to customize the contents of the SRM module you can run the "prepare" stage first, # then make customizations in the temporary directory (for example to add extra files) and then # you run the "create" stage to produce the SRM file. # # Copy the .srm file to archisobasedir (default: "sysresccd") on your boot disk # and add the "loadsrm" boot parameter to SystemRescue. # # There is no mechanism to check if a SRM is compatible with the version of SystemRescue # you are trying to run it with. So it is higly recommended to only use this script on # the exact version of SystemRescue you plan to use the SRM with. # # default paths for temp files TMP_TARGET=/tmp/srm_content/ PKG_FILELIST=${TMP_TARGET}filelist # default options COMPRESS=zstd COMPLEVEL="" SUBCMD="all" OPER_PREPARE=0 OPER_CREATE=0 function usage() { echo echo "Usage: cowpacman2srm [-s subcmd] [-c compalg] [-l complevel] [targetfile.srm]" echo echo "options:" echo "- subcmd is a sub-command to execute, it can be any of: prepare, create, all. (default: all)" echo "- compalg is any of the compression algorithms supported by mksquashfs (default: zstd)" echo "- complevel is the compression level for the given algorithm (if supported with -Xcompression-level)" echo exit 1 } function prepare() { # determine the COW target upperdir, read it out from the mount options of / UPPERDIR=$(findmnt --mountpoint / --noheadings --output FS-OPTIONS | sed -e "s/.*upperdir=\([^,]*\),*.*/\1/") if [ -z "$UPPERDIR" ] || ! [ -d "$UPPERDIR" ]; then echo "ERROR: can't determine upperdir" exit 2 fi if ! [ -d "${UPPERDIR}/var/lib/pacman/local/" ]; then echo "ERROR: no packages installed in the COW space" echo "(${UPPERDIR}/var/lib/pacman/local/ not existing)" exit 3 fi # read the names of the packages in the COW space # package names are in the desc file in the line after the marker %NAME% PACKAGENAMES=$(find ${UPPERDIR}/var/lib/pacman/local/ -name desc -exec grep -A1 --no-filename "%NAME%" \{\} \; | grep -v -E "(--|%NAME%)") if [ -z "$PACKAGENAMES" ]; then echo "ERROR: no packages installed in the COW space" echo "(${UPPERDIR}/var/lib/pacman/local/ empty)" exit 3 fi # create temp dir used to store the package list and later the files to put into the SRM mkdir -p $TMP_TARGET # read all filenames installed by the packages in COW space # sort to make sure dir names come before files in the dir pacman -Q --list --quiet $PACKAGENAMES | sort -u >$PKG_FILELIST if cat $PKG_FILELIST | wc -l | grep -q "^0$"; then echo "ERROR: empty file list for the installed packages" # clean up rm -rf ${TMP_TARGET} exit 3 fi echo -n "Found Packages: " echo "$PACKAGENAMES" | wc -w echo -n "Found Files: " cat $PKG_FILELIST | wc -l echo echo "Copying to temp dir..." # newline separator for for loops, necessary for filenames with spaces in them IFS_SAVE=$IFS IFS=$'\n' # iterate over all files and dirs installed by the packages for FILE in `cat $PKG_FILELIST`; do if [ -d "${FILE}" ] && ! [ -L "${FILE}" ]; then # we have a real dir (not a symlink to a dir) # create it below $TMP_TARGET, copy attributes mkdir -p "${TMP_TARGET}${FILE}" chmod "--reference=${FILE}" "${TMP_TARGET}${FILE}" chown "--reference=${FILE}" "${TMP_TARGET}${FILE}" else # we have a file or symlink, copy it, preserving symlinks as such cp "--target-directory=${TMP_TARGET}" --preserve=all --parents --no-dereference "${FILE}" fi done # copy the pacman package database too # first create the directories in the tmp space for DIR in `find ${UPPERDIR}/var/lib/pacman/local/ -type d -printf "%P\n"`; do mkdir -p "${TMP_TARGET}var/lib/pacman/local/${DIR}" done # then copy all package database files for FILE in `find ${UPPERDIR}/var/lib/pacman/local/ -type f -printf "%P\n"`; do cp --no-dereference "${UPPERDIR}/var/lib/pacman/local/${FILE}" "${TMP_TARGET}var/lib/pacman/local/${FILE}" done # the user may have enabled systemd units provided by the packages in COW space # we want to copy these too # read all systemd symlinks in the cow-dir for SYMLINK in `find "${UPPERDIR}/etc/systemd/system/" -type l -printf "%P\n"`; do TARGET=$(readlink "${UPPERDIR}/etc/systemd/system/${SYMLINK}") # targets the symlink something that is in our packages? if grep -q "${TARGET}" $PKG_FILELIST; then echo "Copying systemd link /etc/systemd/system/${SYMLINK}" # copy the symlink to our target dir LINKDIR=$(dirname "${TMP_TARGET}/etc/systemd/system/${SYMLINK}") mkdir -p "${LINKDIR}" cp --no-dereference "${UPPERDIR}/etc/systemd/system/${SYMLINK}" "${TMP_TARGET}/etc/systemd/system/${SYMLINK}" fi done # restore line separator IFS=$IFS_SAVE rm -f $PKG_FILELIST } function create() { echo echo "Creating squashfs..." mksquashfs ${TMP_TARGET} $TARGETFILE -info -comp "$COMPRESS" $COMPLEVEL # clean up rm -rf ${TMP_TARGET} } while getopts "s:c:l:" opt; do case "${opt}" in s) SUBCMD="${OPTARG}" ;; c) COMPRESS="${OPTARG}" ;; l) COMPLEVEL="-Xcompression-level ${OPTARG}" ;; *) usage ;; esac done case "${SUBCMD}" in prepare) OPER_PREPARE=1 ;; create) OPER_CREATE=1 ;; all) OPER_PREPARE=1 OPER_CREATE=1 ;; *) echo "ERROR: invalid sub-command: '${SUBCMD}'" usage ;; esac shift $((OPTIND-1)) TARGETFILE="$1" if [ "$OPER_CREATE" -eq 1 ] && [ -z "$TARGETFILE" ]; then echo "ERROR: no target file specified" usage fi if [ "$OPER_CREATE" -eq 1 ] && [ -e "$TARGETFILE" ]; then echo "ERROR: target file already exists. Please delete it." exit 1 fi if [ "$OPER_PREPARE" -eq 1 ] && [ -e "$TMP_TARGET" ]; then echo "ERROR: temporary directory $TMP_TARGET already exists. Please delete it." exit 1 fi if [ "$OPER_PREPARE" -eq 0 ] && [ ! -d "$TMP_TARGET" ]; then echo "ERROR: temporary directory $TMP_TARGET does not exist. Please make sure you run the 'prepare' sub-command before 'create'." exit 1 fi if [ "$OPER_PREPARE" -eq 1 ]; then prepare fi if [ "$OPER_CREATE" -eq 1 ]; then create fi