#!/bin/sh ### BEGIN INIT INFO # Provides: ssd fs # Required-Start: after mountall (36) # Required-Stop: before mountall (36) # Default-Start: S # Default-Stop: 0 1 # Short-Description: Avoid write access to SSD's # Description: Creates layer with unionfs ans tmpfs to catch all write operations # and store it to a single ram-disk. On shutdown all written data is # synchronised to disk. # Hints: - Some monts via --rbind beyond /var and /home AFTER start of ssdfs can cause # system-crash on shutdown because of unionfs on umount. You have to ummount # such things before you shotdown ssdfs # - /var/log, /var/tmp, /tmp and /media are volatile and where not synchronised # to disk on shutdown # Installation: cp ssdfs /etc/init.d # chown root:root /etc/init.d/ssdfs # chmod 755 /etc/init.d/ssdfs # update-rc.d ssdfs start 36 S 6 0 . ### END INIT INFO # Author: Tobias Tacke # Copyright 2009 Tobias Tacke, all rights reserved. # This program is free software; you can redistribute it and/or modify it under the GPL-terms (http://dev.perl.org/licenses/gpl1.html) ### Configuration # Size of writing-area in RAM RAMDISK_SIZE="512M" # set this to "yes" to enable asking to proceed... DEBUG="no" ### END Configuration #>>>>>>>>>>>>>>>> #/etc/default/bootlogd BOOTLOGD_ENABLE=yes | wofür ist das gut? #HINWEISE #wenn unterhalb eines unionfs-verzeichnisses etwas NACH dem start gemoiuntet wird, muss es VOR dem stop ungemountet werden sonst hängt unionfs und daten gehen veerloren! #deie mtab wird noch beim Start mit 45 schreiboperationen belegt (mount/umoount dirtied inode) #beim Wiederaufwachendes systemes gibt es einige Schreiboperationen (dirtied+write), keine ahnung wo die hingehen (~50) #durch das rsync beim stoppen kann das ausschalten länger dauern, wenn /var und /home viele inhalte haben #tmp, var/tmp, var/log gehen komplett verloren. Dieser inhalt wird nur über die Ramdisk abgebildet und nicht gesynct (ist im script separiert und kann bei bedarf dort angepasst werden) #>>>>>>>>>>>> #TODO weitermachen,,, #_additional_var_stuff()-# # Das zeug auslagern # dann var/log # var/tmp in /tmp verlinken und beim Start jeweils testen # die daten in log dann neu erstellen ################# PATH=/sbin:/usr/sbin:/bin:/usr/bin DESC="SSD-FS" NAME="ssdfs" DAEMON= DAEMON_ARGS= PIDFILE= SCRIPTNAME="/etc/init.d/$NAME" . /lib/init/vars.sh . /lib/lsb/init-functions _ask() { while true do echo -n " $1 (y/n): " read STDIN case $STDIN in y) return 0 ;; n) return 1; ;; esac done return 1 } _check_command() { case $1 in start|stop|status|force-umount|backup) return 0 ;; restart|reload|force-reload) _output_echo "Error: argument '$1' not supported", "err" return 1 ;; *) echo "Usage: $SCRIPTNAME {start|stop|status|force-umount|backup}" >&2 return 1 ;; esac return 1 } _mount_volatile() { _output_echo " > $1" if [ ! -d /.ssdfs/volatile/sys.$2.volatile ] ; then mkdir /.ssdfs/volatile/sys.$2.volatile || return 1 fi chmod 777 /.ssdfs/volatile/sys.$2.volatile mount --rbind /.ssdfs/volatile/sys.$2.volatile $1 || return 1 _output_echo " ...done" return 0 } _bind_volatile_mtab() { _output_echo " bind mtab to volatile content..." # TODO mtab danach nochmal probieren # if [ "$DEBUG" = "no" ] || _ask "Bind mtab to volatile content?" ; then # cp /etc/mtab /.ssdfs/volatile/etc.mtab # mount --bind /.ssdfs/volatile/etc.mtab /etc/mtab # _output_echo " ...done" # fi } _create_sys_content() { if [ ! -d /.ssdfs ] ; then mkdir /.ssdfs || return 1 fi if [ ! -d /.ssdfs/volatile ] ; then mkdir /.ssdfs/volatile || return 1 fi if [ ! -d /.ssdfs/changes ] ; then mkdir /.ssdfs/changes || return 1 fi if [ ! -d /.ssdfs/backup ] ; then mkdir /.ssdfs/backup || return 1 fi _output_echo " mounting volatile disk with $RAMDISK_SIZE size..." mount -t tmpfs -o size=$RAMDISK_SIZE -o sync -o dirsync -o mode=1777 -o rw -o nosuid -o nodev ssdfs.volatile /.ssdfs/volatile || return 1 _bind_volatile_mtab _output_echo "binding other system dirs to volatile disk..." if ! _mount_volatile "/tmp" "tmp" ; then return 1 fi if ! _mount_volatile "/media" "media" ; then return 1 fi _output_echo " done" return 0 } _remove_sys_content() { umount -l -t tmpfs ssdfs.volatile } _is_mount_complete() { grep "ssdfs.volatile /.ssdfs/volatile tmpfs " /etc/mtab 1> /dev/null || return 1 grep "$1 /.ssdfs/volatile/$2.static none rw,bind " /etc/mtab 1> /dev/null || return 1 grep "ssdfs.$2 $1 unionfs " /etc/mtab 1> /dev/null || return 1 grep "ssdfs.$2.volatile.protector /.ssdfs/volatile/$2.volatile tmpfs " /etc/mtab 1> /dev/null || return 1 grep "ssdfs.$2.static.protector /.ssdfs/volatile/$2.static tmpfs " /etc/mtab 1> /dev/null || return 1 return 0 } _is_skelett_complete() { [ -d /.ssdfs/volatile/$2.static ] || return 1 [ -d /.ssdfs/volatile/$2.volatile ] || return 1 return 0 } _force_unmount() { umount -l -t tmpfs ssdfs.$2.protector umount -l /.ssdfs/changes umount -l -t tmpfs ssdfs.$2.static.protector umount -l -t tmpfs ssdfs.$2.volatile.protector umount -l -t unionfs ssdfs.$2 umount -l /.ssdfs/volatile/$2.static umount -l -t tmpfs ssdfs.$2.volatile return 0 } _mount() { chmod 777 /.ssdfs/volatile/$2.volatile || return 1 mount --rbind $1 /.ssdfs/volatile/$2.static || return 1 mount -t unionfs -o dirs=/.ssdfs/volatile/$2.volatile=rw:/.ssdfs/volatile/$2.static=ro ssdfs.$2 $1 || return 1 mount -t tmpfs -o size=1k -o ro ssdfs.$2.volatile.protector /.ssdfs/volatile/$2.volatile || return 1 mount -t tmpfs -o size=1k -o ro ssdfs.$2.static.protector /.ssdfs/volatile/$2.static || return 1 return 0 } _is_mount_remain() { grep "$1 /.ssdfs/volatile/$2.static none rw,bind " /etc/mtab 1> /dev/null && return 0 grep "union.$2 $1 unionfs " /etc/mtab 1> /dev/null && return 0 grep "ssdfs.$2.volatile.protector /.ssdfs/volatile/$2.volatile tmpfs " /etc/mtab 1> /dev/null && return 0 grep "ssdfs.$2.static.protector /.ssdfs/volatile/$2.static tmpfs " /etc/mtab 1> /dev/null && return 0 return 1 } _create_skelett() { mkdir /.ssdfs/volatile/$2.static || return 1 mkdir /.ssdfs/volatile/$2.volatile || return 1 return 0 } _remove_skelett() { [ -d /.ssdfs/volatile ] || return 0 [ -d /.ssdfs/volatile/$2.static ] && rmdir /.ssdfs/volatile/$2.static/ [ -d /.ssdfs/volatile/$2.volatile ] && rmdir /.ssdfs/volatile/$2.volatile/ return 0 } _unmount_prepare_save() { #TODO Das hier ist "busy" if ! umount -t unionfs $1 ; then _output_echo " warning: dir $1 is busy, umount in lazy mode...", "err" if ! umount -l -t unionfs $1 ; then _output_echo " ... failed", "err" return 1 fi _output_echo " ... ok" fi mount -t tmpfs -o size=1k -o ro ssdfs.$2.protector $1 || return 1 umount -l -t tmpfs ssdfs.$2.volatile.protector || return 1 return 0 } _read_rescue_backup() { [ -r /.ssdfs/backup/$2.backup ] || return 0 _output_echo " try read remaining rescue backup from disk..." if ! rsync --read-batch=/.ssdfs/backup/$2.backup -a -q --delete /.ssdfs/volatile/$2.static/ ; then if _ask "can't read backup file. Delete it?" ; then if [ -f /.ssdfs/backup/$2.backup.try ] ; then rm /.ssdfs/backup/$2.backup.try fi if [ -f /.ssdfs/backup/$2.backup ] ; then rm /.ssdfs/backup/$2.backup fi _output_echo " done" return 0 fi if _ask "Proceed with action?" ; then return 0 fi return 1 fi if [ -f /.ssdfs/backup/$2.backup.try ] ; then rm /.ssdfs/backup/$2.backup.try fi if [ -f /.ssdfs/backup/$2.backup ] ; then rm /.ssdfs/backup/$2.backup fi _output_echo " done" return 0 } _check_run_complete() { if ! _is_skelett_complete $1 $2 ; then _output_echo "Error: $2 - skelett is'n complete", "err" return 1 fi if ! _is_mount_complete $1 $2 ; then _output_echo "Error: $2 mounts are incomplete", "err" return 1 fi return 0 } _check_not_run() { if _is_mount_remain $1 $2 ; then _output_echo "Error: some mounts for $2 still exists", "err" return 1 fi return 0 } _unmount() { #TODO das wieder versuchen einzusetzen, das ist eigentlich wichtig!!! #unionctl $1 --mode /.ssdfs/volatile/$2.volatile ro || _output_echo "warning: can't remount $1 read-only", "err" mount -o remount,ro /.ssdfs/volatile/$2.volatile if grep "ssdfs.changes /.ssdfs/changes tmpfs " /etc/mtab 1> /dev/null ; then umount -l /.ssdfs/changes fi mount -t tmpfs -o size=$RAMDISK_SIZE -o sync -o dirsync -o mode=1777 -o rw -o nosuid -o nodev -o noauto ssdfs.changes /.ssdfs/changes || return 1 umount -l -t tmpfs ssdfs.$2.static.protector || return 1 rsync --only-write-batch=/.ssdfs/changes/$2.data -a -q --delete $1/ /.ssdfs/volatile/$2.static/ || return 1 if ! _unmount_prepare_save $1 $2 ; then _output_echo "unclean unmount for $2: try saving backup-data to disk...", "err" cp /.ssdfs/changes/$2.data /.ssdfs/backup/$2.backup || return 1 _output_echo "done" return 1 fi _output_echo "try saving data on disk..." if ! rsync --read-batch=/.ssdfs/changes/$2.data -a -q --delete /.ssdfs/volatile/$2.static ; then if _ask "Can't write data to disk. Open a bash?" ; then _output_echo "Exit this shell to proceed...", "err" bash fi if _ask "Abort action?" ; then _output_echo "aborted!", "err" return 1 fi fi _output_echo "done" #TODO hier bleibt er haengen, wenn es in var normal weider geht umount -l /.ssdfs/volatile/$2.static || return 1 umount -l -t tmpfs ssdfs.$2.protector || return 1 umount -l -t tmpfs ssdfs.changes || return 1 rm -rf /.ssdfs/volatile/$2.volatile return 0 } _write_rescue_backup() { if ! _check_run_complete $1 $2 ; then if _ask "ssdfs is't correct initialised. Abort rescue backup?" ; then _output_echo "aborted!", "err" return 1 fi fi umount -l -t tmpfs ssdfs.$2.static.protector || return 1 rsync --only-write-batch=/.ssdfs/backup/$2.backup.try -a -q --delete $1/ /.ssdfs/volatile/$2.static/ || return 1 mv /.ssdfs/backup/$2.backup.try /.ssdfs/backup/$2.backup || return 1 mount -t tmpfs -o size=1k -o ro ssdfs.$2.static.protector /.ssdfs/volatile/$2.static || return 1 return 0 } _hint_manual_sync() { echo "how to rescue ssdfs RAM-disk-data for $1 in case of an error (only if ssdfs is running):" echo " umount -l -t tmpfs ssdfs.$2.static.protector" echo " rsync --only-write-batch=/root/ssdfs.$2.data -a -q --delete $1/ /.ssdfs/volatile/$2.static/" echo "--- in case of multiple dirs to rescue, do things above for all dirs before proceeding..." echo " /etc/init.d/ssdfs force-umount" echo " rsync --read-batch=/root/ssdfs.$2.data -a -q --delete $1" } _execute() { _output_echo "--- $1 for dir $3 ($4)" case $1 in status) if _is_mount_complete $3 $4 && _is_skelett_complete $3 $4 ; then _output_echo "started" return 0 fi if ! _is_mount_remain $3 $4 ; then _output_echo "stopped" return 0 fi _output_echo "corrupt" _hint_manual_sync $3 $4 return 1 ;; backup) _output_echo "writing backup..." if ! _write_rescue_backup $3 $4 ; then _output_echo "Error: backup failed", "err" return 1 fi _output_echo "done" return 0 ;; force-umount) if [ "$2" != 'do-it' ] ; then _output_echo "Warning: that will result in loss of data, make backups before!", "err" _output_echo "Say: '/etc/init.d/ssdfs force-umount do-it' to execute this", "err" _hint_manual_sync $3 $4 return 0 fi _force_unmount $3 $4 return 0 ;; stop) if ! _is_mount_remain $3 $4 ; then _output_echo "Nothing to do. Still stopped" return 0 fi if ! _check_run_complete $3 $4 ; then return 1 fi if ! _handle_extras $3 "stop" ; then return 1 fi if ! _unmount $3 $4 ; then _output_echo "Error: can't unmount", "err" _hint_manual_sync $3 $4 return 1 fi if ! _check_not_run $3 $4 ; then return 1 fi _output_echo "stopped" return 0 ;; start) if _is_mount_complete $3 $4 && _is_skelett_complete $3 $4 ; then _output_echo "Nothing to do. Still started" return 0 fi if ! _check_not_run $3 $4 ; then return 1 fi if ! _is_skelett_complete $3 $4 ; then if ! _remove_skelett $3 $4 ; then _output_echo "Error: can't remove skelett", "err" return 1 fi if ! _create_skelett $3 $4 ; then _output_echo "Error: can't create skelett", "err" exit 1 fi fi if ! _read_rescue_backup $3 $4 ; then _output_echo "failed", "err" return 1 fi if ! _mount $3 $4 ; then _output_echo "Error: can't mount", "err" return 1 fi if ! _check_run_complete $3 $4 ; then return 1 fi if ! _handle_extras $3 "start" ; then return 1 fi _output_echo "started" ;; esac return 0 } _handle_extras() { if [ "$1" != "/var" ] ; then return 0 fi if [ "$2" = "start" ] ; then _output_echo " ... bind volatile dirs ... " if ! _additional_var_stuff ; then _output_echo " ... failed", "err" fi else _output_echo " ... change volatile dirs to separate filesystems ... " umount -l /var/log mount -t tmpfs -o size=10M -o sync -o dirsync -o mode=1777 -o rw -o nosuid -o nodev ssdfs.shutdown.var.log /var/log umount -l /var/tmp mount -t tmpfs -o size=10M -o sync -o dirsync -o mode=1777 -o rw -o nosuid -o nodev ssdfs.shutdown.var.tmp /var/tmp fi _output_echo " done" return 0 } _additional_var_stuff() { if ! _mount_volatile "/var/tmp" "var.tmp" ; then return 1 fi if ! _mount_volatile "/var/log" "var.log" ; then return 1 fi touch /var/log/dmesg touch /var/log/mail.warn touch /var/log/user.log touch /var/log/daemon.log touch /var/log/messages touch /var/log/debug touch /var/log/auth.log touch /var/log/mail.err touch /var/log/syslog touch /var/log/mail.log touch /var/log/kern.log touch /var/log/lpr.log touch /var/log/mail.info [ ! -d /var/log/news ] && mkdir /var/log/news touch /var/log/news/news.crit touch /var/log/news/news.err touch /var/log/news/news.notice return 0 } _output_echo() { if [ "$2" == "err" ] || [ "$DEBUG" != "no" ] || [ "$VERBOSE" != "no" ] ; then echo "$1" fi } _run_ssdfs() { RESULT="0" if ! _check_command $1 ; then return 1 fi _output_echo "[ ssdfs ... begin ]" if [ "$1" = "start" ] ; then if ! _create_sys_content ; then RESULT="1" fi fi if [ "$1" = "start" ] ; then if _execute "$1" "$2" "/var" "var"; then _output_echo " < done >" else _output_echo " < failed >", "err" RESULT="1" fi if _execute "$1" "$2" "/home" "home"; then _output_echo " < done >" else _output_echo " < failed >", "err" RESULT="1" fi else if _execute "$1" "$2" "/home" "home"; then _output_echo " < done >" else _output_echo " < failed >", "err" RESULT="1" fi if _execute "$1" "$2" "/var" "var"; then _output_echo " < done >" else _output_echo " < failed >", "err" RESULT="1" fi fi if [ "$1" = "stop" ] ; then if ! _remove_sys_content ; then RESULT="1" fi fi _output_echo "[ ssdfs ... end ]" return "$RESULT" } # helper function to set the usplash timeout. https://launchpad.net/bugs/21617 usplash_timeout () { TIMEOUT=$1 if [ -x /sbin/usplash_write ]; then /sbin/usplash_write "TIMEOUT $TIMEOUT" || true fi } ######### package main; ########## usplash_timeout 10 [ "$VERBOSE" != "no" ] && log_daemon_msg "$1 $DESC" "$NAME" [ "$DEBUG" = "no" ] && log_action_begin_msg "$1 $NAME" if [ "$DEBUG" = "yes" ] ; then if ! _ask "proceed?" ; then _output_echo "aborted!", "err" exit 0 fi fi if [ "$1" = "status" ] ; then DEBUG="yes" fi _run_ssdfs $1 case "$?" in 0) [ "$VERBOSE" != no ] && log_end_msg 0 [ "$DEBUG" = "no" ] && log_action_end_msg 0 ;; 1) [ "$VERBOSE" != no ] && log_end_msg 1 [ "$DEBUG" = "no" ] && log_action_end_msg 1 ;; esac exit 0