diff --git a/kernel/include/kernel/FS/Ext2/FileSystem.h b/kernel/include/kernel/FS/Ext2/FileSystem.h index 7691522137..144a9be515 100644 --- a/kernel/include/kernel/FS/Ext2/FileSystem.h +++ b/kernel/include/kernel/FS/Ext2/FileSystem.h @@ -11,6 +11,18 @@ namespace Kernel class Ext2FS final : public FileSystem { public: + virtual unsigned long bsize() const override; + virtual unsigned long frsize() const override; + virtual fsblkcnt_t blocks() const override; + virtual fsblkcnt_t bfree() const override; + virtual fsblkcnt_t bavail() const override; + virtual fsfilcnt_t files() const override; + virtual fsfilcnt_t ffree() const override; + virtual fsfilcnt_t favail() const override; + virtual unsigned long fsid() const override; + virtual unsigned long flag() const override; + virtual unsigned long namemax() const override; + class BlockBufferWrapper { BAN_NON_COPYABLE(BlockBufferWrapper); diff --git a/kernel/include/kernel/FS/Ext2/Inode.h b/kernel/include/kernel/FS/Ext2/Inode.h index 6a2ea7a646..46baa9cf60 100644 --- a/kernel/include/kernel/FS/Ext2/Inode.h +++ b/kernel/include/kernel/FS/Ext2/Inode.h @@ -29,6 +29,8 @@ namespace Kernel virtual dev_t dev() const override { return 0; } virtual dev_t rdev() const override { return 0; } + virtual const FileSystem* filesystem() const override; + protected: virtual BAN::ErrorOr> find_inode_impl(BAN::StringView) override; virtual BAN::ErrorOr list_next_inodes_impl(off_t, struct dirent*, size_t) override; diff --git a/kernel/include/kernel/FS/FAT/FileSystem.h b/kernel/include/kernel/FS/FAT/FileSystem.h index 9dcc3cd8fe..3ff6d15570 100644 --- a/kernel/include/kernel/FS/FAT/FileSystem.h +++ b/kernel/include/kernel/FS/FAT/FileSystem.h @@ -20,6 +20,18 @@ namespace Kernel }; public: + virtual unsigned long bsize() const override; + virtual unsigned long frsize() const override; + virtual fsblkcnt_t blocks() const override; + virtual fsblkcnt_t bfree() const override; + virtual fsblkcnt_t bavail() const override; + virtual fsfilcnt_t files() const override; + virtual fsfilcnt_t ffree() const override; + virtual fsfilcnt_t favail() const override; + virtual unsigned long fsid() const override; + virtual unsigned long flag() const override; + virtual unsigned long namemax() const override; + static BAN::ErrorOr probe(BAN::RefPtr); static BAN::ErrorOr> create(BAN::RefPtr); @@ -45,13 +57,15 @@ namespace Kernel // TODO: These probably should be constant variables uint32_t root_sector_count() const { return BAN::Math::div_round_up(m_bpb.root_entry_count * 32, m_bpb.bytes_per_sector); } - uint32_t fat_sector_count() const { return m_bpb.fat_size16 ? m_bpb.fat_size16 : m_bpb.ext_32.fat_size32; } + uint32_t fat_size() const { return m_bpb.fat_size16 ? m_bpb.fat_size16 : m_bpb.ext_32.fat_size32; } uint32_t total_sector_count() const { return m_bpb.total_sectors16 ? m_bpb.total_sectors16 : m_bpb.total_sectors32; } - uint32_t data_sector_count() const { return total_sector_count() - (m_bpb.reserved_sector_count + (m_bpb.number_of_fats * fat_sector_count()) + root_sector_count()); } + uint32_t data_sector_count() const { return total_sector_count() - (m_bpb.reserved_sector_count + (m_bpb.number_of_fats * fat_size()) + root_sector_count()); } uint32_t cluster_count() const { return data_sector_count() / m_bpb.sectors_per_cluster; } - uint32_t first_data_sector() const { return m_bpb.reserved_sector_count + (m_bpb.number_of_fats * fat_sector_count()) + root_sector_count(); } + uint32_t first_data_sector() const { return m_bpb.reserved_sector_count + (m_bpb.number_of_fats * fat_size()) + root_sector_count(); } uint32_t first_fat_sector() const { return m_bpb.reserved_sector_count; } + uint8_t bits_per_fat_entry() const { return static_cast(m_type); } + private: BAN::RefPtr m_block_device; BAN::RefPtr m_root_inode; diff --git a/kernel/include/kernel/FS/FAT/Inode.h b/kernel/include/kernel/FS/FAT/Inode.h index f7cfd2c856..8d248a9d8e 100644 --- a/kernel/include/kernel/FS/FAT/Inode.h +++ b/kernel/include/kernel/FS/FAT/Inode.h @@ -29,6 +29,8 @@ namespace Kernel virtual dev_t dev() const override { return 0; } virtual dev_t rdev() const override { return 0; } + virtual const FileSystem* filesystem() const override; + const FAT::DirectoryEntry& entry() const { return m_entry; } protected: diff --git a/kernel/include/kernel/FS/FileSystem.h b/kernel/include/kernel/FS/FileSystem.h index 6a3cef0af0..b785d7255f 100644 --- a/kernel/include/kernel/FS/FileSystem.h +++ b/kernel/include/kernel/FS/FileSystem.h @@ -9,6 +9,18 @@ namespace Kernel class FileSystem : public BAN::RefCounted { public: + virtual unsigned long bsize() const = 0; + virtual unsigned long frsize() const = 0; + virtual fsblkcnt_t blocks() const = 0; + virtual fsblkcnt_t bfree() const = 0; + virtual fsblkcnt_t bavail() const = 0; + virtual fsfilcnt_t files() const = 0; + virtual fsfilcnt_t ffree() const = 0; + virtual fsfilcnt_t favail() const = 0; + virtual unsigned long fsid() const = 0; + virtual unsigned long flag() const = 0; + virtual unsigned long namemax() const = 0; + virtual ~FileSystem() {} static BAN::ErrorOr> from_block_device(BAN::RefPtr); diff --git a/kernel/include/kernel/FS/Inode.h b/kernel/include/kernel/FS/Inode.h index 5f9a314bc6..4b714169a1 100644 --- a/kernel/include/kernel/FS/Inode.h +++ b/kernel/include/kernel/FS/Inode.h @@ -19,6 +19,8 @@ namespace Kernel { + class FileSystem; + class FileBackedRegion; class SharedFileData; @@ -86,6 +88,8 @@ namespace Kernel virtual bool is_pipe() const { return false; } virtual bool is_tty() const { return false; } + virtual const FileSystem* filesystem() const = 0; + // Directory API BAN::ErrorOr> find_inode(BAN::StringView); BAN::ErrorOr list_next_inodes(off_t, struct dirent* list, size_t list_size); diff --git a/kernel/include/kernel/FS/Pipe.h b/kernel/include/kernel/FS/Pipe.h index f4d597e8f0..ea4642537f 100644 --- a/kernel/include/kernel/FS/Pipe.h +++ b/kernel/include/kernel/FS/Pipe.h @@ -30,6 +30,8 @@ namespace Kernel virtual dev_t dev() const override { return 0; } // FIXME virtual dev_t rdev() const override { return 0; } // FIXME + virtual const FileSystem* filesystem() const override { return nullptr; } + protected: virtual BAN::ErrorOr read_impl(off_t, BAN::ByteSpan) override; virtual BAN::ErrorOr write_impl(off_t, BAN::ConstByteSpan) override; diff --git a/kernel/include/kernel/FS/Socket.h b/kernel/include/kernel/FS/Socket.h index 686044152b..474ff27906 100644 --- a/kernel/include/kernel/FS/Socket.h +++ b/kernel/include/kernel/FS/Socket.h @@ -44,6 +44,8 @@ namespace Kernel dev_t dev() const final override { ASSERT_NOT_REACHED(); } dev_t rdev() const final override { ASSERT_NOT_REACHED(); } + const FileSystem* filesystem() const final override { return nullptr; } + protected: Socket(const Info& info) : m_info(info) diff --git a/kernel/include/kernel/FS/TmpFS/FileSystem.h b/kernel/include/kernel/FS/TmpFS/FileSystem.h index 4740d49b9d..0898c8399e 100644 --- a/kernel/include/kernel/FS/TmpFS/FileSystem.h +++ b/kernel/include/kernel/FS/TmpFS/FileSystem.h @@ -40,6 +40,19 @@ namespace Kernel static constexpr size_t no_page_limit = SIZE_MAX; public: + // FIXME: basically all of these :) + virtual unsigned long bsize() const override { return PAGE_SIZE; } + virtual unsigned long frsize() const override { return PAGE_SIZE; } + virtual fsblkcnt_t blocks() const override { return m_max_pages; } + virtual fsblkcnt_t bfree() const override { return m_max_pages - m_used_pages; } + virtual fsblkcnt_t bavail() const override { return m_max_pages - m_used_pages; } + virtual fsfilcnt_t files() const override { return PAGE_SIZE * blocks() / sizeof(TmpDirectoryEntry); } + virtual fsfilcnt_t ffree() const override { return PAGE_SIZE * bfree() / sizeof(TmpDirectoryEntry); } + virtual fsfilcnt_t favail() const override { return PAGE_SIZE * bavail() / sizeof(TmpDirectoryEntry); } + virtual unsigned long fsid() const override { return reinterpret_cast(this); } + virtual unsigned long flag() const override { return 0; } + virtual unsigned long namemax() const override { return PAGE_SIZE; } + static BAN::ErrorOr create(size_t max_pages, mode_t, uid_t, gid_t); ~TmpFileSystem(); diff --git a/kernel/include/kernel/FS/TmpFS/Inode.h b/kernel/include/kernel/FS/TmpFS/Inode.h index 4402c40d6a..79bd9dd864 100644 --- a/kernel/include/kernel/FS/TmpFS/Inode.h +++ b/kernel/include/kernel/FS/TmpFS/Inode.h @@ -42,6 +42,8 @@ namespace Kernel static BAN::ErrorOr> create_from_existing(TmpFileSystem&, ino_t, const TmpInodeInfo&); ~TmpInode(); + virtual const FileSystem* filesystem() const override; + protected: TmpInode(TmpFileSystem&, ino_t, const TmpInodeInfo&); diff --git a/kernel/include/kernel/FS/VirtualFileSystem.h b/kernel/include/kernel/FS/VirtualFileSystem.h index d4aa4ba70a..d023d8acd6 100644 --- a/kernel/include/kernel/FS/VirtualFileSystem.h +++ b/kernel/include/kernel/FS/VirtualFileSystem.h @@ -8,9 +8,22 @@ namespace Kernel { - class VirtualFileSystem : public FileSystem + class VirtualFileSystem final : public FileSystem { public: + virtual unsigned long bsize() const override { return 0; } + virtual unsigned long frsize() const override { return 0; } + virtual fsblkcnt_t blocks() const override { return 0; } + virtual fsblkcnt_t bfree() const override { return 0; } + virtual fsblkcnt_t bavail() const override { return 0; } + virtual fsfilcnt_t files() const override { return 0; } + virtual fsfilcnt_t ffree() const override { return 0; } + virtual fsfilcnt_t favail() const override { return 0; } + virtual unsigned long fsid() const override { return 0; } + virtual unsigned long flag() const override { return 0; } + virtual unsigned long namemax() const override { return 0; } + + static void initialize(BAN::StringView); static VirtualFileSystem& get(); diff --git a/kernel/include/kernel/Process.h b/kernel/include/kernel/Process.h index 6b22c16cf8..e7c0b721b3 100644 --- a/kernel/include/kernel/Process.h +++ b/kernel/include/kernel/Process.h @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -147,6 +148,7 @@ namespace Kernel BAN::ErrorOr sys_fsync(int fd); BAN::ErrorOr sys_fstatat(int fd, const char* path, struct stat* buf, int flag); + BAN::ErrorOr sys_fstatvfsat(int fd, const char* path, struct statvfs* buf); BAN::ErrorOr sys_realpath(const char* path, char* buffer); diff --git a/kernel/kernel/FS/Ext2/FileSystem.cpp b/kernel/kernel/FS/Ext2/FileSystem.cpp index f720a3e78a..a0cd180681 100644 --- a/kernel/kernel/FS/Ext2/FileSystem.cpp +++ b/kernel/kernel/FS/Ext2/FileSystem.cpp @@ -32,6 +32,18 @@ namespace Kernel return superblock.magic == Ext2::Enum::SUPER_MAGIC; } + unsigned long Ext2FS::bsize() const { return block_size(); } + unsigned long Ext2FS::frsize() const { return block_size(); } + fsblkcnt_t Ext2FS::blocks() const { return m_superblock.blocks_count; } + fsblkcnt_t Ext2FS::bfree() const { return m_superblock.free_blocks_count; } + fsblkcnt_t Ext2FS::bavail() const { return m_superblock.free_blocks_count; } // FIXME + fsfilcnt_t Ext2FS::files() const { return m_superblock.inodes_count; } + fsfilcnt_t Ext2FS::ffree() const { return m_superblock.free_inodes_count; } + fsfilcnt_t Ext2FS::favail() const { return m_superblock.free_inodes_count; } // FIXME + unsigned long Ext2FS::fsid() const { return *reinterpret_cast(m_superblock.uuid); } // FIXME? + unsigned long Ext2FS::flag() const { return 0; } + unsigned long Ext2FS::namemax() const { return 0xFF; } + BAN::ErrorOr> Ext2FS::create(BAN::RefPtr block_device) { auto ext2fs = TRY(BAN::RefPtr::create(block_device)); diff --git a/kernel/kernel/FS/Ext2/Inode.cpp b/kernel/kernel/FS/Ext2/Inode.cpp index b449d64d04..c33d3224ea 100644 --- a/kernel/kernel/FS/Ext2/Inode.cpp +++ b/kernel/kernel/FS/Ext2/Inode.cpp @@ -47,6 +47,11 @@ namespace Kernel dwarnln("Could not cleanup inode from FS: {}", ret.error()); } + const FileSystem* Ext2Inode::filesystem() const + { + return &m_fs; + } + BAN::ErrorOr> Ext2Inode::block_from_indirect_block(uint32_t block, uint32_t index, uint32_t depth) { if (block == 0) diff --git a/kernel/kernel/FS/FAT/FileSystem.cpp b/kernel/kernel/FS/FAT/FileSystem.cpp index 63391aee9d..a3ab8afcbf 100644 --- a/kernel/kernel/FS/FAT/FileSystem.cpp +++ b/kernel/kernel/FS/FAT/FileSystem.cpp @@ -64,6 +64,18 @@ namespace Kernel return validate_bpb(bpb_span.as()); } + unsigned long FATFS::bsize() const { return m_bpb.bytes_per_sector; } + unsigned long FATFS::frsize() const { return m_bpb.bytes_per_sector; } + fsblkcnt_t FATFS::blocks() const { return data_sector_count(); } + fsblkcnt_t FATFS::bfree() const { return 0; } // FIXME + fsblkcnt_t FATFS::bavail() const { return 0; } // FIXME + fsfilcnt_t FATFS::files() const { return m_bpb.number_of_fats * fat_size() * 8 / bits_per_fat_entry(); } + fsfilcnt_t FATFS::ffree() const { return 0; } // FIXME + fsfilcnt_t FATFS::favail() const { return 0; } // FIXME + unsigned long FATFS::fsid() const { return m_type == Type::FAT32 ? m_bpb.ext_32.volume_id : m_bpb.ext_12_16.volume_id; } + unsigned long FATFS::flag() const { return 0; } + unsigned long FATFS::namemax() const { return 255; } + BAN::ErrorOr> FATFS::create(BAN::RefPtr block_device) { // support only block devices with sectors at least 512 bytes @@ -226,7 +238,7 @@ namespace Kernel { if (index >= root_sector_count()) return BAN::Error::from_errno(ENOENT); - const uint32_t first_root_sector = m_bpb.reserved_sector_count + (m_bpb.number_of_fats * fat_sector_count()); + const uint32_t first_root_sector = m_bpb.reserved_sector_count + (m_bpb.number_of_fats * fat_size()); TRY(m_block_device->read_blocks(first_root_sector + index, 1, buffer)); return {}; } diff --git a/kernel/kernel/FS/FAT/Inode.cpp b/kernel/kernel/FS/FAT/Inode.cpp index 7542a9a4c0..642ec785ab 100644 --- a/kernel/kernel/FS/FAT/Inode.cpp +++ b/kernel/kernel/FS/FAT/Inode.cpp @@ -46,6 +46,11 @@ namespace Kernel return timespec { .tv_sec = epoch, .tv_nsec = 0 }; } + const FileSystem* FATInode::filesystem() const + { + return &m_fs; + } + BAN::ErrorOr FATInode::for_each_directory_entry(BAN::ConstByteSpan entry_span, BAN::Function callback) { ASSERT(mode().ifdir()); diff --git a/kernel/kernel/FS/TmpFS/Inode.cpp b/kernel/kernel/FS/TmpFS/Inode.cpp index bd18f7aee6..6987798129 100644 --- a/kernel/kernel/FS/TmpFS/Inode.cpp +++ b/kernel/kernel/FS/TmpFS/Inode.cpp @@ -41,6 +41,11 @@ namespace Kernel /* GENERAL INODE */ + const FileSystem* TmpInode::filesystem() const + { + return &m_fs; + } + dev_t TmpInode::dev() const { return m_fs.dev(); diff --git a/kernel/kernel/Process.cpp b/kernel/kernel/Process.cpp index 76846a0728..bc69f32cdf 100644 --- a/kernel/kernel/Process.cpp +++ b/kernel/kernel/Process.cpp @@ -1496,6 +1496,35 @@ namespace Kernel return 0; } + BAN::ErrorOr Process::sys_fstatvfsat(int fd, const char* path, struct statvfs* buf) + { + LockGuard _(m_process_lock); + TRY(validate_pointer_access(buf, sizeof(struct statvfs), true)); + + auto inode = TRY(find_file(fd, path, 0)).inode; + auto* fs = inode->filesystem(); + if (fs == nullptr) + { + ASSERT(inode->mode().ifsock() || inode->mode().ififo()); + dwarnln("TODO: fstatvfs on sockets or pipe?"); + return BAN::Error::from_errno(EINVAL); + } + + buf->f_bsize = fs->bsize(); + buf->f_frsize = fs->frsize(); + buf->f_blocks = fs->blocks(); + buf->f_bfree = fs->bfree(); + buf->f_bavail = fs->bavail(); + buf->f_files = fs->files(); + buf->f_ffree = fs->ffree(); + buf->f_favail = fs->favail(); + buf->f_fsid = fs->fsid(); + buf->f_flag = fs->flag(); + buf->f_namemax = fs->namemax(); + + return 0; + } + BAN::ErrorOr Process::sys_realpath(const char* path, char* buffer) { LockGuard _(m_process_lock); diff --git a/userspace/libraries/LibC/CMakeLists.txt b/userspace/libraries/LibC/CMakeLists.txt index 7fc564fc10..8ff1a9d67c 100644 --- a/userspace/libraries/LibC/CMakeLists.txt +++ b/userspace/libraries/LibC/CMakeLists.txt @@ -32,6 +32,7 @@ set(LIBC_SOURCES sys/select.cpp sys/socket.cpp sys/stat.cpp + sys/statvfs.cpp sys/time.cpp sys/utsname.cpp sys/wait.cpp diff --git a/userspace/libraries/LibC/include/sys/syscall.h b/userspace/libraries/LibC/include/sys/syscall.h index dbe1ee762e..78a4356549 100644 --- a/userspace/libraries/LibC/include/sys/syscall.h +++ b/userspace/libraries/LibC/include/sys/syscall.h @@ -48,6 +48,7 @@ __BEGIN_DECLS O(SYS_FCNTL, fcntl) \ O(SYS_NANOSLEEP, nanosleep) \ O(SYS_FSTATAT, fstatat) \ + O(SYS_FSTATVFSAT, fstatvfsat) \ O(SYS_SYNC, sync) \ O(SYS_MMAP, mmap) \ O(SYS_MUNMAP, munmap) \ diff --git a/userspace/libraries/LibC/sys/statvfs.cpp b/userspace/libraries/LibC/sys/statvfs.cpp new file mode 100644 index 0000000000..d969f8d4c9 --- /dev/null +++ b/userspace/libraries/LibC/sys/statvfs.cpp @@ -0,0 +1,18 @@ +#include +#include +#include + +static int fstatvfsat(int fildes, const char* path, struct statvfs* buf) +{ + return syscall(SYS_FSTATVFSAT, fildes, path, buf); +} + +int fstatvfs(int fildes, struct statvfs* buf) +{ + return fstatvfsat(fildes, nullptr, buf); +} + +int statvfs(const char* __restrict path, struct statvfs* __restrict buf) +{ + return fstatvfsat(AT_FDCWD, path, buf); +}