aboutsummaryrefslogtreecommitdiff
path: root/zencore/filesystem.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'zencore/filesystem.cpp')
-rw-r--r--zencore/filesystem.cpp202
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