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