Add macOS support #34
Some checks failed
BlockStorage/repertory_mac/pipeline/head This commit looks good
BlockStorage/repertory/pipeline/head There was a failure building this commit

This commit is contained in:
2025-08-03 10:41:21 -05:00
parent eee832ce49
commit 29aaf625c7
16 changed files with 292 additions and 133 deletions

View File

@@ -88,11 +88,16 @@ auto fuse_drive::chown_impl(std::string api_path, uid_t uid, gid_t gid,
auto fuse_drive::chown_impl(std::string api_path, uid_t uid, gid_t gid) auto fuse_drive::chown_impl(std::string api_path, uid_t uid, gid_t gid)
-> api_error { -> api_error {
#endif #endif
REPERTORY_USES_FUNCTION_NAME();
return check_and_perform( return check_and_perform(
api_path, X_OK, [&](api_meta_map &meta) -> api_error { api_path, X_OK, [&](api_meta_map &meta) -> api_error {
meta.clear(); meta.clear();
if (uid != static_cast<uid_t>(-1)) { if (uid != static_cast<uid_t>(-1)) {
if (get_effective_uid() != 0 && get_effective_uid() != uid) { if (get_effective_uid() != 0 && get_effective_uid() != uid) {
utils::error::raise_error(
function_name, fmt::format("failed user|{}|{}",
get_effective_uid(), getuid()));
return api_error::permission_denied; return api_error::permission_denied;
} }
@@ -102,6 +107,9 @@ auto fuse_drive::chown_impl(std::string api_path, uid_t uid, gid_t gid)
if (gid != static_cast<gid_t>(-1)) { if (gid != static_cast<gid_t>(-1)) {
if (get_effective_uid() != 0 && if (get_effective_uid() != 0 &&
not utils::is_uid_member_of_group(get_effective_uid(), gid)) { not utils::is_uid_member_of_group(get_effective_uid(), gid)) {
utils::error::raise_error(
function_name, fmt::format("failed group|{}|{}",
get_effective_gid(), getgid()));
return api_error::permission_denied; return api_error::permission_denied;
} }

View File

@@ -36,20 +36,20 @@ auto client::get_drive_information() -> rpc_response {
auto resp = cli.Get("/api/v1/" + rpc_method::get_drive_information); auto resp = cli.Get("/api/v1/" + rpc_method::get_drive_information);
if (resp.error() != httplib::Error::Success) { if (resp.error() != httplib::Error::Success) {
return rpc_response{ return rpc_response{
rpc_response_type::http_error, .response_type = rpc_response_type::http_error,
{{"error", httplib::to_string(resp.error())}}, .data = {{"error", httplib::to_string(resp.error())}},
}; };
} }
if (resp->status != http_error_codes::ok) { if (resp->status != http_error_codes::ok) {
return rpc_response{ return rpc_response{
rpc_response_type::http_error, .response_type = rpc_response_type::http_error,
{{"error", std::to_string(resp->status)}}, .data = {{"error", std::to_string(resp->status)}},
}; };
} }
return rpc_response{ return rpc_response{
rpc_response_type::success, .response_type = rpc_response_type::success,
json::parse(resp->body), .data = json::parse(resp->body),
}; };
} }
@@ -63,20 +63,20 @@ auto client::get_config() -> rpc_response {
auto resp = cli.Get("/api/v1/" + rpc_method::get_config); auto resp = cli.Get("/api/v1/" + rpc_method::get_config);
if (resp.error() != httplib::Error::Success) { if (resp.error() != httplib::Error::Success) {
return rpc_response{ return rpc_response{
rpc_response_type::http_error, .response_type = rpc_response_type::http_error,
{{"error", httplib::to_string(resp.error())}}, .data = {{"error", httplib::to_string(resp.error())}},
}; };
} }
if (resp->status != http_error_codes::ok) { if (resp->status != http_error_codes::ok) {
return rpc_response{ return rpc_response{
rpc_response_type::http_error, .response_type = rpc_response_type::http_error,
{{"error", std::to_string(resp->status)}}, .data = {{"error", std::to_string(resp->status)}},
}; };
} }
return rpc_response{ return rpc_response{
rpc_response_type::success, .response_type = rpc_response_type::success,
json::parse(resp->body), .data = json::parse(resp->body),
}; };
} }
@@ -92,20 +92,20 @@ auto client::get_config_value_by_name(const std::string &name) -> rpc_response {
cli.Get("/api/v1/" + rpc_method::get_config_value_by_name, params, {}); cli.Get("/api/v1/" + rpc_method::get_config_value_by_name, params, {});
if (resp.error() != httplib::Error::Success) { if (resp.error() != httplib::Error::Success) {
return rpc_response{ return rpc_response{
rpc_response_type::http_error, .response_type = rpc_response_type::http_error,
{{"error", httplib::to_string(resp.error())}}, .data = {{"error", httplib::to_string(resp.error())}},
}; };
} }
if (resp->status != http_error_codes::ok) { if (resp->status != http_error_codes::ok) {
return rpc_response{ return rpc_response{
rpc_response_type::http_error, .response_type = rpc_response_type::http_error,
{{"error", std::to_string(resp->status)}}, .data = {{"error", std::to_string(resp->status)}},
}; };
} }
return rpc_response{ return rpc_response{
rpc_response_type::success, .response_type = rpc_response_type::success,
json::parse(resp->body), .data = json::parse(resp->body),
}; };
} }
@@ -120,20 +120,20 @@ auto client::get_directory_items(const std::string &api_path) -> rpc_response {
auto resp = cli.Get("/api/v1/" + rpc_method::get_directory_items, params, {}); auto resp = cli.Get("/api/v1/" + rpc_method::get_directory_items, params, {});
if (resp.error() != httplib::Error::Success) { if (resp.error() != httplib::Error::Success) {
return rpc_response{ return rpc_response{
rpc_response_type::http_error, .response_type = rpc_response_type::http_error,
{{"error", httplib::to_string(resp.error())}}, .data = {{"error", httplib::to_string(resp.error())}},
}; };
} }
if (resp->status != http_error_codes::ok) { if (resp->status != http_error_codes::ok) {
return rpc_response{ return rpc_response{
rpc_response_type::http_error, .response_type = rpc_response_type::http_error,
{{"error", std::to_string(resp->status)}}, .data = {{"error", std::to_string(resp->status)}},
}; };
} }
return rpc_response{ return rpc_response{
rpc_response_type::success, .response_type = rpc_response_type::success,
json::parse(resp->body), .data = json::parse(resp->body),
}; };
} }
@@ -147,20 +147,20 @@ auto client::get_open_files() -> rpc_response {
auto resp = cli.Get("/api/v1/" + rpc_method::get_open_files); auto resp = cli.Get("/api/v1/" + rpc_method::get_open_files);
if (resp.error() != httplib::Error::Success) { if (resp.error() != httplib::Error::Success) {
return rpc_response{ return rpc_response{
rpc_response_type::http_error, .response_type = rpc_response_type::http_error,
{{"error", httplib::to_string(resp.error())}}, .data = {{"error", httplib::to_string(resp.error())}},
}; };
} }
if (resp->status != http_error_codes::ok) { if (resp->status != http_error_codes::ok) {
return rpc_response{ return rpc_response{
rpc_response_type::http_error, .response_type = rpc_response_type::http_error,
{{"error", std::to_string(resp->status)}}, .data = {{"error", std::to_string(resp->status)}},
}; };
} }
return rpc_response{ return rpc_response{
rpc_response_type::success, .response_type = rpc_response_type::success,
json::parse(resp->body), .data = json::parse(resp->body),
}; };
} }
@@ -174,20 +174,20 @@ auto client::get_pinned_files() -> rpc_response {
auto resp = cli.Get("/api/v1/" + rpc_method::get_pinned_files); auto resp = cli.Get("/api/v1/" + rpc_method::get_pinned_files);
if (resp.error() != httplib::Error::Success) { if (resp.error() != httplib::Error::Success) {
return rpc_response{ return rpc_response{
rpc_response_type::http_error, .response_type = rpc_response_type::http_error,
{{"error", httplib::to_string(resp.error())}}, .data = {{"error", httplib::to_string(resp.error())}},
}; };
} }
if (resp->status != http_error_codes::ok) { if (resp->status != http_error_codes::ok) {
return rpc_response{ return rpc_response{
rpc_response_type::http_error, .response_type = rpc_response_type::http_error,
{{"error", std::to_string(resp->status)}}, .data = {{"error", std::to_string(resp->status)}},
}; };
} }
return rpc_response{ return rpc_response{
rpc_response_type::success, .response_type = rpc_response_type::success,
json::parse(resp->body), .data = json::parse(resp->body),
}; };
} }
@@ -202,20 +202,20 @@ auto client::pin_file(const std::string &api_path) -> rpc_response {
auto resp = cli.Post("/api/v1/" + rpc_method::pin_file, params); auto resp = cli.Post("/api/v1/" + rpc_method::pin_file, params);
if (resp.error() != httplib::Error::Success) { if (resp.error() != httplib::Error::Success) {
return rpc_response{ return rpc_response{
rpc_response_type::http_error, .response_type = rpc_response_type::http_error,
{{"error", httplib::to_string(resp.error())}}, .data = {{"error", httplib::to_string(resp.error())}},
}; };
} }
if (resp->status != http_error_codes::ok) { if (resp->status != http_error_codes::ok) {
return rpc_response{ return rpc_response{
rpc_response_type::http_error, .response_type = rpc_response_type::http_error,
{{"error", std::to_string(resp->status)}}, .data = {{"error", std::to_string(resp->status)}},
}; };
} }
return rpc_response{ return rpc_response{
rpc_response_type::success, .response_type = rpc_response_type::success,
{}, .data = {},
}; };
} }
@@ -230,20 +230,20 @@ auto client::pinned_status(const std::string &api_path) -> rpc_response {
auto resp = cli.Get("/api/v1/" + rpc_method::pinned_status, params, {}); auto resp = cli.Get("/api/v1/" + rpc_method::pinned_status, params, {});
if (resp.error() != httplib::Error::Success) { if (resp.error() != httplib::Error::Success) {
return rpc_response{ return rpc_response{
rpc_response_type::http_error, .response_type = rpc_response_type::http_error,
{{"error", httplib::to_string(resp.error())}}, .data = {{"error", httplib::to_string(resp.error())}},
}; };
} }
if (resp->status != http_error_codes::ok) { if (resp->status != http_error_codes::ok) {
return rpc_response{ return rpc_response{
rpc_response_type::http_error, .response_type = rpc_response_type::http_error,
{{"error", std::to_string(resp->status)}}, .data = {{"error", std::to_string(resp->status)}},
}; };
} }
return rpc_response{ return rpc_response{
rpc_response_type::success, .response_type = rpc_response_type::success,
json::parse(resp->body), .data = json::parse(resp->body),
}; };
} }
@@ -265,20 +265,20 @@ auto client::set_config_value_by_name(const std::string &name,
cli.Post("/api/v1/" + rpc_method::set_config_value_by_name, params); cli.Post("/api/v1/" + rpc_method::set_config_value_by_name, params);
if (resp.error() != httplib::Error::Success) { if (resp.error() != httplib::Error::Success) {
return rpc_response{ return rpc_response{
rpc_response_type::http_error, .response_type = rpc_response_type::http_error,
{{"error", httplib::to_string(resp.error())}}, .data = {{"error", httplib::to_string(resp.error())}},
}; };
} }
if (resp->status != http_error_codes::ok) { if (resp->status != http_error_codes::ok) {
return rpc_response{ return rpc_response{
rpc_response_type::http_error, .response_type = rpc_response_type::http_error,
{{"error", std::to_string(resp->status)}}, .data = {{"error", std::to_string(resp->status)}},
}; };
}; };
return rpc_response{ return rpc_response{
rpc_response_type::success, .response_type = rpc_response_type::success,
nlohmann::json::parse(resp->body), .data = nlohmann::json::parse(resp->body),
}; };
} }
@@ -292,20 +292,20 @@ auto client::unmount() -> rpc_response {
auto resp = cli.Post("/api/v1/" + rpc_method::unmount); auto resp = cli.Post("/api/v1/" + rpc_method::unmount);
if (resp.error() != httplib::Error::Success) { if (resp.error() != httplib::Error::Success) {
return rpc_response{ return rpc_response{
rpc_response_type::http_error, .response_type = rpc_response_type::http_error,
{{"error", httplib::to_string(resp.error())}}, .data = {{"error", httplib::to_string(resp.error())}},
}; };
} }
if (resp->status != http_error_codes::ok) { if (resp->status != http_error_codes::ok) {
return rpc_response{ return rpc_response{
rpc_response_type::http_error, .response_type = rpc_response_type::http_error,
{{"error", std::to_string(resp->status)}}, .data = {{"error", std::to_string(resp->status)}},
}; };
} }
return rpc_response{ return rpc_response{
rpc_response_type::success, .response_type = rpc_response_type::success,
{}, .data = {},
}; };
} }
@@ -320,20 +320,20 @@ auto client::unpin_file(const std::string &api_path) -> rpc_response {
auto resp = cli.Post("/api/v1/" + rpc_method::unpin_file, params); auto resp = cli.Post("/api/v1/" + rpc_method::unpin_file, params);
if (resp.error() != httplib::Error::Success) { if (resp.error() != httplib::Error::Success) {
return rpc_response{ return rpc_response{
rpc_response_type::http_error, .response_type = rpc_response_type::http_error,
{{"error", httplib::to_string(resp.error())}}, .data = {{"error", httplib::to_string(resp.error())}},
}; };
} }
if (resp->status != http_error_codes::ok) { if (resp->status != http_error_codes::ok) {
return rpc_response{ return rpc_response{
rpc_response_type::http_error, .response_type = rpc_response_type::http_error,
{{"error", std::to_string(resp->status)}}, .data = {{"error", std::to_string(resp->status)}},
}; };
} }
return rpc_response{ return rpc_response{
rpc_response_type::success, .response_type = rpc_response_type::success,
{}, .data = {},
}; };
} }
} // namespace repertory } // namespace repertory

