rfusefs 1.0.2.RC1 → 1.1.0

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,477 +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
- it "should call the raw_read/raw_close if raw_open returns true" do
279
- ffi = Struct::FuseFileInfo.new()
280
- ffi.flags = Fcntl::O_RDONLY
281
- @mock_fuse.stub!(:can_write?).with(TEST_FILE).and_return(true)
282
- @mock_fuse.should_receive(:raw_open).with(TEST_FILE,"r",true).and_return("raw")
283
- @mock_fuse.should_receive(:raw_read).with(TEST_FILE,5,0,"raw").and_return("12345")
284
- @mock_fuse.should_receive(:raw_read).with(TEST_FILE,5,5,"raw").and_return("67890")
285
- @mock_fuse.should_receive(:raw_close).with(TEST_FILE,"raw")
286
- @fuse.open(nil,TEST_FILE,ffi)
287
- @fuse.read(nil,TEST_FILE,0,5,ffi).should == "12345"
288
- @fuse.read(nil,TEST_FILE,5,5,ffi).should == "67890"
289
- @fuse.flush(nil,TEST_FILE,ffi)
290
- @fuse.release(nil,TEST_FILE,ffi)
291
- end
292
-
293
- end
294
-
295
- context "raw writing" do
296
- it "should call other raw_* methods if raw_open returns true" do
297
- ffi = Struct::FuseFileInfo.new()
298
- ffi.flags = Fcntl::O_WRONLY
299
- raw = Object.new()
300
- @mock_fuse.stub!(:can_write?).with(TEST_FILE).and_return(true)
301
- @mock_fuse.should_receive(:raw_open).with(TEST_FILE,"w",true).and_return(raw)
302
- @mock_fuse.should_receive(:raw_truncate).with(TEST_FILE,0,raw)
303
- @mock_fuse.should_receive(:raw_write).with(TEST_FILE,0,5,"12345",raw).once().and_return(5)
304
- @mock_fuse.should_receive(:raw_write).with(TEST_FILE,5,5,"67890",raw).once().and_return(5)
305
- @mock_fuse.should_receive(:raw_sync).with(TEST_FILE, false, raw)
306
- @mock_fuse.should_receive(:raw_close).with(TEST_FILE,raw)
307
- @fuse.open(nil,TEST_FILE,ffi)
308
- @fuse.ftruncate(nil,TEST_FILE,0,ffi)
309
- @fuse.write(nil,TEST_FILE,"12345",0,ffi).should == 5
310
- @fuse.fsync(nil,TEST_FILE,0,ffi)
311
- @fuse.write(nil,TEST_FILE,"67890",5,ffi).should == 5
312
- @fuse.flush(nil,TEST_FILE,ffi)
313
- @fuse.release(nil,TEST_FILE,ffi)
314
- end
315
-
316
- it "should pass 'wa' to raw_open if fuse sends WRONLY | APPEND" do
317
- ffi = Struct::FuseFileInfo.new()
318
- ffi.flags = Fcntl::O_WRONLY | Fcntl::O_APPEND
319
- raw = Object.new()
320
- @mock_fuse.stub!(:can_write?).with(TEST_FILE).and_return(true)
321
- @mock_fuse.should_receive(:raw_open).with(TEST_FILE,"wa",true).and_return(raw)
322
- @fuse.open(nil,TEST_FILE,ffi)
323
- end
324
- end
325
-
326
- context "deleting files" do
327
- it "should raise EACCES unless :can_delete?" do
328
- @mock_fuse.should_receive(:can_delete?).with(TEST_FILE).and_return(false)
329
- lambda {@fuse.unlink(nil,TEST_FILE)}.should raise_error(Errno::EACCES)
330
- end
331
-
332
- it "should :delete without error if :can_delete?" do
333
- @mock_fuse.stub!(:can_delete?).with(TEST_FILE).and_return(true)
334
- @mock_fuse.should_receive(:delete).with(TEST_FILE)
335
- @fuse.unlink(nil,TEST_FILE)
336
- end
337
-
338
- it "should remove entries created with mknod that have never been opened" do
339
- @mock_fuse.stub!(:file?).with(TEST_FILE).and_return(false)
340
- @mock_fuse.stub!(:directory?).with(TEST_FILE).and_return(false)
341
- @mock_fuse.stub!(:can_delete?).with(TEST_FILE).and_return(true)
342
- @mock_fuse.stub!(:can_write?).with(TEST_FILE).and_return(true)
343
- @fuse.mknod(nil,TEST_FILE,RFuse::Stat::S_IFREG | 0644,0,0)
344
-
345
- @fuse.unlink(nil,TEST_FILE)
346
- lambda {@fuse.getattr(nil,TEST_FILE)}.should raise_error(Errno::ENOENT)
347
- end
348
- end
349
-
350
- context "deleting directories" do
351
- it "should raise EACCES unless :can_rmdir?" do
352
- @mock_fuse.should_receive(:can_rmdir?).with(TEST_DIR).and_return(false)
353
- lambda{@fuse.rmdir(nil,TEST_DIR)}.should raise_error(Errno::EACCES)
354
- end
355
-
356
- it "should :rmdir without error if :can_rmdir?" do
357
- @mock_fuse.stub!(:can_rmdir?).with(TEST_DIR).and_return(true)
358
- @fuse.rmdir(nil,TEST_DIR)
359
- end
360
- end
361
-
362
- context "touching files" do
363
- it "should call :touch in response to utime" do
364
- @mock_fuse.should_receive(:touch).with(TEST_FILE,220)
365
- @fuse.utime(nil,TEST_FILE,100,220)
366
- end
367
- end
368
-
369
- context "renaming files" do
370
- before(:each) do
371
- @oldfile = "/aPath/oldFile"
372
- @newfile = "/aNewFile"
373
- @mock_fuse.stub!(:file?).with(@oldfile).and_return(true)
374
- @mock_fuse.stub!(:directory?).with(@oldfile).and_return(false)
375
- end
376
- it "should raise EACCES unless :can_write? the new file" do
377
- @mock_fuse.stub!(:can_delete?).with(@oldfile).and_return(true)
378
- @mock_fuse.should_receive(:can_write?).with(@newfile).and_return(false)
379
- lambda {@fuse.rename(nil,@oldfile,@newfile)}.should raise_error(Errno::EACCES)
380
- end
381
-
382
- it "should raise EACCES unless :can_delete the old file" do
383
- @mock_fuse.stub!(:can_write?).with(@newfile).and_return(true)
384
- @mock_fuse.should_receive(:can_delete?).with(@oldfile).and_return(false)
385
- lambda {@fuse.rename(nil,@oldfile,@newfile)}.should raise_error(Errno::EACCES)
386
- end
387
-
388
- it "should copy and delete files" do
389
- @mock_fuse.stub!(:can_write?).with(@newfile).and_return(true)
390
- @mock_fuse.stub!(:can_delete?).with(@oldfile).and_return(true)
391
- @mock_fuse.should_receive(:read_file).with(@oldfile).and_return("some contents\n")
392
- @mock_fuse.should_receive(:write_to).with(@newfile,"some contents\n")
393
- @mock_fuse.should_receive(:delete).with(@oldfile)
394
- @fuse.rename(nil,@oldfile,@newfile)
395
- end
396
-
397
- it "should not copy and delete files if fs responds_to? :rename" do
398
- @mock_fuse.should_receive(:rename).with(@oldfile,@newfile).and_return(true)
399
- @fuse.rename(nil,@oldfile,@newfile)
400
- end
401
-
402
- it "should raise EACCES if moving a directory and rename not supported" do
403
- @mock_fuse.stub!(:file?).with(@oldfile).and_return(false)
404
- @mock_fuse.stub!(:directory?).with(@oldfile).and_return(true)
405
- @mock_fuse.stub!(:can_write?).with(@newfile).and_return(true)
406
- @mock_fuse.stub!(:can_delete?).with(@oldfile).and_return(true)
407
- lambda{@fuse.rename(nil,@oldfile,@newfile)}.should raise_error(Errno::EACCES)
408
- end
409
-
410
- end
411
- context "extended attributes" do
412
-
413
- let(:xattr) { mock(:xattr) }
414
- before(:each) { @mock_fuse.stub!(:xattr).with(TEST_FILE).and_return(xattr) }
415
-
416
- it "should list attributes via #keys on result of #xattr" do
417
- xattr.should_receive(:keys).and_return(["one","two"])
418
- @fuse.listxattr(nil,TEST_FILE).should == [ "one","two" ]
419
- end
420
-
421
- it "should get attributes via #xattr.[]" do
422
- xattr.should_receive(:[]).with("user.one").and_return("one")
423
-
424
- @fuse.getxattr(nil,TEST_FILE,"user.one").should == "one"
425
- end
426
-
427
- it "should set attributes via #xattr.[]=" do
428
- xattr.should_receive(:[]=).with("user.two","two")
429
-
430
- @fuse.setxattr(nil,TEST_FILE,"user.two","two",0)
431
- end
432
-
433
- it "should remove attributes via #xattr.delete" do
434
- xattr.should_receive(:delete).with("user.three")
435
-
436
- @fuse.removexattr(nil,TEST_FILE,"user.three")
437
- end
438
-
439
- it "should raise ENODATA when #xattr.[] returns nil" do
440
-
441
- xattr.should_receive(:[]).with("user.xxxx").and_return(nil)
442
- lambda{@fuse.getxattr(nil,TEST_FILE,"user.xxxx") }.should raise_error(Errno::ENODATA)
443
- end
444
- end
445
-
446
- context "#statfs" do
447
- # used space, used files, total_space, total_files
448
- let(:stats) { [ 1000 * 1024, 5, 1200 * 1024, 12 ] }
449
- it "should convert simple array into StatVfs" do
450
-
451
- @mock_fuse.should_receive(:statistics).with(TEST_FILE).and_return(stats)
452
-
453
- result = @fuse.statfs(nil,TEST_FILE)
454
- result.should be_kind_of(RFuse::StatVfs)
455
- result.f_bsize.should == 1024
456
- result.f_blocks.should == 1200
457
- result.f_bavail.should == 200
458
- result.f_files.should == 12
459
- result.f_ffree.should == 7
460
- end
461
-
462
- it "passes on raw statistics" do
463
- statvfs = Object.new()
464
- @mock_fuse.should_receive(:statistics).with(TEST_FILE).and_return(statvfs)
465
-
466
- @fuse.statfs(nil,TEST_FILE).should equal(statvfs)
467
- end
468
-
469
- end
470
- end
471
-
472
- describe "a FuseFS filesystem with gid/uid specific behaviour" do
473
- it "should provide context uid and gid for all API methods"
474
- end
475
- end
476
-
477
-