devicemapper: split out devicemapper bindings
This is a first pass at splitting out devicemapper into separate, usable bindings. Signed-off-by: Vincent Batts <vbatts@redhat.com>
This commit is contained in:
parent
bf0a0e9ca7
commit
a5ad3ec0b7
5 changed files with 1158 additions and 0 deletions
129
devicemapper/attach_loopback.go
Normal file
129
devicemapper/attach_loopback.go
Normal file
|
@ -0,0 +1,129 @@
|
||||||
|
// +build linux
|
||||||
|
|
||||||
|
package devicemapper
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
log "github.com/Sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
func stringToLoopName(src string) [LoNameSize]uint8 {
|
||||||
|
var dst [LoNameSize]uint8
|
||||||
|
copy(dst[:], src[:])
|
||||||
|
return dst
|
||||||
|
}
|
||||||
|
|
||||||
|
func getNextFreeLoopbackIndex() (int, error) {
|
||||||
|
f, err := os.OpenFile("/dev/loop-control", os.O_RDONLY, 0644)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
index, err := ioctlLoopCtlGetFree(f.Fd())
|
||||||
|
if index < 0 {
|
||||||
|
index = 0
|
||||||
|
}
|
||||||
|
return index, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func openNextAvailableLoopback(index int, sparseFile *os.File) (loopFile *os.File, err error) {
|
||||||
|
// Start looking for a free /dev/loop
|
||||||
|
for {
|
||||||
|
target := fmt.Sprintf("/dev/loop%d", index)
|
||||||
|
index++
|
||||||
|
|
||||||
|
fi, err := os.Stat(target)
|
||||||
|
if err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
log.Errorf("There are no more loopback devices available.")
|
||||||
|
}
|
||||||
|
return nil, ErrAttachLoopbackDevice
|
||||||
|
}
|
||||||
|
|
||||||
|
if fi.Mode()&os.ModeDevice != os.ModeDevice {
|
||||||
|
log.Errorf("Loopback device %s is not a block device.", target)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// OpenFile adds O_CLOEXEC
|
||||||
|
loopFile, err = os.OpenFile(target, os.O_RDWR, 0644)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("Error opening loopback device: %s", err)
|
||||||
|
return nil, ErrAttachLoopbackDevice
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to attach to the loop file
|
||||||
|
if err := ioctlLoopSetFd(loopFile.Fd(), sparseFile.Fd()); err != nil {
|
||||||
|
loopFile.Close()
|
||||||
|
|
||||||
|
// If the error is EBUSY, then try the next loopback
|
||||||
|
if err != syscall.EBUSY {
|
||||||
|
log.Errorf("Cannot set up loopback device %s: %s", target, err)
|
||||||
|
return nil, ErrAttachLoopbackDevice
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, we keep going with the loop
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// In case of success, we finished. Break the loop.
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// This can't happen, but let's be sure
|
||||||
|
if loopFile == nil {
|
||||||
|
log.Errorf("Unreachable code reached! Error attaching %s to a loopback device.", sparseFile.Name())
|
||||||
|
return nil, ErrAttachLoopbackDevice
|
||||||
|
}
|
||||||
|
|
||||||
|
return loopFile, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// attachLoopDevice attaches the given sparse file to the next
|
||||||
|
// available loopback device. It returns an opened *os.File.
|
||||||
|
func AttachLoopDevice(sparseName string) (loop *os.File, err error) {
|
||||||
|
|
||||||
|
// Try to retrieve the next available loopback device via syscall.
|
||||||
|
// If it fails, we discard error and start loopking for a
|
||||||
|
// loopback from index 0.
|
||||||
|
startIndex, err := getNextFreeLoopbackIndex()
|
||||||
|
if err != nil {
|
||||||
|
log.Debugf("Error retrieving the next available loopback: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// OpenFile adds O_CLOEXEC
|
||||||
|
sparseFile, err := os.OpenFile(sparseName, os.O_RDWR, 0644)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("Error opening sparse file %s: %s", sparseName, err)
|
||||||
|
return nil, ErrAttachLoopbackDevice
|
||||||
|
}
|
||||||
|
defer sparseFile.Close()
|
||||||
|
|
||||||
|
loopFile, err := openNextAvailableLoopback(startIndex, sparseFile)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the status of the loopback device
|
||||||
|
loopInfo := &LoopInfo64{
|
||||||
|
loFileName: stringToLoopName(loopFile.Name()),
|
||||||
|
loOffset: 0,
|
||||||
|
loFlags: LoFlagsAutoClear,
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := ioctlLoopSetStatus64(loopFile.Fd(), loopInfo); err != nil {
|
||||||
|
log.Errorf("Cannot set up loopback device info: %s", err)
|
||||||
|
|
||||||
|
// If the call failed, then free the loopback device
|
||||||
|
if err := ioctlLoopClrFd(loopFile.Fd()); err != nil {
|
||||||
|
log.Errorf("Error while cleaning up the loopback device")
|
||||||
|
}
|
||||||
|
loopFile.Close()
|
||||||
|
return nil, ErrAttachLoopbackDevice
|
||||||
|
}
|
||||||
|
|
||||||
|
return loopFile, nil
|
||||||
|
}
|
673
devicemapper/devmapper.go
Normal file
673
devicemapper/devmapper.go
Normal file
|
@ -0,0 +1,673 @@
|
||||||
|
// +build linux
|
||||||
|
|
||||||
|
package devicemapper
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"runtime"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
log "github.com/Sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DevmapperLogger interface {
|
||||||
|
DMLog(level int, file string, line int, dmError int, message string)
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
DeviceCreate TaskType = iota
|
||||||
|
DeviceReload
|
||||||
|
DeviceRemove
|
||||||
|
DeviceRemoveAll
|
||||||
|
DeviceSuspend
|
||||||
|
DeviceResume
|
||||||
|
DeviceInfo
|
||||||
|
DeviceDeps
|
||||||
|
DeviceRename
|
||||||
|
DeviceVersion
|
||||||
|
DeviceStatus
|
||||||
|
DeviceTable
|
||||||
|
DeviceWaitevent
|
||||||
|
DeviceList
|
||||||
|
DeviceClear
|
||||||
|
DeviceMknodes
|
||||||
|
DeviceListVersions
|
||||||
|
DeviceTargetMsg
|
||||||
|
DeviceSetGeometry
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
AddNodeOnResume AddNodeType = iota
|
||||||
|
AddNodeOnCreate
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrTaskRun = errors.New("dm_task_run failed")
|
||||||
|
ErrTaskSetName = errors.New("dm_task_set_name failed")
|
||||||
|
ErrTaskSetMessage = errors.New("dm_task_set_message failed")
|
||||||
|
ErrTaskSetAddNode = errors.New("dm_task_set_add_node failed")
|
||||||
|
ErrTaskSetRo = errors.New("dm_task_set_ro failed")
|
||||||
|
ErrTaskAddTarget = errors.New("dm_task_add_target failed")
|
||||||
|
ErrTaskSetSector = errors.New("dm_task_set_sector failed")
|
||||||
|
ErrTaskGetDeps = errors.New("dm_task_get_deps failed")
|
||||||
|
ErrTaskGetInfo = errors.New("dm_task_get_info failed")
|
||||||
|
ErrTaskGetDriverVersion = errors.New("dm_task_get_driver_version failed")
|
||||||
|
ErrTaskSetCookie = errors.New("dm_task_set_cookie failed")
|
||||||
|
ErrNilCookie = errors.New("cookie ptr can't be nil")
|
||||||
|
ErrAttachLoopbackDevice = errors.New("loopback mounting failed")
|
||||||
|
ErrGetBlockSize = errors.New("Can't get block size")
|
||||||
|
ErrUdevWait = errors.New("wait on udev cookie failed")
|
||||||
|
ErrSetDevDir = errors.New("dm_set_dev_dir failed")
|
||||||
|
ErrGetLibraryVersion = errors.New("dm_get_library_version failed")
|
||||||
|
ErrCreateRemoveTask = errors.New("Can't create task of type DeviceRemove")
|
||||||
|
ErrRunRemoveDevice = errors.New("running removeDevice failed")
|
||||||
|
ErrInvalidAddNode = errors.New("Invalide AddNoce type")
|
||||||
|
ErrGetLoopbackBackingFile = errors.New("Unable to get loopback backing file")
|
||||||
|
ErrLoopbackSetCapacity = errors.New("Unable set loopback capacity")
|
||||||
|
ErrBusy = errors.New("Device is Busy")
|
||||||
|
|
||||||
|
dmSawBusy bool
|
||||||
|
dmSawExist bool
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
Task struct {
|
||||||
|
unmanaged *CDmTask
|
||||||
|
}
|
||||||
|
Deps struct {
|
||||||
|
Count uint32
|
||||||
|
Filler uint32
|
||||||
|
Device []uint64
|
||||||
|
}
|
||||||
|
Info struct {
|
||||||
|
Exists int
|
||||||
|
Suspended int
|
||||||
|
LiveTable int
|
||||||
|
InactiveTable int
|
||||||
|
OpenCount int32
|
||||||
|
EventNr uint32
|
||||||
|
Major uint32
|
||||||
|
Minor uint32
|
||||||
|
ReadOnly int
|
||||||
|
TargetCount int32
|
||||||
|
}
|
||||||
|
TaskType int
|
||||||
|
AddNodeType int
|
||||||
|
)
|
||||||
|
|
||||||
|
func (t *Task) destroy() {
|
||||||
|
if t != nil {
|
||||||
|
DmTaskDestroy(t.unmanaged)
|
||||||
|
runtime.SetFinalizer(t, nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TaskCreate(tasktype TaskType) *Task {
|
||||||
|
Ctask := DmTaskCreate(int(tasktype))
|
||||||
|
if Ctask == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
task := &Task{unmanaged: Ctask}
|
||||||
|
runtime.SetFinalizer(task, (*Task).destroy)
|
||||||
|
return task
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Task) Run() error {
|
||||||
|
if res := DmTaskRun(t.unmanaged); res != 1 {
|
||||||
|
return ErrTaskRun
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Task) SetName(name string) error {
|
||||||
|
if res := DmTaskSetName(t.unmanaged, name); res != 1 {
|
||||||
|
return ErrTaskSetName
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Task) SetMessage(message string) error {
|
||||||
|
if res := DmTaskSetMessage(t.unmanaged, message); res != 1 {
|
||||||
|
return ErrTaskSetMessage
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Task) SetSector(sector uint64) error {
|
||||||
|
if res := DmTaskSetSector(t.unmanaged, sector); res != 1 {
|
||||||
|
return ErrTaskSetSector
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Task) SetCookie(cookie *uint, flags uint16) error {
|
||||||
|
if cookie == nil {
|
||||||
|
return ErrNilCookie
|
||||||
|
}
|
||||||
|
if res := DmTaskSetCookie(t.unmanaged, cookie, flags); res != 1 {
|
||||||
|
return ErrTaskSetCookie
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Task) SetAddNode(addNode AddNodeType) error {
|
||||||
|
if addNode != AddNodeOnResume && addNode != AddNodeOnCreate {
|
||||||
|
return ErrInvalidAddNode
|
||||||
|
}
|
||||||
|
if res := DmTaskSetAddNode(t.unmanaged, addNode); res != 1 {
|
||||||
|
return ErrTaskSetAddNode
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Task) SetRo() error {
|
||||||
|
if res := DmTaskSetRo(t.unmanaged); res != 1 {
|
||||||
|
return ErrTaskSetRo
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Task) AddTarget(start, size uint64, ttype, params string) error {
|
||||||
|
if res := DmTaskAddTarget(t.unmanaged, start, size,
|
||||||
|
ttype, params); res != 1 {
|
||||||
|
return ErrTaskAddTarget
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Task) GetDeps() (*Deps, error) {
|
||||||
|
var deps *Deps
|
||||||
|
if deps = DmTaskGetDeps(t.unmanaged); deps == nil {
|
||||||
|
return nil, ErrTaskGetDeps
|
||||||
|
}
|
||||||
|
return deps, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Task) GetInfo() (*Info, error) {
|
||||||
|
info := &Info{}
|
||||||
|
if res := DmTaskGetInfo(t.unmanaged, info); res != 1 {
|
||||||
|
return nil, ErrTaskGetInfo
|
||||||
|
}
|
||||||
|
return info, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Task) GetDriverVersion() (string, error) {
|
||||||
|
res := DmTaskGetDriverVersion(t.unmanaged)
|
||||||
|
if res == "" {
|
||||||
|
return "", ErrTaskGetDriverVersion
|
||||||
|
}
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Task) GetNextTarget(next uintptr) (nextPtr uintptr, start uint64,
|
||||||
|
length uint64, targetType string, params string) {
|
||||||
|
|
||||||
|
return DmGetNextTarget(t.unmanaged, next, &start, &length,
|
||||||
|
&targetType, ¶ms),
|
||||||
|
start, length, targetType, params
|
||||||
|
}
|
||||||
|
|
||||||
|
func getLoopbackBackingFile(file *os.File) (uint64, uint64, error) {
|
||||||
|
loopInfo, err := ioctlLoopGetStatus64(file.Fd())
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("Error get loopback backing file: %s", err)
|
||||||
|
return 0, 0, ErrGetLoopbackBackingFile
|
||||||
|
}
|
||||||
|
return loopInfo.loDevice, loopInfo.loInode, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func LoopbackSetCapacity(file *os.File) error {
|
||||||
|
if err := ioctlLoopSetCapacity(file.Fd(), 0); err != nil {
|
||||||
|
log.Errorf("Error loopbackSetCapacity: %s", err)
|
||||||
|
return ErrLoopbackSetCapacity
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func FindLoopDeviceFor(file *os.File) *os.File {
|
||||||
|
stat, err := file.Stat()
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
targetInode := stat.Sys().(*syscall.Stat_t).Ino
|
||||||
|
targetDevice := stat.Sys().(*syscall.Stat_t).Dev
|
||||||
|
|
||||||
|
for i := 0; true; i++ {
|
||||||
|
path := fmt.Sprintf("/dev/loop%d", i)
|
||||||
|
|
||||||
|
file, err := os.OpenFile(path, os.O_RDWR, 0)
|
||||||
|
if err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ignore all errors until the first not-exist
|
||||||
|
// we want to continue looking for the file
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
dev, inode, err := getLoopbackBackingFile(file)
|
||||||
|
if err == nil && dev == targetDevice && inode == targetInode {
|
||||||
|
return file
|
||||||
|
}
|
||||||
|
file.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func UdevWait(cookie uint) error {
|
||||||
|
if res := DmUdevWait(cookie); res != 1 {
|
||||||
|
log.Debugf("Failed to wait on udev cookie %d", cookie)
|
||||||
|
return ErrUdevWait
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func LogInitVerbose(level int) {
|
||||||
|
DmLogInitVerbose(level)
|
||||||
|
}
|
||||||
|
|
||||||
|
var dmLogger DevmapperLogger = nil
|
||||||
|
|
||||||
|
// initialize the logger for the device mapper library
|
||||||
|
func LogInit(logger DevmapperLogger) {
|
||||||
|
dmLogger = logger
|
||||||
|
LogWithErrnoInit()
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetDevDir(dir string) error {
|
||||||
|
if res := DmSetDevDir(dir); res != 1 {
|
||||||
|
log.Debugf("Error dm_set_dev_dir")
|
||||||
|
return ErrSetDevDir
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetLibraryVersion() (string, error) {
|
||||||
|
var version string
|
||||||
|
if res := DmGetLibraryVersion(&version); res != 1 {
|
||||||
|
return "", ErrGetLibraryVersion
|
||||||
|
}
|
||||||
|
return version, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Useful helper for cleanup
|
||||||
|
func RemoveDevice(name string) error {
|
||||||
|
// TODO(vbatts) just use the other removeDevice()
|
||||||
|
task := TaskCreate(DeviceRemove)
|
||||||
|
if task == nil {
|
||||||
|
return ErrCreateRemoveTask
|
||||||
|
}
|
||||||
|
if err := task.SetName(name); err != nil {
|
||||||
|
log.Debugf("Can't set task name %s", name)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := task.Run(); err != nil {
|
||||||
|
return ErrRunRemoveDevice
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetBlockDeviceSize(file *os.File) (uint64, error) {
|
||||||
|
size, err := ioctlBlkGetSize64(file.Fd())
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("Error getblockdevicesize: %s", err)
|
||||||
|
return 0, ErrGetBlockSize
|
||||||
|
}
|
||||||
|
return uint64(size), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func BlockDeviceDiscard(path string) error {
|
||||||
|
file, err := os.OpenFile(path, os.O_RDWR, 0)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
size, err := GetBlockDeviceSize(file)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := ioctlBlkDiscard(file.Fd(), 0, size); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Without this sometimes the remove of the device that happens after
|
||||||
|
// discard fails with EBUSY.
|
||||||
|
syscall.Sync()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is the programmatic example of "dmsetup create"
|
||||||
|
func CreatePool(poolName string, dataFile, metadataFile *os.File, poolBlockSize uint32) error {
|
||||||
|
task, err := createTask(DeviceCreate, poolName)
|
||||||
|
if task == nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
size, err := GetBlockDeviceSize(dataFile)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Can't get data size %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
params := fmt.Sprintf("%s %s %d 32768 1 skip_block_zeroing", metadataFile.Name(), dataFile.Name(), poolBlockSize)
|
||||||
|
if err := task.AddTarget(0, size/512, "thin-pool", params); err != nil {
|
||||||
|
return fmt.Errorf("Can't add target %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var cookie uint = 0
|
||||||
|
if err := task.SetCookie(&cookie, 0); err != nil {
|
||||||
|
return fmt.Errorf("Can't set cookie %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := task.Run(); err != nil {
|
||||||
|
return fmt.Errorf("Error running DeviceCreate (CreatePool) %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
UdevWait(cookie)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ReloadPool(poolName string, dataFile, metadataFile *os.File, poolBlockSize uint32) error {
|
||||||
|
task, err := createTask(DeviceReload, poolName)
|
||||||
|
if task == nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
size, err := GetBlockDeviceSize(dataFile)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Can't get data size %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
params := fmt.Sprintf("%s %s %d 32768 1 skip_block_zeroing", metadataFile.Name(), dataFile.Name(), poolBlockSize)
|
||||||
|
if err := task.AddTarget(0, size/512, "thin-pool", params); err != nil {
|
||||||
|
return fmt.Errorf("Can't add target %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := task.Run(); err != nil {
|
||||||
|
return fmt.Errorf("Error running DeviceCreate %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func createTask(t TaskType, name string) (*Task, error) {
|
||||||
|
task := TaskCreate(t)
|
||||||
|
if task == nil {
|
||||||
|
return nil, fmt.Errorf("Can't create task of type %d", int(t))
|
||||||
|
}
|
||||||
|
if err := task.SetName(name); err != nil {
|
||||||
|
return nil, fmt.Errorf("Can't set task name %s", name)
|
||||||
|
}
|
||||||
|
return task, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetDeps(name string) (*Deps, error) {
|
||||||
|
task, err := createTask(DeviceDeps, name)
|
||||||
|
if task == nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := task.Run(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return task.GetDeps()
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetInfo(name string) (*Info, error) {
|
||||||
|
task, err := createTask(DeviceInfo, name)
|
||||||
|
if task == nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := task.Run(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return task.GetInfo()
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetDriverVersion() (string, error) {
|
||||||
|
task := TaskCreate(DeviceVersion)
|
||||||
|
if task == nil {
|
||||||
|
return "", fmt.Errorf("Can't create DeviceVersion task")
|
||||||
|
}
|
||||||
|
if err := task.Run(); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return task.GetDriverVersion()
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetStatus(name string) (uint64, uint64, string, string, error) {
|
||||||
|
task, err := createTask(DeviceStatus, name)
|
||||||
|
if task == nil {
|
||||||
|
log.Debugf("GetStatus: Error createTask: %s", err)
|
||||||
|
return 0, 0, "", "", err
|
||||||
|
}
|
||||||
|
if err := task.Run(); err != nil {
|
||||||
|
log.Debugf("GetStatus: Error Run: %s", err)
|
||||||
|
return 0, 0, "", "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
devinfo, err := task.GetInfo()
|
||||||
|
if err != nil {
|
||||||
|
log.Debugf("GetStatus: Error GetInfo: %s", err)
|
||||||
|
return 0, 0, "", "", err
|
||||||
|
}
|
||||||
|
if devinfo.Exists == 0 {
|
||||||
|
log.Debugf("GetStatus: Non existing device %s", name)
|
||||||
|
return 0, 0, "", "", fmt.Errorf("Non existing device %s", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, start, length, targetType, params := task.GetNextTarget(0)
|
||||||
|
return start, length, targetType, params, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetTransactionId(poolName string, oldId uint64, newId uint64) error {
|
||||||
|
task, err := createTask(DeviceTargetMsg, poolName)
|
||||||
|
if task == nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := task.SetSector(0); err != nil {
|
||||||
|
return fmt.Errorf("Can't set sector %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := task.SetMessage(fmt.Sprintf("set_transaction_id %d %d", oldId, newId)); err != nil {
|
||||||
|
return fmt.Errorf("Can't set message %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := task.Run(); err != nil {
|
||||||
|
return fmt.Errorf("Error running SetTransactionId %s", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func SuspendDevice(name string) error {
|
||||||
|
task, err := createTask(DeviceSuspend, name)
|
||||||
|
if task == nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := task.Run(); err != nil {
|
||||||
|
return fmt.Errorf("Error running DeviceSuspend %s", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ResumeDevice(name string) error {
|
||||||
|
task, err := createTask(DeviceResume, name)
|
||||||
|
if task == nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var cookie uint = 0
|
||||||
|
if err := task.SetCookie(&cookie, 0); err != nil {
|
||||||
|
return fmt.Errorf("Can't set cookie %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := task.Run(); err != nil {
|
||||||
|
return fmt.Errorf("Error running DeviceResume %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
UdevWait(cookie)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreateDevice(poolName string, deviceId *int) error {
|
||||||
|
log.Debugf("[devmapper] CreateDevice(poolName=%v, deviceId=%v)", poolName, *deviceId)
|
||||||
|
|
||||||
|
for {
|
||||||
|
task, err := createTask(DeviceTargetMsg, poolName)
|
||||||
|
if task == nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := task.SetSector(0); err != nil {
|
||||||
|
return fmt.Errorf("Can't set sector %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := task.SetMessage(fmt.Sprintf("create_thin %d", *deviceId)); err != nil {
|
||||||
|
return fmt.Errorf("Can't set message %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
dmSawExist = false
|
||||||
|
if err := task.Run(); err != nil {
|
||||||
|
if dmSawExist {
|
||||||
|
// Already exists, try next id
|
||||||
|
*deviceId++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return fmt.Errorf("Error running CreateDevice %s", err)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func DeleteDevice(poolName string, deviceId int) error {
|
||||||
|
task, err := createTask(DeviceTargetMsg, poolName)
|
||||||
|
if task == nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := task.SetSector(0); err != nil {
|
||||||
|
return fmt.Errorf("Can't set sector %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := task.SetMessage(fmt.Sprintf("delete %d", deviceId)); err != nil {
|
||||||
|
return fmt.Errorf("Can't set message %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := task.Run(); err != nil {
|
||||||
|
return fmt.Errorf("Error running DeleteDevice %s", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func removeDevice(name string) error {
|
||||||
|
log.Debugf("[devmapper] RemoveDevice START")
|
||||||
|
defer log.Debugf("[devmapper] RemoveDevice END")
|
||||||
|
task, err := createTask(DeviceRemove, name)
|
||||||
|
if task == nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
dmSawBusy = false
|
||||||
|
if err = task.Run(); err != nil {
|
||||||
|
if dmSawBusy {
|
||||||
|
return ErrBusy
|
||||||
|
}
|
||||||
|
return fmt.Errorf("Error running RemoveDevice %s", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ActivateDevice(poolName string, name string, deviceId int, size uint64) error {
|
||||||
|
task, err := createTask(DeviceCreate, name)
|
||||||
|
if task == nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
params := fmt.Sprintf("%s %d", poolName, deviceId)
|
||||||
|
if err := task.AddTarget(0, size/512, "thin", params); err != nil {
|
||||||
|
return fmt.Errorf("Can't add target %s", err)
|
||||||
|
}
|
||||||
|
if err := task.SetAddNode(AddNodeOnCreate); err != nil {
|
||||||
|
return fmt.Errorf("Can't add node %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var cookie uint = 0
|
||||||
|
if err := task.SetCookie(&cookie, 0); err != nil {
|
||||||
|
return fmt.Errorf("Can't set cookie %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := task.Run(); err != nil {
|
||||||
|
return fmt.Errorf("Error running DeviceCreate (ActivateDevice) %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
UdevWait(cookie)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreateSnapDevice(poolName string, deviceId *int, baseName string, baseDeviceId int) error {
|
||||||
|
devinfo, _ := GetInfo(baseName)
|
||||||
|
doSuspend := devinfo != nil && devinfo.Exists != 0
|
||||||
|
|
||||||
|
if doSuspend {
|
||||||
|
if err := SuspendDevice(baseName); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
task, err := createTask(DeviceTargetMsg, poolName)
|
||||||
|
if task == nil {
|
||||||
|
if doSuspend {
|
||||||
|
ResumeDevice(baseName)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := task.SetSector(0); err != nil {
|
||||||
|
if doSuspend {
|
||||||
|
ResumeDevice(baseName)
|
||||||
|
}
|
||||||
|
return fmt.Errorf("Can't set sector %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := task.SetMessage(fmt.Sprintf("create_snap %d %d", *deviceId, baseDeviceId)); err != nil {
|
||||||
|
if doSuspend {
|
||||||
|
ResumeDevice(baseName)
|
||||||
|
}
|
||||||
|
return fmt.Errorf("Can't set message %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
dmSawExist = false
|
||||||
|
if err := task.Run(); err != nil {
|
||||||
|
if dmSawExist {
|
||||||
|
// Already exists, try next id
|
||||||
|
*deviceId++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if doSuspend {
|
||||||
|
ResumeDevice(baseName)
|
||||||
|
}
|
||||||
|
return fmt.Errorf("Error running DeviceCreate (createSnapDevice) %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if doSuspend {
|
||||||
|
if err := ResumeDevice(baseName); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
30
devicemapper/devmapper_log.go
Normal file
30
devicemapper/devmapper_log.go
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
// +build linux
|
||||||
|
|
||||||
|
package devicemapper
|
||||||
|
|
||||||
|
import "C"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Due to the way cgo works this has to be in a separate file, as devmapper.go has
|
||||||
|
// definitions in the cgo block, which is incompatible with using "//export"
|
||||||
|
|
||||||
|
//export DevmapperLogCallback
|
||||||
|
func DevmapperLogCallback(level C.int, file *C.char, line C.int, dm_errno_or_class C.int, message *C.char) {
|
||||||
|
msg := C.GoString(message)
|
||||||
|
if level < 7 {
|
||||||
|
if strings.Contains(msg, "busy") {
|
||||||
|
dmSawBusy = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.Contains(msg, "File exists") {
|
||||||
|
dmSawExist = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if dmLogger != nil {
|
||||||
|
dmLogger.DMLog(int(level), C.GoString(file), int(line), int(dm_errno_or_class), msg)
|
||||||
|
}
|
||||||
|
}
|
254
devicemapper/devmapper_wrapper.go
Normal file
254
devicemapper/devmapper_wrapper.go
Normal file
|
@ -0,0 +1,254 @@
|
||||||
|
// +build linux
|
||||||
|
|
||||||
|
package devicemapper
|
||||||
|
|
||||||
|
/*
|
||||||
|
#cgo LDFLAGS: -L. -ldevmapper
|
||||||
|
#include <libdevmapper.h>
|
||||||
|
#include <linux/loop.h> // FIXME: present only for defines, maybe we can remove it?
|
||||||
|
#include <linux/fs.h> // FIXME: present only for BLKGETSIZE64, maybe we can remove it?
|
||||||
|
|
||||||
|
#ifndef LOOP_CTL_GET_FREE
|
||||||
|
#define LOOP_CTL_GET_FREE 0x4C82
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef LO_FLAGS_PARTSCAN
|
||||||
|
#define LO_FLAGS_PARTSCAN 8
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// FIXME: Can't we find a way to do the logging in pure Go?
|
||||||
|
extern void DevmapperLogCallback(int level, char *file, int line, int dm_errno_or_class, char *str);
|
||||||
|
|
||||||
|
static void log_cb(int level, const char *file, int line, int dm_errno_or_class, const char *f, ...)
|
||||||
|
{
|
||||||
|
char buffer[256];
|
||||||
|
va_list ap;
|
||||||
|
|
||||||
|
va_start(ap, f);
|
||||||
|
vsnprintf(buffer, 256, f, ap);
|
||||||
|
va_end(ap);
|
||||||
|
|
||||||
|
DevmapperLogCallback(level, (char *)file, line, dm_errno_or_class, buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void log_with_errno_init()
|
||||||
|
{
|
||||||
|
dm_log_with_errno_init(log_cb);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
|
|
||||||
|
import "unsafe"
|
||||||
|
|
||||||
|
type (
|
||||||
|
CDmTask C.struct_dm_task
|
||||||
|
|
||||||
|
CLoopInfo64 C.struct_loop_info64
|
||||||
|
LoopInfo64 struct {
|
||||||
|
loDevice uint64 /* ioctl r/o */
|
||||||
|
loInode uint64 /* ioctl r/o */
|
||||||
|
loRdevice uint64 /* ioctl r/o */
|
||||||
|
loOffset uint64
|
||||||
|
loSizelimit uint64 /* bytes, 0 == max available */
|
||||||
|
loNumber uint32 /* ioctl r/o */
|
||||||
|
loEncrypt_type uint32
|
||||||
|
loEncrypt_key_size uint32 /* ioctl w/o */
|
||||||
|
loFlags uint32 /* ioctl r/o */
|
||||||
|
loFileName [LoNameSize]uint8
|
||||||
|
loCryptName [LoNameSize]uint8
|
||||||
|
loEncryptKey [LoKeySize]uint8 /* ioctl w/o */
|
||||||
|
loInit [2]uint64
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// IOCTL consts
|
||||||
|
const (
|
||||||
|
BlkGetSize64 = C.BLKGETSIZE64
|
||||||
|
BlkDiscard = C.BLKDISCARD
|
||||||
|
|
||||||
|
LoopSetFd = C.LOOP_SET_FD
|
||||||
|
LoopCtlGetFree = C.LOOP_CTL_GET_FREE
|
||||||
|
LoopGetStatus64 = C.LOOP_GET_STATUS64
|
||||||
|
LoopSetStatus64 = C.LOOP_SET_STATUS64
|
||||||
|
LoopClrFd = C.LOOP_CLR_FD
|
||||||
|
LoopSetCapacity = C.LOOP_SET_CAPACITY
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
LoFlagsAutoClear = C.LO_FLAGS_AUTOCLEAR
|
||||||
|
LoFlagsReadOnly = C.LO_FLAGS_READ_ONLY
|
||||||
|
LoFlagsPartScan = C.LO_FLAGS_PARTSCAN
|
||||||
|
LoKeySize = C.LO_KEY_SIZE
|
||||||
|
LoNameSize = C.LO_NAME_SIZE
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
DmGetLibraryVersion = dmGetLibraryVersionFct
|
||||||
|
DmGetNextTarget = dmGetNextTargetFct
|
||||||
|
DmLogInitVerbose = dmLogInitVerboseFct
|
||||||
|
DmSetDevDir = dmSetDevDirFct
|
||||||
|
DmTaskAddTarget = dmTaskAddTargetFct
|
||||||
|
DmTaskCreate = dmTaskCreateFct
|
||||||
|
DmTaskDestroy = dmTaskDestroyFct
|
||||||
|
DmTaskGetDeps = dmTaskGetDepsFct
|
||||||
|
DmTaskGetInfo = dmTaskGetInfoFct
|
||||||
|
DmTaskGetDriverVersion = dmTaskGetDriverVersionFct
|
||||||
|
DmTaskRun = dmTaskRunFct
|
||||||
|
DmTaskSetAddNode = dmTaskSetAddNodeFct
|
||||||
|
DmTaskSetCookie = dmTaskSetCookieFct
|
||||||
|
DmTaskSetMessage = dmTaskSetMessageFct
|
||||||
|
DmTaskSetName = dmTaskSetNameFct
|
||||||
|
DmTaskSetRo = dmTaskSetRoFct
|
||||||
|
DmTaskSetSector = dmTaskSetSectorFct
|
||||||
|
DmUdevWait = dmUdevWaitFct
|
||||||
|
LogWithErrnoInit = logWithErrnoInitFct
|
||||||
|
)
|
||||||
|
|
||||||
|
func free(p *C.char) {
|
||||||
|
C.free(unsafe.Pointer(p))
|
||||||
|
}
|
||||||
|
|
||||||
|
func dmTaskDestroyFct(task *CDmTask) {
|
||||||
|
C.dm_task_destroy((*C.struct_dm_task)(task))
|
||||||
|
}
|
||||||
|
|
||||||
|
func dmTaskCreateFct(taskType int) *CDmTask {
|
||||||
|
return (*CDmTask)(C.dm_task_create(C.int(taskType)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func dmTaskRunFct(task *CDmTask) int {
|
||||||
|
ret, _ := C.dm_task_run((*C.struct_dm_task)(task))
|
||||||
|
return int(ret)
|
||||||
|
}
|
||||||
|
|
||||||
|
func dmTaskSetNameFct(task *CDmTask, name string) int {
|
||||||
|
Cname := C.CString(name)
|
||||||
|
defer free(Cname)
|
||||||
|
|
||||||
|
return int(C.dm_task_set_name((*C.struct_dm_task)(task), Cname))
|
||||||
|
}
|
||||||
|
|
||||||
|
func dmTaskSetMessageFct(task *CDmTask, message string) int {
|
||||||
|
Cmessage := C.CString(message)
|
||||||
|
defer free(Cmessage)
|
||||||
|
|
||||||
|
return int(C.dm_task_set_message((*C.struct_dm_task)(task), Cmessage))
|
||||||
|
}
|
||||||
|
|
||||||
|
func dmTaskSetSectorFct(task *CDmTask, sector uint64) int {
|
||||||
|
return int(C.dm_task_set_sector((*C.struct_dm_task)(task), C.uint64_t(sector)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func dmTaskSetCookieFct(task *CDmTask, cookie *uint, flags uint16) int {
|
||||||
|
cCookie := C.uint32_t(*cookie)
|
||||||
|
defer func() {
|
||||||
|
*cookie = uint(cCookie)
|
||||||
|
}()
|
||||||
|
return int(C.dm_task_set_cookie((*C.struct_dm_task)(task), &cCookie, C.uint16_t(flags)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func dmTaskSetAddNodeFct(task *CDmTask, addNode AddNodeType) int {
|
||||||
|
return int(C.dm_task_set_add_node((*C.struct_dm_task)(task), C.dm_add_node_t(addNode)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func dmTaskSetRoFct(task *CDmTask) int {
|
||||||
|
return int(C.dm_task_set_ro((*C.struct_dm_task)(task)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func dmTaskAddTargetFct(task *CDmTask,
|
||||||
|
start, size uint64, ttype, params string) int {
|
||||||
|
|
||||||
|
Cttype := C.CString(ttype)
|
||||||
|
defer free(Cttype)
|
||||||
|
|
||||||
|
Cparams := C.CString(params)
|
||||||
|
defer free(Cparams)
|
||||||
|
|
||||||
|
return int(C.dm_task_add_target((*C.struct_dm_task)(task), C.uint64_t(start), C.uint64_t(size), Cttype, Cparams))
|
||||||
|
}
|
||||||
|
|
||||||
|
func dmTaskGetDepsFct(task *CDmTask) *Deps {
|
||||||
|
Cdeps := C.dm_task_get_deps((*C.struct_dm_task)(task))
|
||||||
|
if Cdeps == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
deps := &Deps{
|
||||||
|
Count: uint32(Cdeps.count),
|
||||||
|
Filler: uint32(Cdeps.filler),
|
||||||
|
}
|
||||||
|
for _, device := range Cdeps.device {
|
||||||
|
deps.Device = append(deps.Device, (uint64)(device))
|
||||||
|
}
|
||||||
|
return deps
|
||||||
|
}
|
||||||
|
|
||||||
|
func dmTaskGetInfoFct(task *CDmTask, info *Info) int {
|
||||||
|
Cinfo := C.struct_dm_info{}
|
||||||
|
defer func() {
|
||||||
|
info.Exists = int(Cinfo.exists)
|
||||||
|
info.Suspended = int(Cinfo.suspended)
|
||||||
|
info.LiveTable = int(Cinfo.live_table)
|
||||||
|
info.InactiveTable = int(Cinfo.inactive_table)
|
||||||
|
info.OpenCount = int32(Cinfo.open_count)
|
||||||
|
info.EventNr = uint32(Cinfo.event_nr)
|
||||||
|
info.Major = uint32(Cinfo.major)
|
||||||
|
info.Minor = uint32(Cinfo.minor)
|
||||||
|
info.ReadOnly = int(Cinfo.read_only)
|
||||||
|
info.TargetCount = int32(Cinfo.target_count)
|
||||||
|
}()
|
||||||
|
return int(C.dm_task_get_info((*C.struct_dm_task)(task), &Cinfo))
|
||||||
|
}
|
||||||
|
|
||||||
|
func dmTaskGetDriverVersionFct(task *CDmTask) string {
|
||||||
|
buffer := C.malloc(128)
|
||||||
|
defer C.free(buffer)
|
||||||
|
res := C.dm_task_get_driver_version((*C.struct_dm_task)(task), (*C.char)(buffer), 128)
|
||||||
|
if res == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return C.GoString((*C.char)(buffer))
|
||||||
|
}
|
||||||
|
|
||||||
|
func dmGetNextTargetFct(task *CDmTask, next uintptr, start, length *uint64, target, params *string) uintptr {
|
||||||
|
var (
|
||||||
|
Cstart, Clength C.uint64_t
|
||||||
|
CtargetType, Cparams *C.char
|
||||||
|
)
|
||||||
|
defer func() {
|
||||||
|
*start = uint64(Cstart)
|
||||||
|
*length = uint64(Clength)
|
||||||
|
*target = C.GoString(CtargetType)
|
||||||
|
*params = C.GoString(Cparams)
|
||||||
|
}()
|
||||||
|
|
||||||
|
nextp := C.dm_get_next_target((*C.struct_dm_task)(task), unsafe.Pointer(next), &Cstart, &Clength, &CtargetType, &Cparams)
|
||||||
|
return uintptr(nextp)
|
||||||
|
}
|
||||||
|
|
||||||
|
func dmUdevWaitFct(cookie uint) int {
|
||||||
|
return int(C.dm_udev_wait(C.uint32_t(cookie)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func dmLogInitVerboseFct(level int) {
|
||||||
|
C.dm_log_init_verbose(C.int(level))
|
||||||
|
}
|
||||||
|
|
||||||
|
func logWithErrnoInitFct() {
|
||||||
|
C.log_with_errno_init()
|
||||||
|
}
|
||||||
|
|
||||||
|
func dmSetDevDirFct(dir string) int {
|
||||||
|
Cdir := C.CString(dir)
|
||||||
|
defer free(Cdir)
|
||||||
|
|
||||||
|
return int(C.dm_set_dev_dir(Cdir))
|
||||||
|
}
|
||||||
|
|
||||||
|
func dmGetLibraryVersionFct(version *string) int {
|
||||||
|
buffer := C.CString(string(make([]byte, 128)))
|
||||||
|
defer free(buffer)
|
||||||
|
defer func() {
|
||||||
|
*version = C.GoString(buffer)
|
||||||
|
}()
|
||||||
|
return int(C.dm_get_library_version(buffer, 128))
|
||||||
|
}
|
72
devicemapper/ioctl.go
Normal file
72
devicemapper/ioctl.go
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
// +build linux
|
||||||
|
|
||||||
|
package devicemapper
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ioctlLoopCtlGetFree(fd uintptr) (int, error) {
|
||||||
|
index, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, LoopCtlGetFree, 0)
|
||||||
|
if err != 0 {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return int(index), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ioctlLoopSetFd(loopFd, sparseFd uintptr) error {
|
||||||
|
if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, loopFd, LoopSetFd, sparseFd); err != 0 {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ioctlLoopSetStatus64(loopFd uintptr, loopInfo *LoopInfo64) error {
|
||||||
|
if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, loopFd, LoopSetStatus64, uintptr(unsafe.Pointer(loopInfo))); err != 0 {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ioctlLoopClrFd(loopFd uintptr) error {
|
||||||
|
if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, loopFd, LoopClrFd, 0); err != 0 {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ioctlLoopGetStatus64(loopFd uintptr) (*LoopInfo64, error) {
|
||||||
|
loopInfo := &LoopInfo64{}
|
||||||
|
|
||||||
|
if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, loopFd, LoopGetStatus64, uintptr(unsafe.Pointer(loopInfo))); err != 0 {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return loopInfo, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ioctlLoopSetCapacity(loopFd uintptr, value int) error {
|
||||||
|
if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, loopFd, LoopSetCapacity, uintptr(value)); err != 0 {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ioctlBlkGetSize64(fd uintptr) (int64, error) {
|
||||||
|
var size int64
|
||||||
|
if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, BlkGetSize64, uintptr(unsafe.Pointer(&size))); err != 0 {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return size, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ioctlBlkDiscard(fd uintptr, offset, length uint64) error {
|
||||||
|
var r [2]uint64
|
||||||
|
r[0] = offset
|
||||||
|
r[1] = length
|
||||||
|
|
||||||
|
if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, BlkDiscard, uintptr(unsafe.Pointer(&r[0]))); err != 0 {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
Loading…
Reference in a new issue