compute permission bits based on FileType in TreeEntry

Summary:
Our Model TreeEntry code was a bit too general - in reality, both git
and hg only support a handful of specific tree entries: regular files,
executable files, symlinks, and trees.  (git also supports
submodules.)  This diff delays the expansion of a TreeEntry's type
into a full mode_t.

Reviewed By: simpkins

Differential Revision: D6980003

fbshipit-source-id: 73729208000668078a180b728d7e0bb9169c6f3c
This commit is contained in:
Chad Austin 2018-02-15 14:23:07 -08:00 committed by Facebook Github Bot
parent 9dbdeaa4f4
commit 895a0196d9
23 changed files with 197 additions and 287 deletions

View File

@ -67,6 +67,10 @@ inline void PrintTo(
namespace {
bool isExecutable(int perms) {
return perms & S_IXUSR;
}
/**
* An enum to control behavior for many of the checkout tests.
*
@ -230,7 +234,8 @@ void testAddFile(
// Prepare a second tree, by starting with builder1 then adding the new file
auto builder2 = builder1.clone();
builder2.setFile(newFilePath, "this is the new file contents\n", perms);
builder2.setFile(
newFilePath, "this is the new file contents\n", isExecutable(perms));
builder2.finalize(testMount.getBackingStore(), true);
auto commit2 = testMount.getBackingStore()->putCommit("2", builder2);
commit2->setReady();
@ -258,7 +263,6 @@ void runAddFileTests(folly::StringPiece path) {
for (auto loadType : kAddLoadTypes) {
SCOPED_TRACE(folly::to<string>("add ", path, " load type ", loadType));
testAddFile(path, loadType);
testAddFile(path, loadType, 0444);
testAddFile(path, loadType, 0755);
}
}
@ -332,13 +336,13 @@ void testModifyFile(
builder1.setFile("a/test.txt", "test contents\n");
builder1.setFile("a/b/dddd.c", "this is dddd.c\n");
builder1.setFile("a/b/tttt.c", "this is tttt.c\n");
builder1.setFile(path, contents1, perms1);
builder1.setFile(path, contents1, isExecutable(perms1));
TestMount testMount{builder1};
testMount.getClock().advance(9876min);
// Prepare the second tree
auto builder2 = builder1.clone();
builder2.replaceFile(path, contents2, perms2);
builder2.replaceFile(path, contents2, isExecutable(perms2));
builder2.finalize(testMount.getBackingStore(), true);
auto commit2 = testMount.getBackingStore()->putCommit("2", builder2);
commit2->setReady();
@ -450,12 +454,12 @@ void testModifyConflict(
workingDirBuilder.setFile("a/test.txt", "test contents\n");
workingDirBuilder.setFile("a/b/dddd.c", "this is dddd.c\n");
workingDirBuilder.setFile("a/b/tttt.c", "this is tttt.c\n");
workingDirBuilder.setFile(path, currentContents, currentPerms);
workingDirBuilder.setFile(path, currentContents, isExecutable(currentPerms));
TestMount testMount{workingDirBuilder};
// Prepare the "before" tree
auto builder1 = workingDirBuilder.clone();
builder1.replaceFile(path, contents1, perms1);
builder1.replaceFile(path, contents1, isExecutable(perms1));
builder1.finalize(testMount.getBackingStore(), true);
// Reset the EdenMount to point at the tree from builder1, even though the
// contents are still from workingDirBuilder. This lets us trigger the
@ -471,7 +475,7 @@ void testModifyConflict(
// Prepare the destination tree
auto builder2 = builder1.clone();
builder2.replaceFile(path, contents2, perms2);
builder2.replaceFile(path, contents2, isExecutable(perms2));
builder2.replaceFile("a/b/dddd.c", "new dddd contents\n");
builder2.finalize(testMount.getBackingStore(), true);
auto commit2 = testMount.getBackingStore()->putCommit("b", builder2);

View File

@ -483,7 +483,7 @@ void testResetFileModeChanged(bool loadInodes) {
DiffTest t;
auto b2 = t.getBuilder().clone();
b2.replaceFile("src/1.txt", "This is src/1.txt.\n", 0755);
b2.replaceFile("src/1.txt", "This is src/1.txt.\n", true);
auto result = t.resetCommitAndDiff(b2, loadInodes);
EXPECT_THAT(result.getErrors(), UnorderedElementsAre());

View File

@ -74,7 +74,7 @@ TEST(InodeMap, simpleLookups) {
TEST(InodeMap, asyncLookup) {
auto builder = FakeTreeBuilder();
builder.setFile("README", "docs go here\n");
builder.setFile("src/runme.sh", "#!/bin/sh\necho hello world\n", 0755);
builder.setFile("src/runme.sh", "#!/bin/sh\necho hello world\n", true);
builder.setFile("src/test.txt", "this is a test file");
TestMount testMount{builder, false};
@ -100,7 +100,7 @@ TEST(InodeMap, asyncLookup) {
TEST(InodeMap, asyncError) {
auto builder = FakeTreeBuilder();
builder.setFile("README", "docs go here\n");
builder.setFile("src/runme.sh", "#!/bin/sh\necho hello world\n", 0755);
builder.setFile("src/runme.sh", "#!/bin/sh\necho hello world\n", true);
builder.setFile("src/test.txt", "this is a test file");
TestMount testMount{builder, false};

View File

@ -26,44 +26,47 @@ std::string TreeEntry::toLogString() const {
case FileType::REGULAR_FILE:
fileTypeChar = 'f';
break;
case FileType::EXECUTABLE_FILE:
fileTypeChar = 'x';
break;
case FileType::SYMLINK:
fileTypeChar = 'l';
break;
}
std::array<char, 4> modeStr;
modeStr[0] = fileTypeChar;
modeStr[1] = (ownerPermissions_ & 0b100) ? 'r' : '-';
modeStr[2] = (ownerPermissions_ & 0b010) ? 'w' : '-';
modeStr[3] = (ownerPermissions_ & 0b001) ? 'x' : '-';
return folly::to<std::string>(
"(",
name_,
", ",
hash_.toString(),
", ",
folly::StringPiece{modeStr},
")");
"(", name_, ", ", hash_.toString(), ", ", fileTypeChar, ")");
}
std::ostream& operator<<(std::ostream& os, FileType type) {
switch (type) {
case FileType::DIRECTORY:
return os << "DIRECTORY";
case FileType::REGULAR_FILE:
return os << "REGULAR_FILE";
case FileType::EXECUTABLE_FILE:
return os << "EXECUTABLE_FILE";
case FileType::SYMLINK:
return os << "SYMLINK";
}
return os << "FileType::" << int(type);
}
std::ostream& operator<<(std::ostream& os, TreeEntryType type) {
switch (type) {
case TreeEntryType::TREE:
os << "TREE";
return os;
return os << "TREE";
case TreeEntryType::BLOB:
os << "BLOB";
return os;
return os << "BLOB";
}
os << "TreeEntryType::" << int(type);
return os;
return os << "TreeEntryType::" << int(type);
}
bool operator==(const TreeEntry& entry1, const TreeEntry& entry2) {
return (entry1.getHash() == entry2.getHash()) &&
(entry1.getFileType() == entry2.getFileType()) &&
(entry1.getOwnerPermissions() == entry2.getOwnerPermissions()) &&
(entry1.getName() == entry2.getName());
}

View File

@ -14,6 +14,8 @@
#include "eden/fs/utils/PathFuncs.h"
#include <folly/String.h>
#include <folly/experimental/logging/xlog.h>
#include <sys/stat.h>
#include <iosfwd>
namespace facebook {
@ -21,19 +23,16 @@ namespace eden {
class Hash;
enum class TreeEntryType {
enum class TreeEntryType : uint8_t {
BLOB,
TREE,
};
enum class FileType {
// The values for these enum entries correspond to the ones we would expect to
// find in <sys/stat.h>. We hardcode them just in case the values in the
// users's <sys/stat.h> are different. Note that Git performs a similar check:
// https://github.com/git/git/blob/v2.7.4/configure.ac#L912-L917
DIRECTORY = 0040,
REGULAR_FILE = 0100,
SYMLINK = 0120,
enum class FileType : uint8_t {
DIRECTORY,
REGULAR_FILE,
EXECUTABLE_FILE,
SYMLINK,
};
class TreeEntry {
@ -41,12 +40,8 @@ class TreeEntry {
explicit TreeEntry(
const Hash& hash,
folly::StringPiece name,
FileType fileType,
uint8_t ownerPermissions)
: fileType_(fileType),
ownerPermissions_(ownerPermissions),
hash_(hash),
name_(PathComponentPiece(name)) {}
FileType fileType)
: fileType_(fileType), hash_(hash), name_(PathComponentPiece(name)) {}
const Hash& getHash() const {
return hash_;
@ -65,40 +60,29 @@ class TreeEntry {
return fileType_;
}
uint8_t getOwnerPermissions() const {
return ownerPermissions_;
}
mode_t getMode() const {
mode_t mode = static_cast<mode_t>(fileType_) << 9;
// We should always honor the explicit owner permissions.
mode |= ownerPermissions_ << 6;
// We propagate the 'r' and 'x' values for the owner to group and other.
mode |= (ownerPermissions_ & 0b101) << 3;
mode |= (ownerPermissions_ & 0b101);
return mode;
}
/**
* Extract the owner permissions from a mode_t value.
*
* This can be used to construct the ownerPermissions parameter needed for
* the TreeEntry constructor.
*/
static uint8_t modeToOwnerPermissions(mode_t mode) {
return (mode >> 6) & 0b111;
switch (fileType_) {
case FileType::DIRECTORY:
return S_IFDIR | 0755;
case FileType::REGULAR_FILE:
return S_IFREG | 0644;
case FileType::EXECUTABLE_FILE:
return S_IFREG | 0755;
case FileType::SYMLINK:
return S_IFLNK | 0755;
}
XLOG(FATAL) << "illegal file type " << int(fileType_);
}
std::string toLogString() const;
private:
FileType fileType_;
uint8_t ownerPermissions_;
Hash hash_;
PathComponent name_;
};
std::ostream& operator<<(std::ostream& os, FileType type);
std::ostream& operator<<(std::ostream& os, TreeEntryType type);
bool operator==(const TreeEntry& entry1, const TreeEntry& entry2);
bool operator!=(const TreeEntry& entry1, const TreeEntry& entry2);

View File

@ -28,9 +28,6 @@ using std::vector;
namespace facebook {
namespace eden {
const int RWX = 0b111;
const int RW_ = 0b110;
enum GitModeMask {
DIRECTORY = 040000,
GIT_LINK = 0160000,
@ -80,19 +77,14 @@ std::unique_ptr<Tree> deserializeGitTree(
// Determine the individual fields from the mode.
FileType fileType;
uint8_t ownerPermissions;
if (mode == GitModeMask::DIRECTORY) {
fileType = FileType::DIRECTORY;
ownerPermissions = RWX;
} else if (mode == GitModeMask::REGULAR_FILE) {
fileType = FileType::REGULAR_FILE;
ownerPermissions = RW_;
} else if (mode == GitModeMask::REGULAR_EXECUTABLE_FILE) {
fileType = FileType::REGULAR_FILE;
ownerPermissions = RWX;
fileType = FileType::EXECUTABLE_FILE;
} else if (mode == GitModeMask::SYMLINK) {
fileType = FileType::SYMLINK;
ownerPermissions = RWX;
} else if (mode == GitModeMask::GIT_LINK) {
throw std::domain_error(folly::sformat(
"Gitlinks are not currently supported: {:o} in object {}",
@ -105,8 +97,7 @@ std::unique_ptr<Tree> deserializeGitTree(
hash.toString()));
}
entries.emplace_back(
Hash(hashBytes), std::move(name), fileType, ownerPermissions);
entries.emplace_back(Hash(hashBytes), std::move(name), fileType);
}
return std::make_unique<Tree>(std::move(entries), hash);
@ -166,12 +157,10 @@ void GitTreeSerializer::addEntry(const TreeEntry& entry) {
// affect the final tree hash.)
mode_t mode = 0;
if (entry.getFileType() == FileType::REGULAR_FILE) {
if (entry.getOwnerPermissions() & 0001) {
mode = GitModeMask::REGULAR_EXECUTABLE_FILE;
} else {
mode = GitModeMask::REGULAR_FILE;
}
if (entry.getFileType() == FileType::EXECUTABLE_FILE) {
mode = GitModeMask::REGULAR_EXECUTABLE_FILE;
} else if (entry.getFileType() == FileType::REGULAR_FILE) {
mode = GitModeMask::REGULAR_FILE;
} else if (entry.getFileType() == FileType::DIRECTORY) {
mode = GitModeMask::DIRECTORY;
} else if (entry.getFileType() == FileType::SYMLINK) {

View File

@ -78,7 +78,6 @@ TEST(GitTree, testDeserialize) {
EXPECT_EQ(".babelrc", babelrc.getName());
EXPECT_EQ(facebook::eden::TreeEntryType::BLOB, babelrc.getType());
EXPECT_EQ(facebook::eden::FileType::REGULAR_FILE, babelrc.getFileType());
EXPECT_EQ(0b0110, babelrc.getOwnerPermissions());
EXPECT_EQ(
babelrc.getName(),
tree->getEntryAt(PathComponentPiece(".babelrc")).getName());
@ -91,8 +90,8 @@ TEST(GitTree, testDeserialize) {
EXPECT_EQ("nuclide-start-server", nuclideStartServer.getName());
EXPECT_EQ(facebook::eden::TreeEntryType::BLOB, nuclideStartServer.getType());
EXPECT_EQ(
facebook::eden::FileType::REGULAR_FILE, nuclideStartServer.getFileType());
EXPECT_EQ(0b0111, nuclideStartServer.getOwnerPermissions());
facebook::eden::FileType::EXECUTABLE_FILE,
nuclideStartServer.getFileType());
EXPECT_EQ(
nuclideStartServer.getName(),
tree->getEntryAt(PathComponentPiece("nuclide-start-server")).getName());
@ -103,7 +102,6 @@ TEST(GitTree, testDeserialize) {
EXPECT_EQ("lib", lib.getName());
EXPECT_EQ(facebook::eden::TreeEntryType::TREE, lib.getType());
EXPECT_EQ(facebook::eden::FileType::DIRECTORY, lib.getFileType());
EXPECT_EQ(0b0111, lib.getOwnerPermissions());
EXPECT_EQ(
lib.getName(), tree->getEntryAt(PathComponentPiece("lib")).getName());
@ -150,7 +148,6 @@ TEST(GitTree, testDeserializeWithSymlink) {
EXPECT_EQ("contributing.md", contributing.getName());
EXPECT_EQ(facebook::eden::TreeEntryType::BLOB, contributing.getType());
EXPECT_EQ(facebook::eden::FileType::SYMLINK, contributing.getFileType());
EXPECT_EQ(0b0111, contributing.getOwnerPermissions());
}
TEST(GitTree, deserializeEmpty) {
@ -226,40 +223,35 @@ TEST(GitTree, serializeTree) {
serializer.addEntry(TreeEntry(
Hash("c66788d87933862e2111a86304b705dd90bbd427"),
"README.md",
FileType::REGULAR_FILE,
0b110));
FileType::REGULAR_FILE));
serializer.addEntry(TreeEntry(
Hash("a3c8e5c25e5523322f0ea490173dbdc1d844aefb"),
"apm-rest-api.md",
FileType::REGULAR_FILE,
0b110));
"run-tests.sh",
FileType::EXECUTABLE_FILE));
serializer.addEntry(TreeEntry(
Hash("de0b8287939193ed239834991be65b96cbfc4508"),
"build-instructions",
FileType::DIRECTORY,
0b111));
FileType::DIRECTORY));
serializer.addEntry(TreeEntry(
Hash("4576635ff317960be244b1c4adfe2a6eb2eb024d"),
"contributing-to-packages.md",
FileType::REGULAR_FILE,
0b110));
FileType::REGULAR_FILE));
serializer.addEntry(TreeEntry(
Hash("44fcc63439371c8c829df00eec6aedbdc4d0e4cd"),
"contributing.md",
FileType::SYMLINK,
0b111));
FileType::SYMLINK));
auto buf = serializer.finalize();
// Make sure the tree hash is what we expect
auto treeHash = Hash::sha1(&buf);
EXPECT_EQ(Hash("013b7865a6da317bc8d82c7225eb93615f1b1eca"), treeHash);
EXPECT_EQ(Hash("dc2c3be02dd3753c64c8f196a33522905c88c435"), treeHash);
// Make sure we can deserialize it and get back the expected entries.
auto tree = deserializeGitTree(treeHash, &buf);
EXPECT_EQ(5, tree->getTreeEntries().size());
EXPECT_EQ("README.md", tree->getEntryAt(0).getName());
EXPECT_EQ("apm-rest-api.md", tree->getEntryAt(1).getName());
EXPECT_EQ("run-tests.sh", tree->getEntryAt(1).getName());
EXPECT_EQ("build-instructions", tree->getEntryAt(2).getName());
EXPECT_EQ("contributing-to-packages.md", tree->getEntryAt(3).getName());
EXPECT_EQ("contributing.md", tree->getEntryAt(4).getName());
@ -274,8 +266,7 @@ TEST(GitTree, moveSerializer) {
serializer1.addEntry(TreeEntry(
Hash("3b18e512dba79e4c8300dd08aeb37f8e728b8dad"),
"README.md",
FileType::REGULAR_FILE,
0b110));
FileType::REGULAR_FILE));
serializer2 = std::move(serializer1);
}
@ -283,8 +274,7 @@ TEST(GitTree, moveSerializer) {
serializer2.addEntry(TreeEntry(
Hash("43b71c903ff52b9885bd36f3866324ef60e27b9b"),
"eden",
FileType::DIRECTORY,
0b111));
FileType::DIRECTORY));
// Make sure the tree hash is what we expect
auto buf = serializer2.finalize();

