[VirtualFileSystem] InMemoryFileSystem::addFile(): Type and Perms

Summary:
This implements a FIXME in InMemoryFileSystem::addFile(), allowing
clients to specify User, Group, Type, and/or Perms when creating a
file in an in-memory filesystem.

New tests included. Ran tests with:

% ninja BasicTests && ./tools/clang/unittests/Basic/BasicTests

Fixes PR#35172 (https://bugs.llvm.org/show_bug.cgi?id=35172)

Reviewers: bkramer, hokein

Reviewed By: bkramer, hokein

Subscribers: alexfh

Differential Revision: https://reviews.llvm.org/D39572

llvm-svn: 317800
This commit is contained in:
Ben Hamilton 2017-11-09 16:01:16 +00:00
parent d4102df9ad
commit e5af5bde71
3 changed files with 108 additions and 15 deletions

View File

@ -320,15 +320,25 @@ public:
~InMemoryFileSystem() override;
/// Add a buffer to the VFS with a path. The VFS owns the buffer.
/// If present, User, Group, Type and Perms apply to the newly-created file.
/// \return true if the file was successfully added, false if the file already
/// exists in the file system with different contents.
bool addFile(const Twine &Path, time_t ModificationTime,
std::unique_ptr<llvm::MemoryBuffer> Buffer);
std::unique_ptr<llvm::MemoryBuffer> Buffer,
Optional<uint32_t> User = None, Optional<uint32_t> Group = None,
Optional<llvm::sys::fs::file_type> Type = None,
Optional<llvm::sys::fs::perms> Perms = None);
/// Add a buffer to the VFS with a path. The VFS does not own the buffer.
/// If present, User, Group, Type and Perms apply to the newly-created file.
/// \return true if the file was successfully added, false if the file already
/// exists in the file system with different contents.
bool addFileNoOwn(const Twine &Path, time_t ModificationTime,
llvm::MemoryBuffer *Buffer);
llvm::MemoryBuffer *Buffer,
Optional<uint32_t> User = None,
Optional<uint32_t> Group = None,
Optional<llvm::sys::fs::file_type> Type = None,
Optional<llvm::sys::fs::perms> Perms = None);
std::string toString() const;
/// Return true if this file system normalizes . and .. in paths.
bool useNormalizedPaths() const { return UseNormalizedPaths; }

View File

