xref: /dragonfly/initrd/mkinitrd.sh (revision 63e03116)
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 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
124make_img() {
125	makefs -m ${INITRD_SIZE}m -M ${INITRD_SIZE}m -t ffs -o density=131072 \
126	    -o minfree=0 ${INITRD_FILE} ${BUILD_DIR}
127}
128
129make_hier() {
130	mkdir -p ${BUILD_DIR}/new_root ||
131	    error 1 "Failed to mkdir ${BUILD_DIR}/new_root"
132	# Symlink 'sbin' to 'bin'
133	ln -sf bin ${BUILD_DIR}/sbin
134	# Symlink 'tmp' to 'var/tmp', as '/var' will be mounted with
135	# tmpfs, saving a second tmpfs been mounted on '/tmp'.
136	ln -sf var/tmp ${BUILD_DIR}/tmp
137	for _dir in ${INITRD_DIRS}; do
138		[ ! -d "${BUILD_DIR}/${_dir}" ] &&
139		    mkdir -p ${BUILD_DIR}/${_dir}
140	done
141	# Symlink 'usr/bin' and 'usr/sbin' to 'bin'
142	ln -sf ../bin ${BUILD_DIR}/usr/bin
143	ln -sf ../bin ${BUILD_DIR}/usr/sbin
144	echo "Created directory structure"
145}
146
147copy_rescue() {
148	cpdup -o -u ${RESCUE_DIR}/ ${BUILD_DIR}/bin/ &&
149	    echo "Copied ${RESCUE_DIR} to ${BUILD_DIR}/bin" ||
150	    error 1 "Failed to copy ${RESCUE_DIR} to ${BUILD_DIR}/bin"
151}
152
153copy_content() {
154	for _dir in ${CONTENT_DIRS}; do
155		cpdup -o -u ${_dir}/ ${BUILD_DIR}/ &&
156		    echo "Copied ${_dir} to ${BUILD_DIR}" ||
157		    error 1 "Failed to copy ${dir} to ${BUILD_DIR}"
158	done
159}
160
161print_info() {
162	lt ${BUILD_DIR}
163}
164
165# Check the validity of the created initrd image before moving over.
166# This prevents creating an empty and broken initrd image by running
167# this tool but without ${CONTENT_DIRS} prepared.
168#
169# NOTE: Need more improvements.
170#
171check_initrd()
172{
173	[ -x "${BUILD_DIR}/sbin/oinit" ] &&
174	[ -x "${BUILD_DIR}/bin/sh" ] &&
175	[ -x "${BUILD_DIR}/etc/rc" ] || {
176		error 1 "Invalid initrd image!"
177	}
178}
179
180usage() {
181	error 2 \
182	    "usage: ${0##*/} [-b boot_dir] [-r rescue_dir]" \
183	    "[-s size] [-S max_size] -c <content_dirs>"
184}
185
186
187#
188# Main
189#
190
191while getopts :b:c:hr:s:S: opt; do
192	case ${opt} in
193	b)
194		BOOT_DIR="${OPTARG}"
195		;;
196	c)
197		CONTENT_DIRS="${OPTARG}"
198		;;
199	h)
200		usage
201		;;
202	r)
203		RESCUE_DIR="${OPTARG}"
204		;;
205	s)
206		INITRD_SIZE="${OPTARG}"
207		;;
208	S)
209		INITRD_SIZE_MAX="${OPTARG}"
210		;;
211	\?)
212		log "Invalid option -${OPTARG}"
213		usage
214		;;
215	:)
216		log "Option -${OPTARG} requires an argument"
217		usage
218		;;
219	esac
220done
221
222shift $((OPTIND - 1))
223[ $# -ne 0 ] && usage
224[ -z "${BOOT_DIR}" -o -z "${RESCUE_DIR}" -o -z "${CONTENT_DIRS}" ] && usage
225check_dirs ${BOOT_DIR} ${RESCUE_DIR} ${CONTENT_DIRS}
226
227INITRD_SIZE=${INITRD_SIZE%[mM]}  # MB
228INITRD_SIZE_MAX=${INITRD_SIZE_MAX%[mM]}  # MB
229
230BUILD_DIR=$(mktemp -d -t initrd) || error $? "Cannot create build directory"
231echo "Initrd build directory: ${BUILD_DIR}"
232INITRD_FILE="${BUILD_DIR}.img"
233INITRD_DEST="${BOOT_DIR}/kernel/initrd.img.gz"
234
235CSIZE=$(calc_initrd_size ${RESCUE_DIR} ${CONTENT_DIRS})
236echo "Required initrd image size: ${CSIZE} MB"
237if [ -n "${INITRD_SIZE}" -a "${INITRD_SIZE}" != "0" ]; then
238	if [ ${CSIZE} -gt ${INITRD_SIZE} ]; then
239		error 1 "Given initrd size (${INITRD_SIZE} MB) too small"
240	fi
241else
242	INITRD_SIZE=${CSIZE}
243fi
244echo "Initrd size: ${INITRD_SIZE} MB"
245
246if [ -n "${INITRD_SIZE_MAX}" -a "${INITRD_SIZE_MAX}" != "0" ] && \
247   [ ${INITRD_SIZE} -gt ${INITRD_SIZE_MAX} ]; then
248	error 1 "Exceeded the maximum size (${INITRD_SIZE_MAX} MB)"
249fi
250
251make_hier
252copy_rescue
253copy_content
254print_info
255make_img
256rm -rf ${BUILD_DIR}
257
258echo -n "Compressing ${INITRD_FILE} ..."
259gzip -9 ${INITRD_FILE}
260echo " OK"
261
262if [ -f "${INITRD_DEST}" ]; then
263	echo -n "Backing up ${INITRD_DEST} ..."
264	mv ${INITRD_DEST} ${INITRD_DEST}.old
265	echo " OK (${INITRD_DEST}.old)"
266fi
267
268echo -n "Installing ${INITRD_FILE}.gz to ${INITRD_DEST} ..."
269install -o root -g wheel -m 444 ${INITRD_FILE}.gz ${INITRD_DEST}
270echo " OK"
271rm -f ${INITRD_FILE}.gz
272