Module: FFI::Libfuse::Adapter::Ruby

Defined in:
lib/ffi/libfuse/adapter/ruby.rb

Overview

This module assists with converting native C libfuse into idiomatic and duck-typed Ruby behaviour

Class Method helpers

These functions deal with the native fuse FFI::Pointers, Buffers etc

The class ReaddirFiller assists with the complexity of the readdir callback

FUSE Callbacks

Including this module in a Filesystem module changes the method signatures for callbacks to be more idiomatic ruby (via prepending Prepend). It also includes the type 1 adapters Context, Debug and Safe

Filesystems that return IO like objects from ##open need not implement other file io operations Similarly Filesystems that return Dir like objects from ##opendir need not implement ##readdir

Defined Under Namespace

Modules: Prepend Classes: ReaddirFiller

FUSE Callbacks collapse

Class Method Summary collapse

Class Method Details

.getxattr(buf, size) ⇒ Object

Helper for implementing FuseOperations#getxattr

Parameters:

  • buf (FFI::Pointer)
  • size (Integer)

Yield Returns:

  • (String)

    the xattr name

Raises:

  • (Errno::ENODATA)


703
704
705
706
707
708
709
710
711
712
713
714
# File 'lib/ffi/libfuse/adapter/ruby.rb', line 703

def getxattr(buf, size)
  res = yield
  raise Errno::ENODATA unless res

  res = res.to_s

  return res.size if size.zero?
  raise Errno::ERANGE if res.size > size

  buf.write_bytes(res)
  res.size
end

.listxattr(buf, size) ⇒ Object

Helper for implementing FuseOperations#listxattr

Parameters:

  • buf (FFI::Pointer)
  • size (Integer)

Yield Returns:

  • (Array<String>)

    a list of extended attribute names



720
721
722
723
724
725
726
727
728
729
730
731
# File 'lib/ffi/libfuse/adapter/ruby.rb', line 720

def listxattr(buf, size)
  res = yield
  res.reduce(0) do |offset, name|
    name = name.to_s
    unless size.zero?
      raise Errno::ERANGE if offset + name.size >= size

      buf.put_string(offset, name) # put string includes the NUL terminator
    end
    offset + name.size + 1
  end
end

.read(buf, size, offset = nil) { ... } ⇒ Integer

Helper for implementing FuseOperations#read

Parameters:

  • buf (FFI::Pointer)
  • size (Integer)
  • offset (Integer, nil) (defaults to: nil)

Yields:

  • []

Yield Returns:

  • (String, IO)

    the resulting data or IO like object

Returns:

  • (Integer)

    returns the size of data read into buf

Raises:

  • (Errno::ENOTSUP)

    if no data is returned

  • (Errno::ERANGE)

    if data return is larger than size

See Also:



619
620
621
622
623
624
625
626
627
628
# File 'lib/ffi/libfuse/adapter/ruby.rb', line 619

def read(buf, size, offset = nil)
  io = yield
  raise Errno::ENOTSUP unless io

  data = Libfuse::IO.read(io, size, offset)
  raise Errno::ERANGE unless data.size <= size

  buf.write_bytes(data)
  data.size
end

.read_buf(bufp, size, offset = nil) { ... } ⇒ Integer

Helper for implementing FuseOperations#read_buf

Parameters:

  • bufp (FFI::Pointer)
  • size (Integer)
  • offset (Integer) (defaults to: nil)

Yields:

  • []

Yield Returns:

  • (FuseBufVec)

    list of buffers to read from

  • (String, IO, Integer, :fileno)

    String, IO or file_descriptor to read from (see FuseBufVec.create)

Returns:

  • (Integer)

    0 for success

Raises:

  • (Errno:ENOTSUP)

    if no data or io is returned



640
641
642
643
644
645
646
647
648
# File 'lib/ffi/libfuse/adapter/ruby.rb', line 640

