site  news  contact

How Easy works

September 24, 2017 — BarryK

Easy OS is a "new paradigm" for a Linux distribution, a blend of the best ideas from Puppy and Quirky, and a fundamental rethink of the security, maintainability and ease-of-use.

A good way to explain what Easy is all about, is to follow the user (that's you) experience, from downloading the deployed file, to installing and discovering it's features. To start, you would go to one of the websites hosting Easy Linux and download it...

Download

Unlike most other Linux distributions, Easy is not deployed as an image for a CD disk (ISO file). Instead, Easy is an image for an external drive, such as USB Flash stick, SD-card or USB solid state disk (SSD).

One might immediately counter that ISO files can also be written to Flash media -- yes, but that is not an installation -- the distribution still has to be installed somewhere. With Easy, what is written to the Flash media is the installation, nothing more to be done. You can run Easy from the external media indefinitely, but there is also the option of installing to internal drive.

The file you download is named easy-<version>-<architecture>.img.gz, for example easy-0.1.6-amd64.img.gz. The ".gz" on the end means that it is gzip compressed, and with Puppy (and derivatives), if you click on it, there will be an offer to uncompress it. With many other distributions, you have to open a terminal and do it manually:

# gunzip easy-0.1.6-amd64.img.gz

Having done so, you will have easy-0.1.6-amd64.img:
image

...the above snapshot is a ROX-Filer file manager window in Easy Linux.

Installation
This web page is describing how Easy works, not installation. There are separate pages for that, however, it can be mentioned here how easy it is. Just install one of the free GUI tools for writing an image file to a drive, such as USB Image Tool for Windows, or Etch for Windows, Mac and Linux. Or, in Linux it is easy to do on the commandline (for the example of the external drive being /dev/sdb):
# gunzip --stdout easy-0.1.6-amd64.img.gz | dd of=/dev/sdb bs=1M
# sync
Also, Easy can be installed to the internal hard drive of a PC.

The file easy-0.1.6-amd64.img (or whatever version you have downloaded) is an image file, that can be written to any drive. Although it is only 521MB, at first bootup it will expand to use the entire drive. So any drive, from 2GB upwards, is suitable (although at least 8GB is recommended for ongoing usage).

Internally, the file contains a 519MB fat32 partition. If you are already running Easy Linux, you can just click on this file to open it up and see inside. With other Linux distributions, you have to do it from the commandline:

# mkdir mntpt
# mount -t vfat -o loop,offset=1048576 easy-0.1.6-amd64.img mntpt

The fat32 partition actually starts 1MB into the file (which is 1048576 bytes), the first 1MB has partition table and other stuff.

Warning
A warning about the mount utility. Puppy Linux and derivatives may actually run the Busybox mount, which does not support the offset parameter. However, they mostly do have the full version, named as mount-FULL (so substitute "mount-FULL" above).

With Easy, being easy, you just click on the file, and you can see what is in the vfat partition:
image

You should immediately recognise one file in there, vmlinuz, which is the Linux kernel. The image file has Syslinux installed in it, which is a boot manager. The files boot.msg, help.msg, ldlinux.sys, logo.16, syslinux.cfg, and more in the EFI folder, are part of Syslinux.

Once written to a drive, Easy will boot up on any desktop PC or laptop with x86 64-bit CPU, older pre-2012 PCs with traditional BIOS-firmware, and modern PCs with UEFI-firmware. If you don't understand what BIOS or UEFI mean, don't worry.

q.sfs

This is an approximately 380MB - 400MB file that contains all of Easy Linux. It is the entire filesystem, which basically means that it has all of the applications, utilities, data and documentation. It packs a lot into a small space -- for example, Easy includes the SeaMonkey browser suite (based on Firefox, but also has WYSIWYG HTML editor, chat, addressbook, mail & newsgroups), and the LibreOffice suite. It also includes drivers for most hardware, such as network, printing, scanning, cameras.

Technical note
File q.sfs is what is called a squashfs filesystem. It is compressed, with xz compression.

initrd.q

This is a startup filesystem. At bootup, the Linux kernel loads this into RAM and it does various setup operations, before switching over to using q.sfs.
Being a bit more specific, after loading initrd.q, the kernel runs a file inside it called init.

This init is of great interest to us, it shows how Easy is structured at the nuts-and-bolts level.

With Easy, once again, being easy, just click on initrd.q and it will open up and you can see what is in it. And, if you fancy getting into modifying Easy, you can make changes and save it back to initrd.q (popup messages will explain how), and your change will be back in the original easy-0.1.6-amd64.img -- and hey, you have customised Easy! And yes, the same can be done to q.sfs.

Alright, click on initrd.q, and this is what we see:
image

...it is a tiny Linux filesystem, just enough to boot up on.

technical note
The file initrd.q is only 3.2MB and contains static utilities (utilities that do not need shared library files to run), it is not compressed. It is a cpio filesystem.

init script

One way to explain what init does is to insert the code into this web-page, along with explanation of each segment. If you are not familiar with Linux shell scripts, no problem, the accompanying explanation is sufficient. If you do have such knowledge, you can see what is happening in more depth.

Here is the first segment of the script. It is just some setting-up stuff. If it is gobbledy-gook to you, no problem:

#!/bin/sh
#(c) Copyright Barry Kauler, 30 January 2017. Licence: GPL v3 (/usr/share/doc/legal).
#simple script in initramfs to boot Easy Linux.
#deloyed as a drive image, with one 519MB fat32 partition, containing vmlinuz, initrd.q, q.sfs
#text colors: 34=blue, 33=yellow, 32=green, 31=red, 35=purple, 36=aquablue, 38=black

export TEXTDOMAIN=easyinitrd
export OUTPUT_CHARSET=UTF-8
mount -t proc none /proc
mount -t sysfs none /sys
mount -t rootfs -o remount,rw rootfs /
ln -s /proc/mounts /etc/mtab 2> /dev/null
export PATH="/bin:/sbin"

err_exit() {
 echo -e "\\033[1;31mERROR: ${1}\\033[0;39m" #red
 echo 'Have now dropped into a shell in the initramfs.'
 echo 'Note1: Type "exit", the init script will attempt to continue, however,'
 echo '       only an expert who is debugging the bootup should do this.'
 echo 'Note2: On some PCs the keyboard does not work at this stage of bootup.'
 echo 'PLEASE HOLD DOWN THE POWER BUTTON TO SHUTDOWN'
 /bin/sh
}

#170206 reintroducing aufs in the kernel:
if grep -qw aufs /proc/filesystemsthen
 LAYERFS='aufs'
 RO='=ro'
else
 LAYERFS='overlay'
 RO=''
fi

There is one important point that can be extracted from the above code: aufs and overlay.

The fundamental basis of how Easy works is based on a layered filesystem, of which aufs and overlay are two implementations. The use of a layered filesystem goes back to Puppy Linux, in fact, in Puppy the layered f.s. is also the core feature of Puppy.

Easy uses the layered f.s. somewhat differently from Puppy. But first, just what is a layered f.s.?

Layered filesystem

This picture illustrates the basic principle:
image

There need to be at least two layers, one read-write (rw) and one read-only (ro). With the overlay implementation, there can only be one rw layer, but many ro layers -- aufs is a bit more flexible.

The basic idea is that whatever is on the rw layer takes precedence. For example, if there is /usr/bin/afile on both layers, only the one on the rw layer will be seen. If there is no /usr/bin/afile on the rw layer, then the one on the ro layer will be seen.

Files can also be deleted and renamed on the rw layer. For example, /usr/bin/afile on the ro layer can be renamed to /usr/bin/afile2 on the rw layer, and /usr/bin/afile is no longer seen on top.
The magic that achieves this is by special hidden files, known as whiteout files -- you don't see these, they are part of the management of the layered filesystem.

What you see as the user, is the top, which may be the "/" folder.

File q.sfs is compressed and stays compressed. When mounted as a ro layer though, all of its files are accessed as a normal filesystem. All changes will only occur on the rw layer.

In Easy, the rw layer is just a folder. Everything that you do will be in here: network setup, installed packages, whatever. As it is just a folder in the working partition, the storage capacity is limited only by the size of the partition.
This point is mentioned because Puppy Linux has the concept of a "save file", where the rw layer is a file, of a certain size, which does become a problem for users, when they run out of space in that file, then have to make it bigger.
Easy avoids that complication entirely.

Technical note
Well, a few technical notes about the Puppy "save file", for those with Puppy experience. The main advantage of having a save-file is that it can be in a non-Linux partition, such as fat or ntfs. Some Puppy users do what is called a "frugal install" into a fat or ntfs partition where they have Windows installed. Easy requires a partition with a Linux filesystem. Incidentally, Easy is also capable of a frugal install, and there is a tutorial page explaining how.

Continuing with the init script, the next thing it does is locate the drive that it has booted off. It may seem odd that the script has to search for it, given that vmlinuz and initrd.q have already been read from it. But that happens prior to the Linux kernel running, and the kernel has to identify the drives in the PC and assign drive-names to them.

Finding the boot partition

Here is the next segment of code:

###find drives###
#find the drive we are booting on (has vmlinuz, initrd.q, q.sfs), and working drv...
. /BOOT_SPECS #has BOOT_DISKID, BOOT_PARTNUM, BOOT_FS, BOOT_DIR, WKG_DISKID, WKG_PARTNUM, WKG_FS, WKG_DIR, Q_DISTRO_VERSION
echo -n -e "\\033[1;35mFinding drives:\\033[0;39m\n " #purple
CNT=0Pb=''Pw=''BOOT_DRV=''WKG_DRV=''
while $CNT -lt ];do #drives may take couple seconds to become available.
 sleep 1
 CNT=$(($CNT+1))
 for aDRV in /sys/block/sd[a-z] /sys/block/mmcblk[0-9]
 do
  "${aDRV/*]/]}" == "]" ] && continue #170731
  DRV=${aDRV:11:7} #extract drv from /sys/block/<drv>
  echo -n " ${DRV}"
  fdisk -l /dev/${DRV} diskinfo-${DRV} 2>/dev/null
  if grep "$BOOT_DISKID" diskinfo-${DRV} >/dev/null;then BOOT_DRV="$DRV"fi
  if grep "$WKG_DISKID" diskinfo-${DRV} >/dev/null;then WKG_DRV="$DRV"fi
  "$BOOT_DRV" -a "$WKG_DRV" ] && break 2
 done
