devmapper

package module
v0.0.0-...-c47dfb6 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 15, 2026 License: MIT Imports: 13 Imported by: 7

README

devmapper.go

A pure-Go library for Linux device mapper target management.

What is device mapper?

Device mapper is a Linux kernel framework that provides a way to create virtual block devices by mapping them onto one or more underlying block devices. It lives in the kernel and is controlled from userspace via the /dev/mapper/control ioctl interface. The dmsetup command-line tool is the most common way to interact with it, but devmapper.go lets you do the same directly from Go without shelling out.

Device mapper works by defining tables that describe how the virtual device's sectors map to underlying storage. Each table specifies a target type (e.g. linear, crypt, striped) that determines how I/O is transformed. Multiple tables can be stacked to build complex storage configurations — for example, encrypting data on top of a striped RAID set.

Device mapper is the foundation behind many Linux storage tools:

  • LVM2 — logical volume management
  • LUKS/cryptsetup — full-disk encryption
  • dm-verity — read-only integrity verification (used by Android and Chrome OS)
  • Docker/containerd — thin provisioning for container images
  • multipath-tools — SAN multipath I/O

Installation

go get github.com/anatol/devmapper.go

Supported targets

Target Go type Description
linear LinearTable Maps a range of sectors directly to another block device
striped StripeTable Stripes I/O across multiple devices (RAID-0)
mirror MirrorTable Mirrors I/O across multiple devices (RAID-1)
raid RaidTable Various RAID levels using the md framework (raid0, raid1, raid4, raid5, raid6, raid10)
crypt CryptTable Encrypts data using the kernel crypto API (used by LUKS/cryptsetup)
verity VerityTable Read-only integrity verification using a hash tree
integrity IntegrityTable Block-level data integrity protection (dm-integrity)
snapshot SnapshotTable Copy-on-write snapshots of a block device
snapshot-origin SnapshotOriginTable Marks a device as the origin for snapshots
snapshot-merge SnapshotMergeTable Merges a snapshot back into its origin
thin-pool ThinPoolTable Manages a pool of blocks shared between thin devices
thin ThinTable Thin-provisioned device backed by a thin-pool
cache CacheTable Uses a fast device (e.g. SSD) to cache a slower origin device
writecache WritecacheTable Caches writes on a fast device, flushes later to slower storage
era EraTable Tracks which blocks were written within a given period
delay DelayTable Delays I/O for testing purposes
flakey FlakeyTable Simulates unreliable device (periodic I/O errors) for testing
dust DustTable Simulates failing sectors for testing
log-writes LogWritesTable Logs all write operations to a separate device for crash testing
switch SwitchTable Switches between multiple backend devices per region
unstriped UnstripedTable Extracts a single stripe from a striped device
multipath MultipathTable I/O failover and load balancing across multiple paths
error ErrorTable Always fails I/O with EIO (useful for testing)
zero ZeroTable Returns zeros on read, discards writes (like /dev/zero)

Examples

Create an encrypted device (dm-crypt)
name := "mycrypt"
uuid := "2f144136-b0de-4b51-b2eb-bd869cc39a6e"
key := make([]byte, 32)

c := devmapper.CryptTable{
    Length:        60000 * devmapper.SectorSize,
    Encryption:    "aes-xts-plain64",
    Key:           key,
    BackendDevice: "/dev/loop0",
    Flags:         []string{devmapper.CryptFlagAllowDiscards},
}
if err := devmapper.CreateAndLoad(name, uuid, 0, c); err != nil {
    log.Fatal(err)
}
defer devmapper.Remove(name)

// /dev/mapper/mycrypt is now available
Create an encrypted device using Linux kernel keyring
// Load key into kernel keyring
keyname := fmt.Sprintf("cryptsetup:%s-d%d", uuid, luksDigestId)
kid, err := unix.AddKey("logon", keyname, key, unix.KEY_SPEC_THREAD_KEYRING)
if err != nil {
    log.Fatal(err)
}
keyid := fmt.Sprintf(":%v:logon:%v", len(key), keyname)

