Table of Contents

Introduction

These days laptops have so much RAM that it is possible to run a complete operating system from it. Some reasons to do it are:

  • the system does not boot anymore and you need a quick way to access files, browse the web or rescue the system
  • there is no Linux installed on the laptop and you are missing your favourite OS
  • for security reason you want to start with a fresh system or you want to discharge every possible change after a working session

Since my favourite distribution is Slackware [6] I started with Slackware 15 Live edition. However, even though I was able to extract the init script from the initramfs and get all the desired boot options, the system seems to not have unmounted properly the boot device (I get an error when I try to mount it in read-write mode or when I try eject /dev/sda).

So my quest to a better alternative continues.

In this article we are going to review briefly the generic procedure I use to configure the distro to run directly from RAM and we will cover the specific configurations for:

  • XUbuntu [1] or LUbuntu [2]
  • Fedora
  • Porteus Kiosk [3]
  • SliTaz [4]
  • SalixOS Live [5]

The generic procedure

As described in more detail in previous articles (click on the tag #live to see the list) most of the time the procedure I use to run a new Live distro is:

  • download the ISO image of the desired Linux Live
  • mount it read-only in a folder
  • find the following files in the mounted folder:
    • boot configurations (usually in the file grub.cfg or syslinux.cfg or isolinux.cfg)
    • the kernel (usually called bzImage or vmlinuz)
    • the initramfs (usually called initrd.gz or initrd.xz)
    • the image of the root filesystem (the biggest file on the drive sometimes split into multiple modules)
  • create a bootable USB drive with Grub2
  • copy the content of the ISO image to a folder on the USB key
  • create a new entry in the file grub.cfg from the content of the boot configuration files in the ISO image
  • extract the content of the initramfs to a temporary folder and read the init script to learn what boot parameters are supported (it is a file called init or linuxrc located in the root folder or in bin or sbin)
  • use the knowledge from the previous point to tune grub.cfg

Some distributions (like Ubuntu-based and RedHat-based) have the init script (in the initramfs) which is either a binary file or too hard to read. In this situations official documentation is the only possible way to get the list of all the supported parameters. Unfortunately, from my experience, most of the time it is not that good.

XUbuntu or LUbuntu

If your laptop has Secure Boot active and you cannot disable it, your options regarding which distribution to use are limited. If you don’t mind using a systemd-based distro XUbuntu or LUbuntu might come in handy. Differently from others, these two distros have a default configuration to run from the boot device (mounted read-only) and from the live system you can perform a regular installation if you like.

On every boot you have to select some initial parameters like the layout of the keyboard and the timezone. This is annoying if you do not plan to install it. Luckily to get rid of it you just need to pass a few parameters from the boot loader.

So the steps are:

  • download the ISO image from the official website
  • copy it on a USB stick (from any Linux system):
dd if=path/to/isofile of=/dev/sdX bs=4M conv=notrunc,fsync
  • boot the laptop where you want to run XUbuntu/LUbuntu from USB
  • when you see the menu of Grub2 leave the selection on the first entry and press “e” to edit the command line
  • edit the line that starts with “linux” to make it become:
linux /casper/vmlinuz file=/cdrom/preseed/lubuntu.seed toram

(if you are using XUbuntu you can leave the value xubuntu.seed instead of changing it to lubuntu.seed)

Check the supported boot options at [7] for more details.

Fedora

The same considerations done for Ubuntu apply to Fedora [11]. This is the development branch of the Red Hat world and it comes in multiple flavors called “Spins” [12]. Download for example the Xfce version [13] and copy it onto a USB stick with:

dd if=path/to/isofile of=/dev/sdX bs=4M conv=notrunc,fsync

Start your laptop from USB and edit the first Grub entry (moving the selection on the first row and pressing “e”) removing rd.live.check quiet. Then add rd.live.ram selinux=0 3:

linux /images/pxeboot/vmlinuz root=live:CDLABEL=Fedora-Xfce-Live-40-1-14  rd.live.image rd.live.ram selinux=0

Be aware the root user has no password and the regular user is called one (with password one). I recommend setting the root password and changing the default password of one before using it.

A couple of further customization I like to do are:

  • start in runlevel 3 (text mode) and then start the X Window System with startx
  • the root filesystem will have only 1 GB of space, so my first solution was to use the space under /dev/shm or /tmp, however if you want to install new software you need more space and this can be achieved by adding another boot parameter: rd.minmem=<megabyte> [14] The final configuration with this two points is:
linux /images/pxeboot/vmlinuz root=live:CDLABEL=Fedora-Xfce-Live-40-1-14  rd.live.image rd.live.ram selinux=0 rd.minmem=4096 3

Notes:

  • 3 is to start in runlevel 3
  • I used 4096 to have 4 GB of more space in my root filesystem, but it is RAM memory, so do your own math to check if you have enough space and choose a proper value
  • Check the Dracut man page 14 for more available options
  • I don’t remember if you will see linux or linuxefi in the Grub configuration, however it will work with both (keep what you have)

If you are annoyed by always typing all this stuff on every boot, I recently found a grub command called configfile [15] that allows you to load another config file from a partition. So you can copy the original grub.cfg from the USB key (or from the ISO image), customize with these options and once Grub is loaded you can type c to enter the command line and type, for example, configfile (hd1,gpt4)/grub.cfg if your saved file is in the root directory of the forth partition on the second hard drive (check with ls or the search command to find your file).

Porteus Kiosk

Let’s get back to an interesting Slackware-based distro: Porteus Linux. This distribution comes in two flavors, one for the Desktop and one aimed to be a web kiosk. Download the iso image of Porteus Kiosk and mount it to see the boot configuration and to extract the init script from the initrd file (full script in Appendix 1). Copy the content of the iso image in a folder called porteus on the bootable USB drive and add an entry in grub.cfg as the following:

menuentry "Porteus 5 XFCE 64-bit" {
    load_video
    insmod gzio
    insmod ext2
    search -f /porteus/vmlinuz --set=root
    linux     /porteus/vmlinuz copy2ram from=LABEL:USBBOOT nocd nohd locale=en_US.utf8 tz=UTC ipv6.disable=1 3
    initrd    /porteus/initrd.xz
}

Remember to substitute USBBOOT in the third last line to match your label of the usb partition holding the folder porteus.

As you can see I have used copy2ram and nocd nohd to instruct Porteus to copy the root filesystem to ram, unmount the USB drive and avoid mounting the partitions from the main Hard Drive.

In the past I used successfully this configuration, however on my current laptop the firmware of the wifi card is missing so unfortunately I am not able to connect to the network.

SliTaz GNU/Linux

This Swiss distribution [8] is super lightweight and runs with BusyBox and the Xvesa graphic driver [9]. At the time of writing the rolling release is less than 100 MB and should run from RAM by design (so there is no need for an extra boot parameter).

The challenge here is that the configuration of the bootloader is a little bit different. It uses an executable called c32box.c32 (a COM32 binary [10]) to start the bootstrap procedure. In addition the initramfs (which is also the root fs in this case) is split in 4 parts. I wasn’t able to concatenate those parts in the proper way and extract the content, so I limited myself to come up with the following working configuration for Grub2:

menuentry "SliTaz 5 Rolling" {
    load_video
    insmod gzio
    insmod ext2
    search -f /slitaz/bzImage --set=root
    linux     /slitaz/bzImage initrd=/slitaz/rootfs4.gz,/slitaz/rootfs3.gz,/slitaz/rootfs2.gz,/slitaz/rootfs1.gz root=/dev/null video=-32 autologin
    initrd    /slitaz/rootfs4.gz /slitaz/rootfs3.gz /slitaz/rootfs2.gz /slitaz/rootfs1.gz
}

As you can deduce from the configuration, I have copied the content of the SliTaz rolling release in the folder slitaz on my USB drive and I have moved the kernel and the files of the rootfs at the top level.

The first run left me with a system stuck probably because the kernel is too old for my modern laptop. So I tried to use the kernel huge from Slackware (one of the kernels is called huge because it is supposed to have a vast set of drivers compiled directly into it). This time the bootstrap seemed to run more smoothly however it was not able to load some required modules (like zram).

At this point the bootstrap process asked me to choose keyboard layout and time zone and I was left with a login prompt. Eager to test the new system I figured out the credentials for the user root (user=root, password=root). I started inspecting the content of /etc/passwd and of the folder /home searching for a regular user and I found the unprivileged user one. I reset the password with passwd one and logged in on a different shell (accessible with CTRL+ALT+F3 for example). Unfortunately there is nothing more I was able to do. The X server doesn’t start (try to run startx).

SalixOS Live

Left with the last chance, but without hoping on it too much I gave a try to SalixOS Live edition (two flavors also here, pick the correct one).

Since SalixOS is fully compatible with Slackware I thought there weren’t many differences between Slackware Live and this one. However eventually I was positively surprised because everything worked as I wished. My tests are:

  • am I able to successfully eject the removable device?
  • am I able to start the X server?
  • am I able to connect to internet and browse the web?
  • (optional) am I able to connect my bluetooth device?

The first three conditions are met, the last one I haven’t tried it yet.

Let’s see how to configure the system:

  • the first time I tried to make an educated guess and without checking too carefully I was able to boot the kernel and reach the initramfs where the system was telling me it wasn’t able to properly mount the main filesystem
  • even though the boot process was interrupted I was in a shell where I could investigate what was wrong and I started to read the init script with less (see Appendix 2 for the full script)
  • the reason why the root filesystem was not found is a missing parameter from the grub command line (I didn’t pass the label of the removable media)
  • from the code, however, there is no parsing of the input parameters so I suspected that the boot arguments were translated into environment variables (and I was right) - checked with command env
  • the other issue is that there is a hard-coded path: the folder modules of the root filesystem must be in the directory boot under the root of the USB drive

I moved the folder modules from salix64live to boot in the filesystem of the removable device and used the following entry in grub.cgf:

menuentry "Salix Linux Live 64-bit" {
    load_video
    insmod gzio
    insmod ext2
    set args='max_loop=255 vga=791 locale=en_US.utf8 keymap=us useswap=no copy2ram=yes medialabel=USBBOOT tz=Europe/Dublin hwc=UTC runlevel=3'
    search -f /salix64live/vmlinuz --set=root
    linux     /salix64live/vmlinuz initrd=initrd.gz $args ipv6.disable=1 # 3 debug hwc=localtime
    initrd    /salix64live/initrd.gz
}

(Substitute USBBOOT with the label of the filesystem on your USB stick)

Conclusions

In this article we saw some working configurations of Linux distributions that run entirely from RAM. Eventually the two most reliable are XUbuntu/LUbuntu if you have Secure Boot enabled and SalixOS (Live edition) for the rest of us in love with Slackware Linux.

References

[1] https://xubuntu.org

[2] https://lubuntu.me

[3] https://porteus-kiosk.org

[4] https://slitaz.org

[5] https://salixos.org

[6] http://www.slackware.com

[7] https://manpages.ubuntu.com/manpages/bionic/man7/casper.7.html

[8] https://slitaz.org/en/asso/

[9] https://slitaz.org/en/about/#spec

[10] https://www.syslinux.org/old/comboot.php

[11] https://fedoraproject.org/

[12] https://fedoraproject.org/spins/

[13] https://fedoraproject.org/spins/xfce/

[14] https://www.man7.org/linux/man-pages/man7/dracut.cmdline.7.html

[15] https://www.gnu.org/software/grub/manual/grub/html_node/configfile.html

Appendix 1: the full init script of Porteus (extracted from initrd.xz)

#!/bin/sh
### linuxrc script by brokenman <http://www.porteus.org>

export PATH=/bin:/usr/bin:./



# Source functions
#ORIGINAL: . ./finit
### ===> START IMPORT FROM finit <=== ###

## Functions for porteus init
## Author brokenman

# Export some color functions
RED='\033[0;31m'
GREEN='\033[0;32m'
CYAN='\033[0;36m'
YELLOW='\033[1;33m'
BOLD='\033[1;37m'
RST='\033[0m' # Reset Color

# Find a parameter on boot line
param() {
if [ -e $CFGDEV/$FOLDER/$CFG ]; then
	egrep -qo "^${1}$" $CFGDEV/$FOLDER/$CFG || egrep -qo " $1( |\$)" /proc/cmdline
		else
	egrep -qo " $1( |\$)" /proc/cmdline
fi
}

value() {
if [ -e $CFGDEV/$FOLDER/$CFG ]; then
	egrep -o "^${1}=[^ ]+" $CFGDEV/$FOLDER/$CFG | cut -d= -f2
	egrep -o " $1=[^ ]+" /proc/cmdline | cut -d= -f2
		else
	egrep -o " $1=[^ ]+" /proc/cmdline | cut -d= -f2
fi
}

# Begin debug staggered prompt boot mode
debug() { param debug && { echo -e "\n=====\n: Debugging started. Here is the shell for you.\n: Type your desired commands, hit Ctrl+D to continue booting\n: or press Ctrl+Alt+Del to reboot."; setsid sh -c 'exec sh </dev/tty1 >/dev/tty1 2>&1'; echo -e "\n\n"; }; }

# Run fstab for setup
fstab() { rm -f /tmp/devices
param nocd || for x in /dev/sr*; do blkid $x >>/tmp/devices; done
param nohd || blkid | egrep -v '/dev/sr|/dev/loop|/dev/mapper' >>/tmp/devices
dev=`egrep -v 'TYPE="sw|TYPE="LVM|TYPE=".*_raid_member"' /tmp/devices 2>/dev/null | cut -d: -f1 | cut -d/ -f3 | sort | uniq`
cat > /etc/fstab << EOF
# Do not edit this file as fstab is recreated automatically during every boot.
# Please use /etc/rc.d/rc.local or sysvinit scripts if you want to mount/unmount
# drive, filesystem or network share.

# System mounts:
aufs / aufs defaults 0 0
proc /proc proc defaults 0 0
sysfs /sys sysfs defaults 0 0
devtmpfs /dev devtmpfs defaults 0 0
devpts /dev/pts devpts rw,mode=0620,gid=5 0 0

# Device partitions:
EOF
for x in $dev; do
    fs=`grep -w /dev/$x /tmp/devices | egrep -o ' TYPE=[^ ]+' | cut -d'"' -f2`
    [ $fs = vfat ] && echo "/dev/$x /mnt/$x vfat $MOPT,umask=0,check=s,utf8,uid=1000 0 0" >>/etc/fstab || echo "/dev/$x /mnt/$x $fs $MOPT 0 0" >>/etc/fstab
    if [ ! -d /mnt/$x ]; then
	mkdir /mnt/$x
	if [ $fs = ntfs ]; then
	    ntfs-3g /dev/$x /mnt/$x -o $MOPT,uid=1000,big_writes 2>/dev/null || { sed -i "/$x /d" /etc/fstab; rmdir /mnt/$x; }
	else
	    mount -n /mnt/$x 2>/dev/null || { modprobe $fs 2>/dev/null && mount -n /mnt/$x 2>/dev/null || { sed -i "/$x /d" /etc/fstab; rmdir /mnt/$x; }; }
	fi
    fi
done

if [ -z "`egrep -o " noswap( |\$)" /proc/cmdline`" -a -e /tmp/devices ]; then
	#echo -e "\n# Swap partitions:" >>/etc/fstab
	for x in `grep 'TYPE="swap"' /tmp/devices | cut -d: -f1`; do echo "$x none swap sw,pri=1 0 0" >>/etc/fstab; done
fi }

# Mount things
mount_device() {
fs=`blkid /dev/$1 | egrep -o ' TYPE=[^ ]+' | cut -d'"' -f2`
if [ "$fs" ]; then
    mkdir /mnt/$1
    if [ $fs = vfat ]; then
	mount -n /dev/$1 /mnt/$1 -o $MOPT,umask=0,check=s,utf8,uid=1000 2>/dev/null || rmdir /mnt/$1
    elif [ $fs = ntfs ]; then
	ntfs-3g /dev/$1 /mnt/$1 -o $MOPT,uid=1000,big_writes 2>/dev/null || rmdir /mnt/$1
    else
	mount -n /dev/$1 /mnt/$1 -o $MOPT 2>/dev/null || { modprobe $fs 2>/dev/null && mount -n /dev/$1 /mnt/$1 -o $MOPT || rmdir /mnt/$1; }
    fi
fi }

# Search for boot location
search() { FND=none; for x in `ls /mnt | tac`; do
[ $1 /mnt/$x/$2 ] && { DEV=$x; FND=y; break; }; done
[ $FND = y ]; }

# Delay booting a little until devices have settled
nap() { echo -en $i"device not ready yet? delaying $SLEEP seconds \r"; sleep 1; }
lazy() { SLEEP=10; while [ $SLEEP -gt 0 -a $FND = none ]; do nap; let SLEEP=SLEEP-1; fstab; search $*; done }

# Find location of Porteus files
locate() { LPATH=`echo $2 | cut -b-5 | sed s@/dev@/mnt@`
if [ $LPATH = /mnt/ ]; then
    DEV=`echo $2 | cut -d/ -f3`; LPTH=`echo $2 | cut -d/ -f4-`; SLEEP=6
    while [ $SLEEP -gt 0 -a ! -b /dev/$DEV ]; do nap; let SLEEP=SLEEP-1; fstab; done
    [ -d /mnt/$DEV ] || mount_device $DEV
    [ $1 /mnt/$DEV/$LPTH ]
elif [ $LPATH = UUID: -o $LPATH = LABEL ]; then
    ID=`echo $2 | cut -d: -f2 | cut -d/ -f1`; LPTH=`echo $2 | cut -d/ -f2-`; DEV=`blkid | grep $ID | cut -d: -f1 | cut -d/ -f3`; SLEEP=6
    while [ $SLEEP -gt 0 -a "$DEV" = "" ]; do nap; let SLEEP=SLEEP-1; fstab; DEV=`blkid | grep $ID | cut -d: -f1 | cut -d/ -f3`; done
    [ -d /mnt/$DEV ] || mount_device $DEV
    [ $1 /mnt/$DEV/$LPTH ]
else
    LPTH=$2; search $* || lazy $*
fi }

# Check if a location is writable
is_writable(){ touch $1/.test 2>/dev/null; [ -e $1/.test ] && rm $1/.test; }

# Booting failed. Failed to find porteus files.
fail() { echo -e $i"couldn't find $1. Correct your from= cheatcode.\n Documentation in /usr/doc/porteus. Press 'enter' to continue booting."; read -s; }

# Failed to initiate changes. Creating temporary changes on tmpfs for this session.
fail_chn() { mount -nt tmpfs -o size=$RAMSIZE tmpfs /memory/changes; CHANGES=memory; CHNDEV=memory; }

# Just draw a line
draw() { echo """---------------------------------------------------------"""; }

# Error checking a save file.
fsck_dat() { echo $i"checking $1 for errors"
fs=`blkid $1 | egrep -o ' TYPE=[^ ]+' | cut -b8-10`
if [ $fs = xfs ]; then
    echo $i"detected xfs - performing fsck at mount time"
elif [ $fs = ext ]; then
    draw; e2fsck -C 0 -p $1; wait; draw
else
    echo $i"detected unsupported fs - skipping fsck"
fi }

# Copy modules
cpmod() { NUM=`grep -c '.' $1`
modfile=$1
while read x; do
    echo -en """  ($NUM modules left)  \r"""; let NUM=NUM-1; NAME=`basename "$x"`
    cp "$x" /memory/copy2ram 2>/dev/null
    if [ $? -eq 0 ]; then
	sed -i s@^.*/"$NAME"@/memory/copy2ram/"$NAME"@ /tmp/modules
    else
	rm /memory/copy2ram/"$NAME"; echo -e $i"""Not enough memory to copy $NAME"""; echo -e $i"""Other modules will be skipped."""
	find /memory/copy2ram -name "*.xzm" | sort >/tmp/modules
	break
    fi
done < $modfile
echo -en "                       \r"
}

# If bootlog cheatcode is present then make log entry
blog(){
		param log && echo "$1" >> $LOGFILE
}

# Check for a boot param
chk_bootcfg(){ grep "^$1" /union/etc/bootcmd.cfg; }

### ===> END IMPORT FROM finit <=== ###



# Enable pivot_root in initramfs to let it behave like initrd:
if [ -d /m ]; then
	mount -nt tmpfs tmpfs -o mode=0755 /m
	cp -a ??* /m 2>/dev/null
	exec switch_root /m linuxrc "$@"
fi

mount -nt proc proc /proc
mount -nt sysfs sysfs /sys
echo 0 >/proc/sys/kernel/printk

## Check for debug
if [ `grep -q debug /proc/cmdline` ]; then DBUG=0; fi

clear
echo -e "${BOLD}Porteus. Lightweight, lightning fast, portable.${RST}"
echo

# Variables:
i=`echo """^ """`
m=`echo """->"""`
arch=`uname -m`; [ $arch = x86_64 ] || arch=i586
CFG=`value cfgfile`; [ $CFG ] || CFG=porteus-v5.0-$arch.cfg
FROM=`value from`; ISO=`echo $FROM | egrep ".*\.iso( |\$)"`
IP=`value ip | cut -d: -f2`
MOPT=`value mopt`; [ $MOPT ] || MOPT="noatime,nodiratime,suid,dev,exec,async"
CHANGES=`value changes`
RAMSIZE=`value ramsize`; [ $RAMSIZE ] || RAMSIZE=60%
LOAD=`value load | sed 's/;/|/g'`; [ $LOAD ] || LOAD=._null
NOLOAD=`value noload | sed 's/;/|/g'`; [ $NOLOAD ] || NOLOAD=._null
EXTRAMOD=`value extramod | sed 's/;/ /g'`
RAMMOD=`value rammod | sed 's/;/|/g'`
ROOTCOPY=`value rootcopy`
FOLDER=porteus
livedbg=/var/log/livedbg
LOGFILE=/var/log/boot.log

## Let's start!
mount -nt devtmpfs none /dev

# Perform filesystem check:
if param fsck; then
	echo $i"performing linux filesystem check on all available devices:"
	sleep 3
	draw
	for x in `blkid | grep 'TYPE="ext' | cut -d: -f1`; do e2fsck -C 0 -p $x; wait; done
	draw
fi

# Create /etc/fstab and mount devices:
fstab

debug
# Find *.cfg file:
echo $i"searching for $CFG file"
if [ $IP ]; then BOOTDEV=network; CFGDEV=/mnt/nfs
	for x in `lspci | grep 0200: | cut -d: -f3-4 | sed s/:/.*/g | tr a-z A-Z`; do modprobe `grep $x /lib/modules/$(uname -r)/modules.alias | tail -n1 | rev | cut -d" " -f1 | rev` 2>/dev/null; done
	ls /sys/class/net | grep -q eth || { for x in `find /lib/modules/$(uname -r)/kernel/drivers/net -name "*.ko" | sed 's/.ko//g'`; do modprobe `basename $x` 2>/dev/null; ls /sys/class/net | grep -q eth && break || modprobe -r `basename $x` 2>/dev/null; done; }
	mkdir -p /mnt/nfs/porteus /mnt/nfs/storage; udhcpc; modprobe nfsv4; mount -t nfs4 $IP:/srv/pxe/porteus /mnt/nfs/porteus -o ro,nolock 2>/dev/null || { modprobe nfsv3; mount -t nfs $IP:/srv/pxe/porteus /mnt/nfs/porteus -o ro,nolock 2>/dev/null; }
	MAC=`ifconfig | grep eth0 | cut -d: -f5-7 | sed s/://g | cut -d" " -f1`
	if [ "$CHANGES" = /srv/pxe/storage ]; then
		if lsmod | grep -q nfsv3; then
			mount -t nfs $IP:/srv/pxe/storage /mnt/nfs/storage -o rw,nolock 2>/dev/null && { mkdir -p /mnt/nfs/storage/client-$MAC/changes/home; CHANGES="/storage/client-$MAC"; }
		else
			mount -t nfs4 $IP:/srv/pxe/storage /mnt/nfs/storage -o rw,nolock 2>/dev/null && { mkdir -p /mnt/nfs/storage/client-$MAC/changes/home; CHANGES="/storage/client-$MAC"; }
		fi
	fi
elif [ $ISO ]; then CFGDEV=/mnt/isoloop
	locate -e $FROM && { BOOTDEV=/mnt/$DEV; mkdir /mnt/isoloop; mount -o loop /mnt/$DEV/$LPTH /mnt/isoloop; ISOSRC=/mnt/$DEV/$LPTH; }
else
	if [ $FROM ]; then
		locate -e $FROM/porteus/$CFG
		if [ $? -eq 0 ]; then
			DIR=`echo $LPTH | rev | cut -d/ -f3- | rev`; [ $DIR ] && FOLDER=$DIR/porteus
		else
			echo -e "${YELLOW}from= cheatcode is incorrect, press enter to search through all devices${RST}"
			read -s; search -e porteus/$CFG
		fi
	else
		search -e porteus/$CFG || lazy -e porteus/$CFG
	fi
	CFGDEV=/mnt/$DEV
fi

[ -e $CFGDEV/$FOLDER/$CFG ] && PTH=$CFGDEV/$FOLDER || . fatal

# Set some variables to export as environment variables
DELAY=`value delay`; [ $DELAY ] && { echo $i"delaying $DELAY sec (waiting for slow devices to settle)"; sleep $DELAY; }
BOOTDEV=$CFGDEV
MODDIR=$PTH/modules
BASEDIR=${PTH%/*}
PORTDIR=$PTH
PORTCFG=$CFGDEV/$FOLDER/$CFG
echo $i"using Porteus data from $PTH"

# Make all drivers available:
mount -o loop $PTH/base/000-kernel.xzm /opt/000-kernel 2>/dev/null
mount -o bind /opt/000-kernel/lib/modules /lib/modules 2>/dev/null

# Create symlinks used often by porteus scripts:
if [ $CFGDEV = /mnt/nfs -o $CFGDEV = /mnt/isoloop ];then
	ln -sf /mnt/live$PTH/modules  /porteus/modules
	ln -sf /mnt/live$PTH/optional /porteus/optional
else
	ln -sf $PTH/modules  /porteus/modules
	ln -sf $PTH/optional /porteus/optional
fi

debug
# Setup changes:
if [ $CHANGES ]; then
	echo $i"setting up directory for changes"
	CHNEXIT=`echo $CHANGES | cut -d: -f1`; [ $CHNEXIT = EXIT ] && CHANGES=`echo $CHANGES | cut -d: -f2-`
	[ -r $CFGDEV/$CHANGES ] && { DEV=`echo $CFGDEV | sed s@/mnt/@@`; LPTH=$CHANGES; } || locate -r $CHANGES
	if [ $? -eq 0 ]; then
		if [ -d /mnt/$DEV/$LPTH ]; then
			mkdir -p /mnt/$DEV/$LPTH/changes 2>/dev/null && \
			mount -o bind /mnt/$DEV/$LPTH/changes /memory/changes && touch /memory/changes/._test1 2>/dev/null
		else
			if blkid /mnt/$DEV/$LPTH 2>/dev/null | cut -d" " -f3- | grep -q _LUKS; then
				for x in dm_crypt cryptd cbc sha256_generic aes_generic aes_x86_64; do modprobe $x 2>/dev/null; done
				losetup /dev/loop2 /mnt/$DEV/$LPTH
				echo $i"found encrypted .dat container"
				/opt/000-kernel/sbin/cryptsetup luksOpen /dev/loop2 crypt
				fsck_dat /dev/mapper/crypt
				mount /dev/mapper/crypt /memory/changes 2>/dev/null && touch /memory/changes/._test1 2>/dev/null
			else
				fsck_dat /mnt/$DEV/$LPTH
				mount -o loop /mnt/$DEV/$LPTH /memory/changes 2>/dev/null && touch /memory/changes/._test1 2>/dev/null
			fi
		fi
		if [ $? -eq 0 ]; then
			echo $i"testing filesystem on "$CHANGES" for posix compatibility"
			ln -s /memory/changes/._test1 /memory/changes/._test2 2>/dev/null && chmod +x /memory/changes/._test1 2>/dev/null && [ -x /memory/changes/._test1 ] && chmod -x /memory/changes/._test1 2>/dev/null && [ ! -x /memory/changes/._test1 ] && rm -f /memory/changes/._test1 /memory/changes/._test2
			if [ $? -ne 0 ]; then
				rm -f /memory/changes/._test1 /memory/changes/._test2; umount /memory/changes
				echo && echo -e """A Windows filesystem (FAT, NTFS) or other non-posix compatible filesystem\nhas been detected on $CHANGES.\nYour changes cannot be saved directly to the specified storage media with this\nsetup. Please use the 'Porteus save file manager' to create a .dat container\nand use it for saving your changes after your next reboot."""
				echo "boot will continue in 'Always Fresh' mode for this session"
				sleep 10; CHGERR=1; rmdir /mnt/$DEV/$LPTH/changes; fail_chn
			else
				echo $i"filesystem is posix compatible"; CHNDEV=/mnt/$DEV
				rmdir /memory/changes/mnt/* 2>/dev/null
				rm -rf /memory/changes/var/lock/* /var/run/laptop-mode-tools/* /var/spool/cron/cron.??????
				for x in `find /memory/changes/var/run -name "*pid" 2>/dev/null`; do rm $x; done
				if [ $CHNEXIT = EXIT -o "`egrep -o " changes-ro( |\$)" /proc/cmdline`" ]; then
					CHNEXIT=$CHNDEV/$LPTH; echo $CHNEXIT >/tmp/changes-exit
					param changes-ro && echo $i"""changes will not be saved for this session""" || echo $i"""changes will be saved only during reboot/shutdown"""
					for x in `find /memory/changes -name ".wh.*" | grep -v '.wh..wh..opq' | tr ' ' '@' `; do x=`echo $x | tr '@' ' ' `; cp -a --parents "$x" /var; done
					umount /memory/changes; mount -nt tmpfs -o size=$RAMSIZE tmpfs /memory/changes
					## should not be needed with new busybox ## need to fix busybox bug on 'cp -a' as it does not preserve perms on dirs:
					mv /var/memory/changes/* /memory/changes 2>/dev/null; CHANGES=memory
					#chown -R guest /memory/changes/home/guest 2>/dev/null
				fi
			fi
		else
			echo $i"changes not writable, using memory instead"; CHGERR=2; umount /memory/changes 2>/dev/null; fail_chn
		fi
	else
		CHGERR=3; fail $CHANGES; fail_chn
	fi
else
	 echo $i"changes cheatcode not found, using memory only"; fail_chn
fi

mkdir -p /memory/changes/mnt/live

debug
# Setup aufs:
echo $i"creating live filesystem and inserting modules"
mount -t aufs -o nowarn_perm,xino=/memory/xino/.aufs.xino,br:/memory/changes=rw aufs /union
if [ $? -ne 0 ]; then echo -e """cant setup union (aufs) - read only filesystem?\nWhen you finish debugging press Ctrl+Alt+Del to reboot."""; sh; fi

# Check for base modules update
if [ -d $CFGDEV/$FOLDER/updates ]; then
	echo $i"""Base updates detected ..."""
	# Check if user has booted over network or from ISO
	if [ $ISO ]||[ $IP ]||[ "$CFGDEV" = /mnt/isoloop ]; then
		echo $i"Booting from ISO or network detected. Skipping updates ..."
	else
		if is_writable $CFGDEV/$FOLDER/base; then
			echo $i"Copying base updates ... please wait"
			basemods=`ls -1 $CFGDEV/$FOLDER/updates | grep "^00[1-3]-[a-z|A-Z]"`
			for x in $basemods; do
				echo "Copying: $x to $CFGDEV/$FOLDER/porteus/"
				cp $CFGDEV/$FOLDER/updates/$x $CFGDEV/$FOLDER/base/ 2>/dev/null
				rm $CFGDEV/$FOLDER/updates/$x
			done
			## Allow time slow devices?
			echo "Pausing, to allow the dust to settle.. please wait"
			sleep 10
			rm -rf $CFGDEV/$FOLDER/updates && echo "Removed updates folder" || echo "Could not remove updates folder"
		else
			echo $i"Non writable media. Skipping updates ..."
		fi
	fi
fi

# Find modules:
find $PTH/base $PTH/modules -name "*.xzm" 2>/dev/null | egrep -ve "$NOLOAD" | sort >/tmp/modules
find $PTH/optional -name "*.xzm" 2>/dev/null | egrep -e "$LOAD" | sort >>/tmp/modules

if param vga_detect; then
	echo $i"detecting GPU"
	lspci >/tmp/lspci; nv=`grep "0300: 10de:" /tmp/lspci | cut -d":" -f4`; amd=`grep "0300: 1002:" /tmp/lspci | cut -d":" -f4`
	if [ $nv ]; then
		echo $i"$nv chipset found, checking which nvidia driver supports it"
		cd /usr/share/pciids/NVIDIA; NV=`grep $nv * | cut -d: -f1`; cd /
		if [ $NV ]; then
			echo $i"nvidia-$NV.xx driver will be activated -"
			echo $i"if present in $PTH/optional folder"
			find $PTH/optional -name "nvidia-$NV*" 2>/dev/null >>/tmp/modules
		else
			echo $i"latest nvidia driver will be activated -"
			echo $i"if present in $PTH/optional folder"
			find $PTH/optional -name "nvidia-*" 2>/dev/null | egrep -v '96.43.|173.14.|304.' >>/tmp/modules
		fi
	elif [ $amd ]; then
		echo $i"checking if $amd GPU belongs to 'Radeon HD' series"
		cd /usr/share/pciids/AMD; HD=`grep $amd * | cut -d: -f1`; cd /
		if [ $HD ]; then
			echo $i"$HD-xx driver will be activated -"
			echo $i"if present in $PTH/optional folder"
			find $PTH/optional -name "$HD-*" 2>/dev/null >>/tmp/modules
		else
			echo $i"$amd GPU is not supported by amd-catalyst driver - refusing activation"
		fi
	else
		echo $i"could not find any nVidia/AMD GPU on this PC"
	fi
fi

if param base_only; then
	grep base/0 /tmp/modules > /tmp/mod
	mv /tmp/mod /tmp/modules
else
	if [ "$EXTRAMOD" ]; then
	for folder in $EXTRAMOD; do
		echo $i"searching for additional modules in $folder"
		locate -d $folder && { find /mnt/$DEV/$LPTH -name "*.xzm" 2>/dev/null | egrep -ve "$NOLOAD" | sort >>/tmp/modules; } || fail $folder
	done
	fi
fi

# Copy data to RAM:
if param copy2ram; then
	echo $i"copying Porteus data to RAM, this may take some time..."
	[ $RAMMOD ] && { egrep -e "$RAMMOD" /tmp/modules > /tmp/rammod; cpmod /tmp/rammod; } || cpmod /tmp/modules
fi

# Populate aufs with modules:
umount /lib/modules /opt/000-kernel 2>/dev/null
while read line; do
	NAME=`basename "$line"`
	mkdir /memory/images/"$NAME"
	mount -o loop "$line" /memory/images/"$NAME" 2>/dev/null
	if [ $? -eq 0 ]; then
		echo "  $m  $NAME"; mount -no remount,add=1:/memory/images/"$NAME"=rr aufs /union
	else
		echo $i"""Cannot read $NAME - corrupted module?"""; rmdir /memory/images/"$NAME"
	fi
done < /tmp/modules
mount -no bind /union/lib/modules /lib/modules 2>/dev/null

# Add "changes on exit" device/file/folder:
if [ -e /tmp/changes-exit ]; then
	mkdir /memory/images/changes
	if [ -d $CHNEXIT ]; then
		mount -o bind $CHNEXIT/changes /memory/images/changes
	elif [ -b /dev/mapper/crypt ]; then
		mount /dev/mapper/crypt /memory/images/changes
	else
		mount -o loop $CHNEXIT /memory/images/changes
	fi
	echo "  $m  changes"; mount -no remount,add=1:/memory/images/changes=ro aufs /union
	echo $CHNEXIT/changes >>/tmp/modules
	param changes-ro && rm /tmp/changes-exit
fi

debug
# Copy /rootcopy folder:
if param norootcopy; then
	ROOTCOPY=none
	echo $i"skipping /rootcopy directory"
else
	if [ $ROOTCOPY ]; then
		locate -d $ROOTCOPY
		if [ $? -eq 0 ]; then echo $i"copying content of $ROOTCOPY directory"; cp -af /mnt/$DEV/$LPTH/. /union/. 2>/dev/null; else fail $ROOTCOPY; ROOTCOPY=none; fi
	else
		ROOTCOPY=none
		echo $i"copying content of $PTH/rootcopy directory"
		cp -af $PTH/rootcopy/. /union/. 2>/dev/null
	fi
fi

uep=/union/etc/profile.d
uepp=$uep/porteus.sh
[ ! -d $uep ] && mkdir -p $uep
#[ ! `grep -o "DISTRO=" $uepp >/dev/null 2>&1` ] && echo "export DISTRO=porteus" >> $uepp
if [ -e $uepp ]; then
    sed -i '/DISTRO/d' $uepp
	sed -i '/BOOTDEV/d' $uepp
	sed -i '/BASEDIR/d' $uepp
	sed -i '/PORTDIR/d' $uepp
	sed -i '/MODDIR/d' $uepp
	sed -i '/CHGERR/d' $uepp
	sed -i '/PORTCFG/d' $uepp
fi
echo "export DISTRO=porteus" >> $uepp
echo "export BOOTDEV=$CFGDEV" >> $uepp
echo "export BASEDIR=${PTH%/*}" >> $uepp
echo "export PORTDIR=$PTH" >> $uepp
echo "export MODDIR=$MODDIR" >> $uepp
echo "export PORTCFG=$PORTCFG" >> $uepp
if [ $CHGERR ]; then echo "export CHGERR=$CHGERR" >> $uepp; fi
chmod +x $uepp

# Collect boot arguments
grep "^[aA0-zZ9]" $PORTCFG > /union/etc/bootcmd.cfg
cat /proc/cmdline | tr ' ' '\n' >> /union/etc/bootcmd.cfg

## Finish:
# Create 7 free loop devices for truecrypt, etc...
#x=`losetup | tail -n1 | cut -d: -f1 | sed 's@/dev/loop@@'`
x=`grep -oE 'loop[0-9]+$' /proc/partitions  | tail -n1 | tr -d [:alpha:]`
let y=x+7
while [ $x -le $y ]; do [ -b /dev/loop$y ] && break || mknod /dev/loop$y b 7 $y; let y=y-1; done

if param nonetwork; then
	echo $i"disabling dhcpcd and NetworkManager services"
	chmod -x /union/etc/rc.d/rc.inet1 /union/etc/rc.d/rc.networkmanager 2>/dev/null
	nma=/union/etc/xdg/autostart/nm-applet.desktop
	test -e $nma && ! grep -q "Hidden=true" $nma && echo "Hidden=true" >> $nma
fi

if [ "$IP" -a -x /union/etc/rc.d/rc.networkmanager ]; then
	#if [ -z "`egrep -o "copy2ram( |\$)" /proc/cmdline`" -o -d /mnt/nfs/storage/client-$MAC ]; then
	if [ -z "`egrep -o "^copy2ram" /union/etc/bootcmd.cfg`" -o -d /mnt/nfs/storage/client-$MAC ]; then
		echo "nameserver $IP" > /union/etc/resolv.conf
		nmc=/union/etc/NetworkManager/NetworkManager.conf; HW=`ifconfig | grep eth0 | cut -dW -f2 | cut -d" " -f2`
		! grep -q "unmanaged-devices=mac:$HW" $nmc && sed -i '/\[keyfile\]/ a\unmanaged-devices=mac:'$HW'' $nmc
	fi
fi

# Start bluetooth if cheat exists
if param bluetooth; then
	echo "Starting bluetooth ..."
	[ -e /union/etc/rc.d/rc.bluetooth ] && chmod +x /union/etc/rc.d/rc.bluetooth
fi

cp -af /dev/console /union/dev
#cat > /union/etc/mtab << EOF
#aufs / aufs rw 0 0
#proc /proc proc rw 0 0
#sysfs /sys sysfs rw 0 0
#devtmpfs /dev devtmpfs rw 0 0
#devpts /dev/pts devpts rw,mode=0620,gid=5 0 0
#EOF

fstab

debug
#if param copy2ram; then
if chk_bootcfg copy2ram; then
echo $i"""finished copying to RAM - booting media can be removed safely"""
fi

# Create debug file:
[ -e /tmp/devices ] && { echo "# Recognized devices:" >$livedbg; cat /tmp/devices >>$livedbg; }
[ $BOOTDEV ] && CFGDEV=$BOOTDEV
echo -e "\n# Booting device:\n$CFGDEV\n\n# Porteus data found in:\n$PTH\n\n# Changes are stored in:\n$CHANGES\n\n# Non standard /rootcopy dir:\n$ROOTCOPY\n\n# Modules activated during boot time:" >>$livedbg; cat /tmp/modules >>$livedbg
echo -e "\n# Base module versions:" >>$livedbg; cat /union/etc/porteus/* /etc/porteus/* >>$livedbg
grep "^/mnt/isoloop" $livedbg && echo "" >> $livedbg && echo "ISO=$ISOSRC" >> $livedbg
#if [ $ISO ]; then
#	[ -d /union/mnt/isoloop ] && rmdir /union/mnt/isoloop
#	ln -sf /mnt/live/mnt/isoloop /union/mnt/isoloop
#fi
cp -af $livedbg /union/var/log/porteus-livedbg

## Check for text mode cheat code
chk_bootcfg 3 && export OPTS="3"

echo $i"changing root directory"

# Cleanup mount points at union/mnt for this session
for x in `ls -1 /union/mnt | grep -v "live"`; do
 [ -d /union/mnt/$x ] && rmdir /union/mnt/$x # Will fail if dir not empty!
 [ -L /union/mnt/$x ] && rm -f /union/mnt/$x
done

if chk_bootcfg noauto; then
	for x in `grep /mnt/ /etc/fstab | cut -d/ -f3`; do mkdir -p /union/mnt/$x; umount -n /mnt/$x 2>/dev/null && rmdir /mnt/$x; done
else
	#grep /mnt/ /etc/fstab >> /union/etc/mtab
	for x in `grep /mnt/ /etc/fstab | cut -d/ -f3`; do mkdir -p /union/mnt/$x; mount -n --move /mnt/$x /union/mnt/$x; rmdir /mnt/$x; done
fi

sed -i 's/ ntfs / ntfs-3g /g' /etc/fstab
cp -f /etc/fstab /union/etc 2>/dev/null

# Add all symlinks of all mount points at /mnt to union
for x in `ls -1 /mnt`; do
 [ -d /union/mnt/$x ] && rmdir /union/mnt/$x
 ln -sf /mnt/live/mnt/$x /union/mnt/$x
done

cp -r /etc/porteus /union/etc 2>/dev/null
umount -n /lib/modules 2>/dev/null
rm -r /lib/* /usr/*

debug

echo """live system is ready now - starting Porteus"""
cp -f /union/sbin/init /bin
if [ $? -eq 0 ]; then
	pivot_root /union  /union/mnt/live
	exec bin/chroot . /mnt/live/bin/init "$@" $OPTS <dev/console >dev/console 2>&1
else
	echo -e """!!ERROR!!\nSomething went wrong and I cannot continue.\nPress Ctrl+Alt+Del to reboot."""
	sh
fi

Appendix 2: the full init script of SalixOS (extracted from initrd.gz)

#!/bin/ash
#Dimitris Tzemos <dijemos@gmail.com> (changes for salix)
# Eric Hameleers <alien@slackware.com> (encryption support)

LIVELABEL="LIVE" #edit in build-slackware-live.sh script too
#DISTRONAME="Salix-kde-15.0"
DISTRONAME="Salix-xfce-15.0"
#DISTRONAME="Salix-mate-15.0"

if [ ! -z "$medialabel" ]; then
	LIVELABEL=$medialabel
fi

echo ""
echo "*** Live system initialization ***"
mount -v proc /proc -t proc
mount -v sysfs /sys -t sysfs

/load_kernel_modules 2>/dev/null
# Sometimes the devices need extra time to be available.
# A root filesystem on USB is a good example of that.
if [ ! -z "$wait" ]; then
	sleep $wait
else
	sleep 3
fi

# Fire at least one blkid:
#blkid 1>/dev/null 2>/dev/null

mdev -s

mkdir /tmp #fuse needs /tmp

mkdir /slroot
mount -o defaults -t tmpfs none /slroot #-o defaults: bug in busybox (options=0)
mkdir /slroot/live

mkdir /slroot/live/media
mediadetected="none"
if [ ! -z "$nfsroot" ]; then
	mediadetected="nfs"
	address=`echo $ip | cut -f1 -d:`
	netmask=`echo $ip | cut -f4 -d:`
	gateway=`echo $ip | cut -f3 -d:`
	ifconfig eth0 $address netmask $netmask
	route add default gw $gateway
	mount -t nfs -o nolock $nfsroot /slroot/live/media
	echo "$LIVELABEL found on $nfsroot"
else
	sleeptime=0
	while [ "$mediadetected" = "none" ] && [ "$sleeptime" != "10" ]; do #try each seconds, but don't wait (USB) more than 10 seconds
		if blkid | grep -q "LABEL=\"$LIVELABEL\""; then
			livedevice=`blkid | grep "LABEL=\"$LIVELABEL\"" | sed -n 1p | cut -f1 -d:`
			echo "$DISTRONAME-Live found on $livedevice"
			mount -o ro $livedevice /slroot/live/media
			if blkid | grep "LABEL=\"$LIVELABEL\"" | sed -n 1p | grep -q "TYPE=\"iso9660\""
			then mediadetected="cd"
			else mediadetected="sd"
			fi
		else
			sleep 1
			let sleeptime+=1
			mdev -s
		fi
	done
fi

if [ "$mediadetected" == "none" ]; then
	echo "*** Live system error - live media not detected - exiting ***"
	sh
fi

##copy live-media to RAM if requested
if [ "$copy2ram" = "yes" ]; then
	echo -n "Copying live system to RAM ..."
	mkdir /slroot/live/tmp
	mount --move /slroot/live/media /slroot/live/tmp
	mount -t tmpfs none /slroot/live/media

	mkdir /slroot/live/media/boot
	for bootfile in `find /slroot/live/tmp/boot -maxdepth 1 -type f`; do
		cp $bootfile /slroot/live/media/boot/
	done
	cp -r /slroot/live/tmp/boot/syslinux /slroot/live/media/boot/
	touch /slroot/live/media/boot/
	mkdir /slroot/live/media/boot/modules
	if [ `ls /slroot/live/tmp/boot/modules/ | wc -l` != 0 ]; then
		for module in /slroot/live/tmp/boot/modules/*; do #first copy main non excluded modules
			modulename=`basename $module`
			if ! echo $exclude | sed 's/:/\n/g' | grep -q "^$modulename$"; then
				cp /slroot/live/tmp/boot/modules/$modulename /slroot/live/media/boot/modules/
			fi
		done
	fi
	mkdir /slroot/live/media/boot/optional
	for modulename in `echo $include | sed 's/:/ /g'`; do #copy included optional modules
		if [ -f /slroot/live/tmp/boot/optional/$modulename ]; then
			cp /slroot/live/tmp/boot/optional/$modulename /slroot/live/media/boot/optional/
		fi
	done

	umount /slroot/live/tmp
	rmdir /slroot/live/tmp
	mount -o remount,ro /slroot/live/media
	if [ "$mediadetected" = "cd" ]; then
		eject $livedevice
	fi
	echo " done"
fi

#mount all modules in /live/modules/* except excluded ones
mkdir /slroot/live/modules
modulescount=0
if [ `ls /slroot/live/media/boot/modules/ | wc -l` != 0 ]; then
	for module in /slroot/live/media/boot/modules/*; do
		modulename=`basename $module`
		if ! echo $exclude | sed 's/:/\n/g' | grep -q "^$modulename$"; then #if module is not excluded by bootparam
			mkdir /slroot/live/modules/$modulename
			mount -o loop -t squashfs $module /slroot/live/modules/$modulename
			modulesbranches="/slroot/live/modules/$modulename=ro:$modulesbranches"
			echo "Loading SquashFS module $modulename"
			modulescount=$(($modulescount+1))
		fi
	done
fi

#load requested optional modules
for modulename in `echo $include | sed 's/:/ /g'`; do
	if [ -f /slroot/live/media/boot/optional/$modulename ]; then
		mkdir /slroot/live/modules/$modulename
		mount -o loop -t squashfs /slroot/live/media/boot/optional/$modulename /slroot/live/modules/$modulename
		modulesbranches="/slroot/live/modules/$modulename=ro:$modulesbranches"
		echo "Loading SquashFS module $modulename"
		modulescount=$(($modulescount+1))
	fi
done

if [ $modulescount = 0 ]; then
	echo -e "\nError: no modules has been loaded"
	exec sh
fi

modulesbranches=`echo $modulesbranches | sed 's/:$//'`
unionfsmodulesbranches=`echo $modulesbranches | sed 's/\/slroot//g'` #TODO: Unionfs-
overlaymodulesbranches=`echo $modulesbranches | sed 's/=ro//g'`

#mount (union) all modules in /live/system (ro)
mkdir /slroot/live/system
if [ $modulescount = 1 ]
then mount --bind $overlaymodulesbranches /slroot/live/system
else mount -t overlay -o ro,lowerdir=$overlaymodulesbranches overlay /slroot/live/system 2>/dev/null ||
		unionfs -o ro,allow_other,suid,dev,use_ino,cow,max_files=524288,chroot=/slroot $unionfsmodulesbranches /slroot/live/system #TODO - (required for NFS)
fi

find_loop() {
    # The losetup of busybox is different from the real losetup - watch out!
    lodev=$(losetup -f)
    if [ -z "$lodev" ]; then
      # We exhausted the available loop devices, so create the block device:
      for NOD in $(seq 0 ${MAXLOOPS}); do
        if [ ! -b /dev/loop${NOD} ]; then
          mknod -m660 /dev/loop${NOD} b 7 ${NOD}
          break
        fi
      done
      lodev=/dev/loop${NOD}
    elif [ ! -b $lodev ]; then
      # We exhausted the available loop devices, so create the block device:
      mknod -m660 $lodev b 7 $(echo $lodev |sed %/dev/loop%%)
    fi
    echo "$lodev"
  }

#setup persistent system changes
if [ ! -z "$changes" ] && [ ! "$copy2ram" == "yes" ] && [ "$mediadetected" != "cd" ]; then
		echo "Using persistent system storage ($changes)"
	if [ "$mediadetected" == "sd" ]; then
		mkdir -p /slroot/live/$changes
		if echo $changes | grep -q "="; then
			filesize=`echo "$changes" | cut -f2 -d=`
			changes=`echo "$changes" | cut -f1 -d=`
		fi
		if [ ! -d /slroot/live/media/$changes ] && [ ! -f /slroot/live/media/$changes ]; then #storage space creation
			#if  [ ! -z "$filesize" ]; then
			#	echo "Creating $changes ($filesize MB) persistent system storage ..."
			#	dd if=/dev/zero of=/slroot/live/media/$changes bs=1024k count=0 seek=$filesize #TODO: check available space
			#	sleep 1
			#	mkfs.ext3 -F /slroot/live/media/$changes
			#	sleep 1
			#else
				if mount | grep -q "$livedevice on /slroot/live/media type ext3"; then #TODO: check FS type
				echo "mount $livedevice on /slroot/live/media type ext3"
				mount -o remount,rw /slroot/live/media
				mkdir -p /slroot/live/media/$changes
				fi
			#fi
		fi
		if [ -f /slroot/live/media/$changes ];	then ## setup persistent file
			mount -o remount,rw /slroot/live/media
			## add these here for persistent
			# Find a free loop device to mount the persistence container file:
			prdev=$(find_loop)
			losetup $prdev /slroot/live/media/$changes
			# Check if the persistence container is LUKS encrypted:
			if cryptsetup isLuks $prdev 1>/dev/null 2>/dev/null ; then
				echo "Unlocking LUKS encrypted persistence file '/${changes}'"
				cryptsetup luksOpen $prdev $(basename ${changes}) </dev/tty0 >/dev/tty0 2>&1
				if [ $? -ne 0 ]; then
					echo "${DISTRONAME}:  Failed to unlock persistence file '/${changes}'."
					echo "${DISTRONAME}:  Falling back to RAM."
				else
				# LUKS properly unlocked; from now on use the mapper device instead:
				prdev=/dev/mapper/$(basename ${changes})
				fi
			fi
			prfs=$(blkid $prdev |rev |cut -d'"' -f2 |rev)
			mount -t $prfs $prdev /slroot/live/$changes 2>/dev/null
			##
			#mount -o loop /slroot/live/media/$changes /slroot/live/$changes
		fi

		if	[ -d /slroot/live/media/$changes ]; then ## setup persistent folder only for ext3,4 usb filesystem
			mount -o remount,rw /slroot/live/media
			mount --bind /slroot/live/media/$changes /slroot/live/$changes
		fi
	else
		echo "Using persistent system storage ($changes)"
		mkdir -p /slroot/live/$changes
		mount -t nfs -o nolock $changes /slroot/live/$changes #TODO: no auth / security
	fi
fi

#mount (union) all modules (ro) and /live/changes (rw) in /live/union (rw)
if [ ! -z "$changes" ]; then
	UPPERDIR=/slroot/live/$changes/changes
	WORKOVLDIR=/slroot/live/$changes/ofswd
else
	UPPERDIR=/slroot/live/changes
	WORKOVLDIR=/slroot/live/ofswd
fi
mkdir -p /slroot/live/union
[ ! -d ${UPPERDIR} ] && mkdir -p ${UPPERDIR}
[ ! -d ${WORKOVLDIR} ] && mkdir -p ${WORKOVLDIR}
mount -t overlay -o workdir=${WORKOVLDIR},upperdir=${UPPERDIR},lowerdir=$overlaymodulesbranches overlay /slroot/live/union 2>/dev/null ||
	unionfs -o allow_other,suid,dev,use_ino,cow,max_files=524288,chroot=/slroot /live/changes=rw:$unionfsmodulesbranches /slroot/live/union #TODO - (required for NFS)

#setup system tree
for directory in /slroot/live/union/*; do #bind /live/union top directories into /
	directoryname=`basename $directory`
	mkdir /slroot/$directoryname
	mount --bind $directory /slroot/$directoryname
done
mkdir -p /slroot/tmp
mkdir -p /slroot/sys
mkdir -p /slroot/proc
mkdir -p /slroot/dev
mknod /slroot/dev/console c 5 1 2>/dev/null
mknod /slroot/dev/null c 1 3 2>/dev/null #needed to mount /proc (rc.S) on read-only filesystem
cat > /slroot/live/union/etc/fstab << END
proc      /proc       proc        defaults   0   0
sysfs     /sys        sysfs       defaults   0   0
tmpfs     /tmp        tmpfs       defaults,nodev,nosuid,mode=1777  0   0
tmpfs     /var/tmp    tmpfs       defaults,nodev,nosuid,mode=1777  0   0
tmpfs     /dev/shm    tmpfs       defaults,nodev,nosuid,mode=1777  0   0
devpts    /dev/pts    devpts      gid=5,mode=620   0   0
none      /           tmpfs       defaults   1   1

END

#system startup tweaking
if [ ! -z "$changes" ]; then
	#cp -f /slroot/live/system/etc/rc.d/rc.S /slroot/live/union/etc/rc.d/
	#cp -f /slroot/live/system/etc/rc.d/rc.M /slroot/live/union/etc/rc.d/
	#cp -f /slroot/live/system/etc/rc.d/rc.modules /slroot/live/union/etc/rc.d/
	rm -f /slroot/live/union/etc/udev/rules.d/70-persistent-net.rules
fi

touch /slroot/live/union/etc/fastboot
# Disable the root filesystem check altogether:
sed -i -e '/^if \[ ! \$READWRITE = yes/,/^fi # Done checking root filesystem/s/^/#/' /slroot/live/union/etc/rc.d/rc.S
echo "cat /proc/mounts | grep -v '^rootfs' > /etc/mtab" >> /slroot/live/union/etc/rc.d/rc.S #update /etc/mtab
echo "mount -o remount,rw /live/media" >> /slroot/live/union/etc/rc.d/rc.S
if [ "$fastboot" = "yes" ]; then #faster startup (actions already done by "build-slackware-live.sh --sysprep")
	echo "Enabling fast boot"
	sed	-e 's@^\( */usr/bin/fc-cache.*\)$@: #\1@' \
		-e 's@^\( */bin/sh /etc/rc.d/rc.udev\)$@: #\1@' \
		-e 's@^\( */usr/bin/gtk-update.*\)$@: #\1@' \
		-e 's@^\( */usr/bin/update-gdk.*\)$@: #\1@' \
		-e 's@^\( */usr/bin/update-gtk.*\)$@: #\1@' \
		-e 's@^\( */usr/bin/update-mime-database.*\)$@: #\1@' \
		-e 's@^\( */usr/bin/update-pango.*\)$@: #\1@' \
		-e 's@^\( */usr/bin/glib-compile-schemas.*\)$@: #\1@' \
		-e 's@^\( */sbin/ldconfig.*\)$@: #\1@' \
		-i /slroot/live/union/etc/rc.d/rc.M
	sed	-e 's@^\( */sbin/depmod.*\)$@: #\1@' \
		-i /slroot/live/union/etc/rc.d/rc.modules
fi
if [ "$mediadetected" = "nfs" ] && [ -x /slroot/live/union/etc/rc.d/rc.networkmanager ]; then
	chmod -x /slroot/live/union/etc/rc.d/rc.networkmanager #if enabled, system hangs
elif ! [ -x /slroot/live/union/etc/rc.d/rc.wicd -a -x /slroot/live/union/usr/sbin/wicd ]; then
	chmod +x /slroot/live/union/etc/rc.d/rc.networkmanager
fi

#system shutdown tweaking
#if [ ! -z "$changes" ]; then
	#cp -f /slroot/live/system/etc/rc.d/rc.6 /slroot/live/union/etc/rc.d/
#fi
sed -i 's/\(\/rc.[06]\)\( fast\)*/\1 fast/' /slroot/live/union/etc/inittab #to prevent system hang at shutdown
sed -e 's@^\( *\)\([a-z/]*sleep.*\)@\1: #\2@' \
	-e 's@^\( *\)\([a-z/]*sync\)@\1: #\2@' \
	-e 's@^\(.*umount.*\)$@\1 \>/dev/null 2\>\&1@' \
	-i /slroot/live/union/etc/rc.d/rc.6
sed -e 's@^\(.*umount -v -a -t no,proc,sysfs.*\)$@umount -v -a -t no,overlay,unionfs,proc,sysfs \>/dev/null 2\>\&1@' -i /slroot/live/union/etc/rc.d/rc.6
sed -e 's@^\(.*umount -v -a -l -f -r -t nfs,smbfs,cifs.*\)$@umount -v -a -l -f -r -t nfs,smbfs,cifs \>/dev/null 2\>\&1@' -i /slroot/live/union/etc/rc.d/rc.6
sed -e 's@^\(.*umount -v -a -t no,proc,sysfs.*\)$@umount -v -a -t no,proc,sysfs \>/dev/null 2\>\&1@' -i /slroot/live/union/etc/rc.d/rc.6

if [ -z "$changes" ]
then sed -e 's@^\(.*-o remount.*\)$@/bin/mount -o remount,ro /live/media \>/dev/null 2\>\&1 #\1\n/bin/sync\n/bin/sleep 3@' -i /slroot/live/union/etc/rc.d/rc.6
else sed -e 's@^\(.*-o remount.*\)$@/bin/mount -o remount,ro /live/media \>/dev/null 2\>\&1\nmount -o remount,ro /live/changes \>/dev/null 2\>\&1 #\1\nsync; sleep 3@' \
	-i /slroot/live/union/etc/rc.d/rc.6
fi
if [ "$mediadetected" = "nfs" ]; then
	sed -e 's@^\( *\)\([a-z/]*fuser.*\)@\1: #\2@' \
		-e 's@^\( *\)\([a-z/]*dhcpcd -k.*\)@\1: #\2@' \
		-e 's@^\( *\)\(\. /etc/rc\.d/rc\.inet1 stop\)@\1: #\2@' \
		-e 's@-t \(nfs,\)\(.*\)@-t \2 #\1@' \
		-i /slroot/live/union/etc/rc.d/rc.6
fi
if [ "$mediadetected" = "cd" ] && [ ! "$copy2ram" = "yes" ]; then
	sed	-e "s@\(/sbin/reboot\)@reboot -w; shutdown -k now \>/dev/null\n  cdrecord --eject dev=$livedevice \>/dev/null 2\>\&1\n  sleep 5\n  \1@" \
		-e "s@\(/sbin/poweroff\)@poweroff -w; shutdown -k now \>/dev/null\n  cdrecord --eject dev=$livedevice \>/dev/null 2\>\&1\n  sleep 5\n  \1@" \
		-i /slroot/live/union/etc/rc.d/rc.6
fi

#setup persistent homedir
if [ ! -z "$home" ] && [ ! "$copy2ram" == "yes" ] && [ "$mediadetected" != "cd" ]; then
	echo "Setting up persistent home directory ($home)"
	uid=1000
	if [ -z "`grep "^[^:]\+:x:$uid:" /slroot/live/union/etc/passwd | cut -f1 -d:`" ]; then
		uid=0
	fi
	homedir=`grep "x:$uid:" /slroot/live/union/etc/passwd | cut -f6 -d:`
	gid=`grep "x:$uid:" /slroot/live/union/etc/passwd | cut -f4 -d:`
	if [ "$mediadetected" == "sd" ]; then
		if echo $home | grep -q "="; then
			filesize=`echo "$home" | cut -f2 -d=`
			home=`echo "$home" | cut -f1 -d=`
		fi
		if [ ! -d /slroot/live/media/$home ] && [ ! -f /slroot/live/media/$home ]; then #storage space creation
			#if  [ ! -z "$filesize" ]; then
			#	echo "Creating $home ($filesize MB) persistent home directory ..."
			#	dd if=/dev/zero of=/slroot/live/media/$home bs=1024k count=0 seek=$filesize #TODO: check available space
			#	sleep 1
			#	mkfs.ext3 -F /slroot/live/media/$home
			#	sleep 1
			#	mount -o loop /slroot/live/media/$home /slroot/$homedir
			#	cp -dpR /slroot/live/union/etc/skel/.??* /slroot/$homedir/
			#	cp -dpR /slroot/live/union/etc/skel/* /slroot/$homedir/
			#	chown -R $uid:$gid /slroot/$homedir
			#	umount /slroot/$homedir
			#else
				if mount | grep -q "$livedevice on /slroot/live/media type ext3"; then #TODO: check FS type (vfat not supported)
					mount -o remount,rw /slroot/live/media
					mkdir -p /slroot/live/media/$home
					cp -dpR /slroot/live/union/etc/skel/.??* /slroot/live/media/$home/
					cp -dpR /slroot/live/union/etc/skel/* /slroot/live/media/$home/
					cp  /slroot/live/union/$homedir/Desktop/* /slroot/live/media/$home/Desktop
					chown -R $uid:$gid /slroot/live/media/$home
				fi
			#fi
		fi

		if [ -f /slroot/live/media/$home ];	then # home directory in persistent file
			mount -o remount,rw /slroot/live/media
			## add these here for persistent
			# Find a free loop device to mount the persistence container file:
			prdev=$(find_loop)
			losetup $prdev /slroot/live/media/$home
			# Check if the persistence container is LUKS encrypted:
			if cryptsetup isLuks $prdev 1>/dev/null 2>/dev/null ; then
				echo "Unlocking LUKS encrypted persistence file '/${changes}'"
				cryptsetup luksOpen $prdev $(basename ${home}) </dev/tty0 >/dev/tty0 2>&1
				if [ $? -ne 0 ]; then
					echo "${DISTRONAME}:  Failed to unlock persistence file '/${changes}'."
					echo "${DISTRONAME}:  Falling back to RAM."
				else
				# LUKS properly unlocked; from now on use the mapper device instead:
				prdev=/dev/mapper/$(basename ${home})
				fi
			fi
			prfs=$(blkid $prdev |rev |cut -d'"' -f2 |rev)
			mount -t $prfs $prdev /slroot/$homedir 2>/dev/null
			if [ ! -f /slroot/$homedir/.xinitrc ]; then
				cp -dpR /slroot/live/union/etc/skel/.??* /slroot/$homedir/
				cp -dpR /slroot/live/union/etc/skel/* /slroot/$homedir
				cp  /slroot/live/union/$homedir/Desktop/* /slroot/$homedir/Desktop
				chown -R $uid:$gid /slroot/$homedir
			fi
			##
			#mount -o loop /slroot/live/media/$home /slroot/live/media/$homedir
		fi
		if [ -d /slroot/live/media/$home ]; then # persistent home directory
			mount -o remount,rw /slroot/live/media
			mkdir -p /slroot/live/media/$home
			mount --bind /slroot/live/media/$home /slroot/$homedir
		fi
	else
		echo "Setting up persistent home directory ($home)"
		mount -t nfs -o nolock $home /slroot/$homedir #TODO: no auth / security
	fi
fi

#runlevel boot parameter handling
if [ ! -z "$runlevel" ]; then
	echo "Setting up runlevel ($runlevel)"
	sed -i s/^id:[1-5]:initdefault:$/id:$runlevel:initdefault:/ /slroot/live/union/etc/inittab
fi

#autologin (runlevel 5)
runlevel=`cat /slroot/live/union/etc/inittab | sed -n /^id:[1-5]:initdefault:$/p | cut -f2 -d:`
if [ $runlevel = 5 ]; then
	login=`cat /slroot/live/union/etc/passwd | grep ":1000:" | cut -f1 -d:`
	if [ -z "$login" ]; then login="root"; fi
	echo "Setting up autologin (login=$login)"
	sed -i "s/^c1:.*$/c1:5:respawn:\/sbin\/agetty -a $login 38400 tty1 linux/" /slroot/live/union/etc/inittab

	if [ -x /slroot/live/union/usr/bin/startx ]; then
	cat  > /slroot/live/union/etc/profile.d/x.sh << END
if [ -z "\$DISPLAY" ] && [ "\`id -u\`" != "0" ] && [ "\`tty\`" = "/dev/tty1" ]; then
startx
logout
fi
END
		chmod +x /slroot/live/union/etc/profile.d/x.sh
	fi
fi

#system i18n setup
if [ ! -z "$locale" ]; then
	echo "Setting up locale ($locale)"
	if [ -f /slroot/live/union/etc/profile.d/lang.sh ] && grep -q "^export LANG=" /slroot/live/union/etc/profile.d/lang.sh
	then sed -i s/^export\ LANG=.*/export\ LANG=$locale/ /slroot/live/union/etc/profile.d/lang.sh
	else echo "export LANG=$locale" >> /slroot/live/union/etc/profile.d/lang.sh
	fi
	if [ -f /slroot/live/union/etc/kde/kdm/kdmrc ]; then
      locale_noutf8=$(echo $locale | sed 's/\.utf8//')
      sed -i "s/\(^\|^#\)Language=.*/Language=$locale_noutf8/" /slroot/live/union/etc/kde/kdm/kdmrc
    fi
    LIBSUFFIX=
	[ -d /slroot/live/union/usr/lib64 ] && LIBSUFFIX=64
  # Make firefox match OS locale
  # Now included in $DISTRONAME version, but keep it for other Slackware version or distro
  #if [ -w /slroot/live/union/usr/lib$LIBSUFFIX/firefox/greprefs/all.js ]; then
  # sed -i -e 's/pref("intl.locale.matchOS", false);/pref("intl.locale.matchOS", true);/g' /slroot/live/union/usr/lib$LIBSUFFIX/firefox/greprefs/all.js
  #fi
  if [ -w /slroot/live/union/usr/lib$LIBSUFFIX/firefox-*/defaults/pref/langpacks.js ]; then
	sed -i -e 's/pref("intl.locale.matchOS", false);/pref("intl.locale.matchOS", true);/g' /slroot/live/union/usr/lib$LIBSUFFIX/firefox-*/defaults/pref/langpacks.js
  fi
  # Make thunderbird match OS locale
  # Now included in $DISTRONAME version, but keep it for other Slackware version or distro
  #if [ -w /slroot/live/union/usr/lib$LIBSUFFIX/thunderbird/greprefs/all.js ]; then
  #  sed -i -e 's/pref("intl.locale.matchOS", false);/pref("intl.locale.matchOS", true);/g' /slroot/live/union/usr/lib$LIBSUFFIX/thunderbird/greprefs/all.js
  #fi
  if [ -w /slroot/live/union/usr/lib$LIBSUFFIX/thunderbird-*/defaults/pref/langpacks.js ]; then
    sed -i -e 's/pref("intl.locale.matchOS", false);/pref("intl.locale.matchOS", true);/g' /slroot/live/union/usr/lib$LIBSUFFIX/thunderbird-*/defaults/pref/langpacks.js
  fi
fi

if [ ! -z "$keymap" ]; then
 keyb=$keymap
 if [ -n "$keyb" ] || [-n "$numlock" ] || [ -n "$scim" ] || [ -n "$ibus" ]; then
  # keyboard settting
  [ -n "$keyb" ] || keyb=us
  [ -n "$numlock" ] || numlock=off
  [ -n "$scim" ] || scim=off
  [ -n "$ibus" ] || ibus=off

 grep "^$keyb|.*|.*|.*" /keymaps | sed -e "s/^.*|\(.*\)|\(.*\)|\(.*\)/\1|\2|\3/" > /slroot/live/union/tmp/xkb
    xkblayout="$(sed -e "s/^\(.*\)|.*|.*/\1/" /slroot/live/union/tmp/xkb)"
    xkbvariant="$(sed -e "s/^.*|\(.*\)|.*/\1/" /slroot/live/union/tmp/xkb)"
    xkboptions="$(sed -e "s/^.*|.*|\(.*\)/\1/" /slroot/live/union/tmp/xkb)"
    rm /slroot/live/union/tmp/xkb
    # Fall back to keymap if no xkb maping available
    if [ -z "$xkblayout" ]; then
      xkblayout="$keyb"
      xkbvariant=""
      xkboptions=""
    fi
    /usr/bin/loadkeys -u $keyb.map 1>&2 2>/dev/null
    if [ -e /slroot/live/union/etc/rc.d/rc.keymap ]; then
      sed -i "s/\(^[ \t]*\/usr\/bin\/loadkeys -u\).*$/\1 $keyb.map/" /slroot/live/union/etc/rc.d/rc.keymap
    fi
    if [ -e /slroot/live/union/etc/X11/xorg.conf.d ]; then
      cat <<EOF > /slroot/live/union/etc/X11/xorg.conf.d/10-keymap.conf
Section "InputClass"
Identifier "Keyboard settings"
MatchIsKeyboard "yes"
Driver "evdev"
Option "XkbLayout" "$xkblayout"
Option "XkbVariant" "$xkbvariant"
Option "XkbOptions" "$xkboptions"
EndSection
EOF
	fi
 fi
fi

if [ ! -z "$tz" ]; then
	if [ -f /slroot/usr/share/zoneinfo/$tz ]; then
		#cat /slroot/usr/share/zoneinfo/$tz > /slroot/etc/localtime
		ln -sf /usr/share/zoneinfo/$tz /slroot/etc/localtime
		ln -sf /usr/share/zoneinfo/$tz /slroot/etc/localtime-copied-from
	fi
fi
if [ ! -z "$hwc" ]; then
	if [ "$hwc" == "UTC" ] || [ "$hwc" == "localtime" ]; then
		echo "$hwc" > /slroot/etc/hardwareclock
	fi
fi

#setup everything needed to install live system
if [ -f /build-slackware-live.sh ]; then
	cp /build-slackware-live.sh /slroot/live/union/usr/sbin/
	chmod +x /slroot/live/union/usr/sbin/build-slackware-live.sh
fi

#detect fixed partitions
blkid | while read line; do
	partition=`echo $line | cut -f1 -d: | cut -c 6-`
	device=`echo $partition | cut -c1-3`
	type=`echo $line | sed 's/^.*TYPE=\"\([^\"]*\)\".*$/\1/'`
	loopdevice=`echo $partition | cut -c-4`
	if [ "$loopdevice" != "loop" ] && [ "`cat /sys/block/$device/removable`" = "0" ]; then
		if [ "$type" = "swap" ]; then
			if [ "$useswap" = "yes" ]; then
				echo "Enabling swap on /dev/$partition"
				echo "/dev/$partition none swap defaults 0 0" >> /slroot/live/union/etc/fstab
			else
				echo "/dev/$partition none swap defaults,noauto 0 0" >> /slroot/live/union/etc/fstab
			fi
		#else
		#	echo "Detected /dev/$partition ($type)"
		#	echo "/dev/$partition /mnt/$partition $type defaults,noauto,user 0 0" >> /slroot/live/union/etc/fstab
		#	mkdir -p /slroot/live/union/mnt/$partition
		elif [ "$type" = "vfat" ] && [ "$LIBSUFFIX" = "64" ]; then
				mkdir -p /slroot/live/union/boot/efi
			    mount -t vfat /dev/$partition /slroot/live/union/boot/efi 2>/dev/null
				if [ -d /slroot/live/union/boot/efi/EFI ]; then
					echo "Adding EFI partition /dev/$partition to /etc/fstab"
					echo "/dev/$partition /boot/efi vfat defaults 1 0" >> /slroot/live/union/etc/fstab
				    umount /slroot/live/union/boot/efi 2>/dev/null
				else
					umount /slroot/live/union/boot/efi 2>/dev/null
					rm -rf /slroot/live/union/boot/efi
				fi
		fi
	fi
done

#setup root password
if [ ! -z "$rootpw" ]; then
	echo "Setting up root password"
	echo "root:$rootpw" > /slroot/tmp/chpasswd.tmp
	chroot /slroot /usr/sbin/chpasswd < /slroot/tmp/chpasswd.tmp
	rm -f /slroot/tmp/chpasswd.tmp
fi

#chroot to live system
umount /proc
umount /sys
mount -r -o remount /slroot 2>/dev/null #remount root directory read-only for normal startup

echo "*** Live system ready ***"
echo ""
exec switch_root /slroot /sbin/init || exec sh