diff --git a/src/common/platform/sysLinuxFileIO.cpp b/src/common/platform/sysLinuxFileIO.cpp index 5fe914647..08f6a7289 100644 --- a/src/common/platform/sysLinuxFileIO.cpp +++ b/src/common/platform/sysLinuxFileIO.cpp @@ -367,7 +367,7 @@ bool SysFileDeleteFile(const std::filesystem::path& name) { bool SysFileFlush(sys_file_t& f) { if (f.type == SYS_FILE_FILE && f.f != nullptr) { - return (fflush(f.f) == 0); + return fflush(f.f) == 0 && fsync(fileno(f.f)) == 0; } return false; diff --git a/src/kernel/fileSystem.cpp b/src/kernel/fileSystem.cpp index 4e4d7567b..ba2ae845d 100644 --- a/src/kernel/fileSystem.cpp +++ b/src/kernel/fileSystem.cpp @@ -1326,6 +1326,27 @@ int KYTY_SYSV_ABI KernelFtruncate(int d, int64_t length) { return OK; } +int KYTY_SYSV_ABI KernelFsync(int d) { + PRINT_NAME(); + + if (d < DESCRIPTOR_MIN) { + return KERNEL_ERROR_EBADF; + } + + auto* file = g_files->GetFile(d); + if (file == nullptr || !file->opened) { + return KERNEL_ERROR_EBADF; + } + + Common::LockGuard lock(file->mutex); + if (file->f.IsInvalid() || !file->f.Flush()) { + return KERNEL_ERROR_EIO; + } + + LOGF("\tFsync: %s\n", Common::PathToString(file->real_name).c_str()); + return OK; +} + int KYTY_SYSV_ABI KernelUnlink(const char* path) { PRINT_NAME(); diff --git a/src/kernel/fileSystem.h b/src/kernel/fileSystem.h index 351669840..6a76008ad 100644 --- a/src/kernel/fileSystem.h +++ b/src/kernel/fileSystem.h @@ -72,6 +72,7 @@ int KYTY_SYSV_ABI KernelStat(const char* path, FileStat* sb); int KYTY_SYSV_ABI KernelFstat(int d, FileStat* sb); int KYTY_SYSV_ABI KernelTruncate(const char* path, int64_t length); int KYTY_SYSV_ABI KernelFtruncate(int d, int64_t length); +int KYTY_SYSV_ABI KernelFsync(int d); int KYTY_SYSV_ABI KernelUnlink(const char* path); int KYTY_SYSV_ABI KernelRename(const char* from, const char* to); int KYTY_SYSV_ABI KernelGetdirentries(int fd, char* buf, int nbytes, int64_t* basep); diff --git a/src/libs/libKernel.cpp b/src/libs/libKernel.cpp index ac012ba1e..c6fb3d76c 100644 --- a/src/libs/libKernel.cpp +++ b/src/libs/libKernel.cpp @@ -1341,14 +1341,6 @@ static int KYTY_SYSV_ABI KernelGetOperationMode(int* mode, int* submode) { return OK; } -static int KYTY_SYSV_ABI KernelFsync(int fd) { - PRINT_NAME(); - - LOGF("\t fd = %d\n", fd); - - return OK; -} - static void KYTY_SYSV_ABI KernelSync() { PRINT_NAME(); } @@ -1986,6 +1978,12 @@ int KYTY_SYSV_ABI ftruncate(int d, int64_t length) { return POSIX_CALL(LibKernel::FileSystem::KernelFtruncate(d, length)); } +int KYTY_SYSV_ABI fsync(int d) { + PRINT_NAME(); + + return POSIX_CALL(LibKernel::FileSystem::KernelFsync(d)); +} + int KYTY_SYSV_ABI socket(int family, int type, int protocol) { PRINT_NAME(); return Network::Net::Socket(family, type, protocol); @@ -2174,6 +2172,7 @@ LIB_DEFINE(InitLibKernel_1_Posix) { LIB_FUNC("E6ao34wPw+U", stat); LIB_FUNC("JGMio+21L4c", mkdir); LIB_FUNC("ih4CD9-gghM", Posix::ftruncate); + LIB_FUNC("juWbTNM+8hw", Posix::fsync); LIB_FUNC("8nY19bKoiZk", Posix::fcntl); LIB_FUNC("pDuPEf3m4fI", Posix::sem_init); LIB_FUNC("cDW233RAwWo", Posix::sem_destroy); @@ -3394,7 +3393,7 @@ LIB_DEFINE(InitLibKernel_1) { LIB_FUNC("959qrazPIrg", LibKernel::KernelGetProcParam); LIB_FUNC("tU5e3f9gSiU", LibKernel::KernelIsTrinityMode); LIB_FUNC("NH6xARDOVv8", LibKernel::KernelGetOperationMode); - LIB_FUNC("fTx66l5iWIA", LibKernel::KernelFsync); + LIB_FUNC("fTx66l5iWIA", FileSystem::KernelFsync); LIB_FUNC("uvT2iYBBnkY", LibKernel::KernelSync); LIB_FUNC("HoLVWNanBBc", LibKernel::getpid); LIB_FUNC("9BcDykPmo1I", LibKernel::get_error_addr); diff --git a/tests/KernelFileSystemTests.cpp b/tests/KernelFileSystemTests.cpp index 4a417f6ef..035d297ca 100644 --- a/tests/KernelFileSystemTests.cpp +++ b/tests/KernelFileSystemTests.cpp @@ -483,6 +483,27 @@ void TestSaveOpenVisibility() { Check(FileSystem::KernelClose(truncated) == OK, "close truncated save file"); } +void TestFsync() { + Loader::SymbolDatabase symbols; + Libs::InitLibKernel_1(&symbols); + const auto* symbol = symbols.Find( + {"juWbTNM+8hw", "Posix", 1, "libkernel", 1, 1, Loader::SymbolType::Func}); + Check(symbol != nullptr, "POSIX fsync export resolves"); + const auto fsync = reinterpret_cast(symbol->vaddr); + Check(fsync(std::numeric_limits::min()) == -1 && + *Libs::Posix::GetErrorAddr() == Libs::Posix::POSIX_EBADF, + "invalid fsync descriptor reports POSIX EBADF"); + + constexpr char Path[] = "/savedata0/fsync.dat"; + const int fd = FileSystem::KernelOpen(Path, 0x602, 0777); + Check(fd >= 3 && FileSystem::KernelWrite(fd, "save", 4) == 4 && fsync(fd) == OK && + FileSystem::KernelLseek(fd, 0, 1) == 4, + "fsync flushes a writable file without changing its offset"); + Check(FileSystem::KernelClose(fd) == OK && fsync(fd) == -1 && + *Libs::Posix::GetErrorAddr() == Libs::Posix::POSIX_EBADF, + "closed fsync descriptor reports POSIX EBADF"); +} + void TestSaveTruncate() { namespace Kernel = Libs::LibKernel; Loader::SymbolDatabase symbols; @@ -973,6 +994,9 @@ void CheckArchiveMount(const std::filesystem::path &root) { std::equal(bytes.begin(), bytes.begin() + 9, payload.end() - 9) && FileSystem::KernelRead(fd, bytes.data(), bytes.size()) == 0, "archive reads stop at the member boundary"); + Check(FileSystem::KernelFsync(fd) == OK && + FileSystem::KernelLseek(fd, 0, 1) == payload.size(), + "fsync succeeds on an immutable archive member without changing its offset"); Check(FileSystem::KernelWrite(fd, bytes.data(), 1) == Libs::LibKernel::KERNEL_ERROR_EBADF && FileSystem::KernelPwrite(fd, bytes.data(), 1, 0) == @@ -2271,6 +2295,7 @@ int main(int, char**) { CheckAprPaths(temporary.Path()); FileSystem::Mount(temporary.Path(), "/savedata0"); TestSaveOpenVisibility(); + TestFsync(); TestSaveTruncate(); TestSaveWritev(); TestAioBatches();