1#!/bin/sh -e
2#-
3# Copyright (c) 2012-2013 SRI International
4# Copyright (c) 2012 Robert N. M. Watson
5# All rights reserved.
6#
7# This software was developed by SRI International and the University of
8# Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237)
9# ("CTSRD"), as part of the DARPA CRASH research programme.
10#
11# Redistribution and use in source and binary forms, with or without
12# modification, are permitted provided that the following conditions
13# are met:
14# 1. Redistributions of source code must retain the above copyright
15#    notice, this list of conditions and the following disclaimer.
16# 2. Redistributions in binary form must reproduce the above copyright
17#    notice, this list of conditions and the following disclaimer in the
18#    documentation and/or other materials provided with the distribution.
19#
20# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30# SUCH DAMAGE.
31#
32# $FreeBSD$
33
34usage()
35{
36	cat <<EOF 1>&2
37usage: makeroot.sh [-B byte-order] [-d] [-e <extras manifest>] [-f <filelist>]
38                   [-k <keydir> [-K <user>]]
39                   [-p <master.passwd> [-g <groupfile>]] [-s <size>]
40		   <image> <bsdroot>
41EOF
42	exit 1
43}
44
45warn()
46{
47	echo `basename $0` "$@" 1>&2
48}
49
50err()
51{
52	ret=$1
53	shift
54	warn "$@"
55	exit $ret
56}
57
58atexit()
59{
60	if [ -z "${DEBUG}" ]; then
61		rm -rf ${tmpdir}
62	else
63		warn "temp directory left at ${tmpdir}"
64	fi
65}
66
67DEBUG=
68# Allow duplicate manifest entries when not file list is given because the
69# FreeBSD METALOG still includes it.
70DUPFLAG=-D
71EXTRAS=
72FILELIST=
73GROUP=
74KEYDIR=
75KEYUSERS=
76PASSWD=
77
78while getopts "B:de:f:g:K:k:l:p:s:" opt; do
79	case "$opt" in
80	B)	BFLAG="-B ${OPTARG}" ;;
81	d)	DEBUG=1 ;;
82	e)	EXTRAS="${EXTRAS} ${OPTARG}" ;;
83	f)	FILELIST="${OPTARG}";;
84	g)	GROUP="${OPTARG}" ;;
85	K)	KEYUSERS="${KEYUSERS} ${OPTARG}" ;;
86	k)	KEYDIR="${OPTARG}" ;;
87	l)	LABEL="${OPTARG}" ;;
88	p)	PASSWD="${OPTARG}" ;;
89	s)	SIZE="${OPTARG}" ;;
90	*)	usage ;;
91	esac
92done
93shift $(($OPTIND - 1))
94
95if [ $# -ne 2 ]; then
96	usage;
97fi
98
99IMGFILE=$(realpath $(dirname $1))/$(basename $1)
100BSDROOT=$2
101
102DBDIR=${BSDROOT}/etc
103
104if [ ! -r ${BSDROOT}/METALOG ]; then
105	err 1 "${BSDROOT} does not contain a METALOG"
106fi
107
108if [ -n "${GROUP}" -a -z "${PASSWD}" ]; then
109	warn "-g requires -p"
110	usage
111fi
112
113if [ -n "${KEYUSERS}" -a -z "${KEYDIR}" ]; then
114	warn "-K requires -k"
115	usage
116fi
117if [ -n "${KEYDIR}" -a -z "${KEYUSERS}" ]; then
118	KEYUSERS=root
119fi
120
121tmpdir=`mktemp -d /tmp/makeroot.XXXXX`
122if [ -z "${tmpdir}" -o ! -d "${tmpdir}" ]; then
123	err 1 "failed to create tmpdir"
124fi
125trap atexit EXIT
126
127manifest=${tmpdir}/manifest
128
129echo "#mtree 2.0" > ${manifest}
130
131if [ -n "${PASSWD}" ]; then
132	cp ${PASSWD} ${tmpdir}/master.passwd
133	pwd_mkdb -d ${tmpdir} -p ${tmpdir}/master.passwd
134	if [ -z "${GROUP}" ]; then
135		cp ${DBDIR}/group ${tmpdir}
136	else
137		cp ${GROUP} ${tmpdir}
138	fi
139
140	cat <<EOF >> ${tmpdir}/passwd.mtree
141./etc/group type=file uname=root gname=wheel mode=0644 contents=${tmpdir}/group
142./etc/master.passwd type=file uname=root gname=wheel mode=0600 contents=${tmpdir}/master.passwd
143./etc/passwd type=file mode=0644 uname=root gname=wheel contents=${tmpdir}/passwd
144./etc/pwd.db type=file mode=0644 uname=root gname=wheel contents=${tmpdir}/pwd.db
145./etc/spwd.db type=file mode=0600 uname=root gname=wheel contents=${tmpdir}/spwd.db
146EOF
147	EXTRAS="${EXTRAS} ${tmpdir}/passwd.mtree"
148
149	DBDIR=${tmpdir}
150fi
151
152if [ -n "${FILELIST}" ]; then
153	# build manifest from root manifest and FILELIST
154	(echo .; grep -v ^# ${FILELIST} | while read path; do
155		# Print each included path and all its sub-paths with a ./
156		# prepended.  The "sort -u" will then discard all the
157		# duplicate directory entries.  This ensures that we
158		# extract the permissions for each unlisted directory
159		# from the METALOG.
160		path="/${path}"
161		while [ -n "${path}" ]; do
162			echo ".${path}"
163			path="${path%/*}"
164		done
165	done) | sort -u ${BSDROOT}/METALOG - | \
166	    awk '
167		!/ type=/ { file = $1 }
168		/ type=/ { if ($1 == file) {print} }' >> ${manifest}
169elif [ -n "${EXTRAS}" ]; then
170	# Start with all the files in BSDROOT/METALOG except those in
171	# one of the EXTRAS manifests.
172	grep -h type=file ${EXTRAS} | cut -d' ' -f1 | \
173	    sort -u ${BSDROOT}/METALOG - | awk '
174		!/ type=/ { file = $1 }
175		/ type=/ { if ($1 != file) {print} }' >> ${manifest}
176else
177	sort -u ${BSDROOT}/METALOG >> ${manifest}
178fi
179
180# For each extras file, add contents keys relative to the directory the
181# manifest lives in for each file line that does not have one.  Adjust
182# contents keys relative to ./ to be relative to the same directory.
183for eman in ${EXTRAS}; do
184	if [ ! -f ${eman} ]; then
185		err 1 "${eman} is not a regular file"
186	fi
187	extradir=`realpath ${eman}`; extradir=`dirname ${extradir}`
188
189	awk '{
190		if ($0 !~ /type=file/) {
191			print
192		} else {
193			if ($0 !~ /contents=/) {
194				printf ("%s contents=%s\n", $0, $1)
195			} else {
196				print
197			}
198		}
199	}' ${eman} | \
200	    sed -e "s|contents=\./|contents=${extradir}/|" >> ${manifest}
201done
202
203# /etc/rcorder.start allows the startup order to be stable even if
204# not all startup scripts are installed.  In theory it should be
205# unnecessary, but dependencies in rc.d appear to be under recorded.
206# This is a hack local to beri/cheribsd.
207#
208echo /etc/rc.d/FIRST > ${tmpdir}/rcorder.start
209rcorder -s nostart ${BSDROOT}/etc/rc.d/* | sed -e "s:^${BSDROOT}::" | \
210     grep -v LAST | grep -v FIRST >> \
211    ${tmpdir}/rcorder.start
212echo /etc/rc.d/LAST >> ${tmpdir}/rcorder.start
213echo "./etc/rcorder.start type=file mode=644 uname=root gname=wheel" \
214   "contents=${tmpdir}/rcorder.start" >> ${manifest}
215
216# Add all public keys in KEYDIR to roots' authorized_keys file.
217if [ -n "${KEYDIR}" ]; then
218	cat ${KEYDIR}/*.pub > ${tmpdir}/authorized_keys
219	if [ ! -s ${tmpdir}/authorized_keys ]; then
220		err 1 "no keys found in ${KEYDIR}"
221	fi
222	for user in ${KEYUSERS}; do
223		userdir=`awk -F: "{if (\\\$1 == \"${user}\") {print \\\$9; exit} }" ${DBDIR}/master.passwd`
224		gid=`awk -F: "{if (\\\$1 == \"${user}\") {print \\\$4; exit} }" ${DBDIR}/master.passwd`
225		group=`awk -F: "{if (\\\$3 == \"${gid}\") {print \\\$1; exit} }" ${DBDIR}/group`
226		if [ -z "${userdir}" ]; then
227			err 1 "${user}: not found in ${DBDIR}/master.passwd"
228		fi
229		echo ".${userdir}/.ssh type=dir mode=700 uname=${user} gname=${group}" >> ${manifest}
230		echo ".${userdir}/.ssh/authorized_keys type=file mode=600 uname=${user} gname=${group} contents=${tmpdir}/authorized_keys" >> ${manifest}
231	done
232fi
233
234if [ -n "${LABEL}" ]; then
235LABELFLAG="-o label=${LABEL}"
236fi
237if [ -n "${SIZE}" ]; then
238SIZEFLAG="-s ${SIZE}"
239fi
240
241cd ${BSDROOT}; makefs ${DUPFLAG} -N ${DBDIR} ${SIZEFLAG} ${BFLAG} \
242     -t ffs ${LABELFLAG} -f 256 ${IMGFILE} ${manifest}
243