ffi-libfuse 0.0.1.rctest12 → 0.1.0.rc20220550

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +3 -1
  3. data/CHANGES.md +14 -0
  4. data/LICENSE +21 -0
  5. data/README.md +127 -44
  6. data/lib/ffi/accessors.rb +6 -6
  7. data/lib/ffi/boolean_int.rb +27 -0
  8. data/lib/ffi/devt.rb +23 -0
  9. data/lib/ffi/encoding.rb +38 -0
  10. data/lib/ffi/gnu_extensions.rb +1 -1
  11. data/lib/ffi/libfuse/ackbar.rb +3 -3
  12. data/lib/ffi/libfuse/adapter/context.rb +12 -10
  13. data/lib/ffi/libfuse/adapter/fuse2_compat.rb +52 -51
  14. data/lib/ffi/libfuse/adapter/fuse3_support.rb +0 -1
  15. data/lib/ffi/libfuse/adapter/ruby.rb +499 -148
  16. data/lib/ffi/libfuse/adapter/safe.rb +1 -1
  17. data/lib/ffi/libfuse/adapter.rb +1 -2
  18. data/lib/ffi/libfuse/callbacks.rb +1 -1
  19. data/lib/ffi/libfuse/filesystem/accounting.rb +116 -0
  20. data/lib/ffi/libfuse/filesystem/mapped_dir.rb +74 -0
  21. data/lib/ffi/libfuse/filesystem/mapped_files.rb +141 -0
  22. data/lib/ffi/libfuse/filesystem/pass_through_dir.rb +55 -0
  23. data/lib/ffi/libfuse/filesystem/pass_through_file.rb +45 -0
  24. data/lib/ffi/libfuse/filesystem/utils.rb +102 -0
  25. data/lib/ffi/libfuse/filesystem/virtual_dir.rb +306 -0
  26. data/lib/ffi/libfuse/filesystem/virtual_file.rb +94 -0
  27. data/lib/ffi/libfuse/filesystem/virtual_fs.rb +188 -0
  28. data/lib/ffi/libfuse/filesystem/virtual_node.rb +101 -0
  29. data/lib/ffi/libfuse/filesystem.rb +25 -0
  30. data/lib/ffi/libfuse/fuse2.rb +21 -21
  31. data/lib/ffi/libfuse/fuse3.rb +12 -12
  32. data/lib/ffi/libfuse/fuse_args.rb +69 -34
  33. data/lib/ffi/libfuse/fuse_buffer.rb +128 -26
  34. data/lib/ffi/libfuse/fuse_callbacks.rb +1 -5
  35. data/lib/ffi/libfuse/fuse_common.rb +55 -61
  36. data/lib/ffi/libfuse/fuse_config.rb +134 -143
  37. data/lib/ffi/libfuse/fuse_conn_info.rb +310 -134
  38. data/lib/ffi/libfuse/fuse_context.rb +45 -3
  39. data/lib/ffi/libfuse/fuse_operations.rb +43 -19
  40. data/lib/ffi/libfuse/fuse_version.rb +10 -6
  41. data/lib/ffi/libfuse/main.rb +80 -37
  42. data/lib/ffi/libfuse/version.rb +1 -1
  43. data/lib/ffi/libfuse.rb +13 -4
  44. data/lib/ffi/ruby_object.rb +1 -1
  45. data/lib/ffi/stat/constants.rb +9 -0
  46. data/lib/ffi/stat/native.rb +36 -6
  47. data/lib/ffi/stat/time_spec.rb +26 -10
  48. data/lib/ffi/stat.rb +111 -22
  49. data/lib/ffi/stat_vfs.rb +59 -1
  50. data/lib/ffi/struct_wrapper.rb +22 -1
  51. data/sample/hello_fs.rb +54 -0
  52. data/sample/memory_fs.rb +5 -181
  53. data/sample/no_fs.rb +20 -21
  54. data/sample/pass_through_fs.rb +30 -0
  55. metadata +77 -4
  56. data/lib/ffi/libfuse/adapter/thread_local_context.rb +0 -36
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8cc15469bfd68dbe59e7b5dfab82fc1b34fe51e7e3b61e44c686d8a8025ec5ff
4
- data.tar.gz: 691758dbb28272d60cfc1f5f2f37e8cef56de15412706b303f181c5b31cdf03f
3
+ metadata.gz: d71e73d4261d20d3e8318fa01517342da232c0167f4febd73249fb99bb314e15
4
+ data.tar.gz: 5d478d73897b3d590b1c23ddc656f416574865a53db88bb078ddc949c310bc81
5
5
  SHA512:
