blob: 27998389d69be98c1a411f8271f4c33647d48604 (
plain)
1 #!/usr/bin/env bash
2 # Mkinitramfs creates a basic init ram fs with encryption support.
3 # Copyright (C) 2023 Aaron Ball <nullspoon@oper.io>
4 #
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation, either version 3 of the License, or
8 # (at your option) any later version.
9 #
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
14 #
15 # You should have received a copy of the GNU General Public License
16 # along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18
19 # Hash of fully-qualified binaries
20 declare -a fqbins
21
22 #
23 # Searches PATH for all binaries listed as function arguments. Each found
24 # binary is then put into the global "bins" array.
25 #
26 # This function also serves to ensure all specified binaries are present and
27 # in PATH. It will exit code 1 with an error message if any binaries are not
28 # found.
29 #
30 # Note: To use this function, insert "declare -a fqbins" at top of script).
31 #
32 # @param bins All function arguments are the names of binaries to locate.
33 #
34 resolve_bins() {
35 local args=${@}
36 local path=''
37
38 for i in ${args[@]}; do
39 if ! path="$(type -p ${i} 2>/dev/null)"; then
40 printf 'Could not find binary %s. Is it installed?\n' "${i}"
41 exit 1
42 fi
43
44 printf 'Located %s at %s\n' "${i}" "${path}"
45 fqbins+=("${path}")
46 done
47 }
48
49
50 #
51 # Gets first path locatable in a string
52 #
53 get_first_path() {
54 local str="${1:-}"
55 local out=''
56
57 # Find the first occurrence of `/`
58 for ((i=0; i<${#str}; i++)); do
59 [ "${str:$i:1}" = '/' ] && break
60 done
61
62 # Exit if no path found
63 [ "${i}" -eq "${#str}" ] && return 2
64
65 # Find end of string by looking for space
66 while [ "${str:$i:1}" != ' ' ] && [ "${i}" -lt "${#str}" ]; do
67 out="${out}${str:$i:1}"
68 (( i += 1 ))
69 done
70 printf '%s\n' "${out}"
71 }
72
73
74 get_deps() {
75 local bin="${1:-}"
76 for dep in $(ldd ${bin}); do
77 get_first_path "${dep}"
78 done
79 }
80
81
82 #
83 # Creates cpio file for specified initrd cache directory.
84 # Outputs to /boot/initrd-${version}.
85 #
86 # @param cache Path to the uncompressed source directory to be archived
87 # @param version Version of initrd archive
88 #
89 mkcpio() {
90 [ -z "${1:-}" ] && printf 'Initramfs cache path required.\n' && exit 1
91 [ -z "${2:-}" ] && printf 'Kernel version required.\n' && exit 1
92
93 local cache="${1}"
94 local version="${2}"
95
96 cd "${cache}"
97 fspath="/boot/initrd-${version}"
98
99 # Notify user of initrd overwriting
100 [ -f "${fspath}" ] && printf "\n\n%s exists. Overwriting.\n" "${fspath}"
101
102 # Create the initrd
103 printf 'Building initrd to %s\n' "${fspath}"
104 find . -print0 | cpio --null -o --format=newc | xz -C crc32 -9 -c > "${fspath}"
105 }
106
107
108 #
109 # Sets up cache dir for later writing to an archive (via cpio).
110 # Performs simple operations like creating a basic directory structure,
111 #
112 # @param cache Path to the cache directory. Will be created if not exists
113 #
114 cache_dir_setup() {
115 [ -z "${1}" ] && printf 'Please specify a cache dir.\n' && exit 1
116 local cache="${1:-}"
117
118 # Clean the cache so we start fresh
119 rm -r "${cache:?}" && mkdir "${cache}"
120
121 # Create the temporary directory structure
122 for i in bin dev etc lib lib32 lib64 mnt/root proc root run sbin sys usr/share; do
123 mkdir -p "${cache}/${i}"
124 done
125
126 # Copy in terminals database for `l` (linux - stock tty)
127 mkdir -p "${cache}/usr/share/terminfo"
128 cp -r /usr/share/terminfo/l "${cache}/usr/share/terminfo/l"
129 }
130
131
132 #
133 # Checks installed kernel modules for the specified version to see if the
134 # corresponding kernel has encryption support statically compiled in.
135 #
136 # @param version Version of kernel modules to check
137 #
138 check_crypto_support() {
139 [ -z "${1:-}" ] && printf 'Kernel version required.\n' && exit 1
140 local version="${1}"
141 local buf=''
142 local -a copy=(
143 arch/x86/crypto
144 crypto
145 drivers/input/{keyboard,serio,*.ko}
146 drivers/{crypto,usb,platform,md}
147 lib
148 fs/{btrfs,xfs,ext4}
149 )
150
151 mkdir -p "${cache}/lib/modules/${version}"
152 cp -v /lib/modules/${version}/modules*.bin "${cache}/lib/modules/${version}/"
153
154 # Copy in all the rewuired kernel modules
155 for i in ${copy[@]}; do
156 buf=(/lib/modules/${version}/kernel/${i})
157 # For globbing in the array
158 for ent in ${buf[@]}; do
159 printf 'Copying in %s\n' "${ent}"
160 mkdir -p "$(dirname ${cache}/${ent})"
161 cp -r "${ent}" "${cache}/${ent}"
162 done
163 done
164 }
165
166
167 #
168 # Checks to see if kernel modules for the specified version have been
169 # installed.
170 #
171 # @param version Version to check for installation status
172 #
173 check_kernel_version() {
174 [ -z ${1} ] && printf 'Kernel version required.\n' && exit 1
175 local version="${1}"
176 local modulespath="/lib/modules/${version}"
177
178 if [ ! -d ${modulespath} ]; then
179 printf 'Error: Could not find %s.\n' "${modulespath}"
180 printf 'Has kernel version %s been comiled?' "${version}"
181 exit 1
182 fi
183 }
184
185
186 main() {
187 [ -z "${1:-}" ] && printf 'Please specify a kernel version\n' && exit 1
188
189 local version=${1}
190
191 check_kernel_version ${version}
192
193 local cache='/tmp/initrd'
194 local res_path=/usr/share/mkinitramfs/
195
196 # Ensure init script has no syntax errors (linting process)
197 if ! bash -n ${res_path}/init; then
198 printf '\n\nERROR: Syntax errors were found in the init script.\n'
199 printf 'Please resolve the above issues and try again.\n'
200 printf 'Exiting to avoid creating an unbootable initramfs.\n\n'
201 return 1
202 fi
203
204 # List of binaries to exist in the new initramfs
205 # Standard shell binaries
206 resolve_bins bash pidof ps ls grep less switch_root kill
207 # Module management
208 resolve_bins udevadm udevd modprobe rmmod
209 # For encrypted and block device support
210 resolve_bins partprobe dmsetup mount umount cryptsetup
211
212 # For debugging
213 #resolve_bins sleep mkdir chmod chown tr clear cat
214
215 # Set up the archive source dir.
216 cache_dir_setup ${cache}
217
218 # Ensure static crypto support exists in kernel
219 check_crypto_support ${version}
220
221 # Copy in libgcc_s for newer cryptsetup
222 install -v -D /usr/lib/libgcc_s.so "${cache}/usr/lib/libgcc_s.so"
223 install -v -D /usr/lib/libgcc_s.so.1 "${cache}/usr/lib/libgcc_s.so.1"
224
225 # Install udev rules for block devices
226 mkdir -p "${cache}/lib/udev"
227 cp -rv /lib/udev/rules.d "${cache}/lib/udev/rules.d"
228
229 # Copy binary and dependencies to cache dir
230 for bin in ${fqbins[@]}; do
231 # Copy the binary of interest
232 install -v -D "${bin}" "${cache}/${bin}"
233
234 local deps=$(get_deps ${bin})
235 # Copy each of the binary's deps
236 for dep in ${deps[@]}; do
237 install -v -D ${dep} "${cache}/${dep}"
238 done
239 done
240
241 # Copy in message file
242 [ -f "/etc/msg" ] && install -v -D "/etc/msg" "${cache}/etc/msg"
243 # Copy in init script and add execute
244 install -v -D -m 755 "${res_path}/init" "${cache}/init"
245 # Copy in the system shell profile
246 install -v -D "/etc/profile" "${cache}/etc/profile"
247
248 # Create archive image
249 mkcpio ${cache} ${version}
250 }
251
252 main ${@}
|