rfuse 1.0.0 → 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/.yardopts ADDED
@@ -0,0 +1,2 @@
1
+ -
2
+ CHANGES.md
data/CHANGES.md CHANGED
@@ -1,3 +1,12 @@
1
+ 1.0.2- 2012/08/09
2
+ -----------------
3
+
4
+ Support Ruby 1.8 (tested with 1.8.7)
5
+
6
+ Bugfixes
7
+
8
+ * {RFuse::Fuse#utimens} fixed to correctly convert time to nanoseconds
9
+ * Exceptions in {RFuse::Fuse#getattr} fixed to output backtrace etc
1
10
 
2
11
  1.0.0
3
12
  ----------------
data/README.md CHANGED
@@ -3,7 +3,7 @@ RFuse
3
3
 
4
4
  http://rubygems.org/gems/rfuse
5
5
 
6
- FUSE bindings for Ruby 1.9
6
+ FUSE bindings for Ruby
7
7
 
8
8
  FUSE (Filesystem in USErspace) is a simple interface for userspace programs to export a virtual filesystem to the linux kernel. FUSE aims to provide a secure method for non privileged users to create and mount their own filesystem implementations.
9
9
 
@@ -14,7 +14,7 @@ For a more ruby-ish API for creating filesystems see {http://rubygems.org/gems/r
14
14
  Dependencies
15
15
  --------------
16
16
 
17
- * Ruby 1.9
17
+ * Ruby 1.8 or 1.9
18
18
  * Fuse 2.8
19
19
 
20
20
  Installation
data/Rakefile CHANGED
@@ -5,8 +5,6 @@ require 'rspec/core/rake_task'
5
5
 
6
6
  RSpec::Core::RakeTask.new(:spec)
7
7
 
8
- YARD::Rake::YardocTask.new do |t|
9
- t.files = ['lib/**/*.rb', 'ext/**/*.c','-','CHANGES.md'] # optional
10
- end
8
+ YARD::Rake::YardocTask.new
11
9
 
12
10
  task :default => :spec
data/ext/rfuse/extconf.rb CHANGED
@@ -5,6 +5,11 @@ $CFLAGS << ' -Wall'
5
5
  $CFLAGS << ' -D_FILE_OFFSET_BITS=64'
6
6
  $CFLAGS << ' -DFUSE_USE_VERSION=26'
7
7
 
8
+
9
+ if have_func("rb_errinfo")
10
+ puts "Have rb_errinfo"
11
+ end
12
+
8
13
  if have_library('fuse')
9
14
  create_makefile('rfuse/rfuse')
10
15
  else
data/ext/rfuse/rfuse.c CHANGED
@@ -14,7 +14,6 @@
14
14
  #include <sys/xattr.h>
15
15
  //#endif
16
16
 
17
- #include <ruby/encoding.h>
18
17
  #include "helper.h"
19
18
  #include "intern_rfuse.h"
20
19
  #include "filler.h"
@@ -24,13 +23,32 @@
24
23
  #include "pollhandle.h"
25
24
  #include "bufferwrapper.h"
26
25
 
26
+ #ifdef HAVE_RUBY_ENCODING_H
27
+ #include <ruby/encoding.h>
28
+ #endif
29
+
30
+ #ifndef HAVE_RB_ERRINFO
31
+ static VALUE rb_errinfo()
32
+ {
33
+ return ruby_errinfo;
34
+ }
35
+ #endif
27
36
 
28
37
  static VALUE mRFuse;
29
38
  static VALUE eRFuse_Error;
30
39
 
40
+ static VALUE rb_filesystem_encode(VALUE str)
41
+ {
42
+ #ifdef HAVE_RUBY_ENCODING_H
43
+ return rb_enc_associate(str,rb_filesystem_encoding());
44
+ #else
45
+ return str;
46
+ #endif
47
+ }
48
+
31
49
  static int unsafe_return_error(VALUE *args)
32
50
  {
33
-
51
+
34
52
  if (rb_respond_to(rb_errinfo(),rb_intern("errno"))) {
35
53
  //We expect these and they get passed on the fuse so be quiet...
36
54
  return rb_funcall(rb_errinfo(),rb_intern("errno"),0);
@@ -73,7 +91,7 @@ static void init_context_path_args(VALUE *args,struct fuse_context *ctx,const ch
73
91
  args[0] = ctx->private_data;
74
92
  args[1] = wrap_context(ctx);
75
93
  args[2] = rb_str_new2(path);
76
- rb_enc_associate(args[2],rb_filesystem_encoding());
94
+ rb_filesystem_encode(args[2]);
77
95
  }
78
96
  /*
79
97
  @overload readdir(context,path,filler,offset,ffi)
@@ -275,13 +293,14 @@ static int rf_getattr(const char *path, struct stat *stbuf)
275
293
 
276
294
  res=rb_protect((VALUE (*)())unsafe_getattr,(VALUE) args,&error);
277
295
 
278
- if (res == Qnil) {
279
- return -ENOENT;
280
- }
281
296
  if (error)
282
297
  {
283
298
  return -(return_error(ENOENT));
284
299
  }
300
+
301
+ if (res == Qnil) {
302
+ return -ENOENT;
303
+ }
285
304
  else
286
305
  {
287
306
  rstat2stat(res,stbuf);
@@ -733,7 +752,7 @@ static int rf_symlink(const char *path,const char *as)
733
752
  init_context_path_args(args,ctx,path);
734
753
 
735
754
  args[3]=rb_str_new2(as);
736
- rb_enc_associate(args[3],rb_filesystem_encoding());
755
+ rb_filesystem_encode(args[3]);
737
756
 
738
757
  res=rb_protect((VALUE (*)())unsafe_symlink,(VALUE) args,&error);
739
758
 
@@ -768,7 +787,7 @@ static int rf_rename(const char *path,const char *as)
768
787
  init_context_path_args(args,ctx,path);
769
788
 
770
789
  args[3]=rb_str_new2(as);
771
- rb_enc_associate(args[3],rb_filesystem_encoding());
790
+ rb_filesystem_encode(args[3]);
772
791
 
773
792
  res=rb_protect((VALUE (*)())unsafe_rename,(VALUE) args,&error);
774
793
 
@@ -802,7 +821,7 @@ static int rf_link(const char *path,const char * as)
802
821
  struct fuse_context *ctx=fuse_get_context();
803
822
  init_context_path_args(args,ctx,path);
804
823
  args[3]=rb_str_new2(as);
805
- rb_enc_associate(args[3],rb_filesystem_encoding());
824
+ rb_filesystem_encode(args[3]);
806
825
  res=rb_protect((VALUE (*)())unsafe_link,(VALUE) args,&error);
807
826
 
808
827
  return error ? -(return_error(ENOENT)) : 0;
@@ -1585,17 +1604,17 @@ static int rf_utimens(const char * path, const struct timespec tv[2])
1585
1604
  struct fuse_context *ctx = fuse_get_context();
1586
1605
  init_context_path_args(args,ctx,path);
1587
1606
 
1588
- // tv_sec * 1000000 + tv_nsec
1607
+ // tv_sec * 1000000000 + tv_nsec
1589
1608
  args[3] = rb_funcall(
1590
1609
  rb_funcall(
1591
- INT2NUM(tv[0].tv_sec), rb_intern("*"), 1, INT2NUM(1000000)
1610
+ INT2NUM(tv[0].tv_sec), rb_intern("*"), 1, INT2NUM(1000000000)
1592
1611
  ),
1593
1612
  rb_intern("+"), 1, INT2NUM(tv[0].tv_nsec)
1594
1613
  );
1595
1614
 
1596
1615
  args[4] = rb_funcall(
1597
1616
  rb_funcall(
1598
- INT2NUM(tv[1].tv_sec), rb_intern("*"), 1, INT2NUM(1000000)
1617
+ INT2NUM(tv[1].tv_sec), rb_intern("*"), 1, INT2NUM(1000000000)
1599
1618
  ),
1600
1619
  rb_intern("+"), 1, INT2NUM(tv[1].tv_nsec)
1601
1620
  );
@@ -1675,7 +1694,7 @@ static int rf_ioctl(const char *path, int cmd, void *arg,
1675
1694
  int error = 0;
1676
1695
 
1677
1696
  args[0] = rb_str_new2(path);
1678
- rb_enc_associate(args[0],rb_filesystem_encoding());
1697
+ rb_filesystem_encode(args[0]);
1679
1698
  args[1] = INT2NUM(cmd);
1680
1699
  args[2] = wrap_buffer(arg);
1681
1700
  args[3] = get_file_info(ffi);
@@ -1715,7 +1734,7 @@ static int rf_poll(const char *path, struct fuse_file_info *ffi,
1715
1734
  int error = 0;
1716
1735
 
1717
1736
  args[0] = rb_str_new2(path);
1718
- rb_enc_associate(args[0],rb_filesystem_encoding());
1737
+ rb_filesystem_encode(args[0]);
1719
1738
  args[1] = get_file_info(ffi);
1720
1739
  args[2] = wrap_pollhandle(ph);
1721
1740
  args[3] = INT2NUM(*reventsp);
@@ -1756,7 +1775,11 @@ VALUE rf_unmount(VALUE self)
1756
1775
  struct intern_fuse *inf;
1757
1776
  Data_Get_Struct(self,struct intern_fuse,inf);
1758
1777
 
1759
- fuse_exit(inf->fuse);
1778
+ rb_funcall(self,rb_intern("ruby_unmount"),0);
1779
+
1780
+ if (inf->fuse != NULL) {
1781
+ fuse_exit(inf->fuse);
1782
+ }
1760
1783
 
1761
1784
  if (inf->fc != NULL) {
1762
1785
  fuse_unmount(inf->mountpoint, inf->fc);
@@ -1773,7 +1796,7 @@ VALUE rf_mountname(VALUE self)
1773
1796
  struct intern_fuse *inf;
1774
1797
  Data_Get_Struct(self,struct intern_fuse,inf);
1775
1798
  VALUE result = rb_str_new2(inf->mountpoint);
1776
- rb_enc_associate(result,rb_filesystem_encoding());
1799
+ rb_filesystem_encode(result);
1777
1800
 
1778
1801
  return result;
1779
1802
  }
@@ -0,0 +1,20 @@
1
+
2
+ unless IO.instance_methods.include?(:autoclose?)
3
+ class IO
4
+ def self.for_fd(fd, mode_string, options = {})
5
+ IO.new(fd,mode_string)
6
+ end
7
+
8
+ def autoclose?
9
+ true
10
+ end
11
+ end
12
+ end
13
+
14
+ unless Time.instance_methods.include?(:nsec)
15
+ class Time
16
+ def nsec
17
+ usec * 1000
18
+ end
19
+ end
20
+ end
data/lib/rfuse/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module RFuse
2
- VERSION = "1.0.0"
2
+ VERSION = "1.0.2"
3
3
  end
data/lib/rfuse.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  require 'fcntl'
2
2
  require 'rfuse/version'
3
3
  require 'rfuse/rfuse'
4
+ require 'rfuse/compat'
4
5
 
5
6
  # Ruby FUSE (Filesystem in USErspace) binding
6
7
  module RFuse
@@ -30,7 +31,6 @@ module RFuse
30
31
  #
31
32
  # @return [void]
32
33
  # @raise [RFuse::Error] if already running or not mounted
33
- #
34
34
  def loop()
35
35
  raise RFuse::Error, "Already running!" if @running
36
36
  raise RFuse::Error, "FUSE not mounted" unless mounted?
@@ -80,6 +80,18 @@ module RFuse
80
80
  # ruby to do anything with it during GC
81
81
  @fuse_io = IO.for_fd(fd(),"r",:autoclose => false)
82
82
  end
83
+
84
+ # Called by C unmount before doing all the FUSE stuff
85
+ def ruby_unmount
86
+ @pr.close if @pr && !@pr.closed?
87
+ @pw.close if @pw && !@pw.closed?
88
+
89
+ # Ideally we want this IO to avoid autoclosing at GC, but
90
+ # in Ruby 1.8 we have no way to do that. A work around is to close
91
+ # the IO here. FUSE won't necessarily like that but it is the best
92
+ # we can do
93
+ @fuse_io.close() if @fuse_io && !@fuse_io.closed? && @fuse_io.autoclose?
94
+ end
83
95
  end
84
96
 
85
97
  #This class is useful to make your filesystem implementation
data/rfuse.gemspec CHANGED
@@ -10,7 +10,7 @@ Gem::Specification.new do |s|
10
10
  s.email = ["grant@lastweekend.com.au"]
11
11
  s.homepage = "http://rubygems.org/gems/rfuse"
12
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.}
13
+ s.description = %q{Write userspace filesystems in Ruby}
14
14
 
15
15
  s.files = `git ls-files`.split("\n")
16
16
  s.extensions = 'ext/rfuse/extconf.rb'
@@ -18,6 +18,9 @@ Gem::Specification.new do |s|
18
18
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
19
  s.require_paths = ["lib"]
20
20
 
21
+ s.has_rdoc = 'yard'
22
+ s.extra_rdoc_files = 'CHANGES.md'
23
+
21
24
  s.add_development_dependency("rake")
22
25
  s.add_development_dependency("rspec")
23
26
  s.add_development_dependency("yard")
data/spec/basic_spec.rb CHANGED
@@ -1,8 +1,9 @@
1
1
  require 'spec_helper'
2
2
  require 'pathname'
3
+ require 'tempfile'
3
4
 
4
5
  describe RFuse::Fuse do
5
-
6
+
6
7
  let(:dir_stat) { RFuse::Stat.directory(0444) }
7
8
  let(:file_stat) { RFuse::Stat.file(0444) }
8
9
  let!(:mockfs) { m = mock("fuse"); m.stub(:getattr).and_return(nil); m }
@@ -26,7 +27,7 @@ describe RFuse::Fuse do
26
27
  fuse.mounted?.should be_false
27
28
  lambda { fuse.loop }.should raise_error(RFuse::Error)
28
29
  end
29
-
30
+
30
31
  it "should handle a Pathname as a mountpoint" do
31
32
  fuse = RFuse::FuseDelegator.new(mockfs,Pathname.new(mountpoint))
32
33
  fuse.mounted?.should be_true
@@ -57,7 +58,7 @@ describe RFuse::Fuse do
57
58
 
58
59
  mockfs.should_receive(:readdir) do | ctx, path, filler,offset,ffi |
59
60
  filler.push("hello",nil,0)
60
- filler.push("world",nil,0)
61
+ filler.push("world",nil,0)
61
62
  end
62
63
 
63
64
  with_fuse(mountpoint,mockfs) do
@@ -83,38 +84,50 @@ describe RFuse::Fuse do
83
84
 
84
85
  context "timestamps" do
85
86
 
87
+
86
88
  it "should support stat with subsecond resolution" do
87
- begin
88
- atime = Time.now() + 60
89
- sleep(0.001)
90
- end until atime.usec != 0
91
-
92
- begin
93
- mtime = Time.now() + 600
94
- sleep(0.001)
95
- end until mtime.usec != 0
96
-
97
- begin
98
- ctime = Time.now() + 3600
99
- sleep(0.001)
100
- end until ctime.usec != 0
101
-
102
- file_stat.atime = atime
103
- file_stat.mtime = mtime
104
- file_stat.ctime = ctime
105
-
106
-
107
- # ruby can't set file times with ns res, o we are limited to usecs
108
- mockfs.stub(:getattr).with(anything(),"/nanos").and_return(file_stat)
109
-
110
- with_fuse(mountpoint,mockfs) do
111
- stat = File.stat("#{mountpoint}/nanos")
112
- stat.atime.should == atime
113
- stat.ctime.should == ctime
114
- stat.mtime.should == mtime
115
- end
89
+ testns = Tempfile.new("testns")
90
+ stat = File.stat(testns.path)
91
+
92
+ # so if this Ruby has usec resolution for File.stat
93
+ # we'd expect to skip this test 1 in 100,000 times...
94
+ no_usecs = (stat.mtime.usec == 0)
95
+
96
+ if no_usecs
97
+ puts "Skipping due to no usec resolution for File.stat"
98
+ else
99
+ atime,mtime,ctime = usec_times(60,600,3600)
100
+
101
+ file_stat.atime = atime
102
+ file_stat.mtime = mtime
103
+ file_stat.ctime = ctime
104
+
105
+ # ruby can't set file times with ns res, o we are limited to usecs
106
+ mockfs.stub(:getattr).with(anything(),"/nanos").and_return(file_stat)
107
+
108
+ with_fuse(mountpoint,mockfs) do
109
+ stat = File.stat("#{mountpoint}/nanos")
110
+ stat.atime.usec.should == atime.usec
111
+ stat.atime.should == atime
112
+ stat.ctime.should == ctime
113
+ stat.mtime.should == mtime
114
+ end
115
+ end
116
116
  end
117
117
 
118
+ it "should set file access and modification times subsecond resolution" do
119
+ atime,mtime = usec_times(60,600)
120
+
121
+ atime_ns = (atime.to_i * (10**9)) + (atime.nsec)
122
+ mtime_ns = (mtime.to_i * (10**9)) + (mtime.nsec)
123
+
124
+ mockfs.stub(:getattr).with(anything(),"/usec").and_return(file_stat)
125
+ mockfs.should_receive(:utimens).with(anything,"/usec",atime_ns,mtime_ns)
126
+
127
+ with_fuse(mountpoint,mockfs) do
128
+ File.utime(atime,mtime,"#{mountpoint}/usec").should == 1
129
+ end
130
+ end
118
131
  it "should set file access and modification times" do
119
132
 
120
133
  atime = Time.now()
@@ -134,12 +147,12 @@ describe RFuse::Fuse do
134
147
 
135
148
  it "should create files" do
136
149
 
137
- mockfs.stub(:getattr).with(anything(),"/newfile").and_return(nil,file_stat)
138
- mockfs.should_receive(:mknod).with(anything(),"/newfile",file_mode(0644),0,0)
139
-
140
- with_fuse(mountpoint,mockfs) do
150
+ mockfs.stub(:getattr).with(anything(),"/newfile").and_return(nil,file_stat)
151
+ mockfs.should_receive(:mknod).with(anything(),"/newfile",file_mode(0644),0,0)
152
+
153
+ with_fuse(mountpoint,mockfs) do
141
154
  File.open("#{mountpoint}/newfile","w",0644) { |f| }
142
- end
155
+ end
143
156
  end
144
157
 
145
158
  # ruby doesn't seem to have a native method to create these
@@ -158,7 +171,7 @@ describe RFuse::Fuse do
158
171
  end
159
172
 
160
173
  }
161
-
174
+
162
175
  reads = 0
163
176
  mockfs.stub(:read) { |ctx,path,size,offset,ffi|
164
177
  reads += 2
@@ -173,4 +186,22 @@ describe RFuse::Fuse do
173
186
  end
174
187
  end
175
188
  end
189
+
190
+ context "exceptions" do
191
+
192
+ it "should capture exceptions appropriately" do
193
+
194
+ mockfs.should_receive(:getattr).with(anything(),"/exceptions").and_raise(RuntimeError)
195
+
196
+ with_fuse(mountpoint,mockfs) do
197
+ begin
198
+ File.stat("#{mountpoint}/exceptions")
199
+ raise "should not get here"
200
+ rescue Errno::ENOENT
201
+ #all good
202
+ end
203
+ end
204
+ end
205
+
206
+ end
176
207
  end
data/spec/spec_helper.rb CHANGED
@@ -47,5 +47,17 @@ module RFuseHelper
47
47
  def tempmount()
48
48
  Dir.mktmpdir("rfuse-spec")
49
49
  end
50
+
51
+ # Generate a set of times with non-zero usec values
52
+ def usec_times(*increments)
53
+ increments.collect { |inc|
54
+ begin
55
+ time = Time.now() + inc
56
+ sleep(0.001)
57
+ end until time.usec != 0
58
+ time
59
+ }
60
+ end
61
+
50
62
  end
51
63
  include RFuseHelper
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rfuse
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.0.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-08-06 00:00:00.000000000 Z
12
+ date: 2012-08-09 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rake
@@ -91,16 +91,17 @@ dependencies:
91
91
  - - ! '>='
92
92
  - !ruby/object:Gem::Version
93
93
  version: '0'
94
- description: Ruby language binding for FUSE. It was forked from rfuse and modified
95
- for Ruby 1.9.2.
94
+ description: Write userspace filesystems in Ruby
96
95
  email:
97
96
  - grant@lastweekend.com.au
98
97
  executables: []
99
98
  extensions:
100
99
  - ext/rfuse/extconf.rb
101
- extra_rdoc_files: []
100
+ extra_rdoc_files:
101
+ - CHANGES.md
102
102
  files:
103
103
  - .gitignore
104
+ - .yardopts
104
105
  - CHANGES.md
105
106
  - Gemfile
106
107
  - LICENSE
@@ -127,6 +128,7 @@ files:
127
128
  - ext/rfuse/rfuse_mod.c
128
129
  - lib/rfuse-ng.rb
129
130
  - lib/rfuse.rb
131
+ - lib/rfuse/compat.rb
130
132
  - lib/rfuse/version.rb
131
133
  - lib/rfuse_ng.rb
132
134
  - rfuse.gemspec
@@ -161,4 +163,4 @@ signing_key:
161
163
  specification_version: 3
162
164
  summary: Ruby language binding for FUSE
163
165
  test_files: []
164
- has_rdoc:
166
+ has_rdoc: yard