Skip to content

Commit 6533136

Browse files
committed
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 <[email protected]>
1 parent 90be078 commit 6533136

13 files changed

Lines changed: 88 additions & 25 deletions

File tree

container/container_unix.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ import (
1717
"github.com/docker/docker/volume"
1818
volumemounts "github.com/docker/docker/volume/mounts"
1919
"github.com/opencontainers/selinux/go-selinux/label"
20-
"github.com/pkg/errors"
2120
"github.com/sirupsen/logrus"
2221
"golang.org/x/sys/unix"
2322
)
@@ -190,7 +189,7 @@ func (container *Container) UnmountIpcMount() error {
190189
return nil
191190
}
192191
if err = mount.Unmount(shmPath); err != nil && !os.IsNotExist(err) {
193-
return errors.Wrapf(err, "umount %s", shmPath)
192+
return err
194193
}
195194
return nil
196195
}
@@ -380,7 +379,8 @@ func (container *Container) DetachAndUnmount(volumeEventLog func(name, action st
380379

381380
for _, mountPath := range mountPaths {
382381
if err := mount.Unmount(mountPath); err != nil {
383-
logrus.Warnf("%s unmountVolumes: Failed to do lazy umount for volume '%s': %v", container.ID, mountPath, err)
382+
logrus.WithError(err).WithField("container", container.ID).
383+
Warn("Unable to unmount")
384384
}
385385
}
386386
return container.UnmountVolumes(volumeEventLog)

daemon/graphdriver/btrfs/btrfs.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@ func (d *Driver) Cleanup() error {
178178
}
179179

180180
if umountErr != nil {
181-
return errors.Wrapf(umountErr, "error unmounting %s", d.home)
181+
return umountErr
182182
}
183183

184184
return nil

daemon/graphdriver/devmapper/deviceset.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1200,7 +1200,7 @@ func (devices *DeviceSet) growFS(info *devInfo) error {
12001200
options = joinMountOptions(options, devices.mountOptions)
12011201

12021202
if err := mount.Mount(info.DevName(), fsMountPoint, devices.BaseDeviceFilesystem, options); err != nil {
1203-
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)))
1203+
return errors.Wrapf(err, "Failed to mount; dmesg: %s", string(dmesg.Dmesg(256)))
12041204
}
12051205

12061206
defer unix.Unmount(fsMountPoint, unix.MNT_DETACH)
@@ -2381,7 +2381,7 @@ func (devices *DeviceSet) MountDevice(hash, path, mountLabel string) error {
23812381
options = joinMountOptions(options, label.FormatMountLabel("", mountLabel))
23822382

23832383
if err := mount.Mount(info.DevName(), path, fstype, options); err != nil {
2384-
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)))
2384+
return errors.Wrapf(err, "Failed to mount; dmesg: %s", string(dmesg.Dmesg(256)))
23852385
}
23862386

23872387
if fstype == "xfs" && devices.xfsNospaceRetries != "" {

daemon/graphdriver/devmapper/driver.go

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ import (
1616
"github.com/docker/docker/pkg/locker"
1717
"github.com/docker/docker/pkg/mount"
1818
"github.com/docker/go-units"
19-
"github.com/pkg/errors"
2019
"github.com/sirupsen/logrus"
2120
"golang.org/x/sys/unix"
2221
)
@@ -129,11 +128,7 @@ func (d *Driver) Cleanup() error {
129128
return err
130129
}
131130

132-
if umountErr != nil {
133-
return errors.Wrapf(umountErr, "error unmounting %s", d.home)
134-
}
135-
136-
return nil
131+
return umountErr
137132
}
138133

139134
// CreateReadWrite creates a layer that is writable for use as a container

daemon/graphdriver/zfs/zfs.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import (
1919
"github.com/docker/docker/pkg/parsers"
2020
"github.com/mistifyio/go-zfs"
2121
"github.com/opencontainers/selinux/go-selinux/label"
22+
"github.com/pkg/errors"
2223
"github.com/sirupsen/logrus"
2324
"golang.org/x/sys/unix"
2425
)
@@ -390,7 +391,7 @@ func (d *Driver) Get(id, mountLabel string) (_ containerfs.ContainerFS, retErr e
390391
}
391392

392393
if err := mount.Mount(filesystem, mountpoint, "zfs", options); err != nil {
393-
return nil, fmt.Errorf("error creating zfs mount of %s to %s: %v", filesystem, mountpoint, err)
394+
return nil, errors.Wrap(err, "error creating zfs mount")
394395
}
395396

396397
// this could be our first mount after creation of the filesystem, and the root dir may still have root

pkg/mount/mount.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,46 @@ package mount // import "github.com/docker/docker/pkg/mount"
22

33
import (
44
"sort"
5+
"strconv"
56
"strings"
67

78
"github.com/sirupsen/logrus"
89
)
910

