ffi-libfuse 0.3.4 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +22 -0
  3. data/README.md +1 -1
  4. data/lib/ffi/accessors.rb +21 -7
  5. data/lib/ffi/boolean_int.rb +1 -1
  6. data/lib/ffi/devt.rb +3 -3
  7. data/lib/ffi/libfuse/adapter/debug.rb +53 -15
  8. data/lib/ffi/libfuse/adapter/fuse2_compat.rb +38 -21
  9. data/lib/ffi/libfuse/adapter/fuse3_support.rb +0 -1
  10. data/lib/ffi/libfuse/adapter/ruby.rb +210 -159
  11. data/lib/ffi/libfuse/adapter/safe.rb +69 -21
  12. data/lib/ffi/libfuse/callbacks.rb +2 -1
  13. data/lib/ffi/libfuse/filesystem/accounting.rb +1 -1
  14. data/lib/ffi/libfuse/filesystem/mapped_files.rb +33 -7
  15. data/lib/ffi/libfuse/filesystem/pass_through_dir.rb +0 -1
  16. data/lib/ffi/libfuse/filesystem/virtual_dir.rb +293 -126
  17. data/lib/ffi/libfuse/filesystem/virtual_file.rb +85 -79
  18. data/lib/ffi/libfuse/filesystem/virtual_fs.rb +34 -15
  19. data/lib/ffi/libfuse/filesystem/virtual_link.rb +60 -0
  20. data/lib/ffi/libfuse/filesystem/virtual_node.rb +104 -87
  21. data/lib/ffi/libfuse/filesystem.rb +1 -1
  22. data/lib/ffi/libfuse/fuse2.rb +3 -2
  23. data/lib/ffi/libfuse/fuse3.rb +1 -1
  24. data/lib/ffi/libfuse/fuse_args.rb +5 -2
  25. data/lib/ffi/libfuse/fuse_buf.rb +112 -0
  26. data/lib/ffi/libfuse/fuse_buf_vec.rb +228 -0
  27. data/lib/ffi/libfuse/fuse_common.rb +10 -4
  28. data/lib/ffi/libfuse/fuse_config.rb +16 -7
  29. data/lib/ffi/libfuse/fuse_operations.rb +86 -41
  30. data/lib/ffi/libfuse/gem_helper.rb +2 -9
  31. data/lib/ffi/libfuse/io.rb +56 -0
  32. data/lib/ffi/libfuse/main.rb +27 -24
  33. data/lib/ffi/libfuse/test_helper.rb +68 -60
  34. data/lib/ffi/libfuse/version.rb +1 -1
  35. data/lib/ffi/libfuse.rb +1 -1
  36. data/lib/ffi/stat/native.rb +4 -4
  37. data/lib/ffi/stat.rb +19 -3
  38. data/lib/ffi/struct_array.rb +2 -1
  39. data/sample/hello_fs.rb +1 -1
  40. metadata +6 -3
  41. data/lib/ffi/libfuse/fuse_buffer.rb +0 -257
