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.
Files changed (57) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +3 -1
  3. data/CHANGES.md +14 -0
  4. data/LICENSE +21 -0
  5. data/README.md +127 -44
  6. data/lib/ffi/accessors.rb +6 -6
  7. data/lib/ffi/boolean_int.rb +27 -0
  8. data/lib/ffi/devt.rb +23 -0
  9. data/lib/ffi/encoding.rb +38 -0
  10. data/lib/ffi/gnu_extensions.rb +1 -1
  11. data/lib/ffi/libfuse/ackbar.rb +6 -8
  12. data/lib/ffi/libfuse/adapter/context.rb +12 -10
  13. data/lib/ffi/libfuse/adapter/fuse2_compat.rb +52 -51
  14. data/lib/ffi/libfuse/adapter/fuse3_support.rb +0 -1
  15. data/lib/ffi/libfuse/adapter/ruby.rb +499 -148
  16. data/lib/ffi/libfuse/adapter/safe.rb +1 -1
  17. data/lib/ffi/libfuse/adapter.rb +1 -2
  18. data/lib/ffi/libfuse/callbacks.rb +1 -1
  19. data/lib/ffi/libfuse/filesystem/accounting.rb +116 -0
  20. data/lib/ffi/libfuse/filesystem/mapped_dir.rb +74 -0
  21. data/lib/ffi/libfuse/filesystem/mapped_files.rb +141 -0
  22. data/lib/ffi/libfuse/filesystem/pass_through_dir.rb +55 -0
  23. data/lib/ffi/libfuse/filesystem/pass_through_file.rb +45 -0
  24. data/lib/ffi/libfuse/filesystem/utils.rb +102 -0
  25. data/lib/ffi/libfuse/filesystem/virtual_dir.rb +306 -0
  26. data/lib/ffi/libfuse/filesystem/virtual_file.rb +94 -0
  27. data/lib/ffi/libfuse/filesystem/virtual_fs.rb +188 -0
  28. data/lib/ffi/libfuse/filesystem/virtual_node.rb +101 -0
  29. data/lib/ffi/libfuse/filesystem.rb +25 -0
  30. data/lib/ffi/libfuse/fuse2.rb +21 -21
  31. data/lib/ffi/libfuse/fuse3.rb +12 -12
  32. data/lib/ffi/libfuse/fuse_args.rb +69 -34
  33. data/lib/ffi/libfuse/fuse_buffer.rb +128 -26
  34. data/lib/ffi/libfuse/fuse_callbacks.rb +1 -5
  35. data/lib/ffi/libfuse/fuse_common.rb +55 -61
  36. data/lib/ffi/libfuse/fuse_config.rb +134 -143
  37. data/lib/ffi/libfuse/fuse_conn_info.rb +310 -134
  38. data/lib/ffi/libfuse/fuse_context.rb +45 -3
  39. data/lib/ffi/libfuse/fuse_operations.rb +43 -19
  40. data/lib/ffi/libfuse/fuse_version.rb +10 -6
  41. data/lib/ffi/libfuse/main.rb +80 -37
  42. data/lib/ffi/libfuse/thread_pool.rb +1 -1
  43. data/lib/ffi/libfuse/version.rb +1 -1
  44. data/lib/ffi/libfuse.rb +13 -4
  45. data/lib/ffi/ruby_object.rb +1 -1
  46. data/lib/ffi/stat/constants.rb +9 -0
  47. data/lib/ffi/stat/native.rb +36 -6
  48. data/lib/ffi/stat/time_spec.rb +28 -12
  49. data/lib/ffi/stat.rb +111 -22
  50. data/lib/ffi/stat_vfs.rb +59 -1
  51. data/lib/ffi/struct_wrapper.rb +22 -1
  52. data/sample/hello_fs.rb +54 -0
  53. data/sample/memory_fs.rb +5 -181
  54. data/sample/no_fs.rb +20 -21
  55. data/sample/pass_through_fs.rb +30 -0
  56. metadata +66 -7
  57. data/lib/ffi/libfuse/adapter/thread_local_context.rb +0 -36
