Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/contrib/openzfs/lib/libshare/os/linux/smb.c
48546 views
1
// SPDX-License-Identifier: CDDL-1.0
2
/*
3
* CDDL HEADER START
4
*
5
* The contents of this file are subject to the terms of the
6
* Common Development and Distribution License (the "License").
7
* You may not use this file except in compliance with the License.
8
*
9
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10
* or https://opensource.org/licenses/CDDL-1.0.
11
* See the License for the specific language governing permissions
12
* and limitations under the License.
13
*
14
* When distributing Covered Code, include this CDDL HEADER in each
15
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16
* If applicable, add the following below this CDDL HEADER, with the
17
* fields enclosed by brackets "[]" replaced with your own identifying
18
* information: Portions Copyright [yyyy] [name of copyright owner]
19
*
20
* CDDL HEADER END
21
*/
22
23
/*
24
* Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
25
* Copyright (c) 2011,2012 Turbo Fredriksson <[email protected]>, based on nfs.c
26
* by Gunnar Beutner
27
* Copyright (c) 2019, 2020 by Delphix. All rights reserved.
28
*
29
* This is an addition to the zfs device driver to add, modify and remove SMB
30
* shares using the 'net share' command that comes with Samba.
31
*
32
* TESTING
33
* Make sure that samba listens to 'localhost' (127.0.0.1) and that the options
34
* 'usershare max shares' and 'usershare owner only' have been reviewed/set
35
* accordingly (see zfs(8) for information).
36
*
37
* Once configuration in samba have been done, test that this
38
* works with the following three commands (in this case, my ZFS
39
* filesystem is called 'share/Test1'):
40
*
41
* (root)# net -U root -S 127.0.0.1 usershare add Test1 /share/Test1 \
42
* "Comment: /share/Test1" "Everyone:F"
43
* (root)# net usershare list | grep -i test
44
* (root)# net -U root -S 127.0.0.1 usershare delete Test1
45
*
46
* The first command will create a user share that gives everyone full access.
47
* To limit the access below that, use normal UNIX commands (chmod, chown etc).
48
*/
49
50
#include <time.h>
51
#include <stdlib.h>
52
#include <stdio.h>
53
#include <string.h>
54
#include <fcntl.h>
55
#include <sys/wait.h>
56
#include <unistd.h>
57
#include <dirent.h>
58
#include <sys/types.h>
59
#include <sys/stat.h>
60
#include <libzfs.h>
61
#include <libshare.h>
62
#include "libshare_impl.h"
63
#include "smb.h"
64
65
static boolean_t smb_available(void);
66
67
static smb_share_t *smb_shares;
68
static int smb_disable_share(sa_share_impl_t impl_share);
69
static boolean_t smb_is_share_active(sa_share_impl_t impl_share);
70
71
/*
72
* Retrieve the list of SMB shares.
73
*/
74
static int
75
smb_retrieve_shares(void)
76
{
77
int rc = SA_OK;
78
char file_path[PATH_MAX], line[512], *token, *key, *value;
79
char *dup_value = NULL, *path = NULL, *comment = NULL, *name = NULL;
80
char *guest_ok = NULL;
81
DIR *shares_dir;
82
FILE *share_file_fp = NULL;
83
struct dirent *directory;
84
struct stat eStat;
85
smb_share_t *shares, *new_shares = NULL;
86
87
/* opendir(), stat() */
88
shares_dir = opendir(SHARE_DIR);
89
if (shares_dir == NULL)
90
return (SA_SYSTEM_ERR);
91
92
/* Go through the directory, looking for shares */
93
while ((directory = readdir(shares_dir))) {
94
int fd;
95
96
if (directory->d_name[0] == '.')
97
continue;
98
99
snprintf(file_path, sizeof (file_path),
100
"%s/%s", SHARE_DIR, directory->d_name);
101
102
if ((fd = open(file_path, O_RDONLY | O_CLOEXEC)) == -1) {
103
rc = SA_SYSTEM_ERR;
104
goto out;
105
}
106
107
if (fstat(fd, &eStat) == -1) {
108
close(fd);
109
rc = SA_SYSTEM_ERR;
110
goto out;
111
}
112
113
if (!S_ISREG(eStat.st_mode)) {
114
close(fd);
115
continue;
116
}
117
118
if ((share_file_fp = fdopen(fd, "r")) == NULL) {
119
close(fd);
120
rc = SA_SYSTEM_ERR;
121
goto out;
122
}
123
124
name = strdup(directory->d_name);
125
if (name == NULL) {
126
rc = SA_NO_MEMORY;
127
goto out;
128
}
129
130
while (fgets(line, sizeof (line), share_file_fp)) {
131
if (line[0] == '#')
132
continue;
133
134
/* Trim trailing new-line character(s). */
135
while (line[strlen(line) - 1] == '\r' ||
136
line[strlen(line) - 1] == '\n')
137
line[strlen(line) - 1] = '\0';
138
139
/* Split the line in two, separated by '=' */
140
token = strchr(line, '=');
141
if (token == NULL)
142
continue;
143
144
key = line;
145
value = token + 1;
146
*token = '\0';
147
148
dup_value = strdup(value);
149
if (dup_value == NULL) {
150
rc = SA_NO_MEMORY;
151
goto out;
152
}
153
154
if (strcmp(key, "path") == 0) {
155
free(path);
156
path = dup_value;
157
} else if (strcmp(key, "comment") == 0) {
158
free(comment);
159
comment = dup_value;
160
} else if (strcmp(key, "guest_ok") == 0) {
161
free(guest_ok);
162
guest_ok = dup_value;
163
} else
164
free(dup_value);
165
166
dup_value = NULL;
167
168
if (path == NULL || comment == NULL || guest_ok == NULL)
169
continue; /* Incomplete share definition */
170
else {
171
shares = (smb_share_t *)
172
malloc(sizeof (smb_share_t));
173
if (shares == NULL) {
174
rc = SA_NO_MEMORY;
175
goto out;
176
}
177
178
(void) strlcpy(shares->name, name,
179
sizeof (shares->name));
180
181
(void) strlcpy(shares->path, path,
182
sizeof (shares->path));
183
184
(void) strlcpy(shares->comment, comment,
185
sizeof (shares->comment));
186
187
shares->guest_ok = atoi(guest_ok);
188
189
shares->next = new_shares;
190
new_shares = shares;
191
192
free(path);
193
free(comment);
194
free(guest_ok);
195
196
path = NULL;
197
comment = NULL;
198
guest_ok = NULL;
199
}
200
}
201
202
out:
203
if (share_file_fp != NULL) {
204
fclose(share_file_fp);
205
share_file_fp = NULL;
206
}
207
208
free(name);
209
free(path);
210
free(comment);
211
free(guest_ok);
212
213
name = NULL;
214
path = NULL;
215
comment = NULL;
216
guest_ok = NULL;
217
}
218
closedir(shares_dir);
219
220
smb_shares = new_shares;
221
222
return (rc);
223
}
224
225
/*
226
* Used internally by smb_enable_share to enable sharing for a single host.
227
*/
228
static int
229
smb_enable_share_one(const char *sharename, const char *sharepath)
230
{
231
char name[SMB_NAME_MAX], comment[SMB_COMMENT_MAX];
232
233
/* Support ZFS share name regexp '[[:alnum:]_-.: ]' */
234
strlcpy(name, sharename, sizeof (name));
235
for (char *itr = name; *itr != '\0'; ++itr)
236
switch (*itr) {
237
case '/':
238
case '-':
239
case ':':
240
case ' ':
241
*itr = '_';
242
}
243
244
/*
245
* CMD: net -S NET_CMD_ARG_HOST usershare add Test1 /share/Test1 \
246
* "Comment" "Everyone:F"
247
*/
248
snprintf(comment, sizeof (comment), "Comment: %s", sharepath);
249
250
char *argv[] = {
251
(char *)NET_CMD_PATH,
252
(char *)"-S",
253
(char *)NET_CMD_ARG_HOST,
254
(char *)"usershare",
255
(char *)"add",
256
name,
257
(char *)sharepath,
258
comment,
259
(char *)"Everyone:F",
260
NULL,
261
};
262
263
if (libzfs_run_process(argv[0], argv, 0) != 0)
264
return (SA_SYSTEM_ERR);
265
266
/* Reload the share file */
267
(void) smb_retrieve_shares();
268
269
return (SA_OK);
270
}
271
272
/*
273
* Enables SMB sharing for the specified share.
274
*/
275
static int
276
smb_enable_share(sa_share_impl_t impl_share)
277
{
278
if (!smb_available())
279
return (SA_SYSTEM_ERR);
280
281
if (smb_is_share_active(impl_share))
282
smb_disable_share(impl_share);
283
284
if (impl_share->sa_shareopts == NULL) /* on/off */
285
return (SA_SYSTEM_ERR);
286
287
if (strcmp(impl_share->sa_shareopts, "off") == 0)
288
return (SA_OK);
289
290
/* Magic: Enable (i.e., 'create new') share */
291
return (smb_enable_share_one(impl_share->sa_zfsname,
292
impl_share->sa_mountpoint));
293
}
294
295
/*
296
* Used internally by smb_disable_share to disable sharing for a single host.
297
*/
298
static int
299
smb_disable_share_one(const char *sharename)
300
{
301
/* CMD: net -S NET_CMD_ARG_HOST usershare delete Test1 */
302
char *argv[] = {
303
(char *)NET_CMD_PATH,
304
(char *)"-S",
305
(char *)NET_CMD_ARG_HOST,
306
(char *)"usershare",
307
(char *)"delete",
308
(char *)sharename,
309
NULL,
310
};
311
312
if (libzfs_run_process(argv[0], argv, 0) != 0)
313
return (SA_SYSTEM_ERR);
314
else
315
return (SA_OK);
316
}
317
318
/*
319
* Disables SMB sharing for the specified share.
320
*/
321
static int
322
smb_disable_share(sa_share_impl_t impl_share)
323
{
324
if (!smb_available()) {
325
/*
326
* The share can't possibly be active, so nothing
327
* needs to be done to disable it.
328
*/
329
return (SA_OK);
330
}
331
332
for (const smb_share_t *i = smb_shares; i != NULL; i = i->next)
333
if (strcmp(impl_share->sa_mountpoint, i->path) == 0)
334
return (smb_disable_share_one(i->name));
335
336
return (SA_OK);
337
}
338
339
/*
340
* Checks whether the specified SMB share options are syntactically correct.
341
*/
342
static int
343
smb_validate_shareopts(const char *shareopts)
344
{
345
/* TODO: Accept 'name' and sec/acl (?) */
346
if ((strcmp(shareopts, "off") == 0) || (strcmp(shareopts, "on") == 0))
347
return (SA_OK);
348
349
return (SA_SYNTAX_ERR);
350
}
351
352
/*
353
* Checks whether a share is currently active.
354
*/
355
static boolean_t
356
smb_is_share_active(sa_share_impl_t impl_share)
357
{
358
if (!smb_available())
359
return (B_FALSE);
360
361
/* Retrieve the list of (possible) active shares */
362
smb_retrieve_shares();
363
364
for (const smb_share_t *i = smb_shares; i != NULL; i = i->next)
365
if (strcmp(impl_share->sa_mountpoint, i->path) == 0)
366
return (B_TRUE);
367
368
return (B_FALSE);
369
}
370
371
static int
372
smb_update_shares(void)
373
{
374
/* Not implemented */
375
return (0);
376
}
377
378
const sa_fstype_t libshare_smb_type = {
379
.enable_share = smb_enable_share,
380
.disable_share = smb_disable_share,
381
.is_shared = smb_is_share_active,
382
383
.validate_shareopts = smb_validate_shareopts,
384
.commit_shares = smb_update_shares,
385
};
386
387
/*
388
* Provides a convenient wrapper for determining SMB availability
389
*/
390
static boolean_t
391
smb_available(void)
392
{
393
static int avail;
394
395
if (!avail) {
396
struct stat statbuf;
397
398
if (access(NET_CMD_PATH, F_OK) != 0 ||
399
lstat(SHARE_DIR, &statbuf) != 0 ||
400
!S_ISDIR(statbuf.st_mode))
401
avail = -1;
402
else
403
avail = 1;
404
}
405
406
return (avail == 1);
407
}
408
409