@@ -0,0 +1,112 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'fuse_version'
4
+ require_relative '../struct_wrapper'
5
+
6
+ module FFI
7
+ # Ruby FFI Binding for [libfuse](https://github.com/libfuse/libfuse)
8
+ module Libfuse
9
+ bitmask :fuse_buf_flags, [:is_fd, 1, :fd_seek, :fd_retry]
10
+
11
+ #
12
+ # Single io buffer
13
+ #
14
+ # Generic io buffer for I/O, extended attributes, etc...Data may be supplied as a memory pointer or as a file
15
+ # descriptor
16
+ #
17
+ class FuseBuf
18
+ # @!visibility private
19
+ # Native FuseBuf layout
20
+ class Native < FFI::Struct
21
+ layout(
22
+ size: :size_t, # Size of io in bytes
23
+ flags: FFI::Libfuse.find_type(:fuse_buf_flags), # Buffer flags
24
+ mem: :pointer, # Memory pointer - used if :is_fd flag is not set
25
+ fd: :int, # File descriptor - used if :is_fd is set
26
+ pos: :off_t # File position - used if :fd_seek flag is set.
27
+ )
28
+ end
29
+
30
+ include StructWrapper
31
+ native_struct(Native)
32
+
33
+ ffi_attr_reader(:mem, :fd, :pos)
34
+ ffi_attr_accessor(:size)
35
+
36
+ # @!attribute [r] mem
37
+ # @return [FFI::Pointer] the memory in the buffer
38
+
39
+ alias memory mem
40
+
41
+ # @!attribute [r] fd
42
+ # @return [Integer] the file descriptor number
43
+
44
+ alias file_descriptor fd
45
+
46
+ # @attribute [rw] size
47
+ # @return [Integer] the size of the buffer
48
+
49
+ # @return [Boolean] true if this a memory buffer
50
+ def mem?
51
+ !fd?
52
+ end
53
+
54
+ # @return [Boolean] true if this is a file descriptor buffer
55
+ def fd?
56
+ self[:flags].include?(:is_fd)
57
+ end
58
+ alias file_descriptor? fd?
59
+
60
+ # @overload fill(str:)
61
+ # Create a memory buffer from str
62
+ # @param [String,#to_s] str
63
+ #
64
+ # @overload fill(size:)
65
+ # Allocate an empty memory buffer of size bytes
66
+ # @param [Integer] size
67
+ #
68
+ # @overload fill(mem:, size: mem.size)
69
+ # Set the buffer to contain the previously allocated memory
70
+ # @param [FFI::Pointer] mem
71
+ # @param [Integer] size <= mem.size
72
+ #
73
+ # @overload fill(fd:, fd_retry: false, size:, pos: 0)
74
+ # Fill as a FileDescriptor buffer
75
+ # @param [Integer] fd File descriptor
76
+ # @param [Boolean] fd_retry
77
+ # Retry operations on file descriptor
78
+ #
79
+ # If this flag is set then retry operation on file descriptor until size bytes have been copied or an error
80
+ # or EOF is detected.
81
+ #
82
+ # @param [Integer] size
83
+ # number of bytes to read from fd
84
+ # @param [nil, Integer] pos
85
+ # If set then used to seek to the given offset before performing operation on file descriptor.
86
+ # @return [self]
87
+ def fill(
88
+ str: nil,
89
+ mem: str ? FFI::MemoryPointer.from_string(str.to_s) : FFI::Pointer::NULL, size: mem.null? ? 0 : mem.size,
90
+ fd: -1, fd_retry: false, pos: nil # rubocop:disable Naming/MethodParameterName
91
+
92
+ )
93
+
94
+ # Allocate size bytes if we've been given a null pointer
95
+ mem = FFI::MemoryPointer.new(:char, size, true) if fd == -1 && mem.null? && size.positive?
96
+
97
+ mem.autorelease = to_ptr.autorelease? unless mem.null?
98
+
99
+ self[:size] = size
100
+ self[:mem] = mem
101
+ self[:fd] = fd
102
+ flags = []
103
+ flags << :is_fd if fd != -1
104
+ flags << :fd_seek if pos
105
+ flags << :fd_retry if fd_retry
106
+ self[:flags] = flags
107
+ self[:pos] = pos || 0
108
+ self
109
+ end
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,228 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'fuse_version'
4
+ require_relative 'io'
5
+ require_relative 'fuse_buf'
6
+ require_relative '../accessors'
7
+
8
+ module FFI
9
+ # Ruby FFI Binding for [libfuse](https://github.com/libfuse/libfuse)
10
+ module Libfuse
11
+ #
12
+ # Data buffer vector
13
+ #
14
+ # An list of io buffers, each containing a memory pointer or a file descriptor.
15
+ #
16
+ class FuseBufVec < FFI::Struct
17
+ include Accessors
18
+
19
+ layout(
20
+ count: :size_t,
21
+ idx: :size_t,
22
+ off: :size_t,
23
+ # buf is treated as a variable length array of FuseBuf at size +1 following the struct.
24
+ buf: [FuseBuf::Native, 1] # struct fuse_buf[1]
25
+ )
26
+
27
+ ffi_attr_reader(:count, :idx, :off)
28
+
29
+ # @!attribute [r] count
30
+ # @return [Integer] the number of buffers in the array
31
+
32
+ # @!attribute [r] idx
33
+ # @return [Integer] index of current buffer within the array
34
+
35
+ alias index idx
36
+
37
+ # @!attribute [r] off
38
+ # @return [Integer] current offset within the current buffer
39
+
40
+ alias offset off
41
+
42
+ # Create and initialise from a ruby object that quacks like {::File}, {::IO}, or {::String}
43
+ #
44
+ # @param [Object] io
45
+ #
46
+ # * Integer file descriptor or File like object that returns one via :fileno
47
+ # * Otherwise something to pass to {IO.read}(io, size, offset) to create a memory based buffer
48
+ #
49
+ # @param [Integer] size
50
+ # @param [Integer] offset
51
+ # @return [FuseBufVec]
52
+ #
53
+ # @note The returned object's memory is not auto-released, and thus suitable for use with
54
+ # {FuseOperations#read_buf} where the buffers are cleared by libfuse library..
55
+ def self.create(io, size, offset = nil)
56
+ fd = io.respond_to?(:fileno) ? io.fileno : io
57
+ return init(autorelease: false, size: size, fd: fd, pos: offset || 0) if fd.is_a?(Integer)
58
+
59
+ init(autorelease: false, str: Libfuse::IO.read(io, size, offset))
60
+ end
61
+
62
+ # Create and initialise a new FuseBufVec
63
+ #
64
+ # @param [Boolean] autorelease should the struct be freed on GC
65
+ #
66
+ # Use false only if this object is going to be passed to the C library side. eg. {FuseOperations#read_buf}
67
+ # @param [Hash<Symbol>] buf_options options for configuring the initial buffer (see {FuseBuf#fill})
68
+ # @return [FuseBufVec]
69
+ def self.init(autorelease: true, count: 1, **buf_options)
70
+ bufvec_ptr = FFI::MemoryPointer.new(:uchar, FuseBufVec.size + (FuseBuf::Native.size * (count - 1)), true)
71
+ bufvec_ptr.autorelease = autorelease
72
+ bufvec = new(bufvec_ptr)
73
+ bufvec[:count] = count
74
+ bufvec[:idx] = 0
75
+ bufvec[:off] = 0
76
+ bufvec.buffers[0].fill(**buf_options) unless buf_options.empty?
77
+ bufvec
78
+ end
79
+
80
+ # @return [Integer] total size of io in a fuse buffer vector (ie the size of all the fuse buffers)
81
+ def buf_size
82
+ Libfuse.fuse_buf_size(self)
83
+ end
84
+
85
+ # Set {index}/{offset} for reading/writing from pos
86
+ # @param [Integer] pos
87
+ # @return [self]
88
+ # @raise [Errno::ERANGE] if seek past end of file
89
+ def seek(pos)
90
+ buffers.each_with_index do |b, i|
91
+ if pos < b.size
92
+ self[:idx] = i
93
+ self[:off] = pos
94
+ return self
95
+ else
96
+ pos -= b.size
97
+ end
98
+ end
99
+ raise Errno::ERANGE
100
+ end
101
+
102
+ # Copy data from this set of buffers to another set
103
+ #
104
+ # @param [FuseBufVec] dst destination buffers
105
+ # @param [Array<Symbol>] flags Buffer copy flags
106
+ #
107
+ # - :no_splice
108
+ # Don't use splice(2)
109
+ #
110
+ # Always fall back to using read and write instead of splice(2) to copy io from one file descriptor to
111
+ # another.
112
+ #
113
+ # If this flag is not set, then only fall back if splice is unavailable.
114
+ #
115
+ # - :force_splice
116
+ #
117
+ # Always use splice(2) to copy io from one file descriptor to another. If splice is not available, return
118
+ # -EINVAL.
119
+ #
120
+ # - :splice_move
121
+ #
122
+ # Try to move io with splice.
123
+ #
124
+ # If splice is used, try to move pages from the source to the destination instead of copying. See
125
+ # documentation of SPLICE_F_MOVE in splice(2) man page.
126
+ #
127
+ # - :splice_nonblock
128
+ #
129
+ # Don't block on the pipe when copying io with splice
130
+ #
131
+ # Makes the operations on the pipe non-blocking (if the pipe is full or empty). See SPLICE_F_NONBLOCK in
132
+ # the splice(2) man page.
133
+ #
134
+ # @return [Integer] actual number of bytes copied or -errno on error
135
+ #
136
+ def copy_to(dst, *flags)
137
+ Libfuse.fuse_buf_copy(dst, self, flags)
138
+ end
139
+
140
+ # Copy direct to file descriptor
141
+ # @param [Integer] fileno a file descriptor
142
+ # @param [nil, Integer] offset if non nil will first seek to offset
143
+ # @param [Array<Symbol>] flags see {copy_to}
144
+ # @return [Integer] number of bytes copied
145
+ def copy_to_fd(fileno, offset = nil, *flags)
146
+ dst = self.class.init(size: buf_size, fd: fileno, pos: offset)
147
+ copy_to(dst, *flags)
148
+ end
149
+
150
+ # Copy to string via a temporary buffer
151
+ # @param [Array<Symbol>] flags see {copy_to}
152
+ # @return [String] the extracted data
153
+ def copy_to_str(*flags)
154
+ dst = self.class.init(size: buf_size)
155
+ copied = copy_to(dst, *flags)
156
+ dst.buffers.first.memory.read_string(copied)
157
+ end
158
+
159
+ # Copy from another set of buffers to this one
160
+ # @param [FuseBufVec] src source buffers
161
+ # @param [Array<Symbol>] flags
162
+ # @return [Integer] number of bytes written
163
+ # @see copy_to
164
+ def copy_from(src, *flags)
165
+ Libfuse.fuse_buf_copy(self, src, flags)
166
+ end
167
+
168
+ # Store ourself into a pointer location as received by {FuseOperations#read_buf}
169
+ # @param [FFI::Pointer<FuseBufVec>] bufp
170
+ # @return [void]
171
+ def store_to(bufp)
172
+ bufp.write_pointer(to_ptr)
173
+ end
174
+
175
+ # Write data from these buffers to another object
176
+ #
177
+ # @overload copy_to_io(io, *flags)
178
+ # @overload copy_to_io(io, offset = nil, *flags)
179
+ # @param [Object] io one of
180
+ #
181
+ # * another {FuseBufVec} via io.{seek}(offset) and {copy_to}(io, *flags)
182
+ # * an {::Integer} file descriptor to write via {copy_to_fd}(io, offset, *flags)
183
+ # * a {::File} like object that returns a file descriptor via :fileno used as above
184
+ # * an {::IO} like object that accepts a string data as {IO.write}(io, {copy_to_str}(*flags), offset)
185
+ #
186
+ # @param [nil, Integer] offset position in io to begin writing at, or nil if io is already positioned
187
+ # @param [Array<Symbol>] flags see {copy_to}
188
+ #
189
+ # @return [Integer] number of bytes written
190
+ # @raise [Errno::EBADF] if io is not a valid target
191
+ def copy_to_io(io, offset = nil, *flags)
192
+ if offset.is_a?(Symbol)
193
+ flags.unshift(offset)
194
+ offset = nil
195
+ end
196
+
197
+ if io.is_a?(FuseBufVec)
198
+ io.seek(offset) if offset
199
+ return copy_to(io, *flags)
200
+ end
201
+
202
+ fd = (io.respond_to?(:fileno) ? io.fileno : io)
203
+ return copy_to_fd(fd, offset || 0, *flags) if fd.is_a?(Integer)
204
+
205
+ Libfuse::IO.write(io, copy_to_str(*flags), offset)
206
+ end
207
+
208
+ # @return [Array<FuseBuf>] list of buffers
209
+ def buffers
210
+ @buffers ||= Array.new(count) do |i|
211
+ native = i.zero? ? self[:buf].first : FuseBuf::Native.new(self[:buf].to_ptr + (i * FuseBuf::Native.size))
212
+ FuseBuf.new(native)
213
+ end.freeze
214
+ end
215
+ end
216
+
217
+ bitmask :fuse_buf_copy_flags, [:no_splice, 1, :force_splice, 2, :splice_move, 4, :splice_nonblock, 8]
218
+ attach_function :fuse_buf_copy, [FuseBufVec.by_ref, FuseBufVec.by_ref, :fuse_buf_copy_flags], :ssize_t
219
+ attach_function :fuse_buf_size, [FuseBufVec.by_ref], :size_t
220
+
221
+ class << self
222
+ # @!visibility private
223
+ # @!method fuse_buf_size
224
+ # @!method fuse_buf_copy
225
+ # @!method fuse_buf_size
226
+ end
227
+ end
228
+ end
@@ -52,11 +52,13 @@ module FFI
52
52
  teardown
