rfusefs 1.0.2 → 1.0.3
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 +5 -13
- data/Gemfile +3 -1
- data/History.rdoc +6 -0
- data/README.rdoc +4 -5
- data/lib/fuse/fusedir.rb +15 -0
- data/lib/fuse/rfusefs-fuse.rb +446 -414
- data/lib/fusefs/metadir.rb +5 -5
- data/lib/fusefs/pathmapper.rb +7 -7
- data/lib/fusefs/sqlitemapper.rb +7 -2
- data/lib/rfusefs.rb +20 -48
- data/lib/rfusefs/version.rb +1 -1
- data/rfusefs.gemspec +2 -2
- data/samples/demo.rb +2 -2
- data/spec/metadir_spec.rb +280 -275
- data/spec/mount_unmount_spec.rb +2 -2
- data/spec/pathmapper_spec.rb +79 -79
- data/spec/rfusefs_spec.rb +505 -497
- data/spec/sample_spec.rb +12 -9
- data/spec/spec_helper.rb +3 -3
- data/spec/sqlitemapper_spec.rb +12 -12
- metadata +26 -27
- data/.travis.yml +0 -8
data/lib/fusefs/metadir.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
module FuseFS
|
2
2
|
|
3
3
|
# A full in-memory filesystem defined with hashes. It is writable to the
|
4
|
-
# user that mounted it
|
4
|
+
# user that mounted it
|
5
5
|
# may create and edit files within it, as well as the programmer
|
6
6
|
# === Usage
|
7
7
|
# root = Metadir.new()
|
@@ -14,10 +14,10 @@ module FuseFS
|
|
14
14
|
# Because Metadir is fully recursive, you can mount your own or other defined
|
15
15
|
# directory structures under it. For example, to mount a dictionary filesystem
|
16
16
|
# (see samples/dictfs.rb), use:
|
17
|
-
#
|
17
|
+
#
|
18
18
|
# root.mkdir("/dict",DictFS.new())
|
19
|
-
#
|
20
|
-
class MetaDir
|
19
|
+
#
|
20
|
+
class MetaDir
|
21
21
|
|
22
22
|
DEFAULT_FS = FuseDir.new()
|
23
23
|
|
@@ -66,7 +66,7 @@ module FuseFS
|
|
66
66
|
# Extended attributes
|
67
67
|
def xattr(path)
|
68
68
|
pathmethod(:xattr,path) do | path |
|
69
|
-
@xattr[path]
|
69
|
+
@xattr[path]
|
70
70
|
end
|
71
71
|
end
|
72
72
|
|
data/lib/fusefs/pathmapper.rb
CHANGED
@@ -13,7 +13,7 @@ module FuseFS
|
|
13
13
|
#
|
14
14
|
class PathMapperFS < FuseDir
|
15
15
|
|
16
|
-
# Represents a
|
16
|
+
# Represents a mapped file or directory
|
17
17
|
class MNode
|
18
18
|
|
19
19
|
# Merge extended attributes with the ones from the underlying file
|
@@ -112,10 +112,10 @@ module FuseFS
|
|
112
112
|
end
|
113
113
|
|
114
114
|
# Compatibility and convenience method
|
115
|
-
# @param [:pm_real_path,String,Symbol] key
|
116
|
-
# @return [String] {#real_path} if key == :pm_real_path
|
115
|
+
# @param [:pm_real_path,String,Symbol] key
|
116
|
+
# @return [String] {#real_path} if key == :pm_real_path
|
117
117
|
# @return [MNode] the node representing the file named key
|
118
|
-
# @return [Object] shortcut for {#options}[key]
|
118
|
+
# @return [Object] shortcut for {#options}[key]
|
119
119
|
def[](key)
|
120
120
|
case key
|
121
121
|
when :pm_real_path
|
@@ -266,7 +266,7 @@ module FuseFS
|
|
266
266
|
# @yieldparam [Hash] filesystem node
|
267
267
|
# @yieldreturn [true,false] should this node be deleted
|
268
268
|
def cleanup(&block)
|
269
|
-
recursive_cleanup(@root,&block)
|
269
|
+
recursive_cleanup(@root,&block)
|
270
270
|
end
|
271
271
|
|
272
272
|
|
@@ -379,7 +379,7 @@ module FuseFS
|
|
379
379
|
file.sysseek(offset)
|
380
380
|
file.syswrite(buf[0,sz])
|
381
381
|
end
|
382
|
-
|
382
|
+
|
383
383
|
# @!visibility private
|
384
384
|
def raw_sync(path,datasync,file=nil)
|
385
385
|
file = @openfiles[path] unless file
|
@@ -393,7 +393,7 @@ module FuseFS
|
|
393
393
|
# @!visibility private
|
394
394
|
def raw_close(path,file=nil)
|
395
395
|
file = @openfiles.delete(path) unless file
|
396
|
-
|
396
|
+
|
397
397
|
if file && !file.closed?
|
398
398
|
begin
|
399
399
|
flags = file.fcntl(Fcntl::F_GETFL) & Fcntl::O_ACCMODE
|
data/lib/fusefs/sqlitemapper.rb
CHANGED
@@ -15,7 +15,7 @@ module FuseFS
|
|
15
15
|
# Maintains a count of the number of times through the scan loop
|
16
16
|
attr_reader :scan_id
|
17
17
|
|
18
|
-
#
|
18
|
+
#
|
19
19
|
#
|
20
20
|
# @param [String] db_path Path to Sqlite database
|
21
21
|
# @param [String] sql query
|
@@ -55,7 +55,7 @@ module FuseFS
|
|
55
55
|
end
|
56
56
|
|
57
57
|
# FuseFS callback when filesystem is unmounted
|
58
|
-
#
|
58
|
+
#
|
59
59
|
# Stops the database watching threads
|
60
60
|
# @api FuseFS
|
61
61
|
def unmounted()
|
@@ -88,6 +88,11 @@ module FuseFS
|
|
88
88
|
cleanup() { |file_node| file_node.options[:sqlite_scan_id] != @scan_id }
|
89
89
|
end
|
90
90
|
|
91
|
+
# Rescan on HUP signal
|
92
|
+
def sighup
|
93
|
+
rescan()
|
94
|
+
end
|
95
|
+
|
91
96
|
private
|
92
97
|
|
93
98
|
def scan_loop()
|
data/lib/rfusefs.rb
CHANGED
@@ -45,28 +45,14 @@ module FuseFS
|
|
45
45
|
# end
|
46
46
|
#
|
47
47
|
def FuseFS.main(argv=ARGV,options=[],option_usage="",device=nil,exec=File.basename($0))
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
fs = yield options
|
53
|
-
rescue StandardError => ex
|
54
|
-
puts ex.message
|
55
|
-
puts ex.backtrace.join("\n")
|
56
|
-
end
|
57
|
-
|
58
|
-
puts RFuse.usage(device,exec) if options[:help] || !fs
|
59
|
-
puts "Options:\n" if options[:help]
|
60
|
-
|
61
|
-
FuseFS.start(fs,*argv) if fs || options[:help]
|
62
|
-
|
63
|
-
puts option_usage if options[:help]
|
64
|
-
else
|
65
|
-
puts "rfusefs: failed, no mountpoint provided",RFuse.usage(device,exec)
|
48
|
+
RFuse.main(argv,options,option_usage,device,exec) do |options,argv|
|
49
|
+
root = yield options
|
50
|
+
FuseFS.set_root(root)
|
51
|
+
FuseFS.mount_under(*argv)
|
66
52
|
end
|
67
53
|
end
|
68
54
|
|
69
|
-
# Start the FuseFS root at mountpoint with opts.
|
55
|
+
# Start the FuseFS root at mountpoint with opts.
|
70
56
|
#
|
71
57
|
# If not previously set, Signal traps for "TERM" and "INT" are added
|
72
58
|
# to exit the filesystem
|
@@ -77,7 +63,6 @@ module FuseFS
|
|
77
63
|
# @note RFuseFS extension
|
78
64
|
# @return [void]
|
79
65
|
def FuseFS.start(root,mountpoint,*opts)
|
80
|
-
set_default_traps("TERM","INT") { FuseFS.exit }
|
81
66
|
FuseFS.set_root(root)
|
82
67
|
begin
|
83
68
|
FuseFS.mount_under(mountpoint,*opts)
|
@@ -88,7 +73,7 @@ module FuseFS
|
|
88
73
|
end
|
89
74
|
|
90
75
|
# Forks {FuseFS.start} so you can access your filesystem with ruby File
|
91
|
-
# operations (eg for testing).
|
76
|
+
# operations (eg for testing).
|
92
77
|
# @note This is an *RFuseFS* extension
|
93
78
|
# @return [void]
|
94
79
|
def FuseFS.mount(root,mountpoint,*opts)
|
@@ -126,43 +111,40 @@ module FuseFS
|
|
126
111
|
end
|
127
112
|
end
|
128
113
|
|
129
|
-
# Set the root virtual directory
|
114
|
+
# Set the root virtual directory
|
130
115
|
# @param root [Object] an object implementing a subset of {FuseFS::API}
|
131
116
|
# @return [void]
|
132
117
|
def FuseFS.set_root(root)
|
133
|
-
@fs=
|
118
|
+
@fs=Fuse::Root.new(root)
|
134
119
|
end
|
135
120
|
|
136
121
|
# This will cause FuseFS to virtually mount itself under the given path. {set_root} must have
|
137
122
|
# been called previously.
|
138
123
|
# @param [String] mountpoint an existing directory where the filesystem will be virtually mounted
|
139
124
|
# @param [Array<String>] args
|
125
|
+
# @return [Fuse] the mounted fuse filesystem
|
140
126
|
# These are as expected by the "mount" command. Note in particular that the first argument
|
141
127
|
# is expected to be the mount point. For more information, see http://fuse.sourceforge.net
|
142
128
|
# and the manual pages for "mount.fuse"
|
143
|
-
def FuseFS.mount_under(mountpoint, *args)
|
144
|
-
@fuse =
|
129
|
+
def FuseFS.mount_under(mountpoint, *args)
|
130
|
+
@fuse = Fuse.new(@fs,mountpoint,*args)
|
145
131
|
end
|
146
132
|
|
147
|
-
# This is the main loop waiting on then executing filesystem operations from the
|
148
|
-
# kernel.
|
149
|
-
#
|
133
|
+
# This is the main loop waiting on, then executing, filesystem operations from the
|
134
|
+
# kernel.
|
135
|
+
#
|
150
136
|
# Note: Running in a separate thread is generally not useful. In particular
|
151
|
-
# you cannot access your filesystem
|
137
|
+
# you cannot use Ruby File operations to access your filesystem from
|
138
|
+
# within the ruby process that calls run.
|
152
139
|
# @note RFuseFS extension
|
153
140
|
def FuseFS.run
|
154
|
-
@
|
155
|
-
@fuse.loop if @fuse.mounted?
|
156
|
-
ensure
|
157
|
-
@fs.unmounted()
|
141
|
+
@fuse.run()
|
158
142
|
end
|
159
143
|
|
160
|
-
# Exit the run loop and teardown FUSE
|
161
|
-
# Most useful from Signal.trap() or Kernel.at_exit()
|
144
|
+
# Exit the run loop and teardown FUSE
|
145
|
+
# Most useful from Signal.trap() or Kernel.at_exit()
|
162
146
|
def FuseFS.exit
|
163
|
-
if @fuse
|
164
|
-
@fuse.exit
|
165
|
-
end
|
147
|
+
@fuse.exit if @fuse
|
166
148
|
end
|
167
149
|
|
168
150
|
# @return [Fixnum] the calling process uid
|
@@ -188,15 +170,5 @@ module FuseFS
|
|
188
170
|
#do nothing
|
189
171
|
end
|
190
172
|
|
191
|
-
private
|
192
|
-
|
193
|
-
def self.set_default_traps(*signals,&block)
|
194
|
-
signals.each do |sig|
|
195
|
-
old_proc = Signal.trap(sig,block)
|
196
|
-
unless "DEFAULT".eql?(old_proc)
|
197
|
-
Signal.trap(sig,old_proc)
|
198
|
-
end
|
199
|
-
end
|
200
|
-
end
|
201
173
|
end
|
202
174
|
|
data/lib/rfusefs/version.rb
CHANGED
data/rfusefs.gemspec
CHANGED
@@ -20,9 +20,9 @@ Gem::Specification.new do |s|
|
|
20
20
|
s.has_rdoc = 'yard'
|
21
21
|
s_extra_rdoc_files = 'History.rdoc'
|
22
22
|
|
23
|
-
s.add_dependency("rfuse", "
|
23
|
+
s.add_dependency("rfuse", "~> 1.1")
|
24
24
|
s.add_development_dependency("rake")
|
25
|
-
s.add_development_dependency("rspec")
|
25
|
+
s.add_development_dependency("rspec","~> 3")
|
26
26
|
s.add_development_dependency("yard")
|
27
27
|
s.add_development_dependency("redcarpet")
|
28
28
|
s.add_development_dependency("sqlite3")
|
data/samples/demo.rb
CHANGED
data/spec/metadir_spec.rb
CHANGED
@@ -4,361 +4,366 @@ require 'sys/filesystem'
|
|
4
4
|
require 'ffi-xattr'
|
5
5
|
|
6
6
|
describe FuseFS::MetaDir do
|
7
|
-
|
8
|
-
context
|
9
|
-
|
7
|
+
|
8
|
+
context 'in ruby' do
|
9
|
+
|
10
10
|
before(:each) do
|
11
11
|
@metadir = FuseFS::MetaDir.new()
|
12
|
-
@metadir.mkdir(
|
13
|
-
@metadir.mkdir(
|
14
|
-
@metadir.mkdir(
|
15
|
-
@metadir.write_to(
|
12
|
+
@metadir.mkdir('/test')
|
13
|
+
@metadir.mkdir('/test/hello')
|
14
|
+
@metadir.mkdir('/test/hello/emptydir')
|
15
|
+
@metadir.write_to('/test/hello/hello.txt', "Hello World!\n")
|
16
16
|
end
|
17
|
-
|
18
|
-
context
|
19
|
-
it
|
20
|
-
@metadir.contents(
|
21
|
-
@metadir.contents(
|
22
|
-
@metadir.contents(
|
23
|
-
end
|
24
|
-
|
25
|
-
it
|
26
|
-
@metadir.directory?(
|
27
|
-
@metadir.directory?(
|
28
|
-
@metadir.directory?(
|
29
|
-
@metadir.directory?(
|
30
|
-
@metadir.directory?(
|
31
|
-
end
|
32
|
-
|
33
|
-
it
|
34
|
-
@metadir.file?(
|
35
|
-
@metadir.file?(
|
36
|
-
@metadir.file?(
|
37
|
-
@metadir.file?(
|
38
|
-
end
|
39
|
-
|
40
|
-
it
|
41
|
-
@metadir.size(
|
42
|
-
end
|
43
|
-
|
44
|
-
it
|
45
|
-
|
17
|
+
|
18
|
+
context 'general directory methods' do
|
19
|
+
it 'should list directory contents' do
|
20
|
+
expect(@metadir.contents('/')).to match_array(['test'])
|
21
|
+
expect(@metadir.contents('/test')).to match_array(['hello'])
|
22
|
+
expect(@metadir.contents('/test/hello')).to match_array(['hello.txt', 'emptydir'])
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'should indicate paths which are/are not directories' do
|
26
|
+
expect(@metadir.directory?('/test')).to be_truthy
|
27
|
+
expect(@metadir.directory?('/test/hello')).to be_truthy
|
28
|
+
expect(@metadir.directory?('/test/hello/hello.txt')).to be_falsey
|
29
|
+
expect(@metadir.directory?('/nodir')).to be_falsey
|
30
|
+
expect(@metadir.directory?('/test/nodir')).to be_falsey
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'should indicate paths which are/are not files' do
|
34
|
+
expect(@metadir.file?('/test')).to be_falsey
|
35
|
+
expect(@metadir.file?('/test/nodir')).to be_falsey
|
36
|
+
expect(@metadir.file?('/test/hello')).to be_falsey
|
37
|
+
expect(@metadir.file?('/test/hello/hello.txt')).to be_truthy
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'should indicate the size of a file' do
|
41
|
+
expect(@metadir.size('/test/hello/hello.txt')).to be "Hello World!\n".length
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'should report filesystem statistics' do
|
45
|
+
expect(@metadir.statistics('/')).to eq([13, 5, nil, nil])
|
46
46
|
end
|
47
47
|
end
|
48
|
-
|
49
|
-
context
|
50
|
-
|
48
|
+
|
49
|
+
context 'with write access' do
|
50
|
+
|
51
51
|
around(:each) do |example|
|
52
|
-
FuseFS::
|
52
|
+
FuseFS::Fuse::Root.context(fuse_context(), &example)
|
53
53
|
end
|
54
|
-
|
54
|
+
|
55
55
|
before(:each) do
|
56
|
-
FuseFS::reader_uid.
|
57
|
-
FuseFS::reader_gid.
|
58
|
-
end
|
59
|
-
|
60
|
-
|
61
|
-
it
|
62
|
-
@metadir.can_mkdir?(
|
63
|
-
end
|
64
|
-
|
65
|
-
it
|
66
|
-
@metadir.can_write?(
|
67
|
-
@metadir.can_write?(
|
68
|
-
end
|
69
|
-
|
70
|
-
it
|
71
|
-
@metadir.read_file(
|
72
|
-
end
|
73
|
-
|
74
|
-
it
|
75
|
-
@metadir.write_to(
|
76
|
-
@metadir.read_file(
|
77
|
-
end
|
78
|
-
|
79
|
-
it
|
80
|
-
@metadir.can_rmdir?(
|
81
|
-
end
|
82
|
-
|
83
|
-
it
|
84
|
-
@metadir.rmdir(
|
85
|
-
@metadir.contents(
|
86
|
-
end
|
87
|
-
|
88
|
-
it
|
89
|
-
@metadir.can_delete?(
|
90
|
-
@metadir.delete(
|
91
|
-
@metadir.contents(
|
92
|
-
end
|
93
|
-
|
94
|
-
it
|
95
|
-
before = @metadir.contents(
|
96
|
-
@metadir.rename(
|
97
|
-
@metadir.directory?(
|
98
|
-
@metadir.contents(
|
99
|
-
@metadir.read_file(
|
100
|
-
end
|
101
|
-
|
102
|
-
it
|
103
|
-
@metadir.mkdir(
|
104
|
-
@metadir.mkdir(
|
105
|
-
before = @metadir.contents(
|
106
|
-
@metadir.rename(
|
107
|
-
@metadir.contents(
|
108
|
-
@metadir.read_file(
|
109
|
-
end
|
110
|
-
|
111
|
-
it
|
56
|
+
expect(FuseFS::reader_uid).to eq(Process.uid)
|
57
|
+
expect(FuseFS::reader_gid).to eq(Process.gid)
|
58
|
+
end
|
59
|
+
|
60
|
+
|
61
|
+
it 'should allow directory creation' do
|
62
|
+
expect(@metadir.can_mkdir?('/test/otherdir')).to be_truthy
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'should allow file creation and update' do
|
66
|
+
expect(@metadir.can_write?('/test/hello/newfile')).to be_truthy
|
67
|
+
expect(@metadir.can_write?('/test/hello/hello.txt')).to be_truthy
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'should read files' do
|
71
|
+
expect(@metadir.read_file('/test/hello/hello.txt')).to eq("Hello World!\n")
|
72
|
+
end
|
73
|
+
|
74
|
+
it 'should update existing files' do
|
75
|
+
@metadir.write_to('/test/hello/hello.txt', 'new contents')
|
76
|
+
expect(@metadir.read_file('/test/hello/hello.txt')).to eq('new contents')
|
77
|
+
end
|
78
|
+
|
79
|
+
it 'should not allow deletion of non empty directories' do
|
80
|
+
expect(@metadir.can_rmdir?('/test/hello')).to be_falsey
|
81
|
+
end
|
82
|
+
|
83
|
+
it 'should delete directories' do
|
84
|
+
@metadir.rmdir('/test/hello/emptydir')
|
85
|
+
expect(@metadir.contents('/test/hello')).to match_array(['hello.txt'])
|
86
|
+
end
|
87
|
+
|
88
|
+
it 'should allow and delete files' do
|
89
|
+
expect(@metadir.can_delete?('/test/hello/hello.txt')).to be_truthy
|
90
|
+
@metadir.delete('/test/hello/hello.txt')
|
91
|
+
expect(@metadir.contents('/test/hello')).to match_array(['emptydir'])
|
92
|
+
end
|
93
|
+
|
94
|
+
it 'should move directories at same level' do
|
95
|
+
before = @metadir.contents('/test/hello')
|
96
|
+
expect(@metadir.rename('/test/hello', '/test/moved')).to be_truthy
|
97
|
+
expect(@metadir.directory?('/test/moved')).to be_truthy
|
98
|
+
expect(@metadir.contents('/test/moved')).to match_array(before)
|
99
|
+
expect(@metadir.read_file('/test/moved/hello.txt')).to eq("Hello World!\n")
|
100
|
+
end
|
101
|
+
|
102
|
+
it 'should move directories between different paths' do
|
103
|
+
@metadir.mkdir('/test/other')
|
104
|
+
@metadir.mkdir('/test/other/more')
|
105
|
+
before = @metadir.contents('/test/hello')
|
106
|
+
expect(@metadir.rename('/test/hello', '/test/other/more/hello')).to be_truthy
|
107
|
+
expect(@metadir.contents('/test/other/more/hello')).to match_array(before)
|
108
|
+
expect(@metadir.read_file('/test/other/more/hello/hello.txt')).to eq("Hello World!\n")
|
109
|
+
end
|
110
|
+
|
111
|
+
it 'should maintain filesystem statistics' do
|
112
112
|
# remove a directory
|
113
|
-
@metadir.rmdir(
|
113
|
+
@metadir.rmdir('/test/hello/emptydir')
|
114
114
|
|
115
115
|
# replace text for (the only) existing file
|
116
|
-
@metadir.write_to(
|
117
|
-
|
118
|
-
@metadir.statistics(
|
116
|
+
@metadir.write_to('/test/hello/hello.txt', 'new text')
|
117
|
+
|
118
|
+
expect(@metadir.statistics('/')).to eq([8, 4, nil, nil])
|
119
119
|
end
|
120
120
|
end
|
121
|
-
|
122
|
-
context
|
121
|
+
|
122
|
+
context 'with readonly access' do
|
123
123
|
around(:each) do |example|
|
124
124
|
#Simulate a different userid..
|
125
|
-
FuseFS::
|
125
|
+
FuseFS::Fuse::Root.context(fuse_context(-1, -1), &example)
|
126
126
|
end
|
127
|
-
|
127
|
+
|
128
128
|
before(:each) do
|
129
|
-
FuseFS::reader_uid.
|
130
|
-
FuseFS::reader_gid.
|
131
|
-
end
|
132
|
-
|
133
|
-
it
|
134
|
-
@metadir.can_mkdir?(
|
135
|
-
@metadir.can_mkdir?(
|
136
|
-
end
|
137
|
-
|
138
|
-
it
|
139
|
-
@metadir.can_write?(
|
140
|
-
@metadir.can_write?(
|
141
|
-
end
|
142
|
-
|
143
|
-
it
|
144
|
-
@metadir.can_delete?(
|
145
|
-
end
|
146
|
-
|
147
|
-
it
|
148
|
-
@metadir.can_rmdir?(
|
149
|
-
end
|
150
|
-
|
151
|
-
it
|
152
|
-
@metadir.rename(
|
129
|
+
expect(FuseFS::reader_uid).not_to eq(Process.uid)
|
130
|
+
expect(FuseFS::reader_gid).not_to eq(Process.gid)
|
131
|
+
end
|
132
|
+
|
133
|
+
it 'should not allow directory creation' do
|
134
|
+
expect(@metadir.can_mkdir?('/test/anydir')).to be_falsey
|
135
|
+
expect(@metadir.can_mkdir?('/test/hello/otherdir')).to be_falsey
|
136
|
+
end
|
137
|
+
|
138
|
+
it 'should not allow file creation or write access' do
|
139
|
+
expect(@metadir.can_write?('/test/hello/hello.txt')).to be_falsey
|
140
|
+
expect(@metadir.can_write?('/test/hello/newfile')).to be_falsey
|
141
|
+
end
|
142
|
+
|
143
|
+
it 'should not allow file deletion' do
|
144
|
+
expect(@metadir.can_delete?('/test/hello/hello.txt')).to be_falsey
|
145
|
+
end
|
146
|
+
|
147
|
+
it 'should not allow directory deletion' do
|
148
|
+
expect(@metadir.can_rmdir?('/test/emptydir')).to be_falsey
|
149
|
+
end
|
150
|
+
|
151
|
+
it 'should not allow directory renames' do
|
152
|
+
expect(@metadir.rename('/test/emptydir', '/test/otherdir')).to be_falsey
|
153
153
|
#TODO and make sure it doesn't rename
|
154
154
|
end
|
155
155
|
|
156
|
-
it
|
157
|
-
@metadir.rename(
|
156
|
+
it 'should not allow file renames' do
|
157
|
+
expect(@metadir.rename('test/hello/hello.txt', 'test/hello.txt2')).to be_falsey
|
158
158
|
#TODO and make sure it doesn't rename
|
159
159
|
end
|
160
160
|
end
|
161
|
-
|
162
|
-
context
|
161
|
+
|
162
|
+
context 'with subdirectory containing another FuseFS' do
|
163
163
|
around(:each) do |example|
|
164
|
-
FuseFS::
|
164
|
+
FuseFS::Fuse::Root.context(fuse_context(), &example)
|
165
165
|
end
|
166
166
|
|
167
167
|
before(:each) do
|
168
|
-
@fusefs =
|
169
|
-
@metadir.mkdir(
|
170
|
-
@metadir.mkdir(
|
168
|
+
@fusefs = double('mock_fusefs')
|
169
|
+
@metadir.mkdir('/test')
|
170
|
+
@metadir.mkdir('/test/fusefs', @fusefs)
|
171
171
|
end
|
172
172
|
|
173
|
-
api_methods = [:directory?, :file?, :contents, :executable?, :size, :times, :read_file, :can_write?,
|
173
|
+
api_methods = [:directory?, :file?, :contents, :executable?, :size, :times, :read_file, :can_write?, :can_delete?, :delete, :can_mkdir?, :can_rmdir?, :rmdir, :touch, :raw_open, :raw_truncate, :raw_read, :raw_write, :raw_close]
|
174
174
|
api_methods.each do |method|
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
175
|
+
it "should pass on #{method}" do
|
176
|
+
arity = FuseFS::FuseDir.instance_method(method).arity().abs - 1
|
177
|
+
args = Array.new(arity) { |i| i }
|
178
|
+
expect(@fusefs).to receive(method).with('/path/to/file', *args).and_return('anything')
|
179
|
+
@metadir.send(method, '/test/fusefs/path/to/file', *args)
|
180
|
+
end
|
181
181
|
end
|
182
|
-
|
183
|
-
it
|
184
|
-
|
185
|
-
|
182
|
+
|
183
|
+
it 'should pass on :write_to' do
|
184
|
+
expect(@fusefs).to receive(:write_to).with('/path/to/file', "new contents\n")
|
185
|
+
@metadir.write_to('/test/fusefs/path/to/file', "new contents\n")
|
186
186
|
end
|
187
187
|
|
188
|
-
it
|
189
|
-
|
190
|
-
|
191
|
-
|
188
|
+
it 'should pass on :mkdir' do
|
189
|
+
expect(@fusefs).to receive(:mkdir).with('/path/to/file', nil).once().and_raise(ArgumentError)
|
190
|
+
expect(@fusefs).to receive(:mkdir).with('/path/to/file').once().and_return('true')
|
191
|
+
@metadir.mkdir('/test/fusefs/path/to/file')
|
192
192
|
end
|
193
193
|
|
194
|
-
it
|
195
|
-
@fusefs.
|
196
|
-
@metadir.rename(
|
194
|
+
it 'should rename within same directory' do
|
195
|
+
expect(@fusefs).to receive(:rename).with('/oldfile', '/newfile')
|
196
|
+
@metadir.rename('/test/fusefs/oldfile', '/test/fusefs/newfile')
|
197
197
|
end
|
198
198
|
|
199
|
-
it
|
200
|
-
@fusefs.
|
201
|
-
@metadir.rename(
|
199
|
+
it 'should pass rename down common directories' do
|
200
|
+
expect(@fusefs).to receive(:rename).with('/path/to/file', '/new/path/to/file')
|
201
|
+
@metadir.rename('/test/fusefs/path/to/file', '/test/fusefs/new/path/to/file')
|
202
202
|
end
|
203
203
|
|
204
|
-
it
|
205
|
-
@fusefs.
|
206
|
-
|
207
|
-
|
208
|
-
|
204
|
+
it 'should rename across directories if from_path is a FuseFS object that accepts extended rename' do
|
205
|
+
expect(@fusefs).to receive(:rename).with('/path/to/file', '/nonfusepath',
|
206
|
+
an_instance_of(FuseFS::MetaDir)) do |myPath, extPath, extFS|
|
207
|
+
extFS.write_to(extPath, 'look Mum, no hands!')
|
208
|
+
end
|
209
209
|
|
210
|
-
@metadir.rename(
|
211
|
-
@metadir.read_file(
|
210
|
+
expect(@metadir.rename('/test/fusefs/path/to/file', '/test/nonfusepath')).to be_truthy
|
211
|
+
expect(@metadir.read_file('/test/nonfusepath')).to eq('look Mum, no hands!')
|
212
212
|
end
|
213
213
|
|
214
|
-
it
|
215
|
-
@fusefs.
|
216
|
-
|
217
|
-
|
218
|
-
@metadir.rename(
|
214
|
+
it 'should quietly return false if from_path is a FuseFS object that does not accept extended rename' do
|
215
|
+
expect(@fusefs).to receive(:rename).
|
216
|
+
with('/path/to/file', '/nonfusepath', an_instance_of(FuseFS::MetaDir)).
|
217
|
+
and_raise(ArgumentError)
|
218
|
+
expect(@metadir.rename('/test/fusefs/path/to/file', '/test/nonfusepath')).to be_falsey
|
219
219
|
|
220
220
|
end
|
221
221
|
|
222
|
-
it
|
223
|
-
@fusefs.
|
224
|
-
@metadir.write_to(
|
225
|
-
@metadir.rename(
|
222
|
+
it 'should not attempt rename file unless :can_write? the destination' do
|
223
|
+
expect(@fusefs).to receive(:can_write?).with('/newpath/to/file').and_return(false)
|
224
|
+
@metadir.write_to('/test/aFile', 'some contents')
|
225
|
+
expect(@metadir.rename('/test/aFile', '/test/fusefs/newpath/to/file')).to be_falsey
|
226
226
|
end
|
227
227
|
|
228
|
-
it
|
229
|
-
@fusefs.
|
230
|
-
@metadir.mkdir(
|
231
|
-
@metadir.rename(
|
228
|
+
it 'should not attempt rename directory unless :can_mkdir? the destination' do
|
229
|
+
expect(@fusefs).to receive(:can_mkdir?).with('/newpath/to/dir').and_return(false)
|
230
|
+
@metadir.mkdir('/test/aDir', 'some contents')
|
231
|
+
expect(@metadir.rename('/test/aDir', '/test/fusefs/newpath/to/dir')).to be_falsey
|
232
232
|
end
|
233
233
|
|
234
|
-
it
|
235
|
-
@fusefs.
|
234
|
+
it 'should pass on #statistics' do
|
235
|
+
expect(@fusefs).to receive(:statistics).with('/path/to/file')
|
236
236
|
|
237
|
-
@metadir.statistics(
|
237
|
+
@metadir.statistics('test/fusefs/path/to/file')
|
238
238
|
end
|
239
239
|
|
240
|
-
it
|
241
|
-
@fusefs.
|
240
|
+
it 'should pass on #statistics for root' do
|
241
|
+
expect(@fusefs).to receive(:statistics).with('/')
|
242
242
|
|
243
|
-
@metadir.statistics(
|
243
|
+
@metadir.statistics('test/fusefs')
|
244
244
|
end
|
245
245
|
end
|
246
|
-
|
246
|
+
|
247
247
|
end
|
248
|
-
context
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
end
|
264
|
-
|
265
|
-
after(:all) do
|
266
|
-
FuseFS.unmount(mountpoint)
|
267
|
-
sleep(0.5)
|
268
|
-
FileUtils.rm_r(mountpoint)
|
269
|
-
end
|
270
|
-
|
271
|
-
it "should list directory contents" do
|
272
|
-
testdir.entries().should =~ pathnames(".","..","hello.txt")
|
248
|
+
context 'in a mounted FUSE filesystem' do
|
249
|
+
|
250
|
+
before(:all) do
|
251
|
+
metadir = FuseFS::MetaDir.new()
|
252
|
+
mountpoint = Pathname.new(Dir.mktmpdir(['rfusefs', 'metadir']))
|
253
|
+
|
254
|
+
metadir.mkdir('/test')
|
255
|
+
metadir.write_to('/test/hello.txt', "Hello World!\n")
|
256
|
+
metadir.xattr('/test/hello.txt')['user.test'] = 'an extended attribute'
|
257
|
+
metadir.xattr('/test')['user.test'] = 'a dir attribute'
|
258
|
+
FuseFS.mount(metadir, mountpoint)
|
259
|
+
#Give FUSE some time to get started
|
260
|
+
sleep(0.5)
|
261
|
+
@metadir = metadir
|
262
|
+
@mountpoint = mountpoint
|
273
263
|
end
|
274
264
|
|
275
|
-
|
276
|
-
|
277
|
-
|
265
|
+
after(:all) do
|
266
|
+
FuseFS.unmount(@mountpoint)
|
267
|
+
sleep(0.5)
|
268
|
+
FileUtils.rm_r(@mountpoint)
|
278
269
|
end
|
279
270
|
|
280
|
-
|
281
|
-
|
282
|
-
|
271
|
+
let(:testdir) { mountpoint + 'test' }
|
272
|
+
let(:testfile) { testdir + 'hello.txt' }
|
273
|
+
let(:metadir) { @metadir}
|
274
|
+
let(:mountpoint) { @mountpoint }
|
275
|
+
|
276
|
+
it 'should list directory contents' do
|
277
|
+
expect(testdir.entries()).to match_array(pathnames('.', '..', 'hello.txt'))
|
278
|
+
end
|
283
279
|
|
284
|
-
|
285
|
-
|
286
|
-
|
280
|
+
it 'should read files' do
|
281
|
+
expect(testfile.file?).to be_truthy
|
282
|
+
expect(testfile.read()).to eq("Hello World!\n")
|
287
283
|
end
|
288
284
|
|
289
|
-
it
|
290
|
-
|
285
|
+
it 'should read and write extended attributes from files' do
|
286
|
+
x = Xattr.new(testfile.to_s)
|
287
|
+
expect(x['user.test']).to eq('an extended attribute')
|
291
288
|
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
Xattr.new(testdir.to_s)["user.new"].should == "new dir"
|
289
|
+
x['user.new'] = 'new'
|
290
|
+
|
291
|
+
expect(Xattr.new(testfile.to_s)['user.new']).to eq('new')
|
296
292
|
end
|
297
293
|
|
294
|
+
it 'should write extended attributes for directories' do
|
295
|
+
x = Xattr.new(testdir.to_s)
|
296
|
+
|
297
|
+
expect(x['user.test']).to eq('a dir attribute')
|
298
|
+
x['user.new'] = 'new dir'
|
298
299
|
|
299
|
-
|
300
|
-
newdir = testdir + "newdir"
|
301
|
-
newdir.mkdir()
|
302
|
-
newdir.directory?.should be_true
|
303
|
-
testdir.entries().should =~ pathnames(".","..","hello.txt","newdir")
|
300
|
+
expect(Xattr.new(testdir.to_s)['user.new']).to eq('new dir')
|
304
301
|
end
|
305
302
|
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
303
|
+
|
304
|
+
it 'should create directories' do
|
305
|
+
newdir = testdir + 'newdir'
|
306
|
+
newdir.mkdir()
|
307
|
+
expect(newdir.directory?).to be_truthy
|
308
|
+
expect(testdir.entries()).to match_array(pathnames('.', '..', 'hello.txt', 'newdir'))
|
312
309
|
end
|
313
310
|
|
314
|
-
it
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
311
|
+
it 'should create files' do
|
312
|
+
newfile = testdir + 'newfile'
|
313
|
+
newfile.open('w') do |file|
|
314
|
+
file << "A new file\n"
|
315
|
+
end
|
316
|
+
expect(newfile.read).to eq("A new file\n")
|
317
|
+
end
|
318
|
+
|
319
|
+
it 'should move directories' do
|
320
|
+
fromdir = testdir + 'fromdir'
|
321
|
+
fromdir.mkdir()
|
322
|
+
subfile = fromdir + 'afile'
|
323
|
+
subfile.open('w') do |file|
|
324
|
+
file << "testfile\n"
|
325
|
+
end
|
326
|
+
|
327
|
+
movedir = (mountpoint + 'movedir')
|
328
|
+
expect(movedir.directory?).to be_falsey
|
329
|
+
fromdir.rename(movedir)
|
330
|
+
expect(movedir.directory?).to be_truthy
|
321
331
|
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
movedir.directory?.should be_true
|
326
|
-
|
327
|
-
subfile = movedir + "afile"
|
328
|
-
subfile.file?.should be_true
|
329
|
-
subfile.read.should == "testfile\n"
|
332
|
+
subfile = movedir + 'afile'
|
333
|
+
expect(subfile.file?).to be_truthy
|
334
|
+
expect(subfile.read).to eq("testfile\n")
|
330
335
|
end
|
331
336
|
|
332
|
-
it
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
337
|
+
it 'should move files' do
|
338
|
+
movefile = (mountpoint + 'moved.txt')
|
339
|
+
expect(movefile.file?).to be_falsey
|
340
|
+
expect(testfile).to be_truthy
|
341
|
+
testfile.rename(movefile)
|
342
|
+
expect(movefile.read).to eq("Hello World!\n")
|
338
343
|
end
|
339
344
|
|
340
345
|
|
341
|
-
it
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
statfs = Sys::Filesystem.stat(mountpoint.to_s)
|
346
|
+
it 'should report filesystem statistics' do
|
347
|
+
bigfile = testdir + 'bigfile'
|
348
|
+
bigfile.open('w') do |file|
|
349
|
+
file << ('x' * 2048)
|
350
|
+
end
|
348
351
|
|
349
|
-
|
350
|
-
statfs.block_size.should == 1024
|
351
|
-
statfs.fragment_size.should == 1024
|
352
|
+
statfs = Sys::Filesystem.stat(mountpoint.to_s)
|
352
353
|
|
353
|
-
|
354
|
-
|
355
|
-
|
354
|
+
# These are fixed
|
355
|
+
expect(statfs.block_size).to eq(1024)
|
356
|
+
expect(statfs.fragment_size).to eq(1024)
|
356
357
|
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
358
|
+
# These are dependant on the tests above creating files/directories
|
359
|
+
expect(statfs.files).to eq(8)
|
360
|
+
statfs.files_available == 8
|
361
|
+
|
362
|
+
# assume test are less than 1 block, so dependant on bigfile above
|
363
|
+
expect(statfs.blocks).to eq(2)
|
364
|
+
expect(statfs.blocks_available).to eq(0)
|
365
|
+
expect(statfs.blocks_free).to eq(0)
|
361
366
|
end
|
362
367
|
end
|
363
|
-
|
368
|
+
|
364
369
|
end
|