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