summaryrefslogtreecommitdiff
path: root/usr/src/uts/common/fs
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/uts/common/fs')
-rw-r--r--usr/src/uts/common/fs/nfs/nfs3_srv.c64
-rw-r--r--usr/src/uts/common/fs/nfs/nfs_srv.c116
2 files changed, 144 insertions, 36 deletions
diff --git a/usr/src/uts/common/fs/nfs/nfs3_srv.c b/usr/src/uts/common/fs/nfs/nfs3_srv.c
index b10ae94693..7f5f4611b3 100644
--- a/usr/src/uts/common/fs/nfs/nfs3_srv.c
+++ b/usr/src/uts/common/fs/nfs/nfs3_srv.c
@@ -382,6 +382,9 @@ rfs3_lookup(LOOKUP3args *args, LOOKUP3res *resp, struct exportinfo *exi,
dvap = NULL;
+ if (exi != NULL)
+ exi_hold(exi);
+
/*
* Allow lookups from the root - the default
* location of the public filehandle.
@@ -420,8 +423,19 @@ rfs3_lookup(LOOKUP3args *args, LOOKUP3res *resp, struct exportinfo *exi,
fhp = &args->what.dir;
if (strcmp(args->what.name, "..") == 0 &&
EQFID(&exi->exi_fid, FH3TOFIDP(fhp))) {
- resp->status = NFS3ERR_NOENT;
- goto out1;
+ if ((exi->exi_export.ex_flags & EX_NOHIDE) &&
+ (dvp->v_flag & VROOT)) {
+ /*
+ * special case for ".." and 'nohide'exported root
+ */
+ if (rfs_climb_crossmnt(&dvp, &exi, cr) != 0) {
+ resp->status = NFS3ERR_ACCES;
+ goto out1;
+ }
+ } else {
+ resp->status = NFS3ERR_NOENT;
+ goto out1;
+ }
}
ca = (struct sockaddr *)svc_getrpccaller(req->rq_xprt)->buf;
@@ -439,10 +453,12 @@ rfs3_lookup(LOOKUP3args *args, LOOKUP3res *resp, struct exportinfo *exi,
*/
if (PUBLIC_FH3(&args->what.dir)) {
publicfh_flag = TRUE;
+
+ exi_rele(exi);
+
error = rfs_publicfh_mclookup(name, dvp, cr, &vp,
&exi, &sec);
- if (error && exi != NULL)
- exi_rele(exi); /* See comment below Re: publicfh_flag */
+
/*
* Since WebNFS may bypass MOUNT, we need to ensure this
* request didn't come from an unlabeled admin_low client.
@@ -464,8 +480,6 @@ rfs3_lookup(LOOKUP3args *args, LOOKUP3res *resp, struct exportinfo *exi,
if (tp == NULL || tp->tpc_tp.tp_doi !=
l_admin_low->tsl_doi || tp->tpc_tp.host_type !=
SUN_CIPSO) {
- if (exi != NULL)
- exi_rele(exi);
VN_RELE(vp);
error = EACCES;
}
@@ -480,6 +494,12 @@ rfs3_lookup(LOOKUP3args *args, LOOKUP3res *resp, struct exportinfo *exi,
if (name != args->what.name)
kmem_free(name, MAXPATHLEN + 1);
+ if (error == 0 && vn_ismntpt(vp)) {
+ error = rfs_cross_mnt(&vp, &exi);
+ if (error)
+ VN_RELE(vp);
+ }
+
if (is_system_labeled() && error == 0) {
bslabel_t *clabel = req->rq_label;
@@ -490,8 +510,6 @@ rfs3_lookup(LOOKUP3args *args, LOOKUP3res *resp, struct exportinfo *exi,
if (!blequal(&l_admin_low->tsl_label, clabel)) {
if (!do_rfs_label_check(clabel, dvp,
DOMINANCE_CHECK, exi)) {
- if (publicfh_flag && exi != NULL)
- exi_rele(exi);
VN_RELE(vp);
error = EACCES;
}
@@ -512,15 +530,6 @@ rfs3_lookup(LOOKUP3args *args, LOOKUP3res *resp, struct exportinfo *exi,
auth_weak = TRUE;
}
- /*
- * If publicfh_flag is true then we have called rfs_publicfh_mclookup
- * and have obtained a new exportinfo in exi which needs to be
- * released. Note that the original exportinfo pointed to by exi
- * will be released by the caller, common_dispatch.
- */
- if (publicfh_flag)
- exi_rele(exi);
-
if (error) {
VN_RELE(vp);
goto out;
@@ -529,6 +538,7 @@ rfs3_lookup(LOOKUP3args *args, LOOKUP3res *resp, struct exportinfo *exi,
va.va_mask = AT_ALL;
vap = rfs4_delegated_getattr(vp, &va, 0, cr) ? NULL : &va;
+ exi_rele(exi);
VN_RELE(vp);
resp->status = NFS3_OK;
@@ -556,6 +566,9 @@ out:
} else
resp->status = puterrno3(error);
out1:
+ if (exi != NULL)
+ exi_rele(exi);
+
DTRACE_NFSV3_4(op__lookup__done, struct svc_req *, req,
cred_t *, cr, vnode_t *, dvp, LOOKUP3res *, resp);
@@ -3609,13 +3622,18 @@ good:
if (vn_is_nfs_reparse(nvp, cr))
nvap->va_type = VLNK;
- vattr_to_post_op_attr(nvap, &infop[i].attr);
-
- error = makefh3(&infop[i].fh.handle, nvp, exi);
- if (!error)
- infop[i].fh.handle_follows = TRUE;
- else
+ if (vn_ismntpt(nvp)) {
+ infop[i].attr.attributes = FALSE;
infop[i].fh.handle_follows = FALSE;
+ } else {
+ vattr_to_post_op_attr(nvap, &infop[i].attr);
+
+ error = makefh3(&infop[i].fh.handle, nvp, exi);
+ if (!error)
+ infop[i].fh.handle_follows = TRUE;
+ else
+ infop[i].fh.handle_follows = FALSE;
+ }
VN_RELE(nvp);
dp = nextdp(dp);
diff --git a/usr/src/uts/common/fs/nfs/nfs_srv.c b/usr/src/uts/common/fs/nfs/nfs_srv.c
index 0588c5643e..2535ab3219 100644
--- a/usr/src/uts/common/fs/nfs/nfs_srv.c
+++ b/usr/src/uts/common/fs/nfs/nfs_srv.c
@@ -18,9 +18,10 @@
*
* CDDL HEADER END
*/
+
/*
* Copyright (c) 1994, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
* Copyright (c) 2016 by Delphix. All rights reserved.
*/
@@ -329,6 +330,80 @@ rfs_setattr_getfh(struct nfssaargs *args)
return (&args->saa_fh);
}
+/* Change and release @exip and @vpp only in success */
+int
+rfs_cross_mnt(vnode_t **vpp, struct exportinfo **exip)
+{
+ struct exportinfo *exi;
+ vnode_t *vp = *vpp;
+ fid_t fid;
+ int error;
+
+ VN_HOLD(vp);
+
+ if ((error = traverse(&vp)) != 0) {
+ VN_RELE(vp);
+ return (error);
+ }
+
+ bzero(&fid, sizeof (fid));
+ fid.fid_len = MAXFIDSZ;
+ error = VOP_FID(vp, &fid, NULL);
+ if (error) {
+ VN_RELE(vp);
+ return (error);
+ }
+
+ exi = checkexport(&vp->v_vfsp->vfs_fsid, &fid);
+ if (exi == NULL ||
+ (exi->exi_export.ex_flags & EX_NOHIDE) == 0) {
+ /*
+ * It is not error, just subdir is not exported
+ * or "nohide" is not set
+ */
+ if (exi != NULL)
+ exi_rele(exi);
+ VN_RELE(vp);
+ } else {
+ /* go to submount */
+ exi_rele(*exip);
+ *exip = exi;
+
+ VN_RELE(*vpp);
+ *vpp = vp;
+ }
+
+ return (0);
+}
+
+/*
+ * Given mounted "dvp" and "exi", go upper mountpoint
+ * with dvp/exi correction
+ * Return 0 in success
+ */
+int
+rfs_climb_crossmnt(vnode_t **dvpp, struct exportinfo **exip, cred_t *cr)
+{
+ struct exportinfo *exi;
+ vnode_t *dvp = *dvpp;
+
+ ASSERT(dvp->v_flag & VROOT);
+
+ VN_HOLD(dvp);
+ dvp = untraverse(dvp);
+ exi = nfs_vptoexi(NULL, dvp, cr, NULL, NULL, FALSE);
+ if (exi == NULL) {
+ VN_RELE(dvp);
+ return (-1);
+ }
+
+ exi_rele(*exip);
+ *exip = exi;
+ VN_RELE(*dvpp);
+ *dvpp = dvp;
+
+ return (0);
+}
/*
* Directory lookup.
* Returns an fhandle and file attributes for file name in a directory.
@@ -381,6 +456,8 @@ rfs_lookup(struct nfsdiropargs *da, struct nfsdiropres *dr,
}
}
+ exi_hold(exi);
+
/*
* Not allow lookup beyond root.
* If the filehandle matches a filehandle of the exi,
@@ -388,9 +465,19 @@ rfs_lookup(struct nfsdiropargs *da, struct nfsdiropres *dr,
*/
if (strcmp(da->da_name, "..") == 0 &&
EQFID(&exi->exi_fid, (fid_t *)&fhp->fh_len)) {
- VN_RELE(dvp);
- dr->dr_status = NFSERR_NOENT;
- return;
+ if ((exi->exi_export.ex_flags & EX_NOHIDE) &&
+ (dvp->v_flag & VROOT)) {
+ /*
+ * special case for ".." and 'nohide'exported root
+ */
+ if (rfs_climb_crossmnt(&dvp, &exi, cr) != 0) {
+ error = NFSERR_ACCES;
+ goto out;
+ }
+ } else {
+ error = NFSERR_NOENT;
+ goto out;
+ }
}
ca = (struct sockaddr *)svc_getrpccaller(req->rq_xprt)->buf;
@@ -398,8 +485,8 @@ rfs_lookup(struct nfsdiropargs *da, struct nfsdiropres *dr,
MAXPATHLEN);
if (name == NULL) {
- dr->dr_status = NFSERR_ACCES;
- return;
+ error = NFSERR_ACCES;
+ goto out;
}
/*
@@ -413,6 +500,9 @@ rfs_lookup(struct nfsdiropargs *da, struct nfsdiropres *dr,
*/
if (PUBLIC_FH2(fhp)) {
publicfh_flag = TRUE;
+
+ exi_rele(exi);
+
error = rfs_publicfh_mclookup(name, dvp, cr, &vp, &exi,
&sec);
} else {
@@ -426,6 +516,11 @@ rfs_lookup(struct nfsdiropargs *da, struct nfsdiropres *dr,
if (name != da->da_name)
kmem_free(name, MAXPATHLEN);
+ if (error == 0 && vn_ismntpt(vp)) {
+ error = rfs_cross_mnt(&vp, &exi);
+ if (error)
+ VN_RELE(vp);
+ }
if (!error) {
va.va_mask = AT_ALL; /* we want everything */
@@ -452,15 +547,10 @@ rfs_lookup(struct nfsdiropargs *da, struct nfsdiropres *dr,
VN_RELE(vp);
}
+out:
VN_RELE(dvp);
- /*
- * If publicfh_flag is true then we have called rfs_publicfh_mclookup
- * and have obtained a new exportinfo in exi which needs to be
- * released. Note the the original exportinfo pointed to by exi
- * will be released by the caller, comon_dispatch.
- */
- if (publicfh_flag && exi != NULL)
+ if (exi != NULL)
exi_rele(exi);
/*