Introduction
Introduction Statistics Contact Development Disclaimer Help
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 }
You are viewing proxied material from codemadness.org. The copyright of proxied material belongs to its original authors. Any comments or complaints in relation to proxied material should be directed to the original authors of the content concerned. Please see the disclaimer for more details.