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
56static bool mayquit = false;
57
58static ssize_t
59xread(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
77static ssize_t
78xwrite(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 */
100static void *
101readshovel(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
154static void *
155writeshovel(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
217static void
218rumpshovels(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
235static void
236childfail(int sign)
237{
238
239	atf_tc_fail("child died"); /* almost signal-safe */
240}
241
242struct puffstestargs *theargs; /* XXX */
243
244/* XXX: we don't support size */
245static int
246donewfs(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
325int
326puffs_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
354int
355p2k_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
378int
379puffs_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
406int
407puffs_fstest_delfs(const atf_tc_t *tc, void *arg)
408{
409
410	/* useless ... */
411	return 0;
412}
413
414int
415p2k_ffs_fstest_delfs(const atf_tc_t *tc, void *arg)
416{
417
418	return ffs_fstest_delfs(tc, arg);
419}
420
421int
422puffs_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);
453