t_vnops.c revision 1.12 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