summaryrefslogtreecommitdiffhomepage
path: root/test/syscalls/linux/link.cc
diff options
context:
space:
mode:
Diffstat (limited to 'test/syscalls/linux/link.cc')
-rw-r--r--test/syscalls/linux/link.cc360
1 files changed, 0 insertions, 360 deletions
diff --git a/test/syscalls/linux/link.cc b/test/syscalls/linux/link.cc
deleted file mode 100644
index 4f9ca1a65..000000000
--- a/test/syscalls/linux/link.cc
+++ /dev/null
@@ -1,360 +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 <string.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <string>
-
-#include "gtest/gtest.h"
-#include "absl/flags/flag.h"
-#include "absl/strings/str_cat.h"
-#include "test/util/capability_util.h"
-#include "test/util/file_descriptor.h"
-#include "test/util/fs_util.h"
-#include "test/util/posix_error.h"
-#include "test/util/temp_path.h"
-#include "test/util/test_util.h"
-#include "test/util/thread_util.h"
-
-ABSL_FLAG(int32_t, scratch_uid, 65534, "scratch UID");
-
-namespace gvisor {
-namespace testing {
-
-namespace {
-
-// IsSameFile returns true if both filenames have the same device and inode.
-bool IsSameFile(const std::string& f1, const std::string& f2) {
- // Use lstat rather than stat, so that symlinks are not followed.
- struct stat stat1 = {};
- EXPECT_THAT(lstat(f1.c_str(), &stat1), SyscallSucceeds());
- struct stat stat2 = {};
- EXPECT_THAT(lstat(f2.c_str(), &stat2), SyscallSucceeds());
-
- return stat1.st_dev == stat2.st_dev && stat1.st_ino == stat2.st_ino;
-}
-
-// TODO(b/178640646): Add test for linkat with AT_EMPTY_PATH
-
-TEST(LinkTest, CanCreateLinkFile) {
- auto oldfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
- const std::string newname = NewTempAbsPath();
-
- // Get the initial link count.
- uint64_t initial_link_count =
- ASSERT_NO_ERRNO_AND_VALUE(Links(oldfile.path()));
-
- EXPECT_THAT(link(oldfile.path().c_str(), newname.c_str()), SyscallSucceeds());
-
- EXPECT_TRUE(IsSameFile(oldfile.path(), newname));
-
- // Link count should be incremented.
- EXPECT_THAT(Links(oldfile.path()),
- IsPosixErrorOkAndHolds(initial_link_count + 1));
-
- // Delete the link.
- EXPECT_THAT(unlink(newname.c_str()), SyscallSucceeds());
-
- // Link count should be back to initial.
- EXPECT_THAT(Links(oldfile.path()),
- IsPosixErrorOkAndHolds(initial_link_count));
-}
-
-TEST(LinkTest, PermissionDenied) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_FOWNER)));
-
- // Make the file "unsafe" to link by making it only readable, but not
- // writable.
- const auto unwriteable_file =
- ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileMode(0400));
- const std::string special_path = NewTempAbsPath();
- ASSERT_THAT(mkfifo(special_path.c_str(), 0666), SyscallSucceeds());
- const auto setuid_file =
- ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileMode(0666 | S_ISUID));
-
- const std::string newname = NewTempAbsPath();
-
- // Do setuid in a separate thread so that after finishing this test, the
- // process can still open files the test harness created before starting this
- // test. Otherwise, the files are created by root (UID before the test), but
- // cannot be opened by the `uid` set below after the test. After calling
- // setuid(non-zero-UID), there is no way to get root privileges back.
- ScopedThread([&] {
- // Use syscall instead of glibc setuid wrapper because we want this setuid
- // call to only apply to this task. POSIX threads, however, require that all
- // threads have the same UIDs, so using the setuid wrapper sets all threads'
- // real UID.
- // Also drops capabilities.
- EXPECT_THAT(syscall(SYS_setuid, absl::GetFlag(FLAGS_scratch_uid)),
- SyscallSucceeds());
-
- EXPECT_THAT(link(unwriteable_file.path().c_str(), newname.c_str()),
- SyscallFailsWithErrno(EPERM));
- EXPECT_THAT(link(special_path.c_str(), newname.c_str()),
- SyscallFailsWithErrno(EPERM));
- if (!IsRunningWithVFS1()) {
- EXPECT_THAT(link(setuid_file.path().c_str(), newname.c_str()),
- SyscallFailsWithErrno(EPERM));
- }
- });
-}
-
-TEST(LinkTest, CannotLinkDirectory) {
- auto olddir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir());
- const std::string newdir = NewTempAbsPath();
-
- EXPECT_THAT(link(olddir.path().c_str(), newdir.c_str()),
- SyscallFailsWithErrno(EPERM));
-
- EXPECT_THAT(rmdir(olddir.path().c_str()), SyscallSucceeds());
-}
-
-TEST(LinkTest, CannotLinkWithSlash) {
- auto oldfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
- // Put a final "/" on newname.
- const std::string newname = absl::StrCat(NewTempAbsPath(), "/");
-
- EXPECT_THAT(link(oldfile.path().c_str(), newname.c_str()),
- SyscallFailsWithErrno(ENOENT));
-}
-
-TEST(LinkTest, OldnameIsEmpty) {
- const std::string newname = NewTempAbsPath();
- EXPECT_THAT(link("", newname.c_str()), SyscallFailsWithErrno(ENOENT));
-}
-
-TEST(LinkTest, OldnameDoesNotExist) {
- const std::string oldname = NewTempAbsPath();
- const std::string newname = NewTempAbsPath();
- EXPECT_THAT(link("", newname.c_str()), SyscallFailsWithErrno(ENOENT));
-}
-
-TEST(LinkTest, NewnameCannotExist) {
- const std::string newname =
- JoinPath(GetAbsoluteTestTmpdir(), "thisdoesnotexist", "foo");
- EXPECT_THAT(link("/thisdoesnotmatter", newname.c_str()),
- SyscallFailsWithErrno(ENOENT));
-}
-
-TEST(LinkTest, WithOldDirFD) {
- const std::string oldname_parent = NewTempAbsPath();
- const std::string oldname_base = "child";
- const std::string oldname = JoinPath(oldname_parent, oldname_base);
- const std::string newname = NewTempAbsPath();
-
- // Create oldname_parent directory, and get an FD.
- ASSERT_THAT(mkdir(oldname_parent.c_str(), 0777), SyscallSucceeds());
- const FileDescriptor oldname_parent_fd =
- ASSERT_NO_ERRNO_AND_VALUE(Open(oldname_parent, O_DIRECTORY | O_RDONLY));
-
- // Create oldname file.
- const FileDescriptor oldname_fd =
- ASSERT_NO_ERRNO_AND_VALUE(Open(oldname, O_CREAT | O_RDWR, 0666));
-
- // Link oldname to newname, using oldname_parent_fd.
- EXPECT_THAT(linkat(oldname_parent_fd.get(), oldname_base.c_str(), AT_FDCWD,
- newname.c_str(), 0),
- SyscallSucceeds());
-
- EXPECT_TRUE(IsSameFile(oldname, newname));
-
- EXPECT_THAT(unlink(newname.c_str()), SyscallSucceeds());
- EXPECT_THAT(unlink(oldname.c_str()), SyscallSucceeds());
- EXPECT_THAT(rmdir(oldname_parent.c_str()), SyscallSucceeds());
-}
-
-TEST(LinkTest, BogusFlags) {
- ASSERT_THAT(linkat(1, "foo", 2, "bar", 3), SyscallFailsWithErrno(EINVAL));
-}
-
-TEST(LinkTest, WithNewDirFD) {
- auto oldfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
- const std::string newname_parent = NewTempAbsPath();
- const std::string newname_base = "child";
- const std::string newname = JoinPath(newname_parent, newname_base);
-
- // Create newname_parent directory, and get an FD.
- EXPECT_THAT(mkdir(newname_parent.c_str(), 0777), SyscallSucceeds());
- const FileDescriptor newname_parent_fd =
- ASSERT_NO_ERRNO_AND_VALUE(Open(newname_parent, O_DIRECTORY | O_RDONLY));
-
- // Link newname to oldfile, using newname_parent_fd.
- EXPECT_THAT(linkat(AT_FDCWD, oldfile.path().c_str(), newname_parent_fd.get(),
- newname.c_str(), 0),
- SyscallSucceeds());
-
- EXPECT_TRUE(IsSameFile(oldfile.path(), newname));
-
- EXPECT_THAT(unlink(newname.c_str()), SyscallSucceeds());
- EXPECT_THAT(rmdir(newname_parent.c_str()), SyscallSucceeds());
-}
-
-TEST(LinkTest, RelPathsWithNonDirFDs) {
- auto oldfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
-
- // Create a file that will be passed as the directory fd for old/new names.
- const std::string filename = NewTempAbsPath();
- const FileDescriptor file_fd =
- ASSERT_NO_ERRNO_AND_VALUE(Open(filename, O_CREAT | O_RDWR, 0666));
-
- // Using file_fd as olddirfd will fail.
- EXPECT_THAT(linkat(file_fd.get(), "foo", AT_FDCWD, "bar", 0),
- SyscallFailsWithErrno(ENOTDIR));
-
- // Using file_fd as newdirfd will fail.
- EXPECT_THAT(linkat(AT_FDCWD, oldfile.path().c_str(), file_fd.get(), "bar", 0),
- SyscallFailsWithErrno(ENOTDIR));
-}
-
-TEST(LinkTest, AbsPathsWithNonDirFDs) {
- auto oldfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
- const std::string newname = NewTempAbsPath();
-
- // Create a file that will be passed as the directory fd for old/new names.
- const std::string filename = NewTempAbsPath();
- const FileDescriptor file_fd =
- ASSERT_NO_ERRNO_AND_VALUE(Open(filename, O_CREAT | O_RDWR, 0666));
-
- // Using file_fd as the dirfds is OK as long as paths are absolute.
- EXPECT_THAT(linkat(file_fd.get(), oldfile.path().c_str(), file_fd.get(),
- newname.c_str(), 0),
- SyscallSucceeds());
-}
-
-TEST(LinkTest, NewDirFDWithOpath) {
- SKIP_IF(IsRunningWithVFS1());
- auto oldfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
- const std::string newname_parent = NewTempAbsPath();
- const std::string newname_base = "child";
- const std::string newname = JoinPath(newname_parent, newname_base);
-
- // Create newname_parent directory, and get an FD.
- EXPECT_THAT(mkdir(newname_parent.c_str(), 0777), SyscallSucceeds());
- const FileDescriptor newname_parent_fd =
- ASSERT_NO_ERRNO_AND_VALUE(Open(newname_parent, O_DIRECTORY | O_PATH));
-
- // Link newname to oldfile, using newname_parent_fd.
- EXPECT_THAT(linkat(AT_FDCWD, oldfile.path().c_str(), newname_parent_fd.get(),
- newname.c_str(), 0),
- SyscallSucceeds());
-
- EXPECT_TRUE(IsSameFile(oldfile.path(), newname));
-}
-
-TEST(LinkTest, RelPathsNonDirFDsWithOpath) {
- SKIP_IF(IsRunningWithVFS1());
- auto oldfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
-
- // Create a file that will be passed as the directory fd for old/new names.
- TempPath path = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
- FileDescriptor file_fd = ASSERT_NO_ERRNO_AND_VALUE(Open(path.path(), O_PATH));
-
- // Using file_fd as olddirfd will fail.
- EXPECT_THAT(linkat(file_fd.get(), "foo", AT_FDCWD, "bar", 0),
- SyscallFailsWithErrno(ENOTDIR));
-
- // Using file_fd as newdirfd will fail.
- EXPECT_THAT(linkat(AT_FDCWD, oldfile.path().c_str(), file_fd.get(), "bar", 0),
- SyscallFailsWithErrno(ENOTDIR));
-}
-
-TEST(LinkTest, AbsPathsNonDirFDsWithOpath) {
- SKIP_IF(IsRunningWithVFS1());
-
- auto oldfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
- const std::string newname = NewTempAbsPath();
-
- // Create a file that will be passed as the directory fd for old/new names.
- TempPath path = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
- FileDescriptor file_fd = ASSERT_NO_ERRNO_AND_VALUE(Open(path.path(), O_PATH));
-
- // Using file_fd as the dirfds is OK as long as paths are absolute.
- EXPECT_THAT(linkat(file_fd.get(), oldfile.path().c_str(), file_fd.get(),
- newname.c_str(), 0),
- SyscallSucceeds());
-}
-
-TEST(LinkTest, LinkDoesNotFollowSymlinks) {
- // Create oldfile, and oldsymlink which points to it.
- auto oldfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
- const std::string oldsymlink = NewTempAbsPath();
- EXPECT_THAT(symlink(oldfile.path().c_str(), oldsymlink.c_str()),
- SyscallSucceeds());
-
- // Now hard link newname to oldsymlink.
- const std::string newname = NewTempAbsPath();
- EXPECT_THAT(link(oldsymlink.c_str(), newname.c_str()), SyscallSucceeds());
-
- // The link should not have resolved the symlink, so newname and oldsymlink
- // are the same.
- EXPECT_TRUE(IsSameFile(oldsymlink, newname));
- EXPECT_FALSE(IsSameFile(oldfile.path(), newname));
-
- EXPECT_THAT(unlink(oldsymlink.c_str()), SyscallSucceeds());
- EXPECT_THAT(unlink(newname.c_str()), SyscallSucceeds());
-}
-
-TEST(LinkTest, LinkatDoesNotFollowSymlinkByDefault) {
- // Create oldfile, and oldsymlink which points to it.
- auto oldfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
- const std::string oldsymlink = NewTempAbsPath();
- EXPECT_THAT(symlink(oldfile.path().c_str(), oldsymlink.c_str()),
- SyscallSucceeds());
-
- // Now hard link newname to oldsymlink.
- const std::string newname = NewTempAbsPath();
- EXPECT_THAT(
- linkat(AT_FDCWD, oldsymlink.c_str(), AT_FDCWD, newname.c_str(), 0),
- SyscallSucceeds());
-
- // The link should not have resolved the symlink, so newname and oldsymlink
- // are the same.
- EXPECT_TRUE(IsSameFile(oldsymlink, newname));
- EXPECT_FALSE(IsSameFile(oldfile.path(), newname));
-
- EXPECT_THAT(unlink(oldsymlink.c_str()), SyscallSucceeds());
- EXPECT_THAT(unlink(newname.c_str()), SyscallSucceeds());
-}
-
-TEST(LinkTest, LinkatWithSymlinkFollow) {
- // Create oldfile, and oldsymlink which points to it.
- auto oldfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
- const std::string oldsymlink = NewTempAbsPath();
- ASSERT_THAT(symlink(oldfile.path().c_str(), oldsymlink.c_str()),
- SyscallSucceeds());
-
- // Now hard link newname to oldsymlink, and pass AT_SYMLINK_FOLLOW flag.
- const std::string newname = NewTempAbsPath();
- ASSERT_THAT(linkat(AT_FDCWD, oldsymlink.c_str(), AT_FDCWD, newname.c_str(),
- AT_SYMLINK_FOLLOW),
- SyscallSucceeds());
-
- // The link should have resolved the symlink, so oldfile and newname are the
- // same.
- EXPECT_TRUE(IsSameFile(oldfile.path(), newname));
- EXPECT_FALSE(IsSameFile(oldsymlink, newname));
-
- EXPECT_THAT(unlink(oldsymlink.c_str()), SyscallSucceeds());
- EXPECT_THAT(unlink(newname.c_str()), SyscallSucceeds());
-}
-
-} // namespace
-
-} // namespace testing
-} // namespace gvisor