v2.0.5-rc (#41)
All checks were successful
BlockStorage/repertory/pipeline/head This commit looks good

Reviewed-on: #41
This commit is contained in:
2025-03-26 07:02:38 -05:00
parent 24418ba03d
commit 62555e6125
135 changed files with 6792 additions and 796 deletions

View File

@@ -43,8 +43,7 @@ public:
[[nodiscard]] static auto default_remote_api_port(const provider_type &prov)
-> std::uint16_t;
[[nodiscard]] static auto default_rpc_port(const provider_type &prov)
-> std::uint16_t;
[[nodiscard]] static auto default_rpc_port() -> std::uint16_t;
[[nodiscard]] static auto get_provider_display_name(const provider_type &prov)
-> std::string;
@@ -52,6 +51,8 @@ public:
[[nodiscard]] static auto get_provider_name(const provider_type &prov)
-> std::string;
[[nodiscard]] static auto get_root_data_directory() -> std::string;
public:
[[nodiscard]] static auto get_stop_requested() -> bool;
@@ -71,7 +72,7 @@ public:
private:
provider_type prov_;
atomic<std::string> api_auth_;
atomic<std::string> api_password_;
std::atomic<std::uint16_t> api_port_;
atomic<std::string> api_user_;
std::atomic<bool> config_changed_;
@@ -121,7 +122,7 @@ private:
auto set_value(dest &dst, const source &src) -> bool;
public:
[[nodiscard]] auto get_api_auth() const -> std::string;
[[nodiscard]] auto get_api_password() const -> std::string;
[[nodiscard]] auto get_api_port() const -> std::uint16_t;
@@ -199,7 +200,7 @@ public:
void save();
void set_api_auth(const std::string &value);
void set_api_password(const std::string &value);
void set_api_port(std::uint16_t value);

View File

@@ -57,7 +57,7 @@ using json = nlohmann::json;
inline constexpr const std::string_view REPERTORY = "repertory";
inline constexpr const std::wstring_view REPERTORY_W = L"repertory";
inline constexpr const std::uint64_t REPERTORY_CONFIG_VERSION = 1ULL;
inline constexpr const std::uint64_t REPERTORY_CONFIG_VERSION = 2ULL;
inline constexpr const std::string_view REPERTORY_DATA_NAME = "repertory2";
inline constexpr const std::string_view REPERTORY_MIN_REMOTE_VERSION = "2.0.0";

View File

@@ -22,6 +22,13 @@
#ifndef REPERTORY_INCLUDE_PLATFORM_PLATFORM_HPP_
#define REPERTORY_INCLUDE_PLATFORM_PLATFORM_HPP_
#include "types/repertory.hpp"
namespace repertory {
[[nodiscard]] auto create_lock_id(provider_type prov,
std::string_view unique_id)->std::string;
}
#if defined(_WIN32)
#include "platform/win32_platform.hpp"
#include "utils/windows.hpp"

View File

@@ -30,38 +30,45 @@ class i_provider;
class lock_data final {
public:
explicit lock_data(const provider_type &pt, std::string unique_id /*= ""*/);
lock_data(provider_type prov, std::string_view unique_id);
lock_data();
lock_data(const lock_data &) = delete;
lock_data(lock_data &&) = delete;
auto operator=(const lock_data &) -> lock_data & = delete;
auto operator=(lock_data &&) -> lock_data & = delete;
~lock_data();
private:
const provider_type pt_;
const std::string unique_id_;
const std::string mutex_id_;
int lock_fd_;
int lock_status_ = EWOULDBLOCK;
std::string mutex_id_;
private:
int handle_{};
int lock_status_{EWOULDBLOCK};
private:
[[nodiscard]] static auto get_state_directory() -> std::string;
[[nodiscard]] static auto get_lock_data_file() -> std::string;
[[nodiscard]] auto get_lock_data_file() const -> std::string;
[[nodiscard]] auto get_lock_file() -> std::string;
[[nodiscard]] auto get_lock_file() const -> std::string;
private:
[[nodiscard]] static auto
wait_for_lock(int fd, std::uint8_t retry_count = 30u) -> int;
[[nodiscard]] static auto wait_for_lock(int handle,
std::uint8_t retry_count = 30U)
-> int;
public:
[[nodiscard]] auto get_mount_state(json &mount_state) -> bool;
[[nodiscard]] auto grab_lock(std::uint8_t retry_count = 30u) -> lock_result;
[[nodiscard]] auto grab_lock(std::uint8_t retry_count = 30U) -> lock_result;
void release();
[[nodiscard]] auto set_mount_state(bool active,
const std::string &mount_location,
int pid) -> bool;
std::string_view mount_location, int pid)
-> bool;
};
[[nodiscard]] auto create_meta_attributes(
@@ -76,5 +83,5 @@ public:
const api_file &file) -> api_error;
} // namespace repertory
#endif // _WIN32
#endif // !defined(_WIN32)
#endif // REPERTORY_INCLUDE_PLATFORM_UNIXPLATFORM_HPP_

View File

