This commit is contained in:
2023-11-30 09:18:10 -06:00
parent 8436f2e2bb
commit 97487cf0c4
380 changed files with 144184 additions and 144135 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -1,180 +1,180 @@
/*
Copyright <2018-2023> <scott.e.graves@protonmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "comm/curl/curl_comm.hpp"
#include "types/repertory.hpp"
#include "utils/path_utils.hpp"
#include "utils/string_utils.hpp"
namespace repertory {
const curl_comm::write_callback curl_comm::write_data =
static_cast<curl_comm::write_callback>([](char *buffer, size_t size,
size_t nitems,
void *outstream) -> size_t {
auto &info = *reinterpret_cast<read_write_info *>(outstream);
std::copy(buffer, buffer + (size * nitems),
std::back_inserter(info.data));
return info.stop_requested ? 0 : size * nitems;
});
const curl_comm::write_callback curl_comm::write_headers =
static_cast<curl_comm::write_callback>([](char *buffer, size_t size,
size_t nitems,
void *outstream) -> size_t {
auto &headers = *reinterpret_cast<http_headers *>(outstream);
const auto header = std::string(buffer, size * nitems);
const auto parts = utils::string::split(header, ':');
if (parts.size() > 1U) {
auto data = header.substr(parts[0U].size() + 1U);
utils::string::left_trim(data);
utils::string::right_trim(data, '\r');
utils::string::right_trim(data, '\n');
utils::string::right_trim(data, '\r');
headers[utils::string::to_lower(parts[0U])] = data;
}
return size * nitems;
});
curl_comm::curl_comm(host_config cfg)
: host_config_(std::move(cfg)), s3_config_(std::nullopt) {}
curl_comm::curl_comm(s3_config cfg)
: host_config_(std::nullopt), s3_config_(std::move(cfg)) {}
auto curl_comm::construct_url(CURL *curl, const std::string &relative_path,
const host_config &cfg) -> std::string {
static constexpr const auto http = 80U;
static constexpr const auto https = 443U;
auto custom_port = (((cfg.protocol == "http") &&
(cfg.api_port == http || cfg.api_port == 0U)) ||
((cfg.protocol == "https") &&
(cfg.api_port == https || cfg.api_port == 0U)))
? ""
: ":" + std::to_string(cfg.api_port);
auto url = cfg.protocol + "://" +
utils::string::trim_copy(cfg.host_name_or_ip) + custom_port;
static const auto complete_url = [](const std::string &current_path,
const std::string &parent_path,
std::string &final_url) -> std::string & {
final_url += utils::path::create_api_path(current_path);
if (utils::string::ends_with(parent_path, "/")) {
final_url += '/';
}
return final_url;
};
auto path = utils::path::combine("/", {cfg.path});
return relative_path.empty()
? complete_url(path, cfg.path, url)
: complete_url(utils::path::combine(
path, {url_encode(curl, relative_path, true)}),
relative_path, url);
}
auto curl_comm::create_host_config(const s3_config &cfg, bool use_s3_path_style)
-> host_config {
host_config host_cfg{};
host_cfg.api_password = cfg.secret_key;
host_cfg.api_user = cfg.access_key;
auto pos = cfg.url.find(':');
host_cfg.host_name_or_ip = cfg.url.substr(pos + 3U);
if (cfg.use_region_in_url && not cfg.region.empty()) {
auto parts = utils::string::split(host_cfg.host_name_or_ip, '.', false);
if (parts.size() > 1U) {
parts.insert(parts.begin() + 1U, cfg.region);
host_cfg.host_name_or_ip = utils::string::join(parts, '.');
}
}
if (not use_s3_path_style) {
host_cfg.host_name_or_ip = cfg.bucket + '.' + host_cfg.host_name_or_ip;
}
host_cfg.protocol = cfg.url.substr(0U, pos);
if (use_s3_path_style) {
host_cfg.path = '/' + cfg.bucket;
}
return host_cfg;
}
void curl_comm::enable_s3_path_style(bool enable) {
use_s3_path_style_ = enable;
}
auto curl_comm::make_request(const curl::requests::http_delete &del,
long &response_code,
stop_type &stop_requested) const -> bool {
return make_request(
s3_config_.has_value()
? create_host_config(s3_config_.value(), use_s3_path_style_)
: host_config_.value_or(host_config{}),
del, response_code, stop_requested);
}
auto curl_comm::make_request(const curl::requests::http_get &get,
long &response_code,
stop_type &stop_requested) const -> bool {
return make_request(
s3_config_.has_value()
? create_host_config(s3_config_.value(), use_s3_path_style_)
: host_config_.value_or(host_config{}),
get, response_code, stop_requested);
}
auto curl_comm::make_request(const curl::requests::http_head &head,
long &response_code,
stop_type &stop_requested) const -> bool {
return make_request(
s3_config_.has_value()
? create_host_config(s3_config_.value(), use_s3_path_style_)
: host_config_.value_or(host_config{}),
head, response_code, stop_requested);
}
auto curl_comm::make_request(const curl::requests::http_put_file &put_file,
long &response_code,
stop_type &stop_requested) const -> bool {
return make_request(
s3_config_.has_value()
? create_host_config(s3_config_.value(), use_s3_path_style_)
: host_config_.value_or(host_config{}),
put_file, response_code, stop_requested);
}
auto curl_comm::url_encode(CURL *curl, const std::string &data,
bool allow_slash) -> std::string {
auto *value =
curl_easy_escape(curl, data.c_str(), static_cast<int>(data.size()));
std::string ret = value;
curl_free(value);
if (allow_slash) {
utils::string::replace(ret, "%2F", "/");
}
return ret;
}
} // namespace repertory
/*
Copyright <2018-2023> <scott.e.graves@protonmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "comm/curl/curl_comm.hpp"
#include "types/repertory.hpp"
#include "utils/path_utils.hpp"
#include "utils/string_utils.hpp"
namespace repertory {
const curl_comm::write_callback curl_comm::write_data =
static_cast<curl_comm::write_callback>([](char *buffer, size_t size,
size_t nitems,
void *outstream) -> size_t {
auto &info = *reinterpret_cast<read_write_info *>(outstream);
std::copy(buffer, buffer + (size * nitems),
std::back_inserter(info.data));
return info.stop_requested ? 0 : size * nitems;
});
const curl_comm::write_callback curl_comm::write_headers =
static_cast<curl_comm::write_callback>([](char *buffer, size_t size,
size_t nitems,
void *outstream) -> size_t {
auto &headers = *reinterpret_cast<http_headers *>(outstream);
const auto header = std::string(buffer, size * nitems);
const auto parts = utils::string::split(header, ':');
if (parts.size() > 1U) {
auto data = header.substr(parts[0U].size() + 1U);
utils::string::left_trim(data);
utils::string::right_trim(data, '\r');
utils::string::right_trim(data, '\n');
utils::string::right_trim(data, '\r');
headers[utils::string::to_lower(parts[0U])] = data;
}
return size * nitems;
});
curl_comm::curl_comm(host_config cfg)
: host_config_(std::move(cfg)), s3_config_(std::nullopt) {}
curl_comm::curl_comm(s3_config cfg)
: host_config_(std::nullopt), s3_config_(std::move(cfg)) {}
auto curl_comm::construct_url(CURL *curl, const std::string &relative_path,
const host_config &cfg) -> std::string {
static constexpr const auto http = 80U;
static constexpr const auto https = 443U;
auto custom_port = (((cfg.protocol == "http") &&
(cfg.api_port == http || cfg.api_port == 0U)) ||
((cfg.protocol == "https") &&
(cfg.api_port == https || cfg.api_port == 0U)))
? ""
: ":" + std::to_string(cfg.api_port);
auto url = cfg.protocol + "://" +
utils::string::trim_copy(cfg.host_name_or_ip) + custom_port;
static const auto complete_url = [](const std::string &current_path,
const std::string &parent_path,
std::string &final_url) -> std::string & {
final_url += utils::path::create_api_path(current_path);
if (utils::string::ends_with(parent_path, "/")) {
final_url += '/';
}
return final_url;
};
auto path = utils::path::combine("/", {cfg.path});
return relative_path.empty()
? complete_url(path, cfg.path, url)
: complete_url(utils::path::combine(
path, {url_encode(curl, relative_path, true)}),
relative_path, url);
}
auto curl_comm::create_host_config(const s3_config &cfg, bool use_s3_path_style)
-> host_config {
host_config host_cfg{};
host_cfg.api_password = cfg.secret_key;
host_cfg.api_user = cfg.access_key;
auto pos = cfg.url.find(':');
host_cfg.host_name_or_ip = cfg.url.substr(pos + 3U);
if (cfg.use_region_in_url && not cfg.region.empty()) {
auto parts = utils::string::split(host_cfg.host_name_or_ip, '.', false);
if (parts.size() > 1U) {
parts.insert(parts.begin() + 1U, cfg.region);
host_cfg.host_name_or_ip = utils::string::join(parts, '.');
}
}
if (not use_s3_path_style) {
host_cfg.host_name_or_ip = cfg.bucket + '.' + host_cfg.host_name_or_ip;
}
host_cfg.protocol = cfg.url.substr(0U, pos);
if (use_s3_path_style) {
host_cfg.path = '/' + cfg.bucket;
}
return host_cfg;
}
void curl_comm::enable_s3_path_style(bool enable) {
use_s3_path_style_ = enable;
}
auto curl_comm::make_request(const curl::requests::http_delete &del,
long &response_code,
stop_type &stop_requested) const -> bool {
return make_request(
s3_config_.has_value()
? create_host_config(s3_config_.value(), use_s3_path_style_)
: host_config_.value_or(host_config{}),
del, response_code, stop_requested);
}
auto curl_comm::make_request(const curl::requests::http_get &get,
long &response_code,
stop_type &stop_requested) const -> bool {
return make_request(
s3_config_.has_value()
? create_host_config(s3_config_.value(), use_s3_path_style_)
: host_config_.value_or(host_config{}),
get, response_code, stop_requested);
}
auto curl_comm::make_request(const curl::requests::http_head &head,
long &response_code,
stop_type &stop_requested) const -> bool {
return make_request(
s3_config_.has_value()
? create_host_config(s3_config_.value(), use_s3_path_style_)
: host_config_.value_or(host_config{}),
head, response_code, stop_requested);
}
auto curl_comm::make_request(const curl::requests::http_put_file &put_file,
long &response_code,
stop_type &stop_requested) const -> bool {
return make_request(
s3_config_.has_value()
? create_host_config(s3_config_.value(), use_s3_path_style_)
: host_config_.value_or(host_config{}),
put_file, response_code, stop_requested);
}
auto curl_comm::url_encode(CURL *curl, const std::string &data,
bool allow_slash) -> std::string {
auto *value =
curl_easy_escape(curl, data.c_str(), static_cast<int>(data.size()));
std::string ret = value;
curl_free(value);
if (allow_slash) {
utils::string::replace(ret, "%2F", "/");
}
return ret;
}
} // namespace repertory

View File

@@ -1,68 +1,68 @@
/*
Copyright <2018-2023> <scott.e.graves@protonmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "comm/curl/multi_request.hpp"
#include "utils/utils.hpp"
namespace repertory {
multi_request::multi_request(CURL *curl_handle, stop_type &stop_requested)
: curl_handle_(curl_handle),
stop_requested_(stop_requested),
multi_handle_(curl_multi_init()) {
curl_multi_add_handle(multi_handle_, curl_handle);
}
multi_request::~multi_request() {
curl_multi_remove_handle(multi_handle_, curl_handle_);
curl_easy_cleanup(curl_handle_);
curl_multi_cleanup(multi_handle_);
}
void multi_request::get_result(CURLcode &curl_code, long &http_code) {
static constexpr const auto timeout_ms = 100;
curl_code = CURLcode::CURLE_ABORTED_BY_CALLBACK;
http_code = -1;
auto error = false;
int running_handles = 0;
curl_multi_perform(multi_handle_, &running_handles);
while (not error && (running_handles > 0) && not stop_requested_) {
int ignored{};
curl_multi_wait(multi_handle_, nullptr, 0, timeout_ms, &ignored);
const auto ret = curl_multi_perform(multi_handle_, &running_handles);
error = (ret != CURLM_CALL_MULTI_PERFORM) && (ret != CURLM_OK);
}
if (not stop_requested_) {
int remaining_messages = 0;
auto *multi_result =
curl_multi_info_read(multi_handle_, &remaining_messages);
if ((multi_result != nullptr) && (multi_result->msg == CURLMSG_DONE)) {
curl_easy_getinfo(multi_result->easy_handle, CURLINFO_RESPONSE_CODE,
&http_code);
curl_code = multi_result->data.result;
}
}
}
} // namespace repertory
/*
Copyright <2018-2023> <scott.e.graves@protonmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "comm/curl/multi_request.hpp"
#include "utils/utils.hpp"
namespace repertory {
multi_request::multi_request(CURL *curl_handle, stop_type &stop_requested)
: curl_handle_(curl_handle),
stop_requested_(stop_requested),
multi_handle_(curl_multi_init()) {
curl_multi_add_handle(multi_handle_, curl_handle);
}
multi_request::~multi_request() {
curl_multi_remove_handle(multi_handle_, curl_handle_);
curl_easy_cleanup(curl_handle_);
curl_multi_cleanup(multi_handle_);
}
void multi_request::get_result(CURLcode &curl_code, long &http_code) {
static constexpr const auto timeout_ms = 100;
curl_code = CURLcode::CURLE_ABORTED_BY_CALLBACK;
http_code = -1;
auto error = false;
int running_handles = 0;
curl_multi_perform(multi_handle_, &running_handles);
while (not error && (running_handles > 0) && not stop_requested_) {
int ignored{};
curl_multi_wait(multi_handle_, nullptr, 0, timeout_ms, &ignored);
const auto ret = curl_multi_perform(multi_handle_, &running_handles);
error = (ret != CURLM_CALL_MULTI_PERFORM) && (ret != CURLM_OK);
}
if (not stop_requested_) {
int remaining_messages = 0;
auto *multi_result =
curl_multi_info_read(multi_handle_, &remaining_messages);
if ((multi_result != nullptr) && (multi_result->msg == CURLMSG_DONE)) {
curl_easy_getinfo(multi_result->easy_handle, CURLINFO_RESPONSE_CODE,
&http_code);
curl_code = multi_result->data.result;
}
}
}
} // namespace repertory

View File

@@ -1,69 +1,69 @@
/*
Copyright <2018-2023> <scott.e.graves@protonmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "comm/curl/requests/http_put_file.hpp"
#include "utils/string_utils.hpp"
namespace repertory::curl::requests {
auto http_put_file::set_method(CURL *curl, stop_type &stop_requested) const
-> bool {
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PUT");
curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
if (source_path.empty()) {
curl_easy_setopt(curl, CURLOPT_INFILESIZE_LARGE, 0L);
return true;
}
if (reader) {
curl_easy_setopt(curl, CURLOPT_READDATA, reader.get());
curl_easy_setopt(
curl, CURLOPT_READFUNCTION,
static_cast<curl_read_callback>(
utils::encryption::encrypting_reader::reader_function));
curl_easy_setopt(curl, CURLOPT_INFILESIZE_LARGE, reader->get_total_size());
return true;
}
read_info = std::make_shared<read_file_info>(read_file_info{
stop_requested,
});
if (native_file::create_or_open(source_path, read_info->nf) !=
api_error::success) {
return false;
}
read_info->nf->set_auto_close(true);
std::uint64_t file_size{};
if (not read_info->nf->get_file_size(file_size)) {
return false;
}
curl_easy_setopt(curl, CURLOPT_READDATA, read_info.get());
curl_easy_setopt(curl, CURLOPT_READFUNCTION, read_file_data);
curl_easy_setopt(curl, CURLOPT_INFILESIZE_LARGE, file_size);
return true;
}
} // namespace repertory::curl::requests
/*
Copyright <2018-2023> <scott.e.graves@protonmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "comm/curl/requests/http_put_file.hpp"
#include "utils/string_utils.hpp"
namespace repertory::curl::requests {
auto http_put_file::set_method(CURL *curl, stop_type &stop_requested) const
-> bool {
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PUT");
curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
if (source_path.empty()) {
curl_easy_setopt(curl, CURLOPT_INFILESIZE_LARGE, 0L);
return true;
}
if (reader) {
curl_easy_setopt(curl, CURLOPT_READDATA, reader.get());
curl_easy_setopt(
curl, CURLOPT_READFUNCTION,
static_cast<curl_read_callback>(
utils::encryption::encrypting_reader::reader_function));
curl_easy_setopt(curl, CURLOPT_INFILESIZE_LARGE, reader->get_total_size());
return true;
}
read_info = std::make_shared<read_file_info>(read_file_info{
stop_requested,
});
if (native_file::create_or_open(source_path, read_info->nf) !=
api_error::success) {
return false;
}
read_info->nf->set_auto_close(true);
std::uint64_t file_size{};
if (not read_info->nf->get_file_size(file_size)) {
return false;
}
curl_easy_setopt(curl, CURLOPT_READDATA, read_info.get());
curl_easy_setopt(curl, CURLOPT_READFUNCTION, read_file_data);
curl_easy_setopt(curl, CURLOPT_INFILESIZE_LARGE, file_size);
return true;
}
} // namespace repertory::curl::requests

View File

@@ -1,160 +1,160 @@
/*
Copyright <2018-2023> <scott.e.graves@protonmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "comm/packet/client_pool.hpp"
#include "events/event_system.hpp"
#include "events/events.hpp"
#include "utils/error_utils.hpp"
namespace repertory {
void client_pool::pool::execute(
std::uint64_t thread_id, const worker_callback &worker,
const worker_complete_callback &worker_complete) {
const auto index = thread_id % pool_queues_.size();
auto job = std::make_shared<work_item>(worker, worker_complete);
auto &pool_queue = pool_queues_[index];
unique_mutex_lock queue_lock(pool_queue->mutex);
pool_queue->queue.emplace_back(job);
pool_queue->notify.notify_all();
queue_lock.unlock();
}
client_pool::pool::pool(std::uint8_t pool_size) {
event_system::instance().raise<service_started>("client_pool");
for (std::uint8_t i = 0U; i < pool_size; i++) {
pool_queues_.emplace_back(std::make_unique<work_queue>());
}
for (std::size_t i = 0U; i < pool_queues_.size(); i++) {
pool_threads_.emplace_back([this]() {
const auto thread_index = thread_index_++;
auto &pool_queue = pool_queues_[thread_index];
auto &queue = pool_queue->queue;
auto &queue_mutex = pool_queue->mutex;
auto &queue_notify = pool_queue->notify;
unique_mutex_lock queue_lock(queue_mutex);
queue_notify.notify_all();
queue_lock.unlock();
while (not shutdown_) {
queue_lock.lock();
if (queue.empty()) {
queue_notify.wait(queue_lock);
}
while (not queue.empty()) {
auto item = queue.front();
queue.pop_front();
queue_notify.notify_all();
queue_lock.unlock();
try {
const auto result = item->work();
item->work_complete(result);
} catch (const std::exception &e) {
item->work_complete(utils::from_api_error(api_error::error));
utils::error::raise_error(__FUNCTION__, e,
"exception occurred in work item");
}
queue_lock.lock();
}
queue_notify.notify_all();
queue_lock.unlock();
}
queue_lock.lock();
while (not queue.empty()) {
auto job = queue.front();
queue.pop_front();
queue_notify.notify_all();
queue_lock.unlock();
job->work_complete(utils::from_api_error(api_error::download_stopped));
queue_lock.lock();
}
queue_notify.notify_all();
queue_lock.unlock();
});
}
}
void client_pool::pool::shutdown() {
shutdown_ = true;
for (auto &pool_queue : pool_queues_) {
mutex_lock lock(pool_queue->mutex);
pool_queue->notify.notify_all();
}
for (auto &thread : pool_threads_) {
thread.join();
}
pool_queues_.clear();
pool_threads_.clear();
}
void client_pool::execute(const std::string &client_id, std::uint64_t thread_id,
const worker_callback &worker,
const worker_complete_callback &worker_complete) {
unique_mutex_lock pool_lock(pool_mutex_);
if (shutdown_) {
pool_lock.unlock();
throw std::runtime_error("Client pool is shutdown");
}
if (not pool_lookup_[client_id]) {
pool_lookup_[client_id] = std::make_shared<pool>(pool_size_);
}
pool_lookup_[client_id]->execute(thread_id, worker, worker_complete);
pool_lock.unlock();
}
void client_pool::remove_client(const std::string &client_id) {
mutex_lock pool_lock(pool_mutex_);
pool_lookup_.erase(client_id);
}
void client_pool::shutdown() {
if (not shutdown_) {
event_system::instance().raise<service_shutdown_begin>("client_pool");
unique_mutex_lock pool_lock(pool_mutex_);
if (not shutdown_) {
shutdown_ = true;
for (auto &pool_entry : pool_lookup_) {
pool_entry.second->shutdown();
}
pool_lookup_.clear();
}
pool_lock.unlock();
event_system::instance().raise<service_shutdown_end>("client_pool");
}
}
} // namespace repertory
/*
Copyright <2018-2023> <scott.e.graves@protonmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "comm/packet/client_pool.hpp"
#include "events/event_system.hpp"
#include "events/events.hpp"
#include "utils/error_utils.hpp"
namespace repertory {
void client_pool::pool::execute(
std::uint64_t thread_id, const worker_callback &worker,
const worker_complete_callback &worker_complete) {
const auto index = thread_id % pool_queues_.size();
auto job = std::make_shared<work_item>(worker, worker_complete);
auto &pool_queue = pool_queues_[index];
unique_mutex_lock queue_lock(pool_queue->mutex);
pool_queue->queue.emplace_back(job);
pool_queue->notify.notify_all();
queue_lock.unlock();
}
client_pool::pool::pool(std::uint8_t pool_size) {
event_system::instance().raise<service_started>("client_pool");
for (std::uint8_t i = 0U; i < pool_size; i++) {
pool_queues_.emplace_back(std::make_unique<work_queue>());
}
for (std::size_t i = 0U; i < pool_queues_.size(); i++) {
pool_threads_.emplace_back([this]() {
const auto thread_index = thread_index_++;
auto &pool_queue = pool_queues_[thread_index];
auto &queue = pool_queue->queue;
auto &queue_mutex = pool_queue->mutex;
auto &queue_notify = pool_queue->notify;
unique_mutex_lock queue_lock(queue_mutex);
queue_notify.notify_all();
queue_lock.unlock();
while (not shutdown_) {
queue_lock.lock();
if (queue.empty()) {
queue_notify.wait(queue_lock);
}
while (not queue.empty()) {
auto item = queue.front();
queue.pop_front();
queue_notify.notify_all();
queue_lock.unlock();
try {
const auto result = item->work();
item->work_complete(result);
} catch (const std::exception &e) {
item->work_complete(utils::from_api_error(api_error::error));
utils::error::raise_error(__FUNCTION__, e,
"exception occurred in work item");
}
queue_lock.lock();
}
queue_notify.notify_all();
queue_lock.unlock();
}
queue_lock.lock();
while (not queue.empty()) {
auto job = queue.front();
queue.pop_front();
queue_notify.notify_all();
queue_lock.unlock();
job->work_complete(utils::from_api_error(api_error::download_stopped));
queue_lock.lock();
}
queue_notify.notify_all();
queue_lock.unlock();
});
}
}
void client_pool::pool::shutdown() {
shutdown_ = true;
for (auto &pool_queue : pool_queues_) {
mutex_lock lock(pool_queue->mutex);
pool_queue->notify.notify_all();
}
for (auto &thread : pool_threads_) {
thread.join();
}
pool_queues_.clear();
pool_threads_.clear();
}
void client_pool::execute(const std::string &client_id, std::uint64_t thread_id,
const worker_callback &worker,
const worker_complete_callback &worker_complete) {
unique_mutex_lock pool_lock(pool_mutex_);
if (shutdown_) {
pool_lock.unlock();
throw std::runtime_error("Client pool is shutdown");
}
if (not pool_lookup_[client_id]) {
pool_lookup_[client_id] = std::make_shared<pool>(pool_size_);
}
pool_lookup_[client_id]->execute(thread_id, worker, worker_complete);
pool_lock.unlock();
}
void client_pool::remove_client(const std::string &client_id) {
mutex_lock pool_lock(pool_mutex_);
pool_lookup_.erase(client_id);
}
void client_pool::shutdown() {
if (not shutdown_) {
event_system::instance().raise<service_shutdown_begin>("client_pool");
unique_mutex_lock pool_lock(pool_mutex_);
if (not shutdown_) {
shutdown_ = true;
for (auto &pool_entry : pool_lookup_) {
pool_entry.second->shutdown();
}
pool_lookup_.clear();
}
pool_lock.unlock();
event_system::instance().raise<service_shutdown_end>("client_pool");
}
}
} // namespace repertory

File diff suppressed because it is too large Load Diff

View File

@@ -1,247 +1,247 @@
/*
Copyright <2018-2023> <scott.e.graves@protonmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "comm/packet/packet_client.hpp"
#include "events/events.hpp"
#include "types/repertory.hpp"
#include "utils/error_utils.hpp"
#include "utils/timeout.hpp"
namespace repertory {
// clang-format off
E_SIMPLE2(packet_client_timeout, error, true,
std::string, event_name, en, E_STRING,
std::string, message, msg, E_STRING
);
// clang-format on
packet_client::packet_client(std::string host_name_or_ip,
std::uint8_t max_connections, std::uint16_t port,
std::uint16_t receive_timeout,
std::uint16_t send_timeout,
std::string encryption_token)
: io_context_(),
host_name_or_ip_(std::move(host_name_or_ip)),
max_connections_(max_connections ? max_connections : 20u),
port_(port),
receive_timeout_(receive_timeout),
send_timeout_(send_timeout),
encryption_token_(std::move(encryption_token)),
unique_id_(utils::create_uuid_string()) {}
packet_client::~packet_client() {
allow_connections_ = false;
close_all();
io_context_.stop();
}
void packet_client::close(client &cli) const {
try {
boost::system::error_code ec;
cli.socket.close(ec);
} catch (...) {
}
}
void packet_client::close_all() {
unique_mutex_lock clients_lock(clients_mutex_);
for (auto &c : clients_) {
close(*c.get());
}
clients_.clear();
unique_id_ = utils::create_uuid_string();
}
void packet_client::connect(client &c) {
try {
resolve();
boost::asio::connect(c.socket, resolve_results_);
c.socket.set_option(boost::asio::ip::tcp::no_delay(true));
c.socket.set_option(boost::asio::socket_base::linger(false, 0));
packet response;
const auto res = read_packet(c, response);
if (res != 0) {
throw std::runtime_error(std::to_string(res));
}
} catch (const std::exception &e) {
utils::error::raise_error(__FUNCTION__, e, "connection handshake failed");
}
}
auto packet_client::get_client() -> std::shared_ptr<packet_client::client> {
std::shared_ptr<client> ret;
unique_mutex_lock clients_lock(clients_mutex_);
if (allow_connections_) {
if (clients_.empty()) {
clients_lock.unlock();
ret = std::make_shared<client>(io_context_);
connect(*ret);
} else {
ret = clients_[0u];
utils::remove_element_from(clients_, ret);
clients_lock.unlock();
}
}
return ret;
}
void packet_client::put_client(std::shared_ptr<client> &c) {
mutex_lock clientsLock(clients_mutex_);
if (clients_.size() < max_connections_) {
clients_.emplace_back(c);
}
}
auto packet_client::read_packet(client &c, packet &response)
-> packet::error_type {
data_buffer buffer(sizeof(std::uint32_t));
const auto read_buffer = [&]() {
std::uint32_t offset = 0u;
while (offset < buffer.size()) {
const auto bytes_read = boost::asio::read(
c.socket,
boost::asio::buffer(&buffer[offset], buffer.size() - offset));
if (bytes_read <= 0) {
throw std::runtime_error("read failed|" + std::to_string(bytes_read));
}
offset += static_cast<std::uint32_t>(bytes_read);
}
};
read_buffer();
const auto size = boost::endian::big_to_native(
*reinterpret_cast<std::uint32_t *>(&buffer[0u]));
buffer.resize(size);
read_buffer();
response = std::move(buffer);
auto ret = response.decrypt(encryption_token_);
if (ret == 0) {
ret = response.decode(c.nonce);
}
return ret;
}
void packet_client::resolve() {
if (resolve_results_.empty()) {
resolve_results_ = tcp::resolver(io_context_)
.resolve({host_name_or_ip_, std::to_string(port_)});
}
}
auto packet_client::send(const std::string &method,
std::uint32_t &service_flags) -> packet::error_type {
packet request;
return send(method, request, service_flags);
}
auto packet_client::send(const std::string &method, packet &request,
std::uint32_t &service_flags) -> packet::error_type {
packet response;
return send(method, request, response, service_flags);
}
auto packet_client::send(const std::string &method, packet &request,
packet &response, std::uint32_t &service_flags)
-> packet::error_type {
auto success = false;
packet::error_type ret = utils::from_api_error(api_error::error);
request.encode_top(method);
request.encode_top(utils::get_thread_id());
request.encode_top(unique_id_);
request.encode_top(PACKET_SERVICE_FLAGS);
request.encode_top(get_repertory_version());
static const std::uint8_t max_attempts = 5u;
for (std::uint8_t i = 1u;
allow_connections_ && not success && (i <= max_attempts); i++) {
auto c = get_client();
if (c) {
try {
request.encode_top(c->nonce);
request.encrypt(encryption_token_);
timeout request_timeout(
[this, method, c]() {
event_system::instance().raise<packet_client_timeout>("request",
method);
close(*c.get());
},
std::chrono::seconds(send_timeout_));
std::uint32_t offset = 0u;
while (offset < request.get_size()) {
const auto bytes_written = boost::asio::write(
c->socket, boost::asio::buffer(&request[offset],
request.get_size() - offset));
if (bytes_written <= 0) {
throw std::runtime_error("write failed|" +
std::to_string(bytes_written));
}
offset += static_cast<std::uint32_t>(bytes_written);
}
request_timeout.disable();
timeout response_timeout(
[this, method, c]() {
event_system::instance().raise<packet_client_timeout>("response",
method);
close(*c.get());
},
std::chrono::seconds(receive_timeout_));
ret = read_packet(*c, response);
response_timeout.disable();
if (ret == 0) {
if ((ret = response.decode(service_flags)) == 0) {
packet::error_type res{};
if ((ret = response.decode(res)) == 0) {
ret = res;
success = true;
put_client(c);
}
}
}
} catch (const std::exception &e) {
utils::error::raise_error(__FUNCTION__, e, "send failed");
close_all();
if (allow_connections_ && (i < max_attempts)) {
std::this_thread::sleep_for(1s);
}
}
}
if (not allow_connections_) {
ret = utils::from_api_error(api_error::error);
success = true;
}
}
return CONVERT_STATUS_NOT_IMPLEMENTED(ret);
}
} // namespace repertory
/*
Copyright <2018-2023> <scott.e.graves@protonmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "comm/packet/packet_client.hpp"
#include "events/events.hpp"
#include "types/repertory.hpp"
#include "utils/error_utils.hpp"
#include "utils/timeout.hpp"
namespace repertory {
// clang-format off
E_SIMPLE2(packet_client_timeout, error, true,
std::string, event_name, en, E_STRING,
std::string, message, msg, E_STRING
);
// clang-format on
packet_client::packet_client(std::string host_name_or_ip,
std::uint8_t max_connections, std::uint16_t port,
std::uint16_t receive_timeout,
std::uint16_t send_timeout,
std::string encryption_token)
: io_context_(),
host_name_or_ip_(std::move(host_name_or_ip)),
max_connections_(max_connections ? max_connections : 20u),
port_(port),
receive_timeout_(receive_timeout),
send_timeout_(send_timeout),
encryption_token_(std::move(encryption_token)),
unique_id_(utils::create_uuid_string()) {}
packet_client::~packet_client() {
allow_connections_ = false;
close_all();
io_context_.stop();
}
void packet_client::close(client &cli) const {
try {
boost::system::error_code ec;
cli.socket.close(ec);
} catch (...) {
}
}
void packet_client::close_all() {
unique_mutex_lock clients_lock(clients_mutex_);
for (auto &c : clients_) {
close(*c.get());
}
clients_.clear();
unique_id_ = utils::create_uuid_string();
}
void packet_client::connect(client &c) {
try {
resolve();
boost::asio::connect(c.socket, resolve_results_);
c.socket.set_option(boost::asio::ip::tcp::no_delay(true));
c.socket.set_option(boost::asio::socket_base::linger(false, 0));
packet response;
const auto res = read_packet(c, response);
if (res != 0) {
throw std::runtime_error(std::to_string(res));
}
} catch (const std::exception &e) {
utils::error::raise_error(__FUNCTION__, e, "connection handshake failed");
}
}
auto packet_client::get_client() -> std::shared_ptr<packet_client::client> {
std::shared_ptr<client> ret;
unique_mutex_lock clients_lock(clients_mutex_);
if (allow_connections_) {
if (clients_.empty()) {
clients_lock.unlock();
ret = std::make_shared<client>(io_context_);
connect(*ret);
} else {
ret = clients_[0u];
utils::remove_element_from(clients_, ret);
clients_lock.unlock();
}
}
return ret;
}
void packet_client::put_client(std::shared_ptr<client> &c) {
mutex_lock clientsLock(clients_mutex_);
if (clients_.size() < max_connections_) {
clients_.emplace_back(c);
}
}
auto packet_client::read_packet(client &c, packet &response)
-> packet::error_type {
data_buffer buffer(sizeof(std::uint32_t));
const auto read_buffer = [&]() {
std::uint32_t offset = 0u;
while (offset < buffer.size()) {
const auto bytes_read = boost::asio::read(
c.socket,
boost::asio::buffer(&buffer[offset], buffer.size() - offset));
if (bytes_read <= 0) {
throw std::runtime_error("read failed|" + std::to_string(bytes_read));
}
offset += static_cast<std::uint32_t>(bytes_read);
}
};
read_buffer();
const auto size = boost::endian::big_to_native(
*reinterpret_cast<std::uint32_t *>(&buffer[0u]));
buffer.resize(size);
read_buffer();
response = std::move(buffer);
auto ret = response.decrypt(encryption_token_);
if (ret == 0) {
ret = response.decode(c.nonce);
}
return ret;
}
void packet_client::resolve() {
if (resolve_results_.empty()) {
resolve_results_ = tcp::resolver(io_context_)
.resolve({host_name_or_ip_, std::to_string(port_)});
}
}
auto packet_client::send(const std::string &method,
std::uint32_t &service_flags) -> packet::error_type {
packet request;
return send(method, request, service_flags);
}
auto packet_client::send(const std::string &method, packet &request,
std::uint32_t &service_flags) -> packet::error_type {
packet response;
return send(method, request, response, service_flags);
}
auto packet_client::send(const std::string &method, packet &request,
packet &response, std::uint32_t &service_flags)
-> packet::error_type {
auto success = false;
packet::error_type ret = utils::from_api_error(api_error::error);
request.encode_top(method);
request.encode_top(utils::get_thread_id());
request.encode_top(unique_id_);
request.encode_top(PACKET_SERVICE_FLAGS);
request.encode_top(get_repertory_version());
static const std::uint8_t max_attempts = 5u;
for (std::uint8_t i = 1u;
allow_connections_ && not success && (i <= max_attempts); i++) {
auto c = get_client();
if (c) {
try {
request.encode_top(c->nonce);
request.encrypt(encryption_token_);
timeout request_timeout(
[this, method, c]() {
event_system::instance().raise<packet_client_timeout>("request",
method);
close(*c.get());
},
std::chrono::seconds(send_timeout_));
std::uint32_t offset = 0u;
while (offset < request.get_size()) {
const auto bytes_written = boost::asio::write(
c->socket, boost::asio::buffer(&request[offset],
request.get_size() - offset));
if (bytes_written <= 0) {
throw std::runtime_error("write failed|" +
std::to_string(bytes_written));
}
offset += static_cast<std::uint32_t>(bytes_written);
}
request_timeout.disable();
timeout response_timeout(
[this, method, c]() {
event_system::instance().raise<packet_client_timeout>("response",
method);
close(*c.get());
},
std::chrono::seconds(receive_timeout_));
ret = read_packet(*c, response);
response_timeout.disable();
if (ret == 0) {
if ((ret = response.decode(service_flags)) == 0) {
packet::error_type res{};
if ((ret = response.decode(res)) == 0) {
ret = res;
success = true;
put_client(c);
}
}
}
} catch (const std::exception &e) {
utils::error::raise_error(__FUNCTION__, e, "send failed");
close_all();
if (allow_connections_ && (i < max_attempts)) {
std::this_thread::sleep_for(1s);
}
}
}
if (not allow_connections_) {
ret = utils::from_api_error(api_error::error);
success = true;
}
}
return CONVERT_STATUS_NOT_IMPLEMENTED(ret);
}
} // namespace repertory

View File

@@ -1,250 +1,250 @@
/*
Copyright <2018-2023> <scott.e.graves@protonmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "comm/packet/packet_server.hpp"
#include "comm/packet/packet.hpp"
#include "events/event_system.hpp"
#include "events/events.hpp"
#include "types/repertory.hpp"
#include "utils/error_utils.hpp"
#include "utils/string_utils.hpp"
#include "utils/utils.hpp"
namespace repertory {
using std::thread;
packet_server::packet_server(std::uint16_t port, std::string token,
std::uint8_t pool_size, closed_callback closed,
message_handler_callback message_handler)
: encryption_token_(std::move(token)),
closed_(std::move(closed)),
message_handler_(std::move(message_handler)) {
initialize(port, pool_size);
event_system::instance().raise<service_started>("packet_server");
}
packet_server::~packet_server() {
event_system::instance().raise<service_shutdown_begin>("packet_server");
std::thread([this]() {
for (std::size_t i = 0u; i < service_threads_.size(); i++) {
io_context_.stop();
}
}).detach();
server_thread_->join();
server_thread_.reset();
event_system::instance().raise<service_shutdown_end>("packet_server");
}
void packet_server::add_client(connection &c, const std::string &client_id) {
c.client_id = client_id;
recur_mutex_lock connection_lock(connection_mutex_);
if (connection_lookup_.find(client_id) == connection_lookup_.end()) {
connection_lookup_[client_id] = 1u;
} else {
connection_lookup_[client_id]++;
}
}
void packet_server::initialize(const uint16_t &port, uint8_t pool_size) {
pool_size = std::max(uint8_t(1u), pool_size);
server_thread_ = std::make_unique<std::thread>([this, port, pool_size]() {
tcp::acceptor acceptor(io_context_);
try {
const auto endpoint = tcp::endpoint(tcp::v4(), port);
acceptor.open(endpoint.protocol());
acceptor.set_option(socket_base::reuse_address(true));
acceptor.bind(endpoint);
acceptor.listen();
} catch (const std::exception &e) {
repertory::utils::error::raise_error(__FUNCTION__, e,
"exception occurred");
}
listen_for_connection(acceptor);
for (std::uint8_t i = 0u; i < pool_size; i++) {
service_threads_.emplace_back([this]() { io_context_.run(); });
}
for (auto &th : service_threads_) {
th.join();
}
});
}
void packet_server::listen_for_connection(tcp::acceptor &acceptor) {
auto c = std::make_shared<packet_server::connection>(io_context_, acceptor);
acceptor.async_accept(c->socket,
boost::bind(&packet_server::on_accept, this, c,
boost::asio::placeholders::error));
}
void packet_server::on_accept(std::shared_ptr<connection> c,
boost::system::error_code ec) {
listen_for_connection(c->acceptor);
if (ec) {
utils::error::raise_error(__FUNCTION__, ec.message());
std::this_thread::sleep_for(1s);
} else {
c->socket.set_option(boost::asio::ip::tcp::no_delay(true));
c->socket.set_option(boost::asio::socket_base::linger(false, 0));
c->generate_nonce();
packet response;
send_response(c, 0, response);
}
}
void packet_server::read_header(std::shared_ptr<connection> c) {
static const std::string function_name = __FUNCTION__;
c->buffer.resize(sizeof(std::uint32_t));
boost::asio::async_read(
c->socket, boost::asio::buffer(&c->buffer[0u], c->buffer.size()),
[this, c](boost::system::error_code ec, std::size_t) {
if (ec) {
remove_client(*c);
repertory::utils::error::raise_error(function_name, ec.message());
} else {
auto to_read = *reinterpret_cast<std::uint32_t *>(&c->buffer[0u]);
boost::endian::big_to_native_inplace(to_read);
read_packet(c, to_read);
}
});
}
void packet_server::read_packet(std::shared_ptr<connection> c,
std::uint32_t data_size) {
try {
const auto read_buffer = [&]() {
std::uint32_t offset = 0u;
while (offset < c->buffer.size()) {
const auto bytes_read = boost::asio::read(
c->socket,
boost::asio::buffer(&c->buffer[offset], c->buffer.size() - offset));
if (bytes_read <= 0) {
throw std::runtime_error("read failed|" + std::to_string(bytes_read));
}
offset += static_cast<std::uint32_t>(bytes_read);
}
};
auto should_send_response = true;
auto response = std::make_shared<packet>();
c->buffer.resize(data_size);
read_buffer();
packet::error_type ret;
auto request = std::make_shared<packet>(c->buffer);
if (request->decrypt(encryption_token_) == 0) {
std::string nonce;
if ((ret = request->decode(nonce)) == 0) {
if (nonce != c->nonce) {
throw std::runtime_error("invalid nonce");
}
c->generate_nonce();
std::string version;
if ((ret = request->decode(version)) == 0) {
if (utils::compare_version_strings(
version, REPERTORY_MIN_REMOTE_VERSION) >= 0) {
std::uint32_t service_flags = 0u;
DECODE_OR_IGNORE(request, service_flags);
std::string client_id;
DECODE_OR_IGNORE(request, client_id);
std::uint64_t thread_id = 0u;
DECODE_OR_IGNORE(request, thread_id);
std::string method;
DECODE_OR_IGNORE(request, method);
if (ret == 0) {
if (c->client_id.empty()) {
add_client(*c, client_id);
}
should_send_response = false;
message_handler_(service_flags, client_id, thread_id, method,
request.get(), *response,
[this, c, request,
response](const packet::error_type &result) {
this->send_response(c, result, *response);
});
}
} else {
ret = utils::from_api_error(api_error::incompatible_version);
}
} else {
ret = utils::from_api_error(api_error::invalid_version);
}
} else {
throw std::runtime_error("invalid nonce");
}
} else {
throw std::runtime_error("decryption failed");
}
if (should_send_response) {
send_response(c, ret, *response);
}
} catch (const std::exception &e) {
remove_client(*c);
utils::error::raise_error(__FUNCTION__, e, "exception occurred");
}
}
void packet_server::remove_client(connection &c) {
if (not c.client_id.empty()) {
recur_mutex_lock connection_lock(connection_mutex_);
if (not --connection_lookup_[c.client_id]) {
connection_lookup_.erase(c.client_id);
closed_(c.client_id);
}
}
}
void packet_server::send_response(std::shared_ptr<connection> c,
const packet::error_type &result,
packet &response) {
static const std::string function_name = __FUNCTION__;
response.encode_top(result);
response.encode_top(PACKET_SERVICE_FLAGS);
response.encode_top(c->nonce);
response.encrypt(encryption_token_);
response.transfer_into(c->buffer);
boost::asio::async_write(
c->socket, boost::asio::buffer(c->buffer),
[this, c](boost::system::error_code ec, std::size_t /*length*/) {
if (ec) {
remove_client(*c);
utils::error::raise_error(function_name, ec.message());
} else {
read_header(c);
}
});
}
} // namespace repertory
/*
Copyright <2018-2023> <scott.e.graves@protonmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "comm/packet/packet_server.hpp"
#include "comm/packet/packet.hpp"
#include "events/event_system.hpp"
#include "events/events.hpp"
#include "types/repertory.hpp"
#include "utils/error_utils.hpp"
#include "utils/string_utils.hpp"
#include "utils/utils.hpp"
namespace repertory {
using std::thread;
packet_server::packet_server(std::uint16_t port, std::string token,
std::uint8_t pool_size, closed_callback closed,
message_handler_callback message_handler)
: encryption_token_(std::move(token)),
closed_(std::move(closed)),
message_handler_(std::move(message_handler)) {
initialize(port, pool_size);
event_system::instance().raise<service_started>("packet_server");
}
packet_server::~packet_server() {
event_system::instance().raise<service_shutdown_begin>("packet_server");
std::thread([this]() {
for (std::size_t i = 0u; i < service_threads_.size(); i++) {
io_context_.stop();
}
}).detach();
server_thread_->join();
server_thread_.reset();
event_system::instance().raise<service_shutdown_end>("packet_server");
}
void packet_server::add_client(connection &c, const std::string &client_id) {
c.client_id = client_id;
recur_mutex_lock connection_lock(connection_mutex_);
if (connection_lookup_.find(client_id) == connection_lookup_.end()) {
connection_lookup_[client_id] = 1u;
} else {
connection_lookup_[client_id]++;
}
}
void packet_server::initialize(const uint16_t &port, uint8_t pool_size) {
pool_size = std::max(uint8_t(1u), pool_size);
server_thread_ = std::make_unique<std::thread>([this, port, pool_size]() {
tcp::acceptor acceptor(io_context_);
try {
const auto endpoint = tcp::endpoint(tcp::v4(), port);
acceptor.open(endpoint.protocol());
acceptor.set_option(socket_base::reuse_address(true));
acceptor.bind(endpoint);
acceptor.listen();
} catch (const std::exception &e) {
repertory::utils::error::raise_error(__FUNCTION__, e,
"exception occurred");
}
listen_for_connection(acceptor);
for (std::uint8_t i = 0u; i < pool_size; i++) {
service_threads_.emplace_back([this]() { io_context_.run(); });
}
for (auto &th : service_threads_) {
th.join();
}
});
}
void packet_server::listen_for_connection(tcp::acceptor &acceptor) {
auto c = std::make_shared<packet_server::connection>(io_context_, acceptor);
acceptor.async_accept(c->socket,
boost::bind(&packet_server::on_accept, this, c,
boost::asio::placeholders::error));
}
void packet_server::on_accept(std::shared_ptr<connection> c,
boost::system::error_code ec) {
listen_for_connection(c->acceptor);
if (ec) {
utils::error::raise_error(__FUNCTION__, ec.message());
std::this_thread::sleep_for(1s);
} else {
c->socket.set_option(boost::asio::ip::tcp::no_delay(true));
c->socket.set_option(boost::asio::socket_base::linger(false, 0));
c->generate_nonce();
packet response;
send_response(c, 0, response);
}
}
void packet_server::read_header(std::shared_ptr<connection> c) {
static const std::string function_name = __FUNCTION__;
c->buffer.resize(sizeof(std::uint32_t));
boost::asio::async_read(
c->socket, boost::asio::buffer(&c->buffer[0u], c->buffer.size()),
[this, c](boost::system::error_code ec, std::size_t) {
if (ec) {
remove_client(*c);
repertory::utils::error::raise_error(function_name, ec.message());
} else {
auto to_read = *reinterpret_cast<std::uint32_t *>(&c->buffer[0u]);
boost::endian::big_to_native_inplace(to_read);
read_packet(c, to_read);
}
});
}
void packet_server::read_packet(std::shared_ptr<connection> c,
std::uint32_t data_size) {
try {
const auto read_buffer = [&]() {
std::uint32_t offset = 0u;
while (offset < c->buffer.size()) {
const auto bytes_read = boost::asio::read(
c->socket,
boost::asio::buffer(&c->buffer[offset], c->buffer.size() - offset));
if (bytes_read <= 0) {
throw std::runtime_error("read failed|" + std::to_string(bytes_read));
}
offset += static_cast<std::uint32_t>(bytes_read);
}
};
auto should_send_response = true;
auto response = std::make_shared<packet>();
c->buffer.resize(data_size);
read_buffer();
packet::error_type ret;
auto request = std::make_shared<packet>(c->buffer);
if (request->decrypt(encryption_token_) == 0) {
std::string nonce;
if ((ret = request->decode(nonce)) == 0) {
if (nonce != c->nonce) {
throw std::runtime_error("invalid nonce");
}
c->generate_nonce();
std::string version;
if ((ret = request->decode(version)) == 0) {
if (utils::compare_version_strings(
version, REPERTORY_MIN_REMOTE_VERSION) >= 0) {
std::uint32_t service_flags = 0u;
DECODE_OR_IGNORE(request, service_flags);
std::string client_id;
DECODE_OR_IGNORE(request, client_id);
std::uint64_t thread_id = 0u;
DECODE_OR_IGNORE(request, thread_id);
std::string method;
DECODE_OR_IGNORE(request, method);
if (ret == 0) {
if (c->client_id.empty()) {
add_client(*c, client_id);
}
should_send_response = false;
message_handler_(service_flags, client_id, thread_id, method,
request.get(), *response,
[this, c, request,
response](const packet::error_type &result) {
this->send_response(c, result, *response);
});
}
} else {
ret = utils::from_api_error(api_error::incompatible_version);
}
} else {
ret = utils::from_api_error(api_error::invalid_version);
}
} else {
throw std::runtime_error("invalid nonce");
}
} else {
throw std::runtime_error("decryption failed");
}
if (should_send_response) {
send_response(c, ret, *response);
}
} catch (const std::exception &e) {
remove_client(*c);
utils::error::raise_error(__FUNCTION__, e, "exception occurred");
}
}
void packet_server::remove_client(connection &c) {
if (not c.client_id.empty()) {
recur_mutex_lock connection_lock(connection_mutex_);
if (not --connection_lookup_[c.client_id]) {
connection_lookup_.erase(c.client_id);
closed_(c.client_id);
}
}
}
void packet_server::send_response(std::shared_ptr<connection> c,
const packet::error_type &result,
packet &response) {
static const std::string function_name = __FUNCTION__;
response.encode_top(result);
response.encode_top(PACKET_SERVICE_FLAGS);
response.encode_top(c->nonce);
response.encrypt(encryption_token_);
response.transfer_into(c->buffer);
boost::asio::async_write(
c->socket, boost::asio::buffer(c->buffer),
[this, c](boost::system::error_code ec, std::size_t /*length*/) {
if (ec) {
remove_client(*c);
utils::error::raise_error(function_name, ec.message());
} else {
read_header(c);
}
});
}
} // namespace repertory

View File

@@ -1,55 +1,55 @@
/*
Copyright <2018-2023> <scott.e.graves@protonmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "types/repertory.hpp"
#include "utils/utils.hpp"
namespace repertory {
auto get_repertory_git_revision() -> const std::string & {
static const std::string git_revision = "@REPERTORY_GIT_REV@";
return git_revision;
}
auto get_repertory_version() -> const std::string & {
static const std::string version = "@REPERTORY_VERSION@";
return version;
}
void repertory_init() {
#ifdef REPERTORY_MUSL
pthread_attr_t a;
memset(&a, 0, sizeof(pthread_attr_t));
pthread_attr_setstacksize(&a, 8U * 1024U * 1024U);
pthread_attr_setguardsize(&a, 4096U);
pthread_setattr_default_np(&a);
#endif
const auto err = sodium_init();
if (err < 0) {
std::cerr << "failed to initialize libsodium|err|" << err << std::endl;
exit(static_cast<std::int32_t>(exit_code::init_failed));
}
curl_global_init(CURL_GLOBAL_DEFAULT);
}
void repertory_shutdown() { curl_global_cleanup(); }
} // namespace repertory
/*
Copyright <2018-2023> <scott.e.graves@protonmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "types/repertory.hpp"
#include "utils/utils.hpp"
namespace repertory {
auto get_repertory_git_revision() -> const std::string & {
static const std::string git_revision = "@REPERTORY_GIT_REV@";
return git_revision;
}
auto get_repertory_version() -> const std::string & {
static const std::string version = "@REPERTORY_VERSION@";
return version;
}
void repertory_init() {
#ifdef REPERTORY_MUSL
pthread_attr_t a;
memset(&a, 0, sizeof(pthread_attr_t));
pthread_attr_setstacksize(&a, 8U * 1024U * 1024U);
pthread_attr_setguardsize(&a, 4096U);
pthread_setattr_default_np(&a);
#endif
const auto err = sodium_init();
if (err < 0) {
std::cerr << "failed to initialize libsodium|err|" << err << std::endl;
exit(static_cast<std::int32_t>(exit_code::init_failed));
}
curl_global_init(CURL_GLOBAL_DEFAULT);
}
void repertory_shutdown() { curl_global_cleanup(); }
} // namespace repertory

View File

@@ -1,92 +1,92 @@
/*
Copyright <2018-2023> <scott.e.graves@protonmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "drives/directory_cache.hpp"
#include "drives/directory_iterator.hpp"
#include "events/event_system.hpp"
#include "events/events.hpp"
#include "types/repertory.hpp"
namespace repertory {
void directory_cache::execute_action(const std::string &api_path,
const execute_callback &execute) {
recur_mutex_lock directory_lock(directory_mutex_);
if ((directory_lookup_.find(api_path) != directory_lookup_.end())) {
execute(*directory_lookup_[api_path].iterator);
}
}
auto directory_cache::remove_directory(const std::string &api_path)
-> directory_iterator * {
directory_iterator *ret = nullptr;
recur_mutex_lock directory_lock(directory_mutex_);
if (directory_lookup_.find(api_path) != directory_lookup_.end()) {
ret = directory_lookup_[api_path].iterator;
directory_lookup_.erase(api_path);
}
return ret;
}
void directory_cache::remove_directory(directory_iterator *iterator) {
if (iterator) {
recur_mutex_lock directory_lock(directory_mutex_);
const auto it =
std::find_if(directory_lookup_.begin(), directory_lookup_.end(),
[&iterator](const auto &kv) -> bool {
return kv.second.iterator == iterator;
});
if (it != directory_lookup_.end()) {
directory_lookup_.erase(it->first);
}
}
}
void directory_cache::service_function() {
unique_recur_mutex_lock directory_lock(directory_mutex_);
auto lookup = directory_lookup_;
directory_lock.unlock();
for (const auto &kv : lookup) {
if (std::chrono::duration_cast<std::chrono::seconds>(
std::chrono::system_clock::now() - kv.second.last_update) >= 120s) {
directory_lock.lock();
directory_lookup_.erase(kv.first);
directory_lock.unlock();
}
}
if (not get_stop_requested()) {
unique_mutex_lock shutdown_lock(get_mutex());
if (not get_stop_requested()) {
get_notify().wait_for(shutdown_lock, 15s);
}
}
}
void directory_cache::set_directory(const std::string &api_path,
directory_iterator *iterator) {
recur_mutex_lock directory_lock(directory_mutex_);
directory_lookup_[api_path] = {iterator};
}
} // namespace repertory
/*
Copyright <2018-2023> <scott.e.graves@protonmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "drives/directory_cache.hpp"
#include "drives/directory_iterator.hpp"
#include "events/event_system.hpp"
#include "events/events.hpp"
#include "types/repertory.hpp"
namespace repertory {
void directory_cache::execute_action(const std::string &api_path,
const execute_callback &execute) {
recur_mutex_lock directory_lock(directory_mutex_);
if ((directory_lookup_.find(api_path) != directory_lookup_.end())) {
execute(*directory_lookup_[api_path].iterator);
}
}
auto directory_cache::remove_directory(const std::string &api_path)
-> directory_iterator * {
directory_iterator *ret = nullptr;
recur_mutex_lock directory_lock(directory_mutex_);
if (directory_lookup_.find(api_path) != directory_lookup_.end()) {
ret = directory_lookup_[api_path].iterator;
directory_lookup_.erase(api_path);
}
return ret;
}
void directory_cache::remove_directory(directory_iterator *iterator) {
if (iterator) {
recur_mutex_lock directory_lock(directory_mutex_);
const auto it =
std::find_if(directory_lookup_.begin(), directory_lookup_.end(),
[&iterator](const auto &kv) -> bool {
return kv.second.iterator == iterator;
});
if (it != directory_lookup_.end()) {
directory_lookup_.erase(it->first);
}
}
}
void directory_cache::service_function() {
unique_recur_mutex_lock directory_lock(directory_mutex_);
auto lookup = directory_lookup_;
directory_lock.unlock();
for (const auto &kv : lookup) {
if (std::chrono::duration_cast<std::chrono::seconds>(
std::chrono::system_clock::now() - kv.second.last_update) >= 120s) {
directory_lock.lock();
directory_lookup_.erase(kv.first);
directory_lock.unlock();
}
}
if (not get_stop_requested()) {
unique_mutex_lock shutdown_lock(get_mutex());
if (not get_stop_requested()) {
get_notify().wait_for(shutdown_lock, 15s);
}
}
}
void directory_cache::set_directory(const std::string &api_path,
directory_iterator *iterator) {
recur_mutex_lock directory_lock(directory_mutex_);
directory_lookup_[api_path] = {iterator};
}
} // namespace repertory

View File

@@ -1,156 +1,156 @@
/*
Copyright <2018-2023> <scott.e.graves@protonmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "drives/directory_iterator.hpp"
#include "utils/error_utils.hpp"
#include "utils/path_utils.hpp"
namespace repertory {
#ifndef _WIN32
auto directory_iterator::fill_buffer(const remote::file_offset &offset,
fuse_fill_dir_t filler_function,
void *buffer,
populate_stat_callback populate_stat)
-> int {
if (offset < items_.size()) {
try {
std::string item_name;
struct stat st {};
struct stat *pst = nullptr;
switch (offset) {
case 0: {
item_name = ".";
} break;
case 1: {
item_name = "..";
} break;
default: {
const auto &item = items_[offset];
item_name = utils::path::strip_to_file_name(item.api_path);
populate_stat(item.api_path, item.size, item.meta, item.directory, &st);
pst = &st;
} break;
}
#if FUSE_USE_VERSION >= 30
if (filler_function(buffer, &item_name[0], pst, offset + 1,
FUSE_FILL_DIR_PLUS) != 0) {
#else
if (filler_function(buffer, &item_name[0], pst, offset + 1) != 0) {
#endif
errno = ENOMEM;
return -1;
}
} catch (const std::exception &e) {
utils::error::raise_error(__FUNCTION__, e,
"failed to fill fuse directory buffer");
}
return 0;
}
errno = 120;
return -1;
}
#endif // !_WIN32
auto directory_iterator::get(std::size_t offset, std::string &item) -> int {
if (offset < items_.size()) {
item = items_[offset].api_path;
return 0;
}
errno = 120;
return -1;
}
auto directory_iterator::get_directory_item(std::size_t offset,
directory_item &di) -> api_error {
if (offset < items_.size()) {
di = items_[offset];
return api_error::success;
}
return api_error::directory_end_of_files;
}
auto directory_iterator::get_directory_item(const std::string &api_path,
directory_item &di) -> api_error {
auto iter =
std::find_if(items_.begin(), items_.end(), [&](const auto &item) -> bool {
return api_path == item.api_path;
});
if (iter != items_.end()) {
di = *iter;
return api_error::success;
}
return api_error::item_not_found;
}
auto directory_iterator::get_json(std::size_t offset, json &item) -> int {
if (offset < items_.size()) {
item = items_[offset].to_json();
return 0;
}
errno = 120;
return -1;
}
auto directory_iterator::get_next_directory_offset(
const std::string &api_path) const -> std::size_t {
const auto it = std::find_if(
items_.begin(), items_.end(),
[&api_path](const auto &di) -> bool { return api_path == di.api_path; });
return (it == items_.end())
? 0
: std::distance(items_.begin(), it) + std::size_t(1u);
}
auto directory_iterator::operator=(const directory_iterator &iterator) noexcept
-> directory_iterator & {
if (this != &iterator) {
items_ = iterator.items_;
}
return *this;
}
auto directory_iterator::operator=(directory_iterator &&iterator) noexcept
-> directory_iterator & {
if (this != &iterator) {
items_ = std::move(iterator.items_);
}
return *this;
}
auto directory_iterator::operator=(directory_item_list list) noexcept
-> directory_iterator & {
if (&items_ != &list) {
items_ = std::move(list);
}
return *this;
}
} // namespace repertory
/*
Copyright <2018-2023> <scott.e.graves@protonmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "drives/directory_iterator.hpp"
#include "utils/error_utils.hpp"
#include "utils/path_utils.hpp"
namespace repertory {
#ifndef _WIN32
auto directory_iterator::fill_buffer(const remote::file_offset &offset,
fuse_fill_dir_t filler_function,
void *buffer,
populate_stat_callback populate_stat)
-> int {
if (offset < items_.size()) {
try {
std::string item_name;
struct stat st {};
struct stat *pst = nullptr;
switch (offset) {
case 0: {
item_name = ".";
} break;
case 1: {
item_name = "..";
} break;
default: {
const auto &item = items_[offset];
item_name = utils::path::strip_to_file_name(item.api_path);
populate_stat(item.api_path, item.size, item.meta, item.directory, &st);
pst = &st;
} break;
}
#if FUSE_USE_VERSION >= 30
if (filler_function(buffer, &item_name[0], pst, offset + 1,
FUSE_FILL_DIR_PLUS) != 0) {
#else
if (filler_function(buffer, &item_name[0], pst, offset + 1) != 0) {
#endif
errno = ENOMEM;
return -1;
}
} catch (const std::exception &e) {
utils::error::raise_error(__FUNCTION__, e,
"failed to fill fuse directory buffer");
}
return 0;
}
errno = 120;
return -1;
}
#endif // !_WIN32
auto directory_iterator::get(std::size_t offset, std::string &item) -> int {
if (offset < items_.size()) {
item = items_[offset].api_path;
return 0;
}
errno = 120;
return -1;
}
auto directory_iterator::get_directory_item(std::size_t offset,
directory_item &di) -> api_error {
if (offset < items_.size()) {
di = items_[offset];
return api_error::success;
}
return api_error::directory_end_of_files;
}
auto directory_iterator::get_directory_item(const std::string &api_path,
directory_item &di) -> api_error {
auto iter =
std::find_if(items_.begin(), items_.end(), [&](const auto &item) -> bool {
return api_path == item.api_path;
});
if (iter != items_.end()) {
di = *iter;
return api_error::success;
}
return api_error::item_not_found;
}
auto directory_iterator::get_json(std::size_t offset, json &item) -> int {
if (offset < items_.size()) {
item = items_[offset].to_json();
return 0;
}
errno = 120;
return -1;
}
auto directory_iterator::get_next_directory_offset(
const std::string &api_path) const -> std::size_t {
const auto it = std::find_if(
items_.begin(), items_.end(),
[&api_path](const auto &di) -> bool { return api_path == di.api_path; });
return (it == items_.end())
? 0
: std::distance(items_.begin(), it) + std::size_t(1u);
}
auto directory_iterator::operator=(const directory_iterator &iterator) noexcept
-> directory_iterator & {
if (this != &iterator) {
items_ = iterator.items_;
}
return *this;
}
auto directory_iterator::operator=(directory_iterator &&iterator) noexcept
-> directory_iterator & {
if (this != &iterator) {
items_ = std::move(iterator.items_);
}
return *this;
}
auto directory_iterator::operator=(directory_item_list list) noexcept
-> directory_iterator & {
if (&items_ != &list) {
items_ = std::move(list);
}
return *this;
}
} // namespace repertory

View File

@@ -1,141 +1,141 @@
/*
Copyright <2018-2023> <scott.e.graves@protonmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "drives/eviction.hpp"
#include "app_config.hpp"
#include "file_manager/i_file_manager.hpp"
#include "providers/i_provider.hpp"
#include "types/repertory.hpp"
#include "utils/error_utils.hpp"
#include "utils/file_utils.hpp"
#include "utils/unix/unix_utils.hpp"
#include "utils/utils.hpp"
namespace repertory {
auto eviction::check_minimum_requirements(const std::string &file_path)
-> bool {
std::uint64_t file_size{};
if (not utils::file::get_file_size(file_path, file_size)) {
utils::error::raise_error(__FUNCTION__, utils::get_last_error_code(),
file_path, "failed to get file size");
return false;
}
auto ret = false;
if (file_size) {
std::uint64_t reference_time{};
if ((ret =
config_.get_eviction_uses_accessed_time()
? utils::file::get_accessed_time(file_path, reference_time)
: utils::file::get_modified_time(file_path, reference_time))) {
#ifdef _WIN32
const auto now = std::chrono::system_clock::now();
const auto delay =
std::chrono::minutes(config_.get_eviction_delay_mins());
ret = ((std::chrono::system_clock::from_time_t(reference_time) + delay) <=
now);
#else
const auto now = utils::get_time_now();
const auto delay =
(config_.get_eviction_delay_mins() * 60L) * NANOS_PER_SECOND;
ret = ((reference_time + delay) <= now);
#endif
}
}
return ret;
}
auto eviction::get_filtered_cached_files() -> std::deque<std::string> {
auto list =
utils::file::get_directory_files(config_.get_cache_directory(), true);
list.erase(std::remove_if(list.begin(), list.end(),
[this](const std::string &path) -> bool {
return not this->check_minimum_requirements(path);
}),
list.end());
return list;
}
void eviction::service_function() {
auto should_evict = true;
// Handle maximum cache size eviction
auto used_bytes =
utils::file::calculate_used_space(config_.get_cache_directory(), false);
if (config_.get_enable_max_cache_size()) {
should_evict = (used_bytes > config_.get_max_cache_size_bytes());
}
if (should_evict) {
// Remove cached source files that don't meet minimum requirements
auto cached_files_list = get_filtered_cached_files();
while (not get_stop_requested() && should_evict &&
not cached_files_list.empty()) {
try {
std::string api_path;
if (provider_.get_api_path_from_source(
cached_files_list.front(), api_path) == api_error::success) {
api_file file{};
filesystem_item fsi{};
if (provider_.get_filesystem_item_and_file(api_path, file, fsi) ==
api_error::success) {
// Only evict files that match expected size
std::uint64_t file_size{};
if (utils::file::get_file_size(cached_files_list.front(),
file_size)) {
if (file_size == fsi.size) {
// Try to evict file
if (fm_.evict_file(fsi.api_path) &&
config_.get_enable_max_cache_size()) {
// Restrict number of items evicted if maximum cache size is
// enabled
used_bytes -= file_size;
should_evict =
(used_bytes > config_.get_max_cache_size_bytes());
}
}
} else {
utils::error::raise_api_path_error(
__FUNCTION__, file.api_path, file.source_path,
utils::get_last_error_code(), "failed to get file size");
}
}
}
} catch (const std::exception &ex) {
utils::error::raise_error(__FUNCTION__, ex,
"failed to process cached file|sp|" +
cached_files_list.front());
}
cached_files_list.pop_front();
}
}
if (not get_stop_requested()) {
unique_mutex_lock lock(get_mutex());
if (not get_stop_requested()) {
get_notify().wait_for(lock, 30s);
}
}
}
} // namespace repertory
/*
Copyright <2018-2023> <scott.e.graves@protonmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "drives/eviction.hpp"
#include "app_config.hpp"
#include "file_manager/i_file_manager.hpp"
#include "providers/i_provider.hpp"
#include "types/repertory.hpp"
#include "utils/error_utils.hpp"
#include "utils/file_utils.hpp"
#include "utils/unix/unix_utils.hpp"
#include "utils/utils.hpp"
namespace repertory {
auto eviction::check_minimum_requirements(const std::string &file_path)
-> bool {
std::uint64_t file_size{};
if (not utils::file::get_file_size(file_path, file_size)) {
utils::error::raise_error(__FUNCTION__, utils::get_last_error_code(),
file_path, "failed to get file size");
return false;
}
auto ret = false;
if (file_size) {
std::uint64_t reference_time{};
if ((ret =
config_.get_eviction_uses_accessed_time()
? utils::file::get_accessed_time(file_path, reference_time)
: utils::file::get_modified_time(file_path, reference_time))) {
#ifdef _WIN32
const auto now = std::chrono::system_clock::now();
const auto delay =
std::chrono::minutes(config_.get_eviction_delay_mins());
ret = ((std::chrono::system_clock::from_time_t(reference_time) + delay) <=
now);
#else
const auto now = utils::get_time_now();
const auto delay =
(config_.get_eviction_delay_mins() * 60L) * NANOS_PER_SECOND;
ret = ((reference_time + delay) <= now);
#endif
}
}
return ret;
}
auto eviction::get_filtered_cached_files() -> std::deque<std::string> {
auto list =
utils::file::get_directory_files(config_.get_cache_directory(), true);
list.erase(std::remove_if(list.begin(), list.end(),
[this](const std::string &path) -> bool {
return not this->check_minimum_requirements(path);
}),
list.end());
return list;
}
void eviction::service_function() {
auto should_evict = true;
// Handle maximum cache size eviction
auto used_bytes =
utils::file::calculate_used_space(config_.get_cache_directory(), false);
if (config_.get_enable_max_cache_size()) {
should_evict = (used_bytes > config_.get_max_cache_size_bytes());
}
if (should_evict) {
// Remove cached source files that don't meet minimum requirements
auto cached_files_list = get_filtered_cached_files();
while (not get_stop_requested() && should_evict &&
not cached_files_list.empty()) {
try {
std::string api_path;
if (provider_.get_api_path_from_source(
cached_files_list.front(), api_path) == api_error::success) {
api_file file{};
filesystem_item fsi{};
if (provider_.get_filesystem_item_and_file(api_path, file, fsi) ==
api_error::success) {
// Only evict files that match expected size
std::uint64_t file_size{};
if (utils::file::get_file_size(cached_files_list.front(),
file_size)) {
if (file_size == fsi.size) {
// Try to evict file
if (fm_.evict_file(fsi.api_path) &&
config_.get_enable_max_cache_size()) {
// Restrict number of items evicted if maximum cache size is
// enabled
used_bytes -= file_size;
should_evict =
(used_bytes > config_.get_max_cache_size_bytes());
}
}
} else {
utils::error::raise_api_path_error(
__FUNCTION__, file.api_path, file.source_path,
utils::get_last_error_code(), "failed to get file size");
}
}
}
} catch (const std::exception &ex) {
utils::error::raise_error(__FUNCTION__, ex,
"failed to process cached file|sp|" +
cached_files_list.front());
}
cached_files_list.pop_front();
}
}
if (not get_stop_requested()) {
unique_mutex_lock lock(get_mutex());
if (not get_stop_requested()) {
get_notify().wait_for(lock, 30s);
}
}
}
} // namespace repertory

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,353 +1,353 @@
/*
Copyright <2018-2023> <scott.e.graves@protonmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#ifndef _WIN32
#include "drives/fuse/fuse_drive_base.hpp"
#include "providers/i_provider.hpp"
namespace repertory {
auto fuse_drive_base::access_impl(std::string api_path, int mask) -> api_error {
return check_access(api_path, mask);
}
auto fuse_drive_base::check_access(const std::string &api_path, int mask) const
-> api_error {
api_meta_map meta;
const auto res = get_item_meta(api_path, meta);
if (res != api_error::success) {
return res;
}
// Always allow root
auto current_uid = get_current_uid();
if (current_uid != 0) {
// Always allow forced user
if (not forced_uid_.has_value() || (current_uid != get_effective_uid())) {
// Always allow if checking file exists
if (F_OK != mask) {
const auto effective_uid =
(forced_uid_.has_value() ? forced_uid_.value()
: get_uid_from_meta(meta));
const auto effective_gid =
(forced_gid_.has_value() ? forced_gid_.value()
: get_gid_from_meta(meta));
// Create file mode
mode_t effective_mode =
forced_umask_.has_value()
? ((S_IRWXU | S_IRWXG | S_IRWXO) & (~forced_umask_.value()))
: get_mode_from_meta(meta);
// Create access mask
mode_t active_mask = S_IRWXO;
if (current_uid == effective_uid) {
active_mask |= S_IRWXU;
}
if (get_current_gid() == effective_gid) {
active_mask |= S_IRWXG;
}
if (utils::is_uid_member_of_group(current_uid, effective_gid)) {
active_mask |= S_IRWXG;
}
// Calculate effective file mode
effective_mode &= active_mask;
// Check allow execute
if ((mask & X_OK) == X_OK) {
if ((effective_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) == 0) {
return api_error::permission_denied;
}
}
// Check allow write
if ((mask & W_OK) == W_OK) {
if ((effective_mode & (S_IWUSR | S_IWGRP | S_IWOTH)) == 0) {
return api_error::access_denied;
}
}
// Check allow read
if ((mask & R_OK) == R_OK) {
if ((effective_mode & (S_IRUSR | S_IRGRP | S_IROTH)) == 0) {
return api_error::access_denied;
}
}
if (effective_mode == 0) {
// Deny access if effective mode is 0
return api_error::access_denied;
}
}
}
}
return api_error::success;
}
auto fuse_drive_base::check_and_perform(
const std::string &api_path, int parent_mask,
const std::function<api_error(api_meta_map &meta)> &action) -> api_error {
api_meta_map meta;
auto ret = get_item_meta(api_path, meta);
if (ret != api_error::success) {
return ret;
}
if ((ret = check_parent_access(api_path, parent_mask)) !=
api_error::success) {
return ret;
}
if ((ret = check_owner(meta)) != api_error::success) {
return ret;
}
return action(meta);
}
auto fuse_drive_base::check_open_flags(int flags, int mask,
const api_error &fail_error)
-> api_error {
return ((flags & mask) ? fail_error : api_error::success);
}
auto fuse_drive_base::check_owner(const std::string &api_path) const
-> api_error {
api_meta_map meta{};
auto ret = get_item_meta(api_path, meta);
if (ret == api_error::success) {
ret = check_owner(meta);
}
return ret;
}
auto fuse_drive_base::check_owner(const api_meta_map &meta) const -> api_error {
// Always allow root
auto current_uid = get_current_uid();
if ((current_uid != 0) &&
// Always allow forced UID
(not forced_uid_.has_value() || (current_uid != get_effective_uid())) &&
// Check if current uid matches file uid
(get_uid_from_meta(meta) != get_effective_uid())) {
return api_error::permission_denied;
}
return api_error::success;
}
auto fuse_drive_base::check_parent_access(const std::string &api_path,
int mask) const -> api_error {
auto ret = api_error::success;
// Ignore root
if (api_path != "/") {
if ((mask & X_OK) == X_OK) {
for (auto parent = utils::path::get_parent_directory(api_path);
(ret == api_error::success) && not parent.empty();
parent = utils::path::get_parent_directory(parent)) {
if (((ret = check_access(parent, X_OK)) == api_error::success) &&
(parent == "/")) {
break;
}
}
}
if (ret == api_error::success) {
mask &= (~X_OK);
if (mask != 0) {
ret = check_access(utils::path::get_parent_directory(api_path), mask);
}
}
}
return ret;
}
auto fuse_drive_base::check_readable(int flags, const api_error &fail_error)
-> api_error {
const auto mode = (flags & O_ACCMODE);
return ((mode == O_WRONLY) ? fail_error : api_error::success);
}
auto fuse_drive_base::check_writeable(int flags, const api_error &fail_error)
-> api_error {
return ((flags & O_ACCMODE) ? api_error::success : fail_error);
}
auto fuse_drive_base::get_current_gid() const -> gid_t {
auto *ctx = fuse_get_context();
return ctx ? ctx->gid : getgid();
}
auto fuse_drive_base::get_current_uid() const -> uid_t {
auto *ctx = fuse_get_context();
return ctx ? ctx->uid : getuid();
}
auto fuse_drive_base::get_effective_gid() const -> gid_t {
return forced_gid_.has_value() ? forced_gid_.value() : get_current_gid();
}
auto fuse_drive_base::get_effective_uid() const -> uid_t {
return forced_uid_.has_value() ? forced_uid_.value() : get_current_uid();
}
#ifdef __APPLE__
auto fuse_drive_base::get_flags_from_meta(const api_meta_map &meta)
-> __uint32_t {
return utils::string::to_uint32(meta.at(META_OSXFLAGS));
}
#endif // __APPLE__
auto fuse_drive_base::get_gid_from_meta(const api_meta_map &meta) -> gid_t {
return static_cast<gid_t>(utils::string::to_uint32(meta.at(META_GID)));
}
auto fuse_drive_base::get_mode_from_meta(const api_meta_map &meta) -> mode_t {
return static_cast<mode_t>(utils::string::to_uint32(meta.at(META_MODE)));
}
void fuse_drive_base::get_timespec_from_meta(const api_meta_map &meta,
const std::string &name,
struct timespec &ts) {
const auto t = utils::string::to_uint64(meta.at(name));
ts.tv_nsec = t % NANOS_PER_SECOND;
ts.tv_sec = t / NANOS_PER_SECOND;
}
auto fuse_drive_base::get_uid_from_meta(const api_meta_map &meta) -> uid_t {
return static_cast<uid_t>(utils::string::to_uint32(meta.at(META_UID)));
}
#ifdef __APPLE__
auto fuse_drive_base::parse_xattr_parameters(const char *name,
const uint32_t &position,
std::string &attribute_name,
const std::string &api_path)
-> api_error {
#else
auto fuse_drive_base::parse_xattr_parameters(const char *name,
std::string &attribute_name,
const std::string &api_path)
-> api_error {
#endif
auto res = api_path.empty() ? api_error::bad_address : api_error::success;
if (res != api_error::success) {
return res;
}
if (not name) {
return api_error::bad_address;
}
attribute_name = std::string(name);
#ifdef __APPLE__
if (attribute_name == A_KAUTH_FILESEC_XATTR) {
char new_name[MAXPATHLEN] = {0};
memcpy(new_name, A_KAUTH_FILESEC_XATTR, sizeof(A_KAUTH_FILESEC_XATTR));
memcpy(new_name, G_PREFIX, sizeof(G_PREFIX) - 1);
attribute_name = new_name;
} else if (attribute_name.empty() ||
((attribute_name != XATTR_RESOURCEFORK_NAME) && (position != 0))) {
return api_error::invalid_operation;
}
#endif
return api_error::success;
}
#ifdef __APPLE__
auto fuse_drive_base::parse_xattr_parameters(
const char *name, const char *value, size_t size, const uint32_t &position,
std::string &attribute_name, const std::string &api_path) -> api_error {
auto res = parse_xattr_parameters(name, position, attribute_name, api_path);
#else
auto fuse_drive_base::parse_xattr_parameters(const char *name,
const char *value, size_t size,
std::string &attribute_name,
const std::string &api_path)
-> api_error {
auto res = parse_xattr_parameters(name, attribute_name, api_path);
#endif
if (res != api_error::success) {
return res;
}
return (value ? api_error::success
: (size ? api_error::bad_address : api_error::success));
}
void fuse_drive_base::populate_stat(const std::string &api_path,
std::uint64_t size_or_count,
const api_meta_map &meta, bool directory,
i_provider &provider, struct stat *st) {
memset(st, 0, sizeof(struct stat));
st->st_nlink =
(directory
? 2 + (size_or_count ? size_or_count
: provider.get_directory_item_count(api_path))
: 1);
if (directory) {
st->st_blocks = 0;
} else {
st->st_size = size_or_count;
static const auto block_size_stat = static_cast<std::uint64_t>(512u);
static const auto block_size = static_cast<std::uint64_t>(4096u);
const auto size = utils::divide_with_ceiling(
static_cast<std::uint64_t>(st->st_size), block_size) *
block_size;
st->st_blocks = std::max(block_size / block_size_stat,
utils::divide_with_ceiling(size, block_size_stat));
}
st->st_gid = get_gid_from_meta(meta);
st->st_mode = (directory ? S_IFDIR : S_IFREG) | get_mode_from_meta(meta);
st->st_uid = get_uid_from_meta(meta);
#ifdef __APPLE__
st->st_blksize = 0;
st->st_flags = get_flags_from_meta(meta);
set_timespec_from_meta(meta, META_MODIFIED, st->st_mtimespec);
set_timespec_from_meta(meta, META_CREATION, st->st_birthtimespec);
set_timespec_from_meta(meta, META_CHANGED, st->st_ctimespec);
set_timespec_from_meta(meta, META_ACCESSED, st->st_atimespec);
#else // __APPLE__
st->st_blksize = 4096;
set_timespec_from_meta(meta, META_MODIFIED, st->st_mtim);
set_timespec_from_meta(meta, META_CREATION, st->st_ctim);
set_timespec_from_meta(meta, META_ACCESSED, st->st_atim);
#endif // __APPLE__
}
void fuse_drive_base::set_timespec_from_meta(const api_meta_map &meta,
const std::string &name,
struct timespec &ts) {
const auto t = utils::string::to_uint64(meta.at(name));
ts.tv_nsec = t % NANOS_PER_SECOND;
ts.tv_sec = t / NANOS_PER_SECOND;
}
} // namespace repertory
#endif // _WIN32
/*
Copyright <2018-2023> <scott.e.graves@protonmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#ifndef _WIN32
#include "drives/fuse/fuse_drive_base.hpp"
#include "providers/i_provider.hpp"
namespace repertory {
auto fuse_drive_base::access_impl(std::string api_path, int mask) -> api_error {
return check_access(api_path, mask);
}
auto fuse_drive_base::check_access(const std::string &api_path, int mask) const
-> api_error {
api_meta_map meta;
const auto res = get_item_meta(api_path, meta);
if (res != api_error::success) {
return res;
}
// Always allow root
auto current_uid = get_current_uid();
if (current_uid != 0) {
// Always allow forced user
if (not forced_uid_.has_value() || (current_uid != get_effective_uid())) {
// Always allow if checking file exists
if (F_OK != mask) {
const auto effective_uid =
(forced_uid_.has_value() ? forced_uid_.value()
: get_uid_from_meta(meta));
const auto effective_gid =
(forced_gid_.has_value() ? forced_gid_.value()
: get_gid_from_meta(meta));
// Create file mode
mode_t effective_mode =
forced_umask_.has_value()
? ((S_IRWXU | S_IRWXG | S_IRWXO) & (~forced_umask_.value()))
: get_mode_from_meta(meta);
// Create access mask
mode_t active_mask = S_IRWXO;
if (current_uid == effective_uid) {
active_mask |= S_IRWXU;
}
if (get_current_gid() == effective_gid) {
active_mask |= S_IRWXG;
}
if (utils::is_uid_member_of_group(current_uid, effective_gid)) {
active_mask |= S_IRWXG;
}
// Calculate effective file mode
effective_mode &= active_mask;
// Check allow execute
if ((mask & X_OK) == X_OK) {
if ((effective_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) == 0) {
return api_error::permission_denied;
}
}
// Check allow write
if ((mask & W_OK) == W_OK) {
if ((effective_mode & (S_IWUSR | S_IWGRP | S_IWOTH)) == 0) {
return api_error::access_denied;
}
}
// Check allow read
if ((mask & R_OK) == R_OK) {
if ((effective_mode & (S_IRUSR | S_IRGRP | S_IROTH)) == 0) {
return api_error::access_denied;
}
}
if (effective_mode == 0) {
// Deny access if effective mode is 0
return api_error::access_denied;
}
}
}
}
return api_error::success;
}
auto fuse_drive_base::check_and_perform(
const std::string &api_path, int parent_mask,
const std::function<api_error(api_meta_map &meta)> &action) -> api_error {
api_meta_map meta;
auto ret = get_item_meta(api_path, meta);
if (ret != api_error::success) {
return ret;
}
if ((ret = check_parent_access(api_path, parent_mask)) !=
api_error::success) {
return ret;
}
if ((ret = check_owner(meta)) != api_error::success) {
return ret;
}
return action(meta);
}
auto fuse_drive_base::check_open_flags(int flags, int mask,
const api_error &fail_error)
-> api_error {
return ((flags & mask) ? fail_error : api_error::success);
}
auto fuse_drive_base::check_owner(const std::string &api_path) const
-> api_error {
api_meta_map meta{};
auto ret = get_item_meta(api_path, meta);
if (ret == api_error::success) {
ret = check_owner(meta);
}
return ret;
}
auto fuse_drive_base::check_owner(const api_meta_map &meta) const -> api_error {
// Always allow root
auto current_uid = get_current_uid();
if ((current_uid != 0) &&
// Always allow forced UID
(not forced_uid_.has_value() || (current_uid != get_effective_uid())) &&
// Check if current uid matches file uid
(get_uid_from_meta(meta) != get_effective_uid())) {
return api_error::permission_denied;
}
return api_error::success;
}
auto fuse_drive_base::check_parent_access(const std::string &api_path,
int mask) const -> api_error {
auto ret = api_error::success;
// Ignore root
if (api_path != "/") {
if ((mask & X_OK) == X_OK) {
for (auto parent = utils::path::get_parent_directory(api_path);
(ret == api_error::success) && not parent.empty();
parent = utils::path::get_parent_directory(parent)) {
if (((ret = check_access(parent, X_OK)) == api_error::success) &&
(parent == "/")) {
break;
}
}
}
if (ret == api_error::success) {
mask &= (~X_OK);
if (mask != 0) {
ret = check_access(utils::path::get_parent_directory(api_path), mask);
}
}
}
return ret;
}
auto fuse_drive_base::check_readable(int flags, const api_error &fail_error)
-> api_error {
const auto mode = (flags & O_ACCMODE);
return ((mode == O_WRONLY) ? fail_error : api_error::success);
}
auto fuse_drive_base::check_writeable(int flags, const api_error &fail_error)
-> api_error {
return ((flags & O_ACCMODE) ? api_error::success : fail_error);
}
auto fuse_drive_base::get_current_gid() const -> gid_t {
auto *ctx = fuse_get_context();
return ctx ? ctx->gid : getgid();
}
auto fuse_drive_base::get_current_uid() const -> uid_t {
auto *ctx = fuse_get_context();
return ctx ? ctx->uid : getuid();
}
auto fuse_drive_base::get_effective_gid() const -> gid_t {
return forced_gid_.has_value() ? forced_gid_.value() : get_current_gid();
}
auto fuse_drive_base::get_effective_uid() const -> uid_t {
return forced_uid_.has_value() ? forced_uid_.value() : get_current_uid();
}
#ifdef __APPLE__
auto fuse_drive_base::get_flags_from_meta(const api_meta_map &meta)
-> __uint32_t {
return utils::string::to_uint32(meta.at(META_OSXFLAGS));
}
#endif // __APPLE__
auto fuse_drive_base::get_gid_from_meta(const api_meta_map &meta) -> gid_t {
return static_cast<gid_t>(utils::string::to_uint32(meta.at(META_GID)));
}
auto fuse_drive_base::get_mode_from_meta(const api_meta_map &meta) -> mode_t {
return static_cast<mode_t>(utils::string::to_uint32(meta.at(META_MODE)));
}
void fuse_drive_base::get_timespec_from_meta(const api_meta_map &meta,
const std::string &name,
struct timespec &ts) {
const auto t = utils::string::to_uint64(meta.at(name));
ts.tv_nsec = t % NANOS_PER_SECOND;
ts.tv_sec = t / NANOS_PER_SECOND;
}
auto fuse_drive_base::get_uid_from_meta(const api_meta_map &meta) -> uid_t {
return static_cast<uid_t>(utils::string::to_uint32(meta.at(META_UID)));
}
#ifdef __APPLE__
auto fuse_drive_base::parse_xattr_parameters(const char *name,
const uint32_t &position,
std::string &attribute_name,
const std::string &api_path)
-> api_error {
#else
auto fuse_drive_base::parse_xattr_parameters(const char *name,
std::string &attribute_name,
const std::string &api_path)
-> api_error {
#endif
auto res = api_path.empty() ? api_error::bad_address : api_error::success;
if (res != api_error::success) {
return res;
}
if (not name) {
return api_error::bad_address;
}
attribute_name = std::string(name);
#ifdef __APPLE__
if (attribute_name == A_KAUTH_FILESEC_XATTR) {
char new_name[MAXPATHLEN] = {0};
memcpy(new_name, A_KAUTH_FILESEC_XATTR, sizeof(A_KAUTH_FILESEC_XATTR));
memcpy(new_name, G_PREFIX, sizeof(G_PREFIX) - 1);
attribute_name = new_name;
} else if (attribute_name.empty() ||
((attribute_name != XATTR_RESOURCEFORK_NAME) && (position != 0))) {
return api_error::invalid_operation;
}
#endif
return api_error::success;
}
#ifdef __APPLE__
auto fuse_drive_base::parse_xattr_parameters(
const char *name, const char *value, size_t size, const uint32_t &position,
std::string &attribute_name, const std::string &api_path) -> api_error {
auto res = parse_xattr_parameters(name, position, attribute_name, api_path);
#else
auto fuse_drive_base::parse_xattr_parameters(const char *name,
const char *value, size_t size,
std::string &attribute_name,
const std::string &api_path)
-> api_error {
auto res = parse_xattr_parameters(name, attribute_name, api_path);
#endif
if (res != api_error::success) {
return res;
}
return (value ? api_error::success
: (size ? api_error::bad_address : api_error::success));
}
void fuse_drive_base::populate_stat(const std::string &api_path,
std::uint64_t size_or_count,
const api_meta_map &meta, bool directory,
i_provider &provider, struct stat *st) {
memset(st, 0, sizeof(struct stat));
st->st_nlink =
(directory
? 2 + (size_or_count ? size_or_count
: provider.get_directory_item_count(api_path))
: 1);
if (directory) {
st->st_blocks = 0;
} else {
st->st_size = size_or_count;
static const auto block_size_stat = static_cast<std::uint64_t>(512u);
static const auto block_size = static_cast<std::uint64_t>(4096u);
const auto size = utils::divide_with_ceiling(
static_cast<std::uint64_t>(st->st_size), block_size) *
block_size;
st->st_blocks = std::max(block_size / block_size_stat,
utils::divide_with_ceiling(size, block_size_stat));
}
st->st_gid = get_gid_from_meta(meta);
st->st_mode = (directory ? S_IFDIR : S_IFREG) | get_mode_from_meta(meta);
st->st_uid = get_uid_from_meta(meta);
#ifdef __APPLE__
st->st_blksize = 0;
st->st_flags = get_flags_from_meta(meta);
set_timespec_from_meta(meta, META_MODIFIED, st->st_mtimespec);
set_timespec_from_meta(meta, META_CREATION, st->st_birthtimespec);
set_timespec_from_meta(meta, META_CHANGED, st->st_ctimespec);
set_timespec_from_meta(meta, META_ACCESSED, st->st_atimespec);
#else // __APPLE__
st->st_blksize = 4096;
set_timespec_from_meta(meta, META_MODIFIED, st->st_mtim);
set_timespec_from_meta(meta, META_CREATION, st->st_ctim);
set_timespec_from_meta(meta, META_ACCESSED, st->st_atim);
#endif // __APPLE__
}
void fuse_drive_base::set_timespec_from_meta(const api_meta_map &meta,
const std::string &name,
struct timespec &ts) {
const auto t = utils::string::to_uint64(meta.at(name));
ts.tv_nsec = t % NANOS_PER_SECOND;
ts.tv_sec = t / NANOS_PER_SECOND;
}
} // namespace repertory
#endif // _WIN32

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,266 +1,266 @@
/*
Copyright <2018-2023> <scott.e.graves@protonmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "drives/remote/remote_open_file_table.hpp"
#include "events/event_system.hpp"
#include "events/events.hpp"
#include "utils/utils.hpp"
namespace repertory {
void remote_open_file_table::add_directory(const std::string &client_id,
void *dir) {
recur_mutex_lock directory_lock(directory_mutex_);
auto &list = directory_lookup_[client_id];
if (utils::collection_excludes(list, dir)) {
directory_lookup_[client_id].emplace_back(dir);
}
}
void remote_open_file_table::close_all(const std::string &client_id) {
std::vector<remote::file_handle> compat_handles;
unique_recur_mutex_lock compat_lock(compat_mutex_);
for (auto &kv : compat_lookup_) {
if (kv.second.client_id == client_id) {
compat_handles.emplace_back(kv.first);
}
}
compat_lock.unlock();
for (auto &handle : compat_handles) {
#ifdef _WIN32
_close(static_cast<int>(handle));
#else
close(static_cast<int>(handle));
#endif
remove_compat_open_info(handle);
}
std::vector<native_handle> handles;
unique_recur_mutex_lock file_lock(file_mutex_);
for (auto &kv : file_lookup_) {
if (kv.second.client_id == client_id) {
handles.emplace_back(kv.first);
}
}
file_lock.unlock();
for (auto &handle : handles) {
#ifdef _WIN32
::CloseHandle(handle);
#else
close(handle);
#endif
remove_open_info(handle);
}
std::vector<void *> dirs;
unique_recur_mutex_lock directory_lock(directory_mutex_);
for (auto &kv : directory_lookup_) {
if (kv.first == client_id) {
dirs.insert(dirs.end(), kv.second.begin(), kv.second.end());
}
}
directory_lock.unlock();
for (auto *dir : dirs) {
remove_directory(client_id, dir);
}
}
#ifdef _WIN32
auto remote_open_file_table::get_directory_buffer(const native_handle &handle,
PVOID *&buffer) -> bool {
recur_mutex_lock file_lock(file_mutex_);
if (file_lookup_.find(handle) != file_lookup_.end()) {
buffer = &file_lookup_[handle].directory_buffer;
return true;
}
return false;
}
#endif
auto remote_open_file_table::get_open_file_count(
const std::string &file_path) const -> std::size_t {
unique_recur_mutex_lock file_lock(file_mutex_);
const auto count = std::accumulate(
file_lookup_.cbegin(), file_lookup_.cend(), std::size_t(0U),
[&file_path](std::size_t total, const auto &kv) -> std::size_t {
if (kv.second.path == file_path) {
return ++total;
}
return total;
});
return std::accumulate(
compat_lookup_.cbegin(), compat_lookup_.cend(), count,
[&file_path](std::size_t total, const auto &kv) -> std::size_t {
if (kv.second.path == file_path) {
return ++total;
}
return total;
});
}
auto remote_open_file_table::get_open_info(const native_handle &handle,
open_info &oi) -> bool {
recur_mutex_lock file_lock(file_mutex_);
if (file_lookup_.find(handle) != file_lookup_.end()) {
oi = file_lookup_[handle];
return true;
}
return false;
}
auto remote_open_file_table::get_open_file_path(const native_handle &handle)
-> std::string {
recur_mutex_lock file_lock(file_mutex_);
if (file_lookup_.find(handle) != file_lookup_.end()) {
return file_lookup_[handle].path;
}
return "";
}
auto remote_open_file_table::has_open_directory(const std::string &client_id,
void *dir) -> bool {
recur_mutex_lock directory_lock(directory_mutex_);
auto &list = directory_lookup_[client_id];
return (utils::collection_includes(list, dir));
}
auto remote_open_file_table::has_compat_open_info(
const remote::file_handle &handle, int error_return) -> int {
recur_mutex_lock compat_lock(compat_mutex_);
const auto res =
((compat_lookup_.find(handle) == compat_lookup_.end()) ? -1 : 0);
if (res == -1) {
errno = error_return;
}
return res;
}
void remote_open_file_table::remove_all(const std::string &file_path) {
unique_recur_mutex_lock file_lock(file_mutex_);
const auto open_list = std::accumulate(
file_lookup_.begin(), file_lookup_.end(), std::vector<native_handle>(),
[&file_path](std::vector<native_handle> v,
const auto &kv) -> std::vector<native_handle> {
if (kv.second.path == file_path) {
v.emplace_back(kv.first);
}
return v;
});
const auto compat_open_list = std::accumulate(
compat_lookup_.begin(), compat_lookup_.end(),
std::vector<remote::file_handle>(),
[&file_path](std::vector<remote::file_handle> v,
const auto &kv) -> std::vector<remote::file_handle> {
if (kv.second.path == file_path) {
v.emplace_back(kv.first);
}
return v;
});
file_lock.unlock();
for (auto &handle : open_list) {
remove_open_info(handle);
}
for (auto &handle : compat_open_list) {
remove_compat_open_info(handle);
}
}
void remote_open_file_table::remove_compat_open_info(
const remote::file_handle &handle) {
recur_mutex_lock compat_lock(compat_mutex_);
if (compat_lookup_[handle].count > 0) {
compat_lookup_[handle].count--;
}
if (not compat_lookup_[handle].count) {
compat_lookup_.erase(handle);
}
}
auto remote_open_file_table::remove_directory(const std::string &client_id,
void *dir) -> bool {
recur_mutex_lock directory_lock(directory_mutex_);
auto &list = directory_lookup_[client_id];
if (utils::collection_includes(list, dir)) {
utils::remove_element_from(list, dir);
delete_open_directory(dir);
if (directory_lookup_[client_id].empty()) {
directory_lookup_.erase(client_id);
}
return true;
}
return false;
}
void remote_open_file_table::remove_open_info(const native_handle &handle) {
recur_mutex_lock file_lock(file_mutex_);
if (file_lookup_[handle].count > 0) {
file_lookup_[handle].count--;
}
if (not file_lookup_[handle].count) {
#ifdef _WIN32
if (file_lookup_[handle].directory_buffer) {
FspFileSystemDeleteDirectoryBuffer(
&file_lookup_[handle].directory_buffer);
}
#endif
file_lookup_.erase(handle);
}
}
void remote_open_file_table::set_compat_client_id(
const remote::file_handle &handle, const std::string &client_id) {
recur_mutex_lock compat_lock(compat_mutex_);
compat_lookup_[handle].client_id = client_id;
}
void remote_open_file_table::set_client_id(const native_handle &handle,
const std::string &client_id) {
recur_mutex_lock file_lock(file_mutex_);
file_lookup_[handle].client_id = client_id;
}
void remote_open_file_table::set_compat_open_info(
const remote::file_handle &handle, const std::string &file_path) {
recur_mutex_lock compat_lock(compat_mutex_);
if (compat_lookup_.find(handle) == compat_lookup_.end()) {
compat_lookup_[handle] = {0, "", file_path};
}
compat_lookup_[handle].count++;
}
void remote_open_file_table::set_open_info(const native_handle &handle,
open_info oi) {
recur_mutex_lock file_lock(file_mutex_);
if (file_lookup_.find(handle) == file_lookup_.end()) {
file_lookup_[handle] = std::move(oi);
}
file_lookup_[handle].count++;
}
} // namespace repertory
/*
Copyright <2018-2023> <scott.e.graves@protonmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "drives/remote/remote_open_file_table.hpp"
#include "events/event_system.hpp"
#include "events/events.hpp"
#include "utils/utils.hpp"
namespace repertory {
void remote_open_file_table::add_directory(const std::string &client_id,
void *dir) {
recur_mutex_lock directory_lock(directory_mutex_);
auto &list = directory_lookup_[client_id];
if (utils::collection_excludes(list, dir)) {
directory_lookup_[client_id].emplace_back(dir);
}
}
void remote_open_file_table::close_all(const std::string &client_id) {
std::vector<remote::file_handle> compat_handles;
unique_recur_mutex_lock compat_lock(compat_mutex_);
for (auto &kv : compat_lookup_) {
if (kv.second.client_id == client_id) {
compat_handles.emplace_back(kv.first);
}
}
compat_lock.unlock();
for (auto &handle : compat_handles) {
#ifdef _WIN32
_close(static_cast<int>(handle));
#else
close(static_cast<int>(handle));
#endif
remove_compat_open_info(handle);
}
std::vector<native_handle> handles;
unique_recur_mutex_lock file_lock(file_mutex_);
for (auto &kv : file_lookup_) {
if (kv.second.client_id == client_id) {
handles.emplace_back(kv.first);
}
}
file_lock.unlock();
for (auto &handle : handles) {
#ifdef _WIN32
::CloseHandle(handle);
#else
close(handle);
#endif
remove_open_info(handle);
}
std::vector<void *> dirs;
unique_recur_mutex_lock directory_lock(directory_mutex_);
for (auto &kv : directory_lookup_) {
if (kv.first == client_id) {
dirs.insert(dirs.end(), kv.second.begin(), kv.second.end());
}
}
directory_lock.unlock();
for (auto *dir : dirs) {
remove_directory(client_id, dir);
}
}
#ifdef _WIN32
auto remote_open_file_table::get_directory_buffer(const native_handle &handle,
PVOID *&buffer) -> bool {
recur_mutex_lock file_lock(file_mutex_);
if (file_lookup_.find(handle) != file_lookup_.end()) {
buffer = &file_lookup_[handle].directory_buffer;
return true;
}
return false;
}
#endif
auto remote_open_file_table::get_open_file_count(
const std::string &file_path) const -> std::size_t {
unique_recur_mutex_lock file_lock(file_mutex_);
const auto count = std::accumulate(
file_lookup_.cbegin(), file_lookup_.cend(), std::size_t(0U),
[&file_path](std::size_t total, const auto &kv) -> std::size_t {
if (kv.second.path == file_path) {
return ++total;
}
return total;
});
return std::accumulate(
compat_lookup_.cbegin(), compat_lookup_.cend(), count,
[&file_path](std::size_t total, const auto &kv) -> std::size_t {
if (kv.second.path == file_path) {
return ++total;
}
return total;
});
}
auto remote_open_file_table::get_open_info(const native_handle &handle,
open_info &oi) -> bool {
recur_mutex_lock file_lock(file_mutex_);
if (file_lookup_.find(handle) != file_lookup_.end()) {
oi = file_lookup_[handle];
return true;
}
return false;
}
auto remote_open_file_table::get_open_file_path(const native_handle &handle)
-> std::string {
recur_mutex_lock file_lock(file_mutex_);
if (file_lookup_.find(handle) != file_lookup_.end()) {
return file_lookup_[handle].path;
}
return "";
}
auto remote_open_file_table::has_open_directory(const std::string &client_id,
void *dir) -> bool {
recur_mutex_lock directory_lock(directory_mutex_);
auto &list = directory_lookup_[client_id];
return (utils::collection_includes(list, dir));
}
auto remote_open_file_table::has_compat_open_info(
const remote::file_handle &handle, int error_return) -> int {
recur_mutex_lock compat_lock(compat_mutex_);
const auto res =
((compat_lookup_.find(handle) == compat_lookup_.end()) ? -1 : 0);
if (res == -1) {
errno = error_return;
}
return res;
}
void remote_open_file_table::remove_all(const std::string &file_path) {
unique_recur_mutex_lock file_lock(file_mutex_);
const auto open_list = std::accumulate(
file_lookup_.begin(), file_lookup_.end(), std::vector<native_handle>(),
[&file_path](std::vector<native_handle> v,
const auto &kv) -> std::vector<native_handle> {
if (kv.second.path == file_path) {
v.emplace_back(kv.first);
}
return v;
});
const auto compat_open_list = std::accumulate(
compat_lookup_.begin(), compat_lookup_.end(),
std::vector<remote::file_handle>(),
[&file_path](std::vector<remote::file_handle> v,
const auto &kv) -> std::vector<remote::file_handle> {
if (kv.second.path == file_path) {
v.emplace_back(kv.first);
}
return v;
});
file_lock.unlock();
for (auto &handle : open_list) {
remove_open_info(handle);
}
for (auto &handle : compat_open_list) {
remove_compat_open_info(handle);
}
}
void remote_open_file_table::remove_compat_open_info(
const remote::file_handle &handle) {
recur_mutex_lock compat_lock(compat_mutex_);
if (compat_lookup_[handle].count > 0) {
compat_lookup_[handle].count--;
}
if (not compat_lookup_[handle].count) {
compat_lookup_.erase(handle);
}
}
auto remote_open_file_table::remove_directory(const std::string &client_id,
void *dir) -> bool {
recur_mutex_lock directory_lock(directory_mutex_);
auto &list = directory_lookup_[client_id];
if (utils::collection_includes(list, dir)) {
utils::remove_element_from(list, dir);
delete_open_directory(dir);
if (directory_lookup_[client_id].empty()) {
directory_lookup_.erase(client_id);
}
return true;
}
return false;
}
void remote_open_file_table::remove_open_info(const native_handle &handle) {
recur_mutex_lock file_lock(file_mutex_);
if (file_lookup_[handle].count > 0) {
file_lookup_[handle].count--;
}
if (not file_lookup_[handle].count) {
#ifdef _WIN32
if (file_lookup_[handle].directory_buffer) {
FspFileSystemDeleteDirectoryBuffer(
&file_lookup_[handle].directory_buffer);
}
#endif
file_lookup_.erase(handle);
}
}
void remote_open_file_table::set_compat_client_id(
const remote::file_handle &handle, const std::string &client_id) {
recur_mutex_lock compat_lock(compat_mutex_);
compat_lookup_[handle].client_id = client_id;
}
void remote_open_file_table::set_client_id(const native_handle &handle,
const std::string &client_id) {
recur_mutex_lock file_lock(file_mutex_);
file_lookup_[handle].client_id = client_id;
}
void remote_open_file_table::set_compat_open_info(
const remote::file_handle &handle, const std::string &file_path) {
recur_mutex_lock compat_lock(compat_mutex_);
if (compat_lookup_.find(handle) == compat_lookup_.end()) {
compat_lookup_[handle] = {0, "", file_path};
}
compat_lookup_[handle].count++;
}
void remote_open_file_table::set_open_info(const native_handle &handle,
open_info oi) {
recur_mutex_lock file_lock(file_mutex_);
if (file_lookup_.find(handle) == file_lookup_.end()) {
file_lookup_[handle] = std::move(oi);
}
file_lookup_[handle].count++;
}
} // namespace repertory

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,473 +1,473 @@
/*
Copyright <2018-2023> <scott.e.graves@protonmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#ifdef _WIN32
#include <utility>
#include "drives/winfsp/remotewinfsp/remote_winfsp_drive.hpp"
#include "app_config.hpp"
#include "events/consumers/console_consumer.hpp"
#include "events/consumers/logging_consumer.hpp"
#include "events/events.hpp"
#include "platform/platform.hpp"
#include "rpc/server/server.hpp"
#include "utils/error_utils.hpp"
#include "utils/file_utils.hpp"
#include "utils/path_utils.hpp"
#include "utils/utils.hpp"
namespace repertory::remote_winfsp {
remote_winfsp_drive::winfsp_service::winfsp_service(
lock_data &lock, remote_winfsp_drive &drive,
std::vector<std::string> drive_args, app_config &config)
: Service(&(L"RepertoryRemote_" +
utils::string::from_utf8(lock.get_unique_id()))[0u]),
config_(config),
lock_(lock),
drive_(drive),
drive_args_(std::move(drive_args)),
host_(drive) {}
auto remote_winfsp_drive::winfsp_service::OnStart(ULONG, PWSTR *) -> NTSTATUS {
const auto mount_location = utils::string::to_lower(
utils::path::absolute((drive_args_.size() > 1u) ? drive_args_[1u] : ""));
const auto drive_letter =
((mount_location.size() == 2u) ||
((mount_location.size() == 3u) && (mount_location[2u] == '\\'))) &&
(mount_location[1u] == ':');
auto ret = drive_letter ? STATUS_DEVICE_BUSY : STATUS_NOT_SUPPORTED;
if ((drive_letter && not utils::file::is_directory(mount_location))) {
auto unicode_mount_location = utils::string::from_utf8(mount_location);
host_.SetFileSystemName(&unicode_mount_location[0u]);
if (config_.get_enable_mount_manager()) {
unicode_mount_location =
std::wstring(L"\\\\.\\") + unicode_mount_location[0u] + L":";
}
ret = host_.Mount(&unicode_mount_location[0u]);
} else {
std::cerr << (drive_letter ? "Mount location in use: "
: "Mount location not supported: ")
<< mount_location << std::endl;
}
if (ret != STATUS_SUCCESS) {
event_system::instance().raise<drive_mount_failed>(mount_location,
std::to_string(ret));
if (not lock_.set_mount_state(false, "", -1)) {
utils::error::raise_error(__FUNCTION__, "failed to set mount state");
}
}
return ret;
}
auto remote_winfsp_drive::winfsp_service::OnStop() -> NTSTATUS {
host_.Unmount();
if (not lock_.set_mount_state(false, "", -1)) {
utils::error::raise_error(__FUNCTION__, "failed to set mount state");
}
return STATUS_SUCCESS;
}
remote_winfsp_drive::remote_winfsp_drive(app_config &config,
remote_instance_factory factory,
lock_data &lock)
: FileSystemBase(),
config_(config),
lock_(lock),
factory_(std::move(factory)) {
E_SUBSCRIBE_EXACT(unmount_requested, [this](const unmount_requested &) {
std::thread([this]() { this->shutdown(); }).detach();
});
}
auto remote_winfsp_drive::CanDelete(PVOID /*file_node*/, PVOID file_desc,
PWSTR file_name) -> NTSTATUS {
return remote_instance_->winfsp_can_delete(file_desc, file_name);
}
VOID remote_winfsp_drive::Cleanup(PVOID /*file_node*/, PVOID file_desc,
PWSTR file_name, ULONG flags) {
BOOLEAN was_closed;
remote_instance_->winfsp_cleanup(file_desc, file_name, flags, was_closed);
}
VOID remote_winfsp_drive::Close(PVOID /*file_node*/, PVOID file_desc) {
remote_instance_->winfsp_close(file_desc);
}
auto remote_winfsp_drive::Create(PWSTR file_name, UINT32 create_options,
UINT32 granted_access, UINT32 attributes,
PSECURITY_DESCRIPTOR /*descriptor*/,
UINT64 allocation_size, PVOID * /*file_node*/,
PVOID *file_desc, OpenFileInfo *ofi)
-> NTSTATUS {
remote::file_info fi{};
std::string normalized_name;
BOOLEAN exists = 0;
const auto ret = remote_instance_->winfsp_create(
file_name, create_options, granted_access, attributes, allocation_size,
file_desc, &fi, normalized_name, exists);
if (ret == STATUS_SUCCESS) {
set_file_info(ofi->FileInfo, fi);
const auto file_path = utils::string::from_utf8(normalized_name);
wcsncpy(ofi->NormalizedName, &file_path[0], wcslen(&file_path[0]));
ofi->NormalizedNameSize = (UINT16)(wcslen(&file_path[0]) * sizeof(WCHAR));
}
return ret;
}
auto remote_winfsp_drive::Flush(PVOID /*file_node*/, PVOID file_desc,
FileInfo *file_info) -> NTSTATUS {
remote::file_info fi{};
const auto ret = remote_instance_->winfsp_flush(file_desc, &fi);
set_file_info(*file_info, fi);
return ret;
}
auto remote_winfsp_drive::GetFileInfo(PVOID /*file_node*/, PVOID file_desc,
FileInfo *file_info) -> NTSTATUS {
remote::file_info fi{};
const auto ret = remote_instance_->winfsp_get_file_info(file_desc, &fi);
set_file_info(*file_info, fi);
return ret;
}
auto remote_winfsp_drive::GetSecurityByName(PWSTR file_name, PUINT32 attributes,
PSECURITY_DESCRIPTOR descriptor,
SIZE_T *descriptor_size)
-> NTSTATUS {
std::wstring string_descriptor;
std::uint64_t sds = descriptor_size ? *descriptor_size : 0;
auto ret = remote_instance_->winfsp_get_security_by_name(
file_name, attributes, descriptor_size ? &sds : nullptr,
string_descriptor);
*descriptor_size = static_cast<SIZE_T>(sds);
if ((ret == STATUS_SUCCESS) && *descriptor_size) {
PSECURITY_DESCRIPTOR sd = nullptr;
ULONG sz2 = 0u;
if (::ConvertStringSecurityDescriptorToSecurityDescriptorW(
&string_descriptor[0u], SDDL_REVISION_1, &sd, &sz2)) {
if (sz2 > *descriptor_size) {
ret = STATUS_BUFFER_TOO_SMALL;
} else {
::CopyMemory(descriptor, sd, sz2);
}
*descriptor_size = sz2;
::LocalFree(sd);
} else {
ret = FspNtStatusFromWin32(::GetLastError());
}
}
return ret;
}
auto remote_winfsp_drive::GetVolumeInfo(VolumeInfo *volume_info) -> NTSTATUS {
std::string volume_label;
const auto ret = remote_instance_->winfsp_get_volume_info(
volume_info->TotalSize, volume_info->FreeSize, volume_label);
if (ret == STATUS_SUCCESS) {
const auto byte_size =
static_cast<UINT16>(volume_label.size() * sizeof(WCHAR));
wcscpy_s(&volume_info->VolumeLabel[0u], 32,
&utils::string::from_utf8(volume_label)[0u]);
volume_info->VolumeLabelLength =
std::min(static_cast<UINT16>(64u), byte_size);
}
return ret;
}
auto remote_winfsp_drive::Init(PVOID host) -> NTSTATUS {
auto *file_system_host = reinterpret_cast<FileSystemHost *>(host);
if (not config_.get_enable_mount_manager()) {
file_system_host->SetPrefix(
&(L"\\repertory\\" +
std::wstring(file_system_host->FileSystemName()).substr(0, 1))[0u]);
}
file_system_host->SetFileSystemName(REPERTORY_W);
file_system_host->SetFlushAndPurgeOnCleanup(TRUE);
file_system_host->SetReparsePoints(FALSE);
file_system_host->SetReparsePointsAccessCheck(FALSE);
file_system_host->SetSectorSize(WINFSP_ALLOCATION_UNIT);
file_system_host->SetSectorsPerAllocationUnit(1);
file_system_host->SetFileInfoTimeout(1000);
file_system_host->SetCaseSensitiveSearch(FALSE);
file_system_host->SetCasePreservedNames(TRUE);
file_system_host->SetNamedStreams(FALSE);
file_system_host->SetUnicodeOnDisk(TRUE);
file_system_host->SetPersistentAcls(FALSE);
file_system_host->SetPostCleanupWhenModifiedOnly(TRUE);
file_system_host->SetPassQueryDirectoryPattern(FALSE);
file_system_host->SetVolumeCreationTime(utils::get_file_time_now());
file_system_host->SetVolumeSerialNumber(0);
return STATUS_SUCCESS;
}
auto remote_winfsp_drive::mount(const std::vector<std::string> &drive_args)
-> int {
std::vector<std::string> parsed_drive_args;
const auto force_no_console = utils::collection_includes(drive_args, "-nc");
auto enable_console = false;
for (const auto &arg : drive_args) {
if (arg == "-f") {
if (not force_no_console) {
enable_console = true;
}
} else if (arg != "-nc") {
parsed_drive_args.emplace_back(arg);
}
}
logging_consumer l(config_.get_log_directory(), config_.get_event_level());
std::unique_ptr<console_consumer> c;
if (enable_console) {
c = std::make_unique<console_consumer>();
}
event_system::instance().start();
const auto ret =
winfsp_service(lock_, *this, parsed_drive_args, config_).Run();
event_system::instance().raise<drive_mount_result>(std::to_string(ret));
event_system::instance().stop();
c.reset();
return static_cast<int>(ret);
}
auto remote_winfsp_drive::Mounted(PVOID host) -> NTSTATUS {
auto *file_system_host = reinterpret_cast<FileSystemHost *>(host);
remote_instance_ = factory_();
server_ = std::make_unique<server>(config_);
server_->start();
mount_location_ = utils::string::to_utf8(file_system_host->MountPoint());
if (not lock_.set_mount_state(true, mount_location_,
::GetCurrentProcessId())) {
utils::error::raise_error(__FUNCTION__, "failed to set mount state");
}
return remote_instance_->winfsp_mounted(file_system_host->MountPoint());
}
auto remote_winfsp_drive::Open(PWSTR file_name, UINT32 create_options,
UINT32 granted_access, PVOID * /*file_node*/,
PVOID *file_desc, OpenFileInfo *ofi)
-> NTSTATUS {
remote::file_info fi{};
std::string normalize_name;
const auto ret =
remote_instance_->winfsp_open(file_name, create_options, granted_access,
file_desc, &fi, normalize_name);
if (ret == STATUS_SUCCESS) {
set_file_info(ofi->FileInfo, fi);
const auto file_path = utils::string::from_utf8(normalize_name);
wcsncpy(ofi->NormalizedName, &file_path[0], wcslen(&file_path[0]));
ofi->NormalizedNameSize = (UINT16)(wcslen(&file_path[0]) * sizeof(WCHAR));
}
return ret;
}
auto remote_winfsp_drive::Overwrite(PVOID /*file_node*/, PVOID file_desc,
UINT32 attributes,
BOOLEAN replace_attributes,
UINT64 allocation_size, FileInfo *file_info)
-> NTSTATUS {
remote::file_info fi{};
const auto ret = remote_instance_->winfsp_overwrite(
file_desc, attributes, replace_attributes, allocation_size, &fi);
set_file_info(*file_info, fi);
return ret;
}
void remote_winfsp_drive::populate_file_info(const json &item,
FSP_FSCTL_FILE_INFO &file_info) {
const auto di = directory_item::from_json(item);
file_info.FileSize = di.directory ? 0 : di.size;
file_info.AllocationSize =
utils::divide_with_ceiling(file_info.FileSize, WINFSP_ALLOCATION_UNIT) *
WINFSP_ALLOCATION_UNIT;
file_info.ChangeTime = utils::get_changed_time_from_meta(di.meta);
file_info.CreationTime = utils::get_creation_time_from_meta(di.meta);
file_info.FileAttributes = utils::get_attributes_from_meta(di.meta);
file_info.HardLinks = 0;
file_info.IndexNumber = 0;
file_info.LastAccessTime = utils::get_accessed_time_from_meta(di.meta);
file_info.LastWriteTime = utils::get_written_time_from_meta(di.meta);
file_info.ReparseTag = 0;
file_info.EaSize = 0;
}
auto remote_winfsp_drive::Read(PVOID /*file_node*/, PVOID file_desc,
PVOID buffer, UINT64 offset, ULONG length,
PULONG bytes_transferred) -> NTSTATUS {
return remote_instance_->winfsp_read(
file_desc, buffer, offset, length,
reinterpret_cast<PUINT32>(bytes_transferred));
}
auto remote_winfsp_drive::ReadDirectory(PVOID /*file_node*/, PVOID file_desc,
PWSTR pattern, PWSTR marker,
PVOID buffer, ULONG buffer_length,
PULONG bytes_transferred) -> NTSTATUS {
json item_list;
NTSTATUS ret = remote_instance_->winfsp_read_directory(file_desc, pattern,
marker, item_list);
if (ret == STATUS_SUCCESS) {
PVOID *directory_buffer = nullptr;
if ((ret = remote_instance_->winfsp_get_dir_buffer(
file_desc, directory_buffer)) == STATUS_SUCCESS) {
if (FspFileSystemAcquireDirectoryBuffer(
directory_buffer, static_cast<BOOLEAN>(nullptr == marker),
&ret)) {
auto item_found = false;
for (const auto &item : item_list) {
const auto item_path = item["path"].get<std::string>();
const auto display_name = utils::string::from_utf8(
utils::path::strip_to_file_name(item_path));
if (not marker || (marker && item_found)) {
if (not utils::path::is_ads_file_path(item_path)) {
union {
UINT8 B[FIELD_OFFSET(FSP_FSCTL_DIR_INFO, FileNameBuf) +
((MAX_PATH + 1) * sizeof(WCHAR))];
FSP_FSCTL_DIR_INFO D;
} directory_info_buffer;
auto *directory_info = &directory_info_buffer.D;
::ZeroMemory(directory_info, sizeof(*directory_info));
directory_info->Size = static_cast<UINT16>(
FIELD_OFFSET(FSP_FSCTL_DIR_INFO, FileNameBuf) +
(std::min((size_t)MAX_PATH, display_name.size()) *
sizeof(WCHAR)));
if (not item["meta"].empty() ||
((item_path != ".") && (item_path != ".."))) {
populate_file_info(item, directory_info->FileInfo);
}
if (ret == STATUS_SUCCESS) {
::wcscpy_s(&directory_info->FileNameBuf[0], MAX_PATH,
&display_name[0]);
FspFileSystemFillDirectoryBuffer(directory_buffer,
directory_info, &ret);
if (ret != STATUS_SUCCESS) {
break;
}
}
}
} else {
item_found = display_name == std::wstring(marker);
}
}
FspFileSystemReleaseDirectoryBuffer(directory_buffer);
}
if ((ret == STATUS_SUCCESS) || (ret == STATUS_NO_MORE_FILES)) {
FspFileSystemReadDirectoryBuffer(
directory_buffer, marker, buffer, buffer_length,
reinterpret_cast<PULONG>(bytes_transferred));
ret = STATUS_SUCCESS;
}
}
}
return ret;
}
auto remote_winfsp_drive::Rename(PVOID /*file_node*/, PVOID file_desc,
PWSTR file_name, PWSTR new_file_name,
BOOLEAN replace_if_exists) -> NTSTATUS {
return remote_instance_->winfsp_rename(file_desc, file_name, new_file_name,
replace_if_exists);
}
auto remote_winfsp_drive::SetBasicInfo(PVOID /*file_node*/, PVOID file_desc,
UINT32 attributes, UINT64 creation_time,
UINT64 last_access_time,
UINT64 last_write_time,
UINT64 change_time, FileInfo *file_info)
-> NTSTATUS {
remote::file_info fi{};
const auto ret = remote_instance_->winfsp_set_basic_info(
file_desc, attributes, creation_time, last_access_time, last_write_time,
change_time, &fi);
set_file_info(*file_info, fi);
return ret;
}
void remote_winfsp_drive::set_file_info(FileInfo &dest,
const remote::file_info &src) {
dest.FileAttributes = src.FileAttributes;
dest.ReparseTag = src.ReparseTag;
dest.AllocationSize = src.AllocationSize;
dest.FileSize = src.FileSize;
dest.CreationTime = src.CreationTime;
dest.LastAccessTime = src.LastAccessTime;
dest.LastWriteTime = src.LastWriteTime;
dest.ChangeTime = src.ChangeTime;
dest.IndexNumber = src.IndexNumber;
dest.HardLinks = src.HardLinks;
dest.EaSize = src.EaSize;
}
auto remote_winfsp_drive::SetFileSize(PVOID /*file_node*/, PVOID file_desc,
UINT64 new_size,
BOOLEAN set_allocation_size,
FileInfo *file_info) -> NTSTATUS {
remote::file_info fi{};
const auto ret = remote_instance_->winfsp_set_file_size(
file_desc, new_size, set_allocation_size, &fi);
set_file_info(*file_info, fi);
return ret;
}
VOID remote_winfsp_drive::Unmounted(PVOID host) {
server_->stop();
server_.reset();
auto *file_system_host = reinterpret_cast<FileSystemHost *>(host);
if (not lock_.set_mount_state(false, "", -1)) {
utils::error::raise_error(__FUNCTION__, "failed to set mount state");
}
remote_instance_->winfsp_unmounted(file_system_host->MountPoint());
remote_instance_.reset();
mount_location_ = "";
}
auto remote_winfsp_drive::Write(PVOID /*file_node*/, PVOID file_desc,
PVOID buffer, UINT64 offset, ULONG length,
BOOLEAN write_to_end, BOOLEAN constrained_io,
PULONG bytes_transferred, FileInfo *file_info)
-> NTSTATUS {
remote::file_info fi{};
const auto ret = remote_instance_->winfsp_write(
file_desc, buffer, offset, length, write_to_end, constrained_io,
reinterpret_cast<PUINT32>(bytes_transferred), &fi);
set_file_info(*file_info, fi);
return ret;
}
} // namespace repertory::remote_winfsp
#endif // _WIN32
/*
Copyright <2018-2023> <scott.e.graves@protonmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#ifdef _WIN32
#include <utility>
#include "drives/winfsp/remotewinfsp/remote_winfsp_drive.hpp"
#include "app_config.hpp"
#include "events/consumers/console_consumer.hpp"
#include "events/consumers/logging_consumer.hpp"
#include "events/events.hpp"
#include "platform/platform.hpp"
#include "rpc/server/server.hpp"
#include "utils/error_utils.hpp"
#include "utils/file_utils.hpp"
#include "utils/path_utils.hpp"
#include "utils/utils.hpp"
namespace repertory::remote_winfsp {
remote_winfsp_drive::winfsp_service::winfsp_service(
lock_data &lock, remote_winfsp_drive &drive,
std::vector<std::string> drive_args, app_config &config)
: Service(&(L"RepertoryRemote_" +
utils::string::from_utf8(lock.get_unique_id()))[0u]),
config_(config),
lock_(lock),
drive_(drive),
drive_args_(std::move(drive_args)),
host_(drive) {}
auto remote_winfsp_drive::winfsp_service::OnStart(ULONG, PWSTR *) -> NTSTATUS {
const auto mount_location = utils::string::to_lower(
utils::path::absolute((drive_args_.size() > 1u) ? drive_args_[1u] : ""));
const auto drive_letter =
((mount_location.size() == 2u) ||
((mount_location.size() == 3u) && (mount_location[2u] == '\\'))) &&
(mount_location[1u] == ':');
auto ret = drive_letter ? STATUS_DEVICE_BUSY : STATUS_NOT_SUPPORTED;
if ((drive_letter && not utils::file::is_directory(mount_location))) {
auto unicode_mount_location = utils::string::from_utf8(mount_location);
host_.SetFileSystemName(&unicode_mount_location[0u]);
if (config_.get_enable_mount_manager()) {
unicode_mount_location =
std::wstring(L"\\\\.\\") + unicode_mount_location[0u] + L":";
}
ret = host_.Mount(&unicode_mount_location[0u]);
} else {
std::cerr << (drive_letter ? "Mount location in use: "
: "Mount location not supported: ")
<< mount_location << std::endl;
}
if (ret != STATUS_SUCCESS) {
event_system::instance().raise<drive_mount_failed>(mount_location,
std::to_string(ret));
if (not lock_.set_mount_state(false, "", -1)) {
utils::error::raise_error(__FUNCTION__, "failed to set mount state");
}
}
return ret;
}
auto remote_winfsp_drive::winfsp_service::OnStop() -> NTSTATUS {
host_.Unmount();
if (not lock_.set_mount_state(false, "", -1)) {
utils::error::raise_error(__FUNCTION__, "failed to set mount state");
}
return STATUS_SUCCESS;
}
remote_winfsp_drive::remote_winfsp_drive(app_config &config,
remote_instance_factory factory,
lock_data &lock)
: FileSystemBase(),
config_(config),
lock_(lock),
factory_(std::move(factory)) {
E_SUBSCRIBE_EXACT(unmount_requested, [this](const unmount_requested &) {
std::thread([this]() { this->shutdown(); }).detach();
});
}
auto remote_winfsp_drive::CanDelete(PVOID /*file_node*/, PVOID file_desc,
PWSTR file_name) -> NTSTATUS {
return remote_instance_->winfsp_can_delete(file_desc, file_name);
}
VOID remote_winfsp_drive::Cleanup(PVOID /*file_node*/, PVOID file_desc,
PWSTR file_name, ULONG flags) {
BOOLEAN was_closed;
remote_instance_->winfsp_cleanup(file_desc, file_name, flags, was_closed);
}
VOID remote_winfsp_drive::Close(PVOID /*file_node*/, PVOID file_desc) {
remote_instance_->winfsp_close(file_desc);
}
auto remote_winfsp_drive::Create(PWSTR file_name, UINT32 create_options,
UINT32 granted_access, UINT32 attributes,
PSECURITY_DESCRIPTOR /*descriptor*/,
UINT64 allocation_size, PVOID * /*file_node*/,
PVOID *file_desc, OpenFileInfo *ofi)
-> NTSTATUS {
remote::file_info fi{};
std::string normalized_name;
BOOLEAN exists = 0;
const auto ret = remote_instance_->winfsp_create(
file_name, create_options, granted_access, attributes, allocation_size,
file_desc, &fi, normalized_name, exists);
if (ret == STATUS_SUCCESS) {
set_file_info(ofi->FileInfo, fi);
const auto file_path = utils::string::from_utf8(normalized_name);
wcsncpy(ofi->NormalizedName, &file_path[0], wcslen(&file_path[0]));
ofi->NormalizedNameSize = (UINT16)(wcslen(&file_path[0]) * sizeof(WCHAR));
}
return ret;
}
auto remote_winfsp_drive::Flush(PVOID /*file_node*/, PVOID file_desc,
FileInfo *file_info) -> NTSTATUS {
remote::file_info fi{};
const auto ret = remote_instance_->winfsp_flush(file_desc, &fi);
set_file_info(*file_info, fi);
return ret;
}
auto remote_winfsp_drive::GetFileInfo(PVOID /*file_node*/, PVOID file_desc,
FileInfo *file_info) -> NTSTATUS {
remote::file_info fi{};
const auto ret = remote_instance_->winfsp_get_file_info(file_desc, &fi);
set_file_info(*file_info, fi);
return ret;
}
auto remote_winfsp_drive::GetSecurityByName(PWSTR file_name, PUINT32 attributes,
PSECURITY_DESCRIPTOR descriptor,
SIZE_T *descriptor_size)
-> NTSTATUS {
std::wstring string_descriptor;
std::uint64_t sds = descriptor_size ? *descriptor_size : 0;
auto ret = remote_instance_->winfsp_get_security_by_name(
file_name, attributes, descriptor_size ? &sds : nullptr,
string_descriptor);
*descriptor_size = static_cast<SIZE_T>(sds);
if ((ret == STATUS_SUCCESS) && *descriptor_size) {
PSECURITY_DESCRIPTOR sd = nullptr;
ULONG sz2 = 0u;
if (::ConvertStringSecurityDescriptorToSecurityDescriptorW(
&string_descriptor[0u], SDDL_REVISION_1, &sd, &sz2)) {
if (sz2 > *descriptor_size) {
ret = STATUS_BUFFER_TOO_SMALL;
} else {
::CopyMemory(descriptor, sd, sz2);
}
*descriptor_size = sz2;
::LocalFree(sd);
} else {
ret = FspNtStatusFromWin32(::GetLastError());
}
}
return ret;
}
auto remote_winfsp_drive::GetVolumeInfo(VolumeInfo *volume_info) -> NTSTATUS {
std::string volume_label;
const auto ret = remote_instance_->winfsp_get_volume_info(
volume_info->TotalSize, volume_info->FreeSize, volume_label);
if (ret == STATUS_SUCCESS) {
const auto byte_size =
static_cast<UINT16>(volume_label.size() * sizeof(WCHAR));
wcscpy_s(&volume_info->VolumeLabel[0u], 32,
&utils::string::from_utf8(volume_label)[0u]);
volume_info->VolumeLabelLength =
std::min(static_cast<UINT16>(64u), byte_size);
}
return ret;
}
auto remote_winfsp_drive::Init(PVOID host) -> NTSTATUS {
auto *file_system_host = reinterpret_cast<FileSystemHost *>(host);
if (not config_.get_enable_mount_manager()) {
file_system_host->SetPrefix(
&(L"\\repertory\\" +
std::wstring(file_system_host->FileSystemName()).substr(0, 1))[0u]);
}
file_system_host->SetFileSystemName(REPERTORY_W);
file_system_host->SetFlushAndPurgeOnCleanup(TRUE);
file_system_host->SetReparsePoints(FALSE);
file_system_host->SetReparsePointsAccessCheck(FALSE);
file_system_host->SetSectorSize(WINFSP_ALLOCATION_UNIT);
file_system_host->SetSectorsPerAllocationUnit(1);
file_system_host->SetFileInfoTimeout(1000);
file_system_host->SetCaseSensitiveSearch(FALSE);
file_system_host->SetCasePreservedNames(TRUE);
file_system_host->SetNamedStreams(FALSE);
file_system_host->SetUnicodeOnDisk(TRUE);
file_system_host->SetPersistentAcls(FALSE);
file_system_host->SetPostCleanupWhenModifiedOnly(TRUE);
file_system_host->SetPassQueryDirectoryPattern(FALSE);
file_system_host->SetVolumeCreationTime(utils::get_file_time_now());
file_system_host->SetVolumeSerialNumber(0);
return STATUS_SUCCESS;
}
auto remote_winfsp_drive::mount(const std::vector<std::string> &drive_args)
-> int {
std::vector<std::string> parsed_drive_args;
const auto force_no_console = utils::collection_includes(drive_args, "-nc");
auto enable_console = false;
for (const auto &arg : drive_args) {
if (arg == "-f") {
if (not force_no_console) {
enable_console = true;
}
} else if (arg != "-nc") {
parsed_drive_args.emplace_back(arg);
}
}
logging_consumer l(config_.get_log_directory(), config_.get_event_level());
std::unique_ptr<console_consumer> c;
if (enable_console) {
c = std::make_unique<console_consumer>();
}
event_system::instance().start();
const auto ret =
winfsp_service(lock_, *this, parsed_drive_args, config_).Run();
event_system::instance().raise<drive_mount_result>(std::to_string(ret));
event_system::instance().stop();
c.reset();
return static_cast<int>(ret);
}
auto remote_winfsp_drive::Mounted(PVOID host) -> NTSTATUS {
auto *file_system_host = reinterpret_cast<FileSystemHost *>(host);
remote_instance_ = factory_();
server_ = std::make_unique<server>(config_);
server_->start();
mount_location_ = utils::string::to_utf8(file_system_host->MountPoint());
if (not lock_.set_mount_state(true, mount_location_,
::GetCurrentProcessId())) {
utils::error::raise_error(__FUNCTION__, "failed to set mount state");
}
return remote_instance_->winfsp_mounted(file_system_host->MountPoint());
}
auto remote_winfsp_drive::Open(PWSTR file_name, UINT32 create_options,
UINT32 granted_access, PVOID * /*file_node*/,
PVOID *file_desc, OpenFileInfo *ofi)
-> NTSTATUS {
remote::file_info fi{};
std::string normalize_name;
const auto ret =
remote_instance_->winfsp_open(file_name, create_options, granted_access,
file_desc, &fi, normalize_name);
if (ret == STATUS_SUCCESS) {
set_file_info(ofi->FileInfo, fi);
const auto file_path = utils::string::from_utf8(normalize_name);
wcsncpy(ofi->NormalizedName, &file_path[0], wcslen(&file_path[0]));
ofi->NormalizedNameSize = (UINT16)(wcslen(&file_path[0]) * sizeof(WCHAR));
}
return ret;
}
auto remote_winfsp_drive::Overwrite(PVOID /*file_node*/, PVOID file_desc,
UINT32 attributes,
BOOLEAN replace_attributes,
UINT64 allocation_size, FileInfo *file_info)
-> NTSTATUS {
remote::file_info fi{};
const auto ret = remote_instance_->winfsp_overwrite(
file_desc, attributes, replace_attributes, allocation_size, &fi);
set_file_info(*file_info, fi);
return ret;
}
void remote_winfsp_drive::populate_file_info(const json &item,
FSP_FSCTL_FILE_INFO &file_info) {
const auto di = directory_item::from_json(item);
file_info.FileSize = di.directory ? 0 : di.size;
file_info.AllocationSize =
utils::divide_with_ceiling(file_info.FileSize, WINFSP_ALLOCATION_UNIT) *
WINFSP_ALLOCATION_UNIT;
file_info.ChangeTime = utils::get_changed_time_from_meta(di.meta);
file_info.CreationTime = utils::get_creation_time_from_meta(di.meta);
file_info.FileAttributes = utils::get_attributes_from_meta(di.meta);
file_info.HardLinks = 0;
file_info.IndexNumber = 0;
file_info.LastAccessTime = utils::get_accessed_time_from_meta(di.meta);
file_info.LastWriteTime = utils::get_written_time_from_meta(di.meta);
file_info.ReparseTag = 0;
file_info.EaSize = 0;
}
auto remote_winfsp_drive::Read(PVOID /*file_node*/, PVOID file_desc,
PVOID buffer, UINT64 offset, ULONG length,
PULONG bytes_transferred) -> NTSTATUS {
return remote_instance_->winfsp_read(
file_desc, buffer, offset, length,
reinterpret_cast<PUINT32>(bytes_transferred));
}
auto remote_winfsp_drive::ReadDirectory(PVOID /*file_node*/, PVOID file_desc,
PWSTR pattern, PWSTR marker,
PVOID buffer, ULONG buffer_length,
PULONG bytes_transferred) -> NTSTATUS {
json item_list;
NTSTATUS ret = remote_instance_->winfsp_read_directory(file_desc, pattern,
marker, item_list);
if (ret == STATUS_SUCCESS) {
PVOID *directory_buffer = nullptr;
if ((ret = remote_instance_->winfsp_get_dir_buffer(
file_desc, directory_buffer)) == STATUS_SUCCESS) {
if (FspFileSystemAcquireDirectoryBuffer(
directory_buffer, static_cast<BOOLEAN>(nullptr == marker),
&ret)) {
auto item_found = false;
for (const auto &item : item_list) {
const auto item_path = item["path"].get<std::string>();
const auto display_name = utils::string::from_utf8(
utils::path::strip_to_file_name(item_path));
if (not marker || (marker && item_found)) {
if (not utils::path::is_ads_file_path(item_path)) {
union {
UINT8 B[FIELD_OFFSET(FSP_FSCTL_DIR_INFO, FileNameBuf) +
((MAX_PATH + 1) * sizeof(WCHAR))];
FSP_FSCTL_DIR_INFO D;
} directory_info_buffer;
auto *directory_info = &directory_info_buffer.D;
::ZeroMemory(directory_info, sizeof(*directory_info));
directory_info->Size = static_cast<UINT16>(
FIELD_OFFSET(FSP_FSCTL_DIR_INFO, FileNameBuf) +
(std::min((size_t)MAX_PATH, display_name.size()) *
sizeof(WCHAR)));
if (not item["meta"].empty() ||
((item_path != ".") && (item_path != ".."))) {
populate_file_info(item, directory_info->FileInfo);
}
if (ret == STATUS_SUCCESS) {
::wcscpy_s(&directory_info->FileNameBuf[0], MAX_PATH,
&display_name[0]);
FspFileSystemFillDirectoryBuffer(directory_buffer,
directory_info, &ret);
if (ret != STATUS_SUCCESS) {
break;
}
}
}
} else {
item_found = display_name == std::wstring(marker);
}
}
FspFileSystemReleaseDirectoryBuffer(directory_buffer);
}
if ((ret == STATUS_SUCCESS) || (ret == STATUS_NO_MORE_FILES)) {
FspFileSystemReadDirectoryBuffer(
directory_buffer, marker, buffer, buffer_length,
reinterpret_cast<PULONG>(bytes_transferred));
ret = STATUS_SUCCESS;
}
}
}
return ret;
}
auto remote_winfsp_drive::Rename(PVOID /*file_node*/, PVOID file_desc,
PWSTR file_name, PWSTR new_file_name,
BOOLEAN replace_if_exists) -> NTSTATUS {
return remote_instance_->winfsp_rename(file_desc, file_name, new_file_name,
replace_if_exists);
}
auto remote_winfsp_drive::SetBasicInfo(PVOID /*file_node*/, PVOID file_desc,
UINT32 attributes, UINT64 creation_time,
UINT64 last_access_time,
UINT64 last_write_time,
UINT64 change_time, FileInfo *file_info)
-> NTSTATUS {
remote::file_info fi{};
const auto ret = remote_instance_->winfsp_set_basic_info(
file_desc, attributes, creation_time, last_access_time, last_write_time,
change_time, &fi);
set_file_info(*file_info, fi);
return ret;
}
void remote_winfsp_drive::set_file_info(FileInfo &dest,
const remote::file_info &src) {
dest.FileAttributes = src.FileAttributes;
dest.ReparseTag = src.ReparseTag;
dest.AllocationSize = src.AllocationSize;
dest.FileSize = src.FileSize;
dest.CreationTime = src.CreationTime;
dest.LastAccessTime = src.LastAccessTime;
dest.LastWriteTime = src.LastWriteTime;
dest.ChangeTime = src.ChangeTime;
dest.IndexNumber = src.IndexNumber;
dest.HardLinks = src.HardLinks;
dest.EaSize = src.EaSize;
}
auto remote_winfsp_drive::SetFileSize(PVOID /*file_node*/, PVOID file_desc,
UINT64 new_size,
BOOLEAN set_allocation_size,
FileInfo *file_info) -> NTSTATUS {
remote::file_info fi{};
const auto ret = remote_instance_->winfsp_set_file_size(
file_desc, new_size, set_allocation_size, &fi);
set_file_info(*file_info, fi);
return ret;
}
VOID remote_winfsp_drive::Unmounted(PVOID host) {
server_->stop();
server_.reset();
auto *file_system_host = reinterpret_cast<FileSystemHost *>(host);
if (not lock_.set_mount_state(false, "", -1)) {
utils::error::raise_error(__FUNCTION__, "failed to set mount state");
}
remote_instance_->winfsp_unmounted(file_system_host->MountPoint());
remote_instance_.reset();
mount_location_ = "";
}
auto remote_winfsp_drive::Write(PVOID /*file_node*/, PVOID file_desc,
PVOID buffer, UINT64 offset, ULONG length,
BOOLEAN write_to_end, BOOLEAN constrained_io,
PULONG bytes_transferred, FileInfo *file_info)
-> NTSTATUS {
remote::file_info fi{};
const auto ret = remote_instance_->winfsp_write(
file_desc, buffer, offset, length, write_to_end, constrained_io,
reinterpret_cast<PUINT32>(bytes_transferred), &fi);
set_file_info(*file_info, fi);
return ret;
}
} // namespace repertory::remote_winfsp
#endif // _WIN32

File diff suppressed because it is too large Load Diff

View File

@@ -1,178 +1,178 @@
/*
Copyright <2018-2023> <scott.e.graves@protonmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "events/consumers/logging_consumer.hpp"
#include "events/events.hpp"
#include "types/startup_exception.hpp"
#include "utils/error_utils.hpp"
#include "utils/file_utils.hpp"
#include "utils/path_utils.hpp"
#include "utils/unix/unix_utils.hpp"
#include "utils/utils.hpp"
namespace repertory {
logging_consumer::logging_consumer(const std::string &log_directory,
const event_level &level)
: event_level_(level),
log_directory_(utils::path::finalize(log_directory)),
log_path_(utils::path::combine(log_directory, {"repertory.log"})) {
if (not utils::file::create_full_directory_path(log_directory_)) {
throw startup_exception("failed to create log directory|sp|" +
log_directory_ + "|err|" +
std::to_string(utils::get_last_error_code()));
}
check_log_roll(0);
reopen_log_file();
E_SUBSCRIBE_ALL(process_event);
E_SUBSCRIBE_EXACT(event_level_changed,
[this](const event_level_changed &changed) {
event_level_ = event_level_from_string(
changed.get_new_event_level().get<std::string>());
});
logging_thread_ =
std::make_unique<std::thread>([this] { logging_thread(false); });
}
logging_consumer::~logging_consumer() {
E_CONSUMER_RELEASE();
unique_mutex_lock l(log_mutex_);
logging_active_ = false;
log_notify_.notify_all();
l.unlock();
logging_thread_->join();
logging_thread_.reset();
logging_thread(true);
close_log_file();
}
void logging_consumer::check_log_roll(std::size_t count) {
std::uint64_t file_size{};
const auto success = utils::file::get_file_size(log_path_, file_size);
if (success && (file_size + count) >= MAX_LOG_FILE_SIZE) {
close_log_file();
for (std::uint8_t i = MAX_LOG_FILES; i > 0u; i--) {
const auto temp_log_path = utils::path::combine(
log_directory_, {"repertory." + std::to_string(i) + ".log"});
if (utils::file::is_file(temp_log_path)) {
if (i == MAX_LOG_FILES) {
if (not utils::file::retry_delete_file(temp_log_path)) {
}
} else {
const auto next_file_path = utils::path::combine(
log_directory_,
{"repertory." + std::to_string(i + std::uint8_t(1)) + ".log"});
if (not utils::file::move_file(temp_log_path, next_file_path)) {
utils::error::raise_error(__FUNCTION__,
utils::get_last_error_code(),
temp_log_path + "|dest|" + next_file_path,
"failed to move file");
}
}
}
}
auto backup_log_path =
utils::path::combine(log_directory_, {"repertory.1.log"});
if (not utils::file::move_file(log_path_, backup_log_path)) {
utils::error::raise_error(__FUNCTION__, utils::get_last_error_code(),
log_path_ + "|dest|" + backup_log_path,
"failed to move file");
}
reopen_log_file();
}
}
void logging_consumer::close_log_file() {
if (log_file_) {
fclose(log_file_);
log_file_ = nullptr;
}
}
void logging_consumer::logging_thread(bool drain) {
do {
std::deque<std::shared_ptr<event>> events;
{
unique_mutex_lock l(log_mutex_);
if (event_queue_.empty() && not drain) {
log_notify_.wait_for(l, 2s);
} else {
events.insert(events.end(), event_queue_.begin(), event_queue_.end());
event_queue_.clear();
}
}
while (not events.empty()) {
auto event = events.front();
events.pop_front();
if (event->get_event_level() <= event_level_) {
const std::string msg = ([&]() -> std::string {
struct tm local_time {};
utils::get_local_time_now(local_time);
std::stringstream ss;
ss << std::put_time(&local_time, "%F %T") << "|"
<< event_level_to_string(event->get_event_level()).c_str() << "|"
<< event->get_single_line().c_str() << std::endl;
return ss.str();
})();
check_log_roll(msg.length());
auto retry = true;
for (int i = 0; retry && (i < 2); i++) {
retry = (not log_file_ || (fwrite(&msg[0], 1, msg.length(),
log_file_) != msg.length()));
if (retry) {
reopen_log_file();
}
}
if (log_file_) {
fflush(log_file_);
}
}
}
} while (logging_active_);
}
void logging_consumer::process_event(const event &event) {
{
mutex_lock l(log_mutex_);
event_queue_.push_back(event.clone());
log_notify_.notify_all();
}
}
void logging_consumer::reopen_log_file() {
close_log_file();
#ifdef _WIN32
log_file_ = _fsopen(&log_path_[0], "a+", _SH_DENYWR);
#else
log_file_ = fopen(&log_path_[0], "a+");
#endif
}
} // namespace repertory
/*
Copyright <2018-2023> <scott.e.graves@protonmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "events/consumers/logging_consumer.hpp"
#include "events/events.hpp"
#include "types/startup_exception.hpp"
#include "utils/error_utils.hpp"
#include "utils/file_utils.hpp"
#include "utils/path_utils.hpp"
#include "utils/unix/unix_utils.hpp"
#include "utils/utils.hpp"
namespace repertory {
logging_consumer::logging_consumer(const std::string &log_directory,
const event_level &level)
: event_level_(level),
log_directory_(utils::path::finalize(log_directory)),
log_path_(utils::path::combine(log_directory, {"repertory.log"})) {
if (not utils::file::create_full_directory_path(log_directory_)) {
throw startup_exception("failed to create log directory|sp|" +
log_directory_ + "|err|" +
std::to_string(utils::get_last_error_code()));
}
check_log_roll(0);
reopen_log_file();
E_SUBSCRIBE_ALL(process_event);
E_SUBSCRIBE_EXACT(event_level_changed,
[this](const event_level_changed &changed) {
event_level_ = event_level_from_string(
changed.get_new_event_level().get<std::string>());
});
logging_thread_ =
std::make_unique<std::thread>([this] { logging_thread(false); });
}
logging_consumer::~logging_consumer() {
E_CONSUMER_RELEASE();
unique_mutex_lock l(log_mutex_);
logging_active_ = false;
log_notify_.notify_all();
l.unlock();
logging_thread_->join();
logging_thread_.reset();
logging_thread(true);
close_log_file();
}
void logging_consumer::check_log_roll(std::size_t count) {
std::uint64_t file_size{};
const auto success = utils::file::get_file_size(log_path_, file_size);
if (success && (file_size + count) >= MAX_LOG_FILE_SIZE) {
close_log_file();
for (std::uint8_t i = MAX_LOG_FILES; i > 0u; i--) {
const auto temp_log_path = utils::path::combine(
log_directory_, {"repertory." + std::to_string(i) + ".log"});
if (utils::file::is_file(temp_log_path)) {
if (i == MAX_LOG_FILES) {
if (not utils::file::retry_delete_file(temp_log_path)) {
}
} else {
const auto next_file_path = utils::path::combine(
log_directory_,
{"repertory." + std::to_string(i + std::uint8_t(1)) + ".log"});
if (not utils::file::move_file(temp_log_path, next_file_path)) {
utils::error::raise_error(__FUNCTION__,
utils::get_last_error_code(),
temp_log_path + "|dest|" + next_file_path,
"failed to move file");
}
}
}
}
auto backup_log_path =
utils::path::combine(log_directory_, {"repertory.1.log"});
if (not utils::file::move_file(log_path_, backup_log_path)) {
utils::error::raise_error(__FUNCTION__, utils::get_last_error_code(),
log_path_ + "|dest|" + backup_log_path,
"failed to move file");
}
reopen_log_file();
}
}
void logging_consumer::close_log_file() {
if (log_file_) {
fclose(log_file_);
log_file_ = nullptr;
}
}
void logging_consumer::logging_thread(bool drain) {
do {
std::deque<std::shared_ptr<event>> events;
{
unique_mutex_lock l(log_mutex_);
if (event_queue_.empty() && not drain) {
log_notify_.wait_for(l, 2s);
} else {
events.insert(events.end(), event_queue_.begin(), event_queue_.end());
event_queue_.clear();
}
}
while (not events.empty()) {
auto event = events.front();
events.pop_front();
if (event->get_event_level() <= event_level_) {
const std::string msg = ([&]() -> std::string {
struct tm local_time {};
utils::get_local_time_now(local_time);
std::stringstream ss;
ss << std::put_time(&local_time, "%F %T") << "|"
<< event_level_to_string(event->get_event_level()).c_str() << "|"
<< event->get_single_line().c_str() << std::endl;
return ss.str();
})();
check_log_roll(msg.length());
auto retry = true;
for (int i = 0; retry && (i < 2); i++) {
retry = (not log_file_ || (fwrite(&msg[0], 1, msg.length(),
log_file_) != msg.length()));
if (retry) {
reopen_log_file();
}
}
if (log_file_) {
fflush(log_file_);
}
}
}
} while (logging_active_);
}
void logging_consumer::process_event(const event &event) {
{
mutex_lock l(log_mutex_);
event_queue_.push_back(event.clone());
log_notify_.notify_all();
}
}
void logging_consumer::reopen_log_file() {
close_log_file();
#ifdef _WIN32
log_file_ = _fsopen(&log_path_[0], "a+", _SH_DENYWR);
#else
log_file_ = fopen(&log_path_[0], "a+");
#endif
}
} // namespace repertory

View File

@@ -1,59 +1,59 @@
/*
Copyright <2018-2023> <scott.e.graves@protonmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "events/event.hpp"
#include "utils/string_utils.hpp"
namespace repertory {
auto event_level_from_string(std::string level) -> event_level {
level = utils::string::to_lower(level);
if (level == "debug" || level == "event_level::debug") {
return event_level::debug;
} else if (level == "warn" || level == "event_level::warn") {
return event_level::warn;
} else if (level == "normal" || level == "event_level::normal") {
return event_level::normal;
} else if (level == "error" || level == "event_level::error") {
return event_level::error;
} else if (level == "verbose" || level == "event_level::verbose") {
return event_level::verbose;
}
return event_level::normal;
}
auto event_level_to_string(const event_level &level) -> std::string {
switch (level) {
case event_level::debug:
return "debug";
case event_level::error:
return "error";
case event_level::normal:
return "normal";
case event_level::warn:
return "warn";
case event_level::verbose:
return "verbose";
default:
return "normal";
}
}
} // namespace repertory
/*
Copyright <2018-2023> <scott.e.graves@protonmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "events/event.hpp"
#include "utils/string_utils.hpp"
namespace repertory {
auto event_level_from_string(std::string level) -> event_level {
level = utils::string::to_lower(level);
if (level == "debug" || level == "event_level::debug") {
return event_level::debug;
} else if (level == "warn" || level == "event_level::warn") {
return event_level::warn;
} else if (level == "normal" || level == "event_level::normal") {
return event_level::normal;
} else if (level == "error" || level == "event_level::error") {
return event_level::error;
} else if (level == "verbose" || level == "event_level::verbose") {
return event_level::verbose;
}
return event_level::normal;
}
auto event_level_to_string(const event_level &level) -> std::string {
switch (level) {
case event_level::debug:
return "debug";
case event_level::error:
return "error";
case event_level::normal:
return "normal";
case event_level::warn:
return "warn";
case event_level::verbose:
return "verbose";
default:
return "normal";
}
}
} // namespace repertory

View File

@@ -1,36 +1,36 @@
/*
Copyright <2018-2023> <scott.e.graves@protonmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "events/t_event_system.hpp"
#include "events/event.hpp"
namespace repertory {
template <typename event_type>
auto t_event_system<event_type>::instance() -> t_event_system<event_type> & {
return event_system_;
}
template <typename event_type>
t_event_system<event_type> t_event_system<event_type>::event_system_;
template class t_event_system<event>;
} // namespace repertory
/*
Copyright <2018-2023> <scott.e.graves@protonmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "events/t_event_system.hpp"
#include "events/event.hpp"
namespace repertory {
template <typename event_type>
auto t_event_system<event_type>::instance() -> t_event_system<event_type> & {
return event_system_;
}
template <typename event_type>
t_event_system<event_type> t_event_system<event_type>::event_system_;
template class t_event_system<event>;
} // namespace repertory

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,244 +1,244 @@
/*
Copyright <2018-2023> <scott.e.graves@protonmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "file_manager/file_manager.hpp"
#include "utils/path_utils.hpp"
namespace repertory {
file_manager::open_file_base::open_file_base(std::uint64_t chunk_size,
std::uint8_t chunk_timeout,
filesystem_item fsi,
i_provider &provider)
: open_file_base(chunk_size, chunk_timeout, fsi, {}, provider) {}
file_manager::open_file_base::open_file_base(
std::uint64_t chunk_size, std::uint8_t chunk_timeout, filesystem_item fsi,
std::map<std::uint64_t, open_file_data> open_data, i_provider &provider)
: chunk_size_(chunk_size),
chunk_timeout_(chunk_timeout),
fsi_(std::move(fsi)),
last_chunk_size_(static_cast<std::size_t>(
fsi.size <= chunk_size ? fsi.size
: (fsi.size % chunk_size) == 0U ? chunk_size
: fsi.size % chunk_size)),
open_data_(std::move(open_data)),
provider_(provider) {
if (not fsi.directory) {
io_thread_ = std::make_unique<std::thread>([this] { file_io_thread(); });
}
}
void file_manager::open_file_base::add(std::uint64_t handle,
open_file_data ofd) {
recur_mutex_lock file_lock(file_mtx_);
open_data_[handle] = ofd;
if (open_data_.size() == 1U) {
event_system::instance().raise<filesystem_item_opened>(
fsi_.api_path, fsi_.source_path, fsi_.directory);
}
event_system::instance().raise<filesystem_item_handle_opened>(
fsi_.api_path, handle, fsi_.source_path, fsi_.directory);
}
auto file_manager::open_file_base::can_close() const -> bool {
recur_mutex_lock file_lock(file_mtx_);
if (fsi_.directory) {
return true;
}
if (not open_data_.empty()) {
return false;
}
if (modified_) {
return false;
}
if (get_api_error() != api_error::success) {
return true;
}
if (is_download_complete()) {
return true;
}
const std::chrono::system_clock::time_point last_access = last_access_;
const auto duration = std::chrono::duration_cast<std::chrono::seconds>(
std::chrono::system_clock::now() - last_access);
return (duration.count() >= chunk_timeout_);
}
auto file_manager::open_file_base::do_io(std::function<api_error()> action)
-> api_error {
unique_mutex_lock io_lock(io_thread_mtx_);
auto item = std::make_shared<io_item>(action);
io_thread_queue_.emplace_back(item);
io_thread_notify_.notify_all();
io_lock.unlock();
return item->get_result();
}
void file_manager::open_file_base::file_io_thread() {
unique_mutex_lock io_lock(io_thread_mtx_);
io_thread_notify_.notify_all();
io_lock.unlock();
const auto process_queue = [&]() {
io_lock.lock();
if (not io_stop_requested_ && io_thread_queue_.empty()) {
io_thread_notify_.wait(io_lock);
}
while (not io_thread_queue_.empty()) {
auto *item = io_thread_queue_.front().get();
io_thread_notify_.notify_all();
io_lock.unlock();
item->action();
io_lock.lock();
io_thread_queue_.pop_front();
}
io_thread_notify_.notify_all();
io_lock.unlock();
};
while (not io_stop_requested_) {
process_queue();
}
process_queue();
}
auto file_manager::open_file_base::get_api_error() const -> api_error {
mutex_lock error_lock(error_mtx_);
return error_;
}
auto file_manager::open_file_base::get_api_path() const -> std::string {
recur_mutex_lock file_lock(file_mtx_);
return fsi_.api_path;
}
auto file_manager::open_file_base::get_file_size() const -> std::uint64_t {
recur_mutex_lock file_lock(file_mtx_);
return fsi_.size;
}
auto file_manager::open_file_base::get_filesystem_item() const
-> filesystem_item {
recur_mutex_lock file_lock(file_mtx_);
return fsi_;
}
auto file_manager::open_file_base::get_handles() const
-> std::vector<std::uint64_t> {
recur_mutex_lock file_lock(file_mtx_);
std::vector<std::uint64_t> ret;
for (const auto &item : open_data_) {
ret.emplace_back(item.first);
}
return ret;
}
auto file_manager::open_file_base::get_open_data() const
-> std::map<std::uint64_t, open_file_data> {
recur_mutex_lock file_lock(file_mtx_);
return open_data_;
}
auto file_manager::open_file_base::get_open_data(std::uint64_t handle) const
-> open_file_data {
recur_mutex_lock file_lock(file_mtx_);
return open_data_.at(handle);
}
auto file_manager::open_file_base::get_open_file_count() const -> std::size_t {
recur_mutex_lock file_lock(file_mtx_);
return open_data_.size();
}
auto file_manager::open_file_base::is_modified() const -> bool {
recur_mutex_lock file_lock(file_mtx_);
return modified_;
}
void file_manager::open_file_base::remove(std::uint64_t handle) {
recur_mutex_lock file_lock(file_mtx_);
open_data_.erase(handle);
event_system::instance().raise<filesystem_item_handle_closed>(
fsi_.api_path, handle, fsi_.source_path, fsi_.directory, modified_);
if (open_data_.empty()) {
event_system::instance().raise<filesystem_item_closed>(
fsi_.api_path, fsi_.source_path, fsi_.directory, modified_);
}
}
void file_manager::open_file_base::reset_timeout() {
last_access_ = std::chrono::system_clock::now();
}
auto file_manager::open_file_base::set_api_error(const api_error &err)
-> api_error {
mutex_lock error_lock(error_mtx_);
if (error_ != err) {
return ((error_ = (error_ == api_error::success ||
error_ == api_error::download_incomplete ||
error_ == api_error::download_stopped
? err
: error_)));
}
return error_;
}
void file_manager::open_file_base::set_api_path(const std::string &api_path) {
recur_mutex_lock file_lock(file_mtx_);
fsi_.api_path = api_path;
fsi_.api_parent = utils::path::get_parent_api_path(api_path);
}
auto file_manager::open_file_base::close() -> bool {
unique_mutex_lock io_lock(io_thread_mtx_);
if (not fsi_.directory && not io_stop_requested_) {
io_stop_requested_ = true;
io_thread_notify_.notify_all();
io_lock.unlock();
if (io_thread_) {
io_thread_->join();
io_thread_.reset();
return true;
}
return false;
}
io_thread_notify_.notify_all();
io_lock.unlock();
return false;
}
} // namespace repertory
/*
Copyright <2018-2023> <scott.e.graves@protonmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "file_manager/file_manager.hpp"
#include "utils/path_utils.hpp"
namespace repertory {
file_manager::open_file_base::open_file_base(std::uint64_t chunk_size,
std::uint8_t chunk_timeout,
filesystem_item fsi,
i_provider &provider)
: open_file_base(chunk_size, chunk_timeout, fsi, {}, provider) {}
file_manager::open_file_base::open_file_base(
std::uint64_t chunk_size, std::uint8_t chunk_timeout, filesystem_item fsi,
std::map<std::uint64_t, open_file_data> open_data, i_provider &provider)
: chunk_size_(chunk_size),
chunk_timeout_(chunk_timeout),
fsi_(std::move(fsi)),
last_chunk_size_(static_cast<std::size_t>(
fsi.size <= chunk_size ? fsi.size
: (fsi.size % chunk_size) == 0U ? chunk_size
: fsi.size % chunk_size)),
open_data_(std::move(open_data)),
provider_(provider) {
if (not fsi.directory) {
io_thread_ = std::make_unique<std::thread>([this] { file_io_thread(); });
}
}
void file_manager::open_file_base::add(std::uint64_t handle,
open_file_data ofd) {
recur_mutex_lock file_lock(file_mtx_);
open_data_[handle] = ofd;
if (open_data_.size() == 1U) {
event_system::instance().raise<filesystem_item_opened>(
fsi_.api_path, fsi_.source_path, fsi_.directory);
}
event_system::instance().raise<filesystem_item_handle_opened>(
fsi_.api_path, handle, fsi_.source_path, fsi_.directory);
}
auto file_manager::open_file_base::can_close() const -> bool {
recur_mutex_lock file_lock(file_mtx_);
if (fsi_.directory) {
return true;
}
if (not open_data_.empty()) {
return false;
}
if (modified_) {
return false;
}
if (get_api_error() != api_error::success) {
return true;
}
if (is_download_complete()) {
return true;
}
const std::chrono::system_clock::time_point last_access = last_access_;
const auto duration = std::chrono::duration_cast<std::chrono::seconds>(
std::chrono::system_clock::now() - last_access);
return (duration.count() >= chunk_timeout_);
}
auto file_manager::open_file_base::do_io(std::function<api_error()> action)
-> api_error {
unique_mutex_lock io_lock(io_thread_mtx_);
auto item = std::make_shared<io_item>(action);
io_thread_queue_.emplace_back(item);
io_thread_notify_.notify_all();
io_lock.unlock();
return item->get_result();
}
void file_manager::open_file_base::file_io_thread() {
unique_mutex_lock io_lock(io_thread_mtx_);
io_thread_notify_.notify_all();
io_lock.unlock();
const auto process_queue = [&]() {
io_lock.lock();
if (not io_stop_requested_ && io_thread_queue_.empty()) {
io_thread_notify_.wait(io_lock);
}
while (not io_thread_queue_.empty()) {
auto *item = io_thread_queue_.front().get();
io_thread_notify_.notify_all();
io_lock.unlock();
item->action();
io_lock.lock();
io_thread_queue_.pop_front();
}
io_thread_notify_.notify_all();
io_lock.unlock();
};
while (not io_stop_requested_) {
process_queue();
}
process_queue();
}
auto file_manager::open_file_base::get_api_error() const -> api_error {
mutex_lock error_lock(error_mtx_);
return error_;
}
auto file_manager::open_file_base::get_api_path() const -> std::string {
recur_mutex_lock file_lock(file_mtx_);
return fsi_.api_path;
}
auto file_manager::open_file_base::get_file_size() const -> std::uint64_t {
recur_mutex_lock file_lock(file_mtx_);
return fsi_.size;
}
auto file_manager::open_file_base::get_filesystem_item() const
-> filesystem_item {
recur_mutex_lock file_lock(file_mtx_);
return fsi_;
}
auto file_manager::open_file_base::get_handles() const
-> std::vector<std::uint64_t> {
recur_mutex_lock file_lock(file_mtx_);
std::vector<std::uint64_t> ret;
for (const auto &item : open_data_) {
ret.emplace_back(item.first);
}
return ret;
}
auto file_manager::open_file_base::get_open_data() const
-> std::map<std::uint64_t, open_file_data> {
recur_mutex_lock file_lock(file_mtx_);
return open_data_;
}
auto file_manager::open_file_base::get_open_data(std::uint64_t handle) const
-> open_file_data {
recur_mutex_lock file_lock(file_mtx_);
return open_data_.at(handle);
}
auto file_manager::open_file_base::get_open_file_count() const -> std::size_t {
recur_mutex_lock file_lock(file_mtx_);
return open_data_.size();
}
auto file_manager::open_file_base::is_modified() const -> bool {
recur_mutex_lock file_lock(file_mtx_);
return modified_;
}
void file_manager::open_file_base::remove(std::uint64_t handle) {
recur_mutex_lock file_lock(file_mtx_);
open_data_.erase(handle);
event_system::instance().raise<filesystem_item_handle_closed>(
fsi_.api_path, handle, fsi_.source_path, fsi_.directory, modified_);
if (open_data_.empty()) {
event_system::instance().raise<filesystem_item_closed>(
fsi_.api_path, fsi_.source_path, fsi_.directory, modified_);
}
}
void file_manager::open_file_base::reset_timeout() {
last_access_ = std::chrono::system_clock::now();
}
auto file_manager::open_file_base::set_api_error(const api_error &err)
-> api_error {
mutex_lock error_lock(error_mtx_);
if (error_ != err) {
return ((error_ = (error_ == api_error::success ||
error_ == api_error::download_incomplete ||
error_ == api_error::download_stopped
? err
: error_)));
}
return error_;
}
void file_manager::open_file_base::set_api_path(const std::string &api_path) {
recur_mutex_lock file_lock(file_mtx_);
fsi_.api_path = api_path;
fsi_.api_parent = utils::path::get_parent_api_path(api_path);
}
auto file_manager::open_file_base::close() -> bool {
unique_mutex_lock io_lock(io_thread_mtx_);
if (not fsi_.directory && not io_stop_requested_) {
io_stop_requested_ = true;
io_thread_notify_.notify_all();
io_lock.unlock();
if (io_thread_) {
io_thread_->join();
io_thread_.reset();
return true;
}
return false;
}
io_thread_notify_.notify_all();
io_lock.unlock();
return false;
}
} // namespace repertory

View File

@@ -1,43 +1,43 @@
/*
Copyright <2018-2023> <scott.e.graves@protonmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "file_manager/file_manager.hpp"
namespace repertory {
void file_manager::open_file_base::download::notify(const api_error &e) {
complete_ = true;
error_ = e;
unique_mutex_lock lock(mtx_);
notify_.notify_all();
}
auto file_manager::open_file_base::download::wait() -> api_error {
if (not complete_) {
unique_mutex_lock lock(mtx_);
if (not complete_) {
notify_.wait(lock);
}
notify_.notify_all();
}
return error_;
}
} // namespace repertory
/*
Copyright <2018-2023> <scott.e.graves@protonmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "file_manager/file_manager.hpp"
namespace repertory {
void file_manager::open_file_base::download::notify(const api_error &e) {
complete_ = true;
error_ = e;
unique_mutex_lock lock(mtx_);
notify_.notify_all();
}
auto file_manager::open_file_base::download::wait() -> api_error {
if (not complete_) {
unique_mutex_lock lock(mtx_);
if (not complete_) {
notify_.wait(lock);
}
notify_.notify_all();
}
return error_;
}
} // namespace repertory

View File

@@ -1,41 +1,41 @@
/*
Copyright <2018-2023> <scott.e.graves@protonmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "file_manager/file_manager.hpp"
namespace repertory {
void file_manager::open_file_base::io_item::action() {
result_ = action_();
mutex_lock lock(mtx_);
notify_.notify_all();
}
auto file_manager::open_file_base::io_item::get_result() -> api_error {
unique_mutex_lock lock(mtx_);
if (result_.has_value()) {
return result_.value();
}
notify_.wait(lock);
return result_.value();
}
} // namespace repertory
/*
Copyright <2018-2023> <scott.e.graves@protonmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "file_manager/file_manager.hpp"
namespace repertory {
void file_manager::open_file_base::io_item::action() {
result_ = action_();
mutex_lock lock(mtx_);
notify_.notify_all();
}
auto file_manager::open_file_base::io_item::get_result() -> api_error {
unique_mutex_lock lock(mtx_);
if (result_.has_value()) {
return result_.value();
}
notify_.wait(lock);
return result_.value();
}
} // namespace repertory

View File

@@ -1,317 +1,317 @@
/*
Copyright <2018-2023> <scott.e.graves@protonmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "file_manager/file_manager.hpp"
#include "app_config.hpp"
#include "file_manager/events.hpp"
#include "providers/i_provider.hpp"
#include "types/repertory.hpp"
#include "utils/encrypting_reader.hpp"
#include "utils/file_utils.hpp"
#include "utils/path_utils.hpp"
#include "utils/unix/unix_utils.hpp"
#include "utils/utils.hpp"
namespace repertory {
file_manager::ring_buffer_open_file::ring_buffer_open_file(
std::string buffer_directory, std::uint64_t chunk_size,
std::uint8_t chunk_timeout, filesystem_item fsi, i_provider &provider)
: ring_buffer_open_file(std::move(buffer_directory), chunk_size,
chunk_timeout, std::move(fsi), provider,
(1024ULL * 1024ULL * 1024ULL) / chunk_size) {}
file_manager::ring_buffer_open_file::ring_buffer_open_file(
std::string buffer_directory, std::uint64_t chunk_size,
std::uint8_t chunk_timeout, filesystem_item fsi, i_provider &provider,
std::size_t ring_size)
: open_file_base(chunk_size, chunk_timeout, fsi, provider),
ring_state_(ring_size),
total_chunks_(static_cast<std::size_t>(
utils::divide_with_ceiling(fsi.size, chunk_size_))) {
if ((ring_size % 2U) != 0U) {
throw std::runtime_error("ring size must be a multiple of 2");
}
if (ring_size < 4U) {
throw std::runtime_error("ring size must be greater than or equal to 4");
}
if (fsi.size < (ring_state_.size() * chunk_size)) {
throw std::runtime_error("file size is less than ring buffer size");
}
last_chunk_ = ring_state_.size() - 1U;
ring_state_.set(0U, ring_state_.size(), true);
buffer_directory = utils::path::absolute(buffer_directory);
if (not utils::file::create_full_directory_path(buffer_directory)) {
throw std::runtime_error("failed to create buffer directory|path|" +
buffer_directory + "|err|" +
std::to_string(utils::get_last_error_code()));
}
fsi_.source_path =
utils::path::combine(buffer_directory, {utils::create_uuid_string()});
auto res = native_file::create_or_open(fsi_.source_path, nf_);
if (res != api_error::success) {
throw std::runtime_error("failed to create buffer file|err|" +
std::to_string(utils::get_last_error_code()));
}
if (not nf_->truncate(ring_state_.size() * chunk_size)) {
nf_->close();
throw std::runtime_error("failed to resize buffer file|err|" +
std::to_string(utils::get_last_error_code()));
}
}
file_manager::ring_buffer_open_file::~ring_buffer_open_file() {
close();
nf_->close();
if (not utils::file::retry_delete_file(fsi_.source_path)) {
utils::error::raise_api_path_error(
__FUNCTION__, fsi_.api_path, fsi_.source_path,
utils::get_last_error_code(), "failed to delete file");
}
}
auto file_manager::file_manager::ring_buffer_open_file::download_chunk(
std::size_t chunk) -> api_error {
unique_mutex_lock chunk_lock(chunk_mtx_);
if (active_downloads_.find(chunk) != active_downloads_.end()) {
auto active_download = active_downloads_.at(chunk);
chunk_notify_.notify_all();
chunk_lock.unlock();
return active_download->wait();
}
if (ring_state_[chunk % ring_state_.size()]) {
auto active_download = std::make_shared<download>();
active_downloads_[chunk] = active_download;
ring_state_[chunk % ring_state_.size()] = false;
chunk_notify_.notify_all();
chunk_lock.unlock();
data_buffer buffer((chunk == (total_chunks_ - 1U)) ? last_chunk_size_
: chunk_size_);
stop_type stop_requested = !!ring_state_[chunk % ring_state_.size()];
auto res =
provider_.read_file_bytes(fsi_.api_path, buffer.size(),
chunk * chunk_size_, buffer, stop_requested);
if (res == api_error::success) {
res = do_io([&]() -> api_error {
std::size_t bytes_written{};
if (not nf_->write_bytes(buffer.data(), buffer.size(),
(chunk % ring_state_.size()) * chunk_size_,
bytes_written)) {
return api_error::os_error;
}
return api_error::success;
});
}
active_download->notify(res);
chunk_lock.lock();
active_downloads_.erase(chunk);
chunk_notify_.notify_all();
return res;
}
chunk_notify_.notify_all();
chunk_lock.unlock();
return api_error::success;
}
void file_manager::ring_buffer_open_file::forward(std::size_t count) {
mutex_lock chunk_lock(chunk_mtx_);
if ((current_chunk_ + count) > (total_chunks_ - 1U)) {
count = (total_chunks_ - 1U) - current_chunk_;
}
if ((current_chunk_ + count) <= last_chunk_) {
current_chunk_ += count;
} else {
const auto added = count - (last_chunk_ - current_chunk_);
if (added >= ring_state_.size()) {
ring_state_.set(0U, ring_state_.size(), true);
current_chunk_ += count;
first_chunk_ += added;
last_chunk_ =
std::min(total_chunks_ - 1U, first_chunk_ + ring_state_.size() - 1U);
} else {
for (std::size_t i = 0U; i < added; i++) {
ring_state_[(first_chunk_ + i) % ring_state_.size()] = true;
}
first_chunk_ += added;
current_chunk_ += count;
last_chunk_ =
std::min(total_chunks_ - 1U, first_chunk_ + ring_state_.size() - 1U);
}
}
chunk_notify_.notify_all();
}
auto file_manager::ring_buffer_open_file::get_read_state() const
-> boost::dynamic_bitset<> {
recur_mutex_lock file_lock(file_mtx_);
auto read_state = ring_state_;
return read_state.flip();
}
auto file_manager::ring_buffer_open_file::get_read_state(
std::size_t chunk) const -> bool {
recur_mutex_lock file_lock(file_mtx_);
return not ring_state_[chunk % ring_state_.size()];
}
auto file_manager::ring_buffer_open_file::is_download_complete() const -> bool {
return false;
}
auto file_manager::ring_buffer_open_file::native_operation(
const i_open_file::native_operation_callback &callback) -> api_error {
return do_io([&]() -> api_error { return callback(nf_->get_handle()); });
}
void file_manager::ring_buffer_open_file::reverse(std::size_t count) {
mutex_lock chunk_lock(chunk_mtx_);
if (current_chunk_ < count) {
count = current_chunk_;
}
if ((current_chunk_ - count) >= first_chunk_) {
current_chunk_ -= count;
} else {
const auto removed = count - (current_chunk_ - first_chunk_);
if (removed >= ring_state_.size()) {
ring_state_.set(0U, ring_state_.size(), true);
current_chunk_ -= count;
first_chunk_ = current_chunk_;
last_chunk_ =
std::min(total_chunks_ - 1U, first_chunk_ + ring_state_.size() - 1U);
} else {
for (std::size_t i = 0U; i < removed; i++) {
ring_state_[(last_chunk_ - i) % ring_state_.size()] = true;
}
first_chunk_ -= removed;
current_chunk_ -= count;
last_chunk_ =
std::min(total_chunks_ - 1U, first_chunk_ + ring_state_.size() - 1U);
}
}
chunk_notify_.notify_all();
}
auto file_manager::ring_buffer_open_file::read(std::size_t read_size,
std::uint64_t read_offset,
data_buffer &data) -> api_error {
if (fsi_.directory) {
return api_error::invalid_operation;
}
reset_timeout();
read_size = utils::calculate_read_size(fsi_.size, read_size, read_offset);
if (read_size == 0U) {
return api_error::success;
}
const auto start_chunk_index =
static_cast<std::size_t>(read_offset / chunk_size_);
read_offset = read_offset - (start_chunk_index * chunk_size_);
data_buffer buffer(chunk_size_);
auto res = api_error::success;
for (std::size_t chunk = start_chunk_index;
(res == api_error::success) && (read_size > 0U); chunk++) {
if (chunk > current_chunk_) {
forward(chunk - current_chunk_);
} else if (chunk < current_chunk_) {
reverse(current_chunk_ - chunk);
}
reset_timeout();
if ((res = download_chunk(chunk)) == api_error::success) {
const auto to_read = std::min(
static_cast<std::size_t>(chunk_size_ - read_offset), read_size);
res = do_io([this, &buffer, &chunk, &data, read_offset,
&to_read]() -> api_error {
std::size_t bytes_read{};
auto ret = nf_->read_bytes(buffer.data(), buffer.size(),
((chunk % ring_state_.size()) * chunk_size_),
bytes_read)
? api_error::success
: api_error::os_error;
if (ret == api_error::success) {
data.insert(data.end(), buffer.begin() + read_offset,
buffer.begin() + read_offset + to_read);
reset_timeout();
}
return ret;
});
read_offset = 0U;
read_size -= to_read;
}
}
return res;
}
void file_manager::ring_buffer_open_file::set(std::size_t first_chunk,
std::size_t current_chunk) {
mutex_lock chunk_lock(chunk_mtx_);
if (first_chunk >= total_chunks_) {
chunk_notify_.notify_all();
throw std::runtime_error("first chunk must be less than total chunks");
}
first_chunk_ = first_chunk;
last_chunk_ = first_chunk_ + ring_state_.size() - 1U;
if (current_chunk > last_chunk_) {
chunk_notify_.notify_all();
throw std::runtime_error(
"current chunk must be less than or equal to last chunk");
}
current_chunk_ = current_chunk;
ring_state_.set(0U, ring_state_.size(), false);
chunk_notify_.notify_all();
}
void file_manager::ring_buffer_open_file::set_api_path(
const std::string &api_path) {
mutex_lock chunk_lock(chunk_mtx_);
open_file_base::set_api_path(api_path);
chunk_notify_.notify_all();
}
} // namespace repertory
/*
Copyright <2018-2023> <scott.e.graves@protonmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "file_manager/file_manager.hpp"
#include "app_config.hpp"
#include "file_manager/events.hpp"
#include "providers/i_provider.hpp"
#include "types/repertory.hpp"
#include "utils/encrypting_reader.hpp"
#include "utils/file_utils.hpp"
#include "utils/path_utils.hpp"
#include "utils/unix/unix_utils.hpp"
#include "utils/utils.hpp"
namespace repertory {
file_manager::ring_buffer_open_file::ring_buffer_open_file(
std::string buffer_directory, std::uint64_t chunk_size,
std::uint8_t chunk_timeout, filesystem_item fsi, i_provider &provider)
: ring_buffer_open_file(std::move(buffer_directory), chunk_size,
chunk_timeout, std::move(fsi), provider,
(1024ULL * 1024ULL * 1024ULL) / chunk_size) {}
file_manager::ring_buffer_open_file::ring_buffer_open_file(
std::string buffer_directory, std::uint64_t chunk_size,
std::uint8_t chunk_timeout, filesystem_item fsi, i_provider &provider,
std::size_t ring_size)
: open_file_base(chunk_size, chunk_timeout, fsi, provider),
ring_state_(ring_size),
total_chunks_(static_cast<std::size_t>(
utils::divide_with_ceiling(fsi.size, chunk_size_))) {
if ((ring_size % 2U) != 0U) {
throw std::runtime_error("ring size must be a multiple of 2");
}
if (ring_size < 4U) {
throw std::runtime_error("ring size must be greater than or equal to 4");
}
if (fsi.size < (ring_state_.size() * chunk_size)) {
throw std::runtime_error("file size is less than ring buffer size");
}
last_chunk_ = ring_state_.size() - 1U;
ring_state_.set(0U, ring_state_.size(), true);
buffer_directory = utils::path::absolute(buffer_directory);
if (not utils::file::create_full_directory_path(buffer_directory)) {
throw std::runtime_error("failed to create buffer directory|path|" +
buffer_directory + "|err|" +
std::to_string(utils::get_last_error_code()));
}
fsi_.source_path =
utils::path::combine(buffer_directory, {utils::create_uuid_string()});
auto res = native_file::create_or_open(fsi_.source_path, nf_);
if (res != api_error::success) {
throw std::runtime_error("failed to create buffer file|err|" +
std::to_string(utils::get_last_error_code()));
}
if (not nf_->truncate(ring_state_.size() * chunk_size)) {
nf_->close();
throw std::runtime_error("failed to resize buffer file|err|" +
std::to_string(utils::get_last_error_code()));
}
}
file_manager::ring_buffer_open_file::~ring_buffer_open_file() {
close();
nf_->close();
if (not utils::file::retry_delete_file(fsi_.source_path)) {
utils::error::raise_api_path_error(
__FUNCTION__, fsi_.api_path, fsi_.source_path,
utils::get_last_error_code(), "failed to delete file");
}
}
auto file_manager::file_manager::ring_buffer_open_file::download_chunk(
std::size_t chunk) -> api_error {
unique_mutex_lock chunk_lock(chunk_mtx_);
if (active_downloads_.find(chunk) != active_downloads_.end()) {
auto active_download = active_downloads_.at(chunk);
chunk_notify_.notify_all();
chunk_lock.unlock();
return active_download->wait();
}
if (ring_state_[chunk % ring_state_.size()]) {
auto active_download = std::make_shared<download>();
active_downloads_[chunk] = active_download;
ring_state_[chunk % ring_state_.size()] = false;
chunk_notify_.notify_all();
chunk_lock.unlock();
data_buffer buffer((chunk == (total_chunks_ - 1U)) ? last_chunk_size_
: chunk_size_);
stop_type stop_requested = !!ring_state_[chunk % ring_state_.size()];
auto res =
provider_.read_file_bytes(fsi_.api_path, buffer.size(),
chunk * chunk_size_, buffer, stop_requested);
if (res == api_error::success) {
res = do_io([&]() -> api_error {
std::size_t bytes_written{};
if (not nf_->write_bytes(buffer.data(), buffer.size(),
(chunk % ring_state_.size()) * chunk_size_,
bytes_written)) {
return api_error::os_error;
}
return api_error::success;
});
}
active_download->notify(res);
chunk_lock.lock();
active_downloads_.erase(chunk);
chunk_notify_.notify_all();
return res;
}
chunk_notify_.notify_all();
chunk_lock.unlock();
return api_error::success;
}
void file_manager::ring_buffer_open_file::forward(std::size_t count) {
mutex_lock chunk_lock(chunk_mtx_);
if ((current_chunk_ + count) > (total_chunks_ - 1U)) {
count = (total_chunks_ - 1U) - current_chunk_;
}
if ((current_chunk_ + count) <= last_chunk_) {
current_chunk_ += count;
} else {
const auto added = count - (last_chunk_ - current_chunk_);
if (added >= ring_state_.size()) {
ring_state_.set(0U, ring_state_.size(), true);
current_chunk_ += count;
first_chunk_ += added;
last_chunk_ =
std::min(total_chunks_ - 1U, first_chunk_ + ring_state_.size() - 1U);
} else {
for (std::size_t i = 0U; i < added; i++) {
ring_state_[(first_chunk_ + i) % ring_state_.size()] = true;
}
first_chunk_ += added;
current_chunk_ += count;
last_chunk_ =
std::min(total_chunks_ - 1U, first_chunk_ + ring_state_.size() - 1U);
}
}
chunk_notify_.notify_all();
}
auto file_manager::ring_buffer_open_file::get_read_state() const
-> boost::dynamic_bitset<> {
recur_mutex_lock file_lock(file_mtx_);
auto read_state = ring_state_;
return read_state.flip();
}
auto file_manager::ring_buffer_open_file::get_read_state(
std::size_t chunk) const -> bool {
recur_mutex_lock file_lock(file_mtx_);
return not ring_state_[chunk % ring_state_.size()];
}
auto file_manager::ring_buffer_open_file::is_download_complete() const -> bool {
return false;
}
auto file_manager::ring_buffer_open_file::native_operation(
const i_open_file::native_operation_callback &callback) -> api_error {
return do_io([&]() -> api_error { return callback(nf_->get_handle()); });
}
void file_manager::ring_buffer_open_file::reverse(std::size_t count) {
mutex_lock chunk_lock(chunk_mtx_);
if (current_chunk_ < count) {
count = current_chunk_;
}
if ((current_chunk_ - count) >= first_chunk_) {
current_chunk_ -= count;
} else {
const auto removed = count - (current_chunk_ - first_chunk_);
if (removed >= ring_state_.size()) {
ring_state_.set(0U, ring_state_.size(), true);
current_chunk_ -= count;
first_chunk_ = current_chunk_;
last_chunk_ =
std::min(total_chunks_ - 1U, first_chunk_ + ring_state_.size() - 1U);
} else {
for (std::size_t i = 0U; i < removed; i++) {
ring_state_[(last_chunk_ - i) % ring_state_.size()] = true;
}
first_chunk_ -= removed;
current_chunk_ -= count;
last_chunk_ =
std::min(total_chunks_ - 1U, first_chunk_ + ring_state_.size() - 1U);
}
}
chunk_notify_.notify_all();
}
auto file_manager::ring_buffer_open_file::read(std::size_t read_size,
std::uint64_t read_offset,
data_buffer &data) -> api_error {
if (fsi_.directory) {
return api_error::invalid_operation;
}
reset_timeout();
read_size = utils::calculate_read_size(fsi_.size, read_size, read_offset);
if (read_size == 0U) {
return api_error::success;
}
const auto start_chunk_index =
static_cast<std::size_t>(read_offset / chunk_size_);
read_offset = read_offset - (start_chunk_index * chunk_size_);
data_buffer buffer(chunk_size_);
auto res = api_error::success;
for (std::size_t chunk = start_chunk_index;
(res == api_error::success) && (read_size > 0U); chunk++) {
if (chunk > current_chunk_) {
forward(chunk - current_chunk_);
} else if (chunk < current_chunk_) {
reverse(current_chunk_ - chunk);
}
reset_timeout();
if ((res = download_chunk(chunk)) == api_error::success) {
const auto to_read = std::min(
static_cast<std::size_t>(chunk_size_ - read_offset), read_size);
res = do_io([this, &buffer, &chunk, &data, read_offset,
&to_read]() -> api_error {
std::size_t bytes_read{};
auto ret = nf_->read_bytes(buffer.data(), buffer.size(),
((chunk % ring_state_.size()) * chunk_size_),
bytes_read)
? api_error::success
: api_error::os_error;
if (ret == api_error::success) {
data.insert(data.end(), buffer.begin() + read_offset,
buffer.begin() + read_offset + to_read);
reset_timeout();
}
return ret;
});
read_offset = 0U;
read_size -= to_read;
}
}
return res;
}
void file_manager::ring_buffer_open_file::set(std::size_t first_chunk,
std::size_t current_chunk) {
mutex_lock chunk_lock(chunk_mtx_);
if (first_chunk >= total_chunks_) {
chunk_notify_.notify_all();
throw std::runtime_error("first chunk must be less than total chunks");
}
first_chunk_ = first_chunk;
last_chunk_ = first_chunk_ + ring_state_.size() - 1U;
if (current_chunk > last_chunk_) {
chunk_notify_.notify_all();
throw std::runtime_error(
"current chunk must be less than or equal to last chunk");
}
current_chunk_ = current_chunk;
ring_state_.set(0U, ring_state_.size(), false);
chunk_notify_.notify_all();
}
void file_manager::ring_buffer_open_file::set_api_path(
const std::string &api_path) {
mutex_lock chunk_lock(chunk_mtx_);
open_file_base::set_api_path(api_path);
chunk_notify_.notify_all();
}
} // namespace repertory

View File

@@ -1,62 +1,62 @@
/*
Copyright <2018-2023> <scott.e.graves@protonmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "file_manager/file_manager.hpp"
#include "providers/i_provider.hpp"
#include "utils/error_utils.hpp"
#include "utils/file_utils.hpp"
#include "utils/unix/unix_utils.hpp"
namespace repertory {
file_manager::upload::upload(filesystem_item fsi, i_provider &provider)
: fsi_(fsi), provider_(provider) {
thread_ =
std::make_unique<std::thread>(std::bind(&upload::upload_thread, this));
}
file_manager::upload::~upload() {
stop();
thread_->join();
thread_.reset();
}
void file_manager::upload::cancel() {
cancelled_ = true;
stop();
}
void file_manager::upload::stop() { stop_requested_ = true; }
void file_manager::upload::upload_thread() {
error_ =
provider_.upload_file(fsi_.api_path, fsi_.source_path, stop_requested_);
if (not utils::file::reset_modified_time(fsi_.source_path)) {
utils::error::raise_api_path_error(
__FUNCTION__, fsi_.api_path, fsi_.source_path,
utils::get_last_error_code(), "failed to reset modified time");
}
event_system::instance().raise<file_upload_completed>(
get_api_path(), get_source_path(), get_api_error(), cancelled_);
}
} // namespace repertory
/*
Copyright <2018-2023> <scott.e.graves@protonmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "file_manager/file_manager.hpp"
#include "providers/i_provider.hpp"
#include "utils/error_utils.hpp"
#include "utils/file_utils.hpp"
#include "utils/unix/unix_utils.hpp"
namespace repertory {
file_manager::upload::upload(filesystem_item fsi, i_provider &provider)
: fsi_(fsi), provider_(provider) {
thread_ =
std::make_unique<std::thread>(std::bind(&upload::upload_thread, this));
}
file_manager::upload::~upload() {
stop();
thread_->join();
thread_.reset();
}
void file_manager::upload::cancel() {
cancelled_ = true;
stop();
}
void file_manager::upload::stop() { stop_requested_ = true; }
void file_manager::upload::upload_thread() {
error_ =
provider_.upload_file(fsi_.api_path, fsi_.source_path, stop_requested_);
if (not utils::file::reset_modified_time(fsi_.source_path)) {
utils::error::raise_api_path_error(
__FUNCTION__, fsi_.api_path, fsi_.source_path,
utils::get_last_error_code(), "failed to reset modified time");
}
event_system::instance().raise<file_upload_completed>(
get_api_path(), get_source_path(), get_api_error(), cancelled_);
}
} // namespace repertory

View File

@@ -1,164 +1,164 @@
/*
Copyright <2018-2023> <scott.e.graves@protonmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#ifdef REPERTORY_TESTING
#include "events/event_system.hpp"
#include "test_common.hpp"
#include "utils/string_utils.hpp"
#else
#include "cli/actions.hpp"
#include "types/repertory.hpp"
#endif
#include "utils/cli_utils.hpp"
#include "utils/polling.hpp"
using namespace repertory;
#if defined(REPERTORY_TESTING) && defined(_WIN32)
std::size_t PROVIDER_INDEX{0U};
#endif
auto main(int argc, char **argv) -> int {
repertory_init();
std::vector<const char *> args(argv, argv + argc);
#ifdef REPERTORY_TESTING
#ifdef _WIN32
if (utils::cli::has_option(args, "--provider_index")) {
PROVIDER_INDEX = static_cast<std::size_t>(
utils::string::to_uint64(
utils::cli::parse_option(args, "--provider_index", 1U)[0U]) +
1U);
}
#endif // _WIN32
InitGoogleTest(&argc, argv);
auto ret = RUN_ALL_TESTS();
delete_generated_files();
#else
if (argc == 1) {
args.push_back("-h");
}
auto prov = utils::cli::get_provider_type_from_args(args);
std::string data_directory;
auto res = utils::cli::parse_string_option(
args, utils::cli::options::data_directory_option, data_directory);
std::string password;
res = (res == exit_code::success)
? utils::cli::parse_string_option(
args, utils::cli::options::password_option, password)
: res;
std::string user;
res = (res == exit_code::success)
? utils::cli::parse_string_option(
args, utils::cli::options::user_option, user)
: res;
std::string remote_host;
std::uint16_t remote_port{};
std::string unique_id;
if ((res == exit_code::success) && (prov == provider_type::remote)) {
std::string data;
if ((res = utils::cli::parse_string_option(
args, utils::cli::options::remote_mount_option, data)) ==
exit_code::success) {
const auto parts = utils::string::split(data, ':');
if (parts.size() != 2) {
std::cerr << "Invalid syntax for host/port '-rm "
"host:port,--remote_mount host:port'"
<< std::endl;
res = exit_code::invalid_syntax;
} else {
unique_id = parts[0U] + ':' + parts[1U];
remote_host = parts[0U];
try {
remote_port = utils::string::to_uint16(parts[1U]);
data_directory = utils::path::combine(
data_directory.empty() ? app_config::default_data_directory(prov)
: data_directory,
{utils::string::replace_copy(unique_id, ':', '_')});
} catch (const std::exception &e) {
std::cerr << (e.what() == nullptr ? "Unable to parse port" : e.what())
<< std::endl;
res = exit_code::invalid_syntax;
}
}
}
}
#ifdef REPERTORY_ENABLE_S3
if ((res == exit_code::success) && (prov == provider_type::s3)) {
std::string data;
if ((res = utils::cli::parse_string_option(args,
utils::cli::options::name_option,
data)) == exit_code::success) {
unique_id = utils::string::trim(data);
if (unique_id.empty()) {
std::cerr << "Name of S3 instance not provided" << std::endl;
res = exit_code::invalid_syntax;
} else {
data_directory = utils::path::absolute(
data_directory.empty()
? utils::path::combine(app_config::default_data_directory(prov),
{unique_id})
: data_directory);
}
}
}
#endif // REPERTORY_ENABLE_S3
int mount_result{};
if (res == exit_code::success) {
if (utils::cli::has_option(args, utils::cli::options::help_option)) {
cli::actions::help<repertory_drive>(args);
} else if (utils::cli::has_option(args,
utils::cli::options::version_option)) {
cli::actions::version<repertory_drive>(args);
} else {
res = exit_code::option_not_found;
for (std::size_t idx = 0U;
(res == exit_code::option_not_found) &&
(idx < utils::cli::options::option_list.size());
idx++) {
res = cli::actions::perform_action(
utils::cli::options::option_list[idx], args, data_directory, prov,
unique_id, user, password);
}
if (res == exit_code::option_not_found) {
res = cli::actions::mount(args, data_directory, mount_result, prov,
remote_host, remote_port, unique_id);
}
}
}
auto ret =
((res == exit_code::mount_result) ? mount_result
: static_cast<std::int32_t>(res));
#endif
repertory_shutdown();
return ret;
}
/*
Copyright <2018-2023> <scott.e.graves@protonmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#ifdef REPERTORY_TESTING
#include "events/event_system.hpp"
#include "test_common.hpp"
#include "utils/string_utils.hpp"
#else
#include "cli/actions.hpp"
#include "types/repertory.hpp"
#endif
#include "utils/cli_utils.hpp"
#include "utils/polling.hpp"
using namespace repertory;
#if defined(REPERTORY_TESTING) && defined(_WIN32)
std::size_t PROVIDER_INDEX{0U};
#endif
auto main(int argc, char **argv) -> int {
repertory_init();
std::vector<const char *> args(argv, argv + argc);
#ifdef REPERTORY_TESTING
#ifdef _WIN32
if (utils::cli::has_option(args, "--provider_index")) {
PROVIDER_INDEX = static_cast<std::size_t>(
utils::string::to_uint64(
utils::cli::parse_option(args, "--provider_index", 1U)[0U]) +
1U);
}
#endif // _WIN32
InitGoogleTest(&argc, argv);
auto ret = RUN_ALL_TESTS();
delete_generated_files();
#else
if (argc == 1) {
args.push_back("-h");
}
auto prov = utils::cli::get_provider_type_from_args(args);
std::string data_directory;
auto res = utils::cli::parse_string_option(
args, utils::cli::options::data_directory_option, data_directory);
std::string password;
res = (res == exit_code::success)
? utils::cli::parse_string_option(
args, utils::cli::options::password_option, password)
: res;
std::string user;
res = (res == exit_code::success)
? utils::cli::parse_string_option(
args, utils::cli::options::user_option, user)
: res;
std::string remote_host;
std::uint16_t remote_port{};
std::string unique_id;
if ((res == exit_code::success) && (prov == provider_type::remote)) {
std::string data;
if ((res = utils::cli::parse_string_option(
args, utils::cli::options::remote_mount_option, data)) ==
exit_code::success) {
const auto parts = utils::string::split(data, ':');
if (parts.size() != 2) {
std::cerr << "Invalid syntax for host/port '-rm "
"host:port,--remote_mount host:port'"
<< std::endl;
res = exit_code::invalid_syntax;
} else {
unique_id = parts[0U] + ':' + parts[1U];
remote_host = parts[0U];
try {
remote_port = utils::string::to_uint16(parts[1U]);
data_directory = utils::path::combine(
data_directory.empty() ? app_config::default_data_directory(prov)
: data_directory,
{utils::string::replace_copy(unique_id, ':', '_')});
} catch (const std::exception &e) {
std::cerr << (e.what() == nullptr ? "Unable to parse port" : e.what())
<< std::endl;
res = exit_code::invalid_syntax;
}
}
}
}
#ifdef REPERTORY_ENABLE_S3
if ((res == exit_code::success) && (prov == provider_type::s3)) {
std::string data;
if ((res = utils::cli::parse_string_option(args,
utils::cli::options::name_option,
data)) == exit_code::success) {
unique_id = utils::string::trim(data);
if (unique_id.empty()) {
std::cerr << "Name of S3 instance not provided" << std::endl;
res = exit_code::invalid_syntax;
} else {
data_directory = utils::path::absolute(
data_directory.empty()
? utils::path::combine(app_config::default_data_directory(prov),
{unique_id})
: data_directory);
}
}
}
#endif // REPERTORY_ENABLE_S3
int mount_result{};
if (res == exit_code::success) {
if (utils::cli::has_option(args, utils::cli::options::help_option)) {
cli::actions::help<repertory_drive>(args);
} else if (utils::cli::has_option(args,
utils::cli::options::version_option)) {
cli::actions::version<repertory_drive>(args);
} else {
res = exit_code::option_not_found;
for (std::size_t idx = 0U;
(res == exit_code::option_not_found) &&
(idx < utils::cli::options::option_list.size());
idx++) {
res = cli::actions::perform_action(
utils::cli::options::option_list[idx], args, data_directory, prov,
unique_id, user, password);
}
if (res == exit_code::option_not_found) {
res = cli::actions::mount(args, data_directory, mount_result, prov,
remote_host, remote_port, unique_id);
}
}
}
auto ret =
((res == exit_code::mount_result) ? mount_result
: static_cast<std::int32_t>(res));
#endif
repertory_shutdown();
return ret;
}

View File

@@ -1,233 +1,233 @@
/*
Copyright <2018-2023> <scott.e.graves@protonmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#ifndef _WIN32
#include "platform/unix_platform.hpp"
#include "app_config.hpp"
#include "providers/i_provider.hpp"
#include "types/startup_exception.hpp"
#include "utils/error_utils.hpp"
#include "utils/file_utils.hpp"
#include "utils/path_utils.hpp"
#include "utils/unix/unix_utils.hpp"
namespace repertory {
lock_data::lock_data(const provider_type &pt, std::string unique_id /*= ""*/)
: pt_(pt),
unique_id_(std::move(unique_id)),
mutex_id_("repertory2_" + app_config::get_provider_name(pt) + "_" +
unique_id_) {
lock_fd_ = open(get_lock_file().c_str(), O_CREAT | O_RDWR, S_IWUSR | S_IRUSR);
}
lock_data::lock_data()
: pt_(provider_type::sia), unique_id_(""), mutex_id_(""), lock_fd_(-1) {}
lock_data::~lock_data() {
if (lock_fd_ != -1) {
if (lock_status_ == 0) {
unlink(get_lock_file().c_str());
flock(lock_fd_, LOCK_UN);
}
close(lock_fd_);
}
}
auto lock_data::get_lock_data_file() -> std::string {
const auto dir = get_state_directory();
if (not utils::file::create_full_directory_path(dir)) {
throw startup_exception("failed to create directory|sp|" + dir + "|err|" +
std::to_string(utils::get_last_error_code()));
}
return utils::path::combine(
dir, {"mountstate_" + std::to_string(getuid()) + ".json"});
}
auto lock_data::get_lock_file() -> std::string {
const auto dir = get_state_directory();
if (not utils::file::create_full_directory_path(dir)) {
throw startup_exception("failed to create directory|sp|" + dir + "|err|" +
std::to_string(utils::get_last_error_code()));
}
return utils::path::combine(dir,
{mutex_id_ + "_" + std::to_string(getuid())});
}
auto lock_data::get_mount_state(json &mount_state) -> bool {
auto ret = false;
auto fd =
open(get_lock_data_file().c_str(), O_CREAT | O_RDWR, S_IWUSR | S_IRUSR);
if (fd != -1) {
if (wait_for_lock(fd) == 0) {
ret = utils::file::read_json_file(get_lock_data_file(), mount_state);
flock(fd, LOCK_UN);
}
close(fd);
}
return ret;
}
auto lock_data::get_state_directory() -> std::string {
#ifdef __APPLE__
return utils::path::resolve("~/Library/Application Support/" +
std::string(REPERTORY_DATA_NAME) + "/state");
#else
return utils::path::resolve("~/.local/" + std::string(REPERTORY_DATA_NAME) +
"/state");
#endif
}
auto lock_data::grab_lock(std::uint8_t retry_count) -> lock_result {
if (lock_fd_ == -1) {
return lock_result::failure;
}
lock_status_ = wait_for_lock(lock_fd_, retry_count);
switch (lock_status_) {
case 0:
if (not set_mount_state(false, "", -1)) {
utils::error::raise_error(__FUNCTION__, "failed to set mount state");
}
return lock_result::success;
case EWOULDBLOCK:
return lock_result::locked;
default:
return lock_result::failure;
}
}
auto lock_data::set_mount_state(bool active, const std::string &mount_location,
int pid) -> bool {
auto ret = false;
auto fd =
open(get_lock_data_file().c_str(), O_CREAT | O_RDWR, S_IWUSR | S_IRUSR);
if (fd != -1) {
if (wait_for_lock(fd) == 0) {
const auto mount_id =
app_config::get_provider_display_name(pt_) + unique_id_;
json mount_state;
if (not utils::file::read_json_file(get_lock_data_file(), mount_state)) {
utils::error::raise_error(__FUNCTION__,
"failed to read mount state file|sp|" +
get_lock_file());
}
if ((mount_state.find(mount_id) == mount_state.end()) ||
(mount_state[mount_id].find("Active") ==
mount_state[mount_id].end()) ||
(mount_state[mount_id]["Active"].get<bool>() != active) ||
(active && ((mount_state[mount_id].find("Location") ==
mount_state[mount_id].end()) ||
(mount_state[mount_id]["Location"].get<std::string>() !=
mount_location)))) {
const auto lines = utils::file::read_file_lines(get_lock_data_file());
const auto txt =
std::accumulate(lines.begin(), lines.end(), std::string(),
[](std::string s, const std::string &s2) {
return std::move(s) + s2;
});
auto json = json::parse(txt.empty() ? "{}" : txt);
json[mount_id] = {{"Active", active},
{"Location", active ? mount_location : ""},
{"PID", active ? pid : -1}};
ret = utils::file::write_json_file(get_lock_data_file(), json);
} else {
ret = true;
}
flock(fd, LOCK_UN);
}
close(fd);
}
return ret;
}
auto lock_data::wait_for_lock(int fd, std::uint8_t retry_count) -> int {
auto lock_status = EWOULDBLOCK;
std::int16_t remain = retry_count * 100u;
while ((remain > 0) && (lock_status == EWOULDBLOCK)) {
if ((lock_status = flock(fd, LOCK_EX | LOCK_NB)) == -1) {
if ((lock_status = errno) == EWOULDBLOCK) {
const auto sleep_ms = utils::random_between(
std::int16_t(1), std::min(remain, std::int16_t(100)));
std::this_thread::sleep_for(std::chrono::milliseconds(sleep_ms));
remain -= sleep_ms;
}
}
}
return lock_status;
}
auto create_meta_attributes(
std::uint64_t accessed_date, std::uint32_t attributes,
std::uint64_t changed_date, std::uint64_t creation_date, bool directory,
std::uint32_t gid, const std::string &key, std::uint32_t mode,
std::uint64_t modified_date, std::uint32_t osx_backup,
std::uint32_t osx_flags, std::uint64_t size, const std::string &source_path,
std::uint32_t uid, std::uint64_t written_date) -> api_meta_map {
return {
{META_ACCESSED, std::to_string(accessed_date)},
{META_ATTRIBUTES, std::to_string(attributes)},
{META_BACKUP, std::to_string(osx_backup)},
{META_CHANGED, std::to_string(changed_date)},
{META_CREATION, std::to_string(creation_date)},
{META_DIRECTORY, utils::string::from_bool(directory)},
{META_GID, std::to_string(gid)},
{META_KEY, key},
{META_MODE, std::to_string(mode)},
{META_MODIFIED, std::to_string(modified_date)},
{META_OSXFLAGS, std::to_string(osx_flags)},
{META_PINNED, "0"},
{META_SOURCE, source_path},
{META_SIZE, std::to_string(size)},
{META_UID, std::to_string(uid)},
{META_WRITTEN, std::to_string(written_date)},
};
}
auto provider_meta_handler(i_provider &provider, bool directory,
const api_file &file) -> api_error {
const auto meta = create_meta_attributes(
file.accessed_date,
directory ? FILE_ATTRIBUTE_DIRECTORY
: FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_ARCHIVE,
file.changed_date, file.creation_date, directory, getgid(), file.key,
directory ? S_IFDIR | S_IRUSR | S_IWUSR | S_IXUSR
: S_IFREG | S_IRUSR | S_IWUSR,
file.modified_date, 0u, 0u, file.file_size, file.source_path, getuid(),
file.modified_date);
auto res = provider.set_item_meta(file.api_path, meta);
if (res == api_error::success) {
event_system::instance().raise<filesystem_item_added>(
file.api_path, file.api_parent, directory);
}
return res;
}
} // namespace repertory
#endif //_WIN32
/*
Copyright <2018-2023> <scott.e.graves@protonmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#ifndef _WIN32
#include "platform/unix_platform.hpp"
#include "app_config.hpp"
#include "providers/i_provider.hpp"
#include "types/startup_exception.hpp"
#include "utils/error_utils.hpp"
#include "utils/file_utils.hpp"
#include "utils/path_utils.hpp"
#include "utils/unix/unix_utils.hpp"
namespace repertory {
lock_data::lock_data(const provider_type &pt, std::string unique_id /*= ""*/)
: pt_(pt),
unique_id_(std::move(unique_id)),
mutex_id_("repertory2_" + app_config::get_provider_name(pt) + "_" +
unique_id_) {
lock_fd_ = open(get_lock_file().c_str(), O_CREAT | O_RDWR, S_IWUSR | S_IRUSR);
}
lock_data::lock_data()
: pt_(provider_type::sia), unique_id_(""), mutex_id_(""), lock_fd_(-1) {}
lock_data::~lock_data() {
if (lock_fd_ != -1) {
if (lock_status_ == 0) {
unlink(get_lock_file().c_str());
flock(lock_fd_, LOCK_UN);
}
close(lock_fd_);
}
}
auto lock_data::get_lock_data_file() -> std::string {
const auto dir = get_state_directory();
if (not utils::file::create_full_directory_path(dir)) {
throw startup_exception("failed to create directory|sp|" + dir + "|err|" +
std::to_string(utils::get_last_error_code()));
}
return utils::path::combine(
dir, {"mountstate_" + std::to_string(getuid()) + ".json"});
}
auto lock_data::get_lock_file() -> std::string {
const auto dir = get_state_directory();
if (not utils::file::create_full_directory_path(dir)) {
throw startup_exception("failed to create directory|sp|" + dir + "|err|" +
std::to_string(utils::get_last_error_code()));
}
return utils::path::combine(dir,
{mutex_id_ + "_" + std::to_string(getuid())});
}
auto lock_data::get_mount_state(json &mount_state) -> bool {
auto ret = false;
auto fd =
open(get_lock_data_file().c_str(), O_CREAT | O_RDWR, S_IWUSR | S_IRUSR);
if (fd != -1) {
if (wait_for_lock(fd) == 0) {
ret = utils::file::read_json_file(get_lock_data_file(), mount_state);
flock(fd, LOCK_UN);
}
close(fd);
}
return ret;
}
auto lock_data::get_state_directory() -> std::string {
#ifdef __APPLE__
return utils::path::resolve("~/Library/Application Support/" +
std::string(REPERTORY_DATA_NAME) + "/state");
#else
return utils::path::resolve("~/.local/" + std::string(REPERTORY_DATA_NAME) +
"/state");
#endif
}
auto lock_data::grab_lock(std::uint8_t retry_count) -> lock_result {
if (lock_fd_ == -1) {
return lock_result::failure;
}
lock_status_ = wait_for_lock(lock_fd_, retry_count);
switch (lock_status_) {
case 0:
if (not set_mount_state(false, "", -1)) {
utils::error::raise_error(__FUNCTION__, "failed to set mount state");
}
return lock_result::success;
case EWOULDBLOCK:
return lock_result::locked;
default:
return lock_result::failure;
}
}
auto lock_data::set_mount_state(bool active, const std::string &mount_location,
int pid) -> bool {
auto ret = false;
auto fd =
open(get_lock_data_file().c_str(), O_CREAT | O_RDWR, S_IWUSR | S_IRUSR);
if (fd != -1) {
if (wait_for_lock(fd) == 0) {
const auto mount_id =
app_config::get_provider_display_name(pt_) + unique_id_;
json mount_state;
if (not utils::file::read_json_file(get_lock_data_file(), mount_state)) {
utils::error::raise_error(__FUNCTION__,
"failed to read mount state file|sp|" +
get_lock_file());
}
if ((mount_state.find(mount_id) == mount_state.end()) ||
(mount_state[mount_id].find("Active") ==
mount_state[mount_id].end()) ||
(mount_state[mount_id]["Active"].get<bool>() != active) ||
(active && ((mount_state[mount_id].find("Location") ==
mount_state[mount_id].end()) ||
(mount_state[mount_id]["Location"].get<std::string>() !=
mount_location)))) {
const auto lines = utils::file::read_file_lines(get_lock_data_file());
const auto txt =
std::accumulate(lines.begin(), lines.end(), std::string(),
[](std::string s, const std::string &s2) {
return std::move(s) + s2;
});
auto json = json::parse(txt.empty() ? "{}" : txt);
json[mount_id] = {{"Active", active},
{"Location", active ? mount_location : ""},
{"PID", active ? pid : -1}};
ret = utils::file::write_json_file(get_lock_data_file(), json);
} else {
ret = true;
}
flock(fd, LOCK_UN);
}
close(fd);
}
return ret;
}
auto lock_data::wait_for_lock(int fd, std::uint8_t retry_count) -> int {
auto lock_status = EWOULDBLOCK;
std::int16_t remain = retry_count * 100u;
while ((remain > 0) && (lock_status == EWOULDBLOCK)) {
if ((lock_status = flock(fd, LOCK_EX | LOCK_NB)) == -1) {
if ((lock_status = errno) == EWOULDBLOCK) {
const auto sleep_ms = utils::random_between(
std::int16_t(1), std::min(remain, std::int16_t(100)));
std::this_thread::sleep_for(std::chrono::milliseconds(sleep_ms));
remain -= sleep_ms;
}
}
}
return lock_status;
}
auto create_meta_attributes(
std::uint64_t accessed_date, std::uint32_t attributes,
std::uint64_t changed_date, std::uint64_t creation_date, bool directory,
std::uint32_t gid, const std::string &key, std::uint32_t mode,
std::uint64_t modified_date, std::uint32_t osx_backup,
std::uint32_t osx_flags, std::uint64_t size, const std::string &source_path,
std::uint32_t uid, std::uint64_t written_date) -> api_meta_map {
return {
{META_ACCESSED, std::to_string(accessed_date)},
{META_ATTRIBUTES, std::to_string(attributes)},
{META_BACKUP, std::to_string(osx_backup)},
{META_CHANGED, std::to_string(changed_date)},
{META_CREATION, std::to_string(creation_date)},
{META_DIRECTORY, utils::string::from_bool(directory)},
{META_GID, std::to_string(gid)},
{META_KEY, key},
{META_MODE, std::to_string(mode)},
{META_MODIFIED, std::to_string(modified_date)},
{META_OSXFLAGS, std::to_string(osx_flags)},
{META_PINNED, "0"},
{META_SOURCE, source_path},
{META_SIZE, std::to_string(size)},
{META_UID, std::to_string(uid)},
{META_WRITTEN, std::to_string(written_date)},
};
}
auto provider_meta_handler(i_provider &provider, bool directory,
const api_file &file) -> api_error {
const auto meta = create_meta_attributes(
file.accessed_date,
directory ? FILE_ATTRIBUTE_DIRECTORY
: FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_ARCHIVE,
file.changed_date, file.creation_date, directory, getgid(), file.key,
directory ? S_IFDIR | S_IRUSR | S_IWUSR | S_IXUSR
: S_IFREG | S_IRUSR | S_IWUSR,
file.modified_date, 0u, 0u, file.file_size, file.source_path, getuid(),
file.modified_date);
auto res = provider.set_item_meta(file.api_path, meta);
if (res == api_error::success) {
event_system::instance().raise<filesystem_item_added>(
file.api_path, file.api_parent, directory);
}
return res;
}
} // namespace repertory
#endif //_WIN32

View File

@@ -1,212 +1,212 @@
/*
Copyright <2018-2023> <scott.e.graves@protonmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#ifdef _WIN32
#include "platform/win32_platform.hpp"
#include "providers/i_provider.hpp"
#include "utils/error_utils.hpp"
namespace repertory {
auto lock_data::get_mount_state(const provider_type & /*pt*/, json &mount_state)
-> bool {
const auto ret = get_mount_state(mount_state);
if (ret) {
const auto mount_id =
app_config::get_provider_display_name(pt_) + unique_id_;
mount_state = mount_state[mount_id].empty()
? json({{"Active", false}, {"Location", ""}, {"PID", -1}})
: mount_state[mount_id];
}
return ret;
}
auto lock_data::get_mount_state(json &mount_state) -> bool {
HKEY key;
auto ret = !::RegCreateKeyEx(
HKEY_CURRENT_USER,
("SOFTWARE\\" + std::string(REPERTORY_DATA_NAME) + "\\Mounts").c_str(), 0,
nullptr, 0, KEY_ALL_ACCESS, nullptr, &key, nullptr);
if (ret) {
DWORD i = 0u;
DWORD data_size = 0u;
std::string name;
name.resize(32767u);
auto name_size = static_cast<DWORD>(name.size());
while (ret &&
(::RegEnumValue(key, i, &name[0], &name_size, nullptr, nullptr,
nullptr, &data_size) == ERROR_SUCCESS)) {
std::string data;
data.resize(data_size);
name_size++;
if ((ret = !::RegEnumValue(key, i++, &name[0], &name_size, nullptr,
nullptr, reinterpret_cast<LPBYTE>(&data[0]),
&data_size))) {
mount_state[name.c_str()] = json::parse(data);
name_size = static_cast<DWORD>(name.size());
data_size = 0u;
}
}
::RegCloseKey(key);
}
return ret;
}
auto lock_data::grab_lock(std::uint8_t retry_count) -> lock_result {
auto ret = lock_result::success;
if (mutex_handle_ == INVALID_HANDLE_VALUE) {
ret = lock_result::failure;
} else {
for (auto i = 0;
(i <= retry_count) && ((mutex_state_ = ::WaitForSingleObject(
mutex_handle_, 100)) == WAIT_TIMEOUT);
i++) {
}
switch (mutex_state_) {
case WAIT_OBJECT_0: {
ret = lock_result::success;
auto should_reset = true;
json mount_state;
if (get_mount_state(pt_, mount_state)) {
if (mount_state["Active"].get<bool>() &&
mount_state["Location"] == "elevating") {
should_reset = false;
}
}
if (should_reset) {
if (not set_mount_state(false, "", -1)) {
utils::error::raise_error(__FUNCTION__, "failed to set mount state");
}
}
} break;
case WAIT_TIMEOUT:
ret = lock_result::locked;
break;
default:
ret = lock_result::failure;
break;
}
}
return ret;
}
void lock_data::release() {
if (mutex_handle_ != INVALID_HANDLE_VALUE) {
if ((mutex_state_ == WAIT_OBJECT_0) || (mutex_state_ == WAIT_ABANDONED)) {
::ReleaseMutex(mutex_handle_);
}
::CloseHandle(mutex_handle_);
mutex_handle_ = INVALID_HANDLE_VALUE;
}
}
auto lock_data::set_mount_state(bool active, const std::string &mount_location,
const std::int64_t &pid) -> bool {
auto ret = false;
if (mutex_handle_ != INVALID_HANDLE_VALUE) {
const auto mount_id =
app_config::get_provider_display_name(pt_) + unique_id_;
json mount_state;
[[maybe_unused]] auto success = get_mount_state(mount_state);
if ((mount_state.find(mount_id) == mount_state.end()) ||
(mount_state[mount_id].find("Active") == mount_state[mount_id].end()) ||
(mount_state[mount_id]["Active"].get<bool>() != active) ||
(active && ((mount_state[mount_id].find("Location") ==
mount_state[mount_id].end()) ||
(mount_state[mount_id]["Location"].get<std::string>() !=
mount_location)))) {
HKEY key;
if ((ret = !::RegCreateKeyEx(
HKEY_CURRENT_USER,
("SOFTWARE\\" + std::string(REPERTORY_DATA_NAME) + "\\Mounts")
.c_str(),
0, nullptr, 0, KEY_ALL_ACCESS, nullptr, &key, nullptr))) {
const auto str = json({{"Active", active},
{"Location", active ? mount_location : ""},
{"PID", active ? pid : -1}})
.dump(0);
ret = !::RegSetValueEx(key, &mount_id[0], 0, REG_SZ,
reinterpret_cast<const BYTE *>(&str[0]),
static_cast<DWORD>(str.size()));
::RegCloseKey(key);
}
} else {
ret = true;
}
}
return ret;
}
auto create_meta_attributes(
std::uint64_t accessed_date, std::uint32_t attributes,
std::uint64_t changed_date, std::uint64_t creation_date, bool directory,
std::uint32_t gid, const std::string &key, std::uint32_t mode,
std::uint64_t modified_date, std::uint32_t osx_backup,
std::uint32_t osx_flags, std::uint64_t size, const std::string &source_path,
std::uint32_t uid, std::uint64_t written_date) -> api_meta_map {
return {
{META_ACCESSED, std::to_string(accessed_date)},
{META_ATTRIBUTES, std::to_string(attributes)},
{META_BACKUP, std::to_string(osx_backup)},
{META_CHANGED, std::to_string(changed_date)},
{META_CREATION, std::to_string(creation_date)},
{META_DIRECTORY, utils::string::from_bool(directory)},
{META_GID, std::to_string(gid)},
{META_KEY, key},
{META_MODE, std::to_string(mode)},
{META_MODIFIED, std::to_string(modified_date)},
{META_OSXFLAGS, std::to_string(osx_flags)},
{META_PINNED, "0"},
{META_SIZE, std::to_string(size)},
{META_SOURCE, source_path},
{META_UID, std::to_string(uid)},
{META_WRITTEN, std::to_string(written_date)},
};
}
auto provider_meta_handler(i_provider &provider, bool directory,
const api_file &file) -> api_error {
const auto meta = create_meta_attributes(
file.accessed_date,
directory ? FILE_ATTRIBUTE_DIRECTORY
: FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_ARCHIVE,
file.changed_date, file.creation_date, directory, 0u, file.key,
directory ? S_IFDIR : S_IFREG, file.modified_date, 0u, 0u, file.file_size,
file.source_path, 0u, file.modified_date);
auto res = provider.set_item_meta(file.api_path, meta);
if (res == api_error::success) {
event_system::instance().raise<filesystem_item_added>(
file.api_path, file.api_parent, directory);
}
return res;
}
} // namespace repertory
#endif //_WIN32
/*
Copyright <2018-2023> <scott.e.graves@protonmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#ifdef _WIN32
#include "platform/win32_platform.hpp"
#include "providers/i_provider.hpp"
#include "utils/error_utils.hpp"
namespace repertory {
auto lock_data::get_mount_state(const provider_type & /*pt*/, json &mount_state)
-> bool {
const auto ret = get_mount_state(mount_state);
if (ret) {
const auto mount_id =
app_config::get_provider_display_name(pt_) + unique_id_;
mount_state = mount_state[mount_id].empty()
? json({{"Active", false}, {"Location", ""}, {"PID", -1}})
: mount_state[mount_id];
}
return ret;
}
auto lock_data::get_mount_state(json &mount_state) -> bool {
HKEY key;
auto ret = !::RegCreateKeyEx(
HKEY_CURRENT_USER,
("SOFTWARE\\" + std::string(REPERTORY_DATA_NAME) + "\\Mounts").c_str(), 0,
nullptr, 0, KEY_ALL_ACCESS, nullptr, &key, nullptr);
if (ret) {
DWORD i = 0u;
DWORD data_size = 0u;
std::string name;
name.resize(32767u);
auto name_size = static_cast<DWORD>(name.size());
while (ret &&
(::RegEnumValue(key, i, &name[0], &name_size, nullptr, nullptr,
nullptr, &data_size) == ERROR_SUCCESS)) {
std::string data;
data.resize(data_size);
name_size++;
if ((ret = !::RegEnumValue(key, i++, &name[0], &name_size, nullptr,
nullptr, reinterpret_cast<LPBYTE>(&data[0]),
&data_size))) {
mount_state[name.c_str()] = json::parse(data);
name_size = static_cast<DWORD>(name.size());
data_size = 0u;
}
}
::RegCloseKey(key);
}
return ret;
}
auto lock_data::grab_lock(std::uint8_t retry_count) -> lock_result {
auto ret = lock_result::success;
if (mutex_handle_ == INVALID_HANDLE_VALUE) {
ret = lock_result::failure;
} else {
for (auto i = 0;
(i <= retry_count) && ((mutex_state_ = ::WaitForSingleObject(
mutex_handle_, 100)) == WAIT_TIMEOUT);
i++) {
}
switch (mutex_state_) {
case WAIT_OBJECT_0: {
ret = lock_result::success;
auto should_reset = true;
json mount_state;
if (get_mount_state(pt_, mount_state)) {
if (mount_state["Active"].get<bool>() &&
mount_state["Location"] == "elevating") {
should_reset = false;
}
}
if (should_reset) {
if (not set_mount_state(false, "", -1)) {
utils::error::raise_error(__FUNCTION__, "failed to set mount state");
}
}
} break;
case WAIT_TIMEOUT:
ret = lock_result::locked;
break;
default:
ret = lock_result::failure;
break;
}
}
return ret;
}
void lock_data::release() {
if (mutex_handle_ != INVALID_HANDLE_VALUE) {
if ((mutex_state_ == WAIT_OBJECT_0) || (mutex_state_ == WAIT_ABANDONED)) {
::ReleaseMutex(mutex_handle_);
}
::CloseHandle(mutex_handle_);
mutex_handle_ = INVALID_HANDLE_VALUE;
}
}
auto lock_data::set_mount_state(bool active, const std::string &mount_location,
const std::int64_t &pid) -> bool {
auto ret = false;
if (mutex_handle_ != INVALID_HANDLE_VALUE) {
const auto mount_id =
app_config::get_provider_display_name(pt_) + unique_id_;
json mount_state;
[[maybe_unused]] auto success = get_mount_state(mount_state);
if ((mount_state.find(mount_id) == mount_state.end()) ||
(mount_state[mount_id].find("Active") == mount_state[mount_id].end()) ||
(mount_state[mount_id]["Active"].get<bool>() != active) ||
(active && ((mount_state[mount_id].find("Location") ==
mount_state[mount_id].end()) ||
(mount_state[mount_id]["Location"].get<std::string>() !=
mount_location)))) {
HKEY key;
if ((ret = !::RegCreateKeyEx(
HKEY_CURRENT_USER,
("SOFTWARE\\" + std::string(REPERTORY_DATA_NAME) + "\\Mounts")
.c_str(),
0, nullptr, 0, KEY_ALL_ACCESS, nullptr, &key, nullptr))) {
const auto str = json({{"Active", active},
{"Location", active ? mount_location : ""},
{"PID", active ? pid : -1}})
.dump(0);
ret = !::RegSetValueEx(key, &mount_id[0], 0, REG_SZ,
reinterpret_cast<const BYTE *>(&str[0]),
static_cast<DWORD>(str.size()));
::RegCloseKey(key);
}
} else {
ret = true;
}
}
return ret;
}
auto create_meta_attributes(
std::uint64_t accessed_date, std::uint32_t attributes,
std::uint64_t changed_date, std::uint64_t creation_date, bool directory,
std::uint32_t gid, const std::string &key, std::uint32_t mode,
std::uint64_t modified_date, std::uint32_t osx_backup,
std::uint32_t osx_flags, std::uint64_t size, const std::string &source_path,
std::uint32_t uid, std::uint64_t written_date) -> api_meta_map {
return {
{META_ACCESSED, std::to_string(accessed_date)},
{META_ATTRIBUTES, std::to_string(attributes)},
{META_BACKUP, std::to_string(osx_backup)},
{META_CHANGED, std::to_string(changed_date)},
{META_CREATION, std::to_string(creation_date)},
{META_DIRECTORY, utils::string::from_bool(directory)},
{META_GID, std::to_string(gid)},
{META_KEY, key},
{META_MODE, std::to_string(mode)},
{META_MODIFIED, std::to_string(modified_date)},
{META_OSXFLAGS, std::to_string(osx_flags)},
{META_PINNED, "0"},
{META_SIZE, std::to_string(size)},
{META_SOURCE, source_path},
{META_UID, std::to_string(uid)},
{META_WRITTEN, std::to_string(written_date)},
};
}
auto provider_meta_handler(i_provider &provider, bool directory,
const api_file &file) -> api_error {
const auto meta = create_meta_attributes(
file.accessed_date,
directory ? FILE_ATTRIBUTE_DIRECTORY
: FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_ARCHIVE,
file.changed_date, file.creation_date, directory, 0u, file.key,
directory ? S_IFDIR : S_IFREG, file.modified_date, 0u, 0u, file.file_size,
file.source_path, 0u, file.modified_date);
auto res = provider.set_item_meta(file.api_path, meta);
if (res == api_error::success) {
event_system::instance().raise<filesystem_item_added>(
file.api_path, file.api_parent, directory);
}
return res;
}
} // namespace repertory
#endif //_WIN32

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,75 +1,75 @@
/*
Copyright <2018-2023> <scott.e.graves@protonmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "providers/provider.hpp"
#include "app_config.hpp"
#include "comm/curl/curl_comm.hpp"
#include "comm/i_http_comm.hpp"
#include "events/events.hpp"
#include "providers/encrypt/encrypt_provider.hpp"
#include "providers/s3/s3_provider.hpp"
#include "providers/sia/sia_provider.hpp"
#include "types/startup_exception.hpp"
namespace {
template <typename intf_t, typename comm_t, typename config_t>
inline void create_comm(std::unique_ptr<intf_t> &comm, const config_t &config) {
if (comm) {
throw repertory::startup_exception(
"'create_provider' should only be called once");
}
comm = std::make_unique<comm_t>(config);
}
} // namespace
namespace repertory {
auto create_provider(const provider_type &prov, app_config &config)
-> std::unique_ptr<i_provider> {
static std::mutex mutex;
mutex_lock lock(mutex);
static std::unique_ptr<i_http_comm> comm;
switch (prov) {
case provider_type::sia: {
create_comm<i_http_comm, curl_comm, host_config>(comm,
config.get_host_config());
return std::make_unique<sia_provider>(config, *comm);
}
#if defined(REPERTORY_ENABLE_S3)
case provider_type::s3: {
create_comm<i_http_comm, curl_comm, s3_config>(comm,
config.get_s3_config());
return std::make_unique<s3_provider>(config, *comm);
}
#endif // defined(REPERTORY_ENABLE_S3)
case provider_type::encrypt: {
return std::make_unique<encrypt_provider>(config);
}
case provider_type::unknown:
default:
throw startup_exception("provider not supported: " +
app_config::get_provider_display_name(prov));
}
}
} // namespace repertory
/*
Copyright <2018-2023> <scott.e.graves@protonmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "providers/provider.hpp"
#include "app_config.hpp"
#include "comm/curl/curl_comm.hpp"
#include "comm/i_http_comm.hpp"
#include "events/events.hpp"
#include "providers/encrypt/encrypt_provider.hpp"
#include "providers/s3/s3_provider.hpp"
#include "providers/sia/sia_provider.hpp"
#include "types/startup_exception.hpp"
namespace {
template <typename intf_t, typename comm_t, typename config_t>
inline void create_comm(std::unique_ptr<intf_t> &comm, const config_t &config) {
if (comm) {
throw repertory::startup_exception(
"'create_provider' should only be called once");
}
comm = std::make_unique<comm_t>(config);
}
} // namespace
namespace repertory {
auto create_provider(const provider_type &prov, app_config &config)
-> std::unique_ptr<i_provider> {
static std::mutex mutex;
mutex_lock lock(mutex);
static std::unique_ptr<i_http_comm> comm;
switch (prov) {
case provider_type::sia: {
create_comm<i_http_comm, curl_comm, host_config>(comm,
config.get_host_config());
return std::make_unique<sia_provider>(config, *comm);
}
#if defined(REPERTORY_ENABLE_S3)
case provider_type::s3: {
create_comm<i_http_comm, curl_comm, s3_config>(comm,
config.get_s3_config());
return std::make_unique<s3_provider>(config, *comm);
}
#endif // defined(REPERTORY_ENABLE_S3)
case provider_type::encrypt: {
return std::make_unique<encrypt_provider>(config);
}
case provider_type::unknown:
default:
throw startup_exception("provider not supported: " +
app_config::get_provider_display_name(prov));
}
}
} // namespace repertory

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,267 +1,267 @@
/*
Copyright <2018-2023> <scott.e.graves@protonmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "rpc/client/client.hpp"
#include "types/repertory.hpp"
#include "utils/Base64.hpp"
#include "utils/utils.hpp"
namespace repertory {
client::client(rpc_host_info host_info) : host_info_(std::move(host_info)) {
request_id_ = 0u;
}
auto client::get_drive_information() -> rpc_response {
const auto base_url =
"http://" + host_info_.host + ":" + std::to_string(host_info_.port);
httplib::Client cli{base_url};
cli.set_basic_auth(host_info_.user, host_info_.password);
auto resp = cli.Get("/api/v1/" + rpc_method::get_drive_information);
if (resp.error() != httplib::Error::Success) {
return rpc_response{rpc_response_type::http_error,
{{"error", httplib::to_string(resp.error())}}};
}
if (resp->status != 200) {
return rpc_response{rpc_response_type::http_error,
{{"error", std::to_string(resp->status)}}};
}
return rpc_response{rpc_response_type::success, json::parse(resp->body)};
}
auto client::get_config() -> rpc_response {
const auto base_url =
"http://" + host_info_.host + ":" + std::to_string(host_info_.port);
httplib::Client cli{base_url};
cli.set_basic_auth(host_info_.user, host_info_.password);
auto resp = cli.Get("/api/v1/" + rpc_method::get_config);
if (resp.error() != httplib::Error::Success) {
return rpc_response{rpc_response_type::http_error,
{{"error", httplib::to_string(resp.error())}}};
}
if (resp->status != 200) {
return rpc_response{rpc_response_type::http_error,
{{"error", std::to_string(resp->status)}}};
}
return rpc_response{rpc_response_type::success, json::parse(resp->body)};
}
auto client::get_config_value_by_name(const std::string &name) -> rpc_response {
const auto base_url =
"http://" + host_info_.host + ":" + std::to_string(host_info_.port);
httplib::Params params{{"name", name}};
httplib::Client cli{base_url};
cli.set_basic_auth(host_info_.user, host_info_.password);
auto resp =
cli.Get("/api/v1/" + rpc_method::get_config_value_by_name, params, {});
if (resp.error() != httplib::Error::Success) {
return rpc_response{rpc_response_type::http_error,
{{"error", httplib::to_string(resp.error())}}};
}
if (resp->status != 200) {
return rpc_response{rpc_response_type::http_error,
{{"error", std::to_string(resp->status)}}};
}
return rpc_response{rpc_response_type::success, json::parse(resp->body)};
}
auto client::get_directory_items(const std::string &api_path) -> rpc_response {
const auto base_url =
"http://" + host_info_.host + ":" + std::to_string(host_info_.port);
httplib::Params params{{"api_path", api_path}};
httplib::Client cli{base_url};
cli.set_basic_auth(host_info_.user, host_info_.password);
auto resp = cli.Get("/api/v1/" + rpc_method::get_directory_items, params, {});
if (resp.error() != httplib::Error::Success) {
return rpc_response{rpc_response_type::http_error,
{{"error", httplib::to_string(resp.error())}}};
}
if (resp->status != 200) {
return rpc_response{rpc_response_type::http_error,
{{"error", std::to_string(resp->status)}}};
}
return rpc_response{rpc_response_type::success, json::parse(resp->body)};
}
auto client::get_open_files() -> rpc_response {
const auto base_url =
"http://" + host_info_.host + ":" + std::to_string(host_info_.port);
httplib::Client cli{base_url};
cli.set_basic_auth(host_info_.user, host_info_.password);
auto resp = cli.Get("/api/v1/" + rpc_method::get_open_files);
if (resp.error() != httplib::Error::Success) {
return rpc_response{rpc_response_type::http_error,
{{"error", httplib::to_string(resp.error())}}};
}
if (resp->status != 200) {
return rpc_response{rpc_response_type::http_error,
{{"error", std::to_string(resp->status)}}};
}
return rpc_response{rpc_response_type::success, json::parse(resp->body)};
}
auto client::get_pinned_files() -> rpc_response {
const auto base_url =
"http://" + host_info_.host + ":" + std::to_string(host_info_.port);
httplib::Client cli{base_url};
cli.set_basic_auth(host_info_.user, host_info_.password);
auto resp = cli.Get("/api/v1/" + rpc_method::get_pinned_files);
if (resp.error() != httplib::Error::Success) {
return rpc_response{rpc_response_type::http_error,
{{"error", httplib::to_string(resp.error())}}};
}
if (resp->status != 200) {
return rpc_response{rpc_response_type::http_error,
{{"error", std::to_string(resp->status)}}};
}
return rpc_response{rpc_response_type::success, json::parse(resp->body)};
}
auto client::pin_file(const std::string &api_path) -> rpc_response {
const auto base_url =
"http://" + host_info_.host + ":" + std::to_string(host_info_.port);
httplib::Params params{{"api_path", api_path}};
httplib::Client cli{base_url};
cli.set_basic_auth(host_info_.user, host_info_.password);
auto resp = cli.Post("/api/v1/" + rpc_method::pin_file, params);
if (resp.error() != httplib::Error::Success) {
return rpc_response{rpc_response_type::http_error,
{{"error", httplib::to_string(resp.error())}}};
}
if (resp->status != 200) {
return rpc_response{rpc_response_type::http_error,
{{"error", std::to_string(resp->status)}}};
}
return rpc_response{rpc_response_type::success, {}};
}
auto client::pinned_status(const std::string &api_path) -> rpc_response {
const auto base_url =
"http://" + host_info_.host + ":" + std::to_string(host_info_.port);
httplib::Params params{{"api_path", api_path}};
httplib::Client cli{base_url};
cli.set_basic_auth(host_info_.user, host_info_.password);
auto resp = cli.Get("/api/v1/" + rpc_method::pinned_status, params, {});
if (resp.error() != httplib::Error::Success) {
return rpc_response{rpc_response_type::http_error,
{{"error", httplib::to_string(resp.error())}}};
}
if (resp->status != 200) {
return rpc_response{rpc_response_type::http_error,
{{"error", std::to_string(resp->status)}}};
}
return rpc_response{rpc_response_type::success, json::parse(resp->body)};
}
auto client::set_config_value_by_name(const std::string &name,
const std::string &value)
-> rpc_response {
const auto base_url =
"http://" + host_info_.host + ":" + std::to_string(host_info_.port);
httplib::Params params{
{"name", name},
{"value", value},
};
httplib::Client cli{base_url};
cli.set_basic_auth(host_info_.user, host_info_.password);
auto resp =
cli.Post("/api/v1/" + rpc_method::set_config_value_by_name, params);
if (resp.error() != httplib::Error::Success) {
return rpc_response{rpc_response_type::http_error,
{{"error", httplib::to_string(resp.error())}}};
}
if (resp->status != 200) {
return rpc_response{rpc_response_type::http_error,
{{"error", std::to_string(resp->status)}}};
};
return rpc_response{rpc_response_type::success,
nlohmann::json::parse(resp->body)};
}
auto client::unmount() -> rpc_response {
const auto base_url =
"http://" + host_info_.host + ":" + std::to_string(host_info_.port);
httplib::Client cli{base_url};
cli.set_basic_auth(host_info_.user, host_info_.password);
auto resp = cli.Post("/api/v1/" + rpc_method::unmount);
if (resp.error() != httplib::Error::Success) {
return rpc_response{rpc_response_type::http_error,
{{"error", httplib::to_string(resp.error())}}};
}
if (resp->status != 200) {
return rpc_response{rpc_response_type::http_error,
{{"error", std::to_string(resp->status)}}};
}
return rpc_response{rpc_response_type::success, {}};
}
auto client::unpin_file(const std::string &api_path) -> rpc_response {
const auto base_url =
"http://" + host_info_.host + ":" + std::to_string(host_info_.port);
httplib::Params params{{"api_path", api_path}};
httplib::Client cli{base_url};
cli.set_basic_auth(host_info_.user, host_info_.password);
auto resp = cli.Post("/api/v1/" + rpc_method::unpin_file, params);
if (resp.error() != httplib::Error::Success) {
return rpc_response{rpc_response_type::http_error,
{{"error", httplib::to_string(resp.error())}}};
}
if (resp->status != 200) {
return rpc_response{rpc_response_type::http_error,
{{"error", std::to_string(resp->status)}}};
}
return rpc_response{rpc_response_type::success, {}};
}
} // namespace repertory
/*
Copyright <2018-2023> <scott.e.graves@protonmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "rpc/client/client.hpp"
#include "types/repertory.hpp"
#include "utils/Base64.hpp"
#include "utils/utils.hpp"
namespace repertory {
client::client(rpc_host_info host_info) : host_info_(std::move(host_info)) {
request_id_ = 0u;
}
auto client::get_drive_information() -> rpc_response {
const auto base_url =
"http://" + host_info_.host + ":" + std::to_string(host_info_.port);
httplib::Client cli{base_url};
cli.set_basic_auth(host_info_.user, host_info_.password);
auto resp = cli.Get("/api/v1/" + rpc_method::get_drive_information);
if (resp.error() != httplib::Error::Success) {
return rpc_response{rpc_response_type::http_error,
{{"error", httplib::to_string(resp.error())}}};
}
if (resp->status != 200) {
return rpc_response{rpc_response_type::http_error,
{{"error", std::to_string(resp->status)}}};
}
return rpc_response{rpc_response_type::success, json::parse(resp->body)};
}
auto client::get_config() -> rpc_response {
const auto base_url =
"http://" + host_info_.host + ":" + std::to_string(host_info_.port);
httplib::Client cli{base_url};
cli.set_basic_auth(host_info_.user, host_info_.password);
auto resp = cli.Get("/api/v1/" + rpc_method::get_config);
if (resp.error() != httplib::Error::Success) {
return rpc_response{rpc_response_type::http_error,
{{"error", httplib::to_string(resp.error())}}};
}
if (resp->status != 200) {
return rpc_response{rpc_response_type::http_error,
{{"error", std::to_string(resp->status)}}};
}
return rpc_response{rpc_response_type::success, json::parse(resp->body)};
}
auto client::get_config_value_by_name(const std::string &name) -> rpc_response {
const auto base_url =
"http://" + host_info_.host + ":" + std::to_string(host_info_.port);
httplib::Params params{{"name", name}};
httplib::Client cli{base_url};
cli.set_basic_auth(host_info_.user, host_info_.password);
auto resp =
cli.Get("/api/v1/" + rpc_method::get_config_value_by_name, params, {});
if (resp.error() != httplib::Error::Success) {
return rpc_response{rpc_response_type::http_error,
{{"error", httplib::to_string(resp.error())}}};
}
if (resp->status != 200) {
return rpc_response{rpc_response_type::http_error,
{{"error", std::to_string(resp->status)}}};
}
return rpc_response{rpc_response_type::success, json::parse(resp->body)};
}
auto client::get_directory_items(const std::string &api_path) -> rpc_response {
const auto base_url =
"http://" + host_info_.host + ":" + std::to_string(host_info_.port);
httplib::Params params{{"api_path", api_path}};
httplib::Client cli{base_url};
cli.set_basic_auth(host_info_.user, host_info_.password);
auto resp = cli.Get("/api/v1/" + rpc_method::get_directory_items, params, {});
if (resp.error() != httplib::Error::Success) {
return rpc_response{rpc_response_type::http_error,
{{"error", httplib::to_string(resp.error())}}};
}
if (resp->status != 200) {
return rpc_response{rpc_response_type::http_error,
{{"error", std::to_string(resp->status)}}};
}
return rpc_response{rpc_response_type::success, json::parse(resp->body)};
}
auto client::get_open_files() -> rpc_response {
const auto base_url =
"http://" + host_info_.host + ":" + std::to_string(host_info_.port);
httplib::Client cli{base_url};
cli.set_basic_auth(host_info_.user, host_info_.password);
auto resp = cli.Get("/api/v1/" + rpc_method::get_open_files);
if (resp.error() != httplib::Error::Success) {
return rpc_response{rpc_response_type::http_error,
{{"error", httplib::to_string(resp.error())}}};
}
if (resp->status != 200) {
return rpc_response{rpc_response_type::http_error,
{{"error", std::to_string(resp->status)}}};
}
return rpc_response{rpc_response_type::success, json::parse(resp->body)};
}
auto client::get_pinned_files() -> rpc_response {
const auto base_url =
"http://" + host_info_.host + ":" + std::to_string(host_info_.port);
httplib::Client cli{base_url};
cli.set_basic_auth(host_info_.user, host_info_.password);
auto resp = cli.Get("/api/v1/" + rpc_method::get_pinned_files);
if (resp.error() != httplib::Error::Success) {
return rpc_response{rpc_response_type::http_error,
{{"error", httplib::to_string(resp.error())}}};
}
if (resp->status != 200) {
return rpc_response{rpc_response_type::http_error,
{{"error", std::to_string(resp->status)}}};
}
return rpc_response{rpc_response_type::success, json::parse(resp->body)};
}
auto client::pin_file(const std::string &api_path) -> rpc_response {
const auto base_url =
"http://" + host_info_.host + ":" + std::to_string(host_info_.port);
httplib::Params params{{"api_path", api_path}};
httplib::Client cli{base_url};
cli.set_basic_auth(host_info_.user, host_info_.password);
auto resp = cli.Post("/api/v1/" + rpc_method::pin_file, params);
if (resp.error() != httplib::Error::Success) {
return rpc_response{rpc_response_type::http_error,
{{"error", httplib::to_string(resp.error())}}};
}
if (resp->status != 200) {
return rpc_response{rpc_response_type::http_error,
{{"error", std::to_string(resp->status)}}};
}
return rpc_response{rpc_response_type::success, {}};
}
auto client::pinned_status(const std::string &api_path) -> rpc_response {
const auto base_url =
"http://" + host_info_.host + ":" + std::to_string(host_info_.port);
httplib::Params params{{"api_path", api_path}};
httplib::Client cli{base_url};
cli.set_basic_auth(host_info_.user, host_info_.password);
auto resp = cli.Get("/api/v1/" + rpc_method::pinned_status, params, {});
if (resp.error() != httplib::Error::Success) {
return rpc_response{rpc_response_type::http_error,
{{"error", httplib::to_string(resp.error())}}};
}
if (resp->status != 200) {
return rpc_response{rpc_response_type::http_error,
{{"error", std::to_string(resp->status)}}};
}
return rpc_response{rpc_response_type::success, json::parse(resp->body)};
}
auto client::set_config_value_by_name(const std::string &name,
const std::string &value)
-> rpc_response {
const auto base_url =
"http://" + host_info_.host + ":" + std::to_string(host_info_.port);
httplib::Params params{
{"name", name},
{"value", value},
};
httplib::Client cli{base_url};
cli.set_basic_auth(host_info_.user, host_info_.password);
auto resp =
cli.Post("/api/v1/" + rpc_method::set_config_value_by_name, params);
if (resp.error() != httplib::Error::Success) {
return rpc_response{rpc_response_type::http_error,
{{"error", httplib::to_string(resp.error())}}};
}
if (resp->status != 200) {
return rpc_response{rpc_response_type::http_error,
{{"error", std::to_string(resp->status)}}};
};
return rpc_response{rpc_response_type::success,
nlohmann::json::parse(resp->body)};
}
auto client::unmount() -> rpc_response {
const auto base_url =
"http://" + host_info_.host + ":" + std::to_string(host_info_.port);
httplib::Client cli{base_url};
cli.set_basic_auth(host_info_.user, host_info_.password);
auto resp = cli.Post("/api/v1/" + rpc_method::unmount);
if (resp.error() != httplib::Error::Success) {
return rpc_response{rpc_response_type::http_error,
{{"error", httplib::to_string(resp.error())}}};
}
if (resp->status != 200) {
return rpc_response{rpc_response_type::http_error,
{{"error", std::to_string(resp->status)}}};
}
return rpc_response{rpc_response_type::success, {}};
}
auto client::unpin_file(const std::string &api_path) -> rpc_response {
const auto base_url =
"http://" + host_info_.host + ":" + std::to_string(host_info_.port);
httplib::Params params{{"api_path", api_path}};
httplib::Client cli{base_url};
cli.set_basic_auth(host_info_.user, host_info_.password);
auto resp = cli.Post("/api/v1/" + rpc_method::unpin_file, params);
if (resp.error() != httplib::Error::Success) {
return rpc_response{rpc_response_type::http_error,
{{"error", httplib::to_string(resp.error())}}};
}
if (resp->status != 200) {
return rpc_response{rpc_response_type::http_error,
{{"error", std::to_string(resp->status)}}};
}
return rpc_response{rpc_response_type::success, {}};
}
} // namespace repertory

View File

@@ -1,204 +1,204 @@
/*
Copyright <2018-2023> <scott.e.graves@protonmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "rpc/server/full_server.hpp"
#include "app_config.hpp"
#include "drives/directory_iterator.hpp"
#include "file_manager/i_file_manager.hpp"
#include "providers/i_provider.hpp"
#include "types/repertory.hpp"
#include "types/rpc.hpp"
#include "utils/file_utils.hpp"
#include "utils/path_utils.hpp"
namespace repertory {
full_server::full_server(app_config &config, i_provider &provider,
i_file_manager &fm)
: server(config), provider_(provider), fm_(fm) {}
void full_server::handle_get_directory_items(const httplib::Request &req,
httplib::Response &res) {
const auto api_path =
utils::path::create_api_path(req.get_param_value("api_path"));
const auto list = fm_.get_directory_items(api_path);
json items = {{"items", std::vector<json>()}};
for (const auto &item : list) {
items["items"].emplace_back(item.to_json());
}
res.set_content(items.dump(), "application/json");
res.status = 200;
}
void full_server::handle_get_drive_information(const httplib::Request & /*req*/,
httplib::Response &res) {
res.set_content(
json({
{"cache_space_used",
utils::file::calculate_used_space(
get_config().get_cache_directory(), false)},
{"drive_space_total", provider_.get_total_drive_space()},
{"drive_space_used", provider_.get_used_drive_space()},
{"item_count", provider_.get_total_item_count()},
})
.dump(),
"application/json");
res.status = 200;
}
void full_server::handle_get_open_files(const httplib::Request & /*req*/,
httplib::Response &res) {
const auto list = fm_.get_open_files();
json open_files = {{"items", std::vector<json>()}};
for (const auto &kv : list) {
open_files["items"].emplace_back(json({
{"path", kv.first},
{"count", kv.second},
}));
}
res.set_content(open_files.dump(), "application/json");
res.status = 200;
}
void full_server::handle_get_pinned_files(const httplib::Request & /*req*/,
httplib::Response &res) {
res.set_content(json({{"items", provider_.get_pinned_files()}}).dump(),
"application/json");
res.status = 200;
}
void full_server::handle_get_pinned_status(const httplib::Request &req,
httplib::Response &res) {
const auto api_path =
utils::path::create_api_path(req.get_param_value("api_path"));
std::string pinned;
const auto result = provider_.get_item_meta(api_path, META_PINNED, pinned);
if (result != api_error::success) {
utils::error::raise_api_path_error(__FUNCTION__, api_path, result,
"failed to get pinned status");
res.status = 500;
return;
}
res.set_content(
json(
{{"pinned", pinned.empty() ? false : utils::string::to_bool(pinned)}})
.dump(),
"application/json");
res.status = 200;
}
void full_server::handle_pin_file(const httplib::Request &req,
httplib::Response &res) {
const auto api_path =
utils::path::create_api_path(req.get_param_value("api_path"));
bool exists{};
auto result = provider_.is_file(api_path, exists);
if (result != api_error::success) {
utils::error::raise_api_path_error(__FUNCTION__, api_path, result,
"failed to pin file");
res.status = 500;
return;
}
if (exists) {
exists = api_error::success ==
provider_.set_item_meta(api_path, META_PINNED,
utils::string::from_bool(true));
}
if (exists) {
event_system::instance().raise<file_pinned>(api_path);
}
res.status = exists ? 200 : 404;
}
void full_server::handle_unpin_file(const httplib::Request &req,
httplib::Response &res) {
const auto api_path =
utils::path::create_api_path(req.get_param_value("api_path"));
bool exists{};
auto result = provider_.is_file(api_path, exists);
if (result != api_error::success) {
utils::error::raise_api_path_error(__FUNCTION__, api_path, result,
"failed to unpin file");
res.status = 500;
return;
}
if (exists) {
exists = api_error::success ==
provider_.set_item_meta(api_path, META_PINNED,
utils::string::from_bool(false));
}
if (exists) {
event_system::instance().raise<file_unpinned>(api_path);
}
res.status = exists ? 200 : 404;
}
void full_server::initialize(httplib::Server &inst) {
server::initialize(inst);
inst.Get("/api/v1/" + rpc_method::get_directory_items,
[this](auto &&req, auto &&res) {
handle_get_directory_items(std::forward<decltype(req)>(req),
std::forward<decltype(res)>(res));
});
inst.Get("/api/v1/" + rpc_method::get_drive_information,
[this](auto &&req, auto &&res) {
handle_get_drive_information(std::forward<decltype(req)>(req),
std::forward<decltype(res)>(res));
});
inst.Get("/api/v1/" + rpc_method::get_open_files,
[this](auto &&req, auto &&res) {
handle_get_open_files(std::forward<decltype(req)>(req),
std::forward<decltype(res)>(res));
});
inst.Get("/api/v1/" + rpc_method::get_pinned_files,
[this](auto &&req, auto &&res) {
handle_get_pinned_files(std::forward<decltype(req)>(req),
std::forward<decltype(res)>(res));
});
inst.Get("/api/v1/" + rpc_method::pinned_status,
[this](auto &&req, auto &&res) {
handle_get_pinned_status(std::forward<decltype(req)>(req),
std::forward<decltype(res)>(res));
});
inst.Post("/api/v1/" + rpc_method::pin_file, [this](auto &&req, auto &&res) {
handle_pin_file(std::forward<decltype(req)>(req),
std::forward<decltype(res)>(res));
});
inst.Post("/api/v1/" + rpc_method::unpin_file,
[this](auto &&req, auto &&res) {
handle_unpin_file(std::forward<decltype(req)>(req),
std::forward<decltype(res)>(res));
});
}
} // namespace repertory
/*
Copyright <2018-2023> <scott.e.graves@protonmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "rpc/server/full_server.hpp"
#include "app_config.hpp"
#include "drives/directory_iterator.hpp"
#include "file_manager/i_file_manager.hpp"
#include "providers/i_provider.hpp"
#include "types/repertory.hpp"
#include "types/rpc.hpp"
#include "utils/file_utils.hpp"
#include "utils/path_utils.hpp"
namespace repertory {
full_server::full_server(app_config &config, i_provider &provider,
i_file_manager &fm)
: server(config), provider_(provider), fm_(fm) {}
void full_server::handle_get_directory_items(const httplib::Request &req,
httplib::Response &res) {
const auto api_path =
utils::path::create_api_path(req.get_param_value("api_path"));
const auto list = fm_.get_directory_items(api_path);
json items = {{"items", std::vector<json>()}};
for (const auto &item : list) {
items["items"].emplace_back(item.to_json());
}
res.set_content(items.dump(), "application/json");
res.status = 200;
}
void full_server::handle_get_drive_information(const httplib::Request & /*req*/,
httplib::Response &res) {
res.set_content(
json({
{"cache_space_used",
utils::file::calculate_used_space(
get_config().get_cache_directory(), false)},
{"drive_space_total", provider_.get_total_drive_space()},
{"drive_space_used", provider_.get_used_drive_space()},
{"item_count", provider_.get_total_item_count()},
})
.dump(),
"application/json");
res.status = 200;
}
void full_server::handle_get_open_files(const httplib::Request & /*req*/,
httplib::Response &res) {
const auto list = fm_.get_open_files();
json open_files = {{"items", std::vector<json>()}};
for (const auto &kv : list) {
open_files["items"].emplace_back(json({
{"path", kv.first},
{"count", kv.second},
}));
}
res.set_content(open_files.dump(), "application/json");
res.status = 200;
}
void full_server::handle_get_pinned_files(const httplib::Request & /*req*/,
httplib::Response &res) {
res.set_content(json({{"items", provider_.get_pinned_files()}}).dump(),
"application/json");
res.status = 200;
}
void full_server::handle_get_pinned_status(const httplib::Request &req,
httplib::Response &res) {
const auto api_path =
utils::path::create_api_path(req.get_param_value("api_path"));
std::string pinned;
const auto result = provider_.get_item_meta(api_path, META_PINNED, pinned);
if (result != api_error::success) {
utils::error::raise_api_path_error(__FUNCTION__, api_path, result,
"failed to get pinned status");
res.status = 500;
return;
}
res.set_content(
json(
{{"pinned", pinned.empty() ? false : utils::string::to_bool(pinned)}})
.dump(),
"application/json");
res.status = 200;
}
void full_server::handle_pin_file(const httplib::Request &req,
httplib::Response &res) {
const auto api_path =
utils::path::create_api_path(req.get_param_value("api_path"));
bool exists{};
auto result = provider_.is_file(api_path, exists);
if (result != api_error::success) {
utils::error::raise_api_path_error(__FUNCTION__, api_path, result,
"failed to pin file");
res.status = 500;
return;
}
if (exists) {
exists = api_error::success ==
provider_.set_item_meta(api_path, META_PINNED,
utils::string::from_bool(true));
}
if (exists) {
event_system::instance().raise<file_pinned>(api_path);
}
res.status = exists ? 200 : 404;
}
void full_server::handle_unpin_file(const httplib::Request &req,
httplib::Response &res) {
const auto api_path =
utils::path::create_api_path(req.get_param_value("api_path"));
bool exists{};
auto result = provider_.is_file(api_path, exists);
if (result != api_error::success) {
utils::error::raise_api_path_error(__FUNCTION__, api_path, result,
"failed to unpin file");
res.status = 500;
return;
}
if (exists) {
exists = api_error::success ==
provider_.set_item_meta(api_path, META_PINNED,
utils::string::from_bool(false));
}
if (exists) {
event_system::instance().raise<file_unpinned>(api_path);
}
res.status = exists ? 200 : 404;
}
void full_server::initialize(httplib::Server &inst) {
server::initialize(inst);
inst.Get("/api/v1/" + rpc_method::get_directory_items,
[this](auto &&req, auto &&res) {
handle_get_directory_items(std::forward<decltype(req)>(req),
std::forward<decltype(res)>(res));
});
inst.Get("/api/v1/" + rpc_method::get_drive_information,
[this](auto &&req, auto &&res) {
handle_get_drive_information(std::forward<decltype(req)>(req),
std::forward<decltype(res)>(res));
});
inst.Get("/api/v1/" + rpc_method::get_open_files,
[this](auto &&req, auto &&res) {
handle_get_open_files(std::forward<decltype(req)>(req),
std::forward<decltype(res)>(res));
});
inst.Get("/api/v1/" + rpc_method::get_pinned_files,
[this](auto &&req, auto &&res) {
handle_get_pinned_files(std::forward<decltype(req)>(req),
std::forward<decltype(res)>(res));
});
inst.Get("/api/v1/" + rpc_method::pinned_status,
[this](auto &&req, auto &&res) {
handle_get_pinned_status(std::forward<decltype(req)>(req),
std::forward<decltype(res)>(res));
});
inst.Post("/api/v1/" + rpc_method::pin_file, [this](auto &&req, auto &&res) {
handle_pin_file(std::forward<decltype(req)>(req),
std::forward<decltype(res)>(res));
});
inst.Post("/api/v1/" + rpc_method::unpin_file,
[this](auto &&req, auto &&res) {
handle_unpin_file(std::forward<decltype(req)>(req),
std::forward<decltype(res)>(res));
});
}
} // namespace repertory

View File

@@ -1,181 +1,181 @@
/*
Copyright <2018-2023> <scott.e.graves@protonmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "rpc/server/server.hpp"
#include "app_config.hpp"
#include "utils/Base64.hpp"
#include "utils/error_utils.hpp"
namespace repertory {
server::server(app_config &config) : config_(config) {}
auto server::check_authorization(const httplib::Request &req) -> bool {
if (config_.get_api_auth().empty() || config_.get_api_user().empty()) {
utils::error::raise_error(__FUNCTION__,
"authorization user or password is not set");
return false;
}
const auto authorization = req.get_header_value("Authorization");
if (authorization.empty()) {
return false;
}
const auto auth_parts = utils::string::split(authorization, ' ');
if (auth_parts.empty()) {
return false;
}
const auto auth_type = auth_parts[0U];
if (auth_type != "Basic") {
return false;
}
const auto data = macaron::Base64::Decode(authorization.substr(6U));
const auto auth =
utils::string::split(std::string(data.begin(), data.end()), ':');
if (auth.size() != 2U) {
return false;
}
const auto &user = auth[0U];
const auto &pwd = auth[1U];
return (user == config_.get_api_user()) && (pwd == config_.get_api_auth());
}
void server::handle_get_config(const httplib::Request & /*req*/,
httplib::Response &res) {
auto data = config_.get_json();
res.set_content(data.dump(), "application/json");
res.status = 200;
}
void server::handle_get_config_value_by_name(const httplib::Request &req,
httplib::Response &res) {
auto name = req.get_param_value("name");
auto data = json({{"value", config_.get_value_by_name(name)}});
res.set_content(data.dump(), "application/json");
res.status = 200;
}
void server::handle_set_config_value_by_name(const httplib::Request &req,
httplib::Response &res) {
auto name = req.get_param_value("name");
auto value = req.get_param_value("value");
json data = {{"value", config_.set_value_by_name(name, value)}};
res.set_content(data.dump(), "application/json");
res.status = 200;
}
void server::handle_unmount(const httplib::Request & /*req*/,
httplib::Response &res) {
event_system::instance().raise<unmount_requested>();
res.status = 200;
}
void server::initialize(httplib::Server &inst) {
inst.Get("/api/v1/" + rpc_method::get_config, [this](auto &&req, auto &&res) {
handle_get_config(std::forward<decltype(req)>(req),
std::forward<decltype(res)>(res));
});
inst.Get("/api/v1/" + rpc_method::get_config_value_by_name,
[this](auto &&req, auto &&res) {
handle_get_config_value_by_name(std::forward<decltype(req)>(req),
std::forward<decltype(res)>(res));
});
inst.Post("/api/v1/" + rpc_method::set_config_value_by_name,
[this](auto &&req, auto &&res) {
handle_set_config_value_by_name(std::forward<decltype(req)>(req),
std::forward<decltype(res)>(res));
});
inst.Post("/api/v1/" + rpc_method::unmount, [this](auto &&req, auto &&res) {
handle_unmount(std::forward<decltype(req)>(req),
std::forward<decltype(res)>(res));
});
}
void server::start() {
mutex_lock l(start_stop_mutex_);
if (not started_) {
event_system::instance().raise<service_started>("server");
server_ = std::make_unique<httplib::Server>();
server_->set_exception_handler([](const httplib::Request &req,
httplib::Response &res,
std::exception_ptr ep) {
json data = {{"path", req.path}};
try {
std::rethrow_exception(ep);
} catch (std::exception &e) {
data["error"] = e.what() ? e.what() : "unknown error";
utils::error::raise_error(__FUNCTION__, e,
"failed request: " + req.path);
} catch (...) {
data["error"] = "unknown error";
utils::error::raise_error(__FUNCTION__, "unknown error",
"failed request: " + req.path);
}
res.set_content(data.dump(), "application/json");
res.status = 500;
});
server_->set_pre_routing_handler(
[this](auto &&req, auto &&res) -> httplib::Server::HandlerResponse {
if (check_authorization(req)) {
return httplib::Server::HandlerResponse::Unhandled;
}
res.status = 401;
return httplib::Server::HandlerResponse::Handled;
});
initialize(*server_);
server_thread_ = std::make_unique<std::thread>(
[this]() { server_->listen("127.0.0.1", config_.get_api_port()); });
started_ = true;
}
}
void server::stop() {
if (started_) {
mutex_lock l(start_stop_mutex_);
if (started_) {
event_system::instance().raise<service_shutdown_begin>("server");
server_->stop();
server_thread_->join();
server_thread_.reset();
started_ = false;
event_system::instance().raise<service_shutdown_end>("server");
}
}
}
} // namespace repertory
/*
Copyright <2018-2023> <scott.e.graves@protonmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "rpc/server/server.hpp"
#include "app_config.hpp"
#include "utils/Base64.hpp"
#include "utils/error_utils.hpp"
namespace repertory {
server::server(app_config &config) : config_(config) {}
auto server::check_authorization(const httplib::Request &req) -> bool {
if (config_.get_api_auth().empty() || config_.get_api_user().empty()) {
utils::error::raise_error(__FUNCTION__,
"authorization user or password is not set");
return false;
}
const auto authorization = req.get_header_value("Authorization");
if (authorization.empty()) {
return false;
}
const auto auth_parts = utils::string::split(authorization, ' ');
if (auth_parts.empty()) {
return false;
}
const auto auth_type = auth_parts[0U];
if (auth_type != "Basic") {
return false;
}
const auto data = macaron::Base64::Decode(authorization.substr(6U));
const auto auth =
utils::string::split(std::string(data.begin(), data.end()), ':');
if (auth.size() != 2U) {
return false;
}
const auto &user = auth[0U];
const auto &pwd = auth[1U];
return (user == config_.get_api_user()) && (pwd == config_.get_api_auth());
}
void server::handle_get_config(const httplib::Request & /*req*/,
httplib::Response &res) {
auto data = config_.get_json();
res.set_content(data.dump(), "application/json");
res.status = 200;
}
void server::handle_get_config_value_by_name(const httplib::Request &req,
httplib::Response &res) {
auto name = req.get_param_value("name");
auto data = json({{"value", config_.get_value_by_name(name)}});
res.set_content(data.dump(), "application/json");
res.status = 200;
}
void server::handle_set_config_value_by_name(const httplib::Request &req,
httplib::Response &res) {
auto name = req.get_param_value("name");
auto value = req.get_param_value("value");
json data = {{"value", config_.set_value_by_name(name, value)}};
res.set_content(data.dump(), "application/json");
res.status = 200;
}
void server::handle_unmount(const httplib::Request & /*req*/,
httplib::Response &res) {
event_system::instance().raise<unmount_requested>();
res.status = 200;
}
void server::initialize(httplib::Server &inst) {
inst.Get("/api/v1/" + rpc_method::get_config, [this](auto &&req, auto &&res) {
handle_get_config(std::forward<decltype(req)>(req),
std::forward<decltype(res)>(res));
});
inst.Get("/api/v1/" + rpc_method::get_config_value_by_name,
[this](auto &&req, auto &&res) {
handle_get_config_value_by_name(std::forward<decltype(req)>(req),
std::forward<decltype(res)>(res));
});
inst.Post("/api/v1/" + rpc_method::set_config_value_by_name,
[this](auto &&req, auto &&res) {
handle_set_config_value_by_name(std::forward<decltype(req)>(req),
std::forward<decltype(res)>(res));
});
inst.Post("/api/v1/" + rpc_method::unmount, [this](auto &&req, auto &&res) {
handle_unmount(std::forward<decltype(req)>(req),
std::forward<decltype(res)>(res));
});
}
void server::start() {
mutex_lock l(start_stop_mutex_);
if (not started_) {
event_system::instance().raise<service_started>("server");
server_ = std::make_unique<httplib::Server>();
server_->set_exception_handler([](const httplib::Request &req,
httplib::Response &res,
std::exception_ptr ep) {
json data = {{"path", req.path}};
try {
std::rethrow_exception(ep);
} catch (std::exception &e) {
data["error"] = e.what() ? e.what() : "unknown error";
utils::error::raise_error(__FUNCTION__, e,
"failed request: " + req.path);
} catch (...) {
data["error"] = "unknown error";
utils::error::raise_error(__FUNCTION__, "unknown error",
"failed request: " + req.path);
}
res.set_content(data.dump(), "application/json");
res.status = 500;
});
server_->set_pre_routing_handler(
[this](auto &&req, auto &&res) -> httplib::Server::HandlerResponse {
if (check_authorization(req)) {
return httplib::Server::HandlerResponse::Unhandled;
}
res.status = 401;
return httplib::Server::HandlerResponse::Handled;
});
initialize(*server_);
server_thread_ = std::make_unique<std::thread>(
[this]() { server_->listen("127.0.0.1", config_.get_api_port()); });
started_ = true;
}
}
void server::stop() {
if (started_) {
mutex_lock l(start_stop_mutex_);
if (started_) {
event_system::instance().raise<service_shutdown_begin>("server");
server_->stop();
server_thread_->join();
server_thread_.reset();
started_ = false;
event_system::instance().raise<service_shutdown_end>("server");
}
}
}
} // namespace repertory

View File

@@ -1,118 +1,118 @@
/*
Copyright <2018-2023> <scott.e.graves@protonmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "types/remote.hpp"
namespace repertory::remote {
#ifndef _WIN32
auto create_open_flags(std::uint32_t flags) -> open_flags {
open_flags ret{};
{
const auto val = (flags & 3U);
ret |= (val == 1U) ? open_flags::write_only
: (val == 2U) ? open_flags::read_write
: open_flags::read_only;
}
const auto set_if_has_flag = [&flags, &ret](auto flag, open_flags o_flag) {
if ((flags & static_cast<std::uint32_t>(flag)) != 0U) {
ret |= o_flag;
}
};
set_if_has_flag(O_APPEND, open_flags::append);
set_if_has_flag(O_ASYNC, open_flags::async);
set_if_has_flag(O_CLOEXEC, open_flags::clo_exec);
set_if_has_flag(O_CREAT, open_flags::create);
#ifdef O_DIRECT
set_if_has_flag(O_DIRECT, open_flags::direct);
#endif
set_if_has_flag(O_DIRECTORY, open_flags::directory);
#ifdef O_DSYNC
set_if_has_flag(O_DSYNC, open_flags::dsync);
#endif
set_if_has_flag(O_EXCL, open_flags::excl);
#ifdef O_NOATIME
set_if_has_flag(O_NOATIME, open_flags::no_atime);
#endif
set_if_has_flag(O_NOCTTY, open_flags::no_ctty);
set_if_has_flag(O_NOFOLLOW, open_flags::no_follow);
set_if_has_flag(O_NONBLOCK, open_flags::non_blocking);
#ifdef O_PATH
set_if_has_flag(O_PATH, open_flags::path);
#endif
set_if_has_flag(O_SYNC, open_flags::sync);
#ifdef O_TMPFILE
set_if_has_flag(O_TMPFILE, open_flags::temp_file);
#endif
set_if_has_flag(O_TRUNC, open_flags::truncate);
return ret;
}
auto create_os_open_flags(const open_flags &flags) -> std::uint32_t {
std::uint32_t ret{};
const auto set_if_has_flag = [&flags, &ret](auto o_flag, auto flag) -> bool {
if ((flags & o_flag) == o_flag) {
ret |= static_cast<std::uint32_t>(flag);
return true;
}
return false;
};
if (not set_if_has_flag(open_flags::read_write, O_RDWR)) {
if (not set_if_has_flag(open_flags::write_only, O_WRONLY)) {
ret |= static_cast<std::uint32_t>(O_RDONLY);
}
}
set_if_has_flag(open_flags::append, O_APPEND);
set_if_has_flag(open_flags::async, O_ASYNC);
set_if_has_flag(open_flags::clo_exec, O_CLOEXEC);
set_if_has_flag(open_flags::create, O_CREAT);
#ifdef O_DIRECT
set_if_has_flag(open_flags::direct, O_DIRECT);
#endif
set_if_has_flag(open_flags::directory, O_DIRECTORY);
#ifdef O_DSYNC
set_if_has_flag(open_flags::dsync, O_DSYNC);
#endif
set_if_has_flag(open_flags::excl, O_EXCL);
#ifdef O_NOATIME
set_if_has_flag(open_flags::no_atime, O_NOATIME);
#endif
set_if_has_flag(open_flags::no_ctty, O_NOCTTY);
set_if_has_flag(open_flags::no_follow, O_NOFOLLOW);
set_if_has_flag(open_flags::non_blocking, O_NONBLOCK);
#ifdef O_PATH
set_if_has_flag(open_flags::path, O_PATH);
#endif
set_if_has_flag(open_flags::sync, O_SYNC);
#ifdef O_TMPFILE
set_if_has_flag(open_flags::temp_file, O_TMPFILE);
#endif
set_if_has_flag(open_flags::truncate, O_TRUNC);
return ret;
}
#endif
} // namespace repertory::remote
/*
Copyright <2018-2023> <scott.e.graves@protonmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "types/remote.hpp"
namespace repertory::remote {
#ifndef _WIN32
auto create_open_flags(std::uint32_t flags) -> open_flags {
open_flags ret{};
{
const auto val = (flags & 3U);
ret |= (val == 1U) ? open_flags::write_only
: (val == 2U) ? open_flags::read_write
: open_flags::read_only;
}
const auto set_if_has_flag = [&flags, &ret](auto flag, open_flags o_flag) {
if ((flags & static_cast<std::uint32_t>(flag)) != 0U) {
ret |= o_flag;
}
};
set_if_has_flag(O_APPEND, open_flags::append);
set_if_has_flag(O_ASYNC, open_flags::async);
set_if_has_flag(O_CLOEXEC, open_flags::clo_exec);
set_if_has_flag(O_CREAT, open_flags::create);
#ifdef O_DIRECT
set_if_has_flag(O_DIRECT, open_flags::direct);
#endif
set_if_has_flag(O_DIRECTORY, open_flags::directory);
#ifdef O_DSYNC
set_if_has_flag(O_DSYNC, open_flags::dsync);
#endif
set_if_has_flag(O_EXCL, open_flags::excl);
#ifdef O_NOATIME
set_if_has_flag(O_NOATIME, open_flags::no_atime);
#endif
set_if_has_flag(O_NOCTTY, open_flags::no_ctty);
set_if_has_flag(O_NOFOLLOW, open_flags::no_follow);
set_if_has_flag(O_NONBLOCK, open_flags::non_blocking);
#ifdef O_PATH
set_if_has_flag(O_PATH, open_flags::path);
#endif
set_if_has_flag(O_SYNC, open_flags::sync);
#ifdef O_TMPFILE
set_if_has_flag(O_TMPFILE, open_flags::temp_file);
#endif
set_if_has_flag(O_TRUNC, open_flags::truncate);
return ret;
}
auto create_os_open_flags(const open_flags &flags) -> std::uint32_t {
std::uint32_t ret{};
const auto set_if_has_flag = [&flags, &ret](auto o_flag, auto flag) -> bool {
if ((flags & o_flag) == o_flag) {
ret |= static_cast<std::uint32_t>(flag);
return true;
}
return false;
};
if (not set_if_has_flag(open_flags::read_write, O_RDWR)) {
if (not set_if_has_flag(open_flags::write_only, O_WRONLY)) {
ret |= static_cast<std::uint32_t>(O_RDONLY);
}
}
set_if_has_flag(open_flags::append, O_APPEND);
set_if_has_flag(open_flags::async, O_ASYNC);
set_if_has_flag(open_flags::clo_exec, O_CLOEXEC);
set_if_has_flag(open_flags::create, O_CREAT);
#ifdef O_DIRECT
set_if_has_flag(open_flags::direct, O_DIRECT);
#endif
set_if_has_flag(open_flags::directory, O_DIRECTORY);
#ifdef O_DSYNC
set_if_has_flag(open_flags::dsync, O_DSYNC);
#endif
set_if_has_flag(open_flags::excl, O_EXCL);
#ifdef O_NOATIME
set_if_has_flag(open_flags::no_atime, O_NOATIME);
#endif
set_if_has_flag(open_flags::no_ctty, O_NOCTTY);
set_if_has_flag(open_flags::no_follow, O_NOFOLLOW);
set_if_has_flag(open_flags::non_blocking, O_NONBLOCK);
#ifdef O_PATH
set_if_has_flag(open_flags::path, O_PATH);
#endif
set_if_has_flag(open_flags::sync, O_SYNC);
#ifdef O_TMPFILE
set_if_has_flag(open_flags::temp_file, O_TMPFILE);
#endif
set_if_has_flag(open_flags::truncate, O_TRUNC);
return ret;
}
#endif
} // namespace repertory::remote

View File

@@ -1,89 +1,89 @@
/*
Copyright <2018-2023> <scott.e.graves@protonmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "types/repertory.hpp"
#include "types/startup_exception.hpp"
namespace repertory {
static const std::unordered_map<api_error, std::string> LOOKUP = {
{api_error::success, "success"},
{api_error::access_denied, "access_denied"},
{api_error::bad_address, "bad_address"},
{api_error::buffer_overflow, "buffer_overflow"},
{api_error::buffer_too_small, "buffer_too_small"},
{api_error::comm_error, "comm_error"},
{api_error::decryption_error, "decryption_error"},
{api_error::directory_end_of_files, "directory_end_of_files"},
{api_error::directory_exists, "directory_exists"},
{api_error::directory_not_empty, "directory_not_empty"},
{api_error::directory_not_found, "directory_not_found"},
{api_error::download_failed, "download_failed"},
{api_error::download_incomplete, "download_incomplete"},
{api_error::download_stopped, "download_stopped"},
{api_error::empty_ring_buffer_chunk_size, "empty_ring_buffer_chunk_size"},
{api_error::empty_ring_buffer_size, "empty_ring_buffer_size"},
{api_error::error, "error"},
{api_error::file_in_use, "file_in_use"},
{api_error::file_size_mismatch, "file_size_mismatch"},
{api_error::incompatible_version, "incompatible_version"},
{api_error::invalid_handle, "invalid_handle"},
{api_error::invalid_operation, "invalid_operation"},
{api_error::invalid_ring_buffer_multiple, "invalid_ring_buffer_multiple"},
{api_error::invalid_ring_buffer_size, "invalid_ring_buffer_size"},
{api_error::invalid_version, "invalid_version"},
{api_error::item_exists, "item_exists"},
{api_error::item_not_found, "item_not_found"},
{api_error::no_disk_space, "no_disk_space"},
{api_error::not_implemented, "not_implemented"},
{api_error::not_supported, "not_supported"},
{api_error::os_error, "os_error"},
{api_error::out_of_memory, "out_of_memory"},
{api_error::permission_denied, "permission_denied"},
{api_error::upload_failed, "upload_failed"},
{api_error::upload_stopped, "upload_stopped"},
{api_error::xattr_buffer_small, "xattr_buffer_small"},
{api_error::xattr_exists, "xattr_exists"},
{api_error::xattr_not_found, "xattr_not_found"},
{api_error::xattr_too_big, "xattr_too_big"},
};
auto api_error_from_string(std::string_view s) -> api_error {
if (LOOKUP.size() != static_cast<std::size_t>(api_error::ERROR_COUNT)) {
throw startup_exception("undefined api_error strings");
}
const auto it =
std::find_if(LOOKUP.begin(), LOOKUP.end(),
[&s](const std::pair<api_error, std::string> &kv) -> bool {
return kv.second == s;
});
return it == LOOKUP.end() ? api_error::error : it->first;
}
auto api_error_to_string(const api_error &error) -> const std::string & {
if (LOOKUP.size() != static_cast<std::size_t>(api_error::ERROR_COUNT)) {
throw startup_exception("undefined api_error strings");
}
return LOOKUP.at(error);
}
} // namespace repertory
/*
Copyright <2018-2023> <scott.e.graves@protonmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "types/repertory.hpp"
#include "types/startup_exception.hpp"
namespace repertory {
static const std::unordered_map<api_error, std::string> LOOKUP = {
{api_error::success, "success"},
{api_error::access_denied, "access_denied"},
{api_error::bad_address, "bad_address"},
{api_error::buffer_overflow, "buffer_overflow"},
{api_error::buffer_too_small, "buffer_too_small"},
{api_error::comm_error, "comm_error"},
{api_error::decryption_error, "decryption_error"},
{api_error::directory_end_of_files, "directory_end_of_files"},
{api_error::directory_exists, "directory_exists"},
{api_error::directory_not_empty, "directory_not_empty"},
{api_error::directory_not_found, "directory_not_found"},
{api_error::download_failed, "download_failed"},
{api_error::download_incomplete, "download_incomplete"},
{api_error::download_stopped, "download_stopped"},
{api_error::empty_ring_buffer_chunk_size, "empty_ring_buffer_chunk_size"},
{api_error::empty_ring_buffer_size, "empty_ring_buffer_size"},
{api_error::error, "error"},
{api_error::file_in_use, "file_in_use"},
{api_error::file_size_mismatch, "file_size_mismatch"},
{api_error::incompatible_version, "incompatible_version"},
{api_error::invalid_handle, "invalid_handle"},
{api_error::invalid_operation, "invalid_operation"},
{api_error::invalid_ring_buffer_multiple, "invalid_ring_buffer_multiple"},
{api_error::invalid_ring_buffer_size, "invalid_ring_buffer_size"},
{api_error::invalid_version, "invalid_version"},
{api_error::item_exists, "item_exists"},
{api_error::item_not_found, "item_not_found"},
{api_error::no_disk_space, "no_disk_space"},
{api_error::not_implemented, "not_implemented"},
{api_error::not_supported, "not_supported"},
{api_error::os_error, "os_error"},
{api_error::out_of_memory, "out_of_memory"},
{api_error::permission_denied, "permission_denied"},
{api_error::upload_failed, "upload_failed"},
{api_error::upload_stopped, "upload_stopped"},
{api_error::xattr_buffer_small, "xattr_buffer_small"},
{api_error::xattr_exists, "xattr_exists"},
{api_error::xattr_not_found, "xattr_not_found"},
{api_error::xattr_too_big, "xattr_too_big"},
};
auto api_error_from_string(std::string_view s) -> api_error {
if (LOOKUP.size() != static_cast<std::size_t>(api_error::ERROR_COUNT)) {
throw startup_exception("undefined api_error strings");
}
const auto it =
std::find_if(LOOKUP.begin(), LOOKUP.end(),
[&s](const std::pair<api_error, std::string> &kv) -> bool {
return kv.second == s;
});
return it == LOOKUP.end() ? api_error::error : it->first;
}
auto api_error_to_string(const api_error &error) -> const std::string & {
if (LOOKUP.size() != static_cast<std::size_t>(api_error::ERROR_COUNT)) {
throw startup_exception("undefined api_error strings");
}
return LOOKUP.at(error);
}
} // namespace repertory

View File

@@ -1,21 +1,21 @@
#include "utils/action_queue.hpp"
#include "types/repertory.hpp"
namespace repertory::utils::action_queue {
action_queue::action_queue(const std::string &id,
std::uint8_t max_concurrent_actions)
: single_thread_service_base("action_queue_" + id),
id_(id),
max_concurrent_actions_(max_concurrent_actions) {}
void action_queue::service_function() {
//
}
void action_queue::push(std::function<void()> action) {
unique_mutex_lock queue_lock(queue_mtx_);
queue_.emplace_back(action);
queue_notify_.notify_all();
}
} // namespace repertory::utils::action_queue
#include "utils/action_queue.hpp"
#include "types/repertory.hpp"
namespace repertory::utils::action_queue {
action_queue::action_queue(const std::string &id,
std::uint8_t max_concurrent_actions)
: single_thread_service_base("action_queue_" + id),
id_(id),
max_concurrent_actions_(max_concurrent_actions) {}
void action_queue::service_function() {
//
}
void action_queue::push(std::function<void()> action) {
unique_mutex_lock queue_lock(queue_mtx_);
queue_.emplace_back(action);
queue_notify_.notify_all();
}
} // namespace repertory::utils::action_queue

View File

@@ -1,266 +1,266 @@
/*
Copyright <2018-2023> <scott.e.graves@protonmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "utils/cli_utils.hpp"
#include "app_config.hpp"
#include "utils/file_utils.hpp"
#include "utils/path_utils.hpp"
#include "utils/string_utils.hpp"
#include "utils/utils.hpp"
namespace repertory::utils::cli {
void get_api_authentication_data(std::string &user, std::string &password,
std::uint16_t &port, const provider_type &prov,
const std::string &data_directory) {
const auto cfg_file_path = utils::path::combine(
data_directory.empty() ? app_config::default_data_directory(prov)
: data_directory,
{"config.json"});
json data;
const auto success = utils::retryable_action([&]() -> bool {
return utils::file::read_json_file(cfg_file_path, data);
});
if (success) {
if (user.empty() && password.empty()) {
password = data["ApiAuth"].get<std::string>();
user = data["ApiUser"].get<std::string>();
}
port = data["ApiPort"].get<std::uint16_t>();
}
}
[[nodiscard]] auto get_provider_type_from_args(std::vector<const char *> args)
-> provider_type {
#if defined(REPERTORY_ENABLE_S3)
if (has_option(args, options::s3_option)) {
return provider_type::s3;
}
#endif // defined(REPERTORY_ENABLE_S3)
if (has_option(args, options::remote_mount_option)) {
return provider_type::remote;
}
if (has_option(args, options::encrypt_option)) {
return provider_type::encrypt;
}
return provider_type::sia;
}
auto has_option(std::vector<const char *> args, const std::string &option_name)
-> bool {
return std::find_if(args.begin(), args.end(),
[&option_name](const auto &value) -> bool {
return option_name == value;
}) != args.end();
}
auto has_option(std::vector<const char *> args, const option &opt) -> bool {
return has_option(args, opt.at(0U)) || has_option(args, opt.at(1U));
}
auto parse_option(std::vector<const char *> args,
const std::string &option_name, std::uint8_t count)
-> std::vector<std::string> {
std::vector<std::string> ret;
auto found{false};
for (std::size_t i = 0U; not found && (i < args.size()); i++) {
if ((found = (option_name == args.at(i)))) {
if ((++i + count) <= args.size()) {
while ((count--) != 0U) {
ret.emplace_back(args.at(i++));
}
}
}
}
return ret;
}
auto parse_string_option(std::vector<const char *> args, const option &opt,
std::string &value) -> exit_code {
exit_code ret{exit_code::success};
if (has_option(args, opt.at(0U)) || has_option(args, opt.at(1U))) {
auto data = parse_option(args, opt.at(0U), 1U);
if (data.empty()) {
data = parse_option(args, opt.at(1U), 1U);
if (data.empty()) {
std::cerr << "Invalid syntax for '" << opt.at(0U) << "," << opt.at(1U)
<< '\'' << std::endl;
ret = exit_code::invalid_syntax;
}
}
if (not data.empty()) {
value = data.at(0U);
}
}
return ret;
}
auto parse_drive_options(std::vector<const char *> args,
[[maybe_unused]] provider_type &prov,
[[maybe_unused]] std::string &data_directory)
-> std::vector<std::string> {
// Strip out options from command line
const auto &option_list = options::option_list;
std::vector<std::string> drive_args;
for (std::size_t i = 0U; i < args.size(); i++) {
const auto &arg = args.at(i);
if (std::find_if(option_list.begin(), option_list.end(),
[&arg](const auto &pair) -> bool {
return ((pair.at(0U) == arg) || (pair.at(1U) == arg));
}) == option_list.end()) {
drive_args.emplace_back(args.at(i));
continue;
}
if ((std::string(args.at(i)) == options::remote_mount_option.at(0U)) ||
(std::string(args.at(i)) == options::remote_mount_option.at(1U)) ||
(std::string(args.at(i)) == options::data_directory_option.at(0U)) ||
(std::string(args.at(i)) == options::data_directory_option.at(1U))
#ifdef REPERTORY_ENABLE_S3
|| (std::string(args.at(i)) == options::name_option.at(0U)) ||
(std::string(args.at(i)) == options::name_option.at(1U))
#endif // REPERTORY_ENABLE_S3
) {
i++;
continue;
}
}
#ifndef _WIN32
std::vector<std::string> fuse_flags_list;
for (std::size_t i = 1; i < drive_args.size(); i++) {
if (drive_args.at(i).find("-o") == 0) {
std::string options;
if (drive_args[i].size() == 2) {
if ((i + 1) < drive_args.size()) {
options = drive_args.at(++i);
}
} else {
options = drive_args.at(i).substr(2);
}
const auto fuse_option_list = utils::string::split(options, ',');
for (const auto &fuse_option : fuse_option_list) {
#if defined(REPERTORY_ENABLE_S3)
if (fuse_option.find("s3") == 0) {
prov = provider_type::s3;
continue;
}
#endif // defined(REPERTORY_ENABLE_S3)
if ((fuse_option.find("dd") == 0) ||
(fuse_option.find("data_directory") == 0)) {
const auto data = utils::string::split(fuse_option, '=');
if (data.size() == 2U) {
data_directory = data.at(1U);
} else {
std::cerr << "Invalid syntax for '-dd,--data_directory'"
<< std::endl;
exit(3);
}
continue;
}
fuse_flags_list.push_back(fuse_option);
}
}
}
{
std::vector<std::string> new_drive_args(drive_args);
for (std::size_t i = 1; i < new_drive_args.size(); i++) {
if (new_drive_args[i].find("-o") == 0) {
if (new_drive_args[i].size() == 2) {
if ((i + 1) < drive_args.size()) {
utils::remove_element_from(new_drive_args, new_drive_args[i]);
utils::remove_element_from(new_drive_args, new_drive_args[i]);
i--;
}
continue;
}
utils::remove_element_from(new_drive_args, new_drive_args[i]);
i--;
continue;
}
}
#if FUSE_USE_VERSION < 30
{
auto it = std::remove_if(
fuse_flags_list.begin(), fuse_flags_list.end(),
[](const auto &opt) -> bool { return opt.find("hard_remove") == 0; });
if (it == fuse_flags_list.end()) {
fuse_flags_list.emplace_back("hard_remove");
}
}
#endif
#ifdef __APPLE__
{
auto it = std::remove_if(
fuse_flags_list.begin(), fuse_flags_list.end(),
[](const auto &opt) -> bool { return opt.find("volname") == 0; });
if (it != fuse_flags_list.end()) {
fuse_flags_list.erase(it, fuse_flags_list.end());
}
fuse_flags_list.emplace_back("volname=Repertory" +
app_config::get_provider_display_name(prov));
it = std::remove_if(fuse_flags_list.begin(), fuse_flags_list.end(),
[](const auto &opt) -> bool {
return opt.find("daemon_timeout") == 0;
});
if (it != fuse_flags_list.end()) {
fuse_flags_list.erase(it, fuse_flags_list.end());
}
fuse_flags_list.emplace_back("daemon_timeout=300");
it = std::remove_if(fuse_flags_list.begin(), fuse_flags_list.end(),
[](const auto &opt) -> bool {
return opt.find("noappledouble") == 0;
});
if (it == fuse_flags_list.end()) {
fuse_flags_list.emplace_back("noappledouble");
}
}
#endif // __APPLE__
if (not fuse_flags_list.empty()) {
std::string fuse_options;
for (const auto &flag : fuse_flags_list) {
fuse_options += (fuse_options.empty()) ? flag : (',' + flag);
}
new_drive_args.emplace_back("-o");
new_drive_args.emplace_back(fuse_options);
}
drive_args = std::move(new_drive_args);
}
#endif // _WIN32
return drive_args;
}
} // namespace repertory::utils::cli
/*
Copyright <2018-2023> <scott.e.graves@protonmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "utils/cli_utils.hpp"
#include "app_config.hpp"
#include "utils/file_utils.hpp"
#include "utils/path_utils.hpp"
#include "utils/string_utils.hpp"
#include "utils/utils.hpp"
namespace repertory::utils::cli {
void get_api_authentication_data(std::string &user, std::string &password,
std::uint16_t &port, const provider_type &prov,
const std::string &data_directory) {
const auto cfg_file_path = utils::path::combine(
data_directory.empty() ? app_config::default_data_directory(prov)
: data_directory,
{"config.json"});
json data;
const auto success = utils::retryable_action([&]() -> bool {
return utils::file::read_json_file(cfg_file_path, data);
});
if (success) {
if (user.empty() && password.empty()) {
password = data["ApiAuth"].get<std::string>();
user = data["ApiUser"].get<std::string>();
}
port = data["ApiPort"].get<std::uint16_t>();
}
}
[[nodiscard]] auto get_provider_type_from_args(std::vector<const char *> args)
-> provider_type {
#if defined(REPERTORY_ENABLE_S3)
if (has_option(args, options::s3_option)) {
return provider_type::s3;
}
#endif // defined(REPERTORY_ENABLE_S3)
if (has_option(args, options::remote_mount_option)) {
return provider_type::remote;
}
if (has_option(args, options::encrypt_option)) {
return provider_type::encrypt;
}
return provider_type::sia;
}
auto has_option(std::vector<const char *> args, const std::string &option_name)
-> bool {
return std::find_if(args.begin(), args.end(),
[&option_name](const auto &value) -> bool {
return option_name == value;
}) != args.end();
}
auto has_option(std::vector<const char *> args, const option &opt) -> bool {
return has_option(args, opt.at(0U)) || has_option(args, opt.at(1U));
}
auto parse_option(std::vector<const char *> args,
const std::string &option_name, std::uint8_t count)
-> std::vector<std::string> {
std::vector<std::string> ret;
auto found{false};
for (std::size_t i = 0U; not found && (i < args.size()); i++) {
if ((found = (option_name == args.at(i)))) {
if ((++i + count) <= args.size()) {
while ((count--) != 0U) {
ret.emplace_back(args.at(i++));
}
}
}
}
return ret;
}
auto parse_string_option(std::vector<const char *> args, const option &opt,
std::string &value) -> exit_code {
exit_code ret{exit_code::success};
if (has_option(args, opt.at(0U)) || has_option(args, opt.at(1U))) {
auto data = parse_option(args, opt.at(0U), 1U);
if (data.empty()) {
data = parse_option(args, opt.at(1U), 1U);
if (data.empty()) {
std::cerr << "Invalid syntax for '" << opt.at(0U) << "," << opt.at(1U)
<< '\'' << std::endl;
ret = exit_code::invalid_syntax;
}
}
if (not data.empty()) {
value = data.at(0U);
}
}
return ret;
}
auto parse_drive_options(std::vector<const char *> args,
[[maybe_unused]] provider_type &prov,
[[maybe_unused]] std::string &data_directory)
-> std::vector<std::string> {
// Strip out options from command line
const auto &option_list = options::option_list;
std::vector<std::string> drive_args;
for (std::size_t i = 0U; i < args.size(); i++) {
const auto &arg = args.at(i);
if (std::find_if(option_list.begin(), option_list.end(),
[&arg](const auto &pair) -> bool {
return ((pair.at(0U) == arg) || (pair.at(1U) == arg));
}) == option_list.end()) {
drive_args.emplace_back(args.at(i));
continue;
}
if ((std::string(args.at(i)) == options::remote_mount_option.at(0U)) ||
(std::string(args.at(i)) == options::remote_mount_option.at(1U)) ||
(std::string(args.at(i)) == options::data_directory_option.at(0U)) ||
(std::string(args.at(i)) == options::data_directory_option.at(1U))
#ifdef REPERTORY_ENABLE_S3
|| (std::string(args.at(i)) == options::name_option.at(0U)) ||
(std::string(args.at(i)) == options::name_option.at(1U))
#endif // REPERTORY_ENABLE_S3
) {
i++;
continue;
}
}
#ifndef _WIN32
std::vector<std::string> fuse_flags_list;
for (std::size_t i = 1; i < drive_args.size(); i++) {
if (drive_args.at(i).find("-o") == 0) {
std::string options;
if (drive_args[i].size() == 2) {
if ((i + 1) < drive_args.size()) {
options = drive_args.at(++i);
}
} else {
options = drive_args.at(i).substr(2);
}
const auto fuse_option_list = utils::string::split(options, ',');
for (const auto &fuse_option : fuse_option_list) {
#if defined(REPERTORY_ENABLE_S3)
if (fuse_option.find("s3") == 0) {
prov = provider_type::s3;
continue;
}
#endif // defined(REPERTORY_ENABLE_S3)
if ((fuse_option.find("dd") == 0) ||
(fuse_option.find("data_directory") == 0)) {
const auto data = utils::string::split(fuse_option, '=');
if (data.size() == 2U) {
data_directory = data.at(1U);
} else {
std::cerr << "Invalid syntax for '-dd,--data_directory'"
<< std::endl;
exit(3);
}
continue;
}
fuse_flags_list.push_back(fuse_option);
}
}
}
{
std::vector<std::string> new_drive_args(drive_args);
for (std::size_t i = 1; i < new_drive_args.size(); i++) {
if (new_drive_args[i].find("-o") == 0) {
if (new_drive_args[i].size() == 2) {
if ((i + 1) < drive_args.size()) {
utils::remove_element_from(new_drive_args, new_drive_args[i]);
utils::remove_element_from(new_drive_args, new_drive_args[i]);
i--;
}
continue;
}
utils::remove_element_from(new_drive_args, new_drive_args[i]);
i--;
continue;
}
}
#if FUSE_USE_VERSION < 30
{
auto it = std::remove_if(
fuse_flags_list.begin(), fuse_flags_list.end(),
[](const auto &opt) -> bool { return opt.find("hard_remove") == 0; });
if (it == fuse_flags_list.end()) {
fuse_flags_list.emplace_back("hard_remove");
}
}
#endif
#ifdef __APPLE__
{
auto it = std::remove_if(
fuse_flags_list.begin(), fuse_flags_list.end(),
[](const auto &opt) -> bool { return opt.find("volname") == 0; });
if (it != fuse_flags_list.end()) {
fuse_flags_list.erase(it, fuse_flags_list.end());
}
fuse_flags_list.emplace_back("volname=Repertory" +
app_config::get_provider_display_name(prov));
it = std::remove_if(fuse_flags_list.begin(), fuse_flags_list.end(),
[](const auto &opt) -> bool {
return opt.find("daemon_timeout") == 0;
});
if (it != fuse_flags_list.end()) {
fuse_flags_list.erase(it, fuse_flags_list.end());
}
fuse_flags_list.emplace_back("daemon_timeout=300");
it = std::remove_if(fuse_flags_list.begin(), fuse_flags_list.end(),
[](const auto &opt) -> bool {
return opt.find("noappledouble") == 0;
});
if (it == fuse_flags_list.end()) {
fuse_flags_list.emplace_back("noappledouble");
}
}
#endif // __APPLE__
if (not fuse_flags_list.empty()) {
std::string fuse_options;
for (const auto &flag : fuse_flags_list) {
fuse_options += (fuse_options.empty()) ? flag : (',' + flag);
}
new_drive_args.emplace_back("-o");
new_drive_args.emplace_back(fuse_options);
}
drive_args = std::move(new_drive_args);
}
#endif // _WIN32
return drive_args;
}
} // namespace repertory::utils::cli

View File

@@ -1,400 +1,400 @@
/*
Copyright <2018-2023> <scott.e.graves@protonmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "utils/encrypting_reader.hpp"
#include "events/event_system.hpp"
#include "events/events.hpp"
#include "types/repertory.hpp"
#include "utils/encryption.hpp"
#include "utils/error_utils.hpp"
#include "utils/utils.hpp"
namespace repertory::utils::encryption {
class encrypting_streambuf final : public encrypting_reader::streambuf {
public:
encrypting_streambuf(const encrypting_streambuf &) = default;
encrypting_streambuf(encrypting_streambuf &&) = delete;
auto operator=(const encrypting_streambuf &)
-> encrypting_streambuf & = delete;
auto operator=(encrypting_streambuf &&) -> encrypting_streambuf & = delete;
explicit encrypting_streambuf(const encrypting_reader &reader)
: reader_(reader) {
setg(reinterpret_cast<char *>(0), reinterpret_cast<char *>(0),
reinterpret_cast<char *>(reader_.get_total_size()));
}
~encrypting_streambuf() override = default;
private:
encrypting_reader reader_;
protected:
auto seekoff(off_type off, std::ios_base::seekdir dir,
std::ios_base::openmode which = std::ios_base::out |
std::ios_base::in)
-> pos_type override {
if ((which & std::ios_base::in) != std::ios_base::in) {
throw std::runtime_error("output is not supported");
}
const auto set_position = [this](char *next) -> pos_type {
if ((next <= egptr()) && (next >= eback())) {
setg(eback(), next, reinterpret_cast<char *>(reader_.get_total_size()));
return static_cast<std::streamoff>(
reinterpret_cast<std::uintptr_t>(gptr()));
}
return {traits_type::eof()};
};
switch (dir) {
case std::ios_base::beg:
return set_position(eback() + off);
case std::ios_base::cur:
return set_position(gptr() + off);
case std::ios_base::end:
return set_position(egptr() + off);
}
return encrypting_reader::streambuf::seekoff(off, dir, which);
}
auto seekpos(pos_type pos,
std::ios_base::openmode which = std::ios_base::out |
std::ios_base::in)
-> pos_type override {
return seekoff(pos, std::ios_base::beg, which);
}
auto uflow() -> int_type override {
auto ret = underflow();
if (ret == traits_type::eof()) {
return ret;
}
gbump(1);
return ret;
}
auto underflow() -> int_type override {
if (gptr() == egptr()) {
return traits_type::eof();
}
reader_.set_read_position(
static_cast<std::uint64_t>(reinterpret_cast<std::uintptr_t>(gptr())));
char c{};
const auto res = encrypting_reader::reader_function(&c, 1U, 1U, &reader_);
if (res != 1) {
return traits_type::eof();
}
return c;
}
auto xsgetn(char *ptr, std::streamsize count) -> std::streamsize override {
if (gptr() == egptr()) {
return traits_type::eof();
}
reader_.set_read_position(
static_cast<std::uint64_t>(reinterpret_cast<std::uintptr_t>(gptr())));
const auto res = encrypting_reader::reader_function(
ptr, 1U, static_cast<std::size_t>(count), &reader_);
if ((res == reader_.get_error_return()) ||
(reader_.get_stop_requested() && (res == CURL_READFUNC_ABORT))) {
return traits_type::eof();
}
setg(eback(), gptr() + res,
reinterpret_cast<char *>(reader_.get_total_size()));
return static_cast<std::streamsize>(res);
}
};
class encrypting_reader_iostream final : public encrypting_reader::iostream {
public:
encrypting_reader_iostream(const encrypting_reader_iostream &) = delete;
encrypting_reader_iostream(encrypting_reader_iostream &&) = delete;
auto operator=(const encrypting_reader_iostream &)
-> encrypting_reader_iostream & = delete;
auto operator=(encrypting_reader_iostream &&)
-> encrypting_reader_iostream & = delete;
explicit encrypting_reader_iostream(
std::unique_ptr<encrypting_streambuf> buffer)
: encrypting_reader::iostream(buffer.get()), buffer_(std::move(buffer)) {}
~encrypting_reader_iostream() override = default;
private:
std::unique_ptr<encrypting_streambuf> buffer_;
};
const std::size_t encrypting_reader::header_size_ = ([]() {
return crypto_aead_xchacha20poly1305_IETF_NPUBBYTES +
crypto_aead_xchacha20poly1305_IETF_ABYTES;
})();
const std::size_t encrypting_reader::data_chunk_size_ = (8UL * 1024UL * 1024UL);
const std::size_t encrypting_reader::encrypted_chunk_size_ =
data_chunk_size_ + header_size_;
encrypting_reader::encrypting_reader(
const std::string &file_name, const std::string &source_path,
stop_type &stop_requested, const std::string &token,
std::optional<std::string> relative_parent_path, std::size_t error_return)
: key_(utils::encryption::generate_key(token)),
stop_requested_(stop_requested),
error_return_(error_return) {
const auto res = native_file::create_or_open(
source_path, not relative_parent_path.has_value(), source_file_);
if (res != api_error::success) {
throw std::runtime_error("file open failed|src|" + source_path + '|' +
api_error_to_string(res));
}
data_buffer result;
utils::encryption::encrypt_data(key_, file_name.c_str(),
strnlen(file_name.c_str(), file_name.size()),
result);
encrypted_file_name_ = utils::to_hex_string(result);
if (relative_parent_path.has_value()) {
for (const auto &part :
std::filesystem::path(relative_parent_path.value())) {
utils::encryption::encrypt_data(
key_, part.string().c_str(),
strnlen(part.string().c_str(), part.string().size()), result);
encrypted_file_path_ += '/' + utils::to_hex_string(result);
}
encrypted_file_path_ += '/' + encrypted_file_name_;
}
std::uint64_t file_size{};
if (not utils::file::get_file_size(source_path, file_size)) {
throw std::runtime_error("get file size failed|src|" + source_path + '|' +
std::to_string(utils::get_last_error_code()));
}
const auto total_chunks = 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_) == 0U ? data_chunk_size_
: file_size % data_chunk_size_);
iv_list_.resize(total_chunks);
for (auto &iv : iv_list_) {
randombytes_buf(iv.data(), iv.size());
}
}
encrypting_reader::encrypting_reader(const std::string &encrypted_file_path,
const std::string &source_path,
stop_type &stop_requested,
const std::string &token,
std::size_t error_return)
: key_(utils::encryption::generate_key(token)),
stop_requested_(stop_requested),
error_return_(error_return) {
const auto res =
native_file::create_or_open(source_path, false, source_file_);
if (res != api_error::success) {
throw std::runtime_error("file open failed|src|" + source_path + '|' +
api_error_to_string(res));
}
encrypted_file_path_ = encrypted_file_path;
encrypted_file_name_ =
std::filesystem::path(encrypted_file_path_).filename().string();
std::uint64_t file_size{};
if (not utils::file::get_file_size(source_path, file_size)) {
throw std::runtime_error("get file size failed|src|" + source_path + '|' +
std::to_string(utils::get_last_error_code()));
}
const auto total_chunks = 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_) == 0U ? data_chunk_size_
: file_size % data_chunk_size_);
iv_list_.resize(total_chunks);
for (auto &iv : iv_list_) {
randombytes_buf(iv.data(), iv.size());
}
}
encrypting_reader::encrypting_reader(
const std::string &encrypted_file_path, const std::string &source_path,
stop_type &stop_requested, const std::string &token,
std::vector<
std::array<unsigned char, crypto_aead_xchacha20poly1305_IETF_NPUBBYTES>>
iv_list,
std::size_t error_return)
: key_(utils::encryption::generate_key(token)),
stop_requested_(stop_requested),
error_return_(error_return) {
const auto res =
native_file::create_or_open(source_path, false, source_file_);
if (res != api_error::success) {
throw std::runtime_error("file open failed|src|" + source_path + '|' +
api_error_to_string(res));
}
encrypted_file_path_ = encrypted_file_path;
encrypted_file_name_ =
std::filesystem::path(encrypted_file_path_).filename().string();
std::uint64_t file_size{};
if (not utils::file::get_file_size(source_path, file_size)) {
throw std::runtime_error("get file size failed|src|" + source_path + '|' +
std::to_string(utils::get_last_error_code()));
}
const auto total_chunks = 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_) == 0U ? data_chunk_size_
: file_size % data_chunk_size_);
iv_list_ = std::move(iv_list);
}
encrypting_reader::encrypting_reader(const encrypting_reader &reader)
: key_(reader.key_),
stop_requested_(reader.stop_requested_),
error_return_(reader.error_return_),
chunk_buffers_(reader.chunk_buffers_),
encrypted_file_name_(reader.encrypted_file_name_),
encrypted_file_path_(reader.encrypted_file_path_),
iv_list_(reader.iv_list_),
last_data_chunk_(reader.last_data_chunk_),
last_data_chunk_size_(reader.last_data_chunk_size_),
read_offset_(reader.read_offset_),
source_file_(native_file::clone(reader.source_file_)),
total_size_(reader.total_size_) {}
encrypting_reader::~encrypting_reader() {
if (source_file_) {
source_file_->close();
}
}
auto encrypting_reader::calculate_decrypted_size(std::uint64_t total_size)
-> std::uint64_t {
return total_size - (utils::divide_with_ceiling(
total_size, static_cast<std::uint64_t>(
get_encrypted_chunk_size())) *
get_header_size());
}
auto encrypting_reader::calculate_encrypted_size(const std::string &source_path)
-> std::uint64_t {
std::uint64_t file_size{};
if (not utils::file::get_file_size(source_path, file_size)) {
throw std::runtime_error("get file size failed|src|" + source_path + '|' +
std::to_string(utils::get_last_error_code()));
}
const auto total_chunks = static_cast<std::size_t>(utils::divide_with_ceiling(
file_size, static_cast<std::uint64_t>(data_chunk_size_)));
return file_size + (total_chunks * encrypting_reader::get_header_size());
}
auto encrypting_reader::create_iostream() const
-> std::shared_ptr<encrypting_reader::iostream> {
return std::make_shared<encrypting_reader_iostream>(
std::make_unique<encrypting_streambuf>(*this));
}
auto encrypting_reader::reader_function(char *buffer, size_t size,
size_t nitems) -> size_t {
const auto read_size = static_cast<std::size_t>(std::min(
static_cast<std::uint64_t>(size * nitems), total_size_ - read_offset_));
auto chunk = static_cast<std::size_t>(read_offset_ / encrypted_chunk_size_);
auto chunk_offset =
static_cast<std::size_t>(read_offset_ % encrypted_chunk_size_);
std::size_t total_read = 0u;
auto ret = false;
if (read_offset_ < total_size_) {
try {
ret = true;
auto remain = read_size;
while (not stop_requested_ && ret && remain) {
if (chunk_buffers_.find(chunk) == chunk_buffers_.end()) {
auto &chunk_buffer = chunk_buffers_[chunk];
data_buffer file_data(chunk == last_data_chunk_
? last_data_chunk_size_
: data_chunk_size_);
chunk_buffer.resize(file_data.size() + get_header_size());
std::size_t bytes_read{};
if ((ret = source_file_->read_bytes(&file_data[0u], file_data.size(),
chunk * data_chunk_size_,
bytes_read))) {
utils::encryption::encrypt_data(iv_list_.at(chunk), key_, file_data,
chunk_buffer);
}
} else if (chunk) {
chunk_buffers_.erase(chunk - 1u);
}
auto &chunk_buffer = chunk_buffers_[chunk];
const auto to_read =
std::min(chunk_buffer.size() - chunk_offset, remain);
std::memcpy(buffer + total_read, &chunk_buffer[chunk_offset], to_read);
total_read += to_read;
remain -= to_read;
chunk_offset = 0u;
chunk++;
read_offset_ += to_read;
}
} catch (const std::exception &e) {
utils::error::raise_error(__FUNCTION__, e, "exception occurred");
ret = false;
}
}
return stop_requested_ ? CURL_READFUNC_ABORT
: ret ? total_read
: error_return_;
}
} // namespace repertory::utils::encryption
/*
Copyright <2018-2023> <scott.e.graves@protonmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "utils/encrypting_reader.hpp"
#include "events/event_system.hpp"
#include "events/events.hpp"
#include "types/repertory.hpp"
#include "utils/encryption.hpp"
#include "utils/error_utils.hpp"
#include "utils/utils.hpp"
namespace repertory::utils::encryption {
class encrypting_streambuf final : public encrypting_reader::streambuf {
public:
encrypting_streambuf(const encrypting_streambuf &) = default;
encrypting_streambuf(encrypting_streambuf &&) = delete;
auto operator=(const encrypting_streambuf &)
-> encrypting_streambuf & = delete;
auto operator=(encrypting_streambuf &&) -> encrypting_streambuf & = delete;
explicit encrypting_streambuf(const encrypting_reader &reader)
: reader_(reader) {
setg(reinterpret_cast<char *>(0), reinterpret_cast<char *>(0),
reinterpret_cast<char *>(reader_.get_total_size()));
}
~encrypting_streambuf() override = default;
private:
encrypting_reader reader_;
protected:
auto seekoff(off_type off, std::ios_base::seekdir dir,
std::ios_base::openmode which = std::ios_base::out |
std::ios_base::in)
-> pos_type override {
if ((which & std::ios_base::in) != std::ios_base::in) {
throw std::runtime_error("output is not supported");
}
const auto set_position = [this](char *next) -> pos_type {
if ((next <= egptr()) && (next >= eback())) {
setg(eback(), next, reinterpret_cast<char *>(reader_.get_total_size()));
return static_cast<std::streamoff>(
reinterpret_cast<std::uintptr_t>(gptr()));
}
return {traits_type::eof()};
};
switch (dir) {
case std::ios_base::beg:
return set_position(eback() + off);
case std::ios_base::cur:
return set_position(gptr() + off);
case std::ios_base::end:
return set_position(egptr() + off);
}
return encrypting_reader::streambuf::seekoff(off, dir, which);
}
auto seekpos(pos_type pos,
std::ios_base::openmode which = std::ios_base::out |
std::ios_base::in)
-> pos_type override {
return seekoff(pos, std::ios_base::beg, which);
}
auto uflow() -> int_type override {
auto ret = underflow();
if (ret == traits_type::eof()) {
return ret;
}
gbump(1);
return ret;
}
auto underflow() -> int_type override {
if (gptr() == egptr()) {
return traits_type::eof();
}
reader_.set_read_position(
static_cast<std::uint64_t>(reinterpret_cast<std::uintptr_t>(gptr())));
char c{};
const auto res = encrypting_reader::reader_function(&c, 1U, 1U, &reader_);
if (res != 1) {
return traits_type::eof();
}
return c;
}
auto xsgetn(char *ptr, std::streamsize count) -> std::streamsize override {
if (gptr() == egptr()) {
return traits_type::eof();
}
reader_.set_read_position(
static_cast<std::uint64_t>(reinterpret_cast<std::uintptr_t>(gptr())));
const auto res = encrypting_reader::reader_function(
ptr, 1U, static_cast<std::size_t>(count), &reader_);
if ((res == reader_.get_error_return()) ||
(reader_.get_stop_requested() && (res == CURL_READFUNC_ABORT))) {
return traits_type::eof();
}
setg(eback(), gptr() + res,
reinterpret_cast<char *>(reader_.get_total_size()));
return static_cast<std::streamsize>(res);
}
};
class encrypting_reader_iostream final : public encrypting_reader::iostream {
public:
encrypting_reader_iostream(const encrypting_reader_iostream &) = delete;
encrypting_reader_iostream(encrypting_reader_iostream &&) = delete;
auto operator=(const encrypting_reader_iostream &)
-> encrypting_reader_iostream & = delete;
auto operator=(encrypting_reader_iostream &&)
-> encrypting_reader_iostream & = delete;
explicit encrypting_reader_iostream(
std::unique_ptr<encrypting_streambuf> buffer)
: encrypting_reader::iostream(buffer.get()), buffer_(std::move(buffer)) {}
~encrypting_reader_iostream() override = default;
private:
std::unique_ptr<encrypting_streambuf> buffer_;
};
const std::size_t encrypting_reader::header_size_ = ([]() {
return crypto_aead_xchacha20poly1305_IETF_NPUBBYTES +
crypto_aead_xchacha20poly1305_IETF_ABYTES;
})();
const std::size_t encrypting_reader::data_chunk_size_ = (8UL * 1024UL * 1024UL);
const std::size_t encrypting_reader::encrypted_chunk_size_ =
data_chunk_size_ + header_size_;
encrypting_reader::encrypting_reader(
const std::string &file_name, const std::string &source_path,
stop_type &stop_requested, const std::string &token,
std::optional<std::string> relative_parent_path, std::size_t error_return)
: key_(utils::encryption::generate_key(token)),
stop_requested_(stop_requested),
error_return_(error_return) {
const auto res = native_file::create_or_open(
source_path, not relative_parent_path.has_value(), source_file_);
if (res != api_error::success) {
throw std::runtime_error("file open failed|src|" + source_path + '|' +
api_error_to_string(res));
}
data_buffer result;
utils::encryption::encrypt_data(key_, file_name.c_str(),
strnlen(file_name.c_str(), file_name.size()),
result);
encrypted_file_name_ = utils::to_hex_string(result);
if (relative_parent_path.has_value()) {
for (const auto &part :
std::filesystem::path(relative_parent_path.value())) {
utils::encryption::encrypt_data(
key_, part.string().c_str(),
strnlen(part.string().c_str(), part.string().size()), result);
encrypted_file_path_ += '/' + utils::to_hex_string(result);
}
encrypted_file_path_ += '/' + encrypted_file_name_;
}
std::uint64_t file_size{};
if (not utils::file::get_file_size(source_path, file_size)) {
throw std::runtime_error("get file size failed|src|" + source_path + '|' +
std::to_string(utils::get_last_error_code()));
}
const auto total_chunks = 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_) == 0U ? data_chunk_size_
: file_size % data_chunk_size_);
iv_list_.resize(total_chunks);
for (auto &iv : iv_list_) {
randombytes_buf(iv.data(), iv.size());
}
}
encrypting_reader::encrypting_reader(const std::string &encrypted_file_path,
const std::string &source_path,
stop_type &stop_requested,
const std::string &token,
std::size_t error_return)
: key_(utils::encryption::generate_key(token)),
stop_requested_(stop_requested),
error_return_(error_return) {
const auto res =
native_file::create_or_open(source_path, false, source_file_);
if (res != api_error::success) {
throw std::runtime_error("file open failed|src|" + source_path + '|' +
api_error_to_string(res));
}
encrypted_file_path_ = encrypted_file_path;
encrypted_file_name_ =
std::filesystem::path(encrypted_file_path_).filename().string();
std::uint64_t file_size{};
if (not utils::file::get_file_size(source_path, file_size)) {
throw std::runtime_error("get file size failed|src|" + source_path + '|' +
std::to_string(utils::get_last_error_code()));
}
const auto total_chunks = 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_) == 0U ? data_chunk_size_
: file_size % data_chunk_size_);
iv_list_.resize(total_chunks);
for (auto &iv : iv_list_) {
randombytes_buf(iv.data(), iv.size());
}
}
encrypting_reader::encrypting_reader(
const std::string &encrypted_file_path, const std::string &source_path,
stop_type &stop_requested, const std::string &token,
std::vector<
std::array<unsigned char, crypto_aead_xchacha20poly1305_IETF_NPUBBYTES>>
iv_list,
std::size_t error_return)
: key_(utils::encryption::generate_key(token)),
stop_requested_(stop_requested),
error_return_(error_return) {
const auto res =
native_file::create_or_open(source_path, false, source_file_);
if (res != api_error::success) {
throw std::runtime_error("file open failed|src|" + source_path + '|' +
api_error_to_string(res));
}
encrypted_file_path_ = encrypted_file_path;
encrypted_file_name_ =
std::filesystem::path(encrypted_file_path_).filename().string();
std::uint64_t file_size{};
if (not utils::file::get_file_size(source_path, file_size)) {
throw std::runtime_error("get file size failed|src|" + source_path + '|' +
std::to_string(utils::get_last_error_code()));
}
const auto total_chunks = 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_) == 0U ? data_chunk_size_
: file_size % data_chunk_size_);
iv_list_ = std::move(iv_list);
}
encrypting_reader::encrypting_reader(const encrypting_reader &reader)
: key_(reader.key_),
stop_requested_(reader.stop_requested_),
error_return_(reader.error_return_),
chunk_buffers_(reader.chunk_buffers_),
encrypted_file_name_(reader.encrypted_file_name_),
encrypted_file_path_(reader.encrypted_file_path_),
iv_list_(reader.iv_list_),
last_data_chunk_(reader.last_data_chunk_),
last_data_chunk_size_(reader.last_data_chunk_size_),
read_offset_(reader.read_offset_),
source_file_(native_file::clone(reader.source_file_)),
total_size_(reader.total_size_) {}
encrypting_reader::~encrypting_reader() {
if (source_file_) {
source_file_->close();
}
}
auto encrypting_reader::calculate_decrypted_size(std::uint64_t total_size)
-> std::uint64_t {
return total_size - (utils::divide_with_ceiling(
total_size, static_cast<std::uint64_t>(
get_encrypted_chunk_size())) *
get_header_size());
}
auto encrypting_reader::calculate_encrypted_size(const std::string &source_path)
-> std::uint64_t {
std::uint64_t file_size{};
if (not utils::file::get_file_size(source_path, file_size)) {
throw std::runtime_error("get file size failed|src|" + source_path + '|' +
std::to_string(utils::get_last_error_code()));
}
const auto total_chunks = static_cast<std::size_t>(utils::divide_with_ceiling(
file_size, static_cast<std::uint64_t>(data_chunk_size_)));
return file_size + (total_chunks * encrypting_reader::get_header_size());
}
auto encrypting_reader::create_iostream() const
-> std::shared_ptr<encrypting_reader::iostream> {
return std::make_shared<encrypting_reader_iostream>(
std::make_unique<encrypting_streambuf>(*this));
}
auto encrypting_reader::reader_function(char *buffer, size_t size,
size_t nitems) -> size_t {
const auto read_size = static_cast<std::size_t>(std::min(
static_cast<std::uint64_t>(size * nitems), total_size_ - read_offset_));
auto chunk = static_cast<std::size_t>(read_offset_ / encrypted_chunk_size_);
auto chunk_offset =
static_cast<std::size_t>(read_offset_ % encrypted_chunk_size_);
std::size_t total_read = 0u;
auto ret = false;
if (read_offset_ < total_size_) {
try {
ret = true;
auto remain = read_size;
while (not stop_requested_ && ret && remain) {
if (chunk_buffers_.find(chunk) == chunk_buffers_.end()) {
auto &chunk_buffer = chunk_buffers_[chunk];
data_buffer file_data(chunk == last_data_chunk_
? last_data_chunk_size_
: data_chunk_size_);
chunk_buffer.resize(file_data.size() + get_header_size());
std::size_t bytes_read{};
if ((ret = source_file_->read_bytes(&file_data[0u], file_data.size(),
chunk * data_chunk_size_,
bytes_read))) {
utils::encryption::encrypt_data(iv_list_.at(chunk), key_, file_data,
chunk_buffer);
}
} else if (chunk) {
chunk_buffers_.erase(chunk - 1u);
}
auto &chunk_buffer = chunk_buffers_[chunk];
const auto to_read =
std::min(chunk_buffer.size() - chunk_offset, remain);
std::memcpy(buffer + total_read, &chunk_buffer[chunk_offset], to_read);
total_read += to_read;
remain -= to_read;
chunk_offset = 0u;
chunk++;
read_offset_ += to_read;
}
} catch (const std::exception &e) {
utils::error::raise_error(__FUNCTION__, e, "exception occurred");
ret = false;
}
}
return stop_requested_ ? CURL_READFUNC_ABORT
: ret ? total_read
: error_return_;
}
} // namespace repertory::utils::encryption

View File

@@ -1,140 +1,140 @@
/*
Copyright <2018-2023> <scott.e.graves@protonmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "utils/encryption.hpp"
#include "events/event_system.hpp"
#include "events/events.hpp"
#include "types/repertory.hpp"
#include "utils/encrypting_reader.hpp"
#include "utils/utils.hpp"
namespace repertory::utils::encryption {
auto decrypt_file_path(const std::string &encryption_token,
std::string &file_path) -> api_error {
std::string decrypted_file_path{};
for (const auto &part : std::filesystem::path(file_path)) {
auto file_name = part.string();
if (file_name == "/") {
continue;
}
auto res = decrypt_file_name(encryption_token, file_name);
if (res != api_error::success) {
return res;
}
decrypted_file_path += '/' + file_name;
}
file_path = decrypted_file_path;
return api_error::success;
}
auto decrypt_file_name(const std::string &encryption_token,
std::string &file_name) -> api_error {
data_buffer buffer;
if (not utils::from_hex_string(file_name, buffer)) {
return api_error::error;
}
file_name.clear();
if (not utils::encryption::decrypt_data(encryption_token, buffer,
file_name)) {
return api_error::decryption_error;
}
return api_error::success;
}
auto generate_key(const std::string &encryption_token) -> key_type {
crypto_hash_sha256_state state{};
auto res = crypto_hash_sha256_init(&state);
if (res != 0) {
throw std::runtime_error("failed to initialize sha256|" +
std::to_string(res));
}
if ((res = crypto_hash_sha256_update(
&state,
reinterpret_cast<const unsigned char *>(encryption_token.c_str()),
strnlen(encryption_token.c_str(), encryption_token.size()))) != 0) {
throw std::runtime_error("failed to update sha256|" + std::to_string(res));
}
key_type ret{};
if ((res = crypto_hash_sha256_final(&state, ret.data())) != 0) {
throw std::runtime_error("failed to finalize sha256|" +
std::to_string(res));
}
return ret;
}
auto read_encrypted_range(
const http_range &range, const key_type &key,
const std::function<api_error(data_buffer &ct, std::uint64_t start_offset,
std::uint64_t end_offset)> &reader,
std::uint64_t total_size, data_buffer &data) -> api_error {
const auto encrypted_chunk_size =
utils::encryption::encrypting_reader::get_encrypted_chunk_size();
const auto data_chunk_size =
utils::encryption::encrypting_reader::get_data_chunk_size();
const auto header_size =
utils::encryption::encrypting_reader::get_header_size();
const auto start_chunk =
static_cast<std::size_t>(range.begin / data_chunk_size);
const auto end_chunk = static_cast<std::size_t>(range.end / data_chunk_size);
auto remain = range.end - range.begin + 1U;
auto source_offset = static_cast<std::size_t>(range.begin % data_chunk_size);
for (std::size_t chunk = start_chunk; chunk <= end_chunk; chunk++) {
data_buffer ct;
const auto start_offset = chunk * encrypted_chunk_size;
const auto end_offset = std::min(
start_offset + (total_size - (chunk * data_chunk_size)) + header_size -
1U,
static_cast<std::uint64_t>(start_offset + encrypted_chunk_size - 1U));
const auto result = reader(ct, start_offset, end_offset);
if (result != api_error::success) {
return result;
}
data_buffer source_buffer;
if (not utils::encryption::decrypt_data(key, ct, source_buffer)) {
return api_error::decryption_error;
}
ct.clear();
const auto data_size = static_cast<std::size_t>(std::min(
remain, static_cast<std::uint64_t>(data_chunk_size - source_offset)));
std::copy(source_buffer.begin() + source_offset,
source_buffer.begin() + source_offset + data_size,
std::back_inserter(data));
remain -= data_size;
source_offset = 0u;
}
return api_error::success;
}
} // namespace repertory::utils::encryption
/*
Copyright <2018-2023> <scott.e.graves@protonmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "utils/encryption.hpp"
#include "events/event_system.hpp"
#include "events/events.hpp"
#include "types/repertory.hpp"
#include "utils/encrypting_reader.hpp"
#include "utils/utils.hpp"
namespace repertory::utils::encryption {
auto decrypt_file_path(const std::string &encryption_token,
std::string &file_path) -> api_error {
std::string decrypted_file_path{};
for (const auto &part : std::filesystem::path(file_path)) {
auto file_name = part.string();
if (file_name == "/") {
continue;
}
auto res = decrypt_file_name(encryption_token, file_name);
if (res != api_error::success) {
return res;
}
decrypted_file_path += '/' + file_name;
}
file_path = decrypted_file_path;
return api_error::success;
}
auto decrypt_file_name(const std::string &encryption_token,
std::string &file_name) -> api_error {
data_buffer buffer;
if (not utils::from_hex_string(file_name, buffer)) {
return api_error::error;
}
file_name.clear();
if (not utils::encryption::decrypt_data(encryption_token, buffer,
file_name)) {
return api_error::decryption_error;
}
return api_error::success;
}
auto generate_key(const std::string &encryption_token) -> key_type {
crypto_hash_sha256_state state{};
auto res = crypto_hash_sha256_init(&state);
if (res != 0) {
throw std::runtime_error("failed to initialize sha256|" +
std::to_string(res));
}
if ((res = crypto_hash_sha256_update(
&state,
reinterpret_cast<const unsigned char *>(encryption_token.c_str()),
strnlen(encryption_token.c_str(), encryption_token.size()))) != 0) {
throw std::runtime_error("failed to update sha256|" + std::to_string(res));
}
key_type ret{};
if ((res = crypto_hash_sha256_final(&state, ret.data())) != 0) {
throw std::runtime_error("failed to finalize sha256|" +
std::to_string(res));
}
return ret;
}
auto read_encrypted_range(
const http_range &range, const key_type &key,
const std::function<api_error(data_buffer &ct, std::uint64_t start_offset,
std::uint64_t end_offset)> &reader,
std::uint64_t total_size, data_buffer &data) -> api_error {
const auto encrypted_chunk_size =
utils::encryption::encrypting_reader::get_encrypted_chunk_size();
const auto data_chunk_size =
utils::encryption::encrypting_reader::get_data_chunk_size();
const auto header_size =
utils::encryption::encrypting_reader::get_header_size();
const auto start_chunk =
static_cast<std::size_t>(range.begin / data_chunk_size);
const auto end_chunk = static_cast<std::size_t>(range.end / data_chunk_size);
auto remain = range.end - range.begin + 1U;
auto source_offset = static_cast<std::size_t>(range.begin % data_chunk_size);
for (std::size_t chunk = start_chunk; chunk <= end_chunk; chunk++) {
data_buffer ct;
const auto start_offset = chunk * encrypted_chunk_size;
const auto end_offset = std::min(
start_offset + (total_size - (chunk * data_chunk_size)) + header_size -
1U,
static_cast<std::uint64_t>(start_offset + encrypted_chunk_size - 1U));
const auto result = reader(ct, start_offset, end_offset);
if (result != api_error::success) {
return result;
}
data_buffer source_buffer;
if (not utils::encryption::decrypt_data(key, ct, source_buffer)) {
return api_error::decryption_error;
}
ct.clear();
const auto data_size = static_cast<std::size_t>(std::min(
remain, static_cast<std::uint64_t>(data_chunk_size - source_offset)));
std::copy(source_buffer.begin() + source_offset,
source_buffer.begin() + source_offset + data_size,
std::back_inserter(data));
remain -= data_size;
source_offset = 0u;
}
return api_error::success;
}
} // namespace repertory::utils::encryption

View File

@@ -1,172 +1,172 @@
/*
Copyright <2018-2023> <scott.e.graves@protonmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "utils/error_utils.hpp"
#include "events/event_system.hpp"
#include "events/events.hpp"
#include "types/repertory.hpp"
namespace repertory::utils::error {
void raise_error(std::string_view function, std::string_view msg) {
event_system::instance().raise<repertory_exception>(
static_cast<std::string>(function), static_cast<std::string>(msg));
}
void raise_error(std::string_view function, const api_error &e,
std::string_view msg) {
event_system::instance().raise<repertory_exception>(
static_cast<std::string>(function),
static_cast<std::string>(msg) + "|err|" + api_error_to_string(e));
}
void raise_error(std::string_view function, const std::exception &e,
std::string_view msg) {
event_system::instance().raise<repertory_exception>(
static_cast<std::string>(function),
static_cast<std::string>(msg) + "|err|" +
(e.what() ? e.what() : "unknown error"));
}
void raise_error(std::string_view function, const json &e,
std::string_view msg) {
event_system::instance().raise<repertory_exception>(
static_cast<std::string>(function),
static_cast<std::string>(msg) + "|err|" + e.dump(2));
}
void raise_error(std::string_view function, std::int64_t e,
std::string_view msg) {
event_system::instance().raise<repertory_exception>(
static_cast<std::string>(function),
static_cast<std::string>(msg) + "|err|" + std::to_string(e));
}
void raise_error(std::string_view function, const api_error &e,
std::string_view file_path, std::string_view msg) {
event_system::instance().raise<repertory_exception>(
static_cast<std::string>(function),
static_cast<std::string>(msg) + "|sp|" +
static_cast<std::string>(file_path) + "|err|" +
api_error_to_string(e));
}
void raise_error(std::string_view function, std::int64_t e,
std::string_view file_path, std::string_view msg) {
event_system::instance().raise<repertory_exception>(
static_cast<std::string>(function),
static_cast<std::string>(msg) + "|sp|" +
static_cast<std::string>(file_path) + "|err|" + std::to_string(e));
}
void raise_error(std::string_view function, const std::exception &e,
std::string_view file_path, std::string_view msg) {
event_system::instance().raise<repertory_exception>(
static_cast<std::string>(function),
static_cast<std::string>(msg) + "|sp|" +
static_cast<std::string>(file_path) + "|err|" +
(e.what() ? e.what() : "unknown error"));
}
void raise_api_path_error(std::string_view function, std::string_view api_path,
const api_error &e, std::string_view msg) {
event_system::instance().raise<repertory_exception>(
static_cast<std::string>(function),
static_cast<std::string>(msg) + "|ap|" +
static_cast<std::string>(api_path) + "|err|" +
api_error_to_string(e));
}
void raise_api_path_error(std::string_view function, std::string_view api_path,
std::int64_t e, std::string_view msg) {
event_system::instance().raise<repertory_exception>(
static_cast<std::string>(function),
static_cast<std::string>(msg) + "|ap|" +
static_cast<std::string>(api_path) + "|err|" + std::to_string(e));
}
void raise_api_path_error(std::string_view function, std::string_view api_path,
const std::exception &e, std::string_view msg) {
event_system::instance().raise<repertory_exception>(
static_cast<std::string>(function),
static_cast<std::string>(msg) + "|ap|" +
static_cast<std::string>(api_path) + "|err|" +
(e.what() ? e.what() : "unknown error"));
}
void raise_api_path_error(std::string_view function, std::string_view api_path,
std::string_view source_path, const api_error &e,
std::string_view msg) {
event_system::instance().raise<repertory_exception>(
static_cast<std::string>(function),
static_cast<std::string>(msg) + "|ap|" +
static_cast<std::string>(api_path) + "|sp|" +
static_cast<std::string>(source_path) + "|err|" +
api_error_to_string(e));
}
void raise_api_path_error(std::string_view function, std::string_view api_path,
std::string_view source_path, std::int64_t e,
std::string_view msg) {
event_system::instance().raise<repertory_exception>(
static_cast<std::string>(function),
static_cast<std::string>(msg) + "|ap|" +
static_cast<std::string>(api_path) + "|sp|" +
static_cast<std::string>(source_path) + "|err|" + std::to_string(e));
}
void raise_api_path_error(std::string_view function, std::string_view api_path,
const json &e, std::string_view msg) {
event_system::instance().raise<repertory_exception>(
static_cast<std::string>(function),
static_cast<std::string>(msg) + "|ap|" +
static_cast<std::string>(api_path) + "|err|" + e.dump(2));
}
void raise_api_path_error(std::string_view function, std::string_view api_path,
std::string_view source_path, const std::exception &e,
std::string_view msg) {
event_system::instance().raise<repertory_exception>(
static_cast<std::string>(function),
static_cast<std::string>(msg) + "|ap|" +
static_cast<std::string>(api_path) + "|sp|" +
static_cast<std::string>(source_path) + "|err|" +
(e.what() ? e.what() : "unknown error"));
}
void raise_url_error(std::string_view function, std::string_view url,
CURLcode e, std::string_view msg) {
event_system::instance().raise<repertory_exception>(
static_cast<std::string>(function),
static_cast<std::string>(msg) + "|url|" + static_cast<std::string>(url) +
"|err|" + curl_easy_strerror(e));
}
void raise_url_error(std::string_view function, std::string_view url,
std::string_view source_path, const std::exception &e,
std::string_view msg) {
event_system::instance().raise<repertory_exception>(
static_cast<std::string>(function),
static_cast<std::string>(msg) + "|url|" + static_cast<std::string>(url) +
"|sp|" + static_cast<std::string>(source_path) + "|err|" +
(e.what() ? e.what() : "unknown error"));
}
} // namespace repertory::utils::error
/*
Copyright <2018-2023> <scott.e.graves@protonmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "utils/error_utils.hpp"
#include "events/event_system.hpp"
#include "events/events.hpp"
#include "types/repertory.hpp"
namespace repertory::utils::error {
void raise_error(std::string_view function, std::string_view msg) {
event_system::instance().raise<repertory_exception>(
static_cast<std::string>(function), static_cast<std::string>(msg));
}
void raise_error(std::string_view function, const api_error &e,
std::string_view msg) {
event_system::instance().raise<repertory_exception>(
static_cast<std::string>(function),
static_cast<std::string>(msg) + "|err|" + api_error_to_string(e));
}
void raise_error(std::string_view function, const std::exception &e,
std::string_view msg) {
event_system::instance().raise<repertory_exception>(
static_cast<std::string>(function),
static_cast<std::string>(msg) + "|err|" +
(e.what() ? e.what() : "unknown error"));
}
void raise_error(std::string_view function, const json &e,
std::string_view msg) {
event_system::instance().raise<repertory_exception>(
static_cast<std::string>(function),
static_cast<std::string>(msg) + "|err|" + e.dump(2));
}
void raise_error(std::string_view function, std::int64_t e,
std::string_view msg) {
event_system::instance().raise<repertory_exception>(
static_cast<std::string>(function),
static_cast<std::string>(msg) + "|err|" + std::to_string(e));
}
void raise_error(std::string_view function, const api_error &e,
std::string_view file_path, std::string_view msg) {
event_system::instance().raise<repertory_exception>(
static_cast<std::string>(function),
static_cast<std::string>(msg) + "|sp|" +
static_cast<std::string>(file_path) + "|err|" +
api_error_to_string(e));
}
void raise_error(std::string_view function, std::int64_t e,
std::string_view file_path, std::string_view msg) {
event_system::instance().raise<repertory_exception>(
static_cast<std::string>(function),
static_cast<std::string>(msg) + "|sp|" +
static_cast<std::string>(file_path) + "|err|" + std::to_string(e));
}
void raise_error(std::string_view function, const std::exception &e,
std::string_view file_path, std::string_view msg) {
event_system::instance().raise<repertory_exception>(
static_cast<std::string>(function),
static_cast<std::string>(msg) + "|sp|" +
static_cast<std::string>(file_path) + "|err|" +
(e.what() ? e.what() : "unknown error"));
}
void raise_api_path_error(std::string_view function, std::string_view api_path,
const api_error &e, std::string_view msg) {
event_system::instance().raise<repertory_exception>(
static_cast<std::string>(function),
static_cast<std::string>(msg) + "|ap|" +
static_cast<std::string>(api_path) + "|err|" +
api_error_to_string(e));
}
void raise_api_path_error(std::string_view function, std::string_view api_path,
std::int64_t e, std::string_view msg) {
event_system::instance().raise<repertory_exception>(
static_cast<std::string>(function),
static_cast<std::string>(msg) + "|ap|" +
static_cast<std::string>(api_path) + "|err|" + std::to_string(e));
}
void raise_api_path_error(std::string_view function, std::string_view api_path,
const std::exception &e, std::string_view msg) {
event_system::instance().raise<repertory_exception>(
static_cast<std::string>(function),
static_cast<std::string>(msg) + "|ap|" +
static_cast<std::string>(api_path) + "|err|" +
(e.what() ? e.what() : "unknown error"));
}
void raise_api_path_error(std::string_view function, std::string_view api_path,
std::string_view source_path, const api_error &e,
std::string_view msg) {
event_system::instance().raise<repertory_exception>(
static_cast<std::string>(function),
static_cast<std::string>(msg) + "|ap|" +
static_cast<std::string>(api_path) + "|sp|" +
static_cast<std::string>(source_path) + "|err|" +
api_error_to_string(e));
}
void raise_api_path_error(std::string_view function, std::string_view api_path,
std::string_view source_path, std::int64_t e,
std::string_view msg) {
event_system::instance().raise<repertory_exception>(
static_cast<std::string>(function),
static_cast<std::string>(msg) + "|ap|" +
static_cast<std::string>(api_path) + "|sp|" +
static_cast<std::string>(source_path) + "|err|" + std::to_string(e));
}
void raise_api_path_error(std::string_view function, std::string_view api_path,
const json &e, std::string_view msg) {
event_system::instance().raise<repertory_exception>(
static_cast<std::string>(function),
static_cast<std::string>(msg) + "|ap|" +
static_cast<std::string>(api_path) + "|err|" + e.dump(2));
}
void raise_api_path_error(std::string_view function, std::string_view api_path,
std::string_view source_path, const std::exception &e,
std::string_view msg) {
event_system::instance().raise<repertory_exception>(
static_cast<std::string>(function),
static_cast<std::string>(msg) + "|ap|" +
static_cast<std::string>(api_path) + "|sp|" +
static_cast<std::string>(source_path) + "|err|" +
(e.what() ? e.what() : "unknown error"));
}
void raise_url_error(std::string_view function, std::string_view url,
CURLcode e, std::string_view msg) {
event_system::instance().raise<repertory_exception>(
static_cast<std::string>(function),
static_cast<std::string>(msg) + "|url|" + static_cast<std::string>(url) +
"|err|" + curl_easy_strerror(e));
}
void raise_url_error(std::string_view function, std::string_view url,
std::string_view source_path, const std::exception &e,
std::string_view msg) {
event_system::instance().raise<repertory_exception>(
static_cast<std::string>(function),
static_cast<std::string>(msg) + "|url|" + static_cast<std::string>(url) +
"|sp|" + static_cast<std::string>(source_path) + "|err|" +
(e.what() ? e.what() : "unknown error"));
}
} // namespace repertory::utils::error

File diff suppressed because it is too large Load Diff

View File

@@ -1,304 +1,304 @@
/*
Copyright <2018-2023> <scott.e.graves@protonmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "utils/native_file.hpp"
#include "types/repertory.hpp"
#include "utils/string_utils.hpp"
#include "utils/unix/unix_utils.hpp"
#include "utils/utils.hpp"
namespace repertory {
auto native_file::get_handle() -> native_handle { return handle_; }
native_file::~native_file() {
if (auto_close) {
close();
}
}
auto native_file::clone(const native_file_ptr &nf) -> native_file_ptr {
std::string source_path;
#ifdef _WIN32
source_path.resize(MAX_PATH + 1);
::GetFinalPathNameByHandleA(nf->get_handle(), &source_path[0u], MAX_PATH + 1,
FILE_NAME_NORMALIZED | VOLUME_NAME_DOS);
#else
source_path.resize(PATH_MAX + 1);
#ifdef __APPLE__
fcntl(nf->get_handle(), F_GETPATH, &source_path[0u]);
#else
readlink(("/proc/self/fd/" + std::to_string(nf->get_handle())).c_str(),
&source_path[0u], source_path.size());
#endif
#endif
source_path = source_path.c_str();
native_file_ptr clone;
auto res = native_file::open(source_path, clone);
if (res != api_error::success) {
throw std::runtime_error("unable to open file|sp|" + source_path + "|err|" +
api_error_to_string(res));
}
return clone;
}
auto native_file::create_or_open(const std::string &source_path,
[[maybe_unused]] bool should_chmod,
native_file_ptr &nf) -> api_error {
#ifdef _WIN32
auto handle = ::CreateFileA(source_path.c_str(), GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr,
OPEN_ALWAYS, FILE_FLAG_RANDOM_ACCESS, nullptr);
#else
auto handle = should_chmod ? ::open(source_path.c_str(),
O_CREAT | O_RDWR | O_CLOEXEC, 0600u)
: ::open(source_path.c_str(), O_RDWR | O_CLOEXEC);
if (should_chmod) {
chmod(source_path.c_str(), 0600u);
}
#endif
nf = native_file::attach(handle);
return ((handle == REPERTORY_INVALID_HANDLE) ? api_error::os_error
: api_error::success);
}
auto native_file::create_or_open(const std::string &source_path,
native_file_ptr &nf) -> api_error {
return create_or_open(source_path, true, nf);
}
auto native_file::open(const std::string &source_path, native_file_ptr &nf)
-> api_error {
return open(source_path, true, nf);
}
auto native_file::open(const std::string &source_path,
[[maybe_unused]] bool should_chmod, native_file_ptr &nf)
-> api_error {
#ifdef _WIN32
auto handle = ::CreateFileA(source_path.c_str(), GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr,
OPEN_EXISTING, FILE_FLAG_RANDOM_ACCESS, nullptr);
#else
auto handle = should_chmod
? ::open(source_path.c_str(), O_RDWR | O_CLOEXEC, 0600u)
: ::open(source_path.c_str(), O_RDONLY | O_CLOEXEC);
if (should_chmod) {
chmod(source_path.c_str(), 0600u);
}
#endif
nf = native_file::attach(handle);
return ((handle == REPERTORY_INVALID_HANDLE) ? api_error::os_error
: api_error::success);
}
auto native_file::allocate(std::uint64_t file_size) -> bool {
#ifdef _WIN32
LARGE_INTEGER li{};
li.QuadPart = file_size;
return (::SetFilePointerEx(handle_, li, nullptr, FILE_BEGIN) &&
::SetEndOfFile(handle_));
#endif
#ifdef __linux__
return (fallocate(handle_, 0, 0, file_size) >= 0);
#endif
#ifdef __APPLE__
return (ftruncate(handle_, file_size) >= 0);
#endif
}
void native_file::close() {
if (handle_ != REPERTORY_INVALID_HANDLE) {
#ifdef WIN32
::CloseHandle(handle_);
#else
::close(handle_);
#endif
handle_ = REPERTORY_INVALID_HANDLE;
}
}
auto native_file::copy_from(const native_file_ptr &nf) -> bool {
auto ret = false;
std::uint64_t file_size = 0u;
if ((ret = nf->get_file_size(file_size))) {
data_buffer buffer;
buffer.resize(65536u * 2u);
std::uint64_t offset = 0u;
while (ret && (file_size > 0u)) {
std::size_t bytes_read{};
if ((ret = nf->read_bytes(&buffer[0u], buffer.size(), offset,
bytes_read))) {
std::size_t bytes_written = 0u;
ret = write_bytes(&buffer[0u], bytes_read, offset, bytes_written);
file_size -= bytes_read;
offset += bytes_read;
}
}
flush();
}
return ret;
}
auto native_file::copy_from(const std::string &path) -> bool {
auto ret = false;
native_file_ptr nf;
if (native_file::create_or_open(path, nf) == api_error ::success) {
ret = copy_from(nf);
nf->close();
}
return ret;
}
void native_file::flush() {
#ifdef _WIN32
recur_mutex_lock l(read_write_mutex_);
::FlushFileBuffers(handle_);
#else
fsync(handle_);
#endif
}
auto native_file::get_file_size(std::uint64_t &file_size) -> bool {
auto ret = false;
#ifdef _WIN32
LARGE_INTEGER li{};
if ((ret = ::GetFileSizeEx(handle_, &li) && (li.QuadPart >= 0))) {
file_size = static_cast<std::uint64_t>(li.QuadPart);
}
#else
#if __APPLE__
struct stat st {};
if (fstat(handle_, &st) >= 0) {
#else
struct stat64 st {};
if (fstat64(handle_, &st) >= 0) {
#endif
if ((ret = (st.st_size >= 0))) {
file_size = static_cast<uint64_t>(st.st_size);
}
}
#endif
return ret;
}
#ifdef _WIN32
auto native_file::read_bytes(char *buffer, std::size_t read_size,
std::uint64_t read_offset, std::size_t &bytes_read)
-> bool {
recur_mutex_lock l(read_write_mutex_);
auto ret = false;
bytes_read = 0u;
LARGE_INTEGER li{};
li.QuadPart = read_offset;
if ((ret = !!::SetFilePointerEx(handle_, li, nullptr, FILE_BEGIN))) {
DWORD current_read = 0u;
do {
current_read = 0u;
ret = !!::ReadFile(handle_, &buffer[bytes_read],
static_cast<DWORD>(read_size - bytes_read),
&current_read, nullptr);
bytes_read += current_read;
} while (ret && (bytes_read < read_size) && (current_read != 0));
}
if (ret && (read_size != bytes_read)) {
::SetLastError(ERROR_HANDLE_EOF);
}
return ret;
}
#else
auto native_file::read_bytes(char *buffer, std::size_t read_size,
std::uint64_t read_offset, std::size_t &bytes_read)
-> bool {
bytes_read = 0u;
ssize_t result = 0;
do {
result = pread64(handle_, &buffer[bytes_read], read_size - bytes_read,
read_offset + bytes_read);
if (result > 0) {
bytes_read += static_cast<size_t>(result);
}
} while ((result > 0) && (bytes_read < read_size));
return (result >= 0);
}
#endif
auto native_file::truncate(std::uint64_t file_size) -> bool {
#ifdef _WIN32
recur_mutex_lock l(read_write_mutex_);
LARGE_INTEGER li{};
li.QuadPart = file_size;
return (::SetFilePointerEx(handle_, li, nullptr, FILE_BEGIN) &&
::SetEndOfFile(handle_));
#else
return (ftruncate(handle_, file_size) >= 0);
#endif
}
#ifdef _WIN32
auto native_file::write_bytes(const char *buffer, std::size_t write_size,
std::uint64_t write_offset,
std::size_t &bytes_written) -> bool {
recur_mutex_lock l(read_write_mutex_);
bytes_written = 0u;
auto ret = true;
LARGE_INTEGER li{};
li.QuadPart = write_offset;
if ((ret = !!::SetFilePointerEx(handle_, li, nullptr, FILE_BEGIN))) {
do {
DWORD current_write = 0u;
ret = !!::WriteFile(handle_, &buffer[bytes_written],
static_cast<DWORD>(write_size - bytes_written),
&current_write, nullptr);
bytes_written += current_write;
} while (ret && (bytes_written < write_size));
}
return ret;
}
#else
auto native_file::write_bytes(const char *buffer, std::size_t write_size,
std::uint64_t write_offset,
std::size_t &bytes_written) -> bool {
bytes_written = 0;
ssize_t result = 0;
do {
result = pwrite64(handle_, &buffer[bytes_written],
write_size - bytes_written, write_offset + bytes_written);
if (result > 0) {
bytes_written += static_cast<size_t>(result);
}
} while ((result >= 0) && (bytes_written < write_size));
return (bytes_written == write_size);
}
#endif
} // namespace repertory
/*
Copyright <2018-2023> <scott.e.graves@protonmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "utils/native_file.hpp"
#include "types/repertory.hpp"
#include "utils/string_utils.hpp"
#include "utils/unix/unix_utils.hpp"
#include "utils/utils.hpp"
namespace repertory {
auto native_file::get_handle() -> native_handle { return handle_; }
native_file::~native_file() {
if (auto_close) {
close();
}
}
auto native_file::clone(const native_file_ptr &nf) -> native_file_ptr {
std::string source_path;
#ifdef _WIN32
source_path.resize(MAX_PATH + 1);
::GetFinalPathNameByHandleA(nf->get_handle(), &source_path[0u], MAX_PATH + 1,
FILE_NAME_NORMALIZED | VOLUME_NAME_DOS);
#else
source_path.resize(PATH_MAX + 1);
#ifdef __APPLE__
fcntl(nf->get_handle(), F_GETPATH, &source_path[0u]);
#else
readlink(("/proc/self/fd/" + std::to_string(nf->get_handle())).c_str(),
&source_path[0u], source_path.size());
#endif
#endif
source_path = source_path.c_str();
native_file_ptr clone;
auto res = native_file::open(source_path, clone);
if (res != api_error::success) {
throw std::runtime_error("unable to open file|sp|" + source_path + "|err|" +
api_error_to_string(res));
}
return clone;
}
auto native_file::create_or_open(const std::string &source_path,
[[maybe_unused]] bool should_chmod,
native_file_ptr &nf) -> api_error {
#ifdef _WIN32
auto handle = ::CreateFileA(source_path.c_str(), GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr,
OPEN_ALWAYS, FILE_FLAG_RANDOM_ACCESS, nullptr);
#else
auto handle = should_chmod ? ::open(source_path.c_str(),
O_CREAT | O_RDWR | O_CLOEXEC, 0600u)
: ::open(source_path.c_str(), O_RDWR | O_CLOEXEC);
if (should_chmod) {
chmod(source_path.c_str(), 0600u);
}
#endif
nf = native_file::attach(handle);
return ((handle == REPERTORY_INVALID_HANDLE) ? api_error::os_error
: api_error::success);
}
auto native_file::create_or_open(const std::string &source_path,
native_file_ptr &nf) -> api_error {
return create_or_open(source_path, true, nf);
}
auto native_file::open(const std::string &source_path, native_file_ptr &nf)
-> api_error {
return open(source_path, true, nf);
}
auto native_file::open(const std::string &source_path,
[[maybe_unused]] bool should_chmod, native_file_ptr &nf)
-> api_error {
#ifdef _WIN32
auto handle = ::CreateFileA(source_path.c_str(), GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr,
OPEN_EXISTING, FILE_FLAG_RANDOM_ACCESS, nullptr);
#else
auto handle = should_chmod
? ::open(source_path.c_str(), O_RDWR | O_CLOEXEC, 0600u)
: ::open(source_path.c_str(), O_RDONLY | O_CLOEXEC);
if (should_chmod) {
chmod(source_path.c_str(), 0600u);
}
#endif
nf = native_file::attach(handle);
return ((handle == REPERTORY_INVALID_HANDLE) ? api_error::os_error
: api_error::success);
}
auto native_file::allocate(std::uint64_t file_size) -> bool {
#ifdef _WIN32
LARGE_INTEGER li{};
li.QuadPart = file_size;
return (::SetFilePointerEx(handle_, li, nullptr, FILE_BEGIN) &&
::SetEndOfFile(handle_));
#endif
#ifdef __linux__
return (fallocate(handle_, 0, 0, file_size) >= 0);
#endif
#ifdef __APPLE__
return (ftruncate(handle_, file_size) >= 0);
#endif
}
void native_file::close() {
if (handle_ != REPERTORY_INVALID_HANDLE) {
#ifdef WIN32
::CloseHandle(handle_);
#else
::close(handle_);
#endif
handle_ = REPERTORY_INVALID_HANDLE;
}
}
auto native_file::copy_from(const native_file_ptr &nf) -> bool {
auto ret = false;
std::uint64_t file_size = 0u;
if ((ret = nf->get_file_size(file_size))) {
data_buffer buffer;
buffer.resize(65536u * 2u);
std::uint64_t offset = 0u;
while (ret && (file_size > 0u)) {
std::size_t bytes_read{};
if ((ret = nf->read_bytes(&buffer[0u], buffer.size(), offset,
bytes_read))) {
std::size_t bytes_written = 0u;
ret = write_bytes(&buffer[0u], bytes_read, offset, bytes_written);
file_size -= bytes_read;
offset += bytes_read;
}
}
flush();
}
return ret;
}
auto native_file::copy_from(const std::string &path) -> bool {
auto ret = false;
native_file_ptr nf;
if (native_file::create_or_open(path, nf) == api_error ::success) {
ret = copy_from(nf);
nf->close();
}
return ret;
}
void native_file::flush() {
#ifdef _WIN32
recur_mutex_lock l(read_write_mutex_);
::FlushFileBuffers(handle_);
#else
fsync(handle_);
#endif
}
auto native_file::get_file_size(std::uint64_t &file_size) -> bool {
auto ret = false;
#ifdef _WIN32
LARGE_INTEGER li{};
if ((ret = ::GetFileSizeEx(handle_, &li) && (li.QuadPart >= 0))) {
file_size = static_cast<std::uint64_t>(li.QuadPart);
}
#else
#if __APPLE__
struct stat st {};
if (fstat(handle_, &st) >= 0) {
#else
struct stat64 st {};
if (fstat64(handle_, &st) >= 0) {
#endif
if ((ret = (st.st_size >= 0))) {
file_size = static_cast<uint64_t>(st.st_size);
}
}
#endif
return ret;
}
#ifdef _WIN32
auto native_file::read_bytes(char *buffer, std::size_t read_size,
std::uint64_t read_offset, std::size_t &bytes_read)
-> bool {
recur_mutex_lock l(read_write_mutex_);
auto ret = false;
bytes_read = 0u;
LARGE_INTEGER li{};
li.QuadPart = read_offset;
if ((ret = !!::SetFilePointerEx(handle_, li, nullptr, FILE_BEGIN))) {
DWORD current_read = 0u;
do {
current_read = 0u;
ret = !!::ReadFile(handle_, &buffer[bytes_read],
static_cast<DWORD>(read_size - bytes_read),
&current_read, nullptr);
bytes_read += current_read;
} while (ret && (bytes_read < read_size) && (current_read != 0));
}
if (ret && (read_size != bytes_read)) {
::SetLastError(ERROR_HANDLE_EOF);
}
return ret;
}
#else
auto native_file::read_bytes(char *buffer, std::size_t read_size,
std::uint64_t read_offset, std::size_t &bytes_read)
-> bool {
bytes_read = 0u;
ssize_t result = 0;
do {
result = pread64(handle_, &buffer[bytes_read], read_size - bytes_read,
read_offset + bytes_read);
if (result > 0) {
bytes_read += static_cast<size_t>(result);
}
} while ((result > 0) && (bytes_read < read_size));
return (result >= 0);
}
#endif
auto native_file::truncate(std::uint64_t file_size) -> bool {
#ifdef _WIN32
recur_mutex_lock l(read_write_mutex_);
LARGE_INTEGER li{};
li.QuadPart = file_size;
return (::SetFilePointerEx(handle_, li, nullptr, FILE_BEGIN) &&
::SetEndOfFile(handle_));
#else
return (ftruncate(handle_, file_size) >= 0);
#endif
}
#ifdef _WIN32
auto native_file::write_bytes(const char *buffer, std::size_t write_size,
std::uint64_t write_offset,
std::size_t &bytes_written) -> bool {
recur_mutex_lock l(read_write_mutex_);
bytes_written = 0u;
auto ret = true;
LARGE_INTEGER li{};
li.QuadPart = write_offset;
if ((ret = !!::SetFilePointerEx(handle_, li, nullptr, FILE_BEGIN))) {
do {
DWORD current_write = 0u;
ret = !!::WriteFile(handle_, &buffer[bytes_written],
static_cast<DWORD>(write_size - bytes_written),
&current_write, nullptr);
bytes_written += current_write;
} while (ret && (bytes_written < write_size));
}
return ret;
}
#else
auto native_file::write_bytes(const char *buffer, std::size_t write_size,
std::uint64_t write_offset,
std::size_t &bytes_written) -> bool {
bytes_written = 0;
ssize_t result = 0;
do {
result = pwrite64(handle_, &buffer[bytes_written],
write_size - bytes_written, write_offset + bytes_written);
if (result > 0) {
bytes_written += static_cast<size_t>(result);
}
} while ((result >= 0) && (bytes_written < write_size));
return (bytes_written == write_size);
}
#endif
} // namespace repertory

View File

@@ -1,202 +1,202 @@
/*
Copyright <2018-2023> <scott.e.graves@protonmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "utils/path_utils.hpp"
#include "types/repertory.hpp"
#include "utils/error_utils.hpp"
#include "utils/string_utils.hpp"
#include "utils/utils.hpp"
namespace repertory::utils::path {
auto absolute(std::string path) -> std::string {
#ifdef _WIN32
if (not path.empty() && ::PathIsRelative(&path[0u])) {
std::string temp;
temp.resize(MAX_PATH + 1);
path = _fullpath(&temp[0u], &path[0u], MAX_PATH);
}
#else
if (not path.empty() && (path[0u] != '/')) {
auto found = false;
auto tmp = path;
do {
auto *res = realpath(&tmp[0u], nullptr);
if (res) {
path = combine(res, {path.substr(tmp.size())});
free(res);
found = true;
} else if (tmp == ".") {
found = true;
} else {
tmp = dirname(&tmp[0u]);
}
} while (not found);
}
#endif
return finalize(path);
}
auto combine(std::string path, const std::vector<std::string> &paths)
-> std::string {
return finalize(
std::accumulate(paths.begin(), paths.end(), path,
[](std::string next_path, const auto &path_part) {
if (next_path.empty()) {
return path_part;
}
return next_path + (directory_seperator + path_part);
}));
}
auto create_api_path(std::string path) -> std::string {
if (path.empty() || (path == ".") || (path == "/")) {
return "/";
}
format_path(path, "/", "\\");
if (path.find("./") == 0) {
path = path.substr(1);
}
if (path[0u] != '/') {
path = "/" + path;
}
if ((path != "/") && utils::string::ends_with(path, "/")) {
utils::string::right_trim(path, '/');
}
return path;
}
auto finalize(std::string path) -> std::string {
format_path(path, not_directory_seperator, directory_seperator);
format_path(path, directory_seperator, not_directory_seperator);
if ((path.size() > 1u) &&
(path[path.size() - 1u] == directory_seperator[0u])) {
path = path.substr(0u, path.size() - 1u);
}
#ifdef _WIN32
if ((path.size() >= 2u) && (path[1u] == ':')) {
path[0u] = utils::string::to_lower(std::string(1u, path[0u]))[0u];
}
#endif
return path;
}
auto format_path(std::string &path, const std::string &sep,
const std::string &not_sep) -> std::string & {
std::replace(path.begin(), path.end(), not_sep[0u], sep[0u]);
while (utils::string::contains(path, sep + sep)) {
utils::string::replace(path, sep + sep, sep);
}
return path;
}
auto get_parent_api_path(const std::string &path) -> std::string {
std::string ret;
if (path != "/") {
ret = path.substr(0, path.rfind('/') + 1);
if (ret != "/") {
ret = utils::string::right_trim(ret, '/');
}
}
return ret;
}
#ifndef _WIN32
auto get_parent_directory(std::string path) -> std::string {
auto ret = std::string(dirname(&path[0u]));
if (ret == ".") {
ret = "/";
}
return ret;
}
#endif
auto is_ads_file_path([[maybe_unused]] const std::string &path) -> bool {
#ifdef _WIN32
return utils::string::contains(path, ":");
#else
return false;
#endif
}
auto is_trash_directory(std::string path) -> bool {
path = create_api_path(utils::string::to_lower(path));
if (utils::string::begins_with(path, "/.trash-") ||
utils::string::begins_with(path, "/.trashes") ||
utils::string::begins_with(path, "/$recycle.bin")) {
return true;
}
return false;
}
auto remove_file_name(std::string path) -> std::string {
path = finalize(path);
#ifdef _WIN32
::PathRemoveFileSpec(&path[0u]);
path = path.c_str();
#else
if (path != "/") {
auto i = path.size() - 1;
while ((i != 0) && (path[i] != '/')) {
i--;
}
path = (i > 0) ? finalize(path.substr(0, i)) : "/";
}
#endif
return path;
}
#ifndef _WIN32
auto resolve(std::string path) -> std::string {
std::string home{};
use_getpwuid(getuid(), [&home](struct passwd *pw) {
home = (pw->pw_dir ? pw->pw_dir : "");
if (home.empty() || ((home == "/") && (getuid() != 0))) {
home = combine("/home", {pw->pw_name});
}
});
return finalize(utils::string::replace(path, "~", home));
}
#endif
auto strip_to_file_name(std::string path) -> std::string {
#ifdef _WIN32
return ::PathFindFileName(&path[0u]);
#else
return utils::string::contains(path, "/") ? basename(&path[0u]) : path;
#endif
}
} // namespace repertory::utils::path
/*
Copyright <2018-2023> <scott.e.graves@protonmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "utils/path_utils.hpp"
#include "types/repertory.hpp"
#include "utils/error_utils.hpp"
#include "utils/string_utils.hpp"
#include "utils/utils.hpp"
namespace repertory::utils::path {
auto absolute(std::string path) -> std::string {
#ifdef _WIN32
if (not path.empty() && ::PathIsRelative(&path[0u])) {
std::string temp;
temp.resize(MAX_PATH + 1);
path = _fullpath(&temp[0u], &path[0u], MAX_PATH);
}
#else
if (not path.empty() && (path[0u] != '/')) {
auto found = false;
auto tmp = path;
do {
auto *res = realpath(&tmp[0u], nullptr);
if (res) {
path = combine(res, {path.substr(tmp.size())});
free(res);
found = true;
} else if (tmp == ".") {
found = true;
} else {
tmp = dirname(&tmp[0u]);
}
} while (not found);
}
#endif
return finalize(path);
}
auto combine(std::string path, const std::vector<std::string> &paths)
-> std::string {
return finalize(
std::accumulate(paths.begin(), paths.end(), path,
[](std::string next_path, const auto &path_part) {
if (next_path.empty()) {
return path_part;
}
return next_path + (directory_seperator + path_part);
}));
}
auto create_api_path(std::string path) -> std::string {
if (path.empty() || (path == ".") || (path == "/")) {
return "/";
}
format_path(path, "/", "\\");
if (path.find("./") == 0) {
path = path.substr(1);
}
if (path[0u] != '/') {
path = "/" + path;
}
if ((path != "/") && utils::string::ends_with(path, "/")) {
utils::string::right_trim(path, '/');
}
return path;
}
auto finalize(std::string path) -> std::string {
format_path(path, not_directory_seperator, directory_seperator);
format_path(path, directory_seperator, not_directory_seperator);
if ((path.size() > 1u) &&
(path[path.size() - 1u] == directory_seperator[0u])) {
path = path.substr(0u, path.size() - 1u);
}
#ifdef _WIN32
if ((path.size() >= 2u) && (path[1u] == ':')) {
path[0u] = utils::string::to_lower(std::string(1u, path[0u]))[0u];
}
#endif
return path;
}
auto format_path(std::string &path, const std::string &sep,
const std::string &not_sep) -> std::string & {
std::replace(path.begin(), path.end(), not_sep[0u], sep[0u]);
while (utils::string::contains(path, sep + sep)) {
utils::string::replace(path, sep + sep, sep);
}
return path;
}
auto get_parent_api_path(const std::string &path) -> std::string {
std::string ret;
if (path != "/") {
ret = path.substr(0, path.rfind('/') + 1);
if (ret != "/") {
ret = utils::string::right_trim(ret, '/');
}
}
return ret;
}
#ifndef _WIN32
auto get_parent_directory(std::string path) -> std::string {
auto ret = std::string(dirname(&path[0u]));
if (ret == ".") {
ret = "/";
}
return ret;
}
#endif
auto is_ads_file_path([[maybe_unused]] const std::string &path) -> bool {
#ifdef _WIN32
return utils::string::contains(path, ":");
#else
return false;
#endif
}
auto is_trash_directory(std::string path) -> bool {
path = create_api_path(utils::string::to_lower(path));
if (utils::string::begins_with(path, "/.trash-") ||
utils::string::begins_with(path, "/.trashes") ||
utils::string::begins_with(path, "/$recycle.bin")) {
return true;
}
return false;
}
auto remove_file_name(std::string path) -> std::string {
path = finalize(path);
#ifdef _WIN32
::PathRemoveFileSpec(&path[0u]);
path = path.c_str();
#else
if (path != "/") {
auto i = path.size() - 1;
while ((i != 0) && (path[i] != '/')) {
i--;
}
path = (i > 0) ? finalize(path.substr(0, i)) : "/";
}
#endif
return path;
}
#ifndef _WIN32
auto resolve(std::string path) -> std::string {
std::string home{};
use_getpwuid(getuid(), [&home](struct passwd *pw) {
home = (pw->pw_dir ? pw->pw_dir : "");
if (home.empty() || ((home == "/") && (getuid() != 0))) {
home = combine("/home", {pw->pw_name});
}
});
return finalize(utils::string::replace(path, "~", home));
}
#endif
auto strip_to_file_name(std::string path) -> std::string {
#ifdef _WIN32
return ::PathFindFileName(&path[0u]);
#else
return utils::string::contains(path, "/") ? basename(&path[0u]) : path;
#endif
}
} // namespace repertory::utils::path

View File

@@ -1,121 +1,121 @@
/*
Copyright <2018-2023> <scott.e.graves@protonmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "utils/polling.hpp"
#include "app_config.hpp"
namespace repertory {
polling polling::instance_;
void polling::frequency_thread(
std::function<std::uint32_t()> get_frequency_seconds, frequency freq) {
while (not stop_requested_) {
std::deque<std::future<void>> futures;
unique_mutex_lock l(mutex_);
if (not stop_requested_ &&
notify_.wait_for(l, std::chrono::seconds(get_frequency_seconds())) ==
std::cv_status::timeout) {
for (const auto &kv : items_) {
if (kv.second.freq == freq) {
futures.emplace_back(
std::async(std::launch::async, [this, &freq, kv]() -> void {
if (config_->get_event_level() == event_level::verbose ||
freq != frequency::second) {
event_system::instance().raise<polling_item_begin>(kv.first);
}
kv.second.action();
if (config_->get_event_level() == event_level::verbose ||
freq != frequency::second) {
event_system::instance().raise<polling_item_end>(kv.first);
}
}));
}
}
l.unlock();
while (not futures.empty()) {
futures.front().wait();
futures.pop_front();
}
}
}
}
void polling::remove_callback(const std::string &name) {
mutex_lock l(mutex_);
items_.erase(name);
}
void polling::set_callback(const polling_item &pi) {
mutex_lock l(mutex_);
items_[pi.name] = pi;
}
void polling::start(app_config *config) {
mutex_lock l(start_stop_mutex_);
if (not high_frequency_thread_) {
event_system::instance().raise<service_started>("polling");
config_ = config;
stop_requested_ = false;
high_frequency_thread_ = std::make_unique<std::thread>([this]() -> void {
this->frequency_thread(
[this]() -> std::uint32_t {
return config_->get_high_frequency_interval_secs();
},
frequency::high);
});
low_frequency_thread_ = std::make_unique<std::thread>([this]() -> void {
this->frequency_thread(
[this]() -> std::uint32_t {
return config_->get_low_frequency_interval_secs();
},
frequency::low);
});
second_frequency_thread_ = std::make_unique<std::thread>([this]() -> void {
this->frequency_thread([]() -> std::uint32_t { return 1U; },
frequency::second);
});
}
}
void polling::stop() {
if (high_frequency_thread_) {
event_system::instance().raise<service_shutdown_begin>("polling");
mutex_lock l(start_stop_mutex_);
if (high_frequency_thread_) {
{
stop_requested_ = true;
mutex_lock l2(mutex_);
notify_.notify_all();
}
high_frequency_thread_->join();
low_frequency_thread_->join();
second_frequency_thread_->join();
high_frequency_thread_.reset();
low_frequency_thread_.reset();
second_frequency_thread_.reset();
}
event_system::instance().raise<service_shutdown_end>("polling");
}
}
} // namespace repertory
/*
Copyright <2018-2023> <scott.e.graves@protonmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "utils/polling.hpp"
#include "app_config.hpp"
namespace repertory {
polling polling::instance_;
void polling::frequency_thread(
std::function<std::uint32_t()> get_frequency_seconds, frequency freq) {
while (not stop_requested_) {
std::deque<std::future<void>> futures;
unique_mutex_lock l(mutex_);
if (not stop_requested_ &&
notify_.wait_for(l, std::chrono::seconds(get_frequency_seconds())) ==
std::cv_status::timeout) {
for (const auto &kv : items_) {
if (kv.second.freq == freq) {
futures.emplace_back(
std::async(std::launch::async, [this, &freq, kv]() -> void {
if (config_->get_event_level() == event_level::verbose ||
freq != frequency::second) {
event_system::instance().raise<polling_item_begin>(kv.first);
}
kv.second.action();
if (config_->get_event_level() == event_level::verbose ||
freq != frequency::second) {
event_system::instance().raise<polling_item_end>(kv.first);
}
}));
}
}
l.unlock();
while (not futures.empty()) {
futures.front().wait();
futures.pop_front();
}
}
}
}
void polling::remove_callback(const std::string &name) {
mutex_lock l(mutex_);
items_.erase(name);
}
void polling::set_callback(const polling_item &pi) {
mutex_lock l(mutex_);
items_[pi.name] = pi;
}
void polling::start(app_config *config) {
mutex_lock l(start_stop_mutex_);
if (not high_frequency_thread_) {
event_system::instance().raise<service_started>("polling");
config_ = config;
stop_requested_ = false;
high_frequency_thread_ = std::make_unique<std::thread>([this]() -> void {
this->frequency_thread(
[this]() -> std::uint32_t {
return config_->get_high_frequency_interval_secs();
},
frequency::high);
});
low_frequency_thread_ = std::make_unique<std::thread>([this]() -> void {
this->frequency_thread(
[this]() -> std::uint32_t {
return config_->get_low_frequency_interval_secs();
},
frequency::low);
});
second_frequency_thread_ = std::make_unique<std::thread>([this]() -> void {
this->frequency_thread([]() -> std::uint32_t { return 1U; },
frequency::second);
});
}
}
void polling::stop() {
if (high_frequency_thread_) {
event_system::instance().raise<service_shutdown_begin>("polling");
mutex_lock l(start_stop_mutex_);
if (high_frequency_thread_) {
{
stop_requested_ = true;
mutex_lock l2(mutex_);
notify_.notify_all();
}
high_frequency_thread_->join();
low_frequency_thread_->join();
second_frequency_thread_->join();
high_frequency_thread_.reset();
low_frequency_thread_.reset();
second_frequency_thread_.reset();
}
event_system::instance().raise<service_shutdown_end>("polling");
}
}
} // namespace repertory

View File

@@ -1,72 +1,72 @@
/*
Copyright <2018-2023> <scott.e.graves@protonmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "utils/rocksdb_utils.hpp"
#include "app_config.hpp"
#include "events/event_system.hpp"
#include "types/startup_exception.hpp"
#include "utils/error_utils.hpp"
#include "utils/path_utils.hpp"
namespace repertory::utils::db {
void create_rocksdb(const app_config &config, const std::string &name,
std::unique_ptr<rocksdb::DB> &db) {
rocksdb::Options options{};
options.create_if_missing = true;
options.db_log_dir = config.get_log_directory();
options.keep_log_file_num = 10;
rocksdb::DB *db_ptr{};
const auto status = rocksdb::DB::Open(
options, utils::path::combine(config.get_data_directory(), {name}),
&db_ptr);
if (status.ok()) {
db.reset(db_ptr);
} else {
utils::error::raise_error(__FUNCTION__, status.ToString());
throw startup_exception(status.ToString());
}
}
void create_rocksdb(
const app_config &config, const std::string &name,
const std::vector<rocksdb::ColumnFamilyDescriptor> &families,
std::vector<rocksdb::ColumnFamilyHandle *> &handles,
std::unique_ptr<rocksdb::DB> &db) {
rocksdb::Options options{};
options.create_if_missing = true;
options.create_missing_column_families = true;
options.db_log_dir = config.get_log_directory();
options.keep_log_file_num = 10;
rocksdb::DB *db_ptr{};
const auto status = rocksdb::DB::Open(
options, utils::path::combine(config.get_data_directory(), {name}),
families, &handles, &db_ptr);
if (status.ok()) {
db.reset(db_ptr);
} else {
utils::error::raise_error(__FUNCTION__, status.ToString());
throw startup_exception(status.ToString());
}
}
} // namespace repertory::utils::db
/*
Copyright <2018-2023> <scott.e.graves@protonmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "utils/rocksdb_utils.hpp"
#include "app_config.hpp"
#include "events/event_system.hpp"
#include "types/startup_exception.hpp"
#include "utils/error_utils.hpp"
#include "utils/path_utils.hpp"
namespace repertory::utils::db {
void create_rocksdb(const app_config &config, const std::string &name,
std::unique_ptr<rocksdb::DB> &db) {
rocksdb::Options options{};
options.create_if_missing = true;
options.db_log_dir = config.get_log_directory();
options.keep_log_file_num = 10;
rocksdb::DB *db_ptr{};
const auto status = rocksdb::DB::Open(
options, utils::path::combine(config.get_data_directory(), {name}),
&db_ptr);
if (status.ok()) {
db.reset(db_ptr);
} else {
utils::error::raise_error(__FUNCTION__, status.ToString());
throw startup_exception(status.ToString());
}
}
void create_rocksdb(
const app_config &config, const std::string &name,
const std::vector<rocksdb::ColumnFamilyDescriptor> &families,
std::vector<rocksdb::ColumnFamilyHandle *> &handles,
std::unique_ptr<rocksdb::DB> &db) {
rocksdb::Options options{};
options.create_if_missing = true;
options.create_missing_column_families = true;
options.db_log_dir = config.get_log_directory();
options.keep_log_file_num = 10;
rocksdb::DB *db_ptr{};
const auto status = rocksdb::DB::Open(
options, utils::path::combine(config.get_data_directory(), {name}),
families, &handles, &db_ptr);
if (status.ok()) {
db.reset(db_ptr);
} else {
utils::error::raise_error(__FUNCTION__, status.ToString());
throw startup_exception(status.ToString());
}
}
} // namespace repertory::utils::db

View File

@@ -1,65 +1,65 @@
/*
Copyright <2018-2023> <scott.e.graves@protonmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "utils/single_thread_service_base.hpp"
#include "events/event_system.hpp"
#include "events/events.hpp"
#include "types/repertory.hpp"
namespace repertory {
void single_thread_service_base::notify_all() const {
mutex_lock lock(get_mutex());
notify_.notify_all();
}
void single_thread_service_base::start() {
mutex_lock lock(mtx_);
if (not thread_) {
stop_requested_ = false;
on_start();
thread_ = std::make_unique<std::thread>([this]() {
event_system::instance().raise<service_started>(service_name_);
while (not stop_requested_) {
service_function();
}
});
}
}
void single_thread_service_base::stop() {
if (thread_) {
event_system::instance().raise<service_shutdown_begin>(service_name_);
unique_mutex_lock lock(mtx_);
if (thread_) {
stop_requested_ = true;
notify_.notify_all();
lock.unlock();
thread_->join();
thread_.reset();
on_stop();
}
event_system::instance().raise<service_shutdown_end>(service_name_);
}
}
} // namespace repertory
/*
Copyright <2018-2023> <scott.e.graves@protonmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "utils/single_thread_service_base.hpp"
#include "events/event_system.hpp"
#include "events/events.hpp"
#include "types/repertory.hpp"
namespace repertory {
void single_thread_service_base::notify_all() const {
mutex_lock lock(get_mutex());
notify_.notify_all();
}
void single_thread_service_base::start() {
mutex_lock lock(mtx_);
if (not thread_) {
stop_requested_ = false;
on_start();
thread_ = std::make_unique<std::thread>([this]() {
event_system::instance().raise<service_started>(service_name_);
while (not stop_requested_) {
service_function();
}
});
}
}
void single_thread_service_base::stop() {
if (thread_) {
event_system::instance().raise<service_shutdown_begin>(service_name_);
unique_mutex_lock lock(mtx_);
if (thread_) {
stop_requested_ = true;
notify_.notify_all();
lock.unlock();
thread_->join();
thread_.reset();
on_stop();
}
event_system::instance().raise<service_shutdown_end>(service_name_);
}
}
} // namespace repertory

View File

@@ -1,223 +1,223 @@
/*
Copyright <2018-2023> <scott.e.graves@protonmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "utils/string_utils.hpp"
namespace repertory::utils::string {
/* constexpr c++20 */ auto ends_with(std::string_view str, std::string_view val)
-> bool {
if (val.size() > str.size()) {
return false;
}
return std::equal(val.rbegin(), val.rend(), str.rbegin());
}
auto from_bool(bool val) -> std::string { return std::to_string(val); }
auto from_dynamic_bitset(const boost::dynamic_bitset<> &bitset) -> std::string {
std::stringstream ss;
boost::archive::text_oarchive archive(ss);
archive << bitset;
return ss.str();
}
auto from_utf8(const std::string &str) -> std::wstring {
return str.empty()
? L""
: std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t>()
.from_bytes(str);
}
/* constexpr c++20 */ auto is_numeric(std::string_view s) -> bool {
if ((s.length() > 1u) && (s[0u] == '+' || s[0u] == '-')) {
s = s.substr(1u);
}
if (s.empty()) {
return false;
}
auto has_decimal = false;
return std::find_if(
s.begin(), s.end(),
[&has_decimal](const std::string_view::value_type &c) -> bool {
if (has_decimal && c == '.') {
return true;
}
if ((has_decimal = has_decimal || c == '.')) {
return false;
}
return not std::isdigit(c);
}) == s.end();
}
auto join(const std::vector<std::string> &arr, const char &delim)
-> std::string {
if (arr.empty()) {
return "";
}
return std::accumulate(
std::next(arr.begin()), arr.end(), arr[0u],
[&delim](auto s, const auto &v) { return s + delim + v; });
}
auto left_trim(std::string &s) -> std::string & { return left_trim(s, ' '); }
auto left_trim(std::string &s, const char &c) -> std::string & {
s.erase(0, s.find_first_not_of(c));
return s;
}
auto replace(std::string &src, const char &character, const char &with)
-> std::string & {
std::replace(src.begin(), src.end(), character, with);
return src;
}
auto replace(std::string &src, const std::string &find, const std::string &with,
size_t start_pos) -> std::string & {
if (!src.empty() && (start_pos < src.size())) {
while ((start_pos = src.find(find, start_pos)) != std::string::npos) {
src.replace(start_pos, find.size(), with);
start_pos += with.size();
}
}
return src;
}
auto replace_copy(std::string src, const char &character, const char &with)
-> std::string {
std::replace(src.begin(), src.end(), character, with);
return src;
}
auto replace_copy(std::string src, const std::string &find,
const std::string &with, size_t start_pos) -> std::string {
return replace(src, find, with, start_pos);
}
auto right_trim(std::string &s) -> std::string & { return right_trim(s, ' '); }
auto right_trim(std::string &s, const char &c) -> std::string & {
s.erase(s.find_last_not_of(c) + 1);
return s;
}
auto split(const std::string &str, const char &delim, bool should_trim)
-> std::vector<std::string> {
std::vector<std::string> ret;
std::stringstream ss(str);
std::string item;
while (std::getline(ss, item, delim)) {
ret.push_back(should_trim ? trim(item) : item);
}
return ret;
}
auto to_bool(std::string val) -> bool {
auto b = false;
trim(val);
if (is_numeric(val)) {
if (contains(val, ".")) {
b = (to_double(val) != 0.0);
} else {
std::istringstream(val) >> b;
}
} else {
std::istringstream(to_lower(val)) >> std::boolalpha >> b;
}
return b;
}
auto to_double(const std::string &str) -> double { return std::stod(str); }
auto to_dynamic_bitset(const std::string &val) -> boost::dynamic_bitset<> {
std::stringstream ss(val);
boost::dynamic_bitset<> bitset;
boost::archive::text_iarchive archive(ss);
archive >> bitset;
return bitset;
}
auto to_int32(const std::string &val) -> std::int32_t { return std::stoi(val); }
auto to_int64(const std::string &val) -> std::int64_t {
return std::stoll(val);
}
auto to_lower(std::string str) -> std::string {
std::transform(str.begin(), str.end(), str.begin(), ::tolower);
return str;
}
auto to_size_t(const std::string &val) -> std::size_t {
return static_cast<std::size_t>(std::stoull(val));
}
auto to_uint8(const std::string &val) -> std::uint8_t {
return static_cast<std::uint8_t>(std::stoul(val));
}
auto to_uint16(const std::string &val) -> std::uint16_t {
return static_cast<std::uint16_t>(std::stoul(val));
}
auto to_uint32(const std::string &val) -> std::uint32_t {
return static_cast<std::uint32_t>(std::stoul(val));
}
auto to_uint64(const std::string &val) -> std::uint64_t {
return std::stoull(val);
}
auto to_upper(std::string str) -> std::string {
std::transform(str.begin(), str.end(), str.begin(), ::toupper);
return str;
}
auto to_utf8(std::string str) -> std::string { return str; }
auto to_utf8(const std::wstring &str) -> std::string {
return str.empty()
? ""
: std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t>()
.to_bytes(str);
}
auto trim(std::string &str) -> std::string & {
return right_trim(left_trim(str));
}
auto trim(std::string &str, const char &c) -> std::string & {
return right_trim(left_trim(str, c), c);
}
auto trim_copy(std::string str) -> std::string {
return right_trim(left_trim(str));
}
auto trim_copy(std::string str, const char &c) -> std::string {
return right_trim(left_trim(str, c), c);
}
} // namespace repertory::utils::string
/*
Copyright <2018-2023> <scott.e.graves@protonmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "utils/string_utils.hpp"
namespace repertory::utils::string {
/* constexpr c++20 */ auto ends_with(std::string_view str, std::string_view val)
-> bool {
if (val.size() > str.size()) {
return false;
}
return std::equal(val.rbegin(), val.rend(), str.rbegin());
}
auto from_bool(bool val) -> std::string { return std::to_string(val); }
auto from_dynamic_bitset(const boost::dynamic_bitset<> &bitset) -> std::string {
std::stringstream ss;
boost::archive::text_oarchive archive(ss);
archive << bitset;
return ss.str();
}
auto from_utf8(const std::string &str) -> std::wstring {
return str.empty()
? L""
: std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t>()
.from_bytes(str);
}
/* constexpr c++20 */ auto is_numeric(std::string_view s) -> bool {
if ((s.length() > 1u) && (s[0u] == '+' || s[0u] == '-')) {
s = s.substr(1u);
}
if (s.empty()) {
return false;
}
auto has_decimal = false;
return std::find_if(
s.begin(), s.end(),
[&has_decimal](const std::string_view::value_type &c) -> bool {
if (has_decimal && c == '.') {
return true;
}
if ((has_decimal = has_decimal || c == '.')) {
return false;
}
return not std::isdigit(c);
}) == s.end();
}
auto join(const std::vector<std::string> &arr, const char &delim)
-> std::string {
if (arr.empty()) {
return "";
}
return std::accumulate(
std::next(arr.begin()), arr.end(), arr[0u],
[&delim](auto s, const auto &v) { return s + delim + v; });
}
auto left_trim(std::string &s) -> std::string & { return left_trim(s, ' '); }
auto left_trim(std::string &s, const char &c) -> std::string & {
s.erase(0, s.find_first_not_of(c));
return s;
}
auto replace(std::string &src, const char &character, const char &with)
-> std::string & {
std::replace(src.begin(), src.end(), character, with);
return src;
}
auto replace(std::string &src, const std::string &find, const std::string &with,
size_t start_pos) -> std::string & {
if (!src.empty() && (start_pos < src.size())) {
while ((start_pos = src.find(find, start_pos)) != std::string::npos) {
src.replace(start_pos, find.size(), with);
start_pos += with.size();
}
}
return src;
}
auto replace_copy(std::string src, const char &character, const char &with)
-> std::string {
std::replace(src.begin(), src.end(), character, with);
return src;
}
auto replace_copy(std::string src, const std::string &find,
const std::string &with, size_t start_pos) -> std::string {
return replace(src, find, with, start_pos);
}
auto right_trim(std::string &s) -> std::string & { return right_trim(s, ' '); }
auto right_trim(std::string &s, const char &c) -> std::string & {
s.erase(s.find_last_not_of(c) + 1);
return s;
}
auto split(const std::string &str, const char &delim, bool should_trim)
-> std::vector<std::string> {
std::vector<std::string> ret;
std::stringstream ss(str);
std::string item;
while (std::getline(ss, item, delim)) {
ret.push_back(should_trim ? trim(item) : item);
}
return ret;
}
auto to_bool(std::string val) -> bool {
auto b = false;
trim(val);
if (is_numeric(val)) {
if (contains(val, ".")) {
b = (to_double(val) != 0.0);
} else {
std::istringstream(val) >> b;
}
} else {
std::istringstream(to_lower(val)) >> std::boolalpha >> b;
}
return b;
}
auto to_double(const std::string &str) -> double { return std::stod(str); }
auto to_dynamic_bitset(const std::string &val) -> boost::dynamic_bitset<> {
std::stringstream ss(val);
boost::dynamic_bitset<> bitset;
boost::archive::text_iarchive archive(ss);
archive >> bitset;
return bitset;
}
auto to_int32(const std::string &val) -> std::int32_t { return std::stoi(val); }
auto to_int64(const std::string &val) -> std::int64_t {
return std::stoll(val);
}
auto to_lower(std::string str) -> std::string {
std::transform(str.begin(), str.end(), str.begin(), ::tolower);
return str;
}
auto to_size_t(const std::string &val) -> std::size_t {
return static_cast<std::size_t>(std::stoull(val));
}
auto to_uint8(const std::string &val) -> std::uint8_t {
return static_cast<std::uint8_t>(std::stoul(val));
}
auto to_uint16(const std::string &val) -> std::uint16_t {
return static_cast<std::uint16_t>(std::stoul(val));
}
auto to_uint32(const std::string &val) -> std::uint32_t {
return static_cast<std::uint32_t>(std::stoul(val));
}
auto to_uint64(const std::string &val) -> std::uint64_t {
return std::stoull(val);
}
auto to_upper(std::string str) -> std::string {
std::transform(str.begin(), str.end(), str.begin(), ::toupper);
return str;
}
auto to_utf8(std::string str) -> std::string { return str; }
auto to_utf8(const std::wstring &str) -> std::string {
return str.empty()
? ""
: std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t>()
.to_bytes(str);
}
auto trim(std::string &str) -> std::string & {
return right_trim(left_trim(str));
}
auto trim(std::string &str, const char &c) -> std::string & {
return right_trim(left_trim(str, c), c);
}
auto trim_copy(std::string str) -> std::string {
return right_trim(left_trim(str));
}
auto trim_copy(std::string str, const char &c) -> std::string {
return right_trim(left_trim(str, c), c);
}
} // namespace repertory::utils::string

View File

@@ -1,64 +1,64 @@
/*
Copyright <2018-2023> <scott.e.graves@protonmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "utils/throttle.hpp"
#include "types/repertory.hpp"
namespace repertory {
void throttle::decrement() {
mutex_lock l(mutex_);
if (not shutdown_) {
if (count_ > 0) {
count_--;
}
notify_.notify_one();
}
}
void throttle::increment_or_wait() {
unique_mutex_lock l(mutex_);
if (not shutdown_) {
if (count_ >= max_size_) {
notify_.wait(l);
}
if (not shutdown_) {
count_++;
}
}
}
void throttle::reset() {
unique_mutex_lock l(mutex_);
if (shutdown_) {
count_ = 0;
shutdown_ = false;
}
}
void throttle::shutdown() {
if (not shutdown_) {
unique_mutex_lock l(mutex_);
shutdown_ = true;
notify_.notify_all();
}
}
} // namespace repertory
/*
Copyright <2018-2023> <scott.e.graves@protonmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "utils/throttle.hpp"
#include "types/repertory.hpp"
namespace repertory {
void throttle::decrement() {
mutex_lock l(mutex_);
if (not shutdown_) {
if (count_ > 0) {
count_--;
}
notify_.notify_one();
}
}
void throttle::increment_or_wait() {
unique_mutex_lock l(mutex_);
if (not shutdown_) {
if (count_ >= max_size_) {
notify_.wait(l);
}
if (not shutdown_) {
count_++;
}
}
}
void throttle::reset() {
unique_mutex_lock l(mutex_);
if (shutdown_) {
count_ = 0;
shutdown_ = false;
}
}
void throttle::shutdown() {
if (not shutdown_) {
unique_mutex_lock l(mutex_);
shutdown_ = true;
notify_.notify_all();
}
}
} // namespace repertory

View File

@@ -1,54 +1,54 @@
/*
Copyright <2018-2023> <scott.e.graves@protonmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "utils/timeout.hpp"
#include "types/repertory.hpp"
namespace repertory {
timeout::timeout(std::function<void()> timeout_callback,
const std::chrono::system_clock::duration &duration)
: timeout_killed_(duration == 0s) {
if (not timeout_killed_) {
timeout_thread_ =
std::make_unique<std::thread>([this, duration, timeout_callback]() {
unique_mutex_lock lock(timeout_mutex_);
if (not timeout_killed_) {
timeout_notify_.wait_for(lock, duration);
if (not timeout_killed_) {
timeout_callback();
}
}
});
}
}
void timeout::disable() {
if (not timeout_killed_) {
timeout_killed_ = true;
unique_mutex_lock lock(timeout_mutex_);
timeout_notify_.notify_all();
lock.unlock();
timeout_thread_->join();
timeout_thread_.reset();
}
}
} // namespace repertory
/*
Copyright <2018-2023> <scott.e.graves@protonmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "utils/timeout.hpp"
#include "types/repertory.hpp"
namespace repertory {
timeout::timeout(std::function<void()> timeout_callback,
const std::chrono::system_clock::duration &duration)
: timeout_killed_(duration == 0s) {
if (not timeout_killed_) {
timeout_thread_ =
std::make_unique<std::thread>([this, duration, timeout_callback]() {
unique_mutex_lock lock(timeout_mutex_);
if (not timeout_killed_) {
timeout_notify_.wait_for(lock, duration);
if (not timeout_killed_) {
timeout_callback();
}
}
});
}
}
void timeout::disable() {
if (not timeout_killed_) {
timeout_killed_ = true;
unique_mutex_lock lock(timeout_mutex_);
timeout_notify_.notify_all();
lock.unlock();
timeout_thread_->join();
timeout_thread_.reset();
}
}
} // namespace repertory

View File

@@ -1,277 +1,277 @@
/*
Copyright <2018-2023> <scott.e.graves@protonmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#ifndef _WIN32
#include "utils/unix/unix_utils.hpp"
#include "utils/error_utils.hpp"
#include "utils/utils.hpp"
namespace repertory::utils {
#ifndef __APPLE__
auto convert_to_uint64(const pthread_t &t) -> std::uint64_t {
return static_cast<std::uint64_t>(t);
}
#endif
auto from_api_error(const api_error &e) -> int {
switch (e) {
case api_error::access_denied:
return -EACCES;
case api_error::bad_address:
return -EFAULT;
case api_error::directory_end_of_files:
return -EOF;
case api_error::directory_exists:
return -EISDIR;
case api_error::directory_not_empty:
return -ENOTEMPTY;
case api_error::directory_not_found:
return -ENOTDIR;
case api_error::download_failed:
#ifdef __APPLE__
return -EBADMSG;
#else
return -EREMOTEIO;
#endif
case api_error::error:
return -EIO;
case api_error::item_exists:
return -EEXIST;
case api_error::file_in_use:
return -EBUSY;
case api_error::invalid_operation:
return -EINVAL;
case api_error::item_not_found:
return -ENOENT;
case api_error::out_of_memory:
return -ENOMEM;
case api_error::no_disk_space:
return -ENOSPC;
case api_error::os_error:
return -errno;
case api_error::permission_denied:
return -EPERM;
case api_error::success:
return 0;
case api_error::not_supported:
return -ENOTSUP;
case api_error::not_implemented:
return -ENOSYS;
case api_error::upload_failed:
return -ENETDOWN;
case api_error::xattr_buffer_small:
return -ERANGE;
case api_error::xattr_exists:
return -EEXIST;
case api_error::xattr_not_found:
#ifdef __APPLE__
return -ENOATTR;
#else
return -ENODATA;
#endif
case api_error::xattr_too_big:
#ifdef __APPLE__
return -ENAMETOOLONG;
#else
return -E2BIG;
#endif
default:
return -EIO;
}
}
auto get_last_error_code() -> int { return errno; }
auto get_thread_id() -> std::uint64_t {
return convert_to_uint64(pthread_self());
}
auto is_uid_member_of_group(const uid_t &uid, const gid_t &gid) -> bool {
static const auto function_name = __FUNCTION__;
std::vector<gid_t> groups{};
use_getpwuid(uid, [&groups](struct passwd *pw) {
int group_count{};
if (getgrouplist(pw->pw_name, pw->pw_gid, nullptr, &group_count) < 0) {
groups.resize(static_cast<std::size_t>(group_count));
#ifdef __APPLE__
getgrouplist(pw->pw_name, pw->pw_gid,
reinterpret_cast<int *>(groups.data()), &group_count);
#else
getgrouplist(pw->pw_name, pw->pw_gid, groups.data(), &group_count);
#endif
}
});
return collection_includes(groups, gid);
}
auto to_api_error(int e) -> api_error {
switch (abs(e)) {
case 0:
return api_error::success;
case EBADF:
return api_error::invalid_handle;
case EACCES:
return api_error::access_denied;
case EFAULT:
return api_error::bad_address;
case EOF:
return api_error::directory_end_of_files;
case EISDIR:
return api_error::directory_exists;
case ENOTEMPTY:
return api_error::directory_not_empty;
case ENOTDIR:
return api_error::directory_not_found;
#ifdef __APPLE__
case EBADMSG:
return api_error::download_failed;
#else
case EREMOTEIO:
return api_error::download_failed;
#endif
case EIO:
return api_error::error;
case EEXIST:
return api_error::item_exists;
case EBUSY:
return api_error::file_in_use;
case EINVAL:
return api_error::invalid_operation;
case ENOENT:
return api_error::item_not_found;
case ENOMEM:
return api_error::out_of_memory;
case EPERM:
return api_error::permission_denied;
case ENOSPC:
return api_error::no_disk_space;
case ENOTSUP:
return api_error::not_supported;
case ENOSYS:
return api_error::not_implemented;
case ENETDOWN:
return api_error::upload_failed;
case ERANGE:
return api_error::xattr_buffer_small;
#ifdef __APPLE__
case ENOATTR:
return api_error::xattr_not_found;
#else
case ENODATA:
return api_error::xattr_not_found;
#endif
#ifdef __APPLE__
case ENAMETOOLONG:
return api_error::xattr_too_big;
#else
case E2BIG:
return api_error::xattr_too_big;
#endif
default:
return api_error::error;
}
}
void set_last_error_code(int error_code) { errno = error_code; }
auto unix_error_to_windows(int e) -> std::int32_t {
switch (e) {
case 0:
return STATUS_SUCCESS;
case EACCES:
case EPERM:
return STATUS_ACCESS_DENIED;
case EBADF:
return STATUS_INVALID_HANDLE;
case EBUSY:
return STATUS_DEVICE_BUSY;
case EEXIST:
return STATUS_OBJECT_NAME_EXISTS;
case EFAULT:
return STATUS_INVALID_ADDRESS;
case EFBIG:
return STATUS_FILE_TOO_LARGE;
case EINVAL:
return STATUS_INVALID_PARAMETER;
case EIO:
return STATUS_UNEXPECTED_IO_ERROR;
case EISDIR:
return STATUS_OBJECT_NAME_EXISTS;
case EMFILE:
return STATUS_INSUFFICIENT_RESOURCES;
case ENOENT:
return STATUS_OBJECT_NAME_NOT_FOUND;
case ENOEXEC:
return STATUS_INVALID_IMAGE_FORMAT;
case ENOMEM:
return STATUS_NO_MEMORY;
case ENOSPC:
return STATUS_DEVICE_INSUFFICIENT_RESOURCES;
case ENOTDIR:
return STATUS_OBJECT_NAME_NOT_FOUND;
default:
return STATUS_INTERNAL_ERROR;
}
}
auto unix_time_to_windows_time(const remote::file_time &ts) -> UINT64 {
return (ts / 100ull) + 116444736000000000ull;
}
void use_getpwuid(uid_t uid, std::function<void(struct passwd *pw)> fn) {
static std::mutex mtx{};
mutex_lock lock{mtx};
auto *pw = getpwuid(uid);
if (not pw) {
utils::error::raise_error(__FUNCTION__, "'getpwuid' returned nullptr");
return;
}
fn(pw);
}
void windows_create_to_unix(const UINT32 &create_options,
const UINT32 &granted_access, std::uint32_t &flags,
remote::file_mode &mode) {
mode = S_IRUSR | S_IWUSR;
flags = O_CREAT | O_RDWR;
if (create_options & FILE_DIRECTORY_FILE) {
mode |= S_IXUSR;
flags = O_DIRECTORY;
}
if ((granted_access & GENERIC_EXECUTE) ||
(granted_access & FILE_GENERIC_EXECUTE) ||
(granted_access & FILE_EXECUTE)) {
mode |= (S_IXUSR);
}
}
auto windows_time_to_unix_time(std::uint64_t t) -> remote::file_time {
return (t - 116444736000000000ull) * 100ull;
}
} // namespace repertory::utils
#endif // !_WIN32
/*
Copyright <2018-2023> <scott.e.graves@protonmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#ifndef _WIN32
#include "utils/unix/unix_utils.hpp"
#include "utils/error_utils.hpp"
#include "utils/utils.hpp"
namespace repertory::utils {
#ifndef __APPLE__
auto convert_to_uint64(const pthread_t &t) -> std::uint64_t {
return static_cast<std::uint64_t>(t);
}
#endif
auto from_api_error(const api_error &e) -> int {
switch (e) {
case api_error::access_denied:
return -EACCES;
case api_error::bad_address:
return -EFAULT;
case api_error::directory_end_of_files:
return -EOF;
case api_error::directory_exists:
return -EISDIR;
case api_error::directory_not_empty:
return -ENOTEMPTY;
case api_error::directory_not_found:
return -ENOTDIR;
case api_error::download_failed:
#ifdef __APPLE__
return -EBADMSG;
#else
return -EREMOTEIO;
#endif
case api_error::error:
return -EIO;
case api_error::item_exists:
return -EEXIST;
case api_error::file_in_use:
return -EBUSY;
case api_error::invalid_operation:
return -EINVAL;
case api_error::item_not_found:
return -ENOENT;
case api_error::out_of_memory:
return -ENOMEM;
case api_error::no_disk_space:
return -ENOSPC;
case api_error::os_error:
return -errno;
case api_error::permission_denied:
return -EPERM;
case api_error::success:
return 0;
case api_error::not_supported:
return -ENOTSUP;
case api_error::not_implemented:
return -ENOSYS;
case api_error::upload_failed:
return -ENETDOWN;
case api_error::xattr_buffer_small:
return -ERANGE;
case api_error::xattr_exists:
return -EEXIST;
case api_error::xattr_not_found:
#ifdef __APPLE__
return -ENOATTR;
#else
return -ENODATA;
#endif
case api_error::xattr_too_big:
#ifdef __APPLE__
return -ENAMETOOLONG;
#else
return -E2BIG;
#endif
default:
return -EIO;
}
}
auto get_last_error_code() -> int { return errno; }
auto get_thread_id() -> std::uint64_t {
return convert_to_uint64(pthread_self());
}
auto is_uid_member_of_group(const uid_t &uid, const gid_t &gid) -> bool {
static const auto function_name = __FUNCTION__;
std::vector<gid_t> groups{};
use_getpwuid(uid, [&groups](struct passwd *pw) {
int group_count{};
if (getgrouplist(pw->pw_name, pw->pw_gid, nullptr, &group_count) < 0) {
groups.resize(static_cast<std::size_t>(group_count));
#ifdef __APPLE__
getgrouplist(pw->pw_name, pw->pw_gid,
reinterpret_cast<int *>(groups.data()), &group_count);
#else
getgrouplist(pw->pw_name, pw->pw_gid, groups.data(), &group_count);
#endif
}
});
return collection_includes(groups, gid);
}
auto to_api_error(int e) -> api_error {
switch (abs(e)) {
case 0:
return api_error::success;
case EBADF:
return api_error::invalid_handle;
case EACCES:
return api_error::access_denied;
case EFAULT:
return api_error::bad_address;
case EOF:
return api_error::directory_end_of_files;
case EISDIR:
return api_error::directory_exists;
case ENOTEMPTY:
return api_error::directory_not_empty;
case ENOTDIR:
return api_error::directory_not_found;
#ifdef __APPLE__
case EBADMSG:
return api_error::download_failed;
#else
case EREMOTEIO:
return api_error::download_failed;
#endif
case EIO:
return api_error::error;
case EEXIST:
return api_error::item_exists;
case EBUSY:
return api_error::file_in_use;
case EINVAL:
return api_error::invalid_operation;
case ENOENT:
return api_error::item_not_found;
case ENOMEM:
return api_error::out_of_memory;
case EPERM:
return api_error::permission_denied;
case ENOSPC:
return api_error::no_disk_space;
case ENOTSUP:
return api_error::not_supported;
case ENOSYS:
return api_error::not_implemented;
case ENETDOWN:
return api_error::upload_failed;
case ERANGE:
return api_error::xattr_buffer_small;
#ifdef __APPLE__
case ENOATTR:
return api_error::xattr_not_found;
#else
case ENODATA:
return api_error::xattr_not_found;
#endif
#ifdef __APPLE__
case ENAMETOOLONG:
return api_error::xattr_too_big;
#else
case E2BIG:
return api_error::xattr_too_big;
#endif
default:
return api_error::error;
}
}
void set_last_error_code(int error_code) { errno = error_code; }
auto unix_error_to_windows(int e) -> std::int32_t {
switch (e) {
case 0:
return STATUS_SUCCESS;
case EACCES:
case EPERM:
return STATUS_ACCESS_DENIED;
case EBADF:
return STATUS_INVALID_HANDLE;
case EBUSY:
return STATUS_DEVICE_BUSY;
case EEXIST:
return STATUS_OBJECT_NAME_EXISTS;
case EFAULT:
return STATUS_INVALID_ADDRESS;
case EFBIG:
return STATUS_FILE_TOO_LARGE;
case EINVAL:
return STATUS_INVALID_PARAMETER;
case EIO:
return STATUS_UNEXPECTED_IO_ERROR;
case EISDIR:
return STATUS_OBJECT_NAME_EXISTS;
case EMFILE:
return STATUS_INSUFFICIENT_RESOURCES;
case ENOENT:
return STATUS_OBJECT_NAME_NOT_FOUND;
case ENOEXEC:
return STATUS_INVALID_IMAGE_FORMAT;
case ENOMEM:
return STATUS_NO_MEMORY;
case ENOSPC:
return STATUS_DEVICE_INSUFFICIENT_RESOURCES;
case ENOTDIR:
return STATUS_OBJECT_NAME_NOT_FOUND;
default:
return STATUS_INTERNAL_ERROR;
}
}
auto unix_time_to_windows_time(const remote::file_time &ts) -> UINT64 {
return (ts / 100ull) + 116444736000000000ull;
}
void use_getpwuid(uid_t uid, std::function<void(struct passwd *pw)> fn) {
static std::mutex mtx{};
mutex_lock lock{mtx};
auto *pw = getpwuid(uid);
if (not pw) {
utils::error::raise_error(__FUNCTION__, "'getpwuid' returned nullptr");
return;
}
fn(pw);
}
void windows_create_to_unix(const UINT32 &create_options,
const UINT32 &granted_access, std::uint32_t &flags,
remote::file_mode &mode) {
mode = S_IRUSR | S_IWUSR;
flags = O_CREAT | O_RDWR;
if (create_options & FILE_DIRECTORY_FILE) {
mode |= S_IXUSR;
flags = O_DIRECTORY;
}
if ((granted_access & GENERIC_EXECUTE) ||
(granted_access & FILE_GENERIC_EXECUTE) ||
(granted_access & FILE_EXECUTE)) {
mode |= (S_IXUSR);
}
}
auto windows_time_to_unix_time(std::uint64_t t) -> remote::file_time {
return (t - 116444736000000000ull) * 100ull;
}
} // namespace repertory::utils
#endif // !_WIN32

View File

@@ -1,336 +1,336 @@
/*
Copyright <2018-2023> <scott.e.graves@protonmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "utils/utils.hpp"
#include "app_config.hpp"
#include "events/event_system.hpp"
#include "events/events.hpp"
#include "providers/i_provider.hpp"
#include "types/startup_exception.hpp"
#include "utils/com_init_wrapper.hpp"
#include "utils/native_file.hpp"
#include "utils/path_utils.hpp"
#include "utils/string_utils.hpp"
#include <limits>
namespace repertory::utils {
void calculate_allocation_size(bool directory, std::uint64_t file_size,
UINT64 allocation_size,
std::string &allocation_meta_size) {
if (directory) {
allocation_meta_size = "0";
return;
}
if (file_size > allocation_size) {
allocation_size = file_size;
}
allocation_size =
((allocation_size == 0U) ? WINFSP_ALLOCATION_UNIT : allocation_size);
allocation_size =
utils::divide_with_ceiling(allocation_size, WINFSP_ALLOCATION_UNIT) *
WINFSP_ALLOCATION_UNIT;
allocation_meta_size = std::to_string(allocation_size);
}
auto calculate_read_size(const uint64_t &total_size, std::size_t read_size,
const uint64_t &offset) -> std::size_t {
return static_cast<std::size_t>(
((offset + read_size) > total_size)
? ((offset < total_size) ? total_size - offset : 0U)
: read_size);
}
auto compare_version_strings(std::string version1, std::string version2)
-> int {
if (utils::string::contains(version1, "-")) {
version1 = utils::string::split(version1, '-')[0U];
}
if (utils::string::contains(version2, "-")) {
version2 = utils::string::split(version2, '-')[0U];
}
auto nums1 = utils::string::split(version1, '.');
auto nums2 = utils::string::split(version2, '.');
while (nums1.size() > nums2.size()) {
nums2.emplace_back("0");
}
while (nums2.size() > nums1.size()) {
nums1.emplace_back("0");
}
for (std::size_t i = 0U; i < nums1.size(); i++) {
const auto int1 = utils::string::to_uint32(nums1[i]);
const auto int2 = utils::string::to_uint32(nums2[i]);
const auto res = std::memcmp(&int1, &int2, sizeof(int1));
if (res != 0) {
return res;
}
}
return 0;
}
auto convert_api_date(const std::string &date) -> std::uint64_t {
// 2009-10-12T17:50:30.000Z
const auto date_parts = utils::string::split(date, '.');
const auto date_time = date_parts[0U];
const auto nanos =
utils::string::to_uint64(utils::string::split(date_parts[1U], 'Z')[0U]);
struct tm tm1 {};
#ifdef _WIN32
utils::strptime(date_time.c_str(), "%Y-%m-%dT%T", &tm1);
#else
strptime(date_time.c_str(), "%Y-%m-%dT%T", &tm1);
#endif
return nanos + (static_cast<std::uint64_t>(mktime(&tm1)) * NANOS_PER_SECOND);
}
auto create_curl() -> CURL * {
static std::recursive_mutex mtx;
unique_recur_mutex_lock lock(mtx);
curl_global_init(CURL_GLOBAL_DEFAULT);
lock.unlock();
return reset_curl(curl_easy_init());
}
auto create_uuid_string() -> std::string {
std::random_device random_device;
auto seed_data = std::array<int, std::mt19937::state_size>{};
std::generate(std::begin(seed_data), std::end(seed_data),
std::ref(random_device));
std::seed_seq seq(std::begin(seed_data), std::end(seed_data));
std::mt19937 generator(seq);
uuids::uuid_random_generator gen{generator};
return uuids::to_string(gen());
}
auto create_volume_label(const provider_type &prov) -> std::string {
return "repertory_" + app_config::get_provider_name(prov);
}
auto download_type_from_string(std::string type,
const download_type &default_type)
-> download_type {
type = utils::string::to_lower(utils::string::trim(type));
if (type == "direct") {
return download_type::direct;
}
if (type == "fallback") {
return download_type::fallback;
}
if (type == "ring_buffer") {
return download_type::ring_buffer;
}
return default_type;
}
auto download_type_to_string(const download_type &type) -> std::string {
switch (type) {
case download_type::direct:
return "direct";
case download_type::fallback:
return "fallback";
case download_type::ring_buffer:
return "ring_buffer";
default:
return "fallback";
}
}
#ifdef _WIN32
// https://www.frenk.com/2009/12/convert-filetime-to-unix-timestamp/
auto filetime_to_unix_time(const FILETIME &ft) -> remote::file_time {
LARGE_INTEGER date{};
date.HighPart = ft.dwHighDateTime;
date.LowPart = ft.dwLowDateTime;
date.QuadPart -= 116444736000000000ULL;
return date.QuadPart * 100ULL;
}
void unix_time_to_filetime(const remote::file_time &ts, FILETIME &ft) {
const auto win_time = (ts / 100ULL) + 116444736000000000ULL;
ft.dwHighDateTime = win_time >> 32U;
ft.dwLowDateTime = win_time & 0xFFFFFFFF;
}
#endif
auto generate_random_string(std::uint16_t length) -> std::string {
std::string ret;
ret.resize(length);
for (std::uint16_t i = 0U; i < length; i++) {
do {
ret[i] = static_cast<char>(repertory_rand<std::uint8_t>() % 74 + 48);
} while (((ret[i] >= 91) && (ret[i] <= 96)) ||
((ret[i] >= 58) && (ret[i] <= 64)));
}
return ret;
}
auto get_attributes_from_meta(const api_meta_map &meta) -> DWORD {
return static_cast<DWORD>(utils::string::to_uint32(meta.at(META_ATTRIBUTES)));
}
auto get_environment_variable(const std::string &variable) -> std::string {
static std::mutex mtx{};
mutex_lock lock{mtx};
const auto *val = std::getenv(variable.c_str());
auto ret = std::string(val == nullptr ? "" : val);
return ret;
}
auto get_file_time_now() -> std::uint64_t {
#ifdef _WIN32
SYSTEMTIME st{};
::GetSystemTime(&st);
FILETIME ft{};
::SystemTimeToFileTime(&st, &ft);
return static_cast<std::uint64_t>(((LARGE_INTEGER *)&ft)->QuadPart);
#else
return get_time_now();
#endif
}
void get_local_time_now(struct tm &local_time) {
memset(&local_time, 0, sizeof(local_time));
const auto now =
std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
#ifdef _WIN32
localtime_s(&local_time, &now);
#else
static std::mutex mtx{};
mutex_lock lock{mtx};
const auto *tmp = std::localtime(&now);
if (tmp != nullptr) {
memcpy(&local_time, tmp, sizeof(local_time));
}
#endif
}
auto get_next_available_port(std::uint16_t first_port,
std::uint16_t &available_port) -> bool {
using namespace boost::asio;
using ip::tcp;
boost::system::error_code error_code{};
while (first_port != 0U) {
io_service svc{};
tcp::acceptor acceptor(svc);
acceptor.open(tcp::v4(), error_code) ||
acceptor.bind({tcp::v4(), first_port}, error_code);
if (not error_code) {
break;
}
++first_port;
}
if (not error_code) {
available_port = first_port;
}
return not error_code;
}
auto get_time_now() -> std::uint64_t {
#ifdef _WIN32
return static_cast<std::uint64_t>(
std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()));
#else
#if __APPLE__
return std::chrono::nanoseconds(
std::chrono::system_clock::now().time_since_epoch())
.count();
#else
return static_cast<std::uint64_t>(
std::chrono::nanoseconds(
std::chrono::high_resolution_clock::now().time_since_epoch())
.count());
#endif
#endif
}
auto reset_curl(CURL *curl_handle) -> CURL * {
curl_easy_reset(curl_handle);
#if __APPLE__
curl_easy_setopt(curl_handle, CURLOPT_NOSIGNAL, 1);
#endif
return curl_handle;
}
auto retryable_action(const std::function<bool()> &action) -> bool {
static constexpr const auto retry_count = 20U;
auto succeeded = false;
for (std::uint8_t i = 0U; not(succeeded = action()) && (i < retry_count);
i++) {
std::this_thread::sleep_for(100ms);
}
return succeeded;
}
void spin_wait_for_mutex(std::function<bool()> complete,
std::condition_variable &cond, std::mutex &mtx,
const std::string &text) {
while (not complete()) {
unique_mutex_lock lock(mtx);
if (not complete()) {
if (not text.empty()) {
/* event_system::instance().raise<DebugLog>(__FUNCTION__,
* "spin_wait_for_mutex", text); */
}
cond.wait_for(lock, 1s);
}
lock.unlock();
}
}
void spin_wait_for_mutex(bool &complete, std::condition_variable &cond,
std::mutex &mtx, const std::string &text) {
while (not complete) {
unique_mutex_lock lock(mtx);
if (not complete) {
if (not text.empty()) {
/* event_system::instance().raise<DebugLog>(__FUNCTION__,
* "spin_wait_for_mutex", text); */
}
cond.wait_for(lock, 1s);
}
lock.unlock();
}
}
} // namespace repertory::utils
/*
Copyright <2018-2023> <scott.e.graves@protonmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "utils/utils.hpp"
#include "app_config.hpp"
#include "events/event_system.hpp"
#include "events/events.hpp"
#include "providers/i_provider.hpp"
#include "types/startup_exception.hpp"
#include "utils/com_init_wrapper.hpp"
#include "utils/native_file.hpp"
#include "utils/path_utils.hpp"
#include "utils/string_utils.hpp"
#include <limits>
namespace repertory::utils {
void calculate_allocation_size(bool directory, std::uint64_t file_size,
UINT64 allocation_size,
std::string &allocation_meta_size) {
if (directory) {
allocation_meta_size = "0";
return;
}
if (file_size > allocation_size) {
allocation_size = file_size;
}
allocation_size =
((allocation_size == 0U) ? WINFSP_ALLOCATION_UNIT : allocation_size);
allocation_size =
utils::divide_with_ceiling(allocation_size, WINFSP_ALLOCATION_UNIT) *
WINFSP_ALLOCATION_UNIT;
allocation_meta_size = std::to_string(allocation_size);
}
auto calculate_read_size(const uint64_t &total_size, std::size_t read_size,
const uint64_t &offset) -> std::size_t {
return static_cast<std::size_t>(
((offset + read_size) > total_size)
? ((offset < total_size) ? total_size - offset : 0U)
: read_size);
}
auto compare_version_strings(std::string version1, std::string version2)
-> int {
if (utils::string::contains(version1, "-")) {
version1 = utils::string::split(version1, '-')[0U];
}
if (utils::string::contains(version2, "-")) {
version2 = utils::string::split(version2, '-')[0U];
}
auto nums1 = utils::string::split(version1, '.');
auto nums2 = utils::string::split(version2, '.');
while (nums1.size() > nums2.size()) {
nums2.emplace_back("0");
}
while (nums2.size() > nums1.size()) {
nums1.emplace_back("0");
}
for (std::size_t i = 0U; i < nums1.size(); i++) {
const auto int1 = utils::string::to_uint32(nums1[i]);
const auto int2 = utils::string::to_uint32(nums2[i]);
const auto res = std::memcmp(&int1, &int2, sizeof(int1));
if (res != 0) {
return res;
}
}
return 0;
}
auto convert_api_date(const std::string &date) -> std::uint64_t {
// 2009-10-12T17:50:30.000Z
const auto date_parts = utils::string::split(date, '.');
const auto date_time = date_parts[0U];
const auto nanos =
utils::string::to_uint64(utils::string::split(date_parts[1U], 'Z')[0U]);
struct tm tm1 {};
#ifdef _WIN32
utils::strptime(date_time.c_str(), "%Y-%m-%dT%T", &tm1);
#else
strptime(date_time.c_str(), "%Y-%m-%dT%T", &tm1);
#endif
return nanos + (static_cast<std::uint64_t>(mktime(&tm1)) * NANOS_PER_SECOND);
}
auto create_curl() -> CURL * {
static std::recursive_mutex mtx;
unique_recur_mutex_lock lock(mtx);
curl_global_init(CURL_GLOBAL_DEFAULT);
lock.unlock();
return reset_curl(curl_easy_init());
}
auto create_uuid_string() -> std::string {
std::random_device random_device;
auto seed_data = std::array<int, std::mt19937::state_size>{};
std::generate(std::begin(seed_data), std::end(seed_data),
std::ref(random_device));
std::seed_seq seq(std::begin(seed_data), std::end(seed_data));
std::mt19937 generator(seq);
uuids::uuid_random_generator gen{generator};
return uuids::to_string(gen());
}
auto create_volume_label(const provider_type &prov) -> std::string {
return "repertory_" + app_config::get_provider_name(prov);
}
auto download_type_from_string(std::string type,
const download_type &default_type)
-> download_type {
type = utils::string::to_lower(utils::string::trim(type));
if (type == "direct") {
return download_type::direct;
}
if (type == "fallback") {
return download_type::fallback;
}
if (type == "ring_buffer") {
return download_type::ring_buffer;
}
return default_type;
}
auto download_type_to_string(const download_type &type) -> std::string {
switch (type) {
case download_type::direct:
return "direct";
case download_type::fallback:
return "fallback";
case download_type::ring_buffer:
return "ring_buffer";
default:
return "fallback";
}
}
#ifdef _WIN32
// https://www.frenk.com/2009/12/convert-filetime-to-unix-timestamp/
auto filetime_to_unix_time(const FILETIME &ft) -> remote::file_time {
LARGE_INTEGER date{};
date.HighPart = ft.dwHighDateTime;
date.LowPart = ft.dwLowDateTime;
date.QuadPart -= 116444736000000000ULL;
return date.QuadPart * 100ULL;
}
void unix_time_to_filetime(const remote::file_time &ts, FILETIME &ft) {
const auto win_time = (ts / 100ULL) + 116444736000000000ULL;
ft.dwHighDateTime = win_time >> 32U;
ft.dwLowDateTime = win_time & 0xFFFFFFFF;
}
#endif
auto generate_random_string(std::uint16_t length) -> std::string {
std::string ret;
ret.resize(length);
for (std::uint16_t i = 0U; i < length; i++) {
do {
ret[i] = static_cast<char>(repertory_rand<std::uint8_t>() % 74 + 48);
} while (((ret[i] >= 91) && (ret[i] <= 96)) ||
((ret[i] >= 58) && (ret[i] <= 64)));
}
return ret;
}
auto get_attributes_from_meta(const api_meta_map &meta) -> DWORD {
return static_cast<DWORD>(utils::string::to_uint32(meta.at(META_ATTRIBUTES)));
}
auto get_environment_variable(const std::string &variable) -> std::string {
static std::mutex mtx{};
mutex_lock lock{mtx};
const auto *val = std::getenv(variable.c_str());
auto ret = std::string(val == nullptr ? "" : val);
return ret;
}
auto get_file_time_now() -> std::uint64_t {
#ifdef _WIN32
SYSTEMTIME st{};
::GetSystemTime(&st);
FILETIME ft{};
::SystemTimeToFileTime(&st, &ft);
return static_cast<std::uint64_t>(((LARGE_INTEGER *)&ft)->QuadPart);
#else
return get_time_now();
#endif
}
void get_local_time_now(struct tm &local_time) {
memset(&local_time, 0, sizeof(local_time));
const auto now =
std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
#ifdef _WIN32
localtime_s(&local_time, &now);
#else
static std::mutex mtx{};
mutex_lock lock{mtx};
const auto *tmp = std::localtime(&now);
if (tmp != nullptr) {
memcpy(&local_time, tmp, sizeof(local_time));
}
#endif
}
auto get_next_available_port(std::uint16_t first_port,
std::uint16_t &available_port) -> bool {
using namespace boost::asio;
using ip::tcp;
boost::system::error_code error_code{};
while (first_port != 0U) {
io_service svc{};
tcp::acceptor acceptor(svc);
acceptor.open(tcp::v4(), error_code) ||
acceptor.bind({tcp::v4(), first_port}, error_code);
if (not error_code) {
break;
}
++first_port;
}
if (not error_code) {
available_port = first_port;
}
return not error_code;
}
auto get_time_now() -> std::uint64_t {
#ifdef _WIN32
return static_cast<std::uint64_t>(
std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()));
#else
#if __APPLE__
return std::chrono::nanoseconds(
std::chrono::system_clock::now().time_since_epoch())
.count();
#else
return static_cast<std::uint64_t>(
std::chrono::nanoseconds(
std::chrono::high_resolution_clock::now().time_since_epoch())
.count());
#endif
#endif
}
auto reset_curl(CURL *curl_handle) -> CURL * {
curl_easy_reset(curl_handle);
#if __APPLE__
curl_easy_setopt(curl_handle, CURLOPT_NOSIGNAL, 1);
#endif
return curl_handle;
}
auto retryable_action(const std::function<bool()> &action) -> bool {
static constexpr const auto retry_count = 20U;
auto succeeded = false;
for (std::uint8_t i = 0U; not(succeeded = action()) && (i < retry_count);
i++) {
std::this_thread::sleep_for(100ms);
}
return succeeded;
}
void spin_wait_for_mutex(std::function<bool()> complete,
std::condition_variable &cond, std::mutex &mtx,
const std::string &text) {
while (not complete()) {
unique_mutex_lock lock(mtx);
if (not complete()) {
if (not text.empty()) {
/* event_system::instance().raise<DebugLog>(__FUNCTION__,
* "spin_wait_for_mutex", text); */
}
cond.wait_for(lock, 1s);
}
lock.unlock();
}
}
void spin_wait_for_mutex(bool &complete, std::condition_variable &cond,
std::mutex &mtx, const std::string &text) {
while (not complete) {
unique_mutex_lock lock(mtx);
if (not complete) {
if (not text.empty()) {
/* event_system::instance().raise<DebugLog>(__FUNCTION__,
* "spin_wait_for_mutex", text); */
}
cond.wait_for(lock, 1s);
}
lock.unlock();
}
}
} // namespace repertory::utils

View File

@@ -1,241 +1,241 @@
/*
Copyright <2018-2023> <scott.e.graves@protonmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#ifdef _WIN32
#include "utils/windows/windows_utils.hpp"
#include "types/startup_exception.hpp"
#include "utils/com_init_wrapper.hpp"
#include "utils/string_utils.hpp"
#ifndef STATUS_DEVICE_INSUFFICIENT_RESOURCES
#define STATUS_DEVICE_INSUFFICIENT_RESOURCES ((NTSTATUS)0xC0000468L)
#endif
namespace repertory::utils {
auto from_api_error(const api_error &e) -> NTSTATUS {
switch (e) {
case api_error::access_denied:
return STATUS_ACCESS_DENIED;
case api_error::bad_address:
return STATUS_INVALID_ADDRESS;
case api_error::buffer_too_small:
return STATUS_BUFFER_TOO_SMALL;
case api_error::buffer_overflow:
return STATUS_BUFFER_OVERFLOW;
case api_error::directory_end_of_files:
return STATUS_NO_MORE_FILES;
case api_error::directory_exists:
return STATUS_OBJECT_NAME_EXISTS;
case api_error::directory_not_empty:
return STATUS_DIRECTORY_NOT_EMPTY;
case api_error::directory_not_found:
return STATUS_OBJECT_NAME_NOT_FOUND;
case api_error::download_failed:
return FspNtStatusFromWin32(ERROR_INTERNAL_ERROR);
case api_error::error:
return FspNtStatusFromWin32(ERROR_INTERNAL_ERROR);
case api_error::invalid_handle:
return STATUS_INVALID_HANDLE;
case api_error::invalid_operation:
return STATUS_INVALID_PARAMETER;
case api_error::item_exists:
return FspNtStatusFromWin32(ERROR_FILE_EXISTS);
case api_error::file_in_use:
return STATUS_DEVICE_BUSY;
case api_error::incompatible_version:
return STATUS_CLIENT_SERVER_PARAMETERS_INVALID;
case api_error::item_not_found:
return STATUS_OBJECT_NAME_NOT_FOUND;
case api_error::no_disk_space:
return STATUS_DEVICE_INSUFFICIENT_RESOURCES;
case api_error::os_error:
return FspNtStatusFromWin32(::GetLastError());
case api_error::out_of_memory:
return STATUS_NO_MEMORY;
case api_error::permission_denied:
return FspNtStatusFromWin32(ERROR_ACCESS_DENIED);
case api_error::success:
return 0;
case api_error::not_supported:
return FspNtStatusFromWin32(ERROR_INTERNAL_ERROR);
case api_error::not_implemented:
return STATUS_NOT_IMPLEMENTED;
case api_error::upload_failed:
return FspNtStatusFromWin32(ERROR_INTERNAL_ERROR);
default:
return FspNtStatusFromWin32(ERROR_INTERNAL_ERROR);
}
}
auto get_last_error_code() -> DWORD { return ::GetLastError(); }
auto get_local_app_data_directory() -> const std::string & {
static std::string app_data = ([]() -> std::string {
com_init_wrapper cw;
PWSTR local_app_data{};
if (SUCCEEDED(::SHGetKnownFolderPath(FOLDERID_LocalAppData, 0, nullptr,
&local_app_data))) {
auto app_data = utils::string::to_utf8(local_app_data);
::CoTaskMemFree(local_app_data);
return app_data;
}
throw startup_exception("unable to detect local application data folder");
})();
return app_data;
}
auto get_accessed_time_from_meta(const api_meta_map &meta) -> std::uint64_t {
return utils::string::to_uint64(meta.at(META_ACCESSED));
}
auto get_changed_time_from_meta(const api_meta_map &meta) -> std::uint64_t {
return utils::string::to_uint64(meta.at(META_MODIFIED));
}
auto get_creation_time_from_meta(const api_meta_map &meta) -> std::uint64_t {
return utils::string::to_uint64(meta.at(META_CREATION));
}
auto get_written_time_from_meta(const api_meta_map &meta) -> std::uint64_t {
return utils::string::to_uint64(meta.at(META_WRITTEN));
}
auto get_thread_id() -> std::uint64_t {
return static_cast<std::uint64_t>(::GetCurrentThreadId());
}
auto is_process_elevated() -> bool {
auto ret = false;
HANDLE token = INVALID_HANDLE_VALUE;
if (::OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token)) {
TOKEN_ELEVATION te{};
DWORD sz = sizeof(te);
if (::GetTokenInformation(token, TokenElevation, &te, sizeof(te), &sz)) {
ret = (te.TokenIsElevated != 0);
}
}
if (token != INVALID_HANDLE_VALUE) {
::CloseHandle(token);
}
return ret;
}
auto run_process_elevated(std::vector<const char *> args) -> int {
std::cout << "Elevating Process" << std::endl;
std::string parameters = "-hidden";
for (std::size_t i = 1U; i < args.size(); i++) {
parameters +=
(parameters.empty() ? args.at(i) : " " + std::string(args.at(i)));
}
char full_path[MAX_PATH] = {0};
if (::GetModuleFileName(nullptr, full_path, MAX_PATH)) {
SHELLEXECUTEINFO sei{};
sei.fMask = SEE_MASK_NOCLOSEPROCESS;
sei.cbSize = sizeof(sei);
sei.lpVerb = "runas";
sei.lpFile = &full_path[0];
sei.lpParameters = &parameters[0];
sei.hwnd = nullptr;
sei.nShow = SW_NORMAL;
if (::ShellExecuteEx(&sei)) {
::WaitForSingleObject(sei.hProcess, INFINITE);
DWORD exit_code = 0u;
::GetExitCodeProcess(sei.hProcess, &exit_code);
::CloseHandle(sei.hProcess);
return exit_code;
}
}
return ::GetLastError();
}
void set_last_error_code(DWORD error_code) { ::SetLastError(error_code); }
// https://stackoverflow.com/questions/321849/strptime-equivalent-on-windows
auto strptime(const char *s, const char *f, struct tm *tm) -> const char * {
std::istringstream input(s);
input.imbue(std::locale(setlocale(LC_ALL, nullptr)));
input >> std::get_time(tm, f);
if (input.fail()) {
return nullptr;
}
return reinterpret_cast<const char *>(s + input.tellg());
}
auto unix_access_mask_to_windows(std::int32_t mask) -> int {
if (mask & 1) {
mask -= 1;
if (not mask) {
mask = 4;
}
}
return mask & 6;
}
auto unix_open_flags_to_flags_and_perms(const remote::file_mode & /*mode*/,
const remote::open_flags &flags,
std::int32_t &perms) -> int {
auto ret = _O_BINARY | _O_RANDOM;
ret |= (((flags & remote::open_flags::create) == remote::open_flags::create)
? _O_CREAT
: 0);
ret |= (((flags & remote::open_flags::excl) == remote::open_flags::excl)
? _O_EXCL
: 0);
ret |=
(((flags & remote::open_flags::truncate) == remote::open_flags::truncate)
? _O_TRUNC
: 0);
ret |= (((flags & remote::open_flags::append) == remote::open_flags::append)
? _O_APPEND
: 0);
ret |= (((flags & remote::open_flags::temp_file) ==
remote::open_flags::temp_file)
? _O_TEMPORARY
: 0);
ret |= ((flags & remote::open_flags::write_only) ==
remote::open_flags::write_only)
? _O_WRONLY
: ((flags & remote::open_flags::read_write) ==
remote::open_flags::read_write)
? _O_RDWR
: _O_RDONLY;
perms = _S_IREAD | _S_IWRITE;
return ret;
}
auto time64_to_unix_time(const __time64_t &t) -> remote::file_time {
return t * NANOS_PER_SECOND;
}
} // namespace repertory::utils
#endif // _WIN32
/*
Copyright <2018-2023> <scott.e.graves@protonmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#ifdef _WIN32
#include "utils/windows/windows_utils.hpp"
#include "types/startup_exception.hpp"
#include "utils/com_init_wrapper.hpp"
#include "utils/string_utils.hpp"
#ifndef STATUS_DEVICE_INSUFFICIENT_RESOURCES
#define STATUS_DEVICE_INSUFFICIENT_RESOURCES ((NTSTATUS)0xC0000468L)
#endif
namespace repertory::utils {
auto from_api_error(const api_error &e) -> NTSTATUS {
switch (e) {
case api_error::access_denied:
return STATUS_ACCESS_DENIED;
case api_error::bad_address:
return STATUS_INVALID_ADDRESS;
case api_error::buffer_too_small:
return STATUS_BUFFER_TOO_SMALL;
case api_error::buffer_overflow:
return STATUS_BUFFER_OVERFLOW;
case api_error::directory_end_of_files:
return STATUS_NO_MORE_FILES;
case api_error::directory_exists:
return STATUS_OBJECT_NAME_EXISTS;
case api_error::directory_not_empty:
return STATUS_DIRECTORY_NOT_EMPTY;
case api_error::directory_not_found:
return STATUS_OBJECT_NAME_NOT_FOUND;
case api_error::download_failed:
return FspNtStatusFromWin32(ERROR_INTERNAL_ERROR);
case api_error::error:
return FspNtStatusFromWin32(ERROR_INTERNAL_ERROR);
case api_error::invalid_handle:
return STATUS_INVALID_HANDLE;
case api_error::invalid_operation:
return STATUS_INVALID_PARAMETER;
case api_error::item_exists:
return FspNtStatusFromWin32(ERROR_FILE_EXISTS);
case api_error::file_in_use:
return STATUS_DEVICE_BUSY;
case api_error::incompatible_version:
return STATUS_CLIENT_SERVER_PARAMETERS_INVALID;
case api_error::item_not_found:
return STATUS_OBJECT_NAME_NOT_FOUND;
case api_error::no_disk_space:
return STATUS_DEVICE_INSUFFICIENT_RESOURCES;
case api_error::os_error:
return FspNtStatusFromWin32(::GetLastError());
case api_error::out_of_memory:
return STATUS_NO_MEMORY;
case api_error::permission_denied:
return FspNtStatusFromWin32(ERROR_ACCESS_DENIED);
case api_error::success:
return 0;
case api_error::not_supported:
return FspNtStatusFromWin32(ERROR_INTERNAL_ERROR);
case api_error::not_implemented:
return STATUS_NOT_IMPLEMENTED;
case api_error::upload_failed:
return FspNtStatusFromWin32(ERROR_INTERNAL_ERROR);
default:
return FspNtStatusFromWin32(ERROR_INTERNAL_ERROR);
}
}
auto get_last_error_code() -> DWORD { return ::GetLastError(); }
auto get_local_app_data_directory() -> const std::string & {
static std::string app_data = ([]() -> std::string {
com_init_wrapper cw;
PWSTR local_app_data{};
if (SUCCEEDED(::SHGetKnownFolderPath(FOLDERID_LocalAppData, 0, nullptr,
&local_app_data))) {
auto app_data = utils::string::to_utf8(local_app_data);
::CoTaskMemFree(local_app_data);
return app_data;
}
throw startup_exception("unable to detect local application data folder");
})();
return app_data;
}
auto get_accessed_time_from_meta(const api_meta_map &meta) -> std::uint64_t {
return utils::string::to_uint64(meta.at(META_ACCESSED));
}
auto get_changed_time_from_meta(const api_meta_map &meta) -> std::uint64_t {
return utils::string::to_uint64(meta.at(META_MODIFIED));
}
auto get_creation_time_from_meta(const api_meta_map &meta) -> std::uint64_t {
return utils::string::to_uint64(meta.at(META_CREATION));
}
auto get_written_time_from_meta(const api_meta_map &meta) -> std::uint64_t {
return utils::string::to_uint64(meta.at(META_WRITTEN));
}
auto get_thread_id() -> std::uint64_t {
return static_cast<std::uint64_t>(::GetCurrentThreadId());
}
auto is_process_elevated() -> bool {
auto ret = false;
HANDLE token = INVALID_HANDLE_VALUE;
if (::OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token)) {
TOKEN_ELEVATION te{};
DWORD sz = sizeof(te);
if (::GetTokenInformation(token, TokenElevation, &te, sizeof(te), &sz)) {
ret = (te.TokenIsElevated != 0);
}
}
if (token != INVALID_HANDLE_VALUE) {
::CloseHandle(token);
}
return ret;
}
auto run_process_elevated(std::vector<const char *> args) -> int {
std::cout << "Elevating Process" << std::endl;
std::string parameters = "-hidden";
for (std::size_t i = 1U; i < args.size(); i++) {
parameters +=
(parameters.empty() ? args.at(i) : " " + std::string(args.at(i)));
}
char full_path[MAX_PATH] = {0};
if (::GetModuleFileName(nullptr, full_path, MAX_PATH)) {
SHELLEXECUTEINFO sei{};
sei.fMask = SEE_MASK_NOCLOSEPROCESS;
sei.cbSize = sizeof(sei);
sei.lpVerb = "runas";
sei.lpFile = &full_path[0];
sei.lpParameters = &parameters[0];
sei.hwnd = nullptr;
sei.nShow = SW_NORMAL;
if (::ShellExecuteEx(&sei)) {
::WaitForSingleObject(sei.hProcess, INFINITE);
DWORD exit_code = 0u;
::GetExitCodeProcess(sei.hProcess, &exit_code);
::CloseHandle(sei.hProcess);
return exit_code;
}
}
return ::GetLastError();
}
void set_last_error_code(DWORD error_code) { ::SetLastError(error_code); }
// https://stackoverflow.com/questions/321849/strptime-equivalent-on-windows
auto strptime(const char *s, const char *f, struct tm *tm) -> const char * {
std::istringstream input(s);
input.imbue(std::locale(setlocale(LC_ALL, nullptr)));
input >> std::get_time(tm, f);
if (input.fail()) {
return nullptr;
}
return reinterpret_cast<const char *>(s + input.tellg());
}
auto unix_access_mask_to_windows(std::int32_t mask) -> int {
if (mask & 1) {
mask -= 1;
if (not mask) {
mask = 4;
}
}
return mask & 6;
}
auto unix_open_flags_to_flags_and_perms(const remote::file_mode & /*mode*/,
const remote::open_flags &flags,
std::int32_t &perms) -> int {
auto ret = _O_BINARY | _O_RANDOM;
ret |= (((flags & remote::open_flags::create) == remote::open_flags::create)
? _O_CREAT
: 0);
ret |= (((flags & remote::open_flags::excl) == remote::open_flags::excl)
? _O_EXCL
: 0);
ret |=
(((flags & remote::open_flags::truncate) == remote::open_flags::truncate)
? _O_TRUNC
: 0);
ret |= (((flags & remote::open_flags::append) == remote::open_flags::append)
? _O_APPEND
: 0);
ret |= (((flags & remote::open_flags::temp_file) ==
remote::open_flags::temp_file)
? _O_TEMPORARY
: 0);
ret |= ((flags & remote::open_flags::write_only) ==
remote::open_flags::write_only)
? _O_WRONLY
: ((flags & remote::open_flags::read_write) ==
remote::open_flags::read_write)
? _O_RDWR
: _O_RDONLY;
perms = _S_IREAD | _S_IWRITE;
return ret;
}
auto time64_to_unix_time(const __time64_t &t) -> remote::file_time {
return t * NANOS_PER_SECOND;
}
} // namespace repertory::utils
#endif // _WIN32