View File

@ -16,53 +16,33 @@ using namespace facebook::eden;
TEST(TreeEntry, modeAndLogString) {
TreeEntry rwFile(
makeTestHash("faceb00c"), "file.txt", FileType::REGULAR_FILE, 0b110);
makeTestHash("faceb00c"), "file.txt", FileType::REGULAR_FILE);
EXPECT_EQ(S_IFREG | S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH, rwFile.getMode());
EXPECT_EQ(
"(file.txt, 00000000000000000000000000000000faceb00c, frw-)",
"(file.txt, 00000000000000000000000000000000faceb00c, f)",
rwFile.toLogString());
TreeEntry rFile(makeTestHash("7"), "file.txt", FileType::REGULAR_FILE, 0b100);
EXPECT_EQ(S_IFREG | S_IRUSR | S_IRGRP | S_IROTH, rFile.getMode());
EXPECT_EQ(
"(file.txt, 0000000000000000000000000000000000000007, fr--)",
rFile.toLogString());
TreeEntry rwxFile(
makeTestHash("789"), "file.exe", FileType::REGULAR_FILE, 0b111);
TreeEntry rwxFile(makeTestHash("789"), "file.exe", FileType::EXECUTABLE_FILE);
EXPECT_EQ(
S_IFREG | S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH,
rwxFile.getMode());
EXPECT_EQ(
"(file.exe, 0000000000000000000000000000000000000789, frwx)",
"(file.exe, 0000000000000000000000000000000000000789, x)",
rwxFile.toLogString());
TreeEntry nopermsFile(
makeTestHash("13"), "secret.txt", FileType::REGULAR_FILE, 0b000);
EXPECT_EQ(S_IFREG, nopermsFile.getMode());
EXPECT_EQ(
"(secret.txt, 0000000000000000000000000000000000000013, f---)",
nopermsFile.toLogString());
TreeEntry rwLink(makeTestHash("a"), "to-file.txt", FileType::SYMLINK, 0b110);
EXPECT_EQ(S_IFLNK | S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH, rwLink.getMode());
EXPECT_EQ(
"(to-file.txt, 000000000000000000000000000000000000000a, lrw-)",
rwLink.toLogString());
TreeEntry rwxLink(makeTestHash("b"), "to-file.exe", FileType::SYMLINK, 0b111);
TreeEntry rwxLink(makeTestHash("b"), "to-file.exe", FileType::SYMLINK);
EXPECT_EQ(
S_IFLNK | S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH,
rwxLink.getMode());
EXPECT_EQ(
"(to-file.exe, 000000000000000000000000000000000000000b, lrwx)",
"(to-file.exe, 000000000000000000000000000000000000000b, l)",
rwxLink.toLogString());
TreeEntry directory(makeTestHash("abc"), "src", FileType::DIRECTORY, 0b111);
TreeEntry directory(makeTestHash("abc"), "src", FileType::DIRECTORY);
EXPECT_EQ(
S_IFDIR | S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH,
directory.getMode());
EXPECT_EQ(
"(src, 0000000000000000000000000000000000000abc, drwx)",
"(src, 0000000000000000000000000000000000000abc, d)",
directory.toLogString());
}

