sample/memory_fs.rb in ffi-libfuse-0.0.1.rctest12 vs sample/memory_fs.rb in ffi-libfuse-0.1.0.rc20220550
- old
+ new
@@ -1,189 +1,13 @@
#!/usr/bin/env ruby
# frozen_string_literal: true
require 'ffi/libfuse'
+require 'ffi/libfuse/filesystem/virtual_fs'
# A simple in-memory filesystem defined with hashes.
-#
-# It is writable to the user that mounted it may create and edit files within it
-#
-# === Usage
-# root = Memory.new(files: { 'hello' => { 'world.txt' => 'Hello World'}})
-# root.mkdir("/hello")
-# root.("/hello/world","Hello World!\n")
-# root.write("/hello/everybody","Hello Everyone!\n")
-#
-# Libfuse::fuse_main($0,ARGV,operations: root)
-#
-#
-class MemoryFS
- # @return [Hash<String,Object>] list of file objects by path
- attr_reader :root
+class MemoryFS < FFI::Libfuse::Filesystem::VirtualFS; end
- include FFI::Libfuse::Adapter::Fuse3Support
- include FFI::Libfuse::Adapter::Ruby
- include FFI::Libfuse::Adapter::Pathname
+# Set this to test multi-threading etc...
+main_class = ENV.fetch('MEMORY_FS_SKIP_DEFAULT_ARGS', 'N') == 'Y' ? FFI::Libfuse::Main : FFI::Libfuse
- File = Struct.new(:mode, :content, :ctime, :atime, :mtime) do
- def dig(*_args)
- raise Errno::ENOTDIR
- end
-
- def fill_stat(stat = FFI::Stat.new)
- stat.file(mode: mode, ctime: ctime, atime: atime, mtime: mtime, size: content.size)
- end
- end
-
- # rubocop:disable Lint/StructNewOverride
- Dir = Struct.new(:mode, :entries, :ctime, :atime, :mtime) do
- def dig(*args)
- entries.dig(*args)
- end
-
- def fill_stat(stat = FFI::Stat.new)
- stat.directory(mode: mode, ctime: ctime, atime: atime, mtime: mtime)
- end
- end
- # rubocop:enable Lint/StructNewOverride
-
- def initialize(files: {}, max_size: 100_000, max_files: 1_000)
- now = Time.now
- @root = Dir.new(0x755, {}, now, now, now)
- @total_size = 0
- @total_files = 1
- @max_size = max_size
- @max_files = max_files
-
- build(files)
- end
-
- def build(files, path = ::Pathname.new('/'))
- files.each_pair do |basename, content|
- raise 'Initial file keys must be String' unless basename.is_a?(String)
- raise 'Initial file keys must not contain path separators' if basename =~ %r{[/\\]}
-
- entry_path = path + basename
- case content
- when String
- create(entry_path, 0x644)
- write(entry_path, content, 0)
- when Hash
- mkdir(entry_path, 0x755)
- build(content, entry_path)
- else
- raise 'Initial files must be String or Hash'
- end
- end
- end
-
- def fuse_version
- 'MemoryFS: Version x.y.z'
- end
-
- def fuse_traps
- {
- HUP: -> { reload }
- }
- end
-
- def statfs(_path, statfs_buf)
- blocks = @total_size / 1_000
- statfs_buf.bsize = 1 # block size (in Kb)
- statfs_buf.frsize = 1 # fragment size pretty much always bsize
- statfs_buf.blocks = @max_size
- statfs_buf.bfree = @max_size - blocks
- statfs_buf.bavail = @max_size - blocks
- statfs_buf.files = @max_files
- statfs_buf.ffree = @max_files - @total_files
- statfs_buf.favail = @max_files - @total_files
- 0
- end
-
- def getattr(path, stat_buf)
- entry = find(path)
- return -Errno::ENOENT::Errno unless entry
-
- entry.fill_stat(stat_buf)
- 0
- end
-
- def readdir(path, _offset, _ffi)
- %w[. ..].each { |d| yield(d, nil) }
- dir = find(path)
- dir.entries.each_pair { |k, e| yield(k, e.fill_stat) }
- end
-
- def create(path, mode, _ffi)
- dir_entries = find(path.dirname).entries
- now = Time.now
- dir_entries[path.basename.to_s] = File.new(mode, String.new, now, now, now)
- @total_files += 1
- 0
- end
-
- # op[:read] = [:pointer, :size_t, :off_t, FuseFileInfo.by_ref]
- def read(path, len, off, _ffi)
- file = find(path)
- file.atime = Time.now.utc
- FFI::Libfuse::ThreadPool.busy
- sleep 0.5
- file.content[off, len]
- end
-
- # write(const char* path, char *buf, size_t size, off_t offset, struct fuse_file_info* fi)
- def write(path, data, offset, _ffi)
- file = find(path)
- content = file.content
- @total_size -= content.size
- content[offset, data.length] = data
- @total_size += content.size
- file.mtime = Time.now.utc
- end
-
- def truncate(path, size)
- file = find(path)
- @total_size -= file.content.size
- file.content[size..-1] = ''
- file.mtime = Time.now.utc
- @total_size += file.content.size
- 0
- end
-
- def unlink(path)
- dir = find(path.dirname)
- deleted = dir.entries.delete(path.basename.to_s)
- @total_files -= 1
- @total_size -= deleted.content.size if deleted.is_a?(File)
- 0
- end
-
- def mkdir(path, mode)
- entries = find(path.dirname).entries
- now = Time.now
- entries[path.basename.to_s] = Dir.new(mode, {}, now, now, now)
- end
-
- def rmdir(path)
- dir = find(path)
- raise Errno::ENOTDIR unless dir.is_a?(Dir)
- raise Errno::ENOTEMPTY unless dir.entries.empty?
-
- find(path.dirname).entries.delete(path.basename.to_s)
- 0
- end
-
- def utimens(path, atime, mtime)
- entry = find(path)
- entry.atime = atime if atime
- entry.mtime = mtime if mtime
- 0
- end
-
- private
-
- def find(path)
- path.root? ? root : root.dig(*path.to_s.split('/')[1..])
- end
-end
-
-exit(FFI::Libfuse.fuse_main($0, *ARGV, operations: MemoryFS.new)) if __FILE__ == $0
+exit(main_class.fuse_main(operations: MemoryFS.new)) if __FILE__ == $0