Bug Summary

File:contrib/fuse-util/fusermount.c
Location:line 971, column 9
Description:Potential memory leak

Annotated Source Code

1/*
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(*__errno_location ()).h>
20#include <fcntl.h>
21#include <pwd.h>
22#include <limits.h>
23#include <mntent.h>
24#include <sys/wait.h>
25#include <sys/stat.h>
26#include <sys/mount.h>
27#include <sys/fsuid.h>
28#include <sys/socket.h>
29#include <sys/utsname.h>
30#include <sched.h>
31
32#define FUSE_DEVFD_ENV"_FUSE_DEVFD" "_FUSE_DEVFD"
33#define FUSE_COMMFD_ENV"_FUSE_COMMFD" "_FUSE_COMMFD"
34
35#define FUSE_DEV_OLD"/proc/fs/fuse/dev" "/proc/fs/fuse/dev"
36#define FUSE_DEV_NEW"/dev/fuse" "/dev/fuse"
37#define FUSE_VERSION_FILE_OLD"/proc/fs/fuse/version" "/proc/fs/fuse/version"
38#define FUSE_CONF"/etc/fuse.conf" "/etc/fuse.conf"
39
40#ifndef MS_DIRSYNCMS_DIRSYNC
41#define MS_DIRSYNCMS_DIRSYNC 128
42#endif
43#ifndef MS_RECMS_REC
44#define MS_RECMS_REC 16384
45#endif
46#ifndef MS_PRIVATEMS_PRIVATE
47#define MS_PRIVATEMS_PRIVATE (1<<18)
48#endif
49
50static const char *progname;
51
52static int user_allow_other = 0;
53static int mount_max = 1000;
54
55static const char *get_user_name(void)
56{
57 struct passwd *pw = getpwuid(getuid());
58 if (pw != NULL((void*)0) && pw->pw_name != NULL((void*)0))
59 return pw->pw_name;
60 else {
61 fprintf(stderrstderr, "%s: could not determine username\n", progname);
62 return NULL((void*)0);
63 }
64}
65
66static uid_t oldfsuid;
67static gid_t oldfsgid;
68
69static void drop_privs(void)
70{
71 if (getuid() != 0) {
72 oldfsuid = setfsuid(getuid());
73 oldfsgid = setfsgid(getgid());
74 }
75}
76
77static void restore_privs(void)
78{
79 if (getuid() != 0) {
80 setfsuid(oldfsuid);
81 setfsgid(oldfsgid);
82 }
83}
84
85#ifndef IGNORE_MTAB
86/*
87 * Make sure that /etc/mtab is checked and updated atomically
88 */
89static int lock_umount(void)
90{
91 const char *mtab_lock = _PATH_MOUNTED"/etc/mtab" ".fuselock";
92 int mtablock;
93 int res;
94 struct stat mtab_stat;
95
96 /* /etc/mtab could be a symlink to /proc/mounts */
97 if (lstat(_PATH_MOUNTED"/etc/mtab", &mtab_stat) == 0 && S_ISLNK(mtab_stat.st_mode)((((mtab_stat.st_mode)) & 0170000) == (0120000)))
98 return -1;
99
100 mtablock = open(mtab_lock, O_RDWR02 | O_CREAT0100, 0600);
101 if (mtablock == -1) {
102 fprintf(stderrstderr, "%s: unable to open fuse lock file: %s\n",
103 progname, strerror(errno(*__errno_location ())));
104 return -1;
105 }
106 res = lockf(mtablock, F_LOCK1, 0);
107 if (res < 0) {
108 fprintf(stderrstderr, "%s: error getting lock: %s\n", progname,
109 strerror(errno(*__errno_location ())));
110 close(mtablock);
111 return -1;
112 }
113
114 return mtablock;
115}
116
117static void unlock_umount(int mtablock)
118{
119 if (mtablock >= 0) {
120 int res;
121
122 res = lockf(mtablock, F_ULOCK0, 0);
123 if (res < 0) {
124 fprintf(stderrstderr, "%s: error releasing lock: %s\n",
125 progname, strerror(errno(*__errno_location ())));
126 }
127 close(mtablock);
128 }
129}
130
131static int add_mount(const char *source, const char *mnt, const char *type,
132 const char *opts)
133{
134 return fuse_mnt_add_mount(progname, source, mnt, type, opts);
135}
136
137static int may_unmount(const char *mnt, int quiet)
138{
139 struct mntent *entp;
140 FILE *fp;
141 const char *user = NULL((void*)0);
142 char uidstr[32];
143 unsigned uidlen = 0;
144 int found;
145 const char *mtab = _PATH_MOUNTED"/etc/mtab";
146
147 user = get_user_name();
148 if (user == NULL((void*)0))
149 return -1;
150
151 fp = setmntent(mtab, "r");
152 if (fp == NULL((void*)0)) {
153 fprintf(stderrstderr, "%s: failed to open %s: %s\n", progname, mtab,
154 strerror(errno(*__errno_location ())));
155 return -1;
156 }
157
158 uidlen = sprintf(uidstr, "%u", getuid());
159
160 found = 0;
161 while ((entp = getmntent(fp)) != NULL((void*)0)) {
162 if (!found && strcmp(entp->mnt_dir, mnt) == 0 &&
163 (strcmp(entp->mnt_type, "fuse") == 0 ||
164 strcmp(entp->mnt_type, "fuseblk") == 0 ||
165 strncmp(entp->mnt_type, "fuse.", 5) == 0 ||
166 strncmp(entp->mnt_type, "fuseblk.", 8) == 0)) {
167 char *p = strstr(entp->mnt_opts, "user=");
168 if (p &&
169 (p == entp->mnt_opts || *(p-1) == ',') &&
170 strcmp(p + 5, user) == 0) {
171 found = 1;
172 break;
173 }
174 /* /etc/mtab is a link pointing to
175 /proc/mounts: */
176 else if ((p =
177 strstr(entp->mnt_opts, "user_id=")) &&
178 (p == entp->mnt_opts ||
179 *(p-1) == ',') &&
180 strncmp(p + 8, uidstr, uidlen) == 0 &&
181 (*(p+8+uidlen) == ',' ||
182 *(p+8+uidlen) == '\0')) {
183 found = 1;
184 break;
185 }
186 }
187 }
188 endmntent(fp);
189
190 if (!found) {
191 if (!quiet)
192 fprintf(stderrstderr,
193 "%s: entry for %s not found in %s\n",
194 progname, mnt, mtab);
195 return -1;
196 }
197
198 return 0;
199}
200
201/*
202 * Check whether the file specified in "fusermount -u" is really a
203 * mountpoint and not a symlink. This is necessary otherwise the user
204 * could move the mountpoint away and replace it with a symlink
205 * pointing to an arbitrary mount, thereby tricking fusermount into
206 * unmounting that (umount(2) will follow symlinks).
207 *
208 * This is the child process running in a separate mount namespace, so
209 * we don't mess with the global namespace and if the process is
210 * killed for any reason, mounts are automatically cleaned up.
211 *
212 * First make sure nothing is propagated back into the parent
213 * namespace by marking all mounts "private".
214 *
215 * Then bind mount parent onto a stable base where the user can't move
216 * it around.
217 *
218 * Finally check /proc/mounts for an entry matching the requested
219 * mountpoint. If it's found then we are OK, and the user can't move
220 * it around within the parent directory as rename() will return
221 * EBUSY. Be careful to ignore any mounts that existed before the
222 * bind.
223 */
224static int check_is_mount_child(void *p)
225{
226 const char **a = p;
227 const char *last = a[0];
228 const char *mnt = a[1];
229 int res;
230 const char *procmounts = "/proc/mounts";
231 int found;
232 FILE *fp;
233 struct mntent *entp;
234 int count;
235
236 res = mount("", "/", "", MS_PRIVATEMS_PRIVATE | MS_RECMS_REC, NULL((void*)0));
237 if (res == -1) {
238 fprintf(stderrstderr, "%s: failed to mark mounts private: %s\n",
239 progname, strerror(errno(*__errno_location ())));
240 return 1;
241 }
242
243 fp = setmntent(procmounts, "r");
244 if (fp == NULL((void*)0)) {
245 fprintf(stderrstderr, "%s: failed to open %s: %s\n", progname,
246 procmounts, strerror(errno(*__errno_location ())));
247 return 1;
248 }
249
250 count = 0;
251 while (getmntent(fp) != NULL((void*)0))
252 count++;
253 endmntent(fp);
254
255 fp = setmntent(procmounts, "r");
256 if (fp == NULL((void*)0)) {
257 fprintf(stderrstderr, "%s: failed to open %s: %s\n", progname,
258 procmounts, strerror(errno(*__errno_location ())));
259 return 1;
260 }
261
262 res = mount(".", "/", "", MS_BINDMS_BIND | MS_RECMS_REC, NULL((void*)0));
263 if (res == -1) {
264 fprintf(stderrstderr, "%s: failed to bind parent to /: %s\n",
265 progname, strerror(errno(*__errno_location ())));
266 return 1;
267 }
268
269 found = 0;
270 while ((entp = getmntent(fp)) != NULL((void*)0)) {
271 if (count > 0) {
272 count--;
273 continue;
274 }
275 if (entp->mnt_dir[0] == '/' &&
276 strcmp(entp->mnt_dir + 1, last) == 0) {
277 found = 1;
278 break;
279 }
280 }
281 endmntent(fp);
282
283 if (!found) {
284 fprintf(stderrstderr, "%s: %s not mounted\n", progname, mnt);
285 return 1;
286 }
287
288 return 0;
289}
290
291static pid_t clone_newns(void *a)
292{
293 char buf[131072];
294 char *stack = buf + (sizeof(buf) / 2 - ((size_t) buf & 15));
295
296#ifdef __ia64__
297 extern int __clone2(int (*fn)(void *),
298 void *child_stack_base, size_t stack_size,
299 int flags, void *arg, pid_t *ptid,
300 void *tls, pid_t *ctid);
301
302 return __clone2(check_is_mount_child, stack, sizeof(buf) / 2,
303 CLONE_NEWNS0x00020000, a, NULL((void*)0), NULL((void*)0), NULL((void*)0));
304#else
305 return clone(check_is_mount_child, stack, CLONE_NEWNS0x00020000, a);
306#endif
307}
308
309static int check_is_mount(const char *last, const char *mnt)
310{
311 pid_t pid, p;
312 int status;
313 const char *a[2] = { last, mnt };
314
315 pid = clone_newns((void *) a);
316 if (pid == (pid_t) -1) {
317 fprintf(stderrstderr, "%s: failed to clone namespace: %s\n",
318 progname, strerror(errno(*__errno_location ())));
319 return -1;
320 }
321 p = waitpid(pid, &status, __WCLONE0x80000000);
322 if (p == (pid_t) -1) {
323 fprintf(stderrstderr, "%s: waitpid failed: %s\n",
324 progname, strerror(errno(*__errno_location ())));
325 return -1;
326 }
327 if (!WIFEXITED(status)((((__extension__ (((union { __typeof(status) __in; int __i; }
) { .__in = (status) }).__i))) & 0x7f) == 0)
) {
328 fprintf(stderrstderr, "%s: child terminated abnormally (status %i)\n",
329 progname, status);
330 return -1;
331 }
332 if (WEXITSTATUS(status)((((__extension__ (((union { __typeof(status) __in; int __i; }
) { .__in = (status) }).__i))) & 0xff00) >> 8)
!= 0)
333 return -1;
334
335 return 0;
336}
337
338static int chdir_to_parent(char *copy, const char **lastp)
339{
340 char *tmp;
341 const char *parent;
342 char buf[65536];
343 int res;
344
345 tmp = strrchr(copy, '/');
346 if (tmp == NULL((void*)0) || tmp[1] == '\0') {
347 fprintf(stderrstderr, "%s: internal error: invalid abs path: <%s>\n",
348 progname, copy);
349 return -1;
350 }
351 if (tmp != copy) {
352 *tmp = '\0';
353 parent = copy;
354 *lastp = tmp + 1;
355 } else if (tmp[1] != '\0') {
356 *lastp = tmp + 1;
357 parent = "/";
358 } else {
359 *lastp = ".";
360 parent = "/";
361 }
362
363 res = chdir(parent);
364 if (res == -1) {
365 fprintf(stderrstderr, "%s: failed to chdir to %s: %s\n",
366 progname, parent, strerror(errno(*__errno_location ())));
367 return -1;
368 }
369
370 if (getcwd(buf, sizeof(buf)) == NULL((void*)0)) {
371 fprintf(stderrstderr, "%s: failed to obtain current directory: %s\n",
372 progname, strerror(errno(*__errno_location ())));
373 return -1;
374 }
375 if (strcmp(buf, parent) != 0) {
376 fprintf(stderrstderr, "%s: mountpoint moved (%s -> %s)\n", progname,
377 parent, buf);
378 return -1;
379
380 }
381
382 return 0;
383}
384
385static int unmount_fuse_locked(const char *mnt, int quiet, int lazy)
386{
387 char *copy;
388 const char *last;
389 int res;
390
391 if (getuid() != 0) {
392 res = may_unmount(mnt, quiet);
393 if (res == -1)
394 return -1;
395 }
396
397 copy = strdup(mnt);
398 if (copy == NULL((void*)0)) {
399 fprintf(stderrstderr, "%s: failed to allocate memory\n", progname);
400 return -1;
401 }
402
403 res = chdir_to_parent(copy, &last);
404 if (res == -1)
405 goto out;
406
407 res = check_is_mount(last, mnt);
408 if (res == -1)
409 goto out;
410
411 res = fuse_mnt_umount(progname, mnt, last, lazy);
412
413out:
414 free(copy);
415
416 return res;
417}
418
419static int unmount_fuse(const char *mnt, int quiet, int lazy)
420{
421 int res;
422 int mtablock = lock_umount();
423
424 res = unmount_fuse_locked(mnt, quiet, lazy);
425 unlock_umount(mtablock);
426
427 return res;
428}
429
430static int count_fuse_fs(void)
431{
432 struct mntent *entp;
433 int count = 0;
434 const char *mtab = _PATH_MOUNTED"/etc/mtab";
435 FILE *fp = setmntent(mtab, "r");
436 if (fp == NULL((void*)0)) {
437 fprintf(stderrstderr, "%s: failed to open %s: %s\n", progname, mtab,
438 strerror(errno(*__errno_location ())));
439 return -1;
440 }
441 while ((entp = getmntent(fp)) != NULL((void*)0)) {
442 if (strcmp(entp->mnt_type, "fuse") == 0 ||
443 strncmp(entp->mnt_type, "fuse.", 5) == 0)
444 count ++;
445 }
446 endmntent(fp);
447 return count;
448}
449
450
451#else /* IGNORE_MTAB */
452static int count_fuse_fs()
453{
454 return 0;
455}
456
457static int add_mount(const char *source, const char *mnt, const char *type,
458 const char *opts)
459{
460 (void) source;
461 (void) mnt;
462 (void) type;
463 (void) opts;
464 return 0;
465}
466
467static int unmount_fuse(const char *mnt, int quiet, int lazy)
468{
469 return fuse_mnt_umount(progname, mnt, mnt, lazy);
470}
471#endif /* IGNORE_MTAB */
472
473static void strip_line(char *line)
474{
475 char *s = strchr(line, '#');
476 if (s != NULL((void*)0))
477 s[0] = '\0';
478 for (s = line + strlen(line) - 1;
479 s >= line && isspace((unsigned char) *s)((*__ctype_b_loc ())[(int) (((unsigned char) *s))] & (unsigned
short int) _ISspace)
; s--);
480 s[1] = '\0';
481 for (s = line; isspace((unsigned char) *s)((*__ctype_b_loc ())[(int) (((unsigned char) *s))] & (unsigned
short int) _ISspace)
; s++);
482 if (s != line)
483 memmove(line, s, strlen(s)+1);
484}
485
486static void parse_line(char *line, int linenum)
487{
488 int tmp;
489 if (strcmp(line, "user_allow_other") == 0)
490 user_allow_other = 1;
491 else if (sscanf(line, "mount_max = %i", &tmp) == 1)
492 mount_max = tmp;
493 else if(line[0])
494 fprintf(stderrstderr,
495 "%s: unknown parameter in %s at line %i: '%s'\n",
496 progname, FUSE_CONF"/etc/fuse.conf", linenum, line);
497}
498
499static void read_conf(void)
500{
501 FILE *fp = fopen(FUSE_CONF"/etc/fuse.conf", "r");
502 if (fp != NULL((void*)0)) {
503 int linenum = 1;
504 char line[256];
505 int isnewline = 1;
506 while (fgets(line, sizeof(line), fp) != NULL((void*)0)) {
507 if (isnewline) {
508 if (strlen(line) && line[strlen(line)-1] == '\n') {
509 strip_line(line);
510 parse_line(line, linenum);
511 } else {
512 isnewline = 0;
513 }
514 } else if(strlen(line) && line[strlen(line)-1] == '\n') {
515 fprintf(stderrstderr, "%s: reading %s: line %i too long\n", progname, FUSE_CONF"/etc/fuse.conf", linenum);
516
517 isnewline = 1;
518 }
519 if (isnewline)
520 linenum ++;
521 }
522 if (!isnewline) {
523 fprintf(stderrstderr, "%s: reading %s: missing newline at end of file\n", progname, FUSE_CONF"/etc/fuse.conf");
524
525 }
526 fclose(fp);
527 } else if (errno(*__errno_location ()) != ENOENT2) {
528 fprintf(stderrstderr, "%s: failed to open %s: %s\n",
529 progname, FUSE_CONF"/etc/fuse.conf", strerror(errno(*__errno_location ())));
530 }
531}
532
533static int begins_with(const char *s, const char *beg)
534{
535 if (strncmp(s, beg, strlen(beg)) == 0)
536 return 1;
537 else
538 return 0;
539}
540
541struct mount_flags {
542 const char *opt;
543 unsigned long flag;
544 int on;
545 int safe;
546};
547
548static struct mount_flags mount_flags[] = {
549 {"rw", MS_RDONLYMS_RDONLY, 0, 1},
550 {"ro", MS_RDONLYMS_RDONLY, 1, 1},
551 {"suid", MS_NOSUIDMS_NOSUID, 0, 0},
552 {"nosuid", MS_NOSUIDMS_NOSUID, 1, 1},
553 {"dev", MS_NODEVMS_NODEV, 0, 0},
554 {"nodev", MS_NODEVMS_NODEV, 1, 1},
555 {"exec", MS_NOEXECMS_NOEXEC, 0, 1},
556 {"noexec", MS_NOEXECMS_NOEXEC, 1, 1},
557 {"async", MS_SYNCHRONOUSMS_SYNCHRONOUS, 0, 1},
558 {"sync", MS_SYNCHRONOUSMS_SYNCHRONOUS, 1, 1},
559 {"atime", MS_NOATIMEMS_NOATIME, 0, 1},
560 {"noatime", MS_NOATIMEMS_NOATIME, 1, 1},
561 {"dirsync", MS_DIRSYNCMS_DIRSYNC, 1, 1},
562 {NULL((void*)0), 0, 0, 0}
563};
564
565static int find_mount_flag(const char *s, unsigned len, int *on, int *flag)
566{
567 int i;
568
569 for (i = 0; mount_flags[i].opt != NULL((void*)0); i++) {
570 const char *opt = mount_flags[i].opt;
571 if (strlen(opt) == len && strncmp(opt, s, len) == 0) {
572 *on = mount_flags[i].on;
573 *flag = mount_flags[i].flag;
574 if (!mount_flags[i].safe && getuid() != 0) {
575 *flag = 0;
576 fprintf(stderrstderr,
577 "%s: unsafe option %s ignored\n",
578 progname, opt);
579 }
580 return 1;
581 }
582 }
583 return 0;
584}
585
586static int add_option(char **optsp, const char *opt, unsigned expand)
587{
588 char *newopts;
589 if (*optsp == NULL((void*)0))
590 newopts = strdup(opt);
591 else {
592 unsigned oldsize = strlen(*optsp);
593 unsigned newsize = oldsize + 1 + strlen(opt) + expand + 1;
594 newopts = (char *) realloc(*optsp, newsize);
595 if (newopts)
596 sprintf(newopts + oldsize, ",%s", opt);
597 }
598 if (newopts == NULL((void*)0)) {
599 fprintf(stderrstderr, "%s: failed to allocate memory\n", progname);
600 return -1;
601 }
602 *optsp = newopts;
603 return 0;
604}
605
606static int get_mnt_opts(int flags, char *opts, char **mnt_optsp)
607{
608 int i;
609 size_t l;
610
611 if (!(flags & MS_RDONLYMS_RDONLY) && add_option(mnt_optsp, "rw", 0) == -1)
612 return -1;
613
614 for (i = 0; mount_flags[i].opt != NULL((void*)0); i++) {
615 if (mount_flags[i].on && (flags & mount_flags[i].flag) &&
616 add_option(mnt_optsp, mount_flags[i].opt, 0) == -1)
617 return -1;
618 }
619
620 if (add_option(mnt_optsp, opts, 0) == -1)
621 return -1;
622 /* remove comma from end of opts*/
623 l = strlen(*mnt_optsp);
624 if (l && (*mnt_optsp)[l-1] == ',')
625 (*mnt_optsp)[l-1] = '\0';
626 if (getuid() != 0) {
627 const char *user = get_user_name();
628 if (user == NULL((void*)0))
629 return -1;
630
631 if (add_option(mnt_optsp, "user=", strlen(user)) == -1)
632 return -1;
633 strcat(*mnt_optsp, user);
634 }
635 return 0;
636}
637
638static int opt_eq(const char *s, unsigned len, const char *opt)
639{
640 if(strlen(opt) == len && strncmp(s, opt, len) == 0)
641 return 1;
642 else
643 return 0;
644}
645
646static int get_string_opt(const char *s, unsigned len, const char *opt,
647 char **val)
648{
649 int i;
650 unsigned opt_len = strlen(opt);
651 char *d;
652
653 free(*val);
654 *val = (char *) malloc(len - opt_len + 1);
655 if (!*val) {
656 fprintf(stderrstderr, "%s: failed to allocate memory\n", progname);
657 return 0;
658 }
659
660 d = *val;
661 s += opt_len;
662 len -= opt_len;
663 for (i = 0; i < len; i++) {
664 if (s[i] == '\\' && i + 1 < len)
665 i++;
666 *d++ = s[i];
667 }
668 *d = '\0';
669 return 1;
670}
671
672static int do_mount(const char *mnt, char **typep, mode_t rootmode,
673 int fd, const char *opts, const char *dev, char **sourcep,
674 char **mnt_optsp, off_t rootsize)
675{
676 int res;
677 int flags = MS_NOSUIDMS_NOSUID | MS_NODEVMS_NODEV;
678 char *optbuf;
679 char *mnt_opts = NULL((void*)0);
680 const char *s;
681 char *d;
682 char *fsname = NULL((void*)0);
683 char *subtype = NULL((void*)0);
684 char *source = NULL((void*)0);
685 char *type = NULL((void*)0);
686 int check_empty = 1;
687 int blkdev = 0;
688
689 optbuf = (char *) malloc(strlen(opts) + 128);
690 if (!optbuf) {
691 fprintf(stderrstderr, "%s: failed to allocate memory\n", progname);
692 return -1;
693 }
694
695 for (s = opts, d = optbuf; *s;) {
696 unsigned len;
697 const char *fsname_str = "fsname=";
698 const char *subtype_str = "subtype=";
699 for (len = 0; s[len]; len++) {
700 if (s[len] == '\\' && s[len + 1])
701 len++;
702 else if (s[len] == ',')
703 break;
704 }
705 if (begins_with(s, fsname_str)) {
706 if (!get_string_opt(s, len, fsname_str, &fsname))
707 goto err;
708 } else if (begins_with(s, subtype_str)) {
709 if (!get_string_opt(s, len, subtype_str, &subtype))
710 goto err;
711 } else if (opt_eq(s, len, "blkdev")) {
712 if (getuid() != 0) {
713 fprintf(stderrstderr,
714 "%s: option blkdev is privileged\n",
715 progname);
716 goto err;
717 }
718 blkdev = 1;
719 } else if (opt_eq(s, len, "nonempty")) {
720 check_empty = 0;
721 } else if (!begins_with(s, "fd=") &&
722 !begins_with(s, "rootmode=") &&
723 !begins_with(s, "user_id=") &&
724 !begins_with(s, "group_id=")) {
725 int on;
726 int flag;
727 int skip_option = 0;
728 if (opt_eq(s, len, "large_read")) {
729 struct utsname utsname;
730 unsigned kmaj, kmin;
731 res = uname(&utsname);
732 if (res == 0 &&
733 sscanf(utsname.release, "%u.%u",
734 &kmaj, &kmin) == 2 &&
735 (kmaj > 2 || (kmaj == 2 && kmin > 4))) {
736 fprintf(stderrstderr, "%s: note: 'large_read' mount option is deprecated for %i.%i kernels\n", progname, kmaj, kmin);
737 skip_option = 1;
738 }
739 }
740 if (getuid() != 0 && !user_allow_other &&
741 (opt_eq(s, len, "allow_other") ||
742 opt_eq(s, len, "allow_root"))) {
743 fprintf(stderrstderr, "%s: option %.*s only allowed if 'user_allow_other' is set in /etc/fuse.conf\n", progname, len, s);
744 goto err;
745 }
746 if (!skip_option) {
747 if (find_mount_flag(s, len, &on, &flag)) {
748 if (on)
749 flags |= flag;
750 else
751 flags &= ~flag;
752 } else {
753 memcpy(d, s, len);
754 d += len;
755 *d++ = ',';
756 }
757 }
758 }
759 s += len;
760 if (*s)
761 s++;
762 }
763 *d = '\0';
764 res = get_mnt_opts(flags, optbuf, &mnt_opts);
765 if (res == -1)
766 goto err;
767
768 sprintf(d, "fd=%i,rootmode=%o,user_id=%i,group_id=%i",
769 fd, rootmode, getuid(), getgid());
770
771 if (check_empty &&
772 fuse_mnt_check_empty(progname, mnt, rootmode, rootsize) == -1)
773 goto err;
774
775 source = malloc((fsname ? strlen(fsname) : 0) +
776 (subtype ? strlen(subtype) : 0) + strlen(dev) + 32);
777
778 type = malloc((subtype ? strlen(subtype) : 0) + 32);
779 if (!type || !source) {
780 fprintf(stderrstderr, "%s: failed to allocate memory\n", progname);
781 goto err;
782 }
783
784 if (subtype)
785 sprintf(type, "%s.%s", blkdev ? "fuseblk" : "fuse", subtype);
786 else
787 strcpy(type, blkdev ? "fuseblk" : "fuse");
788
789 if (fsname)
790 strcpy(source, fsname);
791 else
792 strcpy(source, subtype ? subtype : dev);
793
794 res = mount(source, mnt, type, flags, optbuf);
795 if (res == -1 && errno(*__errno_location ()) == ENODEV19 && subtype) {
796 /* Probably missing subtype support */
797 strcpy(type, blkdev ? "fuseblk" : "fuse");
798 if (fsname) {
799 if (!blkdev)
800 sprintf(source, "%s#%s", subtype, fsname);
801 } else {
802 strcpy(source, type);
803 }
804
805 res = mount(source, mnt, type, flags, optbuf);
806 }
807 if (res == -1 && errno(*__errno_location ()) == EINVAL22) {
808 /* It could be an old version not supporting group_id */
809 sprintf(d, "fd=%i,rootmode=%o,user_id=%i",
810 fd, rootmode, getuid());
811 res = mount(source, mnt, type, flags, optbuf);
812 }
813 if (res == -1) {
814 int errno_save = errno(*__errno_location ());
815 if (blkdev && errno(*__errno_location ()) == ENODEV19 && !fuse_mnt_check_fuseblk())
816 fprintf(stderrstderr, "%s: 'fuseblk' support missing\n",
817 progname);
818 else
819 fprintf(stderrstderr, "%s: mount failed: %s\n", progname,
820 strerror(errno_save));
821 goto err;
822 }
823 *sourcep = source;
824 *typep = type;
825 *mnt_optsp = mnt_opts;
826 free(fsname);
827 free(optbuf);
828
829 return 0;
830
831err:
832 free(fsname);
833 free(subtype);
834 free(source);
835 free(type);
836 free(mnt_opts);
837 free(optbuf);
838 return -1;
839}
840
841static int check_version(const char *dev)
842{
843 int res;
844 int majorver;
845 int minorver;
846 const char *version_file;
847 FILE *vf;
848
849 if (strcmp(dev, FUSE_DEV_OLD"/proc/fs/fuse/dev") != 0)
850 return 0;
851
852 version_file = FUSE_VERSION_FILE_OLD"/proc/fs/fuse/version";
853 vf = fopen(version_file, "r");
854 if (vf == NULL((void*)0)) {
855 fprintf(stderrstderr, "%s: kernel interface too old\n", progname);
856 return -1;
857 }
858 res = fscanf(vf, "%i.%i", &majorver, &minorver);
859 fclose(vf);
860 if (res != 2) {
861 fprintf(stderrstderr, "%s: error reading %s\n", progname,
862 version_file);
863 return -1;
864 }
865 if (majorver < 3) {
866 fprintf(stderrstderr, "%s: kernel interface too old\n", progname);
867 return -1;
868 }
869 return 0;
870}
871
872static int check_perm(const char **mntp, struct stat *stbuf, int *mountpoint_fd)
873{
874 int res;
875 const char *mnt = *mntp;
876 const char *origmnt = mnt;
877
878 res = lstat(mnt, stbuf);
879 if (res == -1) {
880 fprintf(stderrstderr, "%s: failed to access mountpoint %s: %s\n",
881 progname, mnt, strerror(errno(*__errno_location ())));
882 return -1;
883 }
884
885 /* No permission checking is done for root */
886 if (getuid() == 0)
887 return 0;
888
889 if (S_ISDIR(stbuf->st_mode)((((stbuf->st_mode)) & 0170000) == (0040000))) {
890 res = chdir(mnt);
891 if (res == -1) {
892 fprintf(stderrstderr,
893 "%s: failed to chdir to mountpoint: %s\n",
894 progname, strerror(errno(*__errno_location ())));
895 return -1;
896 }
897 mnt = *mntp = ".";
898 res = lstat(mnt, stbuf);
899 if (res == -1) {
900 fprintf(stderrstderr,
901 "%s: failed to access mountpoint %s: %s\n",
902 progname, origmnt, strerror(errno(*__errno_location ())));
903 return -1;
904 }
905
906 if ((stbuf->st_mode & S_ISVTX01000) && stbuf->st_uid != getuid()) {
907 fprintf(stderrstderr, "%s: mountpoint %s not owned by user\n",
908 progname, origmnt);
909 return -1;
910 }
911
912 res = access(mnt, W_OK2);
913 if (res == -1) {
914 fprintf(stderrstderr, "%s: user has no write access to mountpoint %s\n",
915 progname, origmnt);
916 return -1;
917 }
918 } else if (S_ISREG(stbuf->st_mode)((((stbuf->st_mode)) & 0170000) == (0100000))) {
919 static char procfile[256];
920 *mountpoint_fd = open(mnt, O_WRONLY01);
921 if (*mountpoint_fd == -1) {
922 fprintf(stderrstderr, "%s: failed to open %s: %s\n",
923 progname, mnt, strerror(errno(*__errno_location ())));
924 return -1;
925 }
926 res = fstat(*mountpoint_fd, stbuf);
927 if (res == -1) {
928 fprintf(stderrstderr,
929 "%s: failed to access mountpoint %s: %s\n",
930 progname, mnt, strerror(errno(*__errno_location ())));
931 return -1;
932 }
933 if (!S_ISREG(stbuf->st_mode)((((stbuf->st_mode)) & 0170000) == (0100000))) {
934 fprintf(stderrstderr,
935 "%s: mountpoint %s is no longer a regular file\n",
936 progname, mnt);
937 return -1;
938 }
939
940 sprintf(procfile, "/proc/self/fd/%i", *mountpoint_fd);
941 *mntp = procfile;
942 } else {
943 fprintf(stderrstderr,
944 "%s: mountpoint %s is not a directory or a regular file\n",
945 progname, mnt);
946 return -1;
947 }
948
949
950 return 0;
951}
952
953static int try_open(const char *dev, char **devp, int silent)
954{
955 int fd = open(dev, O_RDWR02);
956 if (fd != -1) {
20
Taking true branch
27
Taking true branch
957 *devp = strdup(dev);
21
Memory is allocated
958 if (*devp == NULL((void*)0)) {
22
Taking false branch
28
Taking false branch
959 fprintf(stderrstderr, "%s: failed to allocate memory\n",
960 progname);
961 close(fd);
962 fd = -1;
963 }
964 } else if (errno(*__errno_location ()) == ENODEV19 ||
965 errno(*__errno_location ()) == ENOENT2)/* check for ENOENT too, for the udev case */
966 return -2;
967 else if (!silent) {
968 fprintf(stderrstderr, "%s: failed to open %s: %s\n", progname, dev,
969 strerror(errno(*__errno_location ())));
970 }
971 return fd;
29
Potential memory leak
972}
973
974static int try_open_fuse_device(char **devp)
975{
976 int fd;
977 int err;
978
979 drop_privs();
980 fd = try_open(FUSE_DEV_NEW"/dev/fuse", devp, 0);
19
Calling 'try_open'
23
Returned allocated memory via 2nd parameter
981 restore_privs();
982 if (fd >= 0)
24
Assuming 'fd' is < 0
25
Taking false branch
983 return fd;
984
985 err = fd;
986 fd = try_open(FUSE_DEV_OLD"/proc/fs/fuse/dev", devp, 1);
26
Calling 'try_open'
987 if (fd >= 0)
988 return fd;
989
990 return err;
991}
992
993static int open_fuse_device(char **devp)
994{
995 int fd = try_open_fuse_device(devp);
18
Calling 'try_open_fuse_device'
996 if (fd >= -1)
997 return fd;
998
999 fprintf(stderrstderr,
1000 "%s: fuse device not found, try 'modprobe fuse' first\n",
1001 progname);
1002
1003 return -1;
1004}
1005
1006static int check_fuse_device(char *devfd, char **devp)
1007{
1008 int res;
1009 char *devlink;
1010
1011 res = asprintf(&devlink, "/proc/self/fd/%s", devfd);
1012 if (res == -1) {
1013 fprintf(stderrstderr, "%s: failed to allocate memory\n", progname);
1014 return -1;
1015 }
1016
1017 *devp = (char *) calloc(1, PATH_MAX4096 + 1);
1018 if (!*devp) {
1019 fprintf(stderrstderr, "%s: failed to allocate memory\n", progname);
1020 free(devlink);
1021 return -1;
1022 }
1023
1024 res = readlink (devlink, *devp, PATH_MAX4096);
1025 free (devlink);
1026 if (res == -1) {
1027 fprintf(stderrstderr, "%s: specified fuse fd is invalid\n",
1028 progname);
1029 return -1;
1030 }
1031
1032 return atoi(devfd);
1033}
1034
1035static int mount_fuse(const char *mnt, const char *opts, char *devfd)
1036{
1037 int res;
1038 int fd;
1039 char *dev;
1040 struct stat stbuf;
1041 char *type = NULL((void*)0);
1042 char *source = NULL((void*)0);
1043 char *mnt_opts = NULL((void*)0);
1044 const char *real_mnt = mnt;
1045 int mountpoint_fd = -1;
1046
1047 fd = devfd ? check_fuse_device(devfd, &dev) : open_fuse_device(&dev);
16
'?' condition is false
17
Calling 'open_fuse_device'
1048 if (fd == -1)
1049 return -1;
1050
1051 drop_privs();
1052 read_conf();
1053
1054 if (getuid() != 0 && mount_max != -1) {
1055 int mount_count = count_fuse_fs();
1056 if (mount_count >= mount_max) {
1057 fprintf(stderrstderr, "%s: too many FUSE filesystems mounted; mount_max=N can be set in /etc/fuse.conf\n", progname);
1058 goto fail_close_fd;
1059 }
1060 }
1061
1062 res = check_version(dev);
1063 if (res != -1) {
1064 res = check_perm(&real_mnt, &stbuf, &mountpoint_fd);
1065 restore_privs();
1066 if (res != -1)
1067 res = do_mount(real_mnt, &type, stbuf.st_mode & S_IFMT0170000,
1068 fd, opts, dev, &source, &mnt_opts,
1069 stbuf.st_size);
1070 } else
1071 restore_privs();
1072
1073 if (mountpoint_fd != -1)
1074 close(mountpoint_fd);
1075
1076 if (res == -1)
1077 goto fail_close_fd;
1078
1079 res = chdir("/");
1080 if (res == -1) {
1081 fprintf(stderrstderr, "%s: failed to chdir to '/'\n", progname);
1082 goto fail_close_fd;
1083 }
1084
1085 if (geteuid() == 0) {
1086 res = add_mount(source, mnt, type, mnt_opts);
1087 if (res == -1) {
1088 /* Can't clean up mount in a non-racy way */
1089 goto fail_close_fd;
1090 }
1091 }
1092
1093out_free:
1094 free(source);
1095 free(type);
1096 free(mnt_opts);
1097 free(dev);
1098
1099 return fd;
1100
1101fail_close_fd:
1102 close(fd);
1103 fd = -1;
1104 goto out_free;
1105}
1106
1107static int send_fd(int sock_fd, int fd)
1108{
1109 int retval;
1110 struct msghdr msg;
1111 struct cmsghdr *p_cmsg;
1112 struct iovec vec;
1113 size_t cmsgbuf[CMSG_SPACE(sizeof(fd))((((sizeof(fd)) + sizeof (size_t) - 1) & (size_t) ~(sizeof
(size_t) - 1)) + (((sizeof (struct cmsghdr)) + sizeof (size_t
) - 1) & (size_t) ~(sizeof (size_t) - 1)))
/ sizeof(size_t)];
1114 int *p_fds;
1115 char sendchar = 0;
1116
1117 msg.msg_control = cmsgbuf;
1118 msg.msg_controllen = sizeof(cmsgbuf);
1119 p_cmsg = CMSG_FIRSTHDR(&msg)((size_t) (&msg)->msg_controllen >= sizeof (struct cmsghdr
) ? (struct cmsghdr *) (&msg)->msg_control : (struct cmsghdr
*) 0)
;
1120 p_cmsg->cmsg_level = SOL_SOCKET1;
1121 p_cmsg->cmsg_type = SCM_RIGHTSSCM_RIGHTS;
1122 p_cmsg->cmsg_len = CMSG_LEN(sizeof(fd))((((sizeof (struct cmsghdr)) + sizeof (size_t) - 1) & (size_t
) ~(sizeof (size_t) - 1)) + (sizeof(fd)))
;
1123 p_fds = (int *) CMSG_DATA(p_cmsg)((p_cmsg)->__cmsg_data);
1124 *p_fds = fd;
1125 msg.msg_controllen = p_cmsg->cmsg_len;
1126 msg.msg_name = NULL((void*)0);
1127 msg.msg_namelen = 0;
1128 msg.msg_iov = &vec;
1129 msg.msg_iovlen = 1;
1130 msg.msg_flags = 0;
1131 /* "To pass file descriptors or credentials you need to send/read at
1132 * least one byte" (man 7 unix) */
1133 vec.iov_base = &sendchar;
1134 vec.iov_len = sizeof(sendchar);
1135 while ((retval = sendmsg(sock_fd, &msg, 0)) == -1 && errno(*__errno_location ()) == EINTR4);
1136 if (retval != 1) {
1137 perror("sending file descriptor");
1138 return -1;
1139 }
1140 return 0;
1141}
1142
1143static void usage(void)
1144{
1145 fprintf(stderrstderr,
1146 "%s: [options] mountpoint\n"
1147 "Options:\n"
1148 " -h print help\n"
1149 " -V print version\n"
1150 " -o opt[,opt...] mount options\n"
1151 " -u unmount\n"
1152 " -q quiet\n"
1153 " -z lazy unmount\n",
1154 progname);
1155 exit(1);
1156}
1157
1158static void show_version(void)
1159{
1160 printf("fusermount version: %s\n", PACKAGE_VERSION"3git");
1161 exit(0);
1162}
1163
1164int main(int argc, char *argv[])
1165{
1166 int ch;
1167 int fd;
1168 int res;
1169 char *origmnt;
1170 char *mnt;
1171 static int unmount = 0;
1172 static int lazy = 0;
1173 static int quiet = 0;
1174 char *devfd;
1175 char *commfd;
1176 int cfd;
1177 const char *opts = "";
1178
1179 static const struct option long_opts[] = {
1180 {"unmount", no_argument0, NULL((void*)0), 'u'},
1181 {"lazy", no_argument0, NULL((void*)0), 'z'},
1182 {"quiet", no_argument0, NULL((void*)0), 'q'},
1183 {"help", no_argument0, NULL((void*)0), 'h'},
1184 {"version", no_argument0, NULL((void*)0), 'V'},
1185 {0, 0, 0, 0}};
1186
1187 progname = strdup(argv[0]);
1188 if (progname == NULL((void*)0)) {
1
Assuming 'progname' is not equal to null
2
Taking false branch
1189 fprintf(stderrstderr, "%s: failed to allocate memory\n", argv[0]);
1190 exit(1);
1191 }
1192
1193 while ((ch = getopt_long(argc, argv, "hVo:uzq", long_opts,
3
Loop condition is false. Execution continues on line 1225
1194 NULL((void*)0))) != -1) {
1195 switch (ch) {
1196 case 'h':
1197 usage();
1198 break;
1199
1200 case 'V':
1201 show_version();
1202 break;
1203
1204 case 'o':
1205 opts = optarg;
1206 break;
1207
1208 case 'u':
1209 unmount = 1;
1210 break;
1211
1212 case 'z':
1213 lazy = 1;
1214 break;
1215
1216 case 'q':
1217 quiet = 1;
1218 break;
1219
1220 default:
1221 exit(1);
1222 }
1223 }
1224
1225 if (lazy && !unmount) {
1226 fprintf(stderrstderr, "%s: -z can only be used with -u\n", progname);
1227 exit(1);
1228 }
1229
1230 if (optind >= argc) {
4
Taking false branch
1231 fprintf(stderrstderr, "%s: missing mountpoint argument\n", progname);
1232 exit(1);
1233 } else if (argc > optind + 1) {
5
Taking false branch
1234 fprintf(stderrstderr, "%s: extra arguments after the mountpoint\n",
1235 progname);
1236 exit(1);
1237 }
1238
1239 origmnt = argv[optind];
1240
1241 drop_privs();
1242 mnt = fuse_mnt_resolve_path(progname, origmnt);
1243 if (mnt != NULL((void*)0)) {
6
Assuming 'mnt' is not equal to null
7
Taking true branch
1244 res = chdir("/");
1245 if (res == -1) {
8
Taking false branch
1246 fprintf(stderrstderr, "%s: failed to chdir to '/'\n", progname);
1247 exit(1);
1248 }
1249 }
1250 restore_privs();
1251 if (mnt == NULL((void*)0))
9
Taking false branch
1252 exit(1);
1253
1254 umask(033);
1255 if (unmount) {
10
Taking false branch
1256 if (geteuid() == 0)
1257 res = unmount_fuse(mnt, quiet, lazy);
1258 else {
1259 res = umount2(mnt, lazy ? 2 : 0);
1260 if (res == -1 && !quiet)
1261 fprintf(stderrstderr,
1262 "%s: failed to unmount %s: %s\n",
1263 progname, mnt, strerror(errno(*__errno_location ())));
1264 }
1265 if (res == -1)
1266 exit(1);
1267 return 0;
1268 }
1269
1270 devfd = getenv(FUSE_DEVFD_ENV"_FUSE_DEVFD");
1271 if (devfd == NULL((void*)0)) {
11
Assuming 'devfd' is equal to null
12
Taking true branch
1272 commfd = getenv(FUSE_COMMFD_ENV"_FUSE_COMMFD");
1273 if (commfd == NULL((void*)0)) {
13
Assuming 'commfd' is not equal to null
14
Taking false branch
1274 fprintf(stderrstderr, "%s: old style mounting not supported\n",
1275 progname);
1276 exit(1);
1277 }
1278 }
1279
1280 fd = mount_fuse(mnt, opts, devfd);
15
Calling 'mount_fuse'
1281 if (fd == -1)
1282 exit(1);
1283
1284 if (devfd == NULL((void*)0)) {
1285 cfd = atoi(commfd);
1286 res = send_fd(cfd, fd);
1287 if (res == -1)
1288 exit(1);
1289 }
1290
1291 return 0;
1292}