aboutsummaryrefslogtreecommitdiff
path: root/src/zencore/filesystem.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/zencore/filesystem.cpp')
-rw-r--r--src/zencore/filesystem.cpp201
1 files changed, 188 insertions, 13 deletions
diff --git a/src/zencore/filesystem.cpp b/src/zencore/filesystem.cpp
index b8c35212f..05e2bf049 100644
--- a/src/zencore/filesystem.cpp
+++ b/src/zencore/filesystem.cpp
@@ -531,7 +531,10 @@ CloneFile(std::filesystem::path FromPath, std::filesystem::path ToPath)
}
void
-CopyFile(std::filesystem::path FromPath, std::filesystem::path ToPath, const CopyFileOptions& Options, std::error_code& OutErrorCode)
+CopyFile(const std::filesystem::path& FromPath,
+ const std::filesystem::path& ToPath,
+ const CopyFileOptions& Options,
+ std::error_code& OutErrorCode)
{
OutErrorCode.clear();
@@ -544,7 +547,7 @@ CopyFile(std::filesystem::path FromPath, std::filesystem::path ToPath, const Cop
}
bool
-CopyFile(std::filesystem::path FromPath, std::filesystem::path ToPath, const CopyFileOptions& Options)
+CopyFile(const std::filesystem::path& FromPath, const std::filesystem::path& ToPath, const CopyFileOptions& Options)
{
bool Success = false;
@@ -587,7 +590,7 @@ CopyFile(std::filesystem::path FromPath, std::filesystem::path ToPath, const Cop
ScopedFd $From = {FromFd};
// To file
- int ToFd = open(ToPath.c_str(), O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC, 0666);
+ int ToFd = open(ToPath.c_str(), O_WRONLY | O_CREAT | O_CLOEXEC, 0666);
if (ToFd < 0)
{
ThrowLastError(fmt::format("failed to create file {}", ToPath));
@@ -595,9 +598,14 @@ CopyFile(std::filesystem::path FromPath, std::filesystem::path ToPath, const Cop
fchmod(ToFd, 0666);
ScopedFd $To = {ToFd};
+ struct stat Stat;
+ fstat(FromFd, &Stat);
+
+ size_t FileSizeBytes = Stat.st_size;
+
// Copy impl
- static const size_t BufferSize = 64 << 10;
- void* Buffer = malloc(BufferSize);
+ const size_t BufferSize = Min(FileSizeBytes, 64u << 10);
+ void* Buffer = malloc(BufferSize);
while (true)
{
int BytesRead = read(FromFd, Buffer, BufferSize);
@@ -607,7 +615,7 @@ CopyFile(std::filesystem::path FromPath, std::filesystem::path ToPath, const Cop
break;
}
- if (write(ToFd, Buffer, BytesRead) != BufferSize)
+ if (write(ToFd, Buffer, BytesRead) != BytesRead)
{
Success = false;
break;
@@ -618,7 +626,7 @@ CopyFile(std::filesystem::path FromPath, std::filesystem::path ToPath, const Cop
if (!Success)
{
- ThrowLastError("file copy failed"sv);
+ ThrowLastError(fmt::format("file copy from {} to {} failed", FromPath, ToPath));
}
return true;
@@ -683,7 +691,7 @@ CopyTree(std::filesystem::path FromPath, std::filesystem::path ToPath, const Cop
{
}
- virtual void VisitFile(const std::filesystem::path& Parent, const path_view& File, uint64_t FileSize, uint32_t) override
+ virtual void VisitFile(const std::filesystem::path& Parent, const path_view& File, uint64_t FileSize, uint32_t, uint64_t) override
{
std::error_code Ec;
const std::filesystem::path Relative = std::filesystem::relative(Parent, BasePath, Ec);
@@ -1236,7 +1244,11 @@ FileSystemTraversal::TraverseFileSystem(const std::filesystem::path& RootDir, Tr
}
else
{
- Visitor.VisitFile(RootDir, FileName, DirInfo->EndOfFile.QuadPart, gsl::narrow<uint32_t>(DirInfo->FileAttributes));
+ Visitor.VisitFile(RootDir,
+ FileName,
+ DirInfo->EndOfFile.QuadPart,
+ gsl::narrow<uint32_t>(DirInfo->FileAttributes),
+ (uint64_t)DirInfo->LastWriteTime.QuadPart);
}
const uint64_t NextOffset = DirInfo->NextEntryOffset;
@@ -1285,7 +1297,7 @@ FileSystemTraversal::TraverseFileSystem(const std::filesystem::path& RootDir, Tr
}
else if (S_ISREG(Stat.st_mode))
{
- Visitor.VisitFile(RootDir, FileName, Stat.st_size, gsl::narrow<uint32_t>(Stat.st_mode));
+ Visitor.VisitFile(RootDir, FileName, Stat.st_size, gsl::narrow<uint32_t>(Stat.st_mode), gsl::narrow<uint64_t>(Stat.st_mtime));
}
else
{
@@ -1465,6 +1477,36 @@ GetModificationTickFromHandle(void* NativeHandle, std::error_code& Ec)
return 0;
}
+uint64_t
+GetModificationTickFromPath(const std::filesystem::path& Filename)
+{
+ // PathFromHandle
+ void* Handle;
+#if ZEN_PLATFORM_WINDOWS
+ Handle = CreateFileW(Filename.c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, 0, nullptr);
+ if (Handle == INVALID_HANDLE_VALUE)
+ {
+ ThrowLastError(fmt::format("Failed to open file {} to check modification tick.", Filename));
+ }
+ auto _ = MakeGuard([Handle]() { CloseHandle(Handle); });
+ std::error_code Ec;
+ uint64_t ModificatonTick = GetModificationTickFromHandle(Handle, Ec);
+ if (Ec)
+ {
+ ThrowSystemError(Ec.value(), Ec.message());
+ }
+ return ModificatonTick;
+#else
+ struct stat Stat;
+ int err = stat(Filename.native().c_str(), &Stat);
+ if (err)
+ {
+ ThrowLastError(fmt::format("Failed to get mode of file {}", Filename));
+ }
+ return gsl::narrow<uint64_t>(Stat.st_mtime);
+#endif
+}
+
std::filesystem::path
GetRunningExecutablePath()
{
@@ -1544,7 +1586,8 @@ GetDirectoryContent(const std::filesystem::path& RootDir, DirectoryContentFlags
virtual void VisitFile(const std::filesystem::path& Parent,
const path_view& File,
uint64_t FileSize,
- uint32_t NativeModeOrAttributes) override
+ uint32_t NativeModeOrAttributes,
+ uint64_t NativeModificationTick) override
{
if (EnumHasAnyFlags(Flags, DirectoryContentFlags::IncludeFiles))
{
@@ -1557,6 +1600,10 @@ GetDirectoryContent(const std::filesystem::path& RootDir, DirectoryContentFlags
{
Content.FileAttributes.push_back(NativeModeOrAttributes);
}
+ if (EnumHasAnyFlags(Flags, DirectoryContentFlags::IncludeModificationTick))
+ {
+ Content.FileModificationTicks.push_back(NativeModificationTick);
+ }
}
}
@@ -1612,7 +1659,8 @@ GetDirectoryContent(const std::filesystem::path& RootDir,
virtual void VisitFile(const std::filesystem::path&,
const path_view& File,
uint64_t FileSize,
- uint32_t NativeModeOrAttributes) override
+ uint32_t NativeModeOrAttributes,
+ uint64_t NativeModificationTick) override
{
if (EnumHasAnyFlags(Flags, DirectoryContentFlags::IncludeFiles))
{
@@ -1625,6 +1673,10 @@ GetDirectoryContent(const std::filesystem::path& RootDir,
{
Content.FileAttributes.push_back(NativeModeOrAttributes);
}
+ if (EnumHasAnyFlags(Flags, DirectoryContentFlags::IncludeModificationTick))
+ {
+ Content.FileModificationTicks.push_back(NativeModificationTick);
+ }
}
}
@@ -1881,6 +1933,129 @@ PickDefaultSystemRootDirectory()
#endif // ZEN_PLATFORM_WINDOWS
}
+#if ZEN_PLATFORM_WINDOWS
+
+uint32_t
+GetFileAttributes(const std::filesystem::path& Filename)
+{
+ DWORD Attributes = ::GetFileAttributes(Filename.native().c_str());
+ if (Attributes == INVALID_FILE_ATTRIBUTES)
+ {
+ ThrowLastError(fmt::format("failed to get attributes of file {}", Filename));
+ }
+ return (uint32_t)Attributes;
+}
+
+void
+SetFileAttributes(const std::filesystem::path& Filename, uint32_t Attributes)
+{
+ if (::SetFileAttributes(Filename.native().c_str(), Attributes) == 0)
+ {
+ ThrowLastError(fmt::format("failed to set attributes of file {}", Filename));
+ }
+}
+
+#endif // ZEN_PLATFORM_WINDOWS
+
+#if ZEN_PLATFORM_LINUX || ZEN_PLATFORM_MAC
+
+uint32_t
+GetFileMode(const std::filesystem::path& Filename)
+{
+ struct stat Stat;
+ int err = stat(Filename.native().c_str(), &Stat);
+ if (err)
+ {
+ ThrowLastError(fmt::format("Failed to get mode of file {}", Filename));
+ }
+ return (uint32_t)Stat.st_mode;
+}
+
+void
+SetFileMode(const std::filesystem::path& Filename, uint32_t Attributes)
+{
+ int err = chmod(Filename.native().c_str(), (mode_t)Attributes);
+ if (err)
+ {
+ ThrowLastError(fmt::format("Failed to set mode of file {}", Filename));
+ }
+}
+
+#endif // ZEN_PLATFORM_LINUX || ZEN_PLATFORM_MAC
+
+#if ZEN_PLATFORM_WINDOWS
+const uint32_t FileAttributesSystemReadOnlyFlag = FILE_ATTRIBUTE_READONLY;
+#else
+const uint32_t FileAttributesSystemReadOnlyFlag = 0x00000001;
+#endif // ZEN_PLATFORM_WINDOWS
+
+const uint32_t FileModeWriteEnableFlags = 0222;
+
+bool
+IsFileAttributeReadOnly(uint32_t FileAttributes)
+{
+#if ZEN_PLATFORM_WINDOWS
+ return (FileAttributes & FileAttributesSystemReadOnlyFlag) != 0;
+#else
+ return (FileAttributes & 0x00000001) != 0;
+#endif // ZEN_PLATFORM_WINDOWS
+}
+
+bool
+IsFileModeReadOnly(uint32_t FileMode)
+{
+ return (FileMode & FileModeWriteEnableFlags) == 0;
+}
+
+uint32_t
+MakeFileAttributeReadOnly(uint32_t FileAttributes, bool ReadOnly)
+{
+ return ReadOnly ? (FileAttributes | FileAttributesSystemReadOnlyFlag) : (FileAttributes & ~FileAttributesSystemReadOnlyFlag);
+}
+
+uint32_t
+MakeFileModeReadOnly(uint32_t FileMode, bool ReadOnly)
+{
+ return ReadOnly ? (FileMode & ~FileModeWriteEnableFlags) : (FileMode | FileModeWriteEnableFlags);
+}
+
+bool
+SetFileReadOnly(const std::filesystem::path& Filename, bool ReadOnly)
+{
+#if ZEN_PLATFORM_WINDOWS
+ uint32_t CurrentAttributes = GetFileAttributes(Filename);
+ uint32_t NewAttributes = MakeFileAttributeReadOnly(CurrentAttributes, ReadOnly);
+ if (CurrentAttributes != NewAttributes)
+ {
+ SetFileAttributes(Filename, NewAttributes);
+ return true;
+ }
+#endif // ZEN_PLATFORM_WINDOWS
+#if ZEN_PLATFORM_LINUX || ZEN_PLATFORM_MAC
+ uint32_t CurrentMode = GetFileMode(Filename);
+ uint32_t NewMode = MakeFileModeReadOnly(CurrentMode, ReadOnly);
+ if (CurrentMode != NewMode)
+ {
+ SetFileMode(Filename, NewMode);
+ return true;
+ }
+#endif // ZEN_PLATFORM_LINUX || ZEN_PLATFORM_MAC
+ return false;
+}
+
+std::filesystem::path
+StringToPath(const std::string_view& Path)
+{
+ if (Path.length() > 2 && Path.front() == '\"' && Path.back() == '\"')
+ {
+ return std::filesystem::path(Path.substr(1, Path.length() - 2)).make_preferred();
+ }
+ else
+ {
+ return std::filesystem::path(Path).make_preferred();
+ }
+}
+
//////////////////////////////////////////////////////////////////////////
//
// Testing related code follows...
@@ -1928,7 +2103,7 @@ TEST_CASE("filesystem")
// Traversal
struct : public FileSystemTraversal::TreeVisitor
{
- virtual void VisitFile(const std::filesystem::path& Parent, const path_view& File, uint64_t, uint32_t) override
+ virtual void VisitFile(const std::filesystem::path& Parent, const path_view& File, uint64_t, uint32_t, uint64_t) override
{
bFoundExpected |= std::filesystem::equivalent(Parent / File, Expected);
}