Serenity Operating System
1/*
2 * Copyright (c) 2020, Sergey Bugaev <bugaevc@serenityos.org>
3 *
4 * SPDX-License-Identifier: BSD-2-Clause
5 */
6
7#include <Kernel/FileSystem/Plan9FS/Inode.h>
8#include <Kernel/Process.h>
9
10namespace Kernel {
11
12Plan9FSInode::Plan9FSInode(Plan9FS& fs, u32 fid)
13 : Inode(fs, fid)
14{
15}
16
17ErrorOr<NonnullRefPtr<Plan9FSInode>> Plan9FSInode::try_create(Plan9FS& fs, u32 fid)
18{
19 return adopt_nonnull_ref_or_enomem(new (nothrow) Plan9FSInode(fs, fid));
20}
21
22Plan9FSInode::~Plan9FSInode()
23{
24 Plan9FSMessage clunk_request { fs(), Plan9FSMessage::Type::Tclunk };
25 clunk_request << fid();
26 // FIXME: Should we observe this error somehow?
27 [[maybe_unused]] auto rc = fs().post_message_and_explicitly_ignore_reply(clunk_request);
28}
29
30ErrorOr<void> Plan9FSInode::ensure_open_for_mode(int mode)
31{
32 bool use_lopen = fs().m_remote_protocol_version >= Plan9FS::ProtocolVersion::v9P2000L;
33 u32 l_mode = 0;
34 u8 p9_mode = 0;
35
36 {
37 MutexLocker locker(m_inode_lock);
38
39 // If it's already open in this mode, we're done.
40 if ((m_open_mode & mode) == mode)
41 return {};
42
43 m_open_mode |= mode;
44
45 if ((m_open_mode & O_RDWR) == O_RDWR) {
46 l_mode |= 2;
47 p9_mode |= 2;
48 } else if (m_open_mode & O_WRONLY) {
49 l_mode |= 1;
50 p9_mode |= 1;
51 } else if (m_open_mode & O_RDONLY) {
52 // Leave the values at 0.
53 }
54 }
55
56 if (use_lopen) {
57 Plan9FSMessage message { fs(), Plan9FSMessage::Type::Tlopen };
58 message << fid() << l_mode;
59 return fs().post_message_and_wait_for_a_reply(message);
60 }
61
62 Plan9FSMessage message { fs(), Plan9FSMessage::Type::Topen };
63 message << fid() << p9_mode;
64 return fs().post_message_and_wait_for_a_reply(message);
65}
66
67ErrorOr<size_t> Plan9FSInode::read_bytes_locked(off_t offset, size_t size, UserOrKernelBuffer& buffer, OpenFileDescription*) const
68{
69 TRY(const_cast<Plan9FSInode&>(*this).ensure_open_for_mode(O_RDONLY));
70
71 size = fs().adjust_buffer_size(size);
72
73 Plan9FSMessage message { fs(), Plan9FSMessage::Type::Treadlink };
74 StringView data;
75
76 // Try readlink first.
77 bool readlink_succeeded = false;
78 if (fs().m_remote_protocol_version >= Plan9FS::ProtocolVersion::v9P2000L && offset == 0) {
79 message << fid();
80 if (auto result = fs().post_message_and_wait_for_a_reply(message); !result.is_error()) {
81 readlink_succeeded = true;
82 message >> data;
83 }
84 }
85
86 if (!readlink_succeeded) {
87 message = Plan9FSMessage { fs(), Plan9FSMessage::Type::Tread };
88 message << fid() << (u64)offset << (u32)size;
89 TRY(fs().post_message_and_wait_for_a_reply(message));
90 data = message.read_data();
91 }
92
93 // Guard against the server returning more data than requested.
94 size_t nread = min(data.length(), size);
95 TRY(buffer.write(data.characters_without_null_termination(), nread));
96 return nread;
97}
98
99ErrorOr<void> Plan9FSInode::replace_child(StringView, Inode&)
100{
101 // TODO
102 return ENOTIMPL;
103}
104
105ErrorOr<size_t> Plan9FSInode::write_bytes_locked(off_t offset, size_t size, UserOrKernelBuffer const& data, OpenFileDescription*)
106{
107 TRY(ensure_open_for_mode(O_WRONLY));
108 size = fs().adjust_buffer_size(size);
109
110 auto data_copy = TRY(data.try_copy_into_kstring(size)); // FIXME: this seems ugly
111
112 Plan9FSMessage message { fs(), Plan9FSMessage::Type::Twrite };
113 message << fid() << (u64)offset;
114 TRY(message.append_data(data_copy->view()));
115 TRY(fs().post_message_and_wait_for_a_reply(message));
116
117 u32 nwritten;
118 message >> nwritten;
119 return nwritten;
120}
121
122InodeMetadata Plan9FSInode::metadata() const
123{
124 InodeMetadata metadata;
125 metadata.inode = identifier();
126
127 // 9P2000.L; TODO: 9P2000 & 9P2000.u
128 Plan9FSMessage message { fs(), Plan9FSMessage::Type::Tgetattr };
129 message << fid() << (u64)GetAttrMask::Basic;
130 auto result = fs().post_message_and_wait_for_a_reply(message);
131 if (result.is_error()) {
132 // Just return blank metadata; hopefully that's enough to result in an
133 // error at some upper layer. Ideally, there would be a way for
134 // Inode::metadata() to return failure.
135 return metadata;
136 }
137
138 u64 valid;
139 Plan9FSQIdentifier qid;
140 u32 mode;
141 u32 uid;
142 u32 gid;
143 u64 nlink;
144 u64 rdev;
145 u64 size;
146 u64 blksize;
147 u64 blocks;
148 message >> valid >> qid >> mode >> uid >> gid >> nlink >> rdev >> size >> blksize >> blocks;
149 // TODO: times...
150
151 if (valid & (u64)GetAttrMask::Mode)
152 metadata.mode = mode;
153 if (valid & (u64)GetAttrMask::NLink)
154 metadata.link_count = nlink;
155
156#if 0
157 // FIXME: Map UID/GID somehow? Or what do we do?
158 if (valid & (u64)GetAttrMask::UID)
159 metadata.uid = uid;
160 if (valid & (u64)GetAttrMask::GID)
161 metadata.uid = gid;
162 // FIXME: What about device nodes?
163 if (valid & (u64)GetAttrMask::RDev)
164 metadata.encoded_device = 0; // TODO
165#endif
166
167 if (valid & (u64)GetAttrMask::Size)
168 metadata.size = size;
169 if (valid & (u64)GetAttrMask::Blocks) {
170 metadata.block_size = blksize;
171 metadata.block_count = blocks;
172 }
173
174 return metadata;
175}
176
177ErrorOr<void> Plan9FSInode::flush_metadata()
178{
179 // Do nothing.
180 return {};
181}
182
183ErrorOr<void> Plan9FSInode::traverse_as_directory(Function<ErrorOr<void>(FileSystem::DirectoryEntryView const&)> callback) const
184{
185 // TODO: Should we synthesize "." and ".." here?
186
187 if (fs().m_remote_protocol_version >= Plan9FS::ProtocolVersion::v9P2000L) {
188 // Start by cloning the fid and opening it.
189 auto clone_fid = fs().allocate_fid();
190 {
191 Plan9FSMessage clone_message { fs(), Plan9FSMessage::Type::Twalk };
192 clone_message << fid() << clone_fid << (u16)0;
193 TRY(fs().post_message_and_wait_for_a_reply(clone_message));
194 Plan9FSMessage open_message { fs(), Plan9FSMessage::Type::Tlopen };
195 open_message << clone_fid << (u32)0;
196 auto result = fs().post_message_and_wait_for_a_reply(open_message);
197 if (result.is_error()) {
198 Plan9FSMessage close_message { fs(), Plan9FSMessage::Type::Tclunk };
199 close_message << clone_fid;
200 // FIXME: Should we observe this error?
201 [[maybe_unused]] auto rc = fs().post_message_and_explicitly_ignore_reply(close_message);
202 return result;
203 }
204 }
205
206 u64 offset = 0;
207 u32 count = fs().adjust_buffer_size(8 * MiB);
208 ErrorOr<void> result;
209
210 while (true) {
211 Plan9FSMessage message { fs(), Plan9FSMessage::Type::Treaddir };
212 message << clone_fid << offset << count;
213 result = fs().post_message_and_wait_for_a_reply(message);
214 if (result.is_error())
215 break;
216
217 StringView data = message.read_data();
218 if (data.is_empty()) {
219 // We've reached the end.
220 break;
221 }
222
223 for (Plan9FSMessage::Decoder decoder { data }; decoder.has_more_data();) {
224 Plan9FSQIdentifier qid;
225 u8 type;
226 StringView name;
227 decoder >> qid >> offset >> type >> name;
228 result = callback({ name, { fsid(), fs().allocate_fid() }, 0 });
229 if (result.is_error())
230 break;
231 }
232
233 if (result.is_error())
234 break;
235 }
236
237 Plan9FSMessage close_message { fs(), Plan9FSMessage::Type::Tclunk };
238 close_message << clone_fid;
239 // FIXME: Should we observe this error?
240 [[maybe_unused]] auto rc = fs().post_message_and_explicitly_ignore_reply(close_message);
241 return result;
242 }
243
244 // TODO
245 return ENOTIMPL;
246}
247
248ErrorOr<NonnullRefPtr<Inode>> Plan9FSInode::lookup(StringView name)
249{
250 u32 newfid = fs().allocate_fid();
251 Plan9FSMessage message { fs(), Plan9FSMessage::Type::Twalk };
252 message << fid() << newfid << (u16)1 << name;
253 TRY(fs().post_message_and_wait_for_a_reply(message));
254 return TRY(Plan9FSInode::try_create(fs(), newfid));
255}
256
257ErrorOr<NonnullRefPtr<Inode>> Plan9FSInode::create_child(StringView, mode_t, dev_t, UserID, GroupID)
258{
259 // TODO
260 return ENOTIMPL;
261}
262
263ErrorOr<void> Plan9FSInode::add_child(Inode&, StringView, mode_t)
264{
265 // TODO
266 return ENOTIMPL;
267}
268
269ErrorOr<void> Plan9FSInode::remove_child(StringView)
270{
271 // TODO
272 return ENOTIMPL;
273}
274
275ErrorOr<void> Plan9FSInode::chmod(mode_t)
276{
277 // TODO
278 return ENOTIMPL;
279}
280
281ErrorOr<void> Plan9FSInode::chown(UserID, GroupID)
282{
283 // TODO
284 return ENOTIMPL;
285}
286
287ErrorOr<void> Plan9FSInode::truncate(u64 new_size)
288{
289 if (fs().m_remote_protocol_version >= Plan9FS::ProtocolVersion::v9P2000L) {
290 Plan9FSMessage message { fs(), Plan9FSMessage::Type::Tsetattr };
291 SetAttrMask valid = SetAttrMask::Size;
292 u32 mode = 0;
293 u32 uid = 0;
294 u32 gid = 0;
295 u64 atime_sec = 0;
296 u64 atime_nsec = 0;
297 u64 mtime_sec = 0;
298 u64 mtime_nsec = 0;
299 message << fid() << (u64)valid << mode << uid << gid << new_size << atime_sec << atime_nsec << mtime_sec << mtime_nsec;
300 return fs().post_message_and_wait_for_a_reply(message);
301 }
302
303 // TODO: wstat version
304 return {};
305}
306
307}