Compare commits
16 Commits
6d899aa6ce
...
a0fbf18d3b
Author | SHA1 | Date |
---|---|---|
Bananymous | a0fbf18d3b | |
Bananymous | 1acc0abf2e | |
Bananymous | c20f773c5d | |
Bananymous | a46b2f43d9 | |
Bananymous | a20f8607de | |
Bananymous | af330f7b8e | |
Bananymous | e33b3bcdff | |
Bananymous | 181d139c7d | |
Bananymous | 639fd8804c | |
Bananymous | cbb2c37e00 | |
Bananymous | ab4f033385 | |
Bananymous | 1ed08f62d3 | |
Bananymous | 8164c15b6c | |
Bananymous | f9bf47ab30 | |
Bananymous | e5ffadb109 | |
Bananymous | 061d10e635 |
|
@ -97,8 +97,7 @@ namespace BAN
|
|||
}
|
||||
|
||||
template<typename S>
|
||||
requires(is_const_v<S>)
|
||||
Span<S> as_span() const
|
||||
const Span<S> as_span() const
|
||||
{
|
||||
ASSERT(m_data);
|
||||
return Span<S>(reinterpret_cast<S*>(m_data), m_size / sizeof(S));
|
||||
|
|
|
@ -28,6 +28,8 @@ set(KERNEL_SOURCES
|
|||
kernel/FS/ProcFS/Inode.cpp
|
||||
kernel/FS/RamFS/FileSystem.cpp
|
||||
kernel/FS/RamFS/Inode.cpp
|
||||
kernel/FS/TmpFS/FileSystem.cpp
|
||||
kernel/FS/TmpFS/Inode.cpp
|
||||
kernel/FS/VirtualFileSystem.cpp
|
||||
kernel/Input/PS2Controller.cpp
|
||||
kernel/Input/PS2Keyboard.cpp
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
#pragma once
|
||||
|
||||
#include <kernel/FS/RamFS/FileSystem.h>
|
||||
#include <kernel/FS/RamFS/Inode.h>
|
||||
#include <kernel/FS/TmpFS/FileSystem.h>
|
||||
#include <kernel/FS/TmpFS/Inode.h>
|
||||
#include <kernel/Process.h>
|
||||
|
||||
namespace Kernel
|
||||
{
|
||||
|
||||
class ProcFileSystem final : public RamFileSystem
|
||||
class ProcFileSystem final : public TmpFileSystem
|
||||
{
|
||||
public:
|
||||
static void initialize();
|
||||
|
@ -17,10 +17,7 @@ namespace Kernel
|
|||
void on_process_delete(Process&);
|
||||
|
||||
private:
|
||||
ProcFileSystem(size_t size);
|
||||
|
||||
private:
|
||||
BAN::RefPtr<RamDirectoryInode> m_root_inode;
|
||||
ProcFileSystem();
|
||||
};
|
||||
|
||||
}
|
|
@ -1,41 +1,46 @@
|
|||
#pragma once
|
||||
|
||||
#include <kernel/FS/RamFS/FileSystem.h>
|
||||
#include <kernel/FS/RamFS/Inode.h>
|
||||
#include <kernel/FS/TmpFS/FileSystem.h>
|
||||
#include <kernel/FS/TmpFS/Inode.h>
|
||||
#include <kernel/Process.h>
|
||||
|
||||
namespace Kernel
|
||||
{
|
||||
|
||||
class ProcPidInode final : public RamDirectoryInode
|
||||
class ProcPidInode final : public TmpDirectoryInode
|
||||
{
|
||||
public:
|
||||
static BAN::ErrorOr<BAN::RefPtr<ProcPidInode>> create(Process&, RamFileSystem&, mode_t, uid_t, gid_t);
|
||||
static BAN::ErrorOr<BAN::RefPtr<ProcPidInode>> create_new(Process&, TmpFileSystem&, mode_t, uid_t, gid_t);
|
||||
~ProcPidInode() = default;
|
||||
|
||||
void cleanup();
|
||||
|
||||
protected:
|
||||
virtual BAN::ErrorOr<void> unlink_impl(BAN::StringView) override { return BAN::Error::from_errno(EPERM); }
|
||||
|
||||
private:
|
||||
ProcPidInode(Process&, RamFileSystem&, const FullInodeInfo&);
|
||||
ProcPidInode(Process&, TmpFileSystem&, const TmpInodeInfo&);
|
||||
|
||||
private:
|
||||
Process& m_process;
|
||||
};
|
||||
|
||||
class ProcROInode final : public RamInode
|
||||
class ProcROInode final : public TmpInode
|
||||
{
|
||||
public:
|
||||
static BAN::ErrorOr<BAN::RefPtr<ProcROInode>> create(Process&, size_t (Process::*callback)(off_t, BAN::ByteSpan) const, RamFileSystem&, mode_t, uid_t, gid_t);
|
||||
static BAN::ErrorOr<BAN::RefPtr<ProcROInode>> create_new(Process&, size_t (Process::*callback)(off_t, BAN::ByteSpan) const, TmpFileSystem&, mode_t, uid_t, gid_t);
|
||||
~ProcROInode() = default;
|
||||
|
||||
protected:
|
||||
virtual BAN::ErrorOr<size_t> read_impl(off_t, BAN::ByteSpan) override;
|
||||
|
||||
// You may not write here and this is always non blocking
|
||||
virtual BAN::ErrorOr<size_t> write_impl(off_t, BAN::ConstByteSpan) override { return BAN::Error::from_errno(EINVAL); }
|
||||
virtual BAN::ErrorOr<size_t> write_impl(off_t, BAN::ConstByteSpan) override { return BAN::Error::from_errno(EINVAL); }
|
||||
virtual BAN::ErrorOr<void> truncate_impl(size_t) override { return BAN::Error::from_errno(EINVAL); }
|
||||
virtual bool has_data_impl() const override { return true; }
|
||||
|
||||
private:
|
||||
ProcROInode(Process&, size_t (Process::*)(off_t, BAN::ByteSpan) const, RamFileSystem&, const FullInodeInfo&);
|
||||
ProcROInode(Process&, size_t (Process::*)(off_t, BAN::ByteSpan) const, TmpFileSystem&, const TmpInodeInfo&);
|
||||
|
||||
private:
|
||||
Process& m_process;
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
#pragma once
|
||||
|
||||
#include <BAN/Array.h>
|
||||
#include <kernel/Memory/Types.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <time.h>
|
||||
|
||||
namespace Kernel
|
||||
{
|
||||
|
||||
struct TmpInodeInfo
|
||||
{
|
||||
mode_t mode { 0 };
|
||||
uid_t uid { 0 };
|
||||
gid_t gid { 0 };
|
||||
timespec atime { 0 };
|
||||
timespec ctime { 0 };
|
||||
timespec mtime { 0 };
|
||||
nlink_t nlink { 0 };
|
||||
size_t size { 0 };
|
||||
blkcnt_t blocks { 0 };
|
||||
|
||||
// 2x direct blocks
|
||||
// 1x singly indirect
|
||||
// 1x doubly indirect
|
||||
// 1x triply indirect
|
||||
BAN::Array<paddr_t, 5> block;
|
||||
static constexpr size_t direct_block_count = 2;
|
||||
static constexpr size_t max_size =
|
||||
direct_block_count * PAGE_SIZE +
|
||||
(PAGE_SIZE / sizeof(paddr_t)) * PAGE_SIZE +
|
||||
(PAGE_SIZE / sizeof(paddr_t)) * (PAGE_SIZE / sizeof(paddr_t)) * PAGE_SIZE +
|
||||
(PAGE_SIZE / sizeof(paddr_t)) * (PAGE_SIZE / sizeof(paddr_t)) * (PAGE_SIZE / sizeof(paddr_t)) * PAGE_SIZE;
|
||||
};
|
||||
static_assert(sizeof(TmpInodeInfo) == 128);
|
||||
|
||||
struct TmpDirectoryEntry
|
||||
{
|
||||
ino_t ino;
|
||||
uint8_t type;
|
||||
size_t name_len;
|
||||
size_t rec_len;
|
||||
char name[];
|
||||
|
||||
BAN::StringView name_sv() const
|
||||
{
|
||||
ASSERT(type != DT_UNKNOWN);
|
||||
return BAN::StringView(name, name_len);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
|
@ -0,0 +1,150 @@
|
|||
#pragma once
|
||||
|
||||
#include <BAN/HashMap.h>
|
||||
#include <BAN/Iteration.h>
|
||||
#include <kernel/FS/FileSystem.h>
|
||||
#include <kernel/FS/TmpFS/Inode.h>
|
||||
#include <kernel/Memory/PageTable.h>
|
||||
#include <kernel/SpinLock.h>
|
||||
|
||||
namespace Kernel
|
||||
{
|
||||
|
||||
namespace TmpFuncs
|
||||
{
|
||||
|
||||
template<typename F>
|
||||
concept for_each_indirect_paddr_allocating_callback = requires(F func, paddr_t paddr, bool was_allocated)
|
||||
{
|
||||
requires BAN::is_same_v<decltype(func(paddr, was_allocated)), BAN::Iteration>;
|
||||
};
|
||||
|
||||
template<typename F>
|
||||
concept with_block_buffer_callback = requires(F func, BAN::ByteSpan buffer)
|
||||
{
|
||||
requires BAN::is_same_v<decltype(func(buffer)), void>;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
||||
class TmpFileSystem : public FileSystem
|
||||
{
|
||||
public:
|
||||
static constexpr size_t no_page_limit = SIZE_MAX;
|
||||
|
||||
public:
|
||||
static BAN::ErrorOr<TmpFileSystem*> create(size_t max_pages, mode_t, uid_t, gid_t);
|
||||
~TmpFileSystem();
|
||||
|
||||
virtual BAN::RefPtr<Inode> root_inode() override { return m_root_inode; }
|
||||
|
||||
BAN::ErrorOr<BAN::RefPtr<TmpInode>> open_inode(ino_t ino);
|
||||
|
||||
BAN::ErrorOr<void> add_to_cache(BAN::RefPtr<TmpInode>);
|
||||
void remove_from_cache(BAN::RefPtr<TmpInode>);
|
||||
|
||||
// FIXME: read_block and write_block should not require external buffer
|
||||
// probably some wrapper like PageTable::with_fast_page could work?
|
||||
|
||||
void read_inode(ino_t ino, TmpInodeInfo& out);
|
||||
void write_inode(ino_t ino, const TmpInodeInfo&);
|
||||
void delete_inode(ino_t ino);
|
||||
BAN::ErrorOr<ino_t> allocate_inode(const TmpInodeInfo&);
|
||||
|
||||
template<TmpFuncs::with_block_buffer_callback F>
|
||||
void with_block_buffer(size_t index, F callback);
|
||||
void free_block(size_t index);
|
||||
BAN::ErrorOr<size_t> allocate_block();
|
||||
|
||||
private:
|
||||
struct PageInfo
|
||||
{
|
||||
enum Flags : paddr_t
|
||||
{
|
||||
Present = 1 << 0,
|
||||
Internal = 1 << 1,
|
||||
};
|
||||
|
||||
// 12 bottom bits of paddr can be used as flags, since
|
||||
// paddr will always be page aligned.
|
||||
static constexpr size_t flag_bits = 12;
|
||||
static constexpr paddr_t flags_mask = (1 << flag_bits) - 1;
|
||||
static constexpr paddr_t paddr_mask = ~flags_mask;
|
||||
static_assert((1 << flag_bits) <= PAGE_SIZE);
|
||||
|
||||
paddr_t paddr() const { return raw & paddr_mask; }
|
||||
paddr_t flags() const { return raw & flags_mask; }
|
||||
|
||||
void set_paddr(paddr_t paddr) { raw = (raw & flags_mask) | (paddr & paddr_mask); }
|
||||
void set_flags(paddr_t flags) { raw = (raw & paddr_mask) | (flags & flags_mask); }
|
||||
|
||||
paddr_t raw { 0 };
|
||||
};
|
||||
|
||||
struct InodeLocation
|
||||
{
|
||||
paddr_t paddr;
|
||||
size_t index;
|
||||
};
|
||||
|
||||
protected:
|
||||
TmpFileSystem(size_t max_pages);
|
||||
BAN::ErrorOr<void> initialize(mode_t, uid_t, gid_t);
|
||||
|
||||
private:
|
||||
InodeLocation find_inode(ino_t ino);
|
||||
|
||||
paddr_t find_block(size_t index);
|
||||
|
||||
template<TmpFuncs::for_each_indirect_paddr_allocating_callback F>
|
||||
BAN::ErrorOr<void> for_each_indirect_paddr_allocating(PageInfo page_info, F callback, size_t depth);
|
||||
template<TmpFuncs::for_each_indirect_paddr_allocating_callback F>
|
||||
BAN::ErrorOr<BAN::Iteration> for_each_indirect_paddr_allocating_internal(PageInfo page_info, F callback, size_t depth);
|
||||
|
||||
paddr_t find_indirect(PageInfo root, size_t index, size_t depth);
|
||||
|
||||
private:
|
||||
RecursiveSpinLock m_lock;
|
||||
|
||||
BAN::HashMap<ino_t, BAN::RefPtr<TmpInode>> m_inode_cache;
|
||||
BAN::RefPtr<TmpDirectoryInode> m_root_inode;
|
||||
|
||||
// We store pages with triple indirection.
|
||||
// With 64-bit pointers we can store 512^3 pages of data (512 GiB)
|
||||
// which should be enough for now.
|
||||
// In future this should be dynamically calculated based on maximum
|
||||
// number of pages for this file system.
|
||||
PageInfo m_data_pages {};
|
||||
static constexpr size_t first_data_page = 1;
|
||||
static constexpr size_t max_data_pages =
|
||||
(PAGE_SIZE / sizeof(PageInfo)) *
|
||||
(PAGE_SIZE / sizeof(PageInfo)) *
|
||||
(PAGE_SIZE / sizeof(PageInfo));
|
||||
|
||||
// We store inodes in pages with double indirection.
|
||||
// With 64-bit pointers we can store 512^2 pages of inodes
|
||||
// which should be enough for now.
|
||||
// In future this should be dynamically calculated based on maximum
|
||||
// number of pages for this file system.
|
||||
PageInfo m_inode_pages;
|
||||
static constexpr size_t first_inode = 1;
|
||||
static constexpr size_t max_inodes =
|
||||
(PAGE_SIZE / sizeof(PageInfo)) *
|
||||
(PAGE_SIZE / sizeof(PageInfo)) *
|
||||
(PAGE_SIZE / sizeof(TmpInodeInfo));
|
||||
|
||||
const size_t m_max_pages;
|
||||
};
|
||||
|
||||
template<TmpFuncs::with_block_buffer_callback F>
|
||||
void TmpFileSystem::with_block_buffer(size_t index, F callback)
|
||||
{
|
||||
paddr_t block_paddr = find_block(index);
|
||||
PageTable::with_fast_page(block_paddr, [&] {
|
||||
BAN::ByteSpan buffer(reinterpret_cast<uint8_t*>(PageTable::fast_page()), PAGE_SIZE);
|
||||
callback(buffer);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,123 @@
|
|||
#pragma once
|
||||
|
||||
#include <BAN/Iteration.h>
|
||||
#include <BAN/Optional.h>
|
||||
#include <kernel/FS/Inode.h>
|
||||
#include <kernel/FS/TmpFS/Definitions.h>
|
||||
|
||||
namespace Kernel
|
||||
{
|
||||
|
||||
namespace TmpFuncs
|
||||
{
|
||||
|
||||
template<typename F>
|
||||
concept for_each_valid_entry_callback = requires(F func, TmpDirectoryEntry& entry)
|
||||
{
|
||||
requires BAN::is_same_v<decltype(func(entry)), BAN::Iteration>;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
class TmpFileSystem;
|
||||
|
||||
class TmpInode : public Inode
|
||||
{
|
||||
public:
|
||||
virtual ino_t ino() const override final { return m_ino; }
|
||||
virtual Mode mode() const override final { return Mode(m_inode_info.mode); }
|
||||
virtual nlink_t nlink() const override final { return m_inode_info.nlink; }
|
||||
virtual uid_t uid() const override final { return m_inode_info.uid; }
|
||||
virtual gid_t gid() const override final { return m_inode_info.gid; }
|
||||
virtual off_t size() const override final { return m_inode_info.size; }
|
||||
virtual timespec atime() const override final { return m_inode_info.atime; }
|
||||
virtual timespec mtime() const override final { return m_inode_info.mtime; }
|
||||
virtual timespec ctime() const override final { return m_inode_info.ctime; }
|
||||
virtual blksize_t blksize() const override final { return PAGE_SIZE; }
|
||||
virtual blkcnt_t blocks() const override final { return m_inode_info.blocks; }
|
||||
virtual dev_t dev() const override final { return 0; } // TODO
|
||||
virtual dev_t rdev() const override final { return 0; } // TODO
|
||||
|
||||
public:
|
||||
static BAN::ErrorOr<BAN::RefPtr<TmpInode>> create_from_existing(TmpFileSystem&, ino_t, const TmpInodeInfo&);
|
||||
~TmpInode();
|
||||
|
||||
protected:
|
||||
TmpInode(TmpFileSystem&, ino_t, const TmpInodeInfo&);
|
||||
|
||||
void sync();
|
||||
void free_all_blocks();
|
||||
virtual BAN::ErrorOr<void> prepare_unlink() { return {}; };
|
||||
|
||||
BAN::Optional<size_t> block_index(size_t data_block_index);
|
||||
BAN::ErrorOr<size_t> block_index_with_allocation(size_t data_block_index);
|
||||
|
||||
protected:
|
||||
TmpFileSystem& m_fs;
|
||||
TmpInodeInfo m_inode_info;
|
||||
const ino_t m_ino;
|
||||
|
||||
// has to be able to increase link count
|
||||
friend class TmpDirectoryInode;
|
||||
};
|
||||
|
||||
class TmpFileInode : public TmpInode
|
||||
{
|
||||
public:
|
||||
static BAN::ErrorOr<BAN::RefPtr<TmpFileInode>> create_new(TmpFileSystem&, mode_t, uid_t, gid_t);
|
||||
~TmpFileInode();
|
||||
|
||||
protected:
|
||||
virtual BAN::ErrorOr<size_t> read_impl(off_t, BAN::ByteSpan) override;
|
||||
virtual BAN::ErrorOr<size_t> write_impl(off_t, BAN::ConstByteSpan) override;
|
||||
virtual BAN::ErrorOr<void> truncate_impl(size_t) override;
|
||||
virtual BAN::ErrorOr<void> chmod_impl(mode_t) override;
|
||||
virtual bool has_data_impl() const override { return true; }
|
||||
|
||||
private:
|
||||
TmpFileInode(TmpFileSystem&, ino_t, const TmpInodeInfo&);
|
||||
|
||||
friend class TmpInode;
|
||||
};
|
||||
|
||||
class TmpSymlinkInode : public TmpInode
|
||||
{
|
||||
public:
|
||||
~TmpSymlinkInode();
|
||||
|
||||
private:
|
||||
TmpSymlinkInode(TmpFileSystem&, ino_t, const TmpInodeInfo&, BAN::StringView target);
|
||||
};
|
||||
|
||||
class TmpDirectoryInode : public TmpInode
|
||||
{
|
||||
public:
|
||||
static BAN::ErrorOr<BAN::RefPtr<TmpDirectoryInode>> create_root(TmpFileSystem&, mode_t, uid_t, gid_t);
|
||||
static BAN::ErrorOr<BAN::RefPtr<TmpDirectoryInode>> create_new(TmpFileSystem&, mode_t, uid_t, gid_t, TmpInode& parent);
|
||||
|
||||
~TmpDirectoryInode();
|
||||
|
||||
BAN::ErrorOr<void> link_inode(TmpInode&, BAN::StringView);
|
||||
|
||||
protected:
|
||||
TmpDirectoryInode(TmpFileSystem&, ino_t, const TmpInodeInfo&);
|
||||
|
||||
virtual BAN::ErrorOr<void> prepare_unlink() override;
|
||||
|
||||
protected:
|
||||
virtual BAN::ErrorOr<BAN::RefPtr<Inode>> find_inode_impl(BAN::StringView) override final;
|
||||
virtual BAN::ErrorOr<void> list_next_inodes_impl(off_t, DirectoryEntryList*, size_t) override final;
|
||||
virtual BAN::ErrorOr<void> create_file_impl(BAN::StringView, mode_t, uid_t, gid_t) override final;
|
||||
virtual BAN::ErrorOr<void> create_directory_impl(BAN::StringView, mode_t, uid_t, gid_t) override final;
|
||||
virtual BAN::ErrorOr<void> unlink_impl(BAN::StringView) override;
|
||||
|
||||
private:
|
||||
template<TmpFuncs::for_each_valid_entry_callback F>
|
||||
void for_each_valid_entry(F callback);
|
||||
|
||||
friend class TmpInode;
|
||||
};
|
||||
|
||||
TmpInodeInfo create_inode_info(mode_t mode, uid_t uid, gid_t gid);
|
||||
|
||||
}
|
|
@ -1,12 +1,20 @@
|
|||
#pragma once
|
||||
|
||||
#include <BAN/Errors.h>
|
||||
#include <BAN/Traits.h>
|
||||
#include <kernel/CriticalScope.h>
|
||||
#include <kernel/Memory/Types.h>
|
||||
#include <kernel/SpinLock.h>
|
||||
|
||||
namespace Kernel
|
||||
{
|
||||
|
||||
template<typename F>
|
||||
concept with_fast_page_callback = requires(F func)
|
||||
{
|
||||
requires BAN::is_same_v<decltype(func()), void>;
|
||||
};
|
||||
|
||||
class PageTable
|
||||
{
|
||||
public:
|
||||
|
@ -33,6 +41,15 @@ namespace Kernel
|
|||
static void unmap_fast_page();
|
||||
static constexpr vaddr_t fast_page() { return KERNEL_OFFSET; }
|
||||
|
||||
template<with_fast_page_callback F>
|
||||
static void with_fast_page(paddr_t paddr, F callback)
|
||||
{
|
||||
CriticalScope _;
|
||||
map_fast_page(paddr);
|
||||
callback();
|
||||
unmap_fast_page();
|
||||
}
|
||||
|
||||
// FIXME: implement sized checks, return span, etc
|
||||
static void* fast_page_as_ptr(size_t offset = 0)
|
||||
{
|
||||
|
@ -47,6 +64,14 @@ namespace Kernel
|
|||
return *reinterpret_cast<T*>(fast_page() + offset);
|
||||
}
|
||||
|
||||
// Retrieves index'th element from fast_page
|
||||
template<typename T>
|
||||
static T& fast_page_as_sized(size_t index)
|
||||
{
|
||||
ASSERT((index + 1) * sizeof(T) <= PAGE_SIZE);
|
||||
return *reinterpret_cast<T*>(fast_page() + index * sizeof(T));
|
||||
}
|
||||
|
||||
static bool is_valid_pointer(uintptr_t);
|
||||
|
||||
static BAN::ErrorOr<PageTable*> create_userspace();
|
||||
|
|
|
@ -60,6 +60,8 @@ namespace Kernel
|
|||
|
||||
bool is_session_leader() const { return pid() == sid(); }
|
||||
|
||||
const Credentials& credentials() const { return m_credentials; }
|
||||
|
||||
BAN::ErrorOr<long> sys_exit(int status);
|
||||
|
||||
BAN::ErrorOr<long> sys_gettermios(::termios*);
|
||||
|
|
|
@ -306,8 +306,7 @@ done:
|
|||
ASSERT(mode().ifdir());
|
||||
ASSERT(offset >= 0);
|
||||
|
||||
const uint32_t data_block_count = blocks();
|
||||
if (offset >= data_block_count)
|
||||
if (offset >= max_used_data_block_count())
|
||||
{
|
||||
list->entry_count = 0;
|
||||
return {};
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
#include <kernel/FS/ProcFS/FileSystem.h>
|
||||
#include <kernel/FS/ProcFS/Inode.h>
|
||||
#include <kernel/FS/RamFS/Inode.h>
|
||||
#include <kernel/LockGuard.h>
|
||||
|
||||
namespace Kernel
|
||||
|
@ -11,11 +10,10 @@ namespace Kernel
|
|||
void ProcFileSystem::initialize()
|
||||
{
|
||||
ASSERT(s_instance == nullptr);
|
||||
s_instance = new ProcFileSystem(1024 * 1024);
|
||||
s_instance = new ProcFileSystem();
|
||||
ASSERT(s_instance);
|
||||
|
||||
s_instance->m_root_inode = MUST(RamDirectoryInode::create(*s_instance, 0, 0555, 0, 0));
|
||||
MUST(s_instance->set_root_inode(s_instance->m_root_inode));
|
||||
MUST(s_instance->TmpFileSystem::initialize(0555, 0, 0));
|
||||
}
|
||||
|
||||
ProcFileSystem& ProcFileSystem::get()
|
||||
|
@ -24,23 +22,28 @@ namespace Kernel
|
|||
return *s_instance;
|
||||
}
|
||||
|
||||
ProcFileSystem::ProcFileSystem(size_t size)
|
||||
: RamFileSystem(size)
|
||||
ProcFileSystem::ProcFileSystem()
|
||||
: TmpFileSystem(-1)
|
||||
{
|
||||
}
|
||||
|
||||
BAN::ErrorOr<void> ProcFileSystem::on_process_create(Process& process)
|
||||
{
|
||||
auto path = BAN::String::formatted("{}", process.pid());
|
||||
auto inode = TRY(ProcPidInode::create(process, *this, 0555, 0, 0));
|
||||
TRY(m_root_inode->add_inode(path, inode));
|
||||
auto inode = TRY(ProcPidInode::create_new(process, *this, 0555, process.credentials().ruid(), process.credentials().rgid()));
|
||||
TRY(reinterpret_cast<TmpDirectoryInode*>(root_inode().ptr())->link_inode(*inode, path));
|
||||
return {};
|
||||
}
|
||||
|
||||
void ProcFileSystem::on_process_delete(Process& process)
|
||||
{
|
||||
auto path = BAN::String::formatted("{}", process.pid());
|
||||
MUST(m_root_inode->unlink(path));
|
||||
|
||||
auto inode = MUST(root_inode()->find_inode(path));
|
||||
reinterpret_cast<ProcPidInode*>(inode.ptr())->cleanup();
|
||||
|
||||
if (auto ret = root_inode()->unlink(path); ret.is_error())
|
||||
dwarnln("{}", ret.error());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -3,31 +3,38 @@
|
|||
namespace Kernel
|
||||
{
|
||||
|
||||
BAN::ErrorOr<BAN::RefPtr<ProcPidInode>> ProcPidInode::create(Process& process, RamFileSystem& fs, mode_t mode, uid_t uid, gid_t gid)
|
||||
BAN::ErrorOr<BAN::RefPtr<ProcPidInode>> ProcPidInode::create_new(Process& process, TmpFileSystem& fs, mode_t mode, uid_t uid, gid_t gid)
|
||||
{
|
||||
FullInodeInfo inode_info(fs, mode, uid, gid);
|
||||
auto inode_info = create_inode_info(Mode::IFDIR | mode, uid, gid);
|
||||
|
||||
auto* inode_ptr = new ProcPidInode(process, fs, inode_info);
|
||||
if (inode_ptr == nullptr)
|
||||
return BAN::Error::from_errno(ENOMEM);
|
||||
auto inode = BAN::RefPtr<ProcPidInode>::adopt(inode_ptr);
|
||||
|
||||
TRY(inode->add_inode("meminfo"sv, MUST(ProcROInode::create(process, &Process::proc_meminfo, fs, 0755, 0, 0))));
|
||||
TRY(inode->add_inode("cmdline"sv, MUST(ProcROInode::create(process, &Process::proc_cmdline, fs, 0755, 0, 0))));
|
||||
TRY(inode->add_inode("environ"sv, MUST(ProcROInode::create(process, &Process::proc_environ, fs, 0755, 0, 0))));
|
||||
TRY(inode->link_inode(*MUST(ProcROInode::create_new(process, &Process::proc_meminfo, fs, 0400, uid, gid)), "meminfo"sv));
|
||||
TRY(inode->link_inode(*MUST(ProcROInode::create_new(process, &Process::proc_cmdline, fs, 0400, uid, gid)), "cmdline"sv));
|
||||
TRY(inode->link_inode(*MUST(ProcROInode::create_new(process, &Process::proc_environ, fs, 0400, uid, gid)), "environ"sv));
|
||||
|
||||
return inode;
|
||||
}
|
||||
|
||||
ProcPidInode::ProcPidInode(Process& process, RamFileSystem& fs, const FullInodeInfo& inode_info)
|
||||
: RamDirectoryInode(fs, inode_info, fs.root_inode()->ino())
|
||||
ProcPidInode::ProcPidInode(Process& process, TmpFileSystem& fs, const TmpInodeInfo& inode_info)
|
||||
: TmpDirectoryInode(fs, MUST(fs.allocate_inode(inode_info)), inode_info)
|
||||
, m_process(process)
|
||||
{
|
||||
}
|
||||
|
||||
BAN::ErrorOr<BAN::RefPtr<ProcROInode>> ProcROInode::create(Process& process, size_t (Process::*callback)(off_t, BAN::ByteSpan) const, RamFileSystem& fs, mode_t mode, uid_t uid, gid_t gid)
|
||||
void ProcPidInode::cleanup()
|
||||
{
|
||||
FullInodeInfo inode_info(fs, mode, uid, gid);
|
||||
(void)TmpDirectoryInode::unlink_impl("meminfo"sv);
|
||||
(void)TmpDirectoryInode::unlink_impl("cmdline"sv);
|
||||
(void)TmpDirectoryInode::unlink_impl("environ"sv);
|
||||
}
|
||||
|
||||
BAN::ErrorOr<BAN::RefPtr<ProcROInode>> ProcROInode::create_new(Process& process, size_t (Process::*callback)(off_t, BAN::ByteSpan) const, TmpFileSystem& fs, mode_t mode, uid_t uid, gid_t gid)
|
||||
{
|
||||
auto inode_info = create_inode_info(Mode::IFREG | mode, uid, gid);
|
||||
|
||||
auto* inode_ptr = new ProcROInode(process, callback, fs, inode_info);
|
||||
if (inode_ptr == nullptr)
|
||||
|
@ -35,8 +42,8 @@ namespace Kernel
|
|||
return BAN::RefPtr<ProcROInode>::adopt(inode_ptr);
|
||||
}
|
||||
|
||||
ProcROInode::ProcROInode(Process& process, size_t (Process::*callback)(off_t, BAN::ByteSpan) const, RamFileSystem& fs, const FullInodeInfo& inode_info)
|
||||
: RamInode(fs, inode_info)
|
||||
ProcROInode::ProcROInode(Process& process, size_t (Process::*callback)(off_t, BAN::ByteSpan) const, TmpFileSystem& fs, const TmpInodeInfo& inode_info)
|
||||
: TmpInode(fs, MUST(fs.allocate_inode(inode_info)), inode_info)
|
||||
, m_process(process)
|
||||
, m_callback(callback)
|
||||
{
|
||||
|
|
|
@ -0,0 +1,276 @@
|
|||
#include <kernel/FS/TmpFS/FileSystem.h>
|
||||
#include <kernel/Memory/Heap.h>
|
||||
|
||||
namespace Kernel
|
||||
{
|
||||
|
||||
BAN::ErrorOr<TmpFileSystem*> 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<void> 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<BAN::RefPtr<TmpInode>> TmpFileSystem::open_inode(ino_t ino)
|
||||
{
|
||||
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<TmpInodeInfo>(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<void> TmpFileSystem::add_to_cache(BAN::RefPtr<TmpInode> inode)
|
||||
{
|
||||
if (!m_inode_cache.contains(inode->ino()))
|
||||
TRY(m_inode_cache.insert(inode->ino(), inode));
|
||||
return {};
|
||||
}
|
||||
|
||||
void TmpFileSystem::remove_from_cache(BAN::RefPtr<TmpInode> inode)
|
||||
{
|
||||
ASSERT(m_inode_cache.contains(inode->ino()));
|
||||
m_inode_cache.remove(inode->ino());
|
||||
}
|
||||
|
||||
void TmpFileSystem::read_inode(ino_t ino, TmpInodeInfo& out)
|
||||
{
|
||||
auto inode_location = find_inode(ino);
|
||||
PageTable::with_fast_page(inode_location.paddr, [&] {
|
||||
out = PageTable::fast_page_as_sized<TmpInodeInfo>(inode_location.index);
|
||||
});
|
||||
}
|
||||
|
||||
void TmpFileSystem::write_inode(ino_t ino, const TmpInodeInfo& info)
|
||||
{
|
||||
auto inode_location = find_inode(ino);
|
||||
PageTable::with_fast_page(inode_location.paddr, [&] {
|
||||
auto& inode_info = PageTable::fast_page_as_sized<TmpInodeInfo>(inode_location.index);
|
||||
inode_info = info;
|
||||
});
|
||||
}
|
||||
|
||||
void TmpFileSystem::delete_inode(ino_t ino)
|
||||
{
|
||||
auto inode_location = find_inode(ino);
|
||||
PageTable::with_fast_page(inode_location.paddr, [&] {
|
||||
auto& inode_info = PageTable::fast_page_as_sized<TmpInodeInfo>(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<ino_t> TmpFileSystem::allocate_inode(const TmpInodeInfo& info)
|
||||
{
|
||||
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<TmpInodeInfo>(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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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<PageInfo>(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<size_t> TmpFileSystem::allocate_block()
|
||||
{
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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<PageInfo>(index_of_page);
|
||||
});
|
||||
|
||||
return find_indirect(next, index_in_page, depth - 1);
|
||||
}
|
||||
|
||||
template<TmpFuncs::for_each_indirect_paddr_allocating_callback F>
|
||||
BAN::ErrorOr<BAN::Iteration> TmpFileSystem::for_each_indirect_paddr_allocating_internal(PageInfo page_info, F callback, size_t depth)
|
||||
{
|
||||
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<PageInfo>(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<PageInfo>(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<TmpFuncs::for_each_indirect_paddr_allocating_callback F>
|
||||
BAN::ErrorOr<void> TmpFileSystem::for_each_indirect_paddr_allocating(PageInfo page_info, F callback, size_t depth)
|
||||
{
|
||||
BAN::Iteration result = TRY(for_each_indirect_paddr_allocating_internal(page_info, callback, depth));
|
||||
ASSERT(result == BAN::Iteration::Break);
|
||||
return {};
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,504 @@
|
|||
#include <kernel/FS/TmpFS/FileSystem.h>
|
||||
#include <kernel/FS/TmpFS/Inode.h>
|
||||
#include <kernel/Timer/Timer.h>
|
||||
|
||||
namespace Kernel
|
||||
{
|
||||
|
||||
TmpInodeInfo create_inode_info(mode_t mode, uid_t uid, gid_t gid)
|
||||
{
|
||||
auto current_time = SystemTimer::get().real_time();
|
||||
|
||||
TmpInodeInfo info;
|
||||
info.uid = uid;
|
||||
info.gid = gid;
|
||||
info.mode = mode;
|
||||
info.atime = current_time;
|
||||
info.mtime = current_time;
|
||||
info.ctime = current_time;
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
static uint8_t inode_mode_to_dt_type(Inode::Mode mode)
|
||||
{
|
||||
if (mode.ifreg())
|
||||
return DT_REG;
|
||||
if (mode.ifdir())
|
||||
return DT_DIR;
|
||||
if (mode.ifchr())
|
||||
return DT_CHR;
|
||||
if (mode.ifblk())
|
||||
return DT_BLK;
|
||||
if (mode.ififo())
|
||||
return DT_FIFO;
|
||||
if (mode.ifsock())
|
||||
return DT_SOCK;
|
||||
if (mode.iflnk())
|
||||
return DT_LNK;
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
/* GENERAL INODE */
|
||||
|
||||
BAN::ErrorOr<BAN::RefPtr<TmpInode>> TmpInode::create_from_existing(TmpFileSystem& fs, ino_t ino, const TmpInodeInfo& info)
|
||||
{
|
||||
TmpInode* inode_ptr = nullptr;
|
||||
switch (info.mode & Mode::TYPE_MASK)
|
||||
{
|
||||
case Mode::IFDIR:
|
||||
inode_ptr = new TmpDirectoryInode(fs, ino, info);
|
||||
break;
|
||||
case Mode::IFREG:
|
||||
inode_ptr = new TmpFileInode(fs, ino, info);
|
||||
break;
|
||||
default:
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
if (inode_ptr == nullptr)
|
||||
return BAN::Error::from_errno(ENOMEM);
|
||||
return BAN::RefPtr<TmpInode>::adopt(inode_ptr);
|
||||
}
|
||||
|
||||
TmpInode::TmpInode(TmpFileSystem& fs, ino_t ino, const TmpInodeInfo& info)
|
||||
: m_fs(fs)
|
||||
, m_inode_info(info)
|
||||
, m_ino(ino)
|
||||
{
|
||||
// FIXME: this should be able to fail
|
||||
MUST(fs.add_to_cache(this));
|
||||
}
|
||||
|
||||
TmpInode::~TmpInode()
|
||||
{
|
||||
if (nlink() > 0)
|
||||
{
|
||||
sync();
|
||||
return;
|
||||
}
|
||||
free_all_blocks();
|
||||
m_fs.delete_inode(ino());
|
||||
}
|
||||
|
||||
void TmpInode::sync()
|
||||
{
|
||||
m_fs.write_inode(m_ino, m_inode_info);
|
||||
}
|
||||
|
||||
void TmpInode::free_all_blocks()
|
||||
{
|
||||
for (size_t i = 0; i < TmpInodeInfo::direct_block_count; i++)
|
||||
{
|
||||
if (m_inode_info.block[i])
|
||||
m_fs.free_block(m_inode_info.block[i]);
|
||||
m_inode_info.block[i] = 0;
|
||||
}
|
||||
for (auto block : m_inode_info.block)
|
||||
ASSERT(block == 0);
|
||||
}
|
||||
|
||||
BAN::Optional<size_t> TmpInode::block_index(size_t data_block_index)
|
||||
{
|
||||
ASSERT(data_block_index < TmpInodeInfo::direct_block_count);
|
||||
if (m_inode_info.block[data_block_index])
|
||||
return m_inode_info.block[data_block_index];
|
||||
return {};
|
||||
}
|
||||
|
||||
BAN::ErrorOr<size_t> TmpInode::block_index_with_allocation(size_t data_block_index)
|
||||
{
|
||||
if (data_block_index >= TmpInodeInfo::direct_block_count)
|
||||
{
|
||||
dprintln("only {} blocks supported :D", TmpInodeInfo::direct_block_count);
|
||||
return BAN::Error::from_errno(ENOSPC);
|
||||
}
|
||||
if (m_inode_info.block[data_block_index] == 0)
|
||||
{
|
||||
m_inode_info.block[data_block_index] = TRY(m_fs.allocate_block());
|
||||
m_inode_info.blocks++;
|
||||
}
|
||||
return m_inode_info.block[data_block_index];
|
||||
}
|
||||
|
||||
/* FILE INODE */
|
||||
|
||||
BAN::ErrorOr<BAN::RefPtr<TmpFileInode>> TmpFileInode::create_new(TmpFileSystem& fs, mode_t mode, uid_t uid, gid_t gid)
|
||||
{
|
||||
auto info = create_inode_info(Mode::IFREG | mode, uid, gid);
|
||||
ino_t ino = TRY(fs.allocate_inode(info));
|
||||
|
||||
auto* inode_ptr = new TmpFileInode(fs, ino, info);
|
||||
if (inode_ptr == nullptr)
|
||||
return BAN::Error::from_errno(ENOMEM);
|
||||
|
||||
return BAN::RefPtr<TmpFileInode>::adopt(inode_ptr);
|
||||
}
|
||||
|
||||
TmpFileInode::TmpFileInode(TmpFileSystem& fs, ino_t ino, const TmpInodeInfo& info)
|
||||
: TmpInode(fs, ino, info)
|
||||
{
|
||||
ASSERT(mode().ifreg());
|
||||
}
|
||||
|
||||
TmpFileInode::~TmpFileInode()
|
||||
{
|
||||
}
|
||||
|
||||
BAN::ErrorOr<size_t> TmpFileInode::read_impl(off_t offset, BAN::ByteSpan out_buffer)
|
||||
{
|
||||
if (offset >= size() || out_buffer.size() == 0)
|
||||
return 0;
|
||||
|
||||
const size_t bytes_to_read = BAN::Math::min<size_t>(size() - offset, out_buffer.size());
|
||||
|
||||
size_t read_done = 0;
|
||||
while (read_done < bytes_to_read)
|
||||
{
|
||||
const size_t data_block_index = (read_done + offset) / blksize();
|
||||
const size_t block_offset = (read_done + offset) % blksize();
|
||||
|
||||
const auto block_index = this->block_index(data_block_index);
|
||||
|
||||
const size_t bytes = BAN::Math::min<size_t>(bytes_to_read - read_done, blksize() - block_offset);
|
||||
|
||||
if (block_index.has_value())
|
||||
m_fs.with_block_buffer(block_index.value(), [&](BAN::ByteSpan block_buffer) {
|
||||
memcpy(out_buffer.data() + read_done, block_buffer.data() + block_offset, bytes);
|
||||
});
|
||||
else
|
||||
memset(out_buffer.data() + read_done, 0x00, bytes);
|
||||
|
||||
read_done += bytes;
|
||||
}
|
||||
|
||||
return read_done;
|
||||
}
|
||||
|
||||
BAN::ErrorOr<size_t> TmpFileInode::write_impl(off_t offset, BAN::ConstByteSpan in_buffer)
|
||||
{
|
||||
// FIXME: handle overflow
|
||||
|
||||
if (offset + in_buffer.size() > (size_t)size())
|
||||
TRY(truncate_impl(offset + in_buffer.size()));
|
||||
|
||||
const size_t bytes_to_write = in_buffer.size();
|
||||
|
||||
size_t write_done = 0;
|
||||
while (write_done < bytes_to_write)
|
||||
{
|
||||
const size_t data_block_index = (write_done + offset) / blksize();
|
||||
const size_t block_offset = (write_done + offset) % blksize();
|
||||
|
||||
const size_t block_index = TRY(block_index_with_allocation(data_block_index));
|
||||
|
||||
const size_t bytes = BAN::Math::min<size_t>(bytes_to_write - write_done, blksize() - block_offset);
|
||||
|
||||
m_fs.with_block_buffer(block_index, [&](BAN::ByteSpan block_buffer) {
|
||||
memcpy(block_buffer.data() + block_offset, in_buffer.data() + write_done, bytes);
|
||||
});
|
||||
|
||||
write_done += bytes;
|
||||
}
|
||||
|
||||
return write_done;
|
||||
}
|
||||
|
||||
BAN::ErrorOr<void> TmpFileInode::truncate_impl(size_t new_size)
|
||||
{
|
||||
m_inode_info.size = new_size;
|
||||
return {};
|
||||
}
|
||||
|
||||
BAN::ErrorOr<void> TmpFileInode::chmod_impl(mode_t new_mode)
|
||||
{
|
||||
m_inode_info.mode = new_mode;
|
||||
return {};
|
||||
}
|
||||
|
||||
/* DIRECTORY INODE */
|
||||
|
||||
BAN::ErrorOr<BAN::RefPtr<TmpDirectoryInode>> TmpDirectoryInode::create_root(TmpFileSystem& fs, mode_t mode, uid_t uid, gid_t gid)
|
||||
{
|
||||
auto info = create_inode_info(Mode::IFDIR | mode, uid, gid);
|
||||
ino_t ino = TRY(fs.allocate_inode(info));
|
||||
|
||||
auto* inode_ptr = new TmpDirectoryInode(fs, ino, info);
|
||||
if (inode_ptr == nullptr)
|
||||
return BAN::Error::from_errno(ENOMEM);
|
||||
|
||||
auto inode = BAN::RefPtr<TmpDirectoryInode>::adopt(inode_ptr);
|
||||
TRY(inode->link_inode(*inode, "."sv));
|
||||
TRY(inode->link_inode(*inode, ".."sv));
|
||||
|
||||
return inode;
|
||||
}
|
||||
|
||||
BAN::ErrorOr<BAN::RefPtr<TmpDirectoryInode>> TmpDirectoryInode::create_new(TmpFileSystem& fs, mode_t mode, uid_t uid, gid_t gid, TmpInode& parent)
|
||||
{
|
||||
auto info = create_inode_info(Mode::IFDIR | mode, uid, gid);
|
||||
ino_t ino = TRY(fs.allocate_inode(info));
|
||||
|
||||
auto* inode_ptr = new TmpDirectoryInode(fs, ino, info);
|
||||
if (inode_ptr == nullptr)
|
||||
return BAN::Error::from_errno(ENOMEM);
|
||||
|
||||
auto inode = BAN::RefPtr<TmpDirectoryInode>::adopt(inode_ptr);
|
||||
TRY(inode->link_inode(*inode, "."sv));
|
||||
TRY(inode->link_inode(parent, ".."sv));
|
||||
|
||||
return inode;
|
||||
}
|
||||
|
||||
TmpDirectoryInode::TmpDirectoryInode(TmpFileSystem& fs, ino_t ino, const TmpInodeInfo& info)
|
||||
: TmpInode(fs, ino, info)
|
||||
{
|
||||
ASSERT(mode().ifdir());
|
||||
}
|
||||
|
||||
TmpDirectoryInode::~TmpDirectoryInode()
|
||||
{
|
||||
}
|
||||
|
||||
BAN::ErrorOr<void> TmpDirectoryInode::prepare_unlink()
|
||||
{
|
||||
ino_t dot_ino = 0;
|
||||
ino_t dotdot_ino = 0;
|
||||
|
||||
bool is_empty = true;
|
||||
for_each_valid_entry([&](TmpDirectoryEntry& entry) {
|
||||
if (entry.name_sv() == "."sv)
|
||||
dot_ino = entry.ino;
|
||||
else if (entry.name_sv() == ".."sv)
|
||||
dotdot_ino = entry.ino;
|
||||
else
|
||||
{
|
||||
is_empty = false;
|
||||
return BAN::Iteration::Break;
|
||||
}
|
||||
return BAN::Iteration::Continue;
|
||||
});
|
||||
if (!is_empty)
|
||||
return BAN::Error::from_errno(ENOTEMPTY);
|
||||
|
||||
// FIXME: can these leak inodes?
|
||||
|
||||
if (dot_ino)
|
||||
{
|
||||
auto inode = TRY(m_fs.open_inode(dot_ino));
|
||||
ASSERT(inode->nlink() > 0);
|
||||
inode->m_inode_info.nlink--;
|
||||
}
|
||||
|
||||
if (dotdot_ino)
|
||||
{
|
||||
auto inode = TRY(m_fs.open_inode(dotdot_ino));
|
||||
ASSERT(inode->nlink() > 0);
|
||||
inode->m_inode_info.nlink--;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
BAN::ErrorOr<BAN::RefPtr<Inode>> TmpDirectoryInode::find_inode_impl(BAN::StringView name)
|
||||
{
|
||||
ino_t result = 0;
|
||||
|
||||
for_each_valid_entry([&](TmpDirectoryEntry& entry) {
|
||||
if (entry.name_sv() != name)
|
||||
return BAN::Iteration::Continue;
|
||||
result = entry.ino;
|
||||
return BAN::Iteration::Break;
|
||||
});
|
||||
|
||||
if (result == 0)
|
||||
return BAN::Error::from_errno(ENOENT);
|
||||
|
||||
auto inode = TRY(m_fs.open_inode(result));
|
||||
return BAN::RefPtr<Inode>(inode);
|
||||
}
|
||||
|
||||
BAN::ErrorOr<void> TmpDirectoryInode::list_next_inodes_impl(off_t data_block_index, DirectoryEntryList* list, size_t list_len)
|
||||
{
|
||||
if (list_len < (size_t)blksize())
|
||||
{
|
||||
dprintln("buffer is too small");
|
||||
return BAN::Error::from_errno(ENOBUFS);
|
||||
}
|
||||
|
||||
auto block_index = this->block_index(data_block_index);
|
||||
|
||||
list->entry_count = 0;
|
||||
|
||||
// if we reach a non-allocated block, it marks the end
|
||||
if (!block_index.has_value())
|
||||
return {};
|
||||
|
||||
auto* dirp = list->array;
|
||||
|
||||
const size_t byte_count = BAN::Math::min<size_t>(size() - data_block_index * blksize(), blksize());
|
||||
m_fs.with_block_buffer(block_index.value(), [&](BAN::ByteSpan bytespan) {
|
||||
bytespan = bytespan.slice(0, byte_count);
|
||||
|
||||
while (bytespan.size() > 0)
|
||||
{
|
||||
const auto& entry = bytespan.as<TmpDirectoryEntry>();
|
||||
|
||||
if (entry.type != DT_UNKNOWN)
|
||||
{
|
||||
// TODO: dirents should be aligned
|
||||
|
||||
dirp->dirent.d_ino = entry.ino;
|
||||
dirp->dirent.d_type = entry.type;
|
||||
strncpy(dirp->dirent.d_name, entry.name, entry.name_len);
|
||||
dirp->dirent.d_name[entry.name_len] = '\0';
|
||||
dirp->rec_len = sizeof(DirectoryEntry) + entry.name_len + 1;
|
||||
dirp = dirp->next();
|
||||
|
||||
list->entry_count++;
|
||||
}
|
||||
|
||||
bytespan = bytespan.slice(entry.rec_len);
|
||||
}
|
||||
});
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
BAN::ErrorOr<void> TmpDirectoryInode::create_file_impl(BAN::StringView name, mode_t mode, uid_t uid, gid_t gid)
|
||||
{
|
||||
auto new_inode = TRY(TmpFileInode::create_new(m_fs, mode, uid, gid));
|
||||
TRY(link_inode(*new_inode, name));
|
||||
return {};
|
||||
}
|
||||
|
||||
BAN::ErrorOr<void> TmpDirectoryInode::create_directory_impl(BAN::StringView name, mode_t mode, uid_t uid, gid_t gid)
|
||||
{
|
||||
auto new_inode = TRY(TmpDirectoryInode::create_new(m_fs, mode, uid, gid, *this));
|
||||
TRY(link_inode(*new_inode, name));
|
||||
return {};
|
||||
}
|
||||
|
||||
BAN::ErrorOr<void> TmpDirectoryInode::unlink_impl(BAN::StringView name)
|
||||
{
|
||||
ino_t entry_ino = 0;
|
||||
|
||||
for_each_valid_entry([&](TmpDirectoryEntry& entry) {
|
||||
if (entry.name_sv() != name)
|
||||
return BAN::Iteration::Continue;
|
||||
entry_ino = entry.ino;
|
||||
return BAN::Iteration::Break;
|
||||
});
|
||||
|
||||
if (entry_ino == 0)
|
||||
return BAN::Error::from_errno(ENOENT);
|
||||
|
||||
auto inode = TRY(m_fs.open_inode(entry_ino));
|
||||
|
||||
ASSERT(inode->nlink() > 0);
|
||||
|
||||
TRY(inode->prepare_unlink());
|
||||
inode->m_inode_info.nlink--;
|
||||
|
||||
if (inode->nlink() == 0)
|
||||
m_fs.remove_from_cache(inode);
|
||||
|
||||
for_each_valid_entry([&](TmpDirectoryEntry& entry) {
|
||||
if (entry.name_sv() != name)
|
||||
return BAN::Iteration::Continue;
|
||||
entry.ino = 0;
|
||||
entry.type = DT_UNKNOWN;
|
||||
return BAN::Iteration::Break;
|
||||
});
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
BAN::ErrorOr<void> TmpDirectoryInode::link_inode(TmpInode& inode, BAN::StringView name)
|
||||
{
|
||||
static constexpr size_t directory_entry_alignment = 16;
|
||||
|
||||
auto find_result = find_inode_impl(name);
|
||||
if (!find_result.is_error())
|
||||
return BAN::Error::from_errno(EEXIST);
|
||||
if (find_result.error().get_error_code() != ENOENT)
|
||||
return find_result.release_error();
|
||||
|
||||
size_t new_entry_size = sizeof(TmpDirectoryEntry) + name.size();
|
||||
if (auto rem = new_entry_size % directory_entry_alignment)
|
||||
new_entry_size += directory_entry_alignment - rem;
|
||||
ASSERT(new_entry_size < (size_t)blksize());
|
||||
|
||||
size_t new_entry_offset = size() % blksize();
|
||||
|
||||
// Target is the last block, or if it doesn't fit the new entry, the next one.
|
||||
size_t target_data_block = size() / blksize();
|
||||
if (blksize() - new_entry_offset < new_entry_size)
|
||||
{
|
||||
// insert an empty entry at the end of current block
|
||||
m_fs.with_block_buffer(block_index(target_data_block).value(), [&](BAN::ByteSpan bytespan) {
|
||||
auto& empty_entry = bytespan.slice(new_entry_offset).as<TmpDirectoryEntry>();
|
||||
empty_entry.type = DT_UNKNOWN;
|
||||
empty_entry.ino = 0;
|
||||
empty_entry.rec_len = blksize() - new_entry_offset;
|
||||
});
|
||||
m_inode_info.size += blksize() - new_entry_offset;
|
||||
|
||||
target_data_block++;
|
||||
new_entry_offset = 0;
|
||||
}
|
||||
|
||||
size_t block_index = TRY(block_index_with_allocation(target_data_block));
|
||||
|
||||
m_fs.with_block_buffer(block_index, [&](BAN::ByteSpan bytespan) {
|
||||
auto& new_entry = bytespan.slice(new_entry_offset).as<TmpDirectoryEntry>();
|
||||
ASSERT(new_entry.type == DT_UNKNOWN);
|
||||
new_entry.type = inode_mode_to_dt_type(inode.mode());
|
||||
new_entry.ino = inode.ino();
|
||||
new_entry.name_len = name.size();
|
||||
new_entry.rec_len = new_entry_size;
|
||||
memcpy(new_entry.name, name.data(), name.size());
|
||||
});
|
||||
|
||||
// increase current size
|
||||
m_inode_info.size += new_entry_size;
|
||||
|
||||
// add link to linked inode
|
||||
inode.m_inode_info.nlink++;
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
template<TmpFuncs::for_each_valid_entry_callback F>
|
||||
void TmpDirectoryInode::for_each_valid_entry(F callback)
|
||||
{
|
||||
bool done = false;
|
||||
for (size_t data_block_index = 0; !done && data_block_index * blksize() < (size_t)size(); data_block_index++)
|
||||
{
|
||||
const size_t block_index = this->block_index(data_block_index).value();
|
||||
const size_t byte_count = BAN::Math::min<size_t>(size() - data_block_index * blksize(), blksize());
|
||||
|
||||
m_fs.with_block_buffer(block_index, [&](BAN::ByteSpan bytespan) {
|
||||
bytespan = bytespan.slice(0, byte_count);
|
||||
while (bytespan.size() > 0)
|
||||
{
|
||||
auto& entry = bytespan.as<TmpDirectoryEntry>();
|
||||
if (entry.type != DT_UNKNOWN)
|
||||
{
|
||||
switch (callback(entry))
|
||||
{
|
||||
case BAN::Iteration::Continue:
|
||||
break;
|
||||
case BAN::Iteration::Break:
|
||||
done = true;
|
||||
return;
|
||||
default:
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
}
|
||||
bytespan = bytespan.slice(entry.rec_len);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -3,8 +3,7 @@
|
|||
#include <kernel/FS/DevFS/FileSystem.h>
|
||||
#include <kernel/FS/Ext2/FileSystem.h>
|
||||
#include <kernel/FS/ProcFS/FileSystem.h>
|
||||
#include <kernel/FS/RamFS/FileSystem.h>
|
||||
#include <kernel/FS/RamFS/Inode.h>
|
||||
#include <kernel/FS/TmpFS/FileSystem.h>
|
||||
#include <kernel/FS/VirtualFileSystem.h>
|
||||
#include <kernel/LockGuard.h>
|
||||
#include <fcntl.h>
|
||||
|
@ -31,7 +30,7 @@ namespace Kernel
|
|||
|
||||
MUST(s_instance->mount(root_creds, &ProcFileSystem::get(), "/proc"sv));
|
||||
|
||||
auto* tmpfs = MUST(RamFileSystem::create(1024 * 1024, 0777, 0, 0));
|
||||
auto* tmpfs = MUST(TmpFileSystem::create(1024, 0777, 0, 0));
|
||||
MUST(s_instance->mount(root_creds, tmpfs, "/tmp"sv));
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#include <ctype.h>
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
@ -30,8 +31,6 @@ int main()
|
|||
if (!is_only_digits(proc_ent->d_name))
|
||||
continue;
|
||||
|
||||
printf("process: ");
|
||||
|
||||
{
|
||||
strcpy(path_buffer, proc_ent->d_name);
|
||||
strcat(path_buffer, "/cmdline");
|
||||
|
@ -39,10 +38,13 @@ int main()
|
|||
int fd = openat(dirfd(proc), path_buffer, O_RDONLY);
|
||||
if (fd == -1)
|
||||
{
|
||||
perror("openat");
|
||||
if (errno != EACCES)
|
||||
perror("openat");
|
||||
continue;
|
||||
}
|
||||
|
||||
printf("process: ");
|
||||
|
||||
while (ssize_t nread = read(fd, path_buffer, sizeof(path_buffer) - 1))
|
||||
{
|
||||
if (nread < 0)
|
||||
|
@ -61,10 +63,12 @@ int main()
|
|||
written += printf("%s ", path_buffer + written);
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
|
||||
close(fd);
|
||||
}
|
||||
|
||||
printf("\n pid: %s\n", proc_ent->d_name);
|
||||
printf(" pid: %s\n", proc_ent->d_name);
|
||||
|
||||
{
|
||||
strcpy(path_buffer, proc_ent->d_name);
|
||||
|
|
Loading…
Reference in New Issue