2016-05-12 23:43:17 +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 "TreeInode.h"
|
2016-05-20 20:33:42 +03:00
|
|
|
|
|
|
|
#include "EdenMount.h"
|
2016-05-12 23:43:17 +03:00
|
|
|
#include "TreeEntryFileInode.h"
|
|
|
|
#include "TreeInodeDirHandle.h"
|
2016-05-26 05:05:48 +03:00
|
|
|
#include "eden/fs/inodes/EdenMount.h"
|
2016-07-06 05:53:16 +03:00
|
|
|
#include "eden/fs/inodes/FileData.h"
|
2016-06-16 00:23:26 +03:00
|
|
|
#include "eden/fs/model/Tree.h"
|
|
|
|
#include "eden/fs/model/TreeEntry.h"
|
2016-05-12 23:43:17 +03:00
|
|
|
#include "eden/fs/overlay/Overlay.h"
|
2016-06-09 04:59:51 +03:00
|
|
|
#include "eden/fs/store/ObjectStore.h"
|
2016-05-26 05:05:48 +03:00
|
|
|
#include "eden/fuse/MountPoint.h"
|
2016-05-18 22:23:09 +03:00
|
|
|
#include "eden/fuse/RequestData.h"
|
2016-05-12 23:43:17 +03:00
|
|
|
#include "eden/utils/PathFuncs.h"
|
|
|
|
|
|
|
|
namespace facebook {
|
|
|
|
namespace eden {
|
|
|
|
|
|
|
|
TreeInode::TreeInode(
|
2016-05-20 20:33:42 +03:00
|
|
|
EdenMount* mount,
|
2016-05-12 23:43:17 +03:00
|
|
|
std::unique_ptr<Tree>&& tree,
|
|
|
|
fuse_ino_t parent,
|
2016-05-20 20:33:42 +03:00
|
|
|
fuse_ino_t ino)
|
2016-06-20 23:38:37 +03:00
|
|
|
: DirInode(ino), mount_(mount), tree_(std::move(tree)), parent_(parent) {}
|
2016-05-12 23:43:17 +03:00
|
|
|
|
2016-05-20 20:33:42 +03:00
|
|
|
TreeInode::TreeInode(EdenMount* mount, fuse_ino_t parent, fuse_ino_t ino)
|
2016-06-20 23:38:37 +03:00
|
|
|
: DirInode(ino), mount_(mount), parent_(parent) {}
|
2016-05-12 23:43:17 +03:00
|
|
|
|
|
|
|
TreeInode::~TreeInode() {}
|
|
|
|
|
|
|
|
folly::Future<fusell::Dispatcher::Attr> TreeInode::getattr() {
|
|
|
|
fusell::Dispatcher::Attr attr;
|
|
|
|
|
|
|
|
attr.st.st_mode = S_IFDIR | 0755;
|
2016-06-20 23:38:37 +03:00
|
|
|
attr.st.st_ino = getNodeId();
|
|
|
|
// TODO: set nlink. It should be 2 plus the number of subdirectories
|
|
|
|
// TODO: set atime, mtime, and ctime
|
2016-05-12 23:43:17 +03:00
|
|
|
|
|
|
|
return attr;
|
|
|
|
}
|
|
|
|
|
|
|
|
folly::Future<std::shared_ptr<fusell::InodeBase>> TreeInode::getChildByName(
|
|
|
|
PathComponentPiece namepiece) {
|
2016-06-20 23:38:37 +03:00
|
|
|
auto myname = getNameMgr()->resolvePathToNode(getNodeId());
|
2016-05-20 20:33:42 +03:00
|
|
|
auto overlay_contents = getOverlay()->readDir(myname);
|
2016-05-12 23:43:17 +03:00
|
|
|
|
|
|
|
const auto& iter = overlay_contents.entries.find(namepiece.copy());
|
|
|
|
if (iter != overlay_contents.entries.end()) {
|
|
|
|
if (iter->second == dtype_t::Whiteout) {
|
|
|
|
// This entry was deleted.
|
|
|
|
folly::throwSystemErrorExplicit(ENOENT);
|
|
|
|
}
|
|
|
|
|
2016-06-20 23:38:37 +03:00
|
|
|
auto node = getNameMgr()->getNodeByName(getNodeId(), namepiece);
|
2016-05-12 23:43:17 +03:00
|
|
|
|
|
|
|
if (iter->second == dtype_t::Dir) {
|
2016-06-16 00:23:26 +03:00
|
|
|
if (!overlay_contents.isOpaque) {
|
|
|
|
// Check to see if we also have a TreeEntry for this directory
|
|
|
|
const auto* entry = getTreeEntry(namepiece);
|
|
|
|
if (entry != nullptr && entry->getFileType() == FileType::DIRECTORY) {
|
|
|
|
auto tree = getStore()->getTree(entry->getHash());
|
|
|
|
return std::make_shared<TreeInode>(
|
2016-06-20 23:38:37 +03:00
|
|
|
mount_, std::move(tree), getNodeId(), node->getNodeId());
|
2016-06-16 00:23:26 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
// No corresponding TreeEntry, this exists only in the overlay.
|
2016-06-20 23:38:37 +03:00
|
|
|
return std::make_shared<TreeInode>(
|
|
|
|
mount_, getNodeId(), node->getNodeId());
|
2016-05-12 23:43:17 +03:00
|
|
|
}
|
|
|
|
|
2016-05-17 00:48:28 +03:00
|
|
|
return std::make_shared<TreeEntryFileInode>(
|
|
|
|
node->getNodeId(),
|
|
|
|
std::static_pointer_cast<TreeInode>(shared_from_this()),
|
|
|
|
nullptr);
|
2016-05-12 23:43:17 +03:00
|
|
|
}
|
|
|
|
|
2016-06-16 00:23:26 +03:00
|
|
|
if (overlay_contents.isOpaque) {
|
2016-05-12 23:43:17 +03:00
|
|
|
// No tree, or nothing from the tree is visible.
|
|
|
|
folly::throwSystemErrorExplicit(ENOENT);
|
|
|
|
}
|
|
|
|
|
2016-06-16 00:23:26 +03:00
|
|
|
const auto* ent = getTreeEntry(namepiece);
|
|
|
|
if (ent != nullptr) {
|
2016-06-20 23:38:37 +03:00
|
|
|
auto node = getNameMgr()->getNodeByName(getNodeId(), namepiece);
|
2016-05-12 23:43:17 +03:00
|
|
|
|
2016-06-16 00:23:26 +03:00
|
|
|
if (ent->getFileType() == FileType::DIRECTORY) {
|
|
|
|
auto tree = getStore()->getTree(ent->getHash());
|
|
|
|
return std::make_shared<TreeInode>(
|
2016-06-20 23:38:37 +03:00
|
|
|
mount_, std::move(tree), getNodeId(), node->getNodeId());
|
2016-05-12 23:43:17 +03:00
|
|
|
}
|
2016-06-16 00:23:26 +03:00
|
|
|
|
|
|
|
return std::make_shared<TreeEntryFileInode>(
|
|
|
|
node->getNodeId(),
|
|
|
|
std::static_pointer_cast<TreeInode>(shared_from_this()),
|
|
|
|
ent);
|
2016-05-12 23:43:17 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// No matching entry with that name
|
|
|
|
folly::throwSystemErrorExplicit(ENOENT);
|
|
|
|
}
|
|
|
|
|
2016-06-16 00:23:26 +03:00
|
|
|
const TreeEntry* TreeInode::getTreeEntry(PathComponentPiece name) {
|
|
|
|
if (!tree_) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2016-07-06 03:41:43 +03:00
|
|
|
return tree_->getEntryPtr(name);
|
2016-06-16 00:23:26 +03:00
|
|
|
}
|
|
|
|
|
2016-05-12 23:43:17 +03:00
|
|
|
const Tree* TreeInode::getTree() const {
|
|
|
|
return tree_.get();
|
|
|
|
}
|
|
|
|
|
|
|
|
fuse_ino_t TreeInode::getParent() const {
|
|
|
|
return parent_;
|
|
|
|
}
|
|
|
|
|
|
|
|
fuse_ino_t TreeInode::getInode() const {
|
2016-06-20 23:38:37 +03:00
|
|
|
return getNodeId();
|
2016-05-12 23:43:17 +03:00
|
|
|
}
|
|
|
|
|
2016-07-26 19:59:18 +03:00
|
|
|
folly::Future<std::shared_ptr<fusell::DirHandle>> TreeInode::opendir(
|
2016-05-12 23:43:17 +03:00
|
|
|
const struct fuse_file_info&) {
|
2016-07-26 19:59:18 +03:00
|
|
|
return std::make_shared<TreeInodeDirHandle>(
|
2016-05-12 23:43:17 +03:00
|
|
|
std::static_pointer_cast<TreeInode>(shared_from_this()));
|
|
|
|
}
|
|
|
|
|
|
|
|
folly::Future<fusell::DirInode::CreateResult>
|
|
|
|
TreeInode::create(PathComponentPiece name, mode_t mode, int flags) {
|
|
|
|
// Figure out the relative path to this inode.
|
2016-06-20 23:38:37 +03:00
|
|
|
auto myname = getNameMgr()->resolvePathToNode(getNodeId());
|
2016-05-12 23:43:17 +03:00
|
|
|
|
|
|
|
// Compute the effective name of the node they want to create.
|
|
|
|
auto targetname = myname + name;
|
|
|
|
|
|
|
|
// Ask the overlay manager to create it.
|
2016-07-06 05:53:15 +03:00
|
|
|
// Since we will move this file into the underlying file data, we
|
|
|
|
// take special care to ensure that it is opened read-write
|
|
|
|
auto file = getOverlay()->openFile(
|
|
|
|
targetname, O_RDWR | O_CREAT | (flags & ~(O_RDONLY | O_WRONLY)), 0600);
|
2016-05-12 23:43:17 +03:00
|
|
|
|
|
|
|
// Generate an inode number for this new entry.
|
2016-06-20 23:38:37 +03:00
|
|
|
auto node = getNameMgr()->getNodeByName(getNodeId(), name);
|
2016-05-12 23:43:17 +03:00
|
|
|
|
2016-05-18 03:22:22 +03:00
|
|
|
// build a corresponding TreeEntryFileInode
|
|
|
|
auto inode = std::make_shared<TreeEntryFileInode>(
|
|
|
|
node->getNodeId(),
|
|
|
|
std::static_pointer_cast<TreeInode>(shared_from_this()),
|
2016-07-06 05:53:15 +03:00
|
|
|
std::move(file));
|
2016-05-18 03:22:22 +03:00
|
|
|
|
|
|
|
fuse_file_info fi;
|
|
|
|
memset(&fi, 0, sizeof(fi));
|
|
|
|
|
|
|
|
// The kernel wants an open operation to return the inode,
|
|
|
|
// the file handle and some attribute information.
|
|
|
|
// Let's open a file handle now.
|
2016-07-26 19:59:18 +03:00
|
|
|
return inode->open(fi).then([=](std::shared_ptr<fusell::FileHandle> handle) {
|
2016-05-18 03:22:22 +03:00
|
|
|
// Now that we have the file handle, let's look up the attributes.
|
2016-06-21 01:24:11 +03:00
|
|
|
auto getattrResult = handle->getattr();
|
|
|
|
return getattrResult.then([ =, handle = std::move(handle) ](
|
2016-05-18 03:22:22 +03:00
|
|
|
fusell::Dispatcher::Attr attr) mutable {
|
|
|
|
fusell::DirInode::CreateResult result;
|
|
|
|
|
|
|
|
// 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;
|
|
|
|
});
|
|
|
|
});
|
2016-05-12 23:43:17 +03:00
|
|
|
}
|
|
|
|
|
2016-05-18 22:23:09 +03:00
|
|
|
folly::Future<fuse_entry_param> TreeInode::mkdir(
|
|
|
|
PathComponentPiece name,
|
|
|
|
mode_t mode) {
|
|
|
|
// Figure out the relative path to this inode.
|
2016-06-20 23:38:37 +03:00
|
|
|
auto myname = getNameMgr()->resolvePathToNode(getNodeId());
|
2016-05-18 22:23:09 +03:00
|
|
|
|
|
|
|
// Compute the effective name of the node they want to create.
|
|
|
|
auto targetName = myname + name;
|
|
|
|
|
|
|
|
// Will throw if we can't make the dir.
|
2016-05-20 20:33:42 +03:00
|
|
|
getOverlay()->makeDir(targetName, mode);
|
2016-05-18 22:23:09 +03:00
|
|
|
|
|
|
|
// Look up the inode for this new dir and return its entry info.
|
2016-05-26 05:05:48 +03:00
|
|
|
return getMount()->getMountPoint()->getDispatcher()->lookup(
|
|
|
|
getNodeId(), name);
|
2016-05-18 22:23:09 +03:00
|
|
|
}
|
|
|
|
|
2016-07-06 05:53:13 +03:00
|
|
|
folly::Future<folly::Unit> TreeInode::unlink(PathComponentPiece name) {
|
|
|
|
// This will throw ENOENT if the name doesn't exist.
|
|
|
|
auto inode = getChildByName(name).get();
|
|
|
|
|
|
|
|
auto treeInode = std::dynamic_pointer_cast<TreeInode>(inode);
|
|
|
|
if (treeInode) {
|
|
|
|
folly::throwSystemErrorExplicit(EISDIR, "cannot unlink a dir");
|
|
|
|
}
|
|
|
|
// Compute the full name of the node they want to remove.
|
|
|
|
auto myname = getNameMgr()->resolvePathToNode(getNodeId());
|
|
|
|
auto targetName = myname + name;
|
|
|
|
|
|
|
|
auto needWhiteout = getTreeEntry(name) != nullptr;
|
|
|
|
auto overlay = getOverlay();
|
|
|
|
overlay->removeFile(targetName, needWhiteout);
|
|
|
|
return folly::Unit{};
|
|
|
|
}
|
|
|
|
|
|
|
|
folly::Future<folly::Unit> TreeInode::rmdir(PathComponentPiece name) {
|
|
|
|
// This will throw ENOENT if the name doesn't exist.
|
|
|
|
auto inode = getChildByName(name).get();
|
|
|
|
|
|
|
|
auto treeInode = std::dynamic_pointer_cast<TreeInode>(inode);
|
|
|
|
if (!treeInode) {
|
|
|
|
folly::throwSystemErrorExplicit(ENOTDIR, "rmdir used on a file");
|
|
|
|
}
|
|
|
|
|
|
|
|
// Compute the full name of the node they want to remove.
|
|
|
|
auto myname = getNameMgr()->resolvePathToNode(getNodeId());
|
|
|
|
auto targetName = myname + name;
|
|
|
|
|
|
|
|
auto childEntry = getTreeEntry(name);
|
|
|
|
auto needWhiteout = childEntry != nullptr;
|
|
|
|
auto overlay = getOverlay();
|
|
|
|
|
|
|
|
// Pre-condition for removing a dir is that it must be empty;
|
|
|
|
auto overlay_contents = overlay->readDir(targetName);
|
|
|
|
|
|
|
|
if (!overlay_contents.isOpaque && childEntry) {
|
|
|
|
auto childTree = getStore()->getTree(childEntry->getHash());
|
|
|
|
// Check for any tree entries that are not marked as removed in the overlay
|
|
|
|
for (auto& treeEntry : childTree->getTreeEntries()) {
|
|
|
|
auto overlayIter = overlay_contents.entries.find(treeEntry.getName());
|
|
|
|
if (overlayIter != overlay_contents.entries.end()) {
|
|
|
|
if (overlayIter->second == dtype_t::Whiteout) {
|
|
|
|
// This entry is marked as deleted, so it doesn't count here
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
folly::throwSystemErrorExplicit(
|
|
|
|
ENOTEMPTY, "rmdir used on dir that is not empty (children in Tree)");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (auto& ent : overlay_contents.entries) {
|
|
|
|
if (ent.second != dtype_t::Whiteout) {
|
|
|
|
folly::throwSystemErrorExplicit(
|
|
|
|
ENOTEMPTY,
|
|
|
|
"rmdir used on dir that is not empty (children in Overlay)");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
overlay->removeDir(targetName, needWhiteout);
|
|
|
|
return folly::Unit{};
|
|
|
|
}
|
|
|
|
|
2016-07-06 05:53:16 +03:00
|
|
|
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 childInode = getChildByName(name).get();
|
|
|
|
if (std::dynamic_pointer_cast<TreeInode>(childInode)) {
|
|
|
|
// We'd have to recursively materialize the dir before we can move it
|
|
|
|
// in our current model, so we're focusing on renaming files first.
|
|
|
|
folly::throwSystemErrorExplicit(ENOSYS, "don't yet support renaming dirs");
|
|
|
|
}
|
|
|
|
|
|
|
|
auto nameMgr = getNameMgr();
|
|
|
|
auto sourceName = nameMgr->resolvePathToNode(getNodeId()) + name;
|
|
|
|
|
|
|
|
// Ensure that the file is materialized, otherwise the rename call
|
|
|
|
// performed by the overlay manager will fail
|
|
|
|
auto fileInode = std::dynamic_pointer_cast<TreeEntryFileInode>(childInode);
|
|
|
|
if (!fileInode) {
|
|
|
|
// This can't happen, but it is better to be safe than sorry.
|
|
|
|
folly::throwSystemErrorExplicit(
|
|
|
|
EXDEV, "source file is not a TreeEntryFileInode");
|
|
|
|
}
|
|
|
|
auto fileData = fileInode->getOrLoadData();
|
|
|
|
// We need O_RDWR so that the overlay manager populates the local file
|
|
|
|
fileData->materialize(O_RDWR, sourceName);
|
|
|
|
|
|
|
|
auto targetName =
|
|
|
|
nameMgr->resolvePathToNode(targetDir->getNodeId()) + newName;
|
|
|
|
bool needWhiteout = getTreeEntry(name) != nullptr;
|
|
|
|
|
|
|
|
getOverlay()->renameFile(sourceName, targetName, needWhiteout);
|
|
|
|
return folly::Unit{};
|
|
|
|
}
|
|
|
|
|
2016-05-20 20:33:42 +03:00
|
|
|
EdenMount* TreeInode::getMount() const {
|
|
|
|
return mount_;
|
|
|
|
}
|
|
|
|
|
2016-05-26 05:05:48 +03:00
|
|
|
fusell::InodeNameManager* TreeInode::getNameMgr() const {
|
|
|
|
return mount_->getMountPoint()->getNameMgr();
|
|
|
|
}
|
|
|
|
|
2016-06-09 04:59:51 +03:00
|
|
|
ObjectStore* TreeInode::getStore() const {
|
|
|
|
return mount_->getObjectStore();
|
2016-05-12 23:43:17 +03:00
|
|
|
}
|
|
|
|
|
2016-05-20 20:33:42 +03:00
|
|
|
const std::shared_ptr<Overlay>& TreeInode::getOverlay() const {
|
|
|
|
return mount_->getOverlay();
|
2016-05-12 23:43:17 +03:00
|
|
|
}
|
|
|
|
|
2016-05-20 20:33:42 +03:00
|
|
|
void TreeInode::performCheckout(const Hash& hash) {
|
2016-05-12 23:43:17 +03:00
|
|
|
throw std::runtime_error("not yet implemented");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|