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