ffi-libfuse 0.0.1.pre → 0.1.0.rc20220550
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/CHANGES.md +14 -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/gnu_extensions.rb +1 -1
- data/lib/ffi/libfuse/ackbar.rb +6 -8
- 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 +0 -1
- data/lib/ffi/libfuse/adapter/ruby.rb +499 -148
- data/lib/ffi/libfuse/adapter/safe.rb +1 -1
- 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 +188 -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 +21 -21
- data/lib/ffi/libfuse/fuse3.rb +12 -12
- data/lib/ffi/libfuse/fuse_args.rb +69 -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 +55 -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 +43 -19
- data/lib/ffi/libfuse/fuse_version.rb +10 -6
- data/lib/ffi/libfuse/main.rb +80 -37
- data/lib/ffi/libfuse/thread_pool.rb +1 -1
- data/lib/ffi/libfuse/version.rb +1 -1
- data/lib/ffi/libfuse.rb +13 -4
- data/lib/ffi/ruby_object.rb +1 -1
- data/lib/ffi/stat/constants.rb +9 -0
- data/lib/ffi/stat/native.rb +36 -6
- data/lib/ffi/stat/time_spec.rb +28 -12
- 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 +66 -7
- data/lib/ffi/libfuse/adapter/thread_local_context.rb +0 -36
@@ -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,8 +107,8 @@ 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)
|
@@ -132,12 +126,16 @@ module FFI
|
|
132
126
|
@io ||= ::IO.for_fd(Libfuse.fuse_chan_fd(@ch), 'r', autoclose: false)
|
133
127
|
end
|
134
128
|
|
135
|
-
def
|
129
|
+
def fuse_exited?
|
130
|
+
!Libfuse.fuse_exited2(@fuse).zero?
|
131
|
+
end
|
132
|
+
|
133
|
+
def fuse_process
|
136
134
|
cmd = Libfuse.fuse_read_cmd(@fuse)
|
137
|
-
|
135
|
+
return false if cmd.null?
|
138
136
|
|
139
|
-
|
140
|
-
|
137
|
+
Libfuse.fuse_process_cmd(@fuse, cmd)
|
138
|
+
true
|
141
139
|
end
|
142
140
|
|
143
141
|
private
|
@@ -147,9 +145,11 @@ module FFI
|
|
147
145
|
end
|
148
146
|
|
149
147
|
def unmount
|
148
|
+
return unless @ch
|
149
|
+
|
150
150
|
c = @ch
|
151
151
|
@ch = nil
|
152
|
-
Libfuse.fuse_unmount2(mountpoint, c)
|
152
|
+
Libfuse.fuse_unmount2(mountpoint, c)
|
153
153
|
end
|
154
154
|
end
|
155
155
|
|
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
|
|
@@ -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?
|
105
105
|
end
|
106
106
|
|
107
107
|
def initialize(mountpoint, args, operations, private_data)
|
@@ -124,19 +124,19 @@ module FFI
|
|
124
124
|
ObjectSpace.define_finalizer(self, self.class.finalize_fuse(@fuse))
|
125
125
|
end
|
126
126
|
|
127
|
-
def
|
128
|
-
|
129
|
-
|
127
|
+
def fuse_exited?
|
128
|
+
!Libfuse.fuse_session_exited(session).zero?
|
129
|
+
end
|
130
130
|
|
131
|
+
def fuse_process
|
132
|
+
se = session
|
133
|
+
buf = Thread.current[:fuse_buffer] ||= FuseBuf.new
|
131
134
|
res = Libfuse.fuse_session_receive_buf(se, buf)
|
132
135
|
|
133
|
-
|
134
|
-
return false if res.negative?
|
135
|
-
|
136
|
-
Libfuse.fuse_session_process_buf(se, buf) if res.positive?
|
136
|
+
return false unless res.positive?
|
137
137
|
|
138
|
-
|
139
|
-
|
138
|
+
Libfuse.fuse_session_process_buf(se, buf)
|
139
|
+
true
|
140
140
|
end
|
141
141
|
|
142
142
|
# [IO] /dev/fuse file descriptor for use with IO.select
|
@@ -11,10 +11,13 @@ 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
|
17
18
|
# @return [FuseArgs]
|
19
|
+
# @example
|
20
|
+
# FFI::Libfuse::FuseArgs.create($0,*ARGV)
|
18
21
|
def self.create(*argv)
|
19
22
|
new.fill(*argv)
|
20
23
|
end
|
@@ -34,13 +37,13 @@ module FFI
|
|
34
37
|
end
|
35
38
|
|
36
39
|
# @!attribute [r] argc
|
37
|
-
#
|
40
|
+
# @return [Integer] count of args
|
38
41
|
def argc
|
39
42
|
self[:argc]
|
40
43
|
end
|
41
44
|
|
42
45
|
# @!attribute [r] argv
|
43
|
-
#
|
46
|
+
# @return [Array<String>] list of args
|
44
47
|
def argv
|
45
48
|
# noinspection RubyResolve
|
46
49
|
self[:argv].get_array_of_pointer(0, argc).map(&:read_string)
|
@@ -53,7 +56,7 @@ module FFI
|
|
53
56
|
|
54
57
|
# @!visibility private
|
55
58
|
def inspect
|
56
|
-
"#{self.class.name} - #{%i[argc argv allocated].
|
59
|
+
"#{self.class.name} - #{%i[argc argv allocated].to_h { |m| [m, send(m)] }}"
|
57
60
|
end
|
58
61
|
|
59
62
|
# Add an arg to this arg list
|
@@ -72,6 +75,8 @@ module FFI
|
|
72
75
|
#
|
73
76
|
# Option parsing function
|
74
77
|
#
|
78
|
+
# Wraps fuse_opt_parse() in ruby sugar and safety
|
79
|
+
#
|
75
80
|
# @param [Hash<String,Symbol>] opts option schema
|
76
81
|
#
|
77
82
|
# hash keys are a String template to match arguments against
|
@@ -80,7 +85,7 @@ module FFI
|
|
80
85
|
# beginning with "-o"
|
81
86
|
# 2. "foo", "foo-bar", etc. These match "-ofoo", "-ofoo-bar" or the relevant option in a comma separated option
|
82
87
|
# list
|
83
|
-
# 3. "bar=", "--foo=", etc. These are variations of 1) and 2) which have a parameter
|
88
|
+
# 3. "bar=", "--foo=", etc. These are variations of 1) and 2) which have a parameter value
|
84
89
|
# 4. '%' Formats Not Supported (or needed for Ruby!)
|
85
90
|
# 5. "-x ", etc. Matches either "-xparam" or "-x param" as two separate arguments
|
86
91
|
# 6. '%' Formats Not Supported
|
@@ -91,45 +96,49 @@ module FFI
|
|
91
96
|
# - :discard Argument is not passed to block, but behave as if the block returns :discard
|
92
97
|
# - any other value is yielded as 'key' property on matching argument
|
93
98
|
#
|
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.
|
99
|
+
# note that multiple templates can refer to the same key to support multiple option styles
|
103
100
|
#
|
101
|
+
# @param [Object] data an optional object that will be passed to the block
|
102
|
+
# @param [Array<Symbol>|nil] ignore
|
103
|
+
# the keys in this list will be kept without being passed to the block. pass nil to observe all keys
|
104
|
+
# @yield [key:, value:, match:, data:, out:]
|
105
|
+
# block is called for each remaining arg
|
104
106
|
# @yieldparam [Symbol] key determines why the processing function was called
|
105
107
|
#
|
106
108
|
# - :unmatched for arguments that *do not match* any supplied option
|
107
109
|
# - :non_option for non-option arguments (after -- or not beginning with -)
|
108
110
|
# - with appropriate value from opts hash for a matching argument
|
109
|
-
#
|
110
|
-
# @yieldparam [
|
111
|
-
#
|
111
|
+
# @yieldparam [String|Boolean] value the option value or true for option without a value
|
112
|
+
# @yieldparam [String] match the matching template specification (ie a key in opts)
|
113
|
+
# @yieldparam [Object] data
|
114
|
+
# @yieldparam [FuseArgs] out can {add} or {insert} additional args as required
|
112
115
|
# eg. if one arg implies another
|
113
116
|
#
|
114
117
|
# @yieldreturn [Symbol] the argument action
|
115
118
|
#
|
116
|
-
# - :error an error
|
117
|
-
# - :keep the current argument
|
118
|
-
# - :handled,:discard
|
119
|
-
#
|
120
|
-
|
121
|
-
|
122
|
-
|
119
|
+
# - :error an error, alternatively raise {Error}
|
120
|
+
# - :keep retain the current argument for further processing
|
121
|
+
# - :handled,:discard remove the current argument from further processing
|
122
|
+
# @return [nil|self] nil on error otherwise self
|
123
|
+
def parse!(opts, data = nil, ignore: %i[non_option unmatched], &block)
|
124
|
+
ignore ||= []
|
125
|
+
|
126
|
+
# first create an array of unique symbols such that positive indexes are custom options and negative indexes are
|
127
|
+
# special values (see fuse_opt.h), ie so we can turn the integer received in fuse_opt_proc back into a symbol
|
123
128
|
symbols = opts.values.uniq + %i[discard keep non_option unmatched]
|
124
129
|
|
130
|
+
# transform our symbol keys into integers suitable for FuseOpts
|
125
131
|
int_opts = opts.transform_values do |v|
|
126
132
|
%i[discard keep].include?(v) ? symbols.rindex(v) - symbols.size : symbols.index(v)
|
127
133
|
end
|
128
134
|
|
129
|
-
|
130
|
-
|
135
|
+
# keep track of opt templates by key so we extract parameter values from arg
|
136
|
+
param_opts, bool_opts = opts.keys.partition { |t| t =~ /(\s+|=)$/ }.map do |opt_list|
|
137
|
+
opt_list.group_by { |t| opts[t] }
|
138
|
+
end
|
131
139
|
|
132
|
-
|
140
|
+
fop = fuse_opt_proc(symbols, bool_opts, param_opts, ignore, &block)
|
141
|
+
Libfuse.fuse_opt_parse(self, data, int_opts, fop).zero? ? self : nil
|
133
142
|
end
|
134
143
|
|
135
144
|
private
|
@@ -137,15 +146,41 @@ module FFI
|
|
137
146
|
# Valid return values from parse! block
|
138
147
|
FUSE_OPT_PROC_RETURN = { error: -1, keep: 1, handled: 0, discard: 0 }.freeze
|
139
148
|
|
140
|
-
def fuse_opt_proc(
|
141
|
-
|
149
|
+
def fuse_opt_proc(symbols, bool_opts, param_opts, ignore, &block)
|
150
|
+
proc do |data, arg, key, out|
|
151
|
+
key = symbols[key]
|
152
|
+
next FUSE_OPT_PROC_RETURN.fetch(:keep) if ignore.include?(key)
|
153
|
+
|
154
|
+
match, value =
|
155
|
+
if %i[unmatched non_option].include?(key)
|
156
|
+
[nil, arg]
|
157
|
+
elsif bool_opts[key]&.include?(arg)
|
158
|
+
[arg, true]
|
159
|
+
elsif (opt = param_opts[key]&.detect { |t| arg.start_with?(t.rstrip) })
|
160
|
+
# Contrary to fuse_opt.h the separating space is not always stripped from these options
|
161
|
+
# https://github.com/libfuse/libfuse/issues/667
|
162
|
+
[opt, arg[opt.rstrip.length..].lstrip]
|
163
|
+
else
|
164
|
+
warn "FuseOptProc error - Cannot match option for #{arg}"
|
165
|
+
next -1
|
166
|
+
end
|
167
|
+
|
168
|
+
safe_opt_proc(key: key, value: value, match: match, data: data, out: out, &block)
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
def safe_opt_proc(**args, &block)
|
173
|
+
res = block.call(**args)
|
142
174
|
res.is_a?(Integer) ? res : FUSE_OPT_PROC_RETURN.fetch(res)
|
143
175
|
rescue KeyError => e
|
144
176
|
warn "FuseOptProc error - Unknown result #{e.key}"
|
145
|
-
|
146
|
-
rescue
|
147
|
-
warn "
|
148
|
-
|
177
|
+
FUSE_OPT_PROC_RETURN.fetch(:error)
|
178
|
+
rescue Error => e
|
179
|
+
warn "#{e.message}: #{args.select { |k, _v| %i[key value].include?(k) }}\n#{argv}"
|
180
|
+
FUSE_OPT_PROC_RETURN.fetch(:error)
|
181
|
+
rescue StandardError, ScriptError => e
|
182
|
+
warn "FuseOptProc error - #{e.class.name}:#{e.message}\n\t#{e.backtrace.join("\n\t")}"
|
183
|
+
FUSE_OPT_PROC_RETURN.fetch(:error)
|
149
184
|
end
|
150
185
|
end
|
151
186
|
|
@@ -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
|