perfused.c revision 1.8 1 /* $NetBSD: perfused.c,v 1.8 2010/09/15 01:51:44 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 access_mount(const char *, uid_t, int);
54 static void new_mount(int, int);
55 static int parse_debug(char *);
56 static void siginfo_handler(int);
57 static int parse_options(int, char **);
58 static void get_mount_info(int, struct perfuse_mount_info *);
59 int main(int, char **);
60
61 /*
62 * Flags for new_mount()
63 */
64 #define PMNT_DEVFUSE 0x0 /* We use /dev/fuse */
65 #define PMNT_SOCKPAIR 0x1 /* We use socketpair */
66
67
68 static int
69 access_mount(mnt, uid, ro)
70 const char *mnt;
71 uid_t uid;
72 int ro;
73 {
74 struct stat st;
75 mode_t mode;
76
77 if (uid == 0)
78 return 0;
79
80 if (stat(mnt, &st) == -1)
81 return -1;
82
83 if (st.st_uid != uid)
84 return -1;
85
86 mode = S_IRUSR;
87 if (!ro)
88 mode |= S_IWUSR;
89
90 if ((st.st_mode & mode) == mode)
91 return 0;
92
93 return -1;
94 }
95
96 static void
97 get_mount_info(fd, pmi)
98 int fd;
99 struct perfuse_mount_info *pmi;
100 {
101 struct perfuse_mount_out *pmo;
102 struct sockcred cred;
103 char *cp;
104 char *source = NULL;
105 char *target = NULL;
106 char *filesystemtype = NULL;
107 long mountflags = 0;
108 void *data = NULL;
109 char *sock = NULL;
110
111 pmo = (struct perfuse_mount_out *)
112 perfuse_recv_early(fd, &cred, sizeof(cred));
113
114 if (pmo == NULL) {
115 if (shutdown(fd, SHUT_RDWR) != 0)
116 DERR(EX_OSERR, "shutdown failed");
117 exit(EX_PROTOCOL);
118 }
119
120 #ifdef PERFUSE_DEBUG
121 if (perfuse_diagflags & PDF_MISC)
122 DPRINTF("perfuse lengths: source = %"PRId32", "
123 "target = %"PRId32", filesystemtype = %"PRId32", "
124 "data = %"PRId32", sock = %"PRId32"\n",
125 pmo->pmo_source_len, pmo->pmo_target_len,
126 pmo->pmo_filesystemtype_len, pmo->pmo_data_len,
127 pmo->pmo_sock_len);
128 #endif
129 cp = (char *)(void *)(pmo + 1);
130
131 if (pmo->pmo_source_len != 0) {
132 source = cp;
133 cp += pmo->pmo_source_len;
134 }
135
136 if (pmo->pmo_target_len != 0) {
137 target = cp;
138 cp += pmo->pmo_target_len;
139 }
140
141 if (pmo->pmo_filesystemtype_len != 0) {
142 filesystemtype = cp;
143 cp += pmo->pmo_filesystemtype_len;
144 }
145
146 mountflags = pmo->pmo_mountflags;
147
148 if (pmo->pmo_data_len != 0) {
149 data = cp;
150 cp += pmo->pmo_data_len;
151 }
152
153 if (pmo->pmo_sock_len != 0) {
154 sock = cp;
155 cp += pmo->pmo_sock_len;
156 }
157
158 #ifdef PERFUSE_DEBUG
159 if (perfuse_diagflags & PDF_MISC)
160 DPRINTF("%s(\"%s\", \"%s\", \"%s\", 0x%lx, \"%s\", \"%s\")\n",
161 __func__, source, target, filesystemtype,
162 mountflags, (const char *)data, sock);
163 #endif
164 pmi->pmi_source = source;
165 pmi->pmi_target = target;
166 pmi->pmi_filesystemtype = filesystemtype;
167 pmi->pmi_mountflags = (int)mountflags;
168 pmi->pmi_data = data;
169
170 pmi->pmi_uid = cred.sc_euid;
171
172 /*
173 * Connect to the remote socket, if provided
174 */
175 if (sock) {
176 const struct sockaddr *sa;
177 struct sockaddr_un sun;
178
179 sa = (const struct sockaddr *)(void *)&sun;
180 sun.sun_len = sizeof(sun);
181 sun.sun_family = AF_LOCAL;
182 strcpy(sun.sun_path, sock);
183
184 if (connect(fd, sa, sun.sun_len) != 0)
185 DERR(EX_OSERR, "connect \"%s\" failed", sun.sun_path);
186 }
187
188 return;
189 }
190
191 static void
192 new_mount(fd, pmnt_flags)
193 int fd;
194 int pmnt_flags;
195 {
196 struct puffs_usermount *pu;
197 struct perfuse_mount_info pmi;
198 struct perfuse_callbacks pc;
199 int ro_flag;
200 pid_t pid;
201 int flags;
202
203 pid = (perfuse_diagflags & PDF_FOREGROUND) ? 0 : fork();
204 switch(pid) {
205 case -1:
206 DERR(EX_OSERR, "cannot fork");
207 break;
208 case 0:
209 break;
210 default:
211 return;
212 /* NOTREACHED */
213 break;
214 }
215
216 /*
217 * Mount information (source, target, mount flags...)
218 */
219 get_mount_info(fd, &pmi);
220
221 /*
222 * Check that peer owns mountpoint and read (and write) on it?
223 */
224 ro_flag = pmi.pmi_mountflags & MNT_RDONLY;
225 if (access_mount(pmi.pmi_target, pmi.pmi_uid, ro_flag) != 0)
226 DERRX(EX_NOPERM, "insuficient privileges to mount on %s",
227 pmi.pmi_target);
228
229
230 /*
231 * Initialize libperfuse, which will initialize libpuffs
232 */
233 pc.pc_new_msg = perfuse_new_pb;
234 pc.pc_xchg_msg = perfuse_xchg_pb;
235 pc.pc_destroy_msg = (perfuse_destroy_msg_fn)puffs_framebuf_destroy;
236 pc.pc_get_inhdr = perfuse_get_inhdr;
237 pc.pc_get_inpayload = perfuse_get_inpayload;
238 pc.pc_get_outhdr = perfuse_get_outhdr;
239 pc.pc_get_outpayload = perfuse_get_outpayload;
240
241 pu = perfuse_init(&pc, &pmi);
242
243 puffs_framev_init(pu, perfuse_readframe, perfuse_writeframe,
244 perfuse_cmpframe, perfuse_gotframe, perfuse_fdnotify);
245
246 if (puffs_framev_addfd(pu, fd, PUFFS_FBIO_READ|PUFFS_FBIO_WRITE) == -1)
247 DERR(EX_SOFTWARE, "puffs_framev_addfd failed");
248
249 perfuse_setspecific(pu, (void *)(long)fd);
250
251 setproctitle("perfused %s", pmi.pmi_target);
252 (void)kill(getpid(), SIGINFO); /* This is for -s option */
253
254 perfuse_fs_init(pu);
255
256 /*
257 * Non blocking I/O on /dev/fuse
258 * This must be done after perfuse_fs_init
259 */
260 if ((flags = fcntl(fd, F_GETFL, 0)) == -1)
261 DERR(EX_OSERR, "fcntl failed");
262 if (fcntl(fd, F_SETFL, flags|O_NONBLOCK) != 0)
263 DERR(EX_OSERR, "fcntl failed");
264
265 /*
266 * Hand over control to puffs main loop.
267 */
268 (void)perfuse_mainloop(pu);
269
270 DERRX(EX_SOFTWARE, "perfuse_mainloop exit");
271
272 /* NOTREACHED */
273 return;
274 }
275
276 static int
277 parse_debug(optstr)
278 char *optstr;
279 {
280 int retval = PDF_SYSLOG;
281 char *opt;
282 char *lastp;
283
284 for (opt = strtok_r(optstr, ",", &lastp);
285 opt;
286 opt = strtok_r(NULL, ",", &lastp)) {
287 if (strcmp(opt, "fuse") == 0)
288 retval |= PDF_FUSE;
289 else if (strcmp(opt, "puffs") == 0)
290 retval |= PDF_PUFFS;
291 else if (strcmp(opt, "dump") == 0)
292 retval |= PDF_DUMP;
293 else if (strcmp(opt, "fh") == 0)
294 retval |= PDF_FH;
295 else if (strcmp(opt, "readdir") == 0)
296 retval |= PDF_READDIR;
297 else if (strcmp(opt, "reclaim") == 0)
298 retval |= PDF_RECLAIM;
299 else if (strcmp(opt, "requeue") == 0)
300 retval |= PDF_REQUEUE;
301 else if (strcmp(opt, "sync") == 0)
302 retval |= PDF_SYNC;
303 else if (strcmp(opt, "misc") == 0)
304 retval |= PDF_MISC;
305 else
306 DERRX(EX_USAGE, "unknown debug flag \"%s\"", opt);
307 }
308
309 return retval;
310 }
311
312 /* ARGSUSED0 */
313 static void
314 siginfo_handler(sig)
315 int sig;
316 {
317 static int old_flags = 0;
318 int swap;
319
320 swap = perfuse_diagflags;
321 perfuse_diagflags = old_flags;
322 old_flags = swap;
323
324 DWARNX("debug %sabled", old_flags == 0 ? "en" : "dis");
325
326 return;
327 }
328
329 static int
330 parse_options(argc, argv)
331 int argc;
332 char **argv;
333 {
334 int ch;
335 int foreground = 0;
336 int retval = -1;
337
338 perfuse_diagflags = PDF_FOREGROUND | PDF_SYSLOG;
339
340 while ((ch = getopt(argc, argv, "d:fsi:")) != -1) {
341 switch (ch) {
342 case 'd':
343 perfuse_diagflags |= parse_debug(optarg);
344 break;
345 case 's':
346 if (signal(SIGINFO, siginfo_handler) != 0)
347 DERR(EX_OSERR, "signal failed");
348 break;
349 case 'f':
350 foreground = 1;
351 perfuse_diagflags |= PDF_MISC;
352 break;
353 case 'i':
354 retval = atoi(optarg);
355 foreground = 1;
356 break;
357 default:
358 DERR(EX_USAGE, "%s [-fs] [-d level] [-i fd]", argv[0]);
359 break;
360 }
361 }
362
363 if (!foreground)
364 perfuse_diagflags &= ~PDF_FOREGROUND;
365
366 return retval;
367 }
368
369 int
370 main(argc, argv)
371 int argc;
372 char **argv;
373 {
374 int s;
375
376 s = parse_options(argc, argv);
377
378 if (perfuse_diagflags & PDF_SYSLOG)
379 openlog("perfused", 0, LOG_DAEMON);
380
381 if (!(perfuse_diagflags & PDF_FOREGROUND))
382 if (daemon(0, 0) != 0)
383 DERR(EX_OSERR, "daemon failed");
384
385 if (s != -1) {
386 new_mount(s, PMNT_SOCKPAIR);
387 DERRX(EX_SOFTWARE, "new_mount exit while -i is used");
388 }
389
390 s = perfuse_open_sock();
391
392 do {
393 #if (PERFUSE_SOCKTYPE != SOCK_DGRAM)
394 struct sockaddr *sa;
395 struct sockaddr_storage ss;
396 socklen_t ss_len;
397 #endif
398 int fd;
399
400 #ifdef PERFUSE_DEBUG
401 if (perfuse_diagflags & PDF_MISC)
402 DPRINTF("waiting connexion\n");
403 #endif
404 #if (PERFUSE_SOCKTYPE == SOCK_DGRAM)
405 fd = s;
406 #else
407 sa = (struct sockaddr *)(void *)&ss;
408 ss_len = sizeof(ss);
409
410 if ((fd = accept(s, sa, &ss_len)) == -1)
411 DERR(EX_OSERR, "accept failed");
412
413 #ifdef PERFUSE_DEBUG
414 if (perfuse_diagflags & PDF_MISC)
415 DPRINTF("connexion accepted\n");
416 #endif /* PERFUSE_DEBUG */
417 #endif /* PERFUSE_SOCKTYPE */
418
419 new_mount(fd, PMNT_DEVFUSE);
420 } while (1 /* CONSTCOND */);
421
422 /* NOTREACHED */
423 return 0;
424 }
425