Skip to content

Commit cf05c75

Browse files
Emulate "bind" mounts using the bind filter
Signed-off-by: Gabriel Adrian Samfira <[email protected]>
1 parent a3328d3 commit cf05c75

File tree

1 file changed

+39
-5
lines changed

1 file changed

+39
-5
lines changed

snapshot/localmounter_windows.go

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,18 @@ package snapshot
33
import (
44
"os"
55

6+
"github.com/Microsoft/go-winio/pkg/bindfilter"
67
"github.com/containerd/containerd/errdefs"
78
"github.com/containerd/containerd/mount"
89
"github.com/pkg/errors"
10+
"golang.org/x/sys/windows"
911
)
1012

1113
func (lm *localMounter) Mount() (string, error) {
1214
lm.mu.Lock()
1315
defer lm.mu.Unlock()
1416

15-
if lm.mounts == nil {
17+
if lm.mounts == nil && lm.mountable != nil {
1618
mounts, release, err := lm.mountable.Mount()
1719
if err != nil {
1820
return "", err
@@ -33,9 +35,18 @@ func (lm *localMounter) Mount() (string, error) {
3335
return "", errors.Wrap(err, "failed to create temp dir")
3436
}
3537

36-
if err := m.Mount(dir); err != nil {
37-
return "", errors.Wrapf(err, "failed to mount %v: %+v", m, err)
38+
if m.Type == "bind" || m.Type == "rbind" {
39+
// The Windows snapshotter does not have any notion of bind mounts. We emulate
40+
// bind mounts here using the bind filter.
41+
if err := bindfilter.ApplyFileBinding(dir, m.Source, m.ReadOnly()); err != nil {
42+
return "", errors.Wrapf(err, "failed to mount %v: %+v", m, err)
43+
}
44+
} else {
45+
if err := m.Mount(dir); err != nil {
46+
return "", errors.Wrapf(err, "failed to mount %v: %+v", m, err)
47+
}
3848
}
49+
3950
lm.target = dir
4051
return lm.target, nil
4152
}
@@ -44,9 +55,32 @@ func (lm *localMounter) Unmount() error {
4455
lm.mu.Lock()
4556
defer lm.mu.Unlock()
4657

58+
// NOTE(gabriel-samfira): Should we just return nil if len(lm.mounts) == 0?
59+
// Calling Mount() would fail on an instance of the localMounter where mounts contains
60+
// anything other than 1 mount.
61+
if len(lm.mounts) != 1 {
62+
return errors.Wrapf(errdefs.ErrNotImplemented, "request to mount %d layers, only 1 is supported", len(lm.mounts))
63+
}
64+
m := lm.mounts[0]
65+
4766
if lm.target != "" {
48-
if err := mount.Unmount(lm.target, 0); err != nil {
49-
return err
67+
if m.Type == "bind" || m.Type == "rbind" {
68+
if err := bindfilter.RemoveFileBinding(lm.target); err != nil {
69+
// The following two errors denote that lm.target is not a mount point.
70+
if !errors.Is(err, windows.ERROR_INVALID_PARAMETER) && !errors.Is(err, windows.ERROR_NOT_FOUND) {
71+
return errors.Wrapf(err, "failed to unmount %v: %+v", lm.target, err)
72+
}
73+
}
74+
} else {
75+
// The containerd snapshotter uses the bind filter internally to mount windows-layer
76+
// volumes. We use same bind filter here to emulate bind mounts. In theory we could
77+
// simply call mount.Unmount() here, without the extra check for bind mounts and explicit
78+
// call to bindfilter.RemoveFileBinding() (above), but this would operate under the
79+
// assumption that the internal implementation in containerd will always be based on the
80+
// bind filter, which feels brittle.
81+
if err := mount.Unmount(lm.target, 0); err != nil {
82+
return errors.Wrapf(err, "failed to unmount %v: %+v", lm.target, err)
83+
}
5084
}
5185
os.RemoveAll(lm.target)
5286
lm.target = ""

0 commit comments

Comments
 (0)