@@ -23,7 +23,6 @@
#define REPERTORY_INCLUDE_PLATFORM_WINPLATFORM_HPP_
#if defined(_WIN32)
#include "app_config.hpp"
#include "types/repertory.hpp"
namespace repertory {
@@ -31,43 +30,32 @@ class i_provider;
class lock_data final {
public:
explicit lock_data(const provider_type &pt, std::string unique_id /*= ""*/)
: pt_(pt),
unique_id_(std::move(unique_id)),
mutex_id_("repertory_" + app_config::get_provider_name(pt) + "_" +
unique_id_),
mutex_handle_(::CreateMutex(nullptr, FALSE, &mutex_id_[0u])) {}
explicit lock_data(provider_type prov, std::string unique_id);
lock_data(const lock_data &) = delete;
lock_data(lock_data &&) = delete;
lock_data()
: pt_(provider_type::sia),
unique_id_(""),
mutex_id_(""),
mutex_handle_(INVALID_HANDLE_VALUE) {}
~lock_data();
~lock_data() { release(); }
auto operator=(const lock_data &) -> lock_data & = delete;
auto operator=(lock_data &&) -> lock_data & = delete;
private:
const provider_type pt_;
const std::string unique_id_;
const std::string mutex_id_;
HANDLE mutex_handle_;
DWORD mutex_state_ = WAIT_FAILED;
std::string mutex_id_;
HANDLE mutex_handle_{INVALID_HANDLE_VALUE};
DWORD mutex_state_{WAIT_FAILED};
[[nodiscard]] auto get_current_mount_state(json &mount_state) -> bool;
public:
[[nodiscard]] auto get_mount_state(const provider_type &pt,
json &mount_state) -> bool;
[[nodiscard]] auto get_mount_state(json &mount_state) -> bool;
[[nodiscard]] auto get_unique_id() const -> std::string { return unique_id_; }
[[nodiscard]] auto grab_lock(std::uint8_t retry_count = 30) -> lock_result;
[[nodiscard]] auto grab_lock(std::uint8_t retry_count = 30U) -> lock_result;
void release();
[[nodiscard]] auto set_mount_state(bool active,
const std::string &mount_location,
const std::int64_t &pid) -> bool;
std::string_view mount_location,
std::int64_t pid) -> bool;
};
[[nodiscard]] auto create_meta_attributes(

View File

@@ -48,9 +48,9 @@ public:
[[nodiscard]] auto get_pinned_files() -> rpc_response;
[[nodiscard]] auto pin_file(const std::string &api_file) -> rpc_response;
[[nodiscard]] auto pin_file(const std::string &api_path) -> rpc_response;
[[nodiscard]] auto pinned_status(const std::string &api_file) -> rpc_response;
[[nodiscard]] auto pinned_status(const std::string &api_path) -> rpc_response;
[[nodiscard]] auto set_config_value_by_name(const std::string &name,
const std::string &value)
@@ -58,7 +58,7 @@ public:
[[nodiscard]] auto unmount() -> rpc_response;
[[nodiscard]] auto unpin_file(const std::string &api_file) -> rpc_response;
[[nodiscard]] auto unpin_file(const std::string &api_path) -> rpc_response;
};
} // namespace repertory

View File

