Shared subtrees

As reply to my previous post about per-process namespaces, Russell wrote about pam-namespace and shared subtrees. As he reports, pam-namespace (that I discovered by the occasion) can do what I was suggesting would be nice for pam-tmpdir.

Anyways, I was actually already planning to write about shared subtrees and how they can be useful with namespaces. In this first post, I will introduce shared subtrees, while in a follow-up post I will introduce how they can usefully be used with namespaces.

As a preliminary note, you should know that while etch's kernel supports shared subtrees, etch's mount doesn't support the necessary options. Lenny's mount is unfortunately uninstallable on etch due to its dependency on a newer libc. But you can find a smount utility's source code in Documentation/filesystems/sharedsubtree.txt in the kernel source, where other explanations about the feature are available. So, you can either backport lenny's util-linux to etch, or build smount to replicate what I will be doing below. If you are using smount, just replace

mount --make-type path

with

smount path type

As seen in my previous post, bind-mounting allows to attach a file hierarchy at some other place in the virtual file system.

mount --bind / /mnt

will make all the contents of / (/bin, /etc, ...) available through /mnt (/mnt/bin, /mnt/etc, ...).

On the other hand, sub-mounts will be ignored in such a case. For instance, /usr is usually a different mount point from /. It means /mnt/usr will be empty (provided it is empty in the underlying physical filesystem), instead of containing the same as /usr:

$ mount --bind / /mnt
$ ls /mnt/usr
$ ls /usr
bin games include lib lib32 lib64 local sbin share src X11R6
$ umount /mnt

If you also want /usr to be bind-mounted, you must use --rbind instead of --bind:

$ mount --rbind / /mnt
$ ls /mnt/usr
bin games include lib lib32 lib64 local sbin share src X11R6
$ ls /usr
bin games include lib lib32 lib64 local sbin share src X11R6

Obviously, sub-sub-mounts will also be propagated.

Since recursively bind-mounting will create a bunch of mount points, unmounting can become a hassle. You can use the following command to unmount everything:

$ awk '$2 ~ /^\/mnt/ {print $2}' /proc/mounts | sort -r | xargs umount

Now, this is where shared subtrees come in. After the bind mount has been done, if you mount something on either side of the bind mount, the new mount is not propagated. This is called private subtrees, and is the default. But before doing anything else, let's setup our testing environment:

$ mkdir -p /a/a /b
$ touch /a/b /a/c
$ ls /a
a b c
$ ls /b

After, bind-mounting /a onto /b, let's see what happens when mounting something under /a, then under /b:

$ mount --bind /a /b
$ ls /b
a b c
$ mount --bind /usr /a/a
$ ls /a/a
bin games include lib lib32 lib64 local sbin share src X11R6
$ ls /b/a
$ umount /a/a
$ mount --bind /usr /b/a
$ ls /a/a
$ ls /b/a
bin games include lib lib32 lib64 local sbin share src X11R6
$ umount /b/a

As I said earlier, these new mounts are not propagated. Note that I used bind-mounts as sub mounts, but it would work all the same with other kind of mounts (device, fuse, etc.).

There are 2 other modes that allow to have some propagation: shared and slave.

shared allows mounts on both ends to be shared. Note you need to set the mode before bind-mounting:

$ umount /b
$ mount --bind /a /a
# This is needed because /a is not initially a mount point and you can only apply subtree modes to mount points.
$ mount --make-shared /a
$ mount --bind /a /b
$ mount /dev/sda1 /a/a
$ ls /a/a
config-2.6.26-1-amd64 grub initrd.img-2.6.26-1-amd64 System.map-2.6.26-1-amd64 vmlinuz-2.6.26-1-amd64
$ ls /b/a
config-2.6.26-1-amd64 grub initrd.img-2.6.26-1-amd64 System.map-2.6.26-1-amd64 vmlinuz-2.6.26-1-amd64
$ umount /a/a
$ mount /dev/sda1 /b/a
$ ls /a/a
config-2.6.26-1-amd64 grub initrd.img-2.6.26-1-amd64 System.map-2.6.26-1-amd64 vmlinuz-2.6.26-1-amd64
$ ls /b/a
config-2.6.26-1-amd64 grub initrd.img-2.6.26-1-amd64 System.map-2.6.26-1-amd64 vmlinuz-2.6.26-1-amd64
$ umount /b/a

You can even mount on one end and unmount from the other:

$ mount /dev/sda1 /a/a
$ umount /b/a
$ ls /a/a

slave allows mounts on the "master" end (/a in our case) to propagate to the "slave" end (/b), but not the other way around. The "master" end need to be shared :

$ umount /b
$ mount --make-shared /a
# Only for completeness, we already set /a as shared earlier.
$ mount --bind /a /b
$ mount --make-slave /b
$ mount /dev/sda1 /a/a
$ ls /a/a
config-2.6.26-1-amd64 grub initrd.img-2.6.26-1-amd64 System.map-2.6.26-1-amd64 vmlinuz-2.6.26-1-amd64
$ ls /b/a
config-2.6.26-1-amd64 grub initrd.img-2.6.26-1-amd64 System.map-2.6.26-1-amd64 vmlinuz-2.6.26-1-amd64
$ umount /a/a
$ mount /dev/sda1 /b/a
$ ls /a/a
$ ls /b/a
config-2.6.26-1-amd64 grub initrd.img-2.6.26-1-amd64 System.map-2.6.26-1-amd64 vmlinuz-2.6.26-1-amd64
$ umount /b/a

There is a third mode, unbindable, that does something different:

$ umount /b
$ mount --make-unbindable /a
$ mount --bind /a /b
mount: wrong fs type, bad option, bad superblock on /a,
       missing codepage or helper program, or other error
       In some cases useful info is found in syslog - try
       dmesg | tail or so
$ mount --bind /a/a /b
mount: wrong fs type, bad option, bad superblock on /a,
       missing codepage or helper program, or other error
       In some cases useful info is found in syslog - try
       dmesg | tail or so

As you can see, it disallows bind mounting of /a and its subdirectories to some other place.

Finally, to put back the default mode, you can use:

$ mount --make-private /a

Similarly to --bind, there is also a recursive version of each: rshared, rslave, runbindable and rprivate, to apply these to sub-mounts.

2008-12-13 12:45:47+0900

p.d.o

Both comments and pings are currently closed.

One Response to “Shared subtrees”

  1. Bind - Shared subtrees — Somewhere out there! Says:

    […] Shared subtrees – in depth look at bind mounts. […]