2016-05-12 23:43:17 +03:00
|
|
|
/*
|
2019-06-20 02:58:25 +03:00
|
|
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
2016-05-12 23:43:17 +03:00
|
|
|
*
|
2019-06-20 02:58:25 +03:00
|
|
|
* This software may be used and distributed according to the terms of the
|
|
|
|
* GNU General Public License version 2.
|
2016-05-12 23:43:17 +03:00
|
|
|
*/
|
2019-10-11 15:26:59 +03:00
|
|
|
|
2017-05-02 04:45:31 +03:00
|
|
|
#include "eden/fs/inodes/FileInode.h"
|
|
|
|
|
2017-07-27 09:39:02 +03:00
|
|
|
#include <folly/io/Cursor.h>
|
|
|
|
#include <folly/io/IOBuf.h>
|
2017-09-19 20:59:46 +03:00
|
|
|
#include <folly/io/async/EventBase.h>
|
2018-05-01 07:20:51 +03:00
|
|
|
#include <folly/logging/xlog.h>
|
2017-05-02 04:45:31 +03:00
|
|
|
#include "eden/fs/inodes/EdenMount.h"
|
|
|
|
#include "eden/fs/inodes/InodeError.h"
|
|
|
|
#include "eden/fs/inodes/TreeInode.h"
|
2016-06-09 04:59:51 +03:00
|
|
|
#include "eden/fs/model/Blob.h"
|
2016-05-28 04:16:30 +03:00
|
|
|
#include "eden/fs/model/Hash.h"
|
2017-05-02 04:45:31 +03:00
|
|
|
#include "eden/fs/store/BlobMetadata.h"
|
2016-06-09 04:59:51 +03:00
|
|
|
#include "eden/fs/store/ObjectStore.h"
|
2017-07-27 09:39:02 +03:00
|
|
|
#include "eden/fs/utils/Bug.h"
|
2017-12-05 20:55:31 +03:00
|
|
|
#include "eden/fs/utils/Clock.h"
|
2017-12-15 03:36:38 +03:00
|
|
|
#include "eden/fs/utils/DirType.h"
|
2020-04-29 04:57:12 +03:00
|
|
|
#include "eden/fs/utils/EnumValue.h"
|
2020-05-06 04:12:37 +03:00
|
|
|
#include "eden/fs/utils/FileUtils.h"
|
2018-08-03 23:08:37 +03:00
|
|
|
#include "eden/fs/utils/UnboundedQueueExecutor.h"
|
2020-04-02 00:51:06 +03:00
|
|
|
|
2020-05-06 04:12:37 +03:00
|
|
|
#ifndef _WIN32
|
2020-04-02 00:51:06 +03:00
|
|
|
#include "eden/fs/inodes/InodeTable.h"
|
|
|
|
#include "eden/fs/inodes/Overlay.h"
|
|
|
|
#include "eden/fs/store/BlobAccess.h"
|
2017-04-14 21:31:48 +03:00
|
|
|
#include "eden/fs/utils/XAttr.h"
|
2020-04-02 00:51:06 +03:00
|
|
|
#endif
|
2016-05-12 23:43:17 +03:00
|
|
|
|
|
|
|
using folly::Future;
|
2018-01-26 22:17:36 +03:00
|
|
|
using folly::makeFuture;
|
2016-05-12 23:43:17 +03:00
|
|
|
using folly::StringPiece;
|
|
|
|
using std::string;
|
|
|
|
using std::vector;
|
|
|
|
|
|
|
|
namespace facebook {
|
|
|
|
namespace eden {
|
|
|
|
|
2018-03-27 21:13:03 +03:00
|
|
|
/*********************************************************************
|
|
|
|
* FileInode::LockedState
|
|
|
|
********************************************************************/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* LockedState is a helper class that wraps
|
|
|
|
* folly::Synchronized<State>::LockedPtr
|
|
|
|
*
|
|
|
|
* It implements operator->() and operator*() so it can be used just like
|
2018-12-13 03:48:11 +03:00
|
|
|
* LockedPtr.
|
2018-03-27 21:13:03 +03:00
|
|
|
*/
|
|
|
|
class FileInode::LockedState {
|
|
|
|
public:
|
|
|
|
explicit LockedState(FileInode* inode) : ptr_{inode->state_.wlock()} {}
|
|
|
|
explicit LockedState(const FileInodePtr& inode)
|
|
|
|
: ptr_{inode->state_.wlock()} {}
|
|
|
|
|
|
|
|
LockedState(LockedState&&) = default;
|
|
|
|
LockedState& operator=(LockedState&&) = default;
|
|
|
|
|
|
|
|
~LockedState();
|
|
|
|
|
|
|
|
State* operator->() const {
|
|
|
|
return ptr_.operator->();
|
|
|
|
}
|
|
|
|
State& operator*() const {
|
|
|
|
return ptr_.operator*();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool isNull() const {
|
|
|
|
return ptr_.isNull();
|
|
|
|
}
|
|
|
|
explicit operator bool() const {
|
|
|
|
return !ptr_.isNull();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Explicitly unlock the LockedState object before it is destroyed.
|
|
|
|
*/
|
|
|
|
void unlock();
|
|
|
|
|
|
|
|
/**
|
2018-12-13 03:48:09 +03:00
|
|
|
* Move the file into the MATERIALIZED_IN_OVERLAY state.
|
2018-03-27 21:13:03 +03:00
|
|
|
*
|
2018-12-13 03:48:11 +03:00
|
|
|
* This updates state->tag and state->hash.
|
2018-03-27 21:13:03 +03:00
|
|
|
*/
|
2018-12-13 03:48:09 +03:00
|
|
|
void setMaterialized();
|
2018-03-27 21:13:03 +03:00
|
|
|
|
2018-12-11 06:28:14 +03:00
|
|
|
/**
|
|
|
|
* If this inode still has access to a cached blob, return it.
|
|
|
|
*
|
|
|
|
* Can only be called when not materialized.
|
|
|
|
*/
|
|
|
|
std::shared_ptr<const Blob> getCachedBlob(
|
|
|
|
EdenMount* mount,
|
|
|
|
BlobCache::Interest interest);
|
|
|
|
|
2018-03-27 21:13:03 +03:00
|
|
|
private:
|
|
|
|
folly::Synchronized<State>::LockedPtr ptr_;
|
|
|
|
};
|
|
|
|
|
|
|
|
FileInode::LockedState::~LockedState() {
|
|
|
|
if (!ptr_) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
// Check the state invariants every time we release the lock
|
|
|
|
ptr_->checkInvariants();
|
|
|
|
}
|
|
|
|
|
|
|
|
void FileInode::LockedState::unlock() {
|
|
|
|
ptr_->checkInvariants();
|
|
|
|
ptr_.unlock();
|
|
|
|
}
|
|
|
|
|
2018-12-11 06:28:14 +03:00
|
|
|
std::shared_ptr<const Blob> FileInode::LockedState::getCachedBlob(
|
|
|
|
EdenMount* mount,
|
|
|
|
BlobCache::Interest interest) {
|
|
|
|
CHECK(!ptr_->isMaterialized())
|
|
|
|
<< "getCachedBlob can only be called when not materialized";
|
|
|
|
|
|
|
|
// Is the previous handle still valid? If so, return it.
|
|
|
|
if (auto blob = ptr_->interestHandle.getBlob()) {
|
|
|
|
return blob;
|
|
|
|
}
|
|
|
|
// Otherwise, does the cache have one?
|
|
|
|
//
|
|
|
|
// The BlobAccess::getBlob call in startLoadingData on a cache miss will also
|
|
|
|
// check the BlobCache, but by checking it here, we can avoid a transition to
|
|
|
|
// BLOB_LOADING and back, and also avoid allocating some futures and closures.
|
|
|
|
auto result = mount->getBlobCache()->get(ptr_->hash.value(), interest);
|
|
|
|
if (result.blob) {
|
|
|
|
ptr_->interestHandle = std::move(result.interestHandle);
|
|
|
|
return std::move(result.blob);
|
|
|
|
}
|
|
|
|
|
|
|
|
// If we received a read and missed cache because the blob was
|
|
|
|
// already evicted, assume the existing readByteRanges CoverageSet
|
|
|
|
// doesn't accurately reflect how much data is in the kernel's
|
|
|
|
// caches.
|
|
|
|
ptr_->interestHandle.reset();
|
2020-04-02 00:51:06 +03:00
|
|
|
#ifndef _WIN32
|
2018-12-11 06:28:14 +03:00
|
|
|
ptr_->readByteRanges.clear();
|
2020-04-02 00:51:06 +03:00
|
|
|
#endif // !_WIN32
|
2018-12-11 06:28:14 +03:00
|
|
|
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2018-12-13 03:48:09 +03:00
|
|
|
void FileInode::LockedState::setMaterialized() {
|
2018-03-27 21:13:03 +03:00
|
|
|
ptr_->hash.reset();
|
|
|
|
ptr_->tag = State::MATERIALIZED_IN_OVERLAY;
|
2018-12-06 22:33:06 +03:00
|
|
|
|
|
|
|
ptr_->interestHandle.reset();
|
2020-04-02 00:51:06 +03:00
|
|
|
|
|
|
|
#ifndef _WIN32
|
2018-12-06 22:33:06 +03:00
|
|
|
ptr_->readByteRanges.clear();
|
2020-04-02 00:51:06 +03:00
|
|
|
#endif
|
2018-03-27 21:13:03 +03:00
|
|
|
}
|
|
|
|
|
2018-03-23 22:34:59 +03:00
|
|
|
/*********************************************************************
|
|
|
|
* Implementations of FileInode private template methods
|
|
|
|
* These definitions need to appear before any functions that use them.
|
|
|
|
********************************************************************/
|
|
|
|
|
2018-11-29 04:31:38 +03:00
|
|
|
template <typename ReturnType, typename Fn>
|
|
|
|
ReturnType FileInode::runWhileDataLoaded(
|
|
|
|
LockedState state,
|
|
|
|
BlobCache::Interest interest,
|
2020-02-06 00:13:11 +03:00
|
|
|
ObjectFetchContext& fetchContext,
|
2018-11-29 04:31:38 +03:00
|
|
|
std::shared_ptr<const Blob> blob,
|
|
|
|
Fn&& fn) {
|
|
|
|
auto future = Future<std::shared_ptr<const Blob>>::makeEmpty();
|
2018-03-23 22:34:59 +03:00
|
|
|
switch (state->tag) {
|
2018-11-29 04:31:38 +03:00
|
|
|
case State::BLOB_NOT_LOADING:
|
|
|
|
if (!blob) {
|
2018-12-11 06:28:14 +03:00
|
|
|
// If no blob is given, check cache.
|
|
|
|
blob = state.getCachedBlob(getMount(), interest);
|
2018-11-29 04:31:38 +03:00
|
|
|
}
|
|
|
|
if (blob) {
|
|
|
|
// The blob was still in cache, so we can run the function immediately.
|
|
|
|
return folly::makeFutureWith([&] {
|
|
|
|
return std::forward<Fn>(fn)(std::move(state), std::move(blob));
|
|
|
|
});
|
|
|
|
} else {
|
2020-07-02 21:58:46 +03:00
|
|
|
future = startLoadingData(std::move(state), interest, fetchContext);
|
2018-11-29 04:31:38 +03:00
|
|
|
}
|
|
|
|
break;
|
2018-03-23 22:34:59 +03:00
|
|
|
case State::BLOB_LOADING:
|
|
|
|
// If we're already loading, latch on to the in-progress load
|
|
|
|
future = state->blobLoadingPromise->getFuture();
|
|
|
|
state.unlock();
|
|
|
|
break;
|
2018-11-29 04:31:38 +03:00
|
|
|
case State::MATERIALIZED_IN_OVERLAY:
|
|
|
|
return folly::makeFutureWith(
|
|
|
|
[&] { return std::forward<Fn>(fn)(std::move(state), nullptr); });
|
2018-03-23 22:34:59 +03:00
|
|
|
}
|
|
|
|
|
2018-11-29 04:31:38 +03:00
|
|
|
return std::move(future).thenValue(
|
2020-02-06 00:13:11 +03:00
|
|
|
[self = inodePtrFromThis(),
|
|
|
|
fn = std::forward<Fn>(fn),
|
|
|
|
interest,
|
2020-07-02 21:58:46 +03:00
|
|
|
&fetchContext](std::shared_ptr<const Blob> blob) mutable {
|
2018-11-29 04:31:38 +03:00
|
|
|
// Simply call runWhileDataLoaded() again when we we finish loading the
|
|
|
|
// blob data. The state should be BLOB_NOT_LOADING or
|
|
|
|
// MATERIALIZED_IN_OVERLAY this time around.
|
|
|
|
auto stateLock = LockedState{self};
|
|
|
|
DCHECK(
|
|
|
|
stateLock->tag == State::BLOB_NOT_LOADING ||
|
|
|
|
stateLock->tag == State::MATERIALIZED_IN_OVERLAY)
|
|
|
|
<< "unexpected FileInode state after loading: " << stateLock->tag;
|
|
|
|
return self->runWhileDataLoaded<ReturnType>(
|
|
|
|
std::move(stateLock),
|
|
|
|
interest,
|
2020-02-06 00:13:11 +03:00
|
|
|
fetchContext,
|
2018-11-29 04:31:38 +03:00
|
|
|
std::move(blob),
|
|
|
|
std::forward<Fn>(fn));
|
|
|
|
});
|
2018-03-23 22:34:59 +03:00
|
|
|
}
|
|
|
|
|
2020-04-02 00:51:06 +03:00
|
|
|
#ifndef _WIN32
|
2018-03-23 22:34:59 +03:00
|
|
|
template <typename Fn>
|
|
|
|
typename folly::futures::detail::callableResult<FileInode::LockedState, Fn>::
|
|
|
|
Return
|
2018-11-29 04:31:38 +03:00
|
|
|
FileInode::runWhileMaterialized(
|
|
|
|
LockedState state,
|
|
|
|
std::shared_ptr<const Blob> blob,
|
|
|
|
Fn&& fn) {
|
|
|
|
auto future = Future<std::shared_ptr<const Blob>>::makeEmpty();
|
2018-03-23 22:34:59 +03:00
|
|
|
switch (state->tag) {
|
2018-11-29 04:31:38 +03:00
|
|
|
case State::BLOB_NOT_LOADING:
|
|
|
|
if (!blob) {
|
2018-12-11 06:28:14 +03:00
|
|
|
// If no blob is given, check cache.
|
|
|
|
blob = state.getCachedBlob(
|
|
|
|
getMount(), BlobCache::Interest::UnlikelyNeededAgain);
|
2018-11-29 04:31:38 +03:00
|
|
|
}
|
|
|
|
if (blob) {
|
|
|
|
// We have the blob data loaded.
|
|
|
|
// Materialize the file now.
|
|
|
|
materializeNow(state, blob);
|
|
|
|
// Call materializeInParent before we return, after we are
|
|
|
|
// sure the state lock has been released. This does mean that our
|
|
|
|
// parent won't have updated our state until after the caller's function
|
|
|
|
// runs, but this is okay. There is always a brief gap between when we
|
|
|
|
// materialize ourself and when our parent gets updated to indicate
|
|
|
|
// this. If we do crash during this period it is not too unreasonable
|
|
|
|
// that recent change right before the crash might be reverted to their
|
|
|
|
// non-materialized state.
|
|
|
|
SCOPE_EXIT {
|
|
|
|
CHECK(state.isNull());
|
|
|
|
materializeInParent();
|
|
|
|
};
|
|
|
|
// Note that we explicitly create a temporary LockedState object
|
|
|
|
// to pass to the caller to ensure that the state lock will be released
|
|
|
|
// when they return, even if the caller's function accepts the state as
|
|
|
|
// an rvalue-reference and does not release it themselves.
|
|
|
|
return folly::makeFutureWith([&] {
|
|
|
|
return std::forward<Fn>(fn)(LockedState{std::move(state)});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2020-09-01 13:37:16 +03:00
|
|
|
static auto context = ObjectFetchContext::getNullContextWithCauseDetail(
|
|
|
|
"FileInode::runWhileMaterialized");
|
2018-11-29 04:31:38 +03:00
|
|
|
// The blob must be loaded, so kick that off. There's no point in caching
|
|
|
|
// it in memory - the blob will immediately be written into the overlay
|
|
|
|
// and then dropped.
|
|
|
|
future = startLoadingData(
|
2020-09-01 13:37:16 +03:00
|
|
|
std::move(state), BlobCache::Interest::UnlikelyNeededAgain, *context);
|
2018-11-29 04:31:38 +03:00
|
|
|
break;
|
2018-03-23 22:34:59 +03:00
|
|
|
case State::BLOB_LOADING:
|
|
|
|
// If we're already loading, latch on to the in-progress load
|
|
|
|
future = state->blobLoadingPromise->getFuture();
|
|
|
|
state.unlock();
|
|
|
|
break;
|
2018-11-29 04:31:38 +03:00
|
|
|
case State::MATERIALIZED_IN_OVERLAY:
|
|
|
|
return folly::makeFutureWith(
|
|
|
|
[&] { return std::forward<Fn>(fn)(LockedState{std::move(state)}); });
|
2018-03-23 22:34:59 +03:00
|
|
|
}
|
|
|
|
|
2018-10-23 23:39:59 +03:00
|
|
|
return std::move(future).thenValue(
|
2018-10-02 20:01:06 +03:00
|
|
|
[self = inodePtrFromThis(),
|
2018-11-29 04:31:38 +03:00
|
|
|
fn = std::forward<Fn>(fn)](std::shared_ptr<const Blob> blob) mutable {
|
|
|
|
// Simply call runWhileMaterialized() again when we we are finished
|
|
|
|
// loading the blob data.
|
2018-10-02 20:01:06 +03:00
|
|
|
auto stateLock = LockedState{self};
|
|
|
|
DCHECK(
|
2018-11-29 04:31:38 +03:00
|
|
|
stateLock->tag == State::BLOB_NOT_LOADING ||
|
2018-10-02 20:01:06 +03:00
|
|
|
stateLock->tag == State::MATERIALIZED_IN_OVERLAY)
|
|
|
|
<< "unexpected FileInode state after loading: " << stateLock->tag;
|
|
|
|
return self->runWhileMaterialized(
|
2018-11-29 04:31:38 +03:00
|
|
|
std::move(stateLock), std::move(blob), std::forward<Fn>(fn));
|
2018-10-02 20:01:06 +03:00
|
|
|
});
|
2018-03-23 22:34:59 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
template <typename Fn>
|
|
|
|
typename std::result_of<Fn(FileInode::LockedState&&)>::type
|
|
|
|
FileInode::truncateAndRun(LockedState state, Fn&& fn) {
|
|
|
|
switch (state->tag) {
|
2018-11-29 04:31:38 +03:00
|
|
|
case State::BLOB_NOT_LOADING:
|
2018-03-23 22:34:59 +03:00
|
|
|
case State::BLOB_LOADING: {
|
2018-03-27 21:13:03 +03:00
|
|
|
// We are not materialized yet. We need to materialize the file now.
|
|
|
|
//
|
|
|
|
// Note that we have to be pretty careful about ordering of operations
|
|
|
|
// here and how we behave if an exception is thrown at any point. We
|
|
|
|
// want to:
|
|
|
|
// - Truncate the file.
|
|
|
|
// - Invoke the input function with the state lock still held.
|
|
|
|
// - Release the state lock
|
|
|
|
// - Assuming we successfully materialized the file, mark ourself
|
|
|
|
// materialized in our parent TreeInode.
|
|
|
|
// - If we successfully materialized the file and were in the
|
|
|
|
// BLOB_LOADING state, fulfill the blobLoadingPromise.
|
2018-11-29 04:31:38 +03:00
|
|
|
std::optional<folly::SharedPromise<std::shared_ptr<const Blob>>>
|
|
|
|
loadingPromise;
|
2018-03-23 22:34:59 +03:00
|
|
|
SCOPE_EXIT {
|
|
|
|
if (loadingPromise) {
|
2018-11-29 04:31:38 +03:00
|
|
|
// If transitioning from the loading state to materialized, fulfill
|
|
|
|
// the loading promise will null. Callbacks will have to handle the
|
|
|
|
// case that the state is now materialized.
|
|
|
|
loadingPromise->setValue(nullptr);
|
2018-03-23 22:34:59 +03:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2018-11-29 04:31:38 +03:00
|
|
|
// Call materializeAndTruncate()
|
|
|
|
materializeAndTruncate(state);
|
2018-03-27 21:13:03 +03:00
|
|
|
|
2018-11-29 04:31:38 +03:00
|
|
|
// Now that materializeAndTruncate() has succeeded, extract the
|
|
|
|
// blobLoadingPromise so we can fulfill it as we exit.
|
|
|
|
loadingPromise = std::move(state->blobLoadingPromise);
|
|
|
|
state->blobLoadingPromise.reset();
|
|
|
|
// Also call materializeInParent() as we exit, before fulfilling the
|
|
|
|
// blobLoadingPromise.
|
|
|
|
SCOPE_EXIT {
|
|
|
|
CHECK(state.isNull());
|
|
|
|
materializeInParent();
|
|
|
|
};
|
2018-03-27 21:13:03 +03:00
|
|
|
|
2018-11-29 04:31:38 +03:00
|
|
|
// Now invoke the input function.
|
|
|
|
// Note that we explicitly create a temporary LockedState object
|
|
|
|
// to pass to the caller to ensure that the state lock will be released
|
|
|
|
// when they return, even if the caller's function accepts the state as
|
|
|
|
// an rvalue-reference and does not release it themselves.
|
|
|
|
return std::forward<Fn>(fn)(LockedState{std::move(state)});
|
2018-03-23 22:34:59 +03:00
|
|
|
}
|
|
|
|
case State::MATERIALIZED_IN_OVERLAY:
|
|
|
|
// We are already materialized.
|
|
|
|
// Truncate the file in the overlay, then call the function.
|
2018-03-27 21:13:03 +03:00
|
|
|
truncateInOverlay(state);
|
|
|
|
return std::forward<Fn>(fn)(std::move(state));
|
2018-03-23 22:34:59 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
XLOG(FATAL) << "unexpected FileInode state " << state->tag;
|
|
|
|
}
|
2020-04-02 00:51:06 +03:00
|
|
|
#endif // !_WIN32
|
2018-03-23 22:34:59 +03:00
|
|
|
|
|
|
|
/*********************************************************************
|
|
|
|
* FileInode::State methods
|
|
|
|
********************************************************************/
|
|
|
|
|
2018-10-24 04:48:38 +03:00
|
|
|
FileInodeState::FileInodeState(const std::optional<Hash>& h) : hash(h) {
|
2018-11-29 04:31:38 +03:00
|
|
|
tag = hash ? BLOB_NOT_LOADING : MATERIALIZED_IN_OVERLAY;
|
2017-11-08 03:07:16 +03:00
|
|
|
|
|
|
|
checkInvariants();
|
2017-07-17 23:17:42 +03:00
|
|
|
}
|
2017-03-11 05:28:13 +03:00
|
|
|
|
2018-06-01 19:29:32 +03:00
|
|
|
FileInodeState::FileInodeState() : tag(MATERIALIZED_IN_OVERLAY) {
|
2017-11-08 03:07:16 +03:00
|
|
|
checkInvariants();
|
|
|
|
}
|
|
|
|
|
2018-03-27 21:13:03 +03:00
|
|
|
/*
|
2018-06-01 19:29:32 +03:00
|
|
|
* Define FileInodeState destructor explicitly to avoid including
|
2018-03-27 21:13:03 +03:00
|
|
|
* some header files in FileInode.h
|
|
|
|
*/
|
2018-06-01 19:29:32 +03:00
|
|
|
FileInodeState::~FileInodeState() = default;
|
2018-03-27 21:13:03 +03:00
|
|
|
|
2018-06-01 19:29:32 +03:00
|
|
|
void FileInodeState::checkInvariants() {
|
2017-11-20 23:03:28 +03:00
|
|
|
switch (tag) {
|
2018-11-29 04:31:38 +03:00
|
|
|
case BLOB_NOT_LOADING:
|
2017-11-20 23:03:28 +03:00
|
|
|
CHECK(hash);
|
|
|
|
CHECK(!blobLoadingPromise);
|
|
|
|
return;
|
|
|
|
case BLOB_LOADING:
|
|
|
|
CHECK(hash);
|
|
|
|
CHECK(blobLoadingPromise);
|
2020-04-02 00:51:06 +03:00
|
|
|
#ifndef _WIN32
|
2018-12-06 22:33:06 +03:00
|
|
|
CHECK(readByteRanges.empty());
|
2020-04-02 00:51:06 +03:00
|
|
|
#endif
|
2017-11-20 23:03:28 +03:00
|
|
|
return;
|
|
|
|
case MATERIALIZED_IN_OVERLAY:
|
|
|
|
// 'materialized'
|
|
|
|
CHECK(!hash);
|
|
|
|
CHECK(!blobLoadingPromise);
|
2020-04-02 00:51:06 +03:00
|
|
|
#ifndef _WIN32
|
2018-12-06 22:33:06 +03:00
|
|
|
CHECK(readByteRanges.empty());
|
2020-04-02 00:51:06 +03:00
|
|
|
#endif
|
2017-11-20 23:03:28 +03:00
|
|
|
return;
|
2017-11-08 03:07:16 +03:00
|
|
|
}
|
2017-11-20 23:03:28 +03:00
|
|
|
|
|
|
|
XLOG(FATAL) << "Unexpected tag value: " << tag;
|
2017-07-27 21:48:19 +03:00
|
|
|
}
|
2017-11-20 23:03:28 +03:00
|
|
|
|
2018-03-23 22:34:59 +03:00
|
|
|
/*********************************************************************
|
|
|
|
* FileInode methods
|
|
|
|
********************************************************************/
|
|
|
|
|
2017-11-20 23:03:28 +03:00
|
|
|
// The FileInode is in NOT_LOADED or MATERIALIZED_IN_OVERLAY state.
|
2016-12-01 02:48:04 +03:00
|
|
|
FileInode::FileInode(
|
2018-03-20 03:01:15 +03:00
|
|
|
InodeNumber ino,
|
2016-12-13 04:48:45 +03:00
|
|
|
TreeInodePtr parentInode,
|
2016-12-13 04:48:43 +03:00
|
|
|
PathComponentPiece name,
|
2018-04-27 01:51:16 +03:00
|
|
|
mode_t initialMode,
|
2018-11-29 02:42:38 +03:00
|
|
|
const std::optional<InodeTimestamps>& initialTimestamps,
|
2018-10-24 04:48:38 +03:00
|
|
|
const std::optional<Hash>& hash)
|
2018-11-29 02:42:38 +03:00
|
|
|
: Base(ino, initialMode, initialTimestamps, std::move(parentInode), name),
|
2018-05-22 20:46:26 +03:00
|
|
|
state_(folly::in_place, hash) {}
|
2016-05-12 23:43:17 +03:00
|
|
|
|
2017-11-20 23:03:28 +03:00
|
|
|
// The FileInode is in MATERIALIZED_IN_OVERLAY state.
|
2016-12-01 02:48:04 +03:00
|
|
|
FileInode::FileInode(
|
2018-03-20 03:01:15 +03:00
|
|
|
InodeNumber ino,
|
2016-12-13 04:48:45 +03:00
|
|
|
TreeInodePtr parentInode,
|
2016-12-13 04:48:43 +03:00
|
|
|
PathComponentPiece name,
|
2018-04-27 01:51:16 +03:00
|
|
|
mode_t initialMode,
|
2018-11-29 02:42:38 +03:00
|
|
|
const InodeTimestamps& initialTimestamps)
|
2018-06-01 21:16:44 +03:00
|
|
|
: Base(ino, initialMode, initialTimestamps, std::move(parentInode), name),
|
2018-05-22 20:46:26 +03:00
|
|
|
state_(folly::in_place) {}
|
2016-07-06 05:53:15 +03:00
|
|
|
|
2020-04-02 00:51:06 +03:00
|
|
|
#ifndef _WIN32
|
2018-05-24 09:59:21 +03:00
|
|
|
folly::Future<Dispatcher::Attr> FileInode::setattr(
|
2018-01-03 03:25:03 +03:00
|
|
|
const fuse_setattr_in& attr) {
|
2018-04-27 06:41:38 +03:00
|
|
|
// If this file is inside of .eden it cannot be reparented, so getParentRacy()
|
|
|
|
// is okay.
|
|
|
|
auto parent = getParentRacy();
|
|
|
|
if (parent && parent->getNodeId() == getMount()->getDotEdenInodeNumber()) {
|
|
|
|
return folly::makeFuture<Dispatcher::Attr>(
|
|
|
|
InodeError(EPERM, inodePtrFromThis()));
|
|
|
|
}
|
|
|
|
|
2018-03-23 22:34:59 +03:00
|
|
|
auto setAttrs = [self = inodePtrFromThis(), attr](LockedState&& state) {
|
2018-12-13 03:48:09 +03:00
|
|
|
auto ino = self->getNodeId();
|
2018-03-20 03:01:15 +03:00
|
|
|
auto result = Dispatcher::Attr{self->getMount()->initStatData()};
|
2017-08-05 06:14:19 +03:00
|
|
|
|
2018-03-27 21:13:03 +03:00
|
|
|
DCHECK_EQ(State::MATERIALIZED_IN_OVERLAY, state->tag)
|
2017-12-05 02:07:44 +03:00
|
|
|
<< "Must have a file in the overlay at this point";
|
|
|
|
|
2018-01-03 03:25:03 +03:00
|
|
|
// Set the size of the file when FATTR_SIZE is set
|
|
|
|
if (attr.valid & FATTR_SIZE) {
|
2018-12-13 03:48:09 +03:00
|
|
|
// Throws upon error.
|
2019-11-07 05:21:39 +03:00
|
|
|
self->getOverlayFileAccess(state)->truncate(*self, attr.size);
|
2017-12-05 02:07:44 +03:00
|
|
|
}
|
2017-08-05 06:14:19 +03:00
|
|
|
|
2018-05-22 20:46:24 +03:00
|
|
|
auto metadata = self->getMount()->getInodeMetadataTable()->modifyOrThrow(
|
2018-12-13 03:48:09 +03:00
|
|
|
ino, [&](auto& metadata) {
|
2018-05-22 20:46:28 +03:00
|
|
|
metadata.updateFromAttr(self->getClock(), attr);
|
2018-05-22 20:46:24 +03:00
|
|
|
});
|
2018-05-22 20:46:26 +03:00
|
|
|
|
2017-12-05 02:07:44 +03:00
|
|
|
// We need to call fstat function here to get the size of the overlay
|
|
|
|
// file. We might update size in the result while truncating the file
|
2018-01-03 03:25:03 +03:00
|
|
|
// when FATTR_SIZE flag is set but when the flag is not set we
|
2017-12-05 02:07:44 +03:00
|
|
|
// have to return the correct size of the file even if some size is sent
|
|
|
|
// in attr.st.st_size.
|
2019-11-07 05:21:39 +03:00
|
|
|
off_t size = self->getOverlayFileAccess(state)->getFileSize(*self);
|
2018-12-13 03:48:09 +03:00
|
|
|
result.st.st_ino = ino.get();
|
|
|
|
result.st.st_size = size;
|
2018-05-22 20:46:24 +03:00
|
|
|
metadata.applyToStat(result.st);
|
2018-02-09 06:32:04 +03:00
|
|
|
result.st.st_nlink = 1;
|
|
|
|
updateBlockCount(result.st);
|
2016-09-19 22:48:11 +03:00
|
|
|
|
2017-12-05 02:07:44 +03:00
|
|
|
// Update the Journal
|
|
|
|
self->updateJournal();
|
|
|
|
return result;
|
2018-03-23 22:34:59 +03:00
|
|
|
};
|
|
|
|
|
|
|
|
// Minor optimization: if we know that the file is being completely truncated
|
|
|
|
// as part of this operation, there's no need to fetch the underlying data,
|
|
|
|
// so use truncateAndRun() rather than runWhileMaterialized()
|
|
|
|
bool truncate = (attr.valid & FATTR_SIZE) && attr.size == 0;
|
2018-03-27 21:13:03 +03:00
|
|
|
auto state = LockedState{this};
|
2018-03-23 22:34:59 +03:00
|
|
|
if (truncate) {
|
|
|
|
return truncateAndRun(std::move(state), setAttrs);
|
|
|
|
} else {
|
2018-11-29 04:31:38 +03:00
|
|
|
return runWhileMaterialized(std::move(state), nullptr, setAttrs);
|
2018-03-23 22:34:59 +03:00
|
|
|
}
|
2016-07-02 01:08:47 +03:00
|
|
|
}
|
|
|
|
|
2020-03-06 22:53:33 +03:00
|
|
|
folly::Future<std::string> FileInode::readlink(
|
|
|
|
ObjectFetchContext& fetchContext,
|
|
|
|
CacheHint cacheHint) {
|
2018-02-13 04:35:47 +03:00
|
|
|
if (dtype_t::Symlink != getType()) {
|
|
|
|
// man 2 readlink says: EINVAL The named file is not a symbolic link.
|
|
|
|
throw InodeError(EINVAL, inodePtrFromThis(), "not a symlink");
|
2016-05-17 00:48:28 +03:00
|
|
|
}
|
|
|
|
|
2017-03-02 19:16:18 +03:00
|
|
|
// The symlink contents are simply the file contents!
|
2020-03-06 22:53:33 +03:00
|
|
|
return readAll(fetchContext, cacheHint);
|
2016-05-12 23:43:17 +03:00
|
|
|
}
|
2020-04-02 00:51:06 +03:00
|
|
|
#endif // !_WIN32
|
2016-05-12 23:43:17 +03:00
|
|
|
|
2018-10-24 04:48:38 +03:00
|
|
|
std::optional<bool> FileInode::isSameAsFast(
|
2018-02-20 22:16:00 +03:00
|
|
|
const Hash& blobID,
|
|
|
|
TreeEntryType entryType) {
|
2017-11-08 03:07:16 +03:00
|
|
|
auto state = state_.rlock();
|
2020-04-02 00:51:06 +03:00
|
|
|
#ifndef _WIN32
|
2018-05-22 20:46:26 +03:00
|
|
|
if (entryType != treeEntryTypeFromMode(getMetadataLocked(*state).mode)) {
|
2017-07-27 09:39:02 +03:00
|
|
|
return false;
|
2017-05-02 04:45:31 +03:00
|
|
|
}
|
2020-04-02 00:51:06 +03:00
|
|
|
#else
|
|
|
|
// Note: the Windows-specific version of getMode() is safe to call here even
|
|
|
|
// though we are holding the state_ lock. On non-Windows getMetadataLocked()
|
|
|
|
// must be used instead when holding the lock.
|
|
|
|
if (entryType != treeEntryTypeFromMode(getMode())) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
#endif // !_WIN32
|
2017-03-11 05:28:13 +03:00
|
|
|
|
2018-10-24 04:48:38 +03:00
|
|
|
if (state->hash.has_value()) {
|
2018-05-01 08:02:14 +03:00
|
|
|
// This file is not materialized, so we can compare blob hashes.
|
|
|
|
// If the hashes are the same then assume the contents are the same.
|
|
|
|
//
|
|
|
|
// Unfortunately we cannot assume that the file contents are different if
|
|
|
|
// the hashes are different: Mercurial's blob hashes also include history
|
|
|
|
// metadata, so there may be multiple different blob hashes for the same
|
|
|
|
// file contents.
|
|
|
|
if (state->hash.value() == blobID) {
|
|
|
|
return true;
|
|
|
|
}
|
2017-05-02 04:45:31 +03:00
|
|
|
}
|
2018-10-24 04:48:38 +03:00
|
|
|
return std::nullopt;
|
2017-05-02 04:45:31 +03:00
|
|
|
}
|
|
|
|
|
2020-11-03 21:55:20 +03:00
|
|
|
folly::Future<bool> FileInode::isSameAsSlow(
|
|
|
|
const Hash& expectedBlobSha1,
|
|
|
|
ObjectFetchContext& fetchContext) {
|
|
|
|
return getSha1(fetchContext)
|
|
|
|
.thenTry([expectedBlobSha1](folly::Try<Hash>&& try_) {
|
|
|
|
if (try_.hasException()) {
|
|
|
|
XLOG(DBG2) << "Assuming changed: "
|
|
|
|
<< folly::exceptionStr(
|
|
|
|
try_.tryGetExceptionObject<std::exception>());
|
|
|
|
return false;
|
|
|
|
} else {
|
|
|
|
return try_.value() == expectedBlobSha1;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2018-06-13 11:14:32 +03:00
|
|
|
folly::Future<bool> FileInode::isSameAs(
|
|
|
|
const Blob& blob,
|
2020-02-06 00:13:11 +03:00
|
|
|
TreeEntryType entryType,
|
|
|
|
ObjectFetchContext& fetchContext) {
|
2018-02-20 22:16:00 +03:00
|
|
|
auto result = isSameAsFast(blob.getHash(), entryType);
|
2018-10-24 04:48:38 +03:00
|
|
|
if (result.has_value()) {
|
2017-07-27 09:39:02 +03:00
|
|
|
return result.value();
|
2017-05-02 04:45:31 +03:00
|
|
|
}
|
|
|
|
|
2018-11-29 03:07:04 +03:00
|
|
|
auto blobSha1 = Hash::sha1(blob.getContents());
|
2020-11-03 21:55:20 +03:00
|
|
|
return isSameAsSlow(blobSha1, fetchContext);
|
|
|
|
}
|
|
|
|
|
|
|
|
folly::Future<bool> FileInode::isSameAs(
|
|
|
|
const Hash& blobID,
|
|
|
|
const Hash& blobSha1,
|
|
|
|
TreeEntryType entryType,
|
|
|
|
ObjectFetchContext& fetchContext) {
|
|
|
|
auto result = isSameAsFast(blobID, entryType);
|
|
|
|
if (result.has_value()) {
|
|
|
|
return result.value();
|
|
|
|
}
|
|
|
|
|
|
|
|
return isSameAsSlow(blobSha1, fetchContext);
|
2017-05-02 04:45:31 +03:00
|
|
|
}
|
|
|
|
|
2018-02-20 22:16:00 +03:00
|
|
|
folly::Future<bool> FileInode::isSameAs(
|
|
|
|
const Hash& blobID,
|
2020-02-06 00:13:11 +03:00
|
|
|
TreeEntryType entryType,
|
|
|
|
ObjectFetchContext& fetchContext) {
|
2018-02-20 22:16:00 +03:00
|
|
|
auto result = isSameAsFast(blobID, entryType);
|
2018-10-24 04:48:38 +03:00
|
|
|
if (result.has_value()) {
|
2017-07-27 09:39:02 +03:00
|
|
|
return makeFuture(result.value());
|
2017-02-16 07:31:48 +03:00
|
|
|
}
|
|
|
|
|
2020-02-06 00:13:11 +03:00
|
|
|
auto f1 = getSha1(fetchContext);
|
|
|
|
auto f2 = getMount()->getObjectStore()->getBlobSha1(blobID, fetchContext);
|
inodes: do not ignore errors when computing sha1
Summary:
On Windows, computing the sha1 of a materialized file requires opening up the
file in the working copy, as the file is cached there. Interestingly, this
potentially means that for computing the sha1 of a file, EdenFS may receive a
callback from ProjectedFS about that file or a parent directory. At this point,
EdenFS just refuses to serve this callback, as doing so may trigger an infinite
loop, or simply deadlocks. While this may sound weird, recursive callbacks are
not expected, as this signify that EdenFS view of the working copy doesn't
match what it actually is.
To close the loop, and from a code perspective, this means that computing the
sha1 of a file can fail and can throw an exception. Unfortunately, the code
didn't reflect this fact and exceptions were simply ignored, when that happens
during a checkout operation, this can leave the working copy in a weird state,
further agravating the mismatch between EdenFS view of the working copy, and
what it actually is.
Reviewed By: wez
Differential Revision: D24282048
fbshipit-source-id: 745af03189fe345150f0b1792ee1b37a1b8fb0d4
2020-10-16 09:55:31 +03:00
|
|
|
return folly::collectUnsafe(f1, f2).thenTry(
|
|
|
|
[](folly::Try<std::tuple<Hash, Hash>>&& try_) {
|
|
|
|
if (try_.hasException()) {
|
|
|
|
XLOG(DBG2) << "Assuming changed: "
|
|
|
|
<< folly::exceptionStr(
|
|
|
|
try_.tryGetExceptionObject<std::exception>());
|
|
|
|
return false;
|
|
|
|
} else {
|
|
|
|
auto hashes = std::move(try_).value();
|
|
|
|
return std::get<0>(hashes) == std::get<1>(hashes);
|
|
|
|
}
|
2020-03-10 21:31:05 +03:00
|
|
|
});
|
2017-02-16 07:31:48 +03:00
|
|
|
}
|
|
|
|
|
2020-04-02 00:51:06 +03:00
|
|
|
#ifndef _WIN32
|
2017-03-03 01:20:34 +03:00
|
|
|
mode_t FileInode::getMode() const {
|
2018-05-22 20:46:26 +03:00
|
|
|
return getMetadata().mode;
|
2017-03-03 01:20:34 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
mode_t FileInode::getPermissions() const {
|
|
|
|
return (getMode() & 07777);
|
|
|
|
}
|
|
|
|
|
2018-05-22 20:46:24 +03:00
|
|
|
InodeMetadata FileInode::getMetadata() const {
|
|
|
|
auto lock = state_.rlock();
|
|
|
|
return getMetadataLocked(*lock);
|
|
|
|
}
|
|
|
|
|
2020-04-02 00:51:06 +03:00
|
|
|
#else
|
|
|
|
mode_t FileInode::getMode() const {
|
|
|
|
// On Windows we only store the dir type info and no permissions bits here.
|
|
|
|
// For file it will always be a regular file.
|
|
|
|
return _S_IFREG;
|
|
|
|
}
|
|
|
|
#endif // !_WIN32
|
|
|
|
|
2018-10-24 04:48:38 +03:00
|
|
|
std::optional<Hash> FileInode::getBlobHash() const {
|
2017-03-11 05:28:13 +03:00
|
|
|
return state_.rlock()->hash;
|
|
|
|
}
|
|
|
|
|
2017-03-11 05:28:08 +03:00
|
|
|
void FileInode::materializeInParent() {
|
|
|
|
auto renameLock = getMount()->acquireRenameLock();
|
|
|
|
auto loc = getLocationInfo(renameLock);
|
|
|
|
if (loc.parent && !loc.unlinked) {
|
2018-03-24 04:17:05 +03:00
|
|
|
loc.parent->childMaterialized(renameLock, loc.name);
|
2017-03-11 05:28:08 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-02 00:51:06 +03:00
|
|
|
#ifndef _WIN32
|
2016-12-01 02:48:04 +03:00
|
|
|
Future<vector<string>> FileInode::listxattr() {
|
2016-05-12 23:43:17 +03:00
|
|
|
vector<string> attributes;
|
2019-09-03 18:02:16 +03:00
|
|
|
// We used to return kXattrSha1 here for regular files, but
|
|
|
|
// that caused some annoying behavior with appledouble
|
|
|
|
// metadata files being created by various tools that wanted
|
|
|
|
// to preserve all of these attributes across copy on macos.
|
|
|
|
// So now we just return an empty set on all systems.
|
2016-05-12 23:43:17 +03:00
|
|
|
return attributes;
|
|
|
|
}
|
|
|
|
|
2016-12-01 02:48:04 +03:00
|
|
|
Future<string> FileInode::getxattr(StringPiece name) {
|
2016-05-12 23:43:17 +03:00
|
|
|
// Currently, we only support the xattr for the SHA-1 of a regular file.
|
2016-05-26 18:22:22 +03:00
|
|
|
if (name != kXattrSha1) {
|
2017-03-11 05:28:13 +03:00
|
|
|
return makeFuture<string>(InodeError(kENOATTR, inodePtrFromThis()));
|
2016-05-26 18:22:22 +03:00
|
|
|
}
|
|
|
|
|
2020-09-01 13:37:16 +03:00
|
|
|
static auto context =
|
|
|
|
ObjectFetchContext::getNullContextWithCauseDetail("FileInode::getxattr");
|
|
|
|
return getSha1(*context).thenValue([](Hash hash) { return hash.toString(); });
|
2016-05-28 04:16:30 +03:00
|
|
|
}
|
2020-04-02 00:51:06 +03:00
|
|
|
#else
|
|
|
|
|
|
|
|
AbsolutePath FileInode::getMaterializedFilePath() {
|
|
|
|
auto filePath = getPath();
|
|
|
|
if (!filePath.has_value()) {
|
|
|
|
throw InodeError(
|
|
|
|
EINVAL, inodePtrFromThis(), "File is unlinked", getLogPath());
|
|
|
|
}
|
|
|
|
return getMount()->getPath() + filePath.value();
|
|
|
|
}
|
|
|
|
#endif
|
2016-05-28 04:16:30 +03:00
|
|
|
|
2020-02-06 00:13:11 +03:00
|
|
|
Future<Hash> FileInode::getSha1(ObjectFetchContext& fetchContext) {
|
2018-03-27 21:13:03 +03:00
|
|
|
auto state = LockedState{this};
|
2017-11-08 03:07:16 +03:00
|
|
|
|
2017-11-20 23:03:28 +03:00
|
|
|
switch (state->tag) {
|
2018-11-29 04:31:38 +03:00
|
|
|
case State::BLOB_NOT_LOADING:
|
2017-11-20 23:03:28 +03:00
|
|
|
case State::BLOB_LOADING:
|
2018-11-29 04:31:38 +03:00
|
|
|
// If a file is not materialized, it should have a hash value.
|
2020-02-06 00:13:11 +03:00
|
|
|
return getObjectStore()->getBlobSha1(state->hash.value(), fetchContext);
|
2017-11-20 23:03:28 +03:00
|
|
|
case State::MATERIALIZED_IN_OVERLAY:
|
2020-04-02 00:51:06 +03:00
|
|
|
#ifdef _WIN32
|
inodes: do not ignore errors when computing sha1
Summary:
On Windows, computing the sha1 of a materialized file requires opening up the
file in the working copy, as the file is cached there. Interestingly, this
potentially means that for computing the sha1 of a file, EdenFS may receive a
callback from ProjectedFS about that file or a parent directory. At this point,
EdenFS just refuses to serve this callback, as doing so may trigger an infinite
loop, or simply deadlocks. While this may sound weird, recursive callbacks are
not expected, as this signify that EdenFS view of the working copy doesn't
match what it actually is.
To close the loop, and from a code perspective, this means that computing the
sha1 of a file can fail and can throw an exception. Unfortunately, the code
didn't reflect this fact and exceptions were simply ignored, when that happens
during a checkout operation, this can leave the working copy in a weird state,
further agravating the mismatch between EdenFS view of the working copy, and
what it actually is.
Reviewed By: wez
Differential Revision: D24282048
fbshipit-source-id: 745af03189fe345150f0b1792ee1b37a1b8fb0d4
2020-10-16 09:55:31 +03:00
|
|
|
return folly::makeFutureWith(
|
|
|
|
[this] { return getFileSha1(getMaterializedFilePath()); });
|
2020-04-02 00:51:06 +03:00
|
|
|
#else
|
2019-11-07 05:21:39 +03:00
|
|
|
return getOverlayFileAccess(state)->getSha1(*this);
|
2020-04-02 00:51:06 +03:00
|
|
|
#endif // _WIN32
|
2017-07-27 09:39:02 +03:00
|
|
|
}
|
2017-11-20 23:03:28 +03:00
|
|
|
|
|
|
|
XLOG(FATAL) << "FileInode in illegal state: " << state->tag;
|
2017-07-27 09:39:02 +03:00
|
|
|
}
|
|
|
|
|
2020-06-18 20:37:32 +03:00
|
|
|
folly::Future<struct stat> FileInode::stat(ObjectFetchContext& context) {
|
2018-10-18 23:59:34 +03:00
|
|
|
auto st = getMount()->initStatData();
|
|
|
|
st.st_nlink = 1; // Eden does not support hard links yet.
|
|
|
|
st.st_ino = getNodeId().get();
|
|
|
|
// NOTE: we don't set rdev to anything special here because we
|
|
|
|
// don't support committing special device nodes.
|
|
|
|
|
|
|
|
auto state = LockedState{this};
|
2018-04-24 23:20:52 +03:00
|
|
|
|
2020-04-29 08:07:58 +03:00
|
|
|
#ifndef _WIN32
|
2018-10-18 23:59:34 +03:00
|
|
|
getMetadataLocked(*state).applyToStat(st);
|
2020-04-29 08:07:58 +03:00
|
|
|
#endif
|
2017-07-27 09:39:02 +03:00
|
|
|
|
2018-10-18 23:59:34 +03:00
|
|
|
switch (state->tag) {
|
2018-11-29 04:31:38 +03:00
|
|
|
case State::BLOB_NOT_LOADING:
|
2018-10-18 23:59:34 +03:00
|
|
|
case State::BLOB_LOADING:
|
|
|
|
CHECK(state->hash.has_value());
|
2019-06-26 06:25:14 +03:00
|
|
|
// While getBlobSize will sometimes need to fetch a blob to compute the
|
|
|
|
// size, if it's already known, return the cached size. This is especially
|
|
|
|
// a win after restarting Eden - size can be loaded from the local cache
|
|
|
|
// more cheaply than deserializing an entire blob.
|
2018-10-18 23:59:34 +03:00
|
|
|
return getObjectStore()
|
2020-06-18 20:37:32 +03:00
|
|
|
->getBlobSize(*state->hash, context)
|
2019-06-26 06:25:14 +03:00
|
|
|
.thenValue([st](const uint64_t size) mutable {
|
|
|
|
st.st_size = size;
|
2018-10-18 23:59:34 +03:00
|
|
|
updateBlockCount(st);
|
|
|
|
return st;
|
|
|
|
});
|
|
|
|
|
|
|
|
case State::MATERIALIZED_IN_OVERLAY:
|
2020-04-29 08:07:58 +03:00
|
|
|
#ifdef _WIN32
|
|
|
|
auto filePath = getPath();
|
|
|
|
if (!filePath.has_value()) {
|
|
|
|
throw InodeError(ENOENT, inodePtrFromThis(), "not a symlink");
|
|
|
|
}
|
|
|
|
AbsolutePath pathToFile = getMount()->getPath() + filePath.value();
|
|
|
|
struct stat targetStat;
|
|
|
|
if (::stat(pathToFile.c_str(), &targetStat) == 0) {
|
|
|
|
st.st_size = targetStat.st_size;
|
|
|
|
}
|
|
|
|
#else
|
2019-11-07 05:21:39 +03:00
|
|
|
st.st_size = getOverlayFileAccess(state)->getFileSize(*this);
|
2020-04-29 08:07:58 +03:00
|
|
|
#endif
|
2018-10-18 23:59:34 +03:00
|
|
|
updateBlockCount(st);
|
|
|
|
return st;
|
|
|
|
}
|
2019-09-11 21:09:13 +03:00
|
|
|
|
2019-11-23 00:26:28 +03:00
|
|
|
return EDEN_BUG_FUTURE(struct stat)
|
2020-04-29 04:57:12 +03:00
|
|
|
<< "unexpected FileInode state tag " << enumValue(state->tag);
|
2017-07-27 09:39:02 +03:00
|
|
|
}
|
|
|
|
|
2018-02-09 06:32:04 +03:00
|
|
|
void FileInode::updateBlockCount(struct stat& st) {
|
2020-04-29 08:07:58 +03:00
|
|
|
// win32 does not have stat::st_blocks
|
|
|
|
#ifndef _WIN32
|
2018-02-09 06:32:04 +03:00
|
|
|
// Compute a value to store in st_blocks based on st_size.
|
|
|
|
// Note that st_blocks always refers to 512 byte blocks, regardless of the
|
|
|
|
// value we report in st.st_blksize.
|
|
|
|
static constexpr off_t kBlockSize = 512;
|
|
|
|
st.st_blocks = ((st.st_size + kBlockSize - 1) / kBlockSize);
|
2020-04-29 08:07:58 +03:00
|
|
|
#endif
|
2018-02-09 06:32:04 +03:00
|
|
|
}
|
|
|
|
|
2020-04-29 08:07:58 +03:00
|
|
|
#ifndef _WIN32
|
2017-07-27 09:39:02 +03:00
|
|
|
void FileInode::fsync(bool datasync) {
|
2018-03-27 21:13:03 +03:00
|
|
|
auto state = LockedState{this};
|
2018-12-13 03:48:09 +03:00
|
|
|
if (state->isMaterialized()) {
|
2019-11-07 05:21:39 +03:00
|
|
|
getOverlayFileAccess(state)->fsync(*this, datasync);
|
2016-05-12 23:43:17 +03:00
|
|
|
}
|
2017-07-27 09:39:02 +03:00
|
|
|
}
|
2020-04-02 00:51:06 +03:00
|
|
|
#endif
|
2017-07-27 09:39:02 +03:00
|
|
|
|
2020-02-06 00:13:11 +03:00
|
|
|
Future<string> FileInode::readAll(
|
|
|
|
ObjectFetchContext& fetchContext,
|
|
|
|
CacheHint cacheHint) {
|
2018-11-29 04:31:38 +03:00
|
|
|
auto interest = BlobCache::Interest::LikelyNeededAgain;
|
|
|
|
switch (cacheHint) {
|
|
|
|
case CacheHint::NotNeededAgain:
|
|
|
|
interest = BlobCache::Interest::UnlikelyNeededAgain;
|
|
|
|
break;
|
|
|
|
case CacheHint::LikelyNeededAgain:
|
|
|
|
// readAll() with LikelyNeededAgain is primarily called for files read
|
|
|
|
// by Eden itself, like .gitignore, and for symlinks on kernels that don't
|
|
|
|
// cache readlink. At least keep the blob around while the inode is
|
|
|
|
// loaded.
|
|
|
|
interest = BlobCache::Interest::WantHandle;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return runWhileDataLoaded<Future<string>>(
|
2018-03-27 21:13:03 +03:00
|
|
|
LockedState{this},
|
2018-11-29 04:31:38 +03:00
|
|
|
interest,
|
2020-02-06 00:13:11 +03:00
|
|
|
fetchContext,
|
2018-11-29 04:31:38 +03:00
|
|
|
nullptr,
|
|
|
|
[self = inodePtrFromThis()](
|
|
|
|
LockedState&& state, std::shared_ptr<const Blob> blob) -> string {
|
2018-03-23 22:34:59 +03:00
|
|
|
std::string result;
|
|
|
|
switch (state->tag) {
|
|
|
|
case State::MATERIALIZED_IN_OVERLAY: {
|
2020-04-02 00:51:06 +03:00
|
|
|
#ifdef _WIN32
|
utils: add a platform independent FileUtils
Summary:
Up to now, Windows had to have its own version of folly::{readFile, writeFile,
writeFileAtomic} as these only operate on `char *` path, which can only
represent ascii paths on Windows. Since the Windows version is slightly
different from folly, this forced the code to either ifdef _WIN32, or use the
folly version pretending that it would be OK. The Windows version was also
behaving slightly differently from folly. For instance, where folly would
return a boolean to indicate success, on Windows we would throw an exception.
To simplify our code, add type safety and unify both, we can implement our own
wrappers on top of either folly or Windows APIs.
We still have some code that uses folly::readFile but these should only be
dealing with filedescriptors. As a following step, we may want to have our own
File class that wraps a file descriptor/HANDLE so we can completely remove all
uses of folly::readFile.
Reviewed By: wez
Differential Revision: D23037325
fbshipit-source-id: 2b9a026f3ee6220ef55097abe649b23e38d9fe91
2020-08-15 04:54:41 +03:00
|
|
|
result = readFile(self->getMaterializedFilePath()).value();
|
2020-04-02 00:51:06 +03:00
|
|
|
#else
|
2018-11-29 04:31:38 +03:00
|
|
|
DCHECK(!blob);
|
2019-11-07 05:21:39 +03:00
|
|
|
result = self->getOverlayFileAccess(state)->readAllContents(*self);
|
2020-04-02 00:51:06 +03:00
|
|
|
#endif
|
2018-03-23 22:34:59 +03:00
|
|
|
break;
|
|
|
|
}
|
2018-11-29 04:31:38 +03:00
|
|
|
case State::BLOB_NOT_LOADING: {
|
|
|
|
const auto& contentsBuf = blob->getContents();
|
2018-03-23 22:34:59 +03:00
|
|
|
folly::io::Cursor cursor(&contentsBuf);
|
|
|
|
result =
|
|
|
|
cursor.readFixedString(contentsBuf.computeChainDataLength());
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
EDEN_BUG() << "neither materialized nor loaded during "
|
|
|
|
"runWhileDataLoaded() call";
|
|
|
|
}
|
2017-07-27 09:39:02 +03:00
|
|
|
|
2020-04-02 00:51:06 +03:00
|
|
|
#ifndef _WIN32
|
2018-03-23 22:34:59 +03:00
|
|
|
// We want to update atime after the read operation.
|
2018-06-01 21:16:44 +03:00
|
|
|
self->updateAtimeLocked(*state);
|
2020-04-02 00:51:06 +03:00
|
|
|
#endif // !_WIN32
|
|
|
|
|
2018-03-23 22:34:59 +03:00
|
|
|
return result;
|
|
|
|
});
|
2017-07-27 09:39:02 +03:00
|
|
|
}
|
|
|
|
|
2020-04-02 00:51:06 +03:00
|
|
|
#ifdef _WIN32
|
|
|
|
void FileInode::materialize() {
|
|
|
|
{
|
|
|
|
auto state = LockedState{this};
|
|
|
|
state.setMaterialized();
|
|
|
|
}
|
|
|
|
|
|
|
|
materializeInParent();
|
2020-07-21 10:38:35 +03:00
|
|
|
updateJournal();
|
2020-04-02 00:51:06 +03:00
|
|
|
}
|
|
|
|
#else
|
|
|
|
|
2020-06-18 20:37:32 +03:00
|
|
|
Future<BufVec>
|
|
|
|
FileInode::read(size_t size, off_t off, ObjectFetchContext& context) {
|
2018-12-06 22:33:06 +03:00
|
|
|
DCHECK_GE(off, 0);
|
2018-11-29 04:31:38 +03:00
|
|
|
return runWhileDataLoaded<Future<BufVec>>(
|
2018-03-27 21:13:03 +03:00
|
|
|
LockedState{this},
|
2018-11-29 04:31:38 +03:00
|
|
|
BlobCache::Interest::WantHandle,
|
2020-02-06 00:13:11 +03:00
|
|
|
// This function is only called by FUSE.
|
2020-06-18 20:37:32 +03:00
|
|
|
context,
|
2018-11-29 04:31:38 +03:00
|
|
|
nullptr,
|
|
|
|
[size, off, self = inodePtrFromThis()](
|
|
|
|
LockedState&& state, std::shared_ptr<const Blob> blob) -> BufVec {
|
2018-03-23 22:34:59 +03:00
|
|
|
SCOPE_SUCCESS {
|
2018-06-01 21:16:44 +03:00
|
|
|
self->updateAtimeLocked(*state);
|
2018-03-23 22:34:59 +03:00
|
|
|
};
|
|
|
|
|
2018-11-29 04:31:38 +03:00
|
|
|
// Materialized either before or during blob load.
|
2018-03-23 22:34:59 +03:00
|
|
|
if (state->tag == State::MATERIALIZED_IN_OVERLAY) {
|
2019-11-07 05:21:39 +03:00
|
|
|
return self->getOverlayFileAccess(state)->read(*self, size, off);
|
2018-11-29 04:31:38 +03:00
|
|
|
}
|
2018-03-22 08:21:16 +03:00
|
|
|
|
2018-11-29 04:31:38 +03:00
|
|
|
// runWhileDataLoaded() ensures that the state is either
|
|
|
|
// MATERIALIZED_IN_OVERLAY or BLOB_NOT_LOADING
|
|
|
|
DCHECK_EQ(state->tag, State::BLOB_NOT_LOADING);
|
|
|
|
DCHECK(blob) << "blob missing after load completed";
|
2018-12-06 22:33:06 +03:00
|
|
|
|
|
|
|
state->readByteRanges.add(off, off + size);
|
|
|
|
if (state->readByteRanges.covers(0, blob->getSize())) {
|
|
|
|
XLOG(DBG4) << "Inode " << self->getNodeId()
|
|
|
|
<< " dropping interest for blob " << blob->getHash()
|
|
|
|
<< " because it's been fully read.";
|
|
|
|
state->interestHandle.reset();
|
|
|
|
state->readByteRanges.clear();
|
|
|
|
}
|
|
|
|
|
2018-11-29 04:31:38 +03:00
|
|
|
auto buf = blob->getContents();
|
|
|
|
folly::io::Cursor cursor(&buf);
|
2018-03-22 08:21:16 +03:00
|
|
|
|
2018-11-29 04:31:38 +03:00
|
|
|
if (!cursor.canAdvance(off)) {
|
|
|
|
// Seek beyond EOF. Return an empty result.
|
|
|
|
return BufVec{folly::IOBuf::wrapBuffer("", 0)};
|
2018-03-23 22:34:59 +03:00
|
|
|
}
|
2018-11-29 04:31:38 +03:00
|
|
|
|
|
|
|
cursor.skip(off);
|
|
|
|
|
|
|
|
std::unique_ptr<folly::IOBuf> result;
|
|
|
|
cursor.cloneAtMost(result, size);
|
|
|
|
|
|
|
|
return BufVec{std::move(result)};
|
2018-03-23 22:34:59 +03:00
|
|
|
});
|
2017-07-27 09:39:02 +03:00
|
|
|
}
|
|
|
|
|
2018-03-23 22:34:59 +03:00
|
|
|
size_t FileInode::writeImpl(
|
2018-03-27 21:13:03 +03:00
|
|
|
LockedState& state,
|
2018-03-23 22:34:59 +03:00
|
|
|
const struct iovec* iov,
|
|
|
|
size_t numIovecs,
|
|
|
|
off_t off) {
|
2018-03-27 21:13:03 +03:00
|
|
|
DCHECK_EQ(state->tag, State::MATERIALIZED_IN_OVERLAY);
|
2017-07-27 09:39:02 +03:00
|
|
|
|
2019-11-07 05:21:39 +03:00
|
|
|
auto xfer = getOverlayFileAccess(state)->write(*this, iov, numIovecs, off);
|
2017-08-15 09:07:53 +03:00
|
|
|
|
2018-06-01 21:16:44 +03:00
|
|
|
updateMtimeAndCtimeLocked(*state, getNow());
|
2017-08-15 09:07:53 +03:00
|
|
|
|
2018-10-08 21:11:34 +03:00
|
|
|
state.unlock();
|
|
|
|
|
2020-07-21 10:38:35 +03:00
|
|
|
updateJournal();
|
2018-10-08 21:11:34 +03:00
|
|
|
|
2017-07-27 09:39:02 +03:00
|
|
|
return xfer;
|
|
|
|
}
|
|
|
|
|
2018-03-23 22:34:59 +03:00
|
|
|
folly::Future<size_t> FileInode::write(BufVec&& buf, off_t off) {
|
|
|
|
return runWhileMaterialized(
|
2018-03-27 21:13:03 +03:00
|
|
|
LockedState{this},
|
2018-11-29 04:31:38 +03:00
|
|
|
nullptr,
|
2018-03-23 22:34:59 +03:00
|
|
|
[buf = std::move(buf), off, self = inodePtrFromThis()](
|
|
|
|
LockedState&& state) {
|
2020-08-03 21:13:33 +03:00
|
|
|
auto vec = buf->getIov();
|
2018-03-23 22:34:59 +03:00
|
|
|
return self->writeImpl(state, vec.data(), vec.size(), off);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2017-12-05 22:08:56 +03:00
|
|
|
folly::Future<size_t> FileInode::write(folly::StringPiece data, off_t off) {
|
2018-03-27 21:13:03 +03:00
|
|
|
auto state = LockedState{this};
|
2017-08-15 09:07:53 +03:00
|
|
|
|
2018-03-23 22:34:59 +03:00
|
|
|
// If we are currently materialized we don't need to copy the input data.
|
|
|
|
if (state->tag == State::MATERIALIZED_IN_OVERLAY) {
|
|
|
|
struct iovec iov;
|
|
|
|
iov.iov_base = const_cast<char*>(data.data());
|
|
|
|
iov.iov_len = data.size();
|
|
|
|
return writeImpl(state, &iov, 1, off);
|
2017-07-27 09:39:02 +03:00
|
|
|
}
|
2017-08-15 09:07:53 +03:00
|
|
|
|
2018-03-23 22:34:59 +03:00
|
|
|
return runWhileMaterialized(
|
|
|
|
std::move(state),
|
2018-11-29 04:31:38 +03:00
|
|
|
nullptr,
|
2018-03-26 22:38:57 +03:00
|
|
|
[data = data.str(), off, self = inodePtrFromThis()](
|
|
|
|
LockedState&& stateLock) {
|
2018-03-23 22:34:59 +03:00
|
|
|
struct iovec iov;
|
|
|
|
iov.iov_base = const_cast<char*>(data.data());
|
|
|
|
iov.iov_len = data.size();
|
2018-03-26 22:38:57 +03:00
|
|
|
return self->writeImpl(stateLock, &iov, 1, off);
|
2018-03-23 22:34:59 +03:00
|
|
|
});
|
2017-07-27 09:39:02 +03:00
|
|
|
}
|
2020-04-02 00:51:06 +03:00
|
|
|
#endif
|
2017-07-27 09:39:02 +03:00
|
|
|
|
2018-11-29 04:31:38 +03:00
|
|
|
Future<std::shared_ptr<const Blob>> FileInode::startLoadingData(
|
|
|
|
LockedState state,
|
2020-02-06 00:13:11 +03:00
|
|
|
BlobCache::Interest interest,
|
2020-07-02 21:58:46 +03:00
|
|
|
ObjectFetchContext& fetchContext) {
|
2018-11-29 04:31:38 +03:00
|
|
|
DCHECK_EQ(state->tag, State::BLOB_NOT_LOADING);
|
2017-07-27 09:39:02 +03:00
|
|
|
|
2018-03-23 22:34:59 +03:00
|
|
|
// Start the blob load first in case this throws an exception.
|
|
|
|
// Ideally the state transition is no-except in tandem with the
|
|
|
|
// Future's .then call.
|
2020-02-06 00:13:11 +03:00
|
|
|
auto getBlobFuture = getMount()->getBlobAccess()->getBlob(
|
2020-07-02 21:58:46 +03:00
|
|
|
state->hash.value(), fetchContext, interest);
|
2017-07-27 09:39:02 +03:00
|
|
|
|
2018-03-23 22:34:59 +03:00
|
|
|
// Everything from here through blobFuture.then should be noexcept.
|
|
|
|
state->blobLoadingPromise.emplace();
|
|
|
|
auto resultFuture = state->blobLoadingPromise->getFuture();
|
|
|
|
state->tag = State::BLOB_LOADING;
|
2017-07-27 09:39:02 +03:00
|
|
|
|
2018-03-23 22:34:59 +03:00
|
|
|
// Unlock state_ while we wait on the blob data to load
|
|
|
|
state.unlock();
|
2018-01-04 04:18:32 +03:00
|
|
|
|
2017-11-11 00:23:26 +03:00
|
|
|
auto self = inodePtrFromThis(); // separate line for formatting
|
2018-11-29 04:31:38 +03:00
|
|
|
std::move(getBlobFuture)
|
|
|
|
.thenTry([self](folly::Try<BlobCache::GetResult> tryResult) mutable {
|
2018-03-27 21:13:03 +03:00
|
|
|
auto state = LockedState{self};
|
2017-07-27 09:39:02 +03:00
|
|
|
|
2017-12-22 23:27:48 +03:00
|
|
|
switch (state->tag) {
|
2018-11-29 04:31:38 +03:00
|
|
|
case State::BLOB_NOT_LOADING:
|
|
|
|
EDEN_BUG()
|
|
|
|
<< "A blob load finished when the inode was in BLOB_NOT_LOADING state";
|
|
|
|
|
2017-12-22 23:27:48 +03:00
|
|
|
// Since the load doesn't hold the state lock for its duration,
|
|
|
|
// sanity check that the inode is still in loading state.
|
|
|
|
//
|
2018-03-23 22:34:59 +03:00
|
|
|
// Note that someone else may have grabbed the lock before us and
|
|
|
|
// materialized the FileInode, so we may already be
|
|
|
|
// MATERIALIZED_IN_OVERLAY at this point.
|
2018-01-04 04:18:32 +03:00
|
|
|
case State::BLOB_LOADING: {
|
|
|
|
auto promise = std::move(*state->blobLoadingPromise);
|
2018-10-24 04:48:38 +03:00
|
|
|
state->blobLoadingPromise.reset();
|
2018-11-29 04:31:38 +03:00
|
|
|
state->tag = State::BLOB_NOT_LOADING;
|
2017-12-22 23:27:48 +03:00
|
|
|
|
2018-11-29 04:31:38 +03:00
|
|
|
// Call the Future's subscribers while the state_ lock is not
|
|
|
|
// held. Even if the FileInode has transitioned to a materialized
|
|
|
|
// state, any pending loads must be unblocked.
|
|
|
|
if (tryResult.hasValue()) {
|
|
|
|
state->interestHandle = std::move(tryResult->interestHandle);
|
|
|
|
state.unlock();
|
|
|
|
promise.setValue(std::move(tryResult->blob));
|
2017-12-22 23:27:48 +03:00
|
|
|
} else {
|
|
|
|
state.unlock();
|
2018-11-29 04:31:38 +03:00
|
|
|
promise.setException(std::move(tryResult).exception());
|
2017-12-22 23:27:48 +03:00
|
|
|
}
|
2018-11-29 04:31:38 +03:00
|
|
|
return;
|
2018-01-04 04:18:32 +03:00
|
|
|
}
|
2017-12-22 23:27:48 +03:00
|
|
|
|
2018-01-04 04:18:32 +03:00
|
|
|
case State::MATERIALIZED_IN_OVERLAY:
|
2018-03-23 22:34:59 +03:00
|
|
|
// The load raced with a someone materializing the file to truncate
|
2018-11-29 04:31:38 +03:00
|
|
|
// it. Nothing left to do here. The truncation completed the
|
|
|
|
// promise with a null blob.
|
|
|
|
CHECK_EQ(false, state->blobLoadingPromise.has_value());
|
|
|
|
return;
|
2017-12-22 23:27:48 +03:00
|
|
|
}
|
|
|
|
})
|
2018-10-23 23:39:59 +03:00
|
|
|
.thenError([](folly::exception_wrapper&&) {
|
2017-12-22 23:27:48 +03:00
|
|
|
// We get here if EDEN_BUG() didn't terminate the process, or if we
|
2018-01-04 04:18:32 +03:00
|
|
|
// threw in the preceding block. Both are bad because we won't
|
2017-12-22 23:27:48 +03:00
|
|
|
// automatically propagate the exception to resultFuture and we
|
|
|
|
// can't trust the state of anything if we get here.
|
|
|
|
// Rather than leaving something hanging, we suicide.
|
|
|
|
// We could probably do a bit better with the error handling here :-/
|
|
|
|
XLOG(FATAL)
|
|
|
|
<< "Failed to propagate failure in getBlob(), no choice but to die";
|
|
|
|
});
|
2018-03-23 22:34:59 +03:00
|
|
|
return resultFuture;
|
2017-11-11 00:23:26 +03:00
|
|
|
}
|
|
|
|
|
2020-04-02 00:51:06 +03:00
|
|
|
#ifndef _WIN32
|
2018-11-29 04:31:38 +03:00
|
|
|
void FileInode::materializeNow(
|
|
|
|
LockedState& state,
|
|
|
|
std::shared_ptr<const Blob> blob) {
|
|
|
|
// This function should only be called from the BLOB_NOT_LOADING state
|
|
|
|
DCHECK_EQ(state->tag, State::BLOB_NOT_LOADING);
|
2017-07-27 09:39:02 +03:00
|
|
|
|
2020-09-01 13:37:16 +03:00
|
|
|
static auto context = ObjectFetchContext::getNullContextWithCauseDetail(
|
|
|
|
"FileInode::materializeNow");
|
2018-12-13 03:48:09 +03:00
|
|
|
// If the blob metadata is immediately available, use it to populate the SHA-1
|
|
|
|
// value in the overlay for this file.
|
2018-03-27 21:13:03 +03:00
|
|
|
// Since this uses state->hash we perform this before calling
|
2018-12-13 03:48:09 +03:00
|
|
|
// state.setMaterialized().
|
2020-09-01 13:37:16 +03:00
|
|
|
auto blobSha1Future =
|
|
|
|
getObjectStore()->getBlobSha1(state->hash.value(), *context);
|
2018-12-13 03:48:09 +03:00
|
|
|
std::optional<Hash> blobSha1;
|
|
|
|
if (blobSha1Future.isReady()) {
|
|
|
|
blobSha1 = blobSha1Future.value();
|
2018-03-23 22:34:59 +03:00
|
|
|
}
|
2018-12-13 03:48:09 +03:00
|
|
|
|
|
|
|
getOverlayFileAccess(state)->createFile(getNodeId(), *blob, blobSha1);
|
|
|
|
|
|
|
|
state.setMaterialized();
|
2017-11-11 00:23:26 +03:00
|
|
|
}
|
|
|
|
|
2018-03-27 21:13:03 +03:00
|
|
|
void FileInode::materializeAndTruncate(LockedState& state) {
|
2018-03-23 22:34:59 +03:00
|
|
|
CHECK_NE(state->tag, State::MATERIALIZED_IN_OVERLAY);
|
2018-12-13 03:48:09 +03:00
|
|
|
getOverlayFileAccess(state)->createEmptyFile(getNodeId());
|
|
|
|
state.setMaterialized();
|
2018-03-23 22:34:59 +03:00
|
|
|
}
|
|
|
|
|
2018-03-27 21:13:03 +03:00
|
|
|
void FileInode::truncateInOverlay(LockedState& state) {
|
2018-03-23 22:34:59 +03:00
|
|
|
CHECK_EQ(state->tag, State::MATERIALIZED_IN_OVERLAY);
|
|
|
|
CHECK(!state->hash);
|
|
|
|
|
2019-11-07 05:21:39 +03:00
|
|
|
getOverlayFileAccess(state)->truncate(*this);
|
2017-07-27 09:39:02 +03:00
|
|
|
}
|
|
|
|
|
2018-12-13 03:48:09 +03:00
|
|
|
OverlayFileAccess* FileInode::getOverlayFileAccess(LockedState&) const {
|
|
|
|
return getMount()->getOverlayFileAccess();
|
2016-05-12 23:43:17 +03:00
|
|
|
}
|
2020-04-02 00:51:06 +03:00
|
|
|
#endif // !_WIN32
|
|
|
|
|
|
|
|
ObjectStore* FileInode::getObjectStore() const {
|
|
|
|
return getMount()->getObjectStore();
|
|
|
|
}
|
2017-07-28 04:12:48 +03:00
|
|
|
|
2017-11-04 01:58:04 +03:00
|
|
|
} // namespace eden
|
|
|
|
} // namespace facebook
|