ffi-libfuse 0.3.4 → 0.4.0

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.
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