lib/ffi/libfuse/adapter/safe.rb in ffi-libfuse-0.3.4 vs lib/ffi/libfuse/adapter/safe.rb in ffi-libfuse-0.4.0
- old
+ new
@@ -2,57 +2,105 @@
module FFI
module Libfuse
module Adapter
# Safe callbacks convert return values into integer responses, and rescues errors
- #
- # Applies to all callbacks except :init, :destroy
module Safe
# @!visibility private
def fuse_wrappers(*wrappers)
wrappers << {
- wrapper: proc { |fm, *args, **_, &b| Safe.safe_callback(fm, *args, default_errno: default_errno, &b) },
- excludes: %i[init destroy]
+ wrapper: proc { |fm, *args, **_, &b| safe_integer_callback(fm, *args, default_errno: default_errno, &b) },
+ excludes: FuseOperations::VOID_RETURN + FuseOperations::MEANINGFUL_RETURN
}
+ wrappers << {
+ wrapper: proc { |fm, *args, **_, &b|
+ safe_meaningful_integer_callback(fm, *args, default_errno: default_errno, &b)
+ },
+ includes: FuseOperations::MEANINGFUL_RETURN
+ }
+ wrappers << {
+ wrapper: proc { |fm, *args, **_, &b| safe_void_callback(fm, *args, &b) },
+ includes: FuseOperations::VOID_RETURN
+ }
return wrappers unless defined?(super)
super(*wrappers)
end
- # @return [Integer] the default errno. ENOTRECOVERABLE unless overridden
+ # @return [Integer] the default errno to return for rescued errors. ENOTRECOVERABLE unless overridden
def default_errno
defined?(super) ? super : Errno::ENOTRECOVERABLE::Errno
end
module_function
- # Process the results of yielding *args for the fuse_method callback
+ # Process the result of yielding to the fuse callback to provide a safe return value to libfuse
#
- # @yieldreturn [SystemCallError] expected callback errors rescued to return equivalent -ve errno value
- # @yieldreturn [StandardError,ScriptError] unexpected callback errors are rescued
- # to return -ve {default_errno} after emitting backtrace to #warn
+ # For callbacks in {FuseOperations.VOID_RETURN}
#
- # @yieldreturn [Integer]
+ # * the return value and any unexpected errors raised are ignored
#
- # * -ve values returned directly
- # * +ve values returned directly for fuse_methods in {FuseOperations.MEANINGFUL_RETURN} list
- # * otherwise returns 0
+ # For callbacks in {FuseOperations.MEANINGFUL_RETURN}
#
- # @yieldreturn [Object] always returns 0 if no exception is raised
+ # * should raise appropriate `Errno` error for expected errors (eg `Errno::ENOENT`)
+ # * must return a value convertable to an integer (via :to_i) - either a positive meaningful value
+ # or a negative errno value which will be returned directly
+ # * otherwise default_errno is returned
#
+ # For remaining path callbacks
+ #
+ # * should raise appropriate `Errno` error for expected errors (eg `Errno::ENOENT`)
+ # * may return a negative Integer (equivalent to raising the corresponding `SystemCallError`)
+ # * any other value returned is considered success and 0 is returned
+ # * unexpected errors raised are rescued and default_errno is returned
+ #
+ # @param [Integer] default_errno
+ # value to return for any unexpected errors
+ #
+ # @return [nil]
+ # For void callbacks
+ # @return [Integer]
+ # For path callbacks, either 0 for success or a negative errno value
def safe_callback(fuse_method, *args, default_errno: Errno::ENOTRECOVERABLE::Errno)
- result = yield(*args)
+ if FuseOperations::MEANINGFUL_RETURN.include?(fuse_method)
+ safe_meaningful_integer_callback(fuse_method, *args, default_errno: default_errno)
+ elsif FuseOperations::VOID_RETURN.include?(fuse_method)
+ safe_void_callback(fuse_method, *args)
+ else
+ safe_integer_callback(fuse_method, *args, default_errno: default_errno)
+ end
+ end
- return result.to_i if FuseOperations.meaningful_return?(fuse_method)
+ private
- 0
+ def safe_integer_callback(_, *args, default_errno: Errno::ENOTRECOVERABLE::Errno)
+ safe_errno(default_errno) do
+ result = yield(*args)
+ result.is_a?(Integer) && result.negative? ? result : 0
+ end
+ end
+
+ def safe_meaningful_integer_callback(_, *args, default_errno: Errno::ENOTRECOVERABLE::Errno)
+ safe_errno(default_errno) do
+ yield(*args).to_i
+ end
+ end
+
+ def safe_errno(default_errno)
+ yield
rescue SystemCallError => e
-e.errno
- rescue StandardError, ScriptError => e
- # rubocop:disable Layout/LineLength
- warn ["FFI::Libfuse error in #{fuse_method}", *e.backtrace.reverse, "#{e.class.name}:#{e.message}"].join("\n\t")
- # rubocop:enable Layout/LineLength
+ rescue StandardError, ScriptError
-default_errno.abs
+ end
+
+ # Process callbacks that return void, simply by swallowing unexpected errors
+ def safe_void_callback(_, *args)
+ yield(*args)
+ nil
+ rescue StandardError, ScriptError
+ # Swallow unexpected exceptions
+ nil
end
end
end
end
end