rfusefs 1.0.2 → 1.1.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.
@@ -1,21 +0,0 @@
1
- require 'spec_helper'
2
- require 'rfusefs'
3
- require 'tmpdir'
4
- require 'pathname'
5
-
6
- describe "a mounted FuseFS" do
7
- let(:mountpoint) { Pathname.new(Dir.mktmpdir("rfusefs_mount_unmount")) }
8
-
9
- after(:each) { FileUtils.rmdir mountpoint }
10
-
11
- it "should get mounted and unmounted callbacks" do
12
- mock_fs = FuseFS::FuseDir.new()
13
- mock_fs.should_receive(:mounted)
14
- mock_fs.should_receive(:unmounted)
15
-
16
- t = Thread.new { sleep 0.5 ; puts "exiting" ; FuseFS.exit }
17
- FuseFS.start(mock_fs,mountpoint)
18
- t.join
19
- end
20
- end
21
-
@@ -1,417 +0,0 @@
1
- #Pathmapper is hard to test because it is difficult to mock Dir/Pathname/File etc...
2
- require "spec_helper"
3
- require "fusefs/pathmapper"
4
- require 'tmpdir'
5
- require 'pathname'
6
- require 'sys/filesystem'
7
- require 'ffi-xattr/extensions'
8
-
9
- class PMFixture
10
- attr_reader :tmpdir
11
-
12
- def initialize()
13
- #Note - these names define the filesystem stats so if you change them those tests will break
14
- end
15
-
16
- def tmpdir
17
- @tmpdir ||= Pathname.new(Dir.mktmpdir("rfusefs_pathmapper"))
18
- end
19
-
20
- def real_path(file)
21
- tmpdir + file
22
- end
23
-
24
- def pathmap(file,mapped_path, content = mapped_path, options = {})
25
- real_file = tmpdir + file
26
- real_file.open("w") do |f|
27
- f << content
28
- end
29
- fs.map_file(real_file,mapped_path,options)
30
- end
31
-
32
- def fs
33
- @fs ||= FuseFS::PathMapperFS.new()
34
- end
35
-
36
- def mount()
37
- return @mountpoint if @mountpoint
38
- @mountpoint = Pathname.new(Dir.mktmpdir("rfusefs_pathmapper_mnt"))
39
- FuseFS.mount(fs,@mountpoint)
40
- sleep(0.5)
41
- @mountpoint
42
- end
43
-
44
- def cleanup
45
- if @mountpoint
46
- FuseFS.unmount(@mountpoint)
47
- sleep(0.5)
48
- FileUtils.rmdir(@mountpoint)
49
- end
50
- FileUtils.rm_r(@tmpdir)
51
- end
52
- end
53
-
54
-
55
- describe FuseFS::PathMapperFS do
56
-
57
- let(:fixture) { PMFixture.new() }
58
- let(:pathmap_fs) { fixture.fs }
59
- let(:tmpdir) { fixture.tmpdir }
60
- let(:mountpoint) { fixture.mount }
61
-
62
- before(:each) do
63
- fixture.pathmap("hello.txt","/textfiles/hello")
64
- fixture.pathmap("mysong.mp3","/artist/album/mysong.mp3")
65
- fixture.pathmap("apicture.jpeg","/pictures/201103/apicture.jpg")
66
- end
67
-
68
- after(:each) do
69
- fixture.cleanup
70
- end
71
-
72
- context "fusefs api" do
73
-
74
- it "maps files and directories" do
75
- pathmap_fs.directory?("/").should be_true
76
- pathmap_fs.directory?("/textfiles").should be_true
77
- pathmap_fs.directory?("/pictures/201103").should be_true
78
- pathmap_fs.file?("/textfiles/hello").should be_true
79
- pathmap_fs.directory?("/textfiles/hello").should be_false
80
- pathmap_fs.file?("/artist/album/mysong.mp3").should be_true
81
- pathmap_fs.directory?("/artist/album/mysong.mp3").should be_false
82
- pathmap_fs.file?("/some/unknown/path").should be_false
83
- pathmap_fs.directory?("/some/unknown/path").should be_false
84
- end
85
-
86
- it "lists the mapped contents of directories" do
87
- pathmap_fs.contents("/").should =~ [ "textfiles","artist","pictures" ]
88
- pathmap_fs.contents("/artist").should =~ [ "album" ]
89
- pathmap_fs.contents("/textfiles").should =~ [ "hello" ]
90
- end
91
-
92
- it "reports the size of a file" do
93
- pathmap_fs.size("/textfiles/hello").should == 16
94
- end
95
-
96
- it "reads the contents of a file" do
97
- pathmap_fs.read_file("/textfiles/hello").should == "/textfiles/hello"
98
- end
99
-
100
- it "does not allow writes" do
101
- pathmap_fs.can_write?("/textfiles/hello").should be_false
102
- end
103
-
104
- it "reports the atime,mtime and ctime of the mapped file" do
105
- atime,mtime,ctime = pathmap_fs.times("/pictures/201103/apicture.jpg")
106
- picture = tmpdir + "apicture.jpeg"
107
- atime.should == picture.atime()
108
- mtime.should == picture.mtime()
109
- ctime.should == picture.ctime()
110
- end
111
-
112
- context "with extended attributes" do
113
- let (:hello_realpath) {fixture.real_path("hello.txt")}
114
- let (:hello_xattr) { Xattr.new(hello_realpath) }
115
- let (:pm_file_xattr) { pathmap_fs.xattr("/textfiles/hello") }
116
- let (:pm_dir_xattr) { pathmap_fs.xattr("/textfiles") }
117
-
118
- before(:each) do
119
- # attribute set on real file
120
- hello_xattr["user.file_attr"] = "fileValue"
121
-
122
- # additional attribute on file
123
- pathmap_fs.node("/textfiles/hello")[:xattr] =
124
- { "user.add_attr" => "addValue" }
125
-
126
- # additional attribute on directory
127
- pathmap_fs.node("/textfiles")[:xattr] =
128
- { "user.dir_attr" => "dirValue" }
129
-
130
- end
131
-
132
- it "should list extended attributes" do
133
- pm_file_xattr.keys.should include("user.file_attr")
134
- pm_file_xattr.keys.should include("user.add_attr")
135
- pm_file_xattr.keys.size.should == 2
136
- end
137
-
138
- it "should read extended attributes from underlying file" do
139
- pm_file_xattr["user.file_attr"].should == "fileValue"
140
- end
141
-
142
- it "should read additional attributes" do
143
- # make sure additional attributes are not on the real file
144
- hello_xattr.list.should_not include("user.add_attr")
145
-
146
- pm_file_xattr["user.add_attr"].should == "addValue"
147
- end
148
-
149
- it "should write extended attributes to the underlying file" do
150
- pm_file_xattr["user.file_attr"] = "written"
151
- hello_xattr["user.file_attr"].should == "written"
152
- end
153
-
154
- it "should remove extended attributes from the underlying file" do
155
- pm_file_xattr.delete("user.file_attr")
156
- hello_xattr.list.should_not include("user.file_attr")
157
- end
158
-
159
- it "raise EACCES when writing to additional attributes" do
160
- lambda {pm_file_xattr["user.add_attr"] = "newValue"}.should raise_error(Errno::EACCES)
161
- end
162
-
163
- it "raise EACCES when removing additional attributes" do
164
- lambda {pm_file_xattr.delete("user.add_attr")}.should raise_error(Errno::EACCES)
165
- end
166
-
167
- it "should list additional attributes from virtual directories" do
168
- pm_dir_xattr.keys.should include("user.dir_attr")
169
- pm_dir_xattr.keys.size.should == 1
170
- end
171
-
172
- it "should read additional attributes from virtual directories" do
173
- pm_dir_xattr["user.dir_attr"].should == "dirValue"
174
-
175
- end
176
-
177
- it "should raise EACCES when writing additional attributes on virtual directories" do
178
- lambda {pm_dir_xattr["user.dir_attr"] = "newValue"}.should raise_error(Errno::EACCES)
179
- end
180
-
181
- it "should raise EACCES when deleting additional attributes on virtual directories" do
182
- lambda {pm_dir_xattr.delete("user.dir_attr")}.should raise_error(Errno::EACCES)
183
- end
184
-
185
- it "should accept xattr as option to #map_file" do
186
- fixture.pathmap("mapped_xattr.txt","/textfiles/mapped_xattr","content",
187
- :xattr => { "user.xattr" => "map_file" })
188
- pathmap_fs.xattr("/textfiles/mapped_xattr")["user.xattr"].should == "map_file"
189
- end
190
- end
191
-
192
- context "filesystem statistics" do
193
-
194
- it "reports accumulated stats about mapped files" do
195
- used_space, used_nodes, max_space, max_nodes = pathmap_fs.statistics("/pictures/201103/apicture.jpg")
196
- used_space.should == 69
197
- used_nodes.should == 9
198
- max_space.should be_nil
199
- max_nodes.should be_nil
200
- end
201
- end
202
-
203
- context "writing to a pathmapped FS" do
204
- before(:each) do
205
- pathmap_fs.allow_write=true
206
- pathmap_fs.write_to("textfiles/hello","updated content")
207
- end
208
-
209
- it "updates the contents of the real file" do
210
- hello_path = tmpdir + "hello.txt"
211
- hello_path.read.should == "updated content"
212
- end
213
-
214
- it "updates the contents of the mapped file" do
215
- pathmap_fs.read_file("textfiles/hello").should == "updated content"
216
- end
217
-
218
- it "changes the reported file size" do
219
- pathmap_fs.size("textfiles/hello").should == 15
220
- end
221
-
222
- it "changes the filesystem statistics" do
223
- used_space, used_nodes, max_space, max_nodes = pathmap_fs.statistics("/pictures/201103/apicture.jpg")
224
- used_space.should == 68
225
- end
226
- end
227
-
228
- end
229
-
230
- context "a real Fuse mounted filesystem" do
231
- before(:each) do
232
- pathmap_fs.allow_write=true
233
- end
234
-
235
- it "maps files and directories" do
236
- (mountpoint + "textfiles").directory?.should be_true
237
- (mountpoint + "textfiles/hello").file?.should be_true
238
- end
239
-
240
- it "lists the mapped contents of directories" do
241
- (mountpoint + "textfiles").entries.should =~ pathnames(".","..","hello")
242
- end
243
-
244
- it "represents the stat information of the underlying files" do
245
- hellopath=(mountpoint + "textfiles/hello")
246
- realpath=(tmpdir + "hello.txt")
247
- mappedstat = hellopath.stat
248
- realstat = realpath.stat
249
- mappedstat.size.should == realstat.size
250
- mappedstat.atime.should == realstat.atime
251
- mappedstat.mtime.should == realstat.mtime
252
- mappedstat.ctime.should == realstat.ctime
253
- end
254
-
255
- it "reads the files" do
256
- hellopath= mountpoint + "textfiles/hello"
257
- hellopath.read.should == "/textfiles/hello"
258
- end
259
-
260
- it "writes the files" do
261
- hellopath= mountpoint + "textfiles/hello"
262
- real_path = tmpdir + "hello.txt"
263
- hellopath.open("w") do |f|
264
- f.print "updated content"
265
- end
266
- hellopath.read.should == "updated content"
267
- real_path.read.should == "updated content"
268
- end
269
-
270
- context "extended attributes" do
271
- let (:hello_realpath) {fixture.real_path("hello.txt")}
272
- let (:hello_xattr) { hello_realpath.xattr }
273
- let (:file_xattr) { (mountpoint + "textfiles/hello").xattr }
274
- let (:dir_xattr) { (mountpoint + "textfiles").xattr }
275
-
276
- before(:each) do
277
- # attribute set on real file
278
- hello_xattr["user.file_attr"] = "fileValue"
279
-
280
- # additional attribute on file
281
- pathmap_fs.node("/textfiles/hello")[:xattr] =
282
- { "user.add_attr" => "addValue" }
283
-
284
- # additional attribute on directory
285
- pathmap_fs.node("/textfiles")[:xattr] =
286
- { "user.dir_attr" => "dirValue" }
287
-
288
- end
289
-
290
- it "should list extended attributes" do
291
- file_xattr.list.should include("user.file_attr")
292
- file_xattr.list.should include("user.add_attr")
293
- file_xattr.list.size.should == 2
294
- end
295
-
296
- it "should read extended attributes from underlying file" do
297
- file_xattr["user.file_attr"].should == "fileValue"
298
- end
299
-
300
- it "should read additional attributes" do
301
- file_xattr["user.add_attr"].should == "addValue"
302
- end
303
-
304
- it "should write extended attributes to the underlying file" do
305
- file_xattr["user.file_attr"] = "written"
306
- hello_xattr["user.file_attr"].should == "written"
307
- end
308
-
309
- it "should remove extended attributes from the underlying file" do
310
- file_xattr.remove("user.file_attr")
311
- hello_xattr.list.should_not include("user.file_attr")
312
- end
313
-
314
- it "raise EACCES when writing to additional attributes" do
315
- lambda {file_xattr["user.add_attr"] = "newValue"}.should raise_error(Errno::EACCES)
316
- end
317
-
318
- it "raise EACCES when removing additional attributes" do
319
- lambda {file_xattr.remove("user.add_attr")}.should raise_error(Errno::EACCES)
320
- end
321
-
322
- it "should list additional attributes from virtual directories" do
323
- dir_xattr.list.should include("user.dir_attr")
324
- dir_xattr.list.size.should == 1
325
- end
326
-
327
- it "should read additional attributes from virtual directories" do
328
- dir_xattr["user.dir_attr"].should == "dirValue"
329
- end
330
-
331
- it "should raise EACCES when writing additional attributes on virtual directories" do
332
- lambda {dir_xattr["user.dir_attr"] = "newValue"}.should raise_error(Errno::EACCES)
333
- end
334
-
335
- it "should raise EACCES when deleting additional attributes on virtual directories" do
336
- lambda {dir_xattr.remove("user.dir_attr")}.should raise_error(Errno::EACCES)
337
- end
338
-
339
- end
340
-
341
- context "filesystem statistics" do
342
- before(:each) do
343
- fixture.pathmap("bigfile.txt","/textfiles/bigfile","x" * 2048)
344
- end
345
-
346
- it "reports stats for files" do
347
- statfs = Sys::Filesystem.stat(mountpoint.to_path)
348
- # These are fixed
349
- statfs.block_size.should == 1024
350
- statfs.fragment_size.should == 1024
351
-
352
- # These are dependant on the tests above creating files/directories
353
- statfs.files.should == 10
354
- statfs.files_available == 10
355
-
356
- # assume test are less than 1 block, so dependant on bigfile above
357
- statfs.blocks.should == 2
358
- statfs.blocks_available.should == 0
359
- statfs.blocks_free.should == 0
360
- end
361
-
362
- it "reports stats for files after writing" do
363
-
364
- (mountpoint + "textfiles/bigfile").open("w") { |f| f.print("y" * 4096) }
365
- statfs = Sys::Filesystem.stat(mountpoint.to_path)
366
- statfs.files.should == 10
367
- statfs.blocks.should == 4
368
-
369
- end
370
-
371
- end
372
- end
373
-
374
- context "a real Fuse mount with raw file access" do
375
-
376
- before(:each) do
377
- pathmap_fs.use_raw_file_access = true
378
- pathmap_fs.allow_write = true
379
- end
380
-
381
- it "reads files" do
382
- hello_path = (mountpoint + "textfiles/hello")
383
- hello_path.open do |f|
384
- f.seek(2)
385
- f.read(3).should == "ext"
386
- end
387
-
388
- hello_path.sysopen do |f|
389
- f.sysseek(1)
390
- f.sysread(3).should == "tex"
391
- end
392
- end
393
-
394
- it "writes files" do
395
- hello_path = (mountpoint + "textfiles/hello")
396
- real_path = tmpdir + "hello.txt"
397
- hello_path.open("r+") do |f|
398
- f.sysseek(2)
399
- f.syswrite("zzz")
400
- f.sysseek(0)
401
- f.sysread(6).should == "/tzzzf"
402
- end
403
-
404
- real_path.read.should == "/tzzzfiles/hello"
405
- end
406
-
407
- it "reports filesystem statistics after raw write" do
408
- hello_path = (mountpoint + "textfiles/hello")
409
- hello_path.open("w") do |f|
410
- f.syswrite("z" * 2048)
411
- end
412
-
413
- statfs = Sys::Filesystem.stat(mountpoint.to_path)
414
- statfs.blocks.should == 2
415
- end
416
- end
417
- end
@@ -1,497 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe FuseFS do
4
-
5
- TEST_FILE = "/aPath/aFile"
6
- TEST_DIR = "/aPath"
7
- ROOT_PATH = "/"
8
- Struct.new("FuseFileInfo",:flags,:fh)
9
-
10
- describe "an empty FuseFS object" do
11
- before(:each) do
12
- @fuse = FuseFS::RFuseFS.new(Object.new())
13
- end
14
-
15
- it "should return an appropriate Stat for the root directory" do
16
- stat = @fuse.getattr(nil,ROOT_PATH)
17
- stat.should respond_to(:dev)
18
- (stat.mode & RFuse::Stat::S_IFDIR).should_not == 0
19
- (stat.mode & RFuse::Stat::S_IFREG).should == 0
20
- permissions(stat.mode).should == 0555
21
- end
22
-
23
- it "should have an empty root directory" do
24
- filler = mock("entries_filler")
25
- filler.should_receive(:push).with(".",nil,0)
26
- filler.should_receive(:push).with("..",nil,0)
27
- @fuse.readdir(nil,"/",filler,nil,nil)
28
- end
29
-
30
- it "should raise ENOENT for other paths" do
31
- lambda { @fuse.getattr(nil,"/somepath") }.should raise_error(Errno::ENOENT)
32
- end
33
-
34
- it "should not allow new files or directories" do
35
- lambda { @fuse.mknod(nil,"/afile",0100644,0,0) }.should raise_error(Errno::EACCES)
36
- lambda { @fuse.mkdir(nil,"/adir",0040555) }.should raise_error(Errno::EACCES)
37
- end
38
- end
39
-
40
- describe "a FuseFS filesystem" do
41
- before(:each) do
42
- @mock_fuse = FuseFS::FuseDir.new()
43
- @fuse = FuseFS::RFuseFS.new(@mock_fuse)
44
- end
45
-
46
- describe :readdir do
47
- before(:each) do
48
- @mock_fuse.should_receive(:contents).with("/apath").and_return(["afile"])
49
- end
50
-
51
- it "should add . and .. to the results of :contents when listing a directory" do
52
- filler = mock("entries_filler")
53
- filler.should_receive(:push).with(".",nil,0)
54
- filler.should_receive(:push).with("..",nil,0)
55
- filler.should_receive(:push).with("afile",nil,0)
56
- @fuse.readdir(nil,"/apath",filler,nil,nil)
57
- end
58
-
59
- end
60
-
61
- describe :getattr do
62
-
63
- #Root directory is special (ish) so we need to run these specs twice.
64
- [ROOT_PATH,TEST_DIR].each do |dir|
65
-
66
- context "of a directory #{ dir }" do
67
-
68
- before(:each) do
69
- @mock_fuse.stub!(:file?).and_return(false)
70
- @mock_fuse.should_receive(:directory?).with(dir).at_most(:once).and_return(true)
71
- @checkfile = (dir == "/" ? "" : dir ) + FuseFS::RFuseFS::CHECK_FILE
72
- end
73
-
74
- it "should return a Stat like object representing a directory" do
75
- @mock_fuse.should_receive(:can_write?).with(@checkfile).at_most(:once).and_return(false)
76
- @mock_fuse.should_receive(:can_mkdir?).with(@checkfile).at_most(:once).and_return(false)
77
- stat = @fuse.getattr(nil,dir)
78
- #Apparently find relies on nlink accurately listing the number of files/directories or nlink being 1
79
- stat.nlink.should == 1
80
- filetype(stat.mode).should == RFuse::Stat::S_IFDIR
81
- permissions(stat.mode).should == 0555
82
- end
83
-
84
-
85
- it "should return writable mode if can_mkdir?" do
86
- @mock_fuse.should_receive(:can_mkdir?).with(@checkfile).at_most(:once).and_return(true)
87
-
88
- stat = @fuse.getattr(nil,dir)
89
- permissions(stat.mode).should == 0777
90
- end
91
-
92
- it "should return writable mode if can_write?" do
93
- @mock_fuse.should_receive(:can_write?).with(@checkfile).at_most(:once).and_return(true)
94
-
95
- stat = @fuse.getattr(nil,dir)
96
- permissions(stat.mode).should == 0777
97
-
98
- end
99
-
100
- it "should return times in the result if available" do
101
- @mock_fuse.should_receive(:times).with(dir).and_return([10,20,30])
102
- stat = @fuse.getattr(nil,dir)
103
- stat.atime.should == 10
104
- stat.mtime.should == 20
105
- stat.ctime.should == 30
106
- end
107
- end
108
- end
109
-
110
- describe "a file" do
111
-
112
- before(:each) do
113
- @file="/aPath/aFile"
114
- @mock_fuse.stub!(:directory?).and_return(false)
115
- @mock_fuse.should_receive(:file?).with(@file).at_most(:once).and_return(true)
116
- end
117
-
118
-
119
- it "should return a Stat like object representing a file" do
120
- stat = @fuse.getattr(nil,@file)
121
- (stat.mode & RFuse::Stat::S_IFDIR).should == 0
122
- (stat.mode & RFuse::Stat::S_IFREG).should_not == 0
123
- permissions(stat.mode).should == 0444
124
- end
125
-
126
- it "should indicate executable mode if executable?" do
127
- @mock_fuse.should_receive(:executable?).with(@file).and_return(true)
128
- stat = @fuse.getattr(nil,@file)
129
- permissions(stat.mode).should == 0555
130
- end
131
-
132
- it "should indicate writable mode if can_write?" do
133
- @mock_fuse.should_receive(:can_write?).with(@file).and_return(true)
134
- stat = @fuse.getattr(nil,@file)
135
- permissions(stat.mode).should == 0666
136
- end
137
-
138
- it "should by 777 mode if can_write? and exectuable?" do
139
- @mock_fuse.should_receive(:can_write?).with(@file).and_return(true)
140
- @mock_fuse.should_receive(:executable?).with(@file).and_return(true)
141
- stat = @fuse.getattr(nil,@file)
142
- permissions(stat.mode).should == 0777
143
- end
144
-
145
- it "should include size in the result if available" do
146
- @mock_fuse.should_receive(:size).with(@file).and_return(234)
147
- stat = @fuse.getattr(nil,@file)
148
- stat.size.should == 234
149
- end
150
-
151
- it "should include times in the result if available" do
152
- @mock_fuse.should_receive(:times).with(@file).and_return([22,33,44])
153
- stat = @fuse.getattr(nil,@file)
154
- stat.atime.should == 22
155
- stat.mtime.should == 33
156
- stat.ctime.should == 44
157
- end
158
- end
159
-
160
- it "should raise ENOENT for a path that does not exist" do
161
- @mock_fuse.should_receive(:file?).with(TEST_FILE).and_return(false)
162
- @mock_fuse.should_receive(:directory?).with(TEST_FILE).and_return(false)
163
- lambda{stat = @fuse.getattr(nil,TEST_FILE) }.should raise_error(Errno::ENOENT)
164
- end
165
- end
166
-
167
- context "creating files and directories" do
168
-
169
- it ":mknod should raise EACCES unless :can_write?" do
170
- @mock_fuse.stub!(:file?).with(TEST_FILE).and_return(false)
171
- @mock_fuse.stub!(:directory?).with(TEST_FILE).and_return(false)
172
- @mock_fuse.should_receive(:can_write?).with(TEST_FILE).and_return(false)
173
- lambda{@fuse.mknod(nil,TEST_FILE,0100644,0,0)}.should raise_error(Errno::EACCES)
174
- end
175
-
176
- it ":mkdir should raise EACCES unless :can_mkdir?" do
177
- @mock_fuse.stub!(:file?).with(TEST_FILE).and_return(false)
178
- @mock_fuse.stub!(:directory?).with(TEST_FILE).and_return(false)
179
- @mock_fuse.should_receive(:can_mkdir?).with(TEST_FILE).and_return(false)
180
- lambda{@fuse.mkdir(nil,TEST_FILE,004555)}.should raise_error(Errno::EACCES)
181
- end
182
-
183
- it ":mknod should raise EACCES unless mode requests a regular file" do
184
- @mock_fuse.stub!(:file?).with(TEST_FILE).and_return(false)
185
- @mock_fuse.stub!(:directory?).with(TEST_FILE).and_return(false)
186
- @mock_fuse.stub!(:can_write?).with(TEST_FILE).and_return(true)
187
- lambda{@fuse.mknod(nil,TEST_FILE,RFuse::Stat::S_IFLNK | 0644,0,0)}.should raise_error(Errno::EACCES)
188
- end
189
-
190
- it ":mknod should result in getattr returning a Stat like object representing an empty file" do
191
- @mock_fuse.stub!(:file?).with(TEST_FILE).and_return(false)
192
- @mock_fuse.stub!(:directory?).with(TEST_FILE).and_return(false)
193
- @mock_fuse.stub!(:can_write?).with(TEST_FILE).and_return(true)
194
- @fuse.mknod(nil,TEST_FILE,RFuse::Stat::S_IFREG | 0644,0,0)
195
-
196
- stat = @fuse.getattr(nil,TEST_FILE)
197
- filetype(stat.mode).should == RFuse::Stat::S_IFREG
198
- stat.size.should == 0
199
- end
200
-
201
- it "should create zero length files" do
202
- ffi = Struct::FuseFileInfo.new()
203
- ffi.flags = Fcntl::O_WRONLY
204
- @mock_fuse.stub!(:file?).with(TEST_FILE).and_return(false)
205
- @mock_fuse.stub!(:directory?).with(TEST_FILE).and_return(false)
206
- @mock_fuse.stub!(:can_write?).with(TEST_FILE).and_return(true)
207
- @mock_fuse.should_receive(:write_to).once.with(TEST_FILE,"")
208
- @fuse.mknod(nil,TEST_FILE,RFuse::Stat::S_IFREG | 0644,0,0)
209
- @fuse.open(nil,TEST_FILE,ffi)
210
- @fuse.flush(nil,TEST_FILE,ffi)
211
- @fuse.release(nil,TEST_FILE,ffi)
212
- end
213
-
214
- it ":mkdir should not raise error if can_mkdir?" do
215
- @mock_fuse.should_receive(:can_mkdir?).with(TEST_FILE).and_return(true)
216
- @fuse.mkdir(nil,TEST_FILE,004555)
217
- end
218
-
219
- end
220
-
221
- context "reading files" do
222
- it "should read the contents of a file" do
223
- ffi = Struct::FuseFileInfo.new()
224
- ffi.flags = Fcntl::O_RDONLY
225
- @mock_fuse.stub!(:file?).with(TEST_FILE).and_return(true)
226
- @mock_fuse.stub!(:read_file).with(TEST_FILE).and_return("Hello World\n")
227
- @fuse.open(nil,TEST_FILE,ffi)
228
- #to me fuse is backwards -- size, offset!
229
- @fuse.read(nil,TEST_FILE,5,0,ffi).should == "Hello"
230
- @fuse.read(nil,TEST_FILE,4,6,ffi).should == "Worl"
231
- @fuse.read(nil,TEST_FILE,10,8,ffi).should == "rld\n"
232
- @fuse.flush(nil,TEST_FILE,ffi)
233
- @fuse.release(nil,TEST_FILE,ffi)
234
- end
235
- end
236
-
237
- context "writing files" do
238
- it "should overwrite a file opened WR_ONLY" do
239
- ffi = Struct::FuseFileInfo.new()
240
- ffi.flags = Fcntl::O_WRONLY
241
- @mock_fuse.stub!(:can_write?).with(TEST_FILE).and_return(true)
242
- @mock_fuse.stub!(:read_file).with(TEST_FILE).and_return("I'm writing a file\n")
243
- @mock_fuse.should_receive(:write_to).once().with(TEST_FILE,"My new contents\n")
244
- @fuse.open(nil,TEST_FILE,ffi)
245
- @fuse.ftruncate(nil,TEST_FILE,0,ffi)
246
- @fuse.write(nil,TEST_FILE,"My new c",0,ffi)
247
- @fuse.write(nil,TEST_FILE,"ontents\n",8,ffi)
248
- @fuse.flush(nil,TEST_FILE,ffi)
249
- #that's right flush can be called more than once.
250
- @fuse.flush(nil,TEST_FILE,ffi)
251
- #but then we can write some more and flush again
252
- @fuse.release(nil,TEST_FILE,ffi)
253
- end
254
-
255
- it "should append to a file opened WR_ONLY | APPEND" do
256
- ffi = Struct::FuseFileInfo.new()
257
- ffi.flags = Fcntl::O_WRONLY | Fcntl::O_APPEND
258
- @mock_fuse.stub!(:can_write?).with(TEST_FILE).and_return(true)
259
- @mock_fuse.stub!(:read_file).with(TEST_FILE).and_return("I'm writing a file\n")
260
- @mock_fuse.should_receive(:write_to).once().with(TEST_FILE,"I'm writing a file\nMy new contents\n")
261
- @fuse.open(nil,TEST_FILE,ffi)
262
- @fuse.write(nil,TEST_FILE,"My new c",0,ffi)
263
- @fuse.write(nil,TEST_FILE,"ontents\n",8,ffi)
264
- @fuse.flush(nil,TEST_FILE,ffi)
265
- #that's right flush can be called more than once. But we should only write-to the first time
266
- @fuse.flush(nil,TEST_FILE,ffi)
267
- @fuse.release(nil,TEST_FILE,ffi)
268
-
269
- end
270
-
271
- it "should do sensible things for files opened RDWR"
272
-
273
- it "should pass on buffered data when requested (fsync)"
274
-
275
- end
276
-
277
- context "raw reading" do
278
-
279
- it "should call the raw_read/raw_close if raw_open returns true" do
280
- ffi = Struct::FuseFileInfo.new()
281
- ffi.flags = Fcntl::O_RDONLY
282
- @mock_fuse.stub!(:can_write?).with(TEST_FILE).and_return(true)
283
- @mock_fuse.should_receive(:raw_open).with(TEST_FILE,"r",true).and_return("raw")
284
- @mock_fuse.should_receive(:raw_read).with(TEST_FILE,5,0,"raw").and_return("12345")
285
- @mock_fuse.should_receive(:raw_read).with(TEST_FILE,5,5,"raw").and_return("67890")
286
- @mock_fuse.should_receive(:raw_close).with(TEST_FILE,"raw")
287
- @fuse.open(nil,TEST_FILE,ffi)
288
- @fuse.read(nil,TEST_FILE,0,5,ffi).should == "12345"
289
- @fuse.read(nil,TEST_FILE,5,5,ffi).should == "67890"
290
- @fuse.flush(nil,TEST_FILE,ffi)
291
- @fuse.release(nil,TEST_FILE,ffi)
292
- end
293
-
294
- end
295
-
296
- context "raw writing" do
297
- it "should call other raw_* methods if raw_open returns true" do
298
- ffi = Struct::FuseFileInfo.new()
299
- ffi.flags = Fcntl::O_WRONLY
300
- raw = Object.new()
301
- @mock_fuse.stub!(:can_write?).with(TEST_FILE).and_return(true)
302
- @mock_fuse.should_receive(:raw_open).with(TEST_FILE,"w",true).and_return(raw)
303
- @mock_fuse.should_receive(:raw_truncate).with(TEST_FILE,0,raw)
304
- @mock_fuse.should_receive(:raw_write).with(TEST_FILE,0,5,"12345",raw).once().and_return(5)
305
- @mock_fuse.should_receive(:raw_write).with(TEST_FILE,5,5,"67890",raw).once().and_return(5)
306
- @mock_fuse.should_receive(:raw_sync).with(TEST_FILE, false, raw)
307
- @mock_fuse.should_receive(:raw_close).with(TEST_FILE,raw)
308
- @fuse.open(nil,TEST_FILE,ffi)
309
- @fuse.ftruncate(nil,TEST_FILE,0,ffi)
310
- @fuse.write(nil,TEST_FILE,"12345",0,ffi).should == 5
311
- @fuse.fsync(nil,TEST_FILE,0,ffi)
312
- @fuse.write(nil,TEST_FILE,"67890",5,ffi).should == 5
313
- @fuse.flush(nil,TEST_FILE,ffi)
314
- @fuse.release(nil,TEST_FILE,ffi)
315
- end
316
-
317
- it "should clean up created files" do
318
- ffi = Struct::FuseFileInfo.new()
319
- ffi.flags = Fcntl::O_WRONLY
320
- raw = Object.new()
321
- @mock_fuse.stub!(:directory?).and_return(false)
322
- @mock_fuse.stub!(:file?).with(TEST_FILE).and_return(false,true)
323
- @mock_fuse.stub!(:can_write?).with(TEST_FILE).and_return(true)
324
- @mock_fuse.should_receive(:raw_open).with(TEST_FILE,"w",true).and_return(raw)
325
- @mock_fuse.should_receive(:raw_close).with(TEST_FILE,raw)
326
- @mock_fuse.should_receive(:size).with(TEST_FILE).and_return(25)
327
-
328
- lambda { @fuse.getattr(nil,TEST_FILE) }.should raise_error(Errno::ENOENT)
329
- @fuse.mknod(nil,TEST_FILE,RFuse::Stat::S_IFREG | 0644,0,0)
330
- @fuse.open(nil,TEST_FILE,ffi)
331
- @fuse.flush(nil,TEST_FILE,ffi)
332
- @fuse.release(nil,TEST_FILE,ffi)
333
- stat = @fuse.getattr(nil,TEST_FILE)
334
- stat.size = 25
335
- end
336
-
337
- it "should pass 'wa' to raw_open if fuse sends WRONLY | APPEND" do
338
- ffi = Struct::FuseFileInfo.new()
339
- ffi.flags = Fcntl::O_WRONLY | Fcntl::O_APPEND
340
- raw = Object.new()
341
- @mock_fuse.stub!(:can_write?).with(TEST_FILE).and_return(true)
342
- @mock_fuse.should_receive(:raw_open).with(TEST_FILE,"wa",true).and_return(raw)
343
- @fuse.open(nil,TEST_FILE,ffi)
344
- end
345
- end
346
-
347
- context "deleting files" do
348
- it "should raise EACCES unless :can_delete?" do
349
- @mock_fuse.should_receive(:can_delete?).with(TEST_FILE).and_return(false)
350
- lambda {@fuse.unlink(nil,TEST_FILE)}.should raise_error(Errno::EACCES)
351
- end
352
-
353
- it "should :delete without error if :can_delete?" do
354
- @mock_fuse.stub!(:can_delete?).with(TEST_FILE).and_return(true)
355
- @mock_fuse.should_receive(:delete).with(TEST_FILE)
356
- @fuse.unlink(nil,TEST_FILE)
357
- end
358
-
359
- it "should remove entries created with mknod that have never been opened" do
360
- @mock_fuse.stub!(:file?).with(TEST_FILE).and_return(false)
361
- @mock_fuse.stub!(:directory?).with(TEST_FILE).and_return(false)
362
- @mock_fuse.stub!(:can_delete?).with(TEST_FILE).and_return(true)
363
- @mock_fuse.stub!(:can_write?).with(TEST_FILE).and_return(true)
364
- @fuse.mknod(nil,TEST_FILE,RFuse::Stat::S_IFREG | 0644,0,0)
365
-
366
- @fuse.unlink(nil,TEST_FILE)
367
- lambda {@fuse.getattr(nil,TEST_FILE)}.should raise_error(Errno::ENOENT)
368
- end
369
- end
370
-
371
- context "deleting directories" do
372
- it "should raise EACCES unless :can_rmdir?" do
373
- @mock_fuse.should_receive(:can_rmdir?).with(TEST_DIR).and_return(false)
374
- lambda{@fuse.rmdir(nil,TEST_DIR)}.should raise_error(Errno::EACCES)
375
- end
376
-
377
- it "should :rmdir without error if :can_rmdir?" do
378
- @mock_fuse.stub!(:can_rmdir?).with(TEST_DIR).and_return(true)
379
- @fuse.rmdir(nil,TEST_DIR)
380
- end
381
- end
382
-
383
- context "touching files" do
384
- it "should call :touch in response to utime" do
385
- @mock_fuse.should_receive(:touch).with(TEST_FILE,220)
386
- @fuse.utime(nil,TEST_FILE,100,220)
387
- end
388
- end
389
-
390
- context "renaming files" do
391
- before(:each) do
392
- @oldfile = "/aPath/oldFile"
393
- @newfile = "/aNewFile"
394
- @mock_fuse.stub!(:file?).with(@oldfile).and_return(true)
395
- @mock_fuse.stub!(:directory?).with(@oldfile).and_return(false)
396
- end
397
- it "should raise EACCES unless :can_write? the new file" do
398
- @mock_fuse.stub!(:can_delete?).with(@oldfile).and_return(true)
399
- @mock_fuse.should_receive(:can_write?).with(@newfile).and_return(false)
400
- lambda {@fuse.rename(nil,@oldfile,@newfile)}.should raise_error(Errno::EACCES)
401
- end
402
-
403
- it "should raise EACCES unless :can_delete the old file" do
404
- @mock_fuse.stub!(:can_write?).with(@newfile).and_return(true)
405
- @mock_fuse.should_receive(:can_delete?).with(@oldfile).and_return(false)
406
- lambda {@fuse.rename(nil,@oldfile,@newfile)}.should raise_error(Errno::EACCES)
407
- end
408
-
409
- it "should copy and delete files" do
410
- @mock_fuse.stub!(:can_write?).with(@newfile).and_return(true)
411
- @mock_fuse.stub!(:can_delete?).with(@oldfile).and_return(true)
412
- @mock_fuse.should_receive(:read_file).with(@oldfile).and_return("some contents\n")
413
- @mock_fuse.should_receive(:write_to).with(@newfile,"some contents\n")
414
- @mock_fuse.should_receive(:delete).with(@oldfile)
415
- @fuse.rename(nil,@oldfile,@newfile)
416
- end
417
-
418
- it "should not copy and delete files if fs responds_to? :rename" do
419
- @mock_fuse.should_receive(:rename).with(@oldfile,@newfile).and_return(true)
420
- @fuse.rename(nil,@oldfile,@newfile)
421
- end
422
-
423
- it "should raise EACCES if moving a directory and rename not supported" do
424
- @mock_fuse.stub!(:file?).with(@oldfile).and_return(false)
425
- @mock_fuse.stub!(:directory?).with(@oldfile).and_return(true)
426
- @mock_fuse.stub!(:can_write?).with(@newfile).and_return(true)
427
- @mock_fuse.stub!(:can_delete?).with(@oldfile).and_return(true)
428
- lambda{@fuse.rename(nil,@oldfile,@newfile)}.should raise_error(Errno::EACCES)
429
- end
430
-
431
- end
432
- context "extended attributes" do
433
-
434
- let(:xattr) { mock(:xattr) }
435
- before(:each) { @mock_fuse.stub!(:xattr).with(TEST_FILE).and_return(xattr) }
436
-
437
- it "should list attributes via #keys on result of #xattr" do
438
- xattr.should_receive(:keys).and_return(["one","two"])
439
- @fuse.listxattr(nil,TEST_FILE).should == [ "one","two" ]
440
- end
441
-
442
- it "should get attributes via #xattr.[]" do
443
- xattr.should_receive(:[]).with("user.one").and_return("one")
444
-
445
- @fuse.getxattr(nil,TEST_FILE,"user.one").should == "one"
446
- end
447
-
448
- it "should set attributes via #xattr.[]=" do
449
- xattr.should_receive(:[]=).with("user.two","two")
450
-
451
- @fuse.setxattr(nil,TEST_FILE,"user.two","two",0)
452
- end
453
-
454
- it "should remove attributes via #xattr.delete" do
455
- xattr.should_receive(:delete).with("user.three")
456
-
457
- @fuse.removexattr(nil,TEST_FILE,"user.three")
458
- end
459
-
460
- it "should raise ENODATA when #xattr.[] returns nil" do
461
-
462
- xattr.should_receive(:[]).with("user.xxxx").and_return(nil)
463
- lambda{@fuse.getxattr(nil,TEST_FILE,"user.xxxx") }.should raise_error(Errno::ENODATA)
464
- end
465
- end
466
-
467
- context "#statfs" do
468
- # used space, used files, total_space, total_files
469
- let(:stats) { [ 1000 * 1024, 5, 1200 * 1024, 12 ] }
470
- it "should convert simple array into StatVfs" do
471
-
472
- @mock_fuse.should_receive(:statistics).with(TEST_FILE).and_return(stats)
473
-
474
- result = @fuse.statfs(nil,TEST_FILE)
475
- result.should be_kind_of(RFuse::StatVfs)
476
- result.f_bsize.should == 1024
477
- result.f_blocks.should == 1200
478
- result.f_bavail.should == 200
479
- result.f_files.should == 12
480
- result.f_ffree.should == 7
481
- end
482
-
483
- it "passes on raw statistics" do
484
- statvfs = Object.new()
485
- @mock_fuse.should_receive(:statistics).with(TEST_FILE).and_return(statvfs)
486
-
487
- @fuse.statfs(nil,TEST_FILE).should equal(statvfs)
488
- end
489
-
490
- end
491
- end
492
-
493
- describe "a FuseFS filesystem with gid/uid specific behaviour" do
494
- it "should provide context uid and gid for all API methods"
495
- end
496
-
497
- end