xref: /dragonfly/initrd/mkinitrd.sh (revision dcb5d66b)
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