Print this page
11083 support NFS server in zone
Portions contributed by: Dan Kruchinin <dan.kruchinin@nexenta.com>
Portions contributed by: Stepan Zastupov <stepan.zastupov@gmail.com>
Portions contributed by: Joyce McIntosh <joyce.mcintosh@nexenta.com>
Portions contributed by: Mike Zeller <mike@mikezeller.net>
Portions contributed by: Dan McDonald <danmcd@joyent.com>
Portions contributed by: Gordon Ross <gordon.w.ross@gmail.com>
Portions contributed by: Vitaliy Gusev <gusev.vitaliy@gmail.com>
Reviewed by: Rick McNeal <rick.mcneal@nexenta.com>
Reviewed by: Rob Gittins <rob.gittins@nexenta.com>
Reviewed by: Sanjay Nadkarni <sanjay.nadkarni@nexenta.com>
Reviewed by: Jason King <jbk@joyent.com>
Reviewed by: C Fraire <cfraire@me.com>
Change-Id: I22f289d357503f9b48a0bc2482cc4328a6d43d16
*** 18,36 ****
*
* CDDL HEADER END
*/
/*
- * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
* Copyright (c) 1990, 2010, Oracle and/or its affiliates. All rights reserved.
*/
/*
* Copyright 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T.
* All rights reserved.
*/
#include <sys/types.h>
#include <sys/param.h>
#include <sys/time.h>
#include <sys/vfs.h>
--- 18,38 ----
*
* CDDL HEADER END
*/
/*
* Copyright (c) 1990, 2010, Oracle and/or its affiliates. All rights reserved.
*/
/*
* Copyright 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T.
* All rights reserved.
*/
+ /*
+ * Copyright 2018 Nexenta Systems, Inc.
+ */
#include <sys/types.h>
#include <sys/param.h>
#include <sys/time.h>
#include <sys/vfs.h>
*** 63,80 ****
#include <nfs/nfs_clnt.h>
#include <nfs/nfs_acl.h>
#include <nfs/nfs_log.h>
#include <nfs/lm.h>
#include <sys/sunddi.h>
- #include <sys/pkp_hash.h>
! treenode_t *ns_root;
! struct exportinfo *exptable_path_hash[PKP_HASH_SIZE];
! struct exportinfo *exptable[EXPTABLESIZE];
!
! static int unexport(exportinfo_t *);
static void exportfree(exportinfo_t *);
static int loadindex(exportdata_t *);
extern void nfsauth_cache_free(exportinfo_t *);
extern int sec_svc_loadrootnames(int, int, caddr_t **, model_t);
--- 65,93 ----
#include <nfs/nfs_clnt.h>
#include <nfs/nfs_acl.h>
#include <nfs/nfs_log.h>
#include <nfs/lm.h>
#include <sys/sunddi.h>
! /*
! * exi_id support
! *
! * exi_id_next The next exi_id available.
! * exi_id_overflow The exi_id_next already overflowed, so we should
! * thoroughly check for duplicates.
! * exi_id_tree AVL tree indexed by exi_id.
! * nfs_exi_id_lock Lock to protect the export ID list
! *
! * All exi_id_next, exi_id_overflow, and exi_id_tree are protected by
! * nfs_exi_id_lock.
! */
! static int exi_id_next;
! static bool_t exi_id_overflow;
! avl_tree_t exi_id_tree;
! kmutex_t nfs_exi_id_lock;
! static int unexport(nfs_export_t *, exportinfo_t *);
static void exportfree(exportinfo_t *);
static int loadindex(exportdata_t *);
extern void nfsauth_cache_free(exportinfo_t *);
extern int sec_svc_loadrootnames(int, int, caddr_t **, model_t);
*** 81,112 ****
extern void sec_svc_freerootnames(int, int, caddr_t *);
static int build_seclist_nodups(exportdata_t *, secinfo_t *, int);
static void srv_secinfo_add(secinfo_t **, int *, secinfo_t *, int, int);
static void srv_secinfo_remove(secinfo_t **, int *, secinfo_t *, int);
! static void srv_secinfo_treeclimb(exportinfo_t *, secinfo_t *, int, bool_t);
#ifdef VOLATILE_FH_TEST
static struct ex_vol_rename *find_volrnm_fh(exportinfo_t *, nfs_fh4 *);
static uint32_t find_volrnm_fh_id(exportinfo_t *, nfs_fh4 *);
static void free_volrnm_list(exportinfo_t *);
#endif /* VOLATILE_FH_TEST */
- /*
- * exported_lock Read/Write lock that protects the exportinfo list.
- * This lock must be held when searching or modifiying
- * the exportinfo list.
- */
- krwlock_t exported_lock;
-
- /*
- * "public" and default (root) location for public filehandle
- */
- struct exportinfo *exi_public, *exi_root;
-
- fid_t exi_rootfid; /* for checking the default public file handle */
-
fhandle_t nullfh2; /* for comparing V2 filehandles */
/*
* macro for static dtrace probes to trace server namespace ref count mods.
*/
--- 94,112 ----
extern void sec_svc_freerootnames(int, int, caddr_t *);
static int build_seclist_nodups(exportdata_t *, secinfo_t *, int);
static void srv_secinfo_add(secinfo_t **, int *, secinfo_t *, int, int);
static void srv_secinfo_remove(secinfo_t **, int *, secinfo_t *, int);
! static void srv_secinfo_treeclimb(nfs_export_t *, exportinfo_t *,
! secinfo_t *, int, bool_t);
#ifdef VOLATILE_FH_TEST
static struct ex_vol_rename *find_volrnm_fh(exportinfo_t *, nfs_fh4 *);
static uint32_t find_volrnm_fh_id(exportinfo_t *, nfs_fh4 *);
static void free_volrnm_list(exportinfo_t *);
#endif /* VOLATILE_FH_TEST */
fhandle_t nullfh2; /* for comparing V2 filehandles */
/*
* macro for static dtrace probes to trace server namespace ref count mods.
*/
*** 115,124 ****
--- 115,133 ----
char *, (tag), int, (int)(flav), int, (int)(aftcnt))
#define exptablehash(fsid, fid) (nfs_fhhash((fsid), (fid)) & (EXPTABLESIZE - 1))
+ extern nfs_export_t *
+ nfs_get_export(void)
+ {
+ nfs_globals_t *ng = nfs_srv_getzg();
+ nfs_export_t *ne = ng->nfs_export;
+ ASSERT(ne != NULL);
+ return (ne);
+ }
+
static uint8_t
xor_hash(uint8_t *data, int len)
{
uint8_t h = 0;
*** 691,718 ****
exi_ret = tnode->tree_exi;
break;
}
}
! ASSERT(exi_ret); /* Every visible should have its home exportinfo */
return (exi_ret);
}
/*
* For NFS V4.
* Add or remove the newly exported or unexported security flavors of the
* given exportinfo from its ancestors upto the system root.
*/
! void
! srv_secinfo_treeclimb(exportinfo_t *exip, secinfo_t *sec, int seccnt,
! bool_t isadd)
{
! treenode_t *tnode = exip->exi_tree;
! ASSERT(RW_WRITE_HELD(&exported_lock));
! ASSERT(tnode != NULL);
if (seccnt == 0)
return;
/*
* If flavors are being added and the new export root isn't
--- 700,739 ----
exi_ret = tnode->tree_exi;
break;
}
}
! /* Every visible should have its home exportinfo */
! ASSERT(exi_ret != NULL);
return (exi_ret);
}
/*
* For NFS V4.
* Add or remove the newly exported or unexported security flavors of the
* given exportinfo from its ancestors upto the system root.
*/
! static void
! srv_secinfo_treeclimb(nfs_export_t *ne, exportinfo_t *exip, secinfo_t *sec,
! int seccnt, bool_t isadd)
{
! treenode_t *tnode;
! ASSERT(RW_WRITE_HELD(&ne->exported_lock));
+ /*
+ * exi_tree can be null for the zone root
+ * which means we're already at the "top"
+ * and there's nothing more to "climb".
+ */
+ tnode = exip->exi_tree;
+ if (tnode == NULL) {
+ /* Should only happen for... */
+ ASSERT(exip == ne->exi_root);
+ return;
+ }
+
if (seccnt == 0)
return;
/*
* If flavors are being added and the new export root isn't
*** 720,729 ****
--- 741,751 ----
* its pseudonode.
* Note - for VROOT exports the implicitly allowed flavors were
* transferred from the PSEUDO export in exportfs()
*/
if (isadd && !(exip->exi_vp->v_flag & VROOT) &&
+ !VN_CMP(exip->exi_vp, EXI_TO_ZONEROOTVP(exip)) &&
tnode->tree_vis->vis_seccnt > 0) {
srv_secinfo_add(&exip->exi_export.ex_secinfo,
&exip->exi_export.ex_seccnt, tnode->tree_vis->vis_secinfo,
tnode->tree_vis->vis_seccnt, FALSE);
}
*** 780,894 ****
if ((exi)->hash_name.next) \
(exi)->hash_name.next->hash_name.prev = (exi); \
*(bucket) = (exi);
void
! export_link(exportinfo_t *exi)
{
exportinfo_t **bckt;
! bckt = &exptable[exptablehash(&exi->exi_fsid, &exi->exi_fid)];
exp_hash_link(exi, fid_hash, bckt);
! bckt = &exptable_path_hash[pkp_tab_hash(exi->exi_export.ex_path,
strlen(exi->exi_export.ex_path))];
exp_hash_link(exi, path_hash, bckt);
}
/*
! * Initialization routine for export routines. Should only be called once.
*/
int
! nfs_exportinit(void)
{
! int error;
int i;
! rw_init(&exported_lock, NULL, RW_DEFAULT, NULL);
/*
* Allocate the place holder for the public file handle, which
* is all zeroes. It is initially set to the root filesystem.
*/
! exi_root = kmem_zalloc(sizeof (*exi_root), KM_SLEEP);
! exi_public = exi_root;
! exi_root->exi_export.ex_flags = EX_PUBLIC;
! exi_root->exi_export.ex_pathlen = 1; /* length of "/" */
! exi_root->exi_export.ex_path =
! kmem_alloc(exi_root->exi_export.ex_pathlen + 1, KM_SLEEP);
! exi_root->exi_export.ex_path[0] = '/';
! exi_root->exi_export.ex_path[1] = '\0';
! exi_root->exi_count = 1;
! mutex_init(&exi_root->exi_lock, NULL, MUTEX_DEFAULT, NULL);
! exi_root->exi_vp = rootdir;
! exi_rootfid.fid_len = MAXFIDSZ;
! error = vop_fid_pseudo(exi_root->exi_vp, &exi_rootfid);
! if (error) {
! mutex_destroy(&exi_root->exi_lock);
! kmem_free(exi_root, sizeof (*exi_root));
! return (error);
! }
/*
! * Initialize auth cache and auth cache lock
*/
for (i = 0; i < AUTH_TABLESIZE; i++) {
! exi_root->exi_cache[i] = kmem_alloc(sizeof (avl_tree_t),
KM_SLEEP);
! avl_create(exi_root->exi_cache[i], nfsauth_cache_clnt_compar,
! sizeof (struct auth_cache_clnt),
offsetof(struct auth_cache_clnt, authc_link));
}
! rw_init(&exi_root->exi_cache_lock, NULL, RW_DEFAULT, NULL);
! /* setup the fhandle template */
! exi_root->exi_fh.fh_fsid = rootdir->v_vfsp->vfs_fsid;
! exi_root->exi_fh.fh_xlen = exi_rootfid.fid_len;
! bcopy(exi_rootfid.fid_data, exi_root->exi_fh.fh_xdata,
! exi_rootfid.fid_len);
! exi_root->exi_fh.fh_len = sizeof (exi_root->exi_fh.fh_data);
! /*
! * Publish the exportinfo in the hash table
! */
! export_link(exi_root);
! nfslog_init();
! ns_root = NULL;
! return (0);
}
/*
! * Finalization routine for export routines. Called to cleanup previously
! * initialization work when the NFS server module could not be loaded correctly.
*/
void
! nfs_exportfini(void)
{
! int i;
/*
! * Deallocate the place holder for the public file handle.
*/
! srv_secinfo_list_free(exi_root->exi_export.ex_secinfo,
! exi_root->exi_export.ex_seccnt);
! mutex_destroy(&exi_root->exi_lock);
! rw_destroy(&exi_root->exi_cache_lock);
for (i = 0; i < AUTH_TABLESIZE; i++) {
! avl_destroy(exi_root->exi_cache[i]);
! kmem_free(exi_root->exi_cache[i], sizeof (avl_tree_t));
}
- kmem_free(exi_root, sizeof (*exi_root));
! rw_destroy(&exported_lock);
}
/*
* Check if 2 gss mechanism identifiers are the same.
*
* return FALSE if not the same.
* return TRUE if the same.
*/
--- 802,1110 ----
if ((exi)->hash_name.next) \
(exi)->hash_name.next->hash_name.prev = (exi); \
*(bucket) = (exi);
void
! export_link(nfs_export_t *ne, exportinfo_t *exi)
{
exportinfo_t **bckt;
! ASSERT(RW_WRITE_HELD(&ne->exported_lock));
!
! bckt = &ne->exptable[exptablehash(&exi->exi_fsid, &exi->exi_fid)];
exp_hash_link(exi, fid_hash, bckt);
! bckt = &ne->exptable_path_hash[pkp_tab_hash(exi->exi_export.ex_path,
strlen(exi->exi_export.ex_path))];
exp_hash_link(exi, path_hash, bckt);
+ exi->exi_ne = ne;
}
/*
! * Helper functions for exi_id handling
*/
+ static int
+ exi_id_compar(const void *v1, const void *v2)
+ {
+ const struct exportinfo *e1 = v1;
+ const struct exportinfo *e2 = v2;
+
+ if (e1->exi_id < e2->exi_id)
+ return (-1);
+ if (e1->exi_id > e2->exi_id)
+ return (1);
+
+ return (0);
+ }
+
int
! exi_id_get_next()
{
! struct exportinfo e;
! int ret = exi_id_next;
!
! ASSERT(MUTEX_HELD(&nfs_exi_id_lock));
!
! do {
! exi_id_next++;
! if (exi_id_next == 0)
! exi_id_overflow = TRUE;
!
! if (!exi_id_overflow)
! break;
!
! if (exi_id_next == ret)
! cmn_err(CE_PANIC, "exi_id exhausted");
!
! e.exi_id = exi_id_next;
! } while (avl_find(&exi_id_tree, &e, NULL) != NULL);
!
! return (ret);
! }
!
! /*
! * Get the root file handle for this zone.
! * Called when nfs_svc() starts
! */
! int
! nfs_export_get_rootfh(nfs_globals_t *g)
! {
! nfs_export_t *ne = g->nfs_export;
! int err;
!
! ne->exi_rootfid.fid_len = MAXFIDSZ;
! err = vop_fid_pseudo(ne->exi_root->exi_vp, &ne->exi_rootfid);
! if (err != 0) {
! ne->exi_rootfid.fid_len = 0;
! return (err);
! }
!
! /* Setup the fhandle template exi_fh */
! ne->exi_root->exi_fh.fh_fsid = rootdir->v_vfsp->vfs_fsid;
! ne->exi_root->exi_fh.fh_xlen = ne->exi_rootfid.fid_len;
! bcopy(ne->exi_rootfid.fid_data, ne->exi_root->exi_fh.fh_xdata,
! ne->exi_rootfid.fid_len);
! ne->exi_root->exi_fh.fh_len = sizeof (ne->exi_root->exi_fh.fh_data);
!
! return (0);
! }
!
! void
! nfs_export_zone_init(nfs_globals_t *ng)
! {
int i;
+ nfs_export_t *ne;
+ zone_t *zone;
! ne = kmem_zalloc(sizeof (*ne), KM_SLEEP);
+ rw_init(&ne->exported_lock, NULL, RW_DEFAULT, NULL);
+
+ ne->ne_globals = ng; /* "up" pointer */
+
/*
* Allocate the place holder for the public file handle, which
* is all zeroes. It is initially set to the root filesystem.
*/
! ne->exi_root = kmem_zalloc(sizeof (*ne->exi_root), KM_SLEEP);
! ne->exi_public = ne->exi_root;
! ne->exi_root->exi_export.ex_flags = EX_PUBLIC;
! ne->exi_root->exi_export.ex_pathlen = 1; /* length of "/" */
! ne->exi_root->exi_export.ex_path =
! kmem_alloc(ne->exi_root->exi_export.ex_pathlen + 1, KM_SLEEP);
! ne->exi_root->exi_export.ex_path[0] = '/';
! ne->exi_root->exi_export.ex_path[1] = '\0';
! ne->exi_root->exi_count = 1;
! mutex_init(&ne->exi_root->exi_lock, NULL, MUTEX_DEFAULT, NULL);
! /*
! * Because we cannot:
! * ASSERT(curzone->zone_id == ng->nfs_zoneid);
! * We grab the zone pointer explicitly (like netstacks do) and
! * set the rootvp here.
! *
! * Subsequent exportinfo_t's that get export_link()ed to "ne" also
! * will backpoint to "ne" such that exi->exi_ne->exi_root->exi_vp
! * will get the zone's rootvp for a given exportinfo_t.
! */
! zone = zone_find_by_id_nolock(ng->nfs_zoneid);
! ne->exi_root->exi_vp = zone->zone_rootvp;
! ne->exi_root->exi_zoneid = ng->nfs_zoneid;
/*
! * Fill in ne->exi_rootfid later, in nfs_export_get_rootfid
! * because we can't correctly return errors here.
*/
+
+ /* Initialize auth cache and auth cache lock */
for (i = 0; i < AUTH_TABLESIZE; i++) {
! ne->exi_root->exi_cache[i] = kmem_alloc(sizeof (avl_tree_t),
KM_SLEEP);
! avl_create(ne->exi_root->exi_cache[i],
! nfsauth_cache_clnt_compar, sizeof (struct auth_cache_clnt),
offsetof(struct auth_cache_clnt, authc_link));
}
! rw_init(&ne->exi_root->exi_cache_lock, NULL, RW_DEFAULT, NULL);
! /* setup exi_fh later, in nfs_export_get_rootfid */
! rw_enter(&ne->exported_lock, RW_WRITER);
! /* Publish the exportinfo in the hash table */
! export_link(ne, ne->exi_root);
! /* Initialize exi_id and exi_kstats */
! mutex_enter(&nfs_exi_id_lock);
! ne->exi_root->exi_id = exi_id_get_next();
! avl_add(&exi_id_tree, ne->exi_root);
! mutex_exit(&nfs_exi_id_lock);
!
! rw_exit(&ne->exported_lock);
! ne->ns_root = NULL;
!
! ng->nfs_export = ne;
}
/*
! * During zone shutdown, remove exports
*/
void
! nfs_export_zone_shutdown(nfs_globals_t *ng)
{
! nfs_export_t *ne = ng->nfs_export;
! struct exportinfo *exi, *nexi;
! int i, errors;
+ rw_enter(&ne->exported_lock, RW_READER);
+
+ errors = 0;
+ for (i = 0; i < EXPTABLESIZE; i++) {
+
+ exi = ne->exptable[i];
+ if (exi != NULL)
+ exi_hold(exi);
+
+ while (exi != NULL) {
+
/*
! * Get and hold next export before
! * dropping the rwlock and unexport
*/
! nexi = exi->fid_hash.next;
! if (nexi != NULL)
! exi_hold(nexi);
!
! rw_exit(&ne->exported_lock);
!
! /*
! * Skip ne->exi_root which gets special
! * create/destroy handling.
! */
! if (exi != ne->exi_root &&
! unexport(ne, exi) != 0)
! errors++;
! exi_rele(exi);
!
! rw_enter(&ne->exported_lock, RW_READER);
! exi = nexi;
! }
! }
! if (errors > 0) {
! cmn_err(CE_NOTE, "NFS: failed un-exports in zone %d",
! (int)ng->nfs_zoneid);
! }
!
! rw_exit(&ne->exported_lock);
! }
!
! void
! nfs_export_zone_fini(nfs_globals_t *ng)
! {
! int i;
! nfs_export_t *ne = ng->nfs_export;
! struct exportinfo *exi;
!
! ng->nfs_export = NULL;
!
! rw_enter(&ne->exported_lock, RW_WRITER);
!
! mutex_enter(&nfs_exi_id_lock);
! avl_remove(&exi_id_tree, ne->exi_root);
! mutex_exit(&nfs_exi_id_lock);
!
! export_unlink(ne, ne->exi_root);
!
! rw_exit(&ne->exported_lock);
!
! /* Deallocate the place holder for the public file handle */
! srv_secinfo_list_free(ne->exi_root->exi_export.ex_secinfo,
! ne->exi_root->exi_export.ex_seccnt);
! mutex_destroy(&ne->exi_root->exi_lock);
!
! rw_destroy(&ne->exi_root->exi_cache_lock);
for (i = 0; i < AUTH_TABLESIZE; i++) {
! avl_destroy(ne->exi_root->exi_cache[i]);
! kmem_free(ne->exi_root->exi_cache[i], sizeof (avl_tree_t));
}
! kmem_free(ne->exi_root->exi_export.ex_path,
! ne->exi_root->exi_export.ex_pathlen + 1);
! kmem_free(ne->exi_root, sizeof (*ne->exi_root));
!
! /*
! * The shutdown hook should have left the exi_id_tree
! * with nothing belonging to this zone.
! */
! mutex_enter(&nfs_exi_id_lock);
! i = 0;
! exi = avl_first(&exi_id_tree);
! while (exi != NULL) {
! if (exi->exi_zoneid == ng->nfs_zoneid)
! i++;
! exi = AVL_NEXT(&exi_id_tree, exi);
! }
! mutex_exit(&nfs_exi_id_lock);
! if (i > 0) {
! cmn_err(CE_NOTE,
! "NFS: zone %d has %d export IDs left after shutdown",
! (int)ng->nfs_zoneid, i);
! }
! rw_destroy(&ne->exported_lock);
! kmem_free(ne, sizeof (*ne));
}
/*
+ * Initialization routine for export routines.
+ * Should only be called once.
+ */
+ void
+ nfs_exportinit(void)
+ {
+ mutex_init(&nfs_exi_id_lock, NULL, MUTEX_DEFAULT, NULL);
+
+ /* exi_id handling initialization */
+ exi_id_next = 0;
+ exi_id_overflow = FALSE;
+ avl_create(&exi_id_tree, exi_id_compar, sizeof (struct exportinfo),
+ offsetof(struct exportinfo, exi_id_link));
+
+ nfslog_init();
+ }
+
+ /*
+ * Finalization routine for export routines.
+ */
+ void
+ nfs_exportfini(void)
+ {
+ avl_destroy(&exi_id_tree);
+ mutex_destroy(&nfs_exi_id_lock);
+ }
+
+ /*
* Check if 2 gss mechanism identifiers are the same.
*
* return FALSE if not the same.
* return TRUE if the same.
*/
*** 920,929 ****
--- 1136,1146 ----
rpc_gss_lock_t *lock, void **cookie)
{
int i, j;
rpc_gss_rawcred_t *raw_cred;
struct exportinfo *exi;
+ nfs_export_t *ne = nfs_get_export();
/*
* We don't deal with delegated credentials.
*/
if (deleg != GSS_C_NO_CREDENTIAL)
*** 930,942 ****
return (FALSE);
raw_cred = lock->raw_cred;
*cookie = NULL;
! rw_enter(&exported_lock, RW_READER);
for (i = 0; i < EXPTABLESIZE; i++) {
! exi = exptable[i];
while (exi) {
if (exi->exi_export.ex_seccnt > 0) {
struct secinfo *secp;
seconfig_t *se;
int seccnt;
--- 1147,1160 ----
return (FALSE);
raw_cred = lock->raw_cred;
*cookie = NULL;
! rw_enter(&ne->exported_lock, RW_READER);
!
for (i = 0; i < EXPTABLESIZE; i++) {
! exi = ne->exptable[i];
while (exi) {
if (exi->exi_export.ex_seccnt > 0) {
struct secinfo *secp;
seconfig_t *se;
int seccnt;
*** 972,982 ****
}
exi = exi->fid_hash.next;
}
}
done:
! rw_exit(&exported_lock);
/*
* If no nfs pseudo number mapping can be found in the export
* table, assign the nfsflavor to NFS_FLAVOR_NOMAP. In V4, we may
* recover the flavor mismatch from NFS layer (NFS4ERR_WRONGSEC).
--- 1190,1200 ----
}
exi = exi->fid_hash.next;
}
}
done:
! rw_exit(&ne->exported_lock);
/*
* If no nfs pseudo number mapping can be found in the export
* table, assign the nfsflavor to NFS_FLAVOR_NOMAP. In V4, we may
* recover the flavor mismatch from NFS layer (NFS4ERR_WRONGSEC).
*** 1039,1075 ****
int newcnt;
struct secinfo oldsec[MAX_FLAVORS];
int oldcnt;
int i;
struct pathname lookpn;
STRUCT_SET_HANDLE(uap, model, args);
/* Read in pathname from userspace */
if (error = pn_get(STRUCT_FGETP(uap, dname), UIO_USERSPACE, &lookpn))
return (error);
/* Walk the export list looking for that pathname */
! rw_enter(&exported_lock, RW_READER);
DTRACE_PROBE(nfss__i__exported_lock1_start);
! for (ex1 = exptable_path_hash[pkp_tab_hash(lookpn.pn_path,
strlen(lookpn.pn_path))]; ex1; ex1 = ex1->path_hash.next) {
! if (ex1 != exi_root && 0 ==
strcmp(ex1->exi_export.ex_path, lookpn.pn_path)) {
exi_hold(ex1);
break;
}
}
DTRACE_PROBE(nfss__i__exported_lock1_stop);
! rw_exit(&exported_lock);
/* Is this an unshare? */
if (STRUCT_FGETP(uap, uex) == NULL) {
pn_free(&lookpn);
if (ex1 == NULL)
return (EINVAL);
! error = unexport(ex1);
exi_rele(ex1);
return (error);
}
/* It is a share or a re-share */
--- 1257,1294 ----
int newcnt;
struct secinfo oldsec[MAX_FLAVORS];
int oldcnt;
int i;
struct pathname lookpn;
+ nfs_export_t *ne = nfs_get_export();
STRUCT_SET_HANDLE(uap, model, args);
/* Read in pathname from userspace */
if (error = pn_get(STRUCT_FGETP(uap, dname), UIO_USERSPACE, &lookpn))
return (error);
/* Walk the export list looking for that pathname */
! rw_enter(&ne->exported_lock, RW_READER);
DTRACE_PROBE(nfss__i__exported_lock1_start);
! for (ex1 = ne->exptable_path_hash[pkp_tab_hash(lookpn.pn_path,
strlen(lookpn.pn_path))]; ex1; ex1 = ex1->path_hash.next) {
! if (ex1 != ne->exi_root && 0 ==
strcmp(ex1->exi_export.ex_path, lookpn.pn_path)) {
exi_hold(ex1);
break;
}
}
DTRACE_PROBE(nfss__i__exported_lock1_stop);
! rw_exit(&ne->exported_lock);
/* Is this an unshare? */
if (STRUCT_FGETP(uap, uex) == NULL) {
pn_free(&lookpn);
if (ex1 == NULL)
return (EINVAL);
! error = unexport(ne, ex1);
exi_rele(ex1);
return (error);
}
/* It is a share or a re-share */
*** 1161,1195 ****
/*
* Do not allow re-sharing a shared vnode under a different path
* PSEUDO export has ex_path fabricated, e.g. "/tmp (pseudo)", skip it.
*/
! rw_enter(&exported_lock, RW_READER);
DTRACE_PROBE(nfss__i__exported_lock2_start);
! for (ex2 = exptable[exptablehash(&fsid, &fid)]; ex2;
ex2 = ex2->fid_hash.next) {
! if (ex2 != exi_root && !PSEUDO(ex2) &&
VN_CMP(ex2->exi_vp, vp) &&
strcmp(ex2->exi_export.ex_path, lookpn.pn_path) != 0) {
DTRACE_PROBE(nfss__i__exported_lock2_stop);
! rw_exit(&exported_lock);
VN_RELE(vp);
if (dvp != NULL)
VN_RELE(dvp);
pn_free(&lookpn);
return (EEXIST);
}
}
DTRACE_PROBE(nfss__i__exported_lock2_stop);
! rw_exit(&exported_lock);
pn_free(&lookpn);
exi = kmem_zalloc(sizeof (*exi), KM_SLEEP);
exi->exi_fsid = fsid;
exi->exi_fid = fid;
exi->exi_vp = vp;
exi->exi_count = 1;
exi->exi_volatile_dev = (vfssw[vp->v_vfsp->vfs_fstype].vsw_flag &
VSW_VOLATILEDEV) ? 1 : 0;
mutex_init(&exi->exi_lock, NULL, MUTEX_DEFAULT, NULL);
exi->exi_dvp = dvp;
--- 1380,1416 ----
/*
* Do not allow re-sharing a shared vnode under a different path
* PSEUDO export has ex_path fabricated, e.g. "/tmp (pseudo)", skip it.
*/
! rw_enter(&ne->exported_lock, RW_READER);
DTRACE_PROBE(nfss__i__exported_lock2_start);
! for (ex2 = ne->exptable[exptablehash(&fsid, &fid)]; ex2;
ex2 = ex2->fid_hash.next) {
! if (ex2 != ne->exi_root && !PSEUDO(ex2) &&
VN_CMP(ex2->exi_vp, vp) &&
strcmp(ex2->exi_export.ex_path, lookpn.pn_path) != 0) {
DTRACE_PROBE(nfss__i__exported_lock2_stop);
! rw_exit(&ne->exported_lock);
VN_RELE(vp);
if (dvp != NULL)
VN_RELE(dvp);
pn_free(&lookpn);
return (EEXIST);
}
}
DTRACE_PROBE(nfss__i__exported_lock2_stop);
! rw_exit(&ne->exported_lock);
pn_free(&lookpn);
exi = kmem_zalloc(sizeof (*exi), KM_SLEEP);
exi->exi_fsid = fsid;
exi->exi_fid = fid;
exi->exi_vp = vp;
exi->exi_count = 1;
+ exi->exi_zoneid = crgetzoneid(cr);
+ ASSERT3U(exi->exi_zoneid, ==, curzone->zone_id);
exi->exi_volatile_dev = (vfssw[vp->v_vfsp->vfs_fstype].vsw_flag &
VSW_VOLATILEDEV) ? 1 : 0;
mutex_init(&exi->exi_lock, NULL, MUTEX_DEFAULT, NULL);
exi->exi_dvp = dvp;
*** 1459,1499 ****
}
/*
* Insert the new entry at the front of the export list
*/
! rw_enter(&exported_lock, RW_WRITER);
DTRACE_PROBE(nfss__i__exported_lock3_start);
! export_link(exi);
/*
* Check the rest of the list for an old entry for the fs.
* If one is found then unlink it, wait until this is the
* only reference and then free it.
*/
for (ex = exi->fid_hash.next; ex != NULL; ex = ex->fid_hash.next) {
! if (ex != exi_root && VN_CMP(ex->exi_vp, vp)) {
! export_unlink(ex);
break;
}
}
/*
* If the public filehandle is pointing at the
* old entry, then point it back at the root.
*/
! if (ex != NULL && ex == exi_public)
! exi_public = exi_root;
/*
* If the public flag is on, make the global exi_public
* point to this entry and turn off the public bit so that
* we can distinguish it from the place holder export.
*/
if (kex->ex_flags & EX_PUBLIC) {
! exi_public = exi;
kex->ex_flags &= ~EX_PUBLIC;
}
#ifdef VOLATILE_FH_TEST
/*
--- 1680,1723 ----
}
/*
* Insert the new entry at the front of the export list
*/
! rw_enter(&ne->exported_lock, RW_WRITER);
DTRACE_PROBE(nfss__i__exported_lock3_start);
! export_link(ne, exi);
/*
* Check the rest of the list for an old entry for the fs.
* If one is found then unlink it, wait until this is the
* only reference and then free it.
*/
for (ex = exi->fid_hash.next; ex != NULL; ex = ex->fid_hash.next) {
! if (ex != ne->exi_root && VN_CMP(ex->exi_vp, vp)) {
! mutex_enter(&nfs_exi_id_lock);
! avl_remove(&exi_id_tree, ex);
! mutex_exit(&nfs_exi_id_lock);
! export_unlink(ne, ex);
break;
}
}
/*
* If the public filehandle is pointing at the
* old entry, then point it back at the root.
*/
! if (ex != NULL && ex == ne->exi_public)
! ne->exi_public = ne->exi_root;
/*
* If the public flag is on, make the global exi_public
* point to this entry and turn off the public bit so that
* we can distinguish it from the place holder export.
*/
if (kex->ex_flags & EX_PUBLIC) {
! ne->exi_public = exi;
kex->ex_flags &= ~EX_PUBLIC;
}
#ifdef VOLATILE_FH_TEST
/*
*** 1521,1541 ****
/* If it's a re-export update namespace tree */
exi->exi_tree = ex->exi_tree;
exi->exi_tree->tree_exi = exi;
/* Update the change timestamp */
! tree_update_change(exi->exi_tree, NULL);
}
/*
* build a unique flavor list from the flavors specified
* in the share cmd. unique means that each flavor only
* appears once in the secinfo list -- no duplicates allowed.
*/
newcnt = build_seclist_nodups(&exi->exi_export, newsec, FALSE);
! srv_secinfo_treeclimb(exi, newsec, newcnt, TRUE);
/*
* If re-sharing an old export entry, update the secinfo data
* depending on if the old entry is a pseudo node or not.
*/
--- 1745,1765 ----
/* If it's a re-export update namespace tree */
exi->exi_tree = ex->exi_tree;
exi->exi_tree->tree_exi = exi;
/* Update the change timestamp */
! tree_update_change(ne, exi->exi_tree, NULL);
}
/*
* build a unique flavor list from the flavors specified
* in the share cmd. unique means that each flavor only
* appears once in the secinfo list -- no duplicates allowed.
*/
newcnt = build_seclist_nodups(&exi->exi_export, newsec, FALSE);
! srv_secinfo_treeclimb(ne, exi, newsec, newcnt, TRUE);
/*
* If re-sharing an old export entry, update the secinfo data
* depending on if the old entry is a pseudo node or not.
*/
*** 1556,1566 ****
/*
* First transfer implicit flavor refs to new export.
* Remove old flavor refs last.
*/
srv_secinfo_exp2exp(&exi->exi_export, oldsec, oldcnt);
! srv_secinfo_treeclimb(ex, oldsec, oldcnt, FALSE);
}
}
/*
* If it's a re-export and the old entry has a pseudonode list,
--- 1780,1790 ----
/*
* First transfer implicit flavor refs to new export.
* Remove old flavor refs last.
*/
srv_secinfo_exp2exp(&exi->exi_export, oldsec, oldcnt);
! srv_secinfo_treeclimb(ne, ex, oldsec, oldcnt, FALSE);
}
}
/*
* If it's a re-export and the old entry has a pseudonode list,
*** 1569,1582 ****
if (ex != NULL && (ex->exi_visible != NULL)) {
exi->exi_visible = ex->exi_visible;
ex->exi_visible = NULL;
}
DTRACE_PROBE(nfss__i__exported_lock3_stop);
! rw_exit(&exported_lock);
! if (exi_public == exi || kex->ex_flags & EX_LOG) {
/*
* Log share operation to this buffer only.
*/
nfslog_share_record(exi, cr);
}
--- 1793,1820 ----
if (ex != NULL && (ex->exi_visible != NULL)) {
exi->exi_visible = ex->exi_visible;
ex->exi_visible = NULL;
}
+ /*
+ * Initialize exi_id and exi_kstats
+ */
+ if (ex != NULL) {
+ exi->exi_id = ex->exi_id;
+ } else {
+ mutex_enter(&nfs_exi_id_lock);
+ exi->exi_id = exi_id_get_next();
+ mutex_exit(&nfs_exi_id_lock);
+ }
+ mutex_enter(&nfs_exi_id_lock);
+ avl_add(&exi_id_tree, exi);
+ mutex_exit(&nfs_exi_id_lock);
+
DTRACE_PROBE(nfss__i__exported_lock3_stop);
! rw_exit(&ne->exported_lock);
! if (ne->exi_public == exi || kex->ex_flags & EX_LOG) {
/*
* Log share operation to this buffer only.
*/
nfslog_share_record(exi, cr);
}
*** 1586,1598 ****
return (0);
out7:
/* Unlink the new export in exptable. */
! export_unlink(exi);
DTRACE_PROBE(nfss__i__exported_lock3_stop);
! rw_exit(&exported_lock);
out6:
if (kex->ex_flags & EX_INDEX)
kmem_free(kex->ex_index, strlen(kex->ex_index) + 1);
out5:
/* free partially completed allocation */
--- 1824,1836 ----
return (0);
out7:
/* Unlink the new export in exptable. */
! export_unlink(ne, exi);
DTRACE_PROBE(nfss__i__exported_lock3_stop);
! rw_exit(&ne->exported_lock);
out6:
if (kex->ex_flags & EX_INDEX)
kmem_free(kex->ex_index, strlen(kex->ex_index) + 1);
out5:
/* free partially completed allocation */
*** 1632,1707 ****
/*
* Remove the exportinfo from the export list
*/
void
! export_unlink(struct exportinfo *exi)
{
! ASSERT(RW_WRITE_HELD(&exported_lock));
exp_hash_unlink(exi, fid_hash);
exp_hash_unlink(exi, path_hash);
}
/*
* Unexport an exported filesystem
*/
static int
! unexport(struct exportinfo *exi)
{
struct secinfo cursec[MAX_FLAVORS];
int curcnt;
! rw_enter(&exported_lock, RW_WRITER);
/* Check if exi is still linked in the export table */
if (!EXP_LINKED(exi) || PSEUDO(exi)) {
! rw_exit(&exported_lock);
return (EINVAL);
}
! export_unlink(exi);
/*
* Remove security flavors before treeclimb_unexport() is called
* because srv_secinfo_treeclimb needs the namespace tree
*/
curcnt = build_seclist_nodups(&exi->exi_export, cursec, TRUE);
- srv_secinfo_treeclimb(exi, cursec, curcnt, FALSE);
-
/*
* If there's a visible list, then need to leave
* a pseudo export here to retain the visible list
* for paths to exports below.
*/
if (exi->exi_visible != NULL) {
struct exportinfo *newexi;
! newexi = pseudo_exportfs(exi->exi_vp, &exi->exi_fid,
exi->exi_visible, &exi->exi_export);
exi->exi_visible = NULL;
/* interconnect the existing treenode with the new exportinfo */
newexi->exi_tree = exi->exi_tree;
newexi->exi_tree->tree_exi = newexi;
/* Update the change timestamp */
! tree_update_change(exi->exi_tree, NULL);
} else {
! treeclimb_unexport(exi);
}
! rw_exit(&exported_lock);
/*
* Need to call into the NFSv4 server and release all data
* held on this particular export. This is important since
* the v4 server may be holding file locks or vnodes under
* this export.
*/
! rfs4_clean_state_exi(exi);
/*
* Notify the lock manager that the filesystem is being
* unexported.
*/
--- 1870,1949 ----
/*
* Remove the exportinfo from the export list
*/
void
! export_unlink(nfs_export_t *ne, struct exportinfo *exi)
{
! ASSERT(RW_WRITE_HELD(&ne->exported_lock));
exp_hash_unlink(exi, fid_hash);
exp_hash_unlink(exi, path_hash);
+ ASSERT3P(exi->exi_ne, ==, ne);
+ exi->exi_ne = NULL;
}
/*
* Unexport an exported filesystem
*/
static int
! unexport(nfs_export_t *ne, struct exportinfo *exi)
{
struct secinfo cursec[MAX_FLAVORS];
int curcnt;
! rw_enter(&ne->exported_lock, RW_WRITER);
/* Check if exi is still linked in the export table */
if (!EXP_LINKED(exi) || PSEUDO(exi)) {
! rw_exit(&ne->exported_lock);
return (EINVAL);
}
! mutex_enter(&nfs_exi_id_lock);
! avl_remove(&exi_id_tree, exi);
! mutex_exit(&nfs_exi_id_lock);
! export_unlink(ne, exi);
/*
* Remove security flavors before treeclimb_unexport() is called
* because srv_secinfo_treeclimb needs the namespace tree
*/
curcnt = build_seclist_nodups(&exi->exi_export, cursec, TRUE);
+ srv_secinfo_treeclimb(ne, exi, cursec, curcnt, FALSE);
/*
* If there's a visible list, then need to leave
* a pseudo export here to retain the visible list
* for paths to exports below.
*/
if (exi->exi_visible != NULL) {
struct exportinfo *newexi;
! newexi = pseudo_exportfs(ne, exi->exi_vp, &exi->exi_fid,
exi->exi_visible, &exi->exi_export);
exi->exi_visible = NULL;
/* interconnect the existing treenode with the new exportinfo */
newexi->exi_tree = exi->exi_tree;
newexi->exi_tree->tree_exi = newexi;
/* Update the change timestamp */
! tree_update_change(ne, exi->exi_tree, NULL);
} else {
! treeclimb_unexport(ne, exi);
}
! rw_exit(&ne->exported_lock);
/*
* Need to call into the NFSv4 server and release all data
* held on this particular export. This is important since
* the v4 server may be holding file locks or vnodes under
* this export.
*/
! rfs4_clean_state_exi(ne, exi);
/*
* Notify the lock manager that the filesystem is being
* unexported.
*/
*** 1709,1727 ****
/*
* If this was a public export, restore
* the public filehandle to the root.
*/
- if (exi == exi_public) {
- exi_public = exi_root;
! nfslog_share_record(exi_public, CRED());
}
! if (exi->exi_export.ex_flags & EX_LOG) {
nfslog_unshare_record(exi, CRED());
- }
exi_rele(exi);
return (0);
}
--- 1951,1973 ----
/*
* If this was a public export, restore
* the public filehandle to the root.
*/
! /*
! * XXX KEBE ASKS --> Should CRED() instead be
! * exi->exi_zone->zone_kcred?
! */
! if (exi == ne->exi_public) {
! ne->exi_public = ne->exi_root;
!
! nfslog_share_record(ne->exi_public, CRED());
}
! if (exi->exi_export.ex_flags & EX_LOG)
nfslog_unshare_record(exi, CRED());
exi_rele(exi);
return (0);
}
*** 1944,1954 ****
/*
* We have just failed finding a matching export.
* If we're at the root of this filesystem, then
* it's time to stop (with failure).
*/
! if (vp->v_flag & VROOT) {
error = EINVAL;
break;
}
if (walk != NULL)
--- 2190,2201 ----
/*
* We have just failed finding a matching export.
* If we're at the root of this filesystem, then
* it's time to stop (with failure).
*/
! ASSERT3P(vp->v_vfsp->vfs_zone, ==, curzone);
! if ((vp->v_flag & VROOT) || VN_IS_CURZONEROOT(vp)) {
error = EINVAL;
break;
}
if (walk != NULL)
*** 2444,2456 ****
*/
struct exportinfo *
checkexport(fsid_t *fsid, fid_t *fid)
{
struct exportinfo *exi;
! rw_enter(&exported_lock, RW_READER);
! for (exi = exptable[exptablehash(fsid, fid)];
exi != NULL;
exi = exi->fid_hash.next) {
if (exportmatch(exi, fsid, fid)) {
/*
* If this is the place holder for the
--- 2691,2704 ----
*/
struct exportinfo *
checkexport(fsid_t *fsid, fid_t *fid)
{
struct exportinfo *exi;
+ nfs_export_t *ne = nfs_get_export();
! rw_enter(&ne->exported_lock, RW_READER);
! for (exi = ne->exptable[exptablehash(fsid, fid)];
exi != NULL;
exi = exi->fid_hash.next) {
if (exportmatch(exi, fsid, fid)) {
/*
* If this is the place holder for the
*** 2457,2475 ****
* public file handle, then return the
* real export entry for the public file
* handle.
*/
if (exi->exi_export.ex_flags & EX_PUBLIC) {
! exi = exi_public;
}
exi_hold(exi);
! rw_exit(&exported_lock);
return (exi);
}
}
! rw_exit(&exported_lock);
return (NULL);
}
/*
--- 2705,2723 ----
* public file handle, then return the
* real export entry for the public file
* handle.
*/
if (exi->exi_export.ex_flags & EX_PUBLIC) {
! exi = ne->exi_public;
}
exi_hold(exi);
! rw_exit(&ne->exported_lock);
return (exi);
}
}
! rw_exit(&ne->exported_lock);
return (NULL);
}
/*
*** 2481,2494 ****
*/
struct exportinfo *
checkexport4(fsid_t *fsid, fid_t *fid, vnode_t *vp)
{
struct exportinfo *exi;
! ASSERT(RW_LOCK_HELD(&exported_lock));
! for (exi = exptable[exptablehash(fsid, fid)];
exi != NULL;
exi = exi->fid_hash.next) {
if (exportmatch(exi, fsid, fid)) {
/*
* If this is the place holder for the
--- 2729,2743 ----
*/
struct exportinfo *
checkexport4(fsid_t *fsid, fid_t *fid, vnode_t *vp)
{
struct exportinfo *exi;
+ nfs_export_t *ne = nfs_get_export();
! ASSERT(RW_LOCK_HELD(&ne->exported_lock));
! for (exi = ne->exptable[exptablehash(fsid, fid)];
exi != NULL;
exi = exi->fid_hash.next) {
if (exportmatch(exi, fsid, fid)) {
/*
* If this is the place holder for the
*** 2495,2505 ****
* public file handle, then return the
* real export entry for the public file
* handle.
*/
if (exi->exi_export.ex_flags & EX_PUBLIC) {
! exi = exi_public;
}
/*
* If vp is given, check if vp is the
* same vnode as the exported node.
--- 2744,2754 ----
* public file handle, then return the
* real export entry for the public file
* handle.
*/
if (exi->exi_export.ex_flags & EX_PUBLIC) {
! exi = ne->exi_public;
}
/*
* If vp is given, check if vp is the
* same vnode as the exported node.