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
@@ -2,62 +2,60 @@
2
2
 
3
3
  require_relative 'fuse_version'
4
4
  require_relative '../accessors'
5
+ require_relative '../boolean_int'
5
6
 
6
7
  module FFI
7
8
  # Ruby FFI Binding for [libfuse](https://github.com/libfuse/libfuse)
8
9
  module Libfuse
9
- # These are constants in fuse_common.h but defined like bitmask
10
- #
11
- # FUSE_CAP_ASYNC_READ: filesystem supports asynchronous read requests
12
- # FUSE_CAP_POSIX_LOCKS: filesystem supports "remote" locking
13
- # FUSE_CAP_ATOMIC_O_TRUNC: filesystem handles the O_TRUNC open flag
14
- # FUSE_CAP_EXPORT_SUPPORT: filesystem handles lookups of "." and ".."
15
- # FUSE_CAP_BIG_WRITES: filesystem can handle write size larger than 4kB
16
- # FUSE_CAP_DONT_MASK: don't apply umask to file mode on create operations
17
- # FUSE_CAP_SPLICE_WRITE: ability to use splice() to write to the fuse device
18
- # FUSE_CAP_SPLICE_MOVE: ability to move data to the fuse device with splice()
19
- # FUSE_CAP_SPLICE_READ: ability to use splice() to read from the fuse device
20
- # FUSE_CAP_FLOCK_LOCKS: ?
21
- # FUSE_CAP_IOCTL_DIR: ioctl support on directories
22
-
23
- # FUSE_IOCTL_COMPAT: 32bit compat ioctl on 64bit machine
24
- # FUSE_IOCTL_UNRESTRICTED: not restricted to well-formed ioctls, retry allowed
25
- # FUSE_IOCTL_RETRY: retry with new iovecs
26
- # FUSE_IOCTL_DIR: is a directory
27
- bitmask :fuse_ioctl, %i[compat unrestricted retry dir]
28
-
29
- # #define FUSE_CAP_ASYNC_READ (1 << 0)
30
- # #define FUSE_CAP_POSIX_LOCKS (1 << 1)
31
- # #define FUSE_CAP_ATOMIC_O_TRUNC (1 << 3)
32
- # #define FUSE_CAP_EXPORT_SUPPORT (1 << 4)
33
- # #define FUSE_CAP_BIG_WRITES (1 << 5)
34
- # #define FUSE_CAP_DONT_MASK (1 << 6)
35
- # #define FUSE_CAP_SPLICE_WRITE (1 << 7)
36
- # #define FUSE_CAP_SPLICE_MOVE (1 << 8)
37
- # #define FUSE_CAP_SPLICE_READ (1 << 9)
38
- # #define FUSE_CAP_FLOCK_LOCKS (1 << 10)
39
- # #define FUSE_CAP_IOCTL_DIR (1 << 11)
40
-
41
- if FUSE_MAJOR_VERSION >= 3
42
- bitmask :fuse_cap, %i[
43
- async_read posix_locks atomic_o_trunc export_support
44
- big_writes dont_mask splice_write splice_move splice_read
45
- flock_locks ioctl_dir
46
- ]
47
- else
48
- bitmask :fuse_cap, %i[
49
- async_read posix_locks atomic_o_trunc export_support
50
- dont_mask splice_write splice_move splice_read flock_locks ioctl_dir
51
- auto_inval_data readdirplus readdirplus_auto async_dio writeback_cache no_open_support
52
- parallel_dirops posix_acl handle_killpriv explicit_inval_data
53
- ]
54
- end
10
+ capabilities =
11
+ if FUSE_MAJOR_VERSION >= 3
12
+ %i[
13
+ async_read
14
+ posix_locks
15
+ atomic_o_trunc
16
+ export_support
17
+ dont_mask
18
+ splice_write
19
+ splice_move
20
+ splice_read
21
+ flock_locks
22
+ ioctl_dir
23
+ auto_inval_data
24
+ readdirplus
25
+ readdirplus_auto
26
+ async_dio
27
+ writeback_cache
28
+ no_open_support
29
+ parallel_dirops
30
+ posix_acl
31
+ handle_killpriv
32
+ cache_symlinks
33
+ no_opendir_support
34
+ explicit_inval_data
35
+ ]
36
+ else
37
+ %i[
38
+ async_readcap
39
+ posix_locks
40
+ atomic_o_trunc
41
+ export_support
42
+ big_writes
43
+ dont_mask
44
+ splice_write
45
+ splice_move
46
+ splice_read
47
+ flock_locks
48
+ ioctl_dir
49
+ ]
50
+ end
51
+
52
+ bitmask :fuse_cap, capabilities
53
+
55
54
  #
