ffi-libfuse 0.3.4 → 0.4.0
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.
- 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
|