File: | contrib/fuse-util/fusermount.c |
Location: | line 971, column 9 |
Description: | Potential memory leak |
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 | ||||
50 | static const char *progname; | |||
51 | ||||
52 | static int user_allow_other = 0; | |||
53 | static int mount_max = 1000; | |||
54 | ||||
55 | static 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 | ||||
66 | static uid_t oldfsuid; | |||
67 | static gid_t oldfsgid; | |||
68 | ||||
69 | static void drop_privs(void) | |||
70 | { | |||
71 | if (getuid() != 0) { | |||
72 | oldfsuid = setfsuid(getuid()); | |||
73 | oldfsgid = setfsgid(getgid()); | |||
74 | } | |||
75 | } | |||
76 | ||||
77 | static 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 | */ | |||
89 | static 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 | ||||
117 | static 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 | ||||
131 | static 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 | ||||
137 | static 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 | */ | |||
224 | static 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 | ||||
291 | static 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 | ||||
309 | static 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 | ||||
338 | static 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 | ||||
385 | static 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 | ||||
413 | out: | |||
414 | free(copy); | |||
415 | ||||
416 | return res; | |||
417 | } | |||
418 | ||||
419 | static 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 | ||||
430 | static 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 */ | |||
452 | static int count_fuse_fs() | |||
453 | { | |||
454 | return 0; | |||
455 | } | |||
456 | ||||
457 | static 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 | ||||
467 | static 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 | ||||
473 | static 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 | ||||
486 | static 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 | ||||
499 | static 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 | ||||
533 | static 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 | ||||
541 | struct mount_flags { | |||
542 | const char *opt; | |||
543 | unsigned long flag; | |||
544 | int on; | |||
545 | int safe; | |||
546 | }; | |||
547 | ||||
548 | static 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 | ||||
565 | static 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 | ||||
586 | static 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 | ||||
606 | static 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 | ||||
638 | static 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 | ||||
646 | static 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 | ||||
672 | static 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 | ||||
831 | err: | |||
832 | free(fsname); | |||
833 | free(subtype); | |||
834 | free(source); | |||
835 | free(type); | |||
836 | free(mnt_opts); | |||
837 | free(optbuf); | |||
838 | return -1; | |||
839 | } | |||
840 | ||||
841 | static 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 | ||||
872 | static 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 | ||||
953 | static int try_open(const char *dev, char **devp, int silent) | |||
954 | { | |||
955 | int fd = open(dev, O_RDWR02); | |||
956 | if (fd != -1) { | |||
957 | *devp = strdup(dev); | |||
958 | if (*devp == NULL((void*)0)) { | |||
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; | |||
| ||||
972 | } | |||
973 | ||||
974 | static 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); | |||
981 | restore_privs(); | |||
982 | if (fd >= 0) | |||
983 | return fd; | |||
984 | ||||
985 | err = fd; | |||
986 | fd = try_open(FUSE_DEV_OLD"/proc/fs/fuse/dev", devp, 1); | |||
987 | if (fd >= 0) | |||
988 | return fd; | |||
989 | ||||
990 | return err; | |||
991 | } | |||
992 | ||||
993 | static int open_fuse_device(char **devp) | |||
994 | { | |||
995 | int fd = try_open_fuse_device(devp); | |||
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 | ||||
1006 | static 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 | ||||
1035 | static 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); | |||
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 | ||||
1093 | out_free: | |||
1094 | free(source); | |||
1095 | free(type); | |||
1096 | free(mnt_opts); | |||
1097 | free(dev); | |||
1098 | ||||
1099 | return fd; | |||
1100 | ||||
1101 | fail_close_fd: | |||
1102 | close(fd); | |||
1103 | fd = -1; | |||
1104 | goto out_free; | |||
1105 | } | |||
1106 | ||||
1107 | static 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 | ||||
1143 | static 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 | ||||
1158 | static void show_version(void) | |||
1159 | { | |||
1160 | printf("fusermount version: %s\n", PACKAGE_VERSION"3git"); | |||
1161 | exit(0); | |||
1162 | } | |||
1163 | ||||
1164 | int 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)) { | |||
| ||||
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, | |||
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) { | |||
1231 | fprintf(stderrstderr, "%s: missing mountpoint argument\n", progname); | |||
1232 | exit(1); | |||
1233 | } else if (argc > optind + 1) { | |||
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)) { | |||
1244 | res = chdir("/"); | |||
1245 | if (res == -1) { | |||
1246 | fprintf(stderrstderr, "%s: failed to chdir to '/'\n", progname); | |||
1247 | exit(1); | |||
1248 | } | |||
1249 | } | |||
1250 | restore_privs(); | |||
1251 | if (mnt == NULL((void*)0)) | |||
1252 | exit(1); | |||
1253 | ||||
1254 | umask(033); | |||
1255 | if (unmount) { | |||
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)) { | |||
1272 | commfd = getenv(FUSE_COMMFD_ENV"_FUSE_COMMFD"); | |||
1273 | if (commfd == NULL((void*)0)) { | |||
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); | |||
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 | } |