From 0cd2df6d03a7d78811b7164d7edd69e1fbbb2b48 Mon Sep 17 00:00:00 2001 From: akutz Date: Mon, 23 May 2016 13:14:39 -0500 Subject: [PATCH] Unix OS Driver This patch renames the Linux and Darwin OS drivers to the Unix OS driver. This driver is loaded for both the Linux and Darwin OSs. The Darwin implementation lacks implementations of the Mount & Format functions. --- client/client.go | 4 +- drivers/os/darwin/darwin.go | 91 ---------- drivers/os/darwin/darwin_os.go | 1 - drivers/os/linux/linux_docs.go | 10 - drivers/os/linux/linux_label.go | 29 --- drivers/os/linux/linux_os.go | 1 - drivers/os/{linux/linux.go => unix/unix.go} | 115 +++++++----- drivers/os/unix/unix_darwin.c | 22 +++ drivers/os/unix/unix_darwin.go | 171 ++++++++++++++++++ drivers/os/unix/unix_darwin.h | 20 ++ drivers/os/unix/unix_darwin_test.go | 67 +++++++ .../linux_mount.go => unix/unix_linux.go} | 60 +++++- .../imports_client.go} | 6 +- imports/client/imports_client_unix.go | 8 + 14 files changed, 413 insertions(+), 192 deletions(-) delete mode 100644 drivers/os/darwin/darwin.go delete mode 100644 drivers/os/darwin/darwin_os.go delete mode 100644 drivers/os/linux/linux_docs.go delete mode 100644 drivers/os/linux/linux_label.go delete mode 100644 drivers/os/linux/linux_os.go rename drivers/os/{linux/linux.go => unix/unix.go} (74%) create mode 100644 drivers/os/unix/unix_darwin.c create mode 100644 drivers/os/unix/unix_darwin.go create mode 100644 drivers/os/unix/unix_darwin.h create mode 100644 drivers/os/unix/unix_darwin_test.go rename drivers/os/{linux/linux_mount.go => unix/unix_linux.go} (89%) rename imports/{local/imports_local.go => client/imports_client.go} (88%) create mode 100644 imports/client/imports_client_unix.go diff --git a/client/client.go b/client/client.go index 8103ca7a..2ded6011 100644 --- a/client/client.go +++ b/client/client.go @@ -12,8 +12,8 @@ import ( "github.com/emccode/libstorage/api/utils" apicnfg "github.com/emccode/libstorage/api/utils/config" - // load the local imports - _ "github.com/emccode/libstorage/imports/local" + // load the client imports + _ "github.com/emccode/libstorage/imports/client" ) type client struct { diff --git a/drivers/os/darwin/darwin.go b/drivers/os/darwin/darwin.go deleted file mode 100644 index e036ee0d..00000000 --- a/drivers/os/darwin/darwin.go +++ /dev/null @@ -1,91 +0,0 @@ -// +build darwin - -package darwin - -import ( - "runtime" - - "github.com/akutz/gofig" - "github.com/akutz/goof" - - "github.com/emccode/libstorage/api/registry" - "github.com/emccode/libstorage/api/types" -) - -const driverName = "darwin" - -var ( - errUnknownOS = goof.New("unknown OS") - errUnknownFileSystem = goof.New("unknown file system") - errUnsupportedFileSystem = goof.New("unsupported file system") -) - -func init() { - registry.RegisterOSDriver(driverName, newDriver) - gofig.Register(configRegistration()) -} - -type driver struct { - config gofig.Config -} - -func newDriver() types.OSDriver { - return &driver{} -} - -func (d *driver) Init(ctx types.Context, config gofig.Config) error { - if runtime.GOOS != "darwin" { - return errUnknownOS - } - d.config = config - return nil -} - -func (d *driver) Name() string { - return driverName -} - -func (d *driver) Mounts( - ctx types.Context, - deviceName, mountPoint string, - opts types.Store) ([]*types.MountInfo, error) { - - return nil, nil -} - -func (d *driver) Mount( - ctx types.Context, - deviceName, mountPoint string, - opts *types.DeviceMountOpts) error { - - return nil -} - -func (d *driver) Unmount( - ctx types.Context, - mountPoint string, - opts types.Store) error { - - return nil -} - -func (d *driver) IsMounted( - ctx types.Context, - mountPoint string, - opts types.Store) (bool, error) { - - return false, nil -} - -func (d *driver) Format( - ctx types.Context, - deviceName string, - opts *types.DeviceFormatOpts) error { - - return nil -} - -func configRegistration() *gofig.Registration { - r := gofig.NewRegistration("Darwin") - return r -} diff --git a/drivers/os/darwin/darwin_os.go b/drivers/os/darwin/darwin_os.go deleted file mode 100644 index 145d0408..00000000 --- a/drivers/os/darwin/darwin_os.go +++ /dev/null @@ -1 +0,0 @@ -package darwin diff --git a/drivers/os/linux/linux_docs.go b/drivers/os/linux/linux_docs.go deleted file mode 100644 index cdde6245..00000000 --- a/drivers/os/linux/linux_docs.go +++ /dev/null @@ -1,10 +0,0 @@ -// +build linux - -/* -Package linux is the OS driver for linux. In order to reduce external -dependencies, this package borrows the following packages: - - - github.com/docker/docker/pkg/mount - - github.com/opencontainers/runc/libcontainer/label -*/ -package linux diff --git a/drivers/os/linux/linux_label.go b/drivers/os/linux/linux_label.go deleted file mode 100644 index 915f4838..00000000 --- a/drivers/os/linux/linux_label.go +++ /dev/null @@ -1,29 +0,0 @@ -// +build linux - -package linux - -import ( - "fmt" -) - -/* -formatMountLabel returns a string to be used by the mount command. -The format of this string will be used to alter the labeling of the mountpoint. -The string returned is suitable to be used as the options field of the mount -command. - -If you need to have additional mount point options, you can pass them in as -the first parameter. Second parameter is the label that you wish to apply -to all content in the mount point. -*/ -func formatMountLabel(src, mountLabel string) string { - if mountLabel != "" { - switch src { - case "": - src = fmt.Sprintf("context=%q", mountLabel) - default: - src = fmt.Sprintf("%s,context=%q", src, mountLabel) - } - } - return src -} diff --git a/drivers/os/linux/linux_os.go b/drivers/os/linux/linux_os.go deleted file mode 100644 index c4fa7a1d..00000000 --- a/drivers/os/linux/linux_os.go +++ /dev/null @@ -1 +0,0 @@ -package linux diff --git a/drivers/os/linux/linux.go b/drivers/os/unix/unix.go similarity index 74% rename from drivers/os/linux/linux.go rename to drivers/os/unix/unix.go index b48342e4..bb78dc05 100644 --- a/drivers/os/linux/linux.go +++ b/drivers/os/unix/unix.go @@ -1,6 +1,13 @@ -// +build linux +// +build linux darwin -package linux +/* +Package unix is the OS driver for linux and darwin. In order to reduce external +dependencies, this package borrows the following packages: + + - github.com/docker/docker/pkg/mount + - github.com/opencontainers/runc/libcontainer/label +*/ +package unix import ( "bytes" @@ -9,8 +16,9 @@ import ( "os/exec" "runtime" "strings" + "syscall" + "time" - log "github.com/Sirupsen/logrus" "github.com/akutz/gofig" "github.com/akutz/goof" @@ -18,10 +26,9 @@ import ( "github.com/emccode/libstorage/api/types" ) -const driverName = "linux" +var driverName = runtime.GOOS var ( - errUnknownOS = goof.New("unknown OS") errUnknownFileSystem = goof.New("unknown file system") errUnsupportedFileSystem = goof.New("unsupported file system") ) @@ -40,9 +47,6 @@ func newDriver() types.OSDriver { } func (d *driver) Init(ctx types.Context, config gofig.Config) error { - if runtime.GOOS != "linux" { - return errUnknownOS - } d.config = config return nil } @@ -56,7 +60,7 @@ func (d *driver) Mounts( deviceName, mountPoint string, opts types.Store) ([]*types.MountInfo, error) { - mounts, err := getMounts() + mounts, err := mounts(ctx, deviceName, mountPoint, opts) if err != nil { return nil, err } @@ -122,7 +126,24 @@ func (d *driver) Unmount( mountPoint string, opts types.Store) error { - return unmount(mountPoint) + var ( + err error + isMounted bool + ) + + isMounted, err = d.IsMounted(ctx, mountPoint, opts) + if err != nil || !isMounted { + return err + } + + for i := 0; i < 10; i++ { + if err = syscall.Unmount(mountPoint, 0); err == nil { + return nil + } + time.Sleep(100 * time.Millisecond) + } + + return nil } func (d *driver) IsMounted( @@ -130,7 +151,18 @@ func (d *driver) IsMounted( mountPoint string, opts types.Store) (bool, error) { - return mounted(mountPoint) + entries, err := mounts(ctx, "", mountPoint, opts) + if err != nil { + return false, err + } + + // Search the table for the mountpoint + for _, e := range entries { + if e.MountPoint == mountPoint { + return true, nil + } + } + return false, nil } func (d *driver) Format( @@ -138,43 +170,7 @@ func (d *driver) Format( deviceName string, opts *types.DeviceFormatOpts) error { - fsType, err := probeFsType(deviceName) - if err != nil && err != errUnknownFileSystem { - return err - } - fsDetected := fsType != "" - - ctx.WithFields(log.Fields{ - "fsDetected": fsDetected, - "fsType": fsType, - "deviceName": deviceName, - "overwriteFs": opts.OverwriteFS, - "driverName": driverName}).Info("probe information") - - if opts.OverwriteFS || !fsDetected { - switch opts.NewFSType { - case "ext4": - if err := exec.Command( - "mkfs.ext4", "-F", deviceName).Run(); err != nil { - return goof.WithFieldE( - "deviceName", deviceName, - "error creating filesystem", - err) - } - case "xfs": - if err := exec.Command( - "mkfs.xfs", "-f", deviceName).Run(); err != nil { - return goof.WithFieldE( - "deviceName", deviceName, - "error creating filesystem", - err) - } - default: - return errUnsupportedFileSystem - } - } - - return nil + return format(ctx, deviceName, opts) } func (d *driver) isNfsDevice(device string) bool { @@ -182,6 +178,7 @@ func (d *driver) isNfsDevice(device string) bool { } func (d *driver) nfsMount(device, target string) error { + command := exec.Command("mount", device, target) output, err := command.CombinedOutput() if err != nil { @@ -246,6 +243,28 @@ func probeFsType(device string) (string, error) { return "", errUnknownFileSystem } +/* +formatMountLabel returns a string to be used by the mount command. +The format of this string will be used to alter the labeling of the mountpoint. +The string returned is suitable to be used as the options field of the mount +command. + +If you need to have additional mount point options, you can pass them in as +the first parameter. Second parameter is the label that you wish to apply +to all content in the mount point. +*/ +func formatMountLabel(src, mountLabel string) string { + if mountLabel != "" { + switch src { + case "": + src = fmt.Sprintf("context=%q", mountLabel) + default: + src = fmt.Sprintf("%s,context=%q", src, mountLabel) + } + } + return src +} + func (d *driver) volumeMountPath(target string) string { return fmt.Sprintf("%s%s", target, d.volumeRootPath()) } diff --git a/drivers/os/unix/unix_darwin.c b/drivers/os/unix/unix_darwin.c new file mode 100644 index 00000000..101b6f1c --- /dev/null +++ b/drivers/os/unix/unix_darwin.c @@ -0,0 +1,22 @@ +// +build darwin + +#include "unix_darwin.h" +#include + +statfs_result _statfs(char* path) { + statfs_result r; + r.val = (struct statfs*)malloc(sizeof(struct statfs)); + r.err = statfs((const char*) path, r.val) == 0 ? 0 : errno; + return r; +} + +getmntinfo_result _getmntinfo(int flags) { + getmntinfo_result r; + r.err = 0; + r.len = getmntinfo(&r.val, flags); + if (r.len < 1) { + r.err = errno; + r.len = 0; + } + return r; +} diff --git a/drivers/os/unix/unix_darwin.go b/drivers/os/unix/unix_darwin.go new file mode 100644 index 00000000..3331dafc --- /dev/null +++ b/drivers/os/unix/unix_darwin.go @@ -0,0 +1,171 @@ +// +build darwin + +package unix + +//#cgo CFLAGS: -I${SRCDIR} +//#include "unix_darwin.h" +import "C" +import ( + "unsafe" + + "github.com/akutz/goof" + + "github.com/emccode/libstorage/api/types" +) + +const ( + // ReadOnly will mount the file system read-only. + ReadOnly = C.MNT_RDONLY + + // NoSetUserID will not allow set-user-identifier or set-group-identifier + // bits to take effect. + NoSetUserID = C.MNT_NOSUID + + // NoDev will not interpret character or block special devices on the file + // system. + NoDev = C.MNT_NODEV + + // NoExec will not allow execution of any binaries on the mounted file + // system. + NoExec = C.MNT_NOEXEC + + // Synchronous will allow I/O to the file system to be done synchronously. + Synchronous = C.MNT_SYNCHRONOUS + + // NoAccessTime will not update the file access time when reading from a + // file. + NoAccessTime = C.MNT_NOATIME + + // Wait instructs calls to get information about a filesystem to refresh + // information about a filesystem before returning it, causing the call to + // block until the refresh operation is complete. + Wait = C.MNT_WAIT + + // NoWait instructs calls to get information about a filesystem to return + // any available information immediately without waiting. + NoWait = C.MNT_NOWAIT +) + +type fsInfo struct { + blockSize int64 + ioSize int64 + blocks int64 + blocksFree int64 + blocksAvail int64 + files int64 + filesFree int64 + fileSystemTypeID int8 + fileSystemTypeName string + mountPath string + devicePath string + mountFlags int64 +} + +func statFS(mountPoint string) (*fsInfo, error) { + + r := C._statfs(C.CString(mountPoint)) + if r.val != nil { + defer C.free(unsafe.Pointer(r.val)) + } + + if r.err != 0 { + return nil, goof.WithFields(goof.Fields{ + "mountPoint": mountPoint, + "error": r.err, + }, "statFS error") + } + + return toFSInfoFromStatFS(r.val), nil +} + +func mounts( + ctx types.Context, + deviceName, mountPoint string, + opts types.Store) ([]*types.MountInfo, error) { + + fsInfo, err := getMountInfo(ctx, true) + if err != nil { + return nil, err + } + + return toMountInfoArray(fsInfo), nil +} + +func mount(device, target, mType, options string) error { + + return nil +} + +func format( + ctx types.Context, + deviceName string, + opts *types.DeviceFormatOpts) error { + + return nil +} + +func getMountInfo(ctx types.Context, wait bool) ([]*fsInfo, error) { + + var flags int + if wait { + flags = Wait + } else { + flags = NoWait + } + + r := C._getmntinfo(C.int(flags)) + if r.err != 0 { + return nil, goof.WithFields(goof.Fields{ + "wait": wait, + "flags": flags, + "len": r.len, + "error": r.err, + }, "getMountInfo error") + } + + ctx.WithField("len", r.len).Debug("got mount info") + fsiList := make([]*fsInfo, r.len) + miSlice := (*[1 << 30]C.struct_statfs)(unsafe.Pointer(r.val))[:r.len:r.len] + + for x, mi := range miSlice { + fsiList[x] = toFSInfoFromStatFS(&mi) + } + + return fsiList, nil +} + +func toMountInfoArray(val []*fsInfo) []*types.MountInfo { + + newVal := make([]*types.MountInfo, len(val)) + for x, fsi := range val { + newVal[x] = toMountInfo(fsi) + } + return newVal +} + +func toMountInfo(val *fsInfo) *types.MountInfo { + + return &types.MountInfo{ + Source: val.devicePath, + MountPoint: val.mountPath, + FSType: val.fileSystemTypeName, + } +} + +func toFSInfoFromStatFS(val *C.struct_statfs) *fsInfo { + + return &fsInfo{ + blockSize: int64(val.f_bsize), + ioSize: int64(val.f_iosize), + blocks: int64(val.f_blocks), + blocksFree: int64(val.f_bfree), + blocksAvail: int64(val.f_bavail), + files: int64(val.f_files), + filesFree: int64(val.f_ffree), + fileSystemTypeID: int8(val.f_type), + fileSystemTypeName: C.GoString(&val.f_fstypename[0]), + mountPath: C.GoString(&val.f_mntonname[0]), + devicePath: C.GoString(&val.f_mntfromname[0]), + mountFlags: int64(val.f_flags), + } +} diff --git a/drivers/os/unix/unix_darwin.h b/drivers/os/unix/unix_darwin.h new file mode 100644 index 00000000..469a8a98 --- /dev/null +++ b/drivers/os/unix/unix_darwin.h @@ -0,0 +1,20 @@ +// +build darwin + +#include +#include +#include + +typedef struct { + struct statfs* val; + int err; +} statfs_result; + +statfs_result _statfs(char* path); + +typedef struct { + int len; + struct statfs* val; + int err; +} getmntinfo_result; + +getmntinfo_result _getmntinfo(int flags); diff --git a/drivers/os/unix/unix_darwin_test.go b/drivers/os/unix/unix_darwin_test.go new file mode 100644 index 00000000..f618bc62 --- /dev/null +++ b/drivers/os/unix/unix_darwin_test.go @@ -0,0 +1,67 @@ +// +build darwin + +package unix + +import ( + "testing" + + "github.com/akutz/goof" + "github.com/stretchr/testify/assert" + + "github.com/emccode/libstorage/api/context" + "github.com/emccode/libstorage/api/utils" +) + +func init() { + goof.IncludeFieldsInError = true + goof.IncludeFieldsInString = true + goof.IncludeFieldsInFormat = true +} + +func TestMounts(t *testing.T) { + + ctx := context.Background() + store := utils.NewStore() + d := newDriver() + + mounts, err := d.Mounts(ctx, "", "", store) + assert.NoError(t, err) + assert.True(t, len(mounts) > 1) + + mounts, err = d.Mounts(ctx, "", "/", store) + assert.NoError(t, err) + assert.True(t, len(mounts) == 1) +} + +func TestIsMounted(t *testing.T) { + + ctx := context.Background() + store := utils.NewStore() + d := newDriver() + + isMounted, err := d.IsMounted(ctx, "/", store) + assert.NoError(t, err) + assert.True(t, isMounted) +} + +func TestStatFS(t *testing.T) { + + r, err := statFS("/") + assert.NoError(t, err) + t.Logf("%+v", r) +} + +func TestGetMountInfoAndStatFS(t *testing.T) { + + r, err := getMountInfo(context.Background(), true) + assert.NoError(t, err) + if err != nil { + t.FailNow() + } + + for _, fsi := range r { + statFSResult, err := statFS(fsi.mountPath) + assert.NoError(t, err) + t.Logf("%+v", statFSResult) + } +} diff --git a/drivers/os/linux/linux_mount.go b/drivers/os/unix/unix_linux.go similarity index 89% rename from drivers/os/linux/linux_mount.go rename to drivers/os/unix/unix_linux.go index 53db9c69..f286535f 100644 --- a/drivers/os/linux/linux_mount.go +++ b/drivers/os/unix/unix_linux.go @@ -1,16 +1,18 @@ -// +build linux - -package linux +package unix import ( "bufio" "fmt" "io" "os" + "os/exec" "strings" "syscall" "time" + log "github.com/Sirupsen/logrus" + "github.com/akutz/goof" + "github.com/emccode/libstorage/api/types" ) @@ -263,8 +265,12 @@ func parseTmpfsOptions(options string) (int, string, error) { return flags, data, nil } -// getMounts retrieves a list of mounts for the current running process. -func getMounts() ([]*types.MountInfo, error) { +// mounts retrieves a list of mounts for the current running process. +func mounts( + ctx types.Context, + deviceName, mountPoint string, + opts types.Store) ([]*types.MountInfo, error) { + return parseMountTable() } @@ -354,3 +360,47 @@ func forceUnmount(target string) (err error) { } return } + +func format( + ctx types.Context, + deviceName string, + opts *types.DeviceFormatOpts) error { + + fsType, err := probeFsType(deviceName) + if err != nil && err != errUnknownFileSystem { + return err + } + fsDetected := fsType != "" + + ctx.WithFields(log.Fields{ + "fsDetected": fsDetected, + "fsType": fsType, + "deviceName": deviceName, + "overwriteFs": opts.OverwriteFS, + "driverName": driverName}).Info("probe information") + + if opts.OverwriteFS || !fsDetected { + switch opts.NewFSType { + case "ext4": + if err := exec.Command( + "mkfs.ext4", "-F", deviceName).Run(); err != nil { + return goof.WithFieldE( + "deviceName", deviceName, + "error creating filesystem", + err) + } + case "xfs": + if err := exec.Command( + "mkfs.xfs", "-f", deviceName).Run(); err != nil { + return goof.WithFieldE( + "deviceName", deviceName, + "error creating filesystem", + err) + } + default: + return errUnsupportedFileSystem + } + } + + return nil +} diff --git a/imports/local/imports_local.go b/imports/client/imports_client.go similarity index 88% rename from imports/local/imports_local.go rename to imports/client/imports_client.go index 8f47b4d1..a85ef262 100644 --- a/imports/local/imports_local.go +++ b/imports/client/imports_client.go @@ -1,4 +1,4 @@ -package local +package client import ( // load the config @@ -7,10 +7,6 @@ import ( // load the libStorage storage driver _ "github.com/emccode/libstorage/drivers/storage/libstorage" - // load the os drivers - _ "github.com/emccode/libstorage/drivers/os/darwin" - _ "github.com/emccode/libstorage/drivers/os/linux" - // load the integration drivers _ "github.com/emccode/libstorage/drivers/integration/docker" diff --git a/imports/client/imports_client_unix.go b/imports/client/imports_client_unix.go new file mode 100644 index 00000000..61f37fa5 --- /dev/null +++ b/imports/client/imports_client_unix.go @@ -0,0 +1,8 @@ +// +build linux darwin + +package client + +import ( + // load the os drivers + _ "github.com/emccode/libstorage/drivers/os/unix" +)