Compare commits

..

No commits in common. "cf901b11b0395f180b9102aa1ca2f7de9f36fb47" and "0e0eb9e729b85964ebdd737d31a8b09d5f9782f5" have entirely different histories.

9 changed files with 230 additions and 269 deletions

View File

@ -115,7 +115,6 @@ googletest
gpath
gtest_version
has_setxattr
hkey
httpapi
httplib
icudata
@ -145,7 +144,6 @@ libuuid_include_dirs
libvlc
linkflags
localappdata
lpbyte
lptr
lpwstr
markdownlint

View File

@ -22,13 +22,6 @@
#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);
}
#if defined(_WIN32)
#include "platform/win32_platform.hpp"
#include "utils/windows.hpp"

View File

@ -30,7 +30,7 @@ class i_provider;
class lock_data final {
public:
explicit lock_data(const provider_type &prov, std::string unique_id);
explicit lock_data(const provider_type &prov, std::string unique_id /*= ""*/);
lock_data(const lock_data &) = delete;
lock_data(lock_data &&) = delete;
@ -41,6 +41,8 @@ public:
~lock_data();
private:
provider_type prov_;
std::string unique_id_;
std::string mutex_id_;
int lock_fd_;
int lock_status_{EWOULDBLOCK};
@ -60,12 +62,12 @@ private:
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,
std::string_view mount_location, int pid)
const std::string &mount_location, int pid)
-> bool;
};
@ -81,5 +83,5 @@ public:
const api_file &file) -> api_error;
} // namespace repertory
#endif // !defined(_WIN32)
#endif // _WIN32
#endif // REPERTORY_INCLUDE_PLATFORM_UNIXPLATFORM_HPP_

View File

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

View File