53
53
  end
54
54
 
55
- # @api private
56
55
  # @param [Boolean] foreground
57
56
  # @param [Boolean] single_thread
58
- # @param [Hash<String,Proc>] traps see {Ackbar.trap}
59
- #
57
+ # @param [Hash<String,Proc|nil>] traps as per Signal.trap
58
+ # these are merged over {default_traps} for INT, HUP, TERM that unmount and exit filesystem. A nil
59
+ # value for these default signals will leave any existing signal handle in place.
60
+ # @param [Integer] remember fuse cache timeout
61
+ # @api private
60
62
  # Implement fuse loop in ruby
61
63
  #
62
64
  # Pros:
@@ -71,6 +73,7 @@ module FFI
71
73
  # * clone_fd is ignored
72
74
  # * filesystem interrupts probably can't work
73
75
  def run_ruby(foreground: true, single_thread: true, traps: {}, remember: false, **options)
76
+ traps = default_traps.merge(traps).keep_if { |_, v| v }
74
77
  Ackbar.trap(default_traps.merge(traps)) do |signals|
75
78
  daemonize unless foreground
76
79
 
@@ -112,7 +115,9 @@ module FFI
112
115
  end
113
116
 
114
117
  # Ruby implementation of fuse default traps
115
- # @see Ackbar
118
+ #
119
+ # * INT, HUP, TERM, TSTP to unmount and exit filesystem
120
+ # * PIPE is ignored
116
121
  def default_traps
