rfuse 1.0.5 → 1.1.0.RC0
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 +7 -0
- data/.yardopts +2 -0
- data/CHANGES.md +12 -0
- data/README.md +7 -9
- data/ext/rfuse/context.c +13 -2
- data/ext/rfuse/file_info.c +4 -1
- data/ext/rfuse/filler.c +1 -1
- data/ext/rfuse/rfuse.c +24 -6
- data/lib/rfuse/version.rb +1 -1
- data/lib/rfuse.rb +362 -41
- data/rfuse.gemspec +1 -1
- data/sample/test-ruby.rb +7 -36
- data/spec/basic_spec.rb +4 -6
- data/spec/fuse_file_info_spec.rb +6 -6
- data/spec/main_spec.rb +161 -0
- data/spec/options_spec.rb +22 -22
- data/spec/ruby_loop_spec.rb +15 -17
- data/spec/run_spec.rb +60 -0
- data/spec/signals_spec.rb +108 -0
- data/spec/spec_helper.rb +56 -10
- data/spec/xattr_spec.rb +1 -1
- metadata +29 -49
- data/.travis.disabled.yml +0 -8
data/lib/rfuse.rb
CHANGED
@@ -6,6 +6,7 @@ require 'rfuse/compat'
|
|
6
6
|
# Ruby FUSE (Filesystem in USErspace) binding
|
7
7
|
module RFuse
|
8
8
|
|
9
|
+
# @private
|
9
10
|
# Used by listxattr
|
10
11
|
def self.packxattr(xattrs)
|
11
12
|
case xattrs
|
@@ -15,7 +16,7 @@ module RFuse
|
|
15
16
|
#assume already \0 separated list of keys
|
16
17
|
xattrs
|
17
18
|
else
|
18
|
-
raise
|
19
|
+
raise Error, ":listxattr must return Array or String, got #{xattrs.inspect}"
|
19
20
|
end
|
20
21
|
end
|
21
22
|
|
@@ -29,54 +30,55 @@ module RFuse
|
|
29
30
|
# from argv, ie so argv is a clean set of options that can be passed
|
30
31
|
# to {RFuse::Fuse} or {RFuse::FuseDelegator}
|
31
32
|
#
|
32
|
-
#
|
33
|
-
# @return [Hash
|
33
|
+
#
|
34
|
+
# @return [Hash<Symbol,String|Boolean>]
|
34
35
|
# the extracted options
|
35
36
|
#
|
36
37
|
# @since 1.0.3
|
37
|
-
#
|
38
|
+
#
|
38
39
|
# Fuse itself will normalise arguments
|
39
40
|
#
|
40
|
-
#
|
41
|
-
#
|
41
|
+
# mount -t fuse </path/to/fs.rb>#<device> mountpoint [options...]
|
42
|
+
# mount.fuse </path/to/fs.rb>#<device> mountpoint [options...]
|
42
43
|
#
|
43
|
-
# both result in
|
44
|
+
# both result in the following command execution
|
44
45
|
#
|
45
|
-
#
|
46
|
+
# /path/to/fs.rb [device] mountpoint [-h] [-d] [-o [opt,optkey=value,...]]
|
46
47
|
#
|
47
48
|
# which this method will parse into a Hash with the following special keys
|
48
49
|
#
|
49
|
-
#
|
50
|
-
#
|
51
|
-
#
|
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
|
52
54
|
#
|
53
55
|
# and any other supplied options.
|
54
56
|
#
|
55
57
|
# @example
|
56
58
|
# ARGV = [ "some/device", "/mnt/point", "-h", "-o", "debug,myfs=aValue" ]
|
57
59
|
# options = RFuse.parse_options(ARGV,:myfs)
|
58
|
-
#
|
59
|
-
# # options ==
|
60
|
+
#
|
61
|
+
# # options ==
|
60
62
|
# { :device => "some/device",
|
61
63
|
# :mountpoint => "/mnt/point",
|
62
64
|
# :help => true,
|
63
65
|
# :debug => true,
|
64
66
|
# :myfs => "aValue"
|
65
67
|
# }
|
66
|
-
# # and ARGV ==
|
68
|
+
# # and ARGV ==
|
67
69
|
# [ "/mnt/point","-h","-o","debug" ]
|
68
70
|
#
|
69
71
|
# fs = create_filesystem(options)
|
70
72
|
# fuse = RFuse::FuseDelegator.new(fs,*ARGV)
|
71
|
-
#
|
73
|
+
#
|
72
74
|
def self.parse_options(argv,*local_opts)
|
73
75
|
result = Hash.new(nil)
|
74
|
-
|
75
|
-
first_opt_index = (argv.find_index() { |opt| opt =~ /-.*/ } || argv.length )
|
76
|
+
|
77
|
+
first_opt_index = (argv.find_index() { |opt| opt =~ /-.*/ } || argv.length )
|
76
78
|
|
77
79
|
result[:device] = argv.shift if first_opt_index > 1
|
78
80
|
result[:mountpoint] = argv[0] if argv.length > 0
|
79
|
-
|
81
|
+
|
80
82
|
if argv.include?("-h")
|
81
83
|
result[:help] = true
|
82
84
|
end
|
@@ -90,7 +92,7 @@ module RFuse
|
|
90
92
|
if opt_index > 1 && opt_index < argv.length
|
91
93
|
options = argv[opt_index].split(",")
|
92
94
|
|
93
|
-
options.delete_if() do |opt|
|
95
|
+
options.delete_if() do |opt|
|
94
96
|
opt.strip!
|
95
97
|
|
96
98
|
opt,value = opt.split("=",2)
|
@@ -118,16 +120,188 @@ module RFuse
|
|
118
120
|
# @param [String] exec the executable
|
119
121
|
# @return [String] the usage string
|
120
122
|
def self.usage(device=nil,exec=File.basename($0))
|
121
|
-
"Usage:\n
|
123
|
+
"Usage:\n #{exec} #{device} mountpoint [-h] [-d] [-o [opt,optkey=value,...]]\n"
|
122
124
|
end
|
123
125
|
|
126
|
+
# Convenience method to launch a fuse filesystem, with nice usage messages and default signal traps
|
127
|
+
#
|
128
|
+
# @param [Array<String>] argv command line arguments
|
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
|
133
|
+
#
|
134
|
+
# @yieldparam [Hash<Symbol,String>] options See {.parse_options}
|
135
|
+
# @yieldparam [Array<String>] argv Cleaned argv See {.parse_options}
|
136
|
+
#
|
137
|
+
# @yieldreturn [Class<Fuse>]
|
138
|
+
# a subclass of {Fuse} that implements your filesystem. Will receive `.new(*extra_options,*argv)`
|
139
|
+
#
|
140
|
+
# @yieldreturn [Class]
|
141
|
+
# a class that implements {FuseDelegator::FUSE_METHODS}. Will receive `.new(*extra_options)`
|
142
|
+
# and the resulting instance sent with `*argv` to {FuseDelegator}
|
143
|
+
#
|
144
|
+
# @yieldreturn [Fuse]
|
145
|
+
# Your initialised (and therefore already mounted) filesystem
|
146
|
+
#
|
147
|
+
# @yieldreturn [Object]
|
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
|
152
|
+
#
|
153
|
+
# @since 1.1.0
|
154
|
+
#
|
155
|
+
# @example
|
156
|
+
#
|
157
|
+
# class MyFuse < Fuse
|
158
|
+
# def initialize(myfs,*argv)
|
159
|
+
# @myfs = myfs # my filesystem local option value
|
160
|
+
# super(*argv)
|
161
|
+
# end
|
162
|
+
# # ... implementations for filesystem methods ...
|
163
|
+
# end
|
164
|
+
#
|
165
|
+
# class MyFSClass # not a subclass of Fuse, FuseDelegator used
|
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])
|
201
|
+
#
|
202
|
+
# end
|
203
|
+
#
|
204
|
+
def self.main(argv=ARGV,extra_options=[],extra_options_usage=nil,device=nil,exec=File.basename($0))
|
205
|
+
|
206
|
+
options = parse_options(argv,*extra_options)
|
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
|
255
|
+
|
256
|
+
end
|
257
|
+
|
258
|
+
|
259
|
+
# @private
|
260
|
+
# Helper to create a {Fuse} from variety of #{.main} yield results
|
261
|
+
def self.create(fs, argv=[], options = {}, extra_options = [])
|
262
|
+
if fs.kind_of?(Fuse)
|
263
|
+
fs
|
264
|
+
elsif fs.is_a?(Class)
|
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
|
275
|
+
end
|
276
|
+
|
277
|
+
public
|
278
|
+
|
124
279
|
class Fuse
|
125
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
|
+
|
126
301
|
# Main processing loop
|
127
302
|
#
|
128
303
|
# Use {#exit} to stop processing (or externally call fusermount -u)
|
129
304
|
#
|
130
|
-
#
|
131
305
|
# Other ruby threads can continue while loop is running, however
|
132
306
|
# no thread can operate on the filesystem itself (ie with File or Dir methods)
|
133
307
|
#
|
@@ -143,8 +317,12 @@ module RFuse
|
|
143
317
|
|
144
318
|
if ready.include?(@pr)
|
145
319
|
|
146
|
-
@pr.
|
147
|
-
|
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
|
148
326
|
|
149
327
|
elsif errors.include?(@fuse_io)
|
150
328
|
|
@@ -159,28 +337,85 @@ module RFuse
|
|
159
337
|
@running = false
|
160
338
|
end
|
161
339
|
end
|
162
|
-
rescue
|
340
|
+
rescue Errno::EWOULDBLOCK, Errno::EAGAIN
|
163
341
|
#oh well...
|
164
342
|
end
|
165
343
|
end
|
166
344
|
end
|
167
345
|
|
168
346
|
# Stop processing {#loop}
|
169
|
-
# eg called from
|
347
|
+
# eg called from signal handlers, or some other monitoring thread
|
170
348
|
def exit
|
171
|
-
@
|
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
|
172
403
|
end
|
404
|
+
alias :sigint :sigterm
|
173
405
|
|
174
406
|
private
|
175
407
|
|
176
408
|
# Called by the C iniitialize
|
177
409
|
# afer the filesystem has been mounted successfully
|
178
410
|
def ruby_initialize
|
411
|
+
@running = false
|
412
|
+
|
413
|
+
# Self-pipe for handling signals and exit
|
179
414
|
@pr,@pw = IO.pipe()
|
180
415
|
|
181
416
|
# The FD was created by FUSE so we don't want
|
182
417
|
# ruby to do anything with it during GC
|
183
|
-
@fuse_io = IO.for_fd(fd(),"r",:autoclose => false)
|
418
|
+
@fuse_io = IO.for_fd(fd(),"r",:autoclose => false)
|
184
419
|
end
|
185
420
|
|
186
421
|
# Called by C unmount before doing all the FUSE stuff
|
@@ -194,6 +429,22 @@ module RFuse
|
|
194
429
|
# we can do
|
195
430
|
@fuse_io.close() if @fuse_io && !@fuse_io.closed? && @fuse_io.autoclose?
|
196
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
|
197
448
|
end
|
198
449
|
|
199
450
|
#This class is useful to make your filesystem implementation
|
@@ -203,7 +454,7 @@ module RFuse
|
|
203
454
|
|
204
455
|
# Available fuse methods -see http://fuse.sourceforge.net/doxygen/structfuse__operations.html
|
205
456
|
# Note :getdir and :utime are deprecated
|
206
|
-
# :ioctl, :poll are not implemented in the C extension
|
457
|
+
# :ioctl, :poll are not implemented in the C extension
|
207
458
|
FUSE_METHODS = [ :getattr, :readlink, :getdir, :mknod, :mkdir,
|
208
459
|
:unlink, :rmdir, :symlink, :rename, :link,
|
209
460
|
:chmod, :chown, :truncate, :utime, :open,
|
@@ -216,32 +467,69 @@ module RFuse
|
|
216
467
|
# @param [Object] fuse_object your filesystem object that responds to fuse methods
|
217
468
|
# @param [String] mountpoint existing directory where the filesystem will be mounted
|
218
469
|
# @param [String...] options fuse mount options (use "-h" to see a list)
|
219
|
-
#
|
470
|
+
#
|
220
471
|
# Create and mount a filesystem
|
221
472
|
#
|
222
|
-
# If ruby debug is enabled then each call to fuse_object will be represented on $stderr
|
223
473
|
def initialize(fuse_object,mountpoint,*options)
|
224
474
|
@fuse_delegate = fuse_object
|
225
475
|
define_fuse_methods(fuse_object)
|
476
|
+
@debug = false
|
477
|
+
self.debug=$DEBUG
|
226
478
|
super(mountpoint,options)
|
227
479
|
end
|
228
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
|
229
517
|
private
|
518
|
+
|
230
519
|
def define_fuse_methods(fuse_object)
|
231
|
-
#Wrap all the rfuse methods in a context block
|
232
520
|
FUSE_METHODS.each do |method|
|
233
521
|
if fuse_object.respond_to?(method)
|
234
522
|
method_name = method.id2name
|
235
523
|
instance_eval(<<-EOM, "(__FUSE_DELEGATE__)",1)
|
236
524
|
def #{method_name} (*args,&block)
|
237
525
|
begin
|
238
|
-
$stderr.puts "==>
|
526
|
+
$stderr.puts "==> \#{ self }.#{ method_name }(\#{args.inspect })" if debug?
|
239
527
|
result = @fuse_delegate.send(:#{method_name},*args,&block)
|
240
|
-
|
528
|
+
$stderr.puts "<== \#{ self }.#{ method_name }()" if debug?
|
241
529
|
result
|
242
|
-
rescue
|
530
|
+
rescue => ex
|
243
531
|
$@.delete_if{|s| /^\\(__FUSE_DELEGATE__\\):/ =~ s}
|
244
|
-
$stderr.puts(ex.message) unless ex.respond_to?(:errno) ||
|
532
|
+
$stderr.puts(ex.message) unless ex.respond_to?(:errno) || debug?
|
245
533
|
Kernel::raise
|
246
534
|
end
|
247
535
|
end
|
@@ -250,25 +538,58 @@ module RFuse
|
|
250
538
|
end
|
251
539
|
end
|
252
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
|
+
|
253
553
|
end #class FuseDelegator
|
254
554
|
|
555
|
+
class Context
|
556
|
+
# @private
|
557
|
+
def to_s
|
558
|
+
"Context::u#{uid},g#{gid},p#{pid}"
|
559
|
+
end
|
560
|
+
end
|
561
|
+
|
562
|
+
class Filler
|
563
|
+
# @private
|
564
|
+
def to_s
|
565
|
+
"Filler::[]"
|
566
|
+
end
|
567
|
+
end
|
568
|
+
|
569
|
+
class FileInfo
|
570
|
+
# @private
|
571
|
+
def to_s
|
572
|
+
"FileInfo::fh->#{fh}"
|
573
|
+
end
|
574
|
+
end
|
575
|
+
|
255
576
|
# Helper class to return from :getattr method
|
256
577
|
class Stat
|
257
578
|
# Format mask
|
258
|
-
S_IFMT = 0170000
|
259
|
-
# Directory
|
579
|
+
S_IFMT = 0170000
|
580
|
+
# Directory
|
260
581
|
S_IFDIR = 0040000
|
261
582
|
# Character device
|
262
583
|
S_IFCHR = 0020000
|
263
584
|
# Block device
|
264
585
|
S_IFBLK = 0060000
|
265
|
-
# Regular file
|
586
|
+
# Regular file
|
266
587
|
S_IFREG = 0100000
|
267
|
-
# FIFO.
|
588
|
+
# FIFO.
|
268
589
|
S_IFIFO = 0010000
|
269
|
-
# Symbolic link
|
590
|
+
# Symbolic link
|
270
591
|
S_IFLNK = 0120000
|
271
|
-
# Socket
|
592
|
+
# Socket
|
272
593
|
S_IFSOCK = 0140000
|
273
594
|
|
274
595
|
# @param [Fixnum] mode file permissions
|
data/rfuse.gemspec
CHANGED
@@ -23,7 +23,7 @@ Gem::Specification.new do |s|
|
|
23
23
|
|
24
24
|
s.add_development_dependency("rake")
|
25
25
|
s.add_development_dependency("rake-compiler")
|
26
|
-
s.add_development_dependency("rspec")
|
26
|
+
s.add_development_dependency("rspec","~> 3.0.0")
|
27
27
|
s.add_development_dependency("yard")
|
28
28
|
s.add_development_dependency("redcarpet")
|
29
29
|
s.add_development_dependency("ffi-xattr")
|
data/sample/test-ruby.rb
CHANGED
@@ -111,7 +111,7 @@ class MyFile
|
|
111
111
|
end
|
112
112
|
def size
|
113
113
|
return content.size
|
114
|
-
end
|
114
|
+
end
|
115
115
|
def isdir
|
116
116
|
false
|
117
117
|
end
|
@@ -127,7 +127,7 @@ class MyFile
|
|
127
127
|
end
|
128
128
|
end
|
129
129
|
|
130
|
-
class MyFuse
|
130
|
+
class MyFuse
|
131
131
|
|
132
132
|
def initialize(root)
|
133
133
|
@root=root
|
@@ -137,7 +137,7 @@ class MyFuse
|
|
137
137
|
def readdir(ctx,path,filler,offset,ffi)
|
138
138
|
d=@root.search(path)
|
139
139
|
if d.isdir then
|
140
|
-
d.each {|name,obj|
|
140
|
+
d.each {|name,obj|
|
141
141
|
filler.push(name,obj.stat,0)
|
142
142
|
}
|
143
143
|
else
|
@@ -211,7 +211,7 @@ class MyFuse
|
|
211
211
|
|
212
212
|
def read(ctx,path,size,offset,fi)
|
213
213
|
d = @root.search(path)
|
214
|
-
if (d.isdir)
|
214
|
+
if (d.isdir)
|
215
215
|
raise Errno::EISDIR.new(path)
|
216
216
|
return nil
|
217
217
|
else
|
@@ -221,7 +221,7 @@ class MyFuse
|
|
221
221
|
|
222
222
|
def write(ctx,path,buf,offset,fi)
|
223
223
|
d=@root.search(path)
|
224
|
-
if (d.isdir)
|
224
|
+
if (d.isdir)
|
225
225
|
raise Errno::EISDIR.new(path)
|
226
226
|
else
|
227
227
|
d.content[offset..offset+buf.length - 1] = buf
|
@@ -236,7 +236,7 @@ class MyFuse
|
|
236
236
|
|
237
237
|
def getxattr(ctx,path,name)
|
238
238
|
d=@root.search(path)
|
239
|
-
if (d)
|
239
|
+
if (d)
|
240
240
|
value=d.getxattr(name)
|
241
241
|
if (!value)
|
242
242
|
value=""
|
@@ -307,33 +307,4 @@ class MyFuse
|
|
307
307
|
|
308
308
|
end #class Fuse
|
309
309
|
|
310
|
-
|
311
|
-
print "\n"
|
312
|
-
print "Usage: [ruby [--debug]] #{$0} mountpoint [mount_options...]\n"
|
313
|
-
print "\n"
|
314
|
-
print " mountpoint must be an existing directory\n"
|
315
|
-
print " mount_option '-h' will list supported options\n"
|
316
|
-
print "\n"
|
317
|
-
print " For verbose debugging output use --debug to ruby\n"
|
318
|
-
print " and '-odebug' as mount_option\n"
|
319
|
-
print "\n"
|
320
|
-
exit(1)
|
321
|
-
end
|
322
|
-
|
323
|
-
fs = MyFuse.new(MyDir.new("",0777));
|
324
|
-
|
325
|
-
fo = RFuse::FuseDelegator.new(fs,*ARGV)
|
326
|
-
|
327
|
-
if fo.mounted?
|
328
|
-
Signal.trap("TERM") { print "Caught TERM\n" ; fo.exit }
|
329
|
-
Signal.trap("INT") { print "Caught INT\n"; fo.exit }
|
330
|
-
|
331
|
-
begin
|
332
|
-
fo.loop
|
333
|
-
rescue
|
334
|
-
print "Error:" + $!
|
335
|
-
ensure
|
336
|
-
fo.unmount if fo.mounted?
|
337
|
-
print "Unmounted #{ARGV[0]}\n"
|
338
|
-
end
|
339
|
-
end
|
310
|
+
RFuse.main(ARGV) { fs = MyFuse.new(MyDir.new("",0777)) }
|