c := devmapper.CryptTable{
    Length:        60000 * devmapper.SectorSize,
    Encryption:    "aes-xts-plain64",
    KeyID:         keyid,
    BackendDevice: "/dev/loop0",
    Flags:         []string{devmapper.CryptFlagAllowDiscards},
}
if err := devmapper.CreateAndLoad(name, uuid, 0, c); err != nil {
    log.Fatal(err)
}
defer devmapper.Remove(name)
Create a linear mapping
l := devmapper.LinearTable{
    Length:        15 * devmapper.SectorSize,
    BackendDevice: "/dev/loop0",
}
if err := devmapper.CreateAndLoad("mylinear", "some-uuid", 0, l); err != nil {
    log.Fatal(err)
}
defer devmapper.Remove("mylinear")
Create a striped (RAID-0) device
s := devmapper.StripeTable{
    Length:    totalSize,
    ChunkSize: 64 * 1024, // 64 KiB chunks
    Devices: []devmapper.StripeDevice{
        {Device: "/dev/loop0"},
        {Device: "/dev/loop1"},
        {Device: "/dev/loop2"},
    },
}
if err := devmapper.CreateAndLoad("mystripe", "some-uuid", 0, s); err != nil {
    log.Fatal(err)
}
defer devmapper.Remove("mystripe")
Create a read-only verity device
v := devmapper.VerityTable{
    Length:        dataSize,
    HashType:      1,
    DataDevice:    "/dev/loop0",
    HashDevice:    "/dev/loop1",
    DataBlockSize: 4096,
    HashBlockSize: 4096,
    NumDataBlocks: 512,
    Algorithm:     "sha256",
    Salt:          salt,
    Digest:        rootHash,
}
if err := devmapper.CreateAndLoad("myverity", "some-uuid", devmapper.ReadOnlyFlag, v); err != nil {
    log.Fatal(err)
}
defer devmapper.Remove("myverity")
Query device mapper state
// List all device mapper devices
items, err := devmapper.List()

// Get device info
info, err := devmapper.InfoByName("mydevice")
fmt.Printf("Name: %s, UUID: %s, Open count: %d\n", info.Name, info.UUID, info.OpenCount)

// Get kernel device mapper version
major, minor, patch, err := devmapper.GetVersion()

Userspace volumes

In addition to creating kernel device mapper targets, devmapper.go supports userspace volumes — a way to read and write data using the same table definitions but without involving the kernel device mapper framework. Instead, all data processing (e.g. encryption, striping) happens in Go userspace.

This is useful for:

  • Reading/writing encrypted or striped data on systems where you cannot load device mapper kernel modules (e.g. containers, CI)
  • Unit testing without root privileges or device mapper setup
  • Offline processing of disk images
c := devmapper.CryptTable{
    Length:        size,
    Encryption:    "aes-xts-plain64",
    Key:           key,
    BackendDevice: "/path/to/encrypted.img",
}

vol, err := devmapper.OpenUserspaceVolume(os.O_RDWR, 0, c)
if err != nil {
    log.Fatal(err)
}
defer vol.Close()

// Read decrypted data
buf := make([]byte, 4096)
_, err = vol.ReadAt(buf, 0)

// Write data (gets encrypted transparently)
_, err = vol.WriteAt(plaintext, 0)

Userspace volumes implement the io.ReaderAt, io.WriterAt, and io.Closer interfaces. Reads and writes must be aligned to devmapper.SectorSize (512 bytes). Multiple tables can be passed to OpenUserspaceVolume to handle devices with multiple table regions.

The following targets have userspace volume support: linear, striped, mirror, crypt, zero, error, delay, flakey, dust, log-writes, unstriped, multipath.

License

See LICENSE.

Documentation

Index

Constants

