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,2012 Turbo Fredriksson <turbo@bayour.com>, based on nfs.c
26 * Copyright (c) 2019, 2020 by Delphix. All rights reserved.
28 * This is an addition to the zfs device driver to add, modify and remove SMB
29 * shares using the 'net share' command that comes with Samba.
32 * Make sure that samba listens to 'localhost' (127.0.0.1) and that the options
33 * 'usershare max shares' and 'usershare owner only' have been reviewed/set
34 * accordingly (see zfs(8) for information).
36 * Once configuration in samba have been done, test that this
37 * works with the following three commands (in this case, my ZFS
38 * filesystem is called 'share/Test1'):
40 * (root)# net -U root -S 127.0.0.1 usershare add Test1 /share/Test1 \
41 * "Comment: /share/Test1" "Everyone:F"
42 * (root)# net usershare list | grep -i test
43 * (root)# net -U root -S 127.0.0.1 usershare delete Test1
45 * The first command will create a user share that gives everyone full access.
46 * To limit the access below that, use normal UNIX commands (chmod, chown etc).
57 #include <sys/types.h>
61 #include "libshare_impl.h"
64 static boolean_t
smb_available(void);
66 static smb_share_t
*smb_shares
;
67 static int smb_disable_share(sa_share_impl_t impl_share
);
68 static boolean_t
smb_is_share_active(sa_share_impl_t impl_share
);
71 * Retrieve the list of SMB shares.
74 smb_retrieve_shares(void)
77 char file_path
[PATH_MAX
], line
[512], *token
, *key
, *value
;
78 char *dup_value
= NULL
, *path
= NULL
, *comment
= NULL
, *name
= NULL
;
79 char *guest_ok
= NULL
;
81 FILE *share_file_fp
= NULL
;
82 struct dirent
*directory
;
84 smb_share_t
*shares
, *new_shares
= NULL
;
86 /* opendir(), stat() */
87 shares_dir
= opendir(SHARE_DIR
);
88 if (shares_dir
== NULL
)
89 return (SA_SYSTEM_ERR
);
91 /* Go through the directory, looking for shares */
92 while ((directory
= readdir(shares_dir
))) {
95 if (directory
->d_name
[0] == '.')
98 snprintf(file_path
, sizeof (file_path
),
99 "%s/%s", SHARE_DIR
, directory
->d_name
);
101 if ((fd
= open(file_path
, O_RDONLY
| O_CLOEXEC
)) == -1) {
106 if (fstat(fd
, &eStat
) == -1) {
112 if (!S_ISREG(eStat
.st_mode
)) {
117 if ((share_file_fp
= fdopen(fd
, "r")) == NULL
) {
123 name
= strdup(directory
->d_name
);
129 while (fgets(line
, sizeof (line
), share_file_fp
)) {
133 /* Trim trailing new-line character(s). */
134 while (line
[strlen(line
) - 1] == '\r' ||
135 line
[strlen(line
) - 1] == '\n')
136 line
[strlen(line
) - 1] = '\0';
138 /* Split the line in two, separated by '=' */
139 token
= strchr(line
, '=');
147 dup_value
= strdup(value
);
148 if (dup_value
== NULL
) {
153 if (strcmp(key
, "path") == 0) {
156 } else if (strcmp(key
, "comment") == 0) {
159 } else if (strcmp(key
, "guest_ok") == 0) {
161 guest_ok
= dup_value
;
167 if (path
== NULL
|| comment
== NULL
|| guest_ok
== NULL
)
168 continue; /* Incomplete share definition */
170 shares
= (smb_share_t
*)
171 malloc(sizeof (smb_share_t
));
172 if (shares
== NULL
) {
177 (void) strlcpy(shares
->name
, name
,
178 sizeof (shares
->name
));
180 (void) strlcpy(shares
->path
, path
,
181 sizeof (shares
->path
));
183 (void) strlcpy(shares
->comment
, comment
,
184 sizeof (shares
->comment
));
186 shares
->guest_ok
= atoi(guest_ok
);
188 shares
->next
= new_shares
;
202 if (share_file_fp
!= NULL
) {
203 fclose(share_file_fp
);
204 share_file_fp
= NULL
;
217 closedir(shares_dir
);
219 smb_shares
= new_shares
;
225 * Used internally by smb_enable_share to enable sharing for a single host.
228 smb_enable_share_one(const char *sharename
, const char *sharepath
)
230 char name
[SMB_NAME_MAX
], comment
[SMB_COMMENT_MAX
];
232 /* Support ZFS share name regexp '[[:alnum:]_-.: ]' */
233 strlcpy(name
, sharename
, sizeof (name
));
234 for (char *itr
= name
; *itr
!= '\0'; ++itr
)
244 * CMD: net -S NET_CMD_ARG_HOST usershare add Test1 /share/Test1 \
245 * "Comment" "Everyone:F"
247 snprintf(comment
, sizeof (comment
), "Comment: %s", sharepath
);
250 (char *)NET_CMD_PATH
,
252 (char *)NET_CMD_ARG_HOST
,
258 (char *)"Everyone:F",
262 if (libzfs_run_process(argv
[0], argv
, 0) != 0)
263 return (SA_SYSTEM_ERR
);
265 /* Reload the share file */
266 (void) smb_retrieve_shares();
272 * Enables SMB sharing for the specified share.
275 smb_enable_share(sa_share_impl_t impl_share
)
277 if (!smb_available())
278 return (SA_SYSTEM_ERR
);
280 if (smb_is_share_active(impl_share
))
281 smb_disable_share(impl_share
);
283 if (impl_share
->sa_shareopts
== NULL
) /* on/off */
284 return (SA_SYSTEM_ERR
);
286 if (strcmp(impl_share
->sa_shareopts
, "off") == 0)
289 /* Magic: Enable (i.e., 'create new') share */
290 return (smb_enable_share_one(impl_share
->sa_zfsname
,
291 impl_share
->sa_mountpoint
));
295 * Used internally by smb_disable_share to disable sharing for a single host.
298 smb_disable_share_one(const char *sharename
)
300 /* CMD: net -S NET_CMD_ARG_HOST usershare delete Test1 */
302 (char *)NET_CMD_PATH
,
304 (char *)NET_CMD_ARG_HOST
,
311 if (libzfs_run_process(argv
[0], argv
, 0) != 0)
312 return (SA_SYSTEM_ERR
);
318 * Disables SMB sharing for the specified share.
321 smb_disable_share(sa_share_impl_t impl_share
)
323 if (!smb_available()) {
325 * The share can't possibly be active, so nothing
326 * needs to be done to disable it.
331 for (const smb_share_t
*i
= smb_shares
; i
!= NULL
; i
= i
->next
)
332 if (strcmp(impl_share
->sa_mountpoint
, i
->path
) == 0)
333 return (smb_disable_share_one(i
->name
));
339 * Checks whether the specified SMB share options are syntactically correct.
342 smb_validate_shareopts(const char *shareopts
)
344 /* TODO: Accept 'name' and sec/acl (?) */
345 if ((strcmp(shareopts
, "off") == 0) || (strcmp(shareopts
, "on") == 0))
348 return (SA_SYNTAX_ERR
);
352 * Checks whether a share is currently active.
355 smb_is_share_active(sa_share_impl_t impl_share
)
357 if (!smb_available())
360 /* Retrieve the list of (possible) active shares */
361 smb_retrieve_shares();
363 for (const smb_share_t
*i
= smb_shares
; i
!= NULL
; i
= i
->next
)
364 if (strcmp(impl_share
->sa_mountpoint
, i
->path
) == 0)
371 smb_update_shares(void)
373 /* Not implemented */
377 const sa_fstype_t libshare_smb_type
= {
378 .enable_share
= smb_enable_share
,
379 .disable_share
= smb_disable_share
,
380 .is_shared
= smb_is_share_active
,
382 .validate_shareopts
= smb_validate_shareopts
,
383 .commit_shares
= smb_update_shares
,
387 * Provides a convenient wrapper for determining SMB availability
397 if (access(NET_CMD_PATH
, F_OK
) != 0 ||
398 lstat(SHARE_DIR
, &statbuf
) != 0 ||
399 !S_ISDIR(statbuf
.st_mode
))