t_vnops.c revision 313498
1/*	$NetBSD: t_vnops.c,v 1.55 2016/01/28 10:10:09 martin 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 <assert.h>
33#include <atf-c.h>
34#include <ctype.h>
35#include <fcntl.h>
36#include <libgen.h>
37#include <stdlib.h>
38#include <string.h>
39#include <unistd.h>
40
41#include <rump/rump_syscalls.h>
42#include <rump/rump.h>
43
44#include "../common/h_fsmacros.h"
45#include "../../h_macros.h"
46
47#define TESTFILE "afile"
48
49#define USES_DIRS					\
50    if (FSTYPE_SYSVBFS(tc))				\
51	atf_tc_skip("directories not supported by file system")
52
53#define USES_SYMLINKS					\
54    if (FSTYPE_SYSVBFS(tc) || FSTYPE_MSDOS(tc))		\
55	atf_tc_skip("symlinks not supported by file system")
56
57static char *
58md(char *buf, const char *base, const char *tail)
59{
60
61	sprintf(buf, "%s/%s", base, tail);
62	return buf;
63}
64
65static void
66lookup_simple(const atf_tc_t *tc, const char *mountpath)
67{
68	char pb[MAXPATHLEN], final[MAXPATHLEN];
69	struct stat sb1, sb2;
70
71	strcpy(final, mountpath);
72	sprintf(pb, "%s/../%s", mountpath, basename(final));
73	if (rump_sys_stat(pb, &sb1) == -1)
74		atf_tc_fail_errno("stat 1");
75
76	sprintf(pb, "%s/./../%s", mountpath, basename(final));
77	if (rump_sys_stat(pb, &sb2) == -1)
78		atf_tc_fail_errno("stat 2");
79
80	ATF_REQUIRE(memcmp(&sb1, &sb2, sizeof(sb1)) == 0);
81}
82
83static void
84lookup_complex(const atf_tc_t *tc, const char *mountpath)
85{
86	char pb[MAXPATHLEN];
87	struct stat sb1, sb2;
88
89	USES_DIRS;
90
91	if (FSTYPE_UDF(tc))
92		atf_tc_expect_fail("PR kern/49033");
93
94	sprintf(pb, "%s/dir", mountpath);
95	if (rump_sys_mkdir(pb, 0777) == -1)
96		atf_tc_fail_errno("mkdir");
97	if (rump_sys_stat(pb, &sb1) == -1)
98		atf_tc_fail_errno("stat 1");
99
100	sprintf(pb, "%s/./dir/../././dir/.", mountpath);
101	if (rump_sys_stat(pb, &sb2) == -1)
102		atf_tc_fail_errno("stat 2");
103
104	if (memcmp(&sb1, &sb2, sizeof(sb1)) != 0) {
105		printf("what\tsb1\t\tsb2\n");
106
107#define FIELD(FN)	\
108		printf(#FN "\t%lld\t%lld\n", \
109		(long long)sb1.FN, (long long)sb2.FN)
110#define TIME(FN)	\
111		printf(#FN "\t%lld.%ld\t%lld.%ld\n", \
112		(long long)sb1.FN.tv_sec, sb1.FN.tv_nsec, \
113		(long long)sb2.FN.tv_sec, sb2.FN.tv_nsec)
114
115		FIELD(st_dev);
116		FIELD(st_mode);
117		FIELD(st_ino);
118		FIELD(st_nlink);
119		FIELD(st_uid);
120		FIELD(st_gid);
121		FIELD(st_rdev);
122		TIME(st_atimespec);
123		TIME(st_mtimespec);
124		TIME(st_ctimespec);
125		TIME(st_birthtimespec);
126		FIELD(st_size);
127		FIELD(st_blocks);
128		FIELD(st_flags);
129		FIELD(st_gen);
130
131#undef FIELD
132#undef TIME
133
134		atf_tc_fail("stat results differ, see ouput for more details");
135	}
136	if (FSTYPE_UDF(tc))
137		atf_tc_fail("random failure of PR kern/49033 "
138			    "did not happen this time");
139}
140
141static void
142dir_simple(const atf_tc_t *tc, const char *mountpath)
143{
144	char pb[MAXPATHLEN];
145	struct stat sb;
146
147	USES_DIRS;
148
149	/* check we can create directories */
150	sprintf(pb, "%s/dir", mountpath);
151	if (rump_sys_mkdir(pb, 0777) == -1)
152		atf_tc_fail_errno("mkdir");
153	if (rump_sys_stat(pb, &sb) == -1)
154		atf_tc_fail_errno("stat new directory");
155
156	/* check we can remove then and that it makes them unreachable */
157	if (rump_sys_rmdir(pb) == -1)
158		atf_tc_fail_errno("rmdir");
159	if (rump_sys_stat(pb, &sb) != -1 || errno != ENOENT)
160		atf_tc_fail("ENOENT expected from stat");
161}
162
163static void
164dir_notempty(const atf_tc_t *tc, const char *mountpath)
165{
166	char pb[MAXPATHLEN], pb2[MAXPATHLEN];
167	int fd, rv;
168
169	USES_DIRS;
170
171	/* check we can create directories */
172	sprintf(pb, "%s/dir", mountpath);
173	if (rump_sys_mkdir(pb, 0777) == -1)
174		atf_tc_fail_errno("mkdir");
175
176	sprintf(pb2, "%s/dir/file", mountpath);
177	fd = rump_sys_open(pb2, O_RDWR | O_CREAT, 0777);
178	if (fd == -1)
179		atf_tc_fail_errno("create file");
180	rump_sys_close(fd);
181
182	rv = rump_sys_rmdir(pb);
183	if (rv != -1 || errno != ENOTEMPTY)
184		atf_tc_fail("non-empty directory removed succesfully");
185
186	if (rump_sys_unlink(pb2) == -1)
187		atf_tc_fail_errno("cannot remove dir/file");
188
189	if (rump_sys_rmdir(pb) == -1)
190		atf_tc_fail_errno("remove directory");
191}
192
193static void
194dir_rmdirdotdot(const atf_tc_t *tc, const char *mp)
195{
196	char pb[MAXPATHLEN];
197	int xerrno;
198
199	USES_DIRS;
200
201	FSTEST_ENTER();
202	RL(rump_sys_mkdir("test", 0777));
203	RL(rump_sys_chdir("test"));
204
205	RL(rump_sys_mkdir("subtest", 0777));
206	RL(rump_sys_chdir("subtest"));
207
208	md(pb, mp, "test/subtest");
209	RL(rump_sys_rmdir(pb));
210	md(pb, mp, "test");
211	RL(rump_sys_rmdir(pb));
212
213	if (FSTYPE_NFS(tc))
214		xerrno = ESTALE;
215	else
216		xerrno = ENOENT;
217	ATF_REQUIRE_ERRNO(xerrno, rump_sys_chdir("..") == -1);
218	FSTEST_EXIT();
219}
220
221static void
222checkfile(const char *path, struct stat *refp)
223{
224	char buf[MAXPATHLEN];
225	struct stat sb;
226	static int n = 1;
227
228	md(buf, path, "file");
229	if (rump_sys_stat(buf, &sb) == -1)
230		atf_tc_fail_errno("cannot stat file %d (%s)", n, buf);
231	if (memcmp(&sb, refp, sizeof(sb)) != 0)
232		atf_tc_fail("stat mismatch %d", n);
233	n++;
234}
235
236static void
237rename_dir(const atf_tc_t *tc, const char *mp)
238{
239	char pb1[MAXPATHLEN], pb2[MAXPATHLEN], pb3[MAXPATHLEN];
240	struct stat ref, sb;
241
242	if (FSTYPE_RUMPFS(tc))
243		atf_tc_skip("rename not supported by file system");
244
245	USES_DIRS;
246
247	md(pb1, mp, "dir1");
248	if (rump_sys_mkdir(pb1, 0777) == -1)
249		atf_tc_fail_errno("mkdir 1");
250
251	md(pb2, mp, "dir2");
252	if (rump_sys_mkdir(pb2, 0777) == -1)
253		atf_tc_fail_errno("mkdir 2");
254	md(pb2, mp, "dir2/subdir");
255	if (rump_sys_mkdir(pb2, 0777) == -1)
256		atf_tc_fail_errno("mkdir 3");
257
258	md(pb3, mp, "dir1/file");
259	if (rump_sys_mknod(pb3, S_IFREG | 0777, -1) == -1)
260		atf_tc_fail_errno("create file");
261	if (rump_sys_stat(pb3, &ref) == -1)
262		atf_tc_fail_errno("stat of file");
263
264	/*
265	 * First try ops which should succeed.
266	 */
267
268	/* rename within directory */
269	md(pb3, mp, "dir3");
270	if (rump_sys_rename(pb1, pb3) == -1)
271		atf_tc_fail_errno("rename 1");
272	checkfile(pb3, &ref);
273
274	/* rename directory onto itself (two ways, should fail) */
275	md(pb1, mp, "dir3/.");
276	if (rump_sys_rename(pb1, pb3) != -1 || errno != EINVAL)
277		atf_tc_fail_errno("rename 2");
278	if (rump_sys_rename(pb3, pb1) != -1 || errno != EISDIR)
279		atf_tc_fail_errno("rename 3");
280
281	checkfile(pb3, &ref);
282
283	/* rename father of directory into directory */
284	md(pb1, mp, "dir2/dir");
285	md(pb2, mp, "dir2");
286	if (rump_sys_rename(pb2, pb1) != -1 || errno != EINVAL)
287		atf_tc_fail_errno("rename 4");
288
289	/* same for grandfather */
290	md(pb1, mp, "dir2/subdir/dir2");
291	if (rump_sys_rename(pb2, pb1) != -1 || errno != EINVAL)
292		atf_tc_fail("rename 5");
293
294	checkfile(pb3, &ref);
295
296	/* rename directory over a non-empty directory */
297	if (rump_sys_rename(pb2, pb3) != -1 || errno != ENOTEMPTY)
298		atf_tc_fail("rename 6");
299
300	/* cross-directory rename */
301	md(pb1, mp, "dir3");
302	md(pb2, mp, "dir2/somedir");
303	if (rump_sys_rename(pb1, pb2) == -1)
304		atf_tc_fail_errno("rename 7");
305	checkfile(pb2, &ref);
306
307	/* move to parent directory */
308	md(pb1, mp, "dir2/somedir/../../dir3");
309	if (rump_sys_rename(pb2, pb1) == -1)
310		atf_tc_fail_errno("rename 8");
311	md(pb1, mp, "dir2/../dir3");
312	checkfile(pb1, &ref);
313
314	/* atomic cross-directory rename */
315	md(pb3, mp, "dir2/subdir");
316	if (rump_sys_rename(pb1, pb3) == -1)
317		atf_tc_fail_errno("rename 9");
318	checkfile(pb3, &ref);
319
320	/* rename directory over an empty directory */
321	md(pb1, mp, "parent");
322	md(pb2, mp, "parent/dir1");
323	md(pb3, mp, "parent/dir2");
324	RL(rump_sys_mkdir(pb1, 0777));
325	RL(rump_sys_mkdir(pb2, 0777));
326	RL(rump_sys_mkdir(pb3, 0777));
327	RL(rump_sys_rename(pb2, pb3));
328
329	RL(rump_sys_stat(pb1, &sb));
330	if (! FSTYPE_MSDOS(tc))
331		ATF_CHECK_EQ(sb.st_nlink, 3);
332	RL(rump_sys_rmdir(pb3));
333	RL(rump_sys_rmdir(pb1));
334}
335
336static void
337rename_dotdot(const atf_tc_t *tc, const char *mp)
338{
339
340	if (FSTYPE_RUMPFS(tc))
341		atf_tc_skip("rename not supported by file system");
342
343	USES_DIRS;
344
345	if (rump_sys_chdir(mp) == -1)
346		atf_tc_fail_errno("chdir mountpoint");
347
348	if (rump_sys_mkdir("dir1", 0777) == -1)
349		atf_tc_fail_errno("mkdir 1");
350	if (rump_sys_mkdir("dir2", 0777) == -1)
351		atf_tc_fail_errno("mkdir 2");
352
353	if (rump_sys_rename("dir1", "dir1/..") != -1 || errno != EINVAL)
354		atf_tc_fail_errno("self-dotdot to");
355
356	if (rump_sys_rename("dir1/..", "sometarget") != -1 || errno != EINVAL)
357		atf_tc_fail_errno("self-dotdot from");
358
359	if (rump_sys_rename("dir1", "dir2/..") != -1 || errno != EINVAL)
360		atf_tc_fail("other-dotdot");
361
362	rump_sys_chdir("/");
363}
364
365static void
366rename_reg_nodir(const atf_tc_t *tc, const char *mp)
367{
368	bool haslinks;
369	struct stat sb;
370	ino_t f1ino;
371
372	if (FSTYPE_RUMPFS(tc))
373		atf_tc_skip("rename not supported by file system");
374
375	if (rump_sys_chdir(mp) == -1)
376		atf_tc_fail_errno("chdir mountpoint");
377
378	if (FSTYPE_MSDOS(tc) || FSTYPE_SYSVBFS(tc))
379		haslinks = false;
380	else
381		haslinks = true;
382
383	if (rump_sys_mknod("file1", S_IFREG | 0777, -1) == -1)
384		atf_tc_fail_errno("create file");
385	if (rump_sys_mknod("file2", S_IFREG | 0777, -1) == -1)
386		atf_tc_fail_errno("create file");
387
388	if (rump_sys_stat("file1", &sb) == -1)
389		atf_tc_fail_errno("stat");
390	f1ino = sb.st_ino;
391
392	if (haslinks) {
393		if (rump_sys_link("file1", "file_link") == -1)
394			atf_tc_fail_errno("link");
395		if (rump_sys_stat("file_link", &sb) == -1)
396			atf_tc_fail_errno("stat");
397		ATF_REQUIRE_EQ(sb.st_ino, f1ino);
398		ATF_REQUIRE_EQ(sb.st_nlink, 2);
399	}
400
401	if (rump_sys_stat("file2", &sb) == -1)
402		atf_tc_fail_errno("stat");
403
404	if (rump_sys_rename("file1", "file3") == -1)
405		atf_tc_fail_errno("rename 1");
406	if (rump_sys_stat("file3", &sb) == -1)
407		atf_tc_fail_errno("stat 1");
408	if (haslinks) {
409		ATF_REQUIRE_EQ(sb.st_ino, f1ino);
410	}
411	if (rump_sys_stat("file1", &sb) != -1 || errno != ENOENT)
412		atf_tc_fail_errno("source 1");
413
414	if (rump_sys_rename("file3", "file2") == -1)
415		atf_tc_fail_errno("rename 2");
416	if (rump_sys_stat("file2", &sb) == -1)
417		atf_tc_fail_errno("stat 2");
418	if (haslinks) {
419		ATF_REQUIRE_EQ(sb.st_ino, f1ino);
420	}
421
422	if (rump_sys_stat("file3", &sb) != -1 || errno != ENOENT)
423		atf_tc_fail_errno("source 2");
424
425	if (haslinks) {
426		if (rump_sys_rename("file2", "file_link") == -1)
427			atf_tc_fail_errno("rename hardlink");
428		if (rump_sys_stat("file2", &sb) != -1 || errno != ENOENT)
429			atf_tc_fail_errno("source 3");
430		if (rump_sys_stat("file_link", &sb) == -1)
431			atf_tc_fail_errno("stat 2");
432		ATF_REQUIRE_EQ(sb.st_ino, f1ino);
433		ATF_REQUIRE_EQ(sb.st_nlink, 1);
434	}
435
436	ATF_CHECK_ERRNO(EFAULT, rump_sys_rename("file2", NULL) == -1);
437	ATF_CHECK_ERRNO(EFAULT, rump_sys_rename(NULL, "file2") == -1);
438
439	rump_sys_chdir("/");
440}
441
442/* PR kern/50607 */
443static void
444create_many(const atf_tc_t *tc, const char *mp)
445{
446	char buf[64];
447	int nfiles = 2324; /* #Nancy */
448	int i;
449
450	/* takes forever with many files */
451	if (FSTYPE_MSDOS(tc))
452		nfiles /= 4;
453
454	RL(rump_sys_chdir(mp));
455
456	if (FSTYPE_SYSVBFS(tc)) {
457		/* fs doesn't support many files or subdirectories */
458		nfiles = 5;
459	} else {
460		/* msdosfs doesn't like many entries in the root directory */
461		RL(rump_sys_mkdir("subdir", 0777));
462		RL(rump_sys_chdir("subdir"));
463	}
464
465	/* create them */
466#define TESTFN "testfile"
467	for (i = 0; i < nfiles; i++) {
468		int fd;
469
470		sprintf(buf, TESTFN "%d", i);
471		RL(fd = rump_sys_open(buf, O_RDWR|O_CREAT|O_EXCL, 0666));
472		RL(rump_sys_close(fd));
473	}
474
475	/* wipe them out */
476	for (i = 0; i < nfiles; i++) {
477		sprintf(buf, TESTFN "%d", i);
478		RL(rump_sys_unlink(buf));
479	}
480#undef TESTFN
481
482	rump_sys_chdir("/");
483}
484
485/*
486 * Test creating files with one-character names using all possible
487 * character values.  Failures to create the file are ignored as the
488 * characters allowed in file names vary by file system, but at least
489 * we can check that the fs does not crash, and if the file is
490 * successfully created, unlinking it should also succeed.
491 */
492static void
493create_nonalphanum(const atf_tc_t *tc, const char *mp)
494{
495	char buf[64];
496	int i;
497
498	RL(rump_sys_chdir(mp));
499
500	for (i = 0; i < 256; i++) {
501		int fd;
502		sprintf(buf, "%c", i);
503		fd = rump_sys_open(buf, O_RDWR|O_CREAT|O_EXCL, 0666);
504		if (fd == -1)
505			continue;
506		RL(rump_sys_close(fd));
507		RL(rump_sys_unlink(buf));
508	}
509	printf("\n");
510
511	rump_sys_chdir("/");
512}
513
514static void
515create_nametoolong(const atf_tc_t *tc, const char *mp)
516{
517	char *name;
518	int fd;
519	long val;
520	size_t len;
521
522	if (rump_sys_chdir(mp) == -1)
523		atf_tc_fail_errno("chdir mountpoint");
524
525	val = rump_sys_pathconf(".", _PC_NAME_MAX);
526	if (val == -1)
527		atf_tc_fail_errno("pathconf");
528
529	len = val + 1;
530	name = malloc(len+1);
531	if (name == NULL)
532		atf_tc_fail_errno("malloc");
533
534	memset(name, 'a', len);
535	*(name+len) = '\0';
536
537	val = rump_sys_pathconf(".", _PC_NO_TRUNC);
538	if (val == -1)
539		atf_tc_fail_errno("pathconf");
540
541	fd = rump_sys_open(name, O_RDWR|O_CREAT, 0666);
542	if (val != 0 && (fd != -1 || errno != ENAMETOOLONG))
543		atf_tc_fail_errno("open");
544
545	if (val == 0 && rump_sys_close(fd) == -1)
546		atf_tc_fail_errno("close");
547	if (val == 0 && rump_sys_unlink(name) == -1)
548		atf_tc_fail_errno("unlink");
549
550	free(name);
551
552	rump_sys_chdir("/");
553}
554
555static void
556create_exist(const atf_tc_t *tc, const char *mp)
557{
558	const char *name = "hoge";
559	int fd;
560
561	RL(rump_sys_chdir(mp));
562	RL(fd = rump_sys_open(name, O_RDWR|O_CREAT|O_EXCL, 0666));
563	RL(rump_sys_close(fd));
564	RL(rump_sys_unlink(name));
565	RL(fd = rump_sys_open(name, O_RDWR|O_CREAT, 0666));
566	RL(rump_sys_close(fd));
567	RL(fd = rump_sys_open(name, O_RDWR|O_CREAT, 0666));
568	RL(rump_sys_close(fd));
569	ATF_REQUIRE_ERRNO(EEXIST,
570	    (fd = rump_sys_open(name, O_RDWR|O_CREAT|O_EXCL, 0666)));
571	RL(rump_sys_unlink(name));
572	RL(rump_sys_chdir("/"));
573}
574
575static void
576rename_nametoolong(const atf_tc_t *tc, const char *mp)
577{
578	char *name;
579	int res, fd;
580	long val;
581	size_t len;
582
583	if (FSTYPE_RUMPFS(tc))
584		atf_tc_skip("rename not supported by file system");
585
586	if (rump_sys_chdir(mp) == -1)
587		atf_tc_fail_errno("chdir mountpoint");
588
589	val = rump_sys_pathconf(".", _PC_NAME_MAX);
590	if (val == -1)
591		atf_tc_fail_errno("pathconf");
592
593	len = val + 1;
594	name = malloc(len+1);
595	if (name == NULL)
596		atf_tc_fail_errno("malloc");
597
598	memset(name, 'a', len);
599	*(name+len) = '\0';
600
601	fd = rump_sys_open("dummy", O_RDWR|O_CREAT, 0666);
602	if (fd == -1)
603		atf_tc_fail_errno("open");
604	if (rump_sys_close(fd) == -1)
605		atf_tc_fail_errno("close");
606
607	val = rump_sys_pathconf(".", _PC_NO_TRUNC);
608	if (val == -1)
609		atf_tc_fail_errno("pathconf");
610
611	res = rump_sys_rename("dummy", name);
612	if (val != 0 && (res != -1 || errno != ENAMETOOLONG))
613		atf_tc_fail_errno("rename");
614
615	if (val == 0 && rump_sys_unlink(name) == -1)
616		atf_tc_fail_errno("unlink");
617
618	free(name);
619
620	rump_sys_chdir("/");
621}
622
623/*
624 * Test creating a symlink whose length is "len" bytes, not including
625 * the terminating NUL.
626 */
627static void
628symlink_len(const atf_tc_t *tc, const char *mp, size_t len)
629{
630	char *buf;
631	int r;
632
633	USES_SYMLINKS;
634
635	RL(rump_sys_chdir(mp));
636
637	buf = malloc(len + 1);
638	ATF_REQUIRE(buf);
639	memset(buf, 'a', len);
640	buf[len] = '\0';
641	r = rump_sys_symlink(buf, "afile");
642	if (r == -1) {
643		ATF_REQUIRE_ERRNO(ENAMETOOLONG, r);
644	} else {
645		RL(rump_sys_unlink("afile"));
646	}
647	free(buf);
648
649	RL(rump_sys_chdir("/"));
650}
651
652static void
653symlink_zerolen(const atf_tc_t *tc, const char *mp)
654{
655	symlink_len(tc, mp, 0);
656}
657
658static void
659symlink_long(const atf_tc_t *tc, const char *mp)
660{
661	/*
662	 * Test lengths close to powers of two, as those are likely
663	 * to be edge cases.
664	 */
665	size_t len;
666	int fuzz;
667	for (len = 2; len <= 65536; len *= 2) {
668		for (fuzz = -1; fuzz <= 1; fuzz++) {
669			symlink_len(tc, mp, len + fuzz);
670		}
671	}
672}
673
674static void
675symlink_root(const atf_tc_t *tc, const char *mp)
676{
677
678	USES_SYMLINKS;
679
680	RL(rump_sys_chdir(mp));
681	RL(rump_sys_symlink("/", "foo"));
682	RL(rump_sys_chdir("foo"));
683}
684
685static void
686attrs(const atf_tc_t *tc, const char *mp)
687{
688	struct stat sb, sb2;
689	struct timeval tv[2];
690	int fd;
691
692	FSTEST_ENTER();
693	RL(fd = rump_sys_open(TESTFILE, O_RDWR | O_CREAT, 0755));
694	RL(rump_sys_close(fd));
695	RL(rump_sys_stat(TESTFILE, &sb));
696	if (!(FSTYPE_MSDOS(tc) || FSTYPE_SYSVBFS(tc))) {
697		RL(rump_sys_chown(TESTFILE, 1, 2));
698		sb.st_uid = 1;
699		sb.st_gid = 2;
700		RL(rump_sys_chmod(TESTFILE, 0123));
701		sb.st_mode = (sb.st_mode & ~ACCESSPERMS) | 0123;
702	}
703
704	tv[0].tv_sec = 1000000000; /* need something >1980 for msdosfs */
705	tv[0].tv_usec = 1;
706	tv[1].tv_sec = 1000000002; /* need even seconds for msdosfs */
707	tv[1].tv_usec = 3;
708	RL(rump_sys_utimes(TESTFILE, tv));
709	RL(rump_sys_utimes(TESTFILE, tv)); /* XXX: utimes & birthtime */
710	sb.st_atimespec.tv_sec = 1000000000;
711	sb.st_atimespec.tv_nsec = 1000;
712	sb.st_mtimespec.tv_sec = 1000000002;
713	sb.st_mtimespec.tv_nsec = 3000;
714
715	RL(rump_sys_stat(TESTFILE, &sb2));
716#define CHECK(a) ATF_REQUIRE_EQ(sb.a, sb2.a)
717	if (!(FSTYPE_MSDOS(tc) || FSTYPE_SYSVBFS(tc))) {
718		CHECK(st_uid);
719		CHECK(st_gid);
720		CHECK(st_mode);
721	}
722	if (!FSTYPE_MSDOS(tc)) {
723		/* msdosfs has only access date, not time */
724		CHECK(st_atimespec.tv_sec);
725	}
726	CHECK(st_mtimespec.tv_sec);
727	if (!(FSTYPE_EXT2FS(tc) || FSTYPE_MSDOS(tc) ||
728	      FSTYPE_SYSVBFS(tc) || FSTYPE_V7FS(tc))) {
729		CHECK(st_atimespec.tv_nsec);
730		CHECK(st_mtimespec.tv_nsec);
731	}
732#undef  CHECK
733
734	FSTEST_EXIT();
735}
736
737static void
738fcntl_lock(const atf_tc_t *tc, const char *mp)
739{
740	int fd, fd2;
741	struct flock l;
742	struct lwp *lwp1, *lwp2;
743
744	FSTEST_ENTER();
745	l.l_pid = 0;
746	l.l_start = l.l_len = 1024;
747	l.l_type = F_RDLCK | F_WRLCK;
748	l.l_whence = SEEK_END;
749
750	lwp1 = rump_pub_lwproc_curlwp();
751	RL(fd = rump_sys_open(TESTFILE, O_RDWR | O_CREAT, 0755));
752	RL(rump_sys_ftruncate(fd, 8192));
753
754	RL(rump_sys_fcntl(fd, F_SETLK, &l));
755
756	/* Next, we fork and try to lock the same area */
757	RZ(rump_pub_lwproc_rfork(RUMP_RFCFDG));
758	lwp2 = rump_pub_lwproc_curlwp();
759	RL(fd2 = rump_sys_open(TESTFILE, O_RDWR, 0));
760	ATF_REQUIRE_ERRNO(EAGAIN, rump_sys_fcntl(fd2, F_SETLK, &l));
761
762	/* Switch back and unlock... */
763	rump_pub_lwproc_switch(lwp1);
764	l.l_type = F_UNLCK;
765	RL(rump_sys_fcntl(fd, F_SETLK, &l));
766
767	/* ... and try to lock again */
768	rump_pub_lwproc_switch(lwp2);
769	l.l_type = F_RDLCK | F_WRLCK;
770	RL(rump_sys_fcntl(fd2, F_SETLK, &l));
771
772	RL(rump_sys_close(fd2));
773	rump_pub_lwproc_releaselwp();
774
775	RL(rump_sys_close(fd));
776
777	FSTEST_EXIT();
778}
779
780static int
781flock_compare(const void *p, const void *q)
782{
783	int a = ((const struct flock *)p)->l_start;
784	int b = ((const struct flock *)q)->l_start;
785	return a < b ? -1 : (a > b ? 1 : 0);
786}
787
788/*
789 * Find all locks set by fcntl_getlock_pids test
790 * using GETLK for a range [start, start+end], and,
791 * if there is a blocking lock, recursively find
792 * all locks to the left (toward the beginning of
793 * a file) and to the right of the lock.
794 * The function also understands "until end of file"
795 * convention when len==0.
796 */
797static unsigned int
798fcntl_getlocks(int fildes, off_t start, off_t len,
799    struct flock *lock, struct flock *end)
800{
801	unsigned int rv = 0;
802	const struct flock l = { start, len, 0, F_RDLCK, SEEK_SET };
803
804	if (lock == end)
805		return rv;
806
807	RL(rump_sys_fcntl(fildes, F_GETLK, &l));
808
809	if (l.l_type == F_UNLCK)
810		return rv;
811
812	*lock++ = l;
813	rv += 1;
814
815	ATF_REQUIRE(l.l_whence == SEEK_SET);
816
817	if (l.l_start > start) {
818		unsigned int n =
819		    fcntl_getlocks(fildes, start, l.l_start - start, lock, end);
820		rv += n;
821		lock += n;
822		if (lock == end)
823			return rv;
824	}
825
826	if (l.l_len == 0) /* does l spans until the end? */
827		return rv;
828
829	if (len == 0) /* are we looking for locks until the end? */ {
830		rv += fcntl_getlocks(fildes, l.l_start + l.l_len, len, lock, end);
831	} else if (l.l_start + l.l_len < start + len) {
832		len -= l.l_start + l.l_len - start;
833		rv += fcntl_getlocks(fildes, l.l_start + l.l_len, len, lock, end);
834	}
835
836	return rv;
837}
838
839static void
840fcntl_getlock_pids(const atf_tc_t *tc, const char *mp)
841{
842	/* test non-overlaping ranges */
843	struct flock expect[4];
844	const struct flock lock[4] = {
845		{ 0, 2, 0, F_WRLCK, SEEK_SET },
846		{ 2, 1, 0, F_WRLCK, SEEK_SET },
847		{ 7, 5, 0, F_WRLCK, SEEK_SET },
848		{ 4, 3, 0, F_WRLCK, SEEK_SET },
849	};
850
851    /* Add extra element to make sure recursion does't stop at array end */
852	struct flock result[5];
853
854	/* Add 5th process */
855	int fd[5];
856	pid_t pid[5];
857	struct lwp *lwp[5];
858
859	unsigned int i, j;
860	const off_t sz = 8192;
861	int omode  = 0755;
862	int oflags = O_RDWR | O_CREAT;
863
864	memcpy(expect, lock, sizeof(lock));
865
866	FSTEST_ENTER();
867
868	/*
869	 * First, we create 4 processes and let each lock a range of the
870	 * file.  Note that the third and fourth processes lock in
871	 * "reverse" order, i.e. the greater pid locks a range before
872	 * the lesser pid.
873	 * Then, we create 5th process which doesn't lock anything.
874	 */
875	for (i = 0; i < __arraycount(lwp); i++) {
876		RZ(rump_pub_lwproc_rfork(RUMP_RFCFDG));
877
878		lwp[i] = rump_pub_lwproc_curlwp();
879		pid[i] = rump_sys_getpid();
880
881		RL(fd[i] = rump_sys_open(TESTFILE, oflags, omode));
882		oflags = O_RDWR;
883		omode  = 0;
884
885		RL(rump_sys_ftruncate(fd[i], sz));
886
887		if (i < __arraycount(lock)) {
888			RL(rump_sys_fcntl(fd[i], F_SETLK, &lock[i]));
889			expect[i].l_pid = pid[i];
890		}
891	}
892
893	qsort(expect, __arraycount(expect), sizeof(expect[0]), &flock_compare);
894
895	/*
896	 * In the context of each process, recursively find all locks
897	 * that would block the current process. Processes 1-4 don't
898	 * see their own lock, we insert it to simplify checks.
899	 * Process 5 sees all 4 locks.
900	 */
901	for (i = 0; i < __arraycount(lwp); i++) {
902		unsigned int nlocks;
903
904		rump_pub_lwproc_switch(lwp[i]);
905
906		memset(result, 0, sizeof(result));
907		nlocks = fcntl_getlocks(fd[i], 0, sz,
908		    result, result + __arraycount(result));
909
910		if (i < __arraycount(lock)) {
911			ATF_REQUIRE(nlocks < __arraycount(result));
912			result[nlocks] = lock[i];
913			result[nlocks].l_pid = pid[i];
914			nlocks++;
915		}
916
917		ATF_CHECK_EQ(nlocks, __arraycount(expect));
918
919		qsort(result, nlocks, sizeof(result[0]), &flock_compare);
920
921		for (j = 0; j < nlocks; j++) {
922			ATF_CHECK_EQ(result[j].l_start,  expect[j].l_start );
923			ATF_CHECK_EQ(result[j].l_len,    expect[j].l_len   );
924			ATF_CHECK_EQ(result[j].l_pid,    expect[j].l_pid   );
925			ATF_CHECK_EQ(result[j].l_type,   expect[j].l_type  );
926			ATF_CHECK_EQ(result[j].l_whence, expect[j].l_whence);
927		}
928	}
929
930	/*
931	 * Release processes.  This also releases the fds and locks
932	 * making fs unmount possible
933	 */
934	for (i = 0; i < __arraycount(lwp); i++) {
935		rump_pub_lwproc_switch(lwp[i]);
936		rump_pub_lwproc_releaselwp();
937	}
938
939	FSTEST_EXIT();
940}
941
942static void
943access_simple(const atf_tc_t *tc, const char *mp)
944{
945	int fd;
946	int tmode;
947
948	FSTEST_ENTER();
949	RL(fd = rump_sys_open("tfile", O_CREAT | O_RDWR, 0777));
950	RL(rump_sys_close(fd));
951
952#define ALLACC (F_OK | X_OK | W_OK | R_OK)
953	if (FSTYPE_SYSVBFS(tc) || FSTYPE_MSDOS(tc))
954		tmode = F_OK;
955	else
956		tmode = ALLACC;
957
958	RL(rump_sys_access("tfile", tmode));
959
960	/* PR kern/44648 */
961	ATF_REQUIRE_ERRNO(EINVAL, rump_sys_access("tfile", ALLACC+1) == -1);
962#undef ALLACC
963	FSTEST_EXIT();
964}
965
966static void
967read_directory(const atf_tc_t *tc, const char *mp)
968{
969	char buf[1024];
970	int fd, res;
971	ssize_t size;
972
973	FSTEST_ENTER();
974	fd = rump_sys_open(".", O_DIRECTORY | O_RDONLY, 0777);
975	ATF_REQUIRE(fd != -1);
976
977	size = rump_sys_pread(fd, buf, sizeof(buf), 0);
978	ATF_CHECK(size != -1 || errno == EISDIR);
979	size = rump_sys_read(fd, buf, sizeof(buf));
980	ATF_CHECK(size != -1 || errno == EISDIR);
981
982	res = rump_sys_close(fd);
983	ATF_REQUIRE(res != -1);
984	FSTEST_EXIT();
985}
986
987static void
988lstat_symlink(const atf_tc_t *tc, const char *mp)
989{
990	const char *src, *dst;
991	int res;
992	struct stat st;
993
994	USES_SYMLINKS;
995
996	FSTEST_ENTER();
997
998	src = "source";
999	dst = "destination";
1000
1001	res = rump_sys_symlink(src, dst);
1002	ATF_REQUIRE(res != -1);
1003	res = rump_sys_lstat(dst, &st);
1004	ATF_REQUIRE(res != -1);
1005
1006	ATF_CHECK(S_ISLNK(st.st_mode) != 0);
1007	ATF_CHECK(st.st_size == (off_t)strlen(src));
1008
1009	FSTEST_EXIT();
1010}
1011
1012ATF_TC_FSAPPLY(lookup_simple, "simple lookup (./.. on root)");
1013ATF_TC_FSAPPLY(lookup_complex, "lookup of non-dot entries");
1014ATF_TC_FSAPPLY(dir_simple, "mkdir/rmdir");
1015ATF_TC_FSAPPLY(dir_notempty, "non-empty directories cannot be removed");
1016ATF_TC_FSAPPLY(dir_rmdirdotdot, "remove .. and try to cd out (PR kern/44657)");
1017ATF_TC_FSAPPLY(rename_dir, "exercise various directory renaming ops "
1018"(PR kern/44288)");
1019ATF_TC_FSAPPLY(rename_dotdot, "rename dir .. (PR kern/43617)");
1020ATF_TC_FSAPPLY(rename_reg_nodir, "rename regular files, no subdirectories");
1021ATF_TC_FSAPPLY(create_nametoolong, "create file with name too long");
1022ATF_TC_FSAPPLY(create_exist, "create with O_EXCL");
1023ATF_TC_FSAPPLY(rename_nametoolong, "rename to file with name too long");
1024ATF_TC_FSAPPLY(symlink_zerolen, "symlink with target of length 0");
1025ATF_TC_FSAPPLY(symlink_long, "symlink with target of length > 0");
1026ATF_TC_FSAPPLY(symlink_root, "symlink to root directory");
1027ATF_TC_FSAPPLY(attrs, "check setting attributes works");
1028ATF_TC_FSAPPLY(fcntl_lock, "check fcntl F_SETLK");
1029ATF_TC_FSAPPLY(fcntl_getlock_pids,"fcntl F_GETLK w/ many procs, PR kern/44494");
1030ATF_TC_FSAPPLY(access_simple, "access(2)");
1031ATF_TC_FSAPPLY(read_directory, "read(2) on directories");
1032ATF_TC_FSAPPLY(lstat_symlink, "lstat(2) values for symbolic links");
1033
1034#undef FSTEST_IMGSIZE
1035#define FSTEST_IMGSIZE (1024*1024*64)
1036ATF_TC_FSAPPLY(create_many, "create many directory entries");
1037ATF_TC_FSAPPLY(create_nonalphanum, "non-alphanumeric filenames");
1038
1039ATF_TP_ADD_TCS(tp)
1040{
1041
1042	ATF_TP_FSAPPLY(lookup_simple);
1043	ATF_TP_FSAPPLY(lookup_complex);
1044	ATF_TP_FSAPPLY(dir_simple);
1045	ATF_TP_FSAPPLY(dir_notempty);
1046	ATF_TP_FSAPPLY(dir_rmdirdotdot);
1047	ATF_TP_FSAPPLY(rename_dir);
1048	ATF_TP_FSAPPLY(rename_dotdot);
1049	ATF_TP_FSAPPLY(rename_reg_nodir);
1050	ATF_TP_FSAPPLY(create_many);
1051	ATF_TP_FSAPPLY(create_nonalphanum);
1052	ATF_TP_FSAPPLY(create_nametoolong);
1053	ATF_TP_FSAPPLY(create_exist);
1054	ATF_TP_FSAPPLY(rename_nametoolong);
1055	ATF_TP_FSAPPLY(symlink_zerolen);
1056	ATF_TP_FSAPPLY(symlink_long);
1057	ATF_TP_FSAPPLY(symlink_root);
1058	ATF_TP_FSAPPLY(attrs);
1059	ATF_TP_FSAPPLY(fcntl_lock);
1060	ATF_TP_FSAPPLY(fcntl_getlock_pids);
1061	ATF_TP_FSAPPLY(access_simple);
1062	ATF_TP_FSAPPLY(read_directory);
1063	ATF_TP_FSAPPLY(lstat_symlink);
1064
1065	return atf_no_error();
1066}
1067