ffi-libfuse 0.0.1.rctest12 → 0.3.3
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/.yardopts +3 -1
- data/CHANGELOG.md +60 -0
- data/LICENSE +21 -0
- data/README.md +127 -44
- data/lib/ffi/accessors.rb +6 -6
- data/lib/ffi/boolean_int.rb +27 -0
- data/lib/ffi/devt.rb +23 -0
- data/lib/ffi/encoding.rb +38 -0
- data/lib/ffi/flock.rb +7 -5
- data/lib/ffi/gnu_extensions.rb +1 -1
- data/lib/ffi/libfuse/ackbar.rb +3 -3
- data/lib/ffi/libfuse/adapter/context.rb +12 -10
- data/lib/ffi/libfuse/adapter/fuse2_compat.rb +52 -51
- data/lib/ffi/libfuse/adapter/fuse3_support.rb +7 -4
- data/lib/ffi/libfuse/adapter/interrupt.rb +1 -1
- data/lib/ffi/libfuse/adapter/ruby.rb +499 -148
- data/lib/ffi/libfuse/adapter/safe.rb +12 -11
- data/lib/ffi/libfuse/adapter.rb +1 -2
- data/lib/ffi/libfuse/callbacks.rb +1 -1
- data/lib/ffi/libfuse/filesystem/accounting.rb +116 -0
- data/lib/ffi/libfuse/filesystem/mapped_dir.rb +74 -0
- data/lib/ffi/libfuse/filesystem/mapped_files.rb +141 -0
- data/lib/ffi/libfuse/filesystem/pass_through_dir.rb +55 -0
- data/lib/ffi/libfuse/filesystem/pass_through_file.rb +45 -0
- data/lib/ffi/libfuse/filesystem/utils.rb +102 -0
- data/lib/ffi/libfuse/filesystem/virtual_dir.rb +306 -0
- data/lib/ffi/libfuse/filesystem/virtual_file.rb +94 -0
- data/lib/ffi/libfuse/filesystem/virtual_fs.rb +196 -0
- data/lib/ffi/libfuse/filesystem/virtual_node.rb +101 -0
- data/lib/ffi/libfuse/filesystem.rb +25 -0
- data/lib/ffi/libfuse/fuse2.rb +32 -24
- data/lib/ffi/libfuse/fuse3.rb +28 -18
- data/lib/ffi/libfuse/fuse_args.rb +71 -34
- data/lib/ffi/libfuse/fuse_buffer.rb +128 -26
- data/lib/ffi/libfuse/fuse_callbacks.rb +1 -5
- data/lib/ffi/libfuse/fuse_common.rb +60 -61
- data/lib/ffi/libfuse/fuse_config.rb +134 -143
- data/lib/ffi/libfuse/fuse_conn_info.rb +310 -134
- data/lib/ffi/libfuse/fuse_context.rb +45 -3
- data/lib/ffi/libfuse/fuse_operations.rb +57 -21
- data/lib/ffi/libfuse/fuse_opt.rb +1 -1
- data/lib/ffi/libfuse/fuse_version.rb +10 -6
- data/lib/ffi/libfuse/gem_version.rb +54 -0
- data/lib/ffi/libfuse/main.rb +96 -48
- data/lib/ffi/libfuse/test_helper.rb +145 -0
- data/lib/ffi/libfuse/version.rb +1 -1
- data/lib/ffi/libfuse.rb +13 -4
- data/lib/ffi/ruby_object.rb +4 -1
- data/lib/ffi/stat/constants.rb +9 -0
- data/lib/ffi/stat/native.rb +36 -6
- data/lib/ffi/stat/time_spec.rb +26 -10
- data/lib/ffi/stat.rb +111 -22
- data/lib/ffi/stat_vfs.rb +59 -1
- data/lib/ffi/struct_wrapper.rb +22 -1
- data/sample/hello_fs.rb +54 -0
- data/sample/memory_fs.rb +5 -181
- data/sample/no_fs.rb +20 -21
- data/sample/pass_through_fs.rb +30 -0
- metadata +83 -10
- data/lib/ffi/libfuse/adapter/thread_local_context.rb +0 -36
- data/lib/ffi/libfuse/test/operations.rb +0 -56
- data/lib/ffi/libfuse/test.rb +0 -3
|
@@ -12,28 +12,36 @@ require_relative '../flock'
|
|
|
12
12
|
require_relative 'thread_pool'
|
|
13
13
|
require_relative '../stat'
|
|
14
14
|
require_relative '../struct_array'
|
|
15
|
+
require_relative '../encoding'
|
|
15
16
|
require_relative 'fuse_callbacks'
|
|
16
17
|
|
|
17
18
|
module FFI
|
|
18
19
|
# Ruby FFI Binding for [libfuse](https://github.com/libfuse/libfuse)
|
|
19
20
|
module Libfuse
|
|
21
|
+
# All paths are encoded in ruby's view of the filesystem encoding
|
|
22
|
+
typedef Encoding.for('filesystem'), :fs_string
|
|
23
|
+
|
|
20
24
|
# typedef int (*fuse_fill_dir_t) (void *buf, const char *name, const struct stat *stbuf, off_t off);
|
|
21
|
-
fill_dir_t_args = [:pointer, :
|
|
25
|
+
fill_dir_t_args = [:pointer, :fs_string, Stat.by_ref, :off_t]
|
|
22
26
|
if FUSE_MAJOR_VERSION > 2
|
|
23
27
|
enum :fuse_readdir_flags, [:fuse_readdir_plus, (1 << 0)]
|
|
24
28
|
enum :fuse_fill_dir_flags, [:fuse_fill_dir_plus, (1 << 1)]
|
|
25
29
|
fill_dir_t_args << :fuse_fill_dir_flags
|
|
26
30
|
end
|
|
27
31
|
|
|
32
|
+
bitmask :fuse_ioctl_flags, %i[compat unrestricted retry dir]
|
|
28
33
|
bitmask :lock_op, [:lock_sh, 0, :lock_ex, 2, :lock_nb, 4, :lock_un, 8]
|
|
29
34
|
bitmask :falloc_mode, %i[keep_size punch_hole no_hide_stale collapse_range zero_range insert_range unshare_range]
|
|
30
35
|
bitmask :flags_mask, %i[nullpath_ok nopath utime_omit_ok] if FUSE_MAJOR_VERSION < 3
|
|
31
|
-
|
|
36
|
+
|
|
37
|
+
# @!visibility private
|
|
38
|
+
XAttr = enum :xattr, [:xattr_create, 1, :xattr_replace]
|
|
39
|
+
|
|
32
40
|
callback :fill_dir_t, fill_dir_t_args, :int
|
|
33
41
|
|
|
34
42
|
# The file system operations as specified in libfuse.
|
|
35
43
|
#
|
|
36
|
-
# All Callback
|
|
44
|
+
# All Callback methods are optional, but some are essential for a useful filesystem
|
|
37
45
|
# e.g. {getattr},{readdir}
|
|
38
46
|
#
|
|
39
47
|
# Almost all callback operations take a path which can be of any length and will return 0 for success, or raise a
|
|
@@ -42,6 +50,14 @@ module FFI
|
|
|
42
50
|
class FuseOperations < FFI::Struct
|
|
43
51
|
include FuseCallbacks
|
|
44
52
|
|
|
53
|
+
# Callbacks that are expected to return meaningful positive integers
|
|
54
|
+
MEANINGFUL_RETURN = %i[read write write_buf lseek copy_file_range getxattr listxattr].freeze
|
|
55
|
+
|
|
56
|
+
# @return [Boolean] true if fuse_callback expects a meaningful integer return
|
|
57
|
+
def self.meaningful_return?(fuse_callback)
|
|
58
|
+
MEANINGFUL_RETURN.include?(fuse_callback)
|
|
59
|
+
end
|
|
60
|
+
|
|
45
61
|
# Container to dynamically build up the operations layout which is dependent on the loaded libfuse version
|
|
46
62
|
op = {}
|
|
47
63
|
|
|
@@ -144,7 +160,7 @@ module FFI
|
|
|
144
160
|
# @return [Integer] 0 for success or -ve errno
|
|
145
161
|
|
|
146
162
|
# int (*symlink) (const char *, const char *);
|
|
147
|
-
op[:symlink] = [:
|
|
163
|
+
op[:symlink] = [:fs_string]
|
|
148
164
|
|
|
149
165
|
# @!method rename(from_path,to_path)
|
|
150
166
|
# @abstract
|
|
@@ -155,7 +171,7 @@ module FFI
|
|
|
155
171
|
# @return [Integer] 0 for success or -ve errno
|
|
156
172
|
|
|
157
173
|
# int (*rename) (const char *, const char *);
|
|
158
|
-
op[:rename] = [:
|
|
174
|
+
op[:rename] = [:fs_string]
|
|
159
175
|
|
|
160
176
|
# @!method link(path,target)
|
|
161
177
|
# @abstract
|
|
@@ -166,7 +182,7 @@ module FFI
|
|
|
166
182
|
# @return [Integer] 0 for success or -ve errno
|
|
167
183
|
|
|
168
184
|
# int (*link) (const char *, const char *);
|
|
169
|
-
op[:link] = [:
|
|
185
|
+
op[:link] = [:fs_string]
|
|
170
186
|
|
|
171
187
|
# @!method chmod(path,mode,fuse_file_info=nil)
|
|
172
188
|
# @abstract
|
|
@@ -208,6 +224,7 @@ module FFI
|
|
|
208
224
|
op[:truncate] = [:off_t]
|
|
209
225
|
op[:truncate] << FuseFileInfo.by_ref if FUSE_MAJOR_VERSION >= 3
|
|
210
226
|
|
|
227
|
+
# Not directly implemented see utimens
|
|
211
228
|
# int (*utime) (const char *, struct utimbuf *);
|
|
212
229
|
op[:utime] = [:pointer] if FUSE_MAJOR_VERSION < 3
|
|
213
230
|
|
|
@@ -563,7 +580,7 @@ module FFI
|
|
|
563
580
|
# @param [String] path
|
|
564
581
|
# @param [FuseFileInfo] fuse_file_info
|
|
565
582
|
# For checking lock ownership, the 'fuse_file_info->owner' argument must be used.
|
|
566
|
-
# @param [Symbol] cmd either :
|
|
583
|
+
# @param [Symbol] cmd either :getlck, :setlck or :setlkw.
|
|
567
584
|
# @param [Flock] flock
|
|
568
585
|
# For the meaning of fields in 'struct flock' see the man page for fcntl(2). The whence field will always
|
|
569
586
|
# be set to :seek_set.
|
|
@@ -660,18 +677,21 @@ module FFI
|
|
|
660
677
|
# @param [Integer] cmd
|
|
661
678
|
# @param [FFI::Pointer] arg
|
|
662
679
|
# @param [FuseFileInfo] fuse_file_info
|
|
663
|
-
# @param [
|
|
664
|
-
# @param [FFI::Pointer] data
|
|
680
|
+
# @param [Array<Symbol>] flags
|
|
665
681
|
#
|
|
666
|
-
#
|
|
667
|
-
#
|
|
668
|
-
#
|
|
669
|
-
#
|
|
682
|
+
# - :compat 32bit compat ioctl on 64bit machine
|
|
683
|
+
# - :unrestricted not restricted to well-formed ioctls, retry allowed (lowlevel fuse)
|
|
684
|
+
# - :retry retry with new iovecs (lowlevel fuse)
|
|
685
|
+
# - :dir is a directory file handle
|
|
670
686
|
#
|
|
671
|
-
#
|
|
687
|
+
# @param [FFI::Pointer] data
|
|
688
|
+
#
|
|
689
|
+
# The size and direction of data is determined by _IOC_*() decoding of cmd. For _IOC_NONE, data will be NULL,
|
|
690
|
+
# for _IOC_WRITE data is out area, for _IOC_READ in area and if both are set in/out area. In all non-NULL
|
|
691
|
+
# cases, the area is of _IOC_SIZE(cmd) bytes.
|
|
672
692
|
|
|
673
693
|
# int (*ioctl) (const char *, int cmd, void *arg, struct fuse_file_info *, unsigned int flags, void *data);
|
|
674
|
-
op[:ioctl] = [:int, :pointer, FuseFileInfo.by_ref, :
|
|
694
|
+
op[:ioctl] = [:int, :pointer, FuseFileInfo.by_ref, :fuse_ioctl_flags, :pointer]
|
|
675
695
|
|
|
676
696
|
# @!method poll(path,fuse_file_info,ph,reventsp)
|
|
677
697
|
# @abstract
|
|
@@ -697,8 +717,9 @@ module FFI
|
|
|
697
717
|
# @abstract
|
|
698
718
|
# Write contents of buffer to an open file
|
|
699
719
|
#
|
|
700
|
-
# Similar to the write() method, but data is supplied in a generic buffer.
|
|
701
|
-
# data to
|
|
720
|
+
# Similar to the write() method, but data is supplied in a generic buffer.
|
|
721
|
+
# Use {FuseBufVec#copy_to_fd} to copy data to an open file descriptor, or {FuseBufVec#copy_to_str} to extract
|
|
722
|
+
# string data from the buffer
|
|
702
723
|
#
|
|
703
724
|
# @param [String] path
|
|
704
725
|
# @param [FuseBufVec] buf
|
|
@@ -804,8 +825,8 @@ module FFI
|
|
|
804
825
|
# );
|
|
805
826
|
op[:copy_file_range] =
|
|
806
827
|
callback [
|
|
807
|
-
:
|
|
808
|
-
:
|
|
828
|
+
:fs_string, FuseFileInfo.by_ref, :off_t,
|
|
829
|
+
:fs_string, FuseFileInfo.by_ref, :off_t,
|
|
809
830
|
:size_t, :int
|
|
810
831
|
], :ssize_t
|
|
811
832
|
|
|
@@ -820,7 +841,7 @@ module FFI
|
|
|
820
841
|
# @see lseek(2)
|
|
821
842
|
|
|
822
843
|
# off_t (*lseek) (const char *, off_t off, int whence, struct fuse_file_info *);
|
|
823
|
-
op[:lseek] = callback [:
|
|
844
|
+
op[:lseek] = callback [:fs_string, :off_t, Flock::Enums::SeekWhence, FuseFileInfo.by_ref], :off_t
|
|
824
845
|
end
|
|
825
846
|
end
|
|
826
847
|
|
|
@@ -829,7 +850,7 @@ module FFI
|
|
|
829
850
|
layout_data = op.transform_values do |v|
|
|
830
851
|
if v.is_a?(Array) && !v.last.is_a?(Integer)
|
|
831
852
|
# A typical fuse callback
|
|
832
|
-
callback([:
|
|
853
|
+
callback([:fs_string] + v, :int)
|
|
833
854
|
else
|
|
834
855
|
v
|
|
835
856
|
end
|
|
@@ -865,6 +886,21 @@ module FFI
|
|
|
865
886
|
fuse_flags.concat(delegate.fuse_flags) if delegate.respond_to?(:fuse_flags)
|
|
866
887
|
send(:[]=, :flags, fuse_flags.uniq)
|
|
867
888
|
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
|
|
868
904
|
end
|
|
869
905
|
end
|
|
870
906
|
end
|
data/lib/ffi/libfuse/fuse_opt.rb
CHANGED
|
@@ -19,7 +19,7 @@ module FFI
|
|
|
19
19
|
def fill(template, value)
|
|
20
20
|
str_ptr = FFI::MemoryPointer.from_string(template)
|
|
21
21
|
self[:template] = str_ptr
|
|
22
|
-
self[:offset] = (2**(
|
|
22
|
+
self[:offset] = (2**(FFI::Type::INT.size * 8)) - 1 # -(1U) in a LONG!!
|
|
23
23
|
self[:value] = value.to_i
|
|
24
24
|
self
|
|
25
25
|
end
|
|
@@ -9,15 +9,18 @@ module FFI
|
|
|
9
9
|
module Libfuse
|
|
10
10
|
extend FFI::Library
|
|
11
11
|
|
|
12
|
+
libs =
|
|
13
|
+
case FFI::Platform::NAME
|
|
14
|
+
when 'x86_64-darwin'
|
|
15
|
+
%w[libfuse.2.dylib]
|
|
16
|
+
else
|
|
17
|
+
%w[libfuse3.so.3 libfuse.so.2]
|
|
18
|
+
end
|
|
12
19
|
# The fuse library to load from 'LIBFUSE' environment variable if set, otherwise prefer Fuse3 over Fuse2
|
|
13
|
-
LIBFUSE = ENV
|
|
20
|
+
LIBFUSE = ENV.fetch('LIBFUSE', libs)
|
|
14
21
|
ffi_lib(LIBFUSE)
|
|
15
22
|
|
|
16
|
-
# @!
|
|
17
|
-
# @!method fuse_version()
|
|
18
|
-
# @return [Integer] the fuse version
|
|
19
|
-
# See {FUSE_VERSION} which captures this result in a constant
|
|
20
|
-
|
|
23
|
+
# @!visibility private
|
|
21
24
|
attach_function :fuse_version, [], :int
|
|
22
25
|
|
|
23
26
|
# prior to 3.10 this is Major * 10 + Minor, after 3.10 and later is Major * 100 + Minor
|
|
@@ -36,6 +39,7 @@ module FFI
|
|
|
36
39
|
require_relative '../gnu_extensions'
|
|
37
40
|
|
|
38
41
|
extend(GNUExtensions)
|
|
42
|
+
|
|
39
43
|
# libfuse2 has busted symbols
|
|
40
44
|
ffi_lib_versions(%w[FUSE_2.9.1 FUSE_2.9 FUSE_2.8 FUSE_2.7 FUSE_2.6 FUSE_2.5 FUSE_2.4 FUSE_2.3 FUSE_2.2])
|
|
41
45
|
end
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'version'
|
|
4
|
+
|
|
5
|
+
module FFI
|
|
6
|
+
# Ruby FFI Binding for [libfuse](https://github.com/libfuse/libfuse)
|
|
7
|
+
module Libfuse
|
|
8
|
+
# @!visibility private
|
|
9
|
+
|
|
10
|
+
SEMVER_TAG_REGEX = /^v\d+\.\d+\.\d+/.freeze
|
|
11
|
+
|
|
12
|
+
# branches the format is refs/heads/<branch_name>,
|
|
13
|
+
# tags it is refs/tags/<tag_name>.
|
|
14
|
+
# for pull requests it is refs/pull/<pr_number>/merge,
|
|
15
|
+
GIT_REF_TYPES = { 'heads' => :branch, 'tags' => :tag, 'pull' => :pull }.freeze
|
|
16
|
+
|
|
17
|
+
def self.git_ref(env: ENV)
|
|
18
|
+
ref = env.fetch('GIT_REF') do
|
|
19
|
+
# get branch ref, or detached head ref to tag
|
|
20
|
+
`git symbolic-ref HEAD 2>/dev/null || git name-rev HEAD | awk '{ gsub(/[\\^~@].*$/,"",$2); printf("refs/%s\\n",$2)}'`.strip # rubocop:disable Layout/LineLength
|
|
21
|
+
rescue StandardError
|
|
22
|
+
nil
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
return [ref, nil] unless ref&.start_with?('refs/')
|
|
26
|
+
|
|
27
|
+
_refs, ref_type, ref_name = ref.split('/', 3)
|
|
28
|
+
[ref_name, GIT_REF_TYPES[ref_type]]
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def self.gem_version(main_branch: 'main', version: VERSION, env: ENV)
|
|
32
|
+
ref_name, ref_type = git_ref(env: env)
|
|
33
|
+
|
|
34
|
+
version =
|
|
35
|
+
case ref_type
|
|
36
|
+
when :branch
|
|
37
|
+
ref_name == main_branch ? [version] : [version, ref_name]
|
|
38
|
+
when :tag
|
|
39
|
+
SEMVER_TAG_REGEX.match?(ref_name) ? [ref_name[1..]] : [version, ref_name]
|
|
40
|
+
when :pull
|
|
41
|
+
pr_number, merge, _rest = ref_name.split('/')
|
|
42
|
+
# GITHUB_BASE_REF The name of the base ref or target branch of the pull request in a workflow run
|
|
43
|
+
base_ref = env.fetch('GIT_BASE_REF', 'undefined')
|
|
44
|
+
[version, base_ref, "#{merge}#{pr_number}"]
|
|
45
|
+
else
|
|
46
|
+
[version, 'pre', ref_name]
|
|
47
|
+
end.select { |p| p && !p.empty? }.join('.').tr('//_-', '')
|
|
48
|
+
|
|
49
|
+
[version, ref_name, ref_type]
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
GEM_VERSION, GIT_REF_NAME, GIT_REF_TYPE = gem_version
|
|
53
|
+
end
|
|
54
|
+
end
|
data/lib/ffi/libfuse/main.rb
CHANGED
|
@@ -9,12 +9,26 @@ module FFI
|
|
|
9
9
|
# Controls the main run loop for a FUSE filesystem
|
|
10
10
|
module Main
|
|
11
11
|
class << self
|
|
12
|
+
# Builds default argument list for #{fuse_main} regardless of being called directly or from mount.fuse3
|
|
13
|
+
#
|
|
14
|
+
# @param [Array<String>] extra_args additional arguments to add to $0 and *ARGV
|
|
15
|
+
# @return [Array<String>]
|
|
16
|
+
# @see https://github.com/libfuse/libfuse/issues/621
|
|
17
|
+
def default_args(*extra_args)
|
|
18
|
+
args = ARGV.dup
|
|
19
|
+
|
|
20
|
+
# If called from mount.fuse3 we already have a 'source' argument which should go at args[0]
|
|
21
|
+
args.unshift($0) unless args.size >= 2 && args[0..1].all? { |a| !a.start_with?('-') }
|
|
22
|
+
args.concat(extra_args)
|
|
23
|
+
end
|
|
24
|
+
|
|
12
25
|
# Main function of FUSE
|
|
13
26
|
#
|
|
14
27
|
# This function:
|
|
15
28
|
#
|
|
16
29
|
# - parses command line options - see {fuse_parse_cmdline}
|
|
17
|
-
#
|
|
30
|
+
# exiting immediately if help or version options were processed
|
|
31
|
+
# - calls {#fuse_debug}, {#fuse_options}, {#fuse_configure} if implemented by operations
|
|
18
32
|
# - installs signal handlers for INT, HUP, TERM to unmount and exit filesystem
|
|
19
33
|
# - installs custom signal handlers if operations implements {fuse_traps}
|
|
20
34
|
# - creates a fuse handle mounted with registered operations - see {fuse_create}
|
|
@@ -22,7 +36,7 @@ module FFI
|
|
|
22
36
|
#
|
|
23
37
|
# @param [Array<String>] argv mount.fuse arguments
|
|
24
38
|
# expects progname, mountpoint, options....
|
|
25
|
-
# @param [FuseArgs] args
|
|
39
|
+
# @param [FuseArgs|Array<String>] args
|
|
26
40
|
# alternatively constructed args
|
|
27
41
|
# @param [Object|FuseOperations] operations
|
|
28
42
|
# something that responds to the fuse callbacks and optionally our abstract configuration methods
|
|
@@ -30,13 +44,18 @@ module FFI
|
|
|
30
44
|
# any data to be made available to the {FuseOperations#init} callback
|
|
31
45
|
#
|
|
32
46
|
# @return [Integer] suitable for process exit code
|
|
33
|
-
def fuse_main(*argv, operations:, args: argv, private_data: nil)
|
|
47
|
+
def fuse_main(*argv, operations:, args: argv.any? ? argv : default_args, private_data: nil)
|
|
34
48
|
run_args = fuse_parse_cmdline(args: args, handler: operations)
|
|
35
49
|
return 2 unless run_args
|
|
36
50
|
|
|
37
51
|
fuse_args = run_args.delete(:args)
|
|
38
52
|
mountpoint = run_args.delete(:mountpoint)
|
|
39
53
|
|
|
54
|
+
return 3 unless fuse_configure(operations: operations, **run_args)
|
|
55
|
+
|
|
56
|
+
warn "FuseCreate: mountpoint: #{mountpoint}, args: [#{fuse_args.argv.join(' ')}]" if run_args[:debug]
|
|
57
|
+
warn "FuseRun: #{run_args}" if run_args[:debug]
|
|
58
|
+
|
|
40
59
|
fuse = fuse_create(mountpoint, args: fuse_args, operations: operations, private_data: private_data)
|
|
41
60
|
|
|
42
61
|
return 0 if run_args[:show_help] || run_args[:show_version]
|
|
@@ -44,52 +63,43 @@ module FFI
|
|
|
44
63
|
|
|
45
64
|
return unless fuse
|
|
46
65
|
|
|
47
|
-
warn run_args.to_s if run_args[:debug]
|
|
48
|
-
|
|
49
66
|
fuse.run(**run_args)
|
|
50
67
|
end
|
|
68
|
+
alias main fuse_main
|
|
51
69
|
|
|
52
70
|
# Parse command line arguments
|
|
53
71
|
#
|
|
54
72
|
# - parses standard command line options (-d -s -h -V)
|
|
55
73
|
# will call {fuse_debug}, {fuse_version}, {fuse_help} if implemented by handler
|
|
56
|
-
# -
|
|
74
|
+
# - calls {fuse_options} for custom option processing if implemented by handler
|
|
57
75
|
# - records signal handlers if operations implements {fuse_traps}
|
|
58
76
|
# - parses standard fuse mount options
|
|
59
77
|
#
|
|
60
78
|
# @param [Array<String>] argv mount.fuse arguments
|
|
61
|
-
# expects progname,
|
|
79
|
+
# expects progname, mountpoint, options....
|
|
62
80
|
# @param [FuseArgs] args
|
|
63
81
|
# alternatively constructed args
|
|
64
82
|
# @param [Object] handler
|
|
65
83
|
# something that responds to our abstract configuration methods
|
|
66
|
-
# @param [Object] private_data passed to handler.fuse_opt_proc
|
|
67
|
-
#
|
|
68
84
|
# @return [Hash<Symbol,Object>]
|
|
69
|
-
# * fsname [String]: the fsspec from /etc/fstab
|
|
70
85
|
# * mountpoint [String]: the mountpoint argument
|
|
71
86
|
# * args [FuseArgs]: remaining fuse_args to pass to {fuse_create}
|
|
72
87
|
# * show_help [Boolean]: -h or --help
|
|
73
88
|
# * show_version [Boolean]: -v or --version
|
|
74
89
|
# * debug [Boolean]: -d
|
|
75
90
|
# * others are options to pass to {FuseCommon#run}
|
|
76
|
-
def fuse_parse_cmdline(*argv, args: argv
|
|
91
|
+
def fuse_parse_cmdline(*argv, args: argv.any? ? argv : default_args, handler: nil)
|
|
77
92
|
args = fuse_init_args(args)
|
|
78
93
|
|
|
79
94
|
# Parse args and print cmdline help
|
|
80
95
|
run_args = Fuse.parse_cmdline(args, handler: handler)
|
|
96
|
+
return nil unless run_args
|
|
81
97
|
|
|
82
|
-
|
|
83
|
-
if %i[fuse_options fuse_opt_proc].all? { |m| handler.respond_to?(m) }
|
|
84
|
-
parse_ok = args.parse!(handler.fuse_options, private_data) do |*p_args|
|
|
85
|
-
handler.fuse_opt_proc(*p_args)
|
|
86
|
-
end
|
|
87
|
-
return unless parse_ok
|
|
88
|
-
end
|
|
98
|
+
return nil if handler.respond_to?(:fuse_options) && !handler.fuse_options(args)
|
|
89
99
|
|
|
90
100
|
run_args[:traps] = handler.fuse_traps if handler.respond_to?(:fuse_traps)
|
|
91
101
|
|
|
92
|
-
|
|
102
|
+
return nil unless parse_run_options(args, run_args)
|
|
93
103
|
|
|
94
104
|
run_args[:args] = args
|
|
95
105
|
run_args
|
|
@@ -97,40 +107,41 @@ module FFI
|
|
|
97
107
|
|
|
98
108
|
# @return [FuseCommon|nil] the mounted filesystem or nil if not mounted
|
|
99
109
|
def fuse_create(mountpoint, *argv, operations:, args: nil, private_data: nil)
|
|
100
|
-
args = fuse_init_args(args || argv
|
|
110
|
+
args = fuse_init_args(args || argv)
|
|
101
111
|
|
|
102
112
|
operations = FuseOperations.new(delegate: operations) unless operations.is_a?(FuseOperations)
|
|
103
113
|
|
|
104
|
-
fuse = Fuse.new(mountpoint, args, operations, private_data)
|
|
114
|
+
fuse = Fuse.new(mountpoint.to_s, args, operations, private_data)
|
|
105
115
|
fuse if fuse.mounted?
|
|
106
116
|
end
|
|
107
117
|
|
|
108
|
-
#
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
118
|
+
# @!visibility private
|
|
119
|
+
def fuse_configure(operations:, show_help: false, show_version: false, **_)
|
|
120
|
+
return true unless operations.respond_to?(:fuse_configure) && !show_help && !show_version
|
|
121
|
+
|
|
122
|
+
# Provide sensible values for FuseContext in case this is referenced during configure
|
|
123
|
+
FFI::Libfuse::FuseContext.overrides do
|
|
124
|
+
operations.fuse_configure
|
|
125
|
+
true
|
|
126
|
+
rescue Error => e
|
|
127
|
+
warn e.message
|
|
128
|
+
false
|
|
129
|
+
rescue StandardError, ScriptError => e
|
|
130
|
+
warn "#{e.class.name}: #{e.message}\n\t#{e.backtrace.join("\n\t")}"
|
|
131
|
+
false
|
|
132
|
+
end
|
|
116
133
|
end
|
|
117
134
|
|
|
118
135
|
# @!visibility private
|
|
119
|
-
|
|
120
|
-
# Version text
|
|
121
136
|
def version
|
|
122
137
|
"#{name}: #{VERSION}"
|
|
123
138
|
end
|
|
124
139
|
|
|
140
|
+
private
|
|
141
|
+
|
|
125
142
|
def fuse_init_args(args)
|
|
126
143
|
if args.is_a?(Array)
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
# https://github.com/libfuse/libfuse/issues/621 handle "source" field sent from /etc/fstab via mount.fuse3
|
|
130
|
-
# if arg[1] and arg[2] are both non option fields then replace arg1 with -ofsname=<arg1>
|
|
131
|
-
unless args.size <= 2 || args[1]&.start_with?('-') || args[2]&.start_with?('-')
|
|
132
|
-
args[1] = "-ofsname=#{args[1]}"
|
|
133
|
-
end
|
|
144
|
+
warn "FuseArgs: #{args.join(' ')}" if args.include?('-d')
|
|
134
145
|
args = FuseArgs.create(*args)
|
|
135
146
|
end
|
|
136
147
|
|
|
@@ -138,23 +149,52 @@ module FFI
|
|
|
138
149
|
|
|
139
150
|
raise ArgumentError "fuse main args: must be Array<String> or #{FuseArgs.class.name}"
|
|
140
151
|
end
|
|
152
|
+
|
|
153
|
+
def parse_run_options(args, run_args)
|
|
154
|
+
args.parse!(RUN_OPTIONS) do |key:, value:, **|
|
|
155
|
+
run_args[key] = value
|
|
156
|
+
next :keep if (STANDARD_OPTIONS.values + %i[remember]).include?(key)
|
|
157
|
+
|
|
158
|
+
:discard
|
|
159
|
+
end
|
|
160
|
+
end
|
|
141
161
|
end
|
|
142
162
|
|
|
143
163
|
# @!group Abstract Configuration
|
|
144
164
|
|
|
145
|
-
# @!method fuse_options
|
|
165
|
+
# @!method fuse_options(args)
|
|
146
166
|
# @abstract
|
|
147
|
-
#
|
|
148
|
-
# @
|
|
149
|
-
|
|
150
|
-
# @!method fuse_opt_proc(data,arg,key,out)
|
|
151
|
-
# @abstract
|
|
152
|
-
# Process custom options
|
|
167
|
+
# Called to allow filesystem to handle custom options and observe standard mount options #
|
|
168
|
+
# @param [FuseArgs] args
|
|
169
|
+
# @return [Boolean] true if args parsed successfully
|
|
153
170
|
# @see FuseArgs#parse!
|
|
171
|
+
# @example
|
|
172
|
+
# OPTIONS = { 'config=' => :config, '-c ' => :config }
|
|
173
|
+
# def fuse_options(args)
|
|
174
|
+
# args.parse!(OPTIONS) do |key:, value:, out:, **opts|
|
|
175
|
+
#
|
|
176
|
+
# # raise errors for invalid config
|
|
177
|
+
# raise FFI::Libfuse::FuseArgs::Error, "Invalid config" unless valid_config?(key,value)
|
|
178
|
+
#
|
|
179
|
+
# # Configure the file system
|
|
180
|
+
# @config = value if key == :config
|
|
181
|
+
#
|
|
182
|
+
# #Optionally manipulate other arguments for fuse_mount() based on the current argument and state
|
|
183
|
+
# out.add('-oopt=val')
|
|
184
|
+
#
|
|
185
|
+
# # Custom options must be marked :handled otherwise fuse_mount() will fail with unknown arguments
|
|
186
|
+
# :handled
|
|
187
|
+
# end
|
|
188
|
+
# end
|
|
154
189
|
|
|
155
190
|
# @!method fuse_traps
|
|
156
191
|
# @abstract
|
|
157
|
-
# @return [Hash]
|
|
192
|
+
# @return [Hash<String|Symbol|Integer,String|Proc>]
|
|
193
|
+
# map of signal name or number to signal handler as per Signal.trap
|
|
194
|
+
# @example
|
|
195
|
+
# def fuse_traps
|
|
196
|
+
# { HUP: ->() { reload() }}
|
|
197
|
+
# end
|
|
158
198
|
|
|
159
199
|
# @!method fuse_version
|
|
160
200
|
# @abstract
|
|
@@ -162,14 +202,22 @@ module FFI
|
|
|
162
202
|
|
|
163
203
|
# @!method fuse_help
|
|
164
204
|
# @abstract
|
|
165
|
-
#
|
|
205
|
+
# Called as part of generating output for the -h option
|
|
206
|
+
# @return [String] help text to explain custom options
|
|
166
207
|
|
|
167
208
|
# @!method fuse_debug(enabled)
|
|
168
209
|
# @abstract
|
|
169
|
-
#
|
|
210
|
+
# Called to indicate to the filesystem whether debugging option is in use.
|
|
170
211
|
# @param [Boolean] enabled if -d option is in use
|
|
171
212
|
# @return [void]
|
|
172
213
|
|
|
214
|
+
# @!method fuse_configure
|
|
215
|
+
# @abstract
|
|
216
|
+
# Called immediately before the filesystem is mounted, after options have been parsed
|
|
217
|
+
#
|
|
218
|
+
# @raise [Error] to prevent the mount from proceeding
|
|
219
|
+
# @return [void]
|
|
220
|
+
|
|
173
221
|
# @!endgroup
|
|
174
222
|
|
|
175
223
|
# @!visibility private
|