]>
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. | |
d1d7e268 | 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 | ||
d1d7e268 | 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; | |
75 | char *dup_value, *path = NULL, *comment = NULL, *name = NULL; | |
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) | |
d1d7e268 | 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), | |
d1d7e268 | 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) { | |
d1d7e268 MK |
111 | rc = SA_NO_MEMORY; |
112 | goto out; | |
645fb9cc TF |
113 | } |
114 | ||
d1d7e268 | 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' || | |
d1d7e268 | 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 | ||
139 | if (strcmp(key, "path") == 0) | |
140 | path = dup_value; | |
141 | if (strcmp(key, "comment") == 0) | |
142 | comment = dup_value; | |
143 | if (strcmp(key, "guest_ok") == 0) | |
144 | guest_ok = dup_value; | |
145 | ||
146 | if (path == NULL || comment == NULL || guest_ok == NULL) | |
147 | continue; /* Incomplete share definition */ | |
148 | else { | |
149 | shares = (smb_share_t *) | |
150 | malloc(sizeof (smb_share_t)); | |
151 | if (shares == NULL) { | |
152 | rc = SA_NO_MEMORY; | |
153 | goto out; | |
154 | } | |
155 | ||
156 | strncpy(shares->name, name, | |
157 | sizeof (shares->name)); | |
d1d7e268 | 158 | shares->name [sizeof (shares->name) - 1] = '\0'; |
645fb9cc TF |
159 | |
160 | strncpy(shares->path, path, | |
d1d7e268 MK |
161 | sizeof (shares->path)); |
162 | shares->path [sizeof (shares->path) - 1] = '\0'; | |
645fb9cc TF |
163 | |
164 | strncpy(shares->comment, comment, | |
d1d7e268 MK |
165 | sizeof (shares->comment)); |
166 | shares->comment[sizeof (shares->comment)-1] = | |
167 | '\0'; | |
645fb9cc TF |
168 | |
169 | shares->guest_ok = atoi(guest_ok); | |
170 | ||
171 | shares->next = new_shares; | |
172 | new_shares = shares; | |
173 | ||
d1d7e268 MK |
174 | name = NULL; |
175 | path = NULL; | |
176 | comment = NULL; | |
645fb9cc TF |
177 | guest_ok = NULL; |
178 | } | |
179 | } | |
180 | ||
181 | out: | |
182 | if (share_file_fp != NULL) | |
183 | fclose(share_file_fp); | |
184 | ||
185 | free(name); | |
186 | free(path); | |
187 | free(comment); | |
188 | free(guest_ok); | |
189 | } | |
190 | closedir(shares_dir); | |
191 | ||
192 | smb_shares = new_shares; | |
193 | ||
d1d7e268 | 194 | return (rc); |
645fb9cc TF |
195 | } |
196 | ||
d1d7e268 | 197 | /* |
645fb9cc TF |
198 | * Used internally by smb_enable_share to enable sharing for a single host. |
199 | */ | |
200 | static int | |
201 | smb_enable_share_one(const char *sharename, const char *sharepath) | |
202 | { | |
203 | char *argv[10], *pos; | |
204 | char name[SMB_NAME_MAX], comment[SMB_COMMENT_MAX]; | |
205 | int rc; | |
206 | ||
207 | /* Support ZFS share name regexp '[[:alnum:]_-.: ]' */ | |
d1d7e268 MK |
208 | strncpy(name, sharename, sizeof (name)); |
209 | name [sizeof (name)-1] = '\0'; | |
645fb9cc TF |
210 | |
211 | pos = name; | |
212 | while (*pos != '\0') { | |
213 | switch (*pos) { | |
214 | case '/': | |
215 | case '-': | |
216 | case ':': | |
217 | case ' ': | |
218 | *pos = '_'; | |
219 | } | |
220 | ||
221 | ++pos; | |
222 | } | |
223 | ||
d1d7e268 MK |
224 | /* |
225 | * CMD: net -S NET_CMD_ARG_HOST usershare add Test1 /share/Test1 \ | |
226 | * "Comment" "Everyone:F" | |
227 | */ | |
228 | snprintf(comment, sizeof (comment), "Comment: %s", sharepath); | |
229 | ||
230 | argv[0] = NET_CMD_PATH; | |
231 | argv[1] = (char *)"-S"; | |
232 | argv[2] = NET_CMD_ARG_HOST; | |
233 | argv[3] = (char *)"usershare"; | |
234 | argv[4] = (char *)"add"; | |
235 | argv[5] = (char *)name; | |
236 | argv[6] = (char *)sharepath; | |
237 | argv[7] = (char *)comment; | |
645fb9cc TF |
238 | argv[8] = "Everyone:F"; |
239 | argv[9] = NULL; | |
240 | ||
241 | rc = libzfs_run_process(argv[0], argv, 0); | |
242 | if (rc < 0) | |
d1d7e268 | 243 | return (SA_SYSTEM_ERR); |
645fb9cc TF |
244 | |
245 | /* Reload the share file */ | |
246 | (void) smb_retrieve_shares(); | |
247 | ||
d1d7e268 | 248 | return (SA_OK); |
645fb9cc TF |
249 | } |
250 | ||
d1d7e268 | 251 | /* |
645fb9cc TF |
252 | * Enables SMB sharing for the specified share. |
253 | */ | |
254 | static int | |
255 | smb_enable_share(sa_share_impl_t impl_share) | |
256 | { | |
257 | char *shareopts; | |
258 | ||
259 | if (!smb_available()) | |
d1d7e268 | 260 | return (SA_SYSTEM_ERR); |
645fb9cc TF |
261 | |
262 | shareopts = FSINFO(impl_share, smb_fstype)->shareopts; | |
263 | if (shareopts == NULL) /* on/off */ | |
d1d7e268 | 264 | return (SA_SYSTEM_ERR); |
645fb9cc TF |
265 | |
266 | if (strcmp(shareopts, "off") == 0) | |
d1d7e268 | 267 | return (SA_OK); |
645fb9cc TF |
268 | |
269 | /* Magic: Enable (i.e., 'create new') share */ | |
d1d7e268 MK |
270 | return (smb_enable_share_one(impl_share->dataset, |
271 | impl_share->sharepath)); | |
645fb9cc TF |
272 | } |
273 | ||
d1d7e268 | 274 | /* |
645fb9cc TF |
275 | * Used internally by smb_disable_share to disable sharing for a single host. |
276 | */ | |
277 | static int | |
278 | smb_disable_share_one(const char *sharename) | |
279 | { | |
280 | int rc; | |
281 | char *argv[7]; | |
282 | ||
283 | /* CMD: net -S NET_CMD_ARG_HOST usershare delete Test1 */ | |
284 | argv[0] = NET_CMD_PATH; | |
d1d7e268 | 285 | argv[1] = (char *)"-S"; |
645fb9cc | 286 | argv[2] = NET_CMD_ARG_HOST; |
d1d7e268 MK |
287 | argv[3] = (char *)"usershare"; |
288 | argv[4] = (char *)"delete"; | |
645fb9cc TF |
289 | argv[5] = strdup(sharename); |
290 | argv[6] = NULL; | |
291 | ||
292 | rc = libzfs_run_process(argv[0], argv, 0); | |
293 | if (rc < 0) | |
d1d7e268 | 294 | return (SA_SYSTEM_ERR); |
645fb9cc | 295 | else |
d1d7e268 | 296 | return (SA_OK); |
645fb9cc TF |
297 | } |
298 | ||
d1d7e268 | 299 | /* |
645fb9cc TF |
300 | * Disables SMB sharing for the specified share. |
301 | */ | |
302 | static int | |
303 | smb_disable_share(sa_share_impl_t impl_share) | |
304 | { | |
305 | smb_share_t *shares = smb_shares; | |
306 | ||
307 | if (!smb_available()) { | |
308 | /* | |
309 | * The share can't possibly be active, so nothing | |
310 | * needs to be done to disable it. | |
311 | */ | |
d1d7e268 | 312 | return (SA_OK); |
645fb9cc TF |
313 | } |
314 | ||
315 | while (shares != NULL) { | |
316 | if (strcmp(impl_share->sharepath, shares->path) == 0) | |
d1d7e268 | 317 | return (smb_disable_share_one(shares->name)); |
645fb9cc TF |
318 | |
319 | shares = shares->next; | |
320 | } | |
321 | ||
d1d7e268 | 322 | return (SA_OK); |
645fb9cc TF |
323 | } |
324 | ||
d1d7e268 | 325 | /* |
645fb9cc TF |
326 | * Checks whether the specified SMB share options are syntactically correct. |
327 | */ | |
328 | static int | |
329 | smb_validate_shareopts(const char *shareopts) | |
330 | { | |
331 | /* TODO: Accept 'name' and sec/acl (?) */ | |
332 | if ((strcmp(shareopts, "off") == 0) || (strcmp(shareopts, "on") == 0)) | |
d1d7e268 | 333 | return (SA_OK); |
645fb9cc | 334 | |
d1d7e268 | 335 | return (SA_SYNTAX_ERR); |
645fb9cc TF |
336 | } |
337 | ||
d1d7e268 | 338 | /* |
645fb9cc TF |
339 | * Checks whether a share is currently active. |
340 | */ | |
341 | static boolean_t | |
342 | smb_is_share_active(sa_share_impl_t impl_share) | |
343 | { | |
344 | if (!smb_available()) | |
d1d7e268 | 345 | return (B_FALSE); |
645fb9cc TF |
346 | |
347 | /* Retrieve the list of (possible) active shares */ | |
348 | smb_retrieve_shares(); | |
349 | ||
350 | while (smb_shares != NULL) { | |
351 | if (strcmp(impl_share->sharepath, smb_shares->path) == 0) | |
d1d7e268 | 352 | return (B_TRUE); |
645fb9cc TF |
353 | |
354 | smb_shares = smb_shares->next; | |
355 | } | |
356 | ||
d1d7e268 | 357 | return (B_FALSE); |
645fb9cc TF |
358 | } |
359 | ||
d1d7e268 | 360 | /* |
645fb9cc TF |
361 | * Called to update a share's options. A share's options might be out of |
362 | * date if the share was loaded from disk and the "sharesmb" dataset | |
363 | * property has changed in the meantime. This function also takes care | |
364 | * of re-enabling the share if necessary. | |
365 | */ | |
366 | static int | |
367 | smb_update_shareopts(sa_share_impl_t impl_share, const char *resource, | |
368 | const char *shareopts) | |
369 | { | |
370 | char *shareopts_dup; | |
371 | boolean_t needs_reshare = B_FALSE; | |
372 | char *old_shareopts; | |
373 | ||
d1d7e268 MK |
374 | if (!impl_share) |
375 | return (SA_SYSTEM_ERR); | |
645fb9cc TF |
376 | |
377 | FSINFO(impl_share, smb_fstype)->active = | |
378 | smb_is_share_active(impl_share); | |
379 | ||
380 | old_shareopts = FSINFO(impl_share, smb_fstype)->shareopts; | |
381 | ||
382 | if (FSINFO(impl_share, smb_fstype)->active && old_shareopts != NULL && | |
383 | strcmp(old_shareopts, shareopts) != 0) { | |
384 | needs_reshare = B_TRUE; | |
385 | smb_disable_share(impl_share); | |
386 | } | |
387 | ||
388 | shareopts_dup = strdup(shareopts); | |
389 | ||
390 | if (shareopts_dup == NULL) | |
d1d7e268 | 391 | return (SA_NO_MEMORY); |
645fb9cc TF |
392 | |
393 | if (old_shareopts != NULL) | |
394 | free(old_shareopts); | |
395 | ||
396 | FSINFO(impl_share, smb_fstype)->shareopts = shareopts_dup; | |
397 | ||
398 | if (needs_reshare) | |
399 | smb_enable_share(impl_share); | |
400 | ||
d1d7e268 | 401 | return (SA_OK); |
645fb9cc TF |
402 | } |
403 | ||
d1d7e268 | 404 | /* |
645fb9cc TF |
405 | * Clears a share's SMB options. Used by libshare to |
406 | * clean up shares that are about to be free()'d. | |
407 | */ | |
408 | static void | |
409 | smb_clear_shareopts(sa_share_impl_t impl_share) | |
410 | { | |
411 | free(FSINFO(impl_share, smb_fstype)->shareopts); | |
412 | FSINFO(impl_share, smb_fstype)->shareopts = NULL; | |
413 | } | |
414 | ||
415 | static const sa_share_ops_t smb_shareops = { | |
416 | .enable_share = smb_enable_share, | |
417 | .disable_share = smb_disable_share, | |
418 | ||
419 | .validate_shareopts = smb_validate_shareopts, | |
420 | .update_shareopts = smb_update_shareopts, | |
421 | .clear_shareopts = smb_clear_shareopts, | |
422 | }; | |
423 | ||
424 | /* | |
425 | * Provides a convenient wrapper for determining SMB availability | |
426 | */ | |
427 | static boolean_t | |
428 | smb_available(void) | |
429 | { | |
be8bc8c0 TF |
430 | struct stat statbuf; |
431 | ||
432 | if (lstat(SHARE_DIR, &statbuf) != 0 || | |
433 | !S_ISDIR(statbuf.st_mode)) | |
d1d7e268 | 434 | return (B_FALSE); |
be8bc8c0 TF |
435 | |
436 | if (access(NET_CMD_PATH, F_OK) != 0) | |
d1d7e268 | 437 | return (B_FALSE); |
be8bc8c0 | 438 | |
d1d7e268 | 439 | return (B_TRUE); |
645fb9cc TF |
440 | } |
441 | ||
d1d7e268 | 442 | /* |
645fb9cc TF |
443 | * Initializes the SMB functionality of libshare. |
444 | */ | |
445 | void | |
446 | libshare_smb_init(void) | |
447 | { | |
448 | smb_fstype = register_fstype("smb", &smb_shareops); | |
449 | } |