1272343Sngie/*	$NetBSD: fstest_nfs.c,v 1.9 2011/02/28 21:08:46 pooka Exp $	*/
2272343Sngie
3272343Sngie/*
4272343Sngie * Copyright (c) 2010 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 <libgen.h>
40272343Sngie#include <pthread.h>
41272343Sngie#include <puffs.h>
42272343Sngie#include <puffsdump.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#include "mount_nfs.h"
53272343Sngie#include "../../net/config/netconfig.c"
54272343Sngie
55272343Sngie#define SERVERADDR "10.3.2.1"
56272343Sngie#define SERVERROADDR "10.4.2.1"
57272343Sngie#define CLIENTADDR "10.3.2.2"
58272343Sngie#define CLIENTROADDR "10.4.2.2"
59272343Sngie#define NETNETMASK "255.255.255.0"
60272343Sngie#define EXPORTPATH "/myexport"
61272343Sngie
62272343Sngiestatic void
63272343Sngiechildfail(int status)
64272343Sngie{
65272343Sngie
66272343Sngie	atf_tc_fail("child died");
67272343Sngie}
68272343Sngie
69272343Sngie/* fork rump nfsd, configure interface */
70272343Sngiestatic int
71272343Sngiedonewfs(const atf_tc_t *tc, void **argp,
72272343Sngie	const char *image, off_t size, void *fspriv)
73272343Sngie{
74272343Sngie	const char *srcdir;
75272343Sngie	char *nfsdargv[16];
76272343Sngie	char nfsdpath[MAXPATHLEN];
77272343Sngie	char imagepath[MAXPATHLEN];
78272343Sngie	char ethername[MAXPATHLEN], ethername_ro[MAXPATHLEN];
79272343Sngie	char ifname[IFNAMSIZ], ifname_ro[IFNAMSIZ];
80272343Sngie	char cwd[MAXPATHLEN];
81272343Sngie	struct nfstestargs *args;
82272343Sngie	pid_t childpid;
83272343Sngie	int pipes[2];
84272343Sngie	int devnull;
85272343Sngie
86272343Sngie	/*
87272343Sngie	 * First, we start the nfs service.
88272343Sngie	 */
89272343Sngie	srcdir = atf_tc_get_config_var(tc, "srcdir");
90272343Sngie	sprintf(nfsdpath, "%s/../nfs/nfsservice/rumpnfsd", srcdir);
91272343Sngie	sprintf(ethername, "/%s/%s.etherbus", getcwd(cwd, sizeof(cwd)), image);
92272343Sngie	sprintf(ethername_ro, "%s_ro", ethername);
93272343Sngie	sprintf(imagepath, "/%s/%s", cwd, image);
94272343Sngie
95272343Sngie	nfsdargv[0] = nfsdpath;
96272343Sngie	nfsdargv[1] = ethername;
97272343Sngie	nfsdargv[2] = ethername_ro;
98272343Sngie	nfsdargv[3] = __UNCONST(SERVERADDR);
99272343Sngie	nfsdargv[4] = __UNCONST(SERVERROADDR);
100272343Sngie	nfsdargv[5] = __UNCONST(NETNETMASK);
101272343Sngie	nfsdargv[6] = __UNCONST(EXPORTPATH);
102272343Sngie	nfsdargv[7] = imagepath;
103272343Sngie	nfsdargv[8] = NULL;
104272343Sngie
105272343Sngie	signal(SIGCHLD, childfail);
106272343Sngie	if (pipe(pipes) == -1)
107272343Sngie		return errno;
108272343Sngie
109272343Sngie	switch ((childpid = fork())) {
110272343Sngie	case 0:
111272343Sngie		if (chdir(dirname(nfsdpath)) == -1)
112272343Sngie			err(1, "chdir");
113272343Sngie		close(pipes[0]);
114272343Sngie		if (dup2(pipes[1], 3) == -1)
115272343Sngie			err(1, "dup2");
116272343Sngie		if (execvp(nfsdargv[0], nfsdargv) == -1)
117272343Sngie			err(1, "execvp");
118272343Sngie	case -1:
119272343Sngie		return errno;
120272343Sngie	default:
121272343Sngie		close(pipes[1]);
122272343Sngie		break;
123272343Sngie	}
124272343Sngie
125272343Sngie	/*
126272343Sngie	 * Ok, nfsd has been run.  The following sleep helps with the
127272343Sngie	 * theoretical problem that nfsd can't start fast enough to
128272343Sngie	 * process our mount request and we end up doing a timeout
129272343Sngie	 * before the mount.  This would take several seconds.  So
130272343Sngie	 * try to make sure nfsd is up&running already at this stage.
131272343Sngie	 */
132272343Sngie	if (read(pipes[0], &devnull, 4) == -1)
133272343Sngie		return errno;
134272343Sngie
135272343Sngie	/*
136272343Sngie	 * Configure our networking interface.
137272343Sngie	 */
138272343Sngie	rump_init();
139272343Sngie	netcfg_rump_makeshmif(ethername, ifname);
140272343Sngie	netcfg_rump_if(ifname, CLIENTADDR, NETNETMASK);
141272343Sngie	netcfg_rump_makeshmif(ethername_ro, ifname_ro);
142272343Sngie	netcfg_rump_if(ifname_ro, CLIENTROADDR, NETNETMASK);
143272343Sngie
144272343Sngie	/*
145272343Sngie	 * That's it.  The rest is done in mount, since we don't have
146272343Sngie	 * the mountpath available here.
147272343Sngie	 */
148272343Sngie	args = malloc(sizeof(*args));
149272343Sngie	if (args == NULL)
150272343Sngie		return errno;
151272343Sngie	memset(args, 0, sizeof(*args));
152272343Sngie	args->ta_childpid = childpid;
153272343Sngie	strcpy(args->ta_ethername, ethername);
154272343Sngie
155272343Sngie	*argp = args;
156272343Sngie
157272343Sngie	return 0;
158272343Sngie}
159272343Sngie
160272343Sngieint
161272343Sngienfs_fstest_newfs(const atf_tc_t *tc, void **argp,
162272343Sngie	const char *image, off_t size, void *fspriv)
163272343Sngie{
164272343Sngie
165272343Sngie	return donewfs(tc, argp, image, size, fspriv);
166272343Sngie}
167272343Sngie
168272343Sngieint
169272343Sngienfsro_fstest_newfs(const atf_tc_t *tc, void **argp,
170272343Sngie	const char *image, off_t size, void *fspriv)
171272343Sngie{
172272343Sngie
173272343Sngie	return donewfs(tc, argp, image, size, fspriv);
174272343Sngie}
175272343Sngie
176272343Sngie/* mount the file system */
177272343Sngiestatic int
178272343Sngiedomount(const atf_tc_t *tc, void *arg, const char *serverpath,
179272343Sngie	const char *path, int flags)
180272343Sngie{
181272343Sngie	char canon_dev[MAXPATHLEN], canon_dir[MAXPATHLEN];
182272343Sngie	const char *nfscliargs[] = {
183272343Sngie		"nfsclient",
184272343Sngie		serverpath,
185272343Sngie		path,
186272343Sngie		NULL,
187272343Sngie	};
188272343Sngie	struct nfs_args args;
189272343Sngie	int mntflags;
190272343Sngie
191272343Sngie	if (rump_sys_mkdir(path, 0777) == -1)
192272343Sngie		return errno;
193272343Sngie
194272343Sngie	/* XXX: atf does not reset values */
195272343Sngie	optind = 1;
196272343Sngie	opterr = 1;
197272343Sngie
198272343Sngie	/*
199272343Sngie	 * We use nfs parseargs here, since as a side effect it
200272343Sngie	 * takes care of the RPC hulabaloo.
201272343Sngie	 */
202272343Sngie	mount_nfs_parseargs(__arraycount(nfscliargs)-1, __UNCONST(nfscliargs),
203272343Sngie	    &args, &mntflags, canon_dev, canon_dir);
204272343Sngie
205272343Sngie	if (rump_sys_mount(MOUNT_NFS, path, flags, &args, sizeof(args)) == -1) {
206272343Sngie		return errno;
207272343Sngie	}
208272343Sngie
209272343Sngie	return 0;
210272343Sngie}
211272343Sngie
212272343Sngieint
213272343Sngienfs_fstest_mount(const atf_tc_t *tc, void *arg, const char *path, int flags)
214272343Sngie{
215272343Sngie
216272343Sngie	return domount(tc, arg, SERVERADDR ":" EXPORTPATH, path, flags);
217272343Sngie}
218272343Sngie
219272343Sngie/*
220272343Sngie * This is where the magic happens!
221272343Sngie *
222272343Sngie * If we are mounting r/w, do the normal thing.  However, if we are
223272343Sngie * doing a r/o mount, switch use the r/o server export address
224272343Sngie * and do a r/w mount.  This way we end up testing the r/o export policy
225272343Sngie * of the server! (yes, slightly questionable semantics, but at least
226272343Sngie * we notice very quickly if our assumption is broken in the future ;)
227272343Sngie */
228272343Sngieint
229272343Sngienfsro_fstest_mount(const atf_tc_t *tc, void *arg, const char *path, int flags)
230272343Sngie{
231272343Sngie
232272343Sngie	if (flags & MNT_RDONLY) {
233272343Sngie		flags &= ~MNT_RDONLY;
234272343Sngie		return domount(tc, arg, SERVERROADDR":"EXPORTPATH, path, flags);
235272343Sngie	} else {
236272343Sngie		return domount(tc, arg, SERVERADDR":"EXPORTPATH, path, flags);
237272343Sngie	}
238272343Sngie}
239272343Sngie
240272343Sngiestatic int
241272343Sngiedodelfs(const atf_tc_t *tc, void *arg)
242272343Sngie{
243272343Sngie
244272343Sngie	/*
245272343Sngie	 * XXX: no access to "args" since we're called from "cleanup".
246272343Sngie	 * Trust atf to kill nfsd process and remove etherfile.
247272343Sngie	 */
248272343Sngie#if 0
249272343Sngie	/*
250272343Sngie	 * It's highly expected that the child will die next, so we
251272343Sngie	 * don't need that information anymore thank you very many.
252272343Sngie	 */
253272343Sngie	signal(SIGCHLD, SIG_IGN);
254272343Sngie
255272343Sngie	/*
256272343Sngie	 * Just KILL it.  Sending it SIGTERM first causes it to try
257272343Sngie	 * to send some unmount RPCs, leading to sticky situations.
258272343Sngie	 */
259272343Sngie	kill(args->ta_childpid, SIGKILL);
260272343Sngie	wait(&status);
261272343Sngie
262272343Sngie	/* remove ethernet bus */
263272343Sngie	if (unlink(args->ta_ethername) == -1)
264272343Sngie		atf_tc_fail_errno("unlink ethername");
265272343Sngie#endif
266272343Sngie
267272343Sngie	return 0;
268272343Sngie}
269272343Sngie
270272343Sngieint
271272343Sngienfs_fstest_delfs(const atf_tc_t *tc, void *arg)
272272343Sngie{
273272343Sngie
274272343Sngie	return dodelfs(tc, arg);
275272343Sngie}
276272343Sngie
277272343Sngieint
278272343Sngienfsro_fstest_delfs(const atf_tc_t *tc, void *arg)
279272343Sngie{
280272343Sngie
281272343Sngie	return dodelfs(tc, arg);
282272343Sngie}
283272343Sngie
284272343Sngiestatic int
285272343Sngiedounmount(const atf_tc_t *tc, const char *path, int flags)
286272343Sngie{
287272343Sngie	int status, i, sverrno;
288272343Sngie
289272343Sngie	/*
290272343Sngie	 * NFS handles sillyrenames in an workqueue.  Some of them might
291272343Sngie	 * be still in the queue even if all user activity has ceased.
292272343Sngie	 * We try to unmount for 2 seconds to give them a chance
293272343Sngie	 * to flush out.
294272343Sngie	 *
295272343Sngie	 * PR kern/43799
296272343Sngie	 */
297272343Sngie	for (i = 0; i < 20; i++) {
298272343Sngie		if ((status = rump_sys_unmount(path, flags)) == 0)
299272343Sngie			break;
300272343Sngie		sverrno = errno;
301272343Sngie		if (sverrno != EBUSY)
302272343Sngie			break;
303272343Sngie		usleep(100000);
304272343Sngie	}
305272343Sngie	if (status == -1)
306272343Sngie		return sverrno;
307272343Sngie
308272343Sngie	if (rump_sys_rmdir(path) == -1)
309272343Sngie		return errno;
310272343Sngie
311272343Sngie	return 0;
312272343Sngie}
313272343Sngie
314272343Sngieint
315272343Sngienfs_fstest_unmount(const atf_tc_t *tc, const char *path, int flags)
316272343Sngie{
317272343Sngie
318272343Sngie	return dounmount(tc, path, flags);
319272343Sngie}
320272343Sngie
321272343Sngieint
322272343Sngienfsro_fstest_unmount(const atf_tc_t *tc, const char *path, int flags)
323272343Sngie{
324272343Sngie
325272343Sngie	return dounmount(tc, path, flags);
326272343Sngie}
327