sapling/eden/fs/inodes/InodeTimestamps.cpp
Puneet Kaushik 3bb8cfbef8 Inode support on Windows
Summary: This diff ports TreeInode, FileInode, InodeMap and related classes to Windows. We don't build or test it here, there are more dependcies we need to port. The built script and the test are part of other diffs in this stack.

Reviewed By: simpkins

Differential Revision: D19956266

fbshipit-source-id: 9eb754233bca3d5a336f465c2400512a8593ca4f
2020-04-01 14:53:30 -07:00

173 lines
4.7 KiB
C++

/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This software may be used and distributed according to the terms of the
* GNU General Public License version 2.
*/
#include "eden/fs/inodes/InodeTimestamps.h"
#include <folly/Conv.h>
#include <sys/stat.h>
#include "eden/fs/utils/Clock.h"
#ifndef _WIN32
#include "eden/fs/fuse/FuseTypes.h"
#endif
namespace facebook {
namespace eden {
namespace {
/**
* Like ext4, our earliest representable date is 2^31 seconds before the unix
* epoch, which works out to December 13th, 1901.
*/
constexpr int64_t kEpochOffsetSeconds = 0x80000000ll;
/**
* Largest representable sec,nsec pair.
*
* $ python3
* >>> kEpochOffsetSeconds = 0x80000000
* >>> kLargestRepresentableSec = 16299260425
* >>> kLargestRepresentableNsec = 709551615
* >>> hex((kEpochOffsetSeconds + kLargestRepresentableSec) * 1000000000 + \
* ... kLargestRepresentableNsec)
* '0xffffffffffffffff'
*/
constexpr int64_t kLargestRepresentableSec = 16299260425ll;
constexpr uint32_t kLargestRepresentableNsec = 709551615u;
struct ClampPolicy {
static constexpr bool is_noexcept = true;
static uint64_t minimum(timespec /*ts*/) noexcept {
return 0;
}
static uint64_t maximum(timespec /*ts*/) noexcept {
return ~0ull;
}
};
struct ThrowPolicy {
static constexpr bool is_noexcept = false;
static uint64_t minimum(timespec ts) {
throw std::underflow_error(folly::to<std::string>(
"underflow converting timespec (",
ts.tv_sec,
" s, ",
ts.tv_nsec,
" ns) to EdenTimestamp"));
}
static uint64_t maximum(timespec ts) {
throw std::overflow_error(folly::to<std::string>(
"overflow converting timespec (",
ts.tv_sec,
" s, ",
ts.tv_nsec,
" ns) to EdenTimestamp"));
}
};
template <typename OutOfRangePolicy>
uint64_t repFromTimespec(timespec ts) noexcept(OutOfRangePolicy::is_noexcept) {
if (ts.tv_sec < -kEpochOffsetSeconds) {
return OutOfRangePolicy::minimum(ts);
} else if (
ts.tv_sec > kLargestRepresentableSec ||
(ts.tv_sec == kLargestRepresentableSec &&
ts.tv_nsec > kLargestRepresentableNsec)) {
return OutOfRangePolicy::maximum(ts);
} else {
// Assume that ts.tv_nsec is within [0, 1000000000).
// The first addition must be unsigned to avoid UB.
return (static_cast<uint64_t>(kEpochOffsetSeconds) +
static_cast<uint64_t>(ts.tv_sec)) *
1000000000ll +
ts.tv_nsec;
}
}
timespec repToTimespec(uint64_t nsec) {
static constexpr uint64_t kEpochNsec = kEpochOffsetSeconds * 1000000000ull;
if (nsec < kEpochNsec) {
int64_t before_epoch = kEpochNsec - nsec;
timespec ts;
auto sec = (before_epoch + 999999999) / 1000000000;
ts.tv_sec = -sec;
ts.tv_nsec = sec * 1000000000 - before_epoch;
return ts;
} else {
uint64_t after_epoch = nsec - kEpochNsec;
timespec ts;
ts.tv_sec = after_epoch / 1000000000;
ts.tv_nsec = after_epoch % 1000000000;
return ts;
}
}
} // namespace
EdenTimestamp::EdenTimestamp(timespec ts, Clamp) noexcept
: nsec_{repFromTimespec<ClampPolicy>(ts)} {}
EdenTimestamp::EdenTimestamp(timespec ts, ThrowIfOutOfRange)
: nsec_{repFromTimespec<ThrowPolicy>(ts)} {}
timespec EdenTimestamp::toTimespec() const noexcept {
return repToTimespec(nsec_);
}
#ifndef _WIN32
void InodeTimestamps::setattrTimes(
const Clock& clock,
const fuse_setattr_in& attr) {
const auto now = clock.getRealtime();
// Set atime for TreeInode.
if (attr.valid & FATTR_ATIME) {
timespec attr_atime;
attr_atime.tv_sec = attr.atime;
attr_atime.tv_nsec = attr.atimensec;
atime = attr_atime;
} else if (attr.valid & FATTR_ATIME_NOW) {
atime = now;
}
// Set mtime for TreeInode.
if (attr.valid & FATTR_MTIME) {
timespec attr_mtime;
attr_mtime.tv_sec = attr.mtime;
attr_mtime.tv_nsec = attr.mtimensec;
mtime = attr_mtime;
} else if (attr.valid & FATTR_MTIME_NOW) {
mtime = now;
}
// we do not allow users to set ctime using setattr. ctime should be changed
// when ever setattr is called, as this function is called in setattr, update
// ctime to now.
ctime = now;
}
void InodeTimestamps::applyToStat(struct stat& st) const {
#ifdef __APPLE__
st.st_atimespec = atime.toTimespec();
st.st_ctimespec = ctime.toTimespec();
st.st_mtimespec = mtime.toTimespec();
#elif defined(_BSD_SOURCE) || defined(_SVID_SOURCE) || \
_POSIX_C_SOURCE >= 200809L || _XOPEN_SOURCE >= 700
st.st_atim = atime.toTimespec();
st.st_ctim = ctime.toTimespec();
st.st_mtim = mtime.toTimespec();
#else
st.st_atime = atime.toTimespec().tv_sec;
st.st_mtime = mtime.toTimespec().tv_sec;
st.st_ctime = ctime.toTimespec().tv_sec;
#endif
}
#endif
} // namespace eden
} // namespace facebook