def read_buf(bufp, size, offset = nil)
  io = yield
  raise Errno::ENOTSUP unless io

  fbv = io.is_a?(FuseBufVec) ? io : FuseBufVec.create(io, size, offset)
  fbv.store_to(bufp)

  0
end

This method returns an undefined value.

Helper for implementing FuseOperations#readlink

Parameters:

  • buf (FFI::Pointer)
  • size (Integer)

Yields:

  • []

Yield Returns:

  • (String)

    the link name

Raises:

  • (Errno::ENOTSUP)

    if no data is returned

  • (Errno::ENAMETOOLONG)

    if data returned is larger than size



600
601
602
603
604
605
606
607
# File 'lib/ffi/libfuse/adapter/ruby.rb', line 600

def readlink(buf, size)
  link = yield
  raise Errno::ENOTSUP unless link
  raise Errno::ENAMETOOLONG unless link.size < size # includes terminating NUL

  buf.put_string(0, link) # with NULL terminator.
  0
end

.rescue_not_implementedObject

Helper to rescue not implemented or not supported errors from sub-filesystems @return[Object] result of block @return[nil] if block raises NotImplementedError or Errno::ENOTSUP



586
587
588
589
590
# File 'lib/ffi/libfuse/adapter/ruby.rb', line 586

def rescue_not_implemented
  yield
rescue NotImplementedError, Errno::ENOTSUP
  nil
end

.write(buf, size) {|data| ... } ⇒ Integer

Helper to implement #FuseOperations#write yields the data and receives expects IO to write the data to @yield(data)

Parameters:

  • buf (FFI::Pointer)
  • size (Integer)

Yield Parameters:

  • data (String)

    data to write

Yield Returns:

  • (nil|false)

    data has not been handled (raises Errno::ENOTSUP)

  • (Integer)

    number of bytes written (will not send to IO.write)

  • (IO)

    to use with IO.write

Returns:

  • (Integer)

    number of bytes written

Raises:

  • (Errno::ENOTSUP)

    if nothing is returned from yield



661
662
663
664
665
666
667
668
669
670
671
# File 'lib/ffi/libfuse/adapter/ruby.rb', line 661

def write(buf, size)
  data = buf.read_bytes(size) if buf.respond_to?(:read_bytes)
  data ||= buf.to_s

  io, offset = yield data

  raise Errno::ENOSUP unless io
  return io if io.is_a?(Integer)

  Libfuse::IO.write(io, data, offset)
end

.write_buf(bufv) {|data| ... } ⇒ Integer

Helper to implement #FuseOperations#write_buf

Yields firstly with data = nil A returned truthy object is sent to buvf.copy_to_io Otherwise yields again with string data from bufv.copy_to_str expecting the caller to write the data and return the number of bytes written

Parameters:

Yields:

  • (data)

Yield Parameters:

  • data (nil)

    first yield is nil

  • data (String)

    second yield is the data

Yield Returns:

  • (nil, Array<IO,Integer,Symbol...>)

    io, [offset,] *flags for first yield can return nil to indicate it wants the data as a string (via second yield) alternative an object to receive the data via FuseBufVec#copy_to_io(io, offset = nil, *flags)

  • (Integer)

    second yield must return number of bytes written

Returns:

  • (Integer)

    number of bytes written

Raises:

  • (Errno::ENOTSUP)

    if nothing is returned from either yield



690
691
692
693
694
695
696
# File 'lib/ffi/libfuse/adapter/ruby.rb', line 690

def write_buf(bufv)
  fh, *flags = yield nil # what kind of result do we want
  return bufv.copy_to_io(fh, offset, *flags) if fh

  data = bufv.copy_to_str(*flags)
  yield data || (raise Errno::ENOTSUP)
end

Instance Method Details

#create(path, mode, fuse_file_info) ⇒ Object

This method is abstract.

File creation

Parameters:

  • path (String)
  • mode (Integer)
  • fuse_file_info (FuseFileInfo)

Returns:

  • (Object)

    file handle (available to future operations in fuse_file_info.fh)

See Also:



