ffi-libfuse 0.0.1.rctest12 → 0.3.3
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 +4 -4
- data/.yardopts +3 -1
- data/CHANGELOG.md +60 -0
- data/LICENSE +21 -0
- data/README.md +127 -44
- data/lib/ffi/accessors.rb +6 -6
- data/lib/ffi/boolean_int.rb +27 -0
- data/lib/ffi/devt.rb +23 -0
- data/lib/ffi/encoding.rb +38 -0
- data/lib/ffi/flock.rb +7 -5
- data/lib/ffi/gnu_extensions.rb +1 -1
- data/lib/ffi/libfuse/ackbar.rb +3 -3
- data/lib/ffi/libfuse/adapter/context.rb +12 -10
- data/lib/ffi/libfuse/adapter/fuse2_compat.rb +52 -51
- data/lib/ffi/libfuse/adapter/fuse3_support.rb +7 -4
- data/lib/ffi/libfuse/adapter/interrupt.rb +1 -1
- data/lib/ffi/libfuse/adapter/ruby.rb +499 -148
- data/lib/ffi/libfuse/adapter/safe.rb +12 -11
- data/lib/ffi/libfuse/adapter.rb +1 -2
- data/lib/ffi/libfuse/callbacks.rb +1 -1
- data/lib/ffi/libfuse/filesystem/accounting.rb +116 -0
- data/lib/ffi/libfuse/filesystem/mapped_dir.rb +74 -0
- data/lib/ffi/libfuse/filesystem/mapped_files.rb +141 -0
- data/lib/ffi/libfuse/filesystem/pass_through_dir.rb +55 -0
- data/lib/ffi/libfuse/filesystem/pass_through_file.rb +45 -0
- data/lib/ffi/libfuse/filesystem/utils.rb +102 -0
- data/lib/ffi/libfuse/filesystem/virtual_dir.rb +306 -0
- data/lib/ffi/libfuse/filesystem/virtual_file.rb +94 -0
- data/lib/ffi/libfuse/filesystem/virtual_fs.rb +196 -0
- data/lib/ffi/libfuse/filesystem/virtual_node.rb +101 -0
- data/lib/ffi/libfuse/filesystem.rb +25 -0
- data/lib/ffi/libfuse/fuse2.rb +32 -24
- data/lib/ffi/libfuse/fuse3.rb +28 -18
- data/lib/ffi/libfuse/fuse_args.rb +71 -34
- data/lib/ffi/libfuse/fuse_buffer.rb +128 -26
- data/lib/ffi/libfuse/fuse_callbacks.rb +1 -5
- data/lib/ffi/libfuse/fuse_common.rb +60 -61
- data/lib/ffi/libfuse/fuse_config.rb +134 -143
- data/lib/ffi/libfuse/fuse_conn_info.rb +310 -134
- data/lib/ffi/libfuse/fuse_context.rb +45 -3
- data/lib/ffi/libfuse/fuse_operations.rb +57 -21
- data/lib/ffi/libfuse/fuse_opt.rb +1 -1
- data/lib/ffi/libfuse/fuse_version.rb +10 -6
- data/lib/ffi/libfuse/gem_version.rb +54 -0
- data/lib/ffi/libfuse/main.rb +96 -48
- data/lib/ffi/libfuse/test_helper.rb +145 -0
- data/lib/ffi/libfuse/version.rb +1 -1
- data/lib/ffi/libfuse.rb +13 -4
- data/lib/ffi/ruby_object.rb +4 -1
- data/lib/ffi/stat/constants.rb +9 -0
- data/lib/ffi/stat/native.rb +36 -6
- data/lib/ffi/stat/time_spec.rb +26 -10
- data/lib/ffi/stat.rb +111 -22
- data/lib/ffi/stat_vfs.rb +59 -1
- data/lib/ffi/struct_wrapper.rb +22 -1
- data/sample/hello_fs.rb +54 -0
- data/sample/memory_fs.rb +5 -181
- data/sample/no_fs.rb +20 -21
- data/sample/pass_through_fs.rb +30 -0
- metadata +83 -10
- data/lib/ffi/libfuse/adapter/thread_local_context.rb +0 -36
- data/lib/ffi/libfuse/test/operations.rb +0 -56
- data/lib/ffi/libfuse/test.rb +0 -3
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'filesystem/virtual_fs'
|
|
4
|
+
|
|
5
|
+
module FFI
|
|
6
|
+
module Libfuse
|
|
7
|
+
# This module namespace contains classes and modules to assist with building and composing filesystems
|
|
8
|
+
#
|
|
9
|
+
# ### Virtual Filesystems
|
|
10
|
+
# Classes to help compose in-memory filesystems {VirtualFS}, {VirtualDir}, {VirtualFile}
|
|
11
|
+
#
|
|
12
|
+
# ### Mapped Filesystem
|
|
13
|
+
# Modules to map paths in the fuse filesystem to either real files or other filesystem objects
|
|
14
|
+
# {MappedFiles}, {MappedDir}
|
|
15
|
+
#
|
|
16
|
+
# ### Pass-through filesystems
|
|
17
|
+
# Classes using the mapped modules above to pass fuse callbacks directly to an underlying file/directory
|
|
18
|
+
# {PassThroughDir}, {PassThroughFile}
|
|
19
|
+
#
|
|
20
|
+
# ### Utilities
|
|
21
|
+
# {Utils} similar to FileUtils to operate directly on filesystem objects, eg to build up initial content
|
|
22
|
+
module Filesystem
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
data/lib/ffi/libfuse/fuse2.rb
CHANGED
|
@@ -20,9 +20,9 @@ module FFI
|
|
|
20
20
|
attach_function :fuse_chan_fd, [:chan], :int
|
|
21
21
|
attach_function :fuse_read_cmd, [:fuse], :cmd, blocking: true
|
|
22
22
|
attach_function :fuse_process_cmd, %i[fuse cmd], :void, blocking: true
|
|
23
|
-
attach_function :
|
|
24
|
-
attach_function :fuse_unmount2, :fuse_unmount, %i[string chan], :void
|
|
23
|
+
attach_function :fuse_unmount2, :fuse_unmount, %i[string chan], :void, blocking: true
|
|
25
24
|
attach_function :fuse_loop_mt2, :fuse_loop_mt, [:fuse], :int, blocking: true
|
|
25
|
+
attach_function :fuse_exited2, :fuse_exited, [:fuse], :int
|
|
26
26
|
|
|
27
27
|
class << self
|
|
28
28
|
# @!visibility private
|
|
@@ -62,28 +62,22 @@ module FFI
|
|
|
62
62
|
foreground = foreground_ptr.get(:int, 0) == 1
|
|
63
63
|
|
|
64
64
|
result = { mountpoint: mountpoint, single_thread: !multi_thread, foreground: foreground }
|
|
65
|
-
args.parse!(Main::STANDARD_OPTIONS, [result, handler]) {
|
|
65
|
+
args.parse!(Main::STANDARD_OPTIONS, [result, handler]) { |**op_args| fuse_opt_proc(**op_args) }
|
|
66
66
|
result
|
|
67
67
|
end
|
|
68
68
|
|
|
69
69
|
# Handle standard custom args
|
|
70
|
-
def fuse_opt_proc(
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
run_args, handler = custom
|
|
70
|
+
def fuse_opt_proc(data:, key:, **)
|
|
71
|
+
run_args, handler = data
|
|
74
72
|
case key
|
|
75
73
|
when :show_help
|
|
76
74
|
warn Main::HELP
|
|
77
|
-
if handler.respond_to?(:fuse_help)
|
|
78
|
-
warn help
|
|
79
|
-
end
|
|
75
|
+
warn handler.fuse_help if handler.respond_to?(:fuse_help)
|
|
80
76
|
when :debug
|
|
81
77
|
handler.fuse_debug(true) if handler.respond_to?(:fuse_debug)
|
|
82
78
|
when :show_version
|
|
83
79
|
warn Main.version
|
|
84
|
-
if handler.respond_to?(:fuse_version)
|
|
85
|
-
warn version
|
|
86
|
-
end
|
|
80
|
+
warn handler.fuse_version if handler.respond_to?(:fuse_version)
|
|
87
81
|
else
|
|
88
82
|
return :keep
|
|
89
83
|
end
|
|
@@ -103,7 +97,7 @@ module FFI
|
|
|
103
97
|
|
|
104
98
|
# Have we requested an unmount (note not actually checking if OS sees the fs as mounted)
|
|
105
99
|
def mounted?
|
|
106
|
-
@fuse &&
|
|
100
|
+
@fuse && !fuse_exited?
|
|
107
101
|
end
|
|
108
102
|
|
|
109
103
|
def initialize(mountpoint, args, operations, private_data)
|
|
@@ -113,17 +107,15 @@ module FFI
|
|
|
113
107
|
# Hang on to our ops and private data
|
|
114
108
|
@operations = operations
|
|
115
109
|
|
|
116
|
-
# Note this outputs the module args.
|
|
117
|
-
@ch = Libfuse.fuse_mount2(@mountpoint, args)
|
|
110
|
+
# Note this outputs the module args. OSX cannot handle null mountpoint with -h/-V
|
|
111
|
+
@ch = Libfuse.fuse_mount2(@mountpoint || '', args)
|
|
118
112
|
@ch = nil if @ch&.null?
|
|
119
113
|
if @ch
|
|
120
114
|
@fuse = Libfuse.fuse_new2(@ch, args, @operations, @operations.size, private_data)
|
|
121
115
|
@fuse = nil if @fuse&.null?
|
|
122
116
|
end
|
|
123
117
|
ensure
|
|
124
|
-
|
|
125
|
-
# as it's weakref will have been GC'd
|
|
126
|
-
ObjectSpace.define_finalizer(self, self.class.finalize_fuse(@fuse, @mountpoint, @ch))
|
|
118
|
+
define_finalizer
|
|
127
119
|
end
|
|
128
120
|
|
|
129
121
|
# [IO] /dev/fuse file descriptor for use with IO.select
|
|
@@ -132,12 +124,16 @@ module FFI
|
|
|
132
124
|
@io ||= ::IO.for_fd(Libfuse.fuse_chan_fd(@ch), 'r', autoclose: false)
|
|
133
125
|
end
|
|
134
126
|
|
|
135
|
-
def
|
|
127
|
+
def fuse_exited?
|
|
128
|
+
!Libfuse.fuse_exited2(@fuse).zero?
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def fuse_process
|
|
136
132
|
cmd = Libfuse.fuse_read_cmd(@fuse)
|
|
137
|
-
|
|
133
|
+
return false if cmd.null?
|
|
138
134
|
|
|
139
|
-
|
|
140
|
-
|
|
135
|
+
Libfuse.fuse_process_cmd(@fuse, cmd)
|
|
136
|
+
true
|
|
141
137
|
end
|
|
142
138
|
|
|
143
139
|
private
|
|
@@ -147,9 +143,21 @@ module FFI
|
|
|
147
143
|
end
|
|
148
144
|
|
|
149
145
|
def unmount
|
|
146
|
+
return unless @ch
|
|
147
|
+
|
|
150
148
|
c = @ch
|
|
151
149
|
@ch = nil
|
|
152
|
-
Libfuse.fuse_unmount2(mountpoint, c)
|
|
150
|
+
Libfuse.fuse_unmount2(mountpoint, c)
|
|
151
|
+
ensure
|
|
152
|
+
# Can't unmount twice
|
|
153
|
+
define_finalizer
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def define_finalizer
|
|
157
|
+
# if we unmount/destroy in the finalizer then the private_data object cannot be used in destory
|
|
158
|
+
# as it's weakref will have been GC'd
|
|
159
|
+
ObjectSpace.undefine_finalizer(self)
|
|
160
|
+
ObjectSpace.define_finalizer(self, self.class.finalize_fuse(@fuse, @mountpoint, @ch))
|
|
153
161
|
end
|
|
154
162
|
end
|
|
155
163
|
|
data/lib/ffi/libfuse/fuse3.rb
CHANGED
|
@@ -27,7 +27,7 @@ module FFI
|
|
|
27
27
|
attach_function :fuse_session_fd, [:session], :int
|
|
28
28
|
attach_function :fuse_session_exit, [:session], :void
|
|
29
29
|
attach_function :fuse_session_exited, [:session], :int
|
|
30
|
-
attach_function :fuse_unmount3, :fuse_unmount, %i[fuse], :void
|
|
30
|
+
attach_function :fuse_unmount3, :fuse_unmount, %i[fuse], :void, blocking: true
|
|
31
31
|
attach_function :fuse_session_receive_buf, [:session, FuseBuf.by_ref], :int, blocking: true
|
|
32
32
|
attach_function :fuse_session_process_buf, [:session, FuseBuf.by_ref], :void, blocking: true
|
|
33
33
|
|
|
@@ -56,7 +56,7 @@ module FFI
|
|
|
56
56
|
class << self
|
|
57
57
|
def parse_cmdline(args, handler: nil)
|
|
58
58
|
cmdline_opts = FuseCmdlineOpts.new
|
|
59
|
-
Libfuse.fuse_parse_cmdline3(args, cmdline_opts)
|
|
59
|
+
return nil unless Libfuse.fuse_parse_cmdline3(args, cmdline_opts).zero?
|
|
60
60
|
|
|
61
61
|
handler&.fuse_debug(cmdline_opts.debug) if handler.respond_to?(:fuse_debug)
|
|
62
62
|
|
|
@@ -87,10 +87,10 @@ module FFI
|
|
|
87
87
|
$stdout.puts "\n#{handler.fuse_help}" if handler.respond_to?(:fuse_help)
|
|
88
88
|
end
|
|
89
89
|
|
|
90
|
-
def finalize_fuse(fuse)
|
|
90
|
+
def finalize_fuse(fuse, mounted)
|
|
91
91
|
proc do
|
|
92
92
|
if fuse
|
|
93
|
-
Libfuse.fuse_unmount3(fuse)
|
|
93
|
+
Libfuse.fuse_unmount3(fuse) if mounted
|
|
94
94
|
Libfuse.fuse_destroy(fuse)
|
|
95
95
|
end
|
|
96
96
|
end
|
|
@@ -101,7 +101,7 @@ module FFI
|
|
|
101
101
|
|
|
102
102
|
# Have we requested an unmount (note not actually checking if OS sees the fs as mounted)
|
|
103
103
|
def mounted?
|
|
104
|
-
|
|
104
|
+
session && !fuse_exited? && @mounted
|
|
105
105
|
end
|
|
106
106
|
|
|
107
107
|
def initialize(mountpoint, args, operations, private_data)
|
|
@@ -119,24 +119,22 @@ module FFI
|
|
|
119
119
|
|
|
120
120
|
@mounted = @fuse && Libfuse.fuse_mount3(@fuse, @mountpoint).zero?
|
|
121
121
|
ensure
|
|
122
|
-
|
|
123
|
-
# as it's weakref will have been GC'd
|
|
124
|
-
ObjectSpace.define_finalizer(self, self.class.finalize_fuse(@fuse))
|
|
122
|
+
define_finalizer
|
|
125
123
|
end
|
|
126
124
|
|
|
127
|
-
def
|
|
128
|
-
|
|
129
|
-
|
|
125
|
+
def fuse_exited?
|
|
126
|
+
!Libfuse.fuse_session_exited(session).zero?
|
|
127
|
+
end
|
|
130
128
|
|
|
129
|
+
def fuse_process
|
|
130
|
+
se = session
|
|
131
|
+
buf = Thread.current[:fuse_buffer] ||= FuseBuf.new
|
|
131
132
|
res = Libfuse.fuse_session_receive_buf(se, buf)
|
|
132
133
|
|
|
133
|
-
|
|
134
|
-
return false if res.negative?
|
|
135
|
-
|
|
136
|
-
Libfuse.fuse_session_process_buf(se, buf) if res.positive?
|
|
134
|
+
return false unless res.positive?
|
|
137
135
|
|
|
138
|
-
|
|
139
|
-
|
|
136
|
+
Libfuse.fuse_session_process_buf(se, buf)
|
|
137
|
+
true
|
|
140
138
|
end
|
|
141
139
|
|
|
142
140
|
# [IO] /dev/fuse file descriptor for use with IO.select
|
|
@@ -152,7 +150,19 @@ module FFI
|
|
|
152
150
|
end
|
|
153
151
|
|
|
154
152
|
def unmount
|
|
155
|
-
|
|
153
|
+
return unless @mounted && @fuse && !@fuse.null?
|
|
154
|
+
|
|
155
|
+
Libfuse.fuse_unmount3(@fuse)
|
|
156
|
+
@mounted = false
|
|
157
|
+
ensure
|
|
158
|
+
define_finalizer
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
def define_finalizer
|
|
162
|
+
# if we unmount/destroy in the finalizer then the private_data object cannot be used in destroy
|
|
163
|
+
# as it's weakref will have been GC'd
|
|
164
|
+
ObjectSpace.undefine_finalizer(self)
|
|
165
|
+
ObjectSpace.define_finalizer(self, self.class.finalize_fuse(@fuse, @mounted))
|
|
156
166
|
end
|
|
157
167
|
end
|
|
158
168
|
|
|
@@ -11,11 +11,16 @@ module FFI
|
|
|
11
11
|
class FuseArgs < FFI::Struct
|
|
12
12
|
layout :argc, :int, :argv, :pointer, :allocated, :int
|
|
13
13
|
|
|
14
|
-
# Create
|
|
14
|
+
# Create a fuse_args struct from command line options
|
|
15
15
|
# @param [Array<String>] argv command line args
|
|
16
|
-
#
|
|
16
|
+
#
|
|
17
|
+
# first arg is expected to be program name and is ignored by fuse_opt_parse
|
|
18
|
+
# it is handled specially only in fuse_parse_cmdline (ie no subtype is given)
|
|
17
19
|
# @return [FuseArgs]
|
|
20
|
+
# @example
|
|
21
|
+
# FFI::Libfuse::FuseArgs.create($0,*ARGV)
|
|
18
22
|
def self.create(*argv)
|
|
23
|
+
argv.unshift('ffi-libfuse') if argv.empty? || argv[0].start_with?('-')
|
|
19
24
|
new.fill(*argv)
|
|
20
25
|
end
|
|
21
26
|
|
|
@@ -34,13 +39,13 @@ module FFI
|
|
|
34
39
|
end
|
|
35
40
|
|
|
36
41
|
# @!attribute [r] argc
|
|
37
|
-
#
|
|
42
|
+
# @return [Integer] count of args
|
|
38
43
|
def argc
|
|
39
44
|
self[:argc]
|
|
40
45
|
end
|
|
41
46
|
|
|
42
47
|
# @!attribute [r] argv
|
|
43
|
-
#
|
|
48
|
+
# @return [Array<String>] list of args
|
|
44
49
|
def argv
|
|
45
50
|
# noinspection RubyResolve
|
|
46
51
|
self[:argv].get_array_of_pointer(0, argc).map(&:read_string)
|
|
@@ -53,7 +58,7 @@ module FFI
|
|
|
53
58
|
|
|
54
59
|
# @!visibility private
|
|
55
60
|
def inspect
|
|
56
|
-
"#{self.class.name} - #{%i[argc argv allocated].
|
|
61
|
+
"#{self.class.name} - #{%i[argc argv allocated].to_h { |m| [m, send(m)] }}"
|
|
57
62
|
end
|
|
58
63
|
|
|
59
64
|
# Add an arg to this arg list
|
|
@@ -72,6 +77,8 @@ module FFI
|
|
|
72
77
|
#
|
|
73
78
|
# Option parsing function
|
|
74
79
|
#
|
|
80
|
+
# Wraps fuse_opt_parse() in ruby sugar and safety
|
|
81
|
+
#
|
|
75
82
|
# @param [Hash<String,Symbol>] opts option schema
|
|
76
83
|
#
|
|
77
84
|
# hash keys are a String template to match arguments against
|
|
@@ -80,7 +87,7 @@ module FFI
|
|
|
80
87
|
# beginning with "-o"
|
|
81
88
|
# 2. "foo", "foo-bar", etc. These match "-ofoo", "-ofoo-bar" or the relevant option in a comma separated option
|
|
82
89
|
# list
|
|
83
|
-
# 3. "bar=", "--foo=", etc. These are variations of 1) and 2) which have a parameter
|
|
90
|
+
# 3. "bar=", "--foo=", etc. These are variations of 1) and 2) which have a parameter value
|
|
84
91
|
# 4. '%' Formats Not Supported (or needed for Ruby!)
|
|
85
92
|
# 5. "-x ", etc. Matches either "-xparam" or "-x param" as two separate arguments
|
|
86
93
|
# 6. '%' Formats Not Supported
|
|
@@ -91,45 +98,49 @@ module FFI
|
|
|
91
98
|
# - :discard Argument is not passed to block, but behave as if the block returns :discard
|
|
92
99
|
# - any other value is yielded as 'key' property on matching argument
|
|
93
100
|
#
|
|
94
|
-
#
|
|
95
|
-
#
|
|
96
|
-
# @yieldparam [Object] data
|
|
97
|
-
# @yieldparam [String] arg is the whole argument or option including the parameter if exists.
|
|
98
|
-
#
|
|
99
|
-
# A two-argument option ("-x foo") is always converted to single argument option of the form "-xfoo" before this
|
|
100
|
-
# function is called.
|
|
101
|
-
#
|
|
102
|
-
# Options of the form '-ofoo' are yielded without the '-o' prefix.
|
|
101
|
+
# note that multiple templates can refer to the same key to support multiple option styles
|
|
103
102
|
#
|
|
103
|
+
# @param [Object] data an optional object that will be passed to the block
|
|
104
|
+
# @param [Array<Symbol>|nil] ignore
|
|
105
|
+
# the keys in this list will be kept without being passed to the block. pass nil to observe all keys
|
|
106
|
+
# @yield [key:, value:, match:, data:, out:]
|
|
107
|
+
# block is called for each remaining arg
|
|
104
108
|
# @yieldparam [Symbol] key determines why the processing function was called
|
|
105
109
|
#
|
|
106
110
|
# - :unmatched for arguments that *do not match* any supplied option
|
|
107
111
|
# - :non_option for non-option arguments (after -- or not beginning with -)
|
|
108
112
|
# - with appropriate value from opts hash for a matching argument
|
|
109
|
-
#
|
|
110
|
-
# @yieldparam [
|
|
111
|
-
#
|
|
113
|
+
# @yieldparam [String|Boolean] value the option value or true for option without a value
|
|
114
|
+
# @yieldparam [String] match the matching template specification (ie a key in opts)
|
|
115
|
+
# @yieldparam [Object] data
|
|
116
|
+
# @yieldparam [FuseArgs] out can {add} or {insert} additional args as required
|
|
112
117
|
# eg. if one arg implies another
|
|
113
118
|
#
|
|
114
119
|
# @yieldreturn [Symbol] the argument action
|
|
115
120
|
#
|
|
116
|
-
# - :error an error
|
|
117
|
-
# - :keep the current argument
|
|
118
|
-
# - :handled,:discard
|
|
119
|
-
#
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
121
|
+
# - :error an error, alternatively raise {Error}
|
|
122
|
+
# - :keep retain the current argument for further processing
|
|
123
|
+
# - :handled,:discard remove the current argument from further processing
|
|
124
|
+
# @return [nil|self] nil on error otherwise self
|
|
125
|
+
def parse!(opts, data = nil, ignore: %i[non_option unmatched], &block)
|
|
126
|
+
ignore ||= []
|
|
127
|
+
|
|
128
|
+
# first create an array of unique symbols such that positive indexes are custom options and negative indexes are
|
|
129
|
+
# special values (see fuse_opt.h), ie so we can turn the integer received in fuse_opt_proc back into a symbol
|
|
123
130
|
symbols = opts.values.uniq + %i[discard keep non_option unmatched]
|
|
124
131
|
|
|
132
|
+
# transform our symbol keys into integers suitable for FuseOpts
|
|
125
133
|
int_opts = opts.transform_values do |v|
|
|
126
134
|
%i[discard keep].include?(v) ? symbols.rindex(v) - symbols.size : symbols.index(v)
|
|
127
135
|
end
|
|
128
136
|
|
|
129
|
-
|
|
130
|
-
|
|
137
|
+
# keep track of opt templates by key so we extract parameter values from arg
|
|
138
|
+
param_opts, bool_opts = opts.keys.partition { |t| t =~ /(\s+|=)$/ }.map do |opt_list|
|
|
139
|
+
opt_list.group_by { |t| opts[t] }
|
|
140
|
+
end
|
|
131
141
|
|
|
132
|
-
|
|
142
|
+
fop = fuse_opt_proc(symbols, bool_opts, param_opts, ignore, &block)
|
|
143
|
+
Libfuse.fuse_opt_parse(self, data, int_opts, fop).zero? ? self : nil
|
|
133
144
|
end
|
|
134
145
|
|
|
135
146
|
private
|
|
@@ -137,15 +148,41 @@ module FFI
|
|
|
137
148
|
# Valid return values from parse! block
|
|
138
149
|
FUSE_OPT_PROC_RETURN = { error: -1, keep: 1, handled: 0, discard: 0 }.freeze
|
|
139
150
|
|
|
140
|
-
def fuse_opt_proc(
|
|
141
|
-
|
|
151
|
+
def fuse_opt_proc(symbols, bool_opts, param_opts, ignore, &block)
|
|
152
|
+
proc do |data, arg, key, out|
|
|
153
|
+
key = symbols[key]
|
|
154
|
+
next FUSE_OPT_PROC_RETURN.fetch(:keep) if ignore.include?(key)
|
|
155
|
+
|
|
156
|
+
match, value =
|
|
157
|
+
if %i[unmatched non_option].include?(key)
|
|
158
|
+
[nil, arg]
|
|
159
|
+
elsif bool_opts[key]&.include?(arg)
|
|
160
|
+
[arg, true]
|
|
161
|
+
elsif (opt = param_opts[key]&.detect { |t| arg.start_with?(t.rstrip) })
|
|
162
|
+
# Contrary to fuse_opt.h the separating space is not always stripped from these options
|
|
163
|
+
# https://github.com/libfuse/libfuse/issues/667
|
|
164
|
+
[opt, arg[opt.rstrip.length..].lstrip]
|
|
165
|
+
else
|
|
166
|
+
warn "FuseOptProc error - Cannot match option for #{arg}"
|
|
167
|
+
next FUSE_OPT_PROC_RETURN.fetch(:error)
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
safe_opt_proc(key: key, value: value, match: match, data: data, out: out, &block)
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
def safe_opt_proc(**args, &block)
|
|
175
|
+
res = block.call(**args)
|
|
142
176
|
res.is_a?(Integer) ? res : FUSE_OPT_PROC_RETURN.fetch(res)
|
|
143
177
|
rescue KeyError => e
|
|
144
178
|
warn "FuseOptProc error - Unknown result #{e.key}"
|
|
145
|
-
|
|
146
|
-
rescue
|
|
147
|
-
warn "
|
|
148
|
-
|
|
179
|
+
FUSE_OPT_PROC_RETURN.fetch(:error)
|
|
180
|
+
rescue Error => e
|
|
181
|
+
warn "#{e.message}: #{args.select { |k, _v| %i[key value].include?(k) }}\n#{argv}"
|
|
182
|
+
FUSE_OPT_PROC_RETURN.fetch(:error)
|
|
183
|
+
rescue StandardError, ScriptError => e
|
|
184
|
+
warn "FuseOptProc error - #{e.class.name}:#{e.message}\n\t#{e.backtrace.join("\n\t")}"
|
|
185
|
+
FUSE_OPT_PROC_RETURN.fetch(:error)
|
|
149
186
|
end
|
|
150
187
|
end
|
|
151
188
|
|
|
@@ -14,7 +14,6 @@ module FFI
|
|
|
14
14
|
# Generic data buffer for I/O, extended attributes, etc...Data may be supplied as a memory pointer or as a file
|
|
15
15
|
# descriptor
|
|
16
16
|
#
|
|
17
|
-
# @todo define helper methods to create buffers pointing to file_descriptors or allocated memory
|
|
18
17
|
class FuseBuf < FFI::Struct
|
|
19
18
|
layout(
|
|
20
19
|
size: :size_t, # Size of data in bytes
|
|
@@ -26,19 +25,60 @@ module FFI
|
|
|
26
25
|
|
|
27
26
|
# rubocop:disable Naming/MethodParameterName
|
|
28
27
|
|
|
29
|
-
#
|
|
30
|
-
#
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
28
|
+
# @!attribute [r] mem
|
|
29
|
+
# @return [FFI::Pointer] the memory in the buffer
|
|
30
|
+
def mem
|
|
31
|
+
self[:mem]
|
|
32
|
+
end
|
|
33
|
+
alias memory mem
|
|
34
|
+
|
|
35
|
+
# @!attribute [r] fd
|
|
36
|
+
# @return [Integer] the file descriptor number
|
|
37
|
+
def fd
|
|
38
|
+
self[:fd]
|
|
39
|
+
end
|
|
40
|
+
alias file_descriptor fd
|
|
41
|
+
|
|
42
|
+
# @return [Boolean] true if this a memory buffer
|
|
43
|
+
def mem?
|
|
44
|
+
!fd?
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# @return [Boolean] true if this is a file descriptor buffer
|
|
48
|
+
def fd?
|
|
49
|
+
self[:flags].include?(:is_fd)
|
|
50
|
+
end
|
|
51
|
+
alias file_descriptor? fd?
|
|
52
|
+
|
|
53
|
+
# Resize mem to smaller than initially allocated
|
|
54
|
+
# @param [Integer] new_size
|
|
55
|
+
# @return [void]
|
|
56
|
+
def resize(new_size)
|
|
57
|
+
self[:size] = new_size
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# @overload fill(size:, mem:, auto_release)
|
|
61
|
+
# Fill as a Memory buffer
|
|
62
|
+
# @param [Integer] size Size of data in bytes
|
|
63
|
+
# @param [FFI::Pointer] mem Memory pointer allocated to size if required
|
|
64
|
+
# @param [Boolean] autorelease
|
|
65
|
+
#
|
|
66
|
+
# @overload fill(fd:, fd_retry:, pos:)
|
|
67
|
+
# Fill as a FileDescriptor buffer
|
|
68
|
+
# @param [Integer] fd File descriptor
|
|
69
|
+
# @param [Boolean] fd_retry
|
|
70
|
+
# Retry operations on file descriptor
|
|
34
71
|
#
|
|
35
|
-
#
|
|
36
|
-
#
|
|
72
|
+
# If this flag is set then retry operation on file descriptor until .size bytes have been copied or an error
|
|
73
|
+
# or EOF is detected.
|
|
37
74
|
#
|
|
38
|
-
#
|
|
39
|
-
#
|
|
75
|
+
# @param [Integer] pos
|
|
76
|
+
# If > 0 then used to seek to the given offset before performing operation on file descriptor.
|
|
40
77
|
# @return [self]
|
|
41
|
-
def fill(
|
|
78
|
+
def fill(mem: FFI::Pointer::NULL, size: mem.null? ? 0 : mem.size, fd: -1, fd_retry: false, pos: 0)
|
|
79
|
+
mem = FFI::MemoryPointer.new(:char, size, true) if fd == -1 && mem.null? && size.positive?
|
|
80
|
+
mem.autorelease = to_ptr.autorelease? unless mem.null?
|
|
81
|
+
|
|
42
82
|
self[:size] = size
|
|
43
83
|
self[:mem] = mem
|
|
44
84
|
self[:fd] = fd
|
|
@@ -60,47 +100,86 @@ module FFI
|
|
|
60
100
|
#
|
|
61
101
|
# Allocate dynamically to add more than one buffer.
|
|
62
102
|
#
|
|
63
|
-
# @todo find a use for {FuseOperations#read_buf} and implement necessary helpers
|
|
64
103
|
class FuseBufVec < FFI::Struct
|
|
65
104
|
layout(
|
|
66
105
|
count: :size_t,
|
|
67
106
|
idx: :size_t,
|
|
68
107
|
off: :size_t,
|
|
69
|
-
|
|
108
|
+
# but is treated as a variable length array of FuseBuf at size +1 following the struct.
|
|
109
|
+
buf: [FuseBuf, 1] # struct fuse_buf[1]
|
|
70
110
|
)
|
|
111
|
+
|
|
71
112
|
# @!attribute [r] count
|
|
72
|
-
# @todo implement
|
|
73
113
|
# @return [Integer] the number of buffers in the array
|
|
114
|
+
def count
|
|
115
|
+
self[:count]
|
|
116
|
+
end
|
|
74
117
|
|
|
75
118
|
# @!attribute [r] index
|
|
76
|
-
# @todo implement
|
|
77
119
|
# @return [Integer] index of current buffer within the array
|
|
120
|
+
def index
|
|
121
|
+
self[:idx]
|
|
122
|
+
end
|
|
123
|
+
alias idx index
|
|
78
124
|
|
|
79
125
|
# @!attribute [r] offset
|
|
80
|
-
# @todo implement
|
|
81
126
|
# @return [Integer] current offset within the current buffer
|
|
127
|
+
def offset
|
|
128
|
+
self[:off]
|
|
129
|
+
end
|
|
130
|
+
alias off offset
|
|
82
131
|
|
|
83
132
|
# @!attribute [r] buffers
|
|
84
|
-
# @todo implement
|
|
85
133
|
# @return [Array<FuseBuf>] array of buffers
|
|
134
|
+
def buffers
|
|
135
|
+
@buffers ||= Array.new(count) do |i|
|
|
136
|
+
next self[:buf].first if i.zero?
|
|
137
|
+
|
|
138
|
+
FuseBuf.new(self[:buf].to_ptr + (i * FuseBuf.size))
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
# @!attribute [r] current
|
|
143
|
+
# @return [FuseBuf] the current buffer
|
|
144
|
+
def current
|
|
145
|
+
return self[:buf].first if index.zero?
|
|
146
|
+
|
|
147
|
+
FuseBuf.new(self[:buf].to_ptr + (index * FuseBuf.size))
|
|
148
|
+
end
|
|
86
149
|
|
|
87
|
-
#
|
|
88
|
-
|
|
89
|
-
|
|
150
|
+
# Create and initialise a new FuseBufVec
|
|
151
|
+
# @param [Boolean] autorelease should the struct be freed on GC (default NO!!!)
|
|
152
|
+
# @param [Hash] buf_options options for configuring the initial buffer. See {#init}
|
|
153
|
+
# @yield(buf,index)
|
|
154
|
+
# @yieldparam [FuseBuf] buf
|
|
155
|
+
# @yieldparam [Integer] index
|
|
156
|
+
# @yieldreturn [void]
|
|
157
|
+
# @return [FuseBufVec]
|
|
158
|
+
def self.init(autorelease: true, count: 1, **buf_options)
|
|
159
|
+
bufvec_ptr = FFI::MemoryPointer.new(:uchar, FuseBufVec.size + (FuseBuf.size * (count - 1)), true)
|
|
160
|
+
bufvec_ptr.autorelease = autorelease
|
|
161
|
+
bufvec = new(bufvec_ptr)
|
|
162
|
+
bufvec[:count] = count
|
|
163
|
+
bufvec.init(**buf_options)
|
|
164
|
+
|
|
165
|
+
buffers.each_with_index { |b, i| yield i, b } if block_given?
|
|
166
|
+
bufvec
|
|
90
167
|
end
|
|
91
168
|
|
|
92
|
-
#
|
|
169
|
+
# Set and initialise a specific buffer
|
|
93
170
|
#
|
|
94
171
|
# See fuse_common.h FUSE_BUFVEC_INIT macro
|
|
95
|
-
# @param [
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
172
|
+
# @param [Integer] index the index of the buffer
|
|
173
|
+
# @param [Hash<Symbol,Object>] buf_options see {FuseBuf#fill}
|
|
174
|
+
# @return [FuseBuf] the initial buffer
|
|
175
|
+
def init(index: 0, **buf_options)
|
|
176
|
+
self[:idx] = index
|
|
99
177
|
self[:off] = 0
|
|
100
|
-
|
|
178
|
+
current.fill(**buf_options) unless buf_options.empty?
|
|
101
179
|
self
|
|
102
180
|
end
|
|
103
181
|
|
|
182
|
+
# Would pref this to be called #size but clashes with FFI::Struct, might need StructWrapper
|
|
104
183
|
# @return [Integer] total size of data in a fuse buffer vector
|
|
105
184
|
def buf_size
|
|
106
185
|
Libfuse.fuse_buf_size(self)
|
|
@@ -141,6 +220,29 @@ module FFI
|
|
|
141
220
|
def copy_to(dst, *flags)
|
|
142
221
|
Libfuse.fuse_buf_copy(dst, self, flags)
|
|
143
222
|
end
|
|
223
|
+
|
|
224
|
+
# Copy our data direct to file descriptor
|
|
225
|
+
# @param [Integer] fileno a file descriptor
|
|
226
|
+
# @param [Integer] offset
|
|
227
|
+
# @param [Array<Symbol>] flags - see {copy_to}
|
|
228
|
+
# @return [Integer] number of bytes copied
|
|
229
|
+
def copy_to_fd(fileno, offset = 0, *flags)
|
|
230
|
+
dst = self.class.init(size: buf_size, fd: fileno, pos: offset)
|
|
231
|
+
copy_to(dst, *flags)
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
# Copy to string via a temporary buffer
|
|
235
|
+
# @param [Array<Symbol>] flags - see {copy_to}
|
|
236
|
+
# @return [String] the extracted data
|
|
237
|
+
def copy_to_str(*flags)
|
|
238
|
+
dst = FuseBufVec.init(size: buf_size)
|
|
239
|
+
copied = copy_to(dst, *flags)
|
|
240
|
+
dst.current.memory.read_string(copied)
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
def copy_from(src, *flags)
|
|
244
|
+
Libfuse.fuse_buf_copy(self, src, flags)
|
|
245
|
+
end
|
|
144
246
|
end
|
|
145
247
|
|
|
146
248
|
attach_function :fuse_buf_size, [FuseBufVec.by_ref], :size_t
|
|
@@ -31,7 +31,7 @@ module FFI
|
|
|
31
31
|
|
|
32
32
|
def initialize_callbacks(delegate:, wrappers: [])
|
|
33
33
|
wrappers = delegate.fuse_wrappers(*wrappers) if delegate.respond_to?(:fuse_wrappers)
|
|
34
|
-
super(
|
|
34
|
+
super(fuse_callbacks, delegate: delegate, wrappers: wrappers)
|
|
35
35
|
end
|
|
36
36
|
|
|
37
37
|
def respond_to_callback?(method, delegate)
|
|
@@ -39,10 +39,6 @@ module FFI
|
|
|
39
39
|
|
|
40
40
|
super
|
|
41
41
|
end
|
|
42
|
-
|
|
43
|
-
def callback_members
|
|
44
|
-
members - [:flags]
|
|
45
|
-
end
|
|
46
42
|
end
|
|
47
43
|
end
|
|
48
44
|
end
|