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
@@ -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
|
|