# File 'lib/ffi/libfuse/adapter/ruby.rb', line 373

#fsync(path, datasync, fuse_file_info) ⇒ Object

This method is abstract.

Parameters:

  • path (String)
  • datasync (Boolean)

    if true only the user data should be flushed, not the meta data.

  • fuse_file_info (FuseFileInfo)


# File 'lib/ffi/libfuse/adapter/ruby.rb', line 567

#getxattr(path, name) ⇒ nil|String

This method is abstract.

Get extended attribute

Parameters:

  • path (String)
  • name (String)

    the attribute name

Returns:

  • (nil|String)

    the attribute value or nil if it does not exist

See Also:



# File 'lib/ffi/libfuse/adapter/ruby.rb', line 525

#listxattr(path) ⇒ Array<String>

This method is abstract.

List extended attributes

Parameters:

  • path (String)

Returns:

  • (Array<String>)

    list of xattribute names

See Also:



# File 'lib/ffi/libfuse/adapter/ruby.rb', line 533

#open(path, fuse_file_info) ⇒ Object

This method is abstract.

File open

Parameters:

Returns:

  • (Object)

    file handle available to future operations in fuse_file_info.fh

    File handles are kept from being GC'd until FuseOperations#release

    If the file handle quacks like IO then the file io operations :read, :write, :flush, :fsync, :release will be invoked on the file handle if not implemented by the filesystem

See Also:



# File 'lib/ffi/libfuse/adapter/ruby.rb', line 380

#opendir(path, fuse_file_info) ⇒ Object

This method is abstract.

Directory open

Parameters:

Returns:

  • (Object)

    directory handle (available to future operations in fuse_file_info.fh)

See Also:



# File 'lib/ffi/libfuse/adapter/ruby.rb', line 404

#read(path, size, offset, ffi) ⇒ Array<Object,Integer>, nil|false

This method is abstract.

Read file data.

If not implemented will send ffi.fh (from #open) to IO.read

Parameters:

  • path (String)
  • size (Integer)
  • offset (Integer)
  • ffi (FuseFileInfo)

Returns:

  • (Array<Object,Integer>)

    io, offset will be passed to IO.read(io, size, offset)

  • (nil|false)

    treat as not implemented

Raises:

  • (NotImplementedError, Errno::ENOTSUP)

    treat as not implemented

See Also:



# File 'lib/ffi/libfuse/adapter/ruby.rb', line 456

#read_buf(path, size, offset, info) ⇒ Array<Object,Integer>, nil|false

This method is abstract.

Read file data directly from a buffer

If not implemented first tries ffi.fh.fileno (from #open) as a file descriptor before falling back to #read

Parameters:

  • path (String)
  • size (Integer)
  • offset (Integer)
  • info (FuseFileInfo)

Returns:

  • (Array<Object,Integer>)

    io, offset passed to FuseBufVec#copy_to_io(io, offset)

  • (nil|false)

    treat as not implemented

Raises:

  • (NotImplementedError, Errno::ENOTSUP)

    treat as not implemented

See Also:



# File 'lib/ffi/libfuse/adapter/ruby.rb', line 470

#readdir(path, offset, fuse_file_info, readdir_plus: ) {|name, stat:, offset:, fill_dir_plus:| ... } ⇒ void

This method is abstract.

This method returns an undefined value.

List directory entries

The filesystem may choose between three modes of operation:

1) The readdir implementation ignores the offset parameter yielding only name, and optional stat The yield will always return true so the whole directory is read in a single readdir operation.

2) The readdir implementation keeps track of the offsets of the directory entries. It uses the offset parameter to restart the iteration and yields non-zero offsets for each entry. When the buffer is full, or an error occurs, yielding to the filler function will return false. Subsequent yields will raise StopIteration

3) Return a Dir like object from #opendir and do not implement this method. The directory will be enumerated from offset via calling :seek, :read and :tell on fuse_file_info.fh

