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