ffi-libfuse 0.3.4 → 0.4.1
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +29 -0
- data/README.md +1 -1
- data/lib/ffi/accessors.rb +419 -93
- data/lib/ffi/boolean_int.rb +1 -1
- data/lib/ffi/devt.rb +36 -10
- data/lib/ffi/flock.rb +31 -27
- data/lib/ffi/libfuse/adapter/context.rb +1 -1
- data/lib/ffi/libfuse/adapter/debug.rb +54 -16
- data/lib/ffi/libfuse/adapter/fuse2_compat.rb +43 -26
- data/lib/ffi/libfuse/adapter/fuse3_support.rb +7 -8
- data/lib/ffi/libfuse/adapter/interrupt.rb +1 -1
- data/lib/ffi/libfuse/adapter/pathname.rb +1 -1
- data/lib/ffi/libfuse/adapter/ruby.rb +211 -160
- data/lib/ffi/libfuse/adapter/safe.rb +70 -22
- data/lib/ffi/libfuse/callbacks.rb +2 -1
- data/lib/ffi/libfuse/filesystem/accounting.rb +1 -1
- data/lib/ffi/libfuse/filesystem/mapped_files.rb +33 -7
- data/lib/ffi/libfuse/filesystem/pass_through_dir.rb +0 -1
- data/lib/ffi/libfuse/filesystem/virtual_dir.rb +294 -127
- data/lib/ffi/libfuse/filesystem/virtual_file.rb +85 -79
- data/lib/ffi/libfuse/filesystem/virtual_fs.rb +34 -15
- data/lib/ffi/libfuse/filesystem/virtual_link.rb +60 -0
- data/lib/ffi/libfuse/filesystem/virtual_node.rb +104 -87
- data/lib/ffi/libfuse/filesystem.rb +1 -1
- data/lib/ffi/libfuse/fuse2.rb +3 -2
- data/lib/ffi/libfuse/fuse3.rb +6 -6
- data/lib/ffi/libfuse/fuse_args.rb +14 -21
- data/lib/ffi/libfuse/fuse_buf.rb +112 -0
- data/lib/ffi/libfuse/fuse_buf_vec.rb +228 -0
- data/lib/ffi/libfuse/fuse_cmdline_opts.rb +19 -16
- data/lib/ffi/libfuse/fuse_common.rb +10 -4
- data/lib/ffi/libfuse/fuse_config.rb +35 -23
- data/lib/ffi/libfuse/fuse_conn_info.rb +1 -1
- data/lib/ffi/libfuse/fuse_context.rb +2 -1
- data/lib/ffi/libfuse/fuse_loop_config.rb +68 -20
- data/lib/ffi/libfuse/fuse_operations.rb +86 -41
- data/lib/ffi/libfuse/gem_helper.rb +2 -9
- data/lib/ffi/libfuse/io.rb +56 -0
- data/lib/ffi/libfuse/main.rb +33 -26
- data/lib/ffi/libfuse/test_helper.rb +67 -61
- data/lib/ffi/libfuse/version.rb +1 -1
- data/lib/ffi/libfuse.rb +1 -1
- data/lib/ffi/stat/native.rb +4 -4
- data/lib/ffi/stat.rb +35 -12
- data/lib/ffi/stat_vfs.rb +1 -2
- data/lib/ffi/struct_array.rb +2 -1
- data/lib/ffi/struct_wrapper.rb +6 -4
- data/sample/hello_fs.rb +1 -1
- metadata +6 -3
- data/lib/ffi/libfuse/fuse_buffer.rb +0 -257
@@ -3,13 +3,17 @@
|
|
3
3
|
require_relative 'fuse_version'
|
4
4
|
require_relative 'fuse_opt'
|
5
5
|
require_relative '../ruby_object'
|
6
|
+
require_relative '../boolean_int'
|
7
|
+
require_relative '../accessors'
|
6
8
|
|
7
9
|
module FFI
|
8
10
|
# Ruby FFI Binding for [libfuse](https://github.com/libfuse/libfuse)
|
9
11
|
module Libfuse
|
10
12
|
# struct fuse_args
|
11
13
|
class FuseArgs < FFI::Struct
|
12
|
-
|
14
|
+
include FFI::Accessors
|
15
|
+
|
16
|
+
layout :argc, :int, :argv, :pointer, :allocated, :bool_int
|
13
17
|
|
14
18
|
# Create a fuse_args struct from command line options
|
15
19
|
# @param [Array<String>] argv command line args
|
@@ -34,33 +38,17 @@ module FFI
|
|
34
38
|
@arg_vector[argv.size].put_pointer(0, FFI::Pointer::NULL)
|
35
39
|
self[:argv] = @arg_vector
|
36
40
|
self[:argc] = argv.size
|
37
|
-
self[:allocated] =
|
41
|
+
self[:allocated] = false # ie libfuse did not allocate
|
38
42
|
self
|
39
43
|
end
|
40
44
|
|
41
|
-
# @!attribute [r] argc
|
42
|
-
# @return [Integer] count of args
|
43
|
-
def argc
|
44
|
-
self[:argc]
|
45
|
-
end
|
46
|
-
|
47
45
|
# @!attribute [r] argv
|
48
46
|
# @return [Array<String>] list of args
|
49
|
-
|
47
|
+
ffi_attr_reader(:argv) do
|
50
48
|
# noinspection RubyResolve
|
51
49
|
self[:argv].get_array_of_pointer(0, argc).map(&:read_string)
|
52
50
|
end
|
53
51
|
|
54
|
-
# @!visibility private
|
55
|
-
def allocated
|
56
|
-
self[:allocated]
|
57
|
-
end
|
58
|
-
|
59
|
-
# @!visibility private
|
60
|
-
def inspect
|
61
|
-
"#{self.class.name} - #{%i[argc argv allocated].to_h { |m| [m, send(m)] }}"
|
62
|
-
end
|
63
|
-
|
64
52
|
# Add an arg to this arg list
|
65
53
|
# @param [String] arg
|
66
54
|
def add(arg)
|
@@ -121,7 +109,8 @@ module FFI
|
|
121
109
|
# - :error an error, alternatively raise {Error}
|
122
110
|
# - :keep retain the current argument for further processing
|
123
111
|
# - :handled,:discard remove the current argument from further processing
|
124
|
-
# @
|
112
|
+
# @raise Error if an error is raised during parsing
|
113
|
+
# @return [self]
|
125
114
|
def parse!(opts, data = nil, ignore: %i[non_option unmatched], &block)
|
126
115
|
ignore ||= []
|
127
116
|
|
@@ -140,7 +129,9 @@ module FFI
|
|
140
129
|
end
|
141
130
|
|
142
131
|
fop = fuse_opt_proc(symbols, bool_opts, param_opts, ignore, &block)
|
143
|
-
Libfuse.fuse_opt_parse(self, data, int_opts, fop).zero?
|
132
|
+
raise Error unless Libfuse.fuse_opt_parse(self, data, int_opts, fop).zero?
|
133
|
+
|
134
|
+
self
|
144
135
|
end
|
145
136
|
|
146
137
|
private
|
@@ -148,6 +139,8 @@ module FFI
|
|
148
139
|
# Valid return values from parse! block
|
149
140
|
FUSE_OPT_PROC_RETURN = { error: -1, keep: 1, handled: 0, discard: 0 }.freeze
|
150
141
|
|
142
|
+
ffi_attr_reader(:argc, :allocated?)
|
143
|
+
|
151
144
|
def fuse_opt_proc(symbols, bool_opts, param_opts, ignore, &block)
|
152
145
|
proc do |data, arg, key, out|
|
153
146
|
key = symbols[key]
|
@@ -0,0 +1,112 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'fuse_version'
|
4
|
+
require_relative '../struct_wrapper'
|
5
|
+
|
6
|
+
module FFI
|
7
|
+
# Ruby FFI Binding for [libfuse](https://github.com/libfuse/libfuse)
|
8
|
+
module Libfuse
|
9
|
+
bitmask :fuse_buf_flags, [:is_fd, 1, :fd_seek, :fd_retry]
|
10
|
+
|
11
|
+
#
|
12
|
+
# Single io buffer
|
13
|
+
#
|
14
|
+
# Generic io buffer for I/O, extended attributes, etc...Data may be supplied as a memory pointer or as a file
|
15
|
+
# descriptor
|
16
|
+
#
|
17
|
+
class FuseBuf
|
18
|
+
# @!visibility private
|
19
|
+
# Native FuseBuf layout
|
20
|
+
class Native < FFI::Struct
|
21
|
+
layout(
|
22
|
+
size: :size_t, # Size of io in bytes
|
23
|
+
flags: FFI::Libfuse.find_type(:fuse_buf_flags), # Buffer flags
|
24
|
+
mem: :pointer, # Memory pointer - used if :is_fd flag is not set
|
25
|
+
fd: :int, # File descriptor - used if :is_fd is set
|
26
|
+
pos: :off_t # File position - used if :fd_seek flag is set.
|
27
|
+
)
|
28
|
+
end
|
29
|
+
|
30
|
+
include StructWrapper
|
31
|
+
native_struct(Native)
|
32
|
+
|
33
|
+
ffi_attr_reader(:mem, :fd, :pos)
|
34
|
+
ffi_attr_accessor(:size)
|
35
|
+
|
36
|
+
# @!attribute [r] mem
|
37
|
+
# @return [FFI::Pointer] the memory in the buffer
|
38
|
+
|
39
|
+
alias memory mem
|
40
|
+
|
41
|
+
# @!attribute [r] fd
|
42
|
+
# @return [Integer] the file descriptor number
|
43
|
+
|
44
|
+
alias file_descriptor fd
|
45
|
+
|
46
|
+
# @attribute [rw] size
|
47
|
+
# @return [Integer] the size of the buffer
|
48
|
+
|
49
|
+
# @return [Boolean] true if this a memory buffer
|
50
|
+
def mem?
|
51
|
+
!fd?
|
52
|
+
end
|
53
|
+
|
54
|
+
# @return [Boolean] true if this is a file descriptor buffer
|
55
|
+
def fd?
|
56
|
+
self[:flags].include?(:is_fd)
|
57
|
+
end
|
58
|
+
alias file_descriptor? fd?
|
59
|
+
|
60
|
+
# @overload fill(str:)
|
61
|
+
# Create a memory buffer from str
|
62
|
+
# @param [String,#to_s] str
|
63
|
+
#
|
64
|
+
# @overload fill(size:)
|
65
|
+
# Allocate an empty memory buffer of size bytes
|
66
|
+
# @param [Integer] size
|
67
|
+
#
|
68
|
+
# @overload fill(mem:, size: mem.size)
|
69
|
+
# Set the buffer to contain the previously allocated memory
|
70
|
+
# @param [FFI::Pointer] mem
|
71
|
+
# @param [Integer] size <= mem.size
|
72
|
+
#
|
73
|
+
# @overload fill(fd:, fd_retry: false, size:, pos: 0)
|
74
|
+
# Fill as a FileDescriptor buffer
|
75
|
+
# @param [Integer] fd File descriptor
|
76
|
+
# @param [Boolean] fd_retry
|
77
|
+
# Retry operations on file descriptor
|
78
|
+
#
|
79
|
+
# If this flag is set then retry operation on file descriptor until size bytes have been copied or an error
|
80
|
+
# or EOF is detected.
|
81
|
+
#
|
82
|
+
# @param [Integer] size
|
83
|
+
# number of bytes to read from fd
|
84
|
+
# @param [nil, Integer] pos
|
85
|
+
# If set then used to seek to the given offset before performing operation on file descriptor.
|
86
|
+
# @return [self]
|
87
|
+
def fill(
|
88
|
+
str: nil,
|
89
|
+
mem: str ? FFI::MemoryPointer.from_string(str.to_s) : FFI::Pointer::NULL, size: mem.null? ? 0 : mem.size,
|
90
|
+
fd: -1, fd_retry: false, pos: nil # rubocop:disable Naming/MethodParameterName
|
91
|
+
|
92
|
+
)
|
93
|
+
|
94
|
+
# Allocate size bytes if we've been given a null pointer
|
95
|
+
mem = FFI::MemoryPointer.new(:char, size, true) if fd == -1 && mem.null? && size.positive?
|
96
|
+
|
97
|
+
mem.autorelease = to_ptr.autorelease? unless mem.null?
|
98
|
+
|
99
|
+
self[:size] = size
|
100
|
+
self[:mem] = mem
|
101
|
+
self[:fd] = fd
|
102
|
+
flags = []
|
103
|
+
flags << :is_fd if fd != -1
|
104
|
+
flags << :fd_seek if pos
|
105
|
+
flags << :fd_retry if fd_retry
|
106
|
+
self[:flags] = flags
|
107
|
+
self[:pos] = pos || 0
|
108
|
+
self
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
@@ -0,0 +1,228 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'fuse_version'
|
4
|
+
require_relative 'io'
|
5
|
+
require_relative 'fuse_buf'
|
6
|
+
require_relative '../accessors'
|
7
|
+
|
8
|
+
module FFI
|
9
|
+
# Ruby FFI Binding for [libfuse](https://github.com/libfuse/libfuse)
|
10
|
+
module Libfuse
|
11
|
+
#
|
12
|
+
# Data buffer vector
|
13
|
+
#
|
14
|
+
# An list of io buffers, each containing a memory pointer or a file descriptor.
|
15
|
+
#
|
16
|
+
class FuseBufVec < FFI::Struct
|
17
|
+
include Accessors
|
18
|
+
|
19
|
+
layout(
|
20
|
+
count: :size_t,
|
21
|
+
idx: :size_t,
|
22
|
+
off: :size_t,
|
23
|
+
# buf is treated as a variable length array of FuseBuf at size +1 following the struct.
|
24
|
+
buf: [FuseBuf::Native, 1] # struct fuse_buf[1]
|
25
|
+
)
|
26
|
+
|
27
|
+
ffi_attr_reader(:count, :idx, :off)
|
28
|
+
|
29
|
+
# @!attribute [r] count
|
30
|
+
# @return [Integer] the number of buffers in the array
|
31
|
+
|
32
|
+
# @!attribute [r] idx
|
33
|
+
# @return [Integer] index of current buffer within the array
|
34
|
+
|
35
|
+
alias index idx
|
36
|
+
|
37
|
+
# @!attribute [r] off
|
38
|
+
# @return [Integer] current offset within the current buffer
|
39
|
+
|
40
|
+
alias offset off
|
41
|
+
|
42
|
+
# Create and initialise from a ruby object that quacks like {::File}, {::IO}, or {::String}
|
43
|
+
#
|
44
|
+
# @param [Object] io
|
45
|
+
#
|
46
|
+
# * Integer file descriptor or File like object that returns one via :fileno
|
47
|
+
# * Otherwise something to pass to {IO.read}(io, size, offset) to create a memory based buffer
|
48
|
+
#
|
49
|
+
# @param [Integer] size
|
50
|
+
# @param [Integer] offset
|
51
|
+
# @return [FuseBufVec]
|
52
|
+
#
|
53
|
+
# @note The returned object's memory is not auto-released, and thus suitable for use with
|
54
|
+
# {FuseOperations#read_buf} where the buffers are cleared by libfuse library..
|
55
|
+
def self.create(io, size, offset = nil)
|
56
|
+
fd = io.respond_to?(:fileno) ? io.fileno : io
|
57
|
+
return init(autorelease: false, size: size, fd: fd, pos: offset || 0) if fd.is_a?(Integer)
|
58
|
+
|
59
|
+
init(autorelease: false, str: Libfuse::IO.read(io, size, offset))
|
60
|
+
end
|
61
|
+
|
62
|
+
# Create and initialise a new FuseBufVec
|
63
|
+
#
|
64
|
+
# @param [Boolean] autorelease should the struct be freed on GC
|
65
|
+
#
|
66
|
+
# Use false only if this object is going to be passed to the C library side. eg. {FuseOperations#read_buf}
|
67
|
+
# @param [Hash<Symbol>] buf_options options for configuring the initial buffer (see {FuseBuf#fill})
|
68
|
+
# @return [FuseBufVec]
|
69
|
+
def self.init(autorelease: true, count: 1, **buf_options)
|
70
|
+
bufvec_ptr = FFI::MemoryPointer.new(:uchar, FuseBufVec.size + (FuseBuf::Native.size * (count - 1)), true)
|
71
|
+
bufvec_ptr.autorelease = autorelease
|
72
|
+
bufvec = new(bufvec_ptr)
|
73
|
+
bufvec[:count] = count
|
74
|
+
bufvec[:idx] = 0
|
75
|
+
bufvec[:off] = 0
|
76
|
+
bufvec.buffers[0].fill(**buf_options) unless buf_options.empty?
|
77
|
+
bufvec
|
78
|
+
end
|
79
|
+
|
80
|
+
# @return [Integer] total size of io in a fuse buffer vector (ie the size of all the fuse buffers)
|
81
|
+
def buf_size
|
82
|
+
Libfuse.fuse_buf_size(self)
|
83
|
+
end
|
84
|
+
|
85
|
+
# Set {index}/{offset} for reading/writing from pos
|
86
|
+
# @param [Integer] pos
|
87
|
+
# @return [self]
|
88
|
+
# @raise [Errno::ERANGE] if seek past end of file
|
89
|
+
def seek(pos)
|
90
|
+
buffers.each_with_index do |b, i|
|
91
|
+
if pos < b.size
|
92
|
+
self[:idx] = i
|
93
|
+
self[:off] = pos
|
94
|
+
return self
|
95
|
+
else
|
96
|
+
pos -= b.size
|
97
|
+
end
|
98
|
+
end
|
99
|
+
raise Errno::ERANGE
|
100
|
+
end
|
101
|
+
|
102
|
+
# Copy data from this set of buffers to another set
|
103
|
+
#
|
104
|
+
# @param [FuseBufVec] dst destination buffers
|
105
|
+
# @param [Array<Symbol>] flags Buffer copy flags
|
106
|
+
#
|
107
|
+
# - :no_splice
|
108
|
+
# Don't use splice(2)
|
109
|
+
#
|
110
|
+
# Always fall back to using read and write instead of splice(2) to copy io from one file descriptor to
|
111
|
+
# another.
|
112
|
+
#
|
113
|
+
# If this flag is not set, then only fall back if splice is unavailable.
|
114
|
+
#
|
115
|
+
# - :force_splice
|
116
|
+
#
|
117
|
+
# Always use splice(2) to copy io from one file descriptor to another. If splice is not available, return
|
118
|
+
# -EINVAL.
|
119
|
+
#
|
120
|
+
# - :splice_move
|
121
|
+
#
|
122
|
+
# Try to move io with splice.
|
123
|
+
#
|
124
|
+
# If splice is used, try to move pages from the source to the destination instead of copying. See
|
125
|
+
# documentation of SPLICE_F_MOVE in splice(2) man page.
|
126
|
+
#
|
127
|
+
# - :splice_nonblock
|
128
|
+
#
|
129
|
+
# Don't block on the pipe when copying io with splice
|
130
|
+
#
|
131
|
+
# Makes the operations on the pipe non-blocking (if the pipe is full or empty). See SPLICE_F_NONBLOCK in
|
132
|
+
# the splice(2) man page.
|
133
|
+
#
|
134
|
+
# @return [Integer] actual number of bytes copied or -errno on error
|
135
|
+
#
|
136
|
+
def copy_to(dst, *flags)
|
137
|
+
Libfuse.fuse_buf_copy(dst, self, flags)
|
138
|
+
end
|
139
|
+
|
140
|
+
# Copy direct to file descriptor
|
141
|
+
# @param [Integer] fileno a file descriptor
|
142
|
+
# @param [nil, Integer] offset if non nil will first seek to offset
|
143
|
+
# @param [Array<Symbol>] flags see {copy_to}
|
144
|
+
# @return [Integer] number of bytes copied
|
145
|
+
def copy_to_fd(fileno, offset = nil, *flags)
|
146
|
+
dst = self.class.init(size: buf_size, fd: fileno, pos: offset)
|
147
|
+
copy_to(dst, *flags)
|
148
|
+
end
|
149
|
+
|
150
|
+
# Copy to string via a temporary buffer
|
151
|
+
# @param [Array<Symbol>] flags see {copy_to}
|
152
|
+
# @return [String] the extracted data
|
153
|
+
def copy_to_str(*flags)
|
154
|
+
dst = self.class.init(size: buf_size)
|
155
|
+
copied = copy_to(dst, *flags)
|
156
|
+
dst.buffers.first.memory.read_string(copied)
|
157
|
+
end
|
158
|
+
|
159
|
+
# Copy from another set of buffers to this one
|
160
|
+
# @param [FuseBufVec] src source buffers
|
161
|
+
# @param [Array<Symbol>] flags
|
162
|
+
# @return [Integer] number of bytes written
|
163
|
+
# @see copy_to
|
164
|
+
def copy_from(src, *flags)
|
165
|
+
Libfuse.fuse_buf_copy(self, src, flags)
|
166
|
+
end
|
167
|
+
|
168
|
+
# Store ourself into a pointer location as received by {FuseOperations#read_buf}
|
169
|
+
# @param [FFI::Pointer<FuseBufVec>] bufp
|
170
|
+
# @return [void]
|
171
|
+
def store_to(bufp)
|
172
|
+
bufp.write_pointer(to_ptr)
|
173
|
+
end
|
174
|
+
|
175
|
+
# Write data from these buffers to another object
|
176
|
+
#
|
177
|
+
# @overload copy_to_io(io, *flags)
|
178
|
+
# @overload copy_to_io(io, offset = nil, *flags)
|
179
|
+
# @param [Object] io one of
|
180
|
+
#
|
181
|
+
# * another {FuseBufVec} via io.{seek}(offset) and {copy_to}(io, *flags)
|
182
|
+
# * an {::Integer} file descriptor to write via {copy_to_fd}(io, offset, *flags)
|
183
|
+
# * a {::File} like object that returns a file descriptor via :fileno used as above
|
184
|
+
# * an {::IO} like object that accepts a string data as {IO.write}(io, {copy_to_str}(*flags), offset)
|
185
|
+
#
|
186
|
+
# @param [nil, Integer] offset position in io to begin writing at, or nil if io is already positioned
|
187
|
+
# @param [Array<Symbol>] flags see {copy_to}
|
188
|
+
#
|
189
|
+
# @return [Integer] number of bytes written
|
190
|
+
# @raise [Errno::EBADF] if io is not a valid target
|
191
|
+
def copy_to_io(io, offset = nil, *flags)
|
192
|
+
if offset.is_a?(Symbol)
|
193
|
+
flags.unshift(offset)
|
194
|
+
offset = nil
|
195
|
+
end
|
196
|
+
|
197
|
+
if io.is_a?(FuseBufVec)
|
198
|
+
io.seek(offset) if offset
|
199
|
+
return copy_to(io, *flags)
|
200
|
+
end
|
201
|
+
|
202
|
+
fd = (io.respond_to?(:fileno) ? io.fileno : io)
|
203
|
+
return copy_to_fd(fd, offset || 0, *flags) if fd.is_a?(Integer)
|
204
|
+
|
205
|
+
Libfuse::IO.write(io, copy_to_str(*flags), offset)
|
206
|
+
end
|
207
|
+
|
208
|
+
# @return [Array<FuseBuf>] list of buffers
|
209
|
+
def buffers
|
210
|
+
@buffers ||= Array.new(count) do |i|
|
211
|
+
native = i.zero? ? self[:buf].first : FuseBuf::Native.new(self[:buf].to_ptr + (i * FuseBuf::Native.size))
|
212
|
+
FuseBuf.new(native)
|
213
|
+
end.freeze
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
bitmask :fuse_buf_copy_flags, [:no_splice, 1, :force_splice, 2, :splice_move, 4, :splice_nonblock, 8]
|
218
|
+
attach_function :fuse_buf_copy, [FuseBufVec.by_ref, FuseBufVec.by_ref, :fuse_buf_copy_flags], :ssize_t
|
219
|
+
attach_function :fuse_buf_size, [FuseBufVec.by_ref], :size_t
|
220
|
+
|
221
|
+
class << self
|
222
|
+
# @!visibility private
|
223
|
+
# @!method fuse_buf_size
|
224
|
+
# @!method fuse_buf_copy
|
225
|
+
# @!method fuse_buf_size
|
226
|
+
end
|
227
|
+
end
|
228
|
+
end
|
@@ -1,10 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require_relative '../accessors'
|
4
|
+
require_relative '../boolean_int'
|
4
5
|
require_relative 'fuse_loop_config'
|
5
6
|
module FFI
|
6
7
|
module Libfuse
|
7
|
-
#
|
8
8
|
# struct fuse_cmdline_opts {
|
9
9
|
# int singlethread;
|
10
10
|
# int foreground;
|
@@ -16,29 +16,32 @@ module FFI
|
|
16
16
|
# int clone_fd;
|
17
17
|
# unsigned int max_idle_threads;
|
18
18
|
# };
|
19
|
+
|
20
|
+
# Command line options
|
19
21
|
# @!visibility private
|
20
22
|
class FuseCmdlineOpts < FFI::Struct
|
21
23
|
include(FFI::Accessors)
|
22
24
|
|
23
|
-
|
24
|
-
single_thread: :
|
25
|
-
foreground: :
|
26
|
-
debug: :
|
27
|
-
nodefault_subtype: :
|
25
|
+
spec = {
|
26
|
+
single_thread: :bool_int,
|
27
|
+
foreground: :bool_int,
|
28
|
+
debug: :bool_int,
|
29
|
+
nodefault_subtype: :bool_int,
|
28
30
|
mountpoint: :string,
|
29
|
-
show_version: :
|
30
|
-
show_help: :
|
31
|
-
clone_fd: :
|
32
|
-
max_idle_threads: :
|
33
|
-
|
31
|
+
show_version: :bool_int,
|
32
|
+
show_help: :bool_int,
|
33
|
+
clone_fd: :bool_int,
|
34
|
+
max_idle_threads: :uint
|
35
|
+
}
|
36
|
+
spec[:max_threads] = :uint if FUSE_MINOR_VERSION >= 12
|
37
|
+
|
38
|
+
layout(spec)
|
34
39
|
|
35
|
-
|
36
|
-
ffi_attr_reader(
|
37
|
-
:clone_fd) do |v|
|
38
|
-
v != 0
|
39
|
-
end
|
40
|
+
bool, = spec.partition { |_, v| v == :bool_int }
|
41
|
+
ffi_attr_reader(*bool.map { |k, _| "#{k}?" })
|
40
42
|
|
41
43
|
ffi_attr_reader(:max_idle_threads, :mountpoint)
|
44
|
+
ffi_attr_reader(:max_threads) if FUSE_MINOR_VERSION >= 12
|
42
45
|
end
|
43
46
|
end
|
44
47
|
end
|
@@ -52,11 +52,13 @@ module FFI
|
|
52
52
|
teardown
|
53
53
|
end
|
54
54
|
|
55
|
-
# @api private
|
56
55
|
# @param [Boolean] foreground
|
57
56
|
# @param [Boolean] single_thread
|
58
|
-
# @param [Hash<String,Proc>] traps
|
59
|
-
#
|
57
|
+
# @param [Hash<String,Proc|nil>] traps as per Signal.trap
|
58
|
+
# these are merged over {default_traps} for INT, HUP, TERM that unmount and exit filesystem. A nil
|
59
|
+
# value for these default signals will leave any existing signal handle in place.
|
60
|
+
# @param [Integer] remember fuse cache timeout
|
61
|
+
# @api private
|
60
62
|
# Implement fuse loop in ruby
|
61
63
|
#
|
62
64
|
# Pros:
|
@@ -71,6 +73,7 @@ module FFI
|
|
71
73
|
# * clone_fd is ignored
|
72
74
|
# * filesystem interrupts probably can't work
|
73
75
|
def run_ruby(foreground: true, single_thread: true, traps: {}, remember: false, **options)
|
76
|
+
traps = default_traps.merge(traps).keep_if { |_, v| v }
|
74
77
|
Ackbar.trap(default_traps.merge(traps)) do |signals|
|
75
78
|
daemonize unless foreground
|
76
79
|
|
@@ -112,7 +115,9 @@ module FFI
|
|
112
115
|
end
|
113
116
|
|
114
117
|
# Ruby implementation of fuse default traps
|
115
|
-
#
|
118
|
+
#
|
119
|
+
# * INT, HUP, TERM, TSTP to unmount and exit filesystem
|
120
|
+
# * PIPE is ignored
|
116
121
|
def default_traps
|
117
122
|
exproc = ->(signame) { exit(signame) }
|
118
123
|
@default_traps ||= { INT: exproc, HUP: exproc, TERM: exproc, TSTP: exproc, PIPE: 'IGNORE' }
|
@@ -190,6 +195,7 @@ module FFI
|
|
190
195
|
fuse_process || (sleep(0.1) && false)
|
191
196
|
end
|
192
197
|
|
198
|
+
# @!visibility private
|
193
199
|
def teardown
|
194
200
|
return unless @fuse
|
195
201
|
|
@@ -10,10 +10,12 @@ module FFI
|
|
10
10
|
# This structure is initialized from the arguments passed to fuse_new(), and then passed to the file system's init()
|
11
11
|
# handler which should ensure that the configuration is compatible with the file system implementation.
|
12
12
|
#
|
13
|
+
# Some options can only be set via the filesystem init method (:use_ino etc..) because the filesystem either
|
14
|
+
# supports them or it doesn't.
|
13
15
|
class FuseConfig < FFI::Struct
|
14
16
|
include FFI::Accessors
|
15
17
|
|
16
|
-
|
18
|
+
spec =
|
17
19
|
{
|
18
20
|
# @!attribute [r] gid
|
19
21
|
# @return [Integer|nil] if set, this value will be used for the :gid attribute of each file
|
@@ -38,7 +40,7 @@ module FFI
|
|
38
40
|
# @!attribute [rw] negative_timeout
|
39
41
|
# The timeout in seconds for which a negative lookup will be cached.
|
40
42
|
#
|
41
|
-
# This means, that if file did not exist (lookup
|
43
|
+
# This means, that if file did not exist (lookup returned ENOENT), the lookup will only be redone after the
|
42
44
|
# timeout, and the file/directory will be assumed to not exist until then. A value of zero means that
|
43
45
|
# negative lookups are not cached.
|
44
46
|
#
|
@@ -53,7 +55,7 @@ module FFI
|
|
53
55
|
# @return [Float]
|
54
56
|
attr_timeout: :double,
|
55
57
|
|
56
|
-
# @!attribute [rw] intr
|
58
|
+
# @!attribute [rw] intr?
|
57
59
|
# Allow requests to be interrupted
|
58
60
|
# @return [Boolean]
|
59
61
|
intr: :bool_int,
|
@@ -78,7 +80,7 @@ module FFI
|
|
78
80
|
# @return [Integer]
|
79
81
|
remember: :int,
|
80
82
|
|
81
|
-
# @!attribute [rw] hard_remove
|
83
|
+
# @!attribute [rw] hard_remove?
|
82
84
|
# should open files be removed immediately
|
83
85
|
#
|
84
86
|
# The default behavior is that if an open file is deleted, the file is renamed to a hidden file
|
@@ -93,7 +95,7 @@ module FFI
|
|
93
95
|
# @return [Boolean]
|
94
96
|
hard_remove: :bool_int,
|
95
97
|
|
96
|
-
# @!attribute [rw] use_ino
|
98
|
+
# @!attribute [rw] use_ino?
|
97
99
|
# use filesystem provided inode values
|
98
100
|
#
|
99
101
|
# Honor the st_ino field in the functions getattr() and fill_dir(). This value is used to fill in the st_ino
|
@@ -107,8 +109,8 @@ module FFI
|
|
107
109
|
# @return [Boolean]
|
108
110
|
use_ino: :bool_int,
|
109
111
|
|
110
|
-
# @!attribute [rw] readdir_ino
|
111
|
-
# generate inodes for readdir even if {#use_ino} is set
|
112
|
+
# @!attribute [rw] readdir_ino?
|
113
|
+
# generate inodes for readdir even if {#use_ino?} is set
|
112
114
|
#
|
113
115
|
# If use_ino option is not given, still try to fill in the d_ino field in readdir(2). If the name was
|
114
116
|
# previously looked up, and is still in the cache, the inode number found there will be used. Otherwise it
|
@@ -116,7 +118,7 @@ module FFI
|
|
116
118
|
# @return [Boolean]
|
117
119
|
readdir_ino: :bool_int,
|
118
120
|
|
119
|
-
# @!attribute [rw] direct_io
|
121
|
+
# @!attribute [rw] direct_io?
|
120
122
|
# disables the use of kernel page cache (file content cache) in the kernel for this filesystem.
|
121
123
|
#
|
122
124
|
# This has several affects:
|
@@ -133,13 +135,13 @@ module FFI
|
|
133
135
|
# @return [Boolean]
|
134
136
|
direct_io: :bool_int,
|
135
137
|
|
136
|
-
# @!attribute [rw] kernel_cache
|
138
|
+
# @!attribute [rw] kernel_cache?
|
137
139
|
# disables flushing the cache of the file contents on every open(2).
|
138
140
|
#
|
139
141
|
# This should only be enabled on filesystem where the file data is never changed externally (not through the
|
140
142
|
# mounted FUSE filesystem). Thus it is not suitable for network filesystem and other intermediate filesystem.
|
141
143
|
#
|
142
|
-
# **Note**: if neither this option or {#direct_io} is specified data is still cached after the open(2),
|
144
|
+
# **Note**: if neither this option or {#direct_io?} is specified data is still cached after the open(2),
|
143
145
|
# so a read(2) system call will not always initiate a read operation.
|
144
146
|
#
|
145
147
|
# Internally, enabling this option causes fuse to set {FuseFileInfo#keep_cache} overwriting any value that was
|
@@ -147,7 +149,7 @@ module FFI
|
|
147
149
|
# @return [Boolean]
|
148
150
|
kernel_cache: :bool_int,
|
149
151
|
|
150
|
-
# @!attribute [rw] auto_cache
|
152
|
+
# @!attribute [rw] auto_cache?
|
151
153
|
# invalidate cached data on open based on changes in file attributes
|
152
154
|
#
|
153
155
|
# This option is an alternative to `kernel_cache`. Instead of unconditionally keeping cached data, the cached
|
@@ -158,12 +160,12 @@ module FFI
|
|
158
160
|
|
159
161
|
# @!attribute [rw] ac_attr_timeout
|
160
162
|
# if set the timeout in seconds for which file attributes are cached for the purpose of checking if
|
161
|
-
#
|
163
|
+
# auto_cache should flush the file data on open.
|
162
164
|
# @return [Float|nil]
|
163
165
|
ac_attr_timeout_set: :bool_int,
|
164
166
|
ac_attr_timeout: :double,
|
165
167
|
|
166
|
-
# @!attribute [rw] nullpath_ok
|
168
|
+
# @!attribute [rw] nullpath_ok?
|
167
169
|
# operations on open files and directories are ok to receive nil paths
|
168
170
|
#
|
169
171
|
# If this option is given the file-system handlers for the following operations will not receive path
|
@@ -179,18 +181,28 @@ module FFI
|
|
179
181
|
modules: :pointer,
|
180
182
|
debug: :bool_int
|
181
183
|
}
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
184
|
+
|
185
|
+
layout(spec)
|
186
|
+
|
187
|
+
# Find the attrs that have a corresponding setter (prefix set_ or suffix _set
|
188
|
+
# map attr => setter
|
189
|
+
setters = spec.keys
|
190
|
+
.map { |k| [k.to_s.sub(/^set_/, '').sub(/_set$/, '').to_sym, k] }
|
191
|
+
.reject { |(s, a)| s == a }
|
192
|
+
.to_h
|
193
|
+
|
194
|
+
ffi_attr_reader_method(*setters.keys) do
|
195
|
+
self[setters[__method__]] ? self[__method__] : nil
|
196
|
+
end
|
197
|
+
|
198
|
+
ffi_attr_writer_method(*setters.keys) do |val|
|
199
|
+
self[setters[__method__]] = !val.nil?
|
200
|
+
self[__method__] = val || 0
|
191
201
|
end
|
192
202
|
|
193
|
-
|
203
|
+
ffi_attr_reader(:show_help?, :debug?)
|
204
|
+
remaining = (spec.keys - setters.keys - setters.values - %i[show_help modules debug])
|
205
|
+
ffi_attr_accessor(*remaining.map { |a| spec[a] == :bool_int ? "#{a}?" : a })
|
194
206
|
end
|
195
207
|
end
|
196
208
|
end
|
@@ -326,7 +326,7 @@ module FFI
|
|
326
326
|
# or unwanted
|
327
327
|
# @return [Array<Symbol>]
|
328
328
|
# @see capable
|
329
|
-
|
329
|
+
ffi_attr_reader_method(:want) do |*caps, **h|
|
330
330
|
next self[:want] if caps.empty? && h.empty?
|
331
331
|
|
332
332
|
h.merge!(caps.pop) if caps.last.is_a?(Hash)
|
@@ -15,7 +15,8 @@ module FFI
|
|
15
15
|
base[:umask] = :mode_t if FUSE_VERSION >= 28
|
16
16
|
layout base
|
17
17
|
|
18
|
-
|
18
|
+
# Define readers, safe from null access
|
19
|
+
ffi_attr_reader_method(*members) do
|
19
20
|
m = __method__
|
20
21
|
|
21
22
|
# Use overrides if they are available, or the default context if the underlying memory is invalid
|