mirror of
https://github.com/facebook/sapling.git
synced 2024-10-10 08:47:12 +03:00
d9a6089dd5
Summary: Make it clear (especially for the upcoming InodeMetadata struct) which operations with EdenTimestamp and InodeTimestamps will never throw. Reviewed By: simpkins Differential Revision: D7920219 fbshipit-source-id: 5917da51b8128455893a1480def6f2c1c8de13d4
166 lines
4.7 KiB
C++
166 lines
4.7 KiB
C++
/*
|
|
* Copyright (c) 2018-present, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
*/
|
|
#include "eden/fs/inodes/InodeTimestamps.h"
|
|
|
|
#include <folly/Conv.h>
|
|
#include <sys/stat.h>
|
|
#include "eden/fs/fuse/FuseTypes.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_);
|
|
}
|
|
|
|
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 {
|
|
#if 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
|
|
}
|
|
|
|
} // namespace eden
|
|
} // namespace facebook
|