From: Valerie Aurora <vaurora@xxxxxxxxxx> A union mount clones the vfsmount tree of all of the read-only layers of the union and keeps a reference to it in the vfsmount of the topmost layer of the union. clone_union_tree() takes the path of the proposed union mountpoint and attempts to clones every vfsmount mounted at that same pathname, as well as their submounts. All these mounts must be read-only, not slave, and not shared. put_union_sb() unwinds everything clone_union_tree() does. It is called when the superblock is deactivated. Thus, you can lazy unmount a union mount and when the last reference goes away, the union will be torn down. --- fs/namespace.c | 67 +++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/mount.h | 2 + 2 files changed, 69 insertions(+), 0 deletions(-) diff --git a/fs/namespace.c b/fs/namespace.c index 7a237f62..ed8390f 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -1622,6 +1622,73 @@ static int check_topmost_union_mnt(struct vfsmount *topmost_mnt, int mnt_flags) #endif } +void put_union_sb(struct super_block *sb) +{ + struct vfsmount *mnt = sb->s_union_lower_mnts; + LIST_HEAD(umount_list); + + if (!mnt) + return; + br_write_lock(vfsmount_lock); + umount_tree(mnt, 0, &umount_list); + br_write_unlock(vfsmount_lock); + release_mounts(&umount_list); + sb->s_union_lower_mnts = 0; + sb->s_union_count = 0; +} + +/** + * clone_union_tree - Clone all union-able mounts at this mountpoint + * @topmost: vfsmount of topmost layer + * @mntpnt: target of union mount + * + * Given the target mountpoint of a union mount, clone all the mounts at that + * mountpoint (well, pathname) that qualify as a union lower layer. Increment + * the hard readonly count of the lower layer superblocks. + * + * Returns error if any of the mounts or submounts mounted on or below this + * pathname are unsuitable for union mounting. This means you can't construct + * a union mount at the root of an existing mount without unioning it. + * + * XXX - Maybe should take # of layers to go down as an argument. But how to + * pass this in through mount options? All solutions look ugly. Currently you + * express your intention through mounting file systems on the same mountpoint, + * which is pretty elegant. + */ +static int clone_union_tree(struct vfsmount *topmost, struct path *mntpnt) +{ + struct vfsmount *mnt, *cloned_tree; + + if (!IS_ROOT(mntpnt->dentry)) { + printk(KERN_INFO "union mount: mount point must be a root dir\n"); + return -EINVAL; + } + + /* Look for the "lowest" layer to union. */ + mnt = mntpnt->mnt; + while (mnt->mnt_parent->mnt_root == mnt->mnt_mountpoint) { + /* Got root (mnt)? */ + if (mnt->mnt_parent == mnt) + break; + mnt = mnt->mnt_parent; + } + + /* Clone all the read-only mounts and submounts, only if they + * are not shared or slave, and increment the hard read-only + * users count on each one. If this can't be done for every + * mount and submount below this one, fail. + */ + cloned_tree = copy_tree(mnt, mnt->mnt_root, + CL_COPY_ALL | CL_PRIVATE | + CL_NO_SHARED | CL_NO_SLAVE | + CL_MAKE_HARD_READONLY); + if (IS_ERR(cloned_tree)) + return PTR_ERR(cloned_tree); + + topmost->mnt_sb->s_union_lower_mnts = cloned_tree; + return 0; +} + /* * @source_mnt : mount tree to be attached * @nd : place the mount tree @source_mnt is attached diff --git a/include/linux/mount.h b/include/linux/mount.h index 3bd75e2..7f4ef7e 100644 --- a/include/linux/mount.h +++ b/include/linux/mount.h @@ -115,4 +115,6 @@ extern void mark_mounts_for_expiry(struct list_head *mounts); extern dev_t name_to_dev_t(char *name); +extern void put_union_sb(struct super_block *sb); + #endif /* _LINUX_MOUNT_H */ -- To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html