ffi-libfuse 0.3.4 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +22 -0
  3. data/README.md +1 -1
  4. data/lib/ffi/accessors.rb +21 -7
  5. data/lib/ffi/boolean_int.rb +1 -1
  6. data/lib/ffi/devt.rb +3 -3
  7. data/lib/ffi/libfuse/adapter/debug.rb +53 -15
  8. data/lib/ffi/libfuse/adapter/fuse2_compat.rb +38 -21
  9. data/lib/ffi/libfuse/adapter/fuse3_support.rb +0 -1
  10. data/lib/ffi/libfuse/adapter/ruby.rb +210 -159
  11. data/lib/ffi/libfuse/adapter/safe.rb +69 -21
  12. data/lib/ffi/libfuse/callbacks.rb +2 -1
  13. data/lib/ffi/libfuse/filesystem/accounting.rb +1 -1
  14. data/lib/ffi/libfuse/filesystem/mapped_files.rb +33 -7
  15. data/lib/ffi/libfuse/filesystem/pass_through_dir.rb +0 -1
  16. data/lib/ffi/libfuse/filesystem/virtual_dir.rb +293 -126
  17. data/lib/ffi/libfuse/filesystem/virtual_file.rb +85 -79
  18. data/lib/ffi/libfuse/filesystem/virtual_fs.rb +34 -15
  19. data/lib/ffi/libfuse/filesystem/virtual_link.rb +60 -0
  20. data/lib/ffi/libfuse/filesystem/virtual_node.rb +104 -87
  21. data/lib/ffi/libfuse/filesystem.rb +1 -1
  22. data/lib/ffi/libfuse/fuse2.rb +3 -2
  23. data/lib/ffi/libfuse/fuse3.rb +1 -1
  24. data/lib/ffi/libfuse/fuse_args.rb +5 -2
  25. data/lib/ffi/libfuse/fuse_buf.rb +112 -0
  26. data/lib/ffi/libfuse/fuse_buf_vec.rb +228 -0
  27. data/lib/ffi/libfuse/fuse_common.rb +10 -4
  28. data/lib/ffi/libfuse/fuse_config.rb +16 -7
  29. data/lib/ffi/libfuse/fuse_operations.rb +86 -41
  30. data/lib/ffi/libfuse/gem_helper.rb +2 -9
  31. data/lib/ffi/libfuse/io.rb +56 -0
  32. data/lib/ffi/libfuse/main.rb +27 -24
  33. data/lib/ffi/libfuse/test_helper.rb +68 -60
  34. data/lib/ffi/libfuse/version.rb +1 -1
  35. data/lib/ffi/libfuse.rb +1 -1
  36. data/lib/ffi/stat/native.rb +4 -4
  37. data/lib/ffi/stat.rb +19 -3
  38. data/lib/ffi/struct_array.rb +2 -1
  39. data/sample/hello_fs.rb +1 -1
  40. metadata +6 -3
  41. data/lib/ffi/libfuse/fuse_buffer.rb +0 -257
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d59c87631dfe38e9dc49b36f829acdd704165162deff3bad6479f3394f348657
4
- data.tar.gz: 01ab562dd537f1f11f7e3c454e49c5df3ef1ce5cfd6cb8b1aa6b5e6a7f89a4c0
3
+ metadata.gz: e105d84b1d0f73af20845ed1d39e51ae61f9ca4998fa9caf505b4e87ae08eaa9
4
+ data.tar.gz: 7f9013b530bc62e9fb4071899e13f191fdc720f662221da1cac0cd554a76bc5f
5
5
  SHA512:
