github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/test/syscalls/linux/getdents.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 <dirent.h> 16 #include <errno.h> 17 #include <fcntl.h> 18 #include <stddef.h> 19 #include <stdint.h> 20 #include <stdio.h> 21 #include <string.h> 22 #include <sys/mman.h> 23 #include <sys/types.h> 24 #include <syscall.h> 25 #include <unistd.h> 26 27 #include <map> 28 #include <string> 29 #include <unordered_map> 30 #include <unordered_set> 31 #include <utility> 32 33 #include "gmock/gmock.h" 34 #include "gtest/gtest.h" 35 #include "absl/container/node_hash_map.h" 36 #include "absl/container/node_hash_set.h" 37 #include "absl/strings/numbers.h" 38 #include "absl/strings/str_cat.h" 39 #include "test/util/eventfd_util.h" 40 #include "test/util/file_descriptor.h" 41 #include "test/util/fs_util.h" 42 #include "test/util/posix_error.h" 43 #include "test/util/temp_path.h" 44 #include "test/util/test_util.h" 45 46 using ::testing::Contains; 47 using ::testing::IsEmpty; 48 using ::testing::IsSupersetOf; 49 using ::testing::Not; 50 using ::testing::NotNull; 51 52 namespace gvisor { 53 namespace testing { 54 55 namespace { 56 57 // New Linux dirent format. 58 struct linux_dirent64 { 59 uint64_t d_ino; // Inode number 60 int64_t d_off; // Offset to next linux_dirent64 61 unsigned short d_reclen; // NOLINT, Length of this linux_dirent64 62 unsigned char d_type; // NOLINT, File type 63 char d_name[0]; // Filename (null-terminated) 64 }; 65 66 // Old Linux dirent format. 67 struct linux_dirent { 68 unsigned long d_ino; // NOLINT 69 unsigned long d_off; // NOLINT 70 unsigned short d_reclen; // NOLINT 71 char d_name[0]; 72 }; 73 74 // Wraps a buffer to provide a set of dirents. 75 // T is the underlying dirent type. 76 template <typename T> 77 class DirentBuffer { 78 public: 79 // DirentBuffer manages the buffer. 80 explicit DirentBuffer(size_t size) 81 : managed_(true), actual_size_(size), reported_size_(size) { 82 data_ = new char[actual_size_]; 83 } 84 85 // The buffer is managed externally. 86 DirentBuffer(char* data, size_t actual_size, size_t reported_size) 87 : managed_(false), 88 data_(data), 89 actual_size_(actual_size), 90 reported_size_(reported_size) {} 91 92 ~DirentBuffer() { 93 if (managed_) { 94 delete[] data_; 95 } 96 } 97 98 T* Data() { return reinterpret_cast<T*>(data_); } 99 100 T* Start(size_t read) { 101 read_ = read; 102 if (read_) { 103 return Data(); 104 } else { 105 return nullptr; 106 } 107 } 108 109 T* Current() { return reinterpret_cast<T*>(&data_[off_]); } 110 111 T* Next() { 112 size_t new_off = off_ + Current()->d_reclen; 113 if (new_off >= read_ || new_off >= actual_size_) { 114 return nullptr; 115 } 116 117 off_ = new_off; 118 return Current(); 119 } 120 121 size_t Size() { return reported_size_; } 122 123 void Reset() { 124 off_ = 0; 125 read_ = 0; 126 memset(data_, 0, actual_size_); 127 } 128 129 private: 130 bool managed_; 131 char* data_; 132 size_t actual_size_; 133 size_t reported_size_; 134 135 size_t off_ = 0; 136 137 size_t read_ = 0; 138 }; 139 140 // Test for getdents/getdents64. 141 // T is the Linux dirent type. 142 template <typename T> 143 class GetdentsTest : public ::testing::Test { 144 public: 145 using LinuxDirentType = T; 146 using DirentBufferType = DirentBuffer<T>; 147 148 protected: 149 void SetUp() override { 150 dir_ = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); 151 fd_ = ASSERT_NO_ERRNO_AND_VALUE(Open(dir_.path(), O_RDONLY | O_DIRECTORY)); 152 } 153 154 // Must be overridden with explicit specialization. See below. 155 int SyscallNum(); 156 157 int Getdents(LinuxDirentType* dirp, unsigned int count) { 158 return RetryEINTR(syscall)(SyscallNum(), fd_.get(), dirp, count); 159 } 160 161 // Fill directory with num files, named by number starting at 0. 162 void FillDirectory(size_t num) { 163 for (size_t i = 0; i < num; i++) { 164 auto name = JoinPath(dir_.path(), absl::StrCat(i)); 165 TEST_CHECK(CreateWithContents(name, "").ok()); 166 } 167 } 168 169 // Fill directory with a given list of filenames. 170 void FillDirectoryWithFiles(const std::vector<std::string>& filenames) { 171 for (const auto& filename : filenames) { 172 auto name = JoinPath(dir_.path(), filename); 173 TEST_CHECK(CreateWithContents(name, "").ok()); 174 } 175 } 176 177 // Seek to the start of the directory. 178 PosixError SeekStart() { 179 constexpr off_t kStartOfFile = 0; 180 off_t offset = lseek(fd_.get(), kStartOfFile, SEEK_SET); 181 if (offset < 0) { 182 return PosixError(errno, absl::StrCat("error seeking to ", kStartOfFile)); 183 } 184 if (offset != kStartOfFile) { 185 return PosixError(EINVAL, absl::StrCat("tried to seek to ", kStartOfFile, 186 " but got ", offset)); 187 } 188 return NoError(); 189 } 190 191 // Call getdents multiple times, reading all dirents and calling f on each. 192 // f has the type signature PosixError f(T*). 193 // If f returns a non-OK error, so does ReadDirents. 194 template <typename F> 195 PosixError ReadDirents(DirentBufferType* dirents, F const& f) { 196 int n; 197 do { 198 dirents->Reset(); 199 200 n = Getdents(dirents->Data(), dirents->Size()); 201 MaybeSave(); 202 if (n < 0) { 203 return PosixError(errno, "getdents"); 204 } 205 206 for (auto d = dirents->Start(n); d; d = dirents->Next()) { 207 RETURN_IF_ERRNO(f(d)); 208 } 209 } while (n > 0); 210 211 return NoError(); 212 } 213 214 // Call Getdents successively and count all entries. 215 int ReadAndCountAllEntries(DirentBufferType* dirents) { 216 int found = 0; 217 218 EXPECT_NO_ERRNO(ReadDirents(dirents, [&](LinuxDirentType* d) { 219 found++; 220 return NoError(); 221 })); 222 223 return found; 224 } 225 226 private: 227 TempPath dir_; 228 FileDescriptor fd_; 229 }; 230 231 // Multiple template parameters are not allowed, so we must use explicit 232 // template specialization to set the syscall number. 233 234 // SYS_getdents isn't defined on arm64. 235 #ifdef __x86_64__ 236 template <> 237 int GetdentsTest<struct linux_dirent>::SyscallNum() { 238 return SYS_getdents; 239 } 240 #endif 241 242 template <> 243 int GetdentsTest<struct linux_dirent64>::SyscallNum() { 244 return SYS_getdents64; 245 } 246 247 #ifdef __x86_64__ 248 // Test both legacy getdents and getdents64 on x86_64. 249 typedef ::testing::Types<struct linux_dirent, struct linux_dirent64> 250 GetdentsTypes; 251 #elif __aarch64__ 252 // Test only getdents64 on arm64. 253 typedef ::testing::Types<struct linux_dirent64> GetdentsTypes; 254 #endif 255 TYPED_TEST_SUITE(GetdentsTest, GetdentsTypes); 256 257 // N.B. TYPED_TESTs require explicitly using this-> to access members of 258 // GetdentsTest, since we are inside of a derived class template. 259 260 TYPED_TEST(GetdentsTest, VerifyEntries) { 261 typename TestFixture::DirentBufferType dirents(1024); 262 263 this->FillDirectory(2); 264 265 // Map of all the entries we expect to find. 266 std::map<std::string, bool> found; 267 found["."] = false; 268 found[".."] = false; 269 found["0"] = false; 270 found["1"] = false; 271 272 EXPECT_NO_ERRNO(this->ReadDirents( 273 &dirents, [&](typename TestFixture::LinuxDirentType* d) { 274 auto kv = found.find(d->d_name); 275 EXPECT_NE(kv, found.end()) << "Unexpected file: " << d->d_name; 276 if (kv != found.end()) { 277 EXPECT_FALSE(kv->second); 278 } 279 found[d->d_name] = true; 280 return NoError(); 281 })); 282 283 for (auto& kv : found) { 284 EXPECT_TRUE(kv.second) << "File not found: " << kv.first; 285 } 286 } 287 288 TYPED_TEST(GetdentsTest, VerifyPadding) { 289 typename TestFixture::DirentBufferType dirents(1024); 290 291 // Create files with names of length 1 through 16. 292 std::vector<std::string> files; 293 std::string filename; 294 for (int i = 0; i < 16; ++i) { 295 absl::StrAppend(&filename, "a"); 296 files.push_back(filename); 297 } 298 this->FillDirectoryWithFiles(files); 299 300 // We expect to find all the files, plus '.' and '..'. 301 const int expect_found = 2 + files.size(); 302 int found = 0; 303 304 EXPECT_NO_ERRNO(this->ReadDirents( 305 &dirents, [&](typename TestFixture::LinuxDirentType* d) { 306 EXPECT_EQ(d->d_reclen % 8, 0) 307 << "Dirent " << d->d_name 308 << " had reclen that was not byte aligned: " << d->d_name; 309 found++; 310 return NoError(); 311 })); 312 313 // Make sure we found all the files. 314 EXPECT_EQ(found, expect_found); 315 } 316 317 // For a small directory, the provided buffer should be large enough 318 // for all entries. 319 TYPED_TEST(GetdentsTest, SmallDir) { 320 // . and .. should be in an otherwise empty directory. 321 int expect = 2; 322 323 // Add some actual files. 324 this->FillDirectory(2); 325 expect += 2; 326 327 typename TestFixture::DirentBufferType dirents(256); 328 329 EXPECT_EQ(expect, this->ReadAndCountAllEntries(&dirents)); 330 } 331 332 // A directory with lots of files requires calling getdents multiple times. 333 TYPED_TEST(GetdentsTest, LargeDir) { 334 // . and .. should be in an otherwise empty directory. 335 int expect = 2; 336 337 // Add some actual files. 338 this->FillDirectory(100); 339 expect += 100; 340 341 typename TestFixture::DirentBufferType dirents(256); 342 343 EXPECT_EQ(expect, this->ReadAndCountAllEntries(&dirents)); 344 } 345 346 // If we lie about the size of the buffer, we should still be able to read the 347 // entries with the available space. 348 TYPED_TEST(GetdentsTest, PartialBuffer) { 349 // . and .. should be in an otherwise empty directory. 350 int expect = 2; 351 352 // Add some actual files. 353 this->FillDirectory(100); 354 expect += 100; 355 356 void* addr = mmap(0, 2 * kPageSize, PROT_READ | PROT_WRITE, 357 MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); 358 ASSERT_NE(addr, MAP_FAILED); 359 360 char* buf = reinterpret_cast<char*>(addr); 361 362 // Guard page 363 EXPECT_THAT( 364 mprotect(reinterpret_cast<void*>(buf + kPageSize), kPageSize, PROT_NONE), 365 SyscallSucceeds()); 366 367 // Limit space in buf to 256 bytes. 368 buf += kPageSize - 256; 369 370 // Lie about the buffer. Even though we claim the buffer is 1 page, 371 // we should still get all of the dirents in the first 256 bytes. 372 typename TestFixture::DirentBufferType dirents(buf, 256, kPageSize); 373 374 EXPECT_EQ(expect, this->ReadAndCountAllEntries(&dirents)); 375 376 EXPECT_THAT(munmap(addr, 2 * kPageSize), SyscallSucceeds()); 377 } 378 379 // Open many file descriptors, then scan through /proc/self/fd to find and close 380 // them all. (The latter is commonly used to handle races between fork/execve 381 // and the creation of unwanted non-O_CLOEXEC file descriptors.) This tests that 382 // getdents iterates correctly despite mutation of /proc/self/fd. 383 TYPED_TEST(GetdentsTest, ProcSelfFd) { 384 constexpr size_t kNfds = 10; 385 absl::node_hash_map<int, FileDescriptor> fds; 386 fds.reserve(kNfds); 387 for (size_t i = 0; i < kNfds; i++) { 388 FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(NewEventFD()); 389 fds.emplace(fd.get(), std::move(fd)); 390 } 391 392 const FileDescriptor proc_self_fd = 393 ASSERT_NO_ERRNO_AND_VALUE(Open("/proc/self/fd", O_RDONLY | O_DIRECTORY)); 394 395 // Make the buffer very small since we want to iterate. 396 typename TestFixture::DirentBufferType dirents( 397 2 * sizeof(typename TestFixture::LinuxDirentType)); 398 absl::node_hash_set<int> prev_fds; 399 while (true) { 400 dirents.Reset(); 401 int rv; 402 ASSERT_THAT(rv = RetryEINTR(syscall)(this->SyscallNum(), proc_self_fd.get(), 403 dirents.Data(), dirents.Size()), 404 SyscallSucceeds()); 405 if (rv == 0) { 406 break; 407 } 408 for (auto* d = dirents.Start(rv); d; d = dirents.Next()) { 409 int dfd; 410 if (!absl::SimpleAtoi(d->d_name, &dfd)) continue; 411 EXPECT_TRUE(prev_fds.insert(dfd).second) 412 << "Repeated observation of /proc/self/fd/" << dfd; 413 fds.erase(dfd); 414 } 415 } 416 417 // Check that we closed every fd. 418 EXPECT_THAT(fds, ::testing::IsEmpty()); 419 } 420 421 // Test that getdents returns ENOTDIR when called on a file. 422 TYPED_TEST(GetdentsTest, NotDir) { 423 auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); 424 auto fd = ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDONLY)); 425 426 typename TestFixture::DirentBufferType dirents(256); 427 EXPECT_THAT(RetryEINTR(syscall)(this->SyscallNum(), fd.get(), dirents.Data(), 428 dirents.Size()), 429 SyscallFailsWithErrno(ENOTDIR)); 430 } 431 432 // Test that getdents returns EBADF when called on an opath file. 433 TYPED_TEST(GetdentsTest, OpathFile) { 434 SKIP_IF(IsRunningWithVFS1()); 435 436 auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); 437 auto fd = ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_PATH)); 438 439 typename TestFixture::DirentBufferType dirents(256); 440 EXPECT_THAT(RetryEINTR(syscall)(this->SyscallNum(), fd.get(), dirents.Data(), 441 dirents.Size()), 442 SyscallFailsWithErrno(EBADF)); 443 } 444 445 // Test that getdents returns EBADF when called on an opath directory. 446 TYPED_TEST(GetdentsTest, OpathDirectory) { 447 SKIP_IF(IsRunningWithVFS1()); 448 449 auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); 450 auto fd = ASSERT_NO_ERRNO_AND_VALUE(Open(dir.path(), O_PATH | O_DIRECTORY)); 451 452 typename TestFixture::DirentBufferType dirents(256); 453 ASSERT_THAT(RetryEINTR(syscall)(this->SyscallNum(), fd.get(), dirents.Data(), 454 dirents.Size()), 455 SyscallFailsWithErrno(EBADF)); 456 } 457 458 // Test that SEEK_SET to 0 causes getdents to re-read the entries. 459 TYPED_TEST(GetdentsTest, SeekResetsCursor) { 460 // . and .. should be in an otherwise empty directory. 461 int expect = 2; 462 463 // Add some files to the directory. 464 this->FillDirectory(10); 465 expect += 10; 466 467 typename TestFixture::DirentBufferType dirents(256); 468 469 // We should get all the expected entries. 470 EXPECT_EQ(expect, this->ReadAndCountAllEntries(&dirents)); 471 472 // Seek back to 0. 473 ASSERT_NO_ERRNO(this->SeekStart()); 474 475 // We should get all the expected entries again. 476 EXPECT_EQ(expect, this->ReadAndCountAllEntries(&dirents)); 477 } 478 479 // Test that getdents() after SEEK_END succeeds. 480 // This is a regression test for #128. 481 TYPED_TEST(GetdentsTest, Issue128ProcSeekEnd) { 482 auto fd = 483 ASSERT_NO_ERRNO_AND_VALUE(Open("/proc/self", O_RDONLY | O_DIRECTORY)); 484 typename TestFixture::DirentBufferType dirents(256); 485 486 ASSERT_THAT(lseek(fd.get(), 0, SEEK_END), SyscallSucceeds()); 487 ASSERT_THAT(RetryEINTR(syscall)(this->SyscallNum(), fd.get(), dirents.Data(), 488 dirents.Size()), 489 SyscallSucceeds()); 490 } 491 492 // Some tests using the glibc readdir interface. 493 TEST(ReaddirTest, OpenDir) { 494 DIR* dev; 495 ASSERT_THAT(dev = opendir("/dev"), NotNull()); 496 EXPECT_THAT(closedir(dev), SyscallSucceeds()); 497 } 498 499 TEST(ReaddirTest, RootContainsBasicDirectories) { 500 EXPECT_THAT(ListDir("/", true), 501 IsPosixErrorOkAndHolds(IsSupersetOf( 502 {"bin", "dev", "etc", "lib", "proc", "sbin", "usr"}))); 503 } 504 505 TEST(ReaddirTest, Bug24096713Dev) { 506 auto contents = ASSERT_NO_ERRNO_AND_VALUE(ListDir("/dev", true)); 507 EXPECT_THAT(contents, Not(IsEmpty())); 508 } 509 510 TEST(ReaddirTest, Bug24096713ProcTid) { 511 auto contents = ASSERT_NO_ERRNO_AND_VALUE( 512 ListDir(absl::StrCat("/proc/", syscall(SYS_gettid), "/"), true)); 513 EXPECT_THAT(contents, Not(IsEmpty())); 514 } 515 516 TEST(ReaddirTest, Bug33429925Proc) { 517 auto contents = ASSERT_NO_ERRNO_AND_VALUE(ListDir("/proc", true)); 518 EXPECT_THAT(contents, Not(IsEmpty())); 519 } 520 521 TEST(ReaddirTest, Bug35110122Root) { 522 auto contents = ASSERT_NO_ERRNO_AND_VALUE(ListDir("/", true)); 523 EXPECT_THAT(contents, Not(IsEmpty())); 524 } 525 526 // Unlink should invalidate getdents cache. 527 TEST(ReaddirTest, GoneAfterRemoveCache) { 528 TempPath dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); 529 TempPath file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(dir.path())); 530 std::string name = std::string(Basename(file.path())); 531 532 auto contents = ASSERT_NO_ERRNO_AND_VALUE(ListDir(dir.path(), true)); 533 EXPECT_THAT(contents, Contains(name)); 534 535 file.reset(); 536 537 contents = ASSERT_NO_ERRNO_AND_VALUE(ListDir(dir.path(), true)); 538 EXPECT_THAT(contents, Not(Contains(name))); 539 } 540 541 // Regression test for b/137398511. Rename should invalidate getdents cache. 542 TEST(ReaddirTest, GoneAfterRenameCache) { 543 TempPath src = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); 544 TempPath dst = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); 545 546 TempPath file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(src.path())); 547 std::string name = std::string(Basename(file.path())); 548 549 auto contents = ASSERT_NO_ERRNO_AND_VALUE(ListDir(src.path(), true)); 550 EXPECT_THAT(contents, Contains(name)); 551 552 ASSERT_THAT(rename(file.path().c_str(), JoinPath(dst.path(), name).c_str()), 553 SyscallSucceeds()); 554 // Release file since it was renamed. dst cleanup will ultimately delete it. 555 file.release(); 556 557 contents = ASSERT_NO_ERRNO_AND_VALUE(ListDir(src.path(), true)); 558 EXPECT_THAT(contents, Not(Contains(name))); 559 560 contents = ASSERT_NO_ERRNO_AND_VALUE(ListDir(dst.path(), true)); 561 EXPECT_THAT(contents, Contains(name)); 562 } 563 564 } // namespace 565 566 } // namespace testing 567 } // namespace gvisor