@@ -0,0 +1,82 @@
/*
Copyright <2018-2025> <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 REPERTORY_INCLUDE_RPC_COMMON_HPP_
#define REPERTORY_INCLUDE_RPC_COMMON_HPP_
#include "utils/base64.hpp"
#include "utils/error_utils.hpp"
#include "utils/string.hpp"
namespace repertory::rpc {
[[nodiscard]] auto check_authorization(const auto &cfg,
const httplib::Request &req) -> bool {
REPERTORY_USES_FUNCTION_NAME();
if (cfg.get_api_password().empty() || cfg.get_api_user().empty()) {
utils::error::raise_error(function_name,
"authorization user or password is not set");
return false;
}
auto authorization = req.get_header_value("Authorization");
if (authorization.empty()) {
utils::error::raise_error(function_name,
"'Authorization' header is not set");
return false;
}
auto auth_parts = utils::string::split(authorization, ' ', true);
if (auth_parts.empty()) {
utils::error::raise_error(function_name, "'Authorization' header is empty");
return false;
}
auto auth_type = auth_parts[0U];
if (auth_type != "Basic") {
utils::error::raise_error(function_name,
"authorization type is not 'Basic'");
return false;
}
auto data = macaron::Base64::Decode(authorization.substr(6U));
auto auth_str = std::string(data.begin(), data.end());
auto auth = utils::string::split(auth_str, ':', false);
if (auth.size() < 2U) {
utils::error::raise_error(function_name, "authorization data is not valid");
return false;
}
auto user = auth.at(0U);
auth.erase(auth.begin());
auto pwd = utils::string::join(auth, ':');
if ((user != cfg.get_api_user()) || (pwd != cfg.get_api_password())) {
utils::error::raise_error(function_name, "authorization failed");
return false;
}
return true;
}
} // namespace repertory::rpc
#endif // REPERTORY_INCLUDE_RPC_COMMON_HPP_

View File

@@ -40,8 +40,6 @@ private:
std::mutex start_stop_mutex_;
private:
[[nodiscard]] auto check_authorization(const httplib::Request &req) -> bool;
void handle_get_config(const httplib::Request &req, httplib::Response &res);
void handle_get_config_value_by_name(const httplib::Request &req,

View File

@@ -23,7 +23,7 @@
#define REPERTORY_INCLUDE_TYPES_REPERTORY_HPP_
namespace repertory {
constexpr const auto default_api_auth_size{48U};
constexpr const auto default_api_password_size{48U};
constexpr const auto default_download_timeout_secs{30U};
constexpr const auto default_eviction_delay_mins{1U};
constexpr const auto default_high_freq_interval_secs{std::uint16_t{30U}};
@@ -38,6 +38,7 @@ constexpr const auto default_retry_read_count{6U};
constexpr const auto default_ring_buffer_file_size{512U};
constexpr const auto default_task_wait_ms{100U};
constexpr const auto default_timeout_ms{60000U};
constexpr const auto default_ui_mgmt_port{std::uint16_t{30000U}};
constexpr const auto max_ring_buffer_file_size{std::uint16_t(1024U)};
constexpr const auto max_s3_object_name_length{1024U};
constexpr const auto min_cache_size_bytes{
@@ -280,6 +281,8 @@ enum class exit_code : std::int32_t {
pin_failed = -16,
unpin_failed = -17,
init_failed = -18,
ui_mount_failed = -19,
exception = -20,
};
enum http_error_codes : std::int32_t {
@@ -304,6 +307,18 @@ enum class provider_type : std::size_t {
unknown,
};
[[nodiscard]] auto
provider_type_from_string(std::string_view type,
provider_type default_type = provider_type::unknown)
-> provider_type;
[[nodiscard]] auto provider_type_to_string(provider_type type) -> std::string;
void clean_json_config(provider_type prov, nlohmann::json &data);
[[nodiscard]] auto clean_json_value(std::string_view name,
std::string_view data) -> std::string;
#if defined(_WIN32)
struct open_file_data final {
PVOID directory_buffer{nullptr};
@@ -452,7 +467,6 @@ using meta_provider_callback = std::function<void(directory_item &)>;
inline constexpr const auto JSON_ACCESS_KEY{"AccessKey"};
inline constexpr const auto JSON_AGENT_STRING{"AgentString"};
inline constexpr const auto JSON_API_AUTH{"ApiAuth"};
inline constexpr const auto JSON_API_PARENT{"ApiParent"};
inline constexpr const auto JSON_API_PASSWORD{"ApiPassword"};
inline constexpr const auto JSON_API_PATH{"ApiPath"};
@@ -487,6 +501,7 @@ inline constexpr const auto JSON_MAX_UPLOAD_COUNT{"MaxUploadCount"};
inline constexpr const auto JSON_MED_FREQ_INTERVAL_SECS{
"MedFreqIntervalSeconds"};
inline constexpr const auto JSON_META{"Meta"};
inline constexpr const auto JSON_MOUNT_LOCATIONS{"MountLocations"};
inline constexpr const auto JSON_ONLINE_CHECK_RETRY_SECS{
"OnlineCheckRetrySeconds"};
inline constexpr const auto JSON_PATH{"Path"};

View File

@@ -49,6 +49,8 @@ static const option password_option = {"-pw", "--password"};
static const option remote_mount_option = {"-rm", "--remote_mount"};
static const option set_option = {"-set", "--set"};
static const option status_option = {"-status", "--status"};
static const option ui_option = {"-ui", "--ui"};
static const option ui_port_option = {"-up", "--ui_port"};
static const option unmount_option = {"-unmount", "--unmount"};
static const option unpin_file_option = {"-uf", "--unpin_file"};
static const option user_option = {"-us", "--user"};
@@ -75,6 +77,8 @@ static const std::vector<option> option_list = {
remote_mount_option,
set_option,
status_option,
ui_option,
ui_port_option,
unmount_option,
unpin_file_option,
user_option,
@@ -87,26 +91,27 @@ void get_api_authentication_data(std::string &user, std::string &password,
std::uint16_t &port, const provider_type &prov,
const std::string &data_directory);
[[nodiscard]] auto
get_provider_type_from_args(std::vector<const char *> args) -> provider_type;
[[nodiscard]] auto get_provider_type_from_args(std::vector<const char *> args)
-> provider_type;
[[nodiscard]] auto has_option(std::vector<const char *> args,
const std::string &option_name) -> bool;
[[nodiscard]] auto has_option(std::vector<const char *> args,
const option &opt) -> bool;
[[nodiscard]] auto has_option(std::vector<const char *> args, const option &opt)
-> bool;
[[nodiscard]] auto parse_option(std::vector<const char *> args,
const std::string &option_name,
std::uint8_t count) -> std::vector<std::string>;
[[nodiscard]] auto parse_string_option(std::vector<const char *> args,
const option &opt,
std::string &value) -> exit_code;
const option &opt, std::string &value)
-> exit_code;
[[nodiscard]] auto
parse_drive_options(std::vector<const char *> args, provider_type &prov,
std::string &data_directory) -> std::vector<std::string>;
[[nodiscard]] auto parse_drive_options(std::vector<const char *> args,
provider_type &prov,
std::string &data_directory)
-> std::vector<std::string>;
} // namespace repertory::utils::cli
#endif // REPERTORY_INCLUDE_UTILS_CLI_UTILS_HPP_

View File

@@ -66,8 +66,8 @@ void app_config::set_stop_requested() { stop_requested.store(true); }
app_config::app_config(const provider_type &prov,
std::string_view data_directory)
: prov_(prov),
api_auth_(utils::generate_random_string(default_api_auth_size)),
api_port_(default_rpc_port(prov)),
api_password_(utils::generate_random_string(default_api_password_size)),
api_port_(default_rpc_port()),
api_user_(std::string{REPERTORY}),
config_changed_(false),
download_timeout_secs_(default_download_timeout_secs),
@@ -124,7 +124,7 @@ app_config::app_config(const provider_type &prov,
}
value_get_lookup_ = {
{JSON_API_AUTH, [this]() { return get_api_auth(); }},
{JSON_API_PASSWORD, [this]() { return get_api_password(); }},
{JSON_API_PORT, [this]() { return std::to_string(get_api_port()); }},
{JSON_API_USER, [this]() { return get_api_user(); }},
{JSON_DATABASE_TYPE,
@@ -253,10 +253,10 @@ app_config::app_config(const provider_type &prov,
value_set_lookup_ = {
{
JSON_API_AUTH,
JSON_API_PASSWORD,
[this](const std::string &value) {
set_api_auth(value);
return get_api_auth();
set_api_password(value);
return get_api_password();
},
},
{
@@ -699,36 +699,37 @@ auto app_config::default_api_port(const provider_type &prov) -> std::uint16_t {
return PROVIDER_API_PORTS.at(static_cast<std::size_t>(prov));
}
auto app_config::default_data_directory(const provider_type &prov)
-> std::string {
auto app_config::get_root_data_directory() -> std::string {
#if defined(_WIN32)
auto data_directory =
utils::path::combine(utils::get_local_app_data_directory(),
{
REPERTORY_DATA_NAME,
app_config::get_provider_name(prov),
});
auto data_directory = utils::path::combine(
utils::get_local_app_data_directory(), {
REPERTORY_DATA_NAME,
});
#else // !defined(_WIN32)
#if defined(__APPLE__)
auto data_directory =
utils::path::combine("~", {
"Library",
"Application Support",
REPERTORY_DATA_NAME,
app_config::get_provider_name(prov),
});
auto data_directory = utils::path::combine("~", {
"Library",
"Application Support",
REPERTORY_DATA_NAME,
});
#else // !defined(__APPLE__)
auto data_directory =
utils::path::combine("~", {
".local",
REPERTORY_DATA_NAME,
app_config::get_provider_name(prov),
});
auto data_directory = utils::path::combine("~", {
".local",
REPERTORY_DATA_NAME,
});
#endif // defined(__APPLE__)
#endif // defined(_WIN32)
return data_directory;
}
auto app_config::default_data_directory(const provider_type &prov)
-> std::string {
return utils::path::combine(app_config::get_root_data_directory(),
{
app_config::get_provider_name(prov),
});
}
auto app_config::default_remote_api_port(const provider_type &prov)
-> std::uint16_t {
static const std::array<std::uint16_t,
@@ -741,19 +742,12 @@ auto app_config::default_remote_api_port(const provider_type &prov)
};
return PROVIDER_REMOTE_PORTS.at(static_cast<std::size_t>(prov));
}
auto app_config::default_rpc_port(const provider_type &prov) -> std::uint16_t {
static const std::array<std::uint16_t,
static_cast<std::size_t>(provider_type::unknown)>
PROVIDER_RPC_PORTS = {
10000U,
10010U,
10100U,
10002U,
};
return PROVIDER_RPC_PORTS.at(static_cast<std::size_t>(prov));
}
auto app_config::get_api_auth() const -> std::string { return api_auth_; }
auto app_config::default_rpc_port() -> std::uint16_t { return 10000U; }
auto app_config::get_api_password() const -> std::string {
return api_password_;
}
auto app_config::get_api_port() const -> std::uint16_t { return api_port_; }
@@ -814,7 +808,7 @@ auto app_config::get_host_config() const -> host_config { return host_config_; }
auto app_config::get_json() const -> json {
json ret = {
{JSON_API_AUTH, api_auth_},
{JSON_API_PASSWORD, api_password_},
{JSON_API_PORT, api_port_},
{JSON_API_USER, api_user_},
{JSON_DOWNLOAD_TIMEOUT_SECS, download_timeout_secs_},
@@ -939,24 +933,18 @@ auto app_config::get_preferred_download_type() const -> download_type {
auto app_config::get_provider_display_name(const provider_type &prov)
-> std::string {
static const std::array<std::string,
static_cast<std::size_t>(provider_type::unknown)>
static_cast<std::size_t>(provider_type::unknown) + 1U>
PROVIDER_DISPLAY_NAMES = {
"Sia",
"Remote",
"S3",
"Encrypt",
"Sia", "Remote", "S3", "Encrypt", "Unknown",
};
return PROVIDER_DISPLAY_NAMES.at(static_cast<std::size_t>(prov));
}
auto app_config::get_provider_name(const provider_type &prov) -> std::string {
static const std::array<std::string,
static_cast<std::size_t>(provider_type::unknown)>
static_cast<std::size_t>(provider_type::unknown) + 1U>
PROVIDER_NAMES = {
"sia",
"remote",
"s3",
"encrypt",
"sia", "remote", "s3", "encrypt", "unknown",
};
return PROVIDER_NAMES.at(static_cast<std::size_t>(prov));
}
@@ -1035,7 +1023,7 @@ auto app_config::load() -> bool {
auto found{true};
auto json_document = json::parse(json_text);
get_value(json_document, JSON_API_AUTH, api_auth_, found);
get_value(json_document, JSON_API_PASSWORD, api_password_, found);
get_value(json_document, JSON_API_PORT, api_port_, found);
get_value(json_document, JSON_API_USER, api_user_, found);
get_value(json_document, JSON_DATABASE_TYPE, db_type_, found);
@@ -1092,6 +1080,13 @@ auto app_config::load() -> bool {
set_value(max_cache_size_bytes_, default_max_cache_size_bytes);
}
}
if (version_ == 2U) {
if (json_document.contains("ApiAuth")) {
api_password_ = json_document.at("ApiAuth").get<std::string>();
}
}
found = false;
}
@@ -1130,8 +1125,8 @@ void app_config::save() {
});
}
void app_config::set_api_auth(const std::string &value) {
set_value(api_auth_, value);
void app_config::set_api_password(const std::string &value) {
set_value(api_password_, value);
}
void app_config::set_api_port(std::uint16_t value) {

View File

@@ -21,9 +21,8 @@
*/
#if !defined(_WIN32)
#include "platform/unix_platform.hpp"
#include "platform/platform.hpp"
#include "app_config.hpp"
#include "events/event_system.hpp"
#include "events/types/filesystem_item_added.hpp"
#include "providers/i_provider.hpp"
@@ -36,61 +35,65 @@
#include "utils/unix.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_("repertory_" + 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(provider_type prov, std::string_view unique_id)
: mutex_id_(create_lock_id(prov, unique_id)) {
handle_ = 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() { release(); }
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();
auto lock_data::get_lock_data_file() const -> std::string {
auto dir = get_state_directory();
if (not utils::file::directory(dir).create_directory()) {
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"});
dir, {
fmt::format("{}_{}.json", mutex_id_, getuid()),
});
}
auto lock_data::get_lock_file() -> std::string {
const auto dir = get_state_directory();
auto lock_data::get_lock_file() const -> std::string {
auto dir = get_state_directory();
if (not utils::file::directory(dir).create_directory()) {
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())});
return utils::path::combine(
dir, {
fmt::format("{}_{}.lock", mutex_id_, 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);
}
auto handle = open(get_lock_data_file().c_str(), O_RDWR, S_IWUSR | S_IRUSR);
if (handle == -1) {
mount_state = {
{"Active", false},
{"Location", ""},
{"PID", -1},
};
close(fd);
return true;
}
auto ret{false};
if (wait_for_lock(handle) == 0) {
ret = utils::file::read_json_file(get_lock_data_file(), mount_state);
if (ret && mount_state.empty()) {
mount_state = {
{"Active", false},
{"Location", ""},
{"PID", -1},
};
}
flock(handle, LOCK_UN);
}
close(handle);
return ret;
}
@@ -98,25 +101,20 @@ auto lock_data::get_state_directory() -> std::string {
#if defined(__APPLE__)
return utils::path::absolute("~/Library/Application Support/" +
std::string{REPERTORY_DATA_NAME} + "/state");
#else
#else // !defined(__APPLE__)
return utils::path::absolute("~/.local/" + std::string{REPERTORY_DATA_NAME} +
"/state");
#endif
#endif // defined(__APPLE__)
}
auto lock_data::grab_lock(std::uint8_t retry_count) -> lock_result {
REPERTORY_USES_FUNCTION_NAME();
if (lock_fd_ == -1) {
if (handle_ == -1) {
return lock_result::failure;
}
lock_status_ = wait_for_lock(lock_fd_, retry_count);
lock_status_ = wait_for_lock(handle_, retry_count);
switch (lock_status_) {
case 0:
if (not set_mount_state(false, "", -1)) {
utils::error::raise_error(function_name, "failed to set mount state");
}
return lock_result::success;
case EWOULDBLOCK:
return lock_result::locked;
@@ -125,61 +123,72 @@ auto lock_data::grab_lock(std::uint8_t retry_count) -> lock_result {
}
}
auto lock_data::set_mount_state(bool active, const std::string &mount_location,
void lock_data::release() {
if (handle_ == -1) {
return;
}
if (lock_status_ == 0) {
[[maybe_unused]] auto success{utils::file::file{get_lock_file()}.remove()};
flock(handle_, LOCK_UN);
}
close(handle_);
handle_ = -1;
}
auto lock_data::set_mount_state(bool active, std::string_view mount_location,
int pid) -> bool {
REPERTORY_USES_FUNCTION_NAME();
auto ret = false;
auto handle =
open(get_lock_data_file().c_str(), O_CREAT | O_RDWR, S_IWUSR | S_IRUSR);
if (handle != -1) {
if (wait_for_lock(handle) == 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_name,
"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(),
[](auto &&val, auto &&line) -> auto { return val + line; });
auto json_data = json::parse(txt.empty() ? "{}" : txt);
json_data[mount_id] = {
{"Active", active},
{"Location", active ? mount_location : ""},
{"PID", active ? pid : -1},
};
ret = utils::file::write_json_file(get_lock_data_file(), json_data);
} else {
ret = true;
}
if (handle == -1) {
return false;
}
flock(handle, LOCK_UN);
auto ret{false};
if (wait_for_lock(handle) == 0) {
json mount_state;
if (not utils::file::read_json_file(get_lock_data_file(), mount_state)) {
utils::error::raise_error(function_name,
"failed to read mount state file|sp|" +
get_lock_file());
}
if ((mount_state.find("Active") == mount_state.end()) ||
(mount_state["Active"].get<bool>() != active) ||
(active &&
((mount_state.find("Location") == mount_state.end()) ||
(mount_state["Location"].get<std::string>() != mount_location)))) {
if (mount_location.empty() && not active) {
ret = utils::file::file{get_lock_data_file()}.remove();
} else {
ret = utils::file::write_json_file(
get_lock_data_file(),
{
{"Active", active},
{"Location", active ? mount_location : ""},
{"PID", active ? pid : -1},
});
}
} else {
ret = true;
}
close(handle);
flock(handle, LOCK_UN);
}
close(handle);
return ret;
}
auto lock_data::wait_for_lock(int fd, std::uint8_t retry_count) -> int {
static constexpr const std::uint32_t max_sleep = 100U;
auto lock_data::wait_for_lock(int handle, std::uint8_t retry_count) -> int {
static constexpr const std::uint32_t max_sleep{100U};
auto lock_status = EWOULDBLOCK;
auto remain = static_cast<std::uint32_t>(retry_count * max_sleep);
auto lock_status{EWOULDBLOCK};
auto remain{static_cast<std::uint32_t>(retry_count * max_sleep)};
while ((remain > 0) && (lock_status == EWOULDBLOCK)) {
lock_status = flock(fd, LOCK_EX | LOCK_NB);
lock_status = flock(handle, LOCK_EX | LOCK_NB);
if (lock_status == -1) {
lock_status = errno;
if (lock_status == EWOULDBLOCK) {
@@ -228,13 +237,13 @@ auto provider_meta_handler(i_provider &provider, bool directory,
const api_file &file) -> api_error {
REPERTORY_USES_FUNCTION_NAME();
const auto meta = create_meta_attributes(
auto meta = create_meta_attributes(
file.accessed_date,
directory ? FILE_ATTRIBUTE_DIRECTORY : 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, 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) {

View File

@@ -21,150 +21,171 @@
*/
#if defined(_WIN32)
#include "platform/win32_platform.hpp"
#include "platform/platform.hpp"
#include "events/event_system.hpp"
#include "events/types/filesystem_item_added.hpp"
#include "providers/i_provider.hpp"
#include "utils/config.hpp"
#include "utils/error_utils.hpp"
#include "utils/string.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];
lock_data::lock_data(provider_type prov, std::string unique_id)
: mutex_id_(create_lock_id(prov, unique_id)),
mutex_handle_(::CreateMutex(nullptr, FALSE,
create_lock_id(prov, unique_id).c_str())) {}
lock_data::~lock_data() { release(); }
auto lock_data::get_current_mount_state(json &mount_state) -> bool {
REPERTORY_USES_FUNCTION_NAME();
HKEY key{};
if (::RegOpenKeyEx(HKEY_CURRENT_USER,
fmt::format(R"(SOFTWARE\{}\Mounts\{})",
REPERTORY_DATA_NAME, mutex_id_)
.c_str(),
0, KEY_ALL_ACCESS, &key) != ERROR_SUCCESS) {
return true;
}
std::string data;
DWORD data_size{};
DWORD type{REG_SZ};
::RegGetValueA(key, nullptr, nullptr, RRF_RT_REG_SZ, &type, nullptr,
&data_size);
data.resize(data_size);
auto res = ::RegGetValueA(key, nullptr, nullptr, RRF_RT_REG_SZ, &type,
data.data(), &data_size);
auto ret = res == ERROR_SUCCESS || res == ERROR_FILE_NOT_FOUND;
if (ret && data_size != 0U) {
try {
mount_state = json::parse(data);
} catch (const std::exception &e) {
utils::error::raise_error(function_name, e, "failed to read mount state");
ret = false;
}
}
::RegCloseKey(key);
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);
if (not get_current_mount_state(mount_state)) {
return false;
}
return ret;
mount_state = mount_state.empty() ? json({
{"Active", false},
{"Location", ""},
{"PID", -1},
})
: mount_state;
return true;
}
auto lock_data::grab_lock(std::uint8_t retry_count) -> lock_result {
REPERTORY_USES_FUNCTION_NAME();
static constexpr const std::uint32_t max_sleep{100U};
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_name, "failed to set mount state");
}
}
} break;
case WAIT_TIMEOUT:
ret = lock_result::locked;
break;
default:
ret = lock_result::failure;
break;
}
return lock_result::failure;
}
return ret;
for (std::uint8_t idx = 0U;
(idx <= retry_count) &&
((mutex_state_ = ::WaitForSingleObject(mutex_handle_, max_sleep)) ==
WAIT_TIMEOUT);
++idx) {
}
switch (mutex_state_) {
case WAIT_OBJECT_0:
return lock_result::success;
case WAIT_TIMEOUT:
return lock_result::locked;
default:
return lock_result::failure;
}
}
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;
if (mutex_handle_ == INVALID_HANDLE_VALUE) {
return;
}
if ((mutex_state_ == WAIT_OBJECT_0) || (mutex_state_ == WAIT_ABANDONED)) {
if (mutex_state_ == WAIT_OBJECT_0) {
[[maybe_unused]] auto success{set_mount_state(false, "", -1)};
}
::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;
}
auto lock_data::set_mount_state(bool active, std::string_view mount_location,
std::int64_t pid) -> bool {
if (mutex_handle_ == INVALID_HANDLE_VALUE) {
return false;
}
json mount_state;
[[maybe_unused]] auto success{get_mount_state(mount_state)};
if (not((mount_state.find("Active") == mount_state.end()) ||
(mount_state["Active"].get<bool>() != active) ||
(active &&
((mount_state.find("Location") == mount_state.end()) ||
(mount_state["Location"].get<std::string>() != mount_location))))) {
return true;
}
HKEY key{};
if (::RegCreateKeyExA(HKEY_CURRENT_USER,
fmt::format(R"(SOFTWARE\{}\Mounts\{})",
REPERTORY_DATA_NAME, mutex_id_)
.c_str(),
0, nullptr, 0, KEY_ALL_ACCESS, nullptr, &key,
nullptr) != ERROR_SUCCESS) {
return false;
}
auto ret{false};
if (mount_location.empty() && not active) {
::RegCloseKey(key);
if (::RegCreateKeyExA(
HKEY_CURRENT_USER,
fmt::format(R"(SOFTWARE\{}\Mounts)", REPERTORY_DATA_NAME).c_str(),
0, nullptr, 0, KEY_ALL_ACCESS, nullptr, &key,
nullptr) != ERROR_SUCCESS) {
return false;
}
ret = (::RegDeleteKeyA(key, mutex_id_.c_str()) == ERROR_SUCCESS);
} else {
auto data{
json({
{"Active", active},
{"Location", active ? mount_location : ""},
{"PID", active ? pid : -1},
})
.dump(),
};
ret = (::RegSetValueEx(key, nullptr, 0, REG_SZ,
reinterpret_cast<const BYTE *>(data.c_str()),
static_cast<DWORD>(data.size())) == ERROR_SUCCESS);
}
::RegCloseKey(key);
return ret;
}
@@ -215,4 +236,4 @@ auto provider_meta_handler(i_provider &provider, bool directory,
}
} // namespace repertory
#endif //_WIN32
#endif // defined(_WIN32)

View File

@@ -22,8 +22,6 @@
#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)) {}

View File

@@ -28,64 +28,16 @@
#include "events/types/service_stop_begin.hpp"
#include "events/types/service_stop_end.hpp"
#include "events/types/unmount_requested.hpp"
#include "utils/base64.hpp"
#include "rpc/common.hpp"
#include "utils/error_utils.hpp"
#include "utils/string.hpp"
namespace repertory {
server::server(app_config &config) : config_(config) {}
auto server::check_authorization(const httplib::Request &req) -> bool {
REPERTORY_USES_FUNCTION_NAME();
if (config_.get_api_auth().empty() || config_.get_api_user().empty()) {
utils::error::raise_error(function_name,
"authorization user or password is not set");
return false;
}
auto authorization = req.get_header_value("Authorization");
if (authorization.empty()) {
utils::error::raise_error(function_name, "Authorization header is not set");
return false;
}
auto auth_parts = utils::string::split(authorization, ' ', true);
if (auth_parts.empty()) {
utils::error::raise_error(function_name, "Authorization header is empty");
return false;
}
auto auth_type = auth_parts[0U];
if (auth_type != "Basic") {
utils::error::raise_error(function_name, "Authorization is not Basic");
return false;
}
auto data = macaron::Base64::Decode(authorization.substr(6U));
auto auth_str = std::string(data.begin(), data.end());
auto auth = utils::string::split(auth_str, ':', false);
if (auth.size() < 2U) {
utils::error::raise_error(function_name, "Authorization is not valid");
return false;
}
auto user = auth.at(0U);
auth.erase(auth.begin());
auto pwd = utils::string::join(auth, ':');
if ((user != config_.get_api_user()) || (pwd != config_.get_api_auth())) {
utils::error::raise_error(function_name, "Authorization failed");
return false;
}
return true;
}
void server::handle_get_config(const httplib::Request & /*req*/,
httplib::Response &res) {
auto data = config_.get_json();
clean_json_config(config_.get_provider_type(), data);
res.set_content(data.dump(), "application/json");
res.status = http_error_codes::ok;
}
@@ -93,7 +45,10 @@ void server::handle_get_config(const httplib::Request & /*req*/,
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)}});
auto data = json({{
"value",
clean_json_value(name, config_.get_value_by_name(name)),
}});
res.set_content(data.dump(), "application/json");
res.status = http_error_codes::ok;
}
@@ -103,7 +58,10 @@ void server::handle_set_config_value_by_name(const httplib::Request &req,
auto name = req.get_param_value("name");
auto value = req.get_param_value("value");
json data = {{"value", config_.set_value_by_name(name, value)}};
json data = {{
"value",
clean_json_value(name, config_.set_value_by_name(name, value)),
}};
res.set_content(data.dump(), "application/json");
res.status = http_error_codes::ok;
}
@@ -173,7 +131,7 @@ void server::start() {
server_->set_pre_routing_handler(
[this](auto &&req, auto &&res) -> httplib::Server::HandlerResponse {
if (check_authorization(req)) {
if (rpc::check_authorization(config_, req)) {
return httplib::Server::HandlerResponse::Unhandled;
}
@@ -183,8 +141,21 @@ void server::start() {
initialize(*server_);
server_thread_ = std::make_unique<std::thread>(
[this]() { server_->listen("127.0.0.1", config_.get_api_port()); });
server_thread_ = std::make_unique<std::thread>([this]() {
server_->set_socket_options([](auto &&sock) {
#if defined(_WIN32)
int enable{1};
setsockopt(sock, SOL_SOCKET, SO_EXCLUSIVEADDRUSE,
reinterpret_cast<const char *>(&enable), sizeof(enable));
#else // !defined(_WIN32)
linger opt{1, 0};
setsockopt(sock, SOL_SOCKET, SO_LINGER,
reinterpret_cast<const char *>(&opt), sizeof(opt));
#endif // defined(_WIN32)
});
server_->listen("127.0.0.1", config_.get_api_port());
});
event_system::instance().raise<service_start_end>(function_name, "server");
}

View File

@@ -21,10 +21,56 @@
*/
#include "types/repertory.hpp"
#include "app_config.hpp"
#include "types/startup_exception.hpp"
#include "utils/string.hpp"
namespace repertory {
void clean_json_config(provider_type prov, nlohmann::json &data) {
data[JSON_API_PASSWORD] = "";
switch (prov) {
case provider_type::encrypt:
data[JSON_ENCRYPT_CONFIG][JSON_ENCRYPTION_TOKEN] = "";
data[JSON_REMOTE_MOUNT][JSON_ENCRYPTION_TOKEN] = "";
break;
case provider_type::remote:
data[JSON_REMOTE_CONFIG][JSON_ENCRYPTION_TOKEN] = "";
break;
case provider_type::s3:
data[JSON_REMOTE_MOUNT][JSON_ENCRYPTION_TOKEN] = "";
data[JSON_S3_CONFIG][JSON_ENCRYPTION_TOKEN] = "";
data[JSON_S3_CONFIG][JSON_SECRET_KEY] = "";
break;
case provider_type::sia:
data[JSON_REMOTE_MOUNT][JSON_ENCRYPTION_TOKEN] = "";
data[JSON_HOST_CONFIG][JSON_API_PASSWORD] = "";
break;
default:
return;
}
}
auto clean_json_value(std::string_view name, std::string_view data)
-> std::string {
if (name ==
fmt::format("{}.{}", JSON_ENCRYPT_CONFIG, JSON_ENCRYPTION_TOKEN) ||
name == fmt::format("{}.{}", JSON_HOST_CONFIG, JSON_API_PASSWORD) ||
name == fmt::format("{}.{}", JSON_REMOTE_CONFIG, JSON_ENCRYPTION_TOKEN) ||
name == fmt::format("{}.{}", JSON_REMOTE_MOUNT, JSON_ENCRYPTION_TOKEN) ||
name == fmt::format("{}.{}", JSON_S3_CONFIG, JSON_ENCRYPTION_TOKEN) ||
name == fmt::format("{}.{}", JSON_S3_CONFIG, JSON_SECRET_KEY) ||
name == JSON_API_PASSWORD) {
return "";
}
return std::string{data};
}
auto database_type_from_string(std::string type, database_type default_type)
-> database_type {
type = utils::string::to_lower(utils::string::trim(type));
@@ -191,4 +237,34 @@ auto api_error_to_string(const api_error &error) -> const std::string & {
return LOOKUP.at(error);
}
auto provider_type_from_string(std::string_view type,
provider_type default_type) -> provider_type {
auto type_lower = utils::string::to_lower(std::string{type});
if (type_lower == "encrypt") {
return provider_type::encrypt;
}
if (type_lower == "remote") {
return provider_type::remote;
}
if (type_lower == "s3") {
return provider_type::s3;
}
if (type_lower == "sia") {
return provider_type::sia;
}
if (type_lower == "unknown") {
return provider_type::unknown;
}
return default_type;
}
auto provider_type_to_string(provider_type type) -> std::string {
return app_config::get_provider_name(type);
}
} // namespace repertory

View File

@@ -45,7 +45,7 @@ void get_api_authentication_data(std::string &user, std::string &password,
if (success) {
if (user.empty() && password.empty()) {
password = data[JSON_API_AUTH].get<std::string>();
password = data[JSON_API_PASSWORD].get<std::string>();
user = data[JSON_API_USER].get<std::string>();
}
port = data[JSON_API_PORT].get<std::uint16_t>();

View File

@@ -44,11 +44,13 @@ struct repertory_exception_handler final
}
};
std::unique_ptr<repertory_exception_handler> handler{([]() -> auto * {
auto *ptr = new repertory_exception_handler{};
repertory::utils::error::set_exception_handler(ptr);
return ptr;
})()};
const auto repertory_handler{
([]() -> auto {
auto ptr = std::make_unique<repertory_exception_handler>();
repertory::utils::error::set_exception_handler(ptr.get());
return ptr;
})(),
};
} // namespace
namespace repertory::utils::error {

View File

@@ -0,0 +1,31 @@
/*
Copyright <2018-2025> <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 "platform/platform.hpp"
#include "app_config.hpp"
namespace repertory {
auto create_lock_id(provider_type prov, std::string_view unique_id)->std::string {
return fmt::format("{}_{}_{}", REPERTORY_DATA_NAME,
app_config::get_provider_name(prov), unique_id);
}
} // namespace repertory