Merge "Undo symlinked libc++fs" into rvc-dev am: a1482062ec

Change-Id: I1f235b848566b501f5676842ed1c2f2f24945c72
This commit is contained in:
Chris Weir 2020-04-21 21:08:43 +00:00 committed by Automerger Merge Worker
commit e1f9a37753
9 changed files with 5337 additions and 37 deletions

View file

@ -23,7 +23,7 @@
#include <android-base/logging.h>
#include <android/hidl/manager/1.2/IServiceManager.h>
#include <filesystem>
#include <automotive/filesystem>
#include <fstream>
#include <regex>
@ -31,6 +31,7 @@ namespace android::hardware::automotive::can::V1_0::implementation {
using IfId = ICanController::BusConfig::InterfaceId;
using IfIdDisc = ICanController::BusConfig::InterfaceId::hidl_discriminator;
namespace fs = android::hardware::automotive::filesystem;
namespace fsErrors {
static const std::error_code ok;
@ -42,10 +43,10 @@ static const std::error_code eacces(EACCES, std::generic_category());
/* In the /sys/devices tree, there are files called "serial", which contain the serial numbers
* for various devices. The exact location inside of this directory is dependent upon the
* hardware we are running on, so we have to start from /sys/devices and work our way down. */
static const std::filesystem::path kDevPath("/sys/devices/");
static const fs::path kDevPath("/sys/devices/");
static const std::regex kTtyRe("^tty[A-Z]+[0-9]+$");
static constexpr auto kOpts = ~(std::filesystem::directory_options::follow_directory_symlink |
std::filesystem::directory_options::skip_permission_denied);
static constexpr auto kOpts = ~(fs::directory_options::follow_directory_symlink |
fs::directory_options::skip_permission_denied);
/**
* A helper object to associate the interface name and type of a USB to CAN adapter.
@ -72,16 +73,16 @@ static bool isValidName(const std::string& name) {
* \param serialPath - Absolute path to a "serial" file for a given device in /sys.
* \return A populated UsbCanIface. On failure, nullopt is returned.
*/
static std::optional<UsbCanIface> getIfaceName(std::filesystem::path serialPath) {
static std::optional<UsbCanIface> getIfaceName(fs::path serialPath) {
std::error_code fsStatus;
// Since the path is to a file called "serial", we need to search its parent directory.
std::filesystem::recursive_directory_iterator fsItr(serialPath.parent_path(), kOpts, fsStatus);
fs::recursive_directory_iterator fsItr(serialPath.parent_path(), kOpts, fsStatus);
if (fsStatus != fsErrors::ok) {
LOG(ERROR) << "Failed to open " << serialPath.parent_path();
return std::nullopt;
}
for (; fsStatus == fsErrors::ok && fsItr != std::filesystem::recursive_directory_iterator();
for (; fsStatus == fsErrors::ok && fsItr != fs::recursive_directory_iterator();
fsItr.increment(fsStatus)) {
/* We want either a directory called "net" or a directory that looks like tty<something>, so
* skip files. */
@ -95,7 +96,7 @@ static std::optional<UsbCanIface> getIfaceName(std::filesystem::path serialPath)
if (currentDir == "net") {
/* This device is a SocketCAN device. The iface name is the only directory under
* net/. Multiple directories under net/ is an error.*/
std::filesystem::directory_iterator netItr(fsItr->path(), kOpts, fsStatus);
fs::directory_iterator netItr(fsItr->path(), kOpts, fsStatus);
if (fsStatus != fsErrors::ok) {
LOG(ERROR) << "Failed to open " << fsItr->path() << " to get net name!";
return std::nullopt;
@ -111,7 +112,7 @@ static std::optional<UsbCanIface> getIfaceName(std::filesystem::path serialPath)
LOG(ERROR) << "Failed to verify " << fsItr->path() << " has valid net name!";
return std::nullopt;
}
if (netItr != std::filesystem::directory_iterator()) {
if (netItr != fs::directory_iterator()) {
// There should never be more than one name under net/
LOG(ERROR) << "Found more than one net name in " << fsItr->path() << "!";
return std::nullopt;
@ -157,13 +158,13 @@ static std::optional<std::string> readSerialNo(const std::string& serialnoPath)
*/
static std::optional<UsbCanIface> findUsbDevice(const hidl_vec<hidl_string>& configSerialnos) {
std::error_code fsStatus;
std::filesystem::recursive_directory_iterator fsItr(kDevPath, kOpts, fsStatus);
fs::recursive_directory_iterator fsItr(kDevPath, kOpts, fsStatus);
if (fsStatus != fsErrors::ok) {
LOG(ERROR) << "Failed to open " << kDevPath;
return std::nullopt;
}
for (; fsStatus == fsErrors::ok && fsItr != std::filesystem::recursive_directory_iterator();
for (; fsStatus == fsErrors::ok && fsItr != fs::recursive_directory_iterator();
fsItr.increment(fsStatus)) {
// We want to find a file called "serial", which is in a directory somewhere. Skip files.
bool isDir = fsItr->is_directory(fsStatus);
@ -174,7 +175,7 @@ static std::optional<UsbCanIface> findUsbDevice(const hidl_vec<hidl_string>& con
if (!isDir) continue;
auto serialnoPath = fsItr->path() / "serial";
bool isReg = std::filesystem::is_regular_file(serialnoPath, fsStatus);
bool isReg = fs::is_regular_file(serialnoPath, fsStatus);
/* Make sure we have permissions to this directory, ignore enoent, since the file
* "serial" may not exist, which is ok. */

View file

@ -0,0 +1,13 @@
BasedOnStyle: LLVM
---
Language: Cpp
Standard: Cpp03
AlwaysBreakTemplateDeclarations: true
PointerAlignment: Left
# Disable formatting options which may break tests.
SortIncludes: false
ReflowComments: false
---

View file

@ -19,7 +19,6 @@
cc_defaults {
name: "android.hardware.automotive@libc++fsdefaults",
host_supported: true,
local_include_dirs: ["include"],
export_include_dirs: ["include"],
cflags: [
@ -28,33 +27,12 @@ cc_defaults {
"-Wno-unused-parameter",
],
cppflags: [
"-std=c++14",
"-std=c++17",
"-fexceptions",
"-DLIBCXX_BUILDING_LIBCXXABI",
"-D_LIBCPP_BUILDING_LIBRARY",
],
rtti: true,
stl: "none",
target: {
linux_bionic: {
enabled: true,
},
windows: {
enabled: true,
cflags: [
"-D_LIBCPP_HAS_THREAD_API_WIN32",
"-D_LIBCXXABI_BUILDING_LIBRARY",
"-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
"-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
"-UWIN32_LEAN_AND_MEAN",
],
},
windows_x86: {
cflags: [
"-fsjlj-exceptions",
],
},
},
}
cc_library_static {

View file

@ -1 +0,0 @@
../../../../../../../external/libcxx/include/

File diff suppressed because it is too large Load diff

View file

@ -1 +0,0 @@
../../../../../../../external/libcxx/src/

View file

@ -0,0 +1,397 @@
//===------------------ directory_iterator.cpp ----------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is dual licensed under the MIT and the University of Illinois Open
// Source Licenses. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
/* clang-format off */
#include "automotive/filesystem"
#include <__config>
#if defined(_LIBCPP_WIN32API)
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#else
#include <dirent.h>
#endif
#include <errno.h>
#include "filesystem_common.h"
namespace android::hardware::automotive::filesystem {
namespace detail {
namespace {
#if !defined(_LIBCPP_WIN32API)
template <class DirEntT, class = decltype(DirEntT::d_type)>
static file_type get_file_type(DirEntT* ent, int) {
switch (ent->d_type) {
case DT_BLK:
return file_type::block;
case DT_CHR:
return file_type::character;
case DT_DIR:
return file_type::directory;
case DT_FIFO:
return file_type::fifo;
case DT_LNK:
return file_type::symlink;
case DT_REG:
return file_type::regular;
case DT_SOCK:
return file_type::socket;
// Unlike in lstat, hitting "unknown" here simply means that the underlying
// filesystem doesn't support d_type. Report is as 'none' so we correctly
// set the cache to empty.
case DT_UNKNOWN:
break;
}
return file_type::none;
}
template <class DirEntT>
static file_type get_file_type(DirEntT* ent, long) {
return file_type::none;
}
static pair<string_view, file_type> posix_readdir(DIR* dir_stream,
error_code& ec) {
struct dirent* dir_entry_ptr = nullptr;
errno = 0; // zero errno in order to detect errors
ec.clear();
if ((dir_entry_ptr = ::readdir(dir_stream)) == nullptr) {
if (errno)
ec = capture_errno();
return {};
} else {
return {dir_entry_ptr->d_name, get_file_type(dir_entry_ptr, 0)};
}
}
#else
static file_type get_file_type(const WIN32_FIND_DATA& data) {
//auto attrs = data.dwFileAttributes;
// FIXME(EricWF)
return file_type::unknown;
}
static uintmax_t get_file_size(const WIN32_FIND_DATA& data) {
return (data.nFileSizeHight * (MAXDWORD + 1)) + data.nFileSizeLow;
}
static file_time_type get_write_time(const WIN32_FIND_DATA& data) {
ULARGE_INTEGER tmp;
FILETIME& time = data.ftLastWriteTime;
tmp.u.LowPart = time.dwLowDateTime;
tmp.u.HighPart = time.dwHighDateTime;
return file_time_type(file_time_type::duration(time.QuadPart));
}
#endif
} // namespace
} // namespace detail
using detail::ErrorHandler;
#if defined(_LIBCPP_WIN32API)
class __dir_stream {
public:
__dir_stream() = delete;
__dir_stream& operator=(const __dir_stream&) = delete;
__dir_stream(__dir_stream&& __ds) noexcept : __stream_(__ds.__stream_),
__root_(move(__ds.__root_)),
__entry_(move(__ds.__entry_)) {
__ds.__stream_ = INVALID_HANDLE_VALUE;
}
__dir_stream(const path& root, directory_options opts, error_code& ec)
: __stream_(INVALID_HANDLE_VALUE), __root_(root) {
__stream_ = ::FindFirstFileEx(root.c_str(), &__data_);
if (__stream_ == INVALID_HANDLE_VALUE) {
ec = error_code(::GetLastError(), generic_category());
const bool ignore_permission_denied =
bool(opts & directory_options::skip_permission_denied);
if (ignore_permission_denied && ec.value() == ERROR_ACCESS_DENIED)
ec.clear();
return;
}
}
~__dir_stream() noexcept {
if (__stream_ == INVALID_HANDLE_VALUE)
return;
close();
}
bool good() const noexcept { return __stream_ != INVALID_HANDLE_VALUE; }
bool advance(error_code& ec) {
while (::FindNextFile(__stream_, &__data_)) {
if (!strcmp(__data_.cFileName, ".") || strcmp(__data_.cFileName, ".."))
continue;
// FIXME: Cache more of this
//directory_entry::__cached_data cdata;
//cdata.__type_ = get_file_type(__data_);
//cdata.__size_ = get_file_size(__data_);
//cdata.__write_time_ = get_write_time(__data_);
__entry_.__assign_iter_entry(
__root_ / __data_.cFileName,
directory_entry::__create_iter_result(get_file_type(__data)));
return true;
}
ec = error_code(::GetLastError(), generic_category());
close();
return false;
}
private:
error_code close() noexcept {
error_code ec;
if (!::FindClose(__stream_))
ec = error_code(::GetLastError(), generic_category());
__stream_ = INVALID_HANDLE_VALUE;
return ec;
}
HANDLE __stream_{INVALID_HANDLE_VALUE};
WIN32_FIND_DATA __data_;
public:
path __root_;
directory_entry __entry_;
};
#else
class __dir_stream {
public:
__dir_stream() = delete;
__dir_stream& operator=(const __dir_stream&) = delete;
__dir_stream(__dir_stream&& other) noexcept : __stream_(other.__stream_),
__root_(move(other.__root_)),
__entry_(move(other.__entry_)) {
other.__stream_ = nullptr;
}
__dir_stream(const path& root, directory_options opts, error_code& ec)
: __stream_(nullptr), __root_(root) {
if ((__stream_ = ::opendir(root.c_str())) == nullptr) {
ec = detail::capture_errno();
const bool allow_eacess =
bool(opts & directory_options::skip_permission_denied);
if (allow_eacess && ec.value() == EACCES)
ec.clear();
return;
}
advance(ec);
}
~__dir_stream() noexcept {
if (__stream_)
close();
}
bool good() const noexcept { return __stream_ != nullptr; }
bool advance(error_code& ec) {
while (true) {
auto str_type_pair = detail::posix_readdir(__stream_, ec);
auto& str = str_type_pair.first;
if (str == "." || str == "..") {
continue;
} else if (ec || str.empty()) {
close();
return false;
} else {
__entry_.__assign_iter_entry(
__root_ / str,
directory_entry::__create_iter_result(str_type_pair.second));
return true;
}
}
}
private:
error_code close() noexcept {
error_code m_ec;
if (::closedir(__stream_) == -1)
m_ec = detail::capture_errno();
__stream_ = nullptr;
return m_ec;
}
DIR* __stream_{nullptr};
public:
path __root_;
directory_entry __entry_;
};
#endif
// directory_iterator
directory_iterator::directory_iterator(const path& p, error_code* ec,
directory_options opts) {
ErrorHandler<void> err("directory_iterator::directory_iterator(...)", ec, &p);
error_code m_ec;
__imp_ = make_shared<__dir_stream>(p, opts, m_ec);
if (ec)
*ec = m_ec;
if (!__imp_->good()) {
__imp_.reset();
if (m_ec)
err.report(m_ec);
}
}
directory_iterator& directory_iterator::__increment(error_code* ec) {
_LIBCPP_ASSERT(__imp_, "Attempting to increment an invalid iterator");
ErrorHandler<void> err("directory_iterator::operator++()", ec);
error_code m_ec;
if (!__imp_->advance(m_ec)) {
path root = move(__imp_->__root_);
__imp_.reset();
if (m_ec)
err.report(m_ec, "at root \"%s\"", root);
}
return *this;
}
directory_entry const& directory_iterator::__dereference() const {
_LIBCPP_ASSERT(__imp_, "Attempting to dereference an invalid iterator");
return __imp_->__entry_;
}
// recursive_directory_iterator
struct recursive_directory_iterator::__shared_imp {
stack<__dir_stream> __stack_;
directory_options __options_;
};
recursive_directory_iterator::recursive_directory_iterator(
const path& p, directory_options opt, error_code* ec)
: __imp_(nullptr), __rec_(true) {
ErrorHandler<void> err("recursive_directory_iterator", ec, &p);
error_code m_ec;
__dir_stream new_s(p, opt, m_ec);
if (m_ec)
err.report(m_ec);
if (m_ec || !new_s.good())
return;
__imp_ = make_shared<__shared_imp>();
__imp_->__options_ = opt;
__imp_->__stack_.push(move(new_s));
}
void recursive_directory_iterator::__pop(error_code* ec) {
_LIBCPP_ASSERT(__imp_, "Popping the end iterator");
if (ec)
ec->clear();
__imp_->__stack_.pop();
if (__imp_->__stack_.size() == 0)
__imp_.reset();
else
__advance(ec);
}
directory_options recursive_directory_iterator::options() const {
return __imp_->__options_;
}
int recursive_directory_iterator::depth() const {
return __imp_->__stack_.size() - 1;
}
const directory_entry& recursive_directory_iterator::__dereference() const {
return __imp_->__stack_.top().__entry_;
}
recursive_directory_iterator&
recursive_directory_iterator::__increment(error_code* ec) {
if (ec)
ec->clear();
if (recursion_pending()) {
if (__try_recursion(ec) || (ec && *ec))
return *this;
}
__rec_ = true;
__advance(ec);
return *this;
}
void recursive_directory_iterator::__advance(error_code* ec) {
ErrorHandler<void> err("recursive_directory_iterator::operator++()", ec);
const directory_iterator end_it;
auto& stack = __imp_->__stack_;
error_code m_ec;
while (stack.size() > 0) {
if (stack.top().advance(m_ec))
return;
if (m_ec)
break;
stack.pop();
}
if (m_ec) {
path root = move(stack.top().__root_);
__imp_.reset();
err.report(m_ec, "at root \"%s\"", root);
} else {
__imp_.reset();
}
}
bool recursive_directory_iterator::__try_recursion(error_code* ec) {
ErrorHandler<void> err("recursive_directory_iterator::operator++()", ec);
bool rec_sym = bool(options() & directory_options::follow_directory_symlink);
auto& curr_it = __imp_->__stack_.top();
bool skip_rec = false;
error_code m_ec;
if (!rec_sym) {
file_status st(curr_it.__entry_.__get_sym_ft(&m_ec));
if (m_ec && status_known(st))
m_ec.clear();
if (m_ec || is_symlink(st) || !is_directory(st))
skip_rec = true;
} else {
file_status st(curr_it.__entry_.__get_ft(&m_ec));
if (m_ec && status_known(st))
m_ec.clear();
if (m_ec || !is_directory(st))
skip_rec = true;
}
if (!skip_rec) {
__dir_stream new_it(curr_it.__entry_.path(), __imp_->__options_, m_ec);
if (new_it.good()) {
__imp_->__stack_.push(move(new_it));
return true;
}
}
if (m_ec) {
const bool allow_eacess =
bool(__imp_->__options_ & directory_options::skip_permission_denied);
if (m_ec.value() == EACCES && allow_eacess) {
if (ec)
ec->clear();
} else {
path at_ent = move(curr_it.__entry_.__p_);
__imp_.reset();
err.report(m_ec, "attempting recursion into \"%s\"", at_ent);
}
}
return false;
}
} // namespace android::hardware::automotive::filesystem
/* clang-format on */

View file

@ -0,0 +1,441 @@
//===----------------------------------------------------------------------===////
//
// The LLVM Compiler Infrastructure
//
// This file is dual licensed under the MIT and the University of Illinois Open
// Source Licenses. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===////
/* clang-format off */
#ifndef AUTO_FILESYSTEM_COMMON_H
#define AUTO_FILESYSTEM_COMMON_H
#include "automotive/filesystem"
#include <array>
#include <chrono>
#include <cstdlib>
#include <climits>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/statvfs.h>
#include <sys/time.h> // for ::utimes as used in __last_write_time
#include <fcntl.h> /* values for fchmodat */
#if !defined(__APPLE__)
// We can use the presence of UTIME_OMIT to detect platforms that provide
// utimensat.
#if defined(UTIME_OMIT)
#define _LIBCPP_USE_UTIMENSAT
#endif
#endif
#if defined(__GNUC__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-function"
#endif
namespace android::hardware::automotive::filesystem {
using namespace std::chrono;
using std::error_code;
using std::is_floating_point;
using std::micro;
using std::nano;
using std::ratio;
namespace detail {
namespace {
static string format_string_imp(const char* msg, ...) {
// we might need a second shot at this, so pre-emptivly make a copy
struct GuardVAList {
va_list& target;
bool active = true;
GuardVAList(va_list& target) : target(target), active(true) {}
void clear() {
if (active)
va_end(target);
active = false;
}
~GuardVAList() {
if (active)
va_end(target);
}
};
va_list args;
va_start(args, msg);
GuardVAList args_guard(args);
va_list args_cp;
va_copy(args_cp, args);
GuardVAList args_copy_guard(args_cp);
std::string result;
array<char, 256> local_buff;
size_t size_with_null = local_buff.size();
auto ret = ::vsnprintf(local_buff.data(), size_with_null, msg, args_cp);
args_copy_guard.clear();
// handle empty expansion
if (ret == 0)
return result;
if (static_cast<size_t>(ret) < size_with_null) {
result.assign(local_buff.data(), static_cast<size_t>(ret));
return result;
}
// we did not provide a long enough buffer on our first attempt. The
// return value is the number of bytes (excluding the null byte) that are
// needed for formatting.
size_with_null = static_cast<size_t>(ret) + 1;
result.__resize_default_init(size_with_null - 1);
ret = ::vsnprintf(&result[0], size_with_null, msg, args);
_LIBCPP_ASSERT(static_cast<size_t>(ret) == (size_with_null - 1), "TODO");
return result;
}
const char* unwrap(string const& s) { return s.c_str(); }
const char* unwrap(path const& p) { return p.native().c_str(); }
template <class Arg>
Arg const& unwrap(Arg const& a) {
static_assert(!is_class<Arg>::value, "cannot pass class here");
return a;
}
template <class... Args>
string format_string(const char* fmt, Args const&... args) {
return format_string_imp(fmt, unwrap(args)...);
}
error_code capture_errno() {
_LIBCPP_ASSERT(errno, "Expected errno to be non-zero");
return error_code(errno, generic_category());
}
template <class T>
T error_value();
template <>
_LIBCPP_CONSTEXPR_AFTER_CXX11 void error_value<void>() {}
template <>
bool error_value<bool>() {
return false;
}
template <>
uintmax_t error_value<uintmax_t>() {
return uintmax_t(-1);
}
template <>
_LIBCPP_CONSTEXPR_AFTER_CXX11 file_time_type error_value<file_time_type>() {
return file_time_type::min();
}
template <>
path error_value<path>() {
return {};
}
template <class T>
struct ErrorHandler {
const char* func_name;
error_code* ec = nullptr;
const path* p1 = nullptr;
const path* p2 = nullptr;
ErrorHandler(const char* fname, error_code* ec, const path* p1 = nullptr,
const path* p2 = nullptr)
: func_name(fname), ec(ec), p1(p1), p2(p2) {
if (ec)
ec->clear();
}
T report(const error_code& m_ec) const {
if (ec) {
*ec = m_ec;
return error_value<T>();
}
string what = string("in ") + func_name;
switch (bool(p1) + bool(p2)) {
case 0:
__throw_filesystem_error(what, m_ec);
case 1:
__throw_filesystem_error(what, *p1, m_ec);
case 2:
__throw_filesystem_error(what, *p1, *p2, m_ec);
}
_LIBCPP_UNREACHABLE();
}
template <class... Args>
T report(const error_code& m_ec, const char* msg, Args const&... args) const {
if (ec) {
*ec = m_ec;
return error_value<T>();
}
string what =
string("in ") + func_name + ": " + format_string(msg, args...);
switch (bool(p1) + bool(p2)) {
case 0:
__throw_filesystem_error(what, m_ec);
case 1:
__throw_filesystem_error(what, *p1, m_ec);
case 2:
__throw_filesystem_error(what, *p1, *p2, m_ec);
}
_LIBCPP_UNREACHABLE();
}
T report(errc const& err) const { return report(make_error_code(err)); }
template <class... Args>
T report(errc const& err, const char* msg, Args const&... args) const {
return report(make_error_code(err), msg, args...);
}
private:
ErrorHandler(ErrorHandler const&) = delete;
ErrorHandler& operator=(ErrorHandler const&) = delete;
};
using chrono::duration;
using chrono::duration_cast;
using TimeSpec = struct ::timespec;
using StatT = struct ::stat;
template <class FileTimeT, class TimeT,
bool IsFloat = is_floating_point<typename FileTimeT::rep>::value>
struct time_util_base {
using rep = typename FileTimeT::rep;
using fs_duration = typename FileTimeT::duration;
using fs_seconds = duration<rep>;
using fs_nanoseconds = duration<rep, nano>;
using fs_microseconds = duration<rep, micro>;
static constexpr rep max_seconds =
duration_cast<fs_seconds>(FileTimeT::duration::max()).count();
static constexpr rep max_nsec =
duration_cast<fs_nanoseconds>(FileTimeT::duration::max() -
fs_seconds(max_seconds))
.count();
static constexpr rep min_seconds =
duration_cast<fs_seconds>(FileTimeT::duration::min()).count();
static constexpr rep min_nsec_timespec =
duration_cast<fs_nanoseconds>(
(FileTimeT::duration::min() - fs_seconds(min_seconds)) +
fs_seconds(1))
.count();
private:
#if _LIBCPP_STD_VER > 11 && !defined(_LIBCPP_HAS_NO_CXX14_CONSTEXPR)
static constexpr fs_duration get_min_nsecs() {
return duration_cast<fs_duration>(
fs_nanoseconds(min_nsec_timespec) -
duration_cast<fs_nanoseconds>(fs_seconds(1)));
}
// Static assert that these values properly round trip.
static_assert(fs_seconds(min_seconds) + get_min_nsecs() ==
FileTimeT::duration::min(),
"value doesn't roundtrip");
static constexpr bool check_range() {
// This kinda sucks, but it's what happens when we don't have __int128_t.
if (sizeof(TimeT) == sizeof(rep)) {
typedef duration<long long, ratio<3600 * 24 * 365> > Years;
return duration_cast<Years>(fs_seconds(max_seconds)) > Years(250) &&
duration_cast<Years>(fs_seconds(min_seconds)) < Years(-250);
}
return max_seconds >= numeric_limits<TimeT>::max() &&
min_seconds <= numeric_limits<TimeT>::min();
}
static_assert(check_range(), "the representable range is unacceptable small");
#endif
};
template <class FileTimeT, class TimeT>
struct time_util_base<FileTimeT, TimeT, true> {
using rep = typename FileTimeT::rep;
using fs_duration = typename FileTimeT::duration;
using fs_seconds = duration<rep>;
using fs_nanoseconds = duration<rep, nano>;
using fs_microseconds = duration<rep, micro>;
static const rep max_seconds;
static const rep max_nsec;
static const rep min_seconds;
static const rep min_nsec_timespec;
};
template <class FileTimeT, class TimeT>
const typename FileTimeT::rep
time_util_base<FileTimeT, TimeT, true>::max_seconds =
duration_cast<fs_seconds>(FileTimeT::duration::max()).count();
template <class FileTimeT, class TimeT>
const typename FileTimeT::rep time_util_base<FileTimeT, TimeT, true>::max_nsec =
duration_cast<fs_nanoseconds>(FileTimeT::duration::max() -
fs_seconds(max_seconds))
.count();
template <class FileTimeT, class TimeT>
const typename FileTimeT::rep
time_util_base<FileTimeT, TimeT, true>::min_seconds =
duration_cast<fs_seconds>(FileTimeT::duration::min()).count();
template <class FileTimeT, class TimeT>
const typename FileTimeT::rep
time_util_base<FileTimeT, TimeT, true>::min_nsec_timespec =
duration_cast<fs_nanoseconds>((FileTimeT::duration::min() -
fs_seconds(min_seconds)) +
fs_seconds(1))
.count();
template <class FileTimeT, class TimeT, class TimeSpecT>
struct time_util : time_util_base<FileTimeT, TimeT> {
using Base = time_util_base<FileTimeT, TimeT>;
using Base::max_nsec;
using Base::max_seconds;
using Base::min_nsec_timespec;
using Base::min_seconds;
using typename Base::fs_duration;
using typename Base::fs_microseconds;
using typename Base::fs_nanoseconds;
using typename Base::fs_seconds;
public:
template <class CType, class ChronoType>
static _LIBCPP_CONSTEXPR_AFTER_CXX11 bool checked_set(CType* out,
ChronoType time) {
using Lim = numeric_limits<CType>;
if (time > Lim::max() || time < Lim::min())
return false;
*out = static_cast<CType>(time);
return true;
}
static _LIBCPP_CONSTEXPR_AFTER_CXX11 bool is_representable(TimeSpecT tm) {
if (tm.tv_sec >= 0) {
return tm.tv_sec < max_seconds ||
(tm.tv_sec == max_seconds && tm.tv_nsec <= max_nsec);
} else if (tm.tv_sec == (min_seconds - 1)) {
return tm.tv_nsec >= min_nsec_timespec;
} else {
return tm.tv_sec >= min_seconds;
}
}
static _LIBCPP_CONSTEXPR_AFTER_CXX11 bool is_representable(FileTimeT tm) {
auto secs = duration_cast<fs_seconds>(tm.time_since_epoch());
auto nsecs = duration_cast<fs_nanoseconds>(tm.time_since_epoch() - secs);
if (nsecs.count() < 0) {
secs = secs + fs_seconds(1);
nsecs = nsecs + fs_seconds(1);
}
using TLim = numeric_limits<TimeT>;
if (secs.count() >= 0)
return secs.count() <= TLim::max();
return secs.count() >= TLim::min();
}
static _LIBCPP_CONSTEXPR_AFTER_CXX11 FileTimeT
convert_from_timespec(TimeSpecT tm) {
if (tm.tv_sec >= 0 || tm.tv_nsec == 0) {
return FileTimeT(fs_seconds(tm.tv_sec) +
duration_cast<fs_duration>(fs_nanoseconds(tm.tv_nsec)));
} else { // tm.tv_sec < 0
auto adj_subsec = duration_cast<fs_duration>(fs_seconds(1) -
fs_nanoseconds(tm.tv_nsec));
auto Dur = fs_seconds(tm.tv_sec + 1) - adj_subsec;
return FileTimeT(Dur);
}
}
template <class SubSecT>
static _LIBCPP_CONSTEXPR_AFTER_CXX11 bool
set_times_checked(TimeT* sec_out, SubSecT* subsec_out, FileTimeT tp) {
auto dur = tp.time_since_epoch();
auto sec_dur = duration_cast<fs_seconds>(dur);
auto subsec_dur = duration_cast<fs_nanoseconds>(dur - sec_dur);
// The tv_nsec and tv_usec fields must not be negative so adjust accordingly
if (subsec_dur.count() < 0) {
if (sec_dur.count() > min_seconds) {
sec_dur = sec_dur - fs_seconds(1);
subsec_dur = subsec_dur + fs_seconds(1);
} else {
subsec_dur = fs_nanoseconds::zero();
}
}
return checked_set(sec_out, sec_dur.count()) &&
checked_set(subsec_out, subsec_dur.count());
}
static _LIBCPP_CONSTEXPR_AFTER_CXX11 bool convert_to_timespec(TimeSpecT& dest,
FileTimeT tp) {
if (!is_representable(tp))
return false;
return set_times_checked(&dest.tv_sec, &dest.tv_nsec, tp);
}
};
using fs_time = time_util<file_time_type, time_t, TimeSpec>;
#if defined(__APPLE__)
TimeSpec extract_mtime(StatT const& st) { return st.st_mtimespec; }
TimeSpec extract_atime(StatT const& st) { return st.st_atimespec; }
#else
TimeSpec extract_mtime(StatT const& st) { return st.st_mtim; }
TimeSpec extract_atime(StatT const& st) { return st.st_atim; }
#endif
// allow the utimes implementation to compile even it we're not going
// to use it.
bool posix_utimes(const path& p, std::array<TimeSpec, 2> const& TS,
error_code& ec) {
using namespace chrono;
auto Convert = [](long nsec) {
using int_type = decltype(std::declval< ::timeval>().tv_usec);
auto dur = duration_cast<microseconds>(nanoseconds(nsec)).count();
return static_cast<int_type>(dur);
};
struct ::timeval ConvertedTS[2] = {{TS[0].tv_sec, Convert(TS[0].tv_nsec)},
{TS[1].tv_sec, Convert(TS[1].tv_nsec)}};
if (::utimes(p.c_str(), ConvertedTS) == -1) {
ec = capture_errno();
return true;
}
return false;
}
#if defined(_LIBCPP_USE_UTIMENSAT)
bool posix_utimensat(const path& p, std::array<TimeSpec, 2> const& TS,
error_code& ec) {
if (::utimensat(AT_FDCWD, p.c_str(), TS.data(), 0) == -1) {
ec = capture_errno();
return true;
}
return false;
}
#endif
bool set_file_times(const path& p, std::array<TimeSpec, 2> const& TS,
error_code& ec) {
#if !defined(_LIBCPP_USE_UTIMENSAT)
return posix_utimes(p, TS, ec);
#else
return posix_utimensat(p, TS, ec);
#endif
}
} // namespace
} // end namespace detail
} // namespace android::hardware::automotive::filesystem
#endif // AUTO_FILESYSTEM_COMMON_H
/* clang-format on */

File diff suppressed because it is too large Load diff