--- usr.sbin/mountd/mountd.c +++ usr.sbin/mountd/mountd.c @@ -2047,6 +2047,8 @@ } else if (!strcmp(cpopt, "webnfs")) { *exflagsp |= (MNT_EXPUBLIC|MNT_EXRDONLY|MNT_EXPORTANON); opt_flags |= OP_MAPALL; + } else if (!strcmp(cpopt, "recursive")) { + *exflagsp |= MNT_EXRECURSIVE; } else if (cpoptarg && !strcmp(cpopt, "index")) { ep->ex_indexfile = strdup(cpoptarg); } else if (!strcmp(cpopt, "quiet")) { --- cddl/compat/opensolaris/misc/fsshare.c +++ cddl/compat/opensolaris/misc/fsshare.c @@ -112,11 +112,12 @@ * * Recognized keywords: * - * ro, maproot, mapall, mask, network, alldirs, public, webnfs, index, quiet + * ro, maproot, mapall, mask, network, alldirs, recursive, public, webnfs, index, quiet * */ static const char *known_opts[] = { "ro", "maproot", "mapall", "mask", - "network", "alldirs", "public", "webnfs", "index", "quiet", NULL }; + "network", "alldirs", "recursive", "public", "webnfs", "index", "quiet", + NULL }; static char * translate_opts(const char *shareopts) { --- sys/sys/mount.h +++ sys/sys/mount.h @@ -250,6 +250,8 @@ #define MNT_EXPORTANON 0x00000400 /* use anon uid mapping for everyone */ #define MNT_EXKERB 0x00000800 /* exported with Kerberos uid mapping */ #define MNT_EXPUBLIC 0x20000000 /* public export (WebNFS) */ +#define MNT_EXRECURSIVE 0x00000001 /* entire file systems hierarchy is + recursively exported */ /* * Flags set by internal operations, @@ -750,6 +752,8 @@ extern TAILQ_HEAD(mntlist, mount) mountlist; /* mounted filesystem list */ extern struct mtx mountlist_mtx; extern struct nfs_public nfs_pub; +struct mount *vfs_exrecursive(struct mount *, struct sockaddr *); +int vfs_extraverse(struct vnode **, struct sockaddr *); /* * Declarations for these vfs default operations are located in --- sys/kern/vfs_export.c +++ sys/kern/vfs_export.c @@ -457,6 +457,71 @@ return (np); } +struct mount * +vfs_exrecursive(struct mount *mp, struct sockaddr *nam) +{ + struct netcred *np; + struct mount *pmp; + + pmp = mp; + for (;;) { + lockmgr(&mp->mnt_explock, LK_SHARED, NULL); + if (mp->mnt_export == NULL) + break; + np = vfs_export_lookup(mp, nam); + if (np == NULL) + break; + if (!(np->netc_exflags & MNT_EXRECURSIVE)) + break; + lockmgr(&mp->mnt_explock, LK_RELEASE, NULL); + if (mp->mnt_vnodecovered == NULL) + return (mp); + pmp = mp; + mp = pmp->mnt_vnodecovered->v_mount; + } + lockmgr(&mp->mnt_explock, LK_RELEASE, NULL); + if (mp != pmp) + return (pmp); + return (NULL); +} + +int +vfs_extraverse(struct vnode **cvpp, struct sockaddr *nam) +{ + struct vnode *cvp, *tvp; + struct mount *mp; + struct netcred *np; + int error, lktype; + + cvp = *cvpp; + if (cvp->v_type != VDIR) + return (0); + lktype = VOP_ISLOCKED(cvp); + for (;;) { + mp = cvp->v_mountedhere; + if (mp == NULL) + goto nounlock; + lockmgr(&mp->mnt_explock, LK_SHARED, NULL); + if (mp->mnt_export == NULL) + break; + np = vfs_export_lookup(mp, nam); + if (np == NULL) + break; + if (!(np->netc_exflags & MNT_EXRECURSIVE)) + break; + lockmgr(&mp->mnt_explock, LK_RELEASE, NULL); + vput(cvp); + error = VFS_ROOT(mp, lktype, &tvp); + if (error != 0) + return (error); + cvp = tvp; + } + lockmgr(&mp->mnt_explock, LK_RELEASE, NULL); +nounlock: + *cvpp = cvp; + return (0); +} + /* * XXX: This comment comes from the deprecated ufs_check_export() * XXX: and may not entirely apply, but lacking something better: --- sys/kern/vfs_lookup.c +++ sys/kern/vfs_lookup.c @@ -625,9 +625,7 @@ if (dp == ndp->ni_rootdir || dp == ndp->ni_topdir || dp == rootvnode || - pr != NULL || - ((dp->v_vflag & VV_ROOT) != 0 && - (cnp->cn_flags & NOCROSSMOUNT) != 0)) { + pr != NULL) { ndp->ni_dvp = dp; ndp->ni_vp = dp; vfslocked = VFS_LOCK_GIANT(dp->v_mount); --- sys/nfsclient/nfs_subs.c +++ sys/nfsclient/nfs_subs.c @@ -545,8 +545,8 @@ vap->va_rdev = rdev; mtime_save = vap->va_mtime; vap->va_mtime = mtime; - vap->va_fsid = vp->v_mount->mnt_stat.f_fsid.val[0]; if (v3) { + vap->va_fsid = fxdr_hyper(&fp->fa3_fsid); vap->va_nlink = fxdr_unsigned(u_short, fp->fa_nlink); vap->va_uid = fxdr_unsigned(uid_t, fp->fa_uid); vap->va_gid = fxdr_unsigned(gid_t, fp->fa_gid); @@ -560,6 +560,7 @@ vap->va_flags = 0; vap->va_filerev = 0; } else { + vap->va_fsid = fxdr_unsigned(u_int32_t, fp->fa2_fsid); vap->va_nlink = fxdr_unsigned(u_short, fp->fa_nlink); vap->va_uid = fxdr_unsigned(uid_t, fp->fa_uid); vap->va_gid = fxdr_unsigned(gid_t, fp->fa_gid); --- sys/nfsserver/nfs_serv.c +++ sys/nfsserver/nfs_serv.c @@ -607,9 +607,12 @@ * the same potential blocking reason ) and reply. */ vp = ndp->ni_vp; - bzero((caddr_t)fhp, sizeof(nfh)); - fhp->fh_fsid = vp->v_mount->mnt_stat.f_fsid; - error = VOP_VPTOFH(vp, &fhp->fh_fid); + error = vfs_extraverse(&vp, nam); + if (!error) { + bzero((caddr_t)fhp, sizeof(nfh)); + fhp->fh_fsid = vp->v_mount->mnt_stat.f_fsid; + error = VOP_VPTOFH(vp, &fhp->fh_fid); + } if (!error) error = VOP_GETATTR(vp, vap, cred); @@ -3231,16 +3234,14 @@ if (VFS_VGET(vp->v_mount, dp->d_fileno, LK_EXCLUSIVE, &nvp)) goto invalid; + if (vfs_extraverse(&nvp, nam)) { + vput(nvp); + nvp = NULL; + goto invalid; + } bzero((caddr_t)nfhp, NFSX_V3FH); nfhp->fh_fsid = nvp->v_mount->mnt_stat.f_fsid; - /* - * XXXRW: Assert the mountpoints are the same so that - * we know that acquiring Giant based on the - * directory is the right thing for the child. - */ - KASSERT(nvp->v_mount == vp->v_mount, - ("nfsrv_readdirplus: nvp mount != vp mount")); if (VOP_VPTOFH(nvp, &nfhp->fh_fid)) { vput(nvp); nvp = NULL; --- sys/nfsserver/nfs_srvsubs.c +++ sys/nfsserver/nfs_srvsubs.c @@ -590,7 +590,7 @@ char *fromcp, *tocp, *cp; struct iovec aiov; struct uio auio; - struct vnode *dp; + struct vnode *dp, *topvp; int error, rdonly, linklen; struct componentname *cnp = &ndp->ni_cnd; int lockleaf = (cnp->cn_flags & LOCKLEAF) != 0; @@ -600,6 +600,7 @@ vfslocked = 0; dvfslocked = 0; *retdirp = NULL; + topvp = NULL; cnp->cn_flags |= NOMACCHECK; cnp->cn_pnbuf = uma_zalloc(namei_zone, M_WAITOK); @@ -733,6 +734,20 @@ vfslocked = tvfslocked; } } else { + struct mount *mp; + + /* + * For recursive export limit lookup to the root of + * the exported hierarchy. + */ + mp = vfs_exrecursive(dp->v_mount, nam); + if (mp == NULL) + mp = dp->v_mount; + error = VFS_ROOT(mp, LK_SHARED, &topvp); + KASSERT(error == 0, ("VFS_ROOT(%s) failed (error=%d).", + mp->mnt_stat.f_mntonname, error)); + VOP_UNLOCK(topvp, 0); + ndp->ni_topdir = topvp; cnp->cn_flags |= NOCROSSMOUNT; } @@ -862,6 +877,8 @@ mtx_unlock(&Giant); cnp->cn_flags &= ~GIANTHELD; } + if (topvp != NULL) + vrele(topvp); /* * nfs_namei() guarentees that fields will not contain garbage --- sys/fs/nfsserver/nfs_nfsdport.c +++ sys/fs/nfsserver/nfs_nfsdport.c @@ -241,6 +241,7 @@ struct vnode **retdirp) { struct componentname *cnp = &ndp->ni_cnd; + struct vnode *topvp; int i; struct iovec aiov; struct uio auio; @@ -248,6 +249,7 @@ int error = 0, crossmnt; char *cp; + topvp = NULL; *retdirp = NULL; cnp->cn_nameptr = cnp->cn_pnbuf; /* @@ -295,6 +297,11 @@ * mount while traversing the file system above * the mount point, unless enable_crossmntpt is set. */ + error = VFS_ROOT(dp->v_mount, LK_SHARED, &topvp); + KASSERT(error == 0, ("VFS_ROOT(%s) failed (error=%d).", + dp->v_mount->mnt_stat.f_mntonname, error)); + VOP_UNLOCK(topvp, 0); + ndp->ni_topdir = topvp; cnp->cn_flags |= NOCROSSMOUNT; crossmnt = 0; } @@ -426,6 +433,8 @@ cnp->cn_flags &= ~LOCKLEAF; out: + if (topvp != NULL) + vrele(topvp); if (error) { uma_zfree(namei_zone, cnp->cn_pnbuf); ndp->ni_vp = NULL;