Serenity Operating System
at master 124 lines 3.5 kB view raw
1/* 2 * Copyright (c) 2022, kleines Filmröllchen <filmroellchen@serenityos.org> 3 * 4 * SPDX-License-Identifier: BSD-2-Clause 5 */ 6 7#include "Directory.h" 8#include "DirIterator.h" 9#include "System.h" 10#include <dirent.h> 11 12namespace Core { 13 14// We assume that the fd is a valid directory. 15Directory::Directory(int fd, LexicalPath path) 16 : m_path(move(path)) 17 , m_directory_fd(fd) 18{ 19} 20 21Directory::Directory(Directory&& other) 22 : m_path(move(other.m_path)) 23 , m_directory_fd(other.m_directory_fd) 24{ 25 other.m_directory_fd = -1; 26} 27 28Directory::~Directory() 29{ 30 if (m_directory_fd != -1) 31 MUST(System::close(m_directory_fd)); 32} 33 34ErrorOr<void> Directory::chown(uid_t uid, gid_t gid) 35{ 36 if (m_directory_fd == -1) 37 return Error::from_syscall("fchown"sv, -EBADF); 38 TRY(Core::System::fchown(m_directory_fd, uid, gid)); 39 return {}; 40} 41 42ErrorOr<bool> Directory::is_valid_directory(int fd) 43{ 44 auto stat = TRY(System::fstat(fd)); 45 return stat.st_mode & S_IFDIR; 46} 47 48ErrorOr<Directory> Directory::adopt_fd(int fd, LexicalPath path) 49{ 50 // This will also fail if the fd is invalid in the first place. 51 if (!TRY(Directory::is_valid_directory(fd))) 52 return Error::from_errno(ENOTDIR); 53 return Directory { fd, move(path) }; 54} 55 56ErrorOr<Directory> Directory::create(DeprecatedString path, CreateDirectories create_directories, mode_t creation_mode) 57{ 58 return create(LexicalPath { move(path) }, create_directories, creation_mode); 59} 60 61ErrorOr<Directory> Directory::create(LexicalPath path, CreateDirectories create_directories, mode_t creation_mode) 62{ 63 if (create_directories == CreateDirectories::Yes) 64 TRY(ensure_directory(path, creation_mode)); 65 // FIXME: doesn't work on Linux probably 66 auto fd = TRY(System::open(path.string(), O_CLOEXEC)); 67 return adopt_fd(fd, move(path)); 68} 69 70ErrorOr<void> Directory::ensure_directory(LexicalPath const& path, mode_t creation_mode) 71{ 72 if (path.basename() == "/" || path.basename() == ".") 73 return {}; 74 75 TRY(ensure_directory(path.parent(), creation_mode)); 76 77 auto return_value = System::mkdir(path.string(), creation_mode); 78 // We don't care if the directory already exists. 79 if (return_value.is_error() && return_value.error().code() != EEXIST) 80 return return_value; 81 82 return {}; 83} 84 85ErrorOr<NonnullOwnPtr<File>> Directory::open(StringView filename, File::OpenMode mode) const 86{ 87 auto fd = TRY(System::openat(m_directory_fd, filename, File::open_mode_to_options(mode))); 88 return File::adopt_fd(fd, mode); 89} 90 91ErrorOr<struct stat> Directory::stat() const 92{ 93 return System::fstat(m_directory_fd); 94} 95 96ErrorOr<void> Directory::for_each_entry(DirIterator::Flags flags, Core::Directory::ForEachEntryCallback callback) 97{ 98 DirIterator iterator { path().string(), flags }; 99 if (iterator.has_error()) 100 return iterator.error(); 101 102 while (iterator.has_next()) { 103 if (iterator.has_error()) 104 return iterator.error(); 105 106 auto entry = iterator.next(); 107 if (!entry.has_value()) 108 break; 109 110 auto decision = TRY(callback(entry.value(), *this)); 111 if (decision == IterationDecision::Break) 112 break; 113 } 114 115 return {}; 116} 117 118ErrorOr<void> Directory::for_each_entry(AK::StringView path, DirIterator::Flags flags, Core::Directory::ForEachEntryCallback callback) 119{ 120 auto directory = TRY(Directory::create(path, CreateDirectories::No)); 121 return directory.for_each_entry(flags, move(callback)); 122} 123 124}