ffi-libfuse 0.3.4 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +22 -0
- data/README.md +1 -1
- data/lib/ffi/accessors.rb +21 -7
- data/lib/ffi/boolean_int.rb +1 -1
- data/lib/ffi/devt.rb +3 -3
- data/lib/ffi/libfuse/adapter/debug.rb +53 -15
- data/lib/ffi/libfuse/adapter/fuse2_compat.rb +38 -21
- data/lib/ffi/libfuse/adapter/fuse3_support.rb +0 -1
- data/lib/ffi/libfuse/adapter/ruby.rb +210 -159
- data/lib/ffi/libfuse/adapter/safe.rb +69 -21
- data/lib/ffi/libfuse/callbacks.rb +2 -1
- data/lib/ffi/libfuse/filesystem/accounting.rb +1 -1
- data/lib/ffi/libfuse/filesystem/mapped_files.rb +33 -7
- data/lib/ffi/libfuse/filesystem/pass_through_dir.rb +0 -1
- data/lib/ffi/libfuse/filesystem/virtual_dir.rb +293 -126
- data/lib/ffi/libfuse/filesystem/virtual_file.rb +85 -79
- data/lib/ffi/libfuse/filesystem/virtual_fs.rb +34 -15
- data/lib/ffi/libfuse/filesystem/virtual_link.rb +60 -0
- data/lib/ffi/libfuse/filesystem/virtual_node.rb +104 -87
- data/lib/ffi/libfuse/filesystem.rb +1 -1
- data/lib/ffi/libfuse/fuse2.rb +3 -2
- data/lib/ffi/libfuse/fuse3.rb +1 -1
- data/lib/ffi/libfuse/fuse_args.rb +5 -2
- data/lib/ffi/libfuse/fuse_buf.rb +112 -0
- data/lib/ffi/libfuse/fuse_buf_vec.rb +228 -0
- data/lib/ffi/libfuse/fuse_common.rb +10 -4
- data/lib/ffi/libfuse/fuse_config.rb +16 -7
- data/lib/ffi/libfuse/fuse_operations.rb +86 -41
- data/lib/ffi/libfuse/gem_helper.rb +2 -9
- data/lib/ffi/libfuse/io.rb +56 -0
- data/lib/ffi/libfuse/main.rb +27 -24
- data/lib/ffi/libfuse/test_helper.rb +68 -60
- data/lib/ffi/libfuse/version.rb +1 -1
- data/lib/ffi/libfuse.rb +1 -1
- data/lib/ffi/stat/native.rb +4 -4
- data/lib/ffi/stat.rb +19 -3
- data/lib/ffi/struct_array.rb +2 -1
- data/sample/hello_fs.rb +1 -1
- metadata +6 -3
- data/lib/ffi/libfuse/fuse_buffer.rb +0 -257
@@ -5,13 +5,6 @@ module FFI
|
|
5
5
|
module Libfuse
|
6
6
|
# @!visibility private
|
7
7
|
class GemHelper
|
8
|
-
SEMVER_TAG_REGEX = /^v\d+\.\d+\.\d+/.freeze
|
9
|
-
|
10
|
-
# branches the format is refs/heads/<branch_name>,
|
11
|
-
# tags it is refs/tags/<tag_name>.
|
12
|
-
# for pull requests it is refs/pull/<pr_number>/merge,
|
13
|
-
GIT_REF_TYPES = { 'heads' => :branch, 'tags' => :tag, 'pull' => :pull }.freeze
|
14
|
-
|
15
8
|
class << self
|
16
9
|
# set when install'd.
|
17
10
|
attr_accessor :instance
|
@@ -33,7 +26,7 @@ module FFI
|
|
33
26
|
return [ref, nil] unless ref&.start_with?('refs/')
|
34
27
|
|
35
28
|
_refs, ref_type, ref_name = ref.split('/', 3)
|
36
|
-
[ref_name,
|
29
|
+
[ref_name, { 'heads' => :branch, 'tags' => :tag, 'pull' => :pull }[ref_type]]
|
37
30
|
end
|
38
31
|
|
39
32
|
def gem_version(main_branch:, version:, env: ENV)
|
@@ -44,7 +37,7 @@ module FFI
|
|
44
37
|
when :branch
|
45
38
|
ref_name == main_branch ? [version] : [version, ref_name]
|
46
39
|
when :tag
|
47
|
-
|
40
|
+
/^v\d+\.\d+\.\d+/.match?(ref_name) ? [ref_name[1..]] : [version, ref_name]
|
48
41
|
when :pull
|
49
42
|
pr_number, merge, _rest = ref_name.split('/')
|
50
43
|
# GITHUB_BASE_REF The name of the base ref or target branch of the pull request in a workflow run
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module FFI
|
4
|
+
module Libfuse
|
5
|
+
# Helpers for reading/writing to io like objects
|
6
|
+
module IO
|
7
|
+
class << self
|
8
|
+
# Helper to convert a ruby object to size bytes required by {FuseOperations#read}
|
9
|
+
# @param [#pread, #seek & #read, #to_s] io the source to read from, either...
|
10
|
+
#
|
11
|
+
# * an {::IO} like object via :pread(size, offset) or :seek(offset) then :read(size)
|
12
|
+
# * or the String from :to_s
|
13
|
+
#
|
14
|
+
# @param [Integer] size
|
15
|
+
# @param [Integer, nil] offset
|
16
|
+
# if nil the io is assumed to be already positioned
|
17
|
+
# @return [String] the extracted data
|
18
|
+
def read(io, size, offset = nil)
|
19
|
+
return io.pread(size, offset) if offset && io.respond_to?(:pread)
|
20
|
+
|
21
|
+
if (offset ? %i[seek read] : %i[read]).all? { |m| io.respond_to?(m) }
|
22
|
+
io.seek(offset) if offset
|
23
|
+
return io.read(size)
|
24
|
+
end
|
25
|
+
|
26
|
+
io.to_s[offset || 0, size] || ''
|
27
|
+
end
|
28
|
+
|
29
|
+
# Helper to write date to {::IO} or {::String} like objects for use with #{FuseOperations#write}
|
30
|
+
# @param [#pwrite, #seek & #write, #[]=] io an object that accepts String data via...
|
31
|
+
#
|
32
|
+
# * ```ruby io.pwrite(data, offset)```
|
33
|
+
# * ```ruby io.seek(offset) ; io.write(data)```
|
34
|
+
# * ```ruby io[offset, data.size] = data```
|
35
|
+
# @param [String] data
|
36
|
+
# @param [nil, Integer] offset
|
37
|
+
# if not nil start start writing at this position in io
|
38
|
+
# @return [Integer] number of bytes written
|
39
|
+
# @raise [Errno::EBADF] if io does not support the requisite methods
|
40
|
+
def write(io, data, offset = nil)
|
41
|
+
if offset && io.respond_to?(:pwrite)
|
42
|
+
io.pwrite(data, offset)
|
43
|
+
elsif (offset ? %i[seek write] : %i[write]).all? { |m| io.respond_to?(m) }
|
44
|
+
io.seek(offset) if offset
|
45
|
+
io.write(data)
|
46
|
+
elsif io.respond_to?(:[]=) # eg String
|
47
|
+
io[offset || 0, data.size] = data
|
48
|
+
data.size
|
49
|
+
else
|
50
|
+
raise "cannot :pwrite or :write to #{io}", Errno::EBADF
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
data/lib/ffi/libfuse/main.rb
CHANGED
@@ -24,15 +24,13 @@ module FFI
|
|
24
24
|
|
25
25
|
# Main function of FUSE
|
26
26
|
#
|
27
|
-
# This function:
|
28
|
-
#
|
29
27
|
# - parses command line options - see {fuse_parse_cmdline}
|
30
|
-
#
|
31
|
-
# -
|
32
|
-
# -
|
33
|
-
# -
|
34
|
-
# -
|
35
|
-
# - calls
|
28
|
+
# - calls {#fuse_configure} if implemented by operations
|
29
|
+
# - creates a fuse handle see {fuse_create}
|
30
|
+
# - returns 0 if help or version options were processed (ie after all messages have been printed by libfuse)
|
31
|
+
# - returns 2 if fuse handle is not successfully mounted
|
32
|
+
# - calls {#fuse_traps} if implemented by operations
|
33
|
+
# - calls run on the fuse handle with options from previous steps- see {FuseCommon#run}
|
36
34
|
#
|
37
35
|
# @param [Array<String>] argv mount.fuse arguments
|
38
36
|
# expects progname, mountpoint, options....
|
@@ -46,23 +44,23 @@ module FFI
|
|
46
44
|
# @return [Integer] suitable for process exit code
|
47
45
|
def fuse_main(*argv, operations:, args: argv.any? ? argv : default_args, private_data: nil)
|
48
46
|
run_args = fuse_parse_cmdline(args: args, handler: operations)
|
49
|
-
return 2 unless run_args
|
50
47
|
|
51
48
|
fuse_args = run_args.delete(:args)
|
52
49
|
mountpoint = run_args.delete(:mountpoint)
|
53
50
|
|
54
|
-
|
51
|
+
show_only = run_args[:show_help] || run_args[:show_version]
|
52
|
+
|
53
|
+
return 3 if !show_only && !fuse_configure(operations)
|
55
54
|
|
56
55
|
warn "FuseCreate: mountpoint: #{mountpoint}, args: [#{fuse_args.argv.join(' ')}]" if run_args[:debug]
|
57
56
|
warn "FuseRun: #{run_args}" if run_args[:debug]
|
58
57
|
|
59
58
|
fuse = fuse_create(mountpoint, args: fuse_args, operations: operations, private_data: private_data)
|
60
59
|
|
61
|
-
return 0 if
|
60
|
+
return 0 if show_only
|
62
61
|
return 2 if !fuse || !mountpoint
|
63
62
|
|
64
|
-
|
65
|
-
|
63
|
+
run_args[:traps] = operations.fuse_traps if operations.respond_to?(:fuse_traps)
|
66
64
|
fuse.run(**run_args)
|
67
65
|
end
|
68
66
|
alias main fuse_main
|
@@ -72,7 +70,6 @@ module FFI
|
|
72
70
|
# - parses standard command line options (-d -s -h -V)
|
73
71
|
# will call {fuse_debug}, {fuse_version}, {fuse_help} if implemented by handler
|
74
72
|
# - calls {fuse_options} for custom option processing if implemented by handler
|
75
|
-
# - records signal handlers if operations implements {fuse_traps}
|
76
73
|
# - parses standard fuse mount options
|
77
74
|
#
|
78
75
|
# @param [Array<String>] argv mount.fuse arguments
|
@@ -88,24 +85,25 @@ module FFI
|
|
88
85
|
# * show_version [Boolean]: -v or --version
|
89
86
|
# * debug [Boolean]: -d
|
90
87
|
# * others are options to pass to {FuseCommon#run}
|
88
|
+
# @return [nil] if args are not parsed successfully
|
91
89
|
def fuse_parse_cmdline(*argv, args: argv.any? ? argv : default_args, handler: nil)
|
92
90
|
args = fuse_init_args(args)
|
93
91
|
|
94
92
|
# Parse args and print cmdline help
|
95
93
|
run_args = Fuse.parse_cmdline(args, handler: handler)
|
96
|
-
return nil unless run_args
|
97
94
|
|
98
|
-
|
95
|
+
handler.fuse_options(args) if handler.respond_to?(:fuse_options)
|
99
96
|
|
100
|
-
run_args
|
101
|
-
|
102
|
-
return nil unless parse_run_options(args, run_args)
|
97
|
+
parse_run_options(args, run_args)
|
103
98
|
|
104
99
|
run_args[:args] = args
|
105
100
|
run_args
|
101
|
+
rescue Error
|
102
|
+
nil
|
106
103
|
end
|
107
104
|
|
108
|
-
# @return [FuseCommon
|
105
|
+
# @return [FuseCommon] the mounted filesystem handle
|
106
|
+
# @return [nil] if not mounted (eg due to --help or --version, or an error)
|
109
107
|
def fuse_create(mountpoint, *argv, operations:, args: nil, private_data: nil)
|
110
108
|
args = fuse_init_args(args || argv)
|
111
109
|
|
@@ -116,8 +114,8 @@ module FFI
|
|
116
114
|
end
|
117
115
|
|
118
116
|
# @!visibility private
|
119
|
-
def fuse_configure(operations
|
120
|
-
return true unless operations.respond_to?(:fuse_configure)
|
117
|
+
def fuse_configure(operations)
|
118
|
+
return true unless operations.respond_to?(:fuse_configure)
|
121
119
|
|
122
120
|
# Provide sensible values for FuseContext in case this is referenced during configure
|
123
121
|
FFI::Libfuse::FuseContext.overrides do
|
@@ -166,7 +164,8 @@ module FFI
|
|
166
164
|
# @abstract
|
167
165
|
# Called to allow filesystem to handle custom options and observe standard mount options #
|
168
166
|
# @param [FuseArgs] args
|
169
|
-
# @
|
167
|
+
# @raise [Error] if there is an error parsing the options
|
168
|
+
# @return [void]
|
170
169
|
# @see FuseArgs#parse!
|
171
170
|
# @example
|
172
171
|
# OPTIONS = { 'config=' => :config, '-c ' => :config }
|
@@ -174,7 +173,7 @@ module FFI
|
|
174
173
|
# args.parse!(OPTIONS) do |key:, value:, out:, **opts|
|
175
174
|
#
|
176
175
|
# # raise errors for invalid config
|
177
|
-
# raise FFI::Libfuse::
|
176
|
+
# raise FFI::Libfuse::Error, "Invalid config" unless valid_config?(key,value)
|
178
177
|
#
|
179
178
|
# # Configure the file system
|
180
179
|
# @config = value if key == :config
|
@@ -189,6 +188,8 @@ module FFI
|
|
189
188
|
|
190
189
|
# @!method fuse_traps
|
191
190
|
# @abstract
|
191
|
+
# Passed to {FuseCommon#run} to allow filesystem to handle custom signal traps. These traps
|
192
|
+
# are merged over those from {FuseCommon#default_traps}
|
192
193
|
# @return [Hash<String|Symbol|Integer,String|Proc>]
|
193
194
|
# map of signal name or number to signal handler as per Signal.trap
|
194
195
|
# @example
|
@@ -198,6 +199,7 @@ module FFI
|
|
198
199
|
|
199
200
|
# @!method fuse_version
|
200
201
|
# @abstract
|
202
|
+
# Called as part of generating output for the -V option
|
201
203
|
# @return [String] a custom version string to output with -V option
|
202
204
|
|
203
205
|
# @!method fuse_help
|
@@ -214,6 +216,7 @@ module FFI
|
|
214
216
|
# @!method fuse_configure
|
215
217
|
# @abstract
|
216
218
|
# Called immediately before the filesystem is mounted, after options have been parsed
|
219
|
+
# (eg to validate required options)
|
217
220
|
#
|
218
221
|
# @raise [Error] to prevent the mount from proceeding
|
219
222
|
# @return [void]
|
@@ -9,9 +9,6 @@ module FFI
|
|
9
9
|
module Libfuse
|
10
10
|
# Can be included test classes to assist with running/debugging filesystems
|
11
11
|
module TestHelper
|
12
|
-
# rubocop:disable Metrics/AbcSize
|
13
|
-
# rubocop:disable Metrics/MethodLength
|
14
|
-
|
15
12
|
# Runs the fuse loop on a pre configured fuse filesystem
|
16
13
|
# @param [FuseOperations] operations
|
17
14
|
# @param [Array<String>] args to pass to {FFI::Libfuse::Main.fuse_create}
|
@@ -19,7 +16,7 @@ module FFI
|
|
19
16
|
# @yield [mnt]
|
20
17
|
# caller can execute and test file operations using mnt and ruby File/Dir etc
|
21
18
|
# the block is run in a forked process and is successful unless an exception is raised
|
22
|
-
# @yieldparam [String] mnt the temporary
|
19
|
+
# @yieldparam [String] mnt the temporary directory used as the mount point
|
23
20
|
# @raise [Error] if unexpected state is found during operations
|
24
21
|
# @return [void]
|
25
22
|
def with_fuse(operations, *args, **options)
|
@@ -34,34 +31,14 @@ module FFI
|
|
34
31
|
yield mnt
|
35
32
|
end
|
36
33
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
# Rake owns INT
|
41
|
-
fuse.default_traps.delete(:TERM)
|
42
|
-
fuse.default_traps.delete(:INT)
|
43
|
-
|
44
|
-
raise FFI::Libfuse::Error, 'fuse object is not mounted?' unless fuse.mounted?
|
34
|
+
run_fuse(mnt, *args, operations: operations, **options) do
|
35
|
+
# TODO: Work out why waitpid2 hangs on mac unless the process has already finished
|
36
|
+
sleep 10 if mac_fuse?
|
45
37
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
sleep 10 if mac_fuse?
|
50
|
-
|
51
|
-
_pid, block_status = Process.waitpid2(fpid)
|
52
|
-
block_exit = block_status.exitstatus
|
53
|
-
fuse.exit('fuse_helper')&.join
|
54
|
-
run_result = t.value
|
55
|
-
|
56
|
-
raise FFI::Libfuse::Error, 'fuse is still mounted after fuse.exit' if fuse.mounted?
|
57
|
-
raise FFI::Libfuse::Error, "forked file operations failed with #{block_exit}" unless block_exit.zero?
|
58
|
-
raise FFI::Libfuse::Error, "fuse run failed #{run_result}" unless run_result.zero?
|
59
|
-
|
60
|
-
if !mac_fuse? && mounted?(mnt)
|
61
|
-
raise FFI::Libfuse::Error, "OS reports fuse is still mounted at #{mnt} after fuse.exit"
|
38
|
+
_pid, block_status = Process.waitpid2(fpid)
|
39
|
+
block_exit = block_status.exitstatus
|
40
|
+
raise FFI::Libfuse::Error, "forked file operations failed with #{block_exit}" unless block_exit.zero?
|
62
41
|
end
|
63
|
-
|
64
|
-
true
|
65
42
|
end
|
66
43
|
end
|
67
44
|
|
@@ -77,42 +54,28 @@ module FFI
|
|
77
54
|
# @note if the filesystem is configured to daemonize then no output will be captured
|
78
55
|
def run_filesystem(filesystem, *args, env: {})
|
79
56
|
fsname = File.basename(filesystem)
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
Bundler.with_unbundled_env do
|
84
|
-
Open3.capture3(env, 'bundle', 'exec', filesystem.to_s, mnt, "-ofsname=#{fsname}", *args, binmode: true)
|
85
|
-
end
|
86
|
-
else
|
87
|
-
Open3.capture3(env, filesystem.to_s, mnt, "-ofsname=#{fsname}", *args, binmode: true)
|
88
|
-
end
|
89
|
-
end
|
57
|
+
|
58
|
+
t, err = safe_fuse do |mnt|
|
59
|
+
t = Thread.new { open3_filesystem(args, env, filesystem, fsname, mnt) }
|
90
60
|
sleep 1
|
91
61
|
|
92
62
|
begin
|
93
|
-
if block_given?
|
94
|
-
raise Error, "#{fsname} not mounted at #{mnt}" unless mounted?(mnt, fsname)
|
95
|
-
|
96
|
-
yield mnt
|
97
|
-
end
|
98
|
-
# rubocop:disable Lint/RescueException
|
99
|
-
# Minitest::Assertion and other test assertion classes are not derived from StandardError
|
100
|
-
rescue Exception => _err
|
101
|
-
# rubocop:enable Lint/RescueException
|
102
|
-
unmount(mnt) if mounted?(mnt)
|
103
|
-
o, e, _s = t.value
|
104
|
-
warn "Errors\n#{e}" unless e.empty?
|
105
|
-
warn "Output\n#{o}" unless o.empty?
|
106
|
-
raise
|
107
|
-
end
|
63
|
+
raise Error, "#{fsname} not mounted at #{mnt}" if block_given? && !mounted?(mnt, fsname)
|
108
64
|
|
109
|
-
|
110
|
-
|
111
|
-
|
65
|
+
yield mnt if block_given?
|
66
|
+
[t, nil]
|
67
|
+
rescue Minitest::Assertion, StandardError => e
|
68
|
+
[t, e]
|
69
|
+
end
|
112
70
|
end
|
71
|
+
|
72
|
+
o, e, s = t.value
|
73
|
+
return [o, e, s.exitstatus] unless err
|
74
|
+
|
75
|
+
warn "Errors\n#{e}" unless e.empty?
|
76
|
+
warn "Output\n#{o}" unless o.empty? Minitest::Assertion, StandardError => e
|
77
|
+
raise err
|
113
78
|
end
|
114
|
-
# rubocop:enable Metrics/AbcSize
|
115
|
-
# rubocop:enable Metrics/MethodLength
|
116
79
|
|
117
80
|
def mounted?(mnt, _filesystem = '.*')
|
118
81
|
type, prefix = mac_fuse? ? %w[macfuse /private] : %w[fuse]
|
@@ -140,6 +103,51 @@ module FFI
|
|
140
103
|
def mac_fuse?
|
141
104
|
FFI::Platform::IS_MAC
|
142
105
|
end
|
106
|
+
|
107
|
+
private
|
108
|
+
|
109
|
+
def open3_filesystem(args, env, filesystem, fsname, mnt)
|
110
|
+
if defined?(Bundler)
|
111
|
+
Bundler.with_unbundled_env do
|
112
|
+
Open3.capture3(env, 'bundle', 'exec', filesystem.to_s, mnt, "-ofsname=#{fsname}", *args, binmode: true)
|
113
|
+
end
|
114
|
+
else
|
115
|
+
Open3.capture3(env, filesystem.to_s, mnt, "-ofsname=#{fsname}", *args, binmode: true)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
def run_fuse(mnt, *args, operations:, **options)
|
120
|
+
fuse = mount_fuse(mnt, *args, operations: operations)
|
121
|
+
begin
|
122
|
+
t = Thread.new do
|
123
|
+
Thread.current.name = 'Fuse Run'
|
124
|
+
# Rake owns INT
|
125
|
+
fuse.run(foreground: true, traps: { INT: nil, TERM: nil }, **options)
|
126
|
+
end
|
127
|
+
|
128
|
+
yield
|
129
|
+
ensure
|
130
|
+
fuse.exit('fuse_helper')&.join
|
131
|
+
run_result = t.value
|
132
|
+
|
133
|
+
raise FFI::Libfuse::Error, 'fuse is still mounted after fuse.exit' if fuse.mounted?
|
134
|
+
raise FFI::Libfuse::Error, "fuse run failed #{run_result}" unless run_result.zero?
|
135
|
+
|
136
|
+
if !mac_fuse? && mounted?(mnt)
|
137
|
+
raise FFI::Libfuse::Error, "OS reports fuse is still mounted at #{mnt} after fuse.exit"
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
def mount_fuse(mnt, *args, operations:)
|
143
|
+
operations.fuse_debug(args.include?('-d')) if operations.respond_to?(:fuse_debug)
|
144
|
+
|
145
|
+
fuse = FFI::Libfuse::Main.fuse_create(mnt, *args, operations: operations)
|
146
|
+
raise FFI::Libfuse::Error, 'No fuse object returned from fuse_create' unless fuse
|
147
|
+
raise FFI::Libfuse::Error, 'fuse object is not mounted?' unless fuse.mounted?
|
148
|
+
|
149
|
+
fuse
|
150
|
+
end
|
143
151
|
end
|
144
152
|
end
|
145
153
|
end
|
data/lib/ffi/libfuse/version.rb
CHANGED
data/lib/ffi/libfuse.rb
CHANGED
@@ -11,7 +11,7 @@ require_relative 'devt'
|
|
11
11
|
module FFI
|
12
12
|
# Ruby FFI Binding for [libfuse](https://github.com/libfuse/libfuse)
|
13
13
|
module Libfuse
|
14
|
-
# Filesystems can raise this error to indicate
|
14
|
+
# Filesystems can raise this error to indicate misconfiguration issues etc...
|
15
15
|
class Error < StandardError; end
|
16
16
|
|
17
17
|
# Opinionated default args for {.main}.
|
data/lib/ffi/stat/native.rb
CHANGED
@@ -27,14 +27,14 @@ module FFI
|
|
27
27
|
:unused, [:long, 3]
|
28
28
|
|
29
29
|
[['', :string], ['l', :string], ['f', :int]].each do |(prefix, ftype)|
|
30
|
-
native_func = "native_#{prefix}stat"
|
31
|
-
lib_func = "#{prefix}stat"
|
30
|
+
native_func = :"native_#{prefix}stat"
|
31
|
+
lib_func = :"#{prefix}stat"
|
32
32
|
begin
|
33
33
|
::FFI::Stat.attach_function native_func, lib_func, [ftype, by_ref], :int
|
34
34
|
rescue FFI::NotFoundError
|
35
35
|
# gLibc 2.31 (Ubuntu focal) does not export these functions, it maps them to __xstat variants
|
36
|
-
native_xfunc = "native_#{prefix}xstat"
|
37
|
-
lib_xfunc = "__#{prefix}xstat"
|
36
|
+
native_xfunc = :"native_#{prefix}xstat"
|
37
|
+
lib_xfunc = :"__#{prefix}xstat"
|
38
38
|
::FFI::Stat.attach_function native_xfunc, lib_xfunc, [:int, ftype, by_ref], :int
|
39
39
|
# 1 is 64 bit versions of struct stat, 3 is 32 bit
|
40
40
|
::FFI::Stat.define_singleton_method(native_func) { |file, buf| send(native_xfunc, 1, file, buf) }
|
data/lib/ffi/stat.rb
CHANGED
@@ -62,9 +62,9 @@ module FFI
|
|
62
62
|
# @param [Integer] gid
|
63
63
|
# @param [Hash] args additional system specific stat fields
|
64
64
|
# @return [self]
|
65
|
-
def file(mode:, size:, uid: Process.uid, gid: Process.gid, **args)
|
65
|
+
def file(mode:, size:, nlink: 1, uid: Process.uid, gid: Process.gid, **args)
|
66
66
|
mode = ((S_IFREG & S_IFMT) | (mode & 0o777))
|
67
|
-
fill(mode: mode, size: size, uid: uid, gid: gid, **args)
|
67
|
+
fill(mode: mode, size: size, nlink: nlink, uid: uid, gid: gid, **args)
|
68
68
|
end
|
69
69
|
|
70
70
|
# Fill content for a directory
|
@@ -80,6 +80,18 @@ module FFI
|
|
80
80
|
end
|
81
81
|
alias directory dir
|
82
82
|
|
83
|
+
# Fill content for a symbolic link
|
84
|
+
# @param [Integer] size length of the target name (including null terminator)
|
85
|
+
# @param [Integer] mode
|
86
|
+
# @param [Integer] uid
|
87
|
+
# @param [Integer] gid
|
88
|
+
# @param [Hash] args additional system specific stat fields
|
89
|
+
# @return [self]
|
90
|
+
def symlink(size:, mode: 0o777, nlink: 1, uid: Process.uid, gid: Process.gid, **args)
|
91
|
+
mode = ((S_IFLNK & S_IFMT) | (mode & 0o777))
|
92
|
+
fill(mode: mode, nlink: nlink, size: size, uid: uid, gid: gid, **args)
|
93
|
+
end
|
94
|
+
|
83
95
|
# Fill attributes from file (using native LIBC calls)
|
84
96
|
# @param [Integer|:to_s] file descriptor or a file path
|
85
97
|
# @param [Boolean] follow links
|
@@ -123,7 +135,7 @@ module FFI
|
|
123
135
|
# @param [Integer] mask (see umask)
|
124
136
|
# @param [Hash] overrides see {fill}
|
125
137
|
# @return self
|
126
|
-
def mask(mask =
|
138
|
+
def mask(mask = S_ISUID, **overrides)
|
127
139
|
fill(mode: mode & (~mask), **overrides)
|
128
140
|
end
|
129
141
|
|
@@ -147,6 +159,10 @@ module FFI
|
|
147
159
|
mode & S_ISVTX != 0
|
148
160
|
end
|
149
161
|
|
162
|
+
def symlink?
|
163
|
+
mode & S_IFLNK != 0
|
164
|
+
end
|
165
|
+
|
150
166
|
class << self
|
151
167
|
# @!method file(stat,**fields)
|
152
168
|
# @return [Stat]
|
data/lib/ffi/struct_array.rb
CHANGED
@@ -10,6 +10,7 @@ module FFI
|
|
10
10
|
def array(size)
|
11
11
|
ArrayConverter.new(self, size)
|
12
12
|
end
|
13
|
+
alias [] array
|
13
14
|
|
14
15
|
# @!visibility private
|
15
16
|
# Helper to handle callbacks containing fixed length array of struct
|
@@ -28,7 +29,7 @@ module FFI
|
|
28
29
|
def from_native(ptr, _ctx)
|
29
30
|
return [] if ptr.null?
|
30
31
|
|
31
|
-
Array.new(@size) { |i| @type.new(ptr + (i * @type.size)) }
|
32
|
+
Array.new(@size) { |i| @type.new(ptr + (i * @type.size)) }.freeze
|
32
33
|
end
|
33
34
|
|
34
35
|
def to_native(ary, _ctx)
|
data/sample/hello_fs.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ffi-libfuse
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Grant Gardner
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2024-01-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: ffi
|
@@ -161,11 +161,13 @@ files:
|
|
161
161
|
- lib/ffi/libfuse/filesystem/virtual_dir.rb
|
162
162
|
- lib/ffi/libfuse/filesystem/virtual_file.rb
|
163
163
|
- lib/ffi/libfuse/filesystem/virtual_fs.rb
|
164
|
+
- lib/ffi/libfuse/filesystem/virtual_link.rb
|
164
165
|
- lib/ffi/libfuse/filesystem/virtual_node.rb
|
165
166
|
- lib/ffi/libfuse/fuse2.rb
|
166
167
|
- lib/ffi/libfuse/fuse3.rb
|
167
168
|
- lib/ffi/libfuse/fuse_args.rb
|
168
|
-
- lib/ffi/libfuse/
|
169
|
+
- lib/ffi/libfuse/fuse_buf.rb
|
170
|
+
- lib/ffi/libfuse/fuse_buf_vec.rb
|
169
171
|
- lib/ffi/libfuse/fuse_callbacks.rb
|
170
172
|
- lib/ffi/libfuse/fuse_cmdline_opts.rb
|
171
173
|
- lib/ffi/libfuse/fuse_common.rb
|
@@ -180,6 +182,7 @@ files:
|
|
180
182
|
- lib/ffi/libfuse/fuse_version.rb
|
181
183
|
- lib/ffi/libfuse/gem_helper.rb
|
182
184
|
- lib/ffi/libfuse/gem_version.rb
|
185
|
+
- lib/ffi/libfuse/io.rb
|
183
186
|
- lib/ffi/libfuse/job_pool.rb
|
184
187
|
- lib/ffi/libfuse/main.rb
|
185
188
|
- lib/ffi/libfuse/test_helper.rb
|