#!/bin/sh # turnup # See the help block at the end for documentation. # . /etc/default/functions # # force: override certain checks force= # # fstype new # The type of the file system mounted on "new" Outputs the last # piece of information found, which should be the one for the # currently visible mount! fstype() { local cwd dev mp type options pass freq result cwd="$(cd "$1"; /bin/pwd)" result= while read dev mp type options pass freq do case "$mp" in "$cwd") result="$type";; esac done # Make sure the candidate rootfs is empty check_rootfs() { local fcount case "$1" in -i) shift case "$force" in -f) return 0;; esac fcount="$(find "$1" ! -type d -print | wc -l)" test "$fcount" -eq 0 && return 0 echo "turnup: $1: partition contains existing files, specify -f to overwrite" >&2 return 1;; *) checkmount "$1" && return 0 echo "turnup: $1: partition does not seem to be a valid root partition" >&2 if test -f "$1"/.recovery then echo " $1/.recovery exists: fix the partition then remove it" >&2 fi return 1;; esac } # # copy_rootfs old new # Make a copy of the given root file system, copying only the # directories needed. The root must be the flash file system copy_rootfs() { local old new old="$1" new="$2" test -d "$old" -a -d "$new" || { echo "turnup: rootfs: copy $old $new: not a directory" >&2 return 1 } # # There are no problem file names in the flash file system, so # it is possible to use -print, not -print0. The following # files and directories are not copied: # # /dev/* # /boot, /boot/* # /linuxrc* # /var/* echo "turnup: copying root file system" >&2 ( cd "$1" find . -mount -print | sed '\@^./dev/@d;\@^./boot/@d;\@^./boot$@d;\@^./linuxrc@d;\@^./var/@d' | cpio -p -d -m -u "$2" ) || { echo "turnup: rootfs: cpio $old $new failed" >&2 return 1 } echo "done" >&2 } # # setup_dev new device_table # Populates the /dev directory, removes the ramfs mount of /dev by # removing the links to the devices startup file. setup_dev() { test -n "$1" -a -d "$1"/dev -a -r "$2" || { echo "turnup: setup_dev($1,$2): expected a directory and a file" >&2 return 1 } # at present the device_table is not used because it is incomplete, # /etc/init.d/devices adds extra stuff. So this script copies the # existing /dev. FIXME. echo "turnup: copying dev file system" >&2 ( cd / find dev -print0 | cpio -p -0 -d -m -u "$1" ) || { echo "turnup: rootfs: cpio /dev $1 failed" >&2 return 1 } echo "done" >&2 # now prevent this being done each time :>"$1"/dev/.permanent return 0 } # # setup_bootdev new device_table # As above but actually uses the supplied device table - this is possible if # the table is just used for boot because the extra setup is not required. setup_bootdev() { test -n "$1" -a -d "$1"/dev -a -r "$2" || { echo "turnup: setup_bootdev($1,$2): expected a directory and a file" >&2 return 1 } # NOTE: this fails silently with 0 return code(!) when a directory # does not exist yet things are created within it. makedevs -r "$1" -D "$2" } # # setup_var new type # Populates /var. Should only be called for true disk systems and NFS - # it's a waste on USB memory sticks! Removes the /var tmpfs setting # for disks, sets it to /var/tmp for NFS. setup_var() { test -n "$1" -a -d "$1"/var || { echo "turnup: setup_var($1,$2): expected a directory" >&2 return 1 } case "$2" in disk|nfs);; *) echo "turnup: setup_var($1,$2): expected 'disk' or 'nfs'" >&2 return 1;; esac # # populate /var, there is a shell script to do this, but it uses # absolute path names chroot "$1" /bin/busybox sh /etc/init.d/populate-var.sh || { echo "turnup: /var: could not populate directory" >&2 return 1 } # the startup link is left for the moment, this seems safer #rm "$1"/etc/rc?.d/[KS]??populate-var.sh # remove the /var tmpfs entry from the new /etc/fstab case "$2" in disk) sed -i '\@\s/var\s\s*tmpfs\s@d' "$1"/etc/fstab echo "turnup: tmpfs will no longer be mounted on /var" >&2;; nfs) sed -i '\@\s/var\s\s*tmpfs\s@s@\s/var@&/tmp@' "$1"/etc/fstab echo "turnup: tmpfs /var mount moved to /var/tmp" >&2;; esac # # Previous versions of turnup removed populate-var.sh from the # startup links, this one doesn't, so /var can be made back into # a tmpfs just by a change to /etc/fstab. return 0 } # # setup_files new # At present just puts the rmrecovery links in. setup_files() { test -n "$1" -a -d "$1"/etc || { echo "turnup: setup_files($1): expected a directory" >&2 return 1 } # # add the script to remove .recovery update-rc.d -r "$1" rmrecovery start 99 1 2 3 4 5 . return 0 } # # setup_syslog new # Moves the syslog to a file - appropriate for disk and nfs types, not # otherwise. setup_syslog() { test -n "$1" -a -d "$1"/etc || { echo "turnup: setup_syslog($1): expected a directory" >&2 return 1 } # # if the syslog is to the buffer redirect it to a file if egrep -q '^DESTINATION="buffer"' "$1"/etc/syslog.conf then if cp "$1"/etc/syslog.conf "$1"/etc/syslog.conf.sav then # the busybox syslog will fail with ROTATESIZE and ROTATEGENS sed -i 's!DESTINATION="buffer"!DESTINATION="file"! /^ROTATESIZE=/d /^ROTATEGENS=/d' "$1"/etc/syslog.conf echo "turnup: /etc/syslog.conf: changed to file buffering" >&2 echo " Old (buffer) version in /etc/syslog.conf.sav" >&2 echo " Log messages will be in /var/log/messages" >&2 else echo "turnup: /etc/syslog.conf: failed to make a copy" >&2 echo " syslog will log to a buffer" >&2 fi fi return 0 } # # setup_rootfs type new device_table # Populates the /dev and /var directories, alters the startup to # not mount or populate them further. Does the right thing according # to the given $type setup_rootfs() { local type new table type="$1" new="$2" table="$3" test -n "$new" -a -d "$new" -a -f "$table" || { echo "turnup: setup_rootfs($type,$new,$table): expected a directory and a file" >&2 return 1 } case "$type" in flash) return 0;; disk) setup_dev "$new" "$table" && setup_var "$new" disk && setup_files "$new" && setup_syslog "$new";; memstick) setup_bootdev "$new" "$table" && setup_files "$new";; nfs) setup_dev "$new" "$table" && setup_var "$new" nfs && setup_files "$new" && setup_syslog "$new";; *) echo "turnup: setup_rootfs: $type: unknown rootfs type" >&2 return 1;; esac # return code of last setup function } # # setup_fstab new fsdev fstype fsoptions # Alters the /etc/fstab entry for / to refer to the correct device and # have the correct type and options. Essential for checkroot to remount # / with the correct options. # bad, since sed won't fail even if it changes nothing. setup_fstab() { sed -i '\@^[^ ]*\s\s*/\s@s@^.*$@'"$2 / $3 $4 1 1"'@' "$1"/etc/fstab egrep -q "^$2 / $3 $4 1 1\$" "$1"/etc/fstab || { echo "turnup: /etc/fstab: root(/) entry not changed" >&2 echo " you probably need to check the options in /etc/fstab" >&2 echo " to ensure that the root partition is mounted correctly" >&2 return 1 } } # # boot_rootfs [options] # Change the flash partition (not the current root!) to boot off # the new root file system boot_rootfs() { local type ffs sleep device opt type="$1" ffs="$2" sleep="$3" device="$4" # test this first as the test does not depend on the correctness # of the other arguments test -n "$ffs" -a -d "$ffs" || { echo "turnup: boot_rootfs($type, $ffs, $device): expected directory" >&2 return 1 } test -x "$ffs"/boot/"$type" || { echo "turnup: boot_rootfs($type, $ffs, $device): invalid boot type $type" >&2 return 1 } shift shift case "$type" in disk) test -n "$device" -a -b "$device" || { echo "turnup: boot_rootfs($ffs, $type, $device): expected block device" >&2 return 1 } shift 2;; nfs) shift 2;; flash) ;; ram) ;; *) echo "turnup: boot_rootfs($type, $ffs, $device): unknown type" >&2 return 1;; esac # # The /linuxrc records the correct options to mount the device, # since we have already mounted if correctly with these options # we can be sure (maybe) that the boot will work. If not /boot/disk # falls back to flash. # # This modifies the boot process, until this point no harm has been # done to the system, but at this point the boot rootfs will change rm -f "$ffs"/linuxrc.new || { echo "turnup: boot_rootfs: failed to remove $ffs/linuxrc.new" >&2 return 1 } case "$type" in flash) ln -s "boot/flash" "$ffs"/linuxrc.new || { echo "turnup: boot_rootfs: failed to create $ffs/linuxrc.new" >&2 return 1 };; ram) { echo '#!/bin/sh' echo 'rm -f /linuxrc.new' echo 'ln -s boot/flash /linuxrc.new' echo 'mv /linuxrc.new /linuxrc' echo 'exec /boot/ram /dev/ram0' echo 'exec /boot/flash' } >"$ffs"/linuxrc.new && chmod 744 "$ffs"/linuxrc.new || { echo "turnup: boot_rootfs: failed to write $ffs/linuxrc.new" >&2 return 1 };; *) { echo '#!/bin/sh' test "$sleep" -gt 0 && echo -n "sleep='$sleep' " echo -n "exec '/boot/$type' '$device'" for opt in "$@" do echo -n " '$opt'" done echo echo 'exec /boot/flash' } >"$ffs"/linuxrc.new && chmod 744 "$ffs"/linuxrc.new || { echo "turnup: boot_rootfs: failed to write $ffs/linuxrc.new" >&2 return 1 };; esac rm -f "$ffs"/linuxrc.sav || { echo "turnup: boot_rootfs: failed to remove $ffs/linuxrc.sav" >&2 return 1 } ln "$ffs"/linuxrc "$ffs"/linuxrc.sav || { echo "turnup: boot_rootfs: failed to save /linuxrc.sav" >&2 return 1 } mv -f "$ffs"/linuxrc.new "$ffs"/linuxrc || { echo "turnup: boot_rootfs: failed to install new /linuxrc" >&2 return 1 } return 0 } # # disk [-m] [-i] [-s