1#!/bin/sh
2#
3# SPDX-License-Identifier: GPL-2.0-only
4#
5
6### BEGIN INIT INFO
7# Provides:             volatile
8# Required-Start:       $local_fs
9# Required-Stop:      $local_fs
10# Default-Start:        S
11# Default-Stop:
12# Short-Description:  Populate the volatile filesystem
13### END INIT INFO
14
15# Get ROOT_DIR
16DIRNAME="$(dirname "$0")"
17ROOT_DIR="$(echo "$DIRNAME" | sed -ne 's:/etc/.*::p')"
18
19[ -e "${ROOT_DIR}/etc/default/rcS" ] && . "${ROOT_DIR}/etc/default/rcS"
20# When running populate-volatile.sh at rootfs time, disable cache.
21[ -n "$ROOT_DIR" ] && VOLATILE_ENABLE_CACHE=no
22# If rootfs is read-only, disable cache.
23[ "$ROOTFS_READ_ONLY" = "yes" ] && VOLATILE_ENABLE_CACHE=no
24
25CFGDIR="${ROOT_DIR}/etc/default/volatiles"
26TMPROOT="${ROOT_DIR}/var/volatile/tmp"
27COREDEF="00_core"
28
29[ "${VERBOSE}" != "no" ] && echo "Populating volatile Filesystems."
30
31create_file() {
32	EXEC=""
33	if [ -z "$2" ]; then
34		EXEC="
35		touch \"$1\";
36		"
37	else
38		EXEC="
39		cp \"$2\" \"$1\";
40		"
41	fi
42	EXEC="
43	${EXEC}
44	chown ${TUSER}:${TGROUP} $1 || echo \"Failed to set owner -${TUSER}- for -$1-.\";
45	chmod ${TMODE} $1 || echo \"Failed to set mode -${TMODE}- for -$1-.\" "
46
47	test "$VOLATILE_ENABLE_CACHE" = yes && echo "$EXEC" >> /etc/volatile.cache.build
48
49	if [ -e "$1" ]; then
50		[ "${VERBOSE}" != "no" ] && echo "Target already exists. Skipping."
51	else
52		if [ -z "$ROOT_DIR" ]; then
53			eval "$EXEC"
54		else
55			# Creating some files at rootfs time may fail and should fail,
56			# but these failures should not be logged to make sure the do_rootfs
57			# process doesn't fail. This does no harm, as this script will
58			# run on target to set up the correct files and directories.
59			eval "$EXEC" > /dev/null 2>&1
60		fi
61	fi
62}
63
64mk_dir() {
65	EXEC="
66	mkdir -p \"$1\";
67	chown ${TUSER}:${TGROUP} $1 || echo \"Failed to set owner -${TUSER}- for -$1-.\";
68	chmod ${TMODE} $1 || echo \"Failed to set mode -${TMODE}- for -$1-.\" "
69
70	test "$VOLATILE_ENABLE_CACHE" = yes && echo "$EXEC" >> /etc/volatile.cache.build
71	if [ -e "$1" ]; then
72		[ "${VERBOSE}" != "no" ] && echo "Target already exists. Skipping."
73	else
74		if [ -z "$ROOT_DIR" ]; then
75			eval "$EXEC"
76		else
77			# For the same reason with create_file(), failures should
78			# not be logged.
79			eval "$EXEC" > /dev/null 2>&1
80		fi
81	fi
82}
83
84link_file() {
85	EXEC="
86	if [ -L \"$2\" ]; then
87		[ \"\$(readlink \"$2\")\" != \"$1\" ] && { rm -f \"$2\"; ln -sf \"$1\" \"$2\"; };
88	elif [ -d \"$2\" ]; then
89		if awk '\$2 == \"$2\" {exit 1}' /proc/mounts; then
90			cp -a $2/* $1 2>/dev/null;
91			cp -a $2/.[!.]* $1 2>/dev/null;
92			rm -rf \"$2\";
93			ln -sf \"$1\" \"$2\";
94		fi
95	else
96		ln -sf \"$1\" \"$2\";
97	fi
98        "
99
100	test "$VOLATILE_ENABLE_CACHE" = yes && echo "	$EXEC" >> /etc/volatile.cache.build
101
102	if [ -z "$ROOT_DIR" ]; then
103		eval "$EXEC"
104	else
105		# For the same reason with create_file(), failures should
106		# not be logged.
107		eval "$EXEC" > /dev/null 2>&1
108	fi
109}
110
111check_requirements() {
112	cleanup() {
113		rm "${TMP_INTERMED}"
114		rm "${TMP_DEFINED}"
115		rm "${TMP_COMBINED}"
116	}
117
118	CFGFILE="$1"
119
120	TMP_INTERMED="${TMPROOT}/tmp.$$"
121	TMP_DEFINED="${TMPROOT}/tmpdefined.$$"
122	TMP_COMBINED="${TMPROOT}/tmpcombined.$$"
123
124	sed 's@\(^:\)*:.*@\1@' "${ROOT_DIR}/etc/passwd" | sort | uniq > "${TMP_DEFINED}"
125	grep -v "^#" "${CFGFILE}" | cut -s -d " " -f 2 > "${TMP_INTERMED}"
126	cat "${TMP_DEFINED}" "${TMP_INTERMED}" | sort | uniq > "${TMP_COMBINED}"
127	NR_DEFINED_USERS="$(wc -l < "${TMP_DEFINED}")"
128	NR_COMBINED_USERS="$(wc -l < "${TMP_COMBINED}")"
129
130	[ "${NR_DEFINED_USERS}" -ne "${NR_COMBINED_USERS}" ] && {
131		echo "Undefined users:"
132		diff "${TMP_DEFINED}" "${TMP_COMBINED}" | grep "^>"
133		cleanup
134		return 1
135	}
136
137
138	sed 's@\(^:\)*:.*@\1@' "${ROOT_DIR}/etc/group" | sort | uniq > "${TMP_DEFINED}"
139	grep -v "^#" "${CFGFILE}" | cut -s -d " " -f 3 > "${TMP_INTERMED}"
140	cat "${TMP_DEFINED}" "${TMP_INTERMED}" | sort | uniq > "${TMP_COMBINED}"
141
142	NR_DEFINED_GROUPS="$(wc -l < "${TMP_DEFINED}")"
143	NR_COMBINED_GROUPS="$(wc -l < "${TMP_COMBINED}")"
144
145	[ "${NR_DEFINED_GROUPS}" -ne "${NR_COMBINED_GROUPS}" ] && {
146		echo "Undefined groups:"
147		diff "${TMP_DEFINED}" "${TMP_COMBINED}" | grep "^>"
148		cleanup
149		return 1
150	}
151
152	# Add checks for required directories here
153
154	cleanup
155	return 0
156}
157
158apply_cfgfile() {
159	CFGFILE="$1"
160	SKIP_REQUIREMENTS="$2"
161
162	[ "${VERBOSE}" != "no" ] && echo "Applying ${CFGFILE}"
163
164	[ "${SKIP_REQUIREMENTS}" = "yes" ] || check_requirements "${CFGFILE}" || {
165		echo "Skipping ${CFGFILE}"
166		return 1
167	}
168
169	sed 's/#.*//' "${CFGFILE}" | \
170	while read -r TTYPE TUSER TGROUP TMODE TNAME TLTARGET; do
171		test -z "${TLTARGET}" && continue
172		TNAME=${ROOT_DIR}${TNAME}
173		[ "${VERBOSE}" != "no" ] && echo "Checking for -${TNAME}-."
174
175		[ "${TTYPE}" = "l" ] && {
176			TSOURCE="$TLTARGET"
177			[ "${VERBOSE}" != "no" ] && echo "Creating link -${TNAME}- pointing to -${TSOURCE}-."
178			link_file "${TSOURCE}" "${TNAME}"
179			continue
180		}
181
182		[ "${TTYPE}" = "b" ] && {
183			TSOURCE="$TLTARGET"
184			[ "${VERBOSE}" != "no" ] && echo "Creating mount-bind -${TNAME}- from -${TSOURCE}-."
185			mount --bind "${TSOURCE}" "${TNAME}"
186			EXEC="
187	mount --bind \"${TSOURCE}\" \"${TNAME}\""
188			test "$VOLATILE_ENABLE_CACHE" = yes && echo "$EXEC" >> /etc/volatile.cache.build
189			continue
190		}
191
192		[ -L "${TNAME}" ] && {
193			[ "${VERBOSE}" != "no" ] && echo "Found link."
194			NEWNAME=$(ls -l "${TNAME}" | sed -e 's/^.*-> \(.*\)$/\1/')
195			if echo "${NEWNAME}" | grep -v "^/" >/dev/null; then
196				TNAME="$(echo "${TNAME}" | sed -e 's@\(.*\)/.*@\1@')/${NEWNAME}"
197				[ "${VERBOSE}" != "no" ] && echo "Converted relative linktarget to absolute path -${TNAME}-."
198			else
199				TNAME="${NEWNAME}"
200				[ "${VERBOSE}" != "no" ] && echo "Using absolute link target -${TNAME}-."
201			fi
202		}
203
204		case "${TTYPE}" in
205			"f")  [ "${VERBOSE}" != "no" ] && echo "Creating file -${TNAME}-."
206				TSOURCE="$TLTARGET"
207				[ "${TSOURCE}" = "none" ] && TSOURCE=""
208				create_file "${TNAME}" "${TSOURCE}"
209				;;
210			"d")  [ "${VERBOSE}" != "no" ] && echo "Creating directory -${TNAME}-."
211				mk_dir "${TNAME}"
212				# Add check to see if there's an entry in fstab to mount.
213				;;
214			*)    [ "${VERBOSE}" != "no" ] && echo "Invalid type -${TTYPE}-."
215				continue
216				;;
217		esac
218	done
219	return 0
220}
221
222clearcache=0
223exec 9</proc/cmdline
224while read -r line <&9
225do
226	case "$line" in
227		*clearcache*)  clearcache=1
228			       ;;
229		*)	       continue
230			       ;;
231	esac
232done
233exec 9>&-
234
235if test -e "${ROOT_DIR}/etc/volatile.cache" -a "$VOLATILE_ENABLE_CACHE" = "yes" -a "x$1" != "xupdate" -a "x$clearcache" = "x0"
236then
237	sh "${ROOT_DIR}/etc/volatile.cache"
238else
239	rm -f "${ROOT_DIR}/etc/volatile.cache" "${ROOT_DIR}/etc/volatile.cache.build"
240
241	# Apply the core file with out checking requirements. ${TMPROOT} is
242	# needed by check_requirements but is setup by this file, so it must be
243	# processed first and without being checked.
244	[ -e "${CFGDIR}/${COREDEF}" ] && apply_cfgfile "${CFGDIR}/${COREDEF}" "yes"
245
246	# Fast path: check_requirements is slow and most of the time doesn't
247	# find any problems. If there are a lot of config files, it is much
248	# faster to to concatenate them all together and process them once to
249	# avoid the overhead of calling check_requirements repeatedly
250	TMP_FILE="${TMPROOT}/tmp_volatile.$$"
251	rm -f "$TMP_FILE"
252
253	CFGFILES="$(ls -1 "${CFGDIR}" | grep -v "^${COREDEF}\$" | sort)"
254	for file in ${CFGFILES}; do
255		cat "${CFGDIR}/${file}" >> "$TMP_FILE"
256	done
257
258	if check_requirements "$TMP_FILE"
259	then
260		apply_cfgfile "$TMP_FILE" "yes"
261	else
262		# Slow path: One or more config files failed requirements.
263		# Process each one individually so the offending one can be
264		# skipped
265		for file in ${CFGFILES}; do
266			apply_cfgfile "${CFGDIR}/${file}"
267		done
268	fi
269	rm "$TMP_FILE"
270
271	[ -e "${ROOT_DIR}/etc/volatile.cache.build" ] && sync && mv "${ROOT_DIR}/etc/volatile.cache.build" "${ROOT_DIR}/etc/volatile.cache"
272fi
273
274if [ -z "${ROOT_DIR}" ] && [ -f /etc/ld.so.cache ] && [ ! -f /var/run/ld.so.cache ]
275then
276	ln -s /etc/ld.so.cache /var/run/ld.so.cache
277fi
278