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