21 Commits

Author SHA1 Message Date
73ec7f2252 winfsp unit tests and fixes
Some checks failed
BlockStorage/repertory/pipeline/head There was a failure building this commit
2024-11-08 14:49:22 -06:00
5450ffc280 winfsp unit tests and fixes 2024-11-08 14:41:59 -06:00
a9125196ce winfsp unit tests and fixes 2024-11-08 14:35:55 -06:00
ab9765582a winfsp unit tests and fixes 2024-11-08 14:27:10 -06:00
060b2b70dc winfsp unit tests and fixes 2024-11-08 13:10:21 -06:00
739a31538f winfsp unit tests and fixes 2024-11-08 13:05:53 -06:00
67595b4d45 winfsp unit tests and fixes 2024-11-08 13:04:15 -06:00
c5003e0ee6 winfsp unit tests and fixes 2024-11-08 13:01:28 -06:00
984657a5dd fix 2024-11-08 12:41:21 -06:00
2930933f19 winfsp unit tests and fixes 2024-11-08 12:37:15 -06:00
72a6f5ae08 refactor 2024-11-08 12:33:38 -06:00
7a96a8cbf3 winfsp unit tests and fixes 2024-11-08 12:32:00 -06:00
0b4befd823 winfsp unit tests and fixes 2024-11-08 12:26:48 -06:00
151b6775b0 winfsp unit tests and fixes 2024-11-08 12:24:01 -06:00
47a6bdbcd2 winfsp unit tests and fixes 2024-11-08 11:53:21 -06:00
20ab95380a winfsp unit tests and fixes 2024-11-08 11:09:03 -06:00
73afdaedb9 winfsp unit tests and fixes 2024-11-08 10:54:23 -06:00
a231c2afaf fuse unit tests and fixes 2024-11-08 10:32:37 -06:00
74109d1195 fuse unit tests and fixes 2024-11-08 10:31:20 -06:00
2bd847b833 winfsp unit tests and fixes 2024-11-08 10:17:43 -06:00
2d74fb30de remote fixes 2024-11-08 09:40:56 -06:00
20 changed files with 1620 additions and 860 deletions

View File

@ -33,25 +33,32 @@ protected:
virtual ~remote_open_file_table() = default; virtual ~remote_open_file_table() = default;
protected: protected:
struct compat_open_info { struct compat_open_info final {
std::size_t count{0U};
std::string client_id; std::string client_id;
std::vector<remote::file_handle> handles;
std::string path; std::string path;
}; };
struct open_info { struct open_info final {
std::size_t count{0U};
std::string client_id; std::string client_id;
PVOID directory_buffer{nullptr}; PVOID directory_buffer{nullptr};
std::vector<native_handle> handles;
std::string path; std::string path;
}; };
private: private:
std::unordered_map<remote::file_handle, compat_open_info> compat_lookup_; std::unordered_map<std::string, std::unique_ptr<compat_open_info>>
std::recursive_mutex compat_mutex_; compat_file_lookup_;
std::unordered_map<remote::file_handle, std::string> compat_handle_lookup_;
private:
std::unordered_map<std::string, std::vector<std::uint64_t>> directory_lookup_; std::unordered_map<std::string, std::vector<std::uint64_t>> directory_lookup_;
std::recursive_mutex directory_mutex_;
std::unordered_map<native_handle, open_info> file_lookup_; private:
std::unordered_map<std::string, std::unique_ptr<open_info>> file_lookup_;
std::unordered_map<native_handle, std::string> handle_lookup_;
private:
mutable std::recursive_mutex file_mutex_; mutable std::recursive_mutex file_mutex_;
protected: protected:
@ -64,11 +71,11 @@ protected:
PVOID *&buffer) -> bool; PVOID *&buffer) -> bool;
#endif // _WIN32 #endif // _WIN32
[[nodiscard]] auto [[nodiscard]] auto get_open_file_path(const native_handle &handle)
get_open_file_path(const native_handle &handle) -> std::string; -> std::string;
[[nodiscard]] auto get_open_info(const native_handle &handle, [[nodiscard]] auto get_open_info(const native_handle &handle, open_info &oi)
open_info &oi) -> bool; -> bool;
[[nodiscard]] auto has_open_directory(const std::string &client_id, [[nodiscard]] auto has_open_directory(const std::string &client_id,
std::uint64_t handle) -> bool; std::uint64_t handle) -> bool;
@ -77,20 +84,21 @@ protected:
int error_return) -> int; int error_return) -> int;
template <typename error_type> template <typename error_type>
[[nodiscard]] auto [[nodiscard]] auto has_open_info(const native_handle &handle,
has_open_info(const native_handle &handle, const error_type &error_return)
const error_type &error_return) -> error_type { -> error_type {
recur_mutex_lock file_lock(file_mutex_); recur_mutex_lock file_lock(file_mutex_);
return ((file_lookup_.find(handle) == file_lookup_.end()) ? error_return return handle_lookup_.contains(handle) ? 0 : error_return;
: 0);
} }
void remove_all(const std::string &file_path); void remove_all(const std::string &file_path);
void remove_and_close_all(const native_handle &handle);
void remove_compat_open_info(const remote::file_handle &handle); void remove_compat_open_info(const remote::file_handle &handle);
auto remove_directory(const std::string &client_id, auto remove_directory(const std::string &client_id, std::uint64_t handle)
std::uint64_t handle) -> bool; -> bool;
void remove_open_info(const native_handle &handle); void remove_open_info(const native_handle &handle);
@ -102,11 +110,11 @@ protected:
void set_compat_open_info(const remote::file_handle &handle, void set_compat_open_info(const remote::file_handle &handle,
const std::string &file_path); const std::string &file_path);
void set_open_info(const native_handle &handle, open_info oi); void set_open_info(const native_handle &handle, open_info op_info);
public: public:
[[nodiscard]] auto [[nodiscard]] auto get_open_file_count(const std::string &file_path) const
get_open_file_count(const std::string &file_path) const -> std::size_t; -> std::size_t;
}; };
} // namespace repertory } // namespace repertory

View File

@ -137,12 +137,12 @@ public:
if (ret == STATUS_SUCCESS) { if (ret == STATUS_SUCCESS) {
#if defined(_WIN32) #if defined(_WIN32)
this->set_client_id(file_desc, client_id); this->set_client_id(file_desc, client_id);
#else #else // !defined(_WIN32)
this->set_client_id( this->set_client_id(
static_cast<native_handle>( static_cast<native_handle>(
reinterpret_cast<std::uintptr_t>(file_desc)), reinterpret_cast<std::uintptr_t>(file_desc)),
client_id); client_id);
#endif #endif // defined(_WIN32)
response.encode(file_desc); response.encode(file_desc);
response.encode(file_info); response.encode(file_info);
response.encode(normalized_name); response.encode(normalized_name);
@ -277,12 +277,12 @@ public:
if (ret == STATUS_SUCCESS) { if (ret == STATUS_SUCCESS) {
#if defined(_WIN32) #if defined(_WIN32)
this->set_client_id(file_desc, client_id); this->set_client_id(file_desc, client_id);
#else #else // !defined(_WIN32)
this->set_client_id( this->set_client_id(
static_cast<native_handle>( static_cast<native_handle>(
reinterpret_cast<std::uintptr_t>(file_desc)), reinterpret_cast<std::uintptr_t>(file_desc)),
client_id); client_id);
#endif #endif // defined(_WIN32)
response.encode(file_desc); response.encode(file_desc);
response.encode(file_info); response.encode(file_info);
response.encode(normalized_name); response.encode(normalized_name);
@ -585,9 +585,9 @@ public:
0) { 0) {
#if defined(_WIN32) #if defined(_WIN32)
this->set_compat_client_id(handle, client_id); this->set_compat_client_id(handle, client_id);
#else #else // !defined(_WIN32)
this->set_client_id(static_cast<native_handle>(handle), client_id); this->set_client_id(static_cast<native_handle>(handle), client_id);
#endif #endif // defined(_WIN32)
response.encode(handle); response.encode(handle);
} }
return ret; return ret;
@ -841,9 +841,9 @@ public:
if ((ret = this->fuse_open(path.c_str(), flags, handle)) >= 0) { if ((ret = this->fuse_open(path.c_str(), flags, handle)) >= 0) {
#if defined(_WIN32) #if defined(_WIN32)
this->set_compat_client_id(handle, client_id); this->set_compat_client_id(handle, client_id);
#else #else // !defined(_WIN32)
this->set_client_id(static_cast<native_handle>(handle), client_id); this->set_client_id(static_cast<native_handle>(handle), client_id);
#endif #endif // defined(_WIN32)
response.encode(handle); response.encode(handle);
} }
return ret; return ret;

View File

@ -24,12 +24,13 @@
#include "events/event_system.hpp" #include "events/event_system.hpp"
#include "events/events.hpp" #include "events/events.hpp"
#include "utils/collection.hpp" #include "utils/collection.hpp"
#include "utils/config.hpp"
#include "utils/utils.hpp" #include "utils/utils.hpp"
namespace repertory { namespace repertory {
void remote_open_file_table::add_directory(const std::string &client_id, void remote_open_file_table::add_directory(const std::string &client_id,
std::uint64_t handle) { std::uint64_t handle) {
recur_mutex_lock directory_lock(directory_mutex_); recur_mutex_lock lock(file_mutex_);
auto &list = directory_lookup_[client_id]; auto &list = directory_lookup_[client_id];
if (utils::collection::excludes(list, handle)) { if (utils::collection::excludes(list, handle)) {
directory_lookup_[client_id].emplace_back(handle); directory_lookup_[client_id].emplace_back(handle);
@ -37,14 +38,32 @@ void remote_open_file_table::add_directory(const std::string &client_id,
} }
void remote_open_file_table::close_all(const std::string &client_id) { void remote_open_file_table::close_all(const std::string &client_id) {
std::vector<remote::file_handle> compat_handles; unique_recur_mutex_lock lock(file_mutex_);
unique_recur_mutex_lock compat_lock(compat_mutex_); auto compat_handles =
for (auto &&kv : compat_lookup_) { std::accumulate(compat_file_lookup_.begin(), compat_file_lookup_.end(),
if (kv.second.client_id == client_id) { std::vector<remote::file_handle>(),
compat_handles.emplace_back(kv.first); [&client_id](auto &&list, auto &&value) {
} auto &&op_info = value.second;
} if (op_info->client_id == client_id) {
compat_lock.unlock(); list.insert(list.end(), op_info->handles.begin(),
op_info->handles.end());
}
return list;
});
auto handles = std::accumulate(
file_lookup_.begin(), file_lookup_.end(), std::vector<native_handle>(),
[&client_id](auto &&list, auto &&value) {
auto &&op_info = value.second;
if (op_info->client_id == client_id) {
list.insert(list.end(), op_info->handles.begin(),
op_info->handles.end());
}
return list;
});
lock.unlock();
for (auto &&handle : compat_handles) { for (auto &&handle : compat_handles) {
#if defined(_WIN32) #if defined(_WIN32)
@ -55,32 +74,23 @@ void remote_open_file_table::close_all(const std::string &client_id) {
remove_compat_open_info(handle); remove_compat_open_info(handle);
} }
std::vector<native_handle> handles;
unique_recur_mutex_lock file_lock(file_mutex_);
for (auto &&kv : file_lookup_) {
if (kv.second.client_id == client_id) {
handles.emplace_back(kv.first);
}
}
file_lock.unlock();
for (auto &&handle : handles) { for (auto &&handle : handles) {
#if defined(_WIN32) #if defined(_WIN32)
::CloseHandle(handle); ::CloseHandle(handle);
#else #else // !defined(_WIN32)
close(handle); close(handle);
#endif #endif // defined(_WIN32)
remove_open_info(handle); remove_open_info(handle);
} }
std::vector<std::uint64_t> dirs; std::vector<std::uint64_t> dirs;
unique_recur_mutex_lock directory_lock(directory_mutex_); lock.lock();
for (auto &&kv : directory_lookup_) { for (auto &&kv : directory_lookup_) {
if (kv.first == client_id) { if (kv.first == client_id) {
dirs.insert(dirs.end(), kv.second.begin(), kv.second.end()); dirs.insert(dirs.end(), kv.second.begin(), kv.second.end());
} }
} }
directory_lock.unlock(); lock.unlock();
for (auto &&dir : dirs) { for (auto &&dir : dirs) {
remove_directory(client_id, dir); remove_directory(client_id, dir);
@ -90,123 +100,123 @@ void remote_open_file_table::close_all(const std::string &client_id) {
#if defined(_WIN32) #if defined(_WIN32)
auto remote_open_file_table::get_directory_buffer(const native_handle &handle, auto remote_open_file_table::get_directory_buffer(const native_handle &handle,
PVOID *&buffer) -> bool { PVOID *&buffer) -> bool {
recur_mutex_lock file_lock(file_mutex_); recur_mutex_lock lock(file_mutex_);
if (file_lookup_.find(handle) != file_lookup_.end()) { if (not handle_lookup_.contains(handle)) {
buffer = &file_lookup_[handle].directory_buffer; return false;
return true;
} }
return false;
buffer = &file_lookup_.at(handle_lookup_.at(handle))->directory_buffer;
return true;
} }
#endif #endif // defined(_WIN32)
auto remote_open_file_table::get_open_file_count( auto remote_open_file_table::get_open_file_count(
const std::string &file_path) const -> std::size_t { const std::string &file_path) const -> std::size_t {
unique_recur_mutex_lock file_lock(file_mutex_); recur_mutex_lock lock(file_mutex_);
const auto count = std::accumulate( return (file_lookup_.contains(file_path)
file_lookup_.cbegin(), file_lookup_.cend(), std::size_t(0U), ? file_lookup_.at(file_path)->handles.size()
[&file_path](std::size_t total, const auto &kv) -> std::size_t { : 0ULL) +
if (kv.second.path == file_path) { (compat_file_lookup_.contains(file_path)
return ++total; ? compat_file_lookup_.at(file_path)->handles.size()
} : 0ULL);
return total;
});
return std::accumulate(
compat_lookup_.cbegin(), compat_lookup_.cend(), count,
[&file_path](std::size_t total, const auto &kv) -> std::size_t {
if (kv.second.path == file_path) {
return ++total;
}
return total;
});
} }
auto remote_open_file_table::get_open_info(const native_handle &handle, auto remote_open_file_table::get_open_info(const native_handle &handle,
open_info &oi) -> bool { open_info &oi) -> bool {
recur_mutex_lock file_lock(file_mutex_); recur_mutex_lock lock(file_mutex_);
if (file_lookup_.find(handle) != file_lookup_.end()) { if (not handle_lookup_.contains(handle)) {
oi = file_lookup_[handle]; return false;
return true;
} }
return false;
oi = *file_lookup_.at(handle_lookup_.at(handle)).get();
return true;
} }
auto remote_open_file_table::get_open_file_path(const native_handle &handle) auto remote_open_file_table::get_open_file_path(const native_handle &handle)
-> std::string { -> std::string {
recur_mutex_lock file_lock(file_mutex_); recur_mutex_lock lock(file_mutex_);
if (file_lookup_.find(handle) != file_lookup_.end()) { if (not handle_lookup_.contains(handle)) {
return file_lookup_[handle].path; return "";
} }
return ""; return file_lookup_.at(handle_lookup_.at(handle))->path;
} }
auto remote_open_file_table::has_open_directory(const std::string &client_id, auto remote_open_file_table::has_open_directory(const std::string &client_id,
std::uint64_t handle) -> bool { std::uint64_t handle) -> bool {
recur_mutex_lock directory_lock(directory_mutex_); recur_mutex_lock lock(file_mutex_);
auto &list = directory_lookup_[client_id]; return (utils::collection::includes(directory_lookup_[client_id], handle));
return (utils::collection::includes(list, handle));
} }
auto remote_open_file_table::has_compat_open_info( auto remote_open_file_table::has_compat_open_info(
const remote::file_handle &handle, int error_return) -> int { const remote::file_handle &handle, int error_return) -> int {
recur_mutex_lock compat_lock(compat_mutex_); recur_mutex_lock compat_lock(file_mutex_);
const auto res = auto res = compat_handle_lookup_.contains(handle) ? 0 : -1;
((compat_lookup_.find(handle) == compat_lookup_.end()) ? -1 : 0);
if (res == -1) { if (res == -1) {
errno = error_return; errno = error_return;
} }
return res; return res;
} }
void remote_open_file_table::remove_all(const std::string &file_path) { void remote_open_file_table::remove_all(const std::string &file_path) {
unique_recur_mutex_lock file_lock(file_mutex_); unique_recur_mutex_lock lock(file_mutex_);
const auto open_list = std::accumulate( auto compat_open_list = std::accumulate(
file_lookup_.begin(), file_lookup_.end(), std::vector<native_handle>(), compat_file_lookup_.begin(), compat_file_lookup_.end(),
[&file_path](std::vector<native_handle> v,
const auto &kv) -> std::vector<native_handle> {
if (kv.second.path == file_path) {
v.emplace_back(kv.first);
}
return v;
});
const auto compat_open_list = std::accumulate(
compat_lookup_.begin(), compat_lookup_.end(),
std::vector<remote::file_handle>(), std::vector<remote::file_handle>(),
[&file_path](std::vector<remote::file_handle> v, [&file_path](auto &&list, auto &&kv) -> std::vector<remote::file_handle> {
const auto &kv) -> std::vector<remote::file_handle> { if (kv.first == file_path) {
if (kv.second.path == file_path) { auto *op_info = kv.second.get();
v.emplace_back(kv.first); list.insert(list.end(), op_info->handles.begin(),
op_info->handles.end());
} }
return v; return list;
}); });
file_lock.unlock();
for (auto &&handle : open_list) { auto open_list = std::accumulate(
remove_open_info(handle); file_lookup_.begin(), file_lookup_.end(), std::vector<native_handle>(),
} [&file_path](auto &&list, auto &&kv) -> std::vector<native_handle> {
if (kv.first == file_path) {
auto *op_info = kv.second.get();
list.insert(list.end(), op_info->handles.begin(),
op_info->handles.end());
}
return list;
});
lock.unlock();
for (auto &&handle : compat_open_list) { for (auto &&handle : compat_open_list) {
remove_compat_open_info(handle); remove_compat_open_info(handle);
} }
for (auto &&handle : open_list) {
remove_open_info(handle);
}
} }
void remote_open_file_table::remove_compat_open_info( void remote_open_file_table::remove_compat_open_info(
const remote::file_handle &handle) { const remote::file_handle &handle) {
recur_mutex_lock compat_lock(compat_mutex_); recur_mutex_lock compat_lock(file_mutex_);
if (compat_lookup_[handle].count > 0) { if (not compat_handle_lookup_.contains(handle)) {
compat_lookup_[handle].count--; return;
} }
if (not compat_lookup_[handle].count) { auto *op_info =
compat_lookup_.erase(handle); compat_file_lookup_.at(compat_handle_lookup_.at(handle)).get();
utils::collection::remove_element(op_info->handles, handle);
compat_handle_lookup_.erase(handle);
if (not op_info->handles.empty()) {
return;
} }
auto path = op_info->path;
compat_file_lookup_.erase(path);
} }
auto remote_open_file_table::remove_directory(const std::string &client_id, auto remote_open_file_table::remove_directory(const std::string &client_id,
std::uint64_t handle) -> bool { std::uint64_t handle) -> bool {
recur_mutex_lock directory_lock(directory_mutex_); recur_mutex_lock lock(file_mutex_);
auto &list = directory_lookup_[client_id]; auto &list = directory_lookup_[client_id];
if (utils::collection::includes(list, handle)) { if (utils::collection::includes(list, handle)) {
utils::collection::remove_element(list, handle); utils::collection::remove_element(list, handle);
@ -220,48 +230,93 @@ auto remote_open_file_table::remove_directory(const std::string &client_id,
} }
void remote_open_file_table::remove_open_info(const native_handle &handle) { void remote_open_file_table::remove_open_info(const native_handle &handle) {
recur_mutex_lock file_lock(file_mutex_); recur_mutex_lock lock(file_mutex_);
if (file_lookup_[handle].count > 0) { if (not handle_lookup_.contains(handle)) {
file_lookup_[handle].count--; return;
} }
if (not file_lookup_[handle].count) {
auto *op_info = file_lookup_.at(handle_lookup_.at(handle)).get();
utils::collection::remove_element(op_info->handles, handle);
handle_lookup_.erase(handle);
if (not op_info->handles.empty()) {
return;
}
#if defined(_WIN32) #if defined(_WIN32)
if (file_lookup_[handle].directory_buffer) { if (op_info->directory_buffer) {
FspFileSystemDeleteDirectoryBuffer( FspFileSystemDeleteDirectoryBuffer(&op_info->directory_buffer);
&file_lookup_[handle].directory_buffer); }
}
#endif #endif
file_lookup_.erase(handle);
auto path = op_info->path;
file_lookup_.erase(path);
}
void remote_open_file_table::remove_and_close_all(const native_handle &handle) {
unique_recur_mutex_lock lock(file_mutex_);
if (not handle_lookup_.contains(handle)) {
return;
}
auto op_info = *file_lookup_.at(handle_lookup_.at(handle));
lock.unlock();
for (auto &&open_handle : op_info.handles) {
#if defined(_WIN32)
::CloseHandle(open_handle);
#else // !defined(_WIN32)
close(open_handle);
#endif // defined(_WIN32)
remove_open_info(open_handle);
} }
} }
void remote_open_file_table::set_compat_client_id( void remote_open_file_table::set_compat_client_id(
const remote::file_handle &handle, const std::string &client_id) { const remote::file_handle &handle, const std::string &client_id) {
recur_mutex_lock compat_lock(compat_mutex_); recur_mutex_lock compat_lock(file_mutex_);
compat_lookup_[handle].client_id = client_id; compat_file_lookup_.at(compat_handle_lookup_.at(handle))->client_id =
client_id;
} }
void remote_open_file_table::set_client_id(const native_handle &handle, void remote_open_file_table::set_client_id(const native_handle &handle,
const std::string &client_id) { const std::string &client_id) {
recur_mutex_lock file_lock(file_mutex_); recur_mutex_lock lock(file_mutex_);
file_lookup_[handle].client_id = client_id; file_lookup_.at(handle_lookup_.at(handle))->client_id = client_id;
} }
void remote_open_file_table::set_compat_open_info( void remote_open_file_table::set_compat_open_info(
const remote::file_handle &handle, const std::string &file_path) { const remote::file_handle &handle, const std::string &file_path) {
recur_mutex_lock compat_lock(compat_mutex_); recur_mutex_lock compat_lock(file_mutex_);
if (compat_lookup_.find(handle) == compat_lookup_.end()) { if (compat_handle_lookup_.contains(handle)) {
compat_lookup_[handle] = {0, "", file_path}; return;
} }
compat_lookup_[handle].count++;
if (not compat_file_lookup_.contains(file_path)) {
compat_file_lookup_[file_path] =
std::make_unique<compat_open_info>(compat_open_info{
"",
{},
file_path,
});
}
compat_handle_lookup_[handle] = file_path;
compat_file_lookup_.at(file_path)->handles.emplace_back(handle);
} }
void remote_open_file_table::set_open_info(const native_handle &handle, void remote_open_file_table::set_open_info(const native_handle &handle,
open_info oi) { open_info op_info) {
recur_mutex_lock file_lock(file_mutex_); recur_mutex_lock lock(file_mutex_);
if (file_lookup_.find(handle) == file_lookup_.end()) { if (handle_lookup_.contains(handle)) {
file_lookup_[handle] = std::move(oi); return;
} }
file_lookup_[handle].count++;
if (not file_lookup_.contains(op_info.path)) {
file_lookup_[op_info.path] = std::make_unique<open_info>(op_info);
}
handle_lookup_[handle] = op_info.path;
file_lookup_.at(op_info.path)->handles.emplace_back(handle);
} }
} // namespace repertory } // namespace repertory

View File

@ -160,10 +160,12 @@ auto remote_client::winfsp_cleanup(PVOID file_desc, PWSTR file_name,
auto ret{ auto ret{
packet_client_.send(function_name, request, response, service_flags), packet_client_.send(function_name, request, response, service_flags),
}; };
DECODE_OR_IGNORE(&response, was_closed); DECODE_OR_IGNORE(&response, was_closed);
if (was_closed != 0U) { if (was_closed != 0U) {
remove_all(file_path); remove_all(file_path);
} }
RAISE_REMOTE_WINFSP_CLIENT_EVENT(function_name, file_path, ret); RAISE_REMOTE_WINFSP_CLIENT_EVENT(function_name, file_path, ret);
return ret; return ret;
} }
@ -222,11 +224,16 @@ auto remote_client::winfsp_create(PWSTR file_name, UINT32 create_options,
DECODE_OR_IGNORE(&response, *file_info); DECODE_OR_IGNORE(&response, *file_info);
DECODE_OR_IGNORE(&response, normalized_name); DECODE_OR_IGNORE(&response, normalized_name);
DECODE_OR_IGNORE(&response, exists); DECODE_OR_IGNORE(&response, exists);
if (ret == STATUS_SUCCESS) { if (ret == STATUS_SUCCESS) {
*file_desc = reinterpret_cast<PVOID>(handle); *file_desc = reinterpret_cast<PVOID>(handle);
set_open_info( set_open_info(to_handle(*file_desc),
to_handle(*file_desc), open_info{
open_info{0, "", nullptr, utils::string::to_utf8(file_name)}); "",
nullptr,
{},
utils::string::to_utf8(file_name),
});
#if defined(_WIN32) #if defined(_WIN32)
if (exists) { if (exists) {
::SetLastError(ERROR_ALREADY_EXISTS); ::SetLastError(ERROR_ALREADY_EXISTS);
@ -390,9 +397,13 @@ auto remote_client::winfsp_open(PWSTR file_name, UINT32 create_options,
if (ret == STATUS_SUCCESS) { if (ret == STATUS_SUCCESS) {
*file_desc = reinterpret_cast<PVOID>(handle); *file_desc = reinterpret_cast<PVOID>(handle);
set_open_info( set_open_info(to_handle(*file_desc),
to_handle(*file_desc), open_info{
open_info{0, "", nullptr, utils::string::to_utf8(file_name)}); "",
nullptr,
{},
utils::string::to_utf8(file_name),
});
} }
} }

View File

@ -945,12 +945,12 @@ auto remote_server::winfsp_cleanup(PVOID file_desc, PWSTR /*file_name*/,
auto *handle = reinterpret_cast<HANDLE>(file_desc); auto *handle = reinterpret_cast<HANDLE>(file_desc);
auto ret = has_open_info(handle, STATUS_INVALID_HANDLE); auto ret = has_open_info(handle, STATUS_INVALID_HANDLE);
if (ret == STATUS_SUCCESS) { if (ret == STATUS_SUCCESS) {
if ((flags & FileSystemBase::CleanupDelete) != 0U) { if ((flags & FspCleanupDelete) != 0U) {
::CloseHandle(handle); remove_and_close_all(file_desc);
remove_open_info(file_desc);
was_closed = TRUE; was_closed = TRUE;
} }
} }
RAISE_REMOTE_WINFSP_SERVER_EVENT(function_name, file_path, ret); RAISE_REMOTE_WINFSP_SERVER_EVENT(function_name, file_path, ret);
return ret; return ret;
} }
@ -1005,8 +1005,12 @@ auto remote_server::winfsp_create(PWSTR file_name, UINT32 create_options,
if (handle != INVALID_HANDLE_VALUE) { if (handle != INVALID_HANDLE_VALUE) {
*file_desc = reinterpret_cast<PVOID>(handle); *file_desc = reinterpret_cast<PVOID>(handle);
normalized_name = utils::string::to_utf8(file_name); normalized_name = utils::string::to_utf8(file_name);
set_open_info(*file_desc, set_open_info(*file_desc, open_info{
open_info{0, "", nullptr, utils::string::to_utf8(file_path)}); "",
nullptr,
{},
utils::string::to_utf8(file_path),
});
} }
auto ret = auto ret =
@ -1140,8 +1144,12 @@ auto remote_server::winfsp_open(PWSTR file_name, UINT32 create_options,
if (handle != INVALID_HANDLE_VALUE) { if (handle != INVALID_HANDLE_VALUE) {
*file_desc = reinterpret_cast<PVOID>(handle); *file_desc = reinterpret_cast<PVOID>(handle);
normalized_name = utils::string::to_utf8(file_name); normalized_name = utils::string::to_utf8(file_name);
set_open_info(*file_desc, set_open_info(*file_desc, open_info{
open_info{0, "", nullptr, utils::string::to_utf8(file_path)}); "",
nullptr,
{},
utils::string::to_utf8(file_path),
});
} }
auto ret = auto ret =

View File

@ -213,9 +213,10 @@ auto remote_winfsp_drive::Init(PVOID host) -> NTSTATUS {
} }
file_system_host->SetCasePreservedNames(TRUE); file_system_host->SetCasePreservedNames(TRUE);
file_system_host->SetCaseSensitiveSearch(FALSE); file_system_host->SetCaseSensitiveSearch(TRUE);
file_system_host->SetFileInfoTimeout(1000); file_system_host->SetFileInfoTimeout(1000);
file_system_host->SetFlushAndPurgeOnCleanup(TRUE); file_system_host->SetFlushAndPurgeOnCleanup(TRUE);
file_system_host->SetMaxComponentLength(255U);
file_system_host->SetNamedStreams(FALSE); file_system_host->SetNamedStreams(FALSE);
file_system_host->SetPassQueryDirectoryPattern(FALSE); file_system_host->SetPassQueryDirectoryPattern(FALSE);
file_system_host->SetPersistentAcls(FALSE); file_system_host->SetPersistentAcls(FALSE);

View File

@ -203,15 +203,15 @@ VOID winfsp_drive::Cleanup(PVOID file_node, PVOID file_desc,
FspFileSystemDeleteDirectoryBuffer(&directory_buffer); FspFileSystemDeleteDirectoryBuffer(&directory_buffer);
} }
if (directory) { if (not directory) {
if (provider_.get_directory_item_count(api_path) == 0) { return handle_error(fm_->remove_file(api_path));
return handle_error(provider_.remove_directory(api_path));
}
return handle_error(api_error::directory_not_empty);
} }
return handle_error(fm_->remove_file(api_path)); if (provider_.get_directory_item_count(api_path) == 0) {
return handle_error(provider_.remove_directory(api_path));
}
return handle_error(api_error::directory_not_empty);
} }
if (((flags & FspCleanupSetArchiveBit) != 0U) && not directory) { if (((flags & FspCleanupSetArchiveBit) != 0U) && not directory) {

View File

@ -73,6 +73,7 @@ protected:
}); });
mount_location = utils::path::combine(test_directory, {"mount"}); mount_location = utils::path::combine(test_directory, {"mount"});
ASSERT_TRUE(utils::file::directory(mount_location).create_directory()); ASSERT_TRUE(utils::file::directory(mount_location).create_directory());
cfg_directory = utils::path::combine(test_directory, {"cfg"}); cfg_directory = utils::path::combine(test_directory, {"cfg"});
@ -170,6 +171,17 @@ public:
return file_path; return file_path;
} }
static auto create_directory_and_test(std::string &dir_name) -> std::string {
dir_name += std::to_string(++idx);
auto dir_path = utils::path::combine(mount_location, {dir_name});
EXPECT_TRUE(utils::file::directory(dir_path).create_directory());
EXPECT_TRUE(utils::file::directory(dir_path).exists());
EXPECT_FALSE(utils::file::file(dir_path).exists());
return dir_path;
}
static auto create_root_file(std::string &file_name) -> std::string { static auto create_root_file(std::string &file_name) -> std::string {
auto file_path = create_file_and_test(file_name); auto file_path = create_file_and_test(file_name);
auto api_path = utils::path::create_api_path(file_name); auto api_path = utils::path::create_api_path(file_name);
@ -204,11 +216,18 @@ public:
EXPECT_TRUE(unmounted); EXPECT_TRUE(unmounted);
} }
static void rmdir_and_test(std::string_view dir_path) {
EXPECT_TRUE(utils::file::directory(dir_path).remove());
EXPECT_FALSE(utils::file::directory(dir_path).exists());
EXPECT_FALSE(utils::file::file(dir_path).exists());
}
static void unlink_file_and_test(std::string_view file_path) { static void unlink_file_and_test(std::string_view file_path) {
EXPECT_TRUE(utils::file::file(file_path).remove()); EXPECT_TRUE(utils::file::file(file_path).remove());
EXPECT_FALSE(utils::file::file(file_path).exists()); EXPECT_FALSE(utils::file::file(file_path).exists());
EXPECT_FALSE(utils::file::directory(file_path).exists()); EXPECT_FALSE(utils::file::directory(file_path).exists());
EXPECT_FALSE(utils::file::file(file_path).exists());
} }
static void unlink_root_file(const std::string &file_path) { static void unlink_root_file(const std::string &file_path) {

View File

@ -75,6 +75,8 @@ protected:
static void SetUpTestCase() { static void SetUpTestCase() {
current_directory = std::filesystem::current_path(); current_directory = std::filesystem::current_path();
mount_location = utils::string::to_lower(std::string{"U:"});
const auto mount_s3 = [&]() { const auto mount_s3 = [&]() {
{ {
auto test_directory = utils::path::combine( auto test_directory = utils::path::combine(
@ -84,8 +86,6 @@ protected:
app_config::get_provider_name(provider_type::s3), app_config::get_provider_name(provider_type::s3),
}); });
mount_location = "U:";
auto cfg_directory = utils::path::combine(test_directory, {"cfg"}); auto cfg_directory = utils::path::combine(test_directory, {"cfg"});
ASSERT_TRUE(utils::file::directory(cfg_directory).create_directory()); ASSERT_TRUE(utils::file::directory(cfg_directory).create_directory());
@ -124,8 +124,6 @@ protected:
app_config::get_provider_name(provider_type::sia), app_config::get_provider_name(provider_type::sia),
}); });
mount_location = "U:";
auto cfg_directory = utils::path::combine(test_directory, {"cfg"}); auto cfg_directory = utils::path::combine(test_directory, {"cfg"});
ASSERT_TRUE(utils::file::directory(cfg_directory).create_directory()); ASSERT_TRUE(utils::file::directory(cfg_directory).create_directory());
@ -166,7 +164,7 @@ protected:
}); });
mount_location2 = mount_location; mount_location2 = mount_location;
mount_location = "V:"; mount_location = utils::string::to_lower(std::string{"V:"});
auto cfg_directory = utils::path::combine(test_directory, {"cfg2"}); auto cfg_directory = utils::path::combine(test_directory, {"cfg2"});
ASSERT_TRUE(utils::file::directory(cfg_directory).create_directory()); ASSERT_TRUE(utils::file::directory(cfg_directory).create_directory());
@ -275,8 +273,9 @@ std::string winfsp_test<provider_t>::mount_location;
template <typename provider_t> template <typename provider_t>
std::string winfsp_test<provider_t>::mount_location2; std::string winfsp_test<provider_t>::mount_location2;
// using winfsp_provider_types = ::testing::Types<local_s3, local_sia,
// remote_s3, remote_sia>; // using winfsp_provider_types = ::testing::Types<local_s3, remote_s3,
// local_sia, remote_sia>;
using winfsp_provider_types = ::testing::Types<local_s3, remote_s3>; using winfsp_provider_types = ::testing::Types<local_s3, remote_s3>;
} // namespace repertory } // namespace repertory

View File

@ -0,0 +1,75 @@
/*
Copyright <2018-2024> <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.
*/
#if !defined(_WIN32)
#include "fixtures/fuse_fixture.hpp"
namespace repertory {
TYPED_TEST_CASE(fuse_test, fuse_provider_types);
TYPED_TEST(fuse_test, can_chmod_if_owner) {
std::string file_name{"chmod_test"};
auto file_path = this->create_file_and_test(file_name);
EXPECT_EQ(0, chmod(file_path.c_str(), S_IRUSR | S_IWUSR));
std::this_thread::sleep_for(SLEEP_SECONDS);
struct stat64 unix_st{};
stat64(file_path.c_str(), &unix_st);
EXPECT_EQ(static_cast<std::uint32_t>(S_IRUSR | S_IWUSR),
ACCESSPERMS & unix_st.st_mode);
this->unlink_file_and_test(file_path);
}
TYPED_TEST(fuse_test, can_not_chmod_if_not_owner) {
std::string file_name{"chmod_test"};
auto file_path = this->create_root_file(file_name);
EXPECT_EQ(-1, chmod(file_path.c_str(), S_IRUSR | S_IWUSR));
EXPECT_EQ(EPERM, errno);
this->unlink_root_file(file_path);
}
TYPED_TEST(fuse_test, can_not_chmod_setgid_if_not_root) {
std::string file_name{"chmod_test"};
auto file_path = this->create_file_and_test(file_name);
EXPECT_EQ(-1, chmod(file_path.c_str(), S_IRUSR | S_IWUSR | S_ISGID));
EXPECT_EQ(EPERM, errno);
this->unlink_file_and_test(file_path);
}
TYPED_TEST(fuse_test, can_not_chmod_setuid_if_not_root) {
std::string file_name{"chmod_test"};
auto file_path = this->create_file_and_test(file_name);
EXPECT_EQ(-1, chmod(file_path.c_str(), S_IRUSR | S_IWUSR | S_ISUID));
EXPECT_EQ(EPERM, errno);
this->unlink_file_and_test(file_path);
}
} // namespace repertory
#endif // !defined(_WIN32)

View File

@ -0,0 +1,113 @@
/*
Copyright <2018-2024> <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.
*/
#if !defined(_WIN32)
#include "fixtures/fuse_fixture.hpp"
namespace repertory {
TYPED_TEST_CASE(fuse_test, fuse_provider_types);
TYPED_TEST(fuse_test, can_not_chmod_set_sticky_if_not_root) {
std::string file_name{"chown_test"};
auto file_path = this->create_file_and_test(file_name);
EXPECT_EQ(-1, chmod(file_path.c_str(), S_IRUSR | S_IWUSR | S_ISVTX));
EXPECT_EQ(EPERM, errno);
this->unlink_file_and_test(file_path);
}
TYPED_TEST(fuse_test, can_chown_group_if_owner_and_a_member_of_the_group) {
std::string file_name{"chown_test"};
auto file_path = this->create_file_and_test(file_name);
struct stat64 unix_st{};
EXPECT_EQ(0, stat64(file_path.c_str(), &unix_st));
EXPECT_EQ(0, chown(file_path.c_str(), static_cast<uid_t>(-1), getgid()));
std::this_thread::sleep_for(SLEEP_SECONDS);
struct stat64 unix_st2{};
stat64(file_path.c_str(), &unix_st2);
EXPECT_EQ(getgid(), unix_st2.st_gid);
EXPECT_EQ(unix_st.st_uid, unix_st2.st_uid);
this->unlink_file_and_test(file_path);
}
TYPED_TEST(fuse_test,
can_not_chown_group_if_owner_but_not_a_member_of_the_group) {
std::string file_name{"chown_test"};
auto file_path = this->create_file_and_test(file_name);
struct stat64 unix_st{};
EXPECT_EQ(0, stat64(file_path.c_str(), &unix_st));
EXPECT_EQ(-1, chown(file_path.c_str(), static_cast<uid_t>(-1), 0));
EXPECT_EQ(EPERM, errno);
struct stat64 unix_st2{};
stat64(file_path.c_str(), &unix_st2);
EXPECT_EQ(unix_st.st_gid, unix_st2.st_gid);
EXPECT_EQ(unix_st.st_uid, unix_st2.st_uid);
this->unlink_file_and_test(file_path);
}
TYPED_TEST(fuse_test, can_not_chown_group_if_not_the_owner) {
std::string file_name{"chown_test"};
auto file_path = this->create_root_file(file_name);
struct stat64 unix_st{};
EXPECT_EQ(0, stat64(file_path.c_str(), &unix_st));
EXPECT_EQ(-1, chown(file_path.c_str(), static_cast<uid_t>(-1), getgid()));
EXPECT_EQ(EPERM, errno);
struct stat64 unix_st2{};
stat64(file_path.c_str(), &unix_st2);
EXPECT_EQ(unix_st.st_gid, unix_st2.st_gid);
EXPECT_EQ(unix_st.st_uid, unix_st2.st_uid);
this->unlink_root_file(file_path);
}
TYPED_TEST(fuse_test, can_not_chown_user_if_not_root) {
std::string file_name{"chown_test"};
auto file_path = this->create_file_and_test(file_name);
struct stat64 unix_st{};
EXPECT_EQ(0, stat64(file_path.c_str(), &unix_st));
EXPECT_EQ(-1, chown(file_path.c_str(), 0, static_cast<gid_t>(-1)));
EXPECT_EQ(EPERM, errno);
struct stat64 unix_st2{};
stat64(file_path.c_str(), &unix_st2);
EXPECT_EQ(unix_st.st_gid, unix_st2.st_gid);
EXPECT_EQ(unix_st.st_uid, unix_st2.st_uid);
this->unlink_file_and_test(file_path);
}
} // namespace repertory
#endif // !defined(_WIN32)

View File

@ -0,0 +1,51 @@
/*
Copyright <2018-2024> <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.
*/
#if !defined(_WIN32)
#include "fixtures/fuse_fixture.hpp"
namespace repertory {
TYPED_TEST_CASE(fuse_test, fuse_provider_types);
TYPED_TEST(fuse_test, can_create_and_remove_directory) {
auto dir_name = std::string{"dir"} + std::to_string(++this->idx);
auto dir_path = utils::path::combine(this->mount_location, {dir_name});
EXPECT_EQ(0, mkdir(dir_path.c_str(), S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP |
S_IWGRP | S_IXGRP));
EXPECT_TRUE(utils::file::is_directory(dir_path));
EXPECT_FALSE(utils::file::is_file(dir_path));
struct stat64 unix_st{};
stat64(dir_path.c_str(), &unix_st);
EXPECT_EQ(getgid(), unix_st.st_gid);
EXPECT_EQ(getuid(), unix_st.st_uid);
EXPECT_EQ(static_cast<std::uint32_t>(S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP |
S_IWGRP | S_IXGRP),
ACCESSPERMS & unix_st.st_mode);
}
} // namespace repertory
#endif // !defined(_WIN32)

View File

@ -1,29 +1,3 @@
/*
Copyright <2018-2024> <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.
*/
#if !defined(_WIN32)
#include "fixtures/fuse_fixture.hpp"
namespace repertory {
// static void rmdir_and_test(const std::string &directory_path) { // static void rmdir_and_test(const std::string &directory_path) {
// std::cout << __FUNCTION__ << std::endl; // std::cout << __FUNCTION__ << std::endl;
// int ret = 0; // int ret = 0;
@ -400,14 +374,6 @@ namespace repertory {
// } // }
// #endif // #endif
// //
// // file_path = create_file_and_test(mount_location, "chown_test");
// // test_chown(utils::path::create_api_path("chown_test"), file_path);
// // unlink_file_and_test(file_path);
// //
// // file_path = utils::path::combine(mount_location, {"mkdir_test"});
// // test_mkdir(utils::path::create_api_path("mkdir_test"), file_path);
// // rmdir_and_test(file_path);
// //
// // file_path = create_file_and_test(mount_location, // // file_path = create_file_and_test(mount_location,
// // "write_read_test"); // // "write_read_test");
// // test_write_and_read(utils::path::create_api_path("write_read_test"), // // test_write_and_read(utils::path::create_api_path("write_read_test"),
@ -514,136 +480,3 @@ namespace repertory {
// // test_xattr_removexattr(file_path); // // test_xattr_removexattr(file_path);
// // unlink_file_and_test(file_path); // // unlink_file_and_test(file_path);
// // #endif // // #endif
TYPED_TEST_CASE(fuse_test, fuse_provider_types);
TYPED_TEST(fuse_test, can_chmod_if_owner) {
std::string file_name{"chmod_test"};
auto file_path = this->create_file_and_test(file_name);
EXPECT_EQ(0, chmod(file_path.c_str(), S_IRUSR | S_IWUSR));
std::this_thread::sleep_for(SLEEP_SECONDS);
struct stat64 unix_st {};
stat64(file_path.c_str(), &unix_st);
EXPECT_EQ(static_cast<std::uint32_t>(S_IRUSR | S_IWUSR),
ACCESSPERMS & unix_st.st_mode);
this->unlink_file_and_test(file_path);
}
TYPED_TEST(fuse_test, can_not_chmod_if_not_owner) {
std::string file_name{"chmod_test"};
auto file_path = this->create_root_file(file_name);
EXPECT_EQ(-1, chmod(file_path.c_str(), S_IRUSR | S_IWUSR));
EXPECT_EQ(EPERM, errno);
this->unlink_root_file(file_path);
}
TYPED_TEST(fuse_test, can_not_chmod_setgid_if_not_root) {
std::string file_name{"chmod_test"};
auto file_path = this->create_file_and_test(file_name);
EXPECT_EQ(-1, chmod(file_path.c_str(), S_IRUSR | S_IWUSR | S_ISGID));
EXPECT_EQ(EPERM, errno);
this->unlink_file_and_test(file_path);
}
TYPED_TEST(fuse_test, can_not_chmod_setuid_if_not_root) {
std::string file_name{"chmod_test"};
auto file_path = this->create_file_and_test(file_name);
EXPECT_EQ(-1, chmod(file_path.c_str(), S_IRUSR | S_IWUSR | S_ISUID));
EXPECT_EQ(EPERM, errno);
this->unlink_file_and_test(file_path);
}
TYPED_TEST(fuse_test, can_not_chmod_set_sticky_if_not_root) {
std::string file_name{"chown_test"};
auto file_path = this->create_file_and_test(file_name);
EXPECT_EQ(-1, chmod(file_path.c_str(), S_IRUSR | S_IWUSR | S_ISVTX));
EXPECT_EQ(EPERM, errno);
this->unlink_file_and_test(file_path);
}
TYPED_TEST(fuse_test, can_chown_group_if_owner_and_a_member_of_the_group) {
std::string file_name{"chown_test"};
auto file_path = this->create_file_and_test(file_name);
struct stat64 unix_st {};
EXPECT_EQ(0, stat64(file_path.c_str(), &unix_st));
EXPECT_EQ(0, chown(file_path.c_str(), static_cast<uid_t>(-1), getgid()));
std::this_thread::sleep_for(SLEEP_SECONDS);
struct stat64 unix_st2 {};
stat64(file_path.c_str(), &unix_st2);
EXPECT_EQ(getgid(), unix_st2.st_gid);
EXPECT_EQ(unix_st.st_uid, unix_st2.st_uid);
this->unlink_file_and_test(file_path);
}
TYPED_TEST(fuse_test,
can_not_chown_group_if_owner_but_not_a_member_of_the_group) {
std::string file_name{"chown_test"};
auto file_path = this->create_file_and_test(file_name);
struct stat64 unix_st {};
EXPECT_EQ(0, stat64(file_path.c_str(), &unix_st));
EXPECT_EQ(-1, chown(file_path.c_str(), static_cast<uid_t>(-1), 0));
EXPECT_EQ(EPERM, errno);
struct stat64 unix_st2 {};
stat64(file_path.c_str(), &unix_st2);
EXPECT_EQ(unix_st.st_gid, unix_st2.st_gid);
EXPECT_EQ(unix_st.st_uid, unix_st2.st_uid);
this->unlink_file_and_test(file_path);
}
TYPED_TEST(fuse_test, can_not_chown_group_if_not_the_owner) {
std::string file_name{"chown_test"};
auto file_path = this->create_root_file(file_name);
struct stat64 unix_st {};
EXPECT_EQ(0, stat64(file_path.c_str(), &unix_st));
EXPECT_EQ(-1, chown(file_path.c_str(), static_cast<uid_t>(-1), getgid()));
EXPECT_EQ(EPERM, errno);
struct stat64 unix_st2 {};
stat64(file_path.c_str(), &unix_st2);
EXPECT_EQ(unix_st.st_gid, unix_st2.st_gid);
EXPECT_EQ(unix_st.st_uid, unix_st2.st_uid);
this->unlink_root_file(file_path);
}
TYPED_TEST(fuse_test, can_not_chown_user_if_not_root) {
std::string file_name{"chown_test"};
auto file_path = this->create_file_and_test(file_name);
struct stat64 unix_st {};
EXPECT_EQ(0, stat64(file_path.c_str(), &unix_st));
EXPECT_EQ(-1, chown(file_path.c_str(), 0, static_cast<gid_t>(-1)));
EXPECT_EQ(EPERM, errno);
struct stat64 unix_st2 {};
stat64(file_path.c_str(), &unix_st2);
EXPECT_EQ(unix_st.st_gid, unix_st2.st_gid);
EXPECT_EQ(unix_st.st_uid, unix_st2.st_uid);
this->unlink_file_and_test(file_path);
}
} // namespace repertory
#endif // !defined(_WIN32)

View File

@ -31,48 +31,52 @@ namespace repertory {
TYPED_TEST_CASE(winfsp_test, winfsp_provider_types); TYPED_TEST_CASE(winfsp_test, winfsp_provider_types);
TYPED_TEST(winfsp_test, cr8_nl_can_create_file_of_max_component_length) { TYPED_TEST(winfsp_test, cr8_nl_can_create_file_of_max_component_length) {
if (this->current_provider != provider_type::s3) { if (this->current_provider == provider_type::s3) {
DWORD max_length{}; return;
EXPECT_TRUE(::GetVolumeInformationA(this->mount_location.c_str(), nullptr,
0, nullptr, &max_length, nullptr,
nullptr, 0));
EXPECT_EQ(255U, max_length);
auto file_path = utils::path::combine(this->mount_location,
{
std::string(max_length, 'a'),
});
auto handle = ::CreateFileA(
file_path.c_str(), GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, CREATE_NEW,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE, nullptr);
ASSERT_NE(INVALID_HANDLE_VALUE, handle);
EXPECT_TRUE(::CloseHandle(handle));
} }
DWORD max_length{};
EXPECT_TRUE(::GetVolumeInformationA(this->mount_location.c_str(), nullptr, 0,
nullptr, &max_length, nullptr, nullptr,
0));
EXPECT_EQ(255U, max_length);
auto file_path = utils::path::combine(this->mount_location,
{
std::string(max_length, 'a'),
});
auto handle =
::CreateFileA(file_path.c_str(), GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, CREATE_NEW,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE, nullptr);
ASSERT_NE(INVALID_HANDLE_VALUE, handle);
EXPECT_TRUE(::CloseHandle(handle));
} }
TYPED_TEST(winfsp_test, TYPED_TEST(winfsp_test,
cr8_nl_can_not_create_file_greater_than_max_component_length) { cr8_nl_can_not_create_file_greater_than_max_component_length) {
if (this->current_provider != provider_type::s3) { if (this->current_provider == provider_type::s3) {
DWORD max_length{}; return;
EXPECT_TRUE(::GetVolumeInformationA(this->mount_location.c_str(), nullptr,
0, nullptr, &max_length, nullptr,
nullptr, 0));
EXPECT_EQ(255U, max_length);
auto file_path = utils::path::combine(this->mount_location,
{
std::string(max_length + 1U, 'a'),
});
auto handle = ::CreateFileA(
file_path.c_str(), GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, CREATE_NEW,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE, nullptr);
ASSERT_EQ(INVALID_HANDLE_VALUE, handle);
EXPECT_EQ(ERROR_INVALID_NAME, ::GetLastError());
} }
DWORD max_length{};
EXPECT_TRUE(::GetVolumeInformationA(this->mount_location.c_str(), nullptr, 0,
nullptr, &max_length, nullptr, nullptr,
0));
EXPECT_EQ(255U, max_length);
auto file_path = utils::path::combine(this->mount_location,
{
std::string(max_length + 1U, 'a'),
});
auto handle =
::CreateFileA(file_path.c_str(), GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, CREATE_NEW,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE, nullptr);
ASSERT_EQ(INVALID_HANDLE_VALUE, handle);
EXPECT_EQ(ERROR_INVALID_NAME, ::GetLastError());
} }
} // namespace repertory } // namespace repertory

View File

@ -256,7 +256,7 @@ TYPED_TEST(winfsp_test, delete_can_delete_on_close_after_mapping) {
16U * sys_info.dwAllocationGranularity, nullptr); 16U * sys_info.dwAllocationGranularity, nullptr);
EXPECT_TRUE(mapping != nullptr); EXPECT_TRUE(mapping != nullptr);
auto *view = MapViewOfFile(mapping, FILE_MAP_ALL_ACCESS, 0, 0, 0); auto *view = ::MapViewOfFile(mapping, FILE_MAP_ALL_ACCESS, 0, 0, 0);
EXPECT_TRUE(view != nullptr); EXPECT_TRUE(view != nullptr);
for (PUINT8 ptr = reinterpret_cast<PUINT8>(view), for (PUINT8 ptr = reinterpret_cast<PUINT8>(view),

View File

@ -124,8 +124,7 @@ TYPED_TEST(winfsp_test, info_can_get_file_name_info) {
auto *info = reinterpret_cast<FILE_NAME_INFO *>(name_info.data()); auto *info = reinterpret_cast<FILE_NAME_INFO *>(name_info.data());
auto expected_name{ auto expected_name{
std::string{"\\repertory\\"} + std::string{"\\repertory\\"} + this->mount_location.at(0U) +
utils::string::to_lower(this->mount_location).at(0U) +
"\\test_file_2", "\\test_file_2",
}; };
@ -229,8 +228,7 @@ TYPED_TEST(winfsp_test, info_can_get_file_path) {
VOLUME_NAME_NONE | FILE_NAME_OPENED); VOLUME_NAME_NONE | FILE_NAME_OPENED);
auto expected_name{ auto expected_name{
std::string{"\\repertory\\"} + std::string{"\\repertory\\"} + this->mount_location.at(0U) +
utils::string::to_lower(this->mount_location).at(0U) +
"\\test_file_2", "\\test_file_2",
}; };
EXPECT_EQ(result, static_cast<DWORD>(expected_name.size())); EXPECT_EQ(result, static_cast<DWORD>(expected_name.size()));

View File

@ -0,0 +1,214 @@
/*
Copyright <2018-2024> <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 <array>
#if defined(_WIN32)
//
// Implemented test cases based on WinFsp tests:
// https://github.com/winfsp/winfsp/blob/v2.0/tst/winfsp-tests
//
#include "fixtures/winfsp_fixture.hpp"
namespace repertory {
TYPED_TEST_CASE(winfsp_test, winfsp_provider_types);
static void test_file(auto &&file_path, auto &&flags) {
auto handle =
::CreateFileA(file_path.c_str(), GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, CREATE_NEW,
FILE_ATTRIBUTE_NORMAL | flags, nullptr);
ASSERT_NE(INVALID_HANDLE_VALUE, handle);
auto pointer = ::SetFilePointer(handle, 0, 0, FILE_BEGIN);
EXPECT_EQ(0, pointer);
auto write_buffer = utils::generate_secure_random<data_buffer>(16U);
DWORD bytes_written{};
EXPECT_TRUE(::WriteFile(handle, write_buffer.data(), write_buffer.size(),
&bytes_written, nullptr));
EXPECT_EQ(static_cast<DWORD>(write_buffer.size()), bytes_written);
EXPECT_EQ(pointer + bytes_written,
::SetFilePointer(handle, 0, 0, FILE_CURRENT));
pointer = ::SetFilePointer(handle, 2U * write_buffer.size(), 0, FILE_BEGIN);
EXPECT_EQ(static_cast<DWORD>(2U * write_buffer.size()), pointer);
EXPECT_TRUE(::WriteFile(handle, write_buffer.data(), write_buffer.size(),
&bytes_written, nullptr));
EXPECT_EQ(static_cast<DWORD>(write_buffer.size()), bytes_written);
EXPECT_EQ(pointer + bytes_written,
::SetFilePointer(handle, 0, 0, FILE_CURRENT));
pointer = ::SetFilePointer(handle, 0, 0, FILE_BEGIN);
EXPECT_EQ(0U, pointer);
DWORD bytes_read{};
std::array<std::uint8_t, 16U> read_buffer{};
EXPECT_TRUE(::ReadFile(handle, read_buffer.data(), read_buffer.size(),
&bytes_read, nullptr));
EXPECT_EQ(static_cast<DWORD>(16U), bytes_read);
EXPECT_EQ(pointer + bytes_read, ::SetFilePointer(handle, 0, 0, FILE_CURRENT));
EXPECT_EQ(0,
std::memcmp(write_buffer.data(), read_buffer.data(), bytes_read));
for (auto idx = 0U; idx < 2U; ++idx) {
pointer = ::SetFilePointer(handle, 2U * write_buffer.size(), 0, FILE_BEGIN);
EXPECT_EQ(2U * write_buffer.size(), pointer);
EXPECT_TRUE(::ReadFile(handle, read_buffer.data(), read_buffer.size(),
&bytes_read, nullptr));
EXPECT_EQ(static_cast<DWORD>(16U), bytes_read);
EXPECT_EQ(pointer + bytes_read,
::SetFilePointer(handle, 0, 0, FILE_CURRENT));
EXPECT_EQ(0,
std::memcmp(write_buffer.data(), read_buffer.data(), bytes_read));
}
pointer = ::SetFilePointer(handle, 3U * write_buffer.size(), 0, FILE_BEGIN);
EXPECT_EQ(3U * write_buffer.size(), pointer);
EXPECT_TRUE(::ReadFile(handle, read_buffer.data(), read_buffer.size(),
&bytes_read, nullptr));
EXPECT_EQ(static_cast<DWORD>(16U), bytes_read);
EXPECT_EQ(pointer + bytes_read, ::SetFilePointer(handle, 0, 0, FILE_CURRENT));
EXPECT_EQ(0,
std::memcmp(write_buffer.data(), read_buffer.data(), bytes_read));
// FilePointer = SetFilePointer(Handle, 0, 0, FILE_BEGIN);
// ASSERT(0 == FilePointer);
// Success = WriteFile(Handle, Buffer[0], 2 * SystemInfo.dwPageSize,
// &BytesTransferred, 0);
// ASSERT(Success);
// ASSERT(2 * SystemInfo.dwPageSize == BytesTransferred);
// ASSERT(FilePointer + BytesTransferred ==
// SetFilePointer(Handle, 0, 0, FILE_CURRENT));
//
// FilePointer = SetFilePointer(Handle, 0, 0, FILE_BEGIN);
// ASSERT(0 == FilePointer);
// memset(AllocBuffer[1], 0, AllocBufferSize);
// Success = ReadFile(Handle, Buffer[1], 2 * SystemInfo.dwPageSize,
// &BytesTransferred, 0);
// ASSERT(Success);
// ASSERT(2 * SystemInfo.dwPageSize == BytesTransferred);
// ASSERT(FilePointer + BytesTransferred ==
// SetFilePointer(Handle, 0, 0, FILE_CURRENT));
// ASSERT(0 == memcmp(Buffer[0], Buffer[1], BytesTransferred));
//
// Buffer[0] = AllocBuffer[0];
// Buffer[1] = AllocBuffer[0];
//
// FilePointer = SetFilePointer(Handle, 0, 0, FILE_BEGIN);
// ASSERT(0 == FilePointer);
// Success = WriteFile(Handle, Buffer[0], 2 * SystemInfo.dwPageSize,
// &BytesTransferred, 0);
// ASSERT(Success);
// ASSERT(2 * SystemInfo.dwPageSize == BytesTransferred);
// ASSERT(FilePointer + BytesTransferred ==
// SetFilePointer(Handle, 0, 0, FILE_CURRENT));
//
// FilePointer = SetFilePointer(Handle, 0, 0, FILE_BEGIN);
// ASSERT(0 == FilePointer);
// memset(AllocBuffer[1], 0, AllocBufferSize);
// Success = ReadFile(Handle, Buffer[1], 2 * SystemInfo.dwPageSize,
// &BytesTransferred, 0);
// ASSERT(Success);
// ASSERT(2 * SystemInfo.dwPageSize == BytesTransferred);
// ASSERT(FilePointer + BytesTransferred ==
// SetFilePointer(Handle, 0, 0, FILE_CURRENT));
// ASSERT(0 == memcmp(Buffer[0], Buffer[1], BytesTransferred));
//
// FilePointer = SetFilePointer(Handle, 0, 0, FILE_BEGIN);
// ASSERT(0 == FilePointer);
// Success =
// WriteFile(Handle, Buffer[0], 2 * SystemInfo.dwPageSize +
// BytesPerSector,
// &BytesTransferred, 0);
// ASSERT(Success);
// ASSERT(2 * SystemInfo.dwPageSize + BytesPerSector == BytesTransferred);
// ASSERT(FilePointer + BytesTransferred ==
// SetFilePointer(Handle, 0, 0, FILE_CURRENT));
//
// FilePointer = SetFilePointer(Handle, 0, 0, FILE_BEGIN);
// ASSERT(0 == FilePointer);
// memset(AllocBuffer[1], 0, AllocBufferSize);
// Success =
// ReadFile(Handle, Buffer[1], 2 * SystemInfo.dwPageSize + BytesPerSector,
// &BytesTransferred, 0);
// ASSERT(Success);
// ASSERT(2 * SystemInfo.dwPageSize + BytesPerSector == BytesTransferred);
// ASSERT(FilePointer + BytesTransferred ==
// SetFilePointer(Handle, 0, 0, FILE_CURRENT));
// ASSERT(0 == memcmp(Buffer[0], Buffer[1], BytesTransferred));
//
EXPECT_TRUE(::CloseHandle(handle));
handle = ::CreateFileA(
file_path.c_str(), GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL | flags | FILE_FLAG_DELETE_ON_CLOSE, nullptr);
ASSERT_NE(INVALID_HANDLE_VALUE, handle);
// FilePointer = SetFilePointer(Handle, 0, 0, FILE_BEGIN);
// ASSERT(0 == FilePointer);
// memset(AllocBuffer[1], 0, AllocBufferSize);
// Success =
// ReadFile(Handle, Buffer[1], 2 * SystemInfo.dwPageSize + BytesPerSector,
// &BytesTransferred, 0);
// ASSERT(Success);
// ASSERT(2 * SystemInfo.dwPageSize + BytesPerSector == BytesTransferred);
// ASSERT(FilePointer + BytesTransferred ==
// SetFilePointer(Handle, 0, 0, FILE_CURRENT));
// ASSERT(0 == memcmp(Buffer[0], Buffer[1], BytesTransferred));
//
EXPECT_TRUE(::CloseHandle(handle));
handle = ::CreateFileA(file_path.c_str(), GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr,
OPEN_EXISTING, 0, nullptr);
ASSERT_EQ(INVALID_HANDLE_VALUE, handle);
ASSERT_EQ(ERROR_FILE_NOT_FOUND, ::GetLastError());
}
TYPED_TEST(winfsp_test, rdrw_can_read_and_write_file_no_flags) {
auto file_path{
utils::path::combine(this->mount_location, {"test_file_5"}),
};
test_file(file_path, 0U);
}
TYPED_TEST(winfsp_test, rdrw_can_read_and_write_file_no_buffering) {
auto file_path{
utils::path::combine(this->mount_location, {"test_file_5"}),
};
test_file(file_path, FILE_FLAG_NO_BUFFERING);
}
TYPED_TEST(winfsp_test, rdrw_can_read_and_write_file_write_through) {
auto file_path{
utils::path::combine(this->mount_location, {"test_file_5"}),
};
test_file(file_path, FILE_FLAG_WRITE_THROUGH);
}
} // namespace repertory
#endif // defined(_WIN32)

View File

@ -0,0 +1,260 @@
/*
Copyright <2018-2024> <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.
*/
#if defined(_WIN32)
//
// Implemented test cases based on WinFsp tests:
// https://github.com/winfsp/winfsp/blob/v2.0/tst/winfsp-tests
//
#include "fixtures/winfsp_fixture.hpp"
namespace repertory {
TYPED_TEST_CASE(winfsp_test, winfsp_provider_types);
TYPED_TEST(winfsp_test, rename_can_rename_file_if_dest_does_not_exist) {
if (this->current_provider == provider_type::s3) {
return;
}
auto dir_path{
utils::path::combine(this->mount_location, {"test_dir_4"}),
};
auto file_path{
utils::path::combine(dir_path, {"test_file_4"}),
};
auto file_path2{
utils::path::combine(dir_path, {"test_file2_4"}),
};
ASSERT_TRUE(::CreateDirectoryA(dir_path.c_str(), nullptr));
auto handle = ::CreateFileA(file_path.c_str(), GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr,
CREATE_NEW, FILE_ATTRIBUTE_NORMAL, nullptr);
ASSERT_NE(INVALID_HANDLE_VALUE, handle);
::CloseHandle(handle);
EXPECT_TRUE(::MoveFileExA(file_path.c_str(), file_path2.c_str(), 0));
EXPECT_TRUE(::DeleteFileA(file_path2.c_str()));
EXPECT_TRUE(::RemoveDirectoryA(dir_path.c_str()));
}
TYPED_TEST(winfsp_test, rename_fails_if_dest_exists_and_replace_is_false) {
if (this->current_provider == provider_type::s3) {
return;
}
auto dir_path{
utils::path::combine(this->mount_location, {"test_dir_4"}),
};
auto file_path{
utils::path::combine(dir_path, {"test_file_4"}),
};
auto file_path2{
utils::path::combine(dir_path, {"test_file2_4"}),
};
ASSERT_TRUE(::CreateDirectoryA(dir_path.c_str(), nullptr));
auto handle = ::CreateFileA(file_path.c_str(), GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr,
CREATE_NEW, FILE_ATTRIBUTE_NORMAL, nullptr);
ASSERT_NE(INVALID_HANDLE_VALUE, handle);
::CloseHandle(handle);
EXPECT_TRUE(::MoveFileExA(file_path.c_str(), file_path2.c_str(), 0));
EXPECT_FALSE(::MoveFileExA(file_path.c_str(), file_path2.c_str(), 0));
EXPECT_EQ(ERROR_ALREADY_EXISTS, ::GetLastError());
EXPECT_TRUE(::DeleteFileA(file_path2.c_str()));
EXPECT_TRUE(::RemoveDirectoryA(dir_path.c_str()));
}
TYPED_TEST(winfsp_test, rename_succeeds_if_dest_exists_and_replace_is_true) {
if (this->current_provider == provider_type::s3) {
return;
}
auto dir_path{
utils::path::combine(this->mount_location, {"test_dir_4"}),
};
auto file_path{
utils::path::combine(dir_path, {"test_file_4"}),
};
auto file_path2{
utils::path::combine(dir_path, {"test_file2_4"}),
};
ASSERT_TRUE(::CreateDirectoryA(dir_path.c_str(), nullptr));
auto handle = ::CreateFileA(file_path.c_str(), GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr,
CREATE_NEW, FILE_ATTRIBUTE_NORMAL, nullptr);
ASSERT_NE(INVALID_HANDLE_VALUE, handle);
::CloseHandle(handle);
EXPECT_TRUE(::MoveFileExA(file_path.c_str(), file_path2.c_str(), 0));
handle = ::CreateFileA(file_path.c_str(), GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr,
CREATE_NEW, FILE_ATTRIBUTE_NORMAL, nullptr);
ASSERT_NE(INVALID_HANDLE_VALUE, handle);
::CloseHandle(handle);
EXPECT_TRUE(::MoveFileExA(file_path.c_str(), file_path2.c_str(),
MOVEFILE_REPLACE_EXISTING));
EXPECT_TRUE(::DeleteFileA(file_path2.c_str()));
EXPECT_TRUE(::RemoveDirectoryA(dir_path.c_str()));
}
TYPED_TEST(winfsp_test, rename_can_rename_dir_if_dest_does_not_exist) {
if (this->current_provider == provider_type::s3) {
return;
}
auto dir_path{
utils::path::combine(this->mount_location, {"test_dir_4"}),
};
auto dir_path2{
utils::path::combine(this->mount_location, {"test_dir2_4"}),
};
ASSERT_TRUE(::CreateDirectoryA(dir_path.c_str(), nullptr));
EXPECT_TRUE(::MoveFileExA(dir_path.c_str(), dir_path2.c_str(), 0));
EXPECT_TRUE(::RemoveDirectoryA(dir_path2.c_str()));
}
TYPED_TEST(winfsp_test, rename_dir_fails_if_dest_exists_and_replace_is_false) {
if (this->current_provider == provider_type::s3) {
return;
}
auto dir_path{
utils::path::combine(this->mount_location, {"test_dir_4"}),
};
auto dir_path2{
utils::path::combine(this->mount_location, {"test_dir2_4"}),
};
ASSERT_TRUE(::CreateDirectoryA(dir_path.c_str(), nullptr));
ASSERT_TRUE(::CreateDirectoryA(dir_path2.c_str(), nullptr));
EXPECT_FALSE(::MoveFileExA(dir_path.c_str(), dir_path2.c_str(), 0));
EXPECT_EQ(ERROR_ACCESS_DENIED, ::GetLastError());
EXPECT_TRUE(::RemoveDirectoryA(dir_path.c_str()));
EXPECT_TRUE(::RemoveDirectoryA(dir_path2.c_str()));
}
TYPED_TEST(winfsp_test, rename_dir_fails_if_dest_exists_and_replace_is_true) {
if (this->current_provider == provider_type::s3) {
return;
}
auto dir_path{
utils::path::combine(this->mount_location, {"test_dir_4"}),
};
auto dir_path2{
utils::path::combine(this->mount_location, {"test_dir2_4"}),
};
ASSERT_TRUE(::CreateDirectoryA(dir_path.c_str(), nullptr));
ASSERT_TRUE(::CreateDirectoryA(dir_path2.c_str(), nullptr));
EXPECT_FALSE(::MoveFileExA(dir_path.c_str(), dir_path2.c_str(),
MOVEFILE_REPLACE_EXISTING));
EXPECT_EQ(ERROR_ACCESS_DENIED, ::GetLastError());
EXPECT_TRUE(::RemoveDirectoryA(dir_path.c_str()));
EXPECT_TRUE(::RemoveDirectoryA(dir_path2.c_str()));
}
TYPED_TEST(winfsp_test,
rename_dir_fails_directory_is_not_empty_and_replace_is_false) {
if (this->current_provider == provider_type::s3) {
return;
}
auto dir_path{
utils::path::combine(this->mount_location, {"test_dir_4"}),
};
auto dir_path2{
utils::path::combine(this->mount_location, {"test_dir2_4"}),
};
auto file_path{
utils::path::combine(dir_path, {"test_file_4"}),
};
ASSERT_TRUE(::CreateDirectoryA(dir_path.c_str(), nullptr));
auto handle = ::CreateFileA(file_path.c_str(), GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr,
CREATE_NEW, FILE_ATTRIBUTE_NORMAL, nullptr);
ASSERT_NE(INVALID_HANDLE_VALUE, handle);
::CloseHandle(handle);
EXPECT_FALSE(::MoveFileExA(dir_path.c_str(), dir_path2.c_str(), 0));
EXPECT_EQ(ERROR_ACCESS_DENIED, ::GetLastError());
EXPECT_TRUE(::DeleteFileA(file_path.c_str()));
EXPECT_TRUE(::RemoveDirectoryA(dir_path.c_str()));
}
TYPED_TEST(winfsp_test,
rename_dir_fails_directory_is_not_empty_and_replace_is_true) {
if (this->current_provider == provider_type::s3) {
return;
}
auto dir_path{
utils::path::combine(this->mount_location, {"test_dir_4"}),
};
auto dir_path2{
utils::path::combine(this->mount_location, {"test_dir2_4"}),
};
auto file_path{
utils::path::combine(dir_path, {"test_file_4"}),
};
ASSERT_TRUE(::CreateDirectoryA(dir_path.c_str(), nullptr));
auto handle = ::CreateFileA(file_path.c_str(), GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr,
CREATE_NEW, FILE_ATTRIBUTE_NORMAL, nullptr);
ASSERT_NE(INVALID_HANDLE_VALUE, handle);
::CloseHandle(handle);
EXPECT_FALSE(::MoveFileExA(dir_path.c_str(), dir_path2.c_str(),
MOVEFILE_REPLACE_EXISTING));
EXPECT_EQ(ERROR_ACCESS_DENIED, ::GetLastError());
EXPECT_TRUE(::DeleteFileA(file_path.c_str()));
EXPECT_TRUE(::RemoveDirectoryA(dir_path.c_str()));
}
} // namespace repertory
#endif // defined(_WIN32)

View File

@ -30,6 +30,17 @@
// TODO revisit create_share // TODO revisit create_share
// TODO revisit delete_access_test // TODO revisit delete_access_test
// TODO revisit getfileattr_test // TODO revisit getfileattr_test
// TODO revisit delete_ex_test
// TODO revisit rename_backslash_test
// TODO revisit rename_open_test
// TODO revisit rename_caseins_test
// TODO revisit rename_flipflop_test
// TODO revisit rename_mmap_test
// TODO revisit rename_standby_test
// TODO revisit rename_pid_test
// TODO revisit rename_ex_test
// TODO revisit setvolinfo_test
// TODO revisit query_winfsp_test
// //
// Implemented test cases based on WinFsp tests: // Implemented test cases based on WinFsp tests:

View File

@ -0,0 +1,100 @@
/*
Copyright <2018-2024> <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.
*/
#if defined(_WIN32)
//
// Implemented test cases based on WinFsp tests:
// https://github.com/winfsp/winfsp/blob/v2.0/tst/winfsp-tests
//
#include "fixtures/winfsp_fixture.hpp"
namespace repertory {
TYPED_TEST_CASE(winfsp_test, winfsp_provider_types);
TYPED_TEST(winfsp_test, volume_can_get_volume_info) {
std::string volume_label;
volume_label.resize(MAX_PATH + 1U);
std::string fs_name;
fs_name.resize(MAX_PATH + 1U);
DWORD flags{};
DWORD max_component_length{};
DWORD serial_num{};
EXPECT_TRUE(::GetVolumeInformationA(
this->mount_location.c_str(), volume_label.data(),
static_cast<DWORD>(volume_label.size()), &serial_num,
&max_component_length, &flags, fs_name.data(),
static_cast<DWORD>(fs_name.size())));
EXPECT_EQ(FILE_CASE_SENSITIVE_SEARCH | FILE_CASE_PRESERVED_NAMES |
FILE_UNICODE_ON_DISK,
flags);
EXPECT_EQ(255U, max_component_length);
EXPECT_EQ(0U, serial_num);
EXPECT_STREQ(
("repertory_" + app_config::get_provider_name(this->current_provider))
.c_str(),
volume_label.c_str());
EXPECT_STREQ(this->mount_location.c_str(), fs_name.c_str());
}
TYPED_TEST(winfsp_test, volume_can_get_size_info) {
{
DWORD bytes_per_sector{};
DWORD free_clusters{};
DWORD sectors_per_cluster{};
DWORD total_clusters{};
EXPECT_TRUE(::GetDiskFreeSpaceA(this->mount_location.c_str(),
&sectors_per_cluster, &bytes_per_sector,
&free_clusters, &total_clusters));
EXPECT_NE(0U, bytes_per_sector);
EXPECT_NE(0U, free_clusters);
EXPECT_NE(0U, sectors_per_cluster);
EXPECT_NE(0U, total_clusters);
}
{
ULARGE_INTEGER caller_free_bytes{};
ULARGE_INTEGER free_bytes{};
ULARGE_INTEGER total_bytes{};
EXPECT_TRUE(::GetDiskFreeSpaceExA(this->mount_location.c_str(),
&caller_free_bytes, &total_bytes,
&free_bytes));
EXPECT_NE(0U, caller_free_bytes.QuadPart);
EXPECT_NE(0U, total_bytes.QuadPart);
EXPECT_NE(0U, free_bytes.QuadPart);
}
}
TYPED_TEST(winfsp_test, volume_can_get_file_type) {
auto handle = ::CreateFileA(
this->mount_location.c_str(), 0, FILE_SHARE_READ | FILE_SHARE_WRITE,
nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr);
ASSERT_NE(INVALID_HANDLE_VALUE, handle);
EXPECT_EQ(FILE_TYPE_REMOTE, ::GetFileType(handle));
::CloseHandle(handle);
}
} // namespace repertory
#endif // defined(_WIN32)