mirror of
https://github.com/rwinkhart/sys.git
synced 2026-09-06 09:07:16 -04:00
unix: fix and test the FIDEDUPERANGE Linux ioctl
The previous implementation didn't match the ioctl spec. Fixes golang/go#43678 Change-Id: Ia1c292c2dbd4913fb1d7e8331d9f18e23169d64a GitHub-Last-Rev: 5331c424ef5dd0384afda9effbb76afd7705ecfd GitHub-Pull-Request: golang/sys#97 Reviewed-on: https://go-review.googlesource.com/c/sys/+/284352 Run-TryBot: Tobias Klauser <tobias.klauser@gmail.com> TryBot-Result: Go Bot <gobot@golang.org> Reviewed-by: Tobias Klauser <tobias.klauser@gmail.com> Trust: Tobias Klauser <tobias.klauser@gmail.com> Trust: Ian Lance Taylor <iant@golang.org>
This commit is contained in:
committed by
Tobias Klauser
parent
683adc9d29
commit
8e9945a547
+10
-1
@@ -474,7 +474,16 @@ type Flock_t C.struct_flock
|
|||||||
|
|
||||||
type FileCloneRange C.struct_file_clone_range
|
type FileCloneRange C.struct_file_clone_range
|
||||||
|
|
||||||
type FileDedupeRange C.struct_file_dedupe_range
|
type RawFileDedupeRange C.struct_file_dedupe_range
|
||||||
|
|
||||||
|
type RawFileDedupeRangeInfo C.struct_file_dedupe_range_info
|
||||||
|
|
||||||
|
const (
|
||||||
|
SizeofRawFileDedupeRange = C.sizeof_struct_file_dedupe_range
|
||||||
|
SizeofRawFileDedupeRangeInfo = C.sizeof_struct_file_dedupe_range_info
|
||||||
|
FILE_DEDUPE_RANGE_SAME = C.FILE_DEDUPE_RANGE_SAME
|
||||||
|
FILE_DEDUPE_RANGE_DIFFERS = C.FILE_DEDUPE_RANGE_DIFFERS
|
||||||
|
)
|
||||||
|
|
||||||
// Filesystem Encryption
|
// Filesystem Encryption
|
||||||
|
|
||||||
|
|||||||
+54
-5
@@ -137,12 +137,61 @@ func IoctlFileClone(destFd, srcFd int) error {
|
|||||||
return ioctl(destFd, FICLONE, uintptr(srcFd))
|
return ioctl(destFd, FICLONE, uintptr(srcFd))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type FileDedupeRange struct {
|
||||||
|
Src_offset uint64
|
||||||
|
Src_length uint64
|
||||||
|
Reserved1 uint16
|
||||||
|
Reserved2 uint32
|
||||||
|
Info []FileDedupeRangeInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
type FileDedupeRangeInfo struct {
|
||||||
|
Dest_fd int64
|
||||||
|
Dest_offset uint64
|
||||||
|
Bytes_deduped uint64
|
||||||
|
Status int32
|
||||||
|
Reserved uint32
|
||||||
|
}
|
||||||
|
|
||||||
// IoctlFileDedupeRange performs an FIDEDUPERANGE ioctl operation to share the
|
// IoctlFileDedupeRange performs an FIDEDUPERANGE ioctl operation to share the
|
||||||
// range of data conveyed in value with the file associated with the file
|
// range of data conveyed in value from the file associated with the file
|
||||||
// descriptor destFd. See the ioctl_fideduperange(2) man page for details.
|
// descriptor srcFd to the value.Info destinations. See the
|
||||||
func IoctlFileDedupeRange(destFd int, value *FileDedupeRange) error {
|
// ioctl_fideduperange(2) man page for details.
|
||||||
err := ioctl(destFd, FIDEDUPERANGE, uintptr(unsafe.Pointer(value)))
|
func IoctlFileDedupeRange(srcFd int, value *FileDedupeRange) error {
|
||||||
runtime.KeepAlive(value)
|
buf := make([]byte, SizeofRawFileDedupeRange+
|
||||||
|
len(value.Info)*SizeofRawFileDedupeRangeInfo)
|
||||||
|
rawrange := (*RawFileDedupeRange)(unsafe.Pointer(&buf[0]))
|
||||||
|
rawrange.Src_offset = value.Src_offset
|
||||||
|
rawrange.Src_length = value.Src_length
|
||||||
|
rawrange.Dest_count = uint16(len(value.Info))
|
||||||
|
rawrange.Reserved1 = value.Reserved1
|
||||||
|
rawrange.Reserved2 = value.Reserved2
|
||||||
|
|
||||||
|
for i := range value.Info {
|
||||||
|
rawinfo := (*RawFileDedupeRangeInfo)(unsafe.Pointer(
|
||||||
|
uintptr(unsafe.Pointer(&buf[0])) + uintptr(SizeofRawFileDedupeRange) +
|
||||||
|
uintptr(i*SizeofRawFileDedupeRangeInfo)))
|
||||||
|
rawinfo.Dest_fd = value.Info[i].Dest_fd
|
||||||
|
rawinfo.Dest_offset = value.Info[i].Dest_offset
|
||||||
|
rawinfo.Bytes_deduped = value.Info[i].Bytes_deduped
|
||||||
|
rawinfo.Status = value.Info[i].Status
|
||||||
|
rawinfo.Reserved = value.Info[i].Reserved
|
||||||
|
}
|
||||||
|
|
||||||
|
err := ioctl(srcFd, FIDEDUPERANGE, uintptr(unsafe.Pointer(&buf[0])))
|
||||||
|
|
||||||
|
// Output
|
||||||
|
for i := range value.Info {
|
||||||
|
rawinfo := (*RawFileDedupeRangeInfo)(unsafe.Pointer(
|
||||||
|
uintptr(unsafe.Pointer(&buf[0])) + uintptr(SizeofRawFileDedupeRange) +
|
||||||
|
uintptr(i*SizeofRawFileDedupeRangeInfo)))
|
||||||
|
value.Info[i].Dest_fd = rawinfo.Dest_fd
|
||||||
|
value.Info[i].Dest_offset = rawinfo.Dest_offset
|
||||||
|
value.Info[i].Bytes_deduped = rawinfo.Bytes_deduped
|
||||||
|
value.Info[i].Status = rawinfo.Status
|
||||||
|
value.Info[i].Reserved = rawinfo.Reserved
|
||||||
|
}
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -796,3 +796,94 @@ func TestOpenat2(t *testing.T) {
|
|||||||
t.Errorf("Openat2 should fail with EXDEV, got %v", err)
|
t.Errorf("Openat2 should fail with EXDEV, got %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestFideduperange(t *testing.T) {
|
||||||
|
if runtime.GOOS == "android" {
|
||||||
|
// The ioctl in the build robot android-amd64 returned ENOTTY,
|
||||||
|
// an error not documented for that syscall.
|
||||||
|
t.Skip("FIDEDUPERANGE ioctl doesn't work on android, skipping test")
|
||||||
|
}
|
||||||
|
|
||||||
|
f1, err := ioutil.TempFile("", t.Name())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer f1.Close()
|
||||||
|
defer os.Remove(f1.Name())
|
||||||
|
|
||||||
|
// Test deduplication with two blocks of zeros
|
||||||
|
data := make([]byte, 4096)
|
||||||
|
|
||||||
|
for i := 0; i < 2; i += 1 {
|
||||||
|
_, err = f1.Write(data)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
f2, err := ioutil.TempFile("", t.Name())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer f2.Close()
|
||||||
|
defer os.Remove(f2.Name())
|
||||||
|
|
||||||
|
for i := 0; i < 2; i += 1 {
|
||||||
|
// Make the 2nd block different
|
||||||
|
if i == 1 {
|
||||||
|
data[1] = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = f2.Write(data)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dedupe := unix.FileDedupeRange{
|
||||||
|
Src_offset: uint64(0),
|
||||||
|
Src_length: uint64(4096),
|
||||||
|
Info: []unix.FileDedupeRangeInfo{
|
||||||
|
unix.FileDedupeRangeInfo{
|
||||||
|
Dest_fd: int64(f2.Fd()),
|
||||||
|
Dest_offset: uint64(0),
|
||||||
|
},
|
||||||
|
unix.FileDedupeRangeInfo{
|
||||||
|
Dest_fd: int64(f2.Fd()),
|
||||||
|
Dest_offset: uint64(4096),
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
|
||||||
|
err = unix.IoctlFileDedupeRange(int(f1.Fd()), &dedupe)
|
||||||
|
if err == unix.EOPNOTSUPP || err == unix.EINVAL {
|
||||||
|
t.Skip("deduplication not supported on this filesystem")
|
||||||
|
} else if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// The first Info should be equal
|
||||||
|
if dedupe.Info[0].Status < 0 {
|
||||||
|
// We expect status to be a negated errno
|
||||||
|
t.Errorf("Unexpected error in FileDedupeRange: %s",
|
||||||
|
unix.ErrnoName(unix.Errno(-dedupe.Info[0].Status)))
|
||||||
|
} else if dedupe.Info[0].Status == unix.FILE_DEDUPE_RANGE_DIFFERS {
|
||||||
|
t.Errorf("Unexpected different bytes in FileDedupeRange")
|
||||||
|
}
|
||||||
|
if dedupe.Info[0].Bytes_deduped != 4096 {
|
||||||
|
t.Errorf("Unexpected amount of bytes deduped %v != %v",
|
||||||
|
dedupe.Info[0].Bytes_deduped, 4096)
|
||||||
|
}
|
||||||
|
|
||||||
|
// The second Info should be different
|
||||||
|
if dedupe.Info[1].Status < 0 {
|
||||||
|
// We expect status to be a negated errno
|
||||||
|
t.Errorf("Unexpected error in FileDedupeRange: %s",
|
||||||
|
unix.ErrnoName(unix.Errno(-dedupe.Info[1].Status)))
|
||||||
|
} else if dedupe.Info[1].Status == unix.FILE_DEDUPE_RANGE_SAME {
|
||||||
|
t.Errorf("Unexpected equal bytes in FileDedupeRange")
|
||||||
|
}
|
||||||
|
if dedupe.Info[1].Bytes_deduped != 0 {
|
||||||
|
t.Errorf("Unexpected amount of bytes deduped %v != %v",
|
||||||
|
dedupe.Info[1].Bytes_deduped, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+16
-1
@@ -84,7 +84,7 @@ type FileCloneRange struct {
|
|||||||
Dest_offset uint64
|
Dest_offset uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
type FileDedupeRange struct {
|
type RawFileDedupeRange struct {
|
||||||
Src_offset uint64
|
Src_offset uint64
|
||||||
Src_length uint64
|
Src_length uint64
|
||||||
Dest_count uint16
|
Dest_count uint16
|
||||||
@@ -92,6 +92,21 @@ type FileDedupeRange struct {
|
|||||||
Reserved2 uint32
|
Reserved2 uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type RawFileDedupeRangeInfo struct {
|
||||||
|
Dest_fd int64
|
||||||
|
Dest_offset uint64
|
||||||
|
Bytes_deduped uint64
|
||||||
|
Status int32
|
||||||
|
Reserved uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
SizeofRawFileDedupeRange = 0x18
|
||||||
|
SizeofRawFileDedupeRangeInfo = 0x20
|
||||||
|
FILE_DEDUPE_RANGE_SAME = 0x0
|
||||||
|
FILE_DEDUPE_RANGE_DIFFERS = 0x1
|
||||||
|
)
|
||||||
|
|
||||||
type FscryptPolicy struct {
|
type FscryptPolicy struct {
|
||||||
Version uint8
|
Version uint8
|
||||||
Contents_encryption_mode uint8
|
Contents_encryption_mode uint8
|
||||||
|
|||||||
Reference in New Issue
Block a user