ffi-libfuse 0.3.4 → 0.4.1

Sign up to get free protection for your applications and to get access to all the features.
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