6
- metadata.gz: ae437c153a84155c86a9e2d71d4dd1c6234879fae7553676efacd311e26cae3cf653dd4439b2ec4ab584cd01698636915fc6c9cefd14a2f045c2b19e4077e60b
7
- data.tar.gz: af5fab46502ff889710facc930ee59d7bb27e129da3c70f0d5089c1fa85e29bbfe3ec91b01f75db907da5f8be220755edec6b4b3976421ce0454cc43830387da
6
+ metadata.gz: 72b0a464ab67be704cb14f3ce4b07f1f9a29d260ee50864dcf8c75cc24ba2eaf43aab6ae9f79fda9edb707cded39fde10e8504be7b8ca011b1a7d625969d62f6
7
+ data.tar.gz: b85626a26f149dc1852ae5928cef67eb5dd7fc4353acb6bafa4fb0061aec38dce6d6e6c8137b216bc540263ba28f5b5f627f3af8feecb9bab417bbfc64df45ab
data/CHANGELOG.md CHANGED
@@ -1,5 +1,27 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.4.0](https://github.com/lwoggardner/ffi-libfuse/compare/v0.3.4...v0.4.0) (2024-01-21)
4
+
5
+
6
+ ### ⚠ BREAKING CHANGES
7
+
8
+ * **filesystem:** Fuse callbacks :init and :destroy are no longer passed on to sub-filesystems.
9
+ * **adapters:** Adapter::Debug now includes Adapter::Safe.
10
+ * Option parsing errors via raise exception rather than return false/nil
11
+
12
+ ### Features
13
+
14
+ * **adapters:** Adapter::Debug now includes Adapter::Safe. ([a595304](https://github.com/lwoggardner/ffi-libfuse/commit/a59530427d7eb85961a724969eaa6ec099c5e4f6))
15
+ * **filesystem:** Support :rename operation in virtual filesystems ([a595304](https://github.com/lwoggardner/ffi-libfuse/commit/a59530427d7eb85961a724969eaa6ec099c5e4f6))
16
+ * **filesystem:** Support symlinks and hardlinks in virtual filesystems (VirtualDir/MemoryFS) ([a595304](https://github.com/lwoggardner/ffi-libfuse/commit/a59530427d7eb85961a724969eaa6ec099c5e4f6))
17
+ * Option parsing errors via raise exception rather than return false/nil ([a595304](https://github.com/lwoggardner/ffi-libfuse/commit/a59530427d7eb85961a724969eaa6ec099c5e4f6))
18
+
19
+
20
+ ### Bug Fixes
21
+
22
+ * **fuse2compat:** Enhanced Fuse2 compatibility in Fuse2Compat module ([a595304](https://github.com/lwoggardner/ffi-libfuse/commit/a59530427d7eb85961a724969eaa6ec099c5e4f6))
23
+ * symlinks and hard links ([a595304](https://github.com/lwoggardner/ffi-libfuse/commit/a59530427d7eb85961a724969eaa6ec099c5e4f6))
24
+
3
25
  ## [0.3.4](https://github.com/lwoggardner/ffi-libfuse/compare/v0.3.3...v0.3.4) (2023-01-08)
4
26
 
5
27
 
data/README.md CHANGED
@@ -84,7 +84,7 @@ class HelloFS
84
84
  end
85
85
 
86
86
  # Start the file system
87
- FFI::Libfuse.fuse_main(operations: HelloFS.new) if __FILE__ == $0
87
+ exit(FFI::Libfuse.fuse_main(operations: HelloFS.new)) if __FILE__ == $0
88
88
 
89
89
  ```
90
90
  <!-- SAMPLE END: sample/hello_fs.rb -->
data/lib/ffi/accessors.rb CHANGED
@@ -19,22 +19,29 @@ module FFI
19
19
 
20
20
  #
21
21
  # Define a struct attribute reader for members
22
- # @param [Array<Symbol>] attrs the attribute names
22
+ # @param [Array<Symbol>]
23
+ # attrs the attribute names used as the reader method name
24
+ #
25
+ # a trailing '?' will be stripped from attribute names for primary reader method name, and cause an
26
+ # boolean alias method to be created.
23
27
  # @param [Proc|String] format
24
- # A Proc, or format string containing a single %s, to convert attr to struct member name
28
+ # A Proc, or format string containing a single %s, to convert each attribute name to the corresponding
29
+ # struct member name
25
30
  # @param [Boolean] simple
26
31
  # Controls how writer methods are defined using block
27
32
  # @param [Proc] block
28
- # An optional block to the struct field(s) into something more useful
33
+ # An optional block to convert the struct field value into something more useful
29
34
  #
30
35
  # If simple is true then block takes the struct field value, otherwise method is defined directly from the block
31
- # and should use __method__ to get the attr name, and self.class.ffi_attr_readers[__method__] to get the member
32
- # name
36
+ # and should use __method__ to get the attribute name. and self.class.ffi_attr_readers[__method__] to get the
37
+ # member name if these are not available from enclosed variables.
33
38
  # @return [void]
34
39
  def ffi_attr_reader(*attrs, format: '%s', simple: true, &block)
35
40
  attrs.each do |attr|
41
+ bool, attr = attr[-1] == '?' ? [true, attr[..-2]] : [false, attr]
42
+
36
43
  member = (format.respond_to?(:call) ? format.call(attr) : format % attr).to_sym
37
- ffi_attr_readers[attr] = member
44
+ ffi_attr_readers[attr.to_sym] = member
38
45
  if !block
39
46
  define_method(attr) { self[member] }
40
47
  elsif simple
@@ -42,11 +49,14 @@ module FFI
42
49
  else
43
50
  define_method(attr, &block)
44
51
  end
52
+
53
+ alias_method "#{attr}?", attr if bool
45
54
  end
46
55
  end
47
56
 
48
57
  # Define a struct attribute writer
49
58
  # @param [Array<Symbol>] attrs the attribute names
59
+ # trailing '?' will be stripped from attribute names
50
60
  # @param [String|Proc] format
51
61
  # A format string containing a single %s to convert attr symbol to struct member
52
62
  # @param [Boolean] simple
@@ -60,6 +70,8 @@ module FFI
60
70
  # @return [void]
61
71
  def ffi_attr_writer(*attrs, format: '%s', simple: true, &block)
62
72
  attrs.each do |attr|
73
+ attr = attr[..-2] if attr[-1] == '?'
74
+
63
75
  member = (format % attr).to_sym
64
76
  ffi_attr_writers[attr.to_sym] = member
65
77
  if !block
@@ -95,7 +107,9 @@ module FFI
95
107
  # @param [Array<Symbol>] flags list of flags
96
108
  # @return [void]
97
109
  def ffi_bitflag_reader(attr, *flags)
98
- flags.each { |f| ffi_attr_reader(f, simple: false) { self[attr].include?(f) } }
110
+ flags.each do |f|
111
+ ffi_attr_reader(:"#{f}?", simple: false) { self[attr].include?(f) }
112
+ end
99
113
  end
100
114
 
101
115
  # Define individual flag writers over a bitmask field
@@ -21,7 +21,7 @@ module FFI
21
21
  end
22
22
 
23
23
  %i[char short int long int8 int16 int32 int64].each do |t|
24
- FFI.typedef(BooleanInt.new(t), "bool_#{t}".to_sym)
24
+ FFI.typedef(BooleanInt.new(t), :"bool_#{t}")
25
25
  end
26
26
  end
27
27
  end
data/lib/ffi/devt.rb CHANGED
@@ -15,17 +15,17 @@ module FFI
15
15
  # @param [Integer] major
16
16
  # @param [Integer] minor
17
17
  # @return [Integer] combined major/minor to a single value to pass to mknod etc
18
- attach_function :makedev, "#{prefix}makedev".to_sym, %i[int int], :int
18
+ attach_function :makedev, :"#{prefix}makedev", %i[int int], :int
19
19
 
20
20
  # @!method major(dev)
21
21
  # @param [Integer] dev
22
22
  # @return [Integer] the major component of dev
23
- attach_function :major, "#{prefix}major".to_sym, [:int], :int
23
+ attach_function :major, :"#{prefix}major", [:int], :int
24
24
 
25
25
  # @!method minor(dev)
26
26
  # @param [Integer] dev
27
27
  # @return [Integer] the minor component of dev
28
- attach_function :minor, "#{prefix}minor".to_sym, [:int], :int
28
+ attach_function :minor, :"#{prefix}minor", [:int], :int
29
29
  rescue FFI::NotFoundError
30
30
  case Platform::NAME
31
31
  when 'x86_64-darwin'
@@ -1,13 +1,24 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative 'safe'
4
+
3
5
  module FFI
4
6
  module Libfuse
5
7
  module Adapter
6
8
  # Debug callbacks
7
9
  #
8
10
  # When included in a filesystem class, and if debugging is enabled via {Main#fuse_debug}, then installs a wrapper
9
- # via #{FuseCallbacks#fuse_wrappers} to log callbacks via #warn
11
+ # via #{FuseCallbacks#fuse_wrappers} to log callbacks.
12
+ #
13
+ # Simple format options can be handled by #{debug_config}, or override the **Module Functions** on an including
14
+ # class for more programmatic control of output.
15
+ #
16
+ # @note {Debug} includes {Safe} as it expects to handle (and re-raise) exceptions.
10
17
  module Debug
18
+ include Safe
19
+
20
+ # Default format
21
+ # @see debug_callback
11
22
  DEFAULT_FORMAT = "%<p>s %<n>s %<t>s %<m>s(%<a>s)\n\t=> %<r>s"
12
23
 
13
24
  # @return [Boolean] true if debug is enabled
@@ -26,8 +37,8 @@ module FFI
26
37
  def fuse_wrappers(*wrappers)
27
38
  conf = { prefix: self.class.name }.merge!(debug_config)
28
39
  # validate config for bad formats, strftime etc
29
- Debug.debug_format(:test_debug, [], :result, **conf)
30
- wrappers << proc { |fm, *args, &b| Debug.debug_callback(fm, *args, **conf, &b) } if debug?
40
+ debug_format(:test_debug, [], :result, **conf)
41
+ wrappers << proc { |fm, *args, &b| debug_callback(fm, *args, **conf, &b) } if debug?
31
42
  return wrappers unless defined?(super)
32
43
 
33
44
  super(*wrappers)
@@ -41,10 +52,11 @@ module FFI
41
52
 
42
53
  module_function
43
54
 
44
- # Debug fuse method, args and result
55
+ # Debug fuse method, args and result of yielding args to the block
56
+ #
45
57
  # @param [Symbol] fuse_method the callback name
46
58
  # @param [Array] args callback arguments
47
- # @param [Hash<Symbol,String>] options see {debug} for defaults
59
+ # @param [Hash<Symbol,String>] options see {debug_format} for defaults
48
60
  # @option options [String] prefix
49
61
  # @option options [String] strftime a date time format
50
62
  # @option options [String] format format string with fields
@@ -55,25 +67,49 @@ module FFI
55
67
  # * %<a>: Comma separate list of arguments
56
68
  # * %<r>: Result of the method call (or any error raised)
57
69
  # * %<p>: The value of prefix option
70
+ # @raise [SystemCallError]
71
+ # expected Errors raised from callbacks are logged with their cause (if any)
72
+ # @raise [StandardError,ScriptError]
73
+ # unexpected Errors raised from callbacks are logged with their backtrace
58
74
  def debug_callback(fuse_method, *args, **options)
59
- debug(fuse_method, args, yield(*args), **options)
60
- rescue SystemCallError => e
61
- # expected behaviour
62
- debug(fuse_method, args, "#{e.class.name}(errno=#{e.errno}): #{e.message}", **options)
63
- raise
75
+ result = yield(*args)
76
+ debug(fuse_method, args, result, **options)
77
+ result
64
78
  rescue StandardError, ScriptError => e
65
- # unexpected, debug with backtrace
66
- debug(fuse_method, args, (["#{e.class.name}: #{e.message}"] + e.backtrace).join("\n\t"), **options)
79
+ debug(fuse_method, args, error_message(e), **options)
80
+ debug_error(e)
67
81
  raise
68
82
  end
69
83
 
70
- # @!visibility private
84
+ # @!group Module Functions
85
+
86
+ # Logs the callback
71
87
  def debug(fuse_method, args, result, **options)
72
88
  warn debug_format(fuse_method, args, result, **options)
73
- result
74
89
  end
75
90
 
76
- # @!visibility private
91
+ # @param [Exception] err
92
+ # @return [String] the detailed error message for err
93
+ def error_message(err)
94
+ if err.is_a?(SystemCallError)
95
+ "#{err.class.name}(errno=#{err.errno}): #{err.message}"
96
+ else
97
+ "#{err.class.name}: #{err.message}"
98
+ end
99
+ end
100
+
101
+ # Log additional information for errors (cause/backtrace etc)
102
+ # @see debug_callback
103
+ def debug_error(err)
104
+ if err.is_a?(SystemCallError)
105
+ warn "Caused by #{error_message(err.cause)}" if err.cause
106
+ else
107
+ warn err.backtrace.join("\n\t")
108
+ end
109
+ end
110
+
111
+ # @return [String] the formatted debug message
112
+ # @see debug_callback
77
113
  def debug_format(fuse_method, args, result, prefix: 'DEBUG', strftime: '%FT%T%:z', format: DEFAULT_FORMAT)
78
114
  format(format,
79
115
  p: prefix,
@@ -83,6 +119,8 @@ module FFI
83
119
  a: args.map(&:to_s).join(','),
84
120
  r: result)
85
121
  end
122
+
123
+ # @!endgroup
86
124
  end
87
125
  end
88
126
  end
@@ -31,8 +31,8 @@ module FFI
31
31
  super(path, mode, fuse_file_info)
32
32
  end
33
33
 
34
- def utimens(path, atime, mtime, fuse_file_info = nil)
35
- super(path, atime, mtime, fuse_file_info)
34
+ def utimens(path, times, fuse_file_info = nil)
35
+ super(path, times, fuse_file_info)
36
36
  end
37
37
 
38
38
  def readdir(path, buffer, filler, offset, fuse_file_info, fuse_readdir_flag = 0)
@@ -40,49 +40,66 @@ module FFI
40
40
  super(path, buffer, f3_fill, offset, fuse_file_info, fuse_readdir_flag)
41
41
  end
42
42
 
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
43
+ def fuse_respond_to?(fuse_method)
44
+ # getdir is never supported here anyway
45
+ # fgetattr and ftruncate already fallback to the respective basic method
46
+ return false if %i[getdir fgetattr ftruncate].include?(fuse_method)
48
47
 
49
- def ftruncate(*args)
50
- truncate(*args)
48
+ super(fuse_method)
51
49
  end
52
50
 
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)
51
+ def fuse_options(args)
52
+ super if defined?(super)
53
+ return unless respond_to?(:init_fuse_config)
54
+
55
+ FUSE_CONFIG_ONLY_ATTRIBUTES.each do |opt|
56
+ args.add("-o#{opt}") if fuse_config.send(opt)
57
+ end
56
58
  end
57
59
 
58
60
  def fuse_flags
59
61
  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
62
+ return res unless respond_to?(:init_fuse_config)
65
63
 
64
+ FUSE_CONFIG_FLAGS.each { |opt| res << opt if fuse_config.send(opt) }
66
65
  res
67
66
  end
68
67
 
68
+ private
69
+
70
+ def fuse_config
71
+ @fuse_config ||= begin
72
+ fuse_config = FuseConfig.new
73
+ init_fuse_config(fuse_config, :fuse2) if respond_to?(:init_fuse_config)
74
+ fuse_config
75
+ end
76
+ end
77
+
69
78
  else
70
79
  def init(*args)
71
- init_fuse_config(args.detect { |a| a.is_a?(FuseConfig) }) if respond_to?(:init_fuse_config)
80
+ init_fuse_config(args.detect { |a| a.is_a?(FuseConfig) }, :fuse3) if respond_to?(:init_fuse_config)
72
81
  super if defined?(super)
73
82
  end
74
83
  end
75
84
  end
76
85
 
86
+ # Attributes in Fuse3 config that cannot be set by Fuse3 options. If set via {init_fuse_config} the
87
+ # equivalent options will be force set under Fuse 2
88
+ FUSE_CONFIG_ONLY_ATTRIBUTES = %i[hard_remove use_ino readdir_ino direct_io].freeze
89
+
90
+ # Attributes in Fuse3 config that were {FuseOperations#fuse_flags} in Fuse2. If set via {init_fuse_config} the
91
+ # equivalent flags will be added
92
+ FUSE_CONFIG_FLAGS = %i[nullpath_ok].freeze
93
+
77
94
  # @!visibility private
78
95
  def self.included(mod)
79
- mod.prepend(Prepend)
96
+ mod.prepend(Prepend) if FUSE_MAJOR_VERSION < 3
80
97
  end
81
98
 
82
99
  # @!method init_fuse_config(fuse_config,compat)
83
100
  # @abstract
84
- # Define this method to configure the fuse config object so that under Fuse2 the config options
85
- # can be converted to appropriate flags.
101
+ # Define this method to configure the {FuseConfig} object so that under Fuse2 the config options
102
+ # can be converted to appropriate flags or options
86
103
  #
87
104
  # @param [FuseConfig] fuse_config the fuse config object
88
105
  # @param [Symbol] compat either :fuse2 or :fuse3
@@ -81,7 +81,6 @@ module FFI
81
81
 
82
82
  # @!visibility private
83
83
  def self.included(mod)
84
- # We prepend our shim module so caller doesn't have to call super
85
84
  mod.prepend(Prepend) if FUSE_MAJOR_VERSION > 2
86
85
  end
87
86
  end