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:
Vincent Batts
2014-11-05 18:10:38 -05:00
parent a61c4dc959
commit e2f8fbfbcc
7 changed files with 87 additions and 82 deletions
@@ -1,129 +0,0 @@
// +build linux
package devmapper
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
}
+41 -39
View File
@@ -20,6 +20,7 @@ import (
log "github.com/Sirupsen/logrus"
"github.com/docker/docker/daemon/graphdriver"
"github.com/docker/docker/pkg/devicemapper"
"github.com/docker/docker/pkg/parsers"
"github.com/docker/docker/pkg/units"
"github.com/docker/libcontainer/label"
@@ -228,7 +229,7 @@ func (devices *DeviceSet) saveMetadata(info *DevInfo) error {
}
if devices.NewTransactionId != devices.TransactionId {
if err = setTransactionId(devices.getPoolDevName(), devices.TransactionId, devices.NewTransactionId); err != nil {
if err = devicemapper.SetTransactionId(devices.getPoolDevName(), devices.TransactionId, devices.NewTransactionId); err != nil {
return fmt.Errorf("Error setting devmapper transition ID: %s", err)
}
devices.TransactionId = devices.NewTransactionId
@@ -280,11 +281,11 @@ func (devices *DeviceSet) registerDevice(id int, hash string, size uint64) (*Dev
func (devices *DeviceSet) activateDeviceIfNeeded(info *DevInfo) error {
log.Debugf("activateDeviceIfNeeded(%v)", info.Hash)
if devinfo, _ := getInfo(info.Name()); devinfo != nil && devinfo.Exists != 0 {
if devinfo, _ := devicemapper.GetInfo(info.Name()); devinfo != nil && devinfo.Exists != 0 {
return nil
}
return activateDevice(devices.getPoolDevName(), info.Name(), info.DeviceId, info.Size)
return devicemapper.ActivateDevice(devices.getPoolDevName(), info.Name(), info.DeviceId, info.Size)
}
func (devices *DeviceSet) createFilesystem(info *DevInfo) error {
@@ -321,7 +322,7 @@ func (devices *DeviceSet) createFilesystem(info *DevInfo) error {
}
func (devices *DeviceSet) initMetaData() error {
_, _, _, params, err := getStatus(devices.getPoolName())
_, _, _, params, err := devicemapper.GetStatus(devices.getPoolName())
if err != nil {
return err
}
@@ -400,7 +401,7 @@ func (devices *DeviceSet) setupBaseImage() error {
id := devices.nextDeviceId
// Create initial device
if err := createDevice(devices.getPoolDevName(), &id); err != nil {
if err := devicemapper.CreateDevice(devices.getPoolDevName(), &id); err != nil {
return err
}
@@ -410,7 +411,7 @@ func (devices *DeviceSet) setupBaseImage() error {
log.Debugf("Registering base device (id %v) with FS size %v", id, devices.baseFsSize)
info, err := devices.registerDevice(id, "", devices.baseFsSize)
if err != nil {
_ = deleteDevice(devices.getPoolDevName(), id)
_ = devicemapper.DeleteDevice(devices.getPoolDevName(), id)
return err
}
@@ -447,11 +448,12 @@ func setCloseOnExec(name string) {
}
}
func (devices *DeviceSet) log(level int, file string, line int, dmError int, message string) {
func (devices *DeviceSet) DMLog(level int, file string, line int, dmError int, message string) {
if level >= 7 {
return // Ignore _LOG_DEBUG
}
// FIXME(vbatts) push this back into ./pkg/devicemapper/
log.Debugf("libdevmapper(%d): %s:%d (%d) %s", level, file, line, dmError, message)
}
@@ -489,7 +491,7 @@ func (devices *DeviceSet) ResizePool(size int64) error {
return fmt.Errorf("Can't shrink file")
}
dataloopback := FindLoopDeviceFor(datafile)
dataloopback := devicemapper.FindLoopDeviceFor(datafile)
if dataloopback == nil {
return fmt.Errorf("Unable to find loopback mount for: %s", datafilename)
}
@@ -501,7 +503,7 @@ func (devices *DeviceSet) ResizePool(size int64) error {
}
defer metadatafile.Close()
metadataloopback := FindLoopDeviceFor(metadatafile)
metadataloopback := devicemapper.FindLoopDeviceFor(metadatafile)
if metadataloopback == nil {
return fmt.Errorf("Unable to find loopback mount for: %s", metadatafilename)
}
@@ -513,22 +515,22 @@ func (devices *DeviceSet) ResizePool(size int64) error {
}
// Reload size for loopback device
if err := LoopbackSetCapacity(dataloopback); err != nil {
if err := devicemapper.LoopbackSetCapacity(dataloopback); err != nil {
return fmt.Errorf("Unable to update loopback capacity: %s", err)
}
// Suspend the pool
if err := suspendDevice(devices.getPoolName()); err != nil {
if err := devicemapper.SuspendDevice(devices.getPoolName()); err != nil {
return fmt.Errorf("Unable to suspend pool: %s", err)
}
// Reload with the new block sizes
if err := reloadPool(devices.getPoolName(), dataloopback, metadataloopback, devices.thinpBlockSize); err != nil {
if err := devicemapper.ReloadPool(devices.getPoolName(), dataloopback, metadataloopback, devices.thinpBlockSize); err != nil {
return fmt.Errorf("Unable to reload pool: %s", err)
}
// Resume the pool
if err := resumeDevice(devices.getPoolName()); err != nil {
if err := devicemapper.ResumeDevice(devices.getPoolName()); err != nil {
return fmt.Errorf("Unable to resume pool: %s", err)
}
@@ -536,9 +538,10 @@ func (devices *DeviceSet) ResizePool(size int64) error {
}
func (devices *DeviceSet) initDevmapper(doInit bool) error {
logInit(devices)
// give ourselves to libdm as a log handler
devicemapper.LogInit(devices)
_, err := getDriverVersion()
_, err := devicemapper.GetDriverVersion()
if err != nil {
// Can't even get driver version, assume not supported
return graphdriver.ErrNotSupported
@@ -566,9 +569,9 @@ func (devices *DeviceSet) initDevmapper(doInit bool) error {
// Check for the existence of the device <prefix>-pool
log.Debugf("Checking for existence of the pool '%s'", devices.getPoolName())
info, err := getInfo(devices.getPoolName())
info, err := devicemapper.GetInfo(devices.getPoolName())
if info == nil {
log.Debugf("Error device getInfo: %s", err)
log.Debugf("Error device devicemapper.GetInfo: %s", err)
return err
}
@@ -610,7 +613,7 @@ func (devices *DeviceSet) initDevmapper(doInit bool) error {
return err
}
dataFile, err = attachLoopDevice(data)
dataFile, err = devicemapper.AttachLoopDevice(data)
if err != nil {
return err
}
@@ -641,7 +644,7 @@ func (devices *DeviceSet) initDevmapper(doInit bool) error {
return err
}
metadataFile, err = attachLoopDevice(metadata)
metadataFile, err = devicemapper.AttachLoopDevice(metadata)
if err != nil {
return err
}
@@ -653,7 +656,7 @@ func (devices *DeviceSet) initDevmapper(doInit bool) error {
}
defer metadataFile.Close()
if err := createPool(devices.getPoolName(), dataFile, metadataFile, devices.thinpBlockSize); err != nil {
if err := devicemapper.CreatePool(devices.getPoolName(), dataFile, metadataFile, devices.thinpBlockSize); err != nil {
return err
}
}
@@ -695,7 +698,7 @@ func (devices *DeviceSet) AddDevice(hash, baseHash string) error {
deviceId := devices.nextDeviceId
if err := createSnapDevice(devices.getPoolDevName(), &deviceId, baseInfo.Name(), baseInfo.DeviceId); err != nil {
if err := devicemapper.CreateSnapDevice(devices.getPoolDevName(), &deviceId, baseInfo.Name(), baseInfo.DeviceId); err != nil {
log.Debugf("Error creating snap device: %s", err)
return err
}
@@ -704,7 +707,7 @@ func (devices *DeviceSet) AddDevice(hash, baseHash string) error {
devices.nextDeviceId = (deviceId + 1) & 0xffffff
if _, err := devices.registerDevice(deviceId, hash, baseInfo.Size); err != nil {
deleteDevice(devices.getPoolDevName(), deviceId)
devicemapper.DeleteDevice(devices.getPoolDevName(), deviceId)
log.Debugf("Error registering device: %s", err)
return err
}
@@ -717,13 +720,13 @@ func (devices *DeviceSet) deleteDevice(info *DevInfo) error {
// on the thin pool when we remove a thinp device, so we do it
// manually
if err := devices.activateDeviceIfNeeded(info); err == nil {
if err := BlockDeviceDiscard(info.DevName()); err != nil {
if err := devicemapper.BlockDeviceDiscard(info.DevName()); err != nil {
log.Debugf("Error discarding block on device: %s (ignoring)", err)
}
}
}
devinfo, _ := getInfo(info.Name())
devinfo, _ := devicemapper.GetInfo(info.Name())
if devinfo != nil && devinfo.Exists != 0 {
if err := devices.removeDeviceAndWait(info.Name()); err != nil {
log.Debugf("Error removing device: %s", err)
@@ -731,7 +734,7 @@ func (devices *DeviceSet) deleteDevice(info *DevInfo) error {
}
}
if err := deleteDevice(devices.getPoolDevName(), info.DeviceId); err != nil {
if err := devicemapper.DeleteDevice(devices.getPoolDevName(), info.DeviceId); err != nil {
log.Debugf("Error deleting device: %s", err)
return err
}
@@ -772,16 +775,16 @@ func (devices *DeviceSet) deactivatePool() error {
defer log.Debugf("[devmapper] deactivatePool END")
devname := devices.getPoolDevName()
devinfo, err := getInfo(devname)
devinfo, err := devicemapper.GetInfo(devname)
if err != nil {
return err
}
if d, err := getDeps(devname); err == nil {
if d, err := devicemapper.GetDeps(devname); err == nil {
// Access to more Debug output
log.Debugf("[devmapper] getDeps() %s: %#v", devname, d)
log.Debugf("[devmapper] devicemapper.GetDeps() %s: %#v", devname, d)
}
if devinfo.Exists != 0 {
return removeDevice(devname)
return devicemapper.RemoveDevice(devname)
}
return nil
@@ -797,7 +800,7 @@ func (devices *DeviceSet) deactivateDevice(info *DevInfo) error {
log.Errorf("Warning: error waiting for device %s to close: %s", info.Hash, err)
}
devinfo, err := getInfo(info.Name())
devinfo, err := devicemapper.GetInfo(info.Name())
if err != nil {
return err
}
@@ -816,11 +819,11 @@ func (devices *DeviceSet) removeDeviceAndWait(devname string) error {
var err error
for i := 0; i < 1000; i++ {
err = removeDevice(devname)
err = devicemapper.RemoveDevice(devname)
if err == nil {
break
}
if err != ErrBusy {
if err != devicemapper.ErrBusy {
return err
}
@@ -848,7 +851,7 @@ func (devices *DeviceSet) waitRemove(devname string) error {
defer log.Debugf("[deviceset %s] waitRemove(%s) END", devices.devicePrefix, devname)
i := 0
for ; i < 1000; i++ {
devinfo, err := getInfo(devname)
devinfo, err := devicemapper.GetInfo(devname)
if err != nil {
// If there is an error we assume the device doesn't exist.
// The error might actually be something else, but we can't differentiate.
@@ -877,7 +880,7 @@ func (devices *DeviceSet) waitRemove(devname string) error {
func (devices *DeviceSet) waitClose(info *DevInfo) error {
i := 0
for ; i < 1000; i++ {
devinfo, err := getInfo(info.Name())
devinfo, err := devicemapper.GetInfo(info.Name())
if err != nil {
return err
}
@@ -898,7 +901,6 @@ func (devices *DeviceSet) waitClose(info *DevInfo) error {
}
func (devices *DeviceSet) Shutdown() error {
log.Debugf("[deviceset %s] shutdown()", devices.devicePrefix)
log.Debugf("[devmapper] Shutting down DeviceSet: %s", devices.root)
defer log.Debugf("[deviceset %s] shutdown END", devices.devicePrefix)
@@ -1065,7 +1067,7 @@ func (devices *DeviceSet) HasActivatedDevice(hash string) bool {
devices.Lock()
defer devices.Unlock()
devinfo, _ := getInfo(info.Name())
devinfo, _ := devicemapper.GetInfo(info.Name())
return devinfo != nil && devinfo.Exists != 0
}
@@ -1087,7 +1089,7 @@ func (devices *DeviceSet) List() []string {
func (devices *DeviceSet) deviceStatus(devName string) (sizeInSectors, mappedSectors, highestMappedSector uint64, err error) {
var params string
_, sizeInSectors, _, params, err = getStatus(devName)
_, sizeInSectors, _, params, err = devicemapper.GetStatus(devName)
if err != nil {
return
}
@@ -1132,7 +1134,7 @@ func (devices *DeviceSet) GetDeviceStatus(hash string) (*DevStatus, error) {
func (devices *DeviceSet) poolStatus() (totalSizeInSectors, transactionId, dataUsed, dataTotal, metadataUsed, metadataTotal uint64, err error) {
var params string
if _, totalSizeInSectors, _, params, err = getStatus(devices.getPoolName()); err == nil {
if _, totalSizeInSectors, _, params, err = devicemapper.GetStatus(devices.getPoolName()); err == nil {
_, err = fmt.Sscanf(params, "%d %d/%d %d/%d", &transactionId, &metadataUsed, &metadataTotal, &dataUsed, &dataTotal)
}
return
@@ -1175,7 +1177,7 @@ func (devices *DeviceSet) Status() *Status {
}
func NewDeviceSet(root string, doInit bool, options []string) (*DeviceSet, error) {
SetDevDir("/dev")
devicemapper.SetDevDir("/dev")
devices := &DeviceSet{
root: root,
-671
View File
@@ -1,671 +0,0 @@
// +build linux
package devmapper
import (
"errors"
"fmt"
"os"
"runtime"
"syscall"
log "github.com/Sirupsen/logrus"
)
type DevmapperLogger interface {
log(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, &params),
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
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 {
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
}
@@ -1,30 +0,0 @@
// +build linux
package devmapper
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.log(int(level), C.GoString(file), int(line), int(dm_errno_or_class), msg)
}
}
@@ -1,254 +0,0 @@
// +build linux
package devmapper
/*
#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))
}
+2 -1
View File
@@ -10,6 +10,7 @@ import (
log "github.com/Sirupsen/logrus"
"github.com/docker/docker/daemon/graphdriver"
"github.com/docker/docker/pkg/devicemapper"
"github.com/docker/docker/pkg/mount"
"github.com/docker/docker/pkg/units"
)
@@ -63,7 +64,7 @@ func (d *Driver) Status() [][2]string {
{"Metadata Space Used", fmt.Sprintf("%s", units.HumanSize(int64(s.Metadata.Used)))},
{"Metadata Space Total", fmt.Sprintf("%s", units.HumanSize(int64(s.Metadata.Total)))},
}
if vStr, err := GetLibraryVersion(); err == nil {
if vStr, err := devicemapper.GetLibraryVersion(); err == nil {
status = append(status, [2]string{"Library Version", vStr})
}
return status
-72
View File
@@ -1,72 +0,0 @@
// +build linux
package devmapper
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
}