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.