ffi-libfuse 0.3.4 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +29 -0
  3. data/README.md +1 -1
  4. data/lib/ffi/accessors.rb +419 -93
  5. data/lib/ffi/boolean_int.rb +1 -1
  6. data/lib/ffi/devt.rb +36 -10
  7. data/lib/ffi/flock.rb +31 -27
  8. data/lib/ffi/libfuse/adapter/context.rb +1 -1
  9. data/lib/ffi/libfuse/adapter/debug.rb +54 -16
  10. data/lib/ffi/libfuse/adapter/fuse2_compat.rb +43 -26
  11. data/lib/ffi/libfuse/adapter/fuse3_support.rb +7 -8
  12. data/lib/ffi/libfuse/adapter/interrupt.rb +1 -1
  13. data/lib/ffi/libfuse/adapter/pathname.rb +1 -1
  14. data/lib/ffi/libfuse/adapter/ruby.rb +211 -160
  15. data/lib/ffi/libfuse/adapter/safe.rb +70 -22
  16. data/lib/ffi/libfuse/callbacks.rb +2 -1
  17. data/lib/ffi/libfuse/filesystem/accounting.rb +1 -1
  18. data/lib/ffi/libfuse/filesystem/mapped_files.rb +33 -7
  19. data/lib/ffi/libfuse/filesystem/pass_through_dir.rb +0 -1
  20. data/lib/ffi/libfuse/filesystem/virtual_dir.rb +294 -127
  21. data/lib/ffi/libfuse/filesystem/virtual_file.rb +85 -79
  22. data/lib/ffi/libfuse/filesystem/virtual_fs.rb +34 -15
  23. data/lib/ffi/libfuse/filesystem/virtual_link.rb +60 -0
  24. data/lib/ffi/libfuse/filesystem/virtual_node.rb +104 -87
  25. data/lib/ffi/libfuse/filesystem.rb +1 -1
  26. data/lib/ffi/libfuse/fuse2.rb +3 -2
  27. data/lib/ffi/libfuse/fuse3.rb +6 -6
  28. data/lib/ffi/libfuse/fuse_args.rb +14 -21
  29. data/lib/ffi/libfuse/fuse_buf.rb +112 -0
  30. data/lib/ffi/libfuse/fuse_buf_vec.rb +228 -0
  31. data/lib/ffi/libfuse/fuse_cmdline_opts.rb +19 -16
  32. data/lib/ffi/libfuse/fuse_common.rb +10 -4
  33. data/lib/ffi/libfuse/fuse_config.rb +35 -23
  34. data/lib/ffi/libfuse/fuse_conn_info.rb +1 -1
  35. data/lib/ffi/libfuse/fuse_context.rb +2 -1
  36. data/lib/ffi/libfuse/fuse_loop_config.rb +68 -20
  37. data/lib/ffi/libfuse/fuse_operations.rb +86 -41
  38. data/lib/ffi/libfuse/gem_helper.rb +2 -9
  39. data/lib/ffi/libfuse/io.rb +56 -0
  40. data/lib/ffi/libfuse/main.rb +33 -26
  41. data/lib/ffi/libfuse/test_helper.rb +67 -61
  42. data/lib/ffi/libfuse/version.rb +1 -1
  43. data/lib/ffi/libfuse.rb +1 -1
  44. data/lib/ffi/stat/native.rb +4 -4
  45. data/lib/ffi/stat.rb +35 -12
  46. data/lib/ffi/stat_vfs.rb +1 -2
  47. data/lib/ffi/struct_array.rb +2 -1
  48. data/lib/ffi/struct_wrapper.rb +6 -4
  49. data/sample/hello_fs.rb +1 -1
  50. metadata +6 -3
  51. data/lib/ffi/libfuse/fuse_buffer.rb +0 -257
data/lib/ffi/devt.rb CHANGED
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'ffi'
4
-
5
4
  module FFI
6
5
  # Calculate major/minor device numbers for use with mknod etc..
7
6
  # @see makedev(3)
