Flip Dirstate -> EdenMount dependency.

Summary:
Previously, `Dirstate` took a `std::shared_ptr<EdenMount>`, but now it takes
pointers to a `MountPoint` and an `ObjectStore` because it does not need the
entire `EdenMount`. Ultimately, this will enable us to have `EdenMount` create
the `Dirstate` itself, but that will be done in a follow-up commit.

Fortunately, it was pretty easy to remove the references to `edenMount_` in
`Dirstate.cpp` and rewrite them in terms of `mountPoint_` or `objectStore_`.
The one thing that I also decided to move was `getModifiedDirectoriesForMount()`
because I already needed to create an `EdenMounts` file (admittedly not a
great name) to collect some utility functions that use members of an `EdenMount`
while not having access to the `EdenMount` itself.

As part of this change, all of the code in `eden/fs/model/hg` has been moved to
`eden/fs/inodes` so that it is alongside `EdenMount`. We are going to change
the `Dirstate` from an Hg-specific concept to a more general concept.

`LocalDirstatePersistence` is no longer one of two implementations of
`DirstatePersistence`. (The other was `FakeDirstatePersistence`.) Now there is
just one concrete implementation called `DirstatePersistence` that takes its
implementation from `LocalDirstatePersistence`. Because there is no longer a
`FakeDirstatePersistence`, `TestMount` must create a `DirstatePersistence` that
uses a `TemporaryFile`.

Because `TestMount` now takes responsibility for creating the `Dirstate`, it
must also give callers the ability to specify the user directives. To that end,
`TestMountBuilder` got an `addUserDirectives()` method while `TestMount` got a
`getDirstate()` method. Surprisingly, `TestMountTest` did not need to be updated
as part of this revision, but `DirstateTest` needed quite a few updates
(which were generally mechanical).

Reviewed By: simpkins

Differential Revision: D4230154

fbshipit-source-id: 9b8cb52b45ef5d75bc8f5e62a58fcd1cddc32bfa
This commit is contained in:
Michael Bolin 2016-11-26 12:00:15 -08:00 committed by Facebook Github Bot
parent 0a174e7128
commit 0f834ea809
22 changed files with 389 additions and 371 deletions

View File

