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