initial commit
This commit is contained in:
236
src/utils/cli_utils.cpp
Normal file
236
src/utils/cli_utils.cpp
Normal file
@ -0,0 +1,236 @@
|
||||
/*
|
||||
Copyright <2018-2022> <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;
|
||||
auto success = false;
|
||||
for (auto i = 0; not(success = utils::file::read_json_file(cfg_file_path, data)) && (i < 20);
|
||||
i++) {
|
||||
std::this_thread::sleep_for(100ms);
|
||||
}
|
||||
|
||||
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>();
|
||||
}
|
||||
}
|
||||
|
||||
provider_type get_provider_type_from_args(const int &argc, char *argv[]) {
|
||||
auto pt = provider_type::unknown;
|
||||
#if defined(REPERTORY_ENABLE_S3)
|
||||
if ((pt == provider_type::unknown) && (has_option(argc, argv, options::s3_option))) {
|
||||
pt = provider_type::s3;
|
||||
}
|
||||
#endif // defined(REPERTORY_ENABLE_S3)
|
||||
#if defined(REPERTORY_ENABLE_SKYNET)
|
||||
if ((pt == provider_type::unknown) && (has_option(argc, argv, options::skynet_option))) {
|
||||
pt = provider_type::skynet;
|
||||
}
|
||||
#endif // defined(REPERTORY_ENABLE_SKYNET)
|
||||
if ((pt == provider_type::unknown) && (has_option(argc, argv, options::remote_mount_option))) {
|
||||
pt = provider_type::remote;
|
||||
}
|
||||
|
||||
if (pt == provider_type::unknown) {
|
||||
pt = provider_type::sia;
|
||||
}
|
||||
|
||||
return pt;
|
||||
}
|
||||
bool has_option(const int &argc, char *argv[], const std::string &option_name) {
|
||||
auto ret = false;
|
||||
for (int i = 0; not ret && (i < argc); i++) {
|
||||
ret = (option_name == argv[i]);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool has_option(const int &argc, char *argv[], const option &opt) {
|
||||
return has_option(argc, argv, opt[0u]) || has_option(argc, argv, opt[1u]);
|
||||
}
|
||||
|
||||
std::vector<std::string> parse_option(const int &argc, char *argv[], const std::string &option_name,
|
||||
std::uint8_t count) {
|
||||
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;
|
||||
}
|
||||
|
||||
exit_code parse_string_option(const int &argc, char **argv, const option &opt, std::string &value) {
|
||||
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;
|
||||
}
|
||||
|
||||
std::vector<std::string> parse_drive_options(const int &argc, char **argv, provider_type &pt,
|
||||
std::string &data_directory) {
|
||||
// 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 defined(REPERTORY_ENABLE_SKYNET)
|
||||
if ((fuse_option.find("sk") == 0) || (fuse_option.find("skynet") == 0)) {
|
||||
pt = provider_type::skynet;
|
||||
}
|
||||
#endif // defined(REPERTORY_ENABLE_SKYNET)
|
||||
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--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#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
|
244
src/utils/encrypting_reader.cpp
Normal file
244
src/utils/encrypting_reader.cpp
Normal file
@ -0,0 +1,244 @@
|
||||
/*
|
||||
Copyright <2018-2022> <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/events.hpp"
|
||||
#include "events/event_system.hpp"
|
||||
#include "utils/encryption.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:
|
||||
pos_type seekoff(off_type off, std::ios_base::seekdir dir,
|
||||
std::ios_base::openmode which = std::ios_base::out |
|
||||
std::ios_base::in) 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 pos_type(reinterpret_cast<std::uintptr_t>(gptr()));
|
||||
}
|
||||
return pos_type(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);
|
||||
}
|
||||
|
||||
pos_type seekpos(pos_type pos, std::ios_base::openmode which = std::ios_base::out |
|
||||
std::ios_base::in) override {
|
||||
return seekoff(pos, std::ios_base::beg, which);
|
||||
}
|
||||
|
||||
int_type uflow() override {
|
||||
auto ret = underflow();
|
||||
if (ret == traits_type::eof()) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
gbump(1);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int_type underflow() 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;
|
||||
}
|
||||
|
||||
std::streamsize xsgetn(char *ptr, std::streamsize count) 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_ = ([]() {
|
||||
CryptoPP::XChaCha20Poly1305::Encryption enc;
|
||||
return enc.IVSize() + enc.DigestSize();
|
||||
})();
|
||||
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,
|
||||
const bool &stop_requested, const std::string &token,
|
||||
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, source_file_);
|
||||
if (res != api_error::success) {
|
||||
throw std::runtime_error("file open failed: " + source_path + ':' + api_error_to_string(res));
|
||||
}
|
||||
|
||||
std::vector<char> 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);
|
||||
|
||||
std::uint64_t file_size{};
|
||||
if (not utils::file::get_file_size(source_path, file_size)) {
|
||||
throw std::runtime_error("get file size failed: " + 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_);
|
||||
}
|
||||
|
||||
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_),
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
std::uint64_t encrypting_reader::calculate_decrypted_size(const std::uint64_t &total_size) {
|
||||
return total_size - (utils::divide_with_ceiling(
|
||||
total_size, static_cast<std::uint64_t>(get_encrypted_chunk_size())) *
|
||||
get_header_size());
|
||||
}
|
||||
|
||||
std::shared_ptr<encrypting_reader::iostream> encrypting_reader::create_iostream() const {
|
||||
return std::make_shared<encrypting_reader_iostream>(
|
||||
std::make_unique<encrypting_streambuf>(*this));
|
||||
}
|
||||
|
||||
size_t encrypting_reader::reader_function(char *buffer, size_t size, size_t nitems) {
|
||||
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];
|
||||
std::vector<char> 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(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) {
|
||||
event_system::instance().raise<repertory_exception>(__FUNCTION__,
|
||||
e.what() ? e.what() : "unkown error");
|
||||
ret = false;
|
||||
}
|
||||
}
|
||||
|
||||
return stop_requested_ ? CURL_READFUNC_ABORT : ret ? total_read : error_return_;
|
||||
}
|
||||
} // namespace repertory::utils::encryption
|
95
src/utils/encryption.cpp
Normal file
95
src/utils/encryption.cpp
Normal file
@ -0,0 +1,95 @@
|
||||
/*
|
||||
Copyright <2018-2022> <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/events.hpp"
|
||||
#include "events/event_system.hpp"
|
||||
#include "utils/encrypting_reader.hpp"
|
||||
#include "utils/utils.hpp"
|
||||
|
||||
namespace repertory::utils::encryption {
|
||||
api_error decrypt_file_name(const std::string &encryption_token, std::string &file_name) {
|
||||
std::vector<char> 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;
|
||||
}
|
||||
|
||||
CryptoPP::SecByteBlock generate_key(const std::string &encryption_token) {
|
||||
CryptoPP::SecByteBlock key(CryptoPP::SHA256::DIGESTSIZE);
|
||||
CryptoPP::SHA256().CalculateDigest(
|
||||
reinterpret_cast<CryptoPP::byte *>(&key[0u]),
|
||||
reinterpret_cast<const CryptoPP::byte *>(encryption_token.c_str()),
|
||||
strnlen(encryption_token.c_str(), encryption_token.size()));
|
||||
|
||||
return key;
|
||||
}
|
||||
|
||||
api_error read_encrypted_range(
|
||||
const http_range &range, const CryptoPP::SecByteBlock &key,
|
||||
const std::function<api_error(std::vector<char> &ct, const std::uint64_t &start_offset,
|
||||
const std::uint64_t &end_offset)> &reader,
|
||||
const std::uint64_t &total_size, std::vector<char> &data) {
|
||||
static constexpr const auto &encrypted_chunk_size =
|
||||
utils::encryption::encrypting_reader::get_encrypted_chunk_size();
|
||||
static constexpr const auto &data_chunk_size =
|
||||
utils::encryption::encrypting_reader::get_data_chunk_size();
|
||||
static constexpr 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++) {
|
||||
std::vector<char> 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;
|
||||
}
|
||||
|
||||
std::vector<char> 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
|
612
src/utils/file_utils.cpp
Normal file
612
src/utils/file_utils.cpp
Normal file
@ -0,0 +1,612 @@
|
||||
/*
|
||||
Copyright <2018-2022> <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/file_utils.hpp"
|
||||
#include "utils/path_utils.hpp"
|
||||
#include "utils/string_utils.hpp"
|
||||
#include "utils/utils.hpp"
|
||||
|
||||
namespace repertory::utils::file {
|
||||
api_error assign_and_get_native_file(filesystem_item &fi, native_file_ptr &nf) {
|
||||
recur_mutex_lock l(*fi.lock);
|
||||
if (fi.handle == REPERTORY_INVALID_HANDLE) {
|
||||
const auto res = native_file::create_or_open(fi.source_path, nf);
|
||||
if (res != api_error::success) {
|
||||
return res;
|
||||
}
|
||||
|
||||
fi.handle = nf->get_handle();
|
||||
} else {
|
||||
nf = native_file::attach(fi.handle);
|
||||
}
|
||||
|
||||
return api_error::success;
|
||||
}
|
||||
|
||||
std::uint64_t calculate_used_space(std::string path, const bool &recursive) {
|
||||
path = utils::path::absolute(path);
|
||||
std::uint64_t ret = 0u;
|
||||
#ifdef _WIN32
|
||||
WIN32_FIND_DATA fd = {0};
|
||||
const auto search = utils::path::combine(path, {"*.*"});
|
||||
auto find = ::FindFirstFile(search.c_str(), &fd);
|
||||
if (find != INVALID_HANDLE_VALUE) {
|
||||
do {
|
||||
const auto file_name = std::string(fd.cFileName);
|
||||
if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
|
||||
if (recursive && (file_name != ".") && (file_name != "..")) {
|
||||
ret += calculate_used_space(utils::path::combine(path, {file_name}), recursive);
|
||||
}
|
||||
} else {
|
||||
std::uint64_t file_size{};
|
||||
if (get_file_size(utils::path::combine(path, {file_name}), file_size)) {
|
||||
ret += file_size;
|
||||
}
|
||||
}
|
||||
} while (::FindNextFile(find, &fd) != 0);
|
||||
::FindClose(find);
|
||||
}
|
||||
#else
|
||||
auto *root = opendir(path.c_str());
|
||||
if (root) {
|
||||
struct dirent *de{};
|
||||
while ((de = readdir(root)) != nullptr) {
|
||||
if (de->d_type == DT_DIR) {
|
||||
if (recursive && (strcmp(de->d_name, ".") != 0) && (strcmp(de->d_name, "..") != 0)) {
|
||||
ret += calculate_used_space(utils::path::combine(path, {de->d_name}), recursive);
|
||||
}
|
||||
} else {
|
||||
std::uint64_t file_size{};
|
||||
if (get_file_size(utils::path::combine(path, {de->d_name}), file_size)) {
|
||||
ret += file_size;
|
||||
}
|
||||
}
|
||||
}
|
||||
closedir(root);
|
||||
}
|
||||
#endif
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void change_to_process_directory() {
|
||||
#ifdef _WIN32
|
||||
std::string file_name;
|
||||
file_name.resize(MAX_PATH);
|
||||
::GetModuleFileNameA(nullptr, &file_name[0u], static_cast<DWORD>(file_name.size()));
|
||||
|
||||
std::string path = file_name.c_str();
|
||||
::PathRemoveFileSpecA(&path[0u]);
|
||||
::SetCurrentDirectoryA(&path[0u]);
|
||||
#else
|
||||
std::string path;
|
||||
path.resize(PATH_MAX + 1);
|
||||
#ifdef __APPLE__
|
||||
proc_pidpath(getpid(), &path[0u], path.size());
|
||||
#else
|
||||
readlink("/proc/self/exe", &path[0u], path.size());
|
||||
#endif
|
||||
path = utils::path::get_parent_directory(path);
|
||||
chdir(path.c_str());
|
||||
#endif
|
||||
}
|
||||
|
||||
bool copy_file(std::string from_path, std::string to_path) {
|
||||
from_path = utils::path::absolute(from_path);
|
||||
to_path = utils::path::absolute(to_path);
|
||||
if (is_file(from_path) && not is_directory(to_path)) {
|
||||
boost::system::error_code ec{};
|
||||
boost::filesystem::copy_file(from_path, to_path, ec);
|
||||
return not ec.failed();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool copy_directory_recursively(std::string from_path, std::string to_path) {
|
||||
from_path = utils::path::absolute(from_path);
|
||||
to_path = utils::path::absolute(to_path);
|
||||
auto ret = create_full_directory_path(to_path);
|
||||
if (ret) {
|
||||
#ifdef _WIN32
|
||||
WIN32_FIND_DATA fd = {0};
|
||||
const auto search = utils::path::combine(from_path, {"*.*"});
|
||||
auto find = ::FindFirstFile(search.c_str(), &fd);
|
||||
if (find != INVALID_HANDLE_VALUE) {
|
||||
ret = true;
|
||||
do {
|
||||
if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
|
||||
if ((std::string(fd.cFileName) != ".") && (std::string(fd.cFileName) != "..")) {
|
||||
ret = copy_directory_recursively(utils::path::combine(from_path, {fd.cFileName}),
|
||||
utils::path::combine(to_path, {fd.cFileName}));
|
||||
}
|
||||
} else {
|
||||
ret = copy_file(utils::path::combine(from_path, {fd.cFileName}),
|
||||
utils::path::combine(to_path, {fd.cFileName}));
|
||||
}
|
||||
} while (ret && (::FindNextFile(find, &fd) != 0));
|
||||
|
||||
::FindClose(find);
|
||||
}
|
||||
#else
|
||||
auto *root = opendir(from_path.c_str());
|
||||
if (root) {
|
||||
struct dirent *de{};
|
||||
while (ret && (de = readdir(root))) {
|
||||
if (de->d_type == DT_DIR) {
|
||||
if ((strcmp(de->d_name, ".") != 0) && (strcmp(de->d_name, "..") != 0)) {
|
||||
ret = copy_directory_recursively(utils::path::combine(from_path, {de->d_name}),
|
||||
utils::path::combine(to_path, {de->d_name}));
|
||||
}
|
||||
} else {
|
||||
ret = copy_file(utils::path::combine(from_path, {de->d_name}),
|
||||
utils::path::combine(to_path, {de->d_name}));
|
||||
}
|
||||
}
|
||||
|
||||
closedir(root);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool create_full_directory_path(std::string path) {
|
||||
#ifdef _WIN32
|
||||
const auto unicode_path = utils::string::from_utf8(utils::path::absolute(path));
|
||||
return is_directory(path) ||
|
||||
(::SHCreateDirectory(nullptr, unicode_path.c_str()) == ERROR_SUCCESS);
|
||||
#else
|
||||
auto ret = true;
|
||||
const auto paths =
|
||||
utils::string::split(utils::path::absolute(path), utils::path::directory_seperator[0u]);
|
||||
std::string current_path;
|
||||
for (std::size_t i = 0u; ret && (i < paths.size()); i++) {
|
||||
if (paths[i].empty()) { // Skip root
|
||||
current_path = utils::path::directory_seperator;
|
||||
} else {
|
||||
current_path = utils::path::combine(current_path, {paths[i]});
|
||||
const auto status = mkdir(current_path.c_str(), S_IRWXU);
|
||||
ret = ((status == 0) || (errno == EEXIST));
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool delete_directory(std::string path, const bool &recursive) {
|
||||
if (recursive) {
|
||||
return delete_directory_recursively(path);
|
||||
}
|
||||
|
||||
path = utils::path::absolute(path);
|
||||
#ifdef _WIN32
|
||||
return (not is_directory(path) ||
|
||||
utils::retryable_action([&]() -> bool { return !!::RemoveDirectoryA(path.c_str()); }));
|
||||
#else
|
||||
return not is_directory(path) || (rmdir(path.c_str()) == 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool delete_directory_recursively(std::string path) {
|
||||
path = utils::path::absolute(path);
|
||||
#ifdef _WIN32
|
||||
|
||||
WIN32_FIND_DATA fd = {0};
|
||||
const auto search = utils::path::combine(path, {"*.*"});
|
||||
auto find = ::FindFirstFile(search.c_str(), &fd);
|
||||
if (find != INVALID_HANDLE_VALUE) {
|
||||
auto res = false;
|
||||
do {
|
||||
if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
|
||||
if ((std::string(fd.cFileName) != ".") && (std::string(fd.cFileName) != "..")) {
|
||||
res = delete_directory_recursively(utils::path::combine(path, {fd.cFileName}));
|
||||
}
|
||||
} else {
|
||||
res = delete_file(utils::path::combine(path, {fd.cFileName}));
|
||||
}
|
||||
} while (res && (::FindNextFile(find, &fd) != 0));
|
||||
|
||||
::FindClose(find);
|
||||
}
|
||||
#else
|
||||
auto *root = opendir(path.c_str());
|
||||
if (root) {
|
||||
auto res = true;
|
||||
struct dirent *de{};
|
||||
while (res && (de = readdir(root))) {
|
||||
if (de->d_type == DT_DIR) {
|
||||
if ((strcmp(de->d_name, ".") != 0) && (strcmp(de->d_name, "..") != 0)) {
|
||||
res = delete_directory_recursively(utils::path::combine(path, {de->d_name}));
|
||||
}
|
||||
} else {
|
||||
res = delete_file(utils::path::combine(path, {de->d_name}));
|
||||
}
|
||||
}
|
||||
|
||||
closedir(root);
|
||||
}
|
||||
#endif
|
||||
|
||||
return delete_directory(path, false);
|
||||
}
|
||||
|
||||
bool delete_file(std::string path) {
|
||||
path = utils::path::absolute(path);
|
||||
#ifdef _WIN32
|
||||
return (not is_file(path) || utils::retryable_action([&]() -> bool {
|
||||
const auto ret = !!::DeleteFileA(path.c_str());
|
||||
if (not ret) {
|
||||
std::cout << "delete failed:" << path << std::endl;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}));
|
||||
#else
|
||||
return (not is_file(path) || (unlink(path.c_str()) == 0));
|
||||
#endif
|
||||
}
|
||||
|
||||
std::string generate_sha256(const std::string &file_path) {
|
||||
std::string value;
|
||||
CryptoPP::SHA256 hash;
|
||||
CryptoPP::FileSource(
|
||||
file_path.c_str(), true,
|
||||
new CryptoPP::HashFilter(hash, new CryptoPP::HexEncoder(new CryptoPP::StringSink(value))));
|
||||
return value;
|
||||
}
|
||||
|
||||
std::uint64_t get_available_drive_space(const std::string &path) {
|
||||
#ifdef _WIN32
|
||||
ULARGE_INTEGER li = {0};
|
||||
::GetDiskFreeSpaceEx(path.c_str(), &li, nullptr, nullptr);
|
||||
return li.QuadPart;
|
||||
#endif
|
||||
#ifdef __linux__
|
||||
std::uint64_t ret = 0;
|
||||
struct statfs64 st {};
|
||||
if (statfs64(path.c_str(), &st) == 0) {
|
||||
ret = st.f_bfree * st.f_bsize;
|
||||
}
|
||||
return ret;
|
||||
#endif
|
||||
#if __APPLE__
|
||||
struct statvfs st {};
|
||||
statvfs(path.c_str(), &st);
|
||||
return st.f_bavail * st.f_frsize;
|
||||
#endif
|
||||
}
|
||||
|
||||
std::deque<std::string> get_directory_files(std::string path, const bool &oldest_first,
|
||||
const bool &recursive) {
|
||||
path = utils::path::absolute(path);
|
||||
std::deque<std::string> ret;
|
||||
std::unordered_map<std::string, std::uint64_t> lookup;
|
||||
#ifdef _WIN32
|
||||
WIN32_FIND_DATA fd = {0};
|
||||
const auto search = utils::path::combine(path, {"*.*"});
|
||||
auto find = ::FindFirstFile(search.c_str(), &fd);
|
||||
if (find != INVALID_HANDLE_VALUE) {
|
||||
do {
|
||||
if ((fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != FILE_ATTRIBUTE_DIRECTORY) {
|
||||
ULARGE_INTEGER li{};
|
||||
li.HighPart = fd.ftLastWriteTime.dwHighDateTime;
|
||||
li.LowPart = fd.ftLastWriteTime.dwLowDateTime;
|
||||
const auto full_path = utils::path::combine(path, {fd.cFileName});
|
||||
lookup[full_path] = li.QuadPart;
|
||||
ret.emplace_back(full_path);
|
||||
}
|
||||
} while (::FindNextFile(find, &fd) != 0);
|
||||
::FindClose(find);
|
||||
|
||||
std::sort(ret.begin(), ret.end(), [&](const auto &p1, const auto &p2) -> bool {
|
||||
return (oldest_first != 0) == (lookup[p1] < lookup[p2]);
|
||||
});
|
||||
}
|
||||
#else
|
||||
auto *root = opendir(path.c_str());
|
||||
if (root) {
|
||||
struct dirent *de{};
|
||||
while ((de = readdir(root)) != nullptr) {
|
||||
if (de->d_type == DT_DIR) {
|
||||
if (recursive) {
|
||||
const auto subFiles = get_directory_files(utils::path::combine(path, {de->d_name}),
|
||||
oldest_first, recursive);
|
||||
ret.insert(ret.end(), subFiles.begin(), subFiles.end());
|
||||
}
|
||||
} else {
|
||||
ret.emplace_back(utils::path::combine(path, {de->d_name}));
|
||||
}
|
||||
}
|
||||
closedir(root);
|
||||
|
||||
const auto add_to_lookup = [&](const std::string &lookup_path) {
|
||||
if (lookup.find(lookup_path) == lookup.end()) {
|
||||
struct stat st {};
|
||||
stat(lookup_path.c_str(), &st);
|
||||
#ifdef __APPLE__
|
||||
lookup[lookup_path] = static_cast<std::uint64_t>(
|
||||
(st.st_mtimespec.tv_sec * NANOS_PER_SECOND) + st.st_mtimespec.tv_nsec);
|
||||
#else
|
||||
lookup[lookup_path] =
|
||||
static_cast<std::uint64_t>((st.st_mtim.tv_sec * NANOS_PER_SECOND) + st.st_mtim.tv_nsec);
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
std::sort(ret.begin(), ret.end(), [&](const auto &p1, const auto &p2) -> bool {
|
||||
add_to_lookup(p1);
|
||||
add_to_lookup(p2);
|
||||
return (oldest_first != 0) == (lookup[p1] < lookup[p2]);
|
||||
});
|
||||
}
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool get_accessed_time(const std::string &path, std::uint64_t &accessed) {
|
||||
auto ret = false;
|
||||
accessed = 0;
|
||||
#ifdef _WIN32
|
||||
struct _stat64 st {};
|
||||
if (_stat64(path.c_str(), &st) != -1) {
|
||||
accessed = static_cast<uint64_t>(st.st_atime);
|
||||
#else
|
||||
struct stat st {};
|
||||
if (stat(path.c_str(), &st) != -1) {
|
||||
#ifdef __APPLE__
|
||||
accessed = static_cast<uint64_t>(st.st_atimespec.tv_nsec +
|
||||
(st.st_atimespec.tv_sec * NANOS_PER_SECOND));
|
||||
#else
|
||||
accessed = static_cast<uint64_t>(st.st_atim.tv_nsec + (st.st_atim.tv_sec * NANOS_PER_SECOND));
|
||||
#endif
|
||||
#endif
|
||||
ret = true;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool get_modified_time(const std::string &path, std::uint64_t &modified) {
|
||||
auto ret = false;
|
||||
modified = 0u;
|
||||
#ifdef _WIN32
|
||||
struct _stat64 st {};
|
||||
if (_stat64(path.c_str(), &st) != -1) {
|
||||
modified = static_cast<uint64_t>(st.st_mtime);
|
||||
#else
|
||||
struct stat st {};
|
||||
if (stat(path.c_str(), &st) != -1) {
|
||||
#ifdef __APPLE__
|
||||
modified = static_cast<uint64_t>(st.st_mtimespec.tv_nsec +
|
||||
(st.st_mtimespec.tv_sec * NANOS_PER_SECOND));
|
||||
#else
|
||||
modified = static_cast<uint64_t>(st.st_mtim.tv_nsec + (st.st_mtim.tv_sec * NANOS_PER_SECOND));
|
||||
#endif
|
||||
#endif
|
||||
ret = true;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool get_file_size(std::string path, std::uint64_t &file_size) {
|
||||
file_size = 0u;
|
||||
path = utils::path::finalize(path);
|
||||
|
||||
#ifdef _WIN32
|
||||
struct _stat64 st {};
|
||||
if (_stat64(path.c_str(), &st) != 0) {
|
||||
#else
|
||||
#if __APPLE__
|
||||
struct stat st {};
|
||||
if (stat(path.c_str(), &st) != 0) {
|
||||
#else
|
||||
struct stat64 st {};
|
||||
if (stat64(path.c_str(), &st) != 0) {
|
||||
#endif
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
if (st.st_size >= 0) {
|
||||
file_size = static_cast<std::uint64_t>(st.st_size);
|
||||
}
|
||||
|
||||
return (st.st_size >= 0);
|
||||
}
|
||||
|
||||
bool is_directory(const std::string &path) {
|
||||
#ifdef _WIN32
|
||||
return ::PathIsDirectory(path.c_str()) != 0;
|
||||
#else
|
||||
struct stat st {};
|
||||
return (not stat(path.c_str(), &st) && S_ISDIR(st.st_mode));
|
||||
#endif
|
||||
}
|
||||
|
||||
bool is_file(const std::string &path) {
|
||||
#ifdef _WIN32
|
||||
return (::PathFileExists(path.c_str()) && not ::PathIsDirectory(path.c_str()));
|
||||
#else
|
||||
struct stat st {};
|
||||
return (not stat(path.c_str(), &st) && not S_ISDIR(st.st_mode));
|
||||
#endif
|
||||
}
|
||||
|
||||
bool is_modified_date_older_than(const std::string &path, const std::chrono::hours &hours) {
|
||||
auto ret = false;
|
||||
std::uint64_t modified = 0;
|
||||
if (get_modified_time(path, modified)) {
|
||||
const auto seconds = std::chrono::duration_cast<std::chrono::seconds>(hours);
|
||||
#ifdef _WIN32
|
||||
return (std::chrono::system_clock::from_time_t(modified) + seconds) <
|
||||
std::chrono::system_clock::now();
|
||||
#else
|
||||
return (modified + (seconds.count() * NANOS_PER_SECOND)) < utils::get_time_now();
|
||||
#endif
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool move_file(std::string from, std::string to) {
|
||||
from = utils::path::finalize(from);
|
||||
to = utils::path::finalize(to);
|
||||
|
||||
const auto directory = utils::path::remove_file_name(to);
|
||||
create_full_directory_path(directory);
|
||||
#ifdef _WIN32
|
||||
const bool ret = ::MoveFile(from.c_str(), to.c_str()) != 0;
|
||||
#else
|
||||
const bool ret = (rename(from.c_str(), to.c_str()) == 0);
|
||||
#endif
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
api_error read_from_source(filesystem_item &fi, const std::size_t &read_size,
|
||||
const std::uint64_t &read_offset, std::vector<char> &data) {
|
||||
native_file_ptr nf;
|
||||
const auto res = assign_and_get_native_file(fi, nf);
|
||||
if (res != api_error::success) {
|
||||
return res;
|
||||
}
|
||||
|
||||
data.resize(read_size);
|
||||
std::size_t bytes_read = 0u;
|
||||
if (not nf->read_bytes(&data[0u], data.size(), read_offset, bytes_read)) {
|
||||
return api_error::os_error;
|
||||
}
|
||||
|
||||
return api_error::success;
|
||||
}
|
||||
|
||||
std::vector<std::string> read_file_lines(const std::string &path) {
|
||||
std::vector<std::string> ret;
|
||||
if (is_file(path)) {
|
||||
std::ifstream fs(path);
|
||||
std::string current_line;
|
||||
while (not fs.eof() && std::getline(fs, current_line)) {
|
||||
ret.emplace_back(utils::string::right_trim(current_line, '\r'));
|
||||
}
|
||||
fs.close();
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool read_json_file(const std::string &path, json &data) {
|
||||
auto ret = false;
|
||||
|
||||
try {
|
||||
std::ifstream file_stream(path.c_str());
|
||||
if (file_stream.is_open()) {
|
||||
std::stringstream ss;
|
||||
ss << file_stream.rdbuf();
|
||||
std::string json_text = ss.str();
|
||||
if (json_text.empty()) {
|
||||
file_stream.close();
|
||||
} else {
|
||||
data = json::parse(json_text.c_str());
|
||||
ret = true;
|
||||
file_stream.close();
|
||||
}
|
||||
}
|
||||
} catch (const std::exception &) {
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool reset_modified_time(const std::string &path) {
|
||||
auto ret = false;
|
||||
#ifdef _WIN32
|
||||
SYSTEMTIME st{};
|
||||
::GetSystemTime(&st);
|
||||
|
||||
FILETIME ft{};
|
||||
if ((ret = !!::SystemTimeToFileTime(&st, &ft))) {
|
||||
auto handle = ::CreateFileA(path.c_str(), FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES,
|
||||
FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr,
|
||||
OPEN_EXISTING, 0, nullptr);
|
||||
if ((ret = (handle != INVALID_HANDLE_VALUE))) {
|
||||
ret = !!::SetFileTime(handle, nullptr, &ft, &ft);
|
||||
::CloseHandle(handle);
|
||||
}
|
||||
}
|
||||
#else
|
||||
auto fd = open(path.c_str(), O_RDWR);
|
||||
if ((ret = (fd != -1))) {
|
||||
ret = not futimens(fd, nullptr);
|
||||
close(fd);
|
||||
}
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
api_error truncate_source(filesystem_item &fi, const std::uint64_t &size) {
|
||||
native_file_ptr nf;
|
||||
auto res = assign_and_get_native_file(fi, nf);
|
||||
if (res != api_error::success) {
|
||||
return res;
|
||||
}
|
||||
|
||||
if (not nf->truncate(size)) {
|
||||
return api_error::os_error;
|
||||
}
|
||||
|
||||
return api_error::success;
|
||||
}
|
||||
|
||||
bool write_json_file(const std::string &path, const json &j) {
|
||||
std::string data;
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << std::setw(2) << j;
|
||||
data = ss.str();
|
||||
}
|
||||
native_file_ptr nf;
|
||||
auto ret = (native_file::create_or_open(path, nf) == api_error::success);
|
||||
if (ret) {
|
||||
std::size_t bytes_written = 0u;
|
||||
ret = nf->truncate(0) && nf->write_bytes(&data[0u], data.size(), 0u, bytes_written);
|
||||
nf->close();
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
api_error write_to_source(filesystem_item &fi, const std::uint64_t &write_offset,
|
||||
const std::vector<char> &data, std::size_t &bytes_written) {
|
||||
bytes_written = 0u;
|
||||
|
||||
native_file_ptr nf;
|
||||
auto res = assign_and_get_native_file(fi, nf);
|
||||
if (res != api_error::success) {
|
||||
return res;
|
||||
}
|
||||
|
||||
if (not nf->write_bytes(&data[0u], data.size(), write_offset, bytes_written)) {
|
||||
return api_error::os_error;
|
||||
}
|
||||
|
||||
return api_error::success;
|
||||
}
|
||||
} // namespace repertory::utils::file
|
40
src/utils/global_data.cpp
Normal file
40
src/utils/global_data.cpp
Normal file
@ -0,0 +1,40 @@
|
||||
/*
|
||||
Copyright <2018-2022> <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/global_data.hpp"
|
||||
|
||||
namespace repertory {
|
||||
global_data global_data::instance_;
|
||||
|
||||
void global_data::update_used_space(const std::uint64_t &file_size,
|
||||
const std::uint64_t &new_file_size, const bool &cache_only) {
|
||||
if (file_size > new_file_size) {
|
||||
const auto diff = (file_size - new_file_size);
|
||||
used_cache_space_ -= diff;
|
||||
if (not cache_only) {
|
||||
used_drive_space_ -= diff;
|
||||
}
|
||||
} else if (file_size < new_file_size) {
|
||||
const auto diff = (new_file_size - file_size);
|
||||
used_cache_space_ += diff;
|
||||
if (not cache_only) {
|
||||
used_drive_space_ += diff;
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace repertory
|
255
src/utils/native_file.cpp
Normal file
255
src/utils/native_file.cpp
Normal file
@ -0,0 +1,255 @@
|
||||
/*
|
||||
Copyright <2018-2022> <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 "utils/string_utils.hpp"
|
||||
|
||||
namespace repertory {
|
||||
native_file_ptr native_file::clone(const native_file_ptr &nf) {
|
||||
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]);
|
||||
a
|
||||
#else
|
||||
readlink(("/proc/self/fd/" + utils::string::from_int64(nf->get_handle())).c_str(),
|
||||
&source_path[0u], source_path.size());
|
||||
#endif
|
||||
#endif
|
||||
source_path = source_path.c_str();
|
||||
|
||||
native_file_ptr clone;
|
||||
if (native_file::open(source_path, clone) != api_error::success) {
|
||||
throw std::runtime_error("unable to clone file " + source_path);
|
||||
}
|
||||
|
||||
return clone;
|
||||
}
|
||||
|
||||
api_error native_file::create_or_open(const std::string &source_path, native_file_ptr &nf) {
|
||||
#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 = ::open(source_path.c_str(), O_CREAT | O_RDWR | O_CLOEXEC, 0600u);
|
||||
chmod(source_path.c_str(), 0600u);
|
||||
#endif
|
||||
nf = native_file::attach(handle);
|
||||
return ((handle == REPERTORY_INVALID_HANDLE) ? api_error::os_error : api_error::success);
|
||||
}
|
||||
|
||||
api_error native_file::open(const std::string &source_path, native_file_ptr &nf) {
|
||||
#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 = ::open(source_path.c_str(), O_RDWR | O_CLOEXEC, 0600u);
|
||||
chmod(source_path.c_str(), 0600u);
|
||||
#endif
|
||||
nf = native_file::attach(handle);
|
||||
return ((handle == REPERTORY_INVALID_HANDLE) ? api_error::os_error : api_error::success);
|
||||
}
|
||||
|
||||
bool native_file::allocate(const std::uint64_t &file_size) {
|
||||
#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;
|
||||
}
|
||||
}
|
||||
|
||||
bool native_file::copy_from(const native_file_ptr &nf) {
|
||||
auto ret = false;
|
||||
std::uint64_t file_size = 0u;
|
||||
if ((ret = nf->get_file_size(file_size))) {
|
||||
std::vector<char> 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;
|
||||
}
|
||||
|
||||
bool native_file::copy_from(const std::string &path) {
|
||||
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
|
||||
}
|
||||
|
||||
bool native_file::get_file_size(std::uint64_t &file_size) {
|
||||
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
|
||||
bool native_file::read_bytes(char *buffer, const std::size_t &read_size,
|
||||
const std::uint64_t &read_offset, std::size_t &bytes_read) {
|
||||
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
|
||||
bool native_file::read_bytes(char *buffer, const std::size_t &read_size,
|
||||
const std::uint64_t &read_offset, std::size_t &bytes_read) {
|
||||
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
|
||||
bool native_file::truncate(const std::uint64_t &file_size) {
|
||||
#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
|
||||
bool native_file::write_bytes(const char *buffer, const std::size_t &write_size,
|
||||
const std::uint64_t &write_offset, std::size_t &bytes_written) {
|
||||
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
|
||||
bool native_file::write_bytes(const char *buffer, const std::size_t &write_size,
|
||||
const std::uint64_t &write_offset, std::size_t &bytes_written) {
|
||||
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
|
181
src/utils/path_utils.cpp
Normal file
181
src/utils/path_utils.cpp
Normal file
@ -0,0 +1,181 @@
|
||||
/*
|
||||
Copyright <2018-2022> <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/string_utils.hpp"
|
||||
#include "utils/utils.hpp"
|
||||
|
||||
namespace repertory::utils::path {
|
||||
static std::string &format_path(std::string &path, const std::string &sep,
|
||||
const std::string ¬_sep);
|
||||
|
||||
std::string absolute(std::string path) {
|
||||
#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 path;
|
||||
}
|
||||
|
||||
std::string combine(std::string path, const std::vector<std::string> &paths) {
|
||||
return finalize(
|
||||
std::accumulate(paths.begin(), paths.end(), path, [](std::string path, const auto &pathPart) {
|
||||
path += (directory_seperator + pathPart);
|
||||
return path;
|
||||
}));
|
||||
}
|
||||
|
||||
std::string create_api_path(std::string path) {
|
||||
if (path.empty() || (path == ".")) {
|
||||
path = "/";
|
||||
} else {
|
||||
format_path(path, "/", "\\");
|
||||
if (path.find("./") == 0) {
|
||||
path = path.substr(1);
|
||||
}
|
||||
if (path[0u] != '/') {
|
||||
path = "/" + path;
|
||||
}
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
static std::string &format_path(std::string &path, const std::string &sep,
|
||||
const std::string ¬_sep) {
|
||||
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;
|
||||
}
|
||||
|
||||
std::string finalize(std::string path) {
|
||||
format_path(path, directory_seperator, not_directory_seperator);
|
||||
if ((path.size() > 1) && (path[path.size() - 1] == directory_seperator[0u])) {
|
||||
path = path.substr(0, path.size() - 1);
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
std::string get_parent_api_path(const std::string &path) {
|
||||
std::string ret;
|
||||
if (path != "/") {
|
||||
ret = path.substr(0, path.rfind('/') + 1);
|
||||
if (ret != "/") {
|
||||
ret = utils::string::right_trim(ret, '/');
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifndef _WIN32
|
||||
std::string get_parent_directory(std::string path) {
|
||||
auto ret = std::string(dirname(&path[0u]));
|
||||
if (ret == ".") {
|
||||
ret = "/";
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool is_ads_file_path(const std::string &path) {
|
||||
#ifdef _WIN32
|
||||
return utils::string::contains(path, ":");
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool is_trash_directory(std::string path) {
|
||||
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;
|
||||
}
|
||||
|
||||
std::string remove_file_name(std::string path) {
|
||||
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
|
||||
std::string resolve(std::string path) {
|
||||
struct passwd *pw = getpwuid(getuid());
|
||||
std::string 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
|
||||
|
||||
std::string strip_to_file_name(std::string path) {
|
||||
#ifdef _WIN32
|
||||
return ::PathFindFileName(&path[0u]);
|
||||
#else
|
||||
return utils::string::contains(path, "/") ? basename(&path[0u]) : path;
|
||||
#endif
|
||||
}
|
||||
} // namespace repertory::utils::path
|
91
src/utils/polling.cpp
Normal file
91
src/utils/polling.cpp
Normal file
@ -0,0 +1,91 @@
|
||||
/*
|
||||
Copyright <2018-2022> <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,
|
||||
bool low_frequency) {
|
||||
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 (auto &kv : items_) {
|
||||
if (kv.second.low_frequency == low_frequency) {
|
||||
futures.emplace_back(std::async(std::launch::async, [kv]() -> void {
|
||||
event_system::instance().raise<polling_item_begin>(kv.first);
|
||||
kv.second.action();
|
||||
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 &pollingItem) {
|
||||
mutex_lock l(mutex_);
|
||||
items_[pollingItem.name] = pollingItem;
|
||||
}
|
||||
|
||||
void polling::start(app_config *config) {
|
||||
mutex_lock l(start_stop_mutex_);
|
||||
if (not high_frequency_thread_) {
|
||||
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(); }, false);
|
||||
});
|
||||
low_frequency_thread_ = std::make_unique<std::thread>([this]() -> void {
|
||||
this->frequency_thread(
|
||||
[this]() -> std::uint32_t { return config_->get_low_frequency_interval_secs(); }, true);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void polling::stop() {
|
||||
mutex_lock l(start_stop_mutex_);
|
||||
if (high_frequency_thread_) {
|
||||
event_system::instance().raise<service_shutdown>("polling");
|
||||
{
|
||||
mutex_lock l2(mutex_);
|
||||
stop_requested_ = true;
|
||||
notify_.notify_all();
|
||||
}
|
||||
high_frequency_thread_->join();
|
||||
low_frequency_thread_->join();
|
||||
high_frequency_thread_.reset();
|
||||
low_frequency_thread_.reset();
|
||||
}
|
||||
}
|
||||
} // namespace repertory
|
65
src/utils/rocksdb_utils.cpp
Normal file
65
src/utils/rocksdb_utils.cpp
Normal file
@ -0,0 +1,65 @@
|
||||
/*
|
||||
Copyright <2018-2022> <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/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 = nullptr;
|
||||
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 {
|
||||
event_system::instance().raise<repertory_exception>(__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 = nullptr;
|
||||
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 {
|
||||
event_system::instance().raise<repertory_exception>(__FUNCTION__, status.ToString());
|
||||
throw startup_exception(status.ToString());
|
||||
}
|
||||
}
|
||||
} // namespace repertory::utils::db
|
195
src/utils/string_utils.cpp
Normal file
195
src/utils/string_utils.cpp
Normal file
@ -0,0 +1,195 @@
|
||||
/*
|
||||
Copyright <2018-2022> <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"
|
||||
#include "common.hpp"
|
||||
|
||||
namespace repertory::utils::string {
|
||||
bool begins_with(const std::string &str, const std::string &val) { return (str.find(val) == 0); }
|
||||
|
||||
bool contains(const std::string &str, const std::string &search) {
|
||||
return (str.find(search) != std::string::npos);
|
||||
}
|
||||
|
||||
bool ends_with(const std::string &str, const std::string &val) {
|
||||
if (val.size() > str.size()) {
|
||||
return false;
|
||||
}
|
||||
return std::equal(val.rbegin(), val.rend(), str.rbegin());
|
||||
}
|
||||
|
||||
std::string from_bool(const bool &val) { return std::to_string(val); }
|
||||
|
||||
std::string from_double(const double &value) { return std::to_string(value); }
|
||||
|
||||
std::string from_dynamic_bitset(const boost::dynamic_bitset<> &bitset) {
|
||||
std::stringstream ss;
|
||||
boost::archive::text_oarchive archive(ss);
|
||||
archive << bitset;
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
std::string from_int32(const std::int32_t &val) { return std::to_string(val); }
|
||||
|
||||
std::string from_int64(const std::int64_t &val) { return std::to_string(val); }
|
||||
|
||||
std::string from_uint8(const std::uint8_t &val) { return std::to_string(val); }
|
||||
|
||||
std::string from_uint16(const std::uint16_t &val) { return std::to_string(val); }
|
||||
|
||||
std::string from_uint32(const std::uint32_t &val) { return std::to_string(val); }
|
||||
|
||||
std::string from_uint64(const std::uint64_t &val) { return std::to_string(val); }
|
||||
|
||||
std::wstring from_utf8(const std::string &str) {
|
||||
return str.empty() ? L""
|
||||
: std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t>().from_bytes(str);
|
||||
}
|
||||
|
||||
bool is_numeric(const std::string &s) {
|
||||
static const auto r = std::regex(R"(^(\+|\-)?(([0-9]*)|(([0-9]*)\.([0-9]*)))$)");
|
||||
return std::regex_match(s, r);
|
||||
}
|
||||
|
||||
std::string join(const std::vector<std::string> &arr, const char &delim) {
|
||||
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; });
|
||||
}
|
||||
|
||||
std::string &left_trim(std::string &s) { return left_trim(s, ' '); }
|
||||
|
||||
std::string &left_trim(std::string &s, const char &c) {
|
||||
s.erase(0, s.find_first_not_of(c));
|
||||
return s;
|
||||
}
|
||||
|
||||
std::string &replace(std::string &src, const char &character, const char &with) {
|
||||
std::replace(src.begin(), src.end(), character, with);
|
||||
return src;
|
||||
}
|
||||
|
||||
std::string &replace(std::string &src, const std::string &find, const std::string &with,
|
||||
size_t startPos) {
|
||||
if (!src.empty() && (startPos < src.size())) {
|
||||
while ((startPos = src.find(find, startPos)) != std::string::npos) {
|
||||
src.replace(startPos, find.size(), with);
|
||||
startPos += with.size();
|
||||
}
|
||||
}
|
||||
return src;
|
||||
}
|
||||
|
||||
std::string replace_copy(std::string src, const char &character, const char &with) {
|
||||
std::replace(src.begin(), src.end(), character, with);
|
||||
return src;
|
||||
}
|
||||
|
||||
std::string replace_copy(std::string src, const std::string &find, const std::string &with,
|
||||
size_t startPos) {
|
||||
return replace(src, find, with, startPos);
|
||||
}
|
||||
|
||||
std::string &right_trim(std::string &s) { return right_trim(s, ' '); }
|
||||
|
||||
std::string &right_trim(std::string &s, const char &c) {
|
||||
s.erase(s.find_last_not_of(c) + 1);
|
||||
return s;
|
||||
}
|
||||
|
||||
std::vector<std::string> split(const std::string &str, const char &delim, const bool &should_trim) {
|
||||
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;
|
||||
}
|
||||
|
||||
bool to_bool(std::string val) {
|
||||
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;
|
||||
}
|
||||
|
||||
double to_double(const std::string &str) { return std::stod(str); }
|
||||
|
||||
boost::dynamic_bitset<> to_dynamic_bitset(const std::string &val) {
|
||||
std::stringstream ss(val);
|
||||
boost::dynamic_bitset<> bitset;
|
||||
boost::archive::text_iarchive archive(ss);
|
||||
archive >> bitset;
|
||||
return bitset;
|
||||
}
|
||||
|
||||
std::int32_t to_int32(const std::string &val) { return std::stoi(val); }
|
||||
|
||||
std::int64_t to_int64(const std::string &val) { return std::stoll(val); }
|
||||
|
||||
std::string to_lower(std::string str) {
|
||||
std::transform(str.begin(), str.end(), str.begin(), ::tolower);
|
||||
return str;
|
||||
}
|
||||
|
||||
std::uint8_t to_uint8(const std::string &val) { return static_cast<std::uint8_t>(std::stoul(val)); }
|
||||
|
||||
std::uint16_t to_uint16(const std::string &val) {
|
||||
return static_cast<std::uint16_t>(std::stoul(val));
|
||||
}
|
||||
|
||||
std::uint32_t to_uint32(const std::string &val) {
|
||||
return static_cast<std::uint32_t>(std::stoul(val));
|
||||
}
|
||||
|
||||
std::uint64_t to_uint64(const std::string &val) { return std::stoull(val); }
|
||||
|
||||
std::string to_upper(std::string str) {
|
||||
std::transform(str.begin(), str.end(), str.begin(), ::toupper);
|
||||
return str;
|
||||
}
|
||||
|
||||
const std::string &to_utf8(const std::string &str) { return str; }
|
||||
|
||||
std::string to_utf8(const std::wstring &str) {
|
||||
return str.empty() ? ""
|
||||
: std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t>().to_bytes(str);
|
||||
}
|
||||
|
||||
std::string &trim(std::string &str) { return right_trim(left_trim(str)); }
|
||||
|
||||
std::string &trim(std::string &str, const char &c) { return right_trim(left_trim(str, c), c); }
|
||||
|
||||
std::string trim_copy(std::string str) { return right_trim(left_trim(str)); }
|
||||
|
||||
std::string trim_copy(std::string str, const char &c) { return right_trim(left_trim(str, c), c); }
|
||||
} // namespace repertory::utils::string
|
60
src/utils/throttle.cpp
Normal file
60
src/utils/throttle.cpp
Normal file
@ -0,0 +1,60 @@
|
||||
/*
|
||||
Copyright <2018-2022> <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
|
49
src/utils/timeout.cpp
Normal file
49
src/utils/timeout.cpp
Normal file
@ -0,0 +1,49 @@
|
||||
/*
|
||||
Copyright <2018-2022> <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
|
191
src/utils/unix/unix_utils.cpp
Normal file
191
src/utils/unix/unix_utils.cpp
Normal file
@ -0,0 +1,191 @@
|
||||
/*
|
||||
Copyright <2018-2022> <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/utils.hpp"
|
||||
|
||||
namespace repertory::utils {
|
||||
#ifndef __APPLE__
|
||||
std::uint64_t convert_to_uint64(const pthread_t &t) { return static_cast<std::uint64_t>(t); }
|
||||
#endif
|
||||
|
||||
int get_last_error_code() { return errno; }
|
||||
|
||||
std::uint64_t get_thread_id() { return convert_to_uint64(pthread_self()); }
|
||||
|
||||
bool is_uid_member_of_group(const uid_t &uid, const gid_t &gid) {
|
||||
auto *pw = getpwuid(uid);
|
||||
std::vector<gid_t> groups;
|
||||
|
||||
int group_count = 0;
|
||||
if (getgrouplist(pw->pw_name, pw->pw_gid, nullptr, &group_count) < 0) {
|
||||
groups.resize(static_cast<unsigned long>(group_count));
|
||||
#ifdef __APPLE__
|
||||
getgrouplist(pw->pw_name, pw->pw_gid, reinterpret_cast<int *>(&groups[0]), &group_count);
|
||||
#else
|
||||
getgrouplist(pw->pw_name, pw->pw_gid, &groups[0], &group_count);
|
||||
#endif
|
||||
}
|
||||
|
||||
return collection_includes(groups, gid);
|
||||
}
|
||||
|
||||
int translate_api_error(const api_error &e) {
|
||||
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:
|
||||
#if __APPLE__
|
||||
return -EBADMSG;
|
||||
#else
|
||||
return -EREMOTEIO;
|
||||
#endif
|
||||
case api_error::error:
|
||||
return -EIO;
|
||||
case api_error::file_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::item_is_file:
|
||||
return -ENOTDIR;
|
||||
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:
|
||||
#if __APPLE__
|
||||
return -EBADMSG;
|
||||
#else
|
||||
return -EREMOTEIO;
|
||||
#endif
|
||||
case api_error::xattr_buffer_small:
|
||||
return -ERANGE;
|
||||
case api_error::xattr_exists:
|
||||
return -EEXIST;
|
||||
case api_error::xattr_invalid_namespace:
|
||||
return -ENOTSUP;
|
||||
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
|
||||
#ifdef __APPLE__
|
||||
case api_error::XAttrOSXInvalid:
|
||||
return -EINVAL;
|
||||
#endif
|
||||
default:
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
|
||||
void set_last_error_code(int error_code) { errno = error_code; }
|
||||
|
||||
std::int32_t unix_error_to_windows(const int &e) {
|
||||
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_FILE_IS_A_DIRECTORY;
|
||||
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_PATH_INVALID;
|
||||
default:
|
||||
return STATUS_INTERNAL_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
UINT64 unix_time_to_windows_time(const remote::file_time &ts) {
|
||||
return (ts / 100ull) + 116444736000000000ull;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
remote::file_time windows_time_to_unix_time(const std::uint64_t &t) {
|
||||
return (t - 116444736000000000ull) * 100ull;
|
||||
}
|
||||
} // namespace repertory::utils
|
||||
|
||||
#endif // !_WIN32
|
371
src/utils/utils.cpp
Normal file
371
src/utils/utils.cpp
Normal file
@ -0,0 +1,371 @@
|
||||
/*
|
||||
Copyright <2018-2022> <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/events.hpp"
|
||||
#include "events/event_system.hpp"
|
||||
#include "providers/i_provider.hpp"
|
||||
#include "types/skynet.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 {
|
||||
hastings api_currency_to_hastings(const api_currency ¤cy) {
|
||||
ttmath::Parser<api_currency> parser;
|
||||
parser.Parse(currency.ToString() + " * (10 ^ 24)");
|
||||
ttmath::Conv conv;
|
||||
conv.scient_from = 256;
|
||||
conv.base = 10u;
|
||||
conv.round = 0;
|
||||
return parser.stack[0u].value.ToString(conv);
|
||||
}
|
||||
|
||||
void calculate_allocation_size(const bool &directory, const 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);
|
||||
}
|
||||
|
||||
std::size_t calculate_read_size(const uint64_t &total_size, const std::size_t &read_size,
|
||||
const uint64_t &offset) {
|
||||
return static_cast<std::size_t>(((offset + read_size) > total_size)
|
||||
? ((offset < total_size) ? total_size - offset : 0u)
|
||||
: read_size);
|
||||
}
|
||||
|
||||
int compare_version_strings(std::string version1, std::string version2) {
|
||||
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;
|
||||
}
|
||||
|
||||
std::uint64_t convert_api_date(const std::string &date) {
|
||||
//"2019-02-21T02:24:37.653091916-06:00"
|
||||
const auto parts = utils::string::split(date, '.');
|
||||
const auto dt = parts[0];
|
||||
const auto nanos = utils::string::to_uint64(utils::string::split(parts[1u], '-')[0u]);
|
||||
|
||||
struct tm tm1 {};
|
||||
#ifdef _WIN32
|
||||
auto convert_time = [](time_t t) -> std::uint64_t {
|
||||
const auto ll = Int32x32To64(t, 10000000) + 116444736000000000ull;
|
||||
ULARGE_INTEGER ft{};
|
||||
ft.LowPart = static_cast<DWORD>(ll);
|
||||
ft.HighPart = static_cast<DWORD>(ll >> 32);
|
||||
return ft.QuadPart;
|
||||
};
|
||||
|
||||
const auto parts2 = utils::string::split(dt, 'T');
|
||||
const auto date_parts = utils::string::split(parts2[0u], '-');
|
||||
const auto time_parts = utils::string::split(parts2[1u], ':');
|
||||
tm1.tm_year = utils::string::to_int32(date_parts[0u]) - 1900;
|
||||
tm1.tm_mon = utils::string::to_int32(date_parts[1u]) - 1;
|
||||
tm1.tm_mday = utils::string::to_int32(date_parts[2u]);
|
||||
tm1.tm_hour = utils::string::to_uint32(time_parts[0u]);
|
||||
tm1.tm_min = utils::string::to_uint32(time_parts[1u]);
|
||||
tm1.tm_sec = utils::string::to_uint32(time_parts[2u]);
|
||||
tm1.tm_wday = -1;
|
||||
tm1.tm_yday = -1;
|
||||
tm1.tm_isdst = -1;
|
||||
return (nanos / 100) + convert_time(mktime(&tm1));
|
||||
#else
|
||||
strptime(&dt[0], "%Y-%m-%dT%T", &tm1);
|
||||
return nanos + (mktime(&tm1) * NANOS_PER_SECOND);
|
||||
#endif
|
||||
}
|
||||
|
||||
CURL *create_curl() { return reset_curl(curl_easy_init()); }
|
||||
|
||||
std::string create_uuid_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
|
||||
}
|
||||
|
||||
std::string create_volume_label(const provider_type &pt) {
|
||||
return "repertory_" + app_config::get_provider_name(pt);
|
||||
}
|
||||
|
||||
download_type download_type_from_string(std::string type, const download_type &default_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;
|
||||
}
|
||||
|
||||
std::string download_type_to_string(const download_type &type) {
|
||||
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/
|
||||
remote::file_time filetime_to_unix_time(const FILETIME &ft) {
|
||||
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
|
||||
|
||||
std::string generate_random_string(const std::uint16_t &length) {
|
||||
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;
|
||||
}
|
||||
|
||||
std::string get_environment_variable(const std::string &variable) {
|
||||
#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
|
||||
}
|
||||
|
||||
std::uint64_t get_file_time_now() {
|
||||
#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
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
#endif
|
||||
|
||||
bool get_next_available_port(std::uint16_t first_port, std::uint16_t &available_port) {
|
||||
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;
|
||||
}
|
||||
|
||||
std::uint64_t get_time_now() {
|
||||
#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
|
||||
}
|
||||
|
||||
api_currency hastings_string_to_api_currency(const std::string &amount) {
|
||||
ttmath::Parser<api_currency> parser;
|
||||
parser.Parse(amount + " / (10 ^ 24)");
|
||||
return parser.stack[0].value;
|
||||
}
|
||||
|
||||
CURL *reset_curl(CURL *curl_handle) {
|
||||
curl_easy_reset(curl_handle);
|
||||
#if __APPLE__
|
||||
curl_easy_setopt(curl_handle, CURLOPT_NOSIGNAL, 1);
|
||||
#endif
|
||||
return curl_handle;
|
||||
}
|
||||
|
||||
bool retryable_action(const std::function<bool()> &action) {
|
||||
auto succeeded = false;
|
||||
for (auto i = 0; not(succeeded = action()) && (i < 10); i++) {
|
||||
std::this_thread::sleep_for(100ms);
|
||||
}
|
||||
return succeeded;
|
||||
}
|
||||
|
||||
/* bool parse_url(const std::string &url, HostConfig &hc) { */
|
||||
/* static const auto pathRegex = std::regex( */
|
||||
/* R"(^((\w+):)?(\/\/((\w+)?(:(\w+))?@)?([^\/\?:]+)(:(\d+))?)?(\/?([^\/\?#][^\?#]*)?)?(\?([^#]+))?(#(\w*))?$)");
|
||||
*/
|
||||
/* */
|
||||
/* std::smatch results; */
|
||||
/* if (std::regex_search(url, results, pathRegex)) { */
|
||||
/* hc.HostNameOrIp = results[8u].str(); */
|
||||
/* hc.Path = utils::path::create_api_path(results[11u].str()); */
|
||||
/* hc.Protocol = utils::string::to_lower(results[2u].str()); */
|
||||
/* hc.ApiPort = results[10u].str().empty() ? ((hc.Protocol == "https") ? 443u : 80u) */
|
||||
/* : utils::string::to_uint16(results[10u].str()); */
|
||||
/* return not hc.HostNameOrIp.empty() && not hc.Protocol.empty() && */
|
||||
/* ((hc.Protocol == "http") || (hc.Protocol == "https")); */
|
||||
/* } */
|
||||
/* */
|
||||
/* return false; */
|
||||
/* } */
|
||||
|
||||
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, 5s);
|
||||
}
|
||||
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, 5s);
|
||||
}
|
||||
l.unlock();
|
||||
}
|
||||
}
|
||||
} // namespace repertory::utils
|
196
src/utils/windows/windows_utils.cpp
Normal file
196
src/utils/windows/windows_utils.cpp
Normal file
@ -0,0 +1,196 @@
|
||||
/*
|
||||
Copyright <2018-2022> <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"
|
||||
|
||||
namespace repertory::utils {
|
||||
DWORD get_last_error_code() { return ::GetLastError(); }
|
||||
|
||||
const std::string &get_local_app_data_directory() {
|
||||
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;
|
||||
}
|
||||
|
||||
std::uint64_t get_accessed_time_from_meta(const api_meta_map &meta) {
|
||||
return utils::string::to_uint64(meta.at(META_ACCESSED));
|
||||
}
|
||||
|
||||
DWORD get_attributes_from_meta(const api_meta_map &meta) {
|
||||
return static_cast<DWORD>(utils::string::to_uint32(meta.at(META_ATTRIBUTES)));
|
||||
}
|
||||
|
||||
std::uint64_t get_changed_time_from_meta(const api_meta_map &meta) {
|
||||
return utils::string::to_uint64(meta.at(META_MODIFIED));
|
||||
}
|
||||
|
||||
std::uint64_t get_creation_time_from_meta(const api_meta_map &meta) {
|
||||
return utils::string::to_uint64(meta.at(META_CREATION));
|
||||
}
|
||||
|
||||
std::uint64_t get_written_time_from_meta(const api_meta_map &meta) {
|
||||
return utils::string::to_uint64(meta.at(META_WRITTEN));
|
||||
}
|
||||
|
||||
std::uint64_t get_thread_id() { return static_cast<std::uint64_t>(::GetCurrentThreadId()); }
|
||||
|
||||
NTSTATUS translate_api_error(const api_error &e) {
|
||||
switch (e) {
|
||||
case api_error::access_denied:
|
||||
return FspNtStatusFromWin32(ERROR_ACCESS_DENIED);
|
||||
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::file_exists:
|
||||
return FspNtStatusFromWin32(ERROR_FILE_EXISTS);
|
||||
case api_error::file_in_use:
|
||||
return FspNtStatusFromWin32(ERROR_BUSY);
|
||||
case api_error::incompatible_version:
|
||||
return STATUS_CLIENT_SERVER_PARAMETERS_INVALID;
|
||||
case api_error::invalid_operation:
|
||||
return FspNtStatusFromWin32(ERROR_INTERNAL_ERROR);
|
||||
case api_error::item_not_found:
|
||||
return STATUS_OBJECT_NAME_NOT_FOUND;
|
||||
case api_error::item_is_file:
|
||||
return STATUS_ACCESS_DENIED;
|
||||
case api_error::os_error:
|
||||
return FspNtStatusFromWin32(::GetLastError());
|
||||
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);
|
||||
}
|
||||
}
|
||||
bool is_process_elevated() {
|
||||
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;
|
||||
}
|
||||
|
||||
int run_process_elevated(int argc, char *argv[]) {
|
||||
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); }
|
||||
|
||||
int unix_access_mask_to_windows(std::int32_t mask) {
|
||||
if (mask & 1) {
|
||||
mask -= 1;
|
||||
if (not mask) {
|
||||
mask = 4;
|
||||
}
|
||||
}
|
||||
|
||||
return mask & 6;
|
||||
}
|
||||
|
||||
int unix_open_flags_to_flags_and_perms(const remote::file_mode &mode,
|
||||
const remote::open_flags &flags, std::int32_t &perms) {
|
||||
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;
|
||||
}
|
||||
|
||||
remote::file_time time64_to_unix_time(const __time64_t &t) { return t * NANOS_PER_SECOND; }
|
||||
} // namespace repertory::utils
|
||||
|
||||
#endif // _WIN32
|
Reference in New Issue
Block a user