sapling/eden/fs/inodes/TreeInodeDirHandle.cpp
Wez Furlong e6239f63c4 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-09 16:57:58 -07:00

136 lines
4.6 KiB
C++

/*
* 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 "TreeInodeDirHandle.h"
#include "eden/fs/model/Tree.h"
#include "eden/fs/overlay/Overlay.h"
#include "eden/utils/DirType.h"
namespace facebook {
namespace eden {
TreeInodeDirHandle::TreeInodeDirHandle(std::shared_ptr<TreeInode> inode)
: inode_(inode) {}
folly::Future<fusell::DirList> TreeInodeDirHandle::readdir(
fusell::DirList&& list,
off_t off) {
// We will be called mulitple times for a given directory read. The first
// time through, the off parameter will be 0 to indicate that it is reading
// from the start. On subsequent calls it will hold the off value from the
// last the entry we added to the DirList. It may actually hold some
// arbitrary offset if the application is seeking around in the dir stream.
// Most applications will perform a full scan until we return an empty
// DirList.
// We need to return as soon as we have filled the available space in the
// provided DirList object.
// The inode of this directory
auto dir_inode = inode_->getInode();
// It's pretty complicated to stitch together the directory contents
// while trying to skip around and respect the offset parameter.
// Let's go for the simple approach and see if that is good enough.
//
// There are three components to the DirList that we need to populate:
// 1. The "." and ".." entries
// 2. The overlay entries if we have any
// 3. The tree entries if we do not have an overlay entry for this dir.
//
// We're going to build a vector of this combined information,
// then we can paginate sanely using the off parameter.
/** This struct is pretty lightweight and doesn't need to make any
* heap copies of names as we accumulate the view of the entries */
struct Entry {
const char* name;
dtype_t type;
/// If 0, look up/assign it based on name
fuse_ino_t ino;
Entry(const char* name, dtype_t type, fuse_ino_t ino = 0)
: name(name), type(type), ino(ino) {}
};
folly::fbvector<Entry> entries;
// Fetch some info now so that we can be more efficient
// while populating entries.
auto myname = inode_->getNameMgr()->resolvePathToNode(dir_inode);
inode_->getContents().withRLock([&](const auto& dir) {
entries.reserve(2 /* "." and ".." */ + dir.entries.size());
// Reserved entries for linking to parent and self.
entries.emplace_back(".", dtype_t::Dir, dir_inode);
entries.emplace_back("..", dtype_t::Dir, inode_->getParent());
for (const auto& entry : dir.entries) {
entries.emplace_back(
entry.first.value().c_str(), mode_to_dtype(entry.second->mode));
}
});
// And now the easy part: seek to the provided offset and fill up
// the DirList with the entries that remain.
auto entry_iter = entries.begin();
std::advance(entry_iter, off);
// The stat struct is only used by the fuse machinery to compute the type
// of the entry so that it can report an appropriate DT_XXX type up to
// the caller of readdir(), so we zero initialize it and only fill in
// the type bits of the mode. The reset are irrelevant and we don't
// need to waste effort populating them. We zero out the struct here
// once and vary just the bits that need to be updated in the loop below.
// https://www.daemon-systems.org/man/DTTOIF.3.html
struct stat st;
memset(&st, 0, sizeof(st));
while (entry_iter < entries.end()) {
const auto& entry = *entry_iter;
st.st_ino = entry.ino;
st.st_mode = dtype_to_mode(entry.type);
if (st.st_ino == 0) {
// We haven't looked up its inode yet, do so now.
// We defer it from the first pass above in case we have a huge
// dir and need to paginate through it across several calls into
// this function.
auto node = inode_->getNameMgr()->getNodeByName(
dir_inode, PathComponentPiece(entry.name));
st.st_ino = node->getNodeId();
}
if (!list.add(entry.name, st, ++off)) {
break;
}
++entry_iter;
}
return std::move(list);
}
folly::Future<fusell::Dispatcher::Attr> TreeInodeDirHandle::setattr(
const struct stat& attr,
int to_set) {
folly::throwSystemErrorExplicit(EROFS);
}
folly::Future<folly::Unit> TreeInodeDirHandle::fsyncdir(bool datasync) {
// We're read-only here, so there is nothing to sync
return folly::Unit{};
}
folly::Future<fusell::Dispatcher::Attr> TreeInodeDirHandle::getattr() {
return inode_->getattr();
}
}
}