@@ -15,22 +14,25 @@ module FFI
15
14
  # @param [Integer] major
16
15
  # @param [Integer] minor
17
16
  # @return [Integer] combined major/minor to a single value to pass to mknod etc
18
- attach_function :makedev, "#{prefix}makedev".to_sym, %i[int int], :int
17
+ attach_function :makedev, :"#{prefix}makedev", %i[int int], :int
19
18
 
20
19
  # @!method major(dev)
21
20
  # @param [Integer] dev
22
21
  # @return [Integer] the major component of dev
23
- attach_function :major, "#{prefix}major".to_sym, [:int], :int
22
+ attach_function :major, :"#{prefix}major", [:int], :int
24
23
 
25
24
  # @!method minor(dev)
26
25
  # @param [Integer] dev
27
26
  # @return [Integer] the minor component of dev
28
- attach_function :minor, "#{prefix}minor".to_sym, [:int], :int
27
+ attach_function :minor, :"#{prefix}minor", [:int], :int
29
28
  rescue FFI::NotFoundError
30
- case Platform::NAME
31
- when 'x86_64-darwin'
32
- # From https://github.com/golang/go/issues/8106 these functions are not defined on Darwin.
33
- class << self
29
+
30
+ class << self
31
+ # rubocop:disable Naming/MethodParameterName
32
+ case RUBY_PLATFORM
33
+ when 'x86_64-darwin'
34
+ # From https://github.com/golang/go/issues/8106 these functions are not defined on Darwin.
35
+
34
36
  # define major(x) ((int32_t)(((u_int32_t)(x) >> 24) & 0xff))
35
37
  def major(dev)
36
38
  (dev >> 24) & 0xff
@@ -45,9 +47,33 @@ module FFI
45
47
  def makedev(major, minor)
46
48
  (major << 24) | minor
47
49
  end
50
+
51
+ when 'x86_64-linux-musl' # eg alpine linux
52
+ # #define major(x) \
53
+ # ((unsigned)( (((x)>>31>>1) & 0xfffff000) | (((x)>>8) & 0x00000fff) ))
54
+ def major(x)
55
+ ((x >> 31 >> 1) & 0xfffff000) | ((x >> 8) & 0x00000fff)
56
+ end
57
+
58
+ # #define minor(x) \
59
+ # ((unsigned)( (((x)>>12) & 0xffffff00) | ((x) & 0x000000ff) ))
60
+ #
61
+ def minor(x)
62
+ ((x >> 12) & 0xffffff00) | (x & 0x000000ff)
63
+ end
64
+
65
+ # #define makedev(x,y) ( \
66
+ # (((x)&0xfffff000ULL) << 32) | \
67
+ # (((x)&0x00000fffULL) << 8) | \
68
+ # (((y)&0xffffff00ULL) << 12) | \
69
+ # (((y)&0x000000ffULL)) )
70
+ def makedev(x, y)
71
+ ((x & 0xfffff000) << 32) | ((x & 0x00000fff) << 8) | ((y & 0xffffff00) << 12) | (y & 0x000000ff)
72
+ end
73
+ else
74
+ raise
48
75
  end
49
- else
50
- raise
76
+ # rubocop:enable Naming/MethodParameterName
51
77
  end
52
78
  end
53
79
  end
data/lib/ffi/flock.rb CHANGED
@@ -18,32 +18,36 @@ module FFI
18
18
 
19
19
  include(Accessors)
20
20
 
21
- layout(l_type: Enums::LockType, l_whence: Enums::SeekWhenceShort, l_start: :off_t, l_len: :off_t, l_pid: :pid_t)
22
-
23
- l_members = members.grep(/^l_/).map { |m| m[2..].to_sym }
24
-
25
- ffi_attr_reader(*l_members, format: 'l_%s')
26
-
27
- # @!attribute [r] type
28
- # @return [Symbol] lock type, :rdlck, :wrlck, :unlck
29
-
30
- # @!attribute [r] whence
31
- # @return [Symbol] specifies what the offset is relative to, one of :seek_set, :seek_cur or :seek_end
32
- # corresponding to the whence argument to fseek(2) or lseek(2),
33
-
34
- # @!attribute [r] start
35
- # @return [Integer] the offset of the start of the region to which the lock applies, and is given in bytes
36
- # relative to the point specified by #{whence} member.
37
-
38
- # @!attribute [r] len
39
- # @return [Integer] the length of the region to be locked.
40
- #
41
- # A value of 0 means the region extends to the end of the file.
42
-
43
- # @!attribute [r] pid
44
- # @return [Integer] the process ID (see Process Creation Concepts) of the process holding the lock.
45
- # It is filled in by calling fcntl with the F_GETLK command, but is ignored when making a lock. If the
46
- # conflicting lock is an open file description lock (see Open File Description Locks), then this field will be
47
- # set to -1.
21
+ layout(
22
+ # @!attribute [r] type
23
+ # @return [Symbol] lock type, :rdlck, :wrlck, :unlck
24
+ l_type: Enums::LockType,
25
+
26
+ # @!attribute [r] whence
27
+ # @return [Symbol] specifies what the offset is relative to, one of :seek_set, :seek_cur or :seek_end
28
+ # corresponding to the whence argument to fseek(2) or lseek(2),
29
+ l_whence: Enums::SeekWhenceShort,
30
+
31
+ # @!attribute [r] start
32
+ # @return [Integer] the offset of the start of the region to which the lock applies, and is given in bytes
33
+ # relative to the point specified by #{whence} member.
34
+ l_start: :off_t,
35
+
36
+ # @!attribute [r] len
37
+ # @return [Integer] the length of the region to be locked.
38
+ #
39
+ # A value of 0 means the region extends to the end of the file.
40
+ l_len: :off_t,
41
+
42
+ # @!attribute [r] pid
43
+ # @return [Integer] the process ID (see Process Creation Concepts) of the process holding the lock.
44
+ # It is filled in by calling fcntl with the F_GETLK command, but is ignored when making a lock. If the
45
+ # conflicting lock is an open file description lock (see Open File Description Locks), then this field will be
46
+ # set to -1.
47
+ l_pid: :pid_t
48
+ )
49
+
50
+ # Strip leading 'l_' to make attribute names
51
+ ffi_attr_reader(**members.to_h { |m| [m[2..].to_sym, m] })
48
52
  end
49
53
  end
@@ -18,7 +18,7 @@ module FFI
18
18
  )
19
19
  return wrappers unless defined?(super)
20
20
 
21
- super(*wrappers)
21
+ super
22
22
  end
23
23
 
24
24
  module_function
@@ -1,13 +1,24 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative 'safe'
4
+
3
5
  module FFI
4
6
  module Libfuse
5
7
  module Adapter
6
8
  # Debug callbacks
7
9
  #
8
10
  # When included in a filesystem class, and if debugging is enabled via {Main#fuse_debug}, then installs a wrapper
9
- # via #{FuseCallbacks#fuse_wrappers} to log callbacks via #warn
11
+ # via #{FuseCallbacks#fuse_wrappers} to log callbacks.
12
+ #
13
+ # Simple format options can be handled by #{debug_config}, or override the **Module Functions** on an including
14
+ # class for more programmatic control of output.
15
+ #
16
+ # @note {Debug} includes {Safe} as it expects to handle (and re-raise) exceptions.
10
17
  module Debug
18
+ include Safe
19
+
20
+ # Default format
21
+ # @see debug_callback
11
22
  DEFAULT_FORMAT = "%<p>s %<n>s %<t>s %<m>s(%<a>s)\n\t=> %<r>s"
12
23
 
13
24
  # @return [Boolean] true if debug is enabled
@@ -26,11 +37,11 @@ module FFI
26
37
  def fuse_wrappers(*wrappers)
27
38
  conf = { prefix: self.class.name }.merge!(debug_config)
28
39
  # validate config for bad formats, strftime etc
29
- Debug.debug_format(:test_debug, [], :result, **conf)
30
- wrappers << proc { |fm, *args, &b| Debug.debug_callback(fm, *args, **conf, &b) } if debug?
40
+ debug_format(:test_debug, [], :result, **conf)
41
+ wrappers << proc { |fm, *args, &b| debug_callback(fm, *args, **conf, &b) } if debug?
31
42
  return wrappers unless defined?(super)
32
43
 
33
- super(*wrappers)
44
+ super
34
45
  end
35
46
 
36
47
  # @!visibility private
@@ -41,10 +52,11 @@ module FFI
41
52
 
42
53
  module_function
43
54
 
44
- # Debug fuse method, args and result
55
+ # Debug fuse method, args and result of yielding args to the block
56
+ #
45
57
  # @param [Symbol] fuse_method the callback name
46
58
  # @param [Array] args callback arguments
47
- # @param [Hash<Symbol,String>] options see {debug} for defaults
59
+ # @param [Hash<Symbol,String>] options see {debug_format} for defaults
48
60
  # @option options [String] prefix
49
61
  # @option options [String] strftime a date time format
50
62
  # @option options [String] format format string with fields
@@ -55,25 +67,49 @@ module FFI
55
67
  # * %<a>: Comma separate list of arguments
56
68
  # * %<r>: Result of the method call (or any error raised)
57
69
  # * %<p>: The value of prefix option
70
+ # @raise [SystemCallError]
71
+ # expected Errors raised from callbacks are logged with their cause (if any)
72
+ # @raise [StandardError,ScriptError]
73
+ # unexpected Errors raised from callbacks are logged with their backtrace
58
74
  def debug_callback(fuse_method, *args, **options)
59
- debug(fuse_method, args, yield(*args), **options)
60
- rescue SystemCallError => e
61
- # expected behaviour
62
- debug(fuse_method, args, "#{e.class.name}(errno=#{e.errno}): #{e.message}", **options)
63
- raise
75
+ result = yield(*args)
76
+ debug(fuse_method, args, result, **options)
77
+ result
64
78
  rescue StandardError, ScriptError => e
65
- # unexpected, debug with backtrace
66
- debug(fuse_method, args, (["#{e.class.name}: #{e.message}"] + e.backtrace).join("\n\t"), **options)
79
+ debug(fuse_method, args, error_message(e), **options)
80
+ debug_error(e)
67
81
  raise
68
82
  end
69
83
 
70
- # @!visibility private
84
+ # @!group Module Functions
85
+
86
+ # Logs the callback
71
87
  def debug(fuse_method, args, result, **options)
72
88
  warn debug_format(fuse_method, args, result, **options)
73
- result
74
89
  end
75
90
 
76
- # @!visibility private
91
+ # @param [Exception] err
92
+ # @return [String] the detailed error message for err
93
+ def error_message(err)
94
+ if err.is_a?(SystemCallError)
95
+ "#{err.class.name}(errno=#{err.errno}): #{err.message}"
96
+ else
97
+ "#{err.class.name}: #{err.message}"
98
+ end
99
+ end
100
+
101
+ # Log additional information for errors (cause/backtrace etc)
102
+ # @see debug_callback
103
+ def debug_error(err)
104
+ if err.is_a?(SystemCallError)
105
+ warn "Caused by #{error_message(err.cause)}" if err.cause
106
+ else
107
+ warn err.backtrace.join("\n\t")
108
+ end
109
+ end
110
+
111
+ # @return [String] the formatted debug message
112
+ # @see debug_callback
77
113
  def debug_format(fuse_method, args, result, prefix: 'DEBUG', strftime: '%FT%T%:z', format: DEFAULT_FORMAT)
78
114
  format(format,
79
115
  p: prefix,
@@ -83,6 +119,8 @@ module FFI
83
119
  a: args.map(&:to_s).join(','),
84
120
  r: result)
85
121
  end
122
+
123
+ # @!endgroup
86
124
  end
