aboutsummaryrefslogtreecommitdiff
path: root/src/zen/cmds/copy_cmd.cpp
diff options
context:
space:
mode:
authorDan Engelbrecht <[email protected]>2023-09-22 08:22:06 -0400
committerGitHub <[email protected]>2023-09-22 14:22:06 +0200
commitc7d4dc6a4d13881028d566f5ce501335e47e48bf (patch)
tree493110da583a8e5d97fe05e14f23469ee6244d2b /src/zen/cmds/copy_cmd.cpp
parentadd trace command to enable/disable tracing at runtime (#416) (diff)
downloadarchived-zen-c7d4dc6a4d13881028d566f5ce501335e47e48bf.tar.xz
archived-zen-c7d4dc6a4d13881028d566f5ce501335e47e48bf.zip
Collect all zen admin-related commands into admin.h/.cpp (#418)
* move commands in scrub.h/cpp to admin_cmd.h/cpp * move job command into admin_cmd.h/.cpp * admin -> admin_cmd * bench -> bench_cmd * cache -> cache_cmd * copy -> copy_cmd * dedup -> dedup_cmd * hash -> hash_cmd * print -> print_cmd * projectstore -> projectstore_cmd * rpcreplay -> rpcreplay_cmd * serve -> serve_cmd * status -> status_cmd * top -> top_cmd * trace -> trace_cmd * up -> up_cmd * version -> version_cmd
Diffstat (limited to 'src/zen/cmds/copy_cmd.cpp')
-rw-r--r--src/zen/cmds/copy_cmd.cpp194
1 files changed, 194 insertions, 0 deletions
diff --git a/src/zen/cmds/copy_cmd.cpp b/src/zen/cmds/copy_cmd.cpp
new file mode 100644
index 000000000..9f689e5bb
--- /dev/null
+++ b/src/zen/cmds/copy_cmd.cpp
@@ -0,0 +1,194 @@
+// Copyright Epic Games, Inc. All Rights Reserved.
+
+#include "copy_cmd.h"
+
+#include <zencore/filesystem.h>
+#include <zencore/fmtutils.h>
+#include <zencore/logging.h>
+#include <zencore/string.h>
+#include <zencore/timer.h>
+
+namespace zen {
+
+CopyCommand::CopyCommand()
+{
+ m_Options.add_options()("h,help", "Print help");
+ m_Options.add_options()("no-clone", "Do not perform block clone", cxxopts::value(m_NoClone)->default_value("false"));
+ m_Options.add_option("", "s", "source", "Copy source", cxxopts::value(m_CopySource), "<file/directory>");
+ m_Options.add_option("", "t", "target", "Copy target", cxxopts::value(m_CopyTarget), "<file/directory>");
+ m_Options.parse_positional({"source", "target"});
+}
+
+CopyCommand::~CopyCommand() = default;
+
+int
+CopyCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv)
+{
+ ZEN_UNUSED(GlobalOptions);
+
+ if (!ZenCmdBase::ParseOptions(argc, argv))
+ {
+ return 0;
+ }
+
+ // Validate arguments
+
+ if (m_CopySource.empty())
+ throw std::runtime_error("No source specified");
+
+ if (m_CopyTarget.empty())
+ throw std::runtime_error("No target specified");
+
+ std::filesystem::path FromPath;
+ std::filesystem::path ToPath;
+
+ FromPath = m_CopySource;
+ ToPath = m_CopyTarget;
+
+ const bool IsFileCopy = std::filesystem::is_regular_file(m_CopySource);
+ const bool IsDirCopy = std::filesystem::is_directory(m_CopySource);
+
+ if (!IsFileCopy && !IsDirCopy)
+ {
+ throw std::runtime_error("Invalid source specification (neither directory nor file)");
+ }
+
+ if (IsFileCopy && IsDirCopy)
+ {
+ throw std::runtime_error("Invalid source specification (both directory AND file!?)");
+ }
+
+ if (IsDirCopy)
+ {
+ if (std::filesystem::exists(ToPath))
+ {
+ const bool IsTargetDir = std::filesystem::is_directory(ToPath);
+ if (!IsTargetDir)
+ {
+ if (std::filesystem::is_regular_file(ToPath))
+ {
+ throw std::runtime_error("Attempted copy of directory into file");
+ }
+ }
+ }
+ else
+ {
+ std::filesystem::create_directories(ToPath);
+ }
+
+ // Multi file copy
+
+ ZEN_CONSOLE("copying {} -> {}", FromPath, ToPath);
+
+ zen::Stopwatch Timer;
+
+ struct CopyVisitor : public FileSystemTraversal::TreeVisitor
+ {
+ CopyVisitor(std::filesystem::path InBasePath, zen::CopyFileOptions InCopyOptions)
+ : BasePath(InBasePath)
+ , CopyOptions(InCopyOptions)
+ {
+ }
+
+ virtual void VisitFile(const std::filesystem::path& Parent, const path_view& File, uint64_t FileSize) override
+ {
+ ZEN_UNUSED(FileSize);
+ std::error_code Ec;
+ const std::filesystem::path Relative = std::filesystem::relative(Parent, BasePath, Ec);
+
+ if (Ec)
+ {
+ FailedFileCount++;
+ }
+ else
+ {
+ const std::filesystem::path FromPath = Parent / File;
+ const std::filesystem::path ToPath = TargetPath / Relative / File;
+
+ try
+ {
+ zen::CreateDirectories(TargetPath / Relative);
+ if (zen::CopyFile(FromPath, ToPath, CopyOptions))
+ {
+ ++FileCount;
+ ByteCount += FileSize;
+ }
+ else
+ {
+ throw std::logic_error("CopyFile failed in an unexpected way");
+ }
+ }
+ catch (std::exception& Ex)
+ {
+ ++FailedFileCount;
+
+ ZEN_CONSOLE("Error: failed to copy '{}' to '{}': '{}'", FromPath, ToPath, Ex.what());
+ }
+ }
+ }
+
+ virtual bool VisitDirectory(const std::filesystem::path&, const path_view&) override { return true; }
+
+ std::filesystem::path BasePath;
+ std::filesystem::path TargetPath;
+ zen::CopyFileOptions CopyOptions;
+ int FileCount = 0;
+ uint64_t ByteCount = 0;
+ int FailedFileCount = 0;
+ };
+
+ zen::CopyFileOptions CopyOptions;
+ CopyOptions.EnableClone = !m_NoClone;
+
+ CopyVisitor Visitor{FromPath, CopyOptions};
+ Visitor.TargetPath = ToPath;
+
+ FileSystemTraversal Traversal;
+ Traversal.TraverseFileSystem(FromPath, Visitor);
+
+ ZEN_CONSOLE("Copy of {} files ({}) completed in {} ({})",
+ Visitor.FileCount,
+ NiceBytes(Visitor.ByteCount),
+ zen::NiceTimeSpanMs(Timer.GetElapsedTimeMs()),
+ zen::NiceRate(Visitor.ByteCount, (uint32_t)Timer.GetElapsedTimeMs()));
+
+ if (Visitor.FailedFileCount)
+ {
+ ZEN_CONSOLE("{} file copy operations FAILED");
+
+ return 1;
+ }
+ }
+ else
+ {
+ // Single file copy
+
+ zen::Stopwatch Timer;
+
+ zen::CopyFileOptions CopyOptions;
+ CopyOptions.EnableClone = !m_NoClone;
+
+ try
+ {
+ zen::CreateDirectories(ToPath.parent_path());
+ if (zen::CopyFile(FromPath, ToPath, CopyOptions))
+ {
+ ZEN_CONSOLE("Copy completed in {}", zen::NiceTimeSpanMs(Timer.GetElapsedTimeMs()));
+ }
+ else
+ {
+ throw std::logic_error("CopyFile failed in an unexpected way");
+ }
+ }
+ catch (std::exception& Ex)
+ {
+ ZEN_CONSOLE("Error: failed to copy '{}' to '{}': '{}'", FromPath, ToPath, Ex.what());
+
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+} // namespace zen