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