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  */
33 static void
syncbar(const char * fs)34 syncbar(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
43 static void __unused
dumpopcount(struct puffstestargs * args)44 dumpopcount(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 
62 ATF_TC(mount);
ATF_TC_HEAD(mount,tc)63 ATF_TC_HEAD(mount, tc)
64 {
65 
66           atf_tc_set_md_var(tc, "descr", "puffs+dtfs un/mount test");
67 }
68 
ATF_TC_BODY(mount,tc)69 ATF_TC_BODY(mount, tc)
70 {
71           void *args;
72 
73           FSTEST_CONSTRUCTOR(tc, puffs, args);
74           FSTEST_DESTRUCTOR(tc, puffs, args);
75 }
76 
77 ATF_TC(root_reg);
ATF_TC_HEAD(root_reg,tc)78 ATF_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 
ATF_TC_BODY(root_reg,tc)86 ATF_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 
107 ATF_TC(root_lnk);
ATF_TC_HEAD(root_lnk,tc)108 ATF_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"
ATF_TC_BODY(root_lnk,tc)115 ATF_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 
136 ATF_TC(root_fifo);
ATF_TC_HEAD(root_fifo,tc)137 ATF_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"
144 static void *
dofifow(void * arg)145 dofifow(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 
ATF_TC_BODY(root_fifo,tc)158 ATF_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 
184 ATF_TC(root_chrdev);
ATF_TC_HEAD(root_chrdev,tc)185 ATF_TC_HEAD(root_chrdev, tc)
186 {
187 
188           atf_tc_set_md_var(tc, "descr", "root is /dev/null");
189 }
190 
ATF_TC_BODY(root_chrdev,tc)191 ATF_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 
220 ATF_TC(inactive_basic);
ATF_TC_HEAD(inactive_basic,tc)221 ATF_TC_HEAD(inactive_basic, tc)
222 {
223 
224           atf_tc_set_md_var(tc, "descr", "inactive gets called");
225 }
226 
ATF_TC_BODY(inactive_basic,tc)227 ATF_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 
257 ATF_TC(inactive_reclaim);
ATF_TC_HEAD(inactive_reclaim,tc)258 ATF_TC_HEAD(inactive_reclaim, tc)
259 {
260 
261           atf_tc_set_md_var(tc, "descr", "inactive/reclaim gets called");
262 }
263 
ATF_TC_BODY(inactive_reclaim,tc)264 ATF_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 
296 ATF_TC(reclaim_hardlink);
ATF_TC_HEAD(reclaim_hardlink,tc)297 ATF_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 
ATF_TC_BODY(reclaim_hardlink,tc)304 ATF_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 
345 ATF_TC(unlink_accessible);
ATF_TC_HEAD(unlink_accessible,tc)346 ATF_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 
ATF_TC_BODY(unlink_accessible,tc)353 ATF_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 
394 ATF_TC(signals);
ATF_TC_HEAD(signals,tc)395 ATF_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 
402 extern struct proc *rumpns_initproc;
403 extern void rumpns_psignal(struct proc *, int);
404 extern void rumpns_sigclearall(struct proc *, void *, void *);
ATF_TC_BODY(signals,tc)405 ATF_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 
ATF_TP_ADD_TCS(tp)437 ATF_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