Kernel Korner - Unionfs: Bringing Filesystems Together
By default, Unionfs attempts to delete all instances of a file (or directory) in all branches; this mode is called DELETE_ALL. Aside from DELETE_ALL, Unionfs also supports two more deletion modes, DELETE_WHITEOUT and DELETE_FIRST. DELETE_WHITEOUT behaves like the default mode externally, but instead of removing all files in the Union, a whiteout is created. This has the advantage that the lower-priority files still are available through the underlying filesystem. DELETE_FIRST departs from classical UNIX semantics. It only removes the highest-priority entry in the union and, thus, allows the lower-priority entries to show through. These modes also are used for the RENAME operation, as it is a combination of a create followed by a delete.
DELETE_FIRST requires some user knowledge of the union's components. This is useful when Unionfs is used for source code versioning, as in our previous example of a kernel source tree. If we change a file in /home/cpw/linux, the file is copied up to the higher-priority branch. If the file is deleted with standard DELETE_ALL semantics, Unionfs creates a whiteout in the highest-priority branch (because it cannot modify the read-only lower-priority branch). The original source file in the lower-priority branch is now inaccessible, so it must be copied into the union from the source, which hardly makes for a convenient versioning system. This situation is precisely where DELETE_FIRST comes in handy. The delete mode is specified as a mount option, as in the following example:
# mount -t unionfs -o \ > dirs=/home/cpw/linux:/usr/src/linux=ro,\ > delete=first none /home/cpw/linux
Now, as before, if we change a file in /home/cpw/linux, the changes don't affect /usr/src/ linux. If we decide we don't like the changes, we simply can remove the file and the original version will show through.
With the unionctl utility, Unionfs's branch configuration can change on the fly. New branches can be added anywhere in the union, branches can be removed and read-write branches can be marked read-only (or vice versa). This flexibility allows Unionfs to create filesystem snapshots. In this example, we use Unionfs to create a snapshot of /usr while installing a new package:
# mount -t unionfs -o dirs=/usr none /usr
At this point, Unionfs has a single branch that is read-write, /usr. All operations are passed to the lower-level filesystem, and it is as if Unionfs didn't exist.
Creating a snapshot involves two steps. The first is to specify the location of the snapshot files by adding a branch (say, /snaps/0), as follows:
# unionctl /usr --add /snaps/0
At this point, Unionfs creates new files for /usr in /snaps/0, but files in subdirectories of /usr are created in the underlying /usr. The reason for this seeming contradiction is the rule that files are created in the highest-priority branch where the parent exists. For files in the root directory of the union, /usr, the parent exists in both branches. Because /snaps/0 is the higher-priority branch, new files and directories are created physically in /snaps/0. However, /snaps/0 is empty, so if a file were created in /usr/local, the highest-priority parent actually would be in the underlying /usr branch.
To complete the migration, the original /usr branch needs to be read-only. Again, we use unionctl to modify the branch configuration:
# unionctl /usr --mode /usr ro
Now, because Unionfs thinks the underlying /usr is read-only, all write operations really take place in /snaps/0. Multiple snapshots can be taken simply by adding another branch, say, /snaps/1, and marking /snaps/0 as read-only.
The first snapshot can be viewed through the underlying directory, /usr. Each snapshot consists of a base directory and several directories that have incremental differences. To view a specific snapshot, all we need is to unify the first snapshot and the incremental changes. For example, to view the snapshot that consists of /usr and /snaps/0, mount Unionfs as follows:
# mount -t unionfs -o ro,dirs=/snaps/0:/usr \ > none /mnt/snap
In this example, Unionfs itself is mounted read-only, so the underlying directories are not modified.
After determining that the changes made in a snapshot are good, the next step often is to merge the snapshot back into the base. The Unionfs distribution includes a snapmerge script that applies incremental Unionfs snapshots to a base directory. This is done by recursively copying the files in the snapshot directory to the base. After the copy procedure is done, new files and changed files are completed. The last step is to handle file deletions, which is done by creating the list of whiteouts and deleting the corresponding files. The whiteouts themselves also are removed so as not to clutter the tree.
Special Reports: DevOps
Have projects in development that need help? Have a great development operation in place that can ALWAYS be better? Regardless of where you are in your DevOps process, Linux Journal can help!
With deep focus on Collaborative Development, Continuous Testing and Release & Deployment, we offer here the DEFINITIVE DevOps for Dummies, a mobile Application Development Primer, advice & help from the experts, plus a host of other books, videos, podcasts and more. All free with a quick, one-time registration. Start browsing now...
- The Ubuntu Conspiracy
- Science on Android
- A First Look at IBM's New Linux Servers
- Disney's Linux Light Bulbs (Not a "Luxo Jr." Reboot)
- Vigilante Malware
- Bluetooth Hacks
- Vagrant Simplified
- Libreboot on an X60, Part I: the Setup
- System Status as SMS Text Messages
- October 2015 Issue of Linux Journal: Raspberry Pi