1 /* $NetBSD: t_basic.c,v 1.11 2011/04/04 15:42:42 plunky 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 struct puffstestargs *pargs; 119 void *args; 120 char buf[PATH_MAX]; 121 ssize_t len; 122 123 FSTEST_CONSTRUCTOR_FSPRIV(tc, puffs, args, theopts); 124 pargs = args; 125 126 if ((len = rump_sys_readlink(FSTEST_MNTNAME, buf, sizeof(buf)-1)) == -1) 127 atf_tc_fail_errno("readlink"); 128 buf[len] = '\0'; 129 130 ATF_REQUIRE_STREQ(buf, LINKSTR); 131 132 #if 0 /* XXX: unmount uses FOLLOW */ 133 if (rump_sys_unmount("/mp", 0) == -1) 134 atf_tc_fail_errno("unmount"); 135 #endif 136 } 137 138 ATF_TC(root_fifo); 139 ATF_TC_HEAD(root_fifo, tc) 140 { 141 142 atf_tc_set_md_var(tc, "descr", "root is a symbolic link"); 143 } 144 145 #define MAGICSTR "nakit ja muusiperunat maustevoilla" 146 static void * 147 dofifow(void *arg) 148 { 149 int fd = (int)(uintptr_t)arg; 150 char buf[512]; 151 152 printf("writing\n"); 153 strcpy(buf, MAGICSTR); 154 if (rump_sys_write(fd, buf, strlen(buf)+1) != strlen(buf)+1) 155 atf_tc_fail_errno("write to fifo"); 156 157 return NULL; 158 } 159 160 ATF_TC_BODY(root_fifo, tc) 161 { 162 MAKEOPTS("-r", "fifo"); 163 void *args; 164 pthread_t pt; 165 char buf[512]; 166 int fd; 167 168 FSTEST_CONSTRUCTOR_FSPRIV(tc, puffs, args, theopts); 169 170 fd = rump_sys_open(FSTEST_MNTNAME, O_RDWR); 171 if (fd == -1) 172 atf_tc_fail_errno("open fifo"); 173 174 pthread_create(&pt, NULL, dofifow, (void *)(uintptr_t)fd); 175 176 memset(buf, 0, sizeof(buf)); 177 if (rump_sys_read(fd, buf, sizeof(buf)) == -1) 178 atf_tc_fail_errno("read fifo"); 179 180 ATF_REQUIRE_STREQ(buf, MAGICSTR); 181 rump_sys_close(fd); 182 183 FSTEST_DESTRUCTOR(tc, puffs, args); 184 } 185 186 ATF_TC(root_chrdev); 187 ATF_TC_HEAD(root_chrdev, tc) 188 { 189 190 atf_tc_set_md_var(tc, "descr", "root is /dev/null"); 191 } 192 193 ATF_TC_BODY(root_chrdev, tc) 194 { 195 MAKEOPTS("-r", "chr 2 2"); 196 void *args; 197 ssize_t rv; 198 char buf[512]; 199 int fd; 200 201 FSTEST_CONSTRUCTOR_FSPRIV(tc, puffs, args, theopts); 202 203 fd = rump_sys_open(FSTEST_MNTNAME, O_RDWR); 204 if (fd == -1) 205 atf_tc_fail_errno("open null"); 206 207 rv = rump_sys_write(fd, buf, sizeof(buf)); 208 ATF_REQUIRE(rv == sizeof(buf)); 209 210 rv = rump_sys_read(fd, buf, sizeof(buf)); 211 ATF_REQUIRE(rv == 0); 212 213 rump_sys_close(fd); 214 215 FSTEST_DESTRUCTOR(tc, puffs, args); 216 } 217 218 /* 219 * Inactive/reclaim tests 220 */ 221 222 ATF_TC(inactive_basic); 223 ATF_TC_HEAD(inactive_basic, tc) 224 { 225 226 atf_tc_set_md_var(tc, "descr", "inactive gets called"); 227 } 228 229 ATF_TC_BODY(inactive_basic, tc) 230 { 231 struct puffstestargs *pargs; 232 void *args; 233 int fd; 234 235 FSTEST_CONSTRUCTOR(tc, puffs, args); 236 FSTEST_ENTER(); 237 pargs = args; 238 239 fd = rump_sys_open("file", O_CREAT | O_RDWR, 0777); 240 if (fd == -1) 241 atf_tc_fail_errno("create"); 242 243 /* none yet */ 244 ATF_REQUIRE_EQ(pargs->pta_vn_toserv_ops[PUFFS_VN_INACTIVE], 0); 245 246 rump_sys_close(fd); 247 248 /* one for file */ 249 ATF_REQUIRE_EQ(pargs->pta_vn_toserv_ops[PUFFS_VN_INACTIVE], 1); 250 251 FSTEST_EXIT(); 252 253 /* another for the mountpoint */ 254 ATF_REQUIRE_EQ(pargs->pta_vn_toserv_ops[PUFFS_VN_INACTIVE], 2); 255 256 FSTEST_DESTRUCTOR(tc, puffs, args); 257 } 258 259 ATF_TC(inactive_reclaim); 260 ATF_TC_HEAD(inactive_reclaim, tc) 261 { 262 263 atf_tc_set_md_var(tc, "descr", "inactive/reclaim gets called"); 264 } 265 266 ATF_TC_BODY(inactive_reclaim, tc) 267 { 268 struct puffstestargs *pargs; 269 void *args; 270 int fd; 271 272 FSTEST_CONSTRUCTOR(tc, puffs, args); 273 FSTEST_ENTER(); 274 pargs = args; 275 276 fd = rump_sys_open("file", O_CREAT | O_RDWR, 0777); 277 if (fd == -1) 278 atf_tc_fail_errno("create"); 279 280 ATF_REQUIRE_EQ(pargs->pta_vn_toserv_ops[PUFFS_VN_INACTIVE], 0); 281 282 if (rump_sys_unlink("file") == -1) 283 atf_tc_fail_errno("remove"); 284 285 ATF_REQUIRE_EQ(pargs->pta_vn_toserv_ops[PUFFS_VN_INACTIVE], 0); 286 ATF_REQUIRE_EQ(pargs->pta_vn_toserv_ops[PUFFS_VN_RECLAIM], 0); 287 288 rump_sys_close(fd); 289 syncbar(FSTEST_MNTNAME); 290 291 ATF_REQUIRE_EQ(pargs->pta_vn_toserv_ops[PUFFS_VN_INACTIVE], 1); 292 ATF_REQUIRE_EQ(pargs->pta_vn_toserv_ops[PUFFS_VN_RECLAIM], 1); 293 294 FSTEST_EXIT(); 295 FSTEST_DESTRUCTOR(tc, puffs, args); 296 } 297 298 ATF_TC(reclaim_hardlink); 299 ATF_TC_HEAD(reclaim_hardlink, tc) 300 { 301 302 atf_tc_set_md_var(tc, "descr", "reclaim gets called only after " 303 "final link is gone"); 304 } 305 306 ATF_TC_BODY(reclaim_hardlink, tc) 307 { 308 struct puffstestargs *pargs; 309 void *args; 310 int fd; 311 int ianow; 312 313 FSTEST_CONSTRUCTOR(tc, puffs, args); 314 FSTEST_ENTER(); 315 pargs = args; 316 317 fd = rump_sys_open("file", O_CREAT | O_RDWR, 0777); 318 if (fd == -1) 319 atf_tc_fail_errno("create"); 320 321 if (rump_sys_link("file", "anotherfile") == -1) 322 atf_tc_fail_errno("create link"); 323 rump_sys_close(fd); 324 325 ATF_REQUIRE_EQ(pargs->pta_vn_toserv_ops[PUFFS_VN_RECLAIM], 0); 326 327 /* unlink first hardlink */ 328 if (rump_sys_unlink("file") == -1) 329 atf_tc_fail_errno("unlink 1"); 330 331 ATF_REQUIRE_EQ(pargs->pta_vn_toserv_ops[PUFFS_VN_RECLAIM], 0); 332 ianow = pargs->pta_vn_toserv_ops[PUFFS_VN_INACTIVE]; 333 334 /* unlink second hardlink */ 335 if (rump_sys_unlink("anotherfile") == -1) 336 atf_tc_fail_errno("unlink 2"); 337 338 syncbar(FSTEST_MNTNAME); 339 340 ATF_REQUIRE(ianow < pargs->pta_vn_toserv_ops[PUFFS_VN_INACTIVE]); 341 ATF_REQUIRE_EQ(pargs->pta_vn_toserv_ops[PUFFS_VN_RECLAIM], 1); 342 343 FSTEST_EXIT(); 344 FSTEST_DESTRUCTOR(tc, puffs, args); 345 } 346 347 ATF_TC(unlink_accessible); 348 ATF_TC_HEAD(unlink_accessible, tc) 349 { 350 351 atf_tc_set_md_var(tc, "descr", "open file is accessible after " 352 "having been unlinked"); 353 } 354 355 ATF_TC_BODY(unlink_accessible, tc) 356 { 357 MAKEOPTS("-i", "-o", "nopagecache"); 358 struct puffstestargs *pargs; 359 void *args; 360 char buf[512]; 361 int fd, ianow; 362 363 assert(sizeof(buf) > sizeof(MAGICSTR)); 364 365 FSTEST_CONSTRUCTOR_FSPRIV(tc, puffs, args, theopts); 366 FSTEST_ENTER(); 367 pargs = args; 368 369 fd = rump_sys_open("file", O_CREAT | O_RDWR, 0777); 370 if (fd == -1) 371 atf_tc_fail_errno("create"); 372 373 if (rump_sys_write(fd, MAGICSTR, sizeof(MAGICSTR)) != sizeof(MAGICSTR)) 374 atf_tc_fail_errno("write"); 375 if (rump_sys_unlink("file") == -1) 376 atf_tc_fail_errno("unlink"); 377 378 ATF_REQUIRE_EQ(pargs->pta_vn_toserv_ops[PUFFS_VN_RECLAIM], 0); 379 ianow = pargs->pta_vn_toserv_ops[PUFFS_VN_INACTIVE]; 380 381 if (rump_sys_pread(fd, buf, sizeof(buf), 0) == -1) 382 atf_tc_fail_errno("read"); 383 rump_sys_close(fd); 384 385 syncbar(FSTEST_MNTNAME); 386 387 ATF_REQUIRE_EQ(pargs->pta_vn_toserv_ops[PUFFS_VN_RECLAIM], 1); 388 ATF_REQUIRE_EQ(pargs->pta_vn_toserv_ops[PUFFS_VN_INACTIVE], ianow+1); 389 390 ATF_REQUIRE_STREQ(buf, MAGICSTR); 391 392 FSTEST_EXIT(); 393 FSTEST_DESTRUCTOR(tc, puffs, args); 394 } 395 396 ATF_TC(signals); 397 ATF_TC_HEAD(signals, tc) 398 { 399 400 atf_tc_set_md_var(tc, "descr", "Checks that sending a signal can " 401 "cause an interrupt to puffs wait"); 402 } 403 404 extern struct proc *rumpns_initproc; 405 extern void rumpns_psignal(struct proc *, int); 406 extern void rumpns_sigclearall(struct proc *, void *, void *); 407 ATF_TC_BODY(signals, tc) 408 { 409 struct stat sb; 410 void *args; 411 412 rump_boot_setsigmodel(RUMP_SIGMODEL_RECORD); 413 414 FSTEST_CONSTRUCTOR(tc, puffs, args); 415 FSTEST_ENTER(); 416 RL(rump_sys_stat(".", &sb)); 417 418 /* send SIGUSR1, should not affect puffs ops */ 419 rump_schedule(); 420 rumpns_psignal(rumpns_initproc, SIGUSR1); 421 rump_unschedule(); 422 RL(rump_sys_stat(".", &sb)); 423 424 /* send SIGTERM, should get EINTR */ 425 rump_schedule(); 426 rumpns_psignal(rumpns_initproc, SIGTERM); 427 rump_unschedule(); 428 ATF_REQUIRE_ERRNO(EINTR, rump_sys_stat(".", &sb) == -1); 429 430 /* clear sigmask so that we can unmount */ 431 rump_schedule(); 432 rumpns_sigclearall(rumpns_initproc, NULL, NULL); 433 rump_unschedule(); 434 435 FSTEST_EXIT(); 436 FSTEST_DESTRUCTOR(tc, puffs, args); 437 } 438 439 ATF_TP_ADD_TCS(tp) 440 { 441 442 ATF_TP_ADD_TC(tp, mount); 443 444 ATF_TP_ADD_TC(tp, root_fifo); 445 ATF_TP_ADD_TC(tp, root_lnk); 446 ATF_TP_ADD_TC(tp, root_reg); 447 ATF_TP_ADD_TC(tp, root_chrdev); 448 449 ATF_TP_ADD_TC(tp, inactive_basic); 450 ATF_TP_ADD_TC(tp, inactive_reclaim); 451 ATF_TP_ADD_TC(tp, reclaim_hardlink); 452 ATF_TP_ADD_TC(tp, unlink_accessible); 453 454 ATF_TP_ADD_TC(tp, signals); 455 456 return atf_no_error(); 457 } 458