From 74c8f7af756ed03131aee051b0ccb926b77e04db Mon Sep 17 00:00:00 2001 From: "Guillaume J. Charmes" Date: Wed, 27 Nov 2013 07:16:34 +0000 Subject: [PATCH 1/7] Refactor attach loop device in pure Go --- graphdriver/devmapper/deviceset.go | 10 +- graphdriver/devmapper/devmapper.go | 125 +++++++++++++-- graphdriver/devmapper/devmapper_wrapper.go | 178 +++++---------------- graphdriver/devmapper/sys.go | 8 +- 4 files changed, 160 insertions(+), 161 deletions(-) diff --git a/graphdriver/devmapper/deviceset.go b/graphdriver/devmapper/deviceset.go index 8c2556104d..77d865ad99 100644 --- a/graphdriver/devmapper/deviceset.go +++ b/graphdriver/devmapper/deviceset.go @@ -383,7 +383,7 @@ func (devices *DeviceSet) ResizePool(size int64) error { return fmt.Errorf("Can't shrink file") } - dataloopback := FindLoopDeviceFor(&osFile{File: datafile}) + dataloopback := FindLoopDeviceFor(datafile) if dataloopback == nil { return fmt.Errorf("Unable to find loopback mount for: %s", datafilename) } @@ -395,7 +395,7 @@ func (devices *DeviceSet) ResizePool(size int64) error { } defer metadatafile.Close() - metadataloopback := FindLoopDeviceFor(&osFile{File: metadatafile}) + metadataloopback := FindLoopDeviceFor(metadatafile) if metadataloopback == nil { return fmt.Errorf("Unable to find loopback mount for: %s", metadatafilename) } @@ -491,14 +491,14 @@ func (devices *DeviceSet) initDevmapper(doInit bool) error { if info.Exists == 0 { utils.Debugf("Pool doesn't exist. Creating it.") - dataFile, err := AttachLoopDevice(data) + dataFile, err := attachLoopDevice(data) if err != nil { utils.Debugf("\n--->Err: %s\n", err) return err } defer dataFile.Close() - metadataFile, err := AttachLoopDevice(metadata) + metadataFile, err := attachLoopDevice(metadata) if err != nil { utils.Debugf("\n--->Err: %s\n", err) return err @@ -637,7 +637,7 @@ func (devices *DeviceSet) deactivateDevice(hash string) error { // or b) the 1 second timeout expires. func (devices *DeviceSet) waitRemove(hash string) error { utils.Debugf("[deviceset %s] waitRemove(%s)", devices.devicePrefix, hash) - defer utils.Debugf("[deviceset %s] waitRemove END", devices.devicePrefix, hash) + defer utils.Debugf("[deviceset %s] waitRemove(%) END", devices.devicePrefix, hash) devname, err := devices.byHash(hash) if err != nil { return err diff --git a/graphdriver/devmapper/devmapper.go b/graphdriver/devmapper/devmapper.go index 89c36d45ad..305ed183d4 100644 --- a/graphdriver/devmapper/devmapper.go +++ b/graphdriver/devmapper/devmapper.go @@ -5,6 +5,7 @@ import ( "fmt" "github.com/dotcloud/docker/utils" "runtime" + "unsafe" ) type DevmapperLogger interface { @@ -177,15 +178,6 @@ func (t *Task) GetNextTarget(next uintptr) (nextPtr uintptr, start uint64, start, length, targetType, params } -func AttachLoopDevice(filename string) (*osFile, error) { - var fd int - res := DmAttachLoopDevice(filename, &fd) - if res == "" { - return nil, ErrAttachLoopbackDevice - } - return &osFile{File: osNewFile(uintptr(fd), res)}, nil -} - func getLoopbackBackingFile(file *osFile) (uint64, uint64, error) { dev, inode, err := DmGetLoopbackBackingFile(file.Fd()) if err != 0 { @@ -223,11 +215,10 @@ func FindLoopDeviceFor(file *osFile) *osFile { continue } - dev, inode, err := getLoopbackBackingFile(&osFile{File: file}) + dev, inode, err := getLoopbackBackingFile(file) if err == nil && dev == targetDevice && inode == targetInode { - return &osFile{File: file} + return file } - file.Close() } @@ -420,7 +411,7 @@ func suspendDevice(name string) error { return err } if err := task.Run(); err != nil { - return fmt.Errorf("Error running DeviceSuspend") + return fmt.Errorf("Error running DeviceSuspend: %s", err) } return nil } @@ -437,7 +428,7 @@ func resumeDevice(name string) error { } if err := task.Run(); err != nil { - return fmt.Errorf("Error running DeviceSuspend") + return fmt.Errorf("Error running DeviceResume") } UdevWait(cookie) @@ -574,3 +565,109 @@ 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 818c968bbf..0ed5bcd332 100644 --- a/graphdriver/devmapper/devmapper_wrapper.go +++ b/graphdriver/devmapper/devmapper_wrapper.go @@ -2,124 +2,14 @@ package devmapper /* #cgo LDFLAGS: -L. -ldevmapper -#include -#include -#include #include -#include -#include -#include -#include -#include -#include -#include - -#ifndef LOOP_CTL_GET_FREE -#define LOOP_CTL_GET_FREE 0x4C82 -#endif - -// FIXME: this could easily be rewritten in go -char* attach_loop_device(const char *filename, int *loop_fd_out) -{ - struct loop_info64 loopinfo = {0}; - struct stat st; - char buf[64]; - int i, loop_fd, fd, start_index; - char* loopname; - - - *loop_fd_out = -1; - - start_index = 0; - fd = open("/dev/loop-control", O_RDONLY); - if (fd >= 0) { - start_index = ioctl(fd, LOOP_CTL_GET_FREE); - close(fd); - - if (start_index < 0) - start_index = 0; - } - - fd = open(filename, O_RDWR); - if (fd < 0) { - perror("open"); - return NULL; - } - - loop_fd = -1; - for (i = start_index ; loop_fd < 0 ; i++ ) { - if (sprintf(buf, "/dev/loop%d", i) < 0) { - close(fd); - return NULL; - } - - if (stat(buf, &st)) { - if (!S_ISBLK(st.st_mode)) { - fprintf(stderr, "[error] Loopback device %s is not a block device.\n", buf); - } else if (errno == ENOENT) { - fprintf(stderr, "[error] There are no more loopback device available.\n"); - } else { - fprintf(stderr, "[error] Unkown error trying to stat the loopback device %s (errno: %d).\n", buf, errno); - } - close(fd); - return NULL; - } - - loop_fd = open(buf, O_RDWR); - if (loop_fd < 0 && errno == ENOENT) { - fprintf(stderr, "[error] The loopback device %s does not exists.\n", buf); - close(fd); - return NULL; - } else if (loop_fd < 0) { - fprintf(stderr, "[error] Unkown error openning the loopback device %s. (errno: %d)\n", buf, errno); - continue; - } - - if (ioctl(loop_fd, LOOP_SET_FD, (void *)(size_t)fd) < 0) { - int errsv = errno; - close(loop_fd); - loop_fd = -1; - if (errsv != EBUSY) { - close(fd); - fprintf(stderr, "cannot set up loopback device %s: %s", buf, strerror(errsv)); - return NULL; - } - continue; - } - - close(fd); - - strncpy((char*)loopinfo.lo_file_name, buf, LO_NAME_SIZE); - loopinfo.lo_offset = 0; - loopinfo.lo_flags = LO_FLAGS_AUTOCLEAR; - - if (ioctl(loop_fd, LOOP_SET_STATUS64, &loopinfo) < 0) { - perror("ioctl LOOP_SET_STATUS64"); - if (ioctl(loop_fd, LOOP_CLR_FD, 0) < 0) { - perror("ioctl LOOP_CLR_FD"); - } - close(loop_fd); - fprintf (stderr, "cannot set up loopback device info"); - return (NULL); - } - - loopname = strdup(buf); - if (loopname == NULL) { - close(loop_fd); - return (NULL); - } - - *loop_fd_out = loop_fd; - return (loopname); - } - - return (NULL); -} +#include // FIXME: present only for defines, maybe we can remove it? +#include // FIXME: present only for BLKGETSIZE64, maybe we can remove it? +// 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, ...) +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; @@ -135,7 +25,6 @@ static void log_with_errno_init() { dm_log_with_errno_init(log_cb); } - */ import "C" @@ -147,6 +36,19 @@ type ( CDmTask C.struct_dm_task ) +// FIXME: Make sure the values are defined in C +const ( + LoopSetFd = C.LOOP_SET_FD + LoopCtlGetFree = C.LOOP_CTL_GET_FREE + LoopSetStatus64 = C.LOOP_SET_STATUS64 + LoopClrFd = C.LOOP_CLR_FD + 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 ( DmAttachLoopDevice = dmAttachLoopDeviceFct DmGetBlockSize = dmGetBlockSizeFct @@ -185,28 +87,26 @@ func dmTaskCreateFct(taskType int) *CDmTask { } func dmTaskRunFct(task *CDmTask) int { - return int(C.dm_task_run((*C.struct_dm_task)(task))) + 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)) + 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)) + 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))) + 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 { @@ -214,13 +114,11 @@ func dmTaskSetCookieFct(task *CDmTask, cookie *uint, flags uint16) int { defer func() { *cookie = uint(cCookie) }() - return int(C.dm_task_set_cookie((*C.struct_dm_task)(task), &cCookie, - C.uint16_t(flags))) + 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))) + return int(C.dm_task_set_add_node((*C.struct_dm_task)(task), C.dm_add_node_t(addNode))) } func dmTaskSetRoFct(task *CDmTask) int { @@ -236,14 +134,12 @@ func dmTaskAddTargetFct(task *CDmTask, 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)) + 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))) + _, _, err := sysSyscall(sysSysIoctl, fd, C.LOOP_GET_STATUS64, uintptr(unsafe.Pointer(&lo64))) return uint64(lo64.lo_device), uint64(lo64.lo_inode), sysErrno(err) } @@ -287,23 +183,23 @@ func dmGetNextTargetFct(task *CDmTask, next uintptr, start, length *uint64, targ *params = C.GoString(Cparams) }() - nextp := C.dm_get_next_target((*C.struct_dm_task)(task), - unsafe.Pointer(next), &Cstart, &Clength, &CtargetType, &Cparams) + nextp := C.dm_get_next_target((*C.struct_dm_task)(task), unsafe.Pointer(next), &Cstart, &Clength, &CtargetType, &Cparams) return uintptr(nextp) } func dmAttachLoopDeviceFct(filename string, fd *int) string { - cFilename := C.CString(filename) - defer free(cFilename) + return "" + // cFilename := C.CString(filename) + // defer free(cFilename) - var cFd C.int - defer func() { - *fd = int(cFd) - }() + // var cFd C.int + // defer func() { + // *fd = int(cFd) + // }() - ret := C.attach_loop_device(cFilename, &cFd) - defer free(ret) - return C.GoString(ret) + // ret := C.attach_loop_device(cFilename, &cFd) + // defer free(ret) + // return C.GoString(ret) } func getBlockSizeFct(fd uintptr, size *uint64) sysErrno { diff --git a/graphdriver/devmapper/sys.go b/graphdriver/devmapper/sys.go index 60bafb5f6d..152e68285b 100644 --- a/graphdriver/devmapper/sys.go +++ b/graphdriver/devmapper/sys.go @@ -19,7 +19,11 @@ var ( sysCloseOnExec = syscall.CloseOnExec sysSyscall = syscall.Syscall - osOpenFile = os.OpenFile + osOpenFile = func(name string, flag int, perm os.FileMode) (*osFile, error) { + f, err := os.OpenFile(name, flag, perm) + return &osFile{File: f}, err + } + osOpen = func(name string) (*osFile, error) { f, err := os.Open(name); return &osFile{File: f}, err } osNewFile = os.NewFile osCreate = os.Create osStat = os.Stat @@ -40,7 +44,9 @@ const ( sysMsRdOnly = syscall.MS_RDONLY sysEInval = syscall.EINVAL sysSysIoctl = syscall.SYS_IOCTL + sysEBusy = syscall.EBUSY + osORdOnly = os.O_RDONLY osORdWr = os.O_RDWR osOCreate = os.O_CREATE ) From eb528b959efc0a3924939991be45704047fd5aea Mon Sep 17 00:00:00 2001 From: "Guillaume J. Charmes" Date: Wed, 27 Nov 2013 17:12:57 -0800 Subject: [PATCH 2/7] Move attach loop device to its own file --- graphdriver/devmapper/attachLoopback.go | 178 +++++++++++++++++++++ graphdriver/devmapper/devmapper.go | 107 ------------- graphdriver/devmapper/devmapper_wrapper.go | 60 +++---- 3 files changed, 208 insertions(+), 137 deletions(-) create mode 100644 graphdriver/devmapper/attachLoopback.go 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 { From 1214b8897bba2a33a7ded8779bcdc966fe1cb176 Mon Sep 17 00:00:00 2001 From: "Guillaume J. Charmes" Date: Wed, 27 Nov 2013 17:44:54 -0800 Subject: [PATCH 3/7] Extract ioctl from wrapper --- graphdriver/devmapper/attachLoopback.go | 63 ++---------------- graphdriver/devmapper/devmapper.go | 15 +++-- graphdriver/devmapper/devmapper_wrapper.go | 76 ++++++++-------------- graphdriver/devmapper/ioctl.go | 58 +++++++++++++++++ 4 files changed, 99 insertions(+), 113 deletions(-) create mode 100644 graphdriver/devmapper/ioctl.go diff --git a/graphdriver/devmapper/attachLoopback.go b/graphdriver/devmapper/attachLoopback.go index d81a482ac4..98d0bf5f5b 100644 --- a/graphdriver/devmapper/attachLoopback.go +++ b/graphdriver/devmapper/attachLoopback.go @@ -3,63 +3,14 @@ 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 stringToLoopName(src string) [LoNameSize]uint8 { + var dst [LoNameSize]uint8 + copy(dst[:], src[:]) + return dst } -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 { @@ -125,12 +76,6 @@ func openNextAvailableLoopback(index int, sparseFile *osFile) (loopFile *osFile, 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) { diff --git a/graphdriver/devmapper/devmapper.go b/graphdriver/devmapper/devmapper.go index 93faf5018d..db41ed6c25 100644 --- a/graphdriver/devmapper/devmapper.go +++ b/graphdriver/devmapper/devmapper.go @@ -178,15 +178,17 @@ func (t *Task) GetNextTarget(next uintptr) (nextPtr uintptr, start uint64, } func getLoopbackBackingFile(file *osFile) (uint64, uint64, error) { - dev, inode, err := DmGetLoopbackBackingFile(file.Fd()) - if err != 0 { + loopInfo, err := ioctlLoopGetStatus64(file.Fd()) + if err != nil { + utils.Errorf("Error get loopback backing file: %s\n", err) return 0, 0, ErrGetLoopbackBackingFile } - return dev, inode, nil + return loopInfo.loDevice, loopInfo.loInode, nil } func LoopbackSetCapacity(file *osFile) error { - if err := DmLoopbackSetCapacity(file.Fd()); err != 0 { + if err := ioctlLoopSetCapacity(file.Fd(), 0); err != nil { + utils.Errorf("Error loopbackSetCapacity: %s", err) return ErrLoopbackSetCapacity } return nil @@ -276,8 +278,9 @@ func RemoveDevice(name string) error { } func GetBlockDeviceSize(file *osFile) (uint64, error) { - size, errno := DmGetBlockSize(file.Fd()) - if size == -1 || errno != 0 { + size, err := ioctlBlkGetSize64(file.Fd()) + if err != nil { + utils.Errorf("Error getblockdevicesize: %s", err) return 0, ErrGetBlockSize } return uint64(size), nil diff --git a/graphdriver/devmapper/devmapper_wrapper.go b/graphdriver/devmapper/devmapper_wrapper.go index 24d9502b49..1b636d392f 100644 --- a/graphdriver/devmapper/devmapper_wrapper.go +++ b/graphdriver/devmapper/devmapper_wrapper.go @@ -33,7 +33,8 @@ import ( ) type ( - CDmTask C.struct_dm_task + CDmTask C.struct_dm_task + CLoopInfo64 C.struct_loop_info64 LoopInfo64 struct { loDevice uint64 /* ioctl r/o */ @@ -54,39 +55,40 @@ type ( // FIXME: Make sure the values are defined in C const ( - LoopSetFd = C.LOOP_SET_FD - LoopCtlGetFree = C.LOOP_CTL_GET_FREE - LoopSetStatus64 = C.LOOP_SET_STATUS64 - LoopClrFd = C.LOOP_CLR_FD + 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 + 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 + + BlkGetSize64 = C.BLKGETSIZE64 ) var ( - DmGetBlockSize = dmGetBlockSizeFct - DmGetLibraryVersion = dmGetLibraryVersionFct - DmGetNextTarget = dmGetNextTargetFct - DmLogInitVerbose = dmLogInitVerboseFct - DmSetDevDir = dmSetDevDirFct - DmTaskAddTarget = dmTaskAddTargetFct - DmTaskCreate = dmTaskCreateFct - DmTaskDestroy = dmTaskDestroyFct - DmTaskGetInfo = dmTaskGetInfoFct - DmTaskRun = dmTaskRunFct - DmTaskSetAddNode = dmTaskSetAddNodeFct - DmTaskSetCookie = dmTaskSetCookieFct - DmTaskSetMessage = dmTaskSetMessageFct - DmTaskSetName = dmTaskSetNameFct - DmTaskSetRo = dmTaskSetRoFct - DmTaskSetSector = dmTaskSetSectorFct - DmUdevWait = dmUdevWaitFct - GetBlockSize = getBlockSizeFct - LogWithErrnoInit = logWithErrnoInitFct - DmGetLoopbackBackingFile = dmGetLoopbackBackingFileFct - DmLoopbackSetCapacity = dmLoopbackSetCapacityFct + DmGetLibraryVersion = dmGetLibraryVersionFct + DmGetNextTarget = dmGetNextTargetFct + DmLogInitVerbose = dmLogInitVerboseFct + DmSetDevDir = dmSetDevDirFct + DmTaskAddTarget = dmTaskAddTargetFct + DmTaskCreate = dmTaskCreateFct + DmTaskDestroy = dmTaskDestroyFct + DmTaskGetInfo = dmTaskGetInfoFct + DmTaskRun = dmTaskRunFct + DmTaskSetAddNode = dmTaskSetAddNodeFct + DmTaskSetCookie = dmTaskSetCookieFct + DmTaskSetMessage = dmTaskSetMessageFct + DmTaskSetName = dmTaskSetNameFct + DmTaskSetRo = dmTaskSetRoFct + DmTaskSetSector = dmTaskSetSectorFct + DmUdevWait = dmUdevWaitFct + LogWithErrnoInit = logWithErrnoInitFct ) func free(p *C.char) { @@ -185,28 +187,6 @@ func dmGetNextTargetFct(task *CDmTask, next uintptr, start, length *uint64, targ return uintptr(nextp) } -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 getBlockSizeFct(fd uintptr, size *uint64) sysErrno { - _, _, err := sysSyscall(sysSysIoctl, fd, C.BLKGETSIZE64, uintptr(unsafe.Pointer(&size))) - return sysErrno(err) -} - func dmUdevWaitFct(cookie uint) int { return int(C.dm_udev_wait(C.uint32_t(cookie))) } diff --git a/graphdriver/devmapper/ioctl.go b/graphdriver/devmapper/ioctl.go new file mode 100644 index 0000000000..65c6c1a512 --- /dev/null +++ b/graphdriver/devmapper/ioctl.go @@ -0,0 +1,58 @@ +package devmapper + +import ( + "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 { + if _, _, err := sysSyscall(sysSysIoctl, loopFd, LoopSetStatus64, uintptr(unsafe.Pointer(loopInfo))); err != 0 { + return err + } + return nil +} + +func ioctlLoopClrFd(loopFd uintptr) error { + if _, _, err := sysSyscall(sysSysIoctl, loopFd, LoopClrFd, 0); err != 0 { + return err + } + return nil +} + +func ioctlLoopGetStatus64(loopFd uintptr) (*LoopInfo64, error) { + loopInfo := &LoopInfo64{} + + if _, _, err := sysSyscall(sysSysIoctl, loopFd, LoopGetStatus64, uintptr(unsafe.Pointer(loopInfo))); err != 0 { + return nil, err + } + return loopInfo, nil +} + +func ioctlLoopSetCapacity(loopFd uintptr, value int) error { + if _, _, err := sysSyscall(sysSysIoctl, loopFd, LoopSetCapacity, uintptr(value)); err != 0 { + return err + } + return nil +} + +func ioctlBlkGetSize64(fd uintptr) (int64, error) { + var size int64 + if _, _, err := sysSyscall(sysSysIoctl, fd, BlkGetSize64, uintptr(unsafe.Pointer(&size))); err != 0 { + return 0, err + } + return size, nil +} From 8a5d927a5337b1c9a0c8478c4e522ce0ded09b85 Mon Sep 17 00:00:00 2001 From: "Guillaume J. Charmes" Date: Wed, 27 Nov 2013 18:21:17 -0800 Subject: [PATCH 4/7] Check if the target loopback is a block device --- graphdriver/devmapper/attachLoopback.go | 5 +++-- graphdriver/devmapper/sys.go | 7 ++++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/graphdriver/devmapper/attachLoopback.go b/graphdriver/devmapper/attachLoopback.go index 98d0bf5f5b..0d556d81c2 100644 --- a/graphdriver/devmapper/attachLoopback.go +++ b/graphdriver/devmapper/attachLoopback.go @@ -39,8 +39,9 @@ func openNextAvailableLoopback(index int, sparseFile *osFile) (loopFile *osFile, return nil, ErrAttachLoopbackDevice } - // FIXME: Check here if target is a block device (in C: S_ISBLK(mode)) - if fi.IsDir() { + if fi.Mode()&osModeDevice != osModeDevice { + utils.Errorf("Loopback device %s is not a block device.", target) + continue } // Open the targeted loopback (use OpenFile because Open sets O_CLOEXEC) diff --git a/graphdriver/devmapper/sys.go b/graphdriver/devmapper/sys.go index 152e68285b..9cf124a9a5 100644 --- a/graphdriver/devmapper/sys.go +++ b/graphdriver/devmapper/sys.go @@ -46,9 +46,10 @@ const ( sysSysIoctl = syscall.SYS_IOCTL sysEBusy = syscall.EBUSY - osORdOnly = os.O_RDONLY - osORdWr = os.O_RDWR - osOCreate = os.O_CREATE + osORdOnly = os.O_RDONLY + osORdWr = os.O_RDWR + osOCreate = os.O_CREATE + osModeDevice = os.ModeDevice ) func toSysStatT(i interface{}) *sysStatT { From 533067bba47acec0f65dc7e499944b9402bafb04 Mon Sep 17 00:00:00 2001 From: "Guillaume J. Charmes" Date: Thu, 28 Nov 2013 10:37:03 -0800 Subject: [PATCH 5/7] Rename file for consistency --- graphdriver/devmapper/{attachLoopback.go => attach_loopback.go} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename graphdriver/devmapper/{attachLoopback.go => attach_loopback.go} (100%) diff --git a/graphdriver/devmapper/attachLoopback.go b/graphdriver/devmapper/attach_loopback.go similarity index 100% rename from graphdriver/devmapper/attachLoopback.go rename to graphdriver/devmapper/attach_loopback.go From a990b3aeb9640d35e73f99030e03e58e71924ed5 Mon Sep 17 00:00:00 2001 From: "Guillaume J. Charmes" Date: Thu, 28 Nov 2013 11:02:53 -0800 Subject: [PATCH 6/7] Correct comments --- graphdriver/devmapper/attach_loopback.go | 4 ++-- graphdriver/devmapper/sys.go | 4 +--- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/graphdriver/devmapper/attach_loopback.go b/graphdriver/devmapper/attach_loopback.go index 0d556d81c2..f91bd20165 100644 --- a/graphdriver/devmapper/attach_loopback.go +++ b/graphdriver/devmapper/attach_loopback.go @@ -44,7 +44,7 @@ func openNextAvailableLoopback(index int, sparseFile *osFile) (loopFile *osFile, continue } - // Open the targeted loopback (use OpenFile because Open sets O_CLOEXEC) + // OpenFile adds O_CLOEXEC loopFile, err = osOpenFile(target, osORdWr, 0644) if err != nil { utils.Errorf("Error openning loopback device: %s", err) @@ -89,7 +89,7 @@ func attachLoopDevice(sparseName string) (loop *osFile, err error) { utils.Debugf("Error retrieving the next available loopback: %s", err) } - // Open the given sparse file (use OpenFile because Open sets O_CLOEXEC) + // OpenFile adds O_CLOEXEC sparseFile, err := osOpenFile(sparseName, osORdWr, 0644) if err != nil { utils.Errorf("Error openning sparse file %s: %s", sparseName, err) diff --git a/graphdriver/devmapper/sys.go b/graphdriver/devmapper/sys.go index 9cf124a9a5..e06165c824 100644 --- a/graphdriver/devmapper/sys.go +++ b/graphdriver/devmapper/sys.go @@ -34,9 +34,7 @@ var ( osRename = os.Rename osReadlink = os.Readlink - execRun = func(name string, args ...string) error { - return exec.Command(name, args...).Run() - } + execRun = func(name string, args ...string) error { return exec.Command(name, args...).Run() } ) const ( From 261bd0d187cbc8f650d77e5c93872332c290b164 Mon Sep 17 00:00:00 2001 From: "Guillaume J. Charmes" Date: Thu, 28 Nov 2013 11:53:09 -0800 Subject: [PATCH 7/7] Improve devmapper unit tests with syscall/ioctl --- graphdriver/devmapper/devmapper_wrapper.go | 7 +- graphdriver/devmapper/driver_test.go | 124 +++++++++++---------- 2 files changed, 69 insertions(+), 62 deletions(-) diff --git a/graphdriver/devmapper/devmapper_wrapper.go b/graphdriver/devmapper/devmapper_wrapper.go index 1b636d392f..3c195f72ea 100644 --- a/graphdriver/devmapper/devmapper_wrapper.go +++ b/graphdriver/devmapper/devmapper_wrapper.go @@ -54,21 +54,24 @@ type ( ) // FIXME: Make sure the values are defined in C +// IOCTL consts const ( + BlkGetSize64 = C.BLKGETSIZE64 + 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 - - BlkGetSize64 = C.BLKGETSIZE64 ) var ( diff --git a/graphdriver/devmapper/driver_test.go b/graphdriver/devmapper/driver_test.go index 3204575dd9..3950152fb7 100644 --- a/graphdriver/devmapper/driver_test.go +++ b/graphdriver/devmapper/driver_test.go @@ -55,12 +55,6 @@ func denyAllDevmapper() { DmGetNextTarget = func(task *CDmTask, next uintptr, start, length *uint64, target, params *string) uintptr { panic("DmGetNextTarget: this method should not be called here") } - DmAttachLoopDevice = func(filename string, fd *int) string { - panic("DmAttachLoopDevice: this method should not be called here") - } - DmGetBlockSize = func(fd uintptr) (int64, sysErrno) { - panic("DmGetBlockSize: this method should not be called here") - } DmUdevWait = func(cookie uint) int { panic("DmUdevWait: this method should not be called here") } @@ -76,9 +70,6 @@ func denyAllDevmapper() { DmTaskDestroy = func(task *CDmTask) { panic("DmTaskDestroy: this method should not be called here") } - GetBlockSize = func(fd uintptr, size *uint64) sysErrno { - panic("GetBlockSize: this method should not be called here") - } LogWithErrnoInit = func() { panic("LogWithErrnoInit: this method should not be called here") } @@ -155,11 +146,10 @@ func (r Set) Assert(t *testing.T, names ...string) { func TestInit(t *testing.T) { var ( - calls = make(Set) - devicesAttached = make(Set) - taskMessages = make(Set) - taskTypes = make(Set) - home = mkTestDirectory(t) + calls = make(Set) + taskMessages = make(Set) + taskTypes = make(Set) + home = mkTestDirectory(t) ) defer osRemoveAll(home) @@ -233,29 +223,6 @@ func TestInit(t *testing.T) { taskMessages[message] = true return 1 } - var ( - fakeDataLoop = "/dev/loop42" - fakeMetadataLoop = "/dev/loop43" - fakeDataLoopFd = 42 - fakeMetadataLoopFd = 43 - ) - var attachCount int - DmAttachLoopDevice = func(filename string, fd *int) string { - calls["DmAttachLoopDevice"] = true - if _, exists := devicesAttached[filename]; exists { - t.Fatalf("Already attached %s", filename) - } - devicesAttached[filename] = true - // This will crash if fd is not dereferenceable - if attachCount == 0 { - attachCount++ - *fd = fakeDataLoopFd - return fakeDataLoop - } else { - *fd = fakeMetadataLoopFd - return fakeMetadataLoop - } - } DmTaskDestroy = func(task *CDmTask) { calls["DmTaskDestroy"] = true expectedTask := &task1 @@ -263,14 +230,6 @@ func TestInit(t *testing.T) { t.Fatalf("Wrong libdevmapper call\nExpected: DmTaskDestroy(%v)\nReceived: DmTaskDestroy(%v)\n", expectedTask, task) } } - fakeBlockSize := int64(4242 * 512) - DmGetBlockSize = func(fd uintptr) (int64, sysErrno) { - calls["DmGetBlockSize"] = true - if expectedFd := uintptr(42); fd != expectedFd { - t.Fatalf("Wrong libdevmapper call\nExpected: DmGetBlockSize(%v)\nReceived: DmGetBlockSize(%v)\n", expectedFd, fd) - } - return fakeBlockSize, 0 - } DmTaskAddTarget = func(task *CDmTask, start, size uint64, ttype, params string) int { calls["DmTaskSetTarget"] = true expectedTask := &task1 @@ -345,11 +304,9 @@ func TestInit(t *testing.T) { "DmTaskSetName", "DmTaskRun", "DmTaskGetInfo", - "DmAttachLoopDevice", "DmTaskDestroy", "execRun", "DmTaskCreate", - "DmGetBlockSize", "DmTaskSetTarget", "DmTaskSetCookie", "DmUdevWait", @@ -357,7 +314,6 @@ func TestInit(t *testing.T) { "DmTaskSetMessage", "DmTaskSetAddNode", ) - devicesAttached.Assert(t, path.Join(home, "devicemapper", "data"), path.Join(home, "devicemapper", "metadata")) taskTypes.Assert(t, "0", "6", "17") taskMessages.Assert(t, "create_thin 0", "set_transaction_id 0 1") } @@ -408,17 +364,9 @@ func mockAllDevmapper(calls Set) { calls["DmTaskSetMessage"] = true return 1 } - DmAttachLoopDevice = func(filename string, fd *int) string { - calls["DmAttachLoopDevice"] = true - return "/dev/loop42" - } DmTaskDestroy = func(task *CDmTask) { calls["DmTaskDestroy"] = true } - DmGetBlockSize = func(fd uintptr) (int64, sysErrno) { - calls["DmGetBlockSize"] = true - return int64(4242 * 512), 0 - } DmTaskAddTarget = func(task *CDmTask, start, size uint64, ttype, params string) int { calls["DmTaskSetTarget"] = true return 1 @@ -489,6 +437,32 @@ func TestDriverCreate(t *testing.T) { return false, nil } + sysSyscall = func(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno) { + calls["sysSyscall"] = true + if trap != sysSysIoctl { + t.Fatalf("Unexpected syscall. Expecting SYS_IOCTL, received: %d", trap) + } + switch a2 { + case LoopSetFd: + calls["ioctl.loopsetfd"] = true + case LoopCtlGetFree: + calls["ioctl.loopctlgetfree"] = true + case LoopGetStatus64: + calls["ioctl.loopgetstatus"] = true + case LoopSetStatus64: + calls["ioctl.loopsetstatus"] = true + case LoopClrFd: + calls["ioctl.loopclrfd"] = true + case LoopSetCapacity: + calls["ioctl.loopsetcapacity"] = true + case BlkGetSize64: + calls["ioctl.blkgetsize"] = true + default: + t.Fatalf("Unexpected IOCTL. Received %d", a2) + } + return 0, 0, 0 + } + func() { d := newDriver(t) @@ -498,16 +472,18 @@ func TestDriverCreate(t *testing.T) { "DmTaskSetName", "DmTaskRun", "DmTaskGetInfo", - "DmAttachLoopDevice", "execRun", "DmTaskCreate", - "DmGetBlockSize", "DmTaskSetTarget", "DmTaskSetCookie", "DmUdevWait", "DmTaskSetSector", "DmTaskSetMessage", "DmTaskSetAddNode", + "sysSyscall", + "ioctl.blkgetsize", + "ioctl.loopsetfd", + "ioctl.loopsetstatus", ) if err := d.Create("1", ""); err != nil { @@ -579,6 +555,32 @@ func TestDriverRemove(t *testing.T) { return false, nil } + sysSyscall = func(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno) { + calls["sysSyscall"] = true + if trap != sysSysIoctl { + t.Fatalf("Unexpected syscall. Expecting SYS_IOCTL, received: %d", trap) + } + switch a2 { + case LoopSetFd: + calls["ioctl.loopsetfd"] = true + case LoopCtlGetFree: + calls["ioctl.loopctlgetfree"] = true + case LoopGetStatus64: + calls["ioctl.loopgetstatus"] = true + case LoopSetStatus64: + calls["ioctl.loopsetstatus"] = true + case LoopClrFd: + calls["ioctl.loopclrfd"] = true + case LoopSetCapacity: + calls["ioctl.loopsetcapacity"] = true + case BlkGetSize64: + calls["ioctl.blkgetsize"] = true + default: + t.Fatalf("Unexpected IOCTL. Received %d", a2) + } + return 0, 0, 0 + } + func() { d := newDriver(t) @@ -588,16 +590,18 @@ func TestDriverRemove(t *testing.T) { "DmTaskSetName", "DmTaskRun", "DmTaskGetInfo", - "DmAttachLoopDevice", "execRun", "DmTaskCreate", - "DmGetBlockSize", "DmTaskSetTarget", "DmTaskSetCookie", "DmUdevWait", "DmTaskSetSector", "DmTaskSetMessage", "DmTaskSetAddNode", + "sysSyscall", + "ioctl.blkgetsize", + "ioctl.loopsetfd", + "ioctl.loopsetstatus", ) if err := d.Create("1", ""); err != nil {