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