sapling/eden/fs/inodes/TreeInode.cpp

604 lines
19 KiB
C++
Raw Normal View History

/*
* 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 "TreeInode.h"
#include "EdenMount.h"
#include "TreeEntryFileInode.h"
#include "TreeInodeDirHandle.h"
#include "eden/fs/inodes/EdenMount.h"
#include "eden/fs/inodes/FileData.h"
#include "eden/fs/model/Tree.h"
#include "eden/fs/model/TreeEntry.h"
#include "eden/fs/overlay/Overlay.h"
#include "eden/fs/store/ObjectStore.h"
#include "eden/fuse/MountPoint.h"
#include "eden/fuse/RequestData.h"
#include "eden/utils/PathFuncs.h"
namespace facebook {
namespace eden {
TreeInode::TreeInode(
EdenMount* mount,
std::unique_ptr<Tree>&& tree,
fuse_ino_t parent,
fuse_ino_t ino)
eden: merge overlay into the inode objects Summary: It was starting to get pretty complex to manage locking across the inodes, filedata, overlay and soon the journal, so as a simplifying step, this folds data that was tracked by the overlay into the TreeInode itself. This is the first diff in a short series for this. This one: 1. Breaks the persistent overlay information, so shutting down eden and bringing it back up will lose your changes (to be restored in the following diff) 2. Allows deferring materialization of file data in more cases 3. Allows renaming dirs. The approach here is now to keep just one source of information about the directory contents; when we construct a TreeInode we import this data from the Tree and then apply mutations to it locally. Each inode can be mutated indepdently from others; we only need to lock the 1, 2 or 3 participating inodes in the various mutation operations. I'll tackle persistence of the mutations in the following diff, but the high level plan for that (to help understand this diff) is to always keep the directory inodes for mutations alive as inode objects. We make use of the canForget functionality introduced by D3774269 to ensure that these don't get evicted early. On startup we'll load this information from the overlay area. This model simplifies some of the processing around reading dirs and looking up children. Since the overlay data now tracks the appropriate tree or content hash we can be much more lazy at materializing data, especially in the rename case. For example, renaming "fbcode" to "fbcod" doesn't require us to recursively materialize the "fbcode" tree. Depends on D3653706 Reviewed By: simpkins Differential Revision: D3657894 fbshipit-source-id: d4561639845ca93b93487dc84bf11ad795927b1f
2016-09-10 02:56:00 +03:00
: DirInode(ino),
mount_(mount),
contents_(buildDirFromTree(tree.get())),
parent_(parent) {}
TreeInode::TreeInode(EdenMount* mount, fuse_ino_t parent, fuse_ino_t ino)
eden: merge overlay into the inode objects Summary: It was starting to get pretty complex to manage locking across the inodes, filedata, overlay and soon the journal, so as a simplifying step, this folds data that was tracked by the overlay into the TreeInode itself. This is the first diff in a short series for this. This one: 1. Breaks the persistent overlay information, so shutting down eden and bringing it back up will lose your changes (to be restored in the following diff) 2. Allows deferring materialization of file data in more cases 3. Allows renaming dirs. The approach here is now to keep just one source of information about the directory contents; when we construct a TreeInode we import this data from the Tree and then apply mutations to it locally. Each inode can be mutated indepdently from others; we only need to lock the 1, 2 or 3 participating inodes in the various mutation operations. I'll tackle persistence of the mutations in the following diff, but the high level plan for that (to help understand this diff) is to always keep the directory inodes for mutations alive as inode objects. We make use of the canForget functionality introduced by D3774269 to ensure that these don't get evicted early. On startup we'll load this information from the overlay area. This model simplifies some of the processing around reading dirs and looking up children. Since the overlay data now tracks the appropriate tree or content hash we can be much more lazy at materializing data, especially in the rename case. For example, renaming "fbcode" to "fbcod" doesn't require us to recursively materialize the "fbcode" tree. Depends on D3653706 Reviewed By: simpkins Differential Revision: D3657894 fbshipit-source-id: d4561639845ca93b93487dc84bf11ad795927b1f
2016-09-10 02:56:00 +03:00
: DirInode(ino),
mount_(mount),
contents_(buildDirFromTree(nullptr)),
parent_(parent) {}
TreeInode::~TreeInode() {}
folly::Future<fusell::Dispatcher::Attr> TreeInode::getattr() {
fusell::Dispatcher::Attr attr;
attr.st.st_mode = S_IFDIR | 0755;
attr.st.st_ino = getNodeId();
// TODO: set nlink. It should be 2 plus the number of subdirectories
// TODO: set atime, mtime, and ctime
return attr;
}
eden: merge overlay into the inode objects Summary: It was starting to get pretty complex to manage locking across the inodes, filedata, overlay and soon the journal, so as a simplifying step, this folds data that was tracked by the overlay into the TreeInode itself. This is the first diff in a short series for this. This one: 1. Breaks the persistent overlay information, so shutting down eden and bringing it back up will lose your changes (to be restored in the following diff) 2. Allows deferring materialization of file data in more cases 3. Allows renaming dirs. The approach here is now to keep just one source of information about the directory contents; when we construct a TreeInode we import this data from the Tree and then apply mutations to it locally. Each inode can be mutated indepdently from others; we only need to lock the 1, 2 or 3 participating inodes in the various mutation operations. I'll tackle persistence of the mutations in the following diff, but the high level plan for that (to help understand this diff) is to always keep the directory inodes for mutations alive as inode objects. We make use of the canForget functionality introduced by D3774269 to ensure that these don't get evicted early. On startup we'll load this information from the overlay area. This model simplifies some of the processing around reading dirs and looking up children. Since the overlay data now tracks the appropriate tree or content hash we can be much more lazy at materializing data, especially in the rename case. For example, renaming "fbcode" to "fbcod" doesn't require us to recursively materialize the "fbcode" tree. Depends on D3653706 Reviewed By: simpkins Differential Revision: D3657894 fbshipit-source-id: d4561639845ca93b93487dc84bf11ad795927b1f
2016-09-10 02:56:00 +03:00
std::shared_ptr<fusell::InodeBase> TreeInode::getChildByNameLocked(
const Dir* contents,
PathComponentPiece name) {
auto iter = contents->entries.find(name);
if (iter == contents->entries.end()) {
folly::throwSystemErrorExplicit(ENOENT);
}
eden: merge overlay into the inode objects Summary: It was starting to get pretty complex to manage locking across the inodes, filedata, overlay and soon the journal, so as a simplifying step, this folds data that was tracked by the overlay into the TreeInode itself. This is the first diff in a short series for this. This one: 1. Breaks the persistent overlay information, so shutting down eden and bringing it back up will lose your changes (to be restored in the following diff) 2. Allows deferring materialization of file data in more cases 3. Allows renaming dirs. The approach here is now to keep just one source of information about the directory contents; when we construct a TreeInode we import this data from the Tree and then apply mutations to it locally. Each inode can be mutated indepdently from others; we only need to lock the 1, 2 or 3 participating inodes in the various mutation operations. I'll tackle persistence of the mutations in the following diff, but the high level plan for that (to help understand this diff) is to always keep the directory inodes for mutations alive as inode objects. We make use of the canForget functionality introduced by D3774269 to ensure that these don't get evicted early. On startup we'll load this information from the overlay area. This model simplifies some of the processing around reading dirs and looking up children. Since the overlay data now tracks the appropriate tree or content hash we can be much more lazy at materializing data, especially in the rename case. For example, renaming "fbcode" to "fbcod" doesn't require us to recursively materialize the "fbcode" tree. Depends on D3653706 Reviewed By: simpkins Differential Revision: D3657894 fbshipit-source-id: d4561639845ca93b93487dc84bf11ad795927b1f
2016-09-10 02:56:00 +03:00
// Only allocate an inode number once we know that the entry exists!
auto node = getNameMgr()->getNodeByName(getNodeId(), name);
auto entry = iter->second.get();
eden: merge overlay into the inode objects Summary: It was starting to get pretty complex to manage locking across the inodes, filedata, overlay and soon the journal, so as a simplifying step, this folds data that was tracked by the overlay into the TreeInode itself. This is the first diff in a short series for this. This one: 1. Breaks the persistent overlay information, so shutting down eden and bringing it back up will lose your changes (to be restored in the following diff) 2. Allows deferring materialization of file data in more cases 3. Allows renaming dirs. The approach here is now to keep just one source of information about the directory contents; when we construct a TreeInode we import this data from the Tree and then apply mutations to it locally. Each inode can be mutated indepdently from others; we only need to lock the 1, 2 or 3 participating inodes in the various mutation operations. I'll tackle persistence of the mutations in the following diff, but the high level plan for that (to help understand this diff) is to always keep the directory inodes for mutations alive as inode objects. We make use of the canForget functionality introduced by D3774269 to ensure that these don't get evicted early. On startup we'll load this information from the overlay area. This model simplifies some of the processing around reading dirs and looking up children. Since the overlay data now tracks the appropriate tree or content hash we can be much more lazy at materializing data, especially in the rename case. For example, renaming "fbcode" to "fbcod" doesn't require us to recursively materialize the "fbcode" tree. Depends on D3653706 Reviewed By: simpkins Differential Revision: D3657894 fbshipit-source-id: d4561639845ca93b93487dc84bf11ad795927b1f
2016-09-10 02:56:00 +03:00
if (S_ISDIR(entry->mode)) {
if (entry->hash) {
auto tree = getStore()->getTree(entry->hash.value());
return std::make_shared<TreeInode>(
mount_, std::move(tree), getNodeId(), node->getNodeId());
}
eden: merge overlay into the inode objects Summary: It was starting to get pretty complex to manage locking across the inodes, filedata, overlay and soon the journal, so as a simplifying step, this folds data that was tracked by the overlay into the TreeInode itself. This is the first diff in a short series for this. This one: 1. Breaks the persistent overlay information, so shutting down eden and bringing it back up will lose your changes (to be restored in the following diff) 2. Allows deferring materialization of file data in more cases 3. Allows renaming dirs. The approach here is now to keep just one source of information about the directory contents; when we construct a TreeInode we import this data from the Tree and then apply mutations to it locally. Each inode can be mutated indepdently from others; we only need to lock the 1, 2 or 3 participating inodes in the various mutation operations. I'll tackle persistence of the mutations in the following diff, but the high level plan for that (to help understand this diff) is to always keep the directory inodes for mutations alive as inode objects. We make use of the canForget functionality introduced by D3774269 to ensure that these don't get evicted early. On startup we'll load this information from the overlay area. This model simplifies some of the processing around reading dirs and looking up children. Since the overlay data now tracks the appropriate tree or content hash we can be much more lazy at materializing data, especially in the rename case. For example, renaming "fbcode" to "fbcod" doesn't require us to recursively materialize the "fbcode" tree. Depends on D3653706 Reviewed By: simpkins Differential Revision: D3657894 fbshipit-source-id: d4561639845ca93b93487dc84bf11ad795927b1f
2016-09-10 02:56:00 +03:00
// No corresponding TreeEntry, this exists only in the overlay.
return std::make_shared<TreeInode>(mount_, getNodeId(), node->getNodeId());
}
eden: merge overlay into the inode objects Summary: It was starting to get pretty complex to manage locking across the inodes, filedata, overlay and soon the journal, so as a simplifying step, this folds data that was tracked by the overlay into the TreeInode itself. This is the first diff in a short series for this. This one: 1. Breaks the persistent overlay information, so shutting down eden and bringing it back up will lose your changes (to be restored in the following diff) 2. Allows deferring materialization of file data in more cases 3. Allows renaming dirs. The approach here is now to keep just one source of information about the directory contents; when we construct a TreeInode we import this data from the Tree and then apply mutations to it locally. Each inode can be mutated indepdently from others; we only need to lock the 1, 2 or 3 participating inodes in the various mutation operations. I'll tackle persistence of the mutations in the following diff, but the high level plan for that (to help understand this diff) is to always keep the directory inodes for mutations alive as inode objects. We make use of the canForget functionality introduced by D3774269 to ensure that these don't get evicted early. On startup we'll load this information from the overlay area. This model simplifies some of the processing around reading dirs and looking up children. Since the overlay data now tracks the appropriate tree or content hash we can be much more lazy at materializing data, especially in the rename case. For example, renaming "fbcode" to "fbcod" doesn't require us to recursively materialize the "fbcode" tree. Depends on D3653706 Reviewed By: simpkins Differential Revision: D3657894 fbshipit-source-id: d4561639845ca93b93487dc84bf11ad795927b1f
2016-09-10 02:56:00 +03:00
return std::make_shared<TreeEntryFileInode>(
node->getNodeId(),
std::static_pointer_cast<TreeInode>(shared_from_this()),
entry);
}
eden: merge overlay into the inode objects Summary: It was starting to get pretty complex to manage locking across the inodes, filedata, overlay and soon the journal, so as a simplifying step, this folds data that was tracked by the overlay into the TreeInode itself. This is the first diff in a short series for this. This one: 1. Breaks the persistent overlay information, so shutting down eden and bringing it back up will lose your changes (to be restored in the following diff) 2. Allows deferring materialization of file data in more cases 3. Allows renaming dirs. The approach here is now to keep just one source of information about the directory contents; when we construct a TreeInode we import this data from the Tree and then apply mutations to it locally. Each inode can be mutated indepdently from others; we only need to lock the 1, 2 or 3 participating inodes in the various mutation operations. I'll tackle persistence of the mutations in the following diff, but the high level plan for that (to help understand this diff) is to always keep the directory inodes for mutations alive as inode objects. We make use of the canForget functionality introduced by D3774269 to ensure that these don't get evicted early. On startup we'll load this information from the overlay area. This model simplifies some of the processing around reading dirs and looking up children. Since the overlay data now tracks the appropriate tree or content hash we can be much more lazy at materializing data, especially in the rename case. For example, renaming "fbcode" to "fbcod" doesn't require us to recursively materialize the "fbcode" tree. Depends on D3653706 Reviewed By: simpkins Differential Revision: D3657894 fbshipit-source-id: d4561639845ca93b93487dc84bf11ad795927b1f
2016-09-10 02:56:00 +03:00
folly::Future<std::shared_ptr<fusell::InodeBase>> TreeInode::getChildByName(
PathComponentPiece namepiece) {
auto contents = contents_.rlock();
return getChildByNameLocked(&*contents, namepiece);
}
fuse_ino_t TreeInode::getParent() const {
return parent_;
}
fuse_ino_t TreeInode::getInode() const {
return getNodeId();
}
folly::Future<std::shared_ptr<fusell::DirHandle>> TreeInode::opendir(
const struct fuse_file_info&) {
return std::make_shared<TreeInodeDirHandle>(
std::static_pointer_cast<TreeInode>(shared_from_this()));
}
eden: merge overlay into the inode objects Summary: It was starting to get pretty complex to manage locking across the inodes, filedata, overlay and soon the journal, so as a simplifying step, this folds data that was tracked by the overlay into the TreeInode itself. This is the first diff in a short series for this. This one: 1. Breaks the persistent overlay information, so shutting down eden and bringing it back up will lose your changes (to be restored in the following diff) 2. Allows deferring materialization of file data in more cases 3. Allows renaming dirs. The approach here is now to keep just one source of information about the directory contents; when we construct a TreeInode we import this data from the Tree and then apply mutations to it locally. Each inode can be mutated indepdently from others; we only need to lock the 1, 2 or 3 participating inodes in the various mutation operations. I'll tackle persistence of the mutations in the following diff, but the high level plan for that (to help understand this diff) is to always keep the directory inodes for mutations alive as inode objects. We make use of the canForget functionality introduced by D3774269 to ensure that these don't get evicted early. On startup we'll load this information from the overlay area. This model simplifies some of the processing around reading dirs and looking up children. Since the overlay data now tracks the appropriate tree or content hash we can be much more lazy at materializing data, especially in the rename case. For example, renaming "fbcode" to "fbcod" doesn't require us to recursively materialize the "fbcode" tree. Depends on D3653706 Reviewed By: simpkins Differential Revision: D3657894 fbshipit-source-id: d4561639845ca93b93487dc84bf11ad795927b1f
2016-09-10 02:56:00 +03:00
/* If we don't yet have an overlay entry for this portion of the tree,
* populate it from the Tree. In order to materialize a dir we have
* to also materialize its parents. */
void TreeInode::materializeDirAndParents() {
if (contents_.rlock()->materialized) {
// Already materialized, all done!
return;
}
// Ensure that our parent(s) are materialized. We can't go higher
// than the root inode though.
if (getNodeId() != FUSE_ROOT_ID) {
auto parentInode = std::dynamic_pointer_cast<TreeInode>(
getMount()->getMountPoint()->getDispatcher()->getDirInode(parent_));
DCHECK(parentInode) << "must always have a TreeInode parent";
// and get it to materialize
parentInode->materializeDirAndParents();
}
contents_.withULockPtr([&](auto ulock) {
if (ulock->materialized) {
// Someone else materialized it in the meantime
return;
}
auto wlock = ulock.moveFromUpgradeToWrite();
wlock->materialized = true;
auto myname = getNameMgr()->resolvePathToNode(getNodeId());
auto dirPath = getOverlay()->getContentDir() + myname;
if (::mkdir(dirPath.c_str(), 0755) != 0 && errno != EEXIST) {
folly::throwSystemError("while materializing, mkdir: ", dirPath);
}
});
}
TreeInode::Dir TreeInode::buildDirFromTree(const Tree* tree) {
// Now build out the Dir based on what we know.
Dir dir;
if (!tree) {
// There's no associated Tree, so we have to persist this to the
// overlay storage area
dir.materialized = true;
return dir;
}
auto myname = getNameMgr()->resolvePathToNode(getNodeId());
dir.treeHash = tree->getHash();
for (const auto& treeEntry : tree->getTreeEntries()) {
Entry entry;
entry.hash = treeEntry.getHash();
switch (treeEntry.getFileType()) {
case FileType::DIRECTORY:
entry.mode = S_IFDIR;
break;
case FileType::REGULAR_FILE:
entry.mode = S_IFREG;
break;
case FileType::SYMLINK:
entry.mode = S_IFLNK;
break;
default:
LOG(FATAL) << "Impossible treeEntry FileType value "
<< int(treeEntry.getFileType()) << " for entry with name "
<< treeEntry.getName() << " in dir " << myname;
}
// Bit 1 is executable; expand that out to the typical executable or
// readable file mode.
entry.mode |= (treeEntry.getOwnerPermissions() & 1) ? 0744 : 0644;
dir.entries.emplace(
treeEntry.getName(), std::make_unique<Entry>(std::move(entry)));
}
return dir;
}
folly::Future<fusell::DirInode::CreateResult>
TreeInode::create(PathComponentPiece name, mode_t mode, int flags) {
// Figure out the relative path to this inode.
auto myname = getNameMgr()->resolvePathToNode(getNodeId());
// Compute the effective name of the node they want to create.
eden: merge overlay into the inode objects Summary: It was starting to get pretty complex to manage locking across the inodes, filedata, overlay and soon the journal, so as a simplifying step, this folds data that was tracked by the overlay into the TreeInode itself. This is the first diff in a short series for this. This one: 1. Breaks the persistent overlay information, so shutting down eden and bringing it back up will lose your changes (to be restored in the following diff) 2. Allows deferring materialization of file data in more cases 3. Allows renaming dirs. The approach here is now to keep just one source of information about the directory contents; when we construct a TreeInode we import this data from the Tree and then apply mutations to it locally. Each inode can be mutated indepdently from others; we only need to lock the 1, 2 or 3 participating inodes in the various mutation operations. I'll tackle persistence of the mutations in the following diff, but the high level plan for that (to help understand this diff) is to always keep the directory inodes for mutations alive as inode objects. We make use of the canForget functionality introduced by D3774269 to ensure that these don't get evicted early. On startup we'll load this information from the overlay area. This model simplifies some of the processing around reading dirs and looking up children. Since the overlay data now tracks the appropriate tree or content hash we can be much more lazy at materializing data, especially in the rename case. For example, renaming "fbcode" to "fbcod" doesn't require us to recursively materialize the "fbcode" tree. Depends on D3653706 Reviewed By: simpkins Differential Revision: D3657894 fbshipit-source-id: d4561639845ca93b93487dc84bf11ad795927b1f
2016-09-10 02:56:00 +03:00
auto targetName = myname + name;
std::shared_ptr<fusell::FileHandle> handle;
std::shared_ptr<TreeEntryFileInode> inode;
std::shared_ptr<fusell::InodeNameManager::Node> node;
materializeDirAndParents();
auto filePath = getOverlay()->getContentDir() + targetName;
// We need to scope the write lock as the getattr call below implicitly
// wants to acquire a read lock.
contents_.withWLock([&](auto& contents) {
// Since we will move this file into the underlying file data, we
// take special care to ensure that it is opened read-write
folly::File file(
filePath.c_str(),
O_RDWR | O_CREAT | (flags & ~(O_RDONLY | O_WRONLY)),
0600);
// Record the new entry
auto& entry = contents.entries[name];
entry = std::make_unique<Entry>();
entry->materialized = true;
struct stat st;
folly::checkUnixError(::fstat(file.fd(), &st));
entry->mode = st.st_mode;
// Generate an inode number for this new entry.
node = getNameMgr()->getNodeByName(getNodeId(), name);
// build a corresponding TreeEntryFileInode
inode = std::make_shared<TreeEntryFileInode>(
node->getNodeId(),
std::static_pointer_cast<TreeInode>(shared_from_this()),
entry.get(),
std::move(file));
eden: merge overlay into the inode objects Summary: It was starting to get pretty complex to manage locking across the inodes, filedata, overlay and soon the journal, so as a simplifying step, this folds data that was tracked by the overlay into the TreeInode itself. This is the first diff in a short series for this. This one: 1. Breaks the persistent overlay information, so shutting down eden and bringing it back up will lose your changes (to be restored in the following diff) 2. Allows deferring materialization of file data in more cases 3. Allows renaming dirs. The approach here is now to keep just one source of information about the directory contents; when we construct a TreeInode we import this data from the Tree and then apply mutations to it locally. Each inode can be mutated indepdently from others; we only need to lock the 1, 2 or 3 participating inodes in the various mutation operations. I'll tackle persistence of the mutations in the following diff, but the high level plan for that (to help understand this diff) is to always keep the directory inodes for mutations alive as inode objects. We make use of the canForget functionality introduced by D3774269 to ensure that these don't get evicted early. On startup we'll load this information from the overlay area. This model simplifies some of the processing around reading dirs and looking up children. Since the overlay data now tracks the appropriate tree or content hash we can be much more lazy at materializing data, especially in the rename case. For example, renaming "fbcode" to "fbcod" doesn't require us to recursively materialize the "fbcode" tree. Depends on D3653706 Reviewed By: simpkins Differential Revision: D3657894 fbshipit-source-id: d4561639845ca93b93487dc84bf11ad795927b1f
2016-09-10 02:56:00 +03:00
// The kernel wants an open operation to return the inode,
// the file handle and some attribute information.
// Let's open a file handle now.
handle = inode->finishCreate();
});
eden: merge overlay into the inode objects Summary: It was starting to get pretty complex to manage locking across the inodes, filedata, overlay and soon the journal, so as a simplifying step, this folds data that was tracked by the overlay into the TreeInode itself. This is the first diff in a short series for this. This one: 1. Breaks the persistent overlay information, so shutting down eden and bringing it back up will lose your changes (to be restored in the following diff) 2. Allows deferring materialization of file data in more cases 3. Allows renaming dirs. The approach here is now to keep just one source of information about the directory contents; when we construct a TreeInode we import this data from the Tree and then apply mutations to it locally. Each inode can be mutated indepdently from others; we only need to lock the 1, 2 or 3 participating inodes in the various mutation operations. I'll tackle persistence of the mutations in the following diff, but the high level plan for that (to help understand this diff) is to always keep the directory inodes for mutations alive as inode objects. We make use of the canForget functionality introduced by D3774269 to ensure that these don't get evicted early. On startup we'll load this information from the overlay area. This model simplifies some of the processing around reading dirs and looking up children. Since the overlay data now tracks the appropriate tree or content hash we can be much more lazy at materializing data, especially in the rename case. For example, renaming "fbcode" to "fbcod" doesn't require us to recursively materialize the "fbcode" tree. Depends on D3653706 Reviewed By: simpkins Differential Revision: D3657894 fbshipit-source-id: d4561639845ca93b93487dc84bf11ad795927b1f
2016-09-10 02:56:00 +03:00
// Now that we have the file handle, let's look up the attributes.
auto getattrResult = handle->getattr();
return getattrResult.then(
[ =, handle = std::move(handle) ](fusell::Dispatcher::Attr attr) mutable {
fusell::DirInode::CreateResult result;
eden: merge overlay into the inode objects Summary: It was starting to get pretty complex to manage locking across the inodes, filedata, overlay and soon the journal, so as a simplifying step, this folds data that was tracked by the overlay into the TreeInode itself. This is the first diff in a short series for this. This one: 1. Breaks the persistent overlay information, so shutting down eden and bringing it back up will lose your changes (to be restored in the following diff) 2. Allows deferring materialization of file data in more cases 3. Allows renaming dirs. The approach here is now to keep just one source of information about the directory contents; when we construct a TreeInode we import this data from the Tree and then apply mutations to it locally. Each inode can be mutated indepdently from others; we only need to lock the 1, 2 or 3 participating inodes in the various mutation operations. I'll tackle persistence of the mutations in the following diff, but the high level plan for that (to help understand this diff) is to always keep the directory inodes for mutations alive as inode objects. We make use of the canForget functionality introduced by D3774269 to ensure that these don't get evicted early. On startup we'll load this information from the overlay area. This model simplifies some of the processing around reading dirs and looking up children. Since the overlay data now tracks the appropriate tree or content hash we can be much more lazy at materializing data, especially in the rename case. For example, renaming "fbcode" to "fbcod" doesn't require us to recursively materialize the "fbcode" tree. Depends on D3653706 Reviewed By: simpkins Differential Revision: D3657894 fbshipit-source-id: d4561639845ca93b93487dc84bf11ad795927b1f
2016-09-10 02:56:00 +03:00
// Return all of the results back to the kernel.
result.inode = inode;
result.file = std::move(handle);
result.attr = attr;
result.node = node;
return result;
});
}
bool TreeInode::canForget() {
// We can't let this inode be forgotten while it is materialized,
// as we hold the source of truth about this entry.
return !contents_.rlock()->materialized;
}
folly::Future<fuse_entry_param> TreeInode::mkdir(
PathComponentPiece name,
mode_t mode) {
// Figure out the relative path to this inode.
auto myname = getNameMgr()->resolvePathToNode(getNodeId());
auto targetName = myname + name;
eden: merge overlay into the inode objects Summary: It was starting to get pretty complex to manage locking across the inodes, filedata, overlay and soon the journal, so as a simplifying step, this folds data that was tracked by the overlay into the TreeInode itself. This is the first diff in a short series for this. This one: 1. Breaks the persistent overlay information, so shutting down eden and bringing it back up will lose your changes (to be restored in the following diff) 2. Allows deferring materialization of file data in more cases 3. Allows renaming dirs. The approach here is now to keep just one source of information about the directory contents; when we construct a TreeInode we import this data from the Tree and then apply mutations to it locally. Each inode can be mutated indepdently from others; we only need to lock the 1, 2 or 3 participating inodes in the various mutation operations. I'll tackle persistence of the mutations in the following diff, but the high level plan for that (to help understand this diff) is to always keep the directory inodes for mutations alive as inode objects. We make use of the canForget functionality introduced by D3774269 to ensure that these don't get evicted early. On startup we'll load this information from the overlay area. This model simplifies some of the processing around reading dirs and looking up children. Since the overlay data now tracks the appropriate tree or content hash we can be much more lazy at materializing data, especially in the rename case. For example, renaming "fbcode" to "fbcod" doesn't require us to recursively materialize the "fbcode" tree. Depends on D3653706 Reviewed By: simpkins Differential Revision: D3657894 fbshipit-source-id: d4561639845ca93b93487dc84bf11ad795927b1f
2016-09-10 02:56:00 +03:00
// Compute the effective name of the node they want to create.
materializeDirAndParents();
contents_.withWLock([&](auto& contents) {
auto entIter = contents.entries.find(name);
if (entIter != contents.entries.end()) {
folly::throwSystemErrorExplicit(
EEXIST, "mkdir: ", targetName, " already exists in the overlay");
}
auto dirPath = getOverlay()->getContentDir() + targetName;
eden: merge overlay into the inode objects Summary: It was starting to get pretty complex to manage locking across the inodes, filedata, overlay and soon the journal, so as a simplifying step, this folds data that was tracked by the overlay into the TreeInode itself. This is the first diff in a short series for this. This one: 1. Breaks the persistent overlay information, so shutting down eden and bringing it back up will lose your changes (to be restored in the following diff) 2. Allows deferring materialization of file data in more cases 3. Allows renaming dirs. The approach here is now to keep just one source of information about the directory contents; when we construct a TreeInode we import this data from the Tree and then apply mutations to it locally. Each inode can be mutated indepdently from others; we only need to lock the 1, 2 or 3 participating inodes in the various mutation operations. I'll tackle persistence of the mutations in the following diff, but the high level plan for that (to help understand this diff) is to always keep the directory inodes for mutations alive as inode objects. We make use of the canForget functionality introduced by D3774269 to ensure that these don't get evicted early. On startup we'll load this information from the overlay area. This model simplifies some of the processing around reading dirs and looking up children. Since the overlay data now tracks the appropriate tree or content hash we can be much more lazy at materializing data, especially in the rename case. For example, renaming "fbcode" to "fbcod" doesn't require us to recursively materialize the "fbcode" tree. Depends on D3653706 Reviewed By: simpkins Differential Revision: D3657894 fbshipit-source-id: d4561639845ca93b93487dc84bf11ad795927b1f
2016-09-10 02:56:00 +03:00
folly::checkUnixError(
::mkdir(dirPath.c_str(), mode), "mkdir: ", dirPath, " mode=", mode);
// We succeeded, let's update our state
struct stat st;
folly::checkUnixError(::lstat(dirPath.c_str(), &st));
auto entry = std::make_unique<Entry>();
entry->mode = st.st_mode;
entry->materialized = true;
contents.entries.emplace(name, std::move(entry));
});
// Look up the inode for this new dir and return its entry info.
return getMount()->getMountPoint()->getDispatcher()->lookup(
getNodeId(), name);
}
folly::Future<folly::Unit> TreeInode::unlink(PathComponentPiece name) {
// Compute the full name of the node they want to remove.
auto myname = getNameMgr()->resolvePathToNode(getNodeId());
auto targetName = myname + name;
eden: merge overlay into the inode objects Summary: It was starting to get pretty complex to manage locking across the inodes, filedata, overlay and soon the journal, so as a simplifying step, this folds data that was tracked by the overlay into the TreeInode itself. This is the first diff in a short series for this. This one: 1. Breaks the persistent overlay information, so shutting down eden and bringing it back up will lose your changes (to be restored in the following diff) 2. Allows deferring materialization of file data in more cases 3. Allows renaming dirs. The approach here is now to keep just one source of information about the directory contents; when we construct a TreeInode we import this data from the Tree and then apply mutations to it locally. Each inode can be mutated indepdently from others; we only need to lock the 1, 2 or 3 participating inodes in the various mutation operations. I'll tackle persistence of the mutations in the following diff, but the high level plan for that (to help understand this diff) is to always keep the directory inodes for mutations alive as inode objects. We make use of the canForget functionality introduced by D3774269 to ensure that these don't get evicted early. On startup we'll load this information from the overlay area. This model simplifies some of the processing around reading dirs and looking up children. Since the overlay data now tracks the appropriate tree or content hash we can be much more lazy at materializing data, especially in the rename case. For example, renaming "fbcode" to "fbcod" doesn't require us to recursively materialize the "fbcode" tree. Depends on D3653706 Reviewed By: simpkins Differential Revision: D3657894 fbshipit-source-id: d4561639845ca93b93487dc84bf11ad795927b1f
2016-09-10 02:56:00 +03:00
// Check pre-conditions with a read lock before we materialize anything
// in case we're processing spurious unlink for a non-existent entry;
// we don't want to materialize part of a tree if we're not actually
// going to do any work in it.
contents_.withRLock([&](const auto& contents) {
auto entIter = contents.entries.find(name);
if (entIter == contents.entries.end()) {
folly::throwSystemErrorExplicit(
ENOENT, "unlink: ", targetName, " does not exist");
}
if (S_ISDIR(entIter->second->mode)) {
folly::throwSystemErrorExplicit(
EISDIR, "unlink: ", targetName, " is a directory");
}
});
materializeDirAndParents();
contents_.withWLock([&](auto& contents) {
// Re-check the pre-conditions in case we raced
auto entIter = contents.entries.find(name);
if (entIter == contents.entries.end()) {
folly::throwSystemErrorExplicit(
ENOENT, "unlink: ", targetName, " does not exist");
}
auto& ent = entIter->second;
if (S_ISDIR(ent->mode)) {
folly::throwSystemErrorExplicit(
EISDIR, "unlink: ", targetName, " is a directory");
}
if (ent->materialized) {
auto filePath = getOverlay()->getContentDir() + targetName;
folly::checkUnixError(::unlink(filePath.c_str()), "unlink: ", filePath);
}
// And actually remove it
contents.entries.erase(entIter);
});
return folly::Unit{};
}
eden: merge overlay into the inode objects Summary: It was starting to get pretty complex to manage locking across the inodes, filedata, overlay and soon the journal, so as a simplifying step, this folds data that was tracked by the overlay into the TreeInode itself. This is the first diff in a short series for this. This one: 1. Breaks the persistent overlay information, so shutting down eden and bringing it back up will lose your changes (to be restored in the following diff) 2. Allows deferring materialization of file data in more cases 3. Allows renaming dirs. The approach here is now to keep just one source of information about the directory contents; when we construct a TreeInode we import this data from the Tree and then apply mutations to it locally. Each inode can be mutated indepdently from others; we only need to lock the 1, 2 or 3 participating inodes in the various mutation operations. I'll tackle persistence of the mutations in the following diff, but the high level plan for that (to help understand this diff) is to always keep the directory inodes for mutations alive as inode objects. We make use of the canForget functionality introduced by D3774269 to ensure that these don't get evicted early. On startup we'll load this information from the overlay area. This model simplifies some of the processing around reading dirs and looking up children. Since the overlay data now tracks the appropriate tree or content hash we can be much more lazy at materializing data, especially in the rename case. For example, renaming "fbcode" to "fbcod" doesn't require us to recursively materialize the "fbcode" tree. Depends on D3653706 Reviewed By: simpkins Differential Revision: D3657894 fbshipit-source-id: d4561639845ca93b93487dc84bf11ad795927b1f
2016-09-10 02:56:00 +03:00
std::shared_ptr<fusell::InodeBase> TreeInode::lookupChildByNameLocked(
const Dir* contents,
PathComponentPiece name) {
auto mountPoint = getMount()->getMountPoint();
auto dispatcher = mountPoint->getDispatcher();
auto mgr = mountPoint->getNameMgr();
eden: merge overlay into the inode objects Summary: It was starting to get pretty complex to manage locking across the inodes, filedata, overlay and soon the journal, so as a simplifying step, this folds data that was tracked by the overlay into the TreeInode itself. This is the first diff in a short series for this. This one: 1. Breaks the persistent overlay information, so shutting down eden and bringing it back up will lose your changes (to be restored in the following diff) 2. Allows deferring materialization of file data in more cases 3. Allows renaming dirs. The approach here is now to keep just one source of information about the directory contents; when we construct a TreeInode we import this data from the Tree and then apply mutations to it locally. Each inode can be mutated indepdently from others; we only need to lock the 1, 2 or 3 participating inodes in the various mutation operations. I'll tackle persistence of the mutations in the following diff, but the high level plan for that (to help understand this diff) is to always keep the directory inodes for mutations alive as inode objects. We make use of the canForget functionality introduced by D3774269 to ensure that these don't get evicted early. On startup we'll load this information from the overlay area. This model simplifies some of the processing around reading dirs and looking up children. Since the overlay data now tracks the appropriate tree or content hash we can be much more lazy at materializing data, especially in the rename case. For example, renaming "fbcode" to "fbcod" doesn't require us to recursively materialize the "fbcode" tree. Depends on D3653706 Reviewed By: simpkins Differential Revision: D3657894 fbshipit-source-id: d4561639845ca93b93487dc84bf11ad795927b1f
2016-09-10 02:56:00 +03:00
auto node = mgr->getNodeByName(getNodeId(), name, false);
if (node) {
return dispatcher->getInode(node->getNodeId(), true);
}
eden: merge overlay into the inode objects Summary: It was starting to get pretty complex to manage locking across the inodes, filedata, overlay and soon the journal, so as a simplifying step, this folds data that was tracked by the overlay into the TreeInode itself. This is the first diff in a short series for this. This one: 1. Breaks the persistent overlay information, so shutting down eden and bringing it back up will lose your changes (to be restored in the following diff) 2. Allows deferring materialization of file data in more cases 3. Allows renaming dirs. The approach here is now to keep just one source of information about the directory contents; when we construct a TreeInode we import this data from the Tree and then apply mutations to it locally. Each inode can be mutated indepdently from others; we only need to lock the 1, 2 or 3 participating inodes in the various mutation operations. I'll tackle persistence of the mutations in the following diff, but the high level plan for that (to help understand this diff) is to always keep the directory inodes for mutations alive as inode objects. We make use of the canForget functionality introduced by D3774269 to ensure that these don't get evicted early. On startup we'll load this information from the overlay area. This model simplifies some of the processing around reading dirs and looking up children. Since the overlay data now tracks the appropriate tree or content hash we can be much more lazy at materializing data, especially in the rename case. For example, renaming "fbcode" to "fbcod" doesn't require us to recursively materialize the "fbcode" tree. Depends on D3653706 Reviewed By: simpkins Differential Revision: D3657894 fbshipit-source-id: d4561639845ca93b93487dc84bf11ad795927b1f
2016-09-10 02:56:00 +03:00
auto child = getChildByNameLocked(contents, name);
node = mgr->getNodeById(child->getNodeId());
dispatcher->recordInode(child);
return child;
}
folly::Future<folly::Unit> TreeInode::rmdir(PathComponentPiece name) {
// Compute the full name of the node they want to remove.
auto myname = getNameMgr()->resolvePathToNode(getNodeId());
auto targetName = myname + name;
eden: merge overlay into the inode objects Summary: It was starting to get pretty complex to manage locking across the inodes, filedata, overlay and soon the journal, so as a simplifying step, this folds data that was tracked by the overlay into the TreeInode itself. This is the first diff in a short series for this. This one: 1. Breaks the persistent overlay information, so shutting down eden and bringing it back up will lose your changes (to be restored in the following diff) 2. Allows deferring materialization of file data in more cases 3. Allows renaming dirs. The approach here is now to keep just one source of information about the directory contents; when we construct a TreeInode we import this data from the Tree and then apply mutations to it locally. Each inode can be mutated indepdently from others; we only need to lock the 1, 2 or 3 participating inodes in the various mutation operations. I'll tackle persistence of the mutations in the following diff, but the high level plan for that (to help understand this diff) is to always keep the directory inodes for mutations alive as inode objects. We make use of the canForget functionality introduced by D3774269 to ensure that these don't get evicted early. On startup we'll load this information from the overlay area. This model simplifies some of the processing around reading dirs and looking up children. Since the overlay data now tracks the appropriate tree or content hash we can be much more lazy at materializing data, especially in the rename case. For example, renaming "fbcode" to "fbcod" doesn't require us to recursively materialize the "fbcode" tree. Depends on D3653706 Reviewed By: simpkins Differential Revision: D3657894 fbshipit-source-id: d4561639845ca93b93487dc84bf11ad795927b1f
2016-09-10 02:56:00 +03:00
// Check pre-conditions with a read lock before we materialize anything
// in case we're processing spurious unlink for a non-existent entry;
// we don't want to materialize part of a tree if we're not actually
// going to do any work in it.
contents_.withRLock([&](const auto& contents) {
auto entIter = contents.entries.find(name);
if (entIter == contents.entries.end()) {
folly::throwSystemErrorExplicit(
ENOENT, "rmdir: ", targetName, " does not exist");
}
if (!S_ISDIR(entIter->second->mode)) {
folly::throwSystemErrorExplicit(
EISDIR, "rmdir: ", targetName, " is not a directory");
}
auto targetInode = lookupChildByNameLocked(&contents, name);
if (!targetInode) {
folly::throwSystemErrorExplicit(
EIO,
"rmdir: ",
targetName,
" is supposed to exist but didn't resolve to an inode object");
}
auto targetDir = std::dynamic_pointer_cast<TreeInode>(targetInode);
if (!targetDir) {
folly::throwSystemErrorExplicit(
EIO,
"rmdir: ",
targetName,
" is supposed to be a dir but didn't resolve to a TreeInode object");
}
targetDir->contents_.withRLock([&](const auto& targetContents) {
if (!targetContents.entries.empty()) {
folly::throwSystemErrorExplicit(
ENOTEMPTY, "rmdir: ", targetName, " is not empty");
}
eden: merge overlay into the inode objects Summary: It was starting to get pretty complex to manage locking across the inodes, filedata, overlay and soon the journal, so as a simplifying step, this folds data that was tracked by the overlay into the TreeInode itself. This is the first diff in a short series for this. This one: 1. Breaks the persistent overlay information, so shutting down eden and bringing it back up will lose your changes (to be restored in the following diff) 2. Allows deferring materialization of file data in more cases 3. Allows renaming dirs. The approach here is now to keep just one source of information about the directory contents; when we construct a TreeInode we import this data from the Tree and then apply mutations to it locally. Each inode can be mutated indepdently from others; we only need to lock the 1, 2 or 3 participating inodes in the various mutation operations. I'll tackle persistence of the mutations in the following diff, but the high level plan for that (to help understand this diff) is to always keep the directory inodes for mutations alive as inode objects. We make use of the canForget functionality introduced by D3774269 to ensure that these don't get evicted early. On startup we'll load this information from the overlay area. This model simplifies some of the processing around reading dirs and looking up children. Since the overlay data now tracks the appropriate tree or content hash we can be much more lazy at materializing data, especially in the rename case. For example, renaming "fbcode" to "fbcod" doesn't require us to recursively materialize the "fbcode" tree. Depends on D3653706 Reviewed By: simpkins Differential Revision: D3657894 fbshipit-source-id: d4561639845ca93b93487dc84bf11ad795927b1f
2016-09-10 02:56:00 +03:00
});
});
materializeDirAndParents();
contents_.withWLock([&](auto& contents) {
// Re-check the pre-conditions in case we raced
auto entIter = contents.entries.find(name);
if (entIter == contents.entries.end()) {
folly::throwSystemErrorExplicit(
ENOENT, "rmdir: ", targetName, " does not exist");
}
auto& ent = entIter->second;
if (!S_ISDIR(ent->mode)) {
folly::throwSystemErrorExplicit(
EISDIR, "rmdir: ", targetName, " is not a directory");
}
auto targetInode = lookupChildByNameLocked(&contents, name);
if (!targetInode) {
folly::throwSystemErrorExplicit(
EIO,
"rmdir: ",
targetName,
" is supposed to exist but didn't resolve to an inode object");
}
auto targetDir = std::dynamic_pointer_cast<TreeInode>(targetInode);
if (!targetDir) {
folly::throwSystemErrorExplicit(
EIO,
"rmdir: ",
targetName,
" is supposed to be a dir but didn't resolve to a TreeInode object");
}
if (!targetDir->contents_.rlock()->entries.empty()) {
folly::throwSystemErrorExplicit(
eden: merge overlay into the inode objects Summary: It was starting to get pretty complex to manage locking across the inodes, filedata, overlay and soon the journal, so as a simplifying step, this folds data that was tracked by the overlay into the TreeInode itself. This is the first diff in a short series for this. This one: 1. Breaks the persistent overlay information, so shutting down eden and bringing it back up will lose your changes (to be restored in the following diff) 2. Allows deferring materialization of file data in more cases 3. Allows renaming dirs. The approach here is now to keep just one source of information about the directory contents; when we construct a TreeInode we import this data from the Tree and then apply mutations to it locally. Each inode can be mutated indepdently from others; we only need to lock the 1, 2 or 3 participating inodes in the various mutation operations. I'll tackle persistence of the mutations in the following diff, but the high level plan for that (to help understand this diff) is to always keep the directory inodes for mutations alive as inode objects. We make use of the canForget functionality introduced by D3774269 to ensure that these don't get evicted early. On startup we'll load this information from the overlay area. This model simplifies some of the processing around reading dirs and looking up children. Since the overlay data now tracks the appropriate tree or content hash we can be much more lazy at materializing data, especially in the rename case. For example, renaming "fbcode" to "fbcod" doesn't require us to recursively materialize the "fbcode" tree. Depends on D3653706 Reviewed By: simpkins Differential Revision: D3657894 fbshipit-source-id: d4561639845ca93b93487dc84bf11ad795927b1f
2016-09-10 02:56:00 +03:00
ENOTEMPTY, "rmdir: ", targetName, " is not empty");
}
eden: merge overlay into the inode objects Summary: It was starting to get pretty complex to manage locking across the inodes, filedata, overlay and soon the journal, so as a simplifying step, this folds data that was tracked by the overlay into the TreeInode itself. This is the first diff in a short series for this. This one: 1. Breaks the persistent overlay information, so shutting down eden and bringing it back up will lose your changes (to be restored in the following diff) 2. Allows deferring materialization of file data in more cases 3. Allows renaming dirs. The approach here is now to keep just one source of information about the directory contents; when we construct a TreeInode we import this data from the Tree and then apply mutations to it locally. Each inode can be mutated indepdently from others; we only need to lock the 1, 2 or 3 participating inodes in the various mutation operations. I'll tackle persistence of the mutations in the following diff, but the high level plan for that (to help understand this diff) is to always keep the directory inodes for mutations alive as inode objects. We make use of the canForget functionality introduced by D3774269 to ensure that these don't get evicted early. On startup we'll load this information from the overlay area. This model simplifies some of the processing around reading dirs and looking up children. Since the overlay data now tracks the appropriate tree or content hash we can be much more lazy at materializing data, especially in the rename case. For example, renaming "fbcode" to "fbcod" doesn't require us to recursively materialize the "fbcode" tree. Depends on D3653706 Reviewed By: simpkins Differential Revision: D3657894 fbshipit-source-id: d4561639845ca93b93487dc84bf11ad795927b1f
2016-09-10 02:56:00 +03:00
if (ent->materialized) {
auto dirPath = getOverlay()->getContentDir() + targetName;
folly::checkUnixError(::rmdir(dirPath.c_str()), "rmdir: ", dirPath);
}
// And actually remove it
contents.entries.erase(entIter);
});
return folly::Unit{};
}
void TreeInode::renameHelper(
Dir* sourceContents,
RelativePathPiece sourceName,
Dir* destContents,
RelativePathPiece destName) {
auto sourceEntIter = sourceContents->entries.find(sourceName.basename());
if (sourceEntIter == sourceContents->entries.end()) {
folly::throwSystemErrorExplicit(
ENOENT, "rename: source file ", sourceName, " does not exist");
}
eden: merge overlay into the inode objects Summary: It was starting to get pretty complex to manage locking across the inodes, filedata, overlay and soon the journal, so as a simplifying step, this folds data that was tracked by the overlay into the TreeInode itself. This is the first diff in a short series for this. This one: 1. Breaks the persistent overlay information, so shutting down eden and bringing it back up will lose your changes (to be restored in the following diff) 2. Allows deferring materialization of file data in more cases 3. Allows renaming dirs. The approach here is now to keep just one source of information about the directory contents; when we construct a TreeInode we import this data from the Tree and then apply mutations to it locally. Each inode can be mutated indepdently from others; we only need to lock the 1, 2 or 3 participating inodes in the various mutation operations. I'll tackle persistence of the mutations in the following diff, but the high level plan for that (to help understand this diff) is to always keep the directory inodes for mutations alive as inode objects. We make use of the canForget functionality introduced by D3774269 to ensure that these don't get evicted early. On startup we'll load this information from the overlay area. This model simplifies some of the processing around reading dirs and looking up children. Since the overlay data now tracks the appropriate tree or content hash we can be much more lazy at materializing data, especially in the rename case. For example, renaming "fbcode" to "fbcod" doesn't require us to recursively materialize the "fbcode" tree. Depends on D3653706 Reviewed By: simpkins Differential Revision: D3657894 fbshipit-source-id: d4561639845ca93b93487dc84bf11ad795927b1f
2016-09-10 02:56:00 +03:00
auto destEntIter = destContents->entries.find(destName.basename());
if (mode_to_dtype(sourceEntIter->second->mode) == dtype_t::Dir &&
destEntIter != destContents->entries.end()) {
// When renaming a directory, the destination must either not exist or
// it must be an empty directory
if (mode_to_dtype(destEntIter->second->mode) != dtype_t::Dir) {
folly::throwSystemErrorExplicit(
ENOTDIR,
"attempted to rename dir ",
sourceName,
" to existing name ",
destName,
" but the latter is not a directory");
}
auto targetInode =
lookupChildByNameLocked(destContents, destName.basename());
auto destDir = std::dynamic_pointer_cast<TreeInode>(targetInode);
if (!destDir) {
folly::throwSystemErrorExplicit(
EIO, "inconsistency between contents and inodes objects");
}
if (!destDir->contents_.rlock()->entries.empty()) {
folly::throwSystemErrorExplicit(
ENOTEMPTY,
eden: merge overlay into the inode objects Summary: It was starting to get pretty complex to manage locking across the inodes, filedata, overlay and soon the journal, so as a simplifying step, this folds data that was tracked by the overlay into the TreeInode itself. This is the first diff in a short series for this. This one: 1. Breaks the persistent overlay information, so shutting down eden and bringing it back up will lose your changes (to be restored in the following diff) 2. Allows deferring materialization of file data in more cases 3. Allows renaming dirs. The approach here is now to keep just one source of information about the directory contents; when we construct a TreeInode we import this data from the Tree and then apply mutations to it locally. Each inode can be mutated indepdently from others; we only need to lock the 1, 2 or 3 participating inodes in the various mutation operations. I'll tackle persistence of the mutations in the following diff, but the high level plan for that (to help understand this diff) is to always keep the directory inodes for mutations alive as inode objects. We make use of the canForget functionality introduced by D3774269 to ensure that these don't get evicted early. On startup we'll load this information from the overlay area. This model simplifies some of the processing around reading dirs and looking up children. Since the overlay data now tracks the appropriate tree or content hash we can be much more lazy at materializing data, especially in the rename case. For example, renaming "fbcode" to "fbcod" doesn't require us to recursively materialize the "fbcode" tree. Depends on D3653706 Reviewed By: simpkins Differential Revision: D3657894 fbshipit-source-id: d4561639845ca93b93487dc84bf11ad795927b1f
2016-09-10 02:56:00 +03:00
"attempted to rename dir ",
sourceName,
" to dir ",
destName,
" but the latter is not a empty directory");
}
}
eden: merge overlay into the inode objects Summary: It was starting to get pretty complex to manage locking across the inodes, filedata, overlay and soon the journal, so as a simplifying step, this folds data that was tracked by the overlay into the TreeInode itself. This is the first diff in a short series for this. This one: 1. Breaks the persistent overlay information, so shutting down eden and bringing it back up will lose your changes (to be restored in the following diff) 2. Allows deferring materialization of file data in more cases 3. Allows renaming dirs. The approach here is now to keep just one source of information about the directory contents; when we construct a TreeInode we import this data from the Tree and then apply mutations to it locally. Each inode can be mutated indepdently from others; we only need to lock the 1, 2 or 3 participating inodes in the various mutation operations. I'll tackle persistence of the mutations in the following diff, but the high level plan for that (to help understand this diff) is to always keep the directory inodes for mutations alive as inode objects. We make use of the canForget functionality introduced by D3774269 to ensure that these don't get evicted early. On startup we'll load this information from the overlay area. This model simplifies some of the processing around reading dirs and looking up children. Since the overlay data now tracks the appropriate tree or content hash we can be much more lazy at materializing data, especially in the rename case. For example, renaming "fbcode" to "fbcod" doesn't require us to recursively materialize the "fbcode" tree. Depends on D3653706 Reviewed By: simpkins Differential Revision: D3657894 fbshipit-source-id: d4561639845ca93b93487dc84bf11ad795927b1f
2016-09-10 02:56:00 +03:00
auto contentDir = getOverlay()->getContentDir();
auto absoluteSourcePath = contentDir + sourceName;
auto absoluteDestPath = contentDir + destName;
// If we haven't actually materialized it yet, the rename() call will
// fail. So don't try that.
if (sourceEntIter->second->materialized) {
folly::checkUnixError(
::rename(absoluteSourcePath.c_str(), absoluteDestPath.c_str()),
"rename ",
absoluteSourcePath,
" to ",
absoluteDestPath,
" failed");
}
// Success.
// Update the destination with the source data (this copies in the hash if
// it happens to be set).
auto& destEnt = destContents->entries[destName.basename()];
// Note: sourceEntIter may have been invalidated by the line above in the
// case that the source and destination dirs are the same. We need to
// recompute that iterator now to be safe.
sourceEntIter = sourceContents->entries.find(sourceName.basename());
// We want to move in the data from the source.
destEnt = std::move(sourceEntIter->second);
// Now remove the source information
sourceContents->entries.erase(sourceEntIter);
}
folly::Future<folly::Unit> TreeInode::rename(
PathComponentPiece name,
std::shared_ptr<DirInode> newParent,
PathComponentPiece newName) {
auto targetDir = std::dynamic_pointer_cast<TreeInode>(newParent);
if (!targetDir) {
// This probably can't happen, but it is better to be safe than sorry.
folly::throwSystemErrorExplicit(EXDEV, "target dir is not a TreeInode");
}
auto nameMgr = getNameMgr();
auto sourceName = nameMgr->resolvePathToNode(getNodeId()) + name;
auto targetName =
nameMgr->resolvePathToNode(targetDir->getNodeId()) + newName;
eden: merge overlay into the inode objects Summary: It was starting to get pretty complex to manage locking across the inodes, filedata, overlay and soon the journal, so as a simplifying step, this folds data that was tracked by the overlay into the TreeInode itself. This is the first diff in a short series for this. This one: 1. Breaks the persistent overlay information, so shutting down eden and bringing it back up will lose your changes (to be restored in the following diff) 2. Allows deferring materialization of file data in more cases 3. Allows renaming dirs. The approach here is now to keep just one source of information about the directory contents; when we construct a TreeInode we import this data from the Tree and then apply mutations to it locally. Each inode can be mutated indepdently from others; we only need to lock the 1, 2 or 3 participating inodes in the various mutation operations. I'll tackle persistence of the mutations in the following diff, but the high level plan for that (to help understand this diff) is to always keep the directory inodes for mutations alive as inode objects. We make use of the canForget functionality introduced by D3774269 to ensure that these don't get evicted early. On startup we'll load this information from the overlay area. This model simplifies some of the processing around reading dirs and looking up children. Since the overlay data now tracks the appropriate tree or content hash we can be much more lazy at materializing data, especially in the rename case. For example, renaming "fbcode" to "fbcod" doesn't require us to recursively materialize the "fbcode" tree. Depends on D3653706 Reviewed By: simpkins Differential Revision: D3657894 fbshipit-source-id: d4561639845ca93b93487dc84bf11ad795927b1f
2016-09-10 02:56:00 +03:00
// Check pre-conditions with a read lock before we materialize anything
// in case we're processing spurious rename for a non-existent entry;
// we don't want to materialize part of a tree if we're not actually
// going to do any work in it.
// There are some more complex pre-conditions that we'd like to check
// before materializing, but we cannot do so in a race free manner
// without locking each of the associated objects. The existence
// check is sufficient to avoid the majority of the potentially
// wasted effort.
contents_.withRLock([&](const auto& contents) {
auto entIter = contents.entries.find(name);
if (entIter == contents.entries.end()) {
folly::throwSystemErrorExplicit(
ENOENT, "rename: source file ", sourceName, " does not exist");
}
});
materializeDirAndParents();
// Can't use SYNCHRONIZED_DUAL for both cases, as we'd self-deadlock by trying
// to wlock the same thing twice
if (targetDir.get() == this) {
contents_.withWLock([&](auto& contents) {
renameHelper(&contents, sourceName, &contents, targetName);
});
} else {
targetDir->materializeDirAndParents();
SYNCHRONIZED_DUAL(
sourceContents, contents_, destContents, targetDir->contents_) {
renameHelper(&sourceContents, sourceName, &destContents, targetName);
}
}
return folly::Unit{};
}
EdenMount* TreeInode::getMount() const {
return mount_;
}
fusell::InodeNameManager* TreeInode::getNameMgr() const {
return mount_->getMountPoint()->getNameMgr();
}
ObjectStore* TreeInode::getStore() const {
return mount_->getObjectStore();
}
const std::shared_ptr<Overlay>& TreeInode::getOverlay() const {
return mount_->getOverlay();
}
void TreeInode::performCheckout(const Hash& hash) {
throw std::runtime_error("not yet implemented");
}
}
}