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