diff --git a/snapshots/devmapper/dmsetup/dmsetup.go b/snapshots/devmapper/dmsetup/dmsetup.go index 90021a79c..0b75cda3f 100644 --- a/snapshots/devmapper/dmsetup/dmsetup.go +++ b/snapshots/devmapper/dmsetup/dmsetup.go @@ -263,6 +263,53 @@ func Version() (string, error) { return dmsetup("version") } +// DeviceStatus represents devmapper device status information +type DeviceStatus struct { + Offset int64 + Length int64 + Target string + Params []string +} + +// Status provides status information for devmapper device +func Status(deviceName string) (*DeviceStatus, error) { + var ( + err error + status DeviceStatus + ) + + output, err := dmsetup("status", deviceName) + if err != nil { + return nil, err + } + + // Status output format: + // Offset (int64) + // Length (int64) + // Target type (string) + // Params (Array of strings) + const MinParseCount = 4 + parts := strings.Split(output, " ") + if len(parts) < MinParseCount { + return nil, errors.Errorf("failed to parse output: %q", output) + } + + status.Offset, err = strconv.ParseInt(parts[0], 10, 64) + if err != nil { + return nil, errors.Wrapf(err, "failed to parse offset: %q", parts[0]) + } + + status.Length, err = strconv.ParseInt(parts[1], 10, 64) + if err != nil { + return nil, errors.Wrapf(err, "failed to parse length: %q", parts[1]) + } + + status.Target = parts[2] + status.Params = parts[3:] + + return &status, nil +} + // GetFullDevicePath returns full path for the given device name (like "/dev/mapper/name") func GetFullDevicePath(deviceName string) string { if strings.HasPrefix(deviceName, DevMapperDir) { diff --git a/snapshots/devmapper/dmsetup/dmsetup_test.go b/snapshots/devmapper/dmsetup/dmsetup_test.go index 14212595f..97fdeb9b1 100644 --- a/snapshots/devmapper/dmsetup/dmsetup_test.go +++ b/snapshots/devmapper/dmsetup/dmsetup_test.go @@ -24,12 +24,13 @@ import ( "strings" "testing" - "github.com/containerd/containerd/pkg/testutil" - "github.com/containerd/containerd/snapshots/devmapper/losetup" "github.com/docker/go-units" "golang.org/x/sys/unix" "gotest.tools/assert" is "gotest.tools/assert/cmp" + + "github.com/containerd/containerd/pkg/testutil" + "github.com/containerd/containerd/snapshots/devmapper/losetup" ) const ( @@ -83,6 +84,7 @@ func TestDMSetup(t *testing.T) { t.Run("DeleteSnapshot", testDeleteSnapshot) t.Run("ActivateDevice", testActivateDevice) + t.Run("DeviceStatus", testDeviceStatus) t.Run("SuspendResumeDevice", testSuspendResumeDevice) t.Run("RemoveDevice", testRemoveDevice) @@ -139,6 +141,16 @@ func testActivateDevice(t *testing.T) { assert.Assert(t, info.TableLive) } +func testDeviceStatus(t *testing.T) { + status, err := Status(testDeviceName) + assert.NilError(t, err) + + assert.Equal(t, int64(0), status.Offset) + assert.Equal(t, int64(2), status.Length) + assert.Equal(t, "thin", status.Target) + assert.DeepEqual(t, status.Params, []string{"0", "-"}) +} + func testSuspendResumeDevice(t *testing.T) { err := SuspendDevice(testDeviceName) assert.NilError(t, err)