4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews// Automated Testing Framework (atf)
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews// Copyright (c) 2007 The NetBSD Foundation, Inc.
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews// All rights reserved.
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews// Redistribution and use in source and binary forms, with or without
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews// modification, are permitted provided that the following conditions
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews// 1. Redistributions of source code must retain the above copyright
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews// notice, this list of conditions and the following disclaimer.
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews// 2. Redistributions in binary form must reproduce the above copyright
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews// notice, this list of conditions and the following disclaimer in the
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews// documentation and/or other materials provided with the distribution.
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews// THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews// CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews// IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews// IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews// IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews// ------------------------------------------------------------------------
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews// Auxiliary functions.
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews// ------------------------------------------------------------------------
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewsstatic void cleanup_aux(const impl::path&, dev_t, bool);
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewsstatic void cleanup_aux_dir(const impl::path&, const impl::file_info&,
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewsstatic bool safe_access(const impl::path&, int, int);
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews//! An implementation of access(2) but using the effective user value
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews//! instead of the real one. Also avoids false positives for root when
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews//! asking for execute permissions, which appear in SunOS.
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews throw tools::system_error(IMPL_NAME "::eaccess",
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews "Cannot get information from file " +
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews /* Early return if we are only checking for existence and the file
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews * exists (stat call returned). */
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews bool ok = false;
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews /* Allow root to read/write any file. */
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews if (!ok && (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))) {
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews /* Allow root to execute the file if any of its execution bits
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews * are set. */
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews if (!ok && (tools::user::euid() == st.st_uid)) {
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews ok = ((mode & access_r) && (st.st_mode & S_IRUSR)) ||
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews ((mode & access_w) && (st.st_mode & S_IWUSR)) ||
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews if (!ok && tools::user::is_member_of_group(st.st_gid)) {
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews ok = ((mode & access_r) && (st.st_mode & S_IRGRP)) ||
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews ((mode & access_w) && (st.st_mode & S_IWGRP)) ||
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews if (!ok && ((tools::user::euid() != st.st_uid) &&
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews !tools::user::is_member_of_group(st.st_gid))) {
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews ok = ((mode & access_r) && (st.st_mode & S_IROTH)) ||
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews ((mode & access_w) && (st.st_mode & S_IWOTH)) ||
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews throw tools::system_error(IMPL_NAME "::eaccess", "Access check failed",
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews//! \brief A controlled version of access(2).
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews//! This function reimplements the standard access(2) system call to
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews//! safely control its exit status and raise an exception in case of
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewssafe_access(const impl::path& p, int mode, int experr)
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews return true;
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews return false;
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews// The cleanup routines below are tricky: they are executed immediately after
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews// a test case's death, and after we have forcibly killed any stale processes.
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews// However, even if the processes are dead, this does not mean that the file
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews// system we are scanning is stable. In particular, if the test case has
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews// mounted file systems through fuse/puffs, the fact that the processes died
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews// does not mean that the file system is truly unmounted.
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews// The code below attempts to cope with this by catching errors and either
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews// ignoring them or retrying the actions on the same file/directory a few times
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews// before giving up.
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews// The erase parameter in this routine is to control nested mount points.
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews// We want to descend into a mount point to unmount anything that is
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews// mounted under it, but we do not want to delete any files while doing
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews// this traversal. In other words, we erase files until we cross the
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews// first mount point, and after that point we only scan and unmount.
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewscleanup_aux(const impl::path& p, dev_t parent_device, bool erase)
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews if (fi.get_type() == impl::file_info::dir_type)
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews cleanup_aux_dir(p, fi, fi.get_device() == parent_device);
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews if (fi.get_type() == impl::file_info::dir_type)
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewscleanup_aux_dir(const impl::path& p, const impl::file_info& fi,
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews if (erase && ((fi.get_mode() & S_IRWXU) != S_IRWXU)) {
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews if (chmod(p.c_str(), fi.get_mode() | S_IRWXU) == -1) {
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews throw tools::system_error(IMPL_NAME "::cleanup(" +
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews for (std::set< std::string >::const_iterator iter = subdirs.begin();
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews // At least, FreeBSD's unmount(2) requires the path to be absolute.
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews // Let's make it absolute in all cases just to be safe that this does
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews // not affect other systems.
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews const impl::path& abs_path = in_path.is_absolute() ?
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews throw tools::system_error(IMPL_NAME "::cleanup(" + in_path.str() +
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews // We could use umount(2) instead if it was available... but
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews // trying to do so under, e.g. Linux, is a nightmare because we
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews // also have to update /etc/mtab to match what we did. It is
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews // stools::fser to just leave the system-specific umount(8) tool deal
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews // with it, at least for now.
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews tools::process::argv_array argv("umount", abs_path.c_str(), NULL);
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews tools::process::status s = tools::process::exec(prog, argv,
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews tools::process::stream_inherit(), tools::process::stream_inherit());
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews if (!s.exited() || s.exitstatus() != EXIT_SUCCESS)
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews throw std::runtime_error("Call to unmount failed");
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews const std::string::size_type next_pos = in.find('/', pos);
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews const std::string component = in.substr(pos, next_pos - pos);
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews// ------------------------------------------------------------------------
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews// The "path" class.
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews// ------------------------------------------------------------------------
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews const std::string::size_type endpos = m_data.rfind('/');
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews else if (endpos == 0)
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews std::string::size_type begpos = m_data.rfind('/');
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews return std::strcmp(m_data.c_str(), p.m_data.c_str()) < 0;
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews// ------------------------------------------------------------------------
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews// The "file_info" class.
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews// ------------------------------------------------------------------------
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews "Cannot get information of " + p.str() + "; " +
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews throw system_error(IMPL_NAME "::file_info", "Unknown file type "
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews// ------------------------------------------------------------------------
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews// The "directory" class.
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews// ------------------------------------------------------------------------
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews throw system_error(IMPL_NAME "::directory::directory(" +
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews insert(value_type(dep->d_name, file_info(entryp)));
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews throw system_error(IMPL_NAME "::directory::directory(" +
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews for (const_iterator iter = begin(); iter != end(); iter++)
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews// ------------------------------------------------------------------------
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews// The "temp_dir" class.
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews// ------------------------------------------------------------------------
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews tools::auto_array< char > buf(new char[p.str().length() + 1]);
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews throw tools::system_error(IMPL_NAME "::temp_dir::temp_dir(" +
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews// ------------------------------------------------------------------------
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews// Free functions.
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews// ------------------------------------------------------------------------
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews return true;
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews } catch (const system_error& e) {
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews return false;
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewsimpl::have_prog_in_path(const std::string& prog)
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews // Do not bother to provide a default value for PATH. If it is not
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews // there something is broken in the user's environment.
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews throw std::runtime_error("PATH not defined in the environment");
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews tools::text::split(tools::env::get("PATH"), ":");
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews for (std::vector< std::string >::const_iterator iter = dirs.begin();
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews return false;
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews if (file_info(p).get_type() == file_info::dir_type)
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews throw tools::system_error(IMPL_NAME "::remove(" + p.str() + ")",
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews "Is a directory",
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews throw tools::system_error(IMPL_NAME "::remove(" + p.str() + ")",
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews /* Some operating systems (e.g. OpenSolaris 200906) return
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews * EEXIST instead of ENOTEMPTY for non-empty directories.
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews * Homogenize the return value so that callers don't need
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews * to bother about differences in operating systems. */
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews throw system_error(IMPL_NAME "::rmdir", "Cannot remove directory",
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews throw tools::system_error(IMPL_NAME "::chdir(" + dir.str() + ")",
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews throw tools::system_error(IMPL_NAME "::get_current_dir()",