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