ffi-libfuse 0.0.1.pre

Sign up to get free protection for your applications and to get access to all the features.
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