1 /* $NetBSD: t_basic.c,v 1.8 2010/08/27 05:34:46 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 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 0"); 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_EQ(pargs->pta_vn_toserv_ops[PUFFS_VN_INACTIVE], 1); 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_EQ(pargs->pta_vn_toserv_ops[PUFFS_VN_INACTIVE], ianow+1); 387 388 ATF_REQUIRE_STREQ(buf, MAGICSTR); 389 390 FSTEST_EXIT(); 391 FSTEST_DESTRUCTOR(tc, puffs, args); 392 } 393 394 ATF_TP_ADD_TCS(tp) 395 { 396 397 ATF_TP_ADD_TC(tp, mount); 398 399 ATF_TP_ADD_TC(tp, root_fifo); 400 ATF_TP_ADD_TC(tp, root_lnk); 401 ATF_TP_ADD_TC(tp, root_reg); 402 ATF_TP_ADD_TC(tp, root_chrdev); 403 404 ATF_TP_ADD_TC(tp, inactive_basic); 405 ATF_TP_ADD_TC(tp, inactive_reclaim); 406 ATF_TP_ADD_TC(tp, reclaim_hardlink); 407 ATF_TP_ADD_TC(tp, unlink_accessible); 408 409 return atf_no_error(); 410 } 411