1#!/bin/sh 2# 3# Copyright (c) 2010, 2018 4# The DragonFly Project. All rights reserved. 5# 6# Redistribution and use in source and binary forms, with or without 7# modification, are permitted provided that the following conditions 8# are met: 9# 10# 1. Redistributions of source code must retain the above copyright 11# notice, this list of conditions and the following disclaimer. 12# 2. Redistributions in binary form must reproduce the above copyright 13# notice, this list of conditions and the following disclaimer in 14# the documentation and/or other materials provided with the 15# distribution. 16# 3. Neither the name of The DragonFly Project nor the names of its 17# contributors may be used to endorse or promote products derived 18# from this software without specific, prior written permission. 19# 20# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 23# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 24# COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 25# INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, 26# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 27# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 28# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 29# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 30# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31# SUCH DAMAGE. 32# 33 34# 35# Description 36# 37# This tool packs the (statically linked) rescue tools (at /rescue by 38# default) and contents specified by "-c <content_dirs>", such as the 39# necessary etc files, into an UFS-formatted VN image. This image is 40# installed at /boot/kernel/initrd.img.gz and used as the initial ramdisk 41# to help mount the real root filesystem when it is encrypted or on LVM. 42# 43 44 45# 46# Default configurations 47# 48 49# Directory hierarchy on the initrd 50# 51# * 'new_root' will always be created 52# * 'sbin', 'usr.bin', and 'usr.sbin' will be symlinked to 'bin' 53# * 'tmp' will be symlinked to 'var/tmp' 54# 55INITRD_DIRS="bin dev etc mnt usr var" 56 57# Directory of the statically linked rescue tools which will be copied 58# onto the initrd. 59RESCUE_DIR="/rescue" 60 61# Specify the location that the initrd will be installed to, i.e., 62# <BOOT_DIR>/kernel/initrd.img.gz 63BOOT_DIR="/boot" 64 65# Maximum size (number of MB) allowed for the initrd image 66INITRD_SIZE_MAX="15" # MB 67 68# When run from the buildworld/installworld environment do not require that 69# things like uniq, kldload, mount, newfs, etc be in the cross-tools. 70# These must run natively for the current system version. 71# 72PATH=${PATH}:/sbin:/usr/sbin:/bin:/usr/bin 73 74# 75# Helper functions 76# 77 78log() { 79 echo "$@" >&2 80} 81 82error() { 83 local rc=$1 84 shift 85 log "$@" 86 exit ${rc} 87} 88 89check_dirs() { 90 for _dir; do 91 [ -d "${_dir}" ] || 92 error 1 "Directory '${_dir}' does not exist" 93 done 94 return 0 95} 96 97 98# 99# Functions 100# 101 102calc_initrd_size() { 103 log "Calculating required initrd size ..." 104 isize=0 105 for _dir; do 106 csize=$(du -kst ${_dir} | awk '{ print $1 }') # KB 107 log "* ${_dir}: ${csize} KB" 108 isize=$((${isize} + ${csize})) 109 done 110 # Round initrd size up by MB 111 isize_mb=$(echo ${isize} | awk ' 112 function ceil(x) { 113 y = int(x); 114 return (x>y ? y+1 : y); 115 } 116 { 117 print ceil($1 / 1024); 118 }') 119 # Reserve another 1 MB for advanced user to add custom files to the 120 # initrd without creating it from scratch. 121 echo $((${isize_mb} + 1)) 122} 123 124create_vn() { 125 kldstat -qm vn || kldload -n vn || 126 error 1 "Failed to load vn kernel module" 127 128 VN_DEV=$(vnconfig -c -S ${INITRD_SIZE}m -Z -T vn ${INITRD_FILE}) && 129 echo "Configured ${VN_DEV}" || 130 error 1 "Failed to configure VN device" 131 132 newfs -i 131072 -m 0 /dev/${VN_DEV}s0 && 133 echo "Formatted initrd image with UFS" || 134 error 1 "Failed to format the initrd image" 135 mount_ufs /dev/${VN_DEV}s0 ${BUILD_DIR} && 136 echo "Mounted initrd image on ${BUILD_DIR}" || 137 error 1 "Failed to mount initrd image on ${BUILD_DIR}" 138} 139 140destroy_vn() { 141 umount /dev/${VN_DEV}s0 && 142 echo "Unmounted initrd image" || 143 error 1 "Failed to umount initrd image" 144 vnconfig -u ${VN_DEV} && 145 echo "Unconfigured ${VN_DEV}" || 146 error 1 "Failed to unconfigure ${VN_DEV}" 147} 148 149make_hier() { 150 mkdir -p ${BUILD_DIR}/new_root || 151 error 1 "Failed to mkdir ${BUILD_DIR}/new_root" 152 # Symlink 'sbin' to 'bin' 153 ln -sf bin ${BUILD_DIR}/sbin 154 # Symlink 'tmp' to 'var/tmp', as '/var' will be mounted with 155 # tmpfs, saving a second tmpfs been mounted on '/tmp'. 156 ln -sf var/tmp ${BUILD_DIR}/tmp 157 for _dir in ${INITRD_DIRS}; do 158 [ ! -d "${BUILD_DIR}/${_dir}" ] && 159 mkdir -p ${BUILD_DIR}/${_dir} 160 done 161 # Symlink 'usr/bin' and 'usr/sbin' to 'bin' 162 ln -sf ../bin ${BUILD_DIR}/usr/bin 163 ln -sf ../bin ${BUILD_DIR}/usr/sbin 164 echo "Created directory structure" 165} 166 167copy_rescue() { 168 cpdup -o -u ${RESCUE_DIR}/ ${BUILD_DIR}/bin/ && 169 echo "Copied ${RESCUE_DIR} to ${BUILD_DIR}/bin" || 170 error 1 "Failed to copy ${RESCUE_DIR} to ${BUILD_DIR}/bin" 171} 172 173copy_content() { 174 for _dir in ${CONTENT_DIRS}; do 175 cpdup -o -u ${_dir}/ ${BUILD_DIR}/ && 176 echo "Copied ${_dir} to ${BUILD_DIR}" || 177 error 1 "Failed to copy ${dir} to ${BUILD_DIR}" 178 done 179} 180 181print_info() { 182 lt ${BUILD_DIR} 183 df -h ${BUILD_DIR} 184} 185 186# Check the validity of the created initrd image before moving over. 187# This prevents creating an empty and broken initrd image by running 188# this tool but without ${CONTENT_DIRS} prepared. 189# 190# NOTE: Need more improvements. 191# 192check_initrd() 193{ 194 [ -x "${BUILD_DIR}/sbin/oinit" ] && 195 [ -x "${BUILD_DIR}/bin/sh" ] && 196 [ -x "${BUILD_DIR}/etc/rc" ] || { 197 destroy_vn 198 error 1 "Ivalid initrd image!" 199 } 200} 201 202usage() { 203 error 2 \ 204 "usage: ${0##*/} [-b boot_dir] [-r rescue_dir]" \ 205 "[-s size] [-S max_size] -c <content_dirs>" 206} 207 208 209# 210# Main 211# 212 213while getopts :b:c:hr:s:S: opt; do 214 case ${opt} in 215 b) 216 BOOT_DIR="${OPTARG}" 217 ;; 218 c) 219 CONTENT_DIRS="${OPTARG}" 220 ;; 221 h) 222 usage 223 ;; 224 r) 225 RESCUE_DIR="${OPTARG}" 226 ;; 227 s) 228 INITRD_SIZE="${OPTARG}" 229 ;; 230 S) 231 INITRD_SIZE_MAX="${OPTARG}" 232 ;; 233 \?) 234 log "Invalid option -${OPTARG}" 235 usage 236 ;; 237 :) 238 log "Option -${OPTARG} requires an argument" 239 usage 240 ;; 241 esac 242done 243 244shift $((OPTIND - 1)) 245[ $# -ne 0 ] && usage 246[ -z "${BOOT_DIR}" -o -z "${RESCUE_DIR}" -o -z "${CONTENT_DIRS}" ] && usage 247check_dirs ${BOOT_DIR} ${RESCUE_DIR} ${CONTENT_DIRS} 248 249VN_DEV="" 250INITRD_SIZE=${INITRD_SIZE%[mM]} # MB 251INITRD_SIZE_MAX=${INITRD_SIZE_MAX%[mM]} # MB 252 253BUILD_DIR=$(mktemp -d -t initrd) || error $? "Cannot create build directory" 254echo "Initrd build directory: ${BUILD_DIR}" 255INITRD_FILE="${BUILD_DIR}.img" 256INITRD_DEST="${BOOT_DIR}/kernel/initrd.img.gz" 257 258CSIZE=$(calc_initrd_size ${RESCUE_DIR} ${CONTENT_DIRS}) 259echo "Required initrd image size: ${CSIZE} MB" 260if [ -n "${INITRD_SIZE}" -a "${INITRD_SIZE}" != "0" ]; then 261 if [ ${CSIZE} -gt ${INITRD_SIZE} ]; then 262 error 1 "Given initrd size (${INITRD_SIZE} MB) too small" 263 fi 264else 265 INITRD_SIZE=${CSIZE} 266fi 267echo "Initrd size: ${INITRD_SIZE} MB" 268 269if [ -n "${INITRD_SIZE_MAX}" -a "${INITRD_SIZE_MAX}" != "0" ] && \ 270 [ ${INITRD_SIZE} -gt ${INITRD_SIZE_MAX} ]; then 271 error 1 "Exceeded the maximum size (${INITRD_SIZE_MAX} MB)" 272fi 273 274create_vn 275make_hier 276copy_rescue 277copy_content 278print_info 279destroy_vn 280rm -rf ${BUILD_DIR} 281 282echo -n "Compressing ${INITRD_FILE} ..." 283gzip -9 ${INITRD_FILE} 284echo " OK" 285 286if [ -f "${INITRD_DEST}" ]; then 287 echo -n "Backing up ${INITRD_DEST} ..." 288 mv ${INITRD_DEST} ${INITRD_DEST}.old 289 echo " OK (${INITRD_DEST}.old)" 290fi 291 292echo -n "Installing ${INITRD_FILE}.gz to ${INITRD_DEST} ..." 293install -o root -g wheel -m 444 ${INITRD_FILE}.gz ${INITRD_DEST} 294echo " OK" 295rm -f ${INITRD_FILE}.gz 296