ffi-libfuse 0.0.1.pre → 0.1.0.rc20220550
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/CHANGES.md +14 -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/gnu_extensions.rb +1 -1
- data/lib/ffi/libfuse/ackbar.rb +6 -8
- 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 +0 -1
- data/lib/ffi/libfuse/adapter/ruby.rb +499 -148
- data/lib/ffi/libfuse/adapter/safe.rb +1 -1
- 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 +188 -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 +21 -21
- data/lib/ffi/libfuse/fuse3.rb +12 -12
- data/lib/ffi/libfuse/fuse_args.rb +69 -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 +55 -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 +43 -19
- data/lib/ffi/libfuse/fuse_version.rb +10 -6
- data/lib/ffi/libfuse/main.rb +80 -37
- data/lib/ffi/libfuse/thread_pool.rb +1 -1
- data/lib/ffi/libfuse/version.rb +1 -1
- data/lib/ffi/libfuse.rb +13 -4
- data/lib/ffi/ruby_object.rb +1 -1
- data/lib/ffi/stat/constants.rb +9 -0
- data/lib/ffi/stat/native.rb +36 -6
- data/lib/ffi/stat/time_spec.rb +28 -12
- 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 +66 -7
- data/lib/ffi/libfuse/adapter/thread_local_context.rb +0 -36
@@ -12,19 +12,24 @@ 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
|
@@ -33,7 +38,7 @@ module FFI
|
|
33
38
|
|
34
39
|
# The file system operations as specified in libfuse.
|
35
40
|
#
|
36
|
-
# All Callback
|
41
|
+
# All Callback methods are optional, but some are essential for a useful filesystem
|
37
42
|
# e.g. {getattr},{readdir}
|
38
43
|
#
|
39
44
|
# Almost all callback operations take a path which can be of any length and will return 0 for success, or raise a
|
@@ -144,7 +149,7 @@ module FFI
|
|
144
149
|
# @return [Integer] 0 for success or -ve errno
|
145
150
|
|
146
151
|
# int (*symlink) (const char *, const char *);
|
147
|
-
op[:symlink] = [:
|
152
|
+
op[:symlink] = [:fs_string]
|
148
153
|
|
149
154
|
# @!method rename(from_path,to_path)
|
150
155
|
# @abstract
|
@@ -155,7 +160,7 @@ module FFI
|
|
155
160
|
# @return [Integer] 0 for success or -ve errno
|
156
161
|
|
157
162
|
# int (*rename) (const char *, const char *);
|
158
|
-
op[:rename] = [:
|
163
|
+
op[:rename] = [:fs_string]
|
159
164
|
|
160
165
|
# @!method link(path,target)
|
161
166
|
# @abstract
|
@@ -166,7 +171,7 @@ module FFI
|
|
166
171
|
# @return [Integer] 0 for success or -ve errno
|
167
172
|
|
168
173
|
# int (*link) (const char *, const char *);
|
169
|
-
op[:link] = [:
|
174
|
+
op[:link] = [:fs_string]
|
170
175
|
|
171
176
|
# @!method chmod(path,mode,fuse_file_info=nil)
|
172
177
|
# @abstract
|
@@ -660,18 +665,21 @@ module FFI
|
|
660
665
|
# @param [Integer] cmd
|
661
666
|
# @param [FFI::Pointer] arg
|
662
667
|
# @param [FuseFileInfo] fuse_file_info
|
663
|
-
# @param [
|
664
|
-
# @param [FFI::Pointer] data
|
668
|
+
# @param [Array<Symbol>] flags
|
665
669
|
#
|
666
|
-
#
|
667
|
-
#
|
668
|
-
#
|
669
|
-
#
|
670
|
+
# - :compat 32bit compat ioctl on 64bit machine
|
671
|
+
# - :unrestricted not restricted to well-formed ioctls, retry allowed (lowlevel fuse)
|
672
|
+
# - :retry retry with new iovecs (lowlevel fuse)
|
673
|
+
# - :dir is a directory file handle
|
670
674
|
#
|
671
|
-
#
|
675
|
+
# @param [FFI::Pointer] data
|
676
|
+
#
|
677
|
+
# The size and direction of data is determined by _IOC_*() decoding of cmd. For _IOC_NONE, data will be NULL,
|
678
|
+
# for _IOC_WRITE data is out area, for _IOC_READ in area and if both are set in/out area. In all non-NULL
|
679
|
+
# cases, the area is of _IOC_SIZE(cmd) bytes.
|
672
680
|
|
673
681
|
# 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, :
|
682
|
+
op[:ioctl] = [:int, :pointer, FuseFileInfo.by_ref, :fuse_ioctl_flags, :pointer]
|
675
683
|
|
676
684
|
# @!method poll(path,fuse_file_info,ph,reventsp)
|
677
685
|
# @abstract
|
@@ -697,8 +705,9 @@ module FFI
|
|
697
705
|
# @abstract
|
698
706
|
# Write contents of buffer to an open file
|
699
707
|
#
|
700
|
-
# Similar to the write() method, but data is supplied in a generic buffer.
|
701
|
-
# data to
|
708
|
+
# Similar to the write() method, but data is supplied in a generic buffer.
|
709
|
+
# Use {FuseBufVec#copy_to_fd} to copy data to an open file descriptor, or {FuseBufVec#copy_to_str} to extract
|
710
|
+
# string data from the buffer
|
702
711
|
#
|
703
712
|
# @param [String] path
|
704
713
|
# @param [FuseBufVec] buf
|
@@ -804,8 +813,8 @@ module FFI
|
|
804
813
|
# );
|
805
814
|
op[:copy_file_range] =
|
806
815
|
callback [
|
807
|
-
:
|
808
|
-
:
|
816
|
+
:fs_string, FuseFileInfo.by_ref, :off_t,
|
817
|
+
:fs_string, FuseFileInfo.by_ref, :off_t,
|
809
818
|
:size_t, :int
|
810
819
|
], :ssize_t
|
811
820
|
|
@@ -820,7 +829,7 @@ module FFI
|
|
820
829
|
# @see lseek(2)
|
821
830
|
|
822
831
|
# off_t (*lseek) (const char *, off_t off, int whence, struct fuse_file_info *);
|
823
|
-
op[:lseek] = callback [:
|
832
|
+
op[:lseek] = callback [:fs_string, :off_t, Flock::Enums::SeekWhence, FuseFileInfo.by_ref], :off_t
|
824
833
|
end
|
825
834
|
end
|
826
835
|
|
@@ -829,7 +838,7 @@ module FFI
|
|
829
838
|
layout_data = op.transform_values do |v|
|
830
839
|
if v.is_a?(Array) && !v.last.is_a?(Integer)
|
831
840
|
# A typical fuse callback
|
832
|
-
callback([:
|
841
|
+
callback([:fs_string] + v, :int)
|
833
842
|
else
|
834
843
|
v
|
835
844
|
end
|
@@ -865,6 +874,21 @@ module FFI
|
|
865
874
|
fuse_flags.concat(delegate.fuse_flags) if delegate.respond_to?(:fuse_flags)
|
866
875
|
send(:[]=, :flags, fuse_flags.uniq)
|
867
876
|
end
|
877
|
+
|
878
|
+
# @!visibility private
|
879
|
+
def fuse_callbacks
|
880
|
+
self.class.fuse_callbacks
|
881
|
+
end
|
882
|
+
|
883
|
+
# @return [Set<Symbol>] list of callback methods
|
884
|
+
def self.fuse_callbacks
|
885
|
+
@fuse_callbacks ||= Set.new(members - [:flags])
|
886
|
+
end
|
887
|
+
|
888
|
+
# @return [Set<Symbol>] list of path callback methods
|
889
|
+
def self.path_callbacks
|
890
|
+
@path_callbacks ||= fuse_callbacks - %i[init destroy]
|
891
|
+
end
|
868
892
|
end
|
869
893
|
end
|
870
894
|
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
|
data/lib/ffi/libfuse/main.rb
CHANGED
@@ -14,7 +14,8 @@ module FFI
|
|
14
14
|
# This function:
|
15
15
|
#
|
16
16
|
# - parses command line options - see {fuse_parse_cmdline}
|
17
|
-
#
|
17
|
+
# exiting immediately if help or version options were processed
|
18
|
+
# - calls {#fuse_debug}, {#fuse_options}, {#fuse_configure} if implemented by operations
|
18
19
|
# - installs signal handlers for INT, HUP, TERM to unmount and exit filesystem
|
19
20
|
# - installs custom signal handlers if operations implements {fuse_traps}
|
20
21
|
# - creates a fuse handle mounted with registered operations - see {fuse_create}
|
@@ -22,7 +23,7 @@ module FFI
|
|
22
23
|
#
|
23
24
|
# @param [Array<String>] argv mount.fuse arguments
|
24
25
|
# expects progname, mountpoint, options....
|
25
|
-
# @param [FuseArgs] args
|
26
|
+
# @param [FuseArgs|Array<String>] args
|
26
27
|
# alternatively constructed args
|
27
28
|
# @param [Object|FuseOperations] operations
|
28
29
|
# something that responds to the fuse callbacks and optionally our abstract configuration methods
|
@@ -30,13 +31,18 @@ module FFI
|
|
30
31
|
# any data to be made available to the {FuseOperations#init} callback
|
31
32
|
#
|
32
33
|
# @return [Integer] suitable for process exit code
|
33
|
-
def fuse_main(*argv, operations:, args: argv, private_data: nil)
|
34
|
+
def fuse_main(*argv, operations:, args: argv.any? ? argv : [$0, *ARGV], private_data: nil)
|
34
35
|
run_args = fuse_parse_cmdline(args: args, handler: operations)
|
35
36
|
return 2 unless run_args
|
36
37
|
|
37
38
|
fuse_args = run_args.delete(:args)
|
38
39
|
mountpoint = run_args.delete(:mountpoint)
|
39
40
|
|
41
|
+
return 3 unless fuse_configure(operations: operations, **run_args)
|
42
|
+
|
43
|
+
warn "FuseCreate: mountpoint: #{mountpoint}, args: [#{fuse_args.argv.join(' ')}]" if run_args[:debug]
|
44
|
+
warn "FuseRun: #{run_args}" if run_args[:debug]
|
45
|
+
|
40
46
|
fuse = fuse_create(mountpoint, args: fuse_args, operations: operations, private_data: private_data)
|
41
47
|
|
42
48
|
return 0 if run_args[:show_help] || run_args[:show_version]
|
@@ -44,16 +50,15 @@ module FFI
|
|
44
50
|
|
45
51
|
return unless fuse
|
46
52
|
|
47
|
-
warn run_args.to_s if run_args[:debug]
|
48
|
-
|
49
53
|
fuse.run(**run_args)
|
50
54
|
end
|
55
|
+
alias main fuse_main
|
51
56
|
|
52
57
|
# Parse command line arguments
|
53
58
|
#
|
54
59
|
# - parses standard command line options (-d -s -h -V)
|
55
60
|
# will call {fuse_debug}, {fuse_version}, {fuse_help} if implemented by handler
|
56
|
-
# -
|
61
|
+
# - calls {fuse_options} for custom option processing if implemented by handler
|
57
62
|
# - records signal handlers if operations implements {fuse_traps}
|
58
63
|
# - parses standard fuse mount options
|
59
64
|
#
|
@@ -63,33 +68,25 @@ module FFI
|
|
63
68
|
# alternatively constructed args
|
64
69
|
# @param [Object] handler
|
65
70
|
# something that responds to our abstract configuration methods
|
66
|
-
# @param [Object] private_data passed to handler.fuse_opt_proc
|
67
|
-
#
|
68
71
|
# @return [Hash<Symbol,Object>]
|
69
|
-
# * fsname [String]: the fsspec from /etc/fstab
|
70
72
|
# * mountpoint [String]: the mountpoint argument
|
71
73
|
# * args [FuseArgs]: remaining fuse_args to pass to {fuse_create}
|
72
74
|
# * show_help [Boolean]: -h or --help
|
73
75
|
# * show_version [Boolean]: -v or --version
|
74
76
|
# * debug [Boolean]: -d
|
75
77
|
# * others are options to pass to {FuseCommon#run}
|
76
|
-
def fuse_parse_cmdline(*argv, args: argv, handler: nil
|
78
|
+
def fuse_parse_cmdline(*argv, args: argv, handler: nil)
|
77
79
|
args = fuse_init_args(args)
|
78
80
|
|
79
81
|
# Parse args and print cmdline help
|
80
82
|
run_args = Fuse.parse_cmdline(args, handler: handler)
|
83
|
+
return nil unless run_args
|
81
84
|
|
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
|
85
|
+
return nil if handler.respond_to?(:fuse_options) && !handler.fuse_options(args)
|
89
86
|
|
90
87
|
run_args[:traps] = handler.fuse_traps if handler.respond_to?(:fuse_traps)
|
91
88
|
|
92
|
-
|
89
|
+
return nil unless parse_run_options(args, run_args)
|
93
90
|
|
94
91
|
run_args[:args] = args
|
95
92
|
run_args
|
@@ -105,18 +102,24 @@ module FFI
|
|
105
102
|
fuse if fuse.mounted?
|
106
103
|
end
|
107
104
|
|
108
|
-
#
|
109
|
-
#
|
110
|
-
# See {FuseArgs.parse!}
|
111
|
-
def hash_opt_proc(hash, arg, key, _out, discard: [])
|
112
|
-
return :keep if %i[unmatched non_option].include?(key)
|
105
|
+
# @!visibility private
|
113
106
|
|
114
|
-
|
115
|
-
|
107
|
+
def fuse_configure(operations:, show_help: false, show_version: false, **_)
|
108
|
+
return true unless operations.respond_to?(:fuse_configure) && !show_help && !show_version
|
109
|
+
|
110
|
+
# Provide sensible values for FuseContext in case this is referenced during configure
|
111
|
+
FFI::Libfuse::FuseContext.overrides do
|
112
|
+
operations.fuse_configure
|
113
|
+
true
|
114
|
+
rescue Error => e
|
115
|
+
warn e.message
|
116
|
+
false
|
117
|
+
rescue StandardError, ScriptError => e
|
118
|
+
warn "#{e.class.name}: #{e.message}\n\t#{e.backtrace.join("\n\t")}"
|
119
|
+
false
|
120
|
+
end
|
116
121
|
end
|
117
122
|
|
118
|
-
# @!visibility private
|
119
|
-
|
120
123
|
# Version text
|
121
124
|
def version
|
122
125
|
"#{name}: #{VERSION}"
|
@@ -131,6 +134,7 @@ module FFI
|
|
131
134
|
unless args.size <= 2 || args[1]&.start_with?('-') || args[2]&.start_with?('-')
|
132
135
|
args[1] = "-ofsname=#{args[1]}"
|
133
136
|
end
|
137
|
+
warn "FuseArgs: #{args.join(' ')}" if args.include?('-d')
|
134
138
|
args = FuseArgs.create(*args)
|
135
139
|
end
|
136
140
|
|
@@ -138,23 +142,54 @@ module FFI
|
|
138
142
|
|
139
143
|
raise ArgumentError "fuse main args: must be Array<String> or #{FuseArgs.class.name}"
|
140
144
|
end
|
145
|
+
|
146
|
+
private
|
147
|
+
|
148
|
+
def parse_run_options(args, run_args)
|
149
|
+
args.parse!(RUN_OPTIONS) do |key:, value:, **|
|
150
|
+
run_args[key] = value
|
151
|
+
next :keep if (STANDARD_OPTIONS.values + %i[remember]).include?(key)
|
152
|
+
|
153
|
+
:discard
|
154
|
+
end
|
155
|
+
end
|
141
156
|
end
|
142
157
|
|
143
158
|
# @!group Abstract Configuration
|
144
159
|
|
145
|
-
# @!method fuse_options
|
160
|
+
# @!method fuse_options(args)
|
146
161
|
# @abstract
|
147
|
-
#
|
148
|
-
# @
|
149
|
-
|
150
|
-
# @!method fuse_opt_proc(data,arg,key,out)
|
151
|
-
# @abstract
|
152
|
-
# Process custom options
|
162
|
+
# Called to allow filesystem to handle custom options and observe standard mount options #
|
163
|
+
# @param [FuseArgs] args
|
164
|
+
# @return [Boolean] true if args parsed successfully
|
153
165
|
# @see FuseArgs#parse!
|
166
|
+
# @example
|
167
|
+
# OPTIONS = { 'config=' => :config, '-c ' => :config }
|
168
|
+
# def fuse_options(args)
|
169
|
+
# args.parse!(OPTIONS) do |key:, value:, out:, **opts|
|
170
|
+
#
|
171
|
+
# # raise errors for invalid config
|
172
|
+
# raise FFI::Libfuse::FuseArgs::Error, "Invalid config" unless valid_config?(key,value)
|
173
|
+
#
|
174
|
+
# # Configure the file system
|
175
|
+
# @config = value if key == :config
|
176
|
+
#
|
177
|
+
# #Optionally manipulate other arguments for fuse_mount() based on the current argument and state
|
178
|
+
# out.add('-oopt=val')
|
179
|
+
#
|
180
|
+
# # Custom options must be marked :handled otherwise fuse_mount() will fail with unknown arguments
|
181
|
+
# :handled
|
182
|
+
# end
|
183
|
+
# end
|
154
184
|
|
155
185
|
# @!method fuse_traps
|
156
186
|
# @abstract
|
157
|
-
# @return [Hash]
|
187
|
+
# @return [Hash<String|Symbol|Integer,String|Proc>]
|
188
|
+
# map of signal name or number to signal handler as per Signal.trap
|
189
|
+
# @example
|
190
|
+
# def fuse_traps
|
191
|
+
# { HUP: ->() { reload() }}
|
192
|
+
# end
|
158
193
|
|
159
194
|
# @!method fuse_version
|
160
195
|
# @abstract
|
@@ -162,14 +197,22 @@ module FFI
|
|
162
197
|
|
163
198
|
# @!method fuse_help
|
164
199
|
# @abstract
|
165
|
-
#
|
200
|
+
# Called as part of generating output for the -h option
|
201
|
+
# @return [String] help text to explain custom options
|
166
202
|
|
167
203
|
# @!method fuse_debug(enabled)
|
168
204
|
# @abstract
|
169
|
-
#
|
205
|
+
# Called to indicate to the filesystem whether debugging option is in use.
|
170
206
|
# @param [Boolean] enabled if -d option is in use
|
171
207
|
# @return [void]
|
172
208
|
|
209
|
+
# @!method fuse_configure
|
210
|
+
# @abstract
|
211
|
+
# Called immediately before the filesystem is mounted, after options have been parsed
|
212
|
+
#
|
213
|
+
# @raise [Error] to prevent the mount from proceeding
|
214
|
+
# @return [void]
|
215
|
+
|
173
216
|
# @!endgroup
|
174
217
|
|
175
218
|
# @!visibility private
|
@@ -136,7 +136,7 @@ module FFI
|
|
136
136
|
def idle_limit_exceeded?
|
137
137
|
return false unless @max_idle
|
138
138
|
|
139
|
-
synchronize { (@size - @busy - @idle_death.size - 1) > @max_idle && @idle_death << Thread.current }
|
139
|
+
synchronize { (@size - @busy - @idle_death.size - 1) > @max_idle && (@idle_death << Thread.current) }
|
140
140
|
end
|
141
141
|
|
142
142
|
def synchronize(&block)
|
data/lib/ffi/libfuse/version.rb
CHANGED
data/lib/ffi/libfuse.rb
CHANGED
@@ -5,20 +5,29 @@ require_relative 'libfuse/fuse2' if FFI::Libfuse::FUSE_MAJOR_VERSION == 2
|
|
5
5
|
require_relative 'libfuse/fuse3' if FFI::Libfuse::FUSE_MAJOR_VERSION == 3
|
6
6
|
require_relative 'libfuse/main'
|
7
7
|
require_relative 'libfuse/adapter'
|
8
|
+
require_relative 'libfuse/filesystem'
|
8
9
|
require_relative 'devt'
|
9
10
|
|
10
11
|
module FFI
|
11
12
|
# Ruby FFI Binding for [libfuse](https://github.com/libfuse/libfuse)
|
12
13
|
module Libfuse
|
14
|
+
# Filesystems can raise this error to indicate errors from filesystem users
|
15
|
+
class Error < StandardError; end
|
16
|
+
|
17
|
+
# Opinionated default args for {.main}.
|
18
|
+
#
|
19
|
+
# Filesystems that want full control (eg to take advantage of multi-threaded operations) should call
|
20
|
+
# {Main.fuse_main} instead
|
21
|
+
# @note These may change between major versions
|
22
|
+
DEFAULT_ARGS = [$0, '-s', '-odefault_permissions', *ARGV].freeze
|
23
|
+
|
13
24
|
class << self
|
14
25
|
# Filesystem entry point
|
15
|
-
# @note This main function defaults to single-threaded operation by injecting the '-s' option. Pass `$0,*ARGV`
|
16
|
-
# if your filesystem can usefully support multi-threaded operation.
|
17
|
-
#
|
18
26
|
# @see Main.fuse_main
|
19
|
-
def fuse_main(*argv, operations:, args: argv.any? ? argv :
|
27
|
+
def fuse_main(*argv, operations:, args: argv.any? ? argv : DEFAULT_ARGS, private_data: nil)
|
20
28
|
Main.fuse_main(args: args, operations: operations, private_data: private_data) || -1
|
21
29
|
end
|
30
|
+
alias main fuse_main
|
22
31
|
end
|
23
32
|
end
|
24
33
|
end
|
data/lib/ffi/ruby_object.rb
CHANGED
data/lib/ffi/stat/constants.rb
CHANGED
data/lib/ffi/stat/native.rb
CHANGED
@@ -5,7 +5,6 @@ require_relative 'time_spec'
|
|
5
5
|
|
6
6
|
module FFI
|
7
7
|
class Stat
|
8
|
-
# Native (and naked) stat from stat.h
|
9
8
|
# @!visibility private
|
10
9
|
class Native < Struct
|
11
10
|
case Platform::NAME
|
@@ -17,31 +16,62 @@ module FFI
|
|
17
16
|
:st_mode, :mode_t,
|
18
17
|
:st_uid, :uid_t,
|
19
18
|
:st_gid, :gid_t,
|
20
|
-
:__pad0, :
|
19
|
+
:__pad0, :uint,
|
21
20
|
:st_rdev, :dev_t,
|
22
21
|
:st_size, :off_t,
|
23
22
|
:st_blksize, :blksize_t,
|
24
23
|
:st_blocks, :blkcnt_t,
|
25
24
|
:st_atimespec, TimeSpec,
|
26
25
|
:st_mtimespec, TimeSpec,
|
27
|
-
:st_ctimespec, TimeSpec
|
26
|
+
:st_ctimespec, TimeSpec,
|
27
|
+
:unused, [:long, 3]
|
28
|
+
|
29
|
+
[['', :string], ['l', :string], ['f', :int]].each do |(prefix, ftype)|
|
30
|
+
native_func = "native_#{prefix}stat".to_sym
|
31
|
+
lib_func = "#{prefix}stat".to_sym
|
32
|
+
begin
|
33
|
+
::FFI::Stat.attach_function native_func, lib_func, [ftype, by_ref], :int
|
34
|
+
rescue FFI::NotFoundError
|
35
|
+
# gLibc 2.31 (Ubuntu focal) does not export these functions, it maps them to __xstat variants
|
36
|
+
native_xfunc = "native_#{prefix}xstat".to_sym
|
37
|
+
lib_xfunc = "__#{prefix}xstat".to_sym
|
38
|
+
::FFI::Stat.attach_function native_xfunc, lib_xfunc, [:int, ftype, by_ref], :int
|
39
|
+
# 1 is 64 bit versions of struct stat, 3 is 32 bit
|
40
|
+
::FFI::Stat.define_singleton_method(native_func) { |file, buf| send(native_xfunc, 1, file, buf) }
|
41
|
+
end
|
42
|
+
end
|
28
43
|
|
29
|
-
when '
|
44
|
+
when 'x86_64-darwin', 'aarch64-darwin'
|
45
|
+
# man stat - this is stat with 64 bit inodes.
|
30
46
|
layout :st_dev, :dev_t,
|
31
|
-
:st_ino, :uint32,
|
32
47
|
:st_mode, :mode_t,
|
33
48
|
:st_nlink, :nlink_t,
|
49
|
+
:st_ino, :ino_t,
|
34
50
|
:st_uid, :uid_t,
|
35
51
|
:st_gid, :gid_t,
|
36
52
|
:st_rdev, :dev_t,
|
37
53
|
:st_atimespec, TimeSpec,
|
38
54
|
:st_mtimespec, TimeSpec,
|
39
55
|
:st_ctimespec, TimeSpec,
|
56
|
+
:st_birthtimespec, TimeSpec,
|
40
57
|
:st_size, :off_t,
|
41
58
|
:st_blocks, :blkcnt_t,
|
42
59
|
:st_blksize, :blksize_t,
|
43
60
|
:st_flags, :uint32,
|
44
|
-
:st_gen, :uint32
|
61
|
+
:st_gen, :uint32,
|
62
|
+
:st_lspare, :int32,
|
63
|
+
:st_gspare, :int64
|
64
|
+
|
65
|
+
begin
|
66
|
+
# TODO: these functions are deprecated, but at least on Cataline -> Monterey the old stat functions
|
67
|
+
# use the stat struct *without* 64 bit inodes, but macfuse is compiled with 64 bit inodes
|
68
|
+
::FFI::Stat.attach_function :native_stat, :stat64, [:string, by_ref], :int
|
69
|
+
::FFI::Stat.attach_function :native_lstat, :lstat64, [:string, by_ref], :int
|
70
|
+
::FFI::Stat.attach_function :native_fstat, :fstat64, [:int, by_ref], :int
|
71
|
+
rescue FFI::NotFoundError
|
72
|
+
# these are only used in testing
|
73
|
+
end
|
74
|
+
|
45
75
|
else
|
46
76
|
raise NotImplementedError, "FFI::Stat not implemented for FFI::Platform #{Platform::NAME}"
|
47
77
|
end
|
data/lib/ffi/stat/time_spec.rb
CHANGED
@@ -14,14 +14,27 @@ module FFI
|
|
14
14
|
# Special nsec value representing a request to omit setting this time - see utimensat(2)
|
15
15
|
UTIME_OMIT = (1 << 30) - 2
|
16
16
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
17
|
+
class << self
|
18
|
+
# A fixed TimeSpec representing the current time
|
19
|
+
def now
|
20
|
+
@now ||= new.set_time(0, UTIME_NOW)
|
21
|
+
end
|
22
|
+
|
23
|
+
# A fixed TimeSpec representing a request to omit setting this time
|
24
|
+
def omit
|
25
|
+
@omit ||= new.set_time(0, UTIME_OMIT)
|
26
|
+
end
|
21
27
|
|
22
|
-
|
23
|
-
|
24
|
-
@
|
28
|
+
# @param [Array<TimeSpec>] times
|
29
|
+
# @param [Integer] size
|
30
|
+
# @return [Array<TimeSpec>] list of times filled out to size with TimeSpec.now if times was empty,
|
31
|
+
# otherwise with TimeSpec.omit
|
32
|
+
def fill_times(times, size = times.size)
|
33
|
+
return times unless times.size < size
|
34
|
+
return Array.new(size, now) if times.empty?
|
35
|
+
|
36
|
+
times.dup.fill(omit, times.size..size - times.size) if times.size < size
|
37
|
+
end
|
25
38
|
end
|
26
39
|
|
27
40
|
layout(
|
@@ -45,15 +58,14 @@ module FFI
|
|
45
58
|
|
46
59
|
# @overload set_time(time)
|
47
60
|
# @param [Time] time
|
48
|
-
# @return [
|
61
|
+
# @return [self]
|
49
62
|
# @overload set_time(sec,nsec=0)
|
50
63
|
# @param [Integer] sec number of (nano/micro)seconds from epoch, precision depending on nsec
|
51
64
|
# @param [Symbol|Integer] nsec
|
52
65
|
# - :nsec to treat sec as number of nanoseconds since epoch
|
53
66
|
# - :usec to treat sec as number of microseconds since epoch
|
54
67
|
# - Integer to treat sec as number of seconds since epoch, and nsec as additional nanoseconds
|
55
|
-
#
|
56
|
-
# @return [TimeSpec] self
|
68
|
+
# @return [self]
|
57
69
|
def set_time(sec, nsec = 0)
|
58
70
|
return set_time(sec.to_i, sec.nsec) if sec.is_a?(Time)
|
59
71
|
|
@@ -95,6 +107,10 @@ module FFI
|
|
95
107
|
Time.at(sec, nsec, :nsec, in: 0).utc
|
96
108
|
end
|
97
109
|
|
110
|
+
def to_s(now = nil)
|
111
|
+
time(now).to_s
|
112
|
+
end
|
113
|
+
|
98
114
|
# Convert to Integer
|
99
115
|
# @param [Time|nil] now
|
100
116
|
# optional value to use if {now?} is true. If not set then Time.now will be used
|
@@ -104,7 +120,7 @@ module FFI
|
|
104
120
|
return nil if omit?
|
105
121
|
|
106
122
|
t = now? ? (now || Time.now) : self
|
107
|
-
t.tv_sec * 10**9 + t.tv_nsec
|
123
|
+
(t.tv_sec * (10**9)) + t.tv_nsec
|
108
124
|
end
|
109
125
|
|
110
126
|
# Convert to Float
|
@@ -116,7 +132,7 @@ module FFI
|
|
116
132
|
return nil if omit?
|
117
133
|
|
118
134
|
t = now? ? (now || Time.now) : self
|
119
|
-
t.tv_sec.to_f + t.tv_nsec.to_f / (10**9)
|
135
|
+
t.tv_sec.to_f + (t.tv_nsec.to_f / (10**9))
|
120
136
|
end
|
121
137
|
|
122
138
|
# @!visibility private
|