rfuse 1.2.3 → 2.0.0.ffilibfuse

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f9bbd56ab8bb580e8c2a40b19b15a75bf7afad004296c72d3e8157e0a6fbb223
4
- data.tar.gz: 1eeef7cb6a0f6a874da3b87d99452af3737cfa7d973650ccb5eaed992174a48f
3
+ metadata.gz: 21bf2d68b3864f3d86fcb6aee697228b006a6195d3b50e75db16f4f57153d7e9
4
+ data.tar.gz: e277766b10e550cc2e249fd9e71a42de6def695fdc5d8c5ad1a8140389851948
5
5
  SHA512:
6
- metadata.gz: 124adc8292a4f1a017e3034d51d645486629992f97ff8b106366929a752b36bf3f25e228001cb8af21a5f7a98147c430bbf2de226d31f7e4392c4bba83151b86
7
- data.tar.gz: 11b76dd86a7b1687ef877c6d660a71c041d728341bb5e3200da5a97f6ba80a64102fd2134338f9c7edca67106688ba0a9109fc76024c2fb800254b33f7994d3d
6
+ metadata.gz: ef0321ae2ae040627784f18726ce368a30225b7ab081c3c67ea10293152387b3e54ec29c4ddd04654f3df2b7acc84dd81864be9b844f402aaec564e3d1b63f50
7
+ data.tar.gz: 4040eeb235b97fc83942dc5757ce3021c30732547f8368c1f1cfc8c90a1f2be9c90f529ae03e7c29cb6f77947439892178a7dd02906709da07830c2969e1d420
data/.yardopts CHANGED
@@ -1,4 +1,5 @@
1
1
  --no-private
2
2
  --markup=markdown
3
3
  -
4
- CHANGES.md
4
+ CHANGELOG.md
5
+
@@ -1,3 +1,9 @@
1
+ 2.0.0 - 2023/01 UNMAINTAINED
2
+ -------------------
3
+ * Uses ffi-libfuse instead of local C extension
4
+ * Starting filesystems through RFuse.main
5
+ * Require Ruby 2.7+
6
+
1
7
  1.2.0 - 2020/09
2
8
  -------------------
3
9
  * Fix builds on recent linux
data/README.md CHANGED
@@ -1,60 +1,32 @@
1
- RFuse
1
+ RFuse - DEPRECATED
2
2
  ===============
3
3
 
