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