rfusefs 1.0.2.RC0 → 1.0.2.RC1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +5 -4
- data/Rakefile +3 -0
- data/lib/fuse/fusedir.rb +106 -22
- data/lib/fuse/rfusefs-fuse.rb +98 -27
- data/lib/fusefs/metadir.rb +46 -4
- data/lib/fusefs/pathmapper.rb +121 -20
- data/lib/rfusefs/version.rb +1 -1
- data/lib/rfusefs.rb +6 -3
- data/rfusefs.gemspec +3 -1
- data/samples/demo.rb +4 -6
- data/samples/hello.rb +1 -4
- data/spec/metadir_spec.rb +69 -0
- data/spec/pathmapper_spec.rb +276 -61
- data/spec/rfusefs_spec.rb +73 -17
- metadata +36 -4
data/README.rdoc
CHANGED
@@ -36,23 +36,24 @@ explore the contents under <mountpoint>
|
|
36
36
|
|
37
37
|
Happy Filesystem Hacking!
|
38
38
|
|
39
|
-
=== the hello world filesystem in
|
39
|
+
=== the hello world filesystem in 14 LOC
|
40
40
|
|
41
41
|
require 'rfusefs'
|
42
42
|
|
43
43
|
class HelloDir
|
44
|
+
|
44
45
|
def contents(path)
|
45
46
|
['hello.txt']
|
46
47
|
end
|
48
|
+
|
47
49
|
def file?(path)
|
48
50
|
path == '/hello.txt'
|
49
51
|
end
|
52
|
+
|
50
53
|
def read_file(path)
|
51
54
|
"Hello, World!\n"
|
52
55
|
end
|
53
|
-
|
54
|
-
read_file(path).size
|
55
|
-
end
|
56
|
+
|
56
57
|
end
|
57
58
|
|
58
59
|
# Usage: #{$0} mountpoint [mount_options]
|
data/Rakefile
CHANGED
data/lib/fuse/fusedir.rb
CHANGED
@@ -1,11 +1,68 @@
|
|
1
1
|
module FuseFS
|
2
2
|
|
3
|
+
# Helper for filesystem accounting
|
4
|
+
class StatsHelper
|
5
|
+
|
6
|
+
# @return [Integer] size of filesystem in bytes
|
7
|
+
attr_accessor :max_space
|
8
|
+
# @return [Integer] maximum number of (virtual) inodes
|
9
|
+
attr_accessor :max_nodes
|
10
|
+
|
11
|
+
# If set true, adjustments that cause space/nodes to exceed
|
12
|
+
# the maximums will raise ENOSPC (no space left on device)
|
13
|
+
# @return [Boolean]
|
14
|
+
attr_accessor :strict
|
15
|
+
|
16
|
+
# @return [Integer] used space in bytes
|
17
|
+
attr_reader :space
|
18
|
+
|
19
|
+
# @return [Integer] used inodes (typically count of files and directories)
|
20
|
+
attr_reader :nodes
|
21
|
+
|
22
|
+
#
|
23
|
+
# @param [Integer] max_space
|
24
|
+
# @param [Integer] max_nodes
|
25
|
+
# @param [Booleanr] strict
|
26
|
+
def initialize(max_space=nil,max_nodes=nil,strict=false)
|
27
|
+
@nodes = 0
|
28
|
+
@space = 0
|
29
|
+
@max_space = max_space
|
30
|
+
@max_nodes = max_nodes
|
31
|
+
@strict = strict
|
32
|
+
end
|
33
|
+
|
34
|
+
# Adjust accumlated statistics
|
35
|
+
# @param [Integer] delta_space change in {#space} usage
|
36
|
+
# @param [Integer] delta_nodes change in {#nodes} usage
|
37
|
+
#
|
38
|
+
# @return [void]
|
39
|
+
# @raise [Errno::ENOSPC] if {#strict} and adjusted {#space}/{#nodes} would exceed {#max_space} or {#max_nodes}
|
40
|
+
def adjust(delta_space,delta_nodes=0)
|
41
|
+
@nodes += delta_nodes
|
42
|
+
@space += delta_space
|
43
|
+
raise Errno::ENOSPC if @strict && ( @nodes > @max_nodes || @space > @max_space )
|
44
|
+
end
|
45
|
+
|
46
|
+
# @overload to_statistics()
|
47
|
+
# @return [Array<Integer>] in format expected by {FuseDir#statistics}
|
48
|
+
# @overload to_statistics(free_space,free_nodes)
|
49
|
+
# Calculate total space so that free space remains fixed
|
50
|
+
# @param [Integer] free_space available space in bytes
|
51
|
+
# @param [Integer] free_nodes available (virtual) inodes
|
52
|
+
# @return [Array<Integer>] in format expected by {FuseDir#statistics}
|
53
|
+
def to_statistics(free_space=nil,free_nodes=nil)
|
54
|
+
total_space = free_space ? space + free_space : max_space
|
55
|
+
total_nodes = free_nodes ? nodes + free_nodes : max_nodes
|
56
|
+
[ @space, @nodes, total_space, total_nodes ]
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
3
60
|
# This class is equivalent to using Object.new() as the virtual directory
|
4
61
|
# for target for {FuseFS.start}. It exists primarily to document the API
|
5
|
-
# but can also be used as a superclass for your filesystem
|
6
|
-
#
|
62
|
+
# but can also be used as a superclass for your filesystem providing sensible defaults
|
63
|
+
#
|
7
64
|
# == Method call sequences
|
8
|
-
#
|
65
|
+
#
|
9
66
|
# === Stat (getattr)
|
10
67
|
#
|
11
68
|
# FUSE itself will generally stat referenced files and validate the results
|
@@ -38,10 +95,10 @@ module FuseFS
|
|
38
95
|
# FUSE confirms path for the new file is a directory
|
39
96
|
#
|
40
97
|
# * {#can_write?} is checked at file open
|
41
|
-
# * {#write_to} is called when the file is flushed or closed
|
98
|
+
# * {#write_to} is called when the file is synced, flushed or closed
|
99
|
+
#
|
100
|
+
# See also {#raw_open}, {#raw_truncate}, {#raw_write}, {#raw_sync}, {#raw_close}
|
42
101
|
#
|
43
|
-
# See also {#raw_open}, {#raw_truncate}, {#raw_write}, {#raw_close}
|
44
|
-
#
|
45
102
|
# === Deleting files
|
46
103
|
#
|
47
104
|
# FUSE confirms path is a file before we call {#can_delete?} then {#delete}
|
@@ -105,12 +162,12 @@ module FuseFS
|
|
105
162
|
|
106
163
|
# File size
|
107
164
|
# @abstract FuseFS api
|
108
|
-
# @return [
|
109
|
-
def size(path);
|
165
|
+
# @return [Integer] the size in byte of a file (lots of applications rely on this being accurate )
|
166
|
+
def size(path); read_file(path).length ;end
|
110
167
|
|
111
168
|
# File time information. RFuseFS extension.
|
112
169
|
# @abstract FuseFS api
|
113
|
-
# @return [Array<
|
170
|
+
# @return [Array<Integer, Time>] a 3 element array [ atime, mtime. ctime ] (good for rsync etc)
|
114
171
|
def times(path);return INIT_TIMES;end
|
115
172
|
|
116
173
|
# @abstract FuseFS api
|
@@ -176,21 +233,29 @@ module FuseFS
|
|
176
233
|
def raw_open(path,mode,rfusefs = nil);end
|
177
234
|
|
178
235
|
# RFuseFS extension.
|
236
|
+
# @abstract FuseFS api
|
179
237
|
#
|
180
|
-
#
|
181
|
-
#
|
238
|
+
# @overload raw_truncate(path,offset,raw)
|
239
|
+
# Truncate an open file to offset bytes
|
240
|
+
# @param [String] path
|
241
|
+
# @param [Integer] offset
|
242
|
+
# @param [Object] raw the filehandle returned from {#raw_open}
|
243
|
+
# @return [void]
|
182
244
|
#
|
183
|
-
#
|
184
|
-
#
|
185
|
-
#
|
186
|
-
#
|
187
|
-
|
245
|
+
# @overload raw_truncate(path,offset)
|
246
|
+
# Optionally truncate a file to offset bytes directly
|
247
|
+
# @param [String] path
|
248
|
+
# @param [Integer] offset
|
249
|
+
# @return [Boolean]
|
250
|
+
# if truncate has been performed, otherwise the truncation will be performed with {#read_file} and {#write_to}
|
251
|
+
#
|
252
|
+
def raw_truncate(path,offset,raw=nil);end
|
188
253
|
|
189
254
|
# Read _sz_ bytes from file at path (or filehandle raw) starting at offset off
|
190
|
-
#
|
255
|
+
#
|
191
256
|
# @param [String] path
|
192
|
-
# @param [
|
193
|
-
# @param [
|
257
|
+
# @param [Integer] offset
|
258
|
+
# @param [Integer] size
|
194
259
|
# @param [Object] raw the filehandle returned by {#raw_open}
|
195
260
|
# @abstract FuseFS api
|
196
261
|
# @return [void]
|
@@ -201,17 +266,36 @@ module FuseFS
|
|
201
266
|
# @return [void]
|
202
267
|
def raw_write(path,off,sz,buf,raw=nil);end
|
203
268
|
|
269
|
+
|
270
|
+
# Sync buffered data to your filesystem
|
271
|
+
# @param [String] path
|
272
|
+
# @param [Boolena] datasync only sync user data, not metadata
|
273
|
+
# @param [Object] raw the filehandle return by {#raw_open}
|
274
|
+
def raw_sync(path,datasync,raw=nil);end
|
275
|
+
|
204
276
|
# Close the file previously opened at path (or filehandle raw)
|
205
277
|
# @abstract FuseFS api
|
206
278
|
# @return [void]
|
207
279
|
def raw_close(path,raw=nil);end
|
208
280
|
|
209
281
|
# RFuseFS extension.
|
210
|
-
# Extended attributes.
|
282
|
+
# Extended attributes.
|
283
|
+
# @param [String] path
|
284
|
+
# @return [Hash] extended attributes for this path.
|
285
|
+
# The returned object will be manipulated directly using :[] :[]=,, :keys and :delete
|
286
|
+
# so the default (a new empty hash on every call) will not retain attributes that are set
|
287
|
+
# @abstract FuseFS api
|
288
|
+
def xattr(path); {} ; end
|
289
|
+
|
290
|
+
# RFuseFS extensions.
|
291
|
+
# File system statistics
|
211
292
|
# @param [String] path
|
212
|
-
# @return [
|
293
|
+
# @return [Array<Integer>] the statistics
|
294
|
+
# used_space (in bytes), used_files, max_space, max_files
|
295
|
+
# See {StatsHelper}
|
296
|
+
# @return [RFuse::StatVfs] or raw statistics
|
213
297
|
# @abstract FuseFS api
|
214
|
-
def
|
298
|
+
def statistics(path); [0,0,0,0]; end
|
215
299
|
|
216
300
|
# RFuseFS extension.
|
217
301
|
# Called when the filesystem is mounted
|
data/lib/fuse/rfusefs-fuse.rb
CHANGED
@@ -16,15 +16,23 @@ module FuseFS
|
|
16
16
|
@path = path
|
17
17
|
@modified = false
|
18
18
|
@contents = ""
|
19
|
+
@size = 0
|
19
20
|
end
|
20
21
|
|
21
22
|
def read(offset,size)
|
22
23
|
contents[offset,size]
|
23
24
|
end
|
24
25
|
|
26
|
+
def create
|
27
|
+
@contents = ""
|
28
|
+
@modified = true
|
29
|
+
end
|
30
|
+
|
25
31
|
def write(offset,data)
|
32
|
+
# TODO: why append?
|
26
33
|
if append? || offset >= contents.length
|
27
34
|
#ignore offset
|
35
|
+
#TODO: should this zero fill?
|
28
36
|
contents << data
|
29
37
|
else
|
30
38
|
contents[offset,data.length]=data
|
@@ -92,6 +100,10 @@ module FuseFS
|
|
92
100
|
def initialize(root)
|
93
101
|
@root = root
|
94
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
|
95
107
|
|
96
108
|
#Define method missing for our filesystem
|
97
109
|
#so we can just call all the API methods as required.
|
@@ -123,8 +135,8 @@ module FuseFS
|
|
123
135
|
|
124
136
|
return wrap_context(ctx,__method__,path) if ctx
|
125
137
|
|
126
|
-
uid = Process.
|
127
|
-
gid = Process.
|
138
|
+
uid = Process.uid
|
139
|
+
gid = Process.gid
|
128
140
|
|
129
141
|
if path == "/" || @root.directory?(path)
|
130
142
|
#set "w" flag based on can_mkdir? || can_write? to path + "/._rfuse_check"
|
@@ -135,14 +147,13 @@ module FuseFS
|
|
135
147
|
#nlink is set to 1 because apparently this makes find work.
|
136
148
|
return RFuse::Stat.directory(mode,{ :uid => uid, :gid => gid, :nlink => 1, :atime => atime, :mtime => mtime, :ctime => ctime })
|
137
149
|
elsif @created_files.has_key?(path)
|
138
|
-
|
139
|
-
return RFuse::Stat.file(@created_files[path],{ :uid => uid, :gid => gid, :atime => now, :mtime => now, :ctime => now })
|
150
|
+
return @created_files[path]
|
140
151
|
elsif @root.file?(path)
|
141
152
|
#Set mode from can_write and executable
|
142
153
|
mode = 0444
|
143
154
|
mode |= 0222 if @root.can_write?(path)
|
144
155
|
mode |= 0111 if @root.executable?(path)
|
145
|
-
size =
|
156
|
+
size = size(path)
|
146
157
|
atime,mtime,ctime = @root.times(path)
|
147
158
|
return RFuse::Stat.file(mode,{ :uid => uid, :gid => gid, :size => size, :atime => atime, :mtime => mtime, :ctime => ctime })
|
148
159
|
else
|
@@ -160,6 +171,7 @@ module FuseFS
|
|
160
171
|
end
|
161
172
|
|
162
173
|
@root.mkdir(path)
|
174
|
+
@adj_nodes += 1
|
163
175
|
end #mkdir
|
164
176
|
|
165
177
|
def mknod(ctx,path,mode,major,minor)
|
@@ -170,10 +182,15 @@ module FuseFS
|
|
170
182
|
raise Errno::EACCES.new(path)
|
171
183
|
end
|
172
184
|
|
173
|
-
|
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
|
174
190
|
end #mknod
|
175
191
|
|
176
192
|
#ftruncate - eg called after opening a file for write without append
|
193
|
+
#sizes are adjusted at file close
|
177
194
|
def ftruncate(ctx,path,offset,ffi)
|
178
195
|
|
179
196
|
return wrap_context(ctx,__method__,path,offset,ffi) if ctx
|
@@ -182,10 +199,11 @@ module FuseFS
|
|
182
199
|
|
183
200
|
if fh.raw
|
184
201
|
@root.raw_truncate(path,offset,fh.raw)
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
202
|
+
if (offset <= 0)
|
203
|
+
fh.contents = ""
|
204
|
+
else
|
205
|
+
fh.contents = fh.contents[0..offset]
|
206
|
+
end
|
189
207
|
end
|
190
208
|
end
|
191
209
|
|
@@ -197,14 +215,16 @@ module FuseFS
|
|
197
215
|
raise Errno::EACESS.new(path)
|
198
216
|
end
|
199
217
|
|
218
|
+
current_size = size(path)
|
200
219
|
unless @root.raw_truncate(path,offset)
|
201
220
|
contents = @root.read_file(path)
|
202
221
|
if (offset <= 0)
|
203
|
-
|
222
|
+
@root.write_to(path,"")
|
204
223
|
elsif offset < contents.length
|
205
224
|
@root.write_to(path,contents[0..offset] )
|
206
225
|
end
|
207
226
|
end
|
227
|
+
@adj_size = @adj_size - current_size + (offset <= 0 ? 0 : offset)
|
208
228
|
end #truncate
|
209
229
|
|
210
230
|
# Open. Create a FileHandler and store in fuse file info
|
@@ -222,17 +242,15 @@ module FuseFS
|
|
222
242
|
end
|
223
243
|
|
224
244
|
unless fh.raw
|
225
|
-
|
226
245
|
if fh.rdonly?
|
227
246
|
fh.contents = @root.read_file(path)
|
228
|
-
elsif fh.
|
247
|
+
elsif fh.writing?
|
229
248
|
unless @root.can_write?(path)
|
230
249
|
raise Errno::EACCES.new(path)
|
231
250
|
end
|
232
251
|
|
233
252
|
if @created_files.has_key?(path)
|
234
|
-
|
235
|
-
fh.contents = "";
|
253
|
+
fh.create
|
236
254
|
else
|
237
255
|
if fh.rdwr? || fh.append?
|
238
256
|
fh.contents = @root.read_file(path)
|
@@ -245,9 +263,9 @@ module FuseFS
|
|
245
263
|
raise Errno::ENOPERM.new(path)
|
246
264
|
end
|
247
265
|
end
|
266
|
+
|
248
267
|
#If we get this far, save our filehandle in the FUSE structure
|
249
268
|
ffi.fh=fh
|
250
|
-
|
251
269
|
end
|
252
270
|
|
253
271
|
def read(ctx,path,size,offset,ffi)
|
@@ -285,7 +303,21 @@ module FuseFS
|
|
285
303
|
else
|
286
304
|
return fh.write(offset,buf)
|
287
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
|
288
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
|
289
321
|
end
|
290
322
|
|
291
323
|
def flush(ctx,path,ffi)
|
@@ -298,13 +330,11 @@ module FuseFS
|
|
298
330
|
#if it was created with mknod it now exists in the filesystem...
|
299
331
|
@created_files.delete(path)
|
300
332
|
end
|
301
|
-
|
302
333
|
end
|
303
334
|
|
304
335
|
def release(ctx,path,ffi)
|
305
336
|
return wrap_context(ctx,__method__,path,ffi) if ctx
|
306
337
|
|
307
|
-
flush(nil,path,ffi)
|
308
338
|
|
309
339
|
fh = ffi.fh
|
310
340
|
if fh && fh.raw
|
@@ -313,8 +343,10 @@ module FuseFS
|
|
313
343
|
else
|
314
344
|
@root.raw_close(path)
|
315
345
|
end
|
346
|
+
else
|
347
|
+
# Probably just had flush called, but no harm calling it again
|
348
|
+
flush(nil,path,ffi)
|
316
349
|
end
|
317
|
-
|
318
350
|
end
|
319
351
|
|
320
352
|
#def chmod(path,mode)
|
@@ -327,9 +359,7 @@ module FuseFS
|
|
327
359
|
return wrap_context(ctx,__method__,path,actime,modtime) if ctx
|
328
360
|
|
329
361
|
#Touch...
|
330
|
-
if @root.respond_to?(:touch)
|
331
|
-
@root.touch(path,modtime)
|
332
|
-
end
|
362
|
+
@root.touch(path,modtime) if @root.respond_to?(:touch)
|
333
363
|
end
|
334
364
|
|
335
365
|
def unlink(ctx,path)
|
@@ -338,7 +368,10 @@ module FuseFS
|
|
338
368
|
unless @root.can_delete?(path)
|
339
369
|
raise Errno::EACCES.new(path)
|
340
370
|
end
|
341
|
-
|
371
|
+
|
372
|
+
@adj_size = @adj_size - size(path)
|
373
|
+
|
374
|
+
@created_files.delete(path)
|
342
375
|
@root.delete(path)
|
343
376
|
end
|
344
377
|
|
@@ -371,8 +404,8 @@ module FuseFS
|
|
371
404
|
#def link(path,as)
|
372
405
|
#end
|
373
406
|
|
374
|
-
def setxattr(ctx,path,name,value)
|
375
|
-
return wrap_context(ctx,__method__,path,name,value) if ctx
|
407
|
+
def setxattr(ctx,path,name,value,flags)
|
408
|
+
return wrap_context(ctx,__method__,path,name,value,flags) if ctx
|
376
409
|
@root.xattr(path)[name]=value
|
377
410
|
end
|
378
411
|
|
@@ -399,12 +432,46 @@ module FuseFS
|
|
399
432
|
#def releasedir(path,ffi)
|
400
433
|
#end
|
401
434
|
|
435
|
+
#
|
402
436
|
#def fsyncdir(path,meta,ffi)
|
403
437
|
#end
|
404
438
|
|
405
439
|
# Some random numbers to show with df command
|
406
|
-
#
|
407
|
-
#
|
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
|
408
475
|
|
409
476
|
def mounted()
|
410
477
|
@root.mounted()
|
@@ -431,5 +498,9 @@ module FuseFS
|
|
431
498
|
self.class.context(ctx) { send(method,nil,*args) }
|
432
499
|
end
|
433
500
|
|
501
|
+
def size(path)
|
502
|
+
@root.respond_to?(:size) ? @root.size(path) : @root.read_file(path).length
|
503
|
+
end
|
504
|
+
|
434
505
|
end #class RFuseFS
|
435
506
|
end #Module FuseFS
|
data/lib/fusefs/metadir.rb
CHANGED
@@ -21,9 +21,15 @@ module FuseFS
|
|
21
21
|
|
22
22
|
DEFAULT_FS = FuseDir.new()
|
23
23
|
|
24
|
-
|
24
|
+
# @return [StatsHelper] helper for filesystem accounting (df etc)
|
25
|
+
attr_reader :stats
|
26
|
+
|
27
|
+
def initialize(stats = nil)
|
25
28
|
@subdirs = Hash.new(nil)
|
26
29
|
@files = Hash.new(nil)
|
30
|
+
@xattr = Hash.new() { |h,k| h[k] = Hash.new }
|
31
|
+
@stats = stats || StatsHelper.new()
|
32
|
+
@stats.adjust(0,1)
|
27
33
|
end
|
28
34
|
|
29
35
|
def split_path(path)
|
@@ -57,6 +63,13 @@ module FuseFS
|
|
57
63
|
end
|
58
64
|
end
|
59
65
|
|
66
|
+
# Extended attributes
|
67
|
+
def xattr(path)
|
68
|
+
pathmethod(:xattr,path) do | path |
|
69
|
+
@xattr[path]
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
60
73
|
def read_file(path)
|
61
74
|
pathmethod(:read_file,path) do |filename|
|
62
75
|
@files[filename].to_s
|
@@ -78,6 +91,14 @@ module FuseFS
|
|
78
91
|
|
79
92
|
def write_to(path,contents)
|
80
93
|
pathmethod(:write_to,path,contents) do |filename, filecontents |
|
94
|
+
adj_size = filecontents.to_s.length
|
95
|
+
adj_nodes = 1
|
96
|
+
if @files.has_key?(filename)
|
97
|
+
adj_size = adj_size - @files[filename].to_s.length
|
98
|
+
adj_nodes = 0
|
99
|
+
end
|
100
|
+
@stats.adjust(adj_size,adj_nodes)
|
101
|
+
|
81
102
|
@files[filename] = filecontents
|
82
103
|
end
|
83
104
|
end
|
@@ -91,7 +112,8 @@ module FuseFS
|
|
91
112
|
|
92
113
|
def delete(path)
|
93
114
|
pathmethod(:delete,path) do |filename|
|
94
|
-
@files.delete(filename)
|
115
|
+
contents = @files.delete(filename)
|
116
|
+
@stats.adjust(-contents.to_s.length,-1)
|
95
117
|
end
|
96
118
|
end
|
97
119
|
|
@@ -104,7 +126,7 @@ module FuseFS
|
|
104
126
|
|
105
127
|
def mkdir(path,dir=nil)
|
106
128
|
pathmethod(:mkdir,path,dir) do | dirname,dirobj |
|
107
|
-
dirobj ||= MetaDir.new
|
129
|
+
dirobj ||= MetaDir.new(@stats)
|
108
130
|
@subdirs[dirname] = dirobj
|
109
131
|
end
|
110
132
|
end
|
@@ -119,6 +141,7 @@ module FuseFS
|
|
119
141
|
def rmdir(path)
|
120
142
|
pathmethod(:rmdir,path) do |dirname|
|
121
143
|
@subdirs.delete(dirname)
|
144
|
+
@stats.adjust(0,-1)
|
122
145
|
end
|
123
146
|
end
|
124
147
|
|
@@ -135,6 +158,8 @@ module FuseFS
|
|
135
158
|
if @files.has_key?(from_base)
|
136
159
|
return false unless can_delete?(from_base) && to_fusefs.can_write?(to_path)
|
137
160
|
to_fusefs.write_to(to_path,@files[from_base])
|
161
|
+
to_fusefs.xattr(to_path).merge!(@xattr[from_base])
|
162
|
+
@xattr.delete(from_base)
|
138
163
|
@files.delete(from_base)
|
139
164
|
elsif @subdirs.has_key?(from_base)
|
140
165
|
# we don't check can_rmdir? because that would prevent us
|
@@ -142,7 +167,11 @@ module FuseFS
|
|
142
167
|
return false unless mount_user? && to_fusefs.can_mkdir?(to_path)
|
143
168
|
begin
|
144
169
|
to_fusefs.mkdir(to_path,@subdirs[from_base])
|
170
|
+
to_fusefs.xattr(to_path).merge!(@xattr[from_base])
|
171
|
+
@xattr.delete(from_base)
|
145
172
|
@subdirs.delete(from_base)
|
173
|
+
@stats.adjust(0,-1)
|
174
|
+
return true
|
146
175
|
rescue ArgumentError
|
147
176
|
# to_rest does not support mkdir with an arbitrary object
|
148
177
|
return false
|
@@ -188,7 +217,20 @@ module FuseFS
|
|
188
217
|
end
|
189
218
|
end
|
190
219
|
|
191
|
-
|
220
|
+
# path is ignored? - recursively calculate for all subdirs - but cache and then rely on fuse to keep count
|
221
|
+
def statistics(path)
|
222
|
+
pathmethod(:statistics,path) do |stats_path|
|
223
|
+
if @subdirs.has_key?(stats_path)
|
224
|
+
#unlike all the other functions where this metadir applies
|
225
|
+
#the function to @subdirs - we need to pass it on
|
226
|
+
@subdirs[stats_path].statistics("/")
|
227
|
+
else
|
228
|
+
@stats.to_statistics
|
229
|
+
end
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
default_methods = FuseDir.public_instance_methods.select { |m|
|
192
234
|
![:mounted,:unmounted].include?(m) &&
|
193
235
|
!self.public_method_defined?(m) && FuseDir.instance_method(m).owner == FuseDir
|
194
236
|
}
|