@@ -13,7 +13,7 @@ module FFI
13
13
  attach_function :fuse_get_session, [:fuse], :session
14
14
  attach_function :fuse_set_signal_handlers, [:session], :int
15
15
  attach_function :fuse_remove_signal_handlers, [:session], :void
16
- attach_function :fuse_loop, [:fuse], :int, blocking: false
16
+ attach_function :fuse_loop, [:fuse], :int
17
17
  attach_function :fuse_clean_cache, [:fuse], :int
18
18
  attach_function :fuse_exit, [:fuse], :void
19
19
  attach_function :fuse_destroy, [:fuse], :void
@@ -42,16 +42,11 @@ module FFI
42
42
  def run(native: false, **options)
43
43
  return false unless mounted?
44
44
 
45
- if native
46
- run_native(**options)
47
- else
48
- run_ruby(**options)
49
- end
45
+ native ? run_native(**options) : run_ruby(**options)
50
46
  rescue Errno => e
51
47
  -e.errno
52
- rescue StandardError => e
53
- warn e
54
- warn e.backtrace.join("\n")
48
+ rescue StandardError, ScriptError => e
49
+ warn "#{e}\n#{e.backtrace.join("\n")}"
55
50
  -1
56
51
  ensure
57
52
  teardown
@@ -75,15 +70,14 @@ module FFI
75
70
  #
76
71
  # * clone_fd is ignored
77
72
  # * filesystem interrupts probably can't work
78
- def run_ruby(foreground: true, single_thread: true, traps: {}, **options)
73
+ def run_ruby(foreground: true, single_thread: true, traps: {}, remember: false, **options)
79
74
  Ackbar.trap(default_traps.merge(traps)) do |signals|
80
75
  daemonize unless foreground
81
76
 
82
- if single_thread
83
- fuse_loop(signals: signals, **options)
84
- else
85
- fuse_loop_mt(signals: signals, **options)
86
- end
77
+ # Monitor for signals (and cache cleaning if required)
78
+ signals.monitor { fuse_cache_timeout(remember) }
79
+
80
+ single_thread ? fuse_loop(**options) : fuse_loop_mt(**options)
87
81
  0
88
82
  end
89
83
  end
@@ -100,43 +94,28 @@ module FFI
100
94
  # * multi-threading will create a new ruby thread for every callback
101
95
  # * cannot daemonize multi-threaded (hangs) TODO: Why - pthread_lock?, GVL?
102
96
  # * cannot pass signals to the filesystem
97
+ # * connot use fuse_context (because the ruby thread is not the native thread)
103
98
  #
104
99
  # @api private
105
100
  # @param [Boolean] foreground
106
101
  # @param [Boolean] single_thread
107
- #
108
102
  def run_native(foreground: true, single_thread: true, **options)
109
- if !single_thread && !foreground
110
- warn 'Cannot run native multi-thread fuse_loop when daemonized. Using single_thread mode'
111
- single_thread = true
112
- end
103
+ raise 'Cannot run deamonized native multi-thread fuse_loop' if !single_thread && !foreground
113
104
 
114
105
  clear_default_traps
115
106
  (se = session) && Libfuse.fuse_set_signal_handlers(se)
116
107
 
117
108
  Libfuse.fuse_daemonize(foreground ? 1 : 0)
118
-
119
- if single_thread
120
- Libfuse.fuse_loop(@fuse)
121
- else
122
- native_fuse_loop_mt(**options)
123
- end
109
+ single_thread ? Libfuse.fuse_loop(@fuse) : native_fuse_loop_mt(**options)
124
110
  ensure
125
111
  (se = session) && Libfuse.fuse_remove_signal_handlers(se)
126
112
  end
127
113
 
