Module: FFI::Libfuse::Adapter::Ruby::Prepend

Includes:
FFI::Libfuse::Adapter
Included in:
Filesystem::VirtualFile, Filesystem::VirtualLink
Defined in:
lib/ffi/libfuse/adapter/ruby.rb

Overview

Note:

callbacks still expect to be ultimately handled by Safe, ie they raise SystemCallError and can return non Integer results

Can be prepended to concrete filesystem implementations to skip duplicate handling of Debug, Safe

FUSE Callbacks collapse

Instance Method Summary collapse

Methods included from FFI::Libfuse::Adapter

#fuse_super_respond_to?

Instance Method Details

#create(path, mode, ffi) ⇒ Object

Calls super if defined as per FFI::Libfuse::Adapter::Ruby#create storing result in ffi.fh and protecting it from GC until #release



# File 'lib/ffi/libfuse/adapter/ruby.rb', line 305

#destroy(init_obj) ⇒ Object

Calls super if defined and allows init_obj to be GC'd



346
347
348
349
# File 'lib/ffi/libfuse/adapter/ruby.rb', line 346

def destroy(init_obj)
  super if fuse_super_respond_to?(:destroy)
  handles.delete(init_obj) if init_obj
end

#flush(path, ffi) ⇒ Object

Flush data to path via

  • super if defined
  • :flush on ffi.fh if defined


251
252
253
254
255
256
# File 'lib/ffi/libfuse/adapter/ruby.rb', line 251

def flush(path, ffi)
  return super if defined?(super)

  fh = ffi&.fh
  fh.flush if fh.respond_to?(:flush)
end

#fsync(path, datasync, ffi) ⇒ Object

Sync data to path via



263
264
265
266
267
268
269
270
# File 'lib/ffi/libfuse/adapter/ruby.rb', line 263

def fsync(path, datasync, ffi)
  return super(path, datasync != 0, ffi) if defined?(super)

  fh = ffi&.fh
  return fh.datasync if datasync && fh.respond_to?(:datasync)

  fh.fsync if fh.respond_to?(:fsync)
end

#fuse_respond_to?(fuse_method) ⇒ Boolean

Returns true if our we can support fuse_method callback

ie. when

  • fuse_method is implemented directly by our superclass
  • the adapter can handle the callback via

Returns:

  • (Boolean)


120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
# File 'lib/ffi/libfuse/adapter/ruby.rb', line 120

def fuse_respond_to?(fuse_method)
  fuse_methods =
    case fuse_method
    when :read, :write, :flush, :release
      %i[open]
    when :read_buf
      %i[open read]
    when :write_buf
      %i[open write]
    when :readdir, :releasedir
      %i[opendir]
    else
      []
    end
  fuse_methods << fuse_method

  fuse_methods.any? { |m| super(m) }
end

#getxattr(path, name, buf, size) ⇒ Object

Get extended attributes via super as per FFI::Libfuse::Adapter::Ruby#getxattr

Raises:

  • (Errno::ENOTSUP)


282
283
284
285
286
# File 'lib/ffi/libfuse/adapter/ruby.rb', line 282

def getxattr(path, name, buf, size)
  raise Errno::ENOTSUP unless defined?(super)

  Ruby.getxattr(buf, size) { super(path, name) }
end

#init(*args) ⇒ Object

Calls super if defined and storing result to protect from GC until #destroy



340
341
342
343
# File 'lib/ffi/libfuse/adapter/ruby.rb', line 340

def init(*args)
  o = super if fuse_super_respond_to?(:init)
  handles << o if o
end

#listxattr(*args) ⇒ Object

List extended attributes via super as per FFI::Libfuse::Adapter::Ruby#listxattr

Raises:

  • (Errno::ENOTSUP)


289
290
291
292
293
294
# File 'lib/ffi/libfuse/adapter/ruby.rb', line 289

def listxattr(*args)
  raise Errno::ENOTSUP unless defined?(super)

  path, buf, size = args
  Ruby.listxattr(buf, size) { super(path) }
end

#open(path, ffi) ⇒ Object

Calls super if defined as per FFI::Libfuse::Adapter::Ruby#open storing result in ffi.fh and protecting it from GC until #release



# File 'lib/ffi/libfuse/adapter/ruby.rb', line 309

#opendir(path, ffi) ⇒ Object

Calls super if defined as per FFI::Libfuse::Adapter::Ruby#opendir storing result in ffi.fh and protecting it from GC until #releasedir



317
318
319
320
321
322
323
# File 'lib/ffi/libfuse/adapter/ruby.rb', line 317

%i[create open opendir].each do |fuse_method|
  define_method(fuse_method) do |*args|
    fh = super(*args) if fuse_super_respond_to?(fuse_method)
    store_handle(args.last, fh)
    0
  end
end

#read(path, buf, size, offset, ffi) ⇒ Object

Read data from path via



174
175
176
177
178
179
180
181
182
# File 'lib/ffi/libfuse/adapter/ruby.rb', line 174

def read(path, buf, size, offset, ffi)
  io, super_offset = defined?(super) && Ruby.rescue_not_implemented { super(path, size, offset, ffi) }
  offset = super_offset if io
  io ||= ffi.fh

  return [io, offset] unless buf # nil buf as called from read_buf, just wants the io/data back

  Ruby.read(buf, size, offset) { io }
end

#read_buf(path, bufp, size, offset, ffi) ⇒ Object

