gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/test/syscalls/linux/link.cc (about)

     1  // Copyright 2018 The gVisor Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  #include <errno.h>
    16  #include <fcntl.h>
    17  #include <string.h>
    18  #include <sys/stat.h>
    19  #include <sys/types.h>
    20  #include <unistd.h>
    21  
    22  #include <string>
    23  
    24  #include "gtest/gtest.h"
    25  #include "absl/flags/flag.h"
    26  #include "absl/strings/str_cat.h"
    27  #include "test/util/capability_util.h"
    28  #include "test/util/file_descriptor.h"
    29  #include "test/util/fs_util.h"
    30  #include "test/util/posix_error.h"
    31  #include "test/util/temp_path.h"
    32  #include "test/util/test_util.h"
    33  #include "test/util/thread_util.h"
    34  
    35  ABSL_FLAG(int32_t, scratch_uid, 65534, "scratch UID");
    36  
    37  namespace gvisor {
    38  namespace testing {
    39  
    40  namespace {
    41  
    42  // IsSameFile returns true if both filenames have the same device and inode.
    43  bool IsSameFile(const std::string& f1, const std::string& f2) {
    44    // Use lstat rather than stat, so that symlinks are not followed.
    45    struct stat stat1 = {};
    46    EXPECT_THAT(lstat(f1.c_str(), &stat1), SyscallSucceeds());
    47    struct stat stat2 = {};
    48    EXPECT_THAT(lstat(f2.c_str(), &stat2), SyscallSucceeds());
    49  
    50    return stat1.st_dev == stat2.st_dev && stat1.st_ino == stat2.st_ino;
    51  }
    52  
    53  // TODO(b/178640646): Add test for linkat with AT_EMPTY_PATH
    54  
    55  TEST(LinkTest, CanCreateLinkFile) {
    56    auto oldfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
    57    const std::string newname = NewTempAbsPath();
    58  
    59    // Get the initial link count.
    60    uint64_t initial_link_count =
    61        ASSERT_NO_ERRNO_AND_VALUE(Links(oldfile.path()));
    62  
    63    EXPECT_THAT(link(oldfile.path().c_str(), newname.c_str()), SyscallSucceeds());
    64  
    65    EXPECT_TRUE(IsSameFile(oldfile.path(), newname));
    66  
    67    // Link count should be incremented.
    68    EXPECT_THAT(Links(oldfile.path()),
    69                IsPosixErrorOkAndHolds(initial_link_count + 1));
    70  
    71    // Delete the link.
    72    EXPECT_THAT(unlink(newname.c_str()), SyscallSucceeds());
    73  
    74    // Link count should be back to initial.
    75    EXPECT_THAT(Links(oldfile.path()),
    76                IsPosixErrorOkAndHolds(initial_link_count));
    77  }
    78  
    79  TEST(LinkTest, PermissionDenied) {
    80    SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_FOWNER)));
    81  
    82    // Make the file "unsafe" to link by making it only readable, but not
    83    // writable.
    84    const auto unwriteable_file =
    85        ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileMode(0400));
    86    const std::string special_path = NewTempAbsPath();
    87    ASSERT_THAT(mkfifo(special_path.c_str(), 0666), SyscallSucceeds());
    88    const auto setuid_file =
    89        ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileMode(0666 | S_ISUID));
    90  
    91    const std::string newname = NewTempAbsPath();
    92  
    93    // Do setuid in a separate thread so that after finishing this test, the
    94    // process can still open files the test harness created before starting this
    95    // test. Otherwise, the files are created by root (UID before the test), but
    96    // cannot be opened by the `uid` set below after the test. After calling
    97    // setuid(non-zero-UID), there is no way to get root privileges back.
    98    ScopedThread([&] {
    99      // Use syscall instead of glibc setuid wrapper because we want this setuid
   100      // call to only apply to this task. POSIX threads, however, require that all
   101      // threads have the same UIDs, so using the setuid wrapper sets all threads'
   102      // real UID.
   103      // Also drops capabilities.
   104      EXPECT_THAT(syscall(SYS_setuid, absl::GetFlag(FLAGS_scratch_uid)),
   105                  SyscallSucceeds());
   106  
   107      EXPECT_THAT(link(unwriteable_file.path().c_str(), newname.c_str()),
   108                  SyscallFailsWithErrno(EPERM));
   109      EXPECT_THAT(link(special_path.c_str(), newname.c_str()),
   110                  SyscallFailsWithErrno(EPERM));
   111      EXPECT_THAT(link(setuid_file.path().c_str(), newname.c_str()),
   112                  SyscallFailsWithErrno(EPERM));
   113    });
   114  }
   115  
   116  TEST(LinkTest, CannotLinkDirectory) {
   117    auto olddir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir());
   118    const std::string newdir = NewTempAbsPath();
   119  
   120    EXPECT_THAT(link(olddir.path().c_str(), newdir.c_str()),
   121                SyscallFailsWithErrno(EPERM));
   122  
   123    EXPECT_THAT(rmdir(olddir.path().c_str()), SyscallSucceeds());
   124  }
   125  
   126  TEST(LinkTest, CannotLinkWithSlash) {
   127    auto oldfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
   128    // Put a final "/" on newname.
   129    const std::string newname = absl::StrCat(NewTempAbsPath(), "/");
   130  
   131    EXPECT_THAT(link(oldfile.path().c_str(), newname.c_str()),
   132                SyscallFailsWithErrno(ENOENT));
   133  }
   134  
   135  TEST(LinkTest, OldnameIsEmpty) {
   136    const std::string newname = NewTempAbsPath();
   137    EXPECT_THAT(link("", newname.c_str()), SyscallFailsWithErrno(ENOENT));
   138  }
   139  
   140  TEST(LinkTest, OldnameDoesNotExist) {
   141    const std::string oldname = NewTempAbsPath();
   142    const std::string newname = NewTempAbsPath();
   143    EXPECT_THAT(link(oldname.c_str(), newname.c_str()),
   144                SyscallFailsWithErrno(ENOENT));
   145  }
   146  
   147  TEST(LinkTest, NewnameCannotExist) {
   148    const std::string newname =
   149        JoinPath(GetAbsoluteTestTmpdir(), "thisdoesnotexist", "foo");
   150    EXPECT_THAT(link("/thisdoesnotmatter", newname.c_str()),
   151                SyscallFailsWithErrno(ENOENT));
   152  }
   153  
   154  TEST(LinkTest, WithOldDirFD) {
   155    const std::string oldname_parent = NewTempAbsPath();
   156    const std::string oldname_base = "child";
   157    const std::string oldname = JoinPath(oldname_parent, oldname_base);
   158    const std::string newname = NewTempAbsPath();
   159  
   160    // Create oldname_parent directory, and get an FD.
   161    ASSERT_THAT(mkdir(oldname_parent.c_str(), 0777), SyscallSucceeds());
   162    const FileDescriptor oldname_parent_fd =
   163        ASSERT_NO_ERRNO_AND_VALUE(Open(oldname_parent, O_DIRECTORY | O_RDONLY));
   164  
   165    // Create oldname file.
   166    const FileDescriptor oldname_fd =
   167        ASSERT_NO_ERRNO_AND_VALUE(Open(oldname, O_CREAT | O_RDWR, 0666));
   168  
   169    // Link oldname to newname, using oldname_parent_fd.
   170    EXPECT_THAT(linkat(oldname_parent_fd.get(), oldname_base.c_str(), AT_FDCWD,
   171                       newname.c_str(), 0),
   172                SyscallSucceeds());
   173  
   174    EXPECT_TRUE(IsSameFile(oldname, newname));
   175  
   176    EXPECT_THAT(unlink(newname.c_str()), SyscallSucceeds());
   177    EXPECT_THAT(unlink(oldname.c_str()), SyscallSucceeds());
   178    EXPECT_THAT(rmdir(oldname_parent.c_str()), SyscallSucceeds());
   179  }
   180  
   181  TEST(LinkTest, BogusFlags) {
   182    ASSERT_THAT(linkat(1, "foo", 2, "bar", 3), SyscallFailsWithErrno(EINVAL));
   183  }
   184  
   185  TEST(LinkTest, WithNewDirFD) {
   186    auto oldfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
   187    const std::string newname_parent = NewTempAbsPath();
   188    const std::string newname_base = "child";
   189    const std::string newname = JoinPath(newname_parent, newname_base);
   190  
   191    // Create newname_parent directory, and get an FD.
   192    EXPECT_THAT(mkdir(newname_parent.c_str(), 0777), SyscallSucceeds());
   193    const FileDescriptor newname_parent_fd =
   194        ASSERT_NO_ERRNO_AND_VALUE(Open(newname_parent, O_DIRECTORY | O_RDONLY));
   195  
   196    // Link newname to oldfile, using newname_parent_fd.
   197    EXPECT_THAT(linkat(AT_FDCWD, oldfile.path().c_str(), newname_parent_fd.get(),
   198                       newname.c_str(), 0),
   199                SyscallSucceeds());
   200  
   201    EXPECT_TRUE(IsSameFile(oldfile.path(), newname));
   202  
   203    EXPECT_THAT(unlink(newname.c_str()), SyscallSucceeds());
   204    EXPECT_THAT(rmdir(newname_parent.c_str()), SyscallSucceeds());
   205  }
   206  
   207  TEST(LinkTest, RelPathsWithNonDirFDs) {
   208    auto oldfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
   209  
   210    // Create a file that will be passed as the directory fd for old/new names.
   211    const std::string filename = NewTempAbsPath();
   212    const FileDescriptor file_fd =
   213        ASSERT_NO_ERRNO_AND_VALUE(Open(filename, O_CREAT | O_RDWR, 0666));
   214  
   215    // Using file_fd as olddirfd will fail.
   216    EXPECT_THAT(linkat(file_fd.get(), "foo", AT_FDCWD, "bar", 0),
   217                SyscallFailsWithErrno(ENOTDIR));
   218  
   219    // Using file_fd as newdirfd will fail.
   220    EXPECT_THAT(linkat(AT_FDCWD, oldfile.path().c_str(), file_fd.get(), "bar", 0),
   221                SyscallFailsWithErrno(ENOTDIR));
   222  }
   223  
   224  TEST(LinkTest, AbsPathsWithNonDirFDs) {
   225    auto oldfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
   226    const std::string newname = NewTempAbsPath();
   227  
   228    // Create a file that will be passed as the directory fd for old/new names.
   229    const std::string filename = NewTempAbsPath();
   230    const FileDescriptor file_fd =
   231        ASSERT_NO_ERRNO_AND_VALUE(Open(filename, O_CREAT | O_RDWR, 0666));
   232  
   233    // Using file_fd as the dirfds is OK as long as paths are absolute.
   234    EXPECT_THAT(linkat(file_fd.get(), oldfile.path().c_str(), file_fd.get(),
   235                       newname.c_str(), 0),
   236                SyscallSucceeds());
   237  }
   238  
   239  TEST(LinkTest, NewDirFDWithOpath) {
   240    auto oldfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
   241    const std::string newname_parent = NewTempAbsPath();
   242    const std::string newname_base = "child";
   243    const std::string newname = JoinPath(newname_parent, newname_base);
   244  
   245    // Create newname_parent directory, and get an FD.
   246    EXPECT_THAT(mkdir(newname_parent.c_str(), 0777), SyscallSucceeds());
   247    const FileDescriptor newname_parent_fd =
   248        ASSERT_NO_ERRNO_AND_VALUE(Open(newname_parent, O_DIRECTORY | O_PATH));
   249  
   250    // Link newname to oldfile, using newname_parent_fd.
   251    EXPECT_THAT(linkat(AT_FDCWD, oldfile.path().c_str(), newname_parent_fd.get(),
   252                       newname.c_str(), 0),
   253                SyscallSucceeds());
   254  
   255    EXPECT_TRUE(IsSameFile(oldfile.path(), newname));
   256  }
   257  
   258  TEST(LinkTest, RelPathsNonDirFDsWithOpath) {
   259    auto oldfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
   260  
   261    // Create a file that will be passed as the directory fd for old/new names.
   262    TempPath path = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
   263    FileDescriptor file_fd = ASSERT_NO_ERRNO_AND_VALUE(Open(path.path(), O_PATH));
   264  
   265    // Using file_fd as olddirfd will fail.
   266    EXPECT_THAT(linkat(file_fd.get(), "foo", AT_FDCWD, "bar", 0),
   267                SyscallFailsWithErrno(ENOTDIR));
   268  
   269    // Using file_fd as newdirfd will fail.
   270    EXPECT_THAT(linkat(AT_FDCWD, oldfile.path().c_str(), file_fd.get(), "bar", 0),
   271                SyscallFailsWithErrno(ENOTDIR));
   272  }
   273  
   274  TEST(LinkTest, AbsPathsNonDirFDsWithOpath) {
   275    auto oldfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
   276    const std::string newname = NewTempAbsPath();
   277  
   278    // Create a file that will be passed as the directory fd for old/new names.
   279    TempPath path = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
   280    FileDescriptor file_fd = ASSERT_NO_ERRNO_AND_VALUE(Open(path.path(), O_PATH));
   281  
   282    // Using file_fd as the dirfds is OK as long as paths are absolute.
   283    EXPECT_THAT(linkat(file_fd.get(), oldfile.path().c_str(), file_fd.get(),
   284                       newname.c_str(), 0),
   285                SyscallSucceeds());
   286  }
   287  
   288  TEST(LinkTest, LinkDoesNotFollowSymlinks) {
   289    // Create oldfile, and oldsymlink which points to it.
   290    auto oldfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
   291    const std::string oldsymlink = NewTempAbsPath();
   292    EXPECT_THAT(symlink(oldfile.path().c_str(), oldsymlink.c_str()),
   293                SyscallSucceeds());
   294  
   295    // Now hard link newname to oldsymlink.
   296    const std::string newname = NewTempAbsPath();
   297    EXPECT_THAT(link(oldsymlink.c_str(), newname.c_str()), SyscallSucceeds());
   298  
   299    // The link should not have resolved the symlink, so newname and oldsymlink
   300    // are the same.
   301    EXPECT_TRUE(IsSameFile(oldsymlink, newname));
   302    EXPECT_FALSE(IsSameFile(oldfile.path(), newname));
   303  
   304    EXPECT_THAT(unlink(oldsymlink.c_str()), SyscallSucceeds());
   305    EXPECT_THAT(unlink(newname.c_str()), SyscallSucceeds());
   306  }
   307  
   308  TEST(LinkTest, LinkatDoesNotFollowSymlinkByDefault) {
   309    // Create oldfile, and oldsymlink which points to it.
   310    auto oldfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
   311    const std::string oldsymlink = NewTempAbsPath();
   312    EXPECT_THAT(symlink(oldfile.path().c_str(), oldsymlink.c_str()),
   313                SyscallSucceeds());
   314  
   315    // Now hard link newname to oldsymlink.
   316    const std::string newname = NewTempAbsPath();
   317    EXPECT_THAT(
   318        linkat(AT_FDCWD, oldsymlink.c_str(), AT_FDCWD, newname.c_str(), 0),
   319        SyscallSucceeds());
   320  
   321    // The link should not have resolved the symlink, so newname and oldsymlink
   322    // are the same.
   323    EXPECT_TRUE(IsSameFile(oldsymlink, newname));
   324    EXPECT_FALSE(IsSameFile(oldfile.path(), newname));
   325  
   326    EXPECT_THAT(unlink(oldsymlink.c_str()), SyscallSucceeds());
   327    EXPECT_THAT(unlink(newname.c_str()), SyscallSucceeds());
   328  }
   329  
   330  TEST(LinkTest, LinkatWithSymlinkFollow) {
   331    // Create oldfile, and oldsymlink which points to it.
   332    auto oldfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
   333    const std::string oldsymlink = NewTempAbsPath();
   334    ASSERT_THAT(symlink(oldfile.path().c_str(), oldsymlink.c_str()),
   335                SyscallSucceeds());
   336  
   337    // Now hard link newname to oldsymlink, and pass AT_SYMLINK_FOLLOW flag.
   338    const std::string newname = NewTempAbsPath();
   339    ASSERT_THAT(linkat(AT_FDCWD, oldsymlink.c_str(), AT_FDCWD, newname.c_str(),
   340                       AT_SYMLINK_FOLLOW),
   341                SyscallSucceeds());
   342  
   343    // The link should have resolved the symlink, so oldfile and newname are the
   344    // same.
   345    EXPECT_TRUE(IsSameFile(oldfile.path(), newname));
   346    EXPECT_FALSE(IsSameFile(oldsymlink, newname));
   347  
   348    EXPECT_THAT(unlink(oldsymlink.c_str()), SyscallSucceeds());
   349    EXPECT_THAT(unlink(newname.c_str()), SyscallSucceeds());
   350  }
   351  
   352  TEST(LinkTest, KernfsAcrossFilesystem) {
   353    auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
   354    EXPECT_THAT(link(file.path().c_str(), "/sys/newfile"),
   355                SyscallFailsWithErrno(::testing::AnyOf(EROFS, EXDEV)));
   356  }
   357  
   358  }  // namespace
   359  
   360  }  // namespace testing
   361  }  // namespace gvisor