ffi-libfuse 0.0.1.pre
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.yardopts +1 -0
- data/README.md +100 -0
- data/lib/ffi/accessors.rb +145 -0
- data/lib/ffi/devt.rb +30 -0
- data/lib/ffi/flock.rb +47 -0
- data/lib/ffi/gnu_extensions.rb +115 -0
- data/lib/ffi/libfuse/ackbar.rb +112 -0
- data/lib/ffi/libfuse/adapter/context.rb +37 -0
- data/lib/ffi/libfuse/adapter/debug.rb +89 -0
- data/lib/ffi/libfuse/adapter/fuse2_compat.rb +91 -0
- data/lib/ffi/libfuse/adapter/fuse3_support.rb +87 -0
- data/lib/ffi/libfuse/adapter/interrupt.rb +37 -0
- data/lib/ffi/libfuse/adapter/pathname.rb +23 -0
- data/lib/ffi/libfuse/adapter/ruby.rb +334 -0
- data/lib/ffi/libfuse/adapter/safe.rb +58 -0
- data/lib/ffi/libfuse/adapter/thread_local_context.rb +36 -0
- data/lib/ffi/libfuse/adapter.rb +79 -0
- data/lib/ffi/libfuse/callbacks.rb +61 -0
- data/lib/ffi/libfuse/fuse2.rb +159 -0
- data/lib/ffi/libfuse/fuse3.rb +162 -0
- data/lib/ffi/libfuse/fuse_args.rb +166 -0
- data/lib/ffi/libfuse/fuse_buffer.rb +155 -0
- data/lib/ffi/libfuse/fuse_callbacks.rb +48 -0
- data/lib/ffi/libfuse/fuse_cmdline_opts.rb +44 -0
- data/lib/ffi/libfuse/fuse_common.rb +249 -0
- data/lib/ffi/libfuse/fuse_config.rb +205 -0
- data/lib/ffi/libfuse/fuse_conn_info.rb +211 -0
- data/lib/ffi/libfuse/fuse_context.rb +79 -0
- data/lib/ffi/libfuse/fuse_file_info.rb +100 -0
- data/lib/ffi/libfuse/fuse_loop_config.rb +43 -0
- data/lib/ffi/libfuse/fuse_operations.rb +870 -0
- data/lib/ffi/libfuse/fuse_opt.rb +54 -0
- data/lib/ffi/libfuse/fuse_poll_handle.rb +59 -0
- data/lib/ffi/libfuse/fuse_version.rb +43 -0
- data/lib/ffi/libfuse/job_pool.rb +53 -0
- data/lib/ffi/libfuse/main.rb +200 -0
- data/lib/ffi/libfuse/test/operations.rb +56 -0
- data/lib/ffi/libfuse/test.rb +3 -0
- data/lib/ffi/libfuse/thread_pool.rb +147 -0
- data/lib/ffi/libfuse/version.rb +8 -0
- data/lib/ffi/libfuse.rb +24 -0
- data/lib/ffi/ruby_object.rb +95 -0
- data/lib/ffi/stat/constants.rb +29 -0
- data/lib/ffi/stat/native.rb +50 -0
- data/lib/ffi/stat/time_spec.rb +137 -0
- data/lib/ffi/stat.rb +96 -0
- data/lib/ffi/stat_vfs.rb +81 -0
- data/lib/ffi/struct_array.rb +39 -0
- data/lib/ffi/struct_wrapper.rb +100 -0
- data/sample/memory_fs.rb +189 -0
- data/sample/no_fs.rb +69 -0
- metadata +165 -0
@@ -0,0 +1,79 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module FFI
|
4
|
+
module Libfuse
|
5
|
+
# This namespace contains helper Modules that alter the signature or behaviour of the native {FuseOperations}
|
6
|
+
# callbacks
|
7
|
+
#
|
8
|
+
# There are two types of Adapters
|
9
|
+
#
|
10
|
+
# 1) Wrap many fuse methods in a proc that manipulates the arguments in a consistent way
|
11
|
+
#
|
12
|
+
# These will implement {FuseOperations#fuse_wrappers} to add the proc which can then...
|
13
|
+
#
|
14
|
+
# * prepend additional arguments - eg. ({Context})
|
15
|
+
# * wrap common arguments - eg. ({Pathname})
|
16
|
+
# * handle return values/exceptions - eg. ({Safe})
|
17
|
+
# * or just wrap the underlying block - eg. ({Debug})
|
18
|
+
#
|
19
|
+
# 2) Override specific callback methods to change their signatures
|
20
|
+
#
|
21
|
+
# These will prepend an internal module that implements the callback methods, manipulating the
|
22
|
+
# argument list and passing on to super and then manipulating the result.
|
23
|
+
#
|
24
|
+
# The prepend module must include Adapter so that the module methods themselves do not register as
|
25
|
+
# callbacks unless there is an implementation by the including class.
|
26
|
+
#
|
27
|
+
# eg. {Ruby}, {Fuse2Compat}, {Fuse3Support}
|
28
|
+
module Adapter
|
29
|
+
# Does something other than an adapter (ie the underlying filesystem itself) implement fuse_method.
|
30
|
+
#
|
31
|
+
# Can be called by custom implementations of fuse_respond_to? to confirm that the implementing module
|
32
|
+
# is not a type 2 Adapter (which itself will call super)
|
33
|
+
def fuse_super_respond_to?(fuse_method)
|
34
|
+
return false unless respond_to?(fuse_method)
|
35
|
+
|
36
|
+
m = method(fuse_method)
|
37
|
+
m = m.super_method while m && Adapter.include?(m.owner)
|
38
|
+
|
39
|
+
m && true
|
40
|
+
end
|
41
|
+
|
42
|
+
# @!visibility private
|
43
|
+
|
44
|
+
# Use {#fuse_super_respond_to?} if no super implementation
|
45
|
+
def fuse_respond_to?(fuse_method)
|
46
|
+
return super if defined?(super)
|
47
|
+
|
48
|
+
fuse_super_respond_to?(fuse_method)
|
49
|
+
end
|
50
|
+
|
51
|
+
class << self
|
52
|
+
# @!visibility private
|
53
|
+
|
54
|
+
# is mod an Adapter
|
55
|
+
def include?(mod)
|
56
|
+
adapters.include?(mod)
|
57
|
+
end
|
58
|
+
|
59
|
+
def included(mod)
|
60
|
+
adapters << mod
|
61
|
+
end
|
62
|
+
|
63
|
+
def adapters
|
64
|
+
# Avoid Kernel#open being considered a fuse callback
|
65
|
+
@adapters ||= [Kernel, Object, BasicObject]
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
require_relative 'adapter/context'
|
73
|
+
require_relative 'adapter/thread_local_context'
|
74
|
+
require_relative 'adapter/debug'
|
75
|
+
require_relative 'adapter/ruby'
|
76
|
+
require_relative 'adapter/interrupt'
|
77
|
+
require_relative 'adapter/pathname'
|
78
|
+
require_relative 'adapter/fuse3_support'
|
79
|
+
require_relative 'adapter/fuse2_compat'
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module FFI
|
4
|
+
module Libfuse
|
5
|
+
# Methods to register callbacks and wrappers
|
6
|
+
module Callbacks
|
7
|
+
# Registers block as a callback method
|
8
|
+
#
|
9
|
+
# @note wrappers are defined in inside out order
|
10
|
+
#
|
11
|
+
# @param [Symbol] method the callback being registered
|
12
|
+
# @param [Array<Proc,Hash<:wrapper,:excludes>,Object>] wrappers with each entry being
|
13
|
+
#
|
14
|
+
# - a Proc(method, *args, &block)
|
15
|
+
#
|
16
|
+
# either handling the callback itself or yielding *args (possibly after manipulation) onwards to &block.
|
17
|
+
# Do not include method in the yield args!
|
18
|
+
# - a Hash with keys
|
19
|
+
#
|
20
|
+
# - wrapper: [Proc] as above
|
21
|
+
# - excludes: [Array<Symbol>] names of methods that the proc should not apply to. Useful when registering
|
22
|
+
# the same list of wrappers for many methods
|
23
|
+
# - an Object responding to #method(*args,&block)
|
24
|
+
#
|
25
|
+
# @param [Proc] block if provided is used as the innermost block to handle the callback - equivalent to
|
26
|
+
# being the first entry in wrappers list
|
27
|
+
def register(method, wrappers = [], &block)
|
28
|
+
callback = wrappers.each.inject(block) do |b, w|
|
29
|
+
next wrap_callback(method, **w, &b) if w.is_a?(Hash)
|
30
|
+
|
31
|
+
wrap_callback(method, w, &b)
|
32
|
+
end
|
33
|
+
send(:[]=, method, callback)
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def initialize_callbacks(callbacks, delegate:, wrappers: [])
|
39
|
+
callbacks.select { |m| respond_to_callback?(m, delegate) }.each do |m|
|
40
|
+
register(m, wrappers) { |*f_args| delegate.send(m, *f_args) }
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def respond_to_callback?(method, delegate)
|
45
|
+
delegate.respond_to?(method)
|
46
|
+
end
|
47
|
+
|
48
|
+
def wrap_callback(method, proc_wrapper = nil, wrapper: proc_wrapper, excludes: [], &block)
|
49
|
+
return block if excludes.include?(method)
|
50
|
+
|
51
|
+
# Wrapper proc takes fuse_method as first arg, but the resulting proc only takes the callback args
|
52
|
+
# ie so wrappers should not yield the fuse_method onwards!!
|
53
|
+
return proc { |*args| wrapper.call(method, *args, &block) } if wrapper.is_a?(Proc)
|
54
|
+
|
55
|
+
return proc { |*args| wrapper.send(method, *args, &block) } if wrapper.respond_to?(method)
|
56
|
+
|
57
|
+
block
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,159 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'fuse_version'
|
4
|
+
require_relative 'fuse_operations'
|
5
|
+
require_relative 'fuse_args'
|
6
|
+
require_relative 'fuse_common'
|
7
|
+
require_relative '../ruby_object'
|
8
|
+
|
9
|
+
module FFI
|
10
|
+
# Ruby FFI Binding for [libfuse](https://github.com/libfuse/libfuse)
|
11
|
+
module Libfuse
|
12
|
+
raise "Cannot load Fuse2 for #{FUSE_VERSION}" unless FUSE_MAJOR_VERSION == 2
|
13
|
+
|
14
|
+
typedef :pointer, :chan
|
15
|
+
typedef :pointer, :cmd
|
16
|
+
|
17
|
+
attach_function :fuse_parse_cmdline2, :fuse_parse_cmdline, [FuseArgs.by_ref, :pointer, :pointer, :pointer], :int
|
18
|
+
attach_function :fuse_mount2, :fuse_mount, [:string, FuseArgs.by_ref], :chan
|
19
|
+
attach_function :fuse_new2, :fuse_new, [:chan, FuseArgs.by_ref, FuseOperations.by_ref, :size_t, RubyObject], :fuse
|
20
|
+
attach_function :fuse_chan_fd, [:chan], :int
|
21
|
+
attach_function :fuse_read_cmd, [:fuse], :cmd, blocking: true
|
22
|
+
attach_function :fuse_process_cmd, %i[fuse cmd], :void, blocking: true
|
23
|
+
attach_function :fuse_exited2, :fuse_exited, [:fuse], :int
|
24
|
+
attach_function :fuse_unmount2, :fuse_unmount, %i[string chan], :void
|
25
|
+
attach_function :fuse_loop_mt2, :fuse_loop_mt, [:fuse], :int, blocking: true
|
26
|
+
|
27
|
+
class << self
|
28
|
+
# @!visibility private
|
29
|
+
# @!method fuse_parse_cmdline2
|
30
|
+
# @!method fuse_mount2
|
31
|
+
# @!method fuse_new2
|
32
|
+
# @!method fuse_chan_fd
|
33
|
+
# @!method fuse_read_cmd
|
34
|
+
# @!method fuse_process_cmd
|
35
|
+
# @!method fuse_exited2
|
36
|
+
# @!method fuse_unmount2
|
37
|
+
# @!method fuse_loop_mt2
|
38
|
+
end
|
39
|
+
|
40
|
+
# Helper class to managed a mounted fuse filesystem
|
41
|
+
# @!visibility private
|
42
|
+
class Fuse2 < FuseCommon
|
43
|
+
class << self
|
44
|
+
def parse_cmdline(args, handler: nil)
|
45
|
+
# This also handles -h to print help information on stderr
|
46
|
+
# Parse mountpoint, -f , -s from args
|
47
|
+
# @return [Array<(String,Boolean,Boolean)>|nil]
|
48
|
+
# mountpoint, multi_thread, foreground options from args if available
|
49
|
+
# nil if no mountpoint, or options is requesting help or version information
|
50
|
+
mountpoint_ptr = FFI::MemoryPointer.new(:pointer, 1)
|
51
|
+
multi_thread_ptr = FFI::MemoryPointer.new(:int, 1)
|
52
|
+
foreground_ptr = FFI::MemoryPointer.new(:int, 1)
|
53
|
+
|
54
|
+
return nil unless Libfuse.fuse_parse_cmdline2(args, mountpoint_ptr, multi_thread_ptr, foreground_ptr).zero?
|
55
|
+
|
56
|
+
# noinspection RubyResolve
|
57
|
+
mp_data_ptr = mountpoint_ptr.get_pointer(0)
|
58
|
+
|
59
|
+
mountpoint = mp_data_ptr.read_string unless mp_data_ptr.null?
|
60
|
+
|
61
|
+
multi_thread = multi_thread_ptr.get(:int, 0) == 1
|
62
|
+
foreground = foreground_ptr.get(:int, 0) == 1
|
63
|
+
|
64
|
+
result = { mountpoint: mountpoint, single_thread: !multi_thread, foreground: foreground }
|
65
|
+
args.parse!(Main::STANDARD_OPTIONS, [result, handler]) { |*proc_args| fuse_opt_proc(*proc_args) }
|
66
|
+
result
|
67
|
+
end
|
68
|
+
|
69
|
+
# Handle standard custom args
|
70
|
+
def fuse_opt_proc(custom, _arg, key, _outargs)
|
71
|
+
return :keep if %i[unmatched non_option].include?(key)
|
72
|
+
|
73
|
+
run_args, handler = custom
|
74
|
+
case key
|
75
|
+
when :show_help
|
76
|
+
warn Main::HELP
|
77
|
+
if handler.respond_to?(:fuse_help) && (help = handler.fuse_help)
|
78
|
+
warn help
|
79
|
+
end
|
80
|
+
when :debug
|
81
|
+
handler.fuse_debug(true) if handler.respond_to?(:fuse_debug)
|
82
|
+
when :show_version
|
83
|
+
warn Main.version
|
84
|
+
if handler.respond_to?(:fuse_version) && (version = handler.fuse_version)
|
85
|
+
warn version
|
86
|
+
end
|
87
|
+
else
|
88
|
+
return :keep
|
89
|
+
end
|
90
|
+
run_args[key] = true
|
91
|
+
:keep
|
92
|
+
end
|
93
|
+
|
94
|
+
def finalize_fuse(fuse, mountpoint, fuse_ch)
|
95
|
+
proc do
|
96
|
+
Libfuse.fuse_unmount2(mountpoint, fuse_ch) if fuse_ch
|
97
|
+
Libfuse.fuse_destroy(fuse) if fuse
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
attr_reader :mountpoint
|
103
|
+
|
104
|
+
# Have we requested an unmount (note not actually checking if OS sees the fs as mounted)
|
105
|
+
def mounted?
|
106
|
+
@fuse && Libfuse.fuse_exited2(@fuse).zero?
|
107
|
+
end
|
108
|
+
|
109
|
+
def initialize(mountpoint, args, operations, private_data)
|
110
|
+
super()
|
111
|
+
@mountpoint = mountpoint
|
112
|
+
|
113
|
+
# Hang on to our ops and private data
|
114
|
+
@operations = operations
|
115
|
+
|
116
|
+
# Note this outputs the module args.
|
117
|
+
@ch = Libfuse.fuse_mount2(@mountpoint, args)
|
118
|
+
@ch = nil if @ch&.null?
|
119
|
+
if @ch
|
120
|
+
@fuse = Libfuse.fuse_new2(@ch, args, @operations, @operations.size, private_data)
|
121
|
+
@fuse = nil if @fuse&.null?
|
122
|
+
end
|
123
|
+
ensure
|
124
|
+
# if we unmount/destroy in the finalizer then the private_data object cannot be used in destory
|
125
|
+
# as it's weakref will have been GC'd
|
126
|
+
ObjectSpace.define_finalizer(self, self.class.finalize_fuse(@fuse, @mountpoint, @ch))
|
127
|
+
end
|
128
|
+
|
129
|
+
# [IO] /dev/fuse file descriptor for use with IO.select
|
130
|
+
def io
|
131
|
+
# The FD is created (and destroyed?) by FUSE so we don't want ruby to do anything with it during GC
|
132
|
+
@io ||= ::IO.for_fd(Libfuse.fuse_chan_fd(@ch), 'r', autoclose: false)
|
133
|
+
end
|
134
|
+
|
135
|
+
def process
|
136
|
+
cmd = Libfuse.fuse_read_cmd(@fuse)
|
137
|
+
Libfuse.fuse_process_cmd(@fuse, cmd) if cmd && !cmd.null?
|
138
|
+
|
139
|
+
# return true unless fuse has exited.
|
140
|
+
Libfuse.fuse_exited2(@fuse).zero?
|
141
|
+
end
|
142
|
+
|
143
|
+
private
|
144
|
+
|
145
|
+
def native_fuse_loop_mt(**_options)
|
146
|
+
Libfuse.fuse_loop_mt2(@fuse)
|
147
|
+
end
|
148
|
+
|
149
|
+
def unmount
|
150
|
+
c = @ch
|
151
|
+
@ch = nil
|
152
|
+
Libfuse.fuse_unmount2(mountpoint, c) if c
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
# @!visibility private
|
157
|
+
Fuse = Fuse2
|
158
|
+
end
|
159
|
+
end
|
@@ -0,0 +1,162 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'fuse_common'
|
4
|
+
require_relative 'fuse_cmdline_opts'
|
5
|
+
require_relative 'fuse_loop_config'
|
6
|
+
require_relative 'fuse_args'
|
7
|
+
require_relative 'fuse_operations'
|
8
|
+
require_relative '../ruby_object'
|
9
|
+
|
10
|
+
module FFI
|
11
|
+
# Ruby FFI Binding for [libfuse](https://github.com/libfuse/libfuse)
|
12
|
+
module Libfuse
|
13
|
+
raise "Cannot load Fuse3 for #{FUSE_VERSION}" unless FUSE_MAJOR_VERSION == 3
|
14
|
+
|
15
|
+
attach_function :fuse_parse_cmdline3,
|
16
|
+
:fuse_parse_cmdline, [FuseArgs.by_ref, FuseCmdlineOpts.by_ref], :int
|
17
|
+
attach_function :fuse_cmdline_help, [], :void
|
18
|
+
attach_function :fuse_lowlevel_help, [], :void
|
19
|
+
attach_function :fuse_lib_help, [FuseArgs.by_ref], :void
|
20
|
+
attach_function :fuse_pkgversion, [], :string
|
21
|
+
attach_function :fuse_lowlevel_version, [], :void
|
22
|
+
attach_function :fuse_new3,
|
23
|
+
:fuse_new, [FuseArgs.by_ref, FuseOperations.by_ref, :size_t, RubyObject], :fuse
|
24
|
+
attach_function :fuse_mount3, :fuse_mount, %i[fuse string], :int
|
25
|
+
attach_function :fuse_loop_mt3,
|
26
|
+
:fuse_loop_mt, [:fuse, FuseLoopConfig.by_ref], :int, blocking: true
|
27
|
+
attach_function :fuse_session_fd, [:session], :int
|
28
|
+
attach_function :fuse_session_exit, [:session], :void
|
29
|
+
attach_function :fuse_session_exited, [:session], :int
|
30
|
+
attach_function :fuse_unmount3, :fuse_unmount, %i[fuse], :void
|
31
|
+
attach_function :fuse_session_receive_buf, [:session, FuseBuf.by_ref], :int, blocking: true
|
32
|
+
attach_function :fuse_session_process_buf, [:session, FuseBuf.by_ref], :void, blocking: true
|
33
|
+
|
34
|
+
class << self
|
35
|
+
# @!visibility private
|
36
|
+
# @!method fuse_parse_cmdline3
|
37
|
+
# @!method fuse_cmdline_help
|
38
|
+
# @!method fuse_lowlevel_help
|
39
|
+
# @!method fuse_lib_help
|
40
|
+
# @!method fuse_pkgversion
|
41
|
+
# @!method fuse_lowlevel_version
|
42
|
+
# @!method fuse_new3
|
43
|
+
# @!method fuse_mount3
|
44
|
+
# @!method fuse_loop_mt3
|
45
|
+
# @!method fuse_session_fd
|
46
|
+
# @!method fuse_session_exit
|
47
|
+
# @!method fuse_session_exited
|
48
|
+
# @!method fuse_unmount3
|
49
|
+
# @!method fuse_session_receive_buf
|
50
|
+
# @!method fuse_session_process_buf
|
51
|
+
end
|
52
|
+
|
53
|
+
# Helper class to managed a mounted fuse filesystem
|
54
|
+
# @!visibility private
|
55
|
+
class Fuse3 < FuseCommon
|
56
|
+
class << self
|
57
|
+
def parse_cmdline(args, handler: nil)
|
58
|
+
cmdline_opts = FuseCmdlineOpts.new
|
59
|
+
Libfuse.fuse_parse_cmdline3(args, cmdline_opts)
|
60
|
+
|
61
|
+
handler&.fuse_debug(cmdline_opts.debug) if handler.respond_to?(:fuse_debug)
|
62
|
+
|
63
|
+
# mimics fuse_main which exits after printing version info, even if -h
|
64
|
+
if cmdline_opts.show_version
|
65
|
+
show_version(handler)
|
66
|
+
elsif cmdline_opts.show_help
|
67
|
+
show_help(args, handler)
|
68
|
+
end
|
69
|
+
|
70
|
+
cmdline_opts.to_h
|
71
|
+
end
|
72
|
+
|
73
|
+
def show_version(handler)
|
74
|
+
$stdout.puts "FUSE library version #{Libfuse.fuse_pkgversion}"
|
75
|
+
Libfuse.fuse_lowlevel_version
|
76
|
+
$stdout.puts Main.version
|
77
|
+
$stdout.puts handler.fuse_version if handler.respond_to?(:fuse_version)
|
78
|
+
end
|
79
|
+
|
80
|
+
def show_help(args, handler)
|
81
|
+
$stdout.puts "usage: #{args.argv.first} [options] <mountpoint>\n\n"
|
82
|
+
$stdout.puts "FUSE options:\n"
|
83
|
+
Libfuse.fuse_cmdline_help
|
84
|
+
Libfuse.fuse_lib_help(args)
|
85
|
+
$stdout.puts "\n"
|
86
|
+
$stdout.puts Main::HELP
|
87
|
+
$stdout.puts "\n#{handler.fuse_help}" if handler.respond_to?(:fuse_help)
|
88
|
+
end
|
89
|
+
|
90
|
+
def finalize_fuse(fuse)
|
91
|
+
proc do
|
92
|
+
if fuse
|
93
|
+
Libfuse.fuse_unmount3(fuse)
|
94
|
+
Libfuse.fuse_destroy(fuse)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
attr_reader :mountpoint
|
101
|
+
|
102
|
+
# Have we requested an unmount (note not actually checking if OS sees the fs as mounted)
|
103
|
+
def mounted?
|
104
|
+
(se = session) && Libfuse.fuse_session_exited(se).zero?
|
105
|
+
end
|
106
|
+
|
107
|
+
def initialize(mountpoint, args, operations, private_data)
|
108
|
+
super()
|
109
|
+
warn 'No mountpoint provided' unless mountpoint
|
110
|
+
return unless mountpoint
|
111
|
+
|
112
|
+
@mountpoint = mountpoint
|
113
|
+
|
114
|
+
# Hang on to our ops and private data
|
115
|
+
@operations = operations
|
116
|
+
|
117
|
+
@fuse = Libfuse.fuse_new3(args, @operations, @operations.size, private_data)
|
118
|
+
@fuse = nil if @fuse&.null?
|
119
|
+
|
120
|
+
@mounted = @fuse && Libfuse.fuse_mount3(@fuse, @mountpoint).zero?
|
121
|
+
ensure
|
122
|
+
# if we unmount/destroy in the finalizer then the private_data object cannot be used in destroy
|
123
|
+
# as it's weakref will have been GC'd
|
124
|
+
ObjectSpace.define_finalizer(self, self.class.finalize_fuse(@fuse))
|
125
|
+
end
|
126
|
+
|
127
|
+
def process
|
128
|
+
buf = Thread.current[:fuse_buffer] ||= FuseBuf.new
|
129
|
+
se = Thread.current[:fuse_session] ||= session
|
130
|
+
|
131
|
+
res = Libfuse.fuse_session_receive_buf(se, buf)
|
132
|
+
|
133
|
+
# errors, or exiting
|
134
|
+
return false if res.negative?
|
135
|
+
|
136
|
+
Libfuse.fuse_session_process_buf(se, buf) if res.positive?
|
137
|
+
|
138
|
+
# return true unless fuse has exited.
|
139
|
+
Libfuse.fuse_session_exited(se).zero?
|
140
|
+
end
|
141
|
+
|
142
|
+
# [IO] /dev/fuse file descriptor for use with IO.select
|
143
|
+
def io
|
144
|
+
# The FD is created (and destroyed?) by FUSE so we don't want ruby to do anything with it during GC
|
145
|
+
@io ||= ::IO.for_fd(Libfuse.fuse_session_fd(session), 'r', autoclose: false)
|
146
|
+
end
|
147
|
+
|
148
|
+
private
|
149
|
+
|
150
|
+
def native_fuse_loop_mt(max_idle_threads: 10, **_options)
|
151
|
+
Libfuse.fuse_loop_mt3(@fuse, FuseLoopConfig.new.fill(max_idle: max_idle_threads))
|
152
|
+
end
|
153
|
+
|
154
|
+
def unmount
|
155
|
+
Libfuse.fuse_unmount3(@fuse) if @mounted && @fuse && !@fuse.null?
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
# @!visibility private
|
160
|
+
Fuse = Fuse3
|
161
|
+
end
|
162
|
+
end
|