xref: /netbsd-src/tests/fs/puffs/t_basic.c (revision c505c4429840c353a86d4eb53b5e2bfc0092264e)
1 /*	$NetBSD: t_basic.c,v 1.6 2010/07/12 13:09:19 pooka 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 
25 struct puffs_args {
26 	uint8_t			*us_pargs;
27 	size_t			us_pargslen;
28 
29 	int			us_pflags;
30 	int			us_servfd;
31 	pid_t			us_childpid;
32 };
33 
34 #define BUFSIZE (64*1024)
35 #define DTFS_DUMP "-o","dump"
36 
37 struct thefds {
38 	int rumpfd;
39 	int servfd;
40 };
41 
42 int vfs_toserv_ops[PUFFS_VFS_MAX];
43 int vn_toserv_ops[PUFFS_VN_MAX];
44 
45 /*
46  * Do a synchronous operation.  When this returns, all FAF operations
47  * have at least been delivered to the file system.
48  *
49  * XXX: is this really good enough considering puffs(9)-issued
50  * callback operations?
51  */
52 static void
53 syncbar(const char *fs)
54 {
55 	struct statvfs svb;
56 
57 	rump_sys_statvfs1(fs, &svb, ST_WAIT);
58 }
59 
60 #ifdef PUFFSDUMP
61 static void __unused
62 dumpopcount(void)
63 {
64 	size_t i;
65 
66 	printf("VFS OPS:\n");
67 	for (i = 0; i < MIN(puffsdump_vfsop_count, PUFFS_VFS_MAX); i++) {
68 		printf("\t%s: %d\n",
69 		    puffsdump_vfsop_revmap[i], vfs_toserv_ops[i]);
70 	}
71 
72 	printf("VN OPS:\n");
73 	for (i = 0; i < MIN(puffsdump_vnop_count, PUFFS_VN_MAX); i++) {
74 		printf("\t%s: %d\n",
75 		    puffsdump_vnop_revmap[i], vn_toserv_ops[i]);
76 	}
77 }
78 #endif
79 
80 /*
81  * Threads which shovel data between comfd and /dev/puffs.
82  * (cannot use polling since fd's are in different namespaces)
83  */
84 static void *
85 readshovel(void *arg)
86 {
87 	struct putter_hdr *phdr;
88 	struct puffs_req *preq;
89 	struct thefds *fds = arg;
90 	char buf[BUFSIZE];
91 	int comfd, puffsfd;
92 
93 	comfd = fds->servfd;
94 	puffsfd = fds->rumpfd;
95 
96 	phdr = (void *)buf;
97 	preq = (void *)buf;
98 
99 	/* use static thread id */
100 	rump_pub_lwp_alloc_and_switch(0, 10);
101 
102 	for (;;) {
103 		ssize_t n;
104 
105 		n = rump_sys_read(puffsfd, buf, sizeof(*phdr));
106 		if (n <= 0)
107 			break;
108 
109 		assert(phdr->pth_framelen < BUFSIZE);
110 		n = rump_sys_read(puffsfd, buf+sizeof(*phdr),
111 		    phdr->pth_framelen - sizeof(*phdr));
112 		if (n <= 0)
113 			break;
114 
115 		/* Analyze request */
116 		if (PUFFSOP_OPCLASS(preq->preq_opclass) == PUFFSOP_VFS) {
117 			assert(preq->preq_optype < PUFFS_VFS_MAX);
118 			vfs_toserv_ops[preq->preq_optype]++;
119 		} else if (PUFFSOP_OPCLASS(preq->preq_opclass) == PUFFSOP_VN) {
120 			assert(preq->preq_optype < PUFFS_VN_MAX);
121 			vn_toserv_ops[preq->preq_optype]++;
122 		}
123 
124 		n = phdr->pth_framelen;
125 		if (write(comfd, buf, n) != n)
126 			break;
127 	}
128 
129 	return NULL;
130 }
131 
132 static void *
133 writeshovel(void *arg)
134 {
135 	struct thefds *fds = arg;
136 	struct putter_hdr *phdr;
137 	char buf[BUFSIZE];
138 	size_t toread;
139 	int comfd, puffsfd;
140 
141 	/* use static thread id */
142 	rump_pub_lwp_alloc_and_switch(0, 11);
143 
144 	comfd = fds->servfd;
145 	puffsfd = fds->rumpfd;
146 
147 	phdr = (struct putter_hdr *)buf;
148 
149 	for (;;) {
150 		off_t off;
151 		ssize_t n;
152 
153 		/*
154 		 * Need to write everything to the "kernel" in one chunk,
155 		 * so make sure we have it here.
156 		 */
157 		off = 0;
158 		toread = sizeof(struct putter_hdr);
159 		assert(toread < BUFSIZE);
160 		do {
161 			n = read(comfd, buf+off, toread);
162 			if (n <= 0) {
163 				break;
164 			}
165 			off += n;
166 			if (off >= sizeof(struct putter_hdr))
167 				toread = phdr->pth_framelen - off;
168 			else
169 				toread = off - sizeof(struct putter_hdr);
170 		} while (toread);
171 
172 		n = rump_sys_write(puffsfd, buf, phdr->pth_framelen);
173 		if (n != phdr->pth_framelen)
174 			break;
175 	}
176 
177 	return NULL;
178 }
179 
180 static void
181 rumpshovels(int rumpfd, int servfd)
182 {
183 	struct thefds *fds;
184 	pthread_t pt;
185 	int rv;
186 
187 	if ((rv = rump_init()) == -1)
188 		err(1, "rump_init");
189 
190 	fds = malloc(sizeof(*fds));
191 	fds->rumpfd = rumpfd;
192 	fds->servfd = servfd;
193 	if (pthread_create(&pt, NULL, readshovel, fds) == -1)
194 		err(1, "read shovel");
195 	pthread_detach(pt);
196 	if (pthread_create(&pt, NULL, writeshovel, fds) == -1)
197 		err(1, "write shovel");
198 	pthread_detach(pt);
199 }
200 
201 static int
202 parseargs(int argc, char *argv[],
203 	struct puffs_args *args, int *mntflags,
204 	char *canon_dev, char *canon_dir)
205 {
206 	pid_t childpid;
207 	int *pflags = &args->us_pflags;
208 	char comfd[16];
209 	int sv[2];
210 	size_t len;
211 	ssize_t n;
212 
213 	/* Create sucketpair for communication with the real file server */
214 	if (socketpair(PF_LOCAL, SOCK_STREAM, 0, sv) == -1)
215 		err(1, "socketpair");
216 
217 	switch ((childpid = fork())) {
218 	case 0:
219 		close(sv[1]);
220 		snprintf(comfd, sizeof(sv[0]), "%d", sv[0]);
221 		if (setenv("PUFFS_COMFD", comfd, 1) == -1)
222 			atf_tc_fail_errno("setenv");
223 
224 		if (execvp(argv[0], argv) == -1)
225 			atf_tc_fail_errno("execvp");
226 		/*NOTREACHED*/
227 	case -1:
228 		atf_tc_fail_errno("fork");
229 		/*NOTREACHED*/
230 	default:
231 		close(sv[0]);
232 		break;
233 	}
234 
235 	/* read args */
236 	if ((n = read(sv[1], &len, sizeof(len))) != sizeof(len))
237 		err(1, "mp 1 %zd", n);
238 	if (len > MAXPATHLEN)
239 		err(1, "mntpath > MAXPATHLEN");
240 	if ((size_t)read(sv[1], canon_dir, len) != len)
241 		err(1, "mp 2");
242 	if (read(sv[1], &len, sizeof(len)) != sizeof(len))
243 		err(1, "fn 1");
244 	if (len > MAXPATHLEN)
245 		err(1, "devpath > MAXPATHLEN");
246 	if ((size_t)read(sv[1], canon_dev, len) != len)
247 		err(1, "fn 2");
248 	if (read(sv[1], mntflags, sizeof(*mntflags)) != sizeof(*mntflags))
249 		err(1, "mntflags");
250 	if (read(sv[1], &args->us_pargslen, sizeof(args->us_pargslen)) != sizeof(args->us_pargslen))
251 		err(1, "puffs_args len");
252 	args->us_pargs = malloc(args->us_pargslen);
253 	if (args->us_pargs == NULL)
254 		err(1, "malloc");
255 	if (read(sv[1], args->us_pargs, args->us_pargslen) != args->us_pargslen)
256 		err(1, "puffs_args");
257 	if (read(sv[1], pflags, sizeof(*pflags)) != sizeof(*pflags))
258 		err(1, "pflags");
259 
260 	args->us_childpid = childpid;
261 	args->us_servfd = sv[1];
262 
263 	return 0;
264 }
265 
266 #define dtfsmountv(a, b)						\
267     struct puffs_args pa;						\
268     dtfsmount1(a, __arraycount(b), b, atf_tc_get_config_var(tc, "srcdir"), &pa)
269 static void
270 dtfsmount1(const char *mp, int optcount, char *opts[], const char *srcdir,
271 	struct puffs_args *pa)
272 {
273 	char canon_dev[MAXPATHLEN], canon_dir[MAXPATHLEN];
274 	char dtfs_path[MAXPATHLEN], mountpath[MAXPATHLEN];
275 	char **dtfsargv;
276 	int mntflag;
277 	int rv, i;
278 	int fd;
279 
280 	dtfsargv = malloc(sizeof(char *) * (optcount+3));
281 
282 	/* build dtfs exec path for atf */
283 	sprintf(dtfs_path, "%s/h_dtfs/h_dtfs", srcdir);
284 	dtfsargv[0] = dtfs_path;
285 
286 	for (i = 0; i < optcount; i++) {
287 		dtfsargv[i+1] = opts[i];
288 	}
289 
290 	strlcpy(mountpath, mp, sizeof(mountpath));
291 	dtfsargv[optcount+1] = mountpath;
292 	dtfsargv[optcount+2] = NULL;
293 
294 	rv = parseargs(optcount+3, dtfsargv,
295 	    pa, &mntflag, canon_dev, canon_dir);
296 	if (rv)
297 		atf_tc_fail("comfd parseargs");
298 
299 	rump_init();
300 	fd = rump_sys_open("/dev/puffs", O_RDWR);
301 	if (fd == -1)
302 		atf_tc_fail_errno("open puffs fd");
303 #if 0
304 	pa->pa_fd = fd;
305 #else
306 	assert(fd == 0); /* XXX: FIXME */
307 #endif
308 
309 	if (rump_sys_mkdir(mp, 0777) == -1)
310 		atf_tc_fail_errno("mkdir mountpoint");
311 
312 	if (rump_sys_mount(MOUNT_PUFFS, mp, 0,
313 	    pa->us_pargs, pa->us_pargslen) == -1)
314 		atf_tc_fail_errno("mount");
315 
316 	rumpshovels(fd, pa->us_servfd);
317 }
318 
319 ATF_TC(mount);
320 ATF_TC_HEAD(mount, tc)
321 {
322 
323 	atf_tc_set_md_var(tc, "descr", "puffs+dtfs un/mount test");
324 }
325 
326 ATF_TC_BODY(mount, tc)
327 {
328 	char *myopts[] = {
329 		"dtfs",
330 	};
331 
332 	dtfsmountv("/mp", myopts);
333 	if (rump_sys_unmount("/mp", 0) == -1)
334 		atf_tc_fail_errno("unmount");
335 }
336 
337 ATF_TC(root_reg);
338 ATF_TC_HEAD(root_reg, tc)
339 {
340 	atf_tc_set_md_var(tc, "descr", "root is a regular file");
341 }
342 
343 ATF_TC_BODY(root_reg, tc)
344 {
345 	char *myopts[] = {
346 		"-r","reg",
347 		"dtfs",
348 	};
349 	int fd, rv;
350 
351 	dtfsmountv("/mp", myopts);
352 
353 	fd = rump_sys_open("/mp", O_RDWR);
354 	if (fd == -1)
355 		atf_tc_fail_errno("open root");
356 	if (rump_sys_write(fd, &fd, sizeof(fd)) != sizeof(fd))
357 		atf_tc_fail_errno("write to root");
358 	rv = rump_sys_mkdir("/mp/test", 0777);
359 	ATF_REQUIRE(errno == ENOTDIR);
360 	ATF_REQUIRE(rv == -1);
361 	rump_sys_close(fd);
362 
363 	if (rump_sys_unmount("/mp", 0) == -1)
364 		atf_tc_fail_errno("unmount");
365 }
366 
367 ATF_TC(root_lnk);
368 ATF_TC_HEAD(root_lnk, tc)
369 {
370 
371 	atf_tc_set_md_var(tc, "descr", "root is a symbolic link");
372 }
373 
374 #define LINKSTR "/path/to/nowhere"
375 ATF_TC_BODY(root_lnk, tc)
376 {
377 	char *myopts[] = {
378 		"-r", "lnk " LINKSTR,
379 		"-s",
380 		"dtfs",
381 	};
382 	char buf[PATH_MAX];
383 	ssize_t len;
384 
385 	dtfsmountv("/mp", myopts);
386 
387 	if ((len = rump_sys_readlink("/mp", buf, sizeof(buf)-1)) == -1)
388 		atf_tc_fail_errno("readlink");
389 	buf[len] = '\0';
390 
391 	ATF_REQUIRE_STREQ(buf, LINKSTR);
392 
393 #if 0 /* XXX: unmount uses FOLLOW */
394 	if (rump_sys_unmount("/mp", 0) == -1)
395 		atf_tc_fail_errno("unmount");
396 #endif
397 
398 	/*
399 	 * XXX2: due to atf issue #53, we must make sure the child dies
400 	 * before we exit.
401 	 */
402 	if (kill(pa.us_childpid, SIGTERM) == -1)
403 		err(1, "kill");
404 }
405 
406 ATF_TC(root_fifo);
407 ATF_TC_HEAD(root_fifo, tc)
408 {
409 
410 	atf_tc_set_md_var(tc, "descr", "root is a symbolic link");
411 }
412 
413 #define MAGICSTR "nakit ja muusiperunat maustevoilla"
414 static void *
415 dofifow(void *arg)
416 {
417 	int fd = (int)(uintptr_t)arg;
418 	char buf[512];
419 
420 	printf("writing\n");
421 	strcpy(buf, MAGICSTR);
422 	if (rump_sys_write(fd, buf, strlen(buf)+1) != strlen(buf)+1)
423 		atf_tc_fail_errno("write to fifo");
424 
425 	return NULL;
426 }
427 
428 ATF_TC_BODY(root_fifo, tc)
429 {
430 	char *myopts[] = {
431 		"-r", "fifo",
432 		"dtfs",
433 	};
434 	pthread_t pt;
435 	char buf[512];
436 	int fd;
437 
438 	dtfsmountv("/mp", myopts);
439 	fd = rump_sys_open("/mp", O_RDWR);
440 	if (fd == -1)
441 		atf_tc_fail_errno("open fifo");
442 
443 	pthread_create(&pt, NULL, dofifow, (void *)(uintptr_t)fd);
444 
445 	printf("reading\n");
446 	memset(buf, 0, sizeof(buf));
447 	if (rump_sys_read(fd, buf, sizeof(buf)) == -1)
448 		atf_tc_fail_errno("read fifo");
449 
450 	ATF_REQUIRE_STREQ(buf, MAGICSTR);
451 
452 	rump_sys_close(fd);
453 	if (rump_sys_unmount("/mp", 0) == -1)
454 		atf_tc_fail_errno("unmount");
455 }
456 
457 ATF_TC(root_chrdev);
458 ATF_TC_HEAD(root_chrdev, tc)
459 {
460 
461 	atf_tc_set_md_var(tc, "descr", "root is /dev/null");
462 }
463 
464 ATF_TC_BODY(root_chrdev, tc)
465 {
466 	char *myopts[] = {
467 		"-r", "chr 2 0",
468 		"dtfs",
469 	};
470 	ssize_t rv;
471 	char buf[512];
472 	int fd;
473 
474 	dtfsmountv("/mp", myopts);
475 	fd = rump_sys_open("/mp", O_RDWR);
476 	if (fd == -1)
477 		atf_tc_fail_errno("open null");
478 
479 	rv = rump_sys_write(fd, buf, sizeof(buf));
480 	ATF_REQUIRE(rv == sizeof(buf));
481 
482 	rv = rump_sys_read(fd, buf, sizeof(buf));
483 	ATF_REQUIRE(rv == 0);
484 
485 	rump_sys_close(fd);
486 	if (rump_sys_unmount("/mp", 0) == -1)
487 		atf_tc_fail_errno("unmount");
488 }
489 
490 /*
491  * Inactive/reclaim tests
492  */
493 
494 ATF_TC(inactive_basic);
495 ATF_TC_HEAD(inactive_basic, tc)
496 {
497 
498 	atf_tc_set_md_var(tc, "descr", "inactive gets called");
499 }
500 
501 ATF_TC_BODY(inactive_basic, tc)
502 {
503 	char *myopts[] = {
504 		"-i",
505 		"dtfs",
506 	};
507 	int fd;
508 
509 	dtfsmountv("/mp", myopts);
510 	fd = rump_sys_open("/mp/file", O_CREAT | O_RDWR, 0777);
511 	if (fd == -1)
512 		atf_tc_fail_errno("create");
513 
514 	/* one for /mp */
515 	ATF_REQUIRE_EQ(vn_toserv_ops[PUFFS_VN_INACTIVE], 1);
516 
517 	rump_sys_close(fd);
518 
519 	/* one for /mp/file */
520 	ATF_REQUIRE_EQ(vn_toserv_ops[PUFFS_VN_INACTIVE], 2);
521 
522 	if (rump_sys_unmount("/mp", 0) == -1)
523 		atf_tc_fail_errno("unmount");
524 
525 	/* one for /mp again */
526 	ATF_REQUIRE_EQ(vn_toserv_ops[PUFFS_VN_INACTIVE], 3);
527 }
528 
529 ATF_TC(inactive_reclaim);
530 ATF_TC_HEAD(inactive_reclaim, tc)
531 {
532 
533 	atf_tc_set_md_var(tc, "descr", "inactive/reclaim gets called");
534 }
535 
536 ATF_TC_BODY(inactive_reclaim, tc)
537 {
538 	char *myopts[] = {
539 		"-i",
540 		"dtfs",
541 	};
542 	int fd;
543 
544 	dtfsmountv("/mp", myopts);
545 	fd = rump_sys_open("/mp/file", O_CREAT | O_RDWR, 0777);
546 	if (fd == -1)
547 		atf_tc_fail_errno("create");
548 
549 	ATF_REQUIRE_EQ(vn_toserv_ops[PUFFS_VN_INACTIVE], 1);
550 
551 	if (rump_sys_unlink("/mp/file") == -1)
552 		atf_tc_fail_errno("remove");
553 
554 	ATF_REQUIRE_EQ(vn_toserv_ops[PUFFS_VN_INACTIVE], 2);
555 	ATF_REQUIRE_EQ(vn_toserv_ops[PUFFS_VN_RECLAIM], 0);
556 
557 	rump_sys_close(fd);
558 	syncbar("/mp");
559 
560 	ATF_REQUIRE_EQ(vn_toserv_ops[PUFFS_VN_INACTIVE], 4);
561 	ATF_REQUIRE_EQ(vn_toserv_ops[PUFFS_VN_RECLAIM], 1);
562 
563 	if (rump_sys_unmount("/mp", 0) == -1)
564 		atf_tc_fail_errno("unmount");
565 }
566 
567 ATF_TC(reclaim_hardlink);
568 ATF_TC_HEAD(reclaim_hardlink, tc)
569 {
570 
571 	atf_tc_set_md_var(tc, "descr", "reclaim gets called only after "
572 	    "final link is gone");
573 }
574 
575 ATF_TC_BODY(reclaim_hardlink, tc)
576 {
577 	char *myopts[] = {
578 		"-i",
579 		"dtfs",
580 	};
581 	int fd;
582 	int ianow;
583 
584 	dtfsmountv("/mp", myopts);
585 	fd = rump_sys_open("/mp/file", O_CREAT | O_RDWR, 0777);
586 	if (fd == -1)
587 		atf_tc_fail_errno("create");
588 
589 	if (rump_sys_link("/mp/file", "/mp/anotherfile") == -1)
590 		atf_tc_fail_errno("create link");
591 	rump_sys_close(fd);
592 
593 	ATF_REQUIRE_EQ(vn_toserv_ops[PUFFS_VN_RECLAIM], 0);
594 
595 	/* unlink first hardlink */
596 	if (rump_sys_unlink("/mp/file") == -1)
597 		atf_tc_fail_errno("unlink 1");
598 
599 	ATF_REQUIRE_EQ(vn_toserv_ops[PUFFS_VN_RECLAIM], 0);
600 	ianow = vn_toserv_ops[PUFFS_VN_INACTIVE];
601 
602 	/* unlink second hardlink */
603 	if (rump_sys_unlink("/mp/anotherfile") == -1)
604 		atf_tc_fail_errno("unlink 2");
605 
606 	syncbar("/mp");
607 
608 	ATF_REQUIRE(ianow < vn_toserv_ops[PUFFS_VN_INACTIVE]);
609 	ATF_REQUIRE_EQ(vn_toserv_ops[PUFFS_VN_RECLAIM], 1);
610 
611 	if (rump_sys_unmount("/mp", 0) == -1)
612 		atf_tc_fail_errno("unmount");
613 }
614 
615 ATF_TC(unlink_accessible);
616 ATF_TC_HEAD(unlink_accessible, tc)
617 {
618 
619 	atf_tc_set_md_var(tc, "descr", "open file is accessible after "
620 	    "having been unlinked");
621 }
622 
623 ATF_TC_BODY(unlink_accessible, tc)
624 {
625 	char *myopts[] = {
626 		"-i",
627 		"-o","nopagecache",
628 		"dtfs",
629 	};
630 	char buf[512];
631 	int fd, ianow;
632 
633 	assert(sizeof(buf) > sizeof(MAGICSTR));
634 
635 	dtfsmountv("/mp", myopts);
636 	fd = rump_sys_open("/mp/file", O_CREAT | O_RDWR, 0777);
637 	if (fd == -1)
638 		atf_tc_fail_errno("create");
639 
640 	if (rump_sys_write(fd, MAGICSTR, sizeof(MAGICSTR)) != sizeof(MAGICSTR))
641 		atf_tc_fail_errno("write");
642 	if (rump_sys_unlink("/mp/file") == -1)
643 		atf_tc_fail_errno("unlink");
644 
645 	ATF_REQUIRE_EQ(vn_toserv_ops[PUFFS_VN_RECLAIM], 0);
646 	ianow = vn_toserv_ops[PUFFS_VN_INACTIVE];
647 
648 	if (rump_sys_pread(fd, buf, sizeof(buf), 0) == -1)
649 		atf_tc_fail_errno("read");
650 	rump_sys_close(fd);
651 
652 	syncbar("/mp");
653 
654 	ATF_REQUIRE_EQ(vn_toserv_ops[PUFFS_VN_RECLAIM], 1);
655 	ATF_REQUIRE_EQ(vn_toserv_ops[PUFFS_VN_INACTIVE], ianow+2);
656 
657 	ATF_REQUIRE_STREQ(buf, MAGICSTR);
658 
659 	if (rump_sys_unmount("/mp", 0) == -1)
660 		atf_tc_fail_errno("unmount");
661 }
662 
663 ATF_TP_ADD_TCS(tp)
664 {
665 
666 	ATF_TP_ADD_TC(tp, mount);
667 
668 	ATF_TP_ADD_TC(tp, root_fifo);
669 	ATF_TP_ADD_TC(tp, root_lnk);
670 	ATF_TP_ADD_TC(tp, root_reg);
671 	ATF_TP_ADD_TC(tp, root_chrdev);
672 
673 	ATF_TP_ADD_TC(tp, inactive_basic);
674 	ATF_TP_ADD_TC(tp, inactive_reclaim);
675 	ATF_TP_ADD_TC(tp, reclaim_hardlink);
676 	ATF_TP_ADD_TC(tp, unlink_accessible);
677 
678 	return atf_no_error();
679 }
680