View Source
const (

	// CryptFlagAllowDiscards is an equivalent of 'allow_discards' crypt option
	CryptFlagAllowDiscards = "allow_discards"
	// CryptFlagSameCPUCrypt is an equivalent of 'same_cpu_crypt' crypt option
	CryptFlagSameCPUCrypt = "same_cpu_crypt"
	// CryptFlagSubmitFromCryptCPUs is an equivalent of 'submit_from_crypt_cpus' crypt option
	CryptFlagSubmitFromCryptCPUs = "submit_from_crypt_cpus"
	// CryptFlagNoReadWorkqueue is an equivalent of 'no_read_workqueue' crypt option
	CryptFlagNoReadWorkqueue = "no_read_workqueue"
	// CryptFlagNoWriteWorkqueue is an equivalent of 'no_write_workqueue' crypt option
	CryptFlagNoWriteWorkqueue = "no_write_workqueue"
)
View Source
const (
	// ReadOnlyFlag is a devmapper readonly flag value
	ReadOnlyFlag = unix.DM_READONLY_FLAG
)
View Source
const SectorSize = 512

SectorSize is a device size used for devmapper calculations. Currently this value hardcoded to 512.

Variables

This section is empty.

Functions

func Create

func Create(name string, uuid string) error

Create creates a new device. No table will be loaded. The device will be in suspended state. Any IO to this device will fail.

func CreateAndLoad

func CreateAndLoad(name string, uuid string, flags uint32, tables ...Table) error

CreateAndLoad creates, loads the provided tables and resumes the device.

func GetVersion

func GetVersion() (major, minor, patch uint32, err error)

GetVersion returns version for the dm-mapper kernel interface

func Load

func Load(name string, flags uint32, tables ...Table) error

Load loads given table into the device

func Message

func Message(name string, sector int, message string) error

Message passes a message string to the target at specific offset of a device.

func Remove

func Remove(name string) error

Remove removes the device and destroys its tables.

func Rename

func Rename(old, new string) error

Rename renames the device

func Resume

func Resume(name string) error

Resume resumes the given device.

func SetUUID

func SetUUID(name, uuid string) error

SetUUID sets uuid for a given device

func Suspend

func Suspend(name string) error

Suspend suspends the given device.

Types

type CacheTable

type CacheTable struct {
	Start          uint64
	Length         uint64
	MetadataDevice string
	CacheDevice    string
	OriginDevice   string
	BlockSize      uint64 // cache block size in bytes
	Features       []string
	Policy         string   // cache policy, e.g. "smq"
	PolicyArgs     []string // policy-specific arguments
}

CacheTable represents information needed for 'cache' target creation. It uses a fast device (e.g. SSD) to cache a slower origin device.

type CryptTable

type CryptTable struct {
	Start         uint64
	Length        uint64
	BackendDevice string // device that stores the encrypted data
	BackendOffset uint64
	Encryption    string
	Key           []byte
	KeyID         string // key id in the keystore e.g. ":32:logon:foobarkey"
	IVTweak       uint64
	Flags         []string // TODO: maybe convert it to bitflag instead?
	SectorSize    uint64   // size of the sector the crypto device operates with
}

CryptTable represents information needed for 'crypt' target creation

type DelayTable

type DelayTable struct {
	Start       uint64
	Length      uint64
	ReadDevice  string
	ReadOffset  uint64
	ReadDelay   uint64 // delay in milliseconds
	WriteDevice string // optional, defaults to ReadDevice
	WriteOffset uint64
	WriteDelay  uint64 // delay in milliseconds
	FlushDevice string // optional, defaults to WriteDevice
	FlushOffset uint64
	FlushDelay  uint64 // delay in milliseconds
}

DelayTable represents information needed for 'delay' target creation. It delays I/O for testing purposes.

type DeviceInfo

type DeviceInfo struct {
	Name       string
	UUID       string
	DevNo      uint64
	OpenCount  int32
	TargetsNum uint32
	Flags      uint32 // combination of unix.DM_*_FLAG
}

DeviceInfo is a type that holds devmapper device information

func InfoByDevno

func InfoByDevno(devno uint64) (*DeviceInfo, error)

InfoByDevno returns device mapper information by its block device number (major/minor)

func InfoByName

func InfoByName(name string) (*DeviceInfo, error)

InfoByName returns device information by its name

type DustTable

type DustTable struct {
	Start     uint64
	Length    uint64
	Device    string
	Offset    uint64
	BlockSize uint64 // block size in bytes
}

DustTable represents information needed for 'dust' target creation. It simulates failing sectors on a device for testing purposes.

type EraTable

