]> git.proxmox.com Git - mirror_zfs.git/blob - lib/libshare/os/linux/smb.c
libshare/smb: cleanup
[mirror_zfs.git] / lib / libshare / os / linux / smb.c
1 /*
2 * CDDL HEADER START
3 *
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.
7 *
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.
12 *
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]
18 *
19 * CDDL HEADER END
20 */
21
22 /*
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
25 * by Gunnar Beutner
26 * Copyright (c) 2019, 2020 by Delphix. All rights reserved.
27 *
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.
30 *
31 * TESTING
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).
35 *
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'):
39 *
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
44 *
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).
47 */
48
49 #include <time.h>
50 #include <stdlib.h>
51 #include <stdio.h>
52 #include <string.h>
53 #include <fcntl.h>
54 #include <sys/wait.h>
55 #include <unistd.h>
56 #include <dirent.h>
57 #include <sys/types.h>
58 #include <sys/stat.h>
59 #include <libzfs.h>
60 #include <libshare.h>
61 #include "libshare_impl.h"
62 #include "smb.h"
63
64 static boolean_t smb_available(void);
65
66 static sa_fstype_t *smb_fstype;
67
68 static smb_share_t *smb_shares;
69 static int smb_disable_share(sa_share_impl_t impl_share);
70 static boolean_t smb_is_share_active(sa_share_impl_t impl_share);
71
72 /*
73 * Retrieve the list of SMB shares.
74 */
75 static int
76 smb_retrieve_shares(void)
77 {
78 int rc = SA_OK;
79 char file_path[PATH_MAX], line[512], *token, *key, *value;
80 char *dup_value = NULL, *path = NULL, *comment = NULL, *name = NULL;
81 char *guest_ok = NULL;
82 DIR *shares_dir;
83 FILE *share_file_fp = NULL;
84 struct dirent *directory;
85 struct stat eStat;
86 smb_share_t *shares, *new_shares = NULL;
87
88 /* opendir(), stat() */
89 shares_dir = opendir(SHARE_DIR);
90 if (shares_dir == NULL)
91 return (SA_SYSTEM_ERR);
92
93 /* Go through the directory, looking for shares */
94 while ((directory = readdir(shares_dir))) {
95 if (directory->d_name[0] == '.')
96 continue;
97
98 snprintf(file_path, sizeof (file_path),
99 "%s/%s", SHARE_DIR, directory->d_name);
100
101 if (stat(file_path, &eStat) == -1) {
102 rc = SA_SYSTEM_ERR;
103 goto out;
104 }
105
106 if (!S_ISREG(eStat.st_mode))
107 continue;
108
109 if ((share_file_fp = fopen(file_path, "re")) == NULL) {
110 rc = SA_SYSTEM_ERR;
111 goto out;
112 }
113
114 name = strdup(directory->d_name);
115 if (name == NULL) {
116 rc = SA_NO_MEMORY;
117 goto out;
118 }
119
120 while (fgets(line, sizeof (line), share_file_fp)) {
121 if (line[0] == '#')
122 continue;
123
124 /* Trim trailing new-line character(s). */
125 while (line[strlen(line) - 1] == '\r' ||
126 line[strlen(line) - 1] == '\n')
127 line[strlen(line) - 1] = '\0';
128
129 /* Split the line in two, separated by '=' */
130 token = strchr(line, '=');
131 if (token == NULL)
132 continue;
133
134 key = line;
135 value = token + 1;
136 *token = '\0';
137
138 dup_value = strdup(value);
139 if (dup_value == NULL) {
140 rc = SA_NO_MEMORY;
141 goto out;
142 }
143
144 if (strcmp(key, "path") == 0) {
145 free(path);
146 path = dup_value;
147 } else if (strcmp(key, "comment") == 0) {
148 free(comment);
149 comment = dup_value;
150 } else if (strcmp(key, "guest_ok") == 0) {
151 free(guest_ok);
152 guest_ok = dup_value;
153 } else
154 free(dup_value);
155
156 dup_value = NULL;
157
158 if (path == NULL || comment == NULL || guest_ok == NULL)
159 continue; /* Incomplete share definition */
160 else {
161 shares = (smb_share_t *)
162 malloc(sizeof (smb_share_t));
163 if (shares == NULL) {
164 rc = SA_NO_MEMORY;
165 goto out;
166 }
167
168 (void) strlcpy(shares->name, name,
169 sizeof (shares->name));
170
171 (void) strlcpy(shares->path, path,
172 sizeof (shares->path));
173
174 (void) strlcpy(shares->comment, comment,
175 sizeof (shares->comment));
176
177 shares->guest_ok = atoi(guest_ok);
178
179 shares->next = new_shares;
180 new_shares = shares;
181
182 free(path);
183 free(comment);
184 free(guest_ok);
185
186 path = NULL;
187 comment = NULL;
188 guest_ok = NULL;
189 }
190 }
191
192 out:
193 if (share_file_fp != NULL) {
194 fclose(share_file_fp);
195 share_file_fp = NULL;
196 }
197
198 free(name);
199 free(path);
200 free(comment);
201 free(guest_ok);
202
203 name = NULL;
204 path = NULL;
205 comment = NULL;
206 guest_ok = NULL;
207 }
208 closedir(shares_dir);
209
210 smb_shares = new_shares;
211
212 return (rc);
213 }
214
215 /*
216 * Used internally by smb_enable_share to enable sharing for a single host.
217 */
218 static int
219 smb_enable_share_one(const char *sharename, const char *sharepath)
220 {
221 char name[SMB_NAME_MAX], comment[SMB_COMMENT_MAX];
222
223 /* Support ZFS share name regexp '[[:alnum:]_-.: ]' */
224 strlcpy(name, sharename, sizeof (name));
225 for (char *itr = name; *itr != '\0'; ++itr)
226 switch (*itr) {
227 case '/':
228 case '-':
229 case ':':
230 case ' ':
231 *itr = '_';
232 }
233
234 /*
235 * CMD: net -S NET_CMD_ARG_HOST usershare add Test1 /share/Test1 \
236 * "Comment" "Everyone:F"
237 */
238 snprintf(comment, sizeof (comment), "Comment: %s", sharepath);
239
240 char *argv[] = {
241 (char *)NET_CMD_PATH,
242 (char *)"-S",
243 (char *)NET_CMD_ARG_HOST,
244 (char *)"usershare",
245 (char *)"add",
246 name,
247 (char *)sharepath,
248 comment,
249 (char *)"Everyone:F",
250 NULL,
251 };
252
253 if (libzfs_run_process(argv[0], argv, 0) < 0)
254 return (SA_SYSTEM_ERR);
255
256 /* Reload the share file */
257 (void) smb_retrieve_shares();
258
259 return (SA_OK);
260 }
261
262 /*
263 * Enables SMB sharing for the specified share.
264 */
265 static int
266 smb_enable_share(sa_share_impl_t impl_share)
267 {
268 char *shareopts;
269
270 if (!smb_available())
271 return (SA_SYSTEM_ERR);
272
273 if (smb_is_share_active(impl_share))
274 smb_disable_share(impl_share);
275
276 shareopts = FSINFO(impl_share, smb_fstype)->shareopts;
277 if (shareopts == NULL) /* on/off */
278 return (SA_SYSTEM_ERR);
279
280 if (strcmp(shareopts, "off") == 0)
281 return (SA_OK);
282
283 /* Magic: Enable (i.e., 'create new') share */
284 return (smb_enable_share_one(impl_share->sa_zfsname,
285 impl_share->sa_mountpoint));
286 }
287
288 /*
289 * Used internally by smb_disable_share to disable sharing for a single host.
290 */
291 static int
292 smb_disable_share_one(const char *sharename)
293 {
294 /* CMD: net -S NET_CMD_ARG_HOST usershare delete Test1 */
295 char *argv[] = {
296 (char *)NET_CMD_PATH,
297 (char *)"-S",
298 (char *)NET_CMD_ARG_HOST,
299 (char *)"usershare",
300 (char *)"delete",
301 (char *)sharename,
302 NULL,
303 };
304
305 if (libzfs_run_process(argv[0], argv, 0) < 0)
306 return (SA_SYSTEM_ERR);
307 else
308 return (SA_OK);
309 }
310
311 /*
312 * Disables SMB sharing for the specified share.
313 */
314 static int
315 smb_disable_share(sa_share_impl_t impl_share)
316 {
317 if (!smb_available()) {
318 /*
319 * The share can't possibly be active, so nothing
320 * needs to be done to disable it.
321 */
322 return (SA_OK);
323 }
324
325 for (const smb_share_t *i = smb_shares; i != NULL; i = i->next)
326 if (strcmp(impl_share->sa_mountpoint, i->path) == 0)
327 return (smb_disable_share_one(i->name));
328
329 return (SA_OK);
330 }
331
332 /*
333 * Checks whether the specified SMB share options are syntactically correct.
334 */
335 static int
336 smb_validate_shareopts(const char *shareopts)
337 {
338 /* TODO: Accept 'name' and sec/acl (?) */
339 if ((strcmp(shareopts, "off") == 0) || (strcmp(shareopts, "on") == 0))
340 return (SA_OK);
341
342 return (SA_SYNTAX_ERR);
343 }
344
345 /*
346 * Checks whether a share is currently active.
347 */
348 static boolean_t
349 smb_is_share_active(sa_share_impl_t impl_share)
350 {
351 if (!smb_available())
352 return (B_FALSE);
353
354 /* Retrieve the list of (possible) active shares */
355 smb_retrieve_shares();
356
357 for (const smb_share_t *i = smb_shares; i != NULL; i = i->next)
358 if (strcmp(impl_share->sa_mountpoint, i->path) == 0)
359 return (B_TRUE);
360
361 return (B_FALSE);
362 }
363
364 /*
365 * Called to update a share's options. A share's options might be out of
366 * date if the share was loaded from disk and the "sharesmb" dataset
367 * property has changed in the meantime. This function also takes care
368 * of re-enabling the share if necessary.
369 */
370 static int
371 smb_update_shareopts(sa_share_impl_t impl_share, const char *shareopts)
372 {
373 if (!impl_share)
374 return (SA_SYSTEM_ERR);
375
376 FSINFO(impl_share, smb_fstype)->shareopts = (char *)shareopts;
377 return (SA_OK);
378 }
379
380 static int
381 smb_update_shares(void)
382 {
383 /* Not implemented */
384 return (0);
385 }
386
387 /*
388 * Clears a share's SMB options. Used by libshare to
389 * clean up shares that are about to be free()'d.
390 */
391 static void
392 smb_clear_shareopts(sa_share_impl_t impl_share)
393 {
394 FSINFO(impl_share, smb_fstype)->shareopts = NULL;
395 }
396
397 static const sa_share_ops_t smb_shareops = {
398 .enable_share = smb_enable_share,
399 .disable_share = smb_disable_share,
400 .is_shared = smb_is_share_active,
401
402 .validate_shareopts = smb_validate_shareopts,
403 .update_shareopts = smb_update_shareopts,
404 .clear_shareopts = smb_clear_shareopts,
405 .commit_shares = smb_update_shares,
406 };
407
408 /*
409 * Provides a convenient wrapper for determining SMB availability
410 */
411 static boolean_t
412 smb_available(void)
413 {
414 struct stat statbuf;
415
416 if (lstat(SHARE_DIR, &statbuf) != 0 ||
417 !S_ISDIR(statbuf.st_mode))
418 return (B_FALSE);
419
420 if (access(NET_CMD_PATH, F_OK) != 0)
421 return (B_FALSE);
422
423 return (B_TRUE);
424 }
425
426 /*
427 * Initializes the SMB functionality of libshare.
428 */
429 void
430 libshare_smb_init(void)
431 {
432 smb_fstype = register_fstype("smb", &smb_shareops);
433 }