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,205 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../accessors'
4
+ module FFI
5
+ module Libfuse
6
+ #
7
+ # Configuration of the high-level API
8
+ #
9
+ # This structure is initialized from the arguments passed to fuse_new(), and then passed to the file system's init()
10
+ # handler which should ensure that the configuration is compatible with the file system implementation.
11
+ #
12
+ class FuseConfig < FFI::Struct
13
+ include FFI::Accessors
14
+ layout(
15
+ {
16
+ # @!method set_gid?
17
+ # @return [Boolean]
18
+ # @!attribute [r] gid
19
+ # @return [Integer]
20
+ # If `set_gid?` is true the st_gid attribute of each file is overwritten with the value of `gid`.
21
+ #
22
+ set_gid: :int,
23
+ gid: :uint,
24
+
25
+ # @!method set_uid?
26
+ # @return [Boolean]
27
+ # @!attribute [r] uid
28
+ # @return [Integer]
29
+ # If `set_uid?' is true the st_uid attribute of each file is overwritten with the value of `uid`.
30
+ #
31
+ set_uid: :int,
32
+ uid: :uint,
33
+
34
+ # @!method set_mode?
35
+ # @return [Boolean]
36
+ # @!attribute [r] mode
37
+ # @return [Integer]
38
+ # If `set_mode?` is true, the any permissions bits set in `umask` are unset in the st_mode attribute of each
39
+ # file.
40
+ #
41
+ set_mode: :int,
42
+ umask: :uint,
43
+ #
44
+ # The timeout in seconds for which name lookups will be
45
+ # cached.
46
+ #
47
+ entry_timeout: :double,
48
+ #
49
+ # The timeout in seconds for which a negative lookup will be
50
+ # cached. This means, that if file did not exist (lookup
51
+ # retuned ENOENT), the lookup will only be redone after the
52
+ # timeout, and the file/directory will be assumed to not
53
+ # exist until then. A value of zero means that negative
54
+ # lookups are not cached.
55
+ #
56
+ negative_timeout: :double,
57
+ #
58
+ # The timeout in seconds for which file/directory attributes
59
+ # (as returned by e.g. the `getattr` handler) are cached.
60
+ #
61
+ attr_timeout: :double,
62
+ #
63
+ # Allow requests to be interrupted
64
+ #
65
+ intr: :int,
66
+ #
67
+ # Specify which signal number to send to the filesystem when
68
+ # a request is interrupted. The default is hardcoded to
69
+ # USR1.
70
+ #
71
+ intr_signal: :int,
72
+ #
73
+ # Normally, FUSE assigns inodes to paths only for as long as
74
+ # the kernel is aware of them. With this option inodes are
75
+ # instead remembered for at least this many seconds. This
76
+ # will require more memory, but may be necessary when using
77
+ # applications that make use of inode numbers.
78
+ #
79
+ # A number of -1 means that inodes will be remembered for the
80
+ # entire life-time of the file-system process.
81
+ #
82
+ remember: :int,
83
+ #
84
+ # The default behavior is that if an open file is deleted,
85
+ # the file is renamed to a hidden file (.fuse_hiddenXXX), and
86
+ # only removed when the file is finally released. This
87
+ # relieves the filesystem implementation of having to deal
88
+ # with this problem. This option disables the hiding
89
+ # behavior, and files are removed immediately in an unlink
90
+ # operation (or in a rename operation which overwrites an
91
+ # existing file).
92
+ #
93
+ # It is recommended that you not use the hard_remove
94
+ # option. When hard_remove is set, the following libc
95
+ # functions fail on unlinked files (returning errno of
96
+ # ENOENT): read(2), write(2), fsync(2), close(2), f*xattr(2),
97
+ # ftruncate(2), fstat(2), fchmod(2), fchown(2)
98
+ #
99
+ hard_remove: :int,
100
+ #
101
+ # Honor the st_ino field in the functions getattr() and
102
+ # fill_dir(). This value is used to fill in the st_ino field
103
+ # in the stat(2), lstat(2), fstat(2) functions and the d_ino
104
+ # field in the readdir(2) function. The filesystem does not
105
+ # have to guarantee uniqueness, however some applications
106
+ # rely on this value being unique for the whole filesystem.
107
+ #
108
+ # Note that this does *not* affect the inode that libfuse
109
+ # and the kernel use internally (also called the "nodeid").
110
+ #
111
+ use_ino: :int,
112
+ #
113
+ # If use_ino option is not given, still try to fill in the
114
+ # d_ino field in readdir(2). If the name was previously
115
+ # looked up, and is still in the cache, the inode number
116
+ # found there will be used. Otherwise it will be set to -1.
117
+ # If use_ino option is given, this option is ignored.
118
+ #
119
+ readdir_ino: :int,
120
+ #
121
+ # This option disables the use of page cache (file content cache)
122
+ # in the kernel for this filesystem. This has several affects:
123
+ #
124
+ # 1. Each read(2) or write(2) system call will initiate one
125
+ # or more read or write operations, data will not be
126
+ # cached in the kernel.
127
+ #
128
+ # 2. The return value of the read() and write() system calls
129
+ # will correspond to the return values of the read and
130
+ # write operations. This is useful for example if the
131
+ # file size is not known in advance (before reading it).
132
+ #
133
+ # Internally, enabling this option causes fuse to set the
134
+ # `direct_io` field of `struct fuse_file_info` - overwriting
135
+ # any value that was put there by the file system.
136
+ #
137
+ direct_io: :int,
138
+ #
139
+ # This option disables flushing the cache of the file
140
+ # contents on every open(2). This should only be enabled on
141
+ # filesystem where the file data is never changed
142
+ # externally (not through the mounted FUSE filesystem). Thus
143
+ # it is not suitable for network filesystem and other
144
+ # intermediate filesystem.
145
+ #
146
+ # NOTE: if this option is not specified (and neither
147
+ # direct_io) data is still cached after the open(2), so a
148
+ # read(2) system call will not always initiate a read
149
+ # operation.
150
+ #
151
+ # Internally, enabling this option causes fuse to set the
152
+ # `keep_cache` field of `struct fuse_file_info` - overwriting
153
+ # any value that was put there by the file system.
154
+ #
155
+ kernel_cache: :int,
156
+ #
157
+ # This option is an alternative to `kernel_cache`. Instead of
158
+ # unconditionally keeping cached data, the cached data is
159
+ # invalidated on open(2) if if the modification time or the
160
+ # size of the file has changed since it was last opened.
161
+ #
162
+ auto_cache: :int,
163
+ #
164
+ # The timeout in seconds for which file attributes are cached
165
+ # for the purpose of checking if auto_cache should flush the
166
+ # file data on open.
167
+ #
168
+ ac_attr_timeout_set: :int,
169
+ ac_attr_timeout: :double,
170
+ #
171
+ # If this option is given the file-system handlers for the
172
+ # following operations will not receive path information:
173
+ # read, write, flush, release, fsync, readdir, releasedir,
174
+ # fsyncdir, lock, ioctl and poll.
175
+ #
176
+ # For the truncate, getattr, chmod, chown and utimens
177
+ # operations the path will be provided only if the struct
178
+ # fuse_file_info argument is NULL.
179
+ #
180
+ nullpath_ok: :int,
181
+ #
182
+ # The remaining options are used by libfuse internally and
183
+ # should not be touched.
184
+ #
185
+ show_help: :int,
186
+ modules: :pointer,
187
+ debug: :int
188
+ }
189
+ )
190
+
191
+ BOOL_ATTRS = %i[
192
+ nullpath_ok ac_attr_timeout_set auto_cache kernel_cache direct_io
193
+ readdir_ino use_ino hard_remove intr set_mode set_uid set_gid
194
+ ].freeze
195
+
196
+ ffi_attr_reader(*BOOL_ATTRS)
197
+ ffi_attr_writer(*BOOL_ATTRS) { |v| v && v != 0 ? 1 : 0 }
198
+ BOOL_ATTRS.each { |a| define_method("#{a}?") { send(a) != 0 } }
199
+
200
+ OTHER_ATTRS = %i[ac_attr_timeout remember intr_signal attr_timeout negative_timeout entry_timeout umask uid
201
+ gid].freeze
202
+ ffi_attr_accessor(*OTHER_ATTRS)
203
+ end
204
+ end
205
+ end
@@ -0,0 +1,211 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'fuse_version'
4
+ require_relative '../accessors'
5
+
6
+ module FFI
7
+ # Ruby FFI Binding for [libfuse](https://github.com/libfuse/libfuse)
8
+ 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
55
+ #
56
+ # Connection information
57
+ #
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.
61
+ #
62
+ # @see FuseOperations#init
63
+ class FuseConnInfo < FFI::Struct
64
+ fuse_layout =
65
+ if FUSE_MAJOR_VERSION >= 3
66
+ {
67
+ proto_major: :uint,
68
+ proto_minor: :uint,
69
+ max_write: :uint,
70
+ max_read: :uint,
71
+ max_readahead: :uint,
72
+ #
73
+ # Capability flags that the kernel supports (read-only)
74
+ #
75
+ 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
+ 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
+ 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
+ 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
+ time_gran: :uint,
137
+ #
138
+ # For future use.
139
+ #
140
+ reserved: [:uint, 22]
141
+ }
142
+ else
143
+ {
144
+ proto_major: :uint,
145
+ proto_minor: :uint,
146
+ async_read: :uint, # This slot is max_read in Fuse 3
147
+ max_write: :uint,
148
+ max_readahead: :uint,
149
+ capable: :fuse_cap,
150
+ want: :fuse_cap,
151
+ max_background: :uint,
152
+ congestion_threshold: :uint,
153
+ reserved: [:uint, 23]
154
+ }
155
+ end.freeze
156
+
157
+ include(FFI::Accessors)
158
+
159
+ layout fuse_layout
160
+
161
+ # @!attribute [r] proto_major
162
+ # @return [Integer] Major version of the protocol (read-only)
163
+
164
+ # @!attribute [r] proto_minor
165
+ # @return [Integer] Minor version of the protocol (read-only)
166
+
167
+ ffi_attr_reader :proto_major, :proto_minor
168
+
169
+ # Is asynchronous read supported (read-write)
170
+ ffi_attr_accessor :async_read if FUSE_MAJOR_VERSION < 3
171
+
172
+ # @!attribute [rw] max_read
173
+ # @return [Integer] Maximum size of read requests.
174
+ #
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.
177
+ #
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.
181
+ ffi_attr_accessor :max_read if FUSE_MAJOR_VERSION >= 3
182
+
183
+ # @!attribute [rw] max_write
184
+ # @return [Integer] Maximum size of the write buffer
185
+
186
+ # @!attribute [rw] max_readahead
187
+ # @return [Integer] Maximum size of the readahead buffer
188
+
189
+ ffi_attr_accessor :max_write, :max_readahead
190
+
191
+ # Capability flags that kernel supports
192
+ ffi_attr_reader :capable
193
+
194
+ # Is capable of all of these FUSE_CAPs
195
+ def capable?(*of)
196
+ of.all? { |c| self[:capable].include?(c) }
197
+ end
198
+
199
+ # Capability flags that the filesystem wants
200
+ ffi_attr_accessor :want
201
+
202
+ # Maximum number of backgrounded requests
203
+ ffi_attr_accessor :max_background
204
+
205
+ # Kernel congestion threshold parameter
206
+ ffi_attr_reader :congestion_threshold
207
+
208
+ ffi_attr_accessor :time_gran if FUSE_MAJOR_VERSION >= 3
209
+ end
210
+ end
211
+ end
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'fuse_common'
4
+ require_relative '../ruby_object'
5
+ require_relative '../accessors'
6
+
7
+ module FFI
8
+ # Ruby FFI Binding for [libfuse](https://github.com/libfuse/libfuse)
9
+ module Libfuse
10
+ # Context for each callback operation
11
+ # @see get
12
+ class FuseContext < FFI::Struct
13
+ include FFI::Accessors
14
+ base = { fuse: :fuse, uid: :uid_t, gid: :gid_t, pid: :pid_t, private_data: RubyObject }
15
+ base[:umask] = :mode_t if FUSE_VERSION >= 28
16
+ layout base
17
+
18
+ ffi_attr_reader(:uid, :gid, :pid, :mode, :private_data)
19
+
20
+ ffi_attr_reader(:umask) if FUSE_VERSION >= 28
21
+
22
+ # @!attribute [r] uid
23
+ # @return [Integer] user id of the calling process
24
+
25
+ # @!attribute [r] gid
26
+ # @return [Integer] group id of the calling process
27
+
28
+ # @!attribute [r] pid
29
+ # @return [Integer] process id of the calling thread
30
+
31
+ # @!attribute [r] private_data
32
+ # @return [Object] private filesystem data
33
+ # @see FuseOperations#init
34
+
35
+ # @!attribute [r] umask
36
+ # @return [Integer] umask of the calling process
37
+
38
+ # @return [Boolean]
39
+ # @see Libfuse.fuse_interrupted?
40
+ def interrupted?
41
+ Libfuse.fuse_interrupted?
42
+ end
43
+
44
+ # @return [void]
45
+ # @raise [Errno::EINTR]
46
+ # @see Libfuse.raise_interrupt
47
+ def raise_interrupt
48
+ Libfuse.raise_interrupt
49
+ end
50
+
51
+ class << self
52
+ # @return [FuseContext] the context for the current filesystem operation
53
+ def fuse_get_context
54
+ Libfuse.fuse_get_context
55
+ end
56
+ alias get fuse_get_context
57
+ end
58
+ end
59
+
60
+ attach_function :fuse_get_context, [], FuseContext.by_ref
61
+ attach_function :fuse_interrupted, [], :int
62
+ class << self
63
+ # @return [Boolean] if the fuse request is marked as interrupted
64
+ def fuse_interrupted?
65
+ fuse_interrupted != 0
66
+ end
67
+
68
+ # @return [void]
69
+ # @raise [Errno::EINTR] if fuse request is marked as interrupted
70
+ def raise_interrupt
71
+ raise Errno::EINTR if fuse_interrupted?
72
+ end
73
+
74
+ # @!visibility private
75
+ # @!method fuse_get_context
76
+ # @!method fuse_interrupted
77
+ end
78
+ end
79
+ end