diff --git a/graphdriver/devmapper/attachLoopback.go b/graphdriver/devmapper/attachLoopback.go new file mode 100644 index 0000000000..d81a482ac4 --- /dev/null +++ b/graphdriver/devmapper/attachLoopback.go @@ -0,0 +1,178 @@ +package devmapper + +import ( + "fmt" + "github.com/dotcloud/docker/utils" + "unsafe" +) + +func ioctlLoopCtlGetFree(fd uintptr) (int, error) { + index, _, err := sysSyscall(sysSysIoctl, fd, LoopCtlGetFree, 0) + if err != 0 { + return 0, err + } + return int(index), nil +} + +func ioctlLoopSetFd(loopFd, sparseFd uintptr) error { + if _, _, err := sysSyscall(sysSysIoctl, loopFd, LoopSetFd, sparseFd); err != 0 { + return err + } + return nil +} + +func ioctlLoopSetStatus64(loopFd uintptr, loopInfo *LoopInfo64) error { + _, _, err := sysSyscall(sysSysIoctl, loopFd, LoopSetStatus64, uintptr(unsafe.Pointer(loopInfo))) + if err != 0 { + return err + } + return nil +} + +func ioctlLoopClrFd(loopFd uintptr) error { + _, _, err := sysSyscall(sysSysIoctl, loopFd, LoopClrFd, 0) + if err != 0 { + return err + } + return nil +} + +// //func dmGetLoopbackBackingFileFct(fd uintptr) (uint64, uint64, sysErrno) { +// func ioctlLoopGetStatus64(loopFd uintptr) (*LoopInfo64, error) { +// var lo64 C.struct_loop_info64 +// _, _, err := sysSyscall(sysSysIoctl, fd, C.LOOP_GET_STATUS64, uintptr(unsafe.Pointer(&lo64))) +// return uint64(lo64.lo_device), uint64(lo64.lo_inode), sysErrno(err) +// } + +// func dmLoopbackSetCapacityFct(fd uintptr) sysErrno { +// _, _, err := sysSyscall(sysSysIoctl, fd, C.LOOP_SET_CAPACITY, 0) +// return sysErrno(err) +// } + +// func dmGetBlockSizeFct(fd uintptr) (int64, sysErrno) { +// var size int64 +// _, _, err := sysSyscall(sysSysIoctl, fd, C.BLKGETSIZE64, uintptr(unsafe.Pointer(&size))) +// return size, sysErrno(err) +// } + +// func getBlockSizeFct(fd uintptr, size *uint64) sysErrno { +// _, _, err := sysSyscall(sysSysIoctl, fd, C.BLKGETSIZE64, uintptr(unsafe.Pointer(&size))) +// return sysErrno(err) +// } + +func getNextFreeLoopbackIndex() (int, error) { + f, err := osOpenFile("/dev/loop-control", osORdOnly, 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 *osFile) (loopFile *osFile, err error) { + // Start looking for a free /dev/loop + for { + target := fmt.Sprintf("/dev/loop%d", index) + index++ + + fi, err := osStat(target) + if err != nil { + if osIsNotExist(err) { + utils.Errorf("There are no more loopback device available.") + } + return nil, ErrAttachLoopbackDevice + } + + // FIXME: Check here if target is a block device (in C: S_ISBLK(mode)) + if fi.IsDir() { + } + + // Open the targeted loopback (use OpenFile because Open sets O_CLOEXEC) + loopFile, err = osOpenFile(target, osORdWr, 0644) + if err != nil { + utils.Errorf("Error openning 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 != sysEBusy { + utils.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 { + utils.Errorf("Unreachable code reached! Error attaching %s to a loopback device.", sparseFile.Name()) + return nil, ErrAttachLoopbackDevice + } + + return loopFile, nil +} + +func stringToLoopName(src string) [LoNameSize]uint8 { + var dst [LoNameSize]uint8 + copy(dst[:], src[:]) + return dst +} + +// attachLoopDevice attaches the given sparse file to the next +// available loopback device. It returns an opened *osFile. +func attachLoopDevice(sparseName string) (loop *osFile, 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 { + utils.Debugf("Error retrieving the next available loopback: %s", err) + } + + // Open the given sparse file (use OpenFile because Open sets O_CLOEXEC) + sparseFile, err := osOpenFile(sparseName, osORdWr, 0644) + if err != nil { + utils.Errorf("Error openning 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 { + utils.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 { + utils.Errorf("Error while cleaning up the loopback device") + } + loopFile.Close() + return nil, ErrAttachLoopbackDevice + } + + return loopFile, nil +} diff --git a/graphdriver/devmapper/devmapper.go b/graphdriver/devmapper/devmapper.go index 305ed183d4..93faf5018d 100644 --- a/graphdriver/devmapper/devmapper.go +++ b/graphdriver/devmapper/devmapper.go @@ -5,7 +5,6 @@ import ( "fmt" "github.com/dotcloud/docker/utils" "runtime" - "unsafe" ) type DevmapperLogger interface { @@ -565,109 +564,3 @@ func (devices *DeviceSet) createSnapDevice(poolName string, deviceId int, baseNa return nil } - -type 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 -} - -// attachLoopDevice attaches the given sparse file to the next -// available loopback device. It returns an opened *osFile. -func attachLoopDevice(filename string) (loop *osFile, err error) { - startIndex := 0 - - // 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. - if f, err := osOpenFile("/dev/loop-control", osORdOnly, 0644); err == nil { - if index, _, err := sysSyscall(sysSysIoctl, f.Fd(), LoopCtlGetFree, 0); err != 0 { - utils.Debugf("Error retrieving the next available loopback: %s", err) - } else if index > 0 { - startIndex = int(index) - } - f.Close() - } - - // Open the given sparse file (use OpenFile because Open sets O_CLOEXEC) - f, err := osOpenFile(filename, osORdWr, 0644) - if err != nil { - return nil, err - } - defer f.Close() - - var ( - target string - loopFile *osFile - ) - // Start looking for a free /dev/loop - for i := startIndex; ; { - target = fmt.Sprintf("/dev/loop%d", i) - - fi, err := osStat(target) - if err != nil { - if osIsNotExist(err) { - utils.Errorf("There are no more loopback device available.") - } - } - - // FIXME: Check here if target is a block device (in C: S_ISBLK(mode)) - if fi.IsDir() { - } - - // Open the targeted loopback (use OpenFile because Open sets O_CLOEXEC) - loopFile, err = osOpenFile(target, osORdWr, 0644) - if err != nil { - return nil, err - } - - // Try to attach to the loop file - if _, _, err := sysSyscall(sysSysIoctl, loopFile.Fd(), LoopSetFd, f.Fd()); err != 0 { - loopFile.Close() - // If the error is EBUSY, then try the next loopback - if err != sysEBusy { - utils.Errorf("Cannot set up loopback device %s: %s", target, err) - return nil, err - } - } else { - // In case of success, we finished. Break the loop. - break - } - // In case of EBUSY error, the loop keep going. - } - - // This can't happen, but let's be sure - if loopFile == nil { - return nil, fmt.Errorf("Unreachable code reached! Error attaching %s to a loopback device.", filename) - } - - // Set the status of the loopback device - var loopInfo LoopInfo64 - - // Due to type incompatibility (string vs [64]uint8), we copy data - copy(loopInfo.loFileName[:], target[:]) - loopInfo.loOffset = 0 - loopInfo.loFlags = LoFlagsAutoClear - - if _, _, err := sysSyscall(sysSysIoctl, loopFile.Fd(), LoopSetStatus64, uintptr(unsafe.Pointer(&loopInfo))); err != 0 { - // If the call failed, then free the loopback device - utils.Errorf("Cannot set up loopback device info: %s", err) - if _, _, err := sysSyscall(sysSysIoctl, loopFile.Fd(), LoopClrFd, 0); err != 0 { - utils.Errorf("Error while cleaning up the loopback device") - } - loopFile.Close() - return nil, err - } - - return loopFile, nil -} diff --git a/graphdriver/devmapper/devmapper_wrapper.go b/graphdriver/devmapper/devmapper_wrapper.go index 0ed5bcd332..24d9502b49 100644 --- a/graphdriver/devmapper/devmapper_wrapper.go +++ b/graphdriver/devmapper/devmapper_wrapper.go @@ -33,7 +33,23 @@ import ( ) type ( - CDmTask C.struct_dm_task + 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 + } ) // FIXME: Make sure the values are defined in C @@ -50,7 +66,6 @@ const ( ) var ( - DmAttachLoopDevice = dmAttachLoopDeviceFct DmGetBlockSize = dmGetBlockSizeFct DmGetLibraryVersion = dmGetLibraryVersionFct DmGetNextTarget = dmGetNextTargetFct @@ -137,23 +152,6 @@ func dmTaskAddTargetFct(task *CDmTask, return int(C.dm_task_add_target((*C.struct_dm_task)(task), C.uint64_t(start), C.uint64_t(size), Cttype, Cparams)) } -func dmGetLoopbackBackingFileFct(fd uintptr) (uint64, uint64, sysErrno) { - var lo64 C.struct_loop_info64 - _, _, err := sysSyscall(sysSysIoctl, fd, C.LOOP_GET_STATUS64, uintptr(unsafe.Pointer(&lo64))) - return uint64(lo64.lo_device), uint64(lo64.lo_inode), sysErrno(err) -} - -func dmLoopbackSetCapacityFct(fd uintptr) sysErrno { - _, _, err := sysSyscall(sysSysIoctl, fd, C.LOOP_SET_CAPACITY, 0) - return sysErrno(err) -} - -func dmGetBlockSizeFct(fd uintptr) (int64, sysErrno) { - var size int64 - _, _, err := sysSyscall(sysSysIoctl, fd, C.BLKGETSIZE64, uintptr(unsafe.Pointer(&size))) - return size, sysErrno(err) -} - func dmTaskGetInfoFct(task *CDmTask, info *Info) int { Cinfo := C.struct_dm_info{} defer func() { @@ -187,19 +185,21 @@ func dmGetNextTargetFct(task *CDmTask, next uintptr, start, length *uint64, targ return uintptr(nextp) } -func dmAttachLoopDeviceFct(filename string, fd *int) string { - return "" - // cFilename := C.CString(filename) - // defer free(cFilename) +func dmGetLoopbackBackingFileFct(fd uintptr) (uint64, uint64, sysErrno) { + var lo64 C.struct_loop_info64 + _, _, err := sysSyscall(sysSysIoctl, fd, C.LOOP_GET_STATUS64, uintptr(unsafe.Pointer(&lo64))) + return uint64(lo64.lo_device), uint64(lo64.lo_inode), sysErrno(err) +} - // var cFd C.int - // defer func() { - // *fd = int(cFd) - // }() +func dmLoopbackSetCapacityFct(fd uintptr) sysErrno { + _, _, err := sysSyscall(sysSysIoctl, fd, C.LOOP_SET_CAPACITY, 0) + return sysErrno(err) +} - // ret := C.attach_loop_device(cFilename, &cFd) - // defer free(ret) - // return C.GoString(ret) +func dmGetBlockSizeFct(fd uintptr) (int64, sysErrno) { + var size int64 + _, _, err := sysSyscall(sysSysIoctl, fd, C.BLKGETSIZE64, uintptr(unsafe.Pointer(&size))) + return size, sysErrno(err) } func getBlockSizeFct(fd uintptr, size *uint64) sysErrno {