1 /* $NetBSD: t_basic.c,v 1.7 2010/07/30 16:15:06 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 #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 static void __unused 43 dumpopcount(struct puffstestargs *args) 44 { 45 size_t i; 46 47 printf("VFS OPS:\n"); 48 for (i = 0; i < MIN(puffsdump_vfsop_count, PUFFS_VFS_MAX); i++) { 49 printf("\t%s: %d\n", 50 puffsdump_vfsop_revmap[i], args->pta_vfs_toserv_ops[i]); 51 } 52 53 printf("VN OPS:\n"); 54 for (i = 0; i < MIN(puffsdump_vnop_count, PUFFS_VN_MAX); i++) { 55 printf("\t%s: %d\n", 56 puffsdump_vnop_revmap[i], args->pta_vn_toserv_ops[i]); 57 } 58 } 59 60 ATF_TC(mount); 61 ATF_TC_HEAD(mount, tc) 62 { 63 64 atf_tc_set_md_var(tc, "descr", "puffs+dtfs un/mount test"); 65 } 66 67 ATF_TC_BODY(mount, tc) 68 { 69 void *args; 70 71 FSTEST_CONSTRUCTOR(tc, puffs, args); 72 FSTEST_DESTRUCTOR(tc, puffs, args); 73 } 74 75 ATF_TC(root_reg); 76 ATF_TC_HEAD(root_reg, tc) 77 { 78 atf_tc_set_md_var(tc, "descr", "root is a regular file"); 79 } 80 81 #define MAKEOPTS(...) \ 82 char *theopts[] = {NULL, "-s", __VA_ARGS__, "dtfs", "n/a", NULL} 83 84 ATF_TC_BODY(root_reg, tc) 85 { 86 MAKEOPTS("-r", "reg"); 87 void *args; 88 int fd, rv; 89 90 FSTEST_CONSTRUCTOR_FSPRIV(tc, puffs, args, theopts); 91 92 fd = rump_sys_open(FSTEST_MNTNAME, O_RDWR); 93 if (fd == -1) 94 atf_tc_fail_errno("open root"); 95 if (rump_sys_write(fd, &fd, sizeof(fd)) != sizeof(fd)) 96 atf_tc_fail_errno("write to root"); 97 rv = rump_sys_mkdir(FSTEST_MNTNAME "/test", 0777); 98 ATF_REQUIRE(errno == ENOTDIR); 99 ATF_REQUIRE(rv == -1); 100 rump_sys_close(fd); 101 102 FSTEST_DESTRUCTOR(tc, puffs, args); 103 } 104 105 ATF_TC(root_lnk); 106 ATF_TC_HEAD(root_lnk, tc) 107 { 108 109 atf_tc_set_md_var(tc, "descr", "root is a symbolic link"); 110 } 111 112 #define LINKSTR "/path/to/nowhere" 113 ATF_TC_BODY(root_lnk, tc) 114 { 115 MAKEOPTS("-r", "lnk " LINKSTR); 116 struct puffstestargs *pargs; 117 void *args; 118 char buf[PATH_MAX]; 119 ssize_t len; 120 121 FSTEST_CONSTRUCTOR_FSPRIV(tc, puffs, args, theopts); 122 pargs = args; 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 * XXX2: due to atf issue #53, we must make sure the child dies 137 * before we exit. 138 */ 139 signal(SIGCHLD, SIG_IGN); 140 if (kill(pargs->pta_childpid, SIGTERM) == -1) 141 atf_tc_fail_errno("kill"); 142 } 143 144 ATF_TC(root_fifo); 145 ATF_TC_HEAD(root_fifo, tc) 146 { 147 148 atf_tc_set_md_var(tc, "descr", "root is a symbolic link"); 149 } 150 151 #define MAGICSTR "nakit ja muusiperunat maustevoilla" 152 static void * 153 dofifow(void *arg) 154 { 155 int fd = (int)(uintptr_t)arg; 156 char buf[512]; 157 158 printf("writing\n"); 159 strcpy(buf, MAGICSTR); 160 if (rump_sys_write(fd, buf, strlen(buf)+1) != strlen(buf)+1) 161 atf_tc_fail_errno("write to fifo"); 162 163 return NULL; 164 } 165 166 ATF_TC_BODY(root_fifo, tc) 167 { 168 MAKEOPTS("-r", "fifo"); 169 void *args; 170 pthread_t pt; 171 char buf[512]; 172 int fd; 173 174 FSTEST_CONSTRUCTOR_FSPRIV(tc, puffs, args, theopts); 175 176 fd = rump_sys_open(FSTEST_MNTNAME, O_RDWR); 177 if (fd == -1) 178 atf_tc_fail_errno("open fifo"); 179 180 pthread_create(&pt, NULL, dofifow, (void *)(uintptr_t)fd); 181 182 memset(buf, 0, sizeof(buf)); 183 if (rump_sys_read(fd, buf, sizeof(buf)) == -1) 184 atf_tc_fail_errno("read fifo"); 185 186 ATF_REQUIRE_STREQ(buf, MAGICSTR); 187 rump_sys_close(fd); 188 189 FSTEST_DESTRUCTOR(tc, puffs, args); 190 } 191 192 ATF_TC(root_chrdev); 193 ATF_TC_HEAD(root_chrdev, tc) 194 { 195 196 atf_tc_set_md_var(tc, "descr", "root is /dev/null"); 197 } 198 199 ATF_TC_BODY(root_chrdev, tc) 200 { 201 MAKEOPTS("-r", "chr 2 0"); 202 void *args; 203 ssize_t rv; 204 char buf[512]; 205 int fd; 206 207 FSTEST_CONSTRUCTOR_FSPRIV(tc, puffs, args, theopts); 208 209 fd = rump_sys_open(FSTEST_MNTNAME, O_RDWR); 210 if (fd == -1) 211 atf_tc_fail_errno("open null"); 212 213 rv = rump_sys_write(fd, buf, sizeof(buf)); 214 ATF_REQUIRE(rv == sizeof(buf)); 215 216 rv = rump_sys_read(fd, buf, sizeof(buf)); 217 ATF_REQUIRE(rv == 0); 218 219 rump_sys_close(fd); 220 221 FSTEST_DESTRUCTOR(tc, puffs, args); 222 } 223 224 /* 225 * Inactive/reclaim tests 226 */ 227 228 ATF_TC(inactive_basic); 229 ATF_TC_HEAD(inactive_basic, tc) 230 { 231 232 atf_tc_set_md_var(tc, "descr", "inactive gets called"); 233 } 234 235 ATF_TC_BODY(inactive_basic, tc) 236 { 237 struct puffstestargs *pargs; 238 void *args; 239 int fd; 240 241 FSTEST_CONSTRUCTOR(tc, puffs, args); 242 FSTEST_ENTER(); 243 pargs = args; 244 245 fd = rump_sys_open("file", O_CREAT | O_RDWR, 0777); 246 if (fd == -1) 247 atf_tc_fail_errno("create"); 248 249 /* none yet */ 250 ATF_REQUIRE_EQ(pargs->pta_vn_toserv_ops[PUFFS_VN_INACTIVE], 0); 251 252 rump_sys_close(fd); 253 254 /* one for file */ 255 ATF_REQUIRE_EQ(pargs->pta_vn_toserv_ops[PUFFS_VN_INACTIVE], 1); 256 257 FSTEST_EXIT(); 258 259 /* another for the mountpoint */ 260 ATF_REQUIRE_EQ(pargs->pta_vn_toserv_ops[PUFFS_VN_INACTIVE], 2); 261 262 FSTEST_DESTRUCTOR(tc, puffs, args); 263 } 264 265 ATF_TC(inactive_reclaim); 266 ATF_TC_HEAD(inactive_reclaim, tc) 267 { 268 269 atf_tc_set_md_var(tc, "descr", "inactive/reclaim gets called"); 270 } 271 272 ATF_TC_BODY(inactive_reclaim, tc) 273 { 274 struct puffstestargs *pargs; 275 void *args; 276 int fd; 277 278 FSTEST_CONSTRUCTOR(tc, puffs, args); 279 FSTEST_ENTER(); 280 pargs = args; 281 282 fd = rump_sys_open("file", O_CREAT | O_RDWR, 0777); 283 if (fd == -1) 284 atf_tc_fail_errno("create"); 285 286 ATF_REQUIRE_EQ(pargs->pta_vn_toserv_ops[PUFFS_VN_INACTIVE], 0); 287 288 if (rump_sys_unlink("file") == -1) 289 atf_tc_fail_errno("remove"); 290 291 ATF_REQUIRE_EQ(pargs->pta_vn_toserv_ops[PUFFS_VN_INACTIVE], 0); 292 ATF_REQUIRE_EQ(pargs->pta_vn_toserv_ops[PUFFS_VN_RECLAIM], 0); 293 294 rump_sys_close(fd); 295 syncbar(FSTEST_MNTNAME); 296 297 ATF_REQUIRE_EQ(pargs->pta_vn_toserv_ops[PUFFS_VN_INACTIVE], 1); 298 ATF_REQUIRE_EQ(pargs->pta_vn_toserv_ops[PUFFS_VN_RECLAIM], 1); 299 300 FSTEST_EXIT(); 301 FSTEST_DESTRUCTOR(tc, puffs, args); 302 } 303 304 ATF_TC(reclaim_hardlink); 305 ATF_TC_HEAD(reclaim_hardlink, tc) 306 { 307 308 atf_tc_set_md_var(tc, "descr", "reclaim gets called only after " 309 "final link is gone"); 310 } 311 312 ATF_TC_BODY(reclaim_hardlink, tc) 313 { 314 struct puffstestargs *pargs; 315 void *args; 316 int fd; 317 int ianow; 318 319 FSTEST_CONSTRUCTOR(tc, puffs, args); 320 FSTEST_ENTER(); 321 pargs = args; 322 323 fd = rump_sys_open("file", O_CREAT | O_RDWR, 0777); 324 if (fd == -1) 325 atf_tc_fail_errno("create"); 326 327 if (rump_sys_link("file", "anotherfile") == -1) 328 atf_tc_fail_errno("create link"); 329 rump_sys_close(fd); 330 331 ATF_REQUIRE_EQ(pargs->pta_vn_toserv_ops[PUFFS_VN_RECLAIM], 0); 332 333 /* unlink first hardlink */ 334 if (rump_sys_unlink("file") == -1) 335 atf_tc_fail_errno("unlink 1"); 336 337 ATF_REQUIRE_EQ(pargs->pta_vn_toserv_ops[PUFFS_VN_RECLAIM], 0); 338 ianow = pargs->pta_vn_toserv_ops[PUFFS_VN_INACTIVE]; 339 340 /* unlink second hardlink */ 341 if (rump_sys_unlink("anotherfile") == -1) 342 atf_tc_fail_errno("unlink 2"); 343 344 syncbar(FSTEST_MNTNAME); 345 346 ATF_REQUIRE(ianow < pargs->pta_vn_toserv_ops[PUFFS_VN_INACTIVE]); 347 ATF_REQUIRE_EQ(pargs->pta_vn_toserv_ops[PUFFS_VN_RECLAIM], 1); 348 349 FSTEST_EXIT(); 350 FSTEST_DESTRUCTOR(tc, puffs, args); 351 } 352 353 ATF_TC(unlink_accessible); 354 ATF_TC_HEAD(unlink_accessible, tc) 355 { 356 357 atf_tc_set_md_var(tc, "descr", "open file is accessible after " 358 "having been unlinked"); 359 } 360 361 ATF_TC_BODY(unlink_accessible, tc) 362 { 363 MAKEOPTS("-i", "-o", "nopagecache"); 364 struct puffstestargs *pargs; 365 void *args; 366 char buf[512]; 367 int fd, ianow; 368 369 assert(sizeof(buf) > sizeof(MAGICSTR)); 370 371 FSTEST_CONSTRUCTOR_FSPRIV(tc, puffs, args, theopts); 372 FSTEST_ENTER(); 373 pargs = args; 374 375 fd = rump_sys_open("file", O_CREAT | O_RDWR, 0777); 376 if (fd == -1) 377 atf_tc_fail_errno("create"); 378 379 if (rump_sys_write(fd, MAGICSTR, sizeof(MAGICSTR)) != sizeof(MAGICSTR)) 380 atf_tc_fail_errno("write"); 381 if (rump_sys_unlink("file") == -1) 382 atf_tc_fail_errno("unlink"); 383 384 ATF_REQUIRE_EQ(pargs->pta_vn_toserv_ops[PUFFS_VN_RECLAIM], 0); 385 ianow = pargs->pta_vn_toserv_ops[PUFFS_VN_INACTIVE]; 386 387 if (rump_sys_pread(fd, buf, sizeof(buf), 0) == -1) 388 atf_tc_fail_errno("read"); 389 rump_sys_close(fd); 390 391 syncbar(FSTEST_MNTNAME); 392 393 ATF_REQUIRE_EQ(pargs->pta_vn_toserv_ops[PUFFS_VN_RECLAIM], 1); 394 ATF_REQUIRE_EQ(pargs->pta_vn_toserv_ops[PUFFS_VN_INACTIVE], ianow+1); 395 396 ATF_REQUIRE_STREQ(buf, MAGICSTR); 397 398 FSTEST_EXIT(); 399 FSTEST_DESTRUCTOR(tc, puffs, args); 400 } 401 402 ATF_TP_ADD_TCS(tp) 403 { 404 405 ATF_TP_ADD_TC(tp, mount); 406 407 ATF_TP_ADD_TC(tp, root_fifo); 408 ATF_TP_ADD_TC(tp, root_lnk); 409 ATF_TP_ADD_TC(tp, root_reg); 410 ATF_TP_ADD_TC(tp, root_chrdev); 411 412 ATF_TP_ADD_TC(tp, inactive_basic); 413 ATF_TP_ADD_TC(tp, inactive_reclaim); 414 ATF_TP_ADD_TC(tp, reclaim_hardlink); 415 ATF_TP_ADD_TC(tp, unlink_accessible); 416 417 return atf_no_error(); 418 } 419