1 // Copyright (c) 2007 The NetBSD Foundation, 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 6 // are met: 7 // 1. Redistributions of source code must retain the above copyright 8 // notice, this list of conditions and the following disclaimer. 9 // 2. Redistributions in binary form must reproduce the above copyright 10 // notice, this list of conditions and the following disclaimer in the 11 // documentation and/or other materials provided with the distribution. 12 // 13 // THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND 14 // CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, 15 // INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 16 // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17 // IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY 18 // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 20 // GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 21 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 22 // IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 23 // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN 24 // IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 26 #include "atf-c++/detail/fs.hpp" 27 28 #if defined(HAVE_CONFIG_H) 29 #include "config.h" 30 #endif 31 32 extern "C" { 33 #include <sys/param.h> 34 #include <sys/types.h> 35 #include <sys/mount.h> 36 #include <sys/stat.h> 37 #include <sys/wait.h> 38 #include <dirent.h> 39 #include <libgen.h> 40 #include <unistd.h> 41 } 42 43 #include <cerrno> 44 #include <cstdlib> 45 #include <cstring> 46 47 extern "C" { 48 #include "atf-c/error.h" 49 } 50 51 #include "atf-c++/detail/env.hpp" 52 #include "atf-c++/detail/exceptions.hpp" 53 #include "atf-c++/detail/process.hpp" 54 #include "atf-c++/detail/sanity.hpp" 55 #include "atf-c++/detail/text.hpp" 56 #include "atf-c++/utils.hpp" 57 58 namespace impl = atf::fs; 59 #define IMPL_NAME "atf::fs" 60 61 // ------------------------------------------------------------------------ 62 // Auxiliary functions. 63 // ------------------------------------------------------------------------ 64 65 static bool safe_access(const impl::path&, int, int); 66 67 //! 68 //! \brief A controlled version of access(2). 69 //! 70 //! This function reimplements the standard access(2) system call to 71 //! safely control its exit status and raise an exception in case of 72 //! failure. 73 //! 74 static 75 bool 76 safe_access(const impl::path& p, int mode, int experr) 77 { 78 bool ok; 79 80 atf_error_t err = atf_fs_eaccess(p.c_path(), mode); 81 if (atf_is_error(err)) { 82 if (atf_error_is(err, "libc")) { 83 if (atf_libc_error_code(err) == experr) { 84 atf_error_free(err); 85 ok = false; 86 } else { 87 atf::throw_atf_error(err); 88 // XXX Silence warning; maybe throw_atf_error should be 89 // an exception and not a function. 90 ok = false; 91 } 92 } else { 93 atf::throw_atf_error(err); 94 // XXX Silence warning; maybe throw_atf_error should be 95 // an exception and not a function. 96 ok = false; 97 } 98 } else 99 ok = true; 100 101 return ok; 102 } 103 104 // ------------------------------------------------------------------------ 105 // The "path" class. 106 // ------------------------------------------------------------------------ 107 108 impl::path::path(const std::string& s) 109 { 110 atf_error_t err = atf_fs_path_init_fmt(&m_path, "%s", s.c_str()); 111 if (atf_is_error(err)) 112 throw_atf_error(err); 113 } 114 115 impl::path::path(const path& p) 116 { 117 atf_error_t err = atf_fs_path_copy(&m_path, &p.m_path); 118 if (atf_is_error(err)) 119 throw_atf_error(err); 120 } 121 122 impl::path::path(const atf_fs_path_t *p) 123 { 124 atf_error_t err = atf_fs_path_copy(&m_path, p); 125 if (atf_is_error(err)) 126 throw_atf_error(err); 127 } 128 129 impl::path::~path(void) 130 { 131 atf_fs_path_fini(&m_path); 132 } 133 134 const char* 135 impl::path::c_str(void) 136 const 137 { 138 return atf_fs_path_cstring(&m_path); 139 } 140 141 const atf_fs_path_t* 142 impl::path::c_path(void) 143 const 144 { 145 return &m_path; 146 } 147 148 std::string 149 impl::path::str(void) 150 const 151 { 152 return c_str(); 153 } 154 155 bool 156 impl::path::is_absolute(void) 157 const 158 { 159 return atf_fs_path_is_absolute(&m_path); 160 } 161 162 bool 163 impl::path::is_root(void) 164 const 165 { 166 return atf_fs_path_is_root(&m_path); 167 } 168 169 impl::path 170 impl::path::branch_path(void) 171 const 172 { 173 atf_fs_path_t bp; 174 atf_error_t err; 175 176 err = atf_fs_path_branch_path(&m_path, &bp); 177 if (atf_is_error(err)) 178 throw_atf_error(err); 179 180 path p(atf_fs_path_cstring(&bp)); 181 atf_fs_path_fini(&bp); 182 return p; 183 } 184 185 std::string 186 impl::path::leaf_name(void) 187 const 188 { 189 atf_dynstr_t ln; 190 atf_error_t err; 191 192 err = atf_fs_path_leaf_name(&m_path, &ln); 193 if (atf_is_error(err)) 194 throw_atf_error(err); 195 196 std::string s(atf_dynstr_cstring(&ln)); 197 atf_dynstr_fini(&ln); 198 return s; 199 } 200 201 impl::path 202 impl::path::to_absolute(void) 203 const 204 { 205 atf_fs_path_t pa; 206 207 atf_error_t err = atf_fs_path_to_absolute(&m_path, &pa); 208 if (atf_is_error(err)) 209 throw_atf_error(err); 210 211 path p(atf_fs_path_cstring(&pa)); 212 atf_fs_path_fini(&pa); 213 return p; 214 } 215 216 impl::path& 217 impl::path::operator=(const path& p) 218 { 219 atf_fs_path_t tmp; 220 221 atf_error_t err = atf_fs_path_init_fmt(&tmp, "%s", p.c_str()); 222 if (atf_is_error(err)) 223 throw_atf_error(err); 224 else { 225 atf_fs_path_fini(&m_path); 226 m_path = tmp; 227 } 228 229 return *this; 230 } 231 232 bool 233 impl::path::operator==(const path& p) 234 const 235 { 236 return atf_equal_fs_path_fs_path(&m_path, &p.m_path); 237 } 238 239 bool 240 impl::path::operator!=(const path& p) 241 const 242 { 243 return !atf_equal_fs_path_fs_path(&m_path, &p.m_path); 244 } 245 246 impl::path 247 impl::path::operator/(const std::string& p) 248 const 249 { 250 path p2 = *this; 251 252 atf_error_t err = atf_fs_path_append_fmt(&p2.m_path, "%s", p.c_str()); 253 if (atf_is_error(err)) 254 throw_atf_error(err); 255 256 return p2; 257 } 258 259 impl::path 260 impl::path::operator/(const path& p) 261 const 262 { 263 path p2 = *this; 264 265 atf_error_t err = atf_fs_path_append_fmt(&p2.m_path, "%s", 266 atf_fs_path_cstring(&p.m_path)); 267 if (atf_is_error(err)) 268 throw_atf_error(err); 269 270 return p2; 271 } 272 273 bool 274 impl::path::operator<(const path& p) 275 const 276 { 277 const char *s1 = atf_fs_path_cstring(&m_path); 278 const char *s2 = atf_fs_path_cstring(&p.m_path); 279 return std::strcmp(s1, s2) < 0; 280 } 281 282 // ------------------------------------------------------------------------ 283 // The "file_info" class. 284 // ------------------------------------------------------------------------ 285 286 const int impl::file_info::blk_type = atf_fs_stat_blk_type; 287 const int impl::file_info::chr_type = atf_fs_stat_chr_type; 288 const int impl::file_info::dir_type = atf_fs_stat_dir_type; 289 const int impl::file_info::fifo_type = atf_fs_stat_fifo_type; 290 const int impl::file_info::lnk_type = atf_fs_stat_lnk_type; 291 const int impl::file_info::reg_type = atf_fs_stat_reg_type; 292 const int impl::file_info::sock_type = atf_fs_stat_sock_type; 293 const int impl::file_info::wht_type = atf_fs_stat_wht_type; 294 295 impl::file_info::file_info(const path& p) 296 { 297 atf_error_t err; 298 299 err = atf_fs_stat_init(&m_stat, p.c_path()); 300 if (atf_is_error(err)) 301 throw_atf_error(err); 302 } 303 304 impl::file_info::file_info(const file_info& fi) 305 { 306 atf_fs_stat_copy(&m_stat, &fi.m_stat); 307 } 308 309 impl::file_info::~file_info(void) 310 { 311 atf_fs_stat_fini(&m_stat); 312 } 313 314 dev_t 315 impl::file_info::get_device(void) 316 const 317 { 318 return atf_fs_stat_get_device(&m_stat); 319 } 320 321 ino_t 322 impl::file_info::get_inode(void) 323 const 324 { 325 return atf_fs_stat_get_inode(&m_stat); 326 } 327 328 mode_t 329 impl::file_info::get_mode(void) 330 const 331 { 332 return atf_fs_stat_get_mode(&m_stat); 333 } 334 335 off_t 336 impl::file_info::get_size(void) 337 const 338 { 339 return atf_fs_stat_get_size(&m_stat); 340 } 341 342 int 343 impl::file_info::get_type(void) 344 const 345 { 346 return atf_fs_stat_get_type(&m_stat); 347 } 348 349 bool 350 impl::file_info::is_owner_readable(void) 351 const 352 { 353 return atf_fs_stat_is_owner_readable(&m_stat); 354 } 355 356 bool 357 impl::file_info::is_owner_writable(void) 358 const 359 { 360 return atf_fs_stat_is_owner_writable(&m_stat); 361 } 362 363 bool 364 impl::file_info::is_owner_executable(void) 365 const 366 { 367 return atf_fs_stat_is_owner_executable(&m_stat); 368 } 369 370 bool 371 impl::file_info::is_group_readable(void) 372 const 373 { 374 return atf_fs_stat_is_group_readable(&m_stat); 375 } 376 377 bool 378 impl::file_info::is_group_writable(void) 379 const 380 { 381 return atf_fs_stat_is_group_writable(&m_stat); 382 } 383 384 bool 385 impl::file_info::is_group_executable(void) 386 const 387 { 388 return atf_fs_stat_is_group_executable(&m_stat); 389 } 390 391 bool 392 impl::file_info::is_other_readable(void) 393 const 394 { 395 return atf_fs_stat_is_other_readable(&m_stat); 396 } 397 398 bool 399 impl::file_info::is_other_writable(void) 400 const 401 { 402 return atf_fs_stat_is_other_writable(&m_stat); 403 } 404 405 bool 406 impl::file_info::is_other_executable(void) 407 const 408 { 409 return atf_fs_stat_is_other_executable(&m_stat); 410 } 411 412 // ------------------------------------------------------------------------ 413 // The "directory" class. 414 // ------------------------------------------------------------------------ 415 416 impl::directory::directory(const path& p) 417 { 418 DIR* dp = ::opendir(p.c_str()); 419 if (dp == NULL) 420 throw system_error(IMPL_NAME "::directory::directory(" + 421 p.str() + ")", "opendir(3) failed", errno); 422 423 struct dirent* dep; 424 while ((dep = ::readdir(dp)) != NULL) { 425 path entryp = p / dep->d_name; 426 insert(value_type(dep->d_name, file_info(entryp))); 427 } 428 429 if (::closedir(dp) == -1) 430 throw system_error(IMPL_NAME "::directory::directory(" + 431 p.str() + ")", "closedir(3) failed", errno); 432 } 433 434 std::set< std::string > 435 impl::directory::names(void) 436 const 437 { 438 std::set< std::string > ns; 439 440 for (const_iterator iter = begin(); iter != end(); iter++) 441 ns.insert((*iter).first); 442 443 return ns; 444 } 445 446 // ------------------------------------------------------------------------ 447 // Free functions. 448 // ------------------------------------------------------------------------ 449 450 bool 451 impl::exists(const path& p) 452 { 453 atf_error_t err; 454 bool b; 455 456 err = atf_fs_exists(p.c_path(), &b); 457 if (atf_is_error(err)) 458 throw_atf_error(err); 459 460 return b; 461 } 462 463 bool 464 impl::have_prog_in_path(const std::string& prog) 465 { 466 PRE(prog.find('/') == std::string::npos); 467 468 // Do not bother to provide a default value for PATH. If it is not 469 // there something is broken in the user's environment. 470 if (!atf::env::has("PATH")) 471 throw std::runtime_error("PATH not defined in the environment"); 472 std::vector< std::string > dirs = 473 atf::text::split(atf::env::get("PATH"), ":"); 474 475 bool found = false; 476 for (std::vector< std::string >::const_iterator iter = dirs.begin(); 477 !found && iter != dirs.end(); iter++) { 478 const path& dir = path(*iter); 479 480 if (is_executable(dir / prog)) 481 found = true; 482 } 483 return found; 484 } 485 486 bool 487 impl::is_executable(const path& p) 488 { 489 if (!exists(p)) 490 return false; 491 return safe_access(p, atf_fs_access_x, EACCES); 492 } 493 494 void 495 impl::remove(const path& p) 496 { 497 if (file_info(p).get_type() == file_info::dir_type) 498 throw atf::system_error(IMPL_NAME "::remove(" + p.str() + ")", 499 "Is a directory", 500 EPERM); 501 if (::unlink(p.c_str()) == -1) 502 throw atf::system_error(IMPL_NAME "::remove(" + p.str() + ")", 503 "unlink(" + p.str() + ") failed", 504 errno); 505 } 506 507 void 508 impl::rmdir(const path& p) 509 { 510 atf_error_t err = atf_fs_rmdir(p.c_path()); 511 if (atf_is_error(err)) 512 throw_atf_error(err); 513 } 514