add status/symlink_status/file_size/hard_link_count

master
Daniel Kolesa 2018-04-20 01:22:28 +02:00
parent af87ee2a8b
commit a85cf13c56
2 changed files with 143 additions and 120 deletions

View File

@ -1224,6 +1224,9 @@ namespace fs {
* @{
*/
/** @brief The time point used to represent file access times. */
using file_time_t = std::chrono::time_point<std::chrono::system_clock>;
/** @brief An exception thrown by filesystem operations.
*
* Unlike ostd::path_error, this is thrown by operations doing syscalls.
@ -1361,22 +1364,54 @@ public:
return file_type(p_val >> 16);
}
void type(file_type type) noexcept {
p_val = ((p_val & 0xFFFF) | (UT(type) << 16));
}
perms permissions() const noexcept {
return perms(p_val & 0xFFFF);
}
void permissions(perms perm) noexcept {
p_val = ((p_val & ~UT(0xFFFF)) | UT(perm));
}
};
struct file_status {
file_status(
file_mode mode, file_time_t mtime,
std::uintmax_t size, std::uintmax_t nlinks
):
p_links(nlinks), p_size(size), p_mtime(mtime), p_mode(mode)
{}
file_mode mode() const noexcept {
return p_mode;
}
file_time_t last_write_time() const noexcept {
return p_mtime;
}
std::uintmax_t hard_link_count() const noexcept {
return p_links;
}
std::uintmax_t size() const noexcept {
return p_size;
}
private:
std::uintmax_t p_links;
std::uintmax_t p_size;
file_time_t p_mtime;
file_mode p_mode;
};
OSTD_EXPORT file_status status(path const &p);
OSTD_EXPORT file_status symlink_status(path const &p);
OSTD_EXPORT file_mode mode(path const &p);
OSTD_EXPORT file_mode symlink_mode(path const &p);
OSTD_EXPORT std::uintmax_t file_size(path const &p);
OSTD_EXPORT std::uintmax_t hard_link_count(path const &p);
OSTD_EXPORT file_time_t last_write_time(path const &p);
OSTD_EXPORT void last_write_time(path const &p, file_time_t new_time);
inline bool is_block_file(file_mode st) noexcept {
return st.type() == file_type::block;
}
@ -1449,34 +1484,6 @@ inline bool mode_known(path const &p) {
return mode_known(mode(p));
}
struct file_status {
file_status() {}
file_status(ostd::path const &p): p_path(p) {}
ostd::path const &path() const noexcept {
return p_path;
}
operator ostd::path const &() const noexcept {
return p_path;
}
void clear() {
p_path.clear();
}
void assign(ostd::path const &p) {
p_path = p;
}
void assign(ostd::path &&p) {
p_path = std::move(p);
}
private:
ostd::path p_path{};
};
namespace detail {
struct dir_range_impl;
struct rdir_range_impl;
@ -1684,11 +1691,6 @@ OSTD_EXPORT std::uintmax_t remove_all(path const &p);
OSTD_EXPORT void rename(path const &op, path const &np);
using file_time_t = std::chrono::time_point<std::chrono::system_clock>;
OSTD_EXPORT file_time_t last_write_time(path const &p);
OSTD_EXPORT void last_write_time(path const &p, file_time_t new_time);
namespace detail {
OSTD_EXPORT void glob_match_impl(
void (*out)(path const &, void *),

View File

@ -9,6 +9,9 @@
#undef _POSIX_C_SOURCE
#endif
/* only because glibc is awful, does not apply to other libcs */
#define _FILE_OFFSET_BITS 64
#define _POSIX_C_SOURCE 200809L
#ifndef _ATFILE_SOURCE
@ -51,26 +54,111 @@ static std::error_code ec_from_int(int v) {
return std::error_code(v, std::system_category());
}
OSTD_EXPORT file_mode mode(path const &p) {
struct stat sb;
if (stat(p.string().data(), &sb) < 0) {
/* ugly test for whether nanosecond precision is available in stat
* could check for existence of st_mtime macro, but this is more reliable
*/
template<typename T>
struct test_mtim {
template<typename TT, TT> struct test_stat;
struct fake_stat {
struct timespec st_mtim;
};
struct stat_test: fake_stat, T {};
template<typename TT>
static char test(test_stat<struct timespec fake_stat::*, &TT::st_mtim> *);
template<typename>
static int test(...);
static constexpr bool value = (sizeof(test<stat_test>(0)) == sizeof(int));
};
template<bool B>
struct mtime_impl {
template<typename S>
static file_time_t get(S const &st) {
return std::chrono::system_clock::from_time_t(st.st_mtime);
}
};
template<>
struct mtime_impl<true> {
template<typename S>
static file_time_t get(S const &st) {
struct timespec ts = st.st_mtim;
auto d = std::chrono::seconds{ts.tv_sec} +
std::chrono::nanoseconds{ts.tv_nsec};
return file_time_t{
std::chrono::duration_cast<std::chrono::system_clock::duration>(d)
};
}
};
using mtime = mtime_impl<test_mtim<struct stat>::value>;
static file_status status_get(path const &p, int ret, struct stat &sb) {
if (ret < 0) {
if (errno == ENOENT) {
return file_mode{file_type::not_found, perms::none};
return file_status{
file_mode{file_type::not_found, perms::none},
file_time_t{}, 0, 0
};
}
throw fs_error{"stat failure", p, errno_ec()};
}
return file_mode{mode_to_type(sb.st_mode), perms(sb.st_mode & 07777)};
return file_status{
file_mode{mode_to_type(sb.st_mode), perms{sb.st_mode & 07777}},
mtime::get(sb),
std::uintmax_t(sb.st_size),
std::uintmax_t(sb.st_nlink)
};
}
OSTD_EXPORT file_status status(path const &p) {
struct stat sb;
return status_get(p, stat(p.string().data(), &sb), sb);
}
OSTD_EXPORT file_status symlink_status(path const &p) {
struct stat sb;
return status_get(p, lstat(p.string().data(), &sb), sb);
}
OSTD_EXPORT file_mode mode(path const &p) {
return status(p).mode();
}
OSTD_EXPORT file_mode symlink_mode(path const &p) {
struct stat sb;
if (lstat(p.string().data(), &sb) < 0) {
if (errno == ENOENT) {
return file_mode{file_type::not_found, perms::none};
}
throw fs_error{"lstat failure", p, errno_ec()};
return symlink_status(p).mode();
}
OSTD_EXPORT file_time_t last_write_time(path const &p) {
return status(p).last_write_time();
}
OSTD_EXPORT std::uintmax_t file_size(path const &p) {
return status(p).size();
}
OSTD_EXPORT std::uintmax_t hard_link_count(path const &p) {
return status(p).hard_link_count();
}
/* TODO: somehow feature-test for utimensat and fallback to utimes */
OSTD_EXPORT void last_write_time(path const &p, file_time_t new_time) {
auto d = new_time.time_since_epoch();
auto sec = std::chrono::floor<std::chrono::seconds>(d);
auto nsec = std::chrono::duration_cast<std::chrono::nanoseconds>(d - sec);
struct timespec times[2] = {
{0, UTIME_OMIT}, {time_t(sec.count()), long(nsec.count())}
};
if (utimensat(0, p.string().data(), times, 0)) {
throw fs_error{"utimensat failure", p, errno_ec()};
}
return file_mode{mode_to_type(sb.st_mode), perms(sb.st_mode & 07777)};
}
} /* namespace fs */
@ -387,72 +475,5 @@ OSTD_EXPORT void rename(path const &op, path const &np) {
}
}
/* ugly test for whether nanosecond precision is available in stat
* could check for existence of st_mtime macro, but this is more reliable
*/
template<typename T>
struct test_mtim {
template<typename TT, TT> struct test_stat;
struct fake_stat {
struct timespec st_mtim;
};
struct stat_test: fake_stat, T {};
template<typename TT>
static char test(test_stat<struct timespec fake_stat::*, &TT::st_mtim> *);
template<typename>
static int test(...);
static constexpr bool value = (sizeof(test<stat_test>(0)) == sizeof(int));
};
template<bool B>
struct mtime_impl {
template<typename S>
static file_time_t get(S const &st) {
return std::chrono::system_clock::from_time_t(st.st_mtime);
}
};
template<>
struct mtime_impl<true> {
template<typename S>
static file_time_t get(S const &st) {
struct timespec ts = st.st_mtim;
auto d = std::chrono::seconds{ts.tv_sec} +
std::chrono::nanoseconds{ts.tv_nsec};
return file_time_t{
std::chrono::duration_cast<std::chrono::system_clock::duration>(d)
};
}
};
using mtime = mtime_impl<test_mtim<struct stat>::value>;
OSTD_EXPORT file_time_t last_write_time(path const &p) {
struct stat sb;
if (stat(p.string().data(), &sb) < 0) {
throw fs_error{"stat failure", p, errno_ec()};
}
return mtime::get(sb);
}
/* TODO: somehow feature-test for utimensat and fallback to utimes */
OSTD_EXPORT void last_write_time(path const &p, file_time_t new_time) {
auto d = new_time.time_since_epoch();
auto sec = std::chrono::floor<std::chrono::seconds>(d);
auto nsec = std::chrono::duration_cast<std::chrono::nanoseconds>(d - sec);
struct timespec times[2] = {
{0, UTIME_OMIT}, {time_t(sec.count()), long(nsec.count())}
};
if (utimensat(0, p.string().data(), times, 0)) {
throw fs_error{"utimensat failure", p, errno_ec()};
}
}
} /* namespace fs */
} /* namespace ostd */