perfused.c revision 1.3 1 /* $NetBSD: perfused.c,v 1.3 2010/08/27 09:58:17 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 void new_mount(int);
56 static int parse_debug(char *);
57 static void siginfo_handler(int);
58 static int 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 void
175 new_mount(fd)
176 int fd;
177 {
178 struct puffs_usermount *pu;
179 struct perfuse_mount_info pmi;
180 struct perfuse_callbacks pc;
181 int ro_flag;
182 pid_t pid;
183 int flags;
184
185
186 pid = (perfuse_diagflags & PDF_FOREGROUND) ? 0 : fork();
187 switch(pid) {
188 case -1:
189 DERR(EX_OSERR, "cannot fork");
190 break;
191 case 0:
192 break;
193 default:
194 return;
195 /* NOTREACHED */
196 break;
197 }
198
199 /*
200 * Mount information (source, target, mount flags...)
201 */
202 get_mount_info(fd, &pmi);
203
204 /*
205 * Get peer identity
206 */
207 if (getpeerid(fd, NULL, &pmi.pmi_uid, NULL) != 0)
208 DWARNX("Unable to retreive peer identity");
209
210 /*
211 * Check that peer owns mountpoint and read (and write) on it?
212 */
213 ro_flag = pmi.pmi_mountflags & MNT_RDONLY;
214 if (access_mount(pmi.pmi_target, pmi.pmi_uid, ro_flag) != 0)
215 DERRX(EX_NOPERM, "insuficient privvileges to mount %s",
216 pmi.pmi_target);
217
218
219 /*
220 * Initialize libperfuse, which will initialize libpuffs
221 */
222 pc.pc_new_msg = perfuse_new_pb;
223 pc.pc_xchg_msg = perfuse_xchg_pb;
224 pc.pc_destroy_msg = (perfuse_destroy_msg_fn)puffs_framebuf_destroy;
225 pc.pc_get_inhdr = perfuse_get_inhdr;
226 pc.pc_get_inpayload = perfuse_get_inpayload;
227 pc.pc_get_outhdr = perfuse_get_outhdr;
228 pc.pc_get_outpayload = perfuse_get_outpayload;
229
230 pu = perfuse_init(&pc, &pmi);
231
232 puffs_framev_init(pu, perfuse_readframe, perfuse_writeframe,
233 perfuse_cmpframe, perfuse_gotframe, perfuse_fdnotify);
234
235 if (puffs_framev_addfd(pu, fd, PUFFS_FBIO_READ|PUFFS_FBIO_WRITE) == -1)
236 DERR(EX_SOFTWARE, "puffs_framev_addfd failed");
237
238 perfuse_setspecific(pu, (void *)fd);
239
240 setproctitle("perfused %s", pmi.pmi_target);
241 (void)kill(getpid(), SIGINFO); /* This is for -s option */
242
243 perfuse_fs_init(pu);
244
245 /*
246 * Non blocking I/O on /dev/fuse
247 * This must be done after perfuse_fs_init
248 */
249 if ((flags = fcntl(fd, F_GETFL, 0)) == -1)
250 DERR(EX_OSERR, "fcntl failed");
251 if (fcntl(fd, F_SETFL, flags|O_NONBLOCK) != 0)
252 DERR(EX_OSERR, "fcntl failed");
253
254 /*
255 * Hand over control to puffs main loop.
256 */
257 (void)perfuse_mainloop(pu);
258
259 DERRX(EX_SOFTWARE, "perfuse_mainloop exit");
260
261 /* NOTREACHED */
262 return;
263 }
264
265 static int
266 parse_debug(optstr)
267 char *optstr;
268 {
269 int retval = PDF_SYSLOG;
270 char *opt;
271 char *lastp;
272
273 for (opt = strtok_r(optstr, ",", &lastp);
274 opt;
275 opt = strtok_r(NULL, ",", &lastp)) {
276 if (strcmp(opt, "fuse") == 0)
277 retval |= PDF_FUSE;
278 else if (strcmp(opt, "puffs") == 0)
279 retval |= PDF_PUFFS;
280 else if (strcmp(opt, "dump") == 0)
281 retval |= PDF_DUMP;
282 else if (strcmp(opt, "fh") == 0)
283 retval |= PDF_FH;
284 else if (strcmp(opt, "readdir") == 0)
285 retval |= PDF_READDIR;
286 else if (strcmp(opt, "reclaim") == 0)
287 retval |= PDF_RECLAIM;
288 else if (strcmp(opt, "requeue") == 0)
289 retval |= PDF_REQUEUE;
290 else if (strcmp(opt, "sync") == 0)
291 retval |= PDF_SYNC;
292 else if (strcmp(opt, "misc") == 0)
293 retval |= PDF_MISC;
294 else
295 DERRX(EX_USAGE, "unknown debug flag \"%s\"", opt);
296 }
297
298 return retval;
299 }
300
301 /* ARGSUSED0 */
302 static void
303 siginfo_handler(sig)
304 int sig;
305 {
306 static int old_flags = 0;
307 int swap;
308
309 swap = perfuse_diagflags;
310 perfuse_diagflags = old_flags;
311 old_flags = swap;
312
313 DWARNX("debug %sabled", old_flags == 0 ? "en" : "dis");
314
315 return;
316 }
317
318 static int
319 parse_options(argc, argv)
320 int argc;
321 char **argv;
322 {
323 int ch;
324 int foreground = 0;
325 int retval = -1;
326
327 perfuse_diagflags = PDF_FOREGROUND | PDF_SYSLOG;
328
329 while ((ch = getopt(argc, argv, "d:fsi:")) != -1) {
330 switch (ch) {
331 case 'd':
332 perfuse_diagflags |= parse_debug(optarg);
333 break;
334 case 's':
335 if (signal(SIGINFO, siginfo_handler) != 0)
336 DERR(EX_OSERR, "signal failed");
337 break;
338 case 'f':
339 foreground = 1;
340 break;
341 case 'i':
342 retval = atoi(optarg);
343 foreground = 1;
344 break;
345 default:
346 DERR(EX_USAGE, "%s [-d level] [-s] [-f] [-i fd]", argv[0]);
347 break;
348 }
349 }
350
351 if (!foreground)
352 perfuse_diagflags &= ~PDF_FOREGROUND;
353
354 return retval;
355 }
356
357 int
358 main(argc, argv)
359 int argc;
360 char **argv;
361 {
362 int s;
363
364 s = parse_options(argc, argv);
365
366 if (perfuse_diagflags & PDF_SYSLOG)
367 openlog("perfused", 0, LOG_DAEMON);
368
369 if (!(perfuse_diagflags & PDF_FOREGROUND))
370 if (daemon(0, 0) != 0)
371 DERR(EX_OSERR, "daemon failed");
372
373 if (s != -1) {
374 new_mount(s);
375 DERRX(EX_SOFTWARE, "new_mount exit while -i is used");
376 }
377
378 s = perfuse_open_sock();
379
380 do {
381 struct sockaddr *sa;
382 struct sockaddr_storage ss;
383 socklen_t ss_len;
384 int fd;
385
386 #ifdef PERFUSE_DEBUG
387 if (perfuse_diagflags & PDF_MISC)
388 DPRINTF("waiting connexion\n");
389 #endif
390 sa = (struct sockaddr *)(void *)&ss;
391 ss_len = sizeof(ss);
392 if ((fd = accept(s, sa, &ss_len)) == -1)
393 DERR(EX_OSERR, "accept failed");
394 #ifdef PERFUSE_DEBUG
395 if (perfuse_diagflags & PDF_MISC)
396 DPRINTF("connexion accepted\n");
397 #endif
398 new_mount(fd);
399 } while (1 /* CONSTCOND */);
400
401 /* NOTREACHED */
402 return 0;
403 }
404