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