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
25 * Copyright (c) 2012 Cyril Plisko. All rights reserved.
26 * Copyright (c) 2019, 2020 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"
48 static sa_fstype_t
*nfs_fstype
;
50 typedef int (*nfs_shareopt_callback_t
)(const char *opt
, const char *value
,
53 typedef int (*nfs_host_callback_t
)(FILE *tmpfile
, const char *sharepath
,
54 const char *host
, const char *security
, const char *access
, void *cookie
);
57 * Invokes the specified callback function for each Solaris share option
58 * listed in the specified string.
61 foreach_nfs_shareopt(const char *shareopts
,
62 nfs_shareopt_callback_t callback
, void *cookie
)
64 char *shareopts_dup
, *opt
, *cur
, *value
;
67 if (shareopts
== NULL
)
70 if (strcmp(shareopts
, "on") == 0)
71 shareopts
= "rw,crossmnt";
73 shareopts_dup
= strdup(shareopts
);
76 if (shareopts_dup
== NULL
)
77 return (SA_NO_MEMORY
);
85 while (*cur
!= ',' && *cur
!= '\0')
94 value
= strchr(opt
, '=');
101 error
= callback(opt
, value
, cookie
);
103 if (error
!= SA_OK
) {
120 typedef struct nfs_host_cookie_s
{
121 nfs_host_callback_t callback
;
122 const char *sharepath
;
125 const char *security
;
129 * Helper function for foreach_nfs_host. This function checks whether the
130 * current share option is a host specification and invokes a callback
131 * function with information about the host.
134 foreach_nfs_host_cb(const char *opt
, const char *value
, void *pcookie
)
138 char *host_dup
, *host
, *next
, *v6Literal
;
139 nfs_host_cookie_t
*udata
= (nfs_host_cookie_t
*)pcookie
;
143 fprintf(stderr
, "foreach_nfs_host_cb: key=%s, value=%s\n", opt
, value
);
146 if (strcmp(opt
, "sec") == 0)
147 udata
->security
= value
;
149 if (strcmp(opt
, "rw") == 0 || strcmp(opt
, "ro") == 0) {
155 host_dup
= strdup(value
);
157 if (host_dup
== NULL
)
158 return (SA_NO_MEMORY
);
165 v6Literal
= strchr(host
, ']');
166 if (v6Literal
== NULL
) {
168 return (SA_SYNTAX_ERR
);
170 if (v6Literal
[1] == '\0') {
173 } else if (v6Literal
[1] == '/') {
174 next
= strchr(v6Literal
+ 2, ':');
177 strlen(v6Literal
+ 1);
181 v6Literal
[cidr_len
] = '\0';
183 cidr_len
= next
- v6Literal
- 1;
187 v6Literal
[cidr_len
] = '\0';
190 } else if (v6Literal
[1] == ':') {
192 next
= v6Literal
+ 2;
195 return (SA_SYNTAX_ERR
);
198 next
= strchr(host
, ':');
205 error
= udata
->callback(udata
->tmpfile
,
206 udata
->sharepath
, host
, udata
->security
,
207 access
, udata
->cookie
);
209 if (error
!= SA_OK
) {
216 } while (host
!= NULL
);
225 * Invokes a callback function for all NFS hosts that are set for a share.
228 foreach_nfs_host(sa_share_impl_t impl_share
, FILE *tmpfile
,
229 nfs_host_callback_t callback
, void *cookie
)
231 nfs_host_cookie_t udata
;
234 udata
.callback
= callback
;
235 udata
.sharepath
= impl_share
->sa_mountpoint
;
236 udata
.cookie
= cookie
;
237 udata
.tmpfile
= tmpfile
;
238 udata
.security
= "sys";
240 shareopts
= FSINFO(impl_share
, nfs_fstype
)->shareopts
;
242 return (foreach_nfs_shareopt(shareopts
, foreach_nfs_host_cb
,
247 * Converts a Solaris NFS host specification to its Linux equivalent.
250 get_linux_hostspec(const char *solaris_hostspec
)
253 * For now we just support CIDR masks (e.g. @192.168.0.0/16) and host
254 * wildcards (e.g. *.example.org).
256 if (solaris_hostspec
[0] == '@') {
258 * Solaris host specifier, e.g. @192.168.0.0/16; we just need
259 * to skip the @ in this case
261 return (solaris_hostspec
+ 1);
263 return (solaris_hostspec
);
268 * Adds a Linux share option to an array of NFS options.
271 add_linux_shareopt(char **plinux_opts
, const char *key
, const char *value
)
274 char *new_linux_opts
;
276 if (*plinux_opts
!= NULL
)
277 len
= strlen(*plinux_opts
);
279 new_linux_opts
= realloc(*plinux_opts
, len
+ 1 + strlen(key
) +
280 (value
? 1 + strlen(value
) : 0) + 1);
282 if (new_linux_opts
== NULL
)
283 return (SA_NO_MEMORY
);
285 new_linux_opts
[len
] = '\0';
288 strcat(new_linux_opts
, ",");
290 strcat(new_linux_opts
, key
);
293 strcat(new_linux_opts
, "=");
294 strcat(new_linux_opts
, value
);
297 *plinux_opts
= new_linux_opts
;
303 * Validates and converts a single Solaris share option to its Linux
307 get_linux_shareopts_cb(const char *key
, const char *value
, void *cookie
)
309 char **plinux_opts
= (char **)cookie
;
311 /* host-specific options, these are taken care of elsewhere */
312 if (strcmp(key
, "ro") == 0 || strcmp(key
, "rw") == 0 ||
313 strcmp(key
, "sec") == 0)
316 if (strcmp(key
, "anon") == 0)
319 if (strcmp(key
, "root_mapping") == 0) {
320 (void) add_linux_shareopt(plinux_opts
, "root_squash", NULL
);
324 if (strcmp(key
, "nosub") == 0)
325 key
= "subtree_check";
327 if (strcmp(key
, "insecure") != 0 && strcmp(key
, "secure") != 0 &&
328 strcmp(key
, "async") != 0 && strcmp(key
, "sync") != 0 &&
329 strcmp(key
, "no_wdelay") != 0 && strcmp(key
, "wdelay") != 0 &&
330 strcmp(key
, "nohide") != 0 && strcmp(key
, "hide") != 0 &&
331 strcmp(key
, "crossmnt") != 0 &&
332 strcmp(key
, "no_subtree_check") != 0 &&
333 strcmp(key
, "subtree_check") != 0 &&
334 strcmp(key
, "insecure_locks") != 0 &&
335 strcmp(key
, "secure_locks") != 0 &&
336 strcmp(key
, "no_auth_nlm") != 0 && strcmp(key
, "auth_nlm") != 0 &&
337 strcmp(key
, "no_acl") != 0 && strcmp(key
, "mountpoint") != 0 &&
338 strcmp(key
, "mp") != 0 && strcmp(key
, "fsuid") != 0 &&
339 strcmp(key
, "refer") != 0 && strcmp(key
, "replicas") != 0 &&
340 strcmp(key
, "root_squash") != 0 &&
341 strcmp(key
, "no_root_squash") != 0 &&
342 strcmp(key
, "all_squash") != 0 &&
343 strcmp(key
, "no_all_squash") != 0 && strcmp(key
, "fsid") != 0 &&
344 strcmp(key
, "anonuid") != 0 && strcmp(key
, "anongid") != 0) {
345 return (SA_SYNTAX_ERR
);
348 (void) add_linux_shareopt(plinux_opts
, key
, value
);
354 * Takes a string containing Solaris share options (e.g. "sync,no_acl") and
355 * converts them to a NULL-terminated array of Linux NFS options.
358 get_linux_shareopts(const char *shareopts
, char **plinux_opts
)
362 assert(plinux_opts
!= NULL
);
366 /* no_subtree_check - Default as of nfs-utils v1.1.0 */
367 (void) add_linux_shareopt(plinux_opts
, "no_subtree_check", NULL
);
369 /* mountpoint - Restrict exports to ZFS mountpoints */
370 (void) add_linux_shareopt(plinux_opts
, "mountpoint", NULL
);
372 error
= foreach_nfs_shareopt(shareopts
, get_linux_shareopts_cb
,
375 if (error
!= SA_OK
) {
384 * This function populates an entry into /etc/exports.d/zfs.exports.
385 * This file is consumed by the linux nfs server so that zfs shares are
386 * automatically exported upon boot or whenever the nfs server restarts.
389 nfs_add_entry(FILE *tmpfile
, const char *sharepath
,
390 const char *host
, const char *security
, const char *access_opts
,
393 const char *linux_opts
= (const char *)pcookie
;
395 if (linux_opts
== NULL
)
398 if (fprintf(tmpfile
, "%s %s(sec=%s,%s,%s)\n", sharepath
,
399 get_linux_hostspec(host
), security
, access_opts
,
401 fprintf(stderr
, "failed to write to temporary file\n");
402 return (SA_SYSTEM_ERR
);
409 * Enables NFS sharing for the specified share.
412 nfs_enable_share_impl(sa_share_impl_t impl_share
, FILE *tmpfile
)
414 char *shareopts
, *linux_opts
;
417 shareopts
= FSINFO(impl_share
, nfs_fstype
)->shareopts
;
418 error
= get_linux_shareopts(shareopts
, &linux_opts
);
422 error
= foreach_nfs_host(impl_share
, tmpfile
, nfs_add_entry
,
429 nfs_enable_share(sa_share_impl_t impl_share
)
431 return (nfs_toggle_share(
432 ZFS_EXPORTS_LOCK
, ZFS_EXPORTS_FILE
, ZFS_EXPORTS_DIR
, impl_share
,
433 nfs_enable_share_impl
));
437 * Disables NFS sharing for the specified share.
440 nfs_disable_share_impl(sa_share_impl_t impl_share
, FILE *tmpfile
)
442 (void) impl_share
, (void) tmpfile
;
447 nfs_disable_share(sa_share_impl_t impl_share
)
449 return (nfs_toggle_share(
450 ZFS_EXPORTS_LOCK
, ZFS_EXPORTS_FILE
, ZFS_EXPORTS_DIR
, impl_share
,
451 nfs_disable_share_impl
));
455 nfs_is_shared(sa_share_impl_t impl_share
)
457 return (nfs_is_shared_impl(ZFS_EXPORTS_FILE
, impl_share
));
461 * Checks whether the specified NFS share options are syntactically correct.
464 nfs_validate_shareopts(const char *shareopts
)
469 error
= get_linux_shareopts(shareopts
, &linux_opts
);
479 nfs_update_shareopts(sa_share_impl_t impl_share
, const char *shareopts
)
481 FSINFO(impl_share
, nfs_fstype
)->shareopts
= (char *)shareopts
;
486 * Clears a share's NFS options. Used by libshare to
487 * clean up shares that are about to be free()'d.
490 nfs_clear_shareopts(sa_share_impl_t impl_share
)
492 FSINFO(impl_share
, nfs_fstype
)->shareopts
= NULL
;
496 nfs_commit_shares(void)
499 (char *)"/usr/sbin/exportfs",
504 return (libzfs_run_process(argv
[0], argv
, 0));
507 static const sa_share_ops_t nfs_shareops
= {
508 .enable_share
= nfs_enable_share
,
509 .disable_share
= nfs_disable_share
,
510 .is_shared
= nfs_is_shared
,
512 .validate_shareopts
= nfs_validate_shareopts
,
513 .update_shareopts
= nfs_update_shareopts
,
514 .clear_shareopts
= nfs_clear_shareopts
,
515 .commit_shares
= nfs_commit_shares
,
519 * Initializes the NFS functionality of libshare.
522 libshare_nfs_init(void)
524 nfs_fstype
= register_fstype("nfs", &nfs_shareops
);