117
122
  exproc = ->(signame) { exit(signame) }
118
123
  @default_traps ||= { INT: exproc, HUP: exproc, TERM: exproc, TSTP: exproc, PIPE: 'IGNORE' }
@@ -190,6 +195,7 @@ module FFI
190
195
  fuse_process || (sleep(0.1) && false)
191
196
  end
192
197
 
198
+ # @!visibility private
193
199
  def teardown
194
200
  return unless @fuse
195
201
 
@@ -10,10 +10,12 @@ module FFI
10
10
  # This structure is initialized from the arguments passed to fuse_new(), and then passed to the file system's init()
11
11
  # handler which should ensure that the configuration is compatible with the file system implementation.
12
12
  #
13
+ # Some options can only be set via the filesystem init method (use_ino etc..) because the filesystem either
14
+ # supports them or it doesn't.
13
15
  class FuseConfig < FFI::Struct
14
16
  include FFI::Accessors
15
17
 
16
- layout(
18
+ spec =
17
19
  {
18
20
  # @!attribute [r] gid
19
21
  # @return [Integer|nil] if set, this value will be used for the :gid attribute of each file
@@ -158,7 +160,7 @@ module FFI
158
160
 
159
161
  # @!attribute [rw] ac_attr_timeout
160
162
  # if set the timeout in seconds for which file attributes are cached for the purpose of checking if
161
- # auto_cache should flush the file data on open.
163
+ # auto_cache should flush the file data on open.
162
164
  # @return [Float|nil]
163
165
  ac_attr_timeout_set: :bool_int,
164
166
  ac_attr_timeout: :double,
@@ -179,18 +181,25 @@ module FFI
179
181
  modules: :pointer,
180
182
  debug: :bool_int
181
183
  }
182
- )
183
184
 
