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