to satisfy secfs validation suite, limit path components to 255 bytes

Summary:
Most filesystems limit path components to 255. To remain consistent,
let's have Eden do the same, at least for write operations.

Reviewed By: simpkins

Differential Revision: D8151020

fbshipit-source-id: 251da94a076f5765111c8e3d9d8a25c37682e2e3
This commit is contained in:
Chad Austin 2018-05-30 23:07:08 -07:00 committed by Facebook Github Bot
parent b9e09a508f
commit 0797ee3a35
7 changed files with 135 additions and 0 deletions

View File

@ -288,6 +288,8 @@ folly::Future<fuse_entry_out> EdenDispatcher::link(
newParent,
newName);
validatePathComponentLength(newName);
// We intentionally do not support hard links.
// These generally cannot be tracked in source control (git or mercurial)
// and are not portable to non-Unix platforms.

View File

@ -978,6 +978,7 @@ TreeInode::create(PathComponentPiece name, mode_t mode, int /*flags*/) {
std::shared_ptr<EdenFileHandle> handle;
FileInodePtr inode;
validatePathComponentLength(name);
materialize();
// We need to scope the write lock as the getattr call below implicitly
@ -1014,6 +1015,7 @@ TreeInode::create(PathComponentPiece name, mode_t mode, int /*flags*/) {
FileInodePtr TreeInode::symlink(
PathComponentPiece name,
folly::StringPiece symlinkTarget) {
validatePathComponentLength(name);
materialize();
{
@ -1026,6 +1028,8 @@ FileInodePtr TreeInode::symlink(
}
FileInodePtr TreeInode::mknod(PathComponentPiece name, mode_t mode, dev_t dev) {
validatePathComponentLength(name);
// Compute the effective name of the node they want to create.
RelativePath targetName;
std::shared_ptr<EdenFileHandle> handle;
@ -1058,6 +1062,7 @@ TreeInodePtr TreeInode::mkdir(PathComponentPiece name, mode_t mode) {
if (getNodeId() == getMount()->getDotEdenInodeNumber()) {
throw InodeError(EPERM, inodePtrFromThis(), name);
}
validatePathComponentLength(name);
RelativePath targetName;
// Compute the effective name of the node they want to create.
@ -1433,6 +1438,7 @@ Future<Unit> TreeInode::rename(
if (destParent->getNodeId() == getMount()->getDotEdenInodeNumber()) {
return makeFuture<Unit>(InodeError(EPERM, destParent, destName));
}
validatePathComponentLength(destName);
bool needSrc = false;
bool needDest = false;

View File

@ -0,0 +1,101 @@
/*
* Copyright (c) 2004-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 "eden/fs/inodes/EdenDispatcher.h"
#include <folly/experimental/TestUtil.h>
#include <folly/test/TestUtils.h>
#include <gtest/gtest.h>
#include "eden/fs/testharness/FakeTreeBuilder.h"
#include "eden/fs/testharness/TestMount.h"
using namespace facebook::eden;
using namespace std::chrono_literals;
using namespace folly::string_piece_literals;
namespace {
struct EdenDispatcherTest : ::testing::Test {
EdenDispatcherTest() : mount{builder} {}
FakeTreeBuilder builder;
TestMount mount;
};
constexpr auto kTooLongPiece = folly::StringPiece{
"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"};
static_assert(256 == kTooLongPiece.size(), "256 is one larger than the max!");
static const auto kTooLong = PathComponentPiece{kTooLongPiece};
} // namespace
TEST_F(EdenDispatcherTest, mknodReturnsNameTooLong) {
try {
mount.getDispatcher()
->mknod(kRootNodeId, kTooLong, S_IFREG | 0644, 0)
.get(0ms);
FAIL() << "should throw";
} catch (std::system_error& e) {
EXPECT_EQ(ENAMETOOLONG, e.code().value());
}
}
TEST_F(EdenDispatcherTest, mkdirReturnsNameTooLong) {
try {
mount.getDispatcher()
->mkdir(kRootNodeId, kTooLong, S_IFDIR | 0755)
.get(0ms);
FAIL() << "should throw";
} catch (std::system_error& e) {
EXPECT_EQ(ENAMETOOLONG, e.code().value());
}
}
TEST_F(EdenDispatcherTest, symlinkReturnsNameTooLong) {
try {
mount.getDispatcher()->symlink(kRootNodeId, kTooLong, "aoeu"_sp).get(0ms);
FAIL() << "should throw";
} catch (std::system_error& e) {
EXPECT_EQ(ENAMETOOLONG, e.code().value());
}
}
TEST_F(EdenDispatcherTest, renameReturnsNameTooLong) {
try {
mount.getDispatcher()
->rename(kRootNodeId, "oldname"_pc, kRootNodeId, kTooLong)
.get(0ms);
FAIL() << "should throw";
} catch (std::system_error& e) {
EXPECT_EQ(ENAMETOOLONG, e.code().value());
}
}
TEST_F(EdenDispatcherTest, linkReturnsNameTooLong) {
try {
// Eden doesn't support hard links yet and this link call could never work
// in the first place, but at least validate the target name length.
mount.getDispatcher()->link(kRootNodeId, kRootNodeId, kTooLong).get(0ms);
FAIL() << "should throw";
} catch (std::system_error& e) {
EXPECT_EQ(ENAMETOOLONG, e.code().value());
}
}
TEST_F(EdenDispatcherTest, createReturnsNameTooLong) {
try {
mount.getDispatcher()
->create(kRootNodeId, kTooLong, S_IFREG | 0644, 0)
.get(0ms);
FAIL() << "should throw";
} catch (std::system_error& e) {
EXPECT_EQ(ENAMETOOLONG, e.code().value());
}
}

View File

@ -189,6 +189,10 @@ void TestMount::initTestDirectory() {
backingStore_ = make_shared<FakeBackingStore>(localStore_);
}
Dispatcher* TestMount::getDispatcher() const {
return edenMount_->getDispatcher();
}
void TestMount::remount() {
// Create a new copy of the ClientConfig
auto config = make_unique<ClientConfig>(*edenMount_->getConfig());

View File

@ -160,6 +160,8 @@ class TestMount {
return backingStore_;
}
Dispatcher* getDispatcher() const;
/**
* Access to the TestMount's FakeClock which is referenced by the underlying
* EdenMount (and thus inodes).

View File

@ -239,5 +239,12 @@ AbsolutePath normalizeBestEffort(folly::StringPiece path) {
return normalizeBestEffort(path.str().c_str());
}
void validatePathComponentLength(PathComponentPiece name) {
if (name.value().size() > kMaxPathComponentLength) {
folly::throwSystemErrorExplicit(
ENAMETOOLONG, "path component too long: ", name);
}
}
} // namespace eden
} // namespace facebook

View File

@ -36,6 +36,13 @@ folly::StringPiece dirname(folly::StringPiece path);
enum : char { kDirSeparator = '/' };
constexpr folly::StringPiece kDirSeparatorStr{"/"};
/**
* FUSE supports components up to 1024 (FUSE_NAME_MAX) by default. For
* compatibility with other filesystems, Eden will limit to 255.
* https://en.wikipedia.org/wiki/Comparison_of_file_systems
*/
enum : size_t { kMaxPathComponentLength = 255 };
/* Some helpers for working with path composition.
* Goals:
*
@ -1636,6 +1643,12 @@ normalizeBestEffort(const T& path) {
return normalizeBestEffort(path.c_str());
}
/**
* Throws std::system_error with ENAMETOOLONG if the given PathComponent is
* longer than kMaxPathComponentLength.
*/
void validatePathComponentLength(PathComponentPiece name);
/**
* Convenient literals for constructing path types.
*/