184
- setters = { gid: :set_gid, uid: :set_uid, umask: :set_mode, ac_attr_timeout: :ac_attr_timeout_set }
185
- setters.each do |(attr, setter)|
186
- ffi_attr_reader(attr)
185
+ layout(spec)
186
+
187
+ # Find the attrs that have a corresponding setter (prefix set_ or suffix _set
188
+ setters = spec.keys
189
+ .map { |k| [k, k.to_s.sub(/^set_/, '').sub(/_set$/, '').to_sym] }
190
+ .reject { |(s, a)| s == a }.to_h
191
+
192
+ setters.each do |(setter, attr)|
193
+ ffi_attr_reader(attr) { |val| self[setter] ? val : nil }
194
+
187
195
  ffi_attr_writer(attr) do |val|
188
196
  self[setter] = !val.nil?
189
197
  val || 0
190
198
  end
191
199
  end
192
200
 
193
- ffi_attr_accessor(*(members - (setters.keys + setters.values)))
201
+ remaining = (spec.keys - setters.keys - setters.values).map { |a| spec[a] == :bool_int ? "#{a}?" : a }
202
+ ffi_attr_accessor(*remaining)
194
203
  end
195
204
  end
196
205
  end
@@ -1,19 +1,18 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative 'fuse_version'
4
- require_relative '../ruby_object'
5
4
  require_relative 'fuse_conn_info'