6
- metadata.gz: 6dfefe8f3a512dcaa6f2ca34c3f45cba0fb61e17af4dc0114b9fec6a1adc7a63b0f156415b69e256b9bae97d5a9574626001e285f85ddaae985df84d840b2ca3
7
- data.tar.gz: cb4d1f15b5220c9ec4c16a2c0874c082b17ca23a9f725d4bc8e62f5eb69f1e02abaa3960dfef6711dd059ef38e7d97abe2e4af69e68a630bf8728ab3afbfb0ad
6
+ metadata.gz: bfc6ff5b7a9e706be919eaf35fbdfe7b3c2575c68e03008f2381c723ab6896f9826047427e78e8915b90985c0ce26e04e83e639817dd5b7f40bbe1adc69e8aa8
7
+ data.tar.gz: ac2c8aa63ee627f73cd2d8a06071273bbbc8af64f9ae21862ff6f9bc235c7a0aa031c4fe601aea2b7e3ad4f93ef8ffd248fba35fbcf7cbabf422c37727995f72
data/.yardopts CHANGED
@@ -1 +1,3 @@
1
- --markup=markdown
1
+ --markup=markdown
2
+ -
3
+ CHANGES.md
data/CHANGES.md ADDED
@@ -0,0 +1,14 @@
1
+ 0.1.0 / 2022-04
2
+ ------------------
3
+
4
+ #### BREAKING changes
5
+ * Changed option parsing.
6
+
7
+ {FFI::Libfuse::Main#fuse_options} now takes a FuseArgs parameter and fuse_opt_proc is not used
8
+
9
+ #### New Features
10
+ * Implemented helper filesystems in {FFI::Libfuse::Filesystem}
11
+
12
+ #### Fixes
13
+ * Test on OSX with macFuse
14
+ * Lots
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2021 Grant Gardner
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md CHANGED
@@ -2,61 +2,160 @@
2
2
 
3
3
  Ruby FFI Binding for [libfuse](https://github.com/libfuse/libfuse)
4
4
 
5
- ## Writing a FUSE Filesystem
5
+ ## Requirements
6
6
 
7
- Create a class that implements the abstract methods of {FFI::Libfuse::Main} and {FFI::Libfuse::FuseOperations}
7
+ * Ruby 2.7
8
+ * Linux: libfuse (Fuse2) or libfuse3 (Fuse3)
9
+ * MacOS: macFuse (https://osxfuse.github.io/)
8
10
 
9
- Call {FFI::Libfuse.fuse_main} to start the filesystem
11
+ ## Building a FUSE Filesystem
12
+
13
+ Install the gem
14
+
15
+ ```bash
16
+ gem install ffi-libfuse
17
+ ```
18
+
19
+ Create a filesystem class
20
+
21
+ * implement FUSE callbacks for filesystem operations satisfying {FFI::Libfuse::FuseOperations}
22
+ * recommend including {FFI::Libfuse::Adapter::Ruby} to add some ruby sugar and safety to the native FUSE Callbacks
23
+ * recommend including {FFI::Libfuse::Adapter::Fuse2Compat} for compatibility with Fuse2/macFuse
24
+ * implement {FFI::Libfuse::Main} configuration methods, eg to parse custom options with {FFI::Libfuse::FuseArgs#parse!}
25
+ (as altered by any included adapters from {FFI::Libfuse::Adapter})
26
+ * Provide an entrypoint to start the filesystem using {FFI::Libfuse::Main.fuse_main}
27
+
28
+ {FFI::Libfuse::Filesystem} contains additional classes and modules to help build and compose filesystems
29
+
30
+ <!-- SAMPLE BEGIN: sample/hello_fs.rb -->
31
+ *sample/hello_fs.rb*
10
32
 
11
33
  ```ruby
34
+ #!/usr/bin/env ruby
35
+ # frozen_string_literal: true
36
+
12
37
  require 'ffi/libfuse'
13
38
 
14
- class MyFS
15
- # include helpers to abstract away from the native C callbacks to more idiomatic Ruby
39
+ # Hello World!
40
+ class HelloFS
16
41
  include FFI::Libfuse::Adapter::Ruby
17
-
18
- # ... FUSE callbacks .... quacks like a FFI:Libfuse::FuseOperations
19
- def getattr(*args)
20
- #...
42
+ include FFI::Libfuse::Adapter::Fuse2Compat
43
+
44
+ # FUSE Configuration methods
45
+
46
+ def fuse_options(args)
47
+ args.parse!({ 'subject=' => :subject }) do |key:, value:, **|
48
+ raise FFI::Libfuse::Error, 'subject option must be at least 2 characters' unless value.size >= 2
49
+
50
+ @subject = value if key == :subject
51
+ :handled
52
+ end
21
53
  end
22
-
23
- def readdir(*args)
24
- #...
54
+
55
+ def fuse_help
56
+ '-o subject=<subject> a target to say hello to'
57
+ end
58
+
59
+ def fuse_configure
60
+ @subject ||= 'World!'
61
+ @content = "Hello #{@subject}\n"
62
+ end
63
+
64
+ # FUSE callbacks
65
+
66
+ def getattr(path, stat, *_args)
67
+ case path
68
+ when '/'
69
+ stat.directory(mode: 0o550)
70
+ when '/hello.txt'
71
+ stat.file(mode: 0o440, size: @content.size)
72
+ else
73
+ raise Errno::ENOENT
74
+ end
75
+ end
76
+
77
+ def readdir(_path, *_args)
78
+ yield 'hello.txt'
79
+ end
80
+
81
+ def read(_path, *_args)
82
+ @content
25
83
  end
26
-
27
84
  end
28
85
 
29
- FFI::Libfuse::fuse_main(operations: MyFS.new) if __FILE__ == $0
86
+ # Start the file system
87
+ FFI::Libfuse.fuse_main(operations: HelloFS.new) if __FILE__ == $0
88
+
30
89
  ```
90
+ <!-- SAMPLE END: sample/hello_fs.rb -->
31
91
 
32
- # Fuse2/Fuse3 compatibility
92
+ Mount the filesystem
93
+
94
+ ```bash
95
+ hello_fs.rb -h # show help
96
+ hello_fs.rb /mnt/hello # run deamonized, mounted at /mnt/hello
97
+ ```
98
+
99
+ Do file things
100
+
101
+ ```bash
102
+ ls /mnt/hello
103
+ cat /mnt/hello/hello.txt
104
+ ```
105
+
106
+ ## Fuse2/Fuse3 compatibility
33
107
 
34
108
  FFI::Libfuse will prefer Fuse3 over Fuse2 by default. See {FFI::Libfuse::LIBFUSE}
35
109
 
36
- For writing filesystems with backwards/forwards compatibility between fuse version see
37
- {FFI::Libfuse::Adapter::Fuse2Compat} and {FFI::Libfuse::Adapter::Fuse3Support}
110
+ New filesystems should write for Fuse3 API and include {FFI::Libfuse::Adapter::Fuse2Compat} for backwards compatibility
111
+
112
+ Alternatively filesystems written against Fuse2 API can include {FFI::Libfuse::Adapter::Fuse3Support}
38
113
 
39
114
  ## MACFuse
40
115
 
41
- [macFUSE](https://osxfuse.github.io/) (previously OSXFuse) supports a superset of the Fuse2 api so FFI::Libfuse is
42
- intended to work in that environment.
116
+ [macFUSE](https://osxfuse.github.io/) (previously OSXFuse) supports a superset of the Fuse2 api
117
+
118
+ **TODO** Implement macFuse extensions
119
+
120
+
121
+ # Under the hood
122
+
123
+ {FFI::Libfuse} provides raw access to the underlying libfuse but there some constraints imposed by Ruby.
124
+
125
+ ## Low-level functions re-implemented in Ruby
43
126
 
44
- # Multi-threading
127
+ The C functions fuse_main(), fuse_daemonize() and fuse_loop<_mt>() are re-implemented to provide
45
128
 
46
- Most Ruby filesystems are unlikely to benefit from multi-threaded operation so
47
- {FFI::Libfuse.fuse_main} as shown above injects the '-s' (single-thread) option by default.
129
+ * dynamic compatibility between Fuse2 and Fuse3
130
+ * support for multi-threading under MRI
131
+ * signal handling in ruby filesystem (eg HUP to reload)
48
132
 
49
- Pass the original options in directly if multi-threaded operation is desired for your filesystem
133
+ The `-o native' option will use the native C functions but only exists to assist with testing that FFI::Libfuse has
134
+ similar behaviour to C libfuse.
135
+
136
+ See {FFI::Libfuse::Main} and {FFI::Libfuse::FuseCommon}
137
+
138
+ ## Multi-threading
139
+
140
+ {FFI::Libfuse.fuse_main} forces the `-s` (single-thread) option to be set since most Ruby filesystems are
141
+ unlikely to benefit from the overhead caused by multi-threaded operation obtaining/releasing the GVL around each
142
+ callback.
143
+
144
+ {FFI::Libfuse::Main.fuse_main} does not pass any options by default and should be used in situations where
145
+ multi-threaded operations may be desirable.
50
146
 
51
147
  ```ruby
52
- FFI::Libfuse::fuse_main($0,*ARGV, operations: MyFS.new) if __FILE__ == $0
148
+ FFI::Libfuse::Main.fuse_main(operations: MyFS.new) if __FILE__ == $0
53
149
  ```
54
150
 
55
- The {FFI::Libfuse::ThreadPool} can be configured with `-o max_threads=<n>,max_idle_threads=<n>` options
151
+ The multi-thread loop uses {FFI::Libfuse::ThreadPool} to control thread usage and can be configured with options
152
+ `-o max_threads=<n>,max_idle_threads=<n>`
56
153
 
57
- Callbacks that are about to block (and release the GVL for MRI) should call {FFI::Libfuse::ThreadPool.busy}.
154
+ Callbacks that are about to block (and release the GVL for MRI) should call {FFI::Libfuse::ThreadPool.busy} which will
155
+ spawn additional worker threads as required.
58
156
 
59
- A typical scenario would be a filesystem where some callbacks are blocking on network IO.
157
+ Note that uncaught exceptions in callbacks will kill the worker thread and if all worker threads are dead the
158
+ file system will stop and unmount. In particular if the first callback raises an exception
60
159
 
61
160
  ```ruby
62
161
  def read(*args)
@@ -70,29 +169,13 @@ end
70
169
  [this discussion](http://fuse.996288.n3.nabble.com/GetAttr-calls-being-serialised-td11741.html)
71
170
  on the serialisation of `#getattr` and `#readdir` calls.
72
171
 
73
- ## Under the hood
172
+ **TODO** Build an example filesystem that makes use of multi-threading
74
173
 
75
- FFI::Libfuse tries to provide raw access to the underlying libfuse but there some constraints imposed by Ruby.
76
-
77
- The functions fuse_main(), fuse_daemonize() and fuse_loop<_mt>() are re-implemented in Ruby so we can provide
78
-
79
- * dynamic compatibility between Fuse2 and Fuse3
80
- * integrated support for multi-threading under MRI (see {FFI::Libfuse::ThreadPool})
81
- * signal handling in ruby filesystem (eg HUP to reload)
82
-
83
- Sending `-o native' will used the native C functions but this exists to assist with testing that FFI::Libfuse has
84
- similar behaviour to libfuse itself.
85
-
86
- See {FFI::Libfuse::Main} and {FFI::Libfuse::FuseCommon}
87
174
 
88
175
  ## Contributing
89
176
 
90
177
  Bug reports and pull requests are welcome on GitHub at https://github.com/lwoggardner/ffi-libfuse.
91
178
 
92
- ### TODO
93
- * Include a MetaFS, PathMapperFS etc (possibly a separate library)
94
- * Build a filesystem that can make use of multi-threaded operations
95
- * Test with macFUSE
96
179
 
97
180
  ## License
98
181
 
data/lib/ffi/accessors.rb CHANGED
@@ -20,8 +20,8 @@ module FFI
20
20
  #
21
21
  # Define a struct attribute reader for members
22
22
  # @param [Array<Symbol>] attrs the attribute names
23
- # @param [String] format
24
- # A format string containing a single %s to convert attr symbol to struct member
23
+ # @param [Proc|String] format
24
+ # A Proc, or format string containing a single %s, to convert attr to struct member name
25
25
  # @param [Boolean] simple
26
26
  # Controls how writer methods are defined using block
27
27
  # @param [Proc] block
@@ -33,7 +33,7 @@ module FFI
33
33
  # @return [void]
34
34
  def ffi_attr_reader(*attrs, format: '%s', simple: true, &block)
35
35
  attrs.each do |attr|
36
- member = (format % attr).to_sym
36
+ member = (format.respond_to?(:call) ? format.call(attr) : format % attr).to_sym
37
37
  ffi_attr_readers[attr] = member
38
38
  if !block
39
39
  define_method(attr) { self[member] }
@@ -47,7 +47,7 @@ module FFI
47
47
 
48
48
  # Define a struct attribute writer
49
49
  # @param [Array<Symbol>] attrs the attribute names
50
- # @param [String] format
50
+ # @param [String|Proc] format
51
51
  # A format string containing a single %s to convert attr symbol to struct member
52
52
  # @param [Boolean] simple
53
53
  # Controls how writer methods are defined using block
@@ -55,8 +55,8 @@ module FFI
55
55
  # An optional block to set the input value into the struct field.
56
56
  #
57
57
  # If simple is true then the struct field is set to the result of calling block with the input value,
58
- # otherwise the method is defined directly from the block. Use __method__[0..-2] to get the attribute name
59
- # and self.class.ffi_attr_writers[__method__[0..-2]] to get the struct field name
58
+ # otherwise the method is defined directly from the block. Use __method__[0..-1] to get the attribute name
59
+ # and self.class.ffi_attr_writers[__method__[0..-1]] to get the struct member name
60
60
  # @return [void]
61
61
  def ffi_attr_writer(*attrs, format: '%s', simple: true, &block)
62
62
  attrs.each do |attr|
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module FFI
4
+ # Converter generator for different sizes ints as boolean
5
+ class BooleanInt
6
+ include DataConverter
7
+ attr_reader :native_type
8
+
9
+ def initialize(int_type)
10
+ @native_type = FFI.find_type(int_type)
11
+ end
12
+
13
+ # Falsey = 0, Truthy = 1
14
+ def to_native(obj, _context)
15
+ obj ? 1 : 0
16
+ end
17
+
18
+ # Not Zero
19
+ def from_native(object_id, _context)
20
+ !object_id.zero?
21
+ end
22
+
23
+ %i[char short int long int8 int16 int32 int64].each do |t|
24
+ FFI.typedef(BooleanInt.new(t), "bool_#{t}".to_sym)
25
+ end
26
+ end
27
+ end
data/lib/ffi/devt.rb CHANGED
@@ -26,5 +26,28 @@ module FFI
26
26
  # @param [Integer] dev
27
27
  # @return [Integer] the minor component of dev
28
28
  attach_function :minor, "#{prefix}minor".to_sym, [:int], :int
29
+ rescue FFI::NotFoundError
30
+ case Platform::NAME
31
+ when 'x86_64-darwin'
32
+ # From https://github.com/golang/go/issues/8106 these functions are not defined on Darwin.
33
+ class << self
34
+ # define major(x) ((int32_t)(((u_int32_t)(x) >> 24) & 0xff))
35
+ def major(dev)
36
+ (dev >> 24) & 0xff
37
+ end
38
+
39
+ # define minor(x) ((int32_t)((x) & 0xffffff))
40
+ def minor(dev)
41
+ (dev & 0xffffff)
42
+ end
43
+
44
+ # define makedev(x,y) ((dev_t)(((x) << 24) | (y)))
45
+ def makedev(major, minor)
46
+ (major << 24) | minor
47
+ end
48
+ end
49
+ else
50
+ raise
51
+ end
29
52
  end
30
53
  end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'ffi'
4
+
5
+ module FFI
6
+ # DataConverter for reading/writing encoded strings
7
+ class Encoding
8
+ include DataConverter
9
+
10
+ attr_reader :encoding
11
+
12
+ # Create a dataconverter for the specified encoding
13
+ # @example
14
+ # module MyFFIModule
15
+ # extend FFI::Library
16
+ # typedef FFI::Encoding.for('utf8'), :utf8_string
17
+ # end
18
+ def self.for(encoding)
19
+ new(encoding)
20
+ end
21
+
22
+ def from_native(value, _ctx)
23
+ value.force_encoding(encoding)
24
+ end
25
+
26
+ def to_native(value, _ctx)
27
+ value.encode(encoding)
28
+ end
29
+
30
+ def native_type(_type = nil)
31
+ FFI::Type::STRING
32
+ end
33
+
34
+ def initialize(encoding)
35
+ @encoding = encoding
36
+ end
37
+ end
38
+ end
@@ -27,7 +27,7 @@ module FFI
27
27
  module GNUExtensions
28
28
  if FFI::Platform::IS_GNU
29
29
  extend FFI::Library
30
- ffi_lib 'libdl'
30
+ ffi_lib(%w[libdl.so libdl.so.2].flat_map { |l| [l, "#{FFI::Platform::NAME}-gnu/#{l}"] })
31
31
 
32
32
  # @!method dlopen(library,type)
33
33
  # @return [FFI::Pointer] library address, possibly NULL
@@ -29,15 +29,15 @@ module FFI
29
29
  end
30
30
 
31
31
  # @param [Hash<Symbol|String|Integer,String|Proc>] traps
32
- # Map of signal or signo to signal handler as per Signal.trap
32
+ # Map of signal or signo to signal handler as per {::Signal.trap}
33
33
  # @param [Boolean] force
34
- # If not set traps that are not currently set to 'DEFAULT' will be ignored
34
+ # Unless set, only installs traps currently set to 'DEFAULT'.
35
35
  def initialize(traps, force: false, signal: Signal)
36
36
  @signal = signal
37
37
  @traps = traps.transform_keys { |k| signame(k) }
38
38
  @pr, @pw = ::IO.pipe
39
39
  @monitor = nil
40
- @restore = @traps.map { |(sig, handler)| [sig, trap(sig, handler, force: force)] }.to_h
40
+ @restore = @traps.to_h { |sig, handler| [sig, trap(sig, handler, force: force)] }
41
41
  end
42
42
 
43
43
  # Handle the next available signal on the pipe (without blocking)
@@ -5,15 +5,14 @@ require_relative '../fuse_context'
5
5
  module FFI
6
6
  module Libfuse
7
7
  module Adapter
8
- # Wrapper module to inject {FuseContext} as first arg to each callback method (except :destroy)
9
- #
10
- # {ThreadLocalContext} may be a less intrusive means to make the context available to callbacks
8
+ # Injects a wrapper via #{FuseCallbacks#fuse_wrappers} make the current {FuseContext} object available to
9
+ # callbacks (except :destroy) via thread local variable :fuse_context
11
10
  module Context
12
11
  # @!visibility private
13
12
  def fuse_wrappers(*wrappers)
14
13
  wrappers.unshift(
15
14
  {
16
- wrapper: proc { |_fm, *args, **_, &b| self.class.context_callback(*args, &b) },
15
+ wrapper: proc { |_fm, *args, **_, &b| Context.thread_local_context(*args, &b) },
17
16
  excludes: %i[destroy]
18
17
  }
19
18
  )
@@ -24,12 +23,15 @@ module FFI
24
23
 
25
24
  module_function
26
25
 
27
- # @yieldparam [FuseContext] ctx
28
- # @yieldparam [Array] *args
29
- def context_callback(*args)
30
- ctx = FuseContext.get
31
- ctx = nil if ctx.null?
32
- yield ctx, *args
26
+ # Capture {FuseContext} in thread local variable
27
+ def fuse_context
28
+ Thread.current[:fuse_context] ||= FuseContext.get
29
+ end
30
+
31
+ def thread_local_context(*args)
32
+ yield(*args)
33
+ ensure
34
+ Thread.current[:fuse_context] = nil
33
35
  end
34
36
  end
35
37
  end
@@ -5,77 +5,78 @@ module FFI
5
5
  module Adapter
6
6
  # Wrapper module to assist filesystem written for Fuse3 to be compatible with Fuse2
7
7
  module Fuse2Compat
8
- # @!visibility private
9
8
  # Wrapper shim for fuse methods to ensure compatibility with Fuse2
10
- module Fuse2Prepend
9
+ module Prepend
11
10
  include Adapter
12
11
 
13
- def getattr(path, stat, fuse_file_info = nil)
14
- super(path, stat, fuse_file_info)
15
- end
16
-
17
- def truncate(path, size, fuse_file_info = nil)
18
- super(path, size, fuse_file_info)
19
- end
12
+ if FUSE_MAJOR_VERSION == 2
13
+ # @!visibility private
14
+ def getattr(path, stat, fuse_file_info = nil)
15
+ super(path, stat, fuse_file_info)
16
+ end
20
17
 
21
- def init(fuse_conn_info, fuse_config = nil)
22
- super(fuse_conn_info, fuse_config)
23
- end
18
+ def truncate(path, size, fuse_file_info = nil)
19
+ super(path, size, fuse_file_info)
20
+ end
24
21
 
25
- def chown(path, uid, gid, fuse_file_info = nil)
26
- super(path, uid, gid, fuse_file_info)
27
- end
22
+ def init(fuse_conn_info, fuse_config = nil)
23
+ super(fuse_conn_info, fuse_config)
24
+ end
28
25
 
29
- def chmod(path, mode, fuse_file_info = nil)
30
- super(path, mode, fuse_file_info)
31
- end
26
+ def chown(path, uid, gid, fuse_file_info = nil)
27
+ super(path, uid, gid, fuse_file_info)
28
+ end
32
29
 
33
- def utimens(path, atime, mtime, fuse_file_info = nil)
34
- super(path, atime, mtime, fuse_file_info)
35
- end
30
+ def chmod(path, mode, fuse_file_info = nil)
31
+ super(path, mode, fuse_file_info)
32
+ end
36
33
 
37
- def readdir(path, buffer, filler, offset, fuse_file_info, fuse_readdir_flag = 0)
38
- filler = proc { |buf, name, stat, off = 0, _fuse_fill_dir_flag = 0| filler.call(buf, name, stat, off) }
39
- super(path, buffer, filler, offset, fuse_file_info, fuse_readdir_flag)
40
- end
34
+ def utimens(path, atime, mtime, fuse_file_info = nil)
35
+ super(path, atime, mtime, fuse_file_info)
36
+ end
41
37
 
42
- def fgetattr(*args)
43
- getattr(*args)
44
- end
38
+ def readdir(path, buffer, filler, offset, fuse_file_info, fuse_readdir_flag = 0)
39
+ f3_fill = proc { |buf, name, stat, off = 0, _fuse_fill_dir_flag = 0| filler.call(buf, name, stat, off) }
40
+ super(path, buffer, f3_fill, offset, fuse_file_info, fuse_readdir_flag)
41
+ end
45
42
 
46
- def ftruncate(*args)
47
- truncate(*args)
48
- end
43
+ def fgetattr(path, stat, ffi)
44
+ stat.clear # For some reason (at least on OSX) the stat is not clear when this is called.
45
+ getattr(path, stat, ffi)
46
+ 0
47
+ end
49
48
 
50
- def fuse_respond_to?(fuse_method)
51
- fuse_method = fuse_method[1..].to_sym if %i[fgetattr ftruncate].include?(fuse_method)
52
- super(fuse_method)
53
- end
49
+ def ftruncate(*args)
50
+ truncate(*args)
51
+ end
54
52
 
55
- def fuse_flags
56
- res = defined?(super) ? super : []
57
- if respond_to?(:init_fuse_config)
58
- fuse_config = FuseConfig.new
59
- init_fuse_config(fuse_config, :fuse2)
60
- res << :nullpath_ok if fuse_config.nullpath_ok?
53
+ def fuse_respond_to?(fuse_method)
54
+ fuse_method = fuse_method[1..].to_sym if %i[fgetattr ftruncate].include?(fuse_method)
55
+ super(fuse_method)
61
56
  end
62
57
 
63
- res
64
- end
65
- end
58
+ def fuse_flags
59
+ res = defined?(super) ? super : []
60
+ if respond_to?(:init_fuse_config)
61
+ fuse_config = FuseConfig.new
62
+ init_fuse_config(fuse_config, :fuse2)
63
+ res << :nullpath_ok if fuse_config.nullpath_ok?
64
+ end
66
65
 
67
- # @!visibility private
68
- module Fuse3Prepend
69
- def init(*args)
70
- init_fuse_config(args.detect { |a| a.is_a?(FuseConfig) }) if respond_to?(:init_fuse_config)
71
- super if defined?(super)
66
+ res
67
+ end
68
+
69
+ else
70
+ def init(*args)
71
+ init_fuse_config(args.detect { |a| a.is_a?(FuseConfig) }) if respond_to?(:init_fuse_config)
72
+ super if defined?(super)
73
+ end
72
74
  end
73
75
  end
74
76
 
75
77
  # @!visibility private
76
78
  def self.included(mod)
77
- prepend_module = Libfuse.FUSE_MAJOR_VERSION == 2 ? Fuse2Prepend : Fuse3Prepend
78
- mod.prepend(prepend_module)
79
+ mod.prepend(Prepend)
79
80
  end
80
81
 
81
82
  # @!method init_fuse_config(fuse_config,compat)
@@ -12,7 +12,6 @@ module FFI
12
12
  #
13
13
  module Fuse3Support
14
14
  # The actual module methods that are prepended
15
- # @!visibility private
16
15
  module Prepend
17
16
  include Adapter
18
17