View File

@ -36,9 +36,8 @@ Hash testHash(testHashHex);
} // namespace
TEST(Tree, testGetEntryPtr) {
uint8_t mode = 0b110;
vector<TreeEntry> entries;
entries.emplace_back(testHash, "a_file", FileType::REGULAR_FILE, mode);
entries.emplace_back(testHash, "a_file", FileType::REGULAR_FILE);
Tree tree(std::move(entries));
// Verify existent path.
@ -48,7 +47,6 @@ TEST(Tree, testGetEntryPtr) {
EXPECT_EQ("a_file", entry->getName());
EXPECT_EQ(TreeEntryType::BLOB, entry->getType());
EXPECT_EQ(FileType::REGULAR_FILE, entry->getFileType());
EXPECT_EQ(mode, entry->getOwnerPermissions());
// Verify non-existent path.
PathComponentPiece nonExistentPath("not_a_file");

View File

@ -98,19 +98,14 @@ unique_ptr<Tree> GitBackingStore::getTreeImpl(const Hash& id) {
auto entryMode = git_tree_entry_filemode(gitEntry);
StringPiece entryName(git_tree_entry_name(gitEntry));
FileType fileType;
uint8_t ownerPerms;
if (entryMode == GIT_FILEMODE_TREE) {
fileType = FileType::DIRECTORY;
ownerPerms = 0b111;
} else if (entryMode == GIT_FILEMODE_BLOB_EXECUTABLE) {
fileType = FileType::REGULAR_FILE;
ownerPerms = 0b111;
fileType = FileType::EXECUTABLE_FILE;
} else if (entryMode == GIT_FILEMODE_LINK) {
fileType = FileType::SYMLINK;
ownerPerms = 0b111;
} else if (entryMode == GIT_FILEMODE_BLOB) {
fileType = FileType::REGULAR_FILE;
ownerPerms = 0b110;
} else {
// TODO: We currently don't handle GIT_FILEMODE_COMMIT
throw std::runtime_error(folly::to<string>(
@ -122,7 +117,7 @@ unique_ptr<Tree> GitBackingStore::getTreeImpl(const Hash& id) {
id));
}
auto entryHash = oid2Hash(git_tree_entry_id(gitEntry));
entries.emplace_back(entryHash, entryName, fileType, ownerPerms);
entries.emplace_back(entryHash, entryName, fileType);
}
auto tree = make_unique<Tree>(std::move(entries), id);
auto hash = localStore_->putTree(tree.get());

View File

@ -620,7 +620,6 @@ std::unique_ptr<Tree> HgImporter::importTreeImpl(
StringPiece entryName(entry->filename, entry->filenamelen);
FileType fileType;
uint8_t ownerPermissions;
StringPiece entryFlag;
if (entry->flag) {
@ -635,16 +634,13 @@ std::unique_ptr<Tree> HgImporter::importTreeImpl(
if (entry->isdirectory()) {
fileType = FileType::DIRECTORY;
ownerPermissions = 0b111;
} else if (entry->flag) {
switch (*entry->flag) {
case 'x':
fileType = FileType::REGULAR_FILE;
ownerPermissions = 0b111;
fileType = FileType::EXECUTABLE_FILE;
break;
case 'l':
fileType = FileType::SYMLINK;
ownerPermissions = 0b111;
break;
default:
throw std::runtime_error(folly::to<string>(
@ -657,13 +653,12 @@ std::unique_ptr<Tree> HgImporter::importTreeImpl(
}
} else {
fileType = FileType::REGULAR_FILE;
ownerPermissions = 0b110;
}
auto proxyHash = HgProxyHash::store(
path + RelativePathPiece(entryName), entryHash, writeBatch);
entries.emplace_back(proxyHash, entryName, fileType, ownerPermissions);
entries.emplace_back(proxyHash, entryName, fileType);
iter.next();
}
@ -827,16 +822,12 @@ void HgImporter::readManifestEntry(
auto pathStr = cursor.readTerminatedString();
FileType fileType;
uint8_t ownerPermissions;
if (flag == ' ') {
fileType = FileType::REGULAR_FILE;
ownerPermissions = 0b110;
} else if (flag == 'x') {
fileType = FileType::REGULAR_FILE;
ownerPermissions = 0b111;
fileType = FileType::EXECUTABLE_FILE;
} else if (flag == 'l') {
fileType = FileType::SYMLINK;
ownerPermissions = 0b111;
} else {
throw std::runtime_error(folly::to<string>(
"unsupported file flags for ", pathStr, ": ", static_cast<int>(flag)));
@ -847,8 +838,7 @@ void HgImporter::readManifestEntry(
// Generate a blob hash from the mercurial (path, fileRev) information
auto blobHash = HgProxyHash::store(path, fileRevHash, writeBatch);
auto entry =
TreeEntry(blobHash, path.basename().value(), fileType, ownerPermissions);
auto entry = TreeEntry(blobHash, path.basename().value(), fileType);
importer.processEntry(path.dirname(), std::move(entry));
}

View File

@ -231,9 +231,7 @@ void HgManifestImporter::popCurrentDir() {
auto dirHash = back.compute(store_);
uint8_t ownerPermissions = 0111;
TreeEntry dirEntry(
dirHash, entryName.stringPiece(), FileType::DIRECTORY, ownerPermissions);
TreeEntry dirEntry{dirHash, entryName.stringPiece(), FileType::DIRECTORY};
dirStack_.back().addEntry(std::move(dirEntry));
dirStack_.back().addPartialTree(std::move(back));
}

View File

@ -94,7 +94,6 @@ void HgImportTest::importTest(bool treemanifest) {
// importer.importTree().
auto fooEntry = rootTree->getEntryAt(PathComponentPiece{"foo"});
ASSERT_EQ(FileType::DIRECTORY, fooEntry.getFileType());
EXPECT_EQ(0b111, fooEntry.getOwnerPermissions());
auto fooTree = treemanifest ? importer.importTree(fooEntry.getHash())
: localStore_.getTree(fooEntry.getHash());
ASSERT_TRUE(fooTree);
@ -111,10 +110,8 @@ void HgImportTest::importTest(bool treemanifest) {
auto barEntry = fooTree->getEntryAt(PathComponentPiece{"bar.txt"});
ASSERT_EQ(FileType::REGULAR_FILE, barEntry.getFileType());
EXPECT_EQ(0b110, barEntry.getOwnerPermissions());
auto testEntry = fooTree->getEntryAt(PathComponentPiece{"test.txt"});
ASSERT_EQ(FileType::REGULAR_FILE, testEntry.getFileType());
EXPECT_EQ(0b110, testEntry.getOwnerPermissions());
// The blobs should not have been imported yet, though
EXPECT_FALSE(localStore_.getBlob(barEntry.getHash()));
@ -123,7 +120,6 @@ void HgImportTest::importTest(bool treemanifest) {
// Get the "src" tree from the LocalStore.
auto srcEntry = rootTree->getEntryAt(PathComponentPiece{"src"});
ASSERT_EQ(FileType::DIRECTORY, srcEntry.getFileType());
EXPECT_EQ(0b111, srcEntry.getOwnerPermissions());
auto srcTree = treemanifest ? importer.importTree(srcEntry.getHash())
: localStore_.getTree(srcEntry.getHash());
ASSERT_TRUE(srcTree);
@ -138,12 +134,10 @@ void HgImportTest::importTest(bool treemanifest) {
auto somelinkEntry = srcTree->getEntryAt(PathComponentPiece{"somelink"});
ASSERT_EQ(FileType::SYMLINK, somelinkEntry.getFileType());
EXPECT_EQ(0b111, somelinkEntry.getOwnerPermissions());
// Get the "src/eden" tree from the LocalStore
auto edenEntry = srcTree->getEntryAt(PathComponentPiece{"eden"});
ASSERT_EQ(FileType::DIRECTORY, edenEntry.getFileType());
EXPECT_EQ(0b111, edenEntry.getOwnerPermissions());
auto edenTree = treemanifest ? importer.importTree(edenEntry.getHash())
: localStore_.getTree(edenEntry.getHash());
ASSERT_TRUE(edenTree);
@ -156,8 +150,7 @@ void HgImportTest::importTest(bool treemanifest) {
}
auto mainEntry = edenTree->getEntryAt(PathComponentPiece{"main.py"});
ASSERT_EQ(FileType::REGULAR_FILE, mainEntry.getFileType());
EXPECT_EQ(0b111, mainEntry.getOwnerPermissions());
ASSERT_EQ(FileType::EXECUTABLE_FILE, mainEntry.getFileType());
// Import and check the blobs
auto barBuf = importer.importFileContents(barEntry.getHash());

View File

@ -171,22 +171,18 @@ std::unique_ptr<Tree> convertBufToTree(
auto hash = Hash(i->at("hash").asString());
auto str_type = i->at("type").asString();
FileType file_type;
uint8_t owner_permissions = 0b110;
if (str_type == "File") {
file_type = FileType::REGULAR_FILE;
} else if (str_type == "Tree") {
file_type = FileType::DIRECTORY;
owner_permissions = 0b111;
} else if (str_type == "Executable") {
file_type = FileType::REGULAR_FILE;
owner_permissions = 0b111;
file_type = FileType::EXECUTABLE_FILE;
} else if (str_type == "Symlink") {
file_type = FileType::SYMLINK;
owner_permissions = 0b111;
} else {
throw std::runtime_error("unknown file type");
}
entries.push_back(TreeEntry(hash, path_elem, file_type, owner_permissions));
entries.push_back(TreeEntry(hash, path_elem, file_type));
}
return std::make_unique<Tree>(std::move(entries), id);
}

View File

@ -237,28 +237,23 @@ TEST_F(MononokeBackingStoreTest, testGetTree) {
TreeEntry(
Hash("b80de5d138758541c5f05265ad144ab9fa86d1db"),
"a",
FileType::REGULAR_FILE,
0b110),
FileType::REGULAR_FILE),
TreeEntry(
Hash("b8e02f6433738021a065f94175c7cd23db5f05be"),
"b",
FileType::REGULAR_FILE,
0b110),
FileType::REGULAR_FILE),
TreeEntry(
Hash("3333333333333333333333333333333333333333"),
"dir",
FileType::DIRECTORY,
0b111),
FileType::DIRECTORY),
TreeEntry(
Hash("4444444444444444444444444444444444444444"),
"exec",
FileType::REGULAR_FILE,
0b111),
FileType::EXECUTABLE_FILE),
TreeEntry(
Hash("5555555555555555555555555555555555555555"),
"link",
FileType::SYMLINK,
0b111),
FileType::SYMLINK),
};
Tree expected_tree(std::move(expected_entries), treehash);
@ -300,28 +295,23 @@ TEST_F(MononokeBackingStoreTest, testGetTreeForCommit) {
TreeEntry(
Hash("b80de5d138758541c5f05265ad144ab9fa86d1db"),
"a",
FileType::REGULAR_FILE,
0b110),
FileType::REGULAR_FILE),
TreeEntry(
Hash("b8e02f6433738021a065f94175c7cd23db5f05be"),
"b",
FileType::REGULAR_FILE,
0b110),
FileType::REGULAR_FILE),
TreeEntry(
Hash("3333333333333333333333333333333333333333"),
"dir",
FileType::DIRECTORY,
0b111),
FileType::DIRECTORY),
TreeEntry(
Hash("4444444444444444444444444444444444444444"),
"exec",
FileType::REGULAR_FILE,
0b111),
FileType::EXECUTABLE_FILE),
TreeEntry(
Hash("5555555555555555555555555555555555555555"),
"link",
FileType::SYMLINK,
0b111),
FileType::SYMLINK),
};
Tree expected_tree(std::move(expected_entries), treehash);

View File

@ -146,7 +146,6 @@ TEST_P(LocalStoreTest, testReadsAndWriteTree) {
EXPECT_EQ("README.md", readmeEntry.getName());
EXPECT_EQ(TreeEntryType::BLOB, readmeEntry.getType());
EXPECT_EQ(FileType::REGULAR_FILE, readmeEntry.getFileType());
EXPECT_EQ(0b0110, readmeEntry.getOwnerPermissions());
}
TEST_P(LocalStoreTest, testGetResult) {

View File

@ -137,41 +137,39 @@ std::pair<StoredBlob*, bool> FakeBackingStore::maybePutBlob(
}
}
static FileType fileTypeFromBlobType(FakeBlobType type) {
switch (type) {
case FakeBlobType::REGULAR_FILE:
return FileType::REGULAR_FILE;
case FakeBlobType::EXECUTABLE_FILE:
return FileType::EXECUTABLE_FILE;
case FakeBlobType::SYMLINK:
return FileType::SYMLINK;
}
XLOG(FATAL) << "Unknown fake blob type " << static_cast<int>(type);
}
FakeBackingStore::TreeEntryData::TreeEntryData(
folly::StringPiece name,
const Blob& blob,
mode_t mode)
: entry{blob.getHash(),
name,
FileType::REGULAR_FILE,
TreeEntry::modeToOwnerPermissions(mode)} {}
FakeBlobType type)
: entry{blob.getHash(), name, fileTypeFromBlobType(type)} {}
FakeBackingStore::TreeEntryData::TreeEntryData(
folly::StringPiece name,
const StoredBlob* blob,
mode_t mode)
: entry{blob->get().getHash(),
name,
FileType::REGULAR_FILE,
TreeEntry::modeToOwnerPermissions(mode)} {}
FakeBlobType type)
: entry{blob->get().getHash(), name, fileTypeFromBlobType(type)} {}
FakeBackingStore::TreeEntryData::TreeEntryData(
folly::StringPiece name,
const Tree& tree,
mode_t mode)
: entry{tree.getHash(),
name,
FileType::DIRECTORY,
TreeEntry::modeToOwnerPermissions(mode)} {}
const Tree& tree)
: entry{tree.getHash(), name, FileType::DIRECTORY} {}
FakeBackingStore::TreeEntryData::TreeEntryData(
folly::StringPiece name,
const StoredTree* tree,
mode_t mode)
: entry{tree->get().getHash(),
name,
FileType::DIRECTORY,
TreeEntry::modeToOwnerPermissions(mode)} {}
const StoredTree* tree)
: entry{tree->get().getHash(), name, FileType::DIRECTORY} {}
StoredTree* FakeBackingStore::putTree(
const std::initializer_list<TreeEntryData>& entryArgs) {

View File

@ -144,6 +144,12 @@ class FakeBackingStore : public BackingStore {
folly::Synchronized<Data> data_;
};
enum class FakeBlobType {
REGULAR_FILE,
EXECUTABLE_FILE,
SYMLINK,
};
/**
* A small helper struct for use with FakeBackingStore::putTree()
*
@ -151,16 +157,18 @@ class FakeBackingStore : public BackingStore {
* initialier-list arguments.
*/
struct FakeBackingStore::TreeEntryData {
TreeEntryData(folly::StringPiece name, const Blob& blob, mode_t mode = 0644);
TreeEntryData(
folly::StringPiece name,
const Blob& blob,
FakeBlobType type = FakeBlobType::REGULAR_FILE);
TreeEntryData(
folly::StringPiece name,
const StoredBlob* blob,
mode_t mode = 0644);
TreeEntryData(folly::StringPiece name, const Tree& tree, mode_t mode = 0755);
TreeEntryData(
folly::StringPiece name,
const StoredTree* tree,
mode_t mode = 0755);
FakeBlobType type = FakeBlobType::REGULAR_FILE);
// tree
TreeEntryData(folly::StringPiece name, const Tree& tree);
// tree
TreeEntryData(folly::StringPiece name, const StoredTree* tree);
TreeEntry entry;
};

