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 initrad 50# 51# * 'new_root' will always be created 52# * 'sbin' will be symlinked to 'bin' 53# * 'tmp' will be symlinked to 'var/tmp' 54# 55INITRD_DIRS="bin dev etc mnt 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 69# 70# Helper functions 71# 72 73log() { 74 echo "$@" >&2 75} 76 77error() { 78 local rc=$1 79 shift 80 log "$@" 81 exit ${rc} 82} 83 84check_dirs() { 85 for _dir; do 86 [ -d "${_dir}" ] || 87 error 1 "Directory '${_dir}' does not exist" 88 done 89 return 0 90} 91 92# Calculate the total size of the given directory, taking care of the 93# hard links. 94# 95# NOTE: Do not use 'du' since it gives the disk usage of the files, 96# which varies between different filesystems. 97# 98calc_size() { 99 find "$1" -ls | \ 100 awk '{ print $7,$1 }' | \ 101 sort -n -k 2 | \ 102 uniq -f 1 | \ 103 awk '{ sum+=$1 } END { print sum }' # byte 104} 105 106 107# 108# Functions 109# 110 111calc_initrd_size() { 112 log "Calculating required initrd size ..." 113 isize=0 114 for _dir; do 115 csize=$(calc_size ${_dir}) 116 log "* ${_dir}: ${csize} bytes" 117 isize=$((${isize} + ${csize})) 118 done 119 # Round initrd size up by MB 120 isize_mb=$(echo ${isize} | awk ' 121 function ceil(x) { 122 y = int(x); 123 return (x>y ? y+1 : y); 124 } 125 { 126 mb = $1/1024/1024; 127 print ceil(mb); 128 }') 129 # Add additional 1 MB 130 echo $((${isize_mb} + 1)) 131} 132 133create_vn() { 134 kldload -n vn 135 VN_DEV=$(vnconfig -c -S ${INITRD_SIZE}m -Z -T vn ${INITRD_FILE}) && 136 echo "Configured ${VN_DEV}" || 137 error 1 "Failed to configure VN device" 138 139 newfs -i 131072 -m 0 /dev/${VN_DEV}s0 && 140 echo "Formatted initrd image with UFS" || 141 error 1 "Failed to format the initrd image" 142 mount_ufs /dev/${VN_DEV}s0 ${BUILD_DIR} && 143 echo "Mounted initrd image on ${BUILD_DIR}" || 144 error 1 "Failed to mount initrd image on ${BUILD_DIR}" 145} 146 147destroy_vn() { 148 umount /dev/${VN_DEV}s0 && 149 echo "Unmounted initrd image" || 150 error 1 "Failed to umount initrd image" 151 vnconfig -u ${VN_DEV} && 152 echo "Unconfigured ${VN_DEV}" || 153 error 1 "Failed to unconfigure ${VN_DEV}" 154} 155 156make_hier() { 157 mkdir -p ${BUILD_DIR}/new_root 158 # Symlink 'sbin' to 'bin' 159 ln -sf bin ${BUILD_DIR}/sbin 160 # Symlink 'tmp' to 'var/tmp', as '/var' will be mounted with 161 # tmpfs, saving a second tmpfs been mounted on '/tmp'. 162 ln -sf var/tmp ${BUILD_DIR}/tmp 163 for _dir in ${INITRD_DIRS}; do 164 [ ! -d "${BUILD_DIR}/${_dir}" ] && 165 mkdir -p ${BUILD_DIR}/${_dir} 166 done 167 echo "Created directory structure" 168} 169 170copy_rescue() { 171 cpdup -o -u ${RESCUE_DIR}/ ${BUILD_DIR}/bin/ && 172 echo "Copied ${RESCUE_DIR} to ${BUILD_DIR}/bin" || 173 error 1 "Failed to copy ${RESCUE_DIR} to ${BUILD_DIR}/bin" 174} 175 176copy_content() { 177 for _dir in ${CONTENT_DIRS}; do 178 cpdup -o -u ${_dir}/ ${BUILD_DIR}/ && 179 echo "Copied ${_dir} to ${BUILD_DIR}" || 180 error 1 "Failed to copy ${dir} to ${BUILD_DIR}" 181 done 182} 183 184print_info() { 185 lt ${BUILD_DIR} 186 df -h ${BUILD_DIR} 187} 188 189# Check the validity of the created initrd image before moving over. 190# This prevents creating an empty and broken initrd image by running 191# this tool but without ${CONTENT_DIRS} prepared. 192# 193# NOTE: Need more improvements. 194# 195check_initrd() 196{ 197 [ -x "${BUILD_DIR}/sbin/oinit" ] || { 198 destroy_vn 199 error 1 "Created initrd image is invalid!" 200 } 201} 202 203usage() { 204 error 2 \ 205 "usage: ${0##*/} [-b boot_dir] [-r rescue_dir]" \ 206 "[-s size] [-S max_size] -c <content_dirs>" 207} 208 209 210# 211# Main 212# 213 214while getopts :b:c:hr:s:S: opt; do 215 case ${opt} in 216 b) 217 BOOT_DIR="${OPTARG}" 218 ;; 219 c) 220 CONTENT_DIRS="${OPTARG}" 221 ;; 222 h) 223 usage 224 ;; 225 r) 226 RESCUE_DIR="${OPTARG}" 227 ;; 228 s) 229 INITRD_SIZE="${OPTARG}" 230 ;; 231 S) 232 INITRD_SIZE_MAX="${OPTARG}" 233 ;; 234 \?) 235 log "Invalid option -${OPTARG}" 236 usage 237 ;; 238 :) 239 log "Option -${OPTARG} requires an argument" 240 usage 241 ;; 242 esac 243done 244 245shift $((OPTIND - 1)) 246[ $# -ne 0 ] && usage 247[ -z "${BOOT_DIR}" -o -z "${RESCUE_DIR}" -o -z "${CONTENT_DIRS}" ] && usage 248check_dirs ${BOOT_DIR} ${RESCUE_DIR} ${CONTENT_DIRS} 249 250VN_DEV="" 251INITRD_SIZE=${INITRD_SIZE%[mM]} # MB 252INITRD_SIZE_MAX=${INITRD_SIZE_MAX%[mM]} # MB 253 254BUILD_DIR=$(mktemp -d -t initrd) || error $? "Cannot create build directory" 255echo "Initrd build directory: ${BUILD_DIR}" 256INITRD_FILE="${BUILD_DIR}.img" 257INITRD_DEST="${BOOT_DIR}/kernel/initrd.img.gz" 258 259CSIZE=$(calc_initrd_size ${RESCUE_DIR} ${CONTENT_DIRS}) 260echo "Required initrd image size: ${CSIZE} MB" 261if [ -n "${INITRD_SIZE}" -a "${INITRD_SIZE}" != "0" ]; then 262 if [ ${CSIZE} -gt ${INITRD_SIZE} ]; then 263 error 1 "Given initrd size (${INITRD_SIZE} MB) too small" 264 fi 265else 266 INITRD_SIZE=${CSIZE} 267fi 268echo "Initrd size: ${INITRD_SIZE} MB" 269 270if [ -n "${INITRD_SIZE_MAX}" -a "${INITRD_SIZE_MAX}" != "0" ] && \ 271 [ ${INITRD_SIZE} -gt ${INITRD_SIZE_MAX} ]; then 272 error 1 "Exceeded the maximum size (${INITRD_SIZE_MAX} MB)" 273fi 274 275create_vn 276make_hier 277copy_rescue 278copy_content 279print_info 280destroy_vn 281rm -rf ${BUILD_DIR} 282 283echo -n "Compressing ${INITRD_FILE} ..." 284gzip -9 ${INITRD_FILE} 285echo " OK" 286 287if [ -f "${INITRD_DEST}" ]; then 288 echo -n "Backing up ${INITRD_DEST} ..." 289 mv ${INITRD_DEST} ${INITRD_DEST}.old 290 echo " OK (${INITRD_DEST}.old)" 291fi 292 293echo -n "Copying ${INITRD_FILE}.gz to ${INITRD_DEST} ..." 294mv ${INITRD_FILE}.gz ${INITRD_DEST} 295echo " OK" 296