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