/[fuse_dbi]/fuse/cvs/util/fusermount.c
This is repository of my old source code which isn't updated any more. Go to git.rot13.org for current projects!
ViewVC logotype

Contents of /fuse/cvs/util/fusermount.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 4 - (show annotations)
Wed Aug 4 11:36:44 2004 UTC (19 years, 9 months ago) by dpavlin
File MIME type: text/plain
File size: 17228 byte(s)
import current CVS of fuse

1 /*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2001-2004 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 /*
11 * NOTE: This program should be part of (or be called from) /bin/mount
12 *
13 * Unless that is done, operations on /etc/mtab are not under lock, and so
14 * data in this file may be lost. (I will _not_ reimplement that locking,
15 * and anyway that should be done in libc, if possible. But probably it
16 * isn't).
17 */
18
19 #include <config.h>
20
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <unistd.h>
25 #include <errno.h>
26 #include <fcntl.h>
27 #include <pwd.h>
28 #include <mntent.h>
29 #include <sys/param.h>
30 #include <sys/wait.h>
31 #include <sys/stat.h>
32 #include <sys/mount.h>
33 #include <sys/fsuid.h>
34 #include <sys/socket.h>
35 #include <sys/un.h>
36 #include <linux/fuse.h>
37
38 #define FUSE_DEV "/proc/fs/fuse/dev"
39
40 #define FUSE_COMMFD_ENV "_FUSE_COMMFD"
41
42 const char *progname;
43
44 static const char *get_user_name()
45 {
46 struct passwd *pw = getpwuid(getuid());
47 if (pw != NULL && pw->pw_name != NULL)
48 return pw->pw_name;
49 else {
50 fprintf(stderr, "%s: could not determine username\n", progname);
51 return NULL;
52 }
53 }
54
55 /* use a lock file so that multiple fusermount processes don't try and
56 modify the mtab file at once! */
57 static int lock_mtab()
58 {
59 const char *mtab_lock = _PATH_MOUNTED ".fuselock";
60 int mtablock;
61 int res;
62
63 mtablock = open(mtab_lock, O_RDWR | O_CREAT, 0600);
64 if (mtablock >= 0) {
65 res = lockf(mtablock, F_LOCK, 0);
66 if (res < 0)
67 perror("error getting lock");
68 } else
69 fprintf(stderr, "unable to open fuse lock file, continuing anyway\n");
70
71 return mtablock;
72 }
73
74 static void unlock_mtab(int mtablock)
75 {
76 if (mtablock >= 0) {
77 lockf(mtablock, F_ULOCK, 0);
78 close(mtablock);
79 }
80 }
81
82 static int add_mount(const char *fsname, const char *mnt, const char *type)
83 {
84 int res;
85 const char *mtab = _PATH_MOUNTED;
86 struct mntent ent;
87 FILE *fp;
88 char *opts;
89
90 fp = setmntent(mtab, "a");
91 if (fp == NULL) {
92 fprintf(stderr, "%s: failed to open %s: %s\n", progname, mtab,
93 strerror(errno));
94 return -1;
95 }
96
97 if (getuid() != 0) {
98 const char *user = get_user_name();
99 if (user == NULL)
100 return -1;
101
102 opts = malloc(strlen(user) + 128);
103 if (opts != NULL)
104 sprintf(opts, "rw,nosuid,nodev,user=%s", user);
105 }
106 else
107 opts = strdup("rw,nosuid,nodev");
108
109 if (opts == NULL) {
110 fprintf(stderr, "%s: failed to allocate memory\n", progname);
111 return -1;
112 }
113
114 ent.mnt_fsname = (char *) fsname;
115 ent.mnt_dir = (char *) mnt;
116 ent.mnt_type = (char *) type;
117 ent.mnt_opts = opts;
118 ent.mnt_freq = 0;
119 ent.mnt_passno = 0;
120 res = addmntent(fp, &ent);
121 if (res != 0) {
122 fprintf(stderr, "%s: failed to add entry to %s: %s\n", progname,
123 mtab, strerror(errno));
124 return -1;
125 }
126
127 endmntent(fp);
128 return 0;
129 }
130
131 static int remove_mount(const char *mnt, int quiet, int lazy)
132 {
133 int res;
134 const char *mtab = _PATH_MOUNTED;
135 const char *mtab_new = _PATH_MOUNTED "~fuse~";
136 struct mntent *entp;
137 FILE *fp;
138 FILE *newfp;
139 const char *user = NULL;
140 int found;
141
142 fp = setmntent(mtab, "r");
143 if (fp == NULL) {
144 fprintf(stderr, "%s failed to open %s: %s\n", progname, mtab,
145 strerror(errno));
146 return -1;
147 }
148
149 newfp = setmntent(mtab_new, "w");
150 if (newfp == NULL) {
151 fprintf(stderr, "%s failed to open %s: %s\n", progname, mtab_new,
152 strerror(errno));
153 return -1;
154 }
155
156 if (getuid() != 0) {
157 user = get_user_name();
158 if (user == NULL)
159 return -1;
160 }
161
162 found = 0;
163 while ((entp = getmntent(fp)) != NULL) {
164 int remove = 0;
165 if (!found && strcmp(entp->mnt_dir, mnt) == 0 &&
166 strcmp(entp->mnt_type, "fuse") == 0) {
167 if (user == NULL)
168 remove = 1;
169 else {
170 char *p = strstr(entp->mnt_opts, "user=");
171 if (p != NULL && strcmp(p + 5, user) == 0)
172 remove = 1;
173 }
174 }
175 if (remove)
176 found = 1;
177 else {
178 res = addmntent(newfp, entp);
179 if (res != 0) {
180 fprintf(stderr, "%s: failed to add entry to %s: %s", progname,
181 mtab_new, strerror(errno));
182
183 }
184 }
185 }
186
187 endmntent(fp);
188 endmntent(newfp);
189
190 if (found) {
191 res = umount2(mnt, lazy ? 2 : 0);
192 if (res == -1) {
193 if (!quiet)
194 fprintf(stderr, "%s: failed to unmount %s: %s\n",
195 progname, mnt,strerror(errno));
196 found = -1;
197 }
198 }
199
200 if (found == 1) {
201 res = rename(mtab_new, mtab);
202 if (res == -1) {
203 fprintf(stderr, "%s: failed to rename %s to %s: %s\n", progname,
204 mtab_new, mtab, strerror(errno));
205 return -1;
206 }
207 }
208 else {
209 if (!found && !quiet)
210 fprintf(stderr, "%s: entry for %s not found in %s\n", progname,
211 mnt, mtab);
212 unlink(mtab_new);
213 return -1;
214 }
215
216 return 0;
217 }
218
219 /* Until there is a nice interface for capabilities in _libc_, this will
220 remain here. I don't think it is fair to expect users to compile libcap
221 for this program. And anyway what's all this fuss about versioning the
222 kernel interface? It is quite good as is. */
223 #define _LINUX_CAPABILITY_VERSION 0x19980330
224
225 typedef struct __user_cap_header_struct {
226 unsigned int version;
227 int pid;
228 } *cap_user_header_t;
229
230 typedef struct __user_cap_data_struct {
231 unsigned int effective;
232 unsigned int permitted;
233 unsigned int inheritable;
234 } *cap_user_data_t;
235
236 int capget(cap_user_header_t header, cap_user_data_t data);
237 int capset(cap_user_header_t header, cap_user_data_t data);
238
239 #define CAP_SYS_ADMIN 21
240
241 static uid_t oldfsuid;
242 static gid_t oldfsgid;
243 static struct __user_cap_data_struct oldcaps;
244
245 static int drop_privs()
246 {
247 int res;
248 struct __user_cap_header_struct head;
249 struct __user_cap_data_struct newcaps;
250
251 head.version = _LINUX_CAPABILITY_VERSION;
252 head.pid = 0;
253 res = capget(&head, &oldcaps);
254 if (res == -1) {
255 fprintf(stderr, "%s: failed to get capabilities: %s\n", progname,
256 strerror(errno));
257 return -1;
258 }
259
260 oldfsuid = setfsuid(getuid());
261 oldfsgid = setfsgid(getgid());
262 newcaps = oldcaps;
263 /* Keep CAP_SYS_ADMIN for mount */
264 newcaps.effective &= (1 << CAP_SYS_ADMIN);
265
266 head.version = _LINUX_CAPABILITY_VERSION;
267 head.pid = 0;
268 res = capset(&head, &newcaps);
269 if (res == -1) {
270 fprintf(stderr, "%s: failed to set capabilities: %s\n", progname,
271 strerror(errno));
272 return -1;
273 }
274 return 0;
275 }
276
277 static void restore_privs()
278 {
279 struct __user_cap_header_struct head;
280 int res;
281
282 head.version = _LINUX_CAPABILITY_VERSION;
283 head.pid = 0;
284 res = capset(&head, &oldcaps);
285 if (res == -1)
286 fprintf(stderr, "%s: failed to restore capabilities: %s\n", progname,
287 strerror(errno));
288
289 setfsuid(oldfsuid);
290 setfsgid(oldfsgid);
291 }
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 static int do_mount(const char *mnt, const char *type, mode_t rootmode,
302 int fd, const char *opts, char **fsnamep)
303 {
304 int res;
305 int flags = MS_NOSUID | MS_NODEV;
306 char *optbuf;
307 const char *s;
308 char *d;
309 char *fsname = NULL;
310
311 if (getuid() != 0) {
312 res = drop_privs();
313 if (res == -1)
314 return -1;
315 }
316
317 optbuf = malloc(strlen(opts) + 64);
318 if (!optbuf) {
319 fprintf(stderr, "%s: failed to allocate memory\n", progname);
320 return -1;
321 }
322
323 for (s = opts, d = optbuf; *s;) {
324 unsigned len;
325 const char *fsname_str = "fsname=";
326 for (len = 0; s[len] && s[len] != ','; len++);
327 if (begins_with(s, fsname_str)) {
328 unsigned fsname_str_len = strlen(fsname_str);
329 if (fsname)
330 free(fsname);
331 fsname = malloc(len - fsname_str_len + 1);
332 if (!fsname) {
333 fprintf(stderr, "%s: failed to allocate memory\n", progname);
334 free(optbuf);
335 return -1;
336 }
337 memcpy(fsname, s + fsname_str_len, len - fsname_str_len);
338 fsname[len - fsname_str_len] = '\0';
339 } else if (!begins_with(s, "fd=") &&
340 !begins_with(s, "rootmode=") &&
341 !begins_with(s, "uid=")) {
342 memcpy(d, s, len);
343 d += len;
344 *d++ = ',';
345 }
346 s += len;
347 if (*s)
348 s++;
349 }
350 sprintf(d, "fd=%i,rootmode=%o,uid=%i", fd, rootmode, getuid());
351 if (fsname == NULL) {
352 fsname = strdup(FUSE_DEV);
353 if (!fsname) {
354 fprintf(stderr, "%s: failed to allocate memory\n", progname);
355 free(optbuf);
356 return -1;
357 }
358 }
359
360 res = mount(fsname, mnt, type, flags, optbuf);
361 free(optbuf);
362 if (res == -1) {
363 fprintf(stderr, "%s: mount failed: %s\n", progname, strerror(errno));
364 free(fsname);
365 }
366 *fsnamep = fsname;
367
368 if (getuid() != 0)
369 restore_privs();
370
371 return res;
372 }
373
374 static int check_perm(const char **mntp, struct stat *stbuf, int *currdir_fd)
375 {
376 int res;
377 const char *mnt = *mntp;
378
379 res = lstat(mnt, stbuf);
380 if (res == -1) {
381 fprintf(stderr, "%s: failed to access mountpoint %s: %s\n",
382 progname, mnt, strerror(errno));
383 return -1;
384 }
385
386 /* No permission checking is done for root */
387 if (getuid() == 0)
388 return 0;
389
390 if (!S_ISDIR(stbuf->st_mode)) {
391 fprintf(stderr, "%s: mountpoint %s is not a directory\n",
392 progname, mnt);
393 return -1;
394 }
395
396 *currdir_fd = open(".", O_RDONLY);
397 if (*currdir_fd == -1) {
398 fprintf(stderr, "%s: failed to open current directory: %s\n",
399 progname, strerror(errno));
400 return -1;
401 }
402 res = chdir(mnt);
403 if (res == -1) {
404 fprintf(stderr, "%s: failed to chdir to mountpoint: %s\n",
405 progname, strerror(errno));
406 return -1;
407 }
408 mnt = *mntp = ".";
409 res = lstat(mnt, stbuf);
410 if (res == -1) {
411 fprintf(stderr, "%s: failed to access mountpoint %s: %s\n",
412 progname, mnt, strerror(errno));
413 return -1;
414 }
415
416 if ((stbuf->st_mode & S_ISVTX) && stbuf->st_uid != getuid()) {
417 fprintf(stderr, "%s: mountpoint %s not owned by user\n",
418 progname, mnt);
419 return -1;
420 }
421
422 res = access(mnt, W_OK);
423 if (res == -1) {
424 fprintf(stderr, "%s: user has no write access to mountpoint %s\n",
425 progname, mnt);
426 return -1;
427 }
428
429 return 0;
430 }
431
432 static int mount_fuse(const char *mnt, const char *opts)
433 {
434 int res;
435 int fd;
436 const char *dev = FUSE_DEV;
437 const char *type = "fuse";
438 struct stat stbuf;
439 int mtablock;
440 char *fsname;
441 const char *real_mnt = mnt;
442 int currdir_fd = -1;
443
444 fd = open(dev, O_RDWR);
445 if (fd == -1
446 #ifndef AUTO_MODPROBE
447 && getuid() == 0
448 #endif
449 ) {
450 int status;
451 pid_t pid = fork();
452 if (pid == 0) {
453 setuid(0);
454 execl("/sbin/modprobe", "/sbin/modprobe", "fuse", NULL);
455 exit(1);
456 }
457 if (pid != -1)
458 waitpid(pid, &status, 0);
459
460 fd = open(dev, O_RDWR);
461 }
462 if (fd == -1) {
463 fprintf(stderr, "%s: unable to open fuse device %s: %s\n", progname,
464 dev, strerror(errno));
465 return -1;
466 }
467
468 res = check_perm(&real_mnt, &stbuf, &currdir_fd);
469 if (res == -1)
470 return -1;
471
472 res = do_mount(real_mnt, type, stbuf.st_mode & S_IFMT, fd, opts, &fsname);
473 if (res == -1)
474 return -1;
475
476 if (currdir_fd != -1) {
477 fchdir(currdir_fd);
478 close(currdir_fd);
479 }
480
481 if (geteuid() == 0) {
482 mtablock = lock_mtab();
483 res = add_mount(fsname, mnt, type);
484 free(fsname);
485 unlock_mtab(mtablock);
486 if (res == -1) {
487 umount2(mnt, 2); /* lazy umount */
488 return -1;
489 }
490 } else
491 free(fsname);
492
493 return fd;
494 }
495
496 static char *resolve_path(const char *orig, int unmount)
497 {
498 char buf[PATH_MAX];
499 char *dst;
500
501 if (unmount) {
502 char *end;
503 /* Resolving at unmount can only be done very carefully, not touching
504 the mountpoint... So for the moment it's not done.
505
506 Just remove trailing slashes instead.
507 */
508 dst = strdup(orig);
509 if (dst == NULL) {
510 fprintf(stderr, "%s: failed to allocate memory\n", progname);
511 return NULL;
512 }
513
514 for (end = dst + strlen(dst) - 1; end > dst && *end == '/'; end --)
515 *end = '\0';
516
517 return dst;
518 }
519
520 if (realpath(orig, buf) == NULL) {
521 fprintf(stderr, "%s: Bad mount point %s: %s\n", progname, orig,
522 strerror(errno));
523 return NULL;
524 }
525
526 dst = strdup(buf);
527 if (dst == NULL)
528 fprintf(stderr, "%s: failed to allocate memory\n", progname);
529 return dst;
530 }
531
532 static int send_fd(int sock_fd, int fd)
533 {
534 int retval;
535 struct msghdr msg;
536 struct cmsghdr *p_cmsg;
537 struct iovec vec;
538 char cmsgbuf[CMSG_SPACE(sizeof(fd))];
539 int *p_fds;
540 char sendchar = 0;
541
542 msg.msg_control = cmsgbuf;
543 msg.msg_controllen = sizeof(cmsgbuf);
544 p_cmsg = CMSG_FIRSTHDR(&msg);
545 p_cmsg->cmsg_level = SOL_SOCKET;
546 p_cmsg->cmsg_type = SCM_RIGHTS;
547 p_cmsg->cmsg_len = CMSG_LEN(sizeof(fd));
548 p_fds = (int *) CMSG_DATA(p_cmsg);
549 *p_fds = fd;
550 msg.msg_controllen = p_cmsg->cmsg_len;
551 msg.msg_name = NULL;
552 msg.msg_namelen = 0;
553 msg.msg_iov = &vec;
554 msg.msg_iovlen = 1;
555 msg.msg_flags = 0;
556 /* "To pass file descriptors or credentials you need to send/read at
557 * least one byte" (man 7 unix) */
558 vec.iov_base = &sendchar;
559 vec.iov_len = sizeof(sendchar);
560 while ((retval = sendmsg(sock_fd, &msg, 0)) == -1 && errno == EINTR);
561 if (retval != 1) {
562 perror("sending file descriptor");
563 return -1;
564 }
565 return 0;
566 }
567
568 static void usage()
569 {
570 fprintf(stderr,
571 "%s: [options] mountpoint\n"
572 "Options:\n"
573 " -h print help\n"
574 " -o opt[,opt...] mount options\n"
575 " -u unmount\n"
576 " -q quiet\n"
577 " -z lazy unmount\n",
578 progname);
579 exit(1);
580 }
581
582 int main(int argc, char *argv[])
583 {
584 int a;
585 int fd;
586 int res;
587 char *origmnt;
588 char *mnt;
589 int unmount = 0;
590 int lazy = 0;
591 char *commfd;
592 int quiet = 0;
593 int cfd;
594 const char *opts = "";
595
596 progname = argv[0];
597
598 for (a = 1; a < argc; a++) {
599 if (argv[a][0] != '-')
600 break;
601
602 switch (argv[a][1]) {
603 case 'h':
604 usage();
605 break;
606
607 case 'o':
608 a++;
609 if (a == argc) {
610 fprintf(stderr, "%s: Missing argument to -o\n", progname);
611 exit(1);
612 }
613 opts = argv[a];
614 break;
615
616 case 'u':
617 unmount = 1;
618 break;
619
620 case 'z':
621 lazy = 1;
622 break;
623
624 case 'q':
625 quiet = 1;
626 break;
627
628 default:
629 fprintf(stderr, "%s: Unknown option %s\n", progname, argv[a]);
630 fprintf(stderr, "Try `%s -h' for more information\n", progname);
631 exit(1);
632 }
633 }
634
635 if (a == argc) {
636 fprintf(stderr, "%s: Missing mountpoint argument\n", progname);
637 exit(1);
638 }
639
640 origmnt = argv[a++];
641
642 if (getuid() != 0)
643 drop_privs();
644
645 mnt = resolve_path(origmnt, unmount);
646 if (mnt == NULL)
647 exit(1);
648
649 if (getuid() != 0)
650 restore_privs();
651
652 if (unmount) {
653 if (geteuid() == 0) {
654 int mtablock = lock_mtab();
655 res = remove_mount(mnt, quiet, lazy);
656 unlock_mtab(mtablock);
657 } else {
658 res = umount2(mnt, lazy ? 2 : 0);
659 if (res == -1) {
660 if (!quiet)
661 fprintf(stderr, "%s: failed to unmount %s: %s\n",
662 progname, mnt, strerror(errno));
663 }
664 }
665 if (res == -1)
666 exit(1);
667 return 0;
668 }
669
670 commfd = getenv(FUSE_COMMFD_ENV);
671 if (commfd == NULL) {
672 fprintf(stderr, "%s: old style mounting not supported\n", progname);
673 exit(1);
674 }
675
676 fd = mount_fuse(mnt, opts);
677 if (fd == -1)
678 exit(1);
679
680 cfd = atoi(commfd);
681 res = send_fd(cfd, fd);
682 if (res == -1)
683 exit(1);
684
685 return 0;
686 }

  ViewVC Help
Powered by ViewVC 1.1.26