ffi-libfuse 0.0.1.pre

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 (53) hide show
  1. checksums.yaml +7 -0
  2. data/.yardopts +1 -0
  3. data/README.md +100 -0
  4. data/lib/ffi/accessors.rb +145 -0
  5. data/lib/ffi/devt.rb +30 -0
  6. data/lib/ffi/flock.rb +47 -0
  7. data/lib/ffi/gnu_extensions.rb +115 -0
  8. data/lib/ffi/libfuse/ackbar.rb +112 -0
  9. data/lib/ffi/libfuse/adapter/context.rb +37 -0
  10. data/lib/ffi/libfuse/adapter/debug.rb +89 -0
  11. data/lib/ffi/libfuse/adapter/fuse2_compat.rb +91 -0
  12. data/lib/ffi/libfuse/adapter/fuse3_support.rb +87 -0
  13. data/lib/ffi/libfuse/adapter/interrupt.rb +37 -0
  14. data/lib/ffi/libfuse/adapter/pathname.rb +23 -0
  15. data/lib/ffi/libfuse/adapter/ruby.rb +334 -0
  16. data/lib/ffi/libfuse/adapter/safe.rb +58 -0
  17. data/lib/ffi/libfuse/adapter/thread_local_context.rb +36 -0
  18. data/lib/ffi/libfuse/adapter.rb +79 -0
  19. data/lib/ffi/libfuse/callbacks.rb +61 -0
  20. data/lib/ffi/libfuse/fuse2.rb +159 -0
  21. data/lib/ffi/libfuse/fuse3.rb +162 -0
  22. data/lib/ffi/libfuse/fuse_args.rb +166 -0
  23. data/lib/ffi/libfuse/fuse_buffer.rb +155 -0
  24. data/lib/ffi/libfuse/fuse_callbacks.rb +48 -0
  25. data/lib/ffi/libfuse/fuse_cmdline_opts.rb +44 -0
  26. data/lib/ffi/libfuse/fuse_common.rb +249 -0
  27. data/lib/ffi/libfuse/fuse_config.rb +205 -0
  28. data/lib/ffi/libfuse/fuse_conn_info.rb +211 -0
  29. data/lib/ffi/libfuse/fuse_context.rb +79 -0
  30. data/lib/ffi/libfuse/fuse_file_info.rb +100 -0
  31. data/lib/ffi/libfuse/fuse_loop_config.rb +43 -0
  32. data/lib/ffi/libfuse/fuse_operations.rb +870 -0
  33. data/lib/ffi/libfuse/fuse_opt.rb +54 -0
  34. data/lib/ffi/libfuse/fuse_poll_handle.rb +59 -0
  35. data/lib/ffi/libfuse/fuse_version.rb +43 -0
  36. data/lib/ffi/libfuse/job_pool.rb +53 -0
  37. data/lib/ffi/libfuse/main.rb +200 -0
  38. data/lib/ffi/libfuse/test/operations.rb +56 -0
  39. data/lib/ffi/libfuse/test.rb +3 -0
  40. data/lib/ffi/libfuse/thread_pool.rb +147 -0
  41. data/lib/ffi/libfuse/version.rb +8 -0
  42. data/lib/ffi/libfuse.rb +24 -0
  43. data/lib/ffi/ruby_object.rb +95 -0
  44. data/lib/ffi/stat/constants.rb +29 -0
  45. data/lib/ffi/stat/native.rb +50 -0
  46. data/lib/ffi/stat/time_spec.rb +137 -0
  47. data/lib/ffi/stat.rb +96 -0
  48. data/lib/ffi/stat_vfs.rb +81 -0
  49. data/lib/ffi/struct_array.rb +39 -0
  50. data/lib/ffi/struct_wrapper.rb +100 -0
  51. data/sample/memory_fs.rb +189 -0
  52. data/sample/no_fs.rb +69 -0
  53. metadata +165 -0