Parameters:

  • path (String)
  • offset (Integer)

    the starting offset (inclusive!)

    this is either 0 for a new operation, or the last value previously yielded to the filler function (when it returned false to indicate the buffer was full).

  • fuse_file_info (FuseFileInfo)
  • readdir_plus (Boolean) (defaults to: )

    true if extended readdir is supported (Fuse3 only)

Yields:

Raises:

  • (SystemCallError)

    an appropriate Errno value

See Also:



# File 'lib/ffi/libfuse/adapter/ruby.rb', line 493

This method is abstract.

Resolve target of a symbolic link

Parameters:

  • path (String)
  • size (Integer)

Returns:

  • (String)

    the link target, truncated to size if necessary

See Also:



# File 'lib/ffi/libfuse/adapter/ruby.rb', line 485

#setxattr(path, name, data, flags) ⇒ void

This method is abstract.

This method returns an undefined value.

Set extended attribute

Parameters:

  • path (String)
  • name (String)
  • data (String)
  • flags (Symbol|Integer)

    0, :xattr_create, :xattr_replace

Raises:

  • (Errno::EEXIST)

    for :xattr_create and name already exists

  • (Errno::ENODATA)

    for :xattr_replace and name does not already exist

See Also:



# File 'lib/ffi/libfuse/adapter/ruby.rb', line 540

#utimens(path, atime, mtime, fuse_file_info = nil) ⇒ void

This method is abstract.
Note:

Either atime or mtime can be nil corresponding to utimensat(2) receiving UTIME_OMIT. The special value UTIME_NOW passed from Fuse is automatically set to the current time

This method returns an undefined value.

Change the access and/or modification times of a file with nanosecond resolution

Parameters:

  • path (String)
  • atime (Time|nil)

    if set the file's new access time (in UTC)

  • mtime (Time|nil)

    if set the file's new modification time (in UTC)

  • fuse_file_info (FuseFileInfo) (defaults to: nil)

    (since Fuse3)

See Also:



# File 'lib/ffi/libfuse/adapter/ruby.rb', line 552

#write(path, data, offset, ffi) ⇒ Integer, ...

This method is abstract.

Write file data. If not implemented will pass ffi.fh (from #open) to IO.write

Parameters:

  • path (String)
  • data (String)
  • offset (Integer)
  • ffi (FuseFileInfo)

Returns:

  • (Integer)

    number of bytes written (<= data.size)

  • (IO)

    an IO object (data will be sent to IO.write)

  • (nil|false)

    treat as not implemented

Raises:

  • (NotImplementedError, Errno::ENOTSUP)

    treat as not implemented

See Also:



# File 'lib/ffi/libfuse/adapter/ruby.rb', line 412

#write_buf(path, offset, ffi, &buffer) {|io = nil, *flags| ... } ⇒ Integer, nil|false

This method is abstract.

Write buffered data to a file via one of the following techniques

  1. Yield object and flags to &buffer to write data directly to a FuseBufVec, File or IO and return the yield result (number of bytes written)

  2. Yield with no params (or explicitly nil and flags) to retrieve String data (via FuseBufVec#copy_to_str) to write to path@offset, returning the number of bytes written (eg data.size)

  3. Return nil, false or do not implement to

    • try ffi.fh.fileno (from #open) as a file descriptor
    • or otherwise fallback to #write

Parameters:

  • path (String)
  • offset (Integer)
  • ffi (FuseFileInfo)
  • buffer (Proc)

Yields:

  • (io = nil, *flags)

    Send data to io, or if not set, receive data as a string

Yield Parameters:

Yield Returns:

  • (String)

    if io not supplied, the chunk of data to write is returned

  • (Integer)

    the number of bytes written to io

Returns:

  • (Integer)

    number of bytes written (<= data.size)

  • (nil|false)

    treat as not implemented (do not yield AND return nil/false)

Raises:

  • (NotImplementedError, Errno::ENOTSUP)

    treat as not implemented

See Also:



# File 'lib/ffi/libfuse/adapter/ruby.rb', line 425