rfuse 1.2.3.rc20201019.81 → 2.0.0.ffilibfuse
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 +2 -1
- data/{CHANGES.md → CHANGELOG.md} +6 -0
- data/README.md +14 -42
- data/lib/rfuse/flock.rb +31 -0
- data/lib/rfuse/gem_version.rb +20 -0
- data/lib/rfuse/rfuse.rb +318 -0
- data/lib/rfuse/stat.rb +43 -0
- data/lib/rfuse/statvfs.rb +33 -0
- data/lib/rfuse/version.rb +1 -1
- data/lib/rfuse.rb +375 -590
- metadata +26 -28
- data/ext/rfuse/bufferwrapper.c +0 -48
- data/ext/rfuse/bufferwrapper.h +0 -10
- data/ext/rfuse/context.c +0 -80
- data/ext/rfuse/context.h +0 -5
- data/ext/rfuse/extconf.rb +0 -17
- data/ext/rfuse/file_info.c +0 -144
- data/ext/rfuse/file_info.h +0 -13
- data/ext/rfuse/filler.c +0 -61
- data/ext/rfuse/filler.h +0 -15
- data/ext/rfuse/helper.c +0 -97
- data/ext/rfuse/helper.h +0 -15
- data/ext/rfuse/intern_rfuse.c +0 -79
- data/ext/rfuse/intern_rfuse.h +0 -22
- data/ext/rfuse/pollhandle.c +0 -54
- data/ext/rfuse/pollhandle.h +0 -10
- data/ext/rfuse/rfuse.c +0 -2007
- data/ext/rfuse/rfuse.h +0 -3
- data/ext/rfuse/rfuse_mod.c +0 -12
- data/ext/rfuse/ruby-compat.h +0 -39
data/lib/rfuse.rb
CHANGED
@@ -1,645 +1,430 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'fcntl'
|
2
|
-
|
3
|
-
|
4
|
-
|
4
|
+
require_relative 'rfuse/version'
|
5
|
+
require_relative 'rfuse/rfuse'
|
6
|
+
require_relative 'rfuse/compat'
|
7
|
+
require_relative 'rfuse/stat'
|
8
|
+
require_relative 'rfuse/statvfs'
|
9
|
+
require_relative 'rfuse/flock'
|
5
10
|
|
6
11
|
# Ruby FUSE (Filesystem in USErspace) binding
|
7
12
|
module RFuse
|
8
13
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
14
|
+
class Error < StandardError; end
|
15
|
+
|
16
|
+
# @private
|
17
|
+
# Used by listxattr
|
18
|
+
def self.packxattr(xattrs)
|
19
|
+
case xattrs
|
20
|
+
when Array
|
21
|
+
xattrs.join("\000").concat("\000")
|
22
|
+
when String
|
23
|
+
# assume already \0 separated list of keys
|
24
|
+
xattrs
|
25
|
+
else
|
26
|
+
raise Error, ":listxattr must return Array or String, got #{xattrs.inspect}"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# Parse mount arguments and options
|
31
|
+
#
|
32
|
+
# @param [Array<String>] argv
|
33
|
+
# normalised fuse options
|
34
|
+
#
|
35
|
+
# @param [Array<Symbol>] local_opts local options
|
36
|
+
# if these are found in the argv entry following "-o" they are removed
|
37
|
+
# from argv, ie so argv is a clean set of options that can be passed
|
38
|
+
# to {RFuse::Fuse} or {RFuse::FuseDelegator}
|
39
|
+
#
|
40
|
+
#
|
41
|
+
# @return [Hash<Symbol,String|Boolean>]
|
42
|
+
# the extracted options
|
43
|
+
#
|
44
|
+
# @since 1.0.3
|
45
|
+
#
|
46
|
+
# Fuse itself will normalise arguments
|
47
|
+
#
|
48
|
+
# mount -t fuse </path/to/fs.rb>#<device> mountpoint [options...]
|
49
|
+
# mount.fuse </path/to/fs.rb>#<device> mountpoint [options...]
|
50
|
+
#
|
51
|
+
# both result in the following command execution
|
52
|
+
#
|
53
|
+
# /path/to/fs.rb [device] mountpoint [-h] [-d] [-o [opt,optkey=value,...]]
|
54
|
+
#
|
55
|
+
# which this method will parse into a Hash with the following special keys
|
56
|
+
#
|
57
|
+
# * `:device` - the optional mount device, removed from argv if present
|
58
|
+
# * `:mountpoint` - required mountpoint
|
59
|
+
# * `:help` - if -h was supplied - will print help text (and not mount the filesystem!)
|
60
|
+
# * `:debug` - if -d (or -o debug) was supplied - will print debug output from the underlying FUSE library
|
61
|
+
#
|
62
|
+
# and any other supplied options.
|
63
|
+
#
|
64
|
+
# @example
|
65
|
+
# argv = [ "some/device", "/mnt/point", "-h", "-o", "debug,myfs=aValue" ]
|
66
|
+
# options = RFuse.parse_options(argv,:myfs)
|
67
|
+
#
|
68
|
+
# # options ==
|
69
|
+
# { :device => "some/device",
|
70
|
+
# :mountpoint => "/mnt/point",
|
71
|
+
# :help => true,
|
72
|
+
# :debug => true,
|
73
|
+
# :myfs => "aValue"
|
74
|
+
# }
|
75
|
+
# # and argv ==
|
76
|
+
# [ "/mnt/point","-h","-o","debug" ]
|
77
|
+
#
|
78
|
+
# fs = create_filesystem(options)
|
79
|
+
# fuse = RFuse::FuseDelegator.new(fs,*ARGV)
|
80
|
+
#
|
81
|
+
def self.parse_options(argv, *local_opts, desc: nil, exec: $0)
|
82
|
+
|
83
|
+
def desc.fuse_help
|
84
|
+
self
|
85
|
+
end if desc && !desc.empty?
|
86
|
+
|
87
|
+
argv.unshift(exec) unless argv.size >= 2 && argv[0..1].none? { |a| a.start_with?('-') }
|
88
|
+
run_args = FFI::Libfuse::Main.fuse_parse_cmdline(*argv, handler: desc)
|
89
|
+
|
90
|
+
run_args[:help] = true if run_args[:show_help] # compatibility
|
91
|
+
|
92
|
+
device_opts = { 'subtype=' => :device, 'fsname=' => :device }
|
93
|
+
local_opt_conf = local_opts.each.with_object(device_opts) do |o, conf|
|
94
|
+
conf.merge!({ "#{o}=" => o.to_sym, "#{o}" => o.to_sym })
|
21
95
|
end
|
22
96
|
|
23
|
-
|
24
|
-
#
|
25
|
-
# @param [Array<String>] argv
|
26
|
-
# normalised fuse options
|
27
|
-
#
|
28
|
-
# @param [Array<Symbol>] local_opts local options
|
29
|
-
# if these are found in the argv entry following "-o" they are removed
|
30
|
-
# from argv, ie so argv is a clean set of options that can be passed
|
31
|
-
# to {RFuse::Fuse} or {RFuse::FuseDelegator}
|
32
|
-
#
|
33
|
-
#
|
34
|
-
# @return [Hash<Symbol,String|Boolean>]
|
35
|
-
# the extracted options
|
36
|
-
#
|
37
|
-
# @since 1.0.3
|
38
|
-
#
|
39
|
-
# Fuse itself will normalise arguments
|
40
|
-
#
|
41
|
-
# mount -t fuse </path/to/fs.rb>#<device> mountpoint [options...]
|
42
|
-
# mount.fuse </path/to/fs.rb>#<device> mountpoint [options...]
|
43
|
-
#
|
44
|
-
# both result in the following command execution
|
45
|
-
#
|
46
|
-
# /path/to/fs.rb [device] mountpoint [-h] [-d] [-o [opt,optkey=value,...]]
|
47
|
-
#
|
48
|
-
# which this method will parse into a Hash with the following special keys
|
49
|
-
#
|
50
|
-
# * `:device` - the optional mount device, removed from argv if present
|
51
|
-
# * `:mountpoint` - required mountpoint
|
52
|
-
# * `:help` - if -h was supplied - will print help text (and not mount the filesystem!)
|
53
|
-
# * `:debug` - if -d (or -o debug) was supplied - will print debug output from the underlying FUSE library
|
54
|
-
#
|
55
|
-
# and any other supplied options.
|
56
|
-
#
|
57
|
-
# @example
|
58
|
-
# ARGV = [ "some/device", "/mnt/point", "-h", "-o", "debug,myfs=aValue" ]
|
59
|
-
# options = RFuse.parse_options(ARGV,:myfs)
|
60
|
-
#
|
61
|
-
# # options ==
|
62
|
-
# { :device => "some/device",
|
63
|
-
# :mountpoint => "/mnt/point",
|
64
|
-
# :help => true,
|
65
|
-
# :debug => true,
|
66
|
-
# :myfs => "aValue"
|
67
|
-
# }
|
68
|
-
# # and ARGV ==
|
69
|
-
# [ "/mnt/point","-h","-o","debug" ]
|
70
|
-
#
|
71
|
-
# fs = create_filesystem(options)
|
72
|
-
# fuse = RFuse::FuseDelegator.new(fs,*ARGV)
|
73
|
-
#
|
74
|
-
def self.parse_options(argv,*local_opts)
|
75
|
-
result = Hash.new(nil)
|
76
|
-
|
77
|
-
first_opt_index = (argv.find_index() { |opt| opt =~ /^-.*/ } || argv.length )
|
78
|
-
|
79
|
-
result[:device] = argv.shift if first_opt_index > 1
|
80
|
-
result[:mountpoint] = argv[0] if argv.length > 0
|
81
|
-
|
82
|
-
if argv.include?("-h")
|
83
|
-
result[:help] = true
|
84
|
-
end
|
97
|
+
fuse_args = run_args.delete(:args)
|
85
98
|
|
86
|
-
|
87
|
-
|
88
|
-
|
99
|
+
fuse_args.parse!(local_opt_conf, run_args, **{}) do |key:, value:, data:, **_|
|
100
|
+
data[key] = value
|
101
|
+
key == :device ? :keep : :discard
|
102
|
+
end
|
89
103
|
|
90
|
-
|
104
|
+
argv.replace(fuse_args.argv)
|
105
|
+
# The first arg is actually the device and ignored in future calls to parse opts
|
106
|
+
# (eg during fuse_new, but rfuse used to return the mountpoint here.
|
107
|
+
argv[0] = run_args[:mountpoint]
|
108
|
+
|
109
|
+
run_args
|
110
|
+
end
|
111
|
+
|
112
|
+
# Generate a usage string
|
113
|
+
#
|
114
|
+
# @param [String] device a description of how the device field should be used
|
115
|
+
# @param [String] exec the executable
|
116
|
+
# @return [String] the usage string
|
117
|
+
def self.usage(device = nil, exec = File.basename($PROGRAM_NAME))
|
118
|
+
"Usage:\n #{exec} #{device} mountpoint [-h] [-d] [-o [opt,optkey=value,...]]\n"
|
119
|
+
end
|
120
|
+
|
121
|
+
# Convenience method to launch a fuse filesystem, with nice usage messages and default signal traps
|
122
|
+
#
|
123
|
+
# @param [Array<String>] argv command line arguments
|
124
|
+
# @param [Array<Symbol>] extra_options list of additional options
|
125
|
+
# @param [String] extra_options_usage describing additional option usage
|
126
|
+
# @param [String] device a description of the device field
|
127
|
+
# @param [String] exec the executable file
|
128
|
+
#
|
129
|
+
# @yieldparam [Hash<Symbol,String>] options See {.parse_options}
|
130
|
+
# @yieldparam [Array<String>] argv Cleaned argv See {.parse_options}
|
131
|
+
#
|
132
|
+
# @yieldreturn [Class<Fuse>]
|
133
|
+
# a subclass of {Fuse} that implements your filesystem. Will receive `.new(*extra_options,*argv)`
|
134
|
+
#
|
135
|
+
# @yieldreturn [Class]
|
136
|
+
# a class that implements {FuseDelegator::FUSE_METHODS}. Will receive `.new(*extra_options)`
|
137
|
+
# and the resulting instance sent with `*argv` to {FuseDelegator}
|
138
|
+
#
|
139
|
+
# @yieldreturn [Fuse]
|
140
|
+
# Your initialised (and therefore already mounted) filesystem
|
141
|
+
#
|
142
|
+
# @yieldreturn [Object]
|
143
|
+
# An object that implements the {Fuse} methods, to be passed with `*argv` to {FuseDelegator}
|
144
|
+
#
|
145
|
+
# @yieldreturn [Error]
|
146
|
+
# raise {Error} with appropriate message for invalid options
|
147
|
+
#
|
148
|
+
# @since 1.1.0
|
149
|
+
#
|
150
|
+
# @example
|
151
|
+
#
|
152
|
+
# class MyFuse < Fuse
|
153
|
+
# def initialize(myfs,*argv)
|
154
|
+
# @myfs = myfs # my filesystem local option value
|
155
|
+
# super(*argv)
|
156
|
+
# end
|
157
|
+
# # ... implementations for filesystem methods ...
|
158
|
+
# end
|
159
|
+
#
|
160
|
+
# class MyFSClass # not a subclass of Fuse, FuseDelegator used
|
161
|
+
# def initialize(myfs)
|
162
|
+
# @myfs = myfs # my filesystem local option value
|
163
|
+
# end
|
164
|
+
# # ...
|
165
|
+
# end
|
166
|
+
#
|
167
|
+
# MY_OPTIONS = [ :myfs ]
|
168
|
+
# OPTION_USAGE = " -o myfs=VAL how to use the myfs option"
|
169
|
+
# DEVICE_USAGE = "how to use device arg"
|
170
|
+
#
|
171
|
+
# # Normally from the command line...
|
172
|
+
# ARGV = [ "some/device", "/mnt/point", "-h", "-o", "debug,myfs=aValue" ]
|
173
|
+
#
|
174
|
+
# RFuse.main(ARGV, MY_OPTIONS, OPTION_USAGE, DEVICE_USAGE, $0) do |options, argv|
|
175
|
+
#
|
176
|
+
# # options ==
|
177
|
+
# { :device => "some/device",
|
178
|
+
# :mountpoint => "/mnt/point",
|
179
|
+
# :help => true,
|
180
|
+
# :debug => true,
|
181
|
+
# :myfs => "aValue"
|
182
|
+
# }
|
183
|
+
#
|
184
|
+
# # ... validate options...
|
185
|
+
# raise RFuse::Error, "Bad option" unless options[:myfs]
|
186
|
+
#
|
187
|
+
# # return the filesystem class to be initialised by RFuse
|
188
|
+
# MyFuse
|
189
|
+
# # or
|
190
|
+
# MyFSClass
|
191
|
+
#
|
192
|
+
# # OR take full control over initialisation yourself and return the object
|
193
|
+
# MyFuse.new(options[:myfs],*argv)
|
194
|
+
# # or
|
195
|
+
# MyFSClass.new(options[:myfs])
|
196
|
+
#
|
197
|
+
# end
|
198
|
+
#
|
199
|
+
def self.main(argv = ARGV.dup, extra_options = [], extra_options_usage = nil, device = nil, exec = File.basename($PROGRAM_NAME))
|
200
|
+
begin
|
201
|
+
fuse_help = ['Filesystem options:',extra_options_usage,device,''].compact.join("\n")
|
202
|
+
|
203
|
+
options = parse_options(argv, *extra_options, desc: extra_options_usage && fuse_help, exec: exec)
|
204
|
+
|
205
|
+
unless options[:mountpoint]
|
206
|
+
warn "rfuse: failed, no mountpoint provided"
|
207
|
+
puts usage(device,exec)
|
208
|
+
return nil
|
209
|
+
end
|
210
|
+
|
211
|
+
fs = yield options, argv
|
212
|
+
|
213
|
+
raise Error, 'no filesystem provided' unless fs
|
214
|
+
|
215
|
+
# create can pass the extra options to a constructor so order and existence is important.
|
216
|
+
fs_options = extra_options.each_with_object({}) { |k,h| h[k] = options.delete(k) }
|
217
|
+
fuse = create(argv: argv, fs: fs, options: fs_options)
|
218
|
+
|
219
|
+
return nil if options[:show_help] || options[:show_version]
|
220
|
+
|
221
|
+
raise Error, "filesystem #{fs} not mounted" unless fuse&.mounted?
|
222
|
+
|
223
|
+
fuse.run(**options)
|
224
|
+
rescue Error => e
|
225
|
+
# These errors are produced generally because the user passed bad arguments etc..
|
226
|
+
puts usage(device, exec) unless options[:help]
|
227
|
+
warn "rfuse failed: #{e.message}"
|
228
|
+
nil
|
229
|
+
rescue StandardError => e
|
230
|
+
# These are other errors related the yield block
|
231
|
+
warn e.message
|
232
|
+
warn e.backtrace.join("\n")
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
# @private
|
237
|
+
# Helper to create a {Fuse} from variety of #{.main} yield results
|
238
|
+
def self.create(fs:, argv: [], options: {})
|
239
|
+
if fs.is_a?(Fuse)
|
240
|
+
fs
|
241
|
+
elsif fs.is_a?(Class)
|
242
|
+
if Fuse > fs
|
243
|
+
fs.new(*options.values, *argv)
|
244
|
+
else
|
245
|
+
delegate = fs.new(*options.values)
|
246
|
+
FuseDelegator.new(delegate, *argv)
|
247
|
+
end
|
248
|
+
elsif fs
|
249
|
+
FuseDelegator.new(fs, *argv)
|
250
|
+
end
|
251
|
+
end
|
91
252
|
|
92
|
-
|
93
|
-
options = argv[opt_index].split(",")
|
253
|
+
class Fuse
|
94
254
|
|
95
|
-
|
96
|
-
|
255
|
+
attr_reader :mountpoint
|
256
|
+
alias mountname mountpoint
|
97
257
|
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
258
|
+
def initialize(mountpoint, *argv)
|
259
|
+
@mountpoint = mountpoint
|
260
|
+
@fuse = FFI::Libfuse::Main.fuse_create(mountpoint, *argv, operations: self, private_data: self)
|
261
|
+
end
|
102
262
|
|
103
|
-
|
104
|
-
|
105
|
-
|
263
|
+
# Is the filesystem successfully mounted
|
264
|
+
#
|
265
|
+
# @return [Boolean] true if mounted, false otherwise
|
266
|
+
def mounted?
|
267
|
+
@fuse && @fuse.mounted?
|
268
|
+
end
|
106
269
|
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
270
|
+
# Convenience method to run a mounted filesystem to completion.
|
271
|
+
#
|
272
|
+
# @param [Array<String|Integer>] signals list of signals to handle.
|
273
|
+
# Default is all available signals. See {#trap_signals}
|
274
|
+
#
|
275
|
+
# @return [void]
|
276
|
+
# @since 1.1.0
|
277
|
+
# @see RFuse.main
|
278
|
+
def run(signals = Signal.list.keys, **run_args)
|
279
|
+
raise Error, 'not mounted' unless @fuse
|
280
|
+
trap_signals(*signals)
|
281
|
+
@fuse.run(traps: @traps, **run_args)
|
282
|
+
end
|
113
283
|
|
114
|
-
|
284
|
+
def loop
|
285
|
+
run([] ,single_thread: true, foreground: true)
|
115
286
|
end
|
116
287
|
|
117
|
-
#
|
118
|
-
|
119
|
-
|
120
|
-
# @param [String] exec the executable
|
121
|
-
# @return [String] the usage string
|
122
|
-
def self.usage(device=nil,exec=File.basename($0))
|
123
|
-
"Usage:\n #{exec} #{device} mountpoint [-h] [-d] [-o [opt,optkey=value,...]]\n"
|
288
|
+
# Stop processing
|
289
|
+
def exit
|
290
|
+
@fuse&.exit&.join
|
124
291
|
end
|
292
|
+
alias :unmount :exit
|
125
293
|
|
126
|
-
#
|
294
|
+
# Set traps
|
127
295
|
#
|
128
|
-
#
|
129
|
-
# @param [Array<Symbol>] extra_options list of additional options
|
130
|
-
# @param [String] extra_options_usage describing additional option usage
|
131
|
-
# @param [String] device a description of the device field
|
132
|
-
# @param [String] exec the executable file
|
296
|
+
# The filesystem supports a signal by providing a `sig<name>` method. eg {#sigint}
|
133
297
|
#
|
134
|
-
#
|
135
|
-
#
|
298
|
+
# The fuse {#loop} is notified of the signal via the self-pipe trick, and calls the corresponding
|
299
|
+
# `sig<name>` method, after any current filesystem operation completes.
|
136
300
|
#
|
137
|
-
#
|
138
|
-
# a subclass of {Fuse} that implements your filesystem. Will receive `.new(*extra_options,*argv)`
|
301
|
+
# This method will not override traps that have previously been set to something other than "DEFAULT"
|
139
302
|
#
|
140
|
-
#
|
141
|
-
# a class that implements {FuseDelegator::FUSE_METHODS}. Will receive `.new(*extra_options)`
|
142
|
-
# and the resulting instance sent with `*argv` to {FuseDelegator}
|
303
|
+
# Note: {Fuse} itself provides {#sigterm} and {#sigint}.
|
143
304
|
#
|
144
|
-
# @
|
145
|
-
#
|
305
|
+
# @param [Array<String>] signames
|
306
|
+
# List of signal names to set traps for, if the filesystem has methods to handle them.
|
307
|
+
# Use `Signal.list.keys` to try all available signals.
|
146
308
|
#
|
147
|
-
# @
|
148
|
-
# An object that implements the {Fuse} methods, to be passed with `*argv` to {FuseDelegator}
|
149
|
-
#
|
150
|
-
# @yieldreturn [Error]
|
151
|
-
# raise {Error} with appropriate message for invalid options
|
309
|
+
# @return [Array<String>] List of signal names that traps have been set for.
|
152
310
|
#
|
153
311
|
# @since 1.1.0
|
154
312
|
#
|
155
313
|
# @example
|
156
|
-
#
|
157
|
-
#
|
158
|
-
#
|
159
|
-
# @myfs = myfs # my filesystem local option value
|
160
|
-
# super(*argv)
|
314
|
+
# class MyFS < Fuse
|
315
|
+
# def sighup()
|
316
|
+
# # do something on HUP signal
|
161
317
|
# end
|
162
|
-
# # ... implementations for filesystem methods ...
|
163
318
|
# end
|
164
319
|
#
|
165
|
-
#
|
166
|
-
# def initialize(myfs)
|
167
|
-
# @myfs = myfs # my filesystem local option value
|
168
|
-
# end
|
169
|
-
# # ...
|
170
|
-
# end
|
171
|
-
#
|
172
|
-
# MY_OPTIONS = [ :myfs ]
|
173
|
-
# OPTION_USAGE = " -o myfs=VAL how to use the myfs option"
|
174
|
-
# DEVICE_USAGE = "how to use device arg"
|
175
|
-
#
|
176
|
-
# # Normally from the command line...
|
177
|
-
# ARGV = [ "some/device", "/mnt/point", "-h", "-o", "debug,myfs=aValue" ]
|
178
|
-
#
|
179
|
-
# RFuse.main(ARGV, MY_OPTIONS, OPTION_USAGE, DEVICE_USAGE, $0) do |options, argv|
|
180
|
-
#
|
181
|
-
# # options ==
|
182
|
-
# { :device => "some/device",
|
183
|
-
# :mountpoint => "/mnt/point",
|
184
|
-
# :help => true,
|
185
|
-
# :debug => true,
|
186
|
-
# :myfs => "aValue"
|
187
|
-
# }
|
188
|
-
#
|
189
|
-
# # ... validate options...
|
190
|
-
# raise RFuse::Error, "Bad option" unless options[:myfs]
|
191
|
-
#
|
192
|
-
# # return the filesystem class to be initialised by RFuse
|
193
|
-
# MyFuse
|
194
|
-
# # or
|
195
|
-
# MyFSClass
|
196
|
-
#
|
197
|
-
# # OR take full control over initialisation yourself and return the object
|
198
|
-
# MyFuse.new(options[:myfs],*argv)
|
199
|
-
# # or
|
200
|
-
# MyFSClass.new(options[:myfs])
|
320
|
+
# fuse = MyFS.new(*args)
|
201
321
|
#
|
322
|
+
# if fuse.mounted?
|
323
|
+
# # Below will result in (effectively) Signal.trap("HUP") { fuse.sighup() }
|
324
|
+
# fuse.trap_signals("HUP","USR1") # ==> ["HUP"]
|
325
|
+
# fuse.loop()
|
202
326
|
# end
|
203
327
|
#
|
204
|
-
def
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
unless options[:mountpoint]
|
209
|
-
$stderr.puts "rfuse: failed, no mountpoint provided"
|
210
|
-
puts usage(device,exec)
|
211
|
-
return nil
|
212
|
-
end
|
213
|
-
|
214
|
-
if options[:help]
|
215
|
-
puts usage(device,exec)
|
216
|
-
# TODO: In Fuse 3.0 this looks like it will move to $stdout
|
217
|
-
help_io = STDERR
|
218
|
-
help_io.puts "Fuse options: (#{FUSE_MAJOR_VERSION}.#{FUSE_MINOR_VERSION})"
|
219
|
-
help_io.puts " -h help - print this help output"
|
220
|
-
help_io.puts " -d |-o debug enable internal FUSE debug output"
|
221
|
-
help_io.puts ""
|
222
|
-
help_io.flush()
|
223
|
-
|
224
|
-
# Cause Fuse kernel Options to print, but don't actually start a filesystem
|
225
|
-
Fuse.new(*argv)
|
226
|
-
|
227
|
-
if extra_options_usage
|
228
|
-
help_io.puts "Filesystem options:"
|
229
|
-
help_io.puts extra_options_usage
|
230
|
-
end
|
231
|
-
|
232
|
-
return nil
|
233
|
-
end
|
234
|
-
|
235
|
-
begin
|
236
|
-
fs = yield options,argv
|
237
|
-
|
238
|
-
raise Error, "no filesystem provided" unless fs
|
239
|
-
|
240
|
-
fuse = create(fs,argv,options,extra_options)
|
241
|
-
|
242
|
-
raise Error, "filesystem #{fs} not mounted" unless fuse && fuse.mounted?
|
243
|
-
|
244
|
-
fuse.run
|
245
|
-
rescue Error => fuse_ex
|
246
|
-
# These errors are produced generally because the user passed bad arguments etc..
|
247
|
-
puts usage(device,exec) unless options[:help]
|
248
|
-
$stderr.puts "rfuse failed: #{fuse_ex.message}"
|
249
|
-
return nil
|
250
|
-
rescue => ex
|
251
|
-
# These are other errors related the yield block
|
252
|
-
$stderr.puts ex.message
|
253
|
-
$stderr.puts ex.backtrace.join("\n")
|
254
|
-
end
|
328
|
+
def trap_signals(*signames)
|
329
|
+
signames = signames.map { |n| n.to_s.upcase }.map { |n| n.start_with?('SIG') ? n[3..-1] : n }
|
330
|
+
signames.keep_if { |n| Signal.list[n] && respond_sigmethod?(sigmethod(n)) }
|
255
331
|
|
332
|
+
@traps ||= {}
|
333
|
+
@traps.merge!(signames.map { |n| [ n, ->() { call_sigmethod(sigmethod(n)) }]}.to_h)
|
334
|
+
@traps.keys
|
256
335
|
end
|
257
336
|
|
337
|
+
private
|
258
338
|
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
extra_option_values = extra_options.map { |o| options[o] }
|
266
|
-
if Fuse > fs
|
267
|
-
fs.new(*extra_option_values,*argv)
|
268
|
-
else
|
269
|
-
delegate = fs.new(*extra_option_values)
|
270
|
-
FuseDelegator.new(delegate,*argv)
|
271
|
-
end
|
272
|
-
elsif fs
|
273
|
-
FuseDelegator.new(fs,*argv)
|
274
|
-
end
|
339
|
+
def respond_sigmethod?(sigmethod)
|
340
|
+
respond_to?(sigmethod)
|
341
|
+
end
|
342
|
+
|
343
|
+
def sigmethod(signame)
|
344
|
+
"sig#{signame.downcase}".to_sym
|
275
345
|
end
|
276
346
|
|
277
|
-
|
278
|
-
|
279
|
-
class Fuse
|
280
|
-
|
281
|
-
# Convenience method to run a mounted filesystem to completion.
|
282
|
-
#
|
283
|
-
# @param [Array<String|Integer>] signals list of signals to handle.
|
284
|
-
# Default is all available signals. See {#trap_signals}
|
285
|
-
#
|
286
|
-
# @return [void]
|
287
|
-
# @since 1.1.0
|
288
|
-
# @see RFuse.main
|
289
|
-
def run(signals=Signal.list.keys)
|
290
|
-
if mounted?
|
291
|
-
begin
|
292
|
-
traps = trap_signals(*signals)
|
293
|
-
self.loop()
|
294
|
-
ensure
|
295
|
-
traps.each { |t| Signal.trap(t,"DEFAULT") }
|
296
|
-
unmount()
|
297
|
-
end
|
298
|
-
end
|
299
|
-
end
|
300
|
-
|
301
|
-
# Main processing loop
|
302
|
-
#
|
303
|
-
# Use {#exit} to stop processing (or externally call fusermount -u)
|
304
|
-
#
|
305
|
-
# Other ruby threads can continue while loop is running, however
|
306
|
-
# no thread can operate on the filesystem itself (ie with File or Dir methods)
|
307
|
-
#
|
308
|
-
# @return [void]
|
309
|
-
# @raise [RFuse::Error] if already running or not mounted
|
310
|
-
def loop()
|
311
|
-
raise RFuse::Error, "Already running!" if @running
|
312
|
-
raise RFuse::Error, "FUSE not mounted" unless mounted?
|
313
|
-
@running = true
|
314
|
-
while @running do
|
315
|
-
begin
|
316
|
-
ready, ignore, errors = IO.select([@fuse_io,@pr],[],[@fuse_io])
|
317
|
-
|
318
|
-
if ready.include?(@pr)
|
319
|
-
|
320
|
-
signo = @pr.read_nonblock(1).unpack("c")[0]
|
321
|
-
|
322
|
-
# Signal.signame exist in Ruby 2, but returns horrible errors for non-signals in 2.1.0
|
323
|
-
if (signame = Signal.list.invert[signo])
|
324
|
-
call_sigmethod(sigmethod(signame))
|
325
|
-
end
|
326
|
-
|
327
|
-
elsif errors.include?(@fuse_io)
|
328
|
-
|
329
|
-
@running = false
|
330
|
-
raise RFuse::Error, "FUSE error"
|
331
|
-
|
332
|
-
elsif ready.include?(@fuse_io)
|
333
|
-
if process() < 0
|
334
|
-
# Fuse has been unmounted externally
|
335
|
-
# TODO: mounted? should now return false
|
336
|
-
# fuse_exited? is not true...
|
337
|
-
@running = false
|
338
|
-
end
|
339
|
-
end
|
340
|
-
rescue Errno::EWOULDBLOCK, Errno::EAGAIN
|
341
|
-
#oh well...
|
342
|
-
end
|
343
|
-
end
|
344
|
-
end
|
345
|
-
|
346
|
-
# Stop processing {#loop}
|
347
|
-
# eg called from signal handlers, or some other monitoring thread
|
348
|
-
def exit
|
349
|
-
@running = false
|
350
|
-
send_signal(-1)
|
351
|
-
end
|
352
|
-
|
353
|
-
# Set traps
|
354
|
-
#
|
355
|
-
# The filesystem supports a signal by providing a `sig<name>` method. eg {#sigint}
|
356
|
-
#
|
357
|
-
# The fuse {#loop} is notified of the signal via the self-pipe trick, and calls the corresponding
|
358
|
-
# `sig<name>` method, after any current filesystem operation completes.
|
359
|
-
#
|
360
|
-
# This method will not override traps that have previously been set to something other than "DEFAULT"
|
361
|
-
#
|
362
|
-
# Note: {Fuse} itself provides {#sigterm} and {#sigint}.
|
363
|
-
#
|
364
|
-
# @param [Array<String>] signames
|
365
|
-
# List of signal names to set traps for, if the filesystem has methods to handle them.
|
366
|
-
# Use `Signal.list.keys` to try all available signals.
|
367
|
-
#
|
368
|
-
# @return [Array<String>] List of signal names that traps have been set for.
|
369
|
-
#
|
370
|
-
# @since 1.1.0
|
371
|
-
#
|
372
|
-
# @example
|
373
|
-
# class MyFS < Fuse
|
374
|
-
# def sighup()
|
375
|
-
# # do something on HUP signal
|
376
|
-
# end
|
377
|
-
# end
|
378
|
-
#
|
379
|
-
# fuse = MyFS.new(*args)
|
380
|
-
#
|
381
|
-
# if fuse.mounted?
|
382
|
-
# # Below will result in (effectively) Signal.trap("HUP") { fuse.sighup() }
|
383
|
-
# fuse.trap_signals("HUP","USR1") # ==> ["HUP"]
|
384
|
-
# fuse.loop()
|
385
|
-
# end
|
386
|
-
#
|
387
|
-
def trap_signals(*signames)
|
388
|
-
signames.map { |n| n.to_s.upcase }.map { |n| n.start_with?("SIG") ? n[3..-1] : n }.select do |signame|
|
389
|
-
next false unless respond_sigmethod?(sigmethod(signame)) && signo = Signal.list[signame]
|
390
|
-
|
391
|
-
next true if (prev = Signal.trap(signo) { |signo| send_signal(signo) }) == "DEFAULT"
|
392
|
-
|
393
|
-
Signal.trap(signo,prev)
|
394
|
-
false
|
395
|
-
end
|
396
|
-
end
|
397
|
-
|
398
|
-
# Default signal handler to exit on TERM/INT
|
399
|
-
# @return [void]
|
400
|
-
# @see #trap_signals
|
401
|
-
def sigterm
|
402
|
-
@running = false
|
403
|
-
end
|
404
|
-
alias :sigint :sigterm
|
405
|
-
|
406
|
-
private
|
407
|
-
|
408
|
-
# Called by the C iniitialize
|
409
|
-
# afer the filesystem has been mounted successfully
|
410
|
-
def ruby_initialize
|
411
|
-
@running = false
|
412
|
-
|
413
|
-
# Self-pipe for handling signals and exit
|
414
|
-
@pr,@pw = IO.pipe()
|
415
|
-
|
416
|
-
# The FD was created by FUSE so we don't want
|
417
|
-
# ruby to do anything with it during GC
|
418
|
-
@fuse_io = IO.for_fd(fd(),"r",:autoclose => false)
|
419
|
-
end
|
420
|
-
|
421
|
-
# Called by C unmount before doing all the FUSE stuff
|
422
|
-
def ruby_unmount
|
423
|
-
@pr.close if @pr && !@pr.closed?
|
424
|
-
@pw.close if @pw && !@pw.closed?
|
425
|
-
|
426
|
-
# Ideally we want this IO to avoid autoclosing at GC, but
|
427
|
-
# in Ruby 1.8 we have no way to do that. A work around is to close
|
428
|
-
# the IO here. FUSE won't necessarily like that but it is the best
|
429
|
-
# we can do
|
430
|
-
@fuse_io.close() if @fuse_io && !@fuse_io.closed? && @fuse_io.autoclose?
|
431
|
-
end
|
432
|
-
|
433
|
-
def call_sigmethod(sigmethod)
|
434
|
-
send(sigmethod)
|
435
|
-
end
|
436
|
-
|
437
|
-
def respond_sigmethod?(sigmethod)
|
438
|
-
respond_to?(sigmethod)
|
439
|
-
end
|
440
|
-
|
441
|
-
def sigmethod(signame)
|
442
|
-
"sig#{signame.downcase}".to_sym
|
443
|
-
end
|
444
|
-
|
445
|
-
def send_signal(signo)
|
446
|
-
@pw.write([signo].pack("c")) unless !@pw || @pw.closed?
|
447
|
-
end
|
347
|
+
def call_sigmethod(sigmethod)
|
348
|
+
send(sigmethod)
|
448
349
|
end
|
449
350
|
|
450
|
-
|
451
|
-
|
452
|
-
#or inherit from {Fuse}
|
453
|
-
class FuseDelegator < Fuse
|
454
|
-
|
455
|
-
# Available fuse methods -see http://fuse.sourceforge.net/doxygen/structfuse__operations.html
|
456
|
-
# Note :getdir and :utime are deprecated
|
457
|
-
# :ioctl, :poll are not implemented in the C extension
|
458
|
-
FUSE_METHODS = [ :getattr, :readlink, :getdir, :mknod, :mkdir,
|
459
|
-
:unlink, :rmdir, :symlink, :rename, :link,
|
460
|
-
:chmod, :chown, :truncate, :utime, :open,
|
461
|
-
:create, :read, :write, :statfs, :flush,
|
462
|
-
:release, :fsync, :setxattr, :getxattr, :listxattr,:removexattr,
|
463
|
-
:opendir, :readdir, :releasedir, :fsycndir,
|
464
|
-
:init, :destroy, :access, :ftruncate, :fgetattr, :lock,
|
465
|
-
:utimens, :bmap, :ioctl, :poll ]
|
466
|
-
|
467
|
-
# @param [Object] fuse_object your filesystem object that responds to fuse methods
|
468
|
-
# @param [String] mountpoint existing directory where the filesystem will be mounted
|
469
|
-
# @param [String...] options fuse mount options (use "-h" to see a list)
|
470
|
-
#
|
471
|
-
# Create and mount a filesystem
|
472
|
-
#
|
473
|
-
def initialize(fuse_object,mountpoint,*options)
|
474
|
-
@fuse_delegate = fuse_object
|
475
|
-
define_fuse_methods(fuse_object)
|
476
|
-
@debug = false
|
477
|
-
self.debug=$DEBUG
|
478
|
-
super(mountpoint,options)
|
479
|
-
end
|
480
|
-
|
481
|
-
# USR1 sig handler - toggle debugging of fuse methods
|
482
|
-
# @return [void]
|
483
|
-
def sigusr1()
|
484
|
-
self.debug=!@debug
|
485
|
-
end
|
486
|
-
|
487
|
-
# RFuse Debugging status
|
488
|
-
#
|
489
|
-
# @note this is independent of the Fuse kernel module debugging enabled with the "-d" mount option
|
490
|
-
#
|
491
|
-
# @return [Boolean] whether debugging information should be printed to $stderr around each fuse method.
|
492
|
-
# Defaults to $DEBUG
|
493
|
-
# @since 1.1.0
|
494
|
-
# @see #sigusr1
|
495
|
-
def debug?
|
496
|
-
@debug
|
497
|
-
end
|
498
|
-
|
499
|
-
# Set debugging on or off
|
500
|
-
# @param [Boolean] value enable or disable debugging
|
501
|
-
# @return [Boolean] the new debug value
|
502
|
-
# @since 1.1.0
|
503
|
-
def debug=(value)
|
504
|
-
value = value ? true : false
|
505
|
-
if @debug && !value
|
506
|
-
$stderr.puts "=== #{ self }.debug=false"
|
507
|
-
elsif !@debug && value
|
508
|
-
$stderr.puts "=== #{ self }.debug=true"
|
509
|
-
end
|
510
|
-
@debug = value
|
511
|
-
end
|
512
|
-
|
513
|
-
# @private
|
514
|
-
def to_s
|
515
|
-
"#{self.class.name}::#{@fuse_delegate}"
|
516
|
-
end
|
517
|
-
private
|
518
|
-
|
519
|
-
def define_fuse_methods(fuse_object)
|
520
|
-
FUSE_METHODS.each do |method|
|
521
|
-
if fuse_object.respond_to?(method)
|
522
|
-
method_name = method.id2name
|
523
|
-
instance_eval(<<-EOM, "(__FUSE_DELEGATE__)",1)
|
524
|
-
def #{method_name} (*args,&block)
|
525
|
-
begin
|
526
|
-
$stderr.puts "==> \#{ self }.#{ method_name }(\#{args.inspect })" if debug?
|
527
|
-
result = @fuse_delegate.send(:#{method_name},*args,&block)
|
528
|
-
$stderr.puts "<== \#{ self }.#{ method_name }()" if debug?
|
529
|
-
result
|
530
|
-
rescue => ex
|
531
|
-
$@.delete_if{|s| /^\\(__FUSE_DELEGATE__\\):/ =~ s}
|
532
|
-
$stderr.puts(ex.message) unless ex.respond_to?(:errno) || debug?
|
533
|
-
Kernel::raise
|
534
|
-
end
|
535
|
-
end
|
536
|
-
EOM
|
537
|
-
end
|
538
|
-
end
|
539
|
-
end
|
540
|
-
|
541
|
-
def call_sigmethod(sigmethod)
|
542
|
-
$stderr.puts "==> #{ self }.#{ sigmethod }()" if debug?
|
543
|
-
dlg = @fuse_delegate.respond_to?(sigmethod) ? @fuse_delegate : self
|
544
|
-
dlg.send(sigmethod)
|
545
|
-
$stderr.puts "<== #{ self }.#{ sigmethod }()" if debug?
|
546
|
-
end
|
547
|
-
|
548
|
-
def respond_sigmethod?(sigmethod)
|
549
|
-
@fuse_delegate.respond_to?(sigmethod) || self.respond_to?(sigmethod)
|
550
|
-
end
|
551
|
-
|
552
|
-
|
553
|
-
end #class FuseDelegator
|
554
|
-
|
555
|
-
class Context
|
556
|
-
# @private
|
557
|
-
def to_s
|
558
|
-
"Context::u#{uid},g#{gid},p#{pid}"
|
559
|
-
end
|
351
|
+
def self.inherited(klass)
|
352
|
+
klass.include(Adapter) unless klass.ancestors.include?(Adapter)
|
560
353
|
end
|
354
|
+
end
|
561
355
|
|
562
|
-
|
563
|
-
|
564
|
-
|
565
|
-
|
566
|
-
|
356
|
+
# This class is useful to make your filesystem implementation
|
357
|
+
# debuggable and testable without needing to mount an actual filesystem
|
358
|
+
# or inherit from {Fuse}
|
359
|
+
class FuseDelegator < Fuse
|
360
|
+
|
361
|
+
# @param [Object] fuse_object your filesystem object that responds to fuse methods
|
362
|
+
# @param [String] mountpoint existing directory where the filesystem will be mounted
|
363
|
+
# @param [String...] options fuse mount options (use "-h" to see a list)
|
364
|
+
#
|
365
|
+
# Create and mount a filesystem
|
366
|
+
#
|
367
|
+
def initialize(fuse_object, mountpoint, *options)
|
368
|
+
@fuse_delegate = fuse_object
|
369
|
+
@debug = $DEBUG
|
370
|
+
super(mountpoint, *options)
|
371
|
+
end
|
372
|
+
|
373
|
+
# USR1 sig handler - toggle debugging of fuse methods
|
374
|
+
# @return [void]
|
375
|
+
def sigusr1
|
376
|
+
@debug = !debug?
|
377
|
+
end
|
378
|
+
|
379
|
+
def debug?
|
380
|
+
@debug
|
567
381
|
end
|
568
382
|
|
569
|
-
|
570
|
-
|
571
|
-
|
572
|
-
"FileInfo::fh->#{fh}"
|
573
|
-
end
|
383
|
+
# @private
|
384
|
+
def to_s
|
385
|
+
"#{self.class.name}::#{@fuse_delegate}"
|
574
386
|
end
|
575
387
|
|
576
|
-
|
577
|
-
|
578
|
-
|
579
|
-
|
580
|
-
|
581
|
-
|
582
|
-
|
583
|
-
S_IFCHR = 0020000
|
584
|
-
# Block device
|
585
|
-
S_IFBLK = 0060000
|
586
|
-
# Regular file
|
587
|
-
S_IFREG = 0100000
|
588
|
-
# FIFO.
|
589
|
-
S_IFIFO = 0010000
|
590
|
-
# Symbolic link
|
591
|
-
S_IFLNK = 0120000
|
592
|
-
# Socket
|
593
|
-
S_IFSOCK = 0140000
|
594
|
-
|
595
|
-
# @param [Fixnum] mode file permissions
|
596
|
-
# @param [Hash<Symbol,Fixnum>] values initial values for other attributes
|
597
|
-
#
|
598
|
-
# @return [Stat] representing a directory
|
599
|
-
def self.directory(mode=0,values = { })
|
600
|
-
return self.new(S_IFDIR,mode,values)
|
601
|
-
end
|
602
|
-
|
603
|
-
# @param [Fixnum] mode file permissions
|
604
|
-
# @param [Hash<Symbol,Fixnum>] values initial values for other attributes
|
605
|
-
#
|
606
|
-
# @return [Stat] representing a regular file
|
607
|
-
def self.file(mode=0,values = { })
|
608
|
-
return self.new(S_IFREG,mode,values)
|
609
|
-
end
|
610
|
-
|
611
|
-
# @return [Integer] see stat(2)
|
612
|
-
attr_accessor :uid,:gid,:mode,:size, :dev,:ino,:nlink,:rdev,:blksize,:blocks
|
613
|
-
|
614
|
-
# @return [Integer, Time] see stat(2)
|
615
|
-
attr_accessor :atime,:mtime,:ctime
|
616
|
-
|
617
|
-
def initialize(type,permissions,values = { })
|
618
|
-
values[:mode] = ((type & S_IFMT) | (permissions & 07777))
|
619
|
-
@uid,@gid,@size,@mode,@atime,@mtime,@ctime,@dev,@ino,@nlink,@rdev,@blksize,@blocks = Array.new(13,0)
|
620
|
-
values.each_pair do |k,v|
|
621
|
-
instance_variable_set("@#{ k }",v)
|
622
|
-
end
|
623
|
-
end
|
388
|
+
def fuse_respond_to?(fuse_method)
|
389
|
+
return false unless @fuse_delegate.respond_to?(fuse_method)
|
390
|
+
|
391
|
+
m = @fuse_delegate.method(fuse_method)
|
392
|
+
m = m.super_method while m && FFI::Libfuse::Adapter.include?(m.owner)
|
393
|
+
|
394
|
+
m && true
|
624
395
|
end
|
625
396
|
|
626
|
-
|
627
|
-
|
628
|
-
|
397
|
+
def respond_to_missing?(method, private=false)
|
398
|
+
FFI::Libfuse::FuseOperations.fuse_callbacks.include?(method) ? @fuse_delegate.respond_to?(method, private) : super
|
399
|
+
end
|
629
400
|
|
630
|
-
|
631
|
-
|
401
|
+
def method_missing(method_name, *args, &block)
|
402
|
+
return super unless FFI::Libfuse::FuseOperations.fuse_callbacks.include?(method_name) && @fuse_delegate.respond_to?(method_name)
|
403
|
+
begin
|
404
|
+
$stderr.puts "==> \#{ self }.#{method_name}(\#{args.inspect })" if debug?
|
405
|
+
result = @fuse_delegate.send(method_name,*args,&block)
|
406
|
+
$stderr.puts "<== \#{ self }.#{method_name}()" if debug?
|
407
|
+
result
|
408
|
+
rescue => ex
|
409
|
+
$@.delete_if{|s| /^\\(__FUSE_DELEGATE__\\):/ =~ s}
|
410
|
+
$stderr.puts(ex.message) unless ex.respond_to?(:errno) || debug?
|
411
|
+
Kernel::raise
|
412
|
+
end
|
413
|
+
end
|
632
414
|
|
633
|
-
|
634
|
-
|
415
|
+
def call_sigmethod(sigmethod)
|
416
|
+
warn "==> #{self}.#{sigmethod}()" if debug?
|
417
|
+
dlg = @fuse_delegate.respond_to?(sigmethod) ? @fuse_delegate : self
|
418
|
+
dlg.send(sigmethod)
|
419
|
+
warn "<== #{self}.#{sigmethod}()" if debug?
|
420
|
+
end
|
635
421
|
|
636
|
-
|
637
|
-
|
638
|
-
@f_bsize, @f_frsize, @f_blocks, @f_bfree, @f_bavail, @f_files, @f_ffree, @f_favail,@f_fsid, @f_flag,@f_namemax = Array.new(13,0)
|
639
|
-
values.each_pair do |k,v|
|
640
|
-
prefix = k.to_s.start_with?("f_") ? "" : "f_"
|
641
|
-
instance_variable_set("@#{prefix}#{k}",v)
|
642
|
-
end
|
643
|
-
end
|
422
|
+
def respond_sigmethod?(sigmethod)
|
423
|
+
@fuse_delegate.respond_to?(sigmethod) || respond_to?(sigmethod)
|
644
424
|
end
|
645
|
-
|
425
|
+
|
426
|
+
# Remove Kernel:open etc..
|
427
|
+
FFI::Libfuse::FuseOperations.fuse_callbacks.each { |c| undef_method(c) rescue nil }
|
428
|
+
end
|
429
|
+
|
430
|
+
end
|