lib/ffi/libfuse/fuse_operations.rb in ffi-libfuse-0.3.4 vs lib/ffi/libfuse/fuse_operations.rb in ffi-libfuse-0.4.0
- old
+ new
@@ -1,21 +1,20 @@
# frozen_string_literal: true
require_relative 'fuse_version'
-require_relative '../ruby_object'
require_relative 'fuse_conn_info'
-require_relative 'fuse_buffer'
+require_relative 'fuse_config'
+require_relative 'fuse_buf_vec'
require_relative 'fuse_context'
require_relative 'fuse_file_info'
require_relative 'fuse_poll_handle'
+require_relative 'fuse_callbacks'
+require_relative '../ruby_object'
require_relative '../stat_vfs'
require_relative '../flock'
-require_relative 'thread_pool'
require_relative '../stat'
-require_relative '../struct_array'
require_relative '../encoding'
-require_relative 'fuse_callbacks'
module FFI
# Ruby FFI Binding for [libfuse](https://github.com/libfuse/libfuse)
module Libfuse
# All paths are encoded in ruby's view of the filesystem encoding
@@ -42,24 +41,86 @@
# The file system operations as specified in libfuse.
#
# All Callback methods are optional, but some are essential for a useful filesystem
# e.g. {getattr},{readdir}
#
- # Almost all callback operations take a path which can be of any length and will return 0 for success, or raise a
- # {::SystemCallError} on failure
+ # Almost all callback operations take a path which can be of any length and will return 0 for success or a negative
+ # `Errno` value on failure.
#
class FuseOperations < FFI::Struct
include FuseCallbacks
+ # Callbacks that have no return value
+ VOID_RETURN = %i[init destroy].freeze
+
# Callbacks that are expected to return meaningful positive integers
MEANINGFUL_RETURN = %i[read write write_buf lseek copy_file_range getxattr listxattr].freeze
- # @return [Boolean] true if fuse_callback expects a meaningful integer return
- def self.meaningful_return?(fuse_callback)
- MEANINGFUL_RETURN.include?(fuse_callback)
+ # @!visibility private
+ # Methods to handle the path argument for most {path_callbacks}
+ NODE_PATH_METHODS = %i[first shift unshift].freeze
+
+ # @!visibility private
+ # Methods to handle the path argument for {link}, {symlink} and {rename}
+ LINK_PATH_METHODS = %i[last pop push].freeze
+
+ # @!visibility private
+ CALLBACK_PATH_ARG_METHODS = Hash.new(NODE_PATH_METHODS).merge(
+ {
+ link: LINK_PATH_METHODS,
+ symlink: LINK_PATH_METHODS,
+ rename: LINK_PATH_METHODS
+ }
+ ).freeze
+
+ class << self
+ # @return [Boolean] true if fuse_callback expects a meaningful integer return
+ def meaningful_return?(fuse_callback)
+ MEANINGFUL_RETURN.include?(fuse_callback)
+ end
+
+ # @return [Boolean] true if fuse_callback expects a void return
+ def void_return?(fuse_callback)
+ VOID_RETURN.include?(fuse_callback)
+ end
+
+ # Helper to determine how to handle the path argument for a path callback
+ # @param [Symbol] fuse_callback callback method name (must be one of #{path_callbacks})
+ # @return [Symbol, Symbol,Symbol] read, remove, add methods.
+ # [:last, :push, :pop] for :link, :symlink, :rename,
+ # [:first, :shift, :unshift] for everything else
+ # @example
+ # def wrap_callback(fuse_method, *args)
+ # read, remove, add = FFI::Libfuse::FuseOperations.path_arg_methods(fuse_method)
+ # path = args.send(read)
+ # # ... do something with path
+ #
+ # path = args.send(remove)
+ # # ... do something to make an alternate path
+ # args.send(add, adjusted_path)
+ # delegate.send(fuse_methoed, *args)
+ # end
+ def path_arg_methods(fuse_callback)
+ CALLBACK_PATH_ARG_METHODS[fuse_callback]
+ end
+
+ # @return [Set<Symbol>] list of callback methods
+ def fuse_callbacks
+ @fuse_callbacks ||= Set.new(members - [:flags])
+ end
+
+ # @return [Set<Symbol>] list of path callback methods
+ def path_callbacks
+ @path_callbacks ||= fuse_callbacks - VOID_RETURN
+ end
end
+ # @!visibility private
+ def fuse_callbacks
+ self.class.fuse_callbacks
+ end
+
# Container to dynamically build up the operations layout which is dependent on the loaded libfuse version
op = {}
# @!group FUSE Callbacks
@@ -149,16 +210,15 @@
# @return [Integer] 0 for success or -ve errno
# int (*rmdir) (const char *);
op[:rmdir] = []
- # @!method symlink(path,target)
+ # @!method symlink(target, path)
# @abstract
# Create a symbolic link
+ # @param [String] target
# @param [String] path
- # @param [String] target the link target
- #
# @return [Integer] 0 for success or -ve errno
# int (*symlink) (const char *, const char *);
op[:symlink] = [:fs_string]
@@ -171,17 +231,17 @@
# @return [Integer] 0 for success or -ve errno
# int (*rename) (const char *, const char *);
op[:rename] = [:fs_string]
- # @!method link(path,target)
+ # @!method link(target, path)
# @abstract
# Create a hard link to a file
- # @param [String] path
# @param [String] target
- #
+ # @param [String] path
# @return [Integer] 0 for success or -ve errno
+ # @see rename(2)
# int (*link) (const char *, const char *);
op[:link] = [:fs_string]
# @!method chmod(path,mode,fuse_file_info=nil)
@@ -484,11 +544,10 @@
# fuse2: void *(*init) (struct fuse_conn_info *conn);
# fuse3: void *(*init) (struct fuse_conn_info *conn, struct fuse_config *cfg);
op[:init] =
if FUSE_MAJOR_VERSION >= 3
- require_relative 'fuse_config'
callback([FuseConnInfo.by_ref, FuseConfig.by_ref], RubyObject)
else
callback([FuseConnInfo.by_ref], RubyObject)
end
@@ -616,11 +675,11 @@
#
# @see utimensat(2)
#
# int (*utimens) (const char *, const struct timespec tv[2]);
- op[:utimens] = [FFI::Stat::TimeSpec.array(2)]
+ op[:utimens] = [FFI::Stat::TimeSpec[2]]
op[:utimens] << FuseFileInfo.by_ref if FUSE_MAJOR_VERSION >= 3
# @!method bmap(path,blocksize,index)
# @abstract
# Map block index within file to block index within device
@@ -653,21 +712,23 @@
#
# Flag indicating that the path need not be calculated for the following operations: read, write, flush,
# release, fsync, readdir, releasedir, fsyncdir, ftruncate, fgetattr, lock, ioctl and poll
#
# Closely related to flag_nullpath_ok, but if this flag is set then the path will not be calculaged even if
- # the file wasnt unlinked. However the path can still be non-NULL if it needs to be calculated for some other
- # reason.
+ # the file wasn't unlinked. However the path can still be non-NULL if it needs to be calculated for some
+ # other reason.
#
# - :utime_omit_ok
#
# Flag indicating that the filesystem accepts special UTIME_NOW and UTIME_OMIT values in its utimens
# operation.
#
- # @return [Array[]Symbol>] a list of flags to set capabilities
+ # @return [Array<Symbol>] a list of flags to set capabilities
# @note Not available in Fuse3
# @deprecated in Fuse3 use fuse_config object in {init}
+
+ # flags
op[:flags] = :flags_mask if FUSE_MAJOR_VERSION < 3
if FUSE_VERSION >= 28
# @!method ioctl(path,cmd,arg,fuse_file_info, flags,data)
@@ -715,11 +776,11 @@
# @!method write_buf(path,buf,offset,fuse_file_info)
# @abstract
# Write contents of buffer to an open file
#
- # Similar to the write() method, but data is supplied in a generic buffer.
+ # Similar to the {write} method, but data is supplied in a generic buffer.
# Use {FuseBufVec#copy_to_fd} to copy data to an open file descriptor, or {FuseBufVec#copy_to_str} to extract
# string data from the buffer
#
# @param [String] path
# @param [FuseBufVec] buf
@@ -732,20 +793,19 @@
op[:write_buf] = [FuseBufVec.by_ref, :off_t, FuseFileInfo.by_ref]
# @!method read_buf(path,bufp,size,offset,fuse_file_info)
# @abstract
#
- # Similar to the read() method, but data is stored and returned in a generic buffer.
+ # Similar to the {read} method, but data is stored and returned in a generic buffer.
#
# No actual copying of data has to take place, the source file descriptor may simply be stored in the buffer
# for later data transfer.
#
# @param [String] path
# @param [FFI::Pointer<FuseBufVec>] bufp
- # The buffer must be allocated dynamically and stored at the location pointed to by bufp. If the buffer
- # contains memory regions, they too must be allocated using malloc(). The allocated memory will be freed by
- # the caller.
+ # The buffer must be allocated dynamically ({FuseBufVec.init})
+ # and stored at the location pointed to by bufp (see {FuseBufVec#store_to}(bufp)).
# @param [Integer] size
# @param [Integer] offset
# @param [FuseFileInfo] fuse_file_info
# @return 0 success or -ve errno
@@ -883,24 +943,9 @@
return unless FUSE_MAJOR_VERSION < 3
fuse_flags.concat(delegate.fuse_flags) if delegate.respond_to?(:fuse_flags)
send(:[]=, :flags, fuse_flags.uniq)
- end
-
- # @!visibility private
- def fuse_callbacks
- self.class.fuse_callbacks
- end
-
- # @return [Set<Symbol>] list of callback methods
- def self.fuse_callbacks
- @fuse_callbacks ||= Set.new(members - [:flags])
- end
-
- # @return [Set<Symbol>] list of path callback methods
- def self.path_callbacks
- @path_callbacks ||= fuse_callbacks - %i[init destroy]
end
end
end
end