6
- require_relative 'fuse_buffer'
5
+ require_relative 'fuse_config'
6
+ require_relative 'fuse_buf_vec'
7
7
  require_relative 'fuse_context'
8
8
  require_relative 'fuse_file_info'
9
9
  require_relative 'fuse_poll_handle'
10
+ require_relative 'fuse_callbacks'
11
+ require_relative '../ruby_object'
10
12
  require_relative '../stat_vfs'
11
13
  require_relative '../flock'
12
- require_relative 'thread_pool'
13
14
  require_relative '../stat'
14
- require_relative '../struct_array'
15
15
  require_relative '../encoding'
16
- require_relative 'fuse_callbacks'
17
16
 
18
17
  module FFI
19
18
  # Ruby FFI Binding for [libfuse](https://github.com/libfuse/libfuse)
@@ -44,18 +43,80 @@ module FFI
44
43
  # All Callback methods are optional, but some are essential for a useful filesystem
45
44
  # e.g. {getattr},{readdir}
46
45
  #
47
- # Almost all callback operations take a path which can be of any length and will return 0 for success, or raise a
48
- # {::SystemCallError} on failure
46
+ # Almost all callback operations take a path which can be of any length and will return 0 for success or a negative
47
+ # `Errno` value on failure.
49
48
  #
50
49
  class FuseOperations < FFI::Struct
51
50
  include FuseCallbacks
52
51
 
52
+ # Callbacks that have no return value
53
+ VOID_RETURN = %i[init destroy].freeze
54
+
53
55
  # Callbacks that are expected to return meaningful positive integers
54
56
  MEANINGFUL_RETURN = %i[read write write_buf lseek copy_file_range getxattr listxattr].freeze
55
57
 
