2016-05-20 20:33:42 +03:00
|
|
|
/*
|
|
|
|
* 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 "EdenMount.h"
|
|
|
|
|
|
|
|
#include <glog/logging.h>
|
|
|
|
|
2016-09-13 04:27:54 +03:00
|
|
|
#include "eden/fs/config/ClientConfig.h"
|
2016-11-26 23:00:16 +03:00
|
|
|
#include "eden/fs/inodes/Dirstate.h"
|
2016-12-02 04:49:42 +03:00
|
|
|
#include "eden/fs/inodes/EdenDispatcher.h"
|
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
2016-11-26 23:00:15 +03:00
|
|
|
#include "eden/fs/inodes/EdenMounts.h"
|
2016-12-02 04:49:34 +03:00
|
|
|
#include "eden/fs/inodes/FileInode.h"
|
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
2016-11-26 23:00:15 +03:00
|
|
|
#include "eden/fs/inodes/Overlay.h"
|
2016-12-02 04:49:34 +03:00
|
|
|
#include "eden/fs/inodes/TreeInode.h"
|
2016-11-26 23:00:16 +03:00
|
|
|
#include "eden/fs/model/Hash.h"
|
2016-10-21 23:30:20 +03:00
|
|
|
#include "eden/fs/model/Tree.h"
|
2016-06-09 04:59:51 +03:00
|
|
|
#include "eden/fs/store/ObjectStore.h"
|
2016-12-02 04:49:42 +03:00
|
|
|
#include "eden/fuse/InodeNameManager.h"
|
2016-05-20 20:33:42 +03:00
|
|
|
#include "eden/fuse/MountPoint.h"
|
|
|
|
|
2016-06-09 04:59:51 +03:00
|
|
|
using std::unique_ptr;
|
2016-09-13 04:27:54 +03:00
|
|
|
using std::vector;
|
2016-05-20 20:33:42 +03:00
|
|
|
|
|
|
|
namespace facebook {
|
|
|
|
namespace eden {
|
|
|
|
|
2016-09-19 22:48:14 +03:00
|
|
|
// We compute this when the process is initialized, but stash a copy
|
|
|
|
// in each EdenMount. We may in the future manage to propagate enough
|
|
|
|
// state across upgrades or restarts that we can preserve this, but
|
|
|
|
// as implemented today, a process restart will invalidate any cached
|
|
|
|
// mountGeneration that a client may be holding on to.
|
|
|
|
// We take the bottom 16-bits of the pid and 32-bits of the current
|
|
|
|
// time and shift them up, leaving 16 bits for a mount point generation
|
|
|
|
// number.
|
|
|
|
static const uint64_t globalProcessGeneration =
|
|
|
|
(uint64_t(getpid()) << 48) | (uint64_t(time(nullptr)) << 16);
|
|
|
|
|
|
|
|
// Each time we create an EdenMount we bump this up and OR it together
|
|
|
|
// with the globalProcessGeneration to come up with a generation number
|
|
|
|
// for a given mount instance.
|
|
|
|
static std::atomic<uint16_t> mountGeneration{0};
|
|
|
|
|
2016-05-20 20:33:42 +03:00
|
|
|
EdenMount::EdenMount(
|
2016-12-02 04:49:30 +03:00
|
|
|
std::unique_ptr<ClientConfig> config,
|
|
|
|
std::unique_ptr<ObjectStore> objectStore)
|
|
|
|
: config_(std::move(config)),
|
2016-12-02 04:49:42 +03:00
|
|
|
dispatcher_{new EdenDispatcher(this)},
|
|
|
|
nameManager_{new fusell::InodeNameManager()},
|
2016-12-02 04:49:30 +03:00
|
|
|
mountPoint_(
|
2016-12-02 04:49:42 +03:00
|
|
|
new fusell::MountPoint(config_->getMountPath(), dispatcher_.get())),
|
2016-06-09 04:59:51 +03:00
|
|
|
objectStore_(std::move(objectStore)),
|
2016-12-02 04:49:30 +03:00
|
|
|
overlay_(std::make_shared<Overlay>(config_->getOverlayPath())),
|
|
|
|
dirstate_(std::make_unique<Dirstate>(this)),
|
|
|
|
bindMounts_(config_->getBindMounts()),
|
2016-09-19 22:48:14 +03:00
|
|
|
mountGeneration_(globalProcessGeneration | ++mountGeneration) {
|
2016-12-02 04:49:30 +03:00
|
|
|
// Load the overlay, if present.
|
|
|
|
auto rootOverlayDir = overlay_->loadOverlayDir(RelativePathPiece());
|
|
|
|
|
|
|
|
// Create the inode for the root of the tree using the hash contained
|
|
|
|
// within the snapshotPath file
|
|
|
|
auto snapshotID = config_->getSnapshotID();
|
2016-12-13 04:48:45 +03:00
|
|
|
TreeInodePtr rootInode;
|
2016-12-02 04:49:30 +03:00
|
|
|
if (rootOverlayDir) {
|
2016-12-13 04:48:43 +03:00
|
|
|
rootInode =
|
|
|
|
std::make_shared<TreeInode>(this, std::move(rootOverlayDir.value()));
|
2016-12-02 04:49:30 +03:00
|
|
|
} else {
|
|
|
|
auto rootTree = objectStore_->getTreeForCommit(snapshotID);
|
2016-12-13 04:48:43 +03:00
|
|
|
rootInode = std::make_shared<TreeInode>(this, std::move(rootTree));
|
2016-12-02 04:49:30 +03:00
|
|
|
}
|
2016-12-02 04:49:42 +03:00
|
|
|
dispatcher_->setRootInode(rootInode);
|
2016-12-02 04:49:30 +03:00
|
|
|
|
|
|
|
// Record the transition from no snapshot to the current snapshot in
|
|
|
|
// the journal. This also sets things up so that we can carry the
|
|
|
|
// snapshot id forward through subsequent journal entries.
|
|
|
|
auto delta = std::make_unique<JournalDelta>();
|
|
|
|
delta->toHash = snapshotID;
|
|
|
|
journal_.wlock()->addDelta(std::move(delta));
|
2016-05-20 20:33:42 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
EdenMount::~EdenMount() {}
|
|
|
|
|
|
|
|
const AbsolutePath& EdenMount::getPath() const {
|
|
|
|
return mountPoint_->getPath();
|
|
|
|
}
|
2016-09-13 04:27:54 +03:00
|
|
|
|
|
|
|
const vector<BindMount>& EdenMount::getBindMounts() const {
|
2016-10-18 22:18:00 +03:00
|
|
|
return bindMounts_;
|
2016-09-13 04:27:54 +03:00
|
|
|
}
|
2016-10-21 23:30:20 +03:00
|
|
|
|
2016-12-13 04:48:45 +03:00
|
|
|
TreeInodePtr EdenMount::getRootInode() const {
|
2016-12-02 04:49:43 +03:00
|
|
|
return dispatcher_->getRootInode();
|
2016-12-02 04:49:34 +03:00
|
|
|
}
|
|
|
|
|
2016-10-21 23:30:20 +03:00
|
|
|
std::unique_ptr<Tree> EdenMount::getRootTree() const {
|
2016-12-02 04:49:37 +03:00
|
|
|
auto rootInode = getRootInode();
|
|
|
|
{
|
|
|
|
auto dir = rootInode->getContents().rlock();
|
|
|
|
auto& rootTreeHash = dir->treeHash.value();
|
|
|
|
auto tree = objectStore_->getTree(rootTreeHash);
|
|
|
|
return tree;
|
|
|
|
}
|
2016-10-21 23:30:20 +03:00
|
|
|
}
|
2016-12-02 04:49:34 +03:00
|
|
|
|
2016-12-13 04:48:45 +03:00
|
|
|
InodePtr EdenMount::getInodeBase(RelativePathPiece path) const {
|
2016-12-02 04:49:42 +03:00
|
|
|
auto inodeBase = dispatcher_->getInode(FUSE_ROOT_ID);
|
2016-12-02 04:49:38 +03:00
|
|
|
auto relativePath = RelativePathPiece{path};
|
|
|
|
|
|
|
|
// Walk down to the path of interest.
|
|
|
|
auto it = relativePath.paths().begin();
|
|
|
|
while (it != relativePath.paths().end()) {
|
|
|
|
// This will throw if there is no such entry.
|
|
|
|
inodeBase =
|
2016-12-02 04:49:42 +03:00
|
|
|
dispatcher_
|
2016-12-02 04:49:38 +03:00
|
|
|
->lookupInodeBase(inodeBase->getNodeId(), it.piece().basename())
|
|
|
|
.get();
|
|
|
|
++it;
|
|
|
|
}
|
|
|
|
|
|
|
|
return inodeBase;
|
2016-12-02 04:49:34 +03:00
|
|
|
}
|
|
|
|
|
2016-12-13 04:48:45 +03:00
|
|
|
TreeInodePtr EdenMount::getTreeInode(RelativePathPiece path) const {
|
2016-12-02 04:49:38 +03:00
|
|
|
auto inodeBase = getInodeBase(path);
|
|
|
|
auto treeInode = std::dynamic_pointer_cast<TreeInode>(inodeBase);
|
|
|
|
if (treeInode) {
|
|
|
|
return treeInode;
|
|
|
|
} else {
|
|
|
|
folly::throwSystemErrorExplicit(
|
|
|
|
ENOTDIR, "not a directory: ", path.stringPiece());
|
|
|
|
}
|
2016-12-02 04:49:34 +03:00
|
|
|
}
|
|
|
|
|
2016-12-13 04:48:45 +03:00
|
|
|
FileInodePtr EdenMount::getFileInode(RelativePathPiece path) const {
|
2016-12-02 04:49:38 +03:00
|
|
|
auto inodeBase = getInodeBase(path);
|
|
|
|
auto fileInode = std::dynamic_pointer_cast<FileInode>(inodeBase);
|
|
|
|
if (fileInode) {
|
|
|
|
return fileInode;
|
|
|
|
} else {
|
|
|
|
folly::throwSystemErrorExplicit(
|
|
|
|
EISDIR, "is a directory: ", path.stringPiece());
|
|
|
|
}
|
2016-12-02 04:49:34 +03:00
|
|
|
}
|
2016-05-20 20:33:42 +03:00
|
|
|
}
|
|
|
|
} // facebook::eden
|