1272343Sngie/* $NetBSD: fstest_puffs.c,v 1.11 2013/09/09 19:47:38 pooka Exp $ */ 2272343Sngie 3272343Sngie/* 4272343Sngie * Copyright (c) 2010, 2011 The NetBSD Foundation, Inc. 5272343Sngie * 6272343Sngie * Redistribution and use in source and binary forms, with or without 7272343Sngie * modification, are permitted provided that the following conditions 8272343Sngie * are met: 9272343Sngie * 1. Redistributions of source code must retain the above copyright 10272343Sngie * notice, this list of conditions and the following disclaimer. 11272343Sngie * 2. Redistributions in binary form must reproduce the above copyright 12272343Sngie * notice, this list of conditions and the following disclaimer in the 13272343Sngie * documentation and/or other materials provided with the distribution. 14272343Sngie * 15272343Sngie * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS 16272343Sngie * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17272343Sngie * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18272343Sngie * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19272343Sngie * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20272343Sngie * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21272343Sngie * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22272343Sngie * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23272343Sngie * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24272343Sngie * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25272343Sngie * SUCH DAMAGE. 26272343Sngie */ 27272343Sngie 28272343Sngie#include <sys/types.h> 29272343Sngie#include <sys/mount.h> 30272343Sngie#include <sys/socket.h> 31272343Sngie#include <sys/statvfs.h> 32272343Sngie#include <sys/wait.h> 33272343Sngie 34272343Sngie#include <assert.h> 35272343Sngie#include <atf-c.h> 36272343Sngie#include <err.h> 37272343Sngie#include <errno.h> 38272343Sngie#include <fcntl.h> 39272343Sngie#include <pthread.h> 40272343Sngie#include <puffs.h> 41272343Sngie#include <puffsdump.h> 42272343Sngie#include <signal.h> 43272343Sngie#include <stdio.h> 44272343Sngie#include <unistd.h> 45272343Sngie#include <string.h> 46272343Sngie#include <stdlib.h> 47272343Sngie 48272343Sngie#include <rump/rump.h> 49272343Sngie#include <rump/rump_syscalls.h> 50272343Sngie 51272343Sngie#include "h_fsmacros.h" 52272343Sngie 53272343Sngie#define BUFSIZE (128*1024) 54272343Sngie#define DTFS_DUMP "-o","dump" 55272343Sngie 56272343Sngiestatic bool mayquit = false; 57272343Sngie 58272343Sngiestatic ssize_t 59272343Sngiexread(int fd, void *vp, size_t n) 60272343Sngie{ 61272343Sngie size_t left; 62272343Sngie 63272343Sngie left = n; 64272343Sngie do { 65272343Sngie ssize_t ssz; 66272343Sngie 67272343Sngie ssz = read(fd, vp, left); 68272343Sngie if (ssz == -1) { 69272343Sngie return ssz; 70272343Sngie } 71272343Sngie left -= ssz; 72272343Sngie vp = (char *)vp + ssz; 73272343Sngie } while (left > 0); 74272343Sngie return n; 75272343Sngie} 76272343Sngie 77272343Sngiestatic ssize_t 78272343Sngiexwrite(int fd, const void *vp, size_t n) 79272343Sngie{ 80272343Sngie size_t left; 81272343Sngie 82272343Sngie left = n; 83272343Sngie do { 84272343Sngie ssize_t ssz; 85272343Sngie 86272343Sngie ssz = write(fd, vp, left); 87272343Sngie if (ssz == -1) { 88272343Sngie return ssz; 89272343Sngie } 90272343Sngie left -= ssz; 91272343Sngie vp = (const char *)vp + ssz; 92272343Sngie } while (left > 0); 93272343Sngie return n; 94272343Sngie} 95272343Sngie 96272343Sngie/* 97272343Sngie * Threads which shovel data between comfd and /dev/puffs. 98272343Sngie * (cannot use polling since fd's are in different namespaces) 99272343Sngie */ 100272343Sngiestatic void * 101272343Sngiereadshovel(void *arg) 102272343Sngie{ 103272343Sngie struct putter_hdr *phdr; 104272343Sngie struct puffs_req *preq; 105272343Sngie struct puffstestargs *args = arg; 106272343Sngie char buf[BUFSIZE]; 107272343Sngie ssize_t n; 108272343Sngie int comfd, puffsfd; 109272343Sngie 110272343Sngie comfd = args->pta_servfd; 111272343Sngie puffsfd = args->pta_rumpfd; 112272343Sngie 113272343Sngie phdr = (void *)buf; 114272343Sngie preq = (void *)buf; 115272343Sngie 116272343Sngie rump_pub_lwproc_newlwp(1); 117272343Sngie 118272343Sngie for (;;) { 119272343Sngie n = rump_sys_read(puffsfd, buf, sizeof(*phdr)); 120272343Sngie if (n <= 0) { 121272343Sngie fprintf(stderr, "readshovel r1 %zd / %d\n", n, errno); 122272343Sngie break; 123272343Sngie } 124272343Sngie 125272343Sngie assert(phdr->pth_framelen < BUFSIZE); 126272343Sngie n = rump_sys_read(puffsfd, buf+sizeof(*phdr), 127272343Sngie phdr->pth_framelen - sizeof(*phdr)); 128272343Sngie if (n <= 0) { 129272343Sngie fprintf(stderr, "readshovel r2 %zd / %d\n", n, errno); 130272343Sngie break; 131272343Sngie } 132272343Sngie 133272343Sngie /* Analyze request */ 134272343Sngie if (PUFFSOP_OPCLASS(preq->preq_opclass) == PUFFSOP_VFS) { 135272343Sngie assert(preq->preq_optype < PUFFS_VFS_MAX); 136272343Sngie args->pta_vfs_toserv_ops[preq->preq_optype]++; 137272343Sngie } else if (PUFFSOP_OPCLASS(preq->preq_opclass) == PUFFSOP_VN) { 138272343Sngie assert(preq->preq_optype < PUFFS_VN_MAX); 139272343Sngie args->pta_vn_toserv_ops[preq->preq_optype]++; 140272343Sngie } 141272343Sngie 142272343Sngie n = phdr->pth_framelen; 143272343Sngie if (xwrite(comfd, buf, n) != n) { 144272343Sngie fprintf(stderr, "readshovel write %zd / %d\n", n, errno); 145272343Sngie break; 146272343Sngie } 147272343Sngie } 148272343Sngie 149272343Sngie if (n != 0 && mayquit == false) 150272343Sngie abort(); 151272343Sngie return NULL; 152272343Sngie} 153272343Sngie 154272343Sngiestatic void * 155272343Sngiewriteshovel(void *arg) 156272343Sngie{ 157272343Sngie struct puffstestargs *args = arg; 158272343Sngie struct putter_hdr *phdr; 159272343Sngie struct puffs_req *preq; 160272343Sngie char buf[BUFSIZE]; 161272343Sngie size_t toread; 162272343Sngie ssize_t n; 163272343Sngie int comfd, puffsfd; 164272343Sngie 165272343Sngie rump_pub_lwproc_newlwp(1); 166272343Sngie 167272343Sngie comfd = args->pta_servfd; 168272343Sngie puffsfd = args->pta_rumpfd; 169272343Sngie 170272343Sngie phdr = (struct putter_hdr *)buf; 171272343Sngie preq = (void *)buf; 172272343Sngie 173272343Sngie for (;;) { 174272343Sngie uint64_t off; 175272343Sngie 176272343Sngie /* 177272343Sngie * Need to write everything to the "kernel" in one chunk, 178272343Sngie * so make sure we have it here. 179272343Sngie */ 180272343Sngie off = 0; 181272343Sngie toread = sizeof(struct putter_hdr); 182272343Sngie assert(toread < BUFSIZE); 183272343Sngie do { 184272343Sngie n = xread(comfd, buf+off, toread); 185272343Sngie if (n <= 0) { 186272343Sngie fprintf(stderr, "writeshovel read %zd / %d\n", 187272343Sngie n, errno); 188272343Sngie goto out; 189272343Sngie } 190272343Sngie off += n; 191272343Sngie if (off >= sizeof(struct putter_hdr)) 192272343Sngie toread = phdr->pth_framelen - off; 193272343Sngie else 194272343Sngie toread = off - sizeof(struct putter_hdr); 195272343Sngie } while (toread); 196272343Sngie 197272343Sngie if (__predict_false( 198272343Sngie PUFFSOP_OPCLASS(preq->preq_opclass) == PUFFSOP_VFS 199272343Sngie && preq->preq_optype == PUFFS_VFS_UNMOUNT)) { 200272343Sngie if (preq->preq_rv == 0) 201272343Sngie mayquit = true; 202272343Sngie } 203272343Sngie 204272343Sngie n = rump_sys_write(puffsfd, buf, phdr->pth_framelen); 205272343Sngie if ((size_t)n != phdr->pth_framelen) { 206272343Sngie fprintf(stderr, "writeshovel wr %zd / %d\n", n, errno); 207272343Sngie break; 208272343Sngie } 209272343Sngie } 210272343Sngie 211272343Sngie out: 212272343Sngie if (n != 0) 213272343Sngie abort(); 214272343Sngie return NULL; 215272343Sngie} 216272343Sngie 217272343Sngiestatic void 218272343Sngierumpshovels(struct puffstestargs *args) 219272343Sngie{ 220272343Sngie pthread_t pt; 221272343Sngie int rv; 222272343Sngie 223272343Sngie if ((rv = rump_init()) == -1) 224272343Sngie err(1, "rump_init"); 225272343Sngie 226272343Sngie if (pthread_create(&pt, NULL, readshovel, args) == -1) 227272343Sngie err(1, "read shovel"); 228272343Sngie pthread_detach(pt); 229272343Sngie 230272343Sngie if (pthread_create(&pt, NULL, writeshovel, args) == -1) 231272343Sngie err(1, "write shovel"); 232272343Sngie pthread_detach(pt); 233272343Sngie} 234272343Sngie 235272343Sngiestatic void 236272343Sngiechildfail(int sign) 237272343Sngie{ 238272343Sngie 239272343Sngie atf_tc_fail("child died"); /* almost signal-safe */ 240272343Sngie} 241272343Sngie 242272343Sngiestruct puffstestargs *theargs; /* XXX */ 243272343Sngie 244272343Sngie/* XXX: we don't support size */ 245272343Sngiestatic int 246272343Sngiedonewfs(const atf_tc_t *tc, void **argp, 247272343Sngie const char *image, off_t size, void *fspriv, char **theargv) 248272343Sngie{ 249272343Sngie struct puffstestargs *args; 250272343Sngie pid_t childpid; 251272343Sngie int *pflags; 252272343Sngie char comfd[16]; 253272343Sngie int sv[2]; 254272343Sngie int mntflags; 255272343Sngie size_t len; 256272343Sngie ssize_t n; 257272343Sngie 258272343Sngie *argp = NULL; 259272343Sngie 260272343Sngie args = malloc(sizeof(*args)); 261272343Sngie if (args == NULL) 262272343Sngie return errno; 263272343Sngie memset(args, 0, sizeof(*args)); 264272343Sngie 265272343Sngie pflags = &args->pta_pflags; 266272343Sngie 267272343Sngie /* Create sucketpair for communication with the real file server */ 268272343Sngie if (socketpair(PF_LOCAL, SOCK_STREAM, 0, sv) == -1) 269272343Sngie return errno; 270272343Sngie 271272343Sngie signal(SIGCHLD, childfail); 272272343Sngie 273272343Sngie switch ((childpid = fork())) { 274272343Sngie case 0: 275272343Sngie close(sv[1]); 276272343Sngie snprintf(comfd, sizeof(sv[0]), "%d", sv[0]); 277272343Sngie if (setenv("PUFFS_COMFD", comfd, 1) == -1) 278272343Sngie return errno; 279272343Sngie 280272343Sngie if (execvp(theargv[0], theargv) == -1) 281272343Sngie return errno; 282272343Sngie case -1: 283272343Sngie return errno; 284272343Sngie default: 285272343Sngie close(sv[0]); 286272343Sngie break; 287272343Sngie } 288272343Sngie 289272343Sngie /* read args */ 290272343Sngie if ((n = xread(sv[1], &len, sizeof(len))) != sizeof(len)) 291272343Sngie err(1, "mp 1 %zd", n); 292272343Sngie if (len > MAXPATHLEN) 293272343Sngie err(1, "mntpath > MAXPATHLEN"); 294272343Sngie if ((size_t)xread(sv[1], args->pta_dir, len) != len) 295272343Sngie err(1, "mp 2"); 296272343Sngie if (xread(sv[1], &len, sizeof(len)) != sizeof(len)) 297272343Sngie err(1, "fn 1"); 298272343Sngie if (len > MAXPATHLEN) 299272343Sngie err(1, "devpath > MAXPATHLEN"); 300272343Sngie if ((size_t)xread(sv[1], args->pta_dev, len) != len) 301272343Sngie err(1, "fn 2"); 302272343Sngie if (xread(sv[1], &mntflags, sizeof(mntflags)) != sizeof(mntflags)) 303272343Sngie err(1, "mntflags"); 304272343Sngie if (xread(sv[1], &args->pta_pargslen, sizeof(args->pta_pargslen)) 305272343Sngie != sizeof(args->pta_pargslen)) 306272343Sngie err(1, "puffstest_args len"); 307272343Sngie args->pta_pargs = malloc(args->pta_pargslen); 308272343Sngie if (args->pta_pargs == NULL) 309272343Sngie err(1, "malloc"); 310272343Sngie if (xread(sv[1], args->pta_pargs, args->pta_pargslen) 311272343Sngie != (ssize_t)args->pta_pargslen) 312272343Sngie err(1, "puffstest_args"); 313272343Sngie if (xread(sv[1], pflags, sizeof(*pflags)) != sizeof(*pflags)) 314272343Sngie err(1, "pflags"); 315272343Sngie 316272343Sngie args->pta_childpid = childpid; 317272343Sngie args->pta_servfd = sv[1]; 318272343Sngie strlcpy(args->pta_dev, image, sizeof(args->pta_dev)); 319272343Sngie 320272343Sngie *argp = theargs = args; 321272343Sngie 322272343Sngie return 0; 323272343Sngie} 324272343Sngie 325272343Sngieint 326272343Sngiepuffs_fstest_newfs(const atf_tc_t *tc, void **argp, 327272343Sngie const char *image, off_t size, void *fspriv) 328272343Sngie{ 329272343Sngie char dtfs_path[MAXPATHLEN]; 330272343Sngie char *dtfsargv[6]; 331272343Sngie char **theargv; 332272343Sngie 333272343Sngie /* build dtfs exec path from atf test dir */ 334272343Sngie sprintf(dtfs_path, "%s/../puffs/h_dtfs/h_dtfs", 335272343Sngie atf_tc_get_config_var(tc, "srcdir")); 336272343Sngie 337272343Sngie if (fspriv) { 338272343Sngie theargv = fspriv; 339272343Sngie theargv[0] = dtfs_path; 340272343Sngie } else { 341272343Sngie dtfsargv[0] = dtfs_path; 342272343Sngie dtfsargv[1] = __UNCONST("-i"); 343272343Sngie dtfsargv[2] = __UNCONST("-s"); 344272343Sngie dtfsargv[3] = __UNCONST("dtfs"); 345272343Sngie dtfsargv[4] = __UNCONST("fictional"); 346272343Sngie dtfsargv[5] = NULL; 347272343Sngie 348272343Sngie theargv = dtfsargv; 349272343Sngie } 350272343Sngie 351272343Sngie return donewfs(tc, argp, image, size, fspriv, theargv); 352272343Sngie} 353272343Sngie 354272343Sngieint 355272343Sngiep2k_ffs_fstest_newfs(const atf_tc_t *tc, void **argp, 356272343Sngie const char *image, off_t size, void *fspriv) 357272343Sngie{ 358272343Sngie char *rumpffs_argv[5]; 359272343Sngie int rv; 360272343Sngie 361272343Sngie rump_init(); 362272343Sngie if ((rv = ffs_fstest_newfs(tc, argp, image, size, fspriv)) != 0) 363272343Sngie return rv; 364272343Sngie if (mkdir("p2kffsfake", 0777) == -1 && errno != EEXIST) 365272343Sngie return errno; 366272343Sngie 367272343Sngie setenv("P2K_NODETACH", "1", 1); 368272343Sngie rumpffs_argv[0] = __UNCONST("rump_ffs"); 369272343Sngie rumpffs_argv[1] = __UNCONST(image); 370272343Sngie rumpffs_argv[2] = __UNCONST("p2kffsfake"); /* NOTUSED */ 371272343Sngie rumpffs_argv[3] = NULL; 372272343Sngie 373272343Sngie if ((rv = donewfs(tc, argp, image, size, fspriv, rumpffs_argv)) != 0) 374272343Sngie ffs_fstest_delfs(tc, argp); 375272343Sngie return rv; 376272343Sngie} 377272343Sngie 378272343Sngieint 379272343Sngiepuffs_fstest_mount(const atf_tc_t *tc, void *arg, const char *path, int flags) 380272343Sngie{ 381272343Sngie struct puffstestargs *pargs = arg; 382272343Sngie int fd; 383272343Sngie 384272343Sngie rump_init(); 385272343Sngie fd = rump_sys_open("/dev/puffs", O_RDWR); 386272343Sngie if (fd == -1) 387272343Sngie return fd; 388272343Sngie 389272343Sngie if (rump_sys_mkdir(path, 0777) == -1) 390272343Sngie return -1; 391272343Sngie 392272343Sngie if (rump_sys_mount(MOUNT_PUFFS, path, flags, 393272343Sngie pargs->pta_pargs, pargs->pta_pargslen) == -1) { 394272343Sngie /* apply "to kill a child" to avoid atf hang (kludge) */ 395272343Sngie kill(pargs->pta_childpid, SIGKILL); 396272343Sngie return -1; 397272343Sngie } 398272343Sngie 399272343Sngie pargs->pta_rumpfd = fd; 400272343Sngie rumpshovels(pargs); 401272343Sngie 402272343Sngie return 0; 403272343Sngie} 404272343Sngie__strong_alias(p2k_ffs_fstest_mount,puffs_fstest_mount); 405272343Sngie 406272343Sngieint 407272343Sngiepuffs_fstest_delfs(const atf_tc_t *tc, void *arg) 408272343Sngie{ 409272343Sngie 410272343Sngie /* useless ... */ 411272343Sngie return 0; 412272343Sngie} 413272343Sngie 414272343Sngieint 415272343Sngiep2k_ffs_fstest_delfs(const atf_tc_t *tc, void *arg) 416272343Sngie{ 417272343Sngie 418272343Sngie return ffs_fstest_delfs(tc, arg); 419272343Sngie} 420272343Sngie 421272343Sngieint 422272343Sngiepuffs_fstest_unmount(const atf_tc_t *tc, const char *path, int flags) 423272343Sngie{ 424272343Sngie struct puffstestargs *pargs = theargs; 425272343Sngie int status; 426272343Sngie int rv; 427272343Sngie 428272343Sngie /* ok, child might exit here */ 429272343Sngie signal(SIGCHLD, SIG_IGN); 430272343Sngie 431272343Sngie rv = rump_sys_unmount(path, flags); 432272343Sngie if (rv) 433272343Sngie return rv; 434272343Sngie 435272343Sngie if ((rv = rump_sys_rmdir(path)) != 0) 436272343Sngie return rv; 437272343Sngie 438272343Sngie if (waitpid(pargs->pta_childpid, &status, WNOHANG) > 0) 439272343Sngie return 0; 440272343Sngie kill(pargs->pta_childpid, SIGTERM); 441272343Sngie usleep(10); 442272343Sngie if (waitpid(pargs->pta_childpid, &status, WNOHANG) > 0) 443272343Sngie return 0; 444272343Sngie kill(pargs->pta_childpid, SIGKILL); 445272343Sngie usleep(500); 446272343Sngie wait(&status); 447272343Sngie 448272343Sngie rmdir("p2kffsfake"); 449272343Sngie 450272343Sngie return 0; 451272343Sngie} 452272343Sngie__strong_alias(p2k_ffs_fstest_unmount,puffs_fstest_unmount); 453