done
[ ! "$BOOT_DRV" ] && err_exit 'Boot drive not found'
[ ! "$WKG_DRV" ] && err_exit 'Working drive not found'
"${BOOT_DRV:0:3}" == "mmc" ] && Pb='p'
"${WKG_DRV:0:3}" == "mmc" ] && Pw='p'
BOOT_DEV="${BOOT_DRV}${Pb}${BOOT_PARTNUM}"
WKG_DEV="${WKG_DRV}${Pw}${WKG_PARTNUM}"
echo -e "\n  Boot-partition: ${BOOT_DEV}  Working-partition: ${WKG_DEV}"
mkdir -p /mnt/${BOOT_DEV}
mkdir -p /mnt/${WKG_DEV}

The key to identification of the boot partition is the file BOOT_SPECS, which is inside initrd.q. Essentially, what the above code does is read BOOT_SPECS and from that identify where Easy is installed. Here is the content of the file:

BOOT_DISKID='694ED775-12F8-4969-9FEA-30189D251140'
BOOT_PARTNUM=1
BOOT_FS='vfat'
BOOT_DIR=''
WKG_DISKID='694ED775-12F8-4969-9FEA-30189D251140'
WKG_PARTNUM=2
WKG_FS='ext4'
WKG_DIR=''
Q_DISTRO_VERSION=0.1.6

