github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/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 if (!IsRunningWithVFS1()) { 112 EXPECT_THAT(link(setuid_file.path().c_str(), newname.c_str()), 113 SyscallFailsWithErrno(EPERM)); 114 } 115 }); 116 } 117 118 TEST(LinkTest, CannotLinkDirectory) { 119 auto olddir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); 120 const std::string newdir = NewTempAbsPath(); 121 122 EXPECT_THAT(link(olddir.path().c_str(), newdir.c_str()), 123 SyscallFailsWithErrno(EPERM)); 124 125 EXPECT_THAT(rmdir(olddir.path().c_str()), SyscallSucceeds()); 126 } 127 128 TEST(LinkTest, CannotLinkWithSlash) { 129 auto oldfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); 130 // Put a final "/" on newname. 131 const std::string newname = absl::StrCat(NewTempAbsPath(), "/"); 132 133 EXPECT_THAT(link(oldfile.path().c_str(), newname.c_str()), 134 SyscallFailsWithErrno(ENOENT)); 135 } 136 137 TEST(LinkTest, OldnameIsEmpty) { 138 const std::string newname = NewTempAbsPath(); 139 EXPECT_THAT(link("", newname.c_str()), SyscallFailsWithErrno(ENOENT)); 140 } 141 142 TEST(LinkTest, OldnameDoesNotExist) { 143 const std::string oldname = NewTempAbsPath(); 144 const std::string newname = NewTempAbsPath(); 145 EXPECT_THAT(link("", newname.c_str()), SyscallFailsWithErrno(ENOENT)); 146 } 147 148 TEST(LinkTest, NewnameCannotExist) { 149 const std::string newname = 150 JoinPath(GetAbsoluteTestTmpdir(), "thisdoesnotexist", "foo"); 151 EXPECT_THAT(link("/thisdoesnotmatter", newname.c_str()), 152 SyscallFailsWithErrno(ENOENT)); 153 } 154 155 TEST(LinkTest, WithOldDirFD) { 156 const std::string oldname_parent = NewTempAbsPath(); 157 const std::string oldname_base = "child"; 158 const std::string oldname = JoinPath(oldname_parent, oldname_base); 159 const std::string newname = NewTempAbsPath(); 160 161 // Create oldname_parent directory, and get an FD. 162 ASSERT_THAT(mkdir(oldname_parent.c_str(), 0777), SyscallSucceeds()); 163 const FileDescriptor oldname_parent_fd = 164 ASSERT_NO_ERRNO_AND_VALUE(Open(oldname_parent, O_DIRECTORY | O_RDONLY)); 165 166 // Create oldname file. 167 const FileDescriptor oldname_fd = 168 ASSERT_NO_ERRNO_AND_VALUE(Open(oldname, O_CREAT | O_RDWR, 0666)); 169 170 // Link oldname to newname, using oldname_parent_fd. 171 EXPECT_THAT(linkat(oldname_parent_fd.get(), oldname_base.c_str(), AT_FDCWD, 172 newname.c_str(), 0), 173 SyscallSucceeds()); 174 175 EXPECT_TRUE(IsSameFile(oldname, newname)); 176 177 EXPECT_THAT(unlink(newname.c_str()), SyscallSucceeds()); 178 EXPECT_THAT(unlink(oldname.c_str()), SyscallSucceeds()); 179 EXPECT_THAT(rmdir(oldname_parent.c_str()), SyscallSucceeds()); 180 } 181 182 TEST(LinkTest, BogusFlags) { 183 ASSERT_THAT(linkat(1, "foo", 2, "bar", 3), SyscallFailsWithErrno(EINVAL)); 184 } 185 186 TEST(LinkTest, WithNewDirFD) { 187 auto oldfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); 188 const std::string newname_parent = NewTempAbsPath(); 189 const std::string newname_base = "child"; 190 const std::string newname = JoinPath(newname_parent, newname_base); 191 192 // Create newname_parent directory, and get an FD. 193 EXPECT_THAT(mkdir(newname_parent.c_str(), 0777), SyscallSucceeds()); 194 const FileDescriptor newname_parent_fd = 195 ASSERT_NO_ERRNO_AND_VALUE(Open(newname_parent, O_DIRECTORY | O_RDONLY)); 196 197 // Link newname to oldfile, using newname_parent_fd. 198 EXPECT_THAT(linkat(AT_FDCWD, oldfile.path().c_str(), newname_parent_fd.get(), 199 newname.c_str(), 0), 200 SyscallSucceeds()); 201 202 EXPECT_TRUE(IsSameFile(oldfile.path(), newname)); 203 204 EXPECT_THAT(unlink(newname.c_str()), SyscallSucceeds()); 205 EXPECT_THAT(rmdir(newname_parent.c_str()), SyscallSucceeds()); 206 } 207 208 TEST(LinkTest, RelPathsWithNonDirFDs) { 209 auto oldfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); 210 211 // Create a file that will be passed as the directory fd for old/new names. 212 const std::string filename = NewTempAbsPath(); 213 const FileDescriptor file_fd = 214 ASSERT_NO_ERRNO_AND_VALUE(Open(filename, O_CREAT | O_RDWR, 0666)); 215 216 // Using file_fd as olddirfd will fail. 217 EXPECT_THAT(linkat(file_fd.get(), "foo", AT_FDCWD, "bar", 0), 218 SyscallFailsWithErrno(ENOTDIR)); 219 220 // Using file_fd as newdirfd will fail. 221 EXPECT_THAT(linkat(AT_FDCWD, oldfile.path().c_str(), file_fd.get(), "bar", 0), 222 SyscallFailsWithErrno(ENOTDIR)); 223 } 224 225 TEST(LinkTest, AbsPathsWithNonDirFDs) { 226 auto oldfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); 227 const std::string newname = NewTempAbsPath(); 228 229 // Create a file that will be passed as the directory fd for old/new names. 230 const std::string filename = NewTempAbsPath(); 231 const FileDescriptor file_fd = 232 ASSERT_NO_ERRNO_AND_VALUE(Open(filename, O_CREAT | O_RDWR, 0666)); 233 234 // Using file_fd as the dirfds is OK as long as paths are absolute. 235 EXPECT_THAT(linkat(file_fd.get(), oldfile.path().c_str(), file_fd.get(), 236 newname.c_str(), 0), 237 SyscallSucceeds()); 238 } 239 240 TEST(LinkTest, NewDirFDWithOpath) { 241 SKIP_IF(IsRunningWithVFS1()); 242 auto oldfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); 243 const std::string newname_parent = NewTempAbsPath(); 244 const std::string newname_base = "child"; 245 const std::string newname = JoinPath(newname_parent, newname_base); 246 247 // Create newname_parent directory, and get an FD. 248 EXPECT_THAT(mkdir(newname_parent.c_str(), 0777), SyscallSucceeds()); 249 const FileDescriptor newname_parent_fd = 250 ASSERT_NO_ERRNO_AND_VALUE(Open(newname_parent, O_DIRECTORY | O_PATH)); 251 252 // Link newname to oldfile, using newname_parent_fd. 253 EXPECT_THAT(linkat(AT_FDCWD, oldfile.path().c_str(), newname_parent_fd.get(), 254 newname.c_str(), 0), 255 SyscallSucceeds()); 256 257 EXPECT_TRUE(IsSameFile(oldfile.path(), newname)); 258 } 259 260 TEST(LinkTest, RelPathsNonDirFDsWithOpath) { 261 SKIP_IF(IsRunningWithVFS1()); 262 auto oldfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); 263 264 // Create a file that will be passed as the directory fd for old/new names. 265 TempPath path = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); 266 FileDescriptor file_fd = ASSERT_NO_ERRNO_AND_VALUE(Open(path.path(), O_PATH)); 267 268 // Using file_fd as olddirfd will fail. 269 EXPECT_THAT(linkat(file_fd.get(), "foo", AT_FDCWD, "bar", 0), 270 SyscallFailsWithErrno(ENOTDIR)); 271 272 // Using file_fd as newdirfd will fail. 273 EXPECT_THAT(linkat(AT_FDCWD, oldfile.path().c_str(), file_fd.get(), "bar", 0), 274 SyscallFailsWithErrno(ENOTDIR)); 275 } 276 277 TEST(LinkTest, AbsPathsNonDirFDsWithOpath) { 278 SKIP_IF(IsRunningWithVFS1()); 279 280 auto oldfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); 281 const std::string newname = NewTempAbsPath(); 282 283 // Create a file that will be passed as the directory fd for old/new names. 284 TempPath path = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); 285 FileDescriptor file_fd = ASSERT_NO_ERRNO_AND_VALUE(Open(path.path(), O_PATH)); 286 287 // Using file_fd as the dirfds is OK as long as paths are absolute. 288 EXPECT_THAT(linkat(file_fd.get(), oldfile.path().c_str(), file_fd.get(), 289 newname.c_str(), 0), 290 SyscallSucceeds()); 291 } 292 293 TEST(LinkTest, LinkDoesNotFollowSymlinks) { 294 // Create oldfile, and oldsymlink which points to it. 295 auto oldfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); 296 const std::string oldsymlink = NewTempAbsPath(); 297 EXPECT_THAT(symlink(oldfile.path().c_str(), oldsymlink.c_str()), 298 SyscallSucceeds()); 299 300 // Now hard link newname to oldsymlink. 301 const std::string newname = NewTempAbsPath(); 302 EXPECT_THAT(link(oldsymlink.c_str(), newname.c_str()), SyscallSucceeds()); 303 304 // The link should not have resolved the symlink, so newname and oldsymlink 305 // are the same. 306 EXPECT_TRUE(IsSameFile(oldsymlink, newname)); 307 EXPECT_FALSE(IsSameFile(oldfile.path(), newname)); 308 309 EXPECT_THAT(unlink(oldsymlink.c_str()), SyscallSucceeds()); 310 EXPECT_THAT(unlink(newname.c_str()), SyscallSucceeds()); 311 } 312 313 TEST(LinkTest, LinkatDoesNotFollowSymlinkByDefault) { 314 // Create oldfile, and oldsymlink which points to it. 315 auto oldfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); 316 const std::string oldsymlink = NewTempAbsPath(); 317 EXPECT_THAT(symlink(oldfile.path().c_str(), oldsymlink.c_str()), 318 SyscallSucceeds()); 319 320 // Now hard link newname to oldsymlink. 321 const std::string newname = NewTempAbsPath(); 322 EXPECT_THAT( 323 linkat(AT_FDCWD, oldsymlink.c_str(), AT_FDCWD, newname.c_str(), 0), 324 SyscallSucceeds()); 325 326 // The link should not have resolved the symlink, so newname and oldsymlink 327 // are the same. 328 EXPECT_TRUE(IsSameFile(oldsymlink, newname)); 329 EXPECT_FALSE(IsSameFile(oldfile.path(), newname)); 330 331 EXPECT_THAT(unlink(oldsymlink.c_str()), SyscallSucceeds()); 332 EXPECT_THAT(unlink(newname.c_str()), SyscallSucceeds()); 333 } 334 335 TEST(LinkTest, LinkatWithSymlinkFollow) { 336 // Create oldfile, and oldsymlink which points to it. 337 auto oldfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); 338 const std::string oldsymlink = NewTempAbsPath(); 339 ASSERT_THAT(symlink(oldfile.path().c_str(), oldsymlink.c_str()), 340 SyscallSucceeds()); 341 342 // Now hard link newname to oldsymlink, and pass AT_SYMLINK_FOLLOW flag. 343 const std::string newname = NewTempAbsPath(); 344 ASSERT_THAT(linkat(AT_FDCWD, oldsymlink.c_str(), AT_FDCWD, newname.c_str(), 345 AT_SYMLINK_FOLLOW), 346 SyscallSucceeds()); 347 348 // The link should have resolved the symlink, so oldfile and newname are the 349 // same. 350 EXPECT_TRUE(IsSameFile(oldfile.path(), newname)); 351 EXPECT_FALSE(IsSameFile(oldsymlink, newname)); 352 353 EXPECT_THAT(unlink(oldsymlink.c_str()), SyscallSucceeds()); 354 EXPECT_THAT(unlink(newname.c_str()), SyscallSucceeds()); 355 } 356 357 } // namespace 358 359 } // namespace testing 360 } // namespace gvisor