]> CyberLeo.Net >> Repos - FreeBSD/releng/10.2.git/blob - contrib/netbsd-tests/fs/common/fstest_puffs.c
- Copy stable/10@285827 to releng/10.2 in preparation for 10.2-RC1
[FreeBSD/releng/10.2.git] / contrib / netbsd-tests / fs / common / fstest_puffs.c
1 /*      $NetBSD: fstest_puffs.c,v 1.11 2013/09/09 19:47:38 pooka Exp $  */
2
3 /*
4  * Copyright (c) 2010, 2011 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 static ssize_t
59 xread(int fd, void *vp, size_t n)
60 {
61         size_t left;
62
63         left = n;
64         do {
65                 ssize_t ssz;
66
67                 ssz = read(fd, vp, left);
68                 if (ssz == -1) {
69                         return ssz;
70                 }
71                 left -= ssz;
72                 vp = (char *)vp + ssz;
73         } while (left > 0);
74         return n;
75 }
76
77 static ssize_t
78 xwrite(int fd, const void *vp, size_t n)
79 {
80         size_t left;
81
82         left = n;
83         do {
84                 ssize_t ssz;
85
86                 ssz = write(fd, vp, left);
87                 if (ssz == -1) {
88                         return ssz;
89                 }
90                 left -= ssz;
91                 vp = (const char *)vp + ssz;
92         } while (left > 0);
93         return n;
94 }
95
96 /*
97  * Threads which shovel data between comfd and /dev/puffs.
98  * (cannot use polling since fd's are in different namespaces)
99  */
100 static void *
101 readshovel(void *arg)
102 {
103         struct putter_hdr *phdr;
104         struct puffs_req *preq;
105         struct puffstestargs *args = arg;
106         char buf[BUFSIZE];
107         ssize_t n;
108         int comfd, puffsfd;
109
110         comfd = args->pta_servfd;
111         puffsfd = args->pta_rumpfd;
112
113         phdr = (void *)buf;
114         preq = (void *)buf;
115
116         rump_pub_lwproc_newlwp(1);
117
118         for (;;) {
119                 n = rump_sys_read(puffsfd, buf, sizeof(*phdr));
120                 if (n <= 0) {
121                         fprintf(stderr, "readshovel r1 %zd / %d\n", n, errno);
122                         break;
123                 }
124
125                 assert(phdr->pth_framelen < BUFSIZE);
126                 n = rump_sys_read(puffsfd, buf+sizeof(*phdr), 
127                     phdr->pth_framelen - sizeof(*phdr));
128                 if (n <= 0) {
129                         fprintf(stderr, "readshovel r2 %zd / %d\n", n, errno);
130                         break;
131                 }
132
133                 /* Analyze request */
134                 if (PUFFSOP_OPCLASS(preq->preq_opclass) == PUFFSOP_VFS) {
135                         assert(preq->preq_optype < PUFFS_VFS_MAX);
136                         args->pta_vfs_toserv_ops[preq->preq_optype]++;
137                 } else if (PUFFSOP_OPCLASS(preq->preq_opclass) == PUFFSOP_VN) {
138                         assert(preq->preq_optype < PUFFS_VN_MAX);
139                         args->pta_vn_toserv_ops[preq->preq_optype]++;
140                 }
141
142                 n = phdr->pth_framelen;
143                 if (xwrite(comfd, buf, n) != n) {
144                         fprintf(stderr, "readshovel write %zd / %d\n", n, errno);
145                         break;
146                 }
147         }
148
149         if (n != 0 && mayquit == false)
150                 abort();
151         return NULL;
152 }
153
154 static void *
155 writeshovel(void *arg)
156 {
157         struct puffstestargs *args = arg;
158         struct putter_hdr *phdr;
159         struct puffs_req *preq;
160         char buf[BUFSIZE];
161         size_t toread;
162         ssize_t n;
163         int comfd, puffsfd;
164
165         rump_pub_lwproc_newlwp(1);
166
167         comfd = args->pta_servfd;
168         puffsfd = args->pta_rumpfd;
169
170         phdr = (struct putter_hdr *)buf;
171         preq = (void *)buf;
172
173         for (;;) {
174                 uint64_t off;
175
176                 /*
177                  * Need to write everything to the "kernel" in one chunk,
178                  * so make sure we have it here.
179                  */
180                 off = 0;
181                 toread = sizeof(struct putter_hdr);
182                 assert(toread < BUFSIZE);
183                 do {
184                         n = xread(comfd, buf+off, toread);
185                         if (n <= 0) {
186                                 fprintf(stderr, "writeshovel read %zd / %d\n",
187                                     n, errno);
188                                 goto out;
189                         }
190                         off += n;
191                         if (off >= sizeof(struct putter_hdr))
192                                 toread = phdr->pth_framelen - off;
193                         else
194                                 toread = off - sizeof(struct putter_hdr);
195                 } while (toread);
196
197                 if (__predict_false(
198                     PUFFSOP_OPCLASS(preq->preq_opclass) == PUFFSOP_VFS
199                     && preq->preq_optype == PUFFS_VFS_UNMOUNT)) {
200                         if (preq->preq_rv == 0)
201                                 mayquit = true;
202                 }
203
204                 n = rump_sys_write(puffsfd, buf, phdr->pth_framelen);
205                 if ((size_t)n != phdr->pth_framelen) {
206                         fprintf(stderr, "writeshovel wr %zd / %d\n", n, errno);
207                         break;
208                 }
209         }
210
211  out:
212         if (n != 0)
213                 abort();
214         return NULL;
215 }
216
217 static void
218 rumpshovels(struct puffstestargs *args)
219 {
220         pthread_t pt;
221         int rv;
222
223         if ((rv = rump_init()) == -1)
224                 err(1, "rump_init");
225
226         if (pthread_create(&pt, NULL, readshovel, args) == -1)
227                 err(1, "read shovel");
228         pthread_detach(pt);
229
230         if (pthread_create(&pt, NULL, writeshovel, args) == -1)
231                 err(1, "write shovel");
232         pthread_detach(pt);
233 }
234
235 static void
236 childfail(int sign)
237 {
238
239         atf_tc_fail("child died"); /* almost signal-safe */
240 }
241
242 struct puffstestargs *theargs; /* XXX */
243
244 /* XXX: we don't support size */
245 static int
246 donewfs(const atf_tc_t *tc, void **argp,
247         const char *image, off_t size, void *fspriv, char **theargv)
248 {
249         struct puffstestargs *args;
250         pid_t childpid;
251         int *pflags;
252         char comfd[16];
253         int sv[2];
254         int mntflags;
255         size_t len;
256         ssize_t n;
257
258         *argp = NULL;
259
260         args = malloc(sizeof(*args));
261         if (args == NULL)
262                 return errno;
263         memset(args, 0, sizeof(*args));
264
265         pflags = &args->pta_pflags;
266
267         /* Create sucketpair for communication with the real file server */
268         if (socketpair(PF_LOCAL, SOCK_STREAM, 0, sv) == -1)
269                 return errno;
270
271         signal(SIGCHLD, childfail);
272
273         switch ((childpid = fork())) {
274         case 0:
275                 close(sv[1]);
276                 snprintf(comfd, sizeof(sv[0]), "%d", sv[0]);
277                 if (setenv("PUFFS_COMFD", comfd, 1) == -1)
278                         return errno;
279
280                 if (execvp(theargv[0], theargv) == -1)
281                         return errno;
282         case -1:
283                 return errno;
284         default:
285                 close(sv[0]);
286                 break;
287         }
288
289         /* read args */
290         if ((n = xread(sv[1], &len, sizeof(len))) != sizeof(len))
291                 err(1, "mp 1 %zd", n);
292         if (len > MAXPATHLEN)
293                 err(1, "mntpath > MAXPATHLEN");
294         if ((size_t)xread(sv[1], args->pta_dir, len) != len)
295                 err(1, "mp 2");
296         if (xread(sv[1], &len, sizeof(len)) != sizeof(len))
297                 err(1, "fn 1");
298         if (len > MAXPATHLEN)
299                 err(1, "devpath > MAXPATHLEN");
300         if ((size_t)xread(sv[1], args->pta_dev, len) != len)
301                 err(1, "fn 2");
302         if (xread(sv[1], &mntflags, sizeof(mntflags)) != sizeof(mntflags))
303                 err(1, "mntflags");
304         if (xread(sv[1], &args->pta_pargslen, sizeof(args->pta_pargslen))
305             != sizeof(args->pta_pargslen))
306                 err(1, "puffstest_args len");
307         args->pta_pargs = malloc(args->pta_pargslen);
308         if (args->pta_pargs == NULL)
309                 err(1, "malloc");
310         if (xread(sv[1], args->pta_pargs, args->pta_pargslen)
311             != (ssize_t)args->pta_pargslen)
312                 err(1, "puffstest_args");
313         if (xread(sv[1], pflags, sizeof(*pflags)) != sizeof(*pflags))
314                 err(1, "pflags");
315
316         args->pta_childpid = childpid;
317         args->pta_servfd = sv[1];
318         strlcpy(args->pta_dev, image, sizeof(args->pta_dev));
319
320         *argp = theargs = args;
321
322         return 0;
323 }
324
325 int
326 puffs_fstest_newfs(const atf_tc_t *tc, void **argp,
327         const char *image, off_t size, void *fspriv)
328 {
329         char dtfs_path[MAXPATHLEN];
330         char *dtfsargv[6];
331         char **theargv;
332
333         /* build dtfs exec path from atf test dir */
334         sprintf(dtfs_path, "%s/../puffs/h_dtfs/h_dtfs",
335             atf_tc_get_config_var(tc, "srcdir"));
336
337         if (fspriv) {
338                 theargv = fspriv;
339                 theargv[0] = dtfs_path;
340         } else {
341                 dtfsargv[0] = dtfs_path;
342                 dtfsargv[1] = __UNCONST("-i");
343                 dtfsargv[2] = __UNCONST("-s");
344                 dtfsargv[3] = __UNCONST("dtfs");
345                 dtfsargv[4] = __UNCONST("fictional");
346                 dtfsargv[5] = NULL;
347
348                 theargv = dtfsargv;
349         }
350
351         return donewfs(tc, argp, image, size, fspriv, theargv);
352 }
353
354 int
355 p2k_ffs_fstest_newfs(const atf_tc_t *tc, void **argp,
356         const char *image, off_t size, void *fspriv)
357 {
358         char *rumpffs_argv[5];
359         int rv;
360
361         rump_init();
362         if ((rv = ffs_fstest_newfs(tc, argp, image, size, fspriv)) != 0)
363                 return rv;
364         if (mkdir("p2kffsfake", 0777) == -1 && errno != EEXIST)
365                 return errno;
366
367         setenv("P2K_NODETACH", "1", 1);
368         rumpffs_argv[0] = __UNCONST("rump_ffs");
369         rumpffs_argv[1] = __UNCONST(image);
370         rumpffs_argv[2] = __UNCONST("p2kffsfake"); /* NOTUSED */
371         rumpffs_argv[3] = NULL;
372
373         if ((rv = donewfs(tc, argp, image, size, fspriv, rumpffs_argv)) != 0)
374                 ffs_fstest_delfs(tc, argp);
375         return rv;
376 }
377
378 int
379 puffs_fstest_mount(const atf_tc_t *tc, void *arg, const char *path, int flags)
380 {
381         struct puffstestargs *pargs = arg;
382         int fd;
383
384         rump_init();
385         fd = rump_sys_open("/dev/puffs", O_RDWR);
386         if (fd == -1)
387                 return fd;
388
389         if (rump_sys_mkdir(path, 0777) == -1)
390                 return -1;
391
392         if (rump_sys_mount(MOUNT_PUFFS, path, flags,
393             pargs->pta_pargs, pargs->pta_pargslen) == -1) {
394                 /* apply "to kill a child" to avoid atf hang (kludge) */
395                 kill(pargs->pta_childpid, SIGKILL);
396                 return -1;
397         }
398
399         pargs->pta_rumpfd = fd;
400         rumpshovels(pargs);
401
402         return 0;
403 }
404 __strong_alias(p2k_ffs_fstest_mount,puffs_fstest_mount);
405
406 int
407 puffs_fstest_delfs(const atf_tc_t *tc, void *arg)
408 {
409
410         /* useless ... */
411         return 0;
412 }
413
414 int
415 p2k_ffs_fstest_delfs(const atf_tc_t *tc, void *arg)
416 {
417
418         return ffs_fstest_delfs(tc, arg);
419 }
420
421 int
422 puffs_fstest_unmount(const atf_tc_t *tc, const char *path, int flags)
423 {
424         struct puffstestargs *pargs = theargs;
425         int status;
426         int rv;
427
428         /* ok, child might exit here */
429         signal(SIGCHLD, SIG_IGN);
430
431         rv = rump_sys_unmount(path, flags);
432         if (rv) 
433                 return rv;
434
435         if ((rv = rump_sys_rmdir(path)) != 0)
436                 return rv;
437
438         if (waitpid(pargs->pta_childpid, &status, WNOHANG) > 0)
439                 return 0;
440         kill(pargs->pta_childpid, SIGTERM);
441         usleep(10);
442         if (waitpid(pargs->pta_childpid, &status, WNOHANG) > 0)
443                 return 0;
444         kill(pargs->pta_childpid, SIGKILL);
445         usleep(500);
446         wait(&status);
447
448         rmdir("p2kffsfake");
449
450         return 0;
451 }
452 __strong_alias(p2k_ffs_fstest_unmount,puffs_fstest_unmount);