2017-01-26 23:45:50 +03:00
|
|
|
/*
|
|
|
|
* Copyright (c) 2016-present, 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 "GlobNode.h"
|
|
|
|
#include "eden/fs/inodes/TreeInode.h"
|
|
|
|
|
|
|
|
using folly::Future;
|
|
|
|
using folly::makeFuture;
|
2018-01-26 22:17:36 +03:00
|
|
|
using folly::StringPiece;
|
2017-04-05 22:50:57 +03:00
|
|
|
using std::make_unique;
|
2017-11-04 01:58:04 +03:00
|
|
|
using std::string;
|
|
|
|
using std::unique_ptr;
|
|
|
|
using std::vector;
|
2017-01-26 23:45:50 +03:00
|
|
|
|
|
|
|
namespace facebook {
|
|
|
|
namespace eden {
|
|
|
|
|
2018-05-25 23:47:46 +03:00
|
|
|
namespace {
|
|
|
|
|
|
|
|
// Policy objects to help avoid duplicating the core globbing logic.
|
|
|
|
// We can walk over two different kinds of trees; either TreeInodes
|
|
|
|
// or raw Trees from the storage layer. While they have similar
|
|
|
|
// properties, accessing them is a little different. These policy
|
|
|
|
// objects are thin shims that make access more uniform.
|
|
|
|
|
|
|
|
/** TreeInodePtrRoot wraps a TreeInodePtr for globbing.
|
|
|
|
* TreeInodes require that a lock be held while its entries
|
|
|
|
* are iterated.
|
|
|
|
* We only need to prefetch children of TreeInodes that are
|
|
|
|
* not materialized.
|
|
|
|
*/
|
|
|
|
struct TreeInodePtrRoot {
|
|
|
|
TreeInodePtr root;
|
|
|
|
|
|
|
|
explicit TreeInodePtrRoot(TreeInodePtr root) : root(root) {}
|
|
|
|
|
|
|
|
/** Return an object that holds a lock over the children */
|
|
|
|
auto lockContents() {
|
|
|
|
return root->getContents().rlock();
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Given the return value from lockContents and a name,
|
|
|
|
* return a pointer to the child with that name, or nullptr
|
|
|
|
* if there is no match */
|
|
|
|
template <typename CONTENTS>
|
2018-06-01 19:29:47 +03:00
|
|
|
const DirEntry* FOLLY_NULLABLE
|
2018-05-25 23:47:46 +03:00
|
|
|
lookupEntry(CONTENTS& contents, PathComponentPiece name) {
|
|
|
|
auto it = contents->entries.find(name);
|
|
|
|
if (it != contents->entries.end()) {
|
|
|
|
return &it->second;
|
|
|
|
}
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Return an object that can be used in a generic for()
|
|
|
|
* constructor to iterate over the contents. You must supply
|
|
|
|
* the CONTENTS object you obtained via lockContents().
|
|
|
|
* The returned iterator yields ENTRY elements that can be
|
|
|
|
* used with the entryXXX methods below. */
|
|
|
|
template <typename CONTENTS>
|
|
|
|
auto& iterate(CONTENTS& contents) {
|
|
|
|
return contents->entries;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Arrange to load a child TreeInode */
|
|
|
|
Future<TreeInodePtr> getOrLoadChildTree(PathComponentPiece name) {
|
|
|
|
return root->getOrLoadChildTree(name);
|
|
|
|
}
|
|
|
|
/** Returns true if we should call getOrLoadChildTree() for the given
|
|
|
|
* ENTRY. We only do this if the child is already materialized */
|
|
|
|
template <typename ENTRY>
|
|
|
|
bool entryShouldLoadChildTree(const ENTRY& entry) {
|
|
|
|
return entry.second.isMaterialized();
|
|
|
|
}
|
2018-06-01 19:29:47 +03:00
|
|
|
bool entryShouldLoadChildTree(const DirEntry* entry) {
|
2018-05-25 23:47:46 +03:00
|
|
|
return entry->isMaterialized();
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Returns the name for a given ENTRY */
|
|
|
|
template <typename ENTRY>
|
|
|
|
PathComponentPiece entryName(const ENTRY& entry) {
|
|
|
|
return entry.first;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Returns true if the given ENTRY is a tree */
|
|
|
|
template <typename ENTRY>
|
|
|
|
bool entryIsTree(const ENTRY& entry) {
|
|
|
|
return entry.second.isDirectory();
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Returns true if the given ENTRY is a tree (pointer version) */
|
2018-06-01 19:29:47 +03:00
|
|
|
bool entryIsTree(const DirEntry* entry) {
|
2018-05-25 23:47:46 +03:00
|
|
|
return entry->isDirectory();
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Returns true if we should prefetch the blob content for the entry.
|
|
|
|
* We only do this if the child is not already materialized */
|
|
|
|
template <typename ENTRY>
|
|
|
|
bool entryShouldPrefetch(const ENTRY& entry) {
|
2018-07-16 21:21:41 +03:00
|
|
|
return !entry.second.isMaterialized() && !entryIsTree(entry);
|
2018-05-25 23:47:46 +03:00
|
|
|
}
|
2018-06-01 19:29:47 +03:00
|
|
|
bool entryShouldPrefetch(const DirEntry* entry) {
|
2018-07-16 21:21:41 +03:00
|
|
|
return !entry->isMaterialized() && !entryIsTree(entry);
|
2018-05-25 23:47:46 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/** Returns the hash for the given ENTRY */
|
|
|
|
template <typename ENTRY>
|
|
|
|
const Hash entryHash(const ENTRY& entry) {
|
|
|
|
return entry.second.getHash();
|
|
|
|
}
|
2018-06-01 19:29:47 +03:00
|
|
|
const Hash entryHash(const DirEntry* entry) {
|
2018-05-25 23:47:46 +03:00
|
|
|
return entry->getHash();
|
|
|
|
}
|
2019-02-19 22:23:30 +03:00
|
|
|
|
|
|
|
template <typename ENTRY>
|
|
|
|
GlobNode::GlobResult entryToResult(
|
|
|
|
RelativePath&& entryPath,
|
|
|
|
const ENTRY& entry) {
|
|
|
|
return this->entryToResult(std::move(entryPath), &entry.second);
|
|
|
|
}
|
|
|
|
GlobNode::GlobResult entryToResult(
|
|
|
|
RelativePath&& entryPath,
|
|
|
|
const DirEntry* entry) {
|
|
|
|
return GlobNode::GlobResult{std::move(entryPath), entry->getDtype()};
|
|
|
|
}
|
2018-05-25 23:47:46 +03:00
|
|
|
};
|
|
|
|
|
|
|
|
/** TreeRoot wraps a Tree for globbing.
|
|
|
|
* The entries do not need to be locked, but to satisfy the interface
|
|
|
|
* we return the entries when lockContents() is called.
|
|
|
|
*/
|
|
|
|
struct TreeRoot {
|
|
|
|
std::shared_ptr<const Tree> tree;
|
|
|
|
|
|
|
|
explicit TreeRoot(const std::shared_ptr<const Tree>& tree) : tree(tree) {}
|
|
|
|
|
|
|
|
/** We don't need to lock the contents, so we just return a reference
|
|
|
|
* to the entries */
|
|
|
|
auto& lockContents() {
|
|
|
|
return tree->getTreeEntries();
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Return an object that can be used in a generic for()
|
|
|
|
* constructor to iterate over the contents. You must supply
|
|
|
|
* the CONTENTS object you obtained via lockContents().
|
|
|
|
* The returned iterator yields ENTRY elements that can be
|
|
|
|
* used with the entryXXX methods below. */
|
|
|
|
template <typename CONTENTS>
|
|
|
|
auto& iterate(CONTENTS& contents) {
|
|
|
|
return contents;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** We can never load a TreeInodePtr from a raw Tree, so this always
|
|
|
|
* fails. We never call this method because entryShouldLoadChildTree()
|
|
|
|
* always returns false. */
|
|
|
|
folly::Future<TreeInodePtr> getOrLoadChildTree(PathComponentPiece) {
|
|
|
|
throw std::runtime_error("impossible to get here");
|
|
|
|
}
|
|
|
|
template <typename ENTRY>
|
|
|
|
bool entryShouldLoadChildTree(const ENTRY&) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename CONTENTS>
|
|
|
|
auto* FOLLY_NULLABLE lookupEntry(CONTENTS&, PathComponentPiece name) {
|
|
|
|
return tree->getEntryPtr(name);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename ENTRY>
|
|
|
|
PathComponentPiece entryName(const ENTRY& entry) {
|
|
|
|
return entry.getName();
|
|
|
|
}
|
|
|
|
template <typename ENTRY>
|
|
|
|
bool entryIsTree(const ENTRY& entry) {
|
|
|
|
return entry.isTree();
|
|
|
|
}
|
|
|
|
bool entryIsTree(const TreeEntry* entry) {
|
|
|
|
return entry->isTree();
|
|
|
|
}
|
|
|
|
|
2018-07-16 21:21:41 +03:00
|
|
|
// We always need to prefetch file children of a raw Tree
|
2018-05-25 23:47:46 +03:00
|
|
|
template <typename ENTRY>
|
2018-07-16 21:21:41 +03:00
|
|
|
bool entryShouldPrefetch(const ENTRY& entry) {
|
|
|
|
return !entryIsTree(entry);
|
2018-05-25 23:47:46 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
template <typename ENTRY>
|
|
|
|
const Hash entryHash(const ENTRY& entry) {
|
|
|
|
return entry.getHash();
|
|
|
|
}
|
|
|
|
const Hash entryHash(const TreeEntry* entry) {
|
|
|
|
return entry->getHash();
|
|
|
|
}
|
2019-02-19 22:23:30 +03:00
|
|
|
|
|
|
|
GlobNode::GlobResult entryToResult(
|
|
|
|
RelativePath&& entryPath,
|
|
|
|
const TreeEntry* entry) {
|
|
|
|
return GlobNode::GlobResult{std::move(entryPath), entry->getDType()};
|
|
|
|
}
|
|
|
|
|
|
|
|
GlobNode::GlobResult entryToResult(
|
|
|
|
RelativePath&& entryPath,
|
|
|
|
const TreeEntry& entry) {
|
|
|
|
return this->entryToResult(std::move(entryPath), &entry);
|
|
|
|
}
|
2018-05-25 23:47:46 +03:00
|
|
|
};
|
|
|
|
} // namespace
|
|
|
|
|
2018-05-03 00:41:32 +03:00
|
|
|
GlobNode::GlobNode(StringPiece pattern, bool includeDotfiles, bool hasSpecials)
|
|
|
|
: pattern_(pattern.str()),
|
|
|
|
includeDotfiles_(includeDotfiles),
|
|
|
|
hasSpecials_(hasSpecials) {
|
|
|
|
if (includeDotfiles && (pattern == "**" || pattern == "*")) {
|
2017-01-26 23:45:50 +03:00
|
|
|
alwaysMatch_ = true;
|
|
|
|
} else {
|
2018-05-03 00:41:32 +03:00
|
|
|
auto options =
|
|
|
|
includeDotfiles ? GlobOptions::DEFAULT : GlobOptions::IGNORE_DOTFILES;
|
|
|
|
auto compiled = GlobMatcher::create(pattern, options);
|
2017-01-26 23:45:50 +03:00
|
|
|
if (compiled.hasError()) {
|
2018-07-13 21:14:54 +03:00
|
|
|
throw std::system_error(
|
2017-01-26 23:45:50 +03:00
|
|
|
EINVAL,
|
2018-07-13 21:14:54 +03:00
|
|
|
std::generic_category(),
|
|
|
|
folly::sformat(
|
|
|
|
"failed to compile pattern `{}` to GlobMatcher: {}",
|
|
|
|
pattern,
|
|
|
|
compiled.error()));
|
2017-01-26 23:45:50 +03:00
|
|
|
}
|
|
|
|
matcher_ = std::move(compiled.value());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void GlobNode::parse(StringPiece pattern) {
|
|
|
|
GlobNode* parent = this;
|
2018-05-03 00:41:32 +03:00
|
|
|
string normalizedPattern;
|
2017-01-26 23:45:50 +03:00
|
|
|
|
|
|
|
while (!pattern.empty()) {
|
|
|
|
StringPiece token;
|
|
|
|
auto* container = &parent->children_;
|
|
|
|
bool hasSpecials;
|
|
|
|
|
|
|
|
if (pattern.startsWith("**")) {
|
|
|
|
// Recursive match defeats most optimizations; we have to stop
|
|
|
|
// tokenizing here.
|
2018-05-03 00:41:32 +03:00
|
|
|
|
|
|
|
// HACK: We special-case "**" if includeDotfiles=false. In this case, we
|
|
|
|
// need to create a GlobMatcher for this pattern, but GlobMatcher is
|
|
|
|
// designed to reject "**". As a workaround, we use "**/*", which is
|
|
|
|
// functionally equivalent in this case because there are no other
|
|
|
|
// "tokens" in the pattern following the "**" at this point.
|
|
|
|
if (pattern == "**" && !includeDotfiles_) {
|
|
|
|
normalizedPattern = "**/*";
|
|
|
|
token = normalizedPattern;
|
|
|
|
} else {
|
|
|
|
token = pattern;
|
|
|
|
}
|
|
|
|
|
2017-01-26 23:45:50 +03:00
|
|
|
pattern = StringPiece();
|
|
|
|
container = &parent->recursiveChildren_;
|
|
|
|
hasSpecials = true;
|
|
|
|
} else {
|
|
|
|
token = tokenize(pattern, &hasSpecials);
|
|
|
|
}
|
|
|
|
|
|
|
|
auto node = lookupToken(container, token);
|
|
|
|
if (!node) {
|
2018-05-03 00:41:32 +03:00
|
|
|
container->emplace_back(
|
|
|
|
std::make_unique<GlobNode>(token, includeDotfiles_, hasSpecials));
|
2017-01-26 23:45:50 +03:00
|
|
|
node = container->back().get();
|
|
|
|
}
|
|
|
|
|
|
|
|
// If there are no more tokens remaining then we have a leaf node
|
|
|
|
// that will emit results. Update the node to reflect this.
|
|
|
|
// Note that this may convert a pre-existing node from an earlier
|
|
|
|
// glob specification to a leaf node.
|
|
|
|
if (pattern.empty()) {
|
|
|
|
node->isLeaf_ = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Continue parsing the remainder of the pattern using this
|
|
|
|
// (possibly new) node as the parent.
|
|
|
|
parent = node;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-05-25 23:47:46 +03:00
|
|
|
template <typename ROOT>
|
2019-02-19 22:23:30 +03:00
|
|
|
Future<vector<GlobNode::GlobResult>> GlobNode::evaluateImpl(
|
2018-05-25 23:47:46 +03:00
|
|
|
const ObjectStore* store,
|
2017-01-26 23:45:50 +03:00
|
|
|
RelativePathPiece rootPath,
|
2018-05-25 23:47:46 +03:00
|
|
|
ROOT&& root,
|
2018-05-25 23:47:49 +03:00
|
|
|
GlobNode::PrefetchList fileBlobsToPrefetch) {
|
2019-02-19 22:23:30 +03:00
|
|
|
vector<GlobResult> results;
|
2018-05-25 23:47:46 +03:00
|
|
|
vector<std::pair<PathComponentPiece, GlobNode*>> recurse;
|
2019-02-19 22:23:30 +03:00
|
|
|
vector<Future<vector<GlobResult>>> futures;
|
2018-05-25 23:47:49 +03:00
|
|
|
futures.emplace_back(evaluateRecursiveComponentImpl(
|
|
|
|
store, rootPath, root, fileBlobsToPrefetch));
|
2017-01-26 23:45:50 +03:00
|
|
|
|
|
|
|
{
|
2018-05-25 23:47:46 +03:00
|
|
|
auto contents = root.lockContents();
|
2017-01-26 23:45:50 +03:00
|
|
|
for (auto& node : children_) {
|
|
|
|
if (!node->hasSpecials_) {
|
|
|
|
// We can try a lookup for the exact name
|
2018-05-25 23:47:46 +03:00
|
|
|
auto name = PathComponentPiece(node->pattern_);
|
|
|
|
auto entry = root.lookupEntry(contents, name);
|
|
|
|
if (entry) {
|
2017-01-26 23:45:50 +03:00
|
|
|
// Matched!
|
|
|
|
if (node->isLeaf_) {
|
2019-02-19 22:23:30 +03:00
|
|
|
results.emplace_back(root.entryToResult(rootPath + name, entry));
|
|
|
|
|
2018-07-16 21:21:41 +03:00
|
|
|
if (fileBlobsToPrefetch && root.entryShouldPrefetch(entry)) {
|
|
|
|
fileBlobsToPrefetch->wlock()->emplace_back(root.entryHash(entry));
|
|
|
|
}
|
2017-01-26 23:45:50 +03:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Not the leaf of a pattern; if this is a dir, we need to recurse
|
2018-05-25 23:47:46 +03:00
|
|
|
if (root.entryIsTree(entry)) {
|
|
|
|
if (root.entryShouldLoadChildTree(entry)) {
|
|
|
|
recurse.emplace_back(std::make_pair(name, node.get()));
|
|
|
|
} else {
|
|
|
|
auto candidateName = rootPath + name;
|
|
|
|
futures.emplace_back(
|
|
|
|
store->getTree(root.entryHash(entry))
|
2018-09-15 02:57:46 +03:00
|
|
|
.thenValue([candidateName,
|
|
|
|
store,
|
|
|
|
innerNode = node.get(),
|
|
|
|
fileBlobsToPrefetch](
|
|
|
|
std::shared_ptr<const Tree> dir) {
|
2018-05-25 23:47:46 +03:00
|
|
|
return innerNode->evaluateImpl(
|
2018-05-25 23:47:49 +03:00
|
|
|
store,
|
|
|
|
candidateName,
|
|
|
|
TreeRoot(dir),
|
|
|
|
fileBlobsToPrefetch);
|
2018-05-25 23:47:46 +03:00
|
|
|
}));
|
|
|
|
}
|
2017-01-26 23:45:50 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// We need to match it out of the entries in this inode
|
2018-05-25 23:47:46 +03:00
|
|
|
for (auto& entry : root.iterate(contents)) {
|
|
|
|
auto name = root.entryName(entry);
|
|
|
|
if (node->alwaysMatch_ || node->matcher_.match(name.stringPiece())) {
|
2017-01-26 23:45:50 +03:00
|
|
|
if (node->isLeaf_) {
|
2019-02-19 22:23:30 +03:00
|
|
|
results.emplace_back(root.entryToResult(rootPath + name, entry));
|
2018-07-16 21:21:41 +03:00
|
|
|
if (fileBlobsToPrefetch && root.entryShouldPrefetch(entry)) {
|
|
|
|
fileBlobsToPrefetch->wlock()->emplace_back(
|
|
|
|
root.entryHash(entry));
|
|
|
|
}
|
2019-02-19 22:23:30 +03:00
|
|
|
|
2017-01-26 23:45:50 +03:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
// Not the leaf of a pattern; if this is a dir, we need to
|
|
|
|
// recurse
|
2018-05-25 23:47:46 +03:00
|
|
|
if (root.entryIsTree(entry)) {
|
|
|
|
if (root.entryShouldLoadChildTree(entry)) {
|
|
|
|
recurse.emplace_back(std::make_pair(name, node.get()));
|
|
|
|
} else {
|
|
|
|
auto candidateName = rootPath + name;
|
|
|
|
futures.emplace_back(
|
|
|
|
store->getTree(root.entryHash(entry))
|
2018-09-15 02:57:46 +03:00
|
|
|
.thenValue([candidateName,
|
|
|
|
store,
|
|
|
|
innerNode = node.get(),
|
|
|
|
fileBlobsToPrefetch](
|
|
|
|
std::shared_ptr<const Tree> dir) {
|
2018-05-25 23:47:46 +03:00
|
|
|
return innerNode->evaluateImpl(
|
|
|
|
store,
|
|
|
|
candidateName,
|
|
|
|
TreeRoot(dir),
|
2018-05-25 23:47:49 +03:00
|
|
|
fileBlobsToPrefetch);
|
2018-05-25 23:47:46 +03:00
|
|
|
}));
|
|
|
|
}
|
2017-01-26 23:45:50 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-05-18 22:51:33 +03:00
|
|
|
// Recursively load child inodes and evaluate matches
|
|
|
|
|
|
|
|
for (auto& item : recurse) {
|
|
|
|
auto candidateName = rootPath + item.first;
|
2018-05-25 23:47:49 +03:00
|
|
|
futures.emplace_back(root.getOrLoadChildTree(item.first)
|
2018-10-23 23:39:59 +03:00
|
|
|
.thenValue([store,
|
|
|
|
candidateName,
|
|
|
|
node = item.second,
|
|
|
|
fileBlobsToPrefetch](
|
|
|
|
TreeInodePtr dir) {
|
2018-05-25 23:47:49 +03:00
|
|
|
return node->evaluateImpl(
|
|
|
|
store,
|
|
|
|
candidateName,
|
|
|
|
TreeInodePtrRoot(dir),
|
|
|
|
fileBlobsToPrefetch);
|
|
|
|
}));
|
2017-05-18 22:51:33 +03:00
|
|
|
}
|
2018-10-23 23:39:59 +03:00
|
|
|
return folly::collect(futures).thenValue(
|
2018-05-25 23:47:49 +03:00
|
|
|
[shadowResults = std::move(results)](
|
2019-02-19 22:23:30 +03:00
|
|
|
vector<vector<GlobNode::GlobResult>>&& matchVector) mutable {
|
2018-05-25 23:47:49 +03:00
|
|
|
for (auto& matches : matchVector) {
|
2018-06-12 22:42:03 +03:00
|
|
|
shadowResults.insert(
|
|
|
|
shadowResults.end(),
|
|
|
|
std::make_move_iterator(matches.begin()),
|
|
|
|
std::make_move_iterator(matches.end()));
|
2018-05-25 23:47:49 +03:00
|
|
|
}
|
|
|
|
return shadowResults;
|
2017-11-04 01:58:04 +03:00
|
|
|
});
|
2017-01-26 23:45:50 +03:00
|
|
|
}
|
|
|
|
|
2019-02-19 22:23:30 +03:00
|
|
|
Future<vector<GlobNode::GlobResult>> GlobNode::evaluate(
|
2018-05-25 23:47:46 +03:00
|
|
|
const ObjectStore* store,
|
|
|
|
RelativePathPiece rootPath,
|
|
|
|
TreeInodePtr root,
|
2018-05-25 23:47:49 +03:00
|
|
|
GlobNode::PrefetchList fileBlobsToPrefetch) {
|
|
|
|
return evaluateImpl(
|
|
|
|
store, rootPath, TreeInodePtrRoot(root), fileBlobsToPrefetch);
|
2018-05-25 23:47:46 +03:00
|
|
|
}
|
|
|
|
|
2019-02-19 22:23:30 +03:00
|
|
|
folly::Future<vector<GlobNode::GlobResult>> GlobNode::evaluate(
|
2018-05-25 23:47:46 +03:00
|
|
|
const ObjectStore* store,
|
|
|
|
RelativePathPiece rootPath,
|
|
|
|
const std::shared_ptr<const Tree>& tree,
|
2018-05-25 23:47:49 +03:00
|
|
|
GlobNode::PrefetchList fileBlobsToPrefetch) {
|
|
|
|
return evaluateImpl(store, rootPath, TreeRoot(tree), fileBlobsToPrefetch);
|
2018-05-25 23:47:46 +03:00
|
|
|
}
|
|
|
|
|
2017-01-26 23:45:50 +03:00
|
|
|
StringPiece GlobNode::tokenize(StringPiece& pattern, bool* hasSpecials) {
|
|
|
|
*hasSpecials = false;
|
|
|
|
|
|
|
|
for (auto it = pattern.begin(); it != pattern.end(); ++it) {
|
|
|
|
switch (*it) {
|
|
|
|
case '*':
|
|
|
|
case '?':
|
|
|
|
case '[':
|
|
|
|
case '\\':
|
|
|
|
*hasSpecials = true;
|
|
|
|
break;
|
|
|
|
case '/':
|
|
|
|
// token is the input up-to-but-not-including the current position,
|
|
|
|
// which is a '/' character
|
|
|
|
StringPiece token(pattern.begin(), it);
|
|
|
|
// update the pattern to be the text after the slash
|
|
|
|
pattern = StringPiece(it + 1, pattern.end());
|
|
|
|
return token;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// No slash found, so the the rest of the pattern is the token
|
|
|
|
StringPiece token = pattern;
|
|
|
|
pattern = StringPiece();
|
|
|
|
return token;
|
|
|
|
}
|
|
|
|
|
|
|
|
GlobNode* GlobNode::lookupToken(
|
|
|
|
vector<unique_ptr<GlobNode>>* container,
|
|
|
|
StringPiece token) {
|
|
|
|
for (auto& child : *container) {
|
|
|
|
if (child->pattern_ == token) {
|
|
|
|
return child.get();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2018-05-25 23:47:46 +03:00
|
|
|
template <typename ROOT>
|
2019-02-19 22:23:30 +03:00
|
|
|
Future<vector<GlobNode::GlobResult>> GlobNode::evaluateRecursiveComponentImpl(
|
2018-05-25 23:47:46 +03:00
|
|
|
const ObjectStore* store,
|
2017-01-26 23:45:50 +03:00
|
|
|
RelativePathPiece rootPath,
|
2018-05-25 23:47:46 +03:00
|
|
|
ROOT&& root,
|
2018-05-25 23:47:49 +03:00
|
|
|
GlobNode::PrefetchList fileBlobsToPrefetch) {
|
2019-02-19 22:23:30 +03:00
|
|
|
vector<GlobResult> results;
|
2017-01-26 23:45:50 +03:00
|
|
|
if (recursiveChildren_.empty()) {
|
|
|
|
return results;
|
|
|
|
}
|
|
|
|
|
|
|
|
vector<RelativePath> subDirNames;
|
2019-02-19 22:23:30 +03:00
|
|
|
vector<Future<vector<GlobResult>>> futures;
|
2017-01-26 23:45:50 +03:00
|
|
|
{
|
2018-05-25 23:47:46 +03:00
|
|
|
auto contents = root.lockContents();
|
|
|
|
for (auto& entry : root.iterate(contents)) {
|
|
|
|
auto candidateName = rootPath + root.entryName(entry);
|
2017-01-26 23:45:50 +03:00
|
|
|
|
|
|
|
for (auto& node : recursiveChildren_) {
|
|
|
|
if (node->alwaysMatch_ ||
|
|
|
|
node->matcher_.match(candidateName.stringPiece())) {
|
2019-02-19 22:23:30 +03:00
|
|
|
results.emplace_back(root.entryToResult(candidateName.copy(), entry));
|
2018-07-16 21:21:41 +03:00
|
|
|
if (fileBlobsToPrefetch && root.entryShouldPrefetch(entry)) {
|
|
|
|
fileBlobsToPrefetch->wlock()->emplace_back(root.entryHash(entry));
|
|
|
|
}
|
2017-01-26 23:45:50 +03:00
|
|
|
// No sense running multiple matches for this same file.
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Remember to recurse through child dirs after we've released
|
|
|
|
// the lock on the contents.
|
2018-05-25 23:47:46 +03:00
|
|
|
if (root.entryIsTree(entry)) {
|
|
|
|
if (root.entryShouldLoadChildTree(entry)) {
|
|
|
|
subDirNames.emplace_back(candidateName);
|
|
|
|
} else {
|
|
|
|
futures.emplace_back(
|
|
|
|
store->getTree(root.entryHash(entry))
|
2018-09-15 02:57:46 +03:00
|
|
|
.thenValue([candidateName, store, this, fileBlobsToPrefetch](
|
|
|
|
const std::shared_ptr<const Tree>& tree) {
|
2018-05-25 23:47:46 +03:00
|
|
|
return evaluateRecursiveComponentImpl(
|
2018-05-25 23:47:49 +03:00
|
|
|
store,
|
|
|
|
candidateName,
|
|
|
|
TreeRoot(tree),
|
|
|
|
fileBlobsToPrefetch);
|
2018-05-25 23:47:46 +03:00
|
|
|
}));
|
|
|
|
}
|
2017-01-26 23:45:50 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-05-18 22:51:33 +03:00
|
|
|
// Recursively load child inodes and evaluate matches
|
|
|
|
for (auto& candidateName : subDirNames) {
|
2018-05-25 23:47:46 +03:00
|
|
|
futures.emplace_back(
|
|
|
|
root.getOrLoadChildTree(candidateName.basename())
|
2018-10-23 23:39:59 +03:00
|
|
|
.thenValue([candidateName, store, this, fileBlobsToPrefetch](
|
|
|
|
TreeInodePtr dir) {
|
2018-05-25 23:47:46 +03:00
|
|
|
return evaluateRecursiveComponentImpl(
|
2018-05-25 23:47:49 +03:00
|
|
|
store,
|
|
|
|
candidateName,
|
|
|
|
TreeInodePtrRoot(dir),
|
|
|
|
fileBlobsToPrefetch);
|
2018-05-25 23:47:46 +03:00
|
|
|
}));
|
2017-05-18 22:51:33 +03:00
|
|
|
}
|
|
|
|
|
2018-10-23 23:39:59 +03:00
|
|
|
return folly::collect(futures).thenValue(
|
2018-05-25 23:47:49 +03:00
|
|
|
[shadowResults = std::move(results)](
|
2019-02-19 22:23:30 +03:00
|
|
|
vector<vector<GlobResult>>&& matchVector) mutable {
|
2018-05-25 23:47:49 +03:00
|
|
|
for (auto& matches : matchVector) {
|
2018-06-12 22:42:03 +03:00
|
|
|
shadowResults.insert(
|
|
|
|
shadowResults.end(),
|
|
|
|
std::make_move_iterator(matches.begin()),
|
|
|
|
std::make_move_iterator(matches.end()));
|
2018-05-25 23:47:49 +03:00
|
|
|
}
|
|
|
|
return shadowResults;
|
2017-11-04 01:58:04 +03:00
|
|
|
});
|
2017-01-26 23:45:50 +03:00
|
|
|
}
|
2018-05-25 23:47:46 +03:00
|
|
|
|
2017-11-04 01:58:04 +03:00
|
|
|
} // namespace eden
|
|
|
|
} // namespace facebook
|