View File

@ -45,8 +45,7 @@ void FakeTreeBuilder::setFiles(
arg.path,
folly::ByteRange{folly::StringPiece{arg.contents}},
false,
FileType::REGULAR_FILE,
arg.permissions);
arg.executable ? FileType::EXECUTABLE_FILE : FileType::REGULAR_FILE);
}
}
@ -60,14 +59,13 @@ void FakeTreeBuilder::setFileImpl(
RelativePathPiece path,
folly::ByteRange contents,
bool replace,
FileType type,
int permissions) {
FileType type) {
CHECK(!finalizedRoot_);
auto dir = getDirEntry(path.dirname(), true);
auto name = path.basename();
auto info = EntryInfo{type, TreeEntry::modeToOwnerPermissions(permissions)};
auto info = EntryInfo{type};
info.contents = folly::StringPiece{contents}.str();
if (replace) {
@ -199,8 +197,7 @@ FakeTreeBuilder::EntryInfo* FakeTreeBuilder::getDirEntry(
throw std::runtime_error(folly::to<string>(
"tried to look up non-existent directory ", path));
}
auto ret =
parent->entries->emplace(name, EntryInfo{FileType::DIRECTORY, 0b111});
auto ret = parent->entries->emplace(name, EntryInfo{FileType::DIRECTORY});
CHECK(ret.second);
parent = &ret.first->second;
} else {
@ -240,17 +237,14 @@ StoredTree* FakeTreeBuilder::getStoredTree(RelativePathPiece path) {
return current;
}
FakeTreeBuilder::EntryInfo::EntryInfo(FileType fileType, uint8_t perms)
: type(fileType), ownerPermissions(perms) {
FakeTreeBuilder::EntryInfo::EntryInfo(FileType fileType) : type(fileType) {
if (type == FileType::DIRECTORY) {
entries = make_unique<PathMap<EntryInfo>>();
}
}
FakeTreeBuilder::EntryInfo::EntryInfo(ExplicitClone, const EntryInfo& orig)
: type(orig.type),
ownerPermissions(orig.ownerPermissions),
contents(orig.contents) {
: type(orig.type), contents(orig.contents) {
if (orig.entries) {
entries = make_unique<PathMap<EntryInfo>>();
for (const auto& e : *orig.entries) {
@ -276,11 +270,7 @@ StoredTree* FakeTreeBuilder::EntryInfo::finalizeTree(
auto* storedBlob = entryInfo.finalizeBlob(builder, setReady);
hash = storedBlob->get().getHash();
}
treeEntries.emplace_back(
hash,
e.first.stringPiece(),
entryInfo.type,
entryInfo.ownerPermissions);
treeEntries.emplace_back(hash, e.first.stringPiece(), entryInfo.type);
}
auto* storedTree = builder->store_->maybePutTree(treeEntries).first;

View File

@ -69,26 +69,30 @@ class FakeTreeBuilder {
void setFile(
folly::StringPiece path,
folly::StringPiece contents,
int permissions = 0644) {
setFile(RelativePathPiece{path}, folly::ByteRange{contents}, permissions);
bool executable = false) {
setFile(RelativePathPiece{path}, folly::ByteRange{contents}, executable);
}
void setFile(
folly::StringPiece path,
folly::ByteRange contents,
int permissions = 0644) {
setFile(RelativePathPiece{path}, contents, permissions);
bool executable = false) {
setFile(RelativePathPiece{path}, contents, executable);
}
void setFile(
RelativePathPiece path,
folly::StringPiece contents,
int permissions = 0644) {
setFile(path, folly::ByteRange{contents}, permissions);
bool executable = false) {
setFile(path, folly::ByteRange{contents}, executable);
}
void setFile(
RelativePathPiece path,
folly::ByteRange contents,
int permissions = 0644) {
setFileImpl(path, contents, false, FileType::REGULAR_FILE, permissions);
bool executable = false) {
setFileImpl(
path,
contents,
false,
executable ? FileType::EXECUTABLE_FILE : FileType::REGULAR_FILE);
}
void setFiles(const std::initializer_list<FileInfo>& fileArgs);
@ -99,27 +103,31 @@ class FakeTreeBuilder {
void replaceFile(
folly::StringPiece path,
folly::StringPiece contents,
int permissions = 0644) {
bool executable = false) {
replaceFile(
RelativePathPiece{path}, folly::ByteRange{contents}, permissions);
RelativePathPiece{path}, folly::ByteRange{contents}, executable);
}
void replaceFile(
folly::StringPiece path,
folly::ByteRange contents,
int permissions = 0644) {
replaceFile(RelativePathPiece{path}, contents, permissions);
bool executable = false) {
replaceFile(RelativePathPiece{path}, contents, executable);
}
void replaceFile(
RelativePathPiece path,
folly::StringPiece contents,
int permissions = 0644) {
replaceFile(path, folly::ByteRange{contents}, permissions);
bool executable = false) {
replaceFile(path, folly::ByteRange{contents}, executable);
}
void replaceFile(
RelativePathPiece path,
folly::ByteRange contents,
int permissions = 0644) {
setFileImpl(path, contents, true, FileType::REGULAR_FILE, permissions);
bool executable = false) {
setFileImpl(
path,
contents,
true,
executable ? FileType::EXECUTABLE_FILE : FileType::REGULAR_FILE);
}
/**
@ -129,7 +137,7 @@ class FakeTreeBuilder {
setSymlink(RelativePathPiece{path}, contents);
}
void setSymlink(RelativePathPiece path, folly::StringPiece contents) {
setFileImpl(path, contents, false, FileType::SYMLINK, 0644);
setFileImpl(path, contents, false, FileType::SYMLINK);
}
/**
@ -139,7 +147,7 @@ class FakeTreeBuilder {
replaceSymlink(RelativePathPiece{path}, contents);
}
void replaceSymlink(RelativePathPiece path, folly::StringPiece contents) {
setFileImpl(path, contents, true, FileType::SYMLINK, 0644);
setFileImpl(path, contents, true, FileType::SYMLINK);
}
/**
@ -225,7 +233,7 @@ class FakeTreeBuilder {
FakeTreeBuilder(ExplicitClone, const FakeTreeBuilder* orig);
struct EntryInfo {
EntryInfo(FileType fileType, uint8_t perms);
explicit EntryInfo(FileType fileType);
EntryInfo(EntryInfo&& other) = default;
EntryInfo& operator=(EntryInfo&& other) = default;
@ -239,7 +247,6 @@ class FakeTreeBuilder {
StoredBlob* finalizeBlob(FakeTreeBuilder* builder, bool setReady) const;
FileType type;
uint8_t ownerPermissions;
std::unique_ptr<PathMap<EntryInfo>> entries;
std::string contents;
};
@ -251,24 +258,23 @@ class FakeTreeBuilder {
RelativePathPiece path,
folly::ByteRange contents,
bool replace,
FileType type,
int permissions);
FileType type);
EntryInfo* getEntry(RelativePathPiece path);
EntryInfo* getDirEntry(RelativePathPiece path, bool create);
StoredTree* getStoredTree(RelativePathPiece path);
std::shared_ptr<FakeBackingStore> store_{nullptr};
EntryInfo root_{FileType::DIRECTORY, 0b111};
EntryInfo root_{FileType::DIRECTORY};
StoredTree* finalizedRoot_{nullptr};
};
struct FakeTreeBuilder::FileInfo {
RelativePath path;
std::string contents;
int permissions = 0644;
bool executable;
FileInfo(folly::StringPiece p, folly::StringPiece c, int perms = 0644)
: path(p), contents(c.str()), permissions(perms) {}
FileInfo(folly::StringPiece p, folly::StringPiece c, bool exec = false)
: path(p), contents(c.str()), executable(exec) {}
};
} // namespace eden
} // namespace facebook

View File

@ -18,13 +18,13 @@
/**
* Check that a FileInode has the expected contents and permissions.
*/
#define EXPECT_FILE_INODE(fileInode, expectedData, expectedPerms) \
do { \
EXPECT_EQ( \
expectedData, \
folly::StringPiece{ \
fileInode->readAll().get(std::chrono::milliseconds(20000))}); \
EXPECT_EQ( \
folly::sformat("{:#o}", (expectedPerms)), \
folly::sformat("{:#o}", (fileInode)->getPermissions())); \
#define EXPECT_FILE_INODE(fileInode, expectedData, expectedPerms) \
do { \
EXPECT_EQ( \
expectedData, \
folly::StringPiece{ \
(fileInode)->readAll().get(std::chrono::seconds(20))}); \
EXPECT_EQ( \
folly::sformat("{:#o}", (expectedPerms)), \
folly::sformat("{:#o}", (fileInode)->getPermissions())); \
} while (0)

View File

@ -140,7 +140,7 @@ TEST_F(FakeBackingStoreTest, getTree) {
makeTestHash("abc"),
{
{"foo", foo},
{"runme", runme, 0755},
{"runme", runme, FakeBlobType::EXECUTABLE_FILE},
});
EXPECT_EQ(makeTestHash("abc"), dir1->get().getHash());
auto* dir2 = store_->putTree({{"README", store_->putBlob("docs go here")}});
@ -149,9 +149,9 @@ TEST_F(FakeBackingStoreTest, getTree) {
auto* rootDir = store_->putTree(
rootHash,
{
{"zzz", foo, 0444},
{"zzz", foo, FakeBlobType::REGULAR_FILE},
{"dir1", dir1},
{"readonly", dir2, 0500},
{"readonly", dir2},
{"bar", bar},
});
@ -183,12 +183,12 @@ TEST_F(FakeBackingStoreTest, getTree) {
EXPECT_EQ(S_IFDIR | 0755, tree2->getEntryAt(1).getMode());
EXPECT_EQ(PathComponentPiece{"readonly"}, tree2->getEntryAt(2).getName());
EXPECT_EQ(dir2->get().getHash(), tree2->getEntryAt(2).getHash());
// TreeEntry objects only track owner permissions, so even though we input
// the permissions as 0500 above this really ends up returning 0555
EXPECT_EQ(S_IFDIR | 0555, tree2->getEntryAt(2).getMode());
// TreeEntry objects only tracking the owner executable bit, so even though we
// input the permissions as 0500 above this really ends up returning 0755
EXPECT_EQ(S_IFDIR | 0755, tree2->getEntryAt(2).getMode());
EXPECT_EQ(PathComponentPiece{"zzz"}, tree2->getEntryAt(3).getName());
EXPECT_EQ(foo->get().getHash(), tree2->getEntryAt(3).getHash());
EXPECT_EQ(S_IFREG | 0444, tree2->getEntryAt(3).getMode());
EXPECT_EQ(S_IFREG | 0644, tree2->getEntryAt(3).getMode());
EXPECT_EQ(rootHash, future3.get()->getHash());

View File

@ -34,8 +34,7 @@ TEST(FakeObjectStore, getObjectsOfAllTypesFromStore) {
// Test getTree().
vector<TreeEntry> entries1;
uint8_t rw_ = 0b110;
entries1.emplace_back(fileHash, "a_file", FileType::REGULAR_FILE, rw_);
entries1.emplace_back(fileHash, "a_file", FileType::REGULAR_FILE);
Tree tree1(std::move(entries1), tree1Hash);
store.addTree(std::move(tree1));
auto foundTree = store.getTree(tree1Hash).get();
@ -52,7 +51,7 @@ TEST(FakeObjectStore, getObjectsOfAllTypesFromStore) {
// Test getTreeForCommit().
vector<TreeEntry> entries2;
entries2.emplace_back(fileHash, "a_file", FileType::REGULAR_FILE, rw_);
entries2.emplace_back(fileHash, "a_file", FileType::REGULAR_FILE);
Tree tree2(std::move(entries2), tree2Hash);
store.setTreeForCommit(commHash, std::move(tree2));
auto foundTreeForCommit = store.getTreeForCommit(commHash).get();