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.
@@ -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