@ -21,7 +21,7 @@
*/
#if !defined(_WIN32)
#include "platform/platform.hpp"
#include "platform/unix_platform.hpp"
#include "app_config.hpp"
#include "events/event_system.hpp"
@ -36,53 +36,49 @@
#include "utils/unix.hpp"
namespace repertory {
lock_data::lock_data(const provider_type &prov, std::string unique_id)
: mutex_id_(create_lock_id(prov, unique_id)) {
lock_data::lock_data(const provider_type &prov, std::string unique_id /*= ""*/)
: prov_(prov),
unique_id_(std::move(unique_id)),
mutex_id_("repertory_" + app_config::get_provider_name(prov) + "_" +
unique_id_) {
lock_fd_ = open(get_lock_file().c_str(), O_CREAT | O_RDWR, S_IWUSR | S_IRUSR);
}
lock_data::~lock_data() { release(); }
auto lock_data::get_lock_data_file() -> std::string {
auto dir = get_state_directory();
const 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, {
fmt::format("{}_{}.json", mutex_id_, getuid()),
});
dir, {"mountstate_" + std::to_string(getuid()) + ".json"});
}
auto lock_data::get_lock_file() -> std::string {
auto dir = get_state_directory();
const 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, {
fmt::format("{}_{}.lock", mutex_id_, getuid()),
});
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) {
return false;
}
auto ret{false};
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;
}
@ -97,6 +93,8 @@ auto lock_data::get_state_directory() -> std::string {
}
auto lock_data::grab_lock(std::uint8_t retry_count) -> lock_result {
REPERTORY_USES_FUNCTION_NAME();
if (lock_fd_ == -1) {
return lock_result::failure;
}
@ -118,7 +116,7 @@ void lock_data::release() {
}
if (lock_status_ == 0) {
utils::file::file{get_lock_file()}.delete();
unlink(get_lock_file().c_str());
flock(lock_fd_, LOCK_UN);
}
@ -130,36 +128,38 @@ auto lock_data::set_mount_state(bool active, const std::string &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) {
return false;
}
auto ret{false};
if (handle != -1) {
if (wait_for_lock(handle) == 0) {
const auto mount_id =
app_config::get_provider_display_name(prov_) + 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("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()}.delete();
} else {
ret = utils::file::write_json_file(
get_lock_data_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;
}
@ -168,14 +168,15 @@ auto lock_data::set_mount_state(bool active, const std::string &mount_location,
}
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};
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);
if (lock_status == -1) {
@ -226,7 +227,7 @@ auto provider_meta_handler(i_provider &provider, bool directory,
const api_file &file) -> api_error {
REPERTORY_USES_FUNCTION_NAME();
auto meta = create_meta_attributes(
const 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,

View File

@ -21,171 +21,136 @@
*/
#if defined(_WIN32)
#include "platform/platform.hpp"
#include "platform/win32_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 {
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;
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];
}
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 {
if (not get_current_mount_state(mount_state)) {
return false;
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;
}
mount_state = mount_state.empty() ? json({
{"Active", false},
{"Location", ""},
{"PID", -1},
})
: mount_state;
return true;
}
::RegCloseKey(key);
}
return ret;
}
auto lock_data::grab_lock(std::uint8_t retry_count) -> lock_result {
static constexpr const std::uint32_t max_sleep{100U};
REPERTORY_USES_FUNCTION_NAME();
auto ret = lock_result::success;
if (mutex_handle_ == INVALID_HANDLE_VALUE) {
return lock_result::failure;
}
for (std::uint8_t idx = 0U;
(idx <= retry_count) &&
((mutex_state_ = ::WaitForSingleObject(mutex_handle_, max_sleep)) ==
WAIT_TIMEOUT);
++idx) {
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:
return lock_result::success;
ret = lock_result::success;
break;
case WAIT_TIMEOUT:
return lock_result::locked;
ret = lock_result::locked;
break;
default:
return lock_result::failure;
ret = lock_result::failure;
break;
}
}
return ret;
}
void lock_data::release() {
if (mutex_handle_ == INVALID_HANDLE_VALUE) {
return;
}
if (mutex_handle_ != INVALID_HANDLE_VALUE) {
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, std::string_view mount_location,
std::int64_t pid) -> bool {
if (mutex_handle_ == INVALID_HANDLE_VALUE) {
return false;
}
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 (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(
[[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,
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},
("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(),
};
ret = (::RegSetValueEx(key, nullptr, 0, REG_SZ,
reinterpret_cast<const BYTE *>(data.c_str()),
static_cast<DWORD>(data.size())) == ERROR_SUCCESS);
{"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;
}
}
::RegCloseKey(key);
return ret;
}
@ -236,4 +201,4 @@ auto provider_meta_handler(i_provider &provider, bool directory,
}
} // namespace repertory
#endif // defined(_WIN32)
#endif //_WIN32

View File

@ -1,31 +0,0 @@
/*
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) {
return fmt::format("{}_{}_{}", REPERTORY_DATA_NAME,
app_config::get_provider_name(prov), unique_id);
}
} // namespace repertory

View File

@ -428,6 +428,8 @@ void handlers::handle_get_mount_location(const httplib::Request &req,
void handlers::handle_get_mount_status(const httplib::Request &req,
httplib::Response &res) const {
REPERTORY_USES_FUNCTION_NAME();
auto name = req.get_param_value("name");
auto prov = provider_type_from_string(req.get_param_value("type"));
@ -436,9 +438,34 @@ void handlers::handle_get_mount_status(const httplib::Request &req,
return;
}
auto status_name = app_config::get_provider_display_name(prov);
switch (prov) {
case provider_type::remote: {
auto parts = utils::string::split(name, '_', false);
status_name =
fmt::format("{}{}:{}", status_name, parts.at(0U), parts.at(1U));
} break;
case provider_type::encrypt:
case provider_type::sia:
case provider_type::s3:
status_name = fmt::format("{}{}", status_name, name);
break;
default:
throw utils::error::create_exception(function_name,
{
"provider is not supported",
provider_type_to_string(prov),
name,
});
}
auto lines = launch_process(prov, name, {"-status"});
auto result = nlohmann::json::parse(utils::string::join(lines, '\n'));
nlohmann::json result(
nlohmann::json::parse(utils::string::join(lines, '\n')).at(status_name));
if (result.at("Location").get<std::string>().empty()) {
result.at("Location") = config_->get_mount_location(prov, name);
} else if (result.at("Active").get<bool>()) {
@ -576,7 +603,7 @@ void handlers::handle_put_set_value_by_name(const httplib::Request &req,
void handlers::handle_put_settings(const httplib::Request &req,
httplib::Response &res) const {
auto data = nlohmann::json::parse(req.get_param_value("data"));
nlohmann::json data = nlohmann::json::parse(req.get_param_value("data"));
if (data.contains(JSON_API_PASSWORD)) {
auto password = decrypt(data.at(JSON_API_PASSWORD).get<std::string>(),

View File

@ -62,16 +62,13 @@ TEST(lock_data_test, set_and_unset_mount_state) {
json mount_state;
EXPECT_TRUE(l.get_mount_state(mount_state));
EXPECT_STREQ(R"({"Active":true,"Location":"C:","PID":99})",
mount_state.dump().c_str());
EXPECT_TRUE(l2.get_mount_state(mount_state));
mount_state["Sia1"].dump().c_str());
EXPECT_STREQ(R"({"Active":true,"Location":"D:","PID":97})",
mount_state.dump().c_str());
EXPECT_TRUE(l3.get_mount_state(mount_state));
mount_state["Remote1"].dump().c_str());
EXPECT_STREQ(R"({"Active":true,"Location":"E:","PID":96})",
mount_state.dump().c_str());
mount_state["Remote2"].dump().c_str());
EXPECT_TRUE(l.set_mount_state(false, "C:", 99));
EXPECT_TRUE(l2.set_mount_state(false, "D:", 98));
@ -79,15 +76,11 @@ TEST(lock_data_test, set_and_unset_mount_state) {
EXPECT_TRUE(l.get_mount_state(mount_state));
EXPECT_STREQ(R"({"Active":false,"Location":"","PID":-1})",
mount_state.dump().c_str());
EXPECT_TRUE(l2.get_mount_state(mount_state));
mount_state["Sia1"].dump().c_str());
EXPECT_STREQ(R"({"Active":false,"Location":"","PID":-1})",
mount_state.dump().c_str());
EXPECT_TRUE(l3.get_mount_state(mount_state));
mount_state["Remote1"].dump().c_str());
EXPECT_STREQ(R"({"Active":false,"Location":"","PID":-1})",
mount_state.dump().c_str());
mount_state["Remote2"].dump().c_str());
}
#else
TEST(lock_data_test, set_and_unset_mount_state) {
@ -98,13 +91,14 @@ TEST(lock_data_test, set_and_unset_mount_state) {
EXPECT_TRUE(l.get_mount_state(mount_state));
EXPECT_STREQ(R"({"Active":true,"Location":"/mnt/1","PID":99})",
mount_state.dump().c_str());
mount_state["Sia1"].dump().c_str());
EXPECT_TRUE(l.set_mount_state(false, "/mnt/1", 99));
EXPECT_TRUE(l.get_mount_state(mount_state));
EXPECT_STREQ(R"({"Active":false,"Location":"","PID":-1})",
mount_state.dump().c_str());
mount_state["Sia1"].dump().c_str());
}
#endif
} // namespace repertory