Read data with FuseBufs via

  • super if defined
  • ffi.fh.fileno if defined and not nil
  • result of #read


189
190
191
192
193
194
195
196
197
198
# File 'lib/ffi/libfuse/adapter/ruby.rb', line 189

def read_buf(path, bufp, size, offset, ffi)
  io, super_offset = defined?(super) && Ruby.rescue_not_implemented { super(path, size, offset, ffi) }
  offset = super_offset if io

  io ||= ffi.fh if ffi.fh.is_a?(Integer) || ffi.fh.respond_to?(:fileno)

  io, offset = read(path, nil, size, offset, ffi) unless io

  Ruby.read_buf(bufp, size, offset) { io }
end

#readdir(path, buf, filler, offset, ffi, flag_arg = nil) ⇒ Object

Read directory entries via



158
159
160
161
162
163
164
165
166
167
168
# File 'lib/ffi/libfuse/adapter/ruby.rb', line 158

def readdir(path, buf, filler, offset, ffi, flag_arg = nil)
  rd_filler = ReaddirFiller.new(buf, filler, fuse3: fuse3_compat?)

  flag_args = {}
  flag_args[:readdir_plus] = (flag_arg == :fuse_readdir_plus) if fuse3_compat?
  return super(path, offset, ffi, **flag_args, &rd_filler) if defined?(super)

  rd_filler.readdir_fh(ffi.fh, offset)
rescue StopIteration
  # do nothing
end

Read link name from path via super as per FFI::Libfuse::Adapter::Ruby#readlink

Raises:

  • (Errno::ENOTSUP)


201
202
203
204
205
# File 'lib/ffi/libfuse/adapter/ruby.rb', line 201

def readlink(path, buf, size)
  raise Errno::ENOTSUP unless defined?(super)

  Ruby.readlink(buf, size) { super(path, size) }
end

#release(path, ffi) ⇒ Object

Calls super if defined and allows ffi.fh to be GC'd



# File 'lib/ffi/libfuse/adapter/ruby.rb', line 325

#releasedir(path, ffi) ⇒ Object

Calls super if defined and allows ffi.fh to be GC'd



331
332
333
334
335
336
337
# File 'lib/ffi/libfuse/adapter/ruby.rb', line 331

%i[release releasedir].each do |fuse_method|
  define_method(fuse_method) do |path, ffi|
    super(path, ffi) if fuse_super_respond_to?(fuse_method)
  ensure
    release_handle(ffi)
  end
end

#root?(path) ⇒ Boolean

Helper to test if path is root

Returns:

  • (Boolean)


140
141
142
# File 'lib/ffi/libfuse/adapter/ruby.rb', line 140

def root?(path)
  path.respond_to?(:root?) ? path.root? : path.to_s == '/'
end

#setxattr(path, name, data, _size, flags) ⇒ Object

Set extended attributes via super as per FFI::Libfuse::Adapter::Ruby#setxattr

Raises:

  • (Errno::ENOTSUP)


273
274
275
276
277
278
279
# File 'lib/ffi/libfuse/adapter/ruby.rb', line 273

def setxattr(path, name, data, _size, flags)
  raise Errno::ENOTSUP unless defined?(super)

  # fuse converts the void* data buffer to a const char* null terminated string
  # which libfuse reads directly, so size is irrelevant
  super(path, name, data, flags)
end

#utimens(path, times, *fuse3_args) ⇒ Object

Set file atime, mtime via super as per FFI::Libfuse::Adapter::Ruby#utimens

Raises:

  • (Errno::ENOTSUP)


297
298
299
300
301
302
303
# File 'lib/ffi/libfuse/adapter/ruby.rb', line 297

def utimens(path, times, *fuse3_args)
  raise Errno::ENOTSUP unless defined?(super)

  atime, mtime = Stat::TimeSpec.fill_times(times[0, 2], 2).map(&:time)
  super(path, atime, mtime, *fuse3_args)
  0
end

#write(path, buf, size = buf.size, offset = 0, ffi = nil) ⇒ Object

Writes data to path via

  • super if defined
  • ffi.fh if not null and quacks like IO (see IO.write)


212
213
214
215
216
# File 'lib/ffi/libfuse/adapter/ruby.rb', line 212

def write(path, buf, size = buf.size, offset = 0, ffi = nil)
  Ruby.write(buf, size) do |data|
    (defined?(super) && Ruby.rescue_not_implemented { super(path, data, offset, ffi) }) || [ffi&.fh, offset]
  end
end

#write_buf(path, bufv, offset, ffi) ⇒ Object

Writes data to path with FuseBufVec via

  • super directly if defined and returns truthy
  • ffi.fh if it represents a file descriptor
  • #write


224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
# File 'lib/ffi/libfuse/adapter/ruby.rb', line 224

def write_buf(path, bufv, offset, ffi)
  super_result =
    if defined?(super)
      Ruby.rescue_not_implemented do
        super(path, offset, ffi) do |fh = nil, *flags|
          Ruby.write_buf(bufv) { |data| data || [fh, *flags] }
        end
      end
    end

  return super_result if super_result

  Ruby.write_buf(bufv) do |data|
    # only handle fileno,  otherwise fall back to write (which will try other kinds of IO)
    if data
      # fallback to #write
      write(path, data, data.size, offset, ffi)
    else
      [ffi&.fh.respond_to?(:fileno) && ffi.fh.fileno, offset]
    end
  end
end