Print this page
2988 nfssrv: need ability to go to submounts for v3 and v2 protocols
Portions contributed by: Marcel Telka <marcel.telka@nexenta.com>
Portions contributed by: Jean McCormack <jean.mccormack@nexenta.com>
Reviewed by: Yuri Pankov <yuri.pankov@nexenta.com>
Reviewed by: Alek Pinchuk <alek.pinchuk@nexenta.com>
Reviewed by: Dan Fields <dan.fields@nexenta.com>
Reviewed by: Dan McDonald <danmcd@joyent.com>
Change-Id: I6fdf110cc17e789353c4442b83a46cb80643456e
@@ -16,13 +16,14 @@
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* 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.
*/
/*
* Copyright (c) 1983,1984,1985,1986,1987,1988,1989 AT&T.
@@ -327,11 +328,85 @@
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.
*/
/* ARGSUSED */
void
@@ -379,29 +454,41 @@
dr->dr_status = NFSERR_STALE;
return;
}
}
+ exi_hold(exi);
+
/*
* Not allow lookup beyond root.
* If the filehandle matches a filehandle of the exi,
* then the ".." refers beyond the root of an exported filesystem.
*/
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;
name = nfscmd_convname(ca, exi, da->da_name, NFSCMD_CONV_INBOUND,
MAXPATHLEN);
if (name == NULL) {
- dr->dr_status = NFSERR_ACCES;
- return;
+ error = NFSERR_ACCES;
+ goto out;
}
/*
* If the public filehandle is used then allow
* a multi-component lookup, i.e. evaluate
@@ -411,10 +498,13 @@
* This may result in a vnode in another filesystem
* which is OK as long as the filesystem is exported.
*/
if (PUBLIC_FH2(fhp)) {
publicfh_flag = TRUE;
+
+ exi_rele(exi);
+
error = rfs_publicfh_mclookup(name, dvp, cr, &vp, &exi,
&sec);
} else {
/*
* Do a normal single component lookup.
@@ -424,10 +514,15 @@
}
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 */
error = rfs4_delegated_getattr(vp, &va, 0, cr);
@@ -450,19 +545,14 @@
}
}
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);
/*
* If it's public fh, no 0x81, and client's flavor is
* invalid, set WebNFS status to WNFSERR_CLNT_FLAVOR now.