summaryrefslogtreecommitdiffhomepage
path: root/test/syscalls/linux/open.cc
diff options
context:
space:
mode:
Diffstat (limited to 'test/syscalls/linux/open.cc')
-rw-r--r--test/syscalls/linux/open.cc503
1 files changed, 0 insertions, 503 deletions
diff --git a/test/syscalls/linux/open.cc b/test/syscalls/linux/open.cc
deleted file mode 100644
index 8f0c9cb49..000000000
--- a/test/syscalls/linux/open.cc
+++ /dev/null
@@ -1,503 +0,0 @@
-// Copyright 2018 The gVisor Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include <errno.h>
-#include <fcntl.h>
-#include <linux/capability.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include "gmock/gmock.h"
-#include "gtest/gtest.h"
-#include "absl/memory/memory.h"
-#include "test/syscalls/linux/file_base.h"
-#include "test/util/capability_util.h"
-#include "test/util/cleanup.h"
-#include "test/util/file_descriptor.h"
-#include "test/util/fs_util.h"
-#include "test/util/temp_path.h"
-#include "test/util/test_util.h"
-#include "test/util/thread_util.h"
-
-namespace gvisor {
-namespace testing {
-
-namespace {
-
-// This test is currently very rudimentary.
-//
-// There are plenty of extra cases to cover once the sentry supports them.
-//
-// Different types of opens:
-// * O_CREAT
-// * O_DIRECTORY
-// * O_NOFOLLOW
-// * O_PATH <- Will we ever support this?
-//
-// Special operations on open:
-// * O_EXCL
-//
-// Special files:
-// * Blocking behavior for a named pipe.
-//
-// Different errors:
-// * EACCES
-// * EEXIST
-// * ENAMETOOLONG
-// * ELOOP
-// * ENOTDIR
-// * EPERM
-class OpenTest : public FileTest {
- void SetUp() override {
- FileTest::SetUp();
-
- ASSERT_THAT(
- write(test_file_fd_.get(), test_data_.c_str(), test_data_.length()),
- SyscallSucceedsWithValue(test_data_.length()));
- EXPECT_THAT(lseek(test_file_fd_.get(), 0, SEEK_SET), SyscallSucceeds());
- }
-
- public:
- const std::string test_data_ = "hello world\n";
-};
-
-TEST_F(OpenTest, OTrunc) {
- auto dirpath = JoinPath(GetAbsoluteTestTmpdir(), "truncd");
- ASSERT_THAT(mkdir(dirpath.c_str(), 0777), SyscallSucceeds());
- ASSERT_THAT(open(dirpath.c_str(), O_TRUNC, 0666),
- SyscallFailsWithErrno(EISDIR));
-}
-
-TEST_F(OpenTest, OTruncAndReadOnlyDir) {
- auto dirpath = JoinPath(GetAbsoluteTestTmpdir(), "truncd");
- ASSERT_THAT(mkdir(dirpath.c_str(), 0777), SyscallSucceeds());
- ASSERT_THAT(open(dirpath.c_str(), O_TRUNC | O_RDONLY, 0666),
- SyscallFailsWithErrno(EISDIR));
-}
-
-TEST_F(OpenTest, OTruncAndReadOnlyFile) {
- auto dirpath = JoinPath(GetAbsoluteTestTmpdir(), "truncfile");
- const FileDescriptor existing =
- ASSERT_NO_ERRNO_AND_VALUE(Open(dirpath.c_str(), O_RDWR | O_CREAT, 0666));
- const FileDescriptor otrunc = ASSERT_NO_ERRNO_AND_VALUE(
- Open(dirpath.c_str(), O_TRUNC | O_RDONLY, 0666));
-}
-
-TEST_F(OpenTest, OCreateDirectory) {
- SKIP_IF(IsRunningWithVFS1());
- auto dirpath = GetAbsoluteTestTmpdir();
-
- // Normal case: existing directory.
- ASSERT_THAT(open(dirpath.c_str(), O_RDWR | O_CREAT, 0666),
- SyscallFailsWithErrno(EISDIR));
- // Trailing separator on existing directory.
- ASSERT_THAT(open(dirpath.append("/").c_str(), O_RDWR | O_CREAT, 0666),
- SyscallFailsWithErrno(EISDIR));
- // Trailing separator on non-existing directory.
- ASSERT_THAT(open(JoinPath(dirpath, "non-existent").append("/").c_str(),
- O_RDWR | O_CREAT, 0666),
- SyscallFailsWithErrno(EISDIR));
- // "." special case.
- ASSERT_THAT(open(JoinPath(dirpath, ".").c_str(), O_RDWR | O_CREAT, 0666),
- SyscallFailsWithErrno(EISDIR));
-}
-
-TEST_F(OpenTest, MustCreateExisting) {
- auto dirPath = GetAbsoluteTestTmpdir();
-
- // Existing directory.
- ASSERT_THAT(open(dirPath.c_str(), O_RDWR | O_CREAT | O_EXCL, 0666),
- SyscallFailsWithErrno(EEXIST));
-
- // Existing file.
- auto newFile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(dirPath));
- ASSERT_THAT(open(newFile.path().c_str(), O_RDWR | O_CREAT | O_EXCL, 0666),
- SyscallFailsWithErrno(EEXIST));
-}
-
-TEST_F(OpenTest, ReadOnly) {
- char buf;
- const FileDescriptor ro_file =
- ASSERT_NO_ERRNO_AND_VALUE(Open(test_file_name_, O_RDONLY));
-
- EXPECT_THAT(read(ro_file.get(), &buf, 1), SyscallSucceedsWithValue(1));
- EXPECT_THAT(lseek(ro_file.get(), 0, SEEK_SET), SyscallSucceeds());
- EXPECT_THAT(write(ro_file.get(), &buf, 1), SyscallFailsWithErrno(EBADF));
-}
-
-TEST_F(OpenTest, WriteOnly) {
- char buf;
- const FileDescriptor wo_file =
- ASSERT_NO_ERRNO_AND_VALUE(Open(test_file_name_, O_WRONLY));
-
- EXPECT_THAT(read(wo_file.get(), &buf, 1), SyscallFailsWithErrno(EBADF));
- EXPECT_THAT(lseek(wo_file.get(), 0, SEEK_SET), SyscallSucceeds());
- EXPECT_THAT(write(wo_file.get(), &buf, 1), SyscallSucceedsWithValue(1));
-}
-
-TEST_F(OpenTest, CreateWithAppend) {
- std::string data = "text";
- std::string new_file = NewTempAbsPath();
- const FileDescriptor file = ASSERT_NO_ERRNO_AND_VALUE(
- Open(new_file, O_WRONLY | O_APPEND | O_CREAT, 0666));
- EXPECT_THAT(write(file.get(), data.c_str(), data.size()),
- SyscallSucceedsWithValue(data.size()));
- EXPECT_THAT(lseek(file.get(), 0, SEEK_SET), SyscallSucceeds());
- EXPECT_THAT(write(file.get(), data.c_str(), data.size()),
- SyscallSucceedsWithValue(data.size()));
-
- // Check that the size of the file is correct and that the offset has been
- // incremented to that size.
- struct stat s0;
- EXPECT_THAT(fstat(file.get(), &s0), SyscallSucceeds());
- EXPECT_EQ(s0.st_size, 2 * data.size());
- EXPECT_THAT(lseek(file.get(), 0, SEEK_CUR),
- SyscallSucceedsWithValue(2 * data.size()));
-}
-
-TEST_F(OpenTest, ReadWrite) {
- char buf;
- const FileDescriptor rw_file =
- ASSERT_NO_ERRNO_AND_VALUE(Open(test_file_name_, O_RDWR));
-
- EXPECT_THAT(read(rw_file.get(), &buf, 1), SyscallSucceedsWithValue(1));
- EXPECT_THAT(lseek(rw_file.get(), 0, SEEK_SET), SyscallSucceeds());
- EXPECT_THAT(write(rw_file.get(), &buf, 1), SyscallSucceedsWithValue(1));
-}
-
-TEST_F(OpenTest, RelPath) {
- auto name = std::string(Basename(test_file_name_));
-
- ASSERT_THAT(chdir(GetAbsoluteTestTmpdir().c_str()), SyscallSucceeds());
- const FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(name, O_RDONLY));
-}
-
-TEST_F(OpenTest, AbsPath) {
- const FileDescriptor fd =
- ASSERT_NO_ERRNO_AND_VALUE(Open(test_file_name_, O_RDONLY));
-}
-
-TEST_F(OpenTest, AtRelPath) {
- auto name = std::string(Basename(test_file_name_));
- const FileDescriptor dirfd = ASSERT_NO_ERRNO_AND_VALUE(
- Open(GetAbsoluteTestTmpdir(), O_RDONLY | O_DIRECTORY));
- const FileDescriptor fd =
- ASSERT_NO_ERRNO_AND_VALUE(OpenAt(dirfd.get(), name, O_RDONLY));
-}
-
-TEST_F(OpenTest, AtAbsPath) {
- const FileDescriptor dirfd = ASSERT_NO_ERRNO_AND_VALUE(
- Open(GetAbsoluteTestTmpdir(), O_RDONLY | O_DIRECTORY));
- const FileDescriptor fd =
- ASSERT_NO_ERRNO_AND_VALUE(OpenAt(dirfd.get(), test_file_name_, O_RDONLY));
-}
-
-TEST_F(OpenTest, OpenNoFollowSymlink) {
- const std::string link_path = JoinPath(GetAbsoluteTestTmpdir(), "link");
- ASSERT_THAT(symlink(test_file_name_.c_str(), link_path.c_str()),
- SyscallSucceeds());
- auto cleanup = Cleanup([link_path]() {
- EXPECT_THAT(unlink(link_path.c_str()), SyscallSucceeds());
- });
-
- // Open will succeed without O_NOFOLLOW and fails with O_NOFOLLOW.
- const FileDescriptor fd2 =
- ASSERT_NO_ERRNO_AND_VALUE(Open(link_path, O_RDONLY));
- ASSERT_THAT(open(link_path.c_str(), O_RDONLY | O_NOFOLLOW),
- SyscallFailsWithErrno(ELOOP));
-}
-
-TEST_F(OpenTest, OpenNoFollowStillFollowsLinksInPath) {
- // We will create the following structure:
- // tmp_folder/real_folder/file
- // tmp_folder/sym_folder -> tmp_folder/real_folder
- //
- // We will then open tmp_folder/sym_folder/file with O_NOFOLLOW and it
- // should succeed as O_NOFOLLOW only applies to the final path component.
- auto tmp_path =
- ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(GetAbsoluteTestTmpdir()));
- auto sym_path = ASSERT_NO_ERRNO_AND_VALUE(
- TempPath::CreateSymlinkTo(GetAbsoluteTestTmpdir(), tmp_path.path()));
- auto file_path =
- ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(tmp_path.path()));
-
- auto path_via_symlink = JoinPath(sym_path.path(), Basename(file_path.path()));
- const FileDescriptor fd2 =
- ASSERT_NO_ERRNO_AND_VALUE(Open(path_via_symlink, O_RDONLY | O_NOFOLLOW));
-}
-
-// Test that open(2) can follow symlinks that point back to the same tree.
-// Test sets up files as follows:
-// root/child/symlink => redirects to ../..
-// root/child/target => regular file
-//
-// open("root/child/symlink/root/child/file")
-TEST_F(OpenTest, SymlinkRecurse) {
- auto root =
- ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(GetAbsoluteTestTmpdir()));
- auto child = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(root.path()));
- auto symlink = ASSERT_NO_ERRNO_AND_VALUE(
- TempPath::CreateSymlinkTo(child.path(), "../.."));
- auto target = ASSERT_NO_ERRNO_AND_VALUE(
- TempPath::CreateFileWith(child.path(), "abc", 0644));
- auto path_via_symlink =
- JoinPath(symlink.path(), Basename(root.path()), Basename(child.path()),
- Basename(target.path()));
- const auto contents =
- ASSERT_NO_ERRNO_AND_VALUE(GetContents(path_via_symlink));
- ASSERT_EQ(contents, "abc");
-}
-
-TEST_F(OpenTest, Fault) {
- char* totally_not_null = nullptr;
- ASSERT_THAT(open(totally_not_null, O_RDONLY), SyscallFailsWithErrno(EFAULT));
-}
-
-TEST_F(OpenTest, AppendOnly) {
- // First write some data to the fresh file.
- const int64_t kBufSize = 1024;
- std::vector<char> buf(kBufSize, 'a');
-
- FileDescriptor fd0 = ASSERT_NO_ERRNO_AND_VALUE(Open(test_file_name_, O_RDWR));
-
- std::fill(buf.begin(), buf.end(), 'a');
- EXPECT_THAT(WriteFd(fd0.get(), buf.data(), buf.size()),
- SyscallSucceedsWithValue(buf.size()));
- fd0.reset(); // Close the file early.
-
- // Next get two handles to the same file. We open two files because we want
- // to make sure that appending is respected between them.
- const FileDescriptor fd1 =
- ASSERT_NO_ERRNO_AND_VALUE(Open(test_file_name_, O_RDWR | O_APPEND));
- EXPECT_THAT(lseek(fd1.get(), 0, SEEK_CUR), SyscallSucceedsWithValue(0));
-
- const FileDescriptor fd2 =
- ASSERT_NO_ERRNO_AND_VALUE(Open(test_file_name_, O_RDWR | O_APPEND));
- EXPECT_THAT(lseek(fd2.get(), 0, SEEK_CUR), SyscallSucceedsWithValue(0));
-
- // Then try to write to the first fd and make sure the bytes are appended.
- EXPECT_THAT(WriteFd(fd1.get(), buf.data(), buf.size()),
- SyscallSucceedsWithValue(buf.size()));
-
- // Check that the size of the file is correct and that the offset has been
- // incremented to that size.
- struct stat s0;
- EXPECT_THAT(fstat(fd1.get(), &s0), SyscallSucceeds());
- EXPECT_EQ(s0.st_size, kBufSize * 2);
- EXPECT_THAT(lseek(fd1.get(), 0, SEEK_CUR),
- SyscallSucceedsWithValue(kBufSize * 2));
-
- // Then try to write to the second fd and make sure the bytes are appended.
- EXPECT_THAT(WriteFd(fd2.get(), buf.data(), buf.size()),
- SyscallSucceedsWithValue(buf.size()));
-
- // Check that the size of the file is correct and that the offset has been
- // incremented to that size.
- struct stat s1;
- EXPECT_THAT(fstat(fd2.get(), &s1), SyscallSucceeds());
- EXPECT_EQ(s1.st_size, kBufSize * 3);
- EXPECT_THAT(lseek(fd2.get(), 0, SEEK_CUR),
- SyscallSucceedsWithValue(kBufSize * 3));
-}
-
-TEST_F(OpenTest, AppendConcurrentWrite) {
- constexpr int kThreadCount = 5;
- constexpr int kBytesPerThread = 10000;
- std::unique_ptr<ScopedThread> threads[kThreadCount];
-
- // In case of the uncached policy, we expect that a file system can be changed
- // externally, so we create a new inode each time when we open a file and we
- // can't guarantee that writes to files with O_APPEND will work correctly.
- SKIP_IF(getenv("GVISOR_GOFER_UNCACHED"));
-
- EXPECT_THAT(truncate(test_file_name_.c_str(), 0), SyscallSucceeds());
-
- std::string filename = test_file_name_;
- DisableSave ds; // Too many syscalls.
- // Start kThreadCount threads which will write concurrently into the same
- // file.
- for (int i = 0; i < kThreadCount; i++) {
- threads[i] = absl::make_unique<ScopedThread>([filename]() {
- const FileDescriptor fd =
- ASSERT_NO_ERRNO_AND_VALUE(Open(filename, O_RDWR | O_APPEND));
-
- for (int j = 0; j < kBytesPerThread; j++) {
- EXPECT_THAT(WriteFd(fd.get(), &j, 1), SyscallSucceedsWithValue(1));
- }
- });
- }
- for (int i = 0; i < kThreadCount; i++) {
- threads[i]->Join();
- }
-
- // Check that the size of the file is correct.
- struct stat st;
- EXPECT_THAT(stat(test_file_name_.c_str(), &st), SyscallSucceeds());
- EXPECT_EQ(st.st_size, kThreadCount * kBytesPerThread);
-}
-
-TEST_F(OpenTest, Truncate) {
- {
- // First write some data to the new file and close it.
- FileDescriptor fd0 =
- ASSERT_NO_ERRNO_AND_VALUE(Open(test_file_name_, O_WRONLY));
- std::vector<char> orig(10, 'a');
- EXPECT_THAT(WriteFd(fd0.get(), orig.data(), orig.size()),
- SyscallSucceedsWithValue(orig.size()));
- }
-
- // Then open with truncate and verify that offset is set to 0.
- const FileDescriptor fd1 =
- ASSERT_NO_ERRNO_AND_VALUE(Open(test_file_name_, O_RDWR | O_TRUNC));
- EXPECT_THAT(lseek(fd1.get(), 0, SEEK_CUR), SyscallSucceedsWithValue(0));
-
- // Then write less data to the file and ensure the old content is gone.
- std::vector<char> want(5, 'b');
- EXPECT_THAT(WriteFd(fd1.get(), want.data(), want.size()),
- SyscallSucceedsWithValue(want.size()));
-
- struct stat stat;
- EXPECT_THAT(fstat(fd1.get(), &stat), SyscallSucceeds());
- EXPECT_EQ(stat.st_size, want.size());
- EXPECT_THAT(lseek(fd1.get(), 0, SEEK_CUR),
- SyscallSucceedsWithValue(want.size()));
-
- // Read the data and ensure only the latest write is in the file.
- std::vector<char> got(want.size() + 1, 'c');
- ASSERT_THAT(pread(fd1.get(), got.data(), got.size(), 0),
- SyscallSucceedsWithValue(want.size()));
- EXPECT_EQ(memcmp(want.data(), got.data(), want.size()), 0)
- << "rbuf=" << got.data();
- EXPECT_EQ(got.back(), 'c'); // Last byte should not have been modified.
-}
-
-TEST_F(OpenTest, NameTooLong) {
- char buf[4097] = {};
- memset(buf, 'a', 4097);
- EXPECT_THAT(open(buf, O_RDONLY), SyscallFailsWithErrno(ENAMETOOLONG));
-}
-
-TEST_F(OpenTest, DotsFromRoot) {
- const FileDescriptor rootfd =
- ASSERT_NO_ERRNO_AND_VALUE(Open("/", O_RDONLY | O_DIRECTORY));
- const FileDescriptor other_rootfd =
- ASSERT_NO_ERRNO_AND_VALUE(OpenAt(rootfd.get(), "..", O_RDONLY));
-}
-
-TEST_F(OpenTest, DirectoryWritableFails) {
- ASSERT_THAT(open(GetAbsoluteTestTmpdir().c_str(), O_RDWR),
- SyscallFailsWithErrno(EISDIR));
-}
-
-TEST_F(OpenTest, FileNotDirectory) {
- // Create a file and try to open it with O_DIRECTORY.
- auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
- ASSERT_THAT(open(file.path().c_str(), O_RDONLY | O_DIRECTORY),
- SyscallFailsWithErrno(ENOTDIR));
-}
-
-TEST_F(OpenTest, Null) {
- char c = '\0';
- ASSERT_THAT(open(&c, O_RDONLY), SyscallFailsWithErrno(ENOENT));
-}
-
-// NOTE(b/119785738): While the man pages specify that this behavior should be
-// undefined, Linux truncates the file on opening read only if we have write
-// permission, so we will too.
-TEST_F(OpenTest, CanTruncateReadOnly) {
- const FileDescriptor fd1 =
- ASSERT_NO_ERRNO_AND_VALUE(Open(test_file_name_, O_RDONLY | O_TRUNC));
-
- struct stat stat;
- EXPECT_THAT(fstat(fd1.get(), &stat), SyscallSucceeds());
- EXPECT_EQ(stat.st_size, 0);
-}
-
-// If we don't have read permission on the file, opening with
-// O_TRUNC should fail.
-TEST_F(OpenTest, CanTruncateReadOnlyNoWritePermission_NoRandomSave) {
- // Drop capabilities that allow us to override file permissions.
- ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false));
-
- const DisableSave ds; // Permissions are dropped.
- ASSERT_THAT(chmod(test_file_name_.c_str(), S_IRUSR | S_IRGRP),
- SyscallSucceeds());
-
- ASSERT_THAT(open(test_file_name_.c_str(), O_RDONLY | O_TRUNC),
- SyscallFailsWithErrno(EACCES));
-
- const FileDescriptor fd1 =
- ASSERT_NO_ERRNO_AND_VALUE(Open(test_file_name_, O_RDONLY));
-
- struct stat stat;
- EXPECT_THAT(fstat(fd1.get(), &stat), SyscallSucceeds());
- EXPECT_EQ(stat.st_size, test_data_.size());
-}
-
-// If we don't have read permission but have write permission, opening O_WRONLY
-// and O_TRUNC should succeed.
-TEST_F(OpenTest, CanTruncateWriteOnlyNoReadPermission_NoRandomSave) {
- const DisableSave ds; // Permissions are dropped.
-
- EXPECT_THAT(fchmod(test_file_fd_.get(), S_IWUSR | S_IWGRP),
- SyscallSucceeds());
-
- const FileDescriptor fd1 =
- ASSERT_NO_ERRNO_AND_VALUE(Open(test_file_name_, O_WRONLY | O_TRUNC));
-
- EXPECT_THAT(fchmod(test_file_fd_.get(), S_IRUSR | S_IRGRP),
- SyscallSucceeds());
-
- const FileDescriptor fd2 =
- ASSERT_NO_ERRNO_AND_VALUE(Open(test_file_name_, O_RDONLY));
-
- struct stat stat;
- EXPECT_THAT(fstat(fd2.get(), &stat), SyscallSucceeds());
- EXPECT_EQ(stat.st_size, 0);
-}
-
-TEST_F(OpenTest, CanTruncateWithStrangePermissions) {
- ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false));
- ASSERT_NO_ERRNO(SetCapability(CAP_DAC_READ_SEARCH, false));
- const DisableSave ds; // Permissions are dropped.
- std::string path = NewTempAbsPath();
- int fd;
- // Create a file without user permissions.
- EXPECT_THAT( // SAVE_BELOW
- fd = open(path.c_str(), O_CREAT | O_TRUNC | O_WRONLY, 055),
- SyscallSucceeds());
- EXPECT_THAT(close(fd), SyscallSucceeds());
-
- // Cannot open file because we are owner and have no permissions set.
- EXPECT_THAT(open(path.c_str(), O_RDONLY), SyscallFailsWithErrno(EACCES));
-
- // We *can* chmod the file, because we are the owner.
- EXPECT_THAT(chmod(path.c_str(), 0755), SyscallSucceeds());
-
- // Now we can open the file again.
- EXPECT_THAT(fd = open(path.c_str(), O_RDWR), SyscallSucceeds());
- EXPECT_THAT(close(fd), SyscallSucceeds());
-}
-
-TEST_F(OpenTest, OpenNonDirectoryWithTrailingSlash) {
- const TempPath file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
- const std::string bad_path = file.path() + "/";
- EXPECT_THAT(open(bad_path.c_str(), O_RDONLY), SyscallFailsWithErrno(ENOTDIR));
-}
-
-} // namespace
-
-} // namespace testing
-} // namespace gvisor