xref: /netbsd-src/tests/fs/vfs/t_vnops.c (revision 4817a0b0b8fe9612e8ebe21a9bf2d97b95038a97)
1 /*	$NetBSD: t_vnops.c,v 1.10 2010/11/11 17:44:44 pooka 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 USES_DIRS \
45     if (FSTYPE_SYSVBFS(tc)) atf_tc_skip("dirs not supported by file system")
46 
47 #define USES_SYMLINKS					\
48     if (FSTYPE_SYSVBFS(tc) || FSTYPE_MSDOS(tc))		\
49 	atf_tc_skip("symlinks not supported by file system")
50 
51 static char *
52 md(char *buf, const char *base, const char *tail)
53 {
54 
55 	sprintf(buf, "%s/%s", base, tail);
56 	return buf;
57 }
58 
59 static void
60 lookup_simple(const atf_tc_t *tc, const char *mountpath)
61 {
62 	char pb[MAXPATHLEN], final[MAXPATHLEN];
63 	struct stat sb1, sb2;
64 
65 	strcpy(final, mountpath);
66 	sprintf(pb, "%s/../%s", mountpath, basename(final));
67 	if (rump_sys_stat(pb, &sb1) == -1)
68 		atf_tc_fail_errno("stat 1");
69 
70 	sprintf(pb, "%s/./../%s", mountpath, basename(final));
71 	if (rump_sys_stat(pb, &sb2) == -1)
72 		atf_tc_fail_errno("stat 2");
73 
74 	ATF_REQUIRE(memcmp(&sb1, &sb2, sizeof(sb1)) == 0);
75 }
76 
77 static void
78 lookup_complex(const atf_tc_t *tc, const char *mountpath)
79 {
80 	char pb[MAXPATHLEN];
81 	struct stat sb1, sb2;
82 
83 	USES_DIRS;
84 
85 	sprintf(pb, "%s/dir", mountpath);
86 	if (rump_sys_mkdir(pb, 0777) == -1)
87 		atf_tc_fail_errno("mkdir");
88 	if (rump_sys_stat(pb, &sb1) == -1)
89 		atf_tc_fail_errno("stat 1");
90 
91 	sprintf(pb, "%s/./dir/../././dir/.", mountpath);
92 	if (rump_sys_stat(pb, &sb2) == -1)
93 		atf_tc_fail_errno("stat 2");
94 
95 	ATF_REQUIRE(memcmp(&sb1, &sb2, sizeof(sb1)) == 0);
96 }
97 
98 static void
99 dir_simple(const atf_tc_t *tc, const char *mountpath)
100 {
101 	char pb[MAXPATHLEN];
102 	struct stat sb;
103 
104 	USES_DIRS;
105 
106 	/* check we can create directories */
107 	sprintf(pb, "%s/dir", mountpath);
108 	if (rump_sys_mkdir(pb, 0777) == -1)
109 		atf_tc_fail_errno("mkdir");
110 	if (rump_sys_stat(pb, &sb) == -1)
111 		atf_tc_fail_errno("stat new directory");
112 
113 	/* check we can remove then and that it makes them unreachable */
114 	if (rump_sys_rmdir(pb) == -1)
115 		atf_tc_fail_errno("rmdir");
116 	if (rump_sys_stat(pb, &sb) != -1 || errno != ENOENT)
117 		atf_tc_fail("ENOENT expected from stat");
118 }
119 
120 static void
121 dir_notempty(const atf_tc_t *tc, const char *mountpath)
122 {
123 	char pb[MAXPATHLEN], pb2[MAXPATHLEN];
124 	int fd, rv;
125 
126 	USES_DIRS;
127 
128 	/* check we can create directories */
129 	sprintf(pb, "%s/dir", mountpath);
130 	if (rump_sys_mkdir(pb, 0777) == -1)
131 		atf_tc_fail_errno("mkdir");
132 
133 	sprintf(pb2, "%s/dir/file", mountpath);
134 	fd = rump_sys_open(pb2, O_RDWR | O_CREAT, 0777);
135 	if (fd == -1)
136 		atf_tc_fail_errno("create file");
137 	rump_sys_close(fd);
138 
139 	rv = rump_sys_rmdir(pb);
140 	if (rv != -1 || errno != ENOTEMPTY)
141 		atf_tc_fail("non-empty directory removed succesfully");
142 
143 	if (rump_sys_unlink(pb2) == -1)
144 		atf_tc_fail_errno("cannot remove dir/file");
145 
146 	if (rump_sys_rmdir(pb) == -1)
147 		atf_tc_fail_errno("remove directory");
148 }
149 
150 static void
151 checkfile(const char *path, struct stat *refp)
152 {
153 	char buf[MAXPATHLEN];
154 	struct stat sb;
155 	static int n = 1;
156 
157 	md(buf, path, "file");
158 	if (rump_sys_stat(buf, &sb) == -1)
159 		atf_tc_fail_errno("cannot stat file %d (%s)", n, buf);
160 	if (memcmp(&sb, refp, sizeof(sb)) != 0)
161 		atf_tc_fail("stat mismatch %d", n);
162 	n++;
163 }
164 
165 static void
166 rename_dir(const atf_tc_t *tc, const char *mp)
167 {
168 	char pb1[MAXPATHLEN], pb2[MAXPATHLEN], pb3[MAXPATHLEN];
169 	struct stat ref;
170 
171 	if (FSTYPE_MSDOS(tc))
172 		atf_tc_skip("test fails in some setups, reason unknown");
173 
174 	if (FSTYPE_RUMPFS(tc))
175 		atf_tc_skip("rename not supported by fs");
176 
177 	USES_DIRS;
178 
179 	md(pb1, mp, "dir1");
180 	if (rump_sys_mkdir(pb1, 0777) == -1)
181 		atf_tc_fail_errno("mkdir 1");
182 
183 	md(pb2, mp, "dir2");
184 	if (rump_sys_mkdir(pb2, 0777) == -1)
185 		atf_tc_fail_errno("mkdir 2");
186 	md(pb2, mp, "dir2/subdir");
187 	if (rump_sys_mkdir(pb2, 0777) == -1)
188 		atf_tc_fail_errno("mkdir 3");
189 
190 	md(pb3, mp, "dir1/file");
191 	if (rump_sys_mknod(pb3, S_IFREG | 0777, -1) == -1)
192 		atf_tc_fail_errno("create file");
193 	if (rump_sys_stat(pb3, &ref) == -1)
194 		atf_tc_fail_errno("stat of file");
195 
196 	/*
197 	 * First try ops which should succeed.
198 	 */
199 
200 	/* rename within directory */
201 	md(pb3, mp, "dir3");
202 	if (rump_sys_rename(pb1, pb3) == -1)
203 		atf_tc_fail_errno("rename 1");
204 	checkfile(pb3, &ref);
205 
206 	/* rename directory onto itself (two ways, should fail) */
207 	md(pb1, mp, "dir3/.");
208 	if (rump_sys_rename(pb1, pb3) != -1 || errno != EINVAL)
209 		atf_tc_fail_errno("rename 2");
210 	if (rump_sys_rename(pb3, pb1) != -1 || errno != EISDIR)
211 		atf_tc_fail_errno("rename 3");
212 
213 	checkfile(pb3, &ref);
214 
215 	/* rename father of directory into directory */
216 	md(pb1, mp, "dir2/dir");
217 	md(pb2, mp, "dir2");
218 	if (rump_sys_rename(pb2, pb1) != -1 || errno != EINVAL)
219 		atf_tc_fail_errno("rename 4");
220 
221 	/* same for grandfather */
222 	md(pb1, mp, "dir2/subdir/dir2");
223 	if (rump_sys_rename(pb2, pb1) != -1 || errno != EINVAL)
224 		atf_tc_fail("rename 5");
225 
226 	checkfile(pb3, &ref);
227 
228 	/* rename directory over a non-empty directory */
229 	if (rump_sys_rename(pb2, pb3) != -1 || errno != ENOTEMPTY)
230 		atf_tc_fail("rename 6");
231 
232 	/* cross-directory rename */
233 	md(pb1, mp, "dir3");
234 	md(pb2, mp, "dir2/somedir");
235 	if (rump_sys_rename(pb1, pb2) == -1)
236 		atf_tc_fail_errno("rename 7");
237 	checkfile(pb2, &ref);
238 
239 	/* move to parent directory */
240 	md(pb1, mp, "dir2/somedir/../../dir3");
241 	if (rump_sys_rename(pb2, pb1) == -1)
242 		atf_tc_fail_errno("rename 8");
243 	md(pb1, mp, "dir2/../dir3");
244 	checkfile(pb1, &ref);
245 
246 	/* finally, atomic cross-directory rename */
247 	md(pb3, mp, "dir2/subdir");
248 	if (rump_sys_rename(pb1, pb3) == -1)
249 		atf_tc_fail_errno("rename 9");
250 	checkfile(pb3, &ref);
251 }
252 
253 static void
254 rename_dotdot(const atf_tc_t *tc, const char *mp)
255 {
256 
257 	if (FSTYPE_RUMPFS(tc))
258 		atf_tc_skip("rename not supported by fs");
259 
260 	USES_DIRS;
261 
262 	if (rump_sys_chdir(mp) == -1)
263 		atf_tc_fail_errno("chdir mountpoint");
264 
265 	if (rump_sys_mkdir("dir1", 0777) == -1)
266 		atf_tc_fail_errno("mkdir 1");
267 	if (rump_sys_mkdir("dir2", 0777) == -1)
268 		atf_tc_fail_errno("mkdir 2");
269 
270 	if (rump_sys_rename("dir1", "dir1/..") != -1 || errno != EINVAL)
271 		atf_tc_fail_errno("self-dotdot to");
272 
273 	if (rump_sys_rename("dir1/..", "sometarget") != -1 || errno != EINVAL)
274 		atf_tc_fail_errno("self-dotdot from");
275 	atf_tc_expect_pass();
276 
277 	if (FSTYPE_TMPFS(tc)) {
278 		atf_tc_expect_fail("PR kern/43617");
279 	}
280 	if (rump_sys_rename("dir1", "dir2/..") != -1 || errno != EINVAL)
281 		atf_tc_fail("other-dotdot");
282 
283 	rump_sys_chdir("/");
284 }
285 
286 static void
287 rename_reg_nodir(const atf_tc_t *tc, const char *mp)
288 {
289 	bool haslinks;
290 	struct stat sb;
291 	ino_t f1ino, f2ino;
292 
293 	if (FSTYPE_RUMPFS(tc))
294 		atf_tc_skip("rename not supported by fs");
295 
296 	if (FSTYPE_MSDOS(tc))
297 		atf_tc_skip("test fails in some setups, reason unknown");
298 
299 	if (rump_sys_chdir(mp) == -1)
300 		atf_tc_fail_errno("chdir mountpoint");
301 
302 	if (FSTYPE_MSDOS(tc) || FSTYPE_SYSVBFS(tc))
303 		haslinks = false;
304 	else
305 		haslinks = true;
306 
307 	if (rump_sys_mknod("file1", S_IFREG | 0777, -1) == -1)
308 		atf_tc_fail_errno("create file");
309 	if (rump_sys_mknod("file2", S_IFREG | 0777, -1) == -1)
310 		atf_tc_fail_errno("create file");
311 
312 	if (rump_sys_stat("file1", &sb) == -1)
313 		atf_tc_fail_errno("stat");
314 	f1ino = sb.st_ino;
315 
316 	if (haslinks) {
317 		if (rump_sys_link("file1", "file_link") == -1)
318 			atf_tc_fail_errno("link");
319 		if (rump_sys_stat("file_link", &sb) == -1)
320 			atf_tc_fail_errno("stat");
321 		ATF_REQUIRE_EQ(sb.st_ino, f1ino);
322 		ATF_REQUIRE_EQ(sb.st_nlink, 2);
323 	}
324 
325 	if (rump_sys_stat("file2", &sb) == -1)
326 		atf_tc_fail_errno("stat");
327 	f2ino = sb.st_ino;
328 
329 	if (rump_sys_rename("file1", "file3") == -1)
330 		atf_tc_fail_errno("rename 1");
331 	if (rump_sys_stat("file3", &sb) == -1)
332 		atf_tc_fail_errno("stat 1");
333 	if (haslinks) {
334 		ATF_REQUIRE_EQ(sb.st_ino, f1ino);
335 	}
336 	if (rump_sys_stat("file1", &sb) != -1 || errno != ENOENT)
337 		atf_tc_fail_errno("source 1");
338 
339 	if (rump_sys_rename("file3", "file2") == -1)
340 		atf_tc_fail_errno("rename 2");
341 	if (rump_sys_stat("file2", &sb) == -1)
342 		atf_tc_fail_errno("stat 2");
343 	if (haslinks) {
344 		ATF_REQUIRE_EQ(sb.st_ino, f1ino);
345 	}
346 
347 	if (rump_sys_stat("file3", &sb) != -1 || errno != ENOENT)
348 		atf_tc_fail_errno("source 2");
349 
350 	if (haslinks) {
351 		if (rump_sys_rename("file2", "file_link") == -1)
352 			atf_tc_fail_errno("rename hardlink");
353 		if (rump_sys_stat("file2", &sb) != -1 || errno != ENOENT)
354 			atf_tc_fail_errno("source 3");
355 		if (rump_sys_stat("file_link", &sb) == -1)
356 			atf_tc_fail_errno("stat 2");
357 		ATF_REQUIRE_EQ(sb.st_ino, f1ino);
358 		ATF_REQUIRE_EQ(sb.st_nlink, 1);
359 	}
360 
361 	rump_sys_chdir("/");
362 }
363 
364 static void
365 create_nametoolong(const atf_tc_t *tc, const char *mp)
366 {
367 	char *name;
368 	int fd;
369 	long val;
370 	size_t len;
371 
372 	if (rump_sys_chdir(mp) == -1)
373 		atf_tc_fail_errno("chdir mountpoint");
374 
375 	val = rump_sys_pathconf(".", _PC_NAME_MAX);
376 	if (val == -1)
377 		atf_tc_fail_errno("pathconf");
378 
379 	len = val + 1;
380 	name = malloc(len+1);
381 	if (name == NULL)
382 		atf_tc_fail_errno("malloc");
383 
384 	memset(name, 'a', len);
385 	*(name+len) = '\0';
386 
387 	val = rump_sys_pathconf(".", _PC_NO_TRUNC);
388 	if (val == -1)
389 		atf_tc_fail_errno("pathconf");
390 
391 	if (FSTYPE_MSDOS(tc))
392 		atf_tc_expect_fail("PR kern/43670");
393 	fd = rump_sys_open(name, O_RDWR|O_CREAT, 0666);
394 	if (val != 0 && (fd != -1 || errno != ENAMETOOLONG))
395 		atf_tc_fail_errno("open");
396 
397 	if (val == 0 && rump_sys_close(fd) == -1)
398 		atf_tc_fail_errno("close");
399 	if (val == 0 && rump_sys_unlink(name) == -1)
400 		atf_tc_fail_errno("unlink");
401 
402 	free(name);
403 
404 	rump_sys_chdir("/");
405 }
406 
407 static void
408 rename_nametoolong(const atf_tc_t *tc, const char *mp)
409 {
410 	char *name;
411 	int res, fd;
412 	long val;
413 	size_t len;
414 
415 	if (FSTYPE_RUMPFS(tc))
416 		atf_tc_skip("rename not supported by fs");
417 
418 	if (rump_sys_chdir(mp) == -1)
419 		atf_tc_fail_errno("chdir mountpoint");
420 
421 	val = rump_sys_pathconf(".", _PC_NAME_MAX);
422 	if (val == -1)
423 		atf_tc_fail_errno("pathconf");
424 
425 	len = val + 1;
426 	name = malloc(len+1);
427 	if (name == NULL)
428 		atf_tc_fail_errno("malloc");
429 
430 	memset(name, 'a', len);
431 	*(name+len) = '\0';
432 
433 	fd = rump_sys_open("dummy", O_RDWR|O_CREAT, 0666);
434 	if (fd == -1)
435 		atf_tc_fail_errno("open");
436 	if (rump_sys_close(fd) == -1)
437 		atf_tc_fail_errno("close");
438 
439 	val = rump_sys_pathconf(".", _PC_NO_TRUNC);
440 	if (val == -1)
441 		atf_tc_fail_errno("pathconf");
442 
443 	if (FSTYPE_MSDOS(tc))
444 		atf_tc_expect_fail("PR kern/43670");
445 	res = rump_sys_rename("dummy", name);
446 	if (val != 0 && (res != -1 || errno != ENAMETOOLONG))
447 		atf_tc_fail_errno("rename");
448 
449 	if (val == 0 && rump_sys_unlink(name) == -1)
450 		atf_tc_fail_errno("unlink");
451 
452 	free(name);
453 
454 	rump_sys_chdir("/");
455 }
456 
457 static void
458 symlink_zerolen(const atf_tc_t *tc, const char *mp)
459 {
460 
461 	USES_SYMLINKS;
462 
463 	RL(rump_sys_chdir(mp));
464 
465 	if (FSTYPE_TMPFS(tc)) {
466 		atf_tc_expect_signal(SIGABRT, "PR kern/43843");
467 	}
468 
469 	RL(rump_sys_symlink("", "afile"));
470 	RL(rump_sys_chdir("/"));
471 }
472 
473 ATF_TC_FSAPPLY(lookup_simple, "simple lookup (./.. on root)");
474 ATF_TC_FSAPPLY(lookup_complex, "lookup of non-dot entries");
475 ATF_TC_FSAPPLY(dir_simple, "mkdir/rmdir");
476 ATF_TC_FSAPPLY(dir_notempty, "non-empty directories cannot be removed");
477 ATF_TC_FSAPPLY(rename_dir, "exercise various directory renaming ops");
478 ATF_TC_FSAPPLY(rename_dotdot, "rename dir ..");
479 ATF_TC_FSAPPLY(rename_reg_nodir, "rename regular files, no subdirectories");
480 ATF_TC_FSAPPLY(create_nametoolong, "create file with name too long");
481 ATF_TC_FSAPPLY(rename_nametoolong, "rename to file with name too long");
482 ATF_TC_FSAPPLY(symlink_zerolen, "symlink with 0-len target");
483 
484 ATF_TP_ADD_TCS(tp)
485 {
486 
487 	ATF_TP_FSAPPLY(lookup_simple);
488 	ATF_TP_FSAPPLY(lookup_complex);
489 	ATF_TP_FSAPPLY(dir_simple);
490 	ATF_TP_FSAPPLY(dir_notempty);
491 	ATF_TP_FSAPPLY(rename_dir);
492 	ATF_TP_FSAPPLY(rename_dotdot);
493 	ATF_TP_FSAPPLY(rename_reg_nodir);
494 	ATF_TP_FSAPPLY(create_nametoolong);
495 	ATF_TP_FSAPPLY(rename_nametoolong);
496 	ATF_TP_FSAPPLY(symlink_zerolen);
497 
498 	return atf_no_error();
499 }
500