]>
Commit | Line | Data |
---|---|---|
645fb9cc TF |
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 | * | |
27 | * This is an addition to the zfs device driver to add, modify and remove SMB | |
28 | * shares using the 'net share' command that comes with Samba. | |
a08ee875 | 29 | * |
645fb9cc TF |
30 | * TESTING |
31 | * Make sure that samba listens to 'localhost' (127.0.0.1) and that the options | |
32 | * 'usershare max shares' and 'usershare owner only' have been rewied/set | |
33 | * accordingly (see zfs(8) for information). | |
34 | * | |
35 | * Once configuration in samba have been done, test that this | |
36 | * works with the following three commands (in this case, my ZFS | |
37 | * filesystem is called 'share/Test1'): | |
38 | * | |
39 | * (root)# net -U root -S 127.0.0.1 usershare add Test1 /share/Test1 \ | |
40 | * "Comment: /share/Test1" "Everyone:F" | |
41 | * (root)# net usershare list | grep -i test | |
42 | * (root)# net -U root -S 127.0.0.1 usershare delete Test1 | |
43 | * | |
44 | * The first command will create a user share that gives everyone full access. | |
45 | * To limit the access below that, use normal UNIX commands (chmod, chown etc). | |
46 | */ | |
47 | ||
48 | #include <time.h> | |
49 | #include <stdlib.h> | |
50 | #include <stdio.h> | |
51 | #include <strings.h> | |
52 | #include <fcntl.h> | |
53 | #include <sys/wait.h> | |
54 | #include <unistd.h> | |
55 | #include <dirent.h> | |
56 | #include <sys/types.h> | |
57 | #include <sys/stat.h> | |
58 | #include <libzfs.h> | |
59 | #include <libshare.h> | |
60 | #include "libshare_impl.h" | |
61 | #include "smb.h" | |
62 | ||
63 | static boolean_t smb_available(void); | |
64 | ||
65 | static sa_fstype_t *smb_fstype; | |
66 | ||
a08ee875 | 67 | /* |
645fb9cc TF |
68 | * Retrieve the list of SMB shares. |
69 | */ | |
70 | static int | |
71 | smb_retrieve_shares(void) | |
72 | { | |
73 | int rc = SA_OK; | |
74 | char file_path[PATH_MAX], line[512], *token, *key, *value; | |
cae5b340 | 75 | char *dup_value = NULL, *path = NULL, *comment = NULL, *name = NULL; |
645fb9cc TF |
76 | char *guest_ok = NULL; |
77 | DIR *shares_dir; | |
78 | FILE *share_file_fp = NULL; | |
79 | struct dirent *directory; | |
80 | struct stat eStat; | |
81 | smb_share_t *shares, *new_shares = NULL; | |
82 | ||
83 | /* opendir(), stat() */ | |
84 | shares_dir = opendir(SHARE_DIR); | |
85 | if (shares_dir == NULL) | |
a08ee875 | 86 | return (SA_SYSTEM_ERR); |
645fb9cc TF |
87 | |
88 | /* Go through the directory, looking for shares */ | |
89 | while ((directory = readdir(shares_dir))) { | |
90 | if (directory->d_name[0] == '.') | |
91 | continue; | |
92 | ||
93 | snprintf(file_path, sizeof (file_path), | |
a08ee875 | 94 | "%s/%s", SHARE_DIR, directory->d_name); |
645fb9cc TF |
95 | |
96 | if (stat(file_path, &eStat) == -1) { | |
97 | rc = SA_SYSTEM_ERR; | |
98 | goto out; | |
99 | } | |
100 | ||
101 | if (!S_ISREG(eStat.st_mode)) | |
102 | continue; | |
103 | ||
104 | if ((share_file_fp = fopen(file_path, "r")) == NULL) { | |
105 | rc = SA_SYSTEM_ERR; | |
106 | goto out; | |
107 | } | |
108 | ||
109 | name = strdup(directory->d_name); | |
110 | if (name == NULL) { | |
a08ee875 LG |
111 | rc = SA_NO_MEMORY; |
112 | goto out; | |
645fb9cc TF |
113 | } |
114 | ||
a08ee875 | 115 | while (fgets(line, sizeof (line), share_file_fp)) { |
645fb9cc TF |
116 | if (line[0] == '#') |
117 | continue; | |
118 | ||
119 | /* Trim trailing new-line character(s). */ | |
120 | while (line[strlen(line) - 1] == '\r' || | |
a08ee875 | 121 | line[strlen(line) - 1] == '\n') |
645fb9cc TF |
122 | line[strlen(line) - 1] = '\0'; |
123 | ||
124 | /* Split the line in two, separated by '=' */ | |
125 | token = strchr(line, '='); | |
126 | if (token == NULL) | |
127 | continue; | |
128 | ||
129 | key = line; | |
130 | value = token + 1; | |
131 | *token = '\0'; | |
132 | ||
133 | dup_value = strdup(value); | |
134 | if (dup_value == NULL) { | |
135 | rc = SA_NO_MEMORY; | |
136 | goto out; | |
137 | } | |
138 | ||
cae5b340 AX |
139 | if (strcmp(key, "path") == 0) { |
140 | free(path); | |
645fb9cc | 141 | path = dup_value; |
cae5b340 AX |
142 | } else if (strcmp(key, "comment") == 0) { |
143 | free(comment); | |
645fb9cc | 144 | comment = dup_value; |
cae5b340 AX |
145 | } else if (strcmp(key, "guest_ok") == 0) { |
146 | free(guest_ok); | |
645fb9cc | 147 | guest_ok = dup_value; |
cae5b340 AX |
148 | } else |
149 | free(dup_value); | |
150 | ||
151 | dup_value = NULL; | |
645fb9cc TF |
152 | |
153 | if (path == NULL || comment == NULL || guest_ok == NULL) | |
154 | continue; /* Incomplete share definition */ | |
155 | else { | |
156 | shares = (smb_share_t *) | |
cae5b340 | 157 | malloc(sizeof (smb_share_t)); |
645fb9cc TF |
158 | if (shares == NULL) { |
159 | rc = SA_NO_MEMORY; | |
160 | goto out; | |
161 | } | |
162 | ||
cae5b340 AX |
163 | (void) strlcpy(shares->name, name, |
164 | sizeof (shares->name)); | |
645fb9cc | 165 | |
cae5b340 | 166 | (void) strlcpy(shares->path, path, |
a08ee875 | 167 | sizeof (shares->path)); |
645fb9cc | 168 | |
cae5b340 | 169 | (void) strlcpy(shares->comment, comment, |
a08ee875 | 170 | sizeof (shares->comment)); |
645fb9cc TF |
171 | |
172 | shares->guest_ok = atoi(guest_ok); | |
173 | ||
174 | shares->next = new_shares; | |
175 | new_shares = shares; | |
176 | ||
cae5b340 AX |
177 | free(path); |
178 | free(comment); | |
179 | free(guest_ok); | |
180 | ||
a08ee875 LG |
181 | path = NULL; |
182 | comment = NULL; | |
645fb9cc TF |
183 | guest_ok = NULL; |
184 | } | |
185 | } | |
186 | ||
187 | out: | |
cae5b340 | 188 | if (share_file_fp != NULL) { |
645fb9cc | 189 | fclose(share_file_fp); |
cae5b340 AX |
190 | share_file_fp = NULL; |
191 | } | |
645fb9cc TF |
192 | |
193 | free(name); | |
194 | free(path); | |
195 | free(comment); | |
196 | free(guest_ok); | |
cae5b340 AX |
197 | |
198 | name = NULL; | |
199 | path = NULL; | |
200 | comment = NULL; | |
201 | guest_ok = NULL; | |
645fb9cc TF |
202 | } |
203 | closedir(shares_dir); | |
204 | ||
205 | smb_shares = new_shares; | |
206 | ||
a08ee875 | 207 | return (rc); |
645fb9cc TF |
208 | } |
209 | ||
a08ee875 | 210 | /* |
645fb9cc TF |
211 | * Used internally by smb_enable_share to enable sharing for a single host. |
212 | */ | |
213 | static int | |
214 | smb_enable_share_one(const char *sharename, const char *sharepath) | |
215 | { | |
216 | char *argv[10], *pos; | |
217 | char name[SMB_NAME_MAX], comment[SMB_COMMENT_MAX]; | |
218 | int rc; | |
219 | ||
220 | /* Support ZFS share name regexp '[[:alnum:]_-.: ]' */ | |
a08ee875 LG |
221 | strncpy(name, sharename, sizeof (name)); |
222 | name [sizeof (name)-1] = '\0'; | |
645fb9cc TF |
223 | |
224 | pos = name; | |
225 | while (*pos != '\0') { | |
226 | switch (*pos) { | |
227 | case '/': | |
228 | case '-': | |
229 | case ':': | |
230 | case ' ': | |
231 | *pos = '_'; | |
232 | } | |
233 | ||
234 | ++pos; | |
235 | } | |
236 | ||
a08ee875 LG |
237 | /* |
238 | * CMD: net -S NET_CMD_ARG_HOST usershare add Test1 /share/Test1 \ | |
239 | * "Comment" "Everyone:F" | |
240 | */ | |
241 | snprintf(comment, sizeof (comment), "Comment: %s", sharepath); | |
242 | ||
243 | argv[0] = NET_CMD_PATH; | |
244 | argv[1] = (char *)"-S"; | |
245 | argv[2] = NET_CMD_ARG_HOST; | |
246 | argv[3] = (char *)"usershare"; | |
247 | argv[4] = (char *)"add"; | |
248 | argv[5] = (char *)name; | |
249 | argv[6] = (char *)sharepath; | |
250 | argv[7] = (char *)comment; | |
645fb9cc TF |
251 | argv[8] = "Everyone:F"; |
252 | argv[9] = NULL; | |
253 | ||
254 | rc = libzfs_run_process(argv[0], argv, 0); | |
255 | if (rc < 0) | |
a08ee875 | 256 | return (SA_SYSTEM_ERR); |
645fb9cc TF |
257 | |
258 | /* Reload the share file */ | |
259 | (void) smb_retrieve_shares(); | |
260 | ||
a08ee875 | 261 | return (SA_OK); |
645fb9cc TF |
262 | } |
263 | ||
a08ee875 | 264 | /* |
645fb9cc TF |
265 | * Enables SMB sharing for the specified share. |
266 | */ | |
267 | static int | |
268 | smb_enable_share(sa_share_impl_t impl_share) | |
269 | { | |
270 | char *shareopts; | |
271 | ||
272 | if (!smb_available()) | |
a08ee875 | 273 | return (SA_SYSTEM_ERR); |
645fb9cc TF |
274 | |
275 | shareopts = FSINFO(impl_share, smb_fstype)->shareopts; | |
276 | if (shareopts == NULL) /* on/off */ | |
a08ee875 | 277 | return (SA_SYSTEM_ERR); |
645fb9cc TF |
278 | |
279 | if (strcmp(shareopts, "off") == 0) | |
a08ee875 | 280 | return (SA_OK); |
645fb9cc TF |
281 | |
282 | /* Magic: Enable (i.e., 'create new') share */ | |
a08ee875 LG |
283 | return (smb_enable_share_one(impl_share->dataset, |
284 | impl_share->sharepath)); | |
645fb9cc TF |
285 | } |
286 | ||
a08ee875 | 287 | /* |
645fb9cc TF |
288 | * Used internally by smb_disable_share to disable sharing for a single host. |
289 | */ | |
290 | static int | |
291 | smb_disable_share_one(const char *sharename) | |
292 | { | |
293 | int rc; | |
294 | char *argv[7]; | |
295 | ||
296 | /* CMD: net -S NET_CMD_ARG_HOST usershare delete Test1 */ | |
297 | argv[0] = NET_CMD_PATH; | |
a08ee875 | 298 | argv[1] = (char *)"-S"; |
645fb9cc | 299 | argv[2] = NET_CMD_ARG_HOST; |
a08ee875 LG |
300 | argv[3] = (char *)"usershare"; |
301 | argv[4] = (char *)"delete"; | |
645fb9cc TF |
302 | argv[5] = strdup(sharename); |
303 | argv[6] = NULL; | |
304 | ||
305 | rc = libzfs_run_process(argv[0], argv, 0); | |
306 | if (rc < 0) | |
a08ee875 | 307 | return (SA_SYSTEM_ERR); |
645fb9cc | 308 | else |
a08ee875 | 309 | return (SA_OK); |
645fb9cc TF |
310 | } |
311 | ||
a08ee875 | 312 | /* |
645fb9cc TF |
313 | * Disables SMB sharing for the specified share. |
314 | */ | |
315 | static int | |
316 | smb_disable_share(sa_share_impl_t impl_share) | |
317 | { | |
318 | smb_share_t *shares = smb_shares; | |
319 | ||
320 | if (!smb_available()) { | |
321 | /* | |
322 | * The share can't possibly be active, so nothing | |
323 | * needs to be done to disable it. | |
324 | */ | |
a08ee875 | 325 | return (SA_OK); |
645fb9cc TF |
326 | } |
327 | ||
328 | while (shares != NULL) { | |
329 | if (strcmp(impl_share->sharepath, shares->path) == 0) | |
a08ee875 | 330 | return (smb_disable_share_one(shares->name)); |
645fb9cc TF |
331 | |
332 | shares = shares->next; | |
333 | } | |
334 | ||
a08ee875 | 335 | return (SA_OK); |
645fb9cc TF |
336 | } |
337 | ||
a08ee875 | 338 | /* |
645fb9cc TF |
339 | * Checks whether the specified SMB share options are syntactically correct. |
340 | */ | |
341 | static int | |
342 | smb_validate_shareopts(const char *shareopts) | |
343 | { | |
344 | /* TODO: Accept 'name' and sec/acl (?) */ | |
345 | if ((strcmp(shareopts, "off") == 0) || (strcmp(shareopts, "on") == 0)) | |
a08ee875 | 346 | return (SA_OK); |
645fb9cc | 347 | |
a08ee875 | 348 | return (SA_SYNTAX_ERR); |
645fb9cc TF |
349 | } |
350 | ||
a08ee875 | 351 | /* |
645fb9cc TF |
352 | * Checks whether a share is currently active. |
353 | */ | |
354 | static boolean_t | |
355 | smb_is_share_active(sa_share_impl_t impl_share) | |
356 | { | |
cae5b340 AX |
357 | smb_share_t *iter = smb_shares; |
358 | ||
645fb9cc | 359 | if (!smb_available()) |
a08ee875 | 360 | return (B_FALSE); |
645fb9cc TF |
361 | |
362 | /* Retrieve the list of (possible) active shares */ | |
363 | smb_retrieve_shares(); | |
364 | ||
cae5b340 AX |
365 | while (iter != NULL) { |
366 | if (strcmp(impl_share->sharepath, iter->path) == 0) | |
a08ee875 | 367 | return (B_TRUE); |
645fb9cc | 368 | |
cae5b340 | 369 | iter = iter->next; |
645fb9cc TF |
370 | } |
371 | ||
a08ee875 | 372 | return (B_FALSE); |
645fb9cc TF |
373 | } |
374 | ||
a08ee875 | 375 | /* |
645fb9cc TF |
376 | * Called to update a share's options. A share's options might be out of |
377 | * date if the share was loaded from disk and the "sharesmb" dataset | |
378 | * property has changed in the meantime. This function also takes care | |
379 | * of re-enabling the share if necessary. | |
380 | */ | |
381 | static int | |
382 | smb_update_shareopts(sa_share_impl_t impl_share, const char *resource, | |
383 | const char *shareopts) | |
384 | { | |
385 | char *shareopts_dup; | |
386 | boolean_t needs_reshare = B_FALSE; | |
387 | char *old_shareopts; | |
388 | ||
a08ee875 LG |
389 | if (!impl_share) |
390 | return (SA_SYSTEM_ERR); | |
645fb9cc TF |
391 | |
392 | FSINFO(impl_share, smb_fstype)->active = | |
393 | smb_is_share_active(impl_share); | |
394 | ||
395 | old_shareopts = FSINFO(impl_share, smb_fstype)->shareopts; | |
396 | ||
397 | if (FSINFO(impl_share, smb_fstype)->active && old_shareopts != NULL && | |
cae5b340 | 398 | strcmp(old_shareopts, shareopts) != 0) { |
645fb9cc TF |
399 | needs_reshare = B_TRUE; |
400 | smb_disable_share(impl_share); | |
401 | } | |
402 | ||
403 | shareopts_dup = strdup(shareopts); | |
404 | ||
405 | if (shareopts_dup == NULL) | |
a08ee875 | 406 | return (SA_NO_MEMORY); |
645fb9cc TF |
407 | |
408 | if (old_shareopts != NULL) | |
409 | free(old_shareopts); | |
410 | ||
411 | FSINFO(impl_share, smb_fstype)->shareopts = shareopts_dup; | |
412 | ||
413 | if (needs_reshare) | |
414 | smb_enable_share(impl_share); | |
415 | ||
a08ee875 | 416 | return (SA_OK); |
645fb9cc TF |
417 | } |
418 | ||
a08ee875 | 419 | /* |
645fb9cc TF |
420 | * Clears a share's SMB options. Used by libshare to |
421 | * clean up shares that are about to be free()'d. | |
422 | */ | |
423 | static void | |
424 | smb_clear_shareopts(sa_share_impl_t impl_share) | |
425 | { | |
426 | free(FSINFO(impl_share, smb_fstype)->shareopts); | |
427 | FSINFO(impl_share, smb_fstype)->shareopts = NULL; | |
428 | } | |
429 | ||
430 | static const sa_share_ops_t smb_shareops = { | |
431 | .enable_share = smb_enable_share, | |
432 | .disable_share = smb_disable_share, | |
433 | ||
434 | .validate_shareopts = smb_validate_shareopts, | |
435 | .update_shareopts = smb_update_shareopts, | |
436 | .clear_shareopts = smb_clear_shareopts, | |
437 | }; | |
438 | ||
439 | /* | |
440 | * Provides a convenient wrapper for determining SMB availability | |
441 | */ | |
442 | static boolean_t | |
443 | smb_available(void) | |
444 | { | |
c06d4368 AX |
445 | struct stat statbuf; |
446 | ||
447 | if (lstat(SHARE_DIR, &statbuf) != 0 || | |
448 | !S_ISDIR(statbuf.st_mode)) | |
a08ee875 | 449 | return (B_FALSE); |
c06d4368 AX |
450 | |
451 | if (access(NET_CMD_PATH, F_OK) != 0) | |
a08ee875 | 452 | return (B_FALSE); |
c06d4368 | 453 | |
a08ee875 | 454 | return (B_TRUE); |
645fb9cc TF |
455 | } |
456 | ||
a08ee875 | 457 | /* |
645fb9cc TF |
458 | * Initializes the SMB functionality of libshare. |
459 | */ | |
460 | void | |
461 | libshare_smb_init(void) | |
462 | { | |
463 | smb_fstype = register_fstype("smb", &smb_shareops); | |
464 | } |