@ -8,12 +8,16 @@
*
*/
#include "Dirstate.h"
#include <folly/Format.h>
#include <folly/io/Cursor.h>
#include <folly/io/IOBuf.h>
#include "eden/fs/inodes/DirstatePersistence.h"
#include "eden/fs/inodes/EdenMounts.h"
#include "eden/fs/inodes/TreeEntryFileInode.h"
#include "eden/fs/inodes/TreeInode.h"
#include "eden/fs/model/Blob.h"
#include "eden/fs/service/EdenMountHandler.h"
#include "eden/fs/model/Tree.h"
#include "eden/fs/store/ObjectStore.h"
#include "eden/fs/store/ObjectStores.h"
#include "eden/fuse/MountPoint.h"
@ -50,19 +54,19 @@ std::string HgStatus::toString() const {
namespace {
template <typename RelativePathType>
void updateManifestWithDirectives(
const std::unordered_map<RelativePathType, HgUserStatusDirective>*
const std::unordered_map<RelativePathType, overlay::UserStatusDirective>*
unaccountedUserDirectives,
std::unordered_map<RelativePath, HgStatusCode>* manifest) {
// We should make sure that every entry in userDirectives_ is accounted for in
// the HgStatus that we return.
for (auto& pair : *unaccountedUserDirectives) {
switch (pair.second) {
case HgUserStatusDirective::ADD:
case overlay::UserStatusDirective::Add:
// The file was marked for addition, but no longer exists in the working
// copy. The user should either restore the file or run `hg forget`.
manifest->emplace(RelativePath(pair.first), HgStatusCode::MISSING);
break;
case HgUserStatusDirective::REMOVE:
case overlay::UserStatusDirective::Remove:
// The file was marked for removal, but it still exists in the working
// copy without any modifications. Although it may seem strange, it
// should still show up as REMOVED in `hg status` even though it is
@ -74,11 +78,10 @@ void updateManifestWithDirectives(
}
}
std::unique_ptr<HgStatus> Dirstate::getStatus() {
std::unique_ptr<HgStatus> Dirstate::getStatus() const {
// Find the modified directories in the overlay and compare them with what is
// in the root tree.
auto mountPoint = edenMount_->getMountPoint();
auto modifiedDirectories = getModifiedDirectoriesForMount(edenMount_.get());
auto modifiedDirectories = getModifiedDirectoriesForMount(mountPoint_);
std::unordered_map<RelativePath, HgStatusCode> manifest;
if (modifiedDirectories->empty()) {
auto userDirectives = userDirectives_.rlock();
@ -87,19 +90,18 @@ std::unique_ptr<HgStatus> Dirstate::getStatus() {
}
auto userDirectives = userDirectives_.rlock();
std::unordered_map<RelativePathPiece, HgUserStatusDirective>
std::unordered_map<RelativePathPiece, overlay::UserStatusDirective>
copyOfUserDirectives(userDirectives->begin(), userDirectives->end());
auto rootTree = edenMount_->getRootTree();
auto objectStore = edenMount_->getObjectStore();
auto rootTree = getRootTree();
for (auto& directory : *modifiedDirectories) {
// Get the directory as a TreeInode.
auto dirInode = mountPoint->getDirInodeForPath(directory);
auto dirInode = mountPoint_->getDirInodeForPath(directory);
auto treeInode = std::dynamic_pointer_cast<TreeInode>(dirInode);
DCHECK_NOTNULL(treeInode.get());
// Get the directory as a Tree.
auto tree = getTreeForDirectory(directory, rootTree.get(), objectStore);
auto tree = getTreeForDirectory(directory, rootTree.get(), objectStore_);
DCHECK_NOTNULL(tree.get());
DirectoryDelta delta;
@ -115,10 +117,10 @@ std::unique_ptr<HgStatus> Dirstate::getStatus() {
if (result != userDirectives->end()) {
auto statusCode = result->second;
switch (statusCode) {
case HgUserStatusDirective::ADD:
case overlay::UserStatusDirective::Add:
manifest.emplace(pathToEntry, HgStatusCode::ADDED);
break;
case HgUserStatusDirective::REMOVE:
case overlay::UserStatusDirective::Remove:
// TODO(mbolin): Is there any weird sequence of modifications with
// adding/removed files matched by .hgignore that could lead to this
// state?
@ -143,7 +145,7 @@ std::unique_ptr<HgStatus> Dirstate::getStatus() {
if (result != userDirectives->end()) {
auto statusCode = result->second;
switch (statusCode) {
case HgUserStatusDirective::ADD:
case overlay::UserStatusDirective::Add:
// TODO(mbolin): Is there any weird sequence of modifications with
// adding/removed files matched by .hgignore that could lead to this
// state?
@ -151,7 +153,7 @@ std::unique_ptr<HgStatus> Dirstate::getStatus() {
"Invariant violation: The user has marked {} for addition, "
"but it already exists in the manifest.",
pathToEntry.stringPiece()));
case HgUserStatusDirective::REMOVE:
case overlay::UserStatusDirective::Remove:
manifest.emplace(pathToEntry, HgStatusCode::REMOVED);
break;
}
@ -171,7 +173,7 @@ std::unique_ptr<HgStatus> Dirstate::getStatus() {
if (result != userDirectives->end()) {
auto statusCode = result->second;
switch (statusCode) {
case HgUserStatusDirective::ADD:
case overlay::UserStatusDirective::Add:
// TODO(mbolin): Is there any weird sequence of modifications with
// adding/removed files matched by .hgignore that could lead to this
// state?
@ -180,7 +182,7 @@ std::unique_ptr<HgStatus> Dirstate::getStatus() {
"but it already exists in the manifest "
"(and is currently removed from disk).",
pathToEntry.stringPiece()));
case HgUserStatusDirective::REMOVE:
case overlay::UserStatusDirective::Remove:
manifest.emplace(pathToEntry, HgStatusCode::REMOVED);
break;
}
@ -200,7 +202,7 @@ std::unique_ptr<HgStatus> Dirstate::getStatus() {
bool hasMatchingAttributes(
const TreeEntry* treeEntry,
const TreeInode::Entry* treeInode,
ObjectStore& objectStore,
ObjectStore* objectStore,
TreeInode& parent, // Has rlock
const TreeInode::Dir& dir) {
if (treeEntry->getMode() != treeInode->mode) {
@ -218,7 +220,7 @@ bool hasMatchingAttributes(
auto fileInode =
std::dynamic_pointer_cast<TreeEntryFileInode>(overlayInode);
auto overlaySHA1 = fileInode->getSHA1().get();
auto blobSHA1 = objectStore.getSha1ForBlob(treeEntry->getHash());
auto blobSHA1 = objectStore->getSha1ForBlob(treeEntry->getHash());
return overlaySHA1 == *blobSHA1;
} else {
auto optionalHash = treeInode->hash;
@ -270,7 +272,7 @@ void Dirstate::computeDelta(
if (!hasMatchingAttributes(
&base,
overlayIterator->second.get(),
*edenMount_->getObjectStore(),
objectStore_,
current,
*dir)) {
delta.modified.push_back(base.getName());
@ -312,16 +314,16 @@ void Dirstate::add(RelativePathPiece path) {
auto result = userDirectives->find(path.copy());
if (result != userDirectives->end()) {
switch (result->second) {
case HgUserStatusDirective::ADD:
case overlay::UserStatusDirective::Add:
// No-op: already added.
break;
case HgUserStatusDirective::REMOVE:
case overlay::UserStatusDirective::Remove:
userDirectives->erase(path.copy());
persistence_->save(*userDirectives);
break;
}
} else {
(*userDirectives)[path.copy()] = HgUserStatusDirective::ADD;
(*userDirectives)[path.copy()] = overlay::UserStatusDirective::Add;
persistence_->save(*userDirectives);
}
}
@ -337,7 +339,7 @@ bool shouldFileBeDeletedByHgRemove(
RelativePathPiece file,
std::shared_ptr<fusell::DirInode> parent,
const TreeEntry* treeEntry,
ObjectStore& objectStore) {
ObjectStore* objectStore) {
auto treeInode = std::dynamic_pointer_cast<TreeInode>(parent);
if (treeInode == nullptr) {
// The parent directory for the file is not in the overlay, so the file must
@ -415,7 +417,7 @@ void Dirstate::remove(RelativePathPiece path, bool force) {
// prefer not to do them while holding the lock.
std::shared_ptr<fusell::DirInode> parent;
try {
parent = edenMount_->getMountPoint()->getDirInodeForPath(path.dirname());
parent = mountPoint_->getDirInodeForPath(path.dirname());
} catch (const std::system_error& e) {
auto value = e.code().value();
if (value == ENOENT || value == ENOTDIR) {
@ -434,8 +436,7 @@ void Dirstate::remove(RelativePathPiece path, bool force) {
}
}
auto entry = getEntryForFile(
path, edenMount_->getRootTree().get(), edenMount_->getObjectStore());
auto entry = getEntryForFile(path, getRootTree().get(), objectStore_);
auto shouldDelete = false;
{
@ -458,17 +459,17 @@ void Dirstate::remove(RelativePathPiece path, bool force) {
// the file has been modified, so we must perform this check before
// updating userDirectives.
shouldDelete = shouldFileBeDeletedByHgRemove(
path, parent, entry.get(), *edenMount_->getObjectStore());
path, parent, entry.get(), objectStore_);
}
}
(*userDirectives)[path.copy()] = HgUserStatusDirective::REMOVE;
(*userDirectives)[path.copy()] = overlay::UserStatusDirective::Remove;
persistence_->save(*userDirectives);
} else {
switch (result->second) {
case HgUserStatusDirective::REMOVE:
case overlay::UserStatusDirective::Remove:
// No-op: already removed.
break;
case HgUserStatusDirective::ADD:
case overlay::UserStatusDirective::Add:
if (inode != nullptr) {
throw std::runtime_error(folly::sformat(
"not removing {}: file has been marked for add "
@ -484,7 +485,7 @@ void Dirstate::remove(RelativePathPiece path, bool force) {
}
if (shouldDelete) {
auto dispatcher = edenMount_->getMountPoint()->getDispatcher();
auto dispatcher = mountPoint_->getDispatcher();
try {
dispatcher->unlink(parent->getNodeId(), path.basename()).get();
} catch (const std::system_error& e) {
@ -496,6 +497,10 @@ void Dirstate::remove(RelativePathPiece path, bool force) {
}
}
std::unique_ptr<Tree> Dirstate::getRootTree() const {
return getRootTreeForMountPoint(mountPoint_, objectStore_);
}
const std::string kStatusCodeCharClean = "C";
const std::string kStatusCodeCharModified = "M";
const std::string kStatusCodeCharAdded = "A";

View File

@ -9,9 +9,8 @@
*/
#pragma once
#include <folly/Synchronized.h>
#include "eden/fs/inodes/EdenMount.h"
#include "eden/fs/model/Tree.h"
#include "eden/fs/store/ObjectStore.h"
#include "eden/fs/inodes/DirstatePersistence.h"
#include "eden/fs/inodes/gen-cpp2/overlay_types.h"
#include "eden/utils/PathFuncs.h"
namespace {
@ -21,16 +20,13 @@ class DirectoryDelta;
namespace facebook {
namespace eden {
class ObjectStore;
class Tree;
class TreeInode;
/**
* Type of change to the manifest that the user has specified for a particular
* file that will apply on the next commit.
*/
enum class HgUserStatusDirective {
ADD,
REMOVE,
};
namespace fusell {
class MountPoint;
}
/**
*
@ -95,14 +91,6 @@ class HgStatus {
std::ostream& operator<<(std::ostream& os, const HgStatus& status);
class DirstatePersistence {
public:
virtual ~DirstatePersistence() {}
virtual void save(
const std::unordered_map<RelativePath, HgUserStatusDirective>&
userDirectives) = 0;
};
/**
* This is designed to be a simple implemenation of an Hg dirstate. It's
* "simple" in that every call to `getStatus()` walks the entire overlay to
@ -126,22 +114,26 @@ class DirstatePersistence {
class Dirstate {
public:
Dirstate(
std::shared_ptr<EdenMount> edenMount,
fusell::MountPoint* mountPoint,
ObjectStore* objectStore,
std::unique_ptr<DirstatePersistence> persistence,
const std::unordered_map<RelativePath, HgUserStatusDirective>*
const std::unordered_map<RelativePath, overlay::UserStatusDirective>*
userDirectives)
: userDirectives_(*userDirectives),
edenMount_(std::move(edenMount)),
persistence_(std::move(persistence)) {}
: mountPoint_(mountPoint),
objectStore_(objectStore),
persistence_(std::move(persistence)),
userDirectives_(*userDirectives) {}
Dirstate(
std::shared_ptr<EdenMount> edenMount,
fusell::MountPoint* mountPoint,
ObjectStore* objectStore,
std::unique_ptr<DirstatePersistence> persistence)
: edenMount_(std::move(edenMount)),
: mountPoint_(mountPoint),
objectStore_(objectStore),
persistence_(std::move(persistence)) {}
/** Analogous to calling `hg status`. */
std::unique_ptr<HgStatus> getStatus();
std::unique_ptr<HgStatus> getStatus() const;
/**
* Analogous to `hg add <path>` where `<path>` is an ordinary file or symlink.
@ -159,15 +151,19 @@ class Dirstate {
TreeInode& current,
DirectoryDelta& delta) const;
std::unique_ptr<Tree> getRootTree() const;
fusell::MountPoint* mountPoint_;
ObjectStore* objectStore_;
std::unique_ptr<DirstatePersistence> persistence_;
/**
* Manifest of files in the working copy whose status is not CLEAN. These are
* also referred to as "nonnormal" files.
* TODO(mbolin): Consider StringKeyedMap instead of unordered_map.
*/
folly::Synchronized<std::unordered_map<RelativePath, HgUserStatusDirective>>
folly::Synchronized<
std::unordered_map<RelativePath, overlay::UserStatusDirective>>
userDirectives_;
std::shared_ptr<EdenMount> edenMount_;
std::unique_ptr<DirstatePersistence> persistence_;
};
}
}

View File

@ -0,0 +1,58 @@
/*
* Copyright (c) 2016, 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 "DirstatePersistence.h"
#include <folly/FileUtil.h>
#include <thrift/lib/cpp2/protocol/Serializer.h>
namespace facebook {
namespace eden {
using apache::thrift::CompactSerializer;
void DirstatePersistence::save(
const std::unordered_map<RelativePath, overlay::UserStatusDirective>&
userDirectives) {
overlay::DirstateData dirstateData;
std::map<std::string, overlay::UserStatusDirective> directives;
for (auto& pair : userDirectives) {
directives[pair.first.stringPiece().str()] = pair.second;
}
dirstateData.directives = directives;
auto serializedData = CompactSerializer::serialize<std::string>(dirstateData);
auto wrote = folly::writeFile(serializedData, storageFile_.c_str());
if (!wrote) {
throw std::runtime_error(folly::to<std::string>(
"Failed to persist Dirstate to file ", storageFile_));
}
}
std::unordered_map<RelativePath, overlay::UserStatusDirective>
DirstatePersistence::load() {
std::string serializedData;
std::unordered_map<RelativePath, overlay::UserStatusDirective> entries;
if (!folly::readFile(storageFile_.c_str(), serializedData)) {
int err = errno;
if (err == ENOENT) {
return entries;
}
folly::throwSystemErrorExplicit(err, "failed to read ", storageFile_);
}
auto dirstateData =
CompactSerializer::deserialize<overlay::DirstateData>(serializedData);
for (auto& pair : dirstateData.directives) {
entries[RelativePath(pair.first)] = pair.second;
}
return entries;
}
}
}

View File

@ -9,30 +9,30 @@
*/
#pragma once
#include "eden/fs/model/hg/Dirstate.h"
#include <unordered_map>
#include "eden/fs/inodes/gen-cpp2/overlay_types.h"
#include "eden/utils/PathFuncs.h"
namespace facebook {
namespace eden {
/**
* Implementation of DirstatePersistence that persists data to a local file.
* Persists dirstate data to a local file.
*/
class LocalDirstatePersistence : public DirstatePersistence {
class DirstatePersistence {
public:
explicit LocalDirstatePersistence(AbsolutePathPiece storageFile)
explicit DirstatePersistence(AbsolutePathPiece storageFile)
: storageFile_(storageFile) {}
virtual ~LocalDirstatePersistence() {}
void save(const std::unordered_map<RelativePath, HgUserStatusDirective>&
userDirectives) override;
void save(
const std::unordered_map<RelativePath, overlay::UserStatusDirective>&
userDirectives);
/**
* If the underlying storage file does not exist, then this returns an empty
* map.
*/
std::unordered_map<RelativePath, HgUserStatusDirective> load();
std::unordered_map<RelativePath, overlay::UserStatusDirective> load();
private:
AbsolutePath storageFile_;

View File

@ -11,8 +11,9 @@
#include <glog/logging.h>
#include "Overlay.h"
#include "eden/fs/config/ClientConfig.h"
#include "eden/fs/inodes/EdenMounts.h"
#include "eden/fs/inodes/Overlay.h"
#include "eden/fs/model/Tree.h"
#include "eden/fs/store/ObjectStore.h"
#include "eden/fuse/MountPoint.h"
@ -77,14 +78,7 @@ const vector<BindMount>& EdenMount::getBindMounts() const {
}
std::unique_ptr<Tree> EdenMount::getRootTree() const {
auto rootAsDirInode = mountPoint_->getRootInode();
auto rootAsTreeInode = std::dynamic_pointer_cast<TreeInode>(rootAsDirInode);
{
auto dir = rootAsTreeInode->getContents().rlock();
auto& rootTreeHash = dir->treeHash.value();
auto tree = getObjectStore()->getTree(rootTreeHash);
return tree;
}
return getRootTreeForMountPoint(mountPoint_.get(), getObjectStore());
}
}
} // facebook::eden

View File

@ -0,0 +1,81 @@
/*
* Copyright (c) 2016, 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 "EdenMounts.h"
#include "eden/fs/inodes/TreeInode.h"
#include "eden/fs/model/Tree.h"
#include "eden/fs/store/ObjectStore.h"
#include "eden/fuse/MountPoint.h"
#include "eden/utils/PathFuncs.h"
namespace facebook {
namespace eden {
std::unique_ptr<Tree> getRootTreeForMountPoint(
fusell::MountPoint* mountPoint,
ObjectStore* objectStore) {
auto rootAsDirInode = mountPoint->getRootInode();
auto rootAsTreeInode = std::dynamic_pointer_cast<TreeInode>(rootAsDirInode);
{
auto dir = rootAsTreeInode->getContents().rlock();
auto& rootTreeHash = dir->treeHash.value();
auto tree = objectStore->getTree(rootTreeHash);
return tree;
}
}
void getModifiedDirectoriesRecursive(
RelativePathPiece dirPath,
TreeInode* dir,
std::vector<RelativePath>* modifiedDirectories) {
dir->getContents().withRLock([&](const auto& contents) mutable {
if (!contents.materialized) {
return;
}
modifiedDirectories->push_back(dirPath.copy());
for (auto& entIter : contents.entries) {
const auto& ent = entIter.second;
if (S_ISDIR(ent->mode) && ent->materialized) {
const auto& name = entIter.first;
auto childInode = dir->lookupChildByNameLocked(&contents, name);
auto childPath = dirPath + name;
auto childDir = std::dynamic_pointer_cast<TreeInode>(childInode);
DCHECK(childDir->getContents().rlock()->materialized)
<< (dirPath + name) << " entry " << ent.get()
<< " materialized is true, but the contained dir is !materialized";
getModifiedDirectoriesRecursive(
childPath, childDir.get(), modifiedDirectories);
}
}
});
}
// This function is not a method of MountPoint because it has a dependency on
// TreeInode. If MountPoint depended on TreeInode, it would create a circular
// dependency, which is why this function lives here.
std::unique_ptr<std::vector<RelativePath>> getModifiedDirectoriesForMount(
fusell::MountPoint* mountPoint) {
auto inodeDispatcher = mountPoint->getDispatcher();
auto rootInode = inodeDispatcher->getDirInode(FUSE_ROOT_ID);
auto treeInode = std::dynamic_pointer_cast<TreeInode>(rootInode);
if (treeInode) {
auto modifiedDirectories = std::make_unique<std::vector<RelativePath>>();
getModifiedDirectoriesRecursive(
RelativePathPiece(), treeInode.get(), modifiedDirectories.get());
return modifiedDirectories;
} else {
throw std::runtime_error(folly::to<std::string>(
"Could not find root TreeInode for ", mountPoint->getPath()));
}
}
}
}

View File

@ -0,0 +1,41 @@
/*
* Copyright (c) 2016, 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.
*
*/
#pragma once
#include <memory>
#include <vector>
#include "eden/utils/PathFuncs.h"
/**
* Utility functions for use with various members of EdenMount.
*/
namespace facebook {
namespace eden {
class ObjectStore;
class Tree;
namespace fusell {
class MountPoint;
}
std::unique_ptr<Tree> getRootTreeForMountPoint(
fusell::MountPoint* mountPoint,
ObjectStore* objectStore);
/**
* @return vector with the RelativePath of every directory that is modified
* according to the overlay in the mount. The vector will be ordered as a
* depth-first traversal of the overlay.
*/
std::unique_ptr<std::vector<RelativePath>> getModifiedDirectoriesForMount(
fusell::MountPoint* mountPoint);
}
}

View File

@ -18,6 +18,7 @@ cpp_library(
'@/eden/fs/journal:journal',
'@/eden/fs/store:store',
'@/eden/fuse:fusell',
'@/eden/utils:utils',
':serialization-cpp2',
],
external_deps = [

View File

@ -32,3 +32,12 @@ struct OverlayData {
// fit in memory and thus that this won't be too big to work with.
1: map<RelativePath, OverlayDir> localDirs
}
enum UserStatusDirective {
Add = 0x0,
Remove = 0x1,
}
struct DirstateData {
1: map<RelativePath, UserStatusDirective> directives
}

View File

@ -11,56 +11,58 @@
#include <folly/experimental/TestUtil.h>
#include <gtest/gtest.h>
#include <thrift/lib/cpp2/protocol/Serializer.h>
#include "eden/fs/model/hg/LocalDirstatePersistence.h"
#include "eden/fs/model/hg/gen-cpp2/dirstate_types.h"
#include "eden/fs/inodes/DirstatePersistence.h"
#include "eden/fs/inodes/gen-cpp2/overlay_types.h"
using namespace facebook::eden;
using apache::thrift::CompactSerializer;
using folly::test::TemporaryFile;
TEST(LocalDirstatePersistence, saveAndReadDirectivesBackOut) {
TEST(DirstatePersistence, saveAndReadDirectivesBackOut) {
TemporaryFile storageFile;
AbsolutePath storageFilePath(storageFile.path().c_str());
LocalDirstatePersistence persistence(storageFilePath);
std::unordered_map<RelativePath, HgUserStatusDirective> userDirectives = {
{RelativePath("add.txt"), HgUserStatusDirective::ADD},
{RelativePath("remove.txt"), HgUserStatusDirective::REMOVE},
};
DirstatePersistence persistence(storageFilePath);
std::unordered_map<RelativePath, overlay::UserStatusDirective>
userDirectives = {
{RelativePath("add.txt"), overlay::UserStatusDirective::Add},
{RelativePath("remove.txt"), overlay::UserStatusDirective::Remove},
};
persistence.save(userDirectives);
auto rehydratedDirectives = persistence.load();
EXPECT_EQ(userDirectives, rehydratedDirectives);
}
TEST(LocalDirstatePersistence, loadFromFileWithWellFormattedData) {
TEST(DirstatePersistence, loadFromFileWithWellFormattedData) {
TemporaryFile storageFile;
dirstate::DirstateData dirstateData;
overlay::DirstateData dirstateData;
dirstateData.directives = {
{"add.txt", dirstate::HgUserStatusDirectiveValue::Add},
{"remove.txt", dirstate::HgUserStatusDirectiveValue::Remove}};
{"add.txt", overlay::UserStatusDirective::Add},
{"remove.txt", overlay::UserStatusDirective::Remove}};
auto serializedData = CompactSerializer::serialize<std::string>(dirstateData);
folly::writeFull(
storageFile.fd(), serializedData.data(), serializedData.size());
AbsolutePath storageFilePath(storageFile.path().c_str());
LocalDirstatePersistence persistence(storageFilePath);
DirstatePersistence persistence(storageFilePath);
auto directives = persistence.load();
std::unordered_map<RelativePath, HgUserStatusDirective> expectedDirectives = {
{RelativePath("add.txt"), HgUserStatusDirective::ADD},
{RelativePath("remove.txt"), HgUserStatusDirective::REMOVE},
};
std::unordered_map<RelativePath, overlay::UserStatusDirective>
expectedDirectives = {
{RelativePath("add.txt"), overlay::UserStatusDirective::Add},
{RelativePath("remove.txt"), overlay::UserStatusDirective::Remove},
};
EXPECT_EQ(expectedDirectives, directives);
}
TEST(LocalDirstatePersistence, attemptLoadFromNonExistentFile) {
TEST(DirstatePersistence, attemptLoadFromNonExistentFile) {
AbsolutePath storageFilePath;
{
TemporaryFile storageFile;
storageFilePath = AbsolutePath(storageFile.path().c_str());
}
LocalDirstatePersistence persistence(storageFilePath);
DirstatePersistence persistence(storageFilePath);
auto directives = persistence.load();
EXPECT_EQ(0, directives.size());
}

View File

@ -9,7 +9,7 @@
*/
#include <gtest/gtest.h>
#include "eden/fs/model/hg/Dirstate.h"
#include "eden/fs/inodes/Dirstate.h"
#include "eden/fs/testharness/TestMount.h"
using namespace facebook::eden;
@ -36,27 +36,19 @@ TEST(HgStatus, toString) {
hgStatus.toString());
}
class FakeDirstatePeristence : public DirstatePersistence {
public:
virtual ~FakeDirstatePeristence() {}
void save(
const std::unordered_map<RelativePath, HgUserStatusDirective>&) override {
}
};
void verifyExpectedDirstate(
Dirstate& dirstate,
const Dirstate* dirstate,
std::unordered_map<std::string, HgStatusCode>&& statuses) {
std::unordered_map<RelativePath, HgStatusCode> expected;
for (auto& pair : statuses) {
expected.emplace(RelativePath(pair.first), pair.second);
}
auto expectedStatus = HgStatus(std::move(expected));
EXPECT_EQ(expectedStatus, *dirstate.getStatus());
EXPECT_EQ(expectedStatus, *dirstate->getStatus().get());
}
void verifyEmptyDirstate(Dirstate& dirstate) {
auto status = dirstate.getStatus();
void verifyEmptyDirstate(const Dirstate* dirstate) {
auto status = dirstate->getStatus();
EXPECT_EQ(0, status->size()) << "Expected dirstate to be empty.";
}
@ -64,26 +56,23 @@ TEST(Dirstate, createDirstate) {
TestMountBuilder builder;
auto testMount = builder.build();
auto persistence = std::make_unique<FakeDirstatePeristence>();
Dirstate dirstate(testMount->getEdenMount(), std::move(persistence));
auto dirstate = testMount->getDirstate();
verifyEmptyDirstate(dirstate);
}
TEST(Dirstate, createDirstateWithInitialState) {
TestMountBuilder builder;
builder.addFile({"removed.txt", "nada"});
builder.addUserDirectives({
{RelativePath("deleted.txt"), overlay::UserStatusDirective::Remove},
{RelativePath("missing.txt"), overlay::UserStatusDirective::Add},
{RelativePath("newfile.txt"), overlay::UserStatusDirective::Add},
{RelativePath("removed.txt"), overlay::UserStatusDirective::Remove},
});
auto testMount = builder.build();
testMount->addFile("newfile.txt", "legitimate add");
auto persistence = std::make_unique<FakeDirstatePeristence>();
std::unordered_map<RelativePath, HgUserStatusDirective> userDirectives{
{RelativePath("deleted.txt"), HgUserStatusDirective::REMOVE},
{RelativePath("missing.txt"), HgUserStatusDirective::ADD},
{RelativePath("newfile.txt"), HgUserStatusDirective::ADD},
{RelativePath("removed.txt"), HgUserStatusDirective::REMOVE},
};
Dirstate dirstate(
testMount->getEdenMount(), std::move(persistence), &userDirectives);
auto dirstate = testMount->getDirstate();
verifyExpectedDirstate(
dirstate,
{
@ -97,36 +86,30 @@ TEST(Dirstate, createDirstateWithInitialState) {
TEST(Dirstate, createDirstateWithUntrackedFile) {
TestMountBuilder builder;
auto testMount = builder.build();
auto dirstate = testMount->getDirstate();
testMount->addFile("hello.txt", "some contents");
auto persistence = std::make_unique<FakeDirstatePeristence>();
Dirstate dirstate(testMount->getEdenMount(), std::move(persistence));
verifyExpectedDirstate(dirstate, {{"hello.txt", HgStatusCode::NOT_TRACKED}});
}
TEST(Dirstate, createDirstateWithAddedFile) {
TestMountBuilder builder;
auto testMount = builder.build();
auto dirstate = testMount->getDirstate();
testMount->addFile("hello.txt", "some contents");
auto persistence = std::make_unique<FakeDirstatePeristence>();
Dirstate dirstate(testMount->getEdenMount(), std::move(persistence));
dirstate.add(RelativePathPiece("hello.txt"));
dirstate->add(RelativePathPiece("hello.txt"));
verifyExpectedDirstate(dirstate, {{"hello.txt", HgStatusCode::ADDED}});
}
TEST(Dirstate, createDirstateWithMissingFile) {
TestMountBuilder builder;
auto testMount = builder.build();
auto dirstate = testMount->getDirstate();
testMount->addFile("hello.txt", "some contents");
auto persistence = std::make_unique<FakeDirstatePeristence>();
Dirstate dirstate(testMount->getEdenMount(), std::move(persistence));
dirstate.add(RelativePathPiece("hello.txt"));
dirstate->add(RelativePathPiece("hello.txt"));
testMount->deleteFile("hello.txt");
verifyExpectedDirstate(dirstate, {{"hello.txt", HgStatusCode::MISSING}});
}
@ -134,11 +117,9 @@ TEST(Dirstate, createDirstateWithModifiedFileContents) {
TestMountBuilder builder;
builder.addFile({"hello.txt", "some contents"});
auto testMount = builder.build();
auto dirstate = testMount->getDirstate();
auto persistence = std::make_unique<FakeDirstatePeristence>();
Dirstate dirstate(testMount->getEdenMount(), std::move(persistence));
testMount->overwriteFile("hello.txt", "other contents");
verifyExpectedDirstate(dirstate, {{"hello.txt", HgStatusCode::MODIFIED}});
}
@ -146,11 +127,9 @@ TEST(Dirstate, createDirstateWithTouchedFile) {
TestMountBuilder builder;
builder.addFile({"hello.txt", "some contents"});
auto testMount = builder.build();
auto dirstate = testMount->getDirstate();
auto persistence = std::make_unique<FakeDirstatePeristence>();
Dirstate dirstate(testMount->getEdenMount(), std::move(persistence));
testMount->overwriteFile("hello.txt", "some contents");
// Although the file has been written, it has not changed in any significant
// way.
verifyEmptyDirstate(dirstate);
@ -160,10 +139,9 @@ TEST(Dirstate, createDirstateWithFileAndThenHgRemoveIt) {
TestMountBuilder builder;
builder.addFile({"hello.txt", "some contents"});
auto testMount = builder.build();
auto dirstate = testMount->getDirstate();
auto persistence = std::make_unique<FakeDirstatePeristence>();
Dirstate dirstate(testMount->getEdenMount(), std::move(persistence));
dirstate.remove(RelativePathPiece("hello.txt"), /* force */ false);
dirstate->remove(RelativePathPiece("hello.txt"), /* force */ false);
EXPECT_FALSE(testMount->hasFileAt("hello.txt"));
verifyExpectedDirstate(dirstate, {{"hello.txt", HgStatusCode::REMOVED}});
@ -173,11 +151,10 @@ TEST(Dirstate, createDirstateWithFileRemoveItAndThenHgRemoveIt) {
TestMountBuilder builder;
builder.addFile({"hello.txt", "some contents"});
auto testMount = builder.build();
auto dirstate = testMount->getDirstate();
auto persistence = std::make_unique<FakeDirstatePeristence>();
Dirstate dirstate(testMount->getEdenMount(), std::move(persistence));
testMount->deleteFile("hello.txt");
dirstate.remove(RelativePathPiece("hello.txt"), /* force */ false);
dirstate->remove(RelativePathPiece("hello.txt"), /* force */ false);
verifyExpectedDirstate(dirstate, {{"hello.txt", HgStatusCode::REMOVED}});
}
@ -186,13 +163,12 @@ TEST(Dirstate, createDirstateWithFileTouchItAndThenHgRemoveIt) {
TestMountBuilder builder;
builder.addFile({"hello.txt", "original contents"});
auto testMount = builder.build();
auto dirstate = testMount->getDirstate();
auto persistence = std::make_unique<FakeDirstatePeristence>();
Dirstate dirstate(testMount->getEdenMount(), std::move(persistence));
testMount->overwriteFile("hello.txt", "some other contents");
try {
dirstate.remove(RelativePathPiece("hello.txt"), /* force */ false);
dirstate->remove(RelativePathPiece("hello.txt"), /* force */ false);
FAIL() << "Should error when trying to remove a modified file.";
} catch (const std::runtime_error& e) {
EXPECT_STREQ(
@ -201,7 +177,7 @@ TEST(Dirstate, createDirstateWithFileTouchItAndThenHgRemoveIt) {
}
testMount->overwriteFile("hello.txt", "original contents");
dirstate.remove(RelativePathPiece("hello.txt"), /* force */ false);
dirstate->remove(RelativePathPiece("hello.txt"), /* force */ false);
EXPECT_FALSE(testMount->hasFileAt("hello.txt"));
verifyExpectedDirstate(dirstate, {{"hello.txt", HgStatusCode::REMOVED}});
@ -211,14 +187,11 @@ TEST(Dirstate, createDirstateWithFileModifyItAndThenHgForceRemoveIt) {
TestMountBuilder builder;
builder.addFile({"hello.txt", "original contents"});
auto testMount = builder.build();
auto dirstate = testMount->getDirstate();
auto persistence = std::make_unique<FakeDirstatePeristence>();
Dirstate dirstate(testMount->getEdenMount(), std::move(persistence));
testMount->overwriteFile("hello.txt", "some other contents");
dirstate.remove(RelativePathPiece("hello.txt"), /* force */ true);
dirstate->remove(RelativePathPiece("hello.txt"), /* force */ true);
EXPECT_FALSE(testMount->hasFileAt("hello.txt"));
verifyExpectedDirstate(dirstate, {{"hello.txt", HgStatusCode::REMOVED}});
}
@ -226,16 +199,14 @@ TEST(Dirstate, ensureSubsequentCallsToHgRemoveHaveNoEffect) {
TestMountBuilder builder;
builder.addFile({"hello.txt", "original contents"});
auto testMount = builder.build();
auto dirstate = testMount->getDirstate();
auto persistence = std::make_unique<FakeDirstatePeristence>();
Dirstate dirstate(testMount->getEdenMount(), std::move(persistence));
dirstate.remove(RelativePathPiece("hello.txt"), /* force */ false);
dirstate->remove(RelativePathPiece("hello.txt"), /* force */ false);
EXPECT_FALSE(testMount->hasFileAt("hello.txt"));
verifyExpectedDirstate(dirstate, {{"hello.txt", HgStatusCode::REMOVED}});
// Calling `hg remove` again should have no effect and not throw any errors.
dirstate.remove(RelativePathPiece("hello.txt"), /* force */ false);
dirstate->remove(RelativePathPiece("hello.txt"), /* force */ false);
EXPECT_FALSE(testMount->hasFileAt("hello.txt"));
verifyExpectedDirstate(dirstate, {{"hello.txt", HgStatusCode::REMOVED}});
@ -246,7 +217,7 @@ TEST(Dirstate, ensureSubsequentCallsToHgRemoveHaveNoEffect) {
verifyExpectedDirstate(dirstate, {{"hello.txt", HgStatusCode::REMOVED}});
// Calling `hg remove` again should have no effect and not throw any errors.
dirstate.remove(RelativePathPiece("hello.txt"), /* force */ false);
dirstate->remove(RelativePathPiece("hello.txt"), /* force */ false);
EXPECT_TRUE(testMount->hasFileAt("hello.txt"));
verifyExpectedDirstate(dirstate, {{"hello.txt", HgStatusCode::REMOVED}});
}
@ -254,34 +225,30 @@ TEST(Dirstate, ensureSubsequentCallsToHgRemoveHaveNoEffect) {
TEST(Dirstate, createDirstateHgAddFileRemoveItThenHgRemoveIt) {
TestMountBuilder builder;
auto testMount = builder.build();
auto persistence = std::make_unique<FakeDirstatePeristence>();
Dirstate dirstate(testMount->getEdenMount(), std::move(persistence));
auto dirstate = testMount->getDirstate();
testMount->addFile("hello.txt", "I will be added.");
dirstate.add(RelativePathPiece("hello.txt"));
dirstate->add(RelativePathPiece("hello.txt"));
verifyExpectedDirstate(dirstate, {{"hello.txt", HgStatusCode::ADDED}});
testMount->deleteFile("hello.txt");
verifyExpectedDirstate(dirstate, {{"hello.txt", HgStatusCode::MISSING}});
dirstate.remove(RelativePathPiece("hello.txt"), /* force */ false);
dirstate->remove(RelativePathPiece("hello.txt"), /* force */ false);
verifyEmptyDirstate(dirstate);
}
TEST(Dirstate, createDirstateHgAddFileThenHgRemoveIt) {
TestMountBuilder builder;
auto testMount = builder.build();
auto persistence = std::make_unique<FakeDirstatePeristence>();
Dirstate dirstate(testMount->getEdenMount(), std::move(persistence));
auto dirstate = testMount->getDirstate();
testMount->addFile("hello.txt", "I will be added.");
dirstate.add(RelativePathPiece("hello.txt"));
dirstate->add(RelativePathPiece("hello.txt"));
verifyExpectedDirstate(dirstate, {{"hello.txt", HgStatusCode::ADDED}});
try {
dirstate.remove(RelativePathPiece("hello.txt"), /* force */ false);
dirstate->remove(RelativePathPiece("hello.txt"), /* force */ false);
FAIL() << "Should error when trying to remove a file scheduled for add.";
} catch (const std::runtime_error& e) {
EXPECT_STREQ(
@ -289,7 +256,6 @@ TEST(Dirstate, createDirstateHgAddFileThenHgRemoveIt) {
"(use 'hg forget' to undo add)",
e.what());
}
verifyExpectedDirstate(dirstate, {{"hello.txt", HgStatusCode::ADDED}});
}
@ -297,10 +263,8 @@ TEST(Dirstate, createDirstateWithFileAndThenDeleteItWithoutCallingHgRemove) {
TestMountBuilder builder;
builder.addFile({"hello.txt", "some contents"});
auto testMount = builder.build();
auto dirstate = testMount->getDirstate();
auto persistence = std::make_unique<FakeDirstatePeristence>();
Dirstate dirstate(testMount->getEdenMount(), std::move(persistence));
testMount->deleteFile("hello.txt");
verifyExpectedDirstate(dirstate, {{"hello.txt", HgStatusCode::MISSING}});
}

View File

@ -7,7 +7,7 @@
* of patent rights can be found in the PATENTS file in the same directory.
*
*/
#include "eden/fs/service/EdenMountHandler.h"
#include "eden/fs/inodes/EdenMounts.h"
#include "eden/fs/testharness/TestMount.h"
#include "eden/utils/PathFuncs.h"
@ -19,7 +19,8 @@ TEST(EdenMountHandler, getModifiedDirectoriesForMountWithNoModifications) {
TestMountBuilder builder;
auto testMount = builder.build();
auto edenMount = testMount->getEdenMount();
auto modifiedDirectories = getModifiedDirectoriesForMount(edenMount.get());
auto modifiedDirectories =
getModifiedDirectoriesForMount(edenMount->getMountPoint());
std::vector<RelativePath> expected = {};
EXPECT_EQ(expected, *modifiedDirectories.get());
}
@ -48,7 +49,8 @@ TEST(EdenMountHandler, getModifiedDirectoriesForMount) {
testMount->addFile("a/b/c/file.txt", "");
auto edenMount = testMount->getEdenMount();
auto modifiedDirectories = getModifiedDirectoriesForMount(edenMount.get());
auto modifiedDirectories =
getModifiedDirectoriesForMount(edenMount->getMountPoint());
std::vector<RelativePath> expected = {
RelativePath(),

View File

@ -3,7 +3,7 @@ cpp_unittest(
headers = AutoHeaders.RECURSIVE_GLOB, # https://fburl.com/424819295
srcs = glob(['*Test.cpp']),
deps = [
'//eden/fs/service:EdenMountHandler',
'//eden/fs/inodes:inodes',
'//eden/fs/testharness:testharness',
'//eden/utils:utils',
],

View File

@ -1,80 +0,0 @@
/*
* Copyright (c) 2016, 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/model/hg/LocalDirstatePersistence.h"
#include <folly/FileUtil.h>
#include <thrift/lib/cpp2/protocol/Serializer.h>
#include "eden/fs/model/hg/gen-cpp2/dirstate_types.h"
namespace facebook {
namespace eden {
using apache::thrift::CompactSerializer;
void LocalDirstatePersistence::save(
const std::unordered_map<RelativePath, HgUserStatusDirective>&
userDirectives) {
dirstate::DirstateData dirstateData;
std::map<std::string, dirstate::HgUserStatusDirectiveValue> directives;
for (auto& pair : userDirectives) {
dirstate::HgUserStatusDirectiveValue value;
switch (pair.second) {
case HgUserStatusDirective::ADD:
value = dirstate::HgUserStatusDirectiveValue::Add;
break;
case HgUserStatusDirective::REMOVE:
value = dirstate::HgUserStatusDirectiveValue::Remove;
break;
}
directives[pair.first.stringPiece().str()] = value;
}
dirstateData.directives = directives;
auto serializedData = CompactSerializer::serialize<std::string>(dirstateData);
auto wrote = folly::writeFile(serializedData, storageFile_.c_str());
if (!wrote) {
throw std::runtime_error(folly::to<std::string>(
"Failed to persist Dirstate to file ", storageFile_));
}
}
std::unordered_map<RelativePath, HgUserStatusDirective>
LocalDirstatePersistence::load() {
std::string serializedData;
std::unordered_map<RelativePath, HgUserStatusDirective> entries;
if (!folly::readFile(storageFile_.c_str(), serializedData)) {
int err = errno;
if (err == ENOENT) {
return entries;
}
folly::throwSystemErrorExplicit(err, "failed to read ", storageFile_);
}
auto dirstateData =
CompactSerializer::deserialize<dirstate::DirstateData>(serializedData);
for (auto& pair : dirstateData.directives) {
HgUserStatusDirective directive;
switch (pair.second) {
case dirstate::HgUserStatusDirectiveValue::Add:
directive = HgUserStatusDirective::ADD;
break;
case dirstate::HgUserStatusDirectiveValue::Remove:
directive = HgUserStatusDirective::REMOVE;
break;
default:
throw std::runtime_error(
"Unexpected value loaded for HgUserStatusDirectiveValue");
}
entries[RelativePath(pair.first)] = directive;
}
return entries;
}
}
}

View File

@ -1,23 +0,0 @@
thrift_library(
name = 'serialization',
thrift_args = ['--strict'],
thrift_srcs = {
'dirstate.thrift': [],
},
languages = ['cpp2'],
)
cpp_library(
name = 'hg',
srcs = glob(['*.cpp']),
headers = glob(['*.h']),
deps = [
':serialization-cpp2',
'//eden/fs/inodes:inodes',
'//eden/fs/model:model',
'//eden/fs/service:EdenMountHandler',
'//eden/fs/store:store',
'//eden/utils:utils',
'//folly:folly',
],
)

View File

@ -1,12 +0,0 @@
namespace cpp2 facebook.eden.dirstate
typedef string RelativePath
enum HgUserStatusDirectiveValue {
Add = 0x0,
Remove = 0x1,
}
struct DirstateData {
1: map<RelativePath, HgUserStatusDirectiveValue> directives
}

View File

@ -1,14 +0,0 @@
cpp_unittest(
name = 'test',
headers = AutoHeaders.RECURSIVE_GLOB, # https://fburl.com/424819295
srcs = glob(['*Test.cpp']),
deps = [
'//eden/fs/model/hg:hg',
'//eden/fs/store/testutil:testutil',
'//eden/fs/testharness:testharness',
'//eden/utils:utils',
],
external_deps = [
'gtest',
],
)

View File

@ -13,6 +13,7 @@
#include "eden/fs/inodes/EdenMount.h"
#include "eden/fs/inodes/TreeEntryFileInode.h"
#include "eden/fs/inodes/TreeInode.h"
#include "eden/fs/service/gen-cpp2/eden_types.h"
#include "eden/fuse/MountPoint.h"
#include "eden/fuse/fuse_headers.h"
#include "eden/utils/PathFuncs.h"
@ -103,49 +104,5 @@ void getMaterializedEntriesRecursive(
}
});
}
void getModifiedDirectoriesRecursive(
RelativePathPiece dirPath,
TreeInode* dir,
std::vector<RelativePath>* modifiedDirectories) {
dir->getContents().withRLock([&](const auto& contents) mutable {
if (!contents.materialized) {
return;
}
modifiedDirectories->push_back(dirPath.copy());
for (auto& entIter : contents.entries) {
const auto& ent = entIter.second;
if (S_ISDIR(ent->mode) && ent->materialized) {
const auto& name = entIter.first;
auto childInode = dir->lookupChildByNameLocked(&contents, name);
auto childPath = dirPath + name;
auto childDir = std::dynamic_pointer_cast<TreeInode>(childInode);
DCHECK(childDir->getContents().rlock()->materialized)
<< (dirPath + name) << " entry " << ent.get()
<< " materialized is true, but the contained dir is !materialized";
getModifiedDirectoriesRecursive(
childPath, childDir.get(), modifiedDirectories);
}
}
});
}
unique_ptr<std::vector<RelativePath>> getModifiedDirectoriesForMount(
EdenMount* edenMount) {
auto inodeDispatcher = edenMount->getMountPoint()->getDispatcher();
auto rootInode = inodeDispatcher->getDirInode(FUSE_ROOT_ID);
auto treeInode = std::dynamic_pointer_cast<TreeInode>(rootInode);
if (treeInode) {
auto modifiedDirectories = std::make_unique<std::vector<RelativePath>>();
getModifiedDirectoriesRecursive(
RelativePathPiece(), treeInode.get(), modifiedDirectories.get());
return modifiedDirectories;
} else {
throw std::runtime_error(folly::to<std::string>(
"Could not find root TreeInode for ", edenMount->getPath()));
}
}
}
}

View File

@ -9,24 +9,14 @@
*/
#pragma once
#include "eden/fs/service/gen-cpp2/EdenService.h"
#include "eden/utils/PathFuncs.h"
namespace facebook {
namespace eden {
class EdenMount;
class MaterializedResult;
void getMaterializedEntriesForMount(
EdenMount* edenMount,
MaterializedResult& out);
/**
* @return vector with the RelativePath of every directory that is modified
* according to the overlay in the mount. The vector will be ordered as a
* depth-first traversal of the overlay.
*/
std::unique_ptr<std::vector<RelativePath>> getModifiedDirectoriesForMount(
EdenMount* edenMount);
}
}

View File

@ -12,6 +12,7 @@
#include <folly/experimental/TestUtil.h>
#include <folly/io/IOBuf.h>
#include "eden/fs/config/ClientConfig.h"
#include "eden/fs/inodes/Dirstate.h"
#include "eden/fs/inodes/FileData.h"
#include "eden/fs/inodes/Overlay.h"
#include "eden/fs/inodes/TreeEntryFileInode.h"
@ -30,6 +31,7 @@ using facebook::eden::fusell::MountPoint;
using folly::ByteRange;
using folly::StringPiece;
using folly::test::TemporaryDirectory;
using folly::test::TemporaryFile;
using std::make_shared;
using std::make_unique;
using std::shared_ptr;
@ -82,6 +84,16 @@ unique_ptr<TestMount> TestMountBuilder::build() {
shared_ptr<Overlay> overlay =
make_shared<Overlay>(AbsolutePathPiece{overlayDir->path().string()});
// TODO(mbolin): Change EdenMount so that it creates the Dirstate.
auto pathToDirstatePersistenceData = make_unique<TemporaryFile>();
auto dirstatePersistence = make_unique<DirstatePersistence>(
AbsolutePathPiece(pathToDirstatePersistenceData->path().string()));
auto dirstate = make_unique<Dirstate>(
mountPoint.get(),
objectStore.get(),
std::move(dirstatePersistence),
&userDirectives_);
std::vector<BindMount> bindMounts;
unique_ptr<EdenMount> edenMount = make_unique<EdenMount>(
mountPoint, std::move(objectStore), overlay, bindMounts);
@ -130,9 +142,19 @@ unique_ptr<TestMount> TestMountBuilder::build() {
return make_unique<TestMount>(
std::move(edenMount),
std::move(dirstate),
std::move(mountPointDir),
std::move(pathToRocksDb),
std::move(overlayDir));
std::move(overlayDir),
std::move(pathToDirstatePersistenceData));
}
void TestMountBuilder::addUserDirectives(
std::unordered_map<RelativePath, overlay::UserStatusDirective>&&
userDirectives) {
for (auto& pair : userDirectives) {
userDirectives_.emplace(pair.first, pair.second);
}
}
void TestMount::addFile(folly::StringPiece path, std::string contents) {

View File

@ -13,9 +13,11 @@
#include <folly/experimental/TestUtil.h>
#include <sys/stat.h>
#include <vector>
#include "eden/fs/inodes/Dirstate.h"
#include "eden/fs/inodes/DirstatePersistence.h"
#include "eden/fs/inodes/EdenMount.h"
#include "eden/fs/inodes/gen-cpp2/overlay_types.h"
#include "eden/fs/model/TreeEntry.h"
#include "eden/fs/store/ObjectStore.h"
#include "eden/utils/PathFuncs.h"
namespace facebook {
@ -44,13 +46,17 @@ class TestMount {
public:
TestMount(
std::shared_ptr<EdenMount> edenMount,
std::unique_ptr<Dirstate> dirstate,
std::unique_ptr<folly::test::TemporaryDirectory> mountPointDir,
std::unique_ptr<folly::test::TemporaryDirectory> pathToRocksDb,
std::unique_ptr<folly::test::TemporaryDirectory> overlayDir)
std::unique_ptr<folly::test::TemporaryDirectory> overlayDir,
std::unique_ptr<folly::test::TemporaryFile> persistenceDataFile)
: edenMount_(edenMount),
dirstate_(std::move(dirstate)),
mountPointDir_(std::move(mountPointDir)),
pathToRocksDb_(std::move(pathToRocksDb)),
overlayDir_(std::move(overlayDir)) {}
overlayDir_(std::move(overlayDir)),
persistenceDataFile_(std::move(persistenceDataFile)) {}
/**
* Add file to the mount; it will be available in the overlay.
@ -80,14 +86,20 @@ class TestMount {
return edenMount_;
}
Dirstate* getDirstate() {
return dirstate_.get();
}
private:
std::shared_ptr<EdenMount> edenMount_;
std::unique_ptr<Dirstate> dirstate_;
// The TestMount must hold onto these TemporaryDirectories because they need
// to live for the duration of the test.
std::unique_ptr<folly::test::TemporaryDirectory> mountPointDir_;
std::unique_ptr<folly::test::TemporaryDirectory> pathToRocksDb_;
std::unique_ptr<folly::test::TemporaryDirectory> overlayDir_;
std::unique_ptr<folly::test::TemporaryFile> persistenceDataFile_;
};
class TestMountBuilder {
@ -104,8 +116,14 @@ class TestMountBuilder {
}
}
void addUserDirectives(
std::unordered_map<RelativePath, overlay::UserStatusDirective>&&
userDirectives);
private:
std::vector<TestMountFile> files_;
std::unordered_map<RelativePath, overlay::UserStatusDirective>
userDirectives_;
};
}
}

View File

@ -124,6 +124,13 @@ class MountPoint {
*/
struct stat initStatData() const;
/**
* @return vector with the RelativePath of every directory that is modified
* according to the overlay in the mount. The vector will be ordered as a
* depth-first traversal of the overlay.
*/
std::unique_ptr<std::vector<RelativePath>> getModifiedDirectories();
private:
enum class Status { UNINIT, STARTING, RUNNING, ERROR };