Compare commits
273 Commits
v2.0.5-rc
...
9841c3f29c
Author | SHA1 | Date | |
---|---|---|---|
9841c3f29c | |||
b72baf2c65 | |||
6244a587db | |||
cd41e026ee | |||
10829fc9d9 | |||
4f69ec5f10 | |||
5dc3a0239f | |||
6b18a72b95 | |||
0e8e56ad90 | |||
e53acf799a | |||
c2eaa92f4a | |||
13eab49207 | |||
616dca89ca | |||
9626f383d3 | |||
a262a79eb2 | |||
f9ec02bf3f | |||
e6793f0d6c | |||
0d972b0b75 | |||
60f0e3dbc1 | |||
630c3463d8 | |||
e3d036fcb8 | |||
143a41588f | |||
9fd5b7c03f | |||
6137b69bd3 | |||
c52c919607 | |||
58841986f3 | |||
874f5319fd | |||
c05b4f6652 | |||
aad26b8529 | |||
e4f59dadfb | |||
f8b1b8ad37 | |||
03c8f3461e | |||
5b09333f0d | |||
40e57f3262 | |||
959d904961 | |||
d301e8d871 | |||
1cc3e6baf0 | |||
f5b912b16f | |||
29f7a78dcc | |||
56d5a57831 | |||
972927b2ac | |||
42c25b73e7 | |||
ea876bd55c | |||
6d2023ba1b | |||
164f8ffc7c | |||
a28151068a | |||
674a5a6fe1 | |||
d9b8a60055 | |||
6570438872 | |||
12ef6910ed | |||
225a5cedc5 | |||
b42dc4e942 | |||
b72744716d | |||
1b6237b9fc | |||
bdb8f0aac9 | |||
29fb70149c | |||
4b45e71193 | |||
0101e92d97 | |||
ba5bde24e1 | |||
40d71223ae | |||
e73fb02101 | |||
7f300b33c8 | |||
c4c509790d | |||
1ac64c9d82 | |||
c037fd5657 | |||
9b9929e69d | |||
5104af84dc | |||
ab42289792 | |||
0e4ebd7742 | |||
ea912b38a3 | |||
f015647b71 | |||
bced895ea1 | |||
901000a085 | |||
d5242c2e6c | |||
7c4cba6797 | |||
10a0e926ad | |||
9fcc50fcc3 | |||
d57fa022d8 | |||
ee1638e1dd | |||
d125cb47d6 | |||
16bb4fe472 | |||
984f4e0bc9 | |||
439be1dea8 | |||
c9281c9bcd | |||
b15393bacf | |||
f3c0f4978f | |||
78ba8553a9 | |||
2cc646390a | |||
87f5b0f953 | |||
c60bb651df | |||
6fb8053e43 | |||
4c18088bd1 | |||
a5eb168032 | |||
187e2454ca | |||
931c861850 | |||
2a1abd0bb0 | |||
c75ce9ad21 | |||
9afc8e3cb6 | |||
56beeacbb3 | |||
4253700fe7 | |||
2d638d6335 | |||
7823627340 | |||
e7accd1cc1 | |||
9b5d642106 | |||
e167079e5d | |||
1081ff0b19 | |||
c720181208 | |||
d7a0fd9d8c | |||
2a7a409506 | |||
97310e2ba9 | |||
54fe6d7bab | |||
f70044b9a8 | |||
ea30396e49 | |||
985e503c21 | |||
b959af26c4 | |||
0add5ec944 | |||
cd9ad1e322 | |||
ca8de7f87d | |||
2ff72eebce | |||
97fce78370 | |||
21af9be9a8 | |||
23de275e11 | |||
28cfcc0344 | |||
30a91e1cb2 | |||
b2d4baa903 | |||
d3070ffee1 | |||
abd7e24b5e | |||
462ebe6fcf | |||
b93e2978b0 | |||
ece002f25b | |||
b9f5f774e2 | |||
52d210974b | |||
7605be809c | |||
d0f2f78698 | |||
69c574b906 | |||
67a669ee17 | |||
c82f07056d | |||
196d721005 | |||
532dd3c417 | |||
c4b4bc9b49 | |||
343ac025d5 | |||
d08fd52e51 | |||
05f323665a | |||
349bede3b3 | |||
1a7dc51c4b | |||
d209b52848 | |||
492ccfbdfb | |||
678dfd6c8b | |||
bc146a48a7 | |||
04523df564 | |||
b1a31b230a | |||
fe08e274ec | |||
33e0066b4c | |||
ce814e96a6 | |||
7dd56cf715 | |||
79262ef862 | |||
e270867bf2 | |||
6b629b81b4 | |||
812f68422b | |||
76c105286f | |||
cd9ac4a02f | |||
6d09b2549e | |||
69f27b2e69 | |||
7cbf1a3937 | |||
dc817797f3 | |||
71789f6cdb | |||
6e8f843252 | |||
f72f86d8ae | |||
45ea5bab8f | |||
9d083a1d93 | |||
85beb3dfea | |||
3eb4ad85d3 | |||
e45119bbbf | |||
4326169186 | |||
e32233a936 | |||
6e8273f9bc | |||
863e316b8a | |||
07311cd710 | |||
637f123c24 | |||
6602e7eff6 | |||
0fd2dc3ddb | |||
d920a55fc0 | |||
edd27e3e34 | |||
6a8a163d0a | |||
99fa7ffc15 | |||
957c1f5256 | |||
0214bcff6d | |||
9173ff8d4d | |||
302cd43a03 | |||
fe51811b39 | |||
35e52d50a7 | |||
eb7bfdddd1 | |||
be6ee3ba07 | |||
4dbe4a30b4 | |||
1d37822451 | |||
5ed74aa5af | |||
94073fe1f1 | |||
992a02a6ee | |||
6f45848db4 | |||
918d7172ec | |||
8da6008e29 | |||
d2065af398 | |||
b4fd093e7c | |||
5e47cdb861 | |||
eb8f66ebe9 | |||
72a2567c83 | |||
c1e5bd6b0b | |||
02157d21ea | |||
0318489b6c | |||
847bf68f85 | |||
6e42899dd3 | |||
35a794106b | |||
c54838ad6f | |||
58fde34cfe | |||
3a72563a5c | |||
9e2ad81cff | |||
18a66953f2 | |||
b2c0f44a7d | |||
791472455a | |||
8638de87f9 | |||
9409622fef | |||
f2db24b239 | |||
199aea55be | |||
8a18e7a4c9 | |||
dce856b2be | |||
51ee46e279 | |||
a0bf5ec3d2 | |||
b74160bfb3 | |||
e35f43af97 | |||
e9d65bb566 | |||
15f6b116cc | |||
71f3567375 | |||
865ed5f0c1 | |||
661f57d599 | |||
badd098fe0 | |||
820617b3ff | |||
717b461eaf | |||
4b3890809d | |||
d9cd2aa88a | |||
c59c846856 | |||
1a400cac8d | |||
57b956b60c | |||
b95d69ccb8 | |||
d90a0eab3d | |||
c6448cc64c | |||
514e9535e2 | |||
14cee39a20 | |||
2c510346f2 | |||
1560804df8 | |||
cc3c0febc3 | |||
0458f12e17 | |||
ae43cedb45 | |||
191eb1620b | |||
9c648583fb | |||
14d78d0b65 | |||
54efde0497 | |||
5f593ab86d | |||
feb09746f5 | |||
faad98c11e | |||
12f04c6064 | |||
ca307c3bf2 | |||
ac6f4bcade | |||
9d48cd97e3 | |||
6c6fb0554f | |||
a134f01436 | |||
44c33652fa | |||
131c36415d | |||
9456c8b1d2 | |||
b8c62612d8 | |||
521874a56f | |||
f41ad47262 | |||
8bb2eeb88c | |||
b1aca46034 |
@ -115,7 +115,6 @@ googletest
|
|||||||
gpath
|
gpath
|
||||||
gtest_version
|
gtest_version
|
||||||
has_setxattr
|
has_setxattr
|
||||||
hkey
|
|
||||||
httpapi
|
httpapi
|
||||||
httplib
|
httplib
|
||||||
icudata
|
icudata
|
||||||
@ -145,7 +144,6 @@ libuuid_include_dirs
|
|||||||
libvlc
|
libvlc
|
||||||
linkflags
|
linkflags
|
||||||
localappdata
|
localappdata
|
||||||
lpbyte
|
|
||||||
lptr
|
lptr
|
||||||
lpwstr
|
lpwstr
|
||||||
markdownlint
|
markdownlint
|
||||||
|
@ -4,14 +4,15 @@
|
|||||||
|
|
||||||
### Issues
|
### Issues
|
||||||
|
|
||||||
|
* ~~\#12 [Unit Test] Complete all providers unit tests~~
|
||||||
|
* ~~\#21 [Unit Test] Complete WinFSP unit tests~~
|
||||||
|
* ~~\#22 [Unit Test] Complete FUSE unit tests~~
|
||||||
* \#39 Create management portal in Flutter
|
* \#39 Create management portal in Flutter
|
||||||
|
|
||||||
### Changes from v2.0.4-rc
|
### Changes from v2.0.4-rc
|
||||||
|
|
||||||
* Continue documentation updates
|
* Continue documentation updates
|
||||||
* Fixed `-status` command erasing active mount information
|
* Prevent overlapping `repertory` `ApiPort`'s
|
||||||
* Fixed overlapping HTTP REST API port's
|
|
||||||
* Refactored/fixed instance locking
|
|
||||||
* Removed passwords and secret key values from API calls
|
* Removed passwords and secret key values from API calls
|
||||||
* Renamed setting `ApiAuth` to `ApiPassword`
|
* Renamed setting `ApiAuth` to `ApiPassword`
|
||||||
* Require `--name,-na` option for encryption provider
|
* Require `--name,-na` option for encryption provider
|
||||||
|
@ -22,13 +22,6 @@
|
|||||||
#ifndef REPERTORY_INCLUDE_PLATFORM_PLATFORM_HPP_
|
#ifndef REPERTORY_INCLUDE_PLATFORM_PLATFORM_HPP_
|
||||||
#define 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)
|
#if defined(_WIN32)
|
||||||
#include "platform/win32_platform.hpp"
|
#include "platform/win32_platform.hpp"
|
||||||
#include "utils/windows.hpp"
|
#include "utils/windows.hpp"
|
||||||
|
@ -30,44 +30,40 @@ class i_provider;
|
|||||||
|
|
||||||
class lock_data final {
|
class lock_data final {
|
||||||
public:
|
public:
|
||||||
lock_data(provider_type prov, std::string_view unique_id);
|
explicit lock_data(const provider_type &pt, std::string unique_id /*= ""*/);
|
||||||
|
|
||||||
lock_data(const lock_data &) = delete;
|
lock_data();
|
||||||
lock_data(lock_data &&) = delete;
|
|
||||||
|
|
||||||
auto operator=(const lock_data &) -> lock_data & = delete;
|
|
||||||
auto operator=(lock_data &&) -> lock_data & = delete;
|
|
||||||
|
|
||||||
~lock_data();
|
~lock_data();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string mutex_id_;
|
const provider_type pt_;
|
||||||
|
const std::string unique_id_;
|
||||||
private:
|
const std::string mutex_id_;
|
||||||
int handle_{};
|
int lock_fd_;
|
||||||
int lock_status_{EWOULDBLOCK};
|
int lock_status_{EWOULDBLOCK};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
[[nodiscard]] static auto get_state_directory() -> std::string;
|
[[nodiscard]] static auto get_state_directory() -> std::string;
|
||||||
|
|
||||||
[[nodiscard]] auto get_lock_data_file() const -> std::string;
|
[[nodiscard]] static auto get_lock_data_file() -> std::string;
|
||||||
|
|
||||||
[[nodiscard]] auto get_lock_file() const -> std::string;
|
[[nodiscard]] auto get_lock_file() -> std::string;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
[[nodiscard]] static auto wait_for_lock(int handle,
|
[[nodiscard]] static auto wait_for_lock(int fd,
|
||||||
std::uint8_t retry_count = 30U)
|
std::uint8_t retry_count = 30u)
|
||||||
-> int;
|
-> int;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
[[nodiscard]] auto get_mount_state(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 grab_lock(std::uint8_t retry_count = 30u) -> lock_result;
|
||||||
|
|
||||||
void release();
|
void release();
|
||||||
|
|
||||||
[[nodiscard]] auto set_mount_state(bool active,
|
[[nodiscard]] auto set_mount_state(bool active,
|
||||||
std::string_view mount_location, int pid)
|
const std::string &mount_location, int pid)
|
||||||
-> bool;
|
-> bool;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -83,5 +79,5 @@ public:
|
|||||||
const api_file &file) -> api_error;
|
const api_file &file) -> api_error;
|
||||||
} // namespace repertory
|
} // namespace repertory
|
||||||
|
|
||||||
#endif // !defined(_WIN32)
|
#endif // _WIN32
|
||||||
#endif // REPERTORY_INCLUDE_PLATFORM_UNIXPLATFORM_HPP_
|
#endif // REPERTORY_INCLUDE_PLATFORM_UNIXPLATFORM_HPP_
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
#define REPERTORY_INCLUDE_PLATFORM_WINPLATFORM_HPP_
|
#define REPERTORY_INCLUDE_PLATFORM_WINPLATFORM_HPP_
|
||||||
#if defined(_WIN32)
|
#if defined(_WIN32)
|
||||||
|
|
||||||
|
#include "app_config.hpp"
|
||||||
#include "types/repertory.hpp"
|
#include "types/repertory.hpp"
|
||||||
|
|
||||||
namespace repertory {
|
namespace repertory {
|
||||||
@ -30,32 +31,43 @@ class i_provider;
|
|||||||
|
|
||||||
class lock_data final {
|
class lock_data final {
|
||||||
public:
|
public:
|
||||||
explicit lock_data(provider_type prov, std::string unique_id);
|
explicit lock_data(const provider_type &pt, std::string unique_id /*= ""*/)
|
||||||
lock_data(const lock_data &) = delete;
|
: pt_(pt),
|
||||||
lock_data(lock_data &&) = delete;
|
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;
|
~lock_data() { release(); }
|
||||||
auto operator=(lock_data &&) -> lock_data & = delete;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string mutex_id_;
|
const provider_type pt_;
|
||||||
HANDLE mutex_handle_{INVALID_HANDLE_VALUE};
|
const std::string unique_id_;
|
||||||
DWORD mutex_state_{WAIT_FAILED};
|
const std::string mutex_id_;
|
||||||
|
HANDLE mutex_handle_;
|
||||||
[[nodiscard]] auto get_current_mount_state(json &mount_state) -> bool;
|
DWORD mutex_state_ = WAIT_FAILED;
|
||||||
|
|
||||||
public:
|
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_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();
|
void release();
|
||||||
|
|
||||||
[[nodiscard]] auto set_mount_state(bool active,
|
[[nodiscard]] auto set_mount_state(bool active,
|
||||||
std::string_view mount_location,
|
const std::string &mount_location,
|
||||||
std::int64_t pid) -> bool;
|
const std::int64_t &pid) -> bool;
|
||||||
};
|
};
|
||||||
|
|
||||||
[[nodiscard]] auto create_meta_attributes(
|
[[nodiscard]] auto create_meta_attributes(
|
||||||
|
@ -21,8 +21,9 @@
|
|||||||
*/
|
*/
|
||||||
#if !defined(_WIN32)
|
#if !defined(_WIN32)
|
||||||
|
|
||||||
#include "platform/platform.hpp"
|
#include "platform/unix_platform.hpp"
|
||||||
|
|
||||||
|
#include "app_config.hpp"
|
||||||
#include "events/event_system.hpp"
|
#include "events/event_system.hpp"
|
||||||
#include "events/types/filesystem_item_added.hpp"
|
#include "events/types/filesystem_item_added.hpp"
|
||||||
#include "providers/i_provider.hpp"
|
#include "providers/i_provider.hpp"
|
||||||
@ -35,65 +36,52 @@
|
|||||||
#include "utils/unix.hpp"
|
#include "utils/unix.hpp"
|
||||||
|
|
||||||
namespace repertory {
|
namespace repertory {
|
||||||
lock_data::lock_data(provider_type prov, std::string_view unique_id)
|
lock_data::lock_data(const provider_type &pt, std::string unique_id /*= ""*/)
|
||||||
: mutex_id_(create_lock_id(prov, unique_id)) {
|
: pt_(pt),
|
||||||
handle_ = open(get_lock_file().c_str(), O_CREAT | O_RDWR, S_IWUSR | S_IRUSR);
|
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()
|
||||||
|
: pt_(provider_type::sia), unique_id_(""), mutex_id_(""), lock_fd_(-1) {}
|
||||||
|
|
||||||
lock_data::~lock_data() { release(); }
|
lock_data::~lock_data() { release(); }
|
||||||
|
|
||||||
auto lock_data::get_lock_data_file() const -> std::string {
|
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()) {
|
if (not utils::file::directory(dir).create_directory()) {
|
||||||
throw startup_exception("failed to create directory|sp|" + dir + "|err|" +
|
throw startup_exception("failed to create directory|sp|" + dir + "|err|" +
|
||||||
std::to_string(utils::get_last_error_code()));
|
std::to_string(utils::get_last_error_code()));
|
||||||
}
|
}
|
||||||
|
|
||||||
return utils::path::combine(
|
return utils::path::combine(
|
||||||
dir, {
|
dir, {"mountstate_" + std::to_string(getuid()) + ".json"});
|
||||||
fmt::format("{}_{}.json", mutex_id_, getuid()),
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto lock_data::get_lock_file() const -> std::string {
|
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()) {
|
if (not utils::file::directory(dir).create_directory()) {
|
||||||
throw startup_exception("failed to create directory|sp|" + dir + "|err|" +
|
throw startup_exception("failed to create directory|sp|" + dir + "|err|" +
|
||||||
std::to_string(utils::get_last_error_code()));
|
std::to_string(utils::get_last_error_code()));
|
||||||
}
|
}
|
||||||
|
|
||||||
return utils::path::combine(
|
return utils::path::combine(dir,
|
||||||
dir, {
|
{mutex_id_ + "_" + std::to_string(getuid())});
|
||||||
fmt::format("{}_{}.lock", mutex_id_, getuid()),
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto lock_data::get_mount_state(json &mount_state) -> bool {
|
auto lock_data::get_mount_state(json &mount_state) -> bool {
|
||||||
auto handle = open(get_lock_data_file().c_str(), O_RDWR, S_IWUSR | S_IRUSR);
|
auto ret = false;
|
||||||
if (handle == -1) {
|
auto fd =
|
||||||
mount_state = {
|
open(get_lock_data_file().c_str(), O_CREAT | O_RDWR, S_IWUSR | S_IRUSR);
|
||||||
{"Active", false},
|
if (fd != -1) {
|
||||||
{"Location", ""},
|
if (wait_for_lock(fd) == 0) {
|
||||||
{"PID", -1},
|
|
||||||
};
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto ret{false};
|
|
||||||
if (wait_for_lock(handle) == 0) {
|
|
||||||
ret = utils::file::read_json_file(get_lock_data_file(), mount_state);
|
ret = utils::file::read_json_file(get_lock_data_file(), mount_state);
|
||||||
if (ret && mount_state.empty()) {
|
flock(fd, LOCK_UN);
|
||||||
mount_state = {
|
|
||||||
{"Active", false},
|
|
||||||
{"Location", ""},
|
|
||||||
{"PID", -1},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
flock(handle, LOCK_UN);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
close(handle);
|
close(fd);
|
||||||
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -101,20 +89,25 @@ auto lock_data::get_state_directory() -> std::string {
|
|||||||
#if defined(__APPLE__)
|
#if defined(__APPLE__)
|
||||||
return utils::path::absolute("~/Library/Application Support/" +
|
return utils::path::absolute("~/Library/Application Support/" +
|
||||||
std::string{REPERTORY_DATA_NAME} + "/state");
|
std::string{REPERTORY_DATA_NAME} + "/state");
|
||||||
#else // !defined(__APPLE__)
|
#else
|
||||||
return utils::path::absolute("~/.local/" + std::string{REPERTORY_DATA_NAME} +
|
return utils::path::absolute("~/.local/" + std::string{REPERTORY_DATA_NAME} +
|
||||||
"/state");
|
"/state");
|
||||||
#endif // defined(__APPLE__)
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
auto lock_data::grab_lock(std::uint8_t retry_count) -> lock_result {
|
auto lock_data::grab_lock(std::uint8_t retry_count) -> lock_result {
|
||||||
if (handle_ == -1) {
|
REPERTORY_USES_FUNCTION_NAME();
|
||||||
|
|
||||||
|
if (lock_fd_ == -1) {
|
||||||
return lock_result::failure;
|
return lock_result::failure;
|
||||||
}
|
}
|
||||||
|
|
||||||
lock_status_ = wait_for_lock(handle_, retry_count);
|
lock_status_ = wait_for_lock(lock_fd_, retry_count);
|
||||||
switch (lock_status_) {
|
switch (lock_status_) {
|
||||||
case 0:
|
case 0:
|
||||||
|
if (not set_mount_state(false, "", -1)) {
|
||||||
|
utils::error::raise_error(function_name, "failed to set mount state");
|
||||||
|
}
|
||||||
return lock_result::success;
|
return lock_result::success;
|
||||||
case EWOULDBLOCK:
|
case EWOULDBLOCK:
|
||||||
return lock_result::locked;
|
return lock_result::locked;
|
||||||
@ -124,53 +117,55 @@ auto lock_data::grab_lock(std::uint8_t retry_count) -> lock_result {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void lock_data::release() {
|
void lock_data::release() {
|
||||||
if (handle_ == -1) {
|
if (lock_fd_ == -1) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lock_status_ == 0) {
|
if (lock_status_ == 0) {
|
||||||
[[maybe_unused]] auto success{utils::file::file{get_lock_file()}.remove()};
|
unlink(get_lock_file().c_str());
|
||||||
flock(handle_, LOCK_UN);
|
flock(lock_fd_, LOCK_UN);
|
||||||
}
|
}
|
||||||
|
|
||||||
close(handle_);
|
close(lock_fd_);
|
||||||
handle_ = -1;
|
lock_fd_ = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto lock_data::set_mount_state(bool active, std::string_view mount_location,
|
auto lock_data::set_mount_state(bool active, const std::string &mount_location,
|
||||||
int pid) -> bool {
|
int pid) -> bool {
|
||||||
REPERTORY_USES_FUNCTION_NAME();
|
REPERTORY_USES_FUNCTION_NAME();
|
||||||
|
|
||||||
|
auto ret = false;
|
||||||
auto handle =
|
auto handle =
|
||||||
open(get_lock_data_file().c_str(), O_CREAT | O_RDWR, S_IWUSR | S_IRUSR);
|
open(get_lock_data_file().c_str(), O_CREAT | O_RDWR, S_IWUSR | S_IRUSR);
|
||||||
if (handle == -1) {
|
if (handle != -1) {
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto ret{false};
|
|
||||||
if (wait_for_lock(handle) == 0) {
|
if (wait_for_lock(handle) == 0) {
|
||||||
|
const auto mount_id =
|
||||||
|
app_config::get_provider_display_name(pt_) + unique_id_;
|
||||||
json mount_state;
|
json mount_state;
|
||||||
if (not utils::file::read_json_file(get_lock_data_file(), mount_state)) {
|
if (not utils::file::read_json_file(get_lock_data_file(), mount_state)) {
|
||||||
utils::error::raise_error(function_name,
|
utils::error::raise_error(function_name,
|
||||||
"failed to read mount state file|sp|" +
|
"failed to read mount state file|sp|" +
|
||||||
get_lock_file());
|
get_lock_file());
|
||||||
}
|
}
|
||||||
if ((mount_state.find("Active") == mount_state.end()) ||
|
if ((mount_state.find(mount_id) == mount_state.end()) ||
|
||||||
(mount_state["Active"].get<bool>() != active) ||
|
(mount_state[mount_id].find("Active") ==
|
||||||
(active &&
|
mount_state[mount_id].end()) ||
|
||||||
((mount_state.find("Location") == mount_state.end()) ||
|
(mount_state[mount_id]["Active"].get<bool>() != active) ||
|
||||||
(mount_state["Location"].get<std::string>() != mount_location)))) {
|
(active && ((mount_state[mount_id].find("Location") ==
|
||||||
if (mount_location.empty() && not active) {
|
mount_state[mount_id].end()) ||
|
||||||
ret = utils::file::file{get_lock_data_file()}.remove();
|
(mount_state[mount_id]["Location"].get<std::string>() !=
|
||||||
} else {
|
mount_location)))) {
|
||||||
ret = utils::file::write_json_file(
|
const auto lines = utils::file::read_file_lines(get_lock_data_file());
|
||||||
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},
|
{"Active", active},
|
||||||
{"Location", active ? mount_location : ""},
|
{"Location", active ? mount_location : ""},
|
||||||
{"PID", active ? pid : -1},
|
{"PID", active ? pid : -1},
|
||||||
});
|
};
|
||||||
}
|
ret = utils::file::write_json_file(get_lock_data_file(), json_data);
|
||||||
} else {
|
} else {
|
||||||
ret = true;
|
ret = true;
|
||||||
}
|
}
|
||||||
@ -179,16 +174,17 @@ auto lock_data::set_mount_state(bool active, std::string_view mount_location,
|
|||||||
}
|
}
|
||||||
|
|
||||||
close(handle);
|
close(handle);
|
||||||
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto lock_data::wait_for_lock(int handle, std::uint8_t retry_count) -> int {
|
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 lock_status = EWOULDBLOCK;
|
||||||
auto remain{static_cast<std::uint32_t>(retry_count * max_sleep)};
|
auto remain = static_cast<std::uint32_t>(retry_count * max_sleep);
|
||||||
while ((remain > 0) && (lock_status == EWOULDBLOCK)) {
|
while ((remain > 0) && (lock_status == EWOULDBLOCK)) {
|
||||||
lock_status = flock(handle, LOCK_EX | LOCK_NB);
|
lock_status = flock(fd, LOCK_EX | LOCK_NB);
|
||||||
if (lock_status == -1) {
|
if (lock_status == -1) {
|
||||||
lock_status = errno;
|
lock_status = errno;
|
||||||
if (lock_status == EWOULDBLOCK) {
|
if (lock_status == EWOULDBLOCK) {
|
||||||
@ -237,13 +233,13 @@ auto provider_meta_handler(i_provider &provider, bool directory,
|
|||||||
const api_file &file) -> api_error {
|
const api_file &file) -> api_error {
|
||||||
REPERTORY_USES_FUNCTION_NAME();
|
REPERTORY_USES_FUNCTION_NAME();
|
||||||
|
|
||||||
auto meta = create_meta_attributes(
|
const auto meta = create_meta_attributes(
|
||||||
file.accessed_date,
|
file.accessed_date,
|
||||||
directory ? FILE_ATTRIBUTE_DIRECTORY : FILE_ATTRIBUTE_ARCHIVE,
|
directory ? FILE_ATTRIBUTE_DIRECTORY : FILE_ATTRIBUTE_ARCHIVE,
|
||||||
file.changed_date, file.creation_date, directory, getgid(), file.key,
|
file.changed_date, file.creation_date, directory, getgid(), file.key,
|
||||||
directory ? S_IFDIR | S_IRUSR | S_IWUSR | S_IXUSR
|
directory ? S_IFDIR | S_IRUSR | S_IWUSR | S_IXUSR
|
||||||
: S_IFREG | S_IRUSR | S_IWUSR,
|
: 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);
|
file.modified_date);
|
||||||
auto res = provider.set_item_meta(file.api_path, meta);
|
auto res = provider.set_item_meta(file.api_path, meta);
|
||||||
if (res == api_error::success) {
|
if (res == api_error::success) {
|
||||||
|
@ -21,171 +21,150 @@
|
|||||||
*/
|
*/
|
||||||
#if defined(_WIN32)
|
#if defined(_WIN32)
|
||||||
|
|
||||||
#include "platform/platform.hpp"
|
#include "platform/win32_platform.hpp"
|
||||||
|
|
||||||
#include "events/event_system.hpp"
|
#include "events/event_system.hpp"
|
||||||
#include "events/types/filesystem_item_added.hpp"
|
#include "events/types/filesystem_item_added.hpp"
|
||||||
#include "providers/i_provider.hpp"
|
#include "providers/i_provider.hpp"
|
||||||
#include "utils/config.hpp"
|
|
||||||
#include "utils/error_utils.hpp"
|
#include "utils/error_utils.hpp"
|
||||||
#include "utils/string.hpp"
|
#include "utils/string.hpp"
|
||||||
|
|
||||||
namespace repertory {
|
namespace repertory {
|
||||||
lock_data::lock_data(provider_type prov, std::string unique_id)
|
auto lock_data::get_mount_state(const provider_type & /*pt*/, json &mount_state)
|
||||||
: mutex_id_(create_lock_id(prov, unique_id)),
|
-> bool {
|
||||||
mutex_handle_(::CreateMutex(nullptr, FALSE,
|
const auto ret = get_mount_state(mount_state);
|
||||||
create_lock_id(prov, unique_id).c_str())) {}
|
if (ret) {
|
||||||
|
const auto mount_id =
|
||||||
lock_data::~lock_data() { release(); }
|
app_config::get_provider_display_name(pt_) + unique_id_;
|
||||||
|
mount_state = mount_state[mount_id].empty()
|
||||||
auto lock_data::get_current_mount_state(json &mount_state) -> bool {
|
? json({{"Active", false}, {"Location", ""}, {"PID", -1}})
|
||||||
REPERTORY_USES_FUNCTION_NAME();
|
: mount_state[mount_id];
|
||||||
|
|
||||||
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;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto lock_data::get_mount_state(json &mount_state) -> bool {
|
auto lock_data::get_mount_state(json &mount_state) -> bool {
|
||||||
if (not get_current_mount_state(mount_state)) {
|
HKEY key;
|
||||||
return false;
|
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({
|
::RegCloseKey(key);
|
||||||
{"Active", false},
|
}
|
||||||
{"Location", ""},
|
return ret;
|
||||||
{"PID", -1},
|
|
||||||
})
|
|
||||||
: mount_state;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto lock_data::grab_lock(std::uint8_t retry_count) -> lock_result {
|
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) {
|
if (mutex_handle_ == INVALID_HANDLE_VALUE) {
|
||||||
return lock_result::failure;
|
ret = lock_result::failure;
|
||||||
}
|
} else {
|
||||||
|
for (auto i = 0;
|
||||||
for (std::uint8_t idx = 0U;
|
(i <= retry_count) && ((mutex_state_ = ::WaitForSingleObject(
|
||||||
(idx <= retry_count) &&
|
mutex_handle_, 100)) == WAIT_TIMEOUT);
|
||||||
((mutex_state_ = ::WaitForSingleObject(mutex_handle_, max_sleep)) ==
|
i++) {
|
||||||
WAIT_TIMEOUT);
|
|
||||||
++idx) {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (mutex_state_) {
|
switch (mutex_state_) {
|
||||||
case WAIT_OBJECT_0:
|
case WAIT_OBJECT_0: {
|
||||||
return lock_result::success;
|
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:
|
case WAIT_TIMEOUT:
|
||||||
return lock_result::locked;
|
ret = lock_result::locked;
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return lock_result::failure;
|
ret = lock_result::failure;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void lock_data::release() {
|
void lock_data::release() {
|
||||||
if (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) || (mutex_state_ == WAIT_ABANDONED)) {
|
||||||
if (mutex_state_ == WAIT_OBJECT_0) {
|
|
||||||
[[maybe_unused]] auto success{set_mount_state(false, "", -1)};
|
|
||||||
}
|
|
||||||
|
|
||||||
::ReleaseMutex(mutex_handle_);
|
::ReleaseMutex(mutex_handle_);
|
||||||
}
|
}
|
||||||
|
|
||||||
::CloseHandle(mutex_handle_);
|
::CloseHandle(mutex_handle_);
|
||||||
mutex_handle_ = INVALID_HANDLE_VALUE;
|
mutex_handle_ = INVALID_HANDLE_VALUE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto lock_data::set_mount_state(bool active, std::string_view mount_location,
|
auto lock_data::set_mount_state(bool active, const std::string &mount_location,
|
||||||
std::int64_t pid) -> bool {
|
const std::int64_t &pid) -> bool {
|
||||||
if (mutex_handle_ == INVALID_HANDLE_VALUE) {
|
auto ret = false;
|
||||||
return false;
|
if (mutex_handle_ != INVALID_HANDLE_VALUE) {
|
||||||
}
|
const auto mount_id =
|
||||||
|
app_config::get_provider_display_name(pt_) + unique_id_;
|
||||||
json mount_state;
|
json mount_state;
|
||||||
[[maybe_unused]] auto success{get_mount_state(mount_state)};
|
[[maybe_unused]] auto success = get_mount_state(mount_state);
|
||||||
if (not((mount_state.find("Active") == mount_state.end()) ||
|
if ((mount_state.find(mount_id) == mount_state.end()) ||
|
||||||
(mount_state["Active"].get<bool>() != active) ||
|
(mount_state[mount_id].find("Active") == mount_state[mount_id].end()) ||
|
||||||
(active &&
|
(mount_state[mount_id]["Active"].get<bool>() != active) ||
|
||||||
((mount_state.find("Location") == mount_state.end()) ||
|
(active && ((mount_state[mount_id].find("Location") ==
|
||||||
(mount_state["Location"].get<std::string>() != mount_location))))) {
|
mount_state[mount_id].end()) ||
|
||||||
return true;
|
(mount_state[mount_id]["Location"].get<std::string>() !=
|
||||||
}
|
mount_location)))) {
|
||||||
|
HKEY key;
|
||||||
HKEY key{};
|
if ((ret = !::RegCreateKeyEx(
|
||||||
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,
|
HKEY_CURRENT_USER,
|
||||||
fmt::format(R"(SOFTWARE\{}\Mounts)", REPERTORY_DATA_NAME).c_str(),
|
("SOFTWARE\\" + std::string{REPERTORY_DATA_NAME} + "\\Mounts")
|
||||||
0, nullptr, 0, KEY_ALL_ACCESS, nullptr, &key,
|
.c_str(),
|
||||||
nullptr) != ERROR_SUCCESS) {
|
0, nullptr, 0, KEY_ALL_ACCESS, nullptr, &key, nullptr))) {
|
||||||
return false;
|
const auto str = json({{"Active", active},
|
||||||
}
|
|
||||||
|
|
||||||
ret = (::RegDeleteKeyA(key, mutex_id_.c_str()) == ERROR_SUCCESS);
|
|
||||||
} else {
|
|
||||||
auto data{
|
|
||||||
json({
|
|
||||||
{"Active", active},
|
|
||||||
{"Location", active ? mount_location : ""},
|
{"Location", active ? mount_location : ""},
|
||||||
{"PID", active ? pid : -1},
|
{"PID", active ? pid : -1}})
|
||||||
})
|
.dump(0);
|
||||||
.dump(),
|
ret = !::RegSetValueEx(key, &mount_id[0], 0, REG_SZ,
|
||||||
};
|
reinterpret_cast<const BYTE *>(&str[0]),
|
||||||
ret = (::RegSetValueEx(key, nullptr, 0, REG_SZ,
|
static_cast<DWORD>(str.size()));
|
||||||
reinterpret_cast<const BYTE *>(data.c_str()),
|
::RegCloseKey(key);
|
||||||
static_cast<DWORD>(data.size())) == ERROR_SUCCESS);
|
}
|
||||||
|
} else {
|
||||||
|
ret = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
::RegCloseKey(key);
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -236,4 +215,4 @@ auto provider_meta_handler(i_provider &provider, bool directory,
|
|||||||
}
|
}
|
||||||
} // namespace repertory
|
} // namespace repertory
|
||||||
|
|
||||||
#endif // defined(_WIN32)
|
#endif //_WIN32
|
||||||
|
@ -29,7 +29,9 @@
|
|||||||
#include "events/types/service_stop_end.hpp"
|
#include "events/types/service_stop_end.hpp"
|
||||||
#include "events/types/unmount_requested.hpp"
|
#include "events/types/unmount_requested.hpp"
|
||||||
#include "rpc/common.hpp"
|
#include "rpc/common.hpp"
|
||||||
|
#include "utils/base64.hpp"
|
||||||
#include "utils/error_utils.hpp"
|
#include "utils/error_utils.hpp"
|
||||||
|
#include "utils/string.hpp"
|
||||||
|
|
||||||
namespace repertory {
|
namespace repertory {
|
||||||
server::server(app_config &config) : config_(config) {}
|
server::server(app_config &config) : config_(config) {}
|
||||||
@ -142,17 +144,13 @@ void server::start() {
|
|||||||
initialize(*server_);
|
initialize(*server_);
|
||||||
|
|
||||||
server_thread_ = std::make_unique<std::thread>([this]() {
|
server_thread_ = std::make_unique<std::thread>([this]() {
|
||||||
|
#ifdef _WIN32
|
||||||
server_->set_socket_options([](auto &&sock) {
|
server_->set_socket_options([](auto &&sock) {
|
||||||
#if defined(_WIN32)
|
int enable = 1;
|
||||||
int enable{1};
|
|
||||||
setsockopt(sock, SOL_SOCKET, SO_EXCLUSIVEADDRUSE,
|
setsockopt(sock, SOL_SOCKET, SO_EXCLUSIVEADDRUSE,
|
||||||
reinterpret_cast<const char *>(&enable), sizeof(enable));
|
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)
|
|
||||||
});
|
});
|
||||||
|
#endif // _WIN32
|
||||||
|
|
||||||
server_->listen("127.0.0.1", config_.get_api_port());
|
server_->listen("127.0.0.1", config_.get_api_port());
|
||||||
});
|
});
|
||||||
|
@ -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)->std::string {
|
|
||||||
return fmt::format("{}_{}_{}", REPERTORY_DATA_NAME,
|
|
||||||
app_config::get_provider_name(prov), unique_id);
|
|
||||||
}
|
|
||||||
} // namespace repertory
|
|
@ -28,13 +28,13 @@
|
|||||||
#include "providers/provider.hpp"
|
#include "providers/provider.hpp"
|
||||||
#include "types/repertory.hpp"
|
#include "types/repertory.hpp"
|
||||||
#include "utils/cli_utils.hpp"
|
#include "utils/cli_utils.hpp"
|
||||||
|
#include "utils/com_init_wrapper.hpp"
|
||||||
#include "utils/file.hpp"
|
#include "utils/file.hpp"
|
||||||
|
|
||||||
#if defined(_WIN32)
|
#if defined(_WIN32)
|
||||||
#include "drives/winfsp/remotewinfsp/remote_client.hpp"
|
#include "drives/winfsp/remotewinfsp/remote_client.hpp"
|
||||||
#include "drives/winfsp/remotewinfsp/remote_winfsp_drive.hpp"
|
#include "drives/winfsp/remotewinfsp/remote_winfsp_drive.hpp"
|
||||||
#include "drives/winfsp/winfsp_drive.hpp"
|
#include "drives/winfsp/winfsp_drive.hpp"
|
||||||
#include "utils/com_init_wrapper.hpp"
|
|
||||||
|
|
||||||
using repertory_drive = repertory::winfsp_drive;
|
using repertory_drive = repertory::winfsp_drive;
|
||||||
using remote_client = repertory::remote_winfsp::remote_client;
|
using remote_client = repertory::remote_winfsp::remote_client;
|
||||||
@ -56,15 +56,6 @@ namespace repertory::cli::actions {
|
|||||||
mount(std::vector<const char *> args, std::string data_directory,
|
mount(std::vector<const char *> args, std::string data_directory,
|
||||||
int &mount_result, provider_type prov, const std::string &remote_host,
|
int &mount_result, provider_type prov, const std::string &remote_host,
|
||||||
std::uint16_t remote_port, const std::string &unique_id) -> exit_code {
|
std::uint16_t remote_port, const std::string &unique_id) -> exit_code {
|
||||||
lock_data global_lock(provider_type::unknown, "global");
|
|
||||||
{
|
|
||||||
auto lock_result = global_lock.grab_lock(100U);
|
|
||||||
if (lock_result != lock_result::success) {
|
|
||||||
std::cerr << "FATAL: Unable to get global lock" << std::endl;
|
|
||||||
return exit_code::lock_failed;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
lock_data lock(prov, unique_id);
|
lock_data lock(prov, unique_id);
|
||||||
auto lock_result = lock.grab_lock();
|
auto lock_result = lock.grab_lock();
|
||||||
if (lock_result == lock_result::locked) {
|
if (lock_result == lock_result::locked) {
|
||||||
@ -106,6 +97,13 @@ mount(std::vector<const char *> args, std::string data_directory,
|
|||||||
}
|
}
|
||||||
#endif // defined(_WIN32)
|
#endif // defined(_WIN32)
|
||||||
|
|
||||||
|
lock_data global_lock(provider_type::unknown, "global");
|
||||||
|
lock_result = global_lock.grab_lock(100U);
|
||||||
|
if (lock_result != lock_result::success) {
|
||||||
|
std::cerr << "FATAL: Unable to get global lock" << std::endl;
|
||||||
|
return exit_code::lock_failed;
|
||||||
|
}
|
||||||
|
|
||||||
auto drive_args = utils::cli::parse_drive_options(args, prov, data_directory);
|
auto drive_args = utils::cli::parse_drive_options(args, prov, data_directory);
|
||||||
app_config config(prov, data_directory);
|
app_config config(prov, data_directory);
|
||||||
{
|
{
|
||||||
|
@ -74,7 +74,7 @@ private:
|
|||||||
[[nodiscard]] auto data_directory_exists(provider_type prov,
|
[[nodiscard]] auto data_directory_exists(provider_type prov,
|
||||||
std::string_view name) const -> bool;
|
std::string_view name) const -> bool;
|
||||||
|
|
||||||
static void handle_get_available_locations(httplib::Response &res);
|
void handle_get_available_locations(httplib::Response &res) const;
|
||||||
|
|
||||||
void handle_get_mount(const httplib::Request &req,
|
void handle_get_mount(const httplib::Request &req,
|
||||||
httplib::Response &res) const;
|
httplib::Response &res) const;
|
||||||
@ -96,9 +96,6 @@ private:
|
|||||||
|
|
||||||
void handle_post_mount(const httplib::Request &req, httplib::Response &res);
|
void handle_post_mount(const httplib::Request &req, httplib::Response &res);
|
||||||
|
|
||||||
void handle_put_mount_location(const httplib::Request &req,
|
|
||||||
httplib::Response &res) const;
|
|
||||||
|
|
||||||
void handle_put_set_value_by_name(const httplib::Request &req,
|
void handle_put_set_value_by_name(const httplib::Request &req,
|
||||||
httplib::Response &res) const;
|
httplib::Response &res) const;
|
||||||
|
|
||||||
|
@ -109,17 +109,13 @@ handlers::handlers(mgmt_app_config *config, httplib::Server *server)
|
|||||||
server_(server) {
|
server_(server) {
|
||||||
REPERTORY_USES_FUNCTION_NAME();
|
REPERTORY_USES_FUNCTION_NAME();
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
server_->set_socket_options([](auto &&sock) {
|
server_->set_socket_options([](auto &&sock) {
|
||||||
#if defined(_WIN32)
|
int enable = 1;
|
||||||
int enable{1};
|
|
||||||
setsockopt(sock, SOL_SOCKET, SO_EXCLUSIVEADDRUSE,
|
setsockopt(sock, SOL_SOCKET, SO_EXCLUSIVEADDRUSE,
|
||||||
reinterpret_cast<const char *>(&enable), sizeof(enable));
|
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)
|
|
||||||
});
|
});
|
||||||
|
#endif // _WIN32
|
||||||
|
|
||||||
server_->set_pre_routing_handler(
|
server_->set_pre_routing_handler(
|
||||||
[this](const httplib::Request &req,
|
[this](const httplib::Request &req,
|
||||||
@ -172,7 +168,7 @@ handlers::handlers(mgmt_app_config *config, httplib::Server *server)
|
|||||||
: http_error_codes::internal_error;
|
: http_error_codes::internal_error;
|
||||||
});
|
});
|
||||||
|
|
||||||
server->Get("/api/v1/locations", [](auto && /* req */, auto &&res) {
|
server->Get("/api/v1/locations", [this](auto && /* req */, auto &&res) {
|
||||||
handle_get_available_locations(res);
|
handle_get_available_locations(res);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -205,10 +201,6 @@ handlers::handlers(mgmt_app_config *config, httplib::Server *server)
|
|||||||
server->Post("/api/v1/mount",
|
server->Post("/api/v1/mount",
|
||||||
[this](auto &&req, auto &&res) { handle_post_mount(req, res); });
|
[this](auto &&req, auto &&res) { handle_post_mount(req, res); });
|
||||||
|
|
||||||
server->Put("/api/v1/mount_location", [this](auto &&req, auto &&res) {
|
|
||||||
handle_put_mount_location(req, res);
|
|
||||||
});
|
|
||||||
|
|
||||||
server->Put("/api/v1/set_value_by_name", [this](auto &&req, auto &&res) {
|
server->Put("/api/v1/set_value_by_name", [this](auto &&req, auto &&res) {
|
||||||
handle_put_set_value_by_name(req, res);
|
handle_put_set_value_by_name(req, res);
|
||||||
});
|
});
|
||||||
@ -306,24 +298,7 @@ auto handlers::data_directory_exists(provider_type prov,
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void handlers::handle_put_mount_location(const httplib::Request &req,
|
void handlers::handle_get_available_locations(httplib::Response &res) const {
|
||||||
httplib::Response &res) const {
|
|
||||||
REPERTORY_USES_FUNCTION_NAME();
|
|
||||||
|
|
||||||
auto prov = provider_type_from_string(req.get_param_value("type"));
|
|
||||||
auto name = req.get_param_value("name");
|
|
||||||
auto location = req.get_param_value("location");
|
|
||||||
|
|
||||||
if (not data_directory_exists(prov, name)) {
|
|
||||||
res.status = http_error_codes::not_found;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
config_->set_mount_location(prov, name, location);
|
|
||||||
res.status = http_error_codes::ok;
|
|
||||||
}
|
|
||||||
|
|
||||||
void handlers::handle_get_available_locations(httplib::Response &res) {
|
|
||||||
#if defined(_WIN32)
|
#if defined(_WIN32)
|
||||||
constexpr const std::array<std::string_view, 26U> letters{
|
constexpr const std::array<std::string_view, 26U> letters{
|
||||||
"A:", "B:", "C:", "D:", "E:", "F:", "G:", "H:", "I:",
|
"A:", "B:", "C:", "D:", "E:", "F:", "G:", "H:", "I:",
|
||||||
@ -432,6 +407,8 @@ void handlers::handle_get_mount_location(const httplib::Request &req,
|
|||||||
|
|
||||||
void handlers::handle_get_mount_status(const httplib::Request &req,
|
void handlers::handle_get_mount_status(const httplib::Request &req,
|
||||||
httplib::Response &res) const {
|
httplib::Response &res) const {
|
||||||
|
REPERTORY_USES_FUNCTION_NAME();
|
||||||
|
|
||||||
auto name = req.get_param_value("name");
|
auto name = req.get_param_value("name");
|
||||||
auto prov = provider_type_from_string(req.get_param_value("type"));
|
auto prov = provider_type_from_string(req.get_param_value("type"));
|
||||||
|
|
||||||
@ -440,9 +417,34 @@ void handlers::handle_get_mount_status(const httplib::Request &req,
|
|||||||
return;
|
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 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()) {
|
if (result.at("Location").get<std::string>().empty()) {
|
||||||
result.at("Location") = config_->get_mount_location(prov, name);
|
result.at("Location") = config_->get_mount_location(prov, name);
|
||||||
} else if (result.at("Active").get<bool>()) {
|
} else if (result.at("Active").get<bool>()) {
|
||||||
@ -488,14 +490,14 @@ void handlers::handle_post_add_mount(const httplib::Request &req,
|
|||||||
for (const auto &[key, value] : cfg.items()) {
|
for (const auto &[key, value] : cfg.items()) {
|
||||||
if (value.is_object()) {
|
if (value.is_object()) {
|
||||||
for (const auto &[key2, value2] : value.items()) {
|
for (const auto &[key2, value2] : value.items()) {
|
||||||
auto sub_key = fmt::format("{}.{}", key, key2);
|
auto subKey = fmt::format("{}.{}", key, key2);
|
||||||
auto skip{false};
|
auto skip{false};
|
||||||
auto decrypted = decrypt_value(
|
auto decrypted = decrypt_value(
|
||||||
config_, sub_key, value2.template get<std::string>(), skip);
|
config_, subKey, value2.template get<std::string>(), skip);
|
||||||
if (skip) {
|
if (skip) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
values[sub_key] = decrypted;
|
values[subKey] = decrypted;
|
||||||
}
|
}
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
@ -544,12 +546,12 @@ void handlers::handle_post_mount(const httplib::Request &req,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
config_->set_mount_location(prov, name, location);
|
|
||||||
|
|
||||||
static std::mutex mount_mtx;
|
static std::mutex mount_mtx;
|
||||||
mutex_lock lock(mount_mtx);
|
mutex_lock lock(mount_mtx);
|
||||||
|
|
||||||
launch_process(prov, name, {location}, true);
|
launch_process(prov, name, {location}, true);
|
||||||
|
config_->set_mount_location(prov, name, location);
|
||||||
|
|
||||||
launch_process(prov, name, {"-status"});
|
launch_process(prov, name, {"-status"});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -580,7 +582,7 @@ void handlers::handle_put_set_value_by_name(const httplib::Request &req,
|
|||||||
|
|
||||||
void handlers::handle_put_settings(const httplib::Request &req,
|
void handlers::handle_put_settings(const httplib::Request &req,
|
||||||
httplib::Response &res) const {
|
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)) {
|
if (data.contains(JSON_API_PASSWORD)) {
|
||||||
auto password = decrypt(data.at(JSON_API_PASSWORD).get<std::string>(),
|
auto password = decrypt(data.at(JSON_API_PASSWORD).get<std::string>(),
|
||||||
@ -659,8 +661,10 @@ auto handlers::launch_process(provider_type prov, std::string_view name,
|
|||||||
args.insert(std::next(args.begin(), 3U), "");
|
args.insert(std::next(args.begin(), 3U), "");
|
||||||
args.insert(std::next(args.begin(), 4U), "/MIN");
|
args.insert(std::next(args.begin(), 4U), "/MIN");
|
||||||
args.insert(std::next(args.begin(), 5U), repertory_binary_);
|
args.insert(std::next(args.begin(), 5U), repertory_binary_);
|
||||||
#else // !defined(_WIN32)
|
#elif defined(__linux__) // defined(__linux__)
|
||||||
args.insert(args.begin(), repertory_binary_);
|
args.insert(args.begin(), repertory_binary_);
|
||||||
|
#else // !defined(__linux__) && !defined(_WIN32)
|
||||||
|
build fails here
|
||||||
#endif // defined(_WIN32)
|
#endif // defined(_WIN32)
|
||||||
|
|
||||||
std::vector<const char *> exec_args;
|
std::vector<const char *> exec_args;
|
||||||
@ -673,9 +677,16 @@ auto handlers::launch_process(provider_type prov, std::string_view name,
|
|||||||
#if defined(_WIN32)
|
#if defined(_WIN32)
|
||||||
_spawnv(_P_DETACH, exec_args.at(0U),
|
_spawnv(_P_DETACH, exec_args.at(0U),
|
||||||
const_cast<char *const *>(exec_args.data()));
|
const_cast<char *const *>(exec_args.data()));
|
||||||
#else // !defined(_WIN32)
|
#elif defined(__linux__) // defined(__linux__)
|
||||||
auto pid = fork();
|
auto pid = fork();
|
||||||
if (pid == 0) {
|
if (pid < 0) {
|
||||||
|
throw utils::error::create_exception(function_name, {"mount failed"});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pid != 0) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
setsid();
|
setsid();
|
||||||
chdir("/");
|
chdir("/");
|
||||||
close(STDIN_FILENO);
|
close(STDIN_FILENO);
|
||||||
@ -685,10 +696,10 @@ auto handlers::launch_process(provider_type prov, std::string_view name,
|
|||||||
open("/dev/null", O_WRONLY);
|
open("/dev/null", O_WRONLY);
|
||||||
open("/dev/null", O_WRONLY);
|
open("/dev/null", O_WRONLY);
|
||||||
|
|
||||||
execvp(exec_args.at(0U), const_cast<char *const *>(exec_args.data()));
|
|
||||||
} else {
|
|
||||||
signal(SIGCHLD, SIG_IGN);
|
signal(SIGCHLD, SIG_IGN);
|
||||||
}
|
execvp(exec_args.at(0U), const_cast<char *const *>(exec_args.data()));
|
||||||
|
#else // !defined(__linux__) && !defined(_WIN32)
|
||||||
|
build fails here
|
||||||
#endif // defined(_WIN32)
|
#endif // defined(_WIN32)
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
@ -62,16 +62,13 @@ TEST(lock_data_test, set_and_unset_mount_state) {
|
|||||||
|
|
||||||
json mount_state;
|
json mount_state;
|
||||||
EXPECT_TRUE(l.get_mount_state(mount_state));
|
EXPECT_TRUE(l.get_mount_state(mount_state));
|
||||||
|
|
||||||
EXPECT_STREQ(R"({"Active":true,"Location":"C:","PID":99})",
|
EXPECT_STREQ(R"({"Active":true,"Location":"C:","PID":99})",
|
||||||
mount_state.dump().c_str());
|
mount_state["Sia1"].dump().c_str());
|
||||||
|
|
||||||
EXPECT_TRUE(l2.get_mount_state(mount_state));
|
|
||||||
EXPECT_STREQ(R"({"Active":true,"Location":"D:","PID":97})",
|
EXPECT_STREQ(R"({"Active":true,"Location":"D:","PID":97})",
|
||||||
mount_state.dump().c_str());
|
mount_state["Remote1"].dump().c_str());
|
||||||
|
|
||||||
EXPECT_TRUE(l3.get_mount_state(mount_state));
|
|
||||||
EXPECT_STREQ(R"({"Active":true,"Location":"E:","PID":96})",
|
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(l.set_mount_state(false, "C:", 99));
|
||||||
EXPECT_TRUE(l2.set_mount_state(false, "D:", 98));
|
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_TRUE(l.get_mount_state(mount_state));
|
||||||
EXPECT_STREQ(R"({"Active":false,"Location":"","PID":-1})",
|
EXPECT_STREQ(R"({"Active":false,"Location":"","PID":-1})",
|
||||||
mount_state.dump().c_str());
|
mount_state["Sia1"].dump().c_str());
|
||||||
|
|
||||||
EXPECT_TRUE(l2.get_mount_state(mount_state));
|
|
||||||
EXPECT_STREQ(R"({"Active":false,"Location":"","PID":-1})",
|
EXPECT_STREQ(R"({"Active":false,"Location":"","PID":-1})",
|
||||||
mount_state.dump().c_str());
|
mount_state["Remote1"].dump().c_str());
|
||||||
|
|
||||||
EXPECT_TRUE(l3.get_mount_state(mount_state));
|
|
||||||
EXPECT_STREQ(R"({"Active":false,"Location":"","PID":-1})",
|
EXPECT_STREQ(R"({"Active":false,"Location":"","PID":-1})",
|
||||||
mount_state.dump().c_str());
|
mount_state["Remote2"].dump().c_str());
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
TEST(lock_data_test, set_and_unset_mount_state) {
|
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_TRUE(l.get_mount_state(mount_state));
|
||||||
|
|
||||||
EXPECT_STREQ(R"({"Active":true,"Location":"/mnt/1","PID":99})",
|
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.set_mount_state(false, "/mnt/1", 99));
|
||||||
|
|
||||||
EXPECT_TRUE(l.get_mount_state(mount_state));
|
EXPECT_TRUE(l.get_mount_state(mount_state));
|
||||||
|
|
||||||
EXPECT_STREQ(R"({"Active":false,"Location":"","PID":-1})",
|
EXPECT_STREQ(R"({"Active":false,"Location":"","PID":-1})",
|
||||||
mount_state.dump().c_str());
|
mount_state["Sia1"].dump().c_str());
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
} // namespace repertory
|
} // namespace repertory
|
||||||
|
@ -25,8 +25,8 @@
|
|||||||
#include "utils/string.hpp"
|
#include "utils/string.hpp"
|
||||||
|
|
||||||
namespace repertory::utils {
|
namespace repertory::utils {
|
||||||
auto compare_version_strings(std::string version1, std::string version2)
|
auto compare_version_strings(std::string version1,
|
||||||
-> std::int32_t {
|
std::string version2) -> std::int32_t {
|
||||||
|
|
||||||
if (utils::string::contains(version1, "-")) {
|
if (utils::string::contains(version1, "-")) {
|
||||||
version1 = utils::string::split(version1, '-', true)[0U];
|
version1 = utils::string::split(version1, '-', true)[0U];
|
||||||
@ -157,7 +157,7 @@ auto get_next_available_port(std::uint16_t first_port,
|
|||||||
++check_port;
|
++check_port;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
acceptor.set_option(boost::asio::ip::tcp::acceptor::linger(true, 0));
|
|
||||||
acceptor.bind({tcp::v4(), static_cast<std::uint16_t>(check_port)},
|
acceptor.bind({tcp::v4(), static_cast<std::uint16_t>(check_port)},
|
||||||
error_code);
|
error_code);
|
||||||
if (error_code) {
|
if (error_code) {
|
||||||
|
@ -3,8 +3,7 @@ import 'package:collection/collection.dart';
|
|||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:repertory/constants.dart' as constants;
|
import 'package:repertory/constants.dart' as constants;
|
||||||
import 'package:repertory/models/auth.dart';
|
import 'package:sodium_libs/sodium_libs.dart';
|
||||||
import 'package:sodium_libs/sodium_libs.dart' show SecureKey, StringX;
|
|
||||||
|
|
||||||
typedef Validator = bool Function(String);
|
typedef Validator = bool Function(String);
|
||||||
|
|
||||||
@ -107,31 +106,25 @@ Map<String, dynamic> createDefaultSettings(String mountType) {
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
void displayAuthError(Auth auth) {
|
void displayAuthError() {
|
||||||
if (!auth.authenticated || constants.navigatorKey.currentContext == null) {
|
if (constants.navigatorKey.currentContext == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
displayErrorMessage(
|
displayErrorMessage(
|
||||||
constants.navigatorKey.currentContext!,
|
constants.navigatorKey.currentContext!,
|
||||||
"Authentication failed",
|
"Authentication failed",
|
||||||
clear: true,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void displayErrorMessage(context, String text, {bool clear = false}) {
|
void displayErrorMessage(context, String text) {
|
||||||
if (!context.mounted) {
|
if (!context.mounted) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final messenger = ScaffoldMessenger.of(context);
|
ScaffoldMessenger.of(
|
||||||
if (clear) {
|
context,
|
||||||
messenger.removeCurrentSnackBar();
|
).showSnackBar(SnackBar(content: Text(text, textAlign: TextAlign.center)));
|
||||||
}
|
|
||||||
|
|
||||||
messenger.showSnackBar(
|
|
||||||
SnackBar(content: Text(text, textAlign: TextAlign.center)),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
String formatMountName(String type, String name) {
|
String formatMountName(String type, String name) {
|
||||||
@ -336,67 +329,3 @@ Map<String, dynamic> getChanged(
|
|||||||
|
|
||||||
return changed;
|
return changed;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<String?> editMountLocation(
|
|
||||||
context,
|
|
||||||
List<String> available, {
|
|
||||||
bool allowEmpty = false,
|
|
||||||
String? location,
|
|
||||||
}) async {
|
|
||||||
String? currentLocation = location;
|
|
||||||
final controller = TextEditingController(text: currentLocation);
|
|
||||||
return await showDialog(
|
|
||||||
context: context,
|
|
||||||
builder: (context) {
|
|
||||||
return StatefulBuilder(
|
|
||||||
builder: (context, setState) {
|
|
||||||
return AlertDialog(
|
|
||||||
actions: [
|
|
||||||
TextButton(
|
|
||||||
child: const Text('Cancel'),
|
|
||||||
onPressed: () => Navigator.of(context).pop(null),
|
|
||||||
),
|
|
||||||
TextButton(
|
|
||||||
child: const Text('OK'),
|
|
||||||
onPressed: () {
|
|
||||||
final result = getSettingValidators('Path').firstWhereOrNull(
|
|
||||||
(validator) => !validator(currentLocation ?? ''),
|
|
||||||
);
|
|
||||||
if (result != null) {
|
|
||||||
return displayErrorMessage(
|
|
||||||
context,
|
|
||||||
"Mount location is not valid",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
Navigator.of(context).pop(currentLocation);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
content:
|
|
||||||
available.isEmpty
|
|
||||||
? TextField(
|
|
||||||
autofocus: true,
|
|
||||||
controller: controller,
|
|
||||||
onChanged:
|
|
||||||
(value) => setState(() => currentLocation = value),
|
|
||||||
)
|
|
||||||
: DropdownButton<String>(
|
|
||||||
hint: const Text("Select drive"),
|
|
||||||
value: currentLocation,
|
|
||||||
onChanged:
|
|
||||||
(value) => setState(() => currentLocation = value),
|
|
||||||
items:
|
|
||||||
available.map<DropdownMenuItem<String>>((item) {
|
|
||||||
return DropdownMenuItem<String>(
|
|
||||||
value: item,
|
|
||||||
child: Text(item),
|
|
||||||
);
|
|
||||||
}).toList(),
|
|
||||||
),
|
|
||||||
title: const Text('Mount Location', textAlign: TextAlign.center),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
@ -54,11 +54,9 @@ class Auth with ChangeNotifier {
|
|||||||
|
|
||||||
void logoff() {
|
void logoff() {
|
||||||
_authenticated = false;
|
_authenticated = false;
|
||||||
_key = SecureKey.random(constants.sodium, 32);
|
|
||||||
_user = "";
|
_user = "";
|
||||||
|
mountList?.clear();
|
||||||
|
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
|
|
||||||
mountList?.clear();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -101,35 +101,6 @@ class Mount with ChangeNotifier {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> setMountLocation(String location) async {
|
|
||||||
try {
|
|
||||||
mountConfig.path = location;
|
|
||||||
|
|
||||||
final auth = await _auth.createAuth();
|
|
||||||
final response = await http.put(
|
|
||||||
Uri.parse(
|
|
||||||
Uri.encodeFull(
|
|
||||||
'${getBaseUri()}/api/v1/mount_location?auth=$auth&name=$name&type=$type&location=$location',
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
if (response.statusCode == 401) {
|
|
||||||
_auth.logoff();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (response.statusCode == 404) {
|
|
||||||
_mountList?.reset();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
return refresh();
|
|
||||||
} catch (e) {
|
|
||||||
debugPrint('$e');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<List<String>> getAvailableLocations() async {
|
Future<List<String>> getAvailableLocations() async {
|
||||||
try {
|
try {
|
||||||
final auth = await _auth.createAuth();
|
final auth = await _auth.createAuth();
|
||||||
@ -207,7 +178,7 @@ class Mount with ChangeNotifier {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (response.statusCode == 401) {
|
if (response.statusCode == 401) {
|
||||||
displayAuthError(_auth);
|
displayAuthError();
|
||||||
_auth.logoff();
|
_auth.logoff();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -63,7 +63,7 @@ class MountList with ChangeNotifier {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (response.statusCode == 401) {
|
if (response.statusCode == 401) {
|
||||||
displayAuthError(_auth);
|
displayAuthError();
|
||||||
_auth.logoff();
|
_auth.logoff();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -155,7 +155,7 @@ class MountList with ChangeNotifier {
|
|||||||
ret = true;
|
ret = true;
|
||||||
break;
|
break;
|
||||||
case 401:
|
case 401:
|
||||||
displayAuthError(_auth);
|
displayAuthError();
|
||||||
_auth.logoff();
|
_auth.logoff();
|
||||||
break;
|
break;
|
||||||
case 404:
|
case 404:
|
||||||
|
@ -38,19 +38,6 @@ class _AuthScreenState extends State<AuthScreen> {
|
|||||||
return SizedBox.shrink();
|
return SizedBox.shrink();
|
||||||
}
|
}
|
||||||
|
|
||||||
createLoginHandler() {
|
|
||||||
return _enabled
|
|
||||||
? () async {
|
|
||||||
setState(() => _enabled = false);
|
|
||||||
await auth.authenticate(
|
|
||||||
_userController.text,
|
|
||||||
_passwordController.text,
|
|
||||||
);
|
|
||||||
setState(() => _enabled = true);
|
|
||||||
}
|
|
||||||
: null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Center(
|
return Center(
|
||||||
child: Card(
|
child: Card(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
@ -72,26 +59,26 @@ class _AuthScreenState extends State<AuthScreen> {
|
|||||||
autofocus: true,
|
autofocus: true,
|
||||||
decoration: InputDecoration(labelText: 'Username'),
|
decoration: InputDecoration(labelText: 'Username'),
|
||||||
controller: _userController,
|
controller: _userController,
|
||||||
textInputAction: TextInputAction.next,
|
|
||||||
),
|
),
|
||||||
const SizedBox(height: constants.padding),
|
const SizedBox(height: constants.padding),
|
||||||
TextField(
|
TextField(
|
||||||
obscureText: true,
|
obscureText: true,
|
||||||
decoration: InputDecoration(labelText: 'Password'),
|
decoration: InputDecoration(labelText: 'Password'),
|
||||||
controller: _passwordController,
|
controller: _passwordController,
|
||||||
textInputAction: TextInputAction.go,
|
|
||||||
onSubmitted: (_) {
|
|
||||||
final handler = createLoginHandler();
|
|
||||||
if (handler == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
handler();
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
const SizedBox(height: constants.padding),
|
const SizedBox(height: constants.padding),
|
||||||
ElevatedButton(
|
ElevatedButton(
|
||||||
onPressed: createLoginHandler(),
|
onPressed:
|
||||||
|
_enabled
|
||||||
|
? () async {
|
||||||
|
setState(() => _enabled = false);
|
||||||
|
await auth.authenticate(
|
||||||
|
_userController.text,
|
||||||
|
_passwordController.text,
|
||||||
|
);
|
||||||
|
setState(() => _enabled = true);
|
||||||
|
}
|
||||||
|
: null,
|
||||||
child: const Text('Login'),
|
child: const Text('Login'),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
@ -17,7 +17,6 @@ class MountWidget extends StatefulWidget {
|
|||||||
|
|
||||||
class _MountWidgetState extends State<MountWidget> {
|
class _MountWidgetState extends State<MountWidget> {
|
||||||
bool _enabled = true;
|
bool _enabled = true;
|
||||||
bool _editEnabled = true;
|
|
||||||
Timer? _timer;
|
Timer? _timer;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -62,33 +61,7 @@ class _MountWidgetState extends State<MountWidget> {
|
|||||||
mount.provider,
|
mount.provider,
|
||||||
style: TextStyle(color: textColor, fontWeight: FontWeight.bold),
|
style: TextStyle(color: textColor, fontWeight: FontWeight.bold),
|
||||||
),
|
),
|
||||||
trailing: Row(
|
trailing: IconButton(
|
||||||
mainAxisAlignment: MainAxisAlignment.end,
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
if (mount.mounted != null && !mount.mounted!)
|
|
||||||
IconButton(
|
|
||||||
icon: const Icon(Icons.edit),
|
|
||||||
color: subTextColor,
|
|
||||||
tooltip: 'Edit mount location',
|
|
||||||
onPressed: () async {
|
|
||||||
setState(() => _editEnabled = false);
|
|
||||||
final available = await mount.getAvailableLocations();
|
|
||||||
if (context.mounted) {
|
|
||||||
final location = await editMountLocation(
|
|
||||||
context,
|
|
||||||
available,
|
|
||||||
location: mount.path,
|
|
||||||
);
|
|
||||||
if (location != null) {
|
|
||||||
await mount.setMountLocation(location);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
setState(() => _editEnabled = true);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
IconButton(
|
|
||||||
icon: Icon(
|
icon: Icon(
|
||||||
mount.mounted == null
|
mount.mounted == null
|
||||||
? Icons.hourglass_top
|
? Icons.hourglass_top
|
||||||
@ -100,16 +73,8 @@ class _MountWidgetState extends State<MountWidget> {
|
|||||||
? Color.fromARGB(255, 163, 96, 76)
|
? Color.fromARGB(255, 163, 96, 76)
|
||||||
: subTextColor,
|
: subTextColor,
|
||||||
),
|
),
|
||||||
tooltip:
|
|
||||||
mount.mounted == null
|
|
||||||
? ''
|
|
||||||
: mount.mounted!
|
|
||||||
? 'Unmount'
|
|
||||||
: 'Mount',
|
|
||||||
onPressed: _createMountHandler(context, mount),
|
onPressed: _createMountHandler(context, mount),
|
||||||
),
|
),
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@ -184,7 +149,70 @@ class _MountWidgetState extends State<MountWidget> {
|
|||||||
return location;
|
return location;
|
||||||
}
|
}
|
||||||
|
|
||||||
return editMountLocation(context, await mount.getAvailableLocations());
|
final available = await mount.getAvailableLocations();
|
||||||
|
|
||||||
|
String? currentLocation;
|
||||||
|
return await showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (context) {
|
||||||
|
return StatefulBuilder(
|
||||||
|
builder: (context, setState) {
|
||||||
|
return AlertDialog(
|
||||||
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
child: const Text('Cancel'),
|
||||||
|
onPressed: () => Navigator.of(context).pop(null),
|
||||||
|
),
|
||||||
|
TextButton(
|
||||||
|
child: const Text('OK'),
|
||||||
|
onPressed: () {
|
||||||
|
final result = getSettingValidators(
|
||||||
|
'Path',
|
||||||
|
).firstWhereOrNull(
|
||||||
|
(validator) => !validator(currentLocation ?? ''),
|
||||||
|
);
|
||||||
|
if (result != null) {
|
||||||
|
return displayErrorMessage(
|
||||||
|
context,
|
||||||
|
"Mount location is not valid",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Navigator.of(context).pop(currentLocation);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
content:
|
||||||
|
available.isEmpty
|
||||||
|
? TextField(
|
||||||
|
autofocus: true,
|
||||||
|
controller: TextEditingController(
|
||||||
|
text: currentLocation,
|
||||||
|
),
|
||||||
|
inputFormatters: [
|
||||||
|
FilteringTextInputFormatter.deny(RegExp(r'\s')),
|
||||||
|
],
|
||||||
|
onChanged:
|
||||||
|
(value) => setState(() => currentLocation = value),
|
||||||
|
)
|
||||||
|
: DropdownButton<String>(
|
||||||
|
hint: const Text("Select drive"),
|
||||||
|
value: currentLocation,
|
||||||
|
onChanged:
|
||||||
|
(value) => setState(() => currentLocation = value),
|
||||||
|
items:
|
||||||
|
available.map<DropdownMenuItem<String>>((item) {
|
||||||
|
return DropdownMenuItem<String>(
|
||||||
|
value: item,
|
||||||
|
child: Text(item),
|
||||||
|
);
|
||||||
|
}).toList(),
|
||||||
|
),
|
||||||
|
title: const Text('Set Mount Location'),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -106,6 +106,7 @@ class _UISettingsWidgetState extends State<UISettingsWidget> {
|
|||||||
void dispose() {
|
void dispose() {
|
||||||
final settings = getChanged(widget.origSettings, widget.settings);
|
final settings = getChanged(widget.origSettings, widget.settings);
|
||||||
if (settings.isNotEmpty) {
|
if (settings.isNotEmpty) {
|
||||||
|
debugPrint("start");
|
||||||
final key =
|
final key =
|
||||||
Provider.of<Auth>(
|
Provider.of<Auth>(
|
||||||
constants.navigatorKey.currentContext!,
|
constants.navigatorKey.currentContext!,
|
||||||
@ -113,13 +114,15 @@ class _UISettingsWidgetState extends State<UISettingsWidget> {
|
|||||||
).key;
|
).key;
|
||||||
convertAllToString(settings, key)
|
convertAllToString(settings, key)
|
||||||
.then((map) async {
|
.then((map) async {
|
||||||
|
debugPrint("map");
|
||||||
try {
|
try {
|
||||||
final authProvider = Provider.of<Auth>(
|
final authProvider = Provider.of<Auth>(
|
||||||
constants.navigatorKey.currentContext!,
|
constants.navigatorKey.currentContext!,
|
||||||
listen: false,
|
listen: false,
|
||||||
);
|
);
|
||||||
|
|
||||||
final auth = await authProvider.createAuth();
|
final auth = await authProvider.createAuth();
|
||||||
|
|
||||||
|
debugPrint("auth");
|
||||||
final response = await http.put(
|
final response = await http.put(
|
||||||
Uri.parse(
|
Uri.parse(
|
||||||
Uri.encodeFull(
|
Uri.encodeFull(
|
||||||
@ -129,7 +132,7 @@ class _UISettingsWidgetState extends State<UISettingsWidget> {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (response.statusCode == 401) {
|
if (response.statusCode == 401) {
|
||||||
displayAuthError(authProvider);
|
displayAuthError();
|
||||||
authProvider.logoff();
|
authProvider.logoff();
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
Reference in New Issue
Block a user