mirror of
https://github.com/facebook/sapling.git
synced 2024-10-09 08:18:15 +03:00
94a46d9c0e
Summary: Taking a fuse_setattr_in arg means that it can only be used on FUSE, and while FUSE has been the only backend on UNIX for a while, this is changing with NFS being added. Thus, we need to find a solution that isn't tied to FUSE. The solution is fairly simple, let's simply have a struct with optional fields that needs changing, FUSE and NFS will set these to what needs changing. Reviewed By: chadaustin Differential Revision: D26742213 fbshipit-source-id: 16e3e8cdd22d88ace43485d9e3744280de1ee8ad
160 lines
4.4 KiB
C++
160 lines
4.4 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/inodes/InodeMetadata.h"
|
|
#include "eden/fs/utils/Clock.h"
|
|
|
|
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 DesiredMetadata& attr) {
|
|
const auto now = clock.getRealtime();
|
|
|
|
// Set atime for TreeInode.
|
|
if (attr.atime.has_value()) {
|
|
atime = attr.atime.value();
|
|
}
|
|
|
|
// Set mtime for TreeInode.
|
|
if (attr.mtime.has_value()) {
|
|
mtime = attr.mtime.value();
|
|
}
|
|
|
|
// 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
|