Serenity Operating System
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}