DirIterator.cpp 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. /*
  2. * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
  3. * Copyright (c) 2023, Sam Atkins <atkinssj@serenityos.org>
  4. *
  5. * SPDX-License-Identifier: BSD-2-Clause
  6. */
  7. #include <AK/Vector.h>
  8. #include <LibCore/DirIterator.h>
  9. #include <errno.h>
  10. #include <fcntl.h>
  11. #include <sys/stat.h>
  12. namespace Core {
  13. DirIterator::DirIterator(ByteString path, Flags flags)
  14. : m_path(move(path))
  15. , m_flags(flags)
  16. {
  17. m_dir = opendir(m_path.characters());
  18. if (!m_dir) {
  19. m_error = Error::from_errno(errno);
  20. }
  21. }
  22. DirIterator::~DirIterator()
  23. {
  24. if (m_dir) {
  25. closedir(m_dir);
  26. m_dir = nullptr;
  27. }
  28. }
  29. DirIterator::DirIterator(DirIterator&& other)
  30. : m_dir(other.m_dir)
  31. , m_error(move(other.m_error))
  32. , m_next(move(other.m_next))
  33. , m_path(move(other.m_path))
  34. , m_flags(other.m_flags)
  35. {
  36. other.m_dir = nullptr;
  37. }
  38. static constexpr bool dirent_has_d_type =
  39. #if defined(AK_OS_SOLARIS) || defined(AK_OS_HAIKU)
  40. false;
  41. #else
  42. true;
  43. #endif
  44. bool DirIterator::advance_next()
  45. {
  46. if (!m_dir)
  47. return false;
  48. while (true) {
  49. errno = 0;
  50. auto* de = readdir(m_dir);
  51. if (!de) {
  52. if (errno != 0) {
  53. m_error = Error::from_errno(errno);
  54. dbgln("DirIteration error: {}", m_error.value());
  55. }
  56. m_next.clear();
  57. return false;
  58. }
  59. if constexpr (dirent_has_d_type)
  60. m_next = DirectoryEntry::from_dirent(*de);
  61. else
  62. m_next = DirectoryEntry::from_stat(m_dir, *de);
  63. if (m_next->name.is_empty())
  64. return false;
  65. if (m_flags & Flags::SkipDots && m_next->name.starts_with('.'))
  66. continue;
  67. if (m_flags & Flags::SkipParentAndBaseDir && (m_next->name == "." || m_next->name == ".."))
  68. continue;
  69. if constexpr (dirent_has_d_type) {
  70. // dirent structures from readdir aren't guaranteed to contain valid file types,
  71. // as it is possible that the underlying filesystem doesn't keep track of those.
  72. // However, if we were requested to not do stat on the entries of this directory,
  73. // the calling code will be given the raw unknown type.
  74. if ((m_flags & Flags::NoStat) == 0 && m_next->type == DirectoryEntry::Type::Unknown) {
  75. struct stat statbuf;
  76. if (fstatat(dirfd(m_dir), de->d_name, &statbuf, AT_SYMLINK_NOFOLLOW) < 0) {
  77. m_error = Error::from_errno(errno);
  78. dbgln("DirIteration error: {}", m_error.value());
  79. return false;
  80. }
  81. m_next->type = DirectoryEntry::directory_entry_type_from_stat(statbuf.st_mode);
  82. }
  83. }
  84. return !m_next->name.is_empty();
  85. }
  86. }
  87. bool DirIterator::has_next()
  88. {
  89. if (m_next.has_value())
  90. return true;
  91. return advance_next();
  92. }
  93. Optional<DirectoryEntry> DirIterator::next()
  94. {
  95. if (!m_next.has_value())
  96. advance_next();
  97. auto result = m_next;
  98. m_next.clear();
  99. return result;
  100. }
  101. ByteString DirIterator::next_path()
  102. {
  103. auto entry = next();
  104. if (entry.has_value())
  105. return entry->name;
  106. return "";
  107. }
  108. ByteString DirIterator::next_full_path()
  109. {
  110. StringBuilder builder;
  111. builder.append(m_path);
  112. if (!m_path.ends_with('/'))
  113. builder.append('/');
  114. builder.append(next_path());
  115. return builder.to_byte_string();
  116. }
  117. int DirIterator::fd() const
  118. {
  119. if (!m_dir)
  120. return -1;
  121. return dirfd(m_dir);
  122. }
  123. }