@ -1,21 +1,21 @@
|
||||
#include "utils/action_queue.hpp"
|
||||
|
||||
#include "types/repertory.hpp"
|
||||
|
||||
namespace repertory::utils::action_queue {
|
||||
action_queue::action_queue(const std::string &id,
|
||||
std::uint8_t max_concurrent_actions)
|
||||
: single_thread_service_base("action_queue_" + id),
|
||||
id_(id),
|
||||
max_concurrent_actions_(max_concurrent_actions) {}
|
||||
|
||||
void action_queue::service_function() {
|
||||
//
|
||||
}
|
||||
|
||||
void action_queue::push(std::function<void()> action) {
|
||||
unique_mutex_lock queue_lock(queue_mtx_);
|
||||
queue_.emplace_back(action);
|
||||
queue_notify_.notify_all();
|
||||
}
|
||||
} // namespace repertory::utils::action_queue
|
||||
#include "utils/action_queue.hpp"
|
||||
|
||||
#include "types/repertory.hpp"
|
||||
|
||||
namespace repertory::utils::action_queue {
|
||||
action_queue::action_queue(const std::string &id,
|
||||
std::uint8_t max_concurrent_actions)
|
||||
: single_thread_service_base("action_queue_" + id),
|
||||
id_(id),
|
||||
max_concurrent_actions_(max_concurrent_actions) {}
|
||||
|
||||
void action_queue::service_function() {
|
||||
//
|
||||
}
|
||||
|
||||
void action_queue::push(std::function<void()> action) {
|
||||
unique_mutex_lock queue_lock(queue_mtx_);
|
||||
queue_.emplace_back(action);
|
||||
queue_notify_.notify_all();
|
||||
}
|
||||
} // namespace repertory::utils::action_queue
|
||||
|
@ -1,256 +1,266 @@
|
||||
/*
|
||||
Copyright <2018-2023> <scott.e.graves@protonmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#include "utils/cli_utils.hpp"
|
||||
|
||||
#include "app_config.hpp"
|
||||
#include "utils/file_utils.hpp"
|
||||
#include "utils/path_utils.hpp"
|
||||
#include "utils/string_utils.hpp"
|
||||
#include "utils/utils.hpp"
|
||||
|
||||
namespace repertory::utils::cli {
|
||||
void get_api_authentication_data(std::string &user, std::string &password,
|
||||
std::uint16_t &port, const provider_type &pt,
|
||||
const std::string &data_directory) {
|
||||
const auto cfg_file_path = utils::path::combine(
|
||||
data_directory.empty() ? app_config::default_data_directory(pt)
|
||||
: data_directory,
|
||||
{"config.json"});
|
||||
|
||||
json data;
|
||||
const auto success = utils::retryable_action([&]() -> bool {
|
||||
return utils::file::read_json_file(cfg_file_path, data);
|
||||
});
|
||||
|
||||
if (success) {
|
||||
if (user.empty() && password.empty()) {
|
||||
password = data["ApiAuth"].get<std::string>();
|
||||
user = data["ApiUser"].get<std::string>();
|
||||
}
|
||||
port = data["ApiPort"].get<std::uint16_t>();
|
||||
}
|
||||
}
|
||||
|
||||
auto get_provider_type_from_args(int argc, char *argv[]) -> provider_type {
|
||||
#if defined(REPERTORY_ENABLE_S3)
|
||||
if (has_option(argc, argv, options::s3_option)) {
|
||||
return provider_type::s3;
|
||||
}
|
||||
#endif // defined(REPERTORY_ENABLE_S3)
|
||||
if (has_option(argc, argv, options::remote_mount_option)) {
|
||||
return provider_type::remote;
|
||||
}
|
||||
if (has_option(argc, argv, options::encrypt_option)) {
|
||||
return provider_type::encrypt;
|
||||
}
|
||||
|
||||
return provider_type::sia;
|
||||
}
|
||||
|
||||
auto has_option(int argc, char *argv[], const std::string &option_name)
|
||||
-> bool {
|
||||
auto ret = false;
|
||||
for (int i = 0; not ret && (i < argc); i++) {
|
||||
ret = (option_name == argv[i]);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
auto has_option(int argc, char *argv[], const option &opt) -> bool {
|
||||
return has_option(argc, argv, opt[0u]) || has_option(argc, argv, opt[1u]);
|
||||
}
|
||||
|
||||
auto parse_option(int argc, char *argv[], const std::string &option_name,
|
||||
std::uint8_t count) -> std::vector<std::string> {
|
||||
std::vector<std::string> ret;
|
||||
auto found = false;
|
||||
for (auto i = 0; not found && (i < argc); i++) {
|
||||
if ((found = (option_name == argv[i]))) {
|
||||
if ((++i + count) <= argc) {
|
||||
while (count--) {
|
||||
ret.emplace_back(argv[i++]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
auto parse_string_option(int argc, char **argv, const option &opt,
|
||||
std::string &value) -> exit_code {
|
||||
auto ret = exit_code::success;
|
||||
if (has_option(argc, argv, opt[0u]) || has_option(argc, argv, opt[1u])) {
|
||||
auto data = parse_option(argc, argv, opt[0u], 1u);
|
||||
if (data.empty()) {
|
||||
data = parse_option(argc, argv, opt[1u], 1u);
|
||||
if (data.empty()) {
|
||||
std::cerr << "Invalid syntax for '" << opt[0u] << "," << opt[1u] << '\''
|
||||
<< std::endl;
|
||||
ret = exit_code::invalid_syntax;
|
||||
}
|
||||
}
|
||||
|
||||
if (not data.empty()) {
|
||||
value = data[0u];
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
auto parse_drive_options(int argc, char **argv,
|
||||
[[maybe_unused]] provider_type &pt,
|
||||
[[maybe_unused]] std::string &data_directory)
|
||||
-> std::vector<std::string> {
|
||||
// Strip out options from command line
|
||||
const auto &option_list = options::option_list;
|
||||
std::vector<std::string> drive_args;
|
||||
for (int i = 0; i < argc; i++) {
|
||||
const auto &a = argv[i];
|
||||
if (std::find_if(option_list.begin(), option_list.end(),
|
||||
[&a](const auto &pair) -> bool {
|
||||
return ((pair[0] == a) || (pair[1] == a));
|
||||
}) == option_list.end()) {
|
||||
drive_args.emplace_back(argv[i]);
|
||||
} else if ((std::string(argv[i]) == options::remote_mount_option[0]) ||
|
||||
(std::string(argv[i]) == options::remote_mount_option[1])) {
|
||||
i++;
|
||||
}
|
||||
#ifdef REPERTORY_ENABLE_S3
|
||||
else if ((std::string(argv[i]) == options::name_option[0]) ||
|
||||
(std::string(argv[i]) == options::name_option[1])) {
|
||||
i++;
|
||||
}
|
||||
#endif // REPERTORY_ENABLE_S3
|
||||
}
|
||||
|
||||
#ifndef _WIN32
|
||||
std::vector<std::string> fuse_flags_list;
|
||||
for (std::size_t i = 1; i < drive_args.size(); i++) {
|
||||
if (drive_args[i].find("-o") == 0) {
|
||||
std::string options = "";
|
||||
if (drive_args[i].size() == 2) {
|
||||
if ((i + 1) < drive_args.size()) {
|
||||
options = drive_args[++i];
|
||||
}
|
||||
} else {
|
||||
options = drive_args[i].substr(2);
|
||||
}
|
||||
|
||||
const auto fuse_option_list = utils::string::split(options, ',');
|
||||
for (const auto &fuse_option : fuse_option_list) {
|
||||
#if defined(REPERTORY_ENABLE_S3)
|
||||
if (fuse_option.find("s3") == 0) {
|
||||
pt = provider_type::s3;
|
||||
}
|
||||
#endif // defined(REPERTORY_ENABLE_S3)
|
||||
if ((fuse_option.find("dd") == 0) ||
|
||||
(fuse_option.find("data_directory") == 0)) {
|
||||
const auto data = utils::string::split(fuse_option, '=');
|
||||
if (data.size() == 2) {
|
||||
data_directory = data[1];
|
||||
} else {
|
||||
std::cerr << "Invalid syntax for '-dd,--data_directory'"
|
||||
<< std::endl;
|
||||
exit(3);
|
||||
}
|
||||
} else {
|
||||
fuse_flags_list.push_back(fuse_option);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
std::vector<std::string> new_drive_args(drive_args);
|
||||
for (std::size_t i = 1; i < new_drive_args.size(); i++) {
|
||||
if (new_drive_args[i].find("-o") == 0) {
|
||||
if (new_drive_args[i].size() == 2) {
|
||||
if ((i + 1) < drive_args.size()) {
|
||||
utils::remove_element_from(new_drive_args, new_drive_args[i]);
|
||||
utils::remove_element_from(new_drive_args, new_drive_args[i]);
|
||||
i--;
|
||||
}
|
||||
} else {
|
||||
utils::remove_element_from(new_drive_args, new_drive_args[i]);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if FUSE_USE_VERSION < 30
|
||||
{
|
||||
auto it = std::remove_if(
|
||||
fuse_flags_list.begin(), fuse_flags_list.end(),
|
||||
[](const auto &opt) -> bool { return opt.find("hard_remove") == 0; });
|
||||
if (it == fuse_flags_list.end()) {
|
||||
fuse_flags_list.emplace_back("hard_remove");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __APPLE__
|
||||
{
|
||||
auto it = std::remove_if(
|
||||
fuse_flags_list.begin(), fuse_flags_list.end(),
|
||||
[](const auto &opt) -> bool { return opt.find("volname") == 0; });
|
||||
if (it != fuse_flags_list.end()) {
|
||||
fuse_flags_list.erase(it, fuse_flags_list.end());
|
||||
}
|
||||
fuse_flags_list.emplace_back("volname=Repertory" +
|
||||
app_config::get_provider_display_name(pt));
|
||||
|
||||
it = std::remove_if(fuse_flags_list.begin(), fuse_flags_list.end(),
|
||||
[](const auto &opt) -> bool {
|
||||
return opt.find("daemon_timeout") == 0;
|
||||
});
|
||||
if (it != fuse_flags_list.end()) {
|
||||
fuse_flags_list.erase(it, fuse_flags_list.end());
|
||||
}
|
||||
fuse_flags_list.emplace_back("daemon_timeout=300");
|
||||
|
||||
it = std::remove_if(fuse_flags_list.begin(), fuse_flags_list.end(),
|
||||
[](const auto &opt) -> bool {
|
||||
return opt.find("noappledouble") == 0;
|
||||
});
|
||||
if (it == fuse_flags_list.end()) {
|
||||
fuse_flags_list.emplace_back("noappledouble");
|
||||
}
|
||||
}
|
||||
#endif // __APPLE__
|
||||
|
||||
if (not fuse_flags_list.empty()) {
|
||||
std::string fuse_options;
|
||||
for (const auto &flag : fuse_flags_list) {
|
||||
fuse_options += (fuse_options.empty()) ? flag : (',' + flag);
|
||||
}
|
||||
new_drive_args.emplace_back("-o");
|
||||
new_drive_args.emplace_back(fuse_options);
|
||||
}
|
||||
drive_args = std::move(new_drive_args);
|
||||
}
|
||||
#endif // _WIN32
|
||||
|
||||
return drive_args;
|
||||
}
|
||||
} // namespace repertory::utils::cli
|
||||
/*
|
||||
Copyright <2018-2023> <scott.e.graves@protonmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#include "utils/cli_utils.hpp"
|
||||
|
||||
#include "app_config.hpp"
|
||||
#include "utils/file_utils.hpp"
|
||||
#include "utils/path_utils.hpp"
|
||||
#include "utils/string_utils.hpp"
|
||||
#include "utils/utils.hpp"
|
||||
|
||||
namespace repertory::utils::cli {
|
||||
void get_api_authentication_data(std::string &user, std::string &password,
|
||||
std::uint16_t &port, const provider_type &prov,
|
||||
const std::string &data_directory) {
|
||||
const auto cfg_file_path = utils::path::combine(
|
||||
data_directory.empty() ? app_config::default_data_directory(prov)
|
||||
: data_directory,
|
||||
{"config.json"});
|
||||
|
||||
json data;
|
||||
const auto success = utils::retryable_action([&]() -> bool {
|
||||
return utils::file::read_json_file(cfg_file_path, data);
|
||||
});
|
||||
|
||||
if (success) {
|
||||
if (user.empty() && password.empty()) {
|
||||
password = data["ApiAuth"].get<std::string>();
|
||||
user = data["ApiUser"].get<std::string>();
|
||||
}
|
||||
port = data["ApiPort"].get<std::uint16_t>();
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] auto get_provider_type_from_args(std::vector<const char *> args)
|
||||
-> provider_type {
|
||||
#if defined(REPERTORY_ENABLE_S3)
|
||||
if (has_option(args, options::s3_option)) {
|
||||
return provider_type::s3;
|
||||
}
|
||||
#endif // defined(REPERTORY_ENABLE_S3)
|
||||
if (has_option(args, options::remote_mount_option)) {
|
||||
return provider_type::remote;
|
||||
}
|
||||
if (has_option(args, options::encrypt_option)) {
|
||||
return provider_type::encrypt;
|
||||
}
|
||||
|
||||
return provider_type::sia;
|
||||
}
|
||||
|
||||
auto has_option(std::vector<const char *> args, const std::string &option_name)
|
||||
-> bool {
|
||||
return std::find_if(args.begin(), args.end(),
|
||||
[&option_name](const auto &value) -> bool {
|
||||
return option_name == value;
|
||||
}) != args.end();
|
||||
}
|
||||
|
||||
auto has_option(std::vector<const char *> args, const option &opt) -> bool {
|
||||
return has_option(args, opt.at(0U)) || has_option(args, opt.at(1U));
|
||||
}
|
||||
|
||||
auto parse_option(std::vector<const char *> args,
|
||||
const std::string &option_name, std::uint8_t count)
|
||||
-> std::vector<std::string> {
|
||||
std::vector<std::string> ret;
|
||||
auto found{false};
|
||||
for (std::size_t i = 0U; not found && (i < args.size()); i++) {
|
||||
if ((found = (option_name == args.at(i)))) {
|
||||
if ((++i + count) <= args.size()) {
|
||||
while ((count--) != 0U) {
|
||||
ret.emplace_back(args.at(i++));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
auto parse_string_option(std::vector<const char *> args, const option &opt,
|
||||
std::string &value) -> exit_code {
|
||||
exit_code ret{exit_code::success};
|
||||
if (has_option(args, opt.at(0U)) || has_option(args, opt.at(1U))) {
|
||||
auto data = parse_option(args, opt.at(0U), 1U);
|
||||
if (data.empty()) {
|
||||
data = parse_option(args, opt.at(1U), 1U);
|
||||
if (data.empty()) {
|
||||
std::cerr << "Invalid syntax for '" << opt.at(0U) << "," << opt.at(1U)
|
||||
<< '\'' << std::endl;
|
||||
ret = exit_code::invalid_syntax;
|
||||
}
|
||||
}
|
||||
|
||||
if (not data.empty()) {
|
||||
value = data.at(0U);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
auto parse_drive_options(std::vector<const char *> args,
|
||||
[[maybe_unused]] provider_type &prov,
|
||||
[[maybe_unused]] std::string &data_directory)
|
||||
-> std::vector<std::string> {
|
||||
// Strip out options from command line
|
||||
const auto &option_list = options::option_list;
|
||||
std::vector<std::string> drive_args;
|
||||
for (std::size_t i = 0U; i < args.size(); i++) {
|
||||
const auto &arg = args.at(i);
|
||||
if (std::find_if(option_list.begin(), option_list.end(),
|
||||
[&arg](const auto &pair) -> bool {
|
||||
return ((pair.at(0U) == arg) || (pair.at(1U) == arg));
|
||||
}) == option_list.end()) {
|
||||
drive_args.emplace_back(args.at(i));
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((std::string(args.at(i)) == options::remote_mount_option.at(0U)) ||
|
||||
(std::string(args.at(i)) == options::remote_mount_option.at(1U)) ||
|
||||
(std::string(args.at(i)) == options::data_directory_option.at(0U)) ||
|
||||
(std::string(args.at(i)) == options::data_directory_option.at(1U))
|
||||
#ifdef REPERTORY_ENABLE_S3
|
||||
|| (std::string(args.at(i)) == options::name_option.at(0U)) ||
|
||||
(std::string(args.at(i)) == options::name_option.at(1U))
|
||||
#endif // REPERTORY_ENABLE_S3
|
||||
) {
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef _WIN32
|
||||
std::vector<std::string> fuse_flags_list;
|
||||
for (std::size_t i = 1; i < drive_args.size(); i++) {
|
||||
if (drive_args.at(i).find("-o") == 0) {
|
||||
std::string options;
|
||||
if (drive_args[i].size() == 2) {
|
||||
if ((i + 1) < drive_args.size()) {
|
||||
options = drive_args.at(++i);
|
||||
}
|
||||
} else {
|
||||
options = drive_args.at(i).substr(2);
|
||||
}
|
||||
|
||||
const auto fuse_option_list = utils::string::split(options, ',');
|
||||
for (const auto &fuse_option : fuse_option_list) {
|
||||
#if defined(REPERTORY_ENABLE_S3)
|
||||
if (fuse_option.find("s3") == 0) {
|
||||
prov = provider_type::s3;
|
||||
continue;
|
||||
}
|
||||
#endif // defined(REPERTORY_ENABLE_S3)
|
||||
if ((fuse_option.find("dd") == 0) ||
|
||||
(fuse_option.find("data_directory") == 0)) {
|
||||
const auto data = utils::string::split(fuse_option, '=');
|
||||
if (data.size() == 2U) {
|
||||
data_directory = data.at(1U);
|
||||
} else {
|
||||
std::cerr << "Invalid syntax for '-dd,--data_directory'"
|
||||
<< std::endl;
|
||||
exit(3);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
fuse_flags_list.push_back(fuse_option);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
std::vector<std::string> new_drive_args(drive_args);
|
||||
for (std::size_t i = 1; i < new_drive_args.size(); i++) {
|
||||
if (new_drive_args[i].find("-o") == 0) {
|
||||
if (new_drive_args[i].size() == 2) {
|
||||
if ((i + 1) < drive_args.size()) {
|
||||
utils::remove_element_from(new_drive_args, new_drive_args[i]);
|
||||
utils::remove_element_from(new_drive_args, new_drive_args[i]);
|
||||
i--;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
utils::remove_element_from(new_drive_args, new_drive_args[i]);
|
||||
i--;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
#if FUSE_USE_VERSION < 30
|
||||
{
|
||||
auto it = std::remove_if(
|
||||
fuse_flags_list.begin(), fuse_flags_list.end(),
|
||||
[](const auto &opt) -> bool { return opt.find("hard_remove") == 0; });
|
||||
if (it == fuse_flags_list.end()) {
|
||||
fuse_flags_list.emplace_back("hard_remove");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __APPLE__
|
||||
{
|
||||
auto it = std::remove_if(
|
||||
fuse_flags_list.begin(), fuse_flags_list.end(),
|
||||
[](const auto &opt) -> bool { return opt.find("volname") == 0; });
|
||||
if (it != fuse_flags_list.end()) {
|
||||
fuse_flags_list.erase(it, fuse_flags_list.end());
|
||||
}
|
||||
fuse_flags_list.emplace_back("volname=Repertory" +
|
||||
app_config::get_provider_display_name(prov));
|
||||
|
||||
it = std::remove_if(fuse_flags_list.begin(), fuse_flags_list.end(),
|
||||
[](const auto &opt) -> bool {
|
||||
return opt.find("daemon_timeout") == 0;
|
||||
});
|
||||
if (it != fuse_flags_list.end()) {
|
||||
fuse_flags_list.erase(it, fuse_flags_list.end());
|
||||
}
|
||||
fuse_flags_list.emplace_back("daemon_timeout=300");
|
||||
|
||||
it = std::remove_if(fuse_flags_list.begin(), fuse_flags_list.end(),
|
||||
[](const auto &opt) -> bool {
|
||||
return opt.find("noappledouble") == 0;
|
||||
});
|
||||
if (it == fuse_flags_list.end()) {
|
||||
fuse_flags_list.emplace_back("noappledouble");
|
||||
}
|
||||
}
|
||||
#endif // __APPLE__
|
||||
|
||||
if (not fuse_flags_list.empty()) {
|
||||
std::string fuse_options;
|
||||
for (const auto &flag : fuse_flags_list) {
|
||||
fuse_options += (fuse_options.empty()) ? flag : (',' + flag);
|
||||
}
|
||||
new_drive_args.emplace_back("-o");
|
||||
new_drive_args.emplace_back(fuse_options);
|
||||
}
|
||||
drive_args = std::move(new_drive_args);
|
||||
}
|
||||
#endif // _WIN32
|
||||
|
||||
return drive_args;
|
||||
}
|
||||
} // namespace repertory::utils::cli
|
||||
|
@ -1,344 +1,398 @@
|
||||
/*
|
||||
Copyright <2018-2023> <scott.e.graves@protonmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#include "utils/encrypting_reader.hpp"
|
||||
|
||||
#include "events/event_system.hpp"
|
||||
#include "events/events.hpp"
|
||||
#include "types/repertory.hpp"
|
||||
#include "utils/encryption.hpp"
|
||||
#include "utils/error_utils.hpp"
|
||||
#include "utils/utils.hpp"
|
||||
|
||||
namespace repertory::utils::encryption {
|
||||
class encrypting_streambuf final : public encrypting_reader::streambuf {
|
||||
public:
|
||||
explicit encrypting_streambuf(const encrypting_reader &reader)
|
||||
: reader_(reader) {
|
||||
setg(reinterpret_cast<char *>(0), reinterpret_cast<char *>(0),
|
||||
reinterpret_cast<char *>(reader_.get_total_size()));
|
||||
}
|
||||
|
||||
~encrypting_streambuf() override = default;
|
||||
|
||||
private:
|
||||
encrypting_reader reader_;
|
||||
|
||||
protected:
|
||||
auto seekoff(off_type off, std::ios_base::seekdir dir,
|
||||
std::ios_base::openmode which = std::ios_base::out |
|
||||
std::ios_base::in)
|
||||
-> pos_type override {
|
||||
if ((which & std::ios_base::in) != std::ios_base::in) {
|
||||
throw std::runtime_error("output is not supported");
|
||||
}
|
||||
|
||||
const auto set_position = [this](char *next) -> pos_type {
|
||||
if ((next <= egptr()) && (next >= eback())) {
|
||||
setg(eback(), next, reinterpret_cast<char *>(reader_.get_total_size()));
|
||||
return static_cast<std::streamoff>(
|
||||
reinterpret_cast<std::uintptr_t>(gptr()));
|
||||
}
|
||||
|
||||
return {traits_type::eof()};
|
||||
};
|
||||
|
||||
switch (dir) {
|
||||
case std::ios_base::beg:
|
||||
return set_position(eback() + off);
|
||||
|
||||
case std::ios_base::cur:
|
||||
return set_position(gptr() + off);
|
||||
|
||||
case std::ios_base::end:
|
||||
return set_position(egptr() + off);
|
||||
}
|
||||
|
||||
return encrypting_reader::streambuf::seekoff(off, dir, which);
|
||||
}
|
||||
|
||||
auto seekpos(pos_type pos,
|
||||
std::ios_base::openmode which = std::ios_base::out |
|
||||
std::ios_base::in)
|
||||
-> pos_type override {
|
||||
return seekoff(pos, std::ios_base::beg, which);
|
||||
}
|
||||
|
||||
auto uflow() -> int_type override {
|
||||
auto ret = underflow();
|
||||
if (ret == traits_type::eof()) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
gbump(1);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
auto underflow() -> int_type override {
|
||||
if (gptr() == egptr()) {
|
||||
return traits_type::eof();
|
||||
}
|
||||
|
||||
reader_.set_read_position(
|
||||
static_cast<std::uint64_t>(reinterpret_cast<std::uintptr_t>(gptr())));
|
||||
|
||||
char c{};
|
||||
const auto res = reader_.reader_function(&c, 1u, 1u, &reader_);
|
||||
if (res != 1) {
|
||||
return traits_type::eof();
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
auto xsgetn(char *ptr, std::streamsize count) -> std::streamsize override {
|
||||
if (gptr() == egptr()) {
|
||||
return traits_type::eof();
|
||||
}
|
||||
|
||||
reader_.set_read_position(
|
||||
static_cast<std::uint64_t>(reinterpret_cast<std::uintptr_t>(gptr())));
|
||||
|
||||
const auto res = reader_.reader_function(ptr, 1u, count, &reader_);
|
||||
if ((res == reader_.get_error_return()) ||
|
||||
(reader_.get_stop_requested() && (res == CURL_READFUNC_ABORT))) {
|
||||
return traits_type::eof();
|
||||
}
|
||||
|
||||
setg(eback(), gptr() + res,
|
||||
reinterpret_cast<char *>(reader_.get_total_size()));
|
||||
return res;
|
||||
}
|
||||
};
|
||||
|
||||
class encrypting_reader_iostream final : public encrypting_reader::iostream {
|
||||
public:
|
||||
explicit encrypting_reader_iostream(
|
||||
std::unique_ptr<encrypting_streambuf> buffer)
|
||||
: encrypting_reader::iostream(buffer.get()), buffer_(std::move(buffer)) {}
|
||||
|
||||
~encrypting_reader_iostream() override = default;
|
||||
|
||||
private:
|
||||
std::unique_ptr<encrypting_streambuf> buffer_;
|
||||
};
|
||||
|
||||
const std::size_t encrypting_reader::header_size_ = ([]() {
|
||||
return crypto_aead_xchacha20poly1305_IETF_NPUBBYTES +
|
||||
crypto_aead_xchacha20poly1305_IETF_ABYTES;
|
||||
})();
|
||||
const std::size_t encrypting_reader::data_chunk_size_ = (8u * 1024u * 1024u);
|
||||
const std::size_t encrypting_reader::encrypted_chunk_size_ =
|
||||
data_chunk_size_ + header_size_;
|
||||
|
||||
encrypting_reader::encrypting_reader(
|
||||
const std::string &file_name, const std::string &source_path,
|
||||
stop_type &stop_requested, const std::string &token,
|
||||
std::optional<std::string> relative_parent_path, const size_t error_return)
|
||||
: key_(utils::encryption::generate_key(token)),
|
||||
stop_requested_(stop_requested),
|
||||
error_return_(error_return) {
|
||||
const auto res = native_file::open(
|
||||
source_path, not relative_parent_path.has_value(), source_file_);
|
||||
if (res != api_error::success) {
|
||||
throw std::runtime_error("file open failed|src|" + source_path + '|' +
|
||||
api_error_to_string(res));
|
||||
}
|
||||
|
||||
data_buffer result;
|
||||
utils::encryption::encrypt_data(key_, file_name.c_str(),
|
||||
strnlen(file_name.c_str(), file_name.size()),
|
||||
result);
|
||||
encrypted_file_name_ = utils::to_hex_string(result);
|
||||
|
||||
if (relative_parent_path.has_value()) {
|
||||
for (const auto &part :
|
||||
std::filesystem::path(relative_parent_path.value())) {
|
||||
utils::encryption::encrypt_data(
|
||||
key_, part.string().c_str(),
|
||||
strnlen(part.string().c_str(), part.string().size()), result);
|
||||
encrypted_file_path_ += '/' + utils::to_hex_string(result);
|
||||
}
|
||||
encrypted_file_path_ += '/' + encrypted_file_name_;
|
||||
}
|
||||
|
||||
std::uint64_t file_size{};
|
||||
if (not utils::file::get_file_size(source_path, file_size)) {
|
||||
throw std::runtime_error("get file size failed|src|" + source_path + '|' +
|
||||
std::to_string(utils::get_last_error_code()));
|
||||
}
|
||||
|
||||
const auto total_chunks = static_cast<std::size_t>(utils::divide_with_ceiling(
|
||||
file_size, static_cast<std::uint64_t>(data_chunk_size_)));
|
||||
total_size_ =
|
||||
file_size + (total_chunks * encrypting_reader::get_header_size());
|
||||
last_data_chunk_ = total_chunks - 1u;
|
||||
last_data_chunk_size_ = static_cast<std::size_t>(
|
||||
(file_size <= data_chunk_size_) ? file_size
|
||||
: (file_size % data_chunk_size_) ? file_size % data_chunk_size_
|
||||
: data_chunk_size_);
|
||||
iv_list_.resize(total_chunks);
|
||||
for (auto &iv : iv_list_) {
|
||||
randombytes_buf(iv.data(), iv.size());
|
||||
}
|
||||
}
|
||||
|
||||
encrypting_reader::encrypting_reader(
|
||||
const std::string &encrypted_file_path, const std::string &source_path,
|
||||
stop_type &stop_requested, const std::string &token,
|
||||
std::vector<
|
||||
std::array<unsigned char, crypto_aead_xchacha20poly1305_IETF_NPUBBYTES>>
|
||||
iv_list,
|
||||
const size_t error_return)
|
||||
: key_(utils::encryption::generate_key(token)),
|
||||
stop_requested_(stop_requested),
|
||||
error_return_(error_return) {
|
||||
const auto res = native_file::open(source_path, false, source_file_);
|
||||
if (res != api_error::success) {
|
||||
throw std::runtime_error("file open failed|src|" + source_path + '|' +
|
||||
api_error_to_string(res));
|
||||
}
|
||||
|
||||
encrypted_file_path_ = encrypted_file_path;
|
||||
encrypted_file_name_ =
|
||||
std::filesystem::path(encrypted_file_path_).filename().string();
|
||||
|
||||
std::uint64_t file_size{};
|
||||
if (not utils::file::get_file_size(source_path, file_size)) {
|
||||
throw std::runtime_error("get file size failed|src|" + source_path + '|' +
|
||||
std::to_string(utils::get_last_error_code()));
|
||||
}
|
||||
|
||||
const auto total_chunks = static_cast<std::size_t>(utils::divide_with_ceiling(
|
||||
file_size, static_cast<std::uint64_t>(data_chunk_size_)));
|
||||
total_size_ =
|
||||
file_size + (total_chunks * encrypting_reader::get_header_size());
|
||||
last_data_chunk_ = total_chunks - 1u;
|
||||
last_data_chunk_size_ = static_cast<std::size_t>(
|
||||
(file_size <= data_chunk_size_) ? file_size
|
||||
: (file_size % data_chunk_size_) ? file_size % data_chunk_size_
|
||||
: data_chunk_size_);
|
||||
iv_list_ = std::move(iv_list);
|
||||
}
|
||||
|
||||
encrypting_reader::encrypting_reader(const encrypting_reader &r)
|
||||
: key_(r.key_),
|
||||
stop_requested_(r.stop_requested_),
|
||||
error_return_(r.error_return_),
|
||||
chunk_buffers_(r.chunk_buffers_),
|
||||
encrypted_file_name_(r.encrypted_file_name_),
|
||||
encrypted_file_path_(r.encrypted_file_path_),
|
||||
iv_list_(r.iv_list_),
|
||||
last_data_chunk_(r.last_data_chunk_),
|
||||
last_data_chunk_size_(r.last_data_chunk_size_),
|
||||
read_offset_(r.read_offset_),
|
||||
source_file_(native_file::clone(r.source_file_)),
|
||||
total_size_(r.total_size_) {}
|
||||
|
||||
encrypting_reader::~encrypting_reader() {
|
||||
if (source_file_) {
|
||||
source_file_->close();
|
||||
}
|
||||
}
|
||||
|
||||
auto encrypting_reader::calculate_decrypted_size(std::uint64_t total_size)
|
||||
-> std::uint64_t {
|
||||
return total_size - (utils::divide_with_ceiling(
|
||||
total_size, static_cast<std::uint64_t>(
|
||||
get_encrypted_chunk_size())) *
|
||||
get_header_size());
|
||||
}
|
||||
|
||||
auto encrypting_reader::calculate_encrypted_size(const std::string &source_path)
|
||||
-> std::uint64_t {
|
||||
std::uint64_t file_size{};
|
||||
if (not utils::file::get_file_size(source_path, file_size)) {
|
||||
throw std::runtime_error("get file size failed|src|" + source_path + '|' +
|
||||
std::to_string(utils::get_last_error_code()));
|
||||
}
|
||||
|
||||
const auto total_chunks = static_cast<std::size_t>(utils::divide_with_ceiling(
|
||||
file_size, static_cast<std::uint64_t>(data_chunk_size_)));
|
||||
return file_size + (total_chunks * encrypting_reader::get_header_size());
|
||||
}
|
||||
|
||||
auto encrypting_reader::create_iostream() const
|
||||
-> std::shared_ptr<encrypting_reader::iostream> {
|
||||
return std::make_shared<encrypting_reader_iostream>(
|
||||
std::make_unique<encrypting_streambuf>(*this));
|
||||
}
|
||||
|
||||
auto encrypting_reader::reader_function(char *buffer, size_t size,
|
||||
size_t nitems) -> size_t {
|
||||
const auto read_size = static_cast<std::size_t>(std::min(
|
||||
static_cast<std::uint64_t>(size * nitems), total_size_ - read_offset_));
|
||||
|
||||
auto chunk = static_cast<std::size_t>(read_offset_ / encrypted_chunk_size_);
|
||||
auto chunk_offset =
|
||||
static_cast<std::size_t>(read_offset_ % encrypted_chunk_size_);
|
||||
std::size_t total_read = 0u;
|
||||
|
||||
auto ret = false;
|
||||
if (read_offset_ < total_size_) {
|
||||
try {
|
||||
ret = true;
|
||||
auto remain = read_size;
|
||||
while (not stop_requested_ && ret && remain) {
|
||||
if (chunk_buffers_.find(chunk) == chunk_buffers_.end()) {
|
||||
auto &chunk_buffer = chunk_buffers_[chunk];
|
||||
data_buffer file_data(chunk == last_data_chunk_
|
||||
? last_data_chunk_size_
|
||||
: data_chunk_size_);
|
||||
chunk_buffer.resize(file_data.size() + get_header_size());
|
||||
|
||||
std::size_t bytes_read{};
|
||||
if ((ret = source_file_->read_bytes(&file_data[0u], file_data.size(),
|
||||
chunk * data_chunk_size_,
|
||||
bytes_read))) {
|
||||
utils::encryption::encrypt_data(iv_list_.at(chunk), key_, file_data,
|
||||
chunk_buffer);
|
||||
}
|
||||
} else if (chunk) {
|
||||
chunk_buffers_.erase(chunk - 1u);
|
||||
}
|
||||
|
||||
auto &chunk_buffer = chunk_buffers_[chunk];
|
||||
const auto to_read =
|
||||
std::min(chunk_buffer.size() - chunk_offset, remain);
|
||||
std::memcpy(buffer + total_read, &chunk_buffer[chunk_offset], to_read);
|
||||
total_read += to_read;
|
||||
remain -= to_read;
|
||||
chunk_offset = 0u;
|
||||
chunk++;
|
||||
read_offset_ += to_read;
|
||||
}
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::raise_error(__FUNCTION__, e, "exception occurred");
|
||||
ret = false;
|
||||
}
|
||||
}
|
||||
|
||||
return stop_requested_ ? CURL_READFUNC_ABORT
|
||||
: ret ? total_read
|
||||
: error_return_;
|
||||
}
|
||||
} // namespace repertory::utils::encryption
|
||||
/*
|
||||
Copyright <2018-2023> <scott.e.graves@protonmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#include "utils/encrypting_reader.hpp"
|
||||
|
||||
#include "events/event_system.hpp"
|
||||
#include "events/events.hpp"
|
||||
#include "types/repertory.hpp"
|
||||
#include "utils/encryption.hpp"
|
||||
#include "utils/error_utils.hpp"
|
||||
#include "utils/utils.hpp"
|
||||
|
||||
namespace repertory::utils::encryption {
|
||||
class encrypting_streambuf final : public encrypting_reader::streambuf {
|
||||
public:
|
||||
encrypting_streambuf(const encrypting_streambuf &) = default;
|
||||
encrypting_streambuf(encrypting_streambuf &&) = delete;
|
||||
auto operator=(const encrypting_streambuf &)
|
||||
-> encrypting_streambuf & = delete;
|
||||
auto operator=(encrypting_streambuf &&) -> encrypting_streambuf & = delete;
|
||||
|
||||
explicit encrypting_streambuf(const encrypting_reader &reader)
|
||||
: reader_(reader) {
|
||||
setg(reinterpret_cast<char *>(0), reinterpret_cast<char *>(0),
|
||||
reinterpret_cast<char *>(reader_.get_total_size()));
|
||||
}
|
||||
|
||||
~encrypting_streambuf() override = default;
|
||||
|
||||
private:
|
||||
encrypting_reader reader_;
|
||||
|
||||
protected:
|
||||
auto seekoff(off_type off, std::ios_base::seekdir dir,
|
||||
std::ios_base::openmode which = std::ios_base::out |
|
||||
std::ios_base::in)
|
||||
-> pos_type override {
|
||||
if ((which & std::ios_base::in) != std::ios_base::in) {
|
||||
throw std::runtime_error("output is not supported");
|
||||
}
|
||||
|
||||
const auto set_position = [this](char *next) -> pos_type {
|
||||
if ((next <= egptr()) && (next >= eback())) {
|
||||
setg(eback(), next, reinterpret_cast<char *>(reader_.get_total_size()));
|
||||
return static_cast<std::streamoff>(
|
||||
reinterpret_cast<std::uintptr_t>(gptr()));
|
||||
}
|
||||
|
||||
return {traits_type::eof()};
|
||||
};
|
||||
|
||||
switch (dir) {
|
||||
case std::ios_base::beg:
|
||||
return set_position(eback() + off);
|
||||
|
||||
case std::ios_base::cur:
|
||||
return set_position(gptr() + off);
|
||||
|
||||
case std::ios_base::end:
|
||||
return set_position(egptr() + off);
|
||||
}
|
||||
|
||||
return encrypting_reader::streambuf::seekoff(off, dir, which);
|
||||
}
|
||||
|
||||
auto seekpos(pos_type pos,
|
||||
std::ios_base::openmode which = std::ios_base::out |
|
||||
std::ios_base::in)
|
||||
-> pos_type override {
|
||||
return seekoff(pos, std::ios_base::beg, which);
|
||||
}
|
||||
|
||||
auto uflow() -> int_type override {
|
||||
auto ret = underflow();
|
||||
if (ret == traits_type::eof()) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
gbump(1);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
auto underflow() -> int_type override {
|
||||
if (gptr() == egptr()) {
|
||||
return traits_type::eof();
|
||||
}
|
||||
|
||||
reader_.set_read_position(reinterpret_cast<std::uintptr_t>(gptr()));
|
||||
|
||||
char c{};
|
||||
const auto res = encrypting_reader::reader_function(&c, 1U, 1U, &reader_);
|
||||
if (res != 1) {
|
||||
return traits_type::eof();
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
auto xsgetn(char *ptr, std::streamsize count) -> std::streamsize override {
|
||||
if (gptr() == egptr()) {
|
||||
return traits_type::eof();
|
||||
}
|
||||
|
||||
reader_.set_read_position(reinterpret_cast<std::uintptr_t>(gptr()));
|
||||
|
||||
const auto res = encrypting_reader::reader_function(
|
||||
ptr, 1U, static_cast<std::size_t>(count), &reader_);
|
||||
if ((res == reader_.get_error_return()) ||
|
||||
(reader_.get_stop_requested() && (res == CURL_READFUNC_ABORT))) {
|
||||
return traits_type::eof();
|
||||
}
|
||||
|
||||
setg(eback(), gptr() + res,
|
||||
reinterpret_cast<char *>(reader_.get_total_size()));
|
||||
return static_cast<std::streamsize>(res);
|
||||
}
|
||||
};
|
||||
|
||||
class encrypting_reader_iostream final : public encrypting_reader::iostream {
|
||||
public:
|
||||
encrypting_reader_iostream(const encrypting_reader_iostream &) = delete;
|
||||
encrypting_reader_iostream(encrypting_reader_iostream &&) = delete;
|
||||
|
||||
auto operator=(const encrypting_reader_iostream &)
|
||||
-> encrypting_reader_iostream & = delete;
|
||||
auto operator=(encrypting_reader_iostream &&)
|
||||
-> encrypting_reader_iostream & = delete;
|
||||
|
||||
explicit encrypting_reader_iostream(
|
||||
std::unique_ptr<encrypting_streambuf> buffer)
|
||||
: encrypting_reader::iostream(buffer.get()), buffer_(std::move(buffer)) {}
|
||||
|
||||
~encrypting_reader_iostream() override = default;
|
||||
|
||||
private:
|
||||
std::unique_ptr<encrypting_streambuf> buffer_;
|
||||
};
|
||||
|
||||
const std::size_t encrypting_reader::header_size_ = ([]() {
|
||||
return crypto_aead_xchacha20poly1305_IETF_NPUBBYTES +
|
||||
crypto_aead_xchacha20poly1305_IETF_ABYTES;
|
||||
})();
|
||||
const std::size_t encrypting_reader::data_chunk_size_ = (8UL * 1024UL * 1024UL);
|
||||
const std::size_t encrypting_reader::encrypted_chunk_size_ =
|
||||
data_chunk_size_ + header_size_;
|
||||
|
||||
encrypting_reader::encrypting_reader(
|
||||
const std::string &file_name, const std::string &source_path,
|
||||
stop_type &stop_requested, const std::string &token,
|
||||
std::optional<std::string> relative_parent_path, std::size_t error_return)
|
||||
: key_(utils::encryption::generate_key(token)),
|
||||
stop_requested_(stop_requested),
|
||||
error_return_(error_return) {
|
||||
const auto res = native_file::create_or_open(
|
||||
source_path, not relative_parent_path.has_value(), source_file_);
|
||||
if (res != api_error::success) {
|
||||
throw std::runtime_error("file open failed|src|" + source_path + '|' +
|
||||
api_error_to_string(res));
|
||||
}
|
||||
|
||||
data_buffer result;
|
||||
utils::encryption::encrypt_data(key_, file_name.c_str(),
|
||||
strnlen(file_name.c_str(), file_name.size()),
|
||||
result);
|
||||
encrypted_file_name_ = utils::to_hex_string(result);
|
||||
|
||||
if (relative_parent_path.has_value()) {
|
||||
for (const auto &part :
|
||||
std::filesystem::path(relative_parent_path.value())) {
|
||||
utils::encryption::encrypt_data(
|
||||
key_, part.string().c_str(),
|
||||
strnlen(part.string().c_str(), part.string().size()), result);
|
||||
encrypted_file_path_ += '/' + utils::to_hex_string(result);
|
||||
}
|
||||
encrypted_file_path_ += '/' + encrypted_file_name_;
|
||||
}
|
||||
|
||||
std::uint64_t file_size{};
|
||||
if (not utils::file::get_file_size(source_path, file_size)) {
|
||||
throw std::runtime_error("get file size failed|src|" + source_path + '|' +
|
||||
std::to_string(utils::get_last_error_code()));
|
||||
}
|
||||
|
||||
const auto total_chunks = utils::divide_with_ceiling(
|
||||
file_size, static_cast<std::uint64_t>(data_chunk_size_));
|
||||
total_size_ =
|
||||
file_size + (total_chunks * encrypting_reader::get_header_size());
|
||||
last_data_chunk_ = total_chunks - 1U;
|
||||
last_data_chunk_size_ = (file_size <= data_chunk_size_) ? file_size
|
||||
: (file_size % data_chunk_size_) == 0U
|
||||
? data_chunk_size_
|
||||
: file_size % data_chunk_size_;
|
||||
iv_list_.resize(total_chunks);
|
||||
for (auto &iv : iv_list_) {
|
||||
randombytes_buf(iv.data(), iv.size());
|
||||
}
|
||||
}
|
||||
|
||||
encrypting_reader::encrypting_reader(const std::string &encrypted_file_path,
|
||||
const std::string &source_path,
|
||||
stop_type &stop_requested,
|
||||
const std::string &token,
|
||||
std::size_t error_return)
|
||||
: key_(utils::encryption::generate_key(token)),
|
||||
stop_requested_(stop_requested),
|
||||
error_return_(error_return) {
|
||||
const auto res =
|
||||
native_file::create_or_open(source_path, false, source_file_);
|
||||
if (res != api_error::success) {
|
||||
throw std::runtime_error("file open failed|src|" + source_path + '|' +
|
||||
api_error_to_string(res));
|
||||
}
|
||||
|
||||
encrypted_file_path_ = encrypted_file_path;
|
||||
encrypted_file_name_ =
|
||||
std::filesystem::path(encrypted_file_path_).filename().string();
|
||||
|
||||
std::uint64_t file_size{};
|
||||
if (not utils::file::get_file_size(source_path, file_size)) {
|
||||
throw std::runtime_error("get file size failed|src|" + source_path + '|' +
|
||||
std::to_string(utils::get_last_error_code()));
|
||||
}
|
||||
|
||||
const auto total_chunks = utils::divide_with_ceiling(
|
||||
file_size, static_cast<std::uint64_t>(data_chunk_size_));
|
||||
total_size_ =
|
||||
file_size + (total_chunks * encrypting_reader::get_header_size());
|
||||
last_data_chunk_ = total_chunks - 1U;
|
||||
last_data_chunk_size_ = (file_size <= data_chunk_size_) ? file_size
|
||||
: (file_size % data_chunk_size_) == 0U
|
||||
? data_chunk_size_
|
||||
: file_size % data_chunk_size_;
|
||||
iv_list_.resize(total_chunks);
|
||||
for (auto &iv : iv_list_) {
|
||||
randombytes_buf(iv.data(), iv.size());
|
||||
}
|
||||
}
|
||||
|
||||
encrypting_reader::encrypting_reader(
|
||||
const std::string &encrypted_file_path, const std::string &source_path,
|
||||
stop_type &stop_requested, const std::string &token,
|
||||
std::vector<
|
||||
std::array<unsigned char, crypto_aead_xchacha20poly1305_IETF_NPUBBYTES>>
|
||||
iv_list,
|
||||
std::size_t error_return)
|
||||
: key_(utils::encryption::generate_key(token)),
|
||||
stop_requested_(stop_requested),
|
||||
error_return_(error_return) {
|
||||
const auto res =
|
||||
native_file::create_or_open(source_path, false, source_file_);
|
||||
if (res != api_error::success) {
|
||||
throw std::runtime_error("file open failed|src|" + source_path + '|' +
|
||||
api_error_to_string(res));
|
||||
}
|
||||
|
||||
encrypted_file_path_ = encrypted_file_path;
|
||||
encrypted_file_name_ =
|
||||
std::filesystem::path(encrypted_file_path_).filename().string();
|
||||
|
||||
std::uint64_t file_size{};
|
||||
if (not utils::file::get_file_size(source_path, file_size)) {
|
||||
throw std::runtime_error("get file size failed|src|" + source_path + '|' +
|
||||
std::to_string(utils::get_last_error_code()));
|
||||
}
|
||||
|
||||
const auto total_chunks = utils::divide_with_ceiling(
|
||||
file_size, static_cast<std::uint64_t>(data_chunk_size_));
|
||||
total_size_ =
|
||||
file_size + (total_chunks * encrypting_reader::get_header_size());
|
||||
last_data_chunk_ = total_chunks - 1U;
|
||||
last_data_chunk_size_ = (file_size <= data_chunk_size_) ? file_size
|
||||
: (file_size % data_chunk_size_) == 0U
|
||||
? data_chunk_size_
|
||||
: file_size % data_chunk_size_;
|
||||
iv_list_ = std::move(iv_list);
|
||||
}
|
||||
|
||||
encrypting_reader::encrypting_reader(const encrypting_reader &reader)
|
||||
: key_(reader.key_),
|
||||
stop_requested_(reader.stop_requested_),
|
||||
error_return_(reader.error_return_),
|
||||
chunk_buffers_(reader.chunk_buffers_),
|
||||
encrypted_file_name_(reader.encrypted_file_name_),
|
||||
encrypted_file_path_(reader.encrypted_file_path_),
|
||||
iv_list_(reader.iv_list_),
|
||||
last_data_chunk_(reader.last_data_chunk_),
|
||||
last_data_chunk_size_(reader.last_data_chunk_size_),
|
||||
read_offset_(reader.read_offset_),
|
||||
source_file_(native_file::clone(reader.source_file_)),
|
||||
total_size_(reader.total_size_) {}
|
||||
|
||||
encrypting_reader::~encrypting_reader() {
|
||||
if (source_file_) {
|
||||
source_file_->close();
|
||||
}
|
||||
}
|
||||
|
||||
auto encrypting_reader::calculate_decrypted_size(std::uint64_t total_size)
|
||||
-> std::uint64_t {
|
||||
return total_size - (utils::divide_with_ceiling(
|
||||
total_size, static_cast<std::uint64_t>(
|
||||
get_encrypted_chunk_size())) *
|
||||
get_header_size());
|
||||
}
|
||||
|
||||
auto encrypting_reader::calculate_encrypted_size(const std::string &source_path)
|
||||
-> std::uint64_t {
|
||||
std::uint64_t file_size{};
|
||||
if (not utils::file::get_file_size(source_path, file_size)) {
|
||||
throw std::runtime_error("get file size failed|src|" + source_path + '|' +
|
||||
std::to_string(utils::get_last_error_code()));
|
||||
}
|
||||
|
||||
const auto total_chunks = utils::divide_with_ceiling(
|
||||
file_size, static_cast<std::uint64_t>(data_chunk_size_));
|
||||
return file_size + (total_chunks * encrypting_reader::get_header_size());
|
||||
}
|
||||
|
||||
auto encrypting_reader::create_iostream() const
|
||||
-> std::shared_ptr<encrypting_reader::iostream> {
|
||||
return std::make_shared<encrypting_reader_iostream>(
|
||||
std::make_unique<encrypting_streambuf>(*this));
|
||||
}
|
||||
|
||||
auto encrypting_reader::reader_function(char *buffer, size_t size,
|
||||
size_t nitems) -> size_t {
|
||||
const auto read_size = static_cast<std::size_t>(std::min(
|
||||
static_cast<std::uint64_t>(size * nitems), total_size_ - read_offset_));
|
||||
|
||||
auto chunk = read_offset_ / encrypted_chunk_size_;
|
||||
auto chunk_offset = read_offset_ % encrypted_chunk_size_;
|
||||
std::size_t total_read = 0u;
|
||||
|
||||
auto ret = false;
|
||||
if (read_offset_ < total_size_) {
|
||||
try {
|
||||
ret = true;
|
||||
auto remain = read_size;
|
||||
while (not stop_requested_ && ret && remain) {
|
||||
if (chunk_buffers_.find(chunk) == chunk_buffers_.end()) {
|
||||
auto &chunk_buffer = chunk_buffers_[chunk];
|
||||
data_buffer file_data(chunk == last_data_chunk_
|
||||
? last_data_chunk_size_
|
||||
: data_chunk_size_);
|
||||
chunk_buffer.resize(file_data.size() + get_header_size());
|
||||
|
||||
std::size_t bytes_read{};
|
||||
if ((ret = source_file_->read_bytes(&file_data[0u], file_data.size(),
|
||||
chunk * data_chunk_size_,
|
||||
bytes_read))) {
|
||||
utils::encryption::encrypt_data(iv_list_.at(chunk), key_, file_data,
|
||||
chunk_buffer);
|
||||
}
|
||||
} else if (chunk) {
|
||||
chunk_buffers_.erase(chunk - 1u);
|
||||
}
|
||||
|
||||
auto &chunk_buffer = chunk_buffers_[chunk];
|
||||
const auto to_read = std::min(
|
||||
static_cast<std::size_t>(chunk_buffer.size() - chunk_offset),
|
||||
remain);
|
||||
std::memcpy(buffer + total_read, &chunk_buffer[chunk_offset], to_read);
|
||||
total_read += to_read;
|
||||
remain -= to_read;
|
||||
chunk_offset = 0u;
|
||||
chunk++;
|
||||
read_offset_ += to_read;
|
||||
}
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::raise_error(__FUNCTION__, e, "exception occurred");
|
||||
ret = false;
|
||||
}
|
||||
}
|
||||
|
||||
return stop_requested_ ? CURL_READFUNC_ABORT
|
||||
: ret ? total_read
|
||||
: error_return_;
|
||||
}
|
||||
} // namespace repertory::utils::encryption
|
||||
|
@ -1,140 +1,140 @@
|
||||
/*
|
||||
Copyright <2018-2023> <scott.e.graves@protonmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#include "utils/encryption.hpp"
|
||||
|
||||
#include "events/event_system.hpp"
|
||||
#include "events/events.hpp"
|
||||
#include "types/repertory.hpp"
|
||||
#include "utils/encrypting_reader.hpp"
|
||||
#include "utils/utils.hpp"
|
||||
|
||||
namespace repertory::utils::encryption {
|
||||
auto decrypt_file_path(const std::string &encryption_token,
|
||||
std::string &file_path) -> api_error {
|
||||
std::string decrypted_file_path{};
|
||||
for (const auto &part : std::filesystem::path(file_path)) {
|
||||
auto file_name = part.string();
|
||||
if (file_name == "/") {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto res = decrypt_file_name(encryption_token, file_name);
|
||||
if (res != api_error::success) {
|
||||
return res;
|
||||
}
|
||||
|
||||
decrypted_file_path += '/' + file_name;
|
||||
}
|
||||
|
||||
file_path = decrypted_file_path;
|
||||
return api_error::success;
|
||||
}
|
||||
|
||||
auto decrypt_file_name(const std::string &encryption_token,
|
||||
std::string &file_name) -> api_error {
|
||||
data_buffer buffer;
|
||||
if (not utils::from_hex_string(file_name, buffer)) {
|
||||
return api_error::error;
|
||||
}
|
||||
|
||||
file_name.clear();
|
||||
if (not utils::encryption::decrypt_data(encryption_token, buffer,
|
||||
file_name)) {
|
||||
return api_error::decryption_error;
|
||||
}
|
||||
|
||||
return api_error::success;
|
||||
}
|
||||
|
||||
auto generate_key(const std::string &encryption_token) -> key_type {
|
||||
crypto_hash_sha256_state state{};
|
||||
auto res = crypto_hash_sha256_init(&state);
|
||||
if (res != 0) {
|
||||
throw std::runtime_error("failed to initialize sha256|" +
|
||||
std::to_string(res));
|
||||
}
|
||||
|
||||
if ((res = crypto_hash_sha256_update(
|
||||
&state,
|
||||
reinterpret_cast<const unsigned char *>(encryption_token.c_str()),
|
||||
strnlen(encryption_token.c_str(), encryption_token.size()))) != 0) {
|
||||
throw std::runtime_error("failed to update sha256|" + std::to_string(res));
|
||||
}
|
||||
|
||||
key_type ret{};
|
||||
if ((res = crypto_hash_sha256_final(&state, ret.data())) != 0) {
|
||||
throw std::runtime_error("failed to finalize sha256|" +
|
||||
std::to_string(res));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
auto read_encrypted_range(
|
||||
const http_range &range, const key_type &key,
|
||||
const std::function<api_error(data_buffer &ct, std::uint64_t start_offset,
|
||||
std::uint64_t end_offset)> &reader,
|
||||
std::uint64_t total_size, data_buffer &data) -> api_error {
|
||||
const auto encrypted_chunk_size =
|
||||
utils::encryption::encrypting_reader::get_encrypted_chunk_size();
|
||||
const auto data_chunk_size =
|
||||
utils::encryption::encrypting_reader::get_data_chunk_size();
|
||||
const auto header_size =
|
||||
utils::encryption::encrypting_reader::get_header_size();
|
||||
|
||||
const auto start_chunk =
|
||||
static_cast<std::size_t>(range.begin / data_chunk_size);
|
||||
const auto end_chunk = static_cast<std::size_t>(range.end / data_chunk_size);
|
||||
auto remain = range.end - range.begin + 1u;
|
||||
auto source_offset = static_cast<std::size_t>(range.begin % data_chunk_size);
|
||||
|
||||
for (std::size_t chunk = start_chunk; chunk <= end_chunk; chunk++) {
|
||||
data_buffer ct;
|
||||
const auto start_offset = chunk * encrypted_chunk_size;
|
||||
const auto end_offset = std::min(
|
||||
start_offset + (total_size - (chunk * data_chunk_size)) + header_size -
|
||||
1u,
|
||||
static_cast<std::uint64_t>(start_offset + encrypted_chunk_size - 1u));
|
||||
|
||||
const auto result = reader(ct, start_offset, end_offset);
|
||||
if (result != api_error::success) {
|
||||
return result;
|
||||
}
|
||||
|
||||
data_buffer source_buffer;
|
||||
if (not utils::encryption::decrypt_data(key, ct, source_buffer)) {
|
||||
return api_error::decryption_error;
|
||||
}
|
||||
ct.clear();
|
||||
|
||||
const auto data_size = static_cast<std::size_t>(std::min(
|
||||
remain, static_cast<std::uint64_t>(data_chunk_size - source_offset)));
|
||||
std::copy(source_buffer.begin() + source_offset,
|
||||
source_buffer.begin() + source_offset + data_size,
|
||||
std::back_inserter(data));
|
||||
remain -= data_size;
|
||||
source_offset = 0u;
|
||||
}
|
||||
|
||||
return api_error::success;
|
||||
}
|
||||
} // namespace repertory::utils::encryption
|
||||
/*
|
||||
Copyright <2018-2023> <scott.e.graves@protonmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#include "utils/encryption.hpp"
|
||||
|
||||
#include "events/event_system.hpp"
|
||||
#include "events/events.hpp"
|
||||
#include "types/repertory.hpp"
|
||||
#include "utils/encrypting_reader.hpp"
|
||||
#include "utils/utils.hpp"
|
||||
|
||||
namespace repertory::utils::encryption {
|
||||
auto decrypt_file_path(const std::string &encryption_token,
|
||||
std::string &file_path) -> api_error {
|
||||
std::string decrypted_file_path{};
|
||||
for (const auto &part : std::filesystem::path(file_path)) {
|
||||
auto file_name = part.string();
|
||||
if (file_name == "/") {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto res = decrypt_file_name(encryption_token, file_name);
|
||||
if (res != api_error::success) {
|
||||
return res;
|
||||
}
|
||||
|
||||
decrypted_file_path += '/' + file_name;
|
||||
}
|
||||
|
||||
file_path = decrypted_file_path;
|
||||
return api_error::success;
|
||||
}
|
||||
|
||||
auto decrypt_file_name(const std::string &encryption_token,
|
||||
std::string &file_name) -> api_error {
|
||||
data_buffer buffer;
|
||||
if (not utils::from_hex_string(file_name, buffer)) {
|
||||
return api_error::error;
|
||||
}
|
||||
|
||||
file_name.clear();
|
||||
if (not utils::encryption::decrypt_data(encryption_token, buffer,
|
||||
file_name)) {
|
||||
return api_error::decryption_error;
|
||||
}
|
||||
|
||||
return api_error::success;
|
||||
}
|
||||
|
||||
auto generate_key(const std::string &encryption_token) -> key_type {
|
||||
crypto_hash_sha256_state state{};
|
||||
auto res = crypto_hash_sha256_init(&state);
|
||||
if (res != 0) {
|
||||
throw std::runtime_error("failed to initialize sha256|" +
|
||||
std::to_string(res));
|
||||
}
|
||||
res = crypto_hash_sha256_update(
|
||||
&state, reinterpret_cast<const unsigned char *>(encryption_token.c_str()),
|
||||
strnlen(encryption_token.c_str(), encryption_token.size()));
|
||||
if (res != 0) {
|
||||
throw std::runtime_error("failed to update sha256|" + std::to_string(res));
|
||||
}
|
||||
|
||||
key_type ret{};
|
||||
res = crypto_hash_sha256_final(&state, ret.data());
|
||||
if (res != 0) {
|
||||
throw std::runtime_error("failed to finalize sha256|" +
|
||||
std::to_string(res));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
auto read_encrypted_range(const http_range &range, const key_type &key,
|
||||
reader_func reader, std::uint64_t total_size,
|
||||
data_buffer &data) -> api_error {
|
||||
const auto encrypted_chunk_size =
|
||||
utils::encryption::encrypting_reader::get_encrypted_chunk_size();
|
||||
const auto data_chunk_size =
|
||||
utils::encryption::encrypting_reader::get_data_chunk_size();
|
||||
const auto header_size =
|
||||
utils::encryption::encrypting_reader::get_header_size();
|
||||
|
||||
const auto start_chunk =
|
||||
static_cast<std::size_t>(range.begin / data_chunk_size);
|
||||
const auto end_chunk = static_cast<std::size_t>(range.end / data_chunk_size);
|
||||
auto remain = range.end - range.begin + 1U;
|
||||
auto source_offset = static_cast<std::size_t>(range.begin % data_chunk_size);
|
||||
|
||||
for (std::size_t chunk = start_chunk; chunk <= end_chunk; chunk++) {
|
||||
data_buffer cypher;
|
||||
const auto start_offset = chunk * encrypted_chunk_size;
|
||||
const auto end_offset = std::min(
|
||||
start_offset + (total_size - (chunk * data_chunk_size)) + header_size -
|
||||
1U,
|
||||
static_cast<std::uint64_t>(start_offset + encrypted_chunk_size - 1U));
|
||||
|
||||
const auto result = reader(cypher, start_offset, end_offset);
|
||||
if (result != api_error::success) {
|
||||
return result;
|
||||
}
|
||||
|
||||
data_buffer source_buffer;
|
||||
if (not utils::encryption::decrypt_data(key, cypher, source_buffer)) {
|
||||
return api_error::decryption_error;
|
||||
}
|
||||
cypher.clear();
|
||||
|
||||
const auto data_size = static_cast<std::size_t>(std::min(
|
||||
remain, static_cast<std::uint64_t>(data_chunk_size - source_offset)));
|
||||
std::copy(std::next(source_buffer.begin(),
|
||||
static_cast<std::int64_t>(source_offset)),
|
||||
std::next(source_buffer.begin(),
|
||||
static_cast<std::int64_t>(source_offset + data_size)),
|
||||
std::back_inserter(data));
|
||||
remain -= data_size;
|
||||
source_offset = 0U;
|
||||
}
|
||||
|
||||
return api_error::success;
|
||||
}
|
||||
} // namespace repertory::utils::encryption
|
||||
|
@ -1,172 +1,166 @@
|
||||
/*
|
||||
Copyright <2018-2023> <scott.e.graves@protonmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#include "utils/error_utils.hpp"
|
||||
|
||||
#include "events/event_system.hpp"
|
||||
#include "events/events.hpp"
|
||||
#include "types/repertory.hpp"
|
||||
|
||||
namespace repertory::utils::error {
|
||||
void raise_error(std::string_view function, std::string_view msg) {
|
||||
event_system::instance().raise<repertory_exception>(
|
||||
static_cast<std::string>(function), static_cast<std::string>(msg));
|
||||
}
|
||||
|
||||
void raise_error(std::string_view function, const api_error &e,
|
||||
std::string_view msg) {
|
||||
event_system::instance().raise<repertory_exception>(
|
||||
static_cast<std::string>(function),
|
||||
static_cast<std::string>(msg) + "|err|" + api_error_to_string(e));
|
||||
}
|
||||
|
||||
void raise_error(std::string_view function, const std::exception &e,
|
||||
std::string_view msg) {
|
||||
event_system::instance().raise<repertory_exception>(
|
||||
static_cast<std::string>(function),
|
||||
static_cast<std::string>(msg) + "|err|" +
|
||||
(e.what() ? e.what() : "unknown error"));
|
||||
}
|
||||
|
||||
void raise_error(std::string_view function, const json &e,
|
||||
std::string_view msg) {
|
||||
event_system::instance().raise<repertory_exception>(
|
||||
static_cast<std::string>(function),
|
||||
static_cast<std::string>(msg) + "|err|" + e.dump(2));
|
||||
}
|
||||
|
||||
void raise_error(std::string_view function, std::int64_t e,
|
||||
std::string_view msg) {
|
||||
event_system::instance().raise<repertory_exception>(
|
||||
static_cast<std::string>(function),
|
||||
static_cast<std::string>(msg) + "|err|" + std::to_string(e));
|
||||
}
|
||||
|
||||
void raise_error(std::string_view function, const api_error &e,
|
||||
std::string_view file_path, std::string_view msg) {
|
||||
event_system::instance().raise<repertory_exception>(
|
||||
static_cast<std::string>(function),
|
||||
static_cast<std::string>(msg) + "|sp|" +
|
||||
static_cast<std::string>(file_path) + "|err|" +
|
||||
api_error_to_string(e));
|
||||
}
|
||||
|
||||
void raise_error(std::string_view function, std::int64_t e,
|
||||
std::string_view file_path, std::string_view msg) {
|
||||
event_system::instance().raise<repertory_exception>(
|
||||
static_cast<std::string>(function),
|
||||
static_cast<std::string>(msg) + "|sp|" +
|
||||
static_cast<std::string>(file_path) + "|err|" + std::to_string(e));
|
||||
}
|
||||
|
||||
void raise_error(std::string_view function, const std::exception &e,
|
||||
std::string_view file_path, std::string_view msg) {
|
||||
event_system::instance().raise<repertory_exception>(
|
||||
static_cast<std::string>(function),
|
||||
static_cast<std::string>(msg) + "|sp|" +
|
||||
static_cast<std::string>(file_path) + "|err|" +
|
||||
(e.what() ? e.what() : "unknown error"));
|
||||
}
|
||||
|
||||
void raise_api_path_error(std::string_view function, std::string_view api_path,
|
||||
const api_error &e, std::string_view msg) {
|
||||
event_system::instance().raise<repertory_exception>(
|
||||
static_cast<std::string>(function),
|
||||
static_cast<std::string>(msg) + "|ap|" +
|
||||
static_cast<std::string>(api_path) + "|err|" +
|
||||
api_error_to_string(e));
|
||||
}
|
||||
|
||||
void raise_api_path_error(std::string_view function, std::string_view api_path,
|
||||
std::int64_t e, std::string_view msg) {
|
||||
event_system::instance().raise<repertory_exception>(
|
||||
static_cast<std::string>(function),
|
||||
static_cast<std::string>(msg) + "|ap|" +
|
||||
static_cast<std::string>(api_path) + "|err|" + std::to_string(e));
|
||||
}
|
||||
|
||||
void raise_api_path_error(std::string_view function, std::string_view api_path,
|
||||
const std::exception &e, std::string_view msg) {
|
||||
event_system::instance().raise<repertory_exception>(
|
||||
static_cast<std::string>(function),
|
||||
static_cast<std::string>(msg) + "|ap|" +
|
||||
static_cast<std::string>(api_path) + "|err|" +
|
||||
(e.what() ? e.what() : "unknown error"));
|
||||
}
|
||||
|
||||
void raise_api_path_error(std::string_view function, std::string_view api_path,
|
||||
std::string_view source_path, const api_error &e,
|
||||
std::string_view msg) {
|
||||
event_system::instance().raise<repertory_exception>(
|
||||
static_cast<std::string>(function),
|
||||
static_cast<std::string>(msg) + "|ap|" +
|
||||
static_cast<std::string>(api_path) + "|sp|" +
|
||||
static_cast<std::string>(source_path) + "|err|" +
|
||||
api_error_to_string(e));
|
||||
}
|
||||
|
||||
void raise_api_path_error(std::string_view function, std::string_view api_path,
|
||||
std::string_view source_path, std::int64_t e,
|
||||
std::string_view msg) {
|
||||
event_system::instance().raise<repertory_exception>(
|
||||
static_cast<std::string>(function),
|
||||
static_cast<std::string>(msg) + "|ap|" +
|
||||
static_cast<std::string>(api_path) + "|sp|" +
|
||||
static_cast<std::string>(source_path) + "|err|" + std::to_string(e));
|
||||
}
|
||||
|
||||
void raise_api_path_error(std::string_view function, std::string_view api_path,
|
||||
const json &e, std::string_view msg) {
|
||||
event_system::instance().raise<repertory_exception>(
|
||||
static_cast<std::string>(function),
|
||||
static_cast<std::string>(msg) + "|ap|" +
|
||||
static_cast<std::string>(api_path) + "|err|" + e.dump(2));
|
||||
}
|
||||
|
||||
void raise_api_path_error(std::string_view function, std::string_view api_path,
|
||||
std::string_view source_path, const std::exception &e,
|
||||
std::string_view msg) {
|
||||
event_system::instance().raise<repertory_exception>(
|
||||
static_cast<std::string>(function),
|
||||
static_cast<std::string>(msg) + "|ap|" +
|
||||
static_cast<std::string>(api_path) + "|sp|" +
|
||||
static_cast<std::string>(source_path) + "|err|" +
|
||||
(e.what() ? e.what() : "unknown error"));
|
||||
}
|
||||
|
||||
void raise_url_error(std::string_view function, std::string_view url,
|
||||
CURLcode e, std::string_view msg) {
|
||||
event_system::instance().raise<repertory_exception>(
|
||||
static_cast<std::string>(function),
|
||||
static_cast<std::string>(msg) + "|url|" + static_cast<std::string>(url) +
|
||||
"|err|" + curl_easy_strerror(e));
|
||||
}
|
||||
|
||||
void raise_url_error(std::string_view function, std::string_view url,
|
||||
std::string_view source_path, const std::exception &e,
|
||||
std::string_view msg) {
|
||||
event_system::instance().raise<repertory_exception>(
|
||||
static_cast<std::string>(function),
|
||||
static_cast<std::string>(msg) + "|url|" + static_cast<std::string>(url) +
|
||||
"|sp|" + static_cast<std::string>(source_path) + "|err|" +
|
||||
(e.what() ? e.what() : "unknown error"));
|
||||
}
|
||||
} // namespace repertory::utils::error
|
||||
/*
|
||||
Copyright <2018-2023> <scott.e.graves@protonmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#include "utils/error_utils.hpp"
|
||||
|
||||
#include "events/event_system.hpp"
|
||||
#include "events/events.hpp"
|
||||
#include "types/repertory.hpp"
|
||||
|
||||
namespace repertory::utils::error {
|
||||
void raise_error(std::string function, std::string_view msg) {
|
||||
event_system::instance().raise<repertory_exception>(
|
||||
function, static_cast<std::string>(msg));
|
||||
}
|
||||
|
||||
void raise_error(std::string function, const api_error &err,
|
||||
std::string_view msg) {
|
||||
event_system::instance().raise<repertory_exception>(
|
||||
function,
|
||||
static_cast<std::string>(msg) + "|err|" + api_error_to_string(err));
|
||||
}
|
||||
|
||||
void raise_error(std::string function, const std::exception &exception,
|
||||
std::string_view msg) {
|
||||
event_system::instance().raise<repertory_exception>(
|
||||
function,
|
||||
static_cast<std::string>(msg) + "|err|" +
|
||||
(exception.what() == nullptr ? "unknown error" : exception.what()));
|
||||
}
|
||||
|
||||
void raise_error(std::string function, const json &err, std::string_view msg) {
|
||||
event_system::instance().raise<repertory_exception>(
|
||||
function, static_cast<std::string>(msg) + "|err|" + err.dump(2));
|
||||
}
|
||||
|
||||
void raise_error(std::string function, std::int64_t err, std::string_view msg) {
|
||||
event_system::instance().raise<repertory_exception>(
|
||||
function, static_cast<std::string>(msg) + "|err|" + std::to_string(err));
|
||||
}
|
||||
|
||||
void raise_error(std::string function, const api_error &err,
|
||||
std::string_view file_path, std::string_view msg) {
|
||||
event_system::instance().raise<repertory_exception>(
|
||||
function, static_cast<std::string>(msg) + "|sp|" +
|
||||
static_cast<std::string>(file_path) + "|err|" +
|
||||
api_error_to_string(err));
|
||||
}
|
||||
|
||||
void raise_error(std::string function, std::int64_t err,
|
||||
std::string_view file_path, std::string_view msg) {
|
||||
event_system::instance().raise<repertory_exception>(
|
||||
function, static_cast<std::string>(msg) + "|sp|" +
|
||||
static_cast<std::string>(file_path) + "|err|" +
|
||||
std::to_string(err));
|
||||
}
|
||||
|
||||
void raise_error(std::string function, const std::exception &exception,
|
||||
std::string_view file_path, std::string_view msg) {
|
||||
event_system::instance().raise<repertory_exception>(
|
||||
function,
|
||||
static_cast<std::string>(msg) + "|sp|" +
|
||||
static_cast<std::string>(file_path) + "|err|" +
|
||||
(exception.what() == nullptr ? "unknown error" : exception.what()));
|
||||
}
|
||||
|
||||
void raise_api_path_error(std::string function, std::string_view api_path,
|
||||
const api_error &err, std::string_view msg) {
|
||||
event_system::instance().raise<repertory_exception>(
|
||||
function, static_cast<std::string>(msg) + "|ap|" +
|
||||
static_cast<std::string>(api_path) + "|err|" +
|
||||
api_error_to_string(err));
|
||||
}
|
||||
|
||||
void raise_api_path_error(std::string function, std::string_view api_path,
|
||||
std::int64_t err, std::string_view msg) {
|
||||
event_system::instance().raise<repertory_exception>(
|
||||
function, static_cast<std::string>(msg) + "|ap|" +
|
||||
static_cast<std::string>(api_path) + "|err|" +
|
||||
std::to_string(err));
|
||||
}
|
||||
|
||||
void raise_api_path_error(std::string function, std::string_view api_path,
|
||||
const std::exception &exception,
|
||||
std::string_view msg) {
|
||||
event_system::instance().raise<repertory_exception>(
|
||||
function,
|
||||
static_cast<std::string>(msg) + "|ap|" +
|
||||
static_cast<std::string>(api_path) + "|err|" +
|
||||
(exception.what() == nullptr ? "unknown error" : exception.what()));
|
||||
}
|
||||
|
||||
void raise_api_path_error(std::string function, std::string_view api_path,
|
||||
std::string_view source_path, const api_error &err,
|
||||
std::string_view msg) {
|
||||
event_system::instance().raise<repertory_exception>(
|
||||
function, static_cast<std::string>(msg) + "|ap|" +
|
||||
static_cast<std::string>(api_path) + "|sp|" +
|
||||
static_cast<std::string>(source_path) + "|err|" +
|
||||
api_error_to_string(err));
|
||||
}
|
||||
|
||||
void raise_api_path_error(std::string function, std::string_view api_path,
|
||||
std::string_view source_path, std::int64_t err,
|
||||
std::string_view msg) {
|
||||
event_system::instance().raise<repertory_exception>(
|
||||
function, static_cast<std::string>(msg) + "|ap|" +
|
||||
static_cast<std::string>(api_path) + "|sp|" +
|
||||
static_cast<std::string>(source_path) + "|err|" +
|
||||
std::to_string(err));
|
||||
}
|
||||
|
||||
void raise_api_path_error(std::string function, std::string_view api_path,
|
||||
const json &err, std::string_view msg) {
|
||||
event_system::instance().raise<repertory_exception>(
|
||||
function, static_cast<std::string>(msg) + "|ap|" +
|
||||
static_cast<std::string>(api_path) + "|err|" + err.dump(2));
|
||||
}
|
||||
|
||||
void raise_api_path_error(std::string function, std::string_view api_path,
|
||||
std::string_view source_path,
|
||||
const std::exception &exception,
|
||||
std::string_view msg) {
|
||||
event_system::instance().raise<repertory_exception>(
|
||||
function,
|
||||
static_cast<std::string>(msg) + "|ap|" +
|
||||
static_cast<std::string>(api_path) + "|sp|" +
|
||||
static_cast<std::string>(source_path) + "|err|" +
|
||||
(exception.what() == nullptr ? "unknown error" : exception.what()));
|
||||
}
|
||||
|
||||
void raise_url_error(std::string function, std::string_view url, CURLcode err,
|
||||
std::string_view msg) {
|
||||
event_system::instance().raise<repertory_exception>(
|
||||
function, static_cast<std::string>(msg) + "|url|" +
|
||||
static_cast<std::string>(url) + "|err|" +
|
||||
curl_easy_strerror(err));
|
||||
}
|
||||
|
||||
void raise_url_error(std::string function, std::string_view url,
|
||||
std::string_view source_path,
|
||||
const std::exception &exception, std::string_view msg) {
|
||||
event_system::instance().raise<repertory_exception>(
|
||||
function,
|
||||
static_cast<std::string>(msg) + "|url|" + static_cast<std::string>(url) +
|
||||
"|sp|" + static_cast<std::string>(source_path) + "|err|" +
|
||||
(exception.what() == nullptr ? "unknown error" : exception.what()));
|
||||
}
|
||||
} // namespace repertory::utils::error
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,304 +1,305 @@
|
||||
/*
|
||||
Copyright <2018-2023> <scott.e.graves@protonmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#include "utils/native_file.hpp"
|
||||
|
||||
#include "types/repertory.hpp"
|
||||
#include "utils/string_utils.hpp"
|
||||
#include "utils/unix/unix_utils.hpp"
|
||||
#include "utils/utils.hpp"
|
||||
|
||||
namespace repertory {
|
||||
auto native_file::get_handle() -> native_handle { return handle_; }
|
||||
|
||||
native_file::~native_file() {
|
||||
if (auto_close) {
|
||||
close();
|
||||
}
|
||||
}
|
||||
|
||||
auto native_file::clone(const native_file_ptr &nf) -> native_file_ptr {
|
||||
std::string source_path;
|
||||
|
||||
#ifdef _WIN32
|
||||
source_path.resize(MAX_PATH + 1);
|
||||
::GetFinalPathNameByHandleA(nf->get_handle(), &source_path[0u], MAX_PATH + 1,
|
||||
FILE_NAME_NORMALIZED | VOLUME_NAME_DOS);
|
||||
#else
|
||||
source_path.resize(PATH_MAX + 1);
|
||||
#ifdef __APPLE__
|
||||
fcntl(nf->get_handle(), F_GETPATH, &source_path[0u]);
|
||||
#else
|
||||
readlink(("/proc/self/fd/" + std::to_string(nf->get_handle())).c_str(),
|
||||
&source_path[0u], source_path.size());
|
||||
#endif
|
||||
#endif
|
||||
source_path = source_path.c_str();
|
||||
|
||||
native_file_ptr clone;
|
||||
auto res = native_file::open(source_path, clone);
|
||||
if (res != api_error::success) {
|
||||
throw std::runtime_error("unable to open file|sp|" + source_path + "|err|" +
|
||||
api_error_to_string(res));
|
||||
}
|
||||
|
||||
return clone;
|
||||
}
|
||||
|
||||
auto native_file::create_or_open(const std::string &source_path,
|
||||
[[maybe_unused]] bool should_chmod,
|
||||
native_file_ptr &nf) -> api_error {
|
||||
#ifdef _WIN32
|
||||
auto handle = ::CreateFileA(source_path.c_str(), GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr,
|
||||
OPEN_ALWAYS, FILE_FLAG_RANDOM_ACCESS, nullptr);
|
||||
#else
|
||||
auto handle = should_chmod ? ::open(source_path.c_str(),
|
||||
O_CREAT | O_RDWR | O_CLOEXEC, 0600u)
|
||||
: ::open(source_path.c_str(), O_RDWR | O_CLOEXEC);
|
||||
if (should_chmod) {
|
||||
chmod(source_path.c_str(), 0600u);
|
||||
}
|
||||
#endif
|
||||
nf = native_file::attach(handle);
|
||||
return ((handle == REPERTORY_INVALID_HANDLE) ? api_error::os_error
|
||||
: api_error::success);
|
||||
}
|
||||
|
||||
auto native_file::create_or_open(const std::string &source_path,
|
||||
native_file_ptr &nf) -> api_error {
|
||||
return create_or_open(source_path, true, nf);
|
||||
}
|
||||
|
||||
auto native_file::open(const std::string &source_path, native_file_ptr &nf)
|
||||
-> api_error {
|
||||
return open(source_path, true, nf);
|
||||
}
|
||||
|
||||
auto native_file::open(const std::string &source_path,
|
||||
[[maybe_unused]] bool should_chmod, native_file_ptr &nf)
|
||||
-> api_error {
|
||||
#ifdef _WIN32
|
||||
auto handle = ::CreateFileA(source_path.c_str(), GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr,
|
||||
OPEN_EXISTING, FILE_FLAG_RANDOM_ACCESS, nullptr);
|
||||
#else
|
||||
auto handle = should_chmod
|
||||
? ::open(source_path.c_str(), O_RDWR | O_CLOEXEC, 0600u)
|
||||
: ::open(source_path.c_str(), O_RDONLY | O_CLOEXEC);
|
||||
if (should_chmod) {
|
||||
chmod(source_path.c_str(), 0600u);
|
||||
}
|
||||
#endif
|
||||
nf = native_file::attach(handle);
|
||||
return ((handle == REPERTORY_INVALID_HANDLE) ? api_error::os_error
|
||||
: api_error::success);
|
||||
}
|
||||
|
||||
auto native_file::allocate(std::uint64_t file_size) -> bool {
|
||||
#ifdef _WIN32
|
||||
LARGE_INTEGER li{};
|
||||
li.QuadPart = file_size;
|
||||
return (::SetFilePointerEx(handle_, li, nullptr, FILE_BEGIN) &&
|
||||
::SetEndOfFile(handle_));
|
||||
#endif
|
||||
#ifdef __linux__
|
||||
return (fallocate(handle_, 0, 0, file_size) >= 0);
|
||||
#endif
|
||||
#ifdef __APPLE__
|
||||
return (ftruncate(handle_, file_size) >= 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
void native_file::close() {
|
||||
if (handle_ != REPERTORY_INVALID_HANDLE) {
|
||||
#ifdef WIN32
|
||||
::CloseHandle(handle_);
|
||||
#else
|
||||
::close(handle_);
|
||||
#endif
|
||||
handle_ = REPERTORY_INVALID_HANDLE;
|
||||
}
|
||||
}
|
||||
|
||||
auto native_file::copy_from(const native_file_ptr &nf) -> bool {
|
||||
auto ret = false;
|
||||
std::uint64_t file_size = 0u;
|
||||
if ((ret = nf->get_file_size(file_size))) {
|
||||
data_buffer buffer;
|
||||
buffer.resize(65536u * 2u);
|
||||
std::uint64_t offset = 0u;
|
||||
while (ret && (file_size > 0u)) {
|
||||
std::size_t bytes_read{};
|
||||
if ((ret = nf->read_bytes(&buffer[0u], buffer.size(), offset,
|
||||
bytes_read))) {
|
||||
std::size_t bytes_written = 0u;
|
||||
ret = write_bytes(&buffer[0u], bytes_read, offset, bytes_written);
|
||||
file_size -= bytes_read;
|
||||
offset += bytes_read;
|
||||
}
|
||||
}
|
||||
flush();
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
auto native_file::copy_from(const std::string &path) -> bool {
|
||||
auto ret = false;
|
||||
native_file_ptr nf;
|
||||
if (native_file::create_or_open(path, nf) == api_error ::success) {
|
||||
ret = copy_from(nf);
|
||||
nf->close();
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void native_file::flush() {
|
||||
#ifdef _WIN32
|
||||
recur_mutex_lock l(read_write_mutex_);
|
||||
::FlushFileBuffers(handle_);
|
||||
#else
|
||||
fsync(handle_);
|
||||
#endif
|
||||
}
|
||||
|
||||
auto native_file::get_file_size(std::uint64_t &file_size) -> bool {
|
||||
auto ret = false;
|
||||
#ifdef _WIN32
|
||||
LARGE_INTEGER li{};
|
||||
if ((ret = ::GetFileSizeEx(handle_, &li) && (li.QuadPart >= 0))) {
|
||||
file_size = static_cast<std::uint64_t>(li.QuadPart);
|
||||
}
|
||||
#else
|
||||
#if __APPLE__
|
||||
struct stat st {};
|
||||
if (fstat(handle_, &st) >= 0) {
|
||||
#else
|
||||
struct stat64 st {};
|
||||
if (fstat64(handle_, &st) >= 0) {
|
||||
#endif
|
||||
if ((ret = (st.st_size >= 0))) {
|
||||
file_size = static_cast<uint64_t>(st.st_size);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
auto native_file::read_bytes(char *buffer, std::size_t read_size,
|
||||
std::uint64_t read_offset, std::size_t &bytes_read)
|
||||
-> bool {
|
||||
recur_mutex_lock l(read_write_mutex_);
|
||||
|
||||
auto ret = false;
|
||||
bytes_read = 0u;
|
||||
LARGE_INTEGER li{};
|
||||
li.QuadPart = read_offset;
|
||||
if ((ret = !!::SetFilePointerEx(handle_, li, nullptr, FILE_BEGIN))) {
|
||||
DWORD current_read = 0u;
|
||||
do {
|
||||
current_read = 0u;
|
||||
ret = !!::ReadFile(handle_, &buffer[bytes_read],
|
||||
static_cast<DWORD>(read_size - bytes_read),
|
||||
¤t_read, nullptr);
|
||||
bytes_read += current_read;
|
||||
} while (ret && (bytes_read < read_size) && (current_read != 0));
|
||||
}
|
||||
|
||||
if (ret && (read_size != bytes_read)) {
|
||||
::SetLastError(ERROR_HANDLE_EOF);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
#else
|
||||
auto native_file::read_bytes(char *buffer, std::size_t read_size,
|
||||
std::uint64_t read_offset, std::size_t &bytes_read)
|
||||
-> bool {
|
||||
bytes_read = 0u;
|
||||
ssize_t result = 0;
|
||||
do {
|
||||
result = pread64(handle_, &buffer[bytes_read], read_size - bytes_read,
|
||||
read_offset + bytes_read);
|
||||
if (result > 0) {
|
||||
bytes_read += static_cast<size_t>(result);
|
||||
}
|
||||
} while ((result > 0) && (bytes_read < read_size));
|
||||
|
||||
return (result >= 0);
|
||||
}
|
||||
#endif
|
||||
auto native_file::truncate(std::uint64_t file_size) -> bool {
|
||||
#ifdef _WIN32
|
||||
recur_mutex_lock l(read_write_mutex_);
|
||||
LARGE_INTEGER li{};
|
||||
li.QuadPart = file_size;
|
||||
return (::SetFilePointerEx(handle_, li, nullptr, FILE_BEGIN) &&
|
||||
::SetEndOfFile(handle_));
|
||||
#else
|
||||
return (ftruncate(handle_, file_size) >= 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
auto native_file::write_bytes(const char *buffer, std::size_t write_size,
|
||||
std::uint64_t write_offset,
|
||||
std::size_t &bytes_written) -> bool {
|
||||
recur_mutex_lock l(read_write_mutex_);
|
||||
|
||||
bytes_written = 0u;
|
||||
auto ret = true;
|
||||
|
||||
LARGE_INTEGER li{};
|
||||
li.QuadPart = write_offset;
|
||||
if ((ret = !!::SetFilePointerEx(handle_, li, nullptr, FILE_BEGIN))) {
|
||||
do {
|
||||
DWORD current_write = 0u;
|
||||
ret = !!::WriteFile(handle_, &buffer[bytes_written],
|
||||
static_cast<DWORD>(write_size - bytes_written),
|
||||
¤t_write, nullptr);
|
||||
bytes_written += current_write;
|
||||
} while (ret && (bytes_written < write_size));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
#else
|
||||
auto native_file::write_bytes(const char *buffer, std::size_t write_size,
|
||||
std::uint64_t write_offset,
|
||||
std::size_t &bytes_written) -> bool {
|
||||
bytes_written = 0;
|
||||
ssize_t result = 0;
|
||||
do {
|
||||
result = pwrite64(handle_, &buffer[bytes_written],
|
||||
write_size - bytes_written, write_offset + bytes_written);
|
||||
if (result > 0) {
|
||||
bytes_written += static_cast<size_t>(result);
|
||||
}
|
||||
} while ((result >= 0) && (bytes_written < write_size));
|
||||
|
||||
return (bytes_written == write_size);
|
||||
}
|
||||
#endif
|
||||
} // namespace repertory
|
||||
/*
|
||||
Copyright <2018-2023> <scott.e.graves@protonmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#include "utils/native_file.hpp"
|
||||
|
||||
#include "types/repertory.hpp"
|
||||
#include "utils/string_utils.hpp"
|
||||
#include "utils/unix/unix_utils.hpp"
|
||||
#include "utils/utils.hpp"
|
||||
|
||||
namespace repertory {
|
||||
auto native_file::get_handle() -> native_handle { return handle_; }
|
||||
|
||||
native_file::~native_file() {
|
||||
if (auto_close) {
|
||||
close();
|
||||
}
|
||||
}
|
||||
|
||||
auto native_file::clone(const native_file_ptr &nf) -> native_file_ptr {
|
||||
std::string source_path;
|
||||
|
||||
#ifdef _WIN32
|
||||
source_path.resize(MAX_PATH + 1);
|
||||
::GetFinalPathNameByHandleA(nf->get_handle(), &source_path[0u], MAX_PATH + 1,
|
||||
FILE_NAME_NORMALIZED | VOLUME_NAME_DOS);
|
||||
#else
|
||||
source_path.resize(PATH_MAX + 1);
|
||||
#ifdef __APPLE__
|
||||
fcntl(nf->get_handle(), F_GETPATH, &source_path[0u]);
|
||||
#else
|
||||
readlink(("/proc/self/fd/" + std::to_string(nf->get_handle())).c_str(),
|
||||
&source_path[0u], source_path.size());
|
||||
#endif
|
||||
#endif
|
||||
source_path = source_path.c_str();
|
||||
|
||||
native_file_ptr clone;
|
||||
auto res = native_file::open(source_path, clone);
|
||||
if (res != api_error::success) {
|
||||
throw std::runtime_error("unable to open file|sp|" + source_path + "|err|" +
|
||||
api_error_to_string(res));
|
||||
}
|
||||
|
||||
return clone;
|
||||
}
|
||||
|
||||
auto native_file::create_or_open(const std::string &source_path,
|
||||
[[maybe_unused]] bool should_chmod,
|
||||
native_file_ptr &nf) -> api_error {
|
||||
#ifdef _WIN32
|
||||
auto handle = ::CreateFileA(source_path.c_str(), GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr,
|
||||
OPEN_ALWAYS, FILE_FLAG_RANDOM_ACCESS, nullptr);
|
||||
#else
|
||||
auto handle = should_chmod ? ::open(source_path.c_str(),
|
||||
O_CREAT | O_RDWR | O_CLOEXEC, 0600u)
|
||||
: ::open(source_path.c_str(), O_RDWR | O_CLOEXEC);
|
||||
if (should_chmod) {
|
||||
chmod(source_path.c_str(), 0600u);
|
||||
}
|
||||
#endif
|
||||
nf = native_file::attach(handle);
|
||||
return ((handle == REPERTORY_INVALID_HANDLE) ? api_error::os_error
|
||||
: api_error::success);
|
||||
}
|
||||
|
||||
auto native_file::create_or_open(const std::string &source_path,
|
||||
native_file_ptr &nf) -> api_error {
|
||||
return create_or_open(source_path, true, nf);
|
||||
}
|
||||
|
||||
auto native_file::open(const std::string &source_path, native_file_ptr &nf)
|
||||
-> api_error {
|
||||
return open(source_path, true, nf);
|
||||
}
|
||||
|
||||
auto native_file::open(const std::string &source_path,
|
||||
[[maybe_unused]] bool should_chmod, native_file_ptr &nf)
|
||||
-> api_error {
|
||||
#ifdef _WIN32
|
||||
auto handle = ::CreateFileA(source_path.c_str(), GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr,
|
||||
OPEN_EXISTING, FILE_FLAG_RANDOM_ACCESS, nullptr);
|
||||
#else
|
||||
auto handle = should_chmod
|
||||
? ::open(source_path.c_str(), O_RDWR | O_CLOEXEC, 0600u)
|
||||
: ::open(source_path.c_str(), O_RDONLY | O_CLOEXEC);
|
||||
if (should_chmod) {
|
||||
chmod(source_path.c_str(), 0600u);
|
||||
}
|
||||
#endif
|
||||
nf = native_file::attach(handle);
|
||||
return ((handle == REPERTORY_INVALID_HANDLE) ? api_error::os_error
|
||||
: api_error::success);
|
||||
}
|
||||
|
||||
auto native_file::allocate(std::uint64_t file_size) -> bool {
|
||||
#ifdef _WIN32
|
||||
LARGE_INTEGER li{};
|
||||
li.QuadPart = file_size;
|
||||
return (::SetFilePointerEx(handle_, li, nullptr, FILE_BEGIN) &&
|
||||
::SetEndOfFile(handle_));
|
||||
#endif
|
||||
#ifdef __linux__
|
||||
return (fallocate(handle_, 0, 0, static_cast<off_t>(file_size)) >= 0);
|
||||
#endif
|
||||
#ifdef __APPLE__
|
||||
return (ftruncate(handle_, file_size) >= 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
void native_file::close() {
|
||||
if (handle_ != REPERTORY_INVALID_HANDLE) {
|
||||
#ifdef WIN32
|
||||
::CloseHandle(handle_);
|
||||
#else
|
||||
::close(handle_);
|
||||
#endif
|
||||
handle_ = REPERTORY_INVALID_HANDLE;
|
||||
}
|
||||
}
|
||||
|
||||
auto native_file::copy_from(const native_file_ptr &nf) -> bool {
|
||||
auto ret = false;
|
||||
std::uint64_t file_size = 0u;
|
||||
if ((ret = nf->get_file_size(file_size))) {
|
||||
data_buffer buffer;
|
||||
buffer.resize(65536u * 2u);
|
||||
std::uint64_t offset = 0u;
|
||||
while (ret && (file_size > 0u)) {
|
||||
std::size_t bytes_read{};
|
||||
if ((ret = nf->read_bytes(&buffer[0u], buffer.size(), offset,
|
||||
bytes_read))) {
|
||||
std::size_t bytes_written = 0u;
|
||||
ret = write_bytes(&buffer[0u], bytes_read, offset, bytes_written);
|
||||
file_size -= bytes_read;
|
||||
offset += bytes_read;
|
||||
}
|
||||
}
|
||||
flush();
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
auto native_file::copy_from(const std::string &path) -> bool {
|
||||
auto ret = false;
|
||||
native_file_ptr nf;
|
||||
if (native_file::create_or_open(path, nf) == api_error ::success) {
|
||||
ret = copy_from(nf);
|
||||
nf->close();
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void native_file::flush() {
|
||||
#ifdef _WIN32
|
||||
recur_mutex_lock l(read_write_mutex_);
|
||||
::FlushFileBuffers(handle_);
|
||||
#else
|
||||
fsync(handle_);
|
||||
#endif
|
||||
}
|
||||
|
||||
auto native_file::get_file_size(std::uint64_t &file_size) -> bool {
|
||||
auto ret = false;
|
||||
#ifdef _WIN32
|
||||
LARGE_INTEGER li{};
|
||||
if ((ret = ::GetFileSizeEx(handle_, &li) && (li.QuadPart >= 0))) {
|
||||
file_size = static_cast<std::uint64_t>(li.QuadPart);
|
||||
}
|
||||
#else
|
||||
#if __APPLE__
|
||||
struct stat st {};
|
||||
if (fstat(handle_, &st) >= 0) {
|
||||
#else
|
||||
struct stat64 st {};
|
||||
if (fstat64(handle_, &st) >= 0) {
|
||||
#endif
|
||||
if ((ret = (st.st_size >= 0))) {
|
||||
file_size = static_cast<uint64_t>(st.st_size);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
auto native_file::read_bytes(char *buffer, std::size_t read_size,
|
||||
std::uint64_t read_offset, std::size_t &bytes_read)
|
||||
-> bool {
|
||||
recur_mutex_lock l(read_write_mutex_);
|
||||
|
||||
auto ret = false;
|
||||
bytes_read = 0u;
|
||||
LARGE_INTEGER li{};
|
||||
li.QuadPart = read_offset;
|
||||
if ((ret = !!::SetFilePointerEx(handle_, li, nullptr, FILE_BEGIN))) {
|
||||
DWORD current_read = 0u;
|
||||
do {
|
||||
current_read = 0u;
|
||||
ret = !!::ReadFile(handle_, &buffer[bytes_read],
|
||||
static_cast<DWORD>(read_size - bytes_read),
|
||||
¤t_read, nullptr);
|
||||
bytes_read += current_read;
|
||||
} while (ret && (bytes_read < read_size) && (current_read != 0));
|
||||
}
|
||||
|
||||
if (ret && (read_size != bytes_read)) {
|
||||
::SetLastError(ERROR_HANDLE_EOF);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
#else
|
||||
auto native_file::read_bytes(char *buffer, std::size_t read_size,
|
||||
std::uint64_t read_offset, std::size_t &bytes_read)
|
||||
-> bool {
|
||||
bytes_read = 0U;
|
||||
ssize_t result = 0;
|
||||
do {
|
||||
result = pread64(handle_, &buffer[bytes_read], read_size - bytes_read,
|
||||
static_cast<off_t>(read_offset + bytes_read));
|
||||
if (result > 0) {
|
||||
bytes_read += static_cast<size_t>(result);
|
||||
}
|
||||
} while ((result > 0) && (bytes_read < read_size));
|
||||
|
||||
return (result >= 0);
|
||||
}
|
||||
#endif
|
||||
auto native_file::truncate(std::uint64_t file_size) -> bool {
|
||||
#ifdef _WIN32
|
||||
recur_mutex_lock l(read_write_mutex_);
|
||||
LARGE_INTEGER li{};
|
||||
li.QuadPart = file_size;
|
||||
return (::SetFilePointerEx(handle_, li, nullptr, FILE_BEGIN) &&
|
||||
::SetEndOfFile(handle_));
|
||||
#else
|
||||
return (ftruncate(handle_, static_cast<off_t>(file_size)) >= 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
auto native_file::write_bytes(const char *buffer, std::size_t write_size,
|
||||
std::uint64_t write_offset,
|
||||
std::size_t &bytes_written) -> bool {
|
||||
recur_mutex_lock l(read_write_mutex_);
|
||||
|
||||
bytes_written = 0u;
|
||||
auto ret = true;
|
||||
|
||||
LARGE_INTEGER li{};
|
||||
li.QuadPart = write_offset;
|
||||
if ((ret = !!::SetFilePointerEx(handle_, li, nullptr, FILE_BEGIN))) {
|
||||
do {
|
||||
DWORD current_write = 0u;
|
||||
ret = !!::WriteFile(handle_, &buffer[bytes_written],
|
||||
static_cast<DWORD>(write_size - bytes_written),
|
||||
¤t_write, nullptr);
|
||||
bytes_written += current_write;
|
||||
} while (ret && (bytes_written < write_size));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
#else
|
||||
auto native_file::write_bytes(const char *buffer, std::size_t write_size,
|
||||
std::uint64_t write_offset,
|
||||
std::size_t &bytes_written) -> bool {
|
||||
bytes_written = 0U;
|
||||
ssize_t result = 0U;
|
||||
do {
|
||||
result =
|
||||
pwrite64(handle_, &buffer[bytes_written], write_size - bytes_written,
|
||||
static_cast<off_t>(write_offset + bytes_written));
|
||||
if (result > 0) {
|
||||
bytes_written += static_cast<size_t>(result);
|
||||
}
|
||||
} while ((result >= 0) && (bytes_written < write_size));
|
||||
|
||||
return (bytes_written == write_size);
|
||||
}
|
||||
#endif
|
||||
} // namespace repertory
|
||||
|
@ -1,202 +1,202 @@
|
||||
/*
|
||||
Copyright <2018-2023> <scott.e.graves@protonmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#include "utils/path_utils.hpp"
|
||||
|
||||
#include "types/repertory.hpp"
|
||||
#include "utils/error_utils.hpp"
|
||||
#include "utils/string_utils.hpp"
|
||||
#include "utils/utils.hpp"
|
||||
|
||||
namespace repertory::utils::path {
|
||||
auto absolute(std::string path) -> std::string {
|
||||
#ifdef _WIN32
|
||||
if (not path.empty() && ::PathIsRelative(&path[0u])) {
|
||||
std::string temp;
|
||||
temp.resize(MAX_PATH + 1);
|
||||
path = _fullpath(&temp[0u], &path[0u], MAX_PATH);
|
||||
}
|
||||
#else
|
||||
if (not path.empty() && (path[0u] != '/')) {
|
||||
auto found = false;
|
||||
auto tmp = path;
|
||||
do {
|
||||
auto *res = realpath(&tmp[0u], nullptr);
|
||||
if (res) {
|
||||
path = combine(res, {path.substr(tmp.size())});
|
||||
free(res);
|
||||
found = true;
|
||||
} else if (tmp == ".") {
|
||||
found = true;
|
||||
} else {
|
||||
tmp = dirname(&tmp[0u]);
|
||||
}
|
||||
} while (not found);
|
||||
}
|
||||
#endif
|
||||
|
||||
return finalize(path);
|
||||
}
|
||||
|
||||
auto combine(std::string path, const std::vector<std::string> &paths)
|
||||
-> std::string {
|
||||
return finalize(
|
||||
std::accumulate(paths.begin(), paths.end(), path,
|
||||
[](std::string next_path, const auto &path_part) {
|
||||
if (not next_path.empty()) {
|
||||
next_path += (directory_seperator + path_part);
|
||||
}
|
||||
return next_path;
|
||||
}));
|
||||
}
|
||||
|
||||
auto create_api_path(std::string path) -> std::string {
|
||||
if (path.empty() || (path == ".") || (path == "/")) {
|
||||
return "/";
|
||||
}
|
||||
|
||||
format_path(path, "/", "\\");
|
||||
if (path.find("./") == 0) {
|
||||
path = path.substr(1);
|
||||
}
|
||||
|
||||
if (path[0u] != '/') {
|
||||
path = "/" + path;
|
||||
}
|
||||
|
||||
if ((path != "/") && utils::string::ends_with(path, "/")) {
|
||||
utils::string::right_trim(path, '/');
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
auto finalize(std::string path) -> std::string {
|
||||
format_path(path, not_directory_seperator, directory_seperator);
|
||||
format_path(path, directory_seperator, not_directory_seperator);
|
||||
if ((path.size() > 1u) &&
|
||||
(path[path.size() - 1u] == directory_seperator[0u])) {
|
||||
path = path.substr(0u, path.size() - 1u);
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
if ((path.size() >= 2u) && (path[1u] == ':')) {
|
||||
path[0u] = utils::string::to_lower(std::string(1u, path[0u]))[0u];
|
||||
}
|
||||
#endif
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
auto format_path(std::string &path, const std::string &sep,
|
||||
const std::string ¬_sep) -> std::string & {
|
||||
std::replace(path.begin(), path.end(), not_sep[0u], sep[0u]);
|
||||
|
||||
while (utils::string::contains(path, sep + sep)) {
|
||||
utils::string::replace(path, sep + sep, sep);
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
auto get_parent_api_path(const std::string &path) -> std::string {
|
||||
std::string ret;
|
||||
if (path != "/") {
|
||||
ret = path.substr(0, path.rfind('/') + 1);
|
||||
if (ret != "/") {
|
||||
ret = utils::string::right_trim(ret, '/');
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifndef _WIN32
|
||||
auto get_parent_directory(std::string path) -> std::string {
|
||||
auto ret = std::string(dirname(&path[0u]));
|
||||
if (ret == ".") {
|
||||
ret = "/";
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
auto is_ads_file_path([[maybe_unused]] const std::string &path) -> bool {
|
||||
#ifdef _WIN32
|
||||
return utils::string::contains(path, ":");
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
auto is_trash_directory(std::string path) -> bool {
|
||||
path = create_api_path(utils::string::to_lower(path));
|
||||
if (utils::string::begins_with(path, "/.trash-") ||
|
||||
utils::string::begins_with(path, "/.trashes") ||
|
||||
utils::string::begins_with(path, "/$recycle.bin")) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
auto remove_file_name(std::string path) -> std::string {
|
||||
path = finalize(path);
|
||||
|
||||
#ifdef _WIN32
|
||||
::PathRemoveFileSpec(&path[0u]);
|
||||
path = path.c_str();
|
||||
#else
|
||||
if (path != "/") {
|
||||
auto i = path.size() - 1;
|
||||
while ((i != 0) && (path[i] != '/')) {
|
||||
i--;
|
||||
}
|
||||
|
||||
path = (i > 0) ? finalize(path.substr(0, i)) : "/";
|
||||
}
|
||||
#endif
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
#ifndef _WIN32
|
||||
auto resolve(std::string path) -> std::string {
|
||||
std::string home{};
|
||||
use_getpwuid(getuid(), [&home](struct passwd *pw) {
|
||||
home = (pw->pw_dir ? pw->pw_dir : "");
|
||||
if (home.empty() || ((home == "/") && (getuid() != 0))) {
|
||||
home = combine("/home", {pw->pw_name});
|
||||
}
|
||||
});
|
||||
|
||||
return finalize(utils::string::replace(path, "~", home));
|
||||
}
|
||||
#endif
|
||||
|
||||
auto strip_to_file_name(std::string path) -> std::string {
|
||||
#ifdef _WIN32
|
||||
return ::PathFindFileName(&path[0u]);
|
||||
#else
|
||||
return utils::string::contains(path, "/") ? basename(&path[0u]) : path;
|
||||
#endif
|
||||
}
|
||||
} // namespace repertory::utils::path
|
||||
/*
|
||||
Copyright <2018-2023> <scott.e.graves@protonmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#include "utils/path_utils.hpp"
|
||||
|
||||
#include "types/repertory.hpp"
|
||||
#include "utils/error_utils.hpp"
|
||||
#include "utils/string_utils.hpp"
|
||||
#include "utils/utils.hpp"
|
||||
|
||||
namespace repertory::utils::path {
|
||||
auto absolute(std::string path) -> std::string {
|
||||
#ifdef _WIN32
|
||||
if (not path.empty() && ::PathIsRelative(&path[0u])) {
|
||||
std::string temp;
|
||||
temp.resize(MAX_PATH + 1);
|
||||
path = _fullpath(&temp[0u], &path[0u], MAX_PATH);
|
||||
}
|
||||
#else
|
||||
if (not path.empty() && (path[0u] != '/')) {
|
||||
auto found = false;
|
||||
auto tmp = path;
|
||||
do {
|
||||
auto *res = realpath(&tmp[0u], nullptr);
|
||||
if (res) {
|
||||
path = combine(res, {path.substr(tmp.size())});
|
||||
free(res);
|
||||
found = true;
|
||||
} else if (tmp == ".") {
|
||||
found = true;
|
||||
} else {
|
||||
tmp = dirname(&tmp[0u]);
|
||||
}
|
||||
} while (not found);
|
||||
}
|
||||
#endif
|
||||
|
||||
return finalize(path);
|
||||
}
|
||||
|
||||
auto combine(std::string path, const std::vector<std::string> &paths)
|
||||
-> std::string {
|
||||
return finalize(
|
||||
std::accumulate(paths.begin(), paths.end(), path,
|
||||
[](std::string next_path, const auto &path_part) {
|
||||
if (next_path.empty()) {
|
||||
return path_part;
|
||||
}
|
||||
return next_path + (directory_seperator + path_part);
|
||||
}));
|
||||
}
|
||||
|
||||
auto create_api_path(std::string path) -> std::string {
|
||||
if (path.empty() || (path == ".") || (path == "/")) {
|
||||
return "/";
|
||||
}
|
||||
|
||||
format_path(path, "/", "\\");
|
||||
if (path.find("./") == 0) {
|
||||
path = path.substr(1);
|
||||
}
|
||||
|
||||
if (path[0u] != '/') {
|
||||
path = "/" + path;
|
||||
}
|
||||
|
||||
if ((path != "/") && utils::string::ends_with(path, "/")) {
|
||||
utils::string::right_trim(path, '/');
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
auto finalize(std::string path) -> std::string {
|
||||
format_path(path, not_directory_seperator, directory_seperator);
|
||||
format_path(path, directory_seperator, not_directory_seperator);
|
||||
if ((path.size() > 1u) &&
|
||||
(path[path.size() - 1u] == directory_seperator[0u])) {
|
||||
path = path.substr(0u, path.size() - 1u);
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
if ((path.size() >= 2u) && (path[1u] == ':')) {
|
||||
path[0u] = utils::string::to_lower(std::string(1u, path[0u]))[0u];
|
||||
}
|
||||
#endif
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
auto format_path(std::string &path, const std::string &sep,
|
||||
const std::string ¬_sep) -> std::string & {
|
||||
std::replace(path.begin(), path.end(), not_sep[0u], sep[0u]);
|
||||
|
||||
while (utils::string::contains(path, sep + sep)) {
|
||||
utils::string::replace(path, sep + sep, sep);
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
auto get_parent_api_path(const std::string &path) -> std::string {
|
||||
std::string ret;
|
||||
if (path != "/") {
|
||||
ret = path.substr(0, path.rfind('/') + 1);
|
||||
if (ret != "/") {
|
||||
ret = utils::string::right_trim(ret, '/');
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifndef _WIN32
|
||||
auto get_parent_directory(std::string path) -> std::string {
|
||||
auto ret = std::string(dirname(&path[0u]));
|
||||
if (ret == ".") {
|
||||
ret = "/";
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
auto is_ads_file_path([[maybe_unused]] const std::string &path) -> bool {
|
||||
#ifdef _WIN32
|
||||
return utils::string::contains(path, ":");
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
auto is_trash_directory(std::string path) -> bool {
|
||||
path = create_api_path(utils::string::to_lower(path));
|
||||
if (utils::string::begins_with(path, "/.trash-") ||
|
||||
utils::string::begins_with(path, "/.trashes") ||
|
||||
utils::string::begins_with(path, "/$recycle.bin")) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
auto remove_file_name(std::string path) -> std::string {
|
||||
path = finalize(path);
|
||||
|
||||
#ifdef _WIN32
|
||||
::PathRemoveFileSpec(&path[0u]);
|
||||
path = path.c_str();
|
||||
#else
|
||||
if (path != "/") {
|
||||
auto i = path.size() - 1;
|
||||
while ((i != 0) && (path[i] != '/')) {
|
||||
i--;
|
||||
}
|
||||
|
||||
path = (i > 0) ? finalize(path.substr(0, i)) : "/";
|
||||
}
|
||||
#endif
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
#ifndef _WIN32
|
||||
auto resolve(std::string path) -> std::string {
|
||||
std::string home{};
|
||||
use_getpwuid(getuid(), [&home](struct passwd *pw) {
|
||||
home = (pw->pw_dir ? pw->pw_dir : "");
|
||||
if (home.empty() || ((home == "/") && (getuid() != 0))) {
|
||||
home = combine("/home", {pw->pw_name});
|
||||
}
|
||||
});
|
||||
|
||||
return finalize(utils::string::replace(path, "~", home));
|
||||
}
|
||||
#endif
|
||||
|
||||
auto strip_to_file_name(std::string path) -> std::string {
|
||||
#ifdef _WIN32
|
||||
return ::PathFindFileName(&path[0u]);
|
||||
#else
|
||||
return utils::string::contains(path, "/") ? basename(&path[0u]) : path;
|
||||
#endif
|
||||
}
|
||||
} // namespace repertory::utils::path
|
||||
|
@ -1,121 +1,121 @@
|
||||
/*
|
||||
Copyright <2018-2023> <scott.e.graves@protonmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#include "utils/polling.hpp"
|
||||
|
||||
#include "app_config.hpp"
|
||||
|
||||
namespace repertory {
|
||||
polling polling::instance_;
|
||||
|
||||
void polling::frequency_thread(
|
||||
std::function<std::uint32_t()> get_frequency_seconds, frequency freq) {
|
||||
while (not stop_requested_) {
|
||||
std::deque<std::future<void>> futures;
|
||||
unique_mutex_lock l(mutex_);
|
||||
if (not stop_requested_ &&
|
||||
notify_.wait_for(l, std::chrono::seconds(get_frequency_seconds())) ==
|
||||
std::cv_status::timeout) {
|
||||
for (const auto &kv : items_) {
|
||||
if (kv.second.freq == freq) {
|
||||
futures.emplace_back(
|
||||
std::async(std::launch::async, [this, &freq, kv]() -> void {
|
||||
if (config_->get_event_level() == event_level::verbose ||
|
||||
freq != frequency::second) {
|
||||
event_system::instance().raise<polling_item_begin>(kv.first);
|
||||
}
|
||||
kv.second.action();
|
||||
if (config_->get_event_level() == event_level::verbose ||
|
||||
freq != frequency::second) {
|
||||
event_system::instance().raise<polling_item_end>(kv.first);
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
l.unlock();
|
||||
|
||||
while (not futures.empty()) {
|
||||
futures.front().wait();
|
||||
futures.pop_front();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void polling::remove_callback(const std::string &name) {
|
||||
mutex_lock l(mutex_);
|
||||
items_.erase(name);
|
||||
}
|
||||
|
||||
void polling::set_callback(const polling_item &pi) {
|
||||
mutex_lock l(mutex_);
|
||||
items_[pi.name] = pi;
|
||||
}
|
||||
|
||||
void polling::start(app_config *config) {
|
||||
mutex_lock l(start_stop_mutex_);
|
||||
if (not high_frequency_thread_) {
|
||||
event_system::instance().raise<service_started>("polling");
|
||||
config_ = config;
|
||||
stop_requested_ = false;
|
||||
high_frequency_thread_ = std::make_unique<std::thread>([this]() -> void {
|
||||
this->frequency_thread(
|
||||
[this]() -> std::uint32_t {
|
||||
return config_->get_high_frequency_interval_secs();
|
||||
},
|
||||
frequency::high);
|
||||
});
|
||||
low_frequency_thread_ = std::make_unique<std::thread>([this]() -> void {
|
||||
this->frequency_thread(
|
||||
[this]() -> std::uint32_t {
|
||||
return config_->get_low_frequency_interval_secs();
|
||||
},
|
||||
frequency::low);
|
||||
});
|
||||
second_frequency_thread_ = std::make_unique<std::thread>([this]() -> void {
|
||||
this->frequency_thread([]() -> std::uint32_t { return 1U; },
|
||||
frequency::second);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void polling::stop() {
|
||||
if (high_frequency_thread_) {
|
||||
event_system::instance().raise<service_shutdown_begin>("polling");
|
||||
mutex_lock l(start_stop_mutex_);
|
||||
if (high_frequency_thread_) {
|
||||
{
|
||||
stop_requested_ = true;
|
||||
|
||||
mutex_lock l2(mutex_);
|
||||
notify_.notify_all();
|
||||
}
|
||||
high_frequency_thread_->join();
|
||||
low_frequency_thread_->join();
|
||||
second_frequency_thread_->join();
|
||||
high_frequency_thread_.reset();
|
||||
low_frequency_thread_.reset();
|
||||
second_frequency_thread_.reset();
|
||||
}
|
||||
event_system::instance().raise<service_shutdown_end>("polling");
|
||||
}
|
||||
}
|
||||
} // namespace repertory
|
||||
/*
|
||||
Copyright <2018-2023> <scott.e.graves@protonmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#include "utils/polling.hpp"
|
||||
|
||||
#include "app_config.hpp"
|
||||
|
||||
namespace repertory {
|
||||
polling polling::instance_;
|
||||
|
||||
void polling::frequency_thread(
|
||||
std::function<std::uint32_t()> get_frequency_seconds, frequency freq) {
|
||||
while (not stop_requested_) {
|
||||
std::deque<std::future<void>> futures;
|
||||
unique_mutex_lock l(mutex_);
|
||||
if (not stop_requested_ &&
|
||||
notify_.wait_for(l, std::chrono::seconds(get_frequency_seconds())) ==
|
||||
std::cv_status::timeout) {
|
||||
for (const auto &kv : items_) {
|
||||
if (kv.second.freq == freq) {
|
||||
futures.emplace_back(
|
||||
std::async(std::launch::async, [this, &freq, kv]() -> void {
|
||||
if (config_->get_event_level() == event_level::verbose ||
|
||||
freq != frequency::second) {
|
||||
event_system::instance().raise<polling_item_begin>(kv.first);
|
||||
}
|
||||
kv.second.action();
|
||||
if (config_->get_event_level() == event_level::verbose ||
|
||||
freq != frequency::second) {
|
||||
event_system::instance().raise<polling_item_end>(kv.first);
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
l.unlock();
|
||||
|
||||
while (not futures.empty()) {
|
||||
futures.front().wait();
|
||||
futures.pop_front();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void polling::remove_callback(const std::string &name) {
|
||||
mutex_lock l(mutex_);
|
||||
items_.erase(name);
|
||||
}
|
||||
|
||||
void polling::set_callback(const polling_item &pi) {
|
||||
mutex_lock l(mutex_);
|
||||
items_[pi.name] = pi;
|
||||
}
|
||||
|
||||
void polling::start(app_config *config) {
|
||||
mutex_lock l(start_stop_mutex_);
|
||||
if (not high_frequency_thread_) {
|
||||
event_system::instance().raise<service_started>("polling");
|
||||
config_ = config;
|
||||
stop_requested_ = false;
|
||||
high_frequency_thread_ = std::make_unique<std::thread>([this]() -> void {
|
||||
this->frequency_thread(
|
||||
[this]() -> std::uint32_t {
|
||||
return config_->get_high_frequency_interval_secs();
|
||||
},
|
||||
frequency::high);
|
||||
});
|
||||
low_frequency_thread_ = std::make_unique<std::thread>([this]() -> void {
|
||||
this->frequency_thread(
|
||||
[this]() -> std::uint32_t {
|
||||
return config_->get_low_frequency_interval_secs();
|
||||
},
|
||||
frequency::low);
|
||||
});
|
||||
second_frequency_thread_ = std::make_unique<std::thread>([this]() -> void {
|
||||
this->frequency_thread([]() -> std::uint32_t { return 1U; },
|
||||
frequency::second);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void polling::stop() {
|
||||
if (high_frequency_thread_) {
|
||||
event_system::instance().raise<service_shutdown_begin>("polling");
|
||||
mutex_lock l(start_stop_mutex_);
|
||||
if (high_frequency_thread_) {
|
||||
{
|
||||
stop_requested_ = true;
|
||||
|
||||
mutex_lock l2(mutex_);
|
||||
notify_.notify_all();
|
||||
}
|
||||
high_frequency_thread_->join();
|
||||
low_frequency_thread_->join();
|
||||
second_frequency_thread_->join();
|
||||
high_frequency_thread_.reset();
|
||||
low_frequency_thread_.reset();
|
||||
second_frequency_thread_.reset();
|
||||
}
|
||||
event_system::instance().raise<service_shutdown_end>("polling");
|
||||
}
|
||||
}
|
||||
} // namespace repertory
|
||||
|
@ -1,72 +0,0 @@
|
||||
/*
|
||||
Copyright <2018-2023> <scott.e.graves@protonmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#include "utils/rocksdb_utils.hpp"
|
||||
|
||||
#include "app_config.hpp"
|
||||
#include "events/event_system.hpp"
|
||||
#include "types/startup_exception.hpp"
|
||||
#include "utils/error_utils.hpp"
|
||||
#include "utils/path_utils.hpp"
|
||||
|
||||
namespace repertory::utils::db {
|
||||
void create_rocksdb(const app_config &config, const std::string &name,
|
||||
std::unique_ptr<rocksdb::DB> &db) {
|
||||
rocksdb::Options options{};
|
||||
options.create_if_missing = true;
|
||||
options.db_log_dir = config.get_log_directory();
|
||||
options.keep_log_file_num = 10;
|
||||
|
||||
rocksdb::DB *db_ptr{};
|
||||
const auto status = rocksdb::DB::Open(
|
||||
options, utils::path::combine(config.get_data_directory(), {name}),
|
||||
&db_ptr);
|
||||
if (status.ok()) {
|
||||
db.reset(db_ptr);
|
||||
} else {
|
||||
utils::error::raise_error(__FUNCTION__, status.ToString());
|
||||
throw startup_exception(status.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
void create_rocksdb(
|
||||
const app_config &config, const std::string &name,
|
||||
const std::vector<rocksdb::ColumnFamilyDescriptor> &families,
|
||||
std::vector<rocksdb::ColumnFamilyHandle *> &handles,
|
||||
std::unique_ptr<rocksdb::DB> &db) {
|
||||
rocksdb::Options options{};
|
||||
options.create_if_missing = true;
|
||||
options.create_missing_column_families = true;
|
||||
options.db_log_dir = config.get_log_directory();
|
||||
options.keep_log_file_num = 10;
|
||||
|
||||
rocksdb::DB *db_ptr{};
|
||||
const auto status = rocksdb::DB::Open(
|
||||
options, utils::path::combine(config.get_data_directory(), {name}),
|
||||
families, &handles, &db_ptr);
|
||||
if (status.ok()) {
|
||||
db.reset(db_ptr);
|
||||
} else {
|
||||
utils::error::raise_error(__FUNCTION__, status.ToString());
|
||||
throw startup_exception(status.ToString());
|
||||
}
|
||||
}
|
||||
} // namespace repertory::utils::db
|
@ -1,65 +1,65 @@
|
||||
/*
|
||||
Copyright <2018-2023> <scott.e.graves@protonmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#include "utils/single_thread_service_base.hpp"
|
||||
|
||||
#include "events/event_system.hpp"
|
||||
#include "events/events.hpp"
|
||||
#include "types/repertory.hpp"
|
||||
|
||||
namespace repertory {
|
||||
void single_thread_service_base::notify_all() const {
|
||||
mutex_lock lock(get_mutex());
|
||||
notify_.notify_all();
|
||||
}
|
||||
|
||||
void single_thread_service_base::start() {
|
||||
mutex_lock lock(mtx_);
|
||||
if (not thread_) {
|
||||
stop_requested_ = false;
|
||||
on_start();
|
||||
thread_ = std::make_unique<std::thread>([this]() {
|
||||
event_system::instance().raise<service_started>(service_name_);
|
||||
while (not stop_requested_) {
|
||||
service_function();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void single_thread_service_base::stop() {
|
||||
if (thread_) {
|
||||
event_system::instance().raise<service_shutdown_begin>(service_name_);
|
||||
unique_mutex_lock lock(mtx_);
|
||||
if (thread_) {
|
||||
stop_requested_ = true;
|
||||
notify_.notify_all();
|
||||
lock.unlock();
|
||||
|
||||
thread_->join();
|
||||
thread_.reset();
|
||||
|
||||
on_stop();
|
||||
}
|
||||
event_system::instance().raise<service_shutdown_end>(service_name_);
|
||||
}
|
||||
}
|
||||
} // namespace repertory
|
||||
/*
|
||||
Copyright <2018-2023> <scott.e.graves@protonmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#include "utils/single_thread_service_base.hpp"
|
||||
|
||||
#include "events/event_system.hpp"
|
||||
#include "events/events.hpp"
|
||||
#include "types/repertory.hpp"
|
||||
|
||||
namespace repertory {
|
||||
void single_thread_service_base::notify_all() const {
|
||||
mutex_lock lock(get_mutex());
|
||||
notify_.notify_all();
|
||||
}
|
||||
|
||||
void single_thread_service_base::start() {
|
||||
mutex_lock lock(mtx_);
|
||||
if (not thread_) {
|
||||
stop_requested_ = false;
|
||||
on_start();
|
||||
thread_ = std::make_unique<std::thread>([this]() {
|
||||
event_system::instance().raise<service_started>(service_name_);
|
||||
while (not stop_requested_) {
|
||||
service_function();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void single_thread_service_base::stop() {
|
||||
if (thread_) {
|
||||
event_system::instance().raise<service_shutdown_begin>(service_name_);
|
||||
unique_mutex_lock lock(mtx_);
|
||||
if (thread_) {
|
||||
stop_requested_ = true;
|
||||
notify_.notify_all();
|
||||
lock.unlock();
|
||||
|
||||
thread_->join();
|
||||
thread_.reset();
|
||||
|
||||
on_stop();
|
||||
}
|
||||
event_system::instance().raise<service_shutdown_end>(service_name_);
|
||||
}
|
||||
}
|
||||
} // namespace repertory
|
||||
|
@ -1,223 +1,223 @@
|
||||
/*
|
||||
Copyright <2018-2023> <scott.e.graves@protonmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#include "utils/string_utils.hpp"
|
||||
|
||||
namespace repertory::utils::string {
|
||||
/* constexpr c++20 */ auto ends_with(std::string_view str, std::string_view val)
|
||||
-> bool {
|
||||
if (val.size() > str.size()) {
|
||||
return false;
|
||||
}
|
||||
return std::equal(val.rbegin(), val.rend(), str.rbegin());
|
||||
}
|
||||
|
||||
auto from_bool(bool val) -> std::string { return std::to_string(val); }
|
||||
|
||||
auto from_dynamic_bitset(const boost::dynamic_bitset<> &bitset) -> std::string {
|
||||
std::stringstream ss;
|
||||
boost::archive::text_oarchive archive(ss);
|
||||
archive << bitset;
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
auto from_utf8(const std::string &str) -> std::wstring {
|
||||
return str.empty()
|
||||
? L""
|
||||
: std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t>()
|
||||
.from_bytes(str);
|
||||
}
|
||||
|
||||
/* constexpr c++20 */ auto is_numeric(std::string_view s) -> bool {
|
||||
if ((s.length() > 1u) && (s[0u] == '+' || s[0u] == '-')) {
|
||||
s = s.substr(1u);
|
||||
}
|
||||
|
||||
if (s.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto has_decimal = false;
|
||||
return std::find_if(
|
||||
s.begin(), s.end(),
|
||||
[&has_decimal](const std::string_view::value_type &c) -> bool {
|
||||
if (has_decimal && c == '.') {
|
||||
return true;
|
||||
}
|
||||
if ((has_decimal = has_decimal || c == '.')) {
|
||||
return false;
|
||||
}
|
||||
return not std::isdigit(c);
|
||||
}) == s.end();
|
||||
}
|
||||
|
||||
auto join(const std::vector<std::string> &arr, const char &delim)
|
||||
-> std::string {
|
||||
if (arr.empty()) {
|
||||
return "";
|
||||
}
|
||||
|
||||
return std::accumulate(
|
||||
std::next(arr.begin()), arr.end(), arr[0u],
|
||||
[&delim](auto s, const auto &v) { return s + delim + v; });
|
||||
}
|
||||
|
||||
auto left_trim(std::string &s) -> std::string & { return left_trim(s, ' '); }
|
||||
|
||||
auto left_trim(std::string &s, const char &c) -> std::string & {
|
||||
s.erase(0, s.find_first_not_of(c));
|
||||
return s;
|
||||
}
|
||||
|
||||
auto replace(std::string &src, const char &character, const char &with)
|
||||
-> std::string & {
|
||||
std::replace(src.begin(), src.end(), character, with);
|
||||
return src;
|
||||
}
|
||||
|
||||
auto replace(std::string &src, const std::string &find, const std::string &with,
|
||||
size_t start_pos) -> std::string & {
|
||||
if (!src.empty() && (start_pos < src.size())) {
|
||||
while ((start_pos = src.find(find, start_pos)) != std::string::npos) {
|
||||
src.replace(start_pos, find.size(), with);
|
||||
start_pos += with.size();
|
||||
}
|
||||
}
|
||||
return src;
|
||||
}
|
||||
|
||||
auto replace_copy(std::string src, const char &character, const char &with)
|
||||
-> std::string {
|
||||
std::replace(src.begin(), src.end(), character, with);
|
||||
return src;
|
||||
}
|
||||
|
||||
auto replace_copy(std::string src, const std::string &find,
|
||||
const std::string &with, size_t start_pos) -> std::string {
|
||||
return replace(src, find, with, start_pos);
|
||||
}
|
||||
|
||||
auto right_trim(std::string &s) -> std::string & { return right_trim(s, ' '); }
|
||||
|
||||
auto right_trim(std::string &s, const char &c) -> std::string & {
|
||||
s.erase(s.find_last_not_of(c) + 1);
|
||||
return s;
|
||||
}
|
||||
|
||||
auto split(const std::string &str, const char &delim, bool should_trim)
|
||||
-> std::vector<std::string> {
|
||||
std::vector<std::string> ret;
|
||||
std::stringstream ss(str);
|
||||
std::string item;
|
||||
while (std::getline(ss, item, delim)) {
|
||||
ret.push_back(should_trim ? trim(item) : item);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
auto to_bool(std::string val) -> bool {
|
||||
auto b = false;
|
||||
|
||||
trim(val);
|
||||
if (is_numeric(val)) {
|
||||
if (contains(val, ".")) {
|
||||
b = (to_double(val) != 0.0);
|
||||
} else {
|
||||
std::istringstream(val) >> b;
|
||||
}
|
||||
} else {
|
||||
std::istringstream(to_lower(val)) >> std::boolalpha >> b;
|
||||
}
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
auto to_double(const std::string &str) -> double { return std::stod(str); }
|
||||
|
||||
auto to_dynamic_bitset(const std::string &val) -> boost::dynamic_bitset<> {
|
||||
std::stringstream ss(val);
|
||||
boost::dynamic_bitset<> bitset;
|
||||
boost::archive::text_iarchive archive(ss);
|
||||
archive >> bitset;
|
||||
return bitset;
|
||||
}
|
||||
|
||||
auto to_int32(const std::string &val) -> std::int32_t { return std::stoi(val); }
|
||||
|
||||
auto to_int64(const std::string &val) -> std::int64_t {
|
||||
return std::stoll(val);
|
||||
}
|
||||
|
||||
auto to_lower(std::string str) -> std::string {
|
||||
std::transform(str.begin(), str.end(), str.begin(), ::tolower);
|
||||
return str;
|
||||
}
|
||||
|
||||
auto to_size_t(const std::string &val) -> std::size_t {
|
||||
return static_cast<std::size_t>(std::stoull(val));
|
||||
}
|
||||
|
||||
auto to_uint8(const std::string &val) -> std::uint8_t {
|
||||
return static_cast<std::uint8_t>(std::stoul(val));
|
||||
}
|
||||
|
||||
auto to_uint16(const std::string &val) -> std::uint16_t {
|
||||
return static_cast<std::uint16_t>(std::stoul(val));
|
||||
}
|
||||
|
||||
auto to_uint32(const std::string &val) -> std::uint32_t {
|
||||
return static_cast<std::uint32_t>(std::stoul(val));
|
||||
}
|
||||
|
||||
auto to_uint64(const std::string &val) -> std::uint64_t {
|
||||
return std::stoull(val);
|
||||
}
|
||||
|
||||
auto to_upper(std::string str) -> std::string {
|
||||
std::transform(str.begin(), str.end(), str.begin(), ::toupper);
|
||||
return str;
|
||||
}
|
||||
|
||||
auto to_utf8(std::string str) -> std::string { return str; }
|
||||
|
||||
auto to_utf8(const std::wstring &str) -> std::string {
|
||||
return str.empty()
|
||||
? ""
|
||||
: std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t>()
|
||||
.to_bytes(str);
|
||||
}
|
||||
|
||||
auto trim(std::string &str) -> std::string & {
|
||||
return right_trim(left_trim(str));
|
||||
}
|
||||
|
||||
auto trim(std::string &str, const char &c) -> std::string & {
|
||||
return right_trim(left_trim(str, c), c);
|
||||
}
|
||||
|
||||
auto trim_copy(std::string str) -> std::string {
|
||||
return right_trim(left_trim(str));
|
||||
}
|
||||
|
||||
auto trim_copy(std::string str, const char &c) -> std::string {
|
||||
return right_trim(left_trim(str, c), c);
|
||||
}
|
||||
} // namespace repertory::utils::string
|
||||
/*
|
||||
Copyright <2018-2023> <scott.e.graves@protonmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#include "utils/string_utils.hpp"
|
||||
|
||||
namespace repertory::utils::string {
|
||||
/* constexpr c++20 */ auto ends_with(std::string_view str, std::string_view val)
|
||||
-> bool {
|
||||
if (val.size() > str.size()) {
|
||||
return false;
|
||||
}
|
||||
return std::equal(val.rbegin(), val.rend(), str.rbegin());
|
||||
}
|
||||
|
||||
auto from_bool(bool val) -> std::string { return std::to_string(val); }
|
||||
|
||||
auto from_dynamic_bitset(const boost::dynamic_bitset<> &bitset) -> std::string {
|
||||
std::stringstream ss;
|
||||
boost::archive::text_oarchive archive(ss);
|
||||
archive << bitset;
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
auto from_utf8(const std::string &str) -> std::wstring {
|
||||
return str.empty()
|
||||
? L""
|
||||
: std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t>()
|
||||
.from_bytes(str);
|
||||
}
|
||||
|
||||
/* constexpr c++20 */ auto is_numeric(std::string_view s) -> bool {
|
||||
if ((s.length() > 1u) && (s[0u] == '+' || s[0u] == '-')) {
|
||||
s = s.substr(1u);
|
||||
}
|
||||
|
||||
if (s.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto has_decimal = false;
|
||||
return std::find_if(
|
||||
s.begin(), s.end(),
|
||||
[&has_decimal](const std::string_view::value_type &c) -> bool {
|
||||
if (has_decimal && c == '.') {
|
||||
return true;
|
||||
}
|
||||
if ((has_decimal = has_decimal || c == '.')) {
|
||||
return false;
|
||||
}
|
||||
return not std::isdigit(c);
|
||||
}) == s.end();
|
||||
}
|
||||
|
||||
auto join(const std::vector<std::string> &arr, const char &delim)
|
||||
-> std::string {
|
||||
if (arr.empty()) {
|
||||
return "";
|
||||
}
|
||||
|
||||
return std::accumulate(
|
||||
std::next(arr.begin()), arr.end(), arr[0u],
|
||||
[&delim](auto s, const auto &v) { return s + delim + v; });
|
||||
}
|
||||
|
||||
auto left_trim(std::string &s) -> std::string & { return left_trim(s, ' '); }
|
||||
|
||||
auto left_trim(std::string &s, const char &c) -> std::string & {
|
||||
s.erase(0, s.find_first_not_of(c));
|
||||
return s;
|
||||
}
|
||||
|
||||
auto replace(std::string &src, const char &character, const char &with)
|
||||
-> std::string & {
|
||||
std::replace(src.begin(), src.end(), character, with);
|
||||
return src;
|
||||
}
|
||||
|
||||
auto replace(std::string &src, const std::string &find, const std::string &with,
|
||||
size_t start_pos) -> std::string & {
|
||||
if (!src.empty() && (start_pos < src.size())) {
|
||||
while ((start_pos = src.find(find, start_pos)) != std::string::npos) {
|
||||
src.replace(start_pos, find.size(), with);
|
||||
start_pos += with.size();
|
||||
}
|
||||
}
|
||||
return src;
|
||||
}
|
||||
|
||||
auto replace_copy(std::string src, const char &character, const char &with)
|
||||
-> std::string {
|
||||
std::replace(src.begin(), src.end(), character, with);
|
||||
return src;
|
||||
}
|
||||
|
||||
auto replace_copy(std::string src, const std::string &find,
|
||||
const std::string &with, size_t start_pos) -> std::string {
|
||||
return replace(src, find, with, start_pos);
|
||||
}
|
||||
|
||||
auto right_trim(std::string &s) -> std::string & { return right_trim(s, ' '); }
|
||||
|
||||
auto right_trim(std::string &s, const char &c) -> std::string & {
|
||||
s.erase(s.find_last_not_of(c) + 1);
|
||||
return s;
|
||||
}
|
||||
|
||||
auto split(const std::string &str, const char &delim, bool should_trim)
|
||||
-> std::vector<std::string> {
|
||||
std::vector<std::string> ret;
|
||||
std::stringstream ss(str);
|
||||
std::string item;
|
||||
while (std::getline(ss, item, delim)) {
|
||||
ret.push_back(should_trim ? trim(item) : item);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
auto to_bool(std::string val) -> bool {
|
||||
auto b = false;
|
||||
|
||||
trim(val);
|
||||
if (is_numeric(val)) {
|
||||
if (contains(val, ".")) {
|
||||
b = (to_double(val) != 0.0);
|
||||
} else {
|
||||
std::istringstream(val) >> b;
|
||||
}
|
||||
} else {
|
||||
std::istringstream(to_lower(val)) >> std::boolalpha >> b;
|
||||
}
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
auto to_double(const std::string &str) -> double { return std::stod(str); }
|
||||
|
||||
auto to_dynamic_bitset(const std::string &val) -> boost::dynamic_bitset<> {
|
||||
std::stringstream ss(val);
|
||||
boost::dynamic_bitset<> bitset;
|
||||
boost::archive::text_iarchive archive(ss);
|
||||
archive >> bitset;
|
||||
return bitset;
|
||||
}
|
||||
|
||||
auto to_int32(const std::string &val) -> std::int32_t { return std::stoi(val); }
|
||||
|
||||
auto to_int64(const std::string &val) -> std::int64_t {
|
||||
return std::stoll(val);
|
||||
}
|
||||
|
||||
auto to_lower(std::string str) -> std::string {
|
||||
std::transform(str.begin(), str.end(), str.begin(), ::tolower);
|
||||
return str;
|
||||
}
|
||||
|
||||
auto to_size_t(const std::string &val) -> std::size_t {
|
||||
return static_cast<std::size_t>(std::stoull(val));
|
||||
}
|
||||
|
||||
auto to_uint8(const std::string &val) -> std::uint8_t {
|
||||
return static_cast<std::uint8_t>(std::stoul(val));
|
||||
}
|
||||
|
||||
auto to_uint16(const std::string &val) -> std::uint16_t {
|
||||
return static_cast<std::uint16_t>(std::stoul(val));
|
||||
}
|
||||
|
||||
auto to_uint32(const std::string &val) -> std::uint32_t {
|
||||
return static_cast<std::uint32_t>(std::stoul(val));
|
||||
}
|
||||
|
||||
auto to_uint64(const std::string &val) -> std::uint64_t {
|
||||
return std::stoull(val);
|
||||
}
|
||||
|
||||
auto to_upper(std::string str) -> std::string {
|
||||
std::transform(str.begin(), str.end(), str.begin(), ::toupper);
|
||||
return str;
|
||||
}
|
||||
|
||||
auto to_utf8(std::string str) -> std::string { return str; }
|
||||
|
||||
auto to_utf8(const std::wstring &str) -> std::string {
|
||||
return str.empty()
|
||||
? ""
|
||||
: std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t>()
|
||||
.to_bytes(str);
|
||||
}
|
||||
|
||||
auto trim(std::string &str) -> std::string & {
|
||||
return right_trim(left_trim(str));
|
||||
}
|
||||
|
||||
auto trim(std::string &str, const char &c) -> std::string & {
|
||||
return right_trim(left_trim(str, c), c);
|
||||
}
|
||||
|
||||
auto trim_copy(std::string str) -> std::string {
|
||||
return right_trim(left_trim(str));
|
||||
}
|
||||
|
||||
auto trim_copy(std::string str, const char &c) -> std::string {
|
||||
return right_trim(left_trim(str, c), c);
|
||||
}
|
||||
} // namespace repertory::utils::string
|
||||
|
@ -1,64 +1,64 @@
|
||||
/*
|
||||
Copyright <2018-2023> <scott.e.graves@protonmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#include "utils/throttle.hpp"
|
||||
|
||||
#include "types/repertory.hpp"
|
||||
|
||||
namespace repertory {
|
||||
void throttle::decrement() {
|
||||
mutex_lock l(mutex_);
|
||||
if (not shutdown_) {
|
||||
if (count_ > 0) {
|
||||
count_--;
|
||||
}
|
||||
notify_.notify_one();
|
||||
}
|
||||
}
|
||||
|
||||
void throttle::increment_or_wait() {
|
||||
unique_mutex_lock l(mutex_);
|
||||
if (not shutdown_) {
|
||||
if (count_ >= max_size_) {
|
||||
notify_.wait(l);
|
||||
}
|
||||
if (not shutdown_) {
|
||||
count_++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void throttle::reset() {
|
||||
unique_mutex_lock l(mutex_);
|
||||
if (shutdown_) {
|
||||
count_ = 0;
|
||||
shutdown_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
void throttle::shutdown() {
|
||||
if (not shutdown_) {
|
||||
unique_mutex_lock l(mutex_);
|
||||
shutdown_ = true;
|
||||
notify_.notify_all();
|
||||
}
|
||||
}
|
||||
} // namespace repertory
|
||||
/*
|
||||
Copyright <2018-2023> <scott.e.graves@protonmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#include "utils/throttle.hpp"
|
||||
|
||||
#include "types/repertory.hpp"
|
||||
|
||||
namespace repertory {
|
||||
void throttle::decrement() {
|
||||
mutex_lock l(mutex_);
|
||||
if (not shutdown_) {
|
||||
if (count_ > 0) {
|
||||
count_--;
|
||||
}
|
||||
notify_.notify_one();
|
||||
}
|
||||
}
|
||||
|
||||
void throttle::increment_or_wait() {
|
||||
unique_mutex_lock l(mutex_);
|
||||
if (not shutdown_) {
|
||||
if (count_ >= max_size_) {
|
||||
notify_.wait(l);
|
||||
}
|
||||
if (not shutdown_) {
|
||||
count_++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void throttle::reset() {
|
||||
unique_mutex_lock l(mutex_);
|
||||
if (shutdown_) {
|
||||
count_ = 0;
|
||||
shutdown_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
void throttle::shutdown() {
|
||||
if (not shutdown_) {
|
||||
unique_mutex_lock l(mutex_);
|
||||
shutdown_ = true;
|
||||
notify_.notify_all();
|
||||
}
|
||||
}
|
||||
} // namespace repertory
|
||||
|
@ -1,54 +1,54 @@
|
||||
/*
|
||||
Copyright <2018-2023> <scott.e.graves@protonmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#include "utils/timeout.hpp"
|
||||
|
||||
#include "types/repertory.hpp"
|
||||
|
||||
namespace repertory {
|
||||
timeout::timeout(std::function<void()> timeout_callback,
|
||||
const std::chrono::system_clock::duration &duration)
|
||||
: timeout_killed_(duration == 0s) {
|
||||
if (not timeout_killed_) {
|
||||
timeout_thread_ =
|
||||
std::make_unique<std::thread>([this, duration, timeout_callback]() {
|
||||
unique_mutex_lock lock(timeout_mutex_);
|
||||
if (not timeout_killed_) {
|
||||
timeout_notify_.wait_for(lock, duration);
|
||||
if (not timeout_killed_) {
|
||||
timeout_callback();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void timeout::disable() {
|
||||
if (not timeout_killed_) {
|
||||
timeout_killed_ = true;
|
||||
unique_mutex_lock lock(timeout_mutex_);
|
||||
timeout_notify_.notify_all();
|
||||
lock.unlock();
|
||||
timeout_thread_->join();
|
||||
timeout_thread_.reset();
|
||||
}
|
||||
}
|
||||
} // namespace repertory
|
||||
/*
|
||||
Copyright <2018-2023> <scott.e.graves@protonmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#include "utils/timeout.hpp"
|
||||
|
||||
#include "types/repertory.hpp"
|
||||
|
||||
namespace repertory {
|
||||
timeout::timeout(std::function<void()> timeout_callback,
|
||||
const std::chrono::system_clock::duration &duration)
|
||||
: timeout_killed_(duration == 0s) {
|
||||
if (not timeout_killed_) {
|
||||
timeout_thread_ =
|
||||
std::make_unique<std::thread>([this, duration, timeout_callback]() {
|
||||
unique_mutex_lock lock(timeout_mutex_);
|
||||
if (not timeout_killed_) {
|
||||
timeout_notify_.wait_for(lock, duration);
|
||||
if (not timeout_killed_) {
|
||||
timeout_callback();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void timeout::disable() {
|
||||
if (not timeout_killed_) {
|
||||
timeout_killed_ = true;
|
||||
unique_mutex_lock lock(timeout_mutex_);
|
||||
timeout_notify_.notify_all();
|
||||
lock.unlock();
|
||||
timeout_thread_->join();
|
||||
timeout_thread_.reset();
|
||||
}
|
||||
}
|
||||
} // namespace repertory
|
||||
|
@ -1,277 +1,276 @@
|
||||
/*
|
||||
Copyright <2018-2023> <scott.e.graves@protonmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#ifndef _WIN32
|
||||
|
||||
#include "utils/unix/unix_utils.hpp"
|
||||
|
||||
#include "utils/error_utils.hpp"
|
||||
#include "utils/utils.hpp"
|
||||
|
||||
namespace repertory::utils {
|
||||
#ifndef __APPLE__
|
||||
auto convert_to_uint64(const pthread_t &t) -> std::uint64_t {
|
||||
return static_cast<std::uint64_t>(t);
|
||||
}
|
||||
#endif
|
||||
|
||||
auto from_api_error(const api_error &e) -> int {
|
||||
switch (e) {
|
||||
case api_error::access_denied:
|
||||
return -EACCES;
|
||||
case api_error::bad_address:
|
||||
return -EFAULT;
|
||||
case api_error::directory_end_of_files:
|
||||
return -EOF;
|
||||
case api_error::directory_exists:
|
||||
return -EISDIR;
|
||||
case api_error::directory_not_empty:
|
||||
return -ENOTEMPTY;
|
||||
case api_error::directory_not_found:
|
||||
return -ENOTDIR;
|
||||
case api_error::download_failed:
|
||||
#ifdef __APPLE__
|
||||
return -EBADMSG;
|
||||
#else
|
||||
return -EREMOTEIO;
|
||||
#endif
|
||||
case api_error::error:
|
||||
return -EIO;
|
||||
case api_error::item_exists:
|
||||
return -EEXIST;
|
||||
case api_error::file_in_use:
|
||||
return -EBUSY;
|
||||
case api_error::invalid_operation:
|
||||
return -EINVAL;
|
||||
case api_error::item_not_found:
|
||||
return -ENOENT;
|
||||
case api_error::out_of_memory:
|
||||
return -ENOMEM;
|
||||
case api_error::no_disk_space:
|
||||
return -ENOSPC;
|
||||
case api_error::os_error:
|
||||
return -errno;
|
||||
case api_error::permission_denied:
|
||||
return -EPERM;
|
||||
case api_error::success:
|
||||
return 0;
|
||||
case api_error::not_supported:
|
||||
return -ENOTSUP;
|
||||
case api_error::not_implemented:
|
||||
return -ENOSYS;
|
||||
case api_error::upload_failed:
|
||||
return -ENETDOWN;
|
||||
case api_error::xattr_buffer_small:
|
||||
return -ERANGE;
|
||||
case api_error::xattr_exists:
|
||||
return -EEXIST;
|
||||
case api_error::xattr_not_found:
|
||||
#ifdef __APPLE__
|
||||
return -ENOATTR;
|
||||
#else
|
||||
return -ENODATA;
|
||||
#endif
|
||||
case api_error::xattr_too_big:
|
||||
#ifdef __APPLE__
|
||||
return -ENAMETOOLONG;
|
||||
#else
|
||||
return -E2BIG;
|
||||
#endif
|
||||
default:
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
|
||||
auto get_last_error_code() -> int { return errno; }
|
||||
|
||||
auto get_thread_id() -> std::uint64_t {
|
||||
return convert_to_uint64(pthread_self());
|
||||
}
|
||||
|
||||
auto is_uid_member_of_group(const uid_t &uid, const gid_t &gid) -> bool {
|
||||
static const auto function_name = __FUNCTION__;
|
||||
|
||||
std::vector<gid_t> groups{};
|
||||
use_getpwuid(uid, [&groups](struct passwd *pw) {
|
||||
int group_count{};
|
||||
if (getgrouplist(pw->pw_name, pw->pw_gid, nullptr, &group_count) < 0) {
|
||||
groups.resize(static_cast<std::size_t>(group_count));
|
||||
#ifdef __APPLE__
|
||||
getgrouplist(pw->pw_name, pw->pw_gid,
|
||||
reinterpret_cast<int *>(groups.data()), &group_count);
|
||||
#else
|
||||
getgrouplist(pw->pw_name, pw->pw_gid, groups.data(), &group_count);
|
||||
#endif
|
||||
}
|
||||
});
|
||||
|
||||
return collection_includes(groups, gid);
|
||||
}
|
||||
|
||||
auto to_api_error(int e) -> api_error {
|
||||
switch (abs(e)) {
|
||||
case 0:
|
||||
return api_error::success;
|
||||
case EBADF:
|
||||
return api_error::invalid_handle;
|
||||
case EACCES:
|
||||
return api_error::access_denied;
|
||||
case EFAULT:
|
||||
return api_error::bad_address;
|
||||
case EOF:
|
||||
return api_error::directory_end_of_files;
|
||||
case EISDIR:
|
||||
return api_error::directory_exists;
|
||||
case ENOTEMPTY:
|
||||
return api_error::directory_not_empty;
|
||||
case ENOTDIR:
|
||||
return api_error::directory_not_found;
|
||||
#ifdef __APPLE__
|
||||
case EBADMSG:
|
||||
return api_error::download_failed;
|
||||
#else
|
||||
case EREMOTEIO:
|
||||
return api_error::download_failed;
|
||||
#endif
|
||||
case EIO:
|
||||
return api_error::error;
|
||||
case EEXIST:
|
||||
return api_error::item_exists;
|
||||
case EBUSY:
|
||||
return api_error::file_in_use;
|
||||
case EINVAL:
|
||||
return api_error::invalid_operation;
|
||||
case ENOENT:
|
||||
return api_error::item_not_found;
|
||||
case ENOMEM:
|
||||
return api_error::out_of_memory;
|
||||
case EPERM:
|
||||
return api_error::permission_denied;
|
||||
case ENOSPC:
|
||||
return api_error::no_disk_space;
|
||||
case ENOTSUP:
|
||||
return api_error::not_supported;
|
||||
case ENOSYS:
|
||||
return api_error::not_implemented;
|
||||
case ENETDOWN:
|
||||
return api_error::upload_failed;
|
||||
case ERANGE:
|
||||
return api_error::xattr_buffer_small;
|
||||
#ifdef __APPLE__
|
||||
case ENOATTR:
|
||||
return api_error::xattr_not_found;
|
||||
#else
|
||||
case ENODATA:
|
||||
return api_error::xattr_not_found;
|
||||
#endif
|
||||
#ifdef __APPLE__
|
||||
case ENAMETOOLONG:
|
||||
return api_error::xattr_too_big;
|
||||
#else
|
||||
case E2BIG:
|
||||
return api_error::xattr_too_big;
|
||||
#endif
|
||||
default:
|
||||
return api_error::error;
|
||||
}
|
||||
}
|
||||
|
||||
void set_last_error_code(int error_code) { errno = error_code; }
|
||||
|
||||
auto unix_error_to_windows(int e) -> std::int32_t {
|
||||
switch (e) {
|
||||
case 0:
|
||||
return STATUS_SUCCESS;
|
||||
case EACCES:
|
||||
case EPERM:
|
||||
return STATUS_ACCESS_DENIED;
|
||||
case EBADF:
|
||||
return STATUS_INVALID_HANDLE;
|
||||
case EBUSY:
|
||||
return STATUS_DEVICE_BUSY;
|
||||
case EEXIST:
|
||||
return STATUS_OBJECT_NAME_EXISTS;
|
||||
case EFAULT:
|
||||
return STATUS_INVALID_ADDRESS;
|
||||
case EFBIG:
|
||||
return STATUS_FILE_TOO_LARGE;
|
||||
case EINVAL:
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
case EIO:
|
||||
return STATUS_UNEXPECTED_IO_ERROR;
|
||||
case EISDIR:
|
||||
return STATUS_OBJECT_NAME_EXISTS;
|
||||
case EMFILE:
|
||||
return STATUS_INSUFFICIENT_RESOURCES;
|
||||
case ENOENT:
|
||||
return STATUS_OBJECT_NAME_NOT_FOUND;
|
||||
case ENOEXEC:
|
||||
return STATUS_INVALID_IMAGE_FORMAT;
|
||||
case ENOMEM:
|
||||
return STATUS_NO_MEMORY;
|
||||
case ENOSPC:
|
||||
return STATUS_DEVICE_INSUFFICIENT_RESOURCES;
|
||||
case ENOTDIR:
|
||||
return STATUS_OBJECT_NAME_NOT_FOUND;
|
||||
default:
|
||||
return STATUS_INTERNAL_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
auto unix_time_to_windows_time(const remote::file_time &ts) -> UINT64 {
|
||||
return (ts / 100ull) + 116444736000000000ull;
|
||||
}
|
||||
|
||||
void use_getpwuid(uid_t uid, std::function<void(struct passwd *pw)> fn) {
|
||||
static std::mutex mtx{};
|
||||
mutex_lock lock{mtx};
|
||||
auto *pw = getpwuid(uid);
|
||||
if (not pw) {
|
||||
utils::error::raise_error(__FUNCTION__, "'getpwuid' returned nullptr");
|
||||
return;
|
||||
}
|
||||
|
||||
fn(pw);
|
||||
}
|
||||
|
||||
void windows_create_to_unix(const UINT32 &create_options,
|
||||
const UINT32 &granted_access, std::uint32_t &flags,
|
||||
remote::file_mode &mode) {
|
||||
mode = S_IRUSR | S_IWUSR;
|
||||
flags = O_CREAT | O_RDWR;
|
||||
if (create_options & FILE_DIRECTORY_FILE) {
|
||||
mode |= S_IXUSR;
|
||||
flags = O_DIRECTORY;
|
||||
}
|
||||
|
||||
if ((granted_access & GENERIC_EXECUTE) ||
|
||||
(granted_access & FILE_GENERIC_EXECUTE) ||
|
||||
(granted_access & FILE_EXECUTE)) {
|
||||
mode |= (S_IXUSR);
|
||||
}
|
||||
}
|
||||
|
||||
auto windows_time_to_unix_time(std::uint64_t t) -> remote::file_time {
|
||||
return (t - 116444736000000000ull) * 100ull;
|
||||
}
|
||||
} // namespace repertory::utils
|
||||
|
||||
#endif // !_WIN32
|
||||
/*
|
||||
Copyright <2018-2023> <scott.e.graves@protonmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#ifndef _WIN32
|
||||
|
||||
#include "utils/unix/unix_utils.hpp"
|
||||
|
||||
#include "utils/error_utils.hpp"
|
||||
#include "utils/utils.hpp"
|
||||
|
||||
namespace repertory::utils {
|
||||
#ifndef __APPLE__
|
||||
auto convert_to_uint64(const pthread_t &thread) -> std::uint64_t {
|
||||
return static_cast<std::uint64_t>(thread);
|
||||
}
|
||||
#endif
|
||||
|
||||
auto from_api_error(const api_error &err) -> int {
|
||||
switch (err) {
|
||||
case api_error::access_denied:
|
||||
return -EACCES;
|
||||
case api_error::bad_address:
|
||||
return -EFAULT;
|
||||
case api_error::directory_end_of_files:
|
||||
return -EOF;
|
||||
case api_error::directory_exists:
|
||||
return -EISDIR;
|
||||
case api_error::directory_not_empty:
|
||||
return -ENOTEMPTY;
|
||||
case api_error::directory_not_found:
|
||||
return -ENOTDIR;
|
||||
case api_error::download_failed:
|
||||
#ifdef __APPLE__
|
||||
return -EBADMSG;
|
||||
#else
|
||||
return -EREMOTEIO;
|
||||
#endif
|
||||
case api_error::error:
|
||||
return -EIO;
|
||||
case api_error::item_exists:
|
||||
return -EEXIST;
|
||||
case api_error::file_in_use:
|
||||
return -EBUSY;
|
||||
case api_error::invalid_operation:
|
||||
return -EINVAL;
|
||||
case api_error::item_not_found:
|
||||
return -ENOENT;
|
||||
case api_error::out_of_memory:
|
||||
return -ENOMEM;
|
||||
case api_error::no_disk_space:
|
||||
return -ENOSPC;
|
||||
case api_error::os_error:
|
||||
return -errno;
|
||||
case api_error::permission_denied:
|
||||
return -EPERM;
|
||||
case api_error::success:
|
||||
return 0;
|
||||
case api_error::not_supported:
|
||||
return -ENOTSUP;
|
||||
case api_error::not_implemented:
|
||||
return -ENOSYS;
|
||||
case api_error::upload_failed:
|
||||
return -ENETDOWN;
|
||||
case api_error::xattr_buffer_small:
|
||||
return -ERANGE;
|
||||
case api_error::xattr_exists:
|
||||
return -EEXIST;
|
||||
case api_error::xattr_not_found:
|
||||
#ifdef __APPLE__
|
||||
return -ENOATTR;
|
||||
#else
|
||||
return -ENODATA;
|
||||
#endif
|
||||
case api_error::xattr_too_big:
|
||||
#ifdef __APPLE__
|
||||
return -ENAMETOOLONG;
|
||||
#else
|
||||
return -E2BIG;
|
||||
#endif
|
||||
default:
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
|
||||
auto get_last_error_code() -> int { return errno; }
|
||||
|
||||
auto get_thread_id() -> std::uint64_t {
|
||||
return convert_to_uint64(pthread_self());
|
||||
}
|
||||
|
||||
auto is_uid_member_of_group(const uid_t &uid, const gid_t &gid) -> bool {
|
||||
std::vector<gid_t> groups{};
|
||||
use_getpwuid(uid, [&groups](struct passwd *pass) {
|
||||
int group_count{};
|
||||
if (getgrouplist(pass->pw_name, pass->pw_gid, nullptr, &group_count) < 0) {
|
||||
groups.resize(static_cast<std::size_t>(group_count));
|
||||
#ifdef __APPLE__
|
||||
getgrouplist(pass->pw_name, pass->pw_gid,
|
||||
reinterpret_cast<int *>(groups.data()), &group_count);
|
||||
#else
|
||||
getgrouplist(pass->pw_name, pass->pw_gid, groups.data(), &group_count);
|
||||
#endif
|
||||
}
|
||||
});
|
||||
|
||||
return collection_includes(groups, gid);
|
||||
}
|
||||
|
||||
auto to_api_error(int err) -> api_error {
|
||||
switch (abs(err)) {
|
||||
case 0:
|
||||
return api_error::success;
|
||||
case EBADF:
|
||||
return api_error::invalid_handle;
|
||||
case EACCES:
|
||||
return api_error::access_denied;
|
||||
case EFAULT:
|
||||
return api_error::bad_address;
|
||||
case EOF:
|
||||
return api_error::directory_end_of_files;
|
||||
case EISDIR:
|
||||
return api_error::directory_exists;
|
||||
case ENOTEMPTY:
|
||||
return api_error::directory_not_empty;
|
||||
case ENOTDIR:
|
||||
return api_error::directory_not_found;
|
||||
#ifdef __APPLE__
|
||||
case EBADMSG:
|
||||
return api_error::download_failed;
|
||||
#else
|
||||
case EREMOTEIO:
|
||||
return api_error::download_failed;
|
||||
#endif
|
||||
case EIO:
|
||||
return api_error::error;
|
||||
case EEXIST:
|
||||
return api_error::item_exists;
|
||||
case EBUSY:
|
||||
return api_error::file_in_use;
|
||||
case EINVAL:
|
||||
return api_error::invalid_operation;
|
||||
case ENOENT:
|
||||
return api_error::item_not_found;
|
||||
case ENOMEM:
|
||||
return api_error::out_of_memory;
|
||||
case EPERM:
|
||||
return api_error::permission_denied;
|
||||
case ENOSPC:
|
||||
return api_error::no_disk_space;
|
||||
case ENOTSUP:
|
||||
return api_error::not_supported;
|
||||
case ENOSYS:
|
||||
return api_error::not_implemented;
|
||||
case ENETDOWN:
|
||||
return api_error::upload_failed;
|
||||
case ERANGE:
|
||||
return api_error::xattr_buffer_small;
|
||||
#ifdef __APPLE__
|
||||
case ENOATTR:
|
||||
return api_error::xattr_not_found;
|
||||
#else
|
||||
case ENODATA:
|
||||
return api_error::xattr_not_found;
|
||||
#endif
|
||||
#ifdef __APPLE__
|
||||
case ENAMETOOLONG:
|
||||
return api_error::xattr_too_big;
|
||||
#else
|
||||
case E2BIG:
|
||||
return api_error::xattr_too_big;
|
||||
#endif
|
||||
default:
|
||||
return api_error::error;
|
||||
}
|
||||
}
|
||||
|
||||
void set_last_error_code(int error_code) { errno = error_code; }
|
||||
|
||||
auto unix_error_to_windows(int err) -> std::int32_t {
|
||||
switch (err) {
|
||||
case 0:
|
||||
return STATUS_SUCCESS;
|
||||
case EACCES:
|
||||
case EPERM:
|
||||
return STATUS_ACCESS_DENIED;
|
||||
case EBADF:
|
||||
return STATUS_INVALID_HANDLE;
|
||||
case EBUSY:
|
||||
return STATUS_DEVICE_BUSY;
|
||||
case EEXIST:
|
||||
return STATUS_OBJECT_NAME_EXISTS;
|
||||
case EFAULT:
|
||||
return STATUS_INVALID_ADDRESS;
|
||||
case EFBIG:
|
||||
return STATUS_FILE_TOO_LARGE;
|
||||
case EINVAL:
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
case EIO:
|
||||
return STATUS_UNEXPECTED_IO_ERROR;
|
||||
case EISDIR:
|
||||
return STATUS_OBJECT_NAME_EXISTS;
|
||||
case EMFILE:
|
||||
return STATUS_INSUFFICIENT_RESOURCES;
|
||||
case ENOENT:
|
||||
return STATUS_OBJECT_NAME_NOT_FOUND;
|
||||
case ENOEXEC:
|
||||
return STATUS_INVALID_IMAGE_FORMAT;
|
||||
case ENOMEM:
|
||||
return STATUS_NO_MEMORY;
|
||||
case ENOSPC:
|
||||
return STATUS_DEVICE_INSUFFICIENT_RESOURCES;
|
||||
case ENOTDIR:
|
||||
return STATUS_OBJECT_NAME_NOT_FOUND;
|
||||
default:
|
||||
return STATUS_INTERNAL_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
auto unix_time_to_windows_time(const remote::file_time &file_time) -> UINT64 {
|
||||
return (file_time / 100ULL) + 116444736000000000ULL;
|
||||
}
|
||||
|
||||
void use_getpwuid(uid_t uid,
|
||||
std::function<void(struct passwd *pass)> callback) {
|
||||
static std::mutex mtx{};
|
||||
mutex_lock lock{mtx};
|
||||
auto *temp_pw = getpwuid(uid);
|
||||
if (temp_pw == nullptr) {
|
||||
utils::error::raise_error(__FUNCTION__, "'getpwuid' returned nullptr");
|
||||
return;
|
||||
}
|
||||
|
||||
callback(temp_pw);
|
||||
}
|
||||
|
||||
void windows_create_to_unix(const UINT32 &create_options,
|
||||
const UINT32 &granted_access, std::uint32_t &flags,
|
||||
remote::file_mode &mode) {
|
||||
mode = S_IRUSR | S_IWUSR;
|
||||
flags = O_CREAT | O_RDWR;
|
||||
if ((create_options & FILE_DIRECTORY_FILE) != 0U) {
|
||||
mode |= S_IXUSR;
|
||||
flags = O_DIRECTORY;
|
||||
}
|
||||
|
||||
if (((granted_access & GENERIC_EXECUTE) != 0U) ||
|
||||
((granted_access & FILE_GENERIC_EXECUTE) != 0U) ||
|
||||
((granted_access & FILE_EXECUTE) != 0U)) {
|
||||
mode |= (S_IXUSR);
|
||||
}
|
||||
}
|
||||
|
||||
auto windows_time_to_unix_time(std::uint64_t win_time) -> remote::file_time {
|
||||
return (win_time - 116444736000000000ULL) * 100ULL;
|
||||
}
|
||||
} // namespace repertory::utils
|
||||
|
||||
#endif // !_WIN32
|
||||
|
@ -1,345 +1,335 @@
|
||||
/*
|
||||
Copyright <2018-2023> <scott.e.graves@protonmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#include "utils/utils.hpp"
|
||||
|
||||
#include "app_config.hpp"
|
||||
#include "events/event_system.hpp"
|
||||
#include "events/events.hpp"
|
||||
#include "providers/i_provider.hpp"
|
||||
#include "types/startup_exception.hpp"
|
||||
#include "utils/com_init_wrapper.hpp"
|
||||
#include "utils/native_file.hpp"
|
||||
#include "utils/path_utils.hpp"
|
||||
#include "utils/string_utils.hpp"
|
||||
|
||||
namespace repertory::utils {
|
||||
void calculate_allocation_size(bool directory, std::uint64_t file_size,
|
||||
UINT64 allocation_size,
|
||||
std::string &allocation_meta_size) {
|
||||
if (directory) {
|
||||
allocation_meta_size = "0";
|
||||
return;
|
||||
}
|
||||
|
||||
if (file_size > allocation_size) {
|
||||
allocation_size = file_size;
|
||||
}
|
||||
allocation_size =
|
||||
((allocation_size == 0u) ? WINFSP_ALLOCATION_UNIT : allocation_size);
|
||||
allocation_size =
|
||||
utils::divide_with_ceiling(allocation_size, WINFSP_ALLOCATION_UNIT) *
|
||||
WINFSP_ALLOCATION_UNIT;
|
||||
allocation_meta_size = std::to_string(allocation_size);
|
||||
}
|
||||
|
||||
auto calculate_read_size(const uint64_t &total_size, std::size_t read_size,
|
||||
const uint64_t &offset) -> std::size_t {
|
||||
return static_cast<std::size_t>(
|
||||
((offset + read_size) > total_size)
|
||||
? ((offset < total_size) ? total_size - offset : 0u)
|
||||
: read_size);
|
||||
}
|
||||
|
||||
auto compare_version_strings(std::string version1, std::string version2)
|
||||
-> int {
|
||||
if (utils::string::contains(version1, "-")) {
|
||||
version1 = utils::string::split(version1, '-')[0u];
|
||||
}
|
||||
|
||||
if (utils::string::contains(version2, "-")) {
|
||||
version2 = utils::string::split(version2, '-')[0u];
|
||||
}
|
||||
|
||||
auto nums1 = utils::string::split(version1, '.');
|
||||
auto nums2 = utils::string::split(version2, '.');
|
||||
|
||||
while (nums1.size() > nums2.size()) {
|
||||
nums2.emplace_back("0");
|
||||
}
|
||||
|
||||
while (nums2.size() > nums1.size()) {
|
||||
nums1.emplace_back("0");
|
||||
}
|
||||
|
||||
for (std::size_t i = 0u; i < nums1.size(); i++) {
|
||||
const auto int1 = utils::string::to_uint32(nums1[i]);
|
||||
const auto int2 = utils::string::to_uint32(nums2[i]);
|
||||
const auto res = std::memcmp(&int1, &int2, sizeof(int1));
|
||||
if (res) {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto convert_api_date(const std::string &date) -> std::uint64_t {
|
||||
// 2009-10-12T17:50:30.000Z
|
||||
const auto date_parts = utils::string::split(date, '.');
|
||||
const auto date_time = date_parts[0U];
|
||||
const auto nanos =
|
||||
utils::string::to_uint64(utils::string::split(date_parts[1U], 'Z')[0U]);
|
||||
|
||||
struct tm tm1 {};
|
||||
#ifdef _WIN32
|
||||
utils::strptime(date_time.c_str(), "%Y-%m-%dT%T", &tm1);
|
||||
#else
|
||||
strptime(date_time.c_str(), "%Y-%m-%dT%T", &tm1);
|
||||
#endif
|
||||
return nanos + (mktime(&tm1) * NANOS_PER_SECOND);
|
||||
}
|
||||
|
||||
auto create_curl() -> CURL * {
|
||||
static std::recursive_mutex mtx;
|
||||
|
||||
unique_recur_mutex_lock l(mtx);
|
||||
curl_global_init(CURL_GLOBAL_DEFAULT);
|
||||
l.unlock();
|
||||
|
||||
return reset_curl(curl_easy_init());
|
||||
}
|
||||
|
||||
auto create_uuid_string() -> std::string {
|
||||
#ifdef _WIN32
|
||||
UUID guid{};
|
||||
UuidCreate(&guid);
|
||||
|
||||
unsigned char *s;
|
||||
UuidToStringA(&guid, &s);
|
||||
|
||||
std::string ret(reinterpret_cast<char *>(s));
|
||||
RpcStringFreeA(&s);
|
||||
|
||||
return ret;
|
||||
#else
|
||||
#if __linux__
|
||||
uuid id;
|
||||
id.make(UUID_MAKE_V4);
|
||||
return id.string();
|
||||
#else
|
||||
uuid_t guid;
|
||||
uuid_generate_random(guid);
|
||||
|
||||
std::string ret;
|
||||
ret.resize(37);
|
||||
uuid_unparse(guid, &ret[0]);
|
||||
|
||||
return ret.c_str();
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
auto create_volume_label(const provider_type &pt) -> std::string {
|
||||
return "repertory_" + app_config::get_provider_name(pt);
|
||||
}
|
||||
|
||||
auto download_type_from_string(std::string type,
|
||||
const download_type &default_type)
|
||||
-> download_type {
|
||||
type = utils::string::to_lower(utils::string::trim(type));
|
||||
if (type == "direct") {
|
||||
return download_type::direct;
|
||||
} else if (type == "fallback") {
|
||||
return download_type::fallback;
|
||||
} else if (type == "ring_buffer") {
|
||||
return download_type::ring_buffer;
|
||||
}
|
||||
|
||||
return default_type;
|
||||
}
|
||||
|
||||
auto download_type_to_string(const download_type &type) -> std::string {
|
||||
switch (type) {
|
||||
case download_type::direct:
|
||||
return "direct";
|
||||
case download_type::fallback:
|
||||
return "fallback";
|
||||
case download_type::ring_buffer:
|
||||
return "ring_buffer";
|
||||
default:
|
||||
return "fallback";
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
// https://www.frenk.com/2009/12/convert-filetime-to-unix-timestamp/
|
||||
auto filetime_to_unix_time(const FILETIME &ft) -> remote::file_time {
|
||||
LARGE_INTEGER date{};
|
||||
date.HighPart = ft.dwHighDateTime;
|
||||
date.LowPart = ft.dwLowDateTime;
|
||||
date.QuadPart -= 116444736000000000ull;
|
||||
|
||||
return date.QuadPart * 100ull;
|
||||
}
|
||||
|
||||
void unix_time_to_filetime(const remote::file_time &ts, FILETIME &ft) {
|
||||
const auto winTime = (ts / 100ull) + 116444736000000000ull;
|
||||
ft.dwHighDateTime = winTime >> 32u;
|
||||
ft.dwLowDateTime = winTime & 0xFFFFFFFF;
|
||||
}
|
||||
#endif
|
||||
|
||||
auto generate_random_string(std::uint16_t length) -> std::string {
|
||||
srand(static_cast<unsigned int>(get_time_now()));
|
||||
|
||||
std::string ret;
|
||||
ret.resize(length);
|
||||
for (std::uint16_t i = 0u; i < length; i++) {
|
||||
do {
|
||||
ret[i] = static_cast<char>(rand() % 74 + 48);
|
||||
} while (((ret[i] >= 91) && (ret[i] <= 96)) ||
|
||||
((ret[i] >= 58) && (ret[i] <= 64)));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
auto get_attributes_from_meta(const api_meta_map &meta) -> DWORD {
|
||||
return static_cast<DWORD>(utils::string::to_uint32(meta.at(META_ATTRIBUTES)));
|
||||
}
|
||||
|
||||
auto get_environment_variable(const std::string &variable) -> std::string {
|
||||
#ifdef _WIN32
|
||||
std::string value;
|
||||
auto sz = ::GetEnvironmentVariable(&variable[0], nullptr, 0);
|
||||
if (sz > 0) {
|
||||
value.resize(sz);
|
||||
::GetEnvironmentVariable(&variable[0], &value[0], sz);
|
||||
}
|
||||
|
||||
return value.c_str();
|
||||
#else
|
||||
const auto *v = getenv(variable.c_str());
|
||||
return std::string(v ? v : "");
|
||||
#endif
|
||||
}
|
||||
|
||||
auto get_file_time_now() -> std::uint64_t {
|
||||
#ifdef _WIN32
|
||||
SYSTEMTIME st{};
|
||||
::GetSystemTime(&st);
|
||||
FILETIME ft{};
|
||||
::SystemTimeToFileTime(&st, &ft);
|
||||
return static_cast<std::uint64_t>(((LARGE_INTEGER *)&ft)->QuadPart);
|
||||
#else
|
||||
return get_time_now();
|
||||
#endif
|
||||
}
|
||||
|
||||
void get_local_time_now(struct tm &local_time) {
|
||||
memset(&local_time, 0, sizeof(local_time));
|
||||
|
||||
const auto now =
|
||||
std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
|
||||
#ifdef _WIN32
|
||||
localtime_s(&local_time, &now);
|
||||
#else
|
||||
const auto *tmp = std::localtime(&now);
|
||||
if (tmp) {
|
||||
memcpy(&local_time, tmp, sizeof(local_time));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
auto get_next_available_port(std::uint16_t first_port,
|
||||
std::uint16_t &available_port) -> bool {
|
||||
using namespace boost::asio;
|
||||
using ip::tcp;
|
||||
boost::system::error_code ec;
|
||||
do {
|
||||
io_service svc;
|
||||
tcp::acceptor a(svc);
|
||||
a.open(tcp::v4(), ec) || a.bind({tcp::v4(), first_port}, ec);
|
||||
} while (ec && (first_port++ < 65535u));
|
||||
|
||||
if (not ec) {
|
||||
available_port = first_port;
|
||||
}
|
||||
|
||||
return not ec;
|
||||
}
|
||||
|
||||
auto get_time_now() -> std::uint64_t {
|
||||
#ifdef _WIN32
|
||||
return static_cast<std::uint64_t>(
|
||||
std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()));
|
||||
#else
|
||||
#if __APPLE__
|
||||
return std::chrono::nanoseconds(
|
||||
std::chrono::system_clock::now().time_since_epoch())
|
||||
.count();
|
||||
#else
|
||||
return static_cast<std::uint64_t>(
|
||||
std::chrono::nanoseconds(
|
||||
std::chrono::high_resolution_clock::now().time_since_epoch())
|
||||
.count());
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
auto reset_curl(CURL *curl_handle) -> CURL * {
|
||||
curl_easy_reset(curl_handle);
|
||||
#if __APPLE__
|
||||
curl_easy_setopt(curl_handle, CURLOPT_NOSIGNAL, 1);
|
||||
#endif
|
||||
return curl_handle;
|
||||
}
|
||||
|
||||
auto retryable_action(const std::function<bool()> &action) -> bool {
|
||||
auto succeeded = false;
|
||||
for (std::uint8_t i = 0u; not(succeeded = action()) && (i < 20u); i++) {
|
||||
std::this_thread::sleep_for(100ms);
|
||||
}
|
||||
return succeeded;
|
||||
}
|
||||
|
||||
void spin_wait_for_mutex(std::function<bool()> complete,
|
||||
std::condition_variable &cv, std::mutex &mtx,
|
||||
const std::string &text) {
|
||||
while (not complete()) {
|
||||
unique_mutex_lock l(mtx);
|
||||
if (not complete()) {
|
||||
if (not text.empty()) {
|
||||
/* event_system::instance().raise<DebugLog>(__FUNCTION__,
|
||||
* "spin_wait_for_mutex", text); */
|
||||
}
|
||||
cv.wait_for(l, 1s);
|
||||
}
|
||||
l.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
void spin_wait_for_mutex(bool &complete, std::condition_variable &cv,
|
||||
std::mutex &mtx, const std::string &text) {
|
||||
while (not complete) {
|
||||
unique_mutex_lock l(mtx);
|
||||
if (not complete) {
|
||||
if (not text.empty()) {
|
||||
/* event_system::instance().raise<DebugLog>(__FUNCTION__,
|
||||
* "spin_wait_for_mutex", text); */
|
||||
}
|
||||
cv.wait_for(l, 1s);
|
||||
}
|
||||
l.unlock();
|
||||
}
|
||||
}
|
||||
} // namespace repertory::utils
|
||||
/*
|
||||
Copyright <2018-2023> <scott.e.graves@protonmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#include "utils/utils.hpp"
|
||||
|
||||
#include "app_config.hpp"
|
||||
#include "events/event_system.hpp"
|
||||
#include "events/events.hpp"
|
||||
#include "providers/i_provider.hpp"
|
||||
#include "types/startup_exception.hpp"
|
||||
#include "utils/com_init_wrapper.hpp"
|
||||
#include "utils/native_file.hpp"
|
||||
#include "utils/path_utils.hpp"
|
||||
#include "utils/string_utils.hpp"
|
||||
|
||||
namespace repertory::utils {
|
||||
void calculate_allocation_size(bool directory, std::uint64_t file_size,
|
||||
UINT64 allocation_size,
|
||||
std::string &allocation_meta_size) {
|
||||
if (directory) {
|
||||
allocation_meta_size = "0";
|
||||
return;
|
||||
}
|
||||
|
||||
if (file_size > allocation_size) {
|
||||
allocation_size = file_size;
|
||||
}
|
||||
allocation_size =
|
||||
((allocation_size == 0U) ? WINFSP_ALLOCATION_UNIT : allocation_size);
|
||||
allocation_size =
|
||||
utils::divide_with_ceiling(allocation_size, WINFSP_ALLOCATION_UNIT) *
|
||||
WINFSP_ALLOCATION_UNIT;
|
||||
allocation_meta_size = std::to_string(allocation_size);
|
||||
}
|
||||
|
||||
auto calculate_read_size(const uint64_t &total_size, std::size_t read_size,
|
||||
const uint64_t &offset) -> std::size_t {
|
||||
return static_cast<std::size_t>(
|
||||
((offset + read_size) > total_size)
|
||||
? ((offset < total_size) ? total_size - offset : 0U)
|
||||
: read_size);
|
||||
}
|
||||
|
||||
auto compare_version_strings(std::string version1, std::string version2)
|
||||
-> int {
|
||||
if (utils::string::contains(version1, "-")) {
|
||||
version1 = utils::string::split(version1, '-')[0U];
|
||||
}
|
||||
|
||||
if (utils::string::contains(version2, "-")) {
|
||||
version2 = utils::string::split(version2, '-')[0U];
|
||||
}
|
||||
|
||||
auto nums1 = utils::string::split(version1, '.');
|
||||
auto nums2 = utils::string::split(version2, '.');
|
||||
|
||||
while (nums1.size() > nums2.size()) {
|
||||
nums2.emplace_back("0");
|
||||
}
|
||||
|
||||
while (nums2.size() > nums1.size()) {
|
||||
nums1.emplace_back("0");
|
||||
}
|
||||
|
||||
for (std::size_t i = 0U; i < nums1.size(); i++) {
|
||||
const auto int1 = utils::string::to_uint32(nums1[i]);
|
||||
const auto int2 = utils::string::to_uint32(nums2[i]);
|
||||
const auto res = std::memcmp(&int1, &int2, sizeof(int1));
|
||||
if (res != 0) {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto convert_api_date(const std::string &date) -> std::uint64_t {
|
||||
// 2009-10-12T17:50:30.000Z
|
||||
const auto date_parts = utils::string::split(date, '.');
|
||||
const auto date_time = date_parts[0U];
|
||||
const auto nanos =
|
||||
utils::string::to_uint64(utils::string::split(date_parts[1U], 'Z')[0U]);
|
||||
|
||||
struct tm tm1 {};
|
||||
#ifdef _WIN32
|
||||
utils::strptime(date_time.c_str(), "%Y-%m-%dT%T", &tm1);
|
||||
#else
|
||||
strptime(date_time.c_str(), "%Y-%m-%dT%T", &tm1);
|
||||
#endif
|
||||
return nanos + (static_cast<std::uint64_t>(mktime(&tm1)) * NANOS_PER_SECOND);
|
||||
}
|
||||
|
||||
auto create_curl() -> CURL * {
|
||||
static std::recursive_mutex mtx;
|
||||
|
||||
unique_recur_mutex_lock lock(mtx);
|
||||
curl_global_init(CURL_GLOBAL_DEFAULT);
|
||||
lock.unlock();
|
||||
|
||||
return reset_curl(curl_easy_init());
|
||||
}
|
||||
|
||||
auto create_uuid_string() -> std::string {
|
||||
std::random_device random_device;
|
||||
auto seed_data = std::array<int, std::mt19937::state_size>{};
|
||||
std::generate(std::begin(seed_data), std::end(seed_data),
|
||||
std::ref(random_device));
|
||||
std::seed_seq seq(std::begin(seed_data), std::end(seed_data));
|
||||
std::mt19937 generator(seq);
|
||||
uuids::uuid_random_generator gen{generator};
|
||||
|
||||
return uuids::to_string(gen());
|
||||
}
|
||||
|
||||
auto create_volume_label(const provider_type &prov) -> std::string {
|
||||
return "repertory_" + app_config::get_provider_name(prov);
|
||||
}
|
||||
|
||||
auto download_type_from_string(std::string type,
|
||||
const download_type &default_type)
|
||||
-> download_type {
|
||||
type = utils::string::to_lower(utils::string::trim(type));
|
||||
if (type == "direct") {
|
||||
return download_type::direct;
|
||||
}
|
||||
|
||||
if (type == "fallback") {
|
||||
return download_type::fallback;
|
||||
}
|
||||
|
||||
if (type == "ring_buffer") {
|
||||
return download_type::ring_buffer;
|
||||
}
|
||||
|
||||
return default_type;
|
||||
}
|
||||
|
||||
auto download_type_to_string(const download_type &type) -> std::string {
|
||||
switch (type) {
|
||||
case download_type::direct:
|
||||
return "direct";
|
||||
case download_type::fallback:
|
||||
return "fallback";
|
||||
case download_type::ring_buffer:
|
||||
return "ring_buffer";
|
||||
default:
|
||||
return "fallback";
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
// https://www.frenk.com/2009/12/convert-filetime-to-unix-timestamp/
|
||||
auto filetime_to_unix_time(const FILETIME &ft) -> remote::file_time {
|
||||
LARGE_INTEGER date{};
|
||||
date.HighPart = ft.dwHighDateTime;
|
||||
date.LowPart = ft.dwLowDateTime;
|
||||
date.QuadPart -= 116444736000000000ULL;
|
||||
|
||||
return date.QuadPart * 100ULL;
|
||||
}
|
||||
|
||||
void unix_time_to_filetime(const remote::file_time &ts, FILETIME &ft) {
|
||||
const auto win_time = (ts / 100ULL) + 116444736000000000ULL;
|
||||
ft.dwHighDateTime = win_time >> 32U;
|
||||
ft.dwLowDateTime = win_time & 0xFFFFFFFF;
|
||||
}
|
||||
#endif
|
||||
|
||||
auto generate_random_string(std::uint16_t length) -> std::string {
|
||||
std::string ret;
|
||||
ret.resize(length);
|
||||
for (std::uint16_t i = 0U; i < length; i++) {
|
||||
do {
|
||||
ret[i] = static_cast<char>(repertory_rand<std::uint8_t>() % 74 + 48);
|
||||
} while (((ret[i] >= 91) && (ret[i] <= 96)) ||
|
||||
((ret[i] >= 58) && (ret[i] <= 64)));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
auto get_attributes_from_meta(const api_meta_map &meta) -> DWORD {
|
||||
return static_cast<DWORD>(utils::string::to_uint32(meta.at(META_ATTRIBUTES)));
|
||||
}
|
||||
|
||||
auto get_environment_variable(const std::string &variable) -> std::string {
|
||||
static std::mutex mtx{};
|
||||
mutex_lock lock{mtx};
|
||||
|
||||
const auto *val = std::getenv(variable.c_str());
|
||||
auto ret = std::string(val == nullptr ? "" : val);
|
||||
return ret;
|
||||
}
|
||||
|
||||
auto get_file_time_now() -> std::uint64_t {
|
||||
#ifdef _WIN32
|
||||
SYSTEMTIME st{};
|
||||
::GetSystemTime(&st);
|
||||
FILETIME ft{};
|
||||
::SystemTimeToFileTime(&st, &ft);
|
||||
return static_cast<std::uint64_t>(((LARGE_INTEGER *)&ft)->QuadPart);
|
||||
#else
|
||||
return get_time_now();
|
||||
#endif
|
||||
}
|
||||
|
||||
void get_local_time_now(struct tm &local_time) {
|
||||
memset(&local_time, 0, sizeof(local_time));
|
||||
|
||||
const auto now =
|
||||
std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
|
||||
#ifdef _WIN32
|
||||
localtime_s(&local_time, &now);
|
||||
#else
|
||||
static std::mutex mtx{};
|
||||
mutex_lock lock{mtx};
|
||||
|
||||
const auto *tmp = std::localtime(&now);
|
||||
if (tmp != nullptr) {
|
||||
memcpy(&local_time, tmp, sizeof(local_time));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
auto get_next_available_port(std::uint16_t first_port,
|
||||
std::uint16_t &available_port) -> bool {
|
||||
using namespace boost::asio;
|
||||
using ip::tcp;
|
||||
|
||||
boost::system::error_code error_code{};
|
||||
while (first_port != 0U) {
|
||||
io_service svc{};
|
||||
tcp::acceptor acceptor(svc);
|
||||
acceptor.open(tcp::v4(), error_code) ||
|
||||
acceptor.bind({tcp::v4(), first_port}, error_code);
|
||||
if (not error_code) {
|
||||
break;
|
||||
}
|
||||
|
||||
++first_port;
|
||||
}
|
||||
|
||||
if (not error_code) {
|
||||
available_port = first_port;
|
||||
}
|
||||
|
||||
return not error_code;
|
||||
}
|
||||
|
||||
auto get_time_now() -> std::uint64_t {
|
||||
#ifdef _WIN32
|
||||
return static_cast<std::uint64_t>(
|
||||
std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()));
|
||||
#else
|
||||
#if __APPLE__
|
||||
return std::chrono::nanoseconds(
|
||||
std::chrono::system_clock::now().time_since_epoch())
|
||||
.count();
|
||||
#else
|
||||
return static_cast<std::uint64_t>(
|
||||
std::chrono::nanoseconds(
|
||||
std::chrono::high_resolution_clock::now().time_since_epoch())
|
||||
.count());
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
auto reset_curl(CURL *curl_handle) -> CURL * {
|
||||
curl_easy_reset(curl_handle);
|
||||
#if __APPLE__
|
||||
curl_easy_setopt(curl_handle, CURLOPT_NOSIGNAL, 1);
|
||||
#endif
|
||||
return curl_handle;
|
||||
}
|
||||
|
||||
auto retryable_action(const std::function<bool()> &action) -> bool {
|
||||
static constexpr const auto retry_count = 20U;
|
||||
|
||||
auto succeeded = false;
|
||||
for (std::uint8_t i = 0U; not(succeeded = action()) && (i < retry_count);
|
||||
i++) {
|
||||
std::this_thread::sleep_for(100ms);
|
||||
}
|
||||
return succeeded;
|
||||
}
|
||||
|
||||
void spin_wait_for_mutex(std::function<bool()> complete,
|
||||
std::condition_variable &cond, std::mutex &mtx,
|
||||
const std::string &text) {
|
||||
while (not complete()) {
|
||||
unique_mutex_lock lock(mtx);
|
||||
if (not complete()) {
|
||||
if (not text.empty()) {
|
||||
/* event_system::instance().raise<DebugLog>(__FUNCTION__,
|
||||
* "spin_wait_for_mutex", text); */
|
||||
}
|
||||
cond.wait_for(lock, 1s);
|
||||
}
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
void spin_wait_for_mutex(bool &complete, std::condition_variable &cond,
|
||||
std::mutex &mtx, const std::string &text) {
|
||||
while (not complete) {
|
||||
unique_mutex_lock lock(mtx);
|
||||
if (not complete) {
|
||||
if (not text.empty()) {
|
||||
/* event_system::instance().raise<DebugLog>(__FUNCTION__,
|
||||
* "spin_wait_for_mutex", text); */
|
||||
}
|
||||
cond.wait_for(lock, 1s);
|
||||
}
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
} // namespace repertory::utils
|
||||
|
@ -1,240 +1,241 @@
|
||||
/*
|
||||
Copyright <2018-2023> <scott.e.graves@protonmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#ifdef _WIN32
|
||||
|
||||
#include "utils/windows/windows_utils.hpp"
|
||||
|
||||
#include "types/startup_exception.hpp"
|
||||
#include "utils/com_init_wrapper.hpp"
|
||||
#include "utils/string_utils.hpp"
|
||||
|
||||
#ifndef STATUS_DEVICE_INSUFFICIENT_RESOURCES
|
||||
#define STATUS_DEVICE_INSUFFICIENT_RESOURCES ((NTSTATUS)0xC0000468L)
|
||||
#endif
|
||||
|
||||
namespace repertory::utils {
|
||||
auto from_api_error(const api_error &e) -> NTSTATUS {
|
||||
switch (e) {
|
||||
case api_error::access_denied:
|
||||
return STATUS_ACCESS_DENIED;
|
||||
case api_error::bad_address:
|
||||
return STATUS_INVALID_ADDRESS;
|
||||
case api_error::buffer_too_small:
|
||||
return STATUS_BUFFER_TOO_SMALL;
|
||||
case api_error::buffer_overflow:
|
||||
return STATUS_BUFFER_OVERFLOW;
|
||||
case api_error::directory_end_of_files:
|
||||
return STATUS_NO_MORE_FILES;
|
||||
case api_error::directory_exists:
|
||||
return STATUS_OBJECT_NAME_EXISTS;
|
||||
case api_error::directory_not_empty:
|
||||
return STATUS_DIRECTORY_NOT_EMPTY;
|
||||
case api_error::directory_not_found:
|
||||
return STATUS_OBJECT_NAME_NOT_FOUND;
|
||||
case api_error::download_failed:
|
||||
return FspNtStatusFromWin32(ERROR_INTERNAL_ERROR);
|
||||
case api_error::error:
|
||||
return FspNtStatusFromWin32(ERROR_INTERNAL_ERROR);
|
||||
case api_error::invalid_handle:
|
||||
return STATUS_INVALID_HANDLE;
|
||||
case api_error::invalid_operation:
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
case api_error::item_exists:
|
||||
return FspNtStatusFromWin32(ERROR_FILE_EXISTS);
|
||||
case api_error::file_in_use:
|
||||
return STATUS_DEVICE_BUSY;
|
||||
case api_error::incompatible_version:
|
||||
return STATUS_CLIENT_SERVER_PARAMETERS_INVALID;
|
||||
case api_error::item_not_found:
|
||||
return STATUS_OBJECT_NAME_NOT_FOUND;
|
||||
case api_error::no_disk_space:
|
||||
return STATUS_DEVICE_INSUFFICIENT_RESOURCES;
|
||||
case api_error::os_error:
|
||||
return FspNtStatusFromWin32(::GetLastError());
|
||||
case api_error::out_of_memory:
|
||||
return STATUS_NO_MEMORY;
|
||||
case api_error::permission_denied:
|
||||
return FspNtStatusFromWin32(ERROR_ACCESS_DENIED);
|
||||
case api_error::success:
|
||||
return 0;
|
||||
case api_error::not_supported:
|
||||
return FspNtStatusFromWin32(ERROR_INTERNAL_ERROR);
|
||||
case api_error::not_implemented:
|
||||
return STATUS_NOT_IMPLEMENTED;
|
||||
case api_error::upload_failed:
|
||||
return FspNtStatusFromWin32(ERROR_INTERNAL_ERROR);
|
||||
default:
|
||||
return FspNtStatusFromWin32(ERROR_INTERNAL_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
auto get_last_error_code() -> DWORD { return ::GetLastError(); }
|
||||
|
||||
auto get_local_app_data_directory() -> const std::string & {
|
||||
static std::string app_data = ([]() -> std::string {
|
||||
com_init_wrapper cw;
|
||||
PWSTR local_app_data{};
|
||||
if (SUCCEEDED(::SHGetKnownFolderPath(FOLDERID_LocalAppData, 0, nullptr,
|
||||
&local_app_data))) {
|
||||
auto app_data = utils::string::to_utf8(local_app_data);
|
||||
::CoTaskMemFree(local_app_data);
|
||||
return app_data;
|
||||
}
|
||||
|
||||
throw startup_exception("unable to detect local application data folder");
|
||||
})();
|
||||
|
||||
return app_data;
|
||||
}
|
||||
|
||||
auto get_accessed_time_from_meta(const api_meta_map &meta) -> std::uint64_t {
|
||||
return utils::string::to_uint64(meta.at(META_ACCESSED));
|
||||
}
|
||||
|
||||
auto get_changed_time_from_meta(const api_meta_map &meta) -> std::uint64_t {
|
||||
return utils::string::to_uint64(meta.at(META_MODIFIED));
|
||||
}
|
||||
|
||||
auto get_creation_time_from_meta(const api_meta_map &meta) -> std::uint64_t {
|
||||
return utils::string::to_uint64(meta.at(META_CREATION));
|
||||
}
|
||||
|
||||
auto get_written_time_from_meta(const api_meta_map &meta) -> std::uint64_t {
|
||||
return utils::string::to_uint64(meta.at(META_WRITTEN));
|
||||
}
|
||||
|
||||
auto get_thread_id() -> std::uint64_t {
|
||||
return static_cast<std::uint64_t>(::GetCurrentThreadId());
|
||||
}
|
||||
|
||||
auto is_process_elevated() -> bool {
|
||||
auto ret = false;
|
||||
HANDLE token = INVALID_HANDLE_VALUE;
|
||||
if (::OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token)) {
|
||||
TOKEN_ELEVATION te{};
|
||||
DWORD sz = sizeof(te);
|
||||
if (::GetTokenInformation(token, TokenElevation, &te, sizeof(te), &sz)) {
|
||||
ret = (te.TokenIsElevated != 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (token != INVALID_HANDLE_VALUE) {
|
||||
::CloseHandle(token);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
auto run_process_elevated(int argc, char *argv[]) -> int {
|
||||
std::cout << "Elevating Process" << std::endl;
|
||||
std::string parameters = "-hidden";
|
||||
for (int i = 1; i < argc; i++) {
|
||||
parameters += (parameters.empty() ? argv[i] : " " + std::string(argv[i]));
|
||||
}
|
||||
|
||||
char full_path[MAX_PATH] = {0};
|
||||
if (::GetModuleFileName(nullptr, full_path, MAX_PATH)) {
|
||||
SHELLEXECUTEINFO sei{};
|
||||
sei.fMask = SEE_MASK_NOCLOSEPROCESS;
|
||||
sei.cbSize = sizeof(sei);
|
||||
sei.lpVerb = "runas";
|
||||
sei.lpFile = &full_path[0];
|
||||
sei.lpParameters = ¶meters[0];
|
||||
sei.hwnd = nullptr;
|
||||
sei.nShow = SW_NORMAL;
|
||||
if (::ShellExecuteEx(&sei)) {
|
||||
::WaitForSingleObject(sei.hProcess, INFINITE);
|
||||
DWORD exit_code = 0u;
|
||||
::GetExitCodeProcess(sei.hProcess, &exit_code);
|
||||
::CloseHandle(sei.hProcess);
|
||||
return exit_code;
|
||||
}
|
||||
}
|
||||
|
||||
return ::GetLastError();
|
||||
}
|
||||
|
||||
void set_last_error_code(DWORD error_code) { ::SetLastError(error_code); }
|
||||
|
||||
// https://stackoverflow.com/questions/321849/strptime-equivalent-on-windows
|
||||
auto strptime(const char *s, const char *f, struct tm *tm) -> const char * {
|
||||
std::istringstream input(s);
|
||||
input.imbue(std::locale(setlocale(LC_ALL, nullptr)));
|
||||
input >> std::get_time(tm, f);
|
||||
if (input.fail()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return reinterpret_cast<const char *>(s + input.tellg());
|
||||
}
|
||||
|
||||
auto unix_access_mask_to_windows(std::int32_t mask) -> int {
|
||||
if (mask & 1) {
|
||||
mask -= 1;
|
||||
if (not mask) {
|
||||
mask = 4;
|
||||
}
|
||||
}
|
||||
|
||||
return mask & 6;
|
||||
}
|
||||
|
||||
auto unix_open_flags_to_flags_and_perms(const remote::file_mode & /*mode*/,
|
||||
const remote::open_flags &flags,
|
||||
std::int32_t &perms) -> int {
|
||||
auto ret = _O_BINARY | _O_RANDOM;
|
||||
ret |= (((flags & remote::open_flags::create) == remote::open_flags::create)
|
||||
? _O_CREAT
|
||||
: 0);
|
||||
ret |= (((flags & remote::open_flags::excl) == remote::open_flags::excl)
|
||||
? _O_EXCL
|
||||
: 0);
|
||||
ret |=
|
||||
(((flags & remote::open_flags::truncate) == remote::open_flags::truncate)
|
||||
? _O_TRUNC
|
||||
: 0);
|
||||
ret |= (((flags & remote::open_flags::append) == remote::open_flags::append)
|
||||
? _O_APPEND
|
||||
: 0);
|
||||
ret |= (((flags & remote::open_flags::temp_file) ==
|
||||
remote::open_flags::temp_file)
|
||||
? _O_TEMPORARY
|
||||
: 0);
|
||||
ret |= ((flags & remote::open_flags::write_only) ==
|
||||
remote::open_flags::write_only)
|
||||
? _O_WRONLY
|
||||
: ((flags & remote::open_flags::read_write) ==
|
||||
remote::open_flags::read_write)
|
||||
? _O_RDWR
|
||||
: _O_RDONLY;
|
||||
|
||||
perms = _S_IREAD | _S_IWRITE;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
auto time64_to_unix_time(const __time64_t &t) -> remote::file_time {
|
||||
return t * NANOS_PER_SECOND;
|
||||
}
|
||||
} // namespace repertory::utils
|
||||
|
||||
#endif // _WIN32
|
||||
/*
|
||||
Copyright <2018-2023> <scott.e.graves@protonmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#ifdef _WIN32
|
||||
|
||||
#include "utils/windows/windows_utils.hpp"
|
||||
|
||||
#include "types/startup_exception.hpp"
|
||||
#include "utils/com_init_wrapper.hpp"
|
||||
#include "utils/string_utils.hpp"
|
||||
|
||||
#ifndef STATUS_DEVICE_INSUFFICIENT_RESOURCES
|
||||
#define STATUS_DEVICE_INSUFFICIENT_RESOURCES ((NTSTATUS)0xC0000468L)
|
||||
#endif
|
||||
|
||||
namespace repertory::utils {
|
||||
auto from_api_error(const api_error &e) -> NTSTATUS {
|
||||
switch (e) {
|
||||
case api_error::access_denied:
|
||||
return STATUS_ACCESS_DENIED;
|
||||
case api_error::bad_address:
|
||||
return STATUS_INVALID_ADDRESS;
|
||||
case api_error::buffer_too_small:
|
||||
return STATUS_BUFFER_TOO_SMALL;
|
||||
case api_error::buffer_overflow:
|
||||
return STATUS_BUFFER_OVERFLOW;
|
||||
case api_error::directory_end_of_files:
|
||||
return STATUS_NO_MORE_FILES;
|
||||
case api_error::directory_exists:
|
||||
return STATUS_OBJECT_NAME_EXISTS;
|
||||
case api_error::directory_not_empty:
|
||||
return STATUS_DIRECTORY_NOT_EMPTY;
|
||||
case api_error::directory_not_found:
|
||||
return STATUS_OBJECT_NAME_NOT_FOUND;
|
||||
case api_error::download_failed:
|
||||
return FspNtStatusFromWin32(ERROR_INTERNAL_ERROR);
|
||||
case api_error::error:
|
||||
return FspNtStatusFromWin32(ERROR_INTERNAL_ERROR);
|
||||
case api_error::invalid_handle:
|
||||
return STATUS_INVALID_HANDLE;
|
||||
case api_error::invalid_operation:
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
case api_error::item_exists:
|
||||
return FspNtStatusFromWin32(ERROR_FILE_EXISTS);
|
||||
case api_error::file_in_use:
|
||||
return STATUS_DEVICE_BUSY;
|
||||
case api_error::incompatible_version:
|
||||
return STATUS_CLIENT_SERVER_PARAMETERS_INVALID;
|
||||
case api_error::item_not_found:
|
||||
return STATUS_OBJECT_NAME_NOT_FOUND;
|
||||
case api_error::no_disk_space:
|
||||
return STATUS_DEVICE_INSUFFICIENT_RESOURCES;
|
||||
case api_error::os_error:
|
||||
return FspNtStatusFromWin32(::GetLastError());
|
||||
case api_error::out_of_memory:
|
||||
return STATUS_NO_MEMORY;
|
||||
case api_error::permission_denied:
|
||||
return FspNtStatusFromWin32(ERROR_ACCESS_DENIED);
|
||||
case api_error::success:
|
||||
return 0;
|
||||
case api_error::not_supported:
|
||||
return FspNtStatusFromWin32(ERROR_INTERNAL_ERROR);
|
||||
case api_error::not_implemented:
|
||||
return STATUS_NOT_IMPLEMENTED;
|
||||
case api_error::upload_failed:
|
||||
return FspNtStatusFromWin32(ERROR_INTERNAL_ERROR);
|
||||
default:
|
||||
return FspNtStatusFromWin32(ERROR_INTERNAL_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
auto get_last_error_code() -> DWORD { return ::GetLastError(); }
|
||||
|
||||
auto get_local_app_data_directory() -> const std::string & {
|
||||
static std::string app_data = ([]() -> std::string {
|
||||
com_init_wrapper cw;
|
||||
PWSTR local_app_data{};
|
||||
if (SUCCEEDED(::SHGetKnownFolderPath(FOLDERID_LocalAppData, 0, nullptr,
|
||||
&local_app_data))) {
|
||||
auto app_data = utils::string::to_utf8(local_app_data);
|
||||
::CoTaskMemFree(local_app_data);
|
||||
return app_data;
|
||||
}
|
||||
|
||||
throw startup_exception("unable to detect local application data folder");
|
||||
})();
|
||||
|
||||
return app_data;
|
||||
}
|
||||
|
||||
auto get_accessed_time_from_meta(const api_meta_map &meta) -> std::uint64_t {
|
||||
return utils::string::to_uint64(meta.at(META_ACCESSED));
|
||||
}
|
||||
|
||||
auto get_changed_time_from_meta(const api_meta_map &meta) -> std::uint64_t {
|
||||
return utils::string::to_uint64(meta.at(META_MODIFIED));
|
||||
}
|
||||
|
||||
auto get_creation_time_from_meta(const api_meta_map &meta) -> std::uint64_t {
|
||||
return utils::string::to_uint64(meta.at(META_CREATION));
|
||||
}
|
||||
|
||||
auto get_written_time_from_meta(const api_meta_map &meta) -> std::uint64_t {
|
||||
return utils::string::to_uint64(meta.at(META_WRITTEN));
|
||||
}
|
||||
|
||||
auto get_thread_id() -> std::uint64_t {
|
||||
return static_cast<std::uint64_t>(::GetCurrentThreadId());
|
||||
}
|
||||
|
||||
auto is_process_elevated() -> bool {
|
||||
auto ret = false;
|
||||
HANDLE token = INVALID_HANDLE_VALUE;
|
||||
if (::OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token)) {
|
||||
TOKEN_ELEVATION te{};
|
||||
DWORD sz = sizeof(te);
|
||||
if (::GetTokenInformation(token, TokenElevation, &te, sizeof(te), &sz)) {
|
||||
ret = (te.TokenIsElevated != 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (token != INVALID_HANDLE_VALUE) {
|
||||
::CloseHandle(token);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
auto run_process_elevated(std::vector<const char *> args) -> int {
|
||||
std::cout << "Elevating Process" << std::endl;
|
||||
std::string parameters = "-hidden";
|
||||
for (std::size_t i = 1U; i < args.size(); i++) {
|
||||
parameters +=
|
||||
(parameters.empty() ? args.at(i) : " " + std::string(args.at(i)));
|
||||
}
|
||||
|
||||
char full_path[MAX_PATH] = {0};
|
||||
if (::GetModuleFileName(nullptr, full_path, MAX_PATH)) {
|
||||
SHELLEXECUTEINFO sei{};
|
||||
sei.fMask = SEE_MASK_NOCLOSEPROCESS;
|
||||
sei.cbSize = sizeof(sei);
|
||||
sei.lpVerb = "runas";
|
||||
sei.lpFile = &full_path[0];
|
||||
sei.lpParameters = ¶meters[0];
|
||||
sei.hwnd = nullptr;
|
||||
sei.nShow = SW_NORMAL;
|
||||
if (::ShellExecuteEx(&sei)) {
|
||||
::WaitForSingleObject(sei.hProcess, INFINITE);
|
||||
DWORD exit_code = 0u;
|
||||
::GetExitCodeProcess(sei.hProcess, &exit_code);
|
||||
::CloseHandle(sei.hProcess);
|
||||
return exit_code;
|
||||
}
|
||||
}
|
||||
|
||||
return ::GetLastError();
|
||||
}
|
||||
|
||||
void set_last_error_code(DWORD error_code) { ::SetLastError(error_code); }
|
||||
|
||||
// https://stackoverflow.com/questions/321849/strptime-equivalent-on-windows
|
||||
auto strptime(const char *s, const char *f, struct tm *tm) -> const char * {
|
||||
std::istringstream input(s);
|
||||
input.imbue(std::locale(setlocale(LC_ALL, nullptr)));
|
||||
input >> std::get_time(tm, f);
|
||||
if (input.fail()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return reinterpret_cast<const char *>(s + input.tellg());
|
||||
}
|
||||
|
||||
auto unix_access_mask_to_windows(std::int32_t mask) -> int {
|
||||
if (mask & 1) {
|
||||
mask -= 1;
|
||||
if (not mask) {
|
||||
mask = 4;
|
||||
}
|
||||
}
|
||||
|
||||
return mask & 6;
|
||||
}
|
||||
|
||||
auto unix_open_flags_to_flags_and_perms(const remote::file_mode & /*mode*/,
|
||||
const remote::open_flags &flags,
|
||||
std::int32_t &perms) -> int {
|
||||
auto ret = _O_BINARY | _O_RANDOM;
|
||||
ret |= (((flags & remote::open_flags::create) == remote::open_flags::create)
|
||||
? _O_CREAT
|
||||
: 0);
|
||||
ret |= (((flags & remote::open_flags::excl) == remote::open_flags::excl)
|
||||
? _O_EXCL
|
||||
: 0);
|
||||
ret |=
|
||||
(((flags & remote::open_flags::truncate) == remote::open_flags::truncate)
|
||||
? _O_TRUNC
|
||||
: 0);
|
||||
ret |= (((flags & remote::open_flags::append) == remote::open_flags::append)
|
||||
? _O_APPEND
|
||||
: 0);
|
||||
ret |= (((flags & remote::open_flags::temp_file) ==
|
||||
remote::open_flags::temp_file)
|
||||
? _O_TEMPORARY
|
||||
: 0);
|
||||
ret |= ((flags & remote::open_flags::write_only) ==
|
||||
remote::open_flags::write_only)
|
||||
? _O_WRONLY
|
||||
: ((flags & remote::open_flags::read_write) ==
|
||||
remote::open_flags::read_write)
|
||||
? _O_RDWR
|
||||
: _O_RDONLY;
|
||||
|
||||
perms = _S_IREAD | _S_IWRITE;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
auto time64_to_unix_time(const __time64_t &t) -> remote::file_time {
|
||||
return t * NANOS_PER_SECOND;
|
||||
}
|
||||
} // namespace repertory::utils
|
||||
|
||||
#endif // _WIN32
|
||||
|
Reference in New Issue
Block a user