11package snapshot
22
33import (
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
913func (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