gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/test/syscalls/linux/symlink.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 <unistd.h> 19 20 #include <string> 21 22 #include "gtest/gtest.h" 23 #include "absl/time/clock.h" 24 #include "test/util/capability_util.h" 25 #include "test/util/file_descriptor.h" 26 #include "test/util/fs_util.h" 27 #include "test/util/temp_path.h" 28 #include "test/util/test_util.h" 29 30 namespace gvisor { 31 namespace testing { 32 33 namespace { 34 35 mode_t FilePermission(const std::string& path) { 36 struct stat buf = {0}; 37 TEST_CHECK(lstat(path.c_str(), &buf) == 0); 38 return buf.st_mode & 0777; 39 } 40 41 // Test that name collisions are checked on the new link path, not the source 42 // path. Regression test for b/31782115. 43 TEST(SymlinkTest, CanCreateSymlinkWithCachedSourceDirent) { 44 const std::string srcname = NewTempAbsPath(); 45 const std::string newname = NewTempAbsPath(); 46 const std::string basedir = std::string(Dirname(srcname)); 47 ASSERT_EQ(basedir, Dirname(newname)); 48 49 ASSERT_THAT(chdir(basedir.c_str()), SyscallSucceeds()); 50 51 // Open the source node to cause the underlying dirent to be cached. It will 52 // remain cached while we have the file open. 53 int fd; 54 ASSERT_THAT(fd = open(srcname.c_str(), O_CREAT | O_RDWR, 0666), 55 SyscallSucceeds()); 56 FileDescriptor fd_closer(fd); 57 58 // Attempt to create a symlink. If the bug exists, this will fail since the 59 // dirent link creation code will check for a name collision on the source 60 // link name. 61 EXPECT_THAT(symlink(std::string(Basename(srcname)).c_str(), 62 std::string(Basename(newname)).c_str()), 63 SyscallSucceeds()); 64 } 65 66 TEST(SymlinkTest, CanCreateSymlinkFile) { 67 const std::string oldname = NewTempAbsPath(); 68 const std::string newname = NewTempAbsPath(); 69 70 int fd; 71 ASSERT_THAT(fd = open(oldname.c_str(), O_CREAT | O_RDWR, 0666), 72 SyscallSucceeds()); 73 EXPECT_THAT(close(fd), SyscallSucceeds()); 74 75 EXPECT_THAT(symlink(oldname.c_str(), newname.c_str()), SyscallSucceeds()); 76 EXPECT_EQ(FilePermission(newname), 0777); 77 78 auto link = ASSERT_NO_ERRNO_AND_VALUE(ReadLink(newname)); 79 EXPECT_EQ(oldname, link); 80 81 EXPECT_THAT(unlink(newname.c_str()), SyscallSucceeds()); 82 EXPECT_THAT(unlink(oldname.c_str()), SyscallSucceeds()); 83 } 84 85 TEST(SymlinkTest, CanCreateSymlinkDir) { 86 const std::string olddir = NewTempAbsPath(); 87 const std::string newdir = NewTempAbsPath(); 88 89 EXPECT_THAT(mkdir(olddir.c_str(), 0777), SyscallSucceeds()); 90 EXPECT_THAT(symlink(olddir.c_str(), newdir.c_str()), SyscallSucceeds()); 91 EXPECT_EQ(FilePermission(newdir), 0777); 92 93 auto link = ASSERT_NO_ERRNO_AND_VALUE(ReadLink(newdir)); 94 EXPECT_EQ(olddir, link); 95 96 EXPECT_THAT(unlink(newdir.c_str()), SyscallSucceeds()); 97 98 ASSERT_THAT(rmdir(olddir.c_str()), SyscallSucceeds()); 99 } 100 101 TEST(SymlinkTest, CannotCreateSymlinkInReadOnlyDir) { 102 // Drop capabilities that allow us to override file and directory permissions. 103 AutoCapability cap1(CAP_DAC_OVERRIDE, false); 104 AutoCapability cap2(CAP_DAC_READ_SEARCH, false); 105 106 const std::string olddir = NewTempAbsPath(); 107 ASSERT_THAT(mkdir(olddir.c_str(), 0444), SyscallSucceeds()); 108 109 const std::string newdir = NewTempAbsPathInDir(olddir); 110 EXPECT_THAT(symlink(olddir.c_str(), newdir.c_str()), 111 SyscallFailsWithErrno(EACCES)); 112 113 ASSERT_THAT(rmdir(olddir.c_str()), SyscallSucceeds()); 114 } 115 116 TEST(SymlinkTest, CannotSymlinkOverExistingFile) { 117 const auto oldfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); 118 const auto newfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); 119 120 EXPECT_THAT(symlink(oldfile.path().c_str(), newfile.path().c_str()), 121 SyscallFailsWithErrno(EEXIST)); 122 } 123 124 TEST(SymlinkTest, CannotSymlinkOverExistingDir) { 125 const auto oldfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); 126 const auto newdir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); 127 128 EXPECT_THAT(symlink(oldfile.path().c_str(), newdir.path().c_str()), 129 SyscallFailsWithErrno(EEXIST)); 130 } 131 132 TEST(SymlinkTest, OldnameIsEmpty) { 133 const std::string newname = NewTempAbsPath(); 134 EXPECT_THAT(symlink("", newname.c_str()), SyscallFailsWithErrno(ENOENT)); 135 } 136 137 TEST(SymlinkTest, OldnameIsDangling) { 138 const std::string newname = NewTempAbsPath(); 139 EXPECT_THAT(symlink("/dangling", newname.c_str()), SyscallSucceeds()); 140 141 // This is required for S/R random save tests, which pre-run this test 142 // in the same TEST_TMPDIR, which means that we need to clean it for any 143 // operations exclusively creating files, like symlink above. 144 EXPECT_THAT(unlink(newname.c_str()), SyscallSucceeds()); 145 } 146 147 TEST(SymlinkTest, NewnameCannotExist) { 148 const std::string newname = 149 JoinPath(GetAbsoluteTestTmpdir(), "thisdoesnotexist", "foo"); 150 EXPECT_THAT(symlink("/thisdoesnotmatter", newname.c_str()), 151 SyscallFailsWithErrno(ENOENT)); 152 } 153 154 TEST(SymlinkTest, CanEvaluateLink) { 155 const auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); 156 157 // We are going to assert that the symlink inode id is the same as the linked 158 // file's inode id. In order for the inode id to be stable across 159 // save/restore, it must be kept open. The FileDescriptor type will do that 160 // for us automatically. 161 auto fd = ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR)); 162 struct stat file_st; 163 EXPECT_THAT(fstat(fd.get(), &file_st), SyscallSucceeds()); 164 165 const std::string link = NewTempAbsPath(); 166 EXPECT_THAT(symlink(file.path().c_str(), link.c_str()), SyscallSucceeds()); 167 EXPECT_EQ(FilePermission(link), 0777); 168 169 auto linkfd = ASSERT_NO_ERRNO_AND_VALUE(Open(link.c_str(), O_RDWR)); 170 struct stat link_st; 171 EXPECT_THAT(fstat(linkfd.get(), &link_st), SyscallSucceeds()); 172 173 // Check that in fact newname points to the file we expect. 174 EXPECT_EQ(file_st.st_dev, link_st.st_dev); 175 EXPECT_EQ(file_st.st_ino, link_st.st_ino); 176 } 177 178 TEST(SymlinkTest, TargetIsNotMapped) { 179 const std::string oldname = NewTempAbsPath(); 180 const std::string newname = NewTempAbsPath(); 181 182 int fd; 183 // Create the target so that when we read the link, it exists. 184 ASSERT_THAT(fd = open(oldname.c_str(), O_CREAT | O_RDWR, 0666), 185 SyscallSucceeds()); 186 EXPECT_THAT(close(fd), SyscallSucceeds()); 187 188 // Create a symlink called newname that points to oldname. 189 EXPECT_THAT(symlink(oldname.c_str(), newname.c_str()), SyscallSucceeds()); 190 191 std::vector<char> buf(1024); 192 int linksize; 193 // Read the link and assert that the oldname is still the same. 194 EXPECT_THAT(linksize = readlink(newname.c_str(), buf.data(), 1024), 195 SyscallSucceeds()); 196 EXPECT_EQ(0, strncmp(oldname.c_str(), buf.data(), linksize)); 197 198 EXPECT_THAT(unlink(newname.c_str()), SyscallSucceeds()); 199 EXPECT_THAT(unlink(oldname.c_str()), SyscallSucceeds()); 200 } 201 202 TEST(SymlinkTest, PreadFromSymlink) { 203 std::string name = NewTempAbsPath(); 204 int fd; 205 ASSERT_THAT(fd = open(name.c_str(), O_CREAT, 0644), SyscallSucceeds()); 206 ASSERT_THAT(close(fd), SyscallSucceeds()); 207 208 std::string linkname = NewTempAbsPath(); 209 ASSERT_THAT(symlink(name.c_str(), linkname.c_str()), SyscallSucceeds()); 210 211 ASSERT_THAT(fd = open(linkname.c_str(), O_RDONLY), SyscallSucceeds()); 212 213 char buf[1024]; 214 EXPECT_THAT(pread64(fd, buf, 1024, 0), SyscallSucceeds()); 215 EXPECT_THAT(close(fd), SyscallSucceeds()); 216 217 EXPECT_THAT(unlink(name.c_str()), SyscallSucceeds()); 218 EXPECT_THAT(unlink(linkname.c_str()), SyscallSucceeds()); 219 } 220 221 TEST(SymlinkTest, PwriteToSymlink) { 222 std::string name = NewTempAbsPath(); 223 int fd; 224 ASSERT_THAT(fd = open(name.c_str(), O_CREAT, 0644), SyscallSucceeds()); 225 ASSERT_THAT(close(fd), SyscallSucceeds()); 226 227 std::string linkname = NewTempAbsPath(); 228 ASSERT_THAT(symlink(name.c_str(), linkname.c_str()), SyscallSucceeds()); 229 230 ASSERT_THAT(fd = open(linkname.c_str(), O_WRONLY), SyscallSucceeds()); 231 232 const int data_size = 10; 233 const std::string data = std::string(data_size, 'a'); 234 EXPECT_THAT(pwrite64(fd, data.c_str(), data.size(), 0), 235 SyscallSucceedsWithValue(data.size())); 236 237 ASSERT_THAT(close(fd), SyscallSucceeds()); 238 ASSERT_THAT(fd = open(name.c_str(), O_RDONLY), SyscallSucceeds()); 239 240 char buf[data_size + 1]; 241 EXPECT_THAT(pread64(fd, buf, data.size(), 0), SyscallSucceeds()); 242 buf[data.size()] = '\0'; 243 EXPECT_STREQ(buf, data.c_str()); 244 245 ASSERT_THAT(close(fd), SyscallSucceeds()); 246 247 EXPECT_THAT(unlink(name.c_str()), SyscallSucceeds()); 248 EXPECT_THAT(unlink(linkname.c_str()), SyscallSucceeds()); 249 } 250 251 TEST(SymlinkTest, SymlinkAtDegradedPermissions) { 252 // Drop capabilities that allow us to override file and directory permissions. 253 AutoCapability cap1(CAP_DAC_OVERRIDE, false); 254 AutoCapability cap2(CAP_DAC_READ_SEARCH, false); 255 256 auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); 257 auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(dir.path())); 258 259 int dirfd; 260 ASSERT_THAT(dirfd = open(dir.path().c_str(), O_DIRECTORY, 0), 261 SyscallSucceeds()); 262 263 const DisableSave ds; // Permissions are dropped. 264 EXPECT_THAT(fchmod(dirfd, 0), SyscallSucceeds()); 265 266 std::string basename = std::string(Basename(file.path())); 267 EXPECT_THAT(symlinkat("/dangling", dirfd, basename.c_str()), 268 SyscallFailsWithErrno(EACCES)); 269 EXPECT_THAT(close(dirfd), SyscallSucceeds()); 270 } 271 272 TEST(SymlinkTest, SymlinkAtDirWithOpath) { 273 auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); 274 const std::string filepath = NewTempAbsPathInDir(dir.path()); 275 const std::string base = std::string(Basename(filepath)); 276 FileDescriptor dirfd = 277 ASSERT_NO_ERRNO_AND_VALUE(Open(dir.path().c_str(), O_DIRECTORY | O_PATH)); 278 279 EXPECT_THAT(symlinkat("/dangling", dirfd.get(), base.c_str()), 280 SyscallSucceeds()); 281 } 282 283 TEST(SymlinkTest, ReadlinkAtDirWithOpath) { 284 auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); 285 const std::string filepath = NewTempAbsPathInDir(dir.path()); 286 const std::string base = std::string(Basename(filepath)); 287 ASSERT_THAT(symlink("/dangling", filepath.c_str()), SyscallSucceeds()); 288 289 FileDescriptor dirfd = 290 ASSERT_NO_ERRNO_AND_VALUE(Open(dir.path().c_str(), O_DIRECTORY | O_PATH)); 291 292 std::vector<char> buf(1024); 293 int linksize; 294 EXPECT_THAT( 295 linksize = readlinkat(dirfd.get(), base.c_str(), buf.data(), 1024), 296 SyscallSucceeds()); 297 EXPECT_EQ(0, strncmp("/dangling", buf.data(), linksize)); 298 } 299 300 TEST(SymlinkTest, ReadlinkAtDegradedPermissions) { 301 // Drop capabilities that allow us to override file and directory permissions. 302 AutoCapability cap1(CAP_DAC_OVERRIDE, false); 303 AutoCapability cap2(CAP_DAC_READ_SEARCH, false); 304 305 auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); 306 const std::string oldpath = NewTempAbsPathInDir(dir.path()); 307 const std::string oldbase = std::string(Basename(oldpath)); 308 ASSERT_THAT(symlink("/dangling", oldpath.c_str()), SyscallSucceeds()); 309 310 int dirfd; 311 EXPECT_THAT(dirfd = open(dir.path().c_str(), O_DIRECTORY, 0), 312 SyscallSucceeds()); 313 314 const DisableSave ds; // Permissions are dropped. 315 EXPECT_THAT(fchmod(dirfd, 0), SyscallSucceeds()); 316 317 char buf[1024]; 318 int linksize; 319 EXPECT_THAT(linksize = readlinkat(dirfd, oldbase.c_str(), buf, 1024), 320 SyscallFailsWithErrno(EACCES)); 321 EXPECT_THAT(close(dirfd), SyscallSucceeds()); 322 } 323 324 TEST(SymlinkTest, ChmodSymlink) { 325 auto target = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); 326 const std::string newpath = NewTempAbsPath(); 327 ASSERT_THAT(symlink(target.path().c_str(), newpath.c_str()), 328 SyscallSucceeds()); 329 EXPECT_EQ(FilePermission(newpath), 0777); 330 EXPECT_THAT(chmod(newpath.c_str(), 0666), SyscallSucceeds()); 331 EXPECT_EQ(FilePermission(newpath), 0777); 332 } 333 334 // Test that following a symlink updates the atime on the symlink. 335 TEST(SymlinkTest, FollowUpdatesATime) { 336 const auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); 337 const std::string link = NewTempAbsPath(); 338 EXPECT_THAT(symlink(file.path().c_str(), link.c_str()), SyscallSucceeds()); 339 340 // Lstat the symlink. 341 struct stat st_before_follow; 342 ASSERT_THAT(lstat(link.c_str(), &st_before_follow), SyscallSucceeds()); 343 344 // Let the clock advance. 345 absl::SleepFor(absl::Seconds(1)); 346 347 // Open the file via the symlink. 348 int fd; 349 ASSERT_THAT(fd = open(link.c_str(), O_RDWR, 0666), SyscallSucceeds()); 350 FileDescriptor fd_closer(fd); 351 352 // Lstat the symlink again, and check that atime is updated. 353 struct stat st_after_follow; 354 ASSERT_THAT(lstat(link.c_str(), &st_after_follow), SyscallSucceeds()); 355 EXPECT_LT(st_before_follow.st_atime, st_after_follow.st_atime); 356 } 357 358 TEST(SymlinkTest, SymlinkAtEmptyPath) { 359 auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); 360 auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); 361 362 auto fd = 363 ASSERT_NO_ERRNO_AND_VALUE(Open(dir.path(), O_RDONLY | O_DIRECTORY, 0666)); 364 EXPECT_THAT(symlinkat(file.path().c_str(), fd.get(), ""), 365 SyscallFailsWithErrno(ENOENT)); 366 } 367 368 // NOTE(b/266111750): Regression test. 369 TEST(SymlinkTest, AbsoluteSymlinkDouble) { 370 const std::string symlinkPath = NewTempAbsPath(); 371 EXPECT_THAT(symlink("/", symlinkPath.c_str()), SyscallSucceeds()); 372 auto doubleSymlinkPath = symlinkPath + symlinkPath; 373 EXPECT_THAT(mkdir(doubleSymlinkPath.c_str(), 0777), 374 SyscallFailsWithErrno(EEXIST)); 375 } 376 377 class ParamSymlinkTest : public ::testing::TestWithParam<std::string> {}; 378 379 // Test that creating an existing symlink with creat will create the target. 380 TEST_P(ParamSymlinkTest, CreatLinkCreatesTarget) { 381 const std::string target = GetParam(); 382 const std::string linkpath = NewTempAbsPath(); 383 384 ASSERT_THAT(symlink(target.c_str(), linkpath.c_str()), SyscallSucceeds()); 385 386 int fd; 387 EXPECT_THAT(fd = creat(linkpath.c_str(), 0666), SyscallSucceeds()); 388 ASSERT_THAT(close(fd), SyscallSucceeds()); 389 390 ASSERT_THAT(chdir(GetAbsoluteTestTmpdir().c_str()), SyscallSucceeds()); 391 struct stat st; 392 EXPECT_THAT(stat(target.c_str(), &st), SyscallSucceeds()); 393 394 ASSERT_THAT(unlink(linkpath.c_str()), SyscallSucceeds()); 395 ASSERT_THAT(unlink(target.c_str()), SyscallSucceeds()); 396 } 397 398 // Test that opening an existing symlink with O_CREAT will create the target. 399 TEST_P(ParamSymlinkTest, OpenLinkCreatesTarget) { 400 const std::string target = GetParam(); 401 const std::string linkpath = NewTempAbsPath(); 402 403 ASSERT_THAT(symlink(target.c_str(), linkpath.c_str()), SyscallSucceeds()); 404 405 int fd; 406 EXPECT_THAT(fd = open(linkpath.c_str(), O_CREAT, 0666), SyscallSucceeds()); 407 ASSERT_THAT(close(fd), SyscallSucceeds()); 408 409 ASSERT_THAT(chdir(GetAbsoluteTestTmpdir().c_str()), SyscallSucceeds()); 410 struct stat st; 411 EXPECT_THAT(stat(target.c_str(), &st), SyscallSucceeds()); 412 413 ASSERT_THAT(unlink(linkpath.c_str()), SyscallSucceeds()); 414 ASSERT_THAT(unlink(target.c_str()), SyscallSucceeds()); 415 } 416 417 // Test that opening a self-symlink with O_CREAT will fail with ELOOP. 418 TEST_P(ParamSymlinkTest, CreateExistingSelfLink) { 419 ASSERT_THAT(chdir(GetAbsoluteTestTmpdir().c_str()), SyscallSucceeds()); 420 421 const std::string linkpath = GetParam(); 422 ASSERT_THAT(symlink(linkpath.c_str(), linkpath.c_str()), SyscallSucceeds()); 423 424 EXPECT_THAT(open(linkpath.c_str(), O_CREAT, 0666), 425 SyscallFailsWithErrno(ELOOP)); 426 427 ASSERT_THAT(unlink(linkpath.c_str()), SyscallSucceeds()); 428 } 429 430 // Test that opening a file that is a symlink to its parent directory fails 431 // with ELOOP. 432 TEST_P(ParamSymlinkTest, CreateExistingParentLink) { 433 ASSERT_THAT(chdir(GetAbsoluteTestTmpdir().c_str()), SyscallSucceeds()); 434 435 const std::string linkpath = GetParam(); 436 const std::string target = JoinPath(linkpath, "child"); 437 ASSERT_THAT(symlink(target.c_str(), linkpath.c_str()), SyscallSucceeds()); 438 439 EXPECT_THAT(open(linkpath.c_str(), O_CREAT, 0666), 440 SyscallFailsWithErrno(ELOOP)); 441 442 ASSERT_THAT(unlink(linkpath.c_str()), SyscallSucceeds()); 443 } 444 445 // Test that opening an existing symlink with O_CREAT|O_EXCL will fail with 446 // EEXIST. 447 TEST_P(ParamSymlinkTest, OpenLinkExclFails) { 448 const std::string target = GetParam(); 449 const std::string linkpath = NewTempAbsPath(); 450 451 ASSERT_THAT(symlink(target.c_str(), linkpath.c_str()), SyscallSucceeds()); 452 453 EXPECT_THAT(open(linkpath.c_str(), O_CREAT | O_EXCL, 0666), 454 SyscallFailsWithErrno(EEXIST)); 455 456 ASSERT_THAT(unlink(linkpath.c_str()), SyscallSucceeds()); 457 } 458 459 // Test that opening an existing symlink with O_CREAT|O_NOFOLLOW will fail with 460 // ELOOP. 461 TEST_P(ParamSymlinkTest, OpenLinkNoFollowFails) { 462 const std::string target = GetParam(); 463 const std::string linkpath = NewTempAbsPath(); 464 465 ASSERT_THAT(symlink(target.c_str(), linkpath.c_str()), SyscallSucceeds()); 466 467 EXPECT_THAT(open(linkpath.c_str(), O_CREAT | O_NOFOLLOW, 0666), 468 SyscallFailsWithErrno(ELOOP)); 469 470 ASSERT_THAT(unlink(linkpath.c_str()), SyscallSucceeds()); 471 } 472 473 INSTANTIATE_TEST_SUITE_P(AbsAndRelTarget, ParamSymlinkTest, 474 ::testing::Values(NewTempAbsPath(), NewTempRelPath())); 475 476 } // namespace 477 478 } // namespace testing 479 } // namespace gvisor