diff --git a/kernel/include/kernel/Device/Device.h b/kernel/include/kernel/Device/Device.h index b038946f..fac1e465 100644 --- a/kernel/include/kernel/Device/Device.h +++ b/kernel/include/kernel/Device/Device.h @@ -24,6 +24,8 @@ namespace Kernel protected: Device(mode_t, uid_t, gid_t); + + virtual BAN::ErrorOr fsync_impl() final override { return BAN::Error::from_errno(EINVAL); } }; class BlockDevice : public Device @@ -31,6 +33,7 @@ namespace Kernel public: virtual BAN::ErrorOr read_blocks(uint64_t first_block, size_t block_count, BAN::ByteSpan) = 0; virtual BAN::ErrorOr write_blocks(uint64_t first_block, size_t block_count, BAN::ConstByteSpan) = 0; + virtual BAN::ErrorOr sync_blocks(uint64_t block, size_t block_count) = 0; virtual blksize_t blksize() const = 0; diff --git a/kernel/include/kernel/FS/Ext2/FileSystem.h b/kernel/include/kernel/FS/Ext2/FileSystem.h index f7606f25..76915221 100644 --- a/kernel/include/kernel/FS/Ext2/FileSystem.h +++ b/kernel/include/kernel/FS/Ext2/FileSystem.h @@ -67,6 +67,7 @@ namespace Kernel BAN::ErrorOr read_block(uint32_t, BlockBufferWrapper&); BAN::ErrorOr write_block(uint32_t, const BlockBufferWrapper&); BAN::ErrorOr sync_superblock(); + BAN::ErrorOr sync_block(uint32_t block); BlockBufferWrapper get_block_buffer(); diff --git a/kernel/include/kernel/FS/Ext2/Inode.h b/kernel/include/kernel/FS/Ext2/Inode.h index 78130a95..6a2ea7a6 100644 --- a/kernel/include/kernel/FS/Ext2/Inode.h +++ b/kernel/include/kernel/FS/Ext2/Inode.h @@ -42,6 +42,7 @@ namespace Kernel virtual BAN::ErrorOr write_impl(off_t, BAN::ConstByteSpan) override; virtual BAN::ErrorOr truncate_impl(size_t) override; virtual BAN::ErrorOr chmod_impl(mode_t) override; + virtual BAN::ErrorOr fsync_impl() override; virtual bool can_read_impl() const override { return true; } virtual bool can_write_impl() const override { return true; } diff --git a/kernel/include/kernel/FS/FAT/Inode.h b/kernel/include/kernel/FS/FAT/Inode.h index a1fbb94e..f7cfd2c8 100644 --- a/kernel/include/kernel/FS/FAT/Inode.h +++ b/kernel/include/kernel/FS/FAT/Inode.h @@ -42,6 +42,7 @@ namespace Kernel //virtual BAN::ErrorOr write_impl(off_t, BAN::ConstByteSpan) override; //virtual BAN::ErrorOr truncate_impl(size_t) override; //virtual BAN::ErrorOr chmod_impl(mode_t) override; + virtual BAN::ErrorOr fsync_impl() override { return {}; } virtual bool can_read_impl() const override { return true; } virtual bool can_write_impl() const override { return true; } diff --git a/kernel/include/kernel/FS/Inode.h b/kernel/include/kernel/FS/Inode.h index ae2c9063..5f9a314b 100644 --- a/kernel/include/kernel/FS/Inode.h +++ b/kernel/include/kernel/FS/Inode.h @@ -111,6 +111,7 @@ namespace Kernel BAN::ErrorOr truncate(size_t); BAN::ErrorOr chmod(mode_t); BAN::ErrorOr chown(uid_t, gid_t); + BAN::ErrorOr fsync(); // Select/Non blocking API bool can_read() const; @@ -145,6 +146,7 @@ namespace Kernel virtual BAN::ErrorOr truncate_impl(size_t) { return BAN::Error::from_errno(ENOTSUP); } virtual BAN::ErrorOr chmod_impl(mode_t) { return BAN::Error::from_errno(ENOTSUP); } virtual BAN::ErrorOr chown_impl(uid_t, gid_t) { return BAN::Error::from_errno(ENOTSUP); } + virtual BAN::ErrorOr fsync_impl() = 0; // Select/Non blocking API virtual bool can_read_impl() const = 0; diff --git a/kernel/include/kernel/FS/Pipe.h b/kernel/include/kernel/FS/Pipe.h index b60545b9..f4d597e8 100644 --- a/kernel/include/kernel/FS/Pipe.h +++ b/kernel/include/kernel/FS/Pipe.h @@ -33,6 +33,7 @@ namespace Kernel protected: virtual BAN::ErrorOr read_impl(off_t, BAN::ByteSpan) override; virtual BAN::ErrorOr write_impl(off_t, BAN::ConstByteSpan) override; + virtual BAN::ErrorOr fsync_impl() final override { return {}; } virtual bool can_read_impl() const override { return m_buffer_size > 0; } virtual bool can_write_impl() const override { return true; } diff --git a/kernel/include/kernel/FS/Socket.h b/kernel/include/kernel/FS/Socket.h index b1e1ea25..68604415 100644 --- a/kernel/include/kernel/FS/Socket.h +++ b/kernel/include/kernel/FS/Socket.h @@ -51,6 +51,7 @@ namespace Kernel BAN::ErrorOr read_impl(off_t, BAN::ByteSpan buffer) override { return recvfrom_impl(buffer, nullptr, nullptr); } BAN::ErrorOr write_impl(off_t, BAN::ConstByteSpan buffer) override { return sendto_impl(buffer, nullptr, 0); } + BAN::ErrorOr fsync_impl() final override { return {}; } private: const Info m_info; diff --git a/kernel/include/kernel/FS/TmpFS/Inode.h b/kernel/include/kernel/FS/TmpFS/Inode.h index 4c0340eb..4402c40d 100644 --- a/kernel/include/kernel/FS/TmpFS/Inode.h +++ b/kernel/include/kernel/FS/TmpFS/Inode.h @@ -45,6 +45,8 @@ namespace Kernel protected: TmpInode(TmpFileSystem&, ino_t, const TmpInodeInfo&); + virtual BAN::ErrorOr fsync_impl() override { return {}; } + void sync(); void free_all_blocks(); virtual BAN::ErrorOr prepare_unlink() { return {}; }; diff --git a/kernel/include/kernel/Process.h b/kernel/include/kernel/Process.h index 1014a64a..de0cf334 100644 --- a/kernel/include/kernel/Process.h +++ b/kernel/include/kernel/Process.h @@ -143,6 +143,8 @@ namespace Kernel BAN::ErrorOr sys_truncate(int fd, off_t length); + BAN::ErrorOr sys_fsync(int fd); + BAN::ErrorOr sys_fstatat(int fd, const char* path, struct stat* buf, int flag); BAN::ErrorOr sys_realpath(const char* path, char* buffer); diff --git a/kernel/include/kernel/Storage/DiskCache.h b/kernel/include/kernel/Storage/DiskCache.h index 24d4129f..0a8ffa0f 100644 --- a/kernel/include/kernel/Storage/DiskCache.h +++ b/kernel/include/kernel/Storage/DiskCache.h @@ -20,10 +20,14 @@ namespace Kernel BAN::ErrorOr write_to_cache(uint64_t sector, BAN::ConstByteSpan, bool dirty); BAN::ErrorOr sync(); + BAN::ErrorOr sync(uint64_t sector, size_t sector_count); size_t release_clean_pages(size_t); size_t release_pages(size_t); void release_all_pages(); + private: + BAN::ErrorOr sync_cache_index(size_t index); + private: struct PageCache { diff --git a/kernel/include/kernel/Storage/Partition.h b/kernel/include/kernel/Storage/Partition.h index a2ec014b..fef313f5 100644 --- a/kernel/include/kernel/Storage/Partition.h +++ b/kernel/include/kernel/Storage/Partition.h @@ -15,13 +15,14 @@ namespace Kernel const BAN::GUID& partition_guid() const { return m_guid; } const BAN::RefPtr device() const { return m_device; } - virtual blksize_t blksize() const { return m_device->blksize(); } + virtual blksize_t blksize() const override { return m_device->blksize(); } BAN::ErrorOr read_sectors(uint64_t first_block, size_t block_count, BAN::ByteSpan buffer) { return read_blocks(first_block, block_count, buffer); } BAN::ErrorOr write_sectors(uint64_t first_block, size_t block_count, BAN::ConstByteSpan buffer) { return write_blocks(first_block, block_count, buffer); } virtual BAN::ErrorOr read_blocks(uint64_t first_block, size_t block_count, BAN::ByteSpan) override; virtual BAN::ErrorOr write_blocks(uint64_t first_block, size_t block_count, BAN::ConstByteSpan) override; + virtual BAN::ErrorOr sync_blocks(uint64_t block, size_t block_count) override; virtual BAN::StringView name() const override { return m_name; } diff --git a/kernel/include/kernel/Storage/StorageDevice.h b/kernel/include/kernel/Storage/StorageDevice.h index 63166f97..3b909500 100644 --- a/kernel/include/kernel/Storage/StorageDevice.h +++ b/kernel/include/kernel/Storage/StorageDevice.h @@ -21,11 +21,12 @@ namespace Kernel virtual BAN::ErrorOr read_blocks(uint64_t lba, size_t sector_count, BAN::ByteSpan buffer) override { return read_sectors(lba, sector_count, buffer); } virtual BAN::ErrorOr write_blocks(uint64_t lba, size_t sector_count, BAN::ConstByteSpan buffer) override { return write_sectors(lba, sector_count, buffer); } + virtual BAN::ErrorOr sync_blocks(uint64_t block, size_t block_count) override; BAN::ErrorOr read_sectors(uint64_t lba, size_t sector_count, BAN::ByteSpan); BAN::ErrorOr write_sectors(uint64_t lba, size_t sector_count, BAN::ConstByteSpan); - virtual blksize_t blksize() const { return sector_size(); } + virtual blksize_t blksize() const override { return sector_size(); } virtual uint32_t sector_size() const = 0; virtual uint64_t total_size() const = 0; diff --git a/kernel/kernel/FS/Ext2/FileSystem.cpp b/kernel/kernel/FS/Ext2/FileSystem.cpp index 9c0324cc..f720a3e7 100644 --- a/kernel/kernel/FS/Ext2/FileSystem.cpp +++ b/kernel/kernel/FS/Ext2/FileSystem.cpp @@ -346,6 +346,17 @@ namespace Kernel return {}; } + BAN::ErrorOr Ext2FS::sync_block(uint32_t block) + { + LockGuard _(m_mutex); + + const uint32_t sector_size = m_block_device->blksize(); + const uint32_t block_size = this->block_size(); + const uint32_t sectors_per_block = block_size / sector_size; + + return m_block_device->sync_blocks(block * sectors_per_block, sectors_per_block); + } + Ext2FS::BlockBufferWrapper Ext2FS::get_block_buffer() { LockGuard _(m_mutex); diff --git a/kernel/kernel/FS/Ext2/Inode.cpp b/kernel/kernel/FS/Ext2/Inode.cpp index 909eb7c1..b449d64d 100644 --- a/kernel/kernel/FS/Ext2/Inode.cpp +++ b/kernel/kernel/FS/Ext2/Inode.cpp @@ -259,6 +259,14 @@ namespace Kernel return {}; } + BAN::ErrorOr Ext2Inode::fsync_impl() + { + for (size_t i = 0; i < max_used_data_block_count(); i++) + if (const auto fs_block = TRY(fs_block_of_data_block_index(i)); fs_block.has_value()) + TRY(m_fs.sync_block(fs_block.value())); + return {}; + } + BAN::ErrorOr Ext2Inode::cleanup_indirect_block(uint32_t block, uint32_t depth) { ASSERT(block); diff --git a/kernel/kernel/FS/Inode.cpp b/kernel/kernel/FS/Inode.cpp index 90ed074d..69d81afc 100644 --- a/kernel/kernel/FS/Inode.cpp +++ b/kernel/kernel/FS/Inode.cpp @@ -1,5 +1,6 @@ #include #include +#include #include @@ -203,6 +204,15 @@ namespace Kernel return chown_impl(uid, gid); } + BAN::ErrorOr Inode::fsync() + { + LockGuard _(m_mutex); + if (auto shared = m_shared_region.lock()) + for (size_t i = 0; i < shared->pages.size(); i++) + shared->sync(i); + return fsync_impl(); + } + bool Inode::can_read() const { LockGuard _(m_mutex); diff --git a/kernel/kernel/Process.cpp b/kernel/kernel/Process.cpp index ff5faa65..27527f0e 100644 --- a/kernel/kernel/Process.cpp +++ b/kernel/kernel/Process.cpp @@ -1448,6 +1448,14 @@ namespace Kernel return 0; } + BAN::ErrorOr Process::sys_fsync(int fd) + { + LockGuard _(m_process_lock); + auto inode = TRY(m_open_file_descriptors.inode_of(fd)); + TRY(inode->fsync()); + return 0; + } + BAN::ErrorOr Process::mount(BAN::StringView source, BAN::StringView target) { BAN::String absolute_source, absolute_target; diff --git a/kernel/kernel/Storage/DiskCache.cpp b/kernel/kernel/Storage/DiskCache.cpp index b958db83..df74b5a7 100644 --- a/kernel/kernel/Storage/DiskCache.cpp +++ b/kernel/kernel/Storage/DiskCache.cpp @@ -32,7 +32,7 @@ namespace Kernel for (auto& cache : m_cache) { - if (cache.first_sector < page_cache_start) + if (cache.first_sector + sectors_per_page <= page_cache_start) continue; if (cache.first_sector > page_cache_start) break; @@ -64,7 +64,7 @@ namespace Kernel { auto& cache = m_cache[index]; - if (cache.first_sector < page_cache_start) + if (cache.first_sector + sectors_per_page <= page_cache_start) continue; if (cache.first_sector > page_cache_start) break; @@ -104,47 +104,73 @@ namespace Kernel return {}; } - BAN::ErrorOr DiskCache::sync() + BAN::ErrorOr DiskCache::sync_cache_index(size_t index) { - if (g_disable_disk_write) + auto& cache = m_cache[index]; + if (cache.dirty_mask == 0) return {}; - for (auto& cache : m_cache) + PageTable::with_fast_page(cache.paddr, [&] { + memcpy(m_sync_cache.data(), PageTable::fast_page_as_ptr(), PAGE_SIZE); + }); + + uint8_t sector_start = 0; + uint8_t sector_count = 0; + + while (sector_start + sector_count <= PAGE_SIZE / m_sector_size) { - if (cache.dirty_mask == 0) - continue; - - PageTable::with_fast_page(cache.paddr, [&] { - memcpy(m_sync_cache.data(), PageTable::fast_page_as_ptr(), PAGE_SIZE); - }); - - uint8_t sector_start = 0; - uint8_t sector_count = 0; - - while (sector_start + sector_count <= PAGE_SIZE / m_sector_size) - { - if (cache.dirty_mask & (1 << (sector_start + sector_count))) - sector_count++; - else if (sector_count == 0) - sector_start++; - else - { - dprintln_if(DEBUG_DISK_SYNC, "syncing {}->{}", cache.first_sector + sector_start, cache.first_sector + sector_start + sector_count); - auto data_slice = m_sync_cache.span().slice(sector_start * m_sector_size, sector_count * m_sector_size); - TRY(m_device.write_sectors_impl(cache.first_sector + sector_start, sector_count, data_slice)); - sector_start += sector_count + 1; - sector_count = 0; - } - } - - if (sector_count > 0) + if (cache.dirty_mask & (1 << (sector_start + sector_count))) + sector_count++; + else if (sector_count == 0) + sector_start++; + else { dprintln_if(DEBUG_DISK_SYNC, "syncing {}->{}", cache.first_sector + sector_start, cache.first_sector + sector_start + sector_count); auto data_slice = m_sync_cache.span().slice(sector_start * m_sector_size, sector_count * m_sector_size); TRY(m_device.write_sectors_impl(cache.first_sector + sector_start, sector_count, data_slice)); + sector_start += sector_count + 1; + sector_count = 0; } + } - cache.dirty_mask = 0; + if (sector_count > 0) + { + dprintln_if(DEBUG_DISK_SYNC, "syncing {}->{}", cache.first_sector + sector_start, cache.first_sector + sector_start + sector_count); + auto data_slice = m_sync_cache.span().slice(sector_start * m_sector_size, sector_count * m_sector_size); + TRY(m_device.write_sectors_impl(cache.first_sector + sector_start, sector_count, data_slice)); + } + + cache.dirty_mask = 0; + + return {}; + } + + BAN::ErrorOr DiskCache::sync() + { + if (g_disable_disk_write) + return {}; + for (size_t i = 0; i < m_cache.size(); i++) + TRY(sync_cache_index(i)); + return {}; + } + + BAN::ErrorOr DiskCache::sync(uint64_t sector, size_t block_count) + { + if (g_disable_disk_write) + return {}; + + uint64_t sectors_per_page = PAGE_SIZE / m_sector_size; + uint64_t page_cache_offset = sector % sectors_per_page; + uint64_t page_cache_start = sector - page_cache_offset; + + for (size_t i = 0; i < m_cache.size(); i++) + { + auto& cache = m_cache[i]; + if (cache.first_sector + sectors_per_page <= page_cache_start) + continue; + if (cache.first_sector * sectors_per_page >= page_cache_start * sectors_per_page + block_count) + break; + TRY(sync_cache_index(i)); } return {}; diff --git a/kernel/kernel/Storage/Partition.cpp b/kernel/kernel/Storage/Partition.cpp index e74104fc..4cf8342e 100644 --- a/kernel/kernel/Storage/Partition.cpp +++ b/kernel/kernel/Storage/Partition.cpp @@ -48,6 +48,15 @@ namespace Kernel return {}; } + BAN::ErrorOr Partition::sync_blocks(uint64_t block, size_t block_count) + { + const uint32_t blocks_in_partition = m_last_block - m_first_block + 1; + if (block + block_count > blocks_in_partition) + return BAN::Error::from_error_code(ErrorCode::Storage_Boundaries); + TRY(m_device->sync_blocks(m_first_block + block, block_count)); + return {}; + } + BAN::ErrorOr Partition::read_impl(off_t offset, BAN::ByteSpan buffer) { ASSERT(offset >= 0); diff --git a/kernel/kernel/Storage/StorageDevice.cpp b/kernel/kernel/Storage/StorageDevice.cpp index 2cd7eded..d7da86e2 100644 --- a/kernel/kernel/Storage/StorageDevice.cpp +++ b/kernel/kernel/Storage/StorageDevice.cpp @@ -286,6 +286,13 @@ namespace Kernel return {}; } + BAN::ErrorOr StorageDevice::sync_blocks(uint64_t block, size_t block_count) + { + if (!m_disk_cache.has_value()) + return {}; + return m_disk_cache->sync(block, block_count); + } + BAN::ErrorOr StorageDevice::read_impl(off_t offset, BAN::ByteSpan buffer) { if (offset % sector_size()) diff --git a/userspace/libraries/LibC/include/sys/syscall.h b/userspace/libraries/LibC/include/sys/syscall.h index bc5d9b27..29f865bc 100644 --- a/userspace/libraries/LibC/include/sys/syscall.h +++ b/userspace/libraries/LibC/include/sys/syscall.h @@ -85,6 +85,7 @@ __BEGIN_DECLS O(SYS_SETITIMER, setitimer) \ O(SYS_POSIX_OPENPT, posix_openpt) \ O(SYS_PTSNAME, ptsname) \ + O(SYS_FSYNC, fsync) \ enum Syscall { diff --git a/userspace/libraries/LibC/unistd.cpp b/userspace/libraries/LibC/unistd.cpp index f12f5332..efc577df 100644 --- a/userspace/libraries/LibC/unistd.cpp +++ b/userspace/libraries/LibC/unistd.cpp @@ -115,6 +115,11 @@ int ftruncate(int fildes, off_t length) return syscall(SYS_TRUNCATE, fildes, length); } +int fsync(int fildes) +{ + return syscall(SYS_FSYNC, fildes); +} + int dup(int fildes) { return syscall(SYS_DUP, fildes);