Serenity Operating System
at master 307 lines 9.4 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/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}