diff --git a/Userland/Libraries/LibCore/CMakeLists.txt b/Userland/Libraries/LibCore/CMakeLists.txt index fcce16726a..96940c060d 100644 --- a/Userland/Libraries/LibCore/CMakeLists.txt +++ b/Userland/Libraries/LibCore/CMakeLists.txt @@ -5,6 +5,7 @@ set(SOURCES ConfigFile.cpp Command.cpp DateTime.cpp + Directory.cpp DirIterator.cpp ElapsedTimer.cpp Event.cpp diff --git a/Userland/Libraries/LibCore/Directory.cpp b/Userland/Libraries/LibCore/Directory.cpp new file mode 100644 index 0000000000..32725992e2 --- /dev/null +++ b/Userland/Libraries/LibCore/Directory.cpp @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2022, kleines Filmröllchen + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include "Directory.h" +#include "DirIterator.h" +#include "System.h" +#include + +namespace Core { + +// We assume that the fd is a valid directory. +Directory::Directory(int fd, Optional path) + : m_path(move(path)) + , m_directory_fd(fd) +{ +} + +Directory::Directory(Directory&& other) + : m_path(move(other.m_path)) + , m_directory_fd(other.m_directory_fd) +{ + other.m_directory_fd = -1; +} + +Directory::~Directory() +{ + if (m_directory_fd != -1) + MUST(System::close(m_directory_fd)); +} + +ErrorOr Directory::is_valid_directory(int fd) +{ + auto stat = TRY(System::fstat(fd)); + return stat.st_mode & S_IFDIR; +} + +ErrorOr Directory::adopt_fd(int fd, Optional path) +{ + // This will also fail if the fd is invalid in the first place. + if (!TRY(Directory::is_valid_directory(fd))) + return Error::from_errno(ENOTDIR); + return Directory { fd, move(path) }; +} + +ErrorOr Directory::create(String path, CreateDirectories create_directories) +{ + return create(LexicalPath { move(path) }, create_directories); +} + +ErrorOr Directory::create(LexicalPath path, CreateDirectories create_directories) +{ + if (create_directories == CreateDirectories::Yes) + TRY(ensure_directory(path)); + // FIXME: doesn't work on Linux probably + auto fd = TRY(System::open(path.string(), O_CLOEXEC)); + return adopt_fd(fd, move(path)); +} + +ErrorOr Directory::ensure_directory(LexicalPath const& path) +{ + if (path.basename() == "/") + return {}; + + TRY(ensure_directory(path.parent())); + + auto return_value = System::mkdir(path.string(), 0755); + // We don't care if the directory already exists. + if (return_value.is_error() && return_value.error().code() != EEXIST) + return return_value; + + return {}; +} + +ErrorOr Directory::path() const +{ + if (!m_path.has_value()) + return Error::from_string_literal("Directory wasn't created with a path"); + return m_path.value(); +} + +ErrorOr> Directory::open(StringView filename, Stream::OpenMode mode) const +{ + auto fd = TRY(System::openat(m_directory_fd, filename, Stream::File::open_mode_to_options(mode))); + return Stream::File::adopt_fd(fd, mode); +} + +ErrorOr Directory::stat() const +{ + return System::fstat(m_directory_fd); +} + +ErrorOr Directory::create_iterator() const +{ + return DirIterator { TRY(path()).string() }; +} + +} diff --git a/Userland/Libraries/LibCore/Directory.h b/Userland/Libraries/LibCore/Directory.h new file mode 100644 index 0000000000..950a93bb9c --- /dev/null +++ b/Userland/Libraries/LibCore/Directory.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2022, kleines Filmröllchen + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Core { + +class DirIterator; + +// Deal with real system directories. Any Directory instance always refers to a valid existing directory. +class Directory { + AK_MAKE_NONCOPYABLE(Directory); + +public: + Directory(Directory&&); + ~Directory(); + + // When this flag is set, both the directory attempted to instantiate as well as all of its parents are created with mode 0755 if necessary. + enum class CreateDirectories : bool { + No, + Yes, + }; + + static ErrorOr create(LexicalPath path, CreateDirectories); + static ErrorOr create(String path, CreateDirectories); + static ErrorOr adopt_fd(int fd, Optional path = {}); + + ErrorOr> open(StringView filename, Stream::OpenMode mode) const; + ErrorOr stat() const; + ErrorOr create_iterator() const; + + ErrorOr path() const; + + static ErrorOr is_valid_directory(int fd); + +private: + Directory(int directory_fd, Optional path); + static ErrorOr ensure_directory(LexicalPath const& path); + + Optional m_path; + int m_directory_fd; +}; + +} + +namespace AK { +template<> +struct Formatter : Formatter { + ErrorOr format(FormatBuilder& builder, Core::Directory const& directory) + { + auto path = directory.path(); + if (path.is_error()) + return Formatter::format(builder, ""); + return Formatter::format(builder, path.release_value().string()); + } +}; + +}