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
@@ -1,43 +1,91 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require_relative '../accessors'
|
4
|
+
require_relative '../boolean_int'
|
4
5
|
|
5
6
|
module FFI
|
6
7
|
module Libfuse
|
7
|
-
#
|
8
|
-
# int clone_fd;
|
9
|
-
# unsigned int max_idle_threads;
|
10
|
-
# };
|
8
|
+
# For native fuse_loop_mt only
|
11
9
|
class FuseLoopConfig < FFI::Struct
|
12
10
|
include(FFI::Accessors)
|
13
11
|
|
14
|
-
|
15
|
-
clone_fd: :int,
|
16
|
-
max_idle: :int
|
17
|
-
)
|
18
|
-
|
19
|
-
# @!attribute [rw] clone_fd
|
12
|
+
# @!attribute [w] clone_fd?
|
20
13
|
# whether to use separate device fds for each thread (may increase performance)
|
21
14
|
# Unused by ffi-libfuse as we do not call fuse_loop_mt
|
22
15
|
# @return [Boolean]
|
23
|
-
ffi_attr_reader(:clone_fd) do |v|
|
24
|
-
v != 0
|
25
|
-
end
|
26
|
-
|
27
|
-
ffi_attr_writer(:clone_fd) do |v|
|
28
|
-
v ? 1 : 0
|
29
|
-
end
|
30
16
|
|
31
|
-
# @!attribute [
|
17
|
+
# @!attribute [w] max_idle_threads
|
32
18
|
# The maximum number of available worker threads before they start to get deleted when they become idle. If not
|
33
19
|
# specified, the default is 10.
|
34
20
|
#
|
35
21
|
# Adjusting this has performance implications; a very small number of threads in the pool will cause a lot of
|
36
22
|
# thread creation and deletion overhead and performance may suffer. When set to 0, a new thread will be created
|
37
23
|
# to service every operation.
|
38
|
-
#
|
24
|
+
# @deprecated at Fuse 3.12. Use max_threads instead
|
39
25
|
# @return [Integer] the maximum number of threads to leave idle
|
40
|
-
|
26
|
+
|
27
|
+
# @!attribute [w] max_threads
|
28
|
+
# @return [Integer]
|
29
|
+
# @since Fuse 3.12
|
30
|
+
|
31
|
+
if FUSE_VERSION >= 312
|
32
|
+
layout(
|
33
|
+
version_id: :int,
|
34
|
+
clone_fd: :bool_int,
|
35
|
+
max_idle_threads: :uint,
|
36
|
+
max_threads: :uint
|
37
|
+
)
|
38
|
+
|
39
|
+
Libfuse.attach_function :fuse_loop_cfg_create, [], by_ref
|
40
|
+
Libfuse.attach_function :fuse_loop_cfg_destroy, [:pointer], :void
|
41
|
+
|
42
|
+
ffi_attr_reader(:clone_fd?)
|
43
|
+
Libfuse.attach_function :fuse_loop_cfg_set_clone_fd, %i[pointer uint], :void
|
44
|
+
def clone_fd=(bool_val)
|
45
|
+
Libfuse.fuse_loop_cfg_set_clone_fd(to_ptr, bool_val ? 1 : 0)
|
46
|
+
end
|
47
|
+
|
48
|
+
ffi_attr_reader(:max_idle_threads)
|
49
|
+
Libfuse.attach_function :fuse_loop_cfg_set_idle_threads, %i[pointer uint], :uint
|
50
|
+
def max_idle_threads=(val)
|
51
|
+
Libfuse.fuse_loop_cfg_set_idle_threads(to_ptr, val) if val
|
52
|
+
end
|
53
|
+
|
54
|
+
Libfuse.attach_function :fuse_loop_cfg_set_max_threads, %i[pointer uint], :uint
|
55
|
+
ffi_attr_reader(:max_threads)
|
56
|
+
def max_threads=(val)
|
57
|
+
Libfuse.fuse_loop_cfg_set_max_threads(to_ptr, val) if val
|
58
|
+
end
|
59
|
+
|
60
|
+
class << self
|
61
|
+
def create(max_idle_threads: nil, max_threads: 10, clone_fd: false, **_)
|
62
|
+
cfg = Libfuse.fuse_loop_cfg_create
|
63
|
+
ObjectSpace.define_finalizer(cfg, finalizer(cfg.to_ptr))
|
64
|
+
cfg.clone_fd = clone_fd
|
65
|
+
cfg.max_idle_threads = max_idle_threads if max_idle_threads
|
66
|
+
cfg.max_threads = max_threads if max_threads
|
67
|
+
cfg
|
68
|
+
end
|
69
|
+
|
70
|
+
def finalizer(ptr)
|
71
|
+
proc { |_| Libfuse.fuse_loop_cfg_destroy(ptr) }
|
72
|
+
end
|
73
|
+
end
|
74
|
+
else
|
75
|
+
layout(
|
76
|
+
clone_fd: :bool_int,
|
77
|
+
max_idle_threads: :uint
|
78
|
+
)
|
79
|
+
|
80
|
+
ffi_attr_accessor(:clone_fd?)
|
81
|
+
ffi_attr_accessor(:max_idle_threads)
|
82
|
+
|
83
|
+
class << self
|
84
|
+
def create(clone_fd: false, max_idle_threads: 10, **_)
|
85
|
+
new.fill(max_idle_threads: max_idle_threads, clone_fd: clone_fd)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
41
89
|
end
|
42
90
|
end
|
43
91
|
end
|
@@ -1,19 +1,18 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require_relative 'fuse_version'
|
4
|
-
require_relative '../ruby_object'
|
5
4
|
require_relative 'fuse_conn_info'
|
6
|
-
require_relative '
|
5
|
+
require_relative 'fuse_config'
|
6
|
+
require_relative 'fuse_buf_vec'
|
7
7
|
require_relative 'fuse_context'
|
8
8
|
require_relative 'fuse_file_info'
|
9
9
|
require_relative 'fuse_poll_handle'
|
10
|
+
require_relative 'fuse_callbacks'
|
11
|
+
require_relative '../ruby_object'
|
10
12
|
require_relative '../stat_vfs'
|
11
13
|
require_relative '../flock'
|
12
|
-
require_relative 'thread_pool'
|
13
14
|
require_relative '../stat'
|
14
|
-
require_relative '../struct_array'
|
15
15
|
require_relative '../encoding'
|
16
|
-
require_relative 'fuse_callbacks'
|
17
16
|
|
18
17
|
module FFI
|
19
18
|
# Ruby FFI Binding for [libfuse](https://github.com/libfuse/libfuse)
|
@@ -44,18 +43,80 @@ module FFI
|
|
44
43
|
# All Callback methods are optional, but some are essential for a useful filesystem
|
45
44
|
# e.g. {getattr},{readdir}
|
46
45
|
#
|
47
|
-
# Almost all callback operations take a path which can be of any length and will return 0 for success
|
48
|
-
#
|
46
|
+
# Almost all callback operations take a path which can be of any length and will return 0 for success or a negative
|
47
|
+
# `Errno` value on failure.
|
49
48
|
#
|
50
49
|
class FuseOperations < FFI::Struct
|
51
50
|
include FuseCallbacks
|
52
51
|
|
52
|
+
# Callbacks that have no return value
|
53
|
+
VOID_RETURN = %i[init destroy].freeze
|
54
|
+
|
53
55
|
# Callbacks that are expected to return meaningful positive integers
|
54
56
|
MEANINGFUL_RETURN = %i[read write write_buf lseek copy_file_range getxattr listxattr].freeze
|
55
57
|
|
56
|
-
#
|
57
|
-
|
58
|
-
|
58
|
+
# @!visibility private
|
59
|
+
# Methods to handle the path argument for most {path_callbacks}
|
60
|
+
NODE_PATH_METHODS = %i[first shift unshift].freeze
|
61
|
+
|
62
|
+
# @!visibility private
|
63
|
+
# Methods to handle the path argument for {link}, {symlink} and {rename}
|
64
|
+
LINK_PATH_METHODS = %i[last pop push].freeze
|
65
|
+
|
66
|
+
# @!visibility private
|
67
|
+
CALLBACK_PATH_ARG_METHODS = Hash.new(NODE_PATH_METHODS).merge(
|
68
|
+
{
|
69
|
+
link: LINK_PATH_METHODS,
|
70
|
+
symlink: LINK_PATH_METHODS,
|
71
|
+
rename: LINK_PATH_METHODS
|
72
|
+
}
|
73
|
+
).freeze
|
74
|
+
|
75
|
+
class << self
|
76
|
+
# @return [Boolean] true if fuse_callback expects a meaningful integer return
|
77
|
+
def meaningful_return?(fuse_callback)
|
78
|
+
MEANINGFUL_RETURN.include?(fuse_callback)
|
79
|
+
end
|
80
|
+
|
81
|
+
# @return [Boolean] true if fuse_callback expects a void return
|
82
|
+
def void_return?(fuse_callback)
|
83
|
+
VOID_RETURN.include?(fuse_callback)
|
84
|
+
end
|
85
|
+
|
86
|
+
# Helper to determine how to handle the path argument for a path callback
|
87
|
+
# @param [Symbol] fuse_callback callback method name (must be one of #{path_callbacks})
|
88
|
+
# @return [Symbol, Symbol,Symbol] read, remove, add methods.
|
89
|
+
# [:last, :push, :pop] for :link, :symlink, :rename,
|
90
|
+
# [:first, :shift, :unshift] for everything else
|
91
|
+
# @example
|
92
|
+
# def wrap_callback(fuse_method, *args)
|
93
|
+
# read, remove, add = FFI::Libfuse::FuseOperations.path_arg_methods(fuse_method)
|
94
|
+
# path = args.send(read)
|
95
|
+
# # ... do something with path
|
96
|
+
#
|
97
|
+
# path = args.send(remove)
|
98
|
+
# # ... do something to make an alternate path
|
99
|
+
# args.send(add, adjusted_path)
|
100
|
+
# delegate.send(fuse_methoed, *args)
|
101
|
+
# end
|
102
|
+
def path_arg_methods(fuse_callback)
|
103
|
+
CALLBACK_PATH_ARG_METHODS[fuse_callback]
|
104
|
+
end
|
105
|
+
|
106
|
+
# @return [Set<Symbol>] list of callback methods
|
107
|
+
def fuse_callbacks
|
108
|
+
@fuse_callbacks ||= Set.new(members - [:flags])
|
109
|
+
end
|
110
|
+
|
111
|
+
# @return [Set<Symbol>] list of path callback methods
|
112
|
+
def path_callbacks
|
113
|
+
@path_callbacks ||= fuse_callbacks - VOID_RETURN
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
# @!visibility private
|
118
|
+
def fuse_callbacks
|
119
|
+
self.class.fuse_callbacks
|
59
120
|
end
|
60
121
|
|
61
122
|
# Container to dynamically build up the operations layout which is dependent on the loaded libfuse version
|
@@ -151,12 +212,11 @@ module FFI
|
|
151
212
|
# int (*rmdir) (const char *);
|
152
213
|
op[:rmdir] = []
|
153
214
|
|
154
|
-
# @!method symlink(path
|
215
|
+
# @!method symlink(target, path)
|
155
216
|
# @abstract
|
156
217
|
# Create a symbolic link
|
218
|
+
# @param [String] target
|
157
219
|
# @param [String] path
|
158
|
-
# @param [String] target the link target
|
159
|
-
#
|
160
220
|
# @return [Integer] 0 for success or -ve errno
|
161
221
|
|
162
222
|
# int (*symlink) (const char *, const char *);
|
@@ -173,13 +233,13 @@ module FFI
|
|
173
233
|
# int (*rename) (const char *, const char *);
|
174
234
|
op[:rename] = [:fs_string]
|
175
235
|
|
176
|
-
# @!method link(path
|
236
|
+
# @!method link(target, path)
|
177
237
|
# @abstract
|
178
238
|
# Create a hard link to a file
|
179
|
-
# @param [String] path
|
180
239
|
# @param [String] target
|
181
|
-
#
|
240
|
+
# @param [String] path
|
182
241
|
# @return [Integer] 0 for success or -ve errno
|
242
|
+
# @see rename(2)
|
183
243
|
|
184
244
|
# int (*link) (const char *, const char *);
|
185
245
|
op[:link] = [:fs_string]
|
@@ -486,7 +546,6 @@ module FFI
|
|
486
546
|
# fuse3: void *(*init) (struct fuse_conn_info *conn, struct fuse_config *cfg);
|
487
547
|
op[:init] =
|
488
548
|
if FUSE_MAJOR_VERSION >= 3
|
489
|
-
require_relative 'fuse_config'
|
490
549
|
callback([FuseConnInfo.by_ref, FuseConfig.by_ref], RubyObject)
|
491
550
|
else
|
492
551
|
callback([FuseConnInfo.by_ref], RubyObject)
|
@@ -618,7 +677,7 @@ module FFI
|
|
618
677
|
#
|
619
678
|
|
620
679
|
# int (*utimens) (const char *, const struct timespec tv[2]);
|
621
|
-
op[:utimens] = [FFI::Stat::TimeSpec
|
680
|
+
op[:utimens] = [FFI::Stat::TimeSpec[2]]
|
622
681
|
op[:utimens] << FuseFileInfo.by_ref if FUSE_MAJOR_VERSION >= 3
|
623
682
|
|
624
683
|
# @!method bmap(path,blocksize,index)
|
@@ -655,17 +714,19 @@ module FFI
|
|
655
714
|
# release, fsync, readdir, releasedir, fsyncdir, ftruncate, fgetattr, lock, ioctl and poll
|
656
715
|
#
|
657
716
|
# Closely related to flag_nullpath_ok, but if this flag is set then the path will not be calculaged even if
|
658
|
-
# the file
|
659
|
-
# reason.
|
717
|
+
# the file wasn't unlinked. However the path can still be non-NULL if it needs to be calculated for some
|
718
|
+
# other reason.
|
660
719
|
#
|
661
720
|
# - :utime_omit_ok
|
662
721
|
#
|
663
722
|
# Flag indicating that the filesystem accepts special UTIME_NOW and UTIME_OMIT values in its utimens
|
664
723
|
# operation.
|
665
724
|
#
|
666
|
-
# @return [Array
|
725
|
+
# @return [Array<Symbol>] a list of flags to set capabilities
|
667
726
|
# @note Not available in Fuse3
|
668
727
|
# @deprecated in Fuse3 use fuse_config object in {init}
|
728
|
+
|
729
|
+
# flags
|
669
730
|
op[:flags] = :flags_mask if FUSE_MAJOR_VERSION < 3
|
670
731
|
|
671
732
|
if FUSE_VERSION >= 28
|
@@ -717,7 +778,7 @@ module FFI
|
|
717
778
|
# @abstract
|
718
779
|
# Write contents of buffer to an open file
|
719
780
|
#
|
720
|
-
# Similar to the write
|
781
|
+
# Similar to the {write} method, but data is supplied in a generic buffer.
|
721
782
|
# Use {FuseBufVec#copy_to_fd} to copy data to an open file descriptor, or {FuseBufVec#copy_to_str} to extract
|
722
783
|
# string data from the buffer
|
723
784
|
#
|
@@ -734,16 +795,15 @@ module FFI
|
|
734
795
|
# @!method read_buf(path,bufp,size,offset,fuse_file_info)
|
735
796
|
# @abstract
|
736
797
|
#
|
737
|
-
# Similar to the read
|
798
|
+
# Similar to the {read} method, but data is stored and returned in a generic buffer.
|
738
799
|
#
|
739
800
|
# No actual copying of data has to take place, the source file descriptor may simply be stored in the buffer
|
740
801
|
# for later data transfer.
|
741
802
|
#
|
742
803
|
# @param [String] path
|
743
804
|
# @param [FFI::Pointer<FuseBufVec>] bufp
|
744
|
-
# The buffer must be allocated dynamically
|
745
|
-
#
|
746
|
-
# the caller.
|
805
|
+
# The buffer must be allocated dynamically ({FuseBufVec.init})
|
806
|
+
# and stored at the location pointed to by bufp (see {FuseBufVec#store_to}(bufp)).
|
747
807
|
# @param [Integer] size
|
748
808
|
# @param [Integer] offset
|
749
809
|
# @param [FuseFileInfo] fuse_file_info
|
@@ -886,21 +946,6 @@ module FFI
|
|
886
946
|
fuse_flags.concat(delegate.fuse_flags) if delegate.respond_to?(:fuse_flags)
|
887
947
|
send(:[]=, :flags, fuse_flags.uniq)
|
888
948
|
end
|
889
|
-
|
890
|
-
# @!visibility private
|
891
|
-
def fuse_callbacks
|
892
|
-
self.class.fuse_callbacks
|
893
|
-
end
|
894
|
-
|
895
|
-
# @return [Set<Symbol>] list of callback methods
|
896
|
-
def self.fuse_callbacks
|
897
|
-
@fuse_callbacks ||= Set.new(members - [:flags])
|
898
|
-
end
|
899
|
-
|
900
|
-
# @return [Set<Symbol>] list of path callback methods
|
901
|
-
def self.path_callbacks
|
902
|
-
@path_callbacks ||= fuse_callbacks - %i[init destroy]
|
903
|
-
end
|
904
949
|
end
|
905
950
|
end
|
906
951
|
end
|
@@ -5,13 +5,6 @@ module FFI
|
|
5
5
|
module Libfuse
|
6
6
|
# @!visibility private
|
7
7
|
class GemHelper
|
8
|
-
SEMVER_TAG_REGEX = /^v\d+\.\d+\.\d+/.freeze
|
9
|
-
|
10
|
-
# branches the format is refs/heads/<branch_name>,
|
11
|
-
# tags it is refs/tags/<tag_name>.
|
12
|
-
# for pull requests it is refs/pull/<pr_number>/merge,
|
13
|
-
GIT_REF_TYPES = { 'heads' => :branch, 'tags' => :tag, 'pull' => :pull }.freeze
|
14
|
-
|
15
8
|
class << self
|
16
9
|
# set when install'd.
|
17
10
|
attr_accessor :instance
|
@@ -33,7 +26,7 @@ module FFI
|
|
33
26
|
return [ref, nil] unless ref&.start_with?('refs/')
|
34
27
|
|
35
28
|
_refs, ref_type, ref_name = ref.split('/', 3)
|
36
|
-
[ref_name,
|
29
|
+
[ref_name, { 'heads' => :branch, 'tags' => :tag, 'pull' => :pull }[ref_type]]
|
37
30
|
end
|
38
31
|
|
39
32
|
def gem_version(main_branch:, version:, env: ENV)
|
@@ -44,7 +37,7 @@ module FFI
|
|
44
37
|
when :branch
|
45
38
|
ref_name == main_branch ? [version] : [version, ref_name]
|
46
39
|
when :tag
|
47
|
-
|
40
|
+
/^v\d+\.\d+\.\d+/.match?(ref_name) ? [ref_name[1..]] : [version, ref_name]
|
48
41
|
when :pull
|
49
42
|
pr_number, merge, _rest = ref_name.split('/')
|
50
43
|
# GITHUB_BASE_REF The name of the base ref or target branch of the pull request in a workflow run
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module FFI
|
4
|
+
module Libfuse
|
5
|
+
# Helpers for reading/writing to io like objects
|
6
|
+
module IO
|
7
|
+
class << self
|
8
|
+
# Helper to convert a ruby object to size bytes required by {FuseOperations#read}
|
9
|
+
# @param [#pread, #seek & #read, #to_s] io the source to read from, either...
|
10
|
+
#
|
11
|
+
# * an {::IO} like object via :pread(size, offset) or :seek(offset) then :read(size)
|
12
|
+
# * or the String from :to_s
|
13
|
+
#
|
14
|
+
# @param [Integer] size
|
15
|
+
# @param [Integer, nil] offset
|
16
|
+
# if nil the io is assumed to be already positioned
|
17
|
+
# @return [String] the extracted data
|
18
|
+
def read(io, size, offset = nil)
|
19
|
+
return io.pread(size, offset) if offset && io.respond_to?(:pread)
|
20
|
+
|
21
|
+
if (offset ? %i[seek read] : %i[read]).all? { |m| io.respond_to?(m) }
|
22
|
+
io.seek(offset) if offset
|
23
|
+
return io.read(size)
|
24
|
+
end
|
25
|
+
|
26
|
+
io.to_s[offset || 0, size] || ''
|
27
|
+
end
|
28
|
+
|
29
|
+
# Helper to write date to {::IO} or {::String} like objects for use with #{FuseOperations#write}
|
30
|
+
# @param [#pwrite, #seek & #write, #[]=] io an object that accepts String data via...
|
31
|
+
#
|
32
|
+
# * ```ruby io.pwrite(data, offset)```
|
33
|
+
# * ```ruby io.seek(offset) ; io.write(data)```
|
34
|
+
# * ```ruby io[offset, data.size] = data```
|
35
|
+
# @param [String] data
|
36
|
+
# @param [nil, Integer] offset
|
37
|
+
# if not nil start start writing at this position in io
|
38
|
+
# @return [Integer] number of bytes written
|
39
|
+
# @raise [Errno::EBADF] if io does not support the requisite methods
|
40
|
+
def write(io, data, offset = nil)
|
41
|
+
if offset && io.respond_to?(:pwrite)
|
42
|
+
io.pwrite(data, offset)
|
43
|
+
elsif (offset ? %i[seek write] : %i[write]).all? { |m| io.respond_to?(m) }
|
44
|
+
io.seek(offset) if offset
|
45
|
+
io.write(data)
|
46
|
+
elsif io.respond_to?(:[]=) # eg String
|
47
|
+
io[offset || 0, data.size] = data
|
48
|
+
data.size
|
49
|
+
else
|
50
|
+
raise "cannot :pwrite or :write to #{io}", Errno::EBADF
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
data/lib/ffi/libfuse/main.rb
CHANGED
@@ -24,15 +24,13 @@ module FFI
|
|
24
24
|
|
25
25
|
# Main function of FUSE
|
26
26
|
#
|
27
|
-
# This function:
|
28
|
-
#
|
29
27
|
# - parses command line options - see {fuse_parse_cmdline}
|
30
|
-
#
|
31
|
-
# -
|
32
|
-
# -
|
33
|
-
# -
|
34
|
-
# -
|
35
|
-
# - calls
|
28
|
+
# - calls {#fuse_configure} if implemented by operations
|
29
|
+
# - creates a fuse handle see {fuse_create}
|
30
|
+
# - returns 0 if help or version options were processed (ie after all messages have been printed by libfuse)
|
31
|
+
# - returns 2 if fuse handle is not successfully mounted
|
32
|
+
# - calls {#fuse_traps} if implemented by operations
|
33
|
+
# - calls run on the fuse handle with options from previous steps- see {FuseCommon#run}
|
36
34
|
#
|
37
35
|
# @param [Array<String>] argv mount.fuse arguments
|
38
36
|
# expects progname, mountpoint, options....
|
@@ -46,23 +44,23 @@ module FFI
|
|
46
44
|
# @return [Integer] suitable for process exit code
|
47
45
|
def fuse_main(*argv, operations:, args: argv.any? ? argv : default_args, private_data: nil)
|
48
46
|
run_args = fuse_parse_cmdline(args: args, handler: operations)
|
49
|
-
return 2 unless run_args
|
50
47
|
|
51
48
|
fuse_args = run_args.delete(:args)
|
52
49
|
mountpoint = run_args.delete(:mountpoint)
|
53
50
|
|
54
|
-
|
51
|
+
show_only = run_args[:show_help] || run_args[:show_version]
|
52
|
+
|
53
|
+
return 3 if !show_only && !fuse_configure(operations)
|
55
54
|
|
56
55
|
warn "FuseCreate: mountpoint: #{mountpoint}, args: [#{fuse_args.argv.join(' ')}]" if run_args[:debug]
|
57
56
|
warn "FuseRun: #{run_args}" if run_args[:debug]
|
58
57
|
|
59
58
|
fuse = fuse_create(mountpoint, args: fuse_args, operations: operations, private_data: private_data)
|
60
59
|
|
61
|
-
return 0 if
|
60
|
+
return 0 if show_only
|
62
61
|
return 2 if !fuse || !mountpoint
|
63
62
|
|
64
|
-
|
65
|
-
|
63
|
+
run_args[:traps] = operations.fuse_traps if operations.respond_to?(:fuse_traps)
|
66
64
|
fuse.run(**run_args)
|
67
65
|
end
|
68
66
|
alias main fuse_main
|
@@ -72,7 +70,6 @@ module FFI
|
|
72
70
|
# - parses standard command line options (-d -s -h -V)
|
73
71
|
# will call {fuse_debug}, {fuse_version}, {fuse_help} if implemented by handler
|
74
72
|
# - calls {fuse_options} for custom option processing if implemented by handler
|
75
|
-
# - records signal handlers if operations implements {fuse_traps}
|
76
73
|
# - parses standard fuse mount options
|
77
74
|
#
|
78
75
|
# @param [Array<String>] argv mount.fuse arguments
|
@@ -88,24 +85,25 @@ module FFI
|
|
88
85
|
# * show_version [Boolean]: -v or --version
|
89
86
|
# * debug [Boolean]: -d
|
90
87
|
# * others are options to pass to {FuseCommon#run}
|
88
|
+
# @return [nil] if args are not parsed successfully
|
91
89
|
def fuse_parse_cmdline(*argv, args: argv.any? ? argv : default_args, handler: nil)
|
92
90
|
args = fuse_init_args(args)
|
93
91
|
|
94
92
|
# Parse args and print cmdline help
|
95
93
|
run_args = Fuse.parse_cmdline(args, handler: handler)
|
96
|
-
return nil unless run_args
|
97
94
|
|
98
|
-
|
95
|
+
handler.fuse_options(args) if handler.respond_to?(:fuse_options)
|
99
96
|
|
100
|
-
run_args
|
101
|
-
|
102
|
-
return nil unless parse_run_options(args, run_args)
|
97
|
+
parse_run_options(args, run_args)
|
103
98
|
|
104
99
|
run_args[:args] = args
|
105
100
|
run_args
|
101
|
+
rescue Error
|
102
|
+
nil
|
106
103
|
end
|
107
104
|
|
108
|
-
# @return [FuseCommon
|
105
|
+
# @return [FuseCommon] the mounted filesystem handle
|
106
|
+
# @return [nil] if not mounted (eg due to --help or --version, or an error)
|
109
107
|
def fuse_create(mountpoint, *argv, operations:, args: nil, private_data: nil)
|
110
108
|
args = fuse_init_args(args || argv)
|
111
109
|
|
@@ -116,8 +114,8 @@ module FFI
|
|
116
114
|
end
|
117
115
|
|
118
116
|
# @!visibility private
|
119
|
-
def fuse_configure(operations
|
120
|
-
return true unless operations.respond_to?(:fuse_configure)
|
117
|
+
def fuse_configure(operations)
|
118
|
+
return true unless operations.respond_to?(:fuse_configure)
|
121
119
|
|
122
120
|
# Provide sensible values for FuseContext in case this is referenced during configure
|
123
121
|
FFI::Libfuse::FuseContext.overrides do
|
@@ -166,7 +164,8 @@ module FFI
|
|
166
164
|
# @abstract
|
167
165
|
# Called to allow filesystem to handle custom options and observe standard mount options #
|
168
166
|
# @param [FuseArgs] args
|
169
|
-
# @
|
167
|
+
# @raise [Error] if there is an error parsing the options
|
168
|
+
# @return [void]
|
170
169
|
# @see FuseArgs#parse!
|
171
170
|
# @example
|
172
171
|
# OPTIONS = { 'config=' => :config, '-c ' => :config }
|
@@ -174,7 +173,7 @@ module FFI
|
|
174
173
|
# args.parse!(OPTIONS) do |key:, value:, out:, **opts|
|
175
174
|
#
|
176
175
|
# # raise errors for invalid config
|
177
|
-
# raise FFI::Libfuse::
|
176
|
+
# raise FFI::Libfuse::Error, "Invalid config" unless valid_config?(key,value)
|
178
177
|
#
|
179
178
|
# # Configure the file system
|
180
179
|
# @config = value if key == :config
|
@@ -189,15 +188,22 @@ module FFI
|
|
189
188
|
|
190
189
|
# @!method fuse_traps
|
191
190
|
# @abstract
|
192
|
-
#
|
191
|
+
# Passed to {FuseCommon#run} to allow filesystem to handle custom signal traps. These traps
|
192
|
+
# are merged over those from {FuseCommon#default_traps}. A nil value can be used to avoid a default trap
|
193
|
+
# being set.
|
194
|
+
# @return [Hash<String|Symbol|Integer,String|Proc|nil>]
|
193
195
|
# map of signal name or number to signal handler as per Signal.trap
|
194
196
|
# @example
|
195
197
|
# def fuse_traps
|
196
|
-
# {
|
198
|
+
# {
|
199
|
+
# HUP: ->() { reload() },
|
200
|
+
# INT: nil
|
201
|
+
# }
|
197
202
|
# end
|
198
203
|
|
199
204
|
# @!method fuse_version
|
200
205
|
# @abstract
|
206
|
+
# Called as part of generating output for the -V option
|
201
207
|
# @return [String] a custom version string to output with -V option
|
202
208
|
|
203
209
|
# @!method fuse_help
|
@@ -214,6 +220,7 @@ module FFI
|
|
214
220
|
# @!method fuse_configure
|
215
221
|
# @abstract
|
216
222
|
# Called immediately before the filesystem is mounted, after options have been parsed
|
223
|
+
# (eg to validate required options)
|
217
224
|
#
|
218
225
|
# @raise [Error] to prevent the mount from proceeding
|
219
226
|
# @return [void]
|