Development Builds Layered on Top of a Stable System by Means of Unionfs
Intro
    Unionfs is a filesystem type by which you can merge multiple directories into one mountpoint. 
    The source directories (branches) are stacked onto each other with filesystem entries 
    in the top layer hiding/overriding entries in the layers below. In this article I'd like 
    to show you how to set up a layered /usr directory, which
    allows you to push and pop a development directory onto/from your distribution's 
    /usr directory. That way you don't ever modify your stable system, and can still 
    test your work in a real context. Further I'll show you how to put boot options into place, to 
    choose between the stable system and the development system at bootup. 
    That way you can replace core system applications that are started by init.
Disclaimer
The steps shown here will deeply interfere with your system's filesystem hierarchy and boot process. You might render your system unusable or even lose all your data. I cannot be held responsible for that. Reader beware; back up your system to a safe location.
Getting unionfs support
Alas, no filesystem unification implementation has made it into the vanilla kernels, and so you have to patch and compile a custom kernel to get the support. If you haven't done that before, there is a distribution specific kernel compilation series on howtoforge.com. They run under the title "How To Compile A Kernel - The XXX Way" where XXX is Fedora, Ubuntu, etc.
    You can get the patches for unionfs and further documentation at the 
    unionfs project.
    Once you have applied the patch, there is very focused documentation below 
    /linux/Documentation/filesystems/unionfs/. I think concepts.txt 
    and usage.txt in particular give you all the information 
    you need to understand and operate a unionfs, and they aren't too long. 
    You'll need to activate unionfs support by checking "File systems -> 
    Layered filesystems -> Union file system" (UNION_FS) and 
    optionally "Unionfs extended attribute" (UNION_FS_XATTR) 
    next to it, in the kernel configuration. Now, compile and install the new kernel.
A New Filesystem Structure
    Unionfs hides everything below the mountpoint (like every other filesystem type), so we have to move 
    the stable branch (the /usr dir of the distribution) to another directory, and hide away this fact, by 
    using a bind mount back onto /usr. As you can guess, this will rumble a bit (i.e., you might miss some 
    commands on the way), and is thus best applied from a live distribution of your choice.
    In this example I'll use the following branches:
        /usr                    # the mountpoint for your unionfs
        /usr_stable             # your distro's stable branch (formerly known as /usr)
        /mnt/devel/usr          # your development branch
        
    So, let's assume you are running a live distro and your normal root filesystem is mounted on /mnt/hda1.
1. Rename the stable branch:
            $ mv /mnt/hda1/usr /mnt/hda1/usr_stable
