rfusefs 0.8.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.
- data/.gemtest +0 -0
- data/History.txt +9 -0
- data/README.rdoc +108 -0
- data/Rakefile +25 -0
- data/TODO.txt +7 -0
- data/lib/fuse/rfusefs-fuse.rb +497 -0
- data/lib/fusefs/dirlink.rb +46 -0
- data/lib/fusefs/metadir.rb +244 -0
- data/lib/fusefs/pathmapper.rb +193 -0
- data/lib/fusefs.rb +8 -0
- data/lib/rfusefs.rb +318 -0
- data/samples/demo.rb +64 -0
- data/samples/dictfs.rb +84 -0
- data/samples/hello.rb +24 -0
- data/samples/openurifs.rb +53 -0
- data/samples/railsfs.rb +77 -0
- data/samples/sqlfs.rb +134 -0
- data/samples/yamlfs.rb +168 -0
- data/spec/rfusefs_spec.rb +400 -0
- data/spec/sample_spec.rb +29 -0
- data/spec/spec_helper.rb +41 -0
- data/spec-fusefs/fusefs_spec.rb +12 -0
- data.tar.gz.sig +0 -0
- metadata +166 -0
- metadata.gz.sig +1 -0
data/.gemtest
ADDED
File without changes
|
data/History.txt
ADDED
data/README.rdoc
ADDED
@@ -0,0 +1,108 @@
|
|
1
|
+
= rfusefs
|
2
|
+
|
3
|
+
* https://github.com/lwoggardner/rfusefs
|
4
|
+
|
5
|
+
== DESCRIPTION
|
6
|
+
|
7
|
+
RFuseFS is a port of the FuseFS[http://rubyforge.org/projects/fusefs]
|
8
|
+
library aimed at allowing Ruby programmers to quickly and easily create
|
9
|
+
virtual filesystems with little more than a few lines of code.
|
10
|
+
|
11
|
+
RFuseFS is api compatible with {FuseFS (0.7.0)}[https://github.com/duairc/fusefs]
|
12
|
+
|
13
|
+
== SYNOPSIS
|
14
|
+
|
15
|
+
FuseFS provides a layer of abstraction to a programmer who wants to create a
|
16
|
+
virtual filesystem via FUSE.
|
17
|
+
|
18
|
+
FuseFS programs consist of two parts:
|
19
|
+
|
20
|
+
1. FuseFS, which is defined in 'rfusefs.rb'
|
21
|
+
2. An object that defines a virtual directory by implementing some
|
22
|
+
subset of the {FuseFS::API}
|
23
|
+
|
24
|
+
To write a FuseFS program, you must:
|
25
|
+
|
26
|
+
* Define and create a Directory object that responds to the methods required
|
27
|
+
by FuseFS for its desired use.
|
28
|
+
|
29
|
+
* Call FuseFS.start <mountpoint> <virtualdir>
|
30
|
+
where <mountpoint> is a real directory on your filesystem
|
31
|
+
and <virtualdir> with an object defining your virtual directory.
|
32
|
+
|
33
|
+
See samples under /samples and also the following starter classes
|
34
|
+
|
35
|
+
* {FuseFS::MetaDir}
|
36
|
+
* {FuseFS::DirLink}
|
37
|
+
* {FuseFS::PathMapperFS}
|
38
|
+
|
39
|
+
To use the filesystem open up your favourite file browser/terminal and
|
40
|
+
explore the contents under <mountpoint>
|
41
|
+
|
42
|
+
Happy Filesystem Hacking!
|
43
|
+
|
44
|
+
=== the hello world filesystem in 14 LOC
|
45
|
+
|
46
|
+
require 'rfusefs'
|
47
|
+
|
48
|
+
class HelloDir
|
49
|
+
def contents(path)
|
50
|
+
['hello.txt']
|
51
|
+
end
|
52
|
+
def file?(path)
|
53
|
+
path == '/hello.txt'
|
54
|
+
end
|
55
|
+
def read_file(path)
|
56
|
+
"Hello, World!\n"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
hellodir = HelloDir.new
|
61
|
+
|
62
|
+
FuseFS.start(ARGV.shift,hellodir)
|
63
|
+
|
64
|
+
== REQUIREMENTS:
|
65
|
+
|
66
|
+
* FUSE (http://fuse.sourceforge.org)
|
67
|
+
* Ruby (>=1.8)
|
68
|
+
* rfuse_ng (>=0.5.3)
|
69
|
+
|
70
|
+
== INSTALL:
|
71
|
+
|
72
|
+
* gem install rfusefs
|
73
|
+
|
74
|
+
== DEVELOPERS:
|
75
|
+
|
76
|
+
After checking out the source, run:
|
77
|
+
|
78
|
+
$ rake newb
|
79
|
+
|
80
|
+
This task will install any missing dependencies, run the tests/specs,
|
81
|
+
and generate the RDoc.
|
82
|
+
|
83
|
+
== LICENSE:
|
84
|
+
|
85
|
+
(The MIT License)
|
86
|
+
|
87
|
+
* Copyright (c) 2005 Greg Millam. (FuseFS)
|
88
|
+
* Copyright (c) 2009 Kyle Maxwell. (FuseFS)
|
89
|
+
* Copyright (c) 2011 Grant Gardner. (RFuseFS)
|
90
|
+
|
91
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
92
|
+
a copy of this software and associated documentation files (the
|
93
|
+
'Software'), to deal in the Software without restriction, including
|
94
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
95
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
96
|
+
permit persons to whom the Software is furnished to do so, subject to
|
97
|
+
the following conditions:
|
98
|
+
|
99
|
+
The above copyright notice and this permission notice shall be
|
100
|
+
included in all copies or substantial portions of the Software.
|
101
|
+
|
102
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
103
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
104
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
105
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
106
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
107
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
108
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'hoe'
|
5
|
+
|
6
|
+
# Hoe.plugin :compiler
|
7
|
+
# Hoe.plugin :gem_prelude_sucks
|
8
|
+
# Hoe.plugin :inline
|
9
|
+
# Hoe.plugin :racc
|
10
|
+
# Hoe.plugin :rubyforge
|
11
|
+
Hoe.plugin :yard
|
12
|
+
Hoe.plugin :git
|
13
|
+
|
14
|
+
Hoe.spec 'rfusefs' do
|
15
|
+
self.readme_file="README.rdoc"
|
16
|
+
developer('Grant Gardner', 'grant@lastweekend.com.au')
|
17
|
+
extra_deps << [ 'rfuse-ng' , '>= 0.5.3' ]
|
18
|
+
end
|
19
|
+
|
20
|
+
#We need to run the fusefs compatibility specs separately
|
21
|
+
RSpec::Core::RakeTask.new(:spec) do |t|
|
22
|
+
t.pattern = 'spec-fusefs/**/*_spec.rb'
|
23
|
+
end
|
24
|
+
|
25
|
+
# vim: syntax=ruby
|
data/TODO.txt
ADDED
@@ -0,0 +1,497 @@
|
|
1
|
+
# RFuseFS - FuseFS over RFuse
|
2
|
+
require 'rfuse_ng'
|
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
|
+
|
10
|
+
# File/Directory attributes
|
11
|
+
class Stat
|
12
|
+
S_IFMT = 0170000 # Format mask
|
13
|
+
S_IFDIR = 0040000 # Directory.
|
14
|
+
S_IFCHR = 0020000 # Character device.
|
15
|
+
S_IFBLK = 0060000 # Block device.
|
16
|
+
S_IFREG = 0100000 # Regular file.
|
17
|
+
S_IFIFO = 0010000 # FIFO.
|
18
|
+
S_IFLNK = 0120000 # Symbolic link.
|
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
|
225
|
+
end
|
226
|
+
|
227
|
+
end
|
228
|
+
end
|
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] )
|
316
|
+
end
|
317
|
+
end
|
318
|
+
end #truncate
|
319
|
+
|
320
|
+
# Open. Create a FileHandler and store in fuse file info
|
321
|
+
# This will be returned to us in read/write
|
322
|
+
# No O_CREATE (mknod first?), no O_TRUNC (truncate first)
|
323
|
+
def open(path,ffi)
|
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 = ""
|
351
|
+
end
|
352
|
+
end
|
353
|
+
else
|
354
|
+
raise Errno::ENOPERM.new(path)
|
355
|
+
end
|
356
|
+
end
|
357
|
+
#If we get this far, save our filehandle in the FUSE structure
|
358
|
+
ffi.fh=fh
|
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)
|
370
|
+
end
|
371
|
+
elsif offset >= 0
|
372
|
+
return fh.read(offset,size)
|
373
|
+
else
|
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
|
390
|
+
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
|
+
|
409
|
+
def release(path,ffi)
|
410
|
+
|
411
|
+
flush(path,ffi)
|
412
|
+
|
413
|
+
fh = ffi.fh
|
414
|
+
if fh.raw
|
415
|
+
if (FuseFS::RFUSEFS_COMPATIBILITY)
|
416
|
+
@root.raw_close(path,fh.raw)
|
417
|
+
else
|
418
|
+
@root.raw_close(path)
|
419
|
+
end
|
420
|
+
end
|
421
|
+
|
422
|
+
end
|
423
|
+
|
424
|
+
#def chmod(path,mode)
|
425
|
+
#end
|
426
|
+
|
427
|
+
#def chown(path,uid,gid)
|
428
|
+
#end
|
429
|
+
|
430
|
+
def utime(path,actime,modtime)
|
431
|
+
#Touch...
|
432
|
+
if @root.respond_to?(:touch)
|
433
|
+
@root.touch(path,modtime)
|
434
|
+
end
|
435
|
+
end
|
436
|
+
|
437
|
+
def unlink(path)
|
438
|
+
unless @root.can_delete?(path)
|
439
|
+
raise Errno::EACCES.new(path)
|
440
|
+
end
|
441
|
+
@created_files.delete(path)
|
442
|
+
@root.delete(path)
|
443
|
+
end
|
444
|
+
|
445
|
+
def rmdir(path)
|
446
|
+
unless @root.can_rmdir?(path)
|
447
|
+
raise Errno::EACCES.new(path)
|
448
|
+
end
|
449
|
+
@root.rmdir(path)
|
450
|
+
end
|
451
|
+
|
452
|
+
#def symlink(path,as)
|
453
|
+
#end
|
454
|
+
|
455
|
+
def rename(from,to)
|
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
|
465
|
+
end
|
466
|
+
|
467
|
+
#def link(path,as)
|
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
|
+
|
497
|
+
end #Module FuseFS
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module FuseFS
|
2
|
+
|
3
|
+
# A FuseFS over an existing directory
|
4
|
+
class DirLink
|
5
|
+
|
6
|
+
def initialize(dir)
|
7
|
+
File.directory?(dir) or raise ArgumentError, "DirLink.initialize expects a valid directory!"
|
8
|
+
@base = dir
|
9
|
+
end
|
10
|
+
|
11
|
+
def directory?(path)
|
12
|
+
File.directory?(File.join(@base,path))
|
13
|
+
end
|
14
|
+
|
15
|
+
def file?(path)
|
16
|
+
File.file?(File.join(@base,path))
|
17
|
+
end
|
18
|
+
|
19
|
+
def size(path)
|
20
|
+
File.size(File.join(@base,path))
|
21
|
+
end
|
22
|
+
|
23
|
+
def contents(path)
|
24
|
+
fn = File.join(@base,path)
|
25
|
+
Dir.entries(fn).map { |file|
|
26
|
+
file = file.sub(/^#{fn}\/?/,'')
|
27
|
+
if ['..','.'].include?(file)
|
28
|
+
nil
|
29
|
+
else
|
30
|
+
file
|
31
|
+
end
|
32
|
+
}.compact.sort
|
33
|
+
end
|
34
|
+
|
35
|
+
def read_file(path)
|
36
|
+
fn = File.join(@base,path)
|
37
|
+
if File.file?(fn)
|
38
|
+
IO.read(fn)
|
39
|
+
else
|
40
|
+
'No such file'
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|