144 lines
3.9 KiB
Ruby
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
|
|
|
|
|
|
|