duperemoverb/lib/deduperemoverb/file_dedupe_range.rb

144 lines
3.9 KiB
Ruby

# vim: set noet sw=2 ts=2 sts=2:
require 'ffi'
require 'pathname'
Errno::Errnos = []
Errno.constants.
map {|c| Errno.const_get c }.
select {|c| c.is_a?( Class) and c.superclass == SystemCallError and c.const_defined?( :Errno) }.
each {|c| Errno::Errnos[c::Errno] = c }
module FileDedupeRange
extend FFI::Library
ffi_lib Pathname.new( __FILE__).
dirname.
join( *w[ext deduperemoverb deduperemoverb.so]).
expand_path.
to_s
# struct file_dedupe_range_info {
# __s64 dest_fd; /* in - destination file */
# __u64 dest_offset; /* in - start of extent in destination */
# __u64 bytes_deduped; /* out - total # of bytes we were able to dedupe from this file. */
# /* status of this dedupe operation:
# * < 0 for error
# * == FILE_DEDUPE_RANGE_SAME if dedupe succeeds
# * == FILE_DEDUPE_RANGE_DIFFERS if data differs
# */
# __s32 status; /* out */
# __u32 reserved; /* must be zero */
# };
class Destination < FFI::Struct
layout :fd, :int64,
:offset, :uint64,
:bytes_deduped, :uint64,
:status, :int32,
:reserved, :uint32
def fd=(i) self[:fd] = i end
def fd() self[:fd] end
def offset=(i) self[:offset] = i end
def offset() self[:offset] end
def bytes_deduped() self[:bytes_deduped] end
def status() self[:status] end
alias initialize_without_defaults initialize
def initialize_with_defaults *a
initialize_without_defaults *a
self[:reserved] = self[:bytes_deduped] = self[:status] = 0
end
alias initialize initialize_with_defaults
def inspect
sprintf "#<%s fd=%d offset=%d bytes_deduped=%d status=%d>",
self.class.name,
self[:fd],
self[:offset],
self[:bytes_deduped],
self[:status]
end
end
# /* from struct btrfs_ioctl_file_extent_same_args */
# struct file_dedupe_range {
# __u64 src_offset; /* in - start of extent in source */
# __u64 src_length; /* in - length of extent */
# __u16 dest_count; /* in - total elements in info array */
# __u16 reserved1; /* must be zero */
# __u32 reserved2; /* must be zero */
# struct file_dedupe_range_info info[0];
# };
class Base < FFI::Struct
class <<self
private
def layout_init dest_count
layout :offset, :uint64,
:length, :uint64,
:dest_count, :uint16,
:reserved1, :uint16,
:reserved2, :uint16,
:destinations, [Destination, dest_count]
end
public
def inspect
"#{superclass.name}[?]"
end
end
def offset() self[:offset] end
def length() self[:length] end
def dest_count() self[:dest_count] end
def destinations() self[:destinations] end
def initialize offset, length
self[:offset], self[:length] = offset, length
self[:reserved1] = self[:reserved2] = 0
self[:dest_count] = layout[:destinations].size / Destination.size
end
def inspect
sprintf "#<%s[%d] %d:%d dests=[%s]>",
self.class.superclass.name,
self[:dest_count].to_i,
self[:offset],
self[:length],
destinations.map {|d|
sprintf "#<%d:%d %d - %d>", d[:fd], d[:offset], d[:bytes_deduped], d[:status]
}.join( ',')
end
end
class <<self
@@classes = []
def new offset:, length:, dest_count:
r = self[dest_count].new offset, length
end
def [] dest_count
@@classes[dest_count] ||=
Class.new( Base).tap {|klass| klass.send :layout_init, dest_count }
end
def errno
Errno::Errnos[_errno]
end
end
# int ioctl_fideduperange(int src_fd, struct file_dedupe_range *arg);
attach_function :dedup, :fideduperange, [:int, FileDedupeRange::Base.ptr], :int
# fetch last errno after last calling dedup
attach_function :_errno, :fideduperange_errno, [], :int
# fetch constants from C-world, which are only provided as macro
attach_function :_consts, :fideduperange_consts, [:pointer], :void
x = FFI::MemoryPointer.new :int64, 3
_consts x
REQUEST_CONST, SAME, DIFFERS = x.read_array_of_int64( 3)
end