diff --git a/Kernel/FileSystem/TmpFS.cpp b/Kernel/FileSystem/TmpFS.cpp new file mode 100644 index 0000000000..4b0281ecfb --- /dev/null +++ b/Kernel/FileSystem/TmpFS.cpp @@ -0,0 +1,341 @@ +#include +#include +#include + +NonnullRefPtr TmpFS::create() +{ + return adopt(*new TmpFS); +} + +TmpFS::TmpFS() +{ +} + +TmpFS::~TmpFS() +{ +} + +bool TmpFS::initialize() +{ + m_root_inode = TmpFSInode::create_root(*this); + return true; +} + +InodeIdentifier TmpFS::root_inode() const +{ + ASSERT(!m_root_inode.is_null()); + return m_root_inode->identifier(); +} + +void TmpFS::register_inode(TmpFSInode& inode) +{ + LOCKER(m_lock); + ASSERT(inode.identifier().fsid() == fsid()); + + unsigned index = inode.identifier().index(); + m_inodes.set(index, inode); +} + +void TmpFS::unregister_inode(InodeIdentifier identifier) +{ + LOCKER(m_lock); + ASSERT(identifier.fsid() == fsid()); + + m_inodes.remove(identifier.index()); +} + +unsigned TmpFS::next_inode_index() +{ + LOCKER(m_lock); + + return m_next_inode_index++; +} + +RefPtr TmpFS::get_inode(InodeIdentifier identifier) const +{ + LOCKER(m_lock); + ASSERT(identifier.fsid() == fsid()); + + auto it = m_inodes.find(identifier.index()); + if (it == m_inodes.end()) + return nullptr; + return it->value; +} + +RefPtr TmpFS::create_inode(InodeIdentifier parent_id, const String& name, mode_t mode, off_t size, dev_t dev, int& error) +{ + LOCKER(m_lock); + ASSERT(parent_id.fsid() == fsid()); + + ASSERT(size == 0); + ASSERT(dev == 0); + + struct timeval now; + kgettimeofday(now); + + InodeMetadata metadata; + metadata.mode = mode; + metadata.uid = current->process().euid(); + metadata.gid = current->process().egid(); + metadata.atime = now.tv_sec; + metadata.ctime = now.tv_sec; + metadata.mtime = now.tv_sec; + + auto inode = TmpFSInode::create(*this, metadata, parent_id); + + auto it = m_inodes.find(parent_id.index()); + ASSERT(it != m_inodes.end()); + auto parent_inode = it->value; + error = parent_inode->add_child(inode->identifier(), name, mode); + + return inode; +} + +RefPtr TmpFS::create_directory(InodeIdentifier parent_id, const String& name, mode_t mode, int& error) +{ + // Ensure it's a directory. + mode &= ~0170000; + mode |= 0040000; + return create_inode(parent_id, name, mode, 0, 0, error); +} + +TmpFSInode::TmpFSInode(TmpFS& fs, InodeMetadata metadata, InodeIdentifier parent) + : Inode(fs, fs.next_inode_index()) + , m_metadata(metadata) + , m_parent(parent) +{ + m_metadata.inode = identifier(); +} + +TmpFSInode::~TmpFSInode() +{ +} + +NonnullRefPtr TmpFSInode::create(TmpFS& fs, InodeMetadata metadata, InodeIdentifier parent) +{ + auto inode = adopt(*new TmpFSInode(fs, metadata, parent)); + fs.register_inode(inode); + return inode; +} + +NonnullRefPtr TmpFSInode::create_root(TmpFS& fs) +{ + InodeMetadata metadata; + metadata.mode = 0040777; + return create(fs, metadata, { fs.fsid(), 1 }); +} + +InodeMetadata TmpFSInode::metadata() const +{ + LOCKER(m_lock); + + return m_metadata; +} + +bool TmpFSInode::traverse_as_directory(Function callback) const +{ + LOCKER(m_lock); + + if (!is_directory()) + return false; + + for (auto& it : m_children) + callback(it.value.entry); + return true; +} + +ssize_t TmpFSInode::read_bytes(off_t offset, ssize_t size, u8* buffer, FileDescription*) const +{ + LOCKER(m_lock); + ASSERT(!is_directory()); + ASSERT(size >= 0); + + if (!m_content.has_value()) + return 0; + + if (static_cast(size) > m_content.value().size() - offset) + size = m_content.value().size() - offset; + memcpy(buffer, m_content.value().data(), size); + + return size; +} + +ssize_t TmpFSInode::write_bytes(off_t offset, ssize_t size, const u8* buffer, FileDescription*) +{ + LOCKER(m_lock); + ASSERT(!is_directory()); + + if (!m_content.has_value()) { + ASSERT(offset == 0); + m_content = KBuffer::copy(buffer, size); + m_metadata.size = size; + set_metadata_dirty(true); + set_metadata_dirty(false); + return size; + } + + if (static_cast(size) > m_content.value().size() - offset) { + auto tmp = KBuffer::create_with_size(offset + size); + memcpy(tmp.data(), m_content.value().data(), m_content.value().size()); + m_content = move(tmp); + m_metadata.size = offset + size; + set_metadata_dirty(true); + set_metadata_dirty(false); + } + + memcpy(m_content.value().data() + offset, buffer, size); + + return size; +} + +InodeIdentifier TmpFSInode::lookup(StringView name) +{ + LOCKER(m_lock); + ASSERT(is_directory()); + + if (name == ".") + return identifier(); + if (name == "..") + return m_parent; + + auto it = m_children.find(name); + if (it == m_children.end()) + return {}; + return it->value.entry.inode; +} + +size_t TmpFSInode::directory_entry_count() const +{ + LOCKER(m_lock); + ASSERT(is_directory()); + + return 2 + m_children.size(); +} + +void TmpFSInode::flush_metadata() +{ + // We don't really have any metadata that could become dirty. + // The only reason we even call set_metadata_dirty() is + // to let the watchers know we have updates. Once that is + // switched to a different mechanism, we can stop ever marking + // our metadata as dirty at all. + set_metadata_dirty(false); +} + +KResult TmpFSInode::chmod(mode_t mode) +{ + LOCKER(m_lock); + + m_metadata.mode = mode; + set_metadata_dirty(true); + set_metadata_dirty(false); + return KSuccess; +} + +KResult TmpFSInode::chown(uid_t uid, gid_t gid) +{ + LOCKER(m_lock); + + m_metadata.uid = uid; + m_metadata.gid = gid; + set_metadata_dirty(true); + set_metadata_dirty(false); + return KSuccess; +} + +KResult TmpFSInode::add_child(InodeIdentifier child_id, const StringView& name, mode_t) +{ + LOCKER(m_lock); + ASSERT(is_directory()); + ASSERT(child_id.fsid() == fsid()); + + String owned_name = name; + FS::DirectoryEntry entry = { owned_name.characters(), owned_name.length(), child_id, 0 }; + RefPtr child_tmp = fs().get_inode(child_id); + NonnullRefPtr child = static_cast>(child_tmp.release_nonnull()); + + m_children.set(owned_name, { entry, move(child) }); + set_metadata_dirty(true); + set_metadata_dirty(false); + return KSuccess; +} + +KResult TmpFSInode::remove_child(const StringView& name) +{ + LOCKER(m_lock); + ASSERT(is_directory()); + + auto it = m_children.find(name); + if (it == m_children.end()) + return KResult(-ENOENT); + m_children.remove(it); + set_metadata_dirty(true); + set_metadata_dirty(false); + return KSuccess; +} + +KResult TmpFSInode::truncate(off_t size) +{ + LOCKER(m_lock); + ASSERT(!is_directory()); + + if (size == 0) + m_content.clear(); + else if (!m_content.has_value()) { + m_content = KBuffer::create_with_size(size); + memset(m_content.value().data(), 0, size); + } else if (static_cast(size) < m_content.value().capacity()) { + size_t prev_size = m_content.value().size(); + m_content.value().set_size(size); + if (prev_size < static_cast(size)) + memset(m_content.value().data() + prev_size, 0, size - prev_size); + } else { + size_t prev_size = m_content.value().size(); + KBuffer tmp = KBuffer::create_with_size(size); + memcpy(tmp.data(), m_content.value().data(), prev_size); + memset(tmp.data() + prev_size, 0, size - prev_size); + m_content = move(tmp); + } + + m_metadata.size = size; + set_metadata_dirty(true); + set_metadata_dirty(false); + + return KSuccess; +} + +int TmpFSInode::set_atime(time_t time) +{ + LOCKER(m_lock); + + m_metadata.atime = time; + set_metadata_dirty(true); + set_metadata_dirty(false); + return KSuccess; +} + +int TmpFSInode::set_ctime(time_t time) +{ + LOCKER(m_lock); + + m_metadata.ctime = time; + set_metadata_dirty(true); + set_metadata_dirty(false); + return KSuccess; +} + +int TmpFSInode::set_mtime(time_t time) +{ + LOCKER(m_lock); + + m_metadata.mtime = time; + set_metadata_dirty(true); + set_metadata_dirty(false); + return KSuccess; +} + +void TmpFSInode::one_ref_left() +{ + // Destroy ourselves. + fs().unregister_inode(identifier()); +} diff --git a/Kernel/FileSystem/TmpFS.h b/Kernel/FileSystem/TmpFS.h new file mode 100644 index 0000000000..0ce6a30d72 --- /dev/null +++ b/Kernel/FileSystem/TmpFS.h @@ -0,0 +1,80 @@ +#pragma once + +#include +#include +#include +#include + +class TmpFSInode; + +class TmpFS final : public FS { + friend class TmpFSInode; + +public: + virtual ~TmpFS() override; + static NonnullRefPtr create(); + virtual bool initialize() override; + + virtual const char* class_name() const override { return "TmpFS"; } + + virtual InodeIdentifier root_inode() const override; + virtual RefPtr get_inode(InodeIdentifier) const override; + + virtual RefPtr create_inode(InodeIdentifier parent_id, const String& name, mode_t, off_t size, dev_t, int& error) override; + virtual RefPtr create_directory(InodeIdentifier parent_id, const String& name, mode_t, int& error) override; + +private: + TmpFS(); + + RefPtr m_root_inode; + + HashMap> m_inodes; + void register_inode(TmpFSInode&); + void unregister_inode(InodeIdentifier); + + unsigned m_next_inode_index { 1 }; + unsigned next_inode_index(); +}; + +class TmpFSInode final : public Inode { + friend class TmpFS; + +public: + virtual ~TmpFSInode() override; + + TmpFS& fs() { return static_cast(Inode::fs()); } + const TmpFS& fs() const { return static_cast(Inode::fs()); } + + // ^Inode + virtual ssize_t read_bytes(off_t, ssize_t, u8* buffer, FileDescription*) const override; + virtual InodeMetadata metadata() const override; + virtual bool traverse_as_directory(Function) const override; + virtual InodeIdentifier lookup(StringView name) override; + virtual void flush_metadata() override; + virtual ssize_t write_bytes(off_t, ssize_t, const u8* buffer, FileDescription*) override; + virtual KResult add_child(InodeIdentifier child_id, const StringView& name, mode_t) override; + virtual KResult remove_child(const StringView& name) override; + virtual size_t directory_entry_count() const override; + virtual KResult chmod(mode_t) override; + virtual KResult chown(uid_t, gid_t) override; + virtual KResult truncate(off_t) override; + virtual int set_atime(time_t) override; + virtual int set_ctime(time_t) override; + virtual int set_mtime(time_t) override; + virtual void one_ref_left() override; + +private: + TmpFSInode(TmpFS& fs, InodeMetadata metadata, InodeIdentifier parent); + static NonnullRefPtr create(TmpFS&, InodeMetadata metadata, InodeIdentifier parent); + static NonnullRefPtr create_root(TmpFS&); + + InodeMetadata m_metadata; + InodeIdentifier m_parent; + + Optional m_content; + struct Child { + FS::DirectoryEntry entry; + NonnullRefPtr inode; + }; + HashMap m_children; +}; diff --git a/Kernel/Makefile b/Kernel/Makefile index db031937a9..97e1a17369 100644 --- a/Kernel/Makefile +++ b/Kernel/Makefile @@ -65,6 +65,7 @@ KERNEL_OBJS = \ VFS_OBJS = \ FileSystem/ProcFS.o \ + FileSystem/TmpFS.o \ FileSystem/Inode.o \ Devices/DiskDevice.o \ Devices/Device.o \ diff --git a/Kernel/init.cpp b/Kernel/init.cpp index fa6c072bae..496e3fb5aa 100644 --- a/Kernel/init.cpp +++ b/Kernel/init.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -117,9 +118,15 @@ VFS* vfs; load_ksyms(); dbgprintf("Loaded ksyms\n"); + // TODO: we should mount these from SystemServer vfs->mount(ProcFS::the(), "/proc"); vfs->mount(DevPtsFS::the(), "/dev/pts"); + auto tmpfs = TmpFS::create(); + if (!tmpfs->initialize()) + ASSERT_NOT_REACHED(); + vfs->mount(move(tmpfs), "/tmp"); + // Now, detect whether or not there are actually any floppy disks attached to the system u8 detect = CMOS::read(0x10); RefPtr fd0;