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 2008-08-29 00:23:18.000000000 +0900 +++ /usr/src/sys/fs/unionfs/union_vfsops.c 2008-10-21 19:50:46.000000000 +0900 @@ -104,6 +104,72 @@ } /* + * Check the cross mount. + * Cross mount operation has no necessity. This function prevent + * the mistake operation of the operator. + * XXX: This function can't check the complex cross mount. + * But now implementation of unionfs allows cross mount. + * So, if this function passed, it has no problem. + */ +static int +unionfs_check_crossmnt(struct vnode* lrvp, struct vnode* urvp, + struct thread *td) +{ + int error; + int len, len1, len2; + char *rbuf1, *rbuf2; + char *fbuf1, *fbuf2; + struct vnode *lvp, *uvp; + + ASSERT_VOP_UNLOCKED(lrvp, "unionfs_check_crossmnt: lrvp is locked."); + ASSERT_VOP_UNLOCKED(urvp, "unionfs_check_crossmnt: urvp is locked."); + + vn_lock(lrvp, LK_EXCLUSIVE | LK_RETRY); + error = vn_fullpath(td, lrvp, &rbuf1, &fbuf1); + VOP_UNLOCK(lrvp, 0); + if (error != 0) + return (error); + vn_lock(urvp, LK_EXCLUSIVE | LK_RETRY); + error = vn_fullpath(td, urvp, &rbuf2, &fbuf2); + VOP_UNLOCK(urvp, 0); + if (error != 0) { + free(fbuf1, M_TEMP); + return (error); + } + len1 = strlen(rbuf1); + len2 = strlen(rbuf2); + len = (len1 < len2 ? len1 : len2); + + UNIONFSDEBUG("unionfs_check_crossmnt: rbuf1: %s\n", rbuf1); + UNIONFSDEBUG("unionfs_check_crossmnt: rbuf2: %s\n", rbuf2); + + if (strncmp(rbuf1, rbuf2, len) == 0) + error = (EXDEV); + + free(fbuf1, M_TEMP); + free(fbuf2, M_TEMP); + + if (error == 0 && lrvp->v_op == &unionfs_vnodeops) { + lvp = UNIONFSVPTOLOWERVP(lrvp); + uvp = UNIONFSVPTOUPPERVP(lrvp); + if (lvp != NULLVP) + error = unionfs_check_crossmnt(lvp, urvp, td); + if (uvp != NULLVP && error == 0) + error = unionfs_check_crossmnt(uvp, urvp, td); + } + if (error == 0 && urvp->v_op == &unionfs_vnodeops) { + lvp = UNIONFSVPTOLOWERVP(urvp); + uvp = UNIONFSVPTOUPPERVP(urvp); + if (lvp != NULLVP) + error = unionfs_check_crossmnt(lrvp, lvp, td); + if (uvp != NULLVP && error == 0) + error = unionfs_check_crossmnt(lrvp, uvp, td); + } + + return (error); +} + +/* * Mount unionfs layer. */ static int @@ -281,6 +347,15 @@ vrele(ndp->ni_dvp); ndp->ni_dvp = NULLVP; + /* check cross 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);