rfuse 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +5 -0
- data/CHANGES.md +40 -0
- data/Gemfile +4 -0
- data/LICENSE +340 -0
- data/README.md +61 -0
- data/Rakefile +12 -0
- data/ext/.gitignore +2 -0
- data/ext/rfuse/bufferwrapper.c +48 -0
- data/ext/rfuse/bufferwrapper.h +10 -0
- data/ext/rfuse/context.c +70 -0
- data/ext/rfuse/context.h +5 -0
- data/ext/rfuse/extconf.rb +12 -0
- data/ext/rfuse/file_info.c +140 -0
- data/ext/rfuse/file_info.h +13 -0
- data/ext/rfuse/filler.c +60 -0
- data/ext/rfuse/filler.h +15 -0
- data/ext/rfuse/helper.c +93 -0
- data/ext/rfuse/helper.h +22 -0
- data/ext/rfuse/intern_rfuse.c +84 -0
- data/ext/rfuse/intern_rfuse.h +22 -0
- data/ext/rfuse/pollhandle.c +54 -0
- data/ext/rfuse/pollhandle.h +10 -0
- data/ext/rfuse/rfuse.c +2045 -0
- data/ext/rfuse/rfuse.h +3 -0
- data/ext/rfuse/rfuse_mod.c +12 -0
- data/lib/rfuse/version.rb +3 -0
- data/lib/rfuse-ng.rb +1 -0
- data/lib/rfuse.rb +211 -0
- data/lib/rfuse_ng.rb +3 -0
- data/rfuse.gemspec +26 -0
- data/sample/test-ruby.rb +339 -0
- data/spec/basic_spec.rb +176 -0
- data/spec/fuse_file_info_spec.rb +46 -0
- data/spec/ruby_loop_spec.rb +65 -0
- data/spec/spec_helper.rb +51 -0
- data/spec/xattr_spec.rb +39 -0
- metadata +164 -0
data/ext/rfuse/rfuse.h
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
#include "rfuse.h"
|
2
|
+
#include "filler.h"
|
3
|
+
#include "file_info.h"
|
4
|
+
#include "context.h"
|
5
|
+
|
6
|
+
void Init_rfuse() {
|
7
|
+
VALUE mRFuse=rb_define_module("RFuse");
|
8
|
+
file_info_init(mRFuse);
|
9
|
+
context_init(mRFuse);
|
10
|
+
rfiller_init(mRFuse);
|
11
|
+
rfuse_init(mRFuse);
|
12
|
+
}
|
data/lib/rfuse-ng.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'rfuse'
|
data/lib/rfuse.rb
ADDED
@@ -0,0 +1,211 @@
|
|
1
|
+
require 'fcntl'
|
2
|
+
require 'rfuse/version'
|
3
|
+
require 'rfuse/rfuse'
|
4
|
+
|
5
|
+
# Ruby FUSE (Filesystem in USErspace) binding
|
6
|
+
module RFuse
|
7
|
+
|
8
|
+
# Used by listxattr
|
9
|
+
def self.packxattr(xattrs)
|
10
|
+
case xattrs
|
11
|
+
when Array
|
12
|
+
xattrs.join("\000")
|
13
|
+
when String
|
14
|
+
#assume already \0 separated list of keys
|
15
|
+
xattrs
|
16
|
+
else
|
17
|
+
raise RFuse::Error, ":listxattr must return Array or String, got #{xattrs.inspect}"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class Fuse
|
22
|
+
|
23
|
+
# Main processing loop
|
24
|
+
#
|
25
|
+
# Use {#exit} to stop processing (or externally call fusermount -u)
|
26
|
+
#
|
27
|
+
#
|
28
|
+
# Other ruby threads can continue while loop is running, however
|
29
|
+
# no thread can operate on the filesystem itself (ie with File or Dir methods)
|
30
|
+
#
|
31
|
+
# @return [void]
|
32
|
+
# @raise [RFuse::Error] if already running or not mounted
|
33
|
+
#
|
34
|
+
def loop()
|
35
|
+
raise RFuse::Error, "Already running!" if @running
|
36
|
+
raise RFuse::Error, "FUSE not mounted" unless mounted?
|
37
|
+
@running = true
|
38
|
+
while @running do
|
39
|
+
begin
|
40
|
+
ready, ignore, errors = IO.select([@fuse_io,@pr],[],[@fuse_io])
|
41
|
+
|
42
|
+
if ready.include?(@pr)
|
43
|
+
|
44
|
+
@pr.getc
|
45
|
+
@running = false
|
46
|
+
|
47
|
+
elsif errors.include?(@fuse_io)
|
48
|
+
|
49
|
+
@running = false
|
50
|
+
raise RFuse::Error, "FUSE error"
|
51
|
+
|
52
|
+
elsif ready.include?(@fuse_io)
|
53
|
+
if process() < 0
|
54
|
+
# Fuse has been unmounted externally
|
55
|
+
# TODO: mounted? should now return false
|
56
|
+
# fuse_exited? is not true...
|
57
|
+
@running = false
|
58
|
+
end
|
59
|
+
end
|
60
|
+
rescue Interrupt
|
61
|
+
#oh well...
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
# Stop processing {#loop}
|
67
|
+
# eg called from Signal handlers, or some other monitoring thread
|
68
|
+
def exit
|
69
|
+
@pw.putc(0)
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
# Called by the C iniitialize
|
75
|
+
# afer the filesystem has been mounted successfully
|
76
|
+
def ruby_initialize
|
77
|
+
@pr,@pw = IO.pipe()
|
78
|
+
|
79
|
+
# The FD was created by FUSE so we don't want
|
80
|
+
# ruby to do anything with it during GC
|
81
|
+
@fuse_io = IO.for_fd(fd(),"r",:autoclose => false)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
#This class is useful to make your filesystem implementation
|
86
|
+
#debuggable and testable without needing to mount an actual filesystem
|
87
|
+
#or inherit from {Fuse}
|
88
|
+
class FuseDelegator < Fuse
|
89
|
+
|
90
|
+
# Available fuse methods -see http://fuse.sourceforge.net/doxygen/structfuse__operations.html
|
91
|
+
# Note :getdir and :utime are deprecated
|
92
|
+
# :ioctl, :poll are not implemented in the C extension
|
93
|
+
FUSE_METHODS = [ :getattr, :readlink, :getdir, :mknod, :mkdir,
|
94
|
+
:unlink, :rmdir, :symlink, :rename, :link,
|
95
|
+
:chmod, :chown, :truncate, :utime, :open,
|
96
|
+
:create, :read, :write, :statfs, :flush,
|
97
|
+
:release, :fsync, :setxattr, :getxattr, :listxattr,:removexattr,
|
98
|
+
:opendir, :readdir, :releasedir, :fsycndir,
|
99
|
+
:init, :destroy, :access, :ftruncate, :fgetattr, :lock,
|
100
|
+
:utimens, :bmap, :ioctl, :poll ]
|
101
|
+
|
102
|
+
# @param [Object] fuse_object your filesystem object that responds to fuse methods
|
103
|
+
# @param [String] mountpoint existing directory where the filesystem will be mounted
|
104
|
+
# @param [String...] options fuse mount options (use "-h" to see a list)
|
105
|
+
#
|
106
|
+
# Create and mount a filesystem
|
107
|
+
#
|
108
|
+
# If ruby debug is enabled then each call to fuse_object will be represented on $stderr
|
109
|
+
def initialize(fuse_object,mountpoint,*options)
|
110
|
+
@fuse_delegate = fuse_object
|
111
|
+
define_fuse_methods(fuse_object)
|
112
|
+
super(mountpoint,options)
|
113
|
+
end
|
114
|
+
|
115
|
+
private
|
116
|
+
def define_fuse_methods(fuse_object)
|
117
|
+
#Wrap all the rfuse methods in a context block
|
118
|
+
FUSE_METHODS.each do |method|
|
119
|
+
if fuse_object.respond_to?(method)
|
120
|
+
method_name = method.id2name
|
121
|
+
instance_eval(<<-EOM, "(__FUSE_DELEGATE__)",1)
|
122
|
+
def #{method_name} (*args,&block)
|
123
|
+
begin
|
124
|
+
$stderr.puts "==> #{ self }.#{ method_name }(\#{args.inspect })" if $DEBUG
|
125
|
+
result = @fuse_delegate.send(:#{method_name},*args,&block)
|
126
|
+
$stderr.puts "<== #{ self }.#{ method_name }()" if $DEBUG
|
127
|
+
result
|
128
|
+
rescue Exception => ex
|
129
|
+
$@.delete_if{|s| /^\\(__FUSE_DELEGATE__\\):/ =~ s}
|
130
|
+
$stderr.puts(ex.message) unless ex.respond_to?(:errno) || $DEBUG
|
131
|
+
Kernel::raise
|
132
|
+
end
|
133
|
+
end
|
134
|
+
EOM
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
end #class FuseDelegator
|
140
|
+
|
141
|
+
# Helper class to return from :getattr method
|
142
|
+
class Stat
|
143
|
+
# Format mask
|
144
|
+
S_IFMT = 0170000
|
145
|
+
# Directory
|
146
|
+
S_IFDIR = 0040000
|
147
|
+
# Character device
|
148
|
+
S_IFCHR = 0020000
|
149
|
+
# Block device
|
150
|
+
S_IFBLK = 0060000
|
151
|
+
# Regular file
|
152
|
+
S_IFREG = 0100000
|
153
|
+
# FIFO.
|
154
|
+
S_IFIFO = 0010000
|
155
|
+
# Symbolic link
|
156
|
+
S_IFLNK = 0120000
|
157
|
+
# Socket
|
158
|
+
S_IFSOCK = 0140000
|
159
|
+
|
160
|
+
# @param [Fixnum] mode file permissions
|
161
|
+
# @param [Hash<Symbol,Fixnum>] values initial values for other attributes
|
162
|
+
#
|
163
|
+
# @return [Stat] representing a directory
|
164
|
+
def self.directory(mode=0,values = { })
|
165
|
+
return self.new(S_IFDIR,mode,values)
|
166
|
+
end
|
167
|
+
|
168
|
+
# @param [Fixnum] mode file permissions
|
169
|
+
# @param [Hash<Symbol,Fixnum>] values initial values for other attributes
|
170
|
+
#
|
171
|
+
# @return [Stat] representing a regular file
|
172
|
+
def self.file(mode=0,values = { })
|
173
|
+
return self.new(S_IFREG,mode,values)
|
174
|
+
end
|
175
|
+
|
176
|
+
# @return [Integer] see stat(2)
|
177
|
+
attr_accessor :uid,:gid,:mode,:size, :dev,:ino,:nlink,:rdev,:blksize,:blocks
|
178
|
+
|
179
|
+
# @return [Integer, Time] see stat(2)
|
180
|
+
attr_accessor :atime,:mtime,:ctime
|
181
|
+
|
182
|
+
def initialize(type,permissions,values = { })
|
183
|
+
values[:mode] = ((type & S_IFMT) | (permissions & 07777))
|
184
|
+
@uid,@gid,@size,@mode,@atime,@mtime,@ctime,@dev,@ino,@nlink,@rdev,@blksize,@blocks = Array.new(13,0)
|
185
|
+
values.each_pair do |k,v|
|
186
|
+
instance_variable_set("@#{ k }",v)
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
# Helper class to return from :statfs (eg for df output)
|
192
|
+
# All attributes are Integers and default to 0
|
193
|
+
class StatVfs
|
194
|
+
|
195
|
+
# @return [Integer]
|
196
|
+
attr_accessor :f_bsize,:f_frsize,:f_blocks,:f_bfree,:f_bavail
|
197
|
+
|
198
|
+
# @return [Integer]
|
199
|
+
attr_accessor :f_files,:f_ffree,:f_favail,:f_fsid,:f_flag,:f_namemax
|
200
|
+
|
201
|
+
# values can be symbols or strings but drop the pointless f_ prefix
|
202
|
+
def initialize(values={ })
|
203
|
+
@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)
|
204
|
+
values.each_pair do |k,v|
|
205
|
+
prefix = k.startswith?("f_") ? "" : "f_"
|
206
|
+
instance_variable_set("@#{prefix}#{k}",v)
|
207
|
+
end
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
end #Module RFuse
|
data/lib/rfuse_ng.rb
ADDED
data/rfuse.gemspec
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "rfuse/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "rfuse"
|
7
|
+
s.version = RFuse::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Grant Gardner"]
|
10
|
+
s.email = ["grant@lastweekend.com.au"]
|
11
|
+
s.homepage = "http://rubygems.org/gems/rfuse"
|
12
|
+
s.summary = %q{Ruby language binding for FUSE}
|
13
|
+
s.description = %q{Ruby language binding for FUSE. It was forked from rfuse and modified for Ruby 1.9.2.}
|
14
|
+
|
15
|
+
s.files = `git ls-files`.split("\n")
|
16
|
+
s.extensions = 'ext/rfuse/extconf.rb'
|
17
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
18
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
19
|
+
s.require_paths = ["lib"]
|
20
|
+
|
21
|
+
s.add_development_dependency("rake")
|
22
|
+
s.add_development_dependency("rspec")
|
23
|
+
s.add_development_dependency("yard")
|
24
|
+
s.add_development_dependency("redcarpet")
|
25
|
+
s.add_development_dependency("ffi-xattr")
|
26
|
+
end
|
data/sample/test-ruby.rb
ADDED
@@ -0,0 +1,339 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
|
3
|
+
# TestFS for RFuse
|
4
|
+
|
5
|
+
require "rfuse"
|
6
|
+
|
7
|
+
class MyDir < Hash
|
8
|
+
attr_accessor :name, :mode , :actime, :modtime, :uid, :gid
|
9
|
+
def initialize(name,mode)
|
10
|
+
@uid=0
|
11
|
+
@gid=0
|
12
|
+
@actime=Time.now
|
13
|
+
@modtime=Time.now
|
14
|
+
@xattr=Hash.new
|
15
|
+
@name=name
|
16
|
+
@mode=mode
|
17
|
+
end
|
18
|
+
|
19
|
+
def stat
|
20
|
+
RFuse::Stat.directory(mode,:uid => uid, :gid => gid, :atime => actime, :mtime => modtime,
|
21
|
+
:size => size)
|
22
|
+
end
|
23
|
+
|
24
|
+
def listxattr()
|
25
|
+
@xattr.keys()
|
26
|
+
end
|
27
|
+
def setxattr(name,value,flag)
|
28
|
+
@xattr[name]=value #TODO:don't ignore flag
|
29
|
+
end
|
30
|
+
def getxattr(name)
|
31
|
+
return @xattr[name]
|
32
|
+
end
|
33
|
+
def removexattr(name)
|
34
|
+
@xattr.delete(name)
|
35
|
+
end
|
36
|
+
def size
|
37
|
+
return 48 #for testing only
|
38
|
+
end
|
39
|
+
def isdir
|
40
|
+
true
|
41
|
+
end
|
42
|
+
def insert_obj(obj,path)
|
43
|
+
d=self.search(File.dirname(path))
|
44
|
+
if d.isdir then
|
45
|
+
d[obj.name]=obj
|
46
|
+
else
|
47
|
+
raise Errno::ENOTDIR.new(d.name)
|
48
|
+
end
|
49
|
+
return d
|
50
|
+
end
|
51
|
+
def remove_obj(path)
|
52
|
+
d=self.search(File.dirname(path))
|
53
|
+
d.delete(File.basename(path))
|
54
|
+
end
|
55
|
+
def search(path)
|
56
|
+
p=path.split('/').delete_if {|x| x==''}
|
57
|
+
if p.length==0 then
|
58
|
+
return self
|
59
|
+
else
|
60
|
+
return self.follow(p)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
def follow (path_array)
|
64
|
+
if path_array.length==0 then
|
65
|
+
return self
|
66
|
+
else
|
67
|
+
d=self[path_array.shift]
|
68
|
+
if d then
|
69
|
+
return d.follow(path_array)
|
70
|
+
else
|
71
|
+
raise Errno::ENOENT.new
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
def to_s
|
76
|
+
return "Dir: " + @name + "(" + @mode.to_s + ")"
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
class MyFile
|
81
|
+
attr_accessor :name, :mode, :actime, :modtime, :uid, :gid, :content
|
82
|
+
def initialize(name,mode,uid,gid)
|
83
|
+
@actime=0
|
84
|
+
@modtime=0
|
85
|
+
@xattr=Hash.new
|
86
|
+
@content=""
|
87
|
+
@uid=uid
|
88
|
+
@gid=gid
|
89
|
+
@name=name
|
90
|
+
@mode=mode
|
91
|
+
end
|
92
|
+
|
93
|
+
def stat
|
94
|
+
RFuse::Stat.file(mode,:uid => uid, :gid => gid, :atime => actime, :mtime => modtime,
|
95
|
+
:size => size)
|
96
|
+
end
|
97
|
+
|
98
|
+
def listxattr()
|
99
|
+
@xattr.keys
|
100
|
+
end
|
101
|
+
|
102
|
+
def setxattr(name,value,flag)
|
103
|
+
@xattr[name]=value #TODO:don't ignore flag
|
104
|
+
end
|
105
|
+
|
106
|
+
def getxattr(name)
|
107
|
+
return @xattr[name]
|
108
|
+
end
|
109
|
+
def removexattr(name)
|
110
|
+
@xattr.delete(name)
|
111
|
+
end
|
112
|
+
def size
|
113
|
+
return content.size
|
114
|
+
end
|
115
|
+
def isdir
|
116
|
+
false
|
117
|
+
end
|
118
|
+
def follow(path_array)
|
119
|
+
if path_array.length != 0 then
|
120
|
+
raise Errno::ENOTDIR.new
|
121
|
+
else
|
122
|
+
return self
|
123
|
+
end
|
124
|
+
end
|
125
|
+
def to_s
|
126
|
+
return "File: " + @name + "(" + @mode.to_s + ")"
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
class MyFuse
|
131
|
+
|
132
|
+
def initialize(root)
|
133
|
+
@root=root
|
134
|
+
end
|
135
|
+
|
136
|
+
# The new readdir way, c+p-ed from getdir
|
137
|
+
def readdir(ctx,path,filler,offset,ffi)
|
138
|
+
d=@root.search(path)
|
139
|
+
if d.isdir then
|
140
|
+
d.each {|name,obj|
|
141
|
+
filler.push(name,obj.stat,0)
|
142
|
+
}
|
143
|
+
else
|
144
|
+
raise Errno::ENOTDIR.new(path)
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
def getattr(ctx,path)
|
149
|
+
d = @root.search(path)
|
150
|
+
return d.stat
|
151
|
+
end #getattr
|
152
|
+
|
153
|
+
def mkdir(ctx,path,mode)
|
154
|
+
@root.insert_obj(MyDir.new(File.basename(path),mode),path)
|
155
|
+
end #mkdir
|
156
|
+
|
157
|
+
def mknod(ctx,path,mode,major,minor)
|
158
|
+
@root.insert_obj(MyFile.new(File.basename(path),mode,ctx.uid,ctx.gid),path)
|
159
|
+
end #mknod
|
160
|
+
|
161
|
+
def open(ctx,path,ffi)
|
162
|
+
end
|
163
|
+
|
164
|
+
#def release(ctx,path,fi)
|
165
|
+
#end
|
166
|
+
|
167
|
+
#def flush(ctx,path,fi)
|
168
|
+
#end
|
169
|
+
|
170
|
+
def chmod(ctx,path,mode)
|
171
|
+
d=@root.search(path)
|
172
|
+
d.mode=mode
|
173
|
+
end
|
174
|
+
|
175
|
+
def chown(ctx,path,uid,gid)
|
176
|
+
d=@root.search(path)
|
177
|
+
d.uid=uid
|
178
|
+
d.gid=gid
|
179
|
+
end
|
180
|
+
|
181
|
+
def truncate(ctx,path,offset)
|
182
|
+
d=@root.search(path)
|
183
|
+
d.content = d.content[0..offset]
|
184
|
+
end
|
185
|
+
|
186
|
+
def utime(ctx,path,actime,modtime)
|
187
|
+
d=@root.search(path)
|
188
|
+
d.actime=actime
|
189
|
+
d.modtime=modtime
|
190
|
+
end
|
191
|
+
|
192
|
+
def unlink(ctx,path)
|
193
|
+
@root.remove_obj(path)
|
194
|
+
end
|
195
|
+
|
196
|
+
def rmdir(ctx,path)
|
197
|
+
@root.remove_obj(path)
|
198
|
+
end
|
199
|
+
|
200
|
+
#def symlink(ctx,path,as)
|
201
|
+
#end
|
202
|
+
|
203
|
+
def rename(ctx,path,as)
|
204
|
+
d = @root.search(path)
|
205
|
+
@root.remove_obj(path)
|
206
|
+
@root.insert_obj(d,path)
|
207
|
+
end
|
208
|
+
|
209
|
+
#def link(ctx,path,as)
|
210
|
+
#end
|
211
|
+
|
212
|
+
def read(ctx,path,size,offset,fi)
|
213
|
+
d = @root.search(path)
|
214
|
+
if (d.isdir)
|
215
|
+
raise Errno::EISDIR.new(path)
|
216
|
+
return nil
|
217
|
+
else
|
218
|
+
return d.content[offset..offset + size - 1]
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
def write(ctx,path,buf,offset,fi)
|
223
|
+
d=@root.search(path)
|
224
|
+
if (d.isdir)
|
225
|
+
raise Errno::EISDIR.new(path)
|
226
|
+
else
|
227
|
+
d.content[offset..offset+buf.length - 1] = buf
|
228
|
+
end
|
229
|
+
return buf.length
|
230
|
+
end
|
231
|
+
|
232
|
+
def setxattr(ctx,path,name,value,size,flags)
|
233
|
+
d=@root.search(path)
|
234
|
+
d.setxattr(name,value,flags)
|
235
|
+
end
|
236
|
+
|
237
|
+
def getxattr(ctx,path,name)
|
238
|
+
d=@root.search(path)
|
239
|
+
if (d)
|
240
|
+
value=d.getxattr(name)
|
241
|
+
if (!value)
|
242
|
+
value=""
|
243
|
+
#raise Errno::ENOENT.new #TODO raise the correct error :
|
244
|
+
#NOATTR which is not implemented in Linux/glibc
|
245
|
+
end
|
246
|
+
else
|
247
|
+
raise Errno::ENOENT.new
|
248
|
+
end
|
249
|
+
return value
|
250
|
+
end
|
251
|
+
|
252
|
+
def listxattr(ctx,path)
|
253
|
+
d=@root.search(path)
|
254
|
+
value= d.listxattr()
|
255
|
+
return value
|
256
|
+
end
|
257
|
+
|
258
|
+
def removexattr(ctx,path,name)
|
259
|
+
d=@root.search(path)
|
260
|
+
d.removexattr(name)
|
261
|
+
end
|
262
|
+
|
263
|
+
#def opendir(ctx,path,ffi)
|
264
|
+
#end
|
265
|
+
|
266
|
+
#def releasedir(ctx,path,ffi)
|
267
|
+
#end
|
268
|
+
|
269
|
+
#def fsyncdir(ctx,path,meta,ffi)
|
270
|
+
#end
|
271
|
+
|
272
|
+
# Some random numbers to show with df command
|
273
|
+
def statfs(ctx,path)
|
274
|
+
s = RFuse::StatVfs.new()
|
275
|
+
s.f_bsize = 1024
|
276
|
+
s.f_frsize = 1024
|
277
|
+
s.f_blocks = 1000000
|
278
|
+
s.f_bfree = 500000
|
279
|
+
s.f_bavail = 990000
|
280
|
+
s.f_files = 10000
|
281
|
+
s.f_ffree = 9900
|
282
|
+
s.f_favail = 9900
|
283
|
+
s.f_fsid = 23423
|
284
|
+
s.f_flag = 0
|
285
|
+
s.f_namemax = 10000
|
286
|
+
return s
|
287
|
+
end
|
288
|
+
|
289
|
+
def ioctl(ctx, path, cmd, arg, ffi, flags, data)
|
290
|
+
# FT: I was not been able to test it.
|
291
|
+
print "*** IOCTL: command: ", cmd, "\n"
|
292
|
+
end
|
293
|
+
|
294
|
+
def poll(ctx, path, ffi, ph, reventsp)
|
295
|
+
print "*** POLL: ", path, "\n"
|
296
|
+
# This is how we notify the caller if something happens:
|
297
|
+
ph.notifyPoll();
|
298
|
+
# when the GC harvests the object it calls fuse_pollhandle_destroy
|
299
|
+
# by itself.
|
300
|
+
end
|
301
|
+
|
302
|
+
def init(ctx,rfuseconninfo)
|
303
|
+
print "RFuse TestFS started\n"
|
304
|
+
print "init called\n"
|
305
|
+
print "proto_major:#{rfuseconninfo.proto_major}\n"
|
306
|
+
end
|
307
|
+
|
308
|
+
end #class Fuse
|
309
|
+
|
310
|
+
if ARGV.length == 0
|
311
|
+
print "\n"
|
312
|
+
print "Usage: [ruby [--debug]] #{$0} mountpoint [mount_options...]\n"
|
313
|
+
print "\n"
|
314
|
+
print " mountpoint must be an existing directory\n"
|
315
|
+
print " mount_option '-h' will list supported options\n"
|
316
|
+
print "\n"
|
317
|
+
print " For verbose debugging output use --debug to ruby\n"
|
318
|
+
print " and '-odebug' as mount_option\n"
|
319
|
+
print "\n"
|
320
|
+
exit(1)
|
321
|
+
end
|
322
|
+
|
323
|
+
fs = MyFuse.new(MyDir.new("",0777));
|
324
|
+
|
325
|
+
fo = RFuse::FuseDelegator.new(fs,*ARGV)
|
326
|
+
|
327
|
+
if fo.mounted?
|
328
|
+
Signal.trap("TERM") { print "Caught TERM\n" ; fo.exit }
|
329
|
+
Signal.trap("INT") { print "Caught INT\n"; fo.exit }
|
330
|
+
|
331
|
+
begin
|
332
|
+
fo.loop
|
333
|
+
rescue
|
334
|
+
print "Error:" + $!
|
335
|
+
ensure
|
336
|
+
fo.unmount if fo.mounted?
|
337
|
+
print "Unmounted #{ARGV[0]}\n"
|
338
|
+
end
|
339
|
+
end
|