Print this page
NEX-15279 support NFS server in zone
NEX-15520 online NFS shares cause zoneadm halt to hang in nfs_export_zone_fini
Portions contributed by: Dan Kruchinin dan.kruchinin@nexenta.com
Portions contributed by: Stepan Zastupov stepan.zastupov@gmail.com
Reviewed by: Joyce McIntosh <joyce.mcintosh@nexenta.com>
Reviewed by: Rob Gittins <rob.gittins@nexenta.com>
Reviewed by: Gordon Ross <gordon.ross@nexenta.com>
OS-20 share_nfs(1m) charset handling is unreliable
OS-22 Page fault at nfscmd_dropped_entrysize+0x1e()
OS-23 NFSv2/3/4: READDIR responses are inconsistent when charset conversion fails
OS-24 rfs3_readdir(): Issues related to nfscmd_convdirent()
Reviewed by: Jan Kryl <jan.kryl@nexenta.com>
Reviewed by: Gordon Ross <gordon.ross@nexenta.com>

@@ -16,15 +16,20 @@
  * fields enclosed by brackets "[]" replaced with your own identifying
  * information: Portions Copyright [yyyy] [name of copyright owner]
  *
  * CDDL HEADER END
  */
+
 /*
  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
+/*
+ * Copyright 2018 Nexenta Systems, Inc.
+ */
+
 #include <sys/param.h>
 #include <sys/types.h>
 #include <sys/pathname.h>
 #include <sys/errno.h>
 #include <sys/cmn_err.h>

@@ -43,38 +48,71 @@
 #ifdef nextdp
 #undef nextdp
 #endif
 #define nextdp(dp)      ((struct dirent64 *)((char *)(dp) + (dp)->d_reclen))
 
-kmutex_t        nfscmd_lock;
-door_handle_t   nfscmd_dh;
+typedef struct nfscmd_globals {
+        kmutex_t        nfscmd_lock;
+        door_handle_t   nfscmd_dh;
+} nfscmd_globals_t;
 
+static zone_key_t nfscmd_zone_key;
+
 static struct charset_cache *nfscmd_charmap(exportinfo_t *exi,
     struct sockaddr *sp);
+static void *nfscmd_zone_init(zoneid_t);
+static void nfscmd_zone_fini(zoneid_t, void *);
 
-
 void
 nfscmd_args(uint_t did)
 {
-        mutex_enter(&nfscmd_lock);
-        if (nfscmd_dh)
-                door_ki_rele(nfscmd_dh);
-        nfscmd_dh = door_ki_lookup(did);
-        mutex_exit(&nfscmd_lock);
+        nfscmd_globals_t *ncg = zone_getspecific(nfscmd_zone_key, curzone);
+
+        mutex_enter(&ncg->nfscmd_lock);
+        if (ncg->nfscmd_dh != NULL)
+                door_ki_rele(ncg->nfscmd_dh);
+        ncg->nfscmd_dh = door_ki_lookup(did);
+        mutex_exit(&ncg->nfscmd_lock);
 }
 
 void
 nfscmd_init(void)
 {
-        mutex_init(&nfscmd_lock, NULL, MUTEX_DEFAULT, NULL);
+        zone_key_create(&nfscmd_zone_key, nfscmd_zone_init,
+            NULL, nfscmd_zone_fini);
 }
 
 void
 nfscmd_fini(void)
 {
+        (void) zone_key_delete(nfscmd_zone_key);
 }
 
