loopback: separate loop logic from devicemapper
The loopback logic is not technically exclusive to the devicemapper driver. This reorganizes the code such that the loopback code is usable outside of the devicemapper package and driver. Signed-off-by: Vincent Batts <vbatts@redhat.com>
This commit is contained in:
parent
c28b794f70
commit
74e35aace0
7 changed files with 200 additions and 165 deletions
|
@ -1,129 +0,0 @@
|
|||
// +build linux
|
||||
|
||||
package devicemapper
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"syscall"
|
||||
|
||||
"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) {
|
||||
logrus.Errorf("There are no more loopback devices available.")
|
||||
}
|
||||
return nil, ErrAttachLoopbackDevice
|
||||
}
|
||||
|
||||
if fi.Mode()&os.ModeDevice != os.ModeDevice {
|
||||
logrus.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 {
|
||||
logrus.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 {
|
||||
logrus.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 {
|
||||
logrus.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 looping for a
|
||||
// loopback from index 0.
|
||||
startIndex, err := getNextFreeLoopbackIndex()
|
||||
if err != nil {
|
||||
logrus.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 {
|
||||
logrus.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 {
|
||||
logrus.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 {
|
||||
logrus.Errorf("Error while cleaning up the loopback device")
|
||||
}
|
||||
loopFile.Close()
|
||||
return nil, ErrAttachLoopbackDevice
|
||||
}
|
||||
|
||||
return loopFile, nil
|
||||
}
|
|
@ -47,32 +47,29 @@ const (
|
|||
|
||||
// List of errors returned when using devicemapper.
|
||||
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")
|
||||
ErrTaskDeferredRemove = errors.New("dm_task_deferred_remove 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("Invalid AddNode type")
|
||||
ErrGetLoopbackBackingFile = errors.New("Unable to get loopback backing file")
|
||||
ErrLoopbackSetCapacity = errors.New("Unable set loopback capacity")
|
||||
ErrBusy = errors.New("Device is Busy")
|
||||
ErrDeviceIDExists = errors.New("Device Id Exists")
|
||||
ErrEnxio = errors.New("No such device or address")
|
||||
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")
|
||||
ErrTaskDeferredRemove = errors.New("dm_task_deferred_remove failed")
|
||||
ErrTaskSetCookie = errors.New("dm_task_set_cookie failed")
|
||||
ErrNilCookie = errors.New("cookie ptr can't be nil")
|
||||
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("Invalid AddNode type")
|
||||
ErrBusy = errors.New("Device is Busy")
|
||||
ErrDeviceIDExists = errors.New("Device Id Exists")
|
||||
ErrEnxio = errors.New("No such device or address")
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -257,58 +254,6 @@ func (t *Task) getNextTarget(next unsafe.Pointer) (nextPtr unsafe.Pointer, start
|
|||
start, length, targetType, params
|
||||
}
|
||||
|
||||
func getLoopbackBackingFile(file *os.File) (uint64, uint64, error) {
|
||||
loopInfo, err := ioctlLoopGetStatus64(file.Fd())
|
||||
if err != nil {
|
||||
logrus.Errorf("devicemapper: Error get loopback backing file: %s", err)
|
||||
return 0, 0, ErrGetLoopbackBackingFile
|
||||
}
|
||||
return loopInfo.loDevice, loopInfo.loInode, nil
|
||||
}
|
||||
|
||||
// LoopbackSetCapacity reloads the size for the loopback device.
|
||||
func LoopbackSetCapacity(file *os.File) error {
|
||||
if err := ioctlLoopSetCapacity(file.Fd(), 0); err != nil {
|
||||
logrus.Errorf("devicemapper: Error loopbackSetCapacity: %s", err)
|
||||
return ErrLoopbackSetCapacity
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// FindLoopDeviceFor returns a loopback device file for the specified file which
|
||||
// is backing file of a loop back device.
|
||||
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
|
||||
}
|
||||
|
||||
// UdevWait waits for any processes that are waiting for udev to complete the specified cookie.
|
||||
func UdevWait(cookie *uint) error {
|
||||
if res := DmUdevWait(*cookie); res != 1 {
|
||||
|
|
|
@ -5,17 +5,8 @@ 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);
|
||||
|
||||
|
@ -45,44 +36,12 @@ import (
|
|||
|
||||
type (
|
||||
cdmTask C.struct_dm_task
|
||||
|
||||
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 */
|
||||
loEncryptType uint32
|
||||
loEncryptKeySize 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
|
||||
)
|
||||
|
||||
// LOOP consts.
|
||||
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
|
||||
)
|
||||
|
||||
// Devicemapper cookie flags.
|
||||
|
|
|
@ -7,51 +7,6 @@ import (
|
|||
"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 {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue