Skip to content

Commit

Permalink
refactor: boolean ints and attribute accessor enhancements (#25)
Browse files Browse the repository at this point in the history
  • Loading branch information
lwoggardner authored Jan 26, 2024
1 parent 1443898 commit 64ae66e
Show file tree
Hide file tree
Showing 15 changed files with 561 additions and 225 deletions.
529 changes: 423 additions & 106 deletions lib/ffi/accessors.rb

Large diffs are not rendered by default.

58 changes: 31 additions & 27 deletions lib/ffi/flock.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,32 +18,36 @@ module Enums

include(Accessors)

layout(l_type: Enums::LockType, l_whence: Enums::SeekWhenceShort, l_start: :off_t, l_len: :off_t, l_pid: :pid_t)

l_members = members.grep(/^l_/).map { |m| m[2..].to_sym }

ffi_attr_reader(*l_members, format: 'l_%s')

# @!attribute [r] type
# @return [Symbol] lock type, :rdlck, :wrlck, :unlck

# @!attribute [r] whence
# @return [Symbol] specifies what the offset is relative to, one of :seek_set, :seek_cur or :seek_end
# corresponding to the whence argument to fseek(2) or lseek(2),

# @!attribute [r] start
# @return [Integer] the offset of the start of the region to which the lock applies, and is given in bytes
# relative to the point specified by #{whence} member.

# @!attribute [r] len
# @return [Integer] the length of the region to be locked.
#
# A value of 0 means the region extends to the end of the file.

# @!attribute [r] pid
# @return [Integer] the process ID (see Process Creation Concepts) of the process holding the lock.
# It is filled in by calling fcntl with the F_GETLK command, but is ignored when making a lock. If the
# conflicting lock is an open file description lock (see Open File Description Locks), then this field will be
# set to -1.
layout(
# @!attribute [r] type
# @return [Symbol] lock type, :rdlck, :wrlck, :unlck
l_type: Enums::LockType,

# @!attribute [r] whence
# @return [Symbol] specifies what the offset is relative to, one of :seek_set, :seek_cur or :seek_end
# corresponding to the whence argument to fseek(2) or lseek(2),
l_whence: Enums::SeekWhenceShort,

# @!attribute [r] start
# @return [Integer] the offset of the start of the region to which the lock applies, and is given in bytes
# relative to the point specified by #{whence} member.
l_start: :off_t,

# @!attribute [r] len
# @return [Integer] the length of the region to be locked.
#
# A value of 0 means the region extends to the end of the file.
l_len: :off_t,

# @!attribute [r] pid
# @return [Integer] the process ID (see Process Creation Concepts) of the process holding the lock.
# It is filled in by calling fcntl with the F_GETLK command, but is ignored when making a lock. If the
# conflicting lock is an open file description lock (see Open File Description Locks), then this field will be
# set to -1.
l_pid: :pid_t
)

# Strip leading 'l_' to make attribute names
ffi_attr_reader(**members.to_h { |m| [m[2..].to_sym, m] })
end
end
8 changes: 4 additions & 4 deletions lib/ffi/libfuse/fuse3.rb
Original file line number Diff line number Diff line change
Expand Up @@ -58,12 +58,12 @@ def parse_cmdline(args, handler: nil)
cmdline_opts = FuseCmdlineOpts.new
raise Error unless Libfuse.fuse_parse_cmdline3(args, cmdline_opts).zero?

handler&.fuse_debug(cmdline_opts.debug) if handler.respond_to?(:fuse_debug)
handler&.fuse_debug(cmdline_opts.debug?) if handler.respond_to?(:fuse_debug)

# mimics fuse_main which exits after printing version info, even if -h
if cmdline_opts.show_version
if cmdline_opts.show_version?
show_version(handler)
elsif cmdline_opts.show_help
elsif cmdline_opts.show_help?
show_help(args, handler)
end

Expand Down Expand Up @@ -146,7 +146,7 @@ def io
private

def native_fuse_loop_mt(max_idle_threads: 10, **_options)
Libfuse.fuse_loop_mt3(@fuse, FuseLoopConfig.new.fill(max_idle: max_idle_threads))
Libfuse.fuse_loop_mt3(@fuse, FuseLoopConfig.new.fill(max_idle_threads: max_idle_threads))
end

def unmount
Expand Down
28 changes: 9 additions & 19 deletions lib/ffi/libfuse/fuse_args.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,17 @@
require_relative 'fuse_version'
require_relative 'fuse_opt'
require_relative '../ruby_object'
require_relative '../boolean_int'
require_relative '../accessors'

module FFI
# Ruby FFI Binding for [libfuse](https://github.com/libfuse/libfuse)
module Libfuse
# struct fuse_args
class FuseArgs < FFI::Struct
layout :argc, :int, :argv, :pointer, :allocated, :int
include FFI::Accessors

layout :argc, :int, :argv, :pointer, :allocated, :bool_int

# Create a fuse_args struct from command line options
# @param [Array<String>] argv command line args
Expand All @@ -34,33 +38,17 @@ def fill(*argv)
@arg_vector[argv.size].put_pointer(0, FFI::Pointer::NULL)
self[:argv] = @arg_vector
self[:argc] = argv.size
self[:allocated] = 0
self[:allocated] = false # ie libfuse did not allocate
self
end

# @!attribute [r] argc
# @return [Integer] count of args
def argc
self[:argc]
end

# @!attribute [r] argv
# @return [Array<String>] list of args
def argv
ffi_attr_reader(:argv) do
# noinspection RubyResolve
self[:argv].get_array_of_pointer(0, argc).map(&:read_string)
end

# @!visibility private
def allocated
self[:allocated]
end

# @!visibility private
def inspect
"#{self.class.name} - #{%i[argc argv allocated].to_h { |m| [m, send(m)] }}"
end

# Add an arg to this arg list
# @param [String] arg
def add(arg)
Expand Down Expand Up @@ -151,6 +139,8 @@ def parse!(opts, data = nil, ignore: %i[non_option unmatched], &block)
# Valid return values from parse! block
FUSE_OPT_PROC_RETURN = { error: -1, keep: 1, handled: 0, discard: 0 }.freeze

ffi_attr_reader(:argc, :allocated?)

def fuse_opt_proc(symbols, bool_opts, param_opts, ignore, &block)
proc do |data, arg, key, out|
key = symbols[key]
Expand Down
31 changes: 16 additions & 15 deletions lib/ffi/libfuse/fuse_cmdline_opts.rb
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
# frozen_string_literal: true

require_relative '../accessors'
require_relative '../boolean_int'
require_relative 'fuse_loop_config'
module FFI
module Libfuse
#
# struct fuse_cmdline_opts {
# int singlethread;
# int foreground;
Expand All @@ -16,27 +16,28 @@ module Libfuse
# int clone_fd;
# unsigned int max_idle_threads;
# };

# Command line options
# @!visibility private
class FuseCmdlineOpts < FFI::Struct
include(FFI::Accessors)

layout(
single_thread: :int,
foreground: :int,
debug: :int,
nodefault_subtype: :int,
spec = {
single_thread: :bool_int,
foreground: :bool_int,
debug: :bool_int,
nodefault_subtype: :bool_int,
mountpoint: :string,
show_version: :int,
show_help: :int,
clone_fd: :int,
show_version: :bool_int,
show_help: :bool_int,
clone_fd: :bool_int,
max_idle_threads: :int
)
}

layout(spec)

# int to booleans
ffi_attr_reader(:single_thread, :foreground, :debug, :nodefault_subtype, :show_version, :show_help,
:clone_fd) do |v|
v != 0
end
bool, = spec.partition { |_, v| v == :bool_int }
ffi_attr_reader(*bool.map { |k, _| "#{k}?" })

ffi_attr_reader(:max_idle_threads, :mountpoint)
end
Expand Down
47 changes: 25 additions & 22 deletions lib/ffi/libfuse/fuse_config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ module Libfuse
# This structure is initialized from the arguments passed to fuse_new(), and then passed to the file system's init()
# handler which should ensure that the configuration is compatible with the file system implementation.
#
# Some options can only be set via the filesystem init method (use_ino etc..) because the filesystem either
# Some options can only be set via the filesystem init method (:use_ino etc..) because the filesystem either
# supports them or it doesn't.
class FuseConfig < FFI::Struct
include FFI::Accessors
Expand Down Expand Up @@ -40,7 +40,7 @@ class FuseConfig < FFI::Struct
# @!attribute [rw] negative_timeout
# The timeout in seconds for which a negative lookup will be cached.
#
# This means, that if file did not exist (lookup retuned ENOENT), the lookup will only be redone after the
# This means, that if file did not exist (lookup returned ENOENT), the lookup will only be redone after the
# timeout, and the file/directory will be assumed to not exist until then. A value of zero means that
# negative lookups are not cached.
#
Expand All @@ -55,7 +55,7 @@ class FuseConfig < FFI::Struct
# @return [Float]
attr_timeout: :double,

# @!attribute [rw] intr
# @!attribute [rw] intr?
# Allow requests to be interrupted
# @return [Boolean]
intr: :bool_int,
Expand All @@ -80,7 +80,7 @@ class FuseConfig < FFI::Struct
# @return [Integer]
remember: :int,

# @!attribute [rw] hard_remove
# @!attribute [rw] hard_remove?
# should open files be removed immediately
#
# The default behavior is that if an open file is deleted, the file is renamed to a hidden file
Expand All @@ -95,7 +95,7 @@ class FuseConfig < FFI::Struct
# @return [Boolean]
hard_remove: :bool_int,

# @!attribute [rw] use_ino
# @!attribute [rw] use_ino?
# use filesystem provided inode values
#
# Honor the st_ino field in the functions getattr() and fill_dir(). This value is used to fill in the st_ino
Expand All @@ -109,16 +109,16 @@ class FuseConfig < FFI::Struct
# @return [Boolean]
use_ino: :bool_int,

# @!attribute [rw] readdir_ino
# generate inodes for readdir even if {#use_ino} is set
# @!attribute [rw] readdir_ino?
# generate inodes for readdir even if {#use_ino?} is set
#
# If use_ino option is not given, still try to fill in the d_ino field in readdir(2). If the name was
# previously looked up, and is still in the cache, the inode number found there will be used. Otherwise it
# will be set to -1. If use_ino option is given, this option is ignored.
# @return [Boolean]
readdir_ino: :bool_int,

# @!attribute [rw] direct_io
# @!attribute [rw] direct_io?
# disables the use of kernel page cache (file content cache) in the kernel for this filesystem.
#
# This has several affects:
Expand All @@ -135,21 +135,21 @@ class FuseConfig < FFI::Struct
# @return [Boolean]
direct_io: :bool_int,

# @!attribute [rw] kernel_cache
# @!attribute [rw] kernel_cache?
# disables flushing the cache of the file contents on every open(2).
#
# This should only be enabled on filesystem where the file data is never changed externally (not through the
# mounted FUSE filesystem). Thus it is not suitable for network filesystem and other intermediate filesystem.
#
# **Note**: if neither this option or {#direct_io} is specified data is still cached after the open(2),
# **Note**: if neither this option or {#direct_io?} is specified data is still cached after the open(2),
# so a read(2) system call will not always initiate a read operation.
#
# Internally, enabling this option causes fuse to set {FuseFileInfo#keep_cache} overwriting any value that was
# put there by the file system.
# @return [Boolean]
kernel_cache: :bool_int,

# @!attribute [rw] auto_cache
# @!attribute [rw] auto_cache?
# invalidate cached data on open based on changes in file attributes
#
# This option is an alternative to `kernel_cache`. Instead of unconditionally keeping cached data, the cached
Expand All @@ -165,7 +165,7 @@ class FuseConfig < FFI::Struct
ac_attr_timeout_set: :bool_int,
ac_attr_timeout: :double,

# @!attribute [rw] nullpath_ok
# @!attribute [rw] nullpath_ok?
# operations on open files and directories are ok to receive nil paths
#
# If this option is given the file-system handlers for the following operations will not receive path
Expand All @@ -185,21 +185,24 @@ class FuseConfig < FFI::Struct
layout(spec)

# Find the attrs that have a corresponding setter (prefix set_ or suffix _set
# map attr => setter
setters = spec.keys
.map { |k| [k, k.to_s.sub(/^set_/, '').sub(/_set$/, '').to_sym] }
.reject { |(s, a)| s == a }.to_h
.map { |k| [k.to_s.sub(/^set_/, '').sub(/_set$/, '').to_sym, k] }
.reject { |(s, a)| s == a }
.to_h

setters.each do |(setter, attr)|
ffi_attr_reader(attr) { |val| self[setter] ? val : nil }
ffi_attr_reader_method(*setters.keys) do
self[setters[__method__]] ? self[__method__] : nil
end

ffi_attr_writer(attr) do |val|
self[setter] = !val.nil?
val || 0
end
ffi_attr_writer_method(*setters.keys) do |val|
self[setters[__method__]] = !val.nil?
self[__method__] = val || 0
end

remaining = (spec.keys - setters.keys - setters.values).map { |a| spec[a] == :bool_int ? "#{a}?" : a }
ffi_attr_accessor(*remaining)
ffi_attr_reader(:show_help?, :debug?)
remaining = (spec.keys - setters.keys - setters.values - %i[show_help modules debug])
ffi_attr_accessor(*remaining.map { |a| spec[a] == :bool_int ? "#{a}?" : a })
end
end
end
2 changes: 1 addition & 1 deletion lib/ffi/libfuse/fuse_conn_info.rb
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,7 @@ def capable?(*capabilities)
# or unwanted
# @return [Array<Symbol>]
# @see capable
ffi_attr_reader(:want, simple: false) do |*caps, **h|
ffi_attr_reader_method(:want) do |*caps, **h|
next self[:want] if caps.empty? && h.empty?

h.merge!(caps.pop) if caps.last.is_a?(Hash)
Expand Down
3 changes: 2 additions & 1 deletion lib/ffi/libfuse/fuse_context.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ class FuseContext < FFI::Struct
base[:umask] = :mode_t if FUSE_VERSION >= 28
layout base

ffi_attr_reader(*members, simple: false) do
# Define readers, safe from null access
ffi_attr_reader_method(*members) do
m = __method__

# Use overrides if they are available, or the default context if the underlying memory is invalid
Expand Down
Loading

0 comments on commit 64ae66e

Please sign in to comment.