1 /* $NetBSD: t_vnops.c,v 1.12 2011/01/11 14:03:38 kefren Exp $ */ 2 3 /*- 4 * Copyright (c) 2010 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #include <sys/stat.h> 30 #include <sys/statvfs.h> 31 32 #include <atf-c.h> 33 #include <fcntl.h> 34 #include <libgen.h> 35 #include <stdlib.h> 36 #include <unistd.h> 37 38 #include <rump/rump_syscalls.h> 39 #include <rump/rump.h> 40 41 #include "../common/h_fsmacros.h" 42 #include "../../h_macros.h" 43 44 #define TESTFILE "afile" 45 46 #define USES_DIRS \ 47 if (FSTYPE_SYSVBFS(tc)) atf_tc_skip("dirs not supported by file system") 48 49 #define USES_SYMLINKS \ 50 if (FSTYPE_SYSVBFS(tc) || FSTYPE_MSDOS(tc)) \ 51 atf_tc_skip("symlinks not supported by file system") 52 53 static char * 54 md(char *buf, const char *base, const char *tail) 55 { 56 57 sprintf(buf, "%s/%s", base, tail); 58 return buf; 59 } 60 61 static void 62 lookup_simple(const atf_tc_t *tc, const char *mountpath) 63 { 64 char pb[MAXPATHLEN], final[MAXPATHLEN]; 65 struct stat sb1, sb2; 66 67 strcpy(final, mountpath); 68 sprintf(pb, "%s/../%s", mountpath, basename(final)); 69 if (rump_sys_stat(pb, &sb1) == -1) 70 atf_tc_fail_errno("stat 1"); 71 72 sprintf(pb, "%s/./../%s", mountpath, basename(final)); 73 if (rump_sys_stat(pb, &sb2) == -1) 74 atf_tc_fail_errno("stat 2"); 75 76 ATF_REQUIRE(memcmp(&sb1, &sb2, sizeof(sb1)) == 0); 77 } 78 79 static void 80 lookup_complex(const atf_tc_t *tc, const char *mountpath) 81 { 82 char pb[MAXPATHLEN]; 83 struct stat sb1, sb2; 84 85 USES_DIRS; 86 87 sprintf(pb, "%s/dir", mountpath); 88 if (rump_sys_mkdir(pb, 0777) == -1) 89 atf_tc_fail_errno("mkdir"); 90 if (rump_sys_stat(pb, &sb1) == -1) 91 atf_tc_fail_errno("stat 1"); 92 93 sprintf(pb, "%s/./dir/../././dir/.", mountpath); 94 if (rump_sys_stat(pb, &sb2) == -1) 95 atf_tc_fail_errno("stat 2"); 96 97 ATF_REQUIRE(memcmp(&sb1, &sb2, sizeof(sb1)) == 0); 98 } 99 100 static void 101 dir_simple(const atf_tc_t *tc, const char *mountpath) 102 { 103 char pb[MAXPATHLEN]; 104 struct stat sb; 105 106 USES_DIRS; 107 108 /* check we can create directories */ 109 sprintf(pb, "%s/dir", mountpath); 110 if (rump_sys_mkdir(pb, 0777) == -1) 111 atf_tc_fail_errno("mkdir"); 112 if (rump_sys_stat(pb, &sb) == -1) 113 atf_tc_fail_errno("stat new directory"); 114 115 /* check we can remove then and that it makes them unreachable */ 116 if (rump_sys_rmdir(pb) == -1) 117 atf_tc_fail_errno("rmdir"); 118 if (rump_sys_stat(pb, &sb) != -1 || errno != ENOENT) 119 atf_tc_fail("ENOENT expected from stat"); 120 } 121 122 static void 123 dir_notempty(const atf_tc_t *tc, const char *mountpath) 124 { 125 char pb[MAXPATHLEN], pb2[MAXPATHLEN]; 126 int fd, rv; 127 128 USES_DIRS; 129 130 /* check we can create directories */ 131 sprintf(pb, "%s/dir", mountpath); 132 if (rump_sys_mkdir(pb, 0777) == -1) 133 atf_tc_fail_errno("mkdir"); 134 135 sprintf(pb2, "%s/dir/file", mountpath); 136 fd = rump_sys_open(pb2, O_RDWR | O_CREAT, 0777); 137 if (fd == -1) 138 atf_tc_fail_errno("create file"); 139 rump_sys_close(fd); 140 141 rv = rump_sys_rmdir(pb); 142 if (rv != -1 || errno != ENOTEMPTY) 143 atf_tc_fail("non-empty directory removed succesfully"); 144 145 if (rump_sys_unlink(pb2) == -1) 146 atf_tc_fail_errno("cannot remove dir/file"); 147 148 if (rump_sys_rmdir(pb) == -1) 149 atf_tc_fail_errno("remove directory"); 150 } 151 152 static void 153 checkfile(const char *path, struct stat *refp) 154 { 155 char buf[MAXPATHLEN]; 156 struct stat sb; 157 static int n = 1; 158 159 md(buf, path, "file"); 160 if (rump_sys_stat(buf, &sb) == -1) 161 atf_tc_fail_errno("cannot stat file %d (%s)", n, buf); 162 if (memcmp(&sb, refp, sizeof(sb)) != 0) 163 atf_tc_fail("stat mismatch %d", n); 164 n++; 165 } 166 167 static void 168 rename_dir(const atf_tc_t *tc, const char *mp) 169 { 170 char pb1[MAXPATHLEN], pb2[MAXPATHLEN], pb3[MAXPATHLEN]; 171 struct stat ref; 172 173 if (FSTYPE_MSDOS(tc)) 174 atf_tc_skip("test fails in some setups, reason unknown"); 175 176 if (FSTYPE_RUMPFS(tc)) 177 atf_tc_skip("rename not supported by fs"); 178 179 USES_DIRS; 180 181 md(pb1, mp, "dir1"); 182 if (rump_sys_mkdir(pb1, 0777) == -1) 183 atf_tc_fail_errno("mkdir 1"); 184 185 md(pb2, mp, "dir2"); 186 if (rump_sys_mkdir(pb2, 0777) == -1) 187 atf_tc_fail_errno("mkdir 2"); 188 md(pb2, mp, "dir2/subdir"); 189 if (rump_sys_mkdir(pb2, 0777) == -1) 190 atf_tc_fail_errno("mkdir 3"); 191 192 md(pb3, mp, "dir1/file"); 193 if (rump_sys_mknod(pb3, S_IFREG | 0777, -1) == -1) 194 atf_tc_fail_errno("create file"); 195 if (rump_sys_stat(pb3, &ref) == -1) 196 atf_tc_fail_errno("stat of file"); 197 198 /* 199 * First try ops which should succeed. 200 */ 201 202 /* rename within directory */ 203 md(pb3, mp, "dir3"); 204 if (rump_sys_rename(pb1, pb3) == -1) 205 atf_tc_fail_errno("rename 1"); 206 checkfile(pb3, &ref); 207 208 /* rename directory onto itself (two ways, should fail) */ 209 md(pb1, mp, "dir3/."); 210 if (rump_sys_rename(pb1, pb3) != -1 || errno != EINVAL) 211 atf_tc_fail_errno("rename 2"); 212 if (rump_sys_rename(pb3, pb1) != -1 || errno != EISDIR) 213 atf_tc_fail_errno("rename 3"); 214 215 checkfile(pb3, &ref); 216 217 /* rename father of directory into directory */ 218 md(pb1, mp, "dir2/dir"); 219 md(pb2, mp, "dir2"); 220 if (rump_sys_rename(pb2, pb1) != -1 || errno != EINVAL) 221 atf_tc_fail_errno("rename 4"); 222 223 /* same for grandfather */ 224 md(pb1, mp, "dir2/subdir/dir2"); 225 if (rump_sys_rename(pb2, pb1) != -1 || errno != EINVAL) 226 atf_tc_fail("rename 5"); 227 228 checkfile(pb3, &ref); 229 230 /* rename directory over a non-empty directory */ 231 if (rump_sys_rename(pb2, pb3) != -1 || errno != ENOTEMPTY) 232 atf_tc_fail("rename 6"); 233 234 /* cross-directory rename */ 235 md(pb1, mp, "dir3"); 236 md(pb2, mp, "dir2/somedir"); 237 if (rump_sys_rename(pb1, pb2) == -1) 238 atf_tc_fail_errno("rename 7"); 239 checkfile(pb2, &ref); 240 241 /* move to parent directory */ 242 md(pb1, mp, "dir2/somedir/../../dir3"); 243 if (rump_sys_rename(pb2, pb1) == -1) 244 atf_tc_fail_errno("rename 8"); 245 md(pb1, mp, "dir2/../dir3"); 246 checkfile(pb1, &ref); 247 248 /* finally, atomic cross-directory rename */ 249 md(pb3, mp, "dir2/subdir"); 250 if (rump_sys_rename(pb1, pb3) == -1) 251 atf_tc_fail_errno("rename 9"); 252 checkfile(pb3, &ref); 253 } 254 255 static void 256 rename_dotdot(const atf_tc_t *tc, const char *mp) 257 { 258 259 if (FSTYPE_RUMPFS(tc)) 260 atf_tc_skip("rename not supported by fs"); 261 262 USES_DIRS; 263 264 if (rump_sys_chdir(mp) == -1) 265 atf_tc_fail_errno("chdir mountpoint"); 266 267 if (rump_sys_mkdir("dir1", 0777) == -1) 268 atf_tc_fail_errno("mkdir 1"); 269 if (rump_sys_mkdir("dir2", 0777) == -1) 270 atf_tc_fail_errno("mkdir 2"); 271 272 if (rump_sys_rename("dir1", "dir1/..") != -1 || errno != EINVAL) 273 atf_tc_fail_errno("self-dotdot to"); 274 275 if (rump_sys_rename("dir1/..", "sometarget") != -1 || errno != EINVAL) 276 atf_tc_fail_errno("self-dotdot from"); 277 atf_tc_expect_pass(); 278 279 if (FSTYPE_TMPFS(tc)) { 280 atf_tc_expect_fail("PR kern/43617"); 281 } 282 if (rump_sys_rename("dir1", "dir2/..") != -1 || errno != EINVAL) 283 atf_tc_fail("other-dotdot"); 284 285 rump_sys_chdir("/"); 286 } 287 288 static void 289 rename_reg_nodir(const atf_tc_t *tc, const char *mp) 290 { 291 bool haslinks; 292 struct stat sb; 293 ino_t f1ino, f2ino; 294 295 if (FSTYPE_RUMPFS(tc)) 296 atf_tc_skip("rename not supported by fs"); 297 298 if (FSTYPE_MSDOS(tc)) 299 atf_tc_skip("test fails in some setups, reason unknown"); 300 301 if (rump_sys_chdir(mp) == -1) 302 atf_tc_fail_errno("chdir mountpoint"); 303 304 if (FSTYPE_MSDOS(tc) || FSTYPE_SYSVBFS(tc)) 305 haslinks = false; 306 else 307 haslinks = true; 308 309 if (rump_sys_mknod("file1", S_IFREG | 0777, -1) == -1) 310 atf_tc_fail_errno("create file"); 311 if (rump_sys_mknod("file2", S_IFREG | 0777, -1) == -1) 312 atf_tc_fail_errno("create file"); 313 314 if (rump_sys_stat("file1", &sb) == -1) 315 atf_tc_fail_errno("stat"); 316 f1ino = sb.st_ino; 317 318 if (haslinks) { 319 if (rump_sys_link("file1", "file_link") == -1) 320 atf_tc_fail_errno("link"); 321 if (rump_sys_stat("file_link", &sb) == -1) 322 atf_tc_fail_errno("stat"); 323 ATF_REQUIRE_EQ(sb.st_ino, f1ino); 324 ATF_REQUIRE_EQ(sb.st_nlink, 2); 325 } 326 327 if (rump_sys_stat("file2", &sb) == -1) 328 atf_tc_fail_errno("stat"); 329 f2ino = sb.st_ino; 330 331 if (rump_sys_rename("file1", "file3") == -1) 332 atf_tc_fail_errno("rename 1"); 333 if (rump_sys_stat("file3", &sb) == -1) 334 atf_tc_fail_errno("stat 1"); 335 if (haslinks) { 336 ATF_REQUIRE_EQ(sb.st_ino, f1ino); 337 } 338 if (rump_sys_stat("file1", &sb) != -1 || errno != ENOENT) 339 atf_tc_fail_errno("source 1"); 340 341 if (rump_sys_rename("file3", "file2") == -1) 342 atf_tc_fail_errno("rename 2"); 343 if (rump_sys_stat("file2", &sb) == -1) 344 atf_tc_fail_errno("stat 2"); 345 if (haslinks) { 346 ATF_REQUIRE_EQ(sb.st_ino, f1ino); 347 } 348 349 if (rump_sys_stat("file3", &sb) != -1 || errno != ENOENT) 350 atf_tc_fail_errno("source 2"); 351 352 if (haslinks) { 353 if (rump_sys_rename("file2", "file_link") == -1) 354 atf_tc_fail_errno("rename hardlink"); 355 if (rump_sys_stat("file2", &sb) != -1 || errno != ENOENT) 356 atf_tc_fail_errno("source 3"); 357 if (rump_sys_stat("file_link", &sb) == -1) 358 atf_tc_fail_errno("stat 2"); 359 ATF_REQUIRE_EQ(sb.st_ino, f1ino); 360 ATF_REQUIRE_EQ(sb.st_nlink, 1); 361 } 362 363 rump_sys_chdir("/"); 364 } 365 366 static void 367 create_nametoolong(const atf_tc_t *tc, const char *mp) 368 { 369 char *name; 370 int fd; 371 long val; 372 size_t len; 373 374 if (rump_sys_chdir(mp) == -1) 375 atf_tc_fail_errno("chdir mountpoint"); 376 377 val = rump_sys_pathconf(".", _PC_NAME_MAX); 378 if (val == -1) 379 atf_tc_fail_errno("pathconf"); 380 381 len = val + 1; 382 name = malloc(len+1); 383 if (name == NULL) 384 atf_tc_fail_errno("malloc"); 385 386 memset(name, 'a', len); 387 *(name+len) = '\0'; 388 389 val = rump_sys_pathconf(".", _PC_NO_TRUNC); 390 if (val == -1) 391 atf_tc_fail_errno("pathconf"); 392 393 if (FSTYPE_MSDOS(tc)) 394 atf_tc_expect_fail("PR kern/43670"); 395 fd = rump_sys_open(name, O_RDWR|O_CREAT, 0666); 396 if (val != 0 && (fd != -1 || errno != ENAMETOOLONG)) 397 atf_tc_fail_errno("open"); 398 399 if (val == 0 && rump_sys_close(fd) == -1) 400 atf_tc_fail_errno("close"); 401 if (val == 0 && rump_sys_unlink(name) == -1) 402 atf_tc_fail_errno("unlink"); 403 404 free(name); 405 406 rump_sys_chdir("/"); 407 } 408 409 static void 410 rename_nametoolong(const atf_tc_t *tc, const char *mp) 411 { 412 char *name; 413 int res, fd; 414 long val; 415 size_t len; 416 417 if (FSTYPE_RUMPFS(tc)) 418 atf_tc_skip("rename not supported by fs"); 419 420 if (rump_sys_chdir(mp) == -1) 421 atf_tc_fail_errno("chdir mountpoint"); 422 423 val = rump_sys_pathconf(".", _PC_NAME_MAX); 424 if (val == -1) 425 atf_tc_fail_errno("pathconf"); 426 427 len = val + 1; 428 name = malloc(len+1); 429 if (name == NULL) 430 atf_tc_fail_errno("malloc"); 431 432 memset(name, 'a', len); 433 *(name+len) = '\0'; 434 435 fd = rump_sys_open("dummy", O_RDWR|O_CREAT, 0666); 436 if (fd == -1) 437 atf_tc_fail_errno("open"); 438 if (rump_sys_close(fd) == -1) 439 atf_tc_fail_errno("close"); 440 441 val = rump_sys_pathconf(".", _PC_NO_TRUNC); 442 if (val == -1) 443 atf_tc_fail_errno("pathconf"); 444 445 if (FSTYPE_MSDOS(tc)) 446 atf_tc_expect_fail("PR kern/43670"); 447 res = rump_sys_rename("dummy", name); 448 if (val != 0 && (res != -1 || errno != ENAMETOOLONG)) 449 atf_tc_fail_errno("rename"); 450 451 if (val == 0 && rump_sys_unlink(name) == -1) 452 atf_tc_fail_errno("unlink"); 453 454 free(name); 455 456 rump_sys_chdir("/"); 457 } 458 459 static void 460 symlink_zerolen(const atf_tc_t *tc, const char *mp) 461 { 462 463 USES_SYMLINKS; 464 465 RL(rump_sys_chdir(mp)); 466 467 if (FSTYPE_TMPFS(tc)) { 468 atf_tc_expect_signal(SIGABRT, "PR kern/43843"); 469 } 470 471 RL(rump_sys_symlink("", "afile")); 472 RL(rump_sys_chdir("/")); 473 } 474 475 static void 476 attrs(const atf_tc_t *tc, const char *mp) 477 { 478 struct stat sb, sb2; 479 struct timeval tv[2]; 480 int fd; 481 482 FSTEST_ENTER(); 483 RL(fd = rump_sys_open(TESTFILE, O_RDWR | O_CREAT, 0755)); 484 RL(rump_sys_close(fd)); 485 RL(rump_sys_stat(TESTFILE, &sb)); 486 if (!(FSTYPE_MSDOS(tc) || FSTYPE_SYSVBFS(tc))) { 487 RL(rump_sys_chown(TESTFILE, 1, 2)); 488 sb.st_uid = 1; 489 sb.st_gid = 2; 490 RL(rump_sys_chmod(TESTFILE, 0123)); 491 sb.st_mode = (sb.st_mode & ~ACCESSPERMS) | 0123; 492 } 493 494 tv[0].tv_sec = 1000000000; /* need something >1980 for msdosfs */ 495 tv[0].tv_usec = 1; 496 tv[1].tv_sec = 1000000002; /* need even seconds for msdosfs */ 497 tv[1].tv_usec = 3; 498 RL(rump_sys_utimes(TESTFILE, tv)); 499 RL(rump_sys_utimes(TESTFILE, tv)); /* XXX: utimes & birthtime */ 500 sb.st_atimespec.tv_sec = 1000000000; 501 sb.st_atimespec.tv_nsec = 1000; 502 sb.st_mtimespec.tv_sec = 1000000002; 503 sb.st_mtimespec.tv_nsec = 3000; 504 505 RL(rump_sys_stat(TESTFILE, &sb2)); 506 #define CHECK(a) ATF_REQUIRE_EQ(sb.a, sb2.a) 507 if (!(FSTYPE_MSDOS(tc) || FSTYPE_SYSVBFS(tc))) { 508 CHECK(st_uid); 509 CHECK(st_gid); 510 CHECK(st_mode); 511 } 512 if (!FSTYPE_MSDOS(tc)) { 513 /* msdosfs has only access date, not time */ 514 CHECK(st_atimespec.tv_sec); 515 } 516 CHECK(st_mtimespec.tv_sec); 517 if (!(FSTYPE_EXT2FS(tc) || FSTYPE_MSDOS(tc) || FSTYPE_SYSVBFS(tc))) { 518 CHECK(st_atimespec.tv_nsec); 519 CHECK(st_mtimespec.tv_nsec); 520 } 521 #undef CHECK 522 523 FSTEST_EXIT(); 524 } 525 526 static void 527 fcntl_lock(const atf_tc_t *tc, const char *mp) 528 { 529 int fd, fd2; 530 struct flock l; 531 struct lwp *lwp1, *lwp2; 532 533 FSTEST_ENTER(); 534 l.l_pid = 0; 535 l.l_start = l.l_len = 1024; 536 l.l_type = F_RDLCK | F_WRLCK; 537 l.l_whence = SEEK_END; 538 539 lwp1 = rump_pub_lwproc_curlwp(); 540 RL(fd = rump_sys_open(TESTFILE, O_RDWR | O_CREAT, 0755)); 541 RL(rump_sys_ftruncate(fd, 8192)); 542 543 /* PR kern/43321 */ 544 RL(rump_sys_fcntl(fd, F_SETLK, &l)); 545 546 /* Next, we fork and try to lock the same area */ 547 RZ(rump_pub_lwproc_rfork(RUMP_RFCFDG)); 548 lwp2 = rump_pub_lwproc_curlwp(); 549 RL(fd2 = rump_sys_open(TESTFILE, O_RDWR, 0)); 550 ATF_REQUIRE_ERRNO(EAGAIN, rump_sys_fcntl(fd2, F_SETLK, &l)); 551 552 /* Switch back and unlock... */ 553 rump_pub_lwproc_switch(lwp1); 554 l.l_type = F_UNLCK; 555 RL(rump_sys_fcntl(fd, F_SETLK, &l)); 556 557 /* ... and try to lock again */ 558 rump_pub_lwproc_switch(lwp2); 559 l.l_type = F_RDLCK | F_WRLCK; 560 RL(rump_sys_fcntl(fd2, F_SETLK, &l)); 561 562 RL(rump_sys_close(fd2)); 563 rump_pub_lwproc_releaselwp(); 564 565 RL(rump_sys_close(fd)); 566 567 FSTEST_EXIT(); 568 } 569 570 ATF_TC_FSAPPLY(lookup_simple, "simple lookup (./.. on root)"); 571 ATF_TC_FSAPPLY(lookup_complex, "lookup of non-dot entries"); 572 ATF_TC_FSAPPLY(dir_simple, "mkdir/rmdir"); 573 ATF_TC_FSAPPLY(dir_notempty, "non-empty directories cannot be removed"); 574 ATF_TC_FSAPPLY(rename_dir, "exercise various directory renaming ops"); 575 ATF_TC_FSAPPLY(rename_dotdot, "rename dir .."); 576 ATF_TC_FSAPPLY(rename_reg_nodir, "rename regular files, no subdirectories"); 577 ATF_TC_FSAPPLY(create_nametoolong, "create file with name too long"); 578 ATF_TC_FSAPPLY(rename_nametoolong, "rename to file with name too long"); 579 ATF_TC_FSAPPLY(symlink_zerolen, "symlink with 0-len target"); 580 ATF_TC_FSAPPLY(attrs, "check setting attributes works"); 581 ATF_TC_FSAPPLY(fcntl_lock, "check fcntl F_SETLK"); 582 583 ATF_TP_ADD_TCS(tp) 584 { 585 586 ATF_TP_FSAPPLY(lookup_simple); 587 ATF_TP_FSAPPLY(lookup_complex); 588 ATF_TP_FSAPPLY(dir_simple); 589 ATF_TP_FSAPPLY(dir_notempty); 590 ATF_TP_FSAPPLY(rename_dir); 591 ATF_TP_FSAPPLY(rename_dotdot); 592 ATF_TP_FSAPPLY(rename_reg_nodir); 593 ATF_TP_FSAPPLY(create_nametoolong); 594 ATF_TP_FSAPPLY(rename_nametoolong); 595 ATF_TP_FSAPPLY(symlink_zerolen); 596 ATF_TP_FSAPPLY(attrs); 597 ATF_TP_FSAPPLY(fcntl_lock); 598 599 return atf_no_error(); 600 } 601