diff -urBN /usr/src.orig/sbin/mount_unionfs/mount_unionfs.8 /usr/src/sbin/mount_unionfs/mount_unionfs.8 --- /usr/src.orig/sbin/mount_unionfs/mount_unionfs.8 2011-04-18 14:58:46.000000000 +0900 +++ /usr/src/sbin/mount_unionfs/mount_unionfs.8 2011-04-21 04:51:17.000000000 +0900 @@ -158,6 +158,17 @@ which will still allow the lower files to be accessed by a different pathname. .Pp +It is possible to mount unionfs multiple times more than once at a +mount point. However, exceeding multiple mounts could consume kernel +stack over its limits and lead a system panic. For this reason, the +unionfs multiple mounts at a mount point is limited to two-layered by +default. It is adjustable with sysctl value 'vfs.unionfs.recursive_limit'. +.Pp +Notice: It is a null effect of 'vfs.unionfs.recursive_limit' by the +combination of union and loopback filesystem. Exceeding multiple mounts +of unionfs at a mount point using the combination of union and loopback +filesystem could lead a system panic easily. +.Pp Except in the case of a directory, access to an object is granted via the normal file system access checks. For directories, the current user must have access to both the upper diff -urBN /usr/src.orig/sys/fs/unionfs/union.h /usr/src/sys/fs/unionfs/union.h --- /usr/src.orig/sys/fs/unionfs/union.h 2011-04-18 15:06:07.000000000 +0900 +++ /usr/src/sys/fs/unionfs/union.h 2011-04-20 07:47:51.000000000 +0900 @@ -106,6 +106,7 @@ int unionfs_init(struct vfsconf *vfsp); int unionfs_uninit(struct vfsconf *vfsp); +int unionfs_check_crossmnt(struct vnode* lrvp, struct vnode* urvp, struct thread *td); int unionfs_nodeget(struct mount *mp, struct vnode *uppervp, struct vnode *lowervp, struct vnode *dvp, struct vnode **vpp, struct componentname *cnp, struct thread *td); void unionfs_noderem(struct vnode *vp, struct thread *td); void unionfs_get_node_status(struct unionfs_node *unp, struct thread *td, struct unionfs_node_status **unspp); diff -urBN /usr/src.orig/sys/fs/unionfs/union_subr.c /usr/src/sys/fs/unionfs/union_subr.c --- /usr/src.orig/sys/fs/unionfs/union_subr.c 2011-04-18 15:06:07.000000000 +0900 +++ /usr/src/sys/fs/unionfs/union_subr.c 2011-04-20 09:22:24.000000000 +0900 @@ -51,6 +51,7 @@ #include #include #include +#include #include @@ -58,19 +59,43 @@ #include -#define NUNIONFSNODECACHE 16 +#define NUNIONFSNODECACHE 16 +#define RECURSIVE_LIMIT_MAX 8 +#define RECURSIVE_LIMIT_DEFAULT 1 static MALLOC_DEFINE(M_UNIONFSHASH, "UNIONFS hash", "UNIONFS hash table"); MALLOC_DEFINE(M_UNIONFSNODE, "UNIONFS node", "UNIONFS vnode private part"); MALLOC_DEFINE(M_UNIONFSPATH, "UNIONFS path", "UNIONFS path private part"); +static struct mtx unionfs_mtx; + +static SYSCTL_NODE(_vfs, OID_AUTO, unionfs, CTLFLAG_RD, 0, "UNION filesystem"); + +static int unionfs_recursive_limit = RECURSIVE_LIMIT_DEFAULT; +static int sysctl_unionfs_recursive_limit(SYSCTL_HANDLER_ARGS); +SYSCTL_PROC(_vfs_unionfs, OID_AUTO, recursive_limit, CTLTYPE_INT | CTLFLAG_RW, + NULL, 0, sysctl_unionfs_recursive_limit, "I", + "unionfs recursive mount limit"); + +struct unionfsv { + STAILQ_ENTRY(unionfsv) entries; + struct vnode *lvp; + struct vnode *uvp; + int nl; +}; + +STAILQ_HEAD(unionfsvq, unionfsv); + +static int unionfsvq_put(struct unionfsvq *head, struct vnode *lvp, + struct vnode *uvp, int nl); + /* * Initialize */ int unionfs_init(struct vfsconf *vfsp) { - UNIONFSDEBUG("unionfs_init\n"); /* printed during system boot */ + mtx_init(&unionfs_mtx, "unionfs lock", NULL, MTX_DEF); return (0); } @@ -80,9 +105,165 @@ int unionfs_uninit(struct vfsconf *vfsp) { + mtx_destroy(&unionfs_mtx); + return (0); +} + +static int +sysctl_unionfs_recursive_limit(SYSCTL_HANDLER_ARGS) +{ + int error; + int val; + + mtx_lock(&unionfs_mtx); + val = unionfs_recursive_limit; + mtx_unlock(&unionfs_mtx); + + error = sysctl_handle_int(oidp, &val, 0, req); + if (error != 0 || req->newptr == NULL) + return (error); + + if (val < 0 || val > RECURSIVE_LIMIT_MAX) + return (EINVAL); + + mtx_lock(&unionfs_mtx); + unionfs_recursive_limit = val; + mtx_unlock(&unionfs_mtx); + + return (error); +} + +static int +unionfsvq_put(struct unionfsvq *head, struct vnode *lvp, struct vnode *uvp, + int nl) +{ + struct unionfsv *np; + + np = (struct unionfsv*)malloc(sizeof(struct unionfsv), + M_TEMP, M_NOWAIT); + if (np == NULL) + return (ENOMEM); + np->lvp = lvp; + np->uvp = uvp; + np->nl = nl +1; + STAILQ_INSERT_HEAD(head, np, entries); return (0); } +/* + * Checking the cross mounts, recursive mounts and the combination of + * cross and recursive mounts of unionfs. + * NOTICE: This function can not detect the complex cross mounts situation. + * (e.g. nullfs) + */ +int +unionfs_check_crossmnt(struct vnode* lrvp, struct vnode* urvp, + struct thread *td) +{ + int error; + int umnl; + int len, len1, len2; + char *rbuf1, *rbuf2; + char *fbuf1, *fbuf2; + struct vnode *lvp, *uvp; + struct unionfsvq head; + struct unionfsv *np, *ntmp; + + np = NULL; + STAILQ_INIT(&head); + + mtx_lock(&unionfs_mtx); + umnl = unionfs_recursive_limit; + mtx_unlock(&unionfs_mtx); + + error = unionfsvq_put(&head, lrvp, urvp, -1); + if (error != 0) + goto unionfs_check_crossmnt_out; + + while (!STAILQ_EMPTY(&head)) { + np = STAILQ_FIRST(&head); + STAILQ_REMOVE_HEAD(&head, entries); + + if (np->nl > umnl) { + printf("unionfs: recursive limit %d was exceeded.\n", + umnl); + error = (EACCES); + goto unionfs_check_crossmnt_out; + } + + error = vn_fullpath(td, np->lvp, &rbuf1, &fbuf1); + if (error != 0) + goto unionfs_check_crossmnt_out; + error = vn_fullpath(td, np->uvp, &rbuf2, &fbuf2); + if (error != 0) { + free(fbuf1, M_TEMP); + goto unionfs_check_crossmnt_out; + } + len1 = strlen(rbuf1); + len2 = strlen(rbuf2); + len = (len1 < len2 ? len1 : len2); + + if (strncmp(rbuf1, rbuf2, len) == 0) + error = (EXDEV); + + free(fbuf1, M_TEMP); + free(fbuf2, M_TEMP); + + if (error != 0) + goto unionfs_check_crossmnt_out; + + if (np->lvp->v_op == &unionfs_vnodeops) { + lvp = UNIONFSVPTOLOWERVP(np->lvp); + uvp = UNIONFSVPTOUPPERVP(np->lvp); + if (lvp != NULLVP) { + error = unionfsvq_put(&head, lvp, + np->uvp, np->nl); + if (error != 0) + goto unionfs_check_crossmnt_out; + } + if (uvp != NULLVP) { + error = unionfsvq_put(&head, uvp, + np->uvp, np->nl); + if (error != 0) + goto unionfs_check_crossmnt_out; + } + } + + if (np->uvp->v_op == &unionfs_vnodeops) { + lvp = UNIONFSVPTOLOWERVP(np->uvp); + uvp = UNIONFSVPTOUPPERVP(np->uvp); + if (lvp != NULLVP) { + error = unionfsvq_put(&head, np->lvp, + lvp, np->nl); + if (error != 0) + goto unionfs_check_crossmnt_out; + } + if (uvp != NULLVP) { + error = unionfsvq_put(&head, np->lvp, + uvp, np->nl); + if (error != 0) + goto unionfs_check_crossmnt_out; + } + } + + free(np, M_TEMP); + np = NULL; + } + +unionfs_check_crossmnt_out: + if (np != NULL) + free(np, M_TEMP); + + np = STAILQ_FIRST(&head); + while (np != NULL) { + ntmp = STAILQ_NEXT(np, entries); + free(np, M_TEMP); + np = ntmp; + } + + return (error); +} + static struct unionfs_node_hashhead * unionfs_get_hashhead(struct vnode *dvp, char *path) { diff -urBN /usr/src.orig/sys/fs/unionfs/union_vfsops.c /usr/src/sys/fs/unionfs/union_vfsops.c --- /usr/src.orig/sys/fs/unionfs/union_vfsops.c 2011-04-18 15:06:07.000000000 +0900 +++ /usr/src/sys/fs/unionfs/union_vfsops.c 2011-04-20 08:35:56.000000000 +0900 @@ -242,6 +242,15 @@ lowerrootvp = mp->mnt_vnodecovered; upperrootvp = ndp->ni_vp; + /* check cross/recursive mount */ + VOP_UNLOCK(upperrootvp, 0); + error = unionfs_check_crossmnt(lowerrootvp, upperrootvp, td); + if (error != 0) { + vrele(upperrootvp); + return (error); + } + vn_lock(upperrootvp, LK_EXCLUSIVE | LK_RETRY); + /* create unionfs_mount */ ump = (struct unionfs_mount *)malloc(sizeof(struct unionfs_mount), M_UNIONFSMNT, M_WAITOK | M_ZERO);