#include #include namespace Kernel { BAN::ErrorOr TmpFileSystem::create(size_t max_pages, mode_t mode, uid_t uid, gid_t gid) { auto* result = new TmpFileSystem(max_pages); if (result == nullptr) return BAN::Error::from_errno(ENOMEM); TRY(result->initialize(mode, uid, gid)); return result; } TmpFileSystem::TmpFileSystem(size_t max_pages) : m_max_pages(max_pages) { } BAN::ErrorOr TmpFileSystem::initialize(mode_t mode, uid_t uid, gid_t gid) { paddr_t data_paddr = Heap::get().take_free_page(); if (data_paddr == 0) return BAN::Error::from_errno(ENOMEM); m_data_pages.set_paddr(data_paddr); m_data_pages.set_flags(PageInfo::Flags::Present); PageTable::with_fast_page(data_paddr, [&] { memset(PageTable::fast_page_as_ptr(), 0x00, PAGE_SIZE); }); paddr_t inodes_paddr = Heap::get().take_free_page(); if (inodes_paddr == 0) return BAN::Error::from_errno(ENOMEM); m_inode_pages.set_paddr(inodes_paddr); m_inode_pages.set_flags(PageInfo::Flags::Present); PageTable::with_fast_page(inodes_paddr, [&] { memset(PageTable::fast_page_as_ptr(), 0x00, PAGE_SIZE); }); m_root_inode = TRY(TmpDirectoryInode::create_root(*this, mode, uid, gid)); return {}; } TmpFileSystem::~TmpFileSystem() { ASSERT_NOT_REACHED(); } BAN::ErrorOr> TmpFileSystem::open_inode(ino_t ino) { LockGuard _(m_lock); if (m_inode_cache.contains(ino)) return m_inode_cache[ino]; TmpInodeInfo inode_info; auto inode_location = find_inode(ino); PageTable::with_fast_page(inode_location.paddr, [&] { inode_info = PageTable::fast_page_as_sized(inode_location.index); }); auto inode = TRY(TmpInode::create_from_existing(*this, ino, inode_info)); TRY(m_inode_cache.insert(ino, inode)); return inode; } BAN::ErrorOr TmpFileSystem::add_to_cache(BAN::RefPtr inode) { LockGuard _(m_lock); if (!m_inode_cache.contains(inode->ino())) TRY(m_inode_cache.insert(inode->ino(), inode)); return {}; } void TmpFileSystem::remove_from_cache(BAN::RefPtr inode) { LockGuard _(m_lock); ASSERT(m_inode_cache.contains(inode->ino())); m_inode_cache.remove(inode->ino()); } void TmpFileSystem::read_inode(ino_t ino, TmpInodeInfo& out) { LockGuard _(m_lock); auto inode_location = find_inode(ino); PageTable::with_fast_page(inode_location.paddr, [&] { out = PageTable::fast_page_as_sized(inode_location.index); }); } void TmpFileSystem::write_inode(ino_t ino, const TmpInodeInfo& info) { LockGuard _(m_lock); auto inode_location = find_inode(ino); PageTable::with_fast_page(inode_location.paddr, [&] { auto& inode_info = PageTable::fast_page_as_sized(inode_location.index); inode_info = info; }); } void TmpFileSystem::delete_inode(ino_t ino) { LockGuard _(m_lock); auto inode_location = find_inode(ino); PageTable::with_fast_page(inode_location.paddr, [&] { auto& inode_info = PageTable::fast_page_as_sized(inode_location.index); ASSERT_EQ(inode_info.nlink, 0); for (auto paddr : inode_info.block) ASSERT_EQ(paddr, 0); inode_info = {}; }); ASSERT(!m_inode_cache.contains(ino)); } BAN::ErrorOr TmpFileSystem::allocate_inode(const TmpInodeInfo& info) { LockGuard _(m_lock); constexpr size_t inodes_per_page = PAGE_SIZE / sizeof(TmpInodeInfo); ino_t ino = first_inode; TRY(for_each_indirect_paddr_allocating(m_inode_pages, [&](paddr_t paddr, bool) { BAN::Iteration result = BAN::Iteration::Continue; PageTable::with_fast_page(paddr, [&] { for (size_t i = 0; i < inodes_per_page; i++, ino++) { auto& inode_info = PageTable::fast_page_as_sized(i); if (inode_info.mode != 0) continue; inode_info = info; result = BAN::Iteration::Break; return; } }); return result; }, 2)); return ino; } TmpFileSystem::InodeLocation TmpFileSystem::find_inode(ino_t ino) { LockGuard _(m_lock); ASSERT_GTE(ino, first_inode); ASSERT_LT(ino, max_inodes); constexpr size_t inodes_per_page = PAGE_SIZE / sizeof(TmpInodeInfo); size_t index_of_page = (ino - first_inode) / inodes_per_page; size_t index_in_page = (ino - first_inode) % inodes_per_page; return { .paddr = find_indirect(m_inode_pages, index_of_page, 2), .index = index_in_page }; } void TmpFileSystem::free_block(size_t index) { LockGuard _(m_lock); constexpr size_t addresses_per_page = PAGE_SIZE / sizeof(PageInfo); const size_t index_of_page = (index - first_data_page) / addresses_per_page; const size_t index_in_page = (index - first_data_page) % addresses_per_page; paddr_t page_containing = find_indirect(m_data_pages, index_of_page, 2); PageTable::with_fast_page(page_containing, [&] { auto& page_info = PageTable::fast_page_as_sized(index_in_page); ASSERT(page_info.flags() & PageInfo::Flags::Present); Heap::get().release_page(page_info.paddr()); page_info.set_paddr(0); page_info.set_flags(0); }); } BAN::ErrorOr TmpFileSystem::allocate_block() { LockGuard _(m_lock); size_t result = first_data_page; TRY(for_each_indirect_paddr_allocating(m_data_pages, [&] (paddr_t paddr, bool allocated) { if (allocated) return BAN::Iteration::Break; result++; return BAN::Iteration::Continue; }, 3)); return result; } paddr_t TmpFileSystem::find_block(size_t index) { LockGuard _(m_lock); ASSERT_GT(index, 0); return find_indirect(m_data_pages, index - first_data_page, 3); } paddr_t TmpFileSystem::find_indirect(PageInfo root, size_t index, size_t depth) { LockGuard _(m_lock); ASSERT(root.flags() & PageInfo::Flags::Present); if (depth == 0) { ASSERT(index == 0); return root.paddr(); } constexpr size_t addresses_per_page = PAGE_SIZE / sizeof(PageInfo); size_t divisor = 1; for (size_t i = 1; i < depth; i++) divisor *= addresses_per_page; size_t index_of_page = index / divisor; size_t index_in_page = index % divisor; ASSERT(index_of_page < addresses_per_page); PageInfo next; PageTable::with_fast_page(root.paddr(), [&] { next = PageTable::fast_page_as_sized(index_of_page); }); return find_indirect(next, index_in_page, depth - 1); } template BAN::ErrorOr TmpFileSystem::for_each_indirect_paddr_allocating_internal(PageInfo page_info, F callback, size_t depth) { LockGuard _(m_lock); ASSERT(page_info.flags() & PageInfo::Flags::Present); if (depth == 0) { bool is_new_block = page_info.flags() & PageInfo::Flags::Internal; return callback(page_info.paddr(), is_new_block); } for (size_t i = 0; i < PAGE_SIZE / sizeof(PageInfo); i++) { PageInfo next_info; PageTable::with_fast_page(page_info.paddr(), [&] { next_info = PageTable::fast_page_as_sized(i); }); if (!(next_info.flags() & PageInfo::Flags::Present)) { paddr_t new_paddr = Heap::get().take_free_page(); if (new_paddr == 0) return BAN::Error::from_errno(ENOMEM); PageTable::with_fast_page(new_paddr, [&] { memset(PageTable::fast_page_as_ptr(), 0x00, PAGE_SIZE); }); next_info.set_paddr(new_paddr); next_info.set_flags(PageInfo::Flags::Present); PageTable::with_fast_page(page_info.paddr(), [&] { auto& to_update_info = PageTable::fast_page_as_sized(i); to_update_info = next_info; }); // Don't sync the internal bit to actual memory next_info.set_flags(PageInfo::Flags::Internal | PageInfo::Flags::Present); } auto result = TRY(for_each_indirect_paddr_allocating_internal(next_info, callback, depth - 1)); switch (result) { case BAN::Iteration::Continue: break; case BAN::Iteration::Break: return BAN::Iteration::Break; default: ASSERT_NOT_REACHED(); } } return BAN::Iteration::Continue; } template BAN::ErrorOr TmpFileSystem::for_each_indirect_paddr_allocating(PageInfo page_info, F callback, size_t depth) { LockGuard _(m_lock); BAN::Iteration result = TRY(for_each_indirect_paddr_allocating_internal(page_info, callback, depth)); ASSERT(result == BAN::Iteration::Break); return {}; } }