1/*	$NetBSD: fstest_nfs.c,v 1.9 2011/02/28 21:08:46 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 <libgen.h>
40#include <pthread.h>
41#include <puffs.h>
42#include <puffsdump.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#include "mount_nfs.h"
53#include "../../net/config/netconfig.c"
54
55#define SERVERADDR "10.3.2.1"
56#define SERVERROADDR "10.4.2.1"
57#define CLIENTADDR "10.3.2.2"
58#define CLIENTROADDR "10.4.2.2"
59#define NETNETMASK "255.255.255.0"
60#define EXPORTPATH "/myexport"
61
62static void
63childfail(int status)
64{
65
66	atf_tc_fail("child died");
67}
68
69/* fork rump nfsd, configure interface */
70static int
71donewfs(const atf_tc_t *tc, void **argp,
72	const char *image, off_t size, void *fspriv)
73{
74	const char *srcdir;
75	char *nfsdargv[16];
76	char nfsdpath[MAXPATHLEN];
77	char imagepath[MAXPATHLEN];
78	char ethername[MAXPATHLEN], ethername_ro[MAXPATHLEN];
79	char ifname[IFNAMSIZ], ifname_ro[IFNAMSIZ];
80	char cwd[MAXPATHLEN];
81	struct nfstestargs *args;
82	pid_t childpid;
83	int pipes[2];
84	int devnull;
85
86	/*
87	 * First, we start the nfs service.
88	 */
89	srcdir = atf_tc_get_config_var(tc, "srcdir");
90	sprintf(nfsdpath, "%s/../nfs/nfsservice/rumpnfsd", srcdir);
91	sprintf(ethername, "/%s/%s.etherbus", getcwd(cwd, sizeof(cwd)), image);
92	sprintf(ethername_ro, "%s_ro", ethername);
93	sprintf(imagepath, "/%s/%s", cwd, image);
94
95	nfsdargv[0] = nfsdpath;
96	nfsdargv[1] = ethername;
97	nfsdargv[2] = ethername_ro;
98	nfsdargv[3] = __UNCONST(SERVERADDR);
99	nfsdargv[4] = __UNCONST(SERVERROADDR);
100	nfsdargv[5] = __UNCONST(NETNETMASK);
101	nfsdargv[6] = __UNCONST(EXPORTPATH);
102	nfsdargv[7] = imagepath;
103	nfsdargv[8] = NULL;
104
105	signal(SIGCHLD, childfail);
106	if (pipe(pipes) == -1)
107		return errno;
108
109	switch ((childpid = fork())) {
110	case 0:
111		if (chdir(dirname(nfsdpath)) == -1)
112			err(1, "chdir");
113		close(pipes[0]);
114		if (dup2(pipes[1], 3) == -1)
115			err(1, "dup2");
116		if (execvp(nfsdargv[0], nfsdargv) == -1)
117			err(1, "execvp");
118	case -1:
119		return errno;
120	default:
121		close(pipes[1]);
122		break;
123	}
124
125	/*
126	 * Ok, nfsd has been run.  The following sleep helps with the
127	 * theoretical problem that nfsd can't start fast enough to
128	 * process our mount request and we end up doing a timeout
129	 * before the mount.  This would take several seconds.  So
130	 * try to make sure nfsd is up&running already at this stage.
131	 */
132	if (read(pipes[0], &devnull, 4) == -1)
133		return errno;
134
135	/*
136	 * Configure our networking interface.
137	 */
138	rump_init();
139	netcfg_rump_makeshmif(ethername, ifname);
140	netcfg_rump_if(ifname, CLIENTADDR, NETNETMASK);
141	netcfg_rump_makeshmif(ethername_ro, ifname_ro);
142	netcfg_rump_if(ifname_ro, CLIENTROADDR, NETNETMASK);
143
144	/*
145	 * That's it.  The rest is done in mount, since we don't have
146	 * the mountpath available here.
147	 */
148	args = malloc(sizeof(*args));
149	if (args == NULL)
150		return errno;
151	memset(args, 0, sizeof(*args));
152	args->ta_childpid = childpid;
153	strcpy(args->ta_ethername, ethername);
154
155	*argp = args;
156
157	return 0;
158}
159
160int
161nfs_fstest_newfs(const atf_tc_t *tc, void **argp,
162	const char *image, off_t size, void *fspriv)
163{
164
165	return donewfs(tc, argp, image, size, fspriv);
166}
167
168int
169nfsro_fstest_newfs(const atf_tc_t *tc, void **argp,
170	const char *image, off_t size, void *fspriv)
171{
172
173	return donewfs(tc, argp, image, size, fspriv);
174}
175
176/* mount the file system */
177static int
178domount(const atf_tc_t *tc, void *arg, const char *serverpath,
179	const char *path, int flags)
180{
181	char canon_dev[MAXPATHLEN], canon_dir[MAXPATHLEN];
182	const char *nfscliargs[] = {
183		"nfsclient",
184		serverpath,
185		path,
186		NULL,
187	};
188	struct nfs_args args;
189	int mntflags;
190
191	if (rump_sys_mkdir(path, 0777) == -1)
192		return errno;
193
194	/* XXX: atf does not reset values */
195	optind = 1;
196	opterr = 1;
197
198	/*
199	 * We use nfs parseargs here, since as a side effect it
200	 * takes care of the RPC hulabaloo.
201	 */
202	mount_nfs_parseargs(__arraycount(nfscliargs)-1, __UNCONST(nfscliargs),
203	    &args, &mntflags, canon_dev, canon_dir);
204
205	if (rump_sys_mount(MOUNT_NFS, path, flags, &args, sizeof(args)) == -1) {
206		return errno;
207	}
208
209	return 0;
210}
211
212int
213nfs_fstest_mount(const atf_tc_t *tc, void *arg, const char *path, int flags)
214{
215
216	return domount(tc, arg, SERVERADDR ":" EXPORTPATH, path, flags);
217}
218
219/*
220 * This is where the magic happens!
221 *
222 * If we are mounting r/w, do the normal thing.  However, if we are
223 * doing a r/o mount, switch use the r/o server export address
224 * and do a r/w mount.  This way we end up testing the r/o export policy
225 * of the server! (yes, slightly questionable semantics, but at least
226 * we notice very quickly if our assumption is broken in the future ;)
227 */
228int
229nfsro_fstest_mount(const atf_tc_t *tc, void *arg, const char *path, int flags)
230{
231
232	if (flags & MNT_RDONLY) {
233		flags &= ~MNT_RDONLY;
234		return domount(tc, arg, SERVERROADDR":"EXPORTPATH, path, flags);
235	} else {
236		return domount(tc, arg, SERVERADDR":"EXPORTPATH, path, flags);
237	}
238}
239
240static int
241dodelfs(const atf_tc_t *tc, void *arg)
242{
243
244	/*
245	 * XXX: no access to "args" since we're called from "cleanup".
246	 * Trust atf to kill nfsd process and remove etherfile.
247	 */
248#if 0
249	/*
250	 * It's highly expected that the child will die next, so we
251	 * don't need that information anymore thank you very many.
252	 */
253	signal(SIGCHLD, SIG_IGN);
254
255	/*
256	 * Just KILL it.  Sending it SIGTERM first causes it to try
257	 * to send some unmount RPCs, leading to sticky situations.
258	 */
259	kill(args->ta_childpid, SIGKILL);
260	wait(&status);
261
262	/* remove ethernet bus */
263	if (unlink(args->ta_ethername) == -1)
264		atf_tc_fail_errno("unlink ethername");
265#endif
266
267	return 0;
268}
269
270int
271nfs_fstest_delfs(const atf_tc_t *tc, void *arg)
272{
273
274	return dodelfs(tc, arg);
275}
276
277int
278nfsro_fstest_delfs(const atf_tc_t *tc, void *arg)
279{
280
281	return dodelfs(tc, arg);
282}
283
284static int
285dounmount(const atf_tc_t *tc, const char *path, int flags)
286{
287	int status, i, sverrno;
288
289	/*
290	 * NFS handles sillyrenames in an workqueue.  Some of them might
291	 * be still in the queue even if all user activity has ceased.
292	 * We try to unmount for 2 seconds to give them a chance
293	 * to flush out.
294	 *
295	 * PR kern/43799
296	 */
297	for (i = 0; i < 20; i++) {
298		if ((status = rump_sys_unmount(path, flags)) == 0)
299			break;
300		sverrno = errno;
301		if (sverrno != EBUSY)
302			break;
303		usleep(100000);
304	}
305	if (status == -1)
306		return sverrno;
307
308	if (rump_sys_rmdir(path) == -1)
309		return errno;
310
311	return 0;
312}
313
314int
315nfs_fstest_unmount(const atf_tc_t *tc, const char *path, int flags)
316{
317
318	return dounmount(tc, path, flags);
319}
320
321int
322nfsro_fstest_unmount(const atf_tc_t *tc, const char *path, int flags)
323{
324
325	return dounmount(tc, path, flags);
326}
327