87
125
  end
88
126
  end
@@ -12,27 +12,27 @@ module FFI
12
12
  if FUSE_MAJOR_VERSION == 2
13
13
  # @!visibility private
14
14
  def getattr(path, stat, fuse_file_info = nil)
15
- super(path, stat, fuse_file_info)
15
+ super
16
16
  end
17
17
 
18
18
  def truncate(path, size, fuse_file_info = nil)
19
- super(path, size, fuse_file_info)
19
+ super
20
20
  end
21
21
 
22
22
  def init(fuse_conn_info, fuse_config = nil)
23
- super(fuse_conn_info, fuse_config)
23
+ super
24
24
  end
25
25
 
26
26
  def chown(path, uid, gid, fuse_file_info = nil)
27
- super(path, uid, gid, fuse_file_info)
27
+ super
28
28
  end
29
29
 
30
30
  def chmod(path, mode, fuse_file_info = nil)
31
- super(path, mode, fuse_file_info)
31
+ super
32
32
  end
33
33
 
34
- def utimens(path, atime, mtime, fuse_file_info = nil)
35
- super(path, atime, mtime, fuse_file_info)
34
+ def utimens(path, times, fuse_file_info = nil)
35
+ super
36
36
  end
37
37
 
38
38
  def readdir(path, buffer, filler, offset, fuse_file_info, fuse_readdir_flag = 0)
@@ -40,49 +40,66 @@ module FFI
40
40
  super(path, buffer, f3_fill, offset, fuse_file_info, fuse_readdir_flag)
41
41
  end
42
42
 
43
- def fgetattr(path, stat, ffi)
44
- stat.clear # For some reason (at least on OSX) the stat is not clear when this is called.
45
- getattr(path, stat, ffi)
46
- 0
47
- end
43
+ def fuse_respond_to?(fuse_method)
44
+ # getdir is never supported here anyway
45
+ # fgetattr and ftruncate already fallback to the respective basic method
46
+ return false if %i[getdir fgetattr ftruncate].include?(fuse_method)
48
47
 
49
- def ftruncate(*args)
50
- truncate(*args)
48
+ super
51
49
  end
52
50
 
53
- def fuse_respond_to?(fuse_method)
54
- fuse_method = fuse_method[1..].to_sym if %i[fgetattr ftruncate].include?(fuse_method)
55
- super(fuse_method)
51
+ def fuse_options(args)
52
+ super if defined?(super)
53
+ return unless respond_to?(:init_fuse_config)
54
+
55
+ FUSE_CONFIG_ONLY_ATTRIBUTES.each do |opt|
56
+ args.add("-o#{opt}") if fuse_config.send(opt)
57
+ end
56
58
  end
57
59
 
58
60
  def fuse_flags
59
61
  res = defined?(super) ? super : []
60
- if respond_to?(:init_fuse_config)
61
- fuse_config = FuseConfig.new
62
- init_fuse_config(fuse_config, :fuse2)
63
- res << :nullpath_ok if fuse_config.nullpath_ok?
64
- end
62
+ return res unless respond_to?(:init_fuse_config)
65
63
 
64
+ FUSE_CONFIG_FLAGS.each { |opt| res << opt if fuse_config.send(opt) }
66
65
  res
67
66
  end
68
67
 
68
+ private
69
+
70
+ def fuse_config
71
+ @fuse_config ||= begin
72
+ fuse_config = FuseConfig.new
73
+ init_fuse_config(fuse_config, :fuse2) if respond_to?(:init_fuse_config)
74
+ fuse_config
75
+ end
76
+ end
77
+
69
78
  else
70
79
  def init(*args)
71
- init_fuse_config(args.detect { |a| a.is_a?(FuseConfig) }) if respond_to?(:init_fuse_config)
80
+ init_fuse_config(args.detect { |a| a.is_a?(FuseConfig) }, :fuse3) if respond_to?(:init_fuse_config)
72
81
  super if defined?(super)
73
82
  end
74
83
  end
