ffi-libfuse 0.0.1.pre
Sign up to get free protection for your applications and to get access to all the features.
- 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
|