type EraTable struct {
	Start          uint64
	Length         uint64
	MetadataDevice string
	OriginDevice   string
	BlockSize      uint64 // block size in bytes
}

EraTable represents information needed for 'era' target creation. It tracks which blocks were written within a given period (era).

type ErrorTable

type ErrorTable struct {
	Start  uint64
	Length uint64
}

ErrorTable represents information needed for 'error' target creation. Any I/O to this target always fails with EIO.

type FlakeyTable

type FlakeyTable struct {
	Start        uint64
	Length       uint64
	Device       string
	Offset       uint64
	UpInterval   uint64 // number of seconds device is available
	DownInterval uint64 // number of seconds device returns errors
	Features     []string
}

FlakeyTable represents information needed for 'flakey' target creation. It creates an unreliable device for testing purposes, periodically returning errors for all I/O.

type IntegrityTable

type IntegrityTable struct {
	Start    uint64
	Length   uint64
	Device   string
	Offset   uint64
	TagSize  uint64
	Mode     string // "J" (journal), "B" (bitmap), "D" (direct)
	Features []string
}

IntegrityTable represents information needed for 'integrity' target creation. It provides block-level data integrity protection (dm-integrity).

type LinearTable

type LinearTable struct {
	Start         uint64
	Length        uint64
	BackendDevice string
	BackendOffset uint64
}

LinearTable represents information needed for 'linear' target creation

type ListItem

type ListItem struct {
	DevNo uint64
	Name  string
}

ListItem represents information about a dmsetup device

func List

func List() ([]ListItem, error)

List provides a list of dmsetup devices

type LogWritesTable

type LogWritesTable struct {
	Start     uint64
	Length    uint64
	Device    string
	LogDevice string
}

LogWritesTable represents information needed for 'log-writes' target creation. It logs all write operations to a separate device for crash consistency testing.

type MirrorDevice

type MirrorDevice struct {
	Device string
	Offset uint64
}

MirrorDevice represents one device in a mirror set

type MirrorTable

type MirrorTable struct {
	Start    uint64
	Length   uint64
	LogType  string   // e.g. "core" or "disk"
	LogArgs  []string // arguments for the log type
	Devices  []MirrorDevice
	Features []string // optional features like "handle_errors"
}

MirrorTable represents information needed for 'mirror' target creation. It mirrors I/O across multiple devices (RAID-1).

type MultipathGroup

type MultipathGroup struct {
	PathSelector string // e.g. "round-robin"
	SelectorArgs []string
	Paths        []MultipathPath
}

MultipathGroup represents a path group in a multipath target

type MultipathPath

type MultipathPath struct {
	Device string
	Args   []string // per-path selector args
}

MultipathPath represents a single path in a multipath path group

type MultipathTable

type MultipathTable struct {
	Start      uint64
	Length     uint64
	Features   []string
	HWHandler  string // hardware handler, e.g. "0" for none
	HWArgs     []string
	PathGroups []MultipathGroup
}

MultipathTable represents information needed for 'multipath' target creation. It provides I/O failover and load balancing across multiple paths.

type RaidDevice

type RaidDevice struct {
	MetaDevice string // metadata device, or "-" if not used
	DataDevice string
}

RaidDevice represents one device pair in a RAID set (metadata + data)

type RaidTable

type RaidTable struct {
	Start    uint64
	Length   uint64
	RaidType string // e.g. "raid0", "raid1", "raid4", "raid5_ls", "raid6_zr", "raid10"
	Params   []string
	Devices  []RaidDevice
}

RaidTable represents information needed for 'raid' target creation. It implements various RAID levels using the md (multiple devices) framework.

type SnapshotMergeTable

type SnapshotMergeTable struct {
	Start        uint64
	Length       uint64
	OriginDevice string
	COWDevice    string
	Persistent   bool
	ChunkSize    uint64 // chunk size in bytes
}

SnapshotMergeTable represents information needed for 'snapshot-merge' target creation. It merges a snapshot back into its origin.

type SnapshotOriginTable

type SnapshotOriginTable struct {
	Start        uint64
	Length       uint64
	OriginDevice string
}

SnapshotOriginTable represents information needed for 'snapshot-origin' target creation. It marks a device as the origin for snapshots.

