Install Debian so you can boot a full install read only mode

Share your own howto's etc. Not for support questions!
Post Reply
Posts: 298
Joined: 2016-08-20 21:00

Install Debian so you can boot a full install read only mode

#1 Post by ruffwoof »

If you download a current Debian stable liveCD you can format a HDD partition (and set its boot flag as being on), copy the /live folder from the CD/ISO to that HDD, install grub4dos (which despite its name can install a bootloader and menu.lst onto a ext format partition) and point menu.lst to boot that /live folders initrd, vmlinuz and filesystem.squashfs.

Allocate that partition a label of 'persistence' and you can boot that same set up but with all changes being preserved into the same partition (alongside the /live folder and grub4dos menu.lst ....etc).

Boot with a peristence persitence-read-only kernel boot parameters and that will read the persistence (changes) during bootup, but wont add to them (changes lost at shutdown) [There is a method to flush those memory based changes to disk if you so desired, but that's more involved and whilst I have a working copy of that, I'm not going to detail that any further here].

Take all that a step further and if you extract the content of the /live/filesystem.squashfs to the / folder, then in effect all changes are being recorded outside of filesystem.squashfs ... which becomes redundant. Rather than just deleting it however its better to create a empty version of that file, as without it no other squashfs files can be loaded at bootup. I just removed filesystem.squashfs and created a new one using

mkdir somedir
mksquashfs somedir filesystem.squashfs
rmdir somedir

(you need squashfs-tools to be installed in order to run mksquashfs).

Taking that one step further still, if you start off with a full install running from / then you can work things the other way around and just install a /live folder with a empty filesystem.squashfs and provided you install grub2 to the root partition rather than the mbr, then you can install grub4dos to the mbr and have that either boot the read only 'live CD' style choice, or chain to the grub2 boot menu to boot a 'full install' boot style.

A nice thing about full install is that updates ...etc work as intended and you can run a 'pure' Debian in the usual manner. But with the liveCD boot style (but booting from HDD) booted read-only (all changes recorded in memory/lost at shutdown (with exception of a optional script to flush memory records to disk as I indicated earlier)) you can run tests on that installation, where any changes are lost when you reboot. Great for trying out new programs etc. installed via apt-get or synaptic or whatever, but where you might decide to 'undo' those changes. Or for trying out different configurations that might end up with a messed up system, but where a simple reboot has all those changes undone. The main appeal to me is that I can run in read-only mode for most of the time (just have to store docs ...etc elsewhere (on another partition)) and reboot the exact same 'factory fresh' version each and every time; But when updates are apparent then reboot the full version (read/write) to install those updates before rebooting back into read-only mode again.

This is what part of my /menu.lst looks like (note that I created a /debian-usb flag file for grub to find)

title Debian Jessie Frugal RO
find --set-root /debian-usb
kernel /vmlinuz boot=live config rw rw-basemount nofastboot persistence persistence-read-only persistence-label=persistence quickreboot noprompt showmounts live-media-path=/live/ config
initrd /initrd.img

# Full installed Linux
title Debian FULL Install : GNU/Linux, kernel 3.16.0-4-amd64(sdf1/boot/grub/menu.lst)
find --set-root /debian-usb
configfile /boot/grub/menu.lst

In my case installation of debian resulted in grub pc being installed into /boot/grub/ ... so the first boot loader chains to another grub legacy style menu - that might equally however have been a chain to teh more normal grub2 style boot config file/loader

PS : a trick I use ... as when you boot the exact same system in RO or RW modes you can sometimes forget which way you actually booted ... is to run the command cat /proc/cmdline which provides a indication of which way the current session was booted.

PPS : if you've booted a RO session then the number/size of changes are limited to available free memory space, beyond that and the system will slow right down (more changes than what can be stored in available free memory).

Posts: 298
Joined: 2016-08-20 21:00

Re: Install Debian so you can boot a full install read only

#2 Post by ruffwoof »

The following is the code that I use during a RO (all changes being stored in memory) boot session, that flushes all of those changes to disk (makes them persistent across reboots). I call it flush2disk and store it as /usr/local/bin/flush2disk (made executable)

