1 // Copyright 2012 Google Inc. 2 // All rights reserved. 3 // 4 // Redistribution and use in source and binary forms, with or without 5 // modification, are permitted provided that the following conditions are 6 // met: 7 // 8 // * Redistributions of source code must retain the above copyright 9 // notice, this list of conditions and the following disclaimer. 10 // * Redistributions in binary form must reproduce the above copyright 11 // notice, this list of conditions and the following disclaimer in the 12 // documentation and/or other materials provided with the distribution. 13 // * Neither the name of Google Inc. nor the names of its contributors 14 // may be used to endorse or promote products derived from this software 15 // without specific prior written permission. 16 // 17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 29 #include "fs.h" 30 31 #include <sys/stat.h> 32 #include <sys/wait.h> 33 34 #include <assert.h> 35 #include <dirent.h> 36 #include <err.h> 37 #include <errno.h> 38 #include <signal.h> 39 #include <stdbool.h> 40 #include <stdio.h> 41 #include <stdlib.h> 42 #include <string.h> 43 #include <unistd.h> 44 45 #include <atf-c.h> 46 47 #include "error.h" 48 49 50 static void run_mount_tmpfs(const char*) KYUA_DEFS_NORETURN; 51 52 53 /// Operating systems recognized by the code below. 54 enum os_type { 55 os_unsupported = 0, 56 os_freebsd, 57 os_linux, 58 os_netbsd, 59 os_sunos, 60 }; 61 62 63 /// The current operating system. 64 static enum os_type current_os = 65 #if defined(__FreeBSD__) 66 os_freebsd 67 #elif defined(__linux__) 68 os_linux 69 #elif defined(__NetBSD__) 70 os_netbsd 71 #elif defined(__SunOS__) 72 os_sunos 73 #else 74 os_unsupported 75 #endif 76 ; 77 78 79 /// Checks if a directory entry exists and matches a specific type. 80 /// 81 /// \param dir The directory in which to look for the entry. 82 /// \param name The name of the entry to look up. 83 /// \param expected_type The expected type of the file as given by dir(5). 84 /// 85 /// \return True if the entry exists and matches the given type; false 86 /// otherwise. 87 static bool 88 lookup(const char* dir, const char* name, const int expected_type) 89 { 90 DIR* dirp = opendir(dir); 91 ATF_REQUIRE(dirp != NULL); 92 93 bool found = false; 94 struct dirent* dp; 95 while (!found && (dp = readdir(dirp)) != NULL) { 96 if (strcmp(dp->d_name, name) == 0 && 97 dp->d_type == expected_type) { 98 found = true; 99 } 100 } 101 closedir(dirp); 102 return found; 103 } 104 105 106 /// Executes 'mount -t tmpfs' (or a similar variant). 107 /// 108 /// This function must be called from a subprocess, as it never returns. 109 /// 110 /// \param mount_point Location on which to mount a tmpfs. 111 static void 112 run_mount_tmpfs(const char* mount_point) 113 { 114 const char* mount_args[16]; 115 116 size_t last = 0; 117 switch (current_os) { 118 case os_freebsd: 119 mount_args[last++] = "mdmfs"; 120 mount_args[last++] = "-s16m"; 121 mount_args[last++] = "md"; 122 mount_args[last++] = mount_point; 123 break; 124 125 case os_linux: 126 mount_args[last++] = "mount"; 127 mount_args[last++] = "-ttmpfs"; 128 mount_args[last++] = "tmpfs"; 129 mount_args[last++] = mount_point; 130 break; 131 132 case os_netbsd: 133 mount_args[last++] = "mount"; 134 mount_args[last++] = "-ttmpfs"; 135 mount_args[last++] = "tmpfs"; 136 mount_args[last++] = mount_point; 137 break; 138 139 case os_sunos: 140 mount_args[last++] = "mount"; 141 mount_args[last++] = "-Ftmpfs"; 142 mount_args[last++] = "tmpfs"; 143 mount_args[last++] = mount_point; 144 break; 145 146 default: 147 err(123, "Don't know how to mount a file system for testing " 148 "purposes"); 149 } 150 mount_args[last] = NULL; 151 152 const char** arg; 153 printf("Mounting tmpfs onto %s with:", mount_point); 154 for (arg = &mount_args[0]; *arg != NULL; arg++) 155 printf(" %s", *arg); 156 printf("\n"); 157 158 const int ret = execvp(mount_args[0], KYUA_DEFS_UNCONST(mount_args)); 159 assert(ret == -1); 160 err(EXIT_FAILURE, "Failed to exec %s", mount_args[0]); 161 }; 162 163 164 /// Mounts a temporary file system. 165 /// 166 /// This is only provided for testing purposes. The mounted file system 167 /// contains no valuable data. 168 /// 169 /// Note that the calling test case is skipped if the current operating system 170 /// is not supported. 171 /// 172 /// \param mount_point The path on which the file system will be mounted. 173 static void 174 mount_tmpfs(const char* mount_point) 175 { 176 // SunOS's mount(8) requires paths to be absolute. To err on the side of 177 // caution, let's make it absolute in all cases. 178 //const fspath abs_mount_point = mount_point.is_absolute() ? 179 // mount_point : mount_point.to_absolute(); 180 181 pid_t pid = fork(); 182 ATF_REQUIRE(pid != -1); 183 if (pid == 0) 184 run_mount_tmpfs(mount_point); 185 int status; 186 ATF_REQUIRE(waitpid(pid, &status, 0) != -1); 187 ATF_REQUIRE(WIFEXITED(status)); 188 if (WEXITSTATUS(status) == 123) 189 atf_tc_skip("Don't know how to mount a file system for testing " 190 "purposes"); 191 else 192 ATF_REQUIRE_EQ(EXIT_SUCCESS, WEXITSTATUS(status)); 193 } 194 195 196 static bool 197 lchmod_fails(void) 198 { 199 ATF_REQUIRE(mkdir("test", 0755) != -1); 200 return lchmod("test", 0700) == -1 && chmod("test", 0700) != -1; 201 } 202 203 204 ATF_TC_WITHOUT_HEAD(cleanup__file); 205 ATF_TC_BODY(cleanup__file, tc) 206 { 207 atf_utils_create_file("root", "%s", ""); 208 ATF_REQUIRE(lookup(".", "root", DT_REG)); 209 ATF_REQUIRE(!kyua_error_is_set(kyua_fs_cleanup("root"))); 210 ATF_REQUIRE(!lookup(".", "root", DT_REG)); 211 } 212 213 214 ATF_TC_WITHOUT_HEAD(cleanup__subdir__empty); 215 ATF_TC_BODY(cleanup__subdir__empty, tc) 216 { 217 ATF_REQUIRE(mkdir("root", 0755) != -1); 218 ATF_REQUIRE(lookup(".", "root", DT_DIR)); 219 ATF_REQUIRE(!kyua_error_is_set(kyua_fs_cleanup("root"))); 220 ATF_REQUIRE(!lookup(".", "root", DT_DIR)); 221 } 222 223 224 ATF_TC_WITHOUT_HEAD(cleanup__subdir__files_and_directories); 225 ATF_TC_BODY(cleanup__subdir__files_and_directories, tc) 226 { 227 ATF_REQUIRE(mkdir("root", 0755) != -1); 228 atf_utils_create_file("root/.hidden_file", "%s", ""); 229 ATF_REQUIRE(mkdir("root/.hidden_dir", 0755) != -1); 230 atf_utils_create_file("root/.hidden_dir/a", "%s", ""); 231 atf_utils_create_file("root/file", "%s", ""); 232 atf_utils_create_file("root/with spaces", "%s", ""); 233 ATF_REQUIRE(mkdir("root/dir1", 0755) != -1); 234 ATF_REQUIRE(mkdir("root/dir1/dir2", 0755) != -1); 235 atf_utils_create_file("root/dir1/dir2/file", "%s", ""); 236 ATF_REQUIRE(mkdir("root/dir1/dir3", 0755) != -1); 237 ATF_REQUIRE(lookup(".", "root", DT_DIR)); 238 ATF_REQUIRE(!kyua_error_is_set(kyua_fs_cleanup("root"))); 239 ATF_REQUIRE(!lookup(".", "root", DT_DIR)); 240 } 241 242 243 ATF_TC_WITHOUT_HEAD(cleanup__subdir__unprotect_regular); 244 ATF_TC_BODY(cleanup__subdir__unprotect_regular, tc) 245 { 246 ATF_REQUIRE(mkdir("root", 0755) != -1); 247 ATF_REQUIRE(mkdir("root/dir1", 0755) != -1); 248 ATF_REQUIRE(mkdir("root/dir1/dir2", 0755) != -1); 249 atf_utils_create_file("root/dir1/dir2/file", "%s", ""); 250 ATF_REQUIRE(chmod("root/dir1/dir2/file", 0000) != -1); 251 ATF_REQUIRE(chmod("root/dir1/dir2", 0000) != -1); 252 ATF_REQUIRE(chmod("root/dir1", 0000) != -1); 253 ATF_REQUIRE(!kyua_error_is_set(kyua_fs_cleanup("root"))); 254 ATF_REQUIRE(!lookup(".", "root", DT_DIR)); 255 } 256 257 258 ATF_TC(cleanup__subdir__unprotect_symlink); 259 ATF_TC_HEAD(cleanup__subdir__unprotect_symlink, tc) 260 { 261 atf_tc_set_md_var(tc, "require.progs", "/bin/ls"); 262 // We are ensuring that chmod is not run on the target of a symlink, so 263 // we cannot be root (nor we don't want to, to prevent unprotecting a 264 // system file!). 265 atf_tc_set_md_var(tc, "require.user", "unprivileged"); 266 } 267 ATF_TC_BODY(cleanup__subdir__unprotect_symlink, tc) 268 { 269 ATF_REQUIRE(mkdir("root", 0755) != -1); 270 ATF_REQUIRE(mkdir("root/dir1", 0755) != -1); 271 ATF_REQUIRE(symlink("/bin/ls", "root/dir1/ls") != -1); 272 ATF_REQUIRE(chmod("root/dir1", 0555) != -1); 273 ATF_REQUIRE(!kyua_error_is_set(kyua_fs_cleanup("root"))); 274 ATF_REQUIRE(!lookup(".", "root", DT_DIR)); 275 } 276 277 278 ATF_TC_WITHOUT_HEAD(cleanup__subdir__links); 279 ATF_TC_BODY(cleanup__subdir__links, tc) 280 { 281 ATF_REQUIRE(mkdir("root", 0755) != -1); 282 ATF_REQUIRE(mkdir("root/dir1", 0755) != -1); 283 ATF_REQUIRE(symlink("../../root", "root/dir1/loop") != -1); 284 ATF_REQUIRE(symlink("non-existent", "root/missing") != -1); 285 ATF_REQUIRE(lookup(".", "root", DT_DIR)); 286 kyua_error_t error = kyua_fs_cleanup("root"); 287 if (kyua_error_is_set(error)) { 288 if (lchmod_fails()) 289 atf_tc_expect_fail("lchmod(2) is not implemented in your system"); 290 kyua_error_free(error); 291 atf_tc_fail("kyua_fs_cleanup returned an error"); 292 } 293 ATF_REQUIRE(!lookup(".", "root", DT_DIR)); 294 } 295 296 297 ATF_TC(cleanup__mount_point__simple); 298 ATF_TC_HEAD(cleanup__mount_point__simple, tc) 299 { 300 atf_tc_set_md_var(tc, "require.user", "root"); 301 } 302 ATF_TC_BODY(cleanup__mount_point__simple, tc) 303 { 304 ATF_REQUIRE(mkdir("root", 0755) != -1); 305 ATF_REQUIRE(mkdir("root/dir1", 0755) != -1); 306 atf_utils_create_file("root/zz", "%s", ""); 307 mount_tmpfs("root/dir1"); 308 ATF_REQUIRE(!kyua_error_is_set(kyua_fs_cleanup("root"))); 309 ATF_REQUIRE(!lookup(".", "root", DT_DIR)); 310 } 311 312 313 ATF_TC(cleanup__mount_point__overlayed); 314 ATF_TC_HEAD(cleanup__mount_point__overlayed, tc) 315 { 316 atf_tc_set_md_var(tc, "require.user", "root"); 317 } 318 ATF_TC_BODY(cleanup__mount_point__overlayed, tc) 319 { 320 ATF_REQUIRE(mkdir("root", 0755) != -1); 321 ATF_REQUIRE(mkdir("root/dir1", 0755) != -1); 322 atf_utils_create_file("root/zz", "%s", ""); 323 mount_tmpfs("root/dir1"); 324 mount_tmpfs("root/dir1"); 325 ATF_REQUIRE(!kyua_error_is_set(kyua_fs_cleanup("root"))); 326 ATF_REQUIRE(!lookup(".", "root", DT_DIR)); 327 } 328 329 330 ATF_TC(cleanup__mount_point__nested); 331 ATF_TC_HEAD(cleanup__mount_point__nested, tc) 332 { 333 atf_tc_set_md_var(tc, "require.user", "root"); 334 } 335 ATF_TC_BODY(cleanup__mount_point__nested, tc) 336 { 337 ATF_REQUIRE(mkdir("root", 0755) != -1); 338 ATF_REQUIRE(mkdir("root/dir1", 0755) != -1); 339 ATF_REQUIRE(mkdir("root/dir1/dir2", 0755) != -1); 340 ATF_REQUIRE(mkdir("root/dir3", 0755) != -1); 341 mount_tmpfs("root/dir1/dir2"); 342 mount_tmpfs("root/dir3"); 343 ATF_REQUIRE(mkdir("root/dir1/dir2/dir4", 0755) != -1); 344 mount_tmpfs("root/dir1/dir2/dir4"); 345 ATF_REQUIRE(mkdir("root/dir1/dir2/not-mount-point", 0755) != -1); 346 ATF_REQUIRE(!kyua_error_is_set(kyua_fs_cleanup("root"))); 347 ATF_REQUIRE(!lookup(".", "root", DT_DIR)); 348 } 349 350 351 ATF_TC(cleanup__mount_point__links); 352 ATF_TC_HEAD(cleanup__mount_point__links, tc) 353 { 354 atf_tc_set_md_var(tc, "require.user", "root"); 355 } 356 ATF_TC_BODY(cleanup__mount_point__links, tc) 357 { 358 ATF_REQUIRE(mkdir("root", 0755) != -1); 359 ATF_REQUIRE(mkdir("root/dir1", 0755) != -1); 360 ATF_REQUIRE(mkdir("root/dir3", 0755) != -1); 361 mount_tmpfs("root/dir1"); 362 ATF_REQUIRE(symlink("../dir3", "root/dir1/link") != -1); 363 ATF_REQUIRE(!kyua_error_is_set(kyua_fs_cleanup("root"))); 364 ATF_REQUIRE(!lookup(".", "root", DT_DIR)); 365 } 366 367 368 ATF_TC(cleanup__mount_point__busy); 369 ATF_TC_HEAD(cleanup__mount_point__busy, tc) 370 { 371 atf_tc_set_md_var(tc, "require.user", "root"); 372 } 373 ATF_TC_BODY(cleanup__mount_point__busy, tc) 374 { 375 ATF_REQUIRE(mkdir("root", 0755) != -1); 376 ATF_REQUIRE(mkdir("root/dir1", 0755) != -1); 377 mount_tmpfs("root/dir1"); 378 379 pid_t pid = fork(); 380 ATF_REQUIRE(pid != -1); 381 if (pid == 0) { 382 if (chdir("root/dir1") == -1) 383 abort(); 384 385 atf_utils_create_file("dont-delete-me", "%s", ""); 386 atf_utils_create_file("../../done", "%s", ""); 387 388 pause(); 389 exit(EXIT_SUCCESS); 390 } else { 391 fprintf(stderr, "Waiting for child to finish preparations\n"); 392 while (!atf_utils_file_exists("done")) {} 393 fprintf(stderr, "Child done; cleaning up\n"); 394 395 ATF_REQUIRE(kyua_error_is_set(kyua_fs_cleanup("root"))); 396 ATF_REQUIRE(atf_utils_file_exists("root/dir1/dont-delete-me")); 397 398 fprintf(stderr, "Killing child\n"); 399 ATF_REQUIRE(kill(pid, SIGKILL) != -1); 400 int status; 401 ATF_REQUIRE(waitpid(pid, &status, 0) != -1); 402 403 ATF_REQUIRE(!kyua_error_is_set(kyua_fs_cleanup("root"))); 404 ATF_REQUIRE(!lookup(".", "root", DT_DIR)); 405 } 406 } 407 408 409 ATF_TC_WITHOUT_HEAD(concat__one); 410 ATF_TC_BODY(concat__one, tc) 411 { 412 char* path; 413 ATF_REQUIRE(!kyua_error_is_set(kyua_fs_concat(&path, "foo", NULL))); 414 ATF_REQUIRE_STREQ("foo", path); 415 free(path); 416 } 417 418 419 ATF_TC_WITHOUT_HEAD(concat__two); 420 ATF_TC_BODY(concat__two, tc) 421 { 422 char* path; 423 ATF_REQUIRE(!kyua_error_is_set(kyua_fs_concat(&path, "foo", "bar", NULL))); 424 ATF_REQUIRE_STREQ("foo/bar", path); 425 free(path); 426 } 427 428 429 ATF_TC_WITHOUT_HEAD(concat__several); 430 ATF_TC_BODY(concat__several, tc) 431 { 432 char* path; 433 ATF_REQUIRE(!kyua_error_is_set(kyua_fs_concat(&path, "/usr", ".", "bin", 434 "ls", NULL))); 435 ATF_REQUIRE_STREQ("/usr/./bin/ls", path); 436 free(path); 437 } 438 439 440 ATF_TC_WITHOUT_HEAD(current_path__ok); 441 ATF_TC_BODY(current_path__ok, tc) 442 { 443 char* previous; 444 ATF_REQUIRE(!kyua_error_is_set(kyua_fs_current_path(&previous))); 445 446 ATF_REQUIRE(mkdir("root", 0755) != -1); 447 ATF_REQUIRE(chdir("root") != -1); 448 char* cwd; 449 ATF_REQUIRE(!kyua_error_is_set(kyua_fs_current_path(&cwd))); 450 451 char* exp_cwd; 452 ATF_REQUIRE(!kyua_error_is_set(kyua_fs_concat(&exp_cwd, previous, "root", 453 NULL))); 454 ATF_REQUIRE_STREQ(exp_cwd, cwd); 455 456 free(exp_cwd); 457 free(cwd); 458 free(previous); 459 } 460 461 462 ATF_TC_WITHOUT_HEAD(current_path__enoent); 463 ATF_TC_BODY(current_path__enoent, tc) 464 { 465 char* previous; 466 ATF_REQUIRE(!kyua_error_is_set(kyua_fs_current_path(&previous))); 467 468 ATF_REQUIRE(mkdir("root", 0755) != -1); 469 ATF_REQUIRE(chdir("root") != -1); 470 ATF_REQUIRE(rmdir("../root") != -1); 471 char* cwd = (char*)0xdeadbeef; 472 kyua_error_t error = kyua_fs_current_path(&cwd); 473 ATF_REQUIRE(kyua_error_is_set(error)); 474 ATF_REQUIRE(kyua_error_is_type(error, "libc")); 475 ATF_REQUIRE_EQ(ENOENT, kyua_libc_error_errno(error)); 476 ATF_REQUIRE_EQ((char*)0xdeadbeef, cwd); 477 kyua_error_free(error); 478 479 free(previous); 480 } 481 482 483 ATF_TC_WITHOUT_HEAD(make_absolute__absolute); 484 ATF_TC_BODY(make_absolute__absolute, tc) 485 { 486 char* absolute; 487 ATF_REQUIRE(!kyua_error_is_set(kyua_fs_make_absolute( 488 "/this/is/absolute", &absolute))); 489 ATF_REQUIRE_STREQ("/this/is/absolute", absolute); 490 free(absolute); 491 } 492 493 494 ATF_TC_WITHOUT_HEAD(make_absolute__relative); 495 ATF_TC_BODY(make_absolute__relative, tc) 496 { 497 kyua_error_t error; 498 char* absolute; 499 500 DIR* previous = opendir("."); 501 ATF_REQUIRE(previous != NULL); 502 ATF_REQUIRE(chdir("/usr") != -1); 503 error = kyua_fs_make_absolute("bin/foobar", &absolute); 504 const int previous_fd = dirfd(previous); 505 ATF_REQUIRE(fchdir(previous_fd) != -1); 506 close(previous_fd); 507 508 ATF_REQUIRE(!kyua_error_is_set(error)); 509 ATF_REQUIRE_STREQ("/usr/bin/foobar", absolute); 510 free(absolute); 511 } 512 513 514 ATF_TC(unmount__ok); 515 ATF_TC_HEAD(unmount__ok, tc) 516 { 517 atf_tc_set_md_var(tc, "require.user", "root"); 518 } 519 ATF_TC_BODY(unmount__ok, tc) 520 { 521 ATF_REQUIRE(mkdir("mount_point", 0755) != -1); 522 523 atf_utils_create_file("mount_point/test1", "%s", ""); 524 mount_tmpfs("mount_point"); 525 atf_utils_create_file("mount_point/test2", "%s", ""); 526 527 ATF_REQUIRE(!atf_utils_file_exists("mount_point/test1")); 528 ATF_REQUIRE( atf_utils_file_exists("mount_point/test2")); 529 kyua_fs_unmount("mount_point"); 530 ATF_REQUIRE( atf_utils_file_exists("mount_point/test1")); 531 ATF_REQUIRE(!atf_utils_file_exists("mount_point/test2")); 532 } 533 534 535 ATF_TC(unmount__fail); 536 ATF_TC_HEAD(unmount__fail, tc) 537 { 538 atf_tc_set_md_var(tc, "require.user", "root"); 539 } 540 ATF_TC_BODY(unmount__fail, tc) 541 { 542 kyua_error_t error = kyua_fs_unmount("mount_point"); 543 ATF_REQUIRE(kyua_error_is_set(error)); 544 kyua_error_free(error); 545 } 546 547 548 ATF_TP_ADD_TCS(tp) 549 { 550 ATF_TP_ADD_TC(tp, cleanup__file); 551 ATF_TP_ADD_TC(tp, cleanup__subdir__empty); 552 ATF_TP_ADD_TC(tp, cleanup__subdir__files_and_directories); 553 ATF_TP_ADD_TC(tp, cleanup__subdir__unprotect_regular); 554 ATF_TP_ADD_TC(tp, cleanup__subdir__unprotect_symlink); 555 ATF_TP_ADD_TC(tp, cleanup__subdir__links); 556 ATF_TP_ADD_TC(tp, cleanup__mount_point__simple); 557 ATF_TP_ADD_TC(tp, cleanup__mount_point__overlayed); 558 ATF_TP_ADD_TC(tp, cleanup__mount_point__nested); 559 ATF_TP_ADD_TC(tp, cleanup__mount_point__links); 560 ATF_TP_ADD_TC(tp, cleanup__mount_point__busy); 561 562 ATF_TP_ADD_TC(tp, concat__one); 563 ATF_TP_ADD_TC(tp, concat__two); 564 ATF_TP_ADD_TC(tp, concat__several); 565 566 ATF_TP_ADD_TC(tp, current_path__ok); 567 ATF_TP_ADD_TC(tp, current_path__enoent); 568 569 ATF_TP_ADD_TC(tp, make_absolute__absolute); 570 ATF_TP_ADD_TC(tp, make_absolute__relative); 571 572 ATF_TP_ADD_TC(tp, unmount__ok); 573 ATF_TP_ADD_TC(tp, unmount__fail); 574 575 return atf_no_error(); 576 } 577