xref: /netbsd-src/tests/fs/vfs/t_vnops.c (revision c2f76ff004a2cb67efe5b12d97bd3ef7fe89e18d)
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