1 /* $NetBSD: fstest_puffs.c,v 1.6 2010/11/01 16:27:07 pooka Exp $ */ 2 3 /* 4 * Copyright (c) 2010 The NetBSD Foundation, Inc. 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 AUTHOR ``AS IS'' AND ANY EXPRESS 16 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 */ 27 28 #include <sys/types.h> 29 #include <sys/mount.h> 30 #include <sys/socket.h> 31 #include <sys/statvfs.h> 32 #include <sys/wait.h> 33 34 #include <assert.h> 35 #include <atf-c.h> 36 #include <err.h> 37 #include <errno.h> 38 #include <fcntl.h> 39 #include <pthread.h> 40 #include <puffs.h> 41 #include <puffsdump.h> 42 #include <signal.h> 43 #include <stdio.h> 44 #include <unistd.h> 45 #include <string.h> 46 #include <stdlib.h> 47 48 #include <rump/rump.h> 49 #include <rump/rump_syscalls.h> 50 51 #include "h_fsmacros.h" 52 53 #define BUFSIZE (128*1024) 54 #define DTFS_DUMP "-o","dump" 55 56 static bool mayquit = false; 57 58 /* 59 * Threads which shovel data between comfd and /dev/puffs. 60 * (cannot use polling since fd's are in different namespaces) 61 */ 62 static void * 63 readshovel(void *arg) 64 { 65 struct putter_hdr *phdr; 66 struct puffs_req *preq; 67 struct puffstestargs *args = arg; 68 char buf[BUFSIZE]; 69 ssize_t n; 70 int comfd, puffsfd; 71 72 comfd = args->pta_servfd; 73 puffsfd = args->pta_rumpfd; 74 75 phdr = (void *)buf; 76 preq = (void *)buf; 77 78 rump_pub_lwproc_newlwp(1); 79 80 for (;;) { 81 n = rump_sys_read(puffsfd, buf, sizeof(*phdr)); 82 if (n <= 0) { 83 fprintf(stderr, "readshovel r1 %zd / %d\n", n, errno); 84 break; 85 } 86 87 assert(phdr->pth_framelen < BUFSIZE); 88 n = rump_sys_read(puffsfd, buf+sizeof(*phdr), 89 phdr->pth_framelen - sizeof(*phdr)); 90 if (n <= 0) { 91 fprintf(stderr, "readshovel r2 %zd / %d\n", n, errno); 92 break; 93 } 94 95 /* Analyze request */ 96 if (PUFFSOP_OPCLASS(preq->preq_opclass) == PUFFSOP_VFS) { 97 assert(preq->preq_optype < PUFFS_VFS_MAX); 98 args->pta_vfs_toserv_ops[preq->preq_optype]++; 99 } else if (PUFFSOP_OPCLASS(preq->preq_opclass) == PUFFSOP_VN) { 100 assert(preq->preq_optype < PUFFS_VN_MAX); 101 args->pta_vn_toserv_ops[preq->preq_optype]++; 102 } 103 104 n = phdr->pth_framelen; 105 if (write(comfd, buf, n) != n) { 106 fprintf(stderr, "readshovel write %zd / %d\n", n, errno); 107 break; 108 } 109 } 110 111 if (n != 0 && mayquit == false) 112 abort(); 113 return NULL; 114 } 115 116 static void * 117 writeshovel(void *arg) 118 { 119 struct puffstestargs *args = arg; 120 struct putter_hdr *phdr; 121 struct puffs_req *preq; 122 char buf[BUFSIZE]; 123 size_t toread; 124 ssize_t n; 125 int comfd, puffsfd; 126 127 rump_pub_lwproc_newlwp(1); 128 129 comfd = args->pta_servfd; 130 puffsfd = args->pta_rumpfd; 131 132 phdr = (struct putter_hdr *)buf; 133 preq = (void *)buf; 134 135 for (;;) { 136 uint64_t off; 137 138 /* 139 * Need to write everything to the "kernel" in one chunk, 140 * so make sure we have it here. 141 */ 142 off = 0; 143 toread = sizeof(struct putter_hdr); 144 assert(toread < BUFSIZE); 145 do { 146 n = read(comfd, buf+off, toread); 147 if (n <= 0) { 148 fprintf(stderr, "writeshovel read %zd / %d\n", 149 n, errno); 150 goto out; 151 } 152 off += n; 153 if (off >= sizeof(struct putter_hdr)) 154 toread = phdr->pth_framelen - off; 155 else 156 toread = off - sizeof(struct putter_hdr); 157 } while (toread); 158 159 if (__predict_false( 160 PUFFSOP_OPCLASS(preq->preq_opclass) == PUFFSOP_VFS 161 && preq->preq_optype == PUFFS_VFS_UNMOUNT)) { 162 if (preq->preq_rv == 0) 163 mayquit = true; 164 } 165 166 n = rump_sys_write(puffsfd, buf, phdr->pth_framelen); 167 if ((size_t)n != phdr->pth_framelen) { 168 fprintf(stderr, "writeshovel wr %zd / %d\n", n, errno); 169 break; 170 } 171 } 172 173 out: 174 if (n != 0) 175 abort(); 176 return NULL; 177 } 178 179 static void 180 rumpshovels(struct puffstestargs *args) 181 { 182 pthread_t pt; 183 int rv; 184 185 if ((rv = rump_init()) == -1) 186 err(1, "rump_init"); 187 188 if (pthread_create(&pt, NULL, readshovel, args) == -1) 189 err(1, "read shovel"); 190 pthread_detach(pt); 191 192 if (pthread_create(&pt, NULL, writeshovel, args) == -1) 193 err(1, "write shovel"); 194 pthread_detach(pt); 195 } 196 197 static void 198 childfail(int sign) 199 { 200 201 atf_tc_fail("child died"); /* almost signal-safe */ 202 } 203 204 struct puffstestargs *theargs; /* XXX */ 205 206 /* XXX: we don't support size */ 207 int 208 puffs_fstest_newfs(const atf_tc_t *tc, void **argp, 209 const char *image, off_t size, void *fspriv) 210 { 211 struct puffstestargs *args; 212 char dtfs_path[MAXPATHLEN]; 213 char *dtfsargv[6]; 214 char **theargv; 215 pid_t childpid; 216 int *pflags; 217 char comfd[16]; 218 int sv[2]; 219 int mntflags; 220 size_t len; 221 ssize_t n; 222 223 *argp = NULL; 224 225 args = malloc(sizeof(*args)); 226 if (args == NULL) 227 return errno; 228 229 pflags = &args->pta_pflags; 230 231 /* build dtfs exec path from atf test dir */ 232 sprintf(dtfs_path, "%s/../puffs/h_dtfs/h_dtfs", 233 atf_tc_get_config_var(tc, "srcdir")); 234 235 if (fspriv) { 236 theargv = fspriv; 237 theargv[0] = dtfs_path; 238 } else { 239 dtfsargv[0] = dtfs_path; 240 dtfsargv[1] = __UNCONST("-i"); 241 dtfsargv[2] = __UNCONST("-s"); 242 dtfsargv[3] = __UNCONST("dtfs"); 243 dtfsargv[4] = __UNCONST("fictional"); 244 dtfsargv[5] = NULL; 245 246 theargv = dtfsargv; 247 } 248 249 /* Create sucketpair for communication with the real file server */ 250 if (socketpair(PF_LOCAL, SOCK_STREAM, 0, sv) == -1) 251 return errno; 252 253 signal(SIGCHLD, childfail); 254 255 switch ((childpid = fork())) { 256 case 0: 257 close(sv[1]); 258 snprintf(comfd, sizeof(sv[0]), "%d", sv[0]); 259 if (setenv("PUFFS_COMFD", comfd, 1) == -1) 260 return errno; 261 262 if (execvp(theargv[0], theargv) == -1) 263 return errno; 264 case -1: 265 return errno; 266 default: 267 close(sv[0]); 268 break; 269 } 270 271 /* read args */ 272 if ((n = read(sv[1], &len, sizeof(len))) != sizeof(len)) 273 err(1, "mp 1 %zd", n); 274 if (len > MAXPATHLEN) 275 err(1, "mntpath > MAXPATHLEN"); 276 if ((size_t)read(sv[1], args->pta_dir, len) != len) 277 err(1, "mp 2"); 278 if (read(sv[1], &len, sizeof(len)) != sizeof(len)) 279 err(1, "fn 1"); 280 if (len > MAXPATHLEN) 281 err(1, "devpath > MAXPATHLEN"); 282 if ((size_t)read(sv[1], args->pta_dev, len) != len) 283 err(1, "fn 2"); 284 if (read(sv[1], &mntflags, sizeof(mntflags)) != sizeof(mntflags)) 285 err(1, "mntflags"); 286 if (read(sv[1], &args->pta_pargslen, sizeof(args->pta_pargslen)) 287 != sizeof(args->pta_pargslen)) 288 err(1, "puffstest_args len"); 289 args->pta_pargs = malloc(args->pta_pargslen); 290 if (args->pta_pargs == NULL) 291 err(1, "malloc"); 292 if (read(sv[1], args->pta_pargs, args->pta_pargslen) 293 != (ssize_t)args->pta_pargslen) 294 err(1, "puffstest_args"); 295 if (read(sv[1], pflags, sizeof(*pflags)) != sizeof(*pflags)) 296 err(1, "pflags"); 297 298 args->pta_childpid = childpid; 299 args->pta_servfd = sv[1]; 300 strlcpy(args->pta_dev, image, sizeof(args->pta_dev)); 301 302 *argp = theargs = args; 303 304 return 0; 305 } 306 307 int 308 puffs_fstest_mount(const atf_tc_t *tc, void *arg, const char *path, int flags) 309 { 310 struct puffstestargs *pargs = arg; 311 int fd; 312 313 rump_init(); 314 fd = rump_sys_open("/dev/puffs", O_RDWR); 315 if (fd == -1) 316 return fd; 317 318 #if 0 319 pa->pa_fd = fd; 320 #else 321 assert(fd == 0); /* XXX: FIXME */ 322 #endif 323 324 if (rump_sys_mkdir(path, 0777) == -1) 325 return -1; 326 327 if (rump_sys_mount(MOUNT_PUFFS, path, flags, 328 pargs->pta_pargs, pargs->pta_pargslen) == -1) { 329 /* apply "to kill a child" to avoid atf hang (kludge) */ 330 kill(pargs->pta_childpid, SIGKILL); 331 return -1; 332 } 333 334 pargs->pta_rumpfd = fd; 335 rumpshovels(pargs); 336 337 return 0; 338 } 339 340 int 341 puffs_fstest_delfs(const atf_tc_t *tc, void *arg) 342 { 343 344 /* useless ... */ 345 return 0; 346 } 347 348 int 349 puffs_fstest_unmount(const atf_tc_t *tc, const char *path, int flags) 350 { 351 struct puffstestargs *pargs = theargs; 352 int status; 353 int rv; 354 355 /* ok, child might exit here */ 356 signal(SIGCHLD, SIG_IGN); 357 358 rv = rump_sys_unmount(path, flags); 359 if (rv) 360 return rv; 361 362 if ((rv = rump_sys_rmdir(path)) != 0) 363 return rv; 364 365 if (waitpid(pargs->pta_childpid, &status, WNOHANG) > 0) 366 return 0; 367 kill(pargs->pta_childpid, SIGTERM); 368 usleep(10); 369 if (waitpid(pargs->pta_childpid, &status, WNOHANG) > 0) 370 return 0; 371 kill(pargs->pta_childpid, SIGKILL); 372 usleep(500); 373 wait(&status); 374 375 return 0; 376 } 377