gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/test/syscalls/linux/stat.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 <sys/stat.h> 18 #include <sys/statfs.h> 19 #include <sys/types.h> 20 #include <unistd.h> 21 22 #include <string> 23 #include <vector> 24 25 #include "gmock/gmock.h" 26 #include "gtest/gtest.h" 27 #include "absl/strings/match.h" 28 #include "absl/strings/str_cat.h" 29 #include "absl/strings/string_view.h" 30 #include "test/syscalls/linux/file_base.h" 31 #include "test/util/cleanup.h" 32 #include "test/util/file_descriptor.h" 33 #include "test/util/fs_util.h" 34 #include "test/util/save_util.h" 35 #include "test/util/temp_path.h" 36 #include "test/util/test_util.h" 37 38 #ifndef AT_STATX_FORCE_SYNC 39 #define AT_STATX_FORCE_SYNC 0x2000 40 #endif 41 #ifndef AT_STATX_DONT_SYNC 42 #define AT_STATX_DONT_SYNC 0x4000 43 #endif 44 45 namespace gvisor { 46 namespace testing { 47 48 namespace { 49 50 class StatTest : public FileTest {}; 51 52 TEST_F(StatTest, FstatatAbs) { 53 struct stat st; 54 55 // Check that the stat works. 56 EXPECT_THAT(fstatat(AT_FDCWD, test_file_name_.c_str(), &st, 0), 57 SyscallSucceeds()); 58 EXPECT_TRUE(S_ISREG(st.st_mode)); 59 } 60 61 TEST_F(StatTest, FstatatEmptyPath) { 62 struct stat st; 63 const auto fd = ASSERT_NO_ERRNO_AND_VALUE(Open(test_file_name_, O_RDONLY)); 64 65 // Check that the stat works. 66 EXPECT_THAT(fstatat(fd.get(), "", &st, AT_EMPTY_PATH), SyscallSucceeds()); 67 EXPECT_TRUE(S_ISREG(st.st_mode)); 68 } 69 70 TEST_F(StatTest, FstatatRel) { 71 struct stat st; 72 int dirfd; 73 auto filename = std::string(Basename(test_file_name_)); 74 75 // Open the temporary directory read-only. 76 ASSERT_THAT(dirfd = open(GetAbsoluteTestTmpdir().c_str(), O_RDONLY), 77 SyscallSucceeds()); 78 79 // Check that the stat works. 80 EXPECT_THAT(fstatat(dirfd, filename.c_str(), &st, 0), SyscallSucceeds()); 81 EXPECT_TRUE(S_ISREG(st.st_mode)); 82 close(dirfd); 83 } 84 85 TEST_F(StatTest, FstatatSymlink) { 86 struct stat st; 87 88 // Check that the link is followed. 89 EXPECT_THAT(fstatat(AT_FDCWD, "/proc/self", &st, 0), SyscallSucceeds()); 90 EXPECT_TRUE(S_ISDIR(st.st_mode)); 91 EXPECT_FALSE(S_ISLNK(st.st_mode)); 92 93 // Check that the flag works. 94 EXPECT_THAT(fstatat(AT_FDCWD, "/proc/self", &st, AT_SYMLINK_NOFOLLOW), 95 SyscallSucceeds()); 96 EXPECT_TRUE(S_ISLNK(st.st_mode)); 97 EXPECT_FALSE(S_ISDIR(st.st_mode)); 98 } 99 100 TEST_F(StatTest, Nlinks) { 101 // Skip this test if we are testing overlayfs because overlayfs does not 102 // (intentionally) return the correct nlink value for directories. 103 // See fs/overlayfs/inode.c:ovl_getattr(). 104 SKIP_IF(ASSERT_NO_ERRNO_AND_VALUE(IsOverlayfs(GetAbsoluteTestTmpdir()))); 105 106 TempPath basedir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); 107 108 // Directory is initially empty, it should contain 2 links (one from itself, 109 // one from "."). 110 EXPECT_THAT(Links(basedir.path()), IsPosixErrorOkAndHolds(2)); 111 112 // Create a file in the test directory. Files shouldn't increase the link 113 // count on the base directory. 114 TempPath file1 = 115 ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(basedir.path())); 116 EXPECT_THAT(Links(basedir.path()), IsPosixErrorOkAndHolds(2)); 117 118 // Create subdirectories. This should increase the link count by 1 per 119 // subdirectory. 120 TempPath dir1 = 121 ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(basedir.path())); 122 EXPECT_THAT(Links(basedir.path()), IsPosixErrorOkAndHolds(3)); 123 TempPath dir2 = 124 ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(basedir.path())); 125 EXPECT_THAT(Links(basedir.path()), IsPosixErrorOkAndHolds(4)); 126 127 // Removing directories should reduce the link count. 128 dir1.reset(); 129 EXPECT_THAT(Links(basedir.path()), IsPosixErrorOkAndHolds(3)); 130 dir2.reset(); 131 EXPECT_THAT(Links(basedir.path()), IsPosixErrorOkAndHolds(2)); 132 133 // Removing files should have no effect on link count. 134 file1.reset(); 135 EXPECT_THAT(Links(basedir.path()), IsPosixErrorOkAndHolds(2)); 136 } 137 138 TEST_F(StatTest, BlocksIncreaseOnWrite) { 139 struct stat st; 140 141 // Stat the empty file. 142 ASSERT_THAT(fstat(test_file_fd_.get(), &st), SyscallSucceeds()); 143 144 const int initial_blocks = st.st_blocks; 145 146 // Write to the file, making sure to exceed the block size. 147 std::vector<char> buf(2 * st.st_blksize, 'a'); 148 ASSERT_THAT(write(test_file_fd_.get(), buf.data(), buf.size()), 149 SyscallSucceedsWithValue(buf.size())); 150 151 // Stat the file again, and verify that number of allocated blocks has 152 // increased. 153 ASSERT_THAT(fstat(test_file_fd_.get(), &st), SyscallSucceeds()); 154 EXPECT_GT(st.st_blocks, initial_blocks); 155 } 156 157 TEST_F(StatTest, PathNotCleaned) { 158 TempPath basedir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); 159 160 // Create a file in the basedir. 161 TempPath file = 162 ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(basedir.path())); 163 164 // Stating the file directly should succeed. 165 struct stat buf; 166 EXPECT_THAT(lstat(file.path().c_str(), &buf), SyscallSucceeds()); 167 168 // Try to stat the file using a directory that does not exist followed by 169 // "..". If the path is cleaned prior to stating (which it should not be) 170 // then this will succeed. 171 const std::string bad_path = JoinPath("/does_not_exist/..", file.path()); 172 EXPECT_THAT(lstat(bad_path.c_str(), &buf), SyscallFailsWithErrno(ENOENT)); 173 } 174 175 TEST_F(StatTest, PathCanContainDotDot) { 176 TempPath basedir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); 177 TempPath subdir = 178 ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(basedir.path())); 179 const std::string subdir_name = std::string(Basename(subdir.path())); 180 181 // Create a file in the subdir. 182 TempPath file = 183 ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(subdir.path())); 184 const std::string file_name = std::string(Basename(file.path())); 185 186 // Stat the file through a path that includes '..' and '.' but still resolves 187 // to the file. 188 const std::string good_path = 189 JoinPath(basedir.path(), subdir_name, "..", subdir_name, ".", file_name); 190 struct stat buf; 191 EXPECT_THAT(lstat(good_path.c_str(), &buf), SyscallSucceeds()); 192 } 193 194 TEST_F(StatTest, PathCanContainEmptyComponent) { 195 TempPath basedir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); 196 197 // Create a file in the basedir. 198 TempPath file = 199 ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(basedir.path())); 200 const std::string file_name = std::string(Basename(file.path())); 201 202 // Stat the file through a path that includes an empty component. We have to 203 // build this ourselves because JoinPath automatically removes empty 204 // components. 205 const std::string good_path = absl::StrCat(basedir.path(), "//", file_name); 206 struct stat buf; 207 EXPECT_THAT(lstat(good_path.c_str(), &buf), SyscallSucceeds()); 208 } 209 210 TEST_F(StatTest, TrailingSlashNotCleanedReturnsENOTDIR) { 211 TempPath basedir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); 212 213 // Create a file in the basedir. 214 TempPath file = 215 ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(basedir.path())); 216 217 // Stat the file with an extra "/" on the end of it. Since file is not a 218 // directory, this should return ENOTDIR. 219 const std::string bad_path = absl::StrCat(file.path(), "/"); 220 struct stat buf; 221 EXPECT_THAT(lstat(bad_path.c_str(), &buf), SyscallFailsWithErrno(ENOTDIR)); 222 } 223 224 TEST_F(StatTest, FstatFileWithOpath) { 225 struct stat st; 226 TempPath tmpfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); 227 FileDescriptor fd = 228 ASSERT_NO_ERRNO_AND_VALUE(Open(tmpfile.path().c_str(), O_PATH)); 229 230 // Stat the directory. 231 ASSERT_THAT(fstat(fd.get(), &st), SyscallSucceeds()); 232 } 233 234 TEST_F(StatTest, FstatDirWithOpath) { 235 struct stat st; 236 TempPath tmpdir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); 237 FileDescriptor dirfd = ASSERT_NO_ERRNO_AND_VALUE( 238 Open(tmpdir.path().c_str(), O_PATH | O_DIRECTORY)); 239 240 // Stat the directory. 241 ASSERT_THAT(fstat(dirfd.get(), &st), SyscallSucceeds()); 242 } 243 244 // fstatat with an O_PATH fd 245 TEST_F(StatTest, FstatatDirWithOpath) { 246 TempPath tmpdir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); 247 FileDescriptor dirfd = ASSERT_NO_ERRNO_AND_VALUE( 248 Open(tmpdir.path().c_str(), O_PATH | O_DIRECTORY)); 249 TempPath tmpfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); 250 251 struct stat st = {}; 252 EXPECT_THAT(fstatat(dirfd.get(), tmpfile.path().c_str(), &st, 0), 253 SyscallSucceeds()); 254 EXPECT_FALSE(S_ISDIR(st.st_mode)); 255 EXPECT_TRUE(S_ISREG(st.st_mode)); 256 } 257 258 // Test fstatating a symlink directory. 259 TEST_F(StatTest, FstatatSymlinkDir) { 260 // Create a directory and symlink to it. 261 const auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); 262 263 const std::string symlink_to_dir = NewTempAbsPath(); 264 EXPECT_THAT(symlink(dir.path().c_str(), symlink_to_dir.c_str()), 265 SyscallSucceeds()); 266 auto cleanup = Cleanup([&symlink_to_dir]() { 267 EXPECT_THAT(unlink(symlink_to_dir.c_str()), SyscallSucceeds()); 268 }); 269 270 // Fstatat the link with AT_SYMLINK_NOFOLLOW should return symlink data. 271 struct stat st = {}; 272 EXPECT_THAT( 273 fstatat(AT_FDCWD, symlink_to_dir.c_str(), &st, AT_SYMLINK_NOFOLLOW), 274 SyscallSucceeds()); 275 EXPECT_FALSE(S_ISDIR(st.st_mode)); 276 EXPECT_TRUE(S_ISLNK(st.st_mode)); 277 278 // Fstatat the link should return dir data. 279 EXPECT_THAT(fstatat(AT_FDCWD, symlink_to_dir.c_str(), &st, 0), 280 SyscallSucceeds()); 281 EXPECT_TRUE(S_ISDIR(st.st_mode)); 282 EXPECT_FALSE(S_ISLNK(st.st_mode)); 283 } 284 285 // Test fstatating a symlink directory with trailing slash. 286 TEST_F(StatTest, FstatatSymlinkDirWithTrailingSlash) { 287 // Create a directory and symlink to it. 288 const auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); 289 const std::string symlink_to_dir = NewTempAbsPath(); 290 EXPECT_THAT(symlink(dir.path().c_str(), symlink_to_dir.c_str()), 291 SyscallSucceeds()); 292 auto cleanup = Cleanup([&symlink_to_dir]() { 293 EXPECT_THAT(unlink(symlink_to_dir.c_str()), SyscallSucceeds()); 294 }); 295 296 // Fstatat on the symlink with a trailing slash should return the directory 297 // data. 298 struct stat st = {}; 299 EXPECT_THAT( 300 fstatat(AT_FDCWD, absl::StrCat(symlink_to_dir, "/").c_str(), &st, 0), 301 SyscallSucceeds()); 302 EXPECT_TRUE(S_ISDIR(st.st_mode)); 303 EXPECT_FALSE(S_ISLNK(st.st_mode)); 304 305 // Fstatat on the symlink with a trailing slash with AT_SYMLINK_NOFOLLOW 306 // should return the directory data. 307 // Symlink to directory with trailing slash will ignore AT_SYMLINK_NOFOLLOW. 308 EXPECT_THAT(fstatat(AT_FDCWD, absl::StrCat(symlink_to_dir, "/").c_str(), &st, 309 AT_SYMLINK_NOFOLLOW), 310 SyscallSucceeds()); 311 EXPECT_TRUE(S_ISDIR(st.st_mode)); 312 EXPECT_FALSE(S_ISLNK(st.st_mode)); 313 } 314 315 // Test fstatating a symlink directory with a trailing slash 316 // should return same stat data with fstatating directory. 317 TEST_F(StatTest, FstatatSymlinkDirWithTrailingSlashSameInode) { 318 // Create a directory and symlink to it. 319 const auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); 320 321 // We are going to assert that the symlink inode id is the same as the linked 322 // dir's inode id. In order for the inode id to be stable across 323 // save/restore, it must be kept open. The FileDescriptor type will do that 324 // for us automatically. 325 auto fd = ASSERT_NO_ERRNO_AND_VALUE(Open(dir.path(), O_RDONLY | O_DIRECTORY)); 326 327 const std::string symlink_to_dir = NewTempAbsPath(); 328 EXPECT_THAT(symlink(dir.path().c_str(), symlink_to_dir.c_str()), 329 SyscallSucceeds()); 330 auto cleanup = Cleanup([&symlink_to_dir]() { 331 EXPECT_THAT(unlink(symlink_to_dir.c_str()), SyscallSucceeds()); 332 }); 333 334 // Fstatat on the symlink with a trailing slash should return the directory 335 // data. 336 struct stat st = {}; 337 EXPECT_THAT(fstatat(AT_FDCWD, absl::StrCat(symlink_to_dir, "/").c_str(), &st, 338 AT_SYMLINK_NOFOLLOW), 339 SyscallSucceeds()); 340 EXPECT_TRUE(S_ISDIR(st.st_mode)); 341 342 // Dir and symlink should point to same inode. 343 struct stat st_dir = {}; 344 EXPECT_THAT( 345 fstatat(AT_FDCWD, dir.path().c_str(), &st_dir, AT_SYMLINK_NOFOLLOW), 346 SyscallSucceeds()); 347 EXPECT_EQ(st.st_ino, st_dir.st_ino); 348 } 349 350 TEST_F(StatTest, LeadingDoubleSlash) { 351 // Create a file, and make sure we can stat it. 352 TempPath file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); 353 struct stat st; 354 ASSERT_THAT(lstat(file.path().c_str(), &st), SyscallSucceeds()); 355 356 // Now add an extra leading slash. 357 const std::string double_slash_path = absl::StrCat("/", file.path()); 358 ASSERT_TRUE(absl::StartsWith(double_slash_path, "//")); 359 360 // We should be able to stat the new path, and it should resolve to the same 361 // file (same device and inode). 362 struct stat double_slash_st; 363 ASSERT_THAT(lstat(double_slash_path.c_str(), &double_slash_st), 364 SyscallSucceeds()); 365 EXPECT_EQ(st.st_dev, double_slash_st.st_dev); 366 // Inode numbers for gofer-accessed files may change across save/restore. 367 if (!IsRunningWithSaveRestore()) { 368 EXPECT_EQ(st.st_ino, double_slash_st.st_ino); 369 } 370 } 371 372 // Test that a rename doesn't change the underlying file. 373 TEST_F(StatTest, StatDoesntChangeAfterRename) { 374 const TempPath old_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); 375 const TempPath new_path(NewTempAbsPath()); 376 377 struct stat st_old = {}; 378 struct stat st_new = {}; 379 380 ASSERT_THAT(stat(old_file.path().c_str(), &st_old), SyscallSucceeds()); 381 ASSERT_THAT(rename(old_file.path().c_str(), new_path.path().c_str()), 382 SyscallSucceeds()); 383 ASSERT_THAT(stat(new_path.path().c_str(), &st_new), SyscallSucceeds()); 384 385 EXPECT_EQ(st_old.st_nlink, st_new.st_nlink); 386 EXPECT_EQ(st_old.st_dev, st_new.st_dev); 387 // Inode numbers for gofer-accessed files on which no reference is held may 388 // change across save/restore because the information that the gofer client 389 // uses to track file identity (9P QID path) is inconsistent between gofer 390 // processes, which are restarted across save/restore. 391 // 392 // Overlay filesystems may synthesize directory inode numbers on the fly. 393 if (!IsRunningWithSaveRestore() && 394 !ASSERT_NO_ERRNO_AND_VALUE(IsOverlayfs(GetAbsoluteTestTmpdir()))) { 395 EXPECT_EQ(st_old.st_ino, st_new.st_ino); 396 } 397 EXPECT_EQ(st_old.st_mode, st_new.st_mode); 398 EXPECT_EQ(st_old.st_uid, st_new.st_uid); 399 EXPECT_EQ(st_old.st_gid, st_new.st_gid); 400 EXPECT_EQ(st_old.st_size, st_new.st_size); 401 } 402 403 // Test link counts with a regular file as the child. 404 TEST_F(StatTest, LinkCountsWithRegularFileChild) { 405 const TempPath dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); 406 407 struct stat st_parent_before = {}; 408 ASSERT_THAT(stat(dir.path().c_str(), &st_parent_before), SyscallSucceeds()); 409 EXPECT_EQ(st_parent_before.st_nlink, 2); 410 411 // Adding a regular file doesn't adjust the parent's link count. 412 const TempPath child = 413 ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(dir.path())); 414 415 struct stat st_parent_after = {}; 416 ASSERT_THAT(stat(dir.path().c_str(), &st_parent_after), SyscallSucceeds()); 417 EXPECT_EQ(st_parent_after.st_nlink, 2); 418 419 // The child should have a single link from the parent. 420 struct stat st_child = {}; 421 ASSERT_THAT(stat(child.path().c_str(), &st_child), SyscallSucceeds()); 422 EXPECT_TRUE(S_ISREG(st_child.st_mode)); 423 EXPECT_EQ(st_child.st_nlink, 1); 424 425 // Finally unlinking the child should not affect the parent's link count. 426 ASSERT_THAT(unlink(child.path().c_str()), SyscallSucceeds()); 427 ASSERT_THAT(stat(dir.path().c_str(), &st_parent_after), SyscallSucceeds()); 428 EXPECT_EQ(st_parent_after.st_nlink, 2); 429 } 430 431 // This test verifies that inodes remain around when there is an open fd 432 // after link count hits 0. 433 // 434 // It is marked NoSave because we don't support saving unlinked files. 435 TEST_F(StatTest, ZeroLinksOpenFdRegularFileChild_NoSave) { 436 // Setting the enviornment variable GVISOR_GOFER_UNCACHED to any value 437 // will prevent this test from running, see the tmpfs lifecycle. 438 // 439 // We need to support this because when a file is unlinked and we forward 440 // the stat to the gofer it would return ENOENT. 441 const char* uncached_gofer = getenv("GVISOR_GOFER_UNCACHED"); 442 SKIP_IF(uncached_gofer != nullptr); 443 444 const TempPath dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); 445 const TempPath child = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( 446 dir.path(), "hello", TempPath::kDefaultFileMode)); 447 448 // The child should have a single link from the parent. 449 struct stat st_child_before = {}; 450 ASSERT_THAT(stat(child.path().c_str(), &st_child_before), SyscallSucceeds()); 451 EXPECT_TRUE(S_ISREG(st_child_before.st_mode)); 452 EXPECT_EQ(st_child_before.st_nlink, 1); 453 EXPECT_EQ(st_child_before.st_size, 5); // Hello is 5 bytes. 454 455 // Open the file so we can fstat after unlinking. 456 const FileDescriptor fd = 457 ASSERT_NO_ERRNO_AND_VALUE(Open(child.path(), O_RDONLY)); 458 459 // Now a stat should return ENOENT but we should still be able to stat 460 // via the open fd and fstat. 461 ASSERT_THAT(unlink(child.path().c_str()), SyscallSucceeds()); 462 463 // Since the file has no more links stat should fail. 464 struct stat st_child_after = {}; 465 ASSERT_THAT(stat(child.path().c_str(), &st_child_after), 466 SyscallFailsWithErrno(ENOENT)); 467 468 // Fstat should still allow us to access the same file via the fd. 469 struct stat st_child_fd = {}; 470 ASSERT_THAT(fstat(fd.get(), &st_child_fd), SyscallSucceeds()); 471 EXPECT_EQ(st_child_before.st_dev, st_child_fd.st_dev); 472 EXPECT_EQ(st_child_before.st_ino, st_child_fd.st_ino); 473 EXPECT_EQ(st_child_before.st_mode, st_child_fd.st_mode); 474 EXPECT_EQ(st_child_before.st_uid, st_child_fd.st_uid); 475 EXPECT_EQ(st_child_before.st_gid, st_child_fd.st_gid); 476 EXPECT_EQ(st_child_before.st_size, st_child_fd.st_size); 477 478 // TODO(b/34861058): This isn't ideal but since fstatfs(2) will always return 479 // OVERLAYFS_SUPER_MAGIC we have no way to know if this fs is backed by a 480 // gofer which doesn't support links. 481 EXPECT_TRUE(st_child_fd.st_nlink == 0 || st_child_fd.st_nlink == 1); 482 } 483 484 // Test link counts with a directory as the child. 485 TEST_F(StatTest, LinkCountsWithDirChild) { 486 // Skip this test if we are testing overlayfs because overlayfs does not 487 // (intentionally) return the correct nlink value for directories. 488 // See fs/overlayfs/inode.c:ovl_getattr(). 489 SKIP_IF(ASSERT_NO_ERRNO_AND_VALUE(IsOverlayfs(GetAbsoluteTestTmpdir()))); 490 491 const TempPath dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); 492 493 // Before a child is added the two links are "." and the link from the parent. 494 struct stat st_parent_before = {}; 495 ASSERT_THAT(stat(dir.path().c_str(), &st_parent_before), SyscallSucceeds()); 496 EXPECT_EQ(st_parent_before.st_nlink, 2); 497 498 // Create a subdirectory and stat for the parent link counts. 499 const TempPath sub_dir = 500 ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(dir.path())); 501 502 // The three links are ".", the link from the parent, and the link from 503 // the child as "..". 504 struct stat st_parent_after = {}; 505 ASSERT_THAT(stat(dir.path().c_str(), &st_parent_after), SyscallSucceeds()); 506 EXPECT_EQ(st_parent_after.st_nlink, 3); 507 508 // The child will have 1 link from the parent and 1 link which represents ".". 509 struct stat st_child = {}; 510 ASSERT_THAT(stat(sub_dir.path().c_str(), &st_child), SyscallSucceeds()); 511 EXPECT_TRUE(S_ISDIR(st_child.st_mode)); 512 EXPECT_EQ(st_child.st_nlink, 2); 513 514 // Finally delete the child dir and the parent link count should return to 2. 515 ASSERT_THAT(rmdir(sub_dir.path().c_str()), SyscallSucceeds()); 516 ASSERT_THAT(stat(dir.path().c_str(), &st_parent_after), SyscallSucceeds()); 517 518 // Now we should only have links from the parent and "." since the subdir 519 // has been removed. 520 EXPECT_EQ(st_parent_after.st_nlink, 2); 521 } 522 523 // Test statting a child of a non-directory. 524 TEST_F(StatTest, ChildOfNonDir) { 525 // Create a path that has a child of a regular file. 526 const std::string filename = JoinPath(test_file_name_, "child"); 527 528 // Statting the path should return ENOTDIR. 529 struct stat st; 530 EXPECT_THAT(lstat(filename.c_str(), &st), SyscallFailsWithErrno(ENOTDIR)); 531 } 532 533 // Test lstating a symlink directory. 534 TEST_F(StatTest, LstatSymlinkDir) { 535 // Create a directory and symlink to it. 536 const auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); 537 const std::string symlink_to_dir = NewTempAbsPath(); 538 EXPECT_THAT(symlink(dir.path().c_str(), symlink_to_dir.c_str()), 539 SyscallSucceeds()); 540 auto cleanup = Cleanup([&symlink_to_dir]() { 541 EXPECT_THAT(unlink(symlink_to_dir.c_str()), SyscallSucceeds()); 542 }); 543 544 // Lstat on the symlink should return symlink data. 545 struct stat st = {}; 546 ASSERT_THAT(lstat(symlink_to_dir.c_str(), &st), SyscallSucceeds()); 547 EXPECT_FALSE(S_ISDIR(st.st_mode)); 548 EXPECT_TRUE(S_ISLNK(st.st_mode)); 549 550 // Lstat on the symlink with a trailing slash should return the directory 551 // data. 552 ASSERT_THAT(lstat(absl::StrCat(symlink_to_dir, "/").c_str(), &st), 553 SyscallSucceeds()); 554 EXPECT_TRUE(S_ISDIR(st.st_mode)); 555 EXPECT_FALSE(S_ISLNK(st.st_mode)); 556 } 557 558 // Verify that we get an ELOOP from too many symbolic links even when there 559 // are directories in the middle. 560 TEST_F(StatTest, LstatELOOPPath) { 561 const TempPath dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); 562 std::string subdir_base = "subdir"; 563 ASSERT_THAT(mkdir(JoinPath(dir.path(), subdir_base).c_str(), 0755), 564 SyscallSucceeds()); 565 566 std::string target = JoinPath(dir.path(), subdir_base, subdir_base); 567 std::string dst = JoinPath("..", subdir_base); 568 ASSERT_THAT(symlink(dst.c_str(), target.c_str()), SyscallSucceeds()); 569 auto cleanup = Cleanup( 570 [&target]() { EXPECT_THAT(unlink(target.c_str()), SyscallSucceeds()); }); 571 572 // Now build a path which is /subdir/subdir/... repeated many times so that 573 // we can build a path that is shorter than PATH_MAX but can still cause 574 // too many symbolic links. Note: Every other subdir is actually a directory 575 // so we're not in a situation where it's a -> b -> a -> b, where a and b 576 // are symbolic links. 577 std::string path = dir.path(); 578 std::string subdir_append = absl::StrCat("/", subdir_base); 579 do { 580 absl::StrAppend(&path, subdir_append); 581 // Keep appending /subdir until we would overflow PATH_MAX. 582 } while ((path.size() + subdir_append.size()) < PATH_MAX); 583 584 struct stat s = {}; 585 ASSERT_THAT(lstat(path.c_str(), &s), SyscallFailsWithErrno(ELOOP)); 586 } 587 588 TEST(SimpleStatTest, DifferentFilesHaveDifferentDeviceInodeNumberPairs) { 589 TempPath file1 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); 590 TempPath file2 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); 591 592 MaybeSave(); 593 struct stat st1 = ASSERT_NO_ERRNO_AND_VALUE(Lstat(file1.path())); 594 MaybeSave(); 595 struct stat st2 = ASSERT_NO_ERRNO_AND_VALUE(Lstat(file2.path())); 596 EXPECT_FALSE(st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino) 597 << "both files have device number " << st1.st_dev << " and inode number " 598 << st1.st_ino; 599 } 600 601 // Ensure that inode allocation for anonymous devices work correctly across 602 // save/restore. In particular, inode numbers should be unique across S/R. 603 TEST(SimpleStatTest, AnonDeviceAllocatesUniqueInodesAcrossSaveRestore) { 604 // Use sockets as a convenient way to create inodes on an anonymous device. 605 int fd; 606 ASSERT_THAT(fd = socket(AF_UNIX, SOCK_STREAM, 0), SyscallSucceeds()); 607 FileDescriptor fd1(fd); 608 MaybeSave(); 609 ASSERT_THAT(fd = socket(AF_UNIX, SOCK_STREAM, 0), SyscallSucceeds()); 610 FileDescriptor fd2(fd); 611 612 struct stat st1; 613 struct stat st2; 614 ASSERT_THAT(fstat(fd1.get(), &st1), SyscallSucceeds()); 615 ASSERT_THAT(fstat(fd2.get(), &st2), SyscallSucceeds()); 616 617 // The two fds should have different inode numbers. 618 EXPECT_NE(st2.st_ino, st1.st_ino); 619 620 // Verify again after another S/R cycle. The inode numbers should remain the 621 // same. 622 MaybeSave(); 623 624 struct stat st1_after; 625 struct stat st2_after; 626 ASSERT_THAT(fstat(fd1.get(), &st1_after), SyscallSucceeds()); 627 ASSERT_THAT(fstat(fd2.get(), &st2_after), SyscallSucceeds()); 628 629 EXPECT_EQ(st1_after.st_ino, st1.st_ino); 630 EXPECT_EQ(st2_after.st_ino, st2.st_ino); 631 } 632 633 #ifndef SYS_statx 634 #if defined(__x86_64__) 635 #define SYS_statx 332 636 #elif defined(__aarch64__) 637 #define SYS_statx 291 638 #elif defined(__riscv) 639 #define SYS_statx 291 640 #else 641 #error "Unknown architecture" 642 #endif 643 #endif // SYS_statx 644 645 #ifndef STATX_ALL 646 #define STATX_ALL 0x00000fffU 647 #endif // STATX_ALL 648 649 // struct kernel_statx_timestamp is a Linux statx_timestamp struct. 650 struct kernel_statx_timestamp { 651 int64_t tv_sec; 652 uint32_t tv_nsec; 653 int32_t __reserved; 654 }; 655 656 // struct kernel_statx is a Linux statx struct. Old versions of glibc do not 657 // expose it. See include/uapi/linux/stat.h 658 struct kernel_statx { 659 uint32_t stx_mask; 660 uint32_t stx_blksize; 661 uint64_t stx_attributes; 662 uint32_t stx_nlink; 663 uint32_t stx_uid; 664 uint32_t stx_gid; 665 uint16_t stx_mode; 666 uint16_t __spare0[1]; 667 uint64_t stx_ino; 668 uint64_t stx_size; 669 uint64_t stx_blocks; 670 uint64_t stx_attributes_mask; 671 struct kernel_statx_timestamp stx_atime; 672 struct kernel_statx_timestamp stx_btime; 673 struct kernel_statx_timestamp stx_ctime; 674 struct kernel_statx_timestamp stx_mtime; 675 uint32_t stx_rdev_major; 676 uint32_t stx_rdev_minor; 677 uint32_t stx_dev_major; 678 uint32_t stx_dev_minor; 679 uint64_t __spare2[14]; 680 }; 681 682 int statx(int dirfd, const char* pathname, int flags, unsigned int mask, 683 struct kernel_statx* statxbuf) { 684 return syscall(SYS_statx, dirfd, pathname, flags, mask, statxbuf); 685 } 686 687 TEST_F(StatTest, StatxAbsPath) { 688 SKIP_IF(!IsRunningOnGvisor() && statx(-1, nullptr, 0, 0, nullptr) < 0 && 689 errno == ENOSYS); 690 691 struct kernel_statx stx; 692 EXPECT_THAT(statx(-1, test_file_name_.c_str(), 0, STATX_ALL, &stx), 693 SyscallSucceeds()); 694 EXPECT_TRUE(S_ISREG(stx.stx_mode)); 695 } 696 697 TEST_F(StatTest, StatxRelPathDirFD) { 698 SKIP_IF(!IsRunningOnGvisor() && statx(-1, nullptr, 0, 0, nullptr) < 0 && 699 errno == ENOSYS); 700 701 struct kernel_statx stx; 702 auto const dirfd = 703 ASSERT_NO_ERRNO_AND_VALUE(Open(GetAbsoluteTestTmpdir(), O_RDONLY)); 704 auto filename = std::string(Basename(test_file_name_)); 705 706 EXPECT_THAT(statx(dirfd.get(), filename.c_str(), 0, STATX_ALL, &stx), 707 SyscallSucceeds()); 708 EXPECT_TRUE(S_ISREG(stx.stx_mode)); 709 } 710 711 TEST_F(StatTest, StatxRelPathCwd) { 712 SKIP_IF(!IsRunningOnGvisor() && statx(-1, nullptr, 0, 0, nullptr) < 0 && 713 errno == ENOSYS); 714 715 ASSERT_THAT(chdir(GetAbsoluteTestTmpdir().c_str()), SyscallSucceeds()); 716 auto filename = std::string(Basename(test_file_name_)); 717 struct kernel_statx stx; 718 EXPECT_THAT(statx(AT_FDCWD, filename.c_str(), 0, STATX_ALL, &stx), 719 SyscallSucceeds()); 720 EXPECT_TRUE(S_ISREG(stx.stx_mode)); 721 } 722 723 TEST_F(StatTest, StatxEmptyPath) { 724 SKIP_IF(!IsRunningOnGvisor() && statx(-1, nullptr, 0, 0, nullptr) < 0 && 725 errno == ENOSYS); 726 727 const auto fd = ASSERT_NO_ERRNO_AND_VALUE(Open(test_file_name_, O_RDONLY)); 728 struct kernel_statx stx; 729 EXPECT_THAT(statx(fd.get(), "", AT_EMPTY_PATH, STATX_ALL, &stx), 730 SyscallSucceeds()); 731 EXPECT_TRUE(S_ISREG(stx.stx_mode)); 732 } 733 734 TEST_F(StatTest, StatxDoesNotRejectExtraneousMaskBits) { 735 SKIP_IF(!IsRunningOnGvisor() && statx(-1, nullptr, 0, 0, nullptr) < 0 && 736 errno == ENOSYS); 737 738 struct kernel_statx stx; 739 // Set all mask bits except for STATX__RESERVED. 740 uint mask = 0xffffffff & ~0x80000000; 741 EXPECT_THAT(statx(-1, test_file_name_.c_str(), 0, mask, &stx), 742 SyscallSucceeds()); 743 EXPECT_TRUE(S_ISREG(stx.stx_mode)); 744 } 745 746 TEST_F(StatTest, StatxRejectsReservedMaskBit) { 747 SKIP_IF(!IsRunningOnGvisor() && statx(-1, nullptr, 0, 0, nullptr) < 0 && 748 errno == ENOSYS); 749 750 struct kernel_statx stx; 751 // Set STATX__RESERVED in the mask. 752 EXPECT_THAT(statx(-1, test_file_name_.c_str(), 0, 0x80000000, &stx), 753 SyscallFailsWithErrno(EINVAL)); 754 } 755 756 TEST_F(StatTest, StatxSymlink) { 757 SKIP_IF(!IsRunningOnGvisor() && statx(-1, nullptr, 0, 0, nullptr) < 0 && 758 errno == ENOSYS); 759 760 std::string parent_dir = GetAbsoluteTestTmpdir(); 761 TempPath link = ASSERT_NO_ERRNO_AND_VALUE( 762 TempPath::CreateSymlinkTo(parent_dir, test_file_name_)); 763 std::string p = link.path(); 764 765 struct kernel_statx stx; 766 EXPECT_THAT(statx(AT_FDCWD, p.c_str(), AT_SYMLINK_NOFOLLOW, STATX_ALL, &stx), 767 SyscallSucceeds()); 768 EXPECT_TRUE(S_ISLNK(stx.stx_mode)); 769 EXPECT_THAT(statx(AT_FDCWD, p.c_str(), 0, STATX_ALL, &stx), 770 SyscallSucceeds()); 771 EXPECT_TRUE(S_ISREG(stx.stx_mode)); 772 } 773 774 TEST_F(StatTest, StatxInvalidFlags) { 775 SKIP_IF(!IsRunningOnGvisor() && statx(-1, nullptr, 0, 0, nullptr) < 0 && 776 errno == ENOSYS); 777 778 struct kernel_statx stx; 779 EXPECT_THAT(statx(AT_FDCWD, test_file_name_.c_str(), 12345, 0, &stx), 780 SyscallFailsWithErrno(EINVAL)); 781 782 // Sync flags are mutually exclusive. 783 EXPECT_THAT(statx(AT_FDCWD, test_file_name_.c_str(), 784 AT_STATX_FORCE_SYNC | AT_STATX_DONT_SYNC, 0, &stx), 785 SyscallFailsWithErrno(EINVAL)); 786 } 787 788 // TODO(b/270247637): AT_NO_AUTOMOUNT flag has no effect because gVisor does 789 // not support automount yet. 790 TEST_F(StatTest, StatIgnoreNoAutomount) { 791 if (IsRunningOnGvisor() || statx(-1, nullptr, 0, 0, nullptr) == 0 || 792 errno != ENOSYS) { 793 struct kernel_statx stx; 794 EXPECT_THAT( 795 statx(-1, test_file_name_.c_str(), AT_NO_AUTOMOUNT, STATX_ALL, &stx), 796 SyscallSucceeds()); 797 } 798 799 struct stat st; 800 EXPECT_THAT(fstatat(AT_FDCWD, test_file_name_.c_str(), &st, AT_NO_AUTOMOUNT), 801 SyscallSucceeds()); 802 } 803 804 } // namespace 805 806 } // namespace testing 807 } // namespace gvisor