t_basic.c revision 314817
1/*	$NetBSD: t_basic.c,v 1.14 2017/01/13 21:30:40 christos Exp $	*/
2
3#include <sys/types.h>
4#include <sys/mount.h>
5#include <sys/socket.h>
6
7#include <assert.h>
8#include <atf-c.h>
9#include <err.h>
10#include <errno.h>
11#include <fcntl.h>
12#include <pthread.h>
13#include <puffs.h>
14#include <puffsdump.h>
15#include <stdio.h>
16#include <unistd.h>
17#include <string.h>
18#include <stdlib.h>
19
20#include <rump/rump.h>
21#include <rump/rump_syscalls.h>
22
23#include "h_macros.h"
24#include "../common/h_fsmacros.h"
25
26/*
27 * Do a synchronous operation.  When this returns, all FAF operations
28 * have at least been delivered to the file system.
29 *
30 * XXX: is this really good enough considering puffs(9)-issued
31 * callback operations?
32 */
33static void
34syncbar(const char *fs)
35{
36	struct statvfs svb;
37
38	if (rump_sys_statvfs1(fs, &svb, ST_WAIT) == -1)
39		atf_tc_fail_errno("statvfs");
40}
41
42#ifdef PUFFSDUMP
43static void __unused
44dumpopcount(struct puffstestargs *args)
45{
46	size_t i;
47
48	printf("VFS OPS:\n");
49	for (i = 0; i < MIN(puffsdump_vfsop_count, PUFFS_VFS_MAX); i++) {
50		printf("\t%s: %d\n",
51		    puffsdump_vfsop_revmap[i], args->pta_vfs_toserv_ops[i]);
52	}
53
54	printf("VN OPS:\n");
55	for (i = 0; i < MIN(puffsdump_vnop_count, PUFFS_VN_MAX); i++) {
56		printf("\t%s: %d\n",
57		    puffsdump_vnop_revmap[i], args->pta_vn_toserv_ops[i]);
58	}
59}
60#endif
61
62ATF_TC(mount);
63ATF_TC_HEAD(mount, tc)
64{
65
66	atf_tc_set_md_var(tc, "descr", "puffs+dtfs un/mount test");
67}
68
69ATF_TC_BODY(mount, tc)
70{
71	void *args;
72
73	FSTEST_CONSTRUCTOR(tc, puffs, args);
74	FSTEST_DESTRUCTOR(tc, puffs, args);
75}
76
77ATF_TC(root_reg);
78ATF_TC_HEAD(root_reg, tc)
79{
80	atf_tc_set_md_var(tc, "descr", "root is a regular file");
81}
82
83#define MAKEOPTS(...) \
84    char *theopts[] = {NULL, "-s", __VA_ARGS__, "dtfs", "n/a", NULL}
85
86ATF_TC_BODY(root_reg, tc)
87{
88	MAKEOPTS("-r", "reg");
89	void *args;
90	int fd, rv;
91
92	FSTEST_CONSTRUCTOR_FSPRIV(tc, puffs, args, theopts);
93
94	fd = rump_sys_open(FSTEST_MNTNAME, O_RDWR);
95	if (fd == -1)
96		atf_tc_fail_errno("open root");
97	if (rump_sys_write(fd, &fd, sizeof(fd)) != sizeof(fd))
98		atf_tc_fail_errno("write to root");
99	rv = rump_sys_mkdir(FSTEST_MNTNAME "/test", 0777);
100	ATF_REQUIRE(errno == ENOTDIR);
101	ATF_REQUIRE(rv == -1);
102	rump_sys_close(fd);
103
104	FSTEST_DESTRUCTOR(tc, puffs, args);
105}
106
107ATF_TC(root_lnk);
108ATF_TC_HEAD(root_lnk, tc)
109{
110
111	atf_tc_set_md_var(tc, "descr", "root is a symbolic link");
112}
113
114#define LINKSTR "/path/to/nowhere"
115ATF_TC_BODY(root_lnk, tc)
116{
117	MAKEOPTS("-r", "lnk " LINKSTR);
118	void *args;
119	char buf[PATH_MAX];
120	ssize_t len;
121
122	FSTEST_CONSTRUCTOR_FSPRIV(tc, puffs, args, theopts);
123
124	if ((len = rump_sys_readlink(FSTEST_MNTNAME, buf, sizeof(buf)-1)) == -1)
125		atf_tc_fail_errno("readlink");
126	buf[len] = '\0';
127
128	ATF_REQUIRE_STREQ(buf, LINKSTR);
129
130#if 0 /* XXX: unmount uses FOLLOW */
131	if (rump_sys_unmount("/mp", 0) == -1)
132		atf_tc_fail_errno("unmount");
133#endif
134}
135
136ATF_TC(root_fifo);
137ATF_TC_HEAD(root_fifo, tc)
138{
139
140	atf_tc_set_md_var(tc, "descr", "root is a symbolic link");
141}
142
143#define MAGICSTR "nakit ja muusiperunat maustevoilla"
144static void *
145dofifow(void *arg)
146{
147	int fd = (int)(uintptr_t)arg;
148	char buf[512];
149
150	printf("writing\n");
151	strcpy(buf, MAGICSTR);
152	if (rump_sys_write(fd, buf, strlen(buf)+1) != strlen(buf)+1)
153		atf_tc_fail_errno("write to fifo");
154
155	return NULL;
156}
157
158ATF_TC_BODY(root_fifo, tc)
159{
160	MAKEOPTS("-r", "fifo");
161	void *args;
162	pthread_t pt;
163	char buf[512];
164	int fd;
165
166	FSTEST_CONSTRUCTOR_FSPRIV(tc, puffs, args, theopts);
167
168	fd = rump_sys_open(FSTEST_MNTNAME, O_RDWR);
169	if (fd == -1)
170		atf_tc_fail_errno("open fifo");
171
172	pthread_create(&pt, NULL, dofifow, (void *)(uintptr_t)fd);
173
174	memset(buf, 0, sizeof(buf));
175	if (rump_sys_read(fd, buf, sizeof(buf)) == -1)
176		atf_tc_fail_errno("read fifo");
177
178	ATF_REQUIRE_STREQ(buf, MAGICSTR);
179	rump_sys_close(fd);
180
181	FSTEST_DESTRUCTOR(tc, puffs, args);
182}
183
184ATF_TC(root_chrdev);
185ATF_TC_HEAD(root_chrdev, tc)
186{
187
188	atf_tc_set_md_var(tc, "descr", "root is /dev/null");
189}
190
191ATF_TC_BODY(root_chrdev, tc)
192{
193	MAKEOPTS("-r", "chr 2 2");
194	void *args;
195	ssize_t rv;
196	char buf[512];
197	int fd;
198
199	FSTEST_CONSTRUCTOR_FSPRIV(tc, puffs, args, theopts);
200
201	fd = rump_sys_open(FSTEST_MNTNAME, O_RDWR);
202	if (fd == -1)
203		atf_tc_fail_errno("open null");
204
205	rv = rump_sys_write(fd, buf, sizeof(buf));
206	ATF_REQUIRE(rv == sizeof(buf));
207
208	rv = rump_sys_read(fd, buf, sizeof(buf));
209	ATF_REQUIRE(rv == 0);
210
211	rump_sys_close(fd);
212
213	FSTEST_DESTRUCTOR(tc, puffs, args);
214}
215
216/*
217 * Inactive/reclaim tests
218 */
219
220ATF_TC(inactive_basic);
221ATF_TC_HEAD(inactive_basic, tc)
222{
223
224	atf_tc_set_md_var(tc, "descr", "inactive gets called");
225}
226
227ATF_TC_BODY(inactive_basic, tc)
228{
229	struct puffstestargs *pargs;
230	void *args;
231	int fd;
232
233	FSTEST_CONSTRUCTOR(tc, puffs, args);
234	FSTEST_ENTER();
235	pargs = args;
236
237	fd = rump_sys_open("file", O_CREAT | O_RDWR, 0777);
238	if (fd == -1)
239		atf_tc_fail_errno("create");
240
241	/* none yet */
242	ATF_REQUIRE_EQ(pargs->pta_vn_toserv_ops[PUFFS_VN_INACTIVE], 0);
243
244	rump_sys_close(fd);
245
246	/* one for file */
247	ATF_REQUIRE_EQ(pargs->pta_vn_toserv_ops[PUFFS_VN_INACTIVE], 1);
248
249	FSTEST_EXIT();
250
251	/* another for the mountpoint */
252	ATF_REQUIRE_EQ(pargs->pta_vn_toserv_ops[PUFFS_VN_INACTIVE], 2);
253
254	FSTEST_DESTRUCTOR(tc, puffs, args);
255}
256
257ATF_TC(inactive_reclaim);
258ATF_TC_HEAD(inactive_reclaim, tc)
259{
260
261	atf_tc_set_md_var(tc, "descr", "inactive/reclaim gets called");
262}
263
264ATF_TC_BODY(inactive_reclaim, tc)
265{
266	struct puffstestargs *pargs;
267	void *args;
268	int fd;
269
270	FSTEST_CONSTRUCTOR(tc, puffs, args);
271	FSTEST_ENTER();
272	pargs = args;
273
274	fd = rump_sys_open("file", O_CREAT | O_RDWR, 0777);
275	if (fd == -1)
276		atf_tc_fail_errno("create");
277
278	ATF_REQUIRE_EQ(pargs->pta_vn_toserv_ops[PUFFS_VN_INACTIVE], 0);
279
280	if (rump_sys_unlink("file") == -1)
281		atf_tc_fail_errno("remove");
282
283	ATF_REQUIRE_EQ(pargs->pta_vn_toserv_ops[PUFFS_VN_INACTIVE], 0);
284	ATF_REQUIRE_EQ(pargs->pta_vn_toserv_ops[PUFFS_VN_RECLAIM], 0);
285
286	rump_sys_close(fd);
287	syncbar(FSTEST_MNTNAME);
288
289	ATF_REQUIRE(pargs->pta_vn_toserv_ops[PUFFS_VN_INACTIVE] > 0);
290	ATF_REQUIRE_EQ(pargs->pta_vn_toserv_ops[PUFFS_VN_RECLAIM], 1);
291
292	FSTEST_EXIT();
293	FSTEST_DESTRUCTOR(tc, puffs, args);
294}
295
296ATF_TC(reclaim_hardlink);
297ATF_TC_HEAD(reclaim_hardlink, tc)
298{
299
300	atf_tc_set_md_var(tc, "descr", "reclaim gets called only after "
301	    "final link is gone");
302}
303
304ATF_TC_BODY(reclaim_hardlink, tc)
305{
306	struct puffstestargs *pargs;
307	void *args;
308	int fd;
309	int ianow;
310
311	FSTEST_CONSTRUCTOR(tc, puffs, args);
312	FSTEST_ENTER();
313	pargs = args;
314
315	fd = rump_sys_open("file", O_CREAT | O_RDWR, 0777);
316	if (fd == -1)
317		atf_tc_fail_errno("create");
318
319	if (rump_sys_link("file", "anotherfile") == -1)
320		atf_tc_fail_errno("create link");
321	rump_sys_close(fd);
322
323	ATF_REQUIRE_EQ(pargs->pta_vn_toserv_ops[PUFFS_VN_RECLAIM], 0);
324
325	/* unlink first hardlink */
326	if (rump_sys_unlink("file") == -1)
327		atf_tc_fail_errno("unlink 1");
328
329	ATF_REQUIRE_EQ(pargs->pta_vn_toserv_ops[PUFFS_VN_RECLAIM], 0);
330	ianow = pargs->pta_vn_toserv_ops[PUFFS_VN_INACTIVE];
331
332	/* unlink second hardlink */
333	if (rump_sys_unlink("anotherfile") == -1)
334		atf_tc_fail_errno("unlink 2");
335
336	syncbar(FSTEST_MNTNAME);
337
338	ATF_REQUIRE(ianow < pargs->pta_vn_toserv_ops[PUFFS_VN_INACTIVE]);
339	ATF_REQUIRE_EQ(pargs->pta_vn_toserv_ops[PUFFS_VN_RECLAIM], 1);
340
341	FSTEST_EXIT();
342	FSTEST_DESTRUCTOR(tc, puffs, args);
343}
344
345ATF_TC(unlink_accessible);
346ATF_TC_HEAD(unlink_accessible, tc)
347{
348
349	atf_tc_set_md_var(tc, "descr", "open file is accessible after "
350	    "having been unlinked");
351}
352
353ATF_TC_BODY(unlink_accessible, tc)
354{
355	MAKEOPTS("-i", "-o", "nopagecache");
356	struct puffstestargs *pargs;
357	void *args;
358	char buf[512];
359	int fd, ianow;
360
361	assert(sizeof(buf) > sizeof(MAGICSTR));
362
363	FSTEST_CONSTRUCTOR_FSPRIV(tc, puffs, args, theopts);
364	FSTEST_ENTER();
365	pargs = args;
366
367	fd = rump_sys_open("file", O_CREAT | O_RDWR, 0777);
368	if (fd == -1)
369		atf_tc_fail_errno("create");
370
371	if (rump_sys_write(fd, MAGICSTR, sizeof(MAGICSTR)) != sizeof(MAGICSTR))
372		atf_tc_fail_errno("write");
373	if (rump_sys_unlink("file") == -1)
374		atf_tc_fail_errno("unlink");
375
376	ATF_REQUIRE_EQ(pargs->pta_vn_toserv_ops[PUFFS_VN_RECLAIM], 0);
377	ianow = pargs->pta_vn_toserv_ops[PUFFS_VN_INACTIVE];
378
379	if (rump_sys_pread(fd, buf, sizeof(buf), 0) == -1)
380		atf_tc_fail_errno("read");
381	rump_sys_close(fd);
382
383	syncbar(FSTEST_MNTNAME);
384
385	ATF_REQUIRE_EQ(pargs->pta_vn_toserv_ops[PUFFS_VN_RECLAIM], 1);
386	ATF_REQUIRE(pargs->pta_vn_toserv_ops[PUFFS_VN_INACTIVE] > ianow);
387
388	ATF_REQUIRE_STREQ(buf, MAGICSTR);
389
390	FSTEST_EXIT();
391	FSTEST_DESTRUCTOR(tc, puffs, args);
392}
393
394ATF_TC(signals);
395ATF_TC_HEAD(signals, tc)
396{
397
398	atf_tc_set_md_var(tc, "descr", "Checks that sending a signal can "
399	    "cause an interrupt to puffs wait");
400}
401
402extern struct proc *rumpns_initproc;
403extern void rumpns_psignal(struct proc *, int);
404extern void rumpns_sigclearall(struct proc *, void *, void *);
405ATF_TC_BODY(signals, tc)
406{
407	struct stat sb;
408	void *args;
409
410	rump_boot_setsigmodel(RUMP_SIGMODEL_RECORD);
411
412	FSTEST_CONSTRUCTOR(tc, puffs, args);
413	FSTEST_ENTER();
414	RL(rump_sys_stat(".", &sb));
415
416	/* send SIGUSR1, should not affect puffs ops */
417	rump_schedule();
418	rumpns_psignal(rumpns_initproc, SIGUSR1);
419	rump_unschedule();
420	RL(rump_sys_stat(".", &sb));
421
422	/* send SIGTERM, should get EINTR */
423	rump_schedule();
424	rumpns_psignal(rumpns_initproc, SIGTERM);
425	rump_unschedule();
426	ATF_REQUIRE_ERRNO(EINTR, rump_sys_stat(".", &sb) == -1);
427
428	/* clear sigmask so that we can unmount */
429	rump_schedule();
430	rumpns_sigclearall(rumpns_initproc, NULL, NULL);
431	rump_unschedule();
432
433	FSTEST_EXIT();
434	FSTEST_DESTRUCTOR(tc, puffs, args);
435}
436
437ATF_TP_ADD_TCS(tp)
438{
439
440	ATF_TP_ADD_TC(tp, mount);
441
442	ATF_TP_ADD_TC(tp, root_fifo);
443	ATF_TP_ADD_TC(tp, root_lnk);
444	ATF_TP_ADD_TC(tp, root_reg);
445	ATF_TP_ADD_TC(tp, root_chrdev);
446
447	ATF_TP_ADD_TC(tp, inactive_basic);
448	ATF_TP_ADD_TC(tp, inactive_reclaim);
449	ATF_TP_ADD_TC(tp, reclaim_hardlink);
450	ATF_TP_ADD_TC(tp, unlink_accessible);
451
452	ATF_TP_ADD_TC(tp, signals);
453
454	return atf_no_error();
455}
456