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
@@ -6,88 +6,94 @@ require 'stringio'
|
|
6
6
|
module FFI
|
7
7
|
module Libfuse
|
8
8
|
module Filesystem
|
9
|
+
module Ruby
|
10
|
+
# Filesystem methods representing a single synthetic file at the root and satisfying
|
11
|
+
# Satisfies the contract of {Adapter::Ruby}
|
12
|
+
module VirtualFile
|
13
|
+
include VirtualNode
|
14
|
+
|
15
|
+
# @return [String] the (binary) content of the synthetic file
|
16
|
+
attr_reader :content
|
17
|
+
|
18
|
+
# @return [Integer] the number of links to this file
|
19
|
+
attr_reader :nlink
|
20
|
+
|
21
|
+
# Create an empty synthetic file
|
22
|
+
def initialize(accounting: nil)
|
23
|
+
super(accounting: accounting)
|
24
|
+
end
|
25
|
+
|
26
|
+
# @!group FUSE Callbacks
|
27
|
+
|
28
|
+
def getattr(path, stat = nil, ffi = nil)
|
29
|
+
# We don't exist until create or otherwise or virtual stat exists
|
30
|
+
raise Errno::ENOENT unless root?(path) && virtual_stat
|
31
|
+
|
32
|
+
stat&.file(size: (ffi&.fh || content).size, nlink: nlink, **virtual_stat)
|
33
|
+
self
|
34
|
+
end
|
35
|
+
|
36
|
+
# @param [String] _path ignored, expected to be '/'
|
37
|
+
# @param [Integer] mode
|
38
|
+
# @param [FuseFileInfo] ffi
|
39
|
+
# @return [Object] a file handled (captured by {Adapter::Ruby::Prepend})
|
40
|
+
def create(_path, mode, ffi = nil)
|
41
|
+
init_node(mode)
|
42
|
+
@content = String.new(encoding: 'binary')
|
43
|
+
@nlink = 1
|
44
|
+
sio(ffi) if ffi
|
45
|
+
end
|
46
|
+
|
47
|
+
def open(_path, ffi)
|
48
|
+
virtual_stat[:atime] = Time.now.utc
|
49
|
+
sio(ffi)
|
50
|
+
end
|
51
|
+
|
52
|
+
# write(const char* path, char *buf, size_t size, off_t offset, struct fuse_file_info* fi)
|
53
|
+
def write(path, data, offset = 0, _ffi = nil)
|
54
|
+
raise Errno::ENOENT unless root?(path)
|
55
|
+
|
56
|
+
accounting&.write(content.size, data.size, offset)
|
57
|
+
virtual_stat[:mtime] = Time.now.utc
|
58
|
+
nil # just let the sio in ffi handle it
|
59
|
+
end
|
60
|
+
|
61
|
+
def truncate(path, size, ffi = nil)
|
62
|
+
raise Errno::ENOENT unless root?(path)
|
63
|
+
|
64
|
+
accounting&.truncate(content.size, size)
|
65
|
+
sio(ffi).truncate(size)
|
66
|
+
virtual_stat[:mtime] = Time.now.utc
|
67
|
+
end
|
68
|
+
|
69
|
+
def link(_target, path)
|
70
|
+
raise Errno::ENOENT unless root?(path)
|
71
|
+
|
72
|
+
accounting&.adjust(content.size, 1) if @nlink.zero?
|
73
|
+
@nlink += 1
|
74
|
+
self
|
75
|
+
end
|
76
|
+
|
77
|
+
def unlink(path)
|
78
|
+
raise Errno::ENOENT unless root?(path)
|
79
|
+
|
80
|
+
@nlink -= 1
|
81
|
+
accounting&.adjust(-content.size, -1) if @nlink.zero?
|
82
|
+
end
|
83
|
+
|
84
|
+
private
|
85
|
+
|
86
|
+
def sio(ffi)
|
87
|
+
ffi&.fh || StringIO.new(content, ffi&.flags)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
9
92
|
# A Filesystem representing a single synthetic file at the root
|
10
|
-
class VirtualFile
|
93
|
+
class VirtualFile
|
11
94
|
prepend Adapter::Ruby::Prepend
|
12
95
|
include Fuse2Compat
|
13
|
-
|
14
|
-
# @return [String] the (binary) content of the synthetic file
|
15
|
-
attr_reader :content
|
16
|
-
|
17
|
-
# Create an empty synthetic file
|
18
|
-
def initialize(accounting: nil)
|
19
|
-
super(accounting: accounting)
|
20
|
-
end
|
21
|
-
|
22
|
-
# @!visibility private
|
23
|
-
def path_method(_method, *_args)
|
24
|
-
raise Errno::ENOENT
|
25
|
-
end
|
26
|
-
|
27
|
-
# @!group FUSE Callbacks
|
28
|
-
|
29
|
-
def getattr(path, stat, ffi = nil)
|
30
|
-
# We don't exist until create or otherwise or virtual stat exists
|
31
|
-
raise Errno::ENOENT unless root?(path) && virtual_stat
|
32
|
-
|
33
|
-
stat.file(size: (ffi&.fh || content).size, **virtual_stat)
|
34
|
-
self
|
35
|
-
end
|
36
|
-
|
37
|
-
# @param [String] _path ignored, expected to be '/'
|
38
|
-
# @param [Integer] mode
|
39
|
-
# @param [FuseFileInfo] ffi
|
40
|
-
# @return [Object] a file handled (captured by {Adapter::Ruby::Prepend})
|
41
|
-
def create(_path, mode, ffi = nil)
|
42
|
-
init_node(mode)
|
43
|
-
@content = String.new(encoding: 'binary')
|
44
|
-
sio(ffi) if ffi
|
45
|
-
end
|
46
|
-
|
47
|
-
def open(_path, ffi)
|
48
|
-
virtual_stat[:atime] = Time.now.utc
|
49
|
-
sio(ffi)
|
50
|
-
end
|
51
|
-
|
52
|
-
# op[:read] = [:pointer, :size_t, :off_t, FuseFileInfo.by_ref]
|
53
|
-
def read(path, size, off, ffi)
|
54
|
-
raise Errno::ENOENT unless root?(path)
|
55
|
-
|
56
|
-
io = sio(ffi)
|
57
|
-
io.seek(off)
|
58
|
-
io.read(size)
|
59
|
-
end
|
60
|
-
|
61
|
-
# write(const char* path, char *buf, size_t size, off_t offset, struct fuse_file_info* fi)
|
62
|
-
def write(path, data, offset = 0, ffi = nil)
|
63
|
-
raise Errno::ENOENT unless root?(path)
|
64
|
-
|
65
|
-
accounting&.write(content.size, data.size, offset)
|
66
|
-
io = sio(ffi)
|
67
|
-
io.seek(offset)
|
68
|
-
io.write(data)
|
69
|
-
virtual_stat[:mtime] = Time.now.utc
|
70
|
-
end
|
71
|
-
|
72
|
-
def truncate(path, size, ffi = nil)
|
73
|
-
raise Errno::ENOENT unless root?(path)
|
74
|
-
|
75
|
-
accounting&.truncate(content.size, size)
|
76
|
-
sio(ffi).truncate(size)
|
77
|
-
virtual_stat[:mtime] = Time.now.utc
|
78
|
-
end
|
79
|
-
|
80
|
-
def unlink(path)
|
81
|
-
raise Errno::ENOENT unless root?(path)
|
82
|
-
|
83
|
-
accounting&.adjust(-content.size, -1)
|
84
|
-
end
|
85
|
-
|
86
|
-
private
|
87
|
-
|
88
|
-
def sio(ffi)
|
89
|
-
ffi&.fh || StringIO.new(content, ffi&.flags)
|
90
|
-
end
|
96
|
+
include Ruby::VirtualFile
|
91
97
|
end
|
92
98
|
end
|
93
99
|
end
|
@@ -15,6 +15,8 @@ module FFI
|
|
15
15
|
# Delegate filesystems like {VirtualDir} may raise ENOTSUP to indicate a callback is not handled at runtime
|
16
16
|
# although the behaviour of C libfuse varies in this regard.
|
17
17
|
#
|
18
|
+
# It is writable to the user that mounted it may create and edit files within it
|
19
|
+
#
|
18
20
|
# Filesystem options
|
19
21
|
# ===
|
20
22
|
#
|
@@ -33,15 +35,14 @@ module FFI
|
|
33
35
|
# Note that {VirtualFile} and {MappedFiles} both prepend {Adapter::Ruby::Prepend} which implements
|
34
36
|
# the logic to fallback from :read/:write_buf to plain :read/:write as necessary to support this option.
|
35
37
|
#
|
36
|
-
# It is writable to the user that mounted it may create and edit files within it
|
37
|
-
#
|
38
38
|
# @example
|
39
39
|
# class MyFS < FFI::Libfuse::Filesystem::VirtualFS
|
40
40
|
# def fuse_configure
|
41
41
|
# build({ 'hello' => { 'world.txt' => 'Hello World'}})
|
42
42
|
# mkdir("/hello")
|
43
|
-
# create("/hello/world").write("Hello World!\n")
|
43
|
+
# create("/hello/world.txt").write("Hello World!\n")
|
44
44
|
# create("/hello/everybody").write("Hello Everyone!\n")
|
45
|
+
# symlink("/hello/link","everybody")`
|
45
46
|
# end
|
46
47
|
# end
|
47
48
|
#
|
@@ -51,7 +52,7 @@ module FFI
|
|
51
52
|
include Utils
|
52
53
|
include Adapter::Context
|
53
54
|
include Adapter::Debug
|
54
|
-
include Adapter::
|
55
|
+
include Adapter::Fuse2Compat
|
55
56
|
|
56
57
|
# @return [Object] the root filesystem that quacks like a {FuseOperations}
|
57
58
|
attr_reader :root
|
@@ -73,12 +74,13 @@ module FFI
|
|
73
74
|
|
74
75
|
# @overload build(files)
|
75
76
|
# Adds files directly to the filesystem
|
76
|
-
# @param [Hash] files map of paths to content
|
77
|
+
# @param [Hash<String,:each_pair, :readdir,:getattr .:to_str] files map of paths to content generated
|
78
|
+
# according to the content implementing one of the methods below
|
77
79
|
#
|
78
80
|
# * :each_pair is treated as a subdir of files
|
79
81
|
# * :readdir (eg {PassThroughDir}) is treated as a directory- sent via mkdir
|
80
82
|
# * :getattr (eg {PassThroughFile}) is treated as a file - sent via create
|
81
|
-
# * :to_str (eg {::String} ) is created
|
83
|
+
# * :to_str (eg {::String} ) is created a default file, with the string sent via write
|
82
84
|
def build(files, base_path = Pathname.new('/'))
|
83
85
|
files.each_pair do |path, content|
|
84
86
|
path = (base_path + path).cleanpath
|
@@ -103,10 +105,6 @@ module FFI
|
|
103
105
|
# * :copy_file_range can raise ENOTSUP to trigger glibc to fallback to inefficient copy
|
104
106
|
def fuse_respond_to?(method)
|
105
107
|
case method
|
106
|
-
when :getdir, :fgetattr
|
107
|
-
# TODO: Find out if fgetattr works on linux, something wrong with stat values on OSX.
|
108
|
-
# https://github.com/osxfuse/osxfuse/issues/887
|
109
|
-
false
|
110
108
|
when :read_buf, :write_buf
|
111
109
|
!no_buf
|
112
110
|
else
|
@@ -117,16 +115,21 @@ module FFI
|
|
117
115
|
# Default fuse options
|
118
116
|
# Subclasses can override this method and call super with the additional options:
|
119
117
|
# @param [Hash] opts additional options to parse into the {#options} attribute
|
120
|
-
|
118
|
+
# @yield(key, value, **args)
|
119
|
+
# Called for each matching key in opts
|
120
|
+
# @see FuseArgs#parse!
|
121
|
+
def fuse_options(args, opts = {}, &block)
|
121
122
|
@options = {}
|
122
123
|
opts = opts.merge({ 'no_buf' => :no_buf }).merge(Accounting::OPTIONS)
|
123
|
-
args.parse!(opts) do |key:, value:,
|
124
|
+
args.parse!(opts) do |key:, value:, **kwargs|
|
124
125
|
case key
|
125
126
|
when *Accounting::OPTIONS.values.uniq
|
126
127
|
next accounting.fuse_opt_proc(key: key, value: value)
|
127
128
|
when :no_buf
|
128
129
|
@no_buf = true
|
129
130
|
else
|
131
|
+
next block.call(key, value, **kwargs) if block
|
132
|
+
|
130
133
|
options[key] = value
|
131
134
|
end
|
132
135
|
:handled
|
@@ -147,6 +150,21 @@ module FFI
|
|
147
150
|
self.class.name
|
148
151
|
end
|
149
152
|
|
153
|
+
# @!visibility private
|
154
|
+
def init_fuse_config(fuse_config, _fuse_version)
|
155
|
+
fuse_config.use_ino = use_ino
|
156
|
+
end
|
157
|
+
|
158
|
+
# Configure whether entries in this filesystem provide useful inode values in #gettattr and #readdir
|
159
|
+
#
|
160
|
+
# Defaults to true since default Dir, File, Link all use {VirtualNode} which uses
|
161
|
+
# Ruby object id as the inode value.
|
162
|
+
#
|
163
|
+
# Subclasses should override to false if some sub-filesystems will not provide inode values.
|
164
|
+
# @return [Boolean]
|
165
|
+
def use_ino
|
166
|
+
true
|
167
|
+
end
|
150
168
|
# @!endgroup
|
151
169
|
|
152
170
|
private
|
@@ -156,15 +174,16 @@ module FFI
|
|
156
174
|
end
|
157
175
|
|
158
176
|
def build_readdir(content, path)
|
159
|
-
@root.mkdir(path.to_s) { content }
|
177
|
+
@root.mkdir(path.to_s) { |_| content }
|
160
178
|
end
|
161
179
|
|
162
180
|
def build_getattr(content, path)
|
163
|
-
@root.create(path.to_s) { content }
|
181
|
+
@root.create(path.to_s) { |_| content }
|
164
182
|
end
|
165
183
|
|
166
184
|
def build_to_str(content, path)
|
167
|
-
@root.create(path.to_s)
|
185
|
+
vf = @root.create(path.to_s)
|
186
|
+
vf.write(content.to_str)
|
168
187
|
end
|
169
188
|
|
170
189
|
# Passes FUSE Callbacks on to the {#root} filesystem
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module FFI
|
4
|
+
module Libfuse
|
5
|
+
module Filesystem
|
6
|
+
module Ruby
|
7
|
+
# Filesystem methods representing a symbolic link
|
8
|
+
# Satisfies the contract of {Adapter::Ruby}
|
9
|
+
module VirtualLink
|
10
|
+
attr_accessor :target
|
11
|
+
|
12
|
+
include VirtualNode
|
13
|
+
def initialize(accounting: nil)
|
14
|
+
@target = target
|
15
|
+
super(accounting: accounting)
|
16
|
+
end
|
17
|
+
|
18
|
+
def readlink(_path, size)
|
19
|
+
@target[0, size - 1] # buffer size needs null terminator
|
20
|
+
end
|
21
|
+
|
22
|
+
def symlink(from_path, path)
|
23
|
+
raise Errno::ENOENT unless root?(path)
|
24
|
+
|
25
|
+
@target = from_path
|
26
|
+
init_node(0o777)
|
27
|
+
end
|
28
|
+
|
29
|
+
def link(_from_path, path)
|
30
|
+
raise Errno::ENOENT unless root?(path)
|
31
|
+
|
32
|
+
# Cannot hard link a symbolic link
|
33
|
+
raise Errno::EPERM
|
34
|
+
end
|
35
|
+
|
36
|
+
def unlink(path)
|
37
|
+
raise Errno::ENOENT unless root?(path)
|
38
|
+
|
39
|
+
accounting&.adjust(0, -1)
|
40
|
+
end
|
41
|
+
|
42
|
+
def getattr(path, stat = nil, _ffi = nil)
|
43
|
+
# We don't exist until create or otherwise or virtual stat exists
|
44
|
+
raise Errno::ENOENT unless root?(path) && virtual_stat
|
45
|
+
|
46
|
+
stat&.symlink(size: @target.length + 1, **virtual_stat)
|
47
|
+
self
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# A Filesystem that represents a single symbolic link at the root
|
53
|
+
class VirtualLink
|
54
|
+
prepend Adapter::Ruby::Prepend
|
55
|
+
include Fuse2Compat
|
56
|
+
include Ruby::VirtualLink
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -6,95 +6,112 @@ require_relative '../adapter/ruby'
|
|
6
6
|
module FFI
|
7
7
|
module Libfuse
|
8
8
|
module Filesystem
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
9
|
+
module Ruby
|
10
|
+
# Common FUSE Callbacks for a virtual inode representing a single filesystem object at '/'
|
11
|
+
#
|
12
|
+
# **Note** this module is used by both {VirtualFile} which is under {Adapter::Ruby::Prepend}
|
13
|
+
# and {VirtualDir} which passes on native {FuseOperations} calls
|
14
|
+
module VirtualNode
|
15
|
+
# @return [Hash<Symbol,Integer>] base file or directory stat information used for :getattr of this node
|
16
|
+
attr_reader :virtual_stat
|
17
|
+
|
18
|
+
# @return [Hash<String,String>] virtual extended attributes
|
19
|
+
attr_reader :virtual_xattr
|
20
|
+
|
21
|
+
# @return [Accounting|nil] file system statistcs accumulator
|
22
|
+
attr_reader :accounting
|
23
|
+
|
24
|
+
# @param [Accounting] accounting accumulator of filesystem statistics
|
25
|
+
def initialize(accounting: Accounting.new)
|
26
|
+
@accounting = accounting
|
27
|
+
|
28
|
+
@virtual_xattr = {}
|
29
|
+
end
|
30
|
+
|
31
|
+
# @!method path_method(callback, *args)
|
32
|
+
# @abstract
|
33
|
+
# called if this node cannot handle the callback (ie path is not root or an entry in this directory)
|
34
|
+
|
35
|
+
# @!group FUSE Callbacks
|
36
|
+
|
37
|
+
def utimens(path, *args)
|
38
|
+
return path_method(__method__, path, *args) unless root?(path)
|
39
|
+
|
40
|
+
atime, mtime, *_fuse3 = args
|
41
|
+
# if native fuse call atime will be Array<Stat::TimeSpec>
|
42
|
+
atime, mtime = Stat::TimeSpec.fill_times(atime[0, 2], 2).map(&:time) if atime.is_a?(Array)
|
43
|
+
virtual_stat[:atime] = atime if atime
|
44
|
+
virtual_stat[:mtime] = mtime if mtime
|
45
|
+
virtual_stat[:ctime] = mtime if mtime
|
46
|
+
end
|
47
|
+
|
48
|
+
def chmod(path, mode, *args)
|
49
|
+
return path_method(__method__, path, mode, *args) unless root?(path)
|
50
|
+
|
51
|
+
virtual_stat[:mode] = mode
|
52
|
+
virtual_stat[:ctime] = Time.now
|
53
|
+
end
|
54
|
+
|
55
|
+
def chown(path, uid, gid, *args)
|
56
|
+
return path_method(__method__, path, uid, gid, *args) unless root?(path)
|
57
|
+
|
58
|
+
virtual_stat[:uid] = uid
|
59
|
+
virtual_stat[:gid] = gid
|
60
|
+
virtual_stat[:ctime] = Time.now
|
61
|
+
end
|
62
|
+
|
63
|
+
def statfs(path, statfs_buf)
|
64
|
+
return path_method(__method__, path, statfs_buf) unless root?(path)
|
65
|
+
raise Errno::ENOTSUP unless accounting
|
66
|
+
|
67
|
+
accounting.to_statvfs(statfs_buf)
|
68
|
+
end
|
69
|
+
|
70
|
+
def getxattr(path, name, buf = nil, size = nil)
|
71
|
+
return path_method(__method__, path, name, buf, size) unless root?(path)
|
72
|
+
return virtual_xattr[name] unless buf
|
73
|
+
|
74
|
+
Adapter::Ruby.getxattr(buf, size) { virtual_xattr[name] }
|
75
|
+
end
|
76
|
+
|
77
|
+
def listxattr(path, buf = nil, size = nil)
|
78
|
+
return path_method(__method__, path) unless root?(path)
|
79
|
+
return virtual_xattr.keys unless buf
|
80
|
+
|
81
|
+
Adapter::Ruby.listxattr(buf, size) { virtual_xattr.keys }
|
82
|
+
end
|
83
|
+
|
84
|
+
# @!endgroup
|
85
|
+
|
86
|
+
# Initialise the stat information for the node - should only be called once (eg from create or mkdir)
|
87
|
+
def init_node(mode, ctx: FuseContext.get, now: Time.now)
|
88
|
+
@virtual_stat =
|
89
|
+
{
|
90
|
+
mode: mode & ~ctx.umask, uid: ctx.uid, gid: ctx.gid,
|
91
|
+
ctime: now, mtime: now, atime: now,
|
92
|
+
ino: object_id
|
93
|
+
}
|
94
|
+
accounting&.adjust(0, +1)
|
95
|
+
self
|
96
|
+
end
|
97
|
+
|
98
|
+
private
|
99
|
+
|
100
|
+
# @!visibility private
|
101
|
+
def path_method(_method, *_args)
|
102
|
+
raise Errno::ENOENT
|
103
|
+
end
|
104
|
+
|
105
|
+
def root?(path)
|
106
|
+
path.to_s == '/'
|
107
|
+
end
|
61
108
|
end
|
109
|
+
end
|
62
110
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
accounting.to_statvfs(statfs_buf)
|
68
|
-
end
|
69
|
-
|
70
|
-
def getxattr(path, name, buf = nil, size = nil)
|
71
|
-
return path_method(__method__, path, name, buf, size) unless root?(path)
|
72
|
-
return virtual_xattr[name] unless buf
|
73
|
-
|
74
|
-
Adapter::Ruby.getxattr(buf, size) { virtual_xattr[name] }
|
75
|
-
end
|
76
|
-
|
77
|
-
def listxattr(path, buf = nil, size = nil)
|
78
|
-
return path_method(__method__, path) unless root?(path)
|
79
|
-
return virtual_xattr.keys unless buf
|
80
|
-
|
81
|
-
Adapter::Ruby.listxattr(buf, size) { virtual_xattr.keys }
|
82
|
-
end
|
83
|
-
|
84
|
-
# @!endgroup
|
85
|
-
|
86
|
-
# Initialise the stat information for the node - should only be called once (eg from create or mkdir)
|
87
|
-
def init_node(mode, ctx: FuseContext.get, now: Time.now)
|
88
|
-
@virtual_stat = { mode: mode & ~ctx.umask, uid: ctx.uid, gid: ctx.gid, ctime: now, mtime: now, atime: now }
|
89
|
-
accounting&.adjust(0, +1)
|
90
|
-
self
|
91
|
-
end
|
92
|
-
|
93
|
-
private
|
94
|
-
|
95
|
-
def root?(path)
|
96
|
-
path.to_s == '/'
|
97
|
-
end
|
111
|
+
# @abstract
|
112
|
+
# Base class Represents a virtual inode
|
113
|
+
class VirtualNode
|
114
|
+
include Ruby::VirtualNode
|
98
115
|
end
|
99
116
|
end
|
100
117
|
end
|
@@ -7,7 +7,7 @@ module FFI
|
|
7
7
|
# This module namespace contains classes and modules to assist with building and composing filesystems
|
8
8
|
#
|
9
9
|
# ### Virtual Filesystems
|
10
|
-
# Classes to help compose in-memory filesystems {VirtualFS}, {VirtualDir}, {VirtualFile}
|
10
|
+
# Classes to help compose in-memory filesystems {VirtualFS}, {VirtualDir}, {VirtualFile}, {VirtualLink}
|
11
11
|
#
|
12
12
|
# ### Mapped Filesystem
|
13
13
|
# Modules to map paths in the fuse filesystem to either real files or other filesystem objects
|
data/lib/ffi/libfuse/fuse2.rb
CHANGED
@@ -44,14 +44,15 @@ module FFI
|
|
44
44
|
def parse_cmdline(args, handler: nil)
|
45
45
|
# This also handles -h to print help information on stderr
|
46
46
|
# Parse mountpoint, -f , -s from args
|
47
|
-
# @return [Array<(String,Boolean,Boolean)
|
47
|
+
# @return [Array<(String,Boolean,Boolean)>]
|
48
48
|
# mountpoint, multi_thread, foreground options from args if available
|
49
49
|
# nil if no mountpoint, or options is requesting help or version information
|
50
50
|
mountpoint_ptr = FFI::MemoryPointer.new(:pointer, 1)
|
51
51
|
multi_thread_ptr = FFI::MemoryPointer.new(:int, 1)
|
52
52
|
foreground_ptr = FFI::MemoryPointer.new(:int, 1)
|
53
53
|
|
54
|
-
|
54
|
+
res = Libfuse.fuse_parse_cmdline2(args, mountpoint_ptr, multi_thread_ptr, foreground_ptr)
|
55
|
+
raise Error unless res.zero?
|
55
56
|
|
56
57
|
# noinspection RubyResolve
|
57
58
|
mp_data_ptr = mountpoint_ptr.get_pointer(0)
|
data/lib/ffi/libfuse/fuse3.rb
CHANGED
@@ -56,7 +56,7 @@ module FFI
|
|
56
56
|
class << self
|
57
57
|
def parse_cmdline(args, handler: nil)
|
58
58
|
cmdline_opts = FuseCmdlineOpts.new
|
59
|
-
|
59
|
+
raise Error unless Libfuse.fuse_parse_cmdline3(args, cmdline_opts).zero?
|
60
60
|
|
61
61
|
handler&.fuse_debug(cmdline_opts.debug) if handler.respond_to?(:fuse_debug)
|
62
62
|
|
@@ -121,7 +121,8 @@ module FFI
|
|
121
121
|
# - :error an error, alternatively raise {Error}
|
122
122
|
# - :keep retain the current argument for further processing
|
123
123
|
# - :handled,:discard remove the current argument from further processing
|
124
|
-
# @
|
124
|
+
# @raise Error if an error is raised during parsing
|
125
|
+
# @return [self]
|
125
126
|
def parse!(opts, data = nil, ignore: %i[non_option unmatched], &block)
|
126
127
|
ignore ||= []
|
127
128
|
|
@@ -140,7 +141,9 @@ module FFI
|
|
140
141
|
end
|
141
142
|
|
142
143
|
fop = fuse_opt_proc(symbols, bool_opts, param_opts, ignore, &block)
|
143
|
-
Libfuse.fuse_opt_parse(self, data, int_opts, fop).zero?
|
144
|
+
raise Error unless Libfuse.fuse_opt_parse(self, data, int_opts, fop).zero?
|
145
|
+
|
146
|
+
self
|
144
147
|
end
|
145
148
|
|
146
149
|
private
|