56
- # @return [Boolean] true if fuse_callback expects a meaningful integer return
57
- def self.meaningful_return?(fuse_callback)
58
- MEANINGFUL_RETURN.include?(fuse_callback)
58
+ # @!visibility private
59
+ # Methods to handle the path argument for most {path_callbacks}
60
+ NODE_PATH_METHODS = %i[first shift unshift].freeze
61
+
62
+ # @!visibility private
63
+ # Methods to handle the path argument for {link}, {symlink} and {rename}
64
+ LINK_PATH_METHODS = %i[last pop push].freeze
65
+
66
+ # @!visibility private
67
+ CALLBACK_PATH_ARG_METHODS = Hash.new(NODE_PATH_METHODS).merge(
68
+ {
69
+ link: LINK_PATH_METHODS,
70
+ symlink: LINK_PATH_METHODS,
71
+ rename: LINK_PATH_METHODS
72
+ }
73
+ ).freeze
74
+
75
+ class << self
76
+ # @return [Boolean] true if fuse_callback expects a meaningful integer return
77
+ def meaningful_return?(fuse_callback)
78
+ MEANINGFUL_RETURN.include?(fuse_callback)
79
+ end
80
+
81
+ # @return [Boolean] true if fuse_callback expects a void return
82
+ def void_return?(fuse_callback)
83
+ VOID_RETURN.include?(fuse_callback)
84
+ end
85
+
86
+ # Helper to determine how to handle the path argument for a path callback
87
+ # @param [Symbol] fuse_callback callback method name (must be one of #{path_callbacks})
88
+ # @return [Symbol, Symbol,Symbol] read, remove, add methods.
89
+ # [:last, :push, :pop] for :link, :symlink, :rename,
90
+ # [:first, :shift, :unshift] for everything else
91
+ # @example
92
+ # def wrap_callback(fuse_method, *args)
93
+ # read, remove, add = FFI::Libfuse::FuseOperations.path_arg_methods(fuse_method)
94
+ # path = args.send(read)
95
+ # # ... do something with path
96
+ #
97
+ # path = args.send(remove)
98
+ # # ... do something to make an alternate path
99
+ # args.send(add, adjusted_path)
100
+ # delegate.send(fuse_methoed, *args)
101
+ # end
102
+ def path_arg_methods(fuse_callback)
103
+ CALLBACK_PATH_ARG_METHODS[fuse_callback]
104
+ end
105
+
106
+ # @return [Set<Symbol>] list of callback methods
107
+ def fuse_callbacks
108
+ @fuse_callbacks ||= Set.new(members - [:flags])
109
+ end
110
+
111
+ # @return [Set<Symbol>] list of path callback methods
112
+ def path_callbacks
113
+ @path_callbacks ||= fuse_callbacks - VOID_RETURN
114
+ end
115
+ end
116
+
117
+ # @!visibility private
118
+ def fuse_callbacks
119
+ self.class.fuse_callbacks
59
120
  end
60
121
 
61
122
  # Container to dynamically build up the operations layout which is dependent on the loaded libfuse version
@@ -151,12 +212,11 @@ module FFI
151
212
  # int (*rmdir) (const char *);
152
213
  op[:rmdir] = []
153
214
 
154
- # @!method symlink(path,target)
215
+ # @!method symlink(target, path)
155
216
  # @abstract
156
217
  # Create a symbolic link
218
+ # @param [String] target
157
219
  # @param [String] path
158
- # @param [String] target the link target
159
- #
160
220
  # @return [Integer] 0 for success or -ve errno
161
221
 
162
222
  # int (*symlink) (const char *, const char *);
@@ -173,13 +233,13 @@ module FFI
173
233
  # int (*rename) (const char *, const char *);
174
234
  op[:rename] = [:fs_string]
175
235
 
176
- # @!method link(path,target)
236
+ # @!method link(target, path)
177
237
  # @abstract
178
238
  # Create a hard link to a file
179
- # @param [String] path
180
239
  # @param [String] target
181
- #
240
+ # @param [String] path
182
241
  # @return [Integer] 0 for success or -ve errno
242
+ # @see rename(2)
183
243
 
184
244
  # int (*link) (const char *, const char *);
185
245
  op[:link] = [:fs_string]
@@ -486,7 +546,6 @@ module FFI
486
546
  # fuse3: void *(*init) (struct fuse_conn_info *conn, struct fuse_config *cfg);
487
547
  op[:init] =
488
548
  if FUSE_MAJOR_VERSION >= 3
489
- require_relative 'fuse_config'
490
549
  callback([FuseConnInfo.by_ref, FuseConfig.by_ref], RubyObject)
491
550
  else
492
551
  callback([FuseConnInfo.by_ref], RubyObject)
@@ -618,7 +677,7 @@ module FFI
618
677
  #
619
678
 
620
679
  # int (*utimens) (const char *, const struct timespec tv[2]);
