rfusefs 1.0.2.RC1 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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