How to create a sandbox using the device mapper

Last modified: Sun Jun 2 10:53:14 EDT 2019

The goal is to set up a writable image of a read-only file system so that the changes (and only the changes) are saved in memory.  The following was tested with mainline kernel version and Slackware 12.1.

# Preconditions:
# - There is an ext2 file system on /dev/sda1.
# - Nothing else is trying to use /dev/sda1 concurrently.
# - Kernel has RAM block device support enabled.

SZ=`blockdev --getsz $DEV`

dmsetup create sandbox --table "0 $SZ snapshot $DEV /dev/ram0 N 1"
# /dev/ram0 = Copy-On-Write (COW) device
#         N = non-persistent
#         1 = chunksize in 512 B sectors

mount -t ext2 -o rw /dev/mapper/sandbox /mnt
cd /mnt
# Play in sandbox.  If /dev/ram0 is exhausted, I/O operations start failing en masse.

umount /mnt
dmsetup remove sandbox

# Postconditions:
# - Sandbox goes poof.
# - /dev/sda1 is unchanged.

This approach works at the raw device level.  A similar thing can be accomplished at the file system level using unionfs if your kernel supports that.  (It is not yet in the mainline kernel.)

Example:  Making a Live CD

A Live CD can work in at least four ways:  initramfs, tmpfs, unionfs, or the device mapper.  To demonstrate how a Live CD could be constructed using the device mapper, the following script converts the initrd of a Slackware 14.0 install disk into a root file system that can run without loading entirely into memory.

# 2013-10-18  Based on previous version of sandbox.html.
# Convert /img/tmp/slack from initramfs to sandbox so that DFI486 can boot.
# Caution.  Lots of hardcoded and version-dependent stuff.

# Change CDROM to /dev/sr0 or /dev/hdc, depending.

set -e
if [[ `whoami` != root ]]; then
  echo You must be root to run this script.

# Convert original initrd to ext2.
declare -i ORIGSZ=`gzip -dc $ORIGF | wc -c`
echo "Original size = $ORIGSZ"
# 5 MB is chewed up by internal fragmentation.
declare -i EXT2SZ=(ORIGSZ+10000000)/512
echo "Expanded size = $EXT2SZ sectors"
dd if=/dev/zero of=$OUTF bs=512 count=$EXT2SZ
# losetup syntax breaking change: -s became --show
DEV=`losetup -f --show $OUTF`
mke2fs -m 0 $DEV
mount -t ext2 $DEV /mnt
pushd /mnt
gzip -dc $ORIGF | cpio -i -d -H newc --no-absolute-filenames
cd /tmp
umount /mnt
losetup -d $DEV
echo "$OUTF is created."

# Create new, much smaller initrd that mounts it using sandbox.
mkinitrd -l ANSI-dvorak -s initrd -L -o stageleft  # -L adds dmsetup.
rm stageleft
cd initrd
cp -a /dev/loop0 dev        # Add missing device.
mkdir mnt2                  # Add second mount point.
# This patch works for mkinitrd 1.4.7 (Slackware 14.0).
# Note that CDROM and EXT2SZ are pasted in.
patch -s init <<EOF
--- init.orig	2012-09-04 17:44:08.000000000 -0400
+++ init	2013-10-18
@@ -282,10 +282,16 @@
   # Switch to real root partition:
-  /sbin/udevadm settle --timeout=10
+  /sbin/udevadm settle
   echo 0x0100 > /proc/sys/kernel/real-root-dev
-  mount -o ro -t \$ROOTFS \$ROOTDEV /mnt
+  mount -t iso9660 -o ro $CDROM /mnt2
+  losetup /dev/loop0 /mnt2/initrd-ext2.img
+  /sbin/dmsetup create sandbox --table "0 $EXT2SZ snapshot /dev/loop0 /dev/ram0 N 1"
+  /sbin/udevadm settle
+  mount -t ext2 -o rw /dev/mapper/sandbox /mnt
+  mkdir /mnt/mnt2
+  mount --move /mnt2 /mnt/mnt2
   if [ ! -r /mnt/sbin/init ]; then
     echo "ERROR:  No /sbin/init found on rootdev (or not mounted).  Trouble ahead."
     echo "        You can try to fix it. Type 'exit' when things are done." 
find . | cpio -o -H newc | gzip -9c > $ORIGF
cd ..
rm -rf initrd
echo "$ORIGF is replaced."

When you boot the CD, everything should look similar to any other Slackware install except that the installation CD is already mounted as /mnt2.  Since the root file system is now running off of the CD instead of from memory, you cannot eject the CD, and you can only install what will fit on one disc.