[unit test] Complete FUSE unit tests #22
Some checks failed
Blockstorage/repertory/pipeline/head This commit looks good
BlockStorage/repertory/pipeline/head There was a failure building this commit

This commit is contained in:
2025-09-20 12:30:29 -05:00
parent 5f4a2703dc
commit 8df54749a5
5 changed files with 258 additions and 4 deletions

View File

@@ -106,6 +106,7 @@ endfunction
eventlib eventlib
expect_streq expect_streq
expect_strne expect_strne
falloc_fl_keep_size
fallocate fallocate
fallocate_impl fallocate_impl
fext fext

View File

@@ -22,6 +22,10 @@
* \#60 Implement secure key via KDF for transparent data encryption/decryption * \#60 Implement secure key via KDF for transparent data encryption/decryption
* \#61 [ui] UI theme should match repertory blue * \#61 [ui] UI theme should match repertory blue
### Changes from v2.0.7-release
* Fixed handling of `FALLOC_FL_KEEP_SIZE` on Linux
## v2.0.7-release ## v2.0.7-release
<!-- markdownlint-disable-next-line --> <!-- markdownlint-disable-next-line -->

View File

@@ -102,7 +102,7 @@ fuse_base::~fuse_base() { E_CONSUMER_RELEASE(); }
auto fuse_base::access_(const char *path, int mask) -> int { auto fuse_base::access_(const char *path, int mask) -> int {
REPERTORY_USES_FUNCTION_NAME(); REPERTORY_USES_FUNCTION_NAME();
return instance().instance().execute_callback( return instance().execute_callback(
function_name, path, [&](std::string api_path) -> api_error { function_name, path, [&](std::string api_path) -> api_error {
return instance().access_impl(std::move(api_path), mask); return instance().access_impl(std::move(api_path), mask);
}); });
@@ -112,7 +112,7 @@ auto fuse_base::access_(const char *path, int mask) -> int {
auto fuse_base::chflags_(const char *path, uint32_t flags) -> int { auto fuse_base::chflags_(const char *path, uint32_t flags) -> int {
REPERTORY_USES_FUNCTION_NAME(); REPERTORY_USES_FUNCTION_NAME();
return instance().instance().execute_callback( return instance().execute_callback(
function_name, path, [&](std::string api_path) -> api_error { function_name, path, [&](std::string api_path) -> api_error {
return instance().chflags_impl(std::move(api_path), flags); return instance().chflags_impl(std::move(api_path), flags);
}); });

View File

@@ -336,6 +336,7 @@ auto fuse_drive::fallocate_impl(std::string /*api_path*/, int mode,
i_open_file::native_operation_callback allocator; i_open_file::native_operation_callback allocator;
auto new_file_size = static_cast<std::uint64_t>(offset + length);
#if defined(__APPLE__) #if defined(__APPLE__)
fstore_t fstore = {0}; fstore_t fstore = {0};
if (not(mode & PREALLOCATE)) { if (not(mode & PREALLOCATE)) {
@@ -368,10 +369,13 @@ auto fuse_drive::fallocate_impl(std::string /*api_path*/, int mode,
return (fallocate(handle, mode, offset, length) == -1) ? api_error::os_error return (fallocate(handle, mode, offset, length) == -1) ? api_error::os_error
: api_error::success; : api_error::success;
}; };
if ((mode & FALLOC_FL_KEEP_SIZE) == FALLOC_FL_KEEP_SIZE) {
new_file_size = open_file->get_file_size();
}
#endif // __APPLE__ #endif // __APPLE__
return open_file->native_operation( return open_file->native_operation(new_file_size, allocator);
static_cast<std::uint64_t>(offset + length), allocator);
} }
auto fuse_drive::fgetattr_impl(std::string api_path, struct stat *u_stat, auto fuse_drive::fgetattr_impl(std::string api_path, struct stat *u_stat,

View File

@@ -0,0 +1,245 @@
/*
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.
*/
#if !defined(_WIN32)
#include "fixtures/drive_fixture.hpp"
namespace repertory {
TYPED_TEST_SUITE(fuse_test, platform_provider_types);
TYPED_TEST(fuse_test, fallocate_basic_preallocation_platform_semantics) {
std::string name{"fallocate"};
auto src = this->create_file_and_test(name);
auto desc = ::open(src.c_str(), O_RDWR);
ASSERT_NE(desc, -1);
constexpr off_t off = 0;
constexpr off_t len = 64 * 1024;
#if defined(__APPLE__)
fstore_t store{};
store.fst_flags = F_ALLOCATECONTIG;
store.fst_posmode = F_PEOFPOSMODE;
store.fst_offset = 0;
store.fst_length = len;
store.fst_bytesalloc = 0;
auto res = ::fcntl(desc, F_PREALLOCATE, &store);
if (res == -1) {
store.fst_flags = F_ALLOCATEALL;
res = ::fcntl(desc, F_PREALLOCATE, &store);
}
EXPECT_EQ(0, res);
struct stat st_unix{};
EXPECT_EQ(0, ::fstat(desc, &st_unix));
EXPECT_TRUE(S_ISREG(st_unix.st_mode));
EXPECT_EQ(0, st_unix.st_size);
#else // !defined(__APPLE__)
auto res = ::posix_fallocate(desc, off, len);
EXPECT_EQ(0, res);
struct stat st_unix{};
EXPECT_EQ(0, ::fstat(desc, &st_unix));
EXPECT_TRUE(S_ISREG(st_unix.st_mode));
EXPECT_EQ(off + len, st_unix.st_size);
#endif // defined(__APPLE__)
::close(desc);
this->unlink_file_and_test(src);
}
TYPED_TEST(fuse_test, fallocate_then_ftruncate_makes_size_visible) {
std::string name{"fallocate"};
auto src = this->create_file_and_test(name);
auto desc = ::open(src.c_str(), O_RDWR);
ASSERT_NE(desc, -1);
constexpr off_t len = 128 * 1024;
#if defined(__APPLE__)
fstore_t store{};
store.fst_flags = F_ALLOCATECONTIG;
store.fst_posmode = F_PEOFPOSMODE;
store.fst_offset = 0;
store.fst_length = len;
store.fst_bytesalloc = 0;
auto res = ::fcntl(desc, F_PREALLOCATE, &store);
if (res == -1) {
store.fst_flags = F_ALLOCATEALL;
res = ::fcntl(desc, F_PREALLOCATE, &store);
}
EXPECT_EQ(0, res);
EXPECT_EQ(0, ::ftruncate(desc, len));
#else // !defined(__APPLE__)
auto res = ::posix_fallocate(desc, 0, len);
EXPECT_EQ(0, res);
EXPECT_EQ(0, ::ftruncate(desc, len / 2));
EXPECT_EQ(0, ::ftruncate(desc, len));
#endif // defined(__APPLE__)
struct stat st_unix{};
EXPECT_EQ(0, ::fstat(desc, &st_unix));
EXPECT_TRUE(S_ISREG(st_unix.st_mode));
EXPECT_EQ(len, st_unix.st_size);
::close(desc);
this->unlink_file_and_test(src);
}
#if !defined(__APPLE__)
TYPED_TEST(fuse_test,
fallocate_does_not_change_size_when_keep_size_is_specified) {
std::string name{"fallocate"};
auto src = this->create_file_and_test(name);
auto desc = ::open(src.c_str(), O_RDWR);
ASSERT_NE(desc, -1);
EXPECT_EQ(0, ::ftruncate(desc, 4096));
constexpr off_t len = 64 * 1024;
errno = 0;
auto res = ::fallocate(desc, FALLOC_FL_KEEP_SIZE, 0, len);
if (res == -1 &&
(errno == ENOSYS || errno == EOPNOTSUPP || errno == EINVAL)) {
::close(desc);
this->unlink_file_and_test(src);
return;
}
EXPECT_EQ(0, res);
struct stat st_unix{};
EXPECT_EQ(0, ::fstat(desc, &st_unix));
EXPECT_TRUE(S_ISREG(st_unix.st_mode));
EXPECT_EQ(4096, st_unix.st_size);
::close(desc);
this->unlink_file_and_test(src);
}
TYPED_TEST(
fuse_test,
fallocate_does_not_change_size_when_keep_size_and_punch_hole_are_specified) {
std::string name{"fallocate"};
auto src = this->create_file_and_test(name);
auto desc = ::open(src.c_str(), O_RDWR);
ASSERT_NE(desc, -1);
constexpr off_t size = 64 * 1024;
EXPECT_EQ(0, ::ftruncate(desc, size));
constexpr off_t hole_off = 24 * 1024;
constexpr off_t hole_len = 8 * 1024;
errno = 0;
auto res = ::fallocate(desc, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE,
hole_off, hole_len);
if (res == -1 &&
(errno == ENOSYS || errno == EOPNOTSUPP || errno == EINVAL)) {
::close(desc);
this->unlink_file_and_test(src);
return;
}
EXPECT_EQ(0, res) << "errno: " << errno;
struct stat st_unix{};
EXPECT_EQ(0, ::fstat(desc, &st_unix));
EXPECT_EQ(size, st_unix.st_size);
::close(desc);
this->unlink_file_and_test(src);
}
#endif // !defined(__APPLE__)
TYPED_TEST(fuse_test, fallocate_can_handle_invalid_arguments) {
std::string name{"fallocate"};
auto src = this->create_file_and_test(name);
auto desc = ::open(src.c_str(), O_RDWR);
ASSERT_NE(desc, -1);
#if defined(__APPLE__)
fstore_t store{};
store.fst_flags = F_ALLOCATEALL;
store.fst_posmode = F_PEOFPOSMODE;
store.fst_offset = 0;
store.fst_length = 0;
errno = 0;
auto res = ::fcntl(desc, F_PREALLOCATE, &store);
if (res == 0) {
::close(desc);
this->unlink_file_and_test(src);
return;
}
EXPECT_EQ(-1, res);
EXPECT_TRUE(errno == EINVAL || errno == EOPNOTSUPP || errno == ENOSYS);
#else // !defined(__APPLE__)
auto ret1 = ::posix_fallocate(desc, -1, 4096);
EXPECT_EQ(EINVAL, ret1);
auto ret2 = ::posix_fallocate(desc, 0, -4096);
EXPECT_EQ(EINVAL, ret2);
#endif // defined(__APPLE__)
::close(desc);
this->unlink_file_and_test(src);
}
TYPED_TEST(fuse_test, fallocate_fails_on_directory) {
std::string dir_name{"dir"};
auto dir = this->create_directory_and_test(dir_name);
#if defined(__APPLE__)
auto desc = ::open(dir.c_str(), O_RDONLY);
EXPECT_NE(desc, -1);
fstore_t store{};
store.fst_flags = F_ALLOCATEALL;
store.fst_posmode = F_PEOFPOSMODE;
store.fst_offset = 0;
store.fst_length = 4096;
errno = 0;
auto res = ::fcntl(desc, F_PREALLOCATE, &store);
EXPECT_EQ(-1, res);
EXPECT_TRUE(errno == EISDIR || errno == EBADF || errno == EOPNOTSUPP ||
errno == ENOTTY || errno == ENOSYS);
::close(desc);
#else // !defined(__APPLE__)
auto desc = ::open(dir.c_str(), O_RDONLY | O_DIRECTORY);
EXPECT_NE(desc, -1);
auto ret = ::posix_fallocate(desc, 0, 4096);
EXPECT_NE(0, ret);
::close(desc);
#endif // defined(__APPLE__)
this->rmdir_and_test(dir);
}
} // namespace repertory
#endif // !defined(_WIN32)