View File

@@ -29,8 +29,8 @@ namespace repertory::cli::actions {
const std::string &data_directory, const std::string &data_directory,
const provider_type &prov, const provider_type &prov,
const std::string &unique_id, const std::string &unique_id,
std::string user, std::string user, std::string password)
std::string password) -> exit_code { -> exit_code {
lock_data lock(prov, unique_id); lock_data lock(prov, unique_id);
const auto res = lock.grab_lock(1U); const auto res = lock.grab_lock(1U);
if (res == lock_result::success) { if (res == lock_result::success) {
@@ -42,8 +42,11 @@ namespace repertory::cli::actions {
auto port = app_config::default_api_port(prov); auto port = app_config::default_api_port(prov);
utils::cli::get_api_authentication_data(user, password, port, prov, utils::cli::get_api_authentication_data(user, password, port, prov,
data_directory); data_directory);
const auto response = auto response = client({.host = "localhost",
client({"localhost", password, port, user}).get_config(); .password = password,
.port = port,
.user = user})
.get_config();
std::cout << static_cast<int>(response.response_type) << std::endl; std::cout << static_cast<int>(response.response_type) << std::endl;
std::cout << response.data.dump(2) << std::endl; std::cout << response.data.dump(2) << std::endl;
} }

View File

@@ -38,8 +38,11 @@ drive_information(std::vector<const char *> /* args */,
auto port = app_config::default_api_port(prov); auto port = app_config::default_api_port(prov);
utils::cli::get_api_authentication_data(user, password, port, prov, utils::cli::get_api_authentication_data(user, password, port, prov,
data_directory); data_directory);
const auto response = auto response = client({.host = "localhost",
client({"localhost", password, port, user}).get_drive_information(); .password = password,
.port = port,
.user = user})
.get_drive_information();
std::cout << static_cast<int>(response.response_type) << std::endl; std::cout << static_cast<int>(response.response_type) << std::endl;
std::cout << response.data.dump(2) << std::endl; std::cout << response.data.dump(2) << std::endl;
} else { } else {

View File

@@ -49,7 +49,10 @@ namespace repertory::cli::actions {
auto port = app_config::default_api_port(prov); auto port = app_config::default_api_port(prov);
utils::cli::get_api_authentication_data(user, password, port, prov, utils::cli::get_api_authentication_data(user, password, port, prov,
data_directory); data_directory);
const auto response = client({"localhost", password, port, user}) auto response = client({.host = "localhost",
.password = password,
.port = port,
.user = user})
.get_config_value_by_name(data); .get_config_value_by_name(data);
std::cout << static_cast<int>(response.response_type) << std::endl; std::cout << static_cast<int>(response.response_type) << std::endl;
std::cout << response.data.dump(2) << std::endl; std::cout << response.data.dump(2) << std::endl;

View File

@@ -36,8 +36,11 @@ namespace repertory::cli::actions {
auto port = app_config::default_api_port(prov); auto port = app_config::default_api_port(prov);
utils::cli::get_api_authentication_data(user, password, port, prov, utils::cli::get_api_authentication_data(user, password, port, prov,
data_directory); data_directory);
const auto response = auto response = client({.host = "localhost",
client({"localhost", password, port, user}).get_directory_items(data); .password = password,
.port = port,
.user = user})
.get_directory_items(data);
if (response.response_type == rpc_response_type::success) { if (response.response_type == rpc_response_type::success) {
std::cout << response.data.dump(2) << std::endl; std::cout << response.data.dump(2) << std::endl;
} else { } else {

View File

@@ -35,8 +35,11 @@ namespace repertory::cli::actions {
auto port = app_config::default_api_port(prov); auto port = app_config::default_api_port(prov);
utils::cli::get_api_authentication_data(user, password, port, prov, utils::cli::get_api_authentication_data(user, password, port, prov,
data_directory); data_directory);
const auto response = auto response = client({.host = "localhost",
client({"localhost", password, port, user}).get_pinned_files(); .password = password,
.port = port,
.user = user})
.get_pinned_files();
if (response.response_type == rpc_response_type::success) { if (response.response_type == rpc_response_type::success) {
std::cout << response.data.dump(2) << std::endl; std::cout << response.data.dump(2) << std::endl;
} else { } else {

View File

@@ -37,8 +37,11 @@ open_files(std::vector<const char *> /* args */,
auto port = app_config::default_api_port(prov); auto port = app_config::default_api_port(prov);
utils::cli::get_api_authentication_data(user, password, port, prov, utils::cli::get_api_authentication_data(user, password, port, prov,
data_directory); data_directory);
const auto response = auto response = client({.host = "localhost",
client({"localhost", password, port, user}).get_open_files(); .password = password,
.port = port,
.user = user})
.get_open_files();
std::cout << static_cast<int>(response.response_type) << std::endl; std::cout << static_cast<int>(response.response_type) << std::endl;
std::cout << response.data.dump(2) << std::endl; std::cout << response.data.dump(2) << std::endl;
} else { } else {

View File

@@ -36,8 +36,11 @@ pin_file(std::vector<const char *> args, const std::string &data_directory,
auto port = app_config::default_api_port(prov); auto port = app_config::default_api_port(prov);
utils::cli::get_api_authentication_data(user, password, port, prov, utils::cli::get_api_authentication_data(user, password, port, prov,
data_directory); data_directory);
const auto response = auto response = client({.host = "localhost",
client({"localhost", password, port, user}).pin_file(data); .password = password,
.port = port,
.user = user})
.pin_file(data);
if (response.response_type == rpc_response_type::success) { if (response.response_type == rpc_response_type::success) {
std::cout << response.data.dump(2) << std::endl; std::cout << response.data.dump(2) << std::endl;
} else { } else {

View File

@@ -36,8 +36,11 @@ pinned_status(std::vector<const char *> args, const std::string &data_directory,
auto port = app_config::default_api_port(prov); auto port = app_config::default_api_port(prov);
utils::cli::get_api_authentication_data(user, password, port, prov, utils::cli::get_api_authentication_data(user, password, port, prov,
data_directory); data_directory);
const auto response = auto response = client({.host = "localhost",
client({"localhost", password, port, user}).pinned_status(data); .password = password,
.port = port,
.user = user})
.pinned_status(data);
if (response.response_type == rpc_response_type::success) { if (response.response_type == rpc_response_type::success) {
std::cout << response.data.dump(2) << std::endl; std::cout << response.data.dump(2) << std::endl;
} else { } else {

View File

@@ -56,7 +56,10 @@ namespace repertory::cli::actions {
auto port = app_config::default_api_port(prov); auto port = app_config::default_api_port(prov);
utils::cli::get_api_authentication_data(user, password, port, prov, utils::cli::get_api_authentication_data(user, password, port, prov,
data_directory); data_directory);
const auto response = client({"localhost", password, port, user}) auto response = client({.host = "localhost",
.password = password,
.port = port,
.user = user})
.set_config_value_by_name(data[0U], data[1U]); .set_config_value_by_name(data[0U], data[1U]);
std::cout << static_cast<int>(response.response_type) << std::endl; std::cout << static_cast<int>(response.response_type) << std::endl;
std::cout << response.data.dump(2) << std::endl; std::cout << response.data.dump(2) << std::endl;

View File

@@ -27,16 +27,42 @@
namespace repertory::cli::actions { namespace repertory::cli::actions {
[[nodiscard]] inline auto [[nodiscard]] inline auto
unmount(std::vector<const char *> /* args */, const std::string &data_directory, unmount(std::vector<const char *> /* args */, const std::string &data_directory,
const provider_type &prov, const std::string & /*unique_id*/, const provider_type &prov, const std::string & /* unique_id */,
std::string user, std::string password) -> exit_code { std::string user, std::string password) -> exit_code {
auto ret = exit_code::success; constexpr const std::uint8_t retry_count{30U};
auto port = app_config::default_api_port(prov);
auto ret{exit_code::success};
auto port{app_config::default_api_port(prov)};
utils::cli::get_api_authentication_data(user, password, port, prov, utils::cli::get_api_authentication_data(user, password, port, prov,
data_directory); data_directory);
const auto response = client({"localhost", password, port, user}).unmount(); auto response = client({.host = "localhost",
std::cout << static_cast<int>(response.response_type) << std::endl; .password = password,
.port = port,
.user = user})
.unmount();
if (response.response_type == rpc_response_type::success) { if (response.response_type == rpc_response_type::success) {
std::cout << "waiting for unmount ..." << std::flush;
for (std::uint8_t retry{0U};
retry < retry_count &&
response.response_type == rpc_response_type::success;
++retry) {
std::this_thread::sleep_for(1s);
response = client({.host = "localhost",
.password = password,
.port = port,
.user = user})
.unmount();
std::cout << "." << std::flush;
}
if (response.response_type == rpc_response_type::success) {
std::cerr << " failed! mount still active" << std::endl;
ret = exit_code::mount_active;
} else {
std::cout << " done!" << std::endl;
std::cout << static_cast<int>(rpc_response_type::success) << std::endl;
std::cout << response.data.dump(2) << std::endl; std::cout << response.data.dump(2) << std::endl;
}
} else { } else {
std::cerr << response.data.dump(2) << std::endl; std::cerr << response.data.dump(2) << std::endl;
ret = exit_code::communication_error; ret = exit_code::communication_error;

View File

@@ -36,8 +36,11 @@ unpin_file(std::vector<const char *> args, const std::string &data_directory,
auto port = app_config::default_api_port(prov); auto port = app_config::default_api_port(prov);
utils::cli::get_api_authentication_data(user, password, port, prov, utils::cli::get_api_authentication_data(user, password, port, prov,
data_directory); data_directory);
const auto response = auto response = client({.host = "localhost",
client({"localhost", password, port, user}).unpin_file(data); .password = password,
.port = port,
.user = user})
.unpin_file(data);
if (response.response_type == rpc_response_type::success) { if (response.response_type == rpc_response_type::success) {
std::cout << response.data.dump(2) << std::endl; std::cout << response.data.dump(2) << std::endl;
} else { } else {

View File

@@ -360,22 +360,12 @@ public:
} }
static void execute_unmount(auto args) { static void execute_unmount(auto args) {
auto unmounted{false};
args.emplace_back("-unmount"); args.emplace_back("-unmount");
auto unmount_cmd = "./repertory " + utils::string::join(args, ' '); auto unmount_cmd = "./repertory " + utils::string::join(args, ' ');
std::cout << "unmount command: " << unmount_cmd << std::endl; std::cout << "unmount command: " << unmount_cmd << std::endl;
for (int i = 0; not unmounted && (i < 50); i++) {
auto res = system(unmount_cmd.c_str()); auto res = system(unmount_cmd.c_str());
unmounted = res == 0;
EXPECT_EQ(0, res); EXPECT_EQ(0, res);
if (not unmounted) {
std::this_thread::sleep_for(5s);
}
}
EXPECT_TRUE(unmounted);
} }
static void rmdir_and_test(std::string_view dir_path) { static void rmdir_and_test(std::string_view dir_path) {

View File

@@ -379,8 +379,11 @@ TYPED_TEST(fuse_test, create_fails_with_excl_if_path_is_directory) {
for (const auto &flags : ops) { for (const auto &flags : ops) {
auto handle = open(dir_path.c_str(), flags, ACCESSPERMS); auto handle = open(dir_path.c_str(), flags, ACCESSPERMS);
EXPECT_EQ(-1, handle); EXPECT_EQ(-1, handle);
EXPECT_EQ(EEXIST, errno); EXPECT_EQ(EEXIST, errno);
if (handle != -1) {
close(handle);
}
} }
this->rmdir_and_test(dir_path); this->rmdir_and_test(dir_path);
@@ -399,8 +402,11 @@ TYPED_TEST(fuse_test, create_fails_with_excl_if_file_exists) {
for (const auto &flags : ops) { for (const auto &flags : ops) {
auto handle = open(file_path.c_str(), flags, ACCESSPERMS); auto handle = open(file_path.c_str(), flags, ACCESSPERMS);
EXPECT_EQ(-1, handle); EXPECT_EQ(-1, handle);
EXPECT_EQ(EEXIST, errno); EXPECT_EQ(EEXIST, errno);
if (handle != -1) {
close(handle);
}
} }
this->unlink_file_and_test(file_path); this->unlink_file_and_test(file_path);
@@ -408,23 +414,40 @@ TYPED_TEST(fuse_test, create_fails_with_excl_if_file_exists) {
TYPED_TEST(fuse_test, create_fails_if_path_is_directory) { TYPED_TEST(fuse_test, create_fails_if_path_is_directory) {
std::array<int, 7U> ops{ std::array<int, 7U> ops{
O_CREAT,
O_CREAT | O_APPEND, O_CREAT | O_APPEND,
O_CREAT | O_RDWR, O_CREAT | O_RDWR,
O_CREAT | O_TRUNC | O_RDWR, O_CREAT | O_TRUNC | O_RDWR,
O_CREAT | O_TRUNC | O_WRONLY, O_CREAT | O_TRUNC | O_WRONLY,
O_CREAT | O_TRUNC, O_CREAT | O_TRUNC,
O_CREAT | O_WRONLY, O_CREAT | O_WRONLY,
O_CREAT,
}; };
std::string dir_name{"create_test"}; std::string dir_name{"create_test"};
auto dir_path = this->create_directory_and_test(dir_name); auto dir_path = this->create_directory_and_test(dir_name);
for (const auto &flags : ops) { for (std::size_t idx{0U}; idx < ops.size(); ++idx) {
auto flags = ops.at(idx);
auto handle = open(dir_path.c_str(), flags, ACCESSPERMS); auto handle = open(dir_path.c_str(), flags, ACCESSPERMS);
EXPECT_EQ(-1, handle); #if defined(__APPLE__)
if (handle == -1) {
EXPECT_EQ(EISDIR, errno); EXPECT_EQ(EISDIR, errno);
} else {
if (idx > 1U) {
std::cerr << "create flags should return invalid handle|idx|" << idx
<< "|flags|" << std::hex << flags << std::endl;
EXPECT_EQ(-1, handle);
}
close(handle);
}
#else // !defined(__APPLE__)
EXPECT_EQ(-1, handle);
EXPECT_EQ(EISDIR, errno);
if (handle != -1) {
close(handle);
}
#endif //! defined(__APPLE__)
} }
this->rmdir_and_test(dir_path); this->rmdir_and_test(dir_path);
@@ -450,11 +473,15 @@ TYPED_TEST(fuse_test, create_fails_if_parent_path_does_not_exist) {
for (const auto &flags : ops) { for (const auto &flags : ops) {
auto handle = open(file_path.c_str(), flags, ACCESSPERMS); auto handle = open(file_path.c_str(), flags, ACCESSPERMS);
EXPECT_EQ(-1, handle); EXPECT_EQ(-1, handle);
EXPECT_EQ(ENOENT, errno); EXPECT_EQ(ENOENT, errno);
if (handle != -1) {
close(handle);
}
} }
} }
#if !defined(__APPLE__)
TYPED_TEST(fuse_test, create_fails_if_invalid) { TYPED_TEST(fuse_test, create_fails_if_invalid) {
std::array<int, 1U> ops{ std::array<int, 1U> ops{
O_CREAT | O_TRUNC | O_APPEND, O_CREAT | O_TRUNC | O_APPEND,
@@ -466,10 +493,14 @@ TYPED_TEST(fuse_test, create_fails_if_invalid) {
for (const auto &flags : ops) { for (const auto &flags : ops) {
auto handle = open(file_path.c_str(), flags, ACCESSPERMS); auto handle = open(file_path.c_str(), flags, ACCESSPERMS);
EXPECT_EQ(-1, handle); EXPECT_EQ(-1, handle);
EXPECT_EQ(EINVAL, errno); EXPECT_EQ(EINVAL, errno);
if (handle != -1) {
close(handle);
}
} }
} }
#endif // !defined(__APPLE__)
TYPED_TEST(fuse_test, create_open_fails_if_path_is_directory) { TYPED_TEST(fuse_test, create_open_fails_if_path_is_directory) {
std::array<int, 9U> ops{ std::array<int, 9U> ops{
@@ -481,13 +512,28 @@ TYPED_TEST(fuse_test, create_open_fails_if_path_is_directory) {
std::string dir_name{"create_test"}; std::string dir_name{"create_test"};
auto dir_path = this->create_directory_and_test(dir_name); auto dir_path = this->create_directory_and_test(dir_name);
for (const auto &flags : ops) { for (std::size_t idx{0U}; idx < ops.size(); ++idx) {
auto flags = ops.at(idx);
auto handle = open(dir_path.c_str(), flags); auto handle = open(dir_path.c_str(), flags);
EXPECT_EQ(-1, handle); #if defined(__APPLE__)
if (handle != -1) { if (handle != -1) {
std::cout << std::oct << flags << std::endl; if (idx != 0U) {
std::cerr << "open flags should return invalid handle|idx|" << idx
<< "|flags|" << std::hex << flags << std::endl;
EXPECT_EQ(-1, handle);
} }
close(handle);
continue;
}
#endif // defined(__APPLE_)
EXPECT_EQ(-1, handle);
EXPECT_EQ(EISDIR, errno); EXPECT_EQ(EISDIR, errno);
if (handle != -1) {
close(handle);
}
} }
this->rmdir_and_test(dir_path); this->rmdir_and_test(dir_path);
@@ -514,6 +560,10 @@ TYPED_TEST(fuse_test, create_open_fails_if_path_does_not_exist) {
auto handle = open(file_path.c_str(), flags); auto handle = open(file_path.c_str(), flags);
EXPECT_EQ(-1, handle); EXPECT_EQ(-1, handle);
EXPECT_EQ(ENOENT, errno); EXPECT_EQ(ENOENT, errno);
if (handle != -1) {
close(handle);
}
} }
} }
} // namespace repertory } // namespace repertory

View File

@@ -23,6 +23,68 @@
#include "utils/unix.hpp" #include "utils/unix.hpp"
#include "utils/collection.hpp" #include "utils/collection.hpp"
#include "utils/error_utils.hpp"
namespace {
[[nodiscard]] auto get_group_list(auto *pass) -> std::vector<gid_t> {
REPERTORY_USES_FUNCTION_NAME();
std::vector<gid_t> groups{};
#if defined(__APPLE__)
constexpr const int buffer_count{8};
constexpr const int max_group_count{1024};
groups.resize(buffer_count);
std::size_t orig_count{0U};
while (true) {
auto group_count{static_cast<int>(groups.size())};
if (group_count > max_group_count) {
repertory::utils::error::raise_error(
function_name, "getgrouplist failed: too many groups");
break;
}
auto res{
getgrouplist(pass->pw_name, static_cast<int>(pass->pw_gid),
reinterpret_cast<int *>(groups.data()), &group_count),
};
if (res < 0) {
if (orig_count == 0U) {
repertory::utils::error::raise_error(
function_name,
fmt::format("failed to get group list|error|{}", errno));
}
break;
}
groups.resize(static_cast<std::size_t>(group_count));
if (groups.size() == orig_count) {
break;
}
orig_count = groups.size();
}
#else // !defined(__APPLE__)
int group_count{};
auto res = getgrouplist(pass->pw_name, pass->pw_gid, nullptr, &group_count);
if (res >= 0) {
repertory::utils::error::raise_error(
function_name, fmt::format("failed to get group list|error|{}", errno));
}
#endif // defined(__APPLE__)
#if !defined(__APPLE__)
res = getgrouplist(pass->pw_name, pass->pw_gid, groups.data(), &group_count);
if (res >= 0) {
repertory::utils::error::raise_error(
function_name, fmt::format("failed to get group list|error|{}", errno));
}
#endif // !defined(__APPLE__)
return groups;
}
} // namespace
namespace repertory::utils { namespace repertory::utils {
#if defined(__APPLE__) #if defined(__APPLE__)
@@ -42,20 +104,11 @@ auto get_thread_id() -> std::uint64_t {
} }
auto is_uid_member_of_group(uid_t uid, gid_t gid) -> bool { auto is_uid_member_of_group(uid_t uid, gid_t gid) -> bool {
std::vector<gid_t> groups{}; REPERTORY_USES_FUNCTION_NAME();
auto res = use_getpwuid(uid, [&groups](struct passwd *pass) {
int group_count{};
if (getgrouplist(pass->pw_name, pass->pw_gid, nullptr, &group_count) < 0) {
groups.resize(static_cast<std::size_t>(group_count));
#if defined(__APPLE__)
getgrouplist(pass->pw_name, pass->pw_gid,
reinterpret_cast<int *>(groups.data()), &group_count);
#else // !defined(__APPLE__)
getgrouplist(pass->pw_name, pass->pw_gid, groups.data(), &group_count);
#endif // defined(__APPLE__)
}
});
std::vector<gid_t> groups{};
auto res = use_getpwuid(
uid, [&groups](struct passwd *pass) { groups = get_group_list(pass); });
if (not res) { if (not res) {
throw utils::error::create_exception(res.function_name, throw utils::error::create_exception(res.function_name,
{"use_getpwuid failed", res.reason}); {"use_getpwuid failed", res.reason});
@@ -73,14 +126,16 @@ auto use_getpwuid(uid_t uid, passwd_callback_t callback) -> result {
auto *temp_pw = getpwuid(uid); auto *temp_pw = getpwuid(uid);
if (temp_pw == nullptr) { if (temp_pw == nullptr) {
return { return {
std::string{function_name}, .function_name = std::string{function_name},
false, .ok = false,
"'getpwuid' returned nullptr", .reason = "'getpwuid' returned nullptr",
}; };
} }
callback(temp_pw); callback(temp_pw);
return {std::string{function_name}}; return {
.function_name = std::string{function_name},
};
} }
} // namespace repertory::utils } // namespace repertory::utils