Merge pull request #8838 from AkihiroSuda/revert-8789
Revert "Add support for bind-mounts on Darwin (a.k.a. "make native snapshotter work")" (#8789)
This commit is contained in:
commit
e26b669ba9
47
diff/apply/apply_darwin.go
Normal file
47
diff/apply/apply_darwin.go
Normal file
@ -0,0 +1,47 @@
|
||||
/*
|
||||
Copyright The containerd Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package apply
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"github.com/containerd/containerd/archive"
|
||||
"github.com/containerd/containerd/mount"
|
||||
)
|
||||
|
||||
func apply(ctx context.Context, mounts []mount.Mount, r io.Reader) error {
|
||||
// We currently do not support mounts nor bind mounts on MacOS in the containerd daemon.
|
||||
// Using this as an exception to enable native snapshotter and allow further research.
|
||||
if len(mounts) == 1 && mounts[0].Type == "bind" {
|
||||
opts := []archive.ApplyOpt{}
|
||||
|
||||
if os.Getuid() != 0 {
|
||||
opts = append(opts, archive.WithNoSameOwner())
|
||||
}
|
||||
|
||||
path := mounts[0].Source
|
||||
_, err := archive.Apply(ctx, path, r, opts...)
|
||||
return err
|
||||
}
|
||||
|
||||
return mount.WithTempMount(ctx, mounts, func(root string) error {
|
||||
_, err := archive.Apply(ctx, root, r)
|
||||
return err
|
||||
})
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
//go:build !linux
|
||||
//go:build !linux && !darwin
|
||||
|
||||
/*
|
||||
Copyright The containerd Authors.
|
||||
|
@ -1,50 +0,0 @@
|
||||
/*
|
||||
Copyright The containerd Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package mount
|
||||
|
||||
import (
|
||||
"os/exec"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// fuseSuperMagic is defined in statfs(2)
|
||||
const fuseSuperMagic = 0x65735546
|
||||
|
||||
func isFUSE(dir string) bool {
|
||||
var st unix.Statfs_t
|
||||
if err := unix.Statfs(dir, &st); err != nil {
|
||||
return false
|
||||
}
|
||||
return st.Type == fuseSuperMagic
|
||||
}
|
||||
|
||||
// unmountFUSE attempts to unmount using fusermount/fusermount3 helper binary.
|
||||
//
|
||||
// For FUSE mounts, using these helper binaries is preferred, see:
|
||||
// https://github.com/containerd/containerd/pull/3765#discussion_r342083514
|
||||
func unmountFUSE(target string) error {
|
||||
var err error
|
||||
for _, helperBinary := range []string{"fusermount3", "fusermount"} {
|
||||
cmd := exec.Command(helperBinary, "-u", target)
|
||||
err = cmd.Run()
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
@ -1,30 +0,0 @@
|
||||
//go:build !linux && !windows
|
||||
|
||||
/*
|
||||
Copyright The containerd Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package mount
|
||||
|
||||
import "fmt"
|
||||
|
||||
func isFUSE(dir string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// unmountFUSE is not implemented on this platform
|
||||
func unmountFUSE(target string) error {
|
||||
return fmt.Errorf("FUSE is not supported on this platform")
|
||||
}
|
@ -1,42 +0,0 @@
|
||||
/*
|
||||
Copyright The containerd Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package mount
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os/exec"
|
||||
)
|
||||
|
||||
// Mount to the provided target.
|
||||
func (m *Mount) mount(target string) error {
|
||||
// See https://github.com/slonopotamus/containerd-darwin-mount-helper for reference implementation
|
||||
const commandName = "containerd-darwin-mount-helper"
|
||||
|
||||
args := []string{"-t", m.Type}
|
||||
for _, option := range m.Options {
|
||||
args = append(args, "-o", option)
|
||||
}
|
||||
args = append(args, m.Source, target)
|
||||
|
||||
cmd := exec.Command(commandName, args...)
|
||||
output, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s [%v] failed: %q: %w", commandName, args, string(output), err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
@ -19,11 +19,18 @@ package mount
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
exec "golang.org/x/sys/execabs"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrNotImplementOnUnix is returned for methods that are not implemented
|
||||
ErrNotImplementOnUnix = errors.New("not implemented under unix")
|
||||
)
|
||||
|
||||
// Mount to the provided target.
|
||||
//
|
||||
// The "syscall" and "golang.org/x/sys/unix" packages do not define a Mount
|
||||
@ -70,3 +77,57 @@ func (m *Mount) mount(target string) error {
|
||||
}
|
||||
return fmt.Errorf("mount [%v] failed with ECHILD (retried %d times)", args, retriesOnECHILD)
|
||||
}
|
||||
|
||||
// Unmount the provided mount path with the flags
|
||||
func Unmount(target string, flags int) error {
|
||||
if err := unmount(target, flags); err != nil && err != unix.EINVAL {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func unmount(target string, flags int) error {
|
||||
for i := 0; i < 50; i++ {
|
||||
if err := unix.Unmount(target, flags); err != nil {
|
||||
switch err {
|
||||
case unix.EBUSY:
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
continue
|
||||
default:
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("failed to unmount target %s: %w", target, unix.EBUSY)
|
||||
}
|
||||
|
||||
// UnmountAll repeatedly unmounts the given mount point until there
|
||||
// are no mounts remaining (EINVAL is returned by mount), which is
|
||||
// useful for undoing a stack of mounts on the same mount point.
|
||||
// UnmountAll all is noop when the first argument is an empty string.
|
||||
// This is done when the containerd client did not specify any rootfs
|
||||
// mounts (e.g. because the rootfs is managed outside containerd)
|
||||
// UnmountAll is noop when the mount path does not exist.
|
||||
func UnmountAll(mount string, flags int) error {
|
||||
if mount == "" {
|
||||
return nil
|
||||
}
|
||||
if _, err := os.Stat(mount); os.IsNotExist(err) {
|
||||
return nil
|
||||
}
|
||||
|
||||
for {
|
||||
if err := unmount(mount, flags); err != nil {
|
||||
// EINVAL is returned if the target is not a
|
||||
// mount point, indicating that we are
|
||||
// done. It can also indicate a few other
|
||||
// things (such as invalid flags) which we
|
||||
// unfortunately end up squelching here too.
|
||||
if err == unix.EINVAL {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -23,6 +23,7 @@ import (
|
||||
"path"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
exec "golang.org/x/sys/execabs"
|
||||
"golang.org/x/sys/unix"
|
||||
@ -119,6 +120,92 @@ func (m *Mount) mount(target string) (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Unmount the provided mount path with the flags
|
||||
func Unmount(target string, flags int) error {
|
||||
if err := unmount(target, flags); err != nil && err != unix.EINVAL {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// fuseSuperMagic is defined in statfs(2)
|
||||
const fuseSuperMagic = 0x65735546
|
||||
|
||||
func isFUSE(dir string) bool {
|
||||
var st unix.Statfs_t
|
||||
if err := unix.Statfs(dir, &st); err != nil {
|
||||
return false
|
||||
}
|
||||
return st.Type == fuseSuperMagic
|
||||
}
|
||||
|
||||
// unmountFUSE attempts to unmount using fusermount/fusermount3 helper binary.
|
||||
//
|
||||
// For FUSE mounts, using these helper binaries is preferred, see:
|
||||
// https://github.com/containerd/containerd/pull/3765#discussion_r342083514
|
||||
func unmountFUSE(target string) error {
|
||||
var err error
|
||||
for _, helperBinary := range []string{"fusermount3", "fusermount"} {
|
||||
cmd := exec.Command(helperBinary, "-u", target)
|
||||
err = cmd.Run()
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func unmount(target string, flags int) error {
|
||||
if isFUSE(target) {
|
||||
if err := unmountFUSE(target); err == nil {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
for i := 0; i < 50; i++ {
|
||||
if err := unix.Unmount(target, flags); err != nil {
|
||||
switch err {
|
||||
case unix.EBUSY:
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
continue
|
||||
default:
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("failed to unmount target %s: %w", target, unix.EBUSY)
|
||||
}
|
||||
|
||||
// UnmountAll repeatedly unmounts the given mount point until there
|
||||
// are no mounts remaining (EINVAL is returned by mount), which is
|
||||
// useful for undoing a stack of mounts on the same mount point.
|
||||
// UnmountAll all is noop when the first argument is an empty string.
|
||||
// This is done when the containerd client did not specify any rootfs
|
||||
// mounts (e.g. because the rootfs is managed outside containerd)
|
||||
// UnmountAll is noop when the mount path does not exist.
|
||||
func UnmountAll(mount string, flags int) error {
|
||||
if mount == "" {
|
||||
return nil
|
||||
}
|
||||
if _, err := os.Stat(mount); os.IsNotExist(err) {
|
||||
return nil
|
||||
}
|
||||
|
||||
for {
|
||||
if err := unmount(mount, flags); err != nil {
|
||||
// EINVAL is returned if the target is not a
|
||||
// mount point, indicating that we are
|
||||
// done. It can also indicate a few other
|
||||
// things (such as invalid flags) which we
|
||||
// unfortunately end up squelching here too.
|
||||
if err == unix.EINVAL {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// parseMountOptions takes fstab style mount options and parses them for
|
||||
// use with a standard mount() syscall
|
||||
func parseMountOptions(options []string) (int, []string, bool) {
|
||||
|
@ -1,4 +1,4 @@
|
||||
//go:build !windows && !openbsd
|
||||
//go:build !windows && !darwin && !openbsd
|
||||
|
||||
/*
|
||||
Copyright The containerd Authors.
|
||||
@ -19,13 +19,9 @@
|
||||
package mount
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
"github.com/moby/sys/mountinfo"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// UnmountRecursive unmounts the target and all mounts underneath, starting
|
||||
@ -63,64 +59,3 @@ func UnmountRecursive(target string, flags int) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func unmount(target string, flags int) error {
|
||||
if isFUSE(target) {
|
||||
// TODO: Why error is ignored?
|
||||
// Shouldn't this just be "return unmountFUSE(target)"?
|
||||
if err := unmountFUSE(target); err == nil {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
for i := 0; i < 50; i++ {
|
||||
if err := unix.Unmount(target, flags); err != nil {
|
||||
switch err {
|
||||
case unix.EBUSY:
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
continue
|
||||
default:
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("failed to unmount target %s: %w", target, unix.EBUSY)
|
||||
}
|
||||
|
||||
// Unmount the provided mount path with the flags
|
||||
func Unmount(target string, flags int) error {
|
||||
if err := unmount(target, flags); err != nil && err != unix.EINVAL {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnmountAll repeatedly unmounts the given mount point until there
|
||||
// are no mounts remaining (EINVAL is returned by mount), which is
|
||||
// useful for undoing a stack of mounts on the same mount point.
|
||||
// UnmountAll all is noop when the first argument is an empty string.
|
||||
// This is done when the containerd client did not specify any rootfs
|
||||
// mounts (e.g. because the rootfs is managed outside containerd)
|
||||
// UnmountAll is noop when the mount path does not exist.
|
||||
func UnmountAll(mount string, flags int) error {
|
||||
if mount == "" {
|
||||
return nil
|
||||
}
|
||||
if _, err := os.Stat(mount); os.IsNotExist(err) {
|
||||
return nil
|
||||
}
|
||||
|
||||
for {
|
||||
if err := unmount(mount, flags); err != nil {
|
||||
// EINVAL is returned if the target is not a
|
||||
// mount point, indicating that we are
|
||||
// done. It can also indicate a few other
|
||||
// things (such as invalid flags) which we
|
||||
// unfortunately end up squelching here too.
|
||||
if err == unix.EINVAL {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
//go:build openbsd
|
||||
//go:build darwin || openbsd
|
||||
|
||||
/*
|
||||
Copyright The containerd Authors.
|
||||
@ -18,26 +18,29 @@
|
||||
|
||||
package mount
|
||||
|
||||
import (
|
||||
"github.com/containerd/containerd/errdefs"
|
||||
import "errors"
|
||||
|
||||
var (
|
||||
// ErrNotImplementOnUnix is returned for methods that are not implemented
|
||||
ErrNotImplementOnUnix = errors.New("not implemented under unix")
|
||||
)
|
||||
|
||||
// Mount is not implemented on this platform
|
||||
func (m *Mount) mount(target string) error {
|
||||
return errdefs.ErrNotImplemented
|
||||
return ErrNotImplementOnUnix
|
||||
}
|
||||
|
||||
// Unmount is not implemented on this platform
|
||||
func Unmount(mount string, flags int) error {
|
||||
return errdefs.ErrNotImplemented
|
||||
return ErrNotImplementOnUnix
|
||||
}
|
||||
|
||||
// UnmountAll is not implemented on this platform
|
||||
func UnmountAll(mount string, flags int) error {
|
||||
return errdefs.ErrNotImplemented
|
||||
return ErrNotImplementOnUnix
|
||||
}
|
||||
|
||||
// UnmountRecursive is not implemented on this platform
|
||||
func UnmountRecursive(mount string, flags int) error {
|
||||
return errdefs.ErrNotImplemented
|
||||
return ErrNotImplementOnUnix
|
||||
}
|
||||
|
@ -33,6 +33,11 @@ import (
|
||||
|
||||
const sourceStreamName = "containerd.io-source"
|
||||
|
||||
var (
|
||||
// ErrNotImplementOnWindows is returned when an action is not implemented for windows
|
||||
ErrNotImplementOnWindows = errors.New("not implemented under windows")
|
||||
)
|
||||
|
||||
// Mount to the provided target.
|
||||
func (m *Mount) mount(target string) (retErr error) {
|
||||
if m.Type != "windows-layer" {
|
||||
|
@ -19,13 +19,12 @@
|
||||
package os
|
||||
|
||||
import (
|
||||
"github.com/containerd/containerd/errdefs"
|
||||
"github.com/containerd/containerd/mount"
|
||||
)
|
||||
|
||||
// Mount will call unix.Mount to mount the file.
|
||||
func (RealOS) Mount(source string, target string, fstype string, flags uintptr, data string) error {
|
||||
return errdefs.ErrNotImplemented
|
||||
return mount.ErrNotImplementOnUnix
|
||||
}
|
||||
|
||||
// Unmount will call Unmount to unmount the file.
|
||||
|
@ -21,6 +21,7 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
|
||||
"github.com/containerd/containerd/identifiers"
|
||||
"github.com/containerd/containerd/mount"
|
||||
@ -128,8 +129,10 @@ type Bundle struct {
|
||||
func (b *Bundle) Delete() error {
|
||||
work, werr := os.Readlink(filepath.Join(b.Path, "work"))
|
||||
rootfs := filepath.Join(b.Path, "rootfs")
|
||||
if err := mount.UnmountRecursive(rootfs, 0); err != nil {
|
||||
return fmt.Errorf("unmount rootfs %s: %w", rootfs, err)
|
||||
if runtime.GOOS != "darwin" {
|
||||
if err := mount.UnmountRecursive(rootfs, 0); err != nil {
|
||||
return fmt.Errorf("unmount rootfs %s: %w", rootfs, err)
|
||||
}
|
||||
}
|
||||
if err := os.Remove(rootfs); err != nil && !os.IsNotExist(err) {
|
||||
return fmt.Errorf("failed to remove bundle rootfs: %w", err)
|
||||
|
Loading…
Reference in New Issue
Block a user