Skip to content

Commit d988f59

Browse files
authored
Merge pull request moby#3783 from gabriel-samfira/fix-local-mounter-windows
[Windows] Fix local mounter on Windows
2 parents bc23425 + f994e78 commit d988f59

File tree

1 file changed

+49
-18
lines changed

1 file changed

+49
-18
lines changed

snapshot/localmounter_windows.go

Lines changed: 49 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,20 @@
11
package snapshot
22

33
import (
4+
"os"
5+
6+
"github.com/Microsoft/go-winio/pkg/bindfilter"
47
"github.com/containerd/containerd/errdefs"
58
"github.com/containerd/containerd/mount"
69
"github.com/pkg/errors"
10+
"golang.org/x/sys/windows"
711
)
812

913
func (lm *localMounter) Mount() (string, error) {
1014
lm.mu.Lock()
1115
defer lm.mu.Unlock()
1216

13-
if lm.mounts == nil {
17+
if lm.mounts == nil && lm.mountable != nil {
1418
mounts, release, err := lm.mountable.Mount()
1519
if err != nil {
1620
return "", err
@@ -26,27 +30,30 @@ func (lm *localMounter) Mount() (string, error) {
2630
}
2731

2832
m := lm.mounts[0]
33+
dir, err := os.MkdirTemp("", "buildkit-mount")
34+
if err != nil {
35+
return "", errors.Wrap(err, "failed to create temp dir")
36+
}
2937

3038
if m.Type == "bind" || m.Type == "rbind" {
31-
ro := false
32-
for _, opt := range m.Options {
33-
if opt == "ro" {
34-
ro = true
35-
break
36-
}
37-
}
38-
if !ro {
39+
if !m.ReadOnly() {
40+
// This is a rw bind mount, we can simply return the source.
41+
// NOTE(gabriel-samfira): This is safe to do if the source of the bind mount is a DOS path
42+
// of a local folder. If it's a \\?\Volume{} (for any reason that I can't think of now)
43+
// we should allow bindfilter.ApplyFileBinding() to mount it.
3944
return m.Source, nil
4045
}
46+
// The Windows snapshotter does not have any notion of bind mounts. We emulate
47+
// bind mounts here using the bind filter.
48+
if err := bindfilter.ApplyFileBinding(dir, m.Source, m.ReadOnly()); err != nil {
49+
return "", errors.Wrapf(err, "failed to mount %v: %+v", m, err)
50+
}
51+
} else {
52+
if err := m.Mount(dir); err != nil {
53+
return "", errors.Wrapf(err, "failed to mount %v: %+v", m, err)
54+
}
4155
}
4256

43-
// Windows mounts always activate in-place, so the target of the mount must be the source directory.
44-
// See https://github.com/containerd/containerd/pull/2366
45-
dir := m.Source
46-
47-
if err := m.Mount(dir); err != nil {
48-
return "", errors.Wrapf(err, "failed to mount in-place: %v", m)
49-
}
5057
lm.target = dir
5158
return lm.target, nil
5259
}
@@ -55,10 +62,34 @@ func (lm *localMounter) Unmount() error {
5562
lm.mu.Lock()
5663
defer lm.mu.Unlock()
5764

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

0 commit comments

Comments
 (0)