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