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