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 https://opensource.org/licenses/CDDL-1.0.
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
25 * Copyright (c) 2012 Cyril Plisko. All rights reserved.
26 * Copyright (c) 2019, 2022 by Delphix. All rights reserved.
36 #include <sys/types.h>
41 #include "libshare_impl.h"
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"
49 static boolean_t
nfs_available(void);
50 static boolean_t
exports_available(void);
52 typedef int (*nfs_shareopt_callback_t
)(const char *opt
, const char *value
,
55 typedef int (*nfs_host_callback_t
)(FILE *tmpfile
, const char *sharepath
,
56 const char *host
, const char *security
, const char *access
, void *cookie
);
59 * Invokes the specified callback function for each Solaris share option
60 * listed in the specified string.
63 foreach_nfs_shareopt(const char *shareopts
,
64 nfs_shareopt_callback_t callback
, void *cookie
)
66 char *shareopts_dup
, *opt
, *cur
, *value
;
69 if (shareopts
== NULL
)
72 if (strcmp(shareopts
, "on") == 0)
73 shareopts
= "rw,crossmnt";
75 shareopts_dup
= strdup(shareopts
);
78 if (shareopts_dup
== NULL
)
79 return (SA_NO_MEMORY
);
87 while (*cur
!= ',' && *cur
!= '\0')
96 value
= strchr(opt
, '=');
103 error
= callback(opt
, value
, cookie
);
105 if (error
!= SA_OK
) {
122 typedef struct nfs_host_cookie_s
{
123 nfs_host_callback_t callback
;
124 const char *sharepath
;
127 const char *security
;
131 * Helper function for foreach_nfs_host. This function checks whether the
132 * current share option is a host specification and invokes a callback
133 * function with information about the host.
136 foreach_nfs_host_cb(const char *opt
, const char *value
, void *pcookie
)
140 char *host_dup
, *host
, *next
, *v6Literal
;
141 nfs_host_cookie_t
*udata
= (nfs_host_cookie_t
*)pcookie
;
145 fprintf(stderr
, "foreach_nfs_host_cb: key=%s, value=%s\n", opt
, value
);
148 if (strcmp(opt
, "sec") == 0)
149 udata
->security
= value
;
151 if (strcmp(opt
, "rw") == 0 || strcmp(opt
, "ro") == 0) {
157 host_dup
= strdup(value
);
159 if (host_dup
== NULL
)
160 return (SA_NO_MEMORY
);
167 v6Literal
= strchr(host
, ']');
168 if (v6Literal
== NULL
) {
170 return (SA_SYNTAX_ERR
);
172 if (v6Literal
[1] == '\0') {
175 } else if (v6Literal
[1] == '/') {
176 next
= strchr(v6Literal
+ 2, ':');
179 strlen(v6Literal
+ 1);
183 v6Literal
[cidr_len
] = '\0';
185 cidr_len
= next
- v6Literal
- 1;
189 v6Literal
[cidr_len
] = '\0';
192 } else if (v6Literal
[1] == ':') {
194 next
= v6Literal
+ 2;
197 return (SA_SYNTAX_ERR
);
200 next
= strchr(host
, ':');
207 error
= udata
->callback(udata
->tmpfile
,
208 udata
->sharepath
, host
, udata
->security
,
209 access
, udata
->cookie
);
211 if (error
!= SA_OK
) {
218 } while (host
!= NULL
);
227 * Invokes a callback function for all NFS hosts that are set for a share.
230 foreach_nfs_host(sa_share_impl_t impl_share
, FILE *tmpfile
,
231 nfs_host_callback_t callback
, void *cookie
)
233 nfs_host_cookie_t udata
;
235 udata
.callback
= callback
;
236 udata
.sharepath
= impl_share
->sa_mountpoint
;
237 udata
.cookie
= cookie
;
238 udata
.tmpfile
= tmpfile
;
239 udata
.security
= "sys";
241 return (foreach_nfs_shareopt(impl_share
->sa_shareopts
,
242 foreach_nfs_host_cb
, &udata
));
246 * Converts a Solaris NFS host specification to its Linux equivalent.
249 get_linux_hostspec(const char *solaris_hostspec
)
252 * For now we just support CIDR masks (e.g. @192.168.0.0/16) and host
253 * wildcards (e.g. *.example.org).
255 if (solaris_hostspec
[0] == '@') {
257 * Solaris host specifier, e.g. @192.168.0.0/16; we just need
258 * to skip the @ in this case
260 return (solaris_hostspec
+ 1);
262 return (solaris_hostspec
);
267 * Adds a Linux share option to an array of NFS options.
270 add_linux_shareopt(char **plinux_opts
, const char *key
, const char *value
)
273 char *new_linux_opts
;
275 if (*plinux_opts
!= NULL
)
276 len
= strlen(*plinux_opts
);
278 new_linux_opts
= realloc(*plinux_opts
, len
+ 1 + strlen(key
) +
279 (value
? 1 + strlen(value
) : 0) + 1);
281 if (new_linux_opts
== NULL
)
282 return (SA_NO_MEMORY
);
284 new_linux_opts
[len
] = '\0';
287 strcat(new_linux_opts
, ",");
289 strcat(new_linux_opts
, key
);
292 strcat(new_linux_opts
, "=");
293 strcat(new_linux_opts
, value
);
296 *plinux_opts
= new_linux_opts
;
301 static int string_cmp(const void *lhs
, const void *rhs
) {
302 const char *const *l
= lhs
, *const *r
= rhs
;
303 return (strcmp(*l
, *r
));
307 * Validates and converts a single Solaris share option to its Linux
311 get_linux_shareopts_cb(const char *key
, const char *value
, void *cookie
)
313 /* This list must remain sorted, since we bsearch() it */
314 static const char *const valid_keys
[] = { "all_squash", "anongid",
315 "anonuid", "async", "auth_nlm", "crossmnt", "fsid", "fsuid", "hide",
316 "insecure", "insecure_locks", "mountpoint", "mp", "no_acl",
317 "no_all_squash", "no_auth_nlm", "no_root_squash",
318 "no_subtree_check", "no_wdelay", "nohide", "refer", "replicas",
319 "root_squash", "secure", "secure_locks", "subtree_check", "sync",
322 char **plinux_opts
= (char **)cookie
;
323 char *host
, *val_dup
, *literal
, *next
;
325 if (strcmp(key
, "sec") == 0)
328 if (strcmp(key
, "ro") == 0 || strcmp(key
, "rw") == 0) {
329 if (value
== NULL
|| strlen(value
) == 0)
331 val_dup
= strdup(value
);
334 return (SA_NO_MEMORY
);
338 literal
= strchr(host
, ']');
339 if (literal
== NULL
) {
341 return (SA_SYNTAX_ERR
);
343 if (literal
[1] == '\0')
345 else if (literal
[1] == '/') {
346 next
= strchr(literal
+ 2, ':');
349 } else if (literal
[1] == ':')
353 return (SA_SYNTAX_ERR
);
356 next
= strchr(host
, ':');
361 } while (host
!= NULL
);
366 if (strcmp(key
, "anon") == 0)
369 if (strcmp(key
, "root_mapping") == 0) {
370 (void) add_linux_shareopt(plinux_opts
, "root_squash", NULL
);
374 if (strcmp(key
, "nosub") == 0)
375 key
= "subtree_check";
377 if (bsearch(&key
, valid_keys
, ARRAY_SIZE(valid_keys
),
378 sizeof (*valid_keys
), string_cmp
) == NULL
)
379 return (SA_SYNTAX_ERR
);
381 (void) add_linux_shareopt(plinux_opts
, key
, value
);
387 * Takes a string containing Solaris share options (e.g. "sync,no_acl") and
388 * converts them to a NULL-terminated array of Linux NFS options.
391 get_linux_shareopts(const char *shareopts
, char **plinux_opts
)
395 assert(plinux_opts
!= NULL
);
399 /* no_subtree_check - Default as of nfs-utils v1.1.0 */
400 (void) add_linux_shareopt(plinux_opts
, "no_subtree_check", NULL
);
402 /* mountpoint - Restrict exports to ZFS mountpoints */
403 (void) add_linux_shareopt(plinux_opts
, "mountpoint", NULL
);
405 error
= foreach_nfs_shareopt(shareopts
, get_linux_shareopts_cb
,
408 if (error
!= SA_OK
) {
417 * This function populates an entry into /etc/exports.d/zfs.exports.
418 * This file is consumed by the linux nfs server so that zfs shares are
419 * automatically exported upon boot or whenever the nfs server restarts.
422 nfs_add_entry(FILE *tmpfile
, const char *sharepath
,
423 const char *host
, const char *security
, const char *access_opts
,
426 const char *linux_opts
= (const char *)pcookie
;
428 if (linux_opts
== NULL
)
433 int rc
= nfs_escape_mountpoint(sharepath
, &mp
, &need_free
);
436 if (fprintf(tmpfile
, "%s %s(sec=%s,%s,%s)\n", mp
,
437 get_linux_hostspec(host
), security
, access_opts
,
439 fprintf(stderr
, "failed to write to temporary file\n");
449 * Enables NFS sharing for the specified share.
452 nfs_enable_share_impl(sa_share_impl_t impl_share
, FILE *tmpfile
)
454 char *linux_opts
= NULL
;
455 int error
= get_linux_shareopts(impl_share
->sa_shareopts
, &linux_opts
);
459 error
= foreach_nfs_host(impl_share
, tmpfile
, nfs_add_entry
,
466 nfs_enable_share(sa_share_impl_t impl_share
)
468 if (!nfs_available())
469 return (SA_SYSTEM_ERR
);
471 return (nfs_toggle_share(
472 ZFS_EXPORTS_LOCK
, ZFS_EXPORTS_FILE
, ZFS_EXPORTS_DIR
, impl_share
,
473 nfs_enable_share_impl
));
477 * Disables NFS sharing for the specified share.
480 nfs_disable_share_impl(sa_share_impl_t impl_share
, FILE *tmpfile
)
482 (void) impl_share
, (void) tmpfile
;
487 nfs_disable_share(sa_share_impl_t impl_share
)
489 if (!nfs_available())
492 return (nfs_toggle_share(
493 ZFS_EXPORTS_LOCK
, ZFS_EXPORTS_FILE
, ZFS_EXPORTS_DIR
, impl_share
,
494 nfs_disable_share_impl
));
498 nfs_is_shared(sa_share_impl_t impl_share
)
500 if (!nfs_available())
501 return (SA_SYSTEM_ERR
);
503 return (nfs_is_shared_impl(ZFS_EXPORTS_FILE
, impl_share
));
507 * Checks whether the specified NFS share options are syntactically correct.
510 nfs_validate_shareopts(const char *shareopts
)
512 char *linux_opts
= NULL
;
514 if (strlen(shareopts
) == 0)
515 return (SA_SYNTAX_ERR
);
517 int error
= get_linux_shareopts(shareopts
, &linux_opts
);
526 nfs_commit_shares(void)
528 if (!nfs_available())
529 return (SA_SYSTEM_ERR
);
532 (char *)"/usr/sbin/exportfs",
537 return (libzfs_run_process(argv
[0], argv
, 0));
541 nfs_truncate_shares(void)
543 if (!exports_available())
545 nfs_reset_shares(ZFS_EXPORTS_LOCK
, ZFS_EXPORTS_FILE
);
548 const sa_fstype_t libshare_nfs_type
= {
549 .enable_share
= nfs_enable_share
,
550 .disable_share
= nfs_disable_share
,
551 .is_shared
= nfs_is_shared
,
553 .validate_shareopts
= nfs_validate_shareopts
,
554 .commit_shares
= nfs_commit_shares
,
555 .truncate_shares
= nfs_truncate_shares
,
564 if (access("/usr/sbin/exportfs", F_OK
) != 0)
574 exports_available(void)
579 if (access(ZFS_EXPORTS_DIR
, F_OK
) != 0)