]> git.proxmox.com Git - mirror_zfs-debian.git/blob - lib/libshare/smb.c
New upstream version 0.7.9
[mirror_zfs-debian.git] / lib / libshare / 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 *
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.
29 *
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
67 /*
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 = NULL, *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)
86 return (SA_SYSTEM_ERR);
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),
94 "%s/%s", SHARE_DIR, directory->d_name);
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) {
111 rc = SA_NO_MEMORY;
112 goto out;
113 }
114
115 while (fgets(line, sizeof (line), share_file_fp)) {
116 if (line[0] == '#')
117 continue;
118
119 /* Trim trailing new-line character(s). */
120 while (line[strlen(line) - 1] == '\r' ||
121 line[strlen(line) - 1] == '\n')
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 free(path);
141 path = dup_value;
142 } else if (strcmp(key, "comment") == 0) {
143 free(comment);
144 comment = dup_value;
145 } else if (strcmp(key, "guest_ok") == 0) {
146 free(guest_ok);
147 guest_ok = dup_value;
148 } else
149 free(dup_value);
150
151 dup_value = NULL;
152
153 if (path == NULL || comment == NULL || guest_ok == NULL)
154 continue; /* Incomplete share definition */
155 else {
156 shares = (smb_share_t *)
157 malloc(sizeof (smb_share_t));
158 if (shares == NULL) {
159 rc = SA_NO_MEMORY;
160 goto out;
161 }
162
163 (void) strlcpy(shares->name, name,
164 sizeof (shares->name));
165
166 (void) strlcpy(shares->path, path,
167 sizeof (shares->path));
168
169 (void) strlcpy(shares->comment, comment,
170 sizeof (shares->comment));
171
172 shares->guest_ok = atoi(guest_ok);
173
174 shares->next = new_shares;
175 new_shares = shares;
176
177 free(path);
178 free(comment);
179 free(guest_ok);
180
181 path = NULL;
182 comment = NULL;
183 guest_ok = NULL;
184 }
185 }
186
187 out:
188 if (share_file_fp != NULL) {
189 fclose(share_file_fp);
190 share_file_fp = NULL;
191 }
192
193 free(name);
194 free(path);
195 free(comment);
196 free(guest_ok);
197
198 name = NULL;
199 path = NULL;
200 comment = NULL;
201 guest_ok = NULL;
202 }
203 closedir(shares_dir);
204
205 smb_shares = new_shares;
206
207 return (rc);
208 }
209
210 /*
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:]_-.: ]' */
221 strncpy(name, sharename, sizeof (name));
222 name [sizeof (name)-1] = '\0';
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
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;
251 argv[8] = "Everyone:F";
252 argv[9] = NULL;
253
254 rc = libzfs_run_process(argv[0], argv, 0);
255 if (rc < 0)
256 return (SA_SYSTEM_ERR);
257
258 /* Reload the share file */
259 (void) smb_retrieve_shares();
260
261 return (SA_OK);
262 }
263
264 /*
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())
273 return (SA_SYSTEM_ERR);
274
275 shareopts = FSINFO(impl_share, smb_fstype)->shareopts;
276 if (shareopts == NULL) /* on/off */
277 return (SA_SYSTEM_ERR);
278
279 if (strcmp(shareopts, "off") == 0)
280 return (SA_OK);
281
282 /* Magic: Enable (i.e., 'create new') share */
283 return (smb_enable_share_one(impl_share->dataset,
284 impl_share->sharepath));
285 }
286
287 /*
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;
298 argv[1] = (char *)"-S";
299 argv[2] = NET_CMD_ARG_HOST;
300 argv[3] = (char *)"usershare";
301 argv[4] = (char *)"delete";
302 argv[5] = strdup(sharename);
303 argv[6] = NULL;
304
305 rc = libzfs_run_process(argv[0], argv, 0);
306 if (rc < 0)
307 return (SA_SYSTEM_ERR);
308 else
309 return (SA_OK);
310 }
311
312 /*
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 */
325 return (SA_OK);
326 }
327
328 while (shares != NULL) {
329 if (strcmp(impl_share->sharepath, shares->path) == 0)
330 return (smb_disable_share_one(shares->name));
331
332 shares = shares->next;
333 }
334
335 return (SA_OK);
336 }
337
338 /*
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))
346 return (SA_OK);
347
348 return (SA_SYNTAX_ERR);
349 }
350
351 /*
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 {
357 smb_share_t *iter = smb_shares;
358
359 if (!smb_available())
360 return (B_FALSE);
361
362 /* Retrieve the list of (possible) active shares */
363 smb_retrieve_shares();
364
365 while (iter != NULL) {
366 if (strcmp(impl_share->sharepath, iter->path) == 0)
367 return (B_TRUE);
368
369 iter = iter->next;
370 }
371
372 return (B_FALSE);
373 }
374
375 /*
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
389 if (!impl_share)
390 return (SA_SYSTEM_ERR);
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 &&
398 strcmp(old_shareopts, shareopts) != 0) {
399 needs_reshare = B_TRUE;
400 smb_disable_share(impl_share);
401 }
402
403 shareopts_dup = strdup(shareopts);
404
405 if (shareopts_dup == NULL)
406 return (SA_NO_MEMORY);
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
416 return (SA_OK);
417 }
418
419 /*
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 {
445 struct stat statbuf;
446
447 if (lstat(SHARE_DIR, &statbuf) != 0 ||
448 !S_ISDIR(statbuf.st_mode))
449 return (B_FALSE);
450
451 if (access(NET_CMD_PATH, F_OK) != 0)
452 return (B_FALSE);
453
454 return (B_TRUE);
455 }
456
457 /*
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 }