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.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
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]
23 * Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
24 * Copyright (c) 2011 Gunnar Beutner
34 #include "libshare_impl.h"
36 static sa_fstype_t *nfs_fstype;
37 static boolean_t nfs_available;
39 typedef int (*nfs_shareopt_callback_t)(const char *opt, const char *value,
42 typedef int (*nfs_host_callback_t)(const char *sharepath, const char *host,
43 const char *security, const char *access, void *cookie);
46 foreach_nfs_shareopt(const char *shareopts,
47 nfs_shareopt_callback_t callback, void *cookie)
49 char *shareopts_dup, *opt, *cur, *value;
52 if (shareopts == NULL)
55 shareopts_dup = strdup(shareopts);
57 if (shareopts_dup == NULL)
66 while (*cur != ',' && *cur != '\0')
75 value = strchr(opt, '=');
82 rc = callback(opt, value, cookie);
101 typedef struct nfs_host_cookie_s {
102 nfs_host_callback_t callback;
103 const char *sharepath;
105 const char *security;
109 foreach_nfs_host_cb(const char *opt, const char *value, void *pcookie)
113 char *host_dup, *host, *next;
114 nfs_host_cookie_t *udata = (nfs_host_cookie_t *)pcookie;
117 fprintf(stderr, "foreach_nfs_host_cb: key=%s, value=%s\n", opt, value);
120 if (strcmp(opt, "sec") == 0)
121 udata->security = value;
123 if (strcmp(opt, "rw") == 0 || strcmp(opt, "ro") == 0) {
129 host_dup = strdup(value);
131 if (host_dup == NULL)
137 next = strchr(host, ':');
143 rc = udata->callback(udata->sharepath, host,
144 udata->security, access, udata->cookie);
153 } while (host != NULL);
162 foreach_nfs_host(sa_share_impl_t impl_share, nfs_host_callback_t callback,
165 nfs_host_cookie_t udata;
168 udata.callback = callback;
169 udata.sharepath = impl_share->sharepath;
170 udata.cookie = cookie;
171 udata.security = "sys";
173 shareopts = FSINFO(impl_share, nfs_fstype)->shareopts;
175 return foreach_nfs_shareopt(shareopts, foreach_nfs_host_cb,
180 get_linux_hostspec(const char *solaris_hostspec, char **plinux_hostspec)
183 * For now we just support CIDR masks (e.g. @192.168.0.0/16) and host
184 * wildcards (e.g. *.example.org).
186 if (solaris_hostspec[0] == '@') {
188 * Solaris host specifier, e.g. @192.168.0.0/16; we just need
189 * to skip the @ in this case
191 *plinux_hostspec = strdup(solaris_hostspec + 1);
193 *plinux_hostspec = strdup(solaris_hostspec);
196 if (*plinux_hostspec == NULL) {
204 nfs_enable_share_one(const char *sharepath, const char *host,
205 const char *security, const char *access, void *pcookie)
209 char *linuxhost, *hostpath, *opts;
210 const char *linux_opts = (const char *)pcookie;
215 return SA_SYSTEM_ERR;
218 while ((rc = waitpid(pid, &status, 0)) <= 0 && errno == EINTR)
219 ; /* empty loop body */
222 return SA_SYSTEM_ERR;
224 if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
225 return SA_CONFIG_ERR;
232 /* exportfs -i -o sec=XX,rX,<opts> <host>:<sharepath> */
234 rc = get_linux_hostspec(host, &linuxhost);
239 hostpath = malloc(strlen(linuxhost) + 1 + strlen(sharepath) + 1);
241 if (hostpath == NULL) {
247 sprintf(hostpath, "%s:%s", linuxhost, sharepath);
251 if (linux_opts == NULL)
254 opts = malloc(4 + strlen(security) + 4 + strlen(linux_opts) + 1);
259 sprintf(opts, "sec=%s,%s,%s", security, access, linux_opts);
262 fprintf(stderr, "sharing %s with opts %s\n", hostpath, opts);
265 rc = execlp("/usr/sbin/exportfs", "exportfs", "-i", \
266 "-o", opts, hostpath, NULL);
278 add_linux_shareopt(char **plinux_opts, const char *key, const char *value)
281 char *new_linux_opts;
283 if (*plinux_opts != NULL)
284 len = strlen(*plinux_opts);
286 new_linux_opts = realloc(*plinux_opts, len + 1 + strlen(key) +
287 (value ? 1 + strlen(value) : 0) + 1);
289 if (new_linux_opts == NULL)
292 new_linux_opts[len] = '\0';
295 strcat(new_linux_opts, ",");
297 strcat(new_linux_opts, key);
300 strcat(new_linux_opts, "=");
301 strcat(new_linux_opts, value);
304 *plinux_opts = new_linux_opts;
310 get_linux_shareopts_cb(const char *key, const char *value, void *cookie)
312 char **plinux_opts = (char **)cookie;
314 /* host-specific options, these are taken care of elsewhere */
315 if (strcmp(key, "ro") == 0 || strcmp(key, "rw") == 0 ||
316 strcmp(key, "sec") == 0)
319 if (strcmp(key, "anon") == 0)
322 if (strcmp(key, "root_mapping") == 0) {
323 (void) add_linux_shareopt(plinux_opts, "root_squash", NULL);
327 if (strcmp(key, "nosub") == 0)
328 key = "subtree_check";
330 if (strcmp(key, "insecure") != 0 && strcmp(key, "secure") != 0 &&
331 strcmp(key, "async") != 0 && strcmp(key, "sync") != 0 &&
332 strcmp(key, "no_wdelay") != 0 && strcmp(key, "wdelay") != 0 &&
333 strcmp(key, "nohide") != 0 && strcmp(key, "hide") != 0 &&
334 strcmp(key, "crossmnt") != 0 &&
335 strcmp(key, "no_subtree_check") != 0 &&
336 strcmp(key, "subtree_check") != 0 &&
337 strcmp(key, "insecure_locks") != 0 &&
338 strcmp(key, "secure_locks") != 0 &&
339 strcmp(key, "no_auth_nlm") != 0 && strcmp(key, "auth_nlm") != 0 &&
340 strcmp(key, "no_acl") != 0 && strcmp(key, "mountpoint") != 0 &&
341 strcmp(key, "mp") != 0 && strcmp(key, "fsuid") != 0 &&
342 strcmp(key, "refer") != 0 && strcmp(key, "replicas") != 0 &&
343 strcmp(key, "root_squash") != 0 &&
344 strcmp(key, "no_root_squash") != 0 &&
345 strcmp(key, "all_squash") != 0 &&
346 strcmp(key, "no_all_squash") != 0 &&
347 strcmp(key, "anonuid") != 0 && strcmp(key, "anongid") != 0) {
348 return SA_SYNTAX_ERR;
351 (void) add_linux_shareopt(plinux_opts, key, value);
357 get_linux_shareopts(const char *shareopts, char **plinux_opts)
361 assert(plinux_opts != NULL);
365 /* default options for Solaris shares */
366 (void) add_linux_shareopt(plinux_opts, "no_subtree_check", NULL);
367 (void) add_linux_shareopt(plinux_opts, "no_root_squash", NULL);
368 (void) add_linux_shareopt(plinux_opts, "mountpoint", NULL);
370 rc = foreach_nfs_shareopt(shareopts, get_linux_shareopts_cb, plinux_opts);
381 nfs_enable_share(sa_share_impl_t impl_share)
383 char *shareopts, *linux_opts;
386 if (!nfs_available) {
387 return SA_SYSTEM_ERR;
390 shareopts = FSINFO(impl_share, nfs_fstype)->shareopts;
392 if (shareopts == NULL)
395 rc = get_linux_shareopts(shareopts, &linux_opts);
400 rc = foreach_nfs_host(impl_share, nfs_enable_share_one, linux_opts);
408 nfs_disable_share_one(const char *sharepath, const char *host,
409 const char *security, const char *access, void *cookie)
413 char *linuxhost, *hostpath;
418 return SA_SYSTEM_ERR;
421 while ((rc = waitpid(pid, &status, 0)) <= 0 && errno == EINTR)
422 ; /* empty loop body */
425 return SA_SYSTEM_ERR;
427 if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
428 return SA_CONFIG_ERR;
435 rc = get_linux_hostspec(host, &linuxhost);
440 hostpath = malloc(strlen(linuxhost) + 1 + strlen(sharepath) + 1);
442 if (hostpath == NULL) {
447 sprintf(hostpath, "%s:%s", linuxhost, sharepath);
452 fprintf(stderr, "unsharing %s\n", hostpath);
455 rc = execlp("/usr/sbin/exportfs", "exportfs", "-u", \
467 nfs_disable_share(sa_share_impl_t impl_share)
469 if (!nfs_available) {
471 * The share can't possibly be active, so nothing
472 * needs to be done to disable it.
477 return foreach_nfs_host(impl_share, nfs_disable_share_one, NULL);
481 nfs_validate_shareopts(const char *shareopts)
486 rc = get_linux_shareopts(shareopts, &linux_opts);
497 * TODO: Rather than invoking "exportfs -v" once for each share we should only
498 * call it once for all shares.
501 is_share_active(sa_share_impl_t impl_share)
506 FILE *exportfs_stdout;
509 boolean_t share_active = B_FALSE;
520 share_active = B_FALSE;
522 exportfs_stdout = fdopen(pipes[0], "r");
525 while (fgets(line, sizeof(line), exportfs_stdout) != NULL) {
530 * exportfs uses separate lines for the share path
531 * and the export options when the share path is longer
532 * than a certain amount of characters; this ignores
538 tab = strchr(line, '\t');
545 * there's no tab character, which means the
546 * NFS options are on a separate line; we just
547 * need to remove the new-line character
548 * at the end of the line
550 cur = line + strlen(line) - 1;
553 /* remove trailing spaces and new-line characters */
554 while (cur >= line &&
555 (*cur == ' ' || *cur == '\n'))
558 if (strcmp(line, impl_share->sharepath) == 0) {
559 share_active = B_TRUE;
562 * We can't break here just yet, otherwise
563 * exportfs might die due to write() failing
564 * with EPIPE once we've closed the pipe file
565 * descriptor - we need to read until EOF
566 * occurs on the stdout pipe.
571 fclose(exportfs_stdout);
573 while ((rc = waitpid(pid, &status, 0)) <= 0 && errno == EINTR)
574 ; /* empty loop body */
579 if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
591 if (dup2(pipes[1], STDOUT_FILENO) < 0)
594 rc = execlp("/usr/sbin/exportfs", "exportfs", "-v", NULL);
604 nfs_update_shareopts(sa_share_impl_t impl_share, const char *resource,
605 const char *shareopts)
608 boolean_t needs_reshare = B_FALSE;
611 FSINFO(impl_share, nfs_fstype)->active = is_share_active(impl_share);
613 old_shareopts = FSINFO(impl_share, nfs_fstype)->shareopts;
615 if (strcmp(shareopts, "on") == 0)
618 if (FSINFO(impl_share, nfs_fstype)->active && old_shareopts != NULL &&
619 strcmp(old_shareopts, shareopts) != 0) {
620 needs_reshare = B_TRUE;
621 nfs_disable_share(impl_share);
624 shareopts_dup = strdup(shareopts);
626 if (shareopts_dup == NULL)
629 if (old_shareopts != NULL)
632 FSINFO(impl_share, nfs_fstype)->shareopts = shareopts_dup;
635 nfs_enable_share(impl_share);
642 nfs_clear_shareopts(sa_share_impl_t impl_share)
644 free(FSINFO(impl_share, nfs_fstype)->shareopts);
645 FSINFO(impl_share, nfs_fstype)->shareopts = NULL;
648 static const sa_share_ops_t nfs_shareops = {
649 .enable_share = nfs_enable_share,
650 .disable_share = nfs_disable_share,
652 .validate_shareopts = nfs_validate_shareopts,
653 .update_shareopts = nfs_update_shareopts,
654 .clear_shareopts = nfs_clear_shareopts,
658 nfs_check_exportfs(void)
661 int rc, status, null_fd;
666 return SA_SYSTEM_ERR;
669 while ((rc = waitpid(pid, &status, 0)) <= 0 && errno == EINTR)
670 ; /* empty loop body */
673 return SA_SYSTEM_ERR;
675 if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
676 return SA_CONFIG_ERR;
683 null_fd = open("/dev/null", O_RDONLY);
685 if (null_fd < 0 || dup2(null_fd, 1) < 0 || dup2(null_fd, 2) < 0)
690 rc = execlp("/usr/sbin/exportfs", "exportfs", NULL);
700 libshare_nfs_init(void)
702 nfs_available = (nfs_check_exportfs() == SA_OK);
704 nfs_fstype = register_fstype("nfs", &nfs_shareops);