defualt DNS for prebuilt rootfs is 1.0.0.1 when this DNS is not accessable, eg. due to proxy. network operation in chroot will not available. use alternative DNS in variable NAMESERVER. Signed-off-by: Zhang Ning <832666+zhangn1985@users.noreply.github.com>
656 lines
26 KiB
Bash
656 lines
26 KiB
Bash
# Copyright (c) 2015 Igor Pecovnik, igor.pecovnik@gma**.com
|
|
#
|
|
# This file is licensed under the terms of the GNU General Public
|
|
# License version 2. This program is licensed "as is" without any
|
|
# warranty of any kind, whether express or implied.
|
|
|
|
# This file is a part of the Armbian build script
|
|
# https://github.com/armbian/build/
|
|
|
|
# Functions:
|
|
# debootstrap_ng
|
|
# create_rootfs_cache
|
|
# prepare_partitions
|
|
# update_initramfs
|
|
# create_image
|
|
|
|
# debootstrap_ng
|
|
#
|
|
debootstrap_ng()
|
|
{
|
|
display_alert "Starting rootfs and image building process for" "$BOARD $RELEASE" "info"
|
|
|
|
[[ $ROOTFS_TYPE != ext4 ]] && display_alert "Assuming $BOARD $BRANCH kernel supports $ROOTFS_TYPE" "" "wrn"
|
|
|
|
# trap to unmount stuff in case of error/manual interruption
|
|
trap unmount_on_exit INT TERM EXIT
|
|
|
|
# stage: clean and create directories
|
|
rm -rf $SDCARD $MOUNT
|
|
mkdir -p $SDCARD $MOUNT $DEST/images $SRC/cache/rootfs
|
|
|
|
# stage: verify tmpfs configuration and mount
|
|
# default maximum size for tmpfs mount is 1/2 of available RAM
|
|
# CLI needs ~1.2GiB+ (Xenial CLI), Desktop - ~2.8GiB+ (Xenial Desktop w/o HW acceleration)
|
|
# calculate and set tmpfs mount to use 2/3 of available RAM
|
|
local phymem=$(( $(awk '/MemTotal/ {print $2}' /proc/meminfo) / 1024 * 2 / 3 )) # MiB
|
|
if [[ $BUILD_DESKTOP == yes ]]; then local tmpfs_max_size=3500; else local tmpfs_max_size=1500; fi # MiB
|
|
if [[ $FORCE_USE_RAMDISK == no ]]; then local use_tmpfs=no
|
|
elif [[ $FORCE_USE_RAMDISK == yes || $phymem -gt $tmpfs_max_size ]]; then
|
|
local use_tmpfs=yes
|
|
fi
|
|
[[ -n $FORCE_TMPFS_SIZE ]] && phymem=$FORCE_TMPFS_SIZE
|
|
|
|
[[ $use_tmpfs == yes ]] && mount -t tmpfs -o size=${phymem}M tmpfs $SDCARD
|
|
|
|
# stage: prepare basic rootfs: unpack cache or create from scratch
|
|
create_rootfs_cache
|
|
|
|
# stage: install kernel and u-boot packages
|
|
# install distribution and board specific applications
|
|
|
|
install_distribution_specific
|
|
install_common
|
|
|
|
# install additional applications
|
|
[[ $EXTERNAL == yes ]] && install_external_applications
|
|
|
|
# install locally built packages
|
|
[[ $EXTERNAL_NEW == compile ]] && chroot_installpackages_local
|
|
|
|
# install from apt.armbian.com
|
|
[[ $EXTERNAL_NEW == prebuilt ]] && chroot_installpackages "yes"
|
|
|
|
# stage: user customization script
|
|
# NOTE: installing too many packages may fill tmpfs mount
|
|
customize_image
|
|
|
|
# create list of installed packages for debug purposes
|
|
chroot $SDCARD /bin/bash -c "dpkg --get-selections" | grep -v deinstall | awk '{print $1}' | cut -f1 -d':' >> $DEST/debug/installed-packages-${RELEASE}.list 2>&1
|
|
|
|
# clean up / prepare for making the image
|
|
umount_chroot "$SDCARD"
|
|
post_debootstrap_tweaks
|
|
|
|
if [[ $ROOTFS_TYPE == fel ]]; then
|
|
FEL_ROOTFS=$SDCARD/
|
|
display_alert "Starting FEL boot" "$BOARD" "info"
|
|
source $SRC/lib/fel-load.sh
|
|
else
|
|
prepare_partitions
|
|
# update initramfs to reflect any configuration changes since kernel installation
|
|
update_initramfs
|
|
create_image
|
|
fi
|
|
|
|
# stage: unmount tmpfs
|
|
[[ $use_tmpfs = yes ]] && umount $SDCARD
|
|
|
|
rm -rf $SDCARD
|
|
|
|
# remove exit trap
|
|
trap - INT TERM EXIT
|
|
} #############################################################################
|
|
|
|
# create_rootfs_cache
|
|
#
|
|
# unpacks cached rootfs for $RELEASE or creates one
|
|
#
|
|
create_rootfs_cache()
|
|
{
|
|
local packages_hash=$(get_package_list_hash)
|
|
local cache_type=$(if [[ ${BUILD_DESKTOP} == yes ]]; then echo "desktop"; else echo "cli";fi)
|
|
local cache_name=${RELEASE}-${cache_type}-${ARCH}.$packages_hash.tar.lz4
|
|
local cache_fname=${SRC}/cache/rootfs/${cache_name}
|
|
local display_name=${RELEASE}-${cache_type}-${ARCH}.${packages_hash:0:3}...${packages_hash:29}.tar.lz4
|
|
|
|
display_alert "Checking for local cache" "$display_name" "info"
|
|
if [[ ! -f $cache_fname && "$ROOT_FS_CREATE_ONLY" != "force" ]]; then
|
|
display_alert "searching on servers"
|
|
download_and_verify "_rootfs" "$cache_name"
|
|
fi
|
|
|
|
if [[ -f $cache_fname && "$ROOT_FS_CREATE_ONLY" != "force" ]]; then
|
|
local date_diff=$(( ($(date +%s) - $(stat -c %Y $cache_fname)) / 86400 ))
|
|
display_alert "Extracting $display_name" "$date_diff days old" "info"
|
|
pv -p -b -r -c -N "[ .... ] $display_name" "$cache_fname" | lz4 -dc | tar xp --xattrs -C $SDCARD/
|
|
[[ $? -ne 0 ]] && rm $cache_fname && exit_with_error "Cache $cache_fname is corrupted and was deleted. Restart."
|
|
rm $SDCARD/etc/resolv.conf
|
|
echo "nameserver $NAMESERVER" >> $SDCARD/etc/resolv.conf
|
|
else
|
|
display_alert "... remote not found" "Creating new rootfs cache for $RELEASE" "info"
|
|
|
|
# stage: debootstrap base system
|
|
if [[ $NO_APT_CACHER != yes ]]; then
|
|
# apt-cacher-ng apt-get proxy parameter
|
|
local apt_extra="-o Acquire::http::Proxy=\"http://${APT_PROXY_ADDR:-localhost:3142}\""
|
|
local apt_mirror="http://${APT_PROXY_ADDR:-localhost:3142}/$APT_MIRROR"
|
|
else
|
|
local apt_mirror="http://$APT_MIRROR"
|
|
fi
|
|
|
|
# fancy progress bars
|
|
[[ -z $OUTPUT_DIALOG ]] && local apt_extra_progress="--show-progress -o DPKG::Progress-Fancy=1"
|
|
|
|
display_alert "Installing base system" "Stage 1/2" "info"
|
|
eval 'debootstrap --include=${DEBOOTSTRAP_LIST} ${PACKAGE_LIST_EXCLUDE:+ --exclude=${PACKAGE_LIST_EXCLUDE// /,}} \
|
|
--arch=$ARCH --components=${DEBOOTSTRAP_COMPONENTS} --foreign $RELEASE $SDCARD/ $apt_mirror' \
|
|
${PROGRESS_LOG_TO_FILE:+' | tee -a $DEST/debug/debootstrap.log'} \
|
|
${OUTPUT_DIALOG:+' | dialog --backtitle "$backtitle" --progressbox "Debootstrap (stage 1/2)..." $TTY_Y $TTY_X'} \
|
|
${OUTPUT_VERYSILENT:+' >/dev/null 2>/dev/null'}
|
|
|
|
[[ ${PIPESTATUS[0]} -ne 0 || ! -f $SDCARD/debootstrap/debootstrap ]] && exit_with_error "Debootstrap base system first stage failed"
|
|
|
|
cp /usr/bin/$QEMU_BINARY $SDCARD/usr/bin/
|
|
|
|
mkdir -p $SDCARD/usr/share/keyrings/
|
|
cp /usr/share/keyrings/debian-archive-keyring.gpg $SDCARD/usr/share/keyrings/
|
|
|
|
display_alert "Installing base system" "Stage 2/2" "info"
|
|
eval 'chroot $SDCARD /bin/bash -c "/debootstrap/debootstrap --second-stage"' \
|
|
${PROGRESS_LOG_TO_FILE:+' | tee -a $DEST/debug/debootstrap.log'} \
|
|
${OUTPUT_DIALOG:+' | dialog --backtitle "$backtitle" --progressbox "Debootstrap (stage 2/2)..." $TTY_Y $TTY_X'} \
|
|
${OUTPUT_VERYSILENT:+' >/dev/null 2>/dev/null'}
|
|
|
|
[[ ${PIPESTATUS[0]} -ne 0 || ! -f $SDCARD/bin/bash ]] && exit_with_error "Debootstrap base system second stage failed"
|
|
|
|
mount_chroot "$SDCARD"
|
|
|
|
# policy-rc.d script prevents starting or reloading services during image creation
|
|
printf '#!/bin/sh\nexit 101' > $SDCARD/usr/sbin/policy-rc.d
|
|
chroot $SDCARD /bin/bash -c "dpkg-divert --quiet --local --rename --add /sbin/initctl"
|
|
chroot $SDCARD /bin/bash -c "dpkg-divert --quiet --local --rename --add /sbin/start-stop-daemon"
|
|
printf '#!/bin/sh\necho "Warning: Fake start-stop-daemon called, doing nothing"' > $SDCARD/sbin/start-stop-daemon
|
|
printf '#!/bin/sh\necho "Warning: Fake initctl called, doing nothing"' > $SDCARD/sbin/initctl
|
|
chmod 755 $SDCARD/usr/sbin/policy-rc.d
|
|
chmod 755 $SDCARD/sbin/initctl
|
|
chmod 755 $SDCARD/sbin/start-stop-daemon
|
|
|
|
# stage: configure language and locales
|
|
display_alert "Configuring locales" "$DEST_LANG" "info"
|
|
|
|
[[ -f $SDCARD/etc/locale.gen ]] && sed -i "s/^# $DEST_LANG/$DEST_LANG/" $SDCARD/etc/locale.gen
|
|
eval 'LC_ALL=C LANG=C chroot $SDCARD /bin/bash -c "locale-gen $DEST_LANG"' ${OUTPUT_VERYSILENT:+' >/dev/null 2>/dev/null'}
|
|
eval 'LC_ALL=C LANG=C chroot $SDCARD /bin/bash -c "update-locale LANG=$DEST_LANG LANGUAGE=$DEST_LANG LC_MESSAGES=$DEST_LANG"' \
|
|
${OUTPUT_VERYSILENT:+' >/dev/null 2>/dev/null'}
|
|
|
|
if [[ -f $SDCARD/etc/default/console-setup ]]; then
|
|
sed -e 's/CHARMAP=.*/CHARMAP="UTF-8"/' -e 's/FONTSIZE=.*/FONTSIZE="8x16"/' \
|
|
-e 's/CODESET=.*/CODESET="guess"/' -i $SDCARD/etc/default/console-setup
|
|
eval 'LC_ALL=C LANG=C chroot $SDCARD /bin/bash -c "setupcon --save"'
|
|
fi
|
|
|
|
# stage: create apt sources list
|
|
create_sources_list "$RELEASE" "$SDCARD/"
|
|
|
|
# stage: add armbian repository and install key
|
|
echo "deb http://apt.armbian.com $RELEASE main ${RELEASE}-utils ${RELEASE}-desktop" > $SDCARD/etc/apt/sources.list.d/armbian.list
|
|
|
|
cp $SRC/config/armbian.key $SDCARD
|
|
eval 'chroot $SDCARD /bin/bash -c "cat armbian.key | apt-key add -"' \
|
|
${OUTPUT_VERYSILENT:+' >/dev/null 2>/dev/null'}
|
|
rm $SDCARD/armbian.key
|
|
|
|
# compressing packages list to gain some space
|
|
echo "Acquire::GzipIndexes "true"; Acquire::CompressionTypes::Order:: "gz";" > $SDCARD/etc/apt/apt.conf.d/02compress-indexes
|
|
echo "Acquire::Languages "none";" > $SDCARD/etc/apt/apt.conf.d/no-languages
|
|
|
|
# add armhf arhitecture to arm64
|
|
[[ $ARCH == arm64 ]] && eval 'LC_ALL=C LANG=C chroot $SDCARD /bin/bash -c "dpkg --add-architecture armhf"'
|
|
|
|
# this should fix resolvconf installation failure in some cases
|
|
chroot $SDCARD /bin/bash -c 'echo "resolvconf resolvconf/linkify-resolvconf boolean false" | debconf-set-selections'
|
|
|
|
# stage: update packages list
|
|
display_alert "Updating package list" "$RELEASE" "info"
|
|
eval 'LC_ALL=C LANG=C chroot $SDCARD /bin/bash -c "apt-get -q -y $apt_extra update"' \
|
|
${PROGRESS_LOG_TO_FILE:+' | tee -a $DEST/debug/debootstrap.log'} \
|
|
${OUTPUT_DIALOG:+' | dialog --backtitle "$backtitle" --progressbox "Updating package lists..." $TTY_Y $TTY_X'} \
|
|
${OUTPUT_VERYSILENT:+' >/dev/null 2>/dev/null'}
|
|
|
|
#[[ ${PIPESTATUS[0]} -ne 0 ]] && exit_with_error "Updating package lists failed"
|
|
|
|
# stage: upgrade base packages from xxx-updates and xxx-backports repository branches
|
|
display_alert "Upgrading base packages" "Armbian" "info"
|
|
eval 'LC_ALL=C LANG=C chroot $SDCARD /bin/bash -c "DEBIAN_FRONTEND=noninteractive apt-get -y -q \
|
|
$apt_extra $apt_extra_progress upgrade"' \
|
|
${PROGRESS_LOG_TO_FILE:+' | tee -a $DEST/debug/debootstrap.log'} \
|
|
${OUTPUT_DIALOG:+' | dialog --backtitle "$backtitle" --progressbox "Upgrading base packages..." $TTY_Y $TTY_X'} \
|
|
${OUTPUT_VERYSILENT:+' >/dev/null 2>/dev/null'}
|
|
|
|
#[[ ${PIPESTATUS[0]} -ne 0 ]] && exit_with_error "Upgrading base packages failed"
|
|
|
|
# stage: install additional packages
|
|
display_alert "Installing packages for" "Armbian" "info"
|
|
eval 'LC_ALL=C LANG=C chroot $SDCARD /bin/bash -c "DEBIAN_FRONTEND=noninteractive apt -y -q \
|
|
$apt_extra $apt_extra_progress --no-install-recommends install $PACKAGE_LIST"' \
|
|
${PROGRESS_LOG_TO_FILE:+' | tee -a $DEST/debug/debootstrap.log'} \
|
|
${OUTPUT_DIALOG:+' | dialog --backtitle "$backtitle" --progressbox "Installing Armbian system..." $TTY_Y $TTY_X'} \
|
|
${OUTPUT_VERYSILENT:+' >/dev/null 2>/dev/null'}
|
|
|
|
[[ ${PIPESTATUS[0]} -ne 0 ]] && exit_with_error "Installation of Armbian packages failed"
|
|
|
|
# DEBUG: print free space
|
|
echo -e "\nFree space:"
|
|
eval 'df -h' ${PROGRESS_LOG_TO_FILE:+' | tee -a $DEST/debug/debootstrap.log'}
|
|
|
|
# stage: remove downloaded packages
|
|
chroot $SDCARD /bin/bash -c "apt-get clean"
|
|
|
|
# create list of installed packages for debug purposes
|
|
chroot $SDCARD /bin/bash -c "dpkg --get-selections" | grep -v deinstall | awk '{print $1}' | cut -f1 -d':' > ${cache_fname}.list 2>&1
|
|
|
|
# this is needed for the build process later since resolvconf generated file in /run is not saved
|
|
rm $SDCARD/etc/resolv.conf
|
|
echo "nameserver $NAMESERVER" >> $SDCARD/etc/resolv.conf
|
|
|
|
# stage: make rootfs cache archive
|
|
display_alert "Ending debootstrap process and preparing cache" "$RELEASE" "info"
|
|
sync
|
|
# the only reason to unmount here is compression progress display
|
|
# based on rootfs size calculation
|
|
umount_chroot "$SDCARD"
|
|
|
|
tar cp --xattrs --directory=$SDCARD/ --exclude='./dev/*' --exclude='./proc/*' --exclude='./run/*' --exclude='./tmp/*' \
|
|
--exclude='./sys/*' . | pv -p -b -r -s $(du -sb $SDCARD/ | cut -f1) -N "$display_name" | lz4 -c > $cache_fname
|
|
|
|
# sign rootfs cache archive that it can be used for web cache once. Internal purposes
|
|
if [[ -n $GPG_PASS ]]; then
|
|
echo $GPG_PASS | gpg --passphrase-fd 0 --armor --detach-sign --pinentry-mode loopback --batch --yes $cache_fname
|
|
fi
|
|
|
|
fi
|
|
|
|
# used for internal purposes. Faster rootfs cache rebuilding
|
|
if [[ -n "$ROOT_FS_CREATE_ONLY" ]]; then
|
|
[[ $use_tmpfs = yes ]] && umount $SDCARD
|
|
rm -rf $SDCARD
|
|
# remove exit trap
|
|
trap - INT TERM EXIT
|
|
exit
|
|
fi
|
|
|
|
mount_chroot "$SDCARD"
|
|
} #############################################################################
|
|
|
|
# prepare_partitions
|
|
#
|
|
# creates image file, partitions and fs
|
|
# and mounts it to local dir
|
|
# FS-dependent stuff (boot and root fs partition types) happens here
|
|
#
|
|
prepare_partitions()
|
|
{
|
|
display_alert "Preparing image file for rootfs" "$BOARD $RELEASE" "info"
|
|
|
|
# possible partition combinations
|
|
# /boot: none, ext4, ext2, fat (BOOTFS_TYPE)
|
|
# root: ext4, btrfs, f2fs, nfs (ROOTFS_TYPE)
|
|
|
|
# declare makes local variables by default if used inside a function
|
|
# NOTE: mountopts string should always start with comma if not empty
|
|
|
|
# array copying in old bash versions is tricky, so having filesystems as arrays
|
|
# with attributes as keys is not a good idea
|
|
declare -A parttype mkopts mkfs mountopts
|
|
|
|
parttype[ext4]=ext4
|
|
parttype[ext2]=ext2
|
|
parttype[fat]=fat16
|
|
parttype[f2fs]=ext4 # not a copy-paste error
|
|
parttype[btrfs]=btrfs
|
|
# parttype[nfs] is empty
|
|
|
|
# metadata_csum and 64bit may need to be disabled explicitly when migrating to newer supported host OS releases
|
|
# TODO: Disable metadata_csum only for older releases (jessie)?
|
|
if [[ $(lsb_release -sc) == bionic ]]; then
|
|
mkopts[ext4]='-q -m 2 -O ^64bit,^metadata_csum'
|
|
elif [[ $(lsb_release -sc) == xenial ]]; then
|
|
mkopts[ext4]='-q -m 2'
|
|
fi
|
|
mkopts[fat]='-n BOOT'
|
|
mkopts[ext2]='-q'
|
|
# mkopts[f2fs] is empty
|
|
# mkopts[btrfs] is empty
|
|
# mkopts[nfs] is empty
|
|
|
|
mkfs[ext4]=ext4
|
|
mkfs[ext2]=ext2
|
|
mkfs[fat]=vfat
|
|
mkfs[f2fs]=f2fs
|
|
mkfs[btrfs]=btrfs
|
|
# mkfs[nfs] is empty
|
|
|
|
mountopts[ext4]=',commit=600,errors=remount-ro'
|
|
# mountopts[ext2] is empty
|
|
# mountopts[fat] is empty
|
|
# mountopts[f2fs] is empty
|
|
mountopts[btrfs]=',commit=600,compress=lzo'
|
|
# mountopts[nfs] is empty
|
|
|
|
# stage: determine partition configuration
|
|
if [[ -n $BOOTFS_TYPE ]]; then
|
|
# 2 partition setup with forced /boot type
|
|
local bootfs=$BOOTFS_TYPE
|
|
local bootpart=1
|
|
local rootpart=2
|
|
[[ -z $BOOTSIZE || $BOOTSIZE -le 8 ]] && BOOTSIZE=64 # MiB
|
|
elif [[ $ROOTFS_TYPE != ext4 && $ROOTFS_TYPE != nfs ]]; then
|
|
# 2 partition setup for non-ext4 local root
|
|
local bootfs=ext4
|
|
local bootpart=1
|
|
local rootpart=2
|
|
[[ -z $BOOTSIZE || $BOOTSIZE -le 8 ]] && BOOTSIZE=64 # MiB
|
|
elif [[ $ROOTFS_TYPE == nfs ]]; then
|
|
# single partition ext4 /boot, no root
|
|
local bootfs=ext4
|
|
local bootpart=1
|
|
[[ -z $BOOTSIZE || $BOOTSIZE -le 8 ]] && BOOTSIZE=64 # MiB, For cleanup processing only
|
|
elif [[ $CRYPTROOT_ENABLE == yes ]]; then
|
|
# 2 partition setup for encrypted /root and non-encrypted /boot
|
|
local bootfs=ext4
|
|
local bootpart=1
|
|
local rootpart=2
|
|
[[ -z $BOOTSIZE || $BOOTSIZE -le 8 ]] && BOOTSIZE=64 # MiB
|
|
else
|
|
# single partition ext4 root
|
|
local rootpart=1
|
|
BOOTSIZE=0
|
|
fi
|
|
|
|
# stage: calculate rootfs size
|
|
local rootfs_size=$(du -sm $SDCARD/ | cut -f1) # MiB
|
|
display_alert "Current rootfs size" "$rootfs_size MiB" "info"
|
|
if [[ -n $FIXED_IMAGE_SIZE && $FIXED_IMAGE_SIZE =~ ^[0-9]+$ ]]; then
|
|
display_alert "Using user-defined image size" "$FIXED_IMAGE_SIZE MiB" "info"
|
|
local sdsize=$FIXED_IMAGE_SIZE
|
|
# basic sanity check
|
|
if [[ $ROOTFS_TYPE != nfs && $sdsize -lt $rootfs_size ]]; then
|
|
exit_with_error "User defined image size is too small" "$sdsize <= $rootfs_size"
|
|
fi
|
|
else
|
|
local imagesize=$(( $rootfs_size + $OFFSET + $BOOTSIZE )) # MiB
|
|
case $ROOTFS_TYPE in
|
|
btrfs)
|
|
# Used for server images, currently no swap functionality, so disk space
|
|
# requirements are rather low since rootfs gets filled with compress-force=zlib
|
|
local sdsize=$(bc -l <<< "scale=0; (($imagesize * 0.8) / 4 + 1) * 4")
|
|
;;
|
|
*)
|
|
# Hardcoded overhead +25% is needed for desktop images,
|
|
# for CLI it could be lower. Align the size up to 4MiB
|
|
if [[ $BUILD_DESKTOP == yes ]]; then
|
|
local sdsize=$(bc -l <<< "scale=0; ((($imagesize * 1.30) / 1 + 0) / 4 + 1) * 4")
|
|
else
|
|
local sdsize=$(bc -l <<< "scale=0; ((($imagesize * 1.25) / 1 + 0) / 4 + 1) * 4")
|
|
fi
|
|
;;
|
|
esac
|
|
fi
|
|
|
|
# stage: create blank image
|
|
display_alert "Creating blank image for rootfs" "$sdsize MiB" "info"
|
|
# truncate --size=${sdsize}M ${SDCARD}.raw # sometimes results in fs corruption, revert to previous know to work solution
|
|
dd if=/dev/zero bs=1M status=none count=$sdsize | pv -p -b -r -s $(( $sdsize * 1024 * 1024 )) | dd status=none of=${SDCARD}.raw
|
|
|
|
# stage: calculate boot partition size
|
|
local bootstart=$(($OFFSET * 2048))
|
|
local rootstart=$(($bootstart + ($BOOTSIZE * 2048)))
|
|
local bootend=$(($rootstart - 1))
|
|
|
|
# stage: create partition table
|
|
display_alert "Creating partitions" "${bootfs:+/boot: $bootfs }root: $ROOTFS_TYPE" "info"
|
|
parted -s ${SDCARD}.raw -- mklabel msdos
|
|
if [[ $ROOTFS_TYPE == nfs ]]; then
|
|
# single /boot partition
|
|
parted -s ${SDCARD}.raw -- mkpart primary ${parttype[$bootfs]} ${bootstart}s -1s
|
|
elif [[ $BOOTSIZE == 0 ]]; then
|
|
# single root partition
|
|
parted -s ${SDCARD}.raw -- mkpart primary ${parttype[$ROOTFS_TYPE]} ${rootstart}s -1s
|
|
else
|
|
# /boot partition + root partition
|
|
parted -s ${SDCARD}.raw -- mkpart primary ${parttype[$bootfs]} ${bootstart}s ${bootend}s
|
|
parted -s ${SDCARD}.raw -- mkpart primary ${parttype[$ROOTFS_TYPE]} ${rootstart}s -1s
|
|
fi
|
|
|
|
# stage: mount image
|
|
# lock access to loop devices
|
|
exec {FD}>/var/lock/armbian-debootstrap-losetup
|
|
flock -x $FD
|
|
|
|
LOOP=$(losetup -f)
|
|
[[ -z $LOOP ]] && exit_with_error "Unable to find free loop device"
|
|
|
|
check_loop_device "$LOOP"
|
|
|
|
# NOTE: losetup -P option is not available in Trusty
|
|
losetup $LOOP ${SDCARD}.raw
|
|
|
|
# loop device was grabbed here, unlock
|
|
flock -u $FD
|
|
|
|
partprobe $LOOP
|
|
|
|
# stage: create fs, mount partitions, create fstab
|
|
rm -f $SDCARD/etc/fstab
|
|
if [[ -n $rootpart ]]; then
|
|
local rootdevice="${LOOP}p${rootpart}"
|
|
|
|
if [[ $CRYPTROOT_ENABLE == yes ]]; then
|
|
display_alert "Encrypting root partition with LUKS..." "cryptsetup luksFormat $rootdevice" ""
|
|
echo -n $CRYPTROOT_PASSPHRASE | cryptsetup luksFormat $CRYPTROOT_PARAMETERS $rootdevice -
|
|
echo -n $CRYPTROOT_PASSPHRASE | cryptsetup luksOpen $rootdevice $ROOT_MAPPER -
|
|
display_alert "Root partition encryption complete." "" "ext"
|
|
# TODO: pass /dev/mapper to Docker
|
|
rootdevice=/dev/mapper/$ROOT_MAPPER # used by `mkfs` and `mount` commands
|
|
fi
|
|
|
|
check_loop_device "$rootdevice"
|
|
display_alert "Creating rootfs" "$ROOTFS_TYPE on $rootdevice"
|
|
mkfs.${mkfs[$ROOTFS_TYPE]} ${mkopts[$ROOTFS_TYPE]} $rootdevice
|
|
[[ $ROOTFS_TYPE == ext4 ]] && tune2fs -o journal_data_writeback $rootdevice > /dev/null
|
|
[[ $ROOTFS_TYPE == btrfs ]] && local fscreateopt="-o compress-force=zlib"
|
|
mount ${fscreateopt} $rootdevice $MOUNT/
|
|
# create fstab (and crypttab) entry
|
|
if [[ $CRYPTROOT_ENABLE == yes ]]; then
|
|
# map the LUKS container partition via its UUID to be the 'cryptroot' device
|
|
echo "$ROOT_MAPPER UUID=$(blkid -s UUID -o value ${LOOP}p${rootpart}) none luks" >> $SDCARD/etc/crypttab
|
|
local rootfs=$rootdevice # used in fstab
|
|
else
|
|
local rootfs="UUID=$(blkid -s UUID -o value $rootdevice)"
|
|
fi
|
|
echo "$rootfs / ${mkfs[$ROOTFS_TYPE]} defaults,noatime,nodiratime${mountopts[$ROOTFS_TYPE]} 0 1" >> $SDCARD/etc/fstab
|
|
fi
|
|
if [[ -n $bootpart ]]; then
|
|
display_alert "Creating /boot" "$bootfs"
|
|
check_loop_device "${LOOP}p${bootpart}"
|
|
mkfs.${mkfs[$bootfs]} ${mkopts[$bootfs]} ${LOOP}p${bootpart}
|
|
mkdir -p $MOUNT/boot/
|
|
mount ${LOOP}p${bootpart} $MOUNT/boot/
|
|
echo "UUID=$(blkid -s UUID -o value ${LOOP}p${bootpart}) /boot ${mkfs[$bootfs]} defaults${mountopts[$bootfs]} 0 2" >> $SDCARD/etc/fstab
|
|
fi
|
|
[[ $ROOTFS_TYPE == nfs ]] && echo "/dev/nfs / nfs defaults 0 0" >> $SDCARD/etc/fstab
|
|
echo "tmpfs /tmp tmpfs defaults,nosuid 0 0" >> $SDCARD/etc/fstab
|
|
|
|
# stage: adjust boot script or boot environment
|
|
if [[ -f $SDCARD/boot/armbianEnv.txt ]]; then
|
|
if [[ $CRYPTROOT_ENABLE == yes ]]; then
|
|
echo "rootdev=$rootdevice cryptdevice=UUID=$(blkid -s UUID -o value ${LOOP}p${rootpart}):$ROOT_MAPPER" >> $SDCARD/boot/armbianEnv.txt
|
|
else
|
|
echo "rootdev=$rootfs" >> $SDCARD/boot/armbianEnv.txt
|
|
fi
|
|
echo "rootfstype=$ROOTFS_TYPE" >> $SDCARD/boot/armbianEnv.txt
|
|
elif [[ $rootpart != 1 ]]; then
|
|
local bootscript_dst=${BOOTSCRIPT##*:}
|
|
sed -i 's/mmcblk0p1/mmcblk0p2/' $SDCARD/boot/$bootscript_dst
|
|
sed -i -e "s/rootfstype=ext4/rootfstype=$ROOTFS_TYPE/" \
|
|
-e "s/rootfstype \"ext4\"/rootfstype \"$ROOTFS_TYPE\"/" $SDCARD/boot/$bootscript_dst
|
|
fi
|
|
|
|
# if we have boot.ini = remove armbianEnv.txt and add UUID there if enabled
|
|
if [[ -f $SDCARD/boot/boot.ini ]]; then
|
|
sed -i -e "s/rootfstype \"ext4\"/rootfstype \"$ROOTFS_TYPE\"/" $SDCARD/boot/boot.ini
|
|
if [[ $CRYPTROOT_ENABLE == yes ]]; then
|
|
local rootpart="UUID=$(blkid -s UUID -o value ${LOOP}p${rootpart})"
|
|
sed -i 's/^setenv rootdev .*/setenv rootdev "\/dev\/mapper\/'$ROOT_MAPPER' cryptdevice='$rootpart':'$ROOT_MAPPER'"/' $SDCARD/boot/boot.ini
|
|
else
|
|
sed -i 's/^setenv rootdev .*/setenv rootdev "'$rootfs'"/' $SDCARD/boot/boot.ini
|
|
fi
|
|
[[ -f $SDCARD/boot/armbianEnv.txt ]] && rm $SDCARD/boot/armbianEnv.txt
|
|
fi
|
|
|
|
# if we have a headless device, set console to DEFAULT_CONSOLE
|
|
if [[ -n $DEFAULT_CONSOLE && -f $SDCARD/boot/armbianEnv.txt ]]; then
|
|
if grep -lq "^console=" $SDCARD/boot/armbianEnv.txt; then
|
|
sed -i "s/console=.*/console=$DEFAULT_CONSOLE/" $SDCARD/boot/armbianEnv.txt
|
|
else
|
|
echo "console=$DEFAULT_CONSOLE" >> $SDCARD/boot/armbianEnv.txt
|
|
fi
|
|
fi
|
|
|
|
# recompile .cmd to .scr if boot.cmd exists
|
|
[[ -f $SDCARD/boot/boot.cmd ]] && \
|
|
mkimage -C none -A arm -T script -d $SDCARD/boot/boot.cmd $SDCARD/boot/boot.scr > /dev/null 2>&1
|
|
|
|
} #############################################################################
|
|
|
|
# update_initramfs
|
|
#
|
|
# this should be invoked as late as possible for any modifications by
|
|
# customize_image (userpatches) and prepare_partitions to be reflected in the
|
|
# final initramfs
|
|
#
|
|
# especially, this needs to be invoked after /etc/crypttab has been created
|
|
# for cryptroot-unlock to work:
|
|
# https://serverfault.com/questions/907254/cryproot-unlock-with-dropbear-timeout-while-waiting-for-askpass
|
|
#
|
|
update_initramfs() {
|
|
|
|
update_initramfs_cmd="update-initramfs -uv -k ${VER}-${LINUXFAMILY}"
|
|
display_alert "Updating initramfs..." "$update_initramfs_cmd" ""
|
|
cp /usr/bin/$QEMU_BINARY $SDCARD/usr/bin/
|
|
mount_chroot "$SDCARD/"
|
|
|
|
chroot $SDCARD /bin/bash -c "$update_initramfs_cmd" >> $DEST/debug/install.log 2>&1
|
|
display_alert "Updated initramfs." "for details see: $DEST/debug/install.log" "ext"
|
|
|
|
umount_chroot "$SDCARD/"
|
|
rm $SDCARD/usr/bin/$QEMU_BINARY
|
|
|
|
} #############################################################################
|
|
|
|
# create_image
|
|
#
|
|
# finishes creation of image from cached rootfs
|
|
#
|
|
create_image()
|
|
{
|
|
# stage: create file name
|
|
local version="Armbian_${REVISION}_${BOARD^}_${DISTRIBUTION}_${RELEASE}_${BRANCH}_${VER/-$LINUXFAMILY/}"
|
|
[[ $BUILD_DESKTOP == yes ]] && version=${version}_desktop
|
|
[[ $ROOTFS_TYPE == nfs ]] && version=${version}_nfsboot
|
|
|
|
if [[ $ROOTFS_TYPE != nfs ]]; then
|
|
display_alert "Copying files to root directory"
|
|
rsync -aHWXh --exclude="/boot/*" --exclude="/dev/*" --exclude="/proc/*" --exclude="/run/*" --exclude="/tmp/*" \
|
|
--exclude="/sys/*" --info=progress2,stats1 $SDCARD/ $MOUNT/
|
|
else
|
|
display_alert "Creating rootfs archive" "rootfs.tgz" "info"
|
|
tar cp --xattrs --directory=$SDCARD/ --exclude='./boot/*' --exclude='./dev/*' --exclude='./proc/*' --exclude='./run/*' --exclude='./tmp/*' \
|
|
--exclude='./sys/*' . | pv -p -b -r -s $(du -sb $SDCARD/ | cut -f1) -N "rootfs.tgz" | gzip -c > $DEST/images/${version}-rootfs.tgz
|
|
fi
|
|
|
|
# stage: rsync /boot
|
|
display_alert "Copying files to /boot directory"
|
|
if [[ $(findmnt --target $MOUNT/boot -o FSTYPE -n) == vfat ]]; then
|
|
# fat32
|
|
rsync -rLtWh --info=progress2,stats1 $SDCARD/boot $MOUNT
|
|
else
|
|
# ext4
|
|
rsync -aHWXh --info=progress2,stats1 $SDCARD/boot $MOUNT
|
|
fi
|
|
|
|
# DEBUG: print free space
|
|
display_alert "Free space:" "SD card" "info"
|
|
eval 'df -h' ${PROGRESS_LOG_TO_FILE:+' | tee -a $DEST/debug/debootstrap.log'}
|
|
|
|
# stage: write u-boot
|
|
write_uboot $LOOP
|
|
|
|
# fix wrong / permissions
|
|
chmod 755 $MOUNT
|
|
|
|
# unmount /boot first, rootfs second, image file last
|
|
sync
|
|
[[ $BOOTSIZE != 0 ]] && umount -l $MOUNT/boot
|
|
[[ $ROOTFS_TYPE != nfs ]] && umount -l $MOUNT
|
|
[[ $CRYPTROOT_ENABLE == yes ]] && cryptsetup luksClose $ROOT_MAPPER
|
|
|
|
losetup -d $LOOP
|
|
rm -rf --one-file-system $DESTIMG $MOUNT
|
|
mkdir -p $DESTIMG
|
|
fingerprint_image "$DESTIMG/${version}.txt" "${version}"
|
|
mv ${SDCARD}.raw $DESTIMG/${version}.img
|
|
|
|
if [[ $BUILD_ALL != yes ]]; then
|
|
if [[ $COMPRESS_OUTPUTIMAGE == yes ]]; then
|
|
COMPRESS_OUTPUTIMAGE="sha,gpg,7z"
|
|
fi
|
|
|
|
if [[ $COMPRESS_OUTPUTIMAGE == *sha* ]]; then
|
|
cd $DESTIMG
|
|
display_alert "SHA256 calculating" "${version}.img" "info"
|
|
sha256sum -b ${version}.img > ${version}.img.sha
|
|
cp ${version}.img.sha "$DEST/images/${version}.img.sha"
|
|
cd ..
|
|
fi
|
|
|
|
if [[ $COMPRESS_OUTPUTIMAGE == *gpg* ]]; then
|
|
cd $DESTIMG
|
|
if [[ -n $GPG_PASS ]]; then
|
|
display_alert "GPG signing" "${version}.img" "info"
|
|
echo $GPG_PASS | gpg --passphrase-fd 0 --armor --detach-sign --pinentry-mode loopback --batch --yes ${version}.img || exit 1
|
|
cp ${version}.img.asc "$DEST/images/${version}.img.asc"
|
|
else
|
|
display_alert "GPG signing skipped - no GPG_PASS" "${version}.img" "wrn"
|
|
fi
|
|
cd ..
|
|
fi
|
|
|
|
if [[ $COMPRESS_OUTPUTIMAGE == *7z* ]]; then
|
|
[[ -f $DEST/images/$CRYPTROOT_SSH_UNLOCK_KEY_NAME ]] && cp $DEST/images/$CRYPTROOT_SSH_UNLOCK_KEY_NAME $DESTIMG/
|
|
# compress image
|
|
cd $DESTIMG
|
|
display_alert "Compressing" "$DEST/images/${version}.7z" "info"
|
|
7za a -t7z -bd -m0=lzma2 -mx=3 -mfb=64 -md=32m -ms=on $DEST/images/${version}.7z ${version}.key ${version}.img* armbian.txt >/dev/null 2>&1
|
|
cd ..
|
|
fi
|
|
|
|
if [[ $COMPRESS_OUTPUTIMAGE == *gz* ]]; then
|
|
display_alert "Compressing" "$DEST/images/${version}.img.gz" "info"
|
|
pigz < $DESTIMG/${version}.img > $DEST/images/${version}.img.gz
|
|
fi
|
|
|
|
mv $DESTIMG/${version}.txt $DEST/images/${version}.txt || exit 1
|
|
mv $DESTIMG/${version}.img $DEST/images/${version}.img || exit 1
|
|
rm -rf $DESTIMG
|
|
fi
|
|
|
|
display_alert "Done building" "$DEST/images/${version}.img" "info"
|
|
|
|
# call custom post build hook
|
|
[[ $(type -t post_build_image) == function ]] && post_build_image "$DEST/images/${version}.img"
|
|
|
|
# write image to SD card
|
|
if [[ $(lsblk "$CARD_DEVICE" 2>/dev/null) && -f $DEST/images/${version}.img && $COMPRESS_OUTPUTIMAGE != yes ]]; then
|
|
display_alert "Writing image" "$CARD_DEVICE" "info"
|
|
balena-etcher $DEST/images/${version}.img -d $CARD_DEVICE -y
|
|
if [ $? -eq 0 ]; then
|
|
display_alert "Writing succeeded" "${version}.img" "info"
|
|
else
|
|
display_alert "Writing failed" "${version}.img" "err"
|
|
fi
|
|
fi
|
|
|
|
} #############################################################################
|