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
*** 21,30 ****
--- 21,35 ----
/*
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
*/
+ /*
+ * Copyright 2018 Nexenta Systems, Inc.
+ * Copyright 2020 Joyent, Inc.
+ */
+
#include <sys/types.h>
#include <sys/types32.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <rpc/types.h>
*** 51,142 ****
bufsz + 1, /* Add one for extra NUL */ \
&len)) { \
error = EFAULT; \
goto cleanup; \
} \
! /* \
! * Need to remove 1 because copyinstr() counts the NUL. \
! */ \
len--; \
sh->sh_##field = kmem_alloc(len + 1, KM_SLEEP); \
bcopy(buf, sh->sh_##field, len); \
sh->sh_##field[len] = '\0'; \
shl.shl_##field = (int)len; \
sh->sh_size += shl.shl_##field; /* Debug counting */
#define SHARETAB_DELETE_FIELD(field) \
! if (sh->sh_##field) { \
kmem_free(sh->sh_##field, \
shl ? shl->shl_##field + 1 : \
strlen(sh->sh_##field) + 1); \
}
! sharetab_t *sharefs_sharetab = NULL; /* The incore sharetab. */
! size_t sharetab_size;
! uint_t sharetab_count;
- krwlock_t sharetab_lock; /* lock to protect the cached sharetab */
-
- krwlock_t sharefs_lock; /* lock to protect the vnode ops */
-
- timestruc_t sharetab_mtime;
- timestruc_t sharetab_snap_time;
-
- uint_t sharetab_generation; /* Only increments and wraps! */
-
/*
* Take care of cleaning up a share.
* If passed in a length array, use it to determine how much
* space to clean up. Else, figure that out.
*/
static void
sharefree(share_t *sh, sharefs_lens_t *shl)
{
! if (!sh)
return;
SHARETAB_DELETE_FIELD(path);
SHARETAB_DELETE_FIELD(res);
SHARETAB_DELETE_FIELD(fstype);
SHARETAB_DELETE_FIELD(opts);
SHARETAB_DELETE_FIELD(descr);
! kmem_free(sh, sizeof (share_t));
}
/*
* If there is no error, then this function is responsible for
* cleaning up the memory associated with the share argument.
*/
static int
! sharefs_remove(share_t *sh, sharefs_lens_t *shl)
{
int iHash;
sharetab_t *sht;
share_t *s, *p;
int iPath;
if (!sh)
return (ENOENT);
! rw_enter(&sharetab_lock, RW_WRITER);
! for (sht = sharefs_sharetab; sht != NULL; sht = sht->s_next) {
! if (strcmp(sh->sh_fstype, sht->s_fstype) == 0) {
break;
}
- }
/*
* There does not exist a fstype in memory which
* matches the share passed in.
*/
! if (!sht) {
! rw_exit(&sharetab_lock);
return (ENOENT);
}
! iPath = shl ? shl->shl_path : strlen(sh->sh_path);
iHash = pkp_tab_hash(sh->sh_path, strlen(sh->sh_path));
/*
* Now walk down the hash table and find the entry to free!
*/
--- 56,133 ----
bufsz + 1, /* Add one for extra NUL */ \
&len)) { \
error = EFAULT; \
goto cleanup; \
} \
! /* Need to remove 1 because copyinstr() counts the NUL */ \
len--; \
sh->sh_##field = kmem_alloc(len + 1, KM_SLEEP); \
bcopy(buf, sh->sh_##field, len); \
sh->sh_##field[len] = '\0'; \
shl.shl_##field = (int)len; \
sh->sh_size += shl.shl_##field; /* Debug counting */
#define SHARETAB_DELETE_FIELD(field) \
! if (sh->sh_##field != NULL) { \
kmem_free(sh->sh_##field, \
shl ? shl->shl_##field + 1 : \
strlen(sh->sh_##field) + 1); \
}
! static zone_key_t sharetab_zone_key;
/*
* Take care of cleaning up a share.
* If passed in a length array, use it to determine how much
* space to clean up. Else, figure that out.
*/
static void
sharefree(share_t *sh, sharefs_lens_t *shl)
{
! if (sh == NULL)
return;
SHARETAB_DELETE_FIELD(path);
SHARETAB_DELETE_FIELD(res);
SHARETAB_DELETE_FIELD(fstype);
SHARETAB_DELETE_FIELD(opts);
SHARETAB_DELETE_FIELD(descr);
! kmem_free(sh, sizeof (*sh));
}
/*
* If there is no error, then this function is responsible for
* cleaning up the memory associated with the share argument.
*/
static int
! sharefs_remove(sharetab_globals_t *sg, share_t *sh, sharefs_lens_t *shl)
{
int iHash;
sharetab_t *sht;
share_t *s, *p;
int iPath;
if (!sh)
return (ENOENT);
! rw_enter(&sg->sharetab_lock, RW_WRITER);
! for (sht = sg->sharefs_sharetab; sht != NULL; sht = sht->s_next) {
! if (strcmp(sh->sh_fstype, sht->s_fstype) == 0)
break;
}
/*
* There does not exist a fstype in memory which
* matches the share passed in.
*/
! if (sht == NULL) {
! rw_exit(&sg->sharetab_lock);
return (ENOENT);
}
! iPath = shl != NULL ? shl->shl_path : strlen(sh->sh_path);
iHash = pkp_tab_hash(sh->sh_path, strlen(sh->sh_path));
/*
* Now walk down the hash table and find the entry to free!
*/
*** 145,259 ****
/*
* We need exact matches.
*/
if (strcmp(sh->sh_path, s->sh_path) == 0 &&
strlen(s->sh_path) == iPath) {
! if (p) {
p->sh_next = s->sh_next;
! } else {
sht->s_buckets[iHash].ssh_sh = s->sh_next;
- }
ASSERT(sht->s_buckets[iHash].ssh_count != 0);
atomic_dec_32(&sht->s_buckets[iHash].ssh_count);
atomic_dec_32(&sht->s_count);
! atomic_dec_32(&sharetab_count);
! ASSERT(sharetab_size >= s->sh_size);
! sharetab_size -= s->sh_size;
! gethrestime(&sharetab_mtime);
! atomic_inc_32(&sharetab_generation);
break;
}
p = s;
}
! rw_exit(&sharetab_lock);
! if (!s) {
return (ENOENT);
- }
s->sh_next = NULL;
sharefree(s, NULL);
! /*
! * We need to free the share for the caller.
! */
sharefree(sh, shl);
return (0);
}
/*
* The caller must have allocated memory for us to use.
*/
static int
! sharefs_add(share_t *sh, sharefs_lens_t *shl)
{
int iHash;
sharetab_t *sht;
share_t *s, *p;
int iPath;
int n;
! if (!sh) {
return (ENOENT);
- }
! /*
! * We need to find the hash buckets for the fstype.
! */
! rw_enter(&sharetab_lock, RW_WRITER);
! for (sht = sharefs_sharetab; sht != NULL; sht = sht->s_next) {
! if (strcmp(sh->sh_fstype, sht->s_fstype) == 0) {
break;
}
- }
! /*
! * Did not exist, so allocate one and add it to the
! * sharetab.
! */
! if (!sht) {
sht = kmem_zalloc(sizeof (*sht), KM_SLEEP);
n = strlen(sh->sh_fstype);
sht->s_fstype = kmem_zalloc(n + 1, KM_SLEEP);
(void) strncpy(sht->s_fstype, sh->sh_fstype, n);
! sht->s_next = sharefs_sharetab;
! sharefs_sharetab = sht;
}
! /*
! * Now we need to find where we have to add the entry.
! */
iHash = pkp_tab_hash(sh->sh_path, strlen(sh->sh_path));
- iPath = shl ? shl->shl_path : strlen(sh->sh_path);
-
if (shl) {
sh->sh_size = shl->shl_path + shl->shl_res +
shl->shl_fstype + shl->shl_opts + shl->shl_descr;
} else {
sh->sh_size = strlen(sh->sh_path) +
strlen(sh->sh_res) + strlen(sh->sh_fstype) +
strlen(sh->sh_opts) + strlen(sh->sh_descr);
}
! /*
! * We need to account for field seperators and
! * the EOL.
! */
sh->sh_size += 5;
! /*
! * Now walk down the hash table and add the new entry!
! */
for (p = NULL, s = sht->s_buckets[iHash].ssh_sh;
s != NULL; s = s->sh_next) {
/*
* We need exact matches.
*
--- 136,231 ----
/*
* We need exact matches.
*/
if (strcmp(sh->sh_path, s->sh_path) == 0 &&
strlen(s->sh_path) == iPath) {
! if (p != NULL)
p->sh_next = s->sh_next;
! else
sht->s_buckets[iHash].ssh_sh = s->sh_next;
ASSERT(sht->s_buckets[iHash].ssh_count != 0);
atomic_dec_32(&sht->s_buckets[iHash].ssh_count);
atomic_dec_32(&sht->s_count);
! atomic_dec_32(&sg->sharetab_count);
! ASSERT(sg->sharetab_size >= s->sh_size);
! sg->sharetab_size -= s->sh_size;
! gethrestime(&sg->sharetab_mtime);
! atomic_inc_32(&sg->sharetab_generation);
break;
}
p = s;
}
! rw_exit(&sg->sharetab_lock);
! if (s == NULL)
return (ENOENT);
s->sh_next = NULL;
sharefree(s, NULL);
! /* We need to free the share for the caller */
sharefree(sh, shl);
return (0);
}
/*
* The caller must have allocated memory for us to use.
*/
static int
! sharefs_add(sharetab_globals_t *sg, share_t *sh, sharefs_lens_t *shl)
{
int iHash;
sharetab_t *sht;
share_t *s, *p;
int iPath;
int n;
! if (sh == NULL)
return (ENOENT);
! /* We need to find the hash buckets for the fstype */
! rw_enter(&sg->sharetab_lock, RW_WRITER);
! for (sht = sg->sharefs_sharetab; sht != NULL; sht = sht->s_next) {
! if (strcmp(sh->sh_fstype, sht->s_fstype) == 0)
break;
}
! /* Did not exist, so allocate one and add it to the sharetab */
! if (sht == NULL) {
sht = kmem_zalloc(sizeof (*sht), KM_SLEEP);
n = strlen(sh->sh_fstype);
sht->s_fstype = kmem_zalloc(n + 1, KM_SLEEP);
(void) strncpy(sht->s_fstype, sh->sh_fstype, n);
! sht->s_next = sg->sharefs_sharetab;
! sg->sharefs_sharetab = sht;
}
! /* Now we need to find where we have to add the entry */
! iPath = shl != NULL ? shl->shl_path : strlen(sh->sh_path);
iHash = pkp_tab_hash(sh->sh_path, strlen(sh->sh_path));
if (shl) {
sh->sh_size = shl->shl_path + shl->shl_res +
shl->shl_fstype + shl->shl_opts + shl->shl_descr;
} else {
sh->sh_size = strlen(sh->sh_path) +
strlen(sh->sh_res) + strlen(sh->sh_fstype) +
strlen(sh->sh_opts) + strlen(sh->sh_descr);
}
! /* We need to account for field separators and the EOL */
sh->sh_size += 5;
! /* Now walk down the hash table and add the new entry */
for (p = NULL, s = sht->s_buckets[iHash].ssh_sh;
s != NULL; s = s->sh_next) {
/*
* We need exact matches.
*
*** 261,292 ****
* duplicate path in a share command or we are
* being asked to replace an existing entry.
*/
if (strcmp(sh->sh_path, s->sh_path) == 0 &&
strlen(s->sh_path) == iPath) {
! if (p) {
p->sh_next = sh;
! } else {
sht->s_buckets[iHash].ssh_sh = sh;
- }
sh->sh_next = s->sh_next;
! ASSERT(sharetab_size >= s->sh_size);
! sharetab_size -= s->sh_size;
! sharetab_size += sh->sh_size;
! /*
! * Get rid of the old node.
! */
sharefree(s, NULL);
! gethrestime(&sharetab_mtime);
! atomic_inc_32(&sharetab_generation);
ASSERT(sht->s_buckets[iHash].ssh_count != 0);
! rw_exit(&sharetab_lock);
return (0);
}
p = s;
--- 233,261 ----
* duplicate path in a share command or we are
* being asked to replace an existing entry.
*/
if (strcmp(sh->sh_path, s->sh_path) == 0 &&
strlen(s->sh_path) == iPath) {
! if (p != NULL)
p->sh_next = sh;
! else
sht->s_buckets[iHash].ssh_sh = sh;
sh->sh_next = s->sh_next;
! ASSERT(sg->sharetab_size >= s->sh_size);
! sg->sharetab_size -= s->sh_size;
! sg->sharetab_size += sh->sh_size;
! /* Get rid of the old node */
sharefree(s, NULL);
! gethrestime(&sg->sharetab_mtime);
! atomic_inc_32(&sg->sharetab_generation);
ASSERT(sht->s_buckets[iHash].ssh_count != 0);
! rw_exit(&sg->sharetab_lock);
return (0);
}
p = s;
*** 298,439 ****
*/
sh->sh_next = sht->s_buckets[iHash].ssh_sh;
sht->s_buckets[iHash].ssh_sh = sh;
atomic_inc_32(&sht->s_buckets[iHash].ssh_count);
atomic_inc_32(&sht->s_count);
! atomic_inc_32(&sharetab_count);
! sharetab_size += sh->sh_size;
! gethrestime(&sharetab_mtime);
! atomic_inc_32(&sharetab_generation);
! rw_exit(&sharetab_lock);
return (0);
}
void
sharefs_sharetab_init(void)
{
! rw_init(&sharetab_lock, NULL, RW_DEFAULT, NULL);
! rw_init(&sharefs_lock, NULL, RW_DEFAULT, NULL);
! sharetab_size = 0;
! sharetab_count = 0;
! sharetab_generation = 1;
!
! gethrestime(&sharetab_mtime);
! gethrestime(&sharetab_snap_time);
}
int
sharefs_impl(enum sharefs_sys_op opcode, share_t *sh_in, uint32_t iMaxLen)
{
int error = 0;
size_t len;
size_t bufsz;
share_t *sh;
-
sharefs_lens_t shl;
-
model_t model;
-
char *buf = NULL;
STRUCT_DECL(share, u_sh);
bufsz = iMaxLen;
/*
* Before we do anything, lets make sure we have
* a sharetab in memory if we need one.
*/
! rw_enter(&sharetab_lock, RW_READER);
switch (opcode) {
! case (SHAREFS_REMOVE) :
! case (SHAREFS_REPLACE) :
! if (!sharefs_sharetab) {
! rw_exit(&sharetab_lock);
return (set_errno(ENOENT));
}
break;
! case (SHAREFS_ADD) :
! default :
break;
}
! rw_exit(&sharetab_lock);
model = get_udatamodel();
/*
* Initialize the data pointers.
*/
STRUCT_INIT(u_sh, model);
! if (copyin(sh_in, STRUCT_BUF(u_sh), STRUCT_SIZE(u_sh))) {
return (set_errno(EFAULT));
- }
! /*
! * Get the share.
! */
sh = kmem_zalloc(sizeof (share_t), KM_SLEEP);
! /*
! * Get some storage for copying in the strings.
! */
buf = kmem_zalloc(bufsz + 1, KM_SLEEP);
bzero(&shl, sizeof (sharefs_lens_t));
! /*
! * Only grab these two until we know what we want.
! */
SHARETAB_COPYIN(path);
SHARETAB_COPYIN(fstype);
switch (opcode) {
! case (SHAREFS_ADD) :
! case (SHAREFS_REPLACE) :
SHARETAB_COPYIN(res);
SHARETAB_COPYIN(opts);
SHARETAB_COPYIN(descr);
!
! error = sharefs_add(sh, &shl);
break;
!
! case (SHAREFS_REMOVE) :
!
! error = sharefs_remove(sh, &shl);
break;
-
default:
error = EINVAL;
break;
}
cleanup:
-
/*
* If there is no error, then we have stashed the structure
* away in the sharetab hash table or have deleted it.
*
* Either way, the only reason to blow away the data is if
* there was an error.
*/
! if (error != 0) {
sharefree(sh, &shl);
- }
! if (buf) {
kmem_free(buf, bufsz + 1);
- }
! return ((error != 0) ? set_errno(error) : 0);
}
int
sharefs(enum sharefs_sys_op opcode, share_t *sh_in, uint32_t iMaxLen)
{
if (secpolicy_sys_config(CRED(), B_FALSE) != 0)
return (set_errno(EPERM));
return (sharefs_impl(opcode, sh_in, iMaxLen));
}
--- 267,463 ----
*/
sh->sh_next = sht->s_buckets[iHash].ssh_sh;
sht->s_buckets[iHash].ssh_sh = sh;
atomic_inc_32(&sht->s_buckets[iHash].ssh_count);
atomic_inc_32(&sht->s_count);
! atomic_inc_32(&sg->sharetab_count);
! sg->sharetab_size += sh->sh_size;
! gethrestime(&sg->sharetab_mtime);
! atomic_inc_32(&sg->sharetab_generation);
! rw_exit(&sg->sharetab_lock);
return (0);
}
+ /* ARGSUSED */
+ static void *
+ sharetab_zone_init(zoneid_t zoneid)
+ {
+ sharetab_globals_t *sg;
+
+ sg = kmem_zalloc(sizeof (*sg), KM_SLEEP);
+
+ rw_init(&sg->sharetab_lock, NULL, RW_DEFAULT, NULL);
+ rw_init(&sg->sharefs_lock, NULL, RW_DEFAULT, NULL);
+
+ sg->sharetab_size = 0;
+ sg->sharetab_count = 0;
+ sg->sharetab_generation = 1;
+
+ gethrestime(&sg->sharetab_mtime);
+ gethrestime(&sg->sharetab_snap_time);
+
+ return (sg);
+ }
+
+ /* ARGSUSED */
+ static void
+ sharetab_zone_fini(zoneid_t zoneid, void *data)
+ {
+ sharetab_globals_t *sg = data;
+
+ rw_destroy(&sg->sharefs_lock);
+ rw_destroy(&sg->sharetab_lock);
+
+ /* ALL of the allocated things must be cleaned before we free sg. */
+ while (sg->sharefs_sharetab != NULL) {
+ int i;
+ sharetab_t *freeing = sg->sharefs_sharetab;
+
+ sg->sharefs_sharetab = freeing->s_next;
+ kmem_free(freeing->s_fstype, strlen(freeing->s_fstype) + 1);
+ for (i = 0; i < PKP_HASH_SIZE; i++) {
+ sharefs_hash_head_t *bucket;
+
+ bucket = &(freeing->s_buckets[i]);
+ while (bucket->ssh_sh != NULL) {
+ share_t *share = bucket->ssh_sh;
+
+ bucket->ssh_sh = share->sh_next;
+ sharefree(share, NULL);
+ }
+ }
+ kmem_free(freeing, sizeof (*freeing));
+ }
+
+ kmem_free(sg, sizeof (*sg));
+ }
+
void
sharefs_sharetab_init(void)
{
! zone_key_create(&sharetab_zone_key, sharetab_zone_init,
! NULL, sharetab_zone_fini);
! }
! sharetab_globals_t *
! sharetab_get_globals(zone_t *zone)
! {
! return (zone_getspecific(sharetab_zone_key, zone));
}
int
sharefs_impl(enum sharefs_sys_op opcode, share_t *sh_in, uint32_t iMaxLen)
{
int error = 0;
size_t len;
size_t bufsz;
share_t *sh;
sharefs_lens_t shl;
model_t model;
char *buf = NULL;
+ sharetab_globals_t *sg = sharetab_get_globals(curzone);
STRUCT_DECL(share, u_sh);
bufsz = iMaxLen;
/*
* Before we do anything, lets make sure we have
* a sharetab in memory if we need one.
*/
! rw_enter(&sg->sharetab_lock, RW_READER);
switch (opcode) {
! case SHAREFS_REMOVE:
! case SHAREFS_REPLACE:
! if (!sg->sharefs_sharetab) {
! rw_exit(&sg->sharetab_lock);
return (set_errno(ENOENT));
}
break;
! case SHAREFS_ADD:
! default:
break;
}
! rw_exit(&sg->sharetab_lock);
model = get_udatamodel();
/*
* Initialize the data pointers.
*/
STRUCT_INIT(u_sh, model);
! if (copyin(sh_in, STRUCT_BUF(u_sh), STRUCT_SIZE(u_sh)))
return (set_errno(EFAULT));
! /* Get the share */
sh = kmem_zalloc(sizeof (share_t), KM_SLEEP);
! /* Get some storage for copying in the strings */
buf = kmem_zalloc(bufsz + 1, KM_SLEEP);
bzero(&shl, sizeof (sharefs_lens_t));
! /* Only grab these two until we know what we want */
SHARETAB_COPYIN(path);
SHARETAB_COPYIN(fstype);
switch (opcode) {
! case SHAREFS_ADD:
! case SHAREFS_REPLACE:
SHARETAB_COPYIN(res);
SHARETAB_COPYIN(opts);
SHARETAB_COPYIN(descr);
! error = sharefs_add(sg, sh, &shl);
break;
! case SHAREFS_REMOVE:
! error = sharefs_remove(sg, sh, &shl);
break;
default:
error = EINVAL;
break;
}
cleanup:
/*
* If there is no error, then we have stashed the structure
* away in the sharetab hash table or have deleted it.
*
* Either way, the only reason to blow away the data is if
* there was an error.
*/
! if (error != 0)
sharefree(sh, &shl);
! if (buf != NULL)
kmem_free(buf, bufsz + 1);
! return (error != 0 ? set_errno(error) : 0);
}
int
sharefs(enum sharefs_sys_op opcode, share_t *sh_in, uint32_t iMaxLen)
{
+ /*
+ * If we're in the global zone PRIV_SYS_CONFIG gives us the
+ * privileges needed to act on sharetab. However if we're in
+ * a non-global zone PRIV_SYS_CONFIG is not allowed. To work
+ * around this issue PRIV_SYS_NFS is used in this case.
+ *
+ * TODO: This basically overloads the definition/use of
+ * PRIV_SYS_NFS to work around the limitation of PRIV_SYS_CONFIG
+ * in a zone. Solaris 11 solved this by implementing a PRIV_SYS_SHARE
+ * we should do the same and replace the use of PRIV_SYS_NFS here and
+ * in zfs_secpolicy_share.
+ */
+ if (INGLOBALZONE(curproc)) {
if (secpolicy_sys_config(CRED(), B_FALSE) != 0)
return (set_errno(EPERM));
+ } else {
+ /* behave like zfs_secpolicy_share() */
+ if (secpolicy_nfs(CRED()) != 0)
+ return (set_errno(EPERM));
+ }
return (sharefs_impl(opcode, sh_in, iMaxLen));
}