Line data Source code
1 : #include "envoy/common/exception.h" 2 : 3 : #include "source/common/common/fmt.h" 4 : #include "source/common/common/utility.h" 5 : #include "source/common/filesystem/directory_iterator_impl.h" 6 : 7 : #include "absl/strings/strip.h" 8 : 9 : namespace Envoy { 10 : namespace Filesystem { 11 : 12 : DirectoryIteratorImpl::DirectoryIteratorImpl(const std::string& directory_path) 13 : : directory_path_(absl::StripSuffix(directory_path, "/")), 14 37 : os_sys_calls_(Api::OsSysCallsSingleton::get()) { 15 37 : openDirectory(); 16 37 : if (status_.ok()) { 17 37 : nextEntry(); 18 37 : } else { 19 0 : entry_ = {"", FileType::Other, absl::nullopt}; 20 0 : } 21 37 : } 22 : 23 74 : DirectoryIteratorImpl::~DirectoryIteratorImpl() { 24 74 : if (dir_ != nullptr) { 25 37 : ::closedir(dir_); 26 37 : } 27 74 : } 28 : 29 111 : DirectoryIteratorImpl& DirectoryIteratorImpl::operator++() { 30 111 : nextEntry(); 31 111 : return *this; 32 111 : } 33 : 34 37 : void DirectoryIteratorImpl::openDirectory() { 35 37 : DIR* temp_dir = ::opendir(directory_path_.c_str()); 36 37 : dir_ = temp_dir; 37 37 : if (!dir_) { 38 0 : status_ = absl::ErrnoToStatus(errno, fmt::format("unable to open directory {}: {}", 39 0 : directory_path_, errorDetails(errno))); 40 0 : } 41 37 : } 42 : 43 148 : void DirectoryIteratorImpl::nextEntry() { 44 148 : errno = 0; 45 148 : dirent* entry = ::readdir(dir_); 46 : 47 148 : if (entry == nullptr) { 48 37 : entry_ = {"", FileType::Other, absl::nullopt}; 49 37 : if (errno != 0) { 50 0 : status_ = absl::ErrnoToStatus(errno, fmt::format("unable to iterate directory {}: {}", 51 0 : directory_path_, errorDetails(errno))); 52 0 : } 53 111 : } else { 54 111 : auto result = makeEntry(entry->d_name); 55 111 : status_ = result.status(); 56 111 : if (!status_.ok()) { 57 : // If we failed to stat one file it might have just been deleted, 58 : // keep iterating over the rest of the dir. 59 0 : return nextEntry(); 60 111 : } else { 61 111 : entry_ = result.value(); 62 111 : } 63 111 : } 64 148 : } 65 : 66 111 : absl::StatusOr<DirectoryEntry> DirectoryIteratorImpl::makeEntry(absl::string_view filename) const { 67 111 : const std::string full_path = absl::StrCat(directory_path_, "/", filename); 68 111 : struct stat stat_buf; 69 111 : const Api::SysCallIntResult result = os_sys_calls_.stat(full_path.c_str(), &stat_buf); 70 111 : if (result.return_value_ != 0) { 71 0 : if (result.errno_ == ENOENT) { 72 : // Special case. This directory entity is likely to be a symlink, 73 : // but the reference is broken as the target could not be stat()'ed. 74 : // If we confirm this with an lstat, treat this file entity as 75 : // a regular file, which may be unlink()'ed. 76 0 : if (::lstat(full_path.c_str(), &stat_buf) == 0 && S_ISLNK(stat_buf.st_mode)) { 77 0 : return DirectoryEntry{std::string{filename}, FileType::Regular, absl::nullopt}; 78 0 : } 79 0 : } 80 0 : return absl::ErrnoToStatus( 81 0 : result.errno_, fmt::format("unable to stat file: '{}' ({})", full_path, result.errno_)); 82 111 : } else if (S_ISDIR(stat_buf.st_mode)) { 83 74 : return DirectoryEntry{std::string{filename}, FileType::Directory, absl::nullopt}; 84 74 : } else if (S_ISREG(stat_buf.st_mode)) { 85 37 : return DirectoryEntry{std::string{filename}, FileType::Regular, 86 37 : static_cast<uint64_t>(stat_buf.st_size)}; 87 37 : } else { 88 0 : return DirectoryEntry{std::string{filename}, FileType::Other, absl::nullopt}; 89 0 : } 90 111 : } 91 : 92 : } // namespace Filesystem 93 : } // namespace Envoy