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