621
- op[:utimens] = [FFI::Stat::TimeSpec.array(2)]
680
+ op[:utimens] = [FFI::Stat::TimeSpec[2]]
622
681
  op[:utimens] << FuseFileInfo.by_ref if FUSE_MAJOR_VERSION >= 3
623
682
 
624
683
  # @!method bmap(path,blocksize,index)
@@ -655,17 +714,19 @@ module FFI
655
714
  # release, fsync, readdir, releasedir, fsyncdir, ftruncate, fgetattr, lock, ioctl and poll
656
715
  #
657
716
  # Closely related to flag_nullpath_ok, but if this flag is set then the path will not be calculaged even if
658
- # the file wasnt unlinked. However the path can still be non-NULL if it needs to be calculated for some other
659
- # reason.
717
+ # the file wasn't unlinked. However the path can still be non-NULL if it needs to be calculated for some
718
+ # other reason.
660
719
  #
661
720
  # - :utime_omit_ok
662
721
  #
663
722
  # Flag indicating that the filesystem accepts special UTIME_NOW and UTIME_OMIT values in its utimens
664
723
  # operation.
665
724
  #
666
- # @return [Array[]Symbol>] a list of flags to set capabilities
725
+ # @return [Array<Symbol>] a list of flags to set capabilities
667
726
  # @note Not available in Fuse3
668
727
  # @deprecated in Fuse3 use fuse_config object in {init}
728
+
729
+ # flags
669
730
  op[:flags] = :flags_mask if FUSE_MAJOR_VERSION < 3
670
731
 
671
732
  if FUSE_VERSION >= 28
@@ -717,7 +778,7 @@ module FFI
717
778
  # @abstract
718
779
  # Write contents of buffer to an open file
719
780
  #
720
- # Similar to the write() method, but data is supplied in a generic buffer.
781
+ # Similar to the {write} method, but data is supplied in a generic buffer.
721
782
  # Use {FuseBufVec#copy_to_fd} to copy data to an open file descriptor, or {FuseBufVec#copy_to_str} to extract
722
783
  # string data from the buffer
723
784
  #
@@ -734,16 +795,15 @@ module FFI
734
795
  # @!method read_buf(path,bufp,size,offset,fuse_file_info)
735
796
  # @abstract
736
797
  #
737
- # Similar to the read() method, but data is stored and returned in a generic buffer.
798
+ # Similar to the {read} method, but data is stored and returned in a generic buffer.
738
799
  #
739
800
  # No actual copying of data has to take place, the source file descriptor may simply be stored in the buffer
740
801
  # for later data transfer.
741
802
  #
742
803
  # @param [String] path
743
804
  # @param [FFI::Pointer<FuseBufVec>] bufp
744
- # The buffer must be allocated dynamically and stored at the location pointed to by bufp. If the buffer
745
- # contains memory regions, they too must be allocated using malloc(). The allocated memory will be freed by
746
- # the caller.
805
+ # The buffer must be allocated dynamically ({FuseBufVec.init})
806
+ # and stored at the location pointed to by bufp (see {FuseBufVec#store_to}(bufp)).
747
807
  # @param [Integer] size
748
808
  # @param [Integer] offset
749
809
  # @param [FuseFileInfo] fuse_file_info
@@ -886,21 +946,6 @@ module FFI
886
946
  fuse_flags.concat(delegate.fuse_flags) if delegate.respond_to?(:fuse_flags)
887
947
  send(:[]=, :flags, fuse_flags.uniq)
888
948
  end
889
-
890
- # @!visibility private
891
- def fuse_callbacks
892
- self.class.fuse_callbacks
893
- end
894
-
895
- # @return [Set<Symbol>] list of callback methods
896
- def self.fuse_callbacks
897
- @fuse_callbacks ||= Set.new(members - [:flags])
898
- end
899
-
900
- # @return [Set<Symbol>] list of path callback methods
901
- def self.path_callbacks
902
- @path_callbacks ||= fuse_callbacks - %i[init destroy]
903
- end
904
949
  end
905
950
  end
906
951
  end