128
- # Tell the processing loop to stop and force unmount the filesystem which is unfortunately required to make
129
- # the processing threads, which are mostly blocked on io reads from /dev/fuse fd, to exit.
130
- def exit
131
- Libfuse.fuse_exit(@fuse) if @fuse
132
- # Force threads blocked reading on #io to finish
133
- unmount
134
- end
135
-
136
114
  # Ruby implementation of fuse default traps
137
115
  # @see Ackbar
138
116
  def default_traps
139
- @default_traps ||= { INT: -> { exit }, HUP: -> { exit }, TERM: -> { exit }, PIPE: 'IGNORE' }
117
+ exproc = ->(signame) { exit(signame) }
118
+ @default_traps ||= { INT: exproc, HUP: exproc, TERM: exproc, TSTP: exproc, PIPE: 'IGNORE' }
140
119
  end
141
120
 
142
121
  # @api private
@@ -185,24 +164,8 @@ module FFI
185
164
 
186
165
  # @api private
187
166
  # Ruby implementation of single threaded fuse loop
188
- def fuse_loop(signals:, remember: false, **_options)
189
- loop do
190
- break unless mounted?
191
-
192
- timeout = remember ? fuse_clean_cache : nil
193
-
194
- ready, _ignore_writable, errors = ::IO.select([io, signals.pr], [], [io], timeout)
195
-
196
- next unless ready || errors
197
-
198
- raise 'FUSE error' unless errors.empty?
199
-
200
- break if ready.include?(io) && !process
201
-
202
- break if ready.include?(signals.pr) && !signals.next
203
- rescue Errno::EBADF
204
- raise if mounted? # This will occur on exit
205
- end
167
+ def fuse_loop(**_options)
168
+ fuse_process until fuse_exited?
206
169
  end
207
170
 
208
171
  # @api private
@@ -212,24 +175,51 @@ module FFI
212
175
  # fuse api.
213
176
  #
214
177
  # @see ThreadPool ThreadPool for the mechanism that controls creation and termination of worker threads
215
- def fuse_loop_mt(signals:, max_idle_threads: 10, max_threads: nil, remember: false, **_options)
216
- # Monitor for signals (and cache cleaning if required)
178
+ def fuse_loop_mt(max_idle_threads: 10, max_threads: nil, **_options)
179
+ ThreadPool.new(name: 'FuseThread', max_idle: max_idle_threads.to_i, max_active: max_threads&.to_i) do
180
+ raise StopIteration if fuse_exited?
217
181
 
218
- signals.monitor { remember ? fuse_clean_cache : nil }
219
- ThreadPool.new(name: 'FuseThread', max_idle: max_idle_threads.to_i, max_active: max_threads) { process }.join
182
+ fuse_process
183
+ end.join
220
184
  end
221
185
 
222
186
  # @!visibility private
223
187
  def teardown
224
188
  return unless @fuse
225
189
 
226
- unmount
227
- Libfuse.fuse_destroy(@fuse)
228
- ObjectSpace.undefine_finalizer(self)
190
+ self.exit&.join
191
+
192
+ Libfuse.fuse_destroy(@fuse) if @fuse
229
193
  @fuse = nil
194
+ ensure
195
+ ObjectSpace.undefine_finalizer(self)
196
+ end
197
+
198
+ # Starts a thread to unmount the filesystem and stop the processing loop.
199
+ # generally expected to be called from a signal handler
200
+ # @return [Thread] the unmount thread
201
+ def exit(_signame = nil)
202
+ return unless @fuse
203
+
204
+ # Unmount/exit in a separate thread so the main fuse thread can keep running.
205
+ @exit ||= Thread.new do
206
+ unmount
207
+
208
+ # without this sleep before exit, MacOS does not complete unmounting
209
+ sleep 0.2 if mac_fuse?
210
+
211
+ Libfuse.fuse_exit(@fuse)
212
+
213
+ true
214
+ end
215
+ end
216
+
217
+ private
218
+
219
+ def fuse_cache_timeout(remember)
220
+ remember ? fuse_clean_cache : nil
230
221
  end
231
222
 
232
- # @!visibility private
233
223
  def session
234
224
  return nil unless @fuse
235
225
 
@@ -244,6 +234,10 @@ module FFI
244
234
  Signal.trap(sig, prev) unless prev == 'DEFAULT'
245
235
  end
246
236
  end
237
+
238
+ def mac_fuse?
239
+ FFI::Platform::IS_MAC
240
+ end
247
241
  end
248
242
  end
249
243
  end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative '../accessors'
4
+ require_relative '../boolean_int'
4
5
  module FFI
5
6
  module Libfuse
6
7
  #
@@ -11,195 +12,185 @@ module FFI
11
12
  #
12
13
  class FuseConfig < FFI::Struct
13
14
  include FFI::Accessors
15
+
14
16
  layout(
15
17
  {
16
- # @!method set_gid?
17
- # @return [Boolean]
18
18
  # @!attribute [r] gid
19
- # @return [Integer]
20
- # If `set_gid?` is true the st_gid attribute of each file is overwritten with the value of `gid`.
21
- #
22
- set_gid: :int,
19
+ # @return [Integer|nil] if set, this value will be used for the :gid attribute of each file
20
+ set_gid: :bool_int,
23
21
  gid: :uint,
24
22
 
25
- # @!method set_uid?
26
- # @return [Boolean]
27
23
  # @!attribute [r] uid
28
- # @return [Integer]
29
- # If `set_uid?' is true the st_uid attribute of each file is overwritten with the value of `uid`.
30
- #
31
- set_uid: :int,
24
+ # @return [Integer|nil] if set, this value will be used for the :uid attribute of each file
25
+ set_uid: :bool_int,
32
26
  uid: :uint,
33
27
 
34
- # @!method set_mode?
35
- # @return [Boolean]
36
- # @!attribute [r] mode
37
- # @return [Integer]
38
- # If `set_mode?` is true, the any permissions bits set in `umask` are unset in the st_mode attribute of each
39
- # file.
40
- #
41
- set_mode: :int,
28
+ # @!attribute [r] umask
29
+ # @return [Integer|nil] if set, this mask will be applied to the mode attribute of each file
30
+ set_mode: :bool_int,
42
31
  umask: :uint,
43
- #
44
- # The timeout in seconds for which name lookups will be
45
- # cached.
46
- #
32
+
33
+ # @!attribute [rw] entry_timeout
34
+ # The timeout in seconds for which name lookups will be cached.
35
+ # @return [Float]
47
36
  entry_timeout: :double,
37
+
38
+ # @!attribute [rw] negative_timeout
39
+ # The timeout in seconds for which a negative lookup will be cached.
48
40
  #
49
- # The timeout in seconds for which a negative lookup will be
50
- # cached. This means, that if file did not exist (lookup
51
- # retuned ENOENT), the lookup will only be redone after the
52
- # timeout, and the file/directory will be assumed to not
53
- # exist until then. A value of zero means that negative
54
- # lookups are not cached.
41
+ # This means, that if file did not exist (lookup retuned ENOENT), the lookup will only be redone after the
42
+ # timeout, and the file/directory will be assumed to not exist until then. A value of zero means that
43
+ # negative lookups are not cached.
55
44
  #
45
+ # @return [Float]
56
46
  negative_timeout: :double,
57
- #
47
+
48
+ # @!attribute [rw] attr_timeout
58
49
  # The timeout in seconds for which file/directory attributes
59
- # (as returned by e.g. the `getattr` handler) are cached.
60
50
  #
61
- attr_timeout: :double,
51
+ # (as returned by e.g. the `getattr` handler) are cached.
62
52
  #
53
+ # @return [Float]
54
+ attr_timeout: :double,
55
+
56
+ # @!attribute [rw] intr
63
57
  # Allow requests to be interrupted
58
+ # @return [Boolean]
59
+ intr: :bool_int,
60
+
61
+ # @!attribute [rw] intr_signal
62
+ # Which signal number to send to the filesystem when a request is interrupted.
64
63
  #
65
- intr: :int,
66
- #
67
- # Specify which signal number to send to the filesystem when
68
- # a request is interrupted. The default is hardcoded to
69
- # USR1.
64
+ # The default is hardcoded to USR1.
70
65
  #
66
+ # @return [Integer]
71
67
  intr_signal: :int,
68
+
69
+ # @!attribute [rw] remember
70
+ # the number of seconds inodes are remembered
72
71
  #
73
- # Normally, FUSE assigns inodes to paths only for as long as
74
- # the kernel is aware of them. With this option inodes are
75
- # instead remembered for at least this many seconds. This
76
- # will require more memory, but may be necessary when using
77
- # applications that make use of inode numbers.
72
+ # Normally, FUSE assigns inodes to paths only for as long as the kernel is aware of them. With this option
73
+ # inodes are instead remembered for at least this many seconds. This will require more memory, but may be
74
+ # necessary when using applications that make use of inode numbers.
78
75
  #
79
- # A number of -1 means that inodes will be remembered for the
80
- # entire life-time of the file-system process.
76
+ # A number of -1 means that inodes will be remembered for the entire life-time of the file-system process.
81
77
  #
78
+ # @return [Integer]
82
79
  remember: :int,
80
+
81
+ # @!attribute [rw] hard_remove
82
+ # should open files be removed immediately
83
83
  #
84
- # The default behavior is that if an open file is deleted,
85
- # the file is renamed to a hidden file (.fuse_hiddenXXX), and
86
- # only removed when the file is finally released. This
87
- # relieves the filesystem implementation of having to deal
88
- # with this problem. This option disables the hiding
89
- # behavior, and files are removed immediately in an unlink
90
- # operation (or in a rename operation which overwrites an
91
- # existing file).
92
- #
93
- # It is recommended that you not use the hard_remove
94
- # option. When hard_remove is set, the following libc
95
- # functions fail on unlinked files (returning errno of
96
- # ENOENT): read(2), write(2), fsync(2), close(2), f*xattr(2),
97
- # ftruncate(2), fstat(2), fchmod(2), fchown(2)
98
- #
99
- hard_remove: :int,
84
+ # The default behavior is that if an open file is deleted, the file is renamed to a hidden file
85
+ # (.fuse_hiddenXXX), and only removed when the file is finally released. This relieves the filesystem
86
+ # implementation of having to deal with this problem. This option disables the hiding behavior, and files are
87
+ # removed immediately in an unlink operation (or in a rename operation which overwrites an existing file).
100
88
  #
101
- # Honor the st_ino field in the functions getattr() and
102
- # fill_dir(). This value is used to fill in the st_ino field
103
- # in the stat(2), lstat(2), fstat(2) functions and the d_ino
104
- # field in the readdir(2) function. The filesystem does not
105
- # have to guarantee uniqueness, however some applications
106
- # rely on this value being unique for the whole filesystem.
89
+ # It is recommended that you not use the hard_remove option. When hard_remove is set, the following libc
90
+ # functions fail on unlinked files (returning errno of ENOENT): read(2), write(2), fsync(2), close(2),
91
+ # f*xattr(2), ftruncate(2), fstat(2), fchmod(2), fchown(2)
107
92
  #
108
- # Note that this does *not* affect the inode that libfuse
109
- # and the kernel use internally (also called the "nodeid").
93
+ # @return [Boolean]
94
+ hard_remove: :bool_int,
95
+
96
+ # @!attribute [rw] use_ino
97
+ # use filesystem provided inode values
110
98
  #
111
- use_ino: :int,
99
+ # Honor the st_ino field in the functions getattr() and fill_dir(). This value is used to fill in the st_ino
100
+ # field in the stat(2), lstat(2), fstat(2) functions and the d_ino field in the readdir(2) function. The
101
+ # filesystem does not have to guarantee uniqueness, however some applications rely on this value being unique
102
+ # for the whole filesystem.
112
103
  #
113
- # If use_ino option is not given, still try to fill in the
114
- # d_ino field in readdir(2). If the name was previously
115
- # looked up, and is still in the cache, the inode number
116
- # found there will be used. Otherwise it will be set to -1.
117
- # If use_ino option is given, this option is ignored.
104
+ # Note that this does *not* affect the inode that libfuse and the kernel use internally (also called the
105
+ # "nodeid").
118
106
  #
119
- readdir_ino: :int,
107
+ # @return [Boolean]
108
+ use_ino: :bool_int,
109
+
110
+ # @!attribute [rw] readdir_ino
111
+ # generate inodes for readdir even if {#use_ino} is set
112
+ #
113
+ # If use_ino option is not given, still try to fill in the d_ino field in readdir(2). If the name was
114
+ # previously looked up, and is still in the cache, the inode number found there will be used. Otherwise it
115
+ # will be set to -1. If use_ino option is given, this option is ignored.
116
+ # @return [Boolean]
117
+ readdir_ino: :bool_int,
118
+
119
+ # @!attribute [rw] direct_io
120
+ # disables the use of kernel page cache (file content cache) in the kernel for this filesystem.
120
121
  #
121
- # This option disables the use of page cache (file content cache)
122
- # in the kernel for this filesystem. This has several affects:
122
+ # This has several affects:
123
123
  #
124
- # 1. Each read(2) or write(2) system call will initiate one
125
- # or more read or write operations, data will not be
124
+ # 1. Each read(2) or write(2) system call will initiate one or more read or write operations, data will not be
126
125
  # cached in the kernel.
127
126
  #
128
- # 2. The return value of the read() and write() system calls
129
- # will correspond to the return values of the read and
130
- # write operations. This is useful for example if the
131
- # file size is not known in advance (before reading it).
132
- #
133
- # Internally, enabling this option causes fuse to set the
134
- # `direct_io` field of `struct fuse_file_info` - overwriting
135
- # any value that was put there by the file system.
136
- #
137
- direct_io: :int,
138
- #
139
- # This option disables flushing the cache of the file
140
- # contents on every open(2). This should only be enabled on
141
- # filesystem where the file data is never changed
142
- # externally (not through the mounted FUSE filesystem). Thus
143
- # it is not suitable for network filesystem and other
144
- # intermediate filesystem.
145
- #
146
- # NOTE: if this option is not specified (and neither
147
- # direct_io) data is still cached after the open(2), so a
148
- # read(2) system call will not always initiate a read
149
- # operation.
150
- #
151
- # Internally, enabling this option causes fuse to set the
152
- # `keep_cache` field of `struct fuse_file_info` - overwriting
153
- # any value that was put there by the file system.
154
- #
155
- kernel_cache: :int,
127
+ # 2. The return value of the read() and write() system calls will correspond to the return values of the read
128
+ # and write operations. This is useful for example if the file size is not known in advance (before reading
129
+ # it).
156
130
  #
157
- # This option is an alternative to `kernel_cache`. Instead of
158
- # unconditionally keeping cached data, the cached data is
159
- # invalidated on open(2) if if the modification time or the
160
- # size of the file has changed since it was last opened.
131
+ # Internally, enabling this option causes fuse to set {FuseFileInfo#direct_io} overwriting any value that was
132
+ # put there by the file system during :open
133
+ # @return [Boolean]
134
+ direct_io: :bool_int,
135
+
136
+ # @!attribute [rw] kernel_cache
137
+ # disables flushing the cache of the file contents on every open(2).
161
138
  #
162
- auto_cache: :int,
139
+ # This should only be enabled on filesystem where the file data is never changed externally (not through the
140
+ # mounted FUSE filesystem). Thus it is not suitable for network filesystem and other intermediate filesystem.
163
141
  #
164
- # The timeout in seconds for which file attributes are cached
165
- # for the purpose of checking if auto_cache should flush the
166
- # file data on open.
142
+ # **Note**: if neither this option or {#direct_io} is specified data is still cached after the open(2),
143
+ # so a read(2) system call will not always initiate a read operation.
167
144
  #
168
- ac_attr_timeout_set: :int,
145
+ # Internally, enabling this option causes fuse to set {FuseFileInfo#keep_cache} overwriting any value that was
146
+ # put there by the file system.
147
+ # @return [Boolean]
148
+ kernel_cache: :bool_int,
149
+
150
+ # @!attribute [rw] auto_cache
151
+ # invalidate cached data on open based on changes in file attributes
152
+ #
153
+ # This option is an alternative to `kernel_cache`. Instead of unconditionally keeping cached data, the cached
154
+ # data is invalidated on open(2) if if the modification time or the size of the file has changed since it was
155
+ # last opened.
156
+ # @return [Boolean]
157
+ auto_cache: :bool_int,
158
+
159
+ # @!attribute [rw] ac_attr_timeout
160
+ # 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.
162
+ # @return [Float|nil]
163
+ ac_attr_timeout_set: :bool_int,
169
164
  ac_attr_timeout: :double,
165
+
166
+ # @!attribute [rw] nullpath_ok
167
+ # operations on open files and directories are ok to receive nil paths
170
168
  #
171
- # If this option is given the file-system handlers for the
172
- # following operations will not receive path information:
173
- # read, write, flush, release, fsync, readdir, releasedir,
174
- # fsyncdir, lock, ioctl and poll.
175
- #
176
- # For the truncate, getattr, chmod, chown and utimens
177
- # operations the path will be provided only if the struct
178
- # fuse_file_info argument is NULL.
179
- #
180
- nullpath_ok: :int,
181
- #
182
- # The remaining options are used by libfuse internally and
183
- # should not be touched.
169
+ # If this option is given the file-system handlers for the following operations will not receive path
170
+ # information: read, write, flush, release, fsync, readdir, releasedir, fsyncdir, lock, ioctl and poll.
184
171
  #
185
- show_help: :int,
172
+ # For the truncate, getattr, chmod, chown and utimens operations the path will be provided only if the
173
+ # {FuseFileInfo} argument is nil.
174
+ # @return [Boolean]
175
+ nullpath_ok: :bool_int,
176
+
177
+ # The remaining options are used by libfuse internally and should not be touched.
178
+ show_help: :bool_int,
186
179
  modules: :pointer,
187
- debug: :int
180
+ debug: :bool_int
188
181
  }
189
182
  )
190
183
 
191
- BOOL_ATTRS = %i[
192
- nullpath_ok ac_attr_timeout_set auto_cache kernel_cache direct_io
193
- readdir_ino use_ino hard_remove intr set_mode set_uid set_gid
194
- ].freeze
195
-
196
- ffi_attr_reader(*BOOL_ATTRS)
197
- ffi_attr_writer(*BOOL_ATTRS) { |v| v && v != 0 ? 1 : 0 }
198
- BOOL_ATTRS.each { |a| define_method("#{a}?") { send(a) != 0 } }
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)
187
+ ffi_attr_writer(attr) do |val|
188
+ self[setter] = !val.nil?
189
+ val || 0
190
+ end
191
+ end
199
192
 
200
- OTHER_ATTRS = %i[ac_attr_timeout remember intr_signal attr_timeout negative_timeout entry_timeout umask uid
201
- gid].freeze
202
- ffi_attr_accessor(*OTHER_ATTRS)
193
+ ffi_attr_accessor(*(members - (setters.keys + setters.values)))
203
194
  end
204
195
  end
205
196
  end