mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
pkg/mount: wrap mount/umount errors
The errors returned from Mount and Unmount functions are raw syscall.Errno errors (like EPERM or EINVAL), which provides no context about what has happened and why. Similar to os.PathError type, introduce mount.Error type with some context. The error messages will now look like this: > mount /tmp/mount-tests/source:/tmp/mount-tests/target, flags: 0x1001: operation not permitted or > mount tmpfs:/tmp/mount-test-source-516297835: operation not permitted Before this patch, it was just > operation not permitted [v2: add Cause()] [v3: rename MountError to Error, document Cause()] [v4: fixes; audited all users] [v5: make Error type private; changes after @cpuguy83 reviews] Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
This commit is contained in:
parent
90be078fe5
commit
6533136961
13 changed files with 88 additions and 25 deletions
|
@ -17,7 +17,6 @@ import (
|
|||
"github.com/docker/docker/volume"
|
||||
volumemounts "github.com/docker/docker/volume/mounts"
|
||||
"github.com/opencontainers/selinux/go-selinux/label"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
@ -190,7 +189,7 @@ func (container *Container) UnmountIpcMount() error {
|
|||
return nil
|
||||
}
|
||||
if err = mount.Unmount(shmPath); err != nil && !os.IsNotExist(err) {
|
||||
return errors.Wrapf(err, "umount %s", shmPath)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -380,7 +379,8 @@ func (container *Container) DetachAndUnmount(volumeEventLog func(name, action st
|
|||
|
||||
for _, mountPath := range mountPaths {
|
||||
if err := mount.Unmount(mountPath); err != nil {
|
||||
logrus.Warnf("%s unmountVolumes: Failed to do lazy umount for volume '%s': %v", container.ID, mountPath, err)
|
||||
logrus.WithError(err).WithField("container", container.ID).
|
||||
Warn("Unable to unmount")
|
||||
}
|
||||
}
|
||||
return container.UnmountVolumes(volumeEventLog)
|
||||
|
|
|
@ -178,7 +178,7 @@ func (d *Driver) Cleanup() error {
|
|||
}
|
||||
|
||||
if umountErr != nil {
|
||||
return errors.Wrapf(umountErr, "error unmounting %s", d.home)
|
||||
return umountErr
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
|
@ -1200,7 +1200,7 @@ func (devices *DeviceSet) growFS(info *devInfo) error {
|
|||
options = joinMountOptions(options, devices.mountOptions)
|
||||
|
||||
if err := mount.Mount(info.DevName(), fsMountPoint, devices.BaseDeviceFilesystem, options); err != nil {
|
||||
return fmt.Errorf("Error mounting '%s' on '%s' (fstype='%s' options='%s'): %s\n%v", info.DevName(), fsMountPoint, devices.BaseDeviceFilesystem, options, err, string(dmesg.Dmesg(256)))
|
||||
return errors.Wrapf(err, "Failed to mount; dmesg: %s", string(dmesg.Dmesg(256)))
|
||||
}
|
||||
|
||||
defer unix.Unmount(fsMountPoint, unix.MNT_DETACH)
|
||||
|
@ -2381,7 +2381,7 @@ func (devices *DeviceSet) MountDevice(hash, path, mountLabel string) error {
|
|||
options = joinMountOptions(options, label.FormatMountLabel("", mountLabel))
|
||||
|
||||
if err := mount.Mount(info.DevName(), path, fstype, options); err != nil {
|
||||
return fmt.Errorf("devmapper: Error mounting '%s' on '%s' (fstype='%s' options='%s'): %s\n%v", info.DevName(), path, fstype, options, err, string(dmesg.Dmesg(256)))
|
||||
return errors.Wrapf(err, "Failed to mount; dmesg: %s", string(dmesg.Dmesg(256)))
|
||||
}
|
||||
|
||||
if fstype == "xfs" && devices.xfsNospaceRetries != "" {
|
||||
|
|
|
@ -16,7 +16,6 @@ import (
|
|||
"github.com/docker/docker/pkg/locker"
|
||||
"github.com/docker/docker/pkg/mount"
|
||||
"github.com/docker/go-units"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
@ -129,11 +128,7 @@ func (d *Driver) Cleanup() error {
|
|||
return err
|
||||
}
|
||||
|
||||
if umountErr != nil {
|
||||
return errors.Wrapf(umountErr, "error unmounting %s", d.home)
|
||||
}
|
||||
|
||||
return nil
|
||||
return umountErr
|
||||
}
|
||||
|
||||
// CreateReadWrite creates a layer that is writable for use as a container
|
||||
|
|
|
@ -19,6 +19,7 @@ import (
|
|||
"github.com/docker/docker/pkg/parsers"
|
||||
"github.com/mistifyio/go-zfs"
|
||||
"github.com/opencontainers/selinux/go-selinux/label"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
@ -390,7 +391,7 @@ func (d *Driver) Get(id, mountLabel string) (_ containerfs.ContainerFS, retErr e
|
|||
}
|
||||
|
||||
if err := mount.Mount(filesystem, mountpoint, "zfs", options); err != nil {
|
||||
return nil, fmt.Errorf("error creating zfs mount of %s to %s: %v", filesystem, mountpoint, err)
|
||||
return nil, errors.Wrap(err, "error creating zfs mount")
|
||||
}
|
||||
|
||||
// this could be our first mount after creation of the filesystem, and the root dir may still have root
|
||||
|
|
|
@ -2,11 +2,46 @@ package mount // import "github.com/docker/docker/pkg/mount"
|
|||
|
||||
import (
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// mountError records an error from mount or unmount operation
|
||||
type mountError struct {
|
||||
op string
|
||||
source, target string
|
||||
flags uintptr
|
||||
data string
|
||||
err error
|
||||
}
|
||||
|
||||
func (e *mountError) Error() string {
|
||||
out := e.op + " "
|
||||
|
||||
if e.source != "" {
|
||||
out += e.source + ":" + e.target
|
||||
} else {
|
||||
out += e.target
|
||||
}
|
||||
|
||||
if e.flags != uintptr(0) {
|
||||
out += ", flags: 0x" + strconv.FormatUint(uint64(e.flags), 16)
|
||||
}
|
||||
if e.data != "" {
|
||||
out += ", data: " + e.data
|
||||
}
|
||||
|
||||
out += ": " + e.err.Error()
|
||||
return out
|
||||
}
|
||||
|
||||
// Cause returns the underlying cause of the error
|
||||
func (e *mountError) Cause() error {
|
||||
return e.err
|
||||
}
|
||||
|
||||
// FilterFunc is a type defining a callback function
|
||||
// to filter out unwanted entries. It takes a pointer
|
||||
// to an Info struct (not fully populated, currently
|
||||
|
|
|
@ -11,8 +11,8 @@ package mount // import "github.com/docker/docker/pkg/mount"
|
|||
import "C"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
|
@ -47,8 +47,13 @@ func mount(device, target, mType string, flag uintptr, data string) error {
|
|||
}
|
||||
|
||||
if errno := C.nmount(&rawOptions[0], C.uint(len(options)), C.int(flag)); errno != 0 {
|
||||
reason := C.GoString(C.strerror(*C.__error()))
|
||||
return fmt.Errorf("Failed to call nmount: %s", reason)
|
||||
return &mountError{
|
||||
op: "mount",
|
||||
source: device,
|
||||
target: target,
|
||||
flags: flag,
|
||||
err: syscall.Errno(errno),
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -33,20 +33,41 @@ func mount(device, target, mType string, flags uintptr, data string) error {
|
|||
// Initial call applying all non-propagation flags for mount
|
||||
// or remount with changed data
|
||||
if err := unix.Mount(device, target, mType, oflags, data); err != nil {
|
||||
return err
|
||||
return &mountError{
|
||||
op: "mount",
|
||||
source: device,
|
||||
target: target,
|
||||
flags: oflags,
|
||||
data: data,
|
||||
err: err,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if flags&ptypes != 0 {
|
||||
// Change the propagation type.
|
||||
if err := unix.Mount("", target, "", flags&pflags, ""); err != nil {
|
||||
return &mountError{
|
||||
op: "remount",
|
||||
target: target,
|
||||
flags: flags & pflags,
|
||||
err: err,
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if oflags&broflags == broflags {
|
||||
// Remount the bind to apply read only.
|
||||
return unix.Mount("", target, "", oflags|unix.MS_REMOUNT, "")
|
||||
if err := unix.Mount("", target, "", oflags|unix.MS_REMOUNT, ""); err != nil {
|
||||
return &mountError{
|
||||
op: "remount-ro",
|
||||
target: target,
|
||||
flags: oflags | unix.MS_REMOUNT,
|
||||
err: err,
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
"path"
|
||||
"testing"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
|
@ -326,7 +327,7 @@ func TestSubtreeUnbindable(t *testing.T) {
|
|||
}()
|
||||
|
||||
// then attempt to mount it to target. It should fail
|
||||
if err := Mount(sourceDir, targetDir, "none", "bind,rw"); err != nil && err != unix.EINVAL {
|
||||
if err := Mount(sourceDir, targetDir, "none", "bind,rw"); err != nil && errors.Cause(err) != unix.EINVAL {
|
||||
t.Fatal(err)
|
||||
} else if err == nil {
|
||||
t.Fatalf("%q should not have been bindable", sourceDir)
|
||||
|
|
|
@ -6,12 +6,17 @@ import "golang.org/x/sys/unix"
|
|||
|
||||
func unmount(target string, flags int) error {
|
||||
err := unix.Unmount(target, flags)
|
||||
if err == unix.EINVAL {
|
||||
if err == nil || err == unix.EINVAL {
|
||||
// Ignore "not mounted" error here. Note the same error
|
||||
// can be returned if flags are invalid, so this code
|
||||
// assumes that the flags value is always correct.
|
||||
err = nil
|
||||
return nil
|
||||
}
|
||||
|
||||
return err
|
||||
return &mountError{
|
||||
op: "umount",
|
||||
target: target,
|
||||
flags: uintptr(flags),
|
||||
err: err,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -61,7 +61,7 @@ func (pm *Manager) enable(p *v2.Plugin, c *controller, force bool) error {
|
|||
if err := pm.executor.Create(p.GetID(), *spec, stdout, stderr); err != nil {
|
||||
if p.PluginObj.Config.PropagatedMount != "" {
|
||||
if err := mount.Unmount(propRoot); err != nil {
|
||||
logrus.Warnf("Could not unmount %s: %v", propRoot, err)
|
||||
logrus.WithField("plugin", p.Name()).WithError(err).Warn("Failed to unmount vplugin propagated mount root")
|
||||
}
|
||||
}
|
||||
return errors.WithStack(err)
|
||||
|
|
|
@ -344,7 +344,7 @@ func (v *localVolume) unmount() error {
|
|||
if v.opts != nil {
|
||||
if err := mount.Unmount(v.path); err != nil {
|
||||
if mounted, mErr := mount.Mounted(v.path); mounted || mErr != nil {
|
||||
return errdefs.System(errors.Wrapf(err, "error while unmounting volume path '%s'", v.path))
|
||||
return errdefs.System(err)
|
||||
}
|
||||
}
|
||||
v.active.mounted = false
|
||||
|
|
|
@ -86,7 +86,7 @@ func (v *localVolume) mount() error {
|
|||
}
|
||||
}
|
||||
err := mount.Mount(v.opts.MountDevice, v.path, v.opts.MountType, mountOpts)
|
||||
return errors.Wrapf(err, "error while mounting volume with options: %s", v.opts)
|
||||
return errors.Wrap(err, "failed to mount local volume")
|
||||
}
|
||||
|
||||
func (v *localVolume) CreatedAt() (time.Time, error) {
|
||||
|
|
Loading…
Reference in a new issue