11+
// mountError records an error from mount or unmount operation
12+
type mountError struct {
13+
op string
14+
source, target string
15+
flags uintptr
16+
data string
17+
err error
18+
}
19+
20+
func (e *mountError) Error() string {
21+
out := e.op + " "
22+
23+
if e.source != "" {
24+
out += e.source + ":" + e.target
25+
} else {
26+
out += e.target
27+
}
28+
29+
if e.flags != uintptr(0) {
30+
out += ", flags: 0x" + strconv.FormatUint(uint64(e.flags), 16)
31+
}
32+
if e.data != "" {
33+
out += ", data: " + e.data
34+
}
35+
36+
out += ": " + e.err.Error()
37+
return out
38+
}
39+
40+
// Cause returns the underlying cause of the error
41+
func (e *mountError) Cause() error {
42+
return e.err
43+
}
44+
1045
// FilterFunc is a type defining a callback function
1146
// to filter out unwanted entries. It takes a pointer
1247
// to an Info struct (not fully populated, currently

pkg/mount/mounter_freebsd.go

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ package mount // import "github.com/docker/docker/pkg/mount"
1111
import "C"
1212

1313
import (
14-
"fmt"
1514
"strings"
15+
"syscall"
1616
"unsafe"
1717
)
1818

@@ -47,8 +47,13 @@ func mount(device, target, mType string, flag uintptr, data string) error {
4747
}
4848

4949
if errno := C.nmount(&rawOptions[0], C.uint(len(options)), C.int(flag)); errno != 0 {
50-
reason := C.GoString(C.strerror(*C.__error()))
51-
return fmt.Errorf("Failed to call nmount: %s", reason)
50+
return &mountError{
51+
op: "mount",
52+
source: device,
53+
target: target,
54+
flags: flag,
55+
err: syscall.Errno(errno),
56+
}
5257
}
5358
return nil
5459
}

pkg/mount/mounter_linux.go

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,20 +33,41 @@ func mount(device, target, mType string, flags uintptr, data string) error {
3333
// Initial call applying all non-propagation flags for mount
3434
// or remount with changed data
3535
if err := unix.Mount(device, target, mType, oflags, data); err != nil {
36-
return err
36+
return &mountError{
37+
op: "mount",
38+
source: device,
39+
target: target,
40+
flags: oflags,
41+
data: data,
42+
err: err,
43+
}
3744
}
3845
}
3946

4047
if flags&ptypes != 0 {
4148
// Change the propagation type.
4249
if err := unix.Mount("", target, "", flags&pflags, ""); err != nil {
50+
return &mountError{
51+
op: "remount",
52+
target: target,
53+
flags: flags & pflags,
54+
err: err,
55+
}
4356
return err
4457
}
4558
}
4659

4760
if oflags&broflags == broflags {
4861
// Remount the bind to apply read only.
49-
return unix.Mount("", target, "", oflags|unix.MS_REMOUNT, "")
62+
if err := unix.Mount("", target, "", oflags|unix.MS_REMOUNT, ""); err != nil {
63+
return &mountError{
64+
op: "remount-ro",
65+
target: target,
66+
flags: oflags | unix.MS_REMOUNT,
67+
err: err,
68+
}
69+
70+
}
5071
}
5172

5273
return nil

pkg/mount/sharedsubtree_linux_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"path"
88
"testing"
99

10+
"github.com/pkg/errors"
1011
"golang.org/x/sys/unix"
1112
)
1213

@@ -326,7 +327,7 @@ func TestSubtreeUnbindable(t *testing.T) {
326327
}()
327328

328329
// then attempt to mount it to target. It should fail
329-
if err := Mount(sourceDir, targetDir, "none", "bind,rw"); err != nil && err != unix.EINVAL {
330+
if err := Mount(sourceDir, targetDir, "none", "bind,rw"); err != nil && errors.Cause(err) != unix.EINVAL {
330331
t.Fatal(err)
331332
} else if err == nil {
332333
t.Fatalf("%q should not have been bindable", sourceDir)

pkg/mount/unmount_unix.go

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,17 @@ import "golang.org/x/sys/unix"
66

77
func unmount(target string, flags int) error {
88
err := unix.Unmount(target, flags)
9-
if err == unix.EINVAL {
9+
if err == nil || err == unix.EINVAL {
1010
// Ignore "not mounted" error here. Note the same error
1111
// can be returned if flags are invalid, so this code
1212
// assumes that the flags value is always correct.
13-
err = nil
13+
return nil
1414
}
1515

16-
return err
16+
return &mountError{
17+
op: "umount",
18+
target: target,
19+
flags: uintptr(flags),
20+
err: err,
21+
}
1722
}

0 commit comments

Comments
 (0)