type SnapshotTable

type SnapshotTable struct {
	Start        uint64
	Length       uint64
	OriginDevice string
	COWDevice    string // copy-on-write device
	Persistent   bool   // persistent (P) or transient (N)
	ChunkSize    uint64 // chunk size in bytes
}

SnapshotTable represents information needed for 'snapshot' target creation. It creates a copy-on-write snapshot of the origin device.

type StripeDevice

type StripeDevice struct {
	Device string
	Offset uint64
}

StripeDevice represents one device in a stripe set

type StripeTable

type StripeTable struct {
	Start     uint64
	Length    uint64
	ChunkSize uint64 // chunk size in bytes
	Devices   []StripeDevice
}

StripeTable represents information needed for 'striped' target creation. It stripes I/O across multiple devices (RAID-0).

type SwitchDevice

type SwitchDevice struct {
	Device string
	Offset uint64
}

SwitchDevice represents one path in a switch target

type SwitchTable

type SwitchTable struct {
	Start      uint64
	Length     uint64
	RegionSize uint64 // region size in bytes
	Devices    []SwitchDevice
}

SwitchTable represents information needed for 'switch' target creation. It allows switching between multiple backend devices on a per-region basis.

type Table

type Table interface {
	// contains filtered or unexported methods
}

Table is a type to represent different devmapper targets like 'zero', 'crypt', ...

type ThinPoolTable

type ThinPoolTable struct {
	Start          uint64
	Length         uint64
	MetadataDevice string
	DataDevice     string
	DataBlockSize  uint64 // block size in bytes
	LowWaterMark   uint64 // free space threshold in blocks
	Features       []string
}

ThinPoolTable represents information needed for 'thin-pool' target creation. It manages a pool of blocks shared between thin devices.

type ThinTable

type ThinTable struct {
	Start          uint64
	Length         uint64
	PoolDevice     string
	DeviceID       uint64
	ExternalOrigin string // optional external origin device
}

ThinTable represents information needed for 'thin' target creation. It creates a thin provisioned device backed by a thin-pool.

type UnstripedTable

type UnstripedTable struct {
	Start      uint64
	Length     uint64
	NumStripes uint64
	ChunkSize  uint64 // chunk size in bytes
	StripeNum  uint64 // which stripe to extract (0-indexed)
	Device     string
	Offset     uint64
}

UnstripedTable represents information needed for 'unstriped' target creation. It extracts a single stripe from a striped device.

type VerityTable

type VerityTable struct {
	Start                         uint64
	Length                        uint64
	HashType                      uint64
	DataDevice                    string // the device containing data, the integrity of which needs to be checked
	HashDevice                    string // device that supplies the hash tree data
	DataBlockSize, HashBlockSize  uint64
	NumDataBlocks, HashStartBlock uint64
	Algorithm, Digest, Salt       string
	Params                        []string
}

VerityTable represents information needed for 'verity' target creation

type Volume

type Volume interface {
	io.ReaderAt
	io.WriterAt
	io.Closer
}

Volume represents reader/writer for the data handled by the device mapper table. Due to constrains the reader and writer operate at SectorSize size. Some targets may put further restrictions on the buffer alignment, e.g. CryptTarget operates at CryptTarget.SectorSize instead

func OpenUserspaceVolume

func OpenUserspaceVolume(flag int, perm fs.FileMode, tables ...Table) (Volume, error)

OpenUserspaceVolume opens a volume that allows to read/write data. It performs the data processing at user-space level without using device-mapper kernel framework. flag and perm parameters are applied to os.OpenFile() when opening the underlying files

type WritecacheTable

type WritecacheTable struct {
	Start       uint64
	Length      uint64
	Type        uint64 // 0 for pmem (DAX), 1 for SSD (block device)
	DataDevice  string
	CacheDevice string
	BlockSize   uint64 // block size in bytes
	Features    []string
}

WritecacheTable represents information needed for 'writecache' target creation. It caches writes on a fast device and later flushes them to the slower origin device.

type ZeroTable

type ZeroTable struct {
	Start  uint64
	Length uint64
}

ZeroTable represents information needed for 'zero' target creation

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL