fuse unit tests and fixes
All checks were successful
BlockStorage/repertory/pipeline/head This commit looks good

This commit is contained in:
Scott E. Graves 2024-11-10 16:53:00 -06:00
parent d7d9199f8e
commit 74546807f4
2 changed files with 104 additions and 71 deletions

View File

@ -81,8 +81,8 @@ auto fuse_drive::chown_impl(std::string api_path, uid_t uid, gid_t gid,
struct fuse_file_info * /*file_info*/) struct fuse_file_info * /*file_info*/)
-> api_error { -> api_error {
#else #else
auto fuse_drive::chown_impl(std::string api_path, uid_t uid, gid_t gid) auto fuse_drive::chown_impl(std::string api_path, uid_t uid,
-> api_error { gid_t gid) -> api_error {
#endif #endif
return check_and_perform( return check_and_perform(
api_path, X_OK, [&](api_meta_map &meta) -> api_error { api_path, X_OK, [&](api_meta_map &meta) -> api_error {
@ -116,16 +116,13 @@ auto fuse_drive::create_impl(std::string api_path, mode_t mode,
struct fuse_file_info *file_info) -> api_error { struct fuse_file_info *file_info) -> api_error {
file_info->fh = 0U; file_info->fh = 0U;
const auto is_directory_op = auto is_create_op = ((file_info->flags & O_CREAT) == O_CREAT);
((file_info->flags & O_DIRECTORY) == O_DIRECTORY); auto is_directory_op = ((file_info->flags & O_DIRECTORY) == O_DIRECTORY);
const auto is_create_op = ((file_info->flags & O_CREAT) == O_CREAT); auto is_truncate_op = ((file_info->flags & O_TRUNC) == O_TRUNC);
const auto is_truncate_op = (((file_info->flags & O_TRUNC) != 0) &&
(((file_info->flags & O_WRONLY) != 0) ||
((file_info->flags & O_RDWR) != 0)));
if (((file_info->flags & O_WRONLY) != 0) || if (((file_info->flags & O_WRONLY) != 0) ||
((file_info->flags & O_RDWR) != 0)) { ((file_info->flags & O_RDWR) != 0)) {
const auto res = provider_.is_file_writeable(api_path) auto res = provider_.is_file_writeable(api_path)
? api_error::success ? api_error::success
: api_error::permission_denied; : api_error::permission_denied;
if (res != api_error::success) { if (res != api_error::success) {
@ -167,21 +164,26 @@ auto fuse_drive::create_impl(std::string api_path, mode_t mode,
if (res != api_error::success) { if (res != api_error::success) {
return res; return res;
} }
if (not(is_directory_op ? dir_exists : file_exists)) { if (not(is_directory_op ? dir_exists : file_exists)) {
return (is_directory_op ? api_error::directory_not_found return (is_directory_op ? api_error::directory_not_found
: api_error::item_not_found); : api_error::item_not_found);
} }
if (is_truncate_op && not file_exists) {
return api_error::item_not_found;
}
} }
std::uint64_t handle{}; std::uint64_t handle{};
{ {
std::shared_ptr<i_open_file> open_file; std::shared_ptr<i_open_file> open_file;
if (is_create_op) { if (is_create_op) {
const auto now = utils::time::get_time_now(); auto now = utils::time::get_time_now();
#if defined(__APPLE__) #if defined(__APPLE__)
const auto osx_flags = static_cast<std::uint32_t>(file_info->flags); auto osx_flags = static_cast<std::uint32_t>(file_info->flags);
#else // !defined(__APPLE__) #else // !defined(__APPLE__)
const auto osx_flags = 0U; auto osx_flags = 0U;
#endif // defined(__APPLE__) #endif // defined(__APPLE__)
auto meta = create_meta_attributes( auto meta = create_meta_attributes(
@ -388,7 +390,7 @@ api_error fuse_drive::ftruncate_impl(std::string /*api_path*/, off_t size,
return api_error::invalid_handle; return api_error::invalid_handle;
} }
const auto res = check_writeable(f->get_open_data(file_info->fh), auto res = check_writeable(f->get_open_data(file_info->fh),
api_error::invalid_handle); api_error::invalid_handle);
if (res != api_error::success) { if (res != api_error::success) {
return res; return res;
@ -440,7 +442,7 @@ auto fuse_drive::get_item_meta(const std::string &api_path,
const std::string &name, const std::string &name,
std::string &value) const -> api_error { std::string &value) const -> api_error {
api_meta_map meta{}; api_meta_map meta{};
const auto ret = get_item_meta(api_path, meta); auto ret = get_item_meta(api_path, meta);
if (ret == api_error::success) { if (ret == api_error::success) {
value = meta[name]; value = meta[name];
} }
@ -453,10 +455,10 @@ auto fuse_drive::getattr_impl(std::string api_path, struct stat *st,
struct fuse_file_info * /*file_info*/) struct fuse_file_info * /*file_info*/)
-> api_error { -> api_error {
#else #else
auto fuse_drive::getattr_impl(std::string api_path, struct stat *st) auto fuse_drive::getattr_impl(std::string api_path,
-> api_error { struct stat *st) -> api_error {
#endif #endif
const auto parent = utils::path::get_parent_api_path(api_path); auto parent = utils::path::get_parent_api_path(api_path);
auto res = check_parent_access(api_path, X_OK); auto res = check_parent_access(api_path, X_OK);
if (res != api_error::success) { if (res != api_error::success) {
@ -536,8 +538,8 @@ auto fuse_drive::getxtimes_impl(std::string api_path, struct timespec *bkuptime,
#endif // __APPLE__ #endif // __APPLE__
#if FUSE_USE_VERSION >= 30 #if FUSE_USE_VERSION >= 30
auto fuse_drive::init_impl(struct fuse_conn_info *conn, struct fuse_config *cfg) auto fuse_drive::init_impl(struct fuse_conn_info *conn,
-> void * { struct fuse_config *cfg) -> void * {
#else #else
void *fuse_drive::init_impl(struct fuse_conn_info *conn) { void *fuse_drive::init_impl(struct fuse_conn_info *conn) {
#endif #endif
@ -617,7 +619,7 @@ auto fuse_drive::mkdir_impl(std::string api_path, mode_t mode) -> api_error {
return res; return res;
} }
const auto now = utils::time::get_time_now(); auto now = utils::time::get_time_now();
auto meta = create_meta_attributes(now, FILE_ATTRIBUTE_DIRECTORY, now, now, auto meta = create_meta_attributes(now, FILE_ATTRIBUTE_DIRECTORY, now, now,
true, get_effective_gid(), "", mode, now, true, get_effective_gid(), "", mode, now,
0U, 0U, 0U, "", get_effective_uid(), now); 0U, 0U, 0U, "", get_effective_uid(), now);
@ -656,8 +658,7 @@ auto fuse_drive::open_impl(std::string api_path,
auto fuse_drive::opendir_impl(std::string api_path, auto fuse_drive::opendir_impl(std::string api_path,
struct fuse_file_info *file_info) -> api_error { struct fuse_file_info *file_info) -> api_error {
const auto mask = auto mask = (O_RDONLY != (file_info->flags & O_ACCMODE) ? W_OK : R_OK) | X_OK;
(O_RDONLY != (file_info->flags & O_ACCMODE) ? W_OK : R_OK) | X_OK;
auto res = check_access(api_path, mask); auto res = check_access(api_path, mask);
if (res != api_error::success) { if (res != api_error::success) {
return res; return res;
@ -762,9 +763,8 @@ auto fuse_drive::release_impl(std::string /*api_path*/,
return api_error::success; return api_error::success;
} }
auto fuse_drive::releasedir_impl(std::string /*api_path*/, auto fuse_drive::releasedir_impl(
struct fuse_file_info *file_info) std::string /*api_path*/, struct fuse_file_info *file_info) -> api_error {
-> api_error {
auto iter = directory_cache_->get_directory(file_info->fh); auto iter = directory_cache_->get_directory(file_info->fh);
if (iter == nullptr) { if (iter == nullptr) {
return api_error::invalid_handle; return api_error::invalid_handle;
@ -776,15 +776,15 @@ auto fuse_drive::releasedir_impl(std::string /*api_path*/,
auto fuse_drive::rename_directory(const std::string &from_api_path, auto fuse_drive::rename_directory(const std::string &from_api_path,
const std::string &to_api_path) -> int { const std::string &to_api_path) -> int {
const auto res = fm_->rename_directory(from_api_path, to_api_path); auto res = fm_->rename_directory(from_api_path, to_api_path);
errno = std::abs(utils::from_api_error(res)); errno = std::abs(utils::from_api_error(res));
return (res == api_error::success) ? 0 : -1; return (res == api_error::success) ? 0 : -1;
} }
auto fuse_drive::rename_file(const std::string &from_api_path, auto fuse_drive::rename_file(const std::string &from_api_path,
const std::string &to_api_path, bool overwrite) const std::string &to_api_path,
-> int { bool overwrite) -> int {
const auto res = fm_->rename_file(from_api_path, to_api_path, overwrite); auto res = fm_->rename_file(from_api_path, to_api_path, overwrite);
errno = std::abs(utils::from_api_error(res)); errno = std::abs(utils::from_api_error(res));
return (res == api_error::success) ? 0 : -1; return (res == api_error::success) ? 0 : -1;
} }
@ -793,8 +793,8 @@ auto fuse_drive::rename_file(const std::string &from_api_path,
auto fuse_drive::rename_impl(std::string from_api_path, std::string to_api_path, auto fuse_drive::rename_impl(std::string from_api_path, std::string to_api_path,
unsigned int /*flags*/) -> api_error { unsigned int /*flags*/) -> api_error {
#else #else
auto fuse_drive::rename_impl(std::string from_api_path, std::string to_api_path) auto fuse_drive::rename_impl(std::string from_api_path,
-> api_error { std::string to_api_path) -> api_error {
#endif #endif
auto res = check_parent_access(to_api_path, W_OK | X_OK); auto res = check_parent_access(to_api_path, W_OK | X_OK);
if (res != api_error::success) { if (res != api_error::success) {
@ -874,7 +874,7 @@ auto fuse_drive::getxattr_common(std::string api_path, const char *name,
((res = provider_.get_item_meta(api_path, meta)) == api_error::success)) { ((res = provider_.get_item_meta(api_path, meta)) == api_error::success)) {
res = api_error::xattr_not_found; res = api_error::xattr_not_found;
if (meta.find(attribute_name) != meta.end()) { if (meta.find(attribute_name) != meta.end()) {
const auto data = macaron::Base64::Decode(meta[attribute_name]); auto data = macaron::Base64::Decode(meta[attribute_name]);
if ((position == nullptr) || (*position < data.size())) { if ((position == nullptr) || (*position < data.size())) {
res = api_error::success; res = api_error::success;
attribute_size = static_cast<int>(data.size()); attribute_size = static_cast<int>(data.size());
@ -901,16 +901,16 @@ auto fuse_drive::getxattr_impl(std::string api_path, const char *name,
} }
#else // __APPLE__ #else // __APPLE__
auto fuse_drive::getxattr_impl(std::string api_path, const char *name, auto fuse_drive::getxattr_impl(std::string api_path, const char *name,
char *value, size_t size, int &attribute_size) char *value, size_t size,
-> api_error { int &attribute_size) -> api_error {
return getxattr_common(api_path, name, value, size, attribute_size, nullptr); return getxattr_common(api_path, name, value, size, attribute_size, nullptr);
} }
#endif // __APPLE__ #endif // __APPLE__
auto fuse_drive::listxattr_impl(std::string api_path, char *buffer, size_t size, auto fuse_drive::listxattr_impl(std::string api_path, char *buffer, size_t size,
int &required_size, bool &return_size) int &required_size,
-> api_error { bool &return_size) -> api_error {
const auto check_size = (size == 0); auto check_size = (size == 0);
auto res = check_parent_access(api_path, X_OK); auto res = check_parent_access(api_path, X_OK);
if (res != api_error::success) { if (res != api_error::success) {
@ -925,7 +925,7 @@ auto fuse_drive::listxattr_impl(std::string api_path, char *buffer, size_t size,
#if defined(__APPLE__) #if defined(__APPLE__)
if (attribute_name != G_KAUTH_FILESEC_XATTR) { if (attribute_name != G_KAUTH_FILESEC_XATTR) {
#endif #endif
const auto attribute_name_size = strlen(attribute_name.c_str()) + 1U; auto attribute_name_size = strlen(attribute_name.c_str()) + 1U;
if (size >= attribute_name_size) { if (size >= attribute_name_size) {
std::memcpy(&buffer[required_size], attribute_name.data(), std::memcpy(&buffer[required_size], attribute_name.data(),
attribute_name_size); attribute_name_size);
@ -948,8 +948,8 @@ auto fuse_drive::listxattr_impl(std::string api_path, char *buffer, size_t size,
return res; return res;
} }
auto fuse_drive::removexattr_impl(std::string api_path, const char *name) auto fuse_drive::removexattr_impl(std::string api_path,
-> api_error { const char *name) -> api_error {
std::string attribute_name; std::string attribute_name;
#if defined(__APPLE__) #if defined(__APPLE__)
auto res = parse_xattr_parameters(name, 0, attribute_name, api_path); auto res = parse_xattr_parameters(name, 0, attribute_name, api_path);
@ -977,8 +977,8 @@ auto fuse_drive::setxattr_impl(std::string api_path, const char *name,
uint32_t position) -> api_error { uint32_t position) -> api_error {
#else // __APPLE__ #else // __APPLE__
auto fuse_drive::setxattr_impl(std::string api_path, const char *name, auto fuse_drive::setxattr_impl(std::string api_path, const char *name,
const char *value, size_t size, int flags) const char *value, size_t size,
-> api_error { int flags) -> api_error {
#endif #endif
std::string attribute_name; std::string attribute_name;
#if defined(__APPLE__) #if defined(__APPLE__)
@ -992,7 +992,7 @@ auto fuse_drive::setxattr_impl(std::string api_path, const char *name,
return res; return res;
} }
const auto attribute_namespace = auto attribute_namespace =
utils::string::contains(attribute_name, ".") utils::string::contains(attribute_name, ".")
? utils::string::split(attribute_name, '.', false)[0U] ? utils::string::split(attribute_name, '.', false)[0U]
: ""; : "";
@ -1053,8 +1053,8 @@ void fuse_drive::set_item_meta(const std::string &api_path,
} }
#if defined(__APPLE__) #if defined(__APPLE__)
auto fuse_drive::setattr_x_impl(std::string api_path, struct setattr_x *attr) auto fuse_drive::setattr_x_impl(std::string api_path,
-> api_error { struct setattr_x *attr) -> api_error {
bool exists{}; bool exists{};
auto res = provider_.is_file(api_path, exists); auto res = provider_.is_file(api_path, exists);
if (res != api_error::success) { if (res != api_error::success) {
@ -1153,12 +1153,11 @@ auto fuse_drive::setattr_x_impl(std::string api_path, struct setattr_x *attr)
return api_error::success; return api_error::success;
} }
auto fuse_drive::setbkuptime_impl(std::string api_path, auto fuse_drive::setbkuptime_impl(
const struct timespec *bkuptime) std::string api_path, const struct timespec *bkuptime) -> api_error {
-> api_error {
return check_and_perform( return check_and_perform(
api_path, X_OK, [&](api_meta_map &meta) -> api_error { api_path, X_OK, [&](api_meta_map &meta) -> api_error {
const auto nanos = bkuptime->tv_nsec + auto nanos = bkuptime->tv_nsec +
(bkuptime->tv_nsec * utils::time::NANOS_PER_SECOND); (bkuptime->tv_nsec * utils::time::NANOS_PER_SECOND);
return provider_.set_item_meta(api_path, META_BACKUP, return provider_.set_item_meta(api_path, META_BACKUP,
std::to_string(nanos)); std::to_string(nanos));
@ -1169,7 +1168,7 @@ auto fuse_drive::setchgtime_impl(std::string api_path,
const struct timespec *chgtime) -> api_error { const struct timespec *chgtime) -> api_error {
return check_and_perform( return check_and_perform(
api_path, X_OK, [&](api_meta_map &meta) -> api_error { api_path, X_OK, [&](api_meta_map &meta) -> api_error {
const auto nanos = chgtime->tv_nsec + auto nanos = chgtime->tv_nsec +
(chgtime->tv_nsec * utils::time::NANOS_PER_SECOND); (chgtime->tv_nsec * utils::time::NANOS_PER_SECOND);
return provider_.set_item_meta(api_path, META_CHANGED, return provider_.set_item_meta(api_path, META_CHANGED,
std::to_string(nanos)); std::to_string(nanos));
@ -1180,7 +1179,7 @@ auto fuse_drive::setcrtime_impl(std::string api_path,
const struct timespec *crtime) -> api_error { const struct timespec *crtime) -> api_error {
return check_and_perform( return check_and_perform(
api_path, X_OK, [&](api_meta_map &meta) -> api_error { api_path, X_OK, [&](api_meta_map &meta) -> api_error {
const auto nanos = auto nanos =
crtime->tv_nsec + (crtime->tv_nsec * utils::time::NANOS_PER_SECOND); crtime->tv_nsec + (crtime->tv_nsec * utils::time::NANOS_PER_SECOND);
return provider_.set_item_meta(api_path, META_CREATION, return provider_.set_item_meta(api_path, META_CREATION,
std::to_string(nanos)); std::to_string(nanos));
@ -1191,15 +1190,15 @@ auto fuse_drive::setvolname_impl(const char * /*volname*/) -> api_error {
return api_error::success; return api_error::success;
} }
auto fuse_drive::statfs_x_impl(std::string /*api_path*/, struct statfs *stbuf) auto fuse_drive::statfs_x_impl(std::string /*api_path*/,
-> api_error { struct statfs *stbuf) -> api_error {
if (statfs(&config_.get_cache_directory()[0], stbuf) != 0) { if (statfs(&config_.get_cache_directory()[0], stbuf) != 0) {
return api_error::os_error; return api_error::os_error;
} }
const auto total_bytes = provider_.get_total_drive_space(); auto total_bytes = provider_.get_total_drive_space();
const auto total_used = provider_.get_used_drive_space(); auto total_used = provider_.get_used_drive_space();
const auto used_blocks = utils::divide_with_ceiling( auto used_blocks = utils::divide_with_ceiling(
total_used, static_cast<std::uint64_t>(stbuf->f_bsize)); total_used, static_cast<std::uint64_t>(stbuf->f_bsize));
stbuf->f_blocks = utils::divide_with_ceiling( stbuf->f_blocks = utils::divide_with_ceiling(
total_bytes, static_cast<std::uint64_t>(stbuf->f_bsize)); total_bytes, static_cast<std::uint64_t>(stbuf->f_bsize));
@ -1217,16 +1216,15 @@ auto fuse_drive::statfs_x_impl(std::string /*api_path*/, struct statfs *stbuf)
return api_error::success; return api_error::success;
} }
#else // __APPLE__ #else // __APPLE__
auto fuse_drive::statfs_impl(std::string /*api_path*/, struct statvfs *stbuf) auto fuse_drive::statfs_impl(std::string /*api_path*/,
-> api_error { struct statvfs *stbuf) -> api_error {
if (statvfs(config_.get_cache_directory().data(), stbuf) != 0) { if (statvfs(config_.get_cache_directory().data(), stbuf) != 0) {
return api_error::os_error; return api_error::os_error;
} }
const auto total_bytes = provider_.get_total_drive_space(); auto total_bytes = provider_.get_total_drive_space();
const auto total_used = provider_.get_used_drive_space(); auto total_used = provider_.get_used_drive_space();
const auto used_blocks = auto used_blocks = utils::divide_with_ceiling(total_used, stbuf->f_frsize);
utils::divide_with_ceiling(total_used, stbuf->f_frsize);
stbuf->f_files = 4294967295; stbuf->f_files = 4294967295;
stbuf->f_blocks = utils::divide_with_ceiling(total_bytes, stbuf->f_frsize); stbuf->f_blocks = utils::divide_with_ceiling(total_bytes, stbuf->f_frsize);
stbuf->f_bavail = stbuf->f_bfree = stbuf->f_bavail = stbuf->f_bfree =
@ -1299,8 +1297,8 @@ auto fuse_drive::utimens_impl(std::string api_path, const struct timespec tv[2],
struct fuse_file_info * /*file_info*/) struct fuse_file_info * /*file_info*/)
-> api_error { -> api_error {
#else #else
auto fuse_drive::utimens_impl(std::string api_path, const struct timespec tv[2]) auto fuse_drive::utimens_impl(std::string api_path,
-> api_error { const struct timespec tv[2]) -> api_error {
#endif #endif
api_meta_map meta; api_meta_map meta;
auto res = provider_.get_item_meta(api_path, meta); auto res = provider_.get_item_meta(api_path, meta);

View File

@ -71,7 +71,42 @@ TYPED_TEST(fuse_test, create_can_create_file) {
this->unlink_file_and_test(file_path); this->unlink_file_and_test(file_path);
} }
TYPED_TEST(fuse_test, create_can_create_and_truncate_file) { TYPED_TEST(fuse_test, create_can_create_with_truncate_file) {
std::string file_name{"create_test"};
auto file_path = this->create_file_path(file_name);
auto handle = open(file_path.c_str(), O_CREAT | O_TRUNC, ACCESSPERMS);
EXPECT_LE(1, handle);
close(handle);
auto size = utils::file::file{file_path}.size();
EXPECT_TRUE(size.has_value());
EXPECT_EQ(0U, *size);
this->unlink_file_and_test(file_path);
}
TYPED_TEST(fuse_test, create_can_create_or_open_and_truncate_existing_file) {
std::string file_name{"create_test"};
auto file_path = this->create_file_and_test(file_name);
EXPECT_EQ(0, truncate(file_path.c_str(), 24U));
auto size = utils::file::file{file_path}.size();
EXPECT_TRUE(size.has_value());
EXPECT_EQ(24U, *size);
auto handle = open(file_path.c_str(), O_CREAT | O_TRUNC, ACCESSPERMS);
EXPECT_LE(1, handle);
close(handle);
size = utils::file::file{file_path}.size();
EXPECT_TRUE(size.has_value());
EXPECT_EQ(0U, *size);
this->unlink_file_and_test(file_path);
}
TYPED_TEST(fuse_test, create_can_open_and_truncate_existing_file) {
std::string file_name{"create_test"}; std::string file_name{"create_test"};
auto file_path = this->create_file_and_test(file_name); auto file_path = this->create_file_and_test(file_name);
EXPECT_EQ(0, truncate(file_path.c_str(), 24U)); EXPECT_EQ(0, truncate(file_path.c_str(), 24U));