#!/bin/bash
CWD=`pwd`
program=`basename $0`
sbindir=`cd $(dirname ${BASH_SOURCE[0]}) >/dev/null 2>&1 && pwd`
# 13 = permission denied (should be root)
# 92 = Cannot create '/tmp/...' directory
EXITSTATUS=0
################################################################
#
# TMPDIR & signal handlers:
#
if [ -z "${DISTRO_NAME}" ] ; then
DISTRO_NAME='radix'
fi
umask 022
if [ ! -z "${TMPDIR}" ] ; then
export TMPDIR
elif [ ! -d "${TMPDIR}" -o ! -w "${TMPDIR}" ] ; then
if [ -d "${HOME}/tmp" -a -w "${HOME}/tmp" ] ; then
export TMPDIR="${HOME}/tmp"
elif [ -d "/tmp" -a -w "/tmp" ] ; then
export TMPDIR="/tmp"
fi
fi
TMP=$(mkdir -p ${TMPDIR}/${DISTRO_NAME} && mktemp -d -p ${TMPDIR}/${DISTRO_NAME} $program.XXXXXXXX) || { echo "Cannot create '/tmp/...' directory" ; exit 92; }
MNT=/mnt
if [ ! -f /etc/system-installer ] ; then
if [ ! -z "${TMPDIR}" ] ; then
mkdir -p ${TMPDIR}/${DISTRO_NAME}/mnt
MNT=${TMPDIR}/${DISTRO_NAME}/mnt
else
mkdir -p /tmp/${DISTRO_NAME}/mnt
MNT=/tmp/${DISTRO_NAME}/mnt
fi
fi
trap 'for dir in "`find ${MNT} -type d -mindepth 1 -maxdepth 1`" ; do \
umount ${dir} 2> /dev/null ; \
done ; \
rm -rf ${TMP} ; \
clear ; \
head -c 100 /dev/zero | tr "\0" "\n" ; \
echo "ERROR: $program - has been terminated." ; \
echo "" ; \
exit 1' INT TERM
#
# Normal exit:
#
trap 'for dir in "`find ${MNT} -type d -mindepth 1 -maxdepth 1`" ; do \
umount ${dir} 2> /dev/null ; \
done ; \
rm -rf ${TMP} ; \
exit 0' EXIT
#
################################################################
DIALOG=
export DIALOGRC=${CWD}/.dialogrc
DDOUTPUT=${TMP}/dd-output.$$
VERBOSE=no
FDISK=`PATH=/sbin:/usr/sbin:$PATH which fdisk`
if [ "`basename ${FDISK}`" != "fdisk" ] ; then
echo "ERROR: fdisk utility is not found in the system"
exit 1
fi
PARTPROBE=`PATH=/sbin:/usr/sbin:$PATH which partprobe`
if [ "`basename ${PARTPROBE}`" != "partprobe" ] ; then
PARTPROBE=
fi
usage() {
cat <<EOF
Usage: ${program} [options] <DEVICE>
options:
-e, --efifs-image <efi32fs-image> - EFI filesystem image;
-r, --rootfs-image <ext4fs-image> - ROOT filesystem image;
-s, --spase <SIZE> - SIZE reserved for other partitions,
for example, home file system.
Default SIZE=1G;
-v, --verbose - Enable verbose output;
-d, --dialog - Enable DIALOG to show progress;
-h, --help - Display this information.
DEVICE can be a regular file or block device such as /dev/sda, /dev/loop0, etc...
EOF
}
check_current_user() {
msg=$1
if [ "$EUID" != "0" ] ; then
if [ "x${msg}" != "x" ] ; then
echo "ERROR: ${msg}"
else
echo "ERROR: ${program} - must be run by superuser"
fi
EXITSTATUS=13
exit $EXITSTATUS
fi
}
################################################################
#
# show_progress() - print progress without DIALOG.
#
# arguments:
# ---------
# $1 - input stream in following format:
#
# XXX
# $PCT
# \n\
# $line
# XXX
#
# where $PCT - persentage [0 ... 100],
# $line - message printed before progress line.
#
show_progress() {
local printed=no
GAUGE_LENGHT=68
local PCT=
while read line ; do
local message=
local lines=0
local lenght=0
local ctx=0
local i=0
if [ "${line}" = "XXX" ] ; then
continue
fi
if [[ "${line}" =~ ^[0-9]*$ ]] ; then
PCT=${line}
continue
fi
while [ "${line:0:1}" = "n" ] ; do
line=${line:1}
message="${message}\n"
let 'lines += 1'
done
message="${message}${line}"
let 'lines += 1'
let 'lenght = 3 + GAUGE_LENGHT + 6'
let 'lines += 3'
if [ "${printed}" = "yes" ] ; then
while [ ${i} -lt ${lines} ] ; do
# Return back and cleaning line by line:
printf "\r\033[K\033[1A"
let 'i +=1'
done
i=0
fi
echo -e "${message}"
printf "\n ["
let 'ctx = GAUGE_LENGHT * PCT / 100'
while [ $ctx -gt 0 ] ; do
printf "\u2588"
let 'ctx -= 1'
let 'i += 1'
done
while [ $i -lt ${GAUGE_LENGHT} ] ; do
printf " "
let 'i += 1'
done
printf "] %3d%%\n\n" $PCT
printed=yes
done < "${1:-/dev/stdin}"
}
################################################################
#
# waitdd() - waiting for DD and display progress.
#
# arguments:
# ---------
# $1 - PID
# $2 - DD output file
# $3 - Size of input stream
# $4 - Dialog title
#
waitdd() {
local pid=$1
local ddout=$2
local isize=$3
local title=$4
local PCT=0
( while [ "x`ps -eo pid | grep $pid`" != "x" ] ; do
kill -USR1 "$pid" 2>/dev/null
line=`tail -n1 $ddout | grep "bytes"`
bytes=`echo "$line" | cut -f 1 -d ' '`
if [ "x$bytes" != "x" ] ; then
let "PCT = bytes * 100 / isize"
fi
cat << EOF
XXX
$PCT
\n\
$line
XXX
EOF
usleep 300
done
if [ "x`ps -eo pid | grep $pid`" = "x" ] ; then
cat << EOF
XXX
100
\n\
Written $isize bytes
XXX
EOF
fi
) | if [ "x${DIALOG}" = "x" ] ; then
show_progress
else
${DIALOG} --colors \
--backtitle "\Z7Radix\Zn \Z1cross\Zn\Z7 Linux\Zn" \
--title " \Z0${title}\Zn " --gauge "\n" 8 74 0
fi
}
################################################################
# Disk Geometry:
#
bytes=
sectors=
unit_size=
log_sector_size=
phy_sector_size=
alignment=4096
format="512N"
#
# Format | logical sector size | physical sector size
# --------+---------------------+----------------------
# 512N | 512 | 512
# --------+---------------------+----------------------
# 512E | 512 | 4096
# --------+---------------------+----------------------
# 4096N | 4096 | 4096
# --------+---------------------+----------------------
#
disk_size_in_bytes() {
disk=$1
echo "`LANG=en_US.UTF-8 ${FDISK} -l $disk`" | while read -r line ; do
found=`echo "$line" | grep "^Disk $disk"`
if [ "$found" != "" ] ; then
bytes=`echo $found | sed 's,.* \([0-9]*\) bytes.*,\1,'`
echo "$bytes"
break
fi
done
}
disk_size_in_sectors() {
disk=$1
echo "`LANG=en_US.UTF-8 ${FDISK} -l $disk`" | while read -r line ; do
found=`echo "$line" | grep "^Disk $disk"`
if [ "$found" != "" ] ; then
sectors=`echo $found | sed 's,.* \([0-9]*\) sectors$,\1,'`
echo "$sectors"
break
fi
done
}
disk_unit_size() {
disk=$1
echo "`LANG=en_US.UTF-8 ${FDISK} -l $disk`" | while read -r line ; do
found=`echo "$line" | grep "^Unit"`
if [ "$found" != "" ] ; then
unit_size=`echo $found | sed 's,.*= \([0-9]*\) bytes$,\1,'`
echo "$unit_size"
break
fi
done
}
disk_log_sector_size() {
disk=$1
echo "`LANG=en_US.UTF-8 ${FDISK} -l $disk`" | while read -r line ; do
found=`echo "$line" | grep '^Sector size'`
if [ "$found" != "" ] ; then
log_sector_size=`echo $found | sed 's,.* \([0-9]*\) bytes / \([0-9]*\) bytes$,\1,'`
echo "$log_sector_size"
break
fi
done
}
disk_phy_sector_size() {
disk=$1
echo "`LANG=en_US.UTF-8 ${FDISK} -l $disk`" | while read -r line ; do
found=`echo "$line" | grep '^Sector size'`
if [ "$found" != "" ] ; then
phy_sector_size=`echo $found | sed 's,.* \([0-9]*\) bytes / \([0-9]*\) bytes$,\2,'`
echo "$phy_sector_size"
break
fi
done
}
disk_geometry() {
disk=$1
bytes=`disk_size_in_bytes $disk`
sectors=`disk_size_in_sectors $disk`
unit_size=`disk_unit_size $disk`
log_sector_size=`disk_log_sector_size $disk`
phy_sector_size=`disk_phy_sector_size $disk`
if [ $unit_size -eq $alignment ] ; then
# Copy of GPT at end of disk:
let "sectors = sectors - 5"
else
# Copy of GPT at end of disk:
let "sectors = sectors - 33"
fi
if [ $phy_sector_size -eq $alignment ] ; then
if [ $log_sector_size -lt $phy_sector_size ] ; then
format="512E"
else
format="4096N"
fi
else
format="512N"
fi
}
#
# End of Disk Geometry:
################################################################
DEVICE=
EFI32FS_IMAGE=
EXT4FS_IMAGE=
efi32fs_size_in_bytes=
ext4fs_size_in_bytes=
#
# Size reserved for home file system:
#
space_size="1G"
space_size_in_bytes=
disk_size_in_bytes=
#
# Parse options:
#
while [ $# -ne 0 ] ; do
if [ "$1" = "-h" -o "$1" = "--help" ] ; then
usage
exit 0
elif [ "$1" = "-v" -o "$1" = "--verbose" ] ; then
VERBOSE=yes
shift 1
elif [ "$1" = "-d" -o "$1" = "--dialog" ] ; then
DIALOG=dialog
shift 1
elif [ "$1" = "-e" -o "$1" = "--efifs-image" ] ; then
if [ "x$2" = "x" ] ; then
echo "ERROR: EFI filesystem image is not specified"
exit 1
fi
EFI32FS_IMAGE="$2"
shift 2
elif [ "$1" = "-r" -o "$1" = "--rootfs-image" ] ; then
if [ "x$2" = "x" ] ; then
echo "ERROR: ROOT filesystem image is not specified"
exit 1
fi
EXT4FS_IMAGE="$2"
shift 2
elif [ "$1" = "-s" -o "$1" = "--space" ] ; then
if [ "$2" = "" ] ; then
echo "ERROR: Space size for additional partitions is not specified"
usage
exit 1
fi
space_size="$2"
if [ `echo "${space_size}" | grep '[Mm]'` ] ; then
size=`echo "${space_size}" | sed 's/^[ \t]*//;s/[ \t]*$//;s/[a-zA-Z]*//g'`
size=`echo "scale=1; $size * 1048576" | bc`
prec=`echo "${size}" | cut -f2 -d'.'`
size=`echo "${size}" | cut -f1 -d'.'`
if [ $prec -gt 5 ] ; then
let "size = size + 1"
fi
space_size_in_bytes=$size
elif [ `echo "${space_size}" | grep '[Gg]'` ] ; then
size=`echo "${space_size}" | sed 's/^[ \t]*//;s/[ \t]*$//;s/[a-zA-Z]*//g'`
size=`echo "scale=1; $size * 1073741824" | bc`
prec=`echo "${size}" | cut -f2 -d'.'`
size=`echo "${size}" | cut -f1 -d'.'`
if [ $prec -gt 5 ] ; then
let "size = size + 1"
fi
space_size_in_bytes=$size
else
space_size_in_bytes=`echo "${space_size}" | sed 's/^[ \t]*//;s/[ \t]*$//;s/[a-zA-Z]*//g'`
fi
if [ `echo "${space_size_in_bytes}" | grep "[^0-9]"` ] ; then
echo "ERROR: Wrong space size for additional partitions"
usage
exit 1
fi
shift 2
else
if [ $# -lt 1 ] ; then
echo "ERROR: Target DEVICE is not specified"
exit 1
fi
DEVICE="$1"
break
fi
done
if [ "x${DEVICE}" = "x" ] ; then
echo "ERROR: Target DEVICE is not specified"
exit 1
fi
if [ "x${EFI32FS_IMAGE}" = "x" ] ; then
echo "ERROR: EFI filesystem image is not specified"
exit 1
fi
if [ "x${EXT4FS_IMAGE}" = "x" ] ; then
echo "ERROR: ROOT filesystem image is not specified"
exit 1
fi
#
# Calculate space size in bytes if not specified in command line:
#
if [ "x${space_size_in_bytes}" = "x" ] ; then
if [ `echo "${space_size}" | grep '[Mm]'` ] ; then
size=`echo "${space_size}" | sed 's/^[ \t]*//;s/[ \t]*$//;s/[a-zA-Z]*//g'`
size=`echo "scale=1; $size * 1048576" | bc`
prec=`echo "${size}" | cut -f2 -d'.'`
size=`echo "${size}" | cut -f1 -d'.'`
if [ $prec -gt 5 ] ; then
let "size = size + 1"
fi
space_size_in_bytes=$size
elif [ `echo "${space_size}" | grep '[Gg]'` ] ; then
size=`echo "${space_size}" | sed 's/^[ \t]*//;s/[ \t]*$//;s/[a-zA-Z]*//g'`
size=`echo "scale=1; $size * 1073741824" | bc`
prec=`echo "${size}" | cut -f2 -d'.'`
size=`echo "${size}" | cut -f1 -d'.'`
if [ $prec -gt 5 ] ; then
let "size = size + 1"
fi
space_size_in_bytes=$size
else
space_size_in_bytes=`echo "${space_size}" | sed 's/^[ \t]*//;s/[ \t]*$//;s/[a-zA-Z]*//g'`
fi
fi
efi32fs_size_in_bytes=$(stat -c%s "${EFI32FS_IMAGE}")
ext4fs_size_in_bytes=$(stat -c%s "${EXT4FS_IMAGE}")
#
# a multiple of 4096:
#
let "size = efi32fs_size_in_bytes / alignment * alignment"
if [ ${size} -ne ${efi32fs_size_in_bytes} ] ; then
echo "ERROR: EFI filesystem image size must be a multiple of 4096"
exit 1
fi
let "size = ext4fs_size_in_bytes / alignment * alignment"
if [ ${size} -ne ${ext4fs_size_in_bytes} ] ; then
echo "ERROR: EFI filesystem image size must be a multiple of 4096"
exit 1
fi
#
# Calculate minimal size needed for input images:
#
disk_size_in_bytes=$(echo "${efi32fs_size_in_bytes} + ${ext4fs_size_in_bytes} + ${space_size_in_bytes}" | bc)
disk_size_in_bytes=$(echo "${disk_size_in_bytes} + 512*2048 + 512*33" | bc)
let "disk_size_in_bytes = disk_size_in_bytes / 512 * 512"
if [ -b "${DEVICE}" ] ; then
check_current_user "${program} should be run by superuser: '${DEVICE}' is not a regular file"
elif [ ! -f "${DEVICE}" ] ; then
if [ "x${DIALOG}" = "x" ] ; then
echo -e "\nCreating an empty disk image..."
fi
cnt=$(echo "${disk_size_in_bytes} / 512" | bc)
rm -f ${DDOUTPUT}
LANG=en_US.UTF-8 dd if=/dev/zero of=${DEVICE} of=disk bs=512 count=${cnt} >> ${DDOUTPUT} 2>&1 & \
waitdd $! ${DDOUTPUT} ${disk_size_in_bytes} "Create empty disk image"
rm -f ${DDOUTPUT}
fi
efi32fs_start_sector=2048
efi32fs_end_sector=
ext4fs_start_sector=
ext4fs_end_sector=
space_start_sector=
space_end_sector=
#
# https://uefi.org/specifications ->
# -> https://uefi.org/sites/default/files/resources/UEFI_Spec_2_9_2021_03_18.pdf
#
# If the block size is 512, the First Usable LBA must be greater than or equal to 34
# (allowing 1 block for the Protective MBR, 1 block for the Partition Table Header, and
# 32 blocks for the GPT Partition Entry Array); if the logical block size is 4096, the
# First Useable LBA must be greater than or equal to 6 (allowing 1 block for the Protective
# MBR, 1 block for the GPT Header, and 4 blocks for the GPT Partition Entry Array).
#
# Backup Partition Table requires 33 blocks in case block size is 512 bytes and 5 blocks
# in case block size is 4096 bytes (Protective MBR has no copy at end of disk).
#
#
# Get size of disk:
#
disk_geometry ${DEVICE}
if [ "${VERBOSE}" = "yes" ] ; then
if [ "x${DIALOG}" != "x" ] ; then
clear
fi
echo ""
echo "Disk Geometry:"
echo "-------------"
echo " bytes = $bytes"
if [ $unit_size -eq $alignment ] ; then
echo " sectors = $sectors + 5"
else
echo " sectors = $sectors + 33"
fi
echo " unit_size = $unit_size"
echo " log_sector_size = $log_sector_size"
echo " phy_sector_size = $phy_sector_size"
echo " alignment = $alignment"
echo " format = $format"
echo ""
fi
if [ ${disk_size_in_bytes} -gt ${bytes} ] ; then
echo "ERROR: Not enough space on '${DEVICE}' device"
exit 1
fi
if [ $unit_size -eq $alignment ] ; then
efi32fs_start_sector=256
fi
let "efi32fs_size_in_sectors = efi32fs_size_in_bytes / unit_size"
let "ext4fs_size_in_sectors = ext4fs_size_in_bytes / unit_size"
#
# Calculation of the initial and final sectors of partitions:
#
let "efi32fs_end_sector = efi32fs_start_sector + ( efi32fs_size_in_bytes / unit_size - 1 )"
let "ext4fs_start_sector = efi32fs_end_sector + 1"
let "ext4fs_end_sector = ext4fs_start_sector + ( ext4fs_size_in_bytes / unit_size - 1 )"
left=
let "left = sectors - ext4fs_end_sector"
need=
let "need = space_size_in_bytes / unit_size"
if [ $left -lt $need ] ; then
echo "WARNING: Not enough space for additional partitions"
fi
if [ ${need} -gt 0 ] ; then
let "space_start_sector = ext4fs_end_sector + 1"
let "space_end_sector = sectors - 1"
else
space_start_sector=-1
space_end_sector=-1
fi
if [ "${VERBOSE}" = "yes" ] ; then
echo ""
echo "Disk Partioning:"
echo "---------------"
echo " efi32fs_size_in_bytes = $efi32fs_size_in_bytes"
echo " ext4fs_size_in_bytes = $ext4fs_size_in_bytes"
echo " space_size_in_bytes = $space_size_in_bytes"
echo ""
echo " efi32fs_size_in_sectors = $efi32fs_size_in_sectors"
echo " efi32fs_start_sector = $efi32fs_start_sector"
echo " efi32fs_end_sector = $efi32fs_end_sector"
echo ""
echo " ext4fs_size_in_sectors = $ext4fs_size_in_sectors"
echo " ext4fs_start_sector = $ext4fs_start_sector"
echo " ext4fs_end_sector = $ext4fs_end_sector"
echo ""
echo " space_start_sector = $space_start_sector"
echo " space_end_sector = $space_end_sector"
echo ""
fi
UEFI_TYPE=C12A7328-F81F-11D2-BA4B-00A0C93EC93B
UEFI_UUID=eaf0eef1-f13a-7565-6669-203aefe0f0f2
UEFI_NAME=uefi
ROOT_TYPE=0FC63DAF-8483-4772-8E79-3D69D8477DE4
ROOT_UUID=eaf0eef1-f13a-726F-6F74-203aefe0f0f2
ROOT_NAME=root
clean_disk() {
disk=$1
LANG=en_US.UTF-8 ${FDISK} --wipe=always --wipe-partition=always $disk 2>/dev/null 1>/dev/null <<EOF
g
w
EOF
}
#
# NOTE: номер первого (и единственного раздела не запрашивается):
#
create_efi_pattition() {
disk=$1
LANG=en_US.UTF-8 ${FDISK} --wipe=always --wipe-partition=always $disk 2>/dev/null 1>/dev/null <<EOF
n
1
${efi32fs_start_sector}
${efi32fs_end_sector}
t
${UEFI_TYPE}
x
n
${UEFI_NAME}
u
${UEFI_UUID}
r
w
EOF
}
create_root_pattition() {
disk=$1
LANG=en_US.UTF-8 ${FDISK} --wipe=always --wipe-partition=always $disk 2>/dev/null 1>/dev/null <<EOF
n
2
${ext4fs_start_sector}
${ext4fs_end_sector}
t
2
${ROOT_TYPE}
x
n
2
${ROOT_NAME}
u
2
${ROOT_UUID}
r
w
EOF
}
#
# Move the shell prompt to last line of Terminal:
#
if [ "x${DIALOG}" != "x" ] ; then
echo -en "\033[$(tput lines)B"
fi
if [ "x${DIALOG}" = "x" ] ; then
echo -e "\nDisk partitioning..."
else
${DIALOG} --colors \
--backtitle "\Z7Radix\Zn \Z1cross\Zn\Z7 Linux\Zn" \
--title " \Z0Disk partitioning...\Zn " \
--infobox "\n Please wait for partitions have been created.\n" 5 74
fi
clean_disk ${DEVICE}
if [ "x${PARTPROBE}" != "x" ] ; then
${PARTPROBE} ${DEVICE} 1>/dev/null 2>/dev/null
fi
create_efi_pattition ${DEVICE}
if [ "x${PARTPROBE}" != "x" ] ; then
${PARTPROBE} ${DEVICE} 1>/dev/null 2>/dev/null
fi
create_root_pattition ${DEVICE}
if [ "x${PARTPROBE}" != "x" ] ; then
${PARTPROBE} ${DEVICE} 1>/dev/null 2>/dev/null
fi
#
# Write EFI boot image:
#
if [ "x${DIALOG}" = "x" ] ; then
echo -e "\nRecording the '`basename ${EFI32FS_IMAGE}`' image..."
fi
rm -f ${DDOUTPUT}
LANG=en_US.UTF-8 \
dd if=${EFI32FS_IMAGE} of=${DEVICE} \
bs=${unit_size} count=${efi32fs_size_in_sectors} \
seek=${efi32fs_start_sector} conv=notrunc >> ${DDOUTPUT} 2>&1 & \
waitdd $! ${DDOUTPUT} ${efi32fs_size_in_bytes} "EFI boot image recording..."
rm -f ${DDOUTPUT}
sleep 1
#
# Write EXT4 rootfs image:
#
if [ "x${DIALOG}" = "x" ] ; then
echo -e "\nRecording the '`basename ${EXT4FS_IMAGE}`' image..."
fi
rm -f ${DDOUTPUT}
LANG=en_US.UTF-8 \
dd if=${EXT4FS_IMAGE} of=${DEVICE} \
bs=${unit_size} count=${ext4fs_size_in_sectors} \
seek=${ext4fs_start_sector} conv=notrunc >> ${DDOUTPUT} 2>&1 & \
waitdd $! ${DDOUTPUT} ${ext4fs_size_in_bytes} "ROOT filesystem image recording..."
rm -f ${DDOUTPUT}
sleep 1
if [ "x${DIALOG}" = "x" ] ; then
echo -e "\nEFI boot disk is written.\n"
else
${DIALOG} --colors \
--backtitle "\Z7Radix\Zn \Z1cross\Zn\Z7 Linux\Zn" \
--title " \Z0EFI Boot Disk is Complete\Zn " \
--infobox "\n Boot disk is written.\n" 5 74
fi