blob: 784fd472db68e2dea08b252b9a7e85426720e836 [file] [log] [blame]
Miklos Szeredi3cb3f142001-11-08 11:34:54 +00001/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2001 Miklos Szeredi (mszeredi@inf.bme.hu)
4
5 This program can be distributed under the terms of the GNU GPL.
6 See the file COPYING.
7*/
8
9#include <stdio.h>
10#include <stdlib.h>
11#include <string.h>
12#include <unistd.h>
13#include <errno.h>
14#include <fcntl.h>
15#include <sys/wait.h>
16#include <sys/stat.h>
17#include <sys/mount.h>
18#include <linux/fuse.h>
19#include <sys/capability.h>
20
21#define FUSE_DEV "/proc/fs/fuse/dev"
22
23const char *progname;
24
25static int do_mount(const char *dev, const char *mnt, const char *type,
26 mode_t rootmode, int fd)
27{
28 int res;
29 struct fuse_mount_data data;
30
31 data.version = FUSE_KERNEL_VERSION;
32 data.fd = fd;
33 data.rootmode = rootmode;
34
35 res = mount(dev, mnt, type, MS_MGC_VAL | MS_NOSUID | MS_NODEV, &data);
36 if(res == -1) {
37 fprintf(stderr, "%s: mount failed: %s\n", progname, strerror(errno));
38 return -1;
39 }
40
41 return 0;
42}
43
44static int check_perm(const char *mnt, struct stat *stbuf)
45{
46 int res;
47
48 res = lstat(mnt, stbuf);
49 if(res == -1) {
50 fprintf(stderr, "%s: failed to access mountpoint %s: %s\n",
51 progname, mnt, strerror(errno));
52 return -1;
53 }
54
55 if(!S_ISDIR(stbuf->st_mode) && !S_ISREG(stbuf->st_mode)) {
56 fprintf(stderr, "%s: mountpoint %s is a special file\n",
57 progname, mnt);
58 return -1;
59 }
60
61 if(getuid() != 0) {
62 if(stbuf->st_uid != getuid()) {
63 fprintf(stderr, "%s: mountpoint %s not owned by user\n",
64 progname, mnt);
65 return -1;
66 }
67
68 res = access(mnt, R_OK | W_OK | (S_ISDIR(stbuf->st_mode) ? X_OK : 0));
69 if(res == -1) {
70 fprintf(stderr, "%s: user has no full access to mountpoint %s\n",
71 progname, mnt);
72 return -1;
73 }
74 }
75
76 return 0;
77}
78
79static int mount_fuse(const char *mnt)
80{
81 int res;
82 int fd;
83 const char *dev = FUSE_DEV;
84 const char *type = "fuse";
85 struct stat stbuf;
86
87 res = check_perm(mnt, &stbuf);
88 if(res == -1)
89 return -1;
90
91 fd = open(dev, O_RDWR);
92 if(fd == -1) {
93 fprintf(stderr, "%s: unable to open fuse device %s: %s\n", progname,
94 dev, strerror(errno));
95 return -1;
96 }
97
98 res = do_mount(dev, mnt, type, stbuf.st_mode & S_IFMT, fd);
99 if(res == -1)
100 return -1;
101
102 return fd;
103}
104
105static void usage()
106{
107 fprintf(stderr,
108 "%s: [options] mountpoint program [args ...]\n"
109 "Options:\n"
110 " -h print help\n",
111 progname);
112 exit(1);
113}
114
115int main(int argc, char *argv[])
116{
117 int a;
118 const char *mnt = NULL;
119 char **userprog;
120 pid_t pid;
121 int fd;
122 int status;
123 int res;
124
125 progname = argv[0];
126
127 for(a = 1; a < argc; a++) {
128 if(argv[a][0] != '-')
129 break;
130
131 switch(argv[a][1]) {
132 case 'h':
133 usage();
134 break;
135
136 default:
137 fprintf(stderr, "%s: Unknown option %s\n", progname, argv[a]);
138 exit(1);
139 }
140 }
141
142 if(a == argc) {
143 fprintf(stderr, "%s: Missing mountpoint argument\n", progname);
144 exit(1);
145 }
146
147 mnt = argv[a++];
148
149 if(a == argc) {
150 fprintf(stderr, "%s: Missing program argument\n", progname);
151 exit(1);
152 }
153
154 userprog = argv + a;
155
156 fd = mount_fuse(mnt);
157 if(fd == -1)
158 exit(1);
159
160 /* Dup the file descriptor to stdin */
161 if(fd != 0) {
162 dup2(fd, 0);
163 close(fd);
164 }
165
166 pid = fork();
167 if(pid == -1) {
168 fprintf(stderr, "%s: Unable to fork: %s\n", progname, strerror(errno));
169 umount(mnt);
170 exit(1);
171 }
172
173 if(pid == 0) {
174 /* Drop setuid/setgid permissions */
175 setuid(getuid());
176 setgid(getgid());
177
178 execv(userprog[0], userprog);
179 fprintf(stderr, "%s: failed to exec %s: %s\n", progname, userprog[0],
180 strerror(errno));
181 exit(1);
182 }
183
184 close(0);
185 res = waitpid(pid, &status, 0);
186 if(res == -1) {
187 fprintf(stderr, "%s: failed to wait for child: %s\n", progname,
188 strerror(errno));
189 exit(1);
190 }
191 res = umount(mnt);
192 if(res == -1) {
193 fprintf(stderr, "%s: failed to unmount %s: %s\n", progname, mnt,
194 strerror(errno));
195 exit(1);
196 }
197
198 return 0;
199}