/* * Copyright (c) 2018-2021, Andreas Kling * Copyright (c) 2021, sin-ack * Copyright (c) 2022, Idan Horowitz * * SPDX-License-Identifier: BSD-2-Clause */ #include #include #include #include #include #include #include #include #include #include #include #include namespace Kernel { static Singleton> s_all_instances; SpinlockProtected& Inode::all_instances() { return s_all_instances; } void Inode::sync_all() { Vector, 32> inodes; Inode::all_instances().with([&](auto& all_inodes) { for (auto& inode : all_inodes) { if (inode.is_metadata_dirty()) inodes.append(inode); } }); for (auto& inode : inodes) { VERIFY(inode->is_metadata_dirty()); (void)inode->flush_metadata(); } } void Inode::sync() { if (is_metadata_dirty()) (void)flush_metadata(); fs().flush_writes(); } ErrorOr> Inode::read_entire(OpenFileDescription* description) const { auto builder = TRY(KBufferBuilder::try_create()); u8 buffer[4096]; off_t offset = 0; for (;;) { auto buf = UserOrKernelBuffer::for_kernel_buffer(buffer); auto nread = TRY(read_bytes(offset, sizeof(buffer), buf, description)); VERIFY(nread <= sizeof(buffer)); if (nread == 0) break; TRY(builder.append((char const*)buffer, nread)); offset += nread; if (nread < sizeof(buffer)) break; } auto entire_file = builder.build(); if (!entire_file) return ENOMEM; return entire_file.release_nonnull(); } ErrorOr> Inode::resolve_as_link(Credentials const& credentials, Custody& base, RefPtr* out_parent, int options, int symlink_recursion_level) const { // The default implementation simply treats the stored // contents as a path and resolves that. That is, it // behaves exactly how you would expect a symlink to work. auto contents = TRY(read_entire()); return VirtualFileSystem::the().resolve_path(credentials, StringView { contents->bytes() }, base, out_parent, options, symlink_recursion_level); } Inode::Inode(FileSystem& fs, InodeIndex index) : m_file_system(fs) , m_index(index) { Inode::all_instances().with([&](auto& all_inodes) { all_inodes.append(*this); }); } Inode::~Inode() { m_watchers.for_each([&](auto& watcher) { watcher->unregister_by_inode({}, identifier()); }); } void Inode::will_be_destroyed() { MutexLocker locker(m_inode_lock); if (m_metadata_dirty) (void)flush_metadata(); } ErrorOr Inode::write_bytes(off_t offset, size_t length, UserOrKernelBuffer const& target_buffer, OpenFileDescription* open_description) { MutexLocker locker(m_inode_lock); TRY(prepare_to_write_data()); return write_bytes_locked(offset, length, target_buffer, open_description); } ErrorOr Inode::read_bytes(off_t offset, size_t length, UserOrKernelBuffer& buffer, OpenFileDescription* open_description) const { MutexLocker locker(m_inode_lock, Mutex::Mode::Shared); return read_bytes_locked(offset, length, buffer, open_description); } ErrorOr Inode::update_timestamps([[maybe_unused]] Optional