rfusefs 0.8.0 → 1.0.0.RC0
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.
- data/.gitignore +9 -0
- data/Gemfile +4 -0
- data/History.rdoc +20 -0
- data/README.rdoc +20 -15
- data/Rakefile +10 -17
- data/TODO.txt +1 -2
- data/lib/fuse/fusedir.rb +202 -0
- data/lib/fuse/rfusefs-fuse.rb +402 -482
- data/lib/fusefs/dirlink.rb +1 -1
- data/lib/fusefs/metadir.rb +240 -240
- data/lib/fusefs/pathmapper.rb +189 -188
- data/lib/rfusefs/version.rb +3 -0
- data/lib/rfusefs.rb +109 -301
- data/samples/demo.rb +9 -14
- data/samples/dictfs.rb +4 -14
- data/samples/hello.rb +1 -2
- data/spec/metadir_spec.rb +295 -0
- data/spec/pathmapper_spec.rb +112 -0
- data/spec/rfusefs_spec.rb +77 -76
- data/spec/sample_spec.rb +3 -2
- data/spec/spec_helper.rb +7 -1
- metadata +112 -124
- data/.gemtest +0 -0
- data/History.txt +0 -9
- data.tar.gz.sig +0 -0
- metadata.gz.sig +0 -1
data/lib/fuse/rfusefs-fuse.rb
CHANGED
@@ -1,497 +1,417 @@
|
|
1
1
|
# RFuseFS - FuseFS over RFuse
|
2
|
-
require '
|
2
|
+
require 'rfuse'
|
3
3
|
require 'fcntl'
|
4
4
|
|
5
5
|
module FuseFS
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
S_IFSOCK = 0140000 # Socket.
|
20
|
-
|
21
|
-
def self.directory(mode=0,values = { })
|
22
|
-
return self.new(S_IFDIR,mode,values)
|
23
|
-
end
|
24
|
-
|
25
|
-
def self.file(mode=0,values = { })
|
26
|
-
return self.new(S_IFREG,mode,values)
|
27
|
-
end
|
28
|
-
|
29
|
-
attr_accessor :uid,:gid,:mode,:size,:atime,:mtime,:ctime
|
30
|
-
attr_accessor :dev,:ino,:nlink,:rdev,:blksize,:blocks
|
31
|
-
|
32
|
-
def initialize(type,permissions,values = { })
|
33
|
-
values[:mode] = ((type & S_IFMT) | (permissions & 07777))
|
34
|
-
@uid,@gid,@size,@mode,@atime,@mtime,@ctime,@dev,@ino,@nlink,@rdev,@blksize,@blocks = Array.new(13,0)
|
35
|
-
values.each_pair do |k,v|
|
36
|
-
instance_variable_set("@#{ k }",v)
|
37
|
-
end
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
# Filesystem attributes (eg for df output)
|
42
|
-
class StatVfs
|
43
|
-
attr_accessor :f_bsize,:f_frsize,:f_blocks,:f_bfree,:f_bavail
|
44
|
-
attr_accessor :f_files,:f_ffree,:f_favail,:f_fsid,:f_flag,:f_namemax
|
45
|
-
#values can be symbols or strings but drop the pointless f_ prefix
|
46
|
-
def initialize(values={ })
|
47
|
-
@f_bsize, @f_frsize, @f_blocks, @f_bfree, @f_bavail, @f_files, @f_ffree, @f_favail,@f_fsid, @f_flag,@f_namemax = Array.new(13,0)
|
48
|
-
values.each_pair do |k,v|
|
49
|
-
instance_variable_set("@f_#{ k }",v)
|
50
|
-
end
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
class FileHandle
|
55
|
-
@@fh = 0
|
56
|
-
attr_reader :id,:flags,:path
|
57
|
-
attr_accessor :raw,:contents
|
58
|
-
def initialize(path,flags)
|
59
|
-
@id = (@@fh += 1)
|
60
|
-
@flags = flags
|
61
|
-
@path = path
|
62
|
-
@modified = false
|
63
|
-
@contents = ""
|
64
|
-
end
|
65
|
-
|
66
|
-
def read(offset,size)
|
67
|
-
contents[offset,size]
|
68
|
-
end
|
69
|
-
|
70
|
-
def write(offset,data)
|
71
|
-
if append? || offset >= contents.length
|
72
|
-
#ignore offset
|
73
|
-
contents << data
|
74
|
-
else
|
75
|
-
contents[offset,data.length]=data
|
76
|
-
end
|
77
|
-
@modified = true
|
78
|
-
return data.length
|
79
|
-
end
|
80
|
-
|
81
|
-
def flush
|
82
|
-
@modified = false
|
83
|
-
contents
|
84
|
-
end
|
85
|
-
|
86
|
-
def modified?
|
87
|
-
@modified
|
88
|
-
end
|
89
|
-
|
90
|
-
def accmode
|
91
|
-
flags & Fcntl::O_ACCMODE
|
92
|
-
end
|
93
|
-
|
94
|
-
def rdwr?
|
95
|
-
accmode == Fcntl::O_RDWR
|
96
|
-
end
|
97
|
-
|
98
|
-
def wronly?
|
99
|
-
accmode == Fcntl::O_WRONLY
|
100
|
-
end
|
101
|
-
|
102
|
-
def rdonly?
|
103
|
-
accmode == Fcntl::O_RDONLY
|
104
|
-
end
|
105
|
-
|
106
|
-
def append?
|
107
|
-
writing? && (flags & Fcntl::O_APPEND != 0)
|
108
|
-
end
|
109
|
-
|
110
|
-
def reading?
|
111
|
-
rdonly? || rdwr?
|
112
|
-
end
|
113
|
-
|
114
|
-
def writing?
|
115
|
-
wronly? || rdwr?
|
116
|
-
end
|
117
|
-
|
118
|
-
def raw_mode
|
119
|
-
mode_str = case accmode
|
120
|
-
when Fcntl::O_RDWR; "rw"
|
121
|
-
when Fcntl::O_RDONLY; "r"
|
122
|
-
when Fcntl::O_WRONLY; "w"
|
123
|
-
end
|
124
|
-
|
125
|
-
mode_str << "a" if append?
|
126
|
-
return mode_str
|
127
|
-
end
|
128
|
-
end
|
129
|
-
|
130
|
-
#This is the class associated with the rfuse_ng extension
|
131
|
-
#We use a delegator here so we can test the RFuseFSAPI
|
132
|
-
#without actually mounting FUSE
|
133
|
-
class RFuseFS < RFuse::Fuse
|
134
|
-
CHECK_FILE="/._rfuse_check_"
|
135
|
-
|
136
|
-
#Wrap all the rfuse methods in a context block
|
137
|
-
[:readdir,:getattr,:mkdir,:mknod,
|
138
|
-
:truncate,:open,:read,:write,:flush,:release,
|
139
|
-
:utime,:rmdir,:unlink,:rename].each do |method|
|
140
|
-
method = method.id2name
|
141
|
-
class_eval(<<-EOM, "(__FUSE_DELEGATE__)",1 )
|
142
|
-
def #{method} (ctx,*args,&block)
|
143
|
-
begin
|
144
|
-
RFuseFS.context(ctx) do
|
145
|
-
puts "==> #{ self }.#{ method }(\#{args.inspect })" if $DEBUG
|
146
|
-
result = @delegate.send(:#{method},*args,&block)
|
147
|
-
puts "<== #{ self }.#{ method }() => \#{ result.inspect }" if $DEBUG
|
148
|
-
result
|
149
|
-
end
|
150
|
-
rescue Exception => ex
|
151
|
-
$@.delete_if{|s| /^\\(__FUSE_DELEGATE__\\):/ =~s}
|
152
|
-
puts(ex.message)
|
153
|
-
Kernel::raise
|
154
|
-
end
|
155
|
-
end
|
156
|
-
EOM
|
157
|
-
end
|
158
|
-
|
159
|
-
def initialize(mnt,kernelopt,libopt,root)
|
160
|
-
@delegate = RFuseFSAPI.new(root)
|
161
|
-
super(mnt.to_s,kernelopt,libopt)
|
162
|
-
|
163
|
-
end
|
164
|
-
|
165
|
-
def init(ctx,rfuseconninfo)
|
166
|
-
#print "Init #{rfuseconninfo.inspect}\n"
|
167
|
-
return nil
|
168
|
-
end
|
169
|
-
|
170
|
-
private
|
171
|
-
def self.context(ctx)
|
172
|
-
begin
|
173
|
-
Thread.current[:fusefs_reader_uid] = ctx.uid
|
174
|
-
Thread.current[:fusefs_reader_gid] = ctx.gid
|
175
|
-
yield if block_given?
|
176
|
-
ensure
|
177
|
-
Thread.current[:fusefs_reader_uid] = nil
|
178
|
-
Thread.current[:fusefs_reader_gid] = nil
|
179
|
-
end
|
180
|
-
end
|
181
|
-
end #class RFuseFS
|
182
|
-
|
183
|
-
# Implements the FuseFS api.
|
184
|
-
# The path supplied to these methods is generally validated by FUSE itself
|
185
|
-
# with a prior "getattr" call so we do not revalidate here.
|
186
|
-
# http://sourceforge.net/apps/mediawiki/fuse/index.php?title=FuseInvariants
|
187
|
-
class RFuseFSAPI
|
188
|
-
|
189
|
-
#If not implemented by our filesystem these values are returned
|
190
|
-
API_METHODS = {
|
191
|
-
:can_write? => false,
|
192
|
-
:write_to => nil,
|
193
|
-
:can_delete? => false,
|
194
|
-
:delete => nil,
|
195
|
-
:can_mkdir? => false,
|
196
|
-
:mkdir => nil,
|
197
|
-
:can_rmdir? => false,
|
198
|
-
:rmdir => nil,
|
199
|
-
:touch => nil,
|
200
|
-
:rename => nil,
|
201
|
-
:raw_truncate => nil,
|
202
|
-
:raw_open => nil,
|
203
|
-
:raw_read => nil,
|
204
|
-
:raw_write => nil,
|
205
|
-
:raw_close => nil,
|
206
|
-
:size => 0,
|
207
|
-
:times => Array.new(3,0),
|
208
|
-
:contents => Array.new(),
|
209
|
-
:file? => false,
|
210
|
-
:directory? => false,
|
211
|
-
:executable? => false
|
212
|
-
}
|
213
|
-
|
214
|
-
def initialize(root)
|
215
|
-
@root = root
|
216
|
-
@created_files = { }
|
217
|
-
|
218
|
-
#Define method missing for our filesystem
|
219
|
-
#so we can just call all the API methods as required.
|
220
|
-
def @root.method_missing(method,*args)
|
221
|
-
if API_METHODS.has_key?(method)
|
222
|
-
return API_METHODS[method]
|
223
|
-
else
|
224
|
-
super
|
6
|
+
#Which raw api should we use?
|
7
|
+
RFUSEFS_COMPATIBILITY = true unless FuseFS.const_defined?(:RFUSEFS_COMPATIBILITY)
|
8
|
+
|
9
|
+
class FileHandle
|
10
|
+
@@fh = 0
|
11
|
+
attr_reader :id,:flags,:path
|
12
|
+
attr_accessor :raw,:contents
|
13
|
+
def initialize(path,flags)
|
14
|
+
@id = (@@fh += 1)
|
15
|
+
@flags = flags
|
16
|
+
@path = path
|
17
|
+
@modified = false
|
18
|
+
@contents = ""
|
225
19
|
end
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
def readdir(path,filler,offset,ffi)
|
232
|
-
|
233
|
-
#Always have "." and ".."
|
234
|
-
filler.push(".",nil,0)
|
235
|
-
filler.push("..",nil,0)
|
236
|
-
|
237
|
-
@root.contents(path).each do | filename |
|
238
|
-
filler.push(filename,nil,0)
|
239
|
-
end
|
240
|
-
|
241
|
-
end
|
242
|
-
|
243
|
-
def getattr(path)
|
244
|
-
uid = Process.gid
|
245
|
-
gid = Process.uid
|
246
|
-
|
247
|
-
if path == "/" || @root.directory?(path)
|
248
|
-
#set "w" flag based on can_mkdir? || can_write? to path + "/._rfuse_check"
|
249
|
-
write_test_path = (path == "/" ? "" : path) + RFuseFS::CHECK_FILE
|
250
|
-
|
251
|
-
mode = (@root.can_mkdir?(write_test_path) || @root.can_write?(write_test_path)) ? 0777 : 0555
|
252
|
-
atime,mtime,ctime = @root.times(path)
|
253
|
-
#nlink is set to 1 because apparently this makes find work.
|
254
|
-
return Stat.directory(mode,{ :uid => uid, :gid => gid, :nlink => 1, :atime => atime, :mtime => mtime, :ctime => ctime })
|
255
|
-
elsif @created_files.has_key?(path)
|
256
|
-
now = Time.now.to_i
|
257
|
-
return Stat.file(@created_files[path],{ :uid => uid, :gid => gid, :atime => now, :mtime => now, :ctime => now })
|
258
|
-
elsif @root.file?(path)
|
259
|
-
#Set mode from can_write and executable
|
260
|
-
mode = 0444
|
261
|
-
mode |= 0222 if @root.can_write?(path)
|
262
|
-
mode |= 0111 if @root.executable?(path)
|
263
|
-
size = @root.size(path)
|
264
|
-
atime,mtime,ctime = @root.times(path)
|
265
|
-
return Stat.file(mode,{ :uid => uid, :gid => gid, :size => size, :atime => atime, :mtime => mtime, :ctime => ctime })
|
266
|
-
else
|
267
|
-
raise Errno::ENOENT.new(path)
|
268
|
-
end
|
269
|
-
|
270
|
-
end #getattr
|
271
|
-
|
272
|
-
def mkdir(path,mode)
|
273
|
-
|
274
|
-
unless @root.can_mkdir?(path)
|
275
|
-
raise Errno::EACCES.new(path)
|
276
|
-
end
|
277
|
-
|
278
|
-
@root.mkdir(path)
|
279
|
-
end #mkdir
|
280
|
-
|
281
|
-
def mknod(path,mode,dev)
|
282
|
-
|
283
|
-
unless ((Stat::S_IFMT & mode) == Stat::S_IFREG ) && @root.can_write?(path)
|
284
|
-
raise Errno::EACCES.new(path)
|
285
|
-
end
|
286
|
-
|
287
|
-
@created_files[path] = mode
|
288
|
-
end #mknod
|
289
|
-
|
290
|
-
#ftruncate - eg called after opening a file for write without append
|
291
|
-
def ftruncate(path,offset,ffi)
|
292
|
-
fh = ffi.fh
|
293
|
-
|
294
|
-
if fh.raw
|
295
|
-
@root.raw_truncate(path,offset,fh.raw)
|
296
|
-
elsif (offset <= 0)
|
297
|
-
fh.contents = ""
|
298
|
-
else
|
299
|
-
fh.contents = fh.contents[0..offset]
|
300
|
-
end
|
301
|
-
end
|
302
|
-
|
303
|
-
#truncate a file outside of open files
|
304
|
-
def truncate(path,offset)
|
305
|
-
|
306
|
-
unless @root.can_write?(path)
|
307
|
-
raise Errno::EACESS.new(path)
|
308
|
-
end
|
309
|
-
|
310
|
-
unless @root.raw_truncate(path,offset)
|
311
|
-
contents = @root.read_file(path)
|
312
|
-
if (offset <= 0)
|
313
|
-
@root.write_to(path,"")
|
314
|
-
elsif offset < contents.length
|
315
|
-
@root.write_to(path,contents[0..offset] )
|
20
|
+
|
21
|
+
def read(offset,size)
|
22
|
+
contents[offset,size]
|
316
23
|
end
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
fh = FileHandle.new(path,ffi.flags)
|
325
|
-
|
326
|
-
#Save the value return from raw_open to be passed back in raw_read/write etc..
|
327
|
-
if (FuseFS::RFUSEFS_COMPATIBILITY)
|
328
|
-
fh.raw = @root.raw_open(path,fh.raw_mode,true)
|
329
|
-
else
|
330
|
-
fh.raw = @root.raw_open(path,fh.raw_mode)
|
331
|
-
end
|
332
|
-
|
333
|
-
unless fh.raw
|
334
|
-
|
335
|
-
if fh.rdonly?
|
336
|
-
fh.contents = @root.read_file(path)
|
337
|
-
elsif fh.rdwr? || fh.wronly?
|
338
|
-
unless @root.can_write?(path)
|
339
|
-
raise Errno::EACCES.new(path)
|
340
|
-
end
|
341
|
-
|
342
|
-
if @created_files.has_key?(path)
|
343
|
-
#we have an empty file
|
344
|
-
fh.contents = "";
|
345
|
-
else
|
346
|
-
if fh.rdwr? || fh.append?
|
347
|
-
fh.contents = @root.read_file(path)
|
348
|
-
else #wronly && !append
|
349
|
-
#We should get a truncate 0, but might as well play it safe
|
350
|
-
fh.contents = ""
|
24
|
+
|
25
|
+
def write(offset,data)
|
26
|
+
if append? || offset >= contents.length
|
27
|
+
#ignore offset
|
28
|
+
contents << data
|
29
|
+
else
|
30
|
+
contents[offset,data.length]=data
|
351
31
|
end
|
352
|
-
|
353
|
-
|
354
|
-
raise Errno::ENOPERM.new(path)
|
32
|
+
@modified = true
|
33
|
+
return data.length
|
355
34
|
end
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
end
|
361
|
-
|
362
|
-
def read(path,size,offset,ffi)
|
363
|
-
fh = ffi.fh
|
364
|
-
|
365
|
-
if fh.raw
|
366
|
-
if FuseFS::RFUSEFS_COMPATIBILITY
|
367
|
-
return @root.raw_read(path,offset,size,fh.raw)
|
368
|
-
else
|
369
|
-
return @root.raw_read(path,offset,size)
|
35
|
+
|
36
|
+
def flush
|
37
|
+
@modified = false
|
38
|
+
contents
|
370
39
|
end
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
#TODO: Raise? what does a negative offset mean
|
375
|
-
return ""
|
376
|
-
end
|
377
|
-
|
378
|
-
|
379
|
-
end
|
380
|
-
|
381
|
-
def write(path,buf,offset,ffi)
|
382
|
-
fh = ffi.fh
|
383
|
-
|
384
|
-
if fh.raw
|
385
|
-
if FuseFS::RFUSEFS_COMPATIBILITY
|
386
|
-
return @root.raw_write(path,offset,buf.length,buf,fh.raw)
|
387
|
-
else
|
388
|
-
@root.raw_write(path,offset,buf.length,buf)
|
389
|
-
return buf.length
|
40
|
+
|
41
|
+
def modified?
|
42
|
+
@modified
|
390
43
|
end
|
391
|
-
else
|
392
|
-
return fh.write(offset,buf)
|
393
|
-
end
|
394
|
-
|
395
|
-
end
|
396
|
-
|
397
|
-
def flush(path,ffi)
|
398
|
-
fh = ffi.fh
|
399
|
-
|
400
|
-
if !fh.raw && fh.modified?
|
401
|
-
#write contents to the file and mark it unmodified
|
402
|
-
@root.write_to(path,fh.flush())
|
403
|
-
#if it was created with mknod it now exists in the filesystem...
|
404
|
-
@created_files.delete(path)
|
405
|
-
end
|
406
|
-
|
407
|
-
end
|
408
44
|
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
45
|
+
def accmode
|
46
|
+
flags & Fcntl::O_ACCMODE
|
47
|
+
end
|
48
|
+
|
49
|
+
def rdwr?
|
50
|
+
accmode == Fcntl::O_RDWR
|
51
|
+
end
|
52
|
+
|
53
|
+
def wronly?
|
54
|
+
accmode == Fcntl::O_WRONLY
|
55
|
+
end
|
56
|
+
|
57
|
+
def rdonly?
|
58
|
+
accmode == Fcntl::O_RDONLY
|
59
|
+
end
|
60
|
+
|
61
|
+
def append?
|
62
|
+
writing? && (flags & Fcntl::O_APPEND != 0)
|
63
|
+
end
|
64
|
+
|
65
|
+
def reading?
|
66
|
+
rdonly? || rdwr?
|
67
|
+
end
|
68
|
+
|
69
|
+
def writing?
|
70
|
+
wronly? || rdwr?
|
71
|
+
end
|
72
|
+
|
73
|
+
def raw_mode
|
74
|
+
mode_str = case accmode
|
75
|
+
when Fcntl::O_RDWR; "rw"
|
76
|
+
when Fcntl::O_RDONLY; "r"
|
77
|
+
when Fcntl::O_WRONLY; "w"
|
78
|
+
end
|
79
|
+
|
80
|
+
mode_str << "a" if append?
|
81
|
+
return mode_str
|
82
|
+
end
|
443
83
|
end
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
84
|
+
|
85
|
+
# Implements RFuseFS
|
86
|
+
# The path supplied to these methods is generally validated by FUSE itself
|
87
|
+
# with a prior "getattr" call so we do not revalidate here.
|
88
|
+
# http://sourceforge.net/apps/mediawiki/fuse/index.php?title=FuseInvariants
|
89
|
+
class RFuseFS
|
90
|
+
CHECK_FILE="/._rfuse_check_"
|
91
|
+
|
92
|
+
def initialize(root)
|
93
|
+
@root = root
|
94
|
+
@created_files = { }
|
95
|
+
|
96
|
+
#Define method missing for our filesystem
|
97
|
+
#so we can just call all the API methods as required.
|
98
|
+
def @root.method_missing(method,*args)
|
99
|
+
# our filesystem might implement method_missing itself
|
100
|
+
super
|
101
|
+
rescue NoMethodError
|
102
|
+
DEFAULT_FS.send(method,*args)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def readdir(ctx,path,filler,offset,ffi)
|
107
|
+
|
108
|
+
return wrap_context(ctx,__method__,path,filler,offset,ffi) if ctx
|
109
|
+
|
110
|
+
#Always have "." and ".."
|
111
|
+
filler.push(".",nil,0)
|
112
|
+
filler.push("..",nil,0)
|
113
|
+
|
114
|
+
files = @root.contents(path)
|
115
|
+
|
116
|
+
files.each do | filename |
|
117
|
+
filler.push(filename,nil,0)
|
118
|
+
end
|
119
|
+
|
120
|
+
end
|
121
|
+
|
122
|
+
def getattr(ctx,path)
|
123
|
+
|
124
|
+
return wrap_context(ctx,__method__,path) if ctx
|
125
|
+
|
126
|
+
uid = Process.gid
|
127
|
+
gid = Process.uid
|
128
|
+
|
129
|
+
if path == "/" || @root.directory?(path)
|
130
|
+
#set "w" flag based on can_mkdir? || can_write? to path + "/._rfuse_check"
|
131
|
+
write_test_path = (path == "/" ? "" : path) + CHECK_FILE
|
132
|
+
|
133
|
+
mode = (@root.can_mkdir?(write_test_path) || @root.can_write?(write_test_path)) ? 0777 : 0555
|
134
|
+
atime,mtime,ctime = @root.times(path)
|
135
|
+
#nlink is set to 1 because apparently this makes find work.
|
136
|
+
return RFuse::Stat.directory(mode,{ :uid => uid, :gid => gid, :nlink => 1, :atime => atime, :mtime => mtime, :ctime => ctime })
|
137
|
+
elsif @created_files.has_key?(path)
|
138
|
+
now = Time.now.to_i
|
139
|
+
return RFuse::Stat.file(@created_files[path],{ :uid => uid, :gid => gid, :atime => now, :mtime => now, :ctime => now })
|
140
|
+
elsif @root.file?(path)
|
141
|
+
#Set mode from can_write and executable
|
142
|
+
mode = 0444
|
143
|
+
mode |= 0222 if @root.can_write?(path)
|
144
|
+
mode |= 0111 if @root.executable?(path)
|
145
|
+
size = @root.size(path)
|
146
|
+
atime,mtime,ctime = @root.times(path)
|
147
|
+
return RFuse::Stat.file(mode,{ :uid => uid, :gid => gid, :size => size, :atime => atime, :mtime => mtime, :ctime => ctime })
|
148
|
+
else
|
149
|
+
raise Errno::ENOENT.new(path)
|
150
|
+
end
|
151
|
+
|
152
|
+
end #getattr
|
153
|
+
|
154
|
+
def mkdir(ctx,path,mode)
|
155
|
+
|
156
|
+
return wrap_context(ctx,__method__,path,mode) if ctx
|
157
|
+
|
158
|
+
unless @root.can_mkdir?(path)
|
159
|
+
raise Errno::EACCES.new(path)
|
160
|
+
end
|
161
|
+
|
162
|
+
@root.mkdir(path)
|
163
|
+
end #mkdir
|
164
|
+
|
165
|
+
def mknod(ctx,path,mode,major,minor)
|
166
|
+
|
167
|
+
return wrap_context(ctx,__method__,path,mode,major,minor) if ctx
|
168
|
+
|
169
|
+
unless ((RFuse::Stat::S_IFMT & mode) == RFuse::Stat::S_IFREG ) && @root.can_write?(path)
|
170
|
+
raise Errno::EACCES.new(path)
|
171
|
+
end
|
172
|
+
|
173
|
+
@created_files[path] = mode
|
174
|
+
end #mknod
|
175
|
+
|
176
|
+
#ftruncate - eg called after opening a file for write without append
|
177
|
+
def ftruncate(ctx,path,offset,ffi)
|
178
|
+
|
179
|
+
return wrap_context(ctx,__method__,path,offset,ffi) if ctx
|
180
|
+
|
181
|
+
fh = ffi.fh
|
182
|
+
|
183
|
+
if fh.raw
|
184
|
+
@root.raw_truncate(path,offset,fh.raw)
|
185
|
+
elsif (offset <= 0)
|
186
|
+
fh.contents = ""
|
187
|
+
else
|
188
|
+
fh.contents = fh.contents[0..offset]
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
#truncate a file outside of open files
|
193
|
+
def truncate(ctx,path,offset)
|
194
|
+
return wrap_context(ctx,__method__,path,offset) if ctx
|
195
|
+
|
196
|
+
unless @root.can_write?(path)
|
197
|
+
raise Errno::EACESS.new(path)
|
198
|
+
end
|
199
|
+
|
200
|
+
unless @root.raw_truncate(path,offset)
|
201
|
+
contents = @root.read_file(path)
|
202
|
+
if (offset <= 0)
|
203
|
+
@root.write_to(path,"")
|
204
|
+
elsif offset < contents.length
|
205
|
+
@root.write_to(path,contents[0..offset] )
|
206
|
+
end
|
207
|
+
end
|
208
|
+
end #truncate
|
209
|
+
|
210
|
+
# Open. Create a FileHandler and store in fuse file info
|
211
|
+
# This will be returned to us in read/write
|
212
|
+
# No O_CREATE (mknod first?), no O_TRUNC (truncate first)
|
213
|
+
def open(ctx,path,ffi)
|
214
|
+
return wrap_context(ctx,__method__,path,ffi) if ctx
|
215
|
+
fh = FileHandle.new(path,ffi.flags)
|
216
|
+
|
217
|
+
#Save the value return from raw_open to be passed back in raw_read/write etc..
|
218
|
+
if (FuseFS::RFUSEFS_COMPATIBILITY)
|
219
|
+
fh.raw = @root.raw_open(path,fh.raw_mode,true)
|
220
|
+
else
|
221
|
+
fh.raw = @root.raw_open(path,fh.raw_mode)
|
222
|
+
end
|
223
|
+
|
224
|
+
unless fh.raw
|
225
|
+
|
226
|
+
if fh.rdonly?
|
227
|
+
fh.contents = @root.read_file(path)
|
228
|
+
elsif fh.rdwr? || fh.wronly?
|
229
|
+
unless @root.can_write?(path)
|
230
|
+
raise Errno::EACCES.new(path)
|
231
|
+
end
|
232
|
+
|
233
|
+
if @created_files.has_key?(path)
|
234
|
+
#we have an empty file
|
235
|
+
fh.contents = "";
|
236
|
+
else
|
237
|
+
if fh.rdwr? || fh.append?
|
238
|
+
fh.contents = @root.read_file(path)
|
239
|
+
else #wronly && !append
|
240
|
+
#We should get a truncate 0, but might as well play it safe
|
241
|
+
fh.contents = ""
|
242
|
+
end
|
243
|
+
end
|
244
|
+
else
|
245
|
+
raise Errno::ENOPERM.new(path)
|
246
|
+
end
|
247
|
+
end
|
248
|
+
#If we get this far, save our filehandle in the FUSE structure
|
249
|
+
ffi.fh=fh
|
250
|
+
|
251
|
+
end
|
252
|
+
|
253
|
+
def read(ctx,path,size,offset,ffi)
|
254
|
+
return wrap_context(ctx,__method__,path,size,offset,ffi) if ctx
|
255
|
+
|
256
|
+
fh = ffi.fh
|
257
|
+
|
258
|
+
if fh.raw
|
259
|
+
if FuseFS::RFUSEFS_COMPATIBILITY
|
260
|
+
return @root.raw_read(path,offset,size,fh.raw)
|
261
|
+
else
|
262
|
+
return @root.raw_read(path,offset,size)
|
263
|
+
end
|
264
|
+
elsif offset >= 0
|
265
|
+
return fh.read(offset,size)
|
266
|
+
else
|
267
|
+
#TODO: Raise? what does a negative offset mean
|
268
|
+
return ""
|
269
|
+
end
|
270
|
+
rescue EOFError
|
271
|
+
return ""
|
272
|
+
end
|
273
|
+
|
274
|
+
def write(ctx,path,buf,offset,ffi)
|
275
|
+
return wrap_context(ctx,__method__,path,buf,offset,ffi) if ctx
|
276
|
+
fh = ffi.fh
|
277
|
+
|
278
|
+
if fh.raw
|
279
|
+
if FuseFS::RFUSEFS_COMPATIBILITY
|
280
|
+
return @root.raw_write(path,offset,buf.length,buf,fh.raw)
|
281
|
+
else
|
282
|
+
@root.raw_write(path,offset,buf.length,buf)
|
283
|
+
return buf.length
|
284
|
+
end
|
285
|
+
else
|
286
|
+
return fh.write(offset,buf)
|
287
|
+
end
|
288
|
+
|
289
|
+
end
|
290
|
+
|
291
|
+
def flush(ctx,path,ffi)
|
292
|
+
return wrap_context(ctx,__method__,path,ffi) if ctx
|
293
|
+
fh = ffi.fh
|
294
|
+
|
295
|
+
if fh && !fh.raw && fh.modified?
|
296
|
+
#write contents to the file and mark it unmodified
|
297
|
+
@root.write_to(path,fh.flush())
|
298
|
+
#if it was created with mknod it now exists in the filesystem...
|
299
|
+
@created_files.delete(path)
|
300
|
+
end
|
301
|
+
|
302
|
+
end
|
303
|
+
|
304
|
+
def release(ctx,path,ffi)
|
305
|
+
return wrap_context(ctx,__method__,path,ffi) if ctx
|
306
|
+
|
307
|
+
flush(nil,path,ffi)
|
308
|
+
|
309
|
+
fh = ffi.fh
|
310
|
+
if fh && fh.raw
|
311
|
+
if (FuseFS::RFUSEFS_COMPATIBILITY)
|
312
|
+
@root.raw_close(path,fh.raw)
|
313
|
+
else
|
314
|
+
@root.raw_close(path)
|
315
|
+
end
|
316
|
+
end
|
317
|
+
|
318
|
+
end
|
319
|
+
|
320
|
+
#def chmod(path,mode)
|
321
|
+
#end
|
322
|
+
|
323
|
+
#def chown(path,uid,gid)
|
324
|
+
#end
|
325
|
+
|
326
|
+
def utime(ctx,path,actime,modtime)
|
327
|
+
return wrap_context(ctx,__method__,path,actime,modtime) if ctx
|
328
|
+
|
329
|
+
#Touch...
|
330
|
+
if @root.respond_to?(:touch)
|
331
|
+
@root.touch(path,modtime)
|
332
|
+
end
|
333
|
+
end
|
334
|
+
|
335
|
+
def unlink(ctx,path)
|
336
|
+
return wrap_context(ctx,__method__,path) if ctx
|
337
|
+
|
338
|
+
unless @root.can_delete?(path)
|
339
|
+
raise Errno::EACCES.new(path)
|
340
|
+
end
|
341
|
+
@created_files.delete(path)
|
342
|
+
@root.delete(path)
|
343
|
+
end
|
344
|
+
|
345
|
+
def rmdir(ctx,path)
|
346
|
+
return wrap_context(ctx,__method__,path) if ctx
|
347
|
+
|
348
|
+
unless @root.can_rmdir?(path)
|
349
|
+
raise Errno::EACCES.new(path)
|
350
|
+
end
|
351
|
+
@root.rmdir(path)
|
352
|
+
end
|
353
|
+
|
354
|
+
#def symlink(path,as)
|
355
|
+
#end
|
356
|
+
|
357
|
+
def rename(ctx,from,to)
|
358
|
+
return wrap_context(ctx,__method__,from,to) if ctx
|
359
|
+
|
360
|
+
if @root.rename(from,to)
|
361
|
+
# nothing to do
|
362
|
+
elsif @root.file?(from) && @root.can_write?(to) && @root.can_delete?(from)
|
363
|
+
contents = @root.read_file(from)
|
364
|
+
@root.write_to(to,contents)
|
365
|
+
@root.delete(from)
|
366
|
+
else
|
367
|
+
raise Errno::EACCES.new("Unable to move directory #{from}")
|
368
|
+
end
|
369
|
+
end
|
370
|
+
|
371
|
+
#def link(path,as)
|
372
|
+
#end
|
373
|
+
|
374
|
+
# def setxattr(path,name,value,size,flags)
|
375
|
+
# end
|
376
|
+
|
377
|
+
# def getxattr(path,name,size)
|
378
|
+
# end
|
379
|
+
|
380
|
+
# def listxattr(path,size)
|
381
|
+
# end
|
382
|
+
|
383
|
+
# def removexattr(path,name)
|
384
|
+
# end
|
385
|
+
|
386
|
+
#def opendir(path,ffi)
|
387
|
+
#end
|
388
|
+
|
389
|
+
#def releasedir(path,ffi)
|
390
|
+
#end
|
391
|
+
|
392
|
+
#def fsyncdir(path,meta,ffi)
|
393
|
+
#end
|
394
|
+
|
395
|
+
# Some random numbers to show with df command
|
396
|
+
#def statfs(path)
|
397
|
+
#end
|
398
|
+
|
399
|
+
def self.context(ctx,&block)
|
400
|
+
begin
|
401
|
+
Thread.current[:fusefs_reader_uid] = ctx.uid
|
402
|
+
Thread.current[:fusefs_reader_gid] = ctx.gid
|
403
|
+
yield
|
404
|
+
ensure
|
405
|
+
Thread.current[:fusefs_reader_uid] = nil
|
406
|
+
Thread.current[:fusefs_reader_gid] = nil
|
407
|
+
end
|
450
408
|
end
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
if @root.rename(from,to)
|
457
|
-
# nothing to do
|
458
|
-
elsif @root.file?(from) && @root.can_write?(to) && @root.can_delete?(from)
|
459
|
-
contents = @root.read_file(from)
|
460
|
-
@root.write_to(to,contents)
|
461
|
-
@root.delete(from)
|
462
|
-
else
|
463
|
-
raise Errno::EACCES.new("Unable to move directory #{from}")
|
464
|
-
end
|
409
|
+
|
410
|
+
private
|
411
|
+
|
412
|
+
def wrap_context(ctx,method,*args)
|
413
|
+
self.class.context(ctx) { send(method,nil,*args) }
|
465
414
|
end
|
466
|
-
|
467
|
-
#
|
468
|
-
#end
|
469
|
-
|
470
|
-
# def setxattr(path,name,value,size,flags)
|
471
|
-
# end
|
472
|
-
|
473
|
-
# def getxattr(path,name,size)
|
474
|
-
# end
|
475
|
-
|
476
|
-
# def listxattr(path,size)
|
477
|
-
# end
|
478
|
-
|
479
|
-
# def removexattr(path,name)
|
480
|
-
# end
|
481
|
-
|
482
|
-
#def opendir(path,ffi)
|
483
|
-
#end
|
484
|
-
|
485
|
-
#def releasedir(path,ffi)
|
486
|
-
#end
|
487
|
-
|
488
|
-
#def fsyncdir(path,meta,ffi)
|
489
|
-
#end
|
490
|
-
|
491
|
-
# Some random numbers to show with df command
|
492
|
-
#def statfs(path)
|
493
|
-
#end
|
494
|
-
|
495
|
-
end #class RFuseFSAPI
|
496
|
-
|
415
|
+
|
416
|
+
end #class RFuseFS
|
497
417
|
end #Module FuseFS
|