75
84
  end
76
85
 
86
+ # Attributes in Fuse3 config that cannot be set by Fuse3 options. If set via {init_fuse_config} the
87
+ # equivalent options will be force set under Fuse 2
88
+ FUSE_CONFIG_ONLY_ATTRIBUTES = %i[hard_remove use_ino readdir_ino direct_io].freeze
89
+
90
+ # Attributes in Fuse3 config that were {FuseOperations#fuse_flags} in Fuse2. If set via {init_fuse_config} the
91
+ # equivalent flags will be added
92
+ FUSE_CONFIG_FLAGS = %i[nullpath_ok].freeze
93
+
77
94
  # @!visibility private
78
95
  def self.included(mod)
79
- mod.prepend(Prepend)
96
+ mod.prepend(Prepend) if FUSE_MAJOR_VERSION < 3
80
97
  end
81
98
 
82
99
  # @!method init_fuse_config(fuse_config,compat)
83
100
  # @abstract
84
- # Define this method to configure the fuse config object so that under Fuse2 the config options
85
- # can be converted to appropriate flags.
101
+ # Define this method to configure the {FuseConfig} object so that under Fuse2 the config options
102
+ # can be converted to appropriate flags or options
86
103
  #
87
104
  # @param [FuseConfig] fuse_config the fuse config object
88
105
  # @param [Symbol] compat either :fuse2 or :fuse3
@@ -24,24 +24,24 @@ module FFI
24
24
  fi = args.pop
25
25
  return fgetattr(*args, fi) if fi && fuse_super_respond_to?(:fgetattr)
26
26
 
27
- super(*args)
27
+ super
28
28
  end
29
29
 
30
30
  def truncate(*args)
31
31
  fi = args.pop
32
32
  return ftruncate(*args, fi) if fi && fuse_super_respond_to?(:ftruncate)
33
33
 
34
- super(*args)
34
+ super
35
35
  end
36
36
 
37
37
  def chown(*args)
38
38
  args.pop
39
- super(*args)
39
+ super
40
40
  end
41
41
 
42
42
  def chmod(*args)
43
43
  args.pop
44
- super(*args)
44
+ super
45
45
  end
46
46
 
47
47
  # TODO: Fuse3 deprecated flag utime_omit_ok - which meant that UTIME_OMIT and UTIME_NOW are passed through
@@ -50,14 +50,14 @@ module FFI
50
50
  # but there is no way to handle OMIT
51
51
  def utimens(*args)
52
52
  args.pop
53
- super(*args) if defined?(super)
53
+ super if defined?(super)
54
54
  end
55
55
 
56
56
  def init(*args)
57
57
  args.pop
58
58
 
59
59
  # TODO: populate FuseConfig with output from fuse_flags/FuseConnInfo where appropriate
60
- super(*args)
60
+ super
61
61
  end
62
62
 
63
63
  def readdir(*args, &block)
@@ -71,7 +71,7 @@ module FFI
71
71
  proc { |buf, name, stat, off| a.call(buf, name, stat, off, 0) }
72
72
  end
73
73
 
74
- super(*args, &block)
74
+ super
75
75
  end
76
76
 
77
77
  def fuse_respond_to(fuse_callback)
@@ -81,7 +81,6 @@ module FFI
81
81
 
82
82
  # @!visibility private
83
83
  def self.included(mod)
84
- # We prepend our shim module so caller doesn't have to call super
85
84
  mod.prepend(Prepend) if FUSE_MAJOR_VERSION > 2
86
85
  end
87
86
  end
@@ -20,7 +20,7 @@ module FFI
20
20
  }
21
21
  return wrappers unless defined?(super)
22
22
 
23
- super(*wrappers)
23
+ super
24
24
  end
25
25
 
26
26
  module_function
@@ -15,7 +15,7 @@ module FFI
15
15
  }
16
16
  return wrappers unless defined?(super)
17
17
 
18
- super(*wrappers)
18
+ super
19
19
  end
20
20
  end
21
21
  end