diff options
Diffstat (limited to 'zencore/filesystem.cpp')
| -rw-r--r-- | zencore/filesystem.cpp | 202 |
1 files changed, 176 insertions, 26 deletions
diff --git a/zencore/filesystem.cpp b/zencore/filesystem.cpp index a642e2cf6..3c5ab2026 100644 --- a/zencore/filesystem.cpp +++ b/zencore/filesystem.cpp @@ -174,7 +174,8 @@ CleanDirectory(const wchar_t* DirPath) bool CreateDirectories(const std::filesystem::path& Dir) { - return std::filesystem::create_directories(Dir); + std::error_code ErrorCode; + return std::filesystem::create_directories(Dir, ErrorCode); } bool @@ -244,6 +245,7 @@ SupportsBlockRefCounting(std::filesystem::path Path) return true; #else + ZEN_UNUSED(Path); return false; #endif // ZEN_PLATFORM_WINDOWS } @@ -406,16 +408,47 @@ CloneFile(std::filesystem::path FromPath, std::filesystem::path ToPath) const bool AllOk = (TRUE == SetFileInformationByHandle(TargetFile, FileDispositionInfo, &FileDisposition, sizeof FileDisposition)); return AllOk; -#else +#elif ZEN_PLATFORM_LINUX +# if 0 + struct ScopedFd + { + ~ScopedFd() { close(Fd); } + int Fd; + }; + + // The 'from' file + int FromFd = open(FromPath.c_str(), O_RDONLY); + if (FromFd < 0) + { + return false; + } + ScopedFd $From = { FromFd }; + + // The 'to' file + int ToFd = open(ToPath.c_str(), O_WRONLY|O_CREAT|O_EXCL, 0666); + if (ToFd < 0) + { + return false; + } + ScopedFd $To = { FromFd }; + + ioctl(ToFd, FICLONE, FromFd); + + return false; +# endif // 0 + ZEN_UNUSED(FromPath, ToPath); ZEN_ERROR("CloneFile() is not implemented on this platform"); return false; +#elif ZEN_PLATFORM_MAC + /* clonefile() syscall if APFS */ +# error not implemented + return false; #endif // ZEN_PLATFORM_WINDOWS } bool CopyFile(std::filesystem::path FromPath, std::filesystem::path ToPath, const CopyFileOptions& Options) { -#if ZEN_PLATFORM_WINDOWS bool Success = false; if (Options.EnableClone) @@ -433,6 +466,7 @@ CopyFile(std::filesystem::path FromPath, std::filesystem::path ToPath, const Cop return false; } +#if ZEN_PLATFORM_WINDOWS BOOL CancelFlag = FALSE; Success = !!::CopyFileExW(FromPath.c_str(), ToPath.c_str(), @@ -440,17 +474,54 @@ CopyFile(std::filesystem::path FromPath, std::filesystem::path ToPath, const Cop /* lpData */ nullptr, &CancelFlag, /* dwCopyFlags */ 0); +#else + using namespace fmt::literals; - if (!Success) + struct ScopedFd { ~ScopedFd() { close(Fd); } int Fd; }; + + // From file + int FromFd = open(FromPath.c_str(), O_RDONLY); + if (FromFd < 0) { - throw std::system_error(std::error_code(::GetLastError(), std::system_category()), "file copy failed"); + ThrowLastError("failed to open file {}"_format(FromPath)); } + ScopedFd $From = { FromFd }; - return Success; -#else - ZEN_ERROR("CopyFile() is not implemented on this platform"); - return false; + // To file + int ToFd = open(ToPath.c_str(), O_WRONLY|O_CREAT|O_EXCL); + if (ToFd < 0) + { + ThrowLastError("failed to create file {}"_format(ToPath)); + } + ScopedFd $To = { ToFd }; + + // Copy impl + static const size_t BufferSize = 64 << 10; + void* Buffer = malloc(BufferSize); + while (true) + { + int BytesRead = read(FromFd, Buffer, BufferSize); + if (BytesRead <= 0) + { + Success = (BytesRead == 0); + break; + } + + if (write(ToFd, Buffer, BytesRead) != BufferSize) + { + Success = false; + break; + } + } + free(Buffer); #endif // ZEN_PLATFORM_WINDOWS + + if (!Success) + { + ThrowLastError("file copy failed"sv); + } + + return true; } void @@ -505,7 +576,7 @@ WriteFile(std::filesystem::path Path, const IoBuffer* const* Data, size_t Buffer ThrowSystemException(hRes, "File write failed for '{}'"_format(Path).c_str()); } #else - if (write(Fd, DataPtr, WriteSize) != WriteSize) + if (write(Fd, DataPtr, WriteSize) != int64_t(WriteSize)) { ThrowLastError("File write failed for '{}'"_format(Path)); } @@ -572,7 +643,9 @@ ReadFile(std::filesystem::path Path) int Fd = open(Path.c_str(), O_RDONLY); if (Fd < 0) { - return FileContents{.ErrorCode = std::error_code(zen::GetLastError(), std::system_category())}; + FileContents Ret; + Ret.ErrorCode = std::error_code(zen::GetLastError(), std::system_category()); + return Ret; } static_assert(sizeof(decltype(stat::st_size)) == sizeof(uint64_t), "fstat() doesn't support large files"); @@ -616,12 +689,43 @@ ScanFile(std::filesystem::path Path, const uint64_t ChunkSize, std::function<voi ProcessFunc(ReadBuffer.data(), dwBytesRead); } - - return true; #else - ZEN_ERROR("ScanFile() is not implemented on this platform"); - return false; + int Fd = open(Path.c_str(), O_RDONLY); + if (Fd < 0) + { + return false; + } + + bool Success = true; + + void* Buffer = malloc(ChunkSize); + while (true) + { + int BytesRead = read(Fd, Buffer, ChunkSize); + if (BytesRead < 0) + { + Success = false; + break; + } + + if (BytesRead == 0) + { + break; + } + + ProcessFunc(Buffer, BytesRead); + } + + free(Buffer); + close(Fd); + + if (!Success) + { + ThrowLastError("file scan failed"); + } #endif // ZEN_PLATFORM_WINDOWS + + return true; } std::string @@ -731,7 +835,7 @@ FileSystemTraversal::TraverseFileSystem(const std::filesystem::path& RootDir, Tr ThrowLastError("Failed to open directory for traversal: {}"_format(RootDir.c_str())); } - for (struct dirent* Entry; Entry = readdir(Dir);) + for (struct dirent* Entry; (Entry = readdir(Dir));) { const char* FileName = Entry->d_name; @@ -787,14 +891,15 @@ PathFromHandle(void* NativeHandle) return FullPath; #elif ZEN_PLATFORM_LINUX - char Buffer[256]; - sprintf(Buffer, "/proc/%d/fd/%d", getpid(), int(uintptr_t(NativeHandle))); - ssize_t BytesRead = readlink(Buffer, Buffer, sizeof(Buffer) - 1); + char Link[256]; + char Path[64]; + sprintf(Path, "/proc/%d/fd/%d", getpid(), int(uintptr_t(NativeHandle))); + ssize_t BytesRead = readlink(Path, Link, sizeof(Link) - 1); if (BytesRead <= 0) return std::filesystem::path(); - Buffer[BytesRead] = '\0'; - return Buffer; + Link[BytesRead] = '\0'; + return Link; #else # error Unimplemented platform #endif // ZEN_PLATFORM_WINDOWS @@ -809,14 +914,15 @@ GetRunningExecutablePath() return {std::wstring_view(ExePath, PathLength)}; #elif ZEN_PLATFORM_LINUX - char Buffer[256]; - sprintf(Buffer, "/proc/%d/exe", getpid()); - ssize_t BytesRead = readlink(Buffer, Buffer, sizeof(Buffer) - 1); + char Link[256]; + char Path[64]; + sprintf(Path, "/proc/%d/exe", getpid()); + ssize_t BytesRead = readlink(Path, Link, sizeof(Link) - 1); if (BytesRead < 0) return {}; - Buffer[BytesRead] = '\0'; - return Buffer; + Link[BytesRead] = '\0'; + return Link; #else # error Unimplemented platform #endif // ZEN_PLATFORM_WINDOWS @@ -881,6 +987,50 @@ TEST_CASE("filesystem") FileSystemTraversal().TraverseFileSystem(BinPath.parent_path().parent_path(), Visitor); CHECK(Visitor.bFoundExpected); + + // Scan/read file + FileContents BinRead = ReadFile(BinPath); + std::vector<uint8_t> BinScan; + ScanFile(BinPath, 16 << 10, [&] (const void* Data, size_t Size) { + const auto* Ptr = (uint8_t*)Data; + BinScan.insert(BinScan.end(), Ptr, Ptr + Size); + }); + CHECK_EQ(BinRead.Data.size(), 1); + CHECK_EQ(BinScan.size(), BinRead.Data[0].GetSize()); +} + +TEST_CASE("PathBuilder") +{ +#if ZEN_PLATFORM_WINDOWS + const char* foo_bar = "/foo\\bar"; +#else + const char* foo_bar = "/foo/bar"; +#endif + + ExtendablePathBuilder<32> Path; + for (const char* Prefix : { "/foo", "/foo/" }) + { + Path.Reset(); + Path.Append(Prefix); + Path /= "bar"; + CHECK(Path.ToPath() == foo_bar); + } + + using fspath = std::filesystem::path; + + Path.Reset(); + Path.Append(fspath("/foo/")); + Path /= (fspath("bar")); + CHECK(Path.ToPath() == foo_bar); + +#if ZEN_PLATFORM_WINDOWS + Path.Reset(); + Path.Append(fspath(L"/\u0119oo/")); + Path /= L"bar"; + printf("%ls\n", Path.ToPath().c_str()); + CHECK(Path.ToView() == L"/\u0119oo/bar"); + CHECK(Path.ToPath() == L"\\\u0119oo\\bar"); +# endif } #endif |