Code: Select all

#2007 Lesser GPL licence v2 (
#Barry Kauler
#Edited for 'porteus-boot' on Debiandog for the "save on exit" boot option, by fredx181
#2016-02-26 Change; Line 89 "--remove-destination" instead of "-f", workaround possible crashing when 
#              copying files from upgraded libc6
#2016-07-19 fredx181 make save 'on demand' (to directory) work also with live-boot (originally idea from
#              Toni (saintless) ).
#2016-07-22 fredx181 parse location of live folder more exact (works now on DebianDog also)
#2016-07-29 Rufwoof - renamed to avoid confusion from save2flash to flush2disk and changed to specifically
#              run on persistence persistence-read-only pure Debian Live CD frugal
#2016-07-30 Rufwoof changed the BASE grep to pick up just the first line (partition) [added -m 1 parameter]
#2016-08-08 fredx181 changed: do the copying by 'rsync' instead of 'cp'
#              rsync does a better job, will skip copying files that are earlier copied to BASE by this script,
#              so much faster then, also it will 'flush' the memory space (tmpfs) in SNAP to (almost) how it was
#              at startup (see last code block). 
#2016-10-05 Rufwoof - changed to launch find as early as possible as for first run that slows things down
#              Also added spinner that's shown whilst waitign for that pre-find to finish

    local pid=$1
    local delay=0.25
    local spinstr='|/-\'
    while [ "$(ps a | awk '{print $1}' | grep $pid)" ]; do
        local temp=${spinstr#?}
        printf " [%c]  " "$spinstr"
        local spinstr=$temp${spinstr%"$temp"}
        sleep $delay
        printf "\b\b\b\b\b\b"
    printf "    \b\b\b\b"

if [ "`whoami`" != "root" ]; then
	exec sudo ${0}

export LANG=C #110206 Dougal: I **think** this should not cause problems with filenames

# Debian LiveCD defaults to use a partition with a label of persistence or a partition
# with a label that matches the perisistence-label= kernel boot parameter (menu.lst)
LABEL=`cat /proc/cmdline | grep persistence-label`
if [ -z "$LABEL" ]; then
 	LABEL=`cat /proc/cmdline | awk 'BEGIN{FS="persistence-label="} {print $2}' | awk 'BEGIN{FS=" "} {print $1}'`
BASE=`mount -l | grep "\[$LABEL\]" | grep -m 1 /lib/live/mount/persistence | awk 'BEGIN{FS=" "} {print $3}'`
if [ -z "$BASE" ]; then
 	echo -----------------------------------------------------
 	echo Only valid when booted a Read Only session and when 
 	echo persistence-label boot parameter specified - exiting
 	echo -----------------------------------------------------
 	sleep 4

# Remount the 'home' partition read-write, otherwise we can't copy the changes
mount -o remount,rw $BASE 2> /dev/null

# help speed up find by pre-running as early as possible
find $BASE/. >/dev/null &
PID=$!      # record pid of background find

dialog --backtitle "Are you sure you want to flush all changes to disk?" --yesno " Save " 5 20
if test $? -ne 0

#dialog --backtitle "Should I leave existing network and graphics values?" --yesno " Save " 5 20
#if test $? -ne 0
#	rm -f /etc/udev/rules.d/70-persistent*
#	rm -f /home/user/.config/autostart/LXRandR*
#	rm -f /etc/X11/xorg.conf

# fredx181 mod, create variable for list containing files to save (to be used further below).

# By default overlay is mounted twice (in case no persistence), unmounting it, sort of 'releases' it, not sure why but it works ;)
# On DebianDog it's different, but doing the below does not harm  
umount /lib/live/mount/overlay 2> /dev/null

# Rufwoof append root to path
if [ ! -d /lib/live/mount/overlay/root ]
	echo "Looks like you're running a continual save session"
 	echo "Invalid operation. Exiting"
 	sleep 5

# create BASE directory if not exist
mkdir $BASE 2> /dev/null
cd $SNAP || exit 1
echo "Merging $SNAP onto $BASE..."
if [ -f /mnt/live/tmp/modules ]; then
 	SFSPoints=$( ls -d -1 /mnt/live/memory/images/* |sort -u ) #110206 Dougal: get a list of the sfs mountpoints
 	# live-boot v3 or v4
 	SFSPoints=$( ls -d -1 /lib/live/mount/rootfs/* |sort -u ) #110206 Dougal: get a list of the sfs mountpoints

# Wait for early launched find to finish, showing a spinner whilst waiting
echo -n "Scanning ..."
spinner ${PID}
#wait $PID # wait until background updatedb finishes
echo "Updating ..."

#Handle Whiteouts...
find . -mount \( -regex '.*/\.wh\.[^/]*' -type f \) | sed -e 's/\.\///;s/\.wh\.//' |
while read N
 	BN="`basename "$N"`"
 	DN="`dirname "$N"`"
 	[ "$BN" = ".wh.aufs" ] && continue #w003 aufs has file .wh..wh.aufs in /initrd/pup_rw.
 	#[ "$DN" = "." ] && continue
 	#110212 unionfs and early aufs: '.wh.__dir_opaque' marks ignore all contents in lower layers...
 	if [ "$BN" = "__dir_opaque" ];then #w003
  		#'.wh.__dir_opaque' marks ignore all contents in lower layers...
  		rm -rf "${BASE}/${DN}" 2>/dev/null #wipe anything in save layer. 110212 delete entire dir.
  		mkdir -p "${BASE}/${DN}" #jemimah: files sometimes mysteriously reappear if you don't delete and recreate the directory, aufs bug? 111229 rerwin: need -p, may have to create parent dir.
  		#also need to save the whiteout file to block all lower layers (may be readonly)...
  		touch "${BASE}/${DN}/.wh.__dir_opaque" 2>/dev/null
  		rm -f "$SNAP/$DN/.wh.__dir_opaque" #should force aufs layer "reval".
 	#110212 recent aufs: .wh.__dir_opaque name changed to .wh..wh..opq ...
 	if [ "$BN" = ".wh..opq" ] ; then
  		rm -rf "${BASE}/${DN}" 2>/dev/null  #wipe anything in save layer.
  		mkdir -p "${BASE}/${DN}" #jemimah: files sometimes mysteriously reappear if you don't delete and recreate the directory, aufs bug? 111229 rerwin: need -p, may have to create parent dir.
  		#also need to save the whiteout file to block all lower layers (may be readonly)...
  		touch "${BASE}/${DN}/.wh..wh..opq" 2>/dev/null 
  		rm -f "$SNAP/$DN/.wh..wh..opq"  #should force aufs layer "reval".
 	#comes in here with the '.wh.' prefix stripped off, leaving actual filename...
 	rm -rf "$BASE/$N"
 	#if file exists on a lower layer, have to save the whiteout file...
 	#110206 Dougal: speedup and refine the search...
 	for P in $SFSPoints
   		if [ -e "$P/$N" ] ; then
     			[ ! -d "${BASE}/${DN}" ] && mkdir -p "${BASE}/${DN}"
     			touch "${BASE}/${DN}/.wh.${BN}"
 	done #110206 End Dougal.

#Directories... v409 remove '^var'. w003 remove aufs .wh. dirs.
#w003 /dev/.udev also needs to be screened out... 100820 added var/tmp #110222 shinobar: remove all /dev
find . -mount -type d | busybox tail +2 | sed -e 's/\.\///' | grep -v -E '^mnt|^initrd|^proc|^sys|^tmp|^root/tmp|^\.wh\.|/\.wh\.|^dev/|^run|^var/run/udev|^run/udev|^var/tmp|^etc/blkid-cache' |
#110224 BK revert, leave save of /dev in for now, just take out some subdirs... 110503 added dev/snd
#find . -mount -type d | busybox tail +2 | sed -e 's/\.\///' | grep -v -E '^mnt|^initrd|^proc|^sys|^tmp|^root/tmp|^\.wh\.|/\.wh\.|^dev/\.|^dev/fd|^dev/pts|^dev/shm|^dev/snd|^var/tmp' |
while read N
 	mkdir -p "$BASE/$N"
 	# I think nathan advised this, to handle non-root user:
 	chmod "$BASE/$N" --reference="$N"
 	OWNER="`stat --format=%U "$N"`"
 	chown $OWNER "$BASE/$N"
 	GRP="`stat --format=%G "$N"`"
 	chgrp $GRP "$BASE/$N"
 	touch "$BASE/$N" --reference="$N"

#Copy Files... v409 remove '^var'. w003 screen out some /dev files. 100222 shinobar: more exclusions. 100422 added ^root/ftpd. 100429 modify 'trash' exclusion. 100820 added var/tmp #110222 shinobar: remove all /dev
find . -mount -not \( -regex '.*/\.wh\.[^/]*' -type f \) -not -type d |  sed -e 's/\.\///' | grep -v -E '^mnt|^initrd|^proc|^sys|^tmp|^pup_|^zdrv_|^root/tmp|_zdrv_|^dev/|^\.wh\.|^run|^var/run/udev|^run/udev|^root/ftpd|^var/tmp' | grep -v -E -i '\.thumbnails|\.trash|trash/|^etc/blkid-cache|\.part$'  |
#110224 BK: revert, leave save of /dev in for now... 120103 rerwin: add .XLOADED
#find . -mount -not \( -regex '.*/\.wh\.[^/]*' -type f \) -not -type d |  sed -e 's/\.\///' | grep -v -E '^mnt|^initrd|^proc|^sys|^tmp|^run|^pup_|^zdrv_|^root/tmp|_zdrv_|^dev/\.|^dev/fd|^dev/pts|^dev/shm|^\.wh\.|^var/run|^root/ftpd|^var/tmp|\.XLOADED$' | grep -v -E -i '\.thumbnails|\.trash|trash/|\.part$'  |
while read N
 	[ -L "$BASE/$N" ] && rm -f "$BASE/$N"
 	# Finally, copy files unconditionally.
 	# fredx181 mod, no, don't use 'cp' just create filelist (for to save) here and run rsync later. 
 	#cp -a --remove-destination "$N" "$BASE/$N"
 	echo "$N" >> "$FILESAVELIST"
 	BN="`basename "$N"`" #111229 rerwin: bugfix for jemimah code (110212).
 	DN="`dirname "$N"`" #111229  "
 	[ -e "$BASE/$DN/.wh.${BN}" ] && rm "$BASE/$DN/.wh.${BN}" #110212 jemimah bugfix - I/O errors if you don't do this

# fredx181 mod, rsync copy from $FILESAVELIST
[ -f "$FILESAVELIST" ] && rsync -a --files-from=$FILESAVELIST "$SNAP" "$BASE"

# Remove files, corresponding with .wh files, from zchanges.dir
# Taken from 'cleanup' script included in the official Porteus initrd.xz 
MNAME="$BASE"; NAME="basename "$MNAME""
find $MNAME -name ".wh.*" 2>/dev/null | while IFS= read -r NAME; do wh=`echo "$NAME" | sed -e 's^$MNAME^^g' -e 's/.wh.//g'`; test -e "$wh" && rm -rf "$NAME"; done

# fredx181 mod, remount BASE and remove the just copied files from SNAP. 
if [ -f "$FILESAVELIST" ]; then
 	# remount BASE 
 	mount -no remount,add:1:"$BASE"=ro+wh aufs /
 	REMOVE=$(echo $(cat "$FILESAVELIST" | grep -v '\->'))
 	cd "$SNAP"
 	# remove files from SNAP that had just been copied to BASE 
 	rm -fr $REMOVE
 	echo "No changes found, nothing to do!"

sync &
echo Done
sleep 1

exit 0

As my system/desktop is setup how I like it, I tend to boot read only most of the time. Periodically I check for updates after first booting
apt-get update
apt-get upgrade
and if some are installed I test them out and could either reboot to apply those same updates again in a read-write session, or just run flush2disk so those updates already applied/tested are made persistent. In exceptional circumstances, such as a large (version) upgrade, running a read only session (all changes being recorded in memory) will fail due to insufficient memory. In which case rebooting into a read/write session and applying the updates is the only way to install those updates.

Post Reply