blob: 4e724dbc3bf44169d28811e0ff3b3b2f66c45f23 [file] [log] [blame]
Steve Kondik2111ad72013-07-07 12:07:44 -07001/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
4
5 This program can be distributed under the terms of the GNU GPL.
6 See the file COPYING.
7*/
8/* This program does the mounting and unmounting of FUSE filesystems */
9
10#include <config.h>
11
12#include "mount_util.h"
13#include <stdio.h>
14#include <stdlib.h>
15#include <string.h>
16#include <ctype.h>
17#include <unistd.h>
18#include <getopt.h>
19#include <errno.h>
20#include <fcntl.h>
21#include <pwd.h>
22
23#ifdef __SOLARIS__
24#include <sys/mnttab.h>
25#else /* __SOLARIS__ */
26#include <grp.h>
27#include <mntent.h>
28#include <sys/fsuid.h>
29#endif /* __SOLARIS__ */
30
31#include <sys/wait.h>
32#include <sys/stat.h>
33#include <sys/mount.h>
34#include <sys/socket.h>
35#include <sys/utsname.h>
36
37#define FUSE_DEV_NEW "/dev/fuse"
38
39#ifndef MS_DIRSYNC
40#define MS_DIRSYNC 128
41#endif
42
Steve Kondik2111ad72013-07-07 12:07:44 -070043static const char *progname = "ntfs-3g-mount";
44
45static int mount_max = 1000;
46
47int drop_privs(void);
48int restore_privs(void);
49
50#ifdef __SOLARIS__
51
52/*
53 * fusermount is not implemented in fuse-lite for Solaris,
54 * only the minimal functions are provided.
55 */
56
57/*
58 * Solaris doesn't have setfsuid/setfsgid.
59 * This doesn't really matter anyway as this program shouldn't be made
60 * suid on Solaris. It should instead be used via a profile with the
61 * sys_mount privilege.
62 */
63
64int drop_privs(void)
65{
66 return (0);
67}
68
69int restore_privs(void)
70{
71 return (0);
72}
73
74#else /* __SOLARIS__ */
75
76static const char *get_user_name(void)
77{
78 struct passwd *pw = getpwuid(getuid());
79 if (pw != NULL && pw->pw_name != NULL)
80 return pw->pw_name;
81 else {
82 fprintf(stderr, "%s: could not determine username\n", progname);
83 return NULL;
84 }
85}
86
87int drop_privs(void)
88{
89 if (!getegid()) {
90
91 gid_t new_gid = getgid();
92
93 if (setresgid(-1, new_gid, getegid()) < 0) {
94 perror("priv drop: setresgid failed");
95 return -1;
96 }
97 if (getegid() != new_gid){
98 perror("dropping group privilege failed");
99 return -1;
100 }
101 }
102
103 if (!geteuid()) {
104
105 uid_t new_uid = getuid();
106
107 if (setresuid(-1, new_uid, geteuid()) < 0) {
108 perror("priv drop: setresuid failed");
109 return -1;
110 }
111 if (geteuid() != new_uid){
112 perror("dropping user privilege failed");
113 return -1;
114 }
115 }
116
117 return 0;
118}
119
120int restore_privs(void)
121{
122 if (geteuid()) {
123
124 uid_t ruid, euid, suid;
125
126 if (getresuid(&ruid, &euid, &suid) < 0) {
127 perror("priv restore: getresuid failed");
128 return -1;
129 }
130 if (setresuid(-1, suid, -1) < 0) {
131 perror("priv restore: setresuid failed");
132 return -1;
133 }
134 if (geteuid() != suid) {
135 perror("restoring privilege failed");
136 return -1;
137 }
138 }
139
140 if (getegid()) {
141
142 gid_t rgid, egid, sgid;
143
144 if (getresgid(&rgid, &egid, &sgid) < 0) {
145 perror("priv restore: getresgid failed");
146 return -1;
147 }
148 if (setresgid(-1, sgid, -1) < 0) {
149 perror("priv restore: setresgid failed");
150 return -1;
151 }
152 if (getegid() != sgid){
153 perror("restoring group privilege failed");
154 return -1;
155 }
156 }
157
158 return 0;
159}
160
161#ifndef IGNORE_MTAB
162static int add_mount(const char *source, const char *mnt, const char *type,
163 const char *opts)
164{
165 return fuse_mnt_add_mount(progname, source, mnt, type, opts);
166}
167
168static int count_fuse_fs(void)
169{
170 struct mntent *entp;
171 int count = 0;
172 const char *mtab = _PATH_MOUNTED;
173 FILE *fp = setmntent(mtab, "r");
174 if (fp == NULL) {
175 fprintf(stderr, "%s: failed to open %s: %s\n", progname, mtab,
176 strerror(errno));
177 return -1;
178 }
179 while ((entp = getmntent(fp)) != NULL) {
180 if (strcmp(entp->mnt_type, "fuse") == 0 ||
181 strncmp(entp->mnt_type, "fuse.", 5) == 0)
182 count ++;
183 }
184 endmntent(fp);
185 return count;
186}
187
188
189#else /* IGNORE_MTAB */
190static int count_fuse_fs()
191{
192 return 0;
193}
194
195static int add_mount(const char *source, const char *mnt, const char *type,
196 const char *opts)
197{
198 (void) source;
199 (void) mnt;
200 (void) type;
201 (void) opts;
202 return 0;
203}
204#endif /* IGNORE_MTAB */
205
206static int begins_with(const char *s, const char *beg)
207{
208 if (strncmp(s, beg, strlen(beg)) == 0)
209 return 1;
210 else
211 return 0;
212}
213
214struct mount_flags {
215 const char *opt;
216 unsigned long flag;
217 int on;
218 int safe;
219};
220
221static struct mount_flags mount_flags[] = {
222 {"rw", MS_RDONLY, 0, 1},
223 {"ro", MS_RDONLY, 1, 1},
224 {"suid", MS_NOSUID, 0, 0},
225 {"nosuid", MS_NOSUID, 1, 1},
226 {"dev", MS_NODEV, 0, 0},
227 {"nodev", MS_NODEV, 1, 1},
228 {"exec", MS_NOEXEC, 0, 1},
229 {"noexec", MS_NOEXEC, 1, 1},
230 {"async", MS_SYNCHRONOUS, 0, 1},
231 {"sync", MS_SYNCHRONOUS, 1, 1},
232 {"atime", MS_NOATIME, 0, 1},
233 {"noatime", MS_NOATIME, 1, 1},
234 {"dirsync", MS_DIRSYNC, 1, 1},
235 {NULL, 0, 0, 0}
236};
237
238static int find_mount_flag(const char *s, unsigned len, int *on, int *flag)
239{
240 int i;
241
242 for (i = 0; mount_flags[i].opt != NULL; i++) {
243 const char *opt = mount_flags[i].opt;
244 if (strlen(opt) == len && strncmp(opt, s, len) == 0) {
245 *on = mount_flags[i].on;
246 *flag = mount_flags[i].flag;
247 if (!mount_flags[i].safe && getuid() != 0) {
248 *flag = 0;
249 fprintf(stderr, "%s: unsafe option '%s' ignored\n",
250 progname, opt);
251 }
252 return 1;
253 }
254 }
255 return 0;
256}
257
258static int add_option(char **optsp, const char *opt, unsigned expand)
259{
260 char *newopts;
261 if (*optsp == NULL)
262 newopts = strdup(opt);
263 else {
264 unsigned oldsize = strlen(*optsp);
265 unsigned newsize = oldsize + 1 + strlen(opt) + expand + 1;
266 newopts = (char *) realloc(*optsp, newsize);
267 if (newopts)
268 sprintf(newopts + oldsize, ",%s", opt);
269 }
270 if (newopts == NULL) {
271 fprintf(stderr, "%s: failed to allocate memory\n", progname);
272 return -1;
273 }
274 *optsp = newopts;
275 return 0;
276}
277
278static int get_mnt_opts(int flags, char *opts, char **mnt_optsp)
279{
280 int i;
281 int l;
282
283 if (!(flags & MS_RDONLY) && add_option(mnt_optsp, "rw", 0) == -1)
284 return -1;
285
286 for (i = 0; mount_flags[i].opt != NULL; i++) {
287 if (mount_flags[i].on && (flags & mount_flags[i].flag) &&
288 add_option(mnt_optsp, mount_flags[i].opt, 0) == -1)
289 return -1;
290 }
291
292 if (add_option(mnt_optsp, opts, 0) == -1)
293 return -1;
294 /* remove comma from end of opts*/
295 l = strlen(*mnt_optsp);
296 if ((*mnt_optsp)[l-1] == ',')
297 (*mnt_optsp)[l-1] = '\0';
298 if (getuid() != 0) {
299 const char *user = get_user_name();
300 if (user == NULL)
301 return -1;
302
303 if (add_option(mnt_optsp, "user=", strlen(user)) == -1)
304 return -1;
305 strcat(*mnt_optsp, user);
306 }
307 return 0;
308}
309
310static int opt_eq(const char *s, unsigned len, const char *opt)
311{
312 if(strlen(opt) == len && strncmp(s, opt, len) == 0)
313 return 1;
314 else
315 return 0;
316}
317
318static int get_string_opt(const char *s, unsigned len, const char *opt,
319 char **val)
320{
321 unsigned opt_len = strlen(opt);
322
323 if (*val)
324 free(*val);
325 *val = (char *) malloc(len - opt_len + 1);
326 if (!*val) {
327 fprintf(stderr, "%s: failed to allocate memory\n", progname);
328 return 0;
329 }
330
331 memcpy(*val, s + opt_len, len - opt_len);
332 (*val)[len - opt_len] = '\0';
333 return 1;
334}
335
336static int do_mount(const char *mnt, char **typep, mode_t rootmode,
337 int fd, const char *opts, const char *dev, char **sourcep,
338 char **mnt_optsp)
339{
340 int res;
341 int flags = MS_NOSUID | MS_NODEV;
342 char *optbuf;
343 char *mnt_opts = NULL;
344 const char *s;
345 char *d;
346 char *fsname = NULL;
347 char *source = NULL;
348 char *type = NULL;
349 int blkdev = 0;
350
351 optbuf = (char *) malloc(strlen(opts) + 128);
352 if (!optbuf) {
353 fprintf(stderr, "%s: failed to allocate memory\n", progname);
354 return -1;
355 }
356
357 for (s = opts, d = optbuf; *s;) {
358 unsigned len;
359 const char *fsname_str = "fsname=";
360 for (len = 0; s[len] && s[len] != ','; len++);
361 if (begins_with(s, fsname_str)) {
362 if (!get_string_opt(s, len, fsname_str, &fsname))
363 goto err;
364 } else if (opt_eq(s, len, "blkdev")) {
365 blkdev = 1;
366 } else if (!begins_with(s, "fd=") &&
367 !begins_with(s, "rootmode=") &&
368 !begins_with(s, "user_id=") &&
369 !begins_with(s, "group_id=")) {
370 int on;
371 int flag;
372 int skip_option = 0;
373 if (opt_eq(s, len, "large_read")) {
374 struct utsname utsname;
375 unsigned kmaj, kmin;
376 res = uname(&utsname);
377 if (res == 0 &&
378 sscanf(utsname.release, "%u.%u", &kmaj, &kmin) == 2 &&
379 (kmaj > 2 || (kmaj == 2 && kmin > 4))) {
380 fprintf(stderr, "%s: note: 'large_read' mount option is "
381 "deprecated for %i.%i kernels\n", progname, kmaj, kmin);
382 skip_option = 1;
383 }
384 }
385 if (!skip_option) {
386 if (find_mount_flag(s, len, &on, &flag)) {
387 if (on)
388 flags |= flag;
389 else
390 flags &= ~flag;
391 } else {
392 memcpy(d, s, len);
393 d += len;
394 *d++ = ',';
395 }
396 }
397 }
398 s += len;
399 if (*s)
400 s++;
401 }
402 *d = '\0';
403 res = get_mnt_opts(flags, optbuf, &mnt_opts);
404 if (res == -1)
405 goto err;
406
407 sprintf(d, "fd=%i,rootmode=%o,user_id=%i,group_id=%i",
408 fd, rootmode, getuid(), getgid());
409
410 source = malloc((fsname ? strlen(fsname) : 0) + strlen(dev) + 32);
411
412 type = malloc(32);
413 if (!type || !source) {
414 fprintf(stderr, "%s: failed to allocate memory\n", progname);
415 goto err;
416 }
417
418 strcpy(type, blkdev ? "fuseblk" : "fuse");
419
420 if (fsname)
421 strcpy(source, fsname);
422 else
423 strcpy(source, dev);
424
425 if (restore_privs())
426 goto err;
427
428 res = mount(source, mnt, type, flags, optbuf);
429 if (res == -1 && errno == EINVAL) {
430 /* It could be an old version not supporting group_id */
431 sprintf(d, "fd=%i,rootmode=%o,user_id=%i", fd, rootmode, getuid());
432 res = mount(source, mnt, type, flags, optbuf);
433 }
434
435 if (drop_privs())
436 goto err;
437
438 if (res == -1) {
439 int errno_save = errno;
440 if (blkdev && errno == ENODEV && !fuse_mnt_check_fuseblk())
441 fprintf(stderr, "%s: 'fuseblk' support missing\n", progname);
442 else {
443 fprintf(stderr, "%s: mount failed: %s\n", progname, strerror(errno_save));
444 if (errno_save == EPERM)
445 fprintf(stderr, "User doesn't have privilege to mount. "
446 "For more information\nplease see: "
447 "http://tuxera.com/community/ntfs-3g-faq/#unprivileged\n");
448 }
449 goto err;
450 } else {
451 *sourcep = source;
452 *typep = type;
453 *mnt_optsp = mnt_opts;
454 }
455out:
456 free(fsname);
457 free(optbuf);
458 return res;
459err:
460 free(source);
461 free(type);
462 free(mnt_opts);
463 res = -1;
464 goto out;
465}
466
467static int check_perm(const char **mntp, struct stat *stbuf, int *currdir_fd,
468 int *mountpoint_fd)
469{
470 int res;
471 const char *mnt = *mntp;
472 const char *origmnt = mnt;
473
474 res = stat(mnt, stbuf);
475 if (res == -1) {
476 fprintf(stderr, "%s: failed to access mountpoint %s: %s\n",
477 progname, mnt, strerror(errno));
478 return -1;
479 }
480
481 /* No permission checking is done for root */
482 if (getuid() == 0)
483 return 0;
484
485 if (S_ISDIR(stbuf->st_mode)) {
486 *currdir_fd = open(".", O_RDONLY);
487 if (*currdir_fd == -1) {
488 fprintf(stderr, "%s: failed to open current directory: %s\n",
489 progname, strerror(errno));
490 return -1;
491 }
492 res = chdir(mnt);
493 if (res == -1) {
494 fprintf(stderr, "%s: failed to chdir to mountpoint: %s\n",
495 progname, strerror(errno));
496 return -1;
497 }
498 mnt = *mntp = ".";
499 res = lstat(mnt, stbuf);
500 if (res == -1) {
501 fprintf(stderr, "%s: failed to access mountpoint %s: %s\n",
502 progname, origmnt, strerror(errno));
503 return -1;
504 }
505
506 if ((stbuf->st_mode & S_ISVTX) && stbuf->st_uid != getuid()) {
507 fprintf(stderr, "%s: mountpoint %s not owned by user\n",
508 progname, origmnt);
509 return -1;
510 }
511
512 res = access(mnt, W_OK);
513 if (res == -1) {
514 fprintf(stderr, "%s: user has no write access to mountpoint %s\n",
515 progname, origmnt);
516 return -1;
517 }
518 } else if (S_ISREG(stbuf->st_mode)) {
519 static char procfile[256];
520 *mountpoint_fd = open(mnt, O_WRONLY);
521 if (*mountpoint_fd == -1) {
522 fprintf(stderr, "%s: failed to open %s: %s\n", progname, mnt,
523 strerror(errno));
524 return -1;
525 }
526 res = fstat(*mountpoint_fd, stbuf);
527 if (res == -1) {
528 fprintf(stderr, "%s: failed to access mountpoint %s: %s\n",
529 progname, mnt, strerror(errno));
530 return -1;
531 }
532 if (!S_ISREG(stbuf->st_mode)) {
533 fprintf(stderr, "%s: mountpoint %s is no longer a regular file\n",
534 progname, mnt);
535 return -1;
536 }
537
538 sprintf(procfile, "/proc/self/fd/%i", *mountpoint_fd);
539 *mntp = procfile;
540 } else {
541 fprintf(stderr,
542 "%s: mountpoint %s is not a directory or a regular file\n",
543 progname, mnt);
544 return -1;
545 }
546
547
548 return 0;
549}
550
551static int try_open(const char *dev, char **devp)
552{
553 int fd;
554
555 if (restore_privs())
556 return -1;
557 fd = open(dev, O_RDWR);
558 if (drop_privs())
559 return -1;
560 if (fd != -1) {
561 *devp = strdup(dev);
562 if (*devp == NULL) {
563 fprintf(stderr, "%s: failed to allocate memory\n", progname);
564 close(fd);
565 fd = -1;
566 }
567 } else if (errno == ENODEV ||
568 errno == ENOENT) /* check for ENOENT too, for the udev case */
569 return -2;
570 else {
571 fprintf(stderr, "%s: failed to open %s: %s\n", progname, dev,
572 strerror(errno));
573 }
574 return fd;
575}
576
577static int open_fuse_device(char **devp)
578{
579 int fd;
580
581 fd = try_open(FUSE_DEV_NEW, devp);
582 if (fd >= -1)
583 return fd;
584
585 fprintf(stderr, "%s: fuse device is missing, try 'modprobe fuse' as root\n",
586 progname);
587
588 return -1;
589}
590
591
592static int mount_fuse(const char *mnt, const char *opts)
593{
594 int res;
595 int fd;
596 char *dev;
597 struct stat stbuf;
598 char *type = NULL;
599 char *source = NULL;
600 char *mnt_opts = NULL;
601 const char *real_mnt = mnt;
602 int currdir_fd = -1;
603 int mountpoint_fd = -1;
604
605 fd = open_fuse_device(&dev);
606 if (fd == -1)
607 return -1;
608
609 if (getuid() != 0 && mount_max != -1) {
610 if (count_fuse_fs() >= mount_max) {
611 fprintf(stderr, "%s: too many mounted FUSE filesystems (%d+)\n",
612 progname, mount_max);
613 goto err;
614 }
615 }
616
617 res = check_perm(&real_mnt, &stbuf, &currdir_fd, &mountpoint_fd);
618 if (res != -1)
619 res = do_mount(real_mnt, &type, stbuf.st_mode & S_IFMT, fd, opts, dev,
620 &source, &mnt_opts);
621
622 if (currdir_fd != -1) {
623 __attribute__((unused))int ignored_fchdir_status =
624 fchdir(currdir_fd);
625 close(currdir_fd);
626 }
627 if (mountpoint_fd != -1)
628 close(mountpoint_fd);
629
630 if (res == -1)
631 goto err;
632
633 if (restore_privs())
634 goto err;
635
636 if (geteuid() == 0) {
637
638 if (setgroups(0, NULL) == -1) {
639 perror("priv drop: setgroups failed");
640 goto err;
641 }
642
643 res = add_mount(source, mnt, type, mnt_opts);
644 if (res == -1) {
645 umount2(mnt, 2); /* lazy umount */
646 drop_privs();
647 goto err;
648 }
649 }
650
651 if (drop_privs())
652 goto err;
653out:
654 free(source);
655 free(type);
656 free(mnt_opts);
657 free(dev);
658
659 return fd;
660err:
661 close(fd);
662 fd = -1;
663 goto out;
664}
665
666int fusermount(int unmount, int quiet, int lazy, const char *opts,
667 const char *origmnt)
668{
669 int res = -1;
670 char *mnt;
671 mode_t old_umask;
672
673 mnt = fuse_mnt_resolve_path(progname, origmnt);
674 if (mnt == NULL)
675 return -1;
676
677 old_umask = umask(033);
678
679 if (unmount) {
680
681 if (restore_privs())
682 goto out;
683
684 if (geteuid() == 0)
685 res = fuse_mnt_umount(progname, mnt, lazy);
686 else {
687 res = umount2(mnt, lazy ? 2 : 0);
688 if (res == -1 && !quiet)
689 fprintf(stderr, "%s: failed to unmount %s: %s\n", progname,
690 mnt, strerror(errno));
691 }
692
693 if (drop_privs())
694 res = -1;
695
696 } else
697 res = mount_fuse(mnt, opts);
698out:
699 umask(old_umask);
700 free(mnt);
701 return res;
702}
703
704#endif /* __SOLARIS__ */