1
1
package snapshot
2
2
3
3
import (
4
+ "os"
5
+
6
+ "github.com/Microsoft/go-winio/pkg/bindfilter"
4
7
"github.com/containerd/containerd/errdefs"
5
8
"github.com/containerd/containerd/mount"
6
9
"github.com/pkg/errors"
10
+ "golang.org/x/sys/windows"
7
11
)
8
12
9
13
func (lm * localMounter ) Mount () (string , error ) {
10
14
lm .mu .Lock ()
11
15
defer lm .mu .Unlock ()
12
16
13
- if lm .mounts == nil {
17
+ if lm .mounts == nil && lm . mountable != nil {
14
18
mounts , release , err := lm .mountable .Mount ()
15
19
if err != nil {
16
20
return "" , err
@@ -26,27 +30,30 @@ func (lm *localMounter) Mount() (string, error) {
26
30
}
27
31
28
32
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
+ }
29
37
30
38
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.
39
44
return m .Source , nil
40
45
}
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
+ }
41
55
}
42
56
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
- }
50
57
lm .target = dir
51
58
return lm .target , nil
52
59
}
@@ -55,10 +62,34 @@ func (lm *localMounter) Unmount() error {
55
62
lm .mu .Lock ()
56
63
defer lm .mu .Unlock ()
57
64
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
+
58
73
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
+ }
61
91
}
92
+ os .RemoveAll (lm .target )
62
93
lm .target = ""
63
94
}
64
95
0 commit comments