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