perfused.c revision 1.1 1 /* $NetBSD: perfused.c,v 1.1 2010/08/25 07:18:01 manu Exp $ */
2
3 /*-
4 * Copyright (c) 2010 Emmanuel Dreyfus. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
16 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
17 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
19 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
20 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
21 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
22 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
23 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
24 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
25 * POSSIBILITY OF SUCH DAMAGE.
26 */
27
28 #include <stdio.h>
29 #include <unistd.h>
30 #include <stdlib.h>
31 #include <fcntl.h>
32 #include <syslog.h>
33 #include <ctype.h>
34 #include <paths.h>
35 #include <stdarg.h>
36 #include <err.h>
37 #include <errno.h>
38 #include <string.h>
39 #include <sysexits.h>
40 #include <signal.h>
41 #include <puffs.h>
42 #include <sys/wait.h>
43 #include <sys/param.h>
44 #include <sys/queue.h>
45 #include <sys/uio.h>
46 #include <sys/socket.h>
47 #include <sys/un.h>
48 #include <machine/vmparam.h>
49
50 #include "../../lib/libperfuse/perfuse_if.h"
51 #include "perfused.h"
52
53 static int getpeerid(int, pid_t *, uid_t *, gid_t *);
54 static int access_mount(const char *, uid_t, int);
55 static int accept_new_mount(int);
56 static int parse_debug(char *);
57 static void siginfo_handler(int);
58 static void parse_options(int, char **);
59 static void get_mount_info(int, struct perfuse_mount_info *);
60 int main(int, char **);
61
62
63 static int
64 getpeerid(s, pidp, uidp, gidp)
65 int s;
66 pid_t *pidp;
67 uid_t *uidp;
68 gid_t *gidp;
69 {
70 struct unpcbid unp;
71 socklen_t len;
72 int error;
73
74 len = sizeof(unp);
75 error = getsockopt(s, 0, LOCAL_PEEREID, &unp, &len);
76 if (error != 0)
77 return error;
78
79 if (pidp != NULL)
80 *pidp = unp.unp_pid;
81
82 if (uidp != NULL)
83 *uidp = unp.unp_euid;
84
85 if (gidp != NULL)
86 *gidp = unp.unp_egid;
87
88 return 0;
89 }
90
91 static int
92 access_mount(mnt, uid, ro)
93 const char *mnt;
94 uid_t uid;
95 int ro;
96 {
97 struct stat st;
98 mode_t mode;
99
100 if (uid == 0)
101 return 0;
102
103 if (stat(mnt, &st) == -1)
104 return -1;
105
106 if (st.st_uid != uid)
107 return -1;
108
109 mode = S_IRUSR;
110 if (!ro)
111 mode |= S_IWUSR;
112
113 if ((st.st_mode & mode) == mode)
114 return 0;
115
116 return -1;
117 }
118
119 static void
120 get_mount_info(fd, pmi)
121 int fd;
122 struct perfuse_mount_info *pmi;
123 {
124 struct perfuse_mount_out *pmo;
125 char *source = NULL;
126 char *target = NULL;
127 char *filesystemtype = NULL;
128 long mountflags = 0;
129 void *data;
130 size_t len;
131
132 pmo = (struct perfuse_mount_out *)perfuse_recv_early(fd, sizeof(*pmo));
133 if (pmo == NULL) {
134 if (shutdown(fd, SHUT_RDWR) != 0)
135 DERR(EX_OSERR, "shutdown failed");
136 exit(EX_PROTOCOL);
137 }
138
139 #ifdef PERFUSE_DEBUG
140 DPRINTF("perfuse lengths: source = %d, target = %d, "
141 "filesystemtype = %d, data = %d\n",
142 pmo->pmo_source_len,
143 pmo->pmo_target_len,
144 pmo->pmo_filesystemtype_len,
145 pmo->pmo_data_len);
146 #endif
147 len = pmo->pmo_source_len;
148 source = perfuse_recv_early(fd, len);
149
150 len = pmo->pmo_target_len;
151 target = perfuse_recv_early(fd, len);
152
153 len = pmo->pmo_filesystemtype_len;
154 filesystemtype = perfuse_recv_early(fd, len);
155
156 mountflags = pmo->pmo_mountflags;
157
158 len = pmo->pmo_data_len;
159 data = perfuse_recv_early(fd, len);
160
161 #ifdef PERFUSE_DEBUG
162 DPRINTF("%s(\"%s\", \"%s\", \"%s\", 0x%lx, \"%s\")\n", __func__,
163 source, target, filesystemtype, mountflags, (const char *)data);
164 #endif
165 pmi->pmi_source = source;
166 pmi->pmi_target = target;
167 pmi->pmi_filesystemtype = filesystemtype;
168 pmi->pmi_mountflags = mountflags;
169 pmi->pmi_data = data;
170
171 return;
172 }
173
174 static int
175 accept_new_mount(s)
176 int s;
177 {
178 struct puffs_usermount *pu;
179 struct sockaddr_storage ss;
180 socklen_t ss_len;
181 struct sockaddr *sa;
182 struct perfuse_mount_info pmi;
183 struct perfuse_callbacks pc;
184 int ro_flag;
185 pid_t pid;
186 int fd;
187 int flags;
188
189 #ifdef PERFUSE_DEBUG
190 DPRINTF("waiting connexion\n");
191 #endif
192 sa = (struct sockaddr *)(void *)&ss;
193 ss_len = sizeof(ss);
194 if ((fd = accept(s, sa, &ss_len)) == -1)
195 DERR(EX_OSERR, "accept failed");
196
197 #ifdef PERFUSE_DEBUG
198 DPRINTF("connexion accepted\n");
199 #endif
200
201 pid = (perfuse_diagflags & PDF_FOREGROUND) ? 0 : fork();
202 switch(pid) {
203 case -1:
204 DERR(EX_OSERR, "cannot fork");
205 break;
206 case 0:
207 break;
208 default:
209 return fd;
210 /* NOTREACHED */
211 break;
212 }
213
214 /*
215 * Mount information (source, target, mount flags...)
216 */
217 get_mount_info(fd, &pmi);
218
219 /*
220 * Get peer identity
221 */
222 if (getpeerid(fd, NULL, &pmi.pmi_uid, NULL) != 0)
223 DWARNX("Unable to retreive peer identity");
224
225 /*
226 * Check that peer owns mountpoint and read (and write) on it?
227 */
228 ro_flag = pmi.pmi_mountflags & MNT_RDONLY;
229 if (access_mount(pmi.pmi_target, pmi.pmi_uid, ro_flag) != 0)
230 DERRX(EX_NOPERM, "insuficient privvileges to mount %s",
231 pmi.pmi_target);
232
233
234 /*
235 * Initialize libperfuse, which will initialize libpuffs
236 */
237 pc.pc_new_msg = perfuse_new_pb;
238 pc.pc_xchg_msg = perfuse_xchg_pb;
239 pc.pc_destroy_msg = (perfuse_destroy_msg_fn)puffs_framebuf_destroy;
240 pc.pc_get_inhdr = perfuse_get_inhdr;
241 pc.pc_get_inpayload = perfuse_get_inpayload;
242 pc.pc_get_outhdr = perfuse_get_outhdr;
243 pc.pc_get_outpayload = perfuse_get_outpayload;
244
245 pu = perfuse_init(&pc, &pmi);
246
247 puffs_framev_init(pu, perfuse_readframe, perfuse_writeframe,
248 perfuse_cmpframe, perfuse_gotframe, NULL);
249
250 if (puffs_framev_addfd(pu, fd, PUFFS_FBIO_READ|PUFFS_FBIO_WRITE) == -1)
251 DERR(EX_SOFTWARE, "puffs_framev_addfd failed");
252
253 perfuse_setspecific(pu, (void *)fd);
254
255 setproctitle("perfused %s", pmi.pmi_target);
256 (void)kill(getpid(), SIGINFO); /* This is for -s option */
257
258 perfuse_fs_init(pu);
259
260 /*
261 * Non blocking I/O on /dev/fuse
262 * This must be done after perfuse_fs_init
263 */
264 if ((flags = fcntl(fd, F_GETFL, 0)) == -1)
265 DERR(EX_OSERR, "fcntl failed");
266 if (fcntl(fd, F_SETFL, flags|O_NONBLOCK) != 0)
267 DERR(EX_OSERR, "fcntl failed");
268
269 /*
270 * Hand over control to puffs main loop.
271 */
272 return perfuse_mainloop(pu);
273 }
274
275 static int
276 parse_debug(optstr)
277 char *optstr;
278 {
279 int retval = PDF_SYSLOG;
280 char *opt;
281 char *lastp;
282
283 for (opt = strtok_r(optstr, ",", &lastp);
284 opt;
285 opt = strtok_r(NULL, ",", &lastp)) {
286 if (strcmp(opt, "fuse") == 0)
287 retval |= PDF_FUSE;
288 else if (strcmp(opt, "puffs") == 0)
289 retval |= PDF_PUFFS;
290 else if (strcmp(opt, "dump") == 0)
291 retval |= PDF_DUMP;
292 else if (strcmp(opt, "fh") == 0)
293 retval |= PDF_FH;
294 else if (strcmp(opt, "readdir") == 0)
295 retval |= PDF_READDIR;
296 else if (strcmp(opt, "reclaim") == 0)
297 retval |= PDF_RECLAIM;
298 else if (strcmp(opt, "requeue") == 0)
299 retval |= PDF_REQUEUE;
300 else if (strcmp(opt, "misc") == 0)
301 retval |= PDF_MISC;
302 else
303 DERRX(EX_USAGE, "unknown debug flag \"%s\"", opt);
304 }
305
306 return retval;
307 }
308
309 /* ARGSUSED0 */
310 static void
311 siginfo_handler(sig)
312 int sig;
313 {
314 static int old_flags = 0;
315 int swap;
316
317 swap = perfuse_diagflags;
318 perfuse_diagflags = old_flags;
319 old_flags = swap;
320
321 DWARNX("debug %sabled", old_flags == 0 ? "en" : "dis");
322
323 return;
324 }
325
326 static void
327 parse_options(argc, argv)
328 int argc;
329 char **argv;
330 {
331 int ch;
332 int foreground = 0;
333
334 perfuse_diagflags = PDF_FOREGROUND | PDF_SYSLOG;
335
336 while ((ch = getopt(argc, argv, "d:fs")) != -1) {
337 switch (ch) {
338 case 'd':
339 perfuse_diagflags |= parse_debug(optarg);
340 break;
341 case 's':
342 if (signal(SIGINFO, siginfo_handler) != 0)
343 DERR(EX_OSERR, "signal failed");
344 break;
345 case 'f':
346 foreground = 1;
347 break;
348 default:
349 DERR(EX_USAGE, "%s [-d level] [-s] [-f]", argv[0]);
350 break;
351 }
352 }
353
354 if (!foreground)
355 perfuse_diagflags &= ~PDF_FOREGROUND;
356
357 return;
358 }
359
360 int
361 main(argc, argv)
362 int argc;
363 char **argv;
364 {
365 int s;
366
367 parse_options(argc, argv);
368
369 if (perfuse_diagflags & PDF_SYSLOG)
370 openlog("perfused", 0, LOG_DAEMON);
371
372 if (!(perfuse_diagflags & PDF_FOREGROUND))
373 if (daemon(0, 0) != 0)
374 DERR(EX_OSERR, "daemon failed");
375
376 s = perfuse_open_sock();
377
378 do {
379 (void)accept_new_mount(s);
380 } while (1 /* CONSTCOND */);
381
382 /* NOTREACHED */
383 return 0;
384 }
385