mirror of
https://git.hardenedbsd.org/hardenedbsd/HardenedBSD.git
synced 2024-11-26 10:53:39 +01:00
b0d29bc47d
Having kyua in the base system will simplify automated testing in CI and eliminates bootstrapping issues on new platforms. The build of kyua is controlled by WITH(OUT)_TESTS_SUPPORT. Reviewed by: emaste Obtained from: CheriBSD Sponsored by: DARPA Differential Revision: https://reviews.freebsd.org/D24103
361 lines
10 KiB
C++
361 lines
10 KiB
C++
// Copyright 2015 The Kyua Authors.
|
|
// All rights reserved.
|
|
//
|
|
// Redistribution and use in source and binary forms, with or without
|
|
// modification, are permitted provided that the following conditions are
|
|
// met:
|
|
//
|
|
// * Redistributions of source code must retain the above copyright
|
|
// notice, this list of conditions and the following disclaimer.
|
|
// * Redistributions in binary form must reproduce the above copyright
|
|
// notice, this list of conditions and the following disclaimer in the
|
|
// documentation and/or other materials provided with the distribution.
|
|
// * Neither the name of Google Inc. nor the names of its contributors
|
|
// may be used to endorse or promote products derived from this software
|
|
// without specific prior written permission.
|
|
//
|
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
#include "utils/fs/directory.hpp"
|
|
|
|
extern "C" {
|
|
#include <sys/types.h>
|
|
|
|
#include <dirent.h>
|
|
}
|
|
|
|
#include <cerrno>
|
|
#include <memory>
|
|
|
|
#include "utils/format/macros.hpp"
|
|
#include "utils/fs/exceptions.hpp"
|
|
#include "utils/fs/path.hpp"
|
|
#include "utils/noncopyable.hpp"
|
|
#include "utils/sanity.hpp"
|
|
#include "utils/text/operations.ipp"
|
|
|
|
namespace detail = utils::fs::detail;
|
|
namespace fs = utils::fs;
|
|
namespace text = utils::text;
|
|
|
|
|
|
/// Constructs a new directory entry.
|
|
///
|
|
/// \param name_ Name of the directory entry.
|
|
fs::directory_entry::directory_entry(const std::string& name_) : name(name_)
|
|
{
|
|
}
|
|
|
|
|
|
/// Checks if two directory entries are equal.
|
|
///
|
|
/// \param other The entry to compare to.
|
|
///
|
|
/// \return True if the two entries are equal; false otherwise.
|
|
bool
|
|
fs::directory_entry::operator==(const directory_entry& other) const
|
|
{
|
|
return name == other.name;
|
|
}
|
|
|
|
|
|
/// Checks if two directory entries are different.
|
|
///
|
|
/// \param other The entry to compare to.
|
|
///
|
|
/// \return True if the two entries are different; false otherwise.
|
|
bool
|
|
fs::directory_entry::operator!=(const directory_entry& other) const
|
|
{
|
|
return !(*this == other);
|
|
}
|
|
|
|
|
|
/// Checks if this entry sorts before another entry.
|
|
///
|
|
/// \param other The entry to compare to.
|
|
///
|
|
/// \return True if this entry sorts before the other entry; false otherwise.
|
|
bool
|
|
fs::directory_entry::operator<(const directory_entry& other) const
|
|
{
|
|
return name < other.name;
|
|
}
|
|
|
|
|
|
/// Formats a directory entry.
|
|
///
|
|
/// \param output Stream into which to inject the formatted entry.
|
|
/// \param entry The entry to format.
|
|
///
|
|
/// \return A reference to output.
|
|
std::ostream&
|
|
fs::operator<<(std::ostream& output, const directory_entry& entry)
|
|
{
|
|
output << F("directory_entry{name=%s}") % text::quote(entry.name, '\'');
|
|
return output;
|
|
}
|
|
|
|
|
|
/// Internal implementation details for the directory_iterator.
|
|
///
|
|
/// In order to support multiple concurrent iterators over the same directory
|
|
/// object, this class is the one that performs all directory-level accesses.
|
|
/// In particular, even if it may seem surprising, this is the class that
|
|
/// handles the DIR object for the directory.
|
|
///
|
|
/// Note that iterators implemented by this class do not rely on the container
|
|
/// directory class at all. This should not be relied on for object lifecycle
|
|
/// purposes.
|
|
struct utils::fs::detail::directory_iterator::impl : utils::noncopyable {
|
|
/// Path of the directory accessed by this iterator.
|
|
const fs::path _path;
|
|
|
|
/// Raw pointer to the system representation of the directory.
|
|
///
|
|
/// We also use this to determine if the iterator is valid (at the end) or
|
|
/// not. A null pointer means an invalid iterator.
|
|
::DIR* _dirp;
|
|
|
|
/// Raw representation of the system directory entry.
|
|
///
|
|
/// We need to keep this at the class level so that we can use the
|
|
/// readdir_r(3) function.
|
|
::dirent _dirent;
|
|
|
|
/// Custom representation of the directory entry.
|
|
///
|
|
/// This is separate from _dirent because this is the type we return to the
|
|
/// user. We must keep this as a pointer so that we can support the common
|
|
/// operators (* and ->) over iterators.
|
|
std::auto_ptr< directory_entry > _entry;
|
|
|
|
/// Constructs an iterator pointing to the "end" of the directory.
|
|
impl(void) : _path("invalid-directory-entry"), _dirp(NULL)
|
|
{
|
|
}
|
|
|
|
/// Constructs a new iterator to start scanning a directory.
|
|
///
|
|
/// \param path The directory that will be scanned.
|
|
///
|
|
/// \throw system_error If there is a problem opening the directory.
|
|
explicit impl(const path& path) : _path(path)
|
|
{
|
|
DIR* dirp = ::opendir(_path.c_str());
|
|
if (dirp == NULL) {
|
|
const int original_errno = errno;
|
|
throw fs::system_error(F("opendir(%s) failed") % _path,
|
|
original_errno);
|
|
}
|
|
_dirp = dirp;
|
|
|
|
// Initialize our first directory entry. Note that this may actually
|
|
// close the directory we just opened if the directory happens to be
|
|
// empty -- but directories are never empty because they at least have
|
|
// '.' and '..' entries.
|
|
next();
|
|
}
|
|
|
|
/// Destructor.
|
|
///
|
|
/// This closes the directory if still open.
|
|
~impl(void)
|
|
{
|
|
if (_dirp != NULL)
|
|
close();
|
|
}
|
|
|
|
/// Closes the directory and invalidates the iterator.
|
|
void
|
|
close(void)
|
|
{
|
|
PRE(_dirp != NULL);
|
|
if (::closedir(_dirp) == -1) {
|
|
UNREACHABLE_MSG("Invalid dirp provided to closedir(3)");
|
|
}
|
|
_dirp = NULL;
|
|
}
|
|
|
|
/// Advances the directory entry to the next one.
|
|
///
|
|
/// It is possible to use this function on a new directory_entry object to
|
|
/// initialize the first entry.
|
|
///
|
|
/// \throw system_error If the call to readdir_r fails.
|
|
void
|
|
next(void)
|
|
{
|
|
::dirent* result;
|
|
|
|
if (::readdir_r(_dirp, &_dirent, &result) == -1) {
|
|
const int original_errno = errno;
|
|
throw fs::system_error(F("readdir_r(%s) failed") % _path,
|
|
original_errno);
|
|
}
|
|
if (result == NULL) {
|
|
_entry.reset(NULL);
|
|
close();
|
|
} else {
|
|
_entry.reset(new directory_entry(_dirent.d_name));
|
|
}
|
|
}
|
|
};
|
|
|
|
|
|
/// Constructs a new directory iterator.
|
|
///
|
|
/// \param pimpl The constructed internal implementation structure to use.
|
|
detail::directory_iterator::directory_iterator(std::shared_ptr< impl > pimpl) :
|
|
_pimpl(pimpl)
|
|
{
|
|
}
|
|
|
|
|
|
/// Destructor.
|
|
detail::directory_iterator::~directory_iterator(void)
|
|
{
|
|
}
|
|
|
|
|
|
/// Creates a new directory iterator for a directory.
|
|
///
|
|
/// \return The directory iterator. Note that the result may be invalid.
|
|
///
|
|
/// \throw system_error If opening the directory or reading its first entry
|
|
/// fails.
|
|
detail::directory_iterator
|
|
detail::directory_iterator::new_begin(const path& path)
|
|
{
|
|
return directory_iterator(std::shared_ptr< impl >(new impl(path)));
|
|
}
|
|
|
|
|
|
/// Creates a new invalid directory iterator.
|
|
///
|
|
/// \return The invalid directory iterator.
|
|
detail::directory_iterator
|
|
detail::directory_iterator::new_end(void)
|
|
{
|
|
return directory_iterator(std::shared_ptr< impl >(new impl()));
|
|
}
|
|
|
|
|
|
/// Checks if two iterators are equal.
|
|
///
|
|
/// We consider two iterators to be equal if both of them are invalid or,
|
|
/// otherwise, if they have the exact same internal representation (as given by
|
|
/// equality of the pimpl pointers).
|
|
///
|
|
/// \param other The object to compare to.
|
|
///
|
|
/// \return True if the two iterators are equal; false otherwise.
|
|
bool
|
|
detail::directory_iterator::operator==(const directory_iterator& other) const
|
|
{
|
|
return (_pimpl->_dirp == NULL && other._pimpl->_dirp == NULL) ||
|
|
_pimpl == other._pimpl;
|
|
}
|
|
|
|
|
|
/// Checks if two iterators are different.
|
|
///
|
|
/// \param other The object to compare to.
|
|
///
|
|
/// \return True if the two iterators are different; false otherwise.
|
|
bool
|
|
detail::directory_iterator::operator!=(const directory_iterator& other) const
|
|
{
|
|
return !(*this == other);
|
|
}
|
|
|
|
|
|
/// Moves the iterator one element forward.
|
|
///
|
|
/// \return A reference to the iterator.
|
|
///
|
|
/// \throw system_error If advancing the iterator fails.
|
|
detail::directory_iterator&
|
|
detail::directory_iterator::operator++(void)
|
|
{
|
|
_pimpl->next();
|
|
return *this;
|
|
}
|
|
|
|
|
|
/// Dereferences the iterator to its contents.
|
|
///
|
|
/// \return A reference to the directory entry pointed to by the iterator.
|
|
const fs::directory_entry&
|
|
detail::directory_iterator::operator*(void) const
|
|
{
|
|
PRE(_pimpl->_entry.get() != NULL);
|
|
return *_pimpl->_entry;
|
|
}
|
|
|
|
|
|
/// Dereferences the iterator to its contents.
|
|
///
|
|
/// \return A pointer to the directory entry pointed to by the iterator.
|
|
const fs::directory_entry*
|
|
detail::directory_iterator::operator->(void) const
|
|
{
|
|
PRE(_pimpl->_entry.get() != NULL);
|
|
return _pimpl->_entry.get();
|
|
}
|
|
|
|
|
|
/// Internal implementation details for the directory.
|
|
struct utils::fs::directory::impl : utils::noncopyable {
|
|
/// Path to the directory to scan.
|
|
fs::path _path;
|
|
|
|
/// Constructs a new directory.
|
|
///
|
|
/// \param path_ Path to the directory to scan.
|
|
impl(const fs::path& path_) : _path(path_)
|
|
{
|
|
}
|
|
};
|
|
|
|
|
|
/// Constructs a new directory.
|
|
///
|
|
/// \param path_ Path to the directory to scan.
|
|
fs::directory::directory(const path& path_) : _pimpl(new impl(path_))
|
|
{
|
|
}
|
|
|
|
|
|
/// Returns an iterator to start scanning the directory.
|
|
///
|
|
/// \return An iterator on the directory.
|
|
///
|
|
/// \throw system_error If the directory cannot be opened to obtain its first
|
|
/// entry.
|
|
fs::directory::const_iterator
|
|
fs::directory::begin(void) const
|
|
{
|
|
return const_iterator::new_begin(_pimpl->_path);
|
|
}
|
|
|
|
|
|
/// Returns an invalid iterator to check for the end of an scan.
|
|
///
|
|
/// \return An invalid iterator.
|
|
fs::directory::const_iterator
|
|
fs::directory::end(void) const
|
|
{
|
|
return const_iterator::new_end();
|
|
}
|