File functions.sh of Package minimal-initramfs
#!/bin/sh
export PATH=/sbin:/usr/sbin:/bin:/usr/bin:$PATH
einfo() { echo -ne "\033[1;30m>\033[0;36m>\033[1;36m> \033[0m${*}\n" ;}
ewarn() { echo -ne "\033[1;30m>\033[0;33m>\033[1;33m> \033[0m${*}\n" >&2;}
eerror() { echo -ne "\033[1;30m>\033[0;31m>\033[1;31m> ${*}\033[0m\n" >&2 ;}
die() { eerror "$*"; rescueshell; }
rescueshell() {
if [ "${console}" ]; then
console="${console%,*}"
fi
if [ "$rescueshell" != 'true' ]; then
# If we did not forced rescueshell by kernel opt, print additional message.
ewarn "Dropping to rescueshell because of above error."
fi
ewarn "Rescue Shell (busybox's /bin/sh)"
ewarn "To reboot, press 'control-alt-delete'."
ewarn "If you wish resume booting process, run 'resume-boot'."
if [ "$console" ] && [ -c "/dev/${console}" ]; then
setsid sh -c "exec sh --login </dev/"${console}" >/dev/${console} 2>&1"
elif command -v cttyhack 1>/dev/null 2>&1; then
setsid cttyhack sh --login
elif [ -c '/dev/tty1' ]; then
setsid sh -c 'exec sh --login </dev/tty1 >/dev/tty1 2>&1'
else
sh --login
fi
echo
:> /rescueshell.pid
}
was_shell() {
[ -f '/rescueshell.pid' ]
return $?
}
run() {
if "$@"; then
echo "Executed: '$@'" >> /init.log
else
eerror "'$@' failed."
echo "Failed: '$@'" >> /init.log
echo "$@" >> /.ash_history
rescueshell
fi
}
run_hooks() {
# Support for optional code injection via hooks placed into
# multiple initramfs images.
if [ -d "/hooks/$1" ]; then
for i in /hooks/$1/*; do
[ "$i" = "/hooks/$1/*" ] && break
einfo "Running '$i' hook ..."
[ -x "$i" ] && . "$i"
done
fi
}
populate_dev_disk_by_label_and_uuid() {
# Create /dev/disk/by-{uuid,label} symlinks.
# We could run it with mdev as a trigger on event,
# but binit uses devtmpfs by default.
# It is possible that later whatever manages /dev
# will not probe block device and create symlinks,
# so we should do it before we move /dev to /newroot/dev
# Fix for an issue reported under Gentoo's bug #559026.
local block_device blkid_output LABEL UUID TYPE
local vars
einfo "Populating /dev/disk/by-{uuid,label} ..."
dodir /dev/disk /dev/disk/by-uuid /dev/disk/by-label
for block_device in /sys/class/block/*; do
unset blkid_output LABEL UUID TYPE
block_device="${block_device##*/}"
blkid_output="$(blkid "/dev/${block_device}")"
[ "${blkid_output}" ] || continue
vars="${blkid_output#*:}"
eval "${vars}"
[ "${LABEL}" ] && ! [ -e "/dev/disk/by-label/${LABEL}" ] && run ln -s "../../${block_device}" "/dev/disk/by-label/${LABEL}"
[ "${UUID}" ] && ! [ -e "/dev/disk/by-uuid/${UUID}" ] && run ln -s "../../${block_device}" "/dev/disk/by-uuid/${UUID}"
done
}
resolve_device() {
# This function will check if variable at $1 contain LABEL or UUID and then, if LABEL/UUID is valid.
eval "device=\"\$$1\""
case "${device}" in
LABEL\=*|UUID\=*)
eval "$1=\"$(findfs $device)\""
if eval "[ -z \"\$$1\" ]"; then
eerror "Wrong UUID or LABEL."
rescueshell
fi
;;
PARTUUID\=*)
eval "$1=\"$(blkid -t $device -o device)\""
if eval "[ -z \"\$$1\" ]"; then
eerror "Wrong UUID or LABEL."
rescueshell
fi
;;
esac
}
process_commandline_options() {
for i in $(cat /proc/cmdline); do
case "${i}" in
initramfsdebug)
set -x
;;
ro|rw)
root_rw_ro=$i
;;
*=*)
# Catch-all for foo=bar.
# And ignore foo.bar=1 etc.
case "${i%%=*}" in
*'.'*)
true
;;
*)
export "${i%%=*}=${i#*=}"
;;
esac
;;
*.*)
# ignore foo.bar
true
;;
*)
# Everything that is not foo=bar should be just exported as 'true'
export "${i}=true"
:;
esac
done
}
use() {
eval "name=\"\$$1\""
# Check if $name isn't empty and if $name isn't set to false or zero.
if [ -n "${name}" ] && [ "${name}" != 'false' ] && [ "${name}" != '0' ]; then
if [ -n "$2" ]; then
$2
else
return 0
fi
else
return 1
fi
}
musthave() {
while [ -n "$1" ]; do
# We can handle it by use() function, yay!
if ! use "$1"; then
eerror "The \"$1\" variable is empty, set to false or zero but shoudn't be."
local missing_variable='true'
fi
shift
done
if [ "${missing_variable}" = 'true' ]; then
rescueshell
fi
}
dodir() {
for dir in "$@"; do
run mkdir -m 700 -p "${dir}"
done
}
loadkeymap() {
if [ -f /keymap ]; then
loadkmap < /keymap
fi
}
get_majorminor() {
local device="$1"
musthave device
local major_hex="$(stat -L -c '%t' "${device}")"
local minor_hex="$(stat -L -c '%T' "${device}")"
musthave major_hex minor_hex
if [ -n "${major_hex}" ] && [ -n "${minor_hex}" ]; then
printf '%u:%u\n' "0x${major_hex}" "0x${minor_hex}"
fi
}
loadmodules() {
einfo "Loading device modules"
FILE="/usr/share/mobile-helpers/devicemodules.conf"
[ -f "$FILE" ] && while read -r module; do
[ -n "$module" ] && modprobe "$module"
done < "$FILE"
einfo "Starting udevd daemon"
/usr/lib/systemd/systemd-udevd &> /dev/null &
#udevadm control --reload
udevadm trigger &> /dev/null
}
CreateLUKS() {
einfo "First time boot"
einfo "Configuring Full Disk Encryption"
SHRINK_MB=64
MAPPER_NAME="enc_root"
UNL0KR_INSTALL_MESSAGE="\
INSTALLATION \
=========================================================\
FDE CONFIGURATION \
=========================================================\
Set you new device password \
"
local IFS=":"
for enc_dev in ${mobileroot}; do
IFS="${default_ifs}"
resolve_device enc_dev
if [ ! -b "$enc_dev" ]; then
eerror "Error: Device '$enc_dev' is not a valid block device."
exit 1
rescueshell
fi
# Check that the device is NOT mounted
if grep -qs "$enc_dev" /proc/mounts; then
eerror "Error: El dispositivo '$enc_dev' está montado."
eerror "Debes desmontarlo primero (ej: sudo umount $enc_dev)"
rescueshell
fi
einfo "(STEP 1/5) Checking FS in ${enc_dev}"
e2fsck -f -y "$enc_dev"
einfo "(STEP 2/5) Shrinking the file system to make space"
# Calculate the space to be reduced
SHRINK_BYTES=$((SHRINK_MB * 1024 * 1024))
BLOCK_SIZE=$(dumpe2fs -h "$enc_dev" 2>/dev/null | grep 'Block size:' | busybox-static awk '{print $3}')
CURRENT_BLOCKS=$(dumpe2fs -h "$enc_dev" 2>/dev/null | grep 'Block count:' | busybox-static awk '{print $3}')
BLOCKS_TO_REMOVE=$((SHRINK_BYTES / BLOCK_SIZE))
NEW_SIZE_BLOCKS=$((CURRENT_BLOCKS - BLOCKS_TO_REMOVE))
# Shrink the file system
resize2fs -p "$enc_dev" $NEW_SIZE_BLOCKS
einfo "(STEP 3/5) Encrypting the partition (take a coffee)"
pass=$(unl0kr -m "$UNL0KR_INSTALL_MESSAGE" 2>/dev/null)
busybox-static printf "%s" "$pass" | cryptsetup reencrypt --new --type luks2 --reduce-device-size ${SHRINK_MB}M --key-file=- --label=ROOT "$enc_dev"
einfo "(STEP 4/5) Open LUKS container"
busybox-static printf "%s" "$pass" | cryptsetup luksOpen --key-file=- "$enc_dev" $MAPPER_NAME
einfo "(STEP 5/5) Expanding filesystem to fill container"
resize2fs /dev/mapper/$MAPPER_NAME
cryptsetup close $MAPPER_NAME
done
}
InitializeLUKS() {
# Verify that the unl0kr binary exists
if ! command -v unl0kr 1>/dev/null 2>&1; then
eerror "El binario unl0kr no se encuentra en la imagen initramfs."
rescueshell
fi
# Verify that the cryptsetup binary exists
if ! command -v cryptsetup 1>/dev/null 2>&1; then
eerror "El binario cryptsetup no se encuentra en la imagen initramfs. Es necesario para abrir el dispositivo LUKS."
rescueshell
fi
einfo "Starting FDE"
local enc_num='1'
local dev_name="enc_root"
local IFS=":"
UNL0KR_MESSAGE="=========================================================\
UNLOCK DEVICE \
========================================================= \
"
for enc_dev in ${mobileroot}; do
IFS="${default_ifs}"
if ! [ "${enc_num}" = '1' ]; then
dev_name="enc_root${enc_num}"
fi
resolve_device enc_dev
einfo "Checking LUKS encrypt on ${enc_dev}"
if cryptsetup isLuks "${enc_dev}"; then
einfo "FDE Device Detected"
else
CreateLUKS
fi
einfo "Unlocking '${enc_dev##*/}' and mapping to '/dev/mapper/${dev_name}'..."
local retries=5
local count=1
local unlocked=false
# Loop to retry unlocking up to 5 times
while [ $count -le $retries ]; do
# Try unlocking the device.
pass=$(unl0kr -m "$UNL0KR_MESSAGE" 2>/dev/null)
if busybox-static printf "%s" "$pass" | cryptsetup luksOpen --key-file=- "${enc_dev}" "${dev_name}"; then
unlocked=true
break
else
ewarn "Fallo al desbloquear. Intento $count de $retries."
count=$((count+1))
# Wait 2 seconds before the next attempt, if it is not the last one
[ $count -le $retries ] && sleep 2
fi
done
# If all attempts fail, enter the rescue shell.
if [ "$unlocked" = "false" ]; then
eerror "Failed to unlock ${enc_dev} after $retries attempts."
rescueshell
fi
enc_num="$((enc_num+1))"
done
}
InitializeLVM() {
einfo "Scaning all disks for volume groups."
# We have to ensure that cache does not exist so vgchange will run 'vgscan' itself.
if [ -d '/etc/lvm/cache' ]; then run rm -rf '/etc/lvm/cache'; fi
run lvm vgchange -a y
}
rereadpt() {
# Check for partition table on all block devices.
for i in /dev/*; do
if [ -b "${i}" ]; then
blockdev --rereadpt "${i}" >/dev/null 2>&1
fi
done
}
register_bcache_devices() {
# Push all the block devices to register_quiet
# If its bcache, it will bring it up, if not, it will simply ignore it.
if ! [ -e /sys/fs/bcache/register_quiet ]; then
ewarn "There's no bcache interface. Missing kernel driver?"
return 0
fi
for i in $(awk '$4 !~ /^(name$|$)/ { print $4 }' /proc/partitions); do
if [ -e "/dev/${i}" ]; then
echo "/dev/${i}" >/sys/fs/bcache/register_quiet
else
echo "Looks like there's no '/dev/${i}', but should be."
fi
done
}
cleanup() {
was_shell && rm /rescueshell.pid
}
boot_newroot() {
init="${init:-/sbin/init}"
einfo "Switching root to /newroot and executing ${init}."
if ! [ -x "/newroot/${init}" ]; then die "There is no executable '/newroot/${init}'."; fi
exec env -i \
TERM="${TERM:-linux}" \
PATH="${PATH:-/bin:/sbin:/usr/bin:/usr/sbin}" \
switch_root /newroot "${init}"
}
emount() {
# All mounts into one place is good idea.
local mountparams
while [ "$#" -gt 0 ]; do
case $1 in
'/newroot')
if busybox-static mountpoint -q '/newroot'; then
einfo "/newroot already mounted, skipping..."
else
einfo "Mounting /newroot..."
musthave root
if [ -n "${rootfstype}" ]; then
mountparams="${mountparams} -t ${rootfstype}"
fi
resolve_device root
if [ ! -b "/dev/mapper/enc_root" ]; then
einfo "Mounting ${mobileroot}"
run mount -o "${rootflags:+${rootflags},}${root_rw_ro:-ro}" ${mountparams} "${mobileroot}" '/newroot'
else
einfo "Mounting FDE"
run mount -o "${rootflags:+${rootflags},}${root_rw_ro:-ro}" ${mountparams} "/dev/mapper/enc_root" '/newroot'
fi
fi
;;
'/newroot/usr')
if [ -f '/newroot/etc/fstab' ]; then
while read device mountpoint fstype fsflags _; do
if [ "${mountpoint}" = '/usr' ]; then
if [ -d '/newroot/usr' ]; then
einfo "Mounting /newroot/usr..."
run mount -o "${fsflags},${root_rw_ro:-ro}" -t "${fstype}" "${device}" '/newroot/usr'
else
die "/usr in fstab present but no mountpoint /newroot/usr found."
fi
break
fi
done < '/newroot/etc/fstab'
else
ewarn "No /newroot/etc/fstab present."
ewarn "Early mouting of /usr will not be done."
fi
;;
'/dev')
local devmountopts='nosuid,relatime,size=10240k,mode=755'
if grep -q 'devtmpfs' '/proc/filesystems' && ! use mdev; then
einfo "Mounting /dev (devtmpfs)..."
run mount -t devtmpfs -o ${devmountopts} devtmpfs /dev
else
einfo "Mounting /dev (mdev over tmpfs)..."
run mount -t tmpfs -o ${devmountopts} dev /dev
run touch /etc/mdev.conf
run echo /sbin/mdev > /proc/sys/kernel/hotplug
run mdev -s
# Looks like mdev create /dev/pktcdvd as a file when both udev and devtmpfs do it as a dir.
# We will do it 'better' to avoid non-fatal-error while starting udev after switching to /newroot.
# TODO: Can mdev.conf handle it?
if [ -c '/dev/pktcdvd' ]; then
run rm '/dev/pktcdvd'
run mkdir '/dev/pktcdvd'
run mknod '/dev/pktcdvd/control' c 10 61
fi
fi
;;
'/proc')
einfo "Mounting /proc..."
run mount -t proc proc /proc
;;
'/sys')
einfo "Mounting /sys..."
run mount -t sysfs sysfs /sys
;;
*)
eerror "emount() does not understand \"$1\""
;;
esac
shift
done
}
eumount() {
while [ "$#" -gt 0 ]; do
case "$1" in
*)
einfo "Unmounting ${1}..."
run umount "$1"
;;
esac
shift
done
}
moveDev() {
einfo "Moving /dev to /newroot/dev..."
if mountpoint -q /dev/pts; then umount -l /dev/pts; fi
if use mdev; then run echo '' > /proc/sys/kernel/hotplug; fi
run mount --move /dev /newroot/dev
}
rootdelay() {
if [ "${rootdelay}" -gt 0 2>/dev/null ]; then
einfo "Waiting ${rootdelay}s (rootdelay)"
run sleep ${rootdelay}
else
ewarn "\$rootdelay variable must be numeric and greater than zero. Skipping rootdelay."
fi
}
# vim: noexpandtab