2. Create the mountpoint. You can create fallback links within the mountpoint in case either of the unionfs or the bind mount fails, and a sentinel file which you can check for existence in scripts:
            $ mkdir /mnt/hda1/usr
            $ cd /mnt/hda1/usr
            $ ln -sn -t . ../usr_stable/*
            $ touch YOU_SHOULD_NEVER_SEE_THIS_FILE
3. Then create a bind mount for the stable system.
            $ echo "/usr_stable  /usr none  bind" >> /mnt/hda1/etc/fstab
Now you should be able to boot into your stable system, and everything should be like before.
Boot Script:
    The next step is to create a script that checks for a chosen identification string on the kernel command line, 
    and if given, releases the bind mount again and puts the union mount into its place. A call to this script must be inserted into
    the boot process right after the corresponding init scripts have processed /etc/fstab and mounted 
    all the given filesystems. Where and how to insert this script varies among the 
    different distributions; if you don't know, you'll have to consult the
    documentation of your distribution for this. To find the corresponding scripts,
    you can grep for 'mount' or 'fstab' in the
	/etc/init.d/ scripts.
            $ grep -Hw -e mount -e fstab /etc/init.d/*
    Good candidates are localmount, bootmisc or similar. Look for the '-a' flag to mount.
    In case of Gentoo the right place to insert the call is here (in this
    example the script is called unionmount-usr):
    --- /etc/init.d/localmount.orgy 2009-02-15 10:26:22.000000000 +0100
    +++ /etc/init.d/localmount      2009-02-15 10:33:02.000000000 +0100
    @@ -23,6 +23,9 @@ start()
            mount -at "${types}"
            eend $? "Some local filesystem failed to mount"
    +       # conditionally mount development branch through unionfs
    +       /etc/init.d/unionmount-usr
    +
            # Always return 0 - some local mounts may not be critical for boot
            return 0
     }
    Note that for the approach given here to work (unmounting the default bind mount), you can't have 
    another bind mount on /usr_stable (or indirectly on /usr) unless you unmount and remount them too.
Now, the script - this is just a very basic version to illustrate the necessary steps. You might, for example, remount the bind mount if the union mount fails. In general, I like to have consistency checker script at the end of the boot process, where you can check for different vital things that you don't want to miss. Besides checking iptables rules and such, you could also check the sentinel file for existence there.
    #!/bin/bash
    # check the kernel command line (/proc/cmdline) for the id-String and if given release the bind mount on /usr.
    # and put the union mount in its place.
    UNION_WANTED_FLAG="UniteWithDevel"      # this is what would be given on the kernel cmdline
    MOUNTPOINT="/usr"
    STABLE_BRANCH="/usr_stable"
    DEVEL_BRANCH="/mnt/devel/usr"
    ERROR_LOG_FILE="/var/log/develUnionFailed.log"
    errorOut() {
        MSG="ERROR ${0} $(date) : ${@}"
        echo "${MSG}" >&2
        echo "${MSG}" >> "${ERROR_LOG_FILE}" 
        exit 88
    }
    wantUnionMount() {
        grep "${UNION_WANTED_FLAG}" /proc/cmdline &>/dev/null
    }
    wantUnionMount && {
        echo "trying to unite..."
        umount "${MOUNTPOINT}" || errorOut "umounting failed"
        mount -t unionfs -o "dirs=${DEVEL_BRANCH}=rw:${STABLE_BRANCH}=rw" none ${MOUNTPOINT} || errorOut "mounting unionfs failed"
    }
Final Step
    Now the final step is to create a boot menu entry which contains the
    ${UNION_WANTED_FLAG} from above. You can put any string on the kernel command line (well, 
    maybe any byte value, I guess all ascii-chars, but at least all ascii-alphanum chars)
    and if the kernel doesn't know it, it seems to be silently ignored but
    still appears in '/proc/cmdline' (and in `dmesg | grep 'Kernel command line')
    So, in the case of the example above, just create a boot menu entry with "UniteWithDevel" on the
    kernel command line and you can boot right into your development work, giving
    you the possibility to replace core system applications and daemons that get started
    by the init process (e.g.: ntp, iptables, sysklogd, etc). E.g:
        kernel /boot/unionkern-2.6.27.10/unionkern-2.6.27.10 root=/dev/hdc9 vga=0x31B UniteWithDevel 
Some Unionfs notes:
- Symlinks start a new lookup. If a symlink on a bottom layer links to a file that is hidden by another layer, the topmost file is looked up.
- 
        You are not allowed to have a read-only branch as the topmost layer of a read-write-unification. Bummer! That prevents you from operating a stable-branch below a read-only devel branch, like you normally would without the devel branch (e.g. with the distro's package management system). This would only be accomplished by a policy that would deviate from normal mount policies, I guess. (You wouldn't be able to write some of the files, even as root.) Two approaches came to my mind: - Make a read-only union and thus prevent any writes. To manage the stable-usr branch, put the bind mount back into place, or remove the devel branch and remount the unionfs as read-write (see below).
- Put a blank read-write branch on top of the read-only devel branch and capture all writes in this branch. Then use a shutdown script to move possibly written files to the stable branch. This affects only newly created files (or previously deleted and recreated files).
 
- 
        If you get some error while mounting with -t unionfs(like wrong fs type), grep dmesg for the real cause, like:$ dmesg | grep unionfs
- If you remount unionfs, and in particular add or remove filesystems, you obviously can't trust the 'mount' output concerning that mountpoint anymore. (Maybe that should be considered a bug?)
- 
          If you are sure none of the apps on the devel branch are currently
          running, you can probably safely switch in different branches as
          the development branch, thus switching arbitrary versions of an app in and out, while the system is running.
          There are unionfs mount options that add and delete a branch from the union. E.g., you can remove the 
          devel branch and remount it as read-write, then remount
          read-only with another devel branch at the highest level in the stack: 
        mount -t unionfs -o remount,rw,del=${DEVEL_BRANCH} none ${MOUNTPOINT} mount -t unionfs -o remount,ro,add=${ANOTHER_DEVEL_BRANCH} none ${MOUNTPOINT}Seelinux/Documentation/filesystems/unionfs/usage.txtfor more.
Union Mount
      There's also an alternative implementation called union mount. For that, see 
      "Unifying filesystems with union mounts".
      As far as I know, the corresponding patches are not yet considered stable at this time.
      Unionfs seems like it will never be supported, but the union mount patches will be. 
      See "Unionfs and related patches pre-merge review".
      Nevertheless, unionfs is widely used (see 
      here) and 
      I didn't have any problems with it (using xserver and xlib). So until 
      the union mount patches go mainline, unionfs seems to be a good opportunity.
      Obviously union mount would make fs-entries below the mountpoint
      accessible which would eventually obviate some of the steps above,
      making a test run much easier (e.g., you wouldn't have to move
      /usr.)
I have used the term union mount at different places in this text and scripts, because it seems to describe what happens. That does not relate to the implementation that goes by that name. Everything except this paragraph is based on unionfs and not "union mount".
Populate
    If you have a binary (i.e., pre-compiled) distribution, there are certainly a lot of points to
    consider. Depending on how much "core" your application wants,
    you have to build the application with the right flags in case other apps make use of those features 
    (--enable-this, --enable-that and friends, in case it is based on autotools.) And of course, 
    you have to use the same paths that the installed package uses, to hide all the files in the stable branch.
    Actually you would probably only need to hide entry points for other
    applications, like binaries, headers, libs and such. If there are single
    differences, you can try to hide them by using symlinks at the topmost layer.
So, with binary distributions, it is not that easy - but it is very easy with Gentoo, as Gentoo is about building apps from source in the first place. For example: to make little modifications to the xserver and try it out, it would only require something similar to this:
        $ ebuild /usr/portage/x11-base/xorg-server/xorg-server-1.5.3-r2.ebuild compile
            #... now modify the sources like so:
            $ cd /var/tmp/portage/x11-base/xorg-server-1.5.3-r2/work/xorg-server-1.5.3
            $ sed -i "s/\"(II)\"/\"(I am Bob and I almost completely rewrote the xserver)\"/g" os/log.c
            #... and restart the build process
            $ make
        $ ebuild /usr/portage/x11-base/xorg-server/xorg-server-1.5.3-r2.ebuild install
        $ cp -a /var/tmp/portage/x11-base/xorg-server-1.5.3-r2/image/usr/* /mnt/devel/usr/
The result would be exactly the same xserver like the one that is in the stable branch (if it was of version 1.5.3-r2) but it would show condign respect to your accomplishments in the servers logfile.
Another more useful example: As a non-native English speaker, I sometimes miss words from English movies. I like using "kaffeine" to watch movies, but the smallest step to skip backward is 20 seconds, which is far too long in response to "Come again?" So, with the steps from the previous example adapted to kaffeine and a bit of source-code browsing resulted in the following patch, which allows rewinding by 5 secs:
    --- kaffeine/src/player-parts/xine-part/xine_part.cpp.orgy      2008-11-10 19:24:18.000000000 +0100
    +++ kaffeine/src/player-parts/xine-part/xine_part.cpp   2008-11-10 19:24:23.000000000 +0100
    @@ -511,7 +511,7 @@ void XinePart::slotPosPlusSmall()
     void XinePart::slotPosMinusSmall()
     {
    -       slotJumpIncrement( -20 );
    +       slotJumpIncrement( -5 );
     }
     void XinePart::slotPosPlusMedium()
The point is that you can easily modify sources and test the results without ever messing up your stable branch.
Your fully functional stable system is just one reboot away.
    As a side note: You can, of course, also use this mechanism to try out
    different versions of an application. Just put a read-write mounted blank branch 
    on top of your read-only mounted stable-branch, and use your distribution's
    package management system to install another, possibly unstable, version
    of that application. If, in the course of that, you want to manage different top level branches,
    you might also consider using "/mnt/devel/usr" as a symlink,
    and let the unionmount-usr script above resolve that symlink
    to the branch you really want, by adding a line like:
    DEVEL_BRANCH=$(readlink -f "${DEVEL_BRANCH}") 
That way you can choose which branch is mounted at bootup by simply redirecting the symlink.
Sources
Now, you've probably guessed that I'm quite satisfied with Gentoo - once it's combined with a unification filesystem. Once you have set up and configured a Gentoo system, and know how to use portage (Gentoo's package manager), updating applications is a no-brainer most of the time. (Big kudos to all the Gentoo developers who make this possible.)
If you are interested, you can also get an initial binary distribution from Gentoo and then still use the source-based package manager afterwards on top of the binary packages. See Gentoo Linux and wikipedia:Gentoo.
Then there is Sabayon which is based on Gentoo and also includes a binary package manager but only supports x86 and x86-64 architectures. See Sabayon Linux and wikipedia:sabayon.
Can it get any better?
Initially I wanted to share my musings about whether it would be possible to bring the demonstrated "Populate" approach to binary distribution as a distro-independent application. When I wrote the last paragraph I took the opportunity to finally have a more thorough look at Sabayon Linux. The impression I got is that you can have a more-or-less semi-annually updated binary base system while still having the opportunity to use all the versatility that Gentoo offers (trying the latest features of bleeding edge apps, always have the newest versions of exposed and endangered apps, and of course easily modifying selected apps). So, if you don't want to go "all source" you can still try Sabayon Linux as a compromise.
If you have any interest in browsing, modifying, and testing open source programs, I strongly recommend giving Gentoo or Sabayon a try, getting accustomed to the Gentoo build system, and optionally trying a unified /usr-directory.
Again, I am very pleased to have the opportunity to develop and test a program while having a stable system just a reboot away. This makes development very pleasant.
And all the difficulties about dependencies and different build systems are covered by portage. This makes tweaking selected apps very convenient.
Talkback: Discuss this article with The Answer Gang
![[BIO]](../gx/2002/note.png) 
I`m a Linux user since 2003 and and after I did my first Linux steps with SuSE, I very soon chose source-based distributions (LFS and later Gentoo) as they offer a convenient way to see and alter any bit of the system that`s actually running.
 
