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));
  }