1 /* $NetBSD: perfused.c,v 1.26 2021/08/08 20:56:54 nia 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 "perfused.h"
51
52 /*
53 * This is used for trace file. of course it will not work if
54 * we ever mount multiple filesystems in a single perfused,
55 * but it is not sure we will ever want to do that.
56 */
57 static struct puffs_usermount *perfused_mount = NULL;
58 static FILE *perfused_trace = NULL;
59
60 static int access_mount(const char *, uid_t, int);
61 static void new_mount(int, int);
62 static int parse_debug(char *);
63 static void siginfo_handler(int);
64 static void sigusr1_handler(int);
65 static int parse_options(int, char **);
66 static void get_mount_info(int, struct perfuse_mount_info *, int);
67
68 /*
69 * Flags for new_mount()
70 */
71 #define PMNT_DEVFUSE 0x0 /* We use /dev/fuse */
72 #define PMNT_SOCKPAIR 0x1 /* We use socketpair */
73 #define PMNT_DGRAM 0x2 /* We use SOCK_DGRAM sockets */
74
75
76 static int
access_mount(const char * mnt,uid_t uid,int ro)77 access_mount(const char *mnt, uid_t uid, int ro)
78 {
79 struct stat st;
80 mode_t mode;
81
82 if (uid == 0)
83 return 0;
84
85 if (stat(mnt, &st) == -1)
86 return -1;
87
88 if (st.st_uid != uid)
89 return -1;
90
91 mode = S_IRUSR;
92 if (!ro)
93 mode |= S_IWUSR;
94
95 if ((st.st_mode & mode) == mode)
96 return 0;
97
98 return -1;
99 }
100
101 static void
get_mount_info(int fd,struct perfuse_mount_info * pmi,int sock_type)102 get_mount_info(int fd, struct perfuse_mount_info *pmi, int sock_type)
103 {
104 struct perfuse_mount_out *pmo;
105 struct sockcred cred;
106 int opt;
107 char *cp;
108 char *source = NULL;
109 char *target = NULL;
110 char *filesystemtype = NULL;
111 long mountflags = 0;
112 void *data = NULL;
113 char *sock = NULL;
114
115 pmo = (struct perfuse_mount_out *)
116 perfused_recv_early(fd, &cred, sizeof(cred));
117
118 if (pmo == NULL) {
119 if (shutdown(fd, SHUT_RDWR) != 0)
120 DERR(EX_OSERR, "shutdown failed");
121 exit(EX_PROTOCOL);
122 }
123
124 /*
125 * We do not need peer creds beyond this point
126 */
127 opt = 0;
128 if (setsockopt(fd, SOL_LOCAL, LOCAL_CREDS, &opt, sizeof(opt)) != 0)
129 DWARN("%s: setsockopt LOCAL_CREDS failed", __func__);
130
131 #ifdef PERFUSE_DEBUG
132 if (perfuse_diagflags & PDF_MISC)
133 DPRINTF("perfuse lengths: source = %"PRId32", "
134 "target = %"PRId32", filesystemtype = %"PRId32", "
135 "data = %"PRId32", sock = %"PRId32"\n",
136 pmo->pmo_source_len, pmo->pmo_target_len,
137 pmo->pmo_filesystemtype_len, pmo->pmo_data_len,
138 pmo->pmo_sock_len);
139 #endif
140 cp = (char *)(void *)(pmo + 1);
141
142 if (pmo->pmo_source_len != 0) {
143 source = cp;
144 cp += pmo->pmo_source_len;
145 }
146
147 if (pmo->pmo_target_len != 0) {
148 target = cp;
149 cp += pmo->pmo_target_len;
150 }
151
152 if (pmo->pmo_filesystemtype_len != 0) {
153 filesystemtype = cp;
154 cp += pmo->pmo_filesystemtype_len;
155 }
156
157 mountflags = pmo->pmo_mountflags;
158
159 if (pmo->pmo_data_len != 0) {
160 data = cp;
161 cp += pmo->pmo_data_len;
162 }
163
164 if (pmo->pmo_sock_len != 0) {
165 sock = cp;
166 cp += pmo->pmo_sock_len;
167 }
168
169 #ifdef PERFUSE_DEBUG
170 if (perfuse_diagflags & PDF_MISC)
171 DPRINTF("%s(\"%s\", \"%s\", \"%s\", 0x%lx, \"%s\", \"%s\")\n",
172 __func__, source, target, filesystemtype,
173 mountflags, (const char *)data, sock);
174 #endif
175 pmi->pmi_source = source;
176 pmi->pmi_target = target;
177 pmi->pmi_filesystemtype = filesystemtype;
178 pmi->pmi_mountflags = (int)mountflags;
179 pmi->pmi_data = data;
180
181 pmi->pmi_uid = cred.sc_euid;
182
183 /*
184 * Connect to the remote socket if provided ans using SOCK_DGRAM
185 */
186 if ((sock_type == SOCK_DGRAM) && sock) {
187 const struct sockaddr *sa;
188 struct sockaddr_un sun;
189
190 sa = (const struct sockaddr *)(void *)&sun;
191 sun.sun_len = sizeof(sun);
192 sun.sun_family = AF_LOCAL;
193 strcpy(sun.sun_path, sock);
194
195 if (connect(fd, sa, sun.sun_len) != 0)
196 DERR(EX_OSERR, "connect \"%s\" failed", sun.sun_path);
197 }
198
199 return;
200 }
201
202 static void
new_mount(int fd,int pmnt_flags)203 new_mount(int fd, int pmnt_flags)
204 {
205 struct puffs_usermount *pu;
206 struct perfuse_mount_info pmi;
207 struct perfuse_callbacks pc;
208 int ro_flag;
209 pid_t pid;
210 int flags;
211 int sock_type;
212 char trace_file[MAXPATHLEN + 1];
213 char trace_name[MAXPATHLEN + 1];
214 ssize_t trace_namelen;
215 int i;
216
217 pid = (perfuse_diagflags & PDF_FOREGROUND) ? 0 : fork();
218 switch(pid) {
219 case -1:
220 DERR(EX_OSERR, "cannot fork");
221 break;
222 case 0:
223 break;
224 default:
225 return;
226 /* NOTREACHED */
227 break;
228 }
229
230 /*
231 * Mount information (source, target, mount flags...)
232 */
233 sock_type = pmnt_flags & PMNT_DGRAM ? SOCK_DGRAM : SOCK_SEQPACKET;
234 get_mount_info(fd, &pmi, sock_type);
235
236 /*
237 * Check that peer owns mountpoint and read (and write) on it?
238 */
239 ro_flag = pmi.pmi_mountflags & MNT_RDONLY;
240 if (access_mount(pmi.pmi_target, pmi.pmi_uid, ro_flag) != 0)
241 DERRX(EX_NOPERM, "insuficient privileges to mount on %s",
242 pmi.pmi_target);
243
244
245 /*
246 * Initialize libperfuse, which will initialize libpuffs
247 */
248 (void)memset(&pc, 0, sizeof(pc));
249 pc.pc_new_msg = perfused_new_pb;
250 pc.pc_xchg_msg = perfused_xchg_pb;
251 pc.pc_destroy_msg = (perfuse_destroy_msg_fn)puffs_framebuf_destroy;
252 pc.pc_get_inhdr = perfused_get_inhdr;
253 pc.pc_get_inpayload = perfused_get_inpayload;
254 pc.pc_get_outhdr = perfused_get_outhdr;
255 pc.pc_get_outpayload = perfused_get_outpayload;
256 pc.pc_umount = perfused_umount;
257 pc.pc_fsreq = perfused_gotframe;
258
259 pu = perfuse_init(&pc, &pmi);
260
261 puffs_framev_init(pu, perfused_readframe, perfused_writeframe,
262 perfused_cmpframe, pc.pc_fsreq, perfused_fdnotify);
263
264 if (puffs_framev_addfd(pu, fd, PUFFS_FBIO_READ|PUFFS_FBIO_WRITE) == -1)
265 DERR(EX_SOFTWARE, "puffs_framev_addfd failed");
266
267 perfuse_setspecific(pu, (void *)(intptr_t)fd);
268
269 setproctitle("perfused %s", pmi.pmi_target);
270 (void)kill(getpid(), SIGINFO); /* This is for -s option */
271
272 perfuse_fs_init(pu);
273
274 /*
275 * Non blocking I/O on /dev/fuse
276 * This must be done after perfused_fs_init
277 */
278 if ((flags = fcntl(fd, F_GETFL, 0)) == -1)
279 DERR(EX_OSERR, "fcntl failed");
280 if (fcntl(fd, F_SETFL, flags|O_NONBLOCK) != 0)
281 DERR(EX_OSERR, "fcntl failed");
282
283 /*
284 * Setup trace file facility
285 */
286 perfused_mount = pu;
287
288 trace_namelen = strlcpy(trace_name, pmi.pmi_target, MAXPATHLEN);
289 for (i = 0; i < trace_namelen; i++)
290 if (trace_name[i] == '/')
291 trace_name[i] = '-';
292
293 (void)snprintf(trace_file, MAXPATHLEN, _PATH_VAR_RUN_PERFUSE_TRACE,
294 trace_name);
295
296 if ((perfused_trace = fopen(trace_file, "w")) == NULL)
297 DERR(EX_OSFILE,
298 "could not open \"%s\"",
299 _PATH_VAR_RUN_PERFUSE_TRACE);
300
301 if (signal(SIGUSR1, sigusr1_handler) == SIG_ERR)
302 DERR(EX_OSERR, "signal failed");
303
304 /*
305 * Hand over control to puffs main loop.
306 */
307 if (perfuse_mainloop(pu) != 0)
308 DERRX(EX_SOFTWARE, "perfused_mainloop exit");
309
310 /*
311 * Normal return after unmount
312 */
313 return;
314 }
315
316 static int
parse_debug(char * optstr)317 parse_debug(char *optstr)
318 {
319 int retval = PDF_SYSLOG;
320 char *opt;
321 char *lastp;
322
323 for (opt = strtok_r(optstr, ",", &lastp);
324 opt;
325 opt = strtok_r(NULL, ",", &lastp)) {
326 if (strcmp(opt, "fuse") == 0)
327 retval |= PDF_FUSE;
328 else if (strcmp(opt, "puffs") == 0)
329 retval |= PDF_PUFFS;
330 else if (strcmp(opt, "dump") == 0)
331 retval |= PDF_DUMP;
332 else if (strcmp(opt, "fh") == 0)
333 retval |= PDF_FH;
334 else if (strcmp(opt, "readdir") == 0)
335 retval |= PDF_READDIR;
336 else if (strcmp(opt, "reclaim") == 0)
337 retval |= PDF_RECLAIM;
338 else if (strcmp(opt, "requeue") == 0)
339 retval |= PDF_REQUEUE;
340 else if (strcmp(opt, "sync") == 0)
341 retval |= PDF_SYNC;
342 else if (strcmp(opt, "misc") == 0)
343 retval |= PDF_MISC;
344 else if (strcmp(opt, "filename") == 0)
345 retval |= PDF_FILENAME;
346 else if (strcmp(opt, "reize") == 0)
347 retval |= PDF_RESIZE;
348 else
349 DWARNX("unknown debug flag \"%s\"", opt);
350 }
351
352 return retval;
353 }
354
355 /* ARGSUSED0 */
356 static void
siginfo_handler(int sig)357 siginfo_handler(int sig)
358 {
359 static int old_flags = 0;
360 int swap;
361
362 swap = perfuse_diagflags;
363 perfuse_diagflags = old_flags;
364 old_flags = swap;
365
366 DWARNX("debug %sabled", old_flags == 0 ? "en" : "dis");
367
368 return;
369 }
370
371 /* ARGSUSED0 */
372 static void
sigusr1_handler(int sig)373 sigusr1_handler(int sig)
374 {
375 if (perfuse_diagflags & PDF_TRACE) {
376 perfuse_trace_dump(perfused_mount, perfused_trace);
377 perfuse_diagflags &= ~PDF_TRACE;
378 DPRINTF("trace dumped, trace disabled\n");
379 } else {
380 perfuse_diagflags |= PDF_TRACE;
381 DPRINTF("trace enabled\n");
382 }
383
384 return;
385 }
386
387 static int
parse_options(int argc,char ** argv)388 parse_options(int argc, char **argv)
389 {
390 int ch;
391 int foreground = 0;
392 int retval = -1;
393
394 perfuse_diagflags = PDF_FOREGROUND | PDF_SYSLOG;
395
396 while ((ch = getopt(argc, argv, "d:fsi:")) != -1) {
397 switch (ch) {
398 case 'd':
399 perfuse_diagflags |= parse_debug(optarg);
400 break;
401 case 's':
402 if (signal(SIGINFO, siginfo_handler) == SIG_ERR)
403 DERR(EX_OSERR, "signal failed");
404 break;
405 case 'f':
406 foreground = 1;
407 perfuse_diagflags |= PDF_MISC;
408 break;
409 case 'i':
410 retval = atoi(optarg);
411 foreground = 1;
412 break;
413 default:
414 DERRX(EX_USAGE, "%s [-fs] [-d classes] [-i fd]", argv[0]);
415 break;
416 }
417 }
418
419 if (!foreground)
420 perfuse_diagflags &= ~PDF_FOREGROUND;
421
422 return retval;
423 }
424
425 int
main(int argc,char ** argv)426 main(int argc, char **argv)
427 {
428 int s;
429 int sock_type;
430 socklen_t len;
431
432 s = parse_options(argc, argv);
433
434 if (perfuse_diagflags & PDF_SYSLOG)
435 openlog("perfused", LOG_NDELAY, LOG_DAEMON);
436
437 if (!(perfuse_diagflags & PDF_FOREGROUND))
438 if (daemon(0, 0) != 0)
439 DERR(EX_OSERR, "daemon failed");
440
441 if (s != -1) {
442 new_mount(s, PMNT_SOCKPAIR);
443 exit(0);
444 }
445
446 s = perfused_open_sock();
447
448 #ifdef PERFUSE_DEBUG
449 if (perfuse_diagflags & PDF_MISC)
450 DPRINTF("perfused ready\n");
451 #endif
452 len = sizeof(sock_type);
453 if (getsockopt(s, SOL_SOCKET, SO_TYPE, &sock_type, &len) != 0)
454 DERR(EX_OSERR, "getsockopt SO_TYPE failed");
455
456 switch(sock_type) {
457 case SOCK_DGRAM:
458 new_mount(s, PMNT_DEVFUSE|PMNT_DGRAM);
459 exit(0);
460 case SOCK_SEQPACKET:
461 if (listen(s, 0) != 0)
462 DERR(EX_OSERR, "listen failed");
463
464 for (;;) {
465 int fd;
466 struct sockaddr_un sun;
467 struct sockaddr *sa;
468
469 len = sizeof(sun);
470 sa = (struct sockaddr *)(void *)&sun;
471 if ((fd = accept(s, sa, &len)) == -1)
472 DERR(EX_OSERR, "accept failed");
473
474 new_mount(fd, PMNT_DEVFUSE);
475 }
476 default:
477 DERRX(EX_SOFTWARE, "unexpected so_type %d", sock_type);
478 break;
479 }
480
481 /* NOTREACHED */
482 return 0;
483 }
484
485 void
perfused_panic(void)486 perfused_panic(void)
487 {
488 DWARNX("filesystem crashed");
489 exit(EX_OK);
490 }
491