Serenity Operating System
at master 363 lines 11 kB view raw
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/FileSystem.h> 8#include <Kernel/FileSystem/Plan9FS/Inode.h> 9#include <Kernel/Process.h> 10 11namespace Kernel { 12 13ErrorOr<NonnullLockRefPtr<FileSystem>> Plan9FS::try_create(OpenFileDescription& file_description) 14{ 15 return TRY(adopt_nonnull_lock_ref_or_enomem(new (nothrow) Plan9FS(file_description))); 16} 17 18Plan9FS::Plan9FS(OpenFileDescription& file_description) 19 : FileBackedFileSystem(file_description) 20 , m_completion_blocker(*this) 21{ 22} 23 24ErrorOr<void> Plan9FS::prepare_to_clear_last_mount() 25{ 26 // FIXME: Do proper cleaning here. 27 return {}; 28} 29 30Plan9FS::~Plan9FS() 31{ 32 // Make sure to destroy the root inode before the FS gets destroyed. 33 if (m_root_inode) { 34 VERIFY(m_root_inode->ref_count() == 1); 35 m_root_inode = nullptr; 36 } 37} 38 39bool Plan9FS::is_initialized_while_locked() 40{ 41 VERIFY(m_lock.is_locked()); 42 return !m_root_inode.is_null(); 43} 44 45ErrorOr<void> Plan9FS::initialize_while_locked() 46{ 47 VERIFY(m_lock.is_locked()); 48 VERIFY(!is_initialized_while_locked()); 49 50 ensure_thread(); 51 52 Plan9FSMessage version_message { *this, Plan9FSMessage::Type::Tversion }; 53 version_message << (u32)m_max_message_size << "9P2000.L"sv; 54 55 TRY(post_message_and_wait_for_a_reply(version_message)); 56 57 u32 msize; 58 StringView remote_protocol_version; 59 version_message >> msize >> remote_protocol_version; 60 dbgln("Remote supports msize={} and protocol version {}", msize, remote_protocol_version); 61 m_remote_protocol_version = parse_protocol_version(remote_protocol_version); 62 m_max_message_size = min(m_max_message_size, (size_t)msize); 63 64 // TODO: auth 65 66 u32 root_fid = allocate_fid(); 67 Plan9FSMessage attach_message { *this, Plan9FSMessage::Type::Tattach }; 68 // FIXME: This needs a user name and an "export" name; but how do we get them? 69 // Perhaps initialize() should accept a string of FS-specific options... 70 attach_message << root_fid << (u32)-1 << "sergey"sv 71 << "/"sv; 72 if (m_remote_protocol_version >= ProtocolVersion::v9P2000u) 73 attach_message << (u32)-1; 74 75 TRY(post_message_and_wait_for_a_reply(attach_message)); 76 m_root_inode = TRY(Plan9FSInode::try_create(*this, root_fid)); 77 return {}; 78} 79 80Plan9FS::ProtocolVersion Plan9FS::parse_protocol_version(StringView s) const 81{ 82 if (s == "9P2000.L") 83 return ProtocolVersion::v9P2000L; 84 if (s == "9P2000.u") 85 return ProtocolVersion::v9P2000u; 86 return ProtocolVersion::v9P2000; 87} 88 89Inode& Plan9FS::root_inode() 90{ 91 return *m_root_inode; 92} 93 94Plan9FS::ReceiveCompletion::ReceiveCompletion(u16 tag) 95 : tag(tag) 96{ 97} 98 99Plan9FS::ReceiveCompletion::~ReceiveCompletion() = default; 100 101bool Plan9FS::Blocker::unblock(u16 tag) 102{ 103 { 104 SpinlockLocker lock(m_lock); 105 if (m_did_unblock) 106 return false; 107 m_did_unblock = true; 108 109 if (m_completion->tag != tag) 110 return false; 111 if (!m_completion->result.is_error()) 112 m_message = move(*m_completion->message); 113 } 114 return unblock(); 115} 116 117bool Plan9FS::Blocker::setup_blocker() 118{ 119 return add_to_blocker_set(m_fs.m_completion_blocker); 120} 121 122void Plan9FS::Blocker::will_unblock_immediately_without_blocking(UnblockImmediatelyReason) 123{ 124 { 125 SpinlockLocker lock(m_lock); 126 if (m_did_unblock) 127 return; 128 } 129 130 m_fs.m_completion_blocker.try_unblock(*this); 131} 132 133bool Plan9FS::Blocker::is_completed() const 134{ 135 SpinlockLocker lock(m_completion->lock); 136 return m_completion->completed; 137} 138 139bool Plan9FS::Plan9FSBlockerSet::should_add_blocker(Thread::Blocker& b, void*) 140{ 141 // NOTE: m_lock is held already! 142 auto& blocker = static_cast<Blocker&>(b); 143 return !blocker.is_completed(); 144} 145 146void Plan9FS::Plan9FSBlockerSet::unblock_completed(u16 tag) 147{ 148 unblock_all_blockers_whose_conditions_are_met([&](Thread::Blocker& b, void*, bool&) { 149 VERIFY(b.blocker_type() == Thread::Blocker::Type::Plan9FS); 150 auto& blocker = static_cast<Blocker&>(b); 151 return blocker.unblock(tag); 152 }); 153} 154 155void Plan9FS::Plan9FSBlockerSet::unblock_all() 156{ 157 unblock_all_blockers_whose_conditions_are_met([&](Thread::Blocker& b, void*, bool&) { 158 VERIFY(b.blocker_type() == Thread::Blocker::Type::Plan9FS); 159 auto& blocker = static_cast<Blocker&>(b); 160 return blocker.unblock(); 161 }); 162} 163 164void Plan9FS::Plan9FSBlockerSet::try_unblock(Plan9FS::Blocker& blocker) 165{ 166 if (m_fs.is_complete(*blocker.completion())) { 167 SpinlockLocker lock(m_lock); 168 blocker.unblock(blocker.completion()->tag); 169 } 170} 171 172bool Plan9FS::is_complete(ReceiveCompletion const& completion) 173{ 174 MutexLocker locker(m_lock); 175 if (m_completions.contains(completion.tag)) { 176 // If it's still in the map then it can't be complete 177 VERIFY(!completion.completed); 178 return false; 179 } 180 181 // if it's not in the map anymore, it must be complete. But we MUST 182 // hold m_lock to be able to check completion.completed! 183 VERIFY(completion.completed); 184 return true; 185} 186 187ErrorOr<void> Plan9FS::post_message(Plan9FSMessage& message, LockRefPtr<ReceiveCompletion> completion) 188{ 189 auto const& buffer = message.build(); 190 u8 const* data = buffer.data(); 191 size_t size = buffer.size(); 192 auto& description = file_description(); 193 194 MutexLocker locker(m_send_lock); 195 196 if (completion) { 197 // Save the completion record *before* we send the message. This 198 // ensures that it exists when the thread reads the response 199 MutexLocker locker(m_lock); 200 auto tag = completion->tag; 201 m_completions.set(tag, completion.release_nonnull()); 202 // TODO: What if there is a collision? Do we need to wait until 203 // the existing record with the tag completes before queueing 204 // this one? 205 } 206 207 while (size > 0) { 208 if (!description.can_write()) { 209 auto unblock_flags = Thread::FileBlocker::BlockFlags::None; 210 if (Thread::current()->block<Thread::WriteBlocker>({}, description, unblock_flags).was_interrupted()) 211 return EINTR; 212 } 213 auto data_buffer = UserOrKernelBuffer::for_kernel_buffer(const_cast<u8*>(data)); 214 auto nwritten = TRY(description.write(data_buffer, size)); 215 data += nwritten; 216 size -= nwritten; 217 } 218 219 return {}; 220} 221 222ErrorOr<void> Plan9FS::do_read(u8* data, size_t size) 223{ 224 auto& description = file_description(); 225 while (size > 0) { 226 if (!description.can_read()) { 227 auto unblock_flags = Thread::FileBlocker::BlockFlags::None; 228 if (Thread::current()->block<Thread::ReadBlocker>({}, description, unblock_flags).was_interrupted()) 229 return EINTR; 230 } 231 auto data_buffer = UserOrKernelBuffer::for_kernel_buffer(data); 232 auto nread = TRY(description.read(data_buffer, size)); 233 if (nread == 0) 234 return EIO; 235 data += nread; 236 size -= nread; 237 } 238 return {}; 239} 240 241ErrorOr<void> Plan9FS::read_and_dispatch_one_message() 242{ 243 struct [[gnu::packed]] Header { 244 u32 size; 245 u8 type; 246 u16 tag; 247 }; 248 Header header; 249 TRY(do_read(reinterpret_cast<u8*>(&header), sizeof(header))); 250 251 auto buffer = TRY(KBuffer::try_create_with_size("Plan9FS: Plan9FSMessage read buffer"sv, header.size, Memory::Region::Access::ReadWrite)); 252 // Copy the already read header into the buffer. 253 memcpy(buffer->data(), &header, sizeof(header)); 254 TRY(do_read(buffer->data() + sizeof(header), header.size - sizeof(header))); 255 256 MutexLocker locker(m_lock); 257 258 auto optional_completion = m_completions.get(header.tag); 259 if (optional_completion.has_value()) { 260 auto* completion = optional_completion.value(); 261 SpinlockLocker lock(completion->lock); 262 completion->result = {}; 263 completion->message = adopt_own_if_nonnull(new (nothrow) Plan9FSMessage { move(buffer) }); 264 completion->completed = true; 265 266 m_completions.remove(header.tag); 267 m_completion_blocker.unblock_completed(header.tag); 268 } else { 269 dbgln("Received a 9p message of type {} with an unexpected tag {}, dropping", header.type, header.tag); 270 } 271 272 return {}; 273} 274 275ErrorOr<void> Plan9FS::post_message_and_explicitly_ignore_reply(Plan9FSMessage& message) 276{ 277 return post_message(message, {}); 278} 279 280ErrorOr<void> Plan9FS::post_message_and_wait_for_a_reply(Plan9FSMessage& message) 281{ 282 auto request_type = message.type(); 283 auto tag = message.tag(); 284 auto completion = adopt_lock_ref(*new ReceiveCompletion(tag)); 285 TRY(post_message(message, completion)); 286 if (Thread::current()->block<Plan9FS::Blocker>({}, *this, message, completion).was_interrupted()) 287 return EINTR; 288 289 if (completion->result.is_error()) { 290 dbgln("Plan9FS: Plan9FSMessage was aborted with error {}", completion->result.error()); 291 return EIO; 292 } 293 294 auto reply_type = message.type(); 295 296 if (reply_type == Plan9FSMessage::Type::Rlerror) { 297 // Contains a numerical Linux errno; hopefully our errno numbers match. 298 u32 error_code; 299 message >> error_code; 300 return Error::from_errno((ErrnoCode)error_code); 301 } 302 if (reply_type == Plan9FSMessage::Type::Rerror) { 303 // Contains an error message. We could attempt to parse it, but for now 304 // we simply return EIO instead. In 9P200.u, it can also contain a 305 // numerical errno in an unspecified encoding; we ignore those too. 306 StringView error_name; 307 message >> error_name; 308 dbgln("Plan9FS: Received error name {}", error_name); 309 return EIO; 310 } 311 if ((u8)reply_type != (u8)request_type + 1) { 312 // Other than those error messages. we only expect the matching reply 313 // message type. 314 dbgln("Plan9FS: Received unexpected message type {} in response to {}", (u8)reply_type, (u8)request_type); 315 return EIO; 316 } 317 318 return {}; 319} 320 321size_t Plan9FS::adjust_buffer_size(size_t size) const 322{ 323 size_t max_size = m_max_message_size - Plan9FSMessage::max_header_size; 324 return min(size, max_size); 325} 326 327void Plan9FS::thread_main() 328{ 329 dbgln("Plan9FS: Thread running"); 330 do { 331 auto result = read_and_dispatch_one_message(); 332 if (result.is_error()) { 333 // If we fail to read, wake up everyone with an error. 334 MutexLocker locker(m_lock); 335 336 for (auto& it : m_completions) { 337 it.value->result = Error::copy(result.error()); 338 it.value->completed = true; 339 } 340 m_completions.clear(); 341 m_completion_blocker.unblock_all(); 342 dbgln("Plan9FS: Thread terminating, error reading"); 343 return; 344 } 345 } while (!m_thread_shutdown); 346 dbgln("Plan9FS: Thread terminating"); 347} 348 349void Plan9FS::ensure_thread() 350{ 351 SpinlockLocker lock(m_thread_lock); 352 if (!m_thread_running.exchange(true, AK::MemoryOrder::memory_order_acq_rel)) { 353 auto process_name = KString::try_create("Plan9FS"sv); 354 if (process_name.is_error()) 355 TODO(); 356 (void)Process::create_kernel_process(m_thread, process_name.release_value(), [&]() { 357 thread_main(); 358 m_thread_running.store(false, AK::MemoryOrder::memory_order_release); 359 }); 360 } 361} 362 363}