4
- http://rubygems.org/gems/rfuse
5
- ![Gem Version](https://badge.fury.io/rb/rfuse.png)
4
+ ### DEPRECATED
6
5
 
7
- Ruby FUSE binding
6
+ This gem exists and is maintained for backwards compatibility only.
8
7
 
9
- 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.
8
+ Existing RFuse filesystems are recommended to refactor to depend directly on
9
+ [ffi_libfuse](http://rubygems.org/gems/ffi-libfuse) which supports Fuse 3 and Macfuse
10
10
 
11
- This library provides the low-level fuse operations as callbacks into a ruby object.
11
+ {RFuse::Adapter} documents the fuse method signatures that are different to ffi-libfuse
12
12
 
13
- For a more ruby-ish API for creating filesystems see {http://rubygems.org/gems/rfusefs RFuseFS} which is built on RFuse and takes care of some of the heavy lifting.
14
-
15
- Dependencies
16
- --------------
17
-
18
- * Ruby 2.5+
19
- * Fuse 2.8+
20
-
21
- Installation
22
13
  ---------------
23
14
 
24
- gem install rfuse
25
-
26
- Creating a filesystem
27
- ---------------------------
28
-
29
- Create your filesystem as a class implementing the abstract FUSE methods from {RFuse::Fuse} as necessary.
30
-
31
- Run it with {RFuse.main}
15
+ ### Dependencies
32
16
 
33
- For a sample filesystem see sample/test-ruby.rb
34
-
35
- To run the example:
36
-
37
- mkdir /tmp/fuse
38
- ruby sample/test-ruby.rb /tmp/fuse
39
-
40
- there should be an empty in-memory filesystem mounted at `/tmp/fuse`
17
+ * Ruby 2.7+
18
+ * Fuse 2.8+ / Fuse 3 / MACFuse
41
19
 
42
20
  HISTORY
43
- ======
44
- This project is forked from rfuse-ng which was forked from the original rfuse.
21
+ ============
22
+
23
+ This project was forked from rfuse-ng which was forked from the original rfuse.
45
24
 
46
25
  RFuse-NG: Tamás László Fábián <giganetom@gmail.com> et al on Github
47
26
 
48
27
  Original Rfuse (@rubyforge): Peter Schrammel AT gmx.de
49
28
 
50
- See also {file:CHANGES.md}
29
+ See also {file:CHANGELOG.md}
51
30
 
52
- CONTRIBUTING
31
+ ~~CONTRIBUTING~~
53
32
  ============
54
-
55
- 1. Fork it on {http://github.com/lwoggardner/rfuse on GitHub}
56
- 2. Create your feature branch (`git checkout -b my-new-feature`)
57
- 3. Write some specs and make them pass
58
- 4. Commit your changes (`git commit -am 'Added some feature'`)
59
- 5. Push to the branch (`git push origin my-new-feature`)
60
- 6. Create new Pull Request
@@ -0,0 +1,31 @@
1
+ module RFuse
2
+ # Wrapper for FFI:Flock back to RFuse
3
+ class Flock
4
+
5
+ attr_reader :delegate
6
+
7
+ def initialize(ffi_flock)
8
+ @delegate = ffi_flock
9
+ end
10
+
11
+ def l_type
12
+ FFI::Flock::Enums::LockType.to_h[delegate.type] || 0
13
+ end
14
+
15
+ def l_whence
16
+ FFI::Flock::Enums::SeekWhenceShort.to_h[delegate.whence] || 0
17
+ end
18
+
19
+ def l_start
20
+ delegate.start
21
+ end
22
+
23
+ def l_len
24
+ delegate.len
25
+ end
26
+
27
+ def l_pid
28
+ delegate.pid
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'version'
4
+ begin
5
+ require 'ffi/libfuse/gem_helper'
6
+ rescue LoadError
7
+ # allow bundle install to run
8
+ end
9
+
10
+ module RFuse
11
+ # @!visibility private
12
+ MAIN_BRANCH = 'master'
13
+ GEM_VERSION, =
14
+ if defined? FFI::Libfuse
15
+ FFI::Libfuse::GemHelper.gem_version(version: VERSION, main_branch: MAIN_BRANCH)
16
+ else
17
+ "#{VERSION}.pre"
18
+ end
19
+ end
20
+
@@ -0,0 +1,318 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'ffi/libfuse'
4
+
5
+ module RFuse
6
+ # Filler object
7
+ # @see readdir
8
+ class Filler < FFI::Libfuse::Adapter::Ruby::ReaddirFiller
9
+
10
+ def push(name, stat, offset = 0)
11
+ fill(name, stat: stat, offset: offset)
12
+ end
13
+ end
14
+
15
+ # Map callback methods to be compatible for legacy RFuse
16
+ #
17
+ # All native {FuseOperations} callback signatures are amended to take {FuseContext} as the first argument.
18
+ #
19
+ # A subset of callbacks are documented here where their signatures are further altered to match RFuse
20
+ #
21
+ # RFuse did not support Fuse3
22
+ # adapter module
23
+ module Adapter
24
+
25
+ # Converts Fuse2 calls into RFuse calls
26
+ # @!visibility private
27
+ module Prepend
28
+
29
+ include FFI::Libfuse::Adapter
30
+
31
+ def readdir(ctx, path, buf, filler, offset, ffi)
32
+
33
+ rd_filler = Filler.new(buf, filler, fuse3: false)
34
+ super(ctx, path, rd_filler, offset, ffi)
35
+ 0
36
+ end
37
+
38
+ def readlink(ctx, path, buf, size)
39
+ link = super(ctx, path)
40
+
41
+ return -Errno::ERANGE::Errno unless link.size <= size
42
+
43
+ buf.write_bytes(link)
44
+ 0
45
+ end
46
+
47
+ def mknod(ctx, path, mode, dev)
48
+ super(ctx, path, mode, FFI::Device.major(dev), FFI::Device.minor(dev))
49
+ 0
50
+ end
51
+
52
+ def getattr(ctx, path, stat_buf)
53
+ result = super(ctx, path)
54
+ raise Errno::ENOENT unless result
55
+
56
+ stat_buf.fill(result)
57
+ 0
58
+ end
59
+
60
+ def fgetattr(ctx, path, stat_buf, ffi)
61
+ stat_buf.fill(super(ctx, path, ffi))
62
+ 0
63
+ end
64
+
65
+ # Set file atime, mtime via super as per {Ruby#utimens}
66
+ def utimens(ctx, path, times)
67
+ atime, mtime = Stat::TimeSpec.fill_times(times[0, 2], 2)
68
+
69
+ begin
70
+ super(ctx, path, atime.nanos, mtime.nanos)
71
+ rescue NoMethodError
72
+ utime(ctx, path, atime.sec, mtime.sec)
73
+ end
74
+ 0
75
+ end
76
+
77
+ def read(ctx, path, buf, size, offset, info)
78
+ res = super(ctx, path, size, offset, info)
79
+
80
+ return -Errno::ERANGE::Errno unless res.size <= size
81
+
82
+ buf.write_bytes(res)
83
+ res.size
84
+ end
85
+
86
+ def write(ctx, path, buf, size, offset, info)
87
+ super(ctx, path, buf.read_bytes(size), offset, info)
88
+ end
89
+
90
+ def statfs(ctx, path, statfs_buf)
91
+ statfs_buf.fill(super(ctx, path))
92
+ 0
93
+ end
94
+
95
+ def setxattr(ctx, path, name, value, _size, flags)
96
+ super(ctx, path, name, value, FFI::Libfuse::XAttr.to_h[flags] || 0)
97
+ 0
98
+ end
99
+
100
+ def getxattr(ctx, path, name, buf, size)
101
+ FFI::Libfuse::Adapter::Ruby.getxattr(buf, size) do
102
+ super(ctx, path, name)
103
+ end
104
+ end
105
+
106
+ def listxattr(ctx, path, buf, size)
107
+ FFI::Libfuse::Adapter::Ruby.listxattr(buf, size) do
108
+ super(ctx, path)
109
+ end
110
+ end
111
+
112
+ %i[create open opendir].each do |fuse_method|
113
+ define_method(fuse_method) do |*args|
114
+ super(*args)
115
+ store_handle(args.last&.fh)
116
+ 0
117
+ end
118
+ end
119
+
120
+ %i[release releasedir].each do |fuse_method|
121
+ define_method(fuse_method) do |*args|
122
+ super(*args)
123
+ 0
124
+ rescue NoMethodError
125
+ # OK, just release the file handle
126
+ 0
127
+ ensure
128
+ release_handle(args.last&.fh)
129
+ end
130
+ end
131
+
132
+ def init(ctx, fuse_conn_info)
133
+ super
134
+ self
135
+ end
136
+
137
+ def lock(ctx, path, ffi, cmd, lock)
138
+ super(ctx, path, ffi, FFI::Flock::Enums::LockCmd.to_h[cmd], Flock.new(lock))
139
+ 0
140
+ end
141
+
142
+ def fuse_respond_to?(fuse_callback)
143
+ return true if super
144
+
145
+ case fuse_callback
146
+ when :release
147
+ # Ensure file handles to be cleaned up at release
148
+ %i[open create].any? { |m| super(m) }
149
+ when :release_dir
150
+ super(:open_dir)
151
+ when :utimens
152
+ super(:utime)
153
+ else
154
+ false
155
+ end
156
+ end
157
+
158
+ def open_files
159
+ handles
160
+ end
161
+
162
+ private
163
+
164
+ # Prevent filehandles from being GCd
165
+ def handles
166
+ @handles ||= Set.new.compare_by_identity
167
+ end
168
+
169
+ def store_handle(file_handle)
170
+ handles << file_handle if file_handle
171
+ end
172
+
173
+ def release_handle(file_handle)
174
+ handles.delete(file_handle)
175
+ end
176
+ end
177
+
178
+
179
+ # @!method readdir(context,path,filler,offset,ffi)
180
+ #
181
+ # List contents of a directory
182
+ #
183
+ # @param [Context] context
184
+ # @param [String] path
185
+ # @param [Filler] filler
186
+ # @param [Fixnum] offset
187
+ # @param [FileInfo] ffi
188
+ #
189
+ # @return [void]
190
+ # @raise [Errno]
191
+
192
+ # @!method readlink(path)
193
+ # @abstract
194
+ # @param [String] path
195
+ # @return [String] the link target
196
+
197
+ # @!method mknod(context,path,mode,major,minor)
198
+ # Create a file node
199
+ # @abstract Fuse Operation {http://fuse.sourceforge.net/doxygen/structfuse__operations.html#1465eb2268cec2bb5ed11cb09bbda42f mknod}
200
+ #
201
+ # @param [Context] context
202
+ # @param [String] path
203
+ # @param [Integer] mode type & permissions
204
+ # @param [Integer] major
205
+ # @param [Integer] minor
206
+ #
207
+ # @return[void]
208
+ #
209
+ # This is called for creation of all non-directory, non-symlink nodes. If the filesystem defines {#create}, then for regular files that will be called instead.
210
+
211
+ # @!method getattr(ctx,path)
212
+ # @param [FuseContext ctx]
213
+ # @param [String] path
214
+ # @return [Stat] the stat information (or something that can fill a stat)
215
+
216
+ # @!method fgetattr(ctx,path,ffi)
217
+ # @param [FuseContext ctx]
218
+ # @param [String] path
219
+ # @return [Stat] the stat information (or something that can fill a stat)
220
+
221
+ # @!method utime(ctx,path,atime,mtime)
222
+ # @abstract
223
+ # @deprecated prefer {utimens}
224
+ #
225
+ # @param [Context] context
226
+ # @param [String] path
227
+ # @param [Integer] atime access time in seconds or nil if only setting mtime
228
+ # @param [Integer] mtime modification time in seconds or nil if only setting atime
229
+ #
230
+ # @return [void]
231
+ # @raise [Errno]
232
+
233
+ # @!method utimens(ctx,path,atime,mtime)
234
+ # @abstract
235
+ # @param [Context] context
236
+ # @param [String] path
237
+ # @param [Integer] atime access time in nanoseconds or nil if only setting mtime
238
+ # @param [Integer] mtime modification time in nanoseconds or nil if only setting atime
239
+ #
240
+ # @return [void]
241
+ # @raise [Errno]
242
+
243
+ # @!method read(ctx,path,size,offset,info)
244
+ # @abstract
245
+ # @param [FuseContext ctx]
246
+ # @param [String] path
247
+ # @param [Integer] size
248
+ # @param [Integer] offset
249
+ # @param [FuseFileInfo] info
250
+ #
251
+ # @return [String] the data, expected to be exactly size bytes
252
+
253
+ # @!method write(ctx,path,data,offset,info)
254
+ # @abstract Fuse operation {http://fuse.sourceforge.net/doxygen/structfuse__operations.html#897d1ece4b8b04c92d97b97b2dbf9768 write}
255
+ # Write data to an open file
256
+ #
257
+ # @param [Context] context
258
+ # @param [String] path
259
+ # @param [String] data
260
+ # @param [Integer] offset
261
+ # @param [FileInfo] ffi
262
+ #
263
+ # @return [Integer] exactly the number of bytes requested except on error
264
+ # @raise [Errno]
265
+
266
+ # Set extended attributes
267
+ #
268
+ # @!method setxattr(context,path,name,data,flags)
269
+ # @abstract Fuse operation {http://fuse.sourceforge.net/doxygen/structfuse__operations.html#988ced7091c2821daa208e6c96d8b598 setxattr}
270
+ # @param [Context] context
271
+ # @param [String] path
272
+ # @param [String] name
273
+ # @param [String] data
274
+ # @param [Integer] flags
275
+ #
276
+ # @return [void]
277
+ # @raise [Errno]
278
+
279
+ # @method getxattr(ctx,path,name)
280
+ # @abstract
281
+ # @param [FuseContext ctx]
282
+ # @param [String] path
283
+ # @param [String] name the attribute name
284
+ # @return [String|nil] the attribute value or nil if it does not exist
285
+
286
+ # @method listxattr(ctx,path)
287
+ # @abstract
288
+ # @param [FuseContext ctx]
289
+ # @param [String] path
290
+ # @return [Array<String>] list of attribute names for path
291
+
292
+ # @!visibility private
293
+
294
+ def fuse_wrappers(*wrappers)
295
+ # Change signatures to match legacy RFuse
296
+ wrappers.unshift({
297
+ wrapper: proc { |_fm, *args, &b| b.call(FFI::Libfuse::FuseContext.get, *args) },
298
+ excludes: %i[destroy]
299
+ })
300
+
301
+ return wrappers unless defined?(super)
302
+
303
+ super(*wrappers)
304
+ end
305
+
306
+ def default_errno
307
+ Errno::ENOENT::Errno
308
+ end
309
+
310
+ def self.included(mod)
311
+ mod.prepend(Prepend)
312
+ mod.include(FFI::Libfuse::Adapter::Fuse3Support)
313
+ mod.include(FFI::Libfuse::Adapter::Safe)
314
+ end
315
+
316
+ end
317
+ end
318
+
data/lib/rfuse/stat.rb ADDED
@@ -0,0 +1,43 @@
1
+ module RFuse
2
+ # Helper class to return from :getattr method
3
+ class Stat
4
+
5
+ # See FFI::Stat constants
6
+ def self.const_missing(const)
7
+ return super unless FFI::Stat.const_defined?(const)
8
+ FFI::Stat.const_get(const)
9
+ end
10
+
11
+ # @param [Fixnum] mode file permissions
12
+ # @param [Hash<Symbol,Fixnum>] values initial values for other attributes
13
+ #
14
+ # @return [Stat] representing a directory
15
+ def self.directory(mode = 0, values = {})
16
+ new(S_IFDIR, mode, values)
17
+ end
18
+
19
+ # @param [Fixnum] mode file permissions
20
+ # @param [Hash<Symbol,Fixnum>] values initial values for other attributes
21
+ #
22
+ # @return [Stat] representing a regular file
23
+ def self.file(mode = 0, values = {})
24
+ new(S_IFREG, mode, values)
25
+ end
26
+
27
+ # @return [Integer] see stat(2)
28
+ attr_accessor :uid, :gid, :mode, :size, :dev, :ino, :nlink, :rdev, :blksize, :blocks
29
+
30
+ # @return [Integer, Time] see stat(2)
31
+ attr_accessor :atime, :mtime, :ctime
32
+
33
+ def initialize(type, permissions, values = {})
34
+ values[:mode] = ((type & S_IFMT) | (permissions & 0o7777))
35
+ @uid, @gid, @size, @mode, @atime, @mtime, @ctime, @dev, @ino, @nlink, @rdev, @blksize, @blocks = Array.new(
36
+ 13, 0
37
+ )
38
+ values.each_pair do |k, v|
39
+ instance_variable_set("@#{k}", v)
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,33 @@
1
+ module RFuse
2
+
3
+ # Helper class to return from :statfs (eg for df output)
4
+ # All attributes are Integers and default to 0
5
+ class StatVfs
6
+ # @return [Integer]
7
+ attr_accessor :f_bsize, :f_frsize, :f_blocks, :f_bfree, :f_bavail
8
+
9
+ # @return [Integer]
10
+ attr_accessor :f_files, :f_ffree, :f_favail, :f_fsid, :f_flag, :f_namemax
11
+
12
+ # values can be symbols or strings but drop the pointless f_ prefix
13
+ def initialize(values = {})
14
+ @f_bsize, @f_frsize, @f_blocks, @f_bfree, @f_bavail, @f_files, @f_ffree, @f_favail, @f_fsid, @f_flag, @f_namemax = Array.new(
15
+ 13, 0
16
+ )
17
+ values.each_pair do |k, v|
18
+ prefix = k.to_s.start_with?('f_') ? '' : 'f_'
19
+ instance_variable_set("@#{prefix}#{k}", v)
20
+ end
21
+ end
22
+
23
+ def respond_to_missing?(method, private=false)
24
+ !method.start_with?('f_') && respond_to?("f_#{method}", private)
25
+ end
26
+
27
+ def method_missing(method, *args)
28
+ return super if method.start_with?('f_')
29
+
30
+ send("f_#{method}", *args)
31
+ end
32
+ end
33
+ end
data/lib/rfuse/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module RFuse
2
- VERSION = "1.2.3"
2
+ VERSION = "2.0.0"
3
3
  end