diff --git a/graphdriver/devmapper/deviceset.go b/graphdriver/devmapper/deviceset.go index e24840634d..d232b510d2 100644 --- a/graphdriver/devmapper/deviceset.go +++ b/graphdriver/devmapper/deviceset.go @@ -6,13 +6,10 @@ import ( "github.com/dotcloud/docker/utils" "io" "io/ioutil" - "os" - "os/exec" "path" "path/filepath" "strconv" "sync" - "syscall" "time" ) @@ -105,7 +102,7 @@ func (devices *DeviceSet) hasImage(name string) bool { dirname := devices.loopbackDir() filename := path.Join(dirname, name) - _, err := os.Stat(filename) + _, err := osStat(filename) return err == nil } @@ -117,16 +114,16 @@ func (devices *DeviceSet) ensureImage(name string, size int64) (string, error) { dirname := devices.loopbackDir() filename := path.Join(dirname, name) - if err := os.MkdirAll(dirname, 0700); err != nil && !os.IsExist(err) { + if err := osMkdirAll(dirname, 0700); err != nil && !osIsExist(err) { return "", err } - if _, err := os.Stat(filename); err != nil { - if !os.IsNotExist(err) { + if _, err := osStat(filename); err != nil { + if !osIsNotExist(err) { return "", err } utils.Debugf("Creating loopback file %s for device-manage use", filename) - file, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE, 0600) + file, err := osOpenFile(filename, osORdWr|osOCreate, 0600) if err != nil { return "", err } @@ -174,7 +171,7 @@ func (devices *DeviceSet) saveMetadata() error { if err := tmpFile.Close(); err != nil { return fmt.Errorf("Error closing metadata file %s: %s", tmpFile.Name(), err) } - if err := os.Rename(tmpFile.Name(), devices.jsonFile()); err != nil { + if err := osRename(tmpFile.Name(), devices.jsonFile()); err != nil { return fmt.Errorf("Error committing metadata file", err) } @@ -225,9 +222,9 @@ func (devices *DeviceSet) activateDeviceIfNeeded(hash string) error { func (devices *DeviceSet) createFilesystem(info *DevInfo) error { devname := info.DevName() - err := exec.Command("mkfs.ext4", "-E", "discard,lazy_itable_init=0,lazy_journal_init=0", devname).Run() + err := execRun("mkfs.ext4", "-E", "discard,lazy_itable_init=0,lazy_journal_init=0", devname) if err != nil { - err = exec.Command("mkfs.ext4", "-E", "discard,lazy_itable_init=0", devname).Run() + err = execRun("mkfs.ext4", "-E", "discard,lazy_itable_init=0", devname) } if err != nil { utils.Debugf("\n--->Err: %s\n", err) @@ -252,7 +249,7 @@ func (devices *DeviceSet) loadMetaData() error { devices.NewTransactionId = devices.TransactionId jsonData, err := ioutil.ReadFile(devices.jsonFile()) - if err != nil && !os.IsNotExist(err) { + if err != nil && !osIsNotExist(err) { utils.Debugf("\n--->Err: %s\n", err) return err } @@ -337,14 +334,13 @@ func (devices *DeviceSet) setupBaseImage() error { } func setCloseOnExec(name string) { - fileInfos, _ := ioutil.ReadDir("/proc/self/fd") - if fileInfos != nil { + if fileInfos, _ := ioutil.ReadDir("/proc/self/fd"); fileInfos != nil { for _, i := range fileInfos { - link, _ := os.Readlink(filepath.Join("/proc/self/fd", i.Name())) + link, _ := osReadlink(filepath.Join("/proc/self/fd", i.Name())) if link == name { fd, err := strconv.Atoi(i.Name()) if err == nil { - syscall.CloseOnExec(fd) + sysCloseOnExec(fd) } } } @@ -372,7 +368,7 @@ func (devices *DeviceSet) ResizePool(size int64) error { datafilename := path.Join(dirname, "data") metadatafilename := path.Join(dirname, "metadata") - datafile, err := os.OpenFile(datafilename, os.O_RDWR, 0) + datafile, err := osOpenFile(datafilename, osORdWr, 0) if datafile == nil { return err } @@ -387,19 +383,19 @@ func (devices *DeviceSet) ResizePool(size int64) error { return fmt.Errorf("Can't shrink file") } - dataloopback := FindLoopDeviceFor(datafile) + dataloopback := FindLoopDeviceFor(&osFile{File: datafile}) if dataloopback == nil { return fmt.Errorf("Unable to find loopback mount for: %s", datafilename) } defer dataloopback.Close() - metadatafile, err := os.OpenFile(metadatafilename, os.O_RDWR, 0) + metadatafile, err := osOpenFile(metadatafilename, osORdWr, 0) if metadatafile == nil { return err } defer metadatafile.Close() - metadataloopback := FindLoopDeviceFor(metadatafile) + metadataloopback := FindLoopDeviceFor(&osFile{File: metadatafile}) if metadataloopback == nil { return fmt.Errorf("Unable to find loopback mount for: %s", metadatafilename) } @@ -464,11 +460,11 @@ func (devices *DeviceSet) initDevmapper(doInit bool) error { // Set the device prefix from the device id and inode of the docker root dir - st, err := os.Stat(devices.root) + st, err := osStat(devices.root) if err != nil { return fmt.Errorf("Error looking up dir %s: %s", devices.root, err) } - sysSt := st.Sys().(*syscall.Stat_t) + sysSt := toSysStatT(st.Sys()) // "reg-" stands for "regular file". // In the future we might use "dev-" for "device file", etc. // docker-maj,min[-inode] stands for: @@ -708,15 +704,16 @@ func (devices *DeviceSet) byHash(hash string) (devname string, err error) { } func (devices *DeviceSet) Shutdown() error { - utils.Debugf("[deviceset %s] shutdown()", devices.devicePrefix) - defer utils.Debugf("[deviceset %s] shutdown END", devices.devicePrefix) devices.Lock() - utils.Debugf("[devmapper] Shutting down DeviceSet: %s", devices.root) defer devices.Unlock() + utils.Debugf("[deviceset %s] shutdown()", devices.devicePrefix) + utils.Debugf("[devmapper] Shutting down DeviceSet: %s", devices.root) + defer utils.Debugf("[deviceset %s] shutdown END", devices.devicePrefix) + for path, count := range devices.activeMounts { for i := count; i > 0; i-- { - if err := syscall.Unmount(path, 0); err != nil { + if err := sysUnmount(path, 0); err != nil { utils.Debugf("Shutdown unmounting %s, error: %s\n", path, err) } } @@ -752,15 +749,15 @@ func (devices *DeviceSet) MountDevice(hash, path string, readOnly bool) error { info := devices.Devices[hash] - var flags uintptr = syscall.MS_MGC_VAL + var flags uintptr = sysMsMgcVal if readOnly { - flags = flags | syscall.MS_RDONLY + flags = flags | sysMsRdOnly } - err := syscall.Mount(info.DevName(), path, "ext4", flags, "discard") - if err != nil && err == syscall.EINVAL { - err = syscall.Mount(info.DevName(), path, "ext4", flags, "") + err := sysMount(info.DevName(), path, "ext4", flags, "discard") + if err != nil && err == sysEInval { + err = sysMount(info.DevName(), path, "ext4", flags, "") } if err != nil { return fmt.Errorf("Error mounting '%s' on '%s': %s", info.DevName(), path, err) @@ -779,7 +776,7 @@ func (devices *DeviceSet) UnmountDevice(hash, path string, deactivate bool) erro defer devices.Unlock() utils.Debugf("[devmapper] Unmount(%s)", path) - if err := syscall.Unmount(path, 0); err != nil { + if err := sysUnmount(path, 0); err != nil { utils.Debugf("\n--->Err: %s\n", err) return err } diff --git a/graphdriver/devmapper/devmapper.go b/graphdriver/devmapper/devmapper.go index 6901ba9a6e..de82bde0fd 100644 --- a/graphdriver/devmapper/devmapper.go +++ b/graphdriver/devmapper/devmapper.go @@ -4,9 +4,7 @@ import ( "errors" "fmt" "github.com/dotcloud/docker/utils" - "os" "runtime" - "syscall" ) type DevmapperLogger interface { @@ -49,7 +47,6 @@ var ( ErrTaskAddTarget = errors.New("dm_task_add_target failed") ErrTaskSetSector = errors.New("dm_task_set_sector 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") @@ -86,7 +83,7 @@ type ( func (t *Task) destroy() { if t != nil { - DmTaskDestory(t.unmanaged) + DmTaskDestroy(t.unmanaged) runtime.SetFinalizer(t, nil) } } @@ -180,16 +177,16 @@ func (t *Task) GetNextTarget(next uintptr) (nextPtr uintptr, start uint64, start, length, targetType, params } -func AttachLoopDevice(filename string) (*os.File, error) { +func AttachLoopDevice(filename string) (*osFile, error) { var fd int res := DmAttachLoopDevice(filename, &fd) if res == "" { return nil, ErrAttachLoopbackDevice } - return os.NewFile(uintptr(fd), res), nil + return &osFile{File: osNewFile(uintptr(fd), res)}, nil } -func getLoopbackBackingFile(file *os.File) (uint64, uint64, error) { +func getLoopbackBackingFile(file *osFile) (uint64, uint64, error) { dev, inode, err := dmGetLoopbackBackingFile(file.Fd()) if err != 0 { return 0, 0, ErrGetLoopbackBackingFile @@ -197,7 +194,7 @@ func getLoopbackBackingFile(file *os.File) (uint64, uint64, error) { return dev, inode, nil } -func LoopbackSetCapacity(file *os.File) error { +func LoopbackSetCapacity(file *osFile) error { err := dmLoopbackSetCapacity(file.Fd()) if err != 0 { return ErrLoopbackSetCapacity @@ -205,20 +202,20 @@ func LoopbackSetCapacity(file *os.File) error { return nil } -func FindLoopDeviceFor(file *os.File) *os.File { +func FindLoopDeviceFor(file *osFile) *osFile { stat, err := file.Stat() if err != nil { return nil } - targetInode := stat.Sys().(*syscall.Stat_t).Ino - targetDevice := stat.Sys().(*syscall.Stat_t).Dev + targetInode := stat.Sys().(*sysStatT).Ino + targetDevice := stat.Sys().(*sysStatT).Dev for i := 0; true; i++ { path := fmt.Sprintf("/dev/loop%d", i) - file, err := os.OpenFile(path, os.O_RDWR, 0) + file, err := osOpenFile(path, osORdWr, 0) if err != nil { - if os.IsNotExist(err) { + if osIsNotExist(err) { return nil } @@ -227,9 +224,9 @@ func FindLoopDeviceFor(file *os.File) *os.File { continue } - dev, inode, err := getLoopbackBackingFile(file) + dev, inode, err := getLoopbackBackingFile(&osFile{File: file}) if err == nil && dev == targetDevice && inode == targetInode { - return file + return &osFile{File: file} } file.Close() @@ -289,7 +286,7 @@ func RemoveDevice(name string) error { return nil } -func GetBlockDeviceSize(file *os.File) (uint64, error) { +func GetBlockDeviceSize(file *osFile) (uint64, error) { size, errno := DmGetBlockSize(file.Fd()) if size == -1 || errno != 0 { return 0, ErrGetBlockSize @@ -298,7 +295,7 @@ func GetBlockDeviceSize(file *os.File) (uint64, error) { } // This is the programmatic example of "dmsetup create" -func createPool(poolName string, dataFile *os.File, metadataFile *os.File) error { +func createPool(poolName string, dataFile, metadataFile *osFile) error { task, err := createTask(DeviceCreate, poolName) if task == nil { return err @@ -328,7 +325,7 @@ func createPool(poolName string, dataFile *os.File, metadataFile *os.File) error return nil } -func reloadPool(poolName string, dataFile *os.File, metadataFile *os.File) error { +func reloadPool(poolName string, dataFile, metadataFile *osFile) error { task, err := createTask(DeviceReload, poolName) if task == nil { return err @@ -394,8 +391,8 @@ func getStatus(name string) (uint64, uint64, string, string, error) { return 0, 0, "", "", fmt.Errorf("Non existing device %s", name) } - _, start, length, target_type, params := task.GetNextTarget(0) - return start, length, target_type, params, nil + _, start, length, targetType, params := task.GetNextTarget(0) + return start, length, targetType, params, nil } func setTransactionId(poolName string, oldId uint64, newId uint64) error { diff --git a/graphdriver/devmapper/devmapper_doc.go b/graphdriver/devmapper/devmapper_doc.go new file mode 100644 index 0000000000..c1c3e3891b --- /dev/null +++ b/graphdriver/devmapper/devmapper_doc.go @@ -0,0 +1,106 @@ +package devmapper + +// Definition of struct dm_task and sub structures (from lvm2) +// +// struct dm_ioctl { +// /* +// * The version number is made up of three parts: +// * major - no backward or forward compatibility, +// * minor - only backwards compatible, +// * patch - both backwards and forwards compatible. +// * +// * All clients of the ioctl interface should fill in the +// * version number of the interface that they were +// * compiled with. +// * +// * All recognised ioctl commands (ie. those that don't +// * return -ENOTTY) fill out this field, even if the +// * command failed. +// */ +// uint32_t version[3]; /* in/out */ +// uint32_t data_size; /* total size of data passed in +// * including this struct */ + +// uint32_t data_start; /* offset to start of data +// * relative to start of this struct */ + +// uint32_t target_count; /* in/out */ +// int32_t open_count; /* out */ +// uint32_t flags; /* in/out */ + +// /* +// * event_nr holds either the event number (input and output) or the +// * udev cookie value (input only). +// * The DM_DEV_WAIT ioctl takes an event number as input. +// * The DM_SUSPEND, DM_DEV_REMOVE and DM_DEV_RENAME ioctls +// * use the field as a cookie to return in the DM_COOKIE +// * variable with the uevents they issue. +// * For output, the ioctls return the event number, not the cookie. +// */ +// uint32_t event_nr; /* in/out */ +// uint32_t padding; + +// uint64_t dev; /* in/out */ + +// char name[DM_NAME_LEN]; /* device name */ +// char uuid[DM_UUID_LEN]; /* unique identifier for +// * the block device */ +// char data[7]; /* padding or data */ +// }; + +// struct target { +// uint64_t start; +// uint64_t length; +// char *type; +// char *params; + +// struct target *next; +// }; + +// typedef enum { +// DM_ADD_NODE_ON_RESUME, /* add /dev/mapper node with dmsetup resume */ +// DM_ADD_NODE_ON_CREATE /* add /dev/mapper node with dmsetup create */ +// } dm_add_node_t; + +// struct dm_task { +// int type; +// char *dev_name; +// char *mangled_dev_name; + +// struct target *head, *tail; + +// int read_only; +// uint32_t event_nr; +// int major; +// int minor; +// int allow_default_major_fallback; +// uid_t uid; +// gid_t gid; +// mode_t mode; +// uint32_t read_ahead; +// uint32_t read_ahead_flags; +// union { +// struct dm_ioctl *v4; +// } dmi; +// char *newname; +// char *message; +// char *geometry; +// uint64_t sector; +// int no_flush; +// int no_open_count; +// int skip_lockfs; +// int query_inactive_table; +// int suppress_identical_reload; +// dm_add_node_t add_node; +// uint64_t existing_table_size; +// int cookie_set; +// int new_uuid; +// int secure_data; +// int retry_remove; +// int enable_checks; +// int expected_errno; + +// char *uuid; +// char *mangled_uuid; +// }; +// diff --git a/graphdriver/devmapper/devmapper_test.go b/graphdriver/devmapper/devmapper_test.go index 8d93ab30b1..ce22864361 100644 --- a/graphdriver/devmapper/devmapper_test.go +++ b/graphdriver/devmapper/devmapper_test.go @@ -1,11 +1,11 @@ package devmapper import ( - "syscall" "testing" ) func TestTaskCreate(t *testing.T) { + t.Skip("FIXME: not a unit test") // Test success taskCreate(t, DeviceInfo) @@ -18,6 +18,7 @@ func TestTaskCreate(t *testing.T) { } func TestTaskRun(t *testing.T) { + t.Skip("FIXME: not a unit test") task := taskCreate(t, DeviceInfo) // Test success @@ -46,6 +47,7 @@ func TestTaskRun(t *testing.T) { } func TestTaskSetName(t *testing.T) { + t.Skip("FIXME: not a unit test") task := taskCreate(t, DeviceInfo) // Test success @@ -63,6 +65,7 @@ func TestTaskSetName(t *testing.T) { } func TestTaskSetMessage(t *testing.T) { + t.Skip("FIXME: not a unit test") task := taskCreate(t, DeviceInfo) // Test success @@ -80,6 +83,7 @@ func TestTaskSetMessage(t *testing.T) { } func TestTaskSetSector(t *testing.T) { + t.Skip("FIXME: not a unit test") task := taskCreate(t, DeviceInfo) // Test success @@ -97,6 +101,7 @@ func TestTaskSetSector(t *testing.T) { } func TestTaskSetCookie(t *testing.T) { + t.Skip("FIXME: not a unit test") var ( cookie uint = 0 task = taskCreate(t, DeviceInfo) @@ -121,6 +126,7 @@ func TestTaskSetCookie(t *testing.T) { } func TestTaskSetAddNode(t *testing.T) { + t.Skip("FIXME: not a unit test") task := taskCreate(t, DeviceInfo) // Test success @@ -142,6 +148,7 @@ func TestTaskSetAddNode(t *testing.T) { } func TestTaskSetRo(t *testing.T) { + t.Skip("FIXME: not a unit test") task := taskCreate(t, DeviceInfo) // Test success @@ -159,6 +166,7 @@ func TestTaskSetRo(t *testing.T) { } func TestTaskAddTarget(t *testing.T) { + t.Skip("FIXME: not a unit test") task := taskCreate(t, DeviceInfo) // Test success @@ -247,10 +255,6 @@ func dmTaskAddTargetFail(task *CDmTask, return -1 } -func dmTaskGetDriverVersionFail(task *CDmTask, version *string) int { - return -1 -} - func dmTaskGetInfoFail(task *CDmTask, info *Info) int { return -1 } @@ -264,14 +268,10 @@ func dmAttachLoopDeviceFail(filename string, fd *int) string { return "" } -func sysGetBlockSizeFail(fd uintptr, size *uint64) syscall.Errno { +func sysGetBlockSizeFail(fd uintptr, size *uint64) sysErrno { return 1 } -func dmGetBlockSizeFail(fd uintptr) int64 { - return -1 -} - func dmUdevWaitFail(cookie uint) int { return -1 } diff --git a/graphdriver/devmapper/devmapper_wrapper.go b/graphdriver/devmapper/devmapper_wrapper.go index f9a7d039cf..1f28412197 100644 --- a/graphdriver/devmapper/devmapper_wrapper.go +++ b/graphdriver/devmapper/devmapper_wrapper.go @@ -140,7 +140,6 @@ static void log_with_errno_init() import "C" import ( - "syscall" "unsafe" ) @@ -149,26 +148,26 @@ type ( ) var ( - DmTaskDestory = dmTaskDestroyFct - DmTaskCreate = dmTaskCreateFct - DmTaskRun = dmTaskRunFct - DmTaskSetName = dmTaskSetNameFct - DmTaskSetMessage = dmTaskSetMessageFct - DmTaskSetSector = dmTaskSetSectorFct - DmTaskSetCookie = dmTaskSetCookieFct - DmTaskSetAddNode = dmTaskSetAddNodeFct - DmTaskSetRo = dmTaskSetRoFct - DmTaskAddTarget = dmTaskAddTargetFct - DmTaskGetInfo = dmTaskGetInfoFct - DmGetNextTarget = dmGetNextTargetFct - DmGetBlockSize = dmGetBlockSizeFct DmAttachLoopDevice = dmAttachLoopDeviceFct - DmUdevWait = dmUdevWaitFct + DmGetBlockSize = dmGetBlockSizeFct + DmGetLibraryVersion = dmGetLibraryVersionFct + DmGetNextTarget = dmGetNextTargetFct DmLogInitVerbose = dmLogInitVerboseFct DmSetDevDir = dmSetDevDirFct - DmGetLibraryVersion = dmGetLibraryVersionFct - LogWithErrnoInit = logWithErrnoInitFct + 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 ) func free(p *C.char) { @@ -239,23 +238,22 @@ func dmTaskAddTargetFct(task *CDmTask, C.uint64_t(start), C.uint64_t(size), Cttype, Cparams)) } -func dmGetLoopbackBackingFile(fd uintptr) (uint64, uint64, syscall.Errno) { +func dmGetLoopbackBackingFile(fd uintptr) (uint64, uint64, sysErrno) { var lo64 C.struct_loop_info64 - _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, C.LOOP_GET_STATUS64, + _, _, err := sysSyscall(sysSysIoctl, fd, C.LOOP_GET_STATUS64, uintptr(unsafe.Pointer(&lo64))) - return uint64(lo64.lo_device), uint64(lo64.lo_inode), err + return uint64(lo64.lo_device), uint64(lo64.lo_inode), sysErrno(err) } -func dmLoopbackSetCapacity(fd uintptr) syscall.Errno { - _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, C.LOOP_SET_CAPACITY, 0) - return err +func dmLoopbackSetCapacity(fd uintptr) sysErrno { + _, _, err := sysSyscall(sysSysIoctl, fd, C.LOOP_SET_CAPACITY, 0) + return sysErrno(err) } -func dmGetBlockSizeFct(fd uintptr) (int64, syscall.Errno) { +func dmGetBlockSizeFct(fd uintptr) (int64, sysErrno) { var size int64 - _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, C.BLKGETSIZE64, - uintptr(unsafe.Pointer(&size))) - return size, err + _, _, err := sysSyscall(sysSysIoctl, fd, C.BLKGETSIZE64, uintptr(unsafe.Pointer(&size))) + return size, sysErrno(err) } func dmTaskGetInfoFct(task *CDmTask, info *Info) int { @@ -275,9 +273,7 @@ func dmTaskGetInfoFct(task *CDmTask, info *Info) int { return int(C.dm_task_get_info((*C.struct_dm_task)(task), &Cinfo)) } -func dmGetNextTargetFct(task *CDmTask, next uintptr, start, length *uint64, - target, params *string) uintptr { - +func dmGetNextTargetFct(task *CDmTask, next uintptr, start, length *uint64, target, params *string) uintptr { var ( Cstart, Clength C.uint64_t CtargetType, Cparams *C.char @@ -288,6 +284,7 @@ func dmGetNextTargetFct(task *CDmTask, next uintptr, start, length *uint64, *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) @@ -307,10 +304,9 @@ func dmAttachLoopDeviceFct(filename string, fd *int) string { return C.GoString(ret) } -func getBlockSizeFct(fd uintptr, size *uint64) syscall.Errno { - _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, C.BLKGETSIZE64, - uintptr(unsafe.Pointer(&size))) - return 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 { diff --git a/graphdriver/devmapper/driver.go b/graphdriver/devmapper/driver.go index 55b8e3e720..b08d5768ef 100644 --- a/graphdriver/devmapper/driver.go +++ b/graphdriver/devmapper/driver.go @@ -4,7 +4,6 @@ import ( "fmt" "github.com/dotcloud/docker/graphdriver" "io/ioutil" - "os" "path" ) @@ -22,7 +21,7 @@ type Driver struct { home string } -func Init(home string) (graphdriver.Driver, error) { +var Init = func(home string) (graphdriver.Driver, error) { deviceSet, err := NewDeviceSet(home, true) if err != nil { return nil, err @@ -57,7 +56,7 @@ func (d *Driver) Cleanup() error { return d.DeviceSet.Shutdown() } -func (d *Driver) Create(id string, parent string) error { +func (d *Driver) Create(id, parent string) error { if err := d.DeviceSet.AddDevice(id, parent); err != nil { return err } @@ -67,7 +66,7 @@ func (d *Driver) Create(id string, parent string) error { return err } - if err := os.MkdirAll(path.Join(mp, "rootfs"), 0755); err != nil && !os.IsExist(err) { + if err := osMkdirAll(path.Join(mp, "rootfs"), 0755); err != nil && !osIsExist(err) { return err } @@ -98,7 +97,7 @@ func (d *Driver) Get(id string) (string, error) { func (d *Driver) mount(id, mountPoint string) error { // Create the target directories if they don't exist - if err := os.MkdirAll(mountPoint, 0755); err != nil && !os.IsExist(err) { + if err := osMkdirAll(mountPoint, 0755); err != nil && !osIsExist(err) { return err } // If mountpoint is already mounted, do nothing diff --git a/graphdriver/devmapper/driver_test.go b/graphdriver/devmapper/driver_test.go index f8704950ad..3204575dd9 100644 --- a/graphdriver/devmapper/driver_test.go +++ b/graphdriver/devmapper/driver_test.go @@ -1,9 +1,13 @@ package devmapper import ( + "fmt" + "github.com/dotcloud/docker/graphdriver" "io/ioutil" - "os" "path" + "runtime" + "strings" + "syscall" "testing" ) @@ -12,7 +16,105 @@ func init() { DefaultDataLoopbackSize = 300 * 1024 * 1024 DefaultMetaDataLoopbackSize = 200 * 1024 * 1024 DefaultBaseFsSize = 300 * 1024 * 1024 +} +// denyAllDevmapper mocks all calls to libdevmapper in the unit tests, and denies them by default +func denyAllDevmapper() { + // Hijack all calls to libdevmapper with default panics. + // Authorized calls are selectively hijacked in each tests. + DmTaskCreate = func(t int) *CDmTask { + panic("DmTaskCreate: this method should not be called here") + } + DmTaskRun = func(task *CDmTask) int { + panic("DmTaskRun: this method should not be called here") + } + DmTaskSetName = func(task *CDmTask, name string) int { + panic("DmTaskSetName: this method should not be called here") + } + DmTaskSetMessage = func(task *CDmTask, message string) int { + panic("DmTaskSetMessage: this method should not be called here") + } + DmTaskSetSector = func(task *CDmTask, sector uint64) int { + panic("DmTaskSetSector: this method should not be called here") + } + DmTaskSetCookie = func(task *CDmTask, cookie *uint, flags uint16) int { + panic("DmTaskSetCookie: this method should not be called here") + } + DmTaskSetAddNode = func(task *CDmTask, addNode AddNodeType) int { + panic("DmTaskSetAddNode: this method should not be called here") + } + DmTaskSetRo = func(task *CDmTask) int { + panic("DmTaskSetRo: this method should not be called here") + } + DmTaskAddTarget = func(task *CDmTask, start, size uint64, ttype, params string) int { + panic("DmTaskAddTarget: this method should not be called here") + } + DmTaskGetInfo = func(task *CDmTask, info *Info) int { + panic("DmTaskGetInfo: this method should not be called here") + } + 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") + } + DmSetDevDir = func(dir string) int { + panic("DmSetDevDir: this method should not be called here") + } + DmGetLibraryVersion = func(version *string) int { + panic("DmGetLibraryVersion: this method should not be called here") + } + DmLogInitVerbose = func(level int) { + panic("DmLogInitVerbose: this method should not be called here") + } + 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") + } +} + +func denyAllSyscall() { + sysMount = func(source, target, fstype string, flags uintptr, data string) (err error) { + panic("sysMount: this method should not be called here") + } + sysUnmount = func(target string, flags int) (err error) { + panic("sysUnmount: this method should not be called here") + } + sysCloseOnExec = func(fd int) { + panic("sysCloseOnExec: this method should not be called here") + } + sysSyscall = func(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno) { + panic("sysSyscall: this method should not be called here") + } + // Not a syscall, but forbidding it here anyway + Mounted = func(mnt string) (bool, error) { + panic("devmapper.Mounted: this method should not be called here") + } + // osOpenFile = os.OpenFile + // osNewFile = os.NewFile + // osCreate = os.Create + // osStat = os.Stat + // osIsNotExist = os.IsNotExist + // osIsExist = os.IsExist + // osMkdirAll = os.MkdirAll + // osRemoveAll = os.RemoveAll + // osRename = os.Rename + // osReadlink = os.Readlink + + // execRun = func(name string, args ...string) error { + // return exec.Command(name, args...).Run() + // } } func mkTestDirectory(t *testing.T) string { @@ -34,72 +136,521 @@ func newDriver(t *testing.T) *Driver { func cleanup(d *Driver) { d.Cleanup() - os.RemoveAll(d.home) + osRemoveAll(d.home) +} + +type Set map[string]bool + +func (r Set) Assert(t *testing.T, names ...string) { + for _, key := range names { + if _, exists := r[key]; !exists { + t.Fatalf("Key not set: %s", key) + } + delete(r, key) + } + if len(r) != 0 { + t.Fatalf("Unexpected keys: %v", r) + } } func TestInit(t *testing.T) { - home := mkTestDirectory(t) - defer os.RemoveAll(home) - driver, err := Init(home) - if err != nil { - t.Fatal(err) - } - defer func() { - if err := driver.Cleanup(); err != nil { + var ( + calls = make(Set) + devicesAttached = make(Set) + taskMessages = make(Set) + taskTypes = make(Set) + home = mkTestDirectory(t) + ) + defer osRemoveAll(home) + + func() { + denyAllDevmapper() + DmSetDevDir = func(dir string) int { + calls["DmSetDevDir"] = true + expectedDir := "/dev" + if dir != expectedDir { + t.Fatalf("Wrong libdevmapper call\nExpected: DmSetDevDir(%v)\nReceived: DmSetDevDir(%v)\n", expectedDir, dir) + } + return 0 + } + LogWithErrnoInit = func() { + calls["DmLogWithErrnoInit"] = true + } + var task1 CDmTask + DmTaskCreate = func(taskType int) *CDmTask { + calls["DmTaskCreate"] = true + taskTypes[fmt.Sprintf("%d", taskType)] = true + return &task1 + } + DmTaskSetName = func(task *CDmTask, name string) int { + calls["DmTaskSetName"] = true + expectedTask := &task1 + if task != expectedTask { + t.Fatalf("Wrong libdevmapper call\nExpected: DmTaskSetName(%v)\nReceived: DmTaskSetName(%v)\n", expectedTask, task) + } + // FIXME: use Set.AssertRegexp() + if !strings.HasPrefix(name, "docker-") && !strings.HasPrefix(name, "/dev/mapper/docker-") || + !strings.HasSuffix(name, "-pool") && !strings.HasSuffix(name, "-base") { + t.Fatalf("Wrong libdevmapper call\nExpected: DmTaskSetName(%v)\nReceived: DmTaskSetName(%v)\n", "docker-...-pool", name) + } + return 1 + } + DmTaskRun = func(task *CDmTask) int { + calls["DmTaskRun"] = true + expectedTask := &task1 + if task != expectedTask { + t.Fatalf("Wrong libdevmapper call\nExpected: DmTaskRun(%v)\nReceived: DmTaskRun(%v)\n", expectedTask, task) + } + return 1 + } + DmTaskGetInfo = func(task *CDmTask, info *Info) int { + calls["DmTaskGetInfo"] = true + expectedTask := &task1 + if task != expectedTask { + t.Fatalf("Wrong libdevmapper call\nExpected: DmTaskGetInfo(%v)\nReceived: DmTaskGetInfo(%v)\n", expectedTask, task) + } + // This will crash if info is not dereferenceable + info.Exists = 0 + return 1 + } + DmTaskSetSector = func(task *CDmTask, sector uint64) int { + calls["DmTaskSetSector"] = true + expectedTask := &task1 + if task != expectedTask { + t.Fatalf("Wrong libdevmapper call\nExpected: DmTaskSetSector(%v)\nReceived: DmTaskSetSector(%v)\n", expectedTask, task) + } + if expectedSector := uint64(0); sector != expectedSector { + t.Fatalf("Wrong libdevmapper call to DmTaskSetSector\nExpected: %v\nReceived: %v\n", expectedSector, sector) + } + return 1 + } + DmTaskSetMessage = func(task *CDmTask, message string) int { + calls["DmTaskSetMessage"] = true + expectedTask := &task1 + if task != expectedTask { + t.Fatalf("Wrong libdevmapper call\nExpected: DmTaskSetSector(%v)\nReceived: DmTaskSetSector(%v)\n", expectedTask, task) + } + 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 + if task != expectedTask { + 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 + if task != expectedTask { + t.Fatalf("Wrong libdevmapper call\nExpected: DmTaskDestroy(%v)\nReceived: DmTaskDestroy(%v)\n", expectedTask, task) + } + if start != 0 { + t.Fatalf("Wrong start: %d != %d", start, 0) + } + if ttype != "thin" && ttype != "thin-pool" { + t.Fatalf("Wrong ttype: %s", ttype) + } + // Quick smoke test + if params == "" { + t.Fatalf("Params should not be empty") + } + return 1 + } + fakeCookie := uint(4321) + DmTaskSetCookie = func(task *CDmTask, cookie *uint, flags uint16) int { + calls["DmTaskSetCookie"] = true + expectedTask := &task1 + if task != expectedTask { + t.Fatalf("Wrong libdevmapper call\nExpected: DmTaskDestroy(%v)\nReceived: DmTaskDestroy(%v)\n", expectedTask, task) + } + if flags != 0 { + t.Fatalf("Cookie flags should be 0 (not %x)", flags) + } + *cookie = fakeCookie + return 1 + } + DmUdevWait = func(cookie uint) int { + calls["DmUdevWait"] = true + if cookie != fakeCookie { + t.Fatalf("Wrong cookie: %d != %d", cookie, fakeCookie) + } + return 1 + } + DmTaskSetAddNode = func(task *CDmTask, addNode AddNodeType) int { + if addNode != AddNodeOnCreate { + t.Fatalf("Wrong AddNoteType: %v (expected %v)", addNode, AddNodeOnCreate) + } + calls["DmTaskSetAddNode"] = true + return 1 + } + execRun = func(name string, args ...string) error { + calls["execRun"] = true + if name != "mkfs.ext4" { + t.Fatalf("Expected %s to be executed, not %s", "mkfs.ext4", name) + } + return nil + } + driver, err := Init(home) + if err != nil { t.Fatal(err) } + defer func() { + if err := driver.Cleanup(); err != nil { + t.Fatal(err) + } + }() }() + // Put all tests in a funciton to make sure the garbage collection will + // occur. - id := "foo" - if err := driver.Create(id, ""); err != nil { - t.Fatal(err) + // Call GC to cleanup runtime.Finalizers + runtime.GC() + + calls.Assert(t, + "DmSetDevDir", + "DmLogWithErrnoInit", + "DmTaskSetName", + "DmTaskRun", + "DmTaskGetInfo", + "DmAttachLoopDevice", + "DmTaskDestroy", + "execRun", + "DmTaskCreate", + "DmGetBlockSize", + "DmTaskSetTarget", + "DmTaskSetCookie", + "DmUdevWait", + "DmTaskSetSector", + "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") +} + +func fakeInit() func(home string) (graphdriver.Driver, error) { + oldInit := Init + Init = func(home string) (graphdriver.Driver, error) { + return &Driver{ + home: home, + }, nil } - dir, err := driver.Get(id) - if err != nil { - t.Fatal(err) + return oldInit +} + +func restoreInit(init func(home string) (graphdriver.Driver, error)) { + Init = init +} + +func mockAllDevmapper(calls Set) { + DmSetDevDir = func(dir string) int { + calls["DmSetDevDir"] = true + return 0 } - if st, err := os.Stat(dir); err != nil { - t.Fatal(err) - } else if !st.IsDir() { - t.Fatalf("Get(%V) did not return a directory", id) + LogWithErrnoInit = func() { + calls["DmLogWithErrnoInit"] = true + } + DmTaskCreate = func(taskType int) *CDmTask { + calls["DmTaskCreate"] = true + return &CDmTask{} + } + DmTaskSetName = func(task *CDmTask, name string) int { + calls["DmTaskSetName"] = true + return 1 + } + DmTaskRun = func(task *CDmTask) int { + calls["DmTaskRun"] = true + return 1 + } + DmTaskGetInfo = func(task *CDmTask, info *Info) int { + calls["DmTaskGetInfo"] = true + return 1 + } + DmTaskSetSector = func(task *CDmTask, sector uint64) int { + calls["DmTaskSetSector"] = true + return 1 + } + DmTaskSetMessage = func(task *CDmTask, message string) int { + 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 + } + DmTaskSetCookie = func(task *CDmTask, cookie *uint, flags uint16) int { + calls["DmTaskSetCookie"] = true + return 1 + } + DmUdevWait = func(cookie uint) int { + calls["DmUdevWait"] = true + return 1 + } + DmTaskSetAddNode = func(task *CDmTask, addNode AddNodeType) int { + calls["DmTaskSetAddNode"] = true + return 1 + } + execRun = func(name string, args ...string) error { + calls["execRun"] = true + return nil } } func TestDriverName(t *testing.T) { - d := newDriver(t) - defer cleanup(d) + denyAllDevmapper() + defer denyAllDevmapper() + oldInit := fakeInit() + defer restoreInit(oldInit) + + d := newDriver(t) if d.String() != "devicemapper" { t.Fatalf("Expected driver name to be devicemapper got %s", d.String()) } } func TestDriverCreate(t *testing.T) { - d := newDriver(t) - defer cleanup(d) + denyAllDevmapper() + denyAllSyscall() + defer denyAllSyscall() + defer denyAllDevmapper() - if err := d.Create("1", ""); err != nil { - t.Fatal(err) + calls := make(Set) + mockAllDevmapper(calls) + + sysMount = func(source, target, fstype string, flags uintptr, data string) (err error) { + calls["sysMount"] = true + // FIXME: compare the exact source and target strings (inodes + devname) + if expectedSource := "/dev/mapper/docker-"; !strings.HasPrefix(source, expectedSource) { + t.Fatalf("Wrong syscall call\nExpected: Mount(%v)\nReceived: Mount(%v)\n", expectedSource, source) + } + if expectedTarget := "/tmp/docker-test-devmapper-"; !strings.HasPrefix(target, expectedTarget) { + t.Fatalf("Wrong syscall call\nExpected: Mount(%v)\nReceived: Mount(%v)\n", expectedTarget, target) + } + if expectedFstype := "ext4"; fstype != expectedFstype { + t.Fatalf("Wrong syscall call\nExpected: Mount(%v)\nReceived: Mount(%v)\n", expectedFstype, fstype) + } + if expectedFlags := uintptr(3236757504); flags != expectedFlags { + t.Fatalf("Wrong syscall call\nExpected: Mount(%v)\nReceived: Mount(%v)\n", expectedFlags, flags) + } + return nil } + + Mounted = func(mnt string) (bool, error) { + calls["Mounted"] = true + if !strings.HasPrefix(mnt, "/tmp/docker-test-devmapper-") || !strings.HasSuffix(mnt, "/mnt/1") { + t.Fatalf("Wrong mounted call\nExpected: Mounted(%v)\nReceived: Mounted(%v)\n", "/tmp/docker-test-devmapper-.../mnt/1", mnt) + } + return false, nil + } + + func() { + d := newDriver(t) + + calls.Assert(t, + "DmSetDevDir", + "DmLogWithErrnoInit", + "DmTaskSetName", + "DmTaskRun", + "DmTaskGetInfo", + "DmAttachLoopDevice", + "execRun", + "DmTaskCreate", + "DmGetBlockSize", + "DmTaskSetTarget", + "DmTaskSetCookie", + "DmUdevWait", + "DmTaskSetSector", + "DmTaskSetMessage", + "DmTaskSetAddNode", + ) + + if err := d.Create("1", ""); err != nil { + t.Fatal(err) + } + calls.Assert(t, + "DmTaskCreate", + "DmTaskGetInfo", + "sysMount", + "Mounted", + "DmTaskRun", + "DmTaskSetTarget", + "DmTaskSetSector", + "DmTaskSetCookie", + "DmUdevWait", + "DmTaskSetName", + "DmTaskSetMessage", + "DmTaskSetAddNode", + ) + + }() + + runtime.GC() + + calls.Assert(t, + "DmTaskDestroy", + ) } func TestDriverRemove(t *testing.T) { - d := newDriver(t) - defer cleanup(d) + denyAllDevmapper() + denyAllSyscall() + defer denyAllSyscall() + defer denyAllDevmapper() - if err := d.Create("1", ""); err != nil { - t.Fatal(err) + calls := make(Set) + mockAllDevmapper(calls) + + sysMount = func(source, target, fstype string, flags uintptr, data string) (err error) { + calls["sysMount"] = true + // FIXME: compare the exact source and target strings (inodes + devname) + if expectedSource := "/dev/mapper/docker-"; !strings.HasPrefix(source, expectedSource) { + t.Fatalf("Wrong syscall call\nExpected: Mount(%v)\nReceived: Mount(%v)\n", expectedSource, source) + } + if expectedTarget := "/tmp/docker-test-devmapper-"; !strings.HasPrefix(target, expectedTarget) { + t.Fatalf("Wrong syscall call\nExpected: Mount(%v)\nReceived: Mount(%v)\n", expectedTarget, target) + } + if expectedFstype := "ext4"; fstype != expectedFstype { + t.Fatalf("Wrong syscall call\nExpected: Mount(%v)\nReceived: Mount(%v)\n", expectedFstype, fstype) + } + if expectedFlags := uintptr(3236757504); flags != expectedFlags { + t.Fatalf("Wrong syscall call\nExpected: Mount(%v)\nReceived: Mount(%v)\n", expectedFlags, flags) + } + return nil + } + sysUnmount = func(target string, flags int) (err error) { + calls["sysUnmount"] = true + // FIXME: compare the exact source and target strings (inodes + devname) + if expectedTarget := "/tmp/docker-test-devmapper-"; !strings.HasPrefix(target, expectedTarget) { + t.Fatalf("Wrong syscall call\nExpected: Mount(%v)\nReceived: Mount(%v)\n", expectedTarget, target) + } + if expectedFlags := 0; flags != expectedFlags { + t.Fatalf("Wrong syscall call\nExpected: Mount(%v)\nReceived: Mount(%v)\n", expectedFlags, flags) + } + return nil + } + Mounted = func(mnt string) (bool, error) { + calls["Mounted"] = true + return false, nil } - if err := d.Remove("1"); err != nil { - t.Fatal(err) - } + func() { + d := newDriver(t) + + calls.Assert(t, + "DmSetDevDir", + "DmLogWithErrnoInit", + "DmTaskSetName", + "DmTaskRun", + "DmTaskGetInfo", + "DmAttachLoopDevice", + "execRun", + "DmTaskCreate", + "DmGetBlockSize", + "DmTaskSetTarget", + "DmTaskSetCookie", + "DmUdevWait", + "DmTaskSetSector", + "DmTaskSetMessage", + "DmTaskSetAddNode", + ) + + if err := d.Create("1", ""); err != nil { + t.Fatal(err) + } + + calls.Assert(t, + "DmTaskCreate", + "DmTaskGetInfo", + "sysMount", + "Mounted", + "DmTaskRun", + "DmTaskSetTarget", + "DmTaskSetSector", + "DmTaskSetCookie", + "DmUdevWait", + "DmTaskSetName", + "DmTaskSetMessage", + "DmTaskSetAddNode", + ) + + Mounted = func(mnt string) (bool, error) { + calls["Mounted"] = true + return true, nil + } + + if err := d.Remove("1"); err != nil { + t.Fatal(err) + } + + calls.Assert(t, + "DmTaskRun", + "DmTaskSetSector", + "DmTaskSetName", + "DmTaskSetMessage", + "DmTaskCreate", + "DmTaskGetInfo", + "Mounted", + "sysUnmount", + ) + }() + runtime.GC() + + calls.Assert(t, + "DmTaskDestroy", + ) } func TestCleanup(t *testing.T) { + t.Skip("FIXME: not a unit test") t.Skip("Unimplemented") d := newDriver(t) - defer os.RemoveAll(d.home) + defer osRemoveAll(d.home) mountPoints := make([]string, 2) @@ -161,6 +712,7 @@ func TestCleanup(t *testing.T) { } func TestNotMounted(t *testing.T) { + t.Skip("FIXME: not a unit test") t.Skip("Not implemented") d := newDriver(t) defer cleanup(d) @@ -179,6 +731,7 @@ func TestNotMounted(t *testing.T) { } func TestMounted(t *testing.T) { + t.Skip("FIXME: not a unit test") d := newDriver(t) defer cleanup(d) @@ -199,6 +752,7 @@ func TestMounted(t *testing.T) { } func TestInitCleanedDriver(t *testing.T) { + t.Skip("FIXME: not a unit test") d := newDriver(t) if err := d.Create("1", ""); err != nil { @@ -225,6 +779,7 @@ func TestInitCleanedDriver(t *testing.T) { } func TestMountMountedDriver(t *testing.T) { + t.Skip("FIXME: not a unit test") d := newDriver(t) defer cleanup(d) @@ -243,6 +798,7 @@ func TestMountMountedDriver(t *testing.T) { } func TestGetReturnsValidDevice(t *testing.T) { + t.Skip("FIXME: not a unit test") d := newDriver(t) defer cleanup(d) @@ -268,6 +824,7 @@ func TestGetReturnsValidDevice(t *testing.T) { } func TestDriverGetSize(t *testing.T) { + t.Skip("FIXME: not a unit test") t.Skipf("Size is currently not implemented") d := newDriver(t) @@ -284,7 +841,7 @@ func TestDriverGetSize(t *testing.T) { size := int64(1024) - f, err := os.Create(path.Join(mountPoint, "test_file")) + f, err := osCreate(path.Join(mountPoint, "test_file")) if err != nil { t.Fatal(err) } @@ -301,3 +858,15 @@ func TestDriverGetSize(t *testing.T) { // t.Fatalf("Expected size %d got %d", size, diffSize) // } } + +func assertMap(t *testing.T, m map[string]bool, keys ...string) { + for _, key := range keys { + if _, exists := m[key]; !exists { + t.Fatalf("Key not set: %s", key) + } + delete(m, key) + } + if len(m) != 0 { + t.Fatalf("Unexpected keys: %v", m) + } +} diff --git a/graphdriver/devmapper/mount.go b/graphdriver/devmapper/mount.go index e3a303e507..7a07fff1e8 100644 --- a/graphdriver/devmapper/mount.go +++ b/graphdriver/devmapper/mount.go @@ -1,27 +1,25 @@ package devmapper import ( - "os" "path/filepath" - "syscall" ) // FIXME: this is copy-pasted from the aufs driver. // It should be moved into the core. -func Mounted(mountpoint string) (bool, error) { - mntpoint, err := os.Stat(mountpoint) +var Mounted = func(mountpoint string) (bool, error) { + mntpoint, err := osStat(mountpoint) if err != nil { - if os.IsNotExist(err) { + if osIsNotExist(err) { return false, nil } return false, err } - parent, err := os.Stat(filepath.Join(mountpoint, "..")) + parent, err := osStat(filepath.Join(mountpoint, "..")) if err != nil { return false, err } - mntpointSt := mntpoint.Sys().(*syscall.Stat_t) - parentSt := parent.Sys().(*syscall.Stat_t) + mntpointSt := toSysStatT(mntpoint.Sys()) + parentSt := toSysStatT(parent.Sys()) return mntpointSt.Dev != parentSt.Dev, nil } diff --git a/graphdriver/devmapper/sys.go b/graphdriver/devmapper/sys.go new file mode 100644 index 0000000000..60bafb5f6d --- /dev/null +++ b/graphdriver/devmapper/sys.go @@ -0,0 +1,50 @@ +package devmapper + +import ( + "os" + "os/exec" + "syscall" +) + +type ( + sysStatT syscall.Stat_t + sysErrno syscall.Errno + + osFile struct{ *os.File } +) + +var ( + sysMount = syscall.Mount + sysUnmount = syscall.Unmount + sysCloseOnExec = syscall.CloseOnExec + sysSyscall = syscall.Syscall + + osOpenFile = os.OpenFile + osNewFile = os.NewFile + osCreate = os.Create + osStat = os.Stat + osIsNotExist = os.IsNotExist + osIsExist = os.IsExist + osMkdirAll = os.MkdirAll + osRemoveAll = os.RemoveAll + osRename = os.Rename + osReadlink = os.Readlink + + execRun = func(name string, args ...string) error { + return exec.Command(name, args...).Run() + } +) + +const ( + sysMsMgcVal = syscall.MS_MGC_VAL + sysMsRdOnly = syscall.MS_RDONLY + sysEInval = syscall.EINVAL + sysSysIoctl = syscall.SYS_IOCTL + + osORdWr = os.O_RDWR + osOCreate = os.O_CREATE +) + +func toSysStatT(i interface{}) *sysStatT { + return (*sysStatT)(i.(*syscall.Stat_t)) +}