ffi-libfuse 0.0.1.pre → 0.1.0.rc20220550
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/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
|