susmb.c - susmb - fork from usmb 20130204: mount SMB/CIFS shares via FUSE | |
git clone git://git.codemadness.org/susmb | |
Log | |
Files | |
Refs | |
README | |
LICENSE | |
--- | |
susmb.c (29964B) | |
--- | |
1 /* susmb - mount SMB shares via FUSE and Samba | |
2 * Copyright (C) 2025 Hiltjo Posthuma | |
3 * Copyright (C) 2006-2013 Geoff Johnstone | |
4 * | |
5 * Portions of this file are taken from Samba 3.2's libsmbclient.h: | |
6 * Copyright (C) Andrew Tridgell 1998 | |
7 * Copyright (C) Richard Sharpe 2000 | |
8 * Copyright (C) John Terpsra 2000 | |
9 * Copyright (C) Tom Jansen (Ninja ISD) 2002 | |
10 * Copyright (C) Derrell Lipman 2003-2008 | |
11 * | |
12 * This program is free software; you can redistribute it and/or modify | |
13 * it under the terms of the GNU General Public License version 3 as | |
14 * published by the Free Software Foundation. | |
15 * | |
16 * This program is distributed in the hope that it will be useful, | |
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
19 * GNU General Public License for more details. | |
20 * | |
21 * You should have received a copy of the GNU General Public License | |
22 * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
23 */ | |
24 | |
25 /* Marks unused parameters */ | |
26 #define UNUSED __attribute__ ((unused)) | |
27 | |
28 #define SUSMB_VERSION "0.9" | |
29 | |
30 #include <libsmbclient.h> | |
31 | |
32 /* Required FUSE API version */ | |
33 #define FUSE_USE_VERSION 26 | |
34 | |
35 #include <fuse.h> | |
36 | |
37 #include <sys/types.h> | |
38 #include <sys/statvfs.h> | |
39 | |
40 /* struct timeval needed by libsmbclient.h */ | |
41 #include <sys/time.h> | |
42 | |
43 #include <dirent.h> | |
44 #include <err.h> | |
45 #include <errno.h> | |
46 #include <limits.h> | |
47 #include <pwd.h> | |
48 #include <stdarg.h> | |
49 #include <stdbool.h> | |
50 #include <stddef.h> | |
51 #include <stdio.h> | |
52 #include <stdlib.h> | |
53 #include <string.h> | |
54 #include <unistd.h> | |
55 | |
56 /* use libbsd stdlib.h for arc4random() */ | |
57 #ifdef __linux__ | |
58 #include <bsd/stdlib.h> | |
59 #endif | |
60 | |
61 #ifndef __OpenBSD__ | |
62 #define unveil(p1,p2) 0 | |
63 #endif | |
64 | |
65 /* ctype-like macros, but always compatible with ASCII / UTF-8 */ | |
66 #define ISALPHA(c) ((((unsigned)c) | 32) - 'a' < 26) | |
67 #define ISCNTRL(c) ((c) < ' ' || (c) == 0x7f) | |
68 #define ISDIGIT(c) (((unsigned)c) - '0' < 10) | |
69 #define ISSPACE(c) ((c) == ' ' || ((((unsigned)c) - '\t') < 5)) | |
70 #define TOLOWER(c) ((((unsigned)c) - 'A' < 26) ? ((c) | 32) : (c)) | |
71 | |
72 /* URI */ | |
73 struct uri { | |
74 char proto[48]; /* scheme including ":" or "://" */ | |
75 char userinfo[256]; /* username [:password] */ | |
76 char host[256]; | |
77 char port[6]; /* numeric port */ | |
78 char path[1024]; | |
79 char query[1024]; | |
80 char fragment[1024]; | |
81 }; | |
82 | |
83 int uri_parse(const char *s, struct uri *u); | |
84 | |
85 char * make_url(const char *path); | |
86 bool create_smb_context(SMBCCTX **pctx); | |
87 void destroy_smb_context(SMBCCTX *ctx_, int shutdown); | |
88 | |
89 int usmb_statfs(const char *path UNUSED, struct statvfs *vfs UNUSED); | |
90 int compat_truncate(const char *path, SMBCFILE *file, off_t size); | |
91 | |
92 void show_about(FILE *fp); | |
93 void show_version(FILE *fp); | |
94 void usage(void); | |
95 | |
96 void build_fuse_args(const char *options, const char *mountpoint, | |
97 int debug, int nofork, | |
98 int *out_argc, char ***out_argv); | |
99 | |
100 int usmb_fuse_main(int argc, char *argv[], | |
101 const struct fuse_operations *op, size_t op… | |
102 void *user_data); | |
103 | |
104 int usmb_getattr(const char *filename, struct stat *st); | |
105 int usmb_fgetattr(const char *filename, struct stat *st, | |
106 struct fuse_file_info *fi); | |
107 int usmb_unlink(const char *filename); | |
108 int usmb_open(const char *filename, struct fuse_file_info *fi); | |
109 int usmb_release(const char *filename, struct fuse_file_info *fi); | |
110 int usmb_read(const char *filename, char *buff, size_t len, off_t off, | |
111 struct fuse_file_info *fi); | |
112 int usmb_write(const char *filename, const char *buff, size_t len, off_t… | |
113 struct fuse_file_info *fi); | |
114 int usmb_mknod(const char *filename, mode_t mode, dev_t dev); | |
115 int usmb_create(const char *filename, mode_t mode, | |
116 struct fuse_file_info *fi); | |
117 int usmb_rename(const char *from, const char *to); | |
118 int usmb_utime(const char *filename, struct utimbuf *utb); | |
119 int usmb_truncate(const char *filename, off_t newsize); | |
120 int usmb_chmod(const char *filename, mode_t mode); | |
121 int usmb_ftruncate(const char *path, off_t size, | |
122 struct fuse_file_info *fi); | |
123 | |
124 /* directory operations */ | |
125 | |
126 int usmb_mkdir(const char *dirname, mode_t mode); | |
127 int usmb_rmdir(const char *dirname); | |
128 int usmb_opendir(const char *dirname, struct fuse_file_info *fi); | |
129 int usmb_readdir(const char *path, void *h, fuse_fill_dir_t filler, | |
130 off_t offset, struct fuse_file_info *fi); | |
131 int usmb_releasedir(const char *path, struct fuse_file_info *fi); | |
132 int usmb_setxattr(const char *path, const char *name, const char *value, | |
133 size_t size, int flags); | |
134 int usmb_getxattr(const char *path, const char *name, char *value, | |
135 size_t size); | |
136 int usmb_listxattr(const char *path, char *list, size_t size); | |
137 int usmb_removexattr(const char *path, const char *name); | |
138 | |
139 void *emalloc(size_t size); | |
140 void *erealloc(void *ptr, size_t size); | |
141 char *estrdup(const char *s); | |
142 | |
143 char * xstrdup(const char *in); | |
144 void clear_and_free(char *ptr); | |
145 void free_errno(void *ptr); | |
146 | |
147 /* globals */ | |
148 | |
149 static SMBCCTX *ctx; | |
150 static int opt_debug, opt_nofork; | |
151 static char *opt_server, *opt_share, *opt_mountpoint, *opt_options, | |
152 *opt_domain, *opt_username, *opt_password; | |
153 static int disconnect; | |
154 static char *sharename; | |
155 static const char *argv0 = "susmb"; | |
156 | |
157 /* for privdrop */ | |
158 static uid_t opt_uid; | |
159 static gid_t opt_gid; | |
160 static int opt_privdrop; | |
161 | |
162 /* fuse_file_info uses a uint64_t for a "File handle" */ | |
163 static inline uint64_t | |
164 smbcfile_to_fd(SMBCFILE *file) | |
165 { | |
166 return (uint64_t)(uintptr_t)file; | |
167 } | |
168 | |
169 static inline SMBCFILE * | |
170 fd_to_smbcfile(uint64_t fd) | |
171 { | |
172 return (SMBCFILE *)(uintptr_t)fd; | |
173 } | |
174 | |
175 void * | |
176 emalloc(size_t size) | |
177 { | |
178 void *p; | |
179 | |
180 p = malloc(size); | |
181 if (p == NULL) | |
182 err(1, "malloc"); | |
183 return p; | |
184 } | |
185 | |
186 void * | |
187 erealloc(void *ptr, size_t size) | |
188 { | |
189 void *p; | |
190 | |
191 p = realloc(ptr, size); | |
192 if (p == NULL) | |
193 err(1, "realloc"); | |
194 return p; | |
195 } | |
196 | |
197 char * | |
198 estrdup(const char *s) | |
199 { | |
200 char *p; | |
201 | |
202 p = strdup(s); | |
203 if (p == NULL) | |
204 err(1, "strdup"); | |
205 return p; | |
206 } | |
207 | |
208 /* Parse URI string `s` into an uri structure `u`. | |
209 * Returns 0 on success or -1 on failure */ | |
210 int | |
211 uri_parse(const char *s, struct uri *u) | |
212 { | |
213 const char *p = s; | |
214 char *endptr; | |
215 size_t i; | |
216 long l; | |
217 | |
218 u->proto[0] = u->userinfo[0] = u->host[0] = u->port[0] = '\0'; | |
219 u->path[0] = u->query[0] = u->fragment[0] = '\0'; | |
220 | |
221 /* protocol-relative */ | |
222 if (*p == '/' && *(p + 1) == '/') { | |
223 p += 2; /* skip "//" */ | |
224 goto parseauth; | |
225 } | |
226 | |
227 /* scheme / protocol part */ | |
228 for (; ISALPHA((unsigned char)*p) || ISDIGIT((unsigned char)*p) … | |
229 *p == '+' || *p == '-' || *p == '.'; p++) | |
230 ; | |
231 /* scheme, except if empty and starts with ":" then it is a path… | |
232 if (*p == ':' && p != s) { | |
233 if (*(p + 1) == '/' && *(p + 2) == '/') | |
234 p += 3; /* skip "://" */ | |
235 else | |
236 p++; /* skip ":" */ | |
237 | |
238 if ((size_t)(p - s) >= sizeof(u->proto)) | |
239 return -1; /* protocol too long */ | |
240 memcpy(u->proto, s, p - s); | |
241 u->proto[p - s] = '\0'; | |
242 | |
243 if (*(p - 1) != '/') | |
244 goto parsepath; | |
245 } else { | |
246 p = s; /* no scheme format, reset to start */ | |
247 goto parsepath; | |
248 } | |
249 | |
250 parseauth: | |
251 /* userinfo (username:password) */ | |
252 i = strcspn(p, "@/?#"); | |
253 if (p[i] == '@') { | |
254 if (i >= sizeof(u->userinfo)) | |
255 return -1; /* userinfo too long */ | |
256 memcpy(u->userinfo, p, i); | |
257 u->userinfo[i] = '\0'; | |
258 p += i + 1; | |
259 } | |
260 | |
261 /* IPv6 address */ | |
262 if (*p == '[') { | |
263 /* bracket not found, host too short or too long */ | |
264 i = strcspn(p, "]"); | |
265 if (p[i] != ']' || i < 3) | |
266 return -1; | |
267 i++; /* including "]" */ | |
268 } else { | |
269 /* domain / host part, skip until port, path or end. */ | |
270 i = strcspn(p, ":/?#"); | |
271 } | |
272 if (i >= sizeof(u->host)) | |
273 return -1; /* host too long */ | |
274 memcpy(u->host, p, i); | |
275 u->host[i] = '\0'; | |
276 p += i; | |
277 | |
278 /* port */ | |
279 if (*p == ':') { | |
280 p++; | |
281 if ((i = strcspn(p, "/?#")) >= sizeof(u->port)) | |
282 return -1; /* port too long */ | |
283 memcpy(u->port, p, i); | |
284 u->port[i] = '\0'; | |
285 /* check for valid port: range 1 - 65535, may be empty */ | |
286 errno = 0; | |
287 l = strtol(u->port, &endptr, 10); | |
288 if (i && (errno || *endptr || l <= 0 || l > 65535)) | |
289 return -1; | |
290 p += i; | |
291 } | |
292 | |
293 parsepath: | |
294 /* path */ | |
295 if ((i = strcspn(p, "?#")) >= sizeof(u->path)) | |
296 return -1; /* path too long */ | |
297 memcpy(u->path, p, i); | |
298 u->path[i] = '\0'; | |
299 p += i; | |
300 | |
301 /* query */ | |
302 if (*p == '?') { | |
303 p++; | |
304 if ((i = strcspn(p, "#")) >= sizeof(u->query)) | |
305 return -1; /* query too long */ | |
306 memcpy(u->query, p, i); | |
307 u->query[i] = '\0'; | |
308 p += i; | |
309 } | |
310 | |
311 /* fragment */ | |
312 if (*p == '#') { | |
313 p++; | |
314 if ((i = strlen(p)) >= sizeof(u->fragment)) | |
315 return -1; /* fragment too long */ | |
316 memcpy(u->fragment, p, i); | |
317 u->fragment[i] = '\0'; | |
318 } | |
319 | |
320 return 0; | |
321 } | |
322 | |
323 char * | |
324 xstrdup(const char *in) | |
325 { | |
326 if (in != NULL) | |
327 return estrdup(in); | |
328 return NULL; | |
329 } | |
330 | |
331 void | |
332 clear_and_free(char *ptr) | |
333 { | |
334 if (ptr != NULL) { | |
335 explicit_bzero(ptr, strlen(ptr)); | |
336 free(ptr); | |
337 } | |
338 } | |
339 | |
340 void | |
341 free_errno(void *ptr) | |
342 { | |
343 int saved_errno = errno; | |
344 free(ptr); | |
345 errno = saved_errno; | |
346 } | |
347 | |
348 int | |
349 usmb_statfs(const char *path, struct statvfs *vfs) | |
350 { | |
351 if (path == NULL || vfs == NULL) | |
352 return -EINVAL; | |
353 | |
354 char *url = make_url(path); | |
355 if (url == NULL) | |
356 return -ENOMEM; | |
357 | |
358 memset(vfs, 0, sizeof(*vfs)); | |
359 | |
360 int ret = (0 > smbc_getFunctionStatVFS(ctx) (ctx, url, vfs)) ? -… | |
361 free(url); | |
362 | |
363 return ret; | |
364 } | |
365 | |
366 int | |
367 compat_truncate(const char *path UNUSED, SMBCFILE *file, off_t size) | |
368 { | |
369 return (0 > smbc_getFunctionFtruncate(ctx) (ctx, file, size)) ? … | |
370 } | |
371 | |
372 static bool | |
373 change_blksiz(struct stat *st) | |
374 { | |
375 if (st == NULL) | |
376 return false; | |
377 | |
378 /* change block size to improve performance of stdio FILE * oper… | |
379 only for regular files to be on the safe side. */ | |
380 if (S_ISREG(st->st_mode)) { | |
381 st->st_blksize = 32768; | |
382 return true; | |
383 } | |
384 | |
385 return false; | |
386 } | |
387 | |
388 /* Samba gets st_nlink wrong for directories. */ | |
389 /* still wrong in 2025-03-03 with Samba 4.20 */ | |
390 static bool | |
391 fix_nlink(const char *url, struct stat *st) | |
392 { | |
393 if (!S_ISDIR(st->st_mode)) | |
394 return true; | |
395 | |
396 SMBCFILE *file = smbc_getFunctionOpendir(ctx) (ctx, url); | |
397 if (file == NULL) | |
398 return false; | |
399 | |
400 st->st_nlink = 0; | |
401 errno = ERANGE; | |
402 | |
403 struct smbc_dirent *dirent; | |
404 while (NULL != (dirent = smbc_getFunctionReaddir(ctx) (ctx, file… | |
405 if (SMBC_DIR == dirent->smbc_type) { | |
406 if (INT_MAX == st->st_nlink++) { | |
407 break; | |
408 } | |
409 } | |
410 } | |
411 | |
412 (void)smbc_getFunctionClosedir(ctx) (ctx, file); | |
413 | |
414 return (dirent == NULL); | |
415 } | |
416 | |
417 int | |
418 usmb_getattr(const char *filename, struct stat *st) | |
419 { | |
420 char *url = make_url(filename); | |
421 if (url == NULL) | |
422 return -ENOMEM; | |
423 | |
424 int ret = smbc_getFunctionStat(ctx) (ctx, url, st); | |
425 | |
426 if ((0 > ret) || !fix_nlink(url, st)) | |
427 ret = -errno; | |
428 | |
429 change_blksiz(st); | |
430 | |
431 free(url); | |
432 | |
433 return ret; | |
434 } | |
435 | |
436 int | |
437 usmb_fgetattr(const char *filename UNUSED, struct stat *st, | |
438 struct fuse_file_info *fi) | |
439 { | |
440 SMBCFILE *file = fd_to_smbcfile(fi->fh); | |
441 | |
442 if (0 > smbc_getFunctionFstat(ctx) (ctx, file, st)) | |
443 return -errno; | |
444 | |
445 if (S_ISDIR(st->st_mode)) { | |
446 char *url = make_url(filename); | |
447 if (url == NULL) | |
448 return -ENOMEM; | |
449 | |
450 bool ok = fix_nlink(url, st); | |
451 free_errno(url); | |
452 | |
453 if (!ok) | |
454 return -errno; | |
455 } | |
456 | |
457 change_blksiz(st); | |
458 | |
459 return 0; | |
460 } | |
461 | |
462 int | |
463 usmb_unlink(const char *filename) | |
464 { | |
465 char *url = make_url(filename); | |
466 if (url == NULL) | |
467 return -ENOMEM; | |
468 | |
469 int ret = (0 > smbc_getFunctionUnlink(ctx) (ctx, url)) ? -errno … | |
470 free(url); | |
471 | |
472 return ret; | |
473 } | |
474 | |
475 int | |
476 usmb_open(const char *filename, struct fuse_file_info *fi) | |
477 { | |
478 char *url = make_url(filename); | |
479 | |
480 if (url == NULL) | |
481 return -ENOMEM; | |
482 | |
483 SMBCFILE *file = smbc_getFunctionOpen(ctx) (ctx, url, fi->flags,… | |
484 | |
485 int ret = (file == NULL) ? -errno : 0; | |
486 free(url); | |
487 fi->fh = smbcfile_to_fd(file); | |
488 | |
489 return ret; | |
490 } | |
491 | |
492 int | |
493 usmb_release(const char *filename UNUSED, struct fuse_file_info *fi) | |
494 { | |
495 SMBCFILE *file = fd_to_smbcfile(fi->fh); | |
496 return (0 > smbc_getFunctionClose(ctx) (ctx, file)) ? -errno : 0; | |
497 } | |
498 | |
499 int | |
500 usmb_read(const char *filename UNUSED, char *buff, size_t len, off_t off, | |
501 struct fuse_file_info *fi) | |
502 { | |
503 SMBCFILE *file = fd_to_smbcfile(fi->fh); | |
504 | |
505 if (0 > smbc_getFunctionLseek(ctx) (ctx, file, off, SEEK_SET)) { | |
506 return -errno; | |
507 } | |
508 | |
509 int bytes = smbc_getFunctionRead(ctx) (ctx, file, buff, len); | |
510 | |
511 return (0 > bytes) ? -errno : (int)bytes; | |
512 } | |
513 | |
514 int | |
515 usmb_write(const char *filename UNUSED, const char *buff, size_t len, | |
516 off_t off, struct fuse_file_info *fi) | |
517 { | |
518 SMBCFILE *file = fd_to_smbcfile(fi->fh); | |
519 size_t written = 0; | |
520 int bytes = 0; | |
521 | |
522 if (0 > smbc_getFunctionLseek(ctx)(ctx, file, off, SEEK_SET)) | |
523 return -errno; | |
524 | |
525 const smbc_write_fn write_fn = smbc_getFunctionWrite(ctx); | |
526 while (written < len) { | |
527 bytes = write_fn(ctx, file, (char *)buff, len); | |
528 if (0 > bytes) | |
529 break; | |
530 | |
531 written += bytes; | |
532 buff += bytes; | |
533 | |
534 /* avoids infinite loops. */ | |
535 if (bytes == 0) | |
536 break; | |
537 } | |
538 | |
539 return (0 > bytes) ? -errno : (int)written; | |
540 } | |
541 | |
542 /* File systems must support mknod on OpenBSD */ | |
543 int | |
544 usmb_mknod(const char *filename, mode_t mode, __attribute__((unused)) de… | |
545 { | |
546 char *url = make_url(filename); | |
547 if (url == NULL) | |
548 return -ENOMEM; | |
549 | |
550 if (S_ISCHR(mode) || S_ISBLK(mode) || S_ISFIFO(mode) || S_ISSOCK… | |
551 return -EPERM; | |
552 | |
553 SMBCFILE *file = smbc_getFunctionCreat(ctx) (ctx, url, mode); | |
554 int ret = (file == NULL) ? -errno : 0; | |
555 | |
556 /* File must not be open when mknod returns. */ | |
557 if (ret == 0) | |
558 smbc_getFunctionClose(ctx) (ctx, file); | |
559 free(url); | |
560 | |
561 return ret; | |
562 } | |
563 | |
564 int | |
565 usmb_create(const char *filename, mode_t mode, struct fuse_file_info *fi) | |
566 { | |
567 char *url = make_url(filename); | |
568 if (url == NULL) | |
569 return -ENOMEM; | |
570 | |
571 SMBCFILE *file = smbc_getFunctionCreat(ctx) (ctx, url, mode); | |
572 | |
573 int ret = (file == NULL) ? -errno : 0; | |
574 fi->fh = smbcfile_to_fd(file); | |
575 | |
576 free(url); | |
577 | |
578 return ret; | |
579 } | |
580 | |
581 int | |
582 usmb_rename(const char *from, const char *to) | |
583 { | |
584 char *fromurl = make_url(from); | |
585 if (fromurl == NULL) | |
586 return -ENOMEM; | |
587 | |
588 char *tourl = make_url(to); | |
589 if (tourl == NULL) { | |
590 free(fromurl); | |
591 return -ENOMEM; | |
592 } | |
593 | |
594 int ret = | |
595 (0 > smbc_getFunctionRename(ctx)(ctx, fromurl, ctx, tourl)) … | |
596 free(tourl); | |
597 free(fromurl); | |
598 | |
599 return ret; | |
600 } | |
601 | |
602 int | |
603 usmb_utime(const char *filename, struct utimbuf *utb) | |
604 { | |
605 struct utimbuf tmp_utb; | |
606 | |
607 if (utb == NULL) { | |
608 for (;;) { | |
609 time_t now = time(NULL); | |
610 if (now != (time_t)-1) { | |
611 tmp_utb.actime = tmp_utb.modtime = now; | |
612 break; | |
613 } | |
614 | |
615 if (EINTR != errno) | |
616 return -errno; | |
617 | |
618 usleep(1000); /* sleep a bit to not hog the CPU … | |
619 } | |
620 utb = &tmp_utb; | |
621 } | |
622 | |
623 char *url = make_url(filename); | |
624 if (url == NULL) | |
625 return -ENOMEM; | |
626 | |
627 struct timeval tv[2] = { | |
628 { .tv_sec = utb->actime, .tv_usec = 0 }, | |
629 { .tv_sec = utb->modtime, .tv_usec = 0 }, | |
630 }; | |
631 | |
632 int ret = (0 > smbc_getFunctionUtimes (ctx) (ctx, url, tv)) ? -e… | |
633 free(url); | |
634 | |
635 return ret; | |
636 } | |
637 | |
638 int | |
639 usmb_chmod(const char *filename, mode_t mode) | |
640 { | |
641 char *url = make_url(filename); | |
642 | |
643 if (url == NULL) | |
644 return -ENOMEM; | |
645 | |
646 int ret = (0 > smbc_getFunctionChmod(ctx) (ctx, url, mode)) ? -e… | |
647 free(url); | |
648 | |
649 return ret; | |
650 } | |
651 | |
652 int | |
653 usmb_truncate(const char *filename, off_t newsize) | |
654 { | |
655 char *url = make_url(filename); | |
656 if (url == NULL) | |
657 return -ENOMEM; | |
658 | |
659 SMBCFILE *file = smbc_getFunctionOpen(ctx) (ctx, url, O_WRONLY, … | |
660 if (file == NULL) { | |
661 int ret = -errno; | |
662 free(url); | |
663 return ret; | |
664 } | |
665 | |
666 int ret = compat_truncate(filename, file, newsize); | |
667 | |
668 smbc_getFunctionClose(ctx) (ctx, file); | |
669 free(url); | |
670 | |
671 return ret; | |
672 } | |
673 | |
674 int | |
675 usmb_ftruncate(const char *path, off_t size, | |
676 struct fuse_file_info *fi) | |
677 { | |
678 return compat_truncate(path, fd_to_smbcfile(fi->fh), size); | |
679 } | |
680 | |
681 /* directory operations below */ | |
682 | |
683 int | |
684 usmb_mkdir(const char *dirname, mode_t mode) | |
685 { | |
686 char *url = make_url(dirname); | |
687 | |
688 if (url == NULL) | |
689 return -ENOMEM; | |
690 | |
691 int ret = smbc_getFunctionMkdir(ctx) (ctx, url, mode) ? -errno :… | |
692 free(url); | |
693 | |
694 return ret; | |
695 } | |
696 | |
697 int | |
698 usmb_rmdir(const char *dirname) | |
699 { | |
700 char *url = make_url(dirname); | |
701 if (url == NULL) | |
702 return -ENOMEM; | |
703 | |
704 int ret = smbc_getFunctionRmdir(ctx) (ctx, url) ? -errno : 0; | |
705 free(url); | |
706 | |
707 return ret; | |
708 } | |
709 | |
710 int | |
711 usmb_opendir(const char *dirname, struct fuse_file_info *fi) | |
712 { | |
713 char *url = make_url(dirname); | |
714 if (url == NULL) | |
715 return -ENOMEM; | |
716 | |
717 SMBCFILE *file = smbc_getFunctionOpendir(ctx) (ctx, url); | |
718 | |
719 int ret = (file == NULL) ? -errno : 0; | |
720 free(url); | |
721 | |
722 fi->fh = smbcfile_to_fd(file); | |
723 | |
724 return ret; | |
725 } | |
726 | |
727 int | |
728 usmb_readdir(const char *path, void *h, fuse_fill_dir_t filler, | |
729 off_t offset UNUSED, struct fuse_file_info *fi UNUSED) | |
730 { | |
731 SMBCCTX *ctx_ = NULL; | |
732 SMBCFILE *file = NULL; | |
733 char *url = NULL; | |
734 struct smbc_dirent *dirent; | |
735 struct stat stbuf; | |
736 int ret = 0; | |
737 | |
738 if (!create_smb_context(&ctx_)) | |
739 return -errno; | |
740 | |
741 do { | |
742 url = make_url(path); | |
743 if (url == NULL) { | |
744 ret = -ENOMEM; | |
745 break; | |
746 } | |
747 | |
748 file = smbc_getFunctionOpendir(ctx_) (ctx_, url); | |
749 if (file == NULL) { | |
750 ret = -errno; | |
751 break; | |
752 } | |
753 | |
754 smbc_getFunctionLseekdir(ctx_) (ctx_, file, 0); | |
755 | |
756 while (NULL != (dirent = smbc_getFunctionReaddir(ctx_) (… | |
757 memset(&stbuf, 0, sizeof(stbuf)); | |
758 /* required for at least OpenBSD for getcwd() to… | |
759 as described in the fuse_new(3) man page near… | |
760 stbuf.st_ino = arc4random(); | |
761 | |
762 switch (dirent->smbc_type) { | |
763 case SMBC_DIR: | |
764 stbuf.st_mode = S_IFDIR; | |
765 break; | |
766 case SMBC_FILE: | |
767 stbuf.st_mode = S_IFREG; | |
768 break; | |
769 case SMBC_LINK: | |
770 stbuf.st_mode = S_IFLNK; | |
771 break; | |
772 default: | |
773 break; | |
774 } | |
775 | |
776 if (1 == filler(h, dirent->name, &stbuf, 0)) { /… | |
777 ret = -1; | |
778 break; | |
779 } | |
780 } | |
781 } while (false /*CONSTCOND*/); | |
782 | |
783 if (file != NULL) | |
784 (void)smbc_getFunctionClosedir(ctx_) (ctx_, file); | |
785 | |
786 free(url); | |
787 destroy_smb_context(ctx_, 0); | |
788 | |
789 return ret; | |
790 } | |
791 | |
792 int | |
793 usmb_releasedir(const char *path UNUSED, struct fuse_file_info *fi) | |
794 { | |
795 SMBCFILE *file = fd_to_smbcfile(fi->fh); | |
796 | |
797 return (0 > smbc_getFunctionClosedir(ctx) (ctx, file)) ? -errno … | |
798 } | |
799 | |
800 int | |
801 usmb_setxattr(const char *path, const char *name, const char *value, | |
802 size_t size, int flags) | |
803 { | |
804 char *url = make_url(path); | |
805 if (url == NULL) | |
806 return -ENOMEM; | |
807 | |
808 int ret = smbc_getFunctionSetxattr(ctx) (ctx, url, name, | |
809 value, size, flags) ? -errno : 0; | |
810 free(url); | |
811 | |
812 return ret; | |
813 } | |
814 | |
815 int | |
816 usmb_getxattr(const char *path, const char *name, char *value, size_t si… | |
817 { | |
818 char *url = make_url(path); | |
819 if (url == NULL) | |
820 return -ENOMEM; | |
821 | |
822 int ret = smbc_getFunctionGetxattr(ctx) (ctx, url, name, | |
823 value, size) ? -errno : 0; | |
824 free(url); | |
825 | |
826 return ret; | |
827 } | |
828 | |
829 int | |
830 usmb_listxattr(const char *path, char *list, size_t size) | |
831 { | |
832 char *url = make_url(path); | |
833 if (url == NULL) | |
834 return -ENOMEM; | |
835 | |
836 int ret = smbc_getFunctionListxattr(ctx) (ctx, url, list, size) … | |
837 free(url); | |
838 | |
839 return ret; | |
840 } | |
841 | |
842 int | |
843 usmb_removexattr(const char *path, const char *name) | |
844 { | |
845 char *url = make_url(path); | |
846 if (url == NULL) | |
847 return -ENOMEM; | |
848 | |
849 int ret = smbc_getFunctionRemovexattr(ctx) (ctx, url, name) ? -e… | |
850 free(url); | |
851 | |
852 return ret; | |
853 } | |
854 | |
855 char * | |
856 make_url(const char *path) | |
857 { | |
858 size_t len; | |
859 char *p; | |
860 | |
861 /* no path or path is empty */ | |
862 if ((path == NULL) || (path[0] == '\0')) { | |
863 return xstrdup(sharename); | |
864 } else { | |
865 len = strlen(sharename) + strlen(path) + 1; | |
866 p = emalloc(len); | |
867 snprintf(p, len, "%s%s", sharename, path); | |
868 return p; | |
869 } | |
870 } | |
871 | |
872 static void | |
873 auth_fn(const char *srv UNUSED, const char *shr UNUSED, | |
874 char *wg, int wglen, char *un, int unlen, | |
875 char *pw, int pwlen) | |
876 { | |
877 /* snprintf is used in this way to behave similar to strlcpy(), … | |
878 | |
879 if (opt_domain != NULL) | |
880 snprintf(wg, wglen, "%s", opt_domain); | |
881 else if (wglen) | |
882 wg[0] = '\0'; /* no domain */ | |
883 | |
884 snprintf(un, unlen, "%s", opt_username); | |
885 snprintf(pw, pwlen, "%s", opt_password); | |
886 } | |
887 | |
888 void | |
889 destroy_smb_context(SMBCCTX *ctx_, int shutdown) | |
890 { | |
891 /* Samba frees the workgroup and user strings but we want to per… | |
892 smbc_setWorkgroup(ctx_, NULL); | |
893 smbc_setUser(ctx_, NULL); | |
894 smbc_free_context(ctx_, shutdown); | |
895 } | |
896 | |
897 bool | |
898 create_smb_context(SMBCCTX **pctx) | |
899 { | |
900 *pctx = smbc_new_context(); | |
901 | |
902 if (*pctx == NULL) { | |
903 perror("Cannot create SMB context"); | |
904 return false; | |
905 } | |
906 | |
907 smbc_setWorkgroup(*pctx, opt_domain); | |
908 smbc_setUser(*pctx, opt_username); | |
909 smbc_setTimeout(*pctx, 5000); | |
910 smbc_setFunctionAuthData(*pctx, auth_fn); | |
911 | |
912 if (smbc_init_context(*pctx) == NULL) { | |
913 perror("Cannot initialise SMB context"); | |
914 destroy_smb_context(*pctx, 1); | |
915 return false; | |
916 } | |
917 | |
918 return true; | |
919 } | |
920 | |
921 static void * | |
922 usmb_init(struct fuse_conn_info *conn UNUSED) | |
923 { | |
924 return NULL; | |
925 } | |
926 | |
927 static void | |
928 usmb_destroy(void *unused UNUSED) | |
929 { | |
930 } | |
931 | |
932 // probably won't (can't ?) implement these: | |
933 // readlink symlink flush fsync | |
934 | |
935 // no easy way of implementing these: | |
936 // access | |
937 | |
938 #define SET_ELEMENT(name,value) name = value | |
939 | |
940 static struct fuse_operations fuse_ops = { | |
941 SET_ELEMENT (.getattr, usmb_getattr), | |
942 SET_ELEMENT (.readlink, NULL), | |
943 SET_ELEMENT (.getdir, NULL), | |
944 SET_ELEMENT (.mknod, usmb_mknod), | |
945 SET_ELEMENT (.mkdir, usmb_mkdir), | |
946 SET_ELEMENT (.unlink, usmb_unlink), | |
947 SET_ELEMENT (.rmdir, usmb_rmdir), | |
948 SET_ELEMENT (.symlink, NULL), | |
949 SET_ELEMENT (.rename, usmb_rename), | |
950 SET_ELEMENT (.link, NULL), | |
951 SET_ELEMENT (.chmod, usmb_chmod), | |
952 SET_ELEMENT (.chown, NULL), // usmb_chown, --not implemented in libsmb… | |
953 SET_ELEMENT (.truncate, usmb_truncate), | |
954 SET_ELEMENT (.utime, usmb_utime), | |
955 SET_ELEMENT (.open, usmb_open), | |
956 SET_ELEMENT (.read, usmb_read), | |
957 SET_ELEMENT (.write, usmb_write), | |
958 SET_ELEMENT (.statfs, usmb_statfs), | |
959 SET_ELEMENT (.flush, NULL), | |
960 SET_ELEMENT (.release, usmb_release), | |
961 SET_ELEMENT (.fsync, NULL), | |
962 SET_ELEMENT (.setxattr, usmb_setxattr), | |
963 SET_ELEMENT (.getxattr, usmb_getxattr), | |
964 SET_ELEMENT (.listxattr, usmb_listxattr), | |
965 SET_ELEMENT (.removexattr, usmb_removexattr), | |
966 SET_ELEMENT (.opendir, usmb_opendir), | |
967 SET_ELEMENT (.readdir, usmb_readdir), | |
968 SET_ELEMENT (.releasedir, usmb_releasedir), | |
969 SET_ELEMENT (.fsyncdir, NULL), | |
970 SET_ELEMENT (.init, usmb_init), | |
971 SET_ELEMENT (.destroy, usmb_destroy), | |
972 SET_ELEMENT (.access, NULL), | |
973 SET_ELEMENT (.create, usmb_create), | |
974 SET_ELEMENT (.ftruncate, usmb_ftruncate), | |
975 SET_ELEMENT (.fgetattr, usmb_fgetattr), | |
976 SET_ELEMENT (.lock, NULL), // TODO: implement | |
977 SET_ELEMENT (.utimens, NULL), // TODO: implement | |
978 SET_ELEMENT (.bmap, NULL), // TODO: implement | |
979 }; | |
980 | |
981 static char * | |
982 create_share_name(const char *server_, const char *sharename) | |
983 { | |
984 /* len: + 2 for "/" and NUL terminator */ | |
985 size_t len = strlen("smb://") + strlen(server_) + strlen(sharena… | |
986 char *p; | |
987 | |
988 p = emalloc(len); | |
989 snprintf(p, len, "smb://%s/%s", server_, sharename); | |
990 | |
991 return p; | |
992 } | |
993 | |
994 static bool | |
995 check_credentials(void) | |
996 { | |
997 char *url = make_url(""); | |
998 if (url == NULL) { | |
999 errno = ENOMEM; | |
1000 return false; | |
1001 } | |
1002 | |
1003 struct stat stat_; | |
1004 bool ret = (0 == (smbc_getFunctionStat(ctx) (ctx, url, &stat_))); | |
1005 | |
1006 free_errno(url); | |
1007 | |
1008 return ret; | |
1009 } | |
1010 | |
1011 static bool | |
1012 get_context(void) | |
1013 { | |
1014 ctx = NULL; | |
1015 | |
1016 if (disconnect) | |
1017 return false; | |
1018 | |
1019 disconnect = 1; | |
1020 if (!create_smb_context(&ctx)) | |
1021 return false; | |
1022 | |
1023 if (!check_credentials()) { | |
1024 perror("Connection failed"); | |
1025 destroy_smb_context(ctx, 1); | |
1026 ctx = NULL; | |
1027 return NULL; | |
1028 } | |
1029 | |
1030 disconnect = 0; | |
1031 | |
1032 return (ctx != NULL); | |
1033 } | |
1034 | |
1035 void | |
1036 show_about(FILE *fp) | |
1037 { | |
1038 fprintf(fp, "susmb - mount SMB shares via FUSE and Samba\n" | |
1039 "\n" | |
1040 "Copyright (C) 2025 Hiltjo Posthuma.\n" | |
1041 "Copyright (C) 2006-2013 Geoff Johnstone.\n" | |
1042 "\n" | |
1043 "Licensed under the GNU General Public License.\n" | |
1044 "susmb comes with ABSOLUTELY NO WARRANTY; " | |
1045 "for details please see\n" | |
1046 "http://www.gnu.org/licenses/gpl.txt\n" | |
1047 "\n" | |
1048 "Please send bug reports, patches etc. to hiltjo@codemad… | |
1049 } | |
1050 | |
1051 void | |
1052 show_version(FILE *fp) | |
1053 { | |
1054 show_about(fp); | |
1055 fputc('\n', fp); | |
1056 fprintf(fp, "susmb version: %s\n" | |
1057 "FUSE version: %d.%d\n" | |
1058 "Samba version: %s\n", | |
1059 SUSMB_VERSION, | |
1060 FUSE_MAJOR_VERSION, FUSE_MINOR_VERSION, | |
1061 smbc_version()); | |
1062 } | |
1063 | |
1064 void | |
1065 usage(void) | |
1066 { | |
1067 fprintf(stdout, | |
1068 "Usage: %s [-dfv] [-o options] [-u user] [-g gid] <smb:/… | |
1069 "\n" | |
1070 "Options:\n" | |
1071 " -d Debug mode\n" | |
1072 " -f Foreground operation\n" | |
1073 " -o Additional FUSE options\n" | |
1074 " -u Privdrop to user and its group or uid\n" | |
1075 " -g Privdrop to group id\n" | |
1076 " -v Show program, FUSE and Samba versions\n", argv0); | |
1077 exit(1); | |
1078 } | |
1079 | |
1080 /* FUSE args are: | |
1081 * | |
1082 * argv[0] | |
1083 * -s | |
1084 * -d -- if debug mode requested | |
1085 * -f -- if foreground mode requested | |
1086 * -o ... -- if any mount options in the config file | |
1087 * mount point | |
1088 */ | |
1089 #define MAXARGS 12 | |
1090 void build_fuse_args(const char *options, const char *mountpoint, | |
1091 int debug, int nofork, | |
1092 int *out_argc, char ***out_argv) | |
1093 { | |
1094 static char SUSMB[] = "susmb"; | |
1095 static char MINUS_S[] = "-s"; | |
1096 static char MINUS_D[] = "-d"; | |
1097 static char MINUS_F[] = "-f"; | |
1098 static char MINUS_O[] = "-o"; | |
1099 static char *argv[MAXARGS]; | |
1100 int argc = 0; | |
1101 | |
1102 argv[argc++] = SUSMB; | |
1103 argv[argc++] = MINUS_S; | |
1104 | |
1105 if (debug) | |
1106 argv[argc++] = MINUS_D; | |
1107 | |
1108 if (nofork) | |
1109 argv[argc++] = MINUS_F; | |
1110 | |
1111 if ((options != NULL) && (options[0] != '\0')) { | |
1112 argv[argc++] = MINUS_O; | |
1113 argv[argc++] = (char *)options; | |
1114 } | |
1115 | |
1116 argv[argc++] = (char *)mountpoint; | |
1117 argv[argc] = NULL; | |
1118 | |
1119 *out_argc = argc; | |
1120 *out_argv = argv; | |
1121 } | |
1122 | |
1123 int usmb_fuse_main(int argc, char *argv[], | |
1124 const struct fuse_operations *op, size_t op_size, | |
1125 void *user_data) | |
1126 { | |
1127 struct fuse_args args = FUSE_ARGS_INIT(argc, argv); | |
1128 struct fuse *fuse = NULL; | |
1129 struct fuse_chan *chan = NULL; | |
1130 struct fuse_session *session = NULL; | |
1131 int fd, res = 1; | |
1132 int fflag = 0, sflag = 0; | |
1133 char *mountpoint = NULL; | |
1134 | |
1135 if (fuse_parse_cmdline(&args, &mountpoint, &sflag, &fflag) != 0) | |
1136 return 1; | |
1137 | |
1138 if (mountpoint == NULL || *mountpoint == '\0') { | |
1139 warnx("error: no mountpoint specified"); | |
1140 res = 2; | |
1141 goto out1; | |
1142 } | |
1143 | |
1144 chan = fuse_mount(mountpoint, &args); | |
1145 if (chan == NULL) { | |
1146 res = 4; | |
1147 goto out2; | |
1148 } | |
1149 | |
1150 fuse = fuse_new(chan, &args, op, op_size, user_data); | |
1151 if (fuse == NULL) { | |
1152 res = 3; | |
1153 goto out1; | |
1154 } | |
1155 | |
1156 /* daemonize */ | |
1157 if (!fflag) { | |
1158 switch (fork()) { | |
1159 case -1: | |
1160 res = 5; | |
1161 warn("fork"); | |
1162 goto out3; | |
1163 case 0: | |
1164 break; | |
1165 default: | |
1166 _exit(0); | |
1167 } | |
1168 | |
1169 if (setsid() == -1) { | |
1170 res = 5; | |
1171 warn("setsid"); | |
1172 goto out3; | |
1173 } | |
1174 | |
1175 (void)chdir("/"); /* nochdir */ | |
1176 | |
1177 /* noclose */ | |
1178 if ((fd = open("/dev/null", O_RDWR)) != -1) { | |
1179 (void)dup2(fd, STDIN_FILENO); | |
1180 (void)dup2(fd, STDOUT_FILENO); | |
1181 (void)dup2(fd, STDERR_FILENO); | |
1182 if (fd > 2) | |
1183 (void)close(fd); | |
1184 } | |
1185 } | |
1186 | |
1187 /* setup signal handlers: can only be used if privdrop is not us… | |
1188 if (!opt_privdrop) { | |
1189 session = fuse_get_session(fuse); | |
1190 if (fuse_set_signal_handlers(session) != 0) { | |
1191 res = 6; | |
1192 goto out3; | |
1193 } | |
1194 } | |
1195 | |
1196 /* privdrop */ | |
1197 if (opt_privdrop) { | |
1198 if (setresgid(opt_uid, opt_uid, opt_uid) == -1) | |
1199 err(1, "setresgid"); | |
1200 if (setresuid(opt_gid, opt_gid, opt_gid) == -1) | |
1201 err(1, "setresuid"); | |
1202 } | |
1203 | |
1204 res = fuse_loop(fuse); | |
1205 if (res) | |
1206 res = 8; | |
1207 | |
1208 if (!opt_privdrop) { | |
1209 if (session) | |
1210 fuse_remove_signal_handlers(session); | |
1211 } | |
1212 | |
1213 out3: | |
1214 if (chan) | |
1215 fuse_unmount(mountpoint, chan); | |
1216 out2: | |
1217 if (fuse) | |
1218 fuse_destroy(fuse); | |
1219 out1: | |
1220 return res; | |
1221 } | |
1222 | |
1223 int | |
1224 main(int argc, char **argv) | |
1225 { | |
1226 struct uri u; | |
1227 struct passwd *pw; | |
1228 char *tmp, *p; | |
1229 char **fuse_argv; | |
1230 int fuse_argc; | |
1231 int ch, ret = 1; | |
1232 long l; | |
1233 | |
1234 while ((ch = getopt(argc, argv, "hvVdfo:u:g:")) != -1) { | |
1235 switch (ch) { | |
1236 case 'd': | |
1237 opt_debug = 1; | |
1238 break; | |
1239 case 'f': | |
1240 opt_nofork = 1; | |
1241 break; | |
1242 case 'o': | |
1243 opt_options = xstrdup(optarg); | |
1244 break; | |
1245 case 'h': | |
1246 usage(); | |
1247 break; | |
1248 case 'u': | |
1249 opt_privdrop = 1; | |
1250 /* by username: use uid and gid from passwd entr… | |
1251 if ((pw = getpwnam(optarg)) != NULL) { | |
1252 opt_uid = pw->pw_uid; | |
1253 opt_gid = pw->pw_gid; | |
1254 } else { | |
1255 /* try to parse number */ | |
1256 errno = 0; | |
1257 l = strtol(optarg, NULL, 10); | |
1258 if (l <= 0 || errno) | |
1259 usage(); | |
1260 opt_uid = (uid_t)l; | |
1261 } | |
1262 break; | |
1263 case 'g': | |
1264 opt_privdrop = 1; | |
1265 /* parse gid as number */ | |
1266 errno = 0; | |
1267 l = strtol(optarg, NULL, 10); | |
1268 if (l <= 0 || errno) | |
1269 usage(); | |
1270 opt_gid = (gid_t)l; | |
1271 break; | |
1272 case 'v': | |
1273 case 'V': | |
1274 show_version(stdout); | |
1275 exit(0); | |
1276 break; | |
1277 default: | |
1278 usage(); | |
1279 } | |
1280 } | |
1281 | |
1282 argc -= optind; | |
1283 argv += optind; | |
1284 | |
1285 if (opt_privdrop && (opt_uid == 0 || opt_gid == 0)) | |
1286 usage(); | |
1287 | |
1288 /* password is read from enviroment variable. | |
1289 It is assumed the environment is secure */ | |
1290 if ((tmp = getenv("SMB_PASS")) != NULL) | |
1291 opt_password = xstrdup(tmp); | |
1292 | |
1293 /* options were succesfully parsed */ | |
1294 if (ch == '?' || ch == ':') { | |
1295 usage(); | |
1296 return 0; | |
1297 } | |
1298 | |
1299 if (argc != 2) | |
1300 usage(); | |
1301 | |
1302 /* parse URI */ | |
1303 tmp = xstrdup(argv[0]); | |
1304 if (uri_parse(tmp, &u) == -1) | |
1305 usage(); | |
1306 | |
1307 /* check required options and format */ | |
1308 if (strcmp(u.proto, "smb://") || | |
1309 u.userinfo[0] == '\0' || | |
1310 u.host[0] == '\0' || | |
1311 u.path[0] != '/') { | |
1312 usage(); | |
1313 } | |
1314 | |
1315 /* password in userinfo field is not allowed */ | |
1316 if (strchr(u.userinfo, ':')) { | |
1317 fprintf(stderr, "password must be specified via $SMB_PAS… | |
1318 usage(); | |
1319 } | |
1320 | |
1321 /* split domain\user if '\' is found */ | |
1322 if ((p = strchr(u.userinfo, '\\'))) { | |
1323 *p = '\0'; | |
1324 opt_domain = xstrdup(u.userinfo); | |
1325 opt_username = xstrdup(p + 1); | |
1326 } else { | |
1327 opt_domain = xstrdup(""); | |
1328 opt_username = xstrdup(u.userinfo); | |
1329 } | |
1330 | |
1331 opt_server = xstrdup(u.host); | |
1332 opt_share = xstrdup(u.path + 1); /* share name, "/Sharename" -> … | |
1333 free(tmp); | |
1334 | |
1335 opt_mountpoint = xstrdup(argv[1]); | |
1336 | |
1337 if (opt_mountpoint == NULL || opt_mountpoint[0] == '\0' || | |
1338 opt_server == NULL || opt_server[0] == '\0' || | |
1339 opt_share == NULL || opt_share[0] == '\0' || | |
1340 opt_username == NULL || opt_username[0] == '\0' || | |
1341 opt_password == NULL) { | |
1342 usage(); | |
1343 } | |
1344 | |
1345 if (unveil("/", "") == -1) | |
1346 err(1, "unveil"); | |
1347 /* required for daemonize mode and ignoring output */ | |
1348 if (unveil("/dev/null", "rw") == -1) | |
1349 err(1, "unveil"); | |
1350 /* read-write permissions to OpenBSD FUSE driver */ | |
1351 if (unveil("/dev/fuse0", "rw") == -1) | |
1352 err(1, "unveil"); | |
1353 /* (r)ead, (w)rite, e(x)ecute, (c)reate permissions to mountpoin… | |
1354 if (unveil(opt_mountpoint, "rwxc") == -1) | |
1355 err(1, "unveil"); | |
1356 /* lock further unveil calls */ | |
1357 if (unveil(NULL, NULL) == -1) | |
1358 err(1, "unveil"); | |
1359 | |
1360 sharename = create_share_name(opt_server, opt_share); | |
1361 if (sharename != NULL) { | |
1362 if (get_context()) { | |
1363 build_fuse_args(opt_options, opt_mountpoint, opt… | |
1364 ret = usmb_fuse_main(fuse_argc, fuse_argv, &fuse… | |
1365 destroy_smb_context(ctx, 1); | |
1366 } | |
1367 } | |
1368 | |
1369 free(sharename); | |
1370 clear_and_free(opt_password); | |
1371 free(opt_username); | |
1372 free(opt_domain); | |
1373 free(opt_options); | |
1374 free(opt_mountpoint); | |
1375 free(opt_share); | |
1376 free(opt_server); | |
1377 | |
1378 return ret; | |
1379 } |