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
@@ -7,7 +7,7 @@ require 'set'
|
|
7
7
|
module FFI
|
8
8
|
module Libfuse
|
9
9
|
module Adapter
|
10
|
-
# This module assists with converting native C libfuse into idiomatic Ruby
|
10
|
+
# This module assists with converting native C libfuse into idiomatic and duck-typed Ruby behaviour
|
11
11
|
#
|
12
12
|
# Class Method helpers
|
13
13
|
# ===
|
@@ -77,6 +77,7 @@ module FFI
|
|
77
77
|
return true if @filler.call(@buf, name, fill_stat, offset, *fill_flags).zero?
|
78
78
|
|
79
79
|
@buf = nil
|
80
|
+
false
|
80
81
|
end
|
81
82
|
|
82
83
|
# @return [Proc] a proc to pass to something that yields like #{fill}
|
@@ -101,8 +102,6 @@ module FFI
|
|
101
102
|
end
|
102
103
|
end
|
103
104
|
|
104
|
-
# rubocop:disable Metrics/ModuleLength
|
105
|
-
|
106
105
|
# Can be prepended to concrete filesystem implementations to skip duplicate handling of {Debug}, {Safe}
|
107
106
|
#
|
108
107
|
# @note callbacks still expect to be ultimately handled by {Safe}, ie they raise SystemCallError and can
|
@@ -152,29 +151,97 @@ module FFI
|
|
152
151
|
|
153
152
|
# @!group FUSE Callbacks
|
154
153
|
|
154
|
+
# Read directory entries via
|
155
|
+
#
|
156
|
+
# * super as per {Ruby#readdir} if defined
|
157
|
+
# * ffi.fh using {ReaddirFiller#readdir_fh}
|
158
|
+
def readdir(path, buf, filler, offset, ffi, flag_arg = nil)
|
159
|
+
rd_filler = ReaddirFiller.new(buf, filler, fuse3: fuse3_compat?)
|
160
|
+
|
161
|
+
flag_args = {}
|
162
|
+
flag_args[:readdir_plus] = (flag_arg == :fuse_readdir_plus) if fuse3_compat?
|
163
|
+
return super(path, offset, ffi, **flag_args, &rd_filler) if defined?(super)
|
164
|
+
|
165
|
+
rd_filler.readdir_fh(ffi.fh, offset)
|
166
|
+
rescue StopIteration
|
167
|
+
# do nothing
|
168
|
+
end
|
169
|
+
|
170
|
+
# Read data from path via
|
171
|
+
#
|
172
|
+
# * super as per {Ruby#read} if defined (or returns nil|false)
|
173
|
+
# * ffi.fh as per {Ruby.read}
|
174
|
+
def read(path, buf, size, offset, ffi)
|
175
|
+
io, super_offset = defined?(super) && Ruby.rescue_not_implemented { super(path, size, offset, ffi) }
|
176
|
+
offset = super_offset if io
|
177
|
+
io ||= ffi.fh
|
178
|
+
|
179
|
+
return [io, offset] unless buf # nil buf as called from read_buf, just wants the io/data back
|
180
|
+
|
181
|
+
Ruby.read(buf, size, offset) { io }
|
182
|
+
end
|
183
|
+
|
184
|
+
# Read data with {FuseBuf}s via
|
185
|
+
#
|
186
|
+
# * super if defined
|
187
|
+
# * ffi.fh.fileno if defined and not nil
|
188
|
+
# * result of {#read}
|
189
|
+
def read_buf(path, bufp, size, offset, ffi)
|
190
|
+
io, super_offset = defined?(super) && Ruby.rescue_not_implemented { super(path, size, offset, ffi) }
|
191
|
+
offset = super_offset if io
|
192
|
+
|
193
|
+
io ||= ffi.fh if ffi.fh.is_a?(Integer) || ffi.fh.respond_to?(:fileno)
|
194
|
+
|
195
|
+
io, offset = read(path, nil, size, offset, ffi) unless io
|
196
|
+
|
197
|
+
Ruby.read_buf(bufp, size, offset) { io }
|
198
|
+
end
|
199
|
+
|
200
|
+
# Read link name from path via super as per {Ruby#readlink}
|
201
|
+
def readlink(path, buf, size)
|
202
|
+
raise Errno::ENOTSUP unless defined?(super)
|
203
|
+
|
204
|
+
Ruby.readlink(buf, size) { super(path, size) }
|
205
|
+
end
|
206
|
+
|
155
207
|
# Writes data to path via
|
156
208
|
#
|
157
|
-
# * super
|
158
|
-
# *
|
209
|
+
# * super if defined
|
210
|
+
# * ffi.fh if not null and quacks like IO (see {IO.write})
|
211
|
+
#
|
159
212
|
def write(path, buf, size = buf.size, offset = 0, ffi = nil)
|
160
|
-
|
161
|
-
|
162
|
-
|
213
|
+
Ruby.write(buf, size) do |data|
|
214
|
+
(defined?(super) && Ruby.rescue_not_implemented { super(path, data, offset, ffi) }) || [ffi&.fh, offset]
|
215
|
+
end
|
163
216
|
end
|
164
217
|
|
165
|
-
# Writes data to path with {
|
218
|
+
# Writes data to path with {FuseBufVec} via
|
219
|
+
#
|
220
|
+
# * super directly if defined and returns truthy
|
221
|
+
# * ffi.fh if it represents a file descriptor
|
222
|
+
# * {#write}
|
166
223
|
#
|
167
|
-
# * super directly if defined
|
168
|
-
# * {FuseBufVec#copy_to_fd} if ffi.fh has non-nil :fileno
|
169
|
-
# * {FuseBufVec#copy_to_str} with the result of {Ruby#write}
|
170
224
|
def write_buf(path, bufv, offset, ffi)
|
171
|
-
|
225
|
+
super_result =
|
226
|
+
if defined?(super)
|
227
|
+
Ruby.rescue_not_implemented do
|
228
|
+
super(path, offset, ffi) do |fh = nil, *flags|
|
229
|
+
Ruby.write_buf(bufv) { |data| data || [fh, *flags] }
|
230
|
+
end
|
231
|
+
end
|
232
|
+
end
|
172
233
|
|
173
|
-
|
174
|
-
return bufv.copy_to_fd(fd, offset) if fd
|
234
|
+
return super_result if super_result
|
175
235
|
|
176
|
-
|
177
|
-
|
236
|
+
Ruby.write_buf(bufv) do |data|
|
237
|
+
# only handle fileno, otherwise fall back to write (which will try other kinds of IO)
|
238
|
+
if data
|
239
|
+
# fallback to #write
|
240
|
+
write(path, data, data.size, offset, ffi)
|
241
|
+
else
|
242
|
+
[ffi&.fh.respond_to?(:fileno) && ffi.fh.fileno, offset]
|
243
|
+
end
|
244
|
+
end
|
178
245
|
end
|
179
246
|
|
180
247
|
# Flush data to path via
|
@@ -202,56 +269,6 @@ module FFI
|
|
202
269
|
fh.fsync if fh.respond_to?(:fsync)
|
203
270
|
end
|
204
271
|
|
205
|
-
# Read data from path via
|
206
|
-
#
|
207
|
-
# * super as per {Ruby#read} if defined
|
208
|
-
# * ffi.fh as per {Ruby.read}
|
209
|
-
def read(path, buf, size, offset, ffi)
|
210
|
-
Ruby.read(buf, size, offset) do
|
211
|
-
defined?(super) ? super(path, size, offset, ffi) : ffi&.fh
|
212
|
-
end
|
213
|
-
end
|
214
|
-
|
215
|
-
# Read data with {FuseBuf}s via
|
216
|
-
#
|
217
|
-
# * super if defined
|
218
|
-
# * ffi.fh.fileno if defined and not nil
|
219
|
-
# * result of {#read}
|
220
|
-
def read_buf(path, bufp, size, offset, ffi)
|
221
|
-
return super if defined?(super)
|
222
|
-
|
223
|
-
Ruby.read_buf(bufp, size, offset) do
|
224
|
-
fh = ffi&.fh
|
225
|
-
fd = fh.fileno if fh.respond_to?(:fileno)
|
226
|
-
next fd if fd
|
227
|
-
|
228
|
-
read(path, nil, size, offset, ffi)
|
229
|
-
end
|
230
|
-
end
|
231
|
-
|
232
|
-
# Read link name from path via super as per {Ruby#readlink}
|
233
|
-
def readlink(path, buf, size)
|
234
|
-
raise Errno::ENOTSUP unless defined?(super)
|
235
|
-
|
236
|
-
Ruby.readlink(buf, size) { super(path, size) }
|
237
|
-
end
|
238
|
-
|
239
|
-
# Read directory entries via
|
240
|
-
#
|
241
|
-
# * super as per {Ruby#readdir} if defined
|
242
|
-
# * ffi.fh using {ReaddirFiller#readdir_fh}
|
243
|
-
def readdir(path, buf, filler, offset, ffi, flag_arg = nil)
|
244
|
-
rd_filler = ReaddirFiller.new(buf, filler, fuse3: fuse3_compat?)
|
245
|
-
|
246
|
-
flag_args = {}
|
247
|
-
flag_args[:readdir_plus] = (flag_arg == :fuse_readdir_plus) if fuse3_compat?
|
248
|
-
return super(path, offset, ffi, **flag_args, &rd_filler) if defined?(super)
|
249
|
-
|
250
|
-
rd_filler.readdir_fh(ffi.fh, offset)
|
251
|
-
rescue StopIteration
|
252
|
-
# do nothing
|
253
|
-
end
|
254
|
-
|
255
272
|
# Set extended attributes via super as per {Ruby#setxattr}
|
256
273
|
def setxattr(path, name, data, _size, flags)
|
257
274
|
raise Errno::ENOTSUP unless defined?(super)
|
@@ -351,8 +368,6 @@ module FFI
|
|
351
368
|
handles.delete(ffi.fh)
|
352
369
|
end
|
353
370
|
end
|
354
|
-
# rubocop:enable Metrics/ModuleLength
|
355
|
-
|
356
371
|
# @!group FUSE Callbacks
|
357
372
|
|
358
373
|
# @!method create(path, mode, fuse_file_info)
|
@@ -394,40 +409,78 @@ module FFI
|
|
394
409
|
# @return [Object] directory handle (available to future operations in fuse_file_info.fh)
|
395
410
|
# @see FuseOperations#opendir
|
396
411
|
|
397
|
-
# @!method write(path,data,offset,
|
412
|
+
# @!method write(path,data,offset,ffi)
|
398
413
|
# @abstract
|
399
|
-
# Write file data. If not implemented will
|
414
|
+
# Write file data. If not implemented will pass ffi.fh (from {open}) to {IO.write}
|
400
415
|
# @param [String] path
|
401
416
|
# @param [String] data
|
402
417
|
# @param [Integer] offset
|
403
|
-
# @param [FuseFileInfo]
|
404
|
-
# @return [
|
405
|
-
# @
|
418
|
+
# @param [FuseFileInfo] ffi
|
419
|
+
# @return [Integer] number of bytes written (<= data.size)
|
420
|
+
# @return [IO] an IO object (data will be sent to {IO.write})
|
421
|
+
# @return [nil|false] treat as not implemented
|
422
|
+
# @raise [NotImplementedError, Errno::ENOTSUP] treat as not implemented
|
406
423
|
# @see FuseOperations#write
|
407
424
|
|
408
|
-
# @!method write_buf(path,
|
425
|
+
# @!method write_buf(path,offset,ffi,&buffer)
|
409
426
|
# @abstract
|
410
|
-
# Write
|
411
|
-
#
|
412
|
-
#
|
413
|
-
|
414
|
-
#
|
427
|
+
# Write buffered data to a file via one of the following techniques
|
428
|
+
#
|
429
|
+
# 1. Yield object and flags to &buffer to write data directly to a {FuseBufVec}, File or IO
|
430
|
+
# and return the yield result (number of bytes written)
|
431
|
+
#
|
432
|
+
# 2. Yield with no params (or explicitly nil and flags) to retrieve String data (via {FuseBufVec#copy_to_str})
|
433
|
+
# to write to path@offset, returning the number of bytes written (eg data.size)
|
434
|
+
#
|
435
|
+
# 3. Return nil, false or do not implement to
|
436
|
+
# * try ffi.fh.fileno (from {#open}) as a file descriptor
|
437
|
+
# * or otherwise fallback to {#write}
|
438
|
+
#
|
439
|
+
# @param [String] path
|
440
|
+
# @param [Integer] offset
|
441
|
+
# @param [FuseFileInfo] ffi
|
442
|
+
# @param [Proc] buffer
|
443
|
+
# @yield [io = nil, *flags] Send data to io, or if not set, receive data as a string
|
444
|
+
# @yieldparam [FuseBufVec] io write directly into these buffers via {FuseBufVec.copy_to}
|
445
|
+
# @yieldparam [Integer|:fileno] io write to an open file descriptor via {FuseBufVec.copy_to_fd}
|
446
|
+
# @yieldparam [IO] io quacks like IO passed to {IO.write} to receive data
|
447
|
+
# @yieldparam [nil|false] io pull data from buffers into a String
|
448
|
+
# @yieldparam [Array<Symbol>] flags see {FuseBufVec}
|
449
|
+
# @yieldreturn [String] if io not supplied, the chunk of data to write is returned
|
450
|
+
# @yieldreturn [Integer] the number of bytes written to io
|
451
|
+
# @return [Integer] number of bytes written (<= data.size)
|
452
|
+
# @return [nil|false] treat as not implemented (do not yield AND return nil/false)
|
453
|
+
# @raise [NotImplementedError, Errno::ENOTSUP] treat as not implemented
|
454
|
+
# @see FuseOperations#write_buf
|
455
|
+
|
456
|
+
# @!method read(path,size,offset,ffi)
|
415
457
|
# @abstract
|
416
|
-
# Read file data.
|
458
|
+
# Read file data.
|
459
|
+
#
|
460
|
+
# If not implemented will send ffi.fh (from {open}) to {IO.read}
|
417
461
|
# @param [String] path
|
418
462
|
# @param [Integer] size
|
419
463
|
# @param [Integer] offset
|
420
|
-
# @param [FuseFileInfo]
|
421
|
-
# @return [
|
422
|
-
# @return [
|
423
|
-
# @raise [Errno::ENOTSUP]
|
464
|
+
# @param [FuseFileInfo] ffi
|
465
|
+
# @return [Array<Object,Integer>] io, offset will be passed to {IO.read}(io, size, offset)
|
466
|
+
# @return [nil|false] treat as not implemented
|
467
|
+
# @raise [NotImplementedError, Errno::ENOTSUP] treat as not implemented
|
424
468
|
# @see FuseOperations#read
|
425
|
-
# @see FuseOperations#read_buf
|
426
469
|
|
427
|
-
# @!method read_buf(path,
|
470
|
+
# @!method read_buf(path,size,offset,info)
|
428
471
|
# @abstract
|
429
|
-
#
|
430
|
-
#
|
472
|
+
# Read file data directly from a buffer
|
473
|
+
#
|
474
|
+
# If not implemented first tries ffi.fh.fileno (from {#open}) as a file descriptor before
|
475
|
+
# falling back to {#read}
|
476
|
+
# @param [String] path
|
477
|
+
# @param [Integer] size
|
478
|
+
# @param [Integer] offset
|
479
|
+
# @param [FuseFileInfo] info
|
480
|
+
# @return [Array<Object,Integer>] io, offset passed to {FuseBufVec#copy_to_io}(io, offset)
|
481
|
+
# @return [nil|false] treat as not implemented
|
482
|
+
# @raise [NotImplementedError, Errno::ENOTSUP] treat as not implemented
|
483
|
+
# @see FuseOperations#read_buf
|
431
484
|
|
432
485
|
# @!method readlink(path, size)
|
433
486
|
# @abstract
|
@@ -524,10 +577,18 @@ module FFI
|
|
524
577
|
mod.prepend(Prepend)
|
525
578
|
mod.include(Context)
|
526
579
|
mod.include(Debug)
|
527
|
-
mod.include(Safe)
|
528
580
|
end
|
529
581
|
|
530
582
|
class << self
|
583
|
+
# Helper to rescue not implemented or not supported errors from sub-filesystems
|
584
|
+
# @return[Object] result of block
|
585
|
+
# @return[nil] if block raises NotImplementedError or Errno::ENOTSUP
|
586
|
+
def rescue_not_implemented
|
587
|
+
yield
|
588
|
+
rescue NotImplementedError, Errno::ENOTSUP
|
589
|
+
nil
|
590
|
+
end
|
591
|
+
|
531
592
|
# Helper for implementing {FuseOperations#readlink}
|
532
593
|
# @param [FFI::Pointer] buf
|
533
594
|
# @param [Integer] size
|
@@ -541,27 +602,25 @@ module FFI
|
|
541
602
|
raise Errno::ENOTSUP unless link
|
542
603
|
raise Errno::ENAMETOOLONG unless link.size < size # includes terminating NUL
|
543
604
|
|
544
|
-
buf.put_string(link)
|
605
|
+
buf.put_string(0, link) # with NULL terminator.
|
545
606
|
0
|
546
607
|
end
|
547
608
|
|
548
609
|
# Helper for implementing {FuseOperations#read}
|
549
610
|
# @param [FFI::Pointer] buf
|
550
611
|
# @param [Integer] size
|
551
|
-
# @param [Integer] offset
|
552
|
-
# @return [Integer] size of data read
|
612
|
+
# @param [Integer, nil] offset
|
613
|
+
# @return [Integer] returns the size of data read into buf
|
553
614
|
# @yield []
|
554
|
-
# @yieldreturn [String,
|
615
|
+
# @yieldreturn [String, IO] the resulting data or IO like object
|
555
616
|
# @raise [Errno::ENOTSUP] if no data is returned
|
556
617
|
# @raise [Errno::ERANGE] if data return is larger than size
|
557
|
-
# @see
|
558
|
-
def read(buf, size, offset =
|
559
|
-
|
560
|
-
raise Errno::ENOTSUP unless
|
561
|
-
|
562
|
-
return data unless buf # called from read_buf
|
618
|
+
# @see Libfuse::IO.read
|
619
|
+
def read(buf, size, offset = nil)
|
620
|
+
io = yield
|
621
|
+
raise Errno::ENOTSUP unless io
|
563
622
|
|
564
|
-
data =
|
623
|
+
data = Libfuse::IO.read(io, size, offset)
|
565
624
|
raise Errno::ERANGE unless data.size <= size
|
566
625
|
|
567
626
|
buf.write_bytes(data)
|
@@ -572,76 +631,68 @@ module FFI
|
|
572
631
|
# @param [FFI::Pointer] bufp
|
573
632
|
# @param [Integer] size
|
574
633
|
# @param [Integer] offset
|
634
|
+
# @return [Integer] 0 for success
|
635
|
+
# @raise [Errno:ENOTSUP] if no data or io is returned
|
575
636
|
# @yield []
|
576
|
-
# @yieldreturn [
|
577
|
-
# @
|
578
|
-
|
579
|
-
|
580
|
-
|
637
|
+
# @yieldreturn [FuseBufVec] list of buffers to read from
|
638
|
+
# @yieldreturn [String, IO, Integer, :fileno] String, IO or file_descriptor to read from
|
639
|
+
# (see {FuseBufVec.create})
|
640
|
+
def read_buf(bufp, size, offset = nil)
|
641
|
+
io = yield
|
642
|
+
raise Errno::ENOTSUP unless io
|
643
|
+
|
644
|
+
fbv = io.is_a?(FuseBufVec) ? io : FuseBufVec.create(io, size, offset)
|
645
|
+
fbv.store_to(bufp)
|
581
646
|
|
582
|
-
bufp.write_pointer(data_to_bufvec(data, size, offset).to_ptr)
|
583
647
|
0
|
584
648
|
end
|
585
649
|
|
586
|
-
# Helper to
|
587
|
-
#
|
650
|
+
# Helper to implement #{FuseOperations#write}
|
651
|
+
# yields the data and receives expects IO to write the data to
|
652
|
+
# @param [FFI::Pointer] buf
|
588
653
|
# @param [Integer] size
|
589
|
-
# @
|
590
|
-
# @
|
591
|
-
|
592
|
-
|
593
|
-
|
594
|
-
|
654
|
+
# @yield(data)
|
655
|
+
# @yieldparam [String] data data to write
|
656
|
+
# @yieldreturn [nil|false] data has not been handled (raises Errno::ENOTSUP)
|
657
|
+
# @yieldreturn [Integer] number of bytes written (will not send to {IO.write})
|
658
|
+
# @yieldreturn [IO] to use with {IO.write}
|
659
|
+
# @return [Integer] number of bytes written
|
660
|
+
# @raise [Errno::ENOTSUP] if nothing is returned from yield
|
661
|
+
def write(buf, size)
|
662
|
+
data = buf.read_bytes(size) if buf.respond_to?(:read_bytes)
|
663
|
+
data ||= buf.to_s
|
595
664
|
|
596
|
-
io
|
597
|
-
end
|
665
|
+
io, offset = yield data
|
598
666
|
|
599
|
-
|
600
|
-
|
601
|
-
# @param [Integer] size
|
602
|
-
# @param [Integer] offset
|
603
|
-
# @return [FuseBufVec]
|
604
|
-
def data_to_bufvec(data, size, offset)
|
605
|
-
data = data.fileno if data.respond_to?(:fileno)
|
606
|
-
return FuseBufVec.init(autorelease: false, size: size, fd: data, pos: offset) if data.is_a?(Integer)
|
667
|
+
raise Errno::ENOSUP unless io
|
668
|
+
return io if io.is_a?(Integer)
|
607
669
|
|
608
|
-
|
609
|
-
FuseBufVec.init(autorelease: false, size: str.size, mem: FFI::MemoryPointer.from_string(str))
|
670
|
+
Libfuse::IO.write(io, data, offset)
|
610
671
|
end
|
611
672
|
|
612
|
-
# Helper to implement #{FuseOperations#
|
613
|
-
#
|
614
|
-
#
|
615
|
-
#
|
673
|
+
# Helper to implement #{FuseOperations#write_buf}
|
674
|
+
#
|
675
|
+
# Yields firstly with data = nil
|
676
|
+
# A returned truthy object is sent to buvf.copy_to_io
|
677
|
+
# Otherwise yields again with string data from bufv.copy_to_str expecting the caller to write the data
|
678
|
+
# and return the number of bytes written
|
679
|
+
#
|
680
|
+
# @param [FuseBufVec] bufv
|
616
681
|
# @yield [data]
|
617
|
-
# @yieldparam [
|
618
|
-
# @
|
619
|
-
|
620
|
-
|
621
|
-
|
622
|
-
|
623
|
-
|
624
|
-
|
625
|
-
|
626
|
-
|
627
|
-
|
628
|
-
|
629
|
-
|
630
|
-
|
631
|
-
# @param [:pwrite,:seek,:write] handle an IO like file handle
|
632
|
-
# @return [Integer] size
|
633
|
-
# @raise [Errno::ENOTSUP] if handle is does not quack like an open file
|
634
|
-
def write_fh(buf, size, offset, handle)
|
635
|
-
write_data(buf, size) do |data|
|
636
|
-
if handle.respond_to?(:pwrite)
|
637
|
-
handle.pwrite(data, offset)
|
638
|
-
elsif handle.respond_to?(:write)
|
639
|
-
handle.seek(offset) if handle.respond_to?(:seek)
|
640
|
-
handle.write(data)
|
641
|
-
else
|
642
|
-
raise Errno::ENOTSUP
|
643
|
-
end
|
644
|
-
end
|
682
|
+
# @yieldparam [nil] data first yield is nil
|
683
|
+
# @yieldparam [String] data second yield is the data
|
684
|
+
# @yieldreturn [nil, Array<IO,Integer,Symbol...>] io, [offset,] *flags
|
685
|
+
# for first yield can return nil to indicate it wants the data as a string (via second yield)
|
686
|
+
# alternative an object to receive the data via {FuseBufVec#copy_to_io}(io, offset = nil, *flags)
|
687
|
+
# @yieldreturn [Integer] second yield must return number of bytes written
|
688
|
+
# @return [Integer] number of bytes written
|
689
|
+
# @raise [Errno::ENOTSUP] if nothing is returned from either yield
|
690
|
+
def write_buf(bufv)
|
691
|
+
fh, *flags = yield nil # what kind of result do we want
|
692
|
+
return bufv.copy_to_io(fh, offset, *flags) if fh
|
693
|
+
|
694
|
+
data = bufv.copy_to_str(*flags)
|
695
|
+
yield data || (raise Errno::ENOTSUP)
|
645
696
|
end
|
646
697
|
|
647
698
|
# Helper for implementing {FuseOperations#getxattr}
|
@@ -4,55 +4,103 @@ module FFI
|
|
4
4
|
module Libfuse
|
5
5
|
module Adapter
|
6
6
|
# Safe callbacks convert return values into integer responses, and rescues errors
|
7
|
-
#
|
8
|
-
# Applies to all callbacks except :init, :destroy
|
9
7
|
module Safe
|
10
8
|
# @!visibility private
|
11
9
|
def fuse_wrappers(*wrappers)
|
12
10
|
wrappers << {
|
13
|
-
wrapper: proc { |fm, *args, **_, &b|
|
14
|
-
excludes:
|
11
|
+
wrapper: proc { |fm, *args, **_, &b| safe_integer_callback(fm, *args, default_errno: default_errno, &b) },
|
12
|
+
excludes: FuseOperations::VOID_RETURN + FuseOperations::MEANINGFUL_RETURN
|
13
|
+
}
|
14
|
+
wrappers << {
|
15
|
+
wrapper: proc { |fm, *args, **_, &b|
|
16
|
+
safe_meaningful_integer_callback(fm, *args, default_errno: default_errno, &b)
|
17
|
+
},
|
18
|
+
includes: FuseOperations::MEANINGFUL_RETURN
|
19
|
+
}
|
20
|
+
wrappers << {
|
21
|
+
wrapper: proc { |fm, *args, **_, &b| safe_void_callback(fm, *args, &b) },
|
22
|
+
includes: FuseOperations::VOID_RETURN
|
15
23
|
}
|
16
24
|
return wrappers unless defined?(super)
|
17
25
|
|
18
26
|
super(*wrappers)
|
19
27
|
end
|
20
28
|
|
21
|
-
# @return [Integer] the default errno. ENOTRECOVERABLE unless overridden
|
29
|
+
# @return [Integer] the default errno to return for rescued errors. ENOTRECOVERABLE unless overridden
|
22
30
|
def default_errno
|
23
31
|
defined?(super) ? super : Errno::ENOTRECOVERABLE::Errno
|
24
32
|
end
|
25
33
|
|
26
34
|
module_function
|
27
35
|
|
28
|
-
# Process the
|
36
|
+
# Process the result of yielding to the fuse callback to provide a safe return value to libfuse
|
37
|
+
#
|
38
|
+
# For callbacks in {FuseOperations.VOID_RETURN}
|
39
|
+
#
|
40
|
+
# * the return value and any unexpected errors raised are ignored
|
41
|
+
#
|
42
|
+
# For callbacks in {FuseOperations.MEANINGFUL_RETURN}
|
29
43
|
#
|
30
|
-
#
|
31
|
-
#
|
32
|
-
#
|
44
|
+
# * should raise appropriate `Errno` error for expected errors (eg `Errno::ENOENT`)
|
45
|
+
# * must return a value convertable to an integer (via :to_i) - either a positive meaningful value
|
46
|
+
# or a negative errno value which will be returned directly
|
47
|
+
# * otherwise default_errno is returned
|
33
48
|
#
|
34
|
-
#
|
49
|
+
# For remaining path callbacks
|
35
50
|
#
|
36
|
-
#
|
37
|
-
#
|
38
|
-
#
|
51
|
+
# * should raise appropriate `Errno` error for expected errors (eg `Errno::ENOENT`)
|
52
|
+
# * may return a negative Integer (equivalent to raising the corresponding `SystemCallError`)
|
53
|
+
# * any other value returned is considered success and 0 is returned
|
54
|
+
# * unexpected errors raised are rescued and default_errno is returned
|
39
55
|
#
|
40
|
-
# @
|
56
|
+
# @param [Integer] default_errno
|
57
|
+
# value to return for any unexpected errors
|
41
58
|
#
|
59
|
+
# @return [nil]
|
60
|
+
# For void callbacks
|
61
|
+
# @return [Integer]
|
62
|
+
# For path callbacks, either 0 for success or a negative errno value
|
42
63
|
def safe_callback(fuse_method, *args, default_errno: Errno::ENOTRECOVERABLE::Errno)
|
43
|
-
|
64
|
+
if FuseOperations::MEANINGFUL_RETURN.include?(fuse_method)
|
65
|
+
safe_meaningful_integer_callback(fuse_method, *args, default_errno: default_errno)
|
66
|
+
elsif FuseOperations::VOID_RETURN.include?(fuse_method)
|
67
|
+
safe_void_callback(fuse_method, *args)
|
68
|
+
else
|
69
|
+
safe_integer_callback(fuse_method, *args, default_errno: default_errno)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
private
|
44
74
|
|
45
|
-
|
75
|
+
def safe_integer_callback(_, *args, default_errno: Errno::ENOTRECOVERABLE::Errno)
|
76
|
+
safe_errno(default_errno) do
|
77
|
+
result = yield(*args)
|
78
|
+
result.is_a?(Integer) && result.negative? ? result : 0
|
79
|
+
end
|
80
|
+
end
|
46
81
|
|
47
|
-
|
82
|
+
def safe_meaningful_integer_callback(_, *args, default_errno: Errno::ENOTRECOVERABLE::Errno)
|
83
|
+
safe_errno(default_errno) do
|
84
|
+
yield(*args).to_i
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def safe_errno(default_errno)
|
89
|
+
yield
|
48
90
|
rescue SystemCallError => e
|
49
91
|
-e.errno
|
50
|
-
rescue StandardError, ScriptError
|
51
|
-
# rubocop:disable Layout/LineLength
|
52
|
-
warn ["FFI::Libfuse error in #{fuse_method}", *e.backtrace.reverse, "#{e.class.name}:#{e.message}"].join("\n\t")
|
53
|
-
# rubocop:enable Layout/LineLength
|
92
|
+
rescue StandardError, ScriptError
|
54
93
|
-default_errno.abs
|
55
94
|
end
|
95
|
+
|
96
|
+
# Process callbacks that return void, simply by swallowing unexpected errors
|
97
|
+
def safe_void_callback(_, *args)
|
98
|
+
yield(*args)
|
99
|
+
nil
|
100
|
+
rescue StandardError, ScriptError
|
101
|
+
# Swallow unexpected exceptions
|
102
|
+
nil
|
103
|
+
end
|
56
104
|
end
|
57
105
|
end
|
58
106
|
end
|
@@ -45,8 +45,9 @@ module FFI
|
|
45
45
|
delegate.respond_to?(method)
|
46
46
|
end
|
47
47
|
|
48
|
-
def wrap_callback(method, proc_wrapper = nil, wrapper: proc_wrapper, excludes: [], &block)
|
48
|
+
def wrap_callback(method, proc_wrapper = nil, wrapper: proc_wrapper, excludes: [], includes: nil, &block)
|
49
49
|
return block if excludes.include?(method)
|
50
|
+
return block unless includes.nil? || includes.include?(method)
|
50
51
|
|
51
52
|
# Wrapper proc takes fuse_method as first arg, but the resulting proc only takes the callback args
|
52
53
|
# ie so wrappers should not yield the fuse_method onwards!!
|
@@ -5,7 +5,7 @@ require_relative '../../stat_vfs'
|
|
5
5
|
module FFI
|
6
6
|
module Libfuse
|
7
7
|
module Filesystem
|
8
|
-
# Helper for filesystem accounting
|
8
|
+
# Helper for filesystem accounting, ie to provide useful df output
|
9
9
|
class Accounting
|
10
10
|
OPTIONS = { 'max_space=' => :max_space, 'max_nodes=' => :max_nodes }.freeze
|
11
11
|
|