4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews//
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews// Automated Testing Framework (atf)
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews//
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews// Copyright (c) 2007 The NetBSD Foundation, Inc.
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews// All rights reserved.
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews//
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// are met:
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//
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
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews#if defined(HAVE_CONFIG_H)
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews#include "config.h"
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews#endif
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewsextern "C" {
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews#include <sys/param.h>
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews#include <sys/types.h>
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews#include <sys/mount.h>
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews#include <sys/stat.h>
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews#include <sys/wait.h>
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews#include <dirent.h>
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews#include <libgen.h>
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews#include <unistd.h>
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews}
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews#include <cassert>
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews#include <cerrno>
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews#include <cstdlib>
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews#include <cstring>
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews#include "auto_array.hpp"
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews#include "env.hpp"
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews#include "exceptions.hpp"
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews#include "fs.hpp"
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews#include "process.hpp"
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews#include "text.hpp"
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews#include "user.hpp"
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewsnamespace impl = tools::fs;
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews#define IMPL_NAME "tools::fs"
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews// ------------------------------------------------------------------------
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews// Auxiliary functions.
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews// ------------------------------------------------------------------------
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 Andrews bool);
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewsstatic void do_unmount(const impl::path&);
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewsstatic bool safe_access(const impl::path&, int, int);
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewsstatic const int access_f = 1 << 0;
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewsstatic const int access_r = 1 << 1;
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewsstatic const int access_w = 1 << 2;
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewsstatic const int access_x = 1 << 3;
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews//!
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//!
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewsstatic
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewsvoid
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewseaccess(const tools::fs::path& p, int mode)
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews{
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews assert(mode & access_f || mode & access_r ||
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews mode & access_w || mode & access_x);
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews struct stat st;
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews if (lstat(p.c_str(), &st) == -1)
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews throw tools::system_error(IMPL_NAME "::eaccess",
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews "Cannot get information from file " +
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews p.str(), errno);
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews /* Early return if we are only checking for existence and the file
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews * exists (stat call returned). */
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews if (mode & access_f)
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews return;
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews bool ok = false;
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews if (tools::user::is_root()) {
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews if (!ok && !(mode & access_x)) {
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews /* Allow root to read/write any file. */
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews ok = true;
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews }
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews
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 ok = true;
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews }
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews } else {
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 ((mode & access_x) && (st.st_mode & S_IXUSR));
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews }
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 ((mode & access_x) && (st.st_mode & S_IXGRP));
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews }
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 ((mode & access_x) && (st.st_mode & S_IXOTH));
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews }
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews }
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews if (!ok)
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews throw tools::system_error(IMPL_NAME "::eaccess", "Access check failed",
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews EACCES);
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews}
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews//!
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews//! \brief A controlled version of access(2).
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews//!
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 Andrews//! failure.
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews//!
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewsstatic
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewsbool
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewssafe_access(const impl::path& p, int mode, int experr)
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews{
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews try {
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews eaccess(p, mode);
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews return true;
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews } catch (const tools::system_error& e) {
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews if (e.code() == experr)
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews return false;
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews else
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews throw e;
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews }
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews}
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews
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//
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 Andrewsstatic const int max_retries = 5;
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewsstatic const int retry_delay_in_seconds = 1;
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews
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 Andrewsstatic
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewsvoid
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewscleanup_aux(const impl::path& p, dev_t parent_device, bool erase)
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews{
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews try {
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews impl::file_info fi(p);
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews
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
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews if (fi.get_device() != parent_device)
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews do_unmount(p);
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews if (erase) {
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews if (fi.get_type() == impl::file_info::dir_type)
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews impl::rmdir(p);
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews else
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews impl::remove(p);
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews }
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews } catch (const tools::system_error& e) {
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews if (e.code() != ENOENT && e.code() != ENOTDIR)
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews throw e;
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews }
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews}
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewsstatic
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewsvoid
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewscleanup_aux_dir(const impl::path& p, const impl::file_info& fi,
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews bool erase)
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews{
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews if (erase && ((fi.get_mode() & S_IRWXU) != S_IRWXU)) {
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews int retries = max_retries;
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewsretry_chmod:
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews if (chmod(p.c_str(), fi.get_mode() | S_IRWXU) == -1) {
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews if (retries > 0) {
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews retries--;
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews ::sleep(retry_delay_in_seconds);
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews goto retry_chmod;
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews } else {
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews throw tools::system_error(IMPL_NAME "::cleanup(" +
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews p.str() + ")", "chmod(2) failed",
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews errno);
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews }
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews }
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews }
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews std::set< std::string > subdirs;
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews {
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews bool ok = false;
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews int retries = max_retries;
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews while (!ok) {
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews assert(retries > 0);
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews try {
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews const impl::directory d(p);
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews subdirs = d.names();
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews ok = true;
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews } catch (const tools::system_error& e) {
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews retries--;
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews if (retries == 0)
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews throw e;
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews ::sleep(retry_delay_in_seconds);
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews }
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews }
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews assert(ok);
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews }
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews for (std::set< std::string >::const_iterator iter = subdirs.begin();
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews iter != subdirs.end(); iter++) {
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews const std::string& name = *iter;
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews if (name != "." && name != "..")
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews cleanup_aux(p / name, fi.get_device(), erase);
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews }
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews}
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewsstatic
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewsvoid
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewsdo_unmount(const impl::path& in_path)
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews{
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 in_path : in_path.to_absolute();
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews#if defined(HAVE_UNMOUNT)
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews int retries = max_retries;
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewsretry_unmount:
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews if (unmount(abs_path.c_str(), 0) == -1) {
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews if (errno == EBUSY && retries > 0) {
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews retries--;
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews ::sleep(retry_delay_in_seconds);
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews goto retry_unmount;
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews } else {
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews throw tools::system_error(IMPL_NAME "::cleanup(" + in_path.str() +
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews ")", "unmount(2) failed", errno);
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews }
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews }
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews#else
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
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews const impl::path prog("umount");
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews tools::process::argv_array argv("umount", abs_path.c_str(), NULL);
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews
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#endif
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews}
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewsstatic
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewsstd::string
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewsnormalize(const std::string& in)
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews{
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews assert(!in.empty());
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews std::string out;
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews std::string::size_type pos = 0;
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews do {
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews const std::string::size_type next_pos = in.find('/', pos);
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews const std::string component = in.substr(pos, next_pos - pos);
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews if (!component.empty()) {
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews if (pos == 0)
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews out += component;
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews else if (component != ".")
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews out += "/" + component;
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews }
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews if (next_pos == std::string::npos)
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews pos = next_pos;
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews else
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews pos = next_pos + 1;
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews } while (pos != std::string::npos);
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews return out.empty() ? "/" : out;
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews}
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews// ------------------------------------------------------------------------
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews// The "path" class.
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews// ------------------------------------------------------------------------
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewsimpl::path::path(const std::string& s) :
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews m_data(normalize(s))
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews{
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews}
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewsimpl::path::~path(void)
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews{
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews}
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewsconst char*
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewsimpl::path::c_str(void)
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews const
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews{
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews return m_data.c_str();
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews}
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewsstd::string
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewsimpl::path::str(void)
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews const
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews{
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews return m_data;
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews}
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewsbool
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewsimpl::path::is_absolute(void)
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews const
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews{
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews return !m_data.empty() && m_data[0] == '/';
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews}
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewsbool
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewsimpl::path::is_root(void)
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews const
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews{
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews return m_data == "/";
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews}
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewsimpl::path
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewsimpl::path::branch_path(void)
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews const
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews{
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews const std::string::size_type endpos = m_data.rfind('/');
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews if (endpos == std::string::npos)
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews return path(".");
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews else if (endpos == 0)
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews return path("/");
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews else
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews return path(m_data.substr(0, endpos));
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews}
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewsstd::string
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewsimpl::path::leaf_name(void)
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews const
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews{
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews std::string::size_type begpos = m_data.rfind('/');
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews if (begpos == std::string::npos)
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews begpos = 0;
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews else
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews begpos++;
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews return m_data.substr(begpos);
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews}
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewsimpl::path
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewsimpl::path::to_absolute(void)
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews const
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews{
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews assert(!is_absolute());
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews return get_current_dir() / m_data;
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews}
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewsbool
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewsimpl::path::operator==(const path& p)
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews const
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews{
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews return m_data == p.m_data;
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews}
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewsbool
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewsimpl::path::operator!=(const path& p)
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews const
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews{
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews return m_data != p.m_data;
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews}
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewsimpl::path
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewsimpl::path::operator/(const std::string& p)
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews const
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews{
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews return path(m_data + "/" + normalize(p));
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews}
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewsimpl::path
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewsimpl::path::operator/(const path& p)
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews const
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews{
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews return path(m_data) / p.m_data;
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews}
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewsbool
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewsimpl::path::operator<(const path& p)
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews const
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews{
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews return std::strcmp(m_data.c_str(), p.m_data.c_str()) < 0;
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews}
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews// ------------------------------------------------------------------------
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews// The "file_info" class.
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews// ------------------------------------------------------------------------
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewsconst int impl::file_info::blk_type = 1;
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewsconst int impl::file_info::chr_type = 2;
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewsconst int impl::file_info::dir_type = 3;
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewsconst int impl::file_info::fifo_type = 4;
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewsconst int impl::file_info::lnk_type = 5;
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewsconst int impl::file_info::reg_type = 6;
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewsconst int impl::file_info::sock_type = 7;
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewsconst int impl::file_info::wht_type = 8;
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewsimpl::file_info::file_info(const path& p)
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews{
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews if (lstat(p.c_str(), &m_sb) == -1)
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews throw system_error(IMPL_NAME "::file_info",
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews "Cannot get information of " + p.str() + "; " +
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews "lstat(2) failed", errno);
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews int type = m_sb.st_mode & S_IFMT;
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews switch (type) {
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews case S_IFBLK: m_type = blk_type; break;
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews case S_IFCHR: m_type = chr_type; break;
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews case S_IFDIR: m_type = dir_type; break;
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews case S_IFIFO: m_type = fifo_type; break;
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews case S_IFLNK: m_type = lnk_type; break;
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews case S_IFREG: m_type = reg_type; break;
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews case S_IFSOCK: m_type = sock_type; break;
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews#if defined(S_IFWHT)
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews case S_IFWHT: m_type = wht_type; break;
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews#endif
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews default:
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews throw system_error(IMPL_NAME "::file_info", "Unknown file type "
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews "error", EINVAL);
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews }
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews}
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewsimpl::file_info::~file_info(void)
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews{
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews}
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewsdev_t
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewsimpl::file_info::get_device(void)
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews const
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews{
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews return m_sb.st_dev;
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews}
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewsino_t
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewsimpl::file_info::get_inode(void)
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews const
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews{
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews return m_sb.st_ino;
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews}
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewsmode_t
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewsimpl::file_info::get_mode(void)
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews const
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews{
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews return m_sb.st_mode & ~S_IFMT;
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews}
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewsoff_t
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewsimpl::file_info::get_size(void)
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews const
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews{
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews return m_sb.st_size;
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews}
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewsint
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewsimpl::file_info::get_type(void)
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews const
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews{
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews return m_type;
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews}
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewsbool
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewsimpl::file_info::is_owner_readable(void)
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews const
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews{
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews return m_sb.st_mode & S_IRUSR;
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews}
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewsbool
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewsimpl::file_info::is_owner_writable(void)
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews const
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews{
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews return m_sb.st_mode & S_IWUSR;
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews}
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewsbool
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewsimpl::file_info::is_owner_executable(void)
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews const
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews{
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews return m_sb.st_mode & S_IXUSR;
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews}
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewsbool
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewsimpl::file_info::is_group_readable(void)
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews const
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews{
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews return m_sb.st_mode & S_IRGRP;
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews}
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewsbool
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewsimpl::file_info::is_group_writable(void)
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews const
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews{
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews return m_sb.st_mode & S_IWGRP;
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews}
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewsbool
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewsimpl::file_info::is_group_executable(void)
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews const
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews{
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews return m_sb.st_mode & S_IXGRP;
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews}
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewsbool
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewsimpl::file_info::is_other_readable(void)
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews const
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews{
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews return m_sb.st_mode & S_IROTH;
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews}
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewsbool
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewsimpl::file_info::is_other_writable(void)
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews const
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews{
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews return m_sb.st_mode & S_IWOTH;
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews}
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewsbool
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewsimpl::file_info::is_other_executable(void)
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews const
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews{
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews return m_sb.st_mode & S_IXOTH;
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews}
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews// ------------------------------------------------------------------------
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews// The "directory" class.
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews// ------------------------------------------------------------------------
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewsimpl::directory::directory(const path& p)
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews{
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews DIR* dp = ::opendir(p.c_str());
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews if (dp == NULL)
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews throw system_error(IMPL_NAME "::directory::directory(" +
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews p.str() + ")", "opendir(3) failed", errno);
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews struct dirent* dep;
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews while ((dep = ::readdir(dp)) != NULL) {
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews path entryp = p / dep->d_name;
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews insert(value_type(dep->d_name, file_info(entryp)));
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews }
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews if (::closedir(dp) == -1)
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews throw system_error(IMPL_NAME "::directory::directory(" +
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews p.str() + ")", "closedir(3) failed", errno);
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews}
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewsstd::set< std::string >
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewsimpl::directory::names(void)
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews const
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews{
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews std::set< std::string > ns;
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews for (const_iterator iter = begin(); iter != end(); iter++)
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews ns.insert((*iter).first);
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews return ns;
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews}
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews// ------------------------------------------------------------------------
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews// The "temp_dir" class.
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews// ------------------------------------------------------------------------
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewsimpl::temp_dir::temp_dir(const path& p)
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews{
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews tools::auto_array< char > buf(new char[p.str().length() + 1]);
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews std::strcpy(buf.get(), p.c_str());
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews if (::mkdtemp(buf.get()) == NULL)
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews throw tools::system_error(IMPL_NAME "::temp_dir::temp_dir(" +
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews p.str() + ")", "mkdtemp(3) failed",
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews errno);
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews m_path.reset(new path(buf.get()));
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews}
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewsimpl::temp_dir::~temp_dir(void)
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews{
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews cleanup(*m_path);
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews}
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewsconst impl::path&
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewsimpl::temp_dir::get_path(void)
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews const
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews{
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews return *m_path;
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews}
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews// ------------------------------------------------------------------------
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews// Free functions.
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews// ------------------------------------------------------------------------
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewsbool
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewsimpl::exists(const path& p)
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews{
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews try {
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews eaccess(p, access_f);
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews return true;
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews } catch (const system_error& e) {
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews if (e.code() == ENOENT)
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews return false;
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews else
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews throw;
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews }
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews}
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewsbool
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewsimpl::have_prog_in_path(const std::string& prog)
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews{
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews assert(prog.find('/') == std::string::npos);
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews
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 if (!tools::env::has("PATH"))
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews throw std::runtime_error("PATH not defined in the environment");
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews std::vector< std::string > dirs =
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews tools::text::split(tools::env::get("PATH"), ":");
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews bool found = false;
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews for (std::vector< std::string >::const_iterator iter = dirs.begin();
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews !found && iter != dirs.end(); iter++) {
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews const path& dir = path(*iter);
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews if (is_executable(dir / prog))
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews found = true;
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews }
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews return found;
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews}
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewsbool
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewsimpl::is_executable(const path& p)
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews{
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews if (!exists(p))
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews return false;
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews return safe_access(p, access_x, EACCES);
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews}
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewsvoid
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewsimpl::remove(const path& p)
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews{
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 EPERM);
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews if (::unlink(p.c_str()) == -1)
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews throw tools::system_error(IMPL_NAME "::remove(" + p.str() + ")",
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews "unlink(" + p.str() + ") failed",
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews errno);
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews}
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewsvoid
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewsimpl::rmdir(const path& p)
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews{
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews if (::rmdir(p.c_str())) {
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews if (errno == EEXIST) {
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 errno = ENOTEMPTY;
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews }
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews throw system_error(IMPL_NAME "::rmdir", "Cannot remove directory",
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews errno);
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews }
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews}
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewsimpl::path
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewsimpl::change_directory(const path& dir)
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews{
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews path olddir = get_current_dir();
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews if (olddir != dir) {
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews if (::chdir(dir.c_str()) == -1)
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews throw tools::system_error(IMPL_NAME "::chdir(" + dir.str() + ")",
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews "chdir(2) failed", errno);
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews }
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews return olddir;
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews}
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewsvoid
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewsimpl::cleanup(const path& p)
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews{
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews impl::file_info fi(p);
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews cleanup_aux(p, fi.get_device(), true);
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews}
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewsimpl::path
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrewsimpl::get_current_dir(void)
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews{
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews std::auto_ptr< char > cwd;
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews#if defined(HAVE_GETCWD_DYN)
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews cwd.reset(getcwd(NULL, 0));
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews#else
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews cwd.reset(getcwd(NULL, MAXPATHLEN));
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews#endif
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews if (cwd.get() == NULL)
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews throw tools::system_error(IMPL_NAME "::get_current_dir()",
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews "getcwd() failed", errno);
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews return path(cwd.get());
4a53e3c2b83c476a93148eaee0272649beb221caMark Andrews}