From a0749e765868b948e5e98cd3cc582c38a3891bfe Mon Sep 17 00:00:00 2001 From: lyndon-li <98304688+Lyndon-Li@users.noreply.github.com> Date: Fri, 17 Jul 2026 16:35:19 +0800 Subject: [PATCH] Block dev for restore (#10013) * add block dev operations for block uploader restore Signed-off-by: Lyndon-Li * add block dev operations for block uploader restore Signed-off-by: Lyndon-Li --------- Signed-off-by: Lyndon-Li --- changelogs/unreleased/10013-Lyndon-Li | 1 + pkg/uploader/block/dev_linux.go | 27 ++++++++++++++++++++++++++ pkg/uploader/block/dev_linux_test.go | 28 +++++++++++++++++++++++++++ pkg/uploader/block/dev_other.go | 4 ++++ 4 files changed, 60 insertions(+) create mode 100644 changelogs/unreleased/10013-Lyndon-Li diff --git a/changelogs/unreleased/10013-Lyndon-Li b/changelogs/unreleased/10013-Lyndon-Li new file mode 100644 index 000000000..501a3673b --- /dev/null +++ b/changelogs/unreleased/10013-Lyndon-Li @@ -0,0 +1 @@ +Add block dev restore operations for block data mover \ No newline at end of file diff --git a/pkg/uploader/block/dev_linux.go b/pkg/uploader/block/dev_linux.go index 297815390..689031acf 100644 --- a/pkg/uploader/block/dev_linux.go +++ b/pkg/uploader/block/dev_linux.go @@ -23,6 +23,7 @@ import ( "os" "path/filepath" "syscall" + "unsafe" "github.com/cockroachdb/errors" ) @@ -76,3 +77,29 @@ func resolveSymlink(path string) (string, error) { return filepath.EvalSymlinks(path) } + +func blkZeroOut(dest *os.File, start int64, length int64) error { + const BLKZEROOUT = 0x127b + + zeroRange := [2]uint64{uint64(start), uint64(length)} + + rawConn, err := dest.SyscallConn() + if err != nil { + return errors.Wrap(err, "error getting raw connection") + } + + ioctlErr := syscall.Errno(0) + if err := rawConn.Control(func(fd uintptr) { + if _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, fd, BLKZEROOUT, uintptr(unsafe.Pointer(&zeroRange[0]))); errno != 0 { + ioctlErr = errno + } + }); err != nil { + return errors.Wrap(err, "error controlling block dev") + } + + if ioctlErr != 0 { + return errors.Wrapf(ioctlErr, "error calling ioctl on block dev") + } + + return nil +} diff --git a/pkg/uploader/block/dev_linux_test.go b/pkg/uploader/block/dev_linux_test.go index 42f0dd83e..b5243cc69 100644 --- a/pkg/uploader/block/dev_linux_test.go +++ b/pkg/uploader/block/dev_linux_test.go @@ -26,6 +26,7 @@ import ( "testing" "time" + "github.com/cockroachdb/errors" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -356,3 +357,30 @@ func TestOpenBlockDevice(t *testing.T) { }) } } + +func TestBlkZeroOut(t *testing.T) { + t.Run("closed file returns error", func(t *testing.T) { + f, err := os.CreateTemp(t.TempDir(), "blkzeroout-test-*") + require.NoError(t, err) + err = f.Close() + require.NoError(t, err) + + err = blkZeroOut(f, 0, 1024) + assert.Error(t, err) + }) + + t.Run("regular file returns ioctl error", func(t *testing.T) { + f, err := os.CreateTemp(t.TempDir(), "blkzeroout-test-*") + require.NoError(t, err) + defer f.Close() + + err = blkZeroOut(f, 0, 1024) + require.Error(t, err) + assert.Contains(t, err.Error(), "error calling ioctl on block dev") + + // On regular files, ioctl with BLKZEROOUT should fail with ENOTTY (inappropriate ioctl for device) or EINVAL + isENOTTY := errors.Is(err, syscall.ENOTTY) + isEINVAL := errors.Is(err, syscall.EINVAL) + assert.True(t, isENOTTY || isEINVAL, "expected error to be ENOTTY or EINVAL, got: %v", err) + }) +} diff --git a/pkg/uploader/block/dev_other.go b/pkg/uploader/block/dev_other.go index c8a55cab2..5a3516d50 100644 --- a/pkg/uploader/block/dev_other.go +++ b/pkg/uploader/block/dev_other.go @@ -27,3 +27,7 @@ import ( func openBlockDevice(_ string, _ bool) (*os.File, error) { return nil, fmt.Errorf("block mode is not supported for non-linux platforms") } + +func blkZeroOut(_ *os.File, _ int64, _ int64) error { + return fmt.Errorf("block mode is not supported for non-linux platforms") +}