rfuse 1.1.2 → 1.2.0.rc202009.69
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -13
- data/CHANGES.md +6 -0
- data/README.md +3 -2
- data/ext/rfuse/rfuse.c +1 -1
- data/lib/rfuse/version.rb +1 -1
- metadata +24 -42
- data/.gitignore +0 -5
- data/.travis.yml +0 -20
- data/Gemfile +0 -4
- data/Rakefile +0 -17
- data/ext/.gitignore +0 -2
- data/rfuse.gemspec +0 -31
- data/sample/test-ruby.rb +0 -310
- data/spec/basic_spec.rb +0 -243
- data/spec/fuse_file_info_spec.rb +0 -119
- data/spec/main_spec.rb +0 -161
- data/spec/options_spec.rb +0 -143
- data/spec/ruby_loop_spec.rb +0 -63
- data/spec/run_spec.rb +0 -60
- data/spec/signals_spec.rb +0 -108
- data/spec/spec_helper.rb +0 -109
- data/spec/xattr_spec.rb +0 -49
data/spec/options_spec.rb
DELETED
@@ -1,143 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
require 'pathname'
|
3
|
-
require 'tempfile'
|
4
|
-
|
5
|
-
describe RFuse do
|
6
|
-
|
7
|
-
let(:dir_stat) { RFuse::Stat.directory(0444) }
|
8
|
-
let(:file_stat) { RFuse::Stat.file(0444) }
|
9
|
-
let!(:mockfs) { m = mock("fuse"); m.stub(:getattr).and_return(nil); m }
|
10
|
-
let(:mountpoint) { tempmount() }
|
11
|
-
|
12
|
-
context "mount options" do
|
13
|
-
it "should handle -h" do
|
14
|
-
fuse = RFuse::FuseDelegator.new(mockfs,mountpoint,"-h")
|
15
|
-
fuse.mounted?.should be_falsey
|
16
|
-
lambda { fuse.loop }.should raise_error(RFuse::Error)
|
17
|
-
end
|
18
|
-
|
19
|
-
it "should behave sensibly for bad mountpoint" do
|
20
|
-
fuse = RFuse::FuseDelegator.new(mockfs,"bad/mount/point")
|
21
|
-
fuse.mounted?.should be_falsey
|
22
|
-
lambda { fuse.loop }.should raise_error(RFuse::Error)
|
23
|
-
end
|
24
|
-
|
25
|
-
it "should behave sensibly for bad options" do
|
26
|
-
fuse = RFuse::FuseDelegator.new(mockfs,mountpoint,"-eviloption")
|
27
|
-
fuse.mounted?.should be_falsey
|
28
|
-
lambda { fuse.loop }.should raise_error(RFuse::Error)
|
29
|
-
end
|
30
|
-
|
31
|
-
it "should handle a Pathname as a mountpoint" do
|
32
|
-
fuse = RFuse::FuseDelegator.new(mockfs,Pathname.new(mountpoint))
|
33
|
-
fuse.mounted?.should be(true)
|
34
|
-
fuse.unmount()
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
context "#parse_options" do
|
39
|
-
|
40
|
-
it "should detect -h" do
|
41
|
-
argv = [ "/mountpoint","-h" ]
|
42
|
-
result = RFuse.parse_options(argv)
|
43
|
-
|
44
|
-
result[:help].should be(true)
|
45
|
-
result[:mountpoint].should == "/mountpoint"
|
46
|
-
result[:debug].should be_falsey
|
47
|
-
end
|
48
|
-
|
49
|
-
it "should detect -h mixed with -o" do
|
50
|
-
argv = [ "/mountpoint","-h", "-o", "debug" ]
|
51
|
-
result = RFuse.parse_options(argv)
|
52
|
-
|
53
|
-
result[:help].should be(true)
|
54
|
-
result[:mountpoint].should == "/mountpoint"
|
55
|
-
result[:debug].should be(true)
|
56
|
-
end
|
57
|
-
|
58
|
-
it "should detect debug" do
|
59
|
-
argv = [ "/mountpoint","-o","debug" ]
|
60
|
-
result = RFuse.parse_options(argv)
|
61
|
-
|
62
|
-
result[:debug].should be(true)
|
63
|
-
result[:help].should be_falsey
|
64
|
-
|
65
|
-
argv = [ "/mountpoint","-o","default_permissions,debug" ]
|
66
|
-
result = RFuse.parse_options(argv)
|
67
|
-
result[:debug].should be(true)
|
68
|
-
end
|
69
|
-
|
70
|
-
it "detects debug as -d" do
|
71
|
-
argv = [ "/mountpoint","-o","someopt","-d" ]
|
72
|
-
result = RFuse.parse_options(argv)
|
73
|
-
result[:debug].should be(true)
|
74
|
-
end
|
75
|
-
|
76
|
-
it "should remove local options" do
|
77
|
-
argv = [ "/mountpoint","-o","debug,myoption" ]
|
78
|
-
|
79
|
-
result = RFuse.parse_options(argv,:myoption)
|
80
|
-
|
81
|
-
result[:debug].should be(true)
|
82
|
-
result[:myoption].should be(true)
|
83
|
-
|
84
|
-
argv.should == [ "/mountpoint", "-o", "debug" ]
|
85
|
-
end
|
86
|
-
|
87
|
-
it "should remove the only local option" do
|
88
|
-
argv = ["/mountpoint","-o","myoption" ]
|
89
|
-
|
90
|
-
result = RFuse.parse_options(argv,:myoption)
|
91
|
-
|
92
|
-
result[:myoption].should be(true)
|
93
|
-
argv.should == [ "/mountpoint" ]
|
94
|
-
end
|
95
|
-
|
96
|
-
|
97
|
-
it "parses the value from the only local option" do
|
98
|
-
argv = [ "adevice", "/mountpoint", "-o","someopt=somevalue"]
|
99
|
-
result = RFuse.parse_options(argv,:someopt)
|
100
|
-
|
101
|
-
result[:someopt].should == "somevalue"
|
102
|
-
argv.should == [ "/mountpoint" ]
|
103
|
-
end
|
104
|
-
|
105
|
-
it "should parse values from options" do
|
106
|
-
argv = [ "/mountpoint", "-o", "debug,someopt=somevalue" ]
|
107
|
-
result = RFuse.parse_options(argv)
|
108
|
-
|
109
|
-
result[:someopt].should == "somevalue"
|
110
|
-
end
|
111
|
-
|
112
|
-
it "should parse values and remove local options" do
|
113
|
-
argv = [ "/mountpoint", "-o", "debug,someopt=somevalue" ]
|
114
|
-
result = RFuse.parse_options(argv,:someopt)
|
115
|
-
|
116
|
-
result[:someopt].should == "somevalue"
|
117
|
-
argv[2].should == "debug"
|
118
|
-
end
|
119
|
-
|
120
|
-
it "should parse and remove optional device" do
|
121
|
-
|
122
|
-
argv = [ "a device", "/mountpoint" , "-o", "rw,debug,default_permissions" ]
|
123
|
-
result = RFuse.parse_options(argv)
|
124
|
-
|
125
|
-
result[:device].should == "a device"
|
126
|
-
result[:mountpoint].should == "/mountpoint"
|
127
|
-
result[:rw].should be(true)
|
128
|
-
result[:debug].should be(true)
|
129
|
-
result[:default_permissions].should be(true)
|
130
|
-
|
131
|
-
argv.should == [ "/mountpoint" , "-o", "rw,debug,default_permissions" ]
|
132
|
-
end
|
133
|
-
|
134
|
-
context "with '-' in device" do
|
135
|
-
it "should parse the device correctly" do
|
136
|
-
argv = [ "a-device", "/mountpoint" , "-o", "rw,debug,default_permissions" ]
|
137
|
-
result = RFuse.parse_options(argv)
|
138
|
-
|
139
|
-
result[:device].should == "a-device"
|
140
|
-
end
|
141
|
-
end
|
142
|
-
end
|
143
|
-
end
|
data/spec/ruby_loop_spec.rb
DELETED
@@ -1,63 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe RFuse::Fuse do
|
4
|
-
|
5
|
-
let(:mockfs) { m = mock("fuse"); m.stub(:getattr).and_return(nil) ; m }
|
6
|
-
let(:mountpoint) { tempmount() }
|
7
|
-
|
8
|
-
context "#loop" do
|
9
|
-
it "should exit from another thread and allow multiple loops" do
|
10
|
-
|
11
|
-
fuse = RFuse::FuseDelegator.new(mockfs,mountpoint)
|
12
|
-
t = Thread.new { sleep 0.2; fuse.exit }
|
13
|
-
fuse.loop()
|
14
|
-
t.join
|
15
|
-
t = Thread.new { sleep 0.2; fuse.exit }
|
16
|
-
fuse.loop
|
17
|
-
t.join
|
18
|
-
fuse.unmount
|
19
|
-
fuse.mounted?.should be(false)
|
20
|
-
end
|
21
|
-
|
22
|
-
|
23
|
-
# This will never work!
|
24
|
-
#it "should allow threads to operate on the filesystem" do
|
25
|
-
#
|
26
|
-
# mockfs = mock("fuse")
|
27
|
-
# mockfs.stub(:getattr).and_return(nil)
|
28
|
-
#
|
29
|
-
# fuse = RFuse::FuseDelegator.new(mockfs,"/tmp/rfuse-spec")
|
30
|
-
#
|
31
|
-
# t = Thread.new { sleep 0.5 ; File.stat("/tmp/rfuse-spec/thread") ; fuse.exit }
|
32
|
-
#
|
33
|
-
# fuse.loop()
|
34
|
-
# fuse.unmount()
|
35
|
-
#end
|
36
|
-
|
37
|
-
it "should allow other threads to be scheduled" do
|
38
|
-
|
39
|
-
file_stat = RFuse::Stat.file(0444)
|
40
|
-
|
41
|
-
thread_ran = false
|
42
|
-
|
43
|
-
mockfs.stub(:getattr).with(anything(),"/before") {
|
44
|
-
thread_ran.should be(false)
|
45
|
-
file_stat
|
46
|
-
}
|
47
|
-
|
48
|
-
mockfs.stub(:getattr).with(anything(),"/missing") {
|
49
|
-
#GC.start()
|
50
|
-
thread_ran.should be(true)
|
51
|
-
file_stat
|
52
|
-
}
|
53
|
-
|
54
|
-
t = Thread.new() { sleep 0.5 ; thread_ran = true }
|
55
|
-
with_fuse(mountpoint,mockfs) do
|
56
|
-
File.stat("#{mountpoint}/before");
|
57
|
-
sleep 1;
|
58
|
-
File.stat("#{mountpoint}/missing");
|
59
|
-
end
|
60
|
-
end
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
data/spec/run_spec.rb
DELETED
@@ -1,60 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe RFuse::Fuse do
|
4
|
-
context "#run" do
|
5
|
-
|
6
|
-
let(:file_stat) { RFuse::Stat.file(0444) }
|
7
|
-
let(:mountpoint) { tempmount() }
|
8
|
-
let(:mockfs) { m = double("fuse"); allow(m).to receive(:getattr) { |ctx,path| puts "#{ctx},#{path}"}; m }
|
9
|
-
let(:fuse) { RFuse::FuseDelegator.new(mockfs,mountpoint) }
|
10
|
-
|
11
|
-
it "runs a mounted filesystem with default traps" do
|
12
|
-
|
13
|
-
# expect traps to be set
|
14
|
-
expect(mockfs).to receive(:sighup) { }
|
15
|
-
expect(mockfs).to receive(:getattr).with(anything(),"/run").and_return(file_stat)
|
16
|
-
|
17
|
-
# Need to call this before we fork..
|
18
|
-
expect(fuse.mountpoint).to eq(mountpoint)
|
19
|
-
|
20
|
-
pid = Process.pid
|
21
|
-
|
22
|
-
fpid = Kernel.fork do
|
23
|
-
File.stat("#{mountpoint}/run")
|
24
|
-
Process.kill("HUP",pid)
|
25
|
-
sleep(0.1)
|
26
|
-
Process.kill("TERM",pid)
|
27
|
-
end
|
28
|
-
|
29
|
-
fuse.run
|
30
|
-
|
31
|
-
rpid,result = Process.waitpid2(fpid)
|
32
|
-
expect(result).to be_success
|
33
|
-
|
34
|
-
expect(fuse).not_to be_mounted
|
35
|
-
expect(Signal.trap("HUP","DEFAULT")).to eq("DEFAULT")
|
36
|
-
end
|
37
|
-
|
38
|
-
it "unmounts the filesystem and resets traps on exception" do
|
39
|
-
expect(mockfs).to receive(:getattr).with(anything(),"/run").and_return(file_stat)
|
40
|
-
# raising an error in a sighandler will exit the loop..
|
41
|
-
expect(mockfs).to receive(:sighup).and_raise("oh noes")
|
42
|
-
pid = Process.pid
|
43
|
-
# Need to call this before we fork..
|
44
|
-
expect(fuse.mountpoint).to eq(mountpoint)
|
45
|
-
|
46
|
-
fpid = Kernel.fork do
|
47
|
-
File.stat("#{mountpoint}/run")
|
48
|
-
Process.kill("HUP",pid)
|
49
|
-
end
|
50
|
-
|
51
|
-
expect { fuse.run }.to raise_error
|
52
|
-
|
53
|
-
pid,result = Process.waitpid2(fpid)
|
54
|
-
|
55
|
-
expect(fuse).not_to be_mounted
|
56
|
-
expect(Signal.trap("HUP","DEFAULT")).to eq("DEFAULT")
|
57
|
-
end
|
58
|
-
|
59
|
-
end
|
60
|
-
end
|
data/spec/signals_spec.rb
DELETED
@@ -1,108 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe RFuse::Fuse do
|
4
|
-
|
5
|
-
let(:mountpoint) { tempmount() }
|
6
|
-
let(:fuse) { RFuse::Fuse.new(mountpoint) }
|
7
|
-
|
8
|
-
before(:each) do
|
9
|
-
# Override traps (note rspec itself traps "INT") with "DEFAULT"
|
10
|
-
@traps = ["INT","TERM","HUP","USR1","USR2"].inject({}) { |h,signame| h[signame] = Signal.trap(signame,"DEFAULT"); h }
|
11
|
-
end
|
12
|
-
|
13
|
-
after(:each) do
|
14
|
-
fuse.unmount()
|
15
|
-
# Restore previously set traps
|
16
|
-
@traps.each_pair { |signame,prev_trap| Signal.trap(signame,prev_trap) }
|
17
|
-
end
|
18
|
-
|
19
|
-
context "#trap_signals" do
|
20
|
-
|
21
|
-
it "returns the list of signals trapped" do
|
22
|
-
|
23
|
-
allow(fuse).to receive(:sighup)
|
24
|
-
|
25
|
-
expect(fuse.trap_signals("HUP")).to include("HUP")
|
26
|
-
expect(Signal.trap("HUP","DEFAULT")).not_to eq("DEFAULT")
|
27
|
-
end
|
28
|
-
|
29
|
-
it "does not override previously set traps" do
|
30
|
-
|
31
|
-
allow(fuse).to receive(:sighup)
|
32
|
-
|
33
|
-
Signal.trap("HUP","HUPCOMMAND")
|
34
|
-
|
35
|
-
expect(fuse.trap_signals()).not_to include("HUP")
|
36
|
-
expect(Signal.trap("HUP","DEFAULT")).to eq("HUPCOMMAND")
|
37
|
-
end
|
38
|
-
|
39
|
-
it "only traps the specified signals" do
|
40
|
-
|
41
|
-
allow(fuse).to receive(:sighup)
|
42
|
-
allow(fuse).to receive(:sigusr2)
|
43
|
-
|
44
|
-
expect(fuse.trap_signals("HUP")).to include("HUP")
|
45
|
-
expect(Signal.trap("HUP","DEFAULT")).not_to eq("DEFAULT")
|
46
|
-
expect(Signal.trap("USR2","DEFAULT")).to eq("DEFAULT")
|
47
|
-
end
|
48
|
-
|
49
|
-
it "allows signals to be handled by the filesystem" do
|
50
|
-
expect(fuse).to receive(:sighup) { fuse.exit }
|
51
|
-
|
52
|
-
expect(fuse.trap_signals("HUP")).to include("HUP")
|
53
|
-
|
54
|
-
pid = Process.pid
|
55
|
-
fork_fuse(fuse) { Process.kill("HUP",pid) }
|
56
|
-
# Exitted loop, but still mounted - ie not with fusermount -u
|
57
|
-
expect(fuse).to be_mounted
|
58
|
-
end
|
59
|
-
|
60
|
-
it "exits on INT by default" do
|
61
|
-
pid = Process.pid
|
62
|
-
expect(fuse.trap_signals("INT")).to include("INT")
|
63
|
-
|
64
|
-
fork_fuse(fuse) { Process.kill("INT",pid) }
|
65
|
-
expect(fuse).to be_mounted
|
66
|
-
end
|
67
|
-
|
68
|
-
it "exits on TERM by default" do
|
69
|
-
pid = Process.pid
|
70
|
-
expect(fuse.trap_signals("TERM")).to include("TERM")
|
71
|
-
|
72
|
-
fork_fuse(fuse) { Process.kill("TERM",pid) }
|
73
|
-
expect(fuse).to be_mounted
|
74
|
-
end
|
75
|
-
|
76
|
-
context "with a delegated filesystem" do
|
77
|
-
let(:mockfs) { m = mock("fuse"); m.stub(:getattr).and_return(nil); m }
|
78
|
-
let(:fuse) { RFuse::FuseDelegator.new(mockfs,mountpoint) }
|
79
|
-
|
80
|
-
it "enables FuseDelegator debugging on USR1" do
|
81
|
-
|
82
|
-
pid = Process.pid
|
83
|
-
expect(fuse.trap_signals("USR1","INT")).to include("USR1")
|
84
|
-
|
85
|
-
expect(fuse.debug?).to be(false)
|
86
|
-
|
87
|
-
fork_fuse(fuse) do
|
88
|
-
Process.kill("USR1",pid)
|
89
|
-
sleep(0.1)
|
90
|
-
Process.kill("INT",pid)
|
91
|
-
end
|
92
|
-
|
93
|
-
expect(fuse.debug?).to be(true)
|
94
|
-
end
|
95
|
-
|
96
|
-
it "allows delegate filesystem to override default sig handler method" do
|
97
|
-
expect(mockfs).to receive(:sigint) { fuse.exit }
|
98
|
-
|
99
|
-
pid = Process.pid
|
100
|
-
fuse.trap_signals("INT")
|
101
|
-
|
102
|
-
fork_fuse(fuse) do
|
103
|
-
Process.kill("INT",pid)
|
104
|
-
end
|
105
|
-
end
|
106
|
-
end
|
107
|
-
end
|
108
|
-
end
|
data/spec/spec_helper.rb
DELETED
@@ -1,109 +0,0 @@
|
|
1
|
-
require 'rfuse'
|
2
|
-
require 'tmpdir'
|
3
|
-
|
4
|
-
class FileModeMatcher
|
5
|
-
def initialize(expected)
|
6
|
-
@expected = expected
|
7
|
-
end
|
8
|
-
|
9
|
-
def description
|
10
|
-
"file mode #{@expected}"
|
11
|
-
end
|
12
|
-
|
13
|
-
def ==(actual)
|
14
|
-
(actual | RFuse::Stat::S_IFMT) == (@expected | RFuse::Stat::S_IFMT)
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
module DoubleAliases
|
19
|
-
def mock(*args, &block)
|
20
|
-
double(*args, &block)
|
21
|
-
end
|
22
|
-
alias stub mock
|
23
|
-
end
|
24
|
-
|
25
|
-
|
26
|
-
module RFuseHelper
|
27
|
-
# Runs the single threaded fuse loop
|
28
|
-
# on a pre configured mock fuse filesystem
|
29
|
-
# Executes fork block in a separate process
|
30
|
-
# that is expected to return success
|
31
|
-
def with_fuse(mnt,mockfs,*options,&fork_block)
|
32
|
-
|
33
|
-
fuse = RFuse::FuseDelegator.new(mockfs,mnt,*options)
|
34
|
-
fuse.mounted?.should be(true)
|
35
|
-
fork_fuse(fuse) do
|
36
|
-
begin
|
37
|
-
fork_block.call() if fork_block
|
38
|
-
ensure
|
39
|
-
fusermount(mnt)
|
40
|
-
end
|
41
|
-
end
|
42
|
-
fuse.open_files.should be_empty()
|
43
|
-
fuse.mounted?.should be(false)
|
44
|
-
end
|
45
|
-
|
46
|
-
def fork_fuse(fuse,&fork_block)
|
47
|
-
|
48
|
-
fpid = Kernel.fork() { fork_block.call() }
|
49
|
-
|
50
|
-
fuse.loop
|
51
|
-
|
52
|
-
pid,result = Process.waitpid2(fpid)
|
53
|
-
result.should be_success
|
54
|
-
end
|
55
|
-
|
56
|
-
def fusermount(mnt)
|
57
|
-
unless system("fusermount -u #{mnt} >/dev/null 2>&1")
|
58
|
-
sleep(0.5)
|
59
|
-
system("fusermount -u #{mnt}")
|
60
|
-
end
|
61
|
-
end
|
62
|
-
|
63
|
-
def file_mode(mode)
|
64
|
-
FileModeMatcher.new(mode)
|
65
|
-
end
|
66
|
-
|
67
|
-
def tempmount()
|
68
|
-
Dir.mktmpdir("rfuse-spec")
|
69
|
-
end
|
70
|
-
|
71
|
-
# Generate a set of times with non-zero usec values
|
72
|
-
def usec_times(*increments)
|
73
|
-
increments.collect { |inc|
|
74
|
-
begin
|
75
|
-
time = Time.now() + inc
|
76
|
-
sleep(0.001)
|
77
|
-
end until time.usec != 0
|
78
|
-
time
|
79
|
-
}
|
80
|
-
end
|
81
|
-
|
82
|
-
end
|
83
|
-
|
84
|
-
RSpec.configure do |config|
|
85
|
-
config.expect_with :rspec do |c|
|
86
|
-
c.syntax = [:should, :expect]
|
87
|
-
end
|
88
|
-
config.mock_with :rspec do |c|
|
89
|
-
c.syntax = [:should, :expect]
|
90
|
-
end
|
91
|
-
|
92
|
-
config.after(:suite) do
|
93
|
-
Dir.glob(Dir.tmpdir + "/rfuse-spec*") do |dir|
|
94
|
-
count = 0
|
95
|
-
begin
|
96
|
-
system("fusermount -u #{dir} >/dev/null 2>&1")
|
97
|
-
FileUtils.rmdir(dir)
|
98
|
-
rescue Errno::EBUSY
|
99
|
-
#puts "Cleaning up #{dir} is busy, retrying..."
|
100
|
-
sleep(0.5)
|
101
|
-
count = count + 1
|
102
|
-
retry unless count > 3
|
103
|
-
end
|
104
|
-
end
|
105
|
-
end
|
106
|
-
|
107
|
-
config.include RFuseHelper
|
108
|
-
config.include DoubleAliases
|
109
|
-
end
|