#!/bin/sh # turnup # See the help block at the end for documentation. # . /etc/default/functions # # configuration # The following variables control which directories in /var end # up on the rootfs and which end up in a temporary file system. INRAM_MEMSTICK="/var/cache /var/lock /var/log /var/run /var/tmp /var/lib/ipkg" INRAM_NFS="/var/cache /var/lock /var/run /var/tmp" INRAM_DISK="" # # force: override certain checks force= # # pfile: the uuid/partition file pfile=/etc/uuid_by_partition # # 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 {mount options} # mount the flash device, writeable, on the given directory get_flash() { local ffsdir ffsdev ffsdir="$1" shift test -n "$ffsdir" -a -d "$ffsdir" || { echo "$0: $ffsdir: internal error, flash mount point not a directory" >&2 return 1 } case "$(machine)" in nslu2) ffsdev="$(mtblockdev Flashdisk)";; *) ffsdev="$(mtblockdev filesystem)";; esac umountflash "$ffsdev" && mountflash "$ffsdev" "$ffsdir" "$@" } # # check_rootfs [-i] # Make sure the candidate rootfs is empty # Environment: rootdev=device or NFS root path 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: $rootdev: partition contains existing files, specify -f to overwrite" >&2 return 1;; *) checkmount "$1" && return 0 echo "turnup: $rootdev: partition does not seem to be a valid root partition" >&2 echo " The partition must contain a full operating system. To ensure that" >&2 echo " this is the case it is checked for the following, all of which must" >&2 echo " exist for the bootstrap to work:" >&2 echo echo " 1) A directory /mnt." >&2 echo " 2) A command line interpreter program in /bin/sh." >&2 echo " 3) The program chroot in /sbin or /usr/sbin." >&2 echo " 4) The program init in /sbin, /etc or /bin." >&2 echo echo " One or more of these items is missing. Mount $rootdev on /mnt" >&2 echo " and examine its contents. You can use turnup disk|nfs -i -f" >&2 echo " to copy this operating system onto the disk, but it may overwrite" >&2 echo " files on the disk." >&2 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 # In flash file systems /dev is in ramfs, in disk systems /dev # can be populated permanently. This is done by creating a # single entry '.noram' in /dev - the devices init script will # then populate the directory without overmounting it. The # devices in the passed in device table are also created, but # note that this is insufficient, /etc/init.d/devices must # also run. 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 } echo "turnup: initialising dev file system" >&2 # init tries to open the following devices: # /dev/console # /dev/tty0 # /dev/null # syslog, and maybe other things, only work if fd 1 is valid, therefore # we must create these devices here... makedevs --root="$1" --devtable="$2" :>"$1"/dev/.noram 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. # Removes the /var tmpfs entry from /etc/fstab. # Creates links from /var into /media/ram for NFS and Memstick. setup_var() { local ram_targets directory test -n "$1" -a -d "$1"/var || { echo "turnup: setup_var($1,$2): expected a directory" >&2 return 1 } case "$2" in disk|nfs|memstick);; *) echo "turnup: setup_var($1,$2): expected 'disk', 'nfs' or 'memstick'" >&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-volatile.sh || { echo "turnup: /var: could not populate directory" >&2 return 1 } case "$2" in disk) ram_targets="$INRAM_DISK";; nfs) ram_targets="$INRAM_NFS";; memstick) ram_targets="$INRAM_MEMSTICK";; esac for directory in $ram_targets do rm -rf "$1/$directory" ln -s "/media/ram/$directory" "$1/$directory" done # 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 sed -i '\@[ ]/var[ ][ ]*tmpfs[ ]@d' "$1"/etc/fstab echo "turnup: tmpfs will no longer be mounted on /var" >&2 # # 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_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" "$type" && setup_syslog "$new";; memstick) setup_bootdev "$new" "$table" && setup_var "$new" "$type" ;; nfs) setup_dev "$new" "$table" && setup_var "$new" "$type" && 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. Writes the initial uuid file. # bad, since sed won't fail even if it changes nothing. setup_fstab() { sed -i '\@^[^ ]*[ ][ ]*/[ ]@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 } # # build $pfile uuid_by_partition >"$1""$pfile" || echo "turnup: $pfile: blkid failed (ignored)" >&2 return 0 } # # 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 uuid opt type="$1" ffs="$2" sleep="$3" device="$4" uuid= # 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 } uuid="$3" shift 3;; 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' " test -n "$uuid" && echo -n "UUID='$uuid' " 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