1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101
|
package snapshot
import (
"os"
"github.com/Microsoft/go-winio/pkg/bindfilter"
"github.com/containerd/containerd/mount"
cerrdefs "github.com/containerd/errdefs"
"github.com/pkg/errors"
"golang.org/x/sys/windows"
)
func (lm *localMounter) Mount() (string, error) {
lm.mu.Lock()
defer lm.mu.Unlock()
if lm.mounts == nil && lm.mountable != nil {
mounts, release, err := lm.mountable.Mount()
if err != nil {
return "", err
}
lm.mounts = mounts
lm.release = release
}
// Windows can only mount a single mount at a given location.
// Parent layers are carried in Options, opaquely to localMounter.
if len(lm.mounts) != 1 {
return "", errors.Wrapf(cerrdefs.ErrNotImplemented, "request to mount %d layers, only 1 is supported", len(lm.mounts))
}
m := lm.mounts[0]
dir, err := os.MkdirTemp("", "buildkit-mount")
if err != nil {
return "", errors.Wrap(err, "failed to create temp dir")
}
if m.Type == "bind" || m.Type == "rbind" {
if !m.ReadOnly() {
// This is a rw bind mount, we can simply return the source.
// NOTE(gabriel-samfira): This is safe to do if the source of the bind mount is a DOS path
// of a local folder. If it's a \\?\Volume{} (for any reason that I can't think of now)
// we should allow bindfilter.ApplyFileBinding() to mount it.
return m.Source, nil
}
// The Windows snapshotter does not have any notion of bind mounts. We emulate
// bind mounts here using the bind filter.
if err := bindfilter.ApplyFileBinding(dir, m.Source, m.ReadOnly()); err != nil {
return "", errors.Wrapf(err, "failed to mount %v: %+v", m, err)
}
} else {
if err := m.Mount(dir); err != nil {
return "", errors.Wrapf(err, "failed to mount %v: %+v", m, err)
}
}
lm.target = dir
return lm.target, nil
}
func (lm *localMounter) Unmount() error {
lm.mu.Lock()
defer lm.mu.Unlock()
// NOTE(gabriel-samfira): Should we just return nil if len(lm.mounts) == 0?
// Calling Mount() would fail on an instance of the localMounter where mounts contains
// anything other than 1 mount.
if len(lm.mounts) != 1 {
return errors.Wrapf(cerrdefs.ErrNotImplemented, "request to mount %d layers, only 1 is supported", len(lm.mounts))
}
m := lm.mounts[0]
if lm.target != "" {
if m.Type == "bind" || m.Type == "rbind" {
if err := bindfilter.RemoveFileBinding(lm.target); err != nil {
// The following two errors denote that lm.target is not a mount point.
if !errors.Is(err, windows.ERROR_INVALID_PARAMETER) && !errors.Is(err, windows.ERROR_NOT_FOUND) {
return errors.Wrapf(err, "failed to unmount %v: %+v", lm.target, err)
}
}
} else {
// The containerd snapshotter uses the bind filter internally to mount windows-layer
// volumes. We use same bind filter here to emulate bind mounts. In theory we could
// simply call mount.Unmount() here, without the extra check for bind mounts and explicit
// call to bindfilter.RemoveFileBinding() (above), but this would operate under the
// assumption that the internal implementation in containerd will always be based on the
// bind filter, which feels brittle.
if err := mount.Unmount(lm.target, 0); err != nil {
return errors.Wrapf(err, "failed to unmount %v: %+v", lm.target, err)
}
}
os.RemoveAll(lm.target)
lm.target = ""
}
if lm.release != nil {
return lm.release()
}
return nil
}
|