Allow IPv6 address entry in tools>ping - Loosens valid character check
[tomato/davidwu.git] / release / src / router / ntfs-3g / libfuse-lite / fusermount.c
blob4e724dbc3bf44169d28811e0ff3b3b2f66c45f23
1 /*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
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 */
10 #include <config.h>
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>
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__ */
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>
37 #define FUSE_DEV_NEW "/dev/fuse"
39 #ifndef MS_DIRSYNC
40 #define MS_DIRSYNC 128
41 #endif
43 static const char *progname = "ntfs-3g-mount";
45 static int mount_max = 1000;
47 int drop_privs(void);
48 int restore_privs(void);
50 #ifdef __SOLARIS__
53 * fusermount is not implemented in fuse-lite for Solaris,
54 * only the minimal functions are provided.
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.
64 int drop_privs(void)
66 return (0);
69 int restore_privs(void)
71 return (0);
74 #else /* __SOLARIS__ */
76 static const char *get_user_name(void)
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;
87 int drop_privs(void)
89 if (!getegid()) {
91 gid_t new_gid = getgid();
93 if (setresgid(-1, new_gid, getegid()) < 0) {
94 perror("priv drop: setresgid failed");
95 return -1;
97 if (getegid() != new_gid){
98 perror("dropping group privilege failed");
99 return -1;
103 if (!geteuid()) {
105 uid_t new_uid = getuid();
107 if (setresuid(-1, new_uid, geteuid()) < 0) {
108 perror("priv drop: setresuid failed");
109 return -1;
111 if (geteuid() != new_uid){
112 perror("dropping user privilege failed");
113 return -1;
117 return 0;
120 int restore_privs(void)
122 if (geteuid()) {
124 uid_t ruid, euid, suid;
126 if (getresuid(&ruid, &euid, &suid) < 0) {
127 perror("priv restore: getresuid failed");
128 return -1;
130 if (setresuid(-1, suid, -1) < 0) {
131 perror("priv restore: setresuid failed");
132 return -1;
134 if (geteuid() != suid) {
135 perror("restoring privilege failed");
136 return -1;
140 if (getegid()) {
142 gid_t rgid, egid, sgid;
144 if (getresgid(&rgid, &egid, &sgid) < 0) {
145 perror("priv restore: getresgid failed");
146 return -1;
148 if (setresgid(-1, sgid, -1) < 0) {
149 perror("priv restore: setresgid failed");
150 return -1;
152 if (getegid() != sgid){
153 perror("restoring group privilege failed");
154 return -1;
158 return 0;
161 #ifndef IGNORE_MTAB
162 static int add_mount(const char *source, const char *mnt, const char *type,
163 const char *opts)
165 return fuse_mnt_add_mount(progname, source, mnt, type, opts);
168 static int count_fuse_fs(void)
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;
179 while ((entp = getmntent(fp)) != NULL) {
180 if (strcmp(entp->mnt_type, "fuse") == 0 ||
181 strncmp(entp->mnt_type, "fuse.", 5) == 0)
182 count ++;
184 endmntent(fp);
185 return count;
189 #else /* IGNORE_MTAB */
190 static int count_fuse_fs()
192 return 0;
195 static int add_mount(const char *source, const char *mnt, const char *type,
196 const char *opts)
198 (void) source;
199 (void) mnt;
200 (void) type;
201 (void) opts;
202 return 0;
204 #endif /* IGNORE_MTAB */
206 static int begins_with(const char *s, const char *beg)
208 if (strncmp(s, beg, strlen(beg)) == 0)
209 return 1;
210 else
211 return 0;
214 struct mount_flags {
215 const char *opt;
216 unsigned long flag;
217 int on;
218 int safe;
221 static 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}
238 static int find_mount_flag(const char *s, unsigned len, int *on, int *flag)
240 int i;
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);
252 return 1;
255 return 0;
258 static int add_option(char **optsp, const char *opt, unsigned expand)
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);
270 if (newopts == NULL) {
271 fprintf(stderr, "%s: failed to allocate memory\n", progname);
272 return -1;
274 *optsp = newopts;
275 return 0;
278 static int get_mnt_opts(int flags, char *opts, char **mnt_optsp)
280 int i;
281 int l;
283 if (!(flags & MS_RDONLY) && add_option(mnt_optsp, "rw", 0) == -1)
284 return -1;
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;
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;
303 if (add_option(mnt_optsp, "user=", strlen(user)) == -1)
304 return -1;
305 strcat(*mnt_optsp, user);
307 return 0;
310 static int opt_eq(const char *s, unsigned len, const char *opt)
312 if(strlen(opt) == len && strncmp(s, opt, len) == 0)
313 return 1;
314 else
315 return 0;
318 static int get_string_opt(const char *s, unsigned len, const char *opt,
319 char **val)
321 unsigned opt_len = strlen(opt);
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;
331 memcpy(*val, s + opt_len, len - opt_len);
332 (*val)[len - opt_len] = '\0';
333 return 1;
336 static 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)
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;
351 optbuf = (char *) malloc(strlen(opts) + 128);
352 if (!optbuf) {
353 fprintf(stderr, "%s: failed to allocate memory\n", progname);
354 return -1;
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;
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++ = ',';
398 s += len;
399 if (*s)
400 s++;
402 *d = '\0';
403 res = get_mnt_opts(flags, optbuf, &mnt_opts);
404 if (res == -1)
405 goto err;
407 sprintf(d, "fd=%i,rootmode=%o,user_id=%i,group_id=%i",
408 fd, rootmode, getuid(), getgid());
410 source = malloc((fsname ? strlen(fsname) : 0) + strlen(dev) + 32);
412 type = malloc(32);
413 if (!type || !source) {
414 fprintf(stderr, "%s: failed to allocate memory\n", progname);
415 goto err;
418 strcpy(type, blkdev ? "fuseblk" : "fuse");
420 if (fsname)
421 strcpy(source, fsname);
422 else
423 strcpy(source, dev);
425 if (restore_privs())
426 goto err;
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);
435 if (drop_privs())
436 goto err;
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");
449 goto err;
450 } else {
451 *sourcep = source;
452 *typep = type;
453 *mnt_optsp = mnt_opts;
455 out:
456 free(fsname);
457 free(optbuf);
458 return res;
459 err:
460 free(source);
461 free(type);
462 free(mnt_opts);
463 res = -1;
464 goto out;
467 static int check_perm(const char **mntp, struct stat *stbuf, int *currdir_fd,
468 int *mountpoint_fd)
470 int res;
471 const char *mnt = *mntp;
472 const char *origmnt = mnt;
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;
481 /* No permission checking is done for root */
482 if (getuid() == 0)
483 return 0;
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;
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;
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;
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;
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;
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;
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;
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;
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;
548 return 0;
551 static int try_open(const char *dev, char **devp)
553 int fd;
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;
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));
574 return fd;
577 static int open_fuse_device(char **devp)
579 int fd;
581 fd = try_open(FUSE_DEV_NEW, devp);
582 if (fd >= -1)
583 return fd;
585 fprintf(stderr, "%s: fuse device is missing, try 'modprobe fuse' as root\n",
586 progname);
588 return -1;
592 static int mount_fuse(const char *mnt, const char *opts)
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;
605 fd = open_fuse_device(&dev);
606 if (fd == -1)
607 return -1;
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;
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);
622 if (currdir_fd != -1) {
623 __attribute__((unused))int ignored_fchdir_status =
624 fchdir(currdir_fd);
625 close(currdir_fd);
627 if (mountpoint_fd != -1)
628 close(mountpoint_fd);
630 if (res == -1)
631 goto err;
633 if (restore_privs())
634 goto err;
636 if (geteuid() == 0) {
638 if (setgroups(0, NULL) == -1) {
639 perror("priv drop: setgroups failed");
640 goto err;
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;
651 if (drop_privs())
652 goto err;
653 out:
654 free(source);
655 free(type);
656 free(mnt_opts);
657 free(dev);
659 return fd;
660 err:
661 close(fd);
662 fd = -1;
663 goto out;
666 int fusermount(int unmount, int quiet, int lazy, const char *opts,
667 const char *origmnt)
669 int res = -1;
670 char *mnt;
671 mode_t old_umask;
673 mnt = fuse_mnt_resolve_path(progname, origmnt);
674 if (mnt == NULL)
675 return -1;
677 old_umask = umask(033);
679 if (unmount) {
681 if (restore_privs())
682 goto out;
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));
693 if (drop_privs())
694 res = -1;
696 } else
697 res = mount_fuse(mnt, opts);
698 out:
699 umask(old_umask);
700 free(mnt);
701 return res;
704 #endif /* __SOLARIS__ */