diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e6789039..b20191f6 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -125,15 +125,7 @@ LLFS_DefineLibrary(llfs ./llfs ${LLFS_Deps}) #=#=#==#==#===============+=+=+=+=++=++++++++++++++-++-+--+-+----+--------------- -file(GLOB llfs_cli_Sources - ./llfs_cli/*.cpp - ./llfs_cli/*/*.cpp - ./llfs_cli/*/*/*.cpp - ) - -add_executable(llfs_cli ${llfs_cli_Sources}) - -target_link_libraries(llfs_cli +target_link_libraries( llfs batteries::batteries Boost::context @@ -143,28 +135,9 @@ target_link_libraries(llfs_cli dl stdc++fs) -set_target_properties(llfs_cli PROPERTIES OUTPUT_NAME "llfs") - -# Default packaging instructions for the library. -# -install(TARGETS llfs_cli DESTINATION "." - RUNTIME DESTINATION bin - ARCHIVE DESTINATION lib - LIBRARY DESTINATION lib - ) - -LLFS_CollectHeaders(llfs_cli ./llfs_cli) #=#=#==#==#===============+=+=+=+=++=++++++++++++++-++-+--+-+----+--------------- -file(GLOB llfs_fuse_Sources - ./llfs_fuse/*.cpp - ./llfs_fuse/*/*.cpp - ./llfs_fuse/*/*/*.cpp - ) - -add_executable(llfs_fuse ${llfs_fuse_Sources}) - set(LLFS_TestDeps llfs batteries::batteries @@ -181,4 +154,3 @@ if (LINUX) set(LLFS_TestDeps ${LLFS_TestDeps} libfuse::libfuse) endif() -target_link_libraries(llfs_fuse ${LLFS_TestDeps}) diff --git a/src/llfs/file_log_driver.cpp b/src/llfs/file_log_driver.cpp deleted file mode 100644 index ef7308da..00000000 --- a/src/llfs/file_log_driver.cpp +++ /dev/null @@ -1,405 +0,0 @@ -//#=##=##=#==#=#==#===#+==#+==========+==+=+=+=+=+=++=+++=+++++=-++++=-+++++++++++ -// -// Part of the LLFS Project, under Apache License v2.0. -// See https://www.apache.org/licenses/LICENSE-2.0 for license information. -// SPDX short identifier: Apache-2.0 -// -//+++++++++++-+-+--+----- --- -- - - - - - -#include -// -#include -#include - -#include - -#include - -#include - -#include -#include -#include - -namespace llfs { - -FileLogDriver::Config FileLogDriver::default_config(const PageCacheOptions& opts) -{ - return Config{ - .min_segment_split_size = 1 * kMiB, - .max_size = opts.default_log_size(), - }; -} - -//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - - -StatusOr> FileLogDriver::initialize( - const Location& location, const Config& config, batt::TaskScheduler& scheduler, - ConfirmThisWillEraseAllMyData confirm) -{ - initialize_status_codes(); - - // Confirm the destructive operation. - // - if (confirm != ConfirmThisWillEraseAllMyData::kYes) { - return ::llfs::make_status(StatusCode::kFileLogEraseNotConfirmed); - } - - std::error_code ec; - - // Delete the old log device file structure, if present. - // - fs::remove_all(location.parent_dir(), ec); - BATT_REQUIRE_OK(ec) << "Could not remove the FileLogDevice parent directory"; - - // Create the parent directory. - // - fs::create_directories(location.parent_dir(), ec); - BATT_REQUIRE_OK(ec) << "Could not create the FileLogDevice parent directory"; - - // Write configuration file. - // - { - std::ofstream ofs(location.config_file_path().string().c_str()); - ofs << config.max_size << " " << config.min_segment_split_size; - if (!ofs.good()) { - return ::llfs::make_status(::llfs::StatusCode::kFileLogDeviceConfigWriteFailed); - } - } - - return FileLogDriver::recover( - location, - /*scan_fn=*/ - [](LogDevice::Reader& reader) -> Status { - BATT_CHECK_EQ(reader.data().size(), 0); - return OkStatus(); - }, - scheduler); -} - -//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - -// -StatusOr> FileLogDriver::recover(const Location& location, - const LogScanFn& scan_fn, - batt::TaskScheduler& scheduler) -{ - // Read configuration file. - // - Config config; - { - std::ifstream ifs(location.config_file_path().string().c_str()); - if (ifs.good()) { - ifs >> config.max_size >> config.min_segment_split_size; - if (!ifs.good() && !ifs.eof()) { - return ::llfs::make_status(::llfs::StatusCode::kFileLogDeviceConfigReadFailed); - } - } - } - - // Create the device. - // - std::unique_ptr log_device = std::make_unique( - RingBuffer::TempFile{config.max_size}, location, config, scheduler); - - FileLogDriver& driver = log_device->driver().impl(); - - // Read all segment files into the ring buffer. - // - StatusOr active_file = driver.recover_segments(); - BATT_REQUIRE_OK(active_file); - - // Run the scan function to validate the loaded data and determine the number (if any) of - // partially flushed bytes that might reside at the end. - // - std::unique_ptr log_reader = - log_device->new_reader(/*slot_lower_bound=*/None, LogReadMode::kDurable); - - StatusOr scan_status = scan_fn(*log_reader); - BATT_REQUIRE_OK(scan_status); - - // Whatever wasn't consumed by the scan function must be truncated from the end of the log. - // - const auto bytes_to_truncate = LLFS_CHECKED_SLOT_DISTANCE(*scan_status, driver.get_flush_pos()); - auto truncate_status = active_file->truncate(bytes_to_truncate); - BATT_REQUIRE_OK(truncate_status); - - // Roll back the commit/flush pointers by the specified amount. If this creates a negative-sized - // log, panic. - // - driver.shared_state_.flush_pos.fetch_sub(bytes_to_truncate); - driver.shared_state_.commit_pos.fetch_sub(bytes_to_truncate); - - BATT_CHECK(!slot_less_than(driver.shared_state_.flush_pos.get_value(), - driver.shared_state_.trim_pos.get_value())) - << "\n flush_pos=" << driver.shared_state_.flush_pos.get_value() - << "\n trim_pos=" << driver.shared_state_.trim_pos.get_value(); - - BATT_CHECK(!slot_less_than(driver.shared_state_.commit_pos.get_value(), - driver.shared_state_.trim_pos.get_value())) - << "\n commit_pos=" << driver.shared_state_.commit_pos.get_value() - << "\n trim_pos=" << driver.shared_state_.trim_pos.get_value(); - - // Ready to go! Start the background tasks (flush and trim). - // - driver.start_tasks(std::move(*active_file)); - - return log_device; -} - -//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - -// -StatusOr FileLogDriver::recover_segments() -{ - LLFS_LOG_INFO() << "FileLogDriver::recover_segments"; - - fs::path prefix_dir = this->shared_state_.location.parent_dir(); - std::string prefix_base(FileLogDriver::Location::segment_file_prefix()); - - // Scan the directory, picking up all segment files and inserting them into a vector of - // SegmentFile objects. - // - std::vector segments; - std::error_code ec; - fs::directory_iterator dir_iter(prefix_dir, ec); - BATT_REQUIRE_OK(ec) << batt::LogLevel::kInfo - << "creating dir_iter from prefix_dir: " << prefix_dir; - for (const auto& p : dir_iter) { - std::string name = p.path().filename().string(); - LLFS_LOG_INFO() << "found file: " << name; - if (boost::algorithm::starts_with(name, prefix_base) && - boost::algorithm::ends_with(name, FileLogDriver::Location::segment_ext())) { - Optional slot_range = - this->shared_state_.location.slot_range_from_segment_file_name(name); - if (slot_range) { - segments.emplace_back(SegmentFile{ - .slot_range = *slot_range, - .file_name = name, - }); - } - } - } - LLFS_LOG_INFO() << "finished scanning log segments"; - - // Sort the segments by slot offset. - // - std::sort(segments.begin(), segments.end(), - [](const SegmentFile& left, const SegmentFile& right) { - return left.slot_range.lower_bound < right.slot_range.lower_bound; - }); - - // Detect any discontinuity in the segments; this indicates slot offset integer wrap-around. - // - // For example: - // [segment_fffe, segment_ffff, segment_0000, segment_0001, segment_0002, ...] - // ^^^^^^^^^^^^^^^^^^^^^^^^^^ We are trying to find pairs like this! - // - auto last = std::adjacent_find( - segments.begin(), segments.end(), [](const SegmentFile& left, const SegmentFile& right) { - return left.slot_range.upper_bound < right.slot_range.lower_bound; - }); - - if (last != segments.end()) { - std::rotate(segments.begin(), std::next(last), segments.end()); - } - - const slot_offset_type recovered_lower_bound = [&]() -> slot_offset_type { - if (segments.empty()) { - return 0; - } - return segments.front().slot_range.lower_bound; - }(); - - LLFS_LOG_INFO() << "recovered log lower_bound=" << recovered_lower_bound; - - // Reset all ring buffer pointers to the start of the recovered segments' slot range. We will - // advance flush_pos and commit_pos as we read segment data into the buffer. - // - this->shared_state_.trim_pos.set_value(recovered_lower_bound); - this->shared_state_.flush_pos.set_value(recovered_lower_bound); - this->shared_state_.commit_pos.set_value(recovered_lower_bound); - - // Read the contents of each segment file into the ring buffer. - // - MutableBuffer dst = this->context_.buffer_.get_mut(recovered_lower_bound); - for (const SegmentFile& s : segments) { - LLFS_LOG_INFO() << "reading segment file contents: " << s.file_name; - StatusOr result = s.read(dst); - BATT_REQUIRE_OK(result); - - dst += result->size(); - this->shared_state_.flush_pos.fetch_add(result->size()); - this->shared_state_.commit_pos.fetch_add(result->size()); - } - LLFS_LOG_INFO() - << "finished reading non-head log segments; attempting to re-activate head segment..."; - - // Now attempt to open the active (head) segment file. - // - StatusOr active_file = ActiveFile::open( - this->shared_state_.location, this->shared_state_.flush_pos.get_value(), dst); - - BATT_REQUIRE_OK(active_file) << batt::LogLevel::kInfo << "opening active log file failed"; - - LLFS_LOG_INFO() << "active segment created/recovered; size=" << active_file->size(); - - this->shared_state_.flush_pos.fetch_add(active_file->size()); - this->shared_state_.commit_pos.fetch_add(active_file->size()); - - // Insert the SegmentFile objects into the segments queue. - // - BATT_REQUIRE_OK(this->shared_state_.segments.push_all(std::move(segments))); - - return active_file; -} - -//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - -// -void FileLogDriver::start_tasks(ActiveFile&& active_file) -{ - // Start the flush task. - // - BATT_CHECK(!this->flush_task_); - this->flush_task_.emplace( - this->scheduler_.schedule_task(), - FlushTaskMain{this->context_.buffer_, this->shared_state_, std::move(active_file)}, - "FileLogDriver::flush_task"); - - // Start the trim task. - // - BATT_CHECK(!this->trim_task_); - this->trim_task_.emplace( - this->scheduler_.schedule_task(), - [this] { - this->trim_task_main(); - }, - "FileLogDriver::trim_task"); -} - -//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - - -FileLogDriver::FileLogDriver(LogStorageDriverContext& context, const Location& location, - const Config& config, batt::TaskScheduler& scheduler) noexcept - : context_{context} - , scheduler_{scheduler} - , shared_state_{location, config} -{ -} - -FileLogDriver::~FileLogDriver() noexcept -{ - this->close().IgnoreError(); -} - -//---- - -Status FileLogDriver::set_trim_pos(slot_offset_type trim_pos) -{ - this->shared_state_.trim_pos.set_value(trim_pos); - return OkStatus(); -} - -slot_offset_type FileLogDriver::get_trim_pos() const -{ - return this->shared_state_.trim_pos.get_value(); -} - -StatusOr FileLogDriver::await_trim_pos(slot_offset_type min_offset) -{ - return await_slot_offset(min_offset, this->shared_state_.trim_pos); -} - -//---- - -slot_offset_type FileLogDriver::get_flush_pos() const -{ - return this->shared_state_.flush_pos.get_value(); -} - -StatusOr FileLogDriver::await_flush_pos(slot_offset_type min_offset) -{ - return await_slot_offset(min_offset, this->shared_state_.flush_pos); -} - -//---- - -Status FileLogDriver::set_commit_pos(slot_offset_type commit_pos) -{ - this->shared_state_.commit_pos.set_value(commit_pos); - return OkStatus(); -} - -slot_offset_type FileLogDriver::get_commit_pos() const -{ - return this->shared_state_.commit_pos.get_value(); -} - -StatusOr FileLogDriver::await_commit_pos(slot_offset_type min_offset) -{ - return await_slot_offset(min_offset, this->shared_state_.commit_pos); -} - -//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - -// -Status FileLogDriver::close() -{ - this->halt(); - this->join(); - - return OkStatus(); -} - -//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - -// -void FileLogDriver::halt() -{ - this->shared_state_.halt(); -} - -//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - -// -void FileLogDriver::join() -{ - if (this->trim_task_) { - this->trim_task_->join(); - this->trim_task_ = None; - } - if (this->flush_task_) { - this->flush_task_->join(); - this->flush_task_ = None; - } -} - -//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - -// -void FileLogDriver::trim_task_main() -{ - const Status status = [&] { - // Trim as many segment files as we can, sleeping only when we have to. - // - for (;;) { - // Grab the next finalized segment. - // - StatusOr oldest_segment = this->shared_state_.segments.await_next(); - BATT_REQUIRE_OK(oldest_segment); - - // Wait for the trim pos to meet or exceed the slot range of this segment. - // - { - auto status = - await_slot_offset(oldest_segment->slot_range.upper_bound, this->shared_state_.trim_pos); - BATT_REQUIRE_OK(status); - } - - // Now we can safely trim the file! - // - { - auto status = oldest_segment->remove(); - BATT_REQUIRE_OK(status); - } - } - }(); - - LLFS_LOG_INFO() << "[FileLogDriver::trim_task_main] finished with status=" << status; -} - -} // namespace llfs diff --git a/src/llfs/file_log_driver.hpp b/src/llfs/file_log_driver.hpp deleted file mode 100644 index a5ce821a..00000000 --- a/src/llfs/file_log_driver.hpp +++ /dev/null @@ -1,337 +0,0 @@ -//#=##=##=#==#=#==#===#+==#+==========+==+=+=+=+=+=++=+++=+++++=-++++=-+++++++++++ -// -// Part of the LLFS Project, under Apache License v2.0. -// See https://www.apache.org/licenses/LICENSE-2.0 for license information. -// SPDX short identifier: Apache-2.0 -// -//+++++++++++-+-+--+----- --- -- - - - - - -#pragma once -#ifndef LLFS_FILE_LOG_DRIVER_HPP -#define LLFS_FILE_LOG_DRIVER_HPP - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -namespace llfs { - -class FileLogDriver; - -using FileLogDevice = BasicRingBufferLogDevice; - -// Buffered log driver that flushes log data to a series of "segment" files. -// -class FileLogDriver -{ - public: - // See - // - class Location - { - public: - static std::string_view segment_ext() - { - return ".tdblog"; - } - - static std::string_view segment_file_prefix() - { - return "segment_"; - } - - static std::string_view active_segment_file_name() - { - return "head.tdblog"; - } - - static std::string_view config_file_name() - { - return "log.tdb_config"; - } - - //==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - - - Location() = default; - - template > - explicit Location(Args&&... args) noexcept : parent_dir_(BATT_FORWARD(args)...) - { - } - - const fs::path& parent_dir() const - { - return this->parent_dir_; - } - - fs::path config_file_path() const; - - Optional slot_range_from_segment_file_name(const std::string& name) const; - - std::string segment_file_name_from_slot_range(const SlotRange& slot_range) const; - - fs::path active_segment_file_path() const; - - private: - fs::path parent_dir_; - }; - - // See - // - struct Config { - // The size at (or above) which a segment file is closed and a new one created. - // - // This parameter trades off between the cost of writing data to the log and the upper bound on - // wasted log space due to unprocessed trim operations. Trimming the log eventually results in - // the removal of segment files, but only once none of the contents of a segment file could be - // in use. Conversely, the cost of opening new segment files and closing old ones is amortized - // over all the writes contained by a single segment file; the bigger the file, the lower the - // amortized cost of managing segments. - // - std::size_t min_segment_split_size; - - // The maximum capacity of the log; i.e., the maximum allowed distance in bytes from trim offset - // to commit offset. - // - std::size_t max_size; - - //==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - - }; - - // See - // - class ActiveFile; - - // See - // - struct SegmentFile { - // Permanently delete the file from durable storage. - // - Status remove(); - - // Read the contents of this file into memory. On success, returns the prefix of `buffer` that - // was filled with data from the segment file. - // - StatusOr read(MutableBuffer buffer) const; - - SlotRange slot_range; - std::string file_name; - }; - - //==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - - - static Config default_config(const PageCacheOptions& opts); - - static StatusOr> initialize(const Location& location, - const Config& config, - batt::TaskScheduler& scheduler, - ConfirmThisWillEraseAllMyData confirm); - - // Recover a closed or crashed log by loading whatever is possible into memory, creating a - // LogDevice::Reader to access it, and executing `scan_fn` to validate the data. `scan_fn` should - // consume as much as possible; any data left unconsumed when it returns will be truncated before - // returning the final device. If the scan_fn returns a non-ok status, that is returned in place - // of the recovered device. - // - static StatusOr> recover(const Location& location, - const LogScanFn& scan_fn, - batt::TaskScheduler& scheduler); - - //==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - - - explicit FileLogDriver(LogStorageDriverContext& context, const Location& location, - const Config& config, batt::TaskScheduler& scheduler) noexcept; - - ~FileLogDriver() noexcept; - - FileLogDriver(const FileLogDriver&) = delete; - FileLogDriver& operator=(const FileLogDriver&) = delete; - - //---- - - Status set_trim_pos(slot_offset_type trim_pos); - - slot_offset_type get_trim_pos() const; - - StatusOr await_trim_pos(slot_offset_type min_offset); - - //---- - - slot_offset_type get_flush_pos() const; - - StatusOr await_flush_pos(slot_offset_type min_offset); - - //---- - - Status set_commit_pos(slot_offset_type commit_pos); - - slot_offset_type get_commit_pos() const; - - StatusOr await_commit_pos(slot_offset_type min_offset); - - //---- - - Status close(); - - void halt(); - - void join(); - - private: - // See - // - struct ConcurrentSharedState { - explicit ConcurrentSharedState(const Location& location_arg, const Config& config_arg) noexcept - : location{location_arg} - , config{config_arg} - { - } - - // The location of the log files. - // - const Location location; - - // The configuration of this driver. - // - const Config config; - - // The filenames of all known non-active (sealed) log segments, indexed by slot range. - // - batt::Queue segments; - - // Log offset pointers. - // - batt::Watch trim_pos; - batt::Watch flush_pos; - batt::Watch commit_pos; - - //==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - - - // Prepare all objects for shutdown. This function MUST NOT block. - // - void halt(); - }; - - // See - // - class FlushTaskMain; - - //==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - - - // Scans the log directory for segment files, loading them into the ring buffer. - // - StatusOr recover_segments(); - - void start_tasks(ActiveFile&& active_file); - - void trim_task_main(); - - //==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - - - // The ring buffer state. - // - LogStorageDriverContext& context_; - - // Scheduler used to launch the trim and flush tasks. - // - batt::TaskScheduler& scheduler_; - - // Thread-safe shared state used to communicate with committers and trimmers. - // - ConcurrentSharedState shared_state_; - - // Deletes old segment files when the trim pos is increased. - // - Optional trim_task_; - - // Writes committed data to the active segment file, closing when `this->segment_size_` is - // reached. - // - Optional flush_task_; -}; - -//=#=#==#==#===============+=+=+=+=++=++++++++++++++-++-+--+-+----+--------------- - -class RecoverFileLogDeviceFactory : public LogDeviceFactory -{ - public: - explicit RecoverFileLogDeviceFactory(const FileLogDriver::Location& location, - batt::TaskScheduler& scheduler) noexcept - : location_{location} - , scheduler_{scheduler} - - { - } - - StatusOr> open_log_device(const LogScanFn& scan_fn) override - { - return FileLogDriver::recover(this->location_, scan_fn, this->scheduler_); - } - - private: - FileLogDriver::Location location_; - batt::TaskScheduler& scheduler_; -}; - -//=#=#==#==#===============+=+=+=+=++=++++++++++++++-++-+--+-+----+--------------- - -class InitializeFileLogDeviceFactory : public LogDeviceFactory -{ - public: - explicit InitializeFileLogDeviceFactory(const FileLogDriver::Location& location, - const FileLogDriver::Config& config, - batt::TaskScheduler& scheduler, - ConfirmThisWillEraseAllMyData confirm) noexcept - : location_{location} - , config_{config} - , scheduler_{scheduler} - , confirm_{confirm} - { - } - - StatusOr> open_log_device(const LogScanFn& scan_fn) override - { - auto device = - FileLogDriver::initialize(this->location_, this->config_, this->scheduler_, this->confirm_); - BATT_REQUIRE_OK(device); - - std::unique_ptr reader = - (*device)->new_reader(/*slot_lower_bound=*/None, LogReadMode::kDurable); - - BATT_CHECK_EQ(reader->data().size(), 0u) << "just initialized FileLogDevice; should be empty!"; - - StatusOr scan_status = scan_fn(*reader); - BATT_REQUIRE_OK(scan_status); - - return device; - } - - private: - FileLogDriver::Location location_; - FileLogDriver::Config config_; - batt::TaskScheduler& scheduler_; - ConfirmThisWillEraseAllMyData confirm_; -}; - -} // namespace llfs - -#include -#include - -#endif // LLFS_FILE_LOG_DRIVER_HPP diff --git a/src/llfs/file_log_driver.test.cpp b/src/llfs/file_log_driver.test.cpp deleted file mode 100644 index 5501483f..00000000 --- a/src/llfs/file_log_driver.test.cpp +++ /dev/null @@ -1,49 +0,0 @@ -//#=##=##=#==#=#==#===#+==#+==========+==+=+=+=+=+=++=+++=+++++=-++++=-+++++++++++ -// -// Part of the LLFS Project, under Apache License v2.0. -// See https://www.apache.org/licenses/LICENSE-2.0 for license information. -// SPDX short identifier: Apache-2.0 -// -//+++++++++++-+-+--+----- --- -- - - - - - -#include -// -#include - -#include -#include - -namespace { - -TEST(FileLogDriverTest, SegmentFilenameParser) -{ - using llfs::FileLogDriver; - using llfs::None; - using llfs::SlotRange; - - FileLogDriver::Location loc{"not_used_"}; - - EXPECT_EQ(*loc.slot_range_from_segment_file_name("not_used_0000.0001.tdblog"), - (SlotRange{0x0, 0x1})); - EXPECT_EQ(*loc.slot_range_from_segment_file_name("not_used_0001.0001.tdblog"), - (SlotRange{0x1, 0x2})); - EXPECT_EQ(*loc.slot_range_from_segment_file_name("not_used_0002.1.tdblog"), - (SlotRange{0x2, 0x3})); - EXPECT_EQ(*loc.slot_range_from_segment_file_name("not_used_000a.1.tdblog"), - (SlotRange{0xa, 0xb})); - EXPECT_EQ(*loc.slot_range_from_segment_file_name("not_used_5b7e.10.tdblog"), - (SlotRange{0x5b7e, 0x5b8e})); - EXPECT_EQ(loc.slot_range_from_segment_file_name("not_used_5b7e.a1.tdblo"), None); - EXPECT_EQ(loc.slot_range_from_segment_file_name("not_used_5b7e..tdblog"), None); - EXPECT_EQ(loc.slot_range_from_segment_file_name("not_used_.7.tdblog"), None); - EXPECT_EQ(loc.slot_range_from_segment_file_name("4.tdblog"), None); - EXPECT_EQ(loc.slot_range_from_segment_file_name(".4.tdblog"), None); - EXPECT_EQ(*loc.slot_range_from_segment_file_name("3.4.tdblog"), (SlotRange{3, 7})); - EXPECT_EQ(loc.slot_range_from_segment_file_name(".tdblog"), None); - EXPECT_EQ(*loc.slot_range_from_segment_file_name("not_used_000a.0.tdblognot_used_000b.0.tdblog"), - (SlotRange{0xb, 0xb})); - EXPECT_EQ(*loc.slot_range_from_segment_file_name("not_used_000b.0.tdblognot_used_000a.0.tdblog"), - (SlotRange{0xa, 0xa})); -} - -} // namespace diff --git a/src/llfs/file_log_driver/active_file.cpp b/src/llfs/file_log_driver/active_file.cpp deleted file mode 100644 index 3c4e5e88..00000000 --- a/src/llfs/file_log_driver/active_file.cpp +++ /dev/null @@ -1,176 +0,0 @@ -//#=##=##=#==#=#==#===#+==#+==========+==+=+=+=+=+=++=+++=+++++=-++++=-+++++++++++ -// -// Part of the LLFS Project, under Apache License v2.0. -// See https://www.apache.org/licenses/LICENSE-2.0 for license information. -// SPDX short identifier: Apache-2.0 -// -//+++++++++++-+-+--+----- --- -- - - - - - -#include -// - -#include - -#include - -namespace llfs { - -StatusOr FileLogDriver::ActiveFile::open(const Location& location, - slot_offset_type base_offset, - MutableBuffer buffer) noexcept -{ - fs::path file_path = location.active_segment_file_path(); - - if (!fs::exists(file_path)) { - StatusOr fd = create_active_file(location); - BATT_REQUIRE_OK(fd); - - return ActiveFile{location, *fd, base_offset, /*size=*/0}; - } - - StatusOr contents = read_file(file_path.string(), buffer); - BATT_REQUIRE_OK(contents) << batt::LogLevel::kError << "Failed to read contents of " << file_path; - - // Try to open the existing file. - // - StatusOr fd = open_file_read_write(file_path.string()); - BATT_REQUIRE_OK(fd) << batt::LogLevel::kError << "Failed to open existing file " << file_path; - - return ActiveFile{location, *fd, base_offset, contents->size()}; -} - -StatusOr FileLogDriver::ActiveFile::create_active_file(const Location& location) -{ - return create_file_read_write(location.active_segment_file_path().string()); -} - -FileLogDriver::ActiveFile::ActiveFile(const Location& location, int fd, - slot_offset_type base_offset, std::size_t size) noexcept - : location_{location} - , fd_{fd} - , slot_range_{ - .lower_bound = base_offset, - .upper_bound = base_offset + size, - } -{ -} - -FileLogDriver::ActiveFile::ActiveFile(ActiveFile&& that) noexcept - : location_{that.location_} - , fd_{that.fd_} - , slot_range_{that.slot_range_} -{ - that.release(); -} - -FileLogDriver::ActiveFile& FileLogDriver::ActiveFile::operator=(ActiveFile&& that) noexcept -{ - if (this != &that) { - this->close(); - this->location_ = std::move(that.location_); - this->fd_ = that.fd_; - this->slot_range_ = that.slot_range_; - that.release(); - } - return *this; -} - -FileLogDriver::ActiveFile::~ActiveFile() noexcept -{ - this->close(); -} - -// Release ownership of the underlying file descriptor. -// -void FileLogDriver::ActiveFile::release() -{ - this->fd_ = -1; - this->slot_range_ = SlotRange{0, 0}; -} - -void FileLogDriver::ActiveFile::close() noexcept -{ - if (this->fd_ != -1) { - batt::syscall_retry([&] { - return ::close(this->fd_); - }); - this->fd_ = -1; - } -} - -Status FileLogDriver::ActiveFile::truncate(u64 bytes_to_drop_from_end) -{ - BATT_CHECK_NE(this->fd_, -1); - - const off_t current_size = batt::syscall_retry([&] { - return ::lseek(this->fd_, /*offset=*/0, /*whence=*/SEEK_END); - }); - BATT_CHECK_LE(bytes_to_drop_from_end, static_cast(current_size)); - - const off_t target_size = current_size - bytes_to_drop_from_end; - - const int retval = batt::syscall_retry([&] { - return ::ftruncate(this->fd_, /*length=*/target_size); - }); - BATT_REQUIRE_OK(status_from_retval(retval)); - - this->slot_range_.upper_bound = this->slot_range_.lower_bound + target_size; - - return OkStatus(); -} - -Status FileLogDriver::ActiveFile::append(ConstBuffer buffer) -{ - while (buffer.size() > 0) { - const auto bytes_written = batt::syscall_retry([&] { - return ::pwrite(this->fd_, buffer.data(), buffer.size(), /*offset=*/this->slot_range_.size()); - }); - BATT_REQUIRE_OK(status_from_retval(bytes_written)); - - buffer += bytes_written; - this->slot_range_.upper_bound += bytes_written; - } - - return status_from_retval(batt::syscall_retry([&] { - return fsync(this->fd_); - })); -} - -StatusOr FileLogDriver::ActiveFile::split() -{ - BATT_CHECK_NE(this->fd_, -1) << "split may only be called on an open ActiveFile!"; - - this->close(); - - // Create a SegmentFile object to return. - // - SegmentFile finished_segment{ - .slot_range = this->slot_range_, - .file_name = this->location_.segment_file_name_from_slot_range(this->slot_range_), - }; - - // Set the name of the now finalized active segment file to reflect its slot range. - // - int retval = batt::syscall_retry([&] { - return ::rename(/*from=*/this->location_.active_segment_file_path().c_str(), - /*to=*/finished_segment.file_name.c_str()); - }); - BATT_REQUIRE_OK(status_from_retval(retval)); - - // Advance the active slot range. - // - this->slot_range_.lower_bound = this->slot_range_.upper_bound; - - // Create a new active file for writing. - // - StatusOr new_fd = create_active_file(this->location_); - BATT_REQUIRE_OK(new_fd); - - this->fd_ = *new_fd; - - // Success! Return the last segment file. - // - return finished_segment; -} - -} // namespace llfs diff --git a/src/llfs/file_log_driver/active_file.hpp b/src/llfs/file_log_driver/active_file.hpp deleted file mode 100644 index 4911e97c..00000000 --- a/src/llfs/file_log_driver/active_file.hpp +++ /dev/null @@ -1,108 +0,0 @@ -//#=##=##=#==#=#==#===#+==#+==========+==+=+=+=+=+=++=+++=+++++=-++++=-+++++++++++ -// -// Part of the LLFS Project, under Apache License v2.0. -// See https://www.apache.org/licenses/LICENSE-2.0 for license information. -// SPDX short identifier: Apache-2.0 -// -//+++++++++++-+-+--+----- --- -- - - - - - -#pragma once -#ifndef LLFS_FILE_LOG_DRIVER_ACTIVE_FILE_HPP -#define LLFS_FILE_LOG_DRIVER_ACTIVE_FILE_HPP - -namespace llfs { - -class FileLogDriver::ActiveFile -{ - public: - // Open an existing active segment file or create a new one. Any existing data will be read into - // `buffer` if an active segment file is found. - // - static StatusOr open(const Location& location, slot_offset_type base_offset, - MutableBuffer buffer) noexcept; - - ActiveFile() = default; - - ActiveFile(const ActiveFile&) = delete; - ActiveFile& operator=(const ActiveFile&) = delete; - - ActiveFile(ActiveFile&&) noexcept; - ActiveFile& operator=(ActiveFile&&) noexcept; - - // Closes any open file descriptors owned by this object. - // - ~ActiveFile() noexcept; - - // True iff this refers to a valid, open segment file. - // - explicit operator bool() const noexcept - { - return this->fd_ != -1; - } - - // This is done on recovery, if we detect partially written data; drop the specified number of - // bytes from the active segment file. - // - Status truncate(u64 bytes_to_drop_from_end); - - // Write and flush the given bytes to the active segment file. - // - Status append(ConstBuffer buffer); - - // Close the file; invalidates this object. - // - void close() noexcept; - - // The current size (flushed, on disk) of the file. - // - u64 size() const - { - return this->slot_range_.size(); - } - - // The log slot offset range currently covered by this file. - // - const SlotRange& slot_range() const - { - return this->slot_range_; - } - - // Finalize the contents of this file and move on to the next one. - // - StatusOr split(); - - private: - // Create a new active file and return its file descriptor. - // - static StatusOr create_active_file(const Location& location); - - //==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - - - // Create a new ActiveFile object. - // - explicit ActiveFile(const Location& location, int fd, slot_offset_type base_offset, - std::size_t size) noexcept; - - // Release ownership of the underlying file descriptor. - // - void release(); - - //==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - - - // The location of the log files. - // - Location location_; - - // The file descriptor for the active file. - // - int fd_ = -1; - - // The current SlotRange interval for the active file. The upper_bound will advance as more data - // is flushed. - // - SlotRange slot_range_{0, 0}; -}; - -} // namespace llfs - -#endif // LLFS_FILE_LOG_DRIVER_ACTIVE_FILE_HPP diff --git a/src/llfs/file_log_driver/concurrent_shared_state.cpp b/src/llfs/file_log_driver/concurrent_shared_state.cpp deleted file mode 100644 index 1cd417f3..00000000 --- a/src/llfs/file_log_driver/concurrent_shared_state.cpp +++ /dev/null @@ -1,21 +0,0 @@ -//#=##=##=#==#=#==#===#+==#+==========+==+=+=+=+=+=++=+++=+++++=-++++=-+++++++++++ -// -// Part of the LLFS Project, under Apache License v2.0. -// See https://www.apache.org/licenses/LICENSE-2.0 for license information. -// SPDX short identifier: Apache-2.0 -// -//+++++++++++-+-+--+----- --- -- - - - - - -#include - -namespace llfs { - -void FileLogDriver::ConcurrentSharedState::halt() -{ - this->trim_pos.close(); - this->flush_pos.close(); - this->commit_pos.close(); - this->segments.close(); -} - -} // namespace llfs diff --git a/src/llfs/file_log_driver/config.cpp b/src/llfs/file_log_driver/config.cpp deleted file mode 100644 index 3dc755dc..00000000 --- a/src/llfs/file_log_driver/config.cpp +++ /dev/null @@ -1,13 +0,0 @@ -//#=##=##=#==#=#==#===#+==#+==========+==+=+=+=+=+=++=+++=+++++=-++++=-+++++++++++ -// -// Part of the LLFS Project, under Apache License v2.0. -// See https://www.apache.org/licenses/LICENSE-2.0 for license information. -// SPDX short identifier: Apache-2.0 -// -//+++++++++++-+-+--+----- --- -- - - - - - -#include - -namespace llfs { - -} // namespace llfs diff --git a/src/llfs/file_log_driver/flush_task_main.cpp b/src/llfs/file_log_driver/flush_task_main.cpp deleted file mode 100644 index cf49da46..00000000 --- a/src/llfs/file_log_driver/flush_task_main.cpp +++ /dev/null @@ -1,68 +0,0 @@ -//#=##=##=#==#=#==#===#+==#+==========+==+=+=+=+=+=++=+++=+++++=-++++=-+++++++++++ -// -// Part of the LLFS Project, under Apache License v2.0. -// See https://www.apache.org/licenses/LICENSE-2.0 for license information. -// SPDX short identifier: Apache-2.0 -// -//+++++++++++-+-+--+----- --- -- - - - - - -#include - -#include - -namespace llfs { - -FileLogDriver::FlushTaskMain::FlushTaskMain(const RingBuffer& buffer, - ConcurrentSharedState& shared_state, - ActiveFile&& active_file) noexcept - : buffer_{buffer} - , shared_state_{shared_state} - , active_file_{std::move(active_file)} -{ -} - -void FileLogDriver::FlushTaskMain::operator()() -{ - const Status status = [&] { - // Do as much work as we can, grabbing the latest flush/commit position values so that we don't - // sleep unless we absolutely have to. - // - auto local_commit_pos = this->shared_state_.commit_pos.get_value(); - auto local_flush_pos = this->shared_state_.flush_pos.get_value(); - for (;;) { - ConstBuffer bytes_to_flush{this->buffer_.get(local_flush_pos).data(), - slot_clamp_distance(local_flush_pos, local_commit_pos)}; - - if (bytes_to_flush.size() == 0) { - // We've caught up! Put this task to sleep awaiting more data to flush. - // - StatusOr updated_commit_pos = - await_slot_offset(local_flush_pos + 1, this->shared_state_.commit_pos); - BATT_REQUIRE_OK(updated_commit_pos); - local_commit_pos = *updated_commit_pos; - continue; - } - - auto append_status = this->active_file_.append(bytes_to_flush); - BATT_REQUIRE_OK(append_status); - - // This task is the only updater of `flush_pos`, so just update our local value and push it - // out to the Watch. - // - local_flush_pos += bytes_to_flush.size(); - this->shared_state_.flush_pos.set_value(local_flush_pos); - - // Check to see if the active file is big enough to be split. - // - if (this->active_file_.size() >= this->shared_state_.config.min_segment_split_size) { - StatusOr next_segment = this->active_file_.split(); - BATT_REQUIRE_OK(next_segment); - BATT_REQUIRE_OK(this->shared_state_.segments.push(std::move(*next_segment))); - } - } - }(); - - LLFS_LOG_INFO() << "[FileLogDriver::flush_task_main] finished with status=" << status; -} - -} // namespace llfs diff --git a/src/llfs/file_log_driver/flush_task_main.hpp b/src/llfs/file_log_driver/flush_task_main.hpp deleted file mode 100644 index 0582e8a5..00000000 --- a/src/llfs/file_log_driver/flush_task_main.hpp +++ /dev/null @@ -1,43 +0,0 @@ -//#=##=##=#==#=#==#===#+==#+==========+==+=+=+=+=+=++=+++=+++++=-++++=-+++++++++++ -// -// Part of the LLFS Project, under Apache License v2.0. -// See https://www.apache.org/licenses/LICENSE-2.0 for license information. -// SPDX short identifier: Apache-2.0 -// -//+++++++++++-+-+--+----- --- -- - - - - - -#pragma once -#ifndef LLFS_FILE_LOG_DRIVER_FLUSH_TASK_MAIN_HPP -#define LLFS_FILE_LOG_DRIVER_FLUSH_TASK_MAIN_HPP - -namespace llfs { - -// Stateful task function that loops flushing committed data to segment files. -// -class FileLogDriver::FlushTaskMain -{ - public: - explicit FlushTaskMain(const RingBuffer& buffer, ConcurrentSharedState& shared_state, - ActiveFile&& active_file) noexcept; - - // The flush task main loop entry point. - // - void operator()(); - - private: //==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - - // Read-only access to the ring buffer contents. - // - const RingBuffer& buffer_; - - // Thread-safe shared state used to communicate with committers and trimmers. - // - ConcurrentSharedState& shared_state_; - - // The current active segment file. - // - ActiveFile active_file_; -}; - -} // namespace llfs - -#endif // LLFS_FILE_LOG_DRIVER_FLUSH_TASK_MAIN_HPP diff --git a/src/llfs/file_log_driver/location.cpp b/src/llfs/file_log_driver/location.cpp deleted file mode 100644 index 792db7e7..00000000 --- a/src/llfs/file_log_driver/location.cpp +++ /dev/null @@ -1,103 +0,0 @@ -//#=##=##=#==#=#==#===#+==#+==========+==+=+=+=+=+=++=+++=+++++=-++++=-+++++++++++ -// -// Part of the LLFS Project, under Apache License v2.0. -// See https://www.apache.org/licenses/LICENSE-2.0 for license information. -// SPDX short identifier: Apache-2.0 -// -//+++++++++++-+-+--+----- --- -- - - - - - -#include -// - -#include - -#include - -#include -#include - -namespace llfs { - -//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - -// -fs::path FileLogDriver::Location::config_file_path() const -{ - return this->parent_dir_ / std::string(Location::config_file_name()); -} - -//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - -// -fs::path FileLogDriver::Location::active_segment_file_path() const -{ - return this->parent_dir_ / std::string(Location::active_segment_file_name()); -} - -//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - -// -std::string FileLogDriver::Location::segment_file_name_from_slot_range( - const SlotRange& slot_range) const -{ - std::ostringstream oss; - oss << (this->parent_dir_ / Location::segment_file_prefix()).string(); - oss << std::hex << std::setw(10) << std::setfill('0') << slot_range.lower_bound << "." << std::hex - << std::setw(1) << slot_range.size() << Location::segment_ext(); - return std::move(oss).str(); -} - -//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - -// -Optional FileLogDriver::Location::slot_range_from_segment_file_name( - const std::string& name) const -{ - if (!boost::algorithm::ends_with(name, Location::segment_ext()) || - name.length() < Location::segment_ext().length() + 1) { - return None; - } - - const auto offset_end = name.length() - Location::segment_ext().length(); - const auto segment_end = name.rfind('.', offset_end - 1); - if (segment_end == std::string::npos) { - return None; - } - auto segment_begin = segment_end; - while (segment_begin > 0) { - --segment_begin; - if (!std::isxdigit(name[segment_begin])) { - ++segment_begin; - break; - } - } - while (name[segment_begin] == '0' && segment_begin + 1 < segment_end) { - ++segment_begin; - } - const auto offset_begin = segment_end + 1; - - if (offset_begin >= offset_end) { - return None; - } - - slot_offset_type range_begin = 0; - { - std::from_chars_result result = std::from_chars( - name.c_str() + segment_begin, name.c_str() + segment_end, range_begin, /*base=*/16); - if (result.ec != std::errc()) { - return None; - } - } - - slot_offset_type range_size = 0; - { - std::from_chars_result result = std::from_chars( - name.c_str() + offset_begin, name.c_str() + offset_end, range_size, /*base=*/16); - if (result.ec != std::errc()) { - return None; - } - } - - return SlotRange{ - .lower_bound = range_begin, - .upper_bound = range_begin + range_size, - }; -} - -} // namespace llfs diff --git a/src/llfs/file_log_driver/segment_file.cpp b/src/llfs/file_log_driver/segment_file.cpp deleted file mode 100644 index c584c44b..00000000 --- a/src/llfs/file_log_driver/segment_file.cpp +++ /dev/null @@ -1,29 +0,0 @@ -//#=##=##=#==#=#==#===#+==#+==========+==+=+=+=+=+=++=+++=+++++=-++++=-+++++++++++ -// -// Part of the LLFS Project, under Apache License v2.0. -// See https://www.apache.org/licenses/LICENSE-2.0 for license information. -// SPDX short identifier: Apache-2.0 -// -//+++++++++++-+-+--+----- --- -- - - - - - -#include -// - -#include - -#include - -namespace llfs { - -Status FileLogDriver::SegmentFile::remove() -{ - LLFS_VLOG(1) << "trimming log segment file: " << this->file_name; - return delete_file(this->file_name); -} - -StatusOr FileLogDriver::SegmentFile::read(MutableBuffer buffer) const -{ - return read_file(this->file_name, buffer); -} - -} // namespace llfs diff --git a/src/llfs/filesystem_page_device.cpp b/src/llfs/filesystem_page_device.cpp deleted file mode 100644 index 408e0e45..00000000 --- a/src/llfs/filesystem_page_device.cpp +++ /dev/null @@ -1,185 +0,0 @@ -//#=##=##=#==#=#==#===#+==#+==========+==+=+=+=+=+=++=+++=+++++=-++++=-+++++++++++ -// -// Part of the LLFS Project, under Apache License v2.0. -// See https://www.apache.org/licenses/LICENSE-2.0 for license information. -// SPDX short identifier: Apache-2.0 -// -//+++++++++++-+-+--+----- --- -- - - - - - -#include -// - -#include -#include - -namespace llfs { - -std::string FilesystemPageDevice::filename_from_id(PageId id) -{ - std::ostringstream oss; - oss << std::hex << std::setw(16) << std::setfill('0') << id.int_value(); - return std::move(oss).str(); -} - -std::unique_ptr FilesystemPageDevice::erase( - const fs::path& parent_dir, page_device_id_int device_id, PageCount capacity, - PageSize page_size, ConfirmThisWillEraseAllMyData confirm) -{ - if (confirm == ConfirmThisWillEraseAllMyData::kNo) { - return nullptr; - } - - // Remove the contents of the parent directory. - // - for (auto& p : fs::directory_iterator(parent_dir)) { - LLFS_LOG_INFO() << "Removing: " << p.path(); - std::error_code ec; - fs::remove_all(p.path(), ec); - if (ec) { - LLFS_LOG_ERROR() << " Failed to remove " << p.path() << ": " << ec; - return nullptr; - } - } - - // Write the config file. - // - { - std::ofstream ofs(parent_dir / ".llfs"); - ofs << capacity << " " << device_id << " " << page_size; - if (ofs.bad()) { - return nullptr; - } - } - - return std::unique_ptr( - new FilesystemPageDevice(page_size, batt::make_copy(parent_dir), device_id, capacity)); -} - -std::unique_ptr FilesystemPageDevice::open(const fs::path& parent_dir) -{ - page_id_int capacity; - page_device_id_int device_id; - u32 page_size; - - LLFS_LOG_INFO() << "opening FilesystemPageDevice at " << parent_dir; - { - std::ifstream ifs(parent_dir / ".llfs"); - ifs >> capacity >> device_id >> page_size; - if (ifs.bad()) { - LLFS_PLOG_ERROR() << "page device config could not be read: " << (parent_dir / ".llfs"); - return nullptr; - } - } - return std::unique_ptr(new FilesystemPageDevice( - PageSize{page_size}, batt::make_copy(parent_dir), device_id, PageCount{capacity})); -} - -//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - -// -StatusOr> FilesystemPageDevice::prepare(PageId page_id) -{ - metrics().prepare_count++; - - return PageBuffer::allocate(this->page_size_, page_id); -} - -//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - -// -void FilesystemPageDevice::write(std::shared_ptr&& page_buffer, - WriteHandler&& handler) -{ - auto result = [&]() -> Status { - metrics().commit_count++; - - BATT_CHECK_EQ(page_buffer->size(), this->page_size_); - BATT_CHECK_EQ(get_page_size(page_buffer), this->page_size_); - - std::unique_lock lock{this->mutex_}; - - auto iter = pre_dropped_.find(page_buffer->page_id()); - if (iter != pre_dropped_.end()) { - metrics().commit_drop_count++; - pre_dropped_.erase(iter); - return OkStatus(); - } - - std::ofstream ofs(page_file_from_id(page_buffer->page_id()), - std::ios_base::out | std::ios_base::binary | std::ios_base::trunc); - - const ConstBuffer buf = get_const_buffer(page_buffer); - if (!ofs.write((const char*)buf.data(), buf.size()).good()) { - return make_status(StatusCode::kFilesystemPageWriteFailed); - } - - auto add_to_live_set = batt::finally([&] { - const bool was_inserted = this->live_.emplace(page_buffer->page_id()).second; - BATT_CHECK(was_inserted); - }); - - return OkStatus(); - }(); - - handler(result); -} - -//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - -// -void FilesystemPageDevice::read(PageId id, ReadHandler&& handler) -{ - auto result = [&]() -> StatusOr> { - metrics().read_count++; - - // TODO [tastolfi 2020-12-07] Pool these? - // - std::shared_ptr page = PageBuffer::allocate(this->page_size_, id); - - BATT_CHECK_NOT_NULLPTR(page); - BATT_CHECK_EQ(page->size(), this->page_size_); - BATT_CHECK_EQ(get_page_size(page), this->page_size_); - - std::ifstream ifs(page_file_from_id(id)); - if (!ifs.good()) { - LLFS_PLOG_ERROR() << "read of page: " << page_file_from_id(id) << " failed"; - return make_status(StatusCode::kFilesystemPageOpenFailed); - } - - if (!ifs.read(reinterpret_cast(page.get()), page->size()).good()) { - return make_status(StatusCode::kFilesystemPageReadFailed); - } - - return {std::move(page)}; - }(); - - handler(result); -} - -//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - -// -void FilesystemPageDevice::drop(PageId id, WriteHandler&& handler) -{ - auto result = [&]() -> Status { - metrics().drop_count++; - - std::unique_lock lock{this->mutex_}; - - std::error_code ec; - fs::path page_file = page_file_from_id(id); - - if (!fs::remove(page_file, ec)) { - LLFS_DLOG_WARNING() << "failed to delete file: " << page_file - << " (is_live=" << this->live_.count(id) << ")"; - metrics().drop_false_count++; - pre_dropped_.emplace(id); - } else if (ec) { - metrics().drop_error_count++; - LLFS_DLOG_WARNING() << "drop page error: value=" << ec.value() << " message='" << ec.message() - << "'"; - return make_status(StatusCode::kFilesystemRemoveFailed); - } - return OkStatus(); - }(); - - handler(result); -} - -} // namespace llfs diff --git a/src/llfs/filesystem_page_device.hpp b/src/llfs/filesystem_page_device.hpp deleted file mode 100644 index 2904a654..00000000 --- a/src/llfs/filesystem_page_device.hpp +++ /dev/null @@ -1,126 +0,0 @@ -//#=##=##=#==#=#==#===#+==#+==========+==+=+=+=+=+=++=+++=+++++=-++++=-+++++++++++ -// -// Part of the LLFS Project, under Apache License v2.0. -// See https://www.apache.org/licenses/LICENSE-2.0 for license information. -// SPDX short identifier: Apache-2.0 -// -//+++++++++++-+-+--+----- --- -- - - - - - -#pragma once -#ifndef LLFS_FILESYSTEM_PAGE_DEVICE_HPP -#define LLFS_FILESYSTEM_PAGE_DEVICE_HPP - -#include -#include -#include -#include - -#include - -#include - -#include -#include -#include -#include -#include - -namespace llfs { - -namespace fs = std::filesystem; - -// Reference implementation of PageDevice. This is slow; not to be used in -// production... for testing/development only! -// -class FilesystemPageDevice : public PageDevice -{ - public: - struct Metrics { - CountMetric prepare_count{0}; - CountMetric commit_count{0}; - CountMetric commit_drop_count{0}; - CountMetric read_count{0}; - CountMetric drop_count{0}; - CountMetric drop_false_count{0}; - CountMetric drop_error_count{0}; - }; - - static Metrics& metrics() - { - static Metrics m_; - return m_; - } - - static std::string filename_from_id(PageId id); - - static std::unique_ptr open(const fs::path& parent_dir); - - static std::unique_ptr erase(const fs::path& parent_dir, - page_device_id_int device_id, - PageCount capacity, PageSize page_size, - ConfirmThisWillEraseAllMyData confirm); - - PageSize page_size() override - { - return this->page_size_; - } - - PageIdFactory page_ids() override - { - return PageIdFactory{this->capacity_, this->device_id_}; - } - - //==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - - // - StatusOr> prepare(PageId id) override; - - //==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - - // - void write(std::shared_ptr&& page_buffer, WriteHandler&& handler) override; - - //==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - - // - void read(PageId id, ReadHandler&& handler) override; - - //==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - - // - void drop(PageId id, WriteHandler&& handler) override; - - private: - explicit FilesystemPageDevice(PageSize page_size, fs::path&& parent_dir, - page_device_id_int device_id, PageCount capacity) noexcept - : page_size_{page_size} - , parent_dir_(std::move(parent_dir)) - , device_id_{device_id} - , capacity_{capacity} - { - } - - fs::path page_file_from_id(PageId id) const - { - return parent_dir_ / filename_from_id(id); - } - - const PageSize page_size_; - const fs::path parent_dir_; - const page_device_id_int device_id_; - const PageCount capacity_; - std::mutex mutex_; - std::unordered_set pre_dropped_; - std::unordered_set live_; -}; - -//#=##=##=#==#=#==#===#+==#+==========+==+=+=+=+=+=++=+++=+++++=-++++=-+++++++++++ - -inline std::ostream& operator<<(std::ostream& out, const FilesystemPageDevice::Metrics& t) -{ - return out << "FSPageDevice::Metrics{.prepares=" << t.prepare_count - << ", .commits(total)=" << t.commit_count << ", .commits(drop)=" << t.commit_drop_count - << ", .reads=" << t.read_count << ", .drops(total)=" << t.drop_count - << ", .drops(false)=" << t.drop_false_count << ", .drops(error)=" << t.drop_error_count - << ",}"; -} - -} // namespace llfs - -#endif // LLFS_FILESYSTEM_PAGE_DEVICE_HPP diff --git a/src/llfs/filesystem_page_device.test.cpp b/src/llfs/filesystem_page_device.test.cpp deleted file mode 100644 index 27ec2e45..00000000 --- a/src/llfs/filesystem_page_device.test.cpp +++ /dev/null @@ -1,22 +0,0 @@ -//#=##=##=#==#=#==#===#+==#+==========+==+=+=+=+=+=++=+++=+++++=-++++=-+++++++++++ -// -// Part of the LLFS Project, under Apache License v2.0. -// See https://www.apache.org/licenses/LICENSE-2.0 for license information. -// SPDX short identifier: Apache-2.0 -// -//+++++++++++-+-+--+----- --- -- - - - - - -#include -// -#include - -#include -#include - -namespace { - -TEST(FilesystemPageDeviceTest, Basic) -{ -} - -} // namespace diff --git a/src/llfs/fuse.cpp b/src/llfs/fuse.cpp deleted file mode 100644 index e81d7548..00000000 --- a/src/llfs/fuse.cpp +++ /dev/null @@ -1,331 +0,0 @@ -//#=##=##=#==#=#==#===#+==#+==========+==+=+=+=+=+=++=+++=+++++=-++++=-+++++++++++ -// -// Part of the LLFS Project, under Apache License v2.0. -// See https://www.apache.org/licenses/LICENSE-2.0 for license information. -// SPDX short identifier: Apache-2.0 -// -//+++++++++++-+-+--+----- --- -- - - - - - -#include - -namespace llfs { - -//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - -// -/*static*/ auto FuseImplBase::const_buffer_vec_from_bufv(fuse_bufvec& bufv, - std::shared_ptr* storage) - -> batt::StatusOr -{ - if (bufv.idx > bufv.count) { - return {batt::status_from_errno(EINVAL)}; - } - - ConstBufferVec vec; - - { - // First pass is to calculate total storage space to allocate, if any. - // - bool storage_needed = false; - usize total_size = 0; - for (usize i = bufv.idx; i < bufv.count; ++i) { - const fuse_buf& buf = bufv.buf[i]; - total_size += buf.size; - if (buf.flags & FUSE_BUF_IS_FD) { - storage_needed = true; - } - } - - // Allocate some temporary memory if we will be reading from fds. - // - if (storage_needed) { - BATT_CHECK_NOT_NULLPTR(storage); - storage->reset(new char[total_size]); - - fuse_bufvec tmp{ - .count = 1, - .idx = 0, - .off = 0, - .buf = {{ - .size = total_size, - .flags = (fuse_buf_flags)0, - .mem = storage->get(), - .fd = 0, - .pos = 0, - }}, - }; - - const isize n_copied = fuse_buf_copy(&tmp, &bufv, /*flags=*/(fuse_buf_copy_flags)0); - if (n_copied < 0) { - return {batt::status_from_errno(-n_copied)}; - } - BATT_CHECK_EQ(n_copied, total_size); - - return const_buffer_vec_from_bufv(tmp, nullptr); - } - } - - // No copy/storage needed; build `vec`. - // - usize offset = bufv.off; - for (usize i = bufv.idx; i < bufv.count; ++i, offset = 0) { - const fuse_buf& buf = bufv.buf[i]; - - if (buf.flags & (FUSE_BUF_IS_FD | FUSE_BUF_FD_SEEK | FUSE_BUF_FD_RETRY)) { - LLFS_VLOG(1) << "buf=" << DumpFuseBufInfo{buf} << BATT_INSPECT(bufv.count) << BATT_INSPECT(i); - return {batt::Status{batt::StatusCode::kUnimplemented}}; - } - - BATT_CHECK_LE(offset, buf.size); - vec.emplace_back(batt::ConstBuffer{buf.mem, buf.size} + offset); - } - - return {std::move(vec)}; -} - -//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - -// -std::ostream& operator<<(std::ostream& out, const DumpFuseBufInfo& t) -{ - return out << "fuse_buf{.size=" << t.buf.size // - << ", .flags=" << DumpFuseBufFlags{t.buf.flags} // - << ", .mem=" << t.buf.mem // - << ", .fd=" << t.buf.fd // - << ", .pos=" << t.buf.pos // - << ",}"; -} - -//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - -// -std::ostream& operator<<(std::ostream& out, const DumpFuseBufFlags& t) -{ - out << (int)t.flags << "{"; - if (t.flags & FUSE_BUF_IS_FD) { - out << "is_fd,"; - } - if (t.flags & FUSE_BUF_FD_SEEK) { - out << "fd_seek,"; - } - if (t.flags & FUSE_BUF_FD_RETRY) { - out << "fd_retry,"; - } - return out << "}"; -} - -//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - -// -/*static*/ int FuseImplBase::errno_from_status(batt::Status status) -{ - if (status.ok()) { - return 0; - } - - static const batt::Status::CodeGroup* errno_group = &(batt::status_from_errno(EIO).group()); - - if (&(status.group()) == errno_group) { - int e = status.code_index_within_group(); - LLFS_VLOG(2) << "(status => errno) " << e << " " << std::strerror(e); - return e; - } - - if (status == batt::StatusCode::kUnimplemented) { - return ENOTSUP; - } - - return EINVAL; -} - -//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - -// -int FuseImplBase::invoke_fuse_reply_buf(fuse_req_t req, const batt::ConstBuffer& cb) -{ - return fuse_reply_buf(req, static_cast(cb.data()), cb.size()); -} - -//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - -// -int FuseImplBase::invoke_fuse_reply_iov(fuse_req_t req, const batt::Slice& cbs) -{ - if (FuseImplBase::can_cast_iovec_to_const_buffer()) { - return fuse_reply_iov(req, reinterpret_cast(cbs.begin()), cbs.size()); - } - - batt::SmallVec tmp; - for (const batt::ConstBuffer& cb : cbs) { - tmp.emplace_back(iovec{const_cast(cb.data()), cb.size()}); - } - return fuse_reply_iov(req, tmp.data(), tmp.size()); -} - -//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - -// -int FuseImplBase::invoke_fuse_reply_data(fuse_req_t req, const FuseConstBufferVec& v) -{ - batt::SmallVec tmp_storage; - - tmp_storage.resize(sizeof(fuse_bufvec) - sizeof(fuse_buf) + - sizeof(fuse_buf) * std::min(1, v.buffers.size())); - - std::memset(tmp_storage.data(), 0, tmp_storage.size()); - - fuse_bufvec* const fbv = reinterpret_cast(tmp_storage.data()); - - fbv->count = v.buffers.size(); - fbv->idx = v.current_buffer_index; - fbv->off = v.current_buffer_offset; - - { - usize i = 0; - for (const FuseConstBuffer& fcb : v.buffers) { - fuse_buf& fb = fbv->buf[i]; - - batt::case_of( // - fcb, // - - //----- --- -- - - - - - [&fb](const batt::ConstBuffer& cb) { - fb.size = cb.size(); - fb.mem = const_cast(cb.data()); - }, - - //----- --- -- - - - - - [&fb](const OwnedConstBuffer& ocb) { - fb.size = ocb.buffer.size(); - fb.mem = const_cast(ocb.buffer.data()); - }, - - //----- --- -- - - - - - [&fb](const FileDataRef& fdr) { - fb.size = fdr.size; - fb.flags = (fuse_buf_flags)((int)fb.flags | (int)FUSE_BUF_IS_FD); - fb.fd = fdr.fd.value(); - if (fdr.offset) { - fb.flags = (fuse_buf_flags)((int)fb.flags | (int)FUSE_BUF_FD_SEEK); - fb.pos = *fdr.offset; - } - if (fdr.should_retry) { - fb.flags = (fuse_buf_flags)((int)fb.flags | (int)FUSE_BUF_FD_RETRY); - } - }); - - ++i; - } - } - - return fuse_reply_data(req, fbv, /*flags=*/(fuse_buf_copy_flags)0); - // ^ - // TODO [tastolfi 2023-06-28] support fuse_buf_copy_flags -} - -//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - -// -std::ostream& operator<<(std::ostream& out, const fuse_file_info& t) -{ - return out << "fuse_file_info{" // - << ".flags=" << t.flags // - << ", .write_page=" << t.writepage // - << ", .direct_io=" << t.direct_io // - << ", .keep_cache=" << t.keep_cache // - << ", .flush=" << t.flush // - << ", .nonseekable=" << t.nonseekable // - << ", .flock_release=" << t.flock_release // - << ", .cache_readdir=" << t.cache_readdir // - << ", .fh=" << t.fh // - << ", .lock_owner=" << t.lock_owner // - << ", .poll_events=" << t.poll_events // - << ",}"; -} - -//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - -// -std::ostream& operator<<(std::ostream& out, const fuse_file_info* t) -{ - if (!t) { - return out << (void*)t; - } - return out << (void*)t << ":" << *t; -} - -//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - -// -std::ostream& operator<<(std::ostream& out, const DumpStat& t) -{ - return out << "stat{" // - << ".st_dev=" << t.s.st_dev // - << ", .st_ino=" << t.s.st_ino // - << ", .st_mode=" << std::bitset<9>{t.s.st_mode} // - << ", .st_size=" << t.s.st_size // - << ", .st_nlink=" << t.s.st_nlink // - << ", .st_uid=" << t.s.st_uid // - << ", .st_gid=" << t.s.st_gid // - << ", .st_rdev=" << t.s.st_rdev // - << ", .st_blksize=" << t.s.st_blksize // - << ", .st_blocks=" << t.s.st_blocks // - << ", ,}"; -} - -//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - -// -std::ostream& operator<<(std::ostream& out, const DumpFileMode t) -{ - if (S_ISBLK(t.mode)) { - out << 'b'; - } else if (S_ISCHR(t.mode)) { - out << 'c'; - } else if (S_ISDIR(t.mode)) { - out << 'd'; - } else if (S_ISFIFO(t.mode)) { - out << 'p'; - } else if (S_ISLNK(t.mode)) { - out << 'l'; - } else { - out << '-'; - } - if ((t.mode & S_IRUSR) != 0) { - out << 'r'; - } else { - out << '-'; - } - if ((t.mode & S_IWUSR) != 0) { - out << 'w'; - } else { - out << '-'; - } - if ((t.mode & S_IXUSR) != 0) { - out << 'x'; - } else { - out << '-'; - } - if ((t.mode & S_IRGRP) != 0) { - out << 'r'; - } else { - out << '-'; - } - if ((t.mode & S_IWGRP) != 0) { - out << 'w'; - } else { - out << '-'; - } - if ((t.mode & S_IXGRP) != 0) { - out << 'x'; - } else { - out << '-'; - } - if ((t.mode & S_IROTH) != 0) { - out << 'r'; - } else { - out << '-'; - } - if ((t.mode & S_IWOTH) != 0) { - out << 'w'; - } else { - out << '-'; - } - if ((t.mode & S_IXOTH) != 0) { - out << 'x'; - } else { - out << '-'; - } - return out; -} - -} //namespace llfs diff --git a/src/llfs/fuse.hpp b/src/llfs/fuse.hpp deleted file mode 100644 index 6bfdeb18..00000000 --- a/src/llfs/fuse.hpp +++ /dev/null @@ -1,755 +0,0 @@ -//#=##=##=#==#=#==#===#+==#+==========+==+=+=+=+=+=++=+++=+++++=-++++=-+++++++++++ -// -// Part of the LLFS Project, under Apache License v2.0. -// See https://www.apache.org/licenses/LICENSE-2.0 for license information. -// SPDX short identifier: Apache-2.0 -// -//+++++++++++-+-+--+----- --- -- - - - - - -#pragma once -#ifndef LLFS_FUSE_HPP -#define LLFS_FUSE_HPP - -#include -// -#include -#include -#include - -#include -#include -#include -#include -#include - -#include - -#include -#include - -#include -#include -#include - -namespace llfs { - -std::ostream& operator<<(std::ostream& out, const fuse_file_info& t); - -std::ostream& operator<<(std::ostream& out, const fuse_file_info* t); - -//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - -// -struct DumpStat { - const struct stat& s; -}; - -std::ostream& operator<<(std::ostream& out, const DumpStat& t); - -//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - -// -struct DumpFileMode { - explicit DumpFileMode(mode_t mode) noexcept : mode{mode} - { - } - - explicit DumpFileMode(int mode) noexcept : mode{(mode_t)mode} - { - } - - mode_t mode; -}; - -std::ostream& operator<<(std::ostream& out, const DumpFileMode t); - -//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - -// -struct DumpFuseBufInfo { - explicit DumpFuseBufInfo(const fuse_buf& buf) noexcept : buf{buf} - { - } - - const fuse_buf& buf; -}; - -std::ostream& operator<<(std::ostream& out, const DumpFuseBufInfo& t); - -//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - -// -struct DumpFuseBufFlags { - explicit DumpFuseBufFlags(enum fuse_buf_flags flags) noexcept : flags{flags} - { - } - - enum fuse_buf_flags flags; -}; - -std::ostream& operator<<(std::ostream& out, const DumpFuseBufFlags& t); - -//=#=#==#==#===============+=+=+=+=++=++++++++++++++-++-+--+-+----+--------------- -// -class FuseImplBase -{ - public: - using ConstBufferVec = batt::SmallVec; - - template - struct WithCleanup { - T value; - batt::SmallFn cleanup; - }; - - template - static WithCleanup> with_cleanup(T&& value, Fn&& fn) - { - return {BATT_FORWARD(value), {BATT_FORWARD(fn)}}; - } - - struct FileDataRef { - FileDescriptorInt fd; - batt::Optional offset; - usize size; - bool should_retry; - }; - - struct OwnedConstBuffer { - std::unique_ptr storage; - batt::ConstBuffer buffer; - }; - - struct OwnedMutableBuffer { - std::unique_ptr storage; - batt::ConstBuffer buffer; - }; - - using FuseConstBuffer = std::variant; - using FuseMutableBuffer = std::variant; - - struct FuseConstBufferVec { - usize current_buffer_index; - usize current_buffer_offset; - batt::Slice buffers; - }; - - struct FuseMutableBufferVec { - usize current_buffer_index; - usize current_buffer_offset; - batt::Slice buffers; - }; - - using FuseReadData = std::variant< // - batt::ConstBuffer, // - OwnedConstBuffer, // - WithCleanup>, // - FuseConstBufferVec // - >; - - using FuseReadDirData = std::variant< // - batt::ConstBuffer, // - OwnedConstBuffer, // - FuseConstBufferVec // - >; - - struct Attributes { - const struct stat* attr; - double timeout_sec; - }; - - struct ExtendedAttribute { - std::string_view name; - batt::ConstBuffer value; - }; - - using FuseGetExtendedAttributeReply = std::variant< // - batt::ConstBuffer, // - OwnedConstBuffer, // - FuseConstBufferVec, // - BufferSizeNeeded // - >; - - struct FuseCreateReply { - const fuse_entry_param* entry; - const fuse_file_info* fi; - }; - - struct FuseIoctlResult { - int value; - batt::SmallVec buffers; - }; - - struct FuseIoctlRetry { - batt::SmallVec in; - batt::SmallVec out; - }; - - using FuseIoctlReply = std::variant; - - //+++++++++++-+-+--+----- --- -- - - - - - - /** \brief - */ - static constexpr bool can_cast_iovec_to_const_buffer() - { - return sizeof(struct iovec) == sizeof(batt::ConstBuffer); - } - - /** \brief - */ - static int errno_from_status(batt::Status status); - - /** \brief - */ - static batt::StatusOr const_buffer_vec_from_bufv( - fuse_bufvec& bufv, std::shared_ptr* storage); - - //+++++++++++-+-+--+----- --- -- - - - - - - FuseImplBase() = default; - - FuseImplBase(const FuseImplBase&) = delete; - FuseImplBase& operator=(const FuseImplBase&) = delete; - - virtual ~FuseImplBase() = default; - - /** \brief - */ - auto make_error_handler(fuse_req_t req) - { - return [req](batt::Status result) { - LLFS_VLOG(1) << BATT_INSPECT(req) << BATT_INSPECT(result); - fuse_reply_err(req, FuseImplBase::errno_from_status(result)); - }; - } - - /** \brief - */ - auto make_entry_handler(fuse_req_t req) - { - return [req](batt::StatusOr result) { - if (!result.ok()) { - LLFS_VLOG(1) << BATT_INSPECT(req) << BATT_INSPECT(result.status()); - fuse_reply_err(req, FuseImplBase::errno_from_status(result.status())); - } else { - BATT_CHECK_NOT_NULLPTR(*result); - LLFS_VLOG(1) << BATT_INSPECT(req) << " OK" << BATT_INSPECT(*result); - fuse_reply_entry(req, *result); - } - }; - } - - /** \brief - */ - auto make_attributes_handler(fuse_req_t req) - { - return [req](batt::StatusOr result) { - if (!result.ok()) { - LLFS_VLOG(1) << BATT_INSPECT(req) << BATT_INSPECT(result.status()); - fuse_reply_err(req, FuseImplBase::errno_from_status(result.status())); - } else { - BATT_CHECK_NOT_NULLPTR(result->attr); - LLFS_VLOG(1) << BATT_INSPECT(req) << " OK" << BATT_INSPECT(DumpStat{*result->attr}); - fuse_reply_attr(req, result->attr, result->timeout_sec); - } - }; - } - - /** \brief - */ - auto make_readlink_handler(fuse_req_t req) - { - return [req](const char* link) { - fuse_reply_readlink(req, link); - }; - } - - /** \brief - */ - auto make_open_handler(fuse_req_t req) - { - return [req](batt::StatusOr result) { - if (!result.ok()) { - fuse_reply_err(req, FuseImplBase::errno_from_status(result.status())); - } else { - fuse_reply_open(req, *result); - } - }; - } - - /** \brief - */ - auto make_create_handler(fuse_req_t req) - { - return [req](const batt::StatusOr& result) { - if (!result.ok()) { - fuse_reply_err(req, FuseImplBase::errno_from_status(result.status())); - } else { - fuse_reply_create(req, result->entry, result->fi); - } - }; - } - - /** \brief - */ - auto make_read_handler(fuse_req_t req) - { - return [req, this](const batt::StatusOr& result) { - if (!result.ok()) { - fuse_reply_err(req, FuseImplBase::errno_from_status(result.status())); - } else { - batt::case_of( // - *result, // - - //----- --- -- - - - - - [&req, this](const batt::ConstBuffer& cb) { - this->invoke_fuse_reply_buf(req, cb); - }, - - //----- --- -- - - - - - [&req, this](const OwnedConstBuffer& ocb) { - this->invoke_fuse_reply_buf(req, ocb.buffer); - }, - - //----- --- -- - - - - - [&req, this](WithCleanup> cbs) { - auto on_scope_exit = batt::finally([&] { - cbs.cleanup(cbs.value); - }); - this->invoke_fuse_reply_iov(req, cbs.value); - }, - - //----- --- -- - - - - - [&req, this](const FuseConstBufferVec& v) { - this->invoke_fuse_reply_data(req, v); - }); - } - }; - } - - /** \brief - */ - auto make_readdir_handler(fuse_req_t req) - { - return [req, this](const batt::StatusOr& result) { - if (!result.ok()) { - fuse_reply_err(req, FuseImplBase::errno_from_status(result.status())); - } else { - batt::case_of( // - *result, // - - //----- --- -- - - - - - [&req, this](const batt::ConstBuffer& cb) { - this->invoke_fuse_reply_buf(req, cb); - }, - - //----- --- -- - - - - - [&req, this](const OwnedConstBuffer& ocb) { - this->invoke_fuse_reply_buf(req, ocb.buffer); - }, - - //----- --- -- - - - - - [&req, this](const FuseConstBufferVec& v) { - this->invoke_fuse_reply_data(req, v); - }); - } - }; - } - - /** \brief - */ - auto make_write_handler(fuse_req_t req) - { - return [req](batt::StatusOr result) { - if (!result.ok()) { - fuse_reply_err(req, FuseImplBase::errno_from_status(result.status())); - } else { - fuse_reply_write(req, /*count=*/*result); - } - }; - } - - /** \brief - */ - auto make_statfs_handler(fuse_req_t req) - { - return [req](batt::StatusOr result) { - if (!result.ok()) { - fuse_reply_err(req, FuseImplBase::errno_from_status(result.status())); - } else { - fuse_reply_statfs(req, /*stbuf=*/*result); - } - }; - } - - /** \brief - */ - auto make_extended_attribute_handler(fuse_req_t req) - { - return [req, this](const batt::StatusOr& result) { - if (!result.ok()) { - fuse_reply_err(req, FuseImplBase::errno_from_status(result.status())); - } else { - batt::case_of( // - *result, // - - //----- --- -- - - - - - [&req, this](const batt::ConstBuffer& cb) { - this->invoke_fuse_reply_buf(req, cb); - }, - - //----- --- -- - - - - - [&req, this](const OwnedConstBuffer& ocb) { - this->invoke_fuse_reply_buf(req, ocb.buffer); - }, - - //----- --- -- - - - - - [&req, this](const FuseConstBufferVec& v) { - this->invoke_fuse_reply_data(req, v); - }, - - //----- --- -- - - - - - [&req](BufferSizeNeeded count) { - fuse_reply_xattr(req, count.value()); - }); - } - }; - } - - /** \brief - */ - auto make_no_arg_handler(fuse_req_t req) - { - return [req]() { - fuse_reply_none(req); - }; - } - - /** \brief - */ - auto make_ioctl_handler(fuse_req_t req) - { - return [req](const batt::StatusOr& reply) { - if (!reply.ok()) { - fuse_reply_err(req, FuseImplBase::errno_from_status(reply.status())); - } else { - batt::case_of( // - *reply, // - - //----- --- -- - - - - - [&req](const FuseIoctlResult& result) { - BATT_CHECK_EQ(result.buffers.size(), 1u) - << "TODO [tastolfi 2023-10-25] Support iovec here!"; - - fuse_reply_ioctl(req, result.value, // - result.buffers[0].data(), // - result.buffers[0].size()); - }, - - //----- --- -- - - - - - [&req](const auto& /*other*/) { - BATT_PANIC() << "TODO [tastolfi 2023-10-25] Not Implemented!"; - }); - } - }; - } - - //+++++++++++-+-+--+----- --- -- - - - - - - /** \brief - */ - int invoke_fuse_reply_buf(fuse_req_t req, const batt::ConstBuffer& cb); - - /** \brief - */ - int invoke_fuse_reply_iov(fuse_req_t req, const batt::Slice& cbs); - - /** \brief - */ - int invoke_fuse_reply_data(fuse_req_t req, const FuseConstBufferVec& v); - - //+++++++++++-+-+--+----- --- -- - - - - - - protected: - fuse_conn_info* conn_ = nullptr; -}; - -//=#=#==#==#===============+=+=+=+=++=++++++++++++++-++-+--+-+----+--------------- - -/** \brief - */ -template -class FuseImpl : public FuseImplBase -{ - public: - using Self = FuseImpl; - - //+++++++++++-+-+--+----- --- -- - - - - - - //----- --- -- - - - - - // FUSE op impls. - //----- --- -- - - - - - - static void op_init_impl(void* userdata, struct fuse_conn_info* conn); - - static void op_destroy_impl(void* userdata); - - static void op_lookup_impl(fuse_req_t req, fuse_ino_t parent, const char* name); - - static void op_forget_impl(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup); - - static void op_getattr_impl(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi); - - static void op_setattr_impl(fuse_req_t req, fuse_ino_t ino, struct stat* attr, int to_set, - struct fuse_file_info* fi); - - static void op_readlink_impl(fuse_req_t req, fuse_ino_t ino); - - static void op_mknod_impl(fuse_req_t req, fuse_ino_t parent, const char* name, mode_t mode, - dev_t rdev); - - static void op_mkdir_impl(fuse_req_t req, fuse_ino_t parent, const char* name, mode_t mode); - - static void op_unlink_impl(fuse_req_t req, fuse_ino_t parent, const char* name); - - static void op_rmdir_impl(fuse_req_t req, fuse_ino_t parent, const char* name); - - static void op_symlink_impl(fuse_req_t req, const char* link, fuse_ino_t parent, - const char* name); - - static void op_rename_impl(fuse_req_t req, fuse_ino_t parent, const char* name, - fuse_ino_t newparent, const char* newname, unsigned int flags); - - static void op_link_impl(fuse_req_t req, fuse_ino_t ino, fuse_ino_t newparent, - const char* newname); - - static void op_open_impl(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi); - - static void op_read_impl(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, - struct fuse_file_info* fi); - - static void op_write_impl(fuse_req_t req, fuse_ino_t ino, const char* buf, size_t size, off_t off, - struct fuse_file_info* fi); - - static void op_flush_impl(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi); - - static void op_release_impl(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi); - - static void op_fsync_impl(fuse_req_t req, fuse_ino_t ino, int datasync, - struct fuse_file_info* fi); - - static void op_opendir_impl(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi); - - static void op_readdir_impl(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, - struct fuse_file_info* fi); - - static void op_releasedir_impl(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi); - - static void op_fsyncdir_impl(fuse_req_t req, fuse_ino_t ino, int datasync, - struct fuse_file_info* fi); - - static void op_statfs_impl(fuse_req_t req, fuse_ino_t ino); - - static void op_setxattr_impl(fuse_req_t req, fuse_ino_t ino, const char* name, const char* value, - size_t size, int flags); - - static void op_getxattr_impl(fuse_req_t req, fuse_ino_t ino, const char* name, size_t size); - - static void op_listxattr_impl(fuse_req_t req, fuse_ino_t ino, size_t size); - - static void op_removexattr_impl(fuse_req_t req, fuse_ino_t ino, const char* name); - - static void op_access_impl(fuse_req_t req, fuse_ino_t ino, int mask); - - static void op_create_impl(fuse_req_t req, fuse_ino_t parent, const char* name, mode_t mode, - struct fuse_file_info* fi); - - static void op_getlk_impl(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi, - struct flock* lock); - - static void op_setlk_impl(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi, - struct flock* lock, int sleep); - - static void op_bmap_impl(fuse_req_t req, fuse_ino_t ino, size_t blocksize, uint64_t idx); - - static void op_ioctl_impl(fuse_req_t req, fuse_ino_t ino, unsigned int cmd, void* arg, - struct fuse_file_info* fi, unsigned flags, const void* in_buf, - size_t in_bufsz, size_t out_bufsz); - - static void op_poll_impl(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi, - struct fuse_pollhandle* ph); - - static void op_write_buf_impl(fuse_req_t req, fuse_ino_t ino, struct fuse_bufvec* bufv, off_t off, - struct fuse_file_info* fi); - - static void op_retrieve_reply_impl(fuse_req_t req, void* cookie, fuse_ino_t ino, off_t offset, - struct fuse_bufvec* bufv); - - static void op_forget_multi_impl(fuse_req_t req, size_t count, struct fuse_forget_data* forgets); - - static void op_flock_impl(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi, int op); - - static void op_fallocate_impl(fuse_req_t req, fuse_ino_t ino, int mode, off_t offset, - off_t length, struct fuse_file_info* fi); - - static void op_readdirplus_impl(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, - struct fuse_file_info* fi); - - static void op_copy_file_range_impl(fuse_req_t req, fuse_ino_t ino_in, off_t off_in, - struct fuse_file_info* fi_in, fuse_ino_t ino_out, - off_t off_out, struct fuse_file_info* fi_out, size_t len, - int flags); - - static void op_lseek_impl(fuse_req_t req, fuse_ino_t ino, off_t off, int whence, - struct fuse_file_info* fi); - - //----- --- -- - - - - - - static const fuse_lowlevel_ops* get_fuse_lowlevel_ops(); - - //+++++++++++-+-+--+----- --- -- - - - - - - FuseImpl() = default; - - Derived* derived_this() noexcept - { - return static_cast(this); - } - - const Derived* derived_this() const noexcept - { - return static_cast(this); - } -}; - -//=#=#==#==#===============+=+=+=+=++=++++++++++++++-++-+--+-+----+--------------- - -class FuseSession -{ - public: - template - static batt::StatusOr from_args(int argc, const char* argv[], - batt::StaticType /*impl*/, - ImplArgs&&... impl_args) - { - FuseSession instance{argc, (char**)argv}; - - if (fuse_parse_cmdline(&instance.args_, &instance.opts_) != 0) { - return {batt::StatusCode::kInvalidArgument}; - } - - instance.impl_ = std::make_unique(BATT_FORWARD(impl_args)...); - - instance.session_.reset(fuse_session_new(&instance.args_, Impl::get_fuse_lowlevel_ops(), - sizeof(struct fuse_lowlevel_ops), - instance.impl_.get())); - - if (instance.session_ == nullptr) { - LLFS_LOG_ERROR() << "fuse_sesion_new returned NULL"; - return {batt::StatusCode::kInternal}; - } - - { - const int retval = fuse_set_signal_handlers(instance.session_.get()); - if (retval != 0) { - LLFS_LOG_ERROR() << "fuse_set_signal_handlers returned " << retval; - return {batt::StatusCode::kInternal}; - } - } - { - const int retval = fuse_session_mount(instance.session_.get(), instance.opts_.mountpoint); - if (retval != 0) { - LLFS_LOG_ERROR() << "fuse_session_mount returned " << retval; - return {batt::StatusCode::kInternal}; - } - instance.mounted_ = true; - } - - return instance; - } - - //+++++++++++-+-+--+----- --- -- - - - - - - FuseSession(const FuseSession&) = delete; - FuseSession& operator=(const FuseSession&) = delete; - - FuseSession(FuseSession&&) = default; - FuseSession& operator=(FuseSession&&) = default; - - ~FuseSession() noexcept - { - if (this->session_ != nullptr) { - if (this->mounted_) { - fuse_session_unmount(this->session_.get()); - this->mounted_ = false; - } - fuse_session_destroy(this->session_.release()); - } - } - - int run() noexcept - { - bool cleanup_thread_id = false; - { - std::unique_lock lock{*this->mutex_}; - if (!this->run_thread_id_) { - this->run_thread_id_ = std::make_unique>(pthread_self()); - this->run_thread_id_->store(pthread_self()); - cleanup_thread_id = true; - } - } - auto on_scope_exit = batt::finally([&] { - if (cleanup_thread_id) { - std::unique_lock lock{*this->mutex_}; - this->run_thread_id_ = nullptr; - } - }); - - if (this->opts_.singlethread) { - return fuse_session_loop(this->session_.get()); - } - - struct fuse_loop_config config; - std::memset(&config, 0, sizeof(config)); - - config.clone_fd = this->opts_.clone_fd; - config.max_idle_threads = this->opts_.max_idle_threads; - - return fuse_session_loop_mt(this->session_.get(), &config); - } - - void halt() - { - fuse_session_exit(this->session_.get()); - { - std::unique_lock lock{*this->mutex_}; - if (this->run_thread_id_) { - pthread_kill(this->run_thread_id_->load(), SIGPIPE); - } - } - } - - private: - FuseSession(int argc, char* argv[]) noexcept : args_ FUSE_ARGS_INIT(argc, argv) - { - } - - //+++++++++++-+-+--+----- --- -- - - - - - - struct fuse_args args_; - - struct fuse_cmdline_opts opts_; - - batt::UniqueNonOwningPtr session_; - - std::unique_ptr impl_; - - bool mounted_ = false; - - std::unique_ptr mutex_ = std::make_unique(); - - std::unique_ptr> run_thread_id_; -}; - -} //namespace llfs - -#endif // LLFS_FUSE_HPP - -#include diff --git a/src/llfs/fuse.ipp b/src/llfs/fuse.ipp deleted file mode 100644 index 41ba93af..00000000 --- a/src/llfs/fuse.ipp +++ /dev/null @@ -1,1629 +0,0 @@ -//#=##=##=#==#=#==#===#+==#+==========+==+=+=+=+=+=++=+++=+++++=-++++=-+++++++++++ -// -// Part of the LLFS Project, under Apache License v2.0. -// See https://www.apache.org/licenses/LICENSE-2.0 for license information. -// SPDX short identifier: Apache-2.0 -// -//+++++++++++-+-+--+----- --- -- - - - - - -#pragma once -#ifndef LLFS_FUSE_IPP -#define LLFS_FUSE_IPP - -#include - -#include -#include - -namespace llfs { - -/** - * Initialize filesystem - * - * This function is called when libfuse establishes - * communication with the FUSE kernel module. The file system - * should use this module to inspect and/or modify the - * connection parameters provided in the `conn` structure. - * - * Note that some parameters may be overwritten by options - * passed to fuse_session_new() which take precedence over the - * values set in this handler. - * - * There's no reply to this function - * - * @param userdata the user data passed to fuse_session_new() - */ // 1/44 -template -/*static*/ inline void FuseImpl::op_init_impl(void* userdata, struct fuse_conn_info* conn) -{ - [[maybe_unused]] auto* impl = static_cast*>(userdata); - - BATT_CHECK_NOT_NULLPTR(impl); - - impl->conn_ = conn; - impl->derived_this()->init(); -} - -/** - * Clean up filesystem. - * - * Called on filesystem exit. When this method is called, the - * connection to the kernel may be gone already, so that eg. calls - * to fuse_lowlevel_notify_* will fail. - * - * There's no reply to this function - * - * @param userdata the user data passed to fuse_session_new() - */ // 2/44 -template -/*static*/ inline void FuseImpl::op_destroy_impl(void* userdata) -{ - [[maybe_unused]] auto* impl = static_cast*>(userdata); - - BATT_CHECK_NOT_NULLPTR(impl); - - auto on_scope_exit = batt::finally([&] { - impl->conn_ = nullptr; - }); - - impl->derived_this()->destroy(); -} - -/** - * Look up a directory entry by name and get its attributes. - * - * Valid replies: - * fuse_reply_entry - * fuse_reply_err - * - * @param req request handle - * @param parent inode number of the parent directory - * @param name the name to look up - */ // 3/44 -template -/*static*/ inline void FuseImpl::op_lookup_impl(fuse_req_t req, fuse_ino_t parent, - const char* name) -{ - void* userdata = fuse_req_userdata(req); - [[maybe_unused]] auto* impl = static_cast*>(userdata); - - impl->derived_this()->async_lookup(req, parent, name, impl->make_entry_handler(req)); -} - -/** - * Forget about an inode - * - * This function is called when the kernel removes an inode - * from its internal caches. - * - * The inode's lookup count increases by one for every call to - * fuse_reply_entry and fuse_reply_create. The nlookup parameter - * indicates by how much the lookup count should be decreased. - * - * Inodes with a non-zero lookup count may receive request from - * the kernel even after calls to unlink, rmdir or (when - * overwriting an existing file) rename. Filesystems must handle - * such requests properly and it is recommended to defer removal - * of the inode until the lookup count reaches zero. Calls to - * unlink, rmdir or rename will be followed closely by forget - * unless the file or directory is open, in which case the - * kernel issues forget only after the release or releasedir - * calls. - * - * Note that if a file system will be exported over NFS the - * inodes lifetime must extend even beyond forget. See the - * generation field in struct fuse_entry_param above. - * - * On unmount the lookup count for all inodes implicitly drops - * to zero. It is not guaranteed that the file system will - * receive corresponding forget messages for the affected - * inodes. - * - * Valid replies: - * fuse_reply_none - * - * @param req request handle - * @param ino the inode number - * @param nlookup the number of lookups to forget - */ // 4/44 -template -/*static*/ inline void FuseImpl::op_forget_impl(fuse_req_t req, fuse_ino_t ino, - uint64_t nlookup) -{ - void* userdata = fuse_req_userdata(req); - [[maybe_unused]] auto* impl = static_cast*>(userdata); - - impl->derived_this()->async_forget_inode(req, ino, nlookup, impl->make_no_arg_handler(req)); -} - -/** - * Get file attributes. - * - * If writeback caching is enabled, the kernel may have a - * better idea of a file's length than the FUSE file system - * (eg if there has been a write that extended the file size, - * but that has not yet been passed to the filesystem. - * - * In this case, the st_size value provided by the file system - * will be ignored. - * - * Valid replies: - * fuse_reply_attr - * fuse_reply_err - * - * @param req request handle - * @param ino the inode number - * @param fi for future use, currently always NULL - */ // 5/44 -template -/*static*/ inline void FuseImpl::op_getattr_impl(fuse_req_t req, fuse_ino_t ino, - struct fuse_file_info* fi) -{ - void* userdata = fuse_req_userdata(req); - [[maybe_unused]] auto* impl = static_cast*>(userdata); - - if (fi != nullptr) { - LLFS_LOG_WARNING() << "(getattr) Expected fi to be NULL!" << BATT_INSPECT(*fi); - } - - impl->derived_this()->async_get_attributes(req, ino, impl->make_attributes_handler(req)); -} - -/** - * Set file attributes - * - * In the 'attr' argument only members indicated by the 'to_set' - * bitmask contain valid values. Other members contain undefined - * values. - * - * Unless FUSE_CAP_HANDLE_KILLPRIV is disabled, this method is - * expected to reset the setuid and setgid bits if the file - * size or owner is being changed. - * - * This method will not be called to update st_atime or st_ctime implicitly - * (eg. after a read() request), and only be called to implicitly update st_mtime - * if writeback caching is active. It is the filesystem's responsibility to update - * these timestamps when needed, and (if desired) to implement mount options like - * `noatime` or `relatime`. - * - * If the setattr was invoked from the ftruncate() system call - * under Linux kernel versions 2.6.15 or later, the fi->fh will - * contain the value set by the open method or will be undefined - * if the open method didn't set any value. Otherwise (not - * ftruncate call, or kernel version earlier than 2.6.15) the fi - * parameter will be NULL. - * - * Valid replies: - * fuse_reply_attr - * fuse_reply_err - * - * @param req request handle - * @param ino the inode number - * @param attr the attributes - * @param to_set bit mask of attributes which should be set - * @param fi file information, or NULL - */ // 6/44 -template -/*static*/ inline void FuseImpl::op_setattr_impl(fuse_req_t req, fuse_ino_t ino, - struct stat* attr, int to_set, - struct fuse_file_info* fi) -{ - void* userdata = fuse_req_userdata(req); - [[maybe_unused]] auto* impl = static_cast*>(userdata); - - impl->derived_this()->async_set_attributes(req, ino, attr, to_set, fi, - impl->make_attributes_handler(req)); -} - -/** - * Read symbolic link - * - * Valid replies: - * fuse_reply_readlink - * fuse_reply_err - * - * @param req request handle - * @param ino the inode number - */ // 7/44 -template -/*static*/ inline void FuseImpl::op_readlink_impl(fuse_req_t req, fuse_ino_t ino) -{ - void* userdata = fuse_req_userdata(req); - [[maybe_unused]] auto* impl = static_cast*>(userdata); - - impl->derived_this()->async_readlink(req, ino, impl->make_readlink_handler(req)); -} - -/** - * Create file node - * - * Create a regular file, character device, block device, fifo or - * socket node. - * - * Valid replies: - * fuse_reply_entry - * fuse_reply_err - * - * @param req request handle - * @param parent inode number of the parent directory - * @param name to create - * @param mode file type and mode with which to create the new file - * @param rdev the device number (only valid if created file is a device) - */ // 8/44 -template -/*static*/ inline void FuseImpl::op_mknod_impl(fuse_req_t req, fuse_ino_t parent, - const char* name, mode_t mode, dev_t rdev) -{ - void* userdata = fuse_req_userdata(req); - [[maybe_unused]] auto* impl = static_cast*>(userdata); - - impl->derived_this()->async_make_node(req, parent, name, mode, rdev, - impl->make_entry_handler(req)); -} - -/** - * Create a directory - * - * Valid replies: - * fuse_reply_entry - * fuse_reply_err - * - * @param req request handle - * @param parent inode number of the parent directory - * @param name to create - * @param mode with which to create the new file - */ // 9/44 -template -/*static*/ inline void FuseImpl::op_mkdir_impl(fuse_req_t req, fuse_ino_t parent, - const char* name, mode_t mode) -{ - void* userdata = fuse_req_userdata(req); - [[maybe_unused]] auto* impl = static_cast*>(userdata); - - impl->derived_this()->async_make_directory(req, parent, name, mode, - impl->make_entry_handler(req)); -} - -/** - * Remove a file - * - * If the file's inode's lookup count is non-zero, the file - * system is expected to postpone any removal of the inode - * until the lookup count reaches zero (see description of the - * forget function). - * - * Valid replies: - * fuse_reply_err - * - * @param req request handle - * @param parent inode number of the parent directory - * @param name to remove - */ // 10/44 -template -/*static*/ inline void FuseImpl::op_unlink_impl(fuse_req_t req, fuse_ino_t parent, - const char* name) -{ - void* userdata = fuse_req_userdata(req); - [[maybe_unused]] auto* impl = static_cast*>(userdata); - - impl->derived_this()->async_unlink(req, parent, name, impl->make_error_handler(req)); -} - -/** - * Remove a directory - * - * If the directory's inode's lookup count is non-zero, the - * file system is expected to postpone any removal of the - * inode until the lookup count reaches zero (see description - * of the forget function). - * - * Valid replies: - * fuse_reply_err - * - * @param req request handle - * @param parent inode number of the parent directory - * @param name to remove - */ // 11/44 -template -/*static*/ inline void FuseImpl::op_rmdir_impl(fuse_req_t req, fuse_ino_t parent, - const char* name) -{ - void* userdata = fuse_req_userdata(req); - [[maybe_unused]] auto* impl = static_cast*>(userdata); - - impl->derived_this()->async_remove_directory(req, parent, name, impl->make_error_handler(req)); -} - -/** - * Create a symbolic link - * - * Valid replies: - * fuse_reply_entry - * fuse_reply_err - * - * @param req request handle - * @param link the contents of the symbolic link - * @param parent inode number of the parent directory - * @param name to create - */ // 12/44 -template -/*static*/ inline void FuseImpl::op_symlink_impl(fuse_req_t req, const char* link, - fuse_ino_t parent, const char* name) -{ - void* userdata = fuse_req_userdata(req); - [[maybe_unused]] auto* impl = static_cast*>(userdata); - - impl->derived_this()->async_symbolic_link(req, link, parent, name, impl->make_entry_handler(req)); -} - -/** Rename a file - * - * If the target exists it should be atomically replaced. If - * the target's inode's lookup count is non-zero, the file - * system is expected to postpone any removal of the inode - * until the lookup count reaches zero (see description of the - * forget function). - * - * If this request is answered with an error code of ENOSYS, this is - * treated as a permanent failure with error code EINVAL, i.e. all - * future bmap requests will fail with EINVAL without being - * send to the filesystem process. - * - * *flags* may be `RENAME_EXCHANGE` or `RENAME_NOREPLACE`. If - * RENAME_NOREPLACE is specified, the filesystem must not - * overwrite *newname* if it exists and return an error - * instead. If `RENAME_EXCHANGE` is specified, the filesystem - * must atomically exchange the two files, i.e. both must - * exist and neither may be deleted. - * - * Valid replies: - * fuse_reply_err - * - * @param req request handle - * @param parent inode number of the old parent directory - * @param name old name - * @param newparent inode number of the new parent directory - * @param newname new name - */ // 13/44 -template -/*static*/ inline void FuseImpl::op_rename_impl(fuse_req_t req, fuse_ino_t parent, - const char* name, fuse_ino_t newparent, - const char* newname, unsigned int flags) -{ - void* userdata = fuse_req_userdata(req); - [[maybe_unused]] auto* impl = static_cast*>(userdata); - - impl->derived_this()->async_rename(req, parent, name, newparent, newname, flags, - impl->make_error_handler(req)); -} - -/** - * Create a hard link - * - * Valid replies: - * fuse_reply_entry - * fuse_reply_err - * - * @param req request handle - * @param ino the old inode number - * @param newparent inode number of the new parent directory - * @param newname new name to create - */ // 14/44 -template -/*static*/ inline void FuseImpl::op_link_impl(fuse_req_t req, fuse_ino_t ino, - fuse_ino_t newparent, const char* newname) -{ - void* userdata = fuse_req_userdata(req); - [[maybe_unused]] auto* impl = static_cast*>(userdata); - - impl->derived_this()->async_hard_link(req, ino, newparent, newname, - impl->make_entry_handler(req)); -} - -/** - * Open a file - * - * Open flags are available in fi->flags. The following rules - * apply. - * - * - Creation (O_CREAT, O_EXCL, O_NOCTTY) flags will be - * filtered out / handled by the kernel. - * - * - Access modes (O_RDONLY, O_WRONLY, O_RDWR) should be used - * by the filesystem to check if the operation is - * permitted. If the ``-o default_permissions`` mount - * option is given, this check is already done by the - * kernel before calling open() and may thus be omitted by - * the filesystem. - * - * - When writeback caching is enabled, the kernel may send - * read requests even for files opened with O_WRONLY. The - * filesystem should be prepared to handle this. - * - * - When writeback caching is disabled, the filesystem is - * expected to properly handle the O_APPEND flag and ensure - * that each write is appending to the end of the file. - * - * - When writeback caching is enabled, the kernel will - * handle O_APPEND. However, unless all changes to the file - * come through the kernel this will not work reliably. The - * filesystem should thus either ignore the O_APPEND flag - * (and let the kernel handle it), or return an error - * (indicating that reliably O_APPEND is not available). - * - * Filesystem may store an arbitrary file handle (pointer, - * index, etc) in fi->fh, and use this in other all other file - * operations (read, write, flush, release, fsync). - * - * Filesystem may also implement stateless file I/O and not store - * anything in fi->fh. - * - * There are also some flags (direct_io, keep_cache) which the - * filesystem may set in fi, to change the way the file is opened. - * See fuse_file_info structure in for more details. - * - * If this request is answered with an error code of ENOSYS - * and FUSE_CAP_NO_OPEN_SUPPORT is set in - * `fuse_conn_info.capable`, this is treated as success and - * future calls to open and release will also succeed without being - * sent to the filesystem process. - * - * Valid replies: - * fuse_reply_open - * fuse_reply_err - * - * @param req request handle - * @param ino the inode number - * @param fi file information - */ // 15/44 -template -/*static*/ inline void FuseImpl::op_open_impl(fuse_req_t req, fuse_ino_t ino, - struct fuse_file_info* fi) -{ - void* userdata = fuse_req_userdata(req); - [[maybe_unused]] auto* impl = static_cast*>(userdata); - - impl->derived_this()->async_open(req, ino, fi, impl->make_open_handler(req)); -} - -/** - * Read data - * - * Read should send exactly the number of bytes requested except - * on EOF or error, otherwise the rest of the data will be - * substituted with zeroes. An exception to this is when the file - * has been opened in 'direct_io' mode, in which case the return - * value of the read system call will reflect the return value of - * this operation. - * - * fi->fh will contain the value set by the open method, or will - * be undefined if the open method didn't set any value. - * - * Valid replies: - * fuse_reply_buf - * fuse_reply_iov - * fuse_reply_data - * fuse_reply_err - * - * @param req request handle - * @param ino the inode number - * @param size number of bytes to read - * @param off offset to read from - * @param fi file information - */ // 16/44 -template -/*static*/ inline void FuseImpl::op_read_impl(fuse_req_t req, fuse_ino_t ino, size_t size, - off_t off, struct fuse_file_info* fi) -{ - void* userdata = fuse_req_userdata(req); - [[maybe_unused]] auto* impl = static_cast*>(userdata); - - BATT_CHECK_NOT_NULLPTR(fi); - - impl->derived_this()->async_read(req, ino, size, FileOffset{off}, FuseFileHandle{fi->fh}, - impl->make_read_handler(req)); -} - -/** - * Write data - * - * Write should return exactly the number of bytes requested - * except on error. An exception to this is when the file has - * been opened in 'direct_io' mode, in which case the return value - * of the write system call will reflect the return value of this - * operation. - * - * Unless FUSE_CAP_HANDLE_KILLPRIV is disabled, this method is - * expected to reset the setuid and setgid bits. - * - * fi->fh will contain the value set by the open method, or will - * be undefined if the open method didn't set any value. - * - * Valid replies: - * fuse_reply_write - * fuse_reply_err - * - * @param req request handle - * @param ino the inode number - * @param buf data to write - * @param size number of bytes to write - * @param off offset to write to - * @param fi file information - */ // 17/44 -template -/*static*/ inline void FuseImpl::op_write_impl(fuse_req_t req, fuse_ino_t ino, - const char* buf, size_t size, off_t off, - struct fuse_file_info* fi) -{ - void* userdata = fuse_req_userdata(req); - [[maybe_unused]] auto* impl = static_cast*>(userdata); - - BATT_CHECK_NOT_NULLPTR(fi); - - impl->derived_this()->async_write(req, ino, batt::ConstBuffer{buf, size}, FileOffset{off}, - FuseFileHandle{fi->fh}, impl->make_write_handler(req)); -} - -/** - * Flush method - * - * This is called on each close() of the opened file. - * - * Since file descriptors can be duplicated (dup, dup2, fork), for - * one open call there may be many flush calls. - * - * Filesystems shouldn't assume that flush will always be called - * after some writes, or that if will be called at all. - * - * fi->fh will contain the value set by the open method, or will - * be undefined if the open method didn't set any value. - * - * NOTE: the name of the method is misleading, since (unlike - * fsync) the filesystem is not forced to flush pending writes. - * One reason to flush data is if the filesystem wants to return - * write errors during close. However, such use is non-portable - * because POSIX does not require [close] to wait for delayed I/O to - * complete. - * - * If the filesystem supports file locking operations (setlk, - * getlk) it should remove all locks belonging to 'fi->owner'. - * - * If this request is answered with an error code of ENOSYS, - * this is treated as success and future calls to flush() will - * succeed automatically without being send to the filesystem - * process. - * - * Valid replies: - * fuse_reply_err - * - * @param req request handle - * @param ino the inode number - * @param fi file information - * - * [close]: http://pubs.opengroup.org/onlinepubs/9699919799/functions/close.html - */ // 18/44 -template -/*static*/ inline void FuseImpl::op_flush_impl(fuse_req_t req, fuse_ino_t ino, - struct fuse_file_info* fi) -{ - void* userdata = fuse_req_userdata(req); - [[maybe_unused]] auto* impl = static_cast*>(userdata); - - BATT_CHECK_NOT_NULLPTR(fi); - - /* TODO [tastolfi 2023-07-14] Take into account: - * - * "If the filesystem supports file locking operations (setlk, - * getlk) it should remove all locks belonging to 'fi->owner'." - */ - - impl->derived_this()->async_flush(req, ino, FuseFileHandle{fi->fh}, - impl->make_error_handler(req)); -} - -/** - * Release an open file - * - * Release is called when there are no more references to an open - * file: all file descriptors are closed and all memory mappings - * are unmapped. - * - * For every open call there will be exactly one release call (unless - * the filesystem is force-unmounted). - * - * The filesystem may reply with an error, but error values are - * not returned to close() or munmap() which triggered the - * release. - * - * fi->fh will contain the value set by the open method, or will - * be undefined if the open method didn't set any value. - * fi->flags will contain the same flags as for open. - * - * Valid replies: - * fuse_reply_err - * - * @param req request handle - * @param ino the inode number - * @param fi file information - */ // 19/44 -template -/*static*/ inline void FuseImpl::op_release_impl(fuse_req_t req, fuse_ino_t ino, - struct fuse_file_info* fi) -{ - void* userdata = fuse_req_userdata(req); - [[maybe_unused]] auto* impl = static_cast*>(userdata); - - BATT_CHECK_NOT_NULLPTR(fi); - - impl->derived_this()->async_release(req, ino, FuseFileHandle{fi->fh}, FileOpenFlags{fi->flags}, - impl->make_error_handler(req)); -} - -/** - * Synchronize file contents - * - * If the datasync parameter is non-zero, then only the user data - * should be flushed, not the meta data. - * - * If this request is answered with an error code of ENOSYS, - * this is treated as success and future calls to fsync() will - * succeed automatically without being send to the filesystem - * process. - * - * Valid replies: - * fuse_reply_err - * - * @param req request handle - * @param ino the inode number - * @param datasync flag indicating if only data should be flushed - * @param fi file information - */ // 20/44 -template -/*static*/ inline void FuseImpl::op_fsync_impl(fuse_req_t req, fuse_ino_t ino, - int datasync, struct fuse_file_info* fi) -{ - void* userdata = fuse_req_userdata(req); - [[maybe_unused]] auto* impl = static_cast*>(userdata); - - BATT_CHECK_NOT_NULLPTR(fi); - - impl->derived_this()->async_fsync(req, ino, IsDataSync{datasync != 0}, FuseFileHandle{fi->fh}, - impl->make_error_handler(req)); -} - -/** - * Open a directory - * - * Filesystem may store an arbitrary file handle (pointer, index, - * etc) in fi->fh, and use this in other all other directory - * stream operations (readdir, releasedir, fsyncdir). - * - * If this request is answered with an error code of ENOSYS and - * FUSE_CAP_NO_OPENDIR_SUPPORT is set in `fuse_conn_info.capable`, - * this is treated as success and future calls to opendir and - * releasedir will also succeed without being sent to the filesystem - * process. In addition, the kernel will cache readdir results - * as if opendir returned FOPEN_KEEP_CACHE | FOPEN_CACHE_DIR. - * - * Valid replies: - * fuse_reply_open - * fuse_reply_err - * - * @param req request handle - * @param ino the inode number - * @param fi file information - */ // 21/44 -template -/*static*/ inline void FuseImpl::op_opendir_impl(fuse_req_t req, fuse_ino_t ino, - struct fuse_file_info* fi) -{ - void* userdata = fuse_req_userdata(req); - [[maybe_unused]] auto* impl = static_cast*>(userdata); - - impl->derived_this()->async_opendir(req, ino, fi, impl->make_open_handler(req)); -} - -/** - * Read directory - * - * Send a buffer filled using fuse_add_direntry(), with size not - * exceeding the requested size. Send an empty buffer on end of - * stream. - * - * fi->fh will contain the value set by the opendir method, or - * will be undefined if the opendir method didn't set any value. - * - * Returning a directory entry from readdir() does not affect - * its lookup count. - * - * If off_t is non-zero, then it will correspond to one of the off_t - * values that was previously returned by readdir() for the same - * directory handle. In this case, readdir() should skip over entries - * coming before the position defined by the off_t value. If entries - * are added or removed while the directory handle is open, the filesystem - * may still include the entries that have been removed, and may not - * report the entries that have been created. However, addition or - * removal of entries must never cause readdir() to skip over unrelated - * entries or to report them more than once. This means - * that off_t can not be a simple index that enumerates the entries - * that have been returned but must contain sufficient information to - * uniquely determine the next directory entry to return even when the - * set of entries is changing. - * - * The function does not have to report the '.' and '..' - * entries, but is allowed to do so. Note that, if readdir does - * not return '.' or '..', they will not be implicitly returned, - * and this behavior is observable by the caller. - * - * Valid replies: - * fuse_reply_buf - * fuse_reply_data - * fuse_reply_err - * - * @param req request handle - * @param ino the inode number - * @param size maximum number of bytes to send - * @param off offset to continue reading the directory stream - * @param fi file information - */ // 22/44 -template -/*static*/ inline void FuseImpl::op_readdir_impl(fuse_req_t req, fuse_ino_t ino, - size_t size, off_t off, - struct fuse_file_info* fi) -{ - void* userdata = fuse_req_userdata(req); - [[maybe_unused]] auto* impl = static_cast*>(userdata); - - BATT_CHECK_NOT_NULLPTR(fi); - - impl->derived_this()->async_readdir(req, ino, size, DirentOffset{off}, FuseFileHandle{fi->fh}, - impl->make_readdir_handler(req)); -} - -/** - * Release an open directory - * - * For every opendir call there will be exactly one releasedir - * call (unless the filesystem is force-unmounted). - * - * fi->fh will contain the value set by the opendir method, or - * will be undefined if the opendir method didn't set any value. - * - * Valid replies: - * fuse_reply_err - * - * @param req request handle - * @param ino the inode number - * @param fi file information - */ // 23/44 -template -/*static*/ inline void FuseImpl::op_releasedir_impl(fuse_req_t req, fuse_ino_t ino, - struct fuse_file_info* fi) -{ - void* userdata = fuse_req_userdata(req); - [[maybe_unused]] auto* impl = static_cast*>(userdata); - - BATT_CHECK_NOT_NULLPTR(fi); - - impl->derived_this()->async_releasedir(req, ino, FuseFileHandle{fi->fh}, - impl->make_error_handler(req)); -} - -/** - * Synchronize directory contents - * - * If the datasync parameter is non-zero, then only the directory - * contents should be flushed, not the meta data. - * - * fi->fh will contain the value set by the opendir method, or - * will be undefined if the opendir method didn't set any value. - * - * If this request is answered with an error code of ENOSYS, - * this is treated as success and future calls to fsyncdir() will - * succeed automatically without being send to the filesystem - * process. - * - * Valid replies: - * fuse_reply_err - * - * @param req request handle - * @param ino the inode number - * @param datasync flag indicating if only data should be flushed - * @param fi file information - */ // 24/44 -template -/*static*/ inline void FuseImpl::op_fsyncdir_impl(fuse_req_t req, fuse_ino_t ino, - int datasync, struct fuse_file_info* fi) -{ - void* userdata = fuse_req_userdata(req); - [[maybe_unused]] auto* impl = static_cast*>(userdata); - - BATT_CHECK_NOT_NULLPTR(fi); - - impl->derived_this()->async_fsyncdir(req, ino, IsDataSync{datasync != 0}, FuseFileHandle{fi->fh}, - impl->make_error_handler(req)); -} - -/** - * Get file system statistics - * - * Valid replies: - * fuse_reply_statfs - * fuse_reply_err - * - * @param req request handle - * @param ino the inode number, zero means "undefined" - */ // 25/44 -template -/*static*/ inline void FuseImpl::op_statfs_impl(fuse_req_t req, fuse_ino_t ino) -{ - void* userdata = fuse_req_userdata(req); - [[maybe_unused]] auto* impl = static_cast*>(userdata); - - impl->derived_this()->async_statfs(req, ino, impl->make_statfs_handler(req)); -} - -/** - * Set an extended attribute - * - * If this request is answered with an error code of ENOSYS, this is - * treated as a permanent failure with error code EOPNOTSUPP, i.e. all - * future setxattr() requests will fail with EOPNOTSUPP without being - * send to the filesystem process. - * - * Valid replies: - * fuse_reply_err - */ // 26/44 -template -/*static*/ inline void FuseImpl::op_setxattr_impl(fuse_req_t req, fuse_ino_t ino, - const char* name, const char* value, - size_t size, int flags) -{ - void* userdata = fuse_req_userdata(req); - [[maybe_unused]] auto* impl = static_cast*>(userdata); - - impl->derived_this()->async_set_extended_attribute(req, ino, - FuseImplBase::ExtendedAttribute{ - .name = std::string_view{name}, - .value = batt::ConstBuffer{value, size}, - }, - flags, impl->make_error_handler(req)); -} - -/** - * Get an extended attribute - * - * If size is zero, the size of the value should be sent with - * fuse_reply_xattr. - * - * If the size is non-zero, and the value fits in the buffer, the - * value should be sent with fuse_reply_buf. - * - * If the size is too small for the value, the ERANGE error should - * be sent. - * - * If this request is answered with an error code of ENOSYS, this is - * treated as a permanent failure with error code EOPNOTSUPP, i.e. all - * future getxattr() requests will fail with EOPNOTSUPP without being - * send to the filesystem process. - * - * Valid replies: - * fuse_reply_buf - * fuse_reply_data - * fuse_reply_xattr - * fuse_reply_err - * - * @param req request handle - * @param ino the inode number - * @param name of the extended attribute - * @param size maximum size of the value to send - */ // 27/44 -template -/*static*/ inline void FuseImpl::op_getxattr_impl(fuse_req_t req, fuse_ino_t ino, - const char* name, size_t size) -{ - void* userdata = fuse_req_userdata(req); - [[maybe_unused]] auto* impl = static_cast*>(userdata); - - impl->derived_this()->async_get_extended_attribute(req, ino, name, size, - impl->make_extended_attribute_handler(req)); -} - -/** - * List extended attribute names - * - * If size is zero, the total size of the attribute list should be - * sent with fuse_reply_xattr. - * - * If the size is non-zero, and the null character separated - * attribute list fits in the buffer, the list should be sent with - * fuse_reply_buf. - * - * If the size is too small for the list, the ERANGE error should - * be sent. - * - * If this request is answered with an error code of ENOSYS, this is - * treated as a permanent failure with error code EOPNOTSUPP, i.e. all - * future listxattr() requests will fail with EOPNOTSUPP without being - * send to the filesystem process. - * - * Valid replies: - * fuse_reply_buf - * fuse_reply_data - * fuse_reply_xattr - * fuse_reply_err - * - * @param req request handle - * @param ino the inode number - * @param size maximum size of the list to send - */ // 28/44 -template -/*static*/ inline void FuseImpl::op_listxattr_impl(fuse_req_t req, fuse_ino_t ino, - size_t size) -{ - void* userdata = fuse_req_userdata(req); - [[maybe_unused]] auto* impl = static_cast*>(userdata); - - (void)ino; - (void)size; - - LLFS_LOG_WARNING_FIRST_N(1) << "Not Implemented: " << BATT_THIS_FUNCTION; - // TODO [tastolfi 2023-06-28] - - fuse_reply_err(req, - FuseImplBase::errno_from_status(batt::Status{batt::StatusCode::kUnimplemented})); -} - -/** - * Remove an extended attribute - * - * If this request is answered with an error code of ENOSYS, this is - * treated as a permanent failure with error code EOPNOTSUPP, i.e. all - * future removexattr() requests will fail with EOPNOTSUPP without being - * send to the filesystem process. - * - * Valid replies: - * fuse_reply_err - * - * @param req request handle - * @param ino the inode number - * @param name of the extended attribute - */ // 29/44 -template -/*static*/ inline void FuseImpl::op_removexattr_impl(fuse_req_t req, fuse_ino_t ino, - const char* name) -{ - void* userdata = fuse_req_userdata(req); - [[maybe_unused]] auto* impl = static_cast*>(userdata); - - impl->derived_this()->async_remove_extended_attribute(req, ino, name, - impl->make_error_handler(req)); -} - -/** - * Check file access permissions - * - * This will be called for the access() and chdir() system - * calls. If the 'default_permissions' mount option is given, - * this method is not called. - * - * This method is not called under Linux kernel versions 2.4.x - * - * If this request is answered with an error code of ENOSYS, this is - * treated as a permanent success, i.e. this and all future access() - * requests will succeed without being send to the filesystem process. - * - * Valid replies: - * fuse_reply_err - * - * @param req request handle - * @param ino the inode number - * @param mask requested access mode - */ // 30/44 -template -/*static*/ inline void FuseImpl::op_access_impl(fuse_req_t req, fuse_ino_t ino, int mask) -{ - void* userdata = fuse_req_userdata(req); - [[maybe_unused]] auto* impl = static_cast*>(userdata); - - impl->derived_this()->async_check_access(req, ino, mask, impl->make_error_handler(req)); -} - -/** - * Create and open a file - * - * If the file does not exist, first create it with the specified - * mode, and then open it. - * - * See the description of the open handler for more - * information. - * - * If this method is not implemented or under Linux kernel - * versions earlier than 2.6.15, the mknod() and open() methods - * will be called instead. - * - * If this request is answered with an error code of ENOSYS, the handler - * is treated as not implemented (i.e., for this and future requests the - * mknod() and open() handlers will be called instead). - * - * Valid replies: - * fuse_reply_create - * fuse_reply_err - * - * @param req request handle - * @param parent inode number of the parent directory - * @param name to create - * @param mode file type and mode with which to create the new file - * @param fi file information - */ // 31/44 -template -/*static*/ inline void FuseImpl::op_create_impl(fuse_req_t req, fuse_ino_t parent, - const char* name, mode_t mode, - struct fuse_file_info* fi) -{ - void* userdata = fuse_req_userdata(req); - [[maybe_unused]] auto* impl = static_cast*>(userdata); - - impl->derived_this()->async_create(req, parent, name, mode, fi, impl->make_create_handler(req)); -} - -/** - * Test for a POSIX file lock - * - * Valid replies: - * fuse_reply_lock - * fuse_reply_err - * - * @param req request handle - * @param ino the inode number - * @param fi file information - * @param lock the region/type to test - */ // 32/44 -template -/*static*/ inline void FuseImpl::op_getlk_impl(fuse_req_t req, fuse_ino_t ino, - struct fuse_file_info* fi, - struct flock* lock) -{ - void* userdata = fuse_req_userdata(req); - [[maybe_unused]] auto* impl = static_cast*>(userdata); - - (void)ino; - (void)fi; - (void)lock; - - LLFS_LOG_WARNING() << "Not Implemented: " << BATT_THIS_FUNCTION; - // TODO [tastolfi 2023-06-28] - - fuse_reply_err(req, - FuseImplBase::errno_from_status(batt::Status{batt::StatusCode::kUnimplemented})); -} - -/** - * Acquire, modify or release a POSIX file lock - * - * For POSIX threads (NPTL) there's a 1-1 relation between pid and - * owner, but otherwise this is not always the case. For checking - * lock ownership, 'fi->owner' must be used. The l_pid field in - * 'struct flock' should only be used to fill in this field in - * getlk(). - * - * Note: if the locking methods are not implemented, the kernel - * will still allow file locking to work locally. Hence these are - * only interesting for network filesystems and similar. - * - * Valid replies: - * fuse_reply_err - * - * @param req request handle - * @param ino the inode number - * @param fi file information - * @param lock the region/type to set - * @param sleep locking operation may sleep - */ // 33/44 -template -/*static*/ inline void FuseImpl::op_setlk_impl(fuse_req_t req, fuse_ino_t ino, - struct fuse_file_info* fi, - struct flock* lock, int sleep) -{ - void* userdata = fuse_req_userdata(req); - [[maybe_unused]] auto* impl = static_cast*>(userdata); - - (void)ino; - (void)fi; - (void)lock; - (void)sleep; - - LLFS_LOG_WARNING() << "Not Implemented: " << BATT_THIS_FUNCTION; - // TODO [tastolfi 2023-06-28] - - fuse_reply_err(req, - FuseImplBase::errno_from_status(batt::Status{batt::StatusCode::kUnimplemented})); -} - -/** - * Map block index within file to block index within device - * - * Note: This makes sense only for block device backed filesystems - * mounted with the 'blkdev' option - * - * If this request is answered with an error code of ENOSYS, this is - * treated as a permanent failure, i.e. all future bmap() requests will - * fail with the same error code without being send to the filesystem - * process. - * - * Valid replies: - * fuse_reply_bmap - * fuse_reply_err - * - * @param req request handle - * @param ino the inode number - * @param blocksize unit of block index - * @param idx block index within file - */ // 34/44 -template -/*static*/ inline void FuseImpl::op_bmap_impl(fuse_req_t req, fuse_ino_t ino, - size_t blocksize, uint64_t idx) -{ - void* userdata = fuse_req_userdata(req); - [[maybe_unused]] auto* impl = static_cast*>(userdata); - - (void)ino; - (void)blocksize; - (void)idx; - - LLFS_LOG_WARNING() << "Not Implemented: " << BATT_THIS_FUNCTION; - // TODO [tastolfi 2023-06-28] - - fuse_reply_err(req, - FuseImplBase::errno_from_status(batt::Status{batt::StatusCode::kUnimplemented})); -} - -/** - * Ioctl - * - * Note: For unrestricted ioctls (not allowed for FUSE - * servers), data in and out areas can be discovered by giving - * iovs and setting FUSE_IOCTL_RETRY in *flags*. For - * restricted ioctls, kernel prepares in/out data area - * according to the information encoded in cmd. - * - * Valid replies: - * fuse_reply_ioctl_retry - * fuse_reply_ioctl - * fuse_reply_ioctl_iov - * fuse_reply_err - * - * @param req request handle - * @param ino the inode number - * @param cmd ioctl command - * @param arg ioctl argument - * @param fi file information - * @param flags for FUSE_IOCTL_* flags - * @param in_buf data fetched from the caller - * @param in_bufsz number of fetched bytes - * @param out_bufsz maximum size of output data - * - * Note : the unsigned long request submitted by the application - * is truncated to 32 bits. - */ // 35/44 -template -/*static*/ inline void FuseImpl::op_ioctl_impl(fuse_req_t req, fuse_ino_t ino, - unsigned int cmd, void* arg, - struct fuse_file_info* fi, unsigned flags, - const void* in_buf, size_t in_bufsz, - size_t out_bufsz) -{ - void* userdata = fuse_req_userdata(req); - [[maybe_unused]] auto* impl = static_cast*>(userdata); - - impl->derived_this()->async_ioctl(req, ino, cmd, arg, fi, flags, - /*in_buf=*/batt::ConstBuffer{in_buf, in_bufsz}, out_bufsz, - impl->make_ioctl_handler(req)); -} - -/** - * Poll for IO readiness - * - * Note: If ph is non-NULL, the client should notify - * when IO readiness events occur by calling - * fuse_lowlevel_notify_poll() with the specified ph. - * - * Regardless of the number of times poll with a non-NULL ph - * is received, single notification is enough to clear all. - * Notifying more times incurs overhead but doesn't harm - * correctness. - * - * The callee is responsible for destroying ph with - * fuse_pollhandle_destroy() when no longer in use. - * - * If this request is answered with an error code of ENOSYS, this is - * treated as success (with a kernel-defined default poll-mask) and - * future calls to pull() will succeed the same way without being send - * to the filesystem process. - * - * Valid replies: - * fuse_reply_poll - * fuse_reply_err - * - * @param req request handle - * @param ino the inode number - * @param fi file information - * @param ph poll handle to be used for notification - */ // 36/44 -template -/*static*/ inline void FuseImpl::op_poll_impl(fuse_req_t req, fuse_ino_t ino, - struct fuse_file_info* fi, - struct fuse_pollhandle* ph) -{ - void* userdata = fuse_req_userdata(req); - [[maybe_unused]] auto* impl = static_cast*>(userdata); - - (void)ino; - (void)fi; - (void)ph; - - LLFS_LOG_WARNING() << "Not Implemented: " << BATT_THIS_FUNCTION; - // TODO [tastolfi 2023-06-28] - - fuse_reply_err(req, - FuseImplBase::errno_from_status(batt::Status{batt::StatusCode::kUnimplemented})); -} - -/** - * Write data made available in a buffer - * - * This is a more generic version of the ->write() method. If - * FUSE_CAP_SPLICE_READ is set in fuse_conn_info.want and the - * kernel supports splicing from the fuse device, then the - * data will be made available in pipe for supporting zero - * copy data transfer. - * - * buf->count is guaranteed to be one (and thus buf->idx is - * always zero). The write_buf handler must ensure that - * bufv->off is correctly updated (reflecting the number of - * bytes read from bufv->buf[0]). - * - * Unless FUSE_CAP_HANDLE_KILLPRIV is disabled, this method is - * expected to reset the setuid and setgid bits. - * - * Valid replies: - * fuse_reply_write - * fuse_reply_err - * - * @param req request handle - * @param ino the inode number - * @param bufv buffer containing the data - * @param off offset to write to - * @param fi file information - */ // 37/44 -template -/*static*/ inline void FuseImpl::op_write_buf_impl(fuse_req_t req, fuse_ino_t ino, - struct fuse_bufvec* bufv, off_t offset, - struct fuse_file_info* fi) -{ - void* userdata = fuse_req_userdata(req); - [[maybe_unused]] auto* impl = static_cast*>(userdata); - - BATT_CHECK_NOT_NULLPTR(fi); - BATT_CHECK_NOT_NULLPTR(bufv); - - std::shared_ptr storage; - - batt::StatusOr buf_vec = - FuseImplBase::const_buffer_vec_from_bufv(*bufv, &storage); - - if (!buf_vec.ok()) { - LLFS_VLOG(1) << " --" << BATT_INSPECT(buf_vec.status()); - fuse_reply_err(req, FuseImplBase::errno_from_status(buf_vec.status())); - return; - } - - impl->derived_this()->async_write_buf(req, ino, *buf_vec, FileOffset{offset}, - FuseFileHandle{fi->fh}, std::move(storage), - impl->make_write_handler(req)); -} - -/** - * Callback function for the retrieve request - * - * Valid replies: - * fuse_reply_none - * - * @param req request handle - * @param cookie user data supplied to fuse_lowlevel_notify_retrieve() - * @param ino the inode number supplied to fuse_lowlevel_notify_retrieve() - * @param offset the offset supplied to fuse_lowlevel_notify_retrieve() - * @param bufv the buffer containing the returned data - */ // 38/44 -template -/*static*/ inline void FuseImpl::op_retrieve_reply_impl(fuse_req_t req, void* cookie, - fuse_ino_t ino, off_t offset, - struct fuse_bufvec* bufv) -{ - void* userdata = fuse_req_userdata(req); - [[maybe_unused]] auto* impl = static_cast*>(userdata); - - // TODO [tastolfi 2023-06-28] bufvec -> MutableBufferSequence? - // - impl->derived_this()->async_retrieve_reply(req, cookie, ino, FileOffset{offset}, bufv, - impl->make_no_arg_handler(req)); -} - -/** - * Forget about multiple inodes - * - * See description of the forget function for more - * information. - * - * Valid replies: - * fuse_reply_none - * - * @param req request handle - */ // 39/44 -template -/*static*/ inline void FuseImpl::op_forget_multi_impl(fuse_req_t req, size_t count, - struct fuse_forget_data* forgets) -{ - void* userdata = fuse_req_userdata(req); - [[maybe_unused]] auto* impl = static_cast*>(userdata); - - impl->derived_this()->async_forget_multiple_inodes(req, batt::as_slice(forgets, count), - impl->make_no_arg_handler(req)); -} - -/** - * Acquire, modify or release a BSD file lock - * - * Note: if the locking methods are not implemented, the kernel - * will still allow file locking to work locally. Hence these are - * only interesting for network filesystems and similar. - * - * Valid replies: - * fuse_reply_err - * - * @param req request handle - * @param ino the inode number - * @param fi file information - * @param op the locking operation, see flock(2) - */ // 40/44 -template -/*static*/ inline void FuseImpl::op_flock_impl(fuse_req_t req, fuse_ino_t ino, - struct fuse_file_info* fi, int op) -{ - void* userdata = fuse_req_userdata(req); - [[maybe_unused]] auto* impl = static_cast*>(userdata); - - (void)ino; - (void)fi; - (void)op; - - LLFS_LOG_WARNING() << "Not Implemented: " << BATT_THIS_FUNCTION; - // TODO [tastolfi 2023-06-28] unpack what kind of request this is and call the appropriate method. - - fuse_reply_err(req, - FuseImplBase::errno_from_status(batt::Status{batt::StatusCode::kUnimplemented})); -} - -/** - * Allocate requested space. If this function returns success then - * subsequent writes to the specified range shall not fail due to the lack - * of free space on the file system storage media. - * - * If this request is answered with an error code of ENOSYS, this is - * treated as a permanent failure with error code EOPNOTSUPP, i.e. all - * future fallocate() requests will fail with EOPNOTSUPP without being - * send to the filesystem process. - * - * Valid replies: - * fuse_reply_err - * - * @param req request handle - * @param ino the inode number - * @param offset starting point for allocated region - * @param length size of allocated region - * @param mode determines the operation to be performed on the given range, - * see fallocate(2) - */ // 41/44 -template -/*static*/ inline void FuseImpl::op_fallocate_impl(fuse_req_t req, fuse_ino_t ino, - int mode, off_t offset, off_t length, - struct fuse_file_info* fi) -{ - void* userdata = fuse_req_userdata(req); - [[maybe_unused]] auto* impl = static_cast*>(userdata); - - impl->derived_this()->async_file_allocate(req, ino, mode, FileOffset{offset}, FileLength{length}, - fi, impl->make_error_handler(req)); -} - -/** - * Read directory with attributes - * - * Send a buffer filled using fuse_add_direntry_plus(), with size not - * exceeding the requested size. Send an empty buffer on end of - * stream. - * - * fi->fh will contain the value set by the opendir method, or - * will be undefined if the opendir method didn't set any value. - * - * In contrast to readdir() (which does not affect the lookup counts), - * the lookup count of every entry returned by readdirplus(), except "." - * and "..", is incremented by one. - * - * Valid replies: - * fuse_reply_buf - * fuse_reply_data - * fuse_reply_err - * - * @param req request handle - * @param ino the inode number - * @param size maximum number of bytes to send - * @param off offset to continue reading the directory stream - * @param fi file information - */ // 42/44 -template -/*static*/ inline void FuseImpl::op_readdirplus_impl(fuse_req_t req, fuse_ino_t ino, - size_t size, off_t off, - struct fuse_file_info* fi) -{ - void* userdata = fuse_req_userdata(req); - [[maybe_unused]] auto* impl = static_cast*>(userdata); - - BATT_CHECK_NOT_NULLPTR(fi); - - impl->derived_this()->async_readdirplus(req, ino, size, DirentOffset{off}, FuseFileHandle{fi->fh}, - impl->make_readdir_handler(req)); -} - -/** - * Copy a range of data from one file to another - * - * Performs an optimized copy between two file descriptors without the - * additional cost of transferring data through the FUSE kernel module - * to user space (glibc) and then back into the FUSE filesystem again. - * - * In case this method is not implemented, glibc falls back to reading - * data from the source and writing to the destination. Effectively - * doing an inefficient copy of the data. - * - * If this request is answered with an error code of ENOSYS, this is - * treated as a permanent failure with error code EOPNOTSUPP, i.e. all - * future copy_file_range() requests will fail with EOPNOTSUPP without - * being send to the filesystem process. - * - * Valid replies: - * fuse_reply_write - * fuse_reply_err - * - * @param req request handle - * @param ino_in the inode number or the source file - * @param off_in starting point from were the data should be read - * @param fi_in file information of the source file - * @param ino_out the inode number or the destination file - * @param off_out starting point where the data should be written - * @param fi_out file information of the destination file - * @param len maximum size of the data to copy - * @param flags passed along with the copy_file_range() syscall - */ // 43/44 -template -/*static*/ inline void FuseImpl::op_copy_file_range_impl( - fuse_req_t req, fuse_ino_t ino_in, off_t off_in, struct fuse_file_info* fi_in, - fuse_ino_t ino_out, off_t off_out, struct fuse_file_info* fi_out, size_t len, int flags) -{ - void* userdata = fuse_req_userdata(req); - [[maybe_unused]] auto* impl = static_cast*>(userdata); - - (void)ino_in; - (void)off_in; - (void)fi_in; - - (void)ino_out; - (void)off_out; - (void)fi_out; - - (void)len; - (void)flags; - - LLFS_LOG_WARNING() << "Not Implemented: " << BATT_THIS_FUNCTION; - // TODO [tastolfi 2023-06-28] - - fuse_reply_err(req, - FuseImplBase::errno_from_status(batt::Status{batt::StatusCode::kUnimplemented})); -} - -/** - * Find next data or hole after the specified offset - * - * If this request is answered with an error code of ENOSYS, this is - * treated as a permanent failure, i.e. all future lseek() requests will - * fail with the same error code without being send to the filesystem - * process. - * - * Valid replies: - * fuse_reply_lseek - * fuse_reply_err - * - * @param req request handle - * @param ino the inode number - * @param off offset to start search from - * @param whence either SEEK_DATA or SEEK_HOLE - * @param fi file information - */ // 44/44 -template -/*static*/ inline void FuseImpl::op_lseek_impl(fuse_req_t req, fuse_ino_t ino, off_t off, - int whence, struct fuse_file_info* fi) -{ - void* userdata = fuse_req_userdata(req); - [[maybe_unused]] auto* impl = static_cast*>(userdata); - - (void)ino; - (void)off; - (void)whence; - - BATT_CHECK_NOT_NULLPTR(fi); - - LLFS_LOG_WARNING() << "Not Implemented: " << BATT_THIS_FUNCTION; - // TODO [tastolfi 2023-06-28] - - fuse_reply_err(req, - FuseImplBase::errno_from_status(batt::Status{batt::StatusCode::kUnimplemented})); -} - -//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - - -template -/*static*/ inline const fuse_lowlevel_ops* FuseImpl::get_fuse_lowlevel_ops() -{ - static const fuse_lowlevel_ops ops_{ - .init = &Self::op_init_impl, // 1 - .destroy = &Self::op_destroy_impl, // 2 - .lookup = &Self::op_lookup_impl, // 3 - .forget = &Self::op_forget_impl, // 4 - .getattr = &Self::op_getattr_impl, // 5 - .setattr = &Self::op_setattr_impl, // 6 - .readlink = &Self::op_readlink_impl, // 7 - .mknod = &Self::op_mknod_impl, // 8 - .mkdir = &Self::op_mkdir_impl, // 9 - .unlink = &Self::op_unlink_impl, // 10 - .rmdir = &Self::op_rmdir_impl, // 11 - .symlink = &Self::op_symlink_impl, // 12 - .rename = &Self::op_rename_impl, // 13 - .link = &Self::op_link_impl, // 14 - .open = &Self::op_open_impl, // 15 - .read = &Self::op_read_impl, // 16 - .write = &Self::op_write_impl, // 17 - .flush = &Self::op_flush_impl, // 18 - .release = &Self::op_release_impl, // 19 - .fsync = &Self::op_fsync_impl, // 20 - .opendir = &Self::op_opendir_impl, // 21 - .readdir = &Self::op_readdir_impl, // 22 - .releasedir = &Self::op_releasedir_impl, // 23 - .fsyncdir = &Self::op_fsyncdir_impl, // 24 - .statfs = &Self::op_statfs_impl, // 25 - .setxattr = &Self::op_setxattr_impl, // 26 - .getxattr = &Self::op_getxattr_impl, // 27 - .listxattr = &Self::op_listxattr_impl, // 28 - .removexattr = &Self::op_removexattr_impl, // 29 - .access = &Self::op_access_impl, // 30 - .create = &Self::op_create_impl, // 31 - .getlk = &Self::op_getlk_impl, // 32 - .setlk = &Self::op_setlk_impl, // 33 - .bmap = &Self::op_bmap_impl, // 34 - .ioctl = &Self::op_ioctl_impl, // 35 - .poll = &Self::op_poll_impl, // 36 - .write_buf = &Self::op_write_buf_impl, // 37 - .retrieve_reply = &Self::op_retrieve_reply_impl, // 38 - .forget_multi = &Self::op_forget_multi_impl, // 39 - .flock = &Self::op_flock_impl, // 40 - .fallocate = &Self::op_fallocate_impl, // 41 - .readdirplus = &Self::op_readdirplus_impl, // 42 - .copy_file_range = &Self::op_copy_file_range_impl, // 43 - .lseek = &Self::op_lseek_impl, // 44 - }; - - return &ops_; -} - -} //namespace llfs - -#endif // LLFS_FUSE_IPP diff --git a/src/llfs/fuse.test.cpp b/src/llfs/fuse.test.cpp deleted file mode 100644 index fc2cb4c9..00000000 --- a/src/llfs/fuse.test.cpp +++ /dev/null @@ -1,26 +0,0 @@ -//#=##=##=#==#=#==#===#+==#+==========+==+=+=+=+=+=++=+++=+++++=-++++=-+++++++++++ -// -// Part of the LLFS Project, under Apache License v2.0. -// See https://www.apache.org/licenses/LICENSE-2.0 for license information. -// SPDX short identifier: Apache-2.0 -// -//+++++++++++-+-+--+----- --- -- - - - - - -#include -// -#include -#include - -#include -#include - -namespace { - -TEST(FuseTest, Test) -{ - const fuse_lowlevel_ops* ops = llfs::NullFuseImpl::get_fuse_lowlevel_ops(); - - ASSERT_NE(ops, nullptr); -} - -} // namespace diff --git a/src/llfs/lru_clock.cpp b/src/llfs/lru_clock.cpp deleted file mode 100644 index 930824f1..00000000 --- a/src/llfs/lru_clock.cpp +++ /dev/null @@ -1,178 +0,0 @@ -//#=##=##=#==#=#==#===#+==#+==========+==+=+=+=+=+=++=+++=+++++=-++++=-+++++++++++ -// -// Part of the LLFS Project, under Apache License v2.0. -// See https://www.apache.org/licenses/LICENSE-2.0 for license information. -// SPDX short identifier: Apache-2.0 -// -//+++++++++++-+-+--+----- --- -- - - - - - -#include -// - -namespace llfs { - -//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - -// -LRUClock::LocalCounter::LocalCounter() noexcept : value{0} -{ - LRUClock::instance().add_local_counter(*this); -} - -//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - -// -LRUClock::LocalCounter::~LocalCounter() noexcept -{ - LRUClock::instance().remove_local_counter(*this); -} - -//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - -// -/*static*/ auto LRUClock::instance() noexcept -> Self& -{ - // Leak instance_ to avoid shutdown destructor ordering issues. - // - static Self* instance_ = new Self; - - return *instance_; -} - -//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - -// -/*static*/ LRUClock::LocalCounter& LRUClock::thread_local_counter() noexcept -{ - thread_local LocalCounter counter_; - - return counter_; -} - -//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - -// -/*static*/ i64 LRUClock::read_local() noexcept -{ - return Self::thread_local_counter().value.load(); -} - -//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - -// -/*static*/ i64 LRUClock::advance_local() noexcept -{ - return Self::thread_local_counter().value.fetch_add(1); -} - -//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - -// -/*static*/ i64 LRUClock::read_global() noexcept -{ - return Self::instance().read_observed_count(); -} - -//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - -// -LRUClock::LRUClock() noexcept - : sync_thread_{[this] { - this->run(); - }} -{ - this->sync_thread_.detach(); -} - -//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - -// -void LRUClock::run() noexcept -{ - static_assert(kMinSyncDelayUsec <= kMaxSyncDelayUsec); - - std::random_device rand_dev; - std::default_random_engine rng(rand_dev()); - std::uniform_int_distribution pick_jitter{ - 0, - Self::kMaxSyncDelayUsec - Self::kMinSyncDelayUsec, - }; - - // Loop forever, waiting and synchronizing thread-local counters. - // - for (;;) { - // Pick a delay with random jitter. - // - const i64 delay_usec = Self::kMinSyncDelayUsec + pick_jitter(rng); - - // Wait... - // - std::this_thread::sleep_for(std::chrono::microseconds(delay_usec)); - - // Synchronize the thread-local counters; this will update this->observed_count_. - // - this->sync_local_counters(); - } -} - -//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - -// -void LRUClock::sync_local_counters() noexcept -{ - std::unique_lock lock{this->mutex_}; - - i64 max_value = this->observed_count_; - - // On the first pass, figure out the maximum counter value. - // - for (LocalCounter& counter : this->counter_list_) { - max_value = std::max(max_value, counter.value.load()); - } - - // Save the observed max counter value so that we continue to advance, even if all threads - // terminate. - // - this->observed_count_ = max_value; - - // On the second pass, use CAS to make sure that all local counters are at least at the - // `max_value` calculated above. - // - for (LocalCounter& counter : this->counter_list_) { - i64 observed = counter.value.load(); - while (observed < max_value) { - if (counter.value.compare_exchange_weak(observed, max_value)) { - break; - } - } - } - - // Done! -} - -//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - -// -void LRUClock::add_local_counter(LocalCounter& counter) noexcept -{ - std::unique_lock lock{this->mutex_}; - - // Initialize the local counter to the max observed global value. - // - counter.value.store(this->observed_count_); - - this->counter_list_.push_back(counter); -} - -//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - -// -void LRUClock::remove_local_counter(LocalCounter& counter) noexcept -{ - std::unique_lock lock{this->mutex_}; - - // Update the global max observed count (last reading from this local counter). - // - this->observed_count_ = std::max(this->observed_count_, counter.value.load()); - - this->counter_list_.erase(this->counter_list_.iterator_to(counter)); -} - -//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - -// -i64 LRUClock::read_observed_count() noexcept -{ - std::unique_lock lock{this->mutex_}; - - return this->observed_count_; -} - -} //namespace llfs diff --git a/src/llfs/lru_clock.hpp b/src/llfs/lru_clock.hpp deleted file mode 100644 index eff53b28..00000000 --- a/src/llfs/lru_clock.hpp +++ /dev/null @@ -1,157 +0,0 @@ -//#=##=##=#==#=#==#===#+==#+==========+==+=+=+=+=+=++=+++=+++++=-++++=-+++++++++++ -// -// Part of the LLFS Project, under Apache License v2.0. -// See https://www.apache.org/licenses/LICENSE-2.0 for license information. -// SPDX short identifier: Apache-2.0 -// -//+++++++++++-+-+--+----- --- -- - - - - - -#pragma once -#ifndef LLFS_LRU_CLOCK_HPP -#define LLFS_LRU_CLOCK_HPP - -#include -// - -#include -#include - -#include -#include -#include -#include -#include - -namespace llfs { - -/** \brief A fast (if slightly inaccurate) logical timestamp maintainer, suitable for comparing - * approximate last-time-of-usage for different objects. - * - * The LRUClock is comprised of two elements: - * - * 1. A (set of) thread-local monotonic event counters - * 2. A background task that periodically synchronizes the thread-local counters - * - * The thread-local event counters are atomic, but since they are almost always accessed from a - * single thread (and we use the weakest possible memory order/fencing), they cause minimal problems - * in terms of cache stalls and contention. Every time a thread reads its counter, it advances it - * by one. Periodically (every 0.5 to 1.5 ms by default, with pseudo-random jitter), the background - * task wakes up and cycles over all the counters, setting them each to the highest value found in - * any of the others (it must, in the worst case, cycle through all twice). - * - * Every time a thread wants to use the LRU clock for the first time, it must first acquire a global - * mutex lock in order to add its local counter to a linked-list. The background synchronization - * task must also grab this mutex to make sure there are no races on the list while it updates all - * the thread-local counters. When a thread exits, it acquires the mutex and removes its counter. - * - * Note: a regular thread never needs to block in order to simply acquire a logical timestamp; it - * only needs to do so when it starts or stops. - */ -class LRUClock -{ - public: - using Self = LRUClock; - - //----- --- -- - - - - - static constexpr i64 kMinSyncDelayUsec = 500; - static constexpr i64 kMaxSyncDelayUsec = 1500; - //----- --- -- - - - - - - class LocalCounter; - - /** \brief A linked-list node; this is the base type for LocalCounter. - */ - using LocalCounterHook = boost::intrusive::list_base_hook>; - - /** \brief A per-thread atomic counter. - */ - class LocalCounter : public LocalCounterHook - { - public: - explicit LocalCounter() noexcept; - - ~LocalCounter() noexcept; - - /** \brief The next unused counter value for the current thread. - */ - std::atomic value{0}; - }; - - /** \brief Alias for the counter linked-list collection type. - */ - using LocalCounterList = - boost::intrusive::list>; - - //+++++++++++-+-+--+----- --- -- - - - - - - /** \brief Returns a reference to the global instance of the LRUClock. - */ - static Self& instance() noexcept; - - /** \brief Returns a reference to the current thread's counter object. - */ - static LocalCounter& thread_local_counter() noexcept; - - //+++++++++++-+-+--+----- --- -- - - - - - - /** \brief Returns the current thread's local counter. - */ - static i64 read_local() noexcept; - - /** \brief Increments the current thread's local counter, returning the old value. - */ - static i64 advance_local() noexcept; - - /** \brief Returns the last observed maximum count (over all thread-local values); this may be - * slightly out of date, as it is only updated by the background sync thread. - */ - static i64 read_global() noexcept; - - //+++++++++++-+-+--+----- --- -- - - - - - private: - LRUClock() noexcept; - - //+++++++++++-+-+--+----- --- -- - - - - - - /** \brief The background counter sync thread entry point. - */ - void run() noexcept; - - /** \brief Locks the counter list mutex, then iterates through all thread-local counters, - * atomically updating each to be at least the maximum observed value. - * - * This takes two passes through the list, so it's not 100% guaranteed that all counters will be - * the same by the end. - */ - void sync_local_counters() noexcept; - - /** \brief Adds the passed LocalCounter to the global list. - */ - void add_local_counter(LocalCounter& counter) noexcept; - - /** \brief Removes the passed LocalCounter from the global list. - */ - void remove_local_counter(LocalCounter& counter) noexcept; - - /** \brief Returns the maximum count value (least upper bound; i.e., the first unused value) from - * the last time sync_local_counters() was called. - */ - i64 read_observed_count() noexcept; - - //+++++++++++-+-+--+----- --- -- - - - - - - std::mutex mutex_; - LocalCounterList counter_list_; - std::thread sync_thread_; - - // Keeps track of the synchronized counter value as it advances, so we don't go backwards if all - // the threads go away temporarily at some point. - // - i64 observed_count_ = 0; -}; - -//#=##=##=#==#=#==#===#+==#+==========+==+=+=+=+=+=++=+++=+++++=-++++=-+++++++++++ - -} //namespace llfs - -#endif // LLFS_LRU_CLOCK_HPP diff --git a/src/llfs/lru_clock.test.cpp b/src/llfs/lru_clock.test.cpp deleted file mode 100644 index bcfca736..00000000 --- a/src/llfs/lru_clock.test.cpp +++ /dev/null @@ -1,230 +0,0 @@ -//#=##=##=#==#=#==#===#+==#+==========+==+=+=+=+=+=++=+++=+++++=-++++=-+++++++++++ -// -// Part of the LLFS Project, under Apache License v2.0. -// See https://www.apache.org/licenses/LICENSE-2.0 for license information. -// SPDX short identifier: Apache-2.0 -// -//+++++++++++-+-+--+----- --- -- - - - - - -#include -// -#include - -#include - -#include -#include - -#include - -namespace { - -// Test Goals: -// - local counts are independent on different threads -// - local counts are monotonic on a given thread -// - llfs::LRUClock::kMaxSyncDelayUsec is an upper bound on the time two threads' counters can be -// out of sync -// -// Test Plan: -// 1. start N threads, update counts on each -// - maintain list of which count values were seen on each thread -// - verify local monotonicity, global independence -// 2. same as (1), but add at least one "slow" thread; verify that it jumps ahead after sleeping -// for the max sync delay. -// -// - -using namespace llfs::int_types; - -//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - -// 1. -// -TEST(LruClockTest, PerThreadUpdate) -{ - const usize kNumThreads = std::thread::hardware_concurrency(); - const usize kUpdatesPerThread = 25 * 1000; - - std::vector> per_thread_values(kNumThreads, std::vector(kUpdatesPerThread)); - - std::atomic start{false}; - std::vector threads; - - for (usize i = 0; i < kNumThreads; ++i) { - threads.emplace_back([i, &start, &per_thread_values] { - while (!start.load()) { - continue; - } - for (usize j = 0; j < kUpdatesPerThread; ++j) { - const i64 value = llfs::LRUClock::advance_local(); - per_thread_values[i][j] = value; - } - }); - } - - start.store(true); - - for (std::thread& t : threads) { - t.join(); - } - - std::map count_per_value; - - for (const std::vector& values : per_thread_values) { - ASSERT_EQ(values.size(), kUpdatesPerThread); - ++count_per_value[values[0]]; - for (usize i = 1; i < kUpdatesPerThread; ++i) { - EXPECT_LT(values[i - 1], values[i]); - ++count_per_value[values[i]]; - } - } - - if (kNumThreads > 1) { - usize repeated_values = 0; - for (const auto& [value, count] : count_per_value) { - if (count > 1) { - ++repeated_values; - } - } - - EXPECT_GT(repeated_values, 0u); - } -} - -//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - -// 2. -// -void run_sync_update_test(const usize kNumFastThreads) -{ - const usize kUpdatesPerThread = 50 * 1000 * 1000; - - // The maximum number of consecutive attempts on the slow thread to read an increasing value. - // - const usize kSyncDelayToleranceFactor = 100; - - // The "slot thread" sleeps for the maximum sync delay times the tolerance factor, then takes a - // reading from its local counter. All observed local counts are recorded for verification below. - // - std::atomic stop_slow_thread{false}; - std::vector slow_thread_values; - std::thread slow_thread{[&slow_thread_values, &stop_slow_thread] { - while (!stop_slow_thread.load()) { - const i64 prev_value = slow_thread_values.empty() ? -1 : slow_thread_values.back(); - slow_thread_values.emplace_back(); - - for (usize j = 0; j < kSyncDelayToleranceFactor; ++j) { - std::this_thread::sleep_for(std::chrono::microseconds(llfs::LRUClock::kMaxSyncDelayUsec)); - - // NOTE: we are only calling `read_local()` here, not `advance_local()`. That means unless - // the other (fast) threads are updating the counter via periodic global synchronization, - // this value will not change! - // - slow_thread_values.back() = llfs::LRUClock::read_local(); - if (slow_thread_values.back() > prev_value || stop_slow_thread.load()) { - break; - } - } - } - }}; - - // The "fast threads" just advance their local counters as fast as possible. For each fast - // thread, we keep track of the maximum observed count value, which should eventually skip ahead - // due to global sync operations. - // - std::vector fast_threads; - std::vector> max_fast_thread_value(kNumFastThreads); - - for (usize i = 0; i < kNumFastThreads; ++i) { - fast_threads.emplace_back([i, &max_fast_thread_value] { - i64 last_value = -1; - for (usize j = 0; j < kUpdatesPerThread; ++j) { - const i64 value = llfs::LRUClock::advance_local(); - EXPECT_GT(value, last_value); - *max_fast_thread_value[i] = std::max(*max_fast_thread_value[i], value); - } - }); - } - - // Wait for all threads to finish. - // - for (std::thread& t : fast_threads) { - t.join(); - } - - stop_slow_thread.store(true); - slow_thread.join(); - - // Calculate the maximum count value observed from any of the fast threads. Since all threads are - // known to have finished, this value will not change. - // - i64 max_count = 0; - for (batt::CpuCacheLineIsolated& count : max_fast_thread_value) { - max_count = std::max(max_count, *count); - } - - const i64 max_synced_count = llfs::LRUClock::read_global(); - EXPECT_EQ(max_synced_count, max_count + 1); - - // Verify the required properties of the slow thread's count observations... - // - { - i64 prev_value = -1; - - for (usize i = 0; i < slow_thread_values.size(); ++i) { - // Once we observe the maximum count, all future observations should be the same (since the - // slow thread does not do any updating on its own). - // - if (slow_thread_values[i] >= max_synced_count) { - for (; i < slow_thread_values.size(); ++i) { - EXPECT_EQ(slow_thread_values[i], max_synced_count); - } - break; - } - - // All other values should be strictly increasing. - // - EXPECT_GT(slow_thread_values[i], prev_value) - << BATT_INSPECT(i) << BATT_INSPECT(slow_thread_values[i]) - << BATT_INSPECT(slow_thread_values[i - 1]) << BATT_INSPECT(max_synced_count) - << BATT_INSPECT(max_count) << BATT_INSPECT_RANGE(slow_thread_values); - - prev_value = slow_thread_values[i]; - } - } - - EXPECT_GT(slow_thread_values.back(), (kUpdatesPerThread * 95) / 100); -} - -TEST(LruClockTest, SyncUpdate1) -{ - run_sync_update_test(1); -} -TEST(LruClockTest, SyncUpdate2) -{ - run_sync_update_test(2); -} -TEST(LruClockTest, SyncUpdate4) -{ - run_sync_update_test(4); -} -TEST(LruClockTest, SyncUpdate8) -{ - run_sync_update_test(8); -} -TEST(LruClockTest, SyncUpdate16) -{ - run_sync_update_test(16); -} -TEST(LruClockTest, SyncUpdate32) -{ - run_sync_update_test(32); -} -TEST(LruClockTest, SyncUpdate64) -{ - run_sync_update_test(64); -} -TEST(LruClockTest, SyncUpdate128) -{ - run_sync_update_test(128); -} - -} // namespace diff --git a/src/llfs/mem_fuse.cpp b/src/llfs/mem_fuse.cpp deleted file mode 100644 index 6a412892..00000000 --- a/src/llfs/mem_fuse.cpp +++ /dev/null @@ -1,159 +0,0 @@ -//#=##=##=#==#=#==#===#+==#+==========+==+=+=+=+=+=++=+++=+++++=-++++=-+++++++++++ -// -// Part of the LLFS Project, under Apache License v2.0. -// See https://www.apache.org/licenses/LICENSE-2.0 for license information. -// SPDX short identifier: Apache-2.0 -// -//+++++++++++-+-+--+----- --- -- - - - - - -#include - -namespace llfs { - -//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - -// -fuse_ino_t MemoryFuseImpl::allocate_ino_int() -{ - return this->next_unused_ino_.fetch_add(1); -} - -//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - -// -FuseFileHandle MemoryFuseImpl::allocate_fh_int() -{ - { - auto locked = this->state_.lock(); - - if (!locked->available_fhs_.empty()) { - const FuseFileHandle fh = locked->available_fhs_.back(); - locked->available_fhs_.pop_back(); - return fh; - } - } - return FuseFileHandle{this->next_unused_fh_int_.fetch_add(1)}; -} - -//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - -// -batt::StatusOr> MemoryFuseImpl::find_inode(fuse_ino_t ino) -{ - auto locked = this->state_.lock(); - - auto iter = locked->inodes_.find(ino); - if (iter == locked->inodes_.end()) { - LLFS_VLOG(1) << "Bad ino: " << ino; - return {batt::status_from_errno(ENOENT)}; - } - return {iter->second}; -} - -//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - -// -batt::StatusOr> MemoryFuseImpl::find_file_handle(FuseFileHandle fh) -{ - auto locked = this->state_.lock(); - - auto iter = locked->file_handles_.find(fh); - if (iter == locked->file_handles_.end()) { - LLFS_VLOG(1) << "Bad fh: " << fh << BATT_INSPECT_RANGE(locked->file_handles_); - return {batt::status_from_errno(EINVAL)}; - } - return {iter->second}; -} - -//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - -// -batt::StatusOr> MemoryFuseImpl::create_inode_impl(fuse_ino_t parent, - const std::string& name, - mode_t mode, - MemInode::IsDir is_dir) -{ - BATT_ASSIGN_OK_RESULT(batt::SharedPtr parent_inode, this->find_inode(parent)); - - const auto category = [&] { - if (is_dir) { - return MemInode::Category::kDirectory; - } - return static_cast(mode & S_IFMT); - }(); - - const fuse_ino_t new_ino = this->allocate_ino_int(); - auto new_inode = batt::make_shared(new_ino, // - category, // - (mode & (S_IRWXU | S_IRWXG | S_IRWXO))); - - BATT_REQUIRE_OK(parent_inode->add_child(name, batt::make_copy(new_inode))); - - { - auto locked = this->state_.lock(); - locked->inodes_.emplace(new_ino, new_inode); - } - - new_inode->add_lookup(1); - - return {std::move(new_inode)}; -} - -//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - -// -batt::Status MemoryFuseImpl::close_impl(FuseFileHandle fh) -{ - BATT_ASSIGN_OK_RESULT(batt::SharedPtr file_handle, this->find_file_handle(fh)); - - { - auto locked = this->state_.lock(); - - locked->file_handles_.erase(fh); - locked->available_fhs_.emplace_back(fh); - - LLFS_VLOG(1) << "close_impl(" << fh << ")" << BATT_INSPECT_RANGE(locked->available_fhs_); - } - - return batt::OkStatus(); -} - -//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - -// -auto MemoryFuseImpl::readdir_impl(fuse_req_t req, fuse_ino_t ino, size_t size, DirentOffset offset, - FuseFileHandle fh, PlusApi plus_api) - -> batt::StatusOr -{ - BATT_ASSIGN_OK_RESULT(batt::SharedPtr inode, this->find_inode(ino)); - BATT_ASSIGN_OK_RESULT(batt::SharedPtr dh, this->find_file_handle(fh)); - - return inode->readdir(req, *dh, size, offset, plus_api); -} - -//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - -// -batt::Status MemoryFuseImpl::unlink_impl(fuse_req_t req, fuse_ino_t parent, const std::string& name, - MemInode::IsDir is_dir) -{ - using ResultPair = std::pair>; - - BATT_ASSIGN_OK_RESULT(batt::SharedPtr parent_inode, this->find_inode(parent)); - BATT_ASSIGN_OK_RESULT(ResultPair child_inode, - parent_inode->remove_child(name, is_dir, MemInode::RequireEmpty{is_dir})); - - if (child_inode.first) { - auto locked = this->state_.lock(); - - locked->inodes_.erase(child_inode.second->get_ino()); - } - - return batt::OkStatus(); -} - -//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - -// -batt::StatusOr MemoryFuseImpl::write_impl( - fuse_req_t req, fuse_ino_t ino, const batt::Slice& buffers, - FileOffset offset, FuseFileHandle fh) -{ - BATT_ASSIGN_OK_RESULT(batt::SharedPtr inode, this->find_inode(ino)); - BATT_ASSIGN_OK_RESULT(batt::SharedPtr dh, this->find_file_handle(fh)); - - return {inode->write(offset, buffers)}; -} - -} //namespace llfs diff --git a/src/llfs/mem_fuse.hpp b/src/llfs/mem_fuse.hpp deleted file mode 100644 index e5787a65..00000000 --- a/src/llfs/mem_fuse.hpp +++ /dev/null @@ -1,660 +0,0 @@ -//#=##=##=#==#=#==#===#+==#+==========+==+=+=+=+=+=++=+++=+++++=-++++=-+++++++++++ -// -// Part of the LLFS Project, under Apache License v2.0. -// See https://www.apache.org/licenses/LICENSE-2.0 for license information. -// SPDX short identifier: Apache-2.0 -// -//+++++++++++-+-+--+----- --- -- - - - - - -#pragma once -#ifndef LLFS_MEM_FUSE_HPP -#define LLFS_MEM_FUSE_HPP - -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include -#include -#include - -namespace llfs { - -BATT_SUPPRESS_IF_GCC("-Wunused-parameter") - -/** \brief A minimal example of a FuseImpl class. - */ -class MemoryFuseImpl : public WorkerTaskFuseImpl -{ - public: - //+++++++++++-+-+--+----- --- -- - - - - - - std::atomic next_unused_ino_{FUSE_ROOT_ID + 1}; - std::atomic next_unused_fh_int_{3}; - - struct State { - std::unordered_map> inodes_; - - std::unordered_map, std::hash> - file_handles_; - - std::vector available_fhs_; - }; - - batt::Mutex state_; - - //+++++++++++-+-+--+----- --- -- - - - - - - explicit MemoryFuseImpl(std::shared_ptr&& work_queue) noexcept - : WorkerTaskFuseImpl{std::move(work_queue)} - { - auto locked = this->state_.lock(); - - locked->inodes_.emplace( - FUSE_ROOT_ID, - batt::make_shared(FUSE_ROOT_ID, MemInode::Category::kDirectory, /*mode=*/0755)); - } - - //+++++++++++-+-+--+----- --- -- - - - - - - /** \brief - */ // 1/44 - void init() - { - BATT_CHECK_NOT_NULLPTR(this->conn_); - } - - /** \brief - */ // 2/44 - void destroy() - { - BATT_CHECK_NOT_NULLPTR(this->conn_); - } - - /** \brief - */ // 3/44 - batt::StatusOr lookup(fuse_req_t req, fuse_ino_t parent, - const std::string& name) - { - LLFS_VLOG(1) << "MemoryFuseImpl::" << __FUNCTION__ // - << "(" << BATT_INSPECT(req) // - << "," << BATT_INSPECT(parent) // - << "," << BATT_INSPECT_STR(name) // - << " )"; - - BATT_ASSIGN_OK_RESULT(batt::SharedPtr parent_inode, // - this->find_inode(parent)); - - return parent_inode->lookup_child(name); - } - - /** \brief - */ // 4/44 - void forget_inode(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup) - { - LLFS_VLOG(1) << "MemoryFuseImpl::" << __FUNCTION__ // - << "(" << BATT_INSPECT(req) // - << "," << BATT_INSPECT(ino) // - << "," << BATT_INSPECT(nlookup) // - << " )"; - - batt::StatusOr> inode = this->find_inode(ino); - if (!inode.ok()) { - LLFS_LOG_ERROR() << "Inode not found! " << BATT_INSPECT(ino) << BATT_INSPECT(inode.status()); - return; - } - - // TODO [tastolfi 2023-07-11] Do something with the result here. - // - [[maybe_unused]] const MemInode::IsDead is_dead = (*inode)->forget(nlookup); - } - - /** \brief - */ // 5/44 - batt::StatusOr get_attributes(fuse_req_t req, fuse_ino_t ino) - { - LLFS_VLOG(1) << "MemoryFuseImpl::" << __FUNCTION__ // - << "(" << BATT_INSPECT(req) // - << "," << BATT_INSPECT(ino) // - << " )"; - - BATT_ASSIGN_OK_RESULT(batt::SharedPtr inode, this->find_inode(ino)); - - return inode->get_attributes(); - } - - /** \brief - */ // 6/44 - batt::StatusOr set_attributes(fuse_req_t req, fuse_ino_t ino, - const struct stat* attr, int to_set, - batt::Optional fh) - { - LLFS_VLOG(1) << "MemoryFuseImpl::" << __FUNCTION__ // - << "(" << BATT_INSPECT(req) // - << "," << BATT_INSPECT(ino) // - << "," << BATT_INSPECT(DumpStat{*attr}) // - << "," << BATT_INSPECT(std::bitset<17>{(u32)to_set}) // - << "," << BATT_INSPECT(fh) // - << " )"; - /* - * If the setattr was invoked from the ftruncate() system call - * under Linux kernel versions 2.6.15 or later, the fi->fh will - * contain the value set by the open method or will be undefined - * if the open method didn't set any value. Otherwise (not - * ftruncate call, or kernel version earlier than 2.6.15) the fi - * parameter will be NULL. - */ - - BATT_ASSIGN_OK_RESULT(batt::SharedPtr inode, this->find_inode(ino)); - - return inode->set_attributes(attr, to_set); - } - - /** \brief - */ // 7/44 - const char* readlink(fuse_req_t req, fuse_ino_t ino) - { - LLFS_LOG_ERROR() << "MemoryFuseImpl::" << __FUNCTION__ // - << "(" // - << " )" - << " NOT IMPLEMENTED"; - - return ""; - } - - /** \brief - */ // 8/44 - batt::StatusOr make_node(fuse_req_t req, fuse_ino_t parent, - const std::string& name, mode_t mode, - dev_t rdev) - { - LLFS_LOG_WARNING() << "MemoryFuseImpl::" << __FUNCTION__ // - << "(" // - << " )" - << " NOT IMPLEMENTED"; - - return {batt::StatusCode::kUnimplemented}; - } - - /** \brief - */ // 9/44 - batt::StatusOr make_directory(fuse_req_t req, fuse_ino_t parent, - const std::string& name, mode_t mode) - { - LLFS_VLOG(1) << "MemoryFuseImpl::" << __FUNCTION__ // - << "(" << BATT_INSPECT(req) // - << "," << BATT_INSPECT(parent) // - << "," << BATT_INSPECT_STR(name) // - << "," << BATT_INSPECT(DumpFileMode{mode}) // - << " )"; - - BATT_ASSIGN_OK_RESULT(batt::SharedPtr new_inode, - this->create_inode_impl(parent, name, mode, MemInode::IsDir{true})); - - return {new_inode->get_fuse_entry_param()}; - } - - /** \brief - */ // 10/44 - batt::Status unlink(fuse_req_t req, fuse_ino_t parent, const std::string& name) - { - LLFS_VLOG(1) << "MemoryFuseImpl::" << __FUNCTION__ // - << "(" << BATT_INSPECT(req) // - << "," << BATT_INSPECT(parent) // - << "," << BATT_INSPECT_STR(name) // - << " )"; - ; - - return this->unlink_impl(req, parent, name, MemInode::IsDir{false}); - } - - /** \brief - */ // 11/44 - batt::Status remove_directory(fuse_req_t req, fuse_ino_t parent, const std::string& name) - { - LLFS_VLOG(1) << "MemoryFuseImpl::" << __FUNCTION__ // - << "(" << BATT_INSPECT(req) // - << "," << BATT_INSPECT(parent) // - << "," << BATT_INSPECT_STR(name) // - << " )"; - - return this->unlink_impl(req, parent, name, MemInode::IsDir{true}); - } - - /** \brief - */ // 12/44 - batt::StatusOr symbolic_link(fuse_req_t req, const std::string& link, - fuse_ino_t parent, const std::string& name) - { - LLFS_LOG_ERROR() << "MemoryFuseImpl::" << __FUNCTION__ // - << "(" // - << " )" - << " NOT IMPLEMENTED"; - - return {batt::StatusCode::kUnimplemented}; - } - - /** \brief - */ // 13/44 - batt::Status rename(fuse_req_t req, fuse_ino_t parent, const std::string& name, - fuse_ino_t newparent, const std::string& newname, unsigned int flags) - { - LLFS_LOG_ERROR() << "MemoryFuseImpl::" << __FUNCTION__ // - << "(" // - << " )" - << " NOT IMPLEMENTED"; - - return {batt::StatusCode::kUnimplemented}; - } - - /** \brief - */ // 14/44 - batt::StatusOr hard_link(fuse_req_t req, fuse_ino_t ino, - fuse_ino_t newparent, - const std::string& newname) - { - LLFS_LOG_ERROR() << "MemoryFuseImpl::" << __FUNCTION__ // - << "(" // - << " )" - << " NOT IMPLEMENTED"; - - return {batt::StatusCode::kUnimplemented}; - } - - /** \brief - */ // 15/44 - batt::StatusOr open(fuse_req_t req, fuse_ino_t ino, - const fuse_file_info& fi) - { - LLFS_VLOG(1) << "MemoryFuseImpl::" << __FUNCTION__ // - << "(" << BATT_INSPECT(req) // - << "," << BATT_INSPECT(ino) // - << "," << BATT_INSPECT(fi) // - << " )"; - - BATT_ASSIGN_OK_RESULT(batt::SharedPtr inode, this->find_inode(ino)); - BATT_ASSIGN_OK_RESULT(const fuse_file_info* ofi, - this->open_inode_impl(batt::make_copy(inode), fi)); - - inode->add_lookup(1); - - return {ofi}; - } - - /** \brief - */ // 16/44 - batt::StatusOr read(fuse_req_t req, fuse_ino_t ino, size_t size, - FileOffset offset, FuseFileHandle fh) - { - LLFS_VLOG(1) << "MemoryFuseImpl::" << __FUNCTION__ // - << "(" << BATT_INSPECT(req) // - << "," << BATT_INSPECT(ino) // - << "," << BATT_INSPECT(size) // - << "," << BATT_INSPECT(offset) // - << "," << BATT_INSPECT(fh) // - << " )"; - - BATT_ASSIGN_OK_RESULT(batt::SharedPtr inode, this->find_inode(ino)); - - return {FuseImplBase::FuseReadData{inode->read(offset, size)}}; - } - - /** \brief - */ // 17/44 - batt::StatusOr write(fuse_req_t req, fuse_ino_t ino, const batt::ConstBuffer& buffer, - FileOffset offset, FuseFileHandle fh) - { - LLFS_VLOG(1) << "MemoryFuseImpl::" << __FUNCTION__ // - << "(" << BATT_INSPECT(req) // - << "," << BATT_INSPECT(ino) // - << "," << BATT_INSPECT(buffer.size()) // - << "," << BATT_INSPECT(offset) // - << "," << BATT_INSPECT(fh) // - << " )"; - - return this->write_impl(req, ino, batt::as_slice(&buffer, 1), offset, fh); - } - - /** \brief - */ // 37/44 - batt::StatusOr write_buf(fuse_req_t req, fuse_ino_t ino, - const FuseImplBase::ConstBufferVec& bufv, FileOffset offset, - FuseFileHandle fh) - { - LLFS_VLOG(1) << "MemoryFuseImpl::" << __FUNCTION__ // - << "(" << BATT_INSPECT(req) // - << "," << BATT_INSPECT(ino) // - << "," << BATT_INSPECT(offset) // - << "," << BATT_INSPECT(fh) // - << " )"; - - return this->write_impl(req, ino, batt::as_slice(bufv), offset, fh); - } - - /** \brief - */ // 18/44 - batt::Status flush(fuse_req_t req, fuse_ino_t ino, FuseFileHandle fh) - { - LLFS_VLOG(1) << "MemoryFuseImpl::" << __FUNCTION__ // - << "(" << BATT_INSPECT(req) // - << "," << BATT_INSPECT(ino) // - << "," << BATT_INSPECT(fh) // - << " )"; - - return batt::OkStatus(); - } - - /** \brief - */ // 19/44 - batt::Status release(fuse_req_t req, fuse_ino_t ino, FuseFileHandle fh, FileOpenFlags flags) - { - LLFS_VLOG(1) << "MemoryFuseImpl::" << __FUNCTION__ // - << "(" << BATT_INSPECT(req) // - << "," << BATT_INSPECT(ino) // - << "," << BATT_INSPECT(fh) // - << "," << BATT_INSPECT(flags) // - << " )"; - - return this->close_impl(fh); - } - - /** \brief - */ // 20/44 - batt::Status fsync(fuse_req_t req, fuse_ino_t ino, IsDataSync datasync, FuseFileHandle fh) - { - LLFS_LOG_ERROR() << "MemoryFuseImpl::" << __FUNCTION__ // - << "(" // - << " )" - << " NOT IMPLEMENTED"; - - return {batt::StatusCode::kUnimplemented}; - } - - /** \brief - */ // 21/44 - batt::StatusOr opendir(fuse_req_t req, fuse_ino_t ino, - const fuse_file_info& fi) - { - LLFS_VLOG(1) << "MemoryFuseImpl::" << __FUNCTION__ // - << "(" << BATT_INSPECT(req) // - << "," << BATT_INSPECT(ino) // - << "," << BATT_INSPECT(fi) // - << " )"; - - BATT_ASSIGN_OK_RESULT(batt::SharedPtr inode, this->find_inode(ino)); - BATT_ASSIGN_OK_RESULT( - const fuse_file_info* ofi, - this->open_inode_impl(batt::make_copy(inode), fi, MemFileHandle::OpenDirState{})); - - inode->add_lookup(1); - - return {ofi}; - } - - /** \brief - */ // 22/44 - batt::StatusOr readdir(fuse_req_t req, fuse_ino_t ino, size_t size, - DirentOffset offset, FuseFileHandle fh) - { - LLFS_VLOG(1) << "MemoryFuseImpl::" << __FUNCTION__ // - << "(" << BATT_INSPECT(req) // - << "," << BATT_INSPECT(ino) // - << "," << BATT_INSPECT(size) // - << "," << BATT_INSPECT(offset) // - << "," << BATT_INSPECT(fh) // - << " )"; - - return this->readdir_impl(req, ino, size, offset, fh, PlusApi{false}); - } - - /** \brief - */ // 23/44 - batt::Status releasedir(fuse_req_t req, fuse_ino_t ino, FuseFileHandle fh) - { - LLFS_VLOG(1) << "MemoryFuseImpl::" << __FUNCTION__ // - << "(" << BATT_INSPECT(req) // - << "," << BATT_INSPECT(ino) // - << "," << BATT_INSPECT(fh) // - << " )"; - - return this->close_impl(fh); - } - - /** \brief - */ // 24/44 - batt::Status fsyncdir(fuse_req_t req, fuse_ino_t ino, IsDataSync datasync, FuseFileHandle fh) - { - LLFS_LOG_ERROR() << "MemoryFuseImpl::" << __FUNCTION__ // - << "(" // - << " )" - << " NOT IMPLEMENTED"; - - return {batt::StatusCode::kUnimplemented}; - } - - /** \brief - */ // 25/44 - batt::StatusOr statfs(fuse_req_t req, fuse_ino_t ino) - { - LLFS_LOG_ERROR() << "MemoryFuseImpl::" << __FUNCTION__ // - << "(" // - << " )" - << " NOT IMPLEMENTED"; - - return {batt::StatusCode::kUnimplemented}; - } - - /** \brief - */ // 26/44 - batt::Status set_extended_attribute(fuse_req_t req, fuse_ino_t ino, - const FuseImplBase::ExtendedAttribute& attr, int flags) - { - LLFS_LOG_ERROR() << "MemoryFuseImpl::" << __FUNCTION__ // - << "(" // - << " )" - << " NOT IMPLEMENTED"; - - return {batt::StatusCode::kUnimplemented}; - } - - /** \brief - */ // 27/44 - batt::StatusOr get_extended_attribute( - fuse_req_t req, fuse_ino_t ino, const std::string& name, size_t size) - { - LLFS_LOG_WARNING() << "MemoryFuseImpl::" << __FUNCTION__ // - << "(" // - << " )" - << " NOT IMPLEMENTED"; - - return {batt::StatusCode::kUnimplemented}; - } - - /** \brief - */ // 29/44 - batt::Status remove_extended_attribute(fuse_req_t req, fuse_ino_t ino, const std::string& name) - { - LLFS_LOG_ERROR() << "MemoryFuseImpl::" << __FUNCTION__ << "(" // - << " )" - << " NOT IMPLEMENTED"; - - return {batt::StatusCode::kUnimplemented}; - } - - /** \brief - */ // 30/44 - batt::Status check_access(fuse_req_t req, fuse_ino_t ino, int mask) - { - LLFS_VLOG(1) << "MemoryFuseImpl::" << __FUNCTION__ // - << "(" << BATT_INSPECT(req) // - << "," << BATT_INSPECT(ino) // - << "," << BATT_INSPECT(mask) // - << " )"; - - return batt::OkStatus(); - } - - /** \brief - */ // 31/44 - batt::StatusOr create(fuse_req_t req, fuse_ino_t parent, - const std::string& name, mode_t mode, - const fuse_file_info& fi) - { - LLFS_VLOG(1) << "MemoryFuseImpl::" << __FUNCTION__ // - << "(" << BATT_INSPECT(req) // - << "," << BATT_INSPECT(parent) // - << "," << BATT_INSPECT_STR(name) // - << "," << BATT_INSPECT(DumpFileMode{mode}) // - << "," << BATT_INSPECT(fi) // - << " )"; - - BATT_ASSIGN_OK_RESULT(batt::SharedPtr new_inode, - this->create_inode_impl(parent, name, mode, MemInode::IsDir{false})); - - BATT_ASSIGN_OK_RESULT(const fuse_file_info* opened_file_info, - this->open_inode_impl(batt::make_copy(new_inode), fi)); - - return FuseImplBase::FuseCreateReply{ - .entry = new_inode->get_fuse_entry_param(), - .fi = opened_file_info, - }; - } - - /** \brief - */ // 35/44 - batt::StatusOr ioctl(fuse_req_t req, fuse_ino_t ino, - unsigned int cmd, void* arg, - struct fuse_file_info* fi, unsigned flags, - const batt::ConstBuffer& in_buf, - size_t out_bufsz) - { - LLFS_LOG_ERROR() << BATT_THIS_FUNCTION // - << BATT_INSPECT(req) // - << BATT_INSPECT(ino) // - << BATT_INSPECT(cmd) // - << BATT_INSPECT(arg) // - << BATT_INSPECT(fi) // - << BATT_INSPECT(flags) // - << BATT_INSPECT(in_buf.size()) // - << BATT_INSPECT(out_bufsz); - - return {batt::StatusCode::kUnimplemented}; - } - - /** \brief - */ // 38/44 - void retrieve_reply(fuse_req_t req, void* cookie, fuse_ino_t ino, off_t offset, - struct fuse_bufvec* bufv) - { - LLFS_LOG_ERROR() << "MemoryFuseImpl::" << __FUNCTION__ << "(" // - << " )" - << " NOT IMPLEMENTED"; - } - - /** \brief - */ // 39/44 - void forget_multiple_inodes(fuse_req_t req, batt::Slice forgets) - { - LLFS_LOG_ERROR() << "MemoryFuseImpl::" << __FUNCTION__ << "(" // - << " )" - << " NOT IMPLEMENTED"; - } - - /** \brief - */ // 41/44 - batt::Status file_allocate(fuse_req_t req, fuse_ino_t ino, int mode, FileOffset offset, - FileLength length, fuse_file_info* fi) - { - LLFS_LOG_ERROR() << "MemoryFuseImpl::" << __FUNCTION__ << "(" // - << " )" - << " NOT IMPLEMENTED"; - - return {batt::StatusCode::kUnimplemented}; - } - - /** \brief - */ // 42/44 - batt::StatusOr readdirplus(fuse_req_t req, fuse_ino_t ino, - size_t size, DirentOffset offset, - FuseFileHandle fh) - { - LLFS_VLOG(1) << "MemoryFuseImpl::" << __FUNCTION__ // - << "(" << BATT_INSPECT(req) // - << "," << BATT_INSPECT(ino) // - << "," << BATT_INSPECT(size) // - << "," << BATT_INSPECT(offset) // - << "," << BATT_INSPECT(fh) // - << " )"; - - return this->readdir_impl(req, ino, size, offset, fh, PlusApi{true}); - } - - private: - //+++++++++++-+-+--+----- --- -- - - - - - - /** \brief Returns an unused inode (ino) integer. - */ - fuse_ino_t allocate_ino_int(); - - /** \brief Returns an unused file handle integer. - */ - FuseFileHandle allocate_fh_int(); - - /** \brief - */ - batt::StatusOr> find_inode(fuse_ino_t ino); - - /** \brief - */ - batt::StatusOr> find_file_handle(FuseFileHandle fh); - - /** \brief - */ - batt::StatusOr> create_inode_impl(fuse_ino_t parent, - const std::string& name, mode_t mode, - MemInode::IsDir is_dir); - - /** \brief - */ - template - batt::StatusOr open_inode_impl(batt::SharedPtr&& inode, - const fuse_file_info& fi, - OpenStateArgs&&... open_state_args); - - /** \brief - */ - batt::Status close_impl(FuseFileHandle fh); - - /** \brief - */ - batt::StatusOr readdir_impl(fuse_req_t req, fuse_ino_t ino, size_t size, - DirentOffset offset, FuseFileHandle fh, - PlusApi plus_api); - - /** \brief - */ - batt::Status unlink_impl(fuse_req_t req, fuse_ino_t parent, const std::string& name, - MemInode::IsDir is_dir); - - /** \brief - */ - batt::StatusOr write_impl(fuse_req_t req, fuse_ino_t ino, - const batt::Slice& buffers, - FileOffset offset, FuseFileHandle fh); - -}; // class MemoryFuseImpl - -//#=##=##=#==#=#==#===#+==#+==========+==+=+=+=+=+=++=+++=+++++=-++++=-+++++++++++ - -} //namespace llfs - -#include - -#endif // LLFS_MEM_FUSE_HPP diff --git a/src/llfs/mem_fuse.ipp b/src/llfs/mem_fuse.ipp deleted file mode 100644 index 79506d53..00000000 --- a/src/llfs/mem_fuse.ipp +++ /dev/null @@ -1,41 +0,0 @@ -//#=##=##=#==#=#==#===#+==#+==========+==+=+=+=+=+=++=+++=+++++=-++++=-+++++++++++ -// -// Part of the LLFS Project, under Apache License v2.0. -// See https://www.apache.org/licenses/LICENSE-2.0 for license information. -// SPDX short identifier: Apache-2.0 -// -//+++++++++++-+-+--+----- --- -- - - - - - -#pragma once -#ifndef LLFS_MEM_FUSE_IPP -#define LLFS_MEM_FUSE_IPP - -namespace llfs { - -//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - -// -template -inline batt::StatusOr MemoryFuseImpl::open_inode_impl( - batt::SharedPtr&& inode, const fuse_file_info& fi, OpenStateArgs&&... open_state_args) -{ - BATT_CHECK_NOT_NULLPTR(inode); - - const u64 fh = this->allocate_fh_int(); - LLFS_VLOG(1) << "open_inode_impl, " << BATT_INSPECT(fh); - - { - auto locked = this->state_.lock(); - - auto [fh_iter, inserted] = locked->file_handles_.emplace( - fh, batt::make_shared(fh, std::move(inode), fi, - BATT_FORWARD(open_state_args)...)); - - BATT_CHECK(inserted); - - return &fh_iter->second->info; - } -} - -} //namespace llfs - -#endif // LLFS_MEM_FUSE_IPP diff --git a/src/llfs/mem_fuse.test.cpp b/src/llfs/mem_fuse.test.cpp deleted file mode 100644 index 6b899491..00000000 --- a/src/llfs/mem_fuse.test.cpp +++ /dev/null @@ -1,318 +0,0 @@ -//#=##=##=#==#=#==#===#+==#+==========+==+=+=+=+=+=++=+++=+++++=-++++=-+++++++++++ -// -// Part of the LLFS Project, under Apache License v2.0. -// See https://www.apache.org/licenses/LICENSE-2.0 for license information. -// SPDX short identifier: Apache-2.0 -// -//+++++++++++-+-+--+----- --- -- - - - - - -#include -// -#include - -#include -#include - -#include -#include -#include - -#include - -#include - -#include - -#include -#include -#include -#include -#include - -#include - -namespace { - -using namespace llfs::int_types; - -//=#=#==#==#===============+=+=+=+=++=++++++++++++++-++-+--+-+----+--------------- -// -class MemFuseTest : public ::testing::Test -{ - public: - void SetUp() override - { - // Enable task dumping signal handler. - // - batt::enable_dump_tasks(); - - // Initialize the work queue used by MemFuseImpl. - // - this->work_queue_ = std::make_shared(); - - // Start a single thread to pull work from the queue. - // - this->worker_task_thread_.emplace([this] { - boost::asio::io_context io; - - llfs::WorkerTask task{batt::make_copy(this->work_queue_), io.get_executor()}; - - io.run(); - }); - - const auto try_umount = [&] { - LLFS_LOG_INFO() << "Attempting to umount " << this->mountpoint_; - int retval = umount2(this->mountpoint_str_.c_str(), MNT_FORCE); - LLFS_LOG_INFO() << BATT_INSPECT(batt::status_from_retval(retval)); - }; - - // Create a fresh mount point directory. - // - for (int retry = 0; retry < 2; ++retry) { - std::error_code ec; - bool mountpoint_exists = std::filesystem::exists(this->mountpoint_, ec); - if (ec && retry == 0) { - try_umount(); - continue; - } - - if (mountpoint_exists) { - std::filesystem::remove_all(this->mountpoint_, ec); - ASSERT_FALSE(ec) << "Failed to remove mountpoint"; - } - - std::filesystem::create_directories(this->mountpoint_, ec); - if (ec && retry == 0) { - try_umount(); - continue; - } - ASSERT_FALSE(ec) << "Failed to initialize mountpoint:" - << BATT_INSPECT_STR(this->mountpoint_.string()) << BATT_INSPECT(ec.value()) - << BATT_INSPECT(ec.message()); - } - - // Start FUSE session on a background thread. - // - { - BATT_CHECK_NOT_NULLPTR(this->work_queue_); - - batt::StatusOr status_or_session = llfs::FuseSession::from_args( - this->argc_, this->argv_.data(), batt::StaticType{}, - batt::make_copy(this->work_queue_)); - - ASSERT_TRUE(status_or_session.ok()) << BATT_INSPECT(status_or_session.status()); - - BATT_CHECK_EQ(this->fuse_session_, batt::None); - this->fuse_session_ = std::move(*status_or_session); - } - BATT_CHECK_EQ(this->fuse_session_thread_, batt::None); - - this->fuse_session_thread_.emplace([this] { - BATT_CHECK_NE(this->fuse_session_, batt::None); - this->fuse_session_->run(); - }); - } - - void TearDown() override - { - if (this->work_queue_) { - LLFS_LOG_INFO() << "Closing work queue"; - this->work_queue_->close(); - if (this->worker_task_thread_) { - LLFS_LOG_INFO() << "Joining worker task thread"; - this->worker_task_thread_->join(); - this->worker_task_thread_ = batt::None; - } - } else { - BATT_CHECK_EQ(this->worker_task_thread_, batt::None); - } - - if (this->fuse_session_) { - LLFS_LOG_INFO() << "Halting fuse session"; - this->fuse_session_->halt(); - if (this->fuse_session_thread_) { - LLFS_LOG_INFO() << "Joining fuse thread"; - this->fuse_session_thread_->join(); - this->fuse_session_thread_ = batt::None; - } - } - - this->work_queue_ = nullptr; - this->fuse_session_ = batt::None; - } - - void print_lstat() - { - struct stat st; - std::memset(&st, 0, sizeof(st)); - - int rt = lstat(".", &st); - - std::cout << std::endl << llfs::DumpStat{st} << BATT_INSPECT(rt) << std::endl << std::endl; - } - - batt::StatusOr> find_files() - { - std::vector files; - - std::error_code ec; - for (const std::filesystem::directory_entry& entry : - std::filesystem::recursive_directory_iterator(this->mountpoint_, ec)) { - files.push_back(entry); - } - - BATT_REQUIRE_OK(ec); - - std::sort(files.begin(), files.end(), - [](const std::filesystem::directory_entry& left, - const std::filesystem::directory_entry& right) { - return left.path().string() < right.path().string(); - }); - - return files; - } - - //+++++++++++-+-+--+----- --- -- - - - - - - std::shared_ptr work_queue_; - - batt::Optional worker_task_thread_; - - const std::filesystem::path mountpoint_{"/tmp/llfs_fuse_test"}; - - const std::string mountpoint_str_ = this->mountpoint_.string(); - - std::array argv_{ - "llfs_Test", - this->mountpoint_str_.c_str(), - }; - - const int argc_ = this->argv_.size(); - - batt::Optional fuse_session_; - - batt::Optional fuse_session_thread_; -}; - -//=#=#==#==#===============+=+=+=+=++=++++++++++++++-++-+--+-+----+--------------- -// -TEST_F(MemFuseTest, StartStop) -{ -} - -//=#=#==#==#===============+=+=+=+=++=++++++++++++++-++-+--+-+----+--------------- -// -TEST_F(MemFuseTest, CreateFile) -{ - const std::string_view data1 = "Some stuff."; - const std::string data2 = [] { - std::ifstream ifs{__FILE__}; - std::ostringstream oss; - oss << ifs.rdbuf(); - return oss.str(); - }(); - - ASSERT_GT(data2.size(), 4096u); - - // Initially there should be no files. - { - batt::StatusOr> files = this->find_files(); - - ASSERT_TRUE(files.ok()) << BATT_INSPECT(files.status()); - EXPECT_TRUE(files->empty()); - } - - // Create some files. - { - std::ofstream ofs{this->mountpoint_ / "file.txt"}; - ofs << data1; - - ASSERT_TRUE(ofs.good()); - } - { - batt::StatusOr fd = - llfs::create_file_read_write((this->mountpoint_ / "file2.txt").string()); - - ASSERT_TRUE(fd.ok()) << BATT_INSPECT(fd); - - auto on_scope_exit = batt::finally([&] { - llfs::close_fd(*fd).IgnoreError(); - }); - - batt::Status wr_status = llfs::write_fd(*fd, llfs::ConstBuffer{data2.data(), data2.size()}, 0); - - ASSERT_TRUE(wr_status.ok()) << BATT_INSPECT(wr_status); - } - - // Expect to find the file we created. - { - batt::StatusOr> files = this->find_files(); - - ASSERT_TRUE(files.ok()) << BATT_INSPECT(files.status()); - ASSERT_EQ(files->size(), 2u); - - EXPECT_TRUE((*files)[0].is_regular_file()); - EXPECT_EQ((*files)[0].path(), this->mountpoint_ / "file.txt"); - EXPECT_EQ((*files)[0].file_size(), data1.size()); - - EXPECT_TRUE((*files)[1].is_regular_file()); - EXPECT_EQ((*files)[1].path(), this->mountpoint_ / "file2.txt"); - EXPECT_EQ((*files)[1].file_size(), data2.size()); - } - - // Read the file we created above. - { - std::ifstream ifs{this->mountpoint_ / "file.txt"}; - - EXPECT_TRUE(ifs.good()); - - std::ostringstream oss; - oss << ifs.rdbuf(); - - EXPECT_THAT(oss.str(), ::testing::StrEq(data1)); - EXPECT_FALSE(oss.bad()); - EXPECT_FALSE(ifs.bad()); - } - - // Truncate the other file and read it. - { - // Verify the original contents. - { - std::ifstream ifs{this->mountpoint_ / "file2.txt"}; - std::ostringstream oss; - oss << ifs.rdbuf(); - - EXPECT_EQ(oss.str().size(), data2.size()); - EXPECT_THAT(oss.str(), ::testing::StrEq(data2)); - } - - const auto resize_and_verify = [&](u64 newsize, u64 expect_from_data2, u64 expect_zeros) { - BATT_CHECK_EQ(newsize, expect_from_data2 + expect_zeros) - << BATT_INSPECT(expect_from_data2) << BATT_INSPECT(expect_zeros); - - std::error_code ec; - std::filesystem::resize_file(this->mountpoint_ / "file2.txt", newsize, ec); - - EXPECT_FALSE(ec); - EXPECT_EQ(std::filesystem::file_size(this->mountpoint_ / "file2.txt", ec), newsize); - EXPECT_FALSE(ec); - - std::ifstream ifs{this->mountpoint_ / "file2.txt"}; - std::ostringstream oss; - oss << ifs.rdbuf(); - - EXPECT_EQ(oss.str().size(), newsize); - EXPECT_THAT(oss.str(), ::testing::StrEq(data2.substr(0, expect_from_data2) + - std::string(expect_zeros, '\0'))); - }; - - resize_and_verify(5555, 5555, 0); - resize_and_verify(5655, 5555, 100); - resize_and_verify(4096, 4096, 0); - resize_and_verify(4000, 4000, 0); - resize_and_verify(3900, 3900, 0); - resize_and_verify(3950, 3900, 50); - } -} - -} // namespace diff --git a/src/llfs/mem_inode.cpp b/src/llfs/mem_inode.cpp deleted file mode 100644 index a1b2f574..00000000 --- a/src/llfs/mem_inode.cpp +++ /dev/null @@ -1,452 +0,0 @@ -//#=##=##=#==#=#==#===#+==#+==========+==+=+=+=+=+=++=+++=+++++=-++++=-+++++++++++ -// -// Part of the LLFS Project, under Apache License v2.0. -// See https://www.apache.org/licenses/LICENSE-2.0 for license information. -// SPDX short identifier: Apache-2.0 -// -//+++++++++++-+-+--+----- --- -- - - - - - -#include -// - -namespace llfs { - -//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - -// -/*explicit*/ MemInode::MemInode(fuse_ino_t ino, Category category, int mode) noexcept - : state_{ino, category, mode} -{ - if (category == Category::kDirectory) { - this->init_directory(); - } -} - -//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - -// -MemInode::State::State(fuse_ino_t ino, Category category, int mode) noexcept -{ - (void)category; - std::memset(&this->entry_, 0, sizeof(this->entry_)); - this->entry_.ino = ino; - this->entry_.attr.st_ino = ino; - this->entry_.attr.st_mode = (mode_t)category | mode; - this->entry_.attr.st_uid = 1001; // TODO [tastolfi 2023-07-12] use arg - this->entry_.attr.st_gid = 1001; // TODO [tastolfi 2023-07-12] use arg - this->entry_.attr.st_blksize = 4096; - this->entry_.attr.st_blocks = 8; -} - -//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - -// -void MemInode::init_directory() -{ -} - -//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - -// -auto MemInode::State::find_child_by_name(const std::string& child_name) - -> batt::StatusOr> -{ - auto child_iter = this->children_by_name_.find(child_name); - if (child_iter == this->children_by_name_.end()) { - return {batt::status_from_errno(ENOENT)}; - } - return child_iter->second; -} - -//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - -// -auto MemInode::lookup_child(const std::string& name) -> batt::StatusOr -{ - auto locked = this->state_.lock(); - - BATT_ASSIGN_OK_RESULT(batt::SharedPtr child_inode, locked->find_child_by_name(name)); - - // Check to see whether the child inode is being deleted. - // - if (child_inode->is_dead()) { - return {batt::status_from_errno(ENOENT)}; - } - - // Increment the lookup count. - // - child_inode->add_lookup(1); - - return {&child_inode->state_.lock()->entry_}; -} - -//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - -// -auto MemInode::get_attributes() -> batt::StatusOr -{ - return {FuseImplBase::Attributes{ - .attr = &this->state_.lock()->entry_.attr, - .timeout_sec = 1.0, - }}; -} - -//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - -// -batt::StatusOr MemInode::set_attributes(const struct stat* attr, - int to_set) -{ - auto locked = this->state_.lock(); - - if ((to_set & FUSE_SET_ATTR_MODE) != 0) { - locked->entry_.attr.st_mode = attr->st_mode; - } - - if ((to_set & FUSE_SET_ATTR_UID) != 0) { - locked->entry_.attr.st_uid = attr->st_uid; - } - - if ((to_set & FUSE_SET_ATTR_GID) != 0) { - locked->entry_.attr.st_gid = attr->st_gid; - } - - if ((to_set & FUSE_SET_ATTR_SIZE) != 0) { - const u64 old_size = locked->entry_.attr.st_size; - const u64 new_size = attr->st_size; - const bool truncated = (new_size < old_size); - - locked->entry_.attr.st_size = new_size; - - if (truncated) { - auto iter = locked->data_blocks_.lower_bound(new_size); - - // Adjust iter, in case lower_bound overshot (we want the greatest lower bound). - // - if (iter != locked->data_blocks_.begin() && - (iter == locked->data_blocks_.end() || iter->first > new_size)) { - --iter; - } - - if (iter != locked->data_blocks_.end()) { - const u64 block_pos = iter->first; - if (iter->first < BATT_CHECKED_CAST(u64, attr->st_size)) { - // Clear out the truncated portion of the new last block. - // - const u64 block_offset = new_size - block_pos; - const usize n_to_clear = - std::min(iter->second->size() - block_offset, old_size - new_size); - - LLFS_VLOG(1) << BATT_INSPECT(block_offset) << BATT_INSPECT(n_to_clear) - << BATT_INSPECT(iter->second->size() - block_offset) - << BATT_INSPECT(old_size - new_size); - - std::memset(iter->second->data() + block_offset, 0, n_to_clear); - - // Move to iter to the next block before erasing. - // - ++iter; - } - locked->data_blocks_.erase(iter, locked->data_blocks_.end()); - } - } - } - - if ((to_set & FUSE_SET_ATTR_ATIME) != 0) { - locked->entry_.attr.st_atime = attr->st_atime; - } - - if ((to_set & FUSE_SET_ATTR_MTIME) != 0) { - locked->entry_.attr.st_mtime = attr->st_mtime; - } - - if ((to_set & FUSE_SET_ATTR_ATIME_NOW) != 0) { - BATT_REQUIRE_OK( - batt::status_from_retval(clock_gettime(CLOCK_REALTIME, &locked->entry_.attr.st_atim))); - } - - if ((to_set & FUSE_SET_ATTR_MTIME_NOW) != 0) { - BATT_REQUIRE_OK( - batt::status_from_retval(clock_gettime(CLOCK_REALTIME, &locked->entry_.attr.st_mtim))); - } - - if ((to_set & FUSE_SET_ATTR_CTIME) != 0) { - locked->entry_.attr.st_ctime = attr->st_ctime; - } - - return {FuseImplBase::Attributes{ - .attr = &locked->entry_.attr, - .timeout_sec = 1.0, - }}; -} - -//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - -// -auto MemInode::State::pack_as_fuse_dir_entry(fuse_req_t req, batt::ConstBuffer& out_buf, - batt::MutableBuffer& dst_buf, const std::string& name, - DirentOffset next_offset, PlusApi plus_api) const - -> batt::Status -{ - // TODO [tastolfi 2023-06-30] do we need to bump the inode refcount here? - // - usize size_needed = [&] { - LLFS_VLOG(1) << "DEBUG: readdir(): added to buffer: " << batt::c_str_literal(name) << ", ino " - << this->entry_.attr.st_ino << ", next_offset " << next_offset; - - if (plus_api) { - return fuse_add_direntry_plus(req, static_cast(dst_buf.data()), dst_buf.size(), - name.c_str(), &this->entry_, next_offset); - } else { - return fuse_add_direntry(req, static_cast(dst_buf.data()), dst_buf.size(), - name.c_str(), &this->entry_.attr, next_offset); - } - }(); - - if (size_needed > dst_buf.size()) { - return {batt::StatusCode::kResourceExhausted}; - } - - out_buf = batt::ConstBuffer{out_buf.data(), out_buf.size() + size_needed}; - dst_buf += size_needed; - - return batt::OkStatus(); -} - -//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - -// -auto MemInode::readdir(fuse_req_t req, MemFileHandle& /*dh*/, size_t size, DirentOffset offset, - PlusApi plus_api) // - -> batt::StatusOr -{ - LLFS_VLOG(1) << "DEBUG: MemInode::readdir(): current offset " << offset; - - std::unique_ptr storage{new (std::nothrow) char[size]}; - if (!storage) { - return {batt::status_from_errno(ENOMEM)}; - } - - batt::ConstBuffer out_buf{storage.get(), 0u}; - batt::MutableBuffer dst_buf{storage.get(), size}; - - { - auto locked = this->state_.lock(); - - auto children_slice = batt::as_slice(locked->children_by_offset_); - children_slice.advance_begin(std::min(offset, children_slice.size())); - - for (const auto& [child_inode, name] : children_slice) { - const auto next_offset = DirentOffset{offset + 1}; - - //----- --- -- - - - - - // TODO [tastolfi 2023-06-30] maybe do something like this? - // - // e.attr.st_ino = entry->d_ino; - // e.attr.st_mode = entry->d_type << 12; - //----- --- -- - - - - - - batt::Status pack_status = child_inode->state_.lock()->pack_as_fuse_dir_entry( - req, out_buf, dst_buf, name.c_str(), next_offset, plus_api); - - if (pack_status == batt::StatusCode::kResourceExhausted) { - break; - } - - /* In contrast to readdir() (which does not affect the lookup counts), - * the lookup count of every entry returned by readdirplus(), except "." - * and "..", is incremented by one. - * - * - libfuse/include/fuse_lowlevel.h - */ - if (plus_api) { - child_inode->add_lookup(1); - } - - BATT_REQUIRE_OK(pack_status); - - offset = next_offset; - } - } - - LLFS_VLOG(1) << "MemInode::readdir() returning buffer size: " << out_buf.size() - << " (max req=" << size << ")"; - - return {FuseReadDirData{FuseImplBase::OwnedConstBuffer{std::move(storage), out_buf}}}; -} - -//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - -// -auto MemInode::add_child(const std::string& name, batt::SharedPtr&& child_inode) - -> batt::Status -{ - BATT_REQUIRE_OK(this->acquire_count_lock()); - auto on_scope_exit = batt::finally([&] { - this->release_count_lock(); - }); - { - auto locked = this->state_.lock(); - - BATT_REQUIRE_OK(child_inode->increment_link_refs(1)); - - locked->children_by_name_.emplace(name, child_inode); - locked->children_by_offset_.emplace_back(std::move(child_inode), name); - - return batt::OkStatus(); - } -} - -//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - -// -auto MemInode::remove_child(const std::string& name, IsDir is_dir, RequireEmpty require_empty) - -> batt::StatusOr>> -{ - auto locked = this->state_.lock(); - - auto iter = locked->children_by_name_.find(name); - if (iter == locked->children_by_name_.end()) { - return batt::status_from_errno(ENOENT); - } - - auto iter2 = std::find_if(locked->children_by_offset_.begin(), locked->children_by_offset_.end(), - [iter](const std::pair, std::string>& entry) { - return entry.first == iter->second; - }); - if (iter2 == locked->children_by_offset_.end()) { - return batt::status_from_errno(EIO); - } - - batt::SharedPtr child_inode = iter->second; - - if (child_inode->is_dir() != is_dir) { - return batt::status_from_errno(EINVAL); - } - - if (require_empty && child_inode->is_dir() && !child_inode->is_empty()) { - return batt::status_from_errno(ENOTEMPTY); - } - - BATT_ASSIGN_OK_RESULT(const IsDead is_dead, child_inode->decrement_link_refs(1, require_empty)); - - locked->children_by_name_.erase(iter); - locked->children_by_offset_.erase(iter2); - - return {std::make_pair(is_dead, child_inode)}; -} - -//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - -// -auto MemInode::is_dir() const noexcept -> IsDir -{ - return IsDir{static_cast(this->state_.lock()->entry_.attr.st_mode & S_IFMT) == - MemInode::Category::kDirectory}; -} - -//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - -// -usize MemInode::write(u64 offset, const batt::Slice& buffers) -{ - auto locked = this->state_.lock(); - - const usize begin_offset = offset; - for (const batt::ConstBuffer& buffer : buffers) { - locked->write_chunk(offset, buffer); - offset += buffer.size(); - } - - // Update the file size. - // - const auto written_upper_bound = BATT_CHECKED_CAST(i64, offset); - if (written_upper_bound > locked->file_size()) { - locked->file_size(written_upper_bound); - } - - return offset - begin_offset; -} - -//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - -// -void MemInode::State::write_chunk(u64 offset, batt::ConstBuffer buffer) -{ - const u64 first_block_pos = batt::round_down_bits(MemInode::kBlockBufferSizeLog2, offset); - - for (u64 block_pos = first_block_pos; buffer.size() > 0; - block_pos += MemInode::kBlockBufferSize) { - const usize block_offset = offset - block_pos; - - BATT_CHECK_GE(offset, block_pos); - - auto& p_block_buf = this->data_blocks_[block_pos]; - if (!p_block_buf) { - p_block_buf = std::make_shared(); - std::memset(p_block_buf->data(), 0, p_block_buf->size()); - } - - usize n_to_copy = std::min(buffer.size(), p_block_buf->size() - block_offset); - std::memcpy(p_block_buf->data() + block_offset, buffer.data(), n_to_copy); - - buffer += n_to_copy; - offset += n_to_copy; - } -} - -//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - -// -FuseImplBase::WithCleanup> MemInode::read(u64 offset, usize count) -{ - std::vector> blocks; - std::vector buffers; - - { - auto locked = this->state_.lock(); - - while (count > 0) { - std::shared_ptr p_block; - - batt::ConstBuffer chunk = locked->read_chunk(offset, count, &p_block); - if (chunk.size() == 0) { - break; - } - buffers.emplace_back(chunk); - - offset += buffers.back().size(); - count -= buffers.back().size(); - } - } - - auto buffers_slice = batt::as_slice(buffers); - return FuseImplBase::with_cleanup( - buffers_slice, [buffers = std::move(buffers), blocks = std::move(blocks)](auto&&...) { - }); -} - -//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - -// -batt::ConstBuffer MemInode::State::read_chunk(u64 offset, usize count, - std::shared_ptr* p_block_out) -{ - static BlockBuffer zero_block; - static const batt::ConstBuffer zero_buf = [] { - std::memset(zero_block.data(), 0, zero_block.size()); - return batt::ConstBuffer{zero_block.data(), zero_block.size()}; - }(); - - const u64 first_block_pos = batt::round_down_bits(MemInode::kBlockBufferSizeLog2, offset); - const u64 first_block_offset = offset - first_block_pos; - - BATT_CHECK_GE(offset, first_block_pos); - - const batt::ConstBuffer data_block = [&] { - auto iter = this->data_blocks_.find(first_block_pos); - if (iter == this->data_blocks_.end()) { - return zero_buf; - } - - BATT_CHECK_NOT_NULLPTR(iter->second); - - if (p_block_out) { - *p_block_out = iter->second; - } - - return batt::ConstBuffer{ - iter->second->data(), - iter->second->size(), - }; - }(); - - return batt::resize_buffer(data_block + first_block_offset, - std::min(count, this->file_size() - offset)); -} - -} //namespace llfs diff --git a/src/llfs/mem_inode.hpp b/src/llfs/mem_inode.hpp deleted file mode 100644 index 3721ee59..00000000 --- a/src/llfs/mem_inode.hpp +++ /dev/null @@ -1,138 +0,0 @@ -//#=##=##=#==#=#==#===#+==#+==========+==+=+=+=+=+=++=+++=+++++=-++++=-+++++++++++ -// -// Part of the LLFS Project, under Apache License v2.0. -// See https://www.apache.org/licenses/LICENSE-2.0 for license information. -// SPDX short identifier: Apache-2.0 -// -//+++++++++++-+-+--+----- --- -- - - - - - -#pragma once -#ifndef LLFS_MEM_INODE_HPP -#define LLFS_MEM_INODE_HPP - -#include -#include - -#include -#include - -#include -#include -#include -#include - -#include -#include -#include - -namespace llfs { - -class MemFileHandle; - -//=#=#==#==#===============+=+=+=+=++=++++++++++++++-++-+--+-+----+--------------- -// -class MemInode : public MemInodeBase -{ - public: - static constexpr i32 kBlockBufferSizeLog2 = 12; - static constexpr usize kBlockBufferSize = usize{1} << kBlockBufferSizeLog2; - - using BlockBuffer = std::array; - - //----- --- -- - - - - - - explicit MemInode(fuse_ino_t ino, Category category, int mode) noexcept; - - //----- --- -- - - - - - - const fuse_entry_param* get_fuse_entry_param() const noexcept - { - auto locked = this->state_.lock(); - return &locked->entry_; - } - - fuse_ino_t get_ino() const noexcept - { - return this->state_.lock()->entry_.ino; - } - - batt::Status add_child(const std::string& name, batt::SharedPtr&& child_inode); - - batt::StatusOr>> remove_child( - const std::string& name, IsDir is_dir, RequireEmpty require_empty); - - //----- --- -- - - - - - - batt::StatusOr lookup_child(const std::string& name); - - IsDir is_dir() const noexcept; - - bool is_empty() const noexcept - { - return this->state_.lock()->children_by_offset_.empty(); - } - - //----- --- -- - - - - - - batt::StatusOr get_attributes(); - - batt::StatusOr set_attributes(const struct stat* attr, int to_set); - - batt::StatusOr readdir(fuse_req_t req, MemFileHandle& dh, - size_t size, DirentOffset offset, - PlusApi plus_api); - - usize write(u64 offset, const batt::Slice& buffers); - - FuseImplBase::WithCleanup> read(u64 offset, usize count); - - //----- --- -- - - - - - private: - void init_directory(); - - //----- --- -- - - - - - struct State { - fuse_entry_param entry_; - - std::unordered_map> children_by_name_; - - std::vector, std::string>> children_by_offset_; - - std::map> data_blocks_; - - //----- --- -- - - - - - - State(fuse_ino_t ino, Category category, int mode) noexcept; - - batt::StatusOr> find_child_by_name(const std::string& child_name); - - batt::Status pack_as_fuse_dir_entry(fuse_req_t req, batt::ConstBuffer& out_buf, - batt::MutableBuffer& dst_buf, const std::string& name, - DirentOffset offset, PlusApi plus_api) const; - - void write_chunk(u64 offset, batt::ConstBuffer buffer); - - batt::ConstBuffer read_chunk(u64 offset, usize count, - std::shared_ptr* p_block_out); - - i64 file_size() const - { - return this->entry_.attr.st_size; - } - - void file_size(i64 new_size) - { - this->entry_.attr.st_size = BATT_CHECKED_CAST(off_t, new_size); - } - }; - - batt::Mutex state_; - - /** \brief See state flags above. - */ - batt::Watch count_{0}; -}; - -} //namespace llfs - -#endif // LLFS_MEM_INODE_HPP diff --git a/src/llfs/mem_inode_base.hpp b/src/llfs/mem_inode_base.hpp deleted file mode 100644 index fd7ac9ab..00000000 --- a/src/llfs/mem_inode_base.hpp +++ /dev/null @@ -1,232 +0,0 @@ -//#=##=##=#==#=#==#===#+==#+==========+==+=+=+=+=+=++=+++=+++++=-++++=-+++++++++++ -// -// Part of the LLFS Project, under Apache License v2.0. -// See https://www.apache.org/licenses/LICENSE-2.0 for license information. -// SPDX short identifier: Apache-2.0 -// -//+++++++++++-+-+--+----- --- -- - - - - - -#pragma once -#ifndef LLFS_MEM_INODE_BASE_HPP -#define LLFS_MEM_INODE_BASE_HPP - -#include - -#include -#include - -#include -#include -#include - -namespace llfs { - -template -class MemInodeBase : public batt::RefCounted -{ - public: - using FuseReadDirData = FuseImplBase::FuseReadDirData; - - BATT_STRONG_TYPEDEF(bool, RequireEmpty); - BATT_STRONG_TYPEDEF(bool, IsDead); - BATT_STRONG_TYPEDEF(bool, IsDir); - - enum struct Category : mode_t { - kBlockSpecial = S_IFBLK, - kCharSpecial = S_IFCHR, - kFifoSpecial = S_IFIFO, - kRegularFile = S_IFREG, - kDirectory = S_IFDIR, - kSymbolicLink = S_IFLNK, - }; - - static constexpr u64 kLookupCountShift = 0; - static constexpr u64 kLinkCountShift = 40; - static constexpr u64 kLockFlag = u64{1} << 63; - static constexpr u64 kDeadFlag = u64{1} << 62; - // - static_assert(kLinkCountShift > kLookupCountShift); - static_assert(kLookupCountShift == 0); - // - static constexpr u64 kLookupCountIncrement = u64{1} << kLookupCountShift; - static constexpr u64 kLinkCountIncrement = u64{1} << kLinkCountShift; - static constexpr u64 kMaxLookupCount = (u64{1} << kLinkCountShift) - 1; - static constexpr u64 kMaxLinkCount = (u64{1} << (62 - kLinkCountShift)) - 1; - static constexpr u64 kLookupCountMask = kMaxLookupCount; - static constexpr u64 kLinkCountMask = kMaxLinkCount; - - static u64 get_lookup_count(u64 count) noexcept - { - return (count >> MemInodeBase::kLookupCountShift) & kLookupCountMask; - } - - static u64 get_link_count(u64 count) noexcept - { - return (count >> MemInodeBase::kLinkCountShift) & kLinkCountMask; - } - - static IsDead is_dead_state(u64 count) noexcept - { - return IsDead{(count & MemInodeBase::kDeadFlag) != 0}; - } - - //+++++++++++-+-+--+----- --- -- - - - - - - void add_lookup(usize count) noexcept - { - const u64 increment = MemInodeBase::kLookupCountIncrement * count; - const u64 prior_value = this->count_.fetch_add(increment); - - BATT_CHECK_LT(get_lookup_count(prior_value), kMaxLookupCount); - BATT_CHECK_LT(get_lookup_count(prior_value), get_lookup_count(prior_value + increment)); - } - - IsDead forget(u64 count) - { - return this->remove_lookup(count); - } - - IsDead is_dead() const noexcept - { - return is_dead_state(this->count_.get_value()); - } - - IsDead remove_lookup(usize count) noexcept; - - batt::Status increment_link_refs(usize count) noexcept; - - batt::StatusOr decrement_link_refs(usize count, RequireEmpty require_empty) noexcept; - - batt::Status acquire_count_lock() noexcept; - - void release_count_lock() noexcept; - - //+++++++++++-+-+--+----- --- -- - - - - - private: - /** \brief See state flags above. - */ - batt::Watch count_{0}; -}; - -//#=##=##=#==#=#==#===#+==#+==========+==+=+=+=+=+=++=+++=+++++=-++++=-+++++++++++ - -//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - -// -template -inline auto MemInodeBase::remove_lookup(usize count) noexcept -> IsDead -{ - const u64 increment = MemInodeBase::kLookupCountIncrement * count; - const u64 prior_value = this->count_.fetch_sub(increment); - - BATT_CHECK_GE(MemInodeBase::get_lookup_count(prior_value), count) - << BATT_INSPECT(prior_value) << BATT_INSPECT(count) << BATT_INSPECT(increment) - << BATT_INSPECT(std::bitset<64>{kLookupCountMask}) << BATT_INSPECT(kLookupCountShift); - - BATT_CHECK_GT(MemInodeBase::get_lookup_count(prior_value), - MemInodeBase::get_lookup_count(prior_value - increment)) - << BATT_INSPECT(prior_value) << BATT_INSPECT(count) << BATT_INSPECT(increment); - - return MemInodeBase::is_dead_state(prior_value - increment); -} - -//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - -// -template -inline batt::Status MemInodeBase::increment_link_refs(usize count) noexcept -{ - const u64 increment = MemInodeBase::kLinkCountIncrement * count; - const u64 prior_value = this->count_.fetch_add(increment); - - BATT_CHECK_LT(MemInodeBase::get_link_count(prior_value), MemInodeBase::kMaxLinkCount); - BATT_CHECK_LT(MemInodeBase::get_link_count(prior_value), - MemInodeBase::get_link_count(prior_value + increment)); - - if ((prior_value & MemInodeBase::kDeadFlag) != 0) { - this->count_.fetch_sub(increment); - return batt::status_from_errno(ENOENT); - } - - return batt::OkStatus(); -} - -//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - -// -template -inline auto MemInodeBase::decrement_link_refs(usize count, - RequireEmpty require_empty) noexcept - -> batt::StatusOr -{ - if (count == 0) { - return {batt::status_from_errno(EINVAL)}; - } - - const u64 increment = MemInodeBase::kLinkCountIncrement * count; - - BATT_REQUIRE_OK(this->acquire_count_lock()); - auto on_scope_exit = batt::finally([&] { - this->release_count_lock(); - }); - - const batt::Optional updated_value = - this->count_.modify_if([&](u64 observed_value) -> batt::Optional { - const u64 observed_link_count = MemInodeBase::get_link_count(observed_value); - const bool will_be_dead = ((observed_link_count - count) == 0); - - BATT_CHECK_LE(count, observed_link_count); - BATT_CHECK_GE(MemInodeBase::get_link_count(observed_value), - MemInodeBase::get_link_count(observed_value - increment)) - << "Integer wrap!"; - - // If the requested decrement would bring the link count to zero but we are not empty, this - // is an error. - // - if (require_empty && will_be_dead && !static_cast(this)->is_empty()) { - return batt::None; - } - - // Everything looks good! Attempt to CAS-modify the count. - // - u64 target_value = observed_value - increment; - if (will_be_dead) { - target_value |= MemInodeBase::kDeadFlag; - } - - return target_value; - }); - - if (!updated_value) { - return {batt::status_from_errno(ENOTEMPTY)}; - } - - return MemInodeBase::is_dead_state(*updated_value); -} - -//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - -// -template -inline batt::Status MemInodeBase::acquire_count_lock() noexcept -{ - for (;;) { - const u64 prior_value = this->count_.fetch_or(MemInodeBase::kLockFlag); - if (!(prior_value & MemInodeBase::kLockFlag)) { - break; - } - if ((prior_value & MemInodeBase::kDeadFlag)) { - return {batt::status_from_errno(ENOENT)}; - } - batt::Task::yield(); - } - return batt::OkStatus(); -} - -//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - -// -template -inline void MemInodeBase::release_count_lock() noexcept -{ - this->count_.fetch_and(~MemInodeBase::kLockFlag); -} - -} //namespace llfs - -#endif // LLFS_MEM_INODE_BASE_HPP diff --git a/src/llfs/null_fuse_impl.hpp b/src/llfs/null_fuse_impl.hpp deleted file mode 100644 index 92252e19..00000000 --- a/src/llfs/null_fuse_impl.hpp +++ /dev/null @@ -1,599 +0,0 @@ -//#=##=##=#==#=#==#===#+==#+==========+==+=+=+=+=+=++=+++=+++++=-++++=-+++++++++++ -// -// Part of the LLFS Project, under Apache License v2.0. -// See https://www.apache.org/licenses/LICENSE-2.0 for license information. -// SPDX short identifier: Apache-2.0 -// -//+++++++++++-+-+--+----- --- -- - - - - - -#pragma once -#ifndef LLFS_NULL_FUSE_IMPL_HPP -#define LLFS_NULL_FUSE_IMPL_HPP - -#include -// -#include -#include - -#include - -namespace llfs { - -/** \brief A minimal example of a FuseImpl class. - */ -class NullFuseImpl : public FuseImpl -{ - public: - using FuseImpl::FuseImpl; - - /** \brief - */ // 1/44 - void init() - { - BATT_CHECK_NOT_NULLPTR(this->conn_); - } - - /** \brief - */ // 2/44 - void destroy() - { - BATT_CHECK_NOT_NULLPTR(this->conn_); - } - - /** \brief - */ // 3/44 - template - void async_lookup(fuse_req_t req, fuse_ino_t parent, const char* name, Handler&& handler) - { - LLFS_LOG_WARNING() << "Not Implemented: " << BATT_THIS_FUNCTION; - - (void)req; - (void)parent; - (void)name; - - BATT_FORWARD(handler) - (batt::StatusOr{batt::Status{batt::StatusCode::kUnimplemented}}); - } - - /** \brief - */ // 4/44 - template - void async_forget_inode(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup, Handler&& handler) - { - LLFS_LOG_WARNING() << "Not Implemented: " << BATT_THIS_FUNCTION; - - (void)req; - (void)ino; - (void)nlookup; - - BATT_FORWARD(handler)(); - } - - /** \brief - */ // 5/44 - template - void async_get_attributes(fuse_req_t req, fuse_ino_t ino, Handler&& handler) - { - LLFS_LOG_WARNING() << "Not Implemented: " << BATT_THIS_FUNCTION; - - (void)req; - (void)ino; - - BATT_FORWARD(handler) - (batt::StatusOr{batt::Status{batt::StatusCode::kUnimplemented}}); - } - - /** \brief - */ // 6/44 - template - void async_set_attributes(fuse_req_t req, fuse_ino_t ino, struct stat* attr, int to_set, - struct fuse_file_info* fi, Handler&& handler) - { - LLFS_LOG_WARNING() << "Not Implemented: " << BATT_THIS_FUNCTION; - - (void)req; - (void)ino; - (void)attr; - (void)to_set; - (void)fi; - - BATT_FORWARD(handler) - (batt::StatusOr{batt::Status{batt::StatusCode::kUnimplemented}}); - } - - /** \brief - */ // 7/44 - template - void async_readlink(fuse_req_t req, fuse_ino_t ino, Handler&& handler) - { - LLFS_LOG_WARNING() << "Not Implemented: " << BATT_THIS_FUNCTION; - - (void)req; - (void)ino; - - BATT_FORWARD(handler)(/*link=*/""); - } - - /** \brief - */ // 8/44 - template - void async_make_node(fuse_req_t req, fuse_ino_t parent, const char* name, mode_t mode, dev_t rdev, - Handler&& handler) - { - LLFS_LOG_WARNING() << "Not Implemented: " << BATT_THIS_FUNCTION; - - (void)req; - (void)parent; - (void)name; - (void)mode; - (void)rdev; - - BATT_FORWARD(handler) - (batt::StatusOr{batt::Status{batt::StatusCode::kUnimplemented}}); - } - - /** \brief - */ // 9/44 - template - void async_make_directory(fuse_req_t req, fuse_ino_t parent, const char* name, mode_t mode, - Handler&& handler) - { - LLFS_LOG_WARNING() << "Not Implemented: " << BATT_THIS_FUNCTION; - - (void)req; - (void)parent; - (void)name; - (void)mode; - - BATT_FORWARD(handler) - (batt::StatusOr{batt::Status{batt::StatusCode::kUnimplemented}}); - } - - /** \brief - */ // 10/44 - template - void async_unlink(fuse_req_t req, fuse_ino_t parent, const char* name, Handler&& handler) - { - LLFS_LOG_WARNING() << "Not Implemented: " << BATT_THIS_FUNCTION; - - (void)req; - (void)parent; - (void)name; - - BATT_FORWARD(handler)(batt::Status{batt::StatusCode::kUnimplemented}); - } - - /** \brief - */ // 11/44 - template - void async_remove_directory(fuse_req_t req, fuse_ino_t parent, const char* name, - Handler&& handler) - { - LLFS_LOG_WARNING() << "Not Implemented: " << BATT_THIS_FUNCTION; - - (void)req; - (void)parent; - (void)name; - - BATT_FORWARD(handler)(batt::Status{batt::StatusCode::kUnimplemented}); - } - - /** \brief - */ // 12/44 - template - void async_symbolic_link(fuse_req_t req, const char* link, fuse_ino_t parent, const char* name, - Handler&& handler) - { - LLFS_LOG_WARNING() << "Not Implemented: " << BATT_THIS_FUNCTION; - - (void)req; - (void)link; - (void)parent; - (void)name; - - BATT_FORWARD(handler) - (batt::StatusOr{batt::Status{batt::StatusCode::kUnimplemented}}); - } - - /** \brief - */ // 13/44 - template - void async_rename(fuse_req_t req, fuse_ino_t parent, const char* name, fuse_ino_t newparent, - const char* newname, unsigned int flags, Handler&& handler) - { - LLFS_LOG_WARNING() << "Not Implemented: " << BATT_THIS_FUNCTION; - - (void)req; - (void)parent; - (void)name; - (void)newparent; - (void)newname; - (void)flags; - - BATT_FORWARD(handler)(batt::Status{batt::StatusCode::kUnimplemented}); - } - - /** \brief - */ // 14/44 - template - void async_hard_link(fuse_req_t req, fuse_ino_t ino, fuse_ino_t newparent, const char* newname, - Handler&& handler) - { - LLFS_LOG_WARNING() << "Not Implemented: " << BATT_THIS_FUNCTION; - - (void)req; - (void)ino; - (void)newparent; - (void)newname; - - BATT_FORWARD(handler) - (batt::StatusOr{batt::Status{batt::StatusCode::kUnimplemented}}); - } - - /** \brief - */ // 15/44 - template - void async_open(fuse_req_t req, fuse_ino_t ino, fuse_file_info* fi, Handler&& handler) - { - LLFS_LOG_WARNING() << "Not Implemented: " << BATT_THIS_FUNCTION; - - (void)req; - (void)ino; - (void)fi; - - BATT_FORWARD(handler) - (batt::StatusOr{batt::Status{batt::StatusCode::kUnimplemented}}); - } - - /** \brief - */ // 16/44 - template - void async_read(fuse_req_t req, fuse_ino_t ino, size_t size, FileOffset off, FuseFileHandle fh, - Handler&& handler) - { - LLFS_LOG_WARNING() << "Not Implemented: " << BATT_THIS_FUNCTION; - - (void)req; - (void)ino; - (void)size; - (void)off; - (void)fh; - - BATT_FORWARD(handler) - (batt::StatusOr{batt::Status{batt::StatusCode::kUnimplemented}}); - } - - /** \brief - */ // 17/44 - template - void async_write(fuse_req_t req, fuse_ino_t ino, const batt::ConstBuffer& buffer, - FileOffset offset, FuseFileHandle fh, Handler&& handler) - { - LLFS_LOG_WARNING() << "Not Implemented: " << BATT_THIS_FUNCTION; - - (void)req; - (void)ino; - (void)buffer; - (void)offset; - (void)fh; - - BATT_FORWARD(handler)(batt::StatusOr{batt::Status{batt::StatusCode::kUnimplemented}}); - } - - /** \brief - */ // 18/44 - template - void async_flush(fuse_req_t req, fuse_ino_t ino, FuseFileHandle fh, Handler&& handler) - { - LLFS_LOG_WARNING() << "Not Implemented: " << BATT_THIS_FUNCTION; - - (void)req; - (void)ino; - (void)fh; - - BATT_FORWARD(handler)(batt::Status{batt::StatusCode::kUnimplemented}); - } - - /** \brief - */ // 19/44 - template - void async_release(fuse_req_t req, fuse_ino_t ino, FuseFileHandle fh, FileOpenFlags flags, - Handler&& handler) - { - LLFS_LOG_WARNING() << "Not Implemented: " << BATT_THIS_FUNCTION; - - (void)req; - (void)ino; - (void)fh; - (void)flags; - - BATT_FORWARD(handler)(batt::Status{batt::StatusCode::kUnimplemented}); - } - - /** \brief - */ // 20/44 - template - void async_fsync(fuse_req_t req, fuse_ino_t ino, IsDataSync datasync, FuseFileHandle fh, - Handler&& handler) - { - LLFS_LOG_WARNING() << "Not Implemented: " << BATT_THIS_FUNCTION; - - (void)req; - (void)ino; - (void)datasync; - (void)fh; - - BATT_FORWARD(handler)(batt::Status{batt::StatusCode::kUnimplemented}); - } - - /** \brief - */ // 21/44 - template - void async_opendir(fuse_req_t req, fuse_ino_t ino, fuse_file_info* fi, Handler&& handler) - { - LLFS_LOG_WARNING() << "Not Implemented: " << BATT_THIS_FUNCTION; - - (void)req; - (void)ino; - (void)fi; - - BATT_FORWARD(handler) - (batt::StatusOr{batt::Status{batt::StatusCode::kUnimplemented}}); - } - - /** \brief - */ // 22/44 - template - void async_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, DirentOffset off, - FuseFileHandle fh, Handler&& handler) - { - LLFS_LOG_WARNING() << "Not Implemented: " << BATT_THIS_FUNCTION; - - (void)req; - (void)ino; - (void)size; - (void)off; - (void)fh; - - BATT_FORWARD(handler) - (batt::StatusOr{batt::Status{batt::StatusCode::kUnimplemented}}); - } - - /** \brief - */ // 23/44 - template - void async_releasedir(fuse_req_t req, fuse_ino_t ino, FuseFileHandle fh, Handler&& handler) - { - LLFS_LOG_WARNING() << "Not Implemented: " << BATT_THIS_FUNCTION; - - (void)req; - (void)ino; - (void)fh; - - BATT_FORWARD(handler)(batt::Status{batt::StatusCode::kUnimplemented}); - } - - /** \brief - */ // 24/44 - template - void async_fsyncdir(fuse_req_t req, fuse_ino_t ino, IsDataSync datasync, FuseFileHandle fh, - Handler&& handler) - { - LLFS_LOG_WARNING() << "Not Implemented: " << BATT_THIS_FUNCTION; - - (void)req; - (void)ino; - (void)datasync; - (void)fh; - - BATT_FORWARD(handler)(batt::Status{batt::StatusCode::kUnimplemented}); - } - - /** \brief - */ // 25/44 - template - void async_statfs(fuse_req_t req, fuse_ino_t ino, Handler&& handler) - { - LLFS_LOG_WARNING() << "Not Implemented: " << BATT_THIS_FUNCTION; - - (void)req; - (void)ino; - - BATT_FORWARD(handler) - (batt::StatusOr{batt::Status{batt::StatusCode::kUnimplemented}}); - } - - /** \brief - */ // 26/44 - template - void async_set_extended_attribute(fuse_req_t req, fuse_ino_t ino, - const FuseImplBase::ExtendedAttribute& attr, int flags, - Handler&& handler) - { - LLFS_LOG_WARNING() << "Not Implemented: " << BATT_THIS_FUNCTION; - - (void)req; - (void)ino; - (void)attr; - (void)flags; - - BATT_FORWARD(handler)(batt::Status{batt::StatusCode::kUnimplemented}); - } - - /** \brief - */ // 27/44 - template - void async_get_extended_attribute(fuse_req_t req, fuse_ino_t ino, const char* name, size_t size, - Handler&& handler) - { - LLFS_LOG_WARNING() << "Not Implemented: " << BATT_THIS_FUNCTION; - - (void)req; - (void)ino; - (void)name; - (void)size; - - BATT_FORWARD(handler) - (batt::StatusOr{batt::Status{batt::StatusCode::kUnimplemented}}); - } - - /** \brief - */ // 29/44 - template - void async_remove_extended_attribute(fuse_req_t req, fuse_ino_t ino, const char* name, - Handler&& handler) - { - LLFS_LOG_WARNING() << "Not Implemented: " << BATT_THIS_FUNCTION; - - (void)req; - (void)ino; - (void)name; - - BATT_FORWARD(handler)(batt::Status{batt::StatusCode::kUnimplemented}); - } - - /** \brief - */ // 30/44 - template - void async_check_access(fuse_req_t req, fuse_ino_t ino, int mask, Handler&& handler) - { - LLFS_LOG_WARNING() << "Not Implemented: " << BATT_THIS_FUNCTION; - - (void)req; - (void)ino; - (void)mask; - - BATT_FORWARD(handler)(batt::Status{batt::StatusCode::kUnimplemented}); - } - - /** \brief - */ // 31/44 - template - void async_create(fuse_req_t req, fuse_ino_t parent, const char* name, mode_t mode, - fuse_file_info* fi, Handler&& handler) - { - LLFS_LOG_WARNING() << "Not Implemented: " << BATT_THIS_FUNCTION; - - (void)req; - (void)parent; - (void)name; - (void)mode; - (void)fi; - - BATT_FORWARD(handler) - (batt::StatusOr{batt::Status{batt::StatusCode::kUnimplemented}}); - } - - /** \brief - */ // 35/44 - template - void async_ioctl(fuse_req_t req, fuse_ino_t ino, unsigned int cmd, void* arg, - struct fuse_file_info* fi, unsigned flags, const batt::ConstBuffer& in_buf, - size_t out_bufsz, Handler&& handler) - { - LLFS_LOG_WARNING() << "Not Implemented: " << BATT_THIS_FUNCTION; - - (void)req; - (void)ino; - (void)cmd; - (void)arg; - (void)fi; - (void)flags; - (void)in_buf; - (void)out_bufsz; - - BATT_FORWARD(handler) - (batt::StatusOr{batt::Status{batt::StatusCode::kUnimplemented}}); - } - - /** \brief - */ // 37/44 - template - void async_write_buf(fuse_req_t req, fuse_ino_t ino, const FuseImplBase::ConstBufferVec& bufv, - FileOffset offset, FuseFileHandle fh, std::shared_ptr&& storage, - Handler&& handler) - { - LLFS_LOG_WARNING() << "Not Implemented: " << BATT_THIS_FUNCTION; - - (void)req; - (void)ino; - (void)bufv; - (void)offset; - (void)fh; - (void)storage; - - BATT_FORWARD(handler)(batt::StatusOr{batt::Status{batt::StatusCode::kUnimplemented}}); - } - - /** \brief - */ // 38/44 - template - void async_retrieve_reply(fuse_req_t req, void* cookie, fuse_ino_t ino, off_t offset, - struct fuse_bufvec* bufv, Handler&& handler) - { - LLFS_LOG_WARNING() << "Not Implemented: " << BATT_THIS_FUNCTION; - - (void)req; - (void)cookie; - (void)ino; - (void)offset; - (void)bufv; - - BATT_FORWARD(handler)(); - } - - /** \brief - */ // 39/44 - template - void async_forget_multiple_inodes(fuse_req_t req, batt::Slice forgets, - Handler&& handler) - { - LLFS_LOG_WARNING() << "Not Implemented: " << BATT_THIS_FUNCTION; - - (void)req; - (void)forgets; - - BATT_FORWARD(handler)(); - } - - /** \brief - */ // 41/44 - template - void async_file_allocate(fuse_req_t req, fuse_ino_t ino, int mode, FileOffset offset, - FileLength length, fuse_file_info* fi, Handler&& handler) - { - LLFS_LOG_WARNING() << "Not Implemented: " << BATT_THIS_FUNCTION; - - (void)req; - (void)ino; - (void)mode; - (void)offset; - (void)length; - (void)fi; - - BATT_FORWARD(handler)(batt::Status{batt::StatusCode::kUnimplemented}); - } - - /** \brief - */ // 42/44 - template - void async_readdirplus(fuse_req_t req, fuse_ino_t ino, size_t size, DirentOffset offset, - FuseFileHandle fh, Handler&& handler) - { - LLFS_LOG_WARNING() << "Not Implemented: " << BATT_THIS_FUNCTION; - - (void)req; - (void)ino; - (void)size; - (void)offset; - (void)fh; - - BATT_FORWARD(handler) - (batt::StatusOr{batt::Status{batt::StatusCode::kUnimplemented}}); - } - -}; // class NullFuseImpl - -} //namespace llfs - -#endif // LLFS_NULL_FUSE_IMPL_HPP diff --git a/src/llfs/null_worker_task_fuse_impl.cpp b/src/llfs/null_worker_task_fuse_impl.cpp deleted file mode 100644 index b83f4d91..00000000 --- a/src/llfs/null_worker_task_fuse_impl.cpp +++ /dev/null @@ -1,9 +0,0 @@ -//#=##=##=#==#=#==#===#+==#+==========+==+=+=+=+=+=++=+++=+++++=-++++=-+++++++++++ -// -// Part of the LLFS Project, under Apache License v2.0. -// See https://www.apache.org/licenses/LICENSE-2.0 for license information. -// SPDX short identifier: Apache-2.0 -// -//+++++++++++-+-+--+----- --- -- - - - - - -#include diff --git a/src/llfs/null_worker_task_fuse_impl.hpp b/src/llfs/null_worker_task_fuse_impl.hpp deleted file mode 100644 index 66306d77..00000000 --- a/src/llfs/null_worker_task_fuse_impl.hpp +++ /dev/null @@ -1,309 +0,0 @@ -//#=##=##=#==#=#==#===#+==#+==========+==+=+=+=+=+=++=+++=+++++=-++++=-+++++++++++ -// -// Part of the LLFS Project, under Apache License v2.0. -// See https://www.apache.org/licenses/LICENSE-2.0 for license information. -// SPDX short identifier: Apache-2.0 -// -//+++++++++++-+-+--+----- --- -- - - - - - -#pragma once -#ifndef LLFS_NULL_WORKER_TASK_FUSE_IMPL_HPP -#define LLFS_NULL_WORKER_TASK_FUSE_IMPL_HPP - -#include -// -#include - -#include - -namespace llfs { - -BATT_SUPPRESS_IF_GCC("-Wunused-parameter") - -class NullWorkerTaskFuseImpl : public WorkerTaskFuseImpl -{ - public: - using Super = WorkerTaskFuseImpl; - - //+++++++++++-+-+--+----- --- -- - - - - - - explicit NullWorkerTaskFuseImpl(std::shared_ptr&& work_queue) noexcept - : Super{std::move(work_queue)} - { - } - - //+++++++++++-+-+--+----- --- -- - - - - - - /** \brief - */ // 1/44 - void init() - { - BATT_CHECK_NOT_NULLPTR(this->conn_); - } - - /** \brief - */ // 2/44 - void destroy() - { - BATT_CHECK_NOT_NULLPTR(this->conn_); - } - - /** \brief - */ // 3/44 - batt::StatusOr lookup(fuse_req_t req, fuse_ino_t parent, - const std::string& name) - { - return {batt::StatusCode::kUnimplemented}; - } - - /** \brief - */ // 4/44 - void forget_inode(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup) - { - } - - /** \brief - */ // 5/44 - batt::StatusOr get_attributes(fuse_req_t req, fuse_ino_t ino, - fuse_file_info* fi) - { - return {batt::StatusCode::kUnimplemented}; - } - - /** \brief - */ // 6/44 - batt::StatusOr set_attributes(fuse_req_t req, fuse_ino_t ino, - struct stat* attr, int to_set, - batt::Optional fh_from_ftruncate) - { - return {batt::StatusCode::kUnimplemented}; - } - - /** \brief - */ // 7/44 - const char* readlink(fuse_req_t req, fuse_ino_t ino) - { - return ""; - } - - /** \brief - */ // 8/44 - batt::StatusOr make_node(fuse_req_t req, fuse_ino_t parent, - const std::string& name, mode_t mode, - dev_t rdev) - { - return {batt::StatusCode::kUnimplemented}; - } - - /** \brief - */ // 9/44 - batt::StatusOr make_directory(fuse_req_t req, fuse_ino_t parent, - const std::string& name, mode_t mode) - { - return {batt::StatusCode::kUnimplemented}; - } - - /** \brief - */ // 10/44 - batt::Status unlink(fuse_req_t req, fuse_ino_t parent, const std::string& name) - { - return {batt::StatusCode::kUnimplemented}; - } - - /** \brief - */ // 11/44 - batt::Status remove_directory(fuse_req_t req, fuse_ino_t parent, const std::string& name) - { - return {batt::StatusCode::kUnimplemented}; - } - - /** \brief - */ // 12/44 - batt::StatusOr symbolic_link(fuse_req_t req, const std::string& link, - fuse_ino_t parent, const std::string& name) - { - return {batt::StatusCode::kUnimplemented}; - } - - /** \brief - */ // 13/44 - batt::Status rename(fuse_req_t req, fuse_ino_t parent, const std::string& name, - fuse_ino_t newparent, const std::string& newname, unsigned int flags) - { - return {batt::StatusCode::kUnimplemented}; - } - - /** \brief - */ // 14/44 - batt::StatusOr hard_link(fuse_req_t req, fuse_ino_t ino, - fuse_ino_t newparent, - const std::string& newname) - { - return {batt::StatusCode::kUnimplemented}; - } - - /** \brief - */ // 15/44 - batt::StatusOr open(fuse_req_t req, fuse_ino_t ino, fuse_file_info* fi) - { - return {batt::StatusCode::kUnimplemented}; - } - - /** \brief - */ // 16/44 - batt::StatusOr read(fuse_req_t req, fuse_ino_t ino, size_t size, - FileOffset offset, fuse_file_info* fi) - { - return {batt::StatusCode::kUnimplemented}; - } - - /** \brief - */ // 17/44 - batt::StatusOr write(fuse_req_t req, fuse_ino_t ino, const batt::ConstBuffer& buffer, - FileOffset offset, fuse_file_info* fi) - { - return {batt::StatusCode::kUnimplemented}; - } - - /** \brief - */ // 18/44 - batt::Status flush(fuse_req_t req, fuse_ino_t ino, fuse_file_info* fi) - { - return {batt::StatusCode::kUnimplemented}; - } - - /** \brief - */ // 19/44 - batt::Status release(fuse_req_t req, fuse_ino_t ino, u64 fh, int flags) - { - return {batt::StatusCode::kUnimplemented}; - } - - /** \brief - */ // 20/44 - batt::Status fsync(fuse_req_t req, fuse_ino_t ino, IsDataSync datasync, FuseFileHandle fh) - { - return {batt::StatusCode::kUnimplemented}; - } - - /** \brief - */ // 21/44 - batt::StatusOr opendir(fuse_req_t req, fuse_ino_t ino, - const fuse_file_info& fi) - { - return {batt::StatusCode::kUnimplemented}; - } - - /** \brief - */ // 22/44 - batt::StatusOr readdir(fuse_req_t req, fuse_ino_t ino, size_t size, - DirentOffset off, fuse_file_info* fi) - { - return {batt::StatusCode::kUnimplemented}; - } - - /** \brief - */ // 23/44 - batt::Status releasedir(fuse_req_t req, fuse_ino_t ino, fuse_file_info* fi) - { - return {batt::StatusCode::kUnimplemented}; - } - - /** \brief - */ // 24/44 - batt::Status fsyncdir(fuse_req_t req, fuse_ino_t ino, IsDataSync datasync, FuseFileHandle fh) - { - return {batt::StatusCode::kUnimplemented}; - } - - /** \brief - */ // 25/44 - batt::StatusOr statfs(fuse_req_t req, fuse_ino_t ino) - { - return {batt::StatusCode::kUnimplemented}; - } - - /** \brief - */ // 26/44 - batt::Status set_extended_attribute(fuse_req_t req, fuse_ino_t ino, - const FuseImplBase::ExtendedAttribute& attr, int flags) - { - return {batt::StatusCode::kUnimplemented}; - } - - /** \brief - */ // 27/44 - batt::StatusOr get_extended_attribute( - fuse_req_t req, fuse_ino_t ino, const std::string& name, size_t size) - { - return {batt::StatusCode::kUnimplemented}; - } - - /** \brief - */ // 29/44 - batt::Status remove_extended_attribute(fuse_req_t req, fuse_ino_t ino, const std::string& name) - { - return {batt::StatusCode::kUnimplemented}; - } - - /** \brief - */ // 30/44 - batt::Status check_access(fuse_req_t req, fuse_ino_t ino, int mask) - { - return {batt::StatusCode::kUnimplemented}; - } - - /** \brief - */ // 31/44 - batt::StatusOr create(fuse_req_t req, fuse_ino_t parent, - const std::string& name, mode_t mode, - fuse_file_info* fi) - { - return {batt::StatusCode::kUnimplemented}; - } - - /** \brief - */ // 37/44 - batt::StatusOr write_buf(fuse_req_t req, fuse_ino_t ino, struct fuse_bufvec* bufv, - FileOffset offset, fuse_file_info* fi) - { - return {batt::StatusCode::kUnimplemented}; - } - - /** \brief - */ // 38/44 - void retrieve_reply(fuse_req_t req, void* cookie, fuse_ino_t ino, off_t offset, - struct fuse_bufvec* bufv) - { - } - - /** \brief - */ // 39/44 - void forget_multiple_inodes(fuse_req_t req, batt::Slice forgets) - { - } - - /** \brief - */ // 41/44 - batt::Status file_allocate(fuse_req_t req, fuse_ino_t ino, int mode, FileOffset offset, - FileLength length, fuse_file_info* fi) - { - return {batt::StatusCode::kUnimplemented}; - } - - /** \brief - */ // 42/44 - batt::StatusOr readdirplus(fuse_req_t req, fuse_ino_t ino, - size_t size, DirentOffset offset, - fuse_file_info* fi) - { - return {batt::StatusCode::kUnimplemented}; - } - -}; // class NullWorkerTaskFuseImpl - -BATT_UNSUPPRESS_IF_GCC() - -} //namespace llfs - -#endif // LLFS_NULL_WORKER_TASK_FUSE_IMPL_HPP diff --git a/src/llfs/page_view.hpp b/src/llfs/page_view.hpp index 033c8004..3ab79644 100644 --- a/src/llfs/page_view.hpp +++ b/src/llfs/page_view.hpp @@ -17,7 +17,6 @@ #include #include #include -#include #include #include diff --git a/src/llfs/sha256.cpp b/src/llfs/sha256.cpp deleted file mode 100644 index 0efca2e5..00000000 --- a/src/llfs/sha256.cpp +++ /dev/null @@ -1,75 +0,0 @@ -//#=##=##=#==#=#==#===#+==#+==========+==+=+=+=+=+=++=+++=+++++=-++++=-+++++++++++ -// -// Part of the LLFS Project, under Apache License v2.0. -// See https://www.apache.org/licenses/LICENSE-2.0 for license information. -// SPDX short identifier: Apache-2.0 -// -//+++++++++++-+-+--+----- --- -- - - - - - -#include -// - -namespace llfs { - -//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - -// -/*static*/ batt::Optional Sha256::from_str(const std::string_view& s) -{ - Sha256 result; - - const char* next_ch = s.data(); - const char* last_ch = s.data() + s.size(); - - for (u8& byte : result.bytes) { - byte = 0; - { - if (next_ch == last_ch) { - return batt::None; - } - const char ch = *next_ch; - - if ('0' <= ch && ch <= '9') { - byte = (ch - '0') << 4; - } else if ('A' <= ch && ch <= 'F') { - byte = (ch - 'A' + 0xa) << 4; - } else if ('a' <= ch && ch <= 'f') { - byte = (ch - 'a' + 0xa) << 4; - } else { - return batt::None; - } - } - ++next_ch; - { - if (next_ch == last_ch) { - return batt::None; - } - - const char ch = *next_ch; - - if ('0' <= ch && ch <= '9') { - byte |= (ch - '0'); - } else if ('A' <= ch && ch <= 'F') { - byte |= (ch - 'A' + 0xa); - } else if ('a' <= ch && ch <= 'f') { - byte |= (ch - 'a' + 0xa); - } else { - return batt::None; - } - } - ++next_ch; - } - - return result; -} - -//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - -// -std::ostream& operator<<(std::ostream& out, const Sha256& t) -{ - for (u8 next_byte : t.bytes) { - out << batt::to_string(std::hex, std::setw(2), std::setfill('0'), (int)next_byte); - } - return out; -} - -} //namespace llfs diff --git a/src/llfs/sha256.hpp b/src/llfs/sha256.hpp deleted file mode 100644 index ab1e7cb5..00000000 --- a/src/llfs/sha256.hpp +++ /dev/null @@ -1,166 +0,0 @@ -//#=##=##=#==#=#==#===#+==#+==========+==+=+=+=+=+=++=+++=+++++=-++++=-+++++++++++ -// -// Part of the LLFS Project, under Apache License v2.0. -// See https://www.apache.org/licenses/LICENSE-2.0 for license information. -// SPDX short identifier: Apache-2.0 -// -//+++++++++++-+-+--+----- --- -- - - - - - -#pragma once -#ifndef LLFS_SHA256_HPP -#define LLFS_SHA256_HPP - -#include -// -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include - -#include - -namespace llfs { - -//=#=#==#==#===============+=+=+=+=++=++++++++++++++-++-+--+-+----+--------------- -// -/** \brief A packable SHA-256 (32-byte) hash. - */ -struct Sha256 { - /** \brief Parses a 64-character hex string and returns the resulting Sha256 value. - * - * \return the parsed Sha256 on success; None if a parsing error occurred. - */ - static batt::Optional from_str(const std::string_view& s); - - //+++++++++++-+-+--+----- --- -- - - - - - - /** \brief The bytes of the SHA hash. - */ - std::array bytes; - - /** \brief Default initializes a Sha256 object; this does NOT set the initial contents of - * `this->bytes`! - */ - Sha256() = default; - - //+++++++++++-+-+--+----- --- -- - - - - - - /** \brief Returns the raw SHA hash bytes as a binary string; this is NOT the inverse of the - * Sha256::from_str function, but rather just a simple type conversion. - */ - std::string_view as_key() const noexcept - { - return std::string_view{(const char*)bytes.data(), bytes.size()}; - } -}; - -// Sanity check: nothing else but the SHA-256 (256-bits == 32-bytes) in class Sha256! -// -BATT_STATIC_ASSERT_EQ(sizeof(Sha256), 32); - -//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - -// -/** \brief Prints a Sha256 as a hex string; this is the inverse of Sha256::from_str. - */ -std::ostream& operator<<(std::ostream& out, const Sha256& t); - -//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - -// -/** \brief Computes a machine-word-sized hash integer based on the passed Sha256; the returned value - * is suitable for use in containers like std::unordered_map. - */ -inline usize hash_value(const Sha256& sha) -{ - usize seed = 0xf345f9e32e60e535ull; - boost::hash_range(seed, sha.bytes.begin(), sha.bytes.end()); - return seed; -} - -//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - -// -/** \brief Equality-comparison of Sha256 objects. - */ -inline bool operator==(const Sha256& l, const Sha256& r) -{ - return !std::memcmp(l.bytes.data(), r.bytes.data(), l.bytes.size()); -} - -BATT_EQUALITY_COMPARABLE((inline), Sha256, Sha256) - -//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - -// -/** \brief Order-comparison of Sha256 objects. Uses the byte-wise lexicographical ordering given by - * std::memcpy (i.e., binary dictionary order). - */ -inline bool operator<(const Sha256& l, const Sha256& r) -{ - return std::memcmp(l.bytes.data(), r.bytes.data(), l.bytes.size()) < 0; -} - -BATT_TOTALLY_ORDERED((inline), Sha256, Sha256) - -//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - -// -/** \brief Performs bounds-checking prior to casting a raw pointer to a (packed) Sha256. - */ -inline batt::Status validate_packed_value(const Sha256& packed, const void* buffer_data, - usize buffer_size) -{ - BATT_REQUIRE_OK(llfs::validate_packed_struct(packed, buffer_data, buffer_size)); - - return batt::OkStatus(); -} - -//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - -// -BATT_SUPPRESS_IF_GCC("-Wdeprecated-declarations") - -/** \brief Computes and returns the SHA-256 hash of the data contained in the passed Seq of - * ConstBuffer. - */ -template >> -Sha256 compute_sha256(ConstBufferSeq&& buffers) -{ - Sha256 hash; - SHA256_CTX ctx; - - SHA256_Init(&ctx); - - for (;;) { - batt::Optional buffer = buffers.next(); - if (!buffer) { - break; - } - SHA256_Update(&ctx, buffer->data(), buffer->size()); - } - - SHA256_Final(hash.bytes.data(), &ctx); - return hash; -} - -/** \brief Computes and returns the SHA-256 hash of the data contained in the passed buffer. - */ -inline Sha256 compute_sha256(const batt::ConstBuffer& single_buffer) -{ - return compute_sha256(batt::seq::single_item(single_buffer) // - | batt::seq::decayed()); -} - -BATT_UNSUPPRESS_IF_GCC() - -} // namespace llfs - -#endif // LLFS_SHA256_HPP diff --git a/src/llfs/sha256.test.cpp b/src/llfs/sha256.test.cpp deleted file mode 100644 index 9ea1b859..00000000 --- a/src/llfs/sha256.test.cpp +++ /dev/null @@ -1,177 +0,0 @@ -//#=##=##=#==#=#==#===#+==#+==========+==+=+=+=+=+=++=+++=+++++=-++++=-+++++++++++ -// -// Part of the LLFS Project, under Apache License v2.0. -// See https://www.apache.org/licenses/LICENSE-2.0 for license information. -// SPDX short identifier: Apache-2.0 -// -//+++++++++++-+-+--+----- --- -- - - - - - -#include -// -#include - -#include -#include - -#include - -#include - -namespace { - -//=#=#==#==#===============+=+=+=+=++=++++++++++++++-++-+--+-+----+--------------- -// -/** \brief Test fixture for Sha256 tests. - */ -class Sha256Test : public ::testing::Test -{ - public: - /** \brief A hash set of Sha256 values. - */ - std::unordered_set> sha_set; - - /** \brief Calculate the SHA-256 of a known string from multiple parts. - */ - llfs::Sha256 sha_a1 = llfs::compute_sha256(batt::as_seq(std::vector{ - boost::asio::buffer("hello,"), - boost::asio::buffer(" world"), - })); - - /** \brief Calculate the SHA-256 of a known string from a single part. - */ - llfs::Sha256 sha_a2 = llfs::compute_sha256(boost::asio::buffer("hello,\0 world")); - - /** \brief Calculate the SHA-256 of a different known string. - */ - llfs::Sha256 sha_b = llfs::compute_sha256(boost::asio::buffer("adios, amigos!")); -}; - -//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - -// -TEST_F(Sha256Test, ToString) -{ - EXPECT_THAT(batt::to_string(this->sha_a1), - ::testing::StrEq("7bf81346f91f572c21001dfab42a8471842e3c32f16ce699253c18ef7e9986a6")); - - EXPECT_THAT(batt::to_string(this->sha_a2), - ::testing::StrEq("7bf81346f91f572c21001dfab42a8471842e3c32f16ce699253c18ef7e9986a6")); - - EXPECT_THAT(batt::to_string(this->sha_b), - ::testing::StrEq("22891753903ba82d35cb14e4de73b1f6b6a49dd75676c12e1568e652e1f80a01")); -} - -//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - -// -TEST_F(Sha256Test, Equality) -{ - EXPECT_EQ(sha_a1, sha_a2); - EXPECT_EQ(sha_a2, sha_a1); - EXPECT_NE(sha_b, sha_a1); - EXPECT_NE(sha_a2, sha_b); -} - -//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - -// -TEST_F(Sha256Test, Ordering) -{ - EXPECT_LE(sha_a1, sha_a2); - EXPECT_LE(sha_a2, sha_a1); - EXPECT_GE(sha_a1, sha_a2); - EXPECT_GE(sha_a2, sha_a1); - EXPECT_LT(sha_b, sha_a1); - EXPECT_LE(sha_b, sha_a1); - EXPECT_GT(sha_a1, sha_b); - EXPECT_GE(sha_a1, sha_b); -} - -//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - -// -TEST_F(Sha256Test, HashValue) -{ - // Insert sha_a1 into the set. - // - EXPECT_EQ(this->sha_set.count(sha_a1), 0u); - - this->sha_set.insert(sha_a1); - - EXPECT_EQ(this->sha_set.size(), 1u); - EXPECT_EQ(this->sha_set.count(sha_a1), 1u); - - // Insert sha_a2 into the set; because it is the same as sha_a1, expect no changes. - // - this->sha_set.insert(sha_a2); - - EXPECT_EQ(this->sha_set.size(), 1u); - EXPECT_EQ(this->sha_set.count(sha_a2), 1u); - - // Insert sha_b into the set. - // - EXPECT_EQ(this->sha_set.count(sha_b), 0u); - - this->sha_set.insert(sha_b); - - EXPECT_EQ(this->sha_set.size(), 2u); - EXPECT_EQ(this->sha_set.count(sha_b), 1u); -} - -//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - -// -TEST_F(Sha256Test, FromString) -{ - batt::Optional maybe_sha_a3 = llfs::Sha256::from_str(batt::to_string(sha_a1)); - - ASSERT_TRUE(maybe_sha_a3); - - const llfs::Sha256& sha_a3 = *maybe_sha_a3; - - EXPECT_EQ(sha_a3, sha_a1); - - // From upper case. - // - batt::Optional maybe_sha_b2 = - llfs::Sha256::from_str("22891753903BA82D35CB14E4DE73B1F6B6A49DD75676C12E1568E652E1F80A01"); - - const llfs::Sha256& sha_b2 = *maybe_sha_b2; - - EXPECT_EQ(sha_b2, sha_b); - - // Negative case 1: bad character. - // - batt::Optional bad_1 = - llfs::Sha256::from_str("22891753903ba82d35cb14e4de73b1f6b6a49dd75676_12e1568e652e1f80a01"); - - EXPECT_FALSE(bad_1); - - // Negative case 2a: too short, one char. - // - batt::Optional bad_2 = - llfs::Sha256::from_str("22891753903ba82d35cb14e4de73b1f6b6a49dd75676c12e1568e652e1f80a0"); - - EXPECT_FALSE(bad_2); - - // Negative case 2b: too short, one digit. - // - batt::Optional bad_3 = - llfs::Sha256::from_str("22891753903ba82d35cb14e4de73b1f6b6a49dd75676c12e1568e652e1f80a"); - - EXPECT_FALSE(bad_3); - - // Negative case 2b: too short, empty string - // - batt::Optional bad_4 = llfs::Sha256::from_str(""); - - EXPECT_FALSE(bad_4); -} - -//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - -// -TEST_F(Sha256Test, AsKey) -{ - std::string_view key = this->sha_a1.as_key(); - - EXPECT_EQ(key.size(), 32u); - EXPECT_EQ(std::memcmp(key.data(), &this->sha_a1, key.size()), 0); - EXPECT_EQ((const void*)key.data(), (const void*)(&this->sha_a1)); -} - -} // namespace diff --git a/src/llfs/stable_string_store.cpp b/src/llfs/stable_string_store.cpp deleted file mode 100644 index 7c69a2f0..00000000 --- a/src/llfs/stable_string_store.cpp +++ /dev/null @@ -1,14 +0,0 @@ -//#=##=##=#==#=#==#===#+==#+==========+==+=+=+=+=+=++=+++=+++++=-++++=-+++++++++++ -// -// Part of the LLFS Project, under Apache License v2.0. -// See https://www.apache.org/licenses/LICENSE-2.0 for license information. -// SPDX short identifier: Apache-2.0 -// -//+++++++++++-+-+--+----- --- -- - - - - - -#include -// - -namespace llfs { - -} // namespace llfs diff --git a/src/llfs/stable_string_store.hpp b/src/llfs/stable_string_store.hpp deleted file mode 100644 index e981b258..00000000 --- a/src/llfs/stable_string_store.hpp +++ /dev/null @@ -1,221 +0,0 @@ -//#=##=##=#==#=#==#===#+==#+==========+==+=+=+=+=+=++=+++=+++++=-++++=-+++++++++++ -// -// Part of the LLFS Project, under Apache License v2.0. -// See https://www.apache.org/licenses/LICENSE-2.0 for license information. -// SPDX short identifier: Apache-2.0 -// -//+++++++++++-+-+--+----- --- -- - - - - - -#pragma once -#ifndef LLFS_STABLE_STRING_STORE_HPP -#define LLFS_STABLE_STRING_STORE_HPP - -#include -#include -#include - -#include -#include -#include - -#include -#include - -#include -#include -#include -#include -#include - -namespace llfs { - -//=#=#==#==#===============+=+=+=+=++=++++++++++++++-++-+--+-+----+--------------- -/** \brief A class that allows the user to efficiently allocate and copy string data in memory that - * is scoped to the lifetime of the object itself. - */ -template -class BasicStableStringStore -{ - public: - using StorageUnit = std::aligned_storage_t<64, 64>; - - static constexpr usize kUnitSizeLog2 = batt::log2_ceil(sizeof(StorageUnit)); - - static constexpr usize kStaticSizeLog2 = std::max(kUnitSizeLog2, // - batt::log2_ceil(kStaticSize)); - static constexpr usize kDynamicSizeLog2 = std::max(kUnitSizeLog2, // - batt::log2_ceil(kDynamicSize)); - - static constexpr usize kStaticAllocSize = usize{1} << kStaticSizeLog2; - static constexpr usize kDynamicAllocSize = usize{1} << kDynamicSizeLog2; - - static constexpr usize kStaticAllocUnitsLog2 = (kStaticSizeLog2 - kUnitSizeLog2); - static constexpr usize kDynamicAllocUnitsLog2 = (kDynamicSizeLog2 - kUnitSizeLog2); - - static constexpr usize kStaticAllocUnits = usize{1} << kStaticAllocUnitsLog2; - static constexpr usize kDynamicAllocUnits = usize{1} << kDynamicAllocUnitsLog2; - - //+++++++++++-+-+--+----- --- -- - - - - - - BasicStableStringStore(); - - BasicStableStringStore(const BasicStableStringStore&) = delete; - BasicStableStringStore& operator=(const BasicStableStringStore&) = delete; - - /** \brief Allocates a buffer of size `n` bytes. - */ - MutableBuffer allocate(usize n); - - /** \brief Copies the given `string_view` into a memory location managed by this - * `BasicStableStringStore` instance, and returns a `string_view` pointing to the stored data. The - * `worker_pool`, if provided, is used the parallelize the copying process if necessary. - */ - std::string_view store(const std::string_view& s, - batt::WorkerPool& worker_pool = batt::WorkerPool::null_pool()); - - /** \brief Copies the given `ConstBuffer` into a memory location managed by this - * `BasicStableStringStore` instance as string data, and returns a `ConstBuffer` pointing to the - * stored data. - */ - ConstBuffer store(const ConstBuffer& buffer, - batt::WorkerPool& worker_pool = batt::WorkerPool::null_pool()) - { - const std::string_view s = this->store( - std::string_view{static_cast(buffer.data()), buffer.size()}, worker_pool); - - return ConstBuffer{s.data(), s.size()}; - } - - /** \brief Concatenates multiple chunks of data and copies the concatenation into a contiguous - * buffer of memory. - */ - template - ConstBuffer concat(Parts&&... parts) - { - usize total_size = 0; - - // Compute the total amount of memory needed to be allocated for the result of the - // concatenation. - // - const auto add_to_total = [&total_size](auto&& part) { - total_size += batt::as_const_buffer(part).size(); - return 0; - }; - - (add_to_total(parts), ...); - - MutableBuffer mbuf = this->allocate(total_size); - MutableBuffer cbuf = mbuf; - - // Copy each part to memory. - // - const auto copy_part = [&mbuf](auto&& part) { - auto src = batt::as_const_buffer(part); - std::memcpy(mbuf.data(), src.data(), src.size()); - mbuf += src.size(); - return 0; - }; - - (copy_part(parts), ...); - - return cbuf; - } - - private: - /** \brief The statically allocated block of memory that is initialized when this - * `BasicStableStringStore` instance is created, used as a starting point for memory allocations - * done by this instance. - */ - std::array chunk0_; - - /** \brief A collection of dynamically allocated memory blocks, managing the chunks allocated - * beyond `chunk0_`. - */ - std::vector> chunks_; - - /** \brief A buffer representing the current chunk of memory that has free space available for - * allocation. - */ - MutableBuffer free_chunk_; -}; - -using StableStringStore = BasicStableStringStore<64, 4096>; - -//#=##=##=#==#=#==#===#+==#+==========+==+=+=+=+=+=++=+++=+++++=-++++=-+++++++++++ - -//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - -// -template -BasicStableStringStore::BasicStableStringStore() - : free_chunk_{std::addressof(this->chunk0_), sizeof(this->chunk0_)} -{ -} - -//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - -// -template -MutableBuffer BasicStableStringStore::allocate(usize n) -{ - // Check if the current free_chunk_ is large enough to hold n bytes. If it isn't, we need to - // dynamically allocate a new chunk. - // - if (this->free_chunk_.size() < n) { - // Allocate new chunk, add it to the list of dynamically allocated chunks, and point free_chunk_ - // to this new chunk. - // - const usize new_chunk_size = batt::round_up_bits(kDynamicSizeLog2, n); - const usize n_units = new_chunk_size >> kUnitSizeLog2; - std::unique_ptr new_chunk{new StorageUnit[n_units]}; - char* const new_chunk_data = (char*)new_chunk.get(); - this->chunks_.emplace_back(std::move(new_chunk)); - this->free_chunk_ = MutableBuffer{new_chunk_data, new_chunk_size}; - } - - BATT_CHECK_GE(this->free_chunk_.size(), n); - - // Return the newly allocated chunk and advance the start of the free_chunk_ buffer by n bytes to - // indicate that this region of memory is now occupied. - // - MutableBuffer stable_buffer{this->free_chunk_.data(), n}; - this->free_chunk_ += n; - return stable_buffer; -} - -//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - -// -template -std::string_view BasicStableStringStore::store( - const std::string_view& s, batt::WorkerPool& worker_pool) -{ - // Allocate a buffer the size of the input string data. - // - MutableBuffer stable_buffer = this->allocate(s.size()); - - BATT_CHECK_EQ(stable_buffer.size(), s.size()); - - // Check if we would benefit from parallelizing the copying process. If we do have workers in the - // worker_pool and the size of the string data isn't too small, parallelize. - // - if (worker_pool.size() == 0 || s.size() < llfs::DataPacker::min_parallel_copy_size()) { - std::memcpy(stable_buffer.data(), s.data(), s.size()); - } else { - batt::ScopedWorkContext work_context{worker_pool}; - - const batt::TaskCount max_tasks{worker_pool.size() + 1}; - const batt::TaskSize min_task_size{llfs::DataPacker::min_parallel_copy_size()}; - - const char* const src_begin = s.data(); - const char* const src_end = src_begin + s.size(); - char* const dst_begin = static_cast(stable_buffer.data()); - - batt::parallel_copy(work_context, src_begin, src_end, dst_begin, min_task_size, max_tasks); - } - - // Return the copy. - // - return std::string_view{static_cast(stable_buffer.data()), stable_buffer.size()}; -} - -} // namespace llfs - -#endif // LLFS_STABLE_STRING_STORE_HPP diff --git a/src/llfs/stable_string_store.test.cpp b/src/llfs/stable_string_store.test.cpp deleted file mode 100644 index 426b0313..00000000 --- a/src/llfs/stable_string_store.test.cpp +++ /dev/null @@ -1,91 +0,0 @@ -//#=##=##=#==#=#==#===#+==#+==========+==+=+=+=+=+=++=+++=+++++=-++++=-+++++++++++ -// -// Part of the LLFS Project, under Apache License v2.0. -// See https://www.apache.org/licenses/LICENSE-2.0 for license information. -// SPDX short identifier: Apache-2.0 -// -//+++++++++++-+-+--+----- --- -- - - - - - -#include -// - -#include -#include - -#include - -namespace { - -using namespace batt::int_types; - -TEST(StableStringStore, StaticAllocationTest) -{ - llfs::StableStringStore strings; - - auto out = - strings.concat(std::string_view{"Hello"}, std::string_view{", "}, std::string_view{"World!"}); - - std::string_view out_str{(const char*)out.data(), out.size()}; - - EXPECT_THAT(out_str, ::testing::StrEq("Hello, World!")); - - // Since "Hello, World!" is less than kStaticAllocSize, test to see that the string's data was - // allocated statically, i.e., it "lives" inside the bounds of the StableStringStore object - // itself. - // - EXPECT_TRUE(out.data() >= static_cast(&strings) && - out.data() < static_cast(&strings + 1)); -} - -TEST(StableStringStore, DynamicAllocationTest) -{ - llfs::StableStringStore strings; - const usize data_size = 1; - const usize num_iterations_of_static_alloc = strings.kStaticAllocSize / data_size; - - // Statically allocate a bunch of string data up to the static allocation limit. - // - for (usize i = 0; i < num_iterations_of_static_alloc; ++i) { - std::string_view string_to_store{"a"}; - std::string_view copied_string = strings.store(string_to_store); - EXPECT_TRUE( - static_cast(copied_string.data()) >= static_cast(&strings) && - static_cast(copied_string.data()) < static_cast(&strings + 1)); - } - - // Now perform another store. Since we have already allocated an amount of data greater than the - // size of kStaticAllocSize, we end up dynmically allocating the data for this string. - // - std::string_view dynamically_allocated_string{"b"}; - std::string_view copy_stored = strings.store(dynamically_allocated_string); - EXPECT_TRUE(static_cast(copy_stored.data()) < static_cast(&strings) || - static_cast(copy_stored.data()) >= - static_cast(&strings + 1)); -} - -TEST(StableStringStore, LargeDynamicAllocationTest) -{ - llfs::StableStringStore strings; - const usize data_size = strings.kDynamicAllocSize + 1; - const usize num_allocations = 10; - - // Allocate large strings, all with a size greater that kDynamicAllocSize. This will trigger - // multiple dynamic memory allocations. - // - std::string_view previous_string; - for (usize i = 0; i < num_allocations; ++i) { - if (i > 0) { - // Check to make sure that the memory for previously allocated strings doesn't go out of - // scope; memory of the string data is scoped to the lifetime of the StableStringObject. - // - std::string expected_previous_string(data_size, 'a' + (i - 1)); - EXPECT_EQ(previous_string, expected_previous_string); - } - - std::string large_string_data(data_size, 'a' + i); - std::string_view string_to_store{large_string_data}; - previous_string = strings.store(string_to_store); - } -} - -} // namespace diff --git a/src/llfs/trie.cpp b/src/llfs/trie.cpp deleted file mode 100644 index 2ca0e1da..00000000 --- a/src/llfs/trie.cpp +++ /dev/null @@ -1,612 +0,0 @@ -//#=##=##=#==#=#==#===#+==#+==========+==+=+=+=+=+=++=+++=+++++=-++++=-+++++++++++ -// -// Part of the LLFS Project, under Apache License v2.0. -// See https://www.apache.org/licenses/LICENSE-2.0 for license information. -// SPDX short identifier: Apache-2.0 -// -//+++++++++++-+-+--+----- --- -- - - - - - -#include -// - -#include - -#include - -namespace llfs { - -namespace { - -//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - -// -template -usize packed_sizeof_bp_trie_node(const BPTrieNode* node, batt::Interval range, i64& space) -{ - if (!node) { - return 0; - } - - const usize prefix_len = node->prefix_.size(); - - usize node_size = prefix_len + (prefix_len + PackedBPTrie::kMaxPrefixChunkLen + 1) / - PackedBPTrie::kMaxPrefixChunkLen; - - usize left_subtree_size = 0; - usize right_subtree_size = 0; - - if (node->left_) { - BATT_CHECK_NOT_NULLPTR(node->right_); - - // pivot (always u8) - // - node_size += 1; - - // pivot_pos - // - const usize pivot_pos_size = batt::log2_ceil((usize)range.size()) / 8 + 1; - BATT_CHECK_LT(range.size(), 1 << (pivot_pos_size * 8)); - node_size += pivot_pos_size; - - // left, right pointers - // - node_size += sizeof(PackedPointer) * 2; - - space -= (i64)node_size; - if (space >= 0) { - const usize middle = range.lower_bound + node->pivot_pos_; - - left_subtree_size = packed_sizeof_bp_trie_node( - node->left_, batt::Interval{range.lower_bound, middle}, space); - - right_subtree_size = packed_sizeof_bp_trie_node( - node->right_, batt::Interval{middle, range.upper_bound}, space); - } - } else { - BATT_CHECK_EQ(node->right_, nullptr); - space -= (i64)node_size; - } - return node_size + left_subtree_size + right_subtree_size; -} - -} //namespace - -//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - -// -usize BPTrie::packed_size() const -{ - u64 observed = this->packed_size_.load(); - if (observed != kInvalidPackedSize) { - return observed; - } - - const BPTrie& object = *this; - batt::Interval range{0, object.size()}; - - const BPTrieNode* root = object.root(); - - i64 space = i64{1} << 8; - u64 z = sizeof(PackedBPTrie) + packed_sizeof_bp_trie_node(root, range, space); - if (space < 0) { - space = i64{1} << 16; - z = sizeof(PackedBPTrie) + packed_sizeof_bp_trie_node(root, range, space); - if (space < 0) { - space = i64{1} << 24; - z = sizeof(PackedBPTrie) + packed_sizeof_bp_trie_node(root, range, space); - if (space < 0) { - space = i64{1} << 32; - z = sizeof(PackedBPTrie) + packed_sizeof_bp_trie_node(root, range, space); - BATT_CHECK_GE(space, 0); - } - } - } - - this->packed_size_.compare_exchange_strong(observed, z); - - return z; -} - -//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - -// -usize packed_sizeof(const BPTrie& object) -{ - return object.packed_size(); -} - -//=#=#==#==#===============+=+=+=+=++=++++++++++++++-++-+--+-+----+--------------- - -namespace { - -template -struct QueueItem { - const BPTrieNode* node; - PackedPointer* p_pointer; - batt::Interval range; -}; - -template -PackedBPTrie* build_packed_trie(const BPTrie& object, PackedBPTrie* packed, DataPacker* dst) -{ - using PackedNodePointer = PackedPointer; - - const BPTrieNode* root = object.root(); - - Queue queue; - queue.push(root, nullptr, batt::Interval{0, packed->size_}); - - while (!queue.empty()) { - QueueItem next = queue.pop(); - - const BPTrieNode* node = next.node; - PackedNodePointer* p_pointer = next.p_pointer; - - PackedBPTrieNodeBase* packed_node = dst->pack_record(); - if (!packed_node) { - return nullptr; - } - - if (p_pointer) { - p_pointer->reset(packed_node, dst); - BATT_CHECK_EQ(packed_node, p_pointer->get()); - } - - // Encode prefix. - { - const u8 mask = (node->left_) ? PackedBPTrie::kParentNodeMask : 0x00; - - const u8* prefix_data = (const u8*)node->prefix_.data(); - usize prefix_len = node->prefix_.size(); - - while (prefix_len >= PackedBPTrie::kMaxPrefixChunkLen) { - if (!dst->pack_raw_data(prefix_data, PackedBPTrie::kMaxPrefixChunkLen)) { - return nullptr; - } - packed_node->header = - (PackedBPTrie::kMaxPrefixChunkLen & PackedBPTrie::kPrefixChunkLenMask) | mask; - prefix_data += PackedBPTrie::kMaxPrefixChunkLen; - prefix_len -= PackedBPTrie::kMaxPrefixChunkLen; - packed_node = dst->pack_record(); - if (!packed_node) { - return nullptr; - } - } - - packed_node->header = (prefix_len & PackedBPTrie::kPrefixChunkLenMask) | mask; - if (prefix_len) { - if (!dst->pack_raw_data(prefix_data, prefix_len)) { - return nullptr; - } - } - } - - // If leaf, continue. - // - if (!node->left_) { - BATT_CHECK_EQ(node->right_, nullptr); - continue; - } - - BATT_CHECK_NOT_NULLPTR(node->right_); - - // Encode parent fields and push left/right subtrees. - // - const auto pack_parent = [&](auto parent_layout) { - using ParentLayout = typename decltype(parent_layout)::type; - - ParentLayout* parent = dst->pack_record(); - if (!parent) { - return parent; - } - - parent->pivot = node->pivot_; - parent->pivot_pos = node->pivot_pos_; - - BATT_CHECK_EQ(node->pivot_pos_, parent->pivot_pos); - - usize middle = next.range.lower_bound + node->pivot_pos_; - - queue.push(node->left_, &parent->left, batt::Interval{next.range.lower_bound, middle}); - queue.push(node->right_, &parent->right, - batt::Interval{middle, next.range.upper_bound}); - - return parent; - }; - - if (next.range.size() <= isize{0xff}) { - if (!pack_parent(batt::StaticType>{})) { - return nullptr; - } - } else if (next.range.size() <= isize{0xffff}) { - if (!pack_parent(batt::StaticType>{})) { - return nullptr; - } - } else if (next.range.size() <= isize{0xffffff}) { - if (!pack_parent(batt::StaticType>{})) { - return nullptr; - } - } else if (next.range.size() <= isize{0xffffffff}) { - if (!pack_parent(batt::StaticType>{})) { - return nullptr; - } - } - } - - return packed; -} - -//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - -// -template -PackedBPTrie* pack_trie_impl(const BPTrie& object, PackedBPTrie* packed, DataPacker* dst) -{ - switch (object.get_packed_layout()) { - case BPTrie::PackedLayout::kBreadthFirst: - return build_packed_trie>>( - object, packed, dst); - - case BPTrie::PackedLayout::kVanEmdeBoas: - return build_packed_trie>>( - object, packed, dst); - - default: - break; - } - return nullptr; -} - -} //namespace - -//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - -// -const PackedBPTrie* pack_object_to(const BPTrie& object, PackedBPTrie* packed, - llfs::DataPacker* dst) -{ - packed->size_ = object.size(); - - const BPTrieNode* root = object.root(); - if (!root) { - return packed; - } - - const usize packed_byte_size = packed_sizeof(object); - if (packed_byte_size <= 0xff) { - packed->offset_kind_ = PackedBPTrie::kOffset8; - return pack_trie_impl(object, packed, dst); - - } else if (packed_byte_size <= 0xffff) { - packed->offset_kind_ = PackedBPTrie::kOffset16; - return pack_trie_impl(object, packed, dst); - - } else if (packed_byte_size <= 0xffffff) { - packed->offset_kind_ = PackedBPTrie::kOffset24; - return pack_trie_impl(object, packed, dst); - - } else { - packed->offset_kind_ = PackedBPTrie::kOffset32; - return pack_trie_impl(object, packed, dst); - } -} - -//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - -// -batt::Interval BPTrie::find(std::string_view key) const noexcept -{ - batt::Interval range{0, this->size()}; - - const BPTrieNode* node = this->root(); - if (!node) { - return range; - } - - for (;;) { - const std::string_view& prefix = node->prefix_; - const usize prefix_len = prefix.size(); - if (prefix_len != 0) { - const usize key_len = key.size(); - const usize common_len = std::min(prefix_len, key_len); - const batt::Order order = batt::compare(key.substr(0, common_len), prefix); - - if (order == batt::Order::Greater) { - range.lower_bound = range.upper_bound; - break; - } - if (order == batt::Order::Less || key_len < prefix_len) { - range.upper_bound = range.lower_bound; - break; - } - } - - if (!node->left_) { - range.upper_bound = range.lower_bound + 1; - break; - } - - const usize middle = range.lower_bound + node->pivot_pos_; - const u8 parent_pivot = node->pivot_; - - key = key.substr(prefix_len); - - if (key.empty() || (u8)key[0] < parent_pivot) { - node = node->left_; - range.upper_bound = middle; - } else { - node = node->right_; - - // Implement right-leaf optimization (the parent pivot is always prefix[0] in this case). - // - if (!node->left_) { - if ((u8)key[0] != parent_pivot) { - range.lower_bound = range.upper_bound; - return range; - } - key = key.substr(1); - } - - range.lower_bound = middle; - } - } - - return range; -} - -//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - -// -std::string_view BPTrie::get_key(usize index, batt::SmallVecBase& buffer) const noexcept -{ - buffer.clear(); - batt::Interval range{0, this->size_}; - const BPTrieNode* node = this->root_; - - for (;;) { - if (!node) { - break; - } - - buffer.insert(buffer.end(), node->prefix_.data(), node->prefix_.data() + node->prefix_.size()); - - const usize middle = range.lower_bound + node->pivot_pos_; - - if (index < middle) { - node = node->left_; - range.upper_bound = middle; - } else { - const char parent_pivot = (char)node->pivot_; - node = node->right_; - - // Implement right-leaf optimization (the parent pivot is always prefix[0] in this case). - // - if (node && !node->left_) { - buffer.push_back(parent_pivot); - buffer.insert(buffer.end(), node->prefix_.data(), - node->prefix_.data() + node->prefix_.size()); - break; - } - - range.lower_bound = middle; - } - } - - return std::string_view{buffer.data(), buffer.size()}; -} - -//=#=#==#==#===============+=+=+=+=++=++++++++++++++-++-+--+-+----+--------------- -namespace { - -template -struct NextSmaller; - -template <> -struct NextSmaller : batt::StaticType { -}; - -template <> -struct NextSmaller : batt::StaticType { -}; - -template <> -struct NextSmaller : batt::StaticType { -}; - -template <> -struct NextSmaller : batt::StaticType { -}; - -//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - -// -template -batt::Interval find_impl(const PackedBPTrieNodeBase* node, std::string_view& key, - batt::Interval& range, usize& key_prefix_match) -{ - constexpr i64 threshold = i64{1} << (sizeof(typename NextSmaller::type) * 8); - - key_prefix_match = 0; - - for (;;) { - if (!std::is_same_v && range.size() < threshold) { - return find_impl::type, SubtreeOffset>(node, key, range, - key_prefix_match); - } - - usize prefix_chunk_len; - for (;;) { - prefix_chunk_len = node->header & PackedBPTrie::kPrefixChunkLenMask; - if (prefix_chunk_len != 0) { - std::string_view prefix_chunk{node->prefix_, prefix_chunk_len}; - const usize key_len = key.size(); - const usize common_len = std::min(prefix_chunk_len, key_len); - const batt::Order order = batt::compare(key.substr(0, common_len), prefix_chunk); - - if (order == batt::Order::Greater) { - range.lower_bound = range.upper_bound; - return range; - } - if (order == batt::Order::Less || key_len < prefix_chunk_len) { - range.upper_bound = range.lower_bound; - return range; - } - - key = key.substr(prefix_chunk_len); - key_prefix_match += prefix_chunk_len; - - if (prefix_chunk_len == PackedBPTrie::kMaxPrefixChunkLen) { - node = reinterpret_cast(&node->prefix_[prefix_chunk_len]); - continue; - } - } - break; - } - - if ((node->header & PackedBPTrie::kParentNodeMask) == 0) { - range.upper_bound = range.lower_bound + 1; - break; - } - - const auto* parent = reinterpret_cast*>( - &node->prefix_[prefix_chunk_len]); - - const usize middle = range.lower_bound + (usize)parent->pivot_pos; - const u8 parent_pivot = parent->pivot; - - if (key.empty() || (u8)key[0] < parent_pivot) { - node = parent->left.get(); - range.upper_bound = middle; - } else { - node = parent->right.get(); - - // Implement right-leaf optimization (the parent pivot is always prefix[0] in this case). - // - if ((node->header & PackedBPTrie::kParentNodeMask) == 0) { - if ((u8)key[0] != parent_pivot) { - range.lower_bound = range.upper_bound; - return range; - } - key = key.substr(1); - key_prefix_match += 1; - } - - range.lower_bound = middle; - } - } - - return range; -} -} //namespace - -//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - -// -batt::Interval PackedBPTrie::find(std::string_view key, - usize& key_prefix_match) const noexcept -{ - batt::Interval range{0, this->size()}; - - if (range.empty()) { - return range; - } - - const PackedBPTrieNodeBase* node = this->root(); - - switch (this->offset_kind_) { - case PackedBPTrie::kOffset8: - return find_impl(node, key, range, key_prefix_match); - - case PackedBPTrie::kOffset16: - return find_impl(node, key, range, key_prefix_match); - - case PackedBPTrie::kOffset24: - return find_impl(node, key, range, key_prefix_match); - - case PackedBPTrie::kOffset32: - return find_impl(node, key, range, key_prefix_match); - } - - BATT_PANIC() << "Bad offset kind: " << (int)this->offset_kind_; - BATT_UNREACHABLE(); -} - -//=#=#==#==#===============+=+=+=+=++=++++++++++++++-++-+--+-+----+--------------- - -namespace { - -template -std::string_view get_key_impl(const PackedBPTrieNodeBase* node, usize index, - batt::SmallVecBase& buffer, batt::Interval& range) -{ - constexpr i64 threshold = i64{1} << (sizeof(typename NextSmaller::type) * 8); - - for (;;) { - if (!std::is_same_v && range.size() < threshold) { - return get_key_impl::type, SubtreeOffset>(node, index, buffer, - range); - } - - usize prefix_chunk_len; - for (;;) { - prefix_chunk_len = node->header & PackedBPTrie::kPrefixChunkLenMask; - if (prefix_chunk_len != 0) { - buffer.insert(buffer.end(), node->prefix_, node->prefix_ + prefix_chunk_len); - - if (prefix_chunk_len == PackedBPTrie::kMaxPrefixChunkLen) { - node = reinterpret_cast(&node->prefix_[prefix_chunk_len]); - continue; - } - } - break; - } - - if ((node->header & PackedBPTrie::kParentNodeMask) == 0) { - break; - } - - const auto* parent = reinterpret_cast*>( - &node->prefix_[prefix_chunk_len]); - - const usize middle = range.lower_bound + (usize)parent->pivot_pos; - - if (index < middle) { - node = parent->left.get(); - range.upper_bound = middle; - } else { - const char parent_pivot = (char)parent->pivot; - node = parent->right.get(); - - // Implement right-leaf optimization (the parent pivot is always prefix[0] in this case). - // - if ((node->header & PackedBPTrie::kParentNodeMask) == 0) { - buffer.push_back(parent_pivot); - } - - range.lower_bound = middle; - } - } - - return std::string_view{buffer.data(), buffer.size()}; -} - -} //namespace - -//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - -// -std::string_view PackedBPTrie::get_key(usize index, batt::SmallVecBase& buffer) const noexcept -{ - buffer.clear(); - batt::Interval range{0, this->size_}; - const PackedBPTrieNodeBase* node = this->root(); - - switch (this->offset_kind_) { - case PackedBPTrie::kOffset8: - return get_key_impl(node, index, buffer, range); - - case PackedBPTrie::kOffset16: - return get_key_impl(node, index, buffer, range); - - case PackedBPTrie::kOffset24: - return get_key_impl(node, index, buffer, range); - - case PackedBPTrie::kOffset32: - return get_key_impl(node, index, buffer, range); - } - - BATT_PANIC() << "Bad offset kind: " << (int)this->offset_kind_; - BATT_UNREACHABLE(); -} - -} //namespace llfs diff --git a/src/llfs/trie.hpp b/src/llfs/trie.hpp deleted file mode 100644 index 20465f10..00000000 --- a/src/llfs/trie.hpp +++ /dev/null @@ -1,351 +0,0 @@ -//#=##=##=#==#=#==#===#+==#+==========+==+=+=+=+=+=++=+++=+++++=-++++=-+++++++++++ -// -// Part of the LLFS Project, under Apache License v2.0. -// See https://www.apache.org/licenses/LICENSE-2.0 for license information. -// SPDX short identifier: Apache-2.0 -// -//+++++++++++-+-+--+----- --- -- - - - - - -#pragma once -#ifndef LLFS_TRIE_HPP -#define LLFS_TRIE_HPP - -#include -// -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -namespace llfs { - -/** \brief A node within an in-memory BPTrie. This class is used internally by `BPTrie`. - */ -struct BPTrieNode { - std::string_view prefix_; - u8 pivot_ = 0; - usize pivot_pos_ = 0; - usize subtree_node_count_ = 0; - BPTrieNode* left_ = nullptr; - BPTrieNode* right_ = nullptr; -}; - -class BPTrieNodeSet -{ - public: - usize size() const noexcept - { - return this->node_count_; - } - - BPTrieNode* new_node() noexcept - { - this->node_count_ += 1; - return new (this->memory_.allocate(sizeof(BPTrieNode)).data()) BPTrieNode{}; - } - - private: - BasicStableStringStore<4096, 4096> memory_; - - usize node_count_ = 0; -}; - -/** \brief Builds a BPTrie subtree from the given range of keys (std::string_view objects). - * - * All new node objects are allocated at the end of the passed `nodes` vector. This function calls - * itself recursively; this is why it takes optional `current_prefix_len` and `is_right_subtree` - * params. Application code that just wants to turn a sorted range of strings into a BPTrie should - * leave these params set to their implicit defaults (and probably should just use the BPTrie - * constructor). - */ -template -BPTrieNode* make_trie(const Range& keys, BPTrieNodeSet& nodes, usize current_prefix_len = 0, - bool is_right_subtree = false); - -//=#=#==#==#===============+=+=+=+=++=++++++++++++++-++-+--+-+----+--------------- -/** \brief A binary prefix trie - implements an ordered set of strings. - * - * BPTrie is currently a _static_ set implementation; once it has been constructed from an initial - * set of strings, it can not be modified. - * - * IMPORTANT: the string data passed to the BPTrie is *not* owned by the BPTrie itself; rather it is - * stored as std::string_view objects internally. That means that the creator of the BPTrie must - * ensure that the underlying string data stays in scope for at least as long as the BPTrie object; - * otherwise pointers will dangle! - * - * Unlike a traditional prefix trie data structure, whose nodes have a potentially very high - * branching factor despite the density of keys, the binary prefix trie has a constant branching - * factor of 2. This is achieved via a `pivot` field on each node, which partitions the key ranges - * of the left and right sub-trie. It also supports key prefix compression via the prefix member. - */ -class BPTrie -{ - public: - /** \brief Controls the order in which BPTrie nodes are packed. - */ - enum struct PackedLayout { - - /** \brief Specifies that nodes should be packed in BFS order (i.e., the typical binary heap - * ordering). - */ - kBreadthFirst = 0, - - /** \brief Specifies that nodes should be packed in vEB order, which minimizes the average - * distance between parent and child nodes, therefore optimizing for locality during key search - * regardless of cache level/block-size (i.e., it is "Cache-Oblivious"). - */ - kVanEmdeBoas = 1, - }; - - //+++++++++++-+-+--+----- --- -- - - - - - - /** \brief Constructs a BPTrie from a sorted, unique range of std::string_view keys. - * - * If the passed range is *not* sorted or the elements are not unique, behavior is undefined! - * - * IMPORTANT: the caller must ensure that all string data remains in-scope for the lifetime of - * this object, as only string pointers (std::string_view) are stored within the BPTrie. - */ - template - explicit BPTrie(const Range& keys) - : nodes_{} - , root_{make_trie(keys, this->nodes_)} - , size_{std::size(keys)} - { - BATT_CHECK_NOT_NULLPTR(this->root_); - BATT_CHECK_EQ(this->root_->subtree_node_count_, this->nodes_.size()); - } - - /** \brief BPTrie is a move-only type. - */ - BPTrie(const BPTrie&) = delete; - - /** \brief BPTrie is a move-only type. - */ - BPTrie& operator=(const BPTrie&) = delete; - - /** \brief BPTrie is a move-only type. - */ - BPTrie(BPTrie&&) = default; - - /** \brief BPTrie is a move-only type. - */ - BPTrie& operator=(BPTrie&&) = default; - - //+++++++++++-+-+--+----- --- -- - - - - - - /** \brief Returns the root node of the trie. - */ - const BPTrieNode* root() const noexcept - { - return this->root_; - } - - /** \brief Returns the number of nodes in the trie. - */ - usize node_count() const noexcept - { - return this->nodes_.size(); - } - - /** \brief Returns the number of strings in the set. - */ - usize size() const noexcept - { - return this->size_; - } - - /** \brief Returns the packed size of the trie. - */ - usize packed_size() const; - - /** \brief Returns an interval of indices into the original range used to construct `this`; this - * interval is the set of strings which are equal to the passed key. If the returned interval is - * non-empty, the key is found and the lower_bound is its position in the original set. If the - * returned interval is empty, then lower_bound is the place `key` would have been inserted in the - * original range. - */ - batt::Interval find(std::string_view key) const noexcept; - - /** \brief Changes the node order used to pack this object. The BFS layout is still supported - * mainly to be able to compare it to the more search-optimized vEB (default) layout, although - * YMMV and there may be workloads for which BFS layout performs better. - */ - void set_packed_layout(PackedLayout layout) noexcept - { - this->layout_ = layout; - } - - /** \brief Returns the current node packing order. - */ - PackedLayout get_packed_layout() const noexcept - { - return this->layout_; - } - - /** \brief Recovers and returns the original key at the given index. - * - * The trie data structure, by its nature, does not store source strings (keys) directly; rather, - * it stores the set as connected string fragments. This function builds up the full string at a - * given index, using the passed `buffer` as the underlying string storage. The returned - * std::string_view will point into `buffer`. - */ - std::string_view get_key(usize index, batt::SmallVecBase& buffer) const noexcept; - - //+++++++++++-+-+--+----- --- -- - - - - - private: - static constexpr u64 kInvalidPackedSize = ~u64{0}; - - BPTrieNodeSet nodes_; - BPTrieNode* root_; - usize size_ = 0; - PackedLayout layout_ = PackedLayout::kVanEmdeBoas; - mutable std::atomic packed_size_{kInvalidPackedSize}; -}; - -//=#=#==#==#===============+=+=+=+=++=++++++++++++++-++-+--+-+----+--------------- - -/** \brief The common layout for all packed trie nodes; internal use only. - */ -struct PackedBPTrieNodeBase { - PackedBPTrieNodeBase(const PackedBPTrieNodeBase&) = delete; - PackedBPTrieNodeBase& operator=(const PackedBPTrieNodeBase&) = delete; - - /** \brief Defines the node type and prefix length. - * - * The most significant bit, if set, indicates that this is a non-leaf (i.e. "parent") node. The - * lower 7 bits are the size of prefix_; if this size is less than the maximum (127), then this is - * the final segment of the prefix string. Otherwise, another PackedBPTrieNodeBase follows this - * one, with a continuation of the prefix string data (even if it is zero-sized). - * - * The MSB must be set consistently (all 0x80 or all 0x00) on *all* such adjacent prefix segments - * for the same node. - */ - u8 header; - - /** \brief Prefix char data. See comment for PackedBPTrieNodeBase::header. - */ - char prefix_[0]; -}; - -BATT_STATIC_ASSERT_EQ(sizeof(PackedBPTrieNodeBase), 1); - -/** \brief The packed layout of non-leaf trie nodes; this layout comes immediately after - * `PackedBPTrieNodeBase` (plus any prefix_ bytes). - */ -template -struct PackedBPTrieNodeParent { - /** \brief Partitions the left and right sub-tries; the least-ordered string in the right sub-trie - * is the least-ordered string in the set that begins with `pivot`. - */ - u8 pivot; - - /** \brief The index (within the sub-trie range) of the least-ordered string that begins with - * `pivot`. - */ - PivotPos pivot_pos; - - /** \brief Points to the left sub-trie root node. - */ - PackedPointer left; - - /** \brief Points to the right sub-trie root node. - */ - PackedPointer right; -}; - -BATT_STATIC_ASSERT_EQ(sizeof(PackedPointer), 1); -BATT_STATIC_ASSERT_EQ(sizeof(PackedPointer), 2); -BATT_STATIC_ASSERT_EQ(sizeof(PackedPointer), 3); -BATT_STATIC_ASSERT_EQ(sizeof(PackedPointer), 4); - -BATT_STATIC_ASSERT_EQ(sizeof(PackedBPTrieNodeParent), 1 + 1 + 1 + 1); -BATT_STATIC_ASSERT_EQ(sizeof(PackedBPTrieNodeParent), 1 + 1 + 2 + 2); -BATT_STATIC_ASSERT_EQ(sizeof(PackedBPTrieNodeParent), 1 + 1 + 3 + 3); -BATT_STATIC_ASSERT_EQ(sizeof(PackedBPTrieNodeParent), 1 + 1 + 4 + 4); - -BATT_STATIC_ASSERT_EQ(sizeof(PackedBPTrieNodeParent), 1 + 2 + 1 + 1); -BATT_STATIC_ASSERT_EQ(sizeof(PackedBPTrieNodeParent), 1 + 2 + 2 + 2); -BATT_STATIC_ASSERT_EQ(sizeof(PackedBPTrieNodeParent), 1 + 2 + 3 + 3); -BATT_STATIC_ASSERT_EQ(sizeof(PackedBPTrieNodeParent), 1 + 2 + 4 + 4); - -BATT_STATIC_ASSERT_EQ(sizeof(PackedBPTrieNodeParent), 1 + 3 + 1 + 1); -BATT_STATIC_ASSERT_EQ(sizeof(PackedBPTrieNodeParent), 1 + 3 + 2 + 2); -BATT_STATIC_ASSERT_EQ(sizeof(PackedBPTrieNodeParent), 1 + 3 + 3 + 3); -BATT_STATIC_ASSERT_EQ(sizeof(PackedBPTrieNodeParent), 1 + 3 + 4 + 4); - -BATT_STATIC_ASSERT_EQ(sizeof(PackedBPTrieNodeParent), 1 + 4 + 1 + 1); -BATT_STATIC_ASSERT_EQ(sizeof(PackedBPTrieNodeParent), 1 + 4 + 2 + 2); -BATT_STATIC_ASSERT_EQ(sizeof(PackedBPTrieNodeParent), 1 + 4 + 3 + 3); -BATT_STATIC_ASSERT_EQ(sizeof(PackedBPTrieNodeParent), 1 + 4 + 4 + 4); - -//=#=#==#==#===============+=+=+=+=++=++++++++++++++-++-+--+-+----+--------------- - -/** \brief The packed representation of a binary prefix trie (BPTrie). - */ -struct PackedBPTrie { - static constexpr usize kMaxPrefixChunkLen = 127; - static constexpr u8 kPrefixChunkLenMask = 0x7f; - static constexpr u8 kParentNodeMask = 0x80; - - static constexpr u8 kOffset8 = 0; - static constexpr u8 kOffset16 = 1; - static constexpr u8 kOffset24 = 2; - static constexpr u8 kOffset32 = 3; - - //+++++++++++-+-+--+----- --- -- - - - - - - little_u64 size_; - u8 offset_kind_; - - //+++++++++++-+-+--+----- --- -- - - - - - - usize size() const noexcept - { - return this->size_; - } - - const PackedBPTrieNodeBase* root() const noexcept - { - return reinterpret_cast(this + 1); - } - - batt::Interval find(std::string_view key, usize& key_prefix_match) const noexcept; - - batt::Interval find(std::string_view key) const noexcept - { - usize ignored; - return this->find(key, ignored); - } - - std::string_view get_key(usize index, batt::SmallVecBase& buffer) const noexcept; -}; - -LLFS_DEFINE_PACKED_TYPE_FOR(BPTrie, PackedBPTrie); - -/** \brief Calculate the size of the given sub-trie. - */ -usize packed_sizeof(const BPTrie& node); - -/** \brief Pack the trie into its compact serialization. - */ -const PackedBPTrie* pack_object_to(const BPTrie& object, PackedBPTrie* packed, - llfs::DataPacker* dst); - -} //namespace llfs - -#endif // LLFS_TRIE_HPP - -#include diff --git a/src/llfs/trie.ipp b/src/llfs/trie.ipp deleted file mode 100644 index 7c9b6782..00000000 --- a/src/llfs/trie.ipp +++ /dev/null @@ -1,149 +0,0 @@ -//#=##=##=#==#=#==#===#+==#+==========+==+=+=+=+=+=++=+++=+++++=-++++=-+++++++++++ -// -// Part of the LLFS Project, under Apache License v2.0. -// See https://www.apache.org/licenses/LICENSE-2.0 for license information. -// SPDX short identifier: Apache-2.0 -// -//+++++++++++-+-+--+----- --- -- - - - - - -#pragma once -#ifndef LLFS_TRIE_IPP -#define LLFS_TRIE_IPP - -#include -// - -#include - -#include -#include - -namespace llfs { - -//#=##=##=#==#=#==#===#+==#+==========+==+=+=+=+=+=++=+++=+++++=-++++=-+++++++++++ - -//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - -// -template -inline BPTrieNode* make_trie(const Range& keys, BPTrieNodeSet& node_set, usize current_prefix_len, - bool is_right_subtree) -{ - // Guard against too much recursion. - // - thread_local int depth = 0; - - BATT_CHECK_LT(depth, 48); - - ++depth; - auto on_scope_exit = batt::finally([&] { - --depth; - }); - - // Grab the iterator pair and find the input range size. - // - auto first = std::begin(keys); - auto last = std::end(keys); - const usize count = std::distance(first, last); - - // Base case 0: empty input. - // - if (count == 0) { - return nullptr; - } - - // We know we are going to create at least one node. - // - auto* node = node_set.new_node(); - - // Base case 1: single key. - // - if (count == 1) { - // Implement right-leaf optimization (the parent pivot is always prefix[0] in this case). - // - if (is_right_subtree) { - current_prefix_len += 1; - } - - node->prefix_ = std::string_view{first->data() + current_prefix_len, // - first->size() - current_prefix_len}; - - node->subtree_node_count_ = 1; - - return node; - } - - // Find the longest common prefix of the input key range. - // - const auto& min_key = *first; - const auto& max_key = *std::prev(last); - - node->prefix_ = find_common_prefix(current_prefix_len, min_key, max_key); - - // This is the first position at which the inputs differ along this branch of the trie. - // - const usize k = node->prefix_.size() + current_prefix_len; - - // Helper: given a key index, return the first byte in that key *not* in the prefix we calculated - // above. - // - const auto get_kth_byte = [&](usize i) { - auto iter = std::next(first, i); - if (iter->size() == k) { - return '\0'; - } - return (*iter)[k]; - }; - - // Find a pivot byte that best bisects the input range. - // - const usize middle_pos = count / 2; - const auto middle_iter = std::next(first, middle_pos); - const u8 middle_value = get_kth_byte(middle_pos); - - // Binary-search the entire input to find the subrange containing the median element. - // - const auto [lo_iter, hi_iter] = std::equal_range(first, last, middle_value, CompareKthByte{k}); - - // We want to take whichever bound is closer to the middle of the input, so calculate that - // distance now. - // - const usize lo_distance = std::distance(lo_iter, middle_iter); - const usize hi_distance = std::distance(middle_iter, hi_iter); - - const auto pivot_iter = [&] { - if (lo_distance < hi_distance) { - return lo_iter; - } else { - return hi_iter; - } - }(); - - // We have our pivot! Subdivide the input range and recurse down left (lower) and right (upper) - // halves. - // - node->pivot_pos_ = std::distance(first, pivot_iter); - node->pivot_ = get_kth_byte(node->pivot_pos_); - - current_prefix_len = k; - BATT_CHECK_NE(first, pivot_iter) << BATT_INSPECT(lo_distance) << BATT_INSPECT(hi_distance); - BATT_CHECK_NE(last, pivot_iter) << BATT_INSPECT(lo_distance) << BATT_INSPECT(hi_distance) - << BATT_INSPECT(middle_pos) << BATT_INSPECT(count) - << batt::dump_range(boost::make_iterator_range(first, last)) - << BATT_INSPECT(current_prefix_len); - - node->left_ = - make_trie(boost::make_iterator_range(first, pivot_iter), node_set, current_prefix_len, false); - - node->right_ = - make_trie(boost::make_iterator_range(pivot_iter, last), node_set, current_prefix_len, true); - - node->subtree_node_count_ = 1 + // - ((node->left_) ? node->left_->subtree_node_count_ : 0) + - ((node->right_) ? node->right_->subtree_node_count_ : 0); - - return node; -} - -} //namespace llfs - -#endif // LLFS_TRIE_IPP diff --git a/src/llfs/trie.test.cpp b/src/llfs/trie.test.cpp deleted file mode 100644 index 3578f107..00000000 --- a/src/llfs/trie.test.cpp +++ /dev/null @@ -1,530 +0,0 @@ -//#=##=##=#==#=#==#===#+==#+==========+==+=+=+=+=+=++=+++=+++++=-++++=-+++++++++++ -// -// Part of the LLFS Project, under Apache License v2.0. -// See https://www.apache.org/licenses/LICENSE-2.0 for license information. -// SPDX short identifier: Apache-2.0 -// -//+++++++++++-+-+--+----- --- -- - - - - - -#include -// -#include - -#include -#include - -#include -#include - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include - -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace { - -using namespace llfs::int_types; - -using llfs::BPTrie; -using llfs::PackedBPTrie; - -std::vector load_words() -{ - std::vector words; - std::string word_file_path = llfs::testing::get_test_data_file_path("words"); - std::ifstream ifs{word_file_path}; - BATT_CHECK(ifs.good()) << BATT_INSPECT_STR(word_file_path); - std::string word; - while (ifs.good()) { - ifs >> word; - words.emplace_back(word); - } - std::sort(words.begin(), words.end()); - words.erase(std::unique(words.begin(), words.end()), words.end()); - return words; -} - -//=#=#==#==#===============+=+=+=+=++=++++++++++++++-++-+--+-+----+--------------- - -constexpr usize kSkip = 0; -constexpr usize kBenchmarkRepeat = 10; - -using batt::Optional; - -struct Trial { - usize skip = 0; - double step = 1.0; - usize take = 100; - Optional ss_table_size; - Optional packed_trie_size; -}; - -struct SSTableWrapper { - const std::vector& items; - - batt::Interval find(const std::string_view& key) const - { - const auto first = items.begin(); - const auto& [lower, upper] = std::equal_range(first, items.end(), key); - return {usize(lower - first), usize(upper - first)}; - } -}; - -struct PackedSSTableWrapper { - const llfs::PackedArray& items; - - struct Compare { - bool operator()(const std::string_view& l, const llfs::PackedBytes& r) const - { - return l < r.as_str(); - } - bool operator()(const llfs::PackedBytes& l, const std::string_view& r) const - { - return l.as_str() < r; - } - }; - - batt::Interval find(const std::string_view& key) const - { - const auto first = items.begin(); - const auto& [lower, upper] = std::equal_range(first, items.end(), key, Compare{}); - return {usize(lower - first), usize(upper - first)}; - } -}; - -TEST(Trie, Test) -{ - llfs::testing::TestConfig test_config; - - const bool extra_testing = test_config.extra_testing(); - - auto words = load_words(); - - LLFS_LOG_INFO() << BATT_INSPECT(words.size()); - - double trials = 0; - double compression_total_bfs = 0; - double compression_total_veb = 0; - double speedup_total_mem_trie = 0; - double speedup_total_mem_sstable = 0; - double speedup_total_packed_bfs = 0; - double speedup_total_packed_veb = 0; - - const usize kMaxTake = extra_testing ? words.size() : 1000; - - for (const usize kTake : {10, 50, 80, 100, 200, 500, 1000, 2000, 3000, 4000, 8000, 16000, 32000, - 64000, 128000, (int)words.size()}) { - if (kTake > kMaxTake) { - break; - } - LOG(INFO) << BATT_INSPECT(kTake); - for (const usize kStep : {1, 2, 3, 4, 5, 6, 7, 8, 16, 32, 64, 128, 256}) { - std::vector sample; - { - usize i = 0; - for (const auto& word : words) { - if (i > kSkip) { - if ((i % kStep) == 0) { - VLOG(1) << sample.size() << ": " << batt::c_str_literal(word); - sample.emplace_back(word); - if (sample.size() >= kTake) { - break; - } - } - } - ++i; - } - } - - trials += 1; - - BPTrie trie{sample}; - - // Pack the trie first in BFS order. - // - trie.set_packed_layout(BPTrie::PackedLayout::kBreadthFirst); - const usize packed_size_bfs = llfs::packed_sizeof(trie); - std::unique_ptr buffer_bfs{new u8[packed_size_bfs]}; - const PackedBPTrie* packed_bfs = nullptr; - { - llfs::DataPacker packer{llfs::MutableBuffer{buffer_bfs.get(), packed_size_bfs}}; - packed_bfs = llfs::pack_object(trie, &packer); - - ASSERT_NE(packed_bfs, nullptr) << BATT_INSPECT(packed_size_bfs); - } - - // Pack the trie again using VEB order. - // - trie.set_packed_layout(BPTrie::PackedLayout::kVanEmdeBoas); - const usize packed_size_veb = llfs::packed_sizeof(trie); - std::unique_ptr buffer_veb{new u8[packed_size_veb]}; - const PackedBPTrie* packed_veb = nullptr; - { - llfs::DataPacker packer{llfs::MutableBuffer{buffer_veb.get(), packed_size_veb}}; - packed_veb = llfs::pack_object(trie, &packer); - - ASSERT_NE(packed_veb, nullptr); - } - - // Finally, pack in SSTable layout. - // - const usize packed_size_sstable = - sizeof(llfs::PackedArray) + - (batt::as_seq(sample) // - | batt::seq::map(BATT_OVERLOADS_OF(llfs::packed_sizeof)) // - | batt::seq::sum() // - ); - std::unique_ptr buffer_sstable{new u8[packed_size_sstable]}; - const llfs::PackedArray* packed_sstable = nullptr; - { - llfs::DataPacker packer{llfs::MutableBuffer{buffer_sstable.get(), packed_size_sstable}}; - packed_sstable = llfs::pack_object(batt::as_seq(sample) | batt::seq::boxed(), &packer); - - ASSERT_NE(packed_sstable, nullptr) << BATT_INSPECT(packed_size_sstable); - } - - const double compression_bfs = double(packed_size_bfs) / double(packed_size_sstable); - const double compression_veb = double(packed_size_veb) / double(packed_size_sstable); - - compression_total_bfs += compression_bfs; - compression_total_veb += compression_veb; - - // Test BPTrie, PackedBPTrie for correctness. - // - for (usize i = 0; i < sample.size() * kStep; ++i) { - if (i + kSkip >= words.size()) { - break; - } - std::string_view word = words[i + kSkip]; - const auto debug_info = [&](std::ostream& out) { - out << BATT_INSPECT(i) << BATT_INSPECT(kSkip) << BATT_INSPECT(kStep) - << " word == " << batt::c_str_literal(word) - << batt::dump_range(sample, batt::Pretty::True); - - //----- --- -- - - - - - // Dump the Trie as Mermaid graph diagram markdown (https://mermaid.live/edit) - // - out << std::endl; - - using llfs::BPTrieNode; - - std::vector stack; - stack.push_back(trie.root()); - std::unordered_map node_to_id; - int next_id = 0; - while (!stack.empty()) { - const BPTrieNode* next = stack.back(); - stack.pop_back(); - - node_to_id[next] = ++next_id; - out << " " << next_id << "["; - out << batt::c_str_literal(batt::to_string(next->prefix_, "/", (char)next->pivot_)); - out << "]" << std::endl; - - if (next->left_) { - stack.push_back(next->right_); - stack.push_back(next->left_); - } - } - - for (const auto& [node, id] : node_to_id) { - if (node->left_) { - out << " " << id << " -->|left| " << node_to_id[node->left_] << std::endl; - out << " " << id << " -->|right| " << node_to_id[node->right_] << std::endl; - } - } - // (end Mermaid markdown) - //----- --- -- - - - - - }; - batt::Interval pos = trie.find(word); - - EXPECT_LE(pos.lower_bound, pos.upper_bound); - - if (i > 0 && ((i + kSkip) % kStep) == 0) { - EXPECT_EQ(pos.lower_bound + 1, pos.upper_bound) - << BATT_INSPECT(pos) << BATT_INSPECT(i) << BATT_INSPECT(kSkip) << BATT_INSPECT(kStep) - << BATT_INSPECT(word) << debug_info; - EXPECT_EQ(sample[pos.lower_bound], word); - } - - if (pos.upper_bound > pos.lower_bound) { - ASSERT_GE(word, sample[pos.lower_bound]) << BATT_INSPECT(pos) << debug_info; - } - if (pos.upper_bound < sample.size()) { - ASSERT_LT(word, sample[pos.upper_bound]) - << BATT_INSPECT(pos) << BATT_INSPECT(i) << BATT_INSPECT(sample.size()) << debug_info; - } - - auto pos2 = packed_bfs->find(word); - auto pos3 = packed_veb->find(word); - - EXPECT_EQ(pos, pos2) << debug_info; - EXPECT_EQ(pos, pos3) << debug_info; - } - - batt::SmallVec buffer; - for (usize i = 0; i < trie.size(); ++i) { - buffer.clear(); - { - std::string_view actual_key = trie.get_key(i, buffer); - EXPECT_EQ(actual_key, sample[i]); - } - buffer.clear(); - { - std::string_view actual_key = packed_bfs->get_key(i, buffer); - EXPECT_EQ(actual_key, sample[i]); - } - buffer.clear(); - { - std::string_view actual_key = packed_veb->get_key(i, buffer); - EXPECT_EQ(actual_key, sample[i]); - } - } - - const auto run_timed_bench = [&sample, &words, &kStep](const auto& target) -> double { - const auto start = std::chrono::steady_clock::now(); - - usize checksum = 0; - for (usize n = 0; n < kBenchmarkRepeat; ++n) { - for (usize i = 0; i < sample.size() * kStep; ++i) { - if (i + kSkip >= words.size()) { - break; - } - std::string_view word = words[i + kSkip]; - batt::Interval pos = target.find(word); - checksum += pos.lower_bound; - } - } - - i64 usec = std::chrono::duration_cast( - std::chrono::steady_clock::now() - start) - .count(); - - EXPECT_GT(checksum, 1u); - - return double(usec) / 1000000.0; - }; - - double mem_trie_time = run_timed_bench(trie); - double mem_sstable_time = run_timed_bench(SSTableWrapper{sample}); - double packed_bfs_time = run_timed_bench(*packed_bfs); - double packed_veb_time = run_timed_bench(*packed_veb); - double packed_sstable_time = run_timed_bench(PackedSSTableWrapper{*packed_sstable}); - - speedup_total_mem_trie += packed_sstable_time / mem_trie_time; - speedup_total_mem_sstable += packed_sstable_time / mem_sstable_time; - speedup_total_packed_bfs += packed_sstable_time / packed_bfs_time; - speedup_total_packed_veb += packed_sstable_time / packed_veb_time; - - VLOG(1) << BATT_INSPECT(mem_trie_time) << BATT_INSPECT(mem_sstable_time) - << BATT_INSPECT(packed_bfs_time) << BATT_INSPECT(packed_veb_time) - << BATT_INSPECT(packed_sstable_time); - } - } - - double avg_compression_bfs = (1.0 - compression_total_bfs / trials) * 100.0; - double avg_compression_veb = (1.0 - compression_total_veb / trials) * 100.0; - double avg_speedup_mem_trie = speedup_total_mem_trie / trials; - double avg_speedup_mem_sstable = speedup_total_mem_sstable / trials; - double avg_speedup_packed_bfs = speedup_total_packed_bfs / trials; - double avg_speedup_packed_veb = speedup_total_packed_veb / trials; - - LOG(INFO) << BATT_INSPECT(avg_compression_bfs) << "%" << BATT_INSPECT(avg_compression_veb) << "%"; - LOG(INFO) << BATT_INSPECT(avg_speedup_mem_trie); - LOG(INFO) << BATT_INSPECT(avg_speedup_mem_sstable); - LOG(INFO) << BATT_INSPECT(avg_speedup_packed_bfs); - LOG(INFO) << BATT_INSPECT(avg_speedup_packed_veb); -} - -//#=##=##=#==#=#==#===#+==#+==========+==+=+=+=+=+=++=+++=+++++=-++++=-+++++++++++ - -struct Rec { - unsigned id; - unsigned priority; -}; - -inline bool operator<(const Rec& l, const Rec& r) -{ - return l.priority < r.priority || (l.priority == r.priority && (l.id > r.id)); -} - -TEST(Trie, VEBLayoutTest) -{ - llfs::testing::TestConfig test_config; - - const bool extra_testing = test_config.extra_testing(); - - std::cerr << "depth, avg(BFS), avg(vEB), avg(RND),"; - for (usize i = 0; i < 32; ++i) { - std::cerr << " vEB(offset=" << (2 << i) << " %branch),"; - } - for (usize i = 0; i < 32; ++i) { - std::cerr << " BFS(offset=" << (2 << i) << " %branch),"; - } - std::cerr << std::endl; - - const int max_depth_limit = extra_testing ? 24 : 16; - - for (int max_depth = 1; max_depth <= max_depth_limit; ++max_depth) { - std::vector n((1 << max_depth) + 1); - std::iota(n.begin(), n.end(), 1); - - const auto left = [&](unsigned id) -> unsigned { - if (id > n.size() / 2) - return 0; - return id * 2; - }; - - const auto right = [&](unsigned id) -> unsigned { - if (id > n.size() / 2) - return 0; - return id * 2 + 1; - }; - - const auto index_of = [](unsigned id) -> unsigned { - return id - 1; - }; - - const auto depth = [](unsigned id) -> unsigned { - return batt::log2_floor(id); - }; - - std::vector l(n.size()), r(n.size()), d(n.size()); - - for (unsigned id : n) { - l[index_of(id)] = left(id); - r[index_of(id)] = right(id); - d[index_of(id)] = depth(id); - } - - std::vector heap{Rec{1, 32}}; - std::vector layout; - - while (!heap.empty()) { - std::pop_heap(heap.begin(), heap.end()); - - Rec next = heap.back(); - heap.pop_back(); - - layout.push_back(next.id); - - if (next.id <= n.size() / 2) { - unsigned left_id = left(next.id); - unsigned right_id = right(next.id); - - unsigned left_priority = __builtin_clz(depth(next.id) ^ depth(left_id)); - unsigned right_priority = __builtin_clz(depth(next.id) ^ depth(right_id)); - - heap.emplace_back(Rec{left_id, left_priority}); - std::push_heap(heap.begin(), heap.end()); - - heap.emplace_back(Rec{right_id, right_priority}); - std::push_heap(heap.begin(), heap.end()); - } - } - - double total_dist_rlayout = 0; - std::vector pos(n.size()); - for (unsigned i = 0; i < layout.size(); ++i) { - unsigned id = layout[i]; - pos[index_of(id)] = i; - } - - double n_seeds = 10.0; - for (unsigned seed = 0; seed < unsigned(n_seeds); ++seed) { - std::default_random_engine rng{seed}; - - std::vector rlayout = n; - std::shuffle(rlayout.begin(), rlayout.end(), rng); - - std::sort(rlayout.begin(), rlayout.end(), [&](unsigned l_id, unsigned r_id) { - return depth(l_id) < depth(r_id); - }); - - std::vector rpos(n.size()); - for (unsigned i = 0; i < rlayout.size(); ++i) { - unsigned id = rlayout[i]; - rpos[index_of(id)] = i; - } - - for (unsigned id : n) { - if (id > n.size() / 2) { - break; - } - total_dist_rlayout += rpos[index_of(left(id))] - rpos[index_of(id)]; - total_dist_rlayout += rpos[index_of(right(id))] - rpos[index_of(id)]; - } - } - - std::array heap_dist_log2, veb_dist_log2; - heap_dist_log2.fill(0); - veb_dist_log2.fill(0); - - double total_dist_heap_layout = 0; - double total_dist_veb_layout = 0; - for (unsigned id : n) { - if (id > n.size() / 2) { - break; - } - - heap_dist_log2[batt::log2_ceil(index_of(left(id)) - index_of(id))] += 1; - heap_dist_log2[batt::log2_ceil(index_of(right(id)) - index_of(id))] += 1; - - total_dist_heap_layout += index_of(left(id)) - index_of(id); - total_dist_heap_layout += index_of(right(id)) - index_of(id); - - veb_dist_log2[batt::log2_ceil(pos[index_of(left(id))] - pos[index_of(id)])] += 1; - veb_dist_log2[batt::log2_ceil(pos[index_of(right(id))] - pos[index_of(id)])] += 1; - - total_dist_veb_layout += pos[index_of(left(id))] - pos[index_of(id)]; - total_dist_veb_layout += pos[index_of(right(id))] - pos[index_of(id)]; - } - - const auto normalize_pct = [](auto& hist) { - auto total = batt::as_seq(hist) | batt::seq::decayed() | batt::seq::sum(); - for (auto& n : hist) { - n = (n * 100) / total; - } - }; - - normalize_pct(heap_dist_log2); - normalize_pct(veb_dist_log2); - - std::cerr << max_depth << ", " // - << total_dist_heap_layout / double(n.size() / 2) << ", " // - << total_dist_veb_layout / double(n.size() / 2) << ", " // - << total_dist_rlayout / double(n.size() / 2 * n_seeds) << ", " // - ; - for (auto pct : veb_dist_log2) { - std::cerr << pct << ", "; - } - for (auto pct : heap_dist_log2) { - std::cerr << pct << ", "; - } - std::cerr << std::endl; - } -} - -} // namespace diff --git a/src/llfs/worker_task.cpp b/src/llfs/worker_task.cpp deleted file mode 100644 index 35609901..00000000 --- a/src/llfs/worker_task.cpp +++ /dev/null @@ -1,245 +0,0 @@ -//#=##=##=#==#=#==#===#+==#+==========+==+=+=+=+=+=++=+++=+++++=-++++=-+++++++++++ -// -// Part of the LLFS Project, under Apache License v2.0. -// See https://www.apache.org/licenses/LICENSE-2.0 for license information. -// SPDX short identifier: Apache-2.0 -// -//+++++++++++-+-+--+----- --- -- - - - - - -#include -// - -namespace llfs { - -//=#=#==#==#===============+=+=+=+=++=++++++++++++++-++-+--+-+----+--------------- -// class WorkQueue - -//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - -// -void WorkQueue::close() noexcept -{ - const u64 prior_state = this->state_.fetch_or(WorkQueue::kClosedFlag); - - LLFS_VLOG(1) << "WorkQueue::close() halting all idle workers"; - this->halt_all_idle_workers(prior_state | WorkQueue::kClosedFlag); -} - -//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - -// -void WorkQueue::halt_all_idle_workers(u64 observed_state) noexcept -{ - while (Self::get_worker_count(observed_state) > 0 && Self::get_job_count(observed_state) == 0) { - u64 worker_count = Self::get_worker_count(observed_state); - - const u64 target_state = WorkQueue::kClosedFlag; - if (!this->state_.compare_exchange_weak(observed_state, target_state)) { - continue; - } - - while (worker_count > 0) { - WorkerTask* worker = nullptr; - - BATT_CHECK(this->worker_queue_.pop(worker)); - BATT_CHECK_NOT_NULLPTR(worker); - - worker->halt(); - --worker_count; - } - break; - } -} - -//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - -// -batt::Status WorkQueue::push_worker(WorkerTask* worker) -{ - LLFS_VLOG(1) << "WorkQueue::push_worker"; - { - const u64 observed_state = this->state_.load(); - - if (Self::is_closed_state(observed_state) && Self::get_job_count(observed_state) == 0) { - LLFS_VLOG(1) << " -- WorkQueue is closed and drained; halting worker"; - worker->halt(); - return batt::StatusCode::kClosed; - } - } - - if (!this->worker_queue_.push(worker)) { - return batt::StatusCode::kUnavailable; - } - - const u64 observed_state = this->state_.fetch_add(kWorkerIncrement) + kWorkerIncrement; - - LLFS_VLOG(1) << " --" << std::hex << BATT_INSPECT(observed_state); - - if (Self::is_closed_state(observed_state) && Self::get_job_count(observed_state) == 0) { - LLFS_VLOG(1) << " -- WorkQueue is closed and drained; halting all idle workers"; - this->halt_all_idle_workers(observed_state); - return batt::StatusCode::kClosed; - } - - return this->dispatch(observed_state, __FUNCTION__); -} - -//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - -// -batt::Status WorkQueue::dispatch(u64 observed_state, const char* from) -{ - LLFS_VLOG(1) << "WorkQueue::dispatch()" << BATT_INSPECT_STR(from); - - for (;;) { - const auto worker_count = Self::get_worker_count(observed_state); - const auto job_count = Self::get_job_count(observed_state); - - LLFS_VLOG(1) << " --" << BATT_INSPECT(worker_count) << BATT_INSPECT(job_count); - - if (worker_count == 0 || job_count == 0) { - LLFS_VLOG(1) << " -- Nothing more we can do (idle state) returning"; - break; - } - - const u64 target_state = observed_state - (kJobIncrement | kWorkerIncrement); - - if (this->state_.compare_exchange_weak(observed_state, target_state)) { - LLFS_VLOG(1) << " -- Found worker/job pair; dispatching!"; - - Job* next_job = nullptr; - WorkerTask* next_worker = nullptr; - - BATT_CHECK(this->worker_queue_.pop(next_worker)); - BATT_CHECK(this->job_queue_.pop(next_job)); - - BATT_CHECK_NOT_NULLPTR(next_job); - BATT_CHECK_NOT_NULLPTR(next_worker); - - auto on_scope_exit = batt::finally([&] { - next_job->~Job(); - usize job_i = next_job - ((Job*)this->job_storage_.data()); - BATT_CHECK(this->storage_queue_.push(job_i)); - }); - - BATT_REQUIRE_OK(next_worker->dispatch_job(std::move(*next_job))); - - LLFS_VLOG(1) << " -- Handed job off successfully!"; - - observed_state = target_state; - } - } - return batt::OkStatus(); -} - -//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - -// -batt::StatusOr WorkQueue::allocate_job_slot() -{ - // Initialize the storage slot index `i` to an out-of-bounds value. - // - usize i = ~0; - - // Try to grab an available slot; if we fail, then try to increase the init upper bound. - // - if (!this->storage_queue_.pop(i)) { - i = this->storage_init_upper_bound_.fetch_add(1); - - // If we are already at the limit, then undo the increment we just did and return failure (no - // space available). - // - if (i >= this->job_storage_.size()) { - this->storage_init_upper_bound_.fetch_sub(1); - return {batt::Status{batt::StatusCode::kUnavailable}}; - } - } - - return {i}; -} - -//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - -// -batt::Status WorkQueue::post_job(Job* job) -{ - // This should never fail because we succeeded in allocating a storage slot. - // - BATT_CHECK(this->job_queue_.push(job)); - - const u64 observed_state = this->state_.fetch_add(kJobIncrement) + kJobIncrement; - - return this->dispatch(observed_state, __FUNCTION__); -} - -//=#=#==#==#===============+=+=+=+=++=++++++++++++++-++-+--+-+----+--------------- -// class WorkerTask - -//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - -// -batt::Status WorkerTask::dispatch_job(WorkQueue::Job&& job) -{ - u32 observed_state = this->state_.get_value(); - - if (observed_state == WorkerTask::kHaltedState) { - return batt::StatusCode::kClosed; - } - BATT_CHECK_EQ(observed_state, WorkerTask::kReadyState); - this->job_.emplace(std::move(job)); - - const u32 prior_state = this->state_.set_value(WorkerTask::kWorkingState); - BATT_CHECK_EQ(prior_state, observed_state); - - return batt::OkStatus(); -} - -//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - -// -void WorkerTask::halt() -{ - const u32 prior_state = this->state_.set_value(WorkerTask::kHaltedState); - BATT_CHECK_EQ(prior_state, WorkerTask::kReadyState); - - this->state_.close(); -} - -//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - -// -void WorkerTask::run() -{ - LLFS_VLOG(1) << "WorkerTask@" << (void*)this << " ENTERED"; - - batt::Status status = [&]() -> batt::Status { - while (!this->halt_requested_.load()) { - LLFS_VLOG(1) << "WorkerTask@" << (void*)this << " Pushing self to work queue"; - - BATT_CHECK_EQ(this->state_.get_value(), WorkerTask::kReadyState); - BATT_REQUIRE_OK(this->work_queue_->push_worker(this)); - - BATT_REQUIRE_OK(this->state_.await_equal(WorkerTask::kWorkingState)); - BATT_CHECK_NE(this->job_, batt::None); - - LLFS_VLOG(1) << "WorkerTask@" << (void*)this << " Got next job; running!"; - - try { - auto on_scope_exit = batt::finally([&] { - this->job_ = batt::None; - }); - this->job_->work_fn(); - } catch (...) { - LLFS_LOG_ERROR() << "Unexpected exception TODO [tastolfi 2023-06-29] print details"; - } - - LLFS_VLOG(1) << "WorkerTask@" << (void*)this << " job done!"; - - const u32 prior_state = this->state_.set_value(WorkerTask::kReadyState); - BATT_CHECK_EQ(prior_state, WorkerTask::kWorkingState); - } - - return batt::OkStatus(); - }(); - - if (!status.ok()) { - if (this->halt_requested_.load() || status == batt::StatusCode::kClosed) { - LLFS_VLOG(1) << "WorkerTask terminated with status: " << status; - } else { - LLFS_LOG_WARNING() << "WorkerTask terminated unexpectedly with error status: " << status; - } - } -} - -} //namespace llfs diff --git a/src/llfs/worker_task.hpp b/src/llfs/worker_task.hpp deleted file mode 100644 index 6b2b3520..00000000 --- a/src/llfs/worker_task.hpp +++ /dev/null @@ -1,200 +0,0 @@ -//#=##=##=#==#=#==#===#+==#+==========+==+=+=+=+=+=++=+++=+++++=-++++=-+++++++++++ -// -// Part of the LLFS Project, under Apache License v2.0. -// See https://www.apache.org/licenses/LICENSE-2.0 for license information. -// SPDX short identifier: Apache-2.0 -// -//+++++++++++-+-+--+----- --- -- - - - - - -#pragma once -#ifndef LLFS_WORKER_TASK_HPP -#define LLFS_WORKER_TASK_HPP - -#include -// -#include - -#include -#include -#include -#include - -#include -#include - -#include -#include -#include - -namespace llfs { - -class WorkerTask; - -//=#=#==#==#===============+=+=+=+=++=++++++++++++++-++-+--+-+----+--------------- -// -class WorkQueue -{ - public: - // These can be changed to tune the WorkQueue. - // - static constexpr usize kMaxDepth = 4096; - static constexpr usize kWorkFnMaxSize = 256; - - using Self = WorkQueue; - - struct Job { - batt::SmallFn work_fn; - }; - - //+++++++++++-+-+--+----- --- -- - - - - - - static constexpr u64 kWorkerIncrement = 1; - static constexpr u64 kWorkerCountMask = (u64{1} << 31) - 1; - static constexpr u64 kWorkerCountShift = 0; - - static constexpr u64 kJobIncrement = kWorkerIncrement << 31; - static constexpr u64 kJobCountMask = kWorkerCountMask << 31; - static constexpr u64 kJobCountShift = 31; - - static constexpr u64 kClosedFlag = u64{1} << 63; - - static constexpr bool is_closed_state(u64 state) noexcept - { - return (state & kClosedFlag) != 0; - } - - static constexpr u64 get_worker_count(u64 state) noexcept - { - return (state & kWorkerCountMask) >> kWorkerCountShift; - } - - static constexpr u64 get_job_count(u64 state) noexcept - { - return (state & kJobCountMask) >> kJobCountShift; - } - - //+++++++++++-+-+--+----- --- -- - - - - - - /** \brief The first call to close causes the queue to go into a "draining" state; push_job will - * fail with batt::StatusCode::kClosed, but push_worker will continue to succeed until there are - * no more jobs left, then it will also fail with batt::StatusCode::kClosed. - * - * This draining method is the best we can do without an interface for job cancellation. - */ - void close() noexcept; - - bool is_closed() const noexcept - { - return Self::is_closed_state(this->state_.load()); - } - - template - batt::Status push_job(WorkFnArg&& arg) - { - if (this->is_closed()) { - return batt::StatusCode::kClosed; - } - - // Allocate a free slot for the job. - // - BATT_ASSIGN_OK_RESULT(const usize i, this->allocate_job_slot()); - - // We have a currently unused storage slot. Initialize it with the passed arg. - // - Job* job = new (&this->job_storage_[i]) Job{{BATT_FORWARD(arg)}}; - - // Post the job; this will automatically dispatch it if a worker is available. - // - return this->post_job(job); - } - - batt::Status push_worker(WorkerTask* worker); - - //+++++++++++-+-+--+----- --- -- - - - - - private: - batt::StatusOr allocate_job_slot(); - - batt::Status post_job(Job* job); - - batt::Status dispatch(u64 observed_state, const char* from); - - void halt_all_idle_workers(u64 observed_state) noexcept; - - //+++++++++++-+-+--+----- --- -- - - - - - - std::atomic state_{0}; - - std::atomic storage_init_upper_bound_{0}; - - std::array, kMaxDepth> job_storage_; - - boost::lockfree::queue, - boost::lockfree::fixed_sized> - worker_queue_; - - boost::lockfree::queue, - boost::lockfree::fixed_sized> - job_queue_; - - boost::lockfree::queue, - boost::lockfree::fixed_sized> - storage_queue_; -}; - -//=#=#==#==#===============+=+=+=+=++=++++++++++++++-++-+--+-+----+--------------- -// -class WorkerTask -{ - public: - static constexpr u32 kReadyState = 0; - static constexpr u32 kWorkingState = 1; - static constexpr u32 kHaltedState = 2; - - //+++++++++++-+-+--+----- --- -- - - - - - - template - explicit WorkerTask(batt::SharedPtr&& work_queue, - const boost::asio::any_io_executor& ex, TaskArgs&&... task_args) noexcept - : work_queue_{std::move(work_queue)} - , task_{ex, - [this] { - this->run(); - }, - BATT_FORWARD(task_args)...} - , job_{} - , state_{WorkerTask::kReadyState} - { - } - - batt::Status dispatch_job(WorkQueue::Job&& job); - - void pre_halt() - { - this->halt_requested_.store(true); - } - - void halt(); - - void join() - { - this->task_.join(); - } - - //+++++++++++-+-+--+----- --- -- - - - - - private: - void run(); - - //+++++++++++-+-+--+----- --- -- - - - - - - batt::SharedPtr work_queue_; - batt::Task task_; - batt::Optional job_; - batt::Watch state_; - std::atomic halt_requested_{false}; -}; - -//#=##=##=#==#=#==#===#+==#+==========+==+=+=+=+=+=++=+++=+++++=-++++=-+++++++++++ - -} //namespace llfs - -#endif // LLFS_WORKER_TASK_HPP diff --git a/src/llfs/worker_task_fuse_impl.hpp b/src/llfs/worker_task_fuse_impl.hpp deleted file mode 100644 index bd57b94c..00000000 --- a/src/llfs/worker_task_fuse_impl.hpp +++ /dev/null @@ -1,798 +0,0 @@ -//#=##=##=#==#=#==#===#+==#+==========+==+=+=+=+=+=++=+++=+++++=-++++=-+++++++++++ -// -// Part of the LLFS Project, under Apache License v2.0. -// See https://www.apache.org/licenses/LICENSE-2.0 for license information. -// SPDX short identifier: Apache-2.0 -// -//+++++++++++-+-+--+----- --- -- - - - - - -#pragma once -#ifndef LLFS_WORKER_TASK_FUSE_IMPL_HPP -#define LLFS_WORKER_TASK_FUSE_IMPL_HPP - -#include -// -#include -#include -#include - -#include -#include - -namespace llfs { - -template -class WorkerTaskFuseImpl : public FuseImpl -{ - public: - explicit WorkerTaskFuseImpl(std::shared_ptr&& work_queue) noexcept - : work_queue_{std::move(work_queue)} - { - } - - // 1/44 (init) - // 2/44 (destroy) - // - // - Not included since they are already non-async. - - /** \brief - */ // 3/44 - template - void async_lookup(fuse_req_t req, fuse_ino_t parent, const char* name, Handler&& handler) - { - LLFS_VLOG(1) << BATT_THIS_FUNCTION << BATT_INSPECT(req) << BATT_INSPECT(parent) - << BATT_INSPECT(name); - - batt::Status push_status = this->work_queue_->push_job( - [this, req, parent, name = std::string{name}, handler = BATT_FORWARD(handler)]() mutable { - BATT_FORWARD(handler)(this->derived_this()->lookup(req, parent, name)); - }); - - if (!push_status.ok()) { - BATT_FORWARD(handler)(batt::StatusOr{push_status}); - } - } - - /** \brief - */ // 4/44 - template - void async_forget_inode(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup, Handler&& handler) - { - LLFS_VLOG(1) << BATT_THIS_FUNCTION << BATT_INSPECT(req) << BATT_INSPECT(ino) - << BATT_INSPECT(nlookup); - - batt::Status push_status = this->work_queue_->push_job( - [this, req, ino, nlookup, handler = BATT_FORWARD(handler)]() mutable { - auto on_scope_exit = batt::finally([&] { - BATT_FORWARD(handler)(); - }); - this->derived_this()->forget_inode(req, ino, nlookup); - }); - - if (!push_status.ok()) { - LLFS_LOG_WARNING() << "Could not push request to work queue;" << BATT_INSPECT(push_status); - BATT_FORWARD(handler)(); - } - } - - /** \brief - */ // 5/44 - template - void async_get_attributes(fuse_req_t req, fuse_ino_t ino, Handler&& handler) - { - LLFS_VLOG(1) << BATT_THIS_FUNCTION // - << BATT_INSPECT(req) // - << BATT_INSPECT(ino); - - batt::Status push_status = - this->work_queue_->push_job([this, req, ino, handler = BATT_FORWARD(handler)]() mutable { - BATT_FORWARD(handler)(this->derived_this()->get_attributes(req, ino)); - }); - - if (!push_status.ok()) { - BATT_FORWARD(handler)(batt::StatusOr{push_status}); - } - } - - /** \brief - */ // 6/44 - template - void async_set_attributes(fuse_req_t req, fuse_ino_t ino, const struct stat* attr, int to_set, - fuse_file_info* fi, Handler&& handler) - { - LLFS_VLOG(1) << BATT_THIS_FUNCTION // - << BATT_INSPECT(req) // - << BATT_INSPECT(ino) // - << BATT_INSPECT(attr) // - << BATT_INSPECT(to_set) // - << BATT_INSPECT(fi); - - /* If the setattr was invoked from the ftruncate() system call - * under Linux kernel versions 2.6.15 or later, the fi->fh will - * contain the value set by the open method or will be undefined - * if the open method didn't set any value. Otherwise (not - * ftruncate call, or kernel version earlier than 2.6.15) the fi - * parameter will be NULL. - * - * (from libfuse/include/fuse_lowlevel.h) - */ - auto fh = [fi]() -> batt::Optional { - if (fi) { - return FuseFileHandle{fi->fh}; - } - return batt::None; - }(); - - batt::Status push_status = this->work_queue_->push_job( - [this, req, ino, attr = *attr, to_set, fh, handler = BATT_FORWARD(handler)]() mutable { - BATT_FORWARD(handler) - (this->derived_this()->set_attributes(req, ino, &attr, to_set, fh)); - }); - - if (!push_status.ok()) { - BATT_FORWARD(handler)(batt::StatusOr{push_status}); - } - } - - /** \brief - */ // 7/44 - template - void async_readlink(fuse_req_t req, fuse_ino_t ino, Handler&& handler) - { - LLFS_VLOG(1) << BATT_THIS_FUNCTION << BATT_INSPECT(req) << BATT_INSPECT(ino); - - batt::Status push_status = - this->work_queue_->push_job([this, req, ino, handler = BATT_FORWARD(handler)]() mutable { - BATT_FORWARD(handler)(this->derived_this()->readlink(req, ino)); - }); - - if (!push_status.ok()) { - BATT_FORWARD(handler)(/*link=*/""); - } - } - - /** \brief - */ // 8/44 - template - void async_make_node(fuse_req_t req, fuse_ino_t parent, const char* name, mode_t mode, dev_t rdev, - Handler&& handler) - { - LLFS_VLOG(1) << BATT_THIS_FUNCTION // - << BATT_INSPECT(req) // - << BATT_INSPECT(parent) // - << BATT_INSPECT(name) // - << BATT_INSPECT(DumpFileMode{mode}) // - << BATT_INSPECT(rdev); - - batt::Status push_status = - this->work_queue_->push_job([this, req, parent, name = std::string{name}, mode, rdev, - handler = BATT_FORWARD(handler)]() mutable { - BATT_FORWARD(handler)(this->derived_this()->make_node(req, parent, name, mode, rdev)); - }); - - if (!push_status.ok()) { - BATT_FORWARD(handler)(batt::StatusOr{push_status}); - } - } - - /** \brief - */ // 9/44 - template - void async_make_directory(fuse_req_t req, fuse_ino_t parent, const char* name, mode_t mode, - Handler&& handler) - { - LLFS_VLOG(1) << BATT_THIS_FUNCTION << BATT_INSPECT(req) << BATT_INSPECT(parent) - << BATT_INSPECT(name) << BATT_INSPECT(DumpFileMode{mode}); - - batt::Status push_status = - this->work_queue_->push_job([this, req, parent, name = std::string{name}, mode, - handler = BATT_FORWARD(handler)]() mutable { - BATT_FORWARD(handler)(this->derived_this()->make_directory(req, parent, name, mode)); - }); - - if (!push_status.ok()) { - BATT_FORWARD(handler)(batt::StatusOr{push_status}); - } - } - - /** \brief - */ // 10/44 - template - void async_unlink(fuse_req_t req, fuse_ino_t parent, const char* name, Handler&& handler) - { - LLFS_VLOG(1) << BATT_THIS_FUNCTION << BATT_INSPECT(req) << BATT_INSPECT(parent) - << BATT_INSPECT(name); - - batt::Status push_status = this->work_queue_->push_job( - [this, req, parent, name = std::string{name}, handler = BATT_FORWARD(handler)]() mutable { - BATT_FORWARD(handler)(this->derived_this()->unlink(req, parent, name)); - }); - - if (!push_status.ok()) { - BATT_FORWARD(handler)(push_status); - } - } - - /** \brief - */ // 11/44 - template - void async_remove_directory(fuse_req_t req, fuse_ino_t parent, const char* name, - Handler&& handler) - { - LLFS_VLOG(1) << BATT_THIS_FUNCTION << BATT_INSPECT(req) << BATT_INSPECT(parent) - << BATT_INSPECT(name); - - batt::Status push_status = this->work_queue_->push_job( - [this, req, parent, name = std::string{name}, handler = BATT_FORWARD(handler)]() mutable { - BATT_FORWARD(handler)(this->derived_this()->remove_directory(req, parent, name)); - }); - - if (!push_status.ok()) { - BATT_FORWARD(handler)(push_status); - } - } - - /** \brief - */ // 12/44 - template - void async_symbolic_link(fuse_req_t req, const char* link, fuse_ino_t parent, const char* name, - Handler&& handler) - { - LLFS_VLOG(1) << BATT_THIS_FUNCTION << BATT_INSPECT(req) << BATT_INSPECT(link) - << BATT_INSPECT(parent) << BATT_INSPECT(name); - - batt::Status push_status = this->work_queue_->push_job( - [this, req, link = std::string{link}, parent, name = std::string{name}, - handler = BATT_FORWARD(handler)]() mutable { - BATT_FORWARD(handler)(this->derived_this()->symbolic_link(req, link, parent, name)); - }); - - if (!push_status.ok()) { - BATT_FORWARD(handler)(batt::StatusOr{push_status}); - } - } - - /** \brief - */ // 13/44 - template - void async_rename(fuse_req_t req, fuse_ino_t parent, const char* name, fuse_ino_t newparent, - const char* newname, unsigned int flags, Handler&& handler) - { - LLFS_VLOG(1) << BATT_THIS_FUNCTION << BATT_INSPECT(req) << BATT_INSPECT(parent) - << BATT_INSPECT(name) << BATT_INSPECT(newparent) << BATT_INSPECT(newname) - << BATT_INSPECT(flags); - - batt::Status push_status = this->work_queue_->push_job( - [this, req, parent, name = std::string{name}, newparent, newname = std::string{newname}, - flags, handler = BATT_FORWARD(handler)]() mutable { - BATT_FORWARD(handler) - (this->derived_this()->rename(req, parent, name, newparent, newname, flags)); - }); - - if (!push_status.ok()) { - BATT_FORWARD(handler)(push_status); - } - } - - /** \brief - */ // 14/44 - template - void async_hard_link(fuse_req_t req, fuse_ino_t ino, fuse_ino_t newparent, const char* newname, - Handler&& handler) - { - LLFS_VLOG(1) << BATT_THIS_FUNCTION << BATT_INSPECT(req) << BATT_INSPECT(ino) - << BATT_INSPECT(newparent) << BATT_INSPECT(newname); - - batt::Status push_status = - this->work_queue_->push_job([this, req, ino, newparent, newname = std::string{newname}, - handler = BATT_FORWARD(handler)]() mutable { - BATT_FORWARD(handler)(this->derived_this()->hard_link(req, ino, newparent, newname)); - }); - - if (!push_status.ok()) { - BATT_FORWARD(handler)(batt::StatusOr{push_status}); - } - } - - /** \brief - */ // 15/44 - template - void async_open(fuse_req_t req, fuse_ino_t ino, fuse_file_info* fi, Handler&& handler) - { - LLFS_VLOG(1) << BATT_THIS_FUNCTION << BATT_INSPECT(req) << BATT_INSPECT(ino) - << BATT_INSPECT(fi); - - BATT_CHECK_NOT_NULLPTR(fi); - - batt::Status push_status = this->work_queue_->push_job( - [this, req, ino, fi = *fi, handler = BATT_FORWARD(handler)]() mutable { - BATT_FORWARD(handler)(this->derived_this()->open(req, ino, fi)); - }); - - if (!push_status.ok()) { - BATT_FORWARD(handler)(batt::StatusOr{push_status}); - } - } - - /** \brief - */ // 16/44 - template - void async_read(fuse_req_t req, fuse_ino_t ino, size_t size, FileOffset offset, FuseFileHandle fh, - Handler&& handler) - { - LLFS_VLOG(1) << BATT_THIS_FUNCTION << BATT_INSPECT(req) << BATT_INSPECT(ino) - << BATT_INSPECT(size) << BATT_INSPECT(offset) << BATT_INSPECT(fh); - - batt::Status push_status = this->work_queue_->push_job( - [this, req, ino, size, offset, fh, handler = BATT_FORWARD(handler)]() mutable { - BATT_FORWARD(handler)(this->derived_this()->read(req, ino, size, offset, fh)); - }); - - if (!push_status.ok()) { - BATT_FORWARD(handler)(batt::StatusOr{push_status}); - } - } - - /** \brief - */ // 17/44 - template - void async_write(fuse_req_t req, fuse_ino_t ino, const batt::ConstBuffer& buffer, - FileOffset offset, FuseFileHandle fh, Handler&& handler) - { - LLFS_VLOG(1) << BATT_THIS_FUNCTION << BATT_INSPECT(req) << BATT_INSPECT(ino) - << BATT_INSPECT(batt::make_printable(buffer)) << BATT_INSPECT(offset) - << BATT_INSPECT(fh); - - batt::Status push_status = this->work_queue_->push_job( - [this, req, ino, buffer, offset, fh, handler = BATT_FORWARD(handler)]() mutable { - BATT_FORWARD(handler)(this->derived_this()->write(req, ino, buffer, offset, fh)); - }); - - if (!push_status.ok()) { - BATT_FORWARD(handler)(batt::StatusOr{push_status}); - } - } - - /** \brief - */ // 18/44 - template - void async_flush(fuse_req_t req, fuse_ino_t ino, FuseFileHandle fh, Handler&& handler) - { - LLFS_VLOG(1) << BATT_THIS_FUNCTION << BATT_INSPECT(req) << BATT_INSPECT(ino) - << BATT_INSPECT(fh); - - batt::Status push_status = this->work_queue_->push_job( - [this, req, ino, fh, handler = BATT_FORWARD(handler)]() mutable { - BATT_FORWARD(handler)(this->derived_this()->flush(req, ino, fh)); - }); - - if (!push_status.ok()) { - BATT_FORWARD(handler)(push_status); - } - } - - /** \brief - */ // 19/44 - template - void async_release(fuse_req_t req, fuse_ino_t ino, FuseFileHandle fh, FileOpenFlags flags, - Handler&& handler) - { - LLFS_VLOG(1) << BATT_THIS_FUNCTION << BATT_INSPECT(req) << BATT_INSPECT(ino) - << BATT_INSPECT(fh); - - batt::Status push_status = this->work_queue_->push_job( - [this, req, ino, fh, flags, handler = BATT_FORWARD(handler)]() mutable { - BATT_FORWARD(handler)(this->derived_this()->release(req, ino, fh, flags)); - }); - - if (!push_status.ok()) { - BATT_FORWARD(handler)(push_status); - } - } - - /** \brief - */ // 20/44 - template - void async_fsync(fuse_req_t req, fuse_ino_t ino, IsDataSync datasync, FuseFileHandle fh, - Handler&& handler) - { - LLFS_VLOG(1) << BATT_THIS_FUNCTION << BATT_INSPECT(req) << BATT_INSPECT(ino) - << BATT_INSPECT(datasync) << BATT_INSPECT(fh); - - batt::Status push_status = this->work_queue_->push_job( - [this, req, ino, datasync, fh, handler = BATT_FORWARD(handler)]() mutable { - BATT_FORWARD(handler)(this->derived_this()->fsync(req, ino, datasync, fh)); - }); - - if (!push_status.ok()) { - BATT_FORWARD(handler)(push_status); - } - } - - /** \brief - */ // 21/44 - template - void async_opendir(fuse_req_t req, fuse_ino_t ino, fuse_file_info* fi, Handler&& handler) - { - LLFS_VLOG(1) << BATT_THIS_FUNCTION << BATT_INSPECT(req) << BATT_INSPECT(ino) - << BATT_INSPECT(fi); - - BATT_CHECK_NOT_NULLPTR(fi); - - batt::Status push_status = this->work_queue_->push_job( - [this, req, ino, fi = *fi, handler = BATT_FORWARD(handler)]() mutable { - BATT_FORWARD(handler)(this->derived_this()->opendir(req, ino, fi)); - }); - - if (!push_status.ok()) { - BATT_FORWARD(handler)(batt::StatusOr{push_status}); - } - } - - /** \brief - */ // 22/44 - template - void async_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, DirentOffset off, - FuseFileHandle fh, Handler&& handler) - { - LLFS_VLOG(1) << BATT_THIS_FUNCTION // - << BATT_INSPECT(req) // - << BATT_INSPECT(ino) // - << BATT_INSPECT(size) // - << BATT_INSPECT(off) // - << BATT_INSPECT(fh); - - batt::Status push_status = this->work_queue_->push_job( - [this, req, ino, size, off, fh, handler = BATT_FORWARD(handler)]() mutable { - BATT_FORWARD(handler) - (this->derived_this()->readdir(req, ino, size, off, fh)); - }); - - if (!push_status.ok()) { - BATT_FORWARD(handler)(batt::StatusOr{push_status}); - } - } - - /** \brief - */ // 23/44 - template - void async_releasedir(fuse_req_t req, fuse_ino_t ino, FuseFileHandle fh, Handler&& handler) - { - LLFS_VLOG(1) << BATT_THIS_FUNCTION // - << BATT_INSPECT(req) // - << BATT_INSPECT(ino) // - << BATT_INSPECT(fh); - - batt::Status push_status = this->work_queue_->push_job( - [this, req, ino, fh, handler = BATT_FORWARD(handler)]() mutable { - BATT_FORWARD(handler)(this->derived_this()->releasedir(req, ino, fh)); - }); - - if (!push_status.ok()) { - BATT_FORWARD(handler)(push_status); - } - } - - /** \brief - */ // 24/44 - template - void async_fsyncdir(fuse_req_t req, fuse_ino_t ino, IsDataSync datasync, FuseFileHandle fh, - Handler&& handler) - { - LLFS_VLOG(1) << BATT_THIS_FUNCTION // - << BATT_INSPECT(req) // - << BATT_INSPECT(ino) // - << BATT_INSPECT(datasync) // - << BATT_INSPECT(fh); - - batt::Status push_status = this->work_queue_->push_job( - [this, req, ino, datasync, fh, handler = BATT_FORWARD(handler)]() mutable { - BATT_FORWARD(handler) - (this->derived_this()->fsyncdir(req, ino, datasync, fh)); - }); - - if (!push_status.ok()) { - BATT_FORWARD(handler)(push_status); - } - } - - /** \brief - */ // 25/44 - template - void async_statfs(fuse_req_t req, fuse_ino_t ino, Handler&& handler) - { - LLFS_VLOG(1) << BATT_THIS_FUNCTION // - << BATT_INSPECT(req) // - << BATT_INSPECT(ino); - - batt::Status push_status = - this->work_queue_->push_job([this, req, ino, handler = BATT_FORWARD(handler)]() mutable { - BATT_FORWARD(handler)(this->derived_this()->statfs(req, ino)); - }); - - if (!push_status.ok()) { - BATT_FORWARD(handler)(batt::StatusOr{push_status}); - } - } - - /** \brief - */ // 26/44 - template - void async_set_extended_attribute(fuse_req_t req, fuse_ino_t ino, - const FuseImplBase::ExtendedAttribute& attr, int flags, - Handler&& handler) - { - LLFS_VLOG(1) << BATT_THIS_FUNCTION // - << BATT_INSPECT(req) // - << BATT_INSPECT(ino) // - << BATT_INSPECT(batt::make_printable(attr)) // - << BATT_INSPECT(flags); - - batt::Status push_status = this->work_queue_->push_job( - [this, req, ino, attr, flags, handler = BATT_FORWARD(handler)]() mutable { - BATT_FORWARD(handler) - (this->derived_this()->set_extended_attribute(req, ino, attr, flags)); - }); - - if (!push_status.ok()) { - BATT_FORWARD(handler)(push_status); - } - } - - /** \brief - */ // 27/44 - template - void async_get_extended_attribute(fuse_req_t req, fuse_ino_t ino, const char* name, size_t size, - Handler&& handler) - { - LLFS_VLOG(1) << BATT_THIS_FUNCTION // - << BATT_INSPECT(req) // - << BATT_INSPECT(ino) // - << BATT_INSPECT(name) // - << BATT_INSPECT(size); - - batt::Status push_status = - this->work_queue_->push_job([this, req, ino, name = std::string{name}, size, - handler = BATT_FORWARD(handler)]() mutable { - BATT_FORWARD(handler) - (this->derived_this()->get_extended_attribute(req, ino, name, size)); - }); - - if (!push_status.ok()) { - BATT_FORWARD(handler) - (batt::StatusOr{push_status}); - } - } - - /** \brief - */ // 29/44 - template - void async_remove_extended_attribute(fuse_req_t req, fuse_ino_t ino, const char* name, - Handler&& handler) - { - LLFS_VLOG(1) << BATT_THIS_FUNCTION // - << BATT_INSPECT(req) // - << BATT_INSPECT(ino) // - << BATT_INSPECT(name); - - batt::Status push_status = this->work_queue_->push_job( - [this, req, ino, name = std::string{name}, handler = BATT_FORWARD(handler)]() mutable { - BATT_FORWARD(handler)(this->derived_this()->remove_extended_attribute(req, ino, name)); - }); - - if (!push_status.ok()) { - BATT_FORWARD(handler)(push_status); - } - } - - /** \brief - */ // 30/44 - template - void async_check_access(fuse_req_t req, fuse_ino_t ino, int mask, Handler&& handler) - { - LLFS_VLOG(1) << BATT_THIS_FUNCTION // - << BATT_INSPECT(req) // - << BATT_INSPECT(ino) // - << BATT_INSPECT(mask); - - batt::Status push_status = this->work_queue_->push_job( - [this, req, ino, mask, handler = BATT_FORWARD(handler)]() mutable { - BATT_FORWARD(handler)(this->derived_this()->check_access(req, ino, mask)); - }); - - if (!push_status.ok()) { - BATT_FORWARD(handler)(push_status); - } - } - - /** \brief - */ // 31/44 - template - void async_create(fuse_req_t req, fuse_ino_t parent, const char* name, mode_t mode, - fuse_file_info* fi, Handler&& handler) - { - LLFS_VLOG(1) << BATT_THIS_FUNCTION // - << BATT_INSPECT(req) // - << BATT_INSPECT(parent) // - << BATT_INSPECT(name) // - << BATT_INSPECT(DumpFileMode{mode}) // - << BATT_INSPECT(fi); - - BATT_CHECK_NOT_NULLPTR(fi); - - batt::Status push_status = - this->work_queue_->push_job([this, req, parent, name = std::string{name}, mode, fi = *fi, - handler = BATT_FORWARD(handler)]() mutable { - BATT_FORWARD(handler)(this->derived_this()->create(req, parent, name, mode, fi)); - }); - - if (!push_status.ok()) { - BATT_FORWARD(handler) - (batt::StatusOr{push_status}); - } - } - - /** \brief - */ // 35/44 - template - void async_ioctl(fuse_req_t req, fuse_ino_t ino, unsigned int cmd, void* arg, - struct fuse_file_info* fi, unsigned flags, const batt::ConstBuffer& in_buf, - size_t out_bufsz, Handler&& handler) - { - LLFS_VLOG(1) << BATT_THIS_FUNCTION // - << BATT_INSPECT(req) // - << BATT_INSPECT(ino) // - << BATT_INSPECT(cmd) // - << BATT_INSPECT(arg) // - << BATT_INSPECT(fi) // - << BATT_INSPECT(flags) // - << BATT_INSPECT(in_buf.size()) // - << BATT_INSPECT(out_bufsz); - - BATT_CHECK_NOT_NULLPTR(fi); - - batt::Status push_status = - this->work_queue_->push_job([this, req, ino, cmd, arg, fi, flags, in_buf, out_bufsz, - handler = BATT_FORWARD(handler)]() mutable { - BATT_FORWARD(handler) - (this->derived_this()->ioctl(req, ino, cmd, arg, fi, flags, in_buf, out_bufsz)); - }); - - if (!push_status.ok()) { - BATT_FORWARD(handler)(batt::StatusOr{push_status}); - } - } - - /** \brief - */ // 37/44 - template - void async_write_buf(fuse_req_t req, fuse_ino_t ino, const FuseImplBase::ConstBufferVec& bufv, - FileOffset offset, FuseFileHandle fh, std::shared_ptr&& storage, - Handler&& handler) - { - // TODO [tastolfi 2023-07-12] print bufv - // - LLFS_VLOG(1) << BATT_THIS_FUNCTION // - << BATT_INSPECT(req) // - << BATT_INSPECT(ino) // - << BATT_INSPECT(offset) // - << BATT_INSPECT(fh); - - batt::Status push_status = - this->work_queue_->push_job([this, req, ino, bufv, offset, fh, storage = std::move(storage), - handler = BATT_FORWARD(handler)]() mutable { - BATT_FORWARD(handler)(this->derived_this()->write_buf(req, ino, bufv, offset, fh)); - }); - - if (!push_status.ok()) { - BATT_FORWARD(handler)(batt::StatusOr{push_status}); - } - } - - /** \brief - */ // 38/44 - template - void async_retrieve_reply(fuse_req_t req, void* cookie, fuse_ino_t ino, off_t offset, - struct fuse_bufvec* bufv, Handler&& handler) - { - LLFS_VLOG(1) << BATT_THIS_FUNCTION // - << BATT_INSPECT(req) // - << BATT_INSPECT(cookie) // - << BATT_INSPECT(ino) // - << BATT_INSPECT(offset) // - << BATT_INSPECT(bufv); - - batt::Status push_status = this->work_queue_->push_job( - [this, req, cookie, ino, offset, bufv, handler = BATT_FORWARD(handler)]() mutable { - auto on_scope_exit = batt::finally([&] { - BATT_FORWARD(handler)(); - }); - this->derived_this()->retrieve_reply(req, cookie, ino, offset, bufv); - }); - - if (!push_status.ok()) { - LLFS_LOG_WARNING() << "Could not push request to work queue;" << BATT_INSPECT(push_status); - BATT_FORWARD(handler)(); - } - } - - /** \brief - */ // 39/44 - template - void async_forget_multiple_inodes(fuse_req_t req, batt::Slice forgets, - Handler&& handler) - { - LLFS_VLOG(1) << BATT_THIS_FUNCTION // - << BATT_INSPECT(req) // - /*<< BATT_INSPECT(batt::make_printable(forgets)) TODO [tastolfi 2023-06-30] */; - - batt::Status push_status = this->work_queue_->push_job( - [this, req, forgets, handler = BATT_FORWARD(handler)]() mutable { - auto on_scope_exit = batt::finally([&] { - BATT_FORWARD(handler)(); - }); - this->derived_this()->forget_multiple_inodes(req, forgets); - }); - - if (!push_status.ok()) { - LLFS_LOG_WARNING() << "Could not push request to work queue;" << BATT_INSPECT(push_status); - BATT_FORWARD(handler)(); - } - } - - /** \brief - */ // 41/44 - template - void async_file_allocate(fuse_req_t req, fuse_ino_t ino, int mode, FileOffset offset, - FileLength length, fuse_file_info* fi, Handler&& handler) - { - LLFS_VLOG(1) << BATT_THIS_FUNCTION // - << BATT_INSPECT(req) // - << BATT_INSPECT(ino) // - << BATT_INSPECT(DumpFileMode{mode}) // - << BATT_INSPECT(offset) // - << BATT_INSPECT(length) // - << BATT_INSPECT(fi); - - batt::Status push_status = this->work_queue_->push_job( - [this, req, ino, mode, offset, length, fi, handler = BATT_FORWARD(handler)]() mutable { - BATT_FORWARD(handler) - (this->derived_this()->file_allocate(req, ino, mode, offset, length, fi)); - }); - - if (!push_status.ok()) { - BATT_FORWARD(handler)(push_status); - } - } - - /** \brief - */ // 42/44 - template - void async_readdirplus(fuse_req_t req, fuse_ino_t ino, size_t size, DirentOffset offset, - FuseFileHandle fh, Handler&& handler) - { - LLFS_VLOG(1) << BATT_THIS_FUNCTION // - << BATT_INSPECT(req) // - << BATT_INSPECT(ino) // - << BATT_INSPECT(size) // - << BATT_INSPECT(offset) // - << BATT_INSPECT(fh); - - batt::Status push_status = this->work_queue_->push_job( - [this, req, ino, size, offset, fh, handler = BATT_FORWARD(handler)]() mutable { - BATT_FORWARD(handler) - (this->derived_this()->readdirplus(req, ino, size, offset, fh)); - }); - - if (!push_status.ok()) { - BATT_FORWARD(handler)(batt::StatusOr{push_status}); - } - } - - private: - std::shared_ptr work_queue_; -}; - -} //namespace llfs - -#endif // LLFS_WORKER_TASK_FUSE_IMPL_HPP diff --git a/src/llfs_cli/list_command.cpp b/src/llfs_cli/list_command.cpp deleted file mode 100644 index d974bf23..00000000 --- a/src/llfs_cli/list_command.cpp +++ /dev/null @@ -1,72 +0,0 @@ -//#=##=##=#==#=#==#===#+==#+==========+==+=+=+=+=+=++=+++=+++++=-++++=-+++++++++++ -// -// Part of the LLFS Project, under Apache License v2.0. -// See https://www.apache.org/licenses/LICENSE-2.0 for license information. -// SPDX short identifier: Apache-2.0 -// -//+++++++++++-+-+--+----- --- -- - - - - - -#include -// - -#include -#include -#include - -#include - -#include - -namespace llfs_cli { - -using namespace llfs; - -//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - -// -CLI::App* add_list_command(CLI::App* cmd) -{ - CLI::App* list_cmd = cmd->add_subcommand("list", "List contents of storage files (.llfs)"); - - auto args = std::make_shared(); - - list_cmd->add_option("files", args->files, "Files whose contents to list."); - - list_cmd->callback([args] { - run_list_command(*args); - }); - - return list_cmd; -} - -//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - - -// -void run_list_command(ListCommandArgs& args) -{ - StatusOr ioring = ScopedIoRing::make_new(MaxQueueDepth{32}, ThreadPoolSize{1}); - BATT_CHECK_OK(ioring); - - std::cout << std::endl; - - for (auto f : args.files) { - std::cout << f << ":" << std::endl; - - StatusOr> file = IoRingRawBlockFile::open( - ioring->get_io_ring(), f.c_str(), /*flags=*/O_RDONLY, /*mode=*/None); - BATT_CHECK_OK(file); - - StatusOr>> config_blocks = - read_storage_file(**file, /*start_offset=*/0); - BATT_CHECK_OK(config_blocks); - - for (const auto& block : *config_blocks) { - for (const PackedConfigSlot& slot : block->get_const().slots) { - std::cout << std::setw(16) << std::setfill(' ') - << PackedConfigSlotBase::Tag::to_string(slot.tag) << " " << slot.uuid - << std::endl; - } - } - std::cout << std::endl; - } -} - -} // namespace llfs_cli diff --git a/src/llfs_cli/list_command.hpp b/src/llfs_cli/list_command.hpp deleted file mode 100644 index 24a757b5..00000000 --- a/src/llfs_cli/list_command.hpp +++ /dev/null @@ -1,34 +0,0 @@ -//#=##=##=#==#=#==#===#+==#+==========+==+=+=+=+=+=++=+++=+++++=-++++=-+++++++++++ -// -// Part of the LLFS Project, under Apache License v2.0. -// See https://www.apache.org/licenses/LICENSE-2.0 for license information. -// SPDX short identifier: Apache-2.0 -// -//+++++++++++-+-+--+----- --- -- - - - - - -#pragma once -#ifndef LLFS_CLI_LIST_COMMAND_HPP -#define LLFS_CLI_LIST_COMMAND_HPP - -#include - -#include - -#include -#include - -namespace llfs_cli { - -using namespace llfs::int_types; - -CLI::App* add_list_command(CLI::App* app); - -struct ListCommandArgs { - std::vector files; -}; - -void run_list_command(ListCommandArgs& args); - -} // namespace llfs_cli - -#endif // LLFS_CLI_LIST_COMMAND_HPP diff --git a/src/llfs_cli/main.cpp b/src/llfs_cli/main.cpp deleted file mode 100644 index b5ffb41d..00000000 --- a/src/llfs_cli/main.cpp +++ /dev/null @@ -1,34 +0,0 @@ -//#=##=##=#==#=#==#===#+==#+==========+==+=+=+=+=+=++=+++=+++++=-++++=-+++++++++++ -// -// Part of the LLFS Project, under Apache License v2.0. -// See https://www.apache.org/licenses/LICENSE-2.0 for license information. -// SPDX short identifier: Apache-2.0 -// -//+++++++++++-+-+--+----- --- -- - - - - - -//######=###=##=#=#=#=#=#==#==#====#+==#+==============+==+==+==+=+==+=+=+=+=+=+=+ -// LLFS Command-Line Interface. -// - -#include - -#include -#include -#include - -#include - -#include - -int main(int argc, char** argv) -{ - CLI::App app{"Low-Level File System (LLFS) Command Line Utility"}; - - llfs_cli::add_list_command(&app); - - app.require_subcommand(); - - CLI11_PARSE(app, argc, argv); - - return 0; -} diff --git a/src/llfs_fuse/main.cpp b/src/llfs_fuse/main.cpp deleted file mode 100644 index 346e2ab6..00000000 --- a/src/llfs_fuse/main.cpp +++ /dev/null @@ -1,163 +0,0 @@ -//#=##=##=#==#=#==#===#+==#+==========+==+=+=+=+=+=++=+++=+++++=-++++=-+++++++++++ -// -// Part of the LLFS Project, under Apache License v2.0. -// See https://www.apache.org/licenses/LICENSE-2.0 for license information. -// SPDX short identifier: Apache-2.0 -// -//+++++++++++-+-+--+----- --- -- - - - - - -#include -#include -#include - -#include - -#include - -#include - -#include -#include - -namespace termxx { - -namespace color { - -constexpr auto Black = "\033[30m"; -constexpr auto Red = "\033[31m"; -constexpr auto Green = "\033[32m"; -constexpr auto Yellow = "\033[33m"; -constexpr auto Blue = "\033[34m"; -constexpr auto Magenta = "\033[35m"; -constexpr auto Cyan = "\033[36m"; -constexpr auto White = "\033[37m"; - -} //namespace color - -namespace fill { - -constexpr auto Black = "\033[40m"; -constexpr auto Red = "\033[41m"; -constexpr auto Green = "\033[42m"; -constexpr auto Yellow = "\033[43m"; -constexpr auto Blue = "\033[44m"; -constexpr auto Magenta = "\033[45m"; -constexpr auto Cyan = "\033[46m"; -constexpr auto White = "\033[47m"; - -} //namespace fill - -namespace style { - -constexpr auto Bold = "\033[1m"; -constexpr auto BoldOff = "\033[21m"; - -constexpr auto Italic = "\033[3m"; -constexpr auto ItalicOff = "\033[23m"; - -constexpr auto Underline = "\033[4m"; -constexpr auto UnderlineOff = "\033[24m"; - -constexpr auto Inverse = "\033[7m"; -constexpr auto InverseOff = "\033[27m"; - -} //namespace style - -constexpr auto Reset = "\033[0m"; - -} //namespace termxx - -struct CustomLogSink : google::LogSink { - void send(google::LogSeverity severity, const char* full_filename, const char* base_filename, - int line, const google::LogMessageTime& time, const char* message, - std::size_t message_len) override - { - auto& s = std::cerr; - - switch (severity) { - case google::GLOG_ERROR: - s << termxx::color::Red; - break; - - case google::GLOG_WARNING: - s << termxx::color::Yellow; - break; - - case google::GLOG_INFO: - s << termxx::color::Blue; - break; - - case google::GLOG_FATAL: // fall-through - default: - break; - } - - s << google::GetLogSeverityName(severity) - << " [" // - - //----- --- -- - - - - - << termxx::color::Green // - << std::setw(4) << 1900 + time.year() // - << "/" << std::setw(2) << 1 + time.month() // - << "/" << std::setw(2) - << time.day() // - - //----- --- -- - - - - - << termxx::color::White << termxx::style::Bold // - << ' ' << std::setw(2) << time.hour() // - << ':' << std::setw(2) << time.min() // - << ':' << std::setw(2) << time.sec() // - << "." << std::setw(6) << time.usec() // - << termxx::Reset // - - //----- --- -- - - - - - << termxx::color::Cyan // - << ' ' << std::setfill(' ') << std::setw(5) << std::this_thread::get_id() << std::setfill('0') - << ' ' - - //----- --- -- - - - - - << termxx::color::Blue << termxx::style::Underline // - << base_filename << ':' << line - << "]" // - - //----- --- -- - - - - - << termxx::Reset; - } -}; - -int main(int argc, char* argv[]) -{ - google::InitGoogleLogging(argv[0]); - - CustomLogSink sink; - google::AddLogSink(&sink); - - struct stat st; - std::memset(&st, 0, sizeof(st)); - - batt::enable_dump_tasks(); - - int rt = lstat(".", &st); - - std::cout << std::endl << llfs::DumpStat{st} << BATT_INSPECT(rt) << std::endl << std::endl; - - auto work_queue = std::make_shared(); - - std::thread t{[&work_queue] { - boost::asio::io_context io; - - llfs::WorkerTask task{batt::make_copy(work_queue), io.get_executor()}; - - io.run(); - }}; - - t.detach(); - - batt::StatusOr session = llfs::FuseSession::from_args( - argc, (const char**)argv, batt::StaticType{}, - batt::make_copy(work_queue)); - - BATT_CHECK_OK(session); - - return session->run(); -}