+/*ARGSUSED*/
+static void *
+nfscmd_zone_init(zoneid_t zoneid)
+{
+        nfscmd_globals_t *ncg;
+
+        ncg = kmem_zalloc(sizeof (*ncg), KM_SLEEP);
+        mutex_init(&ncg->nfscmd_lock, NULL, MUTEX_DEFAULT, NULL);
+
+        return (ncg);
+}
+
+/*ARGSUSED*/
+static void
+nfscmd_zone_fini(zoneid_t zoneid, void *data)
+{
+        nfscmd_globals_t *ncg = data;
+
+        mutex_destroy(&ncg->nfscmd_lock);
+        if (ncg->nfscmd_dh)
+                door_ki_rele(ncg->nfscmd_dh);
+        kmem_free(ncg, sizeof (*ncg));
+}
+
 /*
  * nfscmd_send(arg, result)
  *
  * Send a command to the daemon listening on the door. The result is
  * returned in the result pointer if the function return value is

@@ -86,17 +124,18 @@
         door_handle_t   dh;
         door_arg_t      da;
         door_info_t     di;
         int             ntries = 0;
         int             last = 0;
+        nfscmd_globals_t *ncg = zone_getspecific(nfscmd_zone_key, curzone);
 
 retry:
-        mutex_enter(&nfscmd_lock);
-        dh = nfscmd_dh;
+        mutex_enter(&ncg->nfscmd_lock);
+        dh = ncg->nfscmd_dh;
         if (dh != NULL)
                 door_ki_hold(dh);
-        mutex_exit(&nfscmd_lock);
+        mutex_exit(&ncg->nfscmd_lock);
 
         if (dh == NULL) {
                 /*
                  * The rendezvous point has not been established yet !
                  * This could mean that either mountd(1m) has not yet

@@ -139,14 +178,14 @@
                                  * the (existing) door on us; we
                                  * want to wait to give smf(5) a
                                  * chance to restart mountd(1m)
                                  * and establish a new door handle.
                                  */
-                                mutex_enter(&nfscmd_lock);
-                                if (dh == nfscmd_dh)
-                                        nfscmd_dh = NULL;
-                                mutex_exit(&nfscmd_lock);
+                                mutex_enter(&ncg->nfscmd_lock);
+                                if (dh == ncg->nfscmd_dh)
+                                        ncg->nfscmd_dh = NULL;
+                                mutex_exit(&ncg->nfscmd_lock);
                                 door_ki_rele(dh);
                                 delay(hz);
                                 goto retry;
                         }
                         /*

@@ -254,14 +293,14 @@
         charset = (struct charset_cache *)
             kmem_zalloc(sizeof (struct charset_cache), KM_SLEEP);
 
         if (charset == NULL)
                 return (NULL);
-        if (name != NULL) {
+
                 charset->inbound = kiconv_open("UTF-8", name);
                 charset->outbound = kiconv_open(name, "UTF-8");
-        }
+
         charset->client_addr = *sp;
         mutex_enter(&exi->exi_lock);
         charset->next = exi->exi_charset;
         exi->exi_charset = charset;
         mutex_exit(&exi->exi_lock);

@@ -332,12 +371,12 @@
         size_t osize;
         struct charset_cache *charset = NULL;
 
         charset = nfscmd_findmap(exi, ca);
         if (charset == NULL ||
-            (charset->inbound == NULL && inbound) ||
-            (charset->outbound == NULL && !inbound))
+            (charset->inbound == (kiconv_t)-1 && inbound) ||
+            (charset->outbound == (kiconv_t)-1 && !inbound))
                 return (name);
 
         /* make sure we have more than enough space */
         newname = kmem_zalloc(size, KM_SLEEP);
         nsize = strlen(name);

@@ -353,182 +392,6 @@
                 kmem_free(newname, size);
                 newname = NULL;
         }
 
         return (newname);
