mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 22:02:44 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			307 lines
		
	
	
	
		
			9.1 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			307 lines
		
	
	
	
		
			9.1 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*
 | |
|  * Copyright (c) 2020, Sergey Bugaev <bugaevc@serenityos.org>
 | |
|  *
 | |
|  * SPDX-License-Identifier: BSD-2-Clause
 | |
|  */
 | |
| 
 | |
| #include <Kernel/FileSystem/Plan9FS/Inode.h>
 | |
| #include <Kernel/Tasks/Process.h>
 | |
| 
 | |
| namespace Kernel {
 | |
| 
 | |
| Plan9FSInode::Plan9FSInode(Plan9FS& fs, u32 fid)
 | |
|     : Inode(fs, fid)
 | |
| {
 | |
| }
 | |
| 
 | |
| ErrorOr<NonnullRefPtr<Plan9FSInode>> Plan9FSInode::try_create(Plan9FS& fs, u32 fid)
 | |
| {
 | |
|     return adopt_nonnull_ref_or_enomem(new (nothrow) Plan9FSInode(fs, fid));
 | |
| }
 | |
| 
 | |
| Plan9FSInode::~Plan9FSInode()
 | |
| {
 | |
|     Plan9FSMessage clunk_request { fs(), Plan9FSMessage::Type::Tclunk };
 | |
|     clunk_request << fid();
 | |
|     // FIXME: Should we observe this  error somehow?
 | |
|     [[maybe_unused]] auto rc = fs().post_message_and_explicitly_ignore_reply(clunk_request);
 | |
| }
 | |
| 
 | |
| ErrorOr<void> Plan9FSInode::ensure_open_for_mode(int mode)
 | |
| {
 | |
|     bool use_lopen = fs().m_remote_protocol_version >= Plan9FS::ProtocolVersion::v9P2000L;
 | |
|     u32 l_mode = 0;
 | |
|     u8 p9_mode = 0;
 | |
| 
 | |
|     {
 | |
|         MutexLocker locker(m_inode_lock);
 | |
| 
 | |
|         // If it's already open in this mode, we're done.
 | |
|         if ((m_open_mode & mode) == mode)
 | |
|             return {};
 | |
| 
 | |
|         m_open_mode |= mode;
 | |
| 
 | |
|         if ((m_open_mode & O_RDWR) == O_RDWR) {
 | |
|             l_mode |= 2;
 | |
|             p9_mode |= 2;
 | |
|         } else if (m_open_mode & O_WRONLY) {
 | |
|             l_mode |= 1;
 | |
|             p9_mode |= 1;
 | |
|         } else if (m_open_mode & O_RDONLY) {
 | |
|             // Leave the values at 0.
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     if (use_lopen) {
 | |
|         Plan9FSMessage message { fs(), Plan9FSMessage::Type::Tlopen };
 | |
|         message << fid() << l_mode;
 | |
|         return fs().post_message_and_wait_for_a_reply(message);
 | |
|     }
 | |
| 
 | |
|     Plan9FSMessage message { fs(), Plan9FSMessage::Type::Topen };
 | |
|     message << fid() << p9_mode;
 | |
|     return fs().post_message_and_wait_for_a_reply(message);
 | |
| }
 | |
| 
 | |
| ErrorOr<size_t> Plan9FSInode::read_bytes_locked(off_t offset, size_t size, UserOrKernelBuffer& buffer, OpenFileDescription*) const
 | |
| {
 | |
|     TRY(const_cast<Plan9FSInode&>(*this).ensure_open_for_mode(O_RDONLY));
 | |
| 
 | |
|     size = fs().adjust_buffer_size(size);
 | |
| 
 | |
|     Plan9FSMessage message { fs(), Plan9FSMessage::Type::Treadlink };
 | |
|     StringView data;
 | |
| 
 | |
|     // Try readlink first.
 | |
|     bool readlink_succeeded = false;
 | |
|     if (fs().m_remote_protocol_version >= Plan9FS::ProtocolVersion::v9P2000L && offset == 0) {
 | |
|         message << fid();
 | |
|         if (auto result = fs().post_message_and_wait_for_a_reply(message); !result.is_error()) {
 | |
|             readlink_succeeded = true;
 | |
|             message >> data;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     if (!readlink_succeeded) {
 | |
|         message = Plan9FSMessage { fs(), Plan9FSMessage::Type::Tread };
 | |
|         message << fid() << (u64)offset << (u32)size;
 | |
|         TRY(fs().post_message_and_wait_for_a_reply(message));
 | |
|         data = message.read_data();
 | |
|     }
 | |
| 
 | |
|     // Guard against the server returning more data than requested.
 | |
|     size_t nread = min(data.length(), size);
 | |
|     TRY(buffer.write(data.characters_without_null_termination(), nread));
 | |
|     return nread;
 | |
| }
 | |
| 
 | |
| ErrorOr<void> Plan9FSInode::replace_child(StringView, Inode&)
 | |
| {
 | |
|     // TODO
 | |
|     return ENOTIMPL;
 | |
| }
 | |
| 
 | |
| ErrorOr<size_t> Plan9FSInode::write_bytes_locked(off_t offset, size_t size, UserOrKernelBuffer const& data, OpenFileDescription*)
 | |
| {
 | |
|     TRY(ensure_open_for_mode(O_WRONLY));
 | |
|     size = fs().adjust_buffer_size(size);
 | |
| 
 | |
|     auto data_copy = TRY(data.try_copy_into_kstring(size)); // FIXME: this seems ugly
 | |
| 
 | |
|     Plan9FSMessage message { fs(), Plan9FSMessage::Type::Twrite };
 | |
|     message << fid() << (u64)offset;
 | |
|     TRY(message.append_data(data_copy->view()));
 | |
|     TRY(fs().post_message_and_wait_for_a_reply(message));
 | |
| 
 | |
|     u32 nwritten;
 | |
|     message >> nwritten;
 | |
|     return nwritten;
 | |
| }
 | |
| 
 | |
| InodeMetadata Plan9FSInode::metadata() const
 | |
| {
 | |
|     InodeMetadata metadata;
 | |
|     metadata.inode = identifier();
 | |
| 
 | |
|     // 9P2000.L; TODO: 9P2000 & 9P2000.u
 | |
|     Plan9FSMessage message { fs(), Plan9FSMessage::Type::Tgetattr };
 | |
|     message << fid() << (u64)GetAttrMask::Basic;
 | |
|     auto result = fs().post_message_and_wait_for_a_reply(message);
 | |
|     if (result.is_error()) {
 | |
|         // Just return blank metadata; hopefully that's enough to result in an
 | |
|         // error at some upper layer. Ideally, there would be a way for
 | |
|         // Inode::metadata() to return failure.
 | |
|         return metadata;
 | |
|     }
 | |
| 
 | |
|     u64 valid;
 | |
|     Plan9FSQIdentifier qid;
 | |
|     u32 mode;
 | |
|     u32 uid;
 | |
|     u32 gid;
 | |
|     u64 nlink;
 | |
|     u64 rdev;
 | |
|     u64 size;
 | |
|     u64 blksize;
 | |
|     u64 blocks;
 | |
|     message >> valid >> qid >> mode >> uid >> gid >> nlink >> rdev >> size >> blksize >> blocks;
 | |
|     // TODO: times...
 | |
| 
 | |
|     if (valid & (u64)GetAttrMask::Mode)
 | |
|         metadata.mode = mode;
 | |
|     if (valid & (u64)GetAttrMask::NLink)
 | |
|         metadata.link_count = nlink;
 | |
| 
 | |
| #if 0
 | |
|     // FIXME: Map UID/GID somehow? Or what do we do?
 | |
|     if (valid & (u64)GetAttrMask::UID)
 | |
|         metadata.uid = uid;
 | |
|     if (valid & (u64)GetAttrMask::GID)
 | |
|         metadata.uid = gid;
 | |
|     // FIXME: What about device nodes?
 | |
|     if (valid & (u64)GetAttrMask::RDev)
 | |
|         metadata.encoded_device = 0; // TODO
 | |
| #endif
 | |
| 
 | |
|     if (valid & (u64)GetAttrMask::Size)
 | |
|         metadata.size = size;
 | |
|     if (valid & (u64)GetAttrMask::Blocks) {
 | |
|         metadata.block_size = blksize;
 | |
|         metadata.block_count = blocks;
 | |
|     }
 | |
| 
 | |
|     return metadata;
 | |
| }
 | |
| 
 | |
| ErrorOr<void> Plan9FSInode::flush_metadata()
 | |
| {
 | |
|     // Do nothing.
 | |
|     return {};
 | |
| }
 | |
| 
 | |
| ErrorOr<void> Plan9FSInode::traverse_as_directory(Function<ErrorOr<void>(FileSystem::DirectoryEntryView const&)> callback) const
 | |
| {
 | |
|     // TODO: Should we synthesize "." and ".." here?
 | |
| 
 | |
|     if (fs().m_remote_protocol_version >= Plan9FS::ProtocolVersion::v9P2000L) {
 | |
|         // Start by cloning the fid and opening it.
 | |
|         auto clone_fid = fs().allocate_fid();
 | |
|         {
 | |
|             Plan9FSMessage clone_message { fs(), Plan9FSMessage::Type::Twalk };
 | |
|             clone_message << fid() << clone_fid << (u16)0;
 | |
|             TRY(fs().post_message_and_wait_for_a_reply(clone_message));
 | |
|             Plan9FSMessage open_message { fs(), Plan9FSMessage::Type::Tlopen };
 | |
|             open_message << clone_fid << (u32)0;
 | |
|             auto result = fs().post_message_and_wait_for_a_reply(open_message);
 | |
|             if (result.is_error()) {
 | |
|                 Plan9FSMessage close_message { fs(), Plan9FSMessage::Type::Tclunk };
 | |
|                 close_message << clone_fid;
 | |
|                 // FIXME: Should we observe this error?
 | |
|                 [[maybe_unused]] auto rc = fs().post_message_and_explicitly_ignore_reply(close_message);
 | |
|                 return result;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         u64 offset = 0;
 | |
|         u32 count = fs().adjust_buffer_size(8 * MiB);
 | |
|         ErrorOr<void> result;
 | |
| 
 | |
|         while (true) {
 | |
|             Plan9FSMessage message { fs(), Plan9FSMessage::Type::Treaddir };
 | |
|             message << clone_fid << offset << count;
 | |
|             result = fs().post_message_and_wait_for_a_reply(message);
 | |
|             if (result.is_error())
 | |
|                 break;
 | |
| 
 | |
|             StringView data = message.read_data();
 | |
|             if (data.is_empty()) {
 | |
|                 // We've reached the end.
 | |
|                 break;
 | |
|             }
 | |
| 
 | |
|             for (Plan9FSMessage::Decoder decoder { data }; decoder.has_more_data();) {
 | |
|                 Plan9FSQIdentifier qid;
 | |
|                 u8 type;
 | |
|                 StringView name;
 | |
|                 decoder >> qid >> offset >> type >> name;
 | |
|                 result = callback({ name, { fsid(), fs().allocate_fid() }, 0 });
 | |
|                 if (result.is_error())
 | |
|                     break;
 | |
|             }
 | |
| 
 | |
|             if (result.is_error())
 | |
|                 break;
 | |
|         }
 | |
| 
 | |
|         Plan9FSMessage close_message { fs(), Plan9FSMessage::Type::Tclunk };
 | |
|         close_message << clone_fid;
 | |
|         // FIXME: Should we observe this error?
 | |
|         [[maybe_unused]] auto rc = fs().post_message_and_explicitly_ignore_reply(close_message);
 | |
|         return result;
 | |
|     }
 | |
| 
 | |
|     // TODO
 | |
|     return ENOTIMPL;
 | |
| }
 | |
| 
 | |
| ErrorOr<NonnullRefPtr<Inode>> Plan9FSInode::lookup(StringView name)
 | |
| {
 | |
|     u32 newfid = fs().allocate_fid();
 | |
|     Plan9FSMessage message { fs(), Plan9FSMessage::Type::Twalk };
 | |
|     message << fid() << newfid << (u16)1 << name;
 | |
|     TRY(fs().post_message_and_wait_for_a_reply(message));
 | |
|     return TRY(Plan9FSInode::try_create(fs(), newfid));
 | |
| }
 | |
| 
 | |
| ErrorOr<NonnullRefPtr<Inode>> Plan9FSInode::create_child(StringView, mode_t, dev_t, UserID, GroupID)
 | |
| {
 | |
|     // TODO
 | |
|     return ENOTIMPL;
 | |
| }
 | |
| 
 | |
| ErrorOr<void> Plan9FSInode::add_child(Inode&, StringView, mode_t)
 | |
| {
 | |
|     // TODO
 | |
|     return ENOTIMPL;
 | |
| }
 | |
| 
 | |
| ErrorOr<void> Plan9FSInode::remove_child(StringView)
 | |
| {
 | |
|     // TODO
 | |
|     return ENOTIMPL;
 | |
| }
 | |
| 
 | |
| ErrorOr<void> Plan9FSInode::chmod(mode_t)
 | |
| {
 | |
|     // TODO
 | |
|     return ENOTIMPL;
 | |
| }
 | |
| 
 | |
| ErrorOr<void> Plan9FSInode::chown(UserID, GroupID)
 | |
| {
 | |
|     // TODO
 | |
|     return ENOTIMPL;
 | |
| }
 | |
| 
 | |
| ErrorOr<void> Plan9FSInode::truncate(u64 new_size)
 | |
| {
 | |
|     if (fs().m_remote_protocol_version >= Plan9FS::ProtocolVersion::v9P2000L) {
 | |
|         Plan9FSMessage message { fs(), Plan9FSMessage::Type::Tsetattr };
 | |
|         SetAttrMask valid = SetAttrMask::Size;
 | |
|         u32 mode = 0;
 | |
|         u32 uid = 0;
 | |
|         u32 gid = 0;
 | |
|         u64 atime_sec = 0;
 | |
|         u64 atime_nsec = 0;
 | |
|         u64 mtime_sec = 0;
 | |
|         u64 mtime_nsec = 0;
 | |
|         message << fid() << (u64)valid << mode << uid << gid << new_size << atime_sec << atime_nsec << mtime_sec << mtime_nsec;
 | |
|         return fs().post_message_and_wait_for_a_reply(message);
 | |
|     }
 | |
| 
 | |
|     // TODO: wstat version
 | |
|     return {};
 | |
| }
 | |
| 
 | |
| }
 | 