@@ -0,0 +1,100 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'fuse_version'
4
+ require_relative '../struct_wrapper'
5
+ require_relative '../ruby_object'
6
+
7
+ module FFI
8
+ # Ruby FFI Binding for [libfuse](https://github.com/libfuse/libfuse)
9
+ module Libfuse
10
+ bit_flags = %i[direct_io keep_cache flush nonseekable flock_release cache_readdir]
11
+ bit_flags.unshift(:writepage) if FUSE_MAJOR_VERSION >= 3
12
+ bitmask :file_info_flags, bit_flags
13
+
14
+ # Native struct layout
15
+ # @!visibility private
16
+ class NativeFuseFileInfo < FFI::Struct
17
+ # NOTE: cache_readdir added in Fuse3, but always available for compatibility
18
+ if FUSE_MAJOR_VERSION == 2
19
+ layout(
20
+ flags: :int,
21
+ fh_old: :ulong, # deprecated
22
+ writepage: :int,
23
+ bit_flags: :file_info_flags,
24
+ fh: RubyObject.by_object_id(:uint64_t),
25
+ lock_owner: :uint64_t
26
+ )
27
+ else
28
+ layout(
29
+ flags: :int,
30
+ bit_flags: :file_info_flags,
31
+ padding: :uint,
32
+ fh: RubyObject.by_object_id(:uint64_t),
33
+ lock_owner: :uint64_t,
34
+ poll_events: :uint32_t
35
+ )
36
+ end
37
+ end
38
+
39
+ # FuseFileInfo
40
+ class FuseFileInfo
41
+ # This uses a struct wrapper as references can be null
42
+ include(StructWrapper)
43
+ native_struct(NativeFuseFileInfo)
44
+
45
+ if FUSE_MAJOR_VERSION == 2
46
+ ffi_attr_reader(:writepage) { |v| v != 0 }
47
+ else
48
+ ffi_bitflag_reader(:bit_flags, :writepage)
49
+ end
50
+
51
+ # @!attribute [r] flags
52
+ # @return [Integer] Open flags. Available in open() and release()
53
+ # @see Fcntl
54
+ ffi_attr_reader :flags
55
+
56
+ # @!attribute [r] lock_owner
57
+ # @return [Integer] Lock owner id. Available in locking operations and flush
58
+ ffi_attr_reader :lock_owner
59
+
60
+ # @!attribute [rw] fh
61
+ # Note this fh is weakly referenced by kernel fuse, make sure a reference is kept to prevent it from being
62
+ # garbage collected until release()
63
+ # @return [Object] File handle. May be filled in by filesystem in open()
64
+ ffi_attr_accessor(:fh)
65
+
66
+ # @!attribute [r] writepage
67
+ # In case of a write operation indicates if this was caused by a delayed write from the page cache. If so, then
68
+ # the context's pid, uid, and gid fields will not be valid, and the *fh* value may not match the *fh* value that
69
+ # would have been sent with the corresponding individual write requests if write caching had been disabled.
70
+ # @return [Boolean] indicates if this was caused by a writepage
71
+
72
+ # @!attribute [r] flush
73
+ # Set in flush operation, also maybe set in highlevel lock operation and lowlevel release operation.
74
+ # @return [Boolean] Indicates a flush operation.
75
+ ffi_bitflag_reader(:bit_flags, :flush)
76
+
77
+ # @!attribute [rw] direct_io
78
+ # @return [Boolean] Can be filled in by open, to use direct I/O on this file.
79
+
80
+ # @!attribute [rw] keep_cache
81
+ # @return [Boolean] Can be filled in by open, to indicate, that cached file data need not be invalidated.
82
+
83
+ # @!attribute [rw] nonseekable
84
+ # @return [Boolean] Can be filled in by open, to indicate that the file is not seekable
85
+ # @since Fuse2.8
86
+
87
+ # @!attribute [rw] flock_release
88
+ # If set, lock_owner shall contain a valid value.
89
+ # May only be set in ->release(). Introduced in version 2.9
90
+ # @return [Boolean] Indicates that flock locks for this file should be released.
91
+
92
+ # @!attribute [rw] cache_readdir
93
+ # Can be filled in by opendir.
94
+ # @return [Boolean] signals the kernel to enable caching of entries returned by readdir()
95
+ # @since Fuse3
96
+
97
+ ffi_bitflag_accessor(:bit_flags, :direct_io, :keep_cache, :nonseekable, :flock_release, :cache_readdir)
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../accessors'
4
+
5
+ module FFI
6
+ module Libfuse
7
+ # struct fuse_loop_config {
8
+ # int clone_fd;
9
+ # unsigned int max_idle_threads;
10
+ # };
11
+ class FuseLoopConfig < FFI::Struct
12
+ include(FFI::Accessors)
13
+
14
+ layout(
15
+ clone_fd: :int,
16
+ max_idle: :int
17
+ )
18
+
19
+ # @!attribute [rw] clone_fd
20
+ # whether to use separate device fds for each thread (may increase performance)
21
+ # Unused by ffi-libfuse as we do not call fuse_loop_mt
22
+ # @return [Boolean]
23
+ ffi_attr_reader(:clone_fd) do |v|
24
+ v != 0
25
+ end
26
+
27
+ ffi_attr_writer(:clone_fd) do |v|
28
+ v ? 1 : 0
29
+ end
30
+
31
+ # @!attribute [rw] max_idle_threads
32
+ # The maximum number of available worker threads before they start to get deleted when they become idle. If not
33
+ # specified, the default is 10.
34
+ #
35
+ # Adjusting this has performance implications; a very small number of threads in the pool will cause a lot of
36
+ # thread creation and deletion overhead and performance may suffer. When set to 0, a new thread will be created
37
+ # to service every operation.
38
+ #
39
+ # @return [Integer] the maximum number of threads to leave idle
40
+ ffi_attr_accessor(:max_idle)
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,870 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'fuse_version'
4
+ require_relative '../ruby_object'
5
+ require_relative 'fuse_conn_info'
6
+ require_relative 'fuse_buffer'
7
+ require_relative 'fuse_context'
8
+ require_relative 'fuse_file_info'
9
+ require_relative 'fuse_poll_handle'
10
+ require_relative '../stat_vfs'
11
+ require_relative '../flock'
12
+ require_relative 'thread_pool'
13
+ require_relative '../stat'
14
+ require_relative '../struct_array'
15
+ require_relative 'fuse_callbacks'
16
+
17
+ module FFI
18
+ # Ruby FFI Binding for [libfuse](https://github.com/libfuse/libfuse)
19
+ module Libfuse
20
+ # typedef int (*fuse_fill_dir_t) (void *buf, const char *name, const struct stat *stbuf, off_t off);
21
+ fill_dir_t_args = [:pointer, :string, Stat.by_ref, :off_t]
22
+ if FUSE_MAJOR_VERSION > 2
23
+ enum :fuse_readdir_flags, [:fuse_readdir_plus, (1 << 0)]
24
+ enum :fuse_fill_dir_flags, [:fuse_fill_dir_plus, (1 << 1)]
25
+ fill_dir_t_args << :fuse_fill_dir_flags
26
+ end
27
+
28
+ bitmask :lock_op, [:lock_sh, 0, :lock_ex, 2, :lock_nb, 4, :lock_un, 8]
29
+ bitmask :falloc_mode, %i[keep_size punch_hole no_hide_stale collapse_range zero_range insert_range unshare_range]
30
+ bitmask :flags_mask, %i[nullpath_ok nopath utime_omit_ok] if FUSE_MAJOR_VERSION < 3
31
+ enum :xattr, [:xattr_create, 1, :xattr_replace]
32
+ callback :fill_dir_t, fill_dir_t_args, :int
33
+
34
+ # The file system operations as specified in libfuse.
35
+ #
36
+ # All Callback and Configuration methods are optional, but some are essential for a useful filesystem
37
+ # e.g. {getattr},{readdir}
38
+ #
39
+ # Almost all callback operations take a path which can be of any length and will return 0 for success, or raise a
40
+ # {::SystemCallError} on failure
41
+ #
42
+ class FuseOperations < FFI::Struct
43
+ include FuseCallbacks
44
+
45
+ # Container to dynamically build up the operations layout which is dependent on the loaded libfuse version
46
+ op = {}
47
+
48
+ # @!group FUSE Callbacks
49
+
50
+ # @!method getattr(path,stat,fuse_file_info = nil)
51
+ # @abstract
52
+ # Get file attributes.
53
+ #
54
+ # Similar to stat(). The 'st_dev' and 'st_blksize' fields are ignored. The 'st_ino' field is ignored
55
+ # except if the 'use_ino' mount option is given.
56
+ #
57
+ # @param [String] path
58
+ # @param [Stat] stat to be filled with result information
59
+ # @param [FuseFileInfo] fuse_file_info
60
+ # will always be nil if the file is not currently open, but may also be nil if the file is open.
61
+ # @return [Integer] 0 for success or -ve Errno value
62
+
63
+ # int (*getattr) (const char *, struct stat *);
64
+ op[:getattr] = [Stat.by_ref]
65
+ op[:getattr] << FuseFileInfo.by_ref if FUSE_MAJOR_VERSION >= 3
66
+
67
+ # @!method readlink(path, target_buffer, buffer_size)
68
+ # @abstract
69
+ # Resolve the target of a symbolic link
70
+ #
71
+ # @param [String] path
72
+ # @param [FFI::Pointer] target_buffer
73
+ #
74
+ # The buffer should be filled with a null terminated string. The buffer size argument includes the space for
75
+ # the terminating null character. If the linkname is too long to fit in the buffer, it should be truncated.
76
+ #
77
+ # @param [Integer] buffer_size
78
+ #
79
+ # @return [Integer] 0 for success.
80
+
81
+ # int (*readlink) (const char *, char *, size_t);
82
+ op[:readlink] = %i[pointer size_t]
83
+
84
+ # @!method getdir
85
+ # @deprecated use {readdir} instead
86
+
87
+ # int (*getdir) (const char *, fuse_dirh_t, fuse_dirfil_t);
88
+ op[:getdir] = %i[pointer pointer] if FUSE_MAJOR_VERSION < 3
89
+
90
+ # @!method mknod(path,mode,dev)
91
+ # Create a file node
92
+ #
93
+ # @param [String] path
94
+ # @param [Integer] mode
95
+ # @param [Integer] dev
96
+ # This is called for creation of all non-directory, non-symlink nodes. If the filesystem defines a create()
97
+ # method, then for regular files that will be called instead.
98
+ #
99
+ # @return [Integer] 0 for success or -ve errno
100
+
101
+ # int (*mknod) (const char *, mode_t, dev_t);
102
+ op[:mknod] = %i[mode_t dev_t]
103
+
104
+ # @!method mkdir(path,mode)
105
+ # @abstract
106
+ # Create a directory
107
+ #
108
+ # @param [String] path
109
+ # @param [Integer] mode
110
+ # Note that the mode argument may not have the type specification bits set, i.e. S_ISDIR(mode) can be false. To
111
+ # obtain the correct directory type bits use mode | {FFI::Stat::S_IFDIR}
112
+ #
113
+ # @return [Integer] 0 for success or -ve errno
114
+
115
+ # int (*mkdir) (const char *, mode_t);
116
+ op[:mkdir] = [:mode_t]
117
+
118
+ # @!method unlink(path)
119
+ # @abstract
120
+ # Remove a file
121
+ # @param [String] path
122
+ #
123
+ # @return [Integer] 0 for success or -ve errno
124
+
125
+ # int (*unlink) (const char *);
126
+ op[:unlink] = []
127
+
128
+ # @!method rmdir(path)
129
+ # @abstract
130
+ # Remove a directory
131
+ # @param [String] path
132
+ #
133
+ # @return [Integer] 0 for success or -ve errno
134
+
135
+ # int (*rmdir) (const char *);
136
+ op[:rmdir] = []
137
+
138
+ # @!method symlink(path,target)
139
+ # @abstract
140
+ # Create a symbolic link
141
+ # @param [String] path
142
+ # @param [String] target the link target
143
+ #
144
+ # @return [Integer] 0 for success or -ve errno
145
+
146
+ # int (*symlink) (const char *, const char *);
147
+ op[:symlink] = [:string]
148
+
149
+ # @!method rename(from_path,to_path)
150
+ # @abstract
151
+ # Rename a file
152
+ # @param [String] from_path
153
+ # @param [String] to_path
154
+ #
155
+ # @return [Integer] 0 for success or -ve errno
156
+
157
+ # int (*rename) (const char *, const char *);
158
+ op[:rename] = [:string]
159
+
160
+ # @!method link(path,target)
161
+ # @abstract
162
+ # Create a hard link to a file
163
+ # @param [String] path
164
+ # @param [String] target
165
+ #
166
+ # @return [Integer] 0 for success or -ve errno
167
+
168
+ # int (*link) (const char *, const char *);
169
+ op[:link] = [:string]
170
+
171
+ # @!method chmod(path,mode,fuse_file_info=nil)
172
+ # @abstract
173
+ # Change the permission bits of a file
174
+ # @param [String] path
175
+ # @param [Integer] mode
176
+ # @param [FuseFileInfo] fuse_file_info (Fuse3 only)
177
+ #
178
+ # @return [Integer] 0 for success or -ve errno
179
+
180
+ # int (*chmod) (const char *, mode_t);
181
+ op[:chmod] = [:mode_t]
182
+ op[:chmod] << FuseFileInfo.by_ref if FUSE_MAJOR_VERSION >= 3
183
+
184
+ # @!method chown(path,uid,gid,fuse_file_info=nil)
185
+ # @abstract
186
+ # Change the owner and group of a file
187
+ # @param [String] path
188
+ # @param [Integer] uid
189
+ # @param [Integer] gid
190
+ # @param [FuseFileInfo] fuse_file_info (Fuse3 only)
191
+ #
192
+ # @return [Integer] 0 for success or -ve errno
193
+
194
+ # int (*chown) (const char *, uid_t, gid_t);
195
+ op[:chown] = %i[uid_t gid_t]
196
+ op[:chown] << FuseFileInfo.by_ref if FUSE_MAJOR_VERSION >= 3
197
+
198
+ # @!method truncate(path,offset,fuse_file_info=nil)
199
+ # @abstract
200
+ # Change the size of a file
201
+ # @param [String] path
202
+ # @param [Integer] offset
203
+ # @param [FuseFileInfo] fuse_file_info (Fuse3 only)
204
+ #
205
+ # @return [Integer] 0 for success or -ve errno
206
+
207
+ # int (*truncate) (const char *, off_t);
208
+ op[:truncate] = [:off_t]
209
+ op[:truncate] << FuseFileInfo.by_ref if FUSE_MAJOR_VERSION >= 3
210
+
211
+ # int (*utime) (const char *, struct utimbuf *);
212
+ op[:utime] = [:pointer] if FUSE_MAJOR_VERSION < 3
213
+
214
+ # @!method open(path,fuse_file_info)
215
+ # @abstract
216
+ # File open operation
217
+ #
218
+ # No creation (O_CREAT, O_EXCL) and by default also no truncation (O_TRUNC) flags will be passed to open(). If an
219
+ # application specifies O_TRUNC, fuse first calls truncate() and then open(). Only if 'atomic_o_trunc' has been
220
+ # specified and kernel version is 2.6.24 or later, O_TRUNC is passed on to open.
221
+ #
222
+ # Unless the 'default_permissions' mount option is given, open should check if the operation is permitted for the
223
+ # given flags.
224
+ #
225
+ # Optionally open may also return an arbitrary filehandle in the fuse_file_info structure, which
226
+ # will be passed to all file operations.
227
+ #
228
+ # @param [String] path
229
+ # @param [FuseFileInfo] fuse_file_info
230
+ #
231
+ # @return [Integer] 0 for success or -ve errno
232
+
233
+ # int (*open) (const char *, struct fuse_file_info *);
234
+ op[:open] = [FuseFileInfo.by_ref]
235
+
236
+ # @!method read(path,buf,size,offset,fuse_file_info)
237
+ # @abstract
238
+ # Read data from an open file
239
+ # @param [String] path
240
+ # @param [FFI::Pointer] buf
241
+ # @param [Integer] size
242
+ # @param [Integer] offset
243
+ # @param [FuseFileInfo] fuse_file_info
244
+ #
245
+ # @return [Integer]
246
+ # Read should return exactly the number of bytes requested except on EOF or error, otherwise the rest of the
247
+ # data will be substituted with zeroes. An exception to this is when the 'direct_io' mount option is
248
+ # specified, in which case the return value of the read system call will reflect the return value of this
249
+ # operation.
250
+
251
+ # int (*read) (const char *, char *, size_t, off_t, struct fuse_file_info *);
252
+ op[:read] = [:pointer, :size_t, :off_t, FuseFileInfo.by_ref]
253
+
254
+ # @!method write(path, data, size,offset,fuse_file_info)
255
+ # @abstract
256
+ # Write data to an open file
257
+ #
258
+ # @param [String] path
259
+ # @param [FFI::Pointer] data
260
+ # @param [Integer] size
261
+ # @param [Integer] offset
262
+ # @param [FuseFileInfo] fuse_file_info
263
+ #
264
+ # @return [Integer]
265
+ # Write should return exactly the number of bytes requested except on error. An exception to this is when the
266
+ # 'direct_io' mount option is specified (see read operation).
267
+
268
+ # int (*write) (const char *, const char *, size_t, off_t, struct fuse_file_info *);
269
+ op[:write] = [:pointer, :size_t, :off_t, FuseFileInfo.by_ref]
270
+
271
+ # @!method statfs(path,statvfs)
272
+ # @abstract
273
+ # Get file system statistics
274
+ #
275
+ # @param [String] path
276
+ # @param [StatVfs] statvfs result struct
277
+ # Note 'frsize', 'favail', 'fsid' and 'flag' fields are ignored
278
+ #
279
+ # @return [Integer] 0 for success or -ve errno
280
+
281
+ # int (*statfs) (const char *, struct statvfs *);
282
+ op[:statfs] = [StatVfs.by_ref]
283
+
284
+ # @!method flush(path,fuse_file_info)
285
+ # Possibly flush cached data
286
+ #
287
+ # BIG NOTE: This is not equivalent to fsync(). It's not a request to sync dirty data.
288
+ #
289
+ # Flush is called on each close() of a file descriptor. So if a filesystem wants to return write errors in
290
+ # close() and the file has cached dirty data, this is a good place to write back data and return any errors.
291
+ # Since many applications ignore close() errors this is not always useful.
292
+ #
293
+ # NOTE: The flush() method may be called more than once for each open(). This happens if more than one file
294
+ # descriptor refers to an opened file due to dup(), dup2() or fork() calls. It is not possible to determine if a
295
+ # flush is final, so each flush should be treated equally. Multiple write-flush sequences are relatively rare,
296
+ # so this shouldn't be a problem.
297
+ #
298
+ # Filesystems shouldn't assume that flush will always be called after some writes, or that if will be called at
299
+ # all.
300
+ #
301
+ # @param [String] path
302
+ # @param [FuseFileInfo] fuse_file_info
303
+ #
304
+ # @return [Integer] 0 for success or -ve errno
305
+
306
+ # int (*flush) (const char *, struct fuse_file_info *);
307
+ op[:flush] = [FuseFileInfo.by_ref]
308
+
309
+ # @!method release(path,fuse_file_info)
310
+ # Release an open file
311
+ #
312
+ # Release is called when there are no more references to an open file: all file descriptors are closed and all
313
+ # memory mappings are unmapped.
314
+ #
315
+ # For every open() call there will be exactly one release() call with the same flags and file descriptor. It is
316
+ # possible to have a file opened more than once, in which case only the last release will mean, that no more
317
+ # reads \/ writes will happen on the file.
318
+ #
319
+ # @param [String] path
320
+ # @param [FuseFileInfo] fuse_file_info
321
+ #
322
+ # @return [Integer] The return value of release is ignored.
323
+
324
+ # int (*release) (const char *, struct fuse_file_info *);
325
+ op[:release] = [FuseFileInfo.by_ref]
326
+
327
+ # @!method fsync(path,datasync,fuse_file_info)
328
+ # Synchronize file contents
329
+ #
330
+ # @param [String] path
331
+ # @param [Integer] datasync If non-zero, then only the user data should be flushed, not the meta data.
332
+ # @param [FuseFileInfo] fuse_file_info
333
+ #
334
+ # @return [Integer] 0 for success or -ve errno
335
+
336
+ # int (*fsync) (const char *, int, struct fuse_file_info *);
337
+ op[:fsync] = [:int, FuseFileInfo.by_ref]
338
+
339
+ # @!method setxattr(path,name,data,size,flags)
340
+ # @abstract
341
+ # Set extended attributes
342
+ # @param [String] path
343
+ # @param [String] name
344
+ # @param [String] data
345
+ # @param [Integer] size
346
+ # @param [Symbol|Integer] flags (:xattr_create or :xattr_replace)
347
+ # @return [Integer] 0 for success or -ve errno
348
+ # @see setxattr(2)
349
+
350
+ # int (*setxattr) (const char *, const char *, const char *, size_t, int);
351
+ op[:setxattr] = %i[string string size_t xattr]
352
+
353
+ # @!method getxattr(path,name,buf,size)
354
+ # @abstract
355
+ # Get extended attributes
356
+ # @param [String] path
357
+ # @param [String] name
358
+ # @param [FFI::Pointer] buf
359
+ # @param [Integer] size
360
+ # @return [Integer]
361
+ # @see getxattr(2)
362
+
363
+ # int (*getxattr) (const char *, const char *, char *, size_t);
364
+ op[:getxattr] = %i[string pointer size_t]
365
+
366
+ # @!method listxattr(path,buf,size)
367
+ # @abstract
368
+ # List extended attributes
369
+ # @param [String] path
370
+ # @param [FFI::Pointer] buf
371
+ # @param [Integer] size
372
+ # @return [Integer]
373
+ # @see listxattr(2)
374
+
375
+ # int (*listxattr) (const char *, char *, size_t);
376
+ op[:listxattr] = %i[pointer size_t]
377
+
378
+ # @!method removexattr(path,name)
379
+ # @abstract
380
+ # Remove extended attributes
381
+ # @param [String] path
382
+ # @param [String] name
383
+ # @return [Integer] 0 on success or -ve errno
384
+ # @see removexattr(2)
385
+
386
+ # int (*removexattr) (const char *, const char *);
387
+ op[:removexattr] = [:string]
388
+
389
+ if FUSE_VERSION >= 23
390
+
391
+ # @!method opendir(path,fuse_file_info)
392
+ # @abstract
393
+ # Open directory
394
+ #
395
+ # Unless the 'default_permissions' mount option is given, this method should check if opendir is permitted for
396
+ # this directory. Optionally opendir may also return an arbitrary filehandle in the fuse_file_info structure,
397
+ # which will be passed to readdir, releasedir and fsyncdir.
398
+ #
399
+ # @param [String] path
400
+ # @param [FuseFileInfo] fuse_file_info
401
+ # @return [Integer] 0 for success or -ve errno
402
+
403
+ # int (*opendir) (const char *, struct fuse_file_info *);
404
+ op[:opendir] = [FuseFileInfo.by_ref]
405
+
406
+ # @!method readdir(path,buffer,filler,offset,fuse_file_info, fuse_readdir_flag = 0)
407
+ # @abstract
408
+ # Read directory
409
+ #
410
+ # The filesystem may choose between two modes of operation:
411
+ #
412
+ # 1) The readdir implementation ignores the offset parameter, and passes zero to the filler function's offset.
413
+ # The filler function will not return '1' (unless an error happens), so the whole directory is read in a single
414
+ # readdir operation.
415
+ #
416
+ # 2) The readdir implementation keeps track of the offsets of the directory entries. It uses the offset
417
+ # parameter and always passes non-zero offset to the filler function. When the buffer is full (or an error
418
+ # happens) the filler function will return '1'.
419
+ #
420
+ # @param [String] path
421
+ # @param [FFI::Pointer] buffer
422
+ # @param [Proc<FFI::Pointer,String,Stat,Integer,Integer=0>] filler the filler function to be called
423
+ # for each directory entry, ie filler.call(buffer,next_name,next_stat,next_offset, fuse_fill_dir_flag)
424
+ #
425
+ # @param [Integer] offset the starting offset
426
+ # @param [FuseFileInfo] fuse_file_info
427
+ # @param [Symbol] fuse_readdir_flag (Fuse3 only)
428
+ #
429
+ # @return [Integer] 0 on success or -ve errno
430
+ #
431
+
432
+ # int (*readdir) (const char *, void *buf, fuse_fill_dir_t, off_t, struct fuse_file_info *);
433
+ op[:readdir] = [:pointer, :fill_dir_t, :off_t, FuseFileInfo.by_ref]
434
+ op[:readdir] << :fuse_readdir_flags if FUSE_MAJOR_VERSION > 2
435
+
436
+ # @!method releasedir(path,fuse_file_info)
437
+ # Release directory
438
+ # @param [String] path
439
+ # @param [FuseFileInfo] fuse_file_info
440
+ # @return [Integer] 0 for success or -ve errno
441
+
442
+ # int (*releasedir) (const char *, struct fuse_file_info *);
443
+ op[:releasedir] = [FuseFileInfo.by_ref]
444
+
445
+ # @!method fsyncdir(path,datasync,fuse_file_info)
446
+ # Synchronize directory contents
447
+ #
448
+ # @param [String] path
449
+ # @param [Integer] datasync If non-zero, then only the user data should be flushed, not the meta data
450
+ # @param [FuseFileInfo] fuse_file_info
451
+ # @return [Integer] 0 for success or -ve errno
452
+
453
+ # int (*fsyncdir) (const char *, int, struct fuse_file_info *);
454
+ op[:fsyncdir] = [:int, FuseFileInfo.by_ref]
455
+
456
+ # @!method init(fuse_conn_info,fuse_config=nil)
457
+ # @abstract
458
+ # Initialize filesystem
459
+ #
460
+ # @param [FuseConnInfo] fuse_conn_info
461
+ # @param [FuseConfig|nil] fuse_config only provided for fuse3 and later
462
+ #
463
+ # @return [Object]
464
+ # The return value will passed in the private_data field of fuse_context to all file operations and as a
465
+ # parameter to the destroy() method.
466
+ #
467
+
468
+ # fuse2: void *(*init) (struct fuse_conn_info *conn);
469
+ # fuse3: void *(*init) (struct fuse_conn_info *conn, struct fuse_config *cfg);
470
+ op[:init] =
471
+ if FUSE_MAJOR_VERSION >= 3
472
+ require_relative 'fuse_config'
473
+ callback([FuseConnInfo.by_ref, FuseConfig.by_ref], RubyObject)
474
+ else
475
+ callback([FuseConnInfo.by_ref], RubyObject)
476
+ end
477
+
478
+ # @!method destroy(obj)
479
+ # @abstract
480
+ # @param [Object] obj - the object passed from {init}.
481
+ # Clean up filesystem. Called on filesystem exit.
482
+
483
+ # void (*destroy) (void *);
484
+ op[:destroy] = callback([RubyObject], :void)
485
+ end
486
+
487
+ if FUSE_VERSION >= 25
488
+
489
+ # @!method access(path,mode)
490
+ # @abstract
491
+ # Check file access permissions
492
+ #
493
+ # This will be called for the access() system call. If the 'default_permissions' mount option is given, this
494
+ # method is not called.
495
+ #
496
+ # This method is not called under Linux kernel versions 2.4.x
497
+ #
498
+ # @param [String] path
499
+ # @param [Integer] mode
500
+ # @return [Integer] 0 for success or -ve errno
501
+ # @see access(2)
502
+
503
+ # int (*access) (const char *, int);
504
+ op[:access] = [:int]
505
+
506
+ # @!method create(path,mode,fuse_file_info)
507
+ # @abstract
508
+ # Create and open a file
509
+ #
510
+ # If the file does not exist, first create it with the specified mode, and then open it.
511
+ #
512
+ # If this method is not implemented or under Linux kernel versions earlier than 2.6.15, the mknod() and open()
513
+ # methods will be called instead.
514
+ #
515
+ # @param [String] path
516
+ # @param [Integer] mode
517
+ # @param [FuseFileInfo] fuse_file_info
518
+ # @return [Integer] 0 for success or -ve errno
519
+
520
+ # int (*create) (const char *, mode_t, struct fuse_file_info *);
521
+ op[:create] = [:mode_t, FuseFileInfo.by_ref]
522
+
523
+ # @!method ftruncate(path,offset,fuse_file_info)
524
+ # @deprecated in Fuse3 implement {truncate} instead
525
+ # @abstract
526
+ # Change the size of an open file
527
+ #
528
+ # This method is called instead of the truncate() method if the truncation was invoked from an ftruncate()
529
+ # system call.
530
+ #
531
+ # If this method is not implemented or under Linux kernel versions earlier than 2.6.15, the truncate() method
532
+ # will be called instead.
533
+ #
534
+ # @param [String] path
535
+ # @param [Integer] offset
536
+ # @param [FuseFileInfo] fuse_file_info
537
+ # @return [Integer] 0 for success or -ve errno
538
+
539
+ # int (*ftruncate) (const char *, off_t, struct fuse_file_info *);
540
+ op[:ftruncate] = [:off_t, FuseFileInfo.by_ref] if FUSE_MAJOR_VERSION < 3
541
+
542
+ # @!method fgetattr(path,stat,fuse_file_info)
543
+ # @deprecated in Fuse3 implement {getattr} instead
544
+ # Get attributes from an open file
545
+ #
546
+ # This method is called instead of the getattr() method if the file information is available.
547
+ #
548
+ # Currently this is only called after the create() method if that is implemented (see above). Later it may be
549
+ # called for invocations of fstat() too.
550
+ # @param [String] path
551
+ # @param [Stat] stat
552
+ # @param [FuseFileInfo] fuse_file_info
553
+ # @return [Integer] 0 for success or -ve errno
554
+
555
+ # int (*fgetattr) (const char *, struct stat *, struct fuse_file_info *);
556
+ op[:fgetattr] = [Stat, FuseFileInfo.by_ref] if FUSE_MAJOR_VERSION < 3
557
+ end
558
+ if FUSE_VERSION >= 26
559
+ # @!method lock(path,fuse_file_info,cmd,flock)
560
+ # @abstract
561
+ #
562
+ # Perform POSIX file locking operation
563
+ # @param [String] path
564
+ # @param [FuseFileInfo] fuse_file_info
565
+ # For checking lock ownership, the 'fuse_file_info->owner' argument must be used.
566
+ # @param [Symbol] cmd either :f_getlck, :f_setlck or :f_setlkw.
567
+ # @param [Flock] flock
568
+ # For the meaning of fields in 'struct flock' see the man page for fcntl(2). The whence field will always
569
+ # be set to :seek_set.
570
+ #
571
+ # @return [Integer] 0 for success or -ve errno
572
+ #
573
+ # For :f_getlk operation, the library will first check currently held locks, and if a conflicting lock is found
574
+ # it will return information without calling this method. This ensures, that for local locks the pid field
575
+ # is correctly filled in. The results may not be accurate in case of race conditions and in the presence of
576
+ # hard links, but its unlikely that an application would rely on accurate GETLK results in these cases. If a
577
+ # conflicting lock is not found, this method will be called, and the filesystem may fill out l_pid by a
578
+ # meaningful value, or it may leave this field zero.
579
+ #
580
+ # For :f_setlk and :f_setlkw the pid field will be set to the pid of the process performing the locking
581
+ # operation.
582
+ #
583
+ # @note if this method is not implemented, the kernel will still allow file locking to work locally. Hence it
584
+ # is only interesting for network filesystem and similar.
585
+
586
+ # int (*lock) (const char *, struct fuse_file_info *, int cmd, struct flock *);
587
+ op[:lock] = [FuseFileInfo.by_ref, Flock::Enums::LockCmd, Flock.by_ref]
588
+
589
+ # @!method utimens(path,time_specs,fuse_file_info=nil)
590
+ # @abstract
591
+ # Change the access and modification times of a file with nanosecond resolution
592
+ #
593
+ # This supersedes the old utime() interface. New applications should use this.
594
+ #
595
+ # @param [String] path
596
+ # @param [Array<Stat::TimeSpec>] time_specs atime,mtime
597
+ # @param [FuseFileInfo] fuse_file_info (only since Fuse3)
598
+ # @return [Integer] 0 for success or -ve errno
599
+ #
600
+ # @see utimensat(2)
601
+ #
602
+
603
+ # int (*utimens) (const char *, const struct timespec tv[2]);
604
+ op[:utimens] = [FFI::Stat::TimeSpec.array(2)]
605
+ op[:utimens] << FuseFileInfo.by_ref if FUSE_MAJOR_VERSION >= 3
606
+
607
+ # @!method bmap(path,blocksize,index)
608
+ # @abstract
609
+ # Map block index within file to block index within device
610
+ #
611
+ # @param [String] path
612
+ # @param [Integer] blocksize
613
+ # @param [FFI::Pointer] index pointer to index result
614
+ # @return [Integer] 0 success or -ve errno
615
+ # @note This makes sense only for block device backed filesystem mounted with the 'blkdev' option
616
+
617
+ # int (*bmap) (const char *, size_t blocksize, uint64_t *idx);
618
+ op[:bmap] = %i[size_t pointer]
619
+
620
+ end
621
+
622
+ # @!method fuse_flags
623
+ # @abstract
624
+ # Configuration method to set fuse flags
625
+ #
626
+ # - :nullpath_ok
627
+ #
628
+ # Flag indicating that the filesystem can accept a NULL path as the first argument for the following
629
+ # operations: read, write, flush, release, fsync, readdir, releasedir, fsyncdir, ftruncate, fgetattr, lock,
630
+ # ioctl and poll
631
+ #
632
+ # If this flag is set these operations continue to work on unlinked files even if "-ohard_remove" option was
633
+ # specified.
634
+ #
635
+ # - :nopath
636
+ #
637
+ # Flag indicating that the path need not be calculated for the following operations: read, write, flush,
638
+ # release, fsync, readdir, releasedir, fsyncdir, ftruncate, fgetattr, lock, ioctl and poll
639
+ #
640
+ # Closely related to flag_nullpath_ok, but if this flag is set then the path will not be calculaged even if
641
+ # the file wasnt unlinked. However the path can still be non-NULL if it needs to be calculated for some other
642
+ # reason.
643
+ #
644
+ # - :utime_omit_ok
645
+ #
646
+ # Flag indicating that the filesystem accepts special UTIME_NOW and UTIME_OMIT values in its utimens
647
+ # operation.
648
+ #
649
+ # @return [Array[]Symbol>] a list of flags to set capabilities
650
+ # @note Not available in Fuse3
651
+ # @deprecated in Fuse3 use fuse_config object in {init}
652
+ op[:flags] = :flags_mask if FUSE_MAJOR_VERSION < 3
653
+
654
+ if FUSE_VERSION >= 28
655
+
656
+ # @!method ioctl(path,cmd,arg,fuse_file_info, flags,data)
657
+ # @abstract
658
+ # Ioctl
659
+ # @param [String] path
660
+ # @param [Integer] cmd
661
+ # @param [FFI::Pointer] arg
662
+ # @param [FuseFileInfo] fuse_file_info
663
+ # @param [Integer] flags
664
+ # @param [FFI::Pointer] data
665
+ #
666
+ # flags will have FUSE_IOCTL_COMPAT set for 32bit ioctls in 64bit environment. The size and direction of data
667
+ # is determined by _IOC_*() decoding of cmd. For _IOC_NONE, data will be NULL, for _IOC_WRITE data is out
668
+ # area, for _IOC_READ in area and if both are set in/out area. In all non-NULL cases, the area is of
669
+ # _IOC_SIZE(cmd) bytes.
670
+ #
671
+ # If flags has FUSE_IOCTL_DIR then the fuse_file_info refers to a directory file handle.
672
+
673
+ # int (*ioctl) (const char *, int cmd, void *arg, struct fuse_file_info *, unsigned int flags, void *data);
674
+ op[:ioctl] = [:int, :pointer, FuseFileInfo.by_ref, :uint, :pointer]
675
+
676
+ # @!method poll(path,fuse_file_info,ph,reventsp)
677
+ # @abstract
678
+ # Poll for IO readiness events
679
+ #
680
+ # @param [String] path
681
+ # @param [FuseFileInfo] fuse_file_info
682
+ # @param [FusePollHandle|nil] ph
683
+ # If ph is set, the client should notify when IO readiness events occur by calling
684
+ # {FusePollHandle#notify_poll} (possibly asynchronously)
685
+ #
686
+ # Regardless of the number of times poll is received, single notification is enough to clear
687
+ # all. Notifying more times incurs overhead but doesnt harm correctness.
688
+ #
689
+ # @param [FFI::Pointer] reventsp return events
690
+ # @return [Integer] 0 for success, -ve for error
691
+ # @see poll(2)
692
+
693
+ # int (*poll) (const char *, struct fuse_file_info *, struct fuse_pollhandle *ph, unsigned *reventsp);
694
+ op[:poll] = [FuseFileInfo.by_ref, FusePollHandle, :pointer]
695
+
696
+ # @!method write_buf(path,buf,offset,fuse_file_info)
697
+ # @abstract
698
+ # Write contents of buffer to an open file
699
+ #
700
+ # Similar to the write() method, but data is supplied in a generic buffer. Use fuse_buf_copy() to transfer
701
+ # data to the destination.
702
+ #
703
+ # @param [String] path
704
+ # @param [FuseBufVec] buf
705
+ # @param [Integer] offset
706
+ # @param [FuseFileInfo] fuse_file_info
707
+ #
708
+ # @return [Integer] the number of bytes written or -ve errno
709
+
710
+ # int (*write_buf) (const char *, struct fuse_bufvec *buf, off_t off, struct fuse_file_info *);
711
+ op[:write_buf] = [FuseBufVec.by_ref, :off_t, FuseFileInfo.by_ref]
712
+
713
+ # @!method read_buf(path,bufp,size,offset,fuse_file_info)
714
+ # @abstract
715
+ #
716
+ # Similar to the read() method, but data is stored and returned in a generic buffer.
717
+ #
718
+ # No actual copying of data has to take place, the source file descriptor may simply be stored in the buffer
719
+ # for later data transfer.
720
+ #
721
+ # @param [String] path
722
+ # @param [FFI::Pointer<FuseBufVec>] bufp
723
+ # The buffer must be allocated dynamically and stored at the location pointed to by bufp. If the buffer
724
+ # contains memory regions, they too must be allocated using malloc(). The allocated memory will be freed by
725
+ # the caller.
726
+ # @param [Integer] size
727
+ # @param [Integer] offset
728
+ # @param [FuseFileInfo] fuse_file_info
729
+ # @return 0 success or -ve errno
730
+
731
+ # int (*read_buf) (const char *, struct fuse_bufvec **bufp, size_t size, off_t off, struct fuse_file_info *);
732
+ op[:read_buf] = [:pointer, :size_t, :off_t, FuseFileInfo.by_ref]
733
+
734
+ # @!method flock(path,fuse_file_info,op)
735
+ # @abstract
736
+ # Perform BSD file locking operation
737
+ #
738
+ # @param [String] path
739
+ # @param [FuseFileInfo] fuse_file_info
740
+ # Additionally fi->owner will be set to a value unique to this open file. This same value will be supplied
741
+ # to {release} when the file is released.
742
+ # @param [Array<Symbol>] op the lock operation
743
+ # The op argument will contain one of :lock_sh, :lock_ex, or :lock_un
744
+ # Nonblocking requests also include :lock_nb
745
+ # @return 0 or -ve errno
746
+ # @see flock(2)
747
+ #
748
+ # @note: if this method is not implemented, the kernel will still allow file locking to work locally. Hence it
749
+ # is only interesting for network filesystem and similar.
750
+
751
+ # int (*flock) (const char *, struct fuse_file_info *, int op);
752
+ op[:flock] = [FuseFileInfo.by_ref, :lock_op]
753
+
754
+ # @!method fallocate(path,mode,offset,len,fuse_file_info)
755
+ # @abstract
756
+ # Allocates space for an open file
757
+ #
758
+ # This function ensures that required space is allocated for specified file. If this function returns success
759
+ # then any subsequent write request to specified range is guaranteed not to fail because of lack of space on
760
+ # the file system media.
761
+ #
762
+ # @param [String] path
763
+ # @param [Array<Symbol>] mode allocation mode flags
764
+ # :keep_size :punch_hole :no_hide_stale :collapse_range :zero_range :insert_range :unshare_range
765
+ # see linux/falloc.h
766
+ # @param [Integer] offset
767
+ # @param [Integer] len
768
+ # @return 0 or -ve errno
769
+ #
770
+ # @see fallocate(2)
771
+
772
+ # Introduced in version 2.9.1 - fuse_version does not contain patch level.
773
+ # this will generate a warning on 2.9.0 that the struct is bigger than expected
774
+ # and thus some operations (ie falllocate) may be unsupported.
775
+ #
776
+ # int (*fallocate) (const char *, int, off_t, off_t, struct fuse_file_info *);
777
+ op[:fallocate] = [:falloc_mode, :off_t, :off_t, FuseFileInfo.by_ref]
778
+
779
+ if FUSE_MAJOR_VERSION >= 3
780
+
781
+ # @!method copy_file_range(path_in,fi_in, offset_in, path_out, fi_out, offset_out, size, flags)
782
+ # @abstract
783
+ # Copy a range of data from one file to another
784
+ #
785
+ # Performs an optimized copy between two file descriptors without the additional cost of transferring data
786
+ # through the FUSE kernel module to user space (glibc) and then back into the FUSE filesystem again.
787
+ #
788
+ # In case this method is not implemented, glibc falls back to reading data from the source and writing to the
789
+ # destination. Effectively doing an inefficient copy of the data.
790
+ #
791
+ # @param [String] path_in
792
+ # @param [FuseFileInfo] fi_in
793
+ # @param [Integer] offset_in
794
+ # @param [String] path_out
795
+ # @param [FuseFileInfo] fi_out
796
+ # @param [Integer] offset_out
797
+ # @param [Integer] size
798
+ # @param [Array<Symbol>] flags (unused)
799
+ # @return [Integer] copied size or -ve errno
800
+
801
+ # ssize_t (*copy_file_range) (
802
+ # const char *path_in, struct fuse_file_info *fi_in, off_t offset_in, const char *path_out,
803
+ # struct fuse_file_info *fi_out, off_t offset_out, size_t size, int flags
804
+ # );
805
+ op[:copy_file_range] =
806
+ callback [
807
+ :string, FuseFileInfo.by_ref, :off_t,
808
+ :string, FuseFileInfo.by_ref, :off_t,
809
+ :size_t, :int
810
+ ], :ssize_t
811
+
812
+ # @!method lseek(path,offset,whence,fuse_file_info)
813
+ # @abstract
814
+ # Find next data or hole after the specified offset
815
+ # @param [String] path
816
+ # @param [Integer] offset
817
+ # @param [Symbol] whence
818
+ # either :seek_set ,:seek_cur, :seek_end, :seek_data, :seek_hole
819
+ # @return [Integer] the found offset in bytes from the beginning of the file or -ve errno
820
+ # @see lseek(2)
821
+
822
+ # off_t (*lseek) (const char *, off_t off, int whence, struct fuse_file_info *);
823
+ op[:lseek] = callback [:string, :off_t, Flock::Enums::SeekWhence, FuseFileInfo.by_ref], :off_t
824
+ end
825
+ end
826
+
827
+ # @!endgroup
828
+
829
+ layout_data = op.transform_values do |v|
830
+ if v.is_a?(Array) && !v.last.is_a?(Integer)
831
+ # A typical fuse callback
832
+ callback([:string] + v, :int)
833
+ else
834
+ v
835
+ end
836
+ end
837
+
838
+ layout layout_data
839
+
840
+ # @overload initialize(fuse_wrappers: [], fuse_flags: [], delegate: self)
841
+ # Build a FuseOperations struct and register callback methods
842
+ #
843
+ # The methods to register are identified by delegate.{fuse_respond_to?} if available
844
+ # otherwise delegate.{::respond_to?} is used.
845
+ #
846
+ # @param [Object] delegate
847
+ # delegate object that quacks like our abstract methods (after any wrappers)
848
+ #
849
+ # if not provided defaults to self, ie a subclass of FuseOperations that implements the otherwise abstract
850
+ # callback and configuration methods, or a naked FuseOperations that must be externally configured.
851
+ #
852
+ # @param [Array] fuse_wrappers
853
+ # A list of fuse_wrappers (see {register}). Passed into delegate.{fuse_wrappers} if available
854
+ #
855
+ # @param [Array<Symbol>] fuse_flags list of configuration flags (Not used in Fuse3)
856
+ # concatenated #fuse_flags if available
857
+ def initialize(*args, fuse_wrappers: [], fuse_flags: [], delegate: self)
858
+ super(*args) # FFI::Struct constructor
859
+ return if args.any? # only configure if this is a new allocation
860
+
861
+ initialize_callbacks(wrappers: fuse_wrappers, delegate: delegate)
862
+
863
+ return unless FUSE_MAJOR_VERSION < 3
864
+
865
+ fuse_flags.concat(delegate.fuse_flags) if delegate.respond_to?(:fuse_flags)
866
+ send(:[]=, :flags, fuse_flags.uniq)
867
+ end
868
+ end
869
+ end
870
+ end