The disk ID is a unique identifier of the drive. The script scans the drives looking for this identifier. This is very fast. The reason that there is a waiting loop, is that the kernel needs two or three seconds to identify the drives and assign the drive names.

Technical note
Puppy Linux users will be familiar with the search for installed files at bootup. With the Puppy live-CD for example all partitions are searched for the session save-file. This takes time. Easy does not do any searching, as the the partitions are hard-coded into initrd.q, file BOOT_SPECS.

Note also, the "BOOT_*" and "WKG_*" variables. Boot and working drives and partitions may be different, or not. Also, there are optional folders that Easy may be installed in.

The above BOOT_SPECS example is for the image file that you download, easy-0.1.6-amd64.img.gz (or whatever version you have downloaded). The ID number might be different.

As mentioned before, this image only has one partition, fat32, 519MB.

The working partition is where you will be after bootup, and this is created on-the-fly at first bootup, to fill the drive. This is partition number 2, and is (or rather will be) an ext4 filesystem.

The essence of the above is extreme flexibility. By putting appropriate entries into BOOT_SPECS, Easy can be installed anywhere.

Performance measurements

The init script needs to make decisions based on the amount of RAM and read-speed of the working drive. This is the next segment of code:

###performance measurements###
FREEK=`grep '^MemFree:' /proc/meminfo | tr -s ' ' | cut -f 2 -d ' '` #free RAM
#echo 3 > /proc/sys/vm/drop_caches #clear memory caches. note, could use "hdparm -t ..." but it is slower.
TIMEs="$(dd if=/dev/${WKG_DRV} of=/dev/null bs=1024 count=64 iflag=skip_bytes skip=1048576 2>&1 | grep -o '[0-9.]* seconds,' | cut -f 1 -d ' ')" #read speed.
TIME10k=$(dc ${TIMEs} 10000 mul p | cut -f 1 -d '.') #cheap flash stick: usb2=140 usb3=77, internal eMMC: 

Moving on, at first bootup, the second (working) partition does not exist, so has to be created...

Create the working partition

The init script now has to create the working partition, if it does not exist, which is the case with our easy-0.1.6-amd64.img.gz file, then mount it and perform some initializations. Here is the code:

