rfuse 1.0.5 → 1.1.0.RC0
Sign up to get free protection for your applications and to get access to all the features.
- 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)) }
|