Merge branch 'v2.1.0-rc-develop' of https://git.fifthgrid.com/blockstorage/repertory into v2.1.0-rc-develop
This commit is contained in:
@@ -22,6 +22,8 @@
|
||||
#ifndef REPERTORY_INCLUDE_COMM_PACKET_COMMON_HPP_
|
||||
#define REPERTORY_INCLUDE_COMM_PACKET_COMMON_HPP_
|
||||
|
||||
#include "events/event_system.hpp"
|
||||
#include "events/types/packet_client_timeout.hpp"
|
||||
#include "utils/common.hpp"
|
||||
|
||||
namespace repertory::comm {
|
||||
@@ -46,6 +48,14 @@ private:
|
||||
boost::asio::ip::tcp::socket &sock;
|
||||
};
|
||||
|
||||
template <class op_t>
|
||||
inline void run_with_deadline(boost::asio::io_context &io_ctx,
|
||||
boost::asio::ip::tcp::socket &sock,
|
||||
op_t operation,
|
||||
std::chrono::milliseconds deadline,
|
||||
const std::string &event_name,
|
||||
const std::string &function_name);
|
||||
|
||||
void apply_common_socket_properties(boost::asio::ip::tcp::socket &sock);
|
||||
|
||||
[[nodiscard]] auto is_socket_still_alive(boost::asio::ip::tcp::socket &sock)
|
||||
@@ -66,6 +76,59 @@ void write_all_with_deadline(boost::asio::io_context &io_ctx,
|
||||
boost::asio::ip::tcp::socket &sock,
|
||||
boost::asio::mutable_buffer buf,
|
||||
std::chrono::milliseconds deadline);
|
||||
|
||||
template <class op_t>
|
||||
inline void run_with_deadline(boost::asio::io_context &io_ctx,
|
||||
boost::asio::ip::tcp::socket &sock,
|
||||
op_t operation,
|
||||
std::chrono::milliseconds deadline,
|
||||
const std::string &event_name,
|
||||
const std::string &function_name) {
|
||||
deadline = std::max(deadline, std::chrono::milliseconds{250});
|
||||
|
||||
struct request_state final {
|
||||
std::atomic<bool> done{false};
|
||||
std::atomic<bool> timed_out{false};
|
||||
boost::system::error_code err;
|
||||
};
|
||||
auto state = std::make_shared<request_state>();
|
||||
|
||||
boost::asio::steady_timer timer{io_ctx};
|
||||
timer.expires_after(deadline);
|
||||
|
||||
timer.async_wait([state, &sock](auto &&err) {
|
||||
if (not err && not state->done) {
|
||||
state->timed_out = true;
|
||||
boost::system::error_code ignored_ec;
|
||||
[[maybe_unused]] auto res = sock.cancel(ignored_ec);
|
||||
}
|
||||
});
|
||||
|
||||
operation([state](auto &&err) {
|
||||
state->err = err;
|
||||
state->done = true;
|
||||
});
|
||||
|
||||
io_ctx.restart();
|
||||
while (not state->done && not state->timed_out) {
|
||||
io_ctx.run_one();
|
||||
}
|
||||
|
||||
timer.cancel();
|
||||
|
||||
io_ctx.poll();
|
||||
|
||||
if (state->timed_out) {
|
||||
repertory::event_system::instance().raise<repertory::packet_client_timeout>(
|
||||
std::string(event_name), std::string(function_name));
|
||||
throw std::runtime_error(event_name + " timed-out");
|
||||
}
|
||||
|
||||
if (state->err) {
|
||||
throw std::runtime_error(event_name + " failed|err|" +
|
||||
state->err.message());
|
||||
}
|
||||
}
|
||||
} // namespace repertory::comm
|
||||
|
||||
#endif // REPERTORY_INCLUDE_COMM_PACKET_COMMON_HPP_
|
||||
|
@@ -90,8 +90,7 @@ public:
|
||||
remote::file_handle handle)
|
||||
-> packet::error_type override;
|
||||
|
||||
[[nodiscard]] auto fuse_ftruncate(const char *path,
|
||||
remote::file_offset size,
|
||||
[[nodiscard]] auto fuse_ftruncate(const char *path, remote::file_offset size,
|
||||
remote::file_handle handle)
|
||||
-> packet::error_type override;
|
||||
|
||||
@@ -119,17 +118,17 @@ public:
|
||||
[[nodiscard]] auto fuse_mkdir(const char *path, remote::file_mode mode)
|
||||
-> packet::error_type override;
|
||||
|
||||
[[nodiscard]] auto fuse_opendir(const char *path, remote::file_handle handle)
|
||||
[[nodiscard]] auto fuse_opendir(const char *path, remote::file_handle &handle)
|
||||
-> packet::error_type override;
|
||||
|
||||
[[nodiscard]] auto
|
||||
fuse_create(const char *path, remote::file_mode mode,
|
||||
const remote::open_flags &flags, remote::file_handle handle)
|
||||
[[nodiscard]] auto fuse_create(const char *path, remote::file_mode mode,
|
||||
const remote::open_flags &flags,
|
||||
remote::file_handle &handle)
|
||||
-> packet::error_type override;
|
||||
|
||||
[[nodiscard]] auto fuse_open(const char *path,
|
||||
const remote::open_flags &flags,
|
||||
remote::file_handle handle)
|
||||
remote::file_handle &handle)
|
||||
-> packet::error_type override;
|
||||
|
||||
[[nodiscard]] auto
|
||||
@@ -140,10 +139,9 @@ public:
|
||||
[[nodiscard]] auto fuse_rename(const char *from, const char *to)
|
||||
-> packet::error_type override;
|
||||
|
||||
[[nodiscard]] auto fuse_write(const char *path, const char *buffer,
|
||||
remote::file_size write_size,
|
||||
remote::file_offset write_offset,
|
||||
remote::file_handle handle)
|
||||
[[nodiscard]] auto
|
||||
fuse_write(const char *path, const char *buffer, remote::file_size write_size,
|
||||
remote::file_offset write_offset, remote::file_handle handle)
|
||||
-> packet::error_type override;
|
||||
|
||||
[[nodiscard]] auto fuse_write_base64(const char *path, const char *buffer,
|
||||
@@ -152,9 +150,9 @@ public:
|
||||
remote::file_handle handle)
|
||||
-> packet::error_type override;
|
||||
|
||||
[[nodiscard]] auto
|
||||
fuse_readdir(const char *path, remote::file_offset offset,
|
||||
remote::file_handle handle, std::string &item_path)
|
||||
[[nodiscard]] auto fuse_readdir(const char *path, remote::file_offset offset,
|
||||
remote::file_handle handle,
|
||||
std::string &item_path)
|
||||
-> packet::error_type override;
|
||||
|
||||
[[nodiscard]] auto fuse_release(const char *path, remote::file_handle handle)
|
||||
@@ -181,8 +179,7 @@ public:
|
||||
remote::file_time chgtime)
|
||||
-> packet::error_type override;
|
||||
|
||||
[[nodiscard]] auto fuse_setcrtime(const char *path,
|
||||
remote::file_time crtime)
|
||||
[[nodiscard]] auto fuse_setcrtime(const char *path, remote::file_time crtime)
|
||||
-> packet::error_type override;
|
||||
|
||||
[[nodiscard]] auto fuse_setvolname(const char *volname)
|
||||
@@ -204,8 +201,7 @@ public:
|
||||
remote::statfs_x &r_stat)
|
||||
-> packet::error_type override;
|
||||
|
||||
[[nodiscard]] auto fuse_truncate(const char *path,
|
||||
remote::file_offset size)
|
||||
[[nodiscard]] auto fuse_truncate(const char *path, remote::file_offset size)
|
||||
-> packet::error_type override;
|
||||
|
||||
[[nodiscard]] auto fuse_unlink(const char *path)
|
||||
@@ -215,8 +211,8 @@ public:
|
||||
std::uint64_t op0, std::uint64_t op1)
|
||||
-> packet::error_type override;
|
||||
|
||||
void set_fuse_uid_gid(remote::user_id /* uid */,
|
||||
remote::group_id /* gid */) override {}
|
||||
void set_fuse_uid_gid(remote::user_id /* uid */,
|
||||
remote::group_id /* gid */) override {}
|
||||
|
||||
// JSON Layer
|
||||
[[nodiscard]] auto json_create_directory_snapshot(const std::string &path,
|
||||
|
@@ -21,9 +21,6 @@
|
||||
*/
|
||||
#include "comm/packet/common.hpp"
|
||||
|
||||
#include "events/event_system.hpp"
|
||||
#include "events/types/packet_client_timeout.hpp"
|
||||
|
||||
namespace repertory::comm {
|
||||
non_blocking_guard::non_blocking_guard(boost::asio::ip::tcp::socket &sock_)
|
||||
: non_blocking(sock_.non_blocking()), sock(sock_) {
|
||||
@@ -83,50 +80,6 @@ void apply_common_socket_properties(boost::asio::ip::tcp::socket &sock) {
|
||||
sock.set_option(boost::asio::socket_base::keep_alive(true));
|
||||
}
|
||||
|
||||
template <class op_t>
|
||||
void run_with_deadline(boost::asio::io_context &io_ctx,
|
||||
boost::asio::ip::tcp::socket &sock, op_t &&operation,
|
||||
std::chrono::milliseconds deadline,
|
||||
std::string_view event_name,
|
||||
std::string_view function_name) {
|
||||
deadline = std::max(deadline, std::chrono::milliseconds{250});
|
||||
|
||||
bool done = false;
|
||||
bool timed_out = false;
|
||||
|
||||
boost::asio::steady_timer timer{io_ctx};
|
||||
timer.expires_after(deadline);
|
||||
timer.async_wait([&done, &sock, &timed_out](auto &&err_) {
|
||||
if (not err_ && not done) {
|
||||
timed_out = true;
|
||||
sock.cancel();
|
||||
}
|
||||
});
|
||||
|
||||
boost::system::error_code err{};
|
||||
operation([&done, &err](auto &&err_) {
|
||||
err = err_;
|
||||
done = true;
|
||||
});
|
||||
|
||||
io_ctx.restart();
|
||||
while (not done) {
|
||||
io_ctx.run_one();
|
||||
}
|
||||
timer.cancel();
|
||||
|
||||
if (timed_out) {
|
||||
repertory::event_system::instance().raise<repertory::packet_client_timeout>(
|
||||
std::string(event_name), std::string(function_name));
|
||||
throw std::runtime_error(std::string(event_name) + " timed-out");
|
||||
}
|
||||
|
||||
if (err) {
|
||||
throw std::runtime_error(std::string(event_name) + " failed|err|" +
|
||||
err.message());
|
||||
}
|
||||
}
|
||||
|
||||
void connect_with_deadline(
|
||||
boost::asio::io_context &io_ctx, boost::asio::ip::tcp::socket &sock,
|
||||
boost::asio::ip::basic_resolver<boost::asio::ip::tcp>::results_type
|
||||
@@ -140,7 +93,7 @@ void connect_with_deadline(
|
||||
boost::asio::async_connect(
|
||||
sock, endpoints, [handler](auto &&err, auto &&) { handler(err); });
|
||||
},
|
||||
deadline, "connect", function_name);
|
||||
deadline, "connect", std::string{function_name});
|
||||
}
|
||||
|
||||
void read_exact_with_deadline(boost::asio::io_context &io_ctx,
|
||||
@@ -167,7 +120,7 @@ void read_exact_with_deadline(boost::asio::io_context &io_ctx,
|
||||
handler(err);
|
||||
});
|
||||
},
|
||||
deadline, "read", function_name);
|
||||
deadline, "read", std::string{function_name});
|
||||
|
||||
if (bytes_read == 0U) {
|
||||
throw std::runtime_error("0 bytes read");
|
||||
@@ -200,7 +153,7 @@ void write_all_with_deadline(boost::asio::io_context &io_ctx,
|
||||
handler(err);
|
||||
});
|
||||
},
|
||||
deadline, "write", function_name);
|
||||
deadline, "write", std::string{function_name});
|
||||
|
||||
if (bytes_written == 0U) {
|
||||
throw std::runtime_error("0 bytes written");
|
||||
|
@@ -58,10 +58,11 @@ packet_client::~packet_client() {
|
||||
|
||||
void packet_client::close(client &cli) noexcept {
|
||||
boost::system::error_code err1;
|
||||
auto res = cli.socket.shutdown(boost::asio::socket_base::shutdown_both, err1);
|
||||
[[maybe_unused]] auto res =
|
||||
cli.socket.shutdown(boost::asio::socket_base::shutdown_both, err1);
|
||||
|
||||
boost::system::error_code err2;
|
||||
res = cli.socket.close(err2);
|
||||
[[maybe_unused]] auto res2 = cli.socket.close(err2);
|
||||
}
|
||||
|
||||
void packet_client::close_all() {
|
||||
@@ -244,8 +245,12 @@ auto packet_client::read_packet(client &cli, boost::asio::io_context &ctx,
|
||||
std::memcpy(&to_read, buffer.data(), sizeof(to_read));
|
||||
boost::endian::big_to_native_inplace(to_read);
|
||||
|
||||
if (to_read == 0U || to_read > comm::max_packet_bytes) {
|
||||
return utils::from_api_error(api_error::comm_error);
|
||||
if (to_read > comm::max_packet_bytes) {
|
||||
throw std::runtime_error(fmt::format("packet too large|size|{}", to_read));
|
||||
}
|
||||
|
||||
if (to_read < utils::encryption::encryption_header_size) {
|
||||
throw std::runtime_error(fmt::format("packet too small|size|{}", to_read));
|
||||
}
|
||||
|
||||
buffer.resize(to_read);
|
||||
|
@@ -289,6 +289,11 @@ void packet_server::read_packet(std::shared_ptr<connection> conn,
|
||||
fmt::format("packet too large|size|{}", data_size));
|
||||
}
|
||||
|
||||
if (data_size < utils::encryption::encryption_header_size) {
|
||||
throw std::runtime_error(
|
||||
fmt::format("packet too small|size|{}", data_size));
|
||||
}
|
||||
|
||||
auto should_send_response = true;
|
||||
auto response = std::make_shared<packet>();
|
||||
conn->buffer.resize(data_size);
|
||||
|
@@ -131,8 +131,7 @@ auto remote_server::fuse_chflags(const char *path, std::uint32_t /*flags*/)
|
||||
return ret;
|
||||
}
|
||||
|
||||
auto remote_server::fuse_chmod(const char *path,
|
||||
remote::file_mode /*mode*/)
|
||||
auto remote_server::fuse_chmod(const char *path, remote::file_mode /*mode*/)
|
||||
-> packet::error_type {
|
||||
REPERTORY_USES_FUNCTION_NAME();
|
||||
|
||||
@@ -142,10 +141,8 @@ auto remote_server::fuse_chmod(const char *path,
|
||||
return ret;
|
||||
}
|
||||
|
||||
auto remote_server::fuse_chown(const char *path,
|
||||
remote::user_id /*uid*/,
|
||||
remote::group_id /*gid*/)
|
||||
-> packet::error_type {
|
||||
auto remote_server::fuse_chown(const char *path, remote::user_id /*uid*/,
|
||||
remote::group_id /*gid*/) -> packet::error_type {
|
||||
REPERTORY_USES_FUNCTION_NAME();
|
||||
|
||||
auto file_path = construct_path(path);
|
||||
@@ -211,8 +208,7 @@ auto remote_server::fuse_fsetattr_x(const char *path,
|
||||
return ret;
|
||||
}
|
||||
|
||||
auto remote_server::fuse_fsync(const char *path,
|
||||
std::int32_t /*datasync*/,
|
||||
auto remote_server::fuse_fsync(const char *path, std::int32_t /*datasync*/,
|
||||
remote::file_handle handle)
|
||||
-> packet::error_type {
|
||||
REPERTORY_USES_FUNCTION_NAME();
|
||||
@@ -241,8 +237,7 @@ auto remote_server::fuse_fsync(const char *path,
|
||||
return ret;
|
||||
}
|
||||
|
||||
auto remote_server::fuse_ftruncate(const char *path,
|
||||
remote::file_offset size,
|
||||
auto remote_server::fuse_ftruncate(const char *path, remote::file_offset size,
|
||||
remote::file_handle handle)
|
||||
-> packet::error_type {
|
||||
REPERTORY_USES_FUNCTION_NAME();
|
||||
@@ -336,8 +331,7 @@ construct_path(path); auto ret = STATUS_NOT_IMPLEMENTED;
|
||||
return ret;
|
||||
}*/
|
||||
|
||||
auto remote_server::fuse_mkdir(const char *path,
|
||||
remote::file_mode /*mode*/)
|
||||
auto remote_server::fuse_mkdir(const char *path, remote::file_mode /*mode*/)
|
||||
-> packet::error_type {
|
||||
REPERTORY_USES_FUNCTION_NAME();
|
||||
|
||||
@@ -350,7 +344,7 @@ auto remote_server::fuse_mkdir(const char *path,
|
||||
return ret;
|
||||
}
|
||||
|
||||
auto remote_server::fuse_opendir(const char *path, remote::file_handle handle)
|
||||
auto remote_server::fuse_opendir(const char *path, remote::file_handle &handle)
|
||||
-> packet::error_type {
|
||||
REPERTORY_USES_FUNCTION_NAME();
|
||||
|
||||
@@ -377,7 +371,7 @@ auto remote_server::fuse_opendir(const char *path, remote::file_handle handle)
|
||||
|
||||
auto remote_server::fuse_create(const char *path, remote::file_mode mode,
|
||||
const remote::open_flags &flags,
|
||||
remote::file_handle handle)
|
||||
remote::file_handle &handle)
|
||||
-> packet::error_type {
|
||||
REPERTORY_USES_FUNCTION_NAME();
|
||||
|
||||
@@ -414,7 +408,7 @@ auto remote_server::fuse_create(const char *path, remote::file_mode mode,
|
||||
}
|
||||
|
||||
auto remote_server::fuse_open(const char *path, const remote::open_flags &flags,
|
||||
remote::file_handle handle)
|
||||
remote::file_handle &handle)
|
||||
-> packet::error_type {
|
||||
REPERTORY_USES_FUNCTION_NAME();
|
||||
|
||||
@@ -531,17 +525,17 @@ auto remote_server::fuse_write(const char *path, const char *buffer,
|
||||
return ret;
|
||||
}
|
||||
|
||||
auto remote_server::fuse_write_base64(
|
||||
const char * /*path*/, const char * /*buffer*/,
|
||||
remote::file_size /*write_size*/,
|
||||
remote::file_offset /*write_offset*/,
|
||||
remote::file_handle /*handle*/) -> packet::error_type {
|
||||
auto remote_server::fuse_write_base64(const char * /*path*/,
|
||||
const char * /*buffer*/,
|
||||
remote::file_size /*write_size*/,
|
||||
remote::file_offset /*write_offset*/,
|
||||
remote::file_handle /*handle*/)
|
||||
-> packet::error_type {
|
||||
// DOES NOTHING
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto remote_server::fuse_readdir(const char *path,
|
||||
remote::file_offset offset,
|
||||
auto remote_server::fuse_readdir(const char *path, remote::file_offset offset,
|
||||
remote::file_handle handle,
|
||||
std::string &item_path) -> packet::error_type {
|
||||
REPERTORY_USES_FUNCTION_NAME();
|
||||
@@ -623,7 +617,7 @@ auto remote_server::fuse_setattr_x(const char *path,
|
||||
}
|
||||
|
||||
auto remote_server::fuse_setbkuptime(const char *path,
|
||||
remote::file_time /*bkuptime*/)
|
||||
remote::file_time /*bkuptime*/)
|
||||
-> packet::error_type {
|
||||
REPERTORY_USES_FUNCTION_NAME();
|
||||
|
||||
@@ -634,7 +628,7 @@ auto remote_server::fuse_setbkuptime(const char *path,
|
||||
}
|
||||
|
||||
auto remote_server::fuse_setchgtime(const char *path,
|
||||
remote::file_time /*chgtime*/)
|
||||
remote::file_time /*chgtime*/)
|
||||
-> packet::error_type {
|
||||
REPERTORY_USES_FUNCTION_NAME();
|
||||
|
||||
@@ -645,7 +639,7 @@ auto remote_server::fuse_setchgtime(const char *path,
|
||||
}
|
||||
|
||||
auto remote_server::fuse_setcrtime(const char *path,
|
||||
remote::file_time /*crtime*/)
|
||||
remote::file_time /*crtime*/)
|
||||
-> packet::error_type {
|
||||
REPERTORY_USES_FUNCTION_NAME();
|
||||
|
||||
@@ -725,8 +719,7 @@ auto remote_server::fuse_statfs_x(const char *path, std::uint64_t bsize,
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto remote_server::fuse_truncate(const char *path,
|
||||
remote::file_offset size)
|
||||
auto remote_server::fuse_truncate(const char *path, remote::file_offset size)
|
||||
-> packet::error_type {
|
||||
REPERTORY_USES_FUNCTION_NAME();
|
||||
|
||||
|
@@ -330,7 +330,7 @@ auto winfsp_drive::Create(PWSTR file_name, UINT32 create_options,
|
||||
auto now = utils::time::get_time_now();
|
||||
auto meta = create_meta_attributes(
|
||||
now, attributes, now, now, (attributes & FILE_ATTRIBUTE_DIRECTORY) != 0U,
|
||||
0U, "", 0U, now, 0U, 0U, 0U,
|
||||
0U, "", 0U, now, 0U, 0U,
|
||||
(attributes & FILE_ATTRIBUTE_DIRECTORY) != 0U
|
||||
? ""
|
||||
: utils::path::combine(config_.get_cache_directory(),
|
||||
|
@@ -450,25 +450,8 @@ void ui_server::handle_put_mount_location(const httplib::Request &req,
|
||||
|
||||
void ui_server::handle_get_available_locations(httplib::Response &res) {
|
||||
#if defined(_WIN32)
|
||||
constexpr std::array<std::string_view, 26U> letters{
|
||||
"a:", "b:", "c:", "d:", "e:", "f:", "g:", "h:", "i:",
|
||||
"j:", "k:", "l:", "m:", "n:", "o:", "p:", "q:", "r:",
|
||||
"s:", "t:", "u:", "v:", "w:", "x:", "y:", "z:",
|
||||
};
|
||||
|
||||
auto available = std::accumulate(
|
||||
letters.begin(), letters.end(), std::vector<std::string_view>(),
|
||||
[](auto &&vec, auto &&letter) -> std::vector<std::string_view> {
|
||||
if (utils::file::directory{utils::path::combine(letter, {"\\"})}
|
||||
.exists()) {
|
||||
return vec;
|
||||
}
|
||||
|
||||
vec.emplace_back(letter);
|
||||
return vec;
|
||||
});
|
||||
|
||||
res.set_content(nlohmann::json(available).dump(), "application/json");
|
||||
res.set_content(nlohmann::json(utils::get_available_drive_letters()).dump(),
|
||||
"application/json");
|
||||
#else // !defined(_WIN32)
|
||||
res.set_content(nlohmann::json(std::vector<std::string_view>()).dump(),
|
||||
"application/json");
|
||||
|
@@ -332,14 +332,21 @@ protected:
|
||||
mount_location2 = mount_location;
|
||||
|
||||
#if defined(_WIN32)
|
||||
mount_location = utils::string::to_lower(std::string{"V:"});
|
||||
auto letter = utils::get_available_drive_letter('d');
|
||||
ASSERT_TRUE(letter.has_value());
|
||||
mount_location = utils::string::to_lower(std::string{letter.value()});
|
||||
#else // !defined(_WIN32)
|
||||
mount_location = utils::path::combine(test_dir, {"mount"});
|
||||
ASSERT_TRUE(utils::file::directory(mount_location).create_directory());
|
||||
#endif // defined(_WIN32)
|
||||
|
||||
auto cfg_dir2 = make_cfg_dir(test_dir, "cfg2");
|
||||
#if defined(_WIN32)
|
||||
auto config2 =
|
||||
std::make_unique<app_config>(provider_type::remote, cfg_dir2);
|
||||
#else // !defined(_WIN32)
|
||||
config2 = std::make_unique<app_config>(provider_type::remote, cfg_dir2);
|
||||
#endif // defined(_WIN32)
|
||||
config2->set_enable_drive_events(true);
|
||||
config2->set_event_level(event_level::trace);
|
||||
#if !defined(_WIN32)
|
||||
|
@@ -138,6 +138,89 @@ TYPED_TEST(fuse_test, directory_rmdir_on_non_empty_directory_should_fail) {
|
||||
this->rmdir_and_test(dir);
|
||||
this->rmdir_and_test(dir2);
|
||||
}
|
||||
|
||||
TYPED_TEST(fuse_test,
|
||||
directory_rmdir_open_directory_handle_then_readdir_and_closedir) {
|
||||
std::string dname{"rm_opendir"};
|
||||
auto dir = this->create_directory_and_test(dname);
|
||||
|
||||
auto *dir_ptr = ::opendir(dir.c_str());
|
||||
ASSERT_NE(dir_ptr, nullptr);
|
||||
|
||||
errno = 0;
|
||||
auto res = ::rmdir(dir.c_str());
|
||||
if (res == -1 && errno == EBUSY) {
|
||||
::closedir(dir_ptr);
|
||||
GTEST_SKIP();
|
||||
}
|
||||
ASSERT_EQ(0, res);
|
||||
|
||||
struct stat u_stat{};
|
||||
EXPECT_EQ(-1, ::stat(dir.c_str(), &u_stat));
|
||||
EXPECT_EQ(ENOENT, errno);
|
||||
|
||||
::rewinddir(dir_ptr);
|
||||
|
||||
errno = 0;
|
||||
auto *dir_entry = ::readdir(dir_ptr);
|
||||
EXPECT_EQ(nullptr, dir_entry);
|
||||
::closedir(dir_ptr);
|
||||
}
|
||||
|
||||
TYPED_TEST(fuse_test,
|
||||
directory_rmdir_open_directory_handle_non_empty_ENOTEMPTY) {
|
||||
std::string dname{"rm_opendir_ne"};
|
||||
auto dir = this->create_directory_and_test(dname);
|
||||
|
||||
std::string child{dname + "/child"};
|
||||
auto child_path = this->create_file_and_test(child, 0644);
|
||||
this->overwrite_text(child_path, "x");
|
||||
|
||||
auto *dir_ptr = ::opendir(dir.c_str());
|
||||
ASSERT_NE(dir_ptr, nullptr);
|
||||
|
||||
errno = 0;
|
||||
EXPECT_EQ(-1, ::rmdir(dir.c_str()));
|
||||
EXPECT_EQ(ENOTEMPTY, errno);
|
||||
|
||||
::rewinddir(dir_ptr);
|
||||
[[maybe_unused]] auto *dir_ptr2 = ::readdir(dir_ptr);
|
||||
::closedir(dir_ptr);
|
||||
|
||||
this->unlink_file_and_test(child_path);
|
||||
this->rmdir_and_test(dir);
|
||||
}
|
||||
|
||||
// TODO implement POSIX-compliant rmdir when handle is open
|
||||
// TYPED_TEST(fuse_test, directory_rmdir_open_directory_handle_fstat_dirfd_ok) {
|
||||
// std::string dname{"rm_opendir_fstat"};
|
||||
// auto dir = this->create_directory_and_test(dname);
|
||||
//
|
||||
// auto *dir_ptr = ::opendir(dir.c_str());
|
||||
// ASSERT_NE(dir_ptr, nullptr);
|
||||
// auto dfd = ::dirfd(dir_ptr);
|
||||
// ASSERT_NE(dfd, -1);
|
||||
//
|
||||
// struct stat before{};
|
||||
// ASSERT_EQ(0, ::fstat(dfd, &before));
|
||||
//
|
||||
// errno = 0;
|
||||
// auto res = ::rmdir(dir.c_str());
|
||||
// if (res == -1 && errno == EBUSY) {
|
||||
// ::closedir(dir_ptr);
|
||||
// GTEST_SKIP();
|
||||
// }
|
||||
// ASSERT_EQ(0, res);
|
||||
//
|
||||
// struct stat after{};
|
||||
// ASSERT_EQ(0, ::fstat(dfd, &after));
|
||||
//
|
||||
// ::closedir(dir_ptr);
|
||||
//
|
||||
// struct stat u_stat{};
|
||||
// EXPECT_EQ(-1, ::stat(dir.c_str(), &u_stat));
|
||||
// EXPECT_EQ(ENOENT, errno);
|
||||
// }
|
||||
} // namespace repertory
|
||||
|
||||
#endif // !defined(_WIN32)
|
||||
|
74
repertory/repertory_test/src/packet_comm_common_test.cpp
Normal file
74
repertory/repertory_test/src/packet_comm_common_test.cpp
Normal file
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
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 "test_common.hpp"
|
||||
|
||||
#include "comm/packet/common.hpp"
|
||||
|
||||
using namespace repertory;
|
||||
using namespace repertory::comm;
|
||||
using boost::asio::ip::tcp;
|
||||
|
||||
TEST(packet_comm_common_test, operation_completes_prior_to_timeout) {
|
||||
boost::asio::io_context io_ctx;
|
||||
tcp::socket sock(io_ctx);
|
||||
|
||||
std::atomic<bool> completed{false};
|
||||
|
||||
auto operation = [&](auto &&handler) {
|
||||
boost::asio::post(io_ctx, [&completed, handler]() {
|
||||
completed = true;
|
||||
handler(boost::system::error_code{});
|
||||
});
|
||||
};
|
||||
|
||||
EXPECT_NO_THROW(run_with_deadline(io_ctx, sock, operation,
|
||||
std::chrono::milliseconds(300), "read",
|
||||
"packet_deadline_test"));
|
||||
|
||||
EXPECT_TRUE(completed);
|
||||
|
||||
io_ctx.poll();
|
||||
}
|
||||
|
||||
TEST(packet_comm_common_test, timeout_completes_prior_to_operation) {
|
||||
boost::asio::io_context io_ctx;
|
||||
tcp::socket sock(io_ctx);
|
||||
|
||||
std::atomic<bool> completed{false};
|
||||
|
||||
auto operation = [&](auto &&handler) {
|
||||
auto delayed = std::make_shared<boost::asio::steady_timer>(io_ctx);
|
||||
delayed->expires_after(std::chrono::milliseconds(500));
|
||||
delayed->async_wait([&completed, delayed, handler](auto &&) {
|
||||
completed = true;
|
||||
handler(boost::system::error_code{});
|
||||
});
|
||||
};
|
||||
|
||||
EXPECT_THROW(run_with_deadline(io_ctx, sock, operation,
|
||||
std::chrono::milliseconds(300), "read",
|
||||
"packet_deadline_test"),
|
||||
std::runtime_error);
|
||||
|
||||
for (std::uint8_t idx = 0; idx < 80U && not completed; ++idx) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||
io_ctx.poll();
|
||||
}
|
||||
|
||||
EXPECT_TRUE(completed);
|
||||
}
|
@@ -30,6 +30,12 @@ void create_console();
|
||||
|
||||
void free_console();
|
||||
|
||||
[[nodiscard]] auto get_available_drive_letter(char first = 'a')
|
||||
-> std::optional<std::string_view>;
|
||||
|
||||
[[nodiscard]] auto get_available_drive_letters(char first = 'a')
|
||||
-> std::vector<std::string_view>;
|
||||
|
||||
[[nodiscard]] auto get_local_app_data_directory() -> const std::string &;
|
||||
|
||||
[[nodiscard]] auto get_last_error_code() -> DWORD;
|
||||
|
@@ -210,8 +210,7 @@ auto get_times(std::string_view path) -> std::optional<file_times> {
|
||||
ret.accessed =
|
||||
utils::time::windows_file_time_to_unix_time(times.at(1U));
|
||||
ret.changed = utils::time::windows_file_time_to_unix_time(times.at(2U));
|
||||
ret.created = =
|
||||
utils::time::windows_file_time_to_unix_time(times.at(0U));
|
||||
ret.created = utils::time::windows_file_time_to_unix_time(times.at(0U));
|
||||
ret.modified =
|
||||
utils::time::windows_file_time_to_unix_time(times.at(2U));
|
||||
ret.written = utils::time::windows_file_time_to_unix_time(times.at(2U));
|
||||
|
@@ -29,6 +29,14 @@
|
||||
#include "utils/path.hpp"
|
||||
#include "utils/string.hpp"
|
||||
|
||||
namespace {
|
||||
constexpr std::array<std::string_view, 26U> drive_letters{
|
||||
"a:", "b:", "c:", "d:", "e:", "f:", "g:", "h:", "i:",
|
||||
"j:", "k:", "l:", "m:", "n:", "o:", "p:", "q:", "r:",
|
||||
"s:", "t:", "u:", "v:", "w:", "x:", "y:", "z:",
|
||||
};
|
||||
}
|
||||
|
||||
namespace repertory::utils {
|
||||
void create_console() {
|
||||
if (::AllocConsole() == 0) {
|
||||
@@ -61,6 +69,47 @@ void create_console() {
|
||||
|
||||
void free_console() { ::FreeConsole(); }
|
||||
|
||||
auto get_available_drive_letter(char first) -> std::optional<std::string_view> {
|
||||
const auto *begin = std::ranges::find_if(
|
||||
drive_letters, [first](auto &&val) { return val.at(0U) == first; });
|
||||
if (begin == drive_letters.end()) {
|
||||
begin = drive_letters.begin();
|
||||
}
|
||||
|
||||
auto available =
|
||||
std::ranges::find_if(begin, drive_letters.end(), [](auto &&val) -> bool {
|
||||
return not utils::file::directory{utils::path::combine(val, {"\\"})}
|
||||
.exists();
|
||||
});
|
||||
if (available == drive_letters.end()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return *available;
|
||||
}
|
||||
|
||||
auto get_available_drive_letters(char first) -> std::vector<std::string_view> {
|
||||
const auto *begin =
|
||||
std::ranges::find_if(drive_letters, [first](auto &&val) -> bool {
|
||||
return val.at(0U) == first;
|
||||
});
|
||||
if (begin == drive_letters.end()) {
|
||||
begin = drive_letters.begin();
|
||||
}
|
||||
|
||||
return std::accumulate(
|
||||
begin, drive_letters.end(), std::vector<std::string_view>(),
|
||||
[](auto &&vec, auto &&letter) -> auto {
|
||||
if (utils::file::directory{utils::path::combine(letter, {"\\"})}
|
||||
.exists()) {
|
||||
return vec;
|
||||
}
|
||||
|
||||
vec.emplace_back(letter);
|
||||
return vec;
|
||||
});
|
||||
}
|
||||
|
||||
auto get_last_error_code() -> DWORD { return ::GetLastError(); }
|
||||
|
||||
auto get_local_app_data_directory() -> const std::string & {
|
||||
|
Reference in New Issue
Block a user