###[create and] mount working partition###
if grep " ${WKG_DEV}$" /proc/partitions >/dev/null;then
 echo -e "\\033[1;35mCreating partition ${WKG_DEV} to fill drive\\033[0;39m" #purple
 #170730 now mbr, not gpt, need to specify primary (p) or extended (e)...
 echo -e "n\np\n${WKG_PARTNUM}\n\n\nw" fdisk -u /dev/$WKG_DRV err.log 2>&1
 sync
 if grep -'failed' err.log ;then err_exit "Failed to create working partition ${WKG_DEV}" fi
 echo "  Creating ${WKG_FS} filesystem in partition ${WKG_DEV}"
 mke2fs -q -t ${WKG_FS} -O ^has_journal -L easy2 --b 4096 /dev/${WKG_DEV} #only supporting ext2/3/4
fi
mount -t ${WKG_FS} /dev/${WKG_DEV} /mnt/${WKG_DEV}
$? -ne ] && err_exit "Unable to mount working-partition ${WKG_DEV}"
if [ ! -d /mnt/${WKG_DEV}/${WKG_DIR}repository ];then #populate with skeleton directory hierarchy.
 cp -a -f /skeleton/* /mnt/${WKG_DEV}/${WKG_DIR}
 cp -a -f /skeleton/.[a-z]* /mnt/${WKG_DEV}/${WKG_DIR}
fi

###set date and time###
#could read .session/etc/clock and run hwclock, but for now this probably good enough (refer: rc.shutdown)...
-s /mnt/${WKG_DEV}/${WKG_DIR}.session/root/.var/local/shutdown_date_saved ] && date -s "`cat /mnt/${WKG_DEV}/${WKG_DIR}.session/root/.var/local/shutdown_date_saved`" > /dev/null

###mount boot partition###
if "$BOOT_DEV" != "$WKG_DEV" ];then
 mount -t ${BOOT_FS} /dev/$BOOT_DEV /mnt/$BOOT_DEV
 $? -ne ] && err_exit "Unable to mount boot-partition ${BOOT_DEV}"
fi
[ ! -f /mnt/$BOOT_DEV/${BOOT_DIR}q.sfs ] && err_exit "$(LANG=${wkgLANG} gettext 'Boot-partition does not have file q.sfs')"

You can see above, where the working partition is created if it doesn't exist.

Technical note
The Easy drive image has a conventional MBR, not a GUID partition table (GPT). The latter is a pain when copying images from one drive to another, due to the requirement of a secondary GPT at the physical end of the drive -- and the things that can go wrong if there is already one from previous usage of the drive.
Earlier versions of Easy were deployed as a GPT-based image, but it is an unnecessary complication.

Finally, some directories are created in the new partition. Note in particular, repository, .session, containers, and home directories.
Folder .session has already been mentioned as the rw layer of the layered filesystem -- it is preceded by a "." as that makes it invisible -- reason being that you should not directly access a layer, only from the top.

Recovery and maintenance

As the above title says, this segment of code is concerned with recovery and maintenance. Here it is:

###recovery, maintenance###
"$qfix" ] && QFIX=$qfix #kernel boot param
if "$QFIX" ];then
 for ONEFIX in `echo -n "$QFIX" | tr ',' ' '`
 do
  case $ONEFIX in
   fsck|FSCKecho -n "${WKG_DEV},${WKG_FS},REQUEST" > /mnt/${WKG_DEV}/${WKG_DIR}.session/.fsckme.flg ;;
   back|bak|BACK|BAKecho -n ",erase" > /mnt/${WKG_DEV}/${WKG_DIR}.session/.rollback.flg ;;
  esac
 done
fi
export wkgLANG="$(grep '^LANG=' /mnt/${WKG_DEV}/${WKG_DIR}.session/etc/profile 2>/dev/null | cut -f 2 -d '=')"
[ ! "$wkgLANG" ] && export wkgLANG=C
-s /mnt/${WKG_DEV}/${WKG_DIR}.session/.fsckme.flg ] && fscheck ${WKG_DRV} ${WKG_DEV} #ex: improper shutdown
-s /mnt/${WKG_DEV}/${WKG_DIR}.session/.rollback.flg ] && rollback ${WKG_DRV} ${WKG_DEV}

What the above code is doing, is it looks for the existence of files .fsckme.flg and .rollback.flg in the .session folder. .fsckme.flg specifies that a filesystem check is required, .rollback.flg specifies to roll back to an earlier version or snapshot of Easy (or to roll forward).

Filesystem check and rollback may also be specified on the kernel boot commandline, as "qfix=fsck" and "qfix=bak", or both as "qfix=fsck,bak". In the latter case, there are no options, it just deletes the .session folder (rw layer).

Rollback, however, can be much more powerful than just deleting the rw folder. it enables you to rollback to an earlier version of Easy, and/or to an earlier snapshot of the .session folder. Unlike Quirky Linux, that implemented this in a very slow and failure-prone way, Easy does it as an atomic operation, very fast and failure-proof.

Version control

The aforementioned rollback is part of Easy Version Control. Version control is a powerful feature of Easy. A record is kept of past versions, and snapshots or the .session folder (the rw layer) can be made at any time. Version control also extends to containers, however, let's hold off explaining that for now.

Are your eyes glazing over yet? Maybe this will do it -- version control is a big chunk of code:

###version control###
if [ ! -d /mnt/${WKG_DEV}/${WKG_DIR}repository/easy-${Q_DISTRO_VERSION} ];then
 echo -e "\\033[1;35m$(LANG=${wkgLANG} gettext 'One-time only operation, creating a snapshot of Easy OS')\\033[0;39m"
 echo "  $(LANG=${wkgLANG} gettext 'This will allow future rollback with the Easy Version Control Manager')"
 echo "  $(LANG=${wkgLANG} gettext 'Creating:') /mnt/${WKG_DEV}/${WKG_DIR}repository/easy-${Q_DISTRO_VERSION}"
 mkdir /mnt/${WKG_DEV}/${WKG_DIR}repository/easy-${Q_DISTRO_VERSION}
 
 #170816 there is a limit on history...
 if -s /mnt/${WKG_DEV}/${WKG_DIR}.session/root/.var/local/version-history-depth ];then
  DEPTH_MAX="$(cat /mnt/${WKG_DEV}/${WKG_DIR}.session/root/.var/local/version-history-depth)"
  VERS="$(ls -l /mnt/${WKG_DEV}/${WKG_DIR}repository/easy-* | rev | cut -f 1 -d '-' | rev)"
  sortedVERS="$(echo "$VERS" | sort -t. -k 1,1n -k 2,2n -k 3,3n -k 4,4n)" #lowest to highest.
  for ADEL in `echo "$sortedVERS" | head -n -${DEPTH_MAX} | tr '\n' ' '`
  do
   if -d /mnt/${WKG_DEV}/${WKG_DIR}repositories/easy-${ADEL} ];then
    MSGdel="$(gettext 'Warning, deleting old version:')"
    echo -e "  \\033[1;31m${MSGdel} easy-${ADEL}\\033[0;39m" #red
    rm -rf /mnt/${WKG_DEV}/${WKG_DIR}repositories/easy-${ADEL}
   fi
  done
 fi

 if -f /mnt/${WKG_DEV}/${WKG_DIR}.session/etc/DISTRO_SPECS ];then
  prevVER="$(grep '^DISTRO_VERSION=' /mnt/${WKG_DEV}/${WKG_DIR}.session/etc/DISTRO_SPECS | cut -f 2 -d '=' | cut -f 1 -d ' ')"
  touch /mnt/${WKG_DEV}/${WKG_DIR}.session/.delayedrun_version_upgrade #see /usr/sbin/delayedrun
 else
  prevVER=""
 fi
 echo -n '  initrd.q'cp -f /mnt/${BOOT_DEV}/${BOOT_DIR}initrd.q /mnt/${WKG_DEV}/${WKG_DIR}repository/easy-${Q_DISTRO_VERSION}/
 echo -n ' vmlinuz'cp -f /mnt/${BOOT_DEV}/${BOOT_DIR}vmlinuz /mnt/${WKG_DEV}/${WKG_DIR}repository/easy-${Q_DISTRO_VERSION}/
 echo -n ' q.sfs'cp -f /mnt/${BOOT_DEV}/${BOOT_DIR}q.sfs /mnt/${WKG_DEV}/${WKG_DIR}repository/easy-${Q_DISTRO_VERSION}/
 #echo -n "rw-${Q_DISTRO_VERSION}.sfs"; 
 mksquashfs /mnt/${WKG_DEV}/${WKG_DIR}.session /mnt/${WKG_DEV}/${WKG_DIR}repository/easy-${Q_DISTRO_VERSION}/rw-${Q_DISTRO_VERSION}.sfs -comp gzip #>/dev/null
 mkdir -p /mnt/${WKG_DEV}/${WKG_DIR}.session/etc
 #also save any container sessions:
 for EXE in `ls -1 /mnt/${WKG_DEV}/${WKG_DIR}containers | tr '\n' ' '`
 do
  [ ! -d /mnt/${WKG_DEV}/${WKG_DIR}containers/${EXE}/.session ] && continue
  #echo -n " ec-${EXE}"; mksquashfs /mnt/${WKG_DEV}/${WKG_DIR}containers/${EXE}/.session /mnt/${WKG_DEV}/${WKG_DIR}containers/${EXE}/rw-${prevVER}.sfs -comp gzip >/dev/null
  cp -f /mnt/${WKG_DEV}/${WKG_DIR}containers/${EXE}/configuration /mnt/${WKG_DEV}/${WKG_DIR}containers/${EXE}/configuration-${Q_DISTRO_VERSION}
 done
 echo
 touch /mnt/${WKG_DEV}/${WKG_DIR}repository/easy-${Q_DISTRO_VERSION}/configuration
 touch /mnt/${WKG_DEV}/${WKG_DIR}repository/easy-${Q_DISTRO_VERSION}/configuration-${Q_DISTRO_VERSION}
fi

Don't get bogged down in the above code. Basically, it detects a version upgrade, and saves it in the repository folder. Say we are booting up Easy version 0.1.6 for the first time. The above code will create folder repository/easy-0.1.6 and save all required files in it.

Recompress q.sfs

The QuickSetup GUI app in Easy, has a checkbox to choose to recompress q.sfs from xz compression to gz. The reason for it being xz-compressed is that it makes for a smaller download of the image file from the Internet. However, when mounted as a ro layer, files are uncompressed on-the-fly, and this is slower than gz (gzip) compression. Hence, to get slightly faster operating speed, especially noticeable at app startup, it is a good idea to tick the checkbox. Though, the speed improvement may only be noticeable with slow CPUs. The code:

###recompress q.sfs###
#improve operating speed, recompress q.sfs (refer quicksetup)

if -s /mnt/${WKG_DEV}/${WKG_DIR}.session/.qsfs.flg ];then
 echo -e "\\033[1;35m$(LANG=${wkgLANG} gettext 'Recompressing q.sfs, xz to gz, please wait')\\033[0;39m" #purple
 mkdir /mntsfs
 mount -r -t squashfs -o loop,noatime /mnt/${WKG_DEV}/${WKG_DIR}repository/easy-${Q_DISTRO_VERSION}/q.sfs /mntsfs
 mksquashfs /mntsfs /mnt/${WKG_DEV}/${WKG_DIR}q-gz.sfs -comp gzip #>/dev/null
 sync
 umount /mntsfs
 mv -f /mnt/${WKG_DEV}/${WKG_DIR}q-gz.sfs /mnt/${WKG_DEV}/${WKG_DIR}repository/easy-${Q_DISTRO_VERSION}/q.sfs
 sync
 rm -f /mnt/${WKG_DEV}/${WKG_DIR}.session/.qsfs.flg
fi

Now to progress onto creating the layered filesystem, first, the ro layers...

q.sfs as bottom ro layer

File q.sfs, as already described, is a squashfs filesystem, with all of Easy Linux. This init script now mounts this, getting ready to mount it into the layered filesystem:

###setup bottom ro layer, with q.sfs###
echo -e "\\033[1;35m$(LANG=${wkgLANG} gettext 'Mounting read-only layer of layered filesystem')\\033[0;39m" #purple
QSFSbytes0=`stat -c %s /mnt/${WKG_DEV}/${WKG_DIR}repository/easy-${Q_DISTRO_VERSION}/q.sfs`
QSFSbytes1=$(($QSFSbytes0+500000))
mount -t tmpfs -o size=${QSFSbytes1} tmpfs /q_ro
mkdir /q_ro/q_sfs
#decide whether to copy q.sfs to RAM, or not...
CPYflg=0
$FREEK -gt 1100000 ] && CPYflg=#>1GB ram then copy
$TIME10k -lt 100 ] && CPYflg=0   #but fast drive so don't copy.
$FREEK -gt 3100000 ] && CPYflg=#but heaps of ram, so copy.
if $CPYflg -eq ];then
 #do not copy q.sfs into ram, mount where it is...
 echo "  $(LANG=${wkgLANG} gettext 'Mounting squashfs file q.sfs')"
 QSFS_PATH="/mnt/${WKG_DEV}/${WKG_DIR}repository/easy-${Q_DISTRO_VERSION}/" #need this for setting up containers.
 mount -r -t squashfs -o noatime,loop /mnt/${WKG_DEV}/${WKG_DIR}repository/easy-${Q_DISTRO_VERSION}/q.sfs /q_ro/q_sfs
else
 echo "  $(LANG=${wkgLANG} gettext 'Copying q.sfs to RAM, then mounting')"
 [ ! -f /q_ro/q.sfs ] && cp -a /mnt/${WKG_DEV}/${WKG_DIR}repository/easy-${Q_DISTRO_VERSION}/q.sfs /q_ro/
 QSFS_PATH="/mnt/q_ro/" #need this for setting up containers.
 mount -r -t squashfs -o noatime,loop /q_ro/q.sfs /q_ro/q_sfs
fi
$? -ne ] && err_exit "$(LANG=${wkgLANG} gettext 'Failed to mount q.sfs')"
cp -f /q_ro/q_sfs/etc/DISTRO_SPECS /mnt/${WKG_DEV}/${WKG_DIR}.session/etc#need to be sure correct one is on top.
EXTRASFS=""sessionSFS=""
#precaution...
. /q_ro/q_sfs/etc/DISTRO_SPECS
"$DISTRO_VERSION" != "$Q_DISTRO_VERSION" ] && echo -e "\\033[1;31m$(gettext 'WARNING, versions do not match.') initrd.q: ${Q_DISTRO_VERSION}, q.sfs: ${DISTRO_VERSION}\\033[0;39m" #red

So far, q.sfs squashfs filesystem has been mounted, getting ready to put it into the layered filesystem. But we are not done. There may be a need to load even more ro layers...

Extra SFS files

As with Puppy, Easy can also load extra SFS files, as ro layers. These have to be specified in a configuration file:

###load extra sfs on ro layer###
#the configuration file defines any more sfs files to load...
. /mnt/${WKG_DEV}/${WKG_DIR}repository/easy-${Q_DISTRO_VERSION}/configuration
for NUM in 4 3 2 1
do
 eval "ROsfs=\$EASY_LAYER_RO${NUM}" #implements indirection. EASY_LAYER_RO1='devx*.sfs' in configuration file, will assign 'devx*.sfs' to ROsfs. 170320 170523
 if "$ROsfs" ];then
  #check file exists. note, may have a glob wildcard...
  FNDSFSS="$(ls -1 /mnt/${WKG_DEV}/${WKG_DIR}repository/easy-${Q_DISTRO_VERSION}/${ROsfs} | tr '\n' ' ')"
  for FNDSFS in $FNDSFSS
  do
   ANAME="$(basename $FNDSFS .sfs)"
   mkdir /q_ro/$ANAME
   echo "  $(LANG=${wkgLANG} gettext 'Mounting extra squashfs file:') ${ANAME}.sfs"
   mount -r -t squashfs -o noatime,loop /mnt/${WKG_DEV}/${WKG_DIR}repository/easy-${Q_DISTRO_VERSION}/${ANAME}.sfs /q_ro/${ANAME}
   EXTRASFS="/q_ro/${ANAME}${RO}:${EXTRASFS}" #170206
  done
 fi
done

sync
"$BOOT_DEV" != "$WKG_DEV" ] && umount /dev/${BOOT_DEV}

###fix layers change###
OLDEXTRASFSLIST="$(grep '^EXTRASFSLIST=' /mnt/${WKG_DEV}/${WKG_DIR}.session/etc/rc.d/PUPSTATE 2>/dev/null | cut -f 2 -d "'")"
-s /mnt/${WKG_DEV}/${WKG_DIR}.session/etc/rc.d/PUPSTATE ] && [ "$NEWEXTRASFSLIST" != "$OLDEXTRASFSLIST" -o "$prevVER" != "" ] && /sbin/fixlayers ${WKG_DRV} ${WKG_DEV} "${NEWEXTRASFSLIST}" "${prevVER}"

With the current code, there can be up to four extra SFS files loaded. This applies to the main Easy system, but SFS files may also be loaded in each container.

Note the call to /sbin/fixlayers. This performs some repair operations and sanity checks, required when the layers change.

Mount the layered f.s. and switch_root

Here is the rest of the script. Basically, it creates the layered filesystem, using aufs or overlay, does some housekeeping, then performs a switch_root operation onto the top of the layered filesystem:

###the big moment, create layered f.s.###
#echo -e "\\033[1;35m$(LANG=${wkgLANG} gettext 'Creating layered filesystem, with read-write folder:') /mnt/${WKG_DEV}/.session\\033[0;39m"
echo -e "\\033[1;35m$(LANG=${wkgLANG} gettext 'Creating layered filesystem, type:') ${LAYERFS}\\033[0;39m"
if "$LAYERFS" == "aufs" ];then #170525
 mount -t aufs -o br=/mnt/${WKG_DEV}/${WKG_DIR}.session=rw:${EXTRASFS}/q_ro/q_sfs=ro aufs /q_new
else
 mount -t overlay -o lowerdir=${EXTRASFS}/q_ro/q_sfs,upperdir=/mnt/${WKG_DEV}/${WKG_DIR}.session,workdir=/mnt/${WKG_DEV}/${WKG_DIR}.tempwork overlay /q_new
fi
$? -ne ] && err_exit "$(LANG=${wkgLANG} gettext 'Failed to create layered filesystem')"

#/etc/rc.d/rc.sysinit will append to PUPSTATE, get the ball rolling here...
#PUPMODE, bit-1 (partition has session), bit-2 (bottom layer is sfs) --rough equiv to puppy
echo -e "PUPMODE=6\nBOOT_DEV='${BOOT_DEV}'\nBOOT_FS='${BOOT_FS}'\nBOOT_DIR='${BOOT_DIR}'\nWKG_DEV='${WKG_DEV}'\nWKG_FS='${WKG_FS}'\nWKG_DIR='${WKG_DIR}'\nQSFS_PATH='${QSFS_PATH}'" > /q_new/etc/rc.d/PUPSTATE

###relocate mount-points prior to switch_root###
echo -e "\\033[1;35m$(LANG=${wkgLANG} gettext 'Performing a switch_root onto the layered filesystem')\\033[0;39m" #purple
#move the mount points...
[ ! -d /q_new/mnt/q_ro ] && mkdir /q_new/mnt/q_ro
[ ! -d /q_new/mnt/${WKG_DEV} ] && mkdir /q_new/mnt/${WKG_DEV}
[ ! -d /q_new/mnt/${BOOT_DEV} ] && mkdir /q_new/mnt/${BOOT_DEV}
mount -o move /q_ro /q_new/mnt/q_ro
mount -o move /mnt/${WKG_DEV} /q_new/mnt/${WKG_DEV}

mount -t devtmpfs devtmpfs /q_new/dev #need to do this before switch_root.
sync
umount /sys
umount /proc
exec switch_root /q_new /sbin/init

###END###

Some very interesting things are happening in this segment of code. Take a close look at the mounting of the layered filesystem, using overlay:

 mount -t overlay -o lowerdir=${EXTRASFS}/q_ro/q_sfs,\
   upperdir=/mnt/${WKG_DEV}/${WKG_DIR}.session,\
   workdir=/mnt/${WKG_DEV}/${WKG_DIR}.tempwork overlay /q_new 

ro layers

This can specify multiple read-only layers, delimited by ":". Each layer is a folder, and in our case there are SFS files mounted on those folders. The folder on the left is on top, and the folder on the right is at the bottom layer and it has q.sfs.

rw layer

The .session folder has the read-write layer. At the first bootup of Easy, this will have nothing in it. But after switch_root, this is where all your changes will appear -- it is quite interesting to look in this folder, as long as you don't try to change it's contents while the layered filesystem is mounted.

Top of layered f.s.

the topmost folder is /q_new, and this is the user's view, where you see the end result of all those stacked layers. It is a folder at this point in the code, but by magic, switch_root will transform it into "/".

Yes, there is "magic" that happens now. Filesystems that are mounted on folders in the initramfs are moved onto /q_new. For example, /q_ro actually has a tmpfs filesyestem mounted on it, and SFS files mounted in subdirectories under /q_ro -- /q_ro is moved to /q_new/mnt/q_ro.

Don't get too distracted trying to understand every step above, unless you are a scripting wiz and all of the above is obvious! The main point is that the layers are setup, and a switch_root occurs onto /q_new, which to the user suddenly becomes "/".

After the switch_root

This is a momentous occasion. Suddenly we are in the working environment. it is very much worthwhile thinking a bit more about just what has happened with this switch_root.

As mentioned above, the name "initramfs". This is the name of the filesystem in which the init script is running. As already described, the kernel loads initrd.q into RAM, and this becomes the tiny Linux filesystem, and init is run. At switch_root, all of this initramfs is deleted, as no longer needed.

Deleted, yes, but then what happens to that folder /q_new? switch_root basically performs a chroot operation onto it, then deletes all of the initramfs. Yet that /q_new survives, as "/", but it is not really anywhere, it is kind of in the void, in RAM.

A diagram might help to understand the situation, using the example of this being version 0.1.6 and the drive is /dev/sdb:
image

It is very helpful to have an understanding of this, as after booting up Easy and you look at "/" in the file manager, you will no doubt wonder where are all those files physically located? ROX-Filer will show this:
image

With a "normal" Linux distribution, what you see above are folders physically in a partition. However, with Easy, that "/" is a construct, the top view of those layers -- you can think of it as being somewhere in RAM.

To know where the actual files are physically located, you have to look down into the layers. The rw layer is the .session folder in the second partition (that we call the "working partition") -- in the example, it is /dev/sdb2.

The bottom ro layer is file q.sfs and that may be in the working partition, or has been copied into RAM. There is a bit of a trick here, because in the latter case, you can actually see q.sfs in /mnt/sdb2/q_ro -- just accept this for now, to avoid getting your thoughts twisted!

From the user's perspective

Now that the switch has occurred, Easy-proper is running. As this web page is getting rather long, "How Easy works" from the user's perspective is another page:

http://bkhome.org/easy/how-easy-works-part-2.html

...an easier read for the user!

Tags: easy