فهرست منبع

LibCore: Introduce Directory

Core::Directory represents an existing directory on the system, and it
holds an actual file descriptor so that the user can be sure the
directory stays in existence.
kleines Filmröllchen 3 سال پیش
والد
کامیت
46b76f2f55
3فایلهای تغییر یافته به همراه171 افزوده شده و 0 حذف شده
  1. 1 0
      Userland/Libraries/LibCore/CMakeLists.txt
  2. 100 0
      Userland/Libraries/LibCore/Directory.cpp
  3. 70 0
      Userland/Libraries/LibCore/Directory.h

+ 1 - 0
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

+ 100 - 0
Userland/Libraries/LibCore/Directory.cpp

@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2022, kleines Filmröllchen <filmroellchen@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include "Directory.h"
+#include "DirIterator.h"
+#include "System.h"
+#include <dirent.h>
+
+namespace Core {
+
+// We assume that the fd is a valid directory.
+Directory::Directory(int fd, Optional<LexicalPath> 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<bool> Directory::is_valid_directory(int fd)
+{
+    auto stat = TRY(System::fstat(fd));
+    return stat.st_mode & S_IFDIR;
+}
+
+ErrorOr<Directory> Directory::adopt_fd(int fd, Optional<LexicalPath> 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> Directory::create(String path, CreateDirectories create_directories)
+{
+    return create(LexicalPath { move(path) }, create_directories);
+}
+
+ErrorOr<Directory> 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<void> 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<LexicalPath> 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<NonnullOwnPtr<Stream::File>> 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<struct stat> Directory::stat() const
+{
+    return System::fstat(m_directory_fd);
+}
+
+ErrorOr<DirIterator> Directory::create_iterator() const
+{
+    return DirIterator { TRY(path()).string() };
+}
+
+}

+ 70 - 0
Userland/Libraries/LibCore/Directory.h

@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2022, kleines Filmröllchen <filmroellchen@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include <AK/Error.h>
+#include <AK/Format.h>
+#include <AK/LexicalPath.h>
+#include <AK/Noncopyable.h>
+#include <AK/Optional.h>
+#include <LibCore/Stream.h>
+#include <dirent.h>
+#include <sys/stat.h>
+
+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<Directory> create(LexicalPath path, CreateDirectories);
+    static ErrorOr<Directory> create(String path, CreateDirectories);
+    static ErrorOr<Directory> adopt_fd(int fd, Optional<LexicalPath> path = {});
+
+    ErrorOr<NonnullOwnPtr<Stream::File>> open(StringView filename, Stream::OpenMode mode) const;
+    ErrorOr<struct stat> stat() const;
+    ErrorOr<DirIterator> create_iterator() const;
+
+    ErrorOr<LexicalPath> path() const;
+
+    static ErrorOr<bool> is_valid_directory(int fd);
+
+private:
+    Directory(int directory_fd, Optional<LexicalPath> path);
+    static ErrorOr<void> ensure_directory(LexicalPath const& path);
+
+    Optional<LexicalPath> m_path;
+    int m_directory_fd;
+};
+
+}
+
+namespace AK {
+template<>
+struct Formatter<Core::Directory> : Formatter<StringView> {
+    ErrorOr<void> format(FormatBuilder& builder, Core::Directory const& directory)
+    {
+        auto path = directory.path();
+        if (path.is_error())
+            return Formatter<StringView>::format(builder, "<unknown>");
+        return Formatter<StringView>::format(builder, path.release_value().string());
+    }
+};
+
+}