@ -493,7 +493,11 @@ std::string InMemoryFileSystem::toString() const {
}
bool InMemoryFileSystem::addFile(const Twine &P, time_t ModificationTime,
std::unique_ptr<llvm::MemoryBuffer> Buffer) {
std::unique_ptr<llvm::MemoryBuffer> Buffer,
Optional<uint32_t> User,
Optional<uint32_t> Group,
Optional<llvm::sys::fs::file_type> Type,
Optional<llvm::sys::fs::perms> Perms) {
SmallString<128> Path;
P.toVector(Path);
@ -509,7 +513,14 @@ bool InMemoryFileSystem::addFile(const Twine &P, time_t ModificationTime,
return false;
detail::InMemoryDirectory *Dir = Root.get();
auto I = llvm::sys::path::begin(Path), E = llvm::sys::path::end(Path);
auto I = llvm::sys::path::begin(Path), E = sys::path::end(Path);
const auto ResolvedUser = User.getValueOr(0);
const auto ResolvedGroup = Group.getValueOr(0);
const auto ResolvedType = Type.getValueOr(sys::fs::file_type::regular_file);
const auto ResolvedPerms = Perms.getValueOr(sys::fs::all_all);
// Any intermediate directories we create should be accessible by
// the owner, even if Perms says otherwise for the final path.
const auto NewDirectoryPerms = ResolvedPerms | sys::fs::owner_all;
while (true) {
StringRef Name = *I;
detail::InMemoryNode *Node = Dir->getChild(Name);
@ -517,24 +528,21 @@ bool InMemoryFileSystem::addFile(const Twine &P, time_t ModificationTime,
if (!Node) {
if (I == E) {
// End of the path, create a new file.
// FIXME: expose the status details in the interface.
Status Stat(P.str(), getNextVirtualUniqueID(),
llvm::sys::toTimePoint(ModificationTime), 0, 0,
Buffer->getBufferSize(),
llvm::sys::fs::file_type::regular_file,
llvm::sys::fs::all_all);
llvm::sys::toTimePoint(ModificationTime), ResolvedUser,
ResolvedGroup, Buffer->getBufferSize(), ResolvedType,
ResolvedPerms);
Dir->addChild(Name, llvm::make_unique<detail::InMemoryFile>(
std::move(Stat), std::move(Buffer)));
return true;
}
// Create a new directory. Use the path up to here.
// FIXME: expose the status details in the interface.
Status Stat(
StringRef(Path.str().begin(), Name.end() - Path.str().begin()),
getNextVirtualUniqueID(), llvm::sys::toTimePoint(ModificationTime), 0,
0, Buffer->getBufferSize(), llvm::sys::fs::file_type::directory_file,
llvm::sys::fs::all_all);
getNextVirtualUniqueID(), llvm::sys::toTimePoint(ModificationTime),
ResolvedUser, ResolvedGroup, Buffer->getBufferSize(),
sys::fs::file_type::directory_file, NewDirectoryPerms);
Dir = cast<detail::InMemoryDirectory>(Dir->addChild(
Name, llvm::make_unique<detail::InMemoryDirectory>(std::move(Stat))));
continue;
@ -558,10 +566,16 @@ bool InMemoryFileSystem::addFile(const Twine &P, time_t ModificationTime,
}
bool InMemoryFileSystem::addFileNoOwn(const Twine &P, time_t ModificationTime,
llvm::MemoryBuffer *Buffer) {
llvm::MemoryBuffer *Buffer,
Optional<uint32_t> User,
Optional<uint32_t> Group,
Optional<llvm::sys::fs::file_type> Type,
Optional<llvm::sys::fs::perms> Perms) {
return addFile(P, ModificationTime,
llvm::MemoryBuffer::getMemBuffer(
Buffer->getBuffer(), Buffer->getBufferIdentifier()));
Buffer->getBuffer(), Buffer->getBufferIdentifier()),
std::move(User), std::move(Group), std::move(Type),
std::move(Perms));
}
static ErrorOr<detail::InMemoryNode *>

View File

@ -760,6 +760,75 @@ TEST_F(InMemoryFileSystemTest, WorkingDirectory) {
NormalizedFS.getCurrentWorkingDirectory().get()));
}
TEST_F(InMemoryFileSystemTest, AddFileWithUser) {
FS.addFile("/a/b/c", 0, MemoryBuffer::getMemBuffer("abc"), 0xFEEDFACE);
auto Stat = FS.status("/a");
ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
ASSERT_TRUE(Stat->isDirectory());
ASSERT_EQ(0xFEEDFACE, Stat->getUser());
Stat = FS.status("/a/b");
ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
ASSERT_TRUE(Stat->isDirectory());
ASSERT_EQ(0xFEEDFACE, Stat->getUser());
Stat = FS.status("/a/b/c");
ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
ASSERT_TRUE(Stat->isRegularFile());
ASSERT_EQ(sys::fs::perms::all_all, Stat->getPermissions());
ASSERT_EQ(0xFEEDFACE, Stat->getUser());
}
TEST_F(InMemoryFileSystemTest, AddFileWithGroup) {
FS.addFile("/a/b/c", 0, MemoryBuffer::getMemBuffer("abc"), None, 0xDABBAD00);
auto Stat = FS.status("/a");
ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
ASSERT_TRUE(Stat->isDirectory());
ASSERT_EQ(0xDABBAD00, Stat->getGroup());
Stat = FS.status("/a/b");
ASSERT_TRUE(Stat->isDirectory());
ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
ASSERT_EQ(0xDABBAD00, Stat->getGroup());
Stat = FS.status("/a/b/c");
ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
ASSERT_TRUE(Stat->isRegularFile());
ASSERT_EQ(sys::fs::perms::all_all, Stat->getPermissions());
ASSERT_EQ(0xDABBAD00, Stat->getGroup());
}
TEST_F(InMemoryFileSystemTest, AddFileWithFileType) {
FS.addFile("/a/b/c", 0, MemoryBuffer::getMemBuffer("abc"), None, None,
sys::fs::file_type::socket_file);
auto Stat = FS.status("/a");
ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
ASSERT_TRUE(Stat->isDirectory());
Stat = FS.status("/a/b");
ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
ASSERT_TRUE(Stat->isDirectory());
Stat = FS.status("/a/b/c");
ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
ASSERT_EQ(sys::fs::file_type::socket_file, Stat->getType());
ASSERT_EQ(sys::fs::perms::all_all, Stat->getPermissions());
}
TEST_F(InMemoryFileSystemTest, AddFileWithPerms) {
FS.addFile("/a/b/c", 0, MemoryBuffer::getMemBuffer("abc"), None, None,
None, sys::fs::perms::owner_read | sys::fs::perms::owner_write);
auto Stat = FS.status("/a");
ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
ASSERT_TRUE(Stat->isDirectory());
ASSERT_EQ(sys::fs::perms::owner_read | sys::fs::perms::owner_write |
sys::fs::perms::owner_exe, Stat->getPermissions());
Stat = FS.status("/a/b");
ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
ASSERT_TRUE(Stat->isDirectory());
ASSERT_EQ(sys::fs::perms::owner_read | sys::fs::perms::owner_write |
sys::fs::perms::owner_exe, Stat->getPermissions());
Stat = FS.status("/a/b/c");
ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
ASSERT_TRUE(Stat->isRegularFile());
ASSERT_EQ(sys::fs::perms::owner_read | sys::fs::perms::owner_write,
Stat->getPermissions());
}
// NOTE: in the tests below, we use '//root/' as our root directory, since it is
// a legal *absolute* path on Windows as well as *nix.
class VFSFromYAMLTest : public ::testing::Test {