1eda14cbcSMatt Macy /*
2eda14cbcSMatt Macy * CDDL HEADER START
3eda14cbcSMatt Macy *
4eda14cbcSMatt Macy * The contents of this file are subject to the terms of the
5eda14cbcSMatt Macy * Common Development and Distribution License (the "License").
6eda14cbcSMatt Macy * You may not use this file except in compliance with the License.
7eda14cbcSMatt Macy *
8eda14cbcSMatt Macy * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9271171e0SMartin Matuska * or https://opensource.org/licenses/CDDL-1.0.
10eda14cbcSMatt Macy * See the License for the specific language governing permissions
11eda14cbcSMatt Macy * and limitations under the License.
12eda14cbcSMatt Macy *
13eda14cbcSMatt Macy * When distributing Covered Code, include this CDDL HEADER in each
14eda14cbcSMatt Macy * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15eda14cbcSMatt Macy * If applicable, add the following below this CDDL HEADER, with the
16eda14cbcSMatt Macy * fields enclosed by brackets "[]" replaced with your own identifying
17eda14cbcSMatt Macy * information: Portions Copyright [yyyy] [name of copyright owner]
18eda14cbcSMatt Macy *
19eda14cbcSMatt Macy * CDDL HEADER END
20eda14cbcSMatt Macy */
21eda14cbcSMatt Macy
22eda14cbcSMatt Macy /*
23eda14cbcSMatt Macy * Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
24eda14cbcSMatt Macy * Copyright (c) 2011,2012 Turbo Fredriksson <turbo@bayour.com>, based on nfs.c
25eda14cbcSMatt Macy * by Gunnar Beutner
26eda14cbcSMatt Macy * Copyright (c) 2019, 2020 by Delphix. All rights reserved.
27eda14cbcSMatt Macy *
28eda14cbcSMatt Macy * This is an addition to the zfs device driver to add, modify and remove SMB
29eda14cbcSMatt Macy * shares using the 'net share' command that comes with Samba.
30eda14cbcSMatt Macy *
31eda14cbcSMatt Macy * TESTING
32eda14cbcSMatt Macy * Make sure that samba listens to 'localhost' (127.0.0.1) and that the options
33eda14cbcSMatt Macy * 'usershare max shares' and 'usershare owner only' have been reviewed/set
34eda14cbcSMatt Macy * accordingly (see zfs(8) for information).
35eda14cbcSMatt Macy *
36eda14cbcSMatt Macy * Once configuration in samba have been done, test that this
37eda14cbcSMatt Macy * works with the following three commands (in this case, my ZFS
38eda14cbcSMatt Macy * filesystem is called 'share/Test1'):
39eda14cbcSMatt Macy *
40eda14cbcSMatt Macy * (root)# net -U root -S 127.0.0.1 usershare add Test1 /share/Test1 \
41eda14cbcSMatt Macy * "Comment: /share/Test1" "Everyone:F"
42eda14cbcSMatt Macy * (root)# net usershare list | grep -i test
43eda14cbcSMatt Macy * (root)# net -U root -S 127.0.0.1 usershare delete Test1
44eda14cbcSMatt Macy *
45eda14cbcSMatt Macy * The first command will create a user share that gives everyone full access.
46eda14cbcSMatt Macy * To limit the access below that, use normal UNIX commands (chmod, chown etc).
47eda14cbcSMatt Macy */
48eda14cbcSMatt Macy
49eda14cbcSMatt Macy #include <time.h>
50eda14cbcSMatt Macy #include <stdlib.h>
51eda14cbcSMatt Macy #include <stdio.h>
52eda14cbcSMatt Macy #include <string.h>
53eda14cbcSMatt Macy #include <fcntl.h>
54eda14cbcSMatt Macy #include <sys/wait.h>
55eda14cbcSMatt Macy #include <unistd.h>
56eda14cbcSMatt Macy #include <dirent.h>
57eda14cbcSMatt Macy #include <sys/types.h>
58eda14cbcSMatt Macy #include <sys/stat.h>
59eda14cbcSMatt Macy #include <libzfs.h>
60eda14cbcSMatt Macy #include <libshare.h>
61eda14cbcSMatt Macy #include "libshare_impl.h"
62eda14cbcSMatt Macy #include "smb.h"
63eda14cbcSMatt Macy
64eda14cbcSMatt Macy static boolean_t smb_available(void);
65eda14cbcSMatt Macy
66716fd348SMartin Matuska static smb_share_t *smb_shares;
67eda14cbcSMatt Macy static int smb_disable_share(sa_share_impl_t impl_share);
68eda14cbcSMatt Macy static boolean_t smb_is_share_active(sa_share_impl_t impl_share);
69eda14cbcSMatt Macy
70eda14cbcSMatt Macy /*
71eda14cbcSMatt Macy * Retrieve the list of SMB shares.
72eda14cbcSMatt Macy */
73eda14cbcSMatt Macy static int
smb_retrieve_shares(void)74eda14cbcSMatt Macy smb_retrieve_shares(void)
75eda14cbcSMatt Macy {
76eda14cbcSMatt Macy int rc = SA_OK;
77eda14cbcSMatt Macy char file_path[PATH_MAX], line[512], *token, *key, *value;
78eda14cbcSMatt Macy char *dup_value = NULL, *path = NULL, *comment = NULL, *name = NULL;
79eda14cbcSMatt Macy char *guest_ok = NULL;
80eda14cbcSMatt Macy DIR *shares_dir;
81eda14cbcSMatt Macy FILE *share_file_fp = NULL;
82eda14cbcSMatt Macy struct dirent *directory;
83eda14cbcSMatt Macy struct stat eStat;
84eda14cbcSMatt Macy smb_share_t *shares, *new_shares = NULL;
85eda14cbcSMatt Macy
86eda14cbcSMatt Macy /* opendir(), stat() */
87eda14cbcSMatt Macy shares_dir = opendir(SHARE_DIR);
88eda14cbcSMatt Macy if (shares_dir == NULL)
89eda14cbcSMatt Macy return (SA_SYSTEM_ERR);
90eda14cbcSMatt Macy
91eda14cbcSMatt Macy /* Go through the directory, looking for shares */
92eda14cbcSMatt Macy while ((directory = readdir(shares_dir))) {
93*dbd5678dSMartin Matuska int fd;
94*dbd5678dSMartin Matuska
95eda14cbcSMatt Macy if (directory->d_name[0] == '.')
96eda14cbcSMatt Macy continue;
97eda14cbcSMatt Macy
98eda14cbcSMatt Macy snprintf(file_path, sizeof (file_path),
99eda14cbcSMatt Macy "%s/%s", SHARE_DIR, directory->d_name);
100eda14cbcSMatt Macy
101*dbd5678dSMartin Matuska if ((fd = open(file_path, O_RDONLY | O_CLOEXEC)) == -1) {
102eda14cbcSMatt Macy rc = SA_SYSTEM_ERR;
103eda14cbcSMatt Macy goto out;
104eda14cbcSMatt Macy }
105eda14cbcSMatt Macy
106*dbd5678dSMartin Matuska if (fstat(fd, &eStat) == -1) {
107*dbd5678dSMartin Matuska close(fd);
108*dbd5678dSMartin Matuska rc = SA_SYSTEM_ERR;
109*dbd5678dSMartin Matuska goto out;
110*dbd5678dSMartin Matuska }
111eda14cbcSMatt Macy
112*dbd5678dSMartin Matuska if (!S_ISREG(eStat.st_mode)) {
113*dbd5678dSMartin Matuska close(fd);
114*dbd5678dSMartin Matuska continue;
115*dbd5678dSMartin Matuska }
116*dbd5678dSMartin Matuska
117*dbd5678dSMartin Matuska if ((share_file_fp = fdopen(fd, "r")) == NULL) {
118*dbd5678dSMartin Matuska close(fd);
119eda14cbcSMatt Macy rc = SA_SYSTEM_ERR;
120eda14cbcSMatt Macy goto out;
121eda14cbcSMatt Macy }
122eda14cbcSMatt Macy
123eda14cbcSMatt Macy name = strdup(directory->d_name);
124eda14cbcSMatt Macy if (name == NULL) {
125eda14cbcSMatt Macy rc = SA_NO_MEMORY;
126eda14cbcSMatt Macy goto out;
127eda14cbcSMatt Macy }
128eda14cbcSMatt Macy
129eda14cbcSMatt Macy while (fgets(line, sizeof (line), share_file_fp)) {
130eda14cbcSMatt Macy if (line[0] == '#')
131eda14cbcSMatt Macy continue;
132eda14cbcSMatt Macy
133eda14cbcSMatt Macy /* Trim trailing new-line character(s). */
134eda14cbcSMatt Macy while (line[strlen(line) - 1] == '\r' ||
135eda14cbcSMatt Macy line[strlen(line) - 1] == '\n')
136eda14cbcSMatt Macy line[strlen(line) - 1] = '\0';
137eda14cbcSMatt Macy
138eda14cbcSMatt Macy /* Split the line in two, separated by '=' */
139eda14cbcSMatt Macy token = strchr(line, '=');
140eda14cbcSMatt Macy if (token == NULL)
141eda14cbcSMatt Macy continue;
142eda14cbcSMatt Macy
143eda14cbcSMatt Macy key = line;
144eda14cbcSMatt Macy value = token + 1;
145eda14cbcSMatt Macy *token = '\0';
146eda14cbcSMatt Macy
147eda14cbcSMatt Macy dup_value = strdup(value);
148eda14cbcSMatt Macy if (dup_value == NULL) {
149eda14cbcSMatt Macy rc = SA_NO_MEMORY;
150eda14cbcSMatt Macy goto out;
151eda14cbcSMatt Macy }
152eda14cbcSMatt Macy
153eda14cbcSMatt Macy if (strcmp(key, "path") == 0) {
154eda14cbcSMatt Macy free(path);
155eda14cbcSMatt Macy path = dup_value;
156eda14cbcSMatt Macy } else if (strcmp(key, "comment") == 0) {
157eda14cbcSMatt Macy free(comment);
158eda14cbcSMatt Macy comment = dup_value;
159eda14cbcSMatt Macy } else if (strcmp(key, "guest_ok") == 0) {
160eda14cbcSMatt Macy free(guest_ok);
161eda14cbcSMatt Macy guest_ok = dup_value;
162eda14cbcSMatt Macy } else
163eda14cbcSMatt Macy free(dup_value);
164eda14cbcSMatt Macy
165eda14cbcSMatt Macy dup_value = NULL;
166eda14cbcSMatt Macy
167eda14cbcSMatt Macy if (path == NULL || comment == NULL || guest_ok == NULL)
168eda14cbcSMatt Macy continue; /* Incomplete share definition */
169eda14cbcSMatt Macy else {
170eda14cbcSMatt Macy shares = (smb_share_t *)
171eda14cbcSMatt Macy malloc(sizeof (smb_share_t));
172eda14cbcSMatt Macy if (shares == NULL) {
173eda14cbcSMatt Macy rc = SA_NO_MEMORY;
174eda14cbcSMatt Macy goto out;
175eda14cbcSMatt Macy }
176eda14cbcSMatt Macy
177eda14cbcSMatt Macy (void) strlcpy(shares->name, name,
178eda14cbcSMatt Macy sizeof (shares->name));
179eda14cbcSMatt Macy
180eda14cbcSMatt Macy (void) strlcpy(shares->path, path,
181eda14cbcSMatt Macy sizeof (shares->path));
182eda14cbcSMatt Macy
183eda14cbcSMatt Macy (void) strlcpy(shares->comment, comment,
184eda14cbcSMatt Macy sizeof (shares->comment));
185eda14cbcSMatt Macy
186eda14cbcSMatt Macy shares->guest_ok = atoi(guest_ok);
187eda14cbcSMatt Macy
188eda14cbcSMatt Macy shares->next = new_shares;
189eda14cbcSMatt Macy new_shares = shares;
190eda14cbcSMatt Macy
191eda14cbcSMatt Macy free(path);
192eda14cbcSMatt Macy free(comment);
193eda14cbcSMatt Macy free(guest_ok);
194eda14cbcSMatt Macy
195eda14cbcSMatt Macy path = NULL;
196eda14cbcSMatt Macy comment = NULL;
197eda14cbcSMatt Macy guest_ok = NULL;
198eda14cbcSMatt Macy }
199eda14cbcSMatt Macy }
200eda14cbcSMatt Macy
201eda14cbcSMatt Macy out:
202eda14cbcSMatt Macy if (share_file_fp != NULL) {
203eda14cbcSMatt Macy fclose(share_file_fp);
204eda14cbcSMatt Macy share_file_fp = NULL;
205eda14cbcSMatt Macy }
206eda14cbcSMatt Macy
207eda14cbcSMatt Macy free(name);
208eda14cbcSMatt Macy free(path);
209eda14cbcSMatt Macy free(comment);
210eda14cbcSMatt Macy free(guest_ok);
211eda14cbcSMatt Macy
212eda14cbcSMatt Macy name = NULL;
213eda14cbcSMatt Macy path = NULL;
214eda14cbcSMatt Macy comment = NULL;
215eda14cbcSMatt Macy guest_ok = NULL;
216eda14cbcSMatt Macy }
217eda14cbcSMatt Macy closedir(shares_dir);
218eda14cbcSMatt Macy
219eda14cbcSMatt Macy smb_shares = new_shares;
220eda14cbcSMatt Macy
221eda14cbcSMatt Macy return (rc);
222eda14cbcSMatt Macy }
223eda14cbcSMatt Macy
224eda14cbcSMatt Macy /*
225eda14cbcSMatt Macy * Used internally by smb_enable_share to enable sharing for a single host.
226eda14cbcSMatt Macy */
227eda14cbcSMatt Macy static int
smb_enable_share_one(const char * sharename,const char * sharepath)228eda14cbcSMatt Macy smb_enable_share_one(const char *sharename, const char *sharepath)
229eda14cbcSMatt Macy {
230eda14cbcSMatt Macy char name[SMB_NAME_MAX], comment[SMB_COMMENT_MAX];
231eda14cbcSMatt Macy
232eda14cbcSMatt Macy /* Support ZFS share name regexp '[[:alnum:]_-.: ]' */
233eda14cbcSMatt Macy strlcpy(name, sharename, sizeof (name));
234716fd348SMartin Matuska for (char *itr = name; *itr != '\0'; ++itr)
235716fd348SMartin Matuska switch (*itr) {
236eda14cbcSMatt Macy case '/':
237eda14cbcSMatt Macy case '-':
238eda14cbcSMatt Macy case ':':
239eda14cbcSMatt Macy case ' ':
240716fd348SMartin Matuska *itr = '_';
241eda14cbcSMatt Macy }
242eda14cbcSMatt Macy
243eda14cbcSMatt Macy /*
244eda14cbcSMatt Macy * CMD: net -S NET_CMD_ARG_HOST usershare add Test1 /share/Test1 \
245eda14cbcSMatt Macy * "Comment" "Everyone:F"
246eda14cbcSMatt Macy */
247eda14cbcSMatt Macy snprintf(comment, sizeof (comment), "Comment: %s", sharepath);
248eda14cbcSMatt Macy
249716fd348SMartin Matuska char *argv[] = {
250716fd348SMartin Matuska (char *)NET_CMD_PATH,
251716fd348SMartin Matuska (char *)"-S",
252716fd348SMartin Matuska (char *)NET_CMD_ARG_HOST,
253716fd348SMartin Matuska (char *)"usershare",
254716fd348SMartin Matuska (char *)"add",
255716fd348SMartin Matuska name,
256716fd348SMartin Matuska (char *)sharepath,
257716fd348SMartin Matuska comment,
258716fd348SMartin Matuska (char *)"Everyone:F",
259716fd348SMartin Matuska NULL,
260716fd348SMartin Matuska };
261eda14cbcSMatt Macy
262e3aa18adSMartin Matuska if (libzfs_run_process(argv[0], argv, 0) != 0)
263eda14cbcSMatt Macy return (SA_SYSTEM_ERR);
264eda14cbcSMatt Macy
265eda14cbcSMatt Macy /* Reload the share file */
266eda14cbcSMatt Macy (void) smb_retrieve_shares();
267eda14cbcSMatt Macy
268eda14cbcSMatt Macy return (SA_OK);
269eda14cbcSMatt Macy }
270eda14cbcSMatt Macy
271eda14cbcSMatt Macy /*
272eda14cbcSMatt Macy * Enables SMB sharing for the specified share.
273eda14cbcSMatt Macy */
274eda14cbcSMatt Macy static int
smb_enable_share(sa_share_impl_t impl_share)275eda14cbcSMatt Macy smb_enable_share(sa_share_impl_t impl_share)
276eda14cbcSMatt Macy {
277eda14cbcSMatt Macy if (!smb_available())
278eda14cbcSMatt Macy return (SA_SYSTEM_ERR);
279eda14cbcSMatt Macy
280eda14cbcSMatt Macy if (smb_is_share_active(impl_share))
281eda14cbcSMatt Macy smb_disable_share(impl_share);
282eda14cbcSMatt Macy
283716fd348SMartin Matuska if (impl_share->sa_shareopts == NULL) /* on/off */
284eda14cbcSMatt Macy return (SA_SYSTEM_ERR);
285eda14cbcSMatt Macy
286716fd348SMartin Matuska if (strcmp(impl_share->sa_shareopts, "off") == 0)
287eda14cbcSMatt Macy return (SA_OK);
288eda14cbcSMatt Macy
289eda14cbcSMatt Macy /* Magic: Enable (i.e., 'create new') share */
290eda14cbcSMatt Macy return (smb_enable_share_one(impl_share->sa_zfsname,
291eda14cbcSMatt Macy impl_share->sa_mountpoint));
292eda14cbcSMatt Macy }
293eda14cbcSMatt Macy
294eda14cbcSMatt Macy /*
295eda14cbcSMatt Macy * Used internally by smb_disable_share to disable sharing for a single host.
296eda14cbcSMatt Macy */
297eda14cbcSMatt Macy static int
smb_disable_share_one(const char * sharename)298eda14cbcSMatt Macy smb_disable_share_one(const char *sharename)
299eda14cbcSMatt Macy {
300eda14cbcSMatt Macy /* CMD: net -S NET_CMD_ARG_HOST usershare delete Test1 */
301716fd348SMartin Matuska char *argv[] = {
302716fd348SMartin Matuska (char *)NET_CMD_PATH,
303716fd348SMartin Matuska (char *)"-S",
304716fd348SMartin Matuska (char *)NET_CMD_ARG_HOST,
305716fd348SMartin Matuska (char *)"usershare",
306716fd348SMartin Matuska (char *)"delete",
307716fd348SMartin Matuska (char *)sharename,
308716fd348SMartin Matuska NULL,
309716fd348SMartin Matuska };
310eda14cbcSMatt Macy
311e3aa18adSMartin Matuska if (libzfs_run_process(argv[0], argv, 0) != 0)
312eda14cbcSMatt Macy return (SA_SYSTEM_ERR);
313eda14cbcSMatt Macy else
314eda14cbcSMatt Macy return (SA_OK);
315eda14cbcSMatt Macy }
316eda14cbcSMatt Macy
317eda14cbcSMatt Macy /*
318eda14cbcSMatt Macy * Disables SMB sharing for the specified share.
319eda14cbcSMatt Macy */
320eda14cbcSMatt Macy static int
smb_disable_share(sa_share_impl_t impl_share)321eda14cbcSMatt Macy smb_disable_share(sa_share_impl_t impl_share)
322eda14cbcSMatt Macy {
323eda14cbcSMatt Macy if (!smb_available()) {
324eda14cbcSMatt Macy /*
325eda14cbcSMatt Macy * The share can't possibly be active, so nothing
326eda14cbcSMatt Macy * needs to be done to disable it.
327eda14cbcSMatt Macy */
328eda14cbcSMatt Macy return (SA_OK);
329eda14cbcSMatt Macy }
330eda14cbcSMatt Macy
331716fd348SMartin Matuska for (const smb_share_t *i = smb_shares; i != NULL; i = i->next)
332716fd348SMartin Matuska if (strcmp(impl_share->sa_mountpoint, i->path) == 0)
333716fd348SMartin Matuska return (smb_disable_share_one(i->name));
334eda14cbcSMatt Macy
335eda14cbcSMatt Macy return (SA_OK);
336eda14cbcSMatt Macy }
337eda14cbcSMatt Macy
338eda14cbcSMatt Macy /*
339eda14cbcSMatt Macy * Checks whether the specified SMB share options are syntactically correct.
340eda14cbcSMatt Macy */
341eda14cbcSMatt Macy static int
smb_validate_shareopts(const char * shareopts)342eda14cbcSMatt Macy smb_validate_shareopts(const char *shareopts)
343eda14cbcSMatt Macy {
344eda14cbcSMatt Macy /* TODO: Accept 'name' and sec/acl (?) */
345eda14cbcSMatt Macy if ((strcmp(shareopts, "off") == 0) || (strcmp(shareopts, "on") == 0))
346eda14cbcSMatt Macy return (SA_OK);
347eda14cbcSMatt Macy
348eda14cbcSMatt Macy return (SA_SYNTAX_ERR);
349eda14cbcSMatt Macy }
350eda14cbcSMatt Macy
351eda14cbcSMatt Macy /*
352eda14cbcSMatt Macy * Checks whether a share is currently active.
353eda14cbcSMatt Macy */
354eda14cbcSMatt Macy static boolean_t
smb_is_share_active(sa_share_impl_t impl_share)355eda14cbcSMatt Macy smb_is_share_active(sa_share_impl_t impl_share)
356eda14cbcSMatt Macy {
357eda14cbcSMatt Macy if (!smb_available())
358eda14cbcSMatt Macy return (B_FALSE);
359eda14cbcSMatt Macy
360eda14cbcSMatt Macy /* Retrieve the list of (possible) active shares */
361eda14cbcSMatt Macy smb_retrieve_shares();
362eda14cbcSMatt Macy
363716fd348SMartin Matuska for (const smb_share_t *i = smb_shares; i != NULL; i = i->next)
364716fd348SMartin Matuska if (strcmp(impl_share->sa_mountpoint, i->path) == 0)
365eda14cbcSMatt Macy return (B_TRUE);
366eda14cbcSMatt Macy
367eda14cbcSMatt Macy return (B_FALSE);
368eda14cbcSMatt Macy }
369eda14cbcSMatt Macy
370eda14cbcSMatt Macy static int
smb_update_shares(void)371eda14cbcSMatt Macy smb_update_shares(void)
372eda14cbcSMatt Macy {
373eda14cbcSMatt Macy /* Not implemented */
374eda14cbcSMatt Macy return (0);
375eda14cbcSMatt Macy }
376eda14cbcSMatt Macy
377716fd348SMartin Matuska const sa_fstype_t libshare_smb_type = {
378eda14cbcSMatt Macy .enable_share = smb_enable_share,
379eda14cbcSMatt Macy .disable_share = smb_disable_share,
380eda14cbcSMatt Macy .is_shared = smb_is_share_active,
381eda14cbcSMatt Macy
382eda14cbcSMatt Macy .validate_shareopts = smb_validate_shareopts,
383eda14cbcSMatt Macy .commit_shares = smb_update_shares,
384eda14cbcSMatt Macy };
385eda14cbcSMatt Macy
386eda14cbcSMatt Macy /*
387eda14cbcSMatt Macy * Provides a convenient wrapper for determining SMB availability
388eda14cbcSMatt Macy */
389eda14cbcSMatt Macy static boolean_t
smb_available(void)390eda14cbcSMatt Macy smb_available(void)
391eda14cbcSMatt Macy {
392716fd348SMartin Matuska static int avail;
393716fd348SMartin Matuska
394716fd348SMartin Matuska if (!avail) {
395eda14cbcSMatt Macy struct stat statbuf;
396eda14cbcSMatt Macy
397716fd348SMartin Matuska if (access(NET_CMD_PATH, F_OK) != 0 ||
398716fd348SMartin Matuska lstat(SHARE_DIR, &statbuf) != 0 ||
399eda14cbcSMatt Macy !S_ISDIR(statbuf.st_mode))
400716fd348SMartin Matuska avail = -1;
401716fd348SMartin Matuska else
402716fd348SMartin Matuska avail = 1;
403eda14cbcSMatt Macy }
404eda14cbcSMatt Macy
405716fd348SMartin Matuska return (avail == 1);
406eda14cbcSMatt Macy }
407