-}
-
-/*
- * nfscmd_convdirent()
- *
- * There is only one entry in the data.  Convert to new charset, if
- * required and only return a success if it fits.
- */
-char *
-nfscmd_convdirent(struct sockaddr *ca, struct exportinfo *exi, char *data,
-    size_t size, enum nfsstat3 *error)
-{
-        char *newdata;
-        size_t ret;
-        size_t nsize;
-        size_t count;
-        int err = 0;
-        char *iname;
-        char *oname;
-        struct charset_cache *charset;
-
-        charset = nfscmd_findmap(exi, ca);
-        if (charset == NULL || charset->outbound == (void *)~0)
-                return (data);
-
-        newdata = kmem_zalloc(size, KM_SLEEP);
-
-        nsize = strlen(((struct dirent64 *)data)->d_name);
-        count = size;
-        bcopy(data, newdata, sizeof (struct dirent64));
-
-        iname = ((struct dirent64 *)data)->d_name;
-        oname = ((struct dirent64 *)newdata)->d_name;
-
-        ret = kiconv(charset->outbound, &iname, &nsize, &oname, &count, &err);
-        if (ret == (size_t)-1) {
-                kmem_free(newdata, size);
-                newdata = NULL;
-                if (err == E2BIG) {
-                        if (error != NULL)
-                                *error = NFS3ERR_NAMETOOLONG;
-                } else {
-                        newdata = data;
-                }
-        } else {
-                ret = strlen(((struct dirent64 *)newdata)->d_name);
-                ((struct dirent64 *)newdata)->d_reclen =
-                    DIRENT64_RECLEN(ret + 1);
-        }
-        return (newdata);
-}
-
-/*
- * nfscmd_convdirplus(addr, export, data, nents, maxsize, ndata)
- *
- * Convert the dirents in data into a new list of dirents in ndata.
- */
-
-size_t
-nfscmd_convdirplus(struct sockaddr *ca, struct exportinfo *exi, char *data,
-    size_t nents, size_t maxsize, char **ndata)
-{
-        char *newdata;
-        size_t nsize;
-        struct dirent64 *dp;
-        struct dirent64 *ndp;
-        size_t i;
-        size_t ret;
-        char *iname;
-        char *oname;
-        size_t ilen;
-        size_t olen;
-        int err;
-        size_t skipped;
-        struct charset_cache *charset;
-        *ndata = data;  /* return the data if no changes to make */
-
-        charset = nfscmd_findmap(exi, ca);
-
-        if (charset == NULL || charset->outbound == (void *)~0)
-                return (0);
-
-        newdata = kmem_zalloc(maxsize, KM_SLEEP);
-        nsize = 0;
-
-        dp = (struct dirent64 *)data;
-        ndp = (struct dirent64 *)newdata;
-
-        for (skipped = 0, i = 0; i < nents; i++) {
-                /*
-                 * Copy the dp information if it fits. Then copy and
-                 * convert the name in the entry.
-                 */
-                if ((maxsize - nsize) < dp->d_reclen)
-                        /* doesn't fit */
-                        break;
-                *ndp = *dp;
-                iname = dp->d_name;
-                ilen = strlen(iname);
-                oname = ndp->d_name;
-                olen = MIN(MAXNAMELEN, maxsize - nsize);
-                ret = kiconv(charset->outbound, &iname, &ilen, &oname,
-                    &olen, &err);
-
-                if (ret == (size_t)-1) {
-                        switch (err) {
-                        default:
-                        case E2BIG:
-                                break;
-                        case EILSEQ:
-                                skipped++;
-                                dp = nextdp(dp);
-                                continue;
-                        }
-                }
-                ilen = MIN(MAXNAMELEN, maxsize - nsize) - olen;
-                ndp->d_name[ilen] = '\0';
-                /*
-                 * What to do with other errors?
-                 * For now, we return the unconverted string.
-                 */
-                ndp->d_reclen = DIRENT64_RECLEN(strlen(ndp->d_name) + 1);
-                nsize += ndp->d_reclen;
-                dp = nextdp(dp);
-                ndp = nextdp(ndp);
-        }
-
-        *ndata = newdata;
-        return (nents - (i + skipped));
-}
-
-/*
- * nfscmd_countents(data, len)
- *
- * How many dirents are there in the data buffer?
- */
-
-size_t
-nfscmd_countents(char *data, size_t len)
-{
-        struct dirent64 *dp = (struct dirent64 *)data;
-        size_t curlen;
-        size_t reclen;
-        size_t nents;
-
-        for (nents = 0, curlen = 0; curlen < len; curlen += reclen, nents++) {
-                reclen = dp->d_reclen;
-                dp = nextdp(dp);
-        }
-        return (nents);
-}
-
-/*
- * nfscmd_dropped_entrysize(dir, drop, nents)
- *
- * We need to drop "drop" entries from dir in order to fit in the
- * buffer.  How much do we reduce the overall size by?
- */
-
-size_t
-nfscmd_dropped_entrysize(struct dirent64 *dir, size_t drop, size_t nents)
-{
-        size_t size;
-        size_t i;
-
-        for (i = nents - drop; i > 0 && dir != NULL; i--)
-                dir = nextdp(dir);
-
-        if (dir == NULL)
-                return (0);
-
-        for (size = 0, i = 0; i < drop && dir != NULL; i++) {
-                size += dir->d_reclen;
-                dir = nextdp(dir);
-        }
-        return (size);
 }