The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/contrib/openzfs/lib/libshare/os/linux/nfs.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*
    2  * CDDL HEADER START
    3  *
    4  * The contents of this file are subject to the terms of the
    5  * Common Development and Distribution License (the "License").
    6  * You may not use this file except in compliance with the License.
    7  *
    8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
    9  * or https://opensource.org/licenses/CDDL-1.0.
   10  * See the License for the specific language governing permissions
   11  * and limitations under the License.
   12  *
   13  * When distributing Covered Code, include this CDDL HEADER in each
   14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
   15  * If applicable, add the following below this CDDL HEADER, with the
   16  * fields enclosed by brackets "[]" replaced with your own identifying
   17  * information: Portions Copyright [yyyy] [name of copyright owner]
   18  *
   19  * CDDL HEADER END
   20  */
   21 
   22 /*
   23  * Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
   24  * Copyright (c) 2011 Gunnar Beutner
   25  * Copyright (c) 2012 Cyril Plisko. All rights reserved.
   26  * Copyright (c) 2019, 2022 by Delphix. All rights reserved.
   27  */
   28 
   29 #include <dirent.h>
   30 #include <stdio.h>
   31 #include <string.h>
   32 #include <errno.h>
   33 #include <fcntl.h>
   34 #include <sys/file.h>
   35 #include <sys/stat.h>
   36 #include <sys/types.h>
   37 #include <sys/wait.h>
   38 #include <unistd.h>
   39 #include <libzfs.h>
   40 #include <libshare.h>
   41 #include "libshare_impl.h"
   42 #include "nfs.h"
   43 
   44 #define ZFS_EXPORTS_DIR         "/etc/exports.d"
   45 #define ZFS_EXPORTS_FILE        ZFS_EXPORTS_DIR"/zfs.exports"
   46 #define ZFS_EXPORTS_LOCK        ZFS_EXPORTS_FILE".lock"
   47 
   48 
   49 static boolean_t nfs_available(void);
   50 
   51 typedef int (*nfs_shareopt_callback_t)(const char *opt, const char *value,
   52     void *cookie);
   53 
   54 typedef int (*nfs_host_callback_t)(FILE *tmpfile, const char *sharepath,
   55     const char *host, const char *security, const char *access, void *cookie);
   56 
   57 /*
   58  * Invokes the specified callback function for each Solaris share option
   59  * listed in the specified string.
   60  */
   61 static int
   62 foreach_nfs_shareopt(const char *shareopts,
   63     nfs_shareopt_callback_t callback, void *cookie)
   64 {
   65         char *shareopts_dup, *opt, *cur, *value;
   66         int was_nul, error;
   67 
   68         if (shareopts == NULL)
   69                 return (SA_OK);
   70 
   71         if (strcmp(shareopts, "on") == 0)
   72                 shareopts = "rw,crossmnt";
   73 
   74         shareopts_dup = strdup(shareopts);
   75 
   76 
   77         if (shareopts_dup == NULL)
   78                 return (SA_NO_MEMORY);
   79 
   80         opt = shareopts_dup;
   81         was_nul = 0;
   82 
   83         while (1) {
   84                 cur = opt;
   85 
   86                 while (*cur != ',' && *cur != '\0')
   87                         cur++;
   88 
   89                 if (*cur == '\0')
   90                         was_nul = 1;
   91 
   92                 *cur = '\0';
   93 
   94                 if (cur > opt) {
   95                         value = strchr(opt, '=');
   96 
   97                         if (value != NULL) {
   98                                 *value = '\0';
   99                                 value++;
  100                         }
  101 
  102                         error = callback(opt, value, cookie);
  103 
  104                         if (error != SA_OK) {
  105                                 free(shareopts_dup);
  106                                 return (error);
  107                         }
  108                 }
  109 
  110                 opt = cur + 1;
  111 
  112                 if (was_nul)
  113                         break;
  114         }
  115 
  116         free(shareopts_dup);
  117 
  118         return (SA_OK);
  119 }
  120 
  121 typedef struct nfs_host_cookie_s {
  122         nfs_host_callback_t callback;
  123         const char *sharepath;
  124         void *cookie;
  125         FILE *tmpfile;
  126         const char *security;
  127 } nfs_host_cookie_t;
  128 
  129 /*
  130  * Helper function for foreach_nfs_host. This function checks whether the
  131  * current share option is a host specification and invokes a callback
  132  * function with information about the host.
  133  */
  134 static int
  135 foreach_nfs_host_cb(const char *opt, const char *value, void *pcookie)
  136 {
  137         int error;
  138         const char *access;
  139         char *host_dup, *host, *next, *v6Literal;
  140         nfs_host_cookie_t *udata = (nfs_host_cookie_t *)pcookie;
  141         int cidr_len;
  142 
  143 #ifdef DEBUG
  144         fprintf(stderr, "foreach_nfs_host_cb: key=%s, value=%s\n", opt, value);
  145 #endif
  146 
  147         if (strcmp(opt, "sec") == 0)
  148                 udata->security = value;
  149 
  150         if (strcmp(opt, "rw") == 0 || strcmp(opt, "ro") == 0) {
  151                 if (value == NULL)
  152                         value = "*";
  153 
  154                 access = opt;
  155 
  156                 host_dup = strdup(value);
  157 
  158                 if (host_dup == NULL)
  159                         return (SA_NO_MEMORY);
  160 
  161                 host = host_dup;
  162 
  163                 do {
  164                         if (*host == '[') {
  165                                 host++;
  166                                 v6Literal = strchr(host, ']');
  167                                 if (v6Literal == NULL) {
  168                                         free(host_dup);
  169                                         return (SA_SYNTAX_ERR);
  170                                 }
  171                                 if (v6Literal[1] == '\0') {
  172                                         *v6Literal = '\0';
  173                                         next = NULL;
  174                                 } else if (v6Literal[1] == '/') {
  175                                         next = strchr(v6Literal + 2, ':');
  176                                         if (next == NULL) {
  177                                                 cidr_len =
  178                                                     strlen(v6Literal + 1);
  179                                                 memmove(v6Literal,
  180                                                     v6Literal + 1,
  181                                                     cidr_len);
  182                                                 v6Literal[cidr_len] = '\0';
  183                                         } else {
  184                                                 cidr_len = next - v6Literal - 1;
  185                                                 memmove(v6Literal,
  186                                                     v6Literal + 1,
  187                                                     cidr_len);
  188                                                 v6Literal[cidr_len] = '\0';
  189                                                 next++;
  190                                         }
  191                                 } else if (v6Literal[1] == ':') {
  192                                         *v6Literal = '\0';
  193                                         next = v6Literal + 2;
  194                                 } else {
  195                                         free(host_dup);
  196                                         return (SA_SYNTAX_ERR);
  197                                 }
  198                         } else {
  199                                 next = strchr(host, ':');
  200                                 if (next != NULL) {
  201                                         *next = '\0';
  202                                         next++;
  203                                 }
  204                         }
  205 
  206                         error = udata->callback(udata->tmpfile,
  207                             udata->sharepath, host, udata->security,
  208                             access, udata->cookie);
  209 
  210                         if (error != SA_OK) {
  211                                 free(host_dup);
  212 
  213                                 return (error);
  214                         }
  215 
  216                         host = next;
  217                 } while (host != NULL);
  218 
  219                 free(host_dup);
  220         }
  221 
  222         return (SA_OK);
  223 }
  224 
  225 /*
  226  * Invokes a callback function for all NFS hosts that are set for a share.
  227  */
  228 static int
  229 foreach_nfs_host(sa_share_impl_t impl_share, FILE *tmpfile,
  230     nfs_host_callback_t callback, void *cookie)
  231 {
  232         nfs_host_cookie_t udata;
  233 
  234         udata.callback = callback;
  235         udata.sharepath = impl_share->sa_mountpoint;
  236         udata.cookie = cookie;
  237         udata.tmpfile = tmpfile;
  238         udata.security = "sys";
  239 
  240         return (foreach_nfs_shareopt(impl_share->sa_shareopts,
  241             foreach_nfs_host_cb, &udata));
  242 }
  243 
  244 /*
  245  * Converts a Solaris NFS host specification to its Linux equivalent.
  246  */
  247 static const char *
  248 get_linux_hostspec(const char *solaris_hostspec)
  249 {
  250         /*
  251          * For now we just support CIDR masks (e.g. @192.168.0.0/16) and host
  252          * wildcards (e.g. *.example.org).
  253          */
  254         if (solaris_hostspec[0] == '@') {
  255                 /*
  256                  * Solaris host specifier, e.g. @192.168.0.0/16; we just need
  257                  * to skip the @ in this case
  258                  */
  259                 return (solaris_hostspec + 1);
  260         } else {
  261                 return (solaris_hostspec);
  262         }
  263 }
  264 
  265 /*
  266  * Adds a Linux share option to an array of NFS options.
  267  */
  268 static int
  269 add_linux_shareopt(char **plinux_opts, const char *key, const char *value)
  270 {
  271         size_t len = 0;
  272         char *new_linux_opts;
  273 
  274         if (*plinux_opts != NULL)
  275                 len = strlen(*plinux_opts);
  276 
  277         new_linux_opts = realloc(*plinux_opts, len + 1 + strlen(key) +
  278             (value ? 1 + strlen(value) : 0) + 1);
  279 
  280         if (new_linux_opts == NULL)
  281                 return (SA_NO_MEMORY);
  282 
  283         new_linux_opts[len] = '\0';
  284 
  285         if (len > 0)
  286                 strcat(new_linux_opts, ",");
  287 
  288         strcat(new_linux_opts, key);
  289 
  290         if (value != NULL) {
  291                 strcat(new_linux_opts, "=");
  292                 strcat(new_linux_opts, value);
  293         }
  294 
  295         *plinux_opts = new_linux_opts;
  296 
  297         return (SA_OK);
  298 }
  299 
  300 static int string_cmp(const void *lhs, const void *rhs) {
  301         const char *const *l = lhs, *const *r = rhs;
  302         return (strcmp(*l, *r));
  303 }
  304 
  305 /*
  306  * Validates and converts a single Solaris share option to its Linux
  307  * equivalent.
  308  */
  309 static int
  310 get_linux_shareopts_cb(const char *key, const char *value, void *cookie)
  311 {
  312         /* This list must remain sorted, since we bsearch() it */
  313         static const char *const valid_keys[] = { "all_squash", "anongid",
  314             "anonuid", "async", "auth_nlm", "crossmnt", "fsid", "fsuid", "hide",
  315             "insecure", "insecure_locks", "mountpoint", "mp", "no_acl",
  316             "no_all_squash", "no_auth_nlm", "no_root_squash",
  317             "no_subtree_check", "no_wdelay", "nohide", "refer", "replicas",
  318             "root_squash", "secure", "secure_locks", "subtree_check", "sync",
  319             "wdelay" };
  320 
  321         char **plinux_opts = (char **)cookie;
  322 
  323         /* host-specific options, these are taken care of elsewhere */
  324         if (strcmp(key, "ro") == 0 || strcmp(key, "rw") == 0 ||
  325             strcmp(key, "sec") == 0)
  326                 return (SA_OK);
  327 
  328         if (strcmp(key, "anon") == 0)
  329                 key = "anonuid";
  330 
  331         if (strcmp(key, "root_mapping") == 0) {
  332                 (void) add_linux_shareopt(plinux_opts, "root_squash", NULL);
  333                 key = "anonuid";
  334         }
  335 
  336         if (strcmp(key, "nosub") == 0)
  337                 key = "subtree_check";
  338 
  339         if (bsearch(&key, valid_keys, ARRAY_SIZE(valid_keys),
  340             sizeof (*valid_keys), string_cmp) == NULL)
  341                 return (SA_SYNTAX_ERR);
  342 
  343         (void) add_linux_shareopt(plinux_opts, key, value);
  344 
  345         return (SA_OK);
  346 }
  347 
  348 /*
  349  * Takes a string containing Solaris share options (e.g. "sync,no_acl") and
  350  * converts them to a NULL-terminated array of Linux NFS options.
  351  */
  352 static int
  353 get_linux_shareopts(const char *shareopts, char **plinux_opts)
  354 {
  355         int error;
  356 
  357         assert(plinux_opts != NULL);
  358 
  359         *plinux_opts = NULL;
  360 
  361         /* no_subtree_check - Default as of nfs-utils v1.1.0 */
  362         (void) add_linux_shareopt(plinux_opts, "no_subtree_check", NULL);
  363 
  364         /* mountpoint - Restrict exports to ZFS mountpoints */
  365         (void) add_linux_shareopt(plinux_opts, "mountpoint", NULL);
  366 
  367         error = foreach_nfs_shareopt(shareopts, get_linux_shareopts_cb,
  368             plinux_opts);
  369 
  370         if (error != SA_OK) {
  371                 free(*plinux_opts);
  372                 *plinux_opts = NULL;
  373         }
  374 
  375         return (error);
  376 }
  377 
  378 /*
  379  * This function populates an entry into /etc/exports.d/zfs.exports.
  380  * This file is consumed by the linux nfs server so that zfs shares are
  381  * automatically exported upon boot or whenever the nfs server restarts.
  382  */
  383 static int
  384 nfs_add_entry(FILE *tmpfile, const char *sharepath,
  385     const char *host, const char *security, const char *access_opts,
  386     void *pcookie)
  387 {
  388         const char *linux_opts = (const char *)pcookie;
  389 
  390         if (linux_opts == NULL)
  391                 linux_opts = "";
  392 
  393         boolean_t need_free;
  394         char *mp;
  395         int rc = nfs_escape_mountpoint(sharepath, &mp, &need_free);
  396         if (rc != SA_OK)
  397                 return (rc);
  398         if (fprintf(tmpfile, "%s %s(sec=%s,%s,%s)\n", mp,
  399             get_linux_hostspec(host), security, access_opts,
  400             linux_opts) < 0) {
  401                 fprintf(stderr, "failed to write to temporary file\n");
  402                 rc = SA_SYSTEM_ERR;
  403         }
  404 
  405         if (need_free)
  406                 free(mp);
  407         return (rc);
  408 }
  409 
  410 /*
  411  * Enables NFS sharing for the specified share.
  412  */
  413 static int
  414 nfs_enable_share_impl(sa_share_impl_t impl_share, FILE *tmpfile)
  415 {
  416         char *linux_opts = NULL;
  417         int error = get_linux_shareopts(impl_share->sa_shareopts, &linux_opts);
  418         if (error != SA_OK)
  419                 return (error);
  420 
  421         error = foreach_nfs_host(impl_share, tmpfile, nfs_add_entry,
  422             linux_opts);
  423         free(linux_opts);
  424         return (error);
  425 }
  426 
  427 static int
  428 nfs_enable_share(sa_share_impl_t impl_share)
  429 {
  430         if (!nfs_available())
  431                 return (SA_SYSTEM_ERR);
  432 
  433         return (nfs_toggle_share(
  434             ZFS_EXPORTS_LOCK, ZFS_EXPORTS_FILE, ZFS_EXPORTS_DIR, impl_share,
  435             nfs_enable_share_impl));
  436 }
  437 
  438 /*
  439  * Disables NFS sharing for the specified share.
  440  */
  441 static int
  442 nfs_disable_share_impl(sa_share_impl_t impl_share, FILE *tmpfile)
  443 {
  444         (void) impl_share, (void) tmpfile;
  445         return (SA_OK);
  446 }
  447 
  448 static int
  449 nfs_disable_share(sa_share_impl_t impl_share)
  450 {
  451         if (!nfs_available())
  452                 return (SA_OK);
  453 
  454         return (nfs_toggle_share(
  455             ZFS_EXPORTS_LOCK, ZFS_EXPORTS_FILE, ZFS_EXPORTS_DIR, impl_share,
  456             nfs_disable_share_impl));
  457 }
  458 
  459 static boolean_t
  460 nfs_is_shared(sa_share_impl_t impl_share)
  461 {
  462         if (!nfs_available())
  463                 return (SA_SYSTEM_ERR);
  464 
  465         return (nfs_is_shared_impl(ZFS_EXPORTS_FILE, impl_share));
  466 }
  467 
  468 /*
  469  * Checks whether the specified NFS share options are syntactically correct.
  470  */
  471 static int
  472 nfs_validate_shareopts(const char *shareopts)
  473 {
  474         char *linux_opts = NULL;
  475         int error = get_linux_shareopts(shareopts, &linux_opts);
  476         if (error != SA_OK)
  477                 return (error);
  478 
  479         free(linux_opts);
  480         return (SA_OK);
  481 }
  482 
  483 static int
  484 nfs_commit_shares(void)
  485 {
  486         if (!nfs_available())
  487                 return (SA_SYSTEM_ERR);
  488 
  489         char *argv[] = {
  490             (char *)"/usr/sbin/exportfs",
  491             (char *)"-ra",
  492             NULL
  493         };
  494 
  495         return (libzfs_run_process(argv[0], argv, 0));
  496 }
  497 
  498 static void
  499 nfs_truncate_shares(void)
  500 {
  501         nfs_reset_shares(ZFS_EXPORTS_LOCK, ZFS_EXPORTS_FILE);
  502 }
  503 
  504 const sa_fstype_t libshare_nfs_type = {
  505         .enable_share = nfs_enable_share,
  506         .disable_share = nfs_disable_share,
  507         .is_shared = nfs_is_shared,
  508 
  509         .validate_shareopts = nfs_validate_shareopts,
  510         .commit_shares = nfs_commit_shares,
  511         .truncate_shares = nfs_truncate_shares,
  512 };
  513 
  514 static boolean_t
  515 nfs_available(void)
  516 {
  517         static int avail;
  518 
  519         if (!avail) {
  520                 if (access("/usr/sbin/exportfs", F_OK) != 0)
  521                         avail = -1;
  522                 else
  523                         avail = 1;
  524         }
  525 
  526         return (avail == 1);
  527 }

Cache object: dad978e9c8479230676d75fef91800a1


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.