You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
111 lines
3.4 KiB
111 lines
3.4 KiB
#!/bin/sh |
|
# Copyright (c) Victor Lowther <victor.lowther@gmail.com> |
|
# Licensed under the terms of the GNU GPL v2 or later. |
|
|
|
# some utility functions first |
|
# this is so we can scroll back. |
|
|
|
die() { echo "${1}, dying horribly."; while :;do read line; done } |
|
|
|
# jsut enough to get the job done |
|
simple_find() { |
|
# $1 = file to look for |
|
# $rest = places to look for it |
|
local file=$1 |
|
shift |
|
for loc in "$@"; do |
|
[ -f "$NEWROOT$loc/$file" ] && { echo "$loc/$file"; return 0; } |
|
done |
|
return 1 |
|
} |
|
|
|
# We really should not be doing this from here, but... |
|
find_interp() { |
|
local ldso=$("$NEWROOT$CHROOT" "$NEWROOT" "$LDD" "$1" | |
|
while read interp rest; do |
|
[ -f "${NEWROOT}$interp" ] || continue |
|
echo "$interp" |
|
break |
|
done); |
|
[ "$ldso" ] && echo $ldso |
|
} |
|
|
|
# this makes it easier to run a command entirely from newroot |
|
# $1 = elf interpreter (must pass empty string if none) |
|
# $2 = command or "exec" |
|
# $3 = command if exec was passed |
|
run_from_newroot() { |
|
local ldso="$1" cmd="$2"; shift; shift |
|
if [ "$cmd" = "exec" ]; then |
|
cmd="$1"; shift |
|
if [ "$ldso" ]; then |
|
exec "$NEWROOT$ldso" --library-path "$LIBPATH" "$NEWROOT$cmd" "$@" |
|
else |
|
exec "$NEWROOT$cmd" "$@" |
|
fi |
|
else |
|
if [ "$ldso" ]; then |
|
"$NEWROOT$ldso" --library-path "$LIBPATH" "$NEWROOT$cmd" "$@" |
|
else |
|
"$NEWROOT$cmd" "$@" |
|
fi |
|
fi |
|
} |
|
# update the path to find our dynamic libraries on newroot |
|
update_newroot_libpath() { |
|
local x |
|
LIBPATH=":" |
|
LIBDIRS="$(echo $NEWROOT/lib* $NEWROOT/usr/lib*)" |
|
for x in $LIBDIRS; do |
|
[ -d "$x" ] && LIBPATH="${LIBPATH}${x}:" |
|
done |
|
LIBPATH="${LIBPATH%:}"; LIBPATH="${LIBPATH#:}" |
|
[ "$LIBPATH" ] || die "Cannot find shared library diectories on $NEWROOT" |
|
} |
|
NEWROOT="$1" |
|
INIT="$2" |
|
[ -d "$NEWROOT" ] || die "$NEWROOT is not a directory" |
|
[ -x "$NEWROOT$INIT" ] || die "$NEWROOT/$INIT is not executable." |
|
shift; shift |
|
|
|
update_newroot_libpath |
|
|
|
# start looking for required binaries and bits of infrastructure |
|
BINDIRS="/bin /sbin /usr/bin /usr/sbin" |
|
RM=$(simple_find rm $BINDIRS) || die "Cannnot find rm on $NEWROOT" |
|
CHROOT=$(simple_find chroot $BINDIRS) || die "Cannot find chroot on $NEWROOT" |
|
LDD=$(simple_find ldd $BINDIRS) || die "Cannot find ldd on $NEWROOT" |
|
MOUNT=$(simple_find mount $BINDIRS) || die "Cannot find mount on $NEWROOT" |
|
|
|
# now, start the real process of switching the root |
|
cd / |
|
|
|
# kill udevd, move all our mounts over to the new root |
|
kill $(pidof udevd) |
|
mount --move /proc $NEWROOT/proc |
|
mount --move /sys $NEWROOT/sys |
|
mount --move /dev $NEWROOT/dev |
|
|
|
# Find the binary interpreter for our three required binaries. |
|
# We do it here so that ldd does not complain about a missing /dev/null. |
|
CHROOT_LDSO=$(find_interp "$CHROOT") |
|
RM_LDSO=$(find_interp "$RM") |
|
MOUNT_LDSO=$(find_interp "$MOUNT") |
|
|
|
# redirect to new console. Our old initramfs will not be freed otherwise |
|
CONSOLE=$NEWROOT/dev/console |
|
[ -c $CONSOLE ] && exec >$CONSOLE 2>&1 <$CONSOLE |
|
for x in *; do |
|
[ "/$x" = "$NEWROOT" ] || run_from_newroot "$RM_LDSO" "$RM" -rf -- "$x" |
|
done |
|
# switch to our new root dir |
|
cd "$NEWROOT" |
|
# this moves rootfs to the actual root... |
|
run_from_newroot "$MOUNT_LDSO" "$MOUNT" -n --move . / |
|
# but does not update where / is in directory lookups. |
|
# Therefore, newroot is now ".". Update things accordingly, then chroot and |
|
# exec init. |
|
NEWROOT="." |
|
update_newroot_libpath |
|
run_from_newroot "$CHROOT_LDSO" exec "$CHROOT" "$NEWROOT" "$INIT" "$@" || \ |
|
die "The chroot did not take for some reason"
|
|
|