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/fusefs/dirlink.rb
CHANGED
data/lib/fusefs/metadir.rb
CHANGED
@@ -1,244 +1,244 @@
|
|
1
1
|
module FuseFS
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
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
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
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
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
2
|
+
|
3
|
+
# A full in-memory filesystem defined with hashes. It is writable to the
|
4
|
+
# user that mounted it
|
5
|
+
# may create and edit files within it, as well as the programmer
|
6
|
+
# === Usage
|
7
|
+
# root = Metadir.new()
|
8
|
+
# root.mkdir("/hello")
|
9
|
+
# root.write_to("/hello/world","Hello World!\n")
|
10
|
+
# root.write_to("/hello/everybody","Hello Everyone!\n")
|
11
|
+
#
|
12
|
+
# FuseFS.start(mntpath,root)
|
13
|
+
#
|
14
|
+
# Because Metadir is fully recursive, you can mount your own or other defined
|
15
|
+
# directory structures under it. For example, to mount a dictionary filesystem
|
16
|
+
# (see samples/dictfs.rb), use:
|
17
|
+
#
|
18
|
+
# root.mkdir("/dict",DictFS.new())
|
19
|
+
#
|
20
|
+
class MetaDir
|
21
|
+
|
22
|
+
DEFAULT_FS = FuseDir.new()
|
23
|
+
|
24
|
+
def initialize()
|
25
|
+
@subdirs = Hash.new(nil)
|
26
|
+
@files = Hash.new(nil)
|
27
|
+
end
|
28
|
+
|
29
|
+
def split_path(path)
|
30
|
+
DEFAULT_FS.split_path(path)
|
31
|
+
end
|
32
|
+
|
33
|
+
def scan_path
|
34
|
+
DEFAULT_FS.scan_path(path)
|
35
|
+
end
|
36
|
+
|
37
|
+
def directory?(path)
|
38
|
+
pathmethod(:directory?,path) do |filename|
|
39
|
+
!filename || filename == "/" || @subdirs.has_key?(filename)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def file?(path)
|
44
|
+
pathmethod(:file?,path) do |filename|
|
45
|
+
@files.has_key?(filename)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
#List directory contents
|
50
|
+
def contents(path)
|
51
|
+
pathmethod(:contents,path) do | filename |
|
52
|
+
if !filename
|
53
|
+
(@files.keys + @subdirs.keys).sort.uniq
|
54
|
+
else
|
55
|
+
@subdirs[filename].contents("/")
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def read_file(path)
|
61
|
+
pathmethod(:read_file,path) do |filename|
|
62
|
+
@files[filename].to_s
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def size(path)
|
67
|
+
pathmethod(:size,path) do | filename |
|
68
|
+
return @files[filename].to_s.length
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
#can_write only applies to files... see can_mkdir for directories...
|
73
|
+
def can_write?(path)
|
74
|
+
pathmethod(:can_write?,path) do |filename|
|
75
|
+
return mount_user?
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def write_to(path,contents)
|
80
|
+
pathmethod(:write_to,path,contents) do |filename, filecontents |
|
81
|
+
@files[filename] = filecontents
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
# Delete a file
|
86
|
+
def can_delete?(path)
|
87
|
+
pathmethod(:can_delete?,path) do |filename|
|
88
|
+
return mount_user?
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def delete(path)
|
93
|
+
pathmethod(:delete,path) do |filename|
|
94
|
+
@files.delete(filename)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
#mkdir - does not make intermediate dirs!
|
99
|
+
def can_mkdir?(path)
|
100
|
+
pathmethod(:can_mkdir?,path) do |dirname|
|
101
|
+
return mount_user?
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def mkdir(path,dir=nil)
|
106
|
+
pathmethod(:mkdir,path,dir) do | dirname,dirobj |
|
107
|
+
dirobj ||= MetaDir.new
|
108
|
+
@subdirs[dirname] = dirobj
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
# Delete an existing directory make sure it is not empty
|
113
|
+
def can_rmdir?(path)
|
114
|
+
pathmethod(:can_rmdir?,path) do |dirname|
|
115
|
+
return mount_user? && @subdirs.has_key?(dirname) && @subdirs[dirname].contents("/").empty?
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
def rmdir(path)
|
120
|
+
pathmethod(:rmdir,path) do |dirname|
|
121
|
+
@subdirs.delete(dirname)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def rename(from_path,to_path,to_fusefs = self)
|
126
|
+
|
127
|
+
from_base,from_rest = split_path(from_path)
|
128
|
+
|
129
|
+
case
|
130
|
+
when !from_base
|
131
|
+
# Shouldn't ever happen.
|
132
|
+
raise Errno::EACCES.new("Can't move root")
|
133
|
+
when !from_rest
|
134
|
+
# So now we have a file or directory to move
|
135
|
+
if @files.has_key?(from_base)
|
136
|
+
return false unless can_delete?(from_base) && to_fusefs.can_write?(to_path)
|
137
|
+
to_fusefs.write_to(to_path,@files[from_base])
|
138
|
+
@files.delete(from_base)
|
139
|
+
elsif @subdirs.has_key?(from_base)
|
140
|
+
# we don't check can_rmdir? because that would prevent us
|
141
|
+
# moving non empty directories
|
142
|
+
return false unless mount_user? && to_fusefs.can_mkdir?(to_path)
|
143
|
+
begin
|
144
|
+
to_fusefs.mkdir(to_path,@subdirs[from_base])
|
145
|
+
@subdirs.delete(from_base)
|
146
|
+
rescue ArgumentError
|
147
|
+
# to_rest does not support mkdir with an arbitrary object
|
148
|
+
return false
|
149
|
+
end
|
150
|
+
else
|
151
|
+
#We shouldn't get this either
|
152
|
+
return false
|
153
|
+
end
|
154
|
+
when @subdirs.has_key?(from_base)
|
155
|
+
begin
|
156
|
+
if to_fusefs != self
|
157
|
+
#just keep recursing..
|
158
|
+
return @subdirs[from_base].rename(from_rest,to_path,to_fusefs)
|
159
|
+
else
|
160
|
+
to_base,to_rest = split_path(to_path)
|
161
|
+
if from_base == to_base
|
162
|
+
#mv within a subdir, just pass it on
|
163
|
+
return @subdirs[from_base].rename(from_rest,to_rest)
|
164
|
+
else
|
165
|
+
#OK, this is the tricky part, we want to move something further down
|
166
|
+
#our tree into something in another part of the tree.
|
167
|
+
#from this point on we keep a reference to the fusefs that owns
|
168
|
+
#to_path (ie us) and pass it down, but only if the eventual path
|
169
|
+
#is writable anyway!
|
170
|
+
if (file?(to_path))
|
171
|
+
return false unless can_write?(to_path)
|
172
|
+
else
|
173
|
+
return false unless can_mkdir?(to_path)
|
174
|
+
end
|
175
|
+
|
176
|
+
return @subdirs[from_base].rename(from_rest,to_path,self)
|
177
|
+
end
|
178
|
+
end
|
179
|
+
rescue NoMethodError
|
180
|
+
#sub dir doesn't support rename
|
181
|
+
return false
|
182
|
+
rescue ArgumentError
|
183
|
+
#sub dir doesn't support rename with additional to_fusefs argument
|
184
|
+
return false
|
185
|
+
end
|
160
186
|
else
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
#base is the filename, no more directories to traverse
|
218
|
-
yield(base,*args)
|
219
|
-
when @subdirs.has_key?(base)
|
220
|
-
#base is a subdirectory, pass it on if we can
|
221
|
-
begin
|
222
|
-
@subdirs[base].send(method,rest,*args)
|
223
|
-
rescue NoMethodError
|
224
|
-
#Oh well
|
225
|
-
return RFuseFSAPI::API_METHODS[method]
|
226
|
-
rescue ArgumentError
|
227
|
-
#can_mkdir,mkdir
|
228
|
-
if args.pop.nil?
|
229
|
-
#possibly a default arg, try sending again with one fewer arg
|
230
|
-
@subdirs[base].send(method,rest,*args)
|
231
|
-
else
|
232
|
-
#definitely not a default arg, reraise
|
233
|
-
Kernel.raise
|
234
|
-
end
|
235
|
-
end
|
236
|
-
else
|
237
|
-
#return the default response
|
238
|
-
return RFuseFSAPI::API_METHODS[method]
|
239
|
-
end
|
187
|
+
return false
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
default_methods = FuseDir.public_instance_methods.select { |m|
|
192
|
+
!self.public_method_defined?(m) && FuseDir.instance_method(m).owner == FuseDir
|
193
|
+
}
|
194
|
+
|
195
|
+
default_methods.each do |m|
|
196
|
+
define_method(m) do |*args|
|
197
|
+
pathmethod(m,*args) { |*args| DEFAULT_FS.send(m,*args) }
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
private
|
202
|
+
# is the accessing user the same as the user that mounted our FS?, used for
|
203
|
+
# all write activity
|
204
|
+
def mount_user?
|
205
|
+
return Process.uid == FuseFS.reader_uid
|
206
|
+
end
|
207
|
+
|
208
|
+
#All our FuseFS methods follow the same pattern...
|
209
|
+
def pathmethod(method, path,*args)
|
210
|
+
base,rest = split_path(path)
|
211
|
+
|
212
|
+
case
|
213
|
+
when ! base
|
214
|
+
#request for the root of our fs
|
215
|
+
yield(nil,*args)
|
216
|
+
when ! rest
|
217
|
+
#base is the filename, no more directories to traverse
|
218
|
+
yield(base,*args)
|
219
|
+
when @subdirs.has_key?(base)
|
220
|
+
#base is a subdirectory, pass it on if we can
|
221
|
+
begin
|
222
|
+
@subdirs[base].send(method,rest,*args)
|
223
|
+
rescue NoMethodError
|
224
|
+
#Oh well
|
225
|
+
return DEFAULT_FS.send(method,rest,*args)
|
226
|
+
rescue ArgumentError
|
227
|
+
#can_mkdir,mkdir
|
228
|
+
if args.pop.nil?
|
229
|
+
#possibly a default arg, try sending again with one fewer arg
|
230
|
+
@subdirs[base].send(method,rest,*args)
|
231
|
+
else
|
232
|
+
#definitely not a default arg, reraise
|
233
|
+
Kernel.raise
|
234
|
+
end
|
235
|
+
end
|
236
|
+
else
|
237
|
+
#return the default response
|
238
|
+
return DEFAULT_FS.send(method,path,*args)
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
|
240
243
|
end
|
241
|
-
|
242
|
-
|
243
|
-
end
|
244
244
|
end
|