56
55
  # Connection information
57
56
  #
58
- # Some of the elements are read-write, these can be changed to
59
- # indicate the value requested by the filesystem. The requested
60
- # value must usually be smaller than the indicated value.
57
+ # Some of the elements are read-write, these can be changed to indicate the value requested by the filesystem. The
58
+ # requested value must usually be smaller than the indicated value.
61
59
  #
62
60
  # @see FuseOperations#init
63
61
  class FuseConnInfo < FFI::Struct
@@ -69,81 +67,18 @@ module FFI
69
67
  max_write: :uint,
70
68
  max_read: :uint,
71
69
  max_readahead: :uint,
72
- #
73
- # Capability flags that the kernel supports (read-only)
74
- #
75
70
  capable: :fuse_cap,
76
- #
77
- # Capability flags that the filesystem wants to enable.
78
- #
79
- # libfuse attempts to initialize this field with
80
- # reasonable default values before calling the init() handler.
81
- #
82
71
  want: :fuse_cap,
83
- #
84
- # Maximum number of pending "background" requests. A
85
- # background request is any type of request for which the
86
- # total number is not limited by other means. As of kernel
87
- # 4.8, only two types of requests fall into this category:
88
- #
89
- # 1. Read-ahead requests
90
- # 2. Asynchronous direct I/O requests
91
- #
92
- # Read-ahead requests are generated (if max_readahead is
93
- # non-zero) by the kernel to preemptively fill its caches
94
- # when it anticipates that userspace will soon read more
95
- # data.
96
- #
97
- # Asynchronous direct I/O requests are generated if
98
- # FUSE_CAP_ASYNC_DIO is enabled and userspace submits a large
99
- # direct I/O request. In this case the kernel will internally
100
- # split it up into multiple smaller requests and submit them
101
- # to the filesystem concurrently.
102
- #
103
- # Note that the following requests are *not* background
104
- # requests: writeback requests (limited by the kernel's
105
- # flusher algorithm), regular (i.e., synchronous and
106
- # buffered) userspace read/write requests (limited to one per
107
- # thread), asynchronous read requests (Linux's io_submit(2)
108
- # call actually blocks, so these are also limited to one per
109
- # thread).
110
- #
111
72
  max_background: :uint,
112
- #
113
- # Kernel congestion threshold parameter. If the number of pending
114
- # background requests exceeds this number, the FUSE kernel module will
115
- # mark the filesystem as "congested". This instructs the kernel to
116
- # expect that queued requests will take some time to complete, and to
117
- # adjust its algorithms accordingly (e.g. by putting a waiting thread
118
- # to sleep instead of using a busy-loop).
119
- #
120
73
  congestion_threshold: :uint,
121
- #
122
- # When FUSE_CAP_WRITEBACK_CACHE is enabled, the kernel is responsible
123
- # for updating mtime and ctime when write requests are received. The
124
- # updated values are passed to the filesystem with setattr() requests.
125
- # However, if the filesystem does not support the full resolution of
126
- # the kernel timestamps (nanoseconds), the mtime and ctime values used
127
- # by kernel and filesystem will differ (and result in an apparent
128
- # change of times after a cache flush).
129
- #
130
- # To prevent this problem, this variable can be used to inform the
131
- # kernel about the timestamp granularity supported by the file-system.
132
- # The value should be power of 10. The default is 1, i.e. full
133
- # nano-second resolution. Filesystems supporting only second resolution
134
- # should set this to 1000000000.
135
- #
136
74
  time_gran: :uint,
137
- #
138
- # For future use.
139
- #
140
- reserved: [:uint, 22]
75
+ reserved: [:uint, 22] # for future use
141
76
  }
142
77
  else
143
78
  {
144
79
  proto_major: :uint,
145
80
  proto_minor: :uint,
146
- async_read: :uint, # This slot is max_read in Fuse 3
81
+ async_read: :bool_int, # long deprecated
147
82
  max_write: :uint,
148
83
  max_readahead: :uint,
149
84
  capable: :fuse_cap,
@@ -159,25 +94,23 @@ module FFI
159
94
  layout fuse_layout
160
95
 
161
96
  # @!attribute [r] proto_major
162
- # @return [Integer] Major version of the protocol (read-only)
97
+ # @return [Integer] Major version of the protocol (read-only)
163
98
 
164
99
  # @!attribute [r] proto_minor
165
- # @return [Integer] Minor version of the protocol (read-only)
100
+ # @return [Integer] Minor version of the protocol (read-only)
166
101
 
167
102
  ffi_attr_reader :proto_major, :proto_minor
168
103
 
169
- # Is asynchronous read supported (read-write)
170
- ffi_attr_accessor :async_read if FUSE_MAJOR_VERSION < 3
171
-
172
104
  # @!attribute [rw] max_read
173
- # @return [Integer] Maximum size of read requests.
105
+ # @return [Integer] Maximum size of read requests.
174
106
  #
175
- # A value of zero indicates no limit. However, even if the filesystem does not specify a limit, the maximum size
176
- # of read requests will still be limited by the kernel.
107
+ # A value of zero indicates no limit. However, even if the filesystem does not specify a limit, the maximum size
108
+ # of read requests will still be limited by the kernel.
177
109
  #
178
- # @note For the time being, the maximum size of read requests must be set both here *and* passed to
179
- # using the ``-o max_read=<n>`` mount option. At some point in the future, specifying the mount
180
- # option will no longer be necessary.
110
+ # @note For the time being, the maximum size of read requests must be set both here *and* passed to
111
+ # using the ``-o max_read=<n>`` mount option. At some point in the future, specifying the mount
112
+ # option will no longer be necessary.
113
+ # @since Fuse3
181
114
  ffi_attr_accessor :max_read if FUSE_MAJOR_VERSION >= 3
182
115
 
183
116
  # @!attribute [rw] max_write
@@ -188,23 +121,266 @@ module FFI
188
121
 
189
122
  ffi_attr_accessor :max_write, :max_readahead
190
123
 
191
- # Capability flags that kernel supports
124
+ # @!attribute [r] capable
125
+ # Capability flags supported by kernel fuse module
126
+ #
127
+ # * `:async_read` Indicates that the filesystem supports asynchronous read requests.
128
+ #
129
+ # If this capability is not requested/available, the kernel will ensure that there is at most one pending read
130
+ # request per file-handle at any time, and will attempt to order read requests by increasing offset.
131
+ #
132
+ # This feature is enabled by default when supported by the kernel.
133
+ #
134
+ # * `:posix_locks` Indicates that the filesystem supports "remote" locking.
135
+ #
136
+ # This feature is enabled by default when supported by the kernel,
137
+ # and if getlk() and setlk() handlers are implemented.
138
+ #
139
+ # * `:atomic_o_trunc` Indicates that the filesystem supports the O_TRUNC open flag.
140
+ #
141
+ # If disabled, and an application specifies O_TRUNC, fuse first calls truncate() and then open() with O_TRUNC
142
+ # filtered out.
143
+ #
144
+ # This feature is enabled by default when supported by the kernel.
145
+ #
146
+ # * `:export_support` Indicates that the filesystem supports lookups of "." and "..".
147
+ #
148
+ # This feature is disabled by default.
149
+ #
150
+ # * `:big_writes` Indicates the filesystem can handle write size larger than 4kB.
151
+ #
152
+ # Removed in Fuse3 where is now always active. Filesystems that want to limit the size of write requests should
153
+ # use the {#max_write} option instead.
154
+ #
155
+ # * `:dont_mask` Indicates that the kernel should not apply the umask to the file mode on create operations.
156
+ #
157
+ # This feature is disabled by default.
158
+ #
159
+ # * `:splice_write` Indicates that libfuse should try to use splice() when writing to the fuse device.
160
+ #
161
+ # This may improve performance. This feature is disabled by default.
162
+ #
163
+ # * `:splice_move` Indicates that libfuse should try to move pages instead of copying when writing to / reading
164
+ # from the fuse device.
165
+ #
166
+ # This may improve performance. This feature is disabled by default.
167
+ #
168
+ # * `:splice_read` Indicates that libfuse should try to use splice() when reading from the fuse device.
169
+ #
170
+ # This may improve performance. This feature is enabled by default when supported by the kernel and
171
+ # if the filesystem implements a write_buf() handler.
172
+ #
173
+ # * `:flock_locks` If set, the calls to flock(2) will be emulated using POSIX locks and must then be handled by
174
+ # the filesystem's :flock handler.
175
+ #
176
+ # If not set, flock(2) calls will be handled by the FUSE kernel module internally (so any access that does not go
177
+ # through the kernel cannot be taken into account).
178
+ #
179
+ # This feature is enabled by default when supported by the kernel and if the filesystem implements a flock()
180
+ # handler.
181
+ #
182
+ # * `:ioctl_dir` Indicates that the filesystem supports ioctl's on directories.
183
+ #
184
+ # This feature is enabled by default when supported by the kernel.
185
+ #
186
+ # * :`auto_inval_data`
187
+ #
188
+ # Traditionally, while a file is open the FUSE kernel module only asks the filesystem for an update of the file's
189
+ # attributes when a client attempts to read beyond EOF. This is unsuitable for e.g. network filesystems, where
190
+ # the file contents may change without the kernel knowing about it.
191
+ #
192
+ # If this flag is set, FUSE will check the validity of the attributes on every read. If the attributes are no
193
+ # longer valid (i.e., if the *attr_timeout* passed to fuse_reply_attr() or set in `struct fuse_entry_param` has
194
+ # passed), it will first issue a `getattr` request. If the new mtime differs from the previous value, any cached
195
+ # file *contents* will be invalidated as well.
196
+ #
197
+ # This flag should always be set when available. If all file changes go through the kernel, *attr_timeout* should
198
+ # be set to a very large number to avoid unnecessary getattr() calls.
199
+ #
200
+ # This feature is enabled by default when supported by the kernel.
201
+ #
202
+ # * `:readdirplus` Indicates that the filesystem supports readdirplus.
203
+ #
204
+ # This feature is enabled by default when supported by the kernel and if the filesystem implements the
205
+ # readdirplus() handler
206
+ #
207
+ # * `:readdirplus_auto` Indicates that the filesystem supports adaptive readdirplus.
208
+ #
209
+ # If :readdirplus is not set, this flag has no effect.
210
+ #
211
+ # If :readdirplus is set and this flag is not set, the kernel will always issue readdirplus() requests to
212
+ # retrieve directory contents.
213
+ #
214
+ # If :readdirplus is set and this flag is set, the kernel will issue both readdir() and readdirplus() requests,
215
+ # depending on how much information is expected to be required.
216
+ #
217
+ # As of Linux 4.20, the algorithm is as follows: when userspace starts to read directory entries, issue a
218
+ # :reaadirplus request to the filesystem. If any entry attributes have been looked up by the time userspace
219
+ # requests the next batch of entries continue with :reaadirplus, otherwise switch to plain :readdir. This will
220
+ # result in eg plain "ls" triggering :reaadirplus first then :readdir after that because it doesn't do lookups.
221
+ # "ls -l" should result in all :reaadirplus, except if dentries are already cached.
222
+ #
223
+ # This feature is enabled by default when supported by the kernel and if the filesystem implements both a
224
+ # readdirplus() and a readdir() handler.
225
+ #
226
+ # **Note** The high-level operations mix :readdir and :readdirplus into one operation
227
+ # with flags to indicate behaviour. As such for the purposes of above :readdirplus is always implemented!
228
+ #
229
+ # * `:async_dio` Indicates that the filesystem supports asynchronous direct I/O submission.
230
+ #
231
+ # If this capability is not requested/available, the kernel will ensure that there is at most one pending read
232
+ # and one pending write request per direct I/O file-handle at any time.
233
+ #
234
+ # This feature is enabled by default when supported by the kernel.
235
+ #
236
+ # * `:writeback_cache` Indicates that writeback caching should be enabled.
237
+ #
238
+ # This means that individual write request may be buffered and merged in the kernel before they are send to the
239
+ # filesystem.
240
+ #
241
+ # This feature is disabled by default.
242
+ #
243
+ # * `:no_open_support` Indicates support for zero-message opens.
244
+ #
245
+ # If this flag is set in the `capable` field of the `fuse_conn_info` structure, then the filesystem may return
246
+ # `ENOSYS` from the open() handler to indicate success. Further attempts to open files will be handled in the
247
+ # kernel. (If this flag is not set, returning ENOSYS will be treated as an error and signaled to the caller).
248
+ #
249
+ # Setting (or unsetting) this flag in the `want` field has *no effect*.
250
+ #
251
+ # * `:parallel_dirops` Indicates support for parallel directory operations.
252
+ #
253
+ # If this flag is unset, the FUSE kernel module will ensure that lookup() and readdir() requests are never issued
254
+ # concurrently for the same directory.
255
+ #
256
+ # This feature is enabled by default when supported by the kernel.
257
+ #
258
+ # * `:posix_acl` Indicates support for POSIX ACLs.
259
+ #
260
+ # If this feature is enabled, the kernel will cache and have responsibility for enforcing ACLs. ACL will be
261
+ # stored as xattrs and passed to userspace, which is responsible for updating the ACLs in the filesystem, keeping
262
+ # the file mode in sync with the ACL, and ensuring inheritance of default ACLs when new filesystem nodes are
263
+ # created. Note that this requires that the file system is able to parse and interpret the xattr representation
264
+ # of ACLs.
265
+ #
266
+ # Enabling this feature implicitly turns on the ``default_permissions`` mount option (even if it was not passed
267
+ # to mount(2)).
268
+ #
269
+ # This feature is disabled by default.
270
+ #
271
+ # * `:handle_killpriv` Indicates that the filesystem is responsible for unsetting setuid and setgid bits when a
272
+ # file is written, truncated, or its owner is changed.
273
+ #
274
+ # This feature is enabled by default when supported by the kernel.
275
+ #
276
+ # * `:cache_symlinks` Indicates that the kernel supports caching symlinks in its page cache.
277
+ #
278
+ # When this feature is enabled, symlink targets are saved in the page cache. You can invalidate a cached link by
279
+ # calling: `fuse_lowlevel_notify_inval_inode(se, ino, 0, 0);`
280
+ #
281
+ # This feature is disabled by default.
282
+ #
283
+ # * `:no_opendir_support` Indicates support for zero-message opendirs.
284
+ #
285
+ # If this flag is set then the filesystem may return `ENOSYS` from the opendir() handler to indicate success.
286
+ # Further opendir and releasedir messages will be handled in the kernel. (If this flag is not set, returning
287
+ # ENOSYS will be treated as an error and signalled to the caller.)
288
+ #
289
+ # Setting (or unsetting) this flag in the `want` field has *no effect*.
290
+ #
291
+ # * `:explicit_inval_data` Indicates support for invalidating cached pages only on explicit request.
292
+ #
293
+ # If this flag is set in the `capable` field of the `fuse_conn_info` structure, then the FUSE kernel module
294
+ # supports invalidating cached pages only on explicit request by the filesystem through
295
+ # fuse_lowlevel_notify_inval_inode() or fuse_invalidate_path().
296
+ #
297
+ # By setting this flag in the `want` field of the `fuse_conn_info` structure, the filesystem is responsible for
298
+ # invalidating cached pages through explicit requests to the kernel.
299
+ #
300
+ # Note that setting this flag does not prevent the cached pages from being flushed by OS itself and/or through
301
+ # user actions.
302
+ #
303
+ # Note that if both :explicit_inval_data and :auto_inval_data are set then :auto_inval_data takes precedence.
304
+ #
305
+ # This feature is disabled by default.
306
+ # @return [Array<Symbol>]
192
307
  ffi_attr_reader :capable
193
308
 
194
- # Is capable of all of these FUSE_CAPs
195
- def capable?(*of)
196
- of.all? { |c| self[:capable].include?(c) }
309
+ # @param [Array<Symbol>] capabilities
310
+ # @return [Boolean] true if {#capable} of all capabilities
311
+ def capable?(*capabilities)
312
+ capabilities.all? { |c| self[:capable].include?(c) }
313
+ end
314
+
315
+ # @attribute [rw] want
316
+ # @overload want()
317
+ # Capability flags that the filesystem wants to enable.
318
+ #
319
+ # libfuse attempts to initialize this field with reasonable default values before calling the :init handler.
320
+ #
321
+ # @overload want(*capabiities)
322
+ # Add to the capabilities wanted by the filesystem
323
+ # @param [Array<Symbol>] capabilities list to add
324
+ # @overload want(**capabilities)
325
+ # @param [Hash<Symbol,Boolean>] capabilities map of capability names to whether they are explicitly wanted
326
+ # or unwanted
327
+ # @return [Array<Symbol>]
328
+ # @see capable
329
+ ffi_attr_reader(:want, simple: false) do |*caps, **h|
330
+ next self[:want] if caps.empty? && h.empty?
331
+
332
+ h.merge!(caps.pop) if caps.last.is_a?(Hash)
333
+ add, del = h.keys.partition { |c| h[c] }
334
+ self[:want] = (self[:want] - del + add + caps)
197
335
  end
198
336
 
199
- # Capability flags that the filesystem wants
200
- ffi_attr_accessor :want
337
+ # @param [Array<Symbol>] capabilities
338
+ # @return [Boolean] true if all capabilities are wanted
339
+ def wanted?(*capabilities)
340
+ capabilities.all? { |c| self[:want].include?(c) }
341
+ end
201
342
 
202
- # Maximum number of backgrounded requests
343
+ # @!attribute [rw] max_background
344
+ # @return [Integer] Maximum number of pending "background" requests.
345
+ #
346
+ # A background request is any type of request for which the total number is not limited by other means. As of
347
+ # kernel 4.8, only two types of requests fall into this category:
348
+ #
349
+ # 1. Read-ahead requests
350
+ # 2. Asynchronous direct I/O requests
351
+ #
352
+ # Read-ahead requests are generated (if max_readahead is non-zero) by the kernel to preemptively fill its
353
+ # caches when it anticipates that userspace will soon read more data.
354
+ #
355
+ # Asynchronous direct I/O requests are generated if :async_dio is enabled and userspace submits a large
356
+ # direct I/O request. In this case the kernel will internally split it up into multiple smaller requests and
357
+ # submit them to the filesystem concurrently.
358
+ #
359
+ # Note that the following requests are *not* background requests: writeback requests (limited by the kernel's
360
+ # flusher algorithm), regular (i.e., synchronous and buffered) userspace read/write requests (limited to one per
361
+ # thread), asynchronous read requests (Linux's io_submit(2) call actually blocks, so these are also limited to one
362
+ # per thread).
203
363
  ffi_attr_accessor :max_background
204
364
 
205
- # Kernel congestion threshold parameter
206
- ffi_attr_reader :congestion_threshold
365
+ # @!attribute [rw] congestion_threshold
366
+ # @return [Integer] Kernel congestion threshold parameter
367
+ #
368
+ # f the number of pending background requests exceeds this number, the FUSE kernel module will mark the filesystem
369
+ # as "congested". This instructs the kernel to expect that queued requests will take some time to complete, and to
370
+ # adjust its algorithms accordingly (e.g. by putting a waiting thread to sleep instead of using a busy-loop).
371
+ ffi_attr_accessor :congestion_threshold
207
372
 
373
+ # @!attribute [rw] time_gran
374
+ # @return [Integer] timestamp granularity supported by the file-system
375
+ #
376
+ # When :writeback_cache is enabled, the kernel is responsible for updating mtime and ctime when write requests are
377
+ # received. The updated values are passed to the filesystem with setattr() requests. However, if the filesystem
378
+ # does not support the full resolution of the kernel timestamps (nanoseconds), the mtime and ctime values used by
379
+ # kernel and filesystem will differ (and result in an apparent change of times after a cache flush).
380
+ #
381
+ # To prevent this problem, this variable can be used to inform the kernel about the timestamp granularity
382
+ # supported by the file-system. The value should be power of 10. The default is 1, i.e. full nano-second
383
+ # resolution. Filesystems supporting only second resolution should set this to 1000000000.
208
384
  ffi_attr_accessor :time_gran if FUSE_MAJOR_VERSION >= 3
209
385
  end
210
386
  end
@@ -15,9 +15,20 @@ module FFI
15
15
  base[:umask] = :mode_t if FUSE_VERSION >= 28
16
16
  layout base
17
17
 
18
- ffi_attr_reader(:uid, :gid, :pid, :mode, :private_data)
18
+ ffi_attr_reader(*members, simple: false) do
19
+ m = __method__
19
20
 
20
- ffi_attr_reader(:umask) if FUSE_VERSION >= 28
21
+ # Use overrides if they are available, or the default context if the underlying memory is invalid
22
+ FuseContext.overrides[m] || (null? ? DEFAULT_CONTEXT[m] : self[m])
23
+ end
24
+
25
+ if FUSE_VERSION < 28
26
+ attr_writer :umask
27
+
28
+ def umask
29
+ @umask ||= File.umask
30
+ end
31
+ end
21
32
 
22
33
  # @!attribute [r] uid
23
34
  # @return [Integer] user id of the calling process
@@ -32,7 +43,9 @@ module FFI
32
43
  # @return [Object] private filesystem data
33
44
  # @see FuseOperations#init
34
45
 
35
- # @!attribute [r] umask
46
+ # @!attribute [rw] umask
47
+ #
48
+ # Writable only for Fuse version < 28
36
49
  # @return [Integer] umask of the calling process
37
50
 
38
51
  # @return [Boolean]
@@ -48,8 +61,37 @@ module FFI
48
61
  Libfuse.raise_interrupt
49
62
  end
50
63
 
64
+ # @param [Integer] perms
65
+ # @return perms adjusted by {#umask}
66
+ def mask(perms)
67
+ perms & ~umask
68
+ end
69
+
70
+ DEFAULT_CONTEXT = { uid: Process.uid, gid: Process.gid, umask: File.umask }.freeze
71
+
51
72
  class << self
73
+ # @overload overrides(hash)
74
+ # @param[Hash<Symbol,Object|nil] hash a list of override values that will apply to this context
75
+ #
76
+ # If not set uid, gid, umask will be overridden from the current process, which is useful if
77
+ # {FuseContext} is referenced from outside of a fuse callback
78
+ # @yield [] executes block with the given hash overriding FuseContext values
79
+ # @return [Object] the result of the block
80
+ # @overload overrides()
81
+ # @return [Hash] current thread local overrides for FuseContext
82
+ def overrides(hash = nil)
83
+ return Thread.current[:fuse_context_overrides] ||= {} unless block_given?
84
+
85
+ begin
86
+ Thread.current[:fuse_context_overrides] = hash || DEFAULT_CONTEXT
87
+ yield
88
+ ensure
89
+ Thread.current[:fuse_context_overrides] = nil
90
+ end
91
+ end
92
+
52
93
  # @return [FuseContext] the context for the current filesystem operation
94
+ # @note if called outside a fuse callback the native {FuseContext} will have invalid values. See {overrides}
53
95
  def fuse_get_context
54
96
  Libfuse.fuse_get_context
55
97
  end