/* * libfud * Copyright 2024 Dominick Allen * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "fud_directory.hpp" #include #include #include namespace fud { using CStat = struct stat; struct Stat : public CStat {}; Result DirectoryEntry::fromStat(const String& name, const Stat& statBuffer) { using RetType = Result; static_assert(std::is_same_v); static_assert(sizeof(decltype(statBuffer.st_ino)) <= sizeof(size_t)); static_assert(std::is_same_v); static_assert(sizeof(decltype(statBuffer.st_size)) <= sizeof(size_t)); static_assert(std::is_same_v); static_assert(sizeof(decltype(statBuffer.st_nlink)) <= sizeof(size_t)); size_t size{0}; if (statBuffer.st_size < 0) { return RetType::error(FudStatus::Failure); } size = static_cast(statBuffer.st_size); DirectoryEntryType entryType{}; switch (statBuffer.st_mode & S_IFMT) { case S_IFBLK: entryType = DirectoryEntryType::Block; break; case S_IFCHR: entryType = DirectoryEntryType::Character; break; case S_IFDIR: entryType = DirectoryEntryType::Directory; break; case S_IFIFO: entryType = DirectoryEntryType::NamedPipe; break; case S_IFLNK: entryType = DirectoryEntryType::SymbolicLink; break; case S_IFREG: entryType = DirectoryEntryType::RegularFile; break; case S_IFSOCK: entryType = DirectoryEntryType::UnixSocket; break; default: entryType = DirectoryEntryType::Unknown; break; } return RetType::okay(DirectoryEntry{ name, statBuffer.st_ino, size, static_cast(statBuffer.st_nlink), statBuffer.st_mtime, entryType}); } Directory::Directory(const String& name) : m_name{name} { if (!m_name.valid()) { return; } if (!m_name.utf8Valid()) { m_status = FudStatus::Utf8Invalid; return; } m_directory = opendir(m_name.c_str()); if (m_directory == nullptr) { m_status = FudStatus::Failure; m_errorCode = errno; return; } m_dirFd = dirfd(m_directory); if (m_dirFd == -1) { m_status = FudStatus::Failure; m_errorCode = errno; closedir(m_directory); m_directory = nullptr; return; } m_errorCode = 0; m_status = FudStatus::Success; } Directory::Directory(Directory&& rhs) noexcept : m_name{std::move(rhs.m_name)}, m_directory{rhs.m_directory}, m_dirFd{rhs.m_dirFd} { rhs.m_directory = nullptr; rhs.m_dirFd = -1; } Directory::~Directory() { if (m_directory != nullptr) { closedir(m_directory); m_directory = nullptr; } } bool Directory::valid() const { return m_directory != nullptr && m_dirFd != -1; } Result Directory::info() { using RetType = Result; if (!valid()) { m_status = FudStatus::ObjectInvalid; return RetType::error(m_status); } Stat sBuffer{}; auto fStatus = fstat(m_dirFd, &sBuffer); if (fStatus == -1) { m_errorCode = errno; m_status = FudStatus::Failure; return RetType::error(m_status); } auto retValue = DirectoryEntry::fromStat(m_name, sBuffer); if (retValue.isOkay()) { m_errorCode = 0; m_status = FudStatus::Success; } else { m_status = retValue.getError(); } return retValue; } Result, FudStatus> Directory::getNextEntry() { using RetType = Result, FudStatus>; if (!valid()) { m_status = FudStatus::ObjectInvalid; return RetType::error(m_status); } errno = 0; auto* dirEntry = readdir(m_directory); if (dirEntry == nullptr) { if (errno != 0) { m_errorCode = errno; m_status = FudStatus::Failure; return RetType::error(m_status); } return RetType::okay(std::nullopt); } const char* entryNameCString = dirEntry->d_name; if (entryNameCString == nullptr) { m_errorCode = -1; m_status = FudStatus::NullPointer; return RetType::error(m_status); } m_errorCode = 0; Stat sBuffer{}; auto flags = 0; auto fStatus = fstatat(m_dirFd, entryNameCString, &sBuffer, flags); if (fStatus == -1) { m_errorCode = errno; m_status = FudStatus::Failure; return RetType::error(m_status); } auto entryNameResult = String::makeFromCString(entryNameCString); if (entryNameResult.isError()) { return RetType::error(entryNameResult); } auto retValue = DirectoryEntry::fromStat(entryNameResult.getOkay(), sBuffer); if (retValue.isOkay()) { m_errorCode = 0; m_status = FudStatus::Success; return RetType::okay(retValue.takeOkay()); } m_status = retValue.getError(); return RetType::error(retValue.getError()); } FudStatus Directory::reset() { if (!valid()) { } if (m_directory != nullptr) { rewinddir(m_directory); } else { m_status = FudStatus::ObjectInvalid; return m_status; } return FudStatus::Success; } } // namespace fud