/[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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 4 - (hide 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 dpavlin 4 /*
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