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