fs.cpp revision 275988
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 32extern "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 47extern "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 58namespace impl = atf::fs; 59#define IMPL_NAME "atf::fs" 60 61// ------------------------------------------------------------------------ 62// Auxiliary functions. 63// ------------------------------------------------------------------------ 64 65static 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//! 74static 75bool 76safe_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 108impl::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 115impl::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 122impl::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 129impl::path::~path(void) 130{ 131 atf_fs_path_fini(&m_path); 132} 133 134const char* 135impl::path::c_str(void) 136 const 137{ 138 return atf_fs_path_cstring(&m_path); 139} 140 141const atf_fs_path_t* 142impl::path::c_path(void) 143 const 144{ 145 return &m_path; 146} 147 148std::string 149impl::path::str(void) 150 const 151{ 152 return c_str(); 153} 154 155bool 156impl::path::is_absolute(void) 157 const 158{ 159 return atf_fs_path_is_absolute(&m_path); 160} 161 162bool 163impl::path::is_root(void) 164 const 165{ 166 return atf_fs_path_is_root(&m_path); 167} 168 169impl::path 170impl::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 185std::string 186impl::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 201impl::path 202impl::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 216impl::path& 217impl::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 232bool 233impl::path::operator==(const path& p) 234 const 235{ 236 return atf_equal_fs_path_fs_path(&m_path, &p.m_path); 237} 238 239bool 240impl::path::operator!=(const path& p) 241 const 242{ 243 return !atf_equal_fs_path_fs_path(&m_path, &p.m_path); 244} 245 246impl::path 247impl::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 259impl::path 260impl::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 273bool 274impl::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 286const int impl::file_info::blk_type = atf_fs_stat_blk_type; 287const int impl::file_info::chr_type = atf_fs_stat_chr_type; 288const int impl::file_info::dir_type = atf_fs_stat_dir_type; 289const int impl::file_info::fifo_type = atf_fs_stat_fifo_type; 290const int impl::file_info::lnk_type = atf_fs_stat_lnk_type; 291const int impl::file_info::reg_type = atf_fs_stat_reg_type; 292const int impl::file_info::sock_type = atf_fs_stat_sock_type; 293const int impl::file_info::wht_type = atf_fs_stat_wht_type; 294 295impl::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 304impl::file_info::file_info(const file_info& fi) 305{ 306 atf_fs_stat_copy(&m_stat, &fi.m_stat); 307} 308 309impl::file_info::~file_info(void) 310{ 311 atf_fs_stat_fini(&m_stat); 312} 313 314dev_t 315impl::file_info::get_device(void) 316 const 317{ 318 return atf_fs_stat_get_device(&m_stat); 319} 320 321ino_t 322impl::file_info::get_inode(void) 323 const 324{ 325 return atf_fs_stat_get_inode(&m_stat); 326} 327 328mode_t 329impl::file_info::get_mode(void) 330 const 331{ 332 return atf_fs_stat_get_mode(&m_stat); 333} 334 335off_t 336impl::file_info::get_size(void) 337 const 338{ 339 return atf_fs_stat_get_size(&m_stat); 340} 341 342int 343impl::file_info::get_type(void) 344 const 345{ 346 return atf_fs_stat_get_type(&m_stat); 347} 348 349bool 350impl::file_info::is_owner_readable(void) 351 const 352{ 353 return atf_fs_stat_is_owner_readable(&m_stat); 354} 355 356bool 357impl::file_info::is_owner_writable(void) 358 const 359{ 360 return atf_fs_stat_is_owner_writable(&m_stat); 361} 362 363bool 364impl::file_info::is_owner_executable(void) 365 const 366{ 367 return atf_fs_stat_is_owner_executable(&m_stat); 368} 369 370bool 371impl::file_info::is_group_readable(void) 372 const 373{ 374 return atf_fs_stat_is_group_readable(&m_stat); 375} 376 377bool 378impl::file_info::is_group_writable(void) 379 const 380{ 381 return atf_fs_stat_is_group_writable(&m_stat); 382} 383 384bool 385impl::file_info::is_group_executable(void) 386 const 387{ 388 return atf_fs_stat_is_group_executable(&m_stat); 389} 390 391bool 392impl::file_info::is_other_readable(void) 393 const 394{ 395 return atf_fs_stat_is_other_readable(&m_stat); 396} 397 398bool 399impl::file_info::is_other_writable(void) 400 const 401{ 402 return atf_fs_stat_is_other_writable(&m_stat); 403} 404 405bool 406impl::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 416impl::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 434std::set< std::string > 435impl::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 450bool 451impl::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 463bool 464impl::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 486bool 487impl::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 494void 495impl::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 507void 508impl::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