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 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 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); 63 ATF_TC_HEAD(mount, tc) 64 { 65 66 atf_tc_set_md_var(tc, "descr", "puffs+dtfs un/mount test"); 67 } 68 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); 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 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); 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" 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); 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 * 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 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); 185 ATF_TC_HEAD(root_chrdev, tc) 186 { 187 188 atf_tc_set_md_var(tc, "descr", "root is /dev/null"); 189 } 190 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); 221 ATF_TC_HEAD(inactive_basic, tc) 222 { 223 224 atf_tc_set_md_var(tc, "descr", "inactive gets called"); 225 } 226 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); 258 ATF_TC_HEAD(inactive_reclaim, tc) 259 { 260 261 atf_tc_set_md_var(tc, "descr", "inactive